LVS
/*
 * Building: cc -o com com.c
 * Usage   : ./com /dev/device [speed]
 * Example : ./com /dev/ttyS0 [115200]
 * Keys    : Ctrl-A - exit, Ctrl-X - display control lines status
 * Darcs   : darcs get http://tinyserial.sf.net/
 * Homepage: http://tinyserial.sourceforge.net //원작자 사이트
 * Version : 2009-03-05
 *
 * Ivan Tikhonov, http://www.brokestream.com, kefeer@brokestream.com
 * Patches by Jim Kou, Henry Nestler, Jon Miner, Alan Horstmann
 *
 */


/* Copyright (C) 2007 Ivan Tikhonov

  This software is provided 'as-is', without any express or implied
  warranty.  In no event will the authors be held liable for any damages
  arising from the use of this software.

  Permission is granted to anyone to use this software for any purpose,
  including commercial applications, and to alter it and redistribute it
  freely, subject to the following restrictions:

  1. The origin of this software must not be misrepresented; you must not
     claim that you wrote the original software. If you use this software
     in a product, an acknowledgment in the product documentation would be
     appreciated but is not required.
  2. Altered source versions must be plainly marked as such, and must not be
     misrepresented as being the original software.
  3. This notice may not be removed or altered from any source distribution.

  Ivan Tikhonov, kefeer@brokestream.com

*/

#include <termios.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/signal.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <errno.h>
#include <time.h>

int transfer_byte(int from, int to, int is_control);

typedef struct {char *name; int flag; } speed_spec;


void print_status(int fd) {
int status;
unsigned int arg;
status = ioctl(fd, TIOCMGET, &arg);
fprintf(stderr, "[STATUS]: ");
if(arg & TIOCM_RTS) fprintf(stderr, "RTS ");
if(arg & TIOCM_CTS) fprintf(stderr, "CTS ");
if(arg & TIOCM_DSR) fprintf(stderr, "DSR ");
if(arg & TIOCM_CAR) fprintf(stderr, "DCD ");
if(arg & TIOCM_DTR) fprintf(stderr, "DTR ");
if(arg & TIOCM_RNG) fprintf(stderr, "RI ");
fprintf(stderr, "\r\n");
}


int main(int argc, char *argv[])
{
int comfd;
struct termios oldtio, newtio;       //place for old and new port settings for serial port
struct termios oldkey, newkey;       //place tor old and new port settings for keyboard teletype
char *devicename = argv[1];
int need_exit = 0;
speed_spec speeds[] =
{
{"1200", B1200},
{"2400", B2400},
{"4800", B4800},
{"9600", B9600},
{"19200", B19200},
{"38400", B38400},
{"57600", B57600},
{"115200", B115200},
{NULL, 0}
};
int speed = B9600;

if(argc < 2) {
fprintf(stderr, "example: %s /dev/ttyS0 [115200]\n", argv[0]);
exit(1);
}

comfd = open(devicename, O_RDWR | O_NOCTTY | O_NONBLOCK);
if (comfd < 0)
{
perror(devicename);
exit(-1);
}

if(argc > 2) {
speed_spec *s;
for(s = speeds; s->name; s++) {
if(strcmp(s->name, argv[2]) == 0) {
speed = s->flag;
fprintf(stderr, "setting speed %s\n", s->name);
break;
}
}
}

fprintf(stderr, "C-a exit, C-x modem lines status\n");

tcgetattr(STDIN_FILENO,&oldkey);
newkey.c_cflag = B9600 | CRTSCTS | CS8 | CLOCAL | CREAD;
newkey.c_iflag = IGNPAR;
newkey.c_oflag = 0;
newkey.c_lflag = 0;
newkey.c_cc[VMIN]=1;
newkey.c_cc[VTIME]=0;
tcflush(STDIN_FILENO, TCIFLUSH);
tcsetattr(STDIN_FILENO,TCSANOW,&newkey);


tcgetattr(comfd,&oldtio); // save current port settings 
newtio.c_cflag = speed | CS8 | CLOCAL | CREAD;
newtio.c_iflag = IGNPAR;
newtio.c_oflag = 0;
newtio.c_lflag = 0;
newtio.c_cc[VMIN]=1;
newtio.c_cc[VTIME]=0;
tcflush(comfd, TCIFLUSH);
tcsetattr(comfd,TCSANOW,&newtio);

print_status(comfd);
    
while(!need_exit) {
fd_set fds;
int ret;
FD_ZERO(&fds);
FD_SET(STDIN_FILENO, &fds);
FD_SET(comfd, &fds);


ret = select(comfd+1, &fds, NULL, NULL, NULL);
if(ret == -1) {
perror("select");
} else if (ret > 0) {
if(FD_ISSET(STDIN_FILENO, &fds)) {
need_exit = transfer_byte(STDIN_FILENO, comfd, 1);
}
if(FD_ISSET(comfd, &fds)) {
need_exit = transfer_byte(comfd, STDIN_FILENO, 0);
}
}

    }

tcsetattr(comfd,TCSANOW,&oldtio);
tcsetattr(STDIN_FILENO,TCSANOW,&oldkey);
close(comfd);

return 0;
}


int transfer_byte(int from, int to, int is_control) {
    char c;
int ret;
do {
ret = read(from, &c, 1); 
} while (ret < 0 && errno == EINTR);
if(ret == 1) {
if(is_control) {
if(c == '\x01') { // C-a
return -1;
} else if(c == '\x18') { // C-x
print_status(to);
return 0;
}
}
while(write(to, &c, 1) == -1) {
if(errno!=EAGAIN && errno!=EINTR) { perror("write failed"); break; }
}
} else {
fprintf(stderr, "\nnothing to read. probably port disconnected.\n");
return -2;
}
   struct tm *time_struct;
    time_t local;

    (void)time(&local);
    time_struct=localtime(&local);
    FILE *fp;
    fp =fopen("watt.txt", "a+");
  if(c == '\r'){fprintf(fp, "[%02d-%02d-%02d %02d:%02d:%02d] ",time_struct->tm_year+1900,time_struct->tm_mon+1, time_struct->tm_mday, time_struct->tm_hour, time_struct->tm_min, time_struct->tm_sec);}
       else{fprintf(fp, "%c", c);}
    fclose(fp);
  
    return 0;
}



