본문 바로가기
IT/Kubernetes

[Kubernetes] ConfigMap

by 물통꿀꿀이 2019. 5. 27.

이번 포스팅은 k8s의 ConfigMap에 대해 알아보려고 한다.


ConfigMap

ConfigMap은 Container의 Application에서 사용하는 설정 값을 분리 하여 소스 코드 상에서 직접 사용하던 설정 값들을 유지한다. 

이렇게 ConfigMap에서 따로 유지하는 이유는 여러 가지가 있겠지만 그 중 Application에서 자주 변경되는 설정 값으로 인해 자주 재배포해야하기 때문이다.

다시 돌아와서 k8s에서 ConfigMap은 말 그대로 설정 값을 Mapping 하고 있는 k8s 별도의 리소스로 Key-Value 형태로 설정 값을 가지고 있다.

(ConfigMap은 간단한 문자부터 큰 설정 파일까지 값으로 가질 수 있다.)


그림 1. ConfigMap



그림 1에서 확인 할 수 있듯이 Pod는 ConfigMap과 서로 독립적인 리소스이다. 때문에 Pod에서 ConfigMap을 사용하는 방식은 내부의 다양한 옵션(환경 변수, Volume 등)을 사용하여 ConfigMap에 접근 할 수 있다. 물론 Pod에서 직접 ConfigMap에 접근하여 사용할 수 있지만 Pod의 독립성을 위해 추천하는 방법은 아니다. 


그림 2. ConfigMap Usage


위에서 설명했듯이 ConfigMap은 독립적인 리소스로 동일한 Pod라도 여러개의 ConfigMap을 선택하여 사용할 수 있다. 

이러한 점을 그림 2에서 확인 할 수 있는데 하나의 Pod로 형상에 따라 다양한 환경 변수를 가지고 있는 ConfigMap에 접근하여 사용할 수 있다. (ConfigMap만 변경하여 Pod를 손쉽게 테스트 할 수 있다.


ConfigMap을 만드는 방식은 아래와 같다.

kubectl create configmap <map-name> <data-source>


ConfigMap은 기존과는 다르게 kubectl로 생성하는 방식이 조금 다르다. (create configmap 명령어를 사용)

- map-name : ConfigMap 이름

- data-source : ConfigMap의 key-value를 구성하는 값으로 directory, file, literal value 값이 올 수 있다.


그러면 data-source의 각 방식에 대해 알아보자.

1) From directory

// There are 2 files in /somewhere path

// game.properties

enemies=aliens

lives=3

enemies.cheat=true

enemies.cheat.level=noGoodRotten

secret.code.passphrase=UUDDLRLRBABAS

secret.code.allowed=true

secret.code.lives=30


// ui.properties

color.good=purple

color.bad=yellow

allow.textmode=true

how.nice.to.look=fairlyNice

----


kubectl create configmap game-config --from-file=/somewhere

그림 3. ConfigMap from directory


그림 3을 보면 만들어진 ConfigMap에 Data에 각각의 파일의 내용이 담겨 있는 것을 확인 할 수 있다.


2) From file

kubectl create configmap game-config --from-file=/somewhere/game.properties

그림 4. ConfigMap from file


그림 4에서는 파일 하나만 가지고 ConfigMap이 생성된 것을 확인 할 수 있다. 

그런데 파일로 ConfigMap을 만드는 디렉토리를 통해 만드는 것과 같이 동일한 ConfigMap에 "kubectl create configmap" 작업을 하면 ConfigMap에 파일이 계속 추가된다. 

(즉, ui.properties를 추가하면 디렉토리와 동일하게 game.properties, ui.properties 값이 포함된다.)


3) From literal value

 kubectl create configmap special-config --from-literal=special.how=very --from-literal=special.type=charm

그림 5. ConfigMap from literal value


파일을 사용하지 않고 Command에서 직접 ConfigMap을 위한 key-value를 사용하였다. 그 결과 디렉토리, 파일을 사용하는 것과 마찬가지로 ConfigMap이 생성된 것을 확인 할 수가 있다.


4) Combining all types

이제 까지 알아본 3가지 방식을 동시에 합쳐서 ConfigMap을 만들 수도 있다. (아래와 같이 추가적인 옵션이 필요한 것이 아니라 나열하면 된다.)

 kubectl create configmap new-config \

 --from-file=/somewhere

 --from-file=a.properties

 --from-literal=special.type=charm

