• multi masters의 단일 진입점인 LoadBalancer(LB) 구성 → 사실상 HA 클러스터 구성
  • 192.168.0.100 IP를 사용하는 서버에 Nginx를 이용하여 LB 구성
  • Nginx docker 이미지를 이용하여 LB 사용 → docker 컨테이너 운영 관리에 쉬움

1. nginx 구성 파일을 만들어서 master들의 단일 진입점을 구성

# nginx로 Load Balancer할 수 있게 nginx.conf 파일 생성
$ mkdir /etc/nginx
$ cat << END > /etc/nginx/nginx.conf
events {}
stream {
  upstream stream_backend {
    least_conn;
    server 192.168.0.200:6443;
    server 192.168.0.201:6443;
    server 192.168.0.202:6443;
  }

  server {
    listen    6443;
    proxy_pass stream_backend;
    proxy_timeout 300s;
    proxy_connect_timeout 1s;
  }
}
END

2. 도커 컨테이너로 NGINX를 실행하면서 LB를 운영

# nginx 컨테이너 실행
$ docker run --name proxy -v /etc/nginx/nginx.conf:/etc/nginx/nginx.cof:ro --restart=always -p 6443:6443 -d nginx
Unable to find image 'nginx:latest' locally
latest: Pulling from library/nginx
b380bbd43752: Pull complete
fca7e12d1754: Pull complete
745ab57616cb: Pull complete
a4723e260b6f: Pull complete
1c84ebdff681: Pull complete
858292fd2e56: Pull complete
Digest: sha256:644a70516a26004c97d0d85c7fe1d0c3a67ea8ab7ddf4aff193d9f301670cf36
Status: Downloaded newer image for nginx:latest
4324e6beaef0bd05d76af525fa415c4bcdf34fb807e4280e952108bf0a957630


# nginx 컨테이너가 실행 중 확인
$ docker ps
CONTAINER ID   IMAGE     COMMAND                  CREATED          STATUS          PORTS                                               NAMES
4324e6beaef0   nginx     "/docker-entrypoint.…"   54 seconds ago   Up 53 seconds   80/tcp, 0.0.0.0:6443->6443/tcp, :::6443->6443/tcp   proxy


# 통신이 되는지 확인
$ curl 192.168.0.100:6443

  • HAProxy는 모바일 환경이 발달 → 모바일 환경에서는 빠르고 유연한 확장성이 필수 요소
  • 최초 서비스 구축 시 확장성을 고려해서 L4/L7 스위치 분산까지는 고려
  • 지역간 분산(GSLB 구성)을 고려해서 설계하는 것은 일부 업체를 제외하고는 비용 및 경험 부재로 어려움
  • 'DNS + HAProxy + 클라우드 서비스'를 조합하여 서비스를 진행하면 적은 비용으로 GSLB 구성과 동일한 수준으로 구축 가능
  • HAProxy를 이해하기 위해서 로드 밸런서의 기본 개념과 HAProxy의 동작 방식을 이해 필요


1. 오픈 소스 로드 밸런서 HAProxy

  • HAProxy는 기존의 하드웨어 스위치를 대체하는 소프트웨어 로드 밸런서
  • 네트워크 스위치에서 제공하는 L4, L7 기능 및 로드 밸런서 기능을 제공
  • HAProxy는 설치가 쉽고, 환경 설정도 어렵지 않음
  • 서비스 이중화를 빠르게 구성 가능

2. 로드 밸런싱이란

  • 로드 밸런싱이란 부하 분산을 위해서 가상(virtual) IP를 통해 여러 서버에 접속하도록 분배하는 기능
  • 로드 밸런싱에서 사용하는 주요 기술

2.1. NAT(Network Address Translation)

  • 사설 IP 주소를 공인 IP 주소로 바꾸는 데 사용하는 통신망의 주소 변조기

2.2. DSR(Dynamic Source Routing protocol)

  • 로드 밸런서 사용 시 서버에서 클라이언트로 되돌아가는 경우 목적지 주소를 스위치의 IP 주소가 아닌 클라이언트의 IP 주소로 전달해서 네트워크 스위치를 거치지 않고 바로 클라이언트를 전달

2.3. Tunneling

  • 인터넷상에서 눈에 보이지 않는 통로를 만들어 통신할 수 있게 하는 개념
  • 데이터를 캡슐화해서 연결된 상호 간에만 캡슐화된 패킷을 구별해 캡슐화를 해제 가능


3. 로드 밸런서 동작 방식

  • 네트워크에서 IP 주소와 MAC 주소를 이용해 목적지(destination) IP 주소를 찾아가고 출발지로 되돌아오는 구조
  • 4가지의 로드 밸런서 동작 방식 설명

3.1. Bridge/Transparent Mode

  • 사용자(client)가 서비스(server)를 요청하면 L4로 전달된 목적지 IP 주소를 real server IP 주소로 변조하고, MAC 주소를 변조해서 목적지를 찾아가는 방식
    3.1.1. 요청 전달 시 변조

      사용자(client) → L4 → NAT(IP/MAC 주소 변조) → real server(server)

    3.1.2. 응답 전달 시 변조

      real server(server) → NAT → L4 → 사용자

3.2. Router Mode

  • Bridge/Transparent Mode와 유사하지만 출발지(source) MAC 주소도 변조

3.3. One Arm Mode

  • 사용자(client)가 real server(server)에 접근할 때 목적지 IP는 L4 스위치 IP를 설정
  • L4에 도달하면 L4가 클라이언트에게 받은 목적지 IP 주소를 L4 IP 주소에서 real server IP와 real server MAC 주소로 변조
  • 응답으로 되돌아가는 IP는 L4의 IP pool의 IP 주소로 변조

3.4. DSR (Direct Server Return) Mode

  • 사용자가 real server(server)에 접근할 때 출발지와 목적지의 IP 주소를 변조하지 않고, L4에서 관리하는 real server의 MAC 주소 테이블을 확인해서 MAC 주소만 변조


4. HAProxy 동작 방식

  • HAProxy는 기본적으로 reverse proxy 형태로 동작
  • 브라우저에서 사용하는 proxy는 클라이언트 앞에서 처리하는 기능 → forward proxy라 칭함
  • reverse proxy의 역할은 서비스 요청에 대해서 서버 앞 단에 존재하여, 서버로 들어오는 요청을 대신 받아서 서버에 전달하고 요청한 곳에 결과를 다시 전달하는 것
  • HAProxy의 동작 방식을 알면, HAProxy를 이용해서 어떤 구조로 확장 가능한지 알 수 있음
  • HAProxy의 동작 흐름
    1. 최초 접근 시 서버에 요청 전달
    2. 응답 시 쿠키(cookie)에 서버 정보 추가 후 반환
    3. 재요청 시 proxy에서 쿠키 정보 확인 → 최초 요청 서버로 전달
    4. 다시 접근 시 쿠키 추가 없이 전달 → 클라이언트에 쿠키 정보가 계속 존재함(쿠키 재사용)


5. HAProxy HA(High availability) 구성

  • HAProxy는 기본적으로 VRRP(Virtual Router Redundancy Protocol)를 지원
  • HAProxy는 초당 8만 건 정도의 연결을 처리 가능
  • HAProxy는 소프트웨어 기반의 솔루션이기 때문에 HAProxy가 설치된 서버에서 문제가 발생하면 하드웨어 L4보다는 불안정
  • HA 구성으로 master HAProxy에 문제가 생기는 경우에 slave HAProxy에서 서비스가 원활하게 제공되어야함

5.1. HAProxy HA(High availability) 기본 구성 설명 → HAProxy 무정지하기 위한 Active - Standby 구성

  • 가상 IP 주소를 공유하는 active HAProxy 서버와 standby HAProxy 서버가 heartbeat를 주고 받으면서 서로 정상적으로 동작하는지 여부를 확인
  • active 상태의 서버에 문제가 발생하면 standby HAProxy가 active 상태로 변경
  • 기존 active HAProxy의 가상 IP 주소를 standby HAProxy가 가져오면서 서비스가 무정지 상태를 유지
  • 1초 정도의 순단 현상은 발생


5.2. HAProxy HA(High availability) 기본 구성이 단일 HAPRoxy와 다른 점

  • 단일 HAProxy와 다른 점은 최초 접근 시 쿠키에 바로 서버 정보를 입력하지 않고 서버에서 jsessionid가 전달될 때 서버 정보를 합쳐서 전달
  • 쿠키에 정보 추가 없고 X-Forwarded-For에 정보 추가
  • 쿠키에 추가 없음
  • Jsessionid 추가
  • 서버 정보 + jsessionid를 쿠키에 추가
  • 쿠키에서 서버 판별 후 jsessionid만 전달




6. L4 + HAProxy HA 구성 및 Global 환경에서 구성

  • HAProxy와 기존 하드웨어 스위치를 이용해서 더 확장된 형태의 고가용성 구조를 설계 가능

6.1. 하드웨어 L4 + HAProxy

  • 클라이언트에서 연결되는 부분은 가상 IP 주소 + L4의 구성으로 하드웨어 이중화를 구축
  • L4에서 서버 앞 단에 HAProxy를 구축해서 HAProxy를 더 확장할 수 있는 구조로 설계 가능
  • L4 + HAProxy 구성


6.2. GSLB(Global Service Load Balancing) + HAProxy

  • global 서비스가 증가되면서 IDC 간 이중화 및 global 환경에서의 무정지 서비스를 위한 DR(Disaster Recovery, 재해 복구) 시스템 구축이 필수 요구사항
  • GSLB + HAProxy를 이용하면 global한 무정지 서비스 구축이 가능
  • GSLB 구축에 L4 스위치를 사용할 수도 있지만 GSLB 구성용 L4는 고가의 장비
  • L4를 이용한 GSLB 대신 DNS(BIND)를 이용한 구축 형태 → Global 환경에서 GSLB+HAProxy 구성
    1. 클라이언트에서 DNS로 도메인 조회
    2. 근거리 IDC 정보 전달




7. HAProxy Ncloud 적용 사례

  • 사내 Ncloud 시스템의 DNS RR을 HAProxy로 교체 → DNS를 HA 구성
  • DNS를 HA 구성함으로써 기존 DNS로 구성할 때보다 서버 증설 및 삭제에 있어 유연성이 증가, 서비스 안정성 증가
  • DNS를 HA로 구성함으로써 애플리케이션 서버에서 클라이언트 IP 주소를 찾아서 처리하는 로직에 문제가 발생
    • 문제 발생 이유 → 사용자의 요청이 HAProxy를 거치면서 클라이언트 IP 주소 정보가 HAProxy의 정보로 변조
    • 해결 방안 → 애플리케이션 서버가 HTTP 헤더에서 클라이언트 IP 주소를 조회하면 실제 클라이언트 IP 주소가 반환되도록 보완
      1. HAProxy에서 제공하는 X-Forwarded-For 옵션을 적용
      2. Apache에서 mod_rpaf 모듈을 설치




8. HAProxy 어드민 페이지

