목차

Kubernetes ConfigMap

  • 개발·테스트·운영 환경에 사용되는 각기 다른 환경 값의 분리가 필요한 상황으로, 애플리케이션 Image는 동일하게 사용하고 필요한 환경 구성값을 configMap으로 만들어 사용
    • 개발 환경은 SSH를 통한 보안 접근 해제
    • 테스트 환경은 지정된 USER 및 Key를 이용한 SSH 보안 접근 사용
    • 운영 환경은 테스트 환경과 다르게 지정된 USER 및 Key를 이용한 보안 접근 허용
  • 기밀이 요구되는 데이터는 Secret으로 생성해 Pod에 적용해서 사용
  • 애플리케이션 레벨이 아닌 Object 레벨의 환경 설정 관리할 때 사용
    • 애플리케이션 레벨에서 환경 구성 변경 시 소스코드와의 의존성이 높고, 설정 값 변경 시마다 코드 수정이 불가피하여 개발 속도가 지연될 수 있다.
    • 민첩성을 강조하는 Cloud Native 환경에서 애플리케이션 개발은 애플리케이션 코드와 분리된 configMap, Secret object 사용을 통해 신속하게 적용 가능
    • configMap, Secret은 중앙 관리 방식으로 애플리케이션 코드에서 구성 및 민감한 정보를 분리하도록 설계된 Kubernetes API object → kubectl api-resources

Dockerfile

FROM node:21-slim
EXPOSE 8000
COPY runapp.js .
CMD node runapp.js

# node21 이미지에
# 8000 port 개방하고
# 현재 폴더에서 runapp.js 카피
# 그 파일을 실행

runapp.js

var http = require('http');
const apiKey = process.env.API_KEY;
if (!apiKey) {
        console.log('API_KEY is not set in the environment variables.');
        http.createServer(function (req, res) {
                res.setHeader('Content-Type', 'text/plain');
                res.end("API_KEY is not set in the environment variables." + "\n");
        }).listen(8000);
} else {
        console.log('API_KEY:', apiKey);
        http.createServer(function (req, res) {
                res.setHeader('Content-Type', 'text/plain');
                res.end("Welcome to ConfigMap Kubernetes" + "\n");
        }).listen(8000);
}
// 환경변수에서 API_KEY 있는지 확인하여 결과물 다르게 표출

configMap 테스트 — 이미지 빌드 및 테스트

$ docker build -t crimsonpinus/configmaptest:latest --nocache .
# Dockerfile 빌드

$ docker run -d --name nodetest -p 8000:8000 crimsonpinus/configmaptest

$ curl localhost:8000
API_KEY is not set in the environment variables.

$ docker stop nodetest

$ docker rm nodetest

$ docker run -d --name nodetest -e API_KEY hello -p 8000:8000 crimsonpinus/configmaptest

$ curl localhost:8000
Welcome to ConfigMap Kubernetes

# kubernetes에서 사용하기 위해 본인의 계정에 업로드
$ docker push crimsonpinus/configmaptest:latest

cmtest-pod.yaml

apiVersion: v1
kind: Pod
metadata:
  labels:
    run: cmtest-pod
  name: cmtest-pod
spec:
  containers:
  - image: crimsonpinus/configmaptest
    name: cmtest-pod
    ports:
    - containerPort: 8000
    envFrom:
    - configMapRef:
        name: api-key
# cm-test-pod 생성후 위 envFrom 부분 추가

configMap 사용

$ kubectl run cmtest --image crimsonpinus/configmaptest --port 8000

$ kubectl get po,svc -o wide | grep cmtest
pod/cmtest                              1/1     Running   0          13s     10.111.156.89   k8s-node1    <none>           <none>

$ curl 10.111.156.89:8000
API_KEY is not set in the environment variables.

$ kubectl delete po cmtest
pod "cmtest" deleted

# configMap 생성
$ kubectl create cm api-key --from-literal=API_KEY=k8scm
configmap/api-key created

$ kubectl get cm
NAME                         DATA   AGE
api-key                      1      26s
kube-root-ca.crt             1      7d20h
kubeshark-config-map         22     7d16h
kubeshark-nginx-config-map   1      7d16h

$ kubectl describe cm api-key
Name:         api-key
Namespace:    default
Labels:       <none>
Annotations:  <none>

Data
====
API_KEY:
----
k8scm

BinaryData
====

Events:  <none>

# configMap 사용 - cmtest-pod.yaml 파일 생성
$ kubectl run cmtest --image crimsonpinus/configmaptest --port 8000 --dry-run=client -o yaml > cmtest-pod.yaml
# cmtest-pod.yaml 파일 참조하여 수정

$ kubectl apply -f cmtest-pod.yaml

