모든 노드에 Kubernetes 설치
https://kubernetes.io/ko/docs/setup/production-environment/tools/kubeadm/install-kubeadm/ 해당 링크를 참고하여 설치를 진행해준다.
1. apt 패키지 색인을 업데이트하고, 쿠버네티스 apt 리포지터리를 사용하는 데 필요한 패키지를 설치한다.
sudo apt-get update # apt 패키지 목록을 업데이트한다.
sudo apt-get install -y apt-transport-https ca-certificates curl # 우분투나 데비안 계열 리눅스에서 외부 저장소(특히 HTTPS 기반 저장소)와 안전하게 통신하기 위해 필요한 필수 패키지들을 설치하는 명령어
2. 인증키를 저장할 전용 디렉토리를 만든다.(패키지 위변조 확인 키)
sudo mkdir -p /etc/apt/keyrings
3. 쿠버네티스 저장소의 서명 키를 다운로드 받아 GPG 형식으로 변환한 후 저장한다.
sudo curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.29/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
4. 쿠버네티스 패키지를 받기 위해 공식 URL을 APT에 등록한다.
echo "deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.29/deb/ /" | sudo tee /etc/apt/sources.list.d/kubernetes.list
5. apt 패키지 목록을 업데이트 한다.
sudo apt-get update
6. kublet, kubeadm, kubectl을 설치하고 해당 버전을 고정시킨다.
sudo apt-get install -y kubelet=1.29.15-1.1 kubeadm=1.29.15-1.1 kubectl=1.29.15-1.1
sudo apt-mark hold kubelet kubeadm kubectl # 버전 고정
cf. 버전을 고정하는 이유
쿠버네티스는 구성 요소 간 버전 호환성이 매우 민감하기 때문에 자동 업데이트 시 클러스터 오류가 발생할 수 있다.
따라서 kubelet, kubeadm, kubectl의 버전을 고정하여 예기치 않은 업그레이드로 인한 장애를 예방하는 것이 중요하다.
7. 쿠버네티스 정상 설치 확인.
kubelet --version # kublet 버전 확인
kubeadm version # kubeadm 버전 확인
kubectl version --client --output=yaml # kubectl 버전 확인
kublet, kubeadm, kubectl ?
1. kubelet
노드 에이전트이다. 쿠버네티스의 핵심 실행기다.
- 각 노드(마스터, 워커 둘 다)에 설치되는 데몬 프로세스다.
- kubelet은 **파드(Pod)**를 실제로 실행시키고, 상태를 지속적으로 모니터링한다.
주요 역할은 다음과 같다.
- 노드에서 컨테이너(Pod)를 실행, 모니터링, 종료한다.
- kube-apiserver와 통신하여, “이 노드에서 어떤 Pod가 떠야 하는지”를 수신한다.
- /etc/kubernetes/manifests/ 디렉토리 안의 YAML 파일도 감시한다,
kubelet이 설치되어 있지 않다면, 노드에 컨테이너가 뜨지 않는다. 즉, 쿠버네티스 클러스터가 동작할 수 없게 된다.
2. kubeadm
클러스터 설치/초기화 도구다.
- 쿠버네티스 클러스터를 빠르게 설치하고 초기화하기 위한 설치 자동화도구이다.
- 인증서, 키, 초기 설정 파일을 자동으로 생성해준다.
주요 기능은 다음과 같다.
- kubeadm init: 마스터 노드 초기화
- kubeadm join: 워커 노드를 클러스터에 연결
- kubeadm reset: 클러스터 초기화 삭제
- kubeadm config, certs, token 등의 서브명령어 제공
해당 패키지가 없을 시 쿠버네티스 클러스터를 수동으로 설정해야 하는데, 매우 복잡하고 위험하다.
3. kubectl
쿠버네티스 클러스터를 제어하기 위한 CLI 도구이다.
- 사용자가 쿠버네티스 클러스터를 제어하고 관리할 수 있는 명령어 인터페이스
- kubectl을 통해 클러스터 상태 확인, 리소스 생성/삭제, 로그 확인 등을 할 수 있음
주요 역할은 다음과 같다.
- kubectl get pods, kubectl apply -f, kubectl logs, kubectl exec 등
- 클러스터와 통신할 때 kubeconfig (~/.kube/config)를 사용
해당 패키지가 없을 시, 사용자가 클러스터를 조작할 수 없다. 즉, 쿠버네티스 명령어를 실행할 수 없다.
server01 마스터 노드 설정
먼저, 쿠버네티스 인증서 상태 먼저 확인해준다. 쿠버네티스는 여러 컴포넌트들이 서로 통신하면서 API 요청을 주고받는데, 이 통신은 외부 공격에 대비해 암호화되어 있어야 한다. 이를 위해 다음과 같은 인증서들이 사용된다.
| admin.conf | kubectl이 클러스터에 접근할 때 사용하는 관리자 인증 정보 |
| apiserver.crt | kube-apiserver가 클라이언트 요청을 받을 때 사용하는 서버 인증서 |
| apiserver-kubelet-client.crt | apiserver가 kubelet에 접근할 때 사용하는 클라이언트 인증서 |
| etcd-server.crt | etcd 서버 간 또는 etcd 클라이언트와 통신 시 사용 |
| front-proxy-client.crt | aggregator proxy 기능을 위한 인증서 |
| controller-manager.conf | 컨트롤러 매니저가 클러스터 내부 컴포넌트에 접근할 때 사용 |
| scheduler.conf | 스케줄러가 클러스터에 접근할 때 사용 |
이 외에도 ca.crt, etcd-ca.crt, front-proxy-ca.crt 등 인증서 서명용 CA 인증서도 존재한다. 또한 위 인증서들이 필요한 이유는 크게 다음과 같다.
1. 컴포넌트 간 통신 보안
- 쿠버네티스의 구성 요소는 서로 REST API 호출을 통해 통신한다.
- ex) kube-apiserver ↔ kubelet, kube-controller-manager ↔ apiserver
- 이 때 TLS 인증서를 사용하여 암호화된 연결을 제공하고, 서로의 신원을 인증한다.
2. kubectl의 사용자 인증
- 사용자가 kubectl로 클러스터에 접근할 때, 인증서를 기반으로 인증한다.
- 즉, admin.conf에 있는 인증서가 있어야 kubectl get pods 같은 명령어 실행이 가능하다.
3. 외부 공격 방지
- 인증서가 없다면 누구나 apiserver에 접근할 수 있고, 이는 보안적으로 치명적이다.
- TLS 인증서는 제3자의 패킷 도청, 위장 서버 공격(Man-in-the-middle) 등을 막아준다.
kubeadm init 명령어를 실행하면 /etc/kubernetes/pki/ 경로에 위에서 말한 인증서들을 자동 생성한다. 해당 인증서들은 보통 1년(365일) 유효하며, 갱신도 가능하다. 이후 kubeadm certs check-expiration 명령어로 인증서의 남은 기간을 확인할 수 있다.
인증서 상태는 아래 명령어로 확인 가능하고, 아직 kubeadm init으로 클러스터를 초기화하지 않았기 때문에 결과에 !MISSING!이 뜨게 된다.
kubeadm certs check-expiration

