본문 바로가기

Technical/Cloud, Virtualization, Containers

[Kubernetes] CentOS 7.3 으로 Kubernetes Cluster 구성(with Flannel)-1/4


Kubernetes 를 CentOS 7 에서 설치하는 방법을 정리해 둔다. 3개의 VM을 준비하고, Mater 하나와 두 개의 Minion(Slave 또는 worker node, 그냥 노드라고도 한다) 구성의 Custer를 구현하되, 네트워크 백엔드 솔루션(Network Fabric)은 CoreOS 에서 기본 채택하고 있는 Flannel을 사용하여 서로 다른 호스트(Worker node, Minion)의 컨테이너간 터널링을 통한 연결이 가능하게 설정할 것이다(총 4개의 시리즈로, 마지막 포스팅에서는 Kubeadm 을 통해, BGP 라우팅이 가능한 Calico Network를 적용한 Kubernetes 클러스터를 구현해볼 예정이다)


Docker Engine과 함께 Kubernetes 를 설치하는 방법은, 모든 필수 패키지를 수작업으로 개별 설치하거나, Playbook 레시피를 적용하여 Ansible을 통해 자동화된 설치를 진행할 수도 있고 또는 Kubeadm 을 통해 간편하게 설치 및 설정을 할 수도 있다. 여기서는 첫 번 째에 언급한 수작업 설치(v1.5.2 기준, v1.6.x 이전 버전)을 진행해 보고자 한다.




준비 사항

