컨피그맵이나 시크릿의 데이터를 파드에서 사용할 때 볼륨 마운트 방식으로 사용할 수 있다.

이때 마운트된 볼륨 내의 파일들에 대해서 permission 을 설정해 줄 수 있다. 컨피그맵과 시크릿 모두 기본 퍼미션은 0644(rw-r--r--)이다. 퍼미션은 매니페스트 파일로 작성할 때 8진수 퍼미션 값을 10진수로 변환하여 기입하여야 한다.

8진수(Octal Mode) 10진수 rwx 표기(Symbolic Mode)
0400 256 r--------
0600 384 rw-------
0644 420 rw-r--r--
0700 448 rwx------
0755 493 rwxr-xr-x
0777 511 rwxrwxrwx

 

1. ConfiMap 예시

아래의 매니페스트는 컨피그맵의 쉘 스크립트 파일을 0755 권한으로 마운트한다.

apiVersion: v1
kind: Pod
metadata:
  name: sample-configmap-scripts
spec:
  containers:
    - name: configmap-container
      image: nginx:1.27
      command: ["/config/test.sh"]
      volumeMounts:
        - name: config-volume
          mountPath: /config
  volumes:
    - name: config-volume
      configMap:
        name: sample-configmap
        items:
          - key: test.sh
            path: test.sh
            mode: 493 # 0755

아래 커맨드로 쉘 스크립트 실행을 확인해본다.

$ kubectl apply -f sample-configmap-scripts.yaml

# 권한 확인
$ kubectl exec -it sample-configmap-scripts -- ls -l /config/test.sh
lrwxrwxrwx 1 root root 14 Jan 17 08:01 /config/test.sh -> ..data/test.sh

# 실행됨 확인
$ kubectl logs sample-configmap-scripts
Hello, kubernetes

 

그런데, 권한 확인을 해보니 0755 권한은 rwxr-xr-x 이어야 하는데 rwxrwxrwx 로 출력된다. 쿠버네티스에서 Secret 이나 ConfigMap 을 볼륨으로 마운트하면 Atomic Reference Updates 라는 메커니즘에 의해 파일들이 복잡한 심볼릭 링크 구조를 가지기 때문이다. 심볼릭 링크가 걸린 경로인 "..data/test.sh" 는 실제 데이터 경로가 아니라 쿠버네티스가 데이터 업데이트시 원자성을 보장하기 위해 만든 중간 경로이다. 예를 들어 하나의 시크릿에 10개의 key-value 가 존재하고, 각각의 key-value 를 파일로 만들어서 파드에 볼륨 마운트 한다고 치자. 어느순간 시크릿이 수정되어 10개의 마운트된 파일들이 모두 수정되어야 하는데, 그 중 5개만 수정된 상태에서 애플리케이션에서 마운트된 경로에 접근했을 경우 5개의 파일은 이전버전의 시크릿을, 5개의 파일은 현재버전의 시크릿을 읽게 되어 일관성이 무너지게 된다. 따라서 10개의 파일을 모두 업데이트 한 후, 어느 시점에 한번에 마운트 링크를 변경시키는 방식을 사용하는 것이다. 실제로 마운트 경로의 파일을 모두 출력해보면 "..data" 라는 심볼릭 링크가 있고, 다시 타임스탬프 형식으로 이루어진 디렉토리를 참조하는 형태임을 알 수 있다. 복잡한 심볼릭 링크를 따라가서 실제 파일의 경로를 보려면 -L 옵션(dereference)을 사용한다.

# 심볼릭 링크가 가리키는 중간 심볼릭 링크와 실제 데이터가 저장된 디렉토리를 볼 수 있다.
$ kubectl exec -it sample-configmap-scripts -- ls -alF /config
total 12
drwxrwxrwx 3 root root 4096 Jan 17 08:01 ./
drwxr-xr-x 1 root root 4096 Jan 17 08:01 ../
drwxr-xr-x 2 root root 4096 Jan 17 08:01 ..2026_01_17_08_01_01.3453801296/
lrwxrwxrwx 1 root root   32 Jan 17 08:01 ..data -> ..2026_01_17_08_01_01.3453801296/
lrwxrwxrwx 1 root root   14 Jan 17 08:01 test.sh -> ..data/test.sh*

