[kuber-study] Chapter7. ConfigMaps and Secrets (2)

jeonghyun yu·2022년 1월 28일
1

kuber-study

목록 보기
4/7
post-thumbnail

ConfigMap

환경에 따라 다르거나 자주 변경되는 설정 옵션을 소스 코드와 분리하여 유지하기 위함.

  • 설정 옵션을 별도의 개체로 분리하는 방법.
  • key/value 쌍
  • 애플리케이션과 무관하게 유지

  • 동일한 pod 설정이어도 여러 configMap설정을 가질 수 있다.


생성

  • 명령어로 생성
    $ kubectl create configmap

  • configMap 삭제
    kubectl delete configmap <configmap-name>

# 단일 입력
$ kubectl create configmap fortune-config --from-literal=sleep-interval=25

# 여러개의 리터럴
$ kubectl create configmap myconfigmap --from-literal=foo=bar --from-literal=bar=baz --from-literal=one=two
  • configMap 설정 확인
$ kubectl get configmap fortune-config -o yaml
apiVersion: v1
data:
  sleep-interval: "25" 
kind: ConfigMap 
metadata:
  creationTimestamp: 2016-08-11T20:31:08Z
  name: fortune-config 
  namespace: default
...
  • 파일 실행 : $ kubectl create -f <yaml-file>
  • 컨피그맵 조회 : $ kubectl get cm

  • 파일 내용으로 CONFIGMAP 생성
$ kubectl create configmap my-config --from-file=config-file.conf

# key 지정
$ kubectl create configmap my-config --from-file=customkey=config-file.conf

# 디렉터리로 생성
$ kubectl create configmap my-config --from-file=/path/to/dir
  • 옵션 결합
$ kubectl create configmap my-config 
➥ --from-file=foo.json 
➥ --from-file=bar=foobar.conf 
➥ --from-file=config-opts/ 
➥ --from-literal=some=thing


ConfigMap 항목을 환경 변수로 컨테이너에 전달

  • ConfigMap 항목을 컨테이너로 전달하는 방법
  1. 환경 변수로 설정
  2. command-line 인자로 전달
  3. configMap 볼륨 사용
  • 환경 변수로 설정: valueFrom 사용
apiVersion: v1
kind: Pod
metadata:
  name: fortune-env-from-configmap
spec:
  containers:
  - image: luksa/fortune:env
    env: 
    - name: INTERVAL 
      valueFrom:               # 고정 값 대신 configMap key 사용
        configMapKeyRef: 
          name: fortune-config 
          key: sleep-interval 
...

configMap이 존재하지 않으면?

존재하지 않는 configMap을 참조하는 컨테이너는 시작되지 않지만 스케줄은 되어있는 상태. 누락된 configMap을 생성하면 장애가 발생한 컨테이너가 시작된다.

configMapKeyRef.optional: true 설정 시 configMap이 없어도 컨테이너가 시작된다.

ConfigMap의 모든 항목을 환경 변수로 한 번에 전달

쿠버네티스 1.6 이상은 ConfigMap의 모든 항목을 한번에 환경 변수로 노출시킬 수 있는 방법 제공.

  • envFrom 사용
spec:
  containers:
  - image: some-image
    envFrom: 
    - prefix: CONFIG_         # 접두사 (생략 시 key가 이름)
      configMapRef:           # configMap 참조
        name: my-config-map 
...

❗ 환경 변수 이름에는 대시(-)가 포함되면 안된다. 만일 configMap의 key에 대시가 포함되어 있다면 해당 항목을 건너뛴다.


ConfigMap 항목을 command-line 인자로 전달

containers의 args 필드에서 컨피그맵 항목을 직접 참조할 수는 없지만 환경 변수는 참조 가능하다.

apiVersion: v1
kind: Pod
metadata:
  name: fortune-args-from-configmap
spec:
  containers:
  - image: luksa/fortune:args  # 환경 변수x, 인자로 interval 참조
    env:                       # 환경 변수 정의
    - name: INTERVAL 
      valueFrom: 
        configMapKeyRef: 
          name: fortune-config 
          key: sleep-interval 
    args: ["$(INTERVAL)"]      # 환경 변수 사용
