CKA를 향하여 (롤링 업데이트와 롤백, 컨테이너의 명령어와 인수, 쿠버네티스에서 환경 변수 설정하기, 컨피그 맵, 시크릿, 시크릿 데이터의 저장 암호화, 멀티 컨테이너 파드, 멀티 컨테이너 파드 디자인 패턴, 오토스케일링, 수평 파드 오토스케일러, 파드 리소스의 동적 리사이징, 수직 파드 오토스케일러)
1. 롤링 업데이트와 롤백
롤아웃과 배포 버전 관리
Kubernetes에서 Deployment를 처음 생성하면 롤아웃(Rollout)이 트리거된다. 이 과정에서 새로운 배포 개정(revision)이 생성된다. 처음 만들어진 개정을 “revision 1”이라고 한다.
애플리케이션이 업그레이드될 때(컨테이너 버전이 업데이트될 때) 새로운 롤아웃이 트리거되고 “revision 2”와 같은 새로운 배포 개정이 생성된다. 이러한 개정 관리 시스템은 배포에 대한 변경 사항을 추적하고 필요한 경우 이전 버전으로 롤백할 수 있게 한다.
롤아웃 상태 확인 명령어
1
kubectl rollout status deployment [배포-이름]
롤아웃 기록 확인 명령어
1
kubectl rollout history deployment [배포-이름]
배포 전략
Kubernetes에서는 두 가지 주요 배포 전략이 있다.
Recreate 전략
모든 기존 인스턴스를 한 번에 종료하고 새로운 버전의 인스턴스를 생성한다.
- 문제점: 기존 버전이 종료된 후 새 버전이 시작되기 전까지 애플리케이션이 다운되어 사용자가 접근할 수 없다.
- 이 전략은 Kubernetes의 기본 전략이 아니다.
Rolling Update 전략
이전 버전을 하나씩 종료하고 새 버전을 하나씩 시작한다.
- 장점: 애플리케이션이 다운타임 없이 계속 실행된다.
- 이 전략이 Kubernetes의 기본 배포 전략이다.
배포 업데이트 방법
배포를 업데이트하는 두 가지 주요 방법이 있다.
배포 정의 파일 수정 후 적용
1
kubectl apply -f [배포-파일.yaml]
kubectl set 명령어 사용
1
kubectl set image deployment [배포-이름] [컨테이너-이름]=[새-이미지]
주의: 이 방법으로 업데이트하면 배포 정의 파일과 실제 구성 간에 차이가 발생할 수 있다. 향후 같은 정의 파일로 변경 작업을 할 때 문제가 발생할 수 있으므로 주의해야한다.
배포 롤백 방법
배포 후 문제가 발생하면 이전 버전으로 롤백할 수 있다.
1
kubectl rollout undo deployment [배포-이름]
이 명령은 새 복제본 세트(ReplicaSet)의 파드를 제거하고 이전 복제본 세트의 파드를 다시 활성화하여 애플리케이션을 이전 상태로 되돌린다.
배포의 내부 작동 원리
- 새 배포가 생성되면 자동으로 복제본 세트(ReplicaSet)가 생성된다.
- 이 복제본 세트는 필요한 수의 파드를 생성한다.
- 애플리케이션을 업그레이드하면 Kubernetes는 새 복제본 세트를 생성하여 컨테이너를 배포하고, 동시에 롤링 업데이트 전략에 따라 이전 복제본 세트의 파드를 제거한다.
복제본 세트 확인 명령어
1
kubectl get replicasets
롤백 전후에 이 명령어를 실행하면 복제본 세트의 상태 변화를 확인할 수 있다.
- 롤백 전: 첫 번째 복제본 세트는 0개의 파드, 새 복제본 세트는 5개의 파드
- 롤백 후: 첫 번째 복제본 세트는 5개의 파드, 새 복제본 세트는 0개의 파드
배포 전략 비교
kubectl describe deployment
명령을 실행하여 배포 세부 정보를 확인하면, 다음과 같은 차이점을 볼 수 있다.
- Recreate 전략:
- 이벤트에서 이전 복제본 세트가 먼저 0으로 스케일 다운
- 이후 새 복제본 세트가 5로 스케일 업
- Rolling Update 전략:
- 이전 복제본 세트가 한 번에 하나씩 스케일 다운
- 동시에 새 복제본 세트가 한 번에 하나씩 스케일 업
주요 명령어 요약
- 배포 생성:
kubectl create deployment [배포-이름] --image=[이미지-이름]
- 배포 목록 확인:
kubectl get deployments
- 배포 업데이트:
kubectl apply -f [배포-파일.yaml]
kubectl set image deployment [배포-이름] [컨테이너-이름]=[새-이미지]
- 롤아웃 상태 확인:
kubectl rollout status deployment [배포-이름]
- 롤아웃 기록 확인:
kubectl rollout history deployment [배포-이름]
- 롤백 실행:
kubectl rollout undo deployment [배포-이름]
2. 컨테이너의 명령어와 인수
컨테이너와 명령어의 개념
컨테이너는 가상 머신과 달리 운영 체제를 호스팅하기 위한 것이 아니라 특정 작업이나 프로세스를 실행하기 위한 것입니다. 예를 들어 웹 서버, 애플리케이션 서버, 데이터베이스의 인스턴스를 호스팅하거나 계산이나 분석을 수행하는 등의 작업을 위해 설계되었습니다.
중요한 사실: 컨테이너는 내부 프로세스가 살아있는 동안만 존재합니다. 내부 프로세스가 종료되면 컨테이너도 함께 종료된다.
Docker에서의 명령어 지정 방법
Docker 이미지에는 컨테이너가 시작될 때 실행될 프로그램을 정의하는 CMD(Command) 지시문이 있다.
- NGINX 이미지의 경우
nginx
명령 - MySQL 이미지의 경우
mysqld
명령
기본 Ubuntu 이미지의 경우 기본 명령은 bash
이다. 그러나 bash는 터미널 입력을 기다리는 셸이므로, 터미널이 연결되지 않으면 종료된다. 기본 Ubuntu 컨테이너가 즉시 종료되는 이유다.
명령어 오버라이드하기
컨테이너 시작 시 실행되는 명령을 변경하는 두 가지 방법이 있다.
- 실행 시 명령 추가: Docker 실행 명령에 새 명령을 추가한다.
1
docker run ubuntu sleep 5
- 사용자 정의 이미지 생성: Dockerfile에서 새 명령을 지정한다.
1 2
FROM ubuntu CMD ["sleep", "5"]
CMD와 ENTRYPOINT의 차이
Docker에서는 컨테이너 시작 명령을 지정하는 두 가지 지시문이 있다.
- CMD: 컨테이너 시작 시 실행할 기본 명령을 지정한다.
- Docker 실행 명령에 매개변수를 전달하면 CMD 지시문이 완전히 대체된다.
- ENTRYPOINT: 컨테이너 시작 시 항상 실행되는 명령을 지정한다.
- Docker 실행 명령에 전달된 매개변수는 ENTRYPOINT에 추가된다.
실용적인 사용 예시
다음은 사용자 정의 “ubuntu-sleeper” 이미지를 만드는 예시다.
1
2
3
FROM ubuntu
ENTRYPOINT ["sleep"]
CMD ["5"]
이 이미지의 특징
- 기본적으로
sleep 5
명령을 실행한다. - 실행 시 매개변수를 지정하면(예:
docker run ubuntu-sleeper 10
)sleep 10
이 실행된다. - ENTRYPOINT는 항상 실행되고, CMD는 기본값을 제공하지만 실행 시 오버라이드될 수 있다.
ENTRYPOINT 오버라이드하기
ENTRYPOINT도 오버라이드할 수 있다.
1
docker run --entrypoint sleep2.0 ubuntu-sleeper 10
3. 쿠버네티스 파드에서의 명령어와 인수
빈 파드 정의 템플릿으로 시작하여 파드 이름을 입력하고 이미지 이름을 지정한다. 이 파드가 생성되면, 지정된 이미지에서 컨테이너를 생성하고, 컨테이너는 종료하기 전에 5초 동안 대기한다.
만약 컨테이너가 10초 동안 대기하도록 하려면, 파드 정의 파일에서 추가 인수를 지정하려면 아래와 같이할 수 있다.
Docker run 명령어에 추가되는 모든 것은 다음과 같이 배열 형태로 파드 정의 파일의 args
속성으로 들어간다.
1
2
3
4
5
6
7
8
9
apiVersion: v1
kind: Pod
metadata:
name: ubuntu-sleeper-pod
spec:
containers:
- name: ubuntu-sleeper
image: ubuntu-sleeper
args: ["10"]
이전에 만든 Docker 파일과 관련지어 살펴보면, Docker 파일에는 ENTRYPOINT와 CMD 지시문이 지정되어 있다. ENTRYPOINT는 시작 시 실행되는 명령이고, CMD는 명령에 전달되는 기본 매개변수이다.
파드 정의 파일의 args
옵션을 사용하면 Docker 파일의 CMD 지시문을 오버라이드한다.
ENTRYPOINT를 sleep에서 가상의 sleep2.0 명령으로 오버라이드하려면
Docker 에서는 다음과 같이 --entrypoint
옵션을 새 명령으로 설정하여 docker run 명령을 실행한다.
1
docker run --entrypoint sleep2.0 ubuntu-sleeper 10
쿠버네티스 파드 정의 파일에서는 command
필드를 사용한다.
1
2
3
4
5
6
7
8
9
10
apiVersion: v1
kind: Pod
metadata:
name: ubuntu-sleeper-pod
spec:
containers:
- name: ubuntu-sleeper
image: ubuntu-sleeper
command: ["sleep2.0"]
args: ["10"]
요약하자면, Docker 파일의 두 지시문에 해당하는 두 개의 필드가 있다.
command
필드는 ENTRYPOINT 지시문을 오버라이드한다.args
필드는 CMD 지시문을 오버라이드한다.
Docker 파일의 CMD 지시문을 오버라이드하는 것은 command
필드가 아니라 args
필드라는 것이 중요하다.
4. 쿠버네티스에서 환경 변수 설정하기
환경 변수를 설정하려면 env
속성을 사용한다. env
는 배열이므로, env
속성 아래의 모든 항목은 배열의 요소를 나타내는 대시(-)로 시작한다. 각 항목에는 name
과 value
속성이 있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
apiVersion: v1
kind: Pod
metadata:
name: my-pod
spec:
containers:
- name: my-container
image: my-image
env:
- name: APP_COLOR
value: blue
- name: APP_MODE
value: prod
name
은 컨테이너 내에서 사용 가능한 환경 변수의 이름이고, value
는 그 값이다.
ConfigMap과 Secret과 같은 다른 방법으로 환경 변수를 설정할 수도 있다.
이 경우의 차이점은 value
를 직접 지정하는 대신 valueFrom
을 사용하고, 그 다음에 ConfigMap이나 Secret의 사양을 지정한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
apiVersion: v1
kind: Pod
metadata:
name: my-pod
spec:
containers:
- name: my-container
image: my-image
env:
- name: APP_COLOR
valueFrom:
configMapKeyRef:
name: app-config
key: APP_COLOR
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: app-secret
key: DB_PASSWORD
5. 컨피그 맵
많은 파드 정의 파일이 있을 때, 쿠버네티스 파일 내에 저장된 환경 데이터를 관리하기가 어려워진다. 이러한 정보를 파드 정의 파일에서 분리하여 컨피그맵을 사용해 중앙에서 관리할 수 있다.
컨피그맵이란?
컨피그맵은 쿠버네티스에서 키-값 쌍 형태로 구성 데이터를 전달하는 데 사용된다. 파드가 생성될 때, 컨피그맵을 파드에 주입하여 컨테이너 내부에서 호스팅되는 애플리케이션에 환경 변수로 키-값 쌍을 사용할 수 있게 한다.
컨피그맵 생성 방법
다른 쿠버네티스 객체와 마찬가지로, 컨피그맵을 생성하는 두 가지 방법이 있다.
1. 명령적 방식 (Imperative Way)
컨피그맵 정의 파일을 사용하지 않는 방법
1
kubectl create configmap app-config --from-literal=APP_COLOR=blue
여러 키-값 쌍을 추가하려면 --from-literal
옵션을 여러 번 사용한다.
1
kubectl create configmap app-config --from-literal=APP_COLOR=blue --from-literal=APP_MODE=prod
파일에서 구성 데이터를 입력하는 방법
1
kubectl create configmap app-config --from-file=app-config.properties
2. 선언적 방식 (Declarative Way)
정의 파일을 사용하는 방법
1
2
3
4
5
6
7
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
APP_COLOR: blue
APP_MODE: prod
파일을 생성한 후
1
kubectl create -f config-map.yaml
컨피그맵 확인하기
생성된 컨피그맵을 보려면
1
kubectl get configmaps
컨피그맵의 상세 정보를 보려면
1
kubectl describe configmaps app-config
파드에 컨피그맵 주입하기
컨피그맵이 생성되면, 파드에 주입할 수 있다.
1
2
3
4
5
6
7
8
9
10
11
apiVersion: v1
kind: Pod
metadata:
name: simple-webapp
spec:
containers:
- name: simple-webapp
image: simple-webapp
envFrom:
- configMapRef:
name: app-config
envFrom
속성은 리스트로, 필요한 만큼 많은 환경 변수를 전달할 수 있\다. 목록의 각 항목은 컨피그맵 항목에 해당한다. 우리가 이전에 생성한 컨피그맵의 이름을 지정한다.
이렇게 하면 파란색 배경의 웹 애플리케이션이 생성된다.
다른 주입 방법
컨피그맵을 통해 구성 데이터를 파드에 주입하는 다른 방법이 있다.
- 단일 환경 변수로 주입
- 볼륨의 파일로 전체 데이터 주입
6. 시크릿(Secrets)
시크릿의 필요성
시크릿은 암호나 키와 같은 민감한 정보를 저장하는 데 사용된다. ConfigMap과 유사하지만 인코딩된 형식으로 저장된다는 차이가 있다.
시크릿 생성 단계
ConfigMap과 마찬가지로 시크릿 작업에도 두 단계가 있다.
- 시크릿 생성
- 파드에 시크릿 주입
시크릿 생성 방법
시크릿을 생성하는 두 가지 방법이 있다.
1. 명령적 방식 (Imperative Way)
시크릿 정의 파일을 사용하지 않는 방법
1
kubectl create secret generic app-secret --from-literal=DB_HOST=mysql
여러 키-값 쌍을 추가하려면 --from-literal
옵션을 여러 번 사용한다.
파일에서 시크릿 데이터를 입력하는 방법
1
kubectl create secret generic app-secret --from-file=app-secret.properties
2. 선언적 방식 (Declarative Way)
정의 파일을 사용하는 방법
1
2
3
4
5
6
7
8
apiVersion: v1
kind: Secret
metadata:
name: app-secret
data:
DB_HOST: bXlzcWw=
DB_USER: cm9vdA==
DB_PASSWORD: cGFzc3dvcmQ=
시크릿은 민감한 데이터를 저장하고 인코딩된 형식으로 저장되므로, 선언적 접근 방식으로 시크릿을 생성할 때는 시크릿 값을 인코딩된 형식으로 지정해야 한다.
데이터 인코딩 방법
데이터를 일반 텍스트에서 인코딩된 형식으로 변환하려면 Linux 호스트에서 다음 명령을 실행한다.
1
echo -n 'mysql' | base64
시크릿 확인하기
1
kubectl get secrets
이 명령은 새로 생성된 시크릿과 쿠버네티스가 내부 목적으로 이전에 생성한 다른 시크릿을 나열한다.
1
kubectl describe secret app-secret
이 명령은 시크릿의 속성을 보여주지만 값 자체는 숨긴다.
1
kubectl get secret app-secret -o yaml
이 명령으로 인코딩된 값을 볼 수 있습니다.
인코딩된 값 디코딩 방법
인코딩된 값을 디코딩하려면 이전에 사용한 것과 동일한 base64 명령을 사용하지만, 이번에는 decode 옵션을 추가해야한다.
1
echo 'bXlzcWw=' | base64 --decode
파드에 시크릿 주입하기
시크릿이 생성되면, 파드에 주입할 수 있다.
1
2
3
4
5
6
7
8
9
10
11
apiVersion: v1
kind: Pod
metadata:
name: my-app
spec:
containers:
- name: my-app
image: my-app
envFrom:
- secretRef:
name: app-secret
이렇게 하면 시크릿의 데이터가 애플리케이션의 환경 변수로 사용할 수 있게 된다.
다른 주입 방법
시크릿을 파드에 주입하는 다른 방법이 있다.
- 단일 환경 변수로 주입
- 볼륨의 파일로 전체 시크릿 주입
파드에서 시크릿을 볼륨으로 마운트하면, 시크릿의 각 속성은 내용으로 시크릿의 값을 갖는 파일로 생성된다. 예를 들어, 시크릿에 세 가지 속성이 있는 경우 세 개의 파일이 생성되고, DB_PASSWORD 파일의 내용을 보면 암호를 볼 수 있다.
시크릿 사용 시 주의사항
- 시크릿은 암호화되지 않는다.: 시크릿은 단지 인코딩되어 있으므로, 시크릿 파일이나 객체를 확인한 다음 디코딩하면 기밀 데이터를 볼 수 있다. 코드를 GitHub 등에 푸시할 때 시크릿 정의 파일을 포함하지 않도록 주의해야한다.
- etcd에서 시크릿은 암호화되지 않는다.: 기본적으로 etcd의 데이터는 암호화되지 않는다. “rest 상태에서의 암호화”를 활성화하는 것을 고려해야한다.
- 동일한 네임스페이스에서 파드나 배포를 생성할 수 있는 사람은 시크릿에도 접근할 수 있다.: 특정 네임스페이스에 시크릿을 생성하면, 동일한 네임스페이스에서 파드나 배포에 접근할 수 있는 사람은 그 시크릿을 사용하여 파드나 배포를 생성하고 마운트된 시크릿 객체를 볼 수 있다. 접근을 제한하기 위해 역할 기반 접근 제어(RBAC)를 구성하는 것을 고려해야한다.
- 제3자 시크릿 제공자 고려: AWS, Azure, GCP 또는 Vault 제공자와 같은 제3자 시크릿 제공자를 고려해야한다. 이렇게 하면 시크릿이 etcd가 아닌 외부 시크릿 제공자에 저장되고, 이러한 제공자는 대부분의 보안을 처리한다.
7. 시크릿 데이터의 저장 암호화
현재 문제점 이해하기
기본적인 시크릿이 어떻게 저장되는지 조회하기위해 간단한 시크릿을 생성한다.
1
kubectl create secret generic my-secret --from-literal=key1=supersecret
이 시크릿 데이터가 etcd에 어떻게 저장되어 있는지 확인한다.
1
2
3
4
ETCDCTL_API=3 etcdctl --cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key \
get /registry/secrets/default/my-secret | hexdump -C
위 명령을 실행하면 etcd에 저장된 시크릿 데이터가 암호화되지 않은 형식으로 볼 수 있다. etcd에 접근할 수 있는 사람은 모든 시크릿 데이터를 볼 수 있기 때문에 보안에 취약하다.
암호화 활성화 여부 확인
암호화가 이미 활성화되어 있는지 확인하기 위해, kube-apiserver의 설정을 확인한다.
1
ps aux | grep kube-apiserver | grep encryption-provider-config
또는 kubeadm으로 설정된 클러스터에서는 다음 파일을 확인한다.
1
cat /etc/kubernetes/manifests/kube-apiserver.yaml | grep encryption-provider-config
결과가 나오지 않으면 암호화가 활성화되어 있지 않은 것이다.
암호화 구성 파일 생성
REST 상태에서 암호화를 활성화하려면 다음과 같은 암호화 구성 파일을 생성해야 한다:
1
2
3
4
5
6
7
8
9
10
11
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources:
- secrets
providers:
- aescbc:
keys:
- name: key1
secret: <32바이트 랜덤 키를 Base64로 인코딩한 값>
- identity: {}
32바이트 랜덤 키를 생성하려면 다음 명령을 사용할 수 있다.
1
head -c 32 /dev/urandom | base64
이 파일을 /etc/kubernetes/enc/enc.yaml
과 같은 위치에 저장한다.
kube-apiserver 설정 수정
다음으로, kube-apiserver 설정에 암호화 구성 파일을 적용한다. kubeadm으로 설정된 클러스터에서는 다음과 같이 수정한다:
- kube-apiserver 매니페스트 파일을 편집
1
vi /etc/kubernetes/manifests/kube-apiserver.yaml
- 명령 인자에 다음을 추가 ```yaml spec: containers:
- command:
- kube-apiserver
- –encryption-provider-config=/etc/kubernetes/enc/enc.yaml # 다른 옵션들… ```
- 볼륨 마운트를 추가
1 2 3 4
volumeMounts: - name: encryption-config mountPath: /etc/kubernetes/enc readOnly: true
- 볼륨을 추가
1 2 3 4 5
volumes: - name: encryption-config hostPath: path: /etc/kubernetes/enc type: DirectoryOrCreate
변경 사항을 저장하면 kube-apiserver가 자동으로 재시작된다.
암호화 확인
새로운 시크릿을 생성하고 etcd에 암호화되어 저장되는지 확인한다.
1
kubectl create secret generic my-secret-2 --from-literal=key2=topsecret
다시 etcd 내용을 확인한다.
1
2
3
4
ETCDCTL_API=3 etcdctl --cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key \
get /registry/secrets/default/my-secret-2 | hexdump -C
이제 시크릿 값(topsecret)이 확인되지 않고 암호화된 형태로 저장된 것을 볼 수 있다.
기존 시크릿 다시 암호화하기
암호화를 활성화한 후에는 새로 생성된 시크릿만 암호화된다. 기존 시크릿을 암호화하려면 다음 명령을 실행한다.
1
kubectl get secrets --all-namespaces -o json | kubectl replace -f -
이 명령은 모든 시크릿을 가져와서 같은 데이터로 업데이트하여 재암호화를 트리거한다.
주의사항
- 암호화 구성 파일과 키를 안전하게 백업해야 한다. 이 정보를 잃어버리면 암호화된 데이터를 복구할 수 없다.
- 암호화는 저장된 데이터만 보호한다. 전송 중인 데이터는 보호하지 않는다.
- providers 목록에서 첫 번째 제공자가 암호화에 사용되며, 해독 시에는 목록에 있는 모든 제공자가 시도된다.
- identity 제공자를 첫 번째로 두면 암호화가 되지 않는다.
8. 멀티 컨테이너 파드
마이크로서비스 아키텍처는 애플리케이션을 작고 독립적이며 재사용 가능한 코드로 구성된 하위 컴포넌트로 분할한다. 이러한 아키텍처는 전체 애플리케이션을 수정하는 대신, 필요에 따라 각 서비스를 독립적으로 확장하거나 수정할 수 있게 한다.
그러나 때로는 두 서비스가 함께 작동해야 하는 경우가 있습니다. 예를 들어, 웹 서버와 로깅 서비스인 경우 웹 서버 인스턴스마다 하나의 로깅 에이전트 인스턴스가 필요하며, 두 서비스가 함께 쌍을 이루어야 한다.
멀티 컨테이너 파드의 필요성
웹 서버 인스턴스마다 하나의 에이전트가 필요하고, 두 서비스가 함께 확장되어야 하는 경우가 있다. 멀티 컨테이너 파드는 다음과 같은 특징을 가진다.
- 동일한 생명주기 공유: 함께 생성되고 함께 삭제된다.
- 동일한 네트워크 공간 공유: 서로를 localhost로 참조할 수 있다.
- 동일한 스토리지 볼륨에 접근 가능: 데이터를 쉽게 공유할 수 있다.
이러한 방식으로 파드 간의 볼륨 공유나 서비스를 설정할 필요 없이 서로 통신할 수 있다.
멀티 컨테이너 파드 생성 방법
멀티 컨테이너 파드를 생성하려면 파드 정의 파일에 새 컨테이너 정보를 추가하면 된다. 단일 파드에 여러 컨테이너를 허용하기 위해 파드 정의 파일의 spec 섹션 아래 containers 섹션은 배열이다.
예를 들어, 기존 파드에 ‘log-agent’라는 새 컨테이너를 추가할 수 있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
apiVersion: v1
kind: Pod
metadata:
name: my-web-app
spec:
containers:
- name: web
image: nginx
ports:
- containerPort: 80
- name: log-agent
image: log-agent-image
volumeMounts:
- name: logs-volume
mountPath: /var/log
volumes:
- name: logs-volume
emptyDir: {}
이 yml을 실행시키면 ‘web’ 컨테이너와 ‘log-agent’ 컨테이너가 동일한 파드에 있으며, 두 컨테이너는 동일한 네트워크 공간을 공유하고 동일한 볼륨에 접근할 수 있다.
9. 멀티 컨테이너 파드 디자인 패턴
멀티 컨테이너 파드를 설계할 때 일반적으로 사용되는 3가지 패턴이 있다.
- 사이드카 패턴(Sidecar Pattern): 방금 로깅 서비스 예제에서 본 것처럼, 기본 애플리케이션 컨테이너의 기능을 확장하거나 향상시키는 추가 컨테이너를 포함한다.
- 어댑터 패턴(Adapter Pattern): 기본 애플리케이션의 데이터를 표준화하거나 다른 시스템에서 필요한 형식으로 변환하는 컨테이너를 포함한다.
- 앰배서더 패턴(Ambassador Pattern): 기본 애플리케이션 컨테이너를 대신하여 외부 시스템에 연결하는 프록시 컨테이너를 포함한다.
10. 오토스케일링(Auto Scaling)
CKA 시험에서 다루는 주제로는 수평 파드 오토스케일링(HPA)과 수직 파드 오토스케일링(VPA)이 있다.
전통적인 스케일링 방식
과거에는 사전 정의된 CPU 및 메모리 용량을 가진 물리적 서버에서 애플리케이션을 호스팅했다. 부하가 증가하여 서버의 기존 리소스가 부족해지면 할 수 있는 방법은 두 가지이다.
- 수직 스케일링(Vertical Scaling): 애플리케이션을 중단하고 서버에 더 많은 CPU나 메모리 리소스를 추가한 다음 다시 가동하는 방식으로 기존 서버의 크기를 수직으로 증가시키는 것이다.
- 수평 스케일링(Horizontal Scaling): 애플리케이션이 여러 인스턴스에서 실행될 수 있다면, 서버를 종료하지 않고 더 많은 서버를 추가하여 부하를 분산시키는 방식이다. 더 많은 서버를 추가하여 애플리케이션의 인스턴스를 더 실행하는 것을 수평 스케일링이라고 한다.
쿠버네티스에서의 스케일링
쿠버네티스에서는 두 가지 유형의 스케일링이 있다.
- 워크로드 스케일링: 클러스터에 컨테이너나 파드를 추가하거나 제거하여 워크로드를 스케일링한다.
- 클러스터 스케일링: 클러스터 자체에 더 많은 서버나 인프라를 추가하거나 제거한다.
각 유형은 다시 수평 및 수직 스케일링으로 나눌 수 있다.
클러스터 스케일링
- 수평 스케일링: 클러스터에 더 많은 노드를 추가한다.
- 수직 스케일링: 클러스터의 기존 노드에서 리소스를 증가시킨다.
워크로드 스케일링
- 수평 스케일링: 더 많은 파드를 생성한다.
- 수직 스케일링: 기존 파드에 할당된 리소스를 증가시킨다.
스케일링 방법
스케일링에는 수동 방식과 자동 방식이 있다.
수동 스케일링
- 클러스터 수평 스케일링: 새 노드를 수동으로 프로비저닝하고
kubeadm join
명령을 사용하여 클러스터에 추가한다. - 클러스터 수직 스케일링: 쿠버네티스에서는 일반적이지 않은 접근 방식으로, 서버와 애플리케이션을 중단해야 한다. 대신 더 많은 리소스가 있는 서버를 프로비저닝하고 클러스터에 추가한 다음 이전 서버를 제거할 수 있다.
- 워크로드 수평 스케일링:
kubectl scale
명령을 사용하여 파드 수를 늘리거나 줄인다. - 워크로드 수직 스케일링:
kubectl edit
명령을 사용하여 배포, 스테이트풀셋 또는 레플리카셋의 리소스 제한과 요청을 변경한다.
자동 스케일링
- 클러스터 수평 스케일링: 쿠버네티스 클러스터 오토스케일러(Cluster Autoscaler)를 사용한다.
- 워크로드 수평 스케일링: 수평 파드 오토스케일러(Horizontal Pod Autoscaler, HPA)를 사용한다.
- 워크로드 수직 스케일링: 수직 파드 오토스케일러(Vertical Pod Autoscaler, VPA)를 사용한다.
11. 수평 파드 오토스케일러(HPA)
수동 스케일링 방식
애플리케이션 수요를 충족시키기 위해 충분한 워크로드가 항상 유지되도록 해야 한다. 배포 구성에서 파드가 250 밀리코어의 CPU를 요청하고 500 밀리코어의 CPU 제한이 있다고 가정했을 때. 이는 파드가 최대 500 밀리코어의 CPU를 사용할 수 있으며, 그 이상은 제공받지 못한다는 의미이다.
수동으로 스케일링하려면
kubectl top pod
명령을 실행하여 파드의 리소스 사용량을 모니터링한다. (이 기능은 클러스터에 메트릭 서버가 실행 중이어야 한다.)- CPU 사용량이 임계값(예: 450 밀리코어)에 도달하면,
kubectl scale
명령을 실행하여 더 많은 파드를 추가한다.
이 접근 방식의 문제점
- 지속적으로 리소스 사용량을 모니터링해야 한다.
- 스케일링을 위해 수동으로 명령을 실행해야 한다.
- 갑작스러운 트래픽 증가에 충분히 빠르게 대응하지 못할 수 있다.
수평 파드 오토스케일러(HPA)
이러한 문제를 해결하기 위해 수평 파드 오토스케일러를 사용한다.
- HPA는
top
명령을 사용하여 수동으로 했던 것처럼 지속적으로 메트릭을 모니터링한다. - 배포, 스테이트풀셋 또는 레플리카셋의 파드 수를 CPU, 메모리 또는 사용자 정의 메트릭에 기반하여 자동으로 증가시키거나 감소시킨다.
- CPU 또는 메모리 사용량이 너무 높아지면 더 많은 파드를 생성하고, 사용량이 감소하면 추가 파드를 제거하여 리소스를 절약한다.
HPA 실습
NGINX 배포에 대한 HPA를 구성하는 방법을 살펴본다.
명령적 접근 방식
1
kubectl autoscale deployment my-app --cpu-percent=50 --min=1 --max=10
이 명령은 다음과 같은 작업을 수행한다.
- 배포에 대한 수평 파드 오토스케일러를 생성
- 파드에 구성된 제한을 읽고, 500 밀리코어로 설정되어 있음을 확인
- 메트릭 서버를 지속적으로 폴링하여 사용량을 모니터링
- 사용량이 50%를 초과하면 파드 수를 늘리거나 축소
HPA 상태를 확인하려면
1
kubectl get hpa
이 명령은 현재 CPU 사용량, 설정된 임계값, 최소/최대 파드 수, 현재 복제본 수를 보여준다.
HPA가 더 이상 필요하지 않을 경우
1
kubectl delete hpa my-app
선언적 접근 방식
HPA 정의 파일을 생성할 수도 있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: my-app-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: my-app
minReplicas: 1
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 50
scaleTargetRef
는 HPA가 모니터링할 대상 리소스(my-app 배포)를 지정한다.minReplicas
와maxReplicas
는 최소/최대 복제본 수를 정의한다.metrics
는 모니터링할 메트릭(CPU)과 대상 사용률(50%)을 구성한다.
중요 참고사항
- HPA는 쿠버네티스 버전 1.23부터 기본으로 제공되므로 별도의 설치가 필요하지 않는다.
- HPA는 메트릭 서버에 의존하므로, 메트릭 서버가 설치되어 있어야 한다.
- 내부 메트릭 서버 외에도 다음과 같은 다른 소스를 참조할 수 있다.
- 클러스터에 배포된 워크로드와 같은 내부 소스에서 정보를 검색할 수 있는 사용자 정의 메트릭 어댑터
- Datadog 또는 Dynatrace와 같은 쿠버네티스 클러스터 외부의 도구나 인스턴스와 같은 외부 소스(외부 어댑터 사용)
12. 파드 리소스의 동적 리사이징
현재 파드 리소스 변경의 기본 동작
쿠버네티스 버전 1.32 기준, 배포에서 파드의 리소스 요구사항을 변경하면 기본 동작은 다음과 같다.
- 기존 파드를 삭제
- 새로운 변경사항이 적용된 새 파드를 생성
즉, 파드의 리소스 정의에 대한 변경은 현재 파드에서 즉시 이루어지지 않는다. 파드를 종료하고 새 리소스 정의로 새 파드를 생성해야 한다. 이는 특히 상태 유지(stateful) 워크로드에서 중단을 일으킬 수 있다.
파드 리소스의 현장 업데이트
이러한 문제를 해결하기 위해 ‘파드 리소스의 현장 업데이트(in-place update)’라는 기능이 개발 중이다.
- 이 기능은 쿠버네티스 1.27 릴리스부터 알파 단계에 있으며, 기본적으로 활성화되어 있지 않다.
- 향후 베타 또는 안정 단계에 도달하면 기본적으로 활성화될 예정이다.
- 현재는 기능 플래그
InPlacePodVerticalScaling
을True
로 설정하여 활성화할 수 있다.
리사이징 정책 파라미터
활성화되면 파드 정의는 리사이징 정책 파라미터 세트를 지원한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
spec:
containers:
- name: my-container
resources:
resizePolicy:
- resourceName: cpu
restartPolicy: NotRequired
- resourceName: memory
restartPolicy: RestartContainer
requests:
cpu: 500m
memory: 100Mi
limits:
cpu: 1
memory: 200Mi
이 예시에서:
- CPU 리소스 변경은 파드를 재시작할 필요가 없다.
- 메모리 변경은 파드 재시작이 필요하다
CPU를 1로 업데이트하면, 파드를 종료할 필요 없이 새 리소스로 업데이트된다.
현장 리사이징의 제한사항
이 기능에는 몇 가지 제한사항이 있다.
- CPU와 메모리 리소스에만 작동한다.
- Pod QoS Class 등은 변경할 수 없다.
- Init 컨테이너 및 임시(ephemeral) 컨테이너는 이 방식으로 리사이징할 수 없다.
- 리소스 요청과 제한은 설정된 후에는 이동할 수 없다.
- 컨테이너의 메모리 제한은 사용량 아래로 줄일 수 없다. 요청이 컨테이너를 이 상태로 만들면, 원하는 메모리 제한이 가능해질 때까지 리사이징 상태는 ‘진행 중’으로 유지된다.
- 현재로서는 Windows 파드는 리사이징할 수 없다.
13. 수직 파드 오토스케일러(VPA)
수동으로 스케일링하려면
kubectl top pod
명령을 실행하여 파드의 리소스 사용량을 모니터링 (이 기능은 클러스터에 메트릭 서버가 실행 중이어야 한다.)- 특정 임계값에 도달하면
kubectl edit deployment
명령을 실행 - 배포 내 파드 템플릿의 컨테이너 섹션에서 리소스 요청과 제한을 변경하고 저장
- 이렇게 하면 기존 파드가 종료되고 새로운 리소스 설정으로 새 파드가 생성됨
이런 작업을 수동으로 하고 싶지 않기 때문에 수직 파드 오토스케일러(VPA)를 사용한다.
수직 파드 오토스케일러(VPA)
수평 파드 오토스케일러(HPA)와 유사하게, 수직 파드 오토스케일러(VPA)는 지속적으로 메트릭을 모니터링하고 배포의 파드에 할당된 리소스를 자동으로 증가시키거나 감소시켜 워크로드의 균형을 맞춘다.
VPA 구성 요소
HPA와 달리 VPA는 기본으로 제공되지 않으므로 먼저 배포해야 한다. GitHub 저장소에서 제공하는 VPA 정의 파일을 적용하면, kube-system
네임스페이스에서 다음과 같은 여러 컴포넌트가 배포된다.
- VPA Recommender: 쿠버네티스 메트릭 API에서 리소스 사용량을 지속적으로 모니터링하고, 파드의 과거 및 현재 사용 데이터를 수집하여 최적의 CPU 및 메모리 값에 대한 권장 사항을 제공한다. 직접 파드를 수정하지는 않고 변경 사항만 제안한다.
- VPA Updater: 최적이 아닌 리소스로 실행 중인 파드를 감지하고 업데이트가 필요할 때 해당 파드를 제거한다. Recommender로부터 정보를 얻어 파드를 모니터링하고, 필요시 파드를 종료한다.
- VPA Admission Controller: 파드 생성 과정에 개입하여 Recommender의 권장 사항을 사용해 파드 스펙을 변경함으로써 시작 시 권장된 CPU 및 메모리 값을 적용한다. 이를 통해 새로 생성된 파드가 올바른 리소스 요청으로 시작하도록 보장한다.
VPA 생성 방법
VPA는 내장 컴포넌트가 아니므로, 명령형 명령을 사용해 생성할 수 없다. 대신 정의 파일을 사용해야한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: my-app-vpa
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: my-app
containerPolicies:
- containerName: '*'
minAllowed:
cpu: 100m
maxAllowed:
cpu: 1
updatePolicy:
updateMode: Recreate
여기서:
targetRef
는 VPA가 모니터링할 대상(my-app 배포)을 지정한다.containerPolicies
는 모니터링 대상을 정의한다. 이 경우 CPU의 최소 및 최대 허용값을 구성한다.updatePolicy
의updateMode
는 VPA의 작동 모드를 지정한다.
VPA 작동 모드
VPA는 네 가지 모드로 작동한다.
- Off 모드: 변경 사항만 권장하고 아무 조치도 취하지 않는다. Recommender만 작동하고 Updater와 Admission Controller는 작동하지 않는다.
- Initial 모드: 파드 생성 시에만 변경 사항을 적용하고, 나중에는 적용하지 않는ㄷ나. Recommender가 변경을 권장하고, 다른 이유로 배포가 확장될 때 Admission Controller가 개입하여 새 파드의 리소스 정의를 변경한다. Updater는 파드를 종료하지 않는다.
- Recreate 모드: Updater 컴포넌트가 리소스 소비가 지정된 범위를 초과할 때 기존 파드를 제거한다.
- Auto 모드: 권장된 숫자로 기존 파드를 업데이트한다. 현재는 Recreate 모드와 유사하게 작동하지만, 파드 리소스의 현장 업데이트가 안정 버전의 쿠버네티스에서 사용 가능해지면 Recreate 모드보다 선호될 것으로 보인다.
VPA 권장 사항 확인
VPA가 리소스 사용량을 모니터링하고 조정 사항을 제안하므로, 다음 명령으로 권장 사항을 확인할 수 있다.
1
kubectl describe vpa my-app-vpa
이 명령을 실행하면 VPA가 제안하는 리소스 변경 사항(예: CPU를 1.5로 증가)을 볼 수 있다.
HPA와 VPA 비교
스케일링 방법
- VPA: 기존 파드의 CPU 및 메모리를 증가시키거나 새 리소스로 파드를 재생성한다.
- HPA: 수요에 따라 파드를 추가하거나 제거한다.
이는 VPA가 개별 파드 성능을 최적화하는 반면, HPA는 여러 인스턴스에 걸쳐 부하를 분산시키는 데 중점을 둔다는 것을 의미한다.
파드 동작
- VPA: 새 리소스 값을 적용하기 위해 파드를 재시작하므로, 수직으로 확장할 때 다운타임이 발생한다.
- HPA: 기존 파드를 계속 실행하고 새 파드를 추가하여 지속적인 가용성을 보장한다.
트래픽 스파이크 처리
- HPA: 수요가 증가하면 즉시 더 많은 파드를 추가하므로, 빠른 스케일링이 필요한 애플리케이션에 선호된다.
- VPA: 파드를 재시작해야 하므로 갑작스러운 스파이크를 효율적으로 처리할 수 없다.
비용 최적화
- VPA: 실제 사용량에 맞게 CPU 및 메모리 할당을 조정하여 과잉 프로비저닝을 방지한다.
- HPA: 불필요한 유휴 파드를 방지하여 과도한 인스턴스 실행 없이 리소스를 효율적으로 사용한다.
VPA, HAP 언제 어떤 것을 사용해야 하는가?
- VPA: 데이터베이스, JVM 기반 애플리케이션, 세밀하게 조정된 리소스가 필요한 AI/ML 워크로드와 같은 상태 유지(stateful) 워크로드와 CPU 또는 메모리 집약적 애플리케이션에 적합하다. 예를 들어, 초기 시작 시 많은 CPU가 필요하지만 나중에는 그렇지 않은 애플리케이션에 유용하다.
- HPA: 웹 서버, 메시지 큐, API 기반 애플리케이션과 같은 웹 애플리케이션, 마이크로서비스, 상태 비유지(stateless) 서비스에 적합하다. 이러한 서비스는 변동하는 트래픽을 처리하기 위해 빠른 스케일링이 필요하다.
VPA는 개별 파드의 리소스 할당을 최적화하는 데 중점을 두고, HPA는 수요에 따라 동적으로 파드 수를 스케일링하는 데 중점을 둔다. 올바른 오토스케일러 선택은 워크로드 유형과 애플리케이션 스케일링 요구 사항에 따라 달라진다.