$ kubectl get po,svc -o wide | grep cmtest
pod/cmtest-pod                          1/1     Running   0          98s     10.111.156.90   k8s-node1    <none>           <none>

$ curl 10.111.156.90:8000
Welcome to ConfigMap Kubernetes

$ kubectl exec -it cmtest-pod -- env | grep -i api
API_KEY=k8scm

configMap 값 변경

$ kubectl edit cm api-key
# Please edit the object below. Lines beginning with a '#' will be ignored,
# and an empty file will abort the edit. If an error occurs while saving this file will be
# reopened with the relevant failures.
#
apiVersion: v1
data:
  API_KEY: k8scm-edit  # 값 변경
kind: ConfigMap
metadata:
  creationTimestamp: "2024-04-12T04:56:56Z"
  name: api-key
  namespace: default
  resourceVersion: "1320379"
  uid: 56d43ca7-3a6e-47b7-ab6a-1460a0725bc7

$ kubectl exec -it cmtest-pod -- env | grep -i api
API_KEY=k8scm
# 변경 안되었음

# 강제 재시작
$ kubectl replace --force -f ./cmtest-pod.yaml
pod "cmtest-pod" deleted
pod/cmtest-pod replaced

$ kubectl exec -it cmtest-pod -- env | grep -i api
API_KEY=k8scm-edit

  • 선언형: YAML 코드 작성 — kubectl api-resources | grep configmap
  • 명령형: kubectl create configmap(or cm) configmap_name {설정}
    • 설정1: --from-literal key=value [--from-literal key2=value2] ...
    • 설정2: --from-file file_name (파일 안에 다수의 설정값 저장)
    • 설정3: --from-env-file file_name (key=value의 환경 변수가 저장된 파일)
  • 부가기능 — 불변(immutable)
    • immutable: true
    • 실행 중인 애플리케이션의 값이 변경될 경우 의도치 않은 중단을 야기하는 값에 사용
    • 불변 설정으로 configMap에 대한 APIserver의 감시 중단, 클러스터 성능 향상에 기여
apiVersion: v1
kind: ConfigMap
metadata:
  name: my-configmap
data:
  string-value: "Hello, world!"  # 스트링 형식, 공백이 없으면 따옴표 없어도 됨
  number-value: "42"             # 숫자 형식
  boolean-value: "true"         # 불린 타입은 따옴표 무조건 필요
  multiline-value: |            # | 표시는 아래 줄과 이어진다는 표시
    This is a multiline value
    that spans multiple lines
  list-value: |
    - item1
    - item2
    - item3
  object-value: |
    key1: value1
    key2: value2
  json-value: |-
    {
      "key": "value",
      "array": [1, 2, 3],
      "nested": {
        "innerKey": "innerValue"
      }
    }
  yaml-value: |-
    key: value
    array:
      - 1
      - 2
      - 3
  • Pod 속 환경 변수로 등록 → envFrom.configMapRef.name
  • Pod Volume으로 mount → volumes.configMap.name
    • mountPath: 해당 디렉토리 전체를 업데이트 (mount된 configMap, Secret은 업데이트 시 Pod에 자동 반영)
    • subPath: 해당 디렉토리 안의 지정된 파일만 업데이트
  • Pod 속 컨테이너의 환경변수 값으로 KEY 지정
    • valueFrom.configMapKeyRef.name
    • valueFrom.configMapKeyRef.key
$ kubectl create configmap k8s-env \
  --from-literal orchestrator=kubernetes \
  --from-literal runtime=containerd

$ kubectl describe cm k8s-env
Name:         k8s-env
Namespace:    default
Labels:       <none>
Annotations:  <none>

Data
====
orchestrator:
----
kubernetes
runtime:
----
containerd

BinaryData
====

Events:  <none>

# yaml로 보기
$ kubectl get cm k8s-env -o yaml
apiVersion: v1
data:
  orchestrator: kubernetes
  runtime: containerd
kind: ConfigMap
metadata:
  creationTimestamp: "2024-04-12T05:51:16Z"
  name: k8s-env
  namespace: default
  resourceVersion: "1326898"
  uid: 3bcdf040-6174-497f-903e-a6b3bedf9db2

# JSON으로 보기
$ kubectl get cm k8s-env -o json
{
    "apiVersion": "v1",
    "data": {
        "orchestrator": "kubernetes",
        "runtime": "containerd"
    },
    "kind": "ConfigMap",
    "metadata": {
        "creationTimestamp": "2024-04-12T05:51:16Z",
        "name": "k8s-env",
        "namespace": "default",
        "resourceVersion": "1326898",
        "uid": "3bcdf040-6174-497f-903e-a6b3bedf9db2"
    }
}

