본문 바로가기
Infra/Kubernetes

[Kubernetes] ConfigMap, Secret - Env, Mount

by 진꿈청 2024. 9. 2.

Kubernetes

 

 

이번 포스팅에서는 `ConfigMap`과 `Secret`에 관한 설명이다.

 

설명하기에 앞서 해당 오브젝트를 사용해야 하는 상황은 어떤 경우일까?

 

개발 환경과 상용 환경이 있다고 하자.

또한, A라는 서비스가 있다고 가정하자. 이때, A 서비스에는 일반 접근과 보안 접근이 가능하다.

 

A 서비스(개발 환경)

  • SSH: False
  • User: Dev
  • Key: LS0tLs...

 

따라서, 개발 환경에서는 해당 보안 접근을 해제할 수 있는 옵션이 있다.(`SSH : False`)

만약, 보안 접근을 한다면 접근 유저와 키를 세팅할 수도 있다.(`User: Dev`, `Key: LS0tLs`)

 

 

개발 환경에서는 위처럼 사용한다고 하고, 상용 환경에서 배포를 해야 한다면 해당 값은 변경되어야 할 것이다.

 

A 서비스(상용 환경)

  • SSH: True
  • User: Prod
  • Key: MII3Ld...

 

다시 보안 접속으로 설정을 해야 될 것이며, 유저와 키 값도 변해야 한다.

 

그러나, 이 값은 컨테이너 안에 있는 서비스 이미지에 들어있는 값이기 때문에,

해당 내용을 바꾼다는 건 개발 환경과 상용 환경에 관한 컨테이너 이미지를 각각 관리하겠다는 것을 말한다.

 

 

Container for Dev 

  • A 서비스(개발 환경)
    • SSH: False
    • User: Dev
    • Key: LS0tLs..

 

Container for Prod

  • A 서비스(상용 환경)
    • SSH: True
    • User: Prod
    • Key: MII3Ld...

 

이런식으로 각각 관리해주어야 한다.

 

값 몇 개 때문에 큰 용량의 이미지를 별도로 관리하는 것은 정말 부담되는 일이다.

 

 

따라서, 환경에 따라 변하는 값들을 외부에서 결정할 수 있게 도와주는 것이 `ConfigMap`과 `Secret`이라는 오브젝트이다.

 

분리해야 되는 일반적인 상수들을 모아 `ConfigMap`을 만들고,

키와 같이 보안적인 관리가 필요한 값을 모아 `Secret`를 만든다.

 

그리고 `Pod`가 생성될 때 이 두 오브젝트를 연결하여 컨테이너의 환경변수에 해당 데이터들을 넣는다.

그러면 A 서비스 입장에서는 해당 환경변수 값을 읽어서 로직을 처리함으로 위와 똑같은 작업을 할 수 있게 된다.

 

즉, 이미지를 하나 만들어 넣으면 개발 환경/상용 환경에 맞춰 사용이 가능하다.

(상용 환경에서는 `ConfigMap`과 `Secret`에 데이터만 바꿔주면)

(똑같은 컨테이너 이미지를 사용해 원하는 기능을 사용이 가능하다.)

 

 

ConfigMap 및 Secret 사용 방법

 

`ConfigMap`과 `Secret`을 만들 때 데이터로 상수를 넣을 수 있고 파일을 넣을 수 있다.

그리고 파일을 넣을 때는 환경 변수로 세팅하는 게 아닌 볼륨을 마운트해서 사용을 할 수 있다.

 

ConfigMap

 

`ConfigMap`은 키와 밸류로 고정이 되어있다.

그래서, 위의 그림처럼 필요한 상수들을 정의해 놓으면 `Pod`를 생성할 때,

해당 `ConfigMap`을 가져와 컨테이너 안의 환경변수로 세팅을 할 수 있다.

 

 

Secret

 

그리고 `Secret`도 똑같은 역할을 하는데 이름처럼 뭔가 보안적인 요소의 들을 저장하는 용도이다.

주로 패스워드라든지 인증키들은 `Secret`에 담는다.

 

`Secret`은 평문으로 쿠버네티스 DB(etcd)에 저장된다.

`Secret`의 보안적 요소는 `Secret`를 `Pod`에 파일로 마운팅해서 사용할 때, `Pod` 내부에서는 파일이 보이지만,

 

이런 기능을 구현하기 위해서 `Kubernetes` 입장에서는 `workernode`에 `Secret` 파일을 만들어 놓고,

`Pod`에 이 파일을 마운팅한다. 이때, `workernode`에 `Secret` 파일을 인메모리 파일시스템(tmpfs) 영역에

올려놓고 있다가 `Pod`가 삭제되면 지운다.

 

이렇게 민감한 데이터를 디스크에 저장하지 않기에 `Secret`의 경우 `ConfigMap`보다 보안에 유리할 수 있다.

 

주의

`Secret`은 사용할 때 `ConfigMap`과 달리 `Value`를 넣을 때 `base64 encoding`을 해서 넣어야 한다.

이게 `Secret`의 보안적인 요소는 아니고 단순히 `Secret`의 `Value`를 만들어야 된다는 규칙이다.