VirtualBox VM 3개를 준비(예를 들어 본 Blog의 다른 포스트 - http://bryan.wiki/282 - 의 방법을 통해 CentOS 7.3 VM 3개를 찍어내 두도록 한다, 또는 VM 3개를 각각 만들고 일일이 ConeOS 7.3을 설치하는 수고로움을 즐겨도 좋다).


* Master: CPU 1, Memory 2GB, IP 10.255.10.180

* Minion 1: CPU 1, Memory 1.2GB, IP 10.255.10.181

* Minion 2: CPU 1, Memory 1.2GB, IP 10.255.10.182



[주의] 클러스터 구성에서 각 서버간의 시간동기화가 중요한 이슈가 될 수 있으므로, 기본적으로 ntp 또는 chrony 를 설정하고 Master 노드 또는 해당 환경 내의 전용 Time Server를 기준으로 설정해야 한다. 본 내용에서는 시간동기화 설정에 따로 다루지 않을 것이므로, 이전 포스팅 중 http://bryan.wiki/273 의 내용 중 일부를 찾아서 참고하기를 권한다.



클러스터 구성


아래의 그림과 유사한 아키텍처의 물리적 구성을 해도 좋고, VirtualBox를 통한 가상화 구성을 해도 좋다. 기본적으로 1개의 물리적 네트워크(인터넷 등 외부와 연결되는 External network)와 2개의 가상 네트워크를 사용하여 Kubernetes(=k8s) 클러스터를 구성하게 된다.





* External Private Network: 10.255.10.0/24 - Master/Minion 서버에서 사용하는 기본 네트워크

* Backend Network(Fabric): 172.31.0.0/16 - Container(Pod)가 사용하는 가상 네트워크(Flannel 을 통한 터널링)

* Service Cluster Network: 10.100.0.0/16 - Cluster 내부 DNS에 의해 IP를 자동 할당/관리하게되는 서비스용 가상 네트워크

* K8s에 의해 최초 생성되는 서비스는 'kubernetes' 이며, 항상 Service Cluster Network의 첫번째 IP(10.100.0.1)가 할당됨

* Cluster 내부 DNS(kube-dns 서비스)에는 Service Cluster Network의 IP 중 임의의 하나를 결정하고 할당(10.100.0.100)



각 노드에서 yum repository 설정, Docker/K8s/etcd/flannel 을 설치



# vi /etc/yum.repos.d/virt7-docker-common-release.repo

[virt7-docker-common-release]

name=virt7-docker-common-release

baseurl=http://cbs.centos.org/repos/virt7-docker-common-release/x86_64/os/

gpgcheck=0


# yum -y install --enablerepo=virt7-docker-common-release kubernetes etcd flannel wget git

* Kubernetes 1.5.2 버전(2016/12월 releae)이 설치된다. 아래에 소개 되는 설치/운영 방식은 이후 버전에서는 더 이상 적용되지 않는다.

* [참고] 1.6.0(2017/3월 release) 이후 버전부터는 kubeadm 을 통한 Cluster 설치/설정 운영 방식으로 동작하며, Minion 노드의 kube-proxy는 각 노드에 고루 존재하는 DaemonSet 방식의 Pod로 편재되는 아키텍처로 바뀌었으며 TLS 를 통한 암호화 통신을 기본으로 하는 등 높은 완성도와 안정화를 이루었다.



각 노드에서 Kunernetes 기본 설정


# vi /etc/kubernetes/config

# Comma separated list of nodes running etcd cluster

KUBE_ETCD_SERVERS="--etcd-servers=http://10.255.10.180:2379"


# Logging will be stored in system journal

KUBE_LOGTOSTDERR="--logtostderr=true"


# Journal message level, 0 is debug

KUBE_LOG_LEVEL="--v=0"


# Should this cluster be allowed to run privileged docker containers

KUBE_ALLOW_PRIV="--allow-privileged=false"


# Api-server endpoint used in scheduler and controller-manager

KUBE_MASTER="--master=http://10.255.10.180:8080"



Master 노드에서 etcd, API Server, Controller Manager 설정


[root@kubemaster ~]# vi /etc/etcd/etcd.conf

#[member]

ETCD_NAME=default

ETCD_DATA_DIR="/var/lib/etcd/default.etcd"

ETCD_LISTEN_CLIENT_URLS="http://0.0.0.0:2379"

#[cluster]

ETCD_ADVERTISE_CLIENT_URLS="http://0.0.0.0:2379"


API Server 를 설정하기 위해서는 API 요청시 인증을 처리하기 위한 작업을 진행해야 한다. 웹 브라우저에서 github.com 의 공식 repository를 통해 make-ca-cert.sh 를 열어서 전체 내용을 복사, 작업 디렉토리(~/cacert)에 make-ca-cert.sh 로 저장 후, 30번쨰 라인을 다음과 같이 수정한 후, 지정된 Master와 k8s service 도메인에 대해 인증서를 생성한다.


[root@kubemaster ~]# vi make-ca-cert.sh

...

# /etc/group 에 kube 그룹이 존재함을 확인해 볼 것

cert_group=${CERT_GROUP:-kube}

...

[root@kubemaster ~]# chmod a+x make-ca-cert.sh


[root@kubemaster ~]# bash make-ca-cert.sh "10.255.10.180" "IP:10.255.10.180,IP:10.100.0.1,DNS:kubernetes,DNS:kubernetes.default,DNS:kubernetes.default.svc,DNS:kubernetes.default.svc.cluster.local"


이제 API Server 를 설정한다.


[root@kubemaster ~]# vi /etc/kubernetes/apiserver

# Bind kube API server to this IP

KUBE_API_ADDRESS="--address=0.0.0.0"


# Port that kube api server listens to.

KUBE_API_PORT="--port=8080"


# Port kubelet listen on

#KUBELET_PORT="--kubelet-port=10250"


# Comma separated list of nodes in the etcd cluster

KUBE_ETCD_SERVERS="--etcd-servers=http://10.255.10.180:2379"


# Address range to use for services(Work unit of Kubernetes)

KUBE_SERVICE_ADDRESSES="--service-cluster-ip-range=10.100.0.0/16"


# default admission control policies

KUBE_ADMISSION_CONTROL="--admission-control=NamespaceLifecycle,NamespaceExists,LimitRanger,SecurityContextDeny,ServiceAccount,ResourceQuota"


# Add your own!

KUBE_API_ARGS="--client-ca-file=/srv/kubernetes/ca.crt --tls-cert-file=/srv/kubernetes/server.cert --tls-private-key-file=/srv/kubernetes/server.key"


Controller Manager 를 설정한다.


[root@kubemaster ~]# vi /etc/kubernetes/controller-manager

# Comma separated list of minions

KUBELET_ADDRESSES="--machines=10.255.10.181, 10.255.10.182"


# Add your own!

KUBE_CONTROLLER_MANAGER_ARGS="--root-ca-file=/srv/kubernetes/ca.crt --service-account-private-key-file=/srv/kubernetes/server.key"



각 Minion 노드에서 kubelet을 설정


[root@kubenode1 ~]# vi /etc/kubernetes/kubelet 

###

# kubernetes kubelet (minion) config


# The address for the info server to serve on (set to 0.0.0.0 or "" for all interfaces)

KUBELET_ADDRESS="--address=0.0.0.0"


# The port for the info server to serve on

#KUBELET_PORT="--port=10250"


# You may leave this blank to use the actual hostname

KUBELET_HOSTNAME="--hostname-override=10.255.10.181"


# location of the api-server

KUBELET_API_SERVER="--api-servers=http://10.255.10.180:8080"


# pod infrastructure container

#KUBELET_POD_INFRA_CONTAINER="--pod-infra-container-image=registry.access.redhat.com/rhel7/pod-infrastructure:latest"


# Add your own!

KUBELET_ARGS="--cluster-dns=10.100.0.100 --cluster-domain=cluster.local"


[root@kubenode2 ~]# vi /etc/kubernetes/kubelet 

###

# kubernetes kubelet (minion) config


# The address for the info server to serve on (set to 0.0.0.0 or "" for all interfaces)

KUBELET_ADDRESS="--address=0.0.0.0"


# The port for the info server to serve on

#KUBELET_PORT="--port=10250"


# You may leave this blank to use the actual hostname

KUBELET_HOSTNAME="--hostname-override=10.255.10.182"


# location of the api-server

KUBELET_API_SERVER="--api-servers=http://10.255.10.180:8080"


# pod infrastructure container

#KUBELET_POD_INFRA_CONTAINER="--pod-infra-container-image=registry.access.redhat.com/rhel7/pod-infrastructure:latest"


# Add your own!

KUBELET_ARGS="--cluster-dns=10.100.0.100 --cluster-domain=cluster.local"



컨테이너(Pod) 간 백엔드 네트워크를 위한 Flannel 설정


Master 노드에서 etcd 를 시작한다


[root@kubemaster ~]# systemctl enable etcd

[root@kubemaster ~]# systemctl start etcd



etcd 내에 flannel 을 위한 네트워크 정보 저장 공간을 위한 Key를 생성하고, 네트워크 설정 값을 등록한다. 다음과 같이 등록하면, /16 네트워크에 대한 /24 크기의 Flannel 서브넷이 각 Minion에 자동 할당된다


[root@kubemaster ~]# etcdctl mkdir /kube-centos/network

[root@kubemaster ~]# etcdctl mk /kube-centos/network/config "{ \"Network\": \"172.31.0.0/16\", \"SubnetLen\": 24, \"Backend\": { \"Type\": \"vxlan\" } }"



이제 모든 노드에서 다음과 같이 Flannel 에 대한 설정을 진행한다


# vi /etc/sysconfig/flanneld

# etcd URL location.  Point this to the server where etcd runs

FLANNEL_ETCD_ENDPOINTS="http://10.255.10.180:2379"


# etcd config key.  This is the configuration key that flannel queries

# For address range assignment

FLANNEL_ETCD_PREFIX="/kube-centos/network"


# Any additional options that you want to pass

#FLANNEL_OPTIONS=""



Master와 Minion 에서 firewall을 설정하고 각각 서비스를 enable한 후, 기동

(최초 설치를 시작할 때, 노드에 firewalld 패키지를 설치하지 않았다면 아래 방화벽 설정 부분은 무시해도 되며, 여기서는 Master/etcd node 에 대해서만 방화벽을 설정해 보도록 한다. Port 허용 정보는 -> 참고)


[root@kubemaster ~]# systemctl enable firewalld

[root@kubemaster ~]# systemctl start firewalld

[root@kubemaster ~]# firewall-cmd --permanent --zone=public --add-port=443/tcp

[root@kubemaster ~]# firewall-cmd --permanent --zone=public --add-port=6443/tcp

[root@kubemaster ~]# firewall-cmd --permanent --zone=public --add-port=8080/tcp

[root@kubemaster ~]# firewall-cmd --permanent --zone=public --add-port=8285/udp

[root@kubemaster ~]# firewall-cmd --permanent --zone=public --add-port=8472/udp

[root@kubemaster ~]# firewall-cmd --permanent --zone=public --add-port=2379-2380/tcp

[root@kubemaster ~]# firewall-cmd --reload


[root@kubemaster ~]# systemctl enable flanneld

[root@kubemaster ~]# systemctl start flanneld

[root@kubemaster ~]# systemctl enable kube-controller-manager

[root@kubemaster ~]# systemctl start kube-controller-manager

[root@kubemaster ~]# systemctl enable kube-scheduler

[root@kubemaster ~]# systemctl start kube-scheduler

[root@kubemaster ~]# systemctl enable kube-apiserver

[root@kubemaster ~]# systemctl start kube-apiserver


[root@kubenode1 ~]# systemctl disable firewalld

[root@kubenode1 ~]# systemctl stop firewalld


[root@kubenode1 ~]# systemctl enable flanneld

[root@kubenode1 ~]# systemctl start flanneld

[root@kubenode1 ~]# systemctl enable docker

[root@kubenode1 ~]# systemctl start docker

[root@kubenode1 ~]# systemctl enable kubelet

[root@kubenode1 ~]# systemctl start kubelet

[root@kubenode1 ~]# systemctl enable kube-proxy

[root@kubenode1 ~]# systemctl start kube-proxy



각 노드에서 /24 크기의 Flannel 서브넷의 첫 번째 IP가 docker0에 할당되어 있는지 확인해 본다


[root@kubenode02 ~]# ifconfig

docker0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1450

        inet 172.31.83.1  netmask 255.255.255.0  broadcast 0.0.0.0

        inet6 fe80::42:80ff:fe4a:e1d6  prefixlen 64  scopeid 0x20<link>

        ether 02:42:80:4a:e1:d6  txqueuelen 0  (Ethernet)

        RX packets 236  bytes 20760 (20.2 KiB)

        RX errors 0  dropped 0  overruns 0  frame 0

        TX packets 115  bytes 8656 (8.4 KiB)

        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

...

flannel.1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1450

        inet 172.31.83.0  netmask 255.255.255.255  broadcast 0.0.0.0

        ether d2:bd:7a:e9:cd:e4  txqueuelen 0  (Ethernet)

        RX packets 108  bytes 7310 (7.1 KiB)

        RX errors 0  dropped 0  overruns 0  frame 0

        TX packets 80  bytes 10160 (9.9 KiB)

        TX errors 0  dropped 1 overruns 0  carrier 0  collisions 0



현재까지의 설정이 끝나면, Master 노드에서는 kube-apiserver, etcd, kube-controller-manager, kube-scheduler, flanneld 가 정상 가동 되고 있어야 하며,

Minion 노드에서는 kube-proxy, kubelet, flanneld, docker 가 정상 가동 되고 있어야 한다(systemctl status ~ 로 확인 또는 마지막 부분의 kube-check~ 스크립트를 작성하고 실행).



클러스터의 현재 상태를 확인해 보자


[root@kubemaster ~]# kubectl get nodes

NAME            STATUS    AGE

10.255.10.181   Ready     3m

10.255.10.182   Ready     3m

[root@kubemaster ~]# kubectl cluster-info

Kubernetes master is running at http://localhost:8080


To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.



k8s Addon 의 설치와 설정


kube-dns 서비스(Cluster 내부 DNS) 의 설정으로 서비스 discovery를 가능하게 해 보자. Master 노드에서 다음과 같이 kube-dns ReplicationController와 Service를 기동한다


# mkdir k8s-addon && cd k8s-addon

# git clone https://github.com/DragOnMe/kubernetes-mod-skydns-dashboard-mon.git

# cd kubernetes-mod-skydns-dashboard-mon

# kubectl create -f DNS/skydns-rc.yaml

# vi DNS/skydns-svc.yaml

...

   clusterIP: 10.100.0.100

...

# kubectl create -f DNS/skydns-svc.yaml


각 Minion 노드의 kubelet 서비스를 재시작해 준다


# systemctl restart kubelet


kube-system 네임스페이스에 kube-dns 서비스가 작동중인지 확인해 보자


[root@kubemaster ~]# kubectl get svc --all-namespaces

NAMESPACE     NAME         CLUSTER-IP     EXTERNAL-IP   PORT(S)         AGE

default       kubernetes   10.100.0.1     <none>        443/TCP         44m

kube-system   kube-dns     10.100.0.100   <none>        53/UDP,53/TCP   46s



필수적인 것은 아니지만, k8s 클러스터의 상태 모니터링 등을 위해 도움이 되는 dashboard Addon을 설치해 보자


[root@kubemaster kubernetes-mod-skydns-dashboard-mon]# kubectl create -f Dashboard/dashboard-controller.yaml

deployment "kubernetes-dashboard" created

[root@kubemaster kubernetes-mod-skydns-dashboard-mon]# kubectl create -f Dashboard/dashboard-service.yaml

service "kubernetes-dashboard" created


dashboard 서비스의 접속은 http://10.255.10.180:8080/ui 로 접속해 보면 된다.




Heapster와 Grafana, InfluxDB를 이용한 dashboard 상의 monitoring Addon(Arun Dhyani 님 제공) 을 설치해 보자


[root@kubemaster kubernetes-mod-skydns-dashboard-mon]# kubectl create -f cluster-monitoring/influxdb

service "monitoring-grafana" created

deployment "heapster-v1.2.0" created

service "heapster" created

replicationcontroller "monitoring-influxdb-grafana-v4" created

service "monitoring-influxdb" created


Google의 gcr.io 로부터 heapster 구동에 필요한 컨테이너의 Pull & Deploy 가 진행중이다


Heapster와 influxdb, grafana 의 설치가 완료되면 대쉬보드에서 그래프와 차트가 나타남을 확인할 수 있다



부팅/종료 및 데몬 서비스 기동 순서 & Sample Script


클러스터 운영/관리 단계에서 Master 노드와 Worker 노드를 종료, 기동하는 순서와, 각 데몬 서비스를 종료하고 시작하는 순서가 중요하다. 반드시 다음의 순서를 지켜 주어야 한다


* Kubernetes 클러스터 부팅시: Master 를 먼저 시작하고 데몬 서비스 로딩이 완료되면 Worker 노드를 차례로 시작하고 데몬 서비스 로딩 확인

  Kubernetes 클러스터 종료시: Worker 노드를 먼저 종료하고 마지막에 Master 노드를 종료


* 노드 내의 데몬 서비스 기동 순서(종료는 기동 순서의 역순)

  - Master 노드: etcd > flanneld > kube-controller-manager > kube-scheduler > kube-apiserver

  - Worker 노드: flanneld > docker > kubelet > kube-proxy



* Master 노드 관리 Script

[root@kubemaster ~]# cat kube-check-master.sh 

#!/bin/bash
for SERVICES in etcd flanneld kube-apiserver kube-controller-manager kube-scheduler;
    do echo --- $SERVICES --- ;
    systemctl is-active $SERVICES ;
    systemctl is-enabled $SERVICES ;
    echo "";  
done


[root@kubemaster ~]# cat kube-start-master.sh 

#!/bin/bash
for SERVICES in etcd flanneld kube-controller-manager kube-scheduler kube-apiserver ;
    do echo --- Starting $SERVICES --- ;
    systemctl start $SERVICES ;
    echo "Done";  
    echo "";  
done


[root@kubemaster ~]# cat kube-stop-master.sh 

#!/bin/bash
for SERVICES in kube-apiserver kube-scheduler kube-controller-manager flanneld etcd ;
    do echo --- Stopping $SERVICES --- ;
    systemctl stop $SERVICES ;
    echo "Done";  
    echo "";  
done


* Worker 노드 관리 Script

[root@kubenode01 ~]# cat kube-check-node.sh 

#!/bin/bash
for SERVICES in docker flanneld kubelet kube-proxy;
    do echo --- $SERVICES --- ;
    systemctl is-active $SERVICES ;
    systemctl is-enabled $SERVICES ;
    echo "";  
done


[root@kubenode01 ~]# cat kube-start-node.sh 

#!/bin/bash
for SERVICES in flanneld docker kubelet kube-proxy ;
    do echo --- Starting $SERVICES --- ;
    systemctl start $SERVICES ;
    echo "Done";  
    echo "";  
done


[root@kubenode01 ~]# cat kube-stop-node.sh 

#!/bin/bash
for SERVICES in kube-proxy kubelet docker flanneld;
    do echo --- Stopping $SERVICES --- ;
    systemctl stop $SERVICES ;
    echo "Done";  
    echo "";  
done



- Barracuda -


[관련 글 목록]

[Technical/Cloud, 가상화, PaaS] - [Kubernetes] CentOS 7.3 으로 Kubernetes Cluster 구성(with Flannel)-1/4

[Technical/Cloud, 가상화, PaaS] - [Kubernetes] CentOS 7.3 으로 Kubernetes Cluster 구성(노드 추가하기)-2/4

[Technical/Cloud, 가상화, PaaS] - [Kubernetes] 1.7.3/1.7.4, kubeadm 으로 L3 네트워크 기반 Cluster 구성(with Calico CNI)-3/4

[Technical/Cloud, 가상화, PaaS] - [Kubernetes] Hyper-converged GlusterFs integration with Heketi -4/4

[Technical/Cloud, 가상화, PaaS] - [GlusterFS & Kubernetes] External Gluster PV with Heketi CLI/Rest API