쿠버네티스에서 디플로이먼트, 레플리카셋 등 오브젝트의 spec 변경시 자동으로 업데이트를 진행한다.

이는 쿠버네티스 컨트롤러가 클러스터의 상태를 지속적으로 확인 후 필요한 오브젝트를 생성 또는 변경한다.

각 오브젝트마다 컨트롤러가 존재하며 각 컨트롤러는 명시한 상태를 유지시키려고 노력한다.

디플로이먼트(Deployment) 또한 디플로이먼트 컨트롤러가 루프를 돌면서 명시된 상태를 유지한다.

예시

환경설정

kubectl autocomplete & kubectl alias k

1
2
3
4
source <(kubectl completion bash) # setup autocomplete in bash into the current shell, bash-completion package should be installed first.
echo "source <(kubectl completion bash)" >> ~/.bashrc # add autocomplete permanently to your bash shell.
alias k=kubectl
complete -F __start_kubectl k

이미지 변경 시

sync-nginx 디플로이먼트의 이미지를 nginx:1.18로 지정

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ k create deployment my-nginx --image=nginx:1.18
deployment.apps/my-nginx created
$ k get pod,rs,deployment
NAME                           READY   STATUS    RESTARTS   AGE
pod/my-nginx-dd78ddf67-qmhg6   1/1     Running   0          10m

NAME                                 DESIRED   CURRENT   READY   AGE
replicaset.apps/my-nginx-dd78ddf67   1         1         1       10m

NAME                       READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/my-nginx   1/1     1            1           10m

$ k describe pod | grep image
  Normal  Pulling    10m   kubelet            Pulling image "nginx:1.18"
  Normal  Pulled     10m   kubelet            Successfully pulled image "nginx:1.18" in 2.1321321s

sync-nginx 디플로이먼트의 이미지를 nginx:1.19로 변경

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ k set image deployment/my-nginx nginx=nginx:1.19
deployment.apps/my-nginx image updated
$ k get pod,rs,deployment
NAME                            READY   STATUS    RESTARTS   AGE
pod/my-nginx-655998ff6d-5pqqw   1/1     Running   0          42s

NAME                                  DESIRED   CURRENT   READY   AGE
replicaset.apps/my-nginx-655998ff6d   1         1         1       42s
replicaset.apps/my-nginx-dd78ddf67    0         0         0       14m

NAME                       READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/my-nginx   1/1     1            1           14m

$ k describe pod | grep image
  Normal  Pulling    12m   kubelet            Pulling image "nginx:1.19"
  Normal  Pulled     12m   kubelet            Successfully pulled image "nginx:1.19" in 2.8686335s

위와같이 새로운 ReplicaSet이 생기면서 pod 새로 생성되었다.

리로더(Reloader)

디플로이먼트에서 참조하고있는 컨피그맵 변경시 컨테이너 내부에서 값은 변경돼있으나 보통 프로세스는 설정파일의 적용을 위해 재시작 해줘야한다.

일반적인 시스템에서 프로세스의 재시작은 큰문제가 없으나 컨테이너 환경에서는 지정된 프로세스가 죽게되면 컨테이너는 종료된다.

컨피그맵 변경시 값은 바뀌어 있으나 프로세스가 해당 파일을 적용시키지 못하는 문제가 발생한다.

이러한 문제를 Stacker/Reloader를(이하 리로더) 사용하여 해결할 수 있다.

리로더는 Stacker에서 만든 오픈소스로 커스텀 컨트롤러역할을 하며 컨피그맵, 시크릿 값변경시 디플로이먼트를 롤링업데이트 시켜준다.

요구사항

  • kubernetes ≥ 1.19은 조치없이 바로 사용가능

환경설정

kubectl autocomplete & kubectl alias k

1
2
3
4
source <(kubectl completion bash) # setup autocomplete in bash into the current shell, bash-completion package should be installed first.
echo "source <(kubectl completion bash)" >> ~/.bashrc # add autocomplete permanently to your bash shell.
alias k=kubectl
complete -F __start_kubectl k

설치

1
k apply -k https://github.com/stakater/Reloader/deployments/kubernetes

검증

1
2
3
$ k get pod
NAME                                READY   STATUS    RESTARTS   AGE
reloader-reloader-6c887d48d-pw4nz   1/1     Running   0          10m

Pod Running 중인지 확인

사용법

컨피그맵이나 시크릿을 사용하는 디플로이에 .metadata.annotations 에 값 추가

