
환경에 따라 다르거나 자주 변경되는 설정 옵션을 소스 코드와 분리하여 유지하기 위함.


명령어로 생성
$ kubectl create configmap
configMap 삭제
kubectl delete configmap <configmap-name>
# 단일 입력
$ kubectl create configmap fortune-config --from-literal=sleep-interval=25
# 여러개의 리터럴
$ kubectl create configmap myconfigmap --from-literal=foo=bar --from-literal=bar=baz --from-literal=one=two
$ kubectl get configmap fortune-config -o yaml
apiVersion: v1
data:
sleep-interval: "25"
kind: ConfigMap
metadata:
creationTimestamp: 2016-08-11T20:31:08Z
name: fortune-config
namespace: default
...
$ kubectl create -f <yaml-file>$ kubectl get cm$ kubectl create configmap my-config --from-file=config-file.conf
# key 지정
$ kubectl create configmap my-config --from-file=customkey=config-file.conf
# 디렉터리로 생성
$ kubectl create configmap my-config --from-file=/path/to/dir
$ kubectl create configmap my-config
➥ --from-file=foo.json
➥ --from-file=bar=foobar.conf
➥ --from-file=config-opts/
➥ --from-literal=some=thing

apiVersion: v1
kind: Pod
metadata:
name: fortune-env-from-configmap
spec:
containers:
- image: luksa/fortune:env
env:
- name: INTERVAL
valueFrom: # 고정 값 대신 configMap key 사용
configMapKeyRef:
name: fortune-config
key: sleep-interval
...

존재하지 않는 configMap을 참조하는 컨테이너는 시작되지 않지만 스케줄은 되어있는 상태. 누락된 configMap을 생성하면 장애가 발생한 컨테이너가 시작된다.
➕ configMapKeyRef.optional: true 설정 시 configMap이 없어도 컨테이너가 시작된다.
쿠버네티스 1.6 이상은 ConfigMap의 모든 항목을 한번에 환경 변수로 노출시킬 수 있는 방법 제공.
spec:
containers:
- image: some-image
envFrom:
- prefix: CONFIG_ # 접두사 (생략 시 key가 이름)
configMapRef: # configMap 참조
name: my-config-map
...
❗ 환경 변수 이름에는 대시(-)가 포함되면 안된다. 만일 configMap의 key에 대시가 포함되어 있다면 해당 항목을 건너뛴다.
containers의 args 필드에서 컨피그맵 항목을 직접 참조할 수는 없지만 환경 변수는 참조 가능하다.
apiVersion: v1
kind: Pod
metadata:
name: fortune-args-from-configmap
spec:
containers:
- image: luksa/fortune:args # 환경 변수x, 인자로 interval 참조
env: # 환경 변수 정의
- name: INTERVAL
valueFrom:
configMapKeyRef:
name: fortune-config
key: sleep-interval
args: ["$(INTERVAL)"] # 환경 변수 사용
...

명령줄 인자로 전달하는 것은 보통 짧은 변수 값에 사용한다.
configMap에는 파일이 포함될 수도 있는데 그런 경우에 configMap 볼륨을 사용한다. 파일의 내용을 읽어서 항목의 값을 얻는다.
server {
listen 80;
server_name www.kubia-example.com;
# 일반 텍스트와 XML파일 gzip
gzip on;
gzip_types text/plain application/xml;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
}

configMap 생성 $ kubectl create configmap fortune-config --from-file=configmap-files
두 개의 항목이 포함된 configMap, key는 파일명
$ kubectl get configmap fortune-config -o yaml
apiVersion: v1
data:
my-nginx-config.conf: | # 뒤에 여러줄의 값이 온다
server {
listen 80;
server_name www.kubia-example.com;
gzip on;
gzip_types text/plain application/xml;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
}
sleep-interval: |
25
kind: ConfigMap
...
nginx의 설정 파일은 /etc/nginx/conf.d/ 디렉터리의 모든 .conf 파일을 포함한다. 그곳에 configMap 파일 추가할 것.

apiVersion: v1
kind: Pod
metadata:
name: fortune-configmap-volume
spec:
containers:
- image: nginx:alpine
name: web-server
volumeMounts:
- name: config
mountPath: /etc/nginx/conf.d # 볼륨을 마운트 할 위치
readOnly: true
...
volumes:
...
- name: config
configMap: # 볼륨이 configMap 참조
name: fortune-config
...
# 포트 포워딩
$ kubectl port-forward fortune-configmap-volume 8080:80 &
# 요청
$ curl -H "Accept-Encoding: gzip" -I localhost:8080
# 마운트된 ConfigMap 볼륨의 내용
$ kubectl exec fortune-configmap-volume -c web-server ls /etc/nginx/conf.d
my-nginx-config.conf
sleep-interval
❗ 두 항목이 같은 디렉터리에 추가되었다. 여러 개의 configMap 항목이 같은 컨테이너에 있다는 것은 컨테이너가 밀접하게 연관되어 있어야 한다는 문제점이 발생할 수 있다.
volumes:
- name: config
configMap:
name: fortune-config
items:
- key: my-nginx-config.conf # 원하는 항목의 key
path: gzip.conf
❗ 디렉토리를 마운트하면 해당 디렉토리의 기존 파일이 숨겨진다
볼륨을 디렉터리로 마운트 하게되면 컨테이너 이미지의 해당 디렉터리에(여기서는 /etc/nginx/conf.d) 저장된 파일이 숨겨졌다는 걸 의미한다. 즉, 마운트 동안 기존 파일들에 액세스가 불가능하다.
일반적으로 linux에서 파일 시스템을 비지 않은 디렉터리에 마운트할 때 발생하는 현상이다.