Download: com.c   // <------- 원본 소스
Building: cc -o com com.c
Usage   : ./com /dev/device [speed]  // <-------- 사용방법
Example : ./com /dev/ttyS0 [115200]   //<-------- 예를들어서 이렇게( HPM-100A  경우 .com /dev/ttyUSB0 [9600]
Keys    : Ctrl-A - exit, Ctrl-X - display control lines status
Darcs   : darcs get http://tinyserial.sf.net/
Scr.shot: screenshot.png (8862 bytes)
형광펜 부분이 추가해준 소스 입니다.
추가해준 내용은 TIME 정보를 추가해주었습니다.
S //  start 
22003Vo //전압
10003Am //전류
22002Wa //와트
10001Pf //역률
60001Hz //주파수
E //  end
[2010-01-01 01:01:01] //시간

실제 실행 결과 
S:  21693Vo:  42412Am:  46461Wa:     21Wh:   5051Pf:  59981Hz:     01Kg:     01Eu  E[2011-01-05 07:57:32] 
S:  21693Vo:  42422Am:  46481Wa:     31Wh:   5051Pf:  59981Hz:     01Kg:     01Eu  E[2011-01-05 07:57:33] 
S:  21693Vo:  42422Am:  46481Wa:     31Wh:   5051Pf:  59971Hz:     01Kg:     01Eu  E[2011-01-05 07:57:33] 
S:  21693Vo:  42432Am:  46461Wa:     41Wh:   5041Pf:  59971Hz:     01Kg:     01Eu  E[2011-01-05 07:57:34] 




추가한 소스 분석
(형광펜 부분)
#include <time.h>  // 시간 함수를 사용하기 위한  header 파일
struct tm *time_struct;
/*
struct tm  //  time.h에 정의되어있는 tm  구조체 내부
134 {
135   int tm_sec;           /* Seconds. [0-60] (1 leap second) */
136   int tm_min;           /* Minutes. [0-59] */
137   int tm_hour;          /* Hours.   [0-23] */
138   int tm_mday;          /* Day.     [1-31] */
139   int tm_mon;           /* Month.   [0-11] */
140   int tm_year;          /* Year - 1900.  */ // 1990년 이후 경과 년수를 의미한다,
141   int tm_wday;          /* Day of week. [0-6] */ // 0은 일요일 ~ 6은 토요일을 의미
142   int tm_yday;          /* Days in year.[0-365] */
143   int tm_isdst;         /* DST.     [-1/0/1]*/
144 
145 #ifdef  __USE_BSD
146   long int tm_gmtoff;       /* Seconds east of UTC.  */
147   __const char *tm_zone;    /* Timezone abbreviation.  */
148 #else
149   long int __tm_gmtoff;     /* Seconds east of UTC.  */
150   __const char *__tm_zone;  /* Timezone abbreviation.  */
151 #endif
152 };
*/
time_t local;  //  time_t 시분초일월을 모두 숫자로 압축한 시간 전용 자료형이다.

(void)time(&local); //1970년 1월 1일 0시 후 경과된 초를 조사합니다.
 time_struct=localtime(&local); // time_t 형의 값을  tm  구조체형태로 변환 시켜준다.
 FILE *fp;
 fp =fopen("watt.txt", "a+"); //파일을 열되 마지막 위치한 부분부터 추가
 if(c == '\r'){ //엔터의 아스키 코드값이 '\r'이 들어오면 실행하라는 의미
//현재 시간을 연산해서 보여준다.
fprintf(fp, "[%02d-%02d-%02d %02d:%02d:%02d] ",time_struct->tm_year+1900,time_struct->tm_mon+1, time_struct->tm_mday, time_struct->tm_hour, time_struct->tm_min, time_struct->tm_sec);}
else{     
fprintf(fp, "%c", c);  //시리얼 통신으로 들어오는 문자를 파일에 저장해준다.
}
    fclose(fp);
 
LVS
안녕하세요, 오늘은 LVS  부하 분산 방법 중 DR을 설정하는 법에 대해 알아보겠습니다.

1. LVS 설정

1.1. VIP 및 DIP 설정

LVS에서는 크게 DIP (Director IP)와 VIP (Virtual IP)를 설정해야 한다. 여기에서 DIP는 LVS가 고유하게 가질 IP 주소로서, NIC의 기본 IP 주소가 된다. 반면 VIP는 외부 Clients에게 서비스를 제공하기 위해 사용될 인터페이스 IP 주소 (즉, 외부 Clients가 접속할 IP 주소)로서, Aliasing을 통해 설정할 수 있으며, 부하를 상호 분산할 Real Server도 이 VIP를 반드시 가져야 한다.

따라서, 우선 VIP가 123.234.1.101 이고, DIP가 123.234.1.102 이라고 가정할 때,

># ifconfig eth0 123.234.1.102 netmask 255.255.255.0 up
># ifconfig eth0:1 123.234.1.101 netmask 255.255.255.0 up

위와 같이, ifconfig 명령을 이용하여 VIP와 DIP를 각각 설정한 다음, ifconfig로 설정사항을 확인한다. 단, 이미 eth0에 DIP가 설정되어 있는 경우가 대부분이므로, 이 단계에서는 VIP 만을 설정해도 된다.


1.2. Packet Forwarding 활성화

Dispatcher는 Client로부터 전송된 요청 메시지를 Real Server로 전달해야 하므로, 반드시 Packet Forwarding 기능이 활성화되어 있어야 한다. 이는 proc의 ip_forward의 값을 1로 설정함으로써 가능하다.

># echo 1 >/proc/sys/net/ipv4/ip_forward


1.3. Virtual Service 등록 (IPVSADM 설정)

># ipvsadm -A -t 123.234.1.101:80 -s rr



2. Real Server 설정


2.1. ARP Problem 해결 : ARP Hidden