어노테이션 종류 (a | b).example은 a.exaple OR b.example 둘중 한개

  • reloader.stakater.com/auto: "true" : 디플로이먼트의 컨피그맵, 시크릿 변경시 자동으로 롤아웃
  • reloader.stakater.com/search: "true" : 디플로이먼트의 컨피그맵, 시크릿 중 reloader.stakater.com/match: "true" 어노테이션을 가진 오브젝트 변경시 롤아웃
  • (configmap | secret).reloader.stakater.com/reload: "foo" : foo라는 이름을 가진 컨피그맵 또는 시크릿 변경시 롤아웃

예시

아래 명령어를 사용하여 ConfigMap을 읽어서 값을 출력하는 두개의 디플로이먼트가 있다.

no-reloader는 리로더의 어노테이션을 사용하지 않고 use-reloader는 어노테이션을 지정해줬다.

이름과 어노테이션을 제외하고는 동일한 디플로이먼트이다.

배포

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
k apply -f - <<EOF
---
apiVersion: apps/v1
kind: Deployment
metadata:
  creationTimestamp: null    
  labels:
    app: no-reloder       
  name: no-reloder
spec:
  replicas: 1
  selector:
    matchLabels:
      app: no-reloder    
  strategy: {}
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: no-reloder
    spec:
      containers:
      - command: ["/bin/sh","-c"]
        args: ['cat /var/config.yaml >> /tmp/text.yaml && watch cat /tmp/text.yaml']
        image: busybox
        name: busybox
        resources: {}
        volumeMounts:
        - name: config
          mountPath: /var
      volumes:
        - name: config
          configMap:
            name: config
            items:
              - key: config.yaml
                path: config.yaml
---
apiVersion: apps/v1
kind: Deployment
metadata:
  creationTimestamp: null
  annotations:
    reloader.stakater.com/auto: "true" 
  labels:
    app: use-reloder       
  name: use-reloder
spec:
  replicas: 1
  selector:
    matchLabels:
      app: use-reloder      
  strategy: {}
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: use-reloder
    spec:
      containers:
      - command: ["/bin/sh","-c"]
        args: ['cat /var/config.yaml >> /tmp/text.yaml && watch cat /tmp/text.yaml']
        image: busybox
        name: busybox
        resources: {}
        volumeMounts:
        - name: config
          mountPath: /var
      volumes:
        - name: config
          configMap:
            name: config
            items:
              - key: config.yaml
                path: config.yaml
---
kind: ConfigMap
apiVersion: v1
metadata:
  name: config
data:
  config.yaml: |
    value: A
---
EOF

팟의 출력확인(이때 팟의 이름은 노드마다 다름)

1
2
3
4
5
6
7
8
9
10
11
12
$ k get pod
NAME                           READY   STATUS    RESTARTS   AGE  
no-reloder-8c54d96ff-d9csv     1/1     Running   0          3m40s
use-reloder-55b4545b4c-wvvrz   1/1     Running   0          3m40s
$ k logs use-reloder-55b4545b4c-wvvrz
Every 2.0s: cat /var/config.yaml                            2020-12-29 20:11:33

value: A
$ k logs no-reloder-8c54d96ff-d9csv
Every 2.0s: cat /var/config.yaml                            2020-12-29 20:13:44

value: A

위와같이 모두 컨피그맵에 적혀있는 value: A를 출력하고있다.

컨피그맵 변경 테스트

컨피그맵 값 변경

1
2
3
4
5
6
7
8
9
k apply -f - <<EOF
kind: ConfigMap
apiVersion: v1
metadata:
  name: config
data:
  config.yaml: |
    value: B
EOF

팟을 조회하니 use-reloader의 팟이 롤링 업데이트 진행중임을 알 수있다. 잠시 시간이 흐르면 위의 wvvrz팟을 사라지고 d58tq만 남게된다.

1
2
3
4
5
6
7
8
9
10
$ k get pod
NAME                           READY   STATUS              RESTARTS   AGE
no-reloder-8c54d96ff-d9csv     1/1     Running             0          16m
use-reloder-55b4545b4c-wvvrz   1/1     Running             0          16m
use-reloder-745b4c6cc-d58tq    0/1     ContainerCreating   0          2s
##수십초뒤
$ k get pod
NAME                          READY   STATUS    RESTARTS   AGE
no-reloder-8c54d96ff-d9csv    1/1     Running   0          20m
use-reloder-745b4c6cc-d58tq   1/1     Running   0          3m15s

출력 조회

1
2
3
4
5
6
7
8
9
10
$ k logs no-reloder-8c54d96ff-d9csv
Every 2.0s: cat /tmp/text.yaml                              2020-12-29 20:53:03

value: A

$ k logs use-reloder-745b4c6cc-d58tq

Every 2.0s: cat /tmp/text.yaml                              2020-12-29 20:54:50

value: B

어노테이션을 사용한 디플로이먼트만 값이 변경되었음을 알 수 있다.