spec:
containers:
- image: some/image
volumeMounts:
- name: myvolume
mountPath: /etc/someconfig.conf # 파일에 마운트
subPath: myconfig.conf # 볼륨의 파일
❗개별 파일 마운트 방식은 업데이트 관련 문제점을 가진다..
volumes:
- name: config
configMap:
name: fortune-config
defaultMode: "6600" # -rw-rw------
환경 변수나 command-line 인자와 다르게 configMap 볼륨은 pod나 컨테이너를 재시작할 필요 없이 설정 업데이트가 가능하다. configMap을 업데이트하면 참조하는 모든 볼륨의 파일이 업데이트 된다.
# CONFIGMAP 수정
$ kubectl edit configmap fortune-config
# gzip on -> gzip off로 수정
# 볼륨의 파일 내용 출력
$ kubectl exec fortune-configmap-volume -c web-server
➥ cat /etc/nginx/conf.d/my-nginx-config.conf
볼륨의 파일 내용은 수정되었지만 nginx 서버의 파일은 자동 reload되지 않았을 것.
# NGINX에서 config을 다시 로드하도록 신호 전송
$ kubectl exec fortune-configmap-volume -c web-server -- nginx -s reload
# 확인 시 Content-Encoding: gzip 헤더가 없을 것
$ curl -H "Accept-Encoding: gzip" -I localhost:8080
쿠버네티스가 심볼릭 링크를 사용해서 가능하게 한다. ConfigMap이 업데이트되면, 디렉토리를 생성하고 모든 파일을 기록한 다음 ..data를 새 디렉토리에 링크하여 관리한다.
$ kubectl exec -it fortune-configmap-volume -c web-server -- ls -lA /etc/nginx/conf.d
total 4
drwxr-xr-x ... 12:15 ..4984_09_04_12_15_06.865837643
lrwxrwxrwx ... 12:15 ..data -> ..4984_09_04_12_15_06.865837643
lrwxrwxrwx ... 12:15 my-nginx-config.conf -> ..data/my-nginx-config.conf
lrwxrwxrwx ... 12:15 sleep-interval -> ..data/sleep-interval
❗ 마운트된 단일 파일은 업데이트되지 않는다
-> 전체 볼륨을 다른 디렉토리에 마운트한 다음, 해당 파일을 가리키는 심볼릭 링크를 만들어서 해결
컨테이너의 특징 중 하나는 불변성이다. 그런데 configMap을 수정해서 불변성을 해치는 것은 옳은 일인가?
애플리케이션이 재로딩을 지원하지 않는 경우, 다시 시작된 pod나 컨테이너 부분에서만 다른 설정으로 프로세스가 돌아가는 문제가 발생한다. 그러므로 앱이 지원하지 않는다면 configMap을 수정하는 것은 좋지 않다.
: 보안 유지가 필요한 자격 증명 및 개인 암호화 키 같은 중요한 정보를 유지할 때 사용하는 쿠버네티스의 객체이다.
모든 pod에는 자동으로 secret 볼륨이 부착된다. $ kubectl describe pod 에서 위치와 함께 확인 가능
# 시크릿 출력
$ kubectl get secrets
# 자세한 정보 출력
$ kubectl describe secrets
Name: default-token-cfee9
Namespace: default
Labels: <none>
Annotations: kubernetes.io/service-account.name=default
kubernetes.io/service-account.uid=cc04bb39-b53f-42010af00237
Type: kubernetes.io/service-account-token
Data
====
ca.crt: 1139 bytes
namespace: 7 bytes
token: eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...
# 세 개의 항목을 가진 시크릿

