컨피그맵

컨피그맵 (ConfigMaps)

ConfigMap은 비밀(confidential)이 아닌 데이터를 키-값 쌍으로 저장하는 Kubernetes API 객체예요. Pod는 ConfigMap을 환경 변수, 명령줄 인자, 또는 볼륨(volume)의 구성 파일로 사용할 수 있습니다.

ConfigMap을 사용하면 환경별 구성을 컨테이너 이미지에서 분리할 수 있어서, 애플리케이션을 쉽게 이식(portable)할 수 있어요.

⚠️ 주의: ConfigMap은 비밀성이나 암호화를 제공하지 않아요. 저장하려는 데이터가 기밀이라면 ConfigMap 대신 Secret을 사용하거나, 데이터를 비공개로 유지하기 위한 추가(서드파티) 도구를 사용하세요.

출처: Kubernetes 공식 문서 — ConfigMaps

동기 (Motivation)

ConfigMap을 사용해 구성 데이터를 애플리케이션 코드와 분리해서 설정하세요.

예를 들어, 개발용으로는 자신의 컴퓨터에서, 실제 트래픽 처리용으로는 클라우드에서 실행할 수 있는 애플리케이션을 개발한다고 가정해볼게요. DATABASE_HOST라는 환경 변수를 찾도록 코드를 작성합니다. 로컬에서는 그 변수를 localhost로 설정하고, 클라우드에서는 클러스터에 데이터베이스 컴포넌트를 노출하는 Kubernetes Service를 가리키도록 설정하죠. 이렇게 하면 클라우드에서 실행 중인 컨테이너 이미지를 가져와 필요하면 로컬에서 정확히 같은 코드를 디버깅할 수 있어요.

참고: ConfigMap은 대량의 데이터를 담도록 설계되지 않았어요. ConfigMap에 저장된 데이터는 1 MiB를 초과할 수 없습니다. 이 제한보다 큰 설정을 저장해야 한다면 볼륨을 마운트하거나 별도의 데이터베이스나 파일 서비스를 사용하는 것을 고려해보세요.

ConfigMap 객체 (ConfigMap object)

ConfigMap은 다른 객체가 사용할 구성을 저장하게 해주는 API 객체예요. 대부분의 Kubernetes 객체가 spec을 가지는 것과 달리, ConfigMap은 databinaryData 필드를 가집니다. 이 필드들은 키-값 쌍을 값으로 받아요. data 필드와 binaryData 필드는 모두 선택 사항입니다. data 필드는 UTF-8 문자열을 담도록 설계되었고, binaryData 필드는 base64로 인코딩된 문자열로 바이너리 데이터를 담도록 설계됐어요.

ConfigMap의 이름은 유효한 DNS 서브도메인 이름이어야 합니다.

data 또는 binaryData 필드의 각 키는 영숫자 문자, -, _, .로 구성되어야 해요. data에 저장된 키는 binaryData 필드의 키와 겹치면 안 됩니다.

v1.19부터 ConfigMap 정의에 immutable 필드를 추가해 불변(immutable) ConfigMap을 만들 수 있어요.

ConfigMap과 Pod (ConfigMaps and Pods)

ConfigMap을 참조하고, 그 ConfigMap의 데이터를 기반으로 Pod의 컨테이너들을 구성하는 Pod 스펙을 작성할 수 있어요. Pod와 ConfigMap은 같은 네임스페이스에 있어야 합니다.

참고: 정적 Pod(static Pod)의 스펙은 ConfigMap이나 다른 API 객체를 참조할 수 없습니다.

다음은 단일 값을 가진 키와, 구성 포맷의 일부처럼 보이는 값을 가진 키가 있는 예시 ConfigMap이에요.

apiVersion: v1
kind: ConfigMap
metadata:
  name: game-demo
