쿠버네티스에서 애플리케이션에 설정이 전달되는 과정컨피그맵컨피그맵에 저장한 설정 파일 사용하기환경변수 출처에 따른 우선 순위컨피그맵에 담긴 설정값 데이터 주입하기컨피그맵 데이터 중 단일 항목만 전달하기비밀값을 이용하여 민감한 정보가 담긴 설정값 다루기env 값을 secret을 통해 설정하기 (secretKeyRef)env 값을 volumeMount 를 통해 secret에서 가져오기쿠버네티스의 애플리케이션 설정 관리
쿠버네티스에서 컨테이너에 설정값을 주입하는 데 쓰는 리소스는
컨피그맵
과 비밀값
두 가지다. 이 두가지 리소스 모두 포맷 제한 없이 데이터를 보유할 수 있다. 이 데이터는 클러스터 속에서 다른 리소스와 독립적인 장소에 보관된다. 파드 정의에서 컨피그맵과 비밀값의 데이터를 읽어 오도록 할 수 있다. 이때 파드에 데이터가 전달되는 과정에도 다양한 설정이 가능하다.쿠버네티스에서 애플리케이션에 설정이 전달되는 과정
- 컨피그맵과 비밀값 역시 다른 쿠버네티스 리소스와 마찬가지로 create 명령 or YAML 포맷으로 정의가능함
- 이들 리소스는 파드로 전달되어 컨테이너 환경의 일부가 되는데, 이 상태에서 컨테이너가 컨피그맵이나 비밀값에 저장된 데이터를 읽을 수 있다.
컨피그맵
- 컨피그맵은 파드에서 읽어 들이는 데이터를 저장하는 리소스로, 데이터 형태는 한 개 이상의 키-값 쌍, 텍스트, 바이너리 파일까지 다양함
- 파드 하나에 여러 개의 컨피그맵을 전달할 수 있고, 하나의 컨피그맵을 여러 파드에 전달할 수도 있다.
- 컨피그맵을 사용하지 말아야 할 경우는 민감한 데이터를 다룰 때 뿐임
apiVersion: apps/v1 kind: Deployment metadata: name: sleep spec: selector: matchLabels: app: sleep template: metadata: labels: app: sleep spec: containers: - name: sleep image: kiamol/ch03-sleep env: - name: KIAMOL_CHAPTER value: "04" - name: KIAMOL_SECTION valueFrom: configMapKeyRef: # 이 값은 컨피그맵에서 읽어 들이라는 의미 name: sleep-config-literal # 컨피그맵 이름 key: kiamol.section # 컨피그맵에서 읽어 들일 항목 이름
- 위 파드는 해당 컨피그맵이 있어야 클러스터에 배치할 수있다.
# 명령행 도구를 사용하여 컨피그맵 생성 kubectl create configmap sleep-config-literal --from-literal=kiamol.section='4.1' # 컨피그맵에 들어 있는 데이터 확인 kubectl get cm sleep-config-literal # 컨피그맵의 상세 정보를 보기 좋게 출력 kubectl describe cm sleep-config-literal
컨피그맵에 저장한 설정 파일 사용하기
# .env 파일을 이용해 configMap 생성 $ kubectl create configmap sleep-config-env-file --from-env-file=sleep/ch04.env
apiVersion: apps/v1 kind: Deployment metadata: name: sleep spec: selector: matchLabels: app: sleep template: metadata: labels: app: sleep spec: containers: - name: sleep image: kiamol/ch03-sleep envFrom: - configMapRef: name: sleep-config-env-file env: - name: KIAMOL_CHAPTER value: "04" - name: KIAMOL_SECTION valueFrom: configMapKeyRef: name: sleep-config-literal key: kiamol.section
- 환경 변수 이름이 중복되는 경우
env
항목에서 정의된 값이envFrom
항목에서 정의된 값에 우선한다. (env
>envFrom
)
환경변수 출처에 따른 우선 순위
- 기본 설정값은 컨테이너 이미지에 포함. 이미지에 포함되는 설정은 모든 환경에 공통으로 적용되는 일부 설정일 수도 있고, 모든 설정의 기본값을 포함시켜 별도의 추가 설정이 없다면 애플리케이션이 개발 모드로 동작하게 해도 좋다.
- 각 환경의 실제 설정값은 컨피그맵에 담겨 컨테이너의 파일 시스템에 전달됨. 주로 애플리케이션 설정 파일을 찾도록 지정한 경로에 설정 데이터를 파일 형태로 주입 or 컨테이너 이미지에 담긴 파일을 덮어쓰는 형태
- 변경이 필요한 설정값은 디플로이먼트 내 파드 정의에서 환경 변수 형태로 적용
컨피그맵에 담긴 설정값 데이터 주입하기
환경 변수 위에 설정값을 전달하는 또 다른 방법은 컨테이너 파일 시스템 속 파일로 설정값을 주입하는 것
이 과정에는 파드 정의의 두 가지 항목과 관련된 기능이 관여한다. 첫 번째는 컨피그맵에 담긴 데이터를 파드로 전달하는
볼륨
이다. 두 번째는 컨피그맵을 읽어 들인 볼륨을 파드 컨테이너의 특정 경로에 위치시키는 볼륨 마운트
다.apiVersion: apps/v1 kind: Deployment metadata: name: todo-web spec: selector: matchLabels: app: todo-web template: metadata: labels: app: todo-web spec: containers: - name: web image: kiamol/ch04-todo-list volumeMounts: # 컨테이너에 볼륨을 마운트 - name: config # 마운트할 볼륨 이름 mountPath: "/app/config" # 볼륨이 마운트 될 경로 readOnly: true # 볼륨을 읽기 전용으로 volumes: # 볼륨은 파드 수준에서 정의됨 - name: config # 이 이름이 볼륨 마운트의 이름과 일치해야 함 configMap: # 볼륨의 원본은 컨피그맵이다. name: todo-web-config-dev # 내용을 읽어 올 컨피그맵 이름
- 컨피그맵이 디렉터리로 취급되고 컨피그맵 속 각각의 항목이 컨테이너 파일 시스템 속 파일이 됨
- 위 예제의 애플리케이션은 /app/appsettings.json 파일에서 기본 설정을 읽고, 그 다음으로 /app/config/config.json 파일을 찾아 이 파일에 담긴 설정값을 우선 적용함.
# 애플리케이션 로그 확인 kubectl logs -l app=todo-web # 컨피그맵 업데이트 kubectl apply -f todo-list/configMaps/todo-web-config-dev-with-logging.yaml # 업데이트된 컨피그맵이 파드에 반영될 때까지 대기 sleep 120 # 설정 파일에 반영되었는지 확인 kubectl exec deploy/todo-web -- sh -c 'ls -l /app/config/*.json' # 애플리케이션에 접근하여 로그 출력이 변화했는지 확인 kubectl logs -l app=todo-web
- 파드가 동작중인 상황에서 컨피그맵을 업데이트하면, 쿠버네티스가 수정된 파일을 volumeMount 된 컨테이너에 전달함. 애플리케이션이 설정 파일의 변경에 곧바로 반응하는 앱이라면 효과가 매우 크다.
# 설정에 오류가 있는 파드 배치 kubectl apply -f todo-list/todo-web-dev-broken.yaml # 애플리케이션 로그 확인 kubectl logs -l app=todo-web # 파드 상태도 확인 kubectl get pods -l app=todo-web
- 오류가 있는 파드를 실행하려 하면 계속 오류가 뜨면서 새 파드가 정상 실행되지 않음. 그러면 CrashLoopBackOff 상태가 되며 잠시 재시작을 멈추고, 기존의 파드는 그대로 남아 있게 됨(새 파드가 시작해야 기존 파드가 종료되므로)
컨피그맵 데이터 중 단일 항목만 전달하기
apiVersion: v1 kind: ConfigMap metadata: name: todo-web-config-dev data: config.json: |- { "ConfigController": { "Enabled" : true } } logging.json: |- { "Logging": { "LogLevel": { "ToDoList.Pages" : "Debug" } } } --- apiVersion: apps/v1 kind: Deployment metadata: name: todo-web spec: selector: matchLabels: app: todo-web template: metadata: labels: app: todo-web spec: containers: - name: web image: kiamol/ch04-todo-list volumeMounts: - name: config mountPath: "/app/config" readOnly: true volumes: - name: config configMap: name: todo-web-config-dev items: - key: config.json path: config.json
- 위 Deployment에서 volumes에 보면 configMap에서 config.json key 만 전달하고 있다.
비밀값을 이용하여 민감한 정보가 담긴 설정값 다루기
비밀값은 컨피그맵과 비슷한 api를 가진 별개의 리소스다. 사용 방법은 컨피그맵과 크게 다르지 않고 다른점이 있다면 비밀값은 민감한 정보를 다루므로 클러스터 내부에서 별도로 관리된다는 것임
비밀값 객체에 접근할 권한이 있다면 비밀값의 평문을 읽을 수 있다. 하지만 Base64로 인코딩 되어 있어, 값을 디코딩해야 원문을 볼 수 있음
env 값을 secret을 통해 설정하기 (secretKeyRef)
# 평문 리터럴로 비밀값 생성 kubectl create secret generic sleep-secret-literal --from-literal=secre t=sh...
apiVersion: apps/v1 kind: Deployment metadata: name: sleep spec: selector: matchLabels: app: sleep template: metadata: labels: app: sleep spec: containers: - name: sleep image: kiamol/ch03-sleep env: - name: KIAMOL_SECRET valueFrom: secretKeyRef: # 비밀값에서 도입 name: sleep-secret-literal # 비밀값 이름 key: secret # 비밀값의 항목 이름
kubectl exec deploy/sleep -- printenv KIAMOL_SECRET sh..
- 어떤 방법을 사용하든 쿠버네티스 클러스터에 들어간 비밀값은 권한이 있는 사람이라면 누구나 값을 볼 수 있는 상태
env 값을 volumeMount 를 통해 secret에서 가져오기
apiVersion: v1 kind: Secret metadata: name: todo-db-secret-test type: Opaque stringData: POSTGRES_PASSWORD: "kiamol-2*2*" --- apiVersion: v1 kind: Service metadata: name: todo-db spec: ports: - port: 5432 targetPort: 5432 selector: app: todo-db type: ClusterIP --- apiVersion: apps/v1 kind: Deployment metadata: name: todo-db spec: selector: matchLabels: app: todo-db template: metadata: labels: app: todo-db spec: containers: - name: db image: postgres:11.6-alpine env: - name: POSTGRES_PASSWORD_FILE value: /secrets/postgres_password volumeMounts: - name: secret mountPath: "/secrets" volumes: - name: secret secret: secretName: todo-db-secret-test defaultMode: 0400 # 컨테이너 사용자만 읽을 수 있다. items: - key: POSTGRES_PASSWORD path: postgres_password # 해당 데이터를 저장할 경로
비밀값을 PostgreSQL 데이터베이스의 패스워드로 사용하는 방법은 두 가지다.
- 이상적인 방법은 아니지만 컨테이너 환경에 POSTGRES_PASSWORD 라는 환경 변수로 직접 전달하는 방법
- 컨테이너 환경에 파일 형태로 전달한 후 이 설정 파일의 경로를 환경 변수
POSTGRES_PASSWORD_FILE
에 지정하는 방법 (위의 예시)
쿠버네티스의 애플리케이션 설정 관리
애플리케이션에서 핵심적인 요구사항은 결국 외부 환경에서 설정값을 주입받는 것이다. 그 중에서도 나름의 우선순위가 부여되어 파일과 환경 변수의 형태로 주입되는 것이 이상적
이런 조건을 달성해야 컨피그맵과 비밀값을 배치 절차에 활용할 만한 유연성을 갖추었다고 할 수 있다.
이를 위해 설계 단계에서 염두에 두어야 할 질문이 두 가지 있다.
- 애플리케이션의 중단 없이 설정 변경에 대응이 필요한가?
- 파드 교체조차 필요 없는 무중단 업데이트가 필요 → 환경 변수는 활용 불가(파드 교체가 필요), 볼륨 마운트를 이용하여 설정 파일을 수정해야 함. 볼륨을 수정하게 되면 파드 교체가 불가피
- 컨피그맵이나 비밀값 등 설정 객체를 업데이트하지 않는 대안은 설정 객체의 이름에 버전 명명법을 도입하고 애플리케이션을 업데이트할 때 새로운 설정 객체를 배치한 후 이 새로운 설정 객체를 가리키게 애플리케이션 정의를 수정하는 방식이다.
- 민감 정보를 어떻게 관리할 것인가
- 형상 관리 도구에 저장된 yaml 템플릿 파일로 컨피그맵과 비밀값 정의가 생성되는 완전 자동화된 배치. YAML 템플릿 파일에는 실제 민감 정보 대신 해당 정보가 채워질 빈칸을 두고, 배치 절차 중에 이 빈칸을 AWS SecretManager 같은 안전한 곳에 보관되어 있던 실제 민감 정보로 채워 YAML 파일을 완성하는 방식.