`Secret`이 `Pod`로 주입이 될 때는 자동으로 `decoding`이 되어 환경 변수에서는 원래의 값으로 보인다.

 

 

상수 사용

ConfigMap

apiVersion: v1
kind: ConfigMap
metadata:
  name: cm-dev
data:
  SSH: 'false'
  User: dev

 

YAML 파일 내용을 보면 `ConfigMap`을 만드는데 이름을 지정하고 이 데이터에 키-벨류 형태의 상수를 넣는다.

 

Secret

apiVersion: v1
kind: Secret
metadata:
  name: sec-dev
data:
  Key: MTIzNA==

 

 마찬가지로, `Secret`도 이름을 넣고 데이터에 키를 넣고 `Value`에는 값을 `Base64`로 변환해서 넣으면 된다.

 

 

Pod

apiVersion: v1
kind: Pod
metadata:
  name: pod-1
spec:
  containers:
  - name: container
    image: kubetm/init
    envFrom:
    - configMapRef:
        name: cm-dev
    - secretRef:
        name: sec-dev

 

이제는 `Pod`를 생성할 때 우리가 앞서 만든 `ConfigMap`과 `Secret`를 지정하여 사용하면 된다.

 

컨테이너 안에 `env-from`이라는 속성으로 `ConfigMap`을 래퍼런스하고 가져올 `ConfigMap`의 이름은 `cm-dev`이다.

마찬가지로, `Secret`로 래퍼런스하는데 그 이름은 `sec-dev`이다.

 

 

파일 사용

 

다음으로는 파일환경변수에 넣는 방법이다.

 

위의 그림처럼 파일을 통으로 `ConfigMap`에 담을 수가 있는데 이럴 때 파일 이름가 된다.

파일 안의 내용은 `Value`가 되어 `ConfigMap`이 만들어진다.

 

파일을 `ConfigMap`으로 만드는 것은 대시보드에서 지원해주지 않기 때문에

직접 마스터 콘솔로 들어가 `kubectl` 명령어를 사용해주어야 한다.

 

kubectl create configmap cm-file --from-file=./file.txt

 

`cm-file`이라는 `ConfigMap`을 만들며 `./file.txt` 파일을 넣을 거다라는 명령이다.

 

 

`Secret`의 경우 아래와 같이 명령을 날리면 된다.

kubectl create secret generic sec-file --from-file=./file.txt

 

여기서 한가지 주의할 점은 파일 텍스트 안의 내용이 `Base64`로 변경이 되기 때문에

만약, 이 파일 안에 내용이 이미 `Base64`였다면 두번 인코딩이 된다.

 

 

Pod

apiVersion: v1
kind: Pod
metadata:
  name: pod-file
spec:
  containers:
  - name: container
    image: kubetm/init
    env:
    - name: file-c
      valueFrom:
        configMapKeyRef:
          name: cm-file
          key: file-c.txt
    - name: file-s
      valueFrom:
        secretKeyRef:
          name: sec-file
          key: file-s.txt

 

그래서 `Pod`를 만드는 것을 살펴보면 컨테이너에 환경변수를 넣을 건데,

환경변수의 이름은 `file-c`이고 그리고 이 파일의 값 `valueFrom`을 가져올 건데,

그 값은 `ConfigMap`의 키를 래퍼런스할 것이며(`configMapKeyRef`) 이름은 `cm-file`이다.

 

 

Volume Mount

 

마지막으로, 파일을 마운팅하는 방법이다.

 

파일을 `ConfigMap`에 담는 것까지는 똑같다.

 

Pod

apiVersion: v1
kind: Pod
metadata:
  name: pod-mount
spec:
  containers:
  - name: container
    image: kubetm/init
    volumeMounts:
    - name: file-volume
      mountPath: /mount
  volumes:
  - name: file-volume
    configMap:
      name: cm-file

 

그러나, 다른 부분은 `Pod`를 만들 때 컨테이너 안에 `Volume`을 마운트 할건데

그 `/mountPath`는 `/mount`가 되고 마운트할 `Volume`의 내용을 보면

해당 `Volume` 안에는 `ConfigMap`을 담는다.

 

해당 `ConfigMap`의 이름은 `cm-file`이다.

 

 

이렇게만 보면 `VolumeMount`와 `파일 사용`의 차이를 정확히 알기 어려울 수 있다.

 

만약 `Pod`를 생성한 다음에 각각의 `ConfigMap`에 적용된 파일의 내용을 변경하게 된다면 어떻게 될까?

 

환경변수 방식은 한번 주입을 하면 끝이기 때문에 `ConfigMap`의 데이터가 변해도

`Pod`의 환경변수 값에는 영향이 없다.

 

해당 `Pod`가 죽어 재생성이 되어야지만, 이 변경된 값을 다시 받아와서 수정을 할 수 있다.

 

반면에 `VolumeMount` 방식은 마운트라는게 원본과 연결시켜준다는 개념인것처럼

`ConfigMap`의 데이터 내용이 변하게 되면 해당 `Pod`에 마운팅된 내용도 변하게 된다.

 