패킷 전달 방식에 있어 NAT를 제외한 TUN 및 DR 방식은 모두 ARP Flux Problem을 해결해야 한다. 기본적으로 Dispatcher Node와 Real Server 모두 VIP를 갖고 있기 때문에, 클라이언트가 VIP의 MAC 주소를 묻는 ARP Request를 전송했을 때 Dispatcher와 Real Server 모두 이에 응답하게 되면 클라이언트는 이들 중 특정 서버의 MAC 주소를 해당 VIP에 해당하는 MAC 주소로 기억(ARP Caching)하고는 이 서버에게만 모든 서비스 요청 메시지를 전송한다. 결국 부하 분산을 더 이상 제공할 수 없으며, 임의의 클라이언트가 어떤 서버로부터 서비스를 제공받는지 관리할 수 없다.

결국 이를 해결하기 위해서는 클라이언트가 ARP 요청 메시지를 전송했을 때 Dispatcher만이 이에 응답하고, Real Server는 모두 ARP 요청 메시지를 무시해야 한다. 여기에서는 가장 간단한 ARP Hidden 방식을 소개한다.

일반적으로 Real Server에 VIP는 lo 장치에 Aliasing으로 설정되기 때문에, lo 장치와 다른 모든 인터페이스에 대해 ARP를 무시하도록 설정한다.

># echo 1 >/proc/sys/net/ipv4/conf/lo/arp_ignore
># echo 2 >/proc/sys/net/ipv4/conf/lo/arp_announce
># echo 1 >/proc/sys/net/ipv4/conf/all/arp_ignore
># echo 2 >/proc/sys/net/ipv4/conf/all/arp_announce

 
2.2. VIP 및 RIP 설정

Real Server는 고유의 IP 주소인 RIP (Real Server IP)를 갖고 있으며, Dispatcher는 RIP 주소를 기반으로 서비스 요청을 분산시킨다. 다만, Direct Routing 방식의 특성상 처리된 클라이언트의 요청은 Real Server에서 클라이언트로 직접 전달되므로(NAT 방식에서 Dispatcher를 거치는 것과는 달리), Real Server는 VIP 주소도 함께 lo 장치로서 갖고 있어야 한다. 따라서, 우선 VIP가 123.234.1.101 이고, RIP가 123.234.1.111 이라고 가정할 때,

># ifconfig eth0 123.234.1.111 netmask 255.255.255.0 up
># ifconfig lo:0 123.234.1.101 netmask 255.255.255.255 up
># ifconfig

위와 같이, ifconfig 명령을 이용하여 VIP와 RIP를 각각 설정한 다음, ifconfig로 설정사항을 확인한다. 단, 이미 eth0에 RIP가 설정되어 있는 경우가 대부분이므로, 이 단계에서는 VIP 만을 설정해도 된다.


2.3. Routing Table 설정

lo:0 장치에 VIP를 설정했으므로, Destination이 VIP인 요청 메시지를 lo:0 장치로 전달해야 한다. 그런 다음에는 이를 Real Server의 커널에 의해 처리된다.

># route add -host 123.234.1.101 dev lo:0

 

3. Real Server를 Virtual Service에 등록 (on Dispatcher Node)


3.1. Real Server를 등록하기

일반적인 Real Server를 Virtual Service에 등록하는 방식도 위와 동일하다. 다만 local host 대신 RIP를 입력하는 것만 차이가 있다.

># ipvsadm -a -t 123.234.1.101:80 -r 123.234.1.111 -g

 

4. Virtual Service 상태 모니터링


이상과 같이 Virtual Service에 Real Server가 등록된 이후에는 즉시 VIP를 통한 서비스 제공이 가능하다. 이때 Dispatcher Node에서 주기적으로 서비스 상태(# of Active Connection and # of Inactive Connection)를 계속해서 모니터링하고 싶은 경우 watch 명령을 이용한다.

># watch n -1 ipvsadm -Ln

 
LVS

안녕하세요, 오늘은 프로젝트 관련하여 LVS 구축에 대한 이론적인 부분을 기술하도록 하겠습니다.


1. LVS 구축  

가상서버를 구성하는 방안에는 그 방식에 따라 크게 3가지 다른 방법으로 구성할 수가 있습니다. NAT 를 이용하는 방법, IP터널링을 이용하는 방법, 다이렉트 라우팅을 이용하는 방법으로 각각의 장단점을 간단히 알아보도록 하겠습니다.

1). NAT 를 이용하는 방법

NAT(Network Address Translation) 방식은 패킷 내의 IP 주소를 변경해 부하 분산을 수행하는 방법입니다. 먼저 클라이언트에게는 로드 밸런서(Load Balancer)의 도메인 네임 또는 IP가 알려져 있습니다. 클라이언트가 이 알려진 도메인 네임이나 IP를 사용해 로드 밸런서에게 서비스 요청 패킷을 전송합니다. 또한 로드 밸런서는 n개의 서버 가운데 하나를 정해진 스케줄링 방법에 의해 선택한 후 패킷 내의 목적지 주소를 해당 서버의 IP로 다시 작성합니다. 리얼 서버는 클라이언트의 요청을 처리한 후, 로드 밸런서에게 응답을 돌려줍니다. 이때 로드 밸런서는 실제 응답의 발신지 주소를 다시 자신의 IP로 변경한 후 클라이언트에 서비스를 제공합니다.   

NAT에 의한 가상 서버의 장점은 실제 서버가 TCP/IP를 지원하는 어떠한 운영체제에서도 운용될 수 있다는 것입니다. 실제 서버는 사적 인터넷 주소체제(private Internet addresses)를 사용할 수 있고 단지 부하 분산기(load balancer)만 하나의 IP 주소가 필요합니다.  

단점은 확장성에 제한이 있다는 것입니다. 서버의 노드수가 수십개로 증가될 경우 병목이 발생할수 있습니다. 왜냐하면 패킷이 들어오고 나갈때마다 부하분산서버에서 패킷을 변경해야하기 때문입니다. 즉 항상 부하분산서버(Director)를 통과합니다. 로드 밸런서는 리얼 서버로 오가는 모든 패킷의 주소를 재작성해야 하므로 병목현상이 발생할 수 있습니다. 따라서 클러스터를 구성할 수 있는 서버의 개수에 제한을 받습니다. 이외에도 로드 밸런서가 다운될 경우 전체 서비스가 중단되는 문제가 발생합니다. 이를 방지하기 위해 일반적으로 ‘백업 로드 밸런서’를 두지만, 평상시는 이 고가의 서버를 사용하지 않으므로 낭비 요소가 됩니다. 방식은 하드웨어나 어플라이언스 형태로 제공되는 경우가 많으며, 대표적인 NAT 방식으로는 레이어4 스위치(OSI 7 계층 모델 가운데 4번 계층인 전송 계층에서 이뤄지는 스위칭 기법을 지원하는 네트워크 장비), 시스코의 로컬 디렉터, LVS(Linux Virtual Server)-NAT 방식 등이 있습니다.

 

 
<Figure 1. NAT 방식>

 

