티스토리 뷰

728x90
반응형

이번 블로깅에서는 효율적으로 애플리케이션을 관리하기 위해 자주 사용되는 네임스페이스(Namespace), 컨피그맵(ConfigMap), 시크릿(Secret)의 사용 방법을 알아봅시다.



네임스페이스(Namespace): 리소스를 논리적으로 구분하는 장벽

쿠버네티스에서는 리소스를 논리적으로 구분하기 위해 네임스페이스(Namespace)라는 오프젝트를 제공합니다. 간단히 생각해 네임스페이스는 포드, 레플리카셋, 디플로이먼트, 서비스 등과 같은 쿠버네티스 리소스들이 묶여 있는 하나의 가상 공간 또는 그룹이라고 이해할 수 있습니다.

여러 개의 네임스페이스를 사용하면 마치 하나의 클러스터에서 여러 개의 가상 클러스터를 동시에 사용하는 것처럼 느껴질 것 입니다.



네임스페이스 기본 개념 이해

네임스페이스는 namespaec, ns라는 이름으로 사용할 수 있으며, 네임스페이스의 목록은 kubectl get ns 명령어로 확인할 수 있습니다.

$ kubectl get ns

네임스페이스를 생성하지 않더라도 기본적으로 3개의 네임스페이스가 존재합니다. 각 네임스페이스는 논리적인 리소스 공간이기 때문에 각 네임스페이스에는 포드, 레플리카셋, 서비스와 같은 리소스가 따로 존재합니다. 예를 들어 default라는 이름의 네임스페이스에 생성된 포드를 확인하려면 다음과 같이 kubectl 명령어에 --namespace 옵션을 사용합니다.

$ kubectl get pods --namepsace default

default는 쿠버네티스를 설치하면 자동으로 사용하도록 설정되는 네임스페이스로, kubectl 명령어로 쿠버네티스 리소스를 사용할 때는 기본적으로 default 네임스페이스를 사용합니다.

kube-system이라는 네임스페이스의 포드를 확인해보겠습니다.

$ kubectl get pods -n kube-system 
# --namespace 대신 -n 가능

kube-system 네임스페이스는 쿠버네티스 클러스터 구성에 필수적인 컴포넌트들과 설정값 등이 존재하는 네임스페이스입니다. 지금까지 사용했던 default 네임스페이스와는 논리적으로 구분돼 있기 때문에 지금까지 이런 포드들을 본 적이 없는 것 입니다.

서비스, 레플리카셋을 비롯한 여러 리소스들도 각 네임스페이스에 별도로 존재합니다. 예시로는 kube-system 네임스페이스에는 쿠버네티스의 포드, 서비스 등을 이름으로 찾을 수 있도록 하는 DNS 서버의 서비스가 미리 생성돼 있습니다.

$ kubectl get svc -n kube-system

쿠버네티스 클러스터를 여러 명이 동시에 사용해야 한다면 사용자마다 네임스페이스를 별도록 생성해 사용하도록 설정할 수도 있습니다. 또는 용도에 따라 네임스페이스를 여러 개 만듦으로써 특정 목적의 디플로이먼트, 서비스들은 특정 네임스페이스에서만 존재하도록 만들 수도 있습니다(모니터링, 로드 밸런싱, 인그레스 등..).

각 네임스페이스의 리소스들은 논리적으로만 구분된 것일 뿐, 물리적으로 격리된 것이 아니라는 점을 알아둬야 합니다. 예시로 서로 다른 네임스페이스에서 생성된 포드가 같은 노드에 존재할수도 있습니다.

네임스페이스 vs 라벨

서비스와 포드를 매칭시키기 위해 사용했던 라벨 또한 리소스를 분류하고 구분하기 위한 방법 중 하나입니다.

$ kubectl get pods -l app=webserver

네임스페이스는 라벨보다 더욱 넓은 용도로 사용할 수 있습니다. 예를 들면 ResourceQuota라는 오브젝트를 이용해 특정 네임스페이스에서 생성되는 포드의 자원 사용량을 제한하거나, 애드미션 컨트롤러라는 기능을 이용해 특정 네임스페이스에 생성되는 포드에는 항상 사이드카 컨테이너를 붙이도록 설정할 수 있습니다.



네임스페이스 사용하기

# production-namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: production
$ kubectl apply -f production-namespace.yaml 
$ kubectl create namespace production

네임스페이스는 야믈파일 또는 명령어로 생성할 수 있습니다.

특정 네임스페이스에 리소스를 생성하는 방법은 야믈파일에서 metadata.namespace 항목을 아래와 같이 설정하면 됩니다.

# hostname-deploy-svc-ns.yaml, 테스트용 야믈
apiVersion: apps/v1
kind: Deployment
metadata:
  name: hostname-deployment-ns
  namespace: production # 이거
spec:
  replicas: 1
  selector:
    matchLabels:
      app: webserver
  template:
    metadata:
      name: my-webserver
      labels:
        app: webserver
    spec:
      containers:
      - name: my-webserver
        image: alicek106/rr-test:echo-hostname
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: hostname-svc-clusterip-ns
  namespace: production