따라서, 위의 특성을 잘 알고 필요한 상황에 따라 활용을 잘 해야 한다.

 

 

실습

`ConfigMap`과 `Secret`에 관한 실습을 진행하겠다.

 

 

ConfigMap

 

`ConfigMap`은 특별한 거 없이 아래 YAML 처럼 키-벨류 형태로 값을 넣어주면 된다.

 

apiVersion: v1
kind: ConfigMap
metadata:
  name: cm-dev
data:
  SSH: 'false'
  User: dev

 

여기서 한가지 주의할 점이 있는데 키-벨류는 모두 스트링 값이다.

그렇기 때문에 만약 `boolean` 값을 넣고 싶다면 위의 YAML 파일 내용처럼 `''`을 달아서 넣어줘야 한다.

만약, 뺀다면 에러가 발생한다.

 

 

 Secret

 

`Secret`도 마찬가지로 주의해야 할 점은 `Key`의 `Value`값에 `Base64 Enconding`을 하지 않게 되면 오류가 발생한다.

apiVersion: v1
kind: Secret
metadata:
  name: sec-dev
data:
  Key: MTIzNA==

 

 

대시보드에서는 `Key`의 값을 볼 수 있는 기능이 있어 실습시 편리하지만,

실질적으로 대시보드를 실제 운영에서 사용하지 않는 이유가 이런 중요한 값이 쉽게 노출되기 때문이다.

따라서, 보안상의 이유로 대시보드는 잘 사용하지 않는다고 한다.

 

 

이제는 해당 `ConfigMap`과 `Secret`을 연동한 `Pod`를 생성해보자.

 

 

Pod

apiVersion: v1
kind: Pod
metadata:
  name: pod-1
spec:
  containers:
  - name: container
    image: kubetm/init
    envFrom:
    - configMapRef:
        name: cm-dev
    - secretRef:
        name: sec-dev

 

YAML 파일을 살펴보면 `Pod`의 컨테이너 안에 환경변수로 방금 만든 `ConfigMap`과 `Secret`의 이름을 지정해주었다.

 

그래서, 실제로 접속해서 환경변수를 확인해보면 아래와 같이 잘 적용이 된 것을 확인할 수 있다.

Key값을 보면 `base64 decoding`이 된것을 확인할 수 있다.

 

 

 

다음으로는 `file`로 `ConfigMap`과 `Secret`을 만들어보자.

 

 

ConfigMap(file)

 

앞서, 이론에서 언급했던 것처럼 파일로 `ConfigMap`을 만드는 것은 대시보드에서 지원해주지 않는다.

따라서, 직접 마스터의 콘솔로 만들어야 한다.

 

 

이런식으로, `ConfigMap`에 사용될 파일을 만든 뒤 `kubectl` 명령어를 활용하여 `ConfigMap`을 생성한다.

 

 

 

실제로, `Key`는 파일 이름으로 되어있는 형태로 `ConfigMap`이 잘 생성되었다.

 

 

Secret(file)

`Secret`도 마찬가지로, 콘솔에서 `kubectl` 명령어를 통해 만들어준다. 

 

 

이때, `base64 encoding`을 하지 않아도 명령어에 의해 알아서 `encoding`을 해준다.

 

 

`Secret`도 `Key`값을 파일이름으로 갖으며 잘 생성이 되었다.

 

 

자, 이제는 관련해서 `Pod를 생성해보자.

 

 

Pod(file)

apiVersion: v1
kind: Pod
metadata:
  name: pod-file
spec:
  containers:
  - name: container
    image: kubetm/init
    env:
    - name: file-c
      valueFrom:
        configMapKeyRef:
          name: cm-file
          key: file-c.txt
    - name: file-s
      valueFrom:
        secretKeyRef:
          name: sec-file
          key: file-s.txt

 

기존의 `envFrom` 키워드와는 달리 `env` 키워드를 사용해서 환경변수를 명시를 한다.

이때, 이 환경변수에 방금 만든 `ConfigMap`의 키를 매칭시킨다.

 

`Secret`도 마찬가지로, 키를 매칭시켜준다.

 

 

생성한 `Pod`를 살펴보면 `ConfigMap`과 `Secret`의 내용이 잘 들어가있는 것을  확인할 수 있다.

 

 

마지막으로는 `VolumeMount(File)`에 관해 알아보겠다.

 

 

VolumeMount(File)

 

Pod

apiVersion: v1
kind: Pod
metadata:
  name: pod-mount
spec:
  containers:
  - name: container
    image: kubetm/init
    volumeMounts:
    - name: file-volume
      mountPath: /mount
  volumes:
  - name: file-volume
    configMap:
      name: cm-file

 

 

마운트 경로에 `file-c.txt`가 잘 있는 것을 확인할 수 있다.

 

이론에서 설명했던 것처럼 `VolumeMount`의 경우 `file`의 데이터가 변경되었을 때 적용이 된다고 말했었다.

한번 확인해보자.

 

`file-c.txt`의 값을 변경한 뒤 확인해보면

 

 

잘 변경된 것을 확인할 수 있다.