본문 바로가기

Devops/Kubernetes

[Kubernetes] 쿠버네티스 설치(kubeadm) 및 cluster 구성하기

쿠버네티스를 공부하기 위해서는 제일 처음으로 할일이 여러 노드에 kubernates패키지를 설치하고 master 노드와 worker노드간의 연결을 설정하는 일이다.

 

집에 있는 Synology 718+의 VMM 으로 VM을 3개 만들어 그들간에 cluster를 구성해보려고 한다.

 

시작하기 전에

  • 호환 되는 Linux는 Debian 및 Red Hat 기반 Linux 배포판.
  • 머신당 2GB 이상의 RAM.
  • 2 CPU 이상.
  • 클러스터의 모든 시스템 간의 전체 네트워크 연결(공용 또는 사설 네트워크).
  • 모든 노드에 대한 고유한 호스트 이름, MAC 주소 및 product_uuid.
  • kubelet 위해서는 스왑을 사용하지 않도록 설정함.

마스터 노드 (Master Node)

  • 노드들의 상태를 제어하고 관리
  • 쿠버네티스의 데이터 저장소로 사용하는 etcd를 함께 설치하거나 별도 노드에 분리 가능
  • etcd, kube-apiserver, kube-scheduler, kube-controller-manager, kubelet, kube-proxy, docker 등의 컴포넌트가 실행
  • 상용 서비스라면 고가용성을 고려해 3대 ~ 5대 구성 가능
  • 여러 대의 마스터 노드를 구성하더라도 kube-controller-manager가 활성화 상태로 동작할 수 있는 리더 마스터 노드는 1대만 가능

워커 노드 (Worker Node)

  • kubelet 이라는 프로세스를 통해서 마스터 노드의 명령을 받아 사용자가 선언한 파드 등을 실제로 실행하는 노드
  • kubelet, kube-proxy, docker 등의 컴포넌트가 실행

 

MAC 주소와 product_uuid가 모든 노드에 대해 고유한지 확인

  • ifconfig -a 로 각 노드에 할당된 Mac address 및 IP를 조회하여 충돌이 나지 않는지 확인한다.
