쿠버네티스에서 설정정보를 저장하고 활용하는 내부구조

2021-09-11

.

Data_Engineering_TIL(20210911)

[학습자료]

“클라우드 네이티브를 위한 쿠버네티스 실전 프로젝트” 책을 읽고 정리한 내용입니다.

** 동양북스, 아이자와 고지&사토 가즈히코 지음, 박상욱 옮김

참고자료 URL : https://github.com/dybooksIT/k8s-aws-book

“쿠버네티스 서비스 리소스(ELB)에 HTTPS 적용하기”에 이어서 공부한 내용을 정리한 내용임

** URL : https://minman2115.github.io/DE_TIL277

[학습내용]

  1. 시크릿을 이용한 비밀정보 전달

“EKS 클러스터에 API 어플리케이션 배포하기” 에서 아래와 같이 디비접속을 위한 시크릿을 등록을 했다.

** URL : https://minman2115.github.io/DE_TIL253

사용자 이름, 비밀번호 등 누구나 접근할 수 있는 장소에 저장하면 위험하다. 그래서 미리 안전한 장소에 해당 정보들을 등록해두고 그 정보를 참조하는 방법으로 서비스를 구성해야 한다. 쿠버네티스에서는 시크릿이라는 리소스를 이용해 이러한 구조를 구현할 수 있다.

아래에 명령어에서 backend-app이 사용하는 디비 접속정보를 미리 쿠버네티스의 시크릿에 등록해놓는다. 그리고 파드의 매니패스트는 해당 시크릿에서 값을 읽어들여 파드 내부의 환경변수에 설정하도록 정의한다.

[ec2-user@ip-10-10-1-236 ~]$ cd /home/ec2-user/k8s-aws-book/eks-env

[ec2-user@ip-10-10-1-236 eks-env]$ cat 21_db_config_k8s.yaml.template
apiVersion: v1
kind: Secret
type: Opaque
metadata:
  name: db-config
stringData:
  db-url: ${DB_URL}
  db-username: mywork
  db-password: ${DB_PASSWORD}

# STEP 3) 디비 접속용 시크릿 등록
# API 어플리케이션이 디비에 접속하기 위한 비밀번호 등을 저장하는 '시크릿' 이라는 것을 아래와 같이 생성한다.
# rds 엔드포인트는 클라우드 포메이션의 출력값을 확인하면 되고, 디비 패스워드는 시크릿 매니저 서비스에서 확인하면 된다.
[ec2-user@ip-10-0-1-236 eks-env]$ DB_URL=jdbc:postgresql://eks-work-db.xxxxxxxx.ap-northeast-2.rds.amazonaws.com/myworkdb DB_PASSWORD='wdivF6S}_gC#Ifog' envsubst < 21_db_config_k8s.yaml.template | kubectl apply -f -
secret/db-config created

아래의 명령어는 파드에서 시크릿을 참조하는 구조에 대한 내용이다.

# API 어플리케이션 배포
# 디플로이먼트 오브젝트를 생성한다. 디플로이먼트를 생성하면 그 뒤에 레플리카 셋이 생성되며 레플리카셋 뒤에는 파드가 생성된다.
[ec2-user@ip-10-0-1-236 eks-env]$ ECR_HOST=xxxxxxxxxx.dkr.ecr.ap-northeast-2.amazonaws.com envsubst < 22_deployment_backend-app_k8s.yaml.template | kubectl apply -f -
deployment.apps/backend-app created

[ec2-user@ip-10-0-1-236 eks-env]$ cat 22_deployment_backend-app_k8s.yaml.template
apiVersion: apps/v1
kind: Deployment
metadata:
  name: backend-app
  labels:
    app: backend-app
