• 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/


  • Ansible Vault는 패스워드, 키와 같은 보안에 민감한 파일들을 encryption / decryption 해주는 기능을 가짐(암호화)
  • Ansible-Vault는 변수와 파일을 암호화하여 플레이북이나 role에서 비밀번호. 키와 같은 민감한 콘텐츠를 보호 가능
  • Ansible을 이용할 때 해킹이나, 유출로 인해 서버 정보가 외부로 나갈 수 있음
  • Ansible Vault를 통해 민감한 정보를 평문이 아니라 암호화된 파일로 관리하면 해킹당해도 문제 X
  • SSH 개인 및 공개 키, 암호 및 SSL 인증서도 암호화된 파일로 관리하여 권한 없는 사람이 권한을 얻는 것을 방지
  • Ansible Vault를 이용하면 Ansbile에서 사용하는 모든 구조화된 데이터 파일을 암호화 가능
  • Ansible Vault는 변수 또는 전체 파일 및 YAML 플레이 북을 암호화 → 파일을 암호화하고 복호화 할 때 동일한 암호를 요구

1. Ansible-Vault 설치

  • Ansible을 설치하면 Ansible-vault도 함께 설치됨

  • 암호화 알고리즘 : AES256

    $ ansible-vault -h
    usage: ansible-vault [-h] [--version] [-v]
                         {create,decrypt,edit,view,encrypt,encrypt_string,rekey}
                         ...
    
    encryption/decryption utility for Ansible data files
    
    positional arguments:
      {create,decrypt,edit,view,encrypt,encrypt_string,rekey}
        create              Create new vault encrypted file
        decrypt             Decrypt vault encrypted file
        edit                Edit vault encrypted file
        view                View vault encrypted file
        encrypt             Encrypt YAML file
        encrypt_string      Encrypt a string
        rekey               Re-key a vault encrypted file
    
    optional arguments:
      --version             show program's version number, config file location,
                            configured module search path, module location,
                            executable location and exit
      -h, --help            show this help message and exit
      -v, --verbose         verbose mode (-vvv for more, -vvvv to enable
                            connection debugging)
    
    See 'ansible-vault <command> --help' for more information on a specific
    command.



2. 암호화되지 않은 파일을 암호화 (../vars/main.yml 파일 암호화)

  • 기존에 가지고 있는 파일을 암호화함으로 알수 없는 문자로 이루어진 파일 생성 (Plain text → Encrypt text)

  • 암호화할때 AES256 암호화 표준을 사용

  • 암호화된 파일의 key를 1234로 함

  • 실전에서는 보다 복잡하게 생성해야하지만, 해당 key의 비밀번호를 담고있는 암호화 파일을 추가로 생성하여 vault password 입력할때 활용하는 테스트를 위해 단순하게 생성

    # 암호화할 파일의 내용 확인
    $ vi ../vars/main.yml
    ---
    #사용자 목록
    root_user: "root"
    hippo_user: "hippo"
    
    #변경하고자하는 id의 비밀번호 -> 비밀번호 부분은 ####으로 표시
    root_newpasswd: "########"
    hippo_newpasswd: "########"
    
    # ../var/main.yml 파일을 암호화하여 새로운 암호화 파일 생성
    $ ansible-vault encrypt ../vars/main.yml
    New Vault password:
    Confirm New Vault password:
    Encryption successful
    
    # ../vars/main.yml 파일이 암호화되어있음을 확인 가능
    $ cat ../vars/main.yml
    $ANSIBLE_VAULT;1.1;AES256
    33363235653433386533393236363138376133666234623535313431373835323563323230653838
    3036383965373264646262316464306137623333656435370a333162616433653761336332343439
    61323933646137666663616231613539393264363032613137393538343837393838306231383861
    6265323931656562340a316539306433363436613262393633623463626361303339396430386465
    30326638623336386133303136396431336662326639623439653035373363643966633161656666
    65613632353539616365666530313532373530613239373632316532613838643434623962366332
    31333834613237633134316332343437323063386234643063303464393365326133636462356266
    63343662363237633365646535333733623966323735343865656536626664613263353332393263
    31396361303533663463313832333362353732663939616539616131653533383331343064623961
    38303966393434323266643133323635323461653265633338306430323566373236643662356565
    62326336353336613937623738323861333636336234623030376434316564386131363639643739
    38653764376634613636356665383830306230356364323932373734363831333030313730656464
    31383231653330393764323638393034646539646366336438386533323534336631336133333337
    62626434343532636338613339333739396231643934616135363836323961303032626661303737
    62313433343236636134396136633566343165343963663462336531343162383361396536313434
    35333064313739623464



3. 암호화한 ../vars/main.yml 파일이 이상없이 roles에서 적용가능한지 테스트

  • 비밀번호를 변경하는 roles

  • 암호화한 ../vars/main.yml 을 활용하기 위해서는 키가 필요

  • 키를 수동으로 입력하기 위해서는 옵션을 넣어야함(--ask-vault-pass)

  • --ask-vault-pass 옵션이 없는 경우 ansible-vault로 암호화된 파일을 읽을 수 없어서 에러 발생

    # 아래와 같이 일반 암호와 vault 암호를 같이 입력해야함 -> 정상 실행됨으로 실행 내용은 생략
    # Vault password에 들어갈 비밀번호는 "1234"
    $ ansible-playbook change_passwd_role.yml -k --ask-vault-pass
    SSH password:
    Vault password:
    
    PLAY [change passwd Role] *******************************************************************
    [...생략]



4. 암호화된 파일을 복호화 (../vars/main.yml 파일 복호화)

  • 암호화된 파일을 Plain text 파일로 복호화 (Encrypt text → Plain text)

  • 복호화할때 AES256 암호화 표준을 사용

  • 암호화된 파일에 key를 1234로 하여 복호화

    # 암호화된 ../var/main.yml 파일을 복호화하여 기존 파일으로 변경
    $ ansible-vault decrypt ../vars/main.yml
    Vault password:
    Decryption successful
    
    # 암호화된 ../vars/main.yml 파일이 복호화되어있음을 확인 가능
    $ cat ../vars/main.yml
    ---
    #사용자 목록
    root_user: "root"
    hippo_user: "hippo"
    
    #변경하고자하는 id의 비밀번호 -> 비밀번호 부분은 ####으로 표시
    root_newpasswd: "########"
    hippo_newpasswd: "########"