spec:
  ports:
    - name: web-port
      port: 8080
      targetPort: 80
  selector:
    app: webserver
  type: ClusterIP

하나의 야믈파일에 ----를 명시해 여러 개의 리소스를 정의할 수 있습니다.

$ kubectl apply -f hostname-deploy-svc-ns.yaml
$ kubectl get pods,svc -n production

kubectl get 명령어에 --all-namespaces 옵션을 사용하면 모든 네임스페이스의 리소스를 확인할 수 있습니다.

$ kubectl get pods --all-namespaces



네임스페이스의 서비스에 접근하기

다른 네임스페이스에 존재하는 서비스에는 서비스 이름만으로 접근할 수 없습니다. 예를 들어 이전에 사용했던 테스트용 임시 포드는 default 네임스페이스에 생성됐기 때문에 production 네임스페이스의 서비스에 접근하지 못합니다.

ihp001@azure-testRG-ubuntu-minikube:~/kubernetes_study$ kubectl run -i --tty --rm debug --image=alicek106/ubuntu:curl --restart=Never -- bash

If you don't see a command prompt, try pressing enter.

root@debug:/# curl hostanme-svc-clusterip-ns:8080
curl: (6) Could not resolve host: hostanme-svc-clusterip-ns

하지만 <서비스 이름>.<네임스페이스 이름>.svc처럼 서비스 이름 뒤에 네임스페이스 이름을 붙이면 다른 네임스페이스의 서비스에도 접근할 수 있습니다.

root@debug:/# curl hostname-svc-clusterip-ns.production.svc:8080 --silent | grep Hello
        <p>Hello,  hostname-deployment-ns-7dfd748479-d7cd4</p>  </blockquote>

서비스의 DNS 이름에 대한 FQDN(Fully Qualified Domain Name)은 다음과 같이 구성되어 있음

<서비스 이름>.<네임스페이스 이름>.svc.cluster.local

삭제는 kubectl delete나 네임스페이스를 삭제하면 리소스가 다 지워진다.

$ kubectl delete ns production



네임스페이스에 종속되는 쿠버네티스 오브젝트와 독립적인 오브젝트

네임스페이스를 사용하면 쿠버네티스 리소스를 사용 목적에 따라 논리적으로 격리할 수 있지만, 모든 리소스가 네임스페이스에 의해 구분되는 것은 아닙니다.

포드, 서비스, 레플리카셋, 디플로이먼트는 네임스페이스 단위로 구분할 수 있습니다. 이런 경우를 쿠버에서는 '오브젝트가 네임스페이스에 속한다(namespaced)'라고 표현합니다. 네임스페이스에 속하는 오브젝트의 종류는 다음 명령어로 확인할 수 있습니다.

$ kubectl api-resources --namespaced=true

반대로 네임스페이스에 속하지 않는 쿠버 오브젝트도 있습니다. 노드(nodes)라는 쿠버 오브젝트는 네임스페이스에 속하지 않는 대표적인 오브젝트 중 하나입니다. 따라서 kubectl get nodes 명령에 --namespace 옵션은 의미가 없습니다. nodes는 쿠버 클러스터에서 사용되는 저수준의 오브젝트이며, 네임스페이스에 의해 구분되지 않기 때문입니다.

네임스페이스에 속하지 않는 오브젝트들은 보통 네임스페이스에서 관리되지 않고, 클러스터 전반에 걸쳐 사용되는 경우가 많습니다.

$ kubectl api-resources --namespaced=false

