DevOps/Kubernetes

[Kubernetes] ConfigMap & Secret ( + 환경변수)

TTOII 2022. 5. 29. 21:56
728x90

✔️ 환경변수

 vagrant@k8s-node1  ~  kubectl explain pods.spec.containers.env
  • name : 환경변수의 이름
  • value : 환경변수의 값

myweb.yaml

apiVersion: v1
kind: Pod
metadata:
  name: myweb-env
spec:
  containers:
    - name: myweb
      image: ghcr.io/c1t1d0s7/go-myweb:alpine
      env:
        - name: MESSAGE # 환경변수명
          value: "Customized Hello World"
 vagrant@k8s-node1  ~/configure/env  kubectl create -f myweb.yaml                        
pod/myweb-env created

 vagrant@k8s-node1  ~/configure/env  kubectl get po              
NAME                                      READY   STATUS    RESTARTS   AGE
myweb-env                                 1/1     Running   0          3s
nfs-client-provisioner-758f8cd4d6-wpjbt   1/1     Running   0          53m
 vagrant@k8s-node1  ~/configure/env  kubectl exec -it myweb-env -- sh        
/ # env
KUBERNETES_PORT=tcp://10.233.0.1:443
KUBERNETES_SERVICE_PORT=443
HOSTNAME=myweb-env
SHLVL=1
HOME=/root
TERM=xterm
KUBERNETES_PORT_443_TCP_ADDR=10.233.0.1
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
KUBERNETES_PORT_443_TCP_PORT=443
KUBERNETES_PORT_443_TCP_PROTO=tcp
KUBERNETES_SERVICE_PORT_HTTPS=443
KUBERNETES_PORT_443_TCP=tcp://10.233.0.1:443
MESSAGE=Customized Hello World
KUBERNETES_SERVICE_HOST=10.233.0.1
PWD=/

컨테이너 내부에 MESSAGE=Customized Hello World의 환경변수를 제공한다.

 

 vagrant@k8s-node1  ~/configure/env  kubectl port-forward pod/myweb-env 8080:8080 
Forwarding from 127.0.0.1:8080 -> 8080
Forwarding from [::1]:8080 -> 8080
Handling connection for 8080

 vagrant@k8s-node1  ~  curl localhost:8080                         
Customized Hello World
myweb-env

포트 포워딩을 걸어놓고 접속하면 Customized Hello World가 출력된다.
원래 Hello World만 출력되지만 해당 App안에는 MESSAGE 환경변수가 있으면
그 값을 리턴 시키는 기능을 가지고 있기 때문이다.

 

✔️ ConfigMap & Secret 사용 이유

Kubernetes IN ACTION

ConfigMap과 Secret 오브젝트를 사용하는 이유를 생각해보자

우리가 서비스를 배포할 때는 개발환경과 상용환경의 차이가 있을 것이다.
그 이유는, 대부분 정보나 보안 접근에서 차이가 있기 때문이다.

따라서, 상용환경과 개발환경에서 배포를 한다면 값이 바뀌어야한다는 것을 의미한다.

이러한 값은 컨테이너의 이미지 값이기 때문에 값을 바꾼다는 것은 개발환경과 상용환경에서 사용하는 이미지를 각각 관리해야한다는 것을 말한다.

하지만 상용환경과 개발환경에 사용되는 컨테이너는 같아야한다. 그래야만 개발과 서비스 사이의 환경적 차이에서 오는 문제점을 방지할 수 있다. 

 

 

그러므로, 변하는 값들을 외부에서 결정이 가능하도록 도움을 주는 Object인 ConfigMap과 Secret을 사용한다.

즉, 컨테이너에 필요한 환경 설정을 컨테이너와 분리해서 제공하는 방식을 택하는 것이다.

 

분리해야 하는 일반적인 상수들을 모아서 ConfigMap으로 만들고 Key와 같은 보안적인 관리가 필요한 것을 모아서 Secret으로 만들고 Pod 생성시 환경변수로서 연결해 사용한다.

데이터만 바꿔주면서 똑같은 이미지로 개발환경과 상용환경에서 사용이 가능하게 된다.

 

✔️ ConfigMap

컨피그맵(ConfigMap) | Kubernetes