2). IP터널링을 이용하는 방법

IP터널링 방식에서는 NAT방식과는 다르게 부하분산서버는 들어오는 요청에 대해서 뒷단의 서버들(Real Server)에게 패킷을 전달하는 역할만 수행을 합니다. 뒷단의 서버들은 받은 요청에 대해서 다시 부하분산서버로 패킷을 주는 NAT방식과는 다르게 직접 그 요청의 처리를 수행하기 때문에 병목현상이 없어지게 됩니다. 이러한 IP터널링 방식을 설정하기 위해서는 Director 뿐만 아니라 Real Server에서 역시 IP터널링을 지원하도록 설정을 해주어야 합니다.

 

<Figure 2. IP Tunneling 방식>


3). 다이렉트 라우팅을 이용하는 방법

 

다이렉트 라우팅은 리얼 서버와 로드 밸런서가 가상 IP 주소를 공유합니다. 로드 밸런서와 리얼 서버는 네트워크 인터페이스에 가상 IP가 설정돼 있어야 하며, 이 인터페이스를 이용해 로드 밸런서는 요청 패킷을 받아들이고 스케줄링에 의해 선택된 리얼 서버로 직접 라우팅 합니다.

먼저 클라이언트가 가상 IP로 서비스 요청을 하면 로드 밸런서는 리얼 서버 가운데 하나를 스케줄링합니다. 그리고 선택된 서버로 직접 라우팅해 클라이언트의 요청을 리얼 서버로 전달합니다. 리얼 서버는 요청 사항을 처리한 후, 로드 밸런서를 거치지 않고 클라이언트로 직접 응답을 합니다. 각 리얼 서버는 로드 밸런서와 같은 가상 IP를 공유하고 있기 때문에, 로드 밸런서를 거치지 않고 클라이언트로 직접 응답할 수 있습니다.

이 방식은 로드 밸런서에서 병목현상이 NAT보다 적게 나타납니다. 로드 밸런서의 시스템 환경에 따라 차이가 있겠지만, 이론적으로는 100여 개의 서버를 클러스터로 구성할 수 있다고 합니다. 현재 대부분의 운영체제가 가상 IP 설정을 지원하므로 리얼 서버로 사용함에 있어 운영체제에 큰 제한을 받지 않습니다. 하지만 로드 밸런서뿐만 아니라 리얼 서버도 각각의 리얼 IP를 가지고 있어야 합니다. 그리고 NAT와 마찬가지로 백업 서버를 둬야 하는 부담이 있습니다. 특히 리눅스를 리얼 서버로 사용하는 경우 ARP 문제를 해결하기 위해 커널을 수정해 주어야 하는 경우가 있습니다. 참고로 다이렉트 라우팅 방식을 이용한 제품은 Resonate와 터보 클러스터, LVS-DR 등이 있습니다.

 

 

  <Figure 3. Direct Routing 방식>



2. 가상 서버 스케쥴링 알고리즘 

리눅스 가상서버에서 사용하는 스케쥴링 알고리즘을 설명합니다. 


1) Round-Robin Scheduling (라운드 로빈 스케쥴링)

말그대로 라운드-로빈 방식을 이용해 네트웍 연결을 서로 다른 서버에 연결하는 것을 말합니다. 이 경우 실제서버의 연결 갯수나 반응 시간 등은 고려를 하지 않습니다. 그렇지만 약간의 차이가 있습니다. 라운드 로빈 DNS는 단일한 도메인을 서로 다른 IP로 해석을 하지만, 스케쥴링의 기초는 호스트 기반이며 캐싱때문에 알고리즘을 효율적으로 사용하기 힘듭니다. 그래서 실제 서버사이에 동적인 부하 불균형이 심각해 질수 있습니다. 가상 서버의 스케쥴링 기초는 네트웍 기반이며 라운드 로빈 DNS 에 비해 훨씬 더 훌륭합니다.

2) Weighted Round-Robin Scheduling (가중치기반 라운드 로빈 스케쥴링)

가중치기반 라운드 로빈 스케쥴링은 실제 서버에 서로 다른 처리 용량을 지정할 수 있습니다. 각 서버에 가중치를 부여할 수 있으며, 여기서 지정한 정수값을 통해 처리 용량을 정합니다. 기본 가중치는 1입니다.

가중치가 있는 라운드 로빈 스케쥴링을 사용하면 실제 서버에서 네트웍 접속을 셀 필요가 없고 동적 스케쥴링 알고리즘보다 스케쥴링의 과부하가 적으므로 더 많은 실제 서버를 운영할 수 있습니다. 그러나 요청에 대한 부하가 매우 많을 경우 실제 서버사이에 동적인 부하 불균형 상태가 생길 수 있습니다.


  3) Least-Connection Scheduling (최소 접속 스케쥴링) 

최소 접속 스케쥴링은 가장 접속이 적은 서버로 요청을 직접 연결하는 방식을 말합니다. 각 서버에서 동적으로 실제 접속한 숫자를 세어야하므로 동적인 스케쥴링 알고리즘중의 하나입니다. 비슷한 성능의 서버로 구성된 가상 서버는 아주 큰 요구가 한 서버로만 집중되지 않기 때문에, 접속부하가 매우 큰 경우에도 아주 효과적으로 분산을 합니다.

가장 빠른 서버에서 더 많은 네트웍 접속을 처리할 수 있습니다. 그러므로 다양한 처리 용랑을 지닌 서버로 구성했을 경우에도 훌륭하게 작동 한다는 것을 한눈에 알 수 있을 것입니다. 그렇지만 실제로는 TCP의 TIME_WAIT 상태 때문에 아주 좋은 성능을 낼 수는 없습니다.


4) Weighted Least-Connection Scheduling (가중치 기반 최소 접속 스케쥴링)

