cri-dockerd와 Calico로 구성하는 단일 마스터 클러스터
이 글은 Ubuntu 22.04 환경에서 kubeadm으로 1.28 클러스터를 구성한 Kubernetes 기본 설치 기록입니다. Kubernetes 1.24부터 Docker shim이 제거되었기 때문에 Docker 런타임을 계속 사용하려면 cri-dockerd를 컨테이너 런타임 인터페이스(CRI)로 별도 설치해야 합니다. CNI 플러그인은 Calico를 사용하며, 마스터 노드 1개와 워커 노드 2개로 구성하는 것을 기준으로 작성하였습니다.
버전 범위 안내: 이 글은 Kubernetes 1.28.2와 Ubuntu 22.04.3에서 검증한 과거 구축 기록입니다. Kubernetes 1.28은 현재 지원이 종료된 버전이므로 신규 구축 절차로 그대로 사용하지 말고, Kubernetes 공식 지원 상태와 최신 설치 문서를 먼저 확인하시기 바랍니다.
이 Kubernetes 기본 설치 가이드는 단일 마스터 클러스터 기준입니다. HA(고가용성) 멀티 마스터 구성은 HAProxy 로드밸런서와 etcd 클러스터 구성이 추가로 필요합니다.

구성 내역
설치 환경은 다음과 같습니다.
Kubernetes 1.28.2
Ubuntu 22.04.3 LTS
Container : cri-dockerd
CNI : calico
구성용 계정 : <admin-user>
Master node : <master-node>
Worker node : <worker-node-01> ~ <worker-node-02>
시작 전 모든 노드에서 swap을 비활성화해야 합니다. kubelet은 swap이 활성화된 환경에서 기본적으로 동작을 거부합니다.
sudo swapoff -a sudo sed -i '/ swap / s/^\(.*\)$/#/g' /etc/fstab
[공통]
cri-docker 설치
Kubernetes 1.24 이후 Docker shim이 제거되면서 Docker를 컨테이너 런타임으로 사용하려면 cri-dockerd를 별도로 설치해야 합니다. cri-dockerd는 Mirantis가 관리하는 오픈소스 프로젝트로, Docker Engine과 kubelet 사이의 CRI(Container Runtime Interface) 어댑터 역할을 합니다. 먼저 Docker Engine을 설치한 뒤 cri-dockerd 바이너리를 받아 systemd 서비스로 등록합니다.
Docker cgroup driver를 systemd로 맞추는 것이 중요합니다. kubelet도 기본적으로 systemd cgroup driver를 사용하므로, driver 불일치 시 노드가 NotReady 상태가 됩니다.
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
sudo systemctl enable --now docker && sudo systemctl status docker --no-pager
sudo usermod -aG docker <admin-user>
sudo docker container ls
# cri-docker Install
VER=$(curl -s https://api.github.com/repos/Mirantis/cri-dockerd/releases/latest|grep tag_name | cut -d '"' -f 4|sed 's/v//g')
echo $VER
wget https://github.com/Mirantis/cri-dockerd/releases/download/v${VER}/cri-dockerd-${VER}.amd64.tgz
tar xvf cri-dockerd-${VER}.amd64.tgz
sudo mv cri-dockerd/cri-dockerd /usr/local/bin/
# cri-docker Version Check
cri-dockerd --version
wget https://raw.githubusercontent.com/Mirantis/cri-dockerd/master/packaging/systemd/cri-docker.service
wget https://raw.githubusercontent.com/Mirantis/cri-dockerd/master/packaging/systemd/cri-docker.socket
sudo mv cri-docker.socket cri-docker.service /etc/systemd/system/
sudo sed -i -e 's,/usr/bin/cri-dockerd,/usr/local/bin/cri-dockerd,' /etc/systemd/system/cri-docker.service
sudo systemctl daemon-reload
sudo systemctl enable cri-docker.service
sudo systemctl enable --now cri-docker.socket
# cri-docker Active Check
sudo systemctl restart docker && sudo systemctl restart cri-docker
sudo systemctl status cri-docker.socket --no-pager
# Docker cgroup Change Require to Systemd
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
sudo systemctl restart docker && sudo systemctl restart cri-docker
sudo docker info | grep Cgroup
환경 설정
Kubernetes 네트워킹이 정상 동작하려면 커널 모듈과 sysctl 파라미터를 설정해야 합니다. br_netfilter 모듈은 Linux Bridge를 통과하는 패킷을 iptables/ip6tables에서 처리할 수 있게 해주며, overlay 모듈은 컨테이너 파일시스템에 사용하는 OverlayFS를 위해 필요합니다. net.ipv4.ip_forward = 1은 노드가 패킷을 다른 노드로 포워딩할 수 있도록 허용합니다. 이 설정은 모든 노드(마스터 + 워커)에 적용해야 합니다.
# Kernel Forwarding 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 cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf overlay br_netfilter EOF sudo modprobe overlay sudo modprobe br_netfilter # 필요한 sysctl 파라미터를 설정하면, 재부팅 후에도 값이 유지된다. cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf net.bridge.bridge-nf-call-iptables = 1 net.bridge.bridge-nf-call-ip6tables = 1 net.ipv4.ip_forward = 1 EOF # 재부팅하지 않고 sysctl 파라미터 적용하기 sudo sysctl --system
Package 설치
kubeadm, kubelet, kubectl 세 패키지를 설치합니다. 각 패키지의 역할은 다음과 같습니다.
- kubeadm — 클러스터 초기화(init)와 노드 합류(join)를 담당하는 부트스트랩 도구
- kubelet — 각 노드에서 실행되며 Pod 생명주기를 관리하는 에이전트
- kubectl — 클러스터를 제어하는 CLI 클라이언트
apt-mark hold로 패키지를 고정하면 apt upgrade로 인한 의도치 않은 버전 업그레이드를 방지할 수 있습니다. Kubernetes는 컴포넌트 버전을 맞춰 관리해야 하므로 반드시 hold 설정을 유지하고, 업그레이드 시에는 kubeadm upgrade 절차를 따릅니다.
sudo apt-get update sudo apt-get install -y apt-transport-https ca-certificates curl sudo curl -fsSLo /etc/apt/keyrings/kubernetes-archive-keyring.gpg https://dl.k8s.io/apt/doc/apt-key.gpg && echo "deb [signed-by=/etc/apt/keyrings/kubernetes-archive-keyring.gpg] https://apt.kubernetes.io/ kubernetes-xenial main" | sudo tee /etc/apt/sources.list.d/kubernetes.list sudo apt-get update sudo apt-get install -y kubelet kubeadm kubectl sudo apt-mark hold kubelet kubeadm kubectl sudo systemctl daemon-reload sudo systemctl restart kubelet
위 방법으로 패키지를 찾지 못하는 경우(E: Unable to locate package kubelet), pkgs.k8s.io 신규 리포지토리를 사용합니다.
$ sudo apt-get install -y kubelet kubeadm kubectl E: Unable to locate package kubelet E: Unable to locate package kubeadm E: Unable to locate package kubectl # kubelet kubeadm kubectl 설치시 위 에러 발생할 경우 아래 방안으로 설치 sudo apt-get update sudo apt-get install -y apt-transport-https ca-certificates curl gpg curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.28/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.28/deb/ /' | sudo tee /etc/apt/sources.list.d/kubernetes.list sudo apt-get update sudo apt-get install -y kubelet kubeadm kubectl sudo apt-mark hold kubelet kubeadm kubectl sudo systemctl daemon-reload sudo systemctl restart kubelet
[Master node]
kubeadm init으로 마스터 노드를 초기화합니다. --cri-socket 옵션으로 cri-dockerd 소켓을 명시하지 않으면 kubeadm이 런타임을 자동 감지하는 과정에서 오류가 발생할 수 있습니다. 초기화가 완료되면 출력 마지막에 워커 노드 join 명령어가 표시됩니다. 이 명령어의 토큰은 24시간 후 만료되므로 즉시 복사해 두거나 kubeadm token create --print-join-command로 재발급합니다.
kubeconfig 설정 후 Calico CNI를 설치합니다. CNI가 설치되기 전까지는 노드 상태가 NotReady로 표시되며, calico.yaml 적용 후 수 분 내에 Ready로 전환됩니다.
sudo kubeadm config images pull --cri-socket unix:///run/cri-dockerd.sock sudo kubeadm init --cri-socket /var/run/cri-dockerd.sock mkdir -p $HOME/.kube sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config sudo chown $(id -u):$(id -g) $HOME/.kube/config kubectl get nodes -o wide kubectl get pods -A kubectl describe node <master-node> # Calico CNI 설치 curl https://raw.githubusercontent.com/projectcalico/calico/v3.26.1/manifests/calico.yaml -O kubectl apply -f calico.yaml kubectl get nodes kubectl get pod --all-namespaces
[worker node]
워커 노드에서 kubeadm join 명령어를 실행하여 클러스터에 합류합니다. kubeadm init 출력에서 복사한 명령어를 그대로 사용하되, --cri-socket 옵션을 추가해야 합니다. join이 완료된 후 마스터 노드에서 kubectl get nodes를 실행하여 워커 노드가 Ready 상태로 등록되는지 확인합니다.
# kubeadm init 실행시 마지막 출력되는 명령어 사용 sudo kubeadm join x.x.x.x:6443 --token xxxxxxxxxxxx --discovery-token-ca-cert-hash sha256:xxxxxxxxxxxx --cri-socket /var/run/cri-dockerd.sock kubectl get nodes
kubectl 명령어 자동 완성
kubectl bash completion을 설정하면 Tab 키로 리소스명, 네임스페이스 등을 자동 완성할 수 있어 운영 편의성이 크게 향상됩니다. alias k=kubectl과 함께 설정하면 짧은 명령어로 빠르게 조작할 수 있습니다. 자세한 내용은 kubectl 자동 완성 설정 — Kubernetes 공식 문서를 참고하세요.
echo 'source <(kubectl completion bash)' >> ~/.bashrc echo 'alias k=kubectl' >> ~/.bashrc echo 'complete -o default -F __start_kubectl k' >> ~/.bashrc source ~/.bashrc
노드 Ready와 kube-system 파드 상태 확인
모든 노드가 정상적으로 등록되고 kube-system 파드들이 Running 상태가 되면 클러스터 구성이 완료된 것입니다. 아래 명령어로 최종 상태를 확인합니다.
# 노드 상태 확인 — 모든 노드 Ready 여야 정상 kubectl get nodes -o wide # 시스템 파드 상태 확인 — 모두 Running 또는 Completed kubectl get pods -n kube-system # 클러스터 정보 확인 kubectl cluster-info
워커 노드가 NotReady로 유지될 경우 해당 노드에서 sudo systemctl status kubelet과 journalctl -u kubelet -n 50으로 에러 메시지를 확인합니다. 대부분은 cgroup driver 불일치 또는 cri-dockerd 미기동으로 인한 문제입니다.
참고
참고 문서: kubeadm으로 클러스터 생성 — Kubernetes 공식 문서 · Calico 온프레미스 설치 문서