그림 6. ConfigMap Structure (by Kubernetes in Action)


이제까지 정의한 ConfigMap의 값을 Container에서는 어떻게 사용할 수 있을까?

1) Container Environment Variables

Container에 환경 변수를 설정하는 방식으로 가장 간단하다. 

 kubectl create configmap special-config --from-literal=special.how=very

apiVersion: v1

kind: Pod

metadata:

  name: dapi-test-pod

spec:

  containers:

    - name: test-container

      image: k8s.gcr.io/busybox

      command: [ "/bin/sh", "-c", "env" ]

      env:

        # Define the environment variable

        - name: SPECIAL_LEVEL_KEY

          valueFrom:

            configMapKeyRef:

              # The ConfigMap containing the value you want to assign to SPECIAL_LEVEL_KEY

              name: special-config

              # Specify the key associated with the value

              key: special.how

  restartPolicy: Never 


위의 매니페스트 파일에서 확인 할 수 있듯이, special-config 이름으로 생성한 ConfigMap을 Pod에서 valueFrom 방식으로 가져온다.

(valueFrom.configMapKeyRef.name과 valueFrom.configMapKeyRef.key 참조)


그런데 만약 Pod에서 정의한 ConfigMap이 존재하지 않으면 어떻게 될 것인가?

일반적으로는 해당 ConfigMap을 참조하는 Container는 실행되지 않는다. (즉, Pod의 해당 ConfigMap을 참조하지 않는 다른 Container는 잘 실행된다.)


2) Configure all key-value pairs in a ConfigMap as container environment variables

이전에 알아본 1)번과 같이 ConfigMap에서 사용할 값을 일일이 지정하다 보면 잘못된 값을 참조할 수도 있다. (휴먼 에러 등등)

그래서 k8s는 ConfigMap의 모든 항목을 환경 변수로 표시할 수 있도록 한다.

apiVersion: v1

kind: ConfigMap

metadata:

  name: special-config

  namespace: default

data:

  SPECIAL_LEVEL: very

  SPECIAL_TYPE: charm 

apiVersion: v1

kind: Pod

metadata:

  name: dapi-test-pod

spec:

  containers:

    - name: test-container

      image: k8s.gcr.io/busybox

      command: [ "/bin/sh", "-c", "env" ]

      envFrom:

      - configMapRef:

          name: special-config

  restartPolicy: Never 


위의 매니페스트 파일은 special-config 이름으로 만들어진 ConfigMap을 Pod에서 envFrom으로 접근하고 있다. envFrom을 사용하게 되면 해당 Container는 ConfigMap에서 정의한 모든 값을 가진다.

그림 7. All key-value in Pod


추가로, envFrom.prefix가 포함되면 ConfigMap에서 모든 값을 가져오는 대신 prefix에 포함되는 값만 추려서 가져올 수 있다.


3) Add ConfigMap data to a volume

환경 변수를 Container에 담는 것은 ConfigMap에 존재하는 값이 적을 때이다. 반대로 환경 변수 값이 많다면 ConfigMap Volume을 사용 할 수 있다.

(물론 적은 값도 사용 할 수 있지만 대게 큰 설정 파일을 Container에게 전달하기 위함이다.)


하나의 예시로 ConfigMap Volume을 사용하는 Nginx Container를 만들어보면 다음과 같다.