가중치 기반 최소 접속 스케쥴링은 최소 접속 스케쥴링의 한 부분으로서 각각의 실제 서버에 성능 가중치를 부여할 수 있습니다. 언제라도 가중치가 높은 서버에서 더 많은 요청을 받을 수 있습니다. 가상 서버의 관리자는 각각의 실제 서버에 가중치를 부여할 수 있습니다. 가중치의 비율인 실제 접속자수에 따라 네트웍 접속이 할당됩니다. 기본 가중치는 1입니다.

가중치가 있는 최소 접속 스케줄링 알고리즘은 최소 접속 스케쥴링 알고리즘에 비해 부가적인 배분작업이 필요합니다. 서버들이 같은 처리 용량을 가졌을때는 작업 할당의 간접비용을 최소화 하기위해 최소 접속 스케쥴링과 가중치가 있는 최소 접속 스케쥴링 알고리즘 둘 다 사용할 수 있습니다.

 


참고 : http://hakkoo.net/zeroboard/zboard.php?id=study&page=6&sn1=&divpage=1&category=1&sn=off&ss=on&sc=on&select_arrange=headnum&desc=desc&no=419&PHPSESSID=34ad0df0e6670171141d75494595ea57

 

 
LVS

첨부:

Makefile - module programing Makefile 예시
ip_vs_rr.c - Round Robin 코드 (Linux kernel 원본에 존재)


sequence
1) ip_vs_rr.c를 적절히 수정.
2) Make 하여, Module 생성. (ip_vs_rr.ko)
3) 2)에서 생성한 파일을 /lib/module/(해당커널 버전)/kernel/net/netfilter/ipvs 로 복사
4) ipvs 실행 및 확인



팁 : 
1)  Line by Line
2) insmod : module 올림
    rmmod : mudule  내림
3) printk(""); 를 사용하여, 코드의 실행 부분 확인가능 - printk("") 는 dmesg 로 확인 가능
                   dmesg -c >> dmesg 출력 clear
    




/*

* Round-Robin Scheduling

*/

int i=1, n=1;

static struct ip_vs_dest *

ip_vs_rr_schedule(struct ip_vs_service *svc, const struct sk_buff *skb)

{

struct list_head *p, *q;

struct ip_vs_dest *dest;

IP_VS_DBG(6, "%s(): Scheduling...\n", __func__);

write_lock(&svc->sched_lock);

p = (struct list_head *)svc->sched_data;

if(i==n) // 이부분이 실행되면, 서버가 바뀜. 사용한 조건으로 인해, n번만큼 할당됨.

p = p->next;

q = p;

do {

/* skip list head */

if (q == &svc->destinations) {

q = q->next;

continue;

}

dest = list_entry(q, struct ip_vs_dest, n_list);

if (!(dest->flags & IP_VS_DEST_F_OVERLOAD) &&

atomic_read(&dest->weight) > 0)

/* HIT */

goto out;

q = q->next;

} while (q != p);

write_unlock(&svc->sched_lock);

IP_VS_ERR_RL("RR: no destination available\n");

return NULL;

out:

svc->sched_data = q;

write_unlock(&svc->sched_lock);

IP_VS_DBG_BUF(6, "RR: server %s:%u "

"activeconns %d refcnt %d weight %d\n",

IP_VS_DBG_ADDR(svc->af, &dest->addr), ntohs(dest->port),

atomic_read(&dest->activeconns),

atomic_read(&dest->refcnt), atomic_read(&dest->weight));

if(i==n){

n++;i=0;

}

i++;

return dest;

}

 


'LVS' 카테고리의 다른 글

IPVS Configuration : DR (Direct Routing)  (0) 2010.12.29
LVS 구축에 관한 이론 (NAT 부터 WLC 까지)  (2) 2010.12.23
커널 소켓 프로그래밍  (0) 2010.11.20
모듈 프로그래밍  (1) 2010.11.10
ipvsadm 소스 파일 make하기  (1) 2010.11.09
 
LVS

커널 소켓 프로그래밍

2010. 11. 20. 21:13
1. 서론

이번주는 CPU 점유율을 고려한 Round Robin 알고리즘 수정을 진행하였는데요, 그 중 핵심은 커널 소켓 프로그래밍이 되겠습니다. 제가 주중에 시험을 보는관계로, 여기에 내용을 정리해 두도록 하겠습니다.

2, 방법

Round Robin 알고리즘을 담고있는 ip_vs_rr.c 파일에, tcp_vs_h_dslb.c 의 일부 코드를 이식하는 작업을 진행해 나가면 됩니다.

2.1 이식되어야 할 주요 함수

가장 중요한 함수로는 sock_recvmsg()함수를 담고 있는
example_thread()

Thread 방식(?) 이기 때문에, thread를 시작하고 종료해주는
launch_thread()
kill_thread()
이 세 함수 정도가 가장 중요하다고 볼수 있겠구요, 나머지 연결된 함수들은 make 를 하시면서 하나씩 첨부해 주시면 되겠습니다.

연결된 함수들은 기본적으로 tcp_vs_h_dslb.h 내에서 참고하여 이식하는 방법이 가장 좋으며, 없을 경우 해당 함수를 품고있는 헤더파일을 찾아 include 시켜주는 방법이 좋겠습니다.

커널이 2.4에서 2.6으로 업그레이드 되면서 몇가지 struct 구조에 변화가 생겼습니다. 그것들은 에러가 났을경우 구글링을 통해 검색하셔서 조금씩 수정해주시면 되겠습니다.(argument 문제가 두곳, struct의 변화가 두군데 정도 되었던것으로 기억이 납니다;;)

이식을 끝내신후에는 ip_vs_rr.c 모듈에 시작 함수(module_init 부분 참조) launch_thread()를 첨부하여 thread를 동작하게 만들어 주시고, 종료 함수(module_exit(?) 부분 참조) 에는 kill_thread()를 첨부하여 모듈이 종료되었을 경우 thread또한 종료될수 있도록 만들어 주시기 바랍니다.

P.S. : kill_thread()내부의 kill_proc() 함수가 커널버젼이 바뀌면서 적용이 되지 않고있는데, 아직 이문제는 해결이 되지않았습니다. 때문에 모듈제거가 안되요..ㅠㅠ 해결해 주세요!

3, 결과 확인