...

명령줄 인자로 전달하는 것은 보통 짧은 변수 값에 사용한다.


ConfigMap 볼륨 사용

configMap에는 파일이 포함될 수도 있는데 그런 경우에 configMap 볼륨을 사용한다. 파일의 내용을 읽어서 항목의 값을 얻는다.

CONFIGMAP 생성

  • my-nginx-config.conf (nginx 웹 서버 구성, 압축 활성화)
server {
  listen			 80;
  server_name 		 www.kubia-example.com;
  
  # 일반 텍스트와 XML파일 gzip
  gzip on; 
  gzip_types text/plain application/xml; 
  
  location / {
    root /usr/share/nginx/html;
    index index.html index.htm;
  }
}
  • 디렉토리 안에 conf 파일과 변수로 쓸 일반 텍스트 파일 추가

  • configMap 생성 $ kubectl create configmap fortune-config --from-file=configmap-files

  • 두 개의 항목이 포함된 configMap, key는 파일명

$ kubectl get configmap fortune-config -o yaml
apiVersion: v1
data:
  my-nginx-config.conf: |  # 뒤에 여러줄의 값이 온다
    server { 
      listen              80; 
      server_name         www.kubia-example.com; 
      
      gzip on; 
      gzip_types text/plain application/xml; 
      
      location / { 
        root /usr/share/nginx/html; 
        index index.html index.htm; 
      } 
    } 
  sleep-interval: | 
    25 
kind: ConfigMap
...

CONFIGMAP 항목 사용하는 볼륨

nginx의 설정 파일은 /etc/nginx/conf.d/ 디렉터리의 모든 .conf 파일을 포함한다. 그곳에 configMap 파일 추가할 것.

  • pod 정의 yaml 파일
apiVersion: v1
kind: Pod
metadata:
  name: fortune-configmap-volume
spec:
  containers:
  - image: nginx:alpine
    name: web-server
    volumeMounts:
    - name: config
      mountPath: /etc/nginx/conf.d  # 볼륨을 마운트 할 위치
      readOnly: true
    ...
  volumes:
  ...
  - name: config 
    configMap:                # 볼륨이 configMap 참조
      name: fortune-config 
  ...
  • 확인
# 포트 포워딩
$ kubectl port-forward fortune-configmap-volume 8080:80 &

# 요청
$ curl -H "Accept-Encoding: gzip" -I localhost:8080

# 마운트된 ConfigMap 볼륨의 내용
$ kubectl exec fortune-configmap-volume -c web-server ls /etc/nginx/conf.d
my-nginx-config.conf
sleep-interval

❗ 두 항목이 같은 디렉터리에 추가되었다. 여러 개의 configMap 항목이 같은 컨테이너에 있다는 것은 컨테이너가 밀접하게 연관되어 있어야 한다는 문제점이 발생할 수 있다.

특정 CONFIGMAP 항목만 노출

  • items 속성
volumes:
- name: config 
  configMap: 
    name: fortune-config 
    items: 
    - key: my-nginx-config.conf  # 원하는 항목의 key
      path: gzip.conf

디렉토리를 마운트하면 해당 디렉토리의 기존 파일이 숨겨진다

볼륨을 디렉터리로 마운트 하게되면 컨테이너 이미지의 해당 디렉터리에(여기서는 /etc/nginx/conf.d) 저장된 파일이 숨겨졌다는 걸 의미한다. 즉, 마운트 동안 기존 파일들에 액세스가 불가능하다.
일반적으로 linux에서 파일 시스템을 비지 않은 디렉터리에 마운트할 때 발생하는 현상이다.

다른 파일을 숨기지 않고 각 컨피그맵 항목을 파일로 마운트

  • subPath 속성 사용
spec:
  containers:
  - image: some/image
    volumeMounts:
    - name: myvolume
      mountPath: /etc/someconfig.conf  # 파일에 마운트
      subPath: myconfig.conf           # 볼륨의 파일

