Kubernetes에서는 여러 호스트에서 컨테이너를 실행한다. 컨테이너는 어떤 어플리케이션을 실행하게 될 텐데, 내 컴퓨터에서라면 설정 파일이 로컬에 있으므로 어플리케이션에 설정을 제공하는 것이 간단하지만, 어떤 호스트에서 동작할지 모르는 컨테이너에 설정을 어떻게 제공하는가가 문제가 된다. 이럴 때 Kubernetes의 Configmap을 사용해볼 수 있다.

Configmap의 작성과 간단한 테스트

기본적으로 이런 모양을 지니게 된다. 리소스 이름을 Configmap이다.

apiVersion: v1
kind: ConfigMap
metadata:
  name: testconfigmap
  namespace: default
data:
  testvalue: "1"
  optionaltestvalue: "optional"
  testfile: |
      address=0.0.0.0
      port=8080      

간단히 이런 식으로 작성하고 등록하면 다음과 같은 화면을 볼 수 있다.

$ kubectl create -f testconfigmap.yaml
configmap/testconfigmap created
$ kubectl get configmap
NAME                  DATA   AGE
testconfigmap         2      6s

이를 사용해서 컨테이너에 데이터를 전달해 보자.

apiVersion: v1
kind: Pod
metadata:
  name: nginx
  labels:
    env: test
spec:
  containers:
  - name: nginx
    image: nginx
    imagePullPolicy: IfNotPresent
    volumeMounts:
    - name: testconfig
      mountPath: /opt/test
  volumes:
  - name: testconfig
    configMap:
      name: testconfigmap
      items:
      - key: testfile
        path: testfilepath

volumes의 Configmap에서는 testconfigmap의 data 중, 키 testfile을 testfilepath라는 경로로 매핑한다. 이 Configmap은 testconfig라는 볼륨으로 선언되어 있으므로, 이를 containers의 volumeMounts에서 name으로 사용한다. mountPath는 어느 경로에 마운트할지를 정한다.

따라서 /opt/test/testfilepath에 파일이 마운트가 될 것이다.

이 팟을 생성해서 실제로 보면 다음과 같다.

$ kubectl create -f testnginx.yaml
pod/nginx created
kubectl get pod
NAME                                        READY   STATUS              RESTARTS   AGE
nginx                                       1/1     Running             0          61s
$ kubectl exec -t -i nginx /bin/bash
$ cat /opt/test/testfilepath
address=0.0.0.0
port=8080

Configmap testconfigmaptestfile 내용이 그대로 파일로 지정된 것을 확인할 수 있다.

Configmap을 환경 변수에 연결하기

이번엔 Configmap의 내용을 환경 변수에 연결해보자. 먼저 간단한 팟 nginx2를 만들자.

apiVersion: v1
kind: Pod
metadata:
  name: nginx2
  labels:
    env: test
spec:
  containers:
  - name: nginx2
    image: nginx
    imagePullPolicy: IfNotPresent
    env:
    - name: testenv
      value: THIS_IS_TEST_VALUE
    - name: testenvfromconfigmap
      valueFrom:
        configMapKeyRef:
          name: testconfigmap
          key: testvalue
    - name: testoptionalenvfromconfigmap
      valueFrom:
        configMapKeyRef:
          name: testconfigmap
          key: optionaltestvalue
    - name: testoptionalenvfromconfigmap2
      valueFrom:
        configMapKeyRef:
          name: testconfigmap
          key: optionaltestvalue2
          optinal: true

env는 name/value 페어를 이룬다. value는 바로 매핑해도 되고 valueFrom을 통해 어딘가로부터 값을 가져오도록 지정할 수도 있다. 우선 여기서는 Configmap에서 가져오는 것만 보면, 그 방법으로는 configMapkeyRef를 사용하면 된다. 환경 변수의 이름은 testenvfromconfigmap이 되고, configMapKeyRef.name은 실제 Configmap의 이름, configMapKeyRef.key는 Configmap의 키가 된다.

컨테이너를 생성한 후 다음과 같이 확인해 보자.

$ kubectl describe pod nginx2
Name:         nginx2
Namespace:    default
Priority:     0
...
Containers:
    ...
    Ready:          True
    Restart Count:  0
    Environment:
      testenv:               THIS_IS_TEST_VALUE
      testenvfromconfigmap:           <set to the key 'testvalue' of config map 'testconfigmap'>           Optional: false
      testoptionalenvfromconfigmap:   <set to the key 'optionaltestvalue' of config map 'testconfigmap'>   Optional: false
      testoptionalenvfromconfigmap2:  <set to the key 'optionaltestvalue2' of config map 'testconfigmap'>  Optional: true
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from default-token-5g2zn (ro)
    ...

$ kubectl exec -t -i nginx2 /bin/bash
$ echo $testenv
THIS_IS_TEST_VALUE
$ echo $testenvfromconfigmap
1

위와 같이 환경변수 testenvfromconfigmap가 1의 값을 가지는 것을 볼 수 있다. optional은 값이 없어도 되는 경우를 뜻하는데, 위 케이스에서는 Configmap의 testoptionalenvfromconfigmap2 키가 없어도 컨테이너를 생성하는 것에 아무런 문제가 없도록 지정했다. 만약 저 optional을 지정하지 않았다면?

$ kubectl describe pod nginx2
Name:         nginx2
Namespace:    default
...
...
Events:
  Type     Reason     Age              From                 Message
  ----     ------     ----             ----                 -------
  ...
  ...
  Warning  Failed     7s (x2 over 7s)  kubelet, node-00005  Error: couldn't find key optionaltestvalue2 in ConfigMap default/testconfigmap

Configmap 심화과정

Configmap이 업데이트되는 경우?

Configmap이 업데이트되는 경우 해당 Configmap을 마운트한 팟에서는 결국 업데이트된 대로 내용이 변경된다. 하지만 업데이트 즉시 팟에 반영되지는 않는다.

$ cat /opt/test/testfilepath
address=0.0.0.0
port=8080
$ cat /opt/test/testfilepath
address=0.0.0.0
port=8081

같은 컨테이너에서 cat으로 파일 내용을 찍다가 변경되는 순간을 적어 보았다. 변경은 Kubernetes의 각 노드에 설치되는 kubelet이 처리해 주는데, 바로 변경해주지는 않고 적당한 TTL 이후 변경해 준다고 한다. 특히나 inotify등으로 변경점을 바로 캐치할 수 있으므로, configmap을 사용하는 컨테이너가 변경점을 바로 적용하여 실행하도록 어플리케이션을 잘성할 수도 있겠다.

As a result, the total delay from the moment when the ConfigMap is updated to the moment when new keys are projected to the pod can be as long as kubelet sync period (1 minute by default) + ttl of ConfigMaps cache (1 minute by default) in kubelet.

특히 알아둘 것은 subPath로 Configmap을 지정한 경우 자동 업데이트가 되지 않는다는 것이다. 따라서 최대한 subPath를 사용하지 않는 것이 좋겠다.