5. 암호화 키 제작 → 암호화할 파일의 키를 암호화

  • 암호화할 파일의 키는 "1234" → 중요한 정보를 암호화할 때 사용할 키

  • 암호화할 파일의 키를 복호화할 키는 "9876" → 암호화한 키의 내용이 복호화되면 암호화된 정보까지 도달할 수 있기에 이 부분도 복잡하게 하는 것이 중요 (테스트이기에 단순하게 적용)

  • 중요한 정보를 암호화할 키를 Plain text로 공유하면 관리에서 문제가 발생할 수 있음 → 암호화된 키로 공유하여 관리 문제 해결

    # vault password는 vault_key 파일의 키 -> 복호화할때 사용
    $ ansible-vault create ../vars/vault_key
    New Vault password:
    Confirm New Vault password:
    
    # 1234로 입력하고 wq!으로 저장하면 ../vars/vault_key 파일 생성
    1234
    
    # ../vars/vault_key 파일의 내용 확인 -> 해당 내용으로 암호화가 필요한 중요 파일을 암호화
    $ cat ../vars/vault_key
    $ANSIBLE_VAULT;1.1;AES256
    61336134633736613130643539316264356339333261623865356261366465623065613436313064
    3263356335363463623362646333366338613764643932300a303635663930653934313931303966
    37316437336635333137336330616264623264393831316537303434376138373463393361373936
    6132636239613263350a363863343233656164343832663965353433393136343465383933656636
    3962



6. 중요 파일을 암호화된 키로 암호화 → 암호화된 키 공유하여 ansible 활용

  • 암호화 키를 공유해도 해당 키의 암호키를 모르기에 문제 X

  • 중요한 파일을 암호화할 때 해당 암호화된 파일 활용 → 이전에 Plain text로 "1234" 입력하는 것과 동일 효과

  • 암호화된 키를 활용하기 위해서는 vault-password-file 옵션 사용

  • 해당 이슈 내용은 ansible 버전 업그레이드로 ansible-vault를 통해서 암호화된 파일은 ansible-vault키로 사용 X → 해결 방법으로는 6.1 부분과 6.2 부분를 보고 괜찮은 부분을 따라서 하면됨

  • ansible-vault로 encrypt한 ../vault_key 파일을 활용하여 ../main.yml 파일을 암호화할 때 문제 발생

    $ ansible-vault encrypt ../vars/main.yml --vault-password-file ../vars/vault_key
    
    # 이슈 내용: ansible-vault를 통해서 생성한 암호화 파일을 ansible-vault의 키로 사용할 경우 아래와 같은 에러가 발생함
    [WARNING]: Error in vault password file loading (default): A vault password must be specified to decrypt data
    ERROR! A vault password must be specified to decrypt data

6.1. 생성한 AES256 암호화 파일을 ansible-vault에서 볼트 암호화 파일로 인식하지 못하게 하기

  • vault_key 파일 확인
    $ cat ../vars/vault_key
    $ANSIBLE_VAULT;1.1;AES256
    61336134633736613130643539316264356339333261623865356261366465623065613436313064
    3263356335363463623362646333366338613764643932300a303635663930653934313931303966
    37316437336635333137336330616264623264393831316537303434376138373463393361373936
    6132636239613263350a363863343233656164343832663965353433393136343465383933656636
    3962
  • vault_key 파일 내용 변경 → 출력되는 내용 중에 $(달러 기호)를 삭제
    $ vi ../vars/vault_key
    ANSIBLE_VAULT;1.1;AES256
    61336134633736613130643539316264356339333261623865356261366465623065613436313064
    3263356335363463623362646333366338613764643932300a303635663930653934313931303966
    37316437336635333137336330616264623264393831316537303434376138373463393361373936
    6132636239613263350a363863343233656164343832663965353433393136343465383933656636
    3962
  • vault_key를 평문으로 인식 → ../vars/vault_key 파일을 통해 ../vars/main.yml 파일을 암호화 복호화 테스트

    # 암호화 테스트
    $ ansible-vault encrypt ../vars/main.yml --vault-password-file ../vars/vault_key
    Encryption successful
    
    # 암호화가 정상적으로 진행되었는지 확인
    $ cat ../vars/main.yml
    $ANSIBLE_VAULT;1.1;AES256
    63643230343061333339313964343937336632386364663935643736313333336238663932323865
    3730326361663139626232386562653962396263343931310a333161343061376331306530303736
    36663832626133353932383464323539656431373563613563653230623136343963306230623962
    6538353330343666380a396639323235653465356336346135646562616161306333313033336562
    65333766643234633633633532623062393033636233643534336138366465326237333932643333
    30353563343337643031353966366264383264356562316234393331393832656162653933343136
    38646331333238633766393036336431366634643863323234353133643031653033663037356236
    61393162386637326437623164323364666330613636666462366465363764663364666330303566
    36306433366539623433386434616238663063633734656131316434323332623735346335616266
    61303838393239643834343362386331386534343339313265663334323035653665616632363661
    38376433643239323564616162666535393431326533643432396466633034646562396134356437
    39626134346565326533393836653634316536633164616164393436316330323331336239336665
    64383238383930613930653065386631343933633462383966313236666532353334343137333632
    66643462626164616339303064633733303734653735313862653165633762353430313136386463
    33663434306566323161386630376130333865343238613736356332383862323765303634643732
    66383134383439356361
    
    # 복호화 테스트
    $ ansible-vault decrypt ../vars/main.yml --vault-password-file ../vars/vault_key
    Decryption successful
    
    # 복호화가 정상적으로 진행되었는지 확인
    $ cat ../vars/main.yml
    ---
    #사용자 목록
    root_user: "root"
    hippo_user: "hippo"
    
    #변경하고자하는 id의 비밀번호 -> 비밀번호 부분은 ####으로 표시
    root_newpasswd: "########"
    hippo_newpasswd: "########"