# -L 옵션으로 심볼릭 링크를 쭉 따라가서 원본 파일 정보를 출력할 수 있다.
$ kubectl exec -it sample-configmap-scripts -- ls -lL /config/test.sh
-rwxr-xr-x 1 root root 52 Jan 17 08:01 /config/test.sh

 

2. Secret 예시

아래의 매니페스트는 시크릿의 모든 key-value 를 0400 권한으로 마운트한다.

apiVersion: v1
kind: Pod
metadata:
  name: sample-secret-secure
spec:
  containers:
    - name: secret-container
      image: nginx:1.27
      volumeMounts:
        - name: config-volume
          mountPath: /config
  volumes:
    - name: config-volume
      secret:
        secretName: sample-db-auth
        defaultMode: 256

아래 커맨드로 권한을 확인해본다.

$ kubectl exec -it sample-secret-secure -- ls -l /config/..data/
total 8
-r-------- 1 root root 12 Jan 25 07:39 password
-r-------- 1 root root  4 Jan 25 07:39 username

 

3. 동적 업데이트

위의 컨피그맵 예시에서 사용한 sample-configmap 의 모든 키를 파드에 볼륨 마운트 한 후, 컨피그맵의 thread 값만 "16" -> "32" 로 변경한 후 파드 재시작 없이도 변경된 값이 마운트 경로에서도 변경되는지 확인해보자.

먼저 아래 매니페스트를 적용하여 sample-configmap 컨피그맵의 모든 key-value 를 마운트한다.

apiVersion: v1
kind: Pod
metadata:
  name: sample-configmap-multi-volume
spec:
  containers:
    - name: configmap-container
      image: nginx:1.27
      volumeMounts:
        - name: config-volume
          mountPath: /config
  volumes:
    - name: config-volume
      configMap:
        name: sample-configmap
$ kubectl exec -it sample-configmap-multi-volume -- cat /config/thread
16

이 상태에서 sample-configmap 매니페스트의 thread 값을 32로 변경 후 apply 한다.

기본 업데이트 간격은 60초로 설정되어 있기 때문에 바로 변경되지 않더라도 최대 60초 후에는 변경된 것을 확인할 수 있다.

$ kubectl exec -it sample-configmap-multi-volume -- cat /config/thread
32

 

4. 변경 불가능한 컨피그맵/시크릿 만들기

아래처럼 매니페스트에 immutable: true 항목을 추가하면 컨피그맵이나 시크릿을 한번 생성하면 도중에 절대 변경 불가능하도록 만들 수 있다.

apiVersion: v1
kind: Secret
metadata:
  name: my-secure-config
type: Opaque
immutable: true  # <--- 이 항목을 추가합니다.
data:
  api-key: AAA

kubectl apply, patch, edit 같은 명령어는 물론 어떤 방법으로도 data 를 변경할 수 없다. 데이터를 변경하려면 리소스를 삭제 후 변경된 data 를 포함하여 재생성하여야만 한다. 또한, immutable 항목 자체도 한번 생성하면 변경 불가능하다. 아래와 같은 이점이 있다.

  • Kubelet 부하 감소: 쿠버네티스의 워커 노드(Kubelet)는 Secret이 변경되었는지 주기적으로 API Server를 호출하여 감시(Watch)한다. immutable: true로 설정된 리소스는 변경되지 않음을 보장받으므로, Kubelet이 감시를 중단하여 클러스터 전체의 부하를 줄일 수 있다.
블로그 이미지

망원동똑똑이

프로그래밍 지식을 자유롭게 모아두는 곳입니다.