참고 : kubeconfig라는 파일을 수정함으로써 기본 네임스페이스 변경 가능한데 kubens라는 오픈소스를 사용해 기본 네임스페이스를 좀더 편리하게 변경 가능. (https://github.com/ahmetb/kubectx)



컨피그맵(Configmap), 시크릿(Secret): 설정 값을 포드에 전달

애플리케이션들은 대부분 설정 값을 가지고 있을 것입니다. 예를 들면 애플리케이셔의 로깅 레벨을 정의하는 LOG_LEVEL=INFO와 같이 단순한 키-값 형태의 설정을 사용할 수도 있고, Nginx 웹 서버가 사용하는 nginx.conf처럼 완전한 하나의 파일을 사용해도 할 수도 있습니다.

확실한 방법은 도커 이미지 내부에 설정값, 파일 등을 저장해 놓는 것인데, 도커 이미지는 일단 빌드가 되고 나면 불변의 상태를 가지기 때문에 이 방법은 상황에 따라 설정 옵션을 유연하게 변경할 수 없다는 단점이 있습니다.

이에 대한 대안으로 포드를 정의하는 야믈 파일에 환경 변수를 직접 적어 놓는 하드 코딩 방식을 사용할 수도 있습니다. 아래는 포드의 LOG_LEVEL이라는 이름의 환경 변수를 INFO라는 값을 설정합니다.

# env-hard-coding-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: env-hard-coding-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: my-nginx
  template:
    metadata:
      name: my-nginx-pod
      labels:
        app: my-nginx
    spec:
      containers:
      - name: nginx
        env:
        - name: LOG_LEVEL
          value: INFO
        image: nginx:1.10
        ports:
        - containerPort: 80

위처럼 환경 변수를 포드 템플릿에 직접 명시하는 방식도 나쁘지는 않지만, 상황에 따라서는 환경 변수의 값만 다른, 동일한 여러 개의 YAML이 존재할 수도 있습니다. 만약 운영 환경과 개발 환경에서 각각 디플로이먼트를 생성해야 한다면 환경 변수가 서로 다르게 설정된 두 가지 버전의 YAML 파일이 따로 존재해야 하기 때문입니다.

쿠버네티스는 야믈파일과 설정값을 분리할 수 있는 컨피그맵(Configmap)과 시크릿(secret)이 라는 오브젝트를 제공합니다. 컨피그맵에는 설정값을, 시크릿에는 노출되어서는 안되는 비밀 값을 저장할 수 있습니다.

image

왼쪽처럼 포드의 야믈파일에 환경 변수를 각각 정의해 사용한다면 두 개의 파일이 존재하겠지만, 오른쪽처럼 컨피그맵을 사용하면 1개의 포드 야믈 파일만을 사용하되 환경에 따라 다른 컨피그맵을 생성해 사용하면 된다. 즉, 환경 변수나 설정 값까지 쿠버 오브젝트에서 관리할 수 있으며, 이러한 설정 값 또한 야믈파일로 포드와 함께 배포할 수도 있는 것입니다. 따라서 컨피그맵이나 시크릿을 사용하면 애플리케이션과 설정 값을 별도로 분리해 관리할 수 있다는 장점이 있습니다.



컨피그맵(Configmap)

컨피그맵 사용 방법 익히기

컨피그맵은 일반적인 설정 값을 담아 저장할 수 있는 쿠버 오브젝트이며, 네임스페이스에 속하기 때문에 네임스페이스별로 컨피그맵이 존재합니다.

야믈파일로 컨피그맵 생성도 되지만, kubectl create configmap 명령어를 사용하면 쉽습니다.

$ kubectl create configmap <컨피그맵 이름> <각종 설정값들>
$ kubectl create configmap log-level-configmap --from-literal LOG_LEVEL=DEBUG
$ kubectl create configmap start-k8s --from-literal k8s=kubernetes --from-literal container=docker
# 여러번 가능

컨피그맵에 저장된 설정 값은 kubectl describe, kubectl get 명령어로 확인할 수 있습니다.

$ kubectl get cm 
$ kubectl get configmap
$ kubectl describe configmap log-level-configmap
$ kubectl get cm log-level-configmap -o yaml

생성된 컨피그맵을 포드에서 사용하려면 디플로이먼트 등의 야믈파일에서 포드 템플릿 항목에 컨피그맵을 사용하도록 정의하면 됩니다.

image

컨피그맵을 포드에서 사용하는 방법은 크게 두 가지가 있습니다.

컨피그맵의 값을 컨테이너의 환경 변수로 사용

셸에서 echo $LOG_LEVEL과 같은 방식으로 바로 확인 가능

컨피그맵의 값을 포드 내부의 파일로 마운트해 사용

컨피그맵의 값을 포드 컨테이너 내부의 특정 파일로 마운트합니다. 예를 들어 LOG_LEVEL=INFO라는 값을 가지는 컨피그맵을 /etc/config/log_level이라는 파일로 마운트하면 log_level 파일에는 INFO라는 값이 저장됩니다. 이때 파일이 위치할 경로는 별도로 설정할 수 있습니다. nginx.conf 같은 파일을 통해 설정 값을 읽어 들인다면 이 방법이 좋습니다.

컨피그맵의 데이터를 컨테이너의 환경 변수로 가져오기

# all-env-from-configmap.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: container-env-example
spec:
  containers:
    - name: my-container
      image: busybox
      args: ['tail', '-f', '/dev/null']
      envFrom:
      - configMapRef:
          name: log-level-configmap
      - configMapRef:
          name: start-k8s

여기서는 포드 야믈파일로 설명하지만 포드를 사용하는 쿠버의 모든 오브젝트에 적용해 사용할 수 있습니다. (디플로이먼트, 스테이트풀셋, 데몬셋 등… 나중에 설명..)

envFrom, configMapRef 항목에서 log-level-configmap, start-k8s 2개의 컨피그맵으로부터 값을 가져와 환경 변수를 생성하도록 설정했습니다.

포드를 생성하고, 내부에서 환경 변수 목록을 출력해봅시당

$ kubectl apply -f all-env-from-configmap.yaml
$ kubectl exec container-env-example -- env

총 3개의 설정된 환경 변수가 포드 컨테이너에 설정된 것을 확인할 수 있습니다. 다른 환경변수는 쿠버가 자동으로 서비스에 대한 접근 정보를 컨테이너의 환경 변수로 설정하기 때문에 있는 것입니다.

이번에는 다른 방법으로 포드를 생성해봅시다. valueFrom, configMapKeyRef를 사용하면 여러 개의 키-값 쌍이 들어 있는 컨피그맵에서 특정 데이터만을 선택해 환경 변수로 가져올 수 있습니다.

# selective-env-from-configmap.yaml
apiVersion: v1
kind: Pod
metadata:
  name: container-selective-env-example
spec:
  containers:
    - name: my-container
      image: busybox
      args: ['tail', '-f', '/dev/null']
      env:
      - name: ENV_KEYNAME_1     # (1.1) 컨테이너에 새롭게 등록될 환경 변수 이름
        valueFrom:
          configMapKeyRef:
            name: log-level-configmap
            key: LOG_LEVEL
      - name: ENV_KEYNAME_2  # (1.2) 컨테이너에 새롭게 등록될 환경 변수 이름
        valueFrom:
          configMapKeyRef:
            name: start-k8s      # (2) 참조할 컨피그맵의 이름
            key: k8s             # (3) 가져올 데이터 값의 키
                                 # 최종 결과 -> ENV_KEYNAME_2=$(k8s 키에 해당하는 값) 즉,
                                 #              ENV_KEYNAME_2=kubernetes
$ kubectl apply -f selective-env-from-configmap.yaml
$ kubectl exec container-selective-env-example -- env

정리하자면,

  • envFrom : 컨피그맵에 존재하는 모든 키-값 쌍을 가져옵니다.
  • valueFrom, configMapKeyRef : 컨핌그맵에 존재하는 키-값 쌍 중에서 원하는 데이터만 선택적으로 가져옵니다.

컨피그맵의 내용을 파일로 포드 내부에 마운트하기

특정 파일로부터 설정 값을 읽어온다면 컨피그맵의 데이터를 포드 내부의 파일로 마운트해 사용할 수 있습니다. start-k8s 컨피그맵에 존재하는 모든 키-값 쌍을 /etc/config 디렉터리에 옮기는 야믈파일을 실행 해봅시다.

# volume-mount-configmap.yaml
apiVersion: v1
kind: Pod
metadata:
  name: configmap-volume-pod
spec:
  containers:
    - name: my-container
      image: busybox
      args: [ "tail", "-f", "/dev/null" ]
      volumeMounts:
      - name: configmap-volume          # volumes에서 정의한 컨피그맵 볼륨 이름
        mountPath: /etc/config             # 컨피그맵의 데이터가 위치할 경로

  volumes:
    - name: configmap-volume            # 컨피그맵 볼륨 이름
      configMap:
        name: start-k8s
  • spec.volumes : 야믈 파일에서 사용할 볼륨의 목록을 정의합니다. 위 예시에서는 start-k8s라는 이름의 컨피그맵을 통해 configmap-volume 볼륨을 정의했습니다. volumes 항목에서 정의한 볼륨은 spec.containers 항목에서 참조해 사용하고 있습니다.
  • spec.containers.volumeMounts : volumes 항목에서 정의된 볼륨을 컨테이너 내부의 어떤 디렉터리에 마운트할 것인지 명시합니다. 위 예시에서는 /etc/config 디렉터리에 컨피그맵의 값이 담긴 파일이 마운트될 것입니다.
$ kubectl apply -f volume-mount-configmap.yaml
$ kubectl exec configmap-volume-pod -- ls -l /etc/config
$ kubectl exec configmap-volume-pod -- cat /etc/config/k8s

컨피그맵에 저장돼 있던 두 개의 키-쌍 데이터, 즉 container와 k8s라는 키 이름이 파일로 존재하고 있습니다. 컨피그맵의 모든 키-값 쌍 데이터가 마운트됐으며, 파일 이름은 키의 이름과 같다는 것입니다.

컨피그맵과 같은 쿠버 리소스의 데이터를 포드 내부 디렉터리에 위치시키는 것을 투사(Projection)한다고 표현합니다.

원하는 키-쌍 데이터만 선택해서 포드에 파일로 가져올 수도 있습니다. start-k8s 컨피그맵의 데이터 중 k8s라는 키의 데이터만 포드 내부의 파일로 마운트 해봅시다.

# selective-volume-configmap.yaml
apiVersion: v1
kind: Pod
metadata:
  name: selective-cm-volume-pod 
spec:
  containers:
    - name: my-container
      image: busybox
      args: [ "tail", "-f", "/dev/null" ]
      volumeMounts:
      - name: configmap-volume
        mountPath: /etc/config       # 마운트되는 위치는 변경되지 않았습니다.
  volumes:
    - name: configmap-volume
      configMap:
        name: start-k8s
        items:                       # 컨피그맵에서 가져올 키-값의 목록을 나열합니다. 
        - key: k8s                    # k8s라는 키에 대응하는 값을 가져옵니다.
          path: k8s_fullname         # 최종 파일 이름은 k8s_fullname이 됩니다
  • items 항목 : 컨피그맵에서 가져올 키-값 목록을 의미하며, k8s라는 키만 가져오도록 명시했습니다.
  • path 항목 : 죄종적으로 디렉터리에 위치할 파일의 이름을 입력하는 항목으로, k8s_fullname이라는 값을 입력했습니다.
$ kubectl apply -f selective-volume-configmap.yaml
$ kubectl exec selective-cm-volume-pod -- ls /etc/config
$ kubectl exec selective-cm-volume-pod -- cat /etc/config/k8s_fullname

파일로부터 컨피그맵 생성하기

컨피그맵을 볼륨으로 포드에 제공할 때는 대부분 설정 파일 그 자체를 컨피그맵으로 사용하는 경우가 많습니다. 예를 들면 Nginx의 설정 파일인 nginx.conf, MySQL의 설정 파일인 mysql.conf의 내용을 아예 통째로 컨피그맵에 저장한 뒤 이를 볼륨 파일로 포드 내부에 제공하면 좀 더 효율적인 설정 관리가 가능합니다. 이런 경우를 위해 쿠버는 컨피그맵을 파일로부터 생성하는 기능 또한 제공합니다.

파일로부터 컨피그맵을 생성하려면 --from-file 옵션을 사용하면 된다.

$ kubectl create congifmap <컨피그맵 이름> --from-file <파일 이름> ...

$ echo Hello, world! >> index.html
$ kubectl create configmap index-file --from-file index.html

--from-file 옵션에서 별도의 키를 지정하지 않으면 파일 이름이 키로, 파일의 내용이 값으로 저장됩니다. 위의 예시에서는 index.html이라는 파일로 컨피그맵을 생성했기 때문에 index.html이 라는 키에 Hello, world!라는 값을 설정했습니다.

--from-env-file 옵션으로 여러 개의 키-값 형태의 내용으로 구성된 설정 파일을 한꺼번에 컨피그맵으로 가져올 수 있습니다.

# multiple-keyvalue.env
mykey1=myvalue1
mykey2=myvalue2
mykey3=myvalue3
$ kubectl create cm from-envfile --from-env-file multiple-keyvalue.env
$ kuabectl get cm from-envfile -o yaml

만약 정적 파일을 포드에 제공하려면 --from-file 옵션을 쓰는 것이 편리할 수도 있고, 여러 개의 환경 변수를 포드로 가져와야 한다면 --from-env-file 옵션을 사용하는 것이 편리할 수도 있습니다.

YAML 파일로 컨피그맵 정의하기

kubectl create 명령어에서 --dry-run과 -o yaml 옵션을 사용하면 컨피그맵을 생성하지 않은 채로 YAML 파일의 내용을 출력할 수 있습니다. 출력된 내용을 YAML 파일로 사용하면 컨피그맵 또한 YAML 파일로 배포해 사용할 수 있습니다.

$ kubectl create cm my-configmap \
  --from-literal mykey=myvalue --dry-run=client -o yaml
...
...
...

$ kubectl create cm my-configmap \
  --from-literal mykey=myvalue --dry-run=client -o yaml > my-configmap.yaml

$ kubectl apply -f my-configmap.yaml

일반적으로 dry run이란 특정 작업의 실행 가능 여부를 검토하는 명령어 또는 API를 의미합니다. 예를 들어 kubectl apply, create와 같은 명령어에 --dry-run 옵션을 추가하면 실행 가능 여부를 확인할 수 있으며, 실제로 쿠버 리소스를 생성하지는 않습니다.

컨피그맵의 키-값 데이터가 너무 많아지면 야믈 파일의 길이가 불필요하게 길어진다는 단점이 있습니다. 예를 들어 100줄의 nginx.conf 파일을 야믈파일로 배포하려면 야믈파일에 100줄의 nginx.conf 파일 내용을 모두 포함시켜야 하므로 야믈파일이 너무 길어지게 됩니다. 이럴 때 kubectl 명령어 1.14 버전부터 사용할 수 있는 kustomize 기능을 사용하면 더욱 편하게 컨피그맵을 생성할 수 있습니다. (나중에..)



시크릿(Secret)

시크릿은 SSH 키, 비밀번호 등과 같이 민감한 정보를 저장하기 위한 용도로 사용되며, 네임스페이스에 종속되는 쿠버네티스 오브젝트입니다. 시크릿과 컨피그맵은 사용 방법이 매우 비슷합니다. 시크릿도 문자열 값 등을 똑같이 저장하고, 포드 등의 오브젝트에 사용하는 형태입니다.

시크릿은 민감한 정보를 저장하기 위해 컨피그맵보다 좀 더 세분화된 사용 방법을 제공합니다.

my-password라는 이름의 시크릿을 생성하고, password=1q~~~~이라는 키-값 쌍을 저장해봅시다.

$ kubectl create secret generic my-password --from-literal password=1q2w3e4r

컨피그맵처럼 --from-literal 대신 --from-file이나 --from-env-file 옵션을 이용해 파일로부터 값을 읽어와 사용할 수 있습니다.

$ echo mypassword > pw1 && echo yourpassword > pw2
$ kubectl create secret generic \
> our-password --from-file pw1 --from-file pw2

시크릿에서는 특수한 옵션인 generic이라는 것을 명시했습니다. 시크릿은 컨피그맵과 달리 데이터의 사용 목적에 따라 몇 가지 종류로 나뉘기 때문입니다. 위의 명령어로 생성된 시크릿들을 확인해보면,

$ kubectl get secrets

참고사항으로 위 명령어를 쳤을 때 나오는 default-token-… 이라는 이름의 시크릿이 미리 생성돼 있을텐데 이 시크릿은 쿠버 오브젝트 중 하나인 ServiceAccount에 의해 네임스페이스별로 자동으로 생성된 시크릿입니다. (나중에 학습)

describe, get ~~ -o yaml 명령어로 생성한 시크릿의 내용을 확인해보겠습니다.

$ kubectl describe secret my-password
$ kubectl get secret my-password -o yaml

컨피그맵과 비슷하지만 키-값 쌍에서 값에 해당하는 부분이 이상한 값으로 변형이 되어 있습니다. 이는 시크릿에 값을 저장할 때, 쿠버가 기본적으로 base64로 값을 인코딩하기 때문입니다. 위 명령어를 실행했을 때 출력 내용 중 MXEydz~~~~=1q2w~~~로 인코딩한 값에 해당합니다.

$ echo MXEydzNlNHI= | base64 -d

이런식이기 때문에 야믈 파일로부터 시크릿을 생성할 때는 데이터의 값에 base64로 인코딩이 된 문자열을 사용해야 합니다. ~~ create ~~ --dry-run 옵션을 추가해보면 야믈파일 자체에 base64로 인코딩이 된 문자열이 포함된 것을 볼 수 있습니다.

$ kubectl create secret generic my-password --from-literal password=1q2w3e4r --dry-run=client -o yaml

이렇게 생성된 시크릿은 컨피그맵과 비슷하게 사용할 수 있습니다. 시크릿의 키-값 데이터를 포드의 환경 변수로 설정할 수도 있고, 특정 경로의 파일로 포드 내에 마운트할 수도 있습니다. 단, 사용되는 야믈파일의 항목이 configmap이 아닌 secret라는 점이 다릅니다.

예를 들어, 시크릿에 저장된 모든 키-값 쌍을 포드의 환경 변수로 가져오려면 야믈 파일에서 아래와 같이 정의합니다(secretRef). 또는 컨피그맵과 마찬가지로 시크릿의 키-값 쌍 중에서 특정 데이터만 선택해 원하는 이름의 환경 변수로 가져올 수도 있습니다(secretKeyRef).

# vi env-from-secret.yaml
apiVersion: v1
kind: Pod
metadata:
  name: secret-env-example
spec:
  containers:
    - name: my-container
      image: busybox
      args: ['tail', '-f', '/dev/null']
      envFrom:
      - secretRef:
          name: my-password
# selective-env-from-secret.yaml
apiVersion: v1
kind: Pod
metadata:
  name: selective-secret-env-example
spec:
  containers:
  - name: my-container
    image: busybox
    args: ['tail', '-f', '/dev/null']
    env:
    - name: YOUR_PASSWORD
      valueFrom:
        secretKeyRef:
          name: our-password
          key: pw2

또는 시크릿의 키-값 데이터를 파일로 포드의 볼륨에 마운트 할 수도 있으며, 여러 개의 키-값 쌍이 존재하는 시크릿에서 선택적으로 사용할 수도 있습니다.

# vi volume-mount-secret.yaml
apiVersion: v1
kind: Pod
metadata:
  name: secret-volume-pod
spec:
  containers:
    - name: my-container
      image: busybox
      args: [ "tail", "-f", "/dev/null" ]
      volumeMounts:
      - name: secret-volume          #  volumes에서 정의한 시크릿 볼륨 이름
        mountPath: /etc/secret             # 시크릿의 데이터가 위치할 경로
  volumes:
  - name: secret-volume            # 시크릿 볼륨 이름
    secret:
      secretName: our-password                  # 키-값 쌍을 가져올 컨피그맵 이름
# vi selective-mount-secret.yaml
apiVersion: v1
kind: Pod
metadata:
  name: selective-volume-pod
spec:
  containers:
  - name: my-container
    image: busybox
    args: [ "tail", "-f", "/dev/null" ]
    volumeMounts:
    - name: secret-volume
      mountPath: /etc/secret
  volumes:
  - name: secret-volume
    secret:
      secretName: our-password          # our-password 라는 시크릿을 사용
      items:                       # 시크릿에서 가져올 키-값의 목록
      - key: pw1                    # pw1라는 키에 대응하는 값이 사용됨.
        path: password1           # 최종 파일은 /etc/config/password1이 됨

단, 시크릿을 생성할 때 야믈 파일에 base64로 인코딩한 값을 입력했더라고 시크릿을 포드의 환경 변수나 볼륨 파일로서 가져오면 base64로 디코딩된 원래의 값을 사용하게 됩니다.

$ kubectl apply -f selective-mount-secret.yaml
$ kubectl exec selective-volume-pod -- cat /etc/secret/password1

$ kubectl get secrets our-password -o yaml
$ echo bXlwYXNzd29yZAo= | base64 -d

이미지 레지스트리 접근을 위한 docker-registry 타입의 시크릿 사용하기

kubectl get secrets 명령어로 시크릿의 목록을 확인했을 때 볼 수 있었던 Type 항목이 시크릿의 종류에 해당합니다.

Opaque 타입은 별도로 시크릿의 종류를 명시하지 않으면 자동으로 설정되는 타입으로, 사용자가 정의하는 데이터를 저장할 수 있는 일반적인 목적의 시크릿입니다. 위에서 kubectl create 명령어로 시크릿을 생성할 때 generic이라고 명시했던 것이 바로 Opaque 타입에 해당하는 종류입니다.

여러 종류의 시크릿 중 하나인 비공개 레지스트리에 접근할 때 사용하는 인증 설정 시크릿을 알아봅시다.

지금까지 작성한 야믈 파일 안의 도커 이미지들은 전부 도커 허브에 공개돼 있기 때문에 별도의 레지스트리 인증을 할 필요가 없었지만, 여러분의 사설 레지스트리 또는 도커 허브, GCR(구글), ECR(AWS) 등의 클라우드 레지스트리를 사용하고 있다면 로그인 등과 같은 인증 절차가 필요합니다.

단일 서버에서 도커 데몬을 사용할 때는 docker login 명령어를 사용할 수 있었습니다. 하지만 쿠버네티스에서는 docker login 명령어 대신 레지스트리의 인증 정보를 저장하는 별도의 시크릿을 생성해 사용합니다. 레지스트리 인증을 위해 시크릿을 생성하는 방법은 두 가지가 있습니다. 첫 번쨰는 docker login 명령어로 로그인에 성공했을 때 도커 엔진이 자동으로 생성하는 ~/.docker/config.json 파일을 사용하는 것입니다.

# Window Docker Desktop + minikube + WSL에서
$ sudo find / -name config.json # config.json 경로 찾기
$ kubectl create secret generic registry-auth \
> --from-file=.dockerconfigjson=/home/ihp001/.docker/config.json \
> --type=kubernetes.io/dockerconfigjson
secret/registry-auth created

또는 시크릿을 생성하는 명령어에서 직접 로그인 인증 정보를 명시할 수도 있습니다. 각 옵션에 적절한 인자를 입력하면 되며, --docker-username과 --docker-password 옵션은 로그인 이름과 비밀번호를 입력하는 필수 옵션입니다.

$ kubectl create secret docker-registry registry-auth-by-cmd \
> --docker-username=xxxxxxx \
> --docker-password=xxxxxxx
secret/registry-auth-by-cmd created

--docker-server 옵션을 사용하지 않으면 기본적으로 도커 허브를 사용하도록 설정되지만, 다른 사설 레지스트리를 사용하려면 --docker-server 옵션에 해당 서버의 주소 또는 도메인 이름을 입력하면 됩니다.

위의 명령어들로 생성된 시크릿은 kubernetes.io/dockerconfigjson이라는 타입으로 설정됩니다.

$ kubectl get secrets

이 시크릿은 디플로이먼트 또는 포드 등에서 사설 레지스트리로부터 이미지를 받아올 때 사용할 수 있습니다. 예를 들어 도커 허브의 프라이빗 저장소에 저장된 이미지를 통해 포드를 생성하려면 다음과 같이 야믈파일에서 imagePullSecret 항목을 정의합니다.

apiVersion: apps/v1
kind: Deployment
...
    spec: 
      containers: 
      - name: test-container
        image: {이미지 이름}
      imagePullSecrets:
      - name: registry-auth-registry

TLS 키를 저장할 수 잇는 tls 타입의 시크릿 사용하기

시크릿은 TLS 연결에 사용되는 공개키, 비밀키 등을 쿠버네티스에 자체적으로 저장할 수 있도록 tls 타입을 지원합니다. 따라서 포드 내부의 애플리케이션이 보안 연결을 위해 인증서나 비밀키 등을 가져와야 할 때 시크릿의 값을 포드에 제공하는 방식으로 사용할 수 있습니다.

tls 타입의 시크릿을 사용하는 방법은 kubectl create secret tls 명령어로 쉽게 생성할 수 있습니다. 이전에 사용했던 명령어와 조금 다른 점은 시크릿의 종류로 generic이나 docker-registry 대신 tls를 사용하고, --cert와 --key 옵션을 사용해 인증서와 키를 직접 명시해 준다는 것입니다.

$ openssl req -new -newkey rsa:4096 -days 365 -nodes \
> -x509 -subj "/CN=example.com" -keyout cert.key -out cert.crt
Generating a RSA private key
..++++
......................................................................++++
writing new private key to 'cert.key'
-----

$ kubectl create secret tls my-tls-secret \
> --cert cert.crt --key cert.key
secret/my-tls-secret created

생성된 시크릿의 정보를 확인해보면 cert.crt와 cert.key 파일의 내용이 tls.crt와 tls.key라는 키로 저장돼 있음을 알 수 있습니다. 각 데이터는 모두 base64로 인코딩되어 저장됩니다.

$ kubectl get secrets my-tls-secret
$ kubectl get secrets my-tls-secret -o yaml

좀 더 쉽게 컨피그맵과 시크릿 리소스 배포하기

시크릿을 YAML 파일로 배포하려면 시크릿의 데이터를 YAML 파일에 함께 저장해둬야 합니다. 예를 들어 tls 타입의 시크릿을 인증서와 함께 배포하려면 kubectl create의 --dry-run 명령어에서 출력되는 YAML 파일 내용을 저장해 사용할 수 있습니다.

$ kubectl create secret tls my-tls-secret --cert cert.crt --key cert.key --dry-run -o yaml

그러나 시크릿의 데이터가 많아질수록 tls.crt의 데이터가 매우 길어 가독성이 좋지 않을 뿐만 아니라, YAML 파일과 데이터가 분리되지 않아 데이터를 관리하기에도 불편합니다.

이러한 단점을 해결하면서 시크릿이나 컨피그맵을 배포하기 위해 YAML 파일을 작성할 때, 데이터를 YAML 파일로부터 분리할 수 있는 kustomize 기능을 사용할 수 있습니다. kustomize는 자주 사용되는 YAML 파일의 속성을 별도로 정의해 재사용하거나 여러 YAML 파일을 하나로 묶는 등 다양한 용도로 사용할 수 있는 기능입니다.

# vi kustomization.yaml
secretGenerator:
- name: kustomize-secret
  type: kubernets.io/tls
  files:
  - tls.crt=cert.crt
  - tls.key=cert.key

시크릿을 생성하기 전에 kustomize로부터 생성될 시크릿의 정보를 미리 확인하려면 kubectl kustomize 명령어를 사용합니다. 이는 kubectl create 명령어에서 --dry-run 옵션을 사용했던 것과 비슷합니다.

$ kubectl kustomize ./
apiVersion: v1
data:
  tls.crt: |
    LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUZEVENDQXZXZ0F3SUJBZ0lVVDd6aG
    9NUWx4dzk0djk4NjhKbTVFUldtSmN
    ...
    ...

kustomization.yaml 파일로부터 시크릿을 생성하려면 해당 파일이 위치한 디렉터리에서 kubectl apply -k ./ 명령어를 사용합니다.

$ kubectl apply -k ./
$ kubectl delete -k ./

만약 컨피그맵을 kustomize로부터 생성하고 싶다면 kustomization.yaml 파일에서 secretGenerator 대신 configmapGenerator를 사용하면 됩니다. 컨피그맵은 시크릿과 달리 종류가 존재하지 않으므로 type 항목을 정의하지 않았습니다.

kustomize로부터 생성된 컨피그맵이나 시크릿의 이름 뒤에는 컨피그맵, 시크릿에 저장된 데이터로부터 추출된 해시값이 자동으로 추가됩니다. kubectl 명령어를 사용할 때도 --append-hash 옵션을 이용해 리소스의 이름 뒤에 해시값을 추가할 수 있습니다. --append-hash를 사용하면 데이터를 시크릿이나 컨피그맵의 이름에 명확히 나타낼 수 있다는 장점이 있기 때문에 설정 값을 업데이트할 때 유용하게 사용할 수 있습니다.

$ kubectl create secret tls kustomize-secret --cert cert.crt --key cert.key --append-hash

컨피그맵이나 시크릿을 업데이트해 애플리케이션의 설정 값 변경하기

애플리케이션의 설정 값을 변경해야 한다면 컨피그맵이나 시크릿의 값을 kubectl edit 명령어로 수정해도 되고, YAML 파일을 변경한 뒤 다시 kubectl apply 명령어를 사용해도 됩니다. 지금까지 사용해본 적은 없지만 kubectl patch 라는 명령어도 사용할 수 있습니다.

# vi my-configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: mu-configmap
data:
  mykey: yourvalue
$ kubectl apply -f my-configmap.yaml
configmap/mu-configmap created
$ kubectl get cm
$ sed -i -e 's/myvalue/yourvalue/g' my-configmap.yaml
$ kubectl apply -f my-configmap.yaml
configmap/mu-configmap configured

컨피그맵이나 시크릿을 포드에 제공하는 방법으로 크게 두 가지 방법을 설명했습니다. 첫번쨰는 환경 변수로 포드 내부에 설정 값을 제공하는 방법이고, 두 번째는 볼륨 파일로 포드 내부에 마운트하는 방법이었습니다. 첫 번째 방법으로 설정된 값은 컨피그맵이나 시크릿의 값을 변경해도 자동으로 재설정되지 않으며, 디플로이먼트의 포드를 다시 생성해야만 합니다. 그러나 파일로 포드 내부에 마운트 된 설정 파일은 컨피그맵이나 시크릿을 변경하면 파일의 내용 또한 자동으로 갱신됩니다.

단, 포드 내부에 마운트된 설정 파일이 변경됐다고 해서 포드에서 실행 중인 애플리케이션의 설정이 자동으로 변경되는 것은 아닙니다. 애플리케이션의 프로세스는 이미 이전의 설정 파일로 실행된 상태이며, 변경된 설정 파일을 자동으로 다시 로드하지는 않기 때문입니다. 업데이트된 설정 값을 포드 내부의 프로세스가 다시 로드하려면 로직을 직접 구현해야 합니다.

예를 들어 변경된 파일을 다시 읽어 들이도록 컨테이너의 프로세스에 별도의 시그널을 보내는 사이드카 컨테이너를 포드에 포함시킬 수도 있습니다. 또는 애플리케이션의 소스코드 레벨에서 쿠버네티스의 API를 통해 컨피그맵, 시크릿의 데이터 변경에 대한 알림을 받은 뒤, 자동으로 리로드하는 로직을 생각해 볼 수도 있습니다.





출처
시작하세요! 도커/쿠버네티스(용찬호 저, 위키북스)
example

728x90
반응형
댓글
반응형
250x250
글 보관함
최근에 달린 댓글
«   2024/04   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
Total
Today
Yesterday
링크