Production 환경에서 HA 는 매우 중요하면서도 필수적인 요소입니다.

단일 서버로 서비스를 할 수는 없겠지요.

이번 포스팅에서는 HA 구성에 대해서 알아보도록 하겠습니다.




Master Node 의 HA 구성



모든 요청은 Master Node 의 Api Server 를 거치게 되어 있습니다.

이곳에서 각종 Switching 과 Network Traffic 이 발생되지요.




사전 준비




이번에 구성할 구조는 Master Node 3대입니다.

먼저 각 서버에 Docker 와 Kubernetes 가 설치되어 있어야 합니다.


설치방법은 이전 Kubernetes 설치 포스팅을 참고해 주세요.

http://crystalcube.co.kr/198?category=834418


Docker 와 Kubernetes 간편 설치를 위한 스크립트를 사용하셔도 됩니다. :)

DockerWithKubernetes_Common.txt



설치가 완료되고나면, 이제 HA 구성을 해 보도록 합시다.





파일 전송을 위한 준비




HA 구성을 하다보면, 인증서를 복사해야 하는 일이 많습니다.

매번 아이디와 비밀번호를 일일이 입력하면서 옮기는건 매우 귀찮은 일이므로, ssh 설정을 먼저 하도록 합니다.


모든 서버에서는 ssh 가 구동되고 있어야 합니다. 확인은 아래처럼 가능합니다.


eval $(ssh-agent)


먼저 master node 1번에서 아래처럼 ssh 인증키를 생성합니다.

나중에 복사해야 하는 파일들이 root 권한이 필요하기 때문에, ssh 파일인증서 또한 root 계정에 만듧니다.

아래처럼 환경을 그대로 가져간 채로 root 로 계정으로 전환한 후,,


$ sudo -Es



# ssh-keygen -t rsa


인증서가 제대로 만들어 졌다면, ${HOME}/.ssh/ 위치에 id_rsa 와 id_rsa.pub 두개의 키가 생성되어 있을 것입니다.

이것을 master node 002 와 003 에 복사를 합니다.


# ssh-copy {MasetrNode2}

# ssh-copy {MasetrNode3}


[MasterNode2] 와 [MasterNode3] 부분에는 각각 서버의 HostName 이나 IP 를 입력하여야 합니다.



제대로 인증서가 설치되었으면, 아래처럼 2번과 3번 호스트에 비밀번호 없이 접속이 되는지 확인해 봅니다.


# ssh -A 10.0.0.7







Master Node 를 위한 Load Balancer




로드밸런서를 먼저 구축해야 합니다.

3개의 Master Node 에 대해서 Load Balancer 를 달아줍니다.

6443 포트를 각 Master Node 의 6443 포트로 Balancing 해 줍니다.


그리고 Domain 을 하나 따야하는데, 이번 포스팅에서는 그냥 VIP 를 직접 사용하도록 하겠습니다.





HA 구성을 하기위한 환경변수 설정




HA 구성을 위해서는 Kubernetes 버전이 최소 1.11.1 이상이어야 합니다.

버전 확인은 아래처럼 가능합니다.


$ kubelet --version

Kubernetes v1.11.3


구성하는 동안 각종 서버정보를 입력해야 합니다.

이것을 일일이 수정하는것은 번거로우므로, 먼저 환경변수를 정의해 두고 사용하겠습니다.

서버 정보는 여러분의 환경에 맞게 수정해 주세요.

호스트 네임을 제대로 입력해 주어야 합니다. (hostname 출력값)


export KUBERNETES_VER=v1.11.3

export LOAD_BALANCER_DNS=10.113.228.217

export LOAD_BALANCER_PORT=6443

export CP1_HOSTNAME=test-kube-master001

export CP1_IP=10.106.222.115

export CP2_HOSTNAME=test-kube-master002

export CP2_IP=10.106.223.113

export CP3_HOSTNAME=test-kube-master003

export CP3_IP=10.105.187.187


위 환경변수는 3개의 서버에 모두 세팅해 주어야 합니다.






첫번째 Master Node




첫번째 서버에 접속을 합니다. master001 이 되겠지요.

kubeadm 초기화를 위한 yaml 설정파일을 생성합니다.


Kubernetes 버전은 여러분 버전을 정확히 입력해야 합니다. 주의하세요.

최소 1.11.1 이상 버전을 사용해야 합니다.

버전은 아래처럼 확인 가능합니다.


$ kubelet --version

Kubernetes v1.11.3



이제 kubeadm-config.yaml 을 만들어 줍니다.


