CKA 대비 kubernetes 스터디 - 4. Application LifeCycle Management
Rollout Updates / Rollback
Rollout이 진행될 때 k8s에서는 어떤 절차로 작업이 진행되는가
- 앱을 처음 배포할 때 - 새로운 Deployment를 생성한다
- 해당 deployment는 rollout을 발생시킨다.
- rollout은 새로운 revision을 생성한다. (revision 1)
- 버전이 올라가거나 소스코드가 바뀌는 등의 업그레이드가 발생하면, new deployment를 생성한다.
- new Rollout이 생성되고, 새로운 revision이 생성된다.
두 개의 revision이 유지되고 있기 때문에 새로운 버전으로 rollout하는 것 / 이전 버전으로 rollback하는 것이 가능하다.
Rollout 명령어 / 상태 확인하는 방법.
Deployment Strategy는 크게 두 가지 종류가 있다.
- Recreate: 예전 Revision을 다 지운 후에야 새 revision을 띄운다.
Application Downtime이 존재한다는 약점이 있고, k8s의 디폴트 옵션이 아님. - Rolling update: 이전 버전의 pod을 하나씩 죽이고, 새 pod으로 대체하는 방식.
downtime이 존재하지 않는다. k8s의 디폴트 옵션.
kubectl describe deployment
로 로그를 확인해보면 특징을 보다 명확히 알 수 있다.
- upgrade하기 위해서는, 변경사항을 반영할 수 있도록
kubectl apply
를 적용한다. - set image update 등의 cmd를 사용할 수도 있는데, 이 경우 yaml파일의 설정옵션과 실제 k8s 애플리케이션의 옵션이 달라지게 됨. 추천하지 않음.
upgrade 내부 동작로직
- deployment에서 New replicaSet을 생성함
- 이전 replicaset의 pod 개수를 줄이는 동시에, new replicaset의 pod 개수를 늘림.
rollback: 이전 버전의 replicaset의 pod를 늘리고, 새로 생성한 replicaset의 pod를 줄인다.
Configuring Environment Variable in Applicatioin.
환경변수: env 를 사용한다.
- Array 형태로 선언해 사용한다.
- 일반적으로는 name / value 두 개의 필드.
- configMap이나 Secret 값을 불러와서 적용할 수 있다.
ConfigMap
pod의 정의부에 일일이 env로 값을 추가할 수 없기도 하고, 재사용성을 확보하기 위해 pod 외부로 빼내어 사용하는 값이 Configmap.
- 기본적으로는 key-value Pair로 사용됨.
- pod 정의부에 Inject 가능하다.
생성방법은 create로 직접 생성 / -f 옵션으로 yaml파일 토대로 생성/
- config-name으로 configmap 이름을 지정한다.
- --from-literal : command에 직접 정의해 넣겠다는 뜻. = 형태로. 위 사진의 예시를 보면 APP_COLOR=blue 라는 key-value Pair를 사용했다.
- --from-file : 를 지정하고, 해당 파일을 key-value로 사용하는 경우. --from-file=app_config.properties 처럼 지정할 수 있다.
configmap 정보를 실제 pod에 주입할 때
- envFrom 키워드를 사용해서 Array 형태로 데이터를 입력할 수 있다. configMap 이름을 입력하면 반영됨.
꼭 이 방법이 아니어도 env를 주입하는 방법은 여러 가지가 있음.
Configuring Sceret
- configMap은 plain text 형태로 저장한다.
- Secret은 중요한 정보를 Encoded / hashed 형태로 저장한다.
ConfigMap과 마찬가지로 생성 방법은 여러 가지가 있음.
kubectl create secret generic
형태로 생성하기 (generic이 붙는다)- --from-literal : cmd에서 직접 입력한 값을 사용한다
- --from-file : 파일 값을 사용한다.
- -f 옵션을 줘서 yaml 파일로 생성할 수 있다.
- 단, 이렇게 생성할 경우 yaml 파일에는 데이터가 평문으로 들어가 있다. 보안상 좋지 않음.
이렇게 생성할 경우, 필드를 최소한 Encoded form으로 처리해야 한다.
리눅스에서 base64로 인코딩하는 방법은 아래와 같다.
Secret 파일 확인하고, Decode 진행하기.
kubectl describe
명령어로 secret을 조회할 수 있고,
- -o yaml 형태로 파일을 출력하면 Encoded된 문자열까지 볼 수 있다.
이외에도 env에 secret을 넣을 수 있는 방법엔 여러 가지가 있음.
volume 형태로 secret을 생성할 경우 컨테이너의 경로에 파일이 생성되며, 파일 갯수는 마운트한 secret 개수와 같다. 파일을 확인할 경우 값을 알 수 있다.
Multi Container Pods
마이크로서비스 아키텍처가 등장하면서, 각각의 서비스를 scale up / down하는 작업이 수월해졌다.
하지만 마이크로서비스임에도 불구하고 특정 서비스의 경우 같이 써야 효과적인 경우가 있다. Web App + Logging Service가 그 예시.
- 둘을 굳이 합쳐서 지저분하게 코드를 수정하고 싶지 않다. 각각 개발은 별개로 진행한다.
- 한 몸처럼 같이 움직이며 동작했으면 좋겠다.
LifeCycle을 공유하는 Multi-Container Pod를 사용하는 이유.
- 같은 네트워를 사용하므로, 서로 localhost처럼 접근 가능하다
- 동일한 storage volume을 사용한다.
따라서 두 개의 서비스를 연결하기 위한 Service 오브젝트라던가 Volume Sharing 기능을 별도로 구현할 필요가 없음.
생성하는 방법은 간단하다. spec.containers로 Array 선언해서 사용하면 된다.
Design Pattern
- multi-container에서 사용하는 디자인 패턴은 크게 세 개.
- SideCar: Logging 프로그램을 웹 서비스에 붙이는 식으로 사용할 때
- Adapters / Ambassador 패턴도 있지만, CAKD 커리큘럼에서 다루는 내용이고 CKA에서는 깊이 다루지 않는다.
sidecar 방식의 멀티 컨테이너 환경이라면, 둘 다 컨테이너가 살아있어야 정상적으로 목적을 달성할 수 있음.
- 특히 logging의 경우, 로그를 만드는 애플리케이션이 살아 있어야 그 역할을 다할 수 있다. 따라서 이 경우 컨테이너 둘 중에 하나라도 죽으면, Pod 자체가 재시작한다.
그러나
- 꼭 계속 같이 실행해야만 하는 프로세스가 아닐 수도 있다. 예컨대 처음 시작할 때에는 같이 실행되지만, 목적을 다하면 먼저 종료해도 되는 프로세스.
- pulls a code / binary from a repository 코드의 경우 pod가 실행되는 시점에 한 번만 실행하면 된다.
- 또는 특정 service나 DB가 실행될 때까지 대기해야 하는 경우의 애플리케이션이 있다.
위와 같은 특징을 가진 container의 경우 InitContainers 형태로 정의해서 사용할 수 있다.
apiVersion: v1
kind: Pod
metadata:
name: myapp-pod
labels:
app: myapp
spec:
containers:
- name: myapp-container
image: busybox:1.28
command: ['sh', '-c', 'echo The app is running! && sleep 3600']
# pod가 처음 실행될 때 '한 번만' 실행하고 종료되는 프로세스. 이 예시의 경우 initContainer가 정상적으로 종료된 후에야 본래 컨테이너를 실행할 수 있다고 한다.
initContainers:
- name: init-myservice
image: busybox
command: ['sh', '-c', 'git clone <some-repository-that-will-be-used-by-application> ; done;']
When a POD is first created the initContainer is run, and the process in the initContainer must run to a completion before the real container hosting the application starts.
You can configure multiple such initContainers as well, like how we did for multi-pod containers. In that case each init container is run one at a time in sequential order.
# 멀티 컨테이너를 initContainer를 사용해 구성할 경우, initContainer는 한 번에 하나씩 순차적으로 실행된다.
# 만약 initContainer 어느 하나라도 중간에 실패할 경우, pod를 죽이고 처음부터 다시 실행한다.
If any of the initContainers fail to complete, Kubernetes restarts the Pod repeatedly until the Init Container succeeds.
apiVersion: v1
kind: Pod
metadata:
name: myapp-pod
labels:
app: myapp
spec:
containers:
- name: myapp-container
image: busybox:1.28
command: ['sh', '-c', 'echo The app is running! && sleep 3600']
initContainers:
- name: init-myservice
image: busybox:1.28
command: ['sh', '-c', 'until nslookup myservice; do echo waiting for myservice; sleep 2; done;']
- name: init-mydb
image: busybox:1.28
command: ['sh', '-c', 'until nslookup mydb; do echo waiting for mydb; sleep 2; done;']
cf. Self-healing Application
- replicaSet 또는 replicationController를 사용해서 '특정 pod 개수'를 반드시 유지하도록 할 수 있다.
- 특정 pod의 상태 체크를 위해 readiness / Liveness 라는 부가 기능을 적용할 수 있다.
- 이건 CKAD 시험에나 나오는 내용이라서 CKA에서는 다루지 않는다.