data:
  # property-like keys; each key maps to a simple value
  player_initial_lives: "3"
  ui_properties_file_name: "user-interface.properties"

  # file-like keys
  game.properties: |
    enemy.types=aliens,monsters
    player.maximum-lives=5
  user-interface.properties: |
    color.good=purple
    color.bad=yellow
    allow.textmode=true

ConfigMap을 사용해서 Pod 안의 컨테이너를 구성하는 방법은 네 가지가 있어요.

  1. 컨테이너의 commandargs 안에서
  2. 컨테이너의 환경 변수로
  3. 애플리케이션이 읽을 수 있도록 읽기 전용 볼륨에 파일 추가
  4. Kubernetes API를 사용해 ConfigMap을 읽는 코드를 Pod 안에서 실행

이 서로 다른 방법들은 각각 소비되는 데이터를 모델링하는 방식이 달라요. 처음 세 방법에서는 kubelet이 Pod의 컨테이너를 시작할 때 ConfigMap의 데이터를 사용합니다.

네 번째 방법은 ConfigMap과 그 데이터를 읽는 코드를 직접 작성해야 한다는 뜻이에요. 하지만 Kubernetes API를 직접 사용하기 때문에, 애플리케이션이 ConfigMap이 변경될 때마다 업데이트를 구독하고 그에 반응할 수 있습니다. Kubernetes API에 직접 접근하면 다른 네임스페이스의 ConfigMap에도 접근할 수 있어요.

여기 game-demo의 값을 사용해 Pod를 구성하는 예시 Pod가 있어요.

apiVersion: v1
kind: Pod
metadata:
  name: configmap-demo-pod
spec:
  containers:
    - name: demo
      image: alpine
      command: ["sleep", "3600"]
      env:
        # Define the environment variable
        - name: PLAYER_INITIAL_LIVES # Notice that the case is different here
                                     # from the key name in the ConfigMap.
          valueFrom:
            configMapKeyRef:
              name: game-demo           # The ConfigMap this value comes from.
              key: player_initial_lives # The key to fetch.
        - name: UI_PROPERTIES_FILE_NAME
          valueFrom:
            configMapKeyRef:
              name: game-demo
              key: ui_properties_file_name
      volumeMounts:
      - name: config
        mountPath: "/config"
        readOnly: true
  volumes:
  # You set volumes at the Pod level, then mount them into containers inside that Pod
  - name: config
    configMap:
      # Provide the name of the ConfigMap you want to mount.
      name: game-demo
      # An array of keys from the ConfigMap to create as files
      items:
      - key: "game.properties"
        path: "game.properties"
      - key: "user-interface.properties"
        path: "user-interface.properties"

ConfigMap은 한 줄짜리 속성 값과 여러 줄짜리 파일 형식 값을 구분하지 않아요. 중요한 건 Pod와 다른 객체가 그 값을 어떻게 소비하느냐예요.

이 예에서 볼륨을 정의하고 demo 컨테이너 안에 /config로 마운트하면, ConfigMap에 키가 4개 있어도 /config/game.properties/config/user-interface.properties라는 두 개의 파일이 생성됩니다. 이는 Pod 정의가 volumes 섹션에서 items 배열을 지정하기 때문이에요. items 배열을 생략하면 ConfigMap의 모든 키가 키와 같은 이름의 파일이 되어 4개의 파일이 생깁니다.

ConfigMap 사용하기 (Using ConfigMaps)

ConfigMap은 데이터 볼륨으로 마운트될 수 있어요. 또한 Pod에 직접 노출되지 않고 시스템의 다른 부분에서 사용될 수도 있습니다. 예를 들어 ConfigMap은 시스템의 다른 부분이 구성에 사용해야 하는 데이터를 담을 수 있어요.

ConfigMap을 사용하는 가장 일반적인 방법은 같은 네임스페이스의 Pod에서 실행되는 컨테이너의 설정을 구성하는 것이에요. ConfigMap을 별도로 사용할 수도 있습니다.

예를 들어 ConfigMap을 기반으로 동작을 조정하는 애드온이나 오퍼레이터(operator)를 접할 수 있어요.