# 파일 확인
$ kubectl exec mypod ls /var/run/secrets/kubernetes.io/serviceaccount/
ca.crt
namespace
token
https 트래픽을 서비스 하는 nginx 컨테이너로 업그레이드
개인 키와 인증서가 필요하다.
# 인증서, 개인 키 파일 생성
$ openssl genrsa -out https.key 2048
$ openssl req -new -x509 -key https.key -out https.cert -days 3650 -subj /CN=www.kubia-example.com
# 더미 파일 생성
$ echo bar > foo
# 시크릿 생성
$ kubectl create secret generic fortune-https --from-file=https.key --from-file=https.cert --from-file=foo
두 개의 항목을 가진 generic 시크릿 생성
$ kubectl get secret fortune-https -o yaml
apiVersion: v1
data:
foo: YmFyCg==
https.cert: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURCekNDQ...
https.key: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFcE...
kind: Secret
...
$ kubectl get configmap fortune-config -o yaml
apiVersion: v1
data:
my-nginx-config.conf: |
server {
...
}
sleep-interval: |
25
kind: ConfigMap
...
이진 형식이 아닌 데이터를 설정할 때 사용
kind: Secret
apiVersion: v1
stringData: # 이진 형식이 아닌 데이터 설정
foo: plain text
data:
https.cert: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURCekNDQ...
https.key: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFcE...
$ kubectl get -o yaml 검색 시 표시 안됨# 서버가 인증서와 키 파일을 읽게 설정
$ kubectl edit configmap fortune-config
...
data:
my-nginx-config.conf: |
server {
listen 80;
listen 443 ssl;
server_name www.kubia-example.com;
ssl_certificate certs/https.cert; # 인증서
ssl_certificate_key certs/https.key; # 키
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!MD5;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
}
sleep-interval: |
...
apiVersion: v1
kind: Pod
metadata:
name: fortune-https
spec:
containers:
- image: luksa/fortune:env
name: html-generator
env:
- name: INTERVAL
valueFrom:
configMapKeyRef:
name: fortune-config
key: sleep-interval
volumeMounts:
- name: html
mountPath: /var/htdocs
- image: nginx:alpine
name: web-server
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html
readOnly: true
- name: config
mountPath: /etc/nginx/conf.d
readOnly: true
- name: certs
mountPath: /etc/nginx/certs/ # nginx가 읽기로 한 위치에 시크릿 볼륨을 마운트
readOnly: true
ports:
- containerPort: 80
- containerPort: 443
volumes:
- name: html
emptyDir: {}
- name: config
configMap:
name: fortune-config
items:
- key: my-nginx-config.conf
path: https.conf
- name: certs
secret: # 참조할 시크릿 정의
secretName: fortune-https

# 443 포트 포워드
$ kubectl port-forward fortune-https 8443:443 &
# 요청 전송으로 확인
$ curl https://localhost:8443 -k
# 인증서 확인
$ curl https://localhost:8443 -k -v
* About to connect() to localhost port 8443 (#0)
* Trying ::1...
* Connected to localhost (::1) port 8443 (#0)
* Initializing NSS with certpath: sql:/etc/pki/nssdb
* skipping SSL peer certificate verification
* SSL connection using TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
* Server certificate: # 생성한 인증서
* subject: CN=www.kubia-example.com
* start date: aug 16 18:43:13 2016 GMT
* expire date: aug 14 18:43:13 2026 GMT
* common name: www.kubia-example.com
* issuer: CN=www.kubia-example.com
시크릿 볼륨은 시크릿 파일에 in-memory 파일 시스템을 사용한다.(tmpfs)
env:
- name: FOO_SECRET
valueFrom:
secretKeyRef:
name: fortune-https # 시크릿 이름
key: foo # 가져올 항목 key
⛔ 응용 프로그램은 보통 오류 로그에 환경 변수를 기록하거나 시작 로그에 기록하기 때문에 의도치 않게 노출될 위험이 있다. 또한, 하위 프로세스가 상위 프로세스의 환경 변수를 상속하기 때문에 노출 위험이 있다.
private 컨테이너 이미지 저장소의 이미지를 사용할 때, 쿠버네티스에서 자격 증명을 전달해야 한다.
$ kubectl create secret docker-registry mydockerhubsecret \
--docker-username=myusername --docker-password=mypassword \
--docker-email=my.email@provider.com
apiVersion: v1
kind: Pod
metadata:
name: private-pod
spec:
imagePullSecrets:
- name: mydockerhubsecret # 프라이빗 이미지 저장소에 접근 가능하게 참조
containers:
- image: username/private:tag
name: main
출처
Kubernetes in Action
Q. 컨피그맵의 내용을 사용하기 위해서 컨피그맵 볼륨을 컨테이너의 디렉터리로 마운트 했는데, 디렉터리의 기존 파일이 액세스가 불가해졌습니다. 이때 기존 파일도 함께 접근하려면 어떤 방식을 사용해서 볼륨을 연결해야 할까요? 작성 시에 무슨 속성을 사용하는지도 작성해주세요.
Q. 컨피그맵과 시크릿이 코드적으로(yaml파일에서) 무슨 차이가 있는지 답하고 그 덕분에 각각의 사용이 어떻게 나뉘었는지 설명해주세요.