spec:
  replicas: 2
  selector:
    matchLabels:
      app: backend-app
  template:
    metadata:
      labels:
        app: backend-app
    spec:
      containers:
      - name: backend-app
        image: ${ECR_HOST}/k8sbook/backend-app:1.0.0
        imagePullPolicy: Always
        ports:
        - containerPort: 8080
        env:
        - name: DB_URL
          valueFrom:
            secretKeyRef:
              key: db-url
              name: db-config
        - name: DB_USERNAME
          valueFrom:
            secretKeyRef:
              key: db-username
              name: db-config
        - name: DB_PASSWORD
          valueFrom:
            secretKeyRef:
              key: db-password
              name: db-config
        readinessProbe:
          httpGet:
            port: 8080
            path: /health
          initialDelaySeconds: 15
          periodSeconds: 30
        livenessProbe:
          httpGet:
            port: 8080
            path: /health
          initialDelaySeconds: 30
          periodSeconds: 30
        resources:
          requests:
            cpu: 100m
            memory: 512Mi
          limits:
            cpu: 250m
            memory: 768Mi
        lifecycle:
          preStop:
            exec:
              command: ["/bin/sh", "-c", "sleep 2"]

실제 동작중인 파드 내부의 환경변수는 아래와 같이 조회할 수 있다. 환경변수에 시크릿 값이 설정된 것을 알 수 있다.

[ec2-user@ip-10-10-1-236 ~]$ kubectl get pod
NAME                          READY   STATUS    RESTARTS   AGE
backend-app-7fb899969-mm5dw   1/1     Running   0          45h
backend-app-7fb899969-txg5x   1/1     Running   0          45h

[ec2-user@ip-10-10-1-236 ~]$ kubectl exec backend-app-7fb899969-mm5dw -it -- env | grep DB
DB_URL=jdbc:postgresql://eks-work-db.xxxxxxxxx.ap-northeast-2.rds.amazonaws.com/myworkdb
DB_USERNAME=mywork
DB_PASSWORD=xxxxxxxxx

시크릿에 민감한 정보의 설정값들을 저장해두는 것만으로는 보안을 보장해주는 것이라고 말할 수 없다. 그리고 주의해야할 점이 쿠버네티스 매니페스트 파일이 대부분 infra as a code이기 때문에 깃허브에 업로드하는 실수도 발생할 수 있다.

시크릿 내용은 base64로 인코딩되어 저장되어 있지만 base64는 아래와 같이 해독이 가능하기 때문이다.

[ec2-user@ip-10-10-1-236 ~]$ sudo yum install jq -y

[ec2-user@ip-10-10-1-236 ~]$ kubectl get secret db-config -o json | jq -r '.data'
{
  "db-password": "yyyyyyyyyyyyyyyyyyyyy",
  "db-url": "zzzzzzzzzzzzzzzzzzzzzzzzz",
  "db-username": "tttttttttt"
}

[ec2-user@ip-10-10-1-236 k8s-aws-book]$ kubectl get secret db-config -o json | jq -r '.data["db-password"]' | base64 -d
yyyyyyyyyyyyyyyyyyyyy
  • 컨피그맵을 이용한 설정정보 전달

“EKS 클러스터에 배치 어플리케이션 배포하기” 블로그글 참고할 것

** URL : https://minman2115.github.io/DE_TIL259/

비밀정보가 아닌 일반정보일 경우에는 시크릿을 이용해서 암호화할 필요가 없기 때문에 평문으로 등록해도 된다. 이러한 용도에는 컨피그맵을 이용한다.

아래와 같이 컨피그맵을 등록해주고 그것을 파드에서 참조하는 구조이다.

[ec2-user@ip-10-10-1-236 ~]$ cd /home/ec2-user/k8s-aws-book/eks-env

[ec2-user@ip-10-10-1-236 eks-env]$ cat 41_config_map_batch_k8s.yaml.template
apiVersion: v1
kind: ConfigMap
metadata:
  name: batch-app-config
data:
  bucket-name: eks-work-batch-${BUCKET_SUFFIX}
  folder-name: locationData
  batch-run: "true"
  aws-region: ap-northeast-2
    
[ec2-user@ip-10-10-1-236 eks-env]$ BUCKET_SUFFIX=xxx envsubst < 41_config_map_batch_k8s.yaml.template | kubectl apply -f -
configmap/batch-app-config created