6.2. vault가 아닌 openssl을 이용하여 난수로 작성된 vault_key_by_ssl 파일 생성

  • openssl 명령어를 실행하여 vault_key_by_ssl 이라는 암호화된 파일 생성 → vault_key_by_ssl 파일로 main.yml 파일을 암호화하는 키로 활용
    # ../vars/vault_key_by_ssl 파일이름으로 암호화된 파일 생성(ansible-vault를 활용하지 않지만, openssl로 암호화한 key 활용)
    $ openssl rand -base64 2048 > ../vars/vault_key_by_ssl
  • 생성한 vault_key_by_ssl 확인
    # 암호화된 파일 내용 출력
    $ cat ../vars/vault_key_by_ssl
    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
  • 새로 생성한 vault_key_by_ssl 파일을 이용해서 ../vars/main.yml 파일을 암호화

    # 암호화 테스트
    $ ansible-vault encrypt ../vars/main.yml --vault-password-file ../vars/vault_key_by_ssl
    Encryption successful
    
    # 암호화된 ../vars/main.yml 파일 내용 확인
    $ cat ../vars/main.yml
    $ANSIBLE_VAULT;1.1;AES256
    33303566376566653234393138636562663863393431646566333262643232383862383439626531
    6433303938363137623762316234666163383238383136320a333238623731333539353065313666
    63633864663763346263663564646561666534343137616139373564363964396131343863363263
    6366663236613735320a333264323761353230306239346663656466663638323961396364323138
    35636665333737643838616161343835363732356536633833646431323465646131393432373863
    35653939326435626464373036633531303466356164656437323039343435613532653531636231
    35356237663464306566663463613939313433333031633736323165663839306266376334636336
    39356537323930656263313037396531663961396162333162613765356265653135336535316366
    39666432363036646364613165346461363830613032323961396563643466663633613635343633
    62396536656562303365346563626338353234303161306432333430303662346166353133353230
    39613636666165626362366637633061396266636561646463363966306135323835666536393765
    34316662396265336365383462643932366532663132616361396238396561663166386532636264
    64626266373266326266363739653633623664353765336136356539323665336265653461666466
    33393462633138346638313866643132346239643061353131326132336134653737646565636262
    35356131343362383532353232346430643237383165353132616133643630373466383761323030
    38333061313761373938
    
    # vault_key_by_ssl 파일을 이용해서 ../vars/main.yml 파일을 복호화
    $ ansible-vault decrypt ../vars/main.yml --vault-password-file ../vars/vault_key_by_ssl
    Decryption successful
    
    # 복호화가 정상적으로 진행되었는지 확인
    $ cat ../vars/main.yml
    ---
    #사용자 목록
    root_user: "root"
    hippo_user: "hippo"
    
    #변경하고자하는 id의 비밀번호 -> 비밀번호 부분은 ####으로 표시
    root_newpasswd: "########"
    hippo_newpasswd: "########"



7. 생성된 암호화 키 파일인 ../vars/vault_key과 ../vars/vault_key_by_ssl를 활용하여 ../vars/main.yml 파일을 암호화 → roles에서 적용가능한지 테스트

  • 암호화된 키 파일을 활용 → 6.1과 6.2 중 하나를 이용하면 됨

  • 암호화된 키 파일을 통해서 ../vars/main.yml 파일 암호화

  • roles를 이용하여 암호화된 파일이 이상 없는 지 확인 필요

  • --vault-password-file 옵션을 사용하여 암호화된 키 피일을 적용 → 암호화된 키를 통해 중요한 데이터가 암호화되어야만 정상 roles 작동

  • --vault-password-file 옵션으로 암호화된 파일을 적용하는 것은 문제 없으나, SSH 접속에는 비밀번호가 필요함으로 SSH password 부분에 적합한 비밀번호 적용 필요

  • 해당 부분도 암호화로 변경하여 자동화 할수 있지만, 해당 파일의 목적이 비밀번호 변경임으로 비밀번호 확인 차원에서 SSH 접속 비밀번호는 수동으로 남겨놓음

    # Vault password에 들어갈 비밀번호를 암호화된 파일을 이용하여 적용 -> --vault-password-file 옵션 활용
    # 정상 실행됨으로 실행 내용은 생략
    # 파일 경로는 직접 확인하여 적용 필요
    $ ansible-playbook change_passwd_role.yml -k --vault-password-file ../vars/vault_key
    SSH password:
    
    PLAY [change passwd Role] *******************************************************************
    [...생략]

※ ansible vault의 참고 사항

1. 암호화된 파일의 패스워드 변경하기

$ ansible-vault rekey ../vars/main.yml

2. 암호화된 파일의 내용 보기

$ ansible-vault view ../vars/main.yml


Vault로 유효 기간이 존재하는 인증서 관리

  • 인증서를 직접 발급 수행하는 것은 매우 번거러운일
  • Vault를 사용하면 SSH 인증서를 매우 쉽게 관리하고 발급 가능

Vault를 사용하기 위해서는 모든 클라이언트 Server에 Vault가 설치 필요

1. hashicorp_vault 설치

$ yum install -y yum-utils
$ yum-config-manager --add-repo https://rpm.releases.hashicorp.com/RHEL/hashicorp.repo
$ yum -y install vault

2. hashicorp_vault 설치 확인

$ vault -v



Vault 서버 기본 설정

1. Vault SSH secret engine 활성화

$ vault secrets enable -path=ssh-client-signer ssh
Success! Enabled the ssh secrets engine at: ssh-client-signer/

# SSH secret enbine 생성 확인
$ vault secrets list
Path                       Type         Accessor              Description
----                        ----          --------                 -----------
ssh-client-signer/     ssh          ssh_241cbffa         n/a

2. SSH CA 생성