9. HAProxy 성능

  • HAProxy가 설치되는 서버의 사양에 따라 어느 정도 성능이 제공되는지 확인하기 위해 성능을 측정
  • nGrinder(http://www.nhnopensource.org/ngrinder/) 테스트 결과와 해외 사례를 공유

1. 해외 운영 사례

  • 서버 사양(저사양) : Dell PowerEdge 2850, Intel PCI3 4x Gigabit Fiber card (CPU 1.6GHz dual core x 1)
  • 제공 서비스: 영화 다운로드 서비스
  • 결과: 일별 2.47TB 전송 + 81일 동안 운영

2. nGrinder 테스트

  • 환경: HAProxy (1core), apache 서버 2대
  • 결과: 6,603 TPS


3. 해외 성능 사례

  • 듀얼 CPU 환경에서 초당 2만 세션까지 연결 가능 → CPU 성능이 높아지면 연결 가능 세션 수 증가
  • 초당 1만 건 세션 연결 시 응답에 100밀리초(ms) 소요 → 최대 연결 개수는 하드웨어의 RAM과 file descriptor에 의해 결정됨 → 세션당 16KB 사용 시 6만 세션당 1G RAM 필요




HAProxy 설치 및 옵션 → HAProxy의 중요 옵션 설명

1. HAProxy 운영 시 필요한 옵션

  • HAProxy의 경우 튜닝 옵션을 비롯하여 매우 많은 옵션을 지원 → 실제 구축 시 필요한 옵션만 설명
  • 옵션을 변경하려면 haproxy.cfg 파일을 수정 필요
  • HAProxy 설정 매뉴얼 → https://runebook.dev/ko/docs/haproxy/-index-

2. HAProxy 옵션

  • 전역 옵션(global) 섹션과 기본 옵션(defaults) 섹션, 프록시 옵션 섹션(listen)의 주요 옵션에 관한 설명
  • global # 전역 옵션 섹션
    1. daemon → 백그라운드 모드(background mode)로 실행
    2. log → syslog 설정
    3. log-send-hostname → hostname 설정
    4. uid → 프로세스의 userid를 number로 변경
    5. user → 프로세스의 userid를 name으로 변경
    6. node → 두 개 이상의 프로세스나 서버가 같은 IP 주소를 공유할 때 name 설정(HA 설정)
    7. maxconn → 프로세스당 최대 연결 개수
  • Defaults # 기본 옵션 섹션
    1. log → syslog 설정
    2. maxconn → 프로세스당 최대 연결 개수
  • listen
    1. listen webfarm 10.101.22.76:80→ listen haproxy name ip:port
    2. mode http → 연결 프로토콜
    3. option httpchk → health check
    4. option log-health-checks → health 로그 남김 여부
    5. option forwardfor → 클라이언트 정보 전달
    6. option httpclose → keep-alive 문제 발생 시 off 옵션
    7. cookie SERVERID rewrite → 쿠키로 서버 구별 시 사용 여부
    8. cookie JSESSIONID prefix → HA 구성 시 prefix 이후에 서버 정보 주입 여부
    9. balance roundrobin → 순환 분배 방식
    10. stats enable → 서버 상태 보기 가능 여부
    11. stats uri /admin → 서버 상태 보기 uri
    12. server xvadm01.ncli 10.101.22.18:80 cookie admin_portal_1 check inter 1000 rise 2 fall 5 → real server 정보(server [host명] [ip]:[port] cookie [서버쿠키명] check inter [주기(m/s)] rise [서버구동여부점검횟수], fall [서비스중단여부점검횟수])

3. balance 옵션

  • 로드 밸런싱의 경우 round robin 방식을 일반적으로 사용하지만 다른 여러 방식이 있음 → 옵션에 적용할 수 있는 로드 밸런싱 알고리즘
    1. roundrobin → 순차적으로 분배(최대 연결 가능 서버 4128개)
    2. static-rr → 서버에 부여된 가중치에 따라서 분배
    3. leastconn → 접속 수가 가장 적은 서버로 분배
    4. source → 운영 중인 서버의 가중치를 나눠서 접속자 IP를 해싱(hashing)해서 분배
    5. uri → 접속하는 URI를 해싱해서 운영 중인 서버의 가중치를 나눠서 분배(URI의 길이 또는 depth로 해싱)
    6. url_param → HTTP GET 요청에 대해서 특정 패턴이 있는지 여부 확인 후 조건에 맞는 서버로 분배(조건 없는 경우 round robin으로 처리)
    7. hdr → HTTP 헤더 에서 hdr()으로 지정된 조건이 있는 경우에 대해서만 분배(조건 없는 경우 round robin으로 처리)
    8. rdp-cookie → TCP 요청에 대한 RDP 쿠키에 따른 분배

참고 URL : https://d2.naver.com/helloworld/284659


  • VM 2대에 Keepalived를 설치하여 간단하게 HA를 구성하는 방안

1. Keepalived 기본 설명

  • Keepalived는 가상 IP(VIP; Virtual IP)를 기반으로 작동
  • 마스터 서버(Master Server)를 모니터링하다 해당 노드에 장애가 발생했을 시, 스탠바이 서버(Standby Server)로 페일오버(failover)되도록 지원

2. Keepalived 구성

  • node 1, node 2라는 서버 2대를 생성
  • node 1 서버를 MASTER 서버, node 2서버를 BACKUP 서버로 설정
  • 두 서버는 서로 간의 헬스 체크를 수행하다가 MASTER 서버에 문제가 생겼을 시에 BACKUP 서버가 VIP(Virtual IP)를 Take-Over 하면서 지속적으로 서비스가 운영될 수 있도록 구성
  • eth0에 IP Alias를 사용할 경우, IP Spoofing으로 인식하고 해당 VM의 네트워크 통신을 끊어버리기 때문에, 위와 같은 구성을 하기 위해선 일단 VM에 추가 인터페이스 할당 후, VIP(Virtual IP)를 생성 필요
  • Keepalived를 통한 HA 구성을 하기 전에 아래 사전 작업 진행 필요


3. 3가지 작업을 통해 Keepalived를 활용한 HA 구성을 위한 사전 준비

  1. Private Subnet 생성 → 192.168.100.0/24 대역
  2. node 1, node 2 서버에 추가 인터페이스 할당 → 기존 인터페이스 사용 가능
  3. node 1, node 2 서버에 VIP 할당

3.1. Private Subnet 생성

  • node 1, node 2 서버는 192.168.100.0/24 대역의 Private Subnet을 구성

3.2. node 1, node 2에 추가 인터페이스 할당

  • node1 서버와 node 2 서버에 추가 인터페이스를 할당
    1. node 1 (MASTER) → 192.168.100.101
    2. node 2 (BACKUP) → 192.168.100.102

3.3. node 1, node 2에 VIP(Virtual IP) 할당

  • node 1, node 2에 VIP(Virtual IP)를 할당
  • VIP는 192.168.100.250으로 설정 → 각각 node 1, node2에 eth1:0 인터페이스에 추가
  • node1의 eth1:0에 192.168.100.250 추가

    # node1에서 동작 -> 일시적으로 VIP 추가
    $ ifconfig eth1:0 192.168.100.250 netmask 255.255.255.0
    
    # node1에서 동작 -> 영구적으로 VIP 추가
    $ cp /etc/sysconfig/network-scripts/ifcfg-eth1 /etc/sysconfig/network-scripts/ifcfg-eth1:0
    $ vi /etc/sysconfig/network-scripts/ifcfg-eth1:0
    DEVICE=eth1:0
    ONBOOT=yes
    BOOTPROTO=static
    IPADDR=192.168.100.250
    NETMASK=255.255.255.0
    
    # 네트워크 재시작
    $ systemctl restart network
    
    # node1에서 정상적으로 설정되었는지 확인
    $ ifconfig
    eth1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
            inet 192.168.100.101  netmask 255.255.255.0  broadcast 192.168.100.255
            inet6 fe80::a00:27ff:fe30:d141  prefixlen 64  scopeid 0x20<link>
            ether 08:00:27:30:d1:41  txqueuelen 1000  (Ethernet)
            RX packets 11  bytes 1971 (1.9 KiB)
            RX errors 0  dropped 0  overruns 0  frame 0
            TX packets 22  bytes 2342 (2.2 KiB)
            TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
    
    eth1:0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
            inet 192.168.100.250  netmask 255.255.255.0  broadcast 192.168.100.255
            ether 08:00:27:30:d1:41  txqueuelen 1000  (Ethernet)
  • node2의 eth1:0에 192.168.100.250 추가

    # node2에서 동작 -> 일시적으로 VIP 추가
    $ ifconfig eth1:0 192.168.100.250 netmask 255.255.255.0
    
    # node2에서 동작 -> 영구적으로 VIP 추가
    $ cp /etc/sysconfig/network-scripts/ifcfg-eth1 /etc/sysconfig/network-scripts/ifcfg-eth1:0
    $ vi /etc/sysconfig/network-scripts/ifcfg-eth1:0
    DEVICE=eth1:0
    ONBOOT=yes
    BOOTPROTO=static
    IPADDR=192.168.100.250
    NETMASK=255.255.255.0
    
    # 네트워크 재시작
    $ systemctl restart network
    
    # node1에서 정상적으로 설정되었는지 확인
    $ ifconfig
    eth1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
            inet 192.168.100.102  netmask 255.255.255.0  broadcast 192.168.100.255
            inet6 fe80::a00:27ff:fe44:4b29  prefixlen 64  scopeid 0x20<link>
            ether 08:00:27:44:4b:29  txqueuelen 1000  (Ethernet)
            RX packets 6  bytes 1107 (1.0 KiB)
            RX errors 0  dropped 0  overruns 0  frame 0
            TX packets 22  bytes 2342 (2.2 KiB)
            TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
    
    eth1:0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
            inet 192.168.100.250  netmask 255.255.255.0  broadcast 192.168.100.255
            ether 08:00:27:44:4b:29  txqueuelen 1000  (Ethernet)



4. Keepalived 설치와 설정 파일 수정을 통해 node 1과 node 2에 대한 HA 구성

  • Keepalived를 node1, node2에 설치
  • 설치를 마친 후, Keepalived의 설정 파일을 수정

4.1. Keepalived 설정 내용 → 설정 내용 적용 후 Keepalived 서비스를 node1, node2에 실행

  • MASTER 서버의 Priority는 200으로 설정, BACKUP 서버의 Priority는 100 설정 → Priority 값이 높은 쪽이 MASTER 서버가 됨
  • auth_pass 및 virtual_router 값은 MASTER 서버와 BACKUP 서버 모두 동일해야함 → 해당 값은 default 값으로 그대로 유지
  • auth_pass는 간단하게 '1010'으로 설정
  • auth_pass 값도 MASTER 서버와 BACKUP 서버가 동일하게 설정해야함
  • virtual_ipaddress에는 VIP(192.168.100.250)로 설정해야함

4.2. node 1 (MASTER 서버)

  • MASTER 서버의 Keepalived 패키지 설치 및 실행

    $ yum -y update
    
    # Keepalived 패키지 설치
    $ yum -y install keepalived
    
    # Keepalived 설정 파일 수정
    $ vi /etc/keepalived/keepalived.conf
    ! Configuration File for keepalived
    
    vrrp_instance VI_1 {
        state MASTER
        interface eth1
        virtual_router_id 51
        priority 200
        advert_int 1
        authentication {
            auth_type PASS
            auth_pass 1010
        }
        virtual_ipaddress {
            192.168.100.250
        }
    }
    
    # keepalived 실행
    $ systemctl start keepalived
    $ systemctl enable keepalived
    
    # keepalived 실행 상태 확인
    $ systemctl status keepalived
    ● keepalived.service - LVS and VRRP High Availability Monitor
       Loaded: loaded (/usr/lib/systemd/system/keepalived.service; enabled; vendor preset: disabled)
       Active: active (running) since Fri 2021-07-30 15:40:41 UTC; 11s ago
     Main PID: 1081 (keepalived)
       CGroup: /system.slice/keepalived.service
               ├─1081 /usr/sbin/keepalived -D
               ├─1082 /usr/sbin/keepalived -D
               └─1083 /usr/sbin/keepalived -D
    
    Jul 30 15:40:43 master1 Keepalived_vrrp[1083]: Sending gratuitous ARP on eth1 for 192.168.100.250
    Jul 30 15:40:43 master1 Keepalived_vrrp[1083]: Sending gratuitous ARP on eth1 for 192.168.100.250
    Jul 30 15:40:43 master1 Keepalived_vrrp[1083]: Sending gratuitous ARP on eth1 for 192.168.100.250
    Jul 30 15:40:43 master1 Keepalived_vrrp[1083]: Sending gratuitous ARP on eth1 for 192.168.100.250
    Jul 30 15:40:48 master1 Keepalived_vrrp[1083]: Sending gratuitous ARP on eth1 for 192.168.100.250
    Jul 30 15:40:48 master1 Keepalived_vrrp[1083]: VRRP_Instance(VI_1) Sending/queueing gratuitous...250
    Jul 30 15:40:48 master1 Keepalived_vrrp[1083]: Sending gratuitous ARP on eth1 for 192.168.100.250
    Jul 30 15:40:48 master1 Keepalived_vrrp[1083]: Sending gratuitous ARP on eth1 for 192.168.100.250
    Jul 30 15:40:48 master1 Keepalived_vrrp[1083]: Sending gratuitous ARP on eth1 for 192.168.100.250
    Jul 30 15:40:48 master1 Keepalived_vrrp[1083]: Sending gratuitous ARP on eth1 for 192.168.100.250
    Hint: Some lines were ellipsized, use -l to show in full.

4.3.node 2 (BACKUP 서버)

  • BACKUP 서버의 Keepalived 패키지 설치 및 실행

    $ yum -y update
    
    # Keepalived 패키지 설치
    $ yum -y install keepalived
    
    # Keepalived 설정 파일 수정
    $ vi /etc/keepalived/keepalived.conf
    ! Configuration File for keepalived
    
    vrrp_instance VI_1 {
        state BACKUP
        interface eth1
        virtual_router_id 51
        priority 100
        advert_int 1
        authentication {
            auth_type PASS
            auth_pass 1010
        }
        virtual_ipaddress {
            192.168.100.250
        }
    }
    
    # keepalived 실행
    $ systemctl start keepalived
    $ systemctl enable keepalived
    
    # keepalived 실행 상태 확인
    $ systemctl status keepalived
    ● keepalived.service - LVS and VRRP High Availability Monitor
       Loaded: loaded (/usr/lib/systemd/system/keepalived.service; enabled; vendor preset: disabled)
       Active: active (running) since Fri 2021-07-30 15:40:41 UTC; 11s ago
     Main PID: 1075 (keepalived)
       CGroup: /system.slice/keepalived.service
               ├─1075 /usr/sbin/keepalived -D
               ├─1076 /usr/sbin/keepalived -D
               └─1077 /usr/sbin/keepalived -D
    
    Jul 30 15:40:41 master2 Keepalived_vrrp[1077]: Registering Kernel netlink reflector
    Jul 30 15:40:41 master2 Keepalived_vrrp[1077]: Registering Kernel netlink command channel
    Jul 30 15:40:41 master2 Keepalived_vrrp[1077]: Registering gratuitous ARP shared channel
    Jul 30 15:40:41 master2 Keepalived_vrrp[1077]: Opening file '/etc/keepalived/keepalived.conf'.
    Jul 30 15:40:41 master2 Keepalived_vrrp[1077]: VRRP_Instance(VI_1) removing protocol VIPs.
    Jul 30 15:40:41 master2 Keepalived_vrrp[1077]: Using LinkWatch kernel netlink reflector...
    Jul 30 15:40:41 master2 Keepalived_vrrp[1077]: VRRP_Instance(VI_1) Entering BACKUP STATE
    Jul 30 15:40:41 master2 Keepalived_vrrp[1077]: VRRP sockpool: [ifindex(3), proto(112), unicast...1)]
    Jul 30 15:40:41 master2 Keepalived_healthcheckers[1076]: Initializing ipvs
    Jul 30 15:40:41 master2 Keepalived_healthcheckers[1076]: Opening file '/etc/keepalived/keepaliv...'.
    Hint: Some lines were ellipsized, use -l to show in full.



