컨피그맵(Configmap), 시크릿(Secret): 설정값을 파드에 전달
설정값이나 설정 파일을 내 애플리케이션에 전달하는 가장 확실한 방법은 도커 이미지 내부에 설정값 또는 설정 파일을 정적으로 저장해 놓는 것이다. 하지만 도커 이미지는 일단 빌드되고 나면 불변의 상태를 가지기 때문에 이 방법은 상황에 따라 설정 옵션을 유연하게 변경할 수 없다는 단점이 있다.
이에 대한 대안으로 파드를 정의하는 YAML 파일에 환경 변수를 직접 적어 놓는 하드 코딩 방식을 사용할 수도 있다.
위처럼 환경 변수를 파드 템플릿에 직접 명시하는 방식도 나쁘지는 않지만, 상황에 따라서는 환경 변수의 값만 다른, 동일한 여러 개의 YAML 파일이 존재할 수도 있다. 즉, 운영 환경과 개발 환경이 다른 경우에 각각 디플로이먼트를 생성해야 한다면 환경 변수가 서로 다르게 설정된 두 가지 버전의 YAML 파일이 따로 존재해야 하기 때문이다.
쿠버네티스는 YAML 파일과 설정값을 분리할 수 있는 컨피그맵과 시크릿이라는 오브젝트를 제공한다. 컨피그맵에는 설정값을 시크릿에는 노출되서는 안되는 비밀값을 저장할 수 있다.
kubectl create configmap <컨피그맵 이름> <각종 설정값들>
kubectl create cm log-level-configmap --from-literal LOG_LEVEL=DEBUG
--from-literal 옵션을 여러 번 사용함으로써 여러 개의 키-값을 컨피그맵에서 사용하도록 설정할 수도 있다.
kubectl create cm start-k8s --from-literal k8s=kubernetes --from-literal container=docker
컨피그맵에 저장된 설정값은 kubectl describe configmap 명령어나 kubectl get configmap -o yaml 명령어로 확인할 수 있다.
컨피그맵을 파드에서 사용하는 방법은 크게 두 가지가 있다. 내 애플리케이션이 소스코드 내부에서 어떻게 설정값을 읽는지에 따라 적절한 방법을 선택해야 한다.
- 컨피그맵의 값을 컨테이너의 환경 변수로 사용
- 컨피그맵의 값을 파드의 컨테이너 환경 변수로 가져온다. 컨피그맵에 저장된 키-값 데이터가 컨테이너 환경 변수의 키-값으로서 그대로 사용되기 때문에 echo $LOG_LEVEL과 같은 방식으로도 값을 확인할 수 있다. 내 앱이 시스템 환경 변수로부터 설정값을 가져온다면 이 방법을 사용하는 것이 좋다.
- 컨피그맵의 값을 파드 내부의 파일로 마운트해 사용
- 컨피그맵의 값을 파드 컨테이너 내부의 특정 파일로 마운트한다. 예를 들어 LOG_LEVEL=INFO 라는 값을 가지는 컨피그맵을 /etc/config/log_level 이라는 파일로 마운트하면 log_level 파일에는 INFO 라는 값이 저장된다. 이때 파일이 위치할 경로는 별도로 설정할 수 있다. 내 앱이 nginx.conf, .env 등의 파일을 통해 설정값을 읽어 들인다면 이 방법을 사용하는 것이 좋다.
컨피그맵의 데이터를 컨테이너의 환경 변수로 가져오기
생성한 컨피그맵을 불러와서 환경 변수를 설정하는 방식이다.
envFrom 항목은 하나의 컨피그맵에 여러 개의 키-값 쌍이 존재하더라도 모두 환경 변수로 가져오도록 설정한다. 따라서 start-k8s 컨피그맵에서 2개의 키-값 데이터가 한꺼번에 포드의 환경 변수로 등록된다. 즉 총 3개의 키-값 쌍을 파드로 넘긴 것이다.
valueFrom과 configMapKeyRef 를 사용하면 여러 개의 키-값 쌍이 들어 있는 컨피그맵에서 특정 데이터만을 선택해 환경 변수로 가져올 수도 있다.
$ kubectl exec continer-selective-env-example env | grep ENV
ENV_KEYNAME_2=kubernetes
ENV_KEYNAME_1=DEBUG
- envFrom: 컨피그맵에 존재하는 모든 키-값 쌍을 가져온다.
- valueFrom: 컨피그맵에 존재하는 키-값 쌍 중에서 원하는 데이터만 선택적으로 가져온다.
컨피그맵의 내용을 파일로 파드 내부에 마운트하기
내 앱이 nginx.conf, mysql.conf, .env 등과 같은 특정 파일로부터 설정값을 읽어온다면 컨피그맵의 데이터를 파드 내부의 파일로 마운트해 사용할 수 있다. 예를 들어 아래 YAML 파일은 start-k8s 컨피그맵에 존재하는 모든 키-값 쌍을 /etc/config 디렉토리에 위치시킨다.
- spec.volumes: YAML 파일에서 사용할 볼륨의 목록을 정의한다. 위 예시에서는 start-k8s 라는 이름의 컨피그맵을 통해 configmap-volume 볼륨을 정의했다.
- spec.containers.volumeMounts: volumes 항목에서 정의된 볼륨을 컨테이너 내부의 어떤 디렉토리에 마운트할 것인지 명시한다. 위 예시에서는 /etc/config 디렉토리에 컨피그맵의 값이 담긴 파일이 마운트될 것이다.
$ kubectl exec configmap-volume-pod ls /etc/config
container
k8s
$ kubectl exec configmap-volume-pod ls /etc/config/k8s
kubernetes
컨피그맵에 저장돼 있던 두 개의 키-쌍 데이터, 즉, container와 k8s라는 키 이름이 파일로 존재하고 있다. 여기서 알아둬야 할 것은 컨피그맵의 모든 키-값 쌍 데이터가 마운트 됐으며, 파일 이름은 키의 이름과 같다는 것이다.
💡 컨피그맵과 같은 쿠버네티스 리소스의 데이터를 파드 내부 디렉토리에 위치시키는 것을 쿠버네티스 공식 문서에서는 투사(Projection)라고 표현한다.
키-쌍 데이터를 파드에 마운트하는 것이 아닌, 원하는 키-쌍 데이터만 선택해서 파드에 파일로 가져올 수도 있다.
- items 항목: 컨피그맵에서 가져올 키-값의 목록을 의미하며, k8s라는 키만 가져오도록 명시했다.
- path 항목: 최종적으로 디렉토리에 위치할 파일의 이름을 입력하는 항목으로 k8s_fullname 이라는 값을 입력했다.
즉, /etc/config/k8s_fullname 경로에 파일이 위치하게 된다.
$ kubectl exec selective-cm-volume-pod ls /etc/config
k8s_fullname
$ kubectl exec selective-cm-volume-pod ls /etc/config/k8s_fullname
kubernetes
파일로부터 컨피그맵 생성
컨피그맵을 볼륨으로 포드에 제공할 때는 대부분 설정 파일 그 자체를 컨피그맵으로 사용하는 경우가 많다. 예를 들어, Nginx의 설정 파일인 nginx.conf 또는 MySQL의 설정 파일인 mysql.conf의 내용을 아예 통째로 컨피그맵에 저장한 뒤 이를 볼륨 파일로 포드 내부에 제공하면 좀 더 효율적인 설정 관리가 가능할 것이다. 이러한 경우를 위해 쿠버네티스는 컨피그맵을 파일로부터 생성하는 기능 또한 제공한다.
--from-file 옵션을 사용하면 파일로부터 컨피그맵을 생성할 수 있다. --from-file 옵션을 여러 번 사용해 여러 개의 파일을 컨피그맵에 저장할 수도 있다.
$ kubectl create configmap <컨피그맵 이름> --from-file <파일 이름>
$ echo hello, world >> index.html
$ kubectl create configmap index-file --from-file index.html
configmap/index-file created
--from-file 옵션에서 별도의 키를 지정하지 않으면 파일 이름이 키로, 파일의 내용이 값으로 저장된다. 위의 예시에서는 index.html 이라는 파일로 컨피그맵을 생성했기 때문에 index.html 이라는 키에 hello, world 라는 값이 설정됐다.
YAML 파일로 컨피그맵 정의하기
컨피그맵은 YAML 파일을 사용해서 오브젝트를 생성할 수 있다.
컨피그맵 ⇒ 환경변수로 줄 수도, 파드에 마운트시켜서 컨피그맵 내부의 값을 주거나, 파일을 컨피그맵으로 만들어서 볼륨 마운트시켜 파드에 넘겨줄 수도 있다.
시크릿 사용 방법 익히기
시크릿은 SSH 키, 비밀번호 등과 같이 민감한 정보를 저장하기 위한 용도로 사용되며, 네임스페이스에 종속되는 쿠버네티스 오브젝트이다. 시크릿과 컨피그맵은 사용 방법이 매우 비슷하다.
시크릿은 민감한 정보를 저장하기 위해 컨피그맵보다 좀 더 세분화 된 사용법을 제공한다.
다음 명령어는 my-password 라는 이름의 시크릿을 생성하며, password=1q2w3e4r 이라는 키-값 쌍을 저장한다.
$ kubectl create secret generic my-password --from-literal password=1q2w3e4r
또한, 컨피그맵처럼 --from-literal 대신 --from-file 이나 --from-env-file 옵션을 이용해 파일로부터 값을 읽어와 사용해도 된다.
$ echo mypassword > pw1 && echo yourpassword > pw2
$ kubectl create secret generic our-password --from-file pw1 --from-file pw2
시크릿은 컨피그맵과 달리 데이터의 사용 목적에 따라 몇 가지 종류로 나뉘기 때문에 generic 과 같은 특수 옵션에 대해 알아야 한다.
kubectl describe secret 으로 방금 생성한 시크릿을 확인하면 키-값 쌍 에서 값 부분이 이상한 값으로 변형돼 표기되는 것을 볼 수 있고, 이는 시크릿에 값을 저장할 때 쿠버네티스가 기본적으로 base64로 값을 인코딩하기 때문이다.
따라서 YAML 파일로부터 시크릿을 생성할 때도 데이터의 값에 base64로 인코딩 된 문자열을 사용해야 한다.
시크릿의 키-값 데이터를 파드의 환경 변수로 설정할 수도 있고, 특정 경로의 파일로 파드 내에 마운트할 수도 있다.
아래 예시를 확인하자.
또는 시크릿의 키-값 데이터를 파일로 파드의 볼륨에 마운트할 수도 있으며, 여러 개의 키-값 쌍이 존재하는 시크릿에서 선택적으로 사용할 수도 있다.
이미지 레지스트리 접근을 위한 docker-registry 타입의 시크릿 사용
시크릿은 컨피그맵처럼 단순 문자열이나 설정 파일 등을 저장할 때 사용할 수도 있지만 사용 목적에 따라 여러 종류의 시크릿을 사용할 수도 있다.
시크릿의 기본 타입은 Opaque 로 설정돼 있고, Opaque 타입은 별도로 시크릿의 종류를 명시하지 않으면 자동으로 설정되는 타입이며, 사용자가 정의하는 데이터를 저장할 수 있는 일반적인 목적의 시크릿이다.
kubectl create 명령어로 시크릿을 생성할 때 generic 이라고 명시했던 것이 바로 Opaque 타입에 해당하는 종류이다.
사설 레지스트리에 접근하기 위해서는 cli 상에서 로그인이 필요로 한데 도커의 경우 docker login 을 사용하여 사설 레지스트리에 인증한다.
쿠버네티스에서는 docker login 명령어 대신 레지스트리의 인증 정보를 저장하는 별도의 시크릿을 생성해 사용한다. 레지스트리 인증을 위해 시크릿을 생성하는 방법은 두 가지가 있다.
첫번째는 docker login 명령어로 로그인에 성공했을 때 도커 엔진이 자동으로 생성하는 ~/.docker/config.json 파일을 사용하는 것이다. config.json 파일에는 인증을 위한 정보가 담겨 있기 때문에 이를 그대로 시크릿으로 가져오면 된다.
$ kubectl create secret generic registry-auth \
--from-file=.dockerconfigjson=/root/.docker/config.json \
--type=kubernetes.io/dockerconfigjson
또는 시크릿을 생성하는 명령어에서 직접 로그인 인증 정보를 명시할 수도 있다. 각 옵션에 적절한 인자를 입력하면 되며, --docker-username과 --docker-password 옵션은 로그인 이름과 비밀번호를 입력하는 필수 옵션이다.
$ kubectl create secret docker-registry registry-auth-bt-cmd \
--docker-username=how0326 \
--docker-password=12345
--docker-server 는 필수 옵션이 아니며, 필요에 따라 사용하면 된다. --docker-server 옵션을 사용하지 않으면 기본적으로 도커 허브(docker.io)를 사용하도록 설정되지만, 다른 사설 레지스트리를 사용하려면 --docker-server 옵션에 해당 서버의 주소 또는 도메인 이름을 입력하면 된다.
$ kubectl create secret docker-registry registry-auth-bt-cmd \
--docker-username=how0326 \
--docker-password=12345 \
--docker-server=ecs.registry.com
위 명령어로 생성된 시크릿은 kubernetes.io/dockerconfigjson 이라는 타입으로 설정된다.
이 시크릿은 디플로이먼트 또는 포드 등에서 사설 레지스트리로부터 이미지를 받아올 때 사용할 수 있다. 예를 들어 도커 허브의 프라이빗 저장소에 저장된 이미지를 통해 파드를 생성하려면 다음과 같이 YAML 파일에서 imagePullSecret 항목을 정의한다.
💡 기본적으로는 YAML 파일에 명시된 도커 이미지가 워커 서버에 존재하지 않을 때만 이미지를 받아오도록 설정돼 있지만, imagePullPolicy 항목을 통해 이미지를 받아오는 설정을 변경할 수 있다. imagePullPolicy의 자세한 사용 방법은 쿠버네티스 공식 문서를 참고할 수 있다.
TLS 키를 저장할 수 있는 tls 타입의 시크릿 사용
시크릿은 TLS 연결에 사용되는 공개키, 비밀키 등을 쿠버네티스에 자체적으로 저장할 수 있도록 tls 타입을 지원한다. 따라서 파드 내부의 애플리케이션이 보안 연결을 위해 인증서나 비밀키 등을 가져와야 할 때 시크릿의 값을 파드에 제공하는 방식으로 사용할 수 있다.
tls 타입의 시크릿을 사용하는 방법은 매우 간단하다. 보안 연결에 사용되는 키 페어가 미리 준비돼 있다면 kubectl create secret tls 명령어로 쉽게 생성할 수 있다.
$ openssl req -new -newkey rsa:4096 -days 365 -nodes -x509 -subj "/CN=example.com" -keyout cert.key -out cert.crt
$ ls
cert.crt cert.key ...
$ kubectl create secret tls my-tls-secret --cert cert.crt --key cert.key
secret/my-tls-sercret created
생성된 시크릿의 정보를 확인해보면 cert.crt 와 cert.key 파일의 내용이 tls.crt 와 tls.key 라는 키로 저장돼 있음을 알 수 있다. 각 데이터는 모두 base64로 인코딩되어 저장된다.
좀 더 쉽게 컨피그맵과 시크릿 리소스 배포하기
CLI 상에서 시크릿을 만드는 것보다 YAML 을 통해 시크릿 오브젝트를 배포할 수 있다. 그러나 시크릿의 데이터가 많아질수록 YAML 파일에 직접 시크릿의 데이터를 저장하는 것은 바람직한 방법이 아니다.
이러한 단점을 해결하면서 시크릿이나 컨피그맵을 배포하기 위해 YAML 파일을 작성할 때, 데이터를 YAML 파일로부터 분리할 수 있는 kustomize 기능을 사용할 수 있다.
kustomize는 자주 사용되는 YAML 파일의 속성을 별도로 정의해 재사용하거나 여러 YAML 파일을 하나로 묶는 등 다양한 용도로 사용할 수 있는 기능이다. 그렇지만 지금은 시크릿과 컨피그맵을 좀 더 쉽게 쓰기 위한 용도로 kustomize를 간단하게 사용해보자.
kustomize 로부터 생성될 시크릿의 정보를 미리 확인하려면 kubectl kustomize 명령어를 사용한다.
kustomization.yaml 파일로부터 시크릿을 생성하려면 해당 파일이 위치한 디렉토리에서 kubectl apply -k ./ 명령어를 사용한다.
만약 컨피그맵을 kustomize 로부터 생성하고 싶다면 kustomization.yaml 파일에서 secretGenerator 대신 configmapGenerator 를 사용하면 된다. 컨피그맵은 시크릿과 달리 종류가 존재하지 않으므로 type 항목은 제거한다.