$ vault write ssh-client-signer/config/ca generate_signing_key=true
Key              Value
---                -----
public_key    ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDALoH6ItfBjGnqNJAwFa1xUzOJ38WVosCNZQ3o2BlFJxhXvejU1CalDQDCZXEK8SIxeOqKC3mu/nIGMLYR5lwWRgl5r3janyLw8174MSM1wgSy9ZcURZrivicafKrH2WQAXJ6TKsrlhR5GODwxRCciD02l8LgbvaBs8pt1SRmvCc17xBAMuwu/A0pBU+tHotfiBqeLlQThi2mhpRSZ9u2cIt4neP/g+1/hMDxjlhmge7ehQBNYyWc2n8HDubCYT+z2GANk8vk6DhXs5iaLozM1lALXQZlbK3MpIVh1HySnIewuw3rjgBlOc9sJMcrnQYdrb/EnJzLrcL4Sb4NmzB5gPW29vTc0Thkt4T4ghTCriOLCFjKihlc9sO8rsZ6fV10i/qfUONcrZYp1wVS18WOKQEBblgXp94L/Kl/mRVHFRicNDSVsZij9pHZovRprE3RkqO87qF67ZhcL3yzAVfkoRrWqDzG5PFpjGHLa3mV3nmL1vBKhL9/tkxbxz6svWkPYFSbxZx+JRSzzp3VPqFzSEiBmFNloCCVZfdSUMi5RlAD7r2NaCUR0+tXf0Io/YzXDuzMWk4Gq0FbJ+LWgfo1mXpj+iElmW4YS+oI6jyLVyQZEHZMvGcdiXyIZI76cdvqsjWL2C5e7O7p0ktTFmZfiBxHUUt3FbVZ+PqXk6H86uQ==

3. SSH Server에서 key를 read할 수 있도록 정책 설정

$ vi /vault/config/policy/ssh_policy.tf
path "ssh-client-signer/config/ca" {
  capabilities = ["read"]
}

4. SSH Server에서 key를 read할 수 있도록 정책 적용

$ cd /vault/config/policy/
$ vault policy write ssh-pubkey-read ssh_policy.tf
Success! Uploaded policy: ssh-pubkey-read

# SSH 정책 확인
$ vault policy list
default
ssh-pubkey-read

5. SSH Server에서 VAULT 서버로 접속할 Token 생성 → VAULT의 token값을 사용하기 위해서는 별도 저장 필요

$ vault token create -policy=ssh-pubkey-read
Key                        Value
---                         -----
token                     s.fX6YPfJI8xvi8NKZiI8O12UF
token_accessor       aUTigEQ0p2oNwoLUA8jMjWr6
token_duration       768h
token_renewable     true
token_policies         ["default" "ssh-pubkey-read"]
identity_policies      []
policies                   ["default" "ssh-pubkey-read"]

※ ssh-server에서 export 시켜주면 Vault 서버에서 SSH 인증서 public 키를 가져올 수 있음


6. SSH Client에서 Vault에 CA 사인 요청할 수 있도록 생성

  1. default_user
    • SSH 로그인에 사용될 유저 이름
    • default_user로 hippo 유저로 고정
    • 여러 사용자에 대해 허용할 것이라면 valid_principals 항목을 따로 정의해서 사용 필요
  1. ttl

    • CA가 사인한 키의 유효 기간을 의미

    • role을 통해 생성된 SSH 키는 5분 동안만 유효

      $ vault write ssh-client-signer/roles/my-role -<<"EOF"
      {
        "allow_user_certificates": true,
        "allowed_users": "*",
        "default_extensions": [
          {
            "permit-pty": ""
          }
        ],
        "key_type": "ca",
        "default_user": "hippo",
        "ttl": "5m0s"
      }
      EOF
      Success! Data written to: ssh-client-signer/roles/my-role

7. SSH Client에서 Vault에 CA 사인 update할 수 있도록 정책 설정

$ /vault/config/policy/
$ vi /vault/config/policy/sign_policy.tf
path "ssh-client-signer/sign/my-role" {
  capabilities = ["update"]
}

8. SSH Client에서 Vault에 CA 사인 update할 수 있도록 정책 적용

$ vault policy write require-ssh-sign sign_policy.tf
Success! Uploaded policy: require-ssh-sign

# 생성된 정책 확인
$ vault policy list
default
require-ssh-sign

9. SSH Client에서 VAULT 서버로 접속할 Token 생성 → VAULT의 token값을 사용하기 위해서는 별도 저장 필요

$ vault token create -policy=require-ssh-sign
Key                         Value
---                          -----
token                      s.HI8wvigrGvJuhUsk1H9ANmcl
token_accessor        0jom4yW0a8Nb4Cvw85NVCRtv
token_duration        768h
token_renewable      true
token_policies          ["default" "require-ssh-sign"]
identity_policies       []
policies                    ["default" "require-ssh-sign"]

※ ssh-client에서 export 시켜주면 Vault에서 CA 사인할 수 있는 public 키를 가져올 수 있음



SSH Server 설정 → SSH를 통해 접속하는 대상 서버

1. SSH Server에서 Vault server 접근할 수 있도록 환경변수 설정

  • VAULT_TOKEN 값은 SSH-Server에서 key를 read할 수 있도록 정책 설정 → s.fX6YPfJI8xvi8NKZiI8O12UF

  • VAULT 접속 도메인 설정 → [테스트 도메인]:8200

    # 환경 변수 적용
    export VAULT_ADDR=https://[테스트 도메인]:8200
    export VAULT_TOKEN=s.fX6YPfJI8xvi8NKZiI8O12UF
    
    # 영구적인 환경 변수 적용
    $ echo -e "\n#SSH\nexport VAULT_ADDR=https://[테스트 도메인]:8200" >> /etc/bashrc
    $ echo -e "export VAULT_TOKEN=s.fX6YPfJI8xvi8NKZiI8O12UF" >> /etc/bashrc

2. Vault server에서 CA의 public Key를 SSH Server에 전송

  • Vault server에서 public key 추출

      $ vault read -field=public_key ssh-client-signer/config/ca > /root/trusted-user-ca-keys.pem
    
      # 생성한 CA의 public key 확인
      $ cat /root/trusted-user-ca-keys.pem
      ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDALoH6ItfBjGnqNJAwFa1xUzOJ38WVosCNZQ3o2BlFJxhXvejU1CalDQDCZXEK8SIxeOqKC3mu/nIGMLYR5lwWRgl5r3janyLw8174MSM1wgSy9ZcURZrivicafKrH2WQAXJ6TKsrlhR5GODwxRCciD02l8LgbvaBs8pt1SRmvCc17xBAMuwu/A0pBU+tHotfiBqeLlQThi2mhpRSZ9u2cIt4neP/g+1/hMDxjlhmge7ehQBNYyWc2n8HDubCYT+z2GANk8vk6DhXs5iaLozM1lALXQZlbK3MpIVh1HySnIewuw3rjgBlOc9sJMcrnQYdrb/EnJzLrcL4Sb4NmzB5gPW29vTc0Thkt4T4ghTCriOLCFjKihlc9sO8rsZ6fV10i/qfUONcrZYp1wVS18WOKQEBblgXp94L/Kl/mRVHFRicNDSVsZij9pHZovRprE3RkqO87qF67ZhcL3yzAVfkoRrWqDzG5PFpjGHLa3mV3nmL1vBKhL9/tkxbxz6svWkPYFSbxZx+JRSzzp3VPqFzSEiBmFNloCCVZfdSUMi5RlAD7r2NaCUR0+tXf0Io/YzXDuzMWk4Gq0FbJ+LWgfo1mXpj+iElmW4YS+oI6jyLVyQZEHZMvGcdiXyIZI76cdvqsjWL2C5e7O7p0ktTFmZfiBxHUUt3FbVZ+PqXk6H86uQ==
    
      # scp를 통해 /etc/ssh 디렉토리 아래로 trusted-user-ca-keys.pem 파일 이동
      $ ls -al /etc/ssh/trusted-user-ca-keys.pem
      -rw-r--r-- 1 root root 725 Mar  1 17:39 /etc/ssh/trusted-user-ca-keys.pem