5. 첫 번째 테스트 → Keepalived가 잘 실행되는지 확인

  • node 1, node 2와 같은 Private Subnet(192.168.100.0/24 대역) 안에 있는 테스트 서버(192.168.100.201) 1대를 구성
  • 생성한 VIP(192.168.100.250)로 PING 테스트를 하는 도중에, node 1(MASTER server)을 shut down

5.1. 테스트 서버(192.168.100.201)에서 VIP(192.168.100.250)로 PING 테스트 진행

# 테스트 서버(192.168.100.201)에서 ping 테스트
$ ping 192.168.100.250
PING 192.168.100.250 (192.168.100.250) 56(84) bytes of data.
64 bytes from 192.168.100.250: icmp_seq=1 ttl=64 time=0.335 ms
64 bytes from 192.168.100.250: icmp_seq=2 ttl=64 time=0.392 ms
64 bytes from 192.168.100.250: icmp_seq=3 ttl=64 time=0.477 ms
64 bytes from 192.168.100.250: icmp_seq=4 ttl=64 time=0.469 ms
64 bytes from 192.168.100.250: icmp_seq=5 ttl=64 time=0.337 ms
64 bytes from 192.168.100.250: icmp_seq=6 ttl=64 time=0.322 ms
64 bytes from 192.168.100.250: icmp_seq=7 ttl=64 time=0.720 ms    <----- Node1(MASTER 서버)에서 Node2 (BACKUP서버)로 fail-over
64 bytes from 192.168.100.250: icmp_seq=8 ttl=64 time=0.330 ms
64 bytes from 192.168.100.250: icmp_seq=9 ttl=64 time=0.417 ms
64 bytes from 192.168.100.250: icmp_seq=10 ttl=64 time=0.407 ms
64 bytes from 192.168.100.250: icmp_seq=11 ttl=64 time=0.314 ms
64 bytes from 192.168.100.250: icmp_seq=12 ttl=64 time=0.270 ms
64 bytes from 192.168.100.250: icmp_seq=13 ttl=64 time=0.404 ms
64 bytes from 192.168.100.250: icmp_seq=14 ttl=64 time=0.346 ms
64 bytes from 192.168.100.250: icmp_seq=15 ttl=64 time=0.271 ms

5.2. node2 (Backup 서버)에서 /var/log/messages 확인 결과, node 1(Master 서버)의 VIP(192.168.100.201)를 Take-Over 한것 확인 가능

# node2에서 확인
$ tail -f /var/log/messages
Jul 30 15:50:45 master2 Keepalived_vrrp[1077]: VRRP_Instance(VI_1) Transition to MASTER STATE
Jul 30 15:50:46 master2 Keepalived_vrrp[1077]: VRRP_Instance(VI_1) Entering MASTER STATE
Jul 30 15:50:46 master2 Keepalived_vrrp[1077]: VRRP_Instance(VI_1) setting protocol VIPs.
Jul 30 15:50:46 master2 Keepalived_vrrp[1077]: Sending gratuitous ARP on eth1 for 192.168.100.250
Jul 30 15:50:46 master2 Keepalived_vrrp[1077]: VRRP_Instance(VI_1) Sending/queueing gratuitous ARPs on eth1 for 192.168.100.250
Jul 30 15:50:46 master2 Keepalived_vrrp[1077]: Sending gratuitous ARP on eth1 for 192.168.100.250
Jul 30 15:50:46 master2 Keepalived_vrrp[1077]: Sending gratuitous ARP on eth1 for 192.168.100.250
Jul 30 15:50:46 master2 Keepalived_vrrp[1077]: Sending gratuitous ARP on eth1 for 192.168.100.250
Jul 30 15:50:46 master2 Keepalived_vrrp[1077]: Sending gratuitous ARP on eth1 for 192.168.100.250
Jul 30 15:50:51 master2 Keepalived_vrrp[1077]: Sending gratuitous ARP on eth1 for 192.168.100.250
Jul 30 15:50:51 master2 Keepalived_vrrp[1077]: VRRP_Instance(VI_1) Sending/queueing gratuitous ARPs on eth1 for 192.168.100.250
Jul 30 15:50:51 master2 Keepalived_vrrp[1077]: Sending gratuitous ARP on eth1 for 192.168.100.250
Jul 30 15:50:51 master2 Keepalived_vrrp[1077]: Sending gratuitous ARP on eth1 for 192.168.100.250
Jul 30 15:50:51 master2 Keepalived_vrrp[1077]: Sending gratuitous ARP on eth1 for 192.168.100.250
Jul 30 15:50:51 master2 Keepalived_vrrp[1077]: Sending gratuitous ARP on eth1 for 192.168.100.250
Jul 30 15:51:03 master2 Keepalived_vrrp[1077]: VRRP_Instance(VI_1) Received advert with higher priority 200, ours 100
Jul 30 15:51:03 master2 Keepalived_vrrp[1077]: VRRP_Instance(VI_1) Entering BACKUP STATE
Jul 30 15:51:03 master2 Keepalived_vrrp[1077]: VRRP_Instance(VI_1) removing protocol VIPs.



6. 두 번째 테스트 → Keepalived가 잘 실행되는지 확인

  • node 1, node 2와 같은 Private Subnet(192.168.100.0/24 대역) 안에 있는 테스트 서버(192.168.100.201) 1대를 구성
  • 생성한 VIP(192.168.100.250)로 PING 테스트를 하는 도중에, node 1(MASTER server)의 keepalived 서비스 down 후 트래픽 확인
  • node 1(MASTER server)의 keepalived 서비스 restart 후 트래픽 확인

6.1. 테스트 서버(192.168.100.201)에서 VIP(192.168.100.250)로 PING 테스트 진행

# 테스트 서버(192.168.100.201)에서 ping 테스트
$ ping 192.168.100.250
PING 192.168.100.250 (192.168.100.250) 56(84) bytes of data.
64 bytes from 192.168.100.250: icmp_seq=1 ttl=64 time=0.332 ms
64 bytes from 192.168.100.250: icmp_seq=2 ttl=64 time=0.250 ms
64 bytes from 192.168.100.250: icmp_seq=3 ttl=64 time=0.443 ms
64 bytes from 192.168.100.250: icmp_seq=4 ttl=64 time=0.517 ms
64 bytes from 192.168.100.250: icmp_seq=5 ttl=64 time=0.373 ms
64 bytes from 192.168.100.250: icmp_seq=6 ttl=64 time=0.481 ms
64 bytes from 192.168.100.250: icmp_seq=7 ttl=64 time=0.442 ms
64 bytes from 192.168.100.250: icmp_seq=8 ttl=64 time=0.382 ms
64 bytes from 192.168.100.250: icmp_seq=9 ttl=64 time=0.445 ms

6.2. 일반적인 상황에서 테스트 서버에서 보낸 트래픽이 node1 (Master 서버)의 VIP(eth1:0)로만 트래픽 인입 확인 → node 2(Backup 서버)에는 트래픽 전송 X

# node1 (Master 서버)의 VIP로만 트래픽 인입 확인
$ tcpdump -i eth1:0
16:52:20.057581 IP worker > master1: ICMP echo request, id 19909, seq 1, length 64
16:52:20.057609 IP master1 > worker: ICMP echo reply, id 19909, seq 1, length 64
16:52:20.292151 IP master1 > vrrp.mcast.net: VRRPv2, Advertisement, vrid 51, prio 200, authtype simple, intvl 1s, length 20
16:52:21.061380 IP worker > master1: ICMP echo request, id 19909, seq 2, length 64
16:52:21.061402 IP master1 > worker: ICMP echo reply, id 19909, seq 2, length 64
16:52:21.293746 IP master1 > vrrp.mcast.net: VRRPv2, Advertisement, vrid 51, prio 200, authtype simple, intvl 1s, length 20
16:52:22.062916 IP worker > master1: ICMP echo request, id 19909, seq 3, length 64
16:52:22.062938 IP master1 > worker: ICMP echo reply, id 19909, seq 3, length 64
16:52:22.295863 IP master1 > vrrp.mcast.net: VRRPv2, Advertisement, vrid 51, prio 200, authtype simple, intvl 1s, length 20

