CKA를 향하여 (아키텍처, Docker, Containerd, ETCD, Kube-API Server, Kube Controller Manager, Kube-Scheduler, Kubelet, Kube-Proxy, Kubernetes Pods, YAML로 Pod 생성하기, 레플리카, Deployment, Services, Namespace, 쿠버네티스 관리 방법, kubectl apply 명령어의 작동 방식)
아키텍처로 보는 컴포넌트들
0. 쿠버네티스 아키텍처
마스터 노드(Control Plane) 구성 요소
마스터 노드는 쿠버네티스 클러스터를 관리하고, 다양한 노드에 관한 정보를 저장하며, 컨테이너를 어디에 배치할지 계획하고, 노드와 컨테이너를 모니터링하는 등의 역할을 담당한다.
1. etcd
- 역할: 고가용성 키-값 저장소
- 기능: 클러스터의 모든 데이터를 저장(어떤 컨테이너가 어떤 노드에 있는지, 언제 로드되었는지 등)
- 특징: 키-값 형식으로 정보를 저장하는 데이터베이스
2. kube-scheduler
- 역할: 컨테이너를 적절한 노드에 배치하는 스케줄러
- 기능: 컨테이너의 리소스 요구사항, 작업자 노드의 용량, 테인트(Taints)와 톨러레이션(Tolerations) 또는 노드 어피니티(Node Affinity) 규칙과 같은 정책이나 제약을 기반으로 적절한 노드를 식별
3. 컨트롤러(Controllers)
- 노드 컨트롤러: 노드를 관리하고, 클러스터에 새 노드를 온보딩하며, 노드가 사용 불가능하거나 파괴된 상황을 처리
- 복제 컨트롤러: 복제 그룹에서 항상 원하는 수의 컨테이너가 실행되도록 보장
4. kube-apiserver
- 역할: 쿠버네티스의 주요 관리 컴포넌트
- 기능: 클러스터 내의 모든 작업을 조정
쿠버네티스 API를 노출시켜 외부 사용자가 클러스터에 대한 관리 작업을 수행할 수 있게 하고
다양한 컨트롤러가 클러스터 상태를 모니터링하고 필요에 따라 변경할 수 있게 한다.
또한 워커 노드가 서버와 통신할 수 있게 한다.
워커 노드(Worker Node) 구성 요소
1. 컨테이너 런타임 엔진
- 역할: 컨테이너를 실행하는 소프트웨어
예시: Docker, ContainerD, Rocket
- 클러스터의 모든 노드(마스터 노드 포함)에 설치해야 한다.
2. kubelet
역할: 각 노드에서 실행되는 에이전트 기능: kube-apiserver로부터 지시를 받아 필요에 따라 노드에 컨테이너를 배포하거나 제거한다. 또한 노드와 컨테이너의 상태를 모니터링하기 위해 kube-apiserver에 정기적으로 상태 보고서 전달
3. kube-proxy
- 역할: 워커 노드 간의 통신 활성화
- 기능: 컨테이너가 서로 통신할 수 있도록 작업자 노드에 필요한 규칙을 설정한다.
1. Docker vs Conatinerd
Docker의 등장
컨테이너 시대 초기에는 Docker가 주요 컨테이너 기술이었다. Docker는 컨테이너 작업을 간단하게 만들어 가장 널리쓰이는 컨테이너 도구이다.
쿠버네티스와 Docker의 관계
초기 쿠버네티스는 Docker를 오케스트레이션하기 위해 설계되었다. 쿠버네티스와 Docker가 강하게 결합되어 있었으며, 쿠버네티스는 Docker 외에 다른 컨테이너 솔루션을 지원하지 않았다.
컨테이너 런타임 인터페이스(CRI)의 등장
쿠버네티스의 인기가 높아지면서 rkt와 같은 다른 컨테이너 런타임들도 쿠버네티스와 통합되기어야 하는 요구사항이 생겨났다.
이에 쿠버네티스는 다양한 컨테이너 런타임을 지원하기 위해 컨테이너 런타임 인터페이스(Container Runtime Interface, CRI)를 도입했다.
OCI 표준
CRI는 OCI(Open Container Initiative) 표준을 준수하는 모든 공급업체가 쿠버네티스의 컨테이너 런타임으로 작동할 수 있게 했다.
OCI 표준은 아래와 같다.
- 이미지 스펙(Imagespec): 이미지 빌드 방법에 대한 사양
- 런타임 스펙(Runtimespec): 컨테이너 런타임 개발 방법에 대한 표준
Dockershim과 Containerd
Docker의 구성 요소
Docker는 단순한 컨테이너 런타임이 아니라 여러 도구들을 가리키는 용어이다.
- Docker CLI
- Docker API
- 이미지 빌드 도구
- 볼륨, 인증, 보안 지원
- runC라는 컨테이너 런타임
- Containerd(runC를 관리하는 데몬)
Dockershim의 등장과 제거
Docker는 CRI 이전에 개발되었기 때문에 CRI 표준을 지원하도록 설계되지 않았다. 그래서 쿠버네티스는 Dockershim이라는 임시 솔루션을 도입하여 Docker를 계속 지원했다.
하지만 Dockershim은 유지보수성 문제로 인해 쿠버네티스 1.24 버전에서 제거되었습니다. 이로인해 Docker에 대한 직접적인 지원이 중단되었다.
이미지 호환성
Docker가 제거된 후에도 Docker로 빌드된 모든 이미지는 계속 작동한다. 이는 Docker가 OCI 표준의 이미지 스펙을 따르기 때문이다.
Containerd
Containerd는 원래 Docker의 일부였지만 현재는 독립적인 프로젝트로 발전했으며, CNCF(Cloud Native Computing Foundation)를 Graduated 했다.
따라서 Docker를 설치하지 않고도 Containerd만 독립적으로 설치할 수 있다.
CLI 도구 비교
쿠버네티스 환경에서 컨테이너를 관리하기 위한 여러 CLI 도구가 있다.
1. ctr
- 용도: Containerd 커뮤니티가 개발한 것으로 Containerd 디버깅 전용으로 사용된다.
- 특징: 제한된 기능만 지원하며 사용자 친화적이지 않음
1
2
3
4
5
# 이미지 풀
ctr images pull docker.io/library/redis:latest
# 컨테이너 실행
ctr run docker.io/library/redis:latest redis-container
2. nerdctl
- 용도: Containerd 커뮤니티가 개발한 것으로 일반적인 컨테이너 관리에 사용된다.
- 특징: Docker CLI와 매우 유사하며 Docker가 지원하는 대부분의 옵션 지원
- 추가 기능: 암호화된 컨테이너 이미지, 지연 풀링, P2P 이미지 배포, 이미지 서명 및 검증 등이 있다.
1
2
3
4
5
# 컨테이너 실행
nerdctl run nginx
# 포트 매핑
nerdctl run -p 8080:80 nginx
3. crictl (CRI Control)
- 용도: 쿠버네티스 커뮤니티가 개발한 것으로 컨테이너 디버깅 및 트러블슈팅을 위해 사용된다.
- 특징: 모든 CRI 호환 런타임과 작동
- 추가 기능: 파드(Pod)를 인식할 수 있다.
1
2
3
4
5
6
7
8
9
10
11
# 컨테이너 목록
crictl ps
# 컨테이너 내부 명령 실행
crictl exec -it <container-id> sh
# 로그 확인
crictl logs <container-id>
# 파드 목록
crictl pods
Docker vs crictl 명령어 비교
쿠버네티스 1.24 이전에는 Docker 명령어를 사용하여 컨테이너를 다뤘지만, 이제는 crictl 명령어를 사용해야 한다.
Docker 명령어 | crictl 명령어 | 설명 |
---|---|---|
docker attach | crictl attach | 실행 중인 컨테이너에 연결 |
docker exec | crictl exec | 컨테이너 내에서 명령 실행 |
docker images | crictl images | 이미지 목록 조회 |
docker info | crictl info | 시스템 정보 표시 |
docker inspect | crictl inspect | 컨테이너 상세 정보 조회 |
docker logs | crictl logs | 컨테이너 로그 조회 |
docker ps | crictl ps | 실행 중인 컨테이너 목록 조회 |
docker stats | crictl stats | 리소스 사용량 통계 조회 |
docker version | crictl version | 버전 정보 조회 |
2. ETCD 기초
etcd란 무엇인가?
etcd는 다음과 같은 특징을 가진 분산 키-값 저장소이다.
- 분산형: 여러 노드에 걸쳐 데이터를 저장한다.
- 신뢰성: 데이터의 일관성과 가용성을 보장한다.
- 키-값 저장소: 데이터를 키-값 쌍으로 저장한다.
- 단순함: 사용하기 쉽고 간단한 API를 제공한다.
- 보안성: 보안 기능을 내장하고 있다.
- 빠른 속도: 빠른 읽기와 쓰기 성능을 제공한다.
키-값 저장소 vs RDBMS
RDBMS
RDBMS는 테이블 형식으로 데이터를 저장한다.
- 행(rows)과 열(columns)로 구조화
- 각 행은 개별 레코드(예: 사람)를 표현
- 각 열은 정보의 유형(예: 이름, 나이)을 표현
이러한 구조의 문제점으로
- 새로운 열을 추가하면 전체 테이블에 영향이 있다.
- 구조 변경이 복잡하다.
키-값 저장소
키-값 저장소는 문서나 페이지 형태로 정보를 저장한다.
- 각 개인은 별도의 문서(파일)를 가진다,
- 개인에 관한 모든 정보는 해당 파일 내에 저장된다.
- 파일은 다양한 형식이나 구조를 가질 수 있다
- 한 파일의 변경은 다른 파일에 영향을 주지 않는다.
etcd 시작하기
설치 및 실행
- GitHub Releases 페이지에서 운영 체제에 맞는 바이너리를 다운로드한다.
- 압축을 해제 후 etcd 실행 파일을 실행한다.
etcd를 실행하면 2379 포트에서 서비스가 된다.
etcdctl 클라이언트 사용하기
etcd와 함께 제공되는 기본 클라이언트는 etcdctl
이다. 이는 etcd의 명령줄 클라이언트로, 키-값 쌍을 저장하고 검색하는 데 사용한다.
API 버전 확인하기
etcdctl
을 사용하기 전에 어떤 API 버전으로 구성되어 있는지 확인하는 것이 중요하다.
1
./etcdctl version
출력
1
2
etcdctl version: 3.X.X
API version: 2
여기서 두 가지 버전 정보가 있다:
etcdctl
유틸리티 자체의 버전- 작동하도록 구성된 API 버전(2 또는 3)
API 버전 변경하기
API v3.0으로 작업하려면 두 가지 방법이 있습니다:
- 각 명령 앞에 환경 변수 설정:
1
ETCDCTL_API=3 ./etcdctl <command>
- 전체 세션에 대한 환경 변수 설정:
1
export ETCDCTL_API=3
API v2 명령어 예시
API v2를 사용하는 경우
1
2
3
4
5
# 키-값 저장하기
./etcdctl set key1 value1
# 키-값 검색하기
./etcdctl get key1
API v3 명령어 예시
API v3를 사용하는 경우
1
2
3
4
5
# 키-값 저장하기
./etcdctl put key1 value1
# 키-값 검색하기
./etcdctl get key1
etcd의 역사
etcd의 주요 버전 이력:
- v0.1: 2013년 8월 최초 출시
- v2.0: 2015년 2월 공식 안정 버전 출시
- RAFT 합의 알고리즘 재설계
- 초당 10,000개 이상의 쓰기 지원
- v3.0: 2017년 1월 출시
- 많은 최적화 및 성능 향상
- API 변경 (set/get → put/get)
- CNCF 인큐베이션: 2018년 11월 CNCF에 합류
v2.0과 v3.0 사이의 주요 변경 사항
- API 버전 변경
- 명령어 구문 변경
- v3.0에서는
version
이 옵션이 아닌 명령으로 변경됨
3. K8s에서의 ETCD
ETCD는 Kubernetes 클러스터에 관한 다음과 같은 중요 정보를 저장하며 kubectl get
명령을 실행할 때 볼 수 있는 모든 정보는 ETCD 서버에서 가져온다.
- 노드(Nodes)
- 파드(Pods)
- 컨피그맵(ConfigMaps)
- 시크릿(Secrets)
- 계정(Accounts)
- 역할(Roles)
- 역할 바인딩(Role Bindings)
- 기타 Kubernetes 리소스
클러스터에 대한 모든 변경사항(추가 노드 추가, 파드 배포, 레플리카셋 등)은 ETCD 서버에서 업데이트된다. 이런 변경사항이 ETCD 서버에 업데이트된 후에야 완료된 것으로 간주한다.
Kubernetes 배포 방식에 따른 ETCD 설정
클러스터 설정 방법에 따라 ETCD는 다르게 배포된다.
1. 수동 배포(From Scratch)
클러스터를 처음부터 설정하는 경우
- ETCD 바이너리를 직접 다운로드
- 바이너리 설치 및 구성
- 마스터 노드에서 서비스로 ETCD 구성
이 방식에서는 다음과 같은 여러 옵션이 서비스에 전달된다.
- 인증서 관련 옵션
- 클러스터링 옵션
- Advertise 클라이언트 URL(advertised client URL): ETCD가 수신 대기하는 주소(주로 서버 IP와 기본 포트 2379)
2. kubeadm을 이용한 배포
kubeadm을 사용하여 클러스터를 설정하는 경우
- kubeadm이 kube-system Namespace에 ETCD 서버를 파드로 배포
- 이 파드 내에서 ETCD 제어 유틸리티를 사용하여 ETCD 데이터베이스 탐색 가능
ETCD 데이터 구조
Kubernetes는 특정 디렉토리 구조에 데이터를 저장한다.
- 루트 디렉토리는 ‘레지스트리’(registry)가 있고
- 그 아래에 여러 Kubernetes 컴포넌트가 있다.
- 노드(minions)
- 파드(pods)
- 레플리카셋(replica sets)
- Deployment(deployments) 등
고가용성 환경에서의 ETCD
고가용성 환경에서는 ETCD를 활용하기 위해 알아야하는 점은 아래와 같다.
- 클러스터에 여러 마스터 노드가 존재함
- 마스터 노드에 여러 ETCD 인스턴스가 분산 배치됨
- ETCD 서비스 구성에서 올바른 매개변수 설정 필요
- 초기 클러스터 옵션(initial cluster option)에서 다양한 ETCD 서비스 인스턴스 지정 필요
ETCD 탐색 명령어
ETCDCTL은 ETCD와 통신하는데 사용된다. ETCDCTL은 버전 2와 버전 3이라는 두 가지 API 버전을 사용하여 ETCD 서버와 통신한다.
기본적으로 버전 2로 설정되어 있으며, 각 버전은 서로 다른 명령어를 지원한다.
Kubernetes가 저장한 모든 키를 조회하기 위한 명령어는 아래와 같다.
1
etcdctl get command
ETCDCTL V2
ETCDCTL 버전 2는 다음 명령을 지원한다.
1
2
3
4
5
etcdctl backup
etcdctl cluster-health
etcdctl mk
etcdctl mkdir
etcdctl set
ETCDCTL V3
ETCDCTL 버전 3는 다음 명령을 지원한다.
1
2
3
4
etcdctl snapshot save
etcdctl endpoint health
etcdctl get
etcdctl put
사용하고자 하는 API 버전을 설정하려면 환경 변수 ETCDCTL_API 값을 조정하면된다.
1
export ETCDCTL_API=3
API 버전이 설정되지 않으면 버전 2로 간주되며, 버전 3 명령은 동작하지 않는다.
또한 ETCDCTL이 ETCD API 서버에 인증할 수 있도록 인증서 파일 경로를 지정해야한다. 인증서 파일은 다음 경로의 etcd-master에서 사용할 수 있다.
1
2
3
--cacert /etc/kubernetes/pki/etcd/ca.crt
--cert /etc/kubernetes/pki/etcd/server.crt
--key /etc/kubernetes/pki/etcd/server.key
최종적으로 아래와 같이 인증서를 지정할 수 있다.
1
kubectl exec etcd-master -n kube-system -- sh -c "ETCDCTL_API=3 etcdctl get / --prefix --keys-only --limit=10 --cacert /etc/kubernetes/pki/etcd/ca.crt --cert /etc/kubernetes/pki/etcd/server.crt --key /etc/kubernetes/pki/etcd/server.key"
4. Kube-API Server
Kube-API Server의 역할
Kube-API Server는 쿠버네티스의 주요 관리 컴포넌트이며, 클러스터 내의 모든 작업을 조정한다. kubectl 명령을 실행하면, kube-apiserver에 도달한다.
기본 작동 방식
- 요청 수신 시 인증 및 검증 진행
- etcd 클러스터에서 데이터 검색
- 요청된 정보로 응답
파드 생성 예시 과정
- API 서버가 요청을 인증하고 검증
- 노드 할당 없이 파드 객체 생성
- etcd 서버에 정보 업데이트
- 사용자에게 파드 생성 완료 알림
- 스케줄러가 API 서버 모니터링 후 신규 파드 감지
- 스케줄러가 적절한 노드 식별 후 API 서버에 전달
- API 서버는 etcd 클러스터에 정보 업데이트
- API 서버는 해당 정보를 적절한 워커 노드의 kubelet에 전달
- kubelet이 노드에 파드 생성 및 컨테이너 런타임 엔진에 애플리케이션 이미지 배포 지시
- 작업 완료 후 kubelet이 API 서버에 상태 업데이트
- API 서버는 etcd 클러스터에 데이터 업데이트
주요 책임
- 요청 인증 및 검증
- etcd 데이터 저장소에서 데이터 검색 및 업데이트
- etcd 데이터 저장소와 직접 상호작용하는 유일한 컴포넌트
설치 및 구성 방법
kubeadmin 도구 사용 시
- 마스터 노드의 kube-system Namespace에 파드로 배포
- /etc/kubernetes/manifest 폴더에서 옵션 확인 가능
수동 설정 시
- Kubernetes 릴리스 페이지에서 바이너리 다운로드
- 마스터 노드에서 서비스로 구성
- /etc/systemd/system/kube-apiserver.service에서 옵션 확인 가능
구성 옵션
kube-apiserver는 여러 매개변수로 실행된다.
- 인증 관련 옵션
- 다른 구성 요소와의 통신 경로
- SSL/TLS 인증서
- etcd 서버 위치 및 연결 정보
5. Kube Controller Manager
Kube Controller Manager의 역할
Kube Controller Manager는 쿠버네티스에서 다양한 컨트롤러를 관리하는 컴포넌트이다.
컨트롤러는 시스템 내의 컴포넌트들의 상태를 지속적으로 모니터링하고, 전체 시스템을 Desired Status를 유지하기 위한 프로세스이다.
컨트롤러의 개념과 종류
컨트롤러는 구성 요소의 상태를 지속적으로 모니터링할 뿐만 아니라 상황을 개선하기 위한 필요한 조치를 한다. 컨트롤러의 종류는 여러가지가 있다.
1. 노드 컨트롤러
- 역할: 노드 상태 모니터링 및 필요한 조치 수행
- 특징:
- 5초마다 노드 상태 테스트
- 노드 heartbeat 중단 시 40초 후 ‘unreachable’로 표시
- ‘unreachable’ 표시 후 5분간 복구 대기
- 복구되지 않으면 해당 노드의 파드를 healthy 노드로 이동(레플리카셋의 경우)
2. 레플리케이션 컨트롤러
- 역할: 레플리카셋 상태 모니터링 및 원하는 수의 파드 유지
- 특징:
- 파드가 중단되면 새로운 파드 생성
- 항상 레플리카셋 내에서 설정된 수의 파드 유지
그외의 컨트롤러
- 배포(deployment)
- 서비스(services)
- Namespace(namespaces)
- 퍼시스턴트 볼륨(persistent volumes) 등…
설치 및 구성 방법
수동 설치 시
- Kubernetes 릴리스 페이지에서 Kube Controller Manager 다운로드
- 추출 후 서비스로 실행
- 다양한 옵션 설정 가능(노드 모니터 기간, 유예 기간, 퇴거 시간 초과 등)
- 기본적으로 모든 컨트롤러가 활성화되지만, 선택적으로 일부만 활성화 가능
kubeadmin 도구 사용 시
- 마스터 노드의 kube-system Namespace에 파드로 배포됨
- /etc/kubernetes/manifest 폴더의 파드 정의 파일에서 옵션 확인 가능
비 kubeadmin 설정 시
- 서비스 디렉토리에서 Kube Controller Manager 서비스 확인 가능
- 마스터 노드에서 프로세스 목록을 확인하고 Kube Controller Manager 검색으로 구성 확인 가능
6. Kube-Scheduler
Kube-Scheduler의 역할
Kube-Scheduler는 쿠버네티스에서 파드를 노드에 할당하는 역할을 담당한다.
스케줄러는 어떤 파드가 어떤 노드에 배치될지 결정만 할 뿐, 실제로 파드를 노드에 배치하는 작업은 kubelet이 수행한다.
스케줄러가 필요한 이유
다양한 크기와 특성을 가진 노드와 파드가 있을 때, 적절한 배치를 하기 위해서이다. 이때 스케줄러가 고려하는 점은 아래와 같다.
- 노드가 파드의 리소스 요구사항을 충족할 수 있는지 확인
- 특정 목적지나 용도에 맞는 노드에 파드를 배치
- 여러 리소스 요구사항을 가진 파드들을 고려하여 배치
스케줄링 과정
스케줄러는 파드를 노드에 할당할 때 두 단계의 과정이 있다.
1. 필터링 단계 (Filtering)
- 파드 프로필에 맞지 않는 노드들을 필터링
- 예: 파드가 요청한 CPU와 메모리 리소스가 충분하지 않은 노드 제외
2. 순위 결정 단계 (Scoring)
- 필터링 후 남은 노드들에 점수 부여 (0-10 스케일)
- 우선순위 기능을 사용하여 최적의 노드 선택
- 예: 파드 배치 후 남는 리소스 양, 노드의 현재 부하 등을 계산
예를 들어, 만약 두 노드 중 하나를 선택해야 할 경우, 파드 배치 후 더 많은 CPU가 남는 노드(6개)가 다른 노드(2개)보다 높은 점수를 받아 선택됨
추가 스케줄링 기능
- 리소스 요구사항 및 제한 설정
- Taints, Tolerations
- Node Selectors
- Affinity Rules
설치 및 구성 방법
수동 설치
- Kubernetes 릴리스 페이지에서 kube-scheduler 바이너리 다운로드
- 추출 후 서비스로 실행
- 스케줄러 구성 파일 지정
kubeadm 도구 사용 시
- 마스터 노드의 kube-system Namespace에 파드로 배포
- /etc/kubernetes/manifest/ 폴더의 파드 정의 파일에서 옵션 확인 가능
- 마스터 노드에서 프로세스 목록을 확인하고 kube-scheduler 검색으로 구성 확인 가능
7. Kubelet
Kubelet의 역할
Kubelet은 쿠버네티스의 워커 노드를 실제로 구동하도록 하는 에이전트 역할이다. 주요 책임은 아래와 같다.
- 노드 등록: 노드를 쿠버네티스 클러스터에 등록
- 컨테이너 관리: 노드에 컨테이너 또는 파드를 로드하는 지침을 받으면 컨테이너 런타임 엔진에 필요한 이미지를 가져와 인스턴스를 실행하도록 요청
- 모니터링: 파드와 그 안의 컨테이너 상태를 지속적으로 모니터링
- 보고: 노드와 컨테이너의 상태를 kube-API 서버에 정기적으로 보고
Kubelet 설치 방법
kubeadm 도구로 클러스터를 배포할 때, 다른 컴포넌트와 달리 kubelet은 자동으로 설치되지 않는다.
수동 설치
- 워커 노드에 수동으로 kubelet 설치 필요
- 설치 프로그램 다운로드 및 추출
- 서비스로 실행
8. Kube-Proxy
Kube-Proxy의 역할
Kube-Proxy는 각 노드에서 실행되는 프로세스로, 서비스와 관련된 네트워킹을 관리한다.
클러스터 내 통신 방식
- 쿠버네티스 클러스터 내에서는 모든 파드가 다른 모든 파드에 도달할 수 있다. 이 때 클러스터에 파드 네트워킹 솔루션(Calico, Flannel, etc …)을 사용한다.
- 파드 네트워크는 클러스터의 내부 가상 네트워크이다.
파드 간 직접 통신의 문제점
파드 IP는 변경될 수 있어 안정적이지 않다.
이를 해결하기 위해 서비스를 생성하여 애플리케이션을 클러스터 전체에 노출시킨다. 서비스에는 고유한 IP 주소가 할당된다.
그리고 외부에서는 서비스 이름(예: ‘UserAPI’)을 사용하여 접근이 가능하다.
서비스의 구현 방식
서비스는 실제 존재하는 물리적 객체가 아닌 가상 컴포넌트이다. 파드처럼 컨테이너가 아니며 인터페이스나 활성 리스닝 프로세스가 없다. 또한 쿠버네티스 메모리 내에만 존재한다.
Kube-Proxy의 역할
Kube-Proxy는 다음과 같은 작업을 수행한다.
- 새로운 서비스를 감시
- 새 서비스가 생성될 때마다 각 노드에 적절한 규칙 생성
- 해당 서비스로 향하는 트래픽을 백엔드 파드로 전달
구현 방법:
- 주로 iptables 규칙을 사용
- 예: 서비스 IP(10.96.0.12)로 향하는 트래픽을 실제 파드 IP(10.32.0.15)로 전달하는 규칙 생성
설치 및 구성 방법
수동 설치
- Kubernetes 릴리스 페이지에서 kube-proxy 바이너리 다운로드
- 추출 후 서비스로 실행
kubeadm 도구 사용 시
- Kube-Proxy를 각 노드의 파드로 배포
- DaemonSet으로 배포되어 클러스터의 각 노드에 항상 하나의 파드가 배포됨
9. Kubernetes Pods
Pod의 개념과 역할
쿠버네티스에서 Pod는 애플리케이션의 배포 단위로, 다음과 같은 특징을 가진다.
- Pod는 쿠버네티스에서 생성할 수 있는 가장 작은 객체
- 컨테이너는 직접 워커 노드에 배포되지 않고, Pod로 캡슐화되어 배포됨
- Pod는 애플리케이션의 단일 인스턴스를 나타냄
애플리케이션 확장
애플리케이션을 확장하는 방법
- 사용자 증가 시: 같은 Pod 내에 컨테이너를 추가하는 것이 아니라, 동일한 애플리케이션의 새 인스턴스가 포함된 새로운 Pod를 생성
- 스케일 아웃: 새로운 Pod 생성
- 스케일 다운: 기존 Pod 삭제
- 노드 용량 부족 시: 클러스터에 새 노드를 추가하고 해당 노드에 추가 Pod 배포 가능
다중 컨테이너 Pod
Pod는 일반적으로 컨테이너와 1:1 관계를 가지지만, 다음과 같은 경우 하나의 Pod에 여러 컨테이너를 포함할 수 있다.
- 주 애플리케이션을 지원하는 헬퍼 컨테이너가 필요한 경우
- 헬퍼 컨테이너가 애플리케이션 컨테이너와 생명주기를 공유해야 하는 경우 (함께 생성되고 함께 소멸)
- 컨테이너 간 직접 통신이 필요한 경우 (localhost로 참조 가능)
- 저장 공간 공유가 필요한 경우
Pod vs 단순 Docker 컨테이너
Docker만 사용할 경우의 문제점
- 여러 컨테이너 간 관계 수동 관리 필요
- 네트워크 연결성 직접 구성 필요 (링크, 사용자 정의 네트워크)
- 공유 볼륨 수동 생성 및 관리 필요
- 컨테이너 상태 수동 모니터링 필요
- 관련 컨테이너의 수명 주기 수동 관리 필요
Pod 사용의 이점
- 쿠버네티스가 관련 컨테이너 그룹 자동 관리
- 같은 Pod 내 컨테이너는 자동으로 동일한 저장소, 네트워크 Namespace 공유
- 컨테이너 생명주기 자동 관리 (함께 생성되고 함께 소멸)
- 애플리케이션 아키텍처 변경과 확장에 대비 가능
Pod 배포 방법
Pod를 자동으로 생성하고 지정된 Docker 이미지의 인스턴스를 배포하는 것으로 이미지는 Docker Hub 또는 Private 저장소에서 가져올 수 있다.
1
kubectl run nginx --image=nginx
Pod 목록 확인
1
kubectl get pods
10. YAML로 Pod 생성하기
Kubernetes YAML 파일의 구조
쿠버네티스는 파드, 레플리카, Deployment, 서비스 등의 객체 생성을 위해 yaml 파일을 사용한다. yaml에는 항상 네 가지 최상위(root level) 필드를 포함해야한다.
- apiVersion: 사용하는 쿠버네티스 API 버전
- kind: 생성하려는 객체 유형
- metadata: 객체에 대한 메타데이터(이름, 레이블 등)
- spec: 객체에 대한 추가 정보와 구성
각 필드 자세히 알아보기
1. apiVersion
생성하려는 객체에 따라 적절한 API 버전을 사용해야 한다.
- 파드의 경우:
v1
- 기타 가능한 값:
apps/v1beta
,extensions/v1beta
등
2. kind
생성하려는 객체 유형을 지정한다.
Pod
: 단일 파드- 기타 가능한 값:
ReplicaSet
,Deployment
,Service
등
3. metadata
객체에 대한 데이터로, 사전(dictionary) 형태로 제공된다.
name
: 객체의 이름(문자열)labels
: 객체 식별 및 분류를 위한 키-값 쌍의 사전
메타데이터는 YAML의 들여쓰기 규칙을 따라야 한다.
- 같은 수준의 속성은 동일한 들여쓰기를 가져야 함
- 자식 속성은 부모보다 더 많은 들여쓰기를 가져야 함
레이블의 중요성
- 여러 파드를 그룹화하고 필터링하는 데 사용
- 예: 프론트엔드, 백엔드, 데이터베이스 등으로 파드 분류한다.
4. spec
객체에 따라 다른 형식을 가지며, 해당 객체에 대한 추가 정보를 제공한다.
파드의 경우 spec
섹션에는 컨테이너 정보가 포함된다.
containers
: 파드 내 컨테이너 목록(배열)- 각 컨테이너는
name
과image
속성 필요 -
기호는 목록의 첫 항목을 나타냄
- 각 컨테이너는
Pod 정의 YAML 예시
1
2
3
4
5
6
7
8
9
10
11
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
app: nginx
tier: frontend
spec:
containers:
- name: nginx
image: nginx
Pod 생성 및 관리 명령어
YAML 파일로 파드 생성
1
kubectl create -f pod-definition.yaml
파드 목록 확인
1
kubectl get pods
파드 상세 정보 확인
1
kubectl describe pod myapp-pod
11. 레플리카
컨트롤러의 역할
컨트롤러는 쿠버네티스의 ‘두뇌’로, 다음과 같은 역할을 한다.
- 쿠버네티스 객체를 모니터링하는 프로세스
- Desired Status를 유지하기 위해 필요한 조치 수행
레플리케이션 컨트롤러(Replication Controller)
레플리케이션 컨트롤러는 지정된 수의 파드가 항상 실행되도록 보장하는 컨트롤러이다.
필요한 이유
- 고가용성 보장: 파드가 실패할 경우에도 애플리케이션이 계속 실행되도록 함
- 부하 분산: 여러 파드에 걸쳐 로드를 분산하여 애플리케이션 확장
- 안정성: 단일 파드만 실행 중이더라도 장애 시 자동으로 새 파드 생성
레플리케이션 컨트롤러 vs 레플리카셋
- 레플리케이션 컨트롤러: 이전 기술
- 레플리카셋: 새로운 권장 방법
- 기본 기능은 동일하지만 레플리카셋이 더 많은 기능 제공
레플리케이션 컨트롤러 정의 파일 작성
YAML 파일 구조:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
apiVersion: v1
kind: ReplicationController
metadata:
name: myapp-rc
labels:
app: myapp
type: front-end
spec:
replicas: 3
template:
metadata:
name: myapp-pod
labels:
app: myapp
type: front-end
spec:
containers:
- name: nginx-container
image: nginx
주요 구성 요소:
- apiVersion: v1
- kind: ReplicationController
- metadata: 컨트롤러의 이름과 레이블
- spec:
- replicas: 유지할 파드 수
- template: 파드 템플릿 정의
레플리카셋(ReplicaSet) 정의 파일 작성
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: myapp-replicaset
labels:
app: myapp
type: front-end
spec:
replicas: 3
selector:
matchLabels:
app: myapp
template:
metadata:
name: myapp-pod
labels:
app: myapp
type: front-end
spec:
containers:
- name: nginx-container
image: nginx
주요 차이점
- apiVersion: apps/v1
- kind: ReplicaSet
- selector: 필수 항목으로, 어떤 파드가 레플리카셋에 속하는지 지정
셀렉터(Selector)의 중요성
레플리카셋이 어떤 파드를 관리할지 결정하는 메커니즘
- 기존에 생성된 파드도 관리 가능
- 레이블을 기준으로 파드를 식별
- 레플리케이션 컨트롤러에서는 선택적, 레플리카셋에서는 필수
레플리카셋 스케일링 방법
- 정의 파일 수정 후 업데이트
- YAML 파일에서 replicas 값 변경
kubectl replace -f replicaset-definition.yaml
실행
- scale 명령 사용:
kubectl scale --replicas=6 -f replicaset-definition.yaml
- 또는
kubectl scale --replicas=6 replicaset myapp-replicaset
주요 명령어
- 생성:
kubectl create -f replicaset-definition.yaml
- 조회:
kubectl get replicaset
- 삭제:
kubectl delete replicaset myapp-replicaset
- 교체/업데이트:
kubectl replace -f replicaset-definition.yaml
- 스케일링:
kubectl scale --replicas=6 -f replicaset-definition.yaml
12. Deployment
Deployment의 필요성
Deployment가 제공하는 기능은 아래와 같다.
- 다수의 인스턴스: 고가용성을 위한 여러 인스턴스
- 무중단 업그레이드: 새로운 버전의 애플리케이션을 배포할 때 서비스 중단 없이 진행
- 롤링 업데이트: 모든 인스턴스를 한 번에 업그레이드하지 않고 순차적으로 업데이트
- 롤백 기능: 업그레이드 중 오류 발생 시 이전 버전으로 되돌릴 수 있는 기능
- 일괄 변경: 여러 변경사항(버전 업그레이드, 스케일링, 리소스 할당 등)을 일시 중지했다가 한 번에 적용
쿠버네티스 오브젝트 계층 구조
- 파드(Pod): 애플리케이션의 단일 인스턴스
- 레플리카셋(ReplicaSet): 여러 파드의 복제본 관리
- Deployment(Deployment): 레플리카셋을 관리하며 롤링 업데이트, 롤백 등 상위 기능 제공
Deployment는 상위 계층 구조의 쿠버네티스 오브젝트로, 레플리카셋을 관리하여 더 복잡한 배포 시나리오를 처리한다.
Deployment 정의 파일 작성
Deployment 정의 파일은 레플리카셋 정의 파일과 유사하지만, kind
값이 ‘Deployment’로 변경된다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp-deployment
labels:
app: myapp
type: front-end
spec:
replicas: 3
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
type: front-end
spec:
containers:
- name: nginx-container
image: nginx
Deployment 생성 및 확인
Deployment를 생성하면 자동으로 레플리카셋이 생성되고, 레플리카셋은 파드를 생성한다.
Deployment 생성:
kubectl create -f deployment-definition.yaml
Deployment 확인:
kubectl get deployments
레플리카셋 확인:
kubectl get replicaset
파드 확인:
kubectl get pods
모든 오브젝트 한번에 확인:
kubectl get all
Deployment를 생성하면 다음과 같은 명명 규칙으로 객체가 생성된다.
- Deployment: myapp-deployment
- 레플리카셋: myapp-deployment-[해시값]
- 파드: myapp-deployment-[해시값]-[해시값]
14. Services
Service의 역할
애플리케이션 내부 및 외부의 다양한 구성 요소 간 통신을 가능하게 하는 역할이다.
- 애플리케이션 연결: 다양한 애플리케이션 구성 요소를 서로 연결
- 외부 접근 제공: 최종 사용자가 애플리케이션에 접근할 수 있도록 함
- 마이크로서비스 간 느슨한 결합: 서비스를 통해 마이크로서비스 아키텍처를 효과적으로 구현
서비스의 필요성: 외부 통신 문제
쿠버네티스 노드와 파드 간 네트워크 구성 예시:
- 쿠버네티스 노드 IP: 192.168.1.2
- 사용자 노트북 IP: 192.168.1.10
- 파드 내부 네트워크 대역: 10.244.0.0
- 파드 IP: 10.244.0.2
이 경우 외부 사용자(노트북)에서는 파드의 IP(10.244.0.2)로 직접 접근할 수 없다. 노드에 SSH로 접속하면 파드에 접근할 수 있지만, 좋은 방법은 아니다.
따라서, 노드 IP를 통해 파드에 접근할 수 있는 방법이 필요하며, 이 역할을 쿠버네티스 서비스가 담당합니다.
서비스 유형
- NodePort: 노드의 특정 포트를 외부로 노출시켜 파드에 접근할 수 있게 함
- ClusterIP: 클러스터 내부에 가상 IP를 생성하여 다른 서비스 간 통신을 가능하게 함
- LoadBalancer: 지원되는 클라우드 제공자에서 로드 밸런서를 프로비저닝하여 서비스를 외부에 노출
NodePort
NodePort 서비스에서는 세 가지 포트가 사용된다.
- 타겟포트(TargetPort): 파드에서 애플리케이션이 실제로 실행되는 포트(예: 80)
- 포트(Port): 서비스 자체의 포트(예: 80) - 클러스터 내부에서 서비스에 접근할 때 사용
- 노드포트(NodePort): 노드에서 외부로 노출되는 포트(30000-32767 범위, 예: 30008)
- 서비스는 클러스터 내에서 자체 IP(ClusterIP)를 가진다.
NodePort 서비스 정의 파일 작성
1
2
3
4
5
6
7
8
9
10
11
12
apiVersion: v1
kind: Service
metadata:
name: myapp-service
spec:
type: NodePort
ports:
- targetPort: 80 # 파드의 포트 (생략 시 port와 동일)
port: 80 # 서비스 포트 (필수 필드)
nodePort: 30008 # 노드 포트 (생략 시 30000-32767 범위에서 자동 할당)
selector:
app: myapp # 해당 레이블을 가진 파드를 선택
주요 특징:
- selector: 서비스가 연결될 파드를 지정하는 레이블 선택자
- ports: 포트 매핑 설정 (배열 형태로 여러 포트 매핑 가능)
다중 파드와 서비스
- 자동 선택: 서비스는 selector에 지정된 레이블과 일치하는 모든 파드를 자동으로 선택
- 로드 밸런싱: 서비스는 선택된 파드 간에 트래픽을 분산(기본적으로 무작위 알고리즘 사용)
- 멀티 노드 지원: 파드가 여러 노드에 분산되어 있어도 서비스는 모든 노드에서 동일한 노드포트를 통해 접근 가능
서비스의 유연성
- 파드가 추가되거나 제거될 때 서비스는 자동으로 업데이트됨
- 한 번 생성된 서비스는 추가 구성 변경이 거의 필요 없음
- 클러스터의 어떤 노드 IP:노드포트를 통해서도 서비스에 접근이 가능하다.
15. Services: ClusterIP
마이크로서비스 환경에서의 통신 문제
풀스택 웹 애플리케이션은 일반적으로 다양한 종류의 파드로 구성된다.
- 프론트엔드 웹 서버를 실행하는 파드 그룹
- 백엔드 서버를 실행하는 파드 그룹
- Redis와 같은 키-값 저장소를 실행하는 파드 그룹
- MySQL과 같은 데이터베이스를 실행하는 파드 그룹
파드간의 통신이 필요하지만, 파드의 IP 주소는 여러 문제가 있다.
- 파드 IP 주소는 정적이지 않음 (파드가 재생성되면 IP가 변경됨)
- 동일한 역할을 하는 여러 파드 중 어떤 파드와 통신할지 결정하기 어렵다.
ClusterIP 서비스의 역할
ClusterIP 서비스는 위 문제를 해결하기 위한 솔루션이다.
- 동일한 역할을 하는 파드들을 그룹화
- 그룹화된 파드들에 접근하기 위한 단일 인터페이스(IP 및 이름) 제공
- 요청을 그룹 내 파드들에게 무작위로 분산
마이크로서비스 아키텍처 구현
ClusterIP 서비스를 사용하면 마이크로서비스 기반 애플리케이션을 쿠버네티스 클러스터에 쉽게 배포할 수 있다.
- 각 서비스 계층은 다른 계층과의 통신에 영향을 주지 않고 독립적으로 확장 가능
- 서비스 간 통신은 IP가 아닌 서비스 이름으로 이루어짐
- 서비스 내부의 파드 변경/교체가 다른 서비스에 영향을 주지 않음
ClusterIP 서비스 정의 파일 작성
1
2
3
4
5
6
7
8
9
10
11
12
apiVersion: v1
kind: Service
metadata:
name: backend
spec:
type: ClusterIP # 기본값이므로 생략 가능
ports:
- targetPort: 80 # 백엔드 파드가 노출된 포트
port: 80 # 서비스가 노출될 포트
selector:
app: myapp # 이 레이블을 가진 파드들이 서비스의 대상이 됨
type: backend
주요 특징
- 서비스 타입: ClusterIP(명시적으로 지정하지 않아도 기본값임)
- 포트 정의:
- targetPort: 파드에서 애플리케이션이 실행되는 포트
- port: 서비스가 노출되는 포트
- 셀렉터: 대상 파드를 지정하는 레이블
서비스 생성 및 접근
서비스 생성:
kubectl create -f service-definition.yaml
서비스 상태 확인:
kubectl get services
서비스 접근 방법:
- 클러스터 내의 다른 파드에서 서비스 IP 사용
- 또는 서비스 이름으로 접근 (쿠버네티스 DNS가 서비스 이름을 IP로 해석)
ClusterIP 서비스는 쿠버네티스 클러스터 내부에서만 접근 가능하며, 외부에서는 접근할 수 없다.
따라서 외부 접근이 필요한 경우 NodePort나 LoadBalancer 타입의 서비스를 사용해야 한다.
16. Services: LoadBalancer
NodePort 서비스의 문제
NodePort 서비스는 외부에서 애플리케이션에 접근할 수 있게 해주지만, 다음과 같은 문제가 있다.
- 여러 접근 지점: 클러스터의 모든 노드 IP와 지정된 포트를 통해 접근 가능
- URL 복잡성: 최종 사용자에게 여러 IP:PORT 조합을 제공해야 함
- 사용자 경험: 사용자들은 일반적으로
votingapp.com
같은 단일 URL로 접근하기를 원함
예를 들어 4개의 노드로 구성된 클러스터에서
- 투표 애플리케이션: 노드1 IP:30001, 노드2 IP:30001, 노드3 IP:30001, 노드4 IP:30001
- 결과 애플리케이션: 노드1 IP:30002, 노드2 IP:30002, 노드3 IP:30002, 노드4 IP:30002
이 경우 최종 사용자에게 8개의 AP를 제공해야 하는 문제가있다.
로드 밸런서 구현 방법
이 문제를 해결하기 위한 방법으로 두 가지가 있다.
- 수동 로드 밸런서 구성:
- 별도의 VM 생성
- HA Proxy, Nginx 등의 로드 밸런서 설치 및 구성
- 트래픽을 클러스터 노드로 라우팅하도록 설정
- 단점: 설정 및 유지 관리가 번거로움
- 클라우드 제공업체 로드 밸런서 활용:
- 지원되는 클라우드 플랫폼(GCP, AWS, Azure 등)의 네이티브 로드 밸런서 사용
- 쿠버네티스는 이러한 클라우드 제공업체의 로드 밸런서와 통합 지원
LoadBalancer 서비스 타입 사용
클라우드 환경에서는 간단히 서비스 타입을 LoadBalancer로 설정하여 클라우드 제공업체의 로드 밸런서를 자동으로 프로비저닝할 수 있다.
1
2
3
4
5
6
7
8
9
10
11
apiVersion: v1
kind: Service
metadata:
name: voting-app-service
spec:
type: LoadBalancer
ports:
- port: 80
targetPort: 80
selector:
app: voting-app
주요 특징 및 참고사항
- 클라우드 통합: GCP, AWS, Azure 등 지원되는 클라우드 환경에서만 완전히 작동
- 외부 로드 밸런서: 클라우드 제공업체의 로드 밸런서가 자동으로 프로비저닝됨
- 단일 엔드포인트: 사용자는 로드 밸런서의 단일 IP 또는 DNS로 애플리케이션에 접근
- 비클라우드 환경: 지원되지 않는 환경(예: VirtualBox)에서는 NodePort처럼 작동하며 외부 로드 밸런서는 생성되지 않음
17. Namespace
Namespace의 개념
Namespace를 이해하기 위한 비유
- 두 명의 ‘Mark’라는 이름을 가진 소년이 있을 때, 성(Smith와 Williams)으로 구분
- 각 가정(Smith 가족, Williams 가족)은 자체적인 규칙과 자원을 가진다.
- 가족 내에서는 이름만으로 서로를 부르지만, 외부에서는 성까지 포함하여 부름
쿠버네티스에서 Namespace는 이와 유사한 분리 개념을 제공한다.
- 클러스터 내의 리소스들을 논리적으로 분리
- 같은 Namespace 내에서는 단순 이름으로 리소스 참조 가능
- 다른 Namespace의 리소스 참조 시 Namespace 이름 필요
기본 Namespace
쿠버네티스 클러스터는 시작 시 자동으로 다음 Namespace를 생성한다.
- default: 특별히 Namespace를 지정하지 않은 모든 리소스가 생성되는 기본 Namespace
- kube-system: 쿠버네티스 시스템에서 생성한 객체를 위한 Namespace
- 네트워킹, DNS 서비스 등 내부 목적의 파드와 서비스 포함
- 사용자의 실수로 이러한 중요 시스템 리소스가 삭제되는 것을 방지
- kube-public: 모든 사용자가 접근할 수 있는 리소스를 위한 Namespace
Namespace의 필요성
소규모 환경에는 기본 Namespace만으로 충분할 수 있지만, 다음과 같은 상황에서 별도의 Namespace가 필요하다.
- 환경 분리:
- 개발(Dev)과 운영(Production) 환경을 같은 클러스터에서 분리
- 실수로 운영 리소스를 변경하는 일 방지
- 리소스 할당:
- 각 Namespace별로 리소스 할당량 지정 가능
- 특정 Namespace가 클러스터 전체 리소스를 독점하는 것 방지
- 접근 제어:
- Namespace별로 다른 권한 정책 적용 가능
- 팀이나 사용자별로 권한 분리
Namespace 간 통신
- 같은 Namespace 내 통신:
- 서비스 이름만으로 접근 가능
- 예:
db-service
- 다른 Namespace 접근:
- 전체 도메인 이름(FQDN) 사용
- 형식:
<서비스-이름>.<Namespace>.svc.cluster.local
- 예:
db-service.dev.svc.cluster.local
FQDN 구조:
cluster.local
: 쿠버네티스 클러스터의 기본 도메인svc
: 서비스 하위 도메인<Namespace>
: Namespace 이름<서비스-이름>
: 서비스 자체 이름
Namespace 관련 명령어
- 특정 Namespace의 리소스 확인
1
kubectl get pods --namespace=kube-system
- 리소스 생성 시 Namespace 지정
1
kubectl create -f pod-definition.yaml --namespace=dev
- 정의 파일에 Namespace 포함
1 2 3 4 5 6
apiVersion: v1 kind: Pod metadata: name: myapp-pod namespace: dev spec:
- 새 Namespace 생성 (정의 파일 사용)
1
2
3
4
apiVersion: v1
kind: Namespace
metadata:
name: dev
실행: kubectl create -f namespace-definition.yaml
- 새 Namespace 생성 (직접 명령)
1
kubectl create namespace dev
- 현재 컨텍스트의 기본 Namespace 변경
1
kubectl config set-context $(kubectl config current-context) --namespace=dev
- 모든 Namespace의 리소스 확인
1
kubectl get pods --all-namespaces
리소스 할당량 설정
Namespace의 리소스 사용을 제한하기 위한 ResourceQuota
1
2
3
4
5
6
7
8
9
10
11
12
apiVersion: v1
kind: ResourceQuota
metadata:
name: compute-quota
namespace: dev
spec:
hard:
pods: "10"
requests.cpu: "4"
requests.memory: 5Gi
limits.cpu: "10"
limits.memory: 10Gi
18. 쿠버네티스 관리 방법: 명령적(Imperative) vs 선언적(Declarative) 접근법
명령적(Imperative) vs 선언적(Declarative) 접근법 개념
- 명령적 접근법: 택시 기사에게 목적지까지 가는 모든 경로를 상세히 지시
- “B 거리로 우회전하고, C 거리로 좌회전하고, D 거리로 좌회전 후 우회전하여 집에 도착”
- 무엇을 해야 하는지와 어떻게 해야 하는지 모두 지정
- 선언적 접근법: 우버 앱에서 최종 목적지만 지정
- “톰의 집으로 가주세요”
- 무엇을 원하는지만 지정하고, 어떻게 할지는 시스템이 결정
인프라에서의 명령적 vs 선언적 접근법
명령적 인프라 예시:
- VM 생성, NGINX 설치, 포트 8080 구성, 웹 파일 경로 설정, Git에서 소스코드 다운로드, 서버 시작 등 단계별 지침
- 필요한 것과 그것을 얻는 방법 모두 지정
선언적 인프라 예시:
- “Web Server”라는 VM에 NGINX가 설치되어 있고, 포트 8080이 설정되어 있으며, 웹 파일 경로와 소스코드 위치가 정의된 상태가 필요하다고 선언
- 필요한 상태만 지정하면 시스템이 알아서 구현
명령적 접근법의 한계
- 중간 단계 실패 처리의 복잡성:
- 일부 단계만 실행된 경우 대응이 어려움
- 이미 존재하는 리소스 확인 및 조건부 처리 필요
- 업데이트 시나리오 처리:
- VM이 이미 존재할 경우 어떻게 할지?
- 소프트웨어 버전 업그레이드 시 모든 단계 재정의 필요
- 복잡한 로직 필요:
- 시스템이 현재 상태를 알고 필요한 변경만 적용하는 지능이 필요
쿠버네티스에서의 관리 방법
1. 명령적 명령 (Imperative Commands)
kubectl run
: 파드 생성kubectl create deployment
: 디플로이먼트 생성kubectl expose
: 서비스 생성kubectl edit
: 기존 객체 편집kubectl scale
: 디플로이먼트/레플리카셋 스케일링kubectl set image
: 이미지 업데이트
장점:
- 빠르게 객체 생성/수정 가능
- YAML 파일 작성 필요 없음
- 인증 시험에서 유용
단점:
- 기능 제한적
- 복잡한 구성에는 긴 명령 필요
- 명령 이력만 존재, 다른 사람이 추적하기 어려움
- 복잡한 환경에서 관리 어려움
2. 명령적 객체 구성 (Imperative Object Configuration)
- 객체 구성 파일(YAML) 작성 후 명령어로 적용:
kubectl create -f
: 객체 생성kubectl replace -f
: 객체 업데이트kubectl delete -f
: 객체 삭제
장점:
- 구성 파일 버전 관리 가능(Git 등)
- 변경 검토 및 승인 프로세스 적용 가능
- 구성 변경 내역 추적 가능
단점:
- 아직 명령적 방식 (어떻게 해야 할지 지시)
- 객체 존재 여부 확인 필요
- 객체가 없으면 create 실패, 있으면 replace 실패
객체 수정 방법:
kubectl edit
사용: 라이브 객체 직접 수정, 로컬 파일과 불일치 발생- 로컬 구성 파일 수정 후
kubectl replace
: 변경 내역 추적 가능 - 강제 교체:
kubectl replace --force -f file.yaml
3. 선언적 객체 구성 (Declarative Object Configuration)
kubectl apply -f
: 파일이나 디렉토리의 모든 객체 생성/업데이트- 하나의 명령으로 생성, 업데이트, 관리 가능
장점:
- 객체가 존재하지 않으면 생성, 존재하면 업데이트
- 오류 발생 가능성 최소화
- 여러 파일 또는 디렉토리에 적용 가능
- 시스템이 현재 상태와 원하는 상태를 비교하여 필요한 변경만 적용
사용 예:
1
2
3
4
5
6
7
8
# 단일 파일 적용
kubectl apply -f config.yaml
# 디렉토리 내 모든 파일 적용
kubectl apply -f configs/
# 변경 사항을 로컬 파일에 반영 후 재적용
kubectl apply -f config.yaml
19. kubectl apply 명령어의 작동 방식
kubectl apply 명령어의 내부 동작
kubectl apply 명령어는 쿠버네티스에서 선언적 방식으로 객체를 관리하는 도구이다.
이 명령어는 아래 세 가지 구성 정보를 비교하여 어떤 변경이 필요한지 결정한다.
- 로컬 구성 파일: 사용자가 작성하고 관리하는 YAML 파일
- 라이브 객체 구성: 쿠버네티스 클러스터에 실제로 저장된 객체 정의
- 마지막 적용 구성: 이전에 apply 명령어로 적용된 구성의 스냅샷
객체 생성 및 업데이트 프로세스
객체 생성 시:
- 객체가 존재하지 않으면 생성됨
- 쿠버네티스는 라이브 객체 구성을 생성 (추가 상태 필드 포함)
- 로컬 구성 파일의 내용은 JSON 형식으로 변환되어 “마지막 적용 구성”으로 저장됨
객체 업데이트 시:
- 세 가지 구성(로컬, 라이브, 마지막 적용)을 모두 비교
- 로컬 파일에서 값이 변경된 경우 라이브 구성도 업데이트됨
- 변경 후 “마지막 적용 구성”도 항상 최신 상태로 업데이트됨
필드 삭제 처리 방식
마지막 적용 구성의 중요한 역할은 필드 삭제 감지에 있다.
- 로컬 파일에서 필드가 삭제되고 마지막 적용 구성에는 존재하는 경우
- 해당 필드는 라이브 구성에서도 제거됨
- 예: 이전에 있던 “type” 레이블이 로컬 파일에서 삭제된 경우
- 로컬 파일에 없지만 마지막 적용 구성에도 없는 경우
- 라이브 구성에서 해당 필드는 그대로 유지됨
마지막 적용 구성의 저장 위치
“마지막 적용 구성”은 쿠버네티스 클러스터의 라이브 객체 구성 내에 주석(annotation)으로 저장된다.
- 주석 이름:
kubectl.kubernetes.io/last-applied-configuration
- JSON 형식으로 저장됨
주의사항
- 이 마지막 적용 구성 저장 메커니즘은 kubectl apply 명령을 사용할 때만 적용됨
- kubectl create나 replace 명령은 이러한 주석을 저장하지 않음
- 따라서 명령적 접근법(create, replace)과 선언적 접근법(apply)을 혼합해서 사용하지 않는 것이 중요하다.
apply 명령어를 사용하기 시작했다면, 이후의 모든 변경에도 apply 명령어를 계속 사용하는 것을 권장한다.