❗개별 파일 마운트 방식은 업데이트 관련 문제점을 가진다..

CONFIGMAP 볼륨의 파일에 대한 파일 권한 설정

  • 기본적으로 configMap 볼륨의 모든 파일은 644(-rw-r--r--)권한을 가진다.
  • 변경 방법 : defaultMode 속성
volumes:
- name: config
  configMap:
    name: fortune-config
    defaultMode: "6600"    # -rw-rw------

앱을 재시작하지 않고 설정 업데이트

환경 변수나 command-line 인자와 다르게 configMap 볼륨은 pod나 컨테이너를 재시작할 필요 없이 설정 업데이트가 가능하다. configMap을 업데이트하면 참조하는 모든 볼륨의 파일이 업데이트 된다.

# CONFIGMAP 수정
$ kubectl edit configmap fortune-config
# gzip on -> gzip off로 수정

# 볼륨의 파일 내용 출력
$ kubectl exec fortune-configmap-volume -c web-server
➥ cat /etc/nginx/conf.d/my-nginx-config.conf

볼륨의 파일 내용은 수정되었지만 nginx 서버의 파일은 자동 reload되지 않았을 것.

# NGINX에서 config을 다시 로드하도록 신호 전송
$ kubectl exec fortune-configmap-volume -c web-server -- nginx -s reload

# 확인 시 Content-Encoding: gzip 헤더가 없을 것
$ curl -H "Accept-Encoding: gzip" -I localhost:8080

앱의 파일은 원자적으로 업데이트된다

쿠버네티스가 심볼릭 링크를 사용해서 가능하게 한다. ConfigMap이 업데이트되면, 디렉토리를 생성하고 모든 파일을 기록한 다음 ..data를 새 디렉토리에 링크하여 관리한다.

  • 마운트된 configMap 볼륨에 있는 파일
$ kubectl exec -it fortune-configmap-volume -c web-server -- ls -lA /etc/nginx/conf.d
total 4
drwxr-xr-x ... 12:15 ..4984_09_04_12_15_06.865837643
lrwxrwxrwx ... 12:15 ..data -> ..4984_09_04_12_15_06.865837643
lrwxrwxrwx ... 12:15 my-nginx-config.conf -> ..data/my-nginx-config.conf
lrwxrwxrwx ... 12:15 sleep-interval -> ..data/sleep-interval

마운트된 단일 파일은 업데이트되지 않는다
-> 전체 볼륨을 다른 디렉토리에 마운트한 다음, 해당 파일을 가리키는 심볼릭 링크를 만들어서 해결

CONFIGMAP 업데이트는 좋은걸까?

컨테이너의 특징 중 하나는 불변성이다. 그런데 configMap을 수정해서 불변성을 해치는 것은 옳은 일인가?
애플리케이션이 재로딩을 지원하지 않는 경우, 다시 시작된 pod나 컨테이너 부분에서만 다른 설정으로 프로세스가 돌아가는 문제가 발생한다. 그러므로 앱이 지원하지 않는다면 configMap을 수정하는 것은 좋지 않다.



Secrets

: 보안 유지가 필요한 자격 증명 및 개인 암호화 키 같은 중요한 정보를 유지할 때 사용하는 쿠버네티스의 객체이다.

  • key-value 쌍을 포함
  • configMap과 같이 환경 변수로 전달하는 것과 볼륨의 파일로 표시하는 것 둘 다 가능
  • secret에 액세스해야 하는 pod를 실행하는 node에만 배포되어 안전
  • 물리적 스토리지가 아닌 노드의 메모리에만 저장
  • 마스터 노드(etcd)에서 암호화되지 않은 상태로 저장되기 때문에 마스터 노드의 보안이 필요. (쿠버네티스 1.7부터는 암호화된 상태로 저장)

default token Secret

모든 pod에는 자동으로 secret 볼륨이 부착된다. $ kubectl describe pod 에서 위치와 함께 확인 가능