,

컨피그맵은 파드의 컨테이너에서 참조하여 사용하게 된다. 환경 변수로 전달하는 방법과 볼륨으로 마운트하여 사용하는 방법이 있다.

  • 환경 변수로 전달
    • 컨피그맵의 특정 키 전달: spec.containers[].env[].valueForm.configMapKeyRef.name/key
    • 컨피그맵 전체 키 전달: spec.containers[].envFrom[].configMapRef.name
  • 볼륨으로 마운트
    • 컨피그맵의 특정 키 마운트: spec.volumes[].configMap.items[].key/path
    • 컨피그맵 전체 키 마운트: spec.volumes[].configMap.name

1. 환경 변수로 특정 키 전달

아래 매니페스트와 같이 env[] 에 환경변수들을 이름과 값으로 하나씩 직접 지정한다. sample-configmap 이라는 컨피그맵에 connection.max 키로 담긴 값을 CONNECTION_MAX 라는 환경변수로 세팅하는 것이다.

apiVersion: v1
kind: Pod
metadata:
  name: sample-configmap-single-env
spec:
  containers:
    - name: configmap-container
      image: nginx:1.27
      env:
        - name: CONNECTION_MAX
          valueFrom:
            configMapKeyRef:
              name: sample-configmap
              key: connection.max
$ kubectl apply -f sample-configmap-single-env.yaml
$ kubectl exec -it sample-configmap-single-env -- env | grep CONNECTION_MAX
CONNECTION_MAX=100

 

2. 환경 변수로 전체 키 전달

직접 환경변수명과 불러올 컨피그맵 값을 지정하지 않고 컨피그맵 전체를 환경변수로 그대로 등록하는 방법이다. 간단하긴 하지만, 매니페스트만 봐서는 어떤 환경변수가 설정되는지 알 수 없다는 단점이 있다.

아래 매니페스트처럼 간단히 envFrom[] 에 가져올 컨피그맵들의 참조를 걸어주면 된다.

apiVersion: v1
kind: Pod
metadata:
  name: sample-configmap-multi-env
spec:
  containers:
    - name: configmap-container
      image: nginx:1.27
      envFrom:
        - configMapRef:
            name: sample-configmap
$ kubectl apply -f sample-configmap-multi-env.yaml
$ kubectl exec -it sample-configmap-multi-env -- env
...
connection.max=100
connection.min=10
nginx.conf=user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;

sample.properties=property.1=value-1
property.2=value-2
property.3=value-3

test.sh=#!/bin/bash
echo "Hello, kubernetes"
sleep infinity

thread=16
...

그러나 위처럼 파드에 설정된 환경변수를 출력해보면 nginx.conf, sample.properties, test.sh 가 제대로 설정되지 못했음을 알 수 있다.(개행 처리가 의도대로 되지 않음) 개행된 값을 컨테이너로 주입하고자 할 때는 볼륨 마운트 방식으로 사용하여야 한다. 아래와 같은 주의사항을 참고한다.

  • 환경 변수명에 "." 또는 "-" 가 포함되지 않도록 한다.
  • 환경 변수 값이 개행을 포함하지 않도록 한다.(yaml 문법의 <key>: | 개행 스타일 포함)

 

3. 볼륨으로 특정 키 마운트

컨피그맵의 정보를 컨테이너의 볼륨으로 마운트 하는 방식으로, spec.containers[].volumeMounts[] 에 마운트 할 볼륨명과 마운트 경로를 지정하고, spec.volumes[] 에 생성할 볼륨명과 값을 가져올 컨피그맵 및 읽을 key, 그리고 마운트 경로에 생성할 파일명(path)을 지정한다. 직접 마운트 파일명을 지정한다는 것이 특징이다.

다음과 같은 매니페스트로 생성한다.

apiVersion: v1
kind: Pod
metadata:
  name: sample-configmap-single-volume