# YAML 파일로 생성
$ kubectl create configmap k8s-env \
  --from-literal orchestrator=kubernetes \
  --from-literal runtime=containerd \
  --dry-run=client -o yaml > k8s-env.yaml

$ kubectl apply -f k8s-env.yaml

cm-volume-pod.yaml

apiVersion: v1
kind: Pod
metadata:
  labels:
    run: cm-volume-pod
  name: cm-volume-pod
spec:
  containers:
  - image: nginx:1.25.3-alpine
    name: cm-volume-pod
    ports:
    - containerPort: 80
    volumeMounts:
    - name: cm-volume
      mountPath: /etc/config
  volumes:
  - name: cm-volume
    configMap:
      name: k8s-env
      key: orchestrator  # key를 이용하여 일부의 값만 가져올 수 있음

활용

$ kubectl apply -f cm-volume-pod.yaml
pod/cm-volume-pod created

$ kubectl get po,svc -o wide | grep cm
pod/cm-volume-pod                       1/1     Running   0          31s     10.109.131.17   k8s-node2    <none>           <none>

$ kubectl exec cm-volume-pod -- ls -la /etc/config
total 0
drwxrwxrwx    3 root     root            95 Apr 12 06:11 .
drwxr-xr-x    1 root     root            52 Apr 12 06:11 ..
drwxr-xr-x    2 root     root            41 Apr 12 06:11 ..2024_04_12_06_11_12.4261600263
lrwxrwxrwx    1 root     root            32 Apr 12 06:11 ..data -> ..2024_04_12_06_11_12.4261600263
lrwxrwxrwx    1 root     root            19 Apr 12 06:11 orchestrator -> ..data/orchestrator
lrwxrwxrwx    1 root     root            14 Apr 12 06:11 runtime -> ..data/runtime

$ kubectl exec cm-volume-pod -- cat /etc/config/orchestrator
kubernetes

변경 후 Pod restart 없이 반영

$ kubectl edit cm k8s-env
# orchestrator: k8s 로 변경

# nginx 리로드
$ kubectl exec cm-volume-pod -- nginx -s reload
2024/04/12 06:18:19 [notice] 46#46: signal process started

$ kubectl exec cm-volume-pod -- cat /etc/config/orchestrator
k8s

redis.conf

REDIS_PRIMARY_SERVICE_HOST=10.0.0.11
REDIS_PRIMARY_SERVICE_PORT=6379
REDIS_PRIMARY_PORT=tcp://10.0.0.11:6379
REDIS_PRIMARY_PORT_6379_TCP=tcp://10.0.0.11:6379
REDIS_PRIMARY_PORT_6379_TCP_PROTO=tcp
REDIS_PRIMARY_PORT_6379_TCP_PORT=6379
REDIS_PRIMARY_PORT_6379_TCP_ADDR=10.0.0.11

cmfile-volume-pod.yaml

apiVersion: v1
kind: Pod
metadata:
  labels:
    run: cmfile-volume-pod
  name: cmfile-volume-pod
spec:
  containers:
  - image: nginx:1.25.3-alpine
    name: cmfile-volume-pod
    ports:
    - containerPort: 80
    volumeMounts:
    - name: redis-volume
      mountPath: /opt/redis/config
  volumes:
  - name: redis-volume
    configMap:
      name: redis-config
# 녹색 부분 추가

활용

$ kubectl create cm redis-config --from-file=redis.conf
configmap/redis-config created

$ kubectl get cm
NAME                         DATA   AGE
k8s-env                      2      30m
kube-root-ca.crt             1      7d22h
kubeshark-config-map         22     7d17h
kubeshark-nginx-config-map   1      7d17h
redis-config                 1      9s

$ kubectl describe cm redis-config
Name:         redis-config
Namespace:    default
Labels:       <none>
Annotations:  <none>

Data
====
redis.conf:
----
REDIS_PRIMARY_SERVICE_HOST=10.0.0.11
REDIS_PRIMARY_SERVICE_PORT=6379
REDIS_PRIMARY_PORT=tcp://10.0.0.11:6379
REDIS_PRIMARY_PORT_6379_TCP=tcp://10.0.0.11:6379
REDIS_PRIMARY_PORT_6379_TCP_PROTO=tcp
REDIS_PRIMARY_PORT_6379_TCP_PORT=6379
REDIS_PRIMARY_PORT_6379_TCP_ADDR=10.0.0.11

BinaryData
====

Events:  <none>

$ kubectl apply -f cmfile-volume-pod.yaml

$ kubectl exec cmfile-volume-pod -- ls -l /opt/redis/config

$ kubectl exec cmfile-volume-pod -- cat /opt/redis/config/redis.conf