$ cat <<EOF> ./kubeadm-config.yaml 

apiVersion: kubeadm.k8s.io/v1alpha2

kind: MasterConfiguration

kubernetesVersion: ${KUBERNETES_VER}

apiServerCertSANs:

- "${LOAD_BALANCER_DNS}"

api:

    controlPlaneEndpoint: "$LOAD_BALANCER_DNS:$LOAD_BALANCER_PORT"

etcd:

  local:

    extraArgs:

      listen-client-urls: "https://127.0.0.1:2379,https://${CP1_IP}:2379"

      advertise-client-urls: "https://${CP1_IP}:2379"

      listen-peer-urls: "https://${CP1_IP}:2380"

      initial-advertise-peer-urls: "https://${CP1_IP}:2380"

      initial-cluster: "${CP1_HOSTNAME}=https://${CP1_IP}:2380"

    serverCertSANs:

      - $CP1_HOSTNAME

      - $CP1_IP

    peerCertSANs:

      - $CP1_HOSTNAME

      - $CP1_IP

networking:

    # This CIDR is a Calico default. Substitute or remove for your CNI provider.

    podSubnet: "10.244.0.0/16"

EOF



ipv4 forwarding 가능하도록 설정해 주고,


$ sudo bash -c 'echo 1 > /proc/sys/net/ipv4/ip_forward'



kubeadm 을 초기화 해 줍니다.


$ sudo kubeadm init --config kubeadm-config.yaml



초기화가 끝나면 아래 파일들이 생겨납니다.

이 파일들은 나머지 두개의 Master Node 에 동일한 경로에 복사해 줍니다.


/etc/kubernetes/pki/ca.crt

/etc/kubernetes/pki/ca.key

/etc/kubernetes/pki/sa.key

/etc/kubernetes/pki/sa.pub

/etc/kubernetes/pki/front-proxy-ca.crt

/etc/kubernetes/pki/front-proxy-ca.key

/etc/kubernetes/pki/etcd/ca.crt

/etc/kubernetes/pki/etcd/ca.key

/etc/kubernetes/admin.conf



# USER=ubuntu # customizable

# CONTROL_PLANE_IPS="$CP2_IP $CP3_IP"

# for host in ${CONTROL_PLANE_IPS}; do

    scp /etc/kubernetes/pki/ca.crt "${USER}"@$host:

    scp /etc/kubernetes/pki/ca.key "${USER}"@$host:

    scp /etc/kubernetes/pki/sa.key "${USER}"@$host:

    scp /etc/kubernetes/pki/sa.pub "${USER}"@$host:

    scp /etc/kubernetes/pki/front-proxy-ca.crt "${USER}"@$host:

    scp /etc/kubernetes/pki/front-proxy-ca.key "${USER}"@$host:

    scp /etc/kubernetes/pki/etcd/ca.crt "${USER}"@$host:etcd-ca.crt

    scp /etc/kubernetes/pki/etcd/ca.key "${USER}"@$host:etcd-ca.key

    scp /etc/kubernetes/admin.conf "${USER}"@$host:

done


USER 의 이름은 각자의 상황에 맞게 수정해 주세요.





그리고 root 가 아닌 현재 사용자로 kubectl 을 사용하기 위해서, 아래처럼 admin.conf 파일을 home 에 복사해 줍니다.


$ mkdir -p $HOME/.kube

sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config

sudo chown $(id -u):$(id -g) $HOME/.kube/config






두번째 Master Node




정상적으로 환경변수도 지정해 주고, 인증서 파일과 admin.conf 파일도 첫번째 Master Node 에서 복사해 왔다면, 아래처럼 kubeadm-config.yaml 파일을 생성합니다.


$ cat <<EOF> ./kubeadm-config.yaml

apiVersion: kubeadm.k8s.io/v1alpha2

kind: MasterConfiguration

kubernetesVersion: ${KUBERNETES_VER}

apiServerCertSANs:

- "${LOAD_BALANCER_DNS}"

api:

    controlPlaneEndpoint: "${LOAD_BALANCER_DNS}:${LOAD_BALANCER_PORT}"

etcd:

  local:

    extraArgs:

      listen-client-urls: "https://127.0.0.1:2379,https://${CP2_IP}:2379"

      advertise-client-urls: "https://${CP2_IP}:2379"

      listen-peer-urls: "https://${CP2_IP}:2380"

      initial-advertise-peer-urls: "https://${CP2_IP}:2380"

      initial-cluster: "${CP1_HOSTNAME}=https://${CP1_IP}:2380,${CP2_HOSTNAME}=https://${CP2_IP}:2380"

      initial-cluster-state: existing

    serverCertSANs:

      - ${CP2_HOSTNAME}

      - ${CP2_IP}

    peerCertSANs:

      - ${CP2_HOSTNAME}

      - ${CP2_IP}