3. SSH 설정 → TrustedUserCAKeys /etc/ssh/trusted-user-ca-keys.pem 설정

$ vi /etc/ssh/sshd_config
Port 22000
#AddressFamily any
#ListenAddress 0.0.0.0
#ListenAddress ::

# TrustedUserCAKeys 추가
TrustedUserCAKeys /etc/ssh/trusted-user-ca-keys.pem

4. SSH 재시작 및 상태 확인

$ systemctl restart sshd

# 상태 확인
$ systemctl status sshd
● sshd.service - OpenSSH server daemon
   Loaded: loaded (/usr/lib/systemd/system/sshd.service; enabled; vendor preset: enabled)
   Active: active (running) since Tue 2022-03-01 17:47:07 KST; 3s ago
     Docs: man:sshd(8)
           man:sshd_config(5)
 Main PID: 62539 (sshd)
    Tasks: 1
   Memory: 1.0M
   CGroup: /system.slice/sshd.service
           └─62539 /usr/sbin/sshd -D



SSH Client 설정 → SSH 서버에 접속하는 SSH 클라이언트 서버

1. SSH Client에서 Vault server 접근할 수 있도록 환경변수 설정

  • VAULT_TOKEN 값은 SSH-Client에서 Vault Server에 CA사인 요청할 수 있도록 정책 설정 → s.HI8wvigrGvJuhUsk1H9ANmcl

  • VAULT 접속 도메인 설정 → https://[테스트 도메인]:8200

      # 환경 변수 적용
      export VAULT_ADDR=https://[테스트 도메인]:8200
      export VAULT_TOKEN=s.HI8wvigrGvJuhUsk1H9ANmcl
    
      # 영구적인 환경 변수 적용
      echo -e "\n#SSH\nexport VAULT_ADDR=https://[테스트 도메인]:8200" >> /etc/bashrc
      echo -e "export VAULT_TOKEN=s.HI8wvigrGvJuhUsk1H9ANmcl" >> /etc/bashrc

2. SSH key 생성 → 모두 enter로 넘어감

$ ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/root/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /root/.ssh/id_rsa.
Your public key has been saved in /root/.ssh/id_rsa.pub.
The key fingerprint is:
SHA256:ubVlwjtNjjxM9xoOdw6h8rSTF+zGfTeGt64/E1FIUwU root@180-70-134-115
The key's randomart image is:
+---[RSA 2048]----+
|             .E++|
|              ...|
|                .|
|         o     . |
|        S =.*   .|
|         * &oo . |
|        o %+*o+ .|
|         +oB=B.*o|
|          o+o.B=*|
+----[SHA256]-----+

3. 생성한 key를 Vault server에 사인 요청하고 응답 결과 key를 저장

$ vault write -field=signed_key ssh-client-signer/sign/my-role public_key=@/root/.ssh/id_rsa.pub > /root/.ssh/id_rsa-cert.pub

# id_rsa.pub에 사인 요청을 해서 접속 확인
$ cat /root/.ssh/id_rsa-cert.pub
ssh-rsa-cert-v01@openssh.com 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

4. SSH Client 에서 SSH Server로 접속 확인 → 결과 정상 접속

$ ssh -p 22000 hippo@[SSH 접속 서버 IP]
Last login: Tue Mar  1 17:59:26 2022 from [SSH 클라이언트 서버 IP]

5. 5분뒤 접속 가능한지 확인 → Vault server에서 sign 유효 시간을 5분으로 설정

  • test하기 위해 5분 뒤에 생성했던 id_rsa-cert.pub로 접속 시도 → 실패

  • 인증서 서명 유효기간이 만료되어 password로 접속 시도 하는 것을 확인 → 직접 비밀번호를 모르기에 오류 발생

  • 새롭게 id_rsa-cert.pub을 생성한 후에 다시 로그인 시도

    $ ssh -p 22000 hippo@[SSH 접속 서버 IP]
    no such identity: /root/.ssh/id_ed25519: No such file or directory
    hippo@[SSH 접속 서버 IP]'s password:
    
    # 새롭게 인증서 만들어서 로그인 시도
    $ vault write -field=signed_key ssh-client-signer/sign/my-role public_key=@/root/.ssh/id_rsa.pub > /root/.ssh/id_rsa-cert.pub
    $ ssh -p 22000 hippo@[SSH 접속 서버 IP]
    Last login: Tue Mar  1 17:59:55 2022 from [SSH 클라이언트 서버 IP]

Vault audit log(감사 로그)를 /var/log/ 아래의 파일에 기록하도록 설정

  • /var/log/vault_audit.log 파일에 기록, 수정 가능
    $ vault audit enable file file_path=/var/log/vault_audit.log
    Success! Enabled the file audit device at: file/


Vault audit log(감사 로그) 리스트를 조회

  • audit 로그 생성 위치와 type을 확인 가능
    $ vault audit list
    Path     Type    Description
    ----      ----       -----------
    file/     file       n/a