컨피그맵은 키-값 쌍으로 기밀이 아닌 데이터를 저장하는 데 사용하는 API 오브젝트이다.
파드는 볼륨에서 환경 변수, 커맨드-라인 인수 또는 구성 파일로 컨피그맵을 사용할 수 있다.
컨피그맵을 사용하면 컨테이너 이미지에서 환경별 구성을 분리하여, 애플리케이션을 쉽게 이식할 수 있다.
주의: 컨피그맵은 보안 또는 암호화를 제공하지 않는다. 저장하려는 데이터가 기밀인 경우, 컨피그맵 대신 시크릿(Secret) 또는 추가(써드파티) 도구를 사용하여 데이터를 비공개로 유지하자.

 vagrant@k8s-node1  ~/configure/cm  kubectl api-resources | grep configmap                                           
configmaps                        cm           v1                                     true         ConfigMap
 vagrant@k8s-node1  ~/configure/cm  kubectl explain cm.data  
FIELD:    data <map[string]string>

DESCRIPTION:
     Data contains the configuration data. Each key must consist of alphanumeric
     characters, '-', '_' or '.'. Values with non-UTF-8 byte sequences must use
     the BinaryData field. The keys stored in Data must not overlap with the
     keys in the BinaryData field, this is enforced during validation process.

 

data는 해시 방식으로 key : value를 지정한다.

  • CM : 기밀이 아닌 데이터
  • Secret : 기밀 데이터

CMSecret 모두 기본적으로 key : value를 저장한다.

 

파드에서 두가지 형식으로 사용 가능하다.

1) 환경 변수 : key, value를 환경 변수로 사용 가능
2) 파일로 제공 가능 : key가 파일명이 되며 value가 파일의 내용이 된다.
→ 그래서 볼륨의 종류 중에 CMSecret이 있는 것이다.

 

파일을 제공하지만 데이터를 쓰거나 하기는 어려워서 읽기 전용으로 제공하는 것이 일반적이다.
파일 제공의 목적은 설정 파일 (즉, 읽기만 하는 용도), 인증서, 암호화 키를 제공하는 것이다.

 

✔️ 환경변수로 제공하는 방법

mymessage.yaml

apiVersion: v1
kind: ConfigMap
metadata:
  name: mymessage
data:
  MESSAGE: Customized Hello ConfigMap
 vagrant@k8s-node1  ~/configure/cm  kubectl create -f mymessage.yaml 
configmap/mymessage created

 vagrant@k8s-node1  ~/configure/cm  kubectl get cm                  
NAME               DATA   AGE
kube-root-ca.crt   1      7d22h
mymessage          1      3s
 vagrant@k8s-node1  ~/configure/cm  kubectl describe cm mymessage 
Name:         mymessage
Namespace:    default
Labels:       <none>
Annotations:  <none>

Data
====
MESSAGE:
----
Customized Hello ConfigMap

BinaryData
====

Events:  <none>

key 역할을 하는 MESSAGE가 있고 구분자가 있고 바로 아래에 value가 있다.

key : value는 세트이기 때문에 현재 Data가 한개 존재하는 것이다.

 

 vagrant@k8s-node1  ~/configure/cm  kubectl explain pods.spec.containers.envFrom
FIELDS:
   configMapRef <Object>
     The ConfigMap to select from

   prefix       <string>
     An optional identifier to prepend to each key in the ConfigMap. Must be a
     C_IDENTIFIER.

   secretRef    <Object>
     The Secret to select from

configMapRef : configMap을 참조한다.
secretRef : secret을 참조한다.

 

 vagrant@k8s-node1  ~/configure/cm  kubectl explain pods.spec.containers.envFrom.configMapRef
FIELDS:
   name <string>
     Name of the referent. More info:
     https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names

   optional     <boolean>
     Specify whether the ConfigMap must be defined

name : configMap의 이름

 

 

myweb.yaml

apiVersion: v1
kind: Pod
metadata:
  name: myweb-env
spec:
  containers:
    - name: myweb
      image: ghcr.io/c1t1d0s7/go-myweb:alpine
      envFrom:
        - configMapRef:
            name: mymessage
 vagrant@k8s-node1  ~/configure/cm  kubectl exec -it myweb-env -- sh                         
/ # env
KUBERNETES_SERVICE_PORT=443
KUBERNETES_PORT=tcp://10.233.0.1:443
HOSTNAME=myweb-env
SHLVL=1
HOME=/root
TERM=xterm
KUBERNETES_PORT_443_TCP_ADDR=10.233.0.1
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
KUBERNETES_PORT_443_TCP_PORT=443
KUBERNETES_PORT_443_TCP_PROTO=tcp
KUBERNETES_SERVICE_PORT_HTTPS=443
KUBERNETES_PORT_443_TCP=tcp://10.233.0.1:443
MESSAGE=Customized Hello ConfigMap
KUBERNETES_SERVICE_HOST=10.233.0.1
PWD=/