최종적으로 만드신 ip_vs_rr 모듈을 실제로 올려보시고, host_info로 만든 어플리케이션을 동작시킨 후 실제로 커널에서 메세지를 수신하고 있는지 dmesg 확인합니다. (printk()는 example_thread() 함수 내부에 주석 처리되어있는 두 곳중 cpu_use 관련 내용을 출력하는 곳의 주석을 해제하시면 되겠습니다.)

NCLab 화이팅!

'LVS' 카테고리의 다른 글

LVS 구축에 관한 이론 (NAT 부터 WLC 까지)  (2) 2010.12.23
RR 알고리즘 개선 -> N번째 N번 부하 분산  (0) 2010.12.20
모듈 프로그래밍  (1) 2010.11.10
ipvsadm 소스 파일 make하기  (1) 2010.11.09
LVS server 구축  (2) 2010.11.06
 
LVS

모듈 프로그래밍

2010. 11. 10. 21:10

어느 프로그램이든지 만들려면 Makefile을 만들어야 합니다. 디바이스 드라이버도 역시 Makefile을 만들어 놓고 사용하면 편리합니다. 더욱이 커널 2.6에서는 gcc 를 이용하여 빌드하는 것이 아니기 때문에 반드시 Makefile을 만들어야 한다고 합니다.

저도 간단하게 프로그램을 작성해서 컴파일하려고 Makefile을 만들어 보았습니다.

Makefile 오류

MS-Windos에서 매우 편리한 RAPID 툴만 사용했던 저로서는 Makefile을 만들어야 한다는 것이 별로 반갑지 않습니다. 반갑지 않은 정도가 아니라 성가시고 때로는 알지 못하는 에러 때문에 고생해야 했습니다.

일반 에플리케이션을 만들기 위한 Makefile은 나름대로 큰 수정없이 사용할 수 있는 Makefile을 만들어서 사용할 정도가 되었습니다만 디바이스 드라이버를 학습하면서, 이것도 새로운 영역이라고 새로운 Makefile 형식에 사뭇 당황했습니다. 이것 또 암호문이네...으~

나름 빠르게 해본다고 책에 딸려 온 CD에 있는 소스 파일을 복사해서 main.c를 만들고 역시 CD에 있는 Makefile을 수정했습니다만 제대로 컴파일이 되질 않더군요. 가장 큰 잘못은 책에서 Makefile 부분을 대충 내용만 흝어 본 것이었습니다. 다시 책 내용을 정독하고 Makefile을 수정하고 컴파일해 보았지만 아래와 같은 에러가 계속 나더군요.

]$ make
make -C /lib/modules/2.6.9-42.0.8.ELsmp/build SUBDIRS=/home/jwjw/prjs/forum_sample/115 DeviceDriver modules
make[1]: Entering directory `/usr/src/kernels/2.6.9-42.0.8.EL-smp-i686'
make[1]: *** 타겟 `DeviceDriver'를 만들 규칙이 없음. 멈춤.
make[1]: Leaving directory `/usr/src/kernels/2.6.9-42.0.8.EL-smp-i686'
make: *** [default] 오류 2
]$ make

아~! 정말 미치겟더군요. 이런 기억이 예전에도 있었습니다. 바로  Tab 문자 때문이었죠.

그렇다면 이번에 발생한 에러는 무엇 때문일까요?

make[1]: *** 타겟 `DeviceDriver'를 만들 규칙이 없음. 멈춤.

문제는 바로 작업 디렉토리에 공백 문자가 있어서 였습니다. 공백만 없다면 한글 이름의 디렉토리 명을 사용해도 컴파일할 수가 있었습니다.

115_디바이스드라이버]$ make
make -C /lib/modules/2.6.9-42.0.8.ELsmp/build SUBDIRS=/home/jwjw/prjs/forum_sample/115_디바이스드라이버 modules
make[1]: Entering directory `/usr/src/kernels/2.6.9-42.0.8.EL-smp-i686'
CC [M] /home/jwjw/prjs/forum_sample/115_디바이스드라이버/main.o
Building modules, stage 2.
MODPOST
CC /home/jwjw/prjs/forum_sample/115_디바이스드라이버/main.mod.o
LD [M] /home/jwjw/prjs/forum_sample/115_디바이스드라이버/main.ko
make[1]: Leaving directory `/usr/src/kernels/2.6.9-42.0.8.EL-smp-i686'
115_디바이스드라이버]$

다른 프로그램 컴파일에서는 현재 디렉토리 정보가 중요하지 않았습니다만 디바이스 드라이버, 즉 모듈 프로그래밍에서는 현재의 디렉토리 명을 Makefile에 넘겨 주어야 하므로 Makefile 이 헤깔리지 않도록 디렉토리 명에 공백 문자가 있어서는 안되겠습니다.

또한 커널 별로 컴파일하는 방법이 다릅니다. 다음에 소개되는 커널별 Makefile을 참고하시기 바라며, 혹 사용하시는 시스템의 커널 버전을 확인하시려면 uname -r 을 이용하십시오.

]$ uname -r
2.6.9-42.0.8.ELsmp
]$

Makefile에 대해서는 리 눅스 디바이스 드라이버, 저자 유영창의 책 내용 중 4장 4절의 내용을 그대로 사용하면서 읽기 편하게 편집했습니다. 원저자에 누를 끼치지 않기 설명 글은 원문 그대로 가져 오지 않고 요점만 간추렸음을 말씀드립니다.

2.4 커널 외부 모듈 컴파일 용 Makefile

우선 Makefile의 전체 모습을 보시겠습니다. 예에서는 소스 파일 이름이 test.c 라고 가정하겠습니다.

KERNELDIR   = /lib/modules/$(shell uname -r)/build
CFLAGS = -D__KERNEL__ -DMODULE -I$(KERNELDIR)/include -O

all: test.o

clean:
rm -rf *.o
Makefile 설  명
KERNELDIR = /lib/modules/... 모듈은 커널과 밀접하게 연관되어 있으므로 버전 문제를 방지하기 위해서는 모듈이 동작하는 커널 소스와 일치시켜야 하는데, 이를 위해 컴파일할 모듈이 참조할 커널 소스의 디렉토리를 지정한다.
CFLAGS = -D__KERNEL__ ...