spec:
  containers:
    - name: configmap-container
      image: nginx:1.27
      volumeMounts:
        - name: config-volume
          mountPath: /config
  volumes:
    - name: config-volume
      configMap:
        name: sample-configmap
        items:
          - key: nginx.conf
            path: nginx-sample.conf
$ kubectl apply -f sample-configmap-single-volume.yaml
$ kubectl exec -it sample-configmap-single-volume -- cat /config/nginx-sample.conf
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;

 

4. 볼륨으로 전체 키 마운트

컨피그맵의 전체 정보를 컨테이너의 볼륨으로 마운트하는 방식으로, spec.containers[].volumeMounts[] 에 마운트 할 볼륨명과 마운트 경로를 지정하고, spec.volumes[] 에 생성할 볼륨명과 가져올 컨피그맵을 지정한다. 마운트 경로에 생성되는 파일명은 컨피그맵의 key 와 동일하다. 매니페스트는 간단해지지만 매니페스트만으로는 컨피그맵의 어떤 key-value가 마운트되는지 알 수 없다는 단점이 있다.

다음과 같은 매니페스트로 생성한다.

apiVersion: v1
kind: Pod
metadata:
  name: sample-configmap-multi-volume
spec:
  containers:
    - name: configmap-container
      image: nginx:1.27
      volumeMounts:
        - name: config-volume
          mountPath: /config
  volumes:
    - name: config-volume
      configMap:
        name: sample-configmap
$ kubectl apply -f sample-configmap-multi-volume.yaml
$ kubectl exec -it sample-configmap-multi-volume -- ls /config
connection.max	connection.min	nginx.conf  sample.properties  test.sh	thread
$ kubectl exec -it sample-configmap-multi-volume -- paste /config/connection.max /config/nginx.conf /config/sample.properties
100	user nginx;	property.1=value-1
	worker_processes auto;	property.2=value-2
	error_log /var/log/nginx/error.log;	property.3=value-3
	pid /run/nginx.pid;

 

블로그 이미지

망원동똑똑이

프로그래밍 지식을 자유롭게 모아두는 곳입니다.

,

ConfigMap 은 key-value 들을 저장하는 리소스이다. value 에는 바이너리 데이터나 nginx.conf 같은 설정 내용도 저장할 수 있다. 하나의 ConfigMap에는 1MB 까지 저장 가능하다. 생성하는 방법은 Generic 타입의 Secret 과 거의 동일하며, 3가지 생성하는 방법을 설명한다.

 

1. 매니페스트에서 생성

일반적인 리소스 생성 방식과 동일하게 kubectl apply -f <매니페스트파일명> 또는 kubectl create -f <매니페스트파일명> --save-config 으로 생성하는 방식이다. 시크릿과는 달리 base64 인코딩되지 않는다. value 를 여러 행으로 저장하는 경우(nginx 설정파일 내용 등) YAML 문법대로 <key>: | 로 시작하여 여러 행에 정의한다. 숫자는 큰따옴표로 둘러쌓아야 한다. 아래 예시를 참고한다.

apiVersion: v1
kind: ConfigMap
metadata:
  name: sample-configmap
data:
  thread: "16"
  connection.max: "100"
  connection.min: "10"
  sample.properties: |
    property.1=value-1
    property.2=value-2
    property.3=value-3
  nginx.conf: |
    user nginx;
    worker_processes auto;
    error_log /var/log/nginx/error.log;
    pid /run/nginx.pid;
  test.sh: |
    #!/bin/bash
    echo "Hello, kubernetes"
    sleep infinity

 

아래 커맨드로 저장된 key-value 를 확인해본다.

$ kubectl get configmaps sample-configmap -o yaml
apiVersion: v1
data:
  connection.max: "100"
  connection.min: "10"
  nginx.conf: |
    user nginx;
    worker_processes auto;
    error_log /var/log/nginx/error.log;
    pid /run/nginx.pid;
  sample.properties: |
    property.1=value-1
    property.2=value-2
    property.3=value-3
  test.sh: |
    #!/bin/bash
    echo "Hello, kubernetes"
    sleep infinity
  thread: "16"
