컨피그맵(ConfigMap)
컨피그맵은 키-값 쌍으로 기밀이 아닌 데이터를 저장하는 데 사용하는 API 오브젝트이다. 파드는 볼륨에서 환경 변수, 커맨드-라인 인수 또는 구성 파일로 컨피그맵을 사용할 수 있다.
컨피그맵을 사용하면 컨테이너 이미지에서 환경별 구성을 분리하여, 애플리케이션을 쉽게 이식할 수 있다.
주의: 컨피그맵은 보안 또는 암호화를 제공하지 않는다. 저장하려는 데이터가 기밀인 경우, 컨피그맵 대신 시크릿(Secret) 또는 추가(써드파티) 도구를 사용하여 데이터를 비공개로 유지하자.
사용 동기
애플리케이션 코드와 별도로 구성 데이터를 설정하려면 컨피그맵을 사용하자.
예를 들어, 자신의 컴퓨터(개발용)와 클라우드(실제 트래픽 처리)에서
실행할 수 있는 애플리케이션을 개발한다고 가정해보자.
DATABASE_HOST
라는 환경 변수를 찾기 위해 코드를 작성한다.
로컬에서는 해당 변수를 localhost
로 설정한다. 클라우드에서는, 데이터베이스
컴포넌트를 클러스터에 노출하는 쿠버네티스 서비스를
참조하도록 설정한다.
이를 통해 클라우드에서 실행 중인 컨테이너 이미지를 가져와
필요한 경우 정확히 동일한 코드를 로컬에서 디버깅할 수 있다.
컨피그맵은 많은 양의 데이터를 보유하도록 설계되지 않았다. 컨피그맵에 저장된 데이터는 1MiB를 초과할 수 없다. 이 제한보다 큰 설정을 저장해야 하는 경우, 볼륨을 마운트하는 것을 고려하거나 별도의 데이터베이스 또는 파일 서비스를 사용할 수 있다.
컨피그맵 오브젝트
컨피그맵은 다른 오브젝트가 사용할 구성을 저장할 수 있는
API 오브젝트이다.
spec
이 있는 대부분의 쿠버네티스 오브젝트와 달리, 컨피그맵에는 data
및 binaryData
필드가 있다. 이러한 필드는 키-값 쌍을 값으로 허용한다. data
필드와
binaryData
는 모두 선택 사항이다. data
필드는
UTF-8 바이트 시퀀스를 포함하도록 설계되었으며 binaryData
필드는 바이너리
데이터를 base64로 인코딩된 문자열로 포함하도록 설계되었다.
컨피그맵의 이름은 유효한 DNS 서브도메인 이름이어야 한다.
data
또는 binaryData
필드 아래의 각 키는
영숫자 문자, -
, _
또는 .
으로 구성되어야 한다. data
에 저장된 키는
binaryData
필드의 키와 겹치지 않아야 한다.
v1.19부터 컨피그맵 정의에 immutable
필드를 추가하여
변경할 수 없는 컨피그맵을 만들 수 있다.
컨피그맵과 파드
컨피그맵을 참조하는 파드 spec
을 작성하고 컨피그맵의 데이터를
기반으로 해당 파드의 컨테이너를 구성할 수 있다. 파드와 컨피그맵은
동일한 네임스페이스에 있어야 한다.
다음은 단일 값을 가진 키와, 값이 구성 형식의 일부처럼 보이는 키를 가진 컨피그맵의 예시이다.
apiVersion: v1
kind: ConfigMap
metadata:
name: game-demo
data:
# 속성과 비슷한 키; 각 키는 간단한 값으로 매핑됨
player_initial_lives: "3"
ui_properties_file_name: "user-interface.properties"
# 파일과 비슷한 키
game.properties: |
enemy.types=aliens,monsters
player.maximum-lives=5
user-interface.properties: |
color.good=purple
color.bad=yellow
allow.textmode=true
컨피그맵을 사용하여 파드 내부에 컨테이너를 구성할 수 있는 네 가지 방법이 있다.
- 컨테이너 커맨드와 인수 내에서
- 컨테이너에 대한 환경 변수
- 애플리케이션이 읽을 수 있도록 읽기 전용 볼륨에 파일 추가
- 쿠버네티스 API를 사용하여 컨피그맵을 읽는 파드 내에서 실행할 코드 작성
이러한 방법들은 소비되는 데이터를 모델링하는 방식에 따라 다르게 쓰인다. 처음 세 가지 방법의 경우, kubelet은 파드의 컨테이너를 시작할 때 컨피그맵의 데이터를 사용한다.
네 번째 방법은 컨피그맵과 데이터를 읽기 위해 코드를 작성해야 한다는 것을 의미한다. 그러나, 쿠버네티스 API를 직접 사용하기 때문에, 애플리케이션은 컨피그맵이 변경될 때마다 업데이트를 받기 위해 구독할 수 있고, 업데이트가 있으면 반응한다. 쿠버네티스 API에 직접 접근하면, 이 기술을 사용하여 다른 네임스페이스의 컨피그맵에 접근할 수도 있다.
다음은 game-demo
의 값을 사용하여 파드를 구성하는 파드 예시이다.
apiVersion: v1
kind: Pod
metadata:
name: configmap-demo-pod
spec:
containers:
- name: demo
image: alpine
command: ["sleep", "3600"]
env:
# 환경 변수 정의
- name: PLAYER_INITIAL_LIVES # 참고로 여기서는 컨피그맵의 키 이름과
# 대소문자가 다르다.
valueFrom:
configMapKeyRef:
name: game-demo # 이 값의 컨피그맵.
key: player_initial_lives # 가져올 키.
- name: UI_PROPERTIES_FILE_NAME
valueFrom:
configMapKeyRef:
name: game-demo
key: ui_properties_file_name
volumeMounts:
- name: config
mountPath: "/config"
readOnly: true
volumes:
# 파드 레벨에서 볼륨을 설정한 다음, 해당 파드 내의 컨테이너에 마운트한다.
- name: config
configMap:
# 마운트하려는 컨피그맵의 이름을 제공한다.
name: game-demo
# 컨피그맵에서 파일로 생성할 키 배열
items:
- key: "game.properties"
path: "game.properties"
- key: "user-interface.properties"
path: "user-interface.properties"
컨피그맵은 단일 라인 속성(single line property) 값과 멀티 라인의 파일과 비슷한(multi-line file-like) 값을 구분하지 않는다. 더 중요한 것은 파드와 다른 오브젝트가 이러한 값을 소비하는 방식이다.
이 예제에서, 볼륨을 정의하고 demo
컨테이너에
/config
로 마운트하면 컨피그맵에 4개의 키가 있더라도
/config/game.properties
와 /config/user-interface.properties
2개의 파일이 생성된다. 이것은 파드 정의가
volume
섹션에서 items
배열을 지정하기 때문이다.
items
배열을 완전히 생략하면, 컨피그맵의 모든 키가
키와 이름이 같은 파일이 되고, 4개의 파일을 얻게 된다.
컨피그맵 사용하기
컨피그맵은 데이터 볼륨으로 마운트할 수 있다. 컨피그맵은 파드에 직접적으로 노출되지 않고, 시스템의 다른 부분에서도 사용할 수 있다. 예를 들어, 컨피그맵은 시스템의 다른 부분이 구성을 위해 사용해야 하는 데이터를 보유할 수 있다.
컨피그맵을 사용하는 가장 일반적인 방법은 동일한 네임스페이스의 파드에서 실행되는 컨테이너에 대한 설정을 구성하는 것이다. 컨피그맵을 별도로 사용할 수도 있다.
예를 들어, 컨피그맵에 기반한 동작을 조정하는 애드온이나 오퍼레이터를 사용할 수도 있다.
파드에서 컨피그맵을 파일로 사용하기
파드의 볼륨에서 컨피그맵을 사용하려면 다음을 수행한다.
- 컨피그맵을 생성하거나 기존 컨피그맵을 사용한다. 여러 파드가 동일한 컨피그맵을 참조할 수 있다.
- 파드 정의를 수정해서
.spec.volumes[]
아래에 볼륨을 추가한다. 볼륨 이름은 원하는 대로 정하고, 컨피그맵 오브젝트를 참조하도록.spec.volumes[].configMap.name
필드를 설정한다. - 컨피그맵이 필요한 각 컨테이너에
.spec.containers[].volumeMounts[]
를 추가한다..spec.containers[].volumeMounts[].readOnly = true
를 설정하고 컨피그맵이 연결되기를 원하는 곳에 사용하지 않은 디렉터리 이름으로.spec.containers[].volumeMounts[].mountPath
를 지정한다. - 프로그램이 해당 디렉터리에서 파일을 찾도록 이미지 또는 커맨드 라인을
수정한다. 컨피그맵의
data
맵 각 키는mountPath
아래의 파일 이름이 된다.
다음은 볼륨에 컨피그맵을 마운트하는 파드의 예시이다.
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
사용하려는 각 컨피그맵은 .spec.volumes
에서 참조해야 한다.
파드에 여러 컨테이너가 있는 경우 각 컨테이너에는 자체 volumeMounts
블록이 필요하지만,
컨피그맵은 각 컨피그맵 당 하나의 .spec.volumes
만 필요하다.
마운트된 컨피그맵이 자동으로 업데이트
현재 볼륨에서 사용된 컨피그맵이 업데이트되면, 프로젝션된 키도 마찬가지로 업데이트된다.
kubelet은 모든 주기적인 동기화에서 마운트된 컨피그맵이 최신 상태인지 확인한다.
그러나, kubelet은 로컬 캐시를 사용해서 컨피그맵의 현재 값을 가져온다.
캐시 유형은 KubeletConfiguration 구조체의
ConfigMapAndSecretChangeDetectionStrategy
필드를 사용해서 구성할 수 있다.
컨피그맵은 watch(기본값), ttl 기반 또는 API 서버로 직접
모든 요청을 리디렉션할 수 있다.
따라서 컨피그맵이 업데이트되는 순간부터 새 키가 파드에 업데이트되는 순간까지의
총 지연시간은 kubelet 동기화 기간 + 캐시 전파 지연만큼 길 수 있다. 여기서 캐시
전파 지연은 선택한 캐시 유형에 따라 달라질 수 있다(전파
지연을 지켜보거나, 캐시의 ttl 또는 0에 상응함).
환경 변수로 사용되는 컨피그맵은 자동으로 업데이트되지 않으며 파드를 다시 시작해야 한다.
변경할 수 없는(immutable) 컨피그맵
Kubernetes v1.21 [stable]
쿠버네티스 기능인 변경할 수 없는 시크릿과 컨피그맵 은 개별 시크릿과 컨피그맵을 변경할 수 없는 것으로 설정하는 옵션을 제공한다. 컨피그맵을 광범위하게 사용하는 클러스터(최소 수만 개의 고유한 컨피그맵이 파드에 마운트)의 경우 데이터 변경을 방지하면 다음과 같은 이점이 있다.
- 애플리케이션 중단을 일으킬 수 있는 우발적(또는 원하지 않는) 업데이트로부터 보호
- immutable로 표시된 컨피그맵에 대한 감시를 중단하여, kube-apiserver의 부하를 크게 줄임으로써 클러스터의 성능을 향상시킴
이 기능은 ImmutableEphemeralVolumes
기능 게이트에 의해 제어된다.
immutable
필드를 true
로 설정하여 변경할 수 없는 컨피그맵을 생성할 수 있다.
다음은 예시이다.
apiVersion: v1
kind: ConfigMap
metadata:
...
data:
...
immutable: true
컨피그맵을 immutable로 표시하면, 이 변경 사항을 되돌리거나
data
또는 binaryData
필드 내용을 변경할 수 없다. 컨피그맵만
삭제하고 다시 작성할 수 있다. 기존 파드는 삭제된 컨피그맵에 대한 마운트 지점을
유지하므로, 이러한 파드를 다시 작성하는 것을 권장한다.
다음 내용
- 시크릿에 대해 읽어본다.
- 컨피그맵을 사용하도록 파드 구성하기를 읽어본다.
- 코드를 구성에서 분리하려는 동기를 이해하려면 Twelve-Factor 앱을 읽어본다.