server {

    ...

    location / {

        root   /usr/share/nginx/html;

        index  index.html index.htm;

    }

color.good=purple

color.bad=yellow

allow.textmode=true

how.nice.to.look=fairlyNice 


위의 설정은 각각 Nginx 설정 파일(위)과 임의로 만든 설정 파일(아래)이다. 이 각각의 파일을 위에서 알아본 바와 같이 하나의 ConfigMap으로 묶으면 아래 그림과 같다.

그림 8. ConfigMap (+ Nginx)


이렇게 만들어진 ConfigMap을 Volume으로 생성하여 Container에서 사용하기 위해서는 Pod 매니페스트 파일에 각각 설정이 필요하다.

apiVersion: v1

kind: Pod

...

spec:

  containers:

  - image: luksa/fortune:env

    ...

  - image: nginx:alpine

    name: web-server

    volumeMounts:

    ...

    - name: config

      mountPath: /etc/nginx/conf.d

      ...

  volumes:

  ...

  - name: config

    configMap:

      name: env-config 


Pod 매니페스트 파일을 확인해보면 nginx Container의 mount 경로는 /etc/nginx/conf.d 이다. 이는 nginx 자체적으로 conf.d의 하위 디렉토리의 conf 파일을 포함하기 때문이다.

또한 Volume은 configMap Volume을 생성할 때 기존에 만들어 놓은 ConfigMap을 참조하여 생성된다. 그러므로 Nginx Container는 Volume을 통해 ConfigMap에 접근이 가능해진다.


그런데 이렇게 사용함에 있어서 문제점은 Nginx는 Nginx 설정 파일만 필요한데 불필요한 설정 파일까지 포함된다는 것이다. (ConfigMap에 설정 파일이 합쳐져 있기 때문)

물론 ConfigMap을 각각 만들어도 상관없지만 추천할 만한 방법은 아니다.

대신에 k8s에서는 특정 ConfigMap만 사용할 수 있는 방법이 존재한다.

  volumes:

  - name: config

    configMap:

      name: env-config 

      items:

      - key: ...

         path: ...


위의 Volume을 설정할 때 items를 추가해주면 된다. 

- key : ConfigMap에 포함되어 있는 Key

- path : Volume에 저장되는 파일 이름

결과적으로 Container에서는 Mount 경로에서 설정 파일이 path로 보인다. 즉 path가 test.conf라면 Container에서 {Mount}/{path}로 접근 할 수 있다.


ConfigMap Volume의 처음으로 돌아가서 Container에서 Volume을 Mount하는 방식으로 접근할 때 발생하는 문제점이 있다. 바로 Mount 할 지점에 기존에 파일이 있을 경우이다. Mount가 되면 해당 Volume을 볼 수 있지만 그렇다면 기존 파일들은 어떻게 되는 것일까?

리눅스에서는 보통 파일이 사라지는 것이 아니라 숨겨진 파일로 살아만 있다. (즉, 숨겨지는 것 뿐만 아니라 접근이 되지 않는다.) 때문에 k8s에서는 Mount를 할 때 이를 회피할 수 있는 옵션이 있다.

   volumeMounts:

   - name: myvolume

      mountPath: /etc/someconfig.conf

      subPath: myconfig.conf


바로 subPath 옵션을 사용하는 것이다. 그렇기 때문에 Mount 지점을 파일로 하고 ConfigMap에서 필요한 파일만 가져온다. (전체 Volume을 가져오진 않는다.)


또한 접근 권한과 관련하여 ConfigMap은 Volume을 생성할 때 Permission을 부여할 수 있다.

   volumes:

  - name: config

    configMap:

      name: env-config 

      defaultMode: "6600"

기본적으로는 644 권한을 가지고 있지만 defaultMode를 통해 접근 권한이 있는 리소스(사용자, 그룹 등)만 접근 할 수 있다.


ConfigMap Update

위에서도 언급했었지만 ConfigMap의 가장 큰 장점은 ConfigMap만 수정하면 관련된 모든 Container는 변경할 필요가 없다. (ConfigMap이 수정되면 Volume이 자동적으로 업데이트 되기 때문에)

물론 Container에서 새로운 환경 변수 값을 적용하기 위해서는 reload 과정이 필요하다. 그런데 Volume을 업데이트 해야하기 때문에 1분 정도 동기화 시간이 걸리기도 한다.


이러한 ConfigMap Update 분명 공수가 많이 들지 않는 좋은 방법이기는 하지만 고려해야 할 점은 Container의 Application이 과연 reload를 지원하느냐이다.

지원하지 않는다면 굳이 Application이 동작하는 동안에 ConfigMap Update를 할 필요가 없다. (멈추고 ConfigMap 수정 이후 재시작하는게 더 나을 수 있다.)


Reference

https://kubernetes.io/docs/tasks/configure-pod-container/configure-pod-configmap/

https://matthewpalmer.net/kubernetes-app-developer/articles/ultimate-configmap-guide-kubernetes.html

'IT > Kubernetes' 카테고리의 다른 글

[Kubernetes] Headless Service  (0) 2019.06.07
Docker, Pod 인자 전달 비교  (0) 2019.05.27
[Kubernetes] Internals - Pause Container  (0) 2019.05.16
[Kubernetes] Internals - Init Pod  (0) 2019.05.16
[Kubernetes] Namespace  (0) 2019.05.14

댓글