# node2 (Backup 서버)는 대기 -> master에서 상태 체크만 함
$ tcpdump -i eth1:0
16:02:23.931301 IP master1 > vrrp.mcast.net: VRRPv2, Advertisement, vrid 51, prio 200, authtype simple, intvl 1s, length 20

6.3. node1 (Master 서버)의 keepalived 서비스 down→ node 2(Backup 서버)에 트래픽 인입 확인

$ systemctl stop keepalived

# node1 (Master 서버)는 대기 -> backup에서 상태 체크만 함
$ tcpdump -i eth1:0
16:54:00.591586 IP master2 > vrrp.mcast.net: VRRPv2, Advertisement, vrid 51, prio 100, authtype simp


# node1 (Master 서버)의 /var/log/messages 내용 -> HA Master 종료 확인
$ tail -f /var/log/messages
Jul 30 16:55:02 master1 kernel: device eth1 left promiscuous mode
Jul 30 16:55:29 master1 systemd: Stopping LVS and VRRP High Availability Monitor...
Jul 30 16:55:29 master1 Keepalived[4960]: Stopping
Jul 30 16:55:29 master1 Keepalived_vrrp[4962]: VRRP_Instance(VI_1) sent 0 priority
Jul 30 16:55:29 master1 Keepalived_vrrp[4962]: VRRP_Instance(VI_1) removing protocol VIPs.
Jul 30 16:55:29 master1 Keepalived_healthcheckers[4961]: Stopped
Jul 30 16:55:30 master1 Keepalived_vrrp[4962]: Stopped
Jul 30 16:55:30 master1 Keepalived[4960]: Stopped Keepalived v1.3.5 (03/19,2017), git commit v1.3.5-6-g6fa32f2
Jul 30 16:55:30 master1 systemd: Stopped LVS and VRRP High Availability Monitor.


# node2 (Backup 서버)의 VIP로만 트래픽 인입 확인
$ tcpdump -i eth1:0
16:53:30.532423 IP master2 > vrrp.mcast.net: VRRPv2, Advertisement, vrid 51, prio 100, authtype simple, intvl 1s, length 20
16:53:30.548001 IP worker > master2: ICMP echo request, id 19910, seq 18, length 64
16:53:30.548028 IP master2 > worker: ICMP echo reply, id 19910, seq 18, length 64
16:53:31.549477 IP worker > master2: ICMP echo request, id 19910, seq 19, length 64
16:53:31.549499 IP master2 > worker: ICMP echo reply, id 19910, seq 19, length 64
16:53:31.549530 IP master2 > vrrp.mcast.net: VRRPv2, Advertisement, vrid 51, prio 100, authtype simple, intvl 1s, length 20
16:53:32.549784 IP worker > master2: ICMP echo request, id 19910, seq 20, length 64
16:53:32.549824 IP master2 > worker: ICMP echo reply, id 19910, seq 20, length 64
16:53:32.549972 IP master2 > vrrp.mcast.net: VRRPv2, Advertisement, vrid 51, prio 100, authtype simple, intvl 1s, length 20


# node2 (Backup 서버)의 /var/log/messages 내용 -> HA Backup 시작 확인
$ tail -f /var/log/messages
Jul 30 16:55:29 master2 Keepalived_vrrp[1077]: VRRP_Instance(VI_1) Transition to MASTER STATE
Jul 30 16:55:30 master2 Keepalived_vrrp[1077]: VRRP_Instance(VI_1) Entering MASTER STATE
Jul 30 16:55:30 master2 Keepalived_vrrp[1077]: VRRP_Instance(VI_1) setting protocol VIPs.
Jul 30 16:55:30 master2 Keepalived_vrrp[1077]: Sending gratuitous ARP on eth1 for 192.168.100.250
Jul 30 16:55:30 master2 Keepalived_vrrp[1077]: VRRP_Instance(VI_1) Sending/queueing gratuitous ARPs on eth1 for 192.168.100.250
Jul 30 16:55:30 master2 Keepalived_vrrp[1077]: Sending gratuitous ARP on eth1 for 192.168.100.250
Jul 30 16:55:30 master2 Keepalived_vrrp[1077]: Sending gratuitous ARP on eth1 for 192.168.100.250
Jul 30 16:55:30 master2 Keepalived_vrrp[1077]: Sending gratuitous ARP on eth1 for 192.168.100.250
Jul 30 16:55:30 master2 Keepalived_vrrp[1077]: Sending gratuitous ARP on eth1 for 192.168.100.250
Jul 30 16:55:35 master2 Keepalived_vrrp[1077]: Sending gratuitous ARP on eth1 for 192.168.100.250
Jul 30 16:55:35 master2 Keepalived_vrrp[1077]: VRRP_Instance(VI_1) Sending/queueing gratuitous ARPs on eth1 for 192.168.100.250

6.4. node1 (Master 서버)의 keepalived 서비스 restart → node 1(Master 서버)에 트래픽 다시 인입 확인

$ systemctl restart keepalived

# node1 (Master 서버)의 VIP로만 트래픽 다시 인입 확인
$ tcpdump -i eth1:0
16:54:17.656150 IP master1 > vrrp.mcast.net: VRRPv2, Advertisement, vrid 51, prio 200, authtype simple, intvl 1s, length 20
16:54:18.626524 IP worker > master1: ICMP echo request, id 19910, seq 66, length 64
16:54:18.626563 IP master1 > worker: ICMP echo reply, id 19910, seq 66, length 64
16:54:18.657517 IP master1 > vrrp.mcast.net: VRRPv2, Advertisement, vrid 51, prio 200, authtype simple, intvl 1s, length 20
16:54:19.628842 IP worker > master1: ICMP echo request, id 19910, seq 67, length 64
16:54:19.628864 IP master1 > worker: ICMP echo reply, id 19910, seq 67, length 64
16:54:19.658916 IP master1 > vrrp.mcast.net: VRRPv2, Advertisement, vrid 51, prio 200, authtype simple, intvl 1s, length 20


# node1 (Master 서버)의 /var/log/messages 내용 -> HA Master 재시작 확인
$ tail -f /var/log/messages
Jul 30 16:58:51 master1 Keepalived[4985]: Starting Keepalived v1.3.5 (03/19,2017), git commit v1.3.5-6-g6fa32f2
Jul 30 16:58:51 master1 Keepalived[4985]: Opening file '/etc/keepalived/keepalived.conf'.
Jul 30 16:58:51 master1 Keepalived[4986]: Starting Healthcheck child process, pid=4987
Jul 30 16:58:51 master1 systemd: Started LVS and VRRP High Availability Monitor.
Jul 30 16:58:51 master1 Keepalived[4986]: Starting VRRP child process, pid=4988
Jul 30 16:58:51 master1 Keepalived_vrrp[4988]: Registering Kernel netlink reflector
Jul 30 16:58:51 master1 Keepalived_vrrp[4988]: Registering Kernel netlink command channel
Jul 30 16:58:51 master1 Keepalived_vrrp[4988]: Registering gratuitous ARP shared channel
Jul 30 16:58:51 master1 Keepalived_vrrp[4988]: Opening file '/etc/keepalived/keepalived.conf'.
Jul 30 16:58:51 master1 Keepalived_vrrp[4988]: VRRP_Instance(VI_1) removing protocol VIPs.
Jul 30 16:58:51 master1 Keepalived_vrrp[4988]: Using LinkWatch kernel netlink reflector...
Jul 30 16:58:51 master1 Keepalived_vrrp[4988]: VRRP sockpool: [ifindex(3), proto(112), unicast(0), fd(10,11)]
Jul 30 16:58:51 master1 Keepalived_healthcheckers[4987]: Opening file '/etc/keepalived/keepalived.conf'.
Jul 30 16:58:51 master1 Keepalived_vrrp[4988]: VRRP_Instance(VI_1) Transition to MASTER STATE
Jul 30 16:58:52 master1 Keepalived_vrrp[4988]: VRRP_Instance(VI_1) Entering MASTER STATE
Jul 30 16:58:52 master1 Keepalived_vrrp[4988]: VRRP_Instance(VI_1) setting protocol VIPs.
Jul 30 16:58:52 master1 Keepalived_vrrp[4988]: Sending gratuitous ARP on eth1 for 192.168.100.250
Jul 30 16:58:52 master1 Keepalived_vrrp[4988]: VRRP_Instance(VI_1) Sending/queueing gratuitous ARPs on eth1 for 192.168.100.250
Jul 30 16:58:52 master1 Keepalived_vrrp[4988]: Sending gratuitous ARP on eth1 for 192.168.100.250
Jul 30 16:58:52 master1 Keepalived_vrrp[4988]: Sending gratuitous ARP on eth1 for 192.168.100.250
Jul 30 16:58:52 master1 Keepalived_vrrp[4988]: Sending gratuitous ARP on eth1 for 192.168.100.250
Jul 30 16:58:52 master1 Keepalived_vrrp[4988]: Sending gratuitous ARP on eth1 for 192.168.100.250
Jul 30 16:58:57 master1 Keepalived_vrrp[4988]: Sending gratuitous ARP on eth1 for 192.168.100.250
Jul 30 16:58:57 master1 Keepalived_vrrp[4988]: VRRP_Instance(VI_1) Sending/queueing gratuitous ARPs on eth1 for 192.168.100.250
Jul 30 16:58:57 master1 Keepalived_vrrp[4988]: Sending gratuitous ARP on eth1 for 192.168.100.250
Jul 30 16:58:57 master1 Keepalived_vrrp[4988]: Sending gratuitous ARP on eth1 for 192.168.100.250
Jul 30 16:58:57 master1 Keepalived_vrrp[4988]: Sending gratuitous ARP on eth1 for 192.168.100.250
Jul 30 16:58:57 master1 Keepalived_vrrp[4988]: Sending gratuitous ARP on eth1 for 192.168.100.250


# node2 (Backup 서버)는 대기 -> master에서 상태 체크만 함
$ tcpdump -i eth1:0
16:54:37.679862 IP master1 > vrrp.mcast.net: VRRPv2, Advertisement, vrid 51, prio 200, authtype simple, intvl 1s, length 20


# node2 (Backup 서버)의 /var/log/messages 내용 -> HA backup이 Master로 빼았김 확인
$ tail -f /var/log/messages
Jul 30 16:58:51 master2 Keepalived_vrrp[1077]: VRRP_Instance(VI_1) Received advert with higher priority 200, ours 100
Jul 30 16:58:51 master2 Keepalived_vrrp[1077]: VRRP_Instance(VI_1) Entering BACKUP STATE
Jul 30 16:58:51 master2 Keepalived_vrrp[1077]: VRRP_Instance(VI_1) removing protocol VIPs.



7. 세 번째 테스트 → Keepalived가 잘 실행되는지 확인

  • master 네트워크 절단시 backup으로 인입 OK, master 네트워크 재시동 이후 트래픽이 자동으로 master에 오지 않음(keepalived 서비스 재시작 필요)
  • node 1, node 2와 같은 Private Subnet(192.168.100.0/24 대역) 안에 있는 테스트 서버(192.168.100.201) 1대를 구성
  • 생성한 VIP(192.168.100.250)로 PING 테스트를 하는 도중에, node 1(MASTER server)의 네트워크를 down
  • 일정 시간 지난 후 다시 네트워크를 up한 후 eth1:0 VIP를 구성
  • 네트워크가 up되어도 backup으로 계속 트래픽 발생 → master 서버로 돌아오지 않음