생성된 /var/log/vault_audit.log 파일 내용 확인

  • vault server 동작 내용 확인 가능
    $ cat /var/log/vault_audit.log
    {"time":"2022-02-28T05:16:33.223325954Z","type":"request","auth":{"token_type":"default"},"request":{"id":"f95c9fd1-8414-5a4f-316c-0631b936e8f0","operation":"update","namespace":{"id":"root"},"path":"sys/audit/test"}}
    # [...생략...]

  • root token : s.asQ2M2u8kefJAH7pTRK9VZuY
  • sealing된 vault에는 루트 토큰(root token)으로도 로그인 불가

unseal된 Vault 서버에 로그인

$ vault login
Token (will be hidden):
WARNING! The VAULT_TOKEN environment variable is set! This takes precedence
over the value set by this command. To use the value set by this command,
unset the VAULT_TOKEN environment variable or set it to the token displayed
below.

Success! You are now authenticated. The token information displayed below
is already stored in the token helper. You do NOT need to run "vault login"
again. Future Vault requests will automatically use this token.

Key                         Value
---                           -----
token                       s.asQ2M2u8kefJAH7pTRK9VZuY
token_accessor         DhyddellBSpndis790yPJCIp
token_duration         ∞
token_renewable       false
token_policies           ["root"]
identity_policies        []
policies                     ["root"]



Sealing된 상태에서 Vault 서버에 로그인 → 로그인 시 에러 발생

$ vault login
Token (will be hidden):
Error authenticating: error looking up token: Error making API request.

URL: GET https://[테스트 도메인]:8200/v1/auth/token/lookup-self
Code: 503. Errors:

* Vault is sealed

  • Vault 서버는 sealed(봉인) 상태로 시작 → sealed(봉인) 상태는 저장된 정보를 어떤 경우에도 평문으로 볼 수 없음
  • Vault 데이터를 암호화하여 저장하기에, Unsealing(개봉) 전에는 거의 모든 작업이 불가능
  • 암호화 키로 데이터를 암호화하고, 암호화 키는 키링에 보관하여 데이터와 함께 저장
  • 키링은 마스터 키로 알려진 다른 암호화 키로 암호화
  • Vault는 데이터를 복호화하기 위하여 먼저 암호화 키를 마스터 키를 이용하여 복호화 → Unsealing(복호화)는 마스터 키에 접근하는 프로세스
  • 마스터 키도 다른 모든 데이터와 함께 저장되지만 또 다른 메커니즘인 봉인해제 키(unseal key)에 의해 암호화

1. 데이터는 키링의 암호화 키로, 암호화 키는 마스터 키로, 마스터 키는 봉인해제 키로 암호화

"봉인해제 키 → 마스터 키 → 암호화 키 → 데이터"

2. Shamir’s Secret Sharing 샤미르의 비밀 공유

  • 봉인해제 키는 샤미르의 비밀 공유 알고리즘을 사용하여 여러 조각으로 분할되어 생성 (RSA에 'S'의 그 Shamir)
  • 각 봉인해제 키는 분리된 공간에 저장할 것을 권장
  • 봉인해제 시에 각 키를 입력 받아 임계치 이상의 키가 입력이 되면 봉인해제
  • Vault에서는 기본 값으로 5개의 키가 생성이 되고, 3개의 키가 입력되면 봉인해제


Unsealing(봉인해제)

  • Unsealing는 아래의 과정 중 하나를 통하여 수행
    1. 터미널에서 vault operator unseal 명령을 입력 후 Unsealing 키를 차례로 입력
    2. Unseal API 호출
    3. WebUI에서 키를 차례로 입력
  • Unsealing 상태에서 다시 sealing 상태가 되는 경우는 아래 중 하나의 방법으로 수행
    1. Seal API 호출
    2. Vault 서버 재시작
    3. Vault 저장소에서 복구할 수 없는 오류가 발생


Sealing(봉인)

  • sealing API를 호출하면 메모리에 있는 마스터 키가 폐기 → sealing 상태가 되면 vault를 사용하기 위해서 Unsealing 과정이 필요
  • sealing에는 루트 권한이 있는 단일 작업자만 필요
  • 침입이 감지되었을 때 Vault 데이터를 빠르게 잠가 피해를 최소화 가능


Vault server 초기화할 때 unseal key 발행

  • key-shares → 발행되는 unseal key 개수
  • key-threshold → 인증할 때 unseal key 사용 개수
  • key-shares와 key-threshold 옵션을 사용 X → 5개의 unseal key 발행되고, 그 중 3개를 입력해야 vault CLI 명령 사용 가능
  • vault 서버 초기화할 때는 vault 서버의 API를 설정 필요
    # VAULT_ADDR이 설정
    VAULT_ADDR='https://[테스트 도메인]:8200'

1. vault 서버 초기화

  • 5개의 unseal key 발행되고, 그 중 3개를 입력해야 VAULT 사용 가능

      # unseal key 발행
      $ vault operator init
      Unseal Key 1: ayPqZuzG7bBN3uRp/GYfUBIVg9MGrbIotnHrjYXNWQIm
      Unseal Key 2: iRntqlhFUfybvnjzLCHHxq/GQfDDtuPupxJyFqpLqpNc
      Unseal Key 3: ibTmbjiieSQPLB2ZnUNY2hrplvrr6ab1m8FnyY8moMMu
      Unseal Key 4: RdU4jDtYmAlF9bDFg8J3/l67Nph72ZFuyXlGWTzsP/ja
      Unseal Key 5: yO+uE3m3eCy/IgzU6qQFz4QC2mIlG6ZcVXC8ZH65u0Rr
    
      Initial Root Token: s.asQ2M2u8kefJAH7pTRK9VZuY
    
      Vault initialized with 5 key shares and a key threshold of 3. Please securely
      distribute the key shares printed above. When the Vault is re-sealed,
      restarted, or stopped, you must supply at least 3 of these keys to unseal it
      before it can start servicing requests.
    
      Vault does not store the generated master key. Without at least 3 keys to
      reconstruct the master key, Vault will remain permanently sealed!
    
      It is possible to generate new unseal keys, provided you have a quorum of
      existing unseal keys shares. See "vault operator rekey" for more information.