networking:

    # This CIDR is a calico default. Substitute or remove for your CNI provider.

    podSubnet: "10.244.0.0/16"

EOF



그리고 앞서 master node 1로부터 받은 인증서 파일들을 올바른 위치에 옮겨 넣습니다.


# USER=ubuntu # customizable

# mkdir -p /etc/kubernetes/pki/etcd

# mv /home/${USER}/ca.crt /etc/kubernetes/pki/

# mv /home/${USER}/ca.key /etc/kubernetes/pki/

# mv /home/${USER}/sa.pub /etc/kubernetes/pki/

# mv /home/${USER}/sa.key /etc/kubernetes/pki/

# mv /home/${USER}/front-proxy-ca.crt /etc/kubernetes/pki/

# mv /home/${USER}/front-proxy-ca.key /etc/kubernetes/pki/

# mv /home/${USER}/etcd-ca.crt /etc/kubernetes/pki/etcd/ca.crt

# mv /home/${USER}/etcd-ca.key /etc/kubernetes/pki/etcd/ca.key

# mv /home/${USER}/admin.conf /etc/kubernetes/admin.conf





파일이 만들어졌으면, kubeadm 을 통해 kubelet 을 시작합니다.


# kubeadm alpha phase certs all --config kubeadm-config.yaml

kubeadm alpha phase kubelet config write-to-disk --config kubeadm-config.yaml

# kubeadm alpha phase kubelet write-env-file --config kubeadm-config.yaml

# kubeadm alpha phase kubeconfig kubelet --config kubeadm-config.yaml

# systemctl start kubelet



환경변수를 하나 더 추가합니다.

# export KUBECONFIG=/etc/kubernetes/admin.conf 


이제 etcd 에 현재 master 를 등록합니다.


# kubectl exec -n kube-system etcd-${CP1_HOSTNAME} -- etcdctl --ca-file /etc/kubernetes/pki/etcd/ca.crt --cert-file /etc/kubernetes/pki/etcd/peer.crt --key-file /etc/kubernetes/pki/etcd/peer.key --endpoints=https://${CP1_IP}:2379 member add ${CP1_HOSTNAME} https://${CP2_IP}:2380

kubeadm alpha phase etcd local --config kubeadm-config.yaml



마지막으로 cluster 에 master 로 등록합니다.

# kubeadm alpha phase kubeconfig all --config kubeadm-config.yaml

# kubeadm alpha phase controlplane all --config kubeadm-config.yaml

# kubeadm alpha phase mark-master --config kubeadm-config.yaml




그리고 root 가 아닌 현재 사용자로 kubectl 을 사용하기 위해서, 아래처럼 admin.conf 파일을 home 에 복사해 줍니다.


$ mkdir -p $HOME/.kube

$ sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config

$ sudo chown $(id -u):$(id -g) $HOME/.kube/config






세번째 Master Node




마찬가지로 환경변수를 등록하고 인증서 파일과 admin.config 파일을 제대로 복사해 왔다면, 아래처럼 kubeadm 설정파일을 만들어 줍니다.


$ cat <<EOF> ./kubeadm-config.yaml

apiVersion: kubeadm.k8s.io/v1alpha2

kind: MasterConfiguration

kubernetesVersion: ${KUBERNETES_VER}

apiServerCertSANs:

- "${LOAD_BALANCER_DNS}"

api:

    controlPlaneEndpoint: "${LOAD_BALANCER_DNS}:${LOAD_BALANCER_PORT}"

etcd:

  local:

    extraArgs:

      listen-client-urls: "https://127.0.0.1:2379,https://${CP3_IP}:2379"

      advertise-client-urls: "https://${CP3_IP}:2379"

      listen-peer-urls: "https://${CP3_IP}:2380"

      initial-advertise-peer-urls: "https://${CP3_IP}:2380"

      initial-cluster: "${CP1_HOSTNAME}=https://${CP1_IP}:2380,${CP2_HOSTNAME}=https://${CP2_IP}:2380,${CP3_HOSTNAME}=https://${CP3_IP}:2380"

      initial-cluster-state: existing

    serverCertSANs:

      - ${CP3_HOSTNAME}

      - ${CP3_IP}

    peerCertSANs:

      - ${CP3_HOSTNAME}

      - ${CP3_IP}