7.1. 테스트 서버(192.168.100.201)에서 VIP(192.168.100.250)로 PING 테스트 진행

# 테스트 서버(192.168.100.201)에서 ping 테스트
$ ping 192.168.100.250
PING 192.168.100.250 (192.168.100.250) 56(84) bytes of data.
64 bytes from 192.168.100.250: icmp_seq=1 ttl=64 time=0.335 ms
64 bytes from 192.168.100.250: icmp_seq=2 ttl=64 time=0.392 ms
64 bytes from 192.168.100.250: icmp_seq=3 ttl=64 time=0.477 ms
64 bytes from 192.168.100.250: icmp_seq=4 ttl=64 time=0.469 ms
64 bytes from 192.168.100.250: icmp_seq=5 ttl=64 time=0.337 ms
64 bytes from 192.168.100.250: icmp_seq=6 ttl=64 time=0.322 ms
64 bytes from 192.168.100.250: icmp_seq=7 ttl=64 time=0.720 ms    <----- Node1(MASTER 서버)에서 Node2 (BACKUP서버)로 fail-over
64 bytes from 192.168.100.250: icmp_seq=8 ttl=64 time=0.330 ms
64 bytes from 192.168.100.250: icmp_seq=9 ttl=64 time=0.417 ms
64 bytes from 192.168.100.250: icmp_seq=10 ttl=64 time=0.407 ms
64 bytes from 192.168.100.250: icmp_seq=11 ttl=64 time=0.314 ms
64 bytes from 192.168.100.250: icmp_seq=12 ttl=64 time=0.270 ms
64 bytes from 192.168.100.250: icmp_seq=13 ttl=64 time=0.404 ms
64 bytes from 192.168.100.250: icmp_seq=14 ttl=64 time=0.346 ms
64 bytes from 192.168.100.250: icmp_seq=15 ttl=64 time=0.271 ms
64 bytes from 192.168.100.250: icmp_seq=16 ttl=64 time=0.314 ms

7.2. 일반적인 상황에서 테스트 서버에서 보낸 트래픽이 node1 (Master 서버)의 VIP(eth1:0)로만 트래픽 인입 확인 → node 2(Backup 서버)에는 트래픽 전송 X

# node1 (Master 서버)의 VIP로만 트래픽 인입 확인
$ tcpdump -i eth1:0
15:58:09.214853 IP worker > master1: ICMP echo request, id 1015, seq 1, length 64
15:58:09.214893 IP master1 > worker: ICMP echo reply, id 1015, seq 1, length 64
15:58:10.121214 IP master1 > vrrp.mcast.net: VRRPv2, Advertisement, vrid 51, prio 200, authtype simple, intvl 1s, length 20
15:58:10.216097 IP worker > master1: ICMP echo request, id 1015, seq 2, length 64
15:58:10.216121 IP master1 > worker: ICMP echo reply, id 1015, seq 2, length 64
15:58:11.122587 IP master1 > vrrp.mcast.net: VRRPv2, Advertisement, vrid 51, prio 200, authtype simple, intvl 1s, length 20
15:58:11.216543 IP worker > master1: ICMP echo request, id 1015, seq 3, length 64
15:58:11.216564 IP master1 > worker: ICMP echo reply, id 1015, seq 3, length 64
15:58:12.123952 IP master1 > vrrp.mcast.net: VRRPv2, Advertisement, vrid 51, prio 200, authtype simple, intvl 1s, length 20
15:58:12.218872 IP worker > master1: ICMP echo request, id 1015, seq 4, length 64
15:58:12.218894 IP master1 > worker: ICMP echo reply, id 1015, seq 4, length 64


# node2 (Backup 서버)는 대기 -> master에서 상태 체크만 함
$ tcpdump -i eth1:0
16:02:23.931301 IP master1 > vrrp.mcast.net: VRRPv2, Advertisement, vrid 51, prio 200, authtype simple, intvl 1s, length 20

7.3. node1 (Master 서버)의 network down→ node 2(Backup 서버)에 트래픽 인입 확인

$ systemctl stop network

# node1 (Master 서버)의 VIP로는 어떠한 값도 들어오지 않음 -> 통신 절단
$ tcpdump -i eth1:0


# node2 (Backup 서버)의 VIP로만 트래픽 인입 확인
$ tcpdump -i eth1:0
16:06:43.837509 ARP, Request who-has master2 tell worker, length 46
16:06:43.837526 ARP, Reply master2 is-at 08:00:27:44:4b:29 (oui Unknown), length 28
16:06:43.837837 IP worker > master2: ICMP echo request, id 1015, seq 514, length 64
16:06:43.837854 IP master2 > worker: ICMP echo reply, id 1015, seq 514, length 64
16:06:44.001473 IP master1 > vrrp.mcast.net: VRRPv2, Advertisement, vrid 51, prio 200, authtype simple, intvl 1s, length 20
16:06:44.843864 IP worker > master2: ICMP echo request, id 1015, seq 515, length 64
16:06:44.843888 IP master2 > worker: ICMP echo reply, id 1015, seq 515, length 64
16:06:45.045735 IP master1 > vrrp.mcast.net: VRRPv2, Advertisement, vrid 51, prio 200, authtype simple, intvl 1s, length 20

7.4. node1 (Master 서버)의 network restart 이후 VIP(192.168.100.250) 다시 설정 → node 2(Backup 서버)에 트래픽 인입 확인

$ systemctl restart network