# 시크릿 출력
$ kubectl get secrets

# 자세한 정보 출력
$ kubectl describe secrets
Name:        default-token-cfee9
Namespace:   default
Labels:      <none>
Annotations: kubernetes.io/service-account.name=default
             kubernetes.io/service-account.uid=cc04bb39-b53f-42010af00237
Type:        kubernetes.io/service-account-token

Data
====
ca.crt:      1139 bytes 
namespace:   7 bytes 
token:       eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9... 
# 세 개의 항목을 가진 시크릿
  • ca.crt, namespace, token은 pod 내에서 쿠버네티스 API 서버와 안전하게 통신하기 위한 항목들
  • automountServiceAccountToken: false 설정이나 서비스 계정에서 false 설정하면 컨테이너에 자동으로 마운트되지 않는다.

# 파일 확인
$ kubectl exec mypod ls /var/run/secrets/kubernetes.io/serviceaccount/
ca.crt
namespace
token

생성

https 트래픽을 서비스 하는 nginx 컨테이너로 업그레이드
개인 키와 인증서가 필요하다.

# 인증서, 개인 키 파일 생성
$ openssl genrsa -out https.key 2048
$ openssl req -new -x509 -key https.key -out https.cert -days 3650 -subj /CN=www.kubia-example.com

# 더미 파일 생성
$ echo bar > foo

# 시크릿 생성
$ kubectl create secret generic fortune-https --from-file=https.key --from-file=https.cert --from-file=foo

두 개의 항목을 가진 generic 시크릿 생성


ConfigMaps와 Secret 비교

  • 일반 config 데이터는 configMap 사용
  • 중요하게 보관해야하는 데이터는 secret 사용
  • 중요한 데이터와 중요하지 않은 데이터가 모두 포함된 경우 secret 사용
$ kubectl get secret fortune-https -o yaml
apiVersion: v1
data:
 foo: YmFyCg==
 https.cert: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURCekNDQ...
 https.key: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFcE...
kind: Secret
...

$ kubectl get configmap fortune-config -o yaml
apiVersion: v1
data:
 my-nginx-config.conf: |
 server {
 ...
 }
 sleep-interval: |
 25
kind: ConfigMap
...
  • secret : base64 인코딩 문자열 (이진 데이터 가능)
  • configMap : 일반 텍스트

STRINGDATA 필드

이진 형식이 아닌 데이터를 설정할 때 사용

kind: Secret
apiVersion: v1
stringData:          # 이진 형식이 아닌 데이터 설정
  foo: plain text 
data:
  https.cert: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURCekNDQ...
  https.key: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFcE...
  • 쓰기 전용 필드
  • $ kubectl get -o yaml 검색 시 표시 안됨

secret 사용

  • CONFIGMAP 수정
# 서버가 인증서와 키 파일을 읽게 설정
$ kubectl edit configmap fortune-config
...
data:
  my-nginx-config.conf: |
    server {
      listen              80;
      listen              443 ssl;
      server_name         www.kubia-example.com;
      ssl_certificate     certs/https.cert;      # 인증서
      ssl_certificate_key certs/https.key;       # 키
      ssl_protocols       TLSv1 TLSv1.1 TLSv1.2;
      ssl_ciphers         HIGH:!aNULL:!MD5;
      
      location / {
        root /usr/share/nginx/html;
        index index.html index.htm;
      }
    }
 sleep-interval: |
...
  • SECRET을 POD에 마운트
apiVersion: v1
kind: Pod
metadata:
 name: fortune-https
