※ zone은 사용중이신걸로 변경
# Control Plane
firewall-cmd --permanent --zone=public --add-port=6443/tcp
firewall-cmd --permanent --zone=public --add-port=2379-2380/tcp
firewall-cmd --permanent --zone=public --add-port=10250/tcp
firewall-cmd --permanent --zone=public --add-port=10259/tcp
firewall-cmd --permanent --zone=public --add-port=10257/tcp
# Worker Node
firewall-cmd --permanent --zone=public --add-port=10250/tcp
firewall-cmd --permanent --zone=public --add-port=30000-32726/tcp
※ kube-apiserver (443 open)
# Control Plane
firewall-cmd --permanent --zone=public --add-port=179/tcp
firewall-cmd --permanent --zone=public --add-port=2379/tcp
firewall-cmd --permanent --zone=public --add-port=5473/tcp
firewall-cmd --permanent --zone=public --add-port=4789/udp
firewall-cmd --permanent --zone=public --add-port=51820-51821/udp
# Worker Node
firewall-cmd --permanent --zone=public --add-port=179/tcp
firewall-cmd --permanent --zone=public --add-port=5473/tcp
firewall-cmd --permanent --zone=public --add-port=4789/udp
firewall-cmd --permanent --zone=public --add-port=51820-51821/udp
# Worker Node
firewall-cmd --permanent --zone=public --add-source=192.168.0.0/16
firewall-cmd --permanent --zone=public --add-port=53/udp
firewall-cmd --permanent --zone=public --add-port=53/tcp
firewall-cmd --permanent --zone=public --add-port=9153/tcp
마스터 노드에서 hostnamectl을 사용하여 hostname을 설정
sudo hostnamectl set-hostname control01
sudo exec bash
나머지 워커 노드에서도 동일하게 작업
sudo hostnamectl set-hostname worker01 # 1st worker
sudo hostnamectl set-hostname worker02 # 2nd worker
exec bash
/etc/hosts에 각 노드들을 추가
10.0.163.1 kubemaster01 control01
10.0.163.2 kubeworker01 worker01
10.0.163.3 kubeworker02 worker02
kubelet이 정상 작동하기 위해서는 반드시 스왑을 사용하지 않도록 설정해줘야 한다.
# swap off
sudo swapoff -a
# /etc/fstab의 모든 라인에 주석 추가
sudo sed -i '/ swap / s/^\(.*\)$/#\1/g' /etc/fstab # /etc/fstab 파일을 열어서 /dev/mapper/cs-swap을 주석처리해도 무방하다
# SELinux permissive로 설정
setenforce 0
# 확인
sestatus
... 중략 ...
Current mode: permissive
Mode from config file: permissive
# config 파일에서 permissive로 변경
vi /etc/selinux/config
# SELINUX 값을 permissive로 변경
... 중략 ...
SELINUX=permissive
...
Containerd를 사용할 것임으로 containerd.conf에 overlay와 br_netfilter 모듈 정보를 등록합니다.
sudo tee /etc/modules-load.d/containerd.conf <<EOF
overlay
br_netfilter
EOF
sudo modprobe overlay # overlay
sudo modprobe br_netfilter # kubernetes pod 간의 VxLAN Protocol 통신과 masquerading을 위해
※ OverLay Network / Masquerading
kubernetes에서 bridge를 통과하는 패킷이 iptables로 전송될지에 대한 설정 (on == 1)
sudo tee /etc/sysctl.d/kubernetes.conf <<EOF
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_forward = 1
EOF
변경 사항 Reload
sudo sysctl --system
해당 포스트에서는 containerd를 사용합니다. containerd 설치는 아래 글을 참고해주세요.
kubernetes v1.22 이상에서는 cgroup driver를 default로 systemd를 사용하도록 코드가 변경되었기 때문에 systemd를 cgroup driver로 사용할 수 있도록 containerd 설정을 변경을 해줘야 합니다. /etc/containerd/config.toml 파일을 열어서 아래와 같이 SystemdCgroup을 true로 설정해줍니다.
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc]
...
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
SystemdCgroup = true
변경 후 containerd를 restart 해줍니다.
systemctl restart containerd
Control Plane과 Worker Node에 모두 설치한다.
# kubernetes.io document 발췌
cat <<EOF | sudo tee /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://packages.cloud.google.com/yum/repos/kubernetes-el7-\$basearch
enabled=1
gpgcheck=1
gpgkey=https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg
exclude=kubelet kubeadm kubectl
EOF
# permissive 모드로 SELinux 설정(효과적으로 비활성화)
sudo setenforce 0
sudo sed -i 's/^SELINUX=enforcing$/SELINUX=permissive/' /etc/selinux/config
sudo yum install -y kubelet kubeadm kubectl --disableexcludes=kubernetes
sudo systemctl enable --now kubelet
설치한 kubeadm을 이용하여 control-plane을 초기화한다, 초기화시 kube-apiserver와 control plane의 endpoint 그리고 calico 사용을 위한 pod network cidr 값을 설정해준다.
# control plane 초기화
kubeadm init --apiserver-advertise-address=10.0.163.1 --control-plane-endpoint=control01 --pod-network-cidr=192.168.0.0/16
# 정상 설치 완료 시 아래와 같이 config 설정과 관련된 메세지와
# control-plane 혹은 work로 편입되기 위한 명령어가 나온다
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
You should now deploy a Pod network to the cluster.
Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
/docs/concepts/cluster-administration/addons/
You can now join any number of machines by running the following on each node
as root:
kubeadm join <control-plane-host>:<control-plane-port> --token <token> --discovery-token-ca-cert-hash sha256:<hash>
※ 위와 같이 성공 메세지와 추가로 설정들에 대한 메세지가 나왔다면, 메세지 內 mkdir 부터 chown까지의 작업을 진행해, 특정 사용자 계정에서 kubectl을 사용할 수 있도록 설정해줍니다.
위 설정을 모두 진행했다면 아래 명령어를 실행해 node의 상태와 kube-system의 pod를 확인해봅니다.
# ===================================================================
# node 확인
# ===================================================================
kubectl get nodes
NAME STATUS ROLES AGE VERSION
control01 NotReady control-plane 14s v1.27.4
# ===================================================================
# kube-system pod 확인
# ===================================================================
kubectl get pod -n kube-system
NAME READY STATUS RESTARTS AGE
coredns-5d78c9869d-l7gwc 0/1 ContainerCreating 0 14s
etcd-control01 1/1 Running 0 13s
kube-apiserver-control01 1/1 Running 0 13s
kube-controller-manager-control01 1/1 Running 0 14s
kube-proxy-8rcfl 1/1 Running 0 14s
kube-scheduler-control01 1/1 Running 0 14s
※ 위와 같이 node의 상태가 NotReady로, kube-system 아래의 pod가 coredns를 제외하고 Running이면 정상입니다.
클러스터 DNS(coredns는 CNI 기반 네트워크가 설치되기 전에는 시작되지 않습니다. calico나 flannel등을 설치후 Running으로 상태가 변경됩니다.)
초기화시 나타난 메세지를 이용하여 k8s 클러스터에 worker node를 편입시킵니다.
# ===================================================================
# kubernetes cluster에 worker node 편입
# ===================================================================
kubeadm join control01:6443 --token xxxxxx.xxxxxxxxxxxx --discovery-token-ca-cert-hash sha256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# ===================================================================
# kubernetes node 확인
# ===================================================================
NAME STATUS ROLES AGE VERSION
worker01 NotReady <none> 11m v1.27.4
control01 NotReady control-plane 10m v1.27.4
cni를 설치하지 않고 클러스터를 구성할 시 위 처럼 워커 노드가 NotReady로 되어있는것을 볼 수 있는데, 에러 내용은 아래와 같이 확인이 가능하다.
# ===================================================================
# kubernetes node 확인
# ===================================================================
kubectl describe node worker01
... 중략 ...
Conditions:
Ready False ... 중략 ...
KubeletNotReady container runtime network not ready: NetworkReady=false reason:NetworkPluginNotReady message:Network plugin returns error: cni plugin not initialized
# ===================================================================
# 노드의 kubelet에서도 위와 동일한 에러를 확인할 수 있습니다.
# ===================================================================
journalctl -u kubelet --no-page -f
... 중략 ...
... err="network is not ready: container runtime network not ready: NetworkReady=false reason:NetworkPluginNotReady message:Network plugin returns error: cni plugin not initialized"
CNI가 설치되어있지 않아 kubelet에서 network plugin을 초기화 할 수 없다는 에러가 발생하고 있는것을 볼 수 있다.
Control Plane의 coreDns 그리고 worker의 Ready상태를 위해서 CNI를 설치한다. 해당 포스팅에서는 Calico를 사용합니다. 그외 다른 CNI를 사용하셔도 무방합니다. (다만 각각의 플러그인에 맞는 클러스터 초기화를 진행해주세요)
calico 설치는 아래 페이지의 메니페스트들을 적용해주면 됩니다. 설치 과정이 모두 해당 페이지에 있으므로 해당 글에 추가로 적어두지는 않겠습니다.
CNI를 설치했다면 각 Node들의 상태가 Ready가 되어야하고 kube-system에 있는 coredns도 Running 상태로 변경되어야 한다. 또한 calico-system에 있는 pod들 또한 정상적으로 Running 상태가 되어있어야 합니다.
# ===================================================================
# kubernetes node 확인
# ===================================================================
kubectl get nodes
NAME STATUS ROLES AGE VERSION
worker01 Ready <none> 12m v1.27.4
worker02 Ready <none> 9m25s v1.27.4
control01 Ready control-plane 13m v1.27.4
# ===================================================================
# kube-system namespace 확인
# ===================================================================
kubectl get pod -n kube-system
NAME READY STATUS RESTARTS AGE
coredns-5d78c9869d-cd5nr 1/1 Running 0 14m
coredns-5d78c9869d-lxbq7 1/1 Running 0 14m
etcd-psnname01 1/1 Running 0 15m
kube-apiserver-psnname01 1/1 Running 0 15m
kube-controller-manager-psnname01 1/1 Running 0 15m
kube-proxy-2nq7t 1/1 Running 0 14m
kube-proxy-cb5dn 1/1 Running 0 13m
kube-proxy-vn9sz 1/1 Running 0 10m
kube-scheduler-psnname01 1/1 Running 0 15m
# ===================================================================
# calico-system namespace 확인
# ===================================================================
kubectl get pod -n calico-system
NAME READY STATUS RESTARTS AGE
calico-kube-controllers-5cbf7d5df-r7ldh 1/1 Running 0 13m
calico-node-hhfbb 1/1 Running 0 13m
calico-node-j66vz 1/1 Running 0 11m
calico-node-v5b24 1/1 Running 0 13m
calico-typha-547447d4b8-48dt5 1/1 Running 0 11m
calico-typha-547447d4b8-bb9xp 1/1 Running 0 13m
csi-node-driver-bpx89 2/2 Running 0 11m
csi-node-driver-kzh7n 2/2 Running 0 13m
csi-node-driver-t7lkr 2/2 Running 0 13m
해당 포스트에서는 kube-proxy의 mode를 ipvs를 사용하도록 설정할 것이다.
kube-system의 kube-proxy configmap의 mode를 ipvs로 변경해주고, kube-proxy를 재생성해주도록 하자.
# ===================================================================
# kube-proxy mode 변경
# ===================================================================
kubectl edit cm -n kube-system kube-proxy
apiVersion: v1
data:
config.conf: |-
apiVersion: kubeproxy.config.k8s.io/v1alpha1
bindAddress: 0.0.0.0
bindAddressHardFail: false
... 중략 ...
tcpFinTimeout: 0s
tcpTimeout: 0s
udpTimeout: 0s
kind: KubeProxyConfiguration
metricsBindAddress: ""
mode: "ipvs"
# ===================================================================
# kube-proxy pod 삭제
# ===================================================================
kubectl get pod -n kube-system | grep kube-proxy | awk '{system("kubectl delete pod "$1" -n kube-system")}'
# ===================================================================
# 변경된 kube-proxy mode 확인
# ===================================================================
kubectl logs -n kube-system kube-proxy-8z6ck
... 중략 ...
I0821 13:27:42.896249 1 server_others.go:265] "Using ipvs Proxier"
# ===================================================================
# ipvs 확인
# ===================================================================
ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 10.96.0.1:443 rr
-> 10.0.20.2:6443 Masq 1 0 0
TCP 10.96.0.10:53 rr
-> 192.168.191.194:53 Masq 1 0 0
-> 192.168.191.195:53 Masq 1 0 0
TCP 10.96.0.10:9153 rr
-> 192.168.191.194:9153 Masq 1 0 0
-> 192.168.191.195:9153 Masq 1 0 0
TCP 10.100.48.129:443 rr
-> 192.168.191.197:5443 Masq 1 0 0
-> 192.168.241.130:5443 Masq 1 0 0
테스트용 pod로 nginx pod를 아래를 참고하여 하나 생성해봅니다.
아래 내용을 텍스트 파일로 생성 후 kubectl apply -f [파일 이름]으로 pod를 생성합니다.
apiVersion: v1
kind: Pod
metadata:
name: my-first-pod
spec:
containers:
- name: my-nginx-container
image: nginx:latest
ports:
- containerPort: 80
protocol: TCP
특정 namespace를 명시하지 않았음으로 kubectl get pod 명령어를 통해 생성된 pod를 확인합니다.
# ===================================================================
# 생성된 pod 확인
# ===================================================================
kubectl get pod
NAME READY STATUS RESTARTS AGE
my-first-pod 1/1 Running 0 4m8s