2022. 5. 27. 15:45

[Cloud] 초보자를 위한 Pacemaker + corosync 기반의 클러스터 구축 및 Virtual IP 설정하기(긁어온글)

이번 포스트에서는 pacemaker와 corosync로 Virtual IP를 설정하는 방법을 설명한다. 

 

너무 쉬워서 이걸 굳이 글로 써야하나 고민을 많이 했지만, 이왕 해본 겸 기록하기로 했다.

 

 

 

1. Heartbeat, Keepalived, Pacemaker

 

얼마 전에 남아도는 라즈베리파이에 쿠버네티스를 올려서 HomeLab을 구축해 사용하고 있다. 음악 스트리밍 서비스를 올려서 아이폰에서 잘 쓰고는 있지만.. iptime 공유기에서 특정 라즈베리파이의 IP로 포트포워딩 했기 때문에, 해당 라즈베리파이가 고장날 경우 서비스가 불가능한 상황이 발생할 수 있었다. 대학원 시절 라즈베리파이가 갑자기 고장나는 것을 몇 번 겪었기 때문에, 가능하면 HA나 Failover 대책을 세워두고 싶었다.

 

 

 

가장 먼저 떠오르는 방법은 heartbeat, keepalived, pacemaker 등으로 Virtual IP로 만든 뒤, iptime에서 Virtual IP로 포워딩하는 방식이었다. heartbeat는 구성이 간단하긴 하지만 3대 이상의 IP Failover의 레퍼런스가 거의 전무하며,1 keepalived는 HAProxy가 중간에 끼어들어드는 구조가 영 탐탁지 않았다. 그럼 남은것은 pacemaker인데, 어차피 예전에 학부생 때 온프렘 클러스터에서 써본적이 있기 때문에 remind 겸 다시 써보기로 했다.

 

근데 막상 다 구축하고 보니, 단순히 IP Faillover 기능만 쓸거라면 pacemaker는 너무 무겁다는 생각이 들었다. keepalived로 하면 훨씬 간단한 구조로 쉽게 할 수 있을 것 같은데, 굳이 pacemaker를 쓸 필요가 있었을까. HomeLab에서 HAProxy 때문에 문제될만한 서비스를 운영할 일이 있을까..?

 

 

 

2. Pacemaker와 corosync

 

2.1 Pacemaker, corosync란 무엇인가?

 

대부분의 pacemaker 레퍼런스는 자세한 개념을 설명해주지 않아 여러모로 삽질을 많이 했다. 일반적으로 pacemaker를 사용할 때는 corosync라는 도구를 함께 쓰는데, 그 역할은 아래와 같다.

 

1. corosync : 저수준의 인프라를 관리해주는 역할이라고 많은 사람들이 두루뭉술하게 설명하는데, 이를 좀 더 구체적으로 설명하자면 "노드 간의 멤버쉽, 쿼럼, 메시징" 정도가 될 수 있다. 즉, corosync는 클러스터 내의 노드 간 Discovery, 통신, 동기화 작업 등을 담당한다. 한 가지 특이한 점은, etcd같은 분산 코디네이터 없이 Mesh 형태로 동작하는 것으로 보인다.

 

2. pacemaker : corosync의 기능을 이용해 클러스터의 리소스 제어 및 관리를 수행하며, 사용자 입장에서 클러스터의 특정 기능을 사용할 때는 대부분 pacemaker를 호출하게 된다. 예를 들어, Virtual IP 리소스를 생성하려면 pacemaker의 CLI를 사용하지만, pacemaker는 Virtual IP 리소스 할당을 위해 내부적으로 corosync의 인프라 정보를 사용한다.

 

 

pacemaker와 corosync와는 별개로 pcs (pacemaker/corosync configuration system) 이라는 도구가 따로 존재하는데, pcs는 pacemaker와 corosync를 좀 더 쉽게 설정할 수 있도록 도와주는 CLI 명령어 도구이다. 역으로 말하자면 굳이 pcs를 사용하지 않아도 pacemaker 클러스터를 구축하고 사용할 수는 있지만, 대부분의 레퍼런스가 pcs를 기준으로 설명하고 있으므로 웬만하면 pcs를 사용하는 것이 권장된다.

 

 

