<Table of Contents>
1. 컨테이너 배포 시대의 도래 |
Ⅰ. Overview
이 문서는 RHEL8 환경에서의 Docker 및 Kubernetes 설치에 관한 간략한 연동 가이드를 제공합니다.
Product | Version | IP |
OS | Red Hat Enterprise Linux 8 | 192.168.56.1xx (모든 노드) |
Kubernetes | kubeadm 1.26.1 | 192.168.56.1xx (모든 노드) |
kubectl 1.26.1 | ||
kubelet 1.26.1 | ||
Docker | docker 23.0.1 | 192.168.56.1xx (모든 노드) |
containerd 1.6.16 |
간략하게 K8s 클러스터 구성 정보를 설명하자면 총 4 대의 장비로 구성됩니다.
192.168.56.120 - 마스터노드 (master)
192.168.56.101 - 워커노드 (worker1)
192.168.56.102 - 워커노드 (worker2)
192.168.56.103 - 워커노드 (worker3)
Ⅱ. 기본환경 구성 (모든 노드 - 192.168.56.1xx)
완전 물리 서버에서 어플리케이션을 실행할 수 밖에 없었던 전통적인 배포 방식에서
리소스를 효율적으로 배분할 수 있게 해준 가상화 배포시대를 거쳐
컨테이너 배포 환경을 통해 기존 인프라와의 종속성을 끊어버릴 수 있게 되었습니다.
따라서 클라우드 혹은 온프레미스 환경 모두에 이식이 가능합니다.
# swap 비활성화 cmd> swapoff -a && sed -i '/swap/s/^/#/' /etc/fstab # 방화벽 비활성화 cmd> systemctl stop firewalld cmd> systemctl disable firewalld # SELINUX 정책 수정 cmd> setenforce 0 cmd> sed -i 's/^SELINUX=enforcing$/SELINUX=permissive/' /etc/selinux/config cmd> cat <<EOF > /etc/modules-load.d/k8s.conf br_netfilter EOF cmd> cat <<EOF > /etc/sysctl.d/k8s.conf net.bridge.bridge-nf-call-ip6tables = 1 net.bridge.bridge-nf-call-iptables = 1 EOF cmd> sysctl --system cmd> vi /etc/hosts 127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6 192.168.56.120 master master.test.org 192.168.56.101 node1 node1.test.org 192.168.56.102 node2 node2.test.org 192.168.56.103 node3 node3.test.org 192.168.56.104 node4 node4.test.org 192.168.56.105 node5 node5.test.org |
Ⅲ. Docker 설치 (모든 노드 - 192.168.56.1xx)
컨테이너는 리눅스의 몇몇 기술로부터 시작이 되었습니다.
프로세스마다 컴퓨팅 자원을 제어 가능하게 해주는 cgroups
프로세스에 대한 파일시스템 격리를 도와주는 chroot
프로세스에 대한 환경 자체를 격리시키는 namespace
이후 cgroups와 namespace를 결합한 LXC(Linux Container)라는 지금의 컨테이너의 시초라고 볼 수 있는 기술이 등장합니다.
이후 이는 runc로 대체가 되었고, 아래와 같은 구조를 갖게 되었습니다.
cmd> yum install -y yum-utils device-mapper-persistent-data lvm2
cmd> yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo cmd> yum install -y docker-ce docker-ce-cli containerd.io 패키지 충돌이 난다면, '--allowerasing' 옵션 추가 cmd> mkdir /etc/docker cmd> cat > /etc/docker/daemon.json <<EOF { "exec-opts": ["native-cgroupdriver=systemd"], "log-driver": "json-file", "log-opts": { "max-size": "100m" }, "storage-driver": "overlay2", "storage-opts": [ "overlay2.override_kernel_check=true" ] } EOF cmd> mkdir -p /etc/systemd/system/docker.service.d cmd> systemctl enable --now docker cmd> systemctl status docker |
cmd> docker -v
Docker version 23.0.1, build a5ee5b1 cmd> containerd -v containerd containerd.io 1.6.16 31aa4358a36870b21a992d3ad2bef29e1d693bec |
Ⅳ. Kubernetes 설치 (192.168.56.120 - 마스터 노드)
어플리케이션의 아키텍쳐가 Monolithic 보다는 MSA의 흐름으로 가면서
각각의 컨테이너에 대한 세밀한 관리도 중요하지만,
시스템이 커지면 커질수록 통합 관리(Container Orchestration)가 더욱 필요해졌습니다.
■ 별개의 컨테이너들을 통합 관리
■ 네트워크/스토리지/컴퓨트 자원 관리
■ 빠르고 유연한 컨테이너 증감
■ 동적 컨테이너 연결 및 부하분산
이외에도 위와 같은 장점으로 인해 Kubernetes가 더욱 각광을 받게 되었습니다.
cmd> cat <<EOF | tee /etc/yum.repos.d/kubernetes.repo
[kubernetes] name=Kubernetes baseurl=https://packages.cloud.google.com/yum/repos/kubernetes-el7-x86_64 enabled=1 gpgcheck=1 repo_gpgcheck=1 gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg exclude=kubelet kubeadm kubectl EOF cmd> yum install -y --disableexcludes=kubernetes kubeadm kubelet kubectl # kubelet 은 마스터 노드의 api 서버와 worker 노드 간의 통신을 위한 서비스 cmd> systemctl status kubelet ... Active: inactive (dead) ... cmd> systemctl start kubelet # 아직 정상적으로 k8s 클러스터가 구성되지 않았기 때문에, 기동이 안되는게 정상입니다. cmd> systemctl status kubelet ... Active: failed ... |
# 기존에 구성된 containerd 설정파일의 존재 때문에 런타임 에러가 발생할 수 있기 때문에 아래와 같이 지워주고, 데몬 재실행
cmd> rm -rf /etc/containerd/config.toml cmd> systemctl restart containerd # kubeadm라는 api로 클러스터를 구성해주기 위한 커맨드 cmd> kubeadm init --pod-network-cidr=192.168.56.0/24 --apiserver-advertise-address=192.168.56.120 ... 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 192.168.56.120:6443 --token xxxxxx.xxxxxxxxxxxxxxxx \ --discovery-token-ca-cert-hash sha256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx # k8s 클러스터에 worker 노드를 추가하기 위한 커맨드를 파일 형태로 미리 저장해두기 cmd> cat << EOF > auth_token.txt kubeadm join 192.168.56.120:6443 --token xxxxxx.xxxxxxxxxxxxxxxx \ --discovery-token-ca-cert-hash sha256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx EOF # kubeadm init 과정에서 kubelet 이 자동적으로 실행이 되기 때문에, 따로 데몬을 start 시켜줄 필요 없음. cmd> systemctl status kubelet ... Active: active (running) ... |
※ 참고할 만한 에러 케이스
- 마스터노드 VM의 CPU 코어가 1개라면 아래와 같은 에러 발생 (ERROR NumCPU)
- kubeadm init 하고 다시 해야할 필요가 있는 경우,
kubeadm reset으로 설정 깔끔하게 비워주고 다시 kubeadm init 해주기
- containerd 설정파일의 존재 때문에 런타임 에러가 발생할 수 있음 (이는 v1.26.1 기준이나, k8s 버전마다 다를 수 있음.)
cmd> mkdir $HOME/.kube cmd> cp -i /etc/kubernetes/admin.conf $HOME/.kube/config |
kubeadm init 을 통해 생성된 admin.conf 파일을 위 경로($HOME/.kube)로 복사합니다.
admin.conf 파일은 이 클러스터를 kubectl 커맨드로 제어할 수 있게 만들어주며,
기본 환경변수로 Shell에서 kubectl을 기본 환경변수로 선언하기 위한 작업입니다.
또는 이 작업 대신에 'export KUBECONFIG=/etc/kubernetes/admin.conf'라고 선언해줘도 되지만,
export의 특성으로 인해 이 세션 연결이 끊어지면 기억을 못하기 때문에, 매번 선언해줘야해서 번거로워집니다.
# coredns 파드는 CNI 플러그인이 마저 구성이 되어야 Running으로 변함. cmd> watch kubectl get po -A NAMESPACE NAME READY STATUS ---------------------------------------------------------------------------------------------------- kube-system coredns-787d4945fb-272w7 0/1 ContainerCreating kube-system coredns-787d4945fb-gxtf8 0/1 ContainerCreating kube-system etcd-master.test.org 1/1 Running kube-system kube-apiserver-master.test.org 1/1 Running kube-system kube-controller-manager-master.test.org 1/1 Running kube-system kube-proxy-28xk6 1/1 Running kube-system kube-scheduler-master.test.org 1/1 Running # CNI 플러그인으로 Calico 를 배포 cmd> kubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/v3.25.0/manifests/calico.yaml # watch로 변화상황을 모니터링해보면, Pod들이 Init을 거쳐 Running으로 변하는 모습을 볼 수 있음. cmd> watch kubectl get po -A NAMESPACE NAME READY STATUS ---------------------------------------------------------------------------------------------------- kube-system calico-kube-controllers-57b57c56f-jxzvd 1/1 Running kube-system calico-node-h2ztl 1/1 Running kube-system coredns-787d4945fb-272w7 1/1 Running kube-system coredns-787d4945fb-gxtf8 1/1 Running kube-system etcd-master.test.org 1/1 Running kube-system kube-apiserver-master.test.org 1/1 Running kube-system kube-controller-manager-master.test.org 1/1 Running kube-system kube-proxy-28xk6 1/1 Running kube-system kube-scheduler-master.test.org 1/1 Running |
cmd> kubeadm version -o short
v1.26.1 cmd> kubectl version --short Flag --short has been deprecated, and will be removed in the future. The --short output will become the default. Client Version: v1.26.1 Kustomize Version: v4.5.7 Server Version: v1.26.1 cmd> kubelet --version Kubernetes v1.26.1 |
Ⅴ. Kubernetes 설치 (192.168.56.10x - 워커 노드)
cmd> cat <<EOF | tee /etc/yum.repos.d/kubernetes.repo
[kubernetes] name=Kubernetes baseurl=https://packages.cloud.google.com/yum/repos/kubernetes-el7-x86_64 enabled=1 gpgcheck=1 repo_gpgcheck=1 gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg exclude=kubelet kubeadm kubectl EOF cmd> yum install -y --disableexcludes=kubernetes kubeadm kubelet kubectl # kubelet 은 마스터 노드의 api 서버와 worker 노드 간의 통신을 위한 서비스 cmd> systemctl status kubelet ... Active: inactive (dead) ... cmd> systemctl start kubelet # 아직 정상적으로 k8s 클러스터가 구성되지 않았기 때문에, 기동이 안되는게 정상입니다. cmd> systemctl status kubelet ... Active: failed ... |
# 기존에 구성된 containerd 설정파일의 존재 때문에 런타임 에러가 발생할 수 있기 때문에 아래와 같이 지워주고, 데몬 재실행
cmd> rm -rf /etc/containerd/config.toml cmd> systemctl restart containerd # 아까 저장해둔 토큰 커맨드로 클러스터에 worker 노드 join cmd> kubeadm join 192.168.56.120:6443 --token xxxxxx.xxxxxxxxxxxxxxxx \ --discovery-token-ca-cert-hash sha256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ... [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. # kubeadm join 과정에서 kubelet 이 자동적으로 실행이 되기 때문에, 따로 데몬을 start 시켜줄 필요 없음. cmd> systemctl status kubelet ... Active: active (running) ... |
cmd> mkdir $HOME/.kube/ cmd> scp master:/etc/kubernetes/admin.conf $HOME/.kube/config |
워커 노드에서 kubectl 커맨드를 쓸 일은 굳이 잘 없지만,
마스터 노드의 admin.conf 파일을 워커 노드의 경로($HOME/.kube)로 옮겨오는 방법도 있습니다.
# watch로 변화상황을 모니터링해보면, 총 노드의 개수만큼(마스터 포함) calico-node라는 이름의 pod 생성 확인됨. cmd> watch kubectl get po -A NAMESPACE NAME READY STATUS ---------------------------------------------------------------------------------------------------- kube-system calico-kube-controllers-57b57c56f-jxzvd 1/1 Running kube-system calico-node-h2ztl 1/1 Running kube-system calico-node-drfkc 1/1 Running kube-system calico-node-dtpz5 1/1 Running kube-system calico-node-ace2c 1/1 Running kube-system coredns-787d4945fb-272w7 1/1 Running kube-system coredns-787d4945fb-gxtf8 1/1 Running kube-system etcd-master.test.org 1/1 Running kube-system kube-apiserver-master.test.org 1/1 Running kube-system kube-controller-manager-master.test.org 1/1 Running kube-system kube-proxy-28xk6 1/1 Running kube-system kube-scheduler-master.test.org 1/1 Running |
cmd> kubeadm version -o short
v1.26.1 cmd> kubectl version --short Flag --short has been deprecated, and will be removed in the future. The --short output will become the default. Client Version: v1.26.1 Kustomize Version: v4.5.7 Server Version: v1.26.1 cmd> kubelet --version Kubernetes v1.26.1 |
# watch로 변화상황을 모니터링해보면, 총 노드의 개수만큼 calico-node라는 이름의 pod가 생성된 것을 확인 가능 cmd> watch kubectl get nodes NAME STATUS ROLES AGE VERSION ------------------------------------------------------------------------------- master.test.org Ready control-plane 19m v1.26.1 node1.test.org Ready <none> 8m43s v1.26.1 node2.test.org Ready <none> 8m47s v1.26.1 node3.test.org Ready <none> 8m57s v1.26.1 cmd> kubectl label node master.test.org node-role.kubernetes.io/master="" node/master.test.org labeled cmd> kubectl label node node1.test.org node-role.kubernetes.io/worker="" node/node1.test.org labeled cmd> kubectl label node node2.test.org node-role.kubernetes.io/worker="" node/node2.test.org labeled cmd> kubectl label node node3.test.org node-role.kubernetes.io/worker="" node/node3.test.org labeled cmd> watch kubectl get nodes NAME STATUS ROLES AGE VERSION ------------------------------------------------------------------------------- master.test.org Ready control-plane,master 19m v1.26.1 node1.test.org Ready worker 8m43s v1.26.1 node2.test.org Ready worker 8m47s v1.26.1 node2.test.org Ready worker 8m57s v1.26.1 |