kubectl이 사용할 수 있는 이미지 리스트를 확인한다.
kubeadm config images list

다음으로 쿠버네티스 설치에 필요한 이미지들을 다운로드해준다.
kubeadm config images pull
# 위 명령어에서 CRI 관련 오류가 발생할 시
# kubeadm config images pull --cri-socket /run/containerd/containerd.sock
또한 아래 명령어를 통해 다운로드 된 이미지들을 확인할 수 있다.
sudo crictl images

성공적으로 이미지들이 다운된 것을 확인할 수 있다.
cf. crictl이란?
crictl 명령어는 쿠버네티스 환경에서 컨테이너 런타임을 직접 조작하거나 상태를 확인하기 위한 CLI 도구다.
- 쿠버네티스는 kubelet → CRI(Container Runtime Interface) → 런타임(containerd, CRI-O 등) 구조로 동작한다.
- crictl은 kubelet이 사용하는 컨테이너 런타임 상태를 직접 확인하거나 조작할 수 있는 명령어 도구다.
cf. CRI(Container Runtime Interface)?
CRI(Container Runtime Interface)는 쿠버네티스의 핵심 구성 요소인 kubelet이 다양한 컨테이너 런타임과 통신할 수 있도록 표준화한 인터페이스다. kubelet이 어떤 런타임(Docker, Containerd, ...)이든 똑같이 제어할 수 있도록 만든 중간 계층이 바로 CRI다.
Docker를 쓰던 예전엔 docker ps, docker images, docker logs 등으로 확인했지만, 지금은 containerd, CRI-O 같은 Docker가 아닌 런타임을 사용하는 경우가 많기 때문에 그에 맞는 CLI 도구가 필요하고, 그게 바로 crictl이다.
주요 명령어는 다음과 같다.
| crictl images | 현재 컨테이너 런타임에 저장된 이미지 목록 확인 |
| crictl ps -a | 실행 중이거나 중지된 컨테이너 목록 조회 |
| crictl inspect <CONTAINER_ID> | 특정 컨테이너 상세 정보 조회 |
| crictl logs <CONTAINER_ID> | 컨테이너 로그 확인 |
| crictl pull <IMAGE> | 이미지 수동 다운로드 |
| crictl run | (직접 컨테이너 실행도 가능, 보통은 안 씀) |
기본적으로 crictl은 다음 순서의 런타임으로 연결을 시도한다.
- /var/run/dockershim.sock
- /run/containerd/containerd.sock
- /run/crio/crio.sock