D__KERNEL__ : 커널과 관련된 내용을 활성화 하라
DMODULE : 모듈과 관련된 내용을 활성화 하라
O : 최적화 옵션

test.o 만들어낼 오브젝트
clean:
    rm -rf $*.o
컴파일 결과로 생긴 모든 파일을 삭제

test.o 가 하나의 소스가 아니라 여러 개의 소스 파일로 만들어 진다면 아래와 같이 내용을 추가하시면 됩니다.

all: test.o

test.o : test1.o test2.o test3.o

2.6 커널 외부 모듈 컴파일 용 Makefile

2.6 커널 용은 2.4보다 조금 복잡해 보이는 듯 합니다. 프로그램 소스가 어디에 있는지도 보여 주어야 하고 말이죠. 그러다 보니 위에서 말씀드린 Makefile 오류 경험도 했습니다만 내용을 보면 모듈을 만들어 내기 위해서 2.4보다는 더욱 전문적이고 체계적으로 보입니다.

KERNELDIR = /lib/modules/$(shell uname -r)/build

obj-m := test.o

KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)

default:
$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules

clean:
rm -rf *.ko
rm -rf *.mod.*
rm -rf .*.cmd
rm -rf *.o
Makefile 설  명
KERNELDIR = /lib/modules/... 모듈은 커널과 밀접하게 연관되어 있으므로 버전 문제를 방지하기 위해서는 모듈이 동작하는 커널 소스와 일치시켜야 하는데, 이를 위해 컴파일할 모듈이 참조할 커널 소스의 디렉토리를 지정한다.
obj-m := test.o

생성할 모듈 이름