networking:

    # This CIDR is a calico default. Substitute or remove for your CNI provider.

    podSubnet: "10.244.0.0/16"

EOF



그리고 앞서 master node 1로부터 받은 인증서 파일들을 올바른 위치에 옮겨 넣습니다.


# USER=ubuntu # customizable

# mkdir -p /etc/kubernetes/pki/etcd

# mv /home/${USER}/ca.crt /etc/kubernetes/pki/

# mv /home/${USER}/ca.key /etc/kubernetes/pki/

# mv /home/${USER}/sa.pub /etc/kubernetes/pki/

# mv /home/${USER}/sa.key /etc/kubernetes/pki/

# mv /home/${USER}/front-proxy-ca.crt /etc/kubernetes/pki/

# mv /home/${USER}/front-proxy-ca.key /etc/kubernetes/pki/

# mv /home/${USER}/etcd-ca.crt /etc/kubernetes/pki/etcd/ca.crt

# mv /home/${USER}/etcd-ca.key /etc/kubernetes/pki/etcd/ca.key

# mv /home/${USER}/admin.conf /etc/kubernetes/admin.conf



kubeadm 을 통해 kubelet 을 시작합니다.


# kubeadm alpha phase certs all --config kubeadm-config.yaml

# kubeadm alpha phase kubelet config write-to-disk --config kubeadm-config.yaml

kubeadm alpha phase kubelet write-env-file --config kubeadm-config.yaml

kubeadm alpha phase kubeconfig kubelet --config kubeadm-config.yaml

systemctl start kubelet



환경변수를 하나 더 추가합니다.

# export KUBECONFIG=/etc/kubernetes/admin.conf 



이제 etcd 에 현재 master 를 등록합니다.


# kubectl exec -n kube-system etcd-${CP1_HOSTNAME} -- etcdctl --ca-file /etc/kubernetes/pki/etcd/ca.crt --cert-file /etc/kubernetes/pki/etcd/peer.crt --key-file /etc/kubernetes/pki/etcd/peer.key --endpoints=https://${CP1_IP}:2379 member add ${CP3_HOSTNAME} https://${CP3_IP}:2380

# kubeadm alpha phase etcd local --config kubeadm-config.yaml



마지막으로 cluster 에 master 로 등록합니다.


# kubeadm alpha phase kubeconfig all --config kubeadm-config.yaml

# kubeadm alpha phase controlplane all --config kubeadm-config.yaml

# kubeadm alpha phase mark-master --config kubeadm-config.yaml



그리고 root 가 아닌 현재 사용자로 kubectl 을 사용하기 위해서, 아래처럼 admin.conf 파일을 home 에 복사해 줍니다.


$ mkdir -p $HOME/.kube

$ sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config

$ sudo chown $(id -u):$(id -g) $HOME/.kube/config





마지막 과정




앞선 과정들이 정상적으로 끝났다면, 아래처럼 보여야 합니다.

coredns 를 제외한 모든 pods 가 running 상태인지 확인합니다.


$ kubectl get pods -n kube-system

NAME                                              READY     STATUS    RESTARTS   AGE

coredns-78fcdf6894-rcq4s                      0/1       Pending   0          33m

coredns-78fcdf6894-w8sjp                      0/1       Pending   0          33m

etcd-test-kube-master001                      1/1       Running   0          32m

etcd-test-kube-master002                      1/1       Running   0          7m

etcd-test-kube-master003                      1/1       Running   0          1m

kube-apiserver-test-m001                      1/1       Running   0          32m

kube-apiserver-test-m002                      1/1       Running   0          7m

kube-apiserver-test-m003                      1/1       Running   0          1m

kube-controller-manager-test-kube-master001   1/1       Running   0          32m

kube-controller-manager-test-kube-master002   1/1       Running   0          7m

kube-controller-manager-test-kube-master003   1/1       Running   0          1m

kube-proxy-5nwzn                              1/1       Running   0          12m

kube-proxy-vhcgh                              1/1       Running   0          33m

kube-proxy-w54gw                              1/1       Running   0          3m

kube-scheduler-test-kube-master001            1/1       Running   0          32m

kube-scheduler-test-kube-master002            1/1       Running   0          7m

kube-scheduler-test-kube-master003            1/1       Running   0          1m


Running 이 아닌 ContainerCreating 이거나 다른 상태인 pods 가 있으면, Running 이 될때까지 기다립니다.



이제 마지막으로 처음 설정했던 Master Node 로 돌아와서, pod network 를 설치해 줍니다.