위의 이미지에서 WARN[0000] 메시지가 나왔던 이유는 첫 번째(dockershim)는 사용 안 하니까 경고가 발생한 것이고, 결국 /run/containerd/containerd.sock로 성공적으로 연결된 상태다.
해당 오류를 없애기 위해 다음과 같이 파일을 생성해준다. 현재 나는 컨테이너 런타임으로 containerd를 사용중이기 때문에 crictl의 runti me-endpoint, image-endpoint를 containerd로 명시해서 설정해주어야 위와 같은 오류가 발생하지 않는다.
sudo vi /etc/crictl.yaml
# 아래와 같이 내용을 추가해준다.
# runtime-endpoint: unix:///run/containerd/containerd.sock
# image-endpoint: unix:///run/containerd/containerd.sock
다시 아래 명령어를 입력하면, 오류가 안나는 것을 확인할 수 있다.
sudo crictl images

이제 본격적으로, 클러스터를 초기화 해보자. 다음 명령어를 사용한다.
sudo kubeadm init --apiserver-advertise-address=10.0.2.15 \
--pod-network-cidr=192.168.0.0/16 \
--cri-socket /run/containerd/containerd.sock
- --apiserver-advertise-address: API 서버가 노출할 IP 주소를 적는다. 마스터 노드의 IP를 적어주면 된다.
- --pod-network-cidr: 파드의 네트워크 범위를 적는다. calico는 192.168.0.0/16, flannel은 10.244.0.0/16을 적어준다.
- --cri-socket: 사용할 컨테이너 런타임 명시.(containerd)
위 명령어 실행으로 클러스터가 생성되며, 각 인증서들이 /etc/kubernetes에 생성된다.

위 명령어 출력 해당 부분은 이후 워커 노드와 마스터 노들를 연결할 때 사용되기에 메모장 같은 곳에 따로 적어둔다.
여기까지 한 후, 다음 명령어를 통해 인증서 상태를 확인해보면 인증이 되어있는 것을 확인할 수 있다.
sudo su - # root 계정으로 전환.
kubeadm certs check-expiration

이제 마스터 노드를 편하게 사용할 수 있도록 루트 권한이 아니라 일반 사용자 권한으로도 쿠버네티스를 사용할 수 있게 설정해주자.
kubectl은 클러스터에 접속할 때 config 파일(kubectl이 클러스터에 접근할 수 있는 인증 정보, API 주소, 클러스터명, 사용자 정보 등)이 필요하다. 하지만, 초기 kubeadm init을 하면 생성되는 config 파일은 /etc/kubernetes/admin.conf 여기에 존재한다.
해당 경로는 root 전용 경로고, 일반 사용자 계정으로는 기본적으로 접근이 불가능 하기 때문에 일반 사용자 계정으로도 해당 파일들에 접근할 수 있도록, 디렉토리를 만들어줘야 한다.
# 일반 사용자 계정으로 실행.
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
이어서, 쿠버네티스 클러스터 내의 통신을 위해 CNI(Container Network Interface)를 설치해주자. 필자는 calico로 설치하기로 하였다.
쿠버네티스는 파드를 띄우기만 하지, 네트워크를 직접 구성해주진 않는다.
그래서 클러스터 내부의 모든 통신(파드<->파드, 파드<->서비스, 마스터<->워커, CoreDNS<->파드)을 가능하도록 하기 위해 CNI를 설치해야 한다. CNI가 쿠버네티스 클러스터의 모든 네트워크 구성을 담당한다.
먼저, 아래 명령어를 통해 Calico 설치를 해주는 Operator 먼저 설치해주자.
Operator는 Kubernetes에서 특정 애플리케이션(또는 시스템 구성 요소)을 사람 대신 자동으로 설치, 구성, 운영, 복구해주는 컨트롤러 기반 자동화 도구이다. 결국, Operator는 특정 시스템(예: MySQL, Kafka, Prometheus 등)을 쿠버네티스 환경에서
쿠버네티스다운 방식으로 설치, 운영할 수 있게 만들어주는 자동화 매니저라고 보면 된다.
kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.26.3/manifests/tigera-operator.yaml
Calico 설치에 필요한 커스텀 리소소를 설치한다. 해당 YAML 파일은 Calico 오퍼레이터에게 줄 설정 파일이다. Calico 오퍼레이터는 단순히 Operator일 뿐이고, 어떻게 설치하고 어떤 구성을 사용할지는 이 파일에서 전달해줘야 한다.
curl https://raw.githubusercontent.com/projectcalico/calico/v3.26.3/manifests/custom-resources.yaml -O
ls 명령어를 통해 위 파일이 잘 다운로드 되었는지 확인한다.
ls