root@kindlove-Standard-PC-i440FX-PIIX-1996:~# ifconfig -a
ens3: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 172.30.1.22  netmask 255.255.255.0  broadcast 172.30.1.255
        inet6 fe80::bc91:db54:8d6d:f646  prefixlen 64  scopeid 0x20<link>
        ether 02:11:32:24:f3:f3  txqueuelen 1000  (Ethernet)
        RX packets 973060  bytes 384142144 (384.1 MB)
        RX errors 0  dropped 8  overruns 0  frame 0
        TX packets 31297  bytes 2575688 (2.5 MB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 5653  bytes 513619 (513.6 KB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 5653  bytes 513619 (513.6 KB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

 

  • product_uuid는 다음 명령을 사용하여 확인한다. sudo cat /sys/class/dmi/id/product_uuid
root@kindlove-Standard-PC-i440FX-PIIX-1996:~# cat /sys/class/dmi/id/product_uuid
4d442e6b-82ba-430a-a322-feaab44fe01b

 

iptables가 브리지된 트래픽을 보도록 허용

lsmod | grep br_netfilter br_netfilter모듈이 로드 되었는지 확인한다.

명시적으로 로드하려면 sudo modprobe br_netfilter로 모듈을 로딩한다.

root@kindlove-Standard-PC-i440FX-PIIX-1996:~# sudo modprobe br_netfilter
root@kindlove-Standard-PC-i440FX-PIIX-1996:~# lsmod | grep br
br_netfilter           28672  0
bridge                176128  1 br_netfilter
stp                    16384  1 bridge
llc                    16384  2 bridge,stp

Linux 노드의 iptables가 브리지된 트래픽을 올바르게 보기 위한 요구 사항으로 구성 net.bridge.bridge-nf-call-iptables에서 가 1로 설정되어 있는지 확인한다. 콘솔창에서 다음을 실행한다.

cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
br_netfilter
EOF

cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
EOF
sudo sysctl --system
root@kindlove-Standard-PC-i440FX-PIIX-1996:~# sudo sysctl --system
* Applying /etc/sysctl.d/10-console-messages.conf ...
kernel.printk = 4 4 1 7
* Applying /etc/sysctl.d/10-ipv6-privacy.conf ...
net.ipv6.conf.all.use_tempaddr = 2
net.ipv6.conf.default.use_tempaddr = 2
* Applying /etc/sysctl.d/10-kernel-hardening.conf ...
kernel.kptr_restrict = 1
* Applying /etc/sysctl.d/10-link-restrictions.conf ...
fs.protected_hardlinks = 1
fs.protected_symlinks = 1
* Applying /etc/sysctl.d/10-magic-sysrq.conf ...
kernel.sysrq = 176
* Applying /etc/sysctl.d/10-network-security.conf ...
net.ipv4.conf.default.rp_filter = 1
net.ipv4.conf.all.rp_filter = 1
net.ipv4.tcp_syncookies = 1
* Applying /etc/sysctl.d/10-ptrace.conf ...
kernel.yama.ptrace_scope = 1
* Applying /etc/sysctl.d/10-zeropage.conf ...
vm.mmap_min_addr = 65536
* Applying /usr/lib/sysctl.d/50-default.conf ...
net.ipv4.conf.all.promote_secondaries = 1
net.core.default_qdisc = fq_codel
* Applying /etc/sysctl.d/99-sysctl.conf ...
* Applying /etc/sysctl.d/k8s.conf ...
* Applying /etc/sysctl.conf ...

 

Hostname 변경

master노드임을 알수 있게 hostname을 변경한다. (재로그인시 반영됨)

root@ubuntu-Standard-PC-i440FX-PIIX-1996:~# hostnamectl set-hostname master

 

SELinux, firewall 해제

root@kindlove-Standard-PC-i440FX-PIIX-1996:~# setenforce 0
setenforce: SELinux is disabled
root@kindlove-Standard-PC-i440FX-PIIX-1996:~# ufw disable
방화벽이 비활성 되었으며 시스템이 시작할 때 사용되지 않습니다
root@kindlove-Standard-PC-i440FX-PIIX-1996:~#

 

Swap 해제

root@worker1:~# # swapon && cat /etc/fstab
root@worker1:~# # swapoff -a && sed -i '/swap/s/^/#/' /etc/fstab

 

런타임 설치

Pod에서 컨테이너를 실행하기 위해 Kubernetes는 컨테이터 런타임이 필요하다. (Ubuntu에 Docker 설치하기를 참조 : https://kindloveit.tistory.com/18)

기본적으로 Kubernetes는 컨테이너 런타임 인터페이스 (CRI)를 사용하여 선택한 컨테이너 런타임과 인터페이스하기 때문이다.

런타임을 지정하지 않으면 kubeadm은 잘 알려진 Unix 도메인 소켓 목록을 검색하여 설치된 컨테이너 런타임을 자동으로 감지하려고 시도한다. 현재 자동으로 디텍트 가능한 컨테이너는 다음과 같다. 

 

RuntimePath to Unix domain socket
Docker /var/run/dockershim.sock
containerd /run/containerd/containerd.sock
CRI-O /var/run/crio/crio.sock
Docker와 containerd가 모두 감지되면 Docker가 우선한다. 다른 두 개 이상의 런타임이 감지되면 kubeadm은 오류와 함께 종료된다.
 

kubeadm, kubelet 및 kubectl 설치

모든 컴퓨터에 다음 패키지를 설치합니다.

  • kubeadm: 클러스터를 부트스트랩 함.
  • kubelet: 클러스터의 모든 머신에서 실행되고 포드 및 컨테이너 시작과 같은 작업을 수행하는 구성 요소.
  • kubectl: 클러스터와 통신하기 위한 명령줄 util

현재 실습용으로 사용하고 있는 linux 배포판은 ubuntu 이므로 Debian 기반 배포판 기준으로 아래 설치 방법을 알아본다.

  1. apt패키지 인덱스를 업데이트하고 Kubernetes apt저장소 를 사용하는 데 필요한 패키지를 설치한다.
sudo apt-get update
sudo apt-get install -y apt-transport-https ca-certificates curl

 

  1. Google Cloud 공개 서명 키를 다운로드합니다.
sudo curl -fsSLo /usr/share/keyrings/kubernetes-archive-keyring.gpg https://packages.cloud.google.com/apt/doc/apt-key.gpg

 

  1. Kubernetes apt리포지토리를 추가 합니다.
echo "deb [signed-by=/usr/share/keyrings/kubernetes-archive-keyring.gpg] https://apt.kubernetes.io/ kubernetes-xenial main" | sudo tee /etc/apt/sources.list.d/kubernetes.list

 

  1. apt패키지 인덱스를 업데이트 하고 kubelet, kubeadm 및 kubectl을 설치하고 해당 버전을 고정합니다.
sudo apt-get update
sudo apt-get install -y kubelet kubeadm kubectl
sudo apt-mark hold kubelet kubeadm kubectl

kubeadm이 수행할 작업을 지시할 때까지 충돌 루프에서 대기하므로 kubelet은 이제 몇 초마다 다시 시작됩니다.

 

kubelet cgroup 드라이버 구성 (Docker container 기반)

  • cgroupfs를 컨테이너 런타임과 kubelt에 의해서 제어할 수 있도록 구성한다.
sudo mkdir /etc/docker
cat <<EOF | sudo tee /etc/docker/daemon.json
{
  "exec-opts": ["native.cgroupdriver=systemd"],
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "100m"
  },
  "storage-driver": "overlay2"
}
EOF

 

  • Docker를 다시 시작하고 부팅 시 활성화:
sudo systemctl enable docker
sudo systemctl daemon-reload
sudo systemctl restart docker

 

kubeadm을 사용하여 kubernetes cluster 구성하기

[마스터 노드에서만 실행] kubeadm init 명령을 통해서 클러스터를 생성한다.

root@kindlove-Standard-PC-i440FX-PIIX-1996:~# kubeadm init
[init] Using Kubernetes version: v1.23.1
[preflight] Running pre-flight checks
[preflight] Pulling images required for setting up a Kubernetes cluster
[preflight] This might take a minute or two, depending on the speed of your internet connection
[preflight] You can also perform this action in beforehand using 'kubeadm config images pull'
[certs] Using certificateDir folder "/etc/kubernetes/pki"
[certs] Generating "ca" certificate and key
[certs] Generating "apiserver" certificate and key
[certs] apiserver serving cert is signed for DNS names [kubernetes kubernetes.default kubernetes.default.svc kubernetes.default.svc.cluster.local master] and IPs [10.96.0.1 172.30.1.16]
[certs] Generating "apiserver-kubelet-client" certificate and key
[certs] Generating "front-proxy-ca" certificate and key
[certs] Generating "front-proxy-client" certificate and key
[certs] Generating "etcd/ca" certificate and key
[certs] Generating "etcd/server" certificate and key
[certs] etcd/server serving cert is signed for DNS names [localhost master] and IPs [172.30.1.16 127.0.0.1 ::1]
[certs] Generating "etcd/peer" certificate and key
[certs] etcd/peer serving cert is signed for DNS names [localhost master] and IPs [172.30.1.16 127.0.0.1 ::1]
[certs] Generating "etcd/healthcheck-client" certificate and key
[certs] Generating "apiserver-etcd-client" certificate and key
[certs] Generating "sa" key and public key
[kubeconfig] Using kubeconfig folder "/etc/kubernetes"
[kubeconfig] Writing "admin.conf" kubeconfig file
[kubeconfig] Writing "kubelet.conf" kubeconfig file
[kubeconfig] Writing "controller-manager.conf" kubeconfig file
[kubeconfig] Writing "scheduler.conf" kubeconfig file
[kubelet-start] Writing kubelet environment file with flags to file "/var/lib/kubelet/kubeadm-flags.env"
[kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml"
[kubelet-start] Starting the kubelet
[control-plane] Using manifest folder "/etc/kubernetes/manifests"
[control-plane] Creating static Pod manifest for "kube-apiserver"
[control-plane] Creating static Pod manifest for "kube-controller-manager"
[control-plane] Creating static Pod manifest for "kube-scheduler"
[etcd] Creating static Pod manifest for local etcd in "/etc/kubernetes/manifests"
[wait-control-plane] Waiting for the kubelet to boot up the control plane as static Pods from directory "/etc/kubernetes/manifests". This can take up to 4m0s

[kubelet-check] Initial timeout of 40s passed.
[apiclient] All control plane components are healthy after 68.017142 seconds
[upload-config] Storing the configuration used in ConfigMap "kubeadm-config" in the "kube-system" Namespace
[kubelet] Creating a ConfigMap "kubelet-config-1.23" in namespace kube-system with the configuration for the kubelets in the cluster
NOTE: The "kubelet-config-1.23" naming of the kubelet ConfigMap is deprecated. Once the UnversionedKubeletConfigMap feature gate graduates to Beta the default name will become just "kubelet-config". Kubeadm upgrade will handle this transition transparently.
[upload-certs] Skipping phase. Please see --upload-certs
[mark-control-plane] Marking the node master as control-plane by adding the labels: [node-role.kubernetes.io/master(deprecated) node-role.kubernetes.io/control-plane node.kubernetes.io/exclude-from-external-load-balancers]
[mark-control-plane] Marking the node master as control-plane by adding the taints [node-role.kubernetes.io/master:NoSchedule]
[bootstrap-token] Using token: 5v5tqi.9fbmk1t5bq4rr89l
[bootstrap-token] Configuring bootstrap tokens, cluster-info ConfigMap, RBAC Roles
[bootstrap-token] configured RBAC rules to allow Node Bootstrap tokens to get nodes
[bootstrap-token] configured RBAC rules to allow Node Bootstrap tokens to post CSRs in order for nodes to get long term certificate credentials
[bootstrap-token] configured RBAC rules to allow the csrapprover controller automatically approve CSRs from a Node Bootstrap Token
[bootstrap-token] configured RBAC rules to allow certificate rotation for all node client certificates in the cluster
[bootstrap-token] Creating the "cluster-info" ConfigMap in the "kube-public" namespace
[kubelet-finalize] Updating "/etc/kubernetes/kubelet.conf" to point to a rotatable kubelet client certificate and key
[addons] Applied essential addon: CoreDNS
[addons] Applied essential addon: kube-proxy

Your Kubernetes control-plane has initialized successfully!

To start using your cluster, you need to run the following as a regular user:

  mkdir -p $HOME/.kube
  sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
  sudo chown $(id -u):$(id -g) $HOME/.kube/config

Alternatively, if you are the root user, you can run:

  export KUBECONFIG=/etc/kubernetes/admin.conf

You should now deploy a pod network to the cluster.
Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
  https://kubernetes.io/docs/concepts/cluster-administration/addons/

Then you can join any number of worker nodes by running the following on each as root:

kubeadm join 172.30.1.16:6443 --token 5v5tqi.9fbmk1t5bq4rr89l \
        --discovery-token-ca-cert-hash sha256:e5d020b5cd0ba5f0a45b9482f8b41fdc1704e9a8fbc88c961b43b986cfc57c38


위와 같이 정상적으로 init이 끝나면 Command 실행이 되게 환경 변수를 설정해준다.

root@kindlove-Standard-PC-i440FX-PIIX-1996:~# export KUBECONFIG=/etc/kubernetes/admin.conf

 

[마스터 노드에서만 실행] 쿠버네티스 클러스터에 조인하기 위한 명령어 구문을 저장 해둔다.

root@master:~# cat > token.sh
kubeadm join 172.30.1.16:6443 --token 5v5tqi.9fbmk1t5bq4rr89l \
        --discovery-token-ca-cert-hash sha256:e5d020b5cd0ba5f0a45b9482f8b41fdc1704e9a8fbc88c961b43b986cfc57c38
        
root@master:~# chmod +x token.sh (실행권한 부여)

 

[마스터 노드에서만 실행] Pod가 서로 통신 할 수 있도록 CNI(Container Network Interface) 기반 Pod 네트워크 추가 기능 구성한다.

 

root@kindlove-Standard-PC-i440FX-PIIX-1996:~# wget https://docs.projectcalico.org/manifests/calico.yaml

root@kindlove-Standard-PC-i440FX-PIIX-1996:~# kubectl apply -f calico.yaml

 

정상적으로 노드가 ready 되었는지 확인한다.

root@master:~# kubectl get nodes
NAME     STATUS   ROLES                  AGE     VERSION
master   Ready    control-plane,master   9m13s   v1.23.1

 

calico및 kubernetes pods가 정상 running되는지 확인한다.

root@master:~# kubectl get pods --all-namespaces
NAMESPACE     NAME                                       READY   STATUS    RESTARTS   AGE
kube-system   calico-kube-controllers-647d84984b-rbp5b   1/1     Running   0          4m45s
kube-system   calico-node-gft4v                          1/1     Running   0          4m47s
kube-system   coredns-64897985d-2ndcp                    1/1     Running   0          9m1s
kube-system   coredns-64897985d-bn7r8                    1/1     Running   0          9m2s
kube-system   etcd-master                                1/1     Running   0          9m20s
kube-system   kube-apiserver-master                      1/1     Running   0          9m28s
kube-system   kube-controller-manager-master             1/1     Running   0          9m20s
kube-system   kube-proxy-bvcx9                           1/1     Running   0          9m2s
kube-system   kube-scheduler-master                      1/1     Running   0          9m20s

 

[워커 노드에서만 실행] 각 Worker노드에서 master노드에 등록을 수행한다.

 

kubeadm init 후에 저장한 token.sh을 각 worker 노드로 전송한다.

root@master:~# scp token.sh ubuntu@172.30.1.10:~/
ubuntu@172.30.1.10's password:
token.sh

root@master:~# scp token.sh ubuntu@172.30.1.36:~/
ubuntu@172.30.1.36's password:
token.sh

 

token.sh을 전달받은 각 노드는 실행해서 cluster에 연결한다.

root@worker2:/home/ubuntu# ./token.sh
[preflight] Running pre-flight checks
[preflight] Reading configuration from the cluster...
[preflight] FYI: You can look at this config file with 'kubectl -n kube-system get cm kubeadm-config -o yaml'
W0106 13:23:42.637670   15397 utils.go:69] The recommended value for "resolvConf" in "KubeletConfiguration" is: /run/systemd/resolve/resolv.conf; the provided value is: /run/systemd/resolve/resolv.conf
[kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml"
[kubelet-start] Writing kubelet environment file with flags to file "/var/lib/kubelet/kubeadm-flags.env"
[kubelet-start] Starting the kubelet
[kubelet-start] Waiting for the kubelet to perform the TLS Bootstrap...

This node has joined the cluster:
* Certificate signing request was sent to apiserver and a response was received.
* The Kubelet was informed of the new secure connection details.

Run 'kubectl get nodes' on the control-plane to see this node join the cluster.

 

Master 노드에서 최종 cluster에 붙은 모든 노드들을 확인한다.

root@master:~# kubectl get nodes
NAME      STATUS   ROLES                  AGE     VERSION
master    Ready    control-plane,master   4h57m   v1.23.1
worker1   Ready    <none>                 4m7s    v1.23.1
worker2   Ready    <none>                 94s     v1.23.1

 

-- The End --