2. unseal키 수동 설정하여 vault 서버 초기화 (예시)

  • unseal 키 1개 설정하였으며, 1개의 unseal키를 입력하면 vault 서버 사용 가능

      # unseal key 발행
      $ vault operator init -key-shares=1 -key-threshold=1
    
      Unseal Key 1: 8vTbeSYOS0yWMve+NFy2eisJQkbqaPOb6ocp3zkHS1c=           # Unseal key 1개 발행 확인 -> 보관 필요
    
      Initial Root Token: s.s15mhZCNj4oOwcYji8TuIx5h                                        # Root Token 도 1개 발행 -> 보관 필요
    
      Vault initialized with 1 key shares and a key threshold of 1. Please securely
      distribute the key shares printed above. When the Vault is re-sealed,
      restarted, or stopped, you must supply at least 1 of these keys to unseal it
      before it can start servicing requests.
    
      Vault does not store the generated master key. Without at least 1 keys to
      reconstruct the master key, Vault will remain permanently sealed!
    
      It is possible to generate new unseal keys, provided you have a quorum of
      existing unseal keys shares. See "vault operator rekey" for more information.

3. vault 초기화 후 vault 서버 상태 확인

$ vault status
Key                       Value
---                        -----
Seal Type               shamir
Initialized               true
Sealed                   true
Total Shares           5                  # unseal 발행 개수
Threshold               3                  # unseal 인증 개수
Unseal Progress      0/3
Unseal Nonce         n/a
Version                  1.9.3
Storage Type          file
HA Enabled            false



Root Token인 VAULT_TOKEN을 환경 변수 지정

  • VAULT_TOKEN은 Vault를 초기화(init)할 때 root token이라고 획득 → s.asQ2M2u8kefJAH7pTRK9VZuY
  • Vault 서버의 루트 권한이 있는 토큰
    1. 서버의 모든 작업을 가능
    2. sealed 상태로 변경 가능
    3. unseal 상태로 변경은 불가능
  • vault 토큰은 VAULT_TOKEN 환경변수에 저장하면 vault 클라이언트에서 사용 가능

    # vault token을 환경 변수에 지정
    $ export VAULT_TOKEN=s.asQ2M2u8kefJAH7pTRK9VZuY
    $ echo -e "export VAULT_TOKEN=s.asQ2M2u8kefJAH7pTRK9VZuY" >> /root/.bashrc
    
    # VAULT에서 지정한 환경변수 확인
    $ env | grep -i vault
    VAULT_ADDR=https://[테스트 도메인]:8200
    VAULT_TOKEN=s.asQ2M2u8kefJAH7pTRK9VZuY



Vault Server를 unseal

  • vault init을 할 때 받은 unseal key를 입력 → 초기화할 때 발행받은 5개 중에 3개만 입력하면 됨
    1. 첫번째 : iRntqlhFUfybvnjzLCHHxq/GQfDDtuPupxJyFqpLqpNc
    2. 두번째 : ibTmbjiieSQPLB2ZnUNY2hrplvrr6ab1m8FnyY8moMMu
    3. 세번째 : RdU4jDtYmAlF9bDFg8J3/l67Nph72ZFuyXlGWTzsP/ja
  • 기본적으로 Vault server를 초기화하면 Sealed 상태가 됨 → 즉, 잠겨있어서 Vault 서버에서 정보를 읽는 등 작업 불가능
  • Vault server를 init에 알려준 Unseal Key를 이용하면 Unsealed 상태로 변경 → Vault 서버의 정보 읽기, 쓰기 등 여러 작업 가능

1. vault Server unseal (봉인 해제)

  • 3번의 vault operator unseal을 통해 봉인 해제

  • unseal 명령어를 성공할 때마다 Unseal Progress의 수치가 올라가서 3/3이 되었을 때 unseal이 됨

  • unseal이 완료 → Sealed : false

    # 첫번째 Vault unseal
    $ vault operator unseal
    Unseal Key (will be hidden):
    Key                Value
    ---                -----
    Seal Type          shamir
    Initialized        true
    Sealed             true
    Total Shares       5
    Threshold          3
    Unseal Progress    1/3
    Unseal Nonce       ccbf047b-5956-fdd8-29f9-bad6969cdd45
    Version            1.9.3
    Storage Type       file
    HA Enabled         false
    
    # 두번째 Vault unseal
    $ vault operator unseal
    Unseal Key (will be hidden):
    Key                Value
    ---                -----
    Seal Type          shamir
    Initialized        true
    Sealed             true
    Total Shares       5
    Threshold          3
    Unseal Progress    2/3
    Unseal Nonce       ccbf047b-5956-fdd8-29f9-bad6969cdd45
    Version            1.9.3
    Storage Type       file
    HA Enabled         false
    
    # 세번째 Vault unseal
    $ vault operator unseal
    Unseal Key (will be hidden):
    Key             Value
    ---             -----
    Seal Type       shamir
    Initialized     true
    Sealed          false
    Total Shares    5
    Threshold       3
    Version         1.9.3
    Storage Type    file
    Cluster Name    vault-cluster-0b89b3d2
    Cluster ID      dbea1671-61fa-c4c8-387e-e32390ace762
    HA Enabled      false

※ unseal 참고 사항

  • 연속적으로 unseal을 3번 실행하여 Vault 서버를 열 수 있다.
  • 하지만, 한사람이 unseal key를 다 가지는 것은 위험 → 키를 5명이 나누어 갖고 3명이 unseal을 하면 Vault 서버를 엶
  • Vault에 중요한 정보가 있음으로 서버를 재시작하거나 보안 문제 발생으로 sealed 상태로 바꾸었을 때, 다시 서버를 열려면 unseal 단계를 거쳐야 함
  • unseal 과정은 서버가 기억하고 있으므로 한자리에 모일 필요 없이 각자의 자리에서 unseal을 하기만 하면 됨
  • unseal은 시간 제한도 없기에 충분히 시간을 두고 진행 가능
  • 스토리지 백엔드에도 암호화된 정보가 저장되어 있고, 복호화하려면 unseal 단계가 필수적 → unseal 키 3개가 없으면 Vault 서버도 복호화키도 만들어 낼 수 없음
  • DB가 통째로 털려도 Vault 서버가 통째로 털려도 unseal 키 3개가 없다면 공격자는 정보를 볼 수 없게 되어 안전함