spec:
 containers:
 - image: luksa/fortune:env
  name: html-generator
  env:
  - name: INTERVAL
   valueFrom: 
    configMapKeyRef:
     name: fortune-config
     key: sleep-interval
  volumeMounts:
  - name: html
   mountPath: /var/htdocs
 - image: nginx:alpine
  name: web-server
  volumeMounts:
  - name: html
   mountPath: /usr/share/nginx/html
   readOnly: true
  - name: config
   mountPath: /etc/nginx/conf.d
   readOnly: true
  - name: certs 
   mountPath: /etc/nginx/certs/   # nginx가 읽기로 한 위치에 시크릿 볼륨을 마운트
   readOnly: true 
  ports:
  - containerPort: 80
  - containerPort: 443
 volumes:
 - name: html
  emptyDir: {}
 - name: config
  configMap:
   name: fortune-config
   items:
   - key: my-nginx-config.conf
    path: https.conf
 - name: certs 
  secret:                      # 참조할 시크릿 정의
   secretName: fortune-https 

  • NGINX 서버 테스트
# 443 포트 포워드
$ kubectl port-forward fortune-https 8443:443 &

# 요청 전송으로 확인
$ curl https://localhost:8443 -k

# 인증서 확인
$ curl https://localhost:8443 -k -v
* About to connect() to localhost port 8443 (#0)
*  Trying ::1...
* Connected to localhost (::1) port 8443 (#0)
* Initializing NSS with certpath: sql:/etc/pki/nssdb
* skipping SSL peer certificate verification
* SSL connection using TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
* Server certificate:                   # 생성한 인증서
*  subject: CN=www.kubia-example.com 
*  start date: aug 16 18:43:13 2016 GMT 
*  expire date: aug 14 18:43:13 2026 GMT 
*  common name: www.kubia-example.com 
*  issuer: CN=www.kubia-example.com
  • 메모리에 저장되는 SECRET VOLUMES

시크릿 볼륨은 시크릿 파일에 in-memory 파일 시스템을 사용한다.(tmpfs)

환경 변수를 통해 SECRET 노출

  • secretKeyRef 사용
env:
- name: FOO_SECRET
 valueFrom: 
  secretKeyRef: 
   name: fortune-https   # 시크릿 이름
   key: foo              # 가져올 항목 key

⛔ 응용 프로그램은 보통 오류 로그에 환경 변수를 기록하거나 시작 로그에 기록하기 때문에 의도치 않게 노출될 위험이 있다. 또한, 하위 프로세스가 상위 프로세스의 환경 변수를 상속하기 때문에 노출 위험이 있다.


image를 가져올 때 사용하는 Secrets

private 컨테이너 이미지 저장소의 이미지를 사용할 때, 쿠버네티스에서 자격 증명을 전달해야 한다.

  • 도커 허브의 프라이빗 이미지 저장소 사용
    • 도커 홈페이지에서 체크박스 선택
    • 저장소에 대한 자격 증명을 보관하는 시크릿 생성
    • pod 매니페스트의 imagePullSecrets 필드에 시크릿 참조
  • 도커 레지스트리 인증을 위한 secret 생성
$ kubectl create secret docker-registry mydockerhubsecret \
  --docker-username=myusername --docker-password=mypassword \ 
  --docker-email=my.email@provider.com
  • POD 정의에서 docker-registry secret 사용
apiVersion: v1
kind: Pod
metadata:
  name: private-pod
spec:
  imagePullSecrets:      
  - name: mydockerhubsecret    # 프라이빗 이미지 저장소에 접근 가능하게 참조
  containers:
  - image: username/private:tag
    name: main
  • 모든 pod에서 image pull secret을 지정해줘야 할까?
    ServiceAccount에 secret을 추가하면 모든 pod에 자동으로 추가가 된다.

출처
Kubernetes in Action

Q. 컨피그맵의 내용을 사용하기 위해서 컨피그맵 볼륨을 컨테이너의 디렉터리로 마운트 했는데, 디렉터리의 기존 파일이 액세스가 불가해졌습니다. 이때 기존 파일도 함께 접근하려면 어떤 방식을 사용해서 볼륨을 연결해야 할까요? 작성 시에 무슨 속성을 사용하는지도 작성해주세요.

Q. 컨피그맵과 시크릿이 코드적으로(yaml파일에서) 무슨 차이가 있는지 답하고 그 덕분에 각각의 사용이 어떻게 나뉘었는지 설명해주세요.

0개의 댓글