Pod에서 ConfigMap을 파일로 사용 (Using ConfigMaps as files from a Pod)

Pod의 볼륨에서 ConfigMap을 소비하려면:

  1. ConfigMap을 생성하거나 기존 것을 사용하세요. 여러 Pod가 같은 ConfigMap을 참조할 수 있어요.
  2. Pod 정의를 수정해 .spec.volumes[] 아래에 볼륨을 추가하세요. 볼륨 이름은 무엇이든지 상관없고, 참조할 ConfigMap 객체를 가리키도록 .spec.volumes[].configMap.name 필드를 설정하세요.
  3. ConfigMap이 필요한 각 컨테이너에 .spec.containers[].volumeMounts[]를 추가하세요. .spec.containers[].volumeMounts[].readOnly = true를 지정하고, ConfigMap이 나타나길 원하는 사용하지 않는 디렉터리 이름으로 .spec.containers[].volumeMounts[].mountPath를 지정하세요.
  4. 프로그램이 그 디렉터리의 파일을 찾도록 이미지나 명령줄을 수정하세요. ConfigMap data 맵의 각 키가 mountPath 아래의 파일 이름이 됩니다.

볼륨에 ConfigMap을 마운트하는 Pod 예시:

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
  - name: mypod
    image: redis
    volumeMounts:
    - name: foo
      mountPath: "/etc/foo"
      readOnly: true
  volumes:
  - name: foo
    configMap:
      name: myconfigmap

사용하려는 각 ConfigMap은 .spec.volumes에서 참조되어야 해요.

Pod에 컨테이너가 여러 개 있다면 각 컨테이너는 자신의 volumeMounts 블록이 필요하지만, ConfigMap당 .spec.volumes는 하나만 있으면 됩니다.

마운트된 ConfigMap은 자동으로 업데이트됩니다 (Mounted ConfigMaps are updated automatically)

현재 볼륨에서 소비되는 ConfigMap이 업데이트되면, 투영된(projected) 키도 결국 업데이트돼요. kubelet은 매 주기적 동기화 시 마운트된 ConfigMap이 최신인지 확인합니다. 하지만 kubelet은 ConfigMap의 현재 값을 얻기 위해 로컬 캐시를 사용해요. 캐시 유형은 KubeletConfiguration 구조체의 configMapAndSecretChangeDetectionStrategy 필드로 구성할 수 있어요. ConfigMap은 watch(기본값), ttl 기반, 또는 모든 요청을 API 서버로 직접 리다이렉트하여 전파될 수 있습니다. 결과적으로 ConfigMap이 업데이트된 시점부터 새 키가 Pod에 투영되는 시점까지의 총 지연은 kubelet 동기화 주기 + 캐시 전파 지연만큼 길어질 수 있어요. 캐시 전파 지연은 선택한 캐시 유형에 따라 달라집니다(watch 전파 지연, 캐시의 ttl, 또는 각각 0과 같아요).

환경 변수로 소비되는 ConfigMap은 자동으로 업데이트되지 않으며 Pod 재시작이 필요합니다.

참고: ConfigMap을 subPath 볼륨 마운트로 사용하는 컨테이너는 ConfigMap 업데이트를 받지 못해요.

ConfigMap을 환경 변수로 사용 (Using ConfigMaps as environment variables)

Pod에서 ConfigMap을 환경 변수로 사용하려면:

  1. Pod 스펙의 각 컨테이너에 대해, 사용하려는 각 ConfigMap 키의 환경 변수를 env[].valueFrom.configMapKeyRef 필드에 추가하세요.
  2. 프로그램이 지정된 환경 변수에서 값을 찾도록 이미지 및/또는 명령줄을 수정하세요.

Pod 환경 변수로 ConfigMap을 정의하는 예시:

다음 ConfigMap(myconfigmap.yaml)은 usernameaccess_level 두 속성을 저장합니다.

apiVersion: v1
kind: ConfigMap
metadata:
  name: myconfigmap