2. unseal할 때 출력한 vault server 상태와 vault status 명령어로 출력되는 상태가 같은 지 확인

  • vault server가 unseal됨 → token을 입력해야 서버 내부 접근 가능

    $ vault status
    Key                 Value
    ---                   -----
    Seal Type         shamir
    Initialized         true
    Sealed             false
    Total Shares      5
    Threshold         3
    Version             1.9.3
    Storage Type     file
    Cluster Name    vault-cluster-0b89b3d2
    Cluster ID         dbea1671-61fa-c4c8-387e-e32390ace762
    HA Enabled       false



Vault Server를 Sealing

  • vault를 서버에 보안 문제가 있거나, 안전하게 관리하기 위해서 unsealing되어있는 것을 sealing으로 변경

      $ vault operator seal
      Success! Vault is sealed.
  • 다시 sealing된 vault 서버의 상태 확인

      $ vault status
      Key                    Value
      ---                   -----
      Seal Type          shamir
      Initialized          true
      Sealed               true
      Total Shares        5
      Threshold           3
      Unseal Progress     0/3
      Unseal Nonce       n/a
      Version               1.9.3
      Storage Type        file
      HA Enabled          false

  • systemd 서비스 사용하여 vault 서버 구축
  • Vault 실 서버에 사용할 도메인 및 인증서 필요
  • VAult 관련 참고 URL : https://www.vaultproject.io/docs

Valut 설치 및 systemd로 서비스 관리

1. Vault 설치 및 패키지 버전 확인

$ yum install -y yum-utils
$ yum-config-manager --add-repo https://rpm.releases.hashicorp.com/RHEL/hashicorp.repo
$ yum -y install vault

2. 설치한 vault 패키지 버전 확인

$ vault version
Vault v1.9.3 (7dbdd57243a0d8d9d9e07cd01eb657369f8e1b8a)

3. Vault 서비스를 systemd로 관리

$ vi /etc/systemd/system/vault.service
[Unit]
Description=Vault secret store
Wants=network-online.target
After=network-online.target

[Service]
Type=simple
ExecStart=/usr/bin/vault server -config=/vault/config/vault-config.hcl
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target



Vault 서버 실행

  • vault를 다운받게 되면 기본적으로 /opt/vault/ 디렉토리 아래에 있는 data 디렉토리와 tls 디렉토리 생성
  • data 디렉토리는 Vault 데이터가 저장되는 공간
  • tls 디렉토리는 Vault의 https를 사용하기 위한 인증서 키가 있는 공간 → https를 사용할 인증서 설치 필요
  • /vault/ 디렉토리 data 디렉토리와 tls 디렉토리 복사 필요

1. Vault config 적용 → https://[Vault 서버 도메인]:8200

  • Vault Server는 configuration file을 통해서 configuration을 얻음.

  • vault-config.hcl 이름을 가진 configuration 파일을 통해서 Vault Server에서 사용할 configuration 확인 가능

  • 기본 Vault 서버의 API를 사용하는 주소를 설정 → VAULT_ADDR=’https://[Vault 서버 도메인]:8200’

  • vault-config.hcl을 생성하기 윈한 내용 참고 URL : https://www.vaultproject.io/docs/configuration

    $ mkdir /vault
    $ cp -r /opt/vault/* /vault/
    $ ls /vault/
    data  tls
    
    $ mkdir /vault/config/
    
    # vault-config를 설정할 hcl 생성
    $ vi /vault/config/vault-config.hcl
    # ui 사용 허가
    ui = true
    
    # Vault 데이터가 저장되는 스토리지 백엔드를 구성
    storage "file" {
    path = "/vault/data"
    }
    
    # API 서버의 IP와 PORT 선언
    api_addr = "https://[Vault 서버 도메인]:8200"
    
    # HTTP listener -> HTTP 사용 X
    #listener "tcp" {
    #  address = "0.0.0.0:8200"
    #  tls_disable = 1
    #}
    
    # HTTPS listener -> HTTPS 사용
    listener "tcp" {
    address       = "0.0.0.0:8200"
    tls_cert_file = "/vault/tls/tls.crt"
    tls_key_file  = "/vault/tls/tls.key"
    }
    
    # 기본 VAULT_ADDR은 "https://127.0.0.1:8200"으로 되어있기에 vault 서버의 API를 사용하는 주소를 설정 
    # VAULT_ADDR='https://[Vault 서버 도메인]:8200'
    $ export VAULT_ADDR='https://[Vault 서버 도메인]:8200'
    $ echo -e "export VAULT_ADDR='https://[Vault 서버 도메인]:8200'" >> /root/.bashrc
    $ echo $VAULT_ADDR
    https://[Vault 서버 도메인]:8200

2. vault.service를 재시작하여 설정 적용

$ systemctl daemon-reload
$ systemctl start vault.service
$ systemctl status vault
● vault.service - Vault secret store
   Loaded: loaded (/etc/systemd/system/vault.service; disabled; vendor preset: disabled)
   Active: active (running) since Sun 2022-02-27 16:42:28 KST; 24s ago
 Main PID: 14832 (vault)
   CGroup: /system.slice/vault.service
           └─14832 /usr/bin/vault server -config=/vault/config/vault-config.hcl

3. config 설정을 적용한 vault 서버가 정상적으로 실행되는 지 확인 → API

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


# 외부에서 https://[Vault 서버 도메인]:8200 도메인 요청하여  vault server에게 API 성공 확인
$ curl https://[Vault 서버 도메인]:8200/v1/sys/seal-status | jq
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   168  100   168    0     0     29      0  0:00:05  0:00:05 --:--:--    40
{
  "type": "shamir",
  "initialized": false,
  "sealed": true,
  "t": 0,
  "n": 0,
  "progress": 0,
  "nonce": "",
  "version": "1.9.3",
  "migration": false,
  "recovery_seal": false,
  "storage_type": "file"
}

4. config 설정을 적용한 vault 서버에 status 확인

# vault 서버에서 vault status 명령어 확인
$ vault status
Key                     Value
---                      -----
Seal Type            shamir
Initialized            false
Sealed                 true
Total Shares         0
Threshold             0
Unseal Progress    0/0
Unseal Nonce        n/a
Version                1.9.3
Storage Type        file
HA Enabled          false



Vault UI 웹페이지 Open

  1. https://[Vault 서버 도메인]:8200으로 접속
  2. 아직 Unseal을 하지 않았기 때문에 접근은 불가능 → unseal을 하면 웹 페이지 안에 접근 가능

+ Recent posts