2.2 Pacemaker, corosync 설치

 

이번 포스트에서는 kernel: 4.19가 설치된 3개의 Rasbian Buster 노드를 기준으로 설명한다. 하지만 설치 방법은 Ubuntu 16.04를 참고했기 때문에, 데비안 계열이기만 하면 쉽게 따라할 수 있을것이라 생각한다. 또한, 실제 pacemaker 클러스터에서는 적어도 3개의 스위치 (SAN, heartbeat, service) 가 필요한 것 같지만, 여기서는 단일 IP를 사용하는 간소화된 방법을 사용한다.

 

시작하기 전, /etc/hosts에 각 노드의 이름과 IP를 명시한다.

 

1
2
3
worker0 : 192.168.0.50
worker1 : 192.168.0.51
worker2 : 192.168.0.52
cs

 

모든 노드에서 아래의 명령어를 입력해 각 도구를 설치한다. crmsh는 pcs 이전에 사용되던 클러스터 관리용 CLI 도구인데, 굳이 설치하지 않아도 큰 문제는 없다. crmsh의 CLI인 crm 명령어로 사용할 수 있는 기능은 pcs에서 동일하게 사용할 수 있기 때문이다.

 

1
$ apt-get install pacemaker corosync crmsh pcs
cs

 

설치된 각 도구의 버전은 아래와 같다.

 

1
2
3
4
corosync : (3.0.1-2).
crmsh : (4.0.0~git20190108.3d56538-3).
pacemaker : (2.0.1-5).
pcs : (0.10.1-2).
cs

 

노드 중 하나에서, corosync 통신을 위한 auth key를 생성한 뒤 이를 다른 노드로 복사한다.

 

1
2
3
4
5
6
7
root@worker0:/ corosync-keygen
Corosync Cluster Engine Authentication key generator.
Gathering 2048 bits for key from /dev/urandom.
Writing corosync key to /etc/corosync/authkey.
 
root@worker0:/ scp /etc/corosync/authkey root@worker1:/etc/corosync/
root@worker0:/ scp /etc/corosync/authkey root@worker2:/etc/corosync/
cs

 

모든 노드에서, 아래의 내용으로 /etc/corosync/corosync.conf 파일을 작성한다. 단, 중간의 bindnetaddr과 nodelist는 각 노드의 IP로 적절히 변경해 사용한다. 또한, 사용할 수 있는 세부 옵션들은 man corosync.conf로 확인해도 되고, 여기 링크를 참고해도 된다. 필요한 옵션을 적당히 찾아서 사용할 것을 추천한다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
totem {
  version: 2
  cluster_name: lbcluster
  transport: udpu
  interface {
    ringnumber: 0
    bindnetaddr: 192.168.0.50 각 노드에 맞게 IP를 변경해 사용한다.
    broadcast: yes
    mcastport: 5405
  }
}
 
quorum {
  provider: corosync_votequorum
  expected_votes: 3
}
 
nodelist {
  node {
    ring0_addr: 192.168.0.50
    name: worker0
    nodeid: 1
  }
  node {
    ring0_addr: 192.168.0.51
    name: worker1
    nodeid: 2
  }
  node {
    ring0_addr: 192.168.0.52
    name: worker2
    nodeid: 3
  }
}
 
logging {
  to_logfile: yes
  logfile: /var/log/corosync/corosync.log
  to_syslog: yes
  timestamp: on
}
cs

 

사실 pcs로 corosync 설정을 할 수도 있긴 한데.. apt 저장소에서 기본으로 설치되는 pcs는 이상하게도 corosync 구축 기능이 잘 동작하지 않아서, 직접 conf 파일을 작성하는 방법을 택했다. 어느 방법을 사용하든지 크게 문제는 없을 것으로 보인다.

 

그 뒤, corosync 서비스를 시작하고 나서 각 노드가 잘 잡혔는지 확인한다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ systemctl enable corosync     
$ systemctl start corosync     
 