data:
  username: k8s-admin
  access_level: "1"

다음 명령이 ConfigMap 객체를 생성합니다.

kubectl apply -f myconfigmap.yaml

다음 Pod는 ConfigMap의 내용을 환경 변수로 소비합니다.

apiVersion: v1
kind: Pod
metadata:
  name: env-configmap
spec:
  containers:
    - name: app
      command: ["/bin/sh", "-c", "printenv"]
      image: busybox:latest
      envFrom:
        - configMapRef:
            name: myconfigmap

envFrom 필드는 그 안에 중첩된 소스에서 환경 변수를 생성하도록 Kubernetes에 지시해요. 안쪽의 configMapRef는 이름으로 ConfigMap을 참조하고 모든 키-값 쌍을 선택합니다. Pod를 클러스터에 추가한 다음 printenv 명령의 출력을 보려면 로그를 검색하세요. ConfigMap의 두 키-값 쌍이 환경 변수로 설정되었는지 확인할 수 있어요.

kubectl apply -f env-configmap.yaml
kubectl logs pod/env-configmap

출력은 다음과 비슷합니다.

...
username: "k8s-admin"
access_level: "1"
...

때로는 Pod가 ConfigMap의 모든 값에 접근할 필요가 없을 수도 있어요. 예를 들어 ConfigMap의 username 값만 사용하는 다른 Pod가 있을 수 있죠. 이 사용 사례에서는 env.valueFrom 구문을 대신 사용할 수 있는데, 이 구문은 ConfigMap에서 개별 키를 선택하게 해줍니다. 환경 변수의 이름도 ConfigMap 안의 키와 다를 수 있어요. 예를 들면:

apiVersion: v1
kind: Pod
metadata:
  name: env-configmap
spec:
  containers:
  - name: envars-test-container
    image: nginx
    env:
    - name: CONFIGMAP_USERNAME
      valueFrom:
        configMapKeyRef:
          name: myconfigmap
          key: username

이 매니페스트로 만든 Pod에서는 환경 변수 CONFIGMAP_USERNAME이 ConfigMap의 username 값으로 설정된 것을 볼 수 있어요. ConfigMap 데이터의 다른 키는 환경에 복사되지 않습니다.

Pod의 환경 변수 이름에 허용되는 문자의 범위는 제한되어 있다는 점이 중요해요. 어떤 키가 규칙을 충족하지 못하면, Pod가 시작되는 것은 허용되지만 그 키는 컨테이너에서 사용할 수 없게 됩니다.

불변 ConfigMap (Immutable ConfigMaps)

FEATURE STATE: Kubernetes v1.21 [stable]

Kubernetes의 불변 Secret과 ConfigMaps(Immutable Secrets and ConfigMaps) 기능은 개별 Secret과 ConfigMap을 불변으로 설정하는 옵션을 제공해요. ConfigMap을 대규모로 사용하는 클러스터(최소 수만 개의 고유 ConfigMap-to-Pod 마운트)에서 데이터 변경을 방지하면 다음과 같은 이점이 있습니다.

  • 애플리케이션 중단을 일으킬 수 있는 우발적(또는 원치 않는) 업데이트로부터 보호해요.
  • 불변으로 표시된 ConfigMap에 대한 watch를 닫아 kube-apiserver의 부하를 크게 줄여 클러스터 성능을 향상시켜요.

immutable 필드를 true로 설정하면 불변 ConfigMap을 만들 수 있어요.

apiVersion: v1
kind: ConfigMap
metadata:
  ...
data:
  ...
immutable: true

일단 ConfigMap이 불변으로 표시되면, 이 변경을 되돌리거나 databinaryData 필드의 내용을 변경할 수 없어요. 삭제하고 다시 생성하는 것만 가능합니다. 기존 Pod가 삭제된 ConfigMap에 대한 마운트 지점을 유지하므로, 이 Pod들을 다시 생성하는 것이 권장돼요.

더 알아보기 (Learn more)