kind: ConfigMap
...

 

2. 리터럴 값을 전달하여 생성

kubectl 커맨드로 시크릿을 생성하되, --from-literal 옵션으로 직접 key-value 쌍을 전달하는 방식이다. 아래처럼 생성한다.

# 생성
$ kubectl create configmap --save-config web-config \
  --from-literal=connection.max=100 --from-literal=connection.min=10

# 확인
$ kubectl get cm web-config -o json | jq '.data'
{
  "connection.max": "100",
  "connection.min": "10"
}

 

3. 파일의 내용을 value 로 생성

kubectl 커맨드로 생성하되, --from-file 옵션으로 key-value 로 사용될 파일들을 지정하는 방식이다. 파일명이 key 가 되고, 파일 내용이 value 가 된다. 만약 파일명 대신 다른 key 를 사용하고자 한다면 --from-file=<사용할key>=<파일명> 형식으로 입력한다.

# 생성
$ kubectl create configmap --save-config sample-configmap-file \
> --from-file=./nginx.conf

# 확인
$ kubectl get configmaps sample-configmap-file -o json | jq '.data'
{
  "nginx.conf": "user  nginx;\nworker_processes  auto;\nerror_log  /var/log/nginx/error.log warn;\npid        /var/run/nginx.pid;\n"
}

# describe 확인
$ kubectl describe configmaps sample-configmap-file
Name:         sample-configmap-file
Namespace:    default
Labels:       <none>
Annotations:  <none>

Data
====
nginx.conf:
----
user  nginx;
worker_processes  auto;
error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;



BinaryData
====

Events:  <none>

 

k8s 에 실제 등록하지 않고 매니페스트 파일로 출력하려면 --dry-run=client -o yaml 옵션을 주어 실행한다. UTF-8 이외의 데이터를 저장하면 자동으로 binaryData 필드로 base64 인코딩 되어 저장된다.

$ kubectl create configmap sample-configmap-binary-2 \
  --from-file=image.png \
  --from-literal=index.html="Hello, Kubernetes" \
  --dry-run=client -o yaml \
  > sample-configmap-binary-2.yaml
# sample-configmap-binary-2.yaml 매니페스트
apiVersion: v1
binaryData:
  image.png: iVBORw0KGgoAAAANSUhEUgAAAs...<생략>
data:
  index.html: Hello, Kubernetes
kind: ConfigMap
metadata:
  creationTimestamp: null
  name: sample-configmap-binary-2

이제 이를 기반으로 ConfigMap 을 생성한 후, 아래 매니페스트로 ConfigMap 을 볼륨 마운트 한 파드를 생성한 후, curl 로 호출해보자

apiVersion: v1
kind: Pod
metadata:
  name: sample-configmap-binary-webserver
spec:
  containers:
    - name: nginx-container
      image: nginx:1.27
      volumeMounts:
        - name: config-volume
          mountPath: /usr/share/nginx/html
  volumes:
    - name: config-volume
      configMap:
        name: sample-configmap-binary-2
# ConfigMap 생성
$ kubectl apply -f sample-configmap-binary-2.yaml

# Pod 생성
$ kubectl apply -f sample-configmap-binary-webserver.yaml

# 확인을 위한 포트 포워딩
$ kubectl port-forward sample-configmap-binary-webserver 8080:80

# 호출 확인
$ curl http://localhost:8080/index.html
Hello, Kubernetes

# 볼륨 마운트 경로에 파일 생성 확인
$ kubectl exec -it sample-configmap-binary-webserver -- ls /usr/share/nginx/html
image.png  index.html

 

블로그 이미지

망원동똑똑이

프로그래밍 지식을 자유롭게 모아두는 곳입니다.

,