[ec2-user@ip-10-10-1-236 eks-env]$ kubectl describe configmap batch-app-config
Name:         batch-app-config
Namespace:    eks-work
Labels:       <none>
Annotations:  <none>

Data
====
aws-region:
----
ap-northeast-2
batch-run:
----
true
bucket-name:
----
eks-work-batch-pms
folder-name:
----
locationData

BinaryData
====

Events:  <none>

컨피그맵은 배치 어플리케이션(43_cronjob_k8s.yaml.template)에서 참조하게 된다.

“EKS 클러스터에 배치 어플리케이션 배포하기” 블로그글을 보면 시크릿(AWS accesskey, secret key가 저장된 시크릿)도 참조하고, 컨피그맵도 참조하는 것을 알 수 있다.

[ec2-user@ip-10-10-1-181 eks-env]$ cat 42_batch_secrets_k8s.yaml.template
apiVersion: v1
kind: Secret
type: Opaque
metadata:
  name: batch-secret-config
stringData:
  aws-accesskey: ${AWS_ACCESSKEY}
  aws-secretkey: ${AWS_SECRETKEY}

[ec2-user@ip-10-10-1-181 eks-env]$ AWS_ACCESSKEY=xxxxxxx AWS_SECRETKEY=yyyyyyy envsubst < 42_batch_secrets_k8s.yaml.template | kubectl apply -f -
secret/batch-secret-config created

[ec2-user@ip-10-10-1-236 eks-env]$ kubectl get secret batch-secret-config -o json | jq -r '.data'
{
  "aws-accesskey": "xxxxxxxxxxxxxx",
  "aws-secretkey": "yyyyyyyyyyyyyy"
}

[ec2-user@ip-10-10-1-236 eks-env]$ cat 43_cronjob_k8s.yaml.template
apiVersion: batch/v1beta1
kind: CronJob
metadata:
  name: batch-app
spec:
  schedule: "*/5 * * * *" # min hour day-of-month month day-of-week
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: batch-app
            image: ${ECR_HOST}/k8sbook/batch-app:1.0.0
            imagePullPolicy: Always
            env:
            - name: DB_URL
              valueFrom:
                secretKeyRef:
                  key: db-url
                  name: db-config
            - name: DB_USERNAME
              valueFrom:
                secretKeyRef:
                  key: db-username
                  name: db-config
            - name: DB_PASSWORD
              valueFrom:
                secretKeyRef:
                  key: db-password
                  name: db-config
            - name: CLOUD_AWS_CREDENTIALS_ACCESSKEY
              valueFrom:
                secretKeyRef:
                  key: aws-accesskey
                  name: batch-secret-config
            - name: CLOUD_AWS_CREDENTIALS_SECRETKEY
              valueFrom:
                secretKeyRef:
                  key: aws-secretkey
                  name: batch-secret-config
            - name: CLOUD_AWS_REGION_STATIC
              valueFrom:
                configMapKeyRef:
                  key: aws-region
                  name: batch-app-config
            - name: SAMPLE_APP_BATCH_BUCKET_NAME
              valueFrom:
                configMapKeyRef:
                  key: bucket-name
                  name: batch-app-config
            - name: SAMPLE_APP_BATCH_FOLDER_NAME
              valueFrom:
                configMapKeyRef:
                  key: folder-name
                  name: batch-app-config
            - name: SAMPLE_APP_BATCH_RUN
              valueFrom:
                configMapKeyRef:
                  key: batch-run
                  name: batch-app-config
          restartPolicy: OnFailure

시크릿과 컨피그맵은 환경변수가 아닌 파드 내부의 볼륨으로 마운트하여 파일로 전달할 수도 있다. 어플리케이션 특성상 환경변수로는 전달할 수 없거나, 미들웨어의 사용제약으로 파일을 참조할 수 밖에 없는 경우 등에는 마운트하는 방식을 사용할 수도 있다.