KDIR := /lib/modules/$(shel ... 커널의 소스 위치
PWD := $(shell pwd) 컴파일 대상 프로그램 소스 위치
default:
   $(MAKE) -C $(KDIR) ...
모듈 컴파일 명령
clean:
   rm -rf *.ko .....
컴파일 결과로 생긴 모든 파일을 삭제

test.o 가 하나의 소스 파일로 되어 있지 않고 여러 파일로 만들어져 잇다면 아래와 같이 행을 추가하시면 됩니다.

obj-m	:= test.o
module-objs := test1.o test2.o test3.o

이제 컴파일해서 모듈을 만들어 내는 방법까지 알게 되었으므로 다음 시간부터는 디바이스 드라이버 프로그램에 작성에 대해 하나씩 알아 보도록 하겠습니다.

 

태그: *디바이스드라이버

참조: http://forum.falinux.com/zbxe/?document_srl=406210&mid=device_driver

'LVS' 카테고리의 다른 글

LVS 구축에 관한 이론 (NAT 부터 WLC 까지)  (2) 2010.12.23
RR 알고리즘 개선 -> N번째 N번 부하 분산  (0) 2010.12.20
커널 소켓 프로그래밍  (0) 2010.11.20
ipvsadm 소스 파일 make하기  (1) 2010.11.09
LVS server 구축  (2) 2010.11.06
 
LVS
 http://www.linuxvirtualserver.org/
사이트를 접속해서 ipvsadm-1.25.tar.gz 을 받습니다.

압축을 풀어 줍니다.

압춘 폴더에 들어가 make를 실행시켜주면

[root@namyong ipvsadm-1.25]# make install
make -C libipvs
make[1]: Entering directory `/home/namyong/바탕화면/ipvsadm-1.25/libipvs'
gcc -Wall -Wunused -Wstrict-prototypes -g -fPIC -DLIBIPVS_USE_NL  -DHAVE_NET_IP_VS_H -c -o libipvs.o libipvs.c
In file included from libipvs.h:13,
                 from libipvs.c:23:
ip_vs.h:15:29: error: netlink/netlink.h: 그런 파일이나 디렉터리가 없습니다
ip_vs.h:16:31: error: netlink/genl/genl.h: 그런 파일이나 디렉터리가 없습니다
ip_vs.h:17:31: error: netlink/genl/ctrl.h: 그런 파일이나 디렉터리가 없습니다
In file included from libipvs.h:13,
                 from libipvs.c:23:
같은 에러가 발생합니다.

위 에러를 해결하기 위해서는 http://www.infradead.org/~tgr/libnl/에
들어가서 libnl-2.0.tar.gz 파일을 다운로드 하여 압축을 풀어줍니다.

libnl은 netlink socket를 다루는 프로그램을 위한 라이브러리입니다.

압축이 풀린 폴더를 들어가보면 inclued/netlink/version.h.in이라는 파일이 있습니다.
그 파일의 이름을 version.h로 변경해줍니다.
그리고 include폴더를 통째로
/usr/include에 덮어쓰기를 합니다.

덮어쓸때 에러가 발생한다면 chmod 명령어를 이용해 잠깐 권한을 풀어준 후 다시 시도하면 됩니다.

덮어쓰기가 끝나면

make clean을 한후
make를 실행시키면 됩니다.

혹시 make 실행 도중 popt.h 파일 등등 기타 파일을 찾을수 없다고 나오면
chmod - R 777 /usr/include와 같이 권한을 잠시 풀어주고 다시 시도하면 해결할 수 있습니다.


'LVS' 카테고리의 다른 글

LVS 구축에 관한 이론 (NAT 부터 WLC 까지)  (2) 2010.12.23
RR 알고리즘 개선 -> N번째 N번 부하 분산  (0) 2010.12.20
커널 소켓 프로그래밍  (0) 2010.11.20
모듈 프로그래밍  (1) 2010.11.10
LVS server 구축  (2) 2010.11.06
 
LVS

LVS server 구축

2010. 11. 6. 14:50


 

 IP 설정 및 구축

  ◇LVS server(ipvsdm) : LAN1 - IP Addr   : 220.70.2.xxx             >> Real address

                                                  - Netmask : 255.255.255.0

                                                  - Gateway : 220.70.2.254

                                                  - DNS       : 168.126.63.1

 

                                        LAN2 - IP Addr    : 192.168.1.1             >> Virtual address

                                                 - Netmask : 255.255.255.0

 

 ◇Real Server1      :     IP Addr   : 192.168.1.2

                                     Netmask : 255.255.255.0

                                    Gateway : 192.168.1.1

                                    DNS       : 192.168.1.1

 

 ◇Real Server2     :     IP Addr   : 192.168.1.3

                                     Netmask : 255.255.255.0

                                    Gateway : 192.168.1.1

                                    DNS       : 192.168.1.1

 





 

 LVS Server 설정

◇ipvsadm 설치

  # yum -y install ipvsadm

 

동작확인

# ipvsadm

IP Virtual Server version 1.2.1 (size=4096)

ProtProt LocalAddress:Port Scheduler Flags

-> RemoteAddress:Port Forward Weight ActiveConn InActConn

 

◇시스템을 통해 다른 시스템으로패킷이 포워딩 될수 있도록 수정

# vi  /etc/sysctl.conf

     net.ipv4.ip_forward=1

        -> 적용

            # sysctl -p



ipvsadm 구동

  # service  ipvsadm  start

 

ipvsadm 설정

# ipvsadm -A -t  220.70.2.xxx:80 -s rr

# ipvsadm -a -t  220.70.2.xxx:80 -r 192.168.1.2:80 -m

# ipvsadm -a -t  220.70.2.xxx:80 -r 192.168.1.3:80 -m

                                                                       >> -m : 마스커레이드 방법 사용

 

>>> 목록 저장 - SELinux를 disabled 시켜야 함.

    #service ipvsadm save


SELinux disabled :
  #vim /etc/selinux/config

line#7  SELINUX=enforcing    -->> SELINUX=disabled 로 수정



 

ipvsadm 설정 확인

# ipvsadm -L

IP Virtual Server version 1.2.11.2.1 (size=4096)

Prot LocalAddress:Port Scheduler Flags

-> RemoteAddress:Port Forward Weight ActiveConn InActConn

TCP 220.70.2.x:http rr

-> 192.168.1.2:http Route 1 0 0

-> 192.168.1.3:http Route 1 0 0

 

 

 REAL Server 설정

server 페이지 만들기

# vim /var/www/html/index.html

<내용 입력, 저장 후 종료(wq)>

 

server 구동

 # service httpd start

 

 

 

 

 

 

 

 

 

1. ipvsadm의 기본 문법

- 서비스

ipvsadm [명령] [프로토콜 옵션] [서비스] [스케줄링 방법] [세션유지 옵션]

 

- 리얼서버

ipvsadm [명령] [프로토콜 옵션] [서비스] -r [리얼서버 옵션] [가중치 옵션]

 

서비스/리얼서버 관련 >명령<

-A : 서비스 추가

-E : 서비스 수정

-D : 서비스 삭제

-a : 리얼서버 추가

-e : 리얼서버 수정

-d : 리얼서버 삭제

 

 

>프로토콜<

-t     TCP

-u    UDP

-f     firewall 서비스 추가(NAT 방식에서 사용)



기타

-s [ rr | wrr | lc | wlc | lblc | lblcr | dh | sh | sed | nq ] : 스케줄링 방법을 설정한다.

-p [timeout]: 세션 타임아웃 설정

-r [서버주소:서비스포트] : 리얼서버를 설정한다.

-w [가중치] : 가중치를 설정한다.

   

설정 확인

-L : 서비스/리얼서버 리스트를 출력한다.

-n : 숫자형식(호스트네임->아이피주소)로 출력한다.

-c : 현재 연결을 출력한다.

   

입/출력

-S : 표준출력(화면프린트)으로 현재 설정을 내보낸다.

-R : 표준입력을 통해 설정을 가져온다

 

 

2. 스케줄링 방식 정리(rr,wrr,lc,wlc)

ipvsadm에서 지원하는 스케줄링 방식은 총 8개가 있지만 이 문서에서는 네개의 방식만 정리.

(1) rr

round-robin방식은 가장 기본적인 스케줄링 방식으로 "순차적으로" 작업을 각각의 노드에 분배하는 방식이다. 만약 3 대의 노드(A,B,C)에 round robin 방식을 사용하면, A->B->C->A->B->C와 같이 실행이 된다.

 

 

(2) wrr

노드별로 가중치를 설정하여 작업을 순차적으로 분배하는 방식으로 여러대의 서버가 각각 성능이 다른경우 성능에 맞춰 가중치를 설정해 주면 성능이 낮은 서버에 과도한 부하가 걸리는 것을 방지할 수 있다.

 

 

(3) lc

"현재 활성화 되어있는(ESTABLISHED)" 접속량이 적은 서버부터 작업을 할당하는 방식으로, 불필요한 유휴자원이 발생하는 것을 막을 수 있다. 만약 접속이 오래 유지되지 않는 경우(http) 이 방식을 사용하면, A->A->A->A->...와 같이 리스트 위쪽에 있는 노드 위주로 작업이 할당된다.

 

 

(4) wlc

노드별로 가중치를 설정하여 접속량이 적은 서버부터 작업을 할당하는 방식으로 lc보다 효율적인 자원분배가 가능하다.

 

 

3. 타임아웃

(1)세션 타임아웃

ipvsadm에서 -p 옵션을 이용하여 어떤 서비스에 세션 타임아웃 시간을 설정하면, 한번 세션이 열린후 타임아웃 시간 내에 동일한 주소에서 세션이 다시 열렸을 때 처음에 접속했던 RIP로 접속이 된다.

   

(2)Idle 타임아웃

ipvsadm --set옵션을 이용하면 프로토콜의 Idle timeout 시간을 설정할 수 있다. timeout 시간이 지나면 연결이 끊어지게 된다. 예를 들어,

ivpsadm --set 10 20 30

로 설정을 해 주게 되고 ftp연결후 10초간 아무것도 하지 않으면 연결이 끊어진다. 이는 http와 같이 한번 보내면 해당 연결이 끊겨도 별 문제가 없는 서비스의 경우는 상관없지만, ftp, nfs와 같은 서비스를 사용할 때에는 신중하게 값을 설정해야 한다.

 





'LVS' 카테고리의 다른 글

LVS 구축에 관한 이론 (NAT 부터 WLC 까지)  (2) 2010.12.23
RR 알고리즘 개선 -> N번째 N번 부하 분산  (0) 2010.12.20
커널 소켓 프로그래밍  (0) 2010.11.20
모듈 프로그래밍  (1) 2010.11.10
ipvsadm 소스 파일 make하기  (1) 2010.11.09
 

New Post