MESSAGE=Customized Hello ConfigMap 이라는 환경변수가 있다.
configMap에서 Customized Hello ConfigMap를 가져와서 등록시킨 것을 볼 수 있다.

myweb.yamlenvFrom.configMapRef.namemymessage.yamlname을 가리킨다.
그래서 mymessage.yamlMESSAGE: Customized Hello ConfigMapkey : value 형태로 시스템의 환경변수로 설정된다.

 

 vagrant@k8s-node1  ~/configure/cm  cat ../env/myweb.yaml       
apiVersion: v1
kind: Pod
metadata:
  name: myweb-env
spec:
  containers:
    - name: myweb
      image: ghcr.io/c1t1d0s7/go-myweb:alpine
      env:
        - name: MESSAGE
          value: "Customized Hello World"

환경변수를 직접 작성하는 방식과 비교해서 선택적으로 사용하면 된다.

 

✔️ 파일로 제공하는 방법

 vagrant@k8s-node1  ~/configure/cm  kubectl explain pods.spec.volumes.configMap

   name <string>
     Name of the referent. More info:
     https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names

 

myweb-cmvol.yaml

apiVersion: v1
kind: Pod
metadata:
  name: myweb-cm-vol
spec:
  containers:
    - name: myweb
      image: ghcr.io/c1t1d0s7/go-myweb:alpine
      volumeMounts:
        - name: cmvol
          mountPath: /myvol

  volumes:
    - name: cmvol
      configMap:
        name: mymessage
 vagrant@k8s-node1  ~/configure/cm  kubectl exec -it myweb-cm-vol -- sh
/ # ls
bin    dev    etc    home   lib    media  mnt    myvol  myweb  opt    proc   root   run    sbin   srv    sys    tmp    usr    var
/ # cd myvol
/myvol # ls
MESSAGE
/myvol # cat MESSAGE 
Customized Hello ConfigMap/myvol # 

key가 파일명이 되며 안에있는 데이터가 내용이된다.

 

✔️ env.valueFrom을 사용하는 방법

apiVersion: v1
kind: Pod
metadata:
  name: myweb-env
spec:
  containers:
    - name: myweb
      image: ghcr.io/c1t1d0s7/go-myweb:alpine
      env:
        valueFrom:
          configMapKeyRef:
            name: mymessage
            key: MESSAGE

envFron을 사용하는 방식과 env.valueFrom을 사용하는 방식을 비교해보면
전자는 configMap이 가진 데이터(key : value 형식)의 개수와 상관없이 다 등록하는 것이고

후자는 key를 직접 지정하므로 여러개의 데이터 중에 한개만 등록하고 싶을 때 사용할 수 있다.

즉, 특정 key를 지칭하고 싶을 때는 후자 방식을 사용하면 된다.

 


 

✔️ Secret

시크릿은 암호, 토큰 또는 키와 같은 소량의 중요한 데이터를 포함하는 오브젝트이다.
이를 사용하지 않으면 중요한 정보가 파드 명세나 컨테이너 이미지에 포함될 수 있다.
시크릿을 사용한다는 것은 사용자의 기밀 데이터를 애플리케이션 코드에 넣을 필요가 없음을 뜻한다.
시크릿은 시크릿을 사용하는 파드와 독립적으로 생성될 수 있기 때문에, 파드를 생성하고, 확인하고, 수정하는 워크플로우 동안 시크릿(그리고 데이터)이 노출되는 것에 대한 위험을 경감시킬 수 있다.
쿠버네티스 및 클러스터에서 실행되는 애플리케이션은 기밀 데이터를 비휘발성 저장소에 쓰는 것을 피하는 것과 같이, 시크릿에 대해 추가 예방 조치를 취할 수도 있다.
시크릿은 컨피그맵과 유사하지만 특별히 기밀 데이터를 보관하기 위한 것이다.

 

기본적인 원리는 configMap과 같이 key : value를 제공하는 것이다.
value + base64 → encoded data
base64는 암호화가 아니다. key 없이도 얼마든지 decoding시킬 수 있다.
secret은 민감한 데이터를 저장하기 위해 만들어놨지만 인코딩만 할 뿐 암호화를 하지 않아 안전하지 않다.

 

AWS의 KMS와 연동해서 암호화할 수 있다.
또는 vault 라는 서비스와 base64를 연동해서 암호화할 수 있다.