# node1 (Master 서버)의 eth1 인터페이스 up하면 자동으로 VIP가 생성되지 않음 -> eth1의 VIP(eth1:0)를 수동으로 설정 필요
$ ifconfig
eth1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.100.101  netmask 255.255.255.0  broadcast 192.168.100.255
        inet6 fe80::a00:27ff:fe30:d141  prefixlen 64  scopeid 0x20<link>
        ether 08:00:27:30:d1:41  txqueuelen 1000  (Ethernet)
        RX packets 625  bytes 59642 (58.2 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 1663  bytes 120386 (117.5 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0


# eth1의 VIP(eth1:0)를 수동으로 설정
$ ifconfig eth1:0 192.168.100.250 netmask 255.255.255.0
$ ifconfig
eth1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.100.101  netmask 255.255.255.0  broadcast 192.168.100.255
        inet6 fe80::a00:27ff:fe30:d141  prefixlen 64  scopeid 0x20<link>
        ether 08:00:27:30:d1:41  txqueuelen 1000  (Ethernet)
        RX packets 625  bytes 59642 (58.2 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 1679  bytes 121346 (118.5 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

eth1:0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.100.250  netmask 255.255.255.0  broadcast 192.168.100.255
        ether 08:00:27:30:d1:41  txqueuelen 1000  (Ethernet)


# # node1 (Master 서버)는 대기 -> backup에서 상태 체크만 함
$ tcpdump -i eth1:0
16:02:23.931301 IP master1 > vrrp.mcast.net: VRRPv2, Advertisement, vrid 51, prio 200, authtype simple, intvl 1s, length 20


# node2 (Backup 서버)의 VIP로만 트래픽 인입 확인
$ tcpdump -i eth1:0
16:06:43.837509 ARP, Request who-has master2 tell worker, length 46
16:06:43.837526 ARP, Reply master2 is-at 08:00:27:44:4b:29 (oui Unknown), length 28
16:06:43.837837 IP worker > master2: ICMP echo request, id 1015, seq 514, length 64
16:06:43.837854 IP master2 > worker: ICMP echo reply, id 1015, seq 514, length 64
16:06:44.001473 IP master1 > vrrp.mcast.net: VRRPv2, Advertisement, vrid 51, prio 200, authtype simple, intvl 1s, length 20
16:06:44.843864 IP worker > master2: ICMP echo request, id 1015, seq 515, length 64
16:06:44.843888 IP master2 > worker: ICMP echo reply, id 1015, seq 515, length 64
16:06:45.045735 IP master1 > vrrp.mcast.net: VRRPv2, Advertisement, vrid 51, prio 200, authtype simple, intvl 1s, length 20

7.5. 테스트 서버(192.168.100.201)에서 VIP(192.168.100.250)로 기존의 PING 테스트 세션을 종료 후 다시 진행해도 그대로 유지

# 테스트 서버(192.168.100.201)에서 ping 테스트
$ ping 192.168.100.250
PING 192.168.100.250 (192.168.100.250) 56(84) bytes of data.
64 bytes from 192.168.100.250: icmp_seq=1 ttl=64 time=0.335 ms
64 bytes from 192.168.100.250: icmp_seq=2 ttl=64 time=0.392 ms
64 bytes from 192.168.100.250: icmp_seq=3 ttl=64 time=0.477 ms
64 bytes from 192.168.100.250: icmp_seq=4 ttl=64 time=0.469 ms
64 bytes from 192.168.100.250: icmp_seq=5 ttl=64 time=0.337 ms
64 bytes from 192.168.100.250: icmp_seq=6 ttl=64 time=0.322 ms
64 bytes from 192.168.100.250: icmp_seq=7 ttl=64 time=0.720 ms
64 bytes from 192.168.100.250: icmp_seq=8 ttl=64 time=0.330 ms
64 bytes from 192.168.100.250: icmp_seq=9 ttl=64 time=0.417 ms
64 bytes from 192.168.100.250: icmp_seq=10 ttl=64 time=0.407 ms
64 bytes from 192.168.100.250: icmp_seq=11 ttl=64 time=0.314 ms
64 bytes from 192.168.100.250: icmp_seq=12 ttl=64 time=0.270 ms
64 bytes from 192.168.100.250: icmp_seq=13 ttl=64 time=0.404 ms
64 bytes from 192.168.100.250: icmp_seq=14 ttl=64 time=0.346 ms
64 bytes from 192.168.100.250: icmp_seq=15 ttl=64 time=0.271 ms
64 bytes from 192.168.100.250: icmp_seq=16 ttl=64 time=0.314 ms
64 bytes from 192.168.100.250: icmp_seq=17 ttl=64 time=0.491 ms
64 bytes from 192.168.100.250: icmp_seq=18 ttl=64 time=0.480 ms
64 bytes from 192.168.100.250: icmp_seq=19 ttl=64 time=0.389 ms
64 bytes from 192.168.100.250: icmp_seq=20 ttl=64 time=0.362 ms
64 bytes from 192.168.100.250: icmp_seq=21 ttl=64 time=0.392 ms
64 bytes from 192.168.100.250: icmp_seq=22 ttl=64 time=0.399 ms
64 bytes from 192.168.100.250: icmp_seq=23 ttl=64 time=0.381 ms
64 bytes from 192.168.100.250: icmp_seq=24 ttl=64 time=0.277 ms
64 bytes from 192.168.100.250: icmp_seq=25 ttl=64 time=0.570 ms
[...생략]
^C
--- 192.168.100.250 ping statistics ---
908 packets transmitted, 889 received, 2% packet loss, time 910037ms
rtt min/avg/max/mdev = 0.151/0.452/6.065/0.216 ms


# 테스트 서버(192.168.100.201)에서 다시 ping 테스트
$ ping 192.168.100.250
PING 192.168.100.250 (192.168.100.250) 56(84) bytes of data.
64 bytes from 192.168.100.250: icmp_seq=1 ttl=64 time=0.446 ms
64 bytes from 192.168.100.250: icmp_seq=2 ttl=64 time=0.608 ms
64 bytes from 192.168.100.250: icmp_seq=3 ttl=64 time=0.395 ms
64 bytes from 192.168.100.250: icmp_seq=4 ttl=64 time=0.452 ms
64 bytes from 192.168.100.250: icmp_seq=5 ttl=64 time=0.504 ms
64 bytes from 192.168.100.250: icmp_seq=6 ttl=64 time=0.483 ms

7.6. node 1(Master 서버)의 VIP(192.168.100.250)에 트래픽 다시 인입 확인

# node1 (Master 서버)는 대기 -> backup에서 상태 체크만 함
$ tcpdump -i eth1:0
16:41:59.321344 IP master2 > vrrp.mcast.net: VRRPv2, Advertisement, vrid 51, prio 100, authtype simple, intvl 1s, length 20


# node2 (Backup 서버)의 VIP로만 트래픽 인입 확인
$ tcpdump -i eth1:0
16:42:06.024044 IP worker > master2: ICMP echo request, id 19904, seq 45, length 64
16:42:06.024077 IP master2 > worker: ICMP echo reply, id 19904, seq 45, length 64
16:42:06.343284 IP master2 > vrrp.mcast.net: VRRPv2, Advertisement, vrid 51, prio 100, authtype simple, intvl 1s, length 20
16:42:07.033519 IP worker > master2: ICMP echo request, id 19904, seq 46, length 64
16:42:07.033543 IP master2 > worker: ICMP echo reply, id 19904, seq 46, length 64
16:42:07.346173 IP master2 > vrrp.mcast.net: VRRPv2, Advertisement, vrid 51, prio 100, authtype simple, intvl 1s, length 20

  • HAproxy(High Availability proxy)는 고가용성 프록시를 의미 → HAProxy는 소프트웨어 기반 L7 스위치
  • HAproxy는 C 언어로 작성
  • 무료 오픈소스 프로그램
  • HAproxy는 TCP/HTTP 로드 밸런서로 프록시 솔루션에 사용
  • HAproxy의 일반적인 용도는 웹서버, DB 서버 등 부하를 분산시키는 용도로 많이 사용
  • HAProxy는 기존의 하드웨어 스위치를 대체하는 소프트웨어 로드 밸런서 → 네트워크 스위치에서 제공하는 L4, L7 기능 및 로드 밸런서 기능을 제공
  • 설치가 쉽고 빠르기에 서비스 이중화(HA- High Availability)를 구성하는데 주로 사용

1. HAproxy 로드밸런서 테스트 준비사항 → VM 환경에서 진행

  • HAproxy를 설치할 VM 1대, 웹서버 VM 2대, 테스트 서버 1대
    1. HAproxy 서버 → 192.168.100.101, centos7, hostname : haproxy, 60000 → 80포트로 proxy
    2. 첫번째 web 서버 → 192.168.100.201, centos7, hostname: web1
    3. 두번째 web 서버 → 192.168.100.202, centos7, hostname: web2
    4. 테스트 서버 → 192.168.100.10, centos7, hostname: test

2. HAproxy.conf 옵션

  • haproxy.cfg 의 옵션은 (https://cbonte.github.io/haproxy-dconv/) 에서 자세하게 확인 가능
  • 기본적인 옵션만 아래 설명 → 여러 섹션으로 나누어져 있으며 섹션 하단에 파라미터를 추가하는 방식으로 구성

2.1. global 구간

  • -daemon
    • 백그라운드에서 HAproxy 서비스 실행
    • 데몬형식 fork 하면서 실행 하는 파라미터
    • 만약 daemon을 추가 하지 않을 경우 포그라운드에서 실행 → 실행할 때 &를 추가하여 명시적으로 백그라운드에서 실행되게 해야함
  • -maxconn
    • 최대 연결 수를 지정
    • (ulimit -n) 을 통해 해당 서버의 기본 프로세스가 오픈 할 수 있는 갯수 만큼 지정 가능

2.2. defaults 구간

  • -mode (http,tcp,health)
    • tcp→ 해당 포트에 대한 어플리케이션 프록시
    • http→ http 인스턴스에서 작동
    • health→ 단순한 health check 모드
  • -timeout connect
    • 전체 외부 클라이언트에서 실제 real 서버 까지의 time out 설정
  • -timeout client
    • 외부 클라이언트에서 vip(virtual IP)까지의 timeout 설정
  • -timeout server
    • vip(virtual IP) 서버에서 실제 real 서버 까지의 timeout 설정

2.3. frontend 구간

  • -bind
    • bind 는 apache 의 binding 설정과 비슷 하며, 해당 서버로 listening 되는 아이피와 포트를 지정
  • -default_backend
    • frontend 에서 받은 정보를 backend server로 보낼 이름을 지정
    • servers 라는 backend 설정된 아이피로 포워딩

2.4. backend servers 구간 → server <name> <address>[:[port]][param*]

  • 위 형태로 명시된 아이피와 포트로 데이터를 전송

2.5. listen 구간

  • frontend + backend 한번에 설정 가능한 섹션


3. HAproxy balance 옵션

  • backend 에서 사용 할 balancing 알고리즘을 설정 가능

3.1. roundrobin

  • 순서대로 데이터를 전송

3.2. static-rr

  • 서버에 weight 가중치를 부여해, 부여된 가중치에 따라 데이터 전송

3.3. balance_url_param

  • HTTP GET 요청에 대해서 특정 패턴이 있는지 여부 조사후 조건에 맞는 서버로 트래픽 전송
  • 조건이 없는 경우 roundrobin 으로 트래픽 전송

3.4. balance hdr

  • HTTP Header 에서 hdr(name) 으로 지정된 조건이 있는 경우에 대해서만 트래픽 전송
  • 조건이 없는경우 roundrobin 으로 트래픽 전송


4. HAproxy 패키지 설치

4.1. 첫번째 방법. yum으로 haproxy 패키지 설치

  • HAproxy는 버전에 따라 설정이 약간씩 다르기에 필요한 버전을 수동으로 다운받는게 좋음
    $ yum -y install haproxy

4.2. 두번째 방법. HAproxy 패키지 수동으로 설치 → HAproxy 2.3.9 버전 설치

  • 아래 명령어를 입력하여 haproxy 2.3.9 버전 설치 파일을 다운로드

    $ cd /usr/local/lib
    
    # haproxy 2.3.9 버전 파일 설치
    $ curl -O http://www.haproxy.org/download/2.3/src/haproxy-2.3.9.tar.gz
      % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                     Dload  Upload   Total   Spent    Left  Speed
    100 2860k  100 2860k    0     0   564k      0  0:00:05  0:00:05 --:--:--  672k
    
    # 다운받은 haproxy 2.3.9 버전 설치 파일 확인
    $ ls /usr/local/lib
    haproxy-2.3.9.tar.gz
  • 설치파일을 압축해제
    $ tar zxf haproxy-2.3.9.tar.gz
    $ ls -al /usr/local/lib
    total 2868
    drwxr-xr-x.  3 root root      55 Aug  1 13:09 .
    drwxr-xr-x. 12 root root     131 Apr 30  2020 ..
    drwxrwxr-x  11 root root    4096 Mar 30 16:38 haproxy-2.3.9
    -rw-r--r--   1 root root 2928660 Aug  1 13:08 haproxy-2.3.9.tar.gz
  • HAproxy를 설치하기 전에 의존성 패키지를 설치
    $ yum install gcc openssl openssl-devel pcre-static pcre-devel systemd-devel -y
  • HAproxy 2.3.9 설치 옵션을 설정
    1. TARGET=linux-glibc → HAProxy 2.0 부터는 "linux2628" 대신에 "linux-glibc" 로 "TARGET"이 변경
    2. USE_OPENSSL=1 → SSL 인증서를 활성화 하기 위해서 USE_OPENSSL를 활성화
    3. USE_PCRE=1 → 펄(PERL) 호환 정규 표현식을 사용하기 위해 활성화
    4. USE_ZLIB=1 → http 압축을 이용하기 위해 활성화
    5. USE_SYSTEMD=1 → systemd 를 통해서 서비스를 컨트롤 하기 위해서 USE_SYSTEMD도 활성화
      $ cd /usr/local/lib/haproxy-2.3.9
      $ make TARGET=linux-glibc USE_OPENSSL=1 USE_PCRE=1 USE_ZLIB=1 USE_SYSTEMD=1
       CC      src/ev_poll.o
       CC      src/ev_epoll.o
       CC      src/ssl_sample.o
       CC      src/ssl_sock.o
      [...생략]
  • HAproxy를 설치
    $ make install
    ‘haproxy’ -> ‘/usr/local/sbin/haproxy’
    ‘doc/haproxy.1’ -> ‘/usr/local/share/man/man1/haproxy.1’
    install: creating directory ‘/usr/local/doc’
    install: creating directory ‘/usr/local/doc/haproxy’
    ‘doc/configuration.txt’ -> ‘/usr/local/doc/haproxy/configuration.txt’
    ‘doc/management.txt’ -> ‘/usr/local/doc/haproxy/management.txt’
    ‘doc/seamless_reload.txt’ -> ‘/usr/local/doc/haproxy/seamless_reload.txt’
    ‘doc/architecture.txt’ -> ‘/usr/local/doc/haproxy/architecture.txt’
    ‘doc/peers-v2.0.txt’ -> ‘/usr/local/doc/haproxy/peers-v2.0.txt’
    ‘doc/regression-testing.txt’ -> ‘/usr/local/doc/haproxy/regression-testing.txt’
    ‘doc/cookie-options.txt’ -> ‘/usr/local/doc/haproxy/cookie-options.txt’
    ‘doc/lua.txt’ -> ‘/usr/local/doc/haproxy/lua.txt’
    ‘doc/WURFL-device-detection.txt’ -> ‘/usr/local/doc/haproxy/WURFL-device-detection.txt’
    ‘doc/proxy-protocol.txt’ -> ‘/usr/local/doc/haproxy/proxy-protocol.txt’
    ‘doc/linux-syn-cookies.txt’ -> ‘/usr/local/doc/haproxy/linux-syn-cookies.txt’
    ‘doc/SOCKS4.protocol.txt’ -> ‘/usr/local/doc/haproxy/SOCKS4.protocol.txt’
    ‘doc/network-namespaces.txt’ -> ‘/usr/local/doc/haproxy/network-namespaces.txt’
    ‘doc/DeviceAtlas-device-detection.txt’ -> ‘/usr/local/doc/haproxy/DeviceAtlas-device-detection.txt’
    ‘doc/51Degrees-device-detection.txt’ -> ‘/usr/local/doc/haproxy/51Degrees-device-detection.txt’
    ‘doc/netscaler-client-ip-insertion-protocol.txt’ -> ‘/usr/local/doc/haproxy/netscaler-client-ip-insertion-protocol.txt’
    ‘doc/peers.txt’ -> ‘/usr/local/doc/haproxy/peers.txt’
    ‘doc/close-options.txt’ -> ‘/usr/local/doc/haproxy/close-options.txt’
    ‘doc/SPOE.txt’ -> ‘/usr/local/doc/haproxy/SPOE.txt’
    ‘doc/intro.txt’ -> ‘/usr/local/doc/haproxy/intro.txt’
  • 설치된 HAproxy 버전을 확인 → HAproxy 2.3.9로 잘 설치된 것을 확인
    $ /usr/local/sbin/haproxy -v
    HA-Proxy version 2.3.9-53945bf 2021/03/30 - https://haproxy.org/
    Status: stable branch - will stop receiving fixes around Q1 2022.
    Known bugs: http://www.haproxy.org/bugs/bugs-2.3.9.html
    Running on: Linux 3.10.0-1160.36.2.el7.x86_64 #1 SMP Wed Jul 21 11:57:15 UTC 2021 x86_64
  • HAProxy 서비스 예제 파일을 다운로드
    $ curl "http://git.haproxy.org/?p=haproxy-2.3.git;a=blob_plain;f=contrib/systemd/haproxy.service.in" -o /etc/systemd/system/haproxy.service
    % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                   Dload  Upload   Total   Spent    Left  Speed
    100  1405    0  1405    0     0    550      0 --:--:--  0:00:02 --:--:--   550
  • HAproxy 서비스 예제 파일 수정

    1. $CONFIG 값 설정 → /etc/haproxy/haproxy.cfg

    2. $EXTRAOPTS 값 설정 → /run/haproxy-master.sock

    3. $PIDFILE 값 설정 → /run/haproxy.pid

      $ vi /etc/systemd/system/haproxy.service
      [Unit]
      Description=HAProxy Load Balancer
      After=network-online.target
      Wants=network-online.target
      
      [Service]
      EnvironmentFile=-/etc/default/haproxy
      EnvironmentFile=-/etc/sysconfig/haproxy
      Environment="CONFIG=/etc/haproxy/haproxy.cfg" "PIDFILE=/run/haproxy.pid" "EXTRAOPTS=-S /run/haproxy-master.sock"
      
      # 수정 전 내용 아래
      ExecStartPre=@SBINDIR@/haproxy -f $CONFIG -c -q $EXTRAOPTS
      ExecStart=@SBINDIR@/haproxy -Ws -f $CONFIG -p $PIDFILE $EXTRAOPTS
      ExecReload=@SBINDIR@/haproxy -Ws -f $CONFIG -c -q $EXTRAOPTS
      
      # 수정 후 내용 아래
      ExecStartPre=/usr/local/sbin/haproxy -f /etc/haproxy/haproxy.cfg -c -q -S /run/haproxy-master.sock
      ExecStart=/usr/local/sbin/haproxy -Ws -f /etc/haproxy/haproxy.cfg -p /run/haproxy.pid -S /run/haproxy-master.sock
      ExecReload=/usr/local/sbin/haproxy -Ws -f /etc/haproxy/haproxy.cfg -c -q -S /run/haproxy-master.sock
      
      ExecReload=/bin/kill -USR2 $MAINPID
      KillMode=mixed
      Restart=always
      SuccessExitStatus=143
      Type=notify
      
      # The following lines leverage SystemD's sandboxing options to provide
      # defense in depth protection at the expense of restricting some flexibility
      # in your setup (e.g. placement of your configuration files) or possibly
      # reduced performance. See systemd.service(5) and systemd.exec(5) for further
      # information.
      
      # NoNewPrivileges=true
      # ProtectHome=true
      # If you want to use 'ProtectSystem=strict' you should whitelist the PIDFILE,
      # any state files and any other files written using 'ReadWritePaths' or
      # 'RuntimeDirectory'.
      # ProtectSystem=true
      # ProtectKernelTunables=true
      # ProtectKernelModules=true
      # ProtectControlGroups=true
      # If your SystemD version supports them, you can add: @reboot, @swap, @sync
      # SystemCallFilter=~@cpu-emulation @keyring @module @obsolete @raw-io
      
      [Install]
      WantedBy=multi-user.target
  • HAproxy가 사용할 디렉토리를 생성
    $ mkdir -p /etc/haproxy
    $ mkdir -p /var/log/haproxy
    $ mkdir -p /var/lib/haproxy
    $ mkdir -p /etc/haproxy/certs
    $ mkdir -p /etc/haproxy/errors/


5. haproxy.conf 파일 생성 또는 수정

5.1. haproxy.conf 파일에 들어가는 내용 옵션 확인

$ vi /etc/haproxy/haproxy.cfg
#---------------------------------------------------------------------
# Global settings
#---------------------------------------------------------------------
global
    # to have these messages end up in /var/log/haproxy.log you will
    # need to:
    #
    # 1) configure syslog to accept network log events.  This is done
    #    by adding the '-r' option to the SYSLOGD_OPTIONS in
    #    /etc/sysconfig/syslog
    #
    # 2) configure local2 events to go to the /var/log/haproxy.log
    #   file. A line like the following can be added to
    #   /etc/sysconfig/syslog
    #
    #    local2.*                       /var/log/haproxy.log
    #
    log         127.0.0.1 local2         # syslog. UDP 514번 포트 Open 필요

    chroot      /var/lib/haproxy         # 모든 동작은 /var/lib/haproxy에서 수행, 보안 상승
    pidfile     /var/run/haproxy.pid     # 실행 pid명
    maxconn     4000                     # 프로세스 당 최대 연결 수치
    user        haproxy                  # haproxy 서비스 사용자
    group       haproxy                  # harpoxy 서비스 그룹
    daemon                               # background로 실행될 수 있도록 설정

    # turn on stats unix socket
    stats socket /var/lib/haproxy/stats



#---------------------------------------------------------------------
# common defaults that all the 'listen' and 'backend' sections will
# use if not designated in their block
#---------------------------------------------------------------------
defaults                                 # front, back, listen에 관련 설정 부분
    mode                    http         # http 프로토콜을 사용하는 로드벨런싱 모드
    log                     global       # 로그는 global에 설정한 방법으로 사용
    option                  httplog      # 기본 Log는 SIP, DIP와 Name만 표기됨, 해당 옵션을 사용하면 디테일하게 Log 표시
    option                  dontlognull  # Log 비대화 방지 -> Probe(정찰, 스캔과 같은 불필요한 기록을 HAProxy Log 기록 X
    option http-server-close             # 클라이언트와 리얼 서버 연결 종료시, 디폴트로 유휴 대기하지 않고, 서버에서 Handshake를 종료
                                         # 더 빠른 새로운 세션을 준비할 수 있도록 해줌
                                         # 디폴트 옵션에 선언되어 있어도, 인스턴스 별로 no 옵션으로 제외처리 가능
    option forwardfor       except 127.0.0.0/8     # 서버에 대한 응답을 HAProxy가 받기 때문에(VIP), 리얼 서버 IP를 HTTP 헤더에 표기하는 옵션
    option                  redispatch   # mode HTTP에서 Cookie에 지정된 Real 서버가 다운되면, 외부 클라이언트가 쿠키를 플로시하기 전에 서비스에 문제 발생 가능
                                         # redispatch 옵션을 통해 프로시 지속성을 무시하고 쿠키, 세션 재분재를 실행 -> retries값이 0보다 커야함
    retries                 3            # redispatch 횟수 결정
    timeout http-request    10s          # Request시의 헤더에만 적용. Dos 방어를 위해, HTTP 요청 타임 아웃시간 설정 -> 클라이언트의 연결 타임아웃과는 관계 X, HAProxy의 옵션
    timeout queue           1m           # 서버의 maxconn에 도달시, 무한정 보류 상태로 두지 않고 HTTP 503 응답을 보내면서 연결을 버리기까지의 시간
    timeout connect         10s          # TCP 패킷 손실을 막기 위한 Real 서버로의 연결 최대 지연시간 설정 -> Backend에 적용, 전역에도 적용 가능
    timeout client          1m           # 외부 클라이언트의 요청이나 데이터와의 연결 최대 시 ->  http-request가 선행, 서버 안정성에 기여
    timeout server          1m           # 서버가 데이터를 승인하거나, 전송해야 할 때의 연결 최대 시간
    timeout http-keep-alive 10s          # 클라이언트의 요청에 따른 응답 전송 후, 다음 요청까지의 대기 시간 -> http-request가 선행 필수
    timeout check           10s          # timeout server와 동일하거나 작은 값을 가져야함
    maxconn                 3000         # 프로세스당 최대 연결 개수

#---------------------------------------------------------------------
# main frontend which proxys to the backends
#---------------------------------------------------------------------
frontend http
    bind: *:80                               # haproxy 구동시 사용할 포트를 지정 -> 클라이언트의 연결을 받는 부분(WAF 기능과 유사)
    acl url_static       path_beg       -i /static /images /javascript /stylesheets
    acl url_static       path_end       -i .jpg .gif .png .css .js

    use_backend static          if url_static    # ACL에서 정의한 style들을 backend static으로 전송
    default_backend             static           # 아래 backend 중에서 static 부분을 사용 -> 미리 여러개 설정해놓고 선택


#---------------------------------------------------------------------
# static backend for serving up images, stylesheets and such
#---------------------------------------------------------------------
backend static                                   # 실제 접속시 로드밸런싱하는 서버
    balance     roundrobin                       # roundrobin은 무조건 한번씩 번갈아 접속시키는 방식 -> source 값을 이용
    server      static 192.168.100.201:80 check  # 포워딩할 서버 IP와 포트 설정
    server      static 192.168.100.202:80 check  # 포워딩할 서버 IP와 포트 설정

#---------------------------------------------------------------------
# round robin balancing between the various backends
#---------------------------------------------------------------------
backend app
    balance     roundrobin
    server  app1 127.0.0.1:5001 check
    server  app2 127.0.0.1:5002 check
    server  app3 127.0.0.1:5003 check
    server  app4 127.0.0.1:5004 check


#listen                                          # 프론트/백엔드 연결의 포트/옵션등 정의 -> TCP 제어나 Proxy에 주로 사용
#listen stats                                    # "stats"라는 이름으로 listen 지정
#    bind *:9000                                 # 접속 포트 지정
#    stats enable
#    stats realm Haproxy Statistics              # 브라우저 타이틀
#    stats uri /haproxy_stats                    # stat 를 제공할 URI
#    #stats auth Username:Password               # 인증이 필요하면 추가

5.2. haproxy.conf 파일에 넣은 내용 → 간단하게 적용

global
  log  127.0.0.1 local2

  chroot  /var/lib/haproxy
  pidfile /var/run/haproxy.pid
  maxconn 4000
  daemon

defaults
    mode  http
    log  global
    option  httplog
    option  dontlognull
    option http-server-close
    option forwardfor  except 127.0.0.0/8
    option  redispatch
    retries  3
    timeout http-request  10s
    timeout queue  1m
    timeout connect  10s
    timeout client  1m
    timeout server  1m
    timeout http-keep-alive  10s
    timeout check  10s
    maxconn  3000

frontend http
    bind *:80
    acl url_static       path_beg       -i /static /images /javascript /stylesheets
    acl url_static       path_end       -i .jpg .gif .png .css .js

    default_backend  back_server


backend back_server
    balance  roundrobin
    server  web1 192.168.100.201:80 check
    server  web2 192.168.100.202:80 check



6. HAproxy의 conf 파일에 문제 없는지 Check

  • haproxy.cfg 설정이 이상 없는지 확인
    $ haproxy -f /etc/haproxy/haproxy.cfg -c
    Configuration file is valid


7. 방화벽 해제(firewalld)

$ systemctl stop firewalld
$ systemctl disable firewalld



8. HAproxy 서버스를 원할하게 실행하기 실행을 위한 커널 파라미터 변경

8.1. net.ipv4.ip_forward = 1

  • Kernel이 패킷을 전달하게 하는 경우 사용
  • Keepalived가 네트워크 패킷을 실제 서버에 정상적으로 전달하려면 각 라우터 노드가 커널에서 IP Forward를 설정 필요

8.2. net.ipv4.ip_nonlocal_bind = 1

  • HAProxy 및 Keepalived의 로드밸런싱은 동시에 로컬이 아닌 IP 주소에 바인딩할 수 있어야함
  • 네트워크 인터페이스에 없는 주소로 바인딩할 수 있도록 해주는 커널
  • 네트워크 인터페이스가 지정된 정적 IP가 아닌 동적 IP를 바인딩할 수 있음
  • 해당 옵션이 비활성화 되어 있어도 서비스가 시작하면서 인터페이스에 특정 IP를 바인딩할 수 있으나 FailOver시 문제 발생

8.3. 커널 파라미터 적용

$ cat << EOF >> /etc/sysctl.conf
net.ipv4.ip_forward = 1
net.ipv4.ip_nonlocal_bind = 1
EOF

# /etc/sysctl.conf 파일 확인
$ cat /etc/sysctl.conf
net.ipv4.ip_forward = 1
net.ipv4.ip_nonlocal_bind = 1

# /etc/sysctl.conf 파일 내용 적용
$ sysctl --system
* Applying /usr/lib/sysctl.d/00-system.conf ...
* Applying /usr/lib/sysctl.d/10-default-yama-scope.conf ...
kernel.yama.ptrace_scope = 0
* Applying /usr/lib/sysctl.d/50-default.conf ...
kernel.sysrq = 16
kernel.core_uses_pid = 1
kernel.kptr_restrict = 1
net.ipv4.conf.default.rp_filter = 1
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.accept_source_route = 0
net.ipv4.conf.all.accept_source_route = 0
net.ipv4.conf.default.promote_secondaries = 1
net.ipv4.conf.all.promote_secondaries = 1
fs.protected_hardlinks = 1
fs.protected_symlinks = 1
* Applying /etc/sysctl.d/99-sysctl.conf ...
net.ipv4.ip_forward = 1
net.ipv4.ip_nonlocal_bind = 1
* Applying /etc/sysctl.conf ...
net.ipv4.ip_forward = 1
net.ipv4.ip_nonlocal_bind = 1



9. 로그 설정

9.1. rsyslog 에 HAproxy 용 로그를 남기도록 설정 → /etc/rsyslog.d/haproxy.conf 파일 구성

$ vi /etc/rsyslog.d/haproxy.conf
# Provides UDP syslog reception
$ModLoad imudp
$UDPServerRun 514
$template Haproxy, "%msg%\n"

#rsyslog 에는 rsyslog 가 메세지를 수신한 시각 및 데몬 이름같은 추가적인 정보가 prepend 되므로, message 만 출력하는 템플릿 지정
# 이를 haproxy-info.log 에만 적용한다.
# 모든 haproxy 를 남기려면 다음을 주석해재, 단 access log 가 기록되므로, 양이 많다.
#local0.*   /var/log/haproxy/haproxy.log

# local0.=info 는 haproxy 에서 에러로 처리된 이벤트들만 기록하게 됨 (포맷 적용)
local0.=info    /var/log/haproxy/haproxy-info.log;Haproxy

# local0.notice 는 haproxy 가 재시작되는 경우와 같은 시스템 메세지를 기록하게됨 (포맷 미적용)
local0.notice   /var/log/haproxy/haproxy-allbutinfo.log

9.2. logroate에 HAproxy 설정을 추가 → /etc/logrotate.d/haproxy 파일 구성

  • haproxy는 재시작할 필요가 없으므로 rsyslog 를 재시작해줌

    $ vi /etc/logrotate.d/haproxy
    /var/log/haproxy/*log {
        daily
        rotate 90
        create 0644 nobody nobody
        missingok
        notifempty
        compress
        sharedscripts
        postrotate
            /bin/systemctl restart rsyslog.service > /dev/null 2>/dev/null || true
        endscript
    }
    
    # rsyslog 서비스 재시작
    $ systemctl restart rsyslog



10. HAproxy 서비스 시작

  • HAproxy 서비스 시작 및 영구 설정

    $ systemctl start haproxy
    $ systemctl enable haproxy
    
    $ systemctl status haproxy
    ● haproxy.service - HAProxy Load Balancer
       Loaded: loaded (/usr/lib/systemd/system/haproxy.service; enabled; vendor preset: disabled)
       Active: active (running) since Sun 2021-08-01 15:32:34 KST; 5s ago
     Main PID: 1030 (haproxy-systemd)
       CGroup: /system.slice/haproxy.service
               ├─1030 /usr/sbin/haproxy-systemd-wrapper -f /etc/haproxy/haproxy.cfg -p /run/haproxy.pid
               ├─1031 /usr/sbin/haproxy -f /etc/haproxy/haproxy.cfg -p /run/haproxy.pid -Ds
               └─1032 /usr/sbin/haproxy -f /etc/haproxy/haproxy.cfg -p /run/haproxy.pid -Ds



11. Web 서버 2대(192.168.100.201, 192.168.100.202)에 nginx 설치 및 index.html 파일 변경

11.1. web1(192.168.100.201) 서버에 nginx 설치 및 index.html 파일 변경

# nginx 패키지 설치
$ yum install -y nginx

# nginx의 index.html 파일 내용 변경
$ vi /usr/share/nginx/html/index.html
hello~~~~
webser : web1
Bye

# nginx 시작
$ systemctl enable --now nginx

11.2. web1(192.168.100.202) 서버에 nginx 설치 및 index.html 파일 변경

# nginx 패키지 설치
$ yum install -y nginx

# nginx의 index.html 파일 내용 변경
$ vi /usr/share/nginx/html/index.html
hello~~~~
webser : web2
Bye

# nginx 시작
$ systemctl enable --now nginx



12. HAproxy 트래픽 테스트

  • harpoxy의 IP(192.168.100.101)으로 트래픽을 전달하면 backend에 있는 서버(192.168.100.201, 192.168.100.202)로 트래픽 전송

  • Round-Robin으로 설정하였기에 192.168.100.201과 192.168.100.202에 번갈아가면서 트래픽 전달

  • haproxy 트래픽 테스트는 해당 서버와 상관없는 192.168.100.10에서 진행

    $ while true; do curl 192.168.100.101; echo -e "\n"; sleep 3; done
    hello~~~~
    webser : web1
    Bye
    
    hello~~~~
    webser : web2
    Bye
    
    hello~~~~
    webser : web1
    Bye
    
    hello~~~~
    webser : web2
    Bye



13. HAProxy 어드민 페이지 설정

13.1. HAProxy의 상태 및 통계 제공 웹을 위한 설정

  • mode http → 통계 사이트는 http를 통해 접속
  • bind * :8020 → haproxy의 IP 에 8020 포트 접속
  • stats enable→ HAProy의 어드민 페이지를 stats로 칭하여 enable 표시함으로 사용함
  • stats auth root:hello→ HAProy의 어드민 페이지의 ID는 root, PW는 hello
  • stats uri /monitor → path를 /monitor로 지정
    # /etc/haproxy/haproxy.cfg 파일 가장 아래에 추가
    $ vi /etc/haproxy/haproxy.cfg
    listen monitor
      mode http
      bind *:8020
      stats enable
      stats auth root:hello
      stats uri /monitor

13.2. HAProxy.cfg 파일 검증 및 재시작

# 위 내용이 적합한지 검증
$ haproxy -f /etc/haproxy/haproxy.cfg -c
Configuration file is valid

$ systemctl restart haproxy

13.3. HAProxy monitor에 접속 → http://haproxy_IP:8020/monitor 접속

  • 로그인 후 monitor 화면 출력

14. 추가 HAproxy Kernel Parameter

14.1. 가장 중요한 Paramater

net.ipv4.ip_local_port_range = "1025 65534"
net.ipv4.tcp_max_syn_backlog = 100000
net.core.netdev_max_backlog = 100000
net.core.somaxconn = 65534
ipv4.tcp_rmem = "4096 16060 64060"
ipv4.tcp_wmem = "4096 16384 262144"

14.2. 작업량에 따라 조절

tcp_slow_start_after_idle = 0

14.3. iptables tuning

net.netfilter.nf_conntrack_max = 131072

14.4. HAproxy Kernel Parameter 사용시 주의 사항

  • conntrack이 잘못 구성되면 HAProxy가 최대 성능을 발휘 X
  • iptables가 활성화 되어 있으면 connection tracking 작업을 위해 iptables에 규칙이 없더라도 20%의 CPU를 소모

참고 URL : https://lascrea.tistory.com/213
참고 URL : https://findstar.pe.kr/2018/07/27/install-haproxy/
참고 URL : https://medium.com/@tamm_/%EA%B0%9C%EB%B0%9C-haproxy-%EC%84%A4%EC%B9%98-%EB%B0%8F-%EA%B8%B0%EB%B3%B8-%EC%84%A4%EC%A0%95-f4623815622
참고 URL : https://blog.develope.kr/2020/01/haproxy%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-%EB%A1%9C%EB%93%9C-%EB%B0%B8%EB%9F%B0%EC%8B%B1-%EA%B5%AC%EC%B6%95%ED%95%98%EA%B8%B0-2-haproxy-%EC%84%A4%EC%B9%98-%EC%84%B8%ED%8C%85%ED%95%98%EA%B8%B0/


서버 부하 분산의 정의

  • 서버 부하 분산(Server Load Balancing)은 외부의 사용자로부터 들어오는 다수의 요청(naver 접속 등)을 서버들에게 적절히 배분하여 서버들로 하여금 요청을 처리케 하는 것
  • 분산 처리는 부하 분산 Network Switch 혹은 소프트웨어가 담당
  • 외부로부터의 요청을 서버가 직접 받는 것이 아닌 '부하 분산 Network Switch' 혹은 '소프트웨어'가 받은 후 요청 받은 내용을 서버들로 적절히 나누어 주는 것
  • 서버 부하 분산을 담당하는 Network Switch를 L4/L7 Switch(Layer 4)라고 부르며 Cloud에서는 Load Balancer라고 부름
  • 현업에서는 서버 부하 분산(Server Load Balancing)을 부르기 편하게 로드밸런싱 혹은 LB(Load balancing)라고 부름

 

서버 부하 분산(Server Load Balancing)의 방법

  • 서버에게 부하를 고르게 분산하는 것도 다양한 방법있음
  • 서버의 능력을 고려하여 분배해야지 서버가 Down되지 않기 때문에 서버의 상황에 맞춰 적절한 방법을 선택 필요
  • 서버의 상황에 맞춰 적절한 방법을 Load Balancing Method이라고 함

 

1. Round Robin

  • Round Robin은 로드밸런서가 다수의 서버에게 순서대로 요청을 할당하는 방법
  • 가장 단순한 방법
  • 서버군에 차례로 요청을 할당하여 분산함

 

 

2. Least Connection

  • 로드밸런서가 서버에게 요청을 전달한 뒤, 사용자와 서버가 정상적인 연결을 맺으면 사용자와 서버는 'Connection'을 생성함
  • 로드밸런서 또한 중간자로서 Connection 정보를 갖고 있는데 이 Connection 수 정보를 기반으로 가장 Connection이 적은 서버, 즉 부하가 가장 덜한 서버에게 요청을 전달

 

3. Ratio(가중치)

  • 서버의 처리 능력을 고려하여 할당될 수 있는 각 서버가 가질 수 있는 Connection의 비율를 이미 정해둠.
  • 서버 부하 분산 비율이 100%라고 했을 때 성능이 가장 떨어지는 서버에게 10%를 나머지 서버 3대에게 각각 30%씩을 할당 가능

 

4. Fastest(Response Time)

  • 응답속도가 가장 빠른 서버에게 우선적으로 할당하는 방식
  • Fastest(Response Time)의 예시
    • 서버에 할당된 Connection이 5개인데 서버가 보내오는 Response가 5개라면 갖고 있는 Connection에 대해 모두 응답하고 있으므로 성능이 충분하다고 판단하고 추가 요청을 보냄
    • 다른 서버에 할당된 Connection이 10개인데 서버가 보내오는 Response가 5개뿐이라면 현재 성능이 충분치 않아 제대로 답변하지 못하는 것으로 판단하고 추가 요청을 해당 서버로 보내지 않음

 

참고 URL : <서버 부하 분산(출처 : 서버 구축이야기!님 블로그(https://server-talk.tistory.com/)>

 

 

+ Recent posts