해당 YAML을 활용해 calico를 설치해준다.
kubectl create -f custom-resources.yaml
설치가 끝난 후에는 아래 명령어를 통해 calico에 대한 파드가 실행 중인지를 확인한다. 모든 요소가 작동하려면 약 2분 정도의 시간이 소요된다.
watch kubectl get pods -n calico-system

위와 같이 calico 파드 3개 모두 정상적으로 동작하고 있는 것을 확인할 수 있다.
| calico-kube-controllers-**** | Calico 컨트롤러. CRD 감시 등 내부 관리 (컨트롤 플레인 역할) |
| calico-node-**** | 각 노드에 설치된 Calico 데몬셋 (네트워크 엔진) |
| calico-typha-**** (선택) | Typha는 성능 최적화용 컴포넌트 |
cf. 네임스페이스(Namespace)?
쿠버네티스 클러스터 안에서 리소스를 논리적으로 분리하는 단위이다.
기본적으로 default 네임스페이스가 존재하며, Calico 같은 시스템 구성 요소들은 calico-system이나 kube-system 같은 별도의 네임스페이스에 배포된다.
네임스페이스는 리소스 이름이 충돌하지 않도록 하고, 멀티테넌시 환경에서도 구분된 공간을 제공한다.
쿠버네티스 클러스터 노드도 확인해준다.
kubectl get node -o wide

위와 같이 마스터 노드가 정상적으로 등록되어 있다. 아래와 같은 항목들을 주로 확인해주면 된다.
- STATUS: Ready면 정상
- ROLES: control-plane이면 마스터 노드
- CONTAINER-RUNTIME: containerd인지, docker인지 등
- INTERNAL-IP: kubelet이 바인딩한 내부 IP
- EXTERNAL-IP: 외부 통신용 IP (없으면 <none>)
CF. 마스터 노드에 파드를 추가하는 방법
마스터 노드는 클러스터 제어용이기 때문에 부하를 최소화하기 위해 파드를 올리지 못하도록 설정되어 있다. 하지만 테스트 환경이나 소규모 환경에서는 마스터 노드에 파드를 올려야 할 때가 있기에, 마스터 노드에 파드를 올릴 수 있도록 다음 설정을 해야할 때도 있다.
먼저, 클러스터에 등록된 노드들의 이름과 상태들을 먼저 확인해준다.
kubectl get node
마스터 노드에 설정된 Taint(오염값)를 확인한다.
kubectl describe node server01 | grep Taints

위 값은 마스터 노드에는 일반 파드를 스케줄링 하지 말라는 의미이다.
해당 Taint를 해제해준다. Taint는 쉽게 말해 노드 쪽에 거는 제약 조건이다. <key>=<value>:<effect> 이와 같은 구조로 이루어졌다.
kubectl taint nodes --all node-role.kubernetes.io/control-plane-
보통 현업 운영 환경에서는 마스터 노드에 일반 파드를 추가해서 운영하지 않는 것이 일반적이다. 다음과 같은 이유 때문이다.
| 안정성 | 마스터 노드는 etcd, scheduler, controller 등 핵심 구성 요소가 돌아간다. 여기에 일반 파드까지 올리면 자원 경쟁이 발생해 클러스터 전체가 불안정해질 수 있다. |
| 보안 | 일반 워크로드가 올라가면 외부 트래픽을 통해 마스터 노드로 접근 가능성이 생기기에 보안성이 낮아진다. |
| 운영 분리 | 관리와 운영을 분리하는 게 유지보수/문제 해결 시 명확해서 좋다. |
그래서 현업 운영 환경에서는 다음과 같이 주로 구성을 한다.
| 마스터 노드 | control-plane 전용으로 사용. taint 제거 x. |
| 워커 노드(Worker) | 사용자 애플리케이션과 서비스 파드가 올라가는 전용 노드들. 필요 시 autoscaling 구성한다. |
| 관리 서버(Management Server) | kubectl이나 helm 등 클러스터 제어를 위한 CLI 도구를 설치해서 사용하는 별도의 일반 서버. 마스터 노드 직접 접속은 자제한다. |
| 운영 도구 | GitOps(예: ArgoCD), Jenkins, Ansible 등으로 CI/CD와 배포 자동화. 관리 서버에서 원격 조작한다. |
다음 포스팅에서는 워커 노드를 설정해보기로 하자.
Reference
- https://kubernetes.io/ko/docs/home/
- 정철원, ⌜한권으로 배우는 도커&쿠버네티스⌟, 한빛미디어(주), 2024, 556쪽