보통 on-prem에서 사용할 때는 vault를 연동시켜 사용하고 클라우드 서비스를 사용할 때는 Key Managed Service를 연동해서 암호화한다.

 

 vagrant@k8s-node1  ~/configure/secret  kubectl api-resources | grep secret    
secrets                                        v1                                     true         Secret
 vagrant@k8s-node1  ~/configure/secret  kubectl explain secret.type         

 

📌 Secret type

key : value 를 저장할 때 value가 어떤 형태인지 타입을 지정해야 한다.

 

✔️ 환경변수로 제공하는 방법

mydata.yaml

apiVersion: v1
kind: Secret
metadata:
  name: mydata
type: Opaque
data:
  id: admin
  pwd: P@ssw0rd

다음 YAML 파일은 정상적으로 동작하지 않을 것이다.

 

 vagrant@k8s-node1  ~/configure/secret  kubectl create -f mydata.yaml     
Error from server (BadRequest): error when creating "mydata.yaml": Secret in version "v1" cannot be handled as a Secret: v1.Secret.Data: decode base64: illegal base64 data at input byte 4, error found in #10 byte of ...|d":"admin","pwd":"P@|..., bigger context ...|{"apiVersion":"v1","data":{"id":"admin","pwd":"P@ssw0rd"},"kind":"Secret","metadata":{"na|...

역시 동작하지 않는다. base64로 값을 인코딩해야 한다.

 

 vagrant@k8s-node1  ~/configure/secret  echo "admin" | base64                                                                                                                       
YWRtaW4K
 vagrant@k8s-node1  ~/configure/secret  echo "P@ssw0rd" | base64
UEBzc3cwcmQK

인코딩

 

apiVersion: v1
kind: Secret
metadata:
  name: mydata
type: Opaque
data:
  id: YWRtaW4K
  pwd: UEBzc3cwcmQK

처음 YAML 파일의 data의 id와 pwd를 인코딩한 값으로 변경한다.

 

 vagrant@k8s-node1  ~/configure/secret  kubectl get secret 
NAME                                 TYPE                                  DATA   AGE
default-token-h87sc                  kubernetes.io/service-account-token   3      7d23h
mydata                               Opaque                                2      13s
nfs-client-provisioner-token-xzr7h   kubernetes.io/service-account-token   3      91m

 vagrant@k8s-node1  ~/configure/secret  kubectl describe secret mydata      
Name:         mydata
Namespace:    default
Labels:       <none>
Annotations:  <none>

Type:  Opaque

Data
====
id:   6 bytes
pwd:  9 bytes

secretData의 내용을 보여주지 않는다.

 

 vagrant@k8s-node1  ~/configure/secret  kubectl get secret mydata -o yaml
apiVersion: v1
data:
  id: YWRtaW4K
  pwd: UEBzc3cwcmQK
kind: Secret
metadata:
  creationTimestamp: "2022-05-24T06:41:38Z"
  name: mydata
  namespace: default
  resourceVersion: "883840"
  uid: 8cb72ad1-0cdb-45d9-b913-00596e058fba
type: Opaque

인코딩된 형태로 볼 수 있긴 하지만 암호화가 되지않아 안전하지 않다.

 

 vagrant@k8s-node1  ~/configure/secret  kubectl explain pod.spec.containers.envFrom.secretRef
FIELDS:
   name <string>
     Name of the referent. More info:
     https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names

   optional     <boolean>
     Specify whether the Secret must be defined

 

myweb.yaml

apiVersion: v1
kind: Pod
metadata:
  name: myweb-secret
spec:
  containers:
    - name: myweb
      image: ghcr.io/c1t1d0s7/go-myweb:alpine
      envFrom:
        - secretRef:
            name: mydata

key까지 설정하는 방법

apiVersion: v1
kind: Pod
metadata:
  name: myweb-env
spec:
  containers:
    - name: myweb
      image: ghcr.io/c1t1d0s7/go-myweb:alpine
      env:
        valueFrom:
          secretKeyRef:
            name: mydata 
            key: id

 

✔️ 파일로 제공하는 방법

apiVersion: v1
kind: Pod
metadata:
  name: myweb-sec-vol
spec:
  containers:
    - name: myweb
      image: ghcr.io/c1t1d0s7/go-myweb:alpine
      volumeMounts:
        - name: secvol
          mountPath: /secvol

  volumes:
    - name: secvol
      secret:
        secretName: mydata
728x90