저의 경우 flannel 을 사용하였으므로 아래처럼 해 줍니다.


$ kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/v0.10.0/Documentation/kube-flannel.yml







설치 확인




다음처럼 cluster 정보와 node, 그리고 pods 들이 정상적으로 running 상태면 완료된 것입니다.



$ kubectl get nodes

NAME                      STATUS    ROLES     AGE       VERSION

test-kube-master001       Ready     master    36m       v1.11.3

test-kube-master002       Ready     master    28m       v1.11.3

test-kube-master003       Ready     master    14m       v1.11.3



$ kubectl cluster-info

Kubernetes master is running at https://10.113.228.217:6443

KubeDNS is running at https://10.113.228.217:6443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy



$ kubectl get pods -n kube-system

NAME                                              READY     STATUS             RESTARTS   AGE

coredns-78fcdf6894-bz4lq                          1/1       Running            0          39m

coredns-78fcdf6894-g6hpp                          1/1       Running            0          39m

etcd-test-kube-master001                          1/1       Running            0          38m

etcd-test-kube-master002                          1/1       Running            0          24m

etcd-test-kube-master003                          1/1       Running            0          4m

kube-apiserver-test-kube-master001                1/1       Running            0          38m

kube-apiserver-test-kube-master002                1/1       Running            0          23m

kube-apiserver-test-kube-master003                1/1       Running            0          4m

kube-controller-manager-test-kube-master001       1/1       Running            0          38m

kube-controller-manager-test-kube-master002       1/1       Running            0          23m

kube-controller-manager-test-kube-master003       1/1       Running            0          4m

kube-flannel-ds-6hhbt                             1/1       Running            0          13m

kube-flannel-ds-p5tzk                             1/1       Running            0          13m

kube-flannel-ds-wqs52                             1/1       Running            0          13m

kube-proxy-8dbjw                                  1/1       Running            0          30m

kube-proxy-fsw9z                                  1/1       Running            0          17m

kube-proxy-spsd6                                  1/1       Running            0          39m

kube-scheduler-test-kube-master001                1/1       Running            0          38m

kube-scheduler-test-kube-master002                1/1       Running            0          23m

kube-scheduler-test-kube-master003                1/1       Running            0          4m






Trouble Shooting



Pod Sync 에러로 인한 ETCD POD 생성 실패.


만약에 pod sync 실패로 정상적으로 etcd 실행이 실패한다면, 해당 Master Host 에서 다음처럼 합니다.


$ sudo kubeadm reset

$ sudo systemctl stop kubelet

$ sudo systemctl stop docker

$ sudo rm -rf /var/lib/cni/

$ sudo rm -rf /var/lib/kubelet/*

$ sudo rm -rf /etc/cni/

$ sudo /sbin/ifconfig cni0 down

$ sudo /sbin/ifconfig flannel.1 down

$ sudo /sbin/ifconfig docker0 down

$ sudo /sbin/ip link delete cni0

$ sudo /sbin/ip link delete flannel.1

$ sudo systemctl start docker


이후에 join 하는 과정을 처음부터 다시 합니다.






  • forkballpitch 2018.10.04 19:59 신고

    안녕하세요~ kubernetes 1.12.0 버전에서 HA 구성을 따라하다가 궁금한점이 생겨 댓글드립니다~
    kubeadm-config.yaml 에서LOAD_BALANCER_DNS IP 할당은 keepalived로 할당하신건가요~?
    저는 그렇게 했는데 init 할때 자꾸 에러가 나서요~ ㅜ 고견부탁드립니다. 감사합니다.

    • unD3R 2018.10.05 17:12 신고

      안녕하세요?
      어떤 에러인지는 모르겠으나 LOAD BALANCING 만 되면 상관 없는거라 다른 이유로 에러가 발생하는건 아닐까요?
      참고로 VIP 를 직접 넣는것 보다는 DNS 를 넣으시는게 낫습니다.

  • russa 2019.08.21 17:20

    안녕하세요. 설명해주신 글은 정말 잘 보았습니다.
    그런데 궁금한 점이 있는데요.
    써주신 글은 stacked etcd cluster 형태로해서 master 3대에 etcd가 모두 들어가 있는 형태로 보이는데요.
    각 master 의 etcd는 서로 동기화는 안되는건가요?

    이 다음글에 ETCD HA 글을 보게 되면 동기화가 따로 안되는거 같다는 생각이들어서요~!
    답변 부탁드리겠습니다. 감사합니다 :)