$ corosync-cmapctl | grep members
runtime.members.1.config_version (u64) = 0
runtime.members.1.ip (str= r(0) ip(192.168.0.50)
runtime.members.1.join_count (u32) = 1
runtime.members.1.status (str= joined
runtime.members.2.config_version (u64) = 0
runtime.members.2.ip (str= r(0) ip(192.168.0.51)
runtime.members.2.join_count (u32) = 1
runtime.members.2.status (str= joined
runtime.members.3.config_version (u64) = 0
runtime.members.3.ip (str= r(0) ip(192.168.0.52)
runtime.members.3.join_count (u32) = 1
runtime.members.3.status (str= joined
cs

 

여기까지 잘 됐다면 pcs도 문제없이 사용할 수 있다. 앞으로 pacemaker를 쓸 때는 pcs 명령어를 사용하면 된다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ systemctl enable pcsd
$ systemctl start pcsd
 
$ pcs status
Cluster name: lbcluster
Stack: corosync
...
 
3 nodes configured
0 resource configured
 
Online: [ worker0 worker1 worker2 ]
 
...
cs

 

 

 

2.3 resource 개념 및 사용하기

 

pacemaker에는 프로바이더와 리소스라는 개념이 존재한다. 특정 프로바이더는 여러 리소스 기능을 제공하고, 그 기능을 이용해 원하는 리소스를 생성하면 된다. 최신 버전의 pacemaker에서 사용 가능한 프로바이더의 목록은 아래의 명령어로 확인할 수 있다. (원래는 openstack도 있는 것 같은데, 기본적으로 활성화되어 있지는 않은 것 같다)

 

1
2
3
4
root@worker0:~ pcs resource providers
heartbeat
pacemaker
redhat
cs

 

heartbeat 프로바이더에서 사용 가능한 리소스의 목록을 출력해보자.2 참고로, pcs 명령어는 어느 노드에서 실행하든지 상관이 없다. 별도의 role을 줘서 권한을 제한할 수 있을 것 같긴 한데, 지금 당장 필요한 기능은 아니라서 굳이 다루지 않기로 했다.

 

1
2
3
4
5
6
7
8
9
root@worker0:~ pcs resource agents ocf:heartbeat
...
IPaddr
IPaddr2
ipsec
IPsrcaddr
IPv6addr
...
ZFS
cs

 

VIP를 사용하기 위해서는 heartbeat 프로바이더의 IPaddr2라는 리소스를 사용하면 된다. 바로 이 리소스를 생성.. 해도 되지만, 그 전에 몇 가지 설정이 필요하다. 먼저, stickiness라는 기본 값을 설정하고 시작한다. 

 

1
$ pcs resource defaults resource-stickiness=60
cs

 

리소스 (지금은 Virtual IP) 가 특정 노드에 할당되고 난 뒤, 해당 노드가 Fail하면 리소스가 다른 노드로 옮겨가는 Failover가 발생하게 된다. 이 때, Fail했던 노드가 다시 되살아날 경우 리소스가 다시 이동하지 않도록 설정하는 값이 resource-stickiness라고 한다. 이 값은 리소스를 다른 노드로 옮길 때의 비용을 의미한다곤 하는데, 이 값을 세부적으로 어떻게 튜닝할 수 있을지는 좀 더 고민해 볼 필요가 있어보인다. 

 

또 다른 설정으로는 STONITH3 비활성화가 있다. STONITH는 무서우면서도 재밌는 기능인데, 다른 노드에 장애가 발생할 경우 해당 노드의 물리적 전원을 내림으로써 Shared Storage에 접근하는 것을 Fencing한다. I/O Fence라는 관점에서는 굉장히 (?) 재밌는 기능이지만, 지금 당장은 필요하지 않기 때문에 비활성화하기로 했다. 참고로 이 기능을 끄지 않으면 리소스 생성 시 에러가 출력된다.

 

1
2
3
4
5
6
7
8
9
$ pcs property set stonith-enabled=false # 또는 crm configure property stonith-enabled=false
 
$ pcs property
Cluster Properties:
 cluster-infrastructure: corosync
 cluster-name: debian
 dc-version: 2.0.1-9e909a5bdd
 have-watchdog: false
 stonith-enabled: false
cs

 

드디어 Virtual IP를 생성할 차례가 되었다. 아래의 명령어로 VIP를 생성한다.

 

1
2
3
$ pcs resource create kubernetes_api_port ocf:heartbeat:IPaddr2 \
ip=192.168.0.150 cidr_netmask=32 op monitor interval=5s \
meta migration-threshold="2" failure-timeout="60s"
cs

 

옵션들에 대한 간단한 설명은 아래와 같다. 모든 옵션에 대한 설명 여기를 참고할 수 있다.

 

 

  • kubernetes_api_port : 이 리소스의 고유한 이름이다.
  • ocf:heartbeat:IPaddr2 : 이 리소스의 클래스(카테고리)이다.4 heartbeat 프로바이더의 IPaddr2를 사용한다.
  • ip=192.168.0.150 : Virtual IP에 할당될 IP를 의미한다. 별도의 constraint가 설정되어 있지 않다면, 클러스터 내의 노드 중 하나에 192.168.0.150이라는 IP가 추가로 설정된다. (heartbeat를 쓰면 parent-child NIC 형태가 되는 것 같지만, pacemaker를 쓰면 ip addr add 명령어를 썼을 때와 동일한 형태가 된다) 

 

 

VIP가 생성된 뒤, 해당 리소스의 정보를 확인해본다. 192.168.0.150 IP가 노드 중 하나에 할당되었을 것이다.

 

1
2
3
4
5
6
7
root@worker0:/ pcs resource config kubernetes_api_port
 Resource: kubernetes_api_port (class=ocf provider=heartbeat type=IPaddr2)
  Attributes: cidr_netmask=32 ip=192.168.0.150
  Meta Attrs: failure-timeout=60s migration-threshold=2
  Operations: monitor interval=5s (kubernetes_api_port-monitor-interval-5s)
              start interval=0s timeout=20s (kubernetes_api_port-start-interval-0s)
              stop interval=0s timeout=20s (kubernetes_api_port-stop-interval-0s)
cs

 

1
2
3
4
5
6
7
8
9
root@worker0:~ ip a | grep 192
    inet 192.168.0.50/24 brd 192.168.0.255 scope global noprefixroute eth0
    inet 192.168.0.150/32 brd 192.168.0.255 scope global eth0
 
root@worker1:~ ip a | grep 192
    inet 192.168.0.51/24 brd 192.168.0.255 scope global noprefixroute eth0
 
root@worker2:~ ip a | grep 192
    inet 192.168.0.52/24 brd 192.168.0.255 scope global noprefixroute eth0
cs

 

위 예제에서는 worker0에 할당되었으며, worker0이 Down될 경우 worker1이나 worker2로 VIP가 인계된다. 즉, 클러스터 내부에서는 여전히 계속 VIP로 접근할 수 있는 셈이다. 보통은 IPVS 등으로 VIrtualIP의 종착점을 LB Target로 설정해서 쓰는 것 같은데, 나는 쿠버네티스의 NodePort 및 API Endpoint 용도로 사용할 것이기 때문에 Routing Rule은 굳이 다루지 않기로 했다.

 

리소스는 pcs resource delete로 간단히 삭제할 수 있다.

 

1
2
root@worker2:/ pcs resource delete kubernetes_api_port
Attempting to stop: kubernetes_api_port... Stopped
cs

 

 

 

끝.

 

참고 자료

 

[1] High Availability 를 위한 VRRP와 keepalived (1)

[2] Pacemaker를 통한 Clustering (서버 이중화)

[3] AllThatLinux의 모든 Article

[4] ClusterLab의 pacemaker 관련 문서들

[5] 리눅스 HA(corosync, pacemaker) – Part 1 

[6] [오픈소스컨설팅]RHEL7/CentOS7 Pacemaker기반-HA시스템구성-v1.0

[7] [ICON DEVPORTAL] Creating HA network Stage 2


1. Mailing List에 해답이 있는 것 같긴 한데, 굳이 그렇게까지 찾아보고 싶지는 않았다.
2. ocf는 Open Cluster Framework의 줄임말이다.
3. Shoot The Other Node In The Head
4. crm에서는 이를 클래스라고 부른다. crm ra info ocf:heartbeat:IPaddr2 명령어로 자세한 옵션을 볼 수도 있다.

 

 

- 원본

https://m.blog.naver.com/alice_k106/221786625711