본문 바로가기
킷도우의 클라우드, 쿠버네티스/CKA 자격증

[CKA 자격증 준비] 쿠버네티스 핵심 개념 5탄(kubectl 명령어 위주 정리, kubectl 명령어 모음집) - Application Lifecycle Management in kubernetes

by 킷도우 2024. 1. 20.
반응형

안녕하세요. IT 윈도우 킷도우입니다.

CKA 자격증 준비 4탄 쿠버네티스 클러스터에 이어서 계속해서 5탄 Scheduling에 대해서 알아보겠습니다.

 

본 내용은 CKA 강의로 유명한 Udemy CKA with practice tests의 내용을 토대로 정리한 것입니다.

Deployment Strategy

배포 전략에는 두가지가 있습니다.

 

1.  Recreate 배포

배포된 것을 모두 파괴하고 새롭게 rollout 하는 것입니다. 이 방법을 사용하게 되면 중간에 Application Down이 발생할 수 있습니다.

 

2. Rolling Update 배포

먼저 새롭게 Replicaset을 만들고 파드를 선배포합니다. 그리고 기존의 Replicaset의 파드를 하나씩 삭제합니다. 배포 방법을 정의하지 않을 경우 롤링 배포로 default 진행됩니다.

 

명령어 정리

kubectl create -f deployment-definition.yaml #create
kubectl get deployments #select
kubectl apply -f deployment-definiition.yaml #create and deploy
kubectl set image deployment/myapp-deployment nginx=nginx:1.9.1 #deploy
kubectl rollout status deployment/myapp-deployment #select status
kubectl rollout history deployment/myapp-deployment #select history
kubectl rollout undo deployment/myapp-deployment #rollback

 

Commands and Argements

파드에 커맨드를 지정하는 방법은 아래와 같다.

apiVersion: v1
kind: Pod
metadata:
  name: ubuntu-sleeper-pod
spec:
  containers:
  - name: ubuntu-sleeper
    image: ubuntu-sleeper
    command:["sleep2.0"]  # ENTRYPOINT in docker image
    args: ["10"]          # CMD in docker image
    
# 위 정의는 아래와 같은 명령의 효과가 있습니다
# docker run --name ubuntu-sleeper --entrypoint sleep2.0 ubuntu-sleeper 10
# pod에 command를 정의할 땐 반드시 배열 형태로 정의해야합니다.
# ex. command: ["sleep","5000"]
# 또는
# command:
# -"sleep"
# -"5000"
# 또는 별도로 arge key를 사용해서 매개변수를 별도로 선언해도 됩니다.
# command: ["sleep"]
# arge: ["5000"]

 

Configure Environment Variable in Application

어플레케이션에 리눅스 환경 변수를 선언하는 법은 아래와 같습니다.

apiVersion: v1
kind: Pod
metadata:
  name: simple-webapp-color
spec:
  containers:
  - name: simple-webapp-color
    image: simple-webapp-color
    ports:
      - containerPort: 8080
    env:
    - name: APP_COLOR
      value: pink
      
# in docker : docker run -e APP_COLOR=pink simple-webapp-color

 

이것은 plain 하게 환경 변수를 저장하는 방법입니다. 어플리케이션의 환경 변수를 지정하는 방법은 크게 3가지가 있습니다.

 

1. Plain Key Value

 evn:
 - name: APP_COLOR
   value: pink

 

2. ConfigMap

evn:
- name: APP_COLOR
  valueFrom:
    configMapKeyRef

 

3. Secret

env:
- name: APP_COLOR
  valueFrom:
    secretKeyRef

 

Configuring ConfigMaps in Applications

ConfigMap 이 만들어진 목적은 아무래도 파드 단위로 환경 변수를 개별 선언, 저장하게 되면 관리하기가 매우 복잡해집니다. 이 환경 변수를 좀 더 중앙집중적으로 관리하기 위해 ConfigMap이 만들어진 것입니다.

 

Impertative한 방법으로 ConfigMap을 추가하는 방법은 아래와 같습니다.

kubectl create configmap <config-name> --from-literal=<key>=<value>

kubectl create configmap app-config --from-literal=APP_COLOR=blue

#여기서 환경 변수를 추가 하고 싶다면 --from-literal 명령을 계속 추가로 넣어주면 됩니다.
kubectl create configmap app-config --from-literal=APP_COLOR=blue --from-literal=APP_MOD=prod

하지만 이렇게 관리하는 것은 환견 변수가 많아지면 많아질 수록 관리하기 복잡해지므로 아래와 같이 파일을 통해서 선언합니다.

kubectl create configmap <config-name> --from-file=<path-to-file>				
# ex) kubectl create configmap app-config --from-literal=app_config.prorperties

 

 file은 아래와 같이 key value 형태로 저장합니다.

APP_COLOR: blue	
APP_MODE: prod

 

이제 declarative 한 방법으로 ConfigMap을 만드는 방법을 알아보겠습니다.  apiVersion, kind, metadata, data 이 큰 틀로 들어갑니다. pod와 다른점은 spec부분이 없습니다.

apiVersion: v1	
kind: ConfigMap	
metadata:	
  name: app-config	
data:	
  APP_COLOR: blue	
  APP_MODE: prod
  
# 이 이후 다음 명령어 수행하여 생성
# kubectl create -f config-map.yaml

 

이렇게 ConfigMap을 만들었다면, Pod에 이 ConfigMap 설정을 주입하면 됩니다.

apiVersion: v1
kind: Pod
metadata:
  name: simple-webapp-color
spec:
  containers
  - name: simple-webapp-color
    image: simple-webapp-color
    ports:
    - containerPort: 8080
    envFrom:
    - configMapRef:
        name: app-config

 

생성한 configmap은 아래와 같이 조회할 수 있습니다.

kubectl get configmaps
kubectl descirbe configmaps

 

ConfigMap 데이터를 파드에 주입하는 다른 방법도 있습니다. Volumn을 활용합니다.

apiVersion: v1
kind: Pod
metdata:
  name: simple-webapp-color
  labels:
    name: simple-webapp-color
spec:
  containers:
  - name: simple-webapp-color
    image: simple-webapp-color
    ports:
    - containerPort: 8080
  volumes:
  - name: app-config-volume
    configMap:
      name: app-config
반응형

Configure Secrets in Applications

Secrets와 Configmaps의 차이는 Secrets이 좀 더 Safe하게 데이터를 저장한다. BaseMap으로 저장하는 것이 특징이다.

Imperative 한 방법으로 Secret을 만드는 법은 아래와 같습니다.

kubectl create secret generic --from-literal=<key>=<value>
# ex) kubectl create secret generic app-secret --from-literal=DB_Host=mysql
#여러 개 추가하고 싶다면 --from-literal=DB_User=root 이런식으로 여러 개 붙이면 된다.

#configmap과 마찬가지로 개별 설정이 너무 많으면 관리가 복잡해지니 이런경우 파일을 통해서 한번에 읽어오게 하자.

kubectl create secret generic <secret-name> --from-file=<path-to-file>
# ex) kubectl create secret generic app-secret --from-file=app_secret.prorperties

 

declarative 한 방법으로 Secret을 만드는 법은 아래와 같습니다.

apiVersion: v1
kind: Secret
metadata:
  name: app-secret
data:
  DB_Host: bXlxcWw= #(->mysql의 인코딩 데이터)
  DB_User: cm9vdA== #(-> root의 인코딩 데이터)
  DB_Password: cGFzd3JK #(->paswrd의 인코딩 데이터)

 

인코딩 데이터를 넣는 방법은 리눅스에서 echo -n 그리고 base64 명령어를 활용합니다.

echo -n 'mysql' | base64

 

secret을 조회하려면 아래와 같습니다.

kubectl get secrets
kubectl describe secrets
# 이렇게 조회할 경우 환경변수 값을 볼수가 없다.
# 따라서 아래와 같이 -o yaml 형태로 조회한다.
kubectl get secret app-secret -o yaml

 

여기서 base64로 인코딩된 값을 보려면 echo -n 그리고 base64  --decode 명령어를 활용하여 봅니다.

echo -n 'bXlzcWw=' | base64 --decode

 

다음으로 이 secret을 파드에 주입하는 방법은 configmap과 유사합니다.

apiVersion: v1
kind: Pod
metadata:
  name: simple-webapp-color
spec:
  containers:
  - name: simple-webapp-color
    image: simple-webapp-color
    ports:
    - containerPort: 8080
    envFrom:
    - secretRef:
        name: app-secret

 

Single Value만 가져오는 경우는 아래와 같습니다.

apiVersion: v1
kind: Pod
metadata:
  name: simple-webapp-color
spec:
  containers:
  - name: simple-webapp-color
    image: simple-webapp-color
    ports:
    - containerPort: 8080
    evn
    - name: DB_Password
      valueFrom:
        secretKeyRef:
          name: app-secret
          key: DB_Password

 

Volume을 활용하여 가져오는 경우 아래와 같이 진행합니다.

apiVersion: v1
kind: Pod
metadata:
  name: simple-webapp-color
spec:
  containers:
  - name: simple-webapp-color
    image: simple-webapp-color
    ports:
    - containerPort: 8080
    volumes:
    - name:  app-secret-volume
      secret:
        secretName: app-secret

 

Imperative 방식으로 Secret을 만드는 것과 declarative한 방법으로 만드는 것은 차이가 있습니다. Imperative 한 방식으로 Secret을 만들 경우 value값이 자동으로 base64로 인코딩되서 들어갑니다. 하지만 declarative한 방식으로 선언할 경우 우리가 직접 인코딩을 해줘야합니다. 따라서 시험을 치루르땐 secret은 Imperative한 방법으로 만드는 것이 좀 더 편리하다고 볼 수 있겠습니다.

 

More Detail Info about Secret

시크릿은 암호화되어 있지 않습니다. 단순 인코딩 돼있는 것으로 누구든 파일 열어 디코딩할 수 있습니다. 따라서 완벽히 안전한 데이터라고 볼 수 없다는 것을 명심하고 gitgub등에 파일을 push하면 안됩니다. 시크릿은 그 존재 자체만으로는 안전하지 않으면 그 주변 구성을 어떻게 하느냐에 따라서 Safe한지 Unsafe한지가 결졍된다고 보면 되겠습니다.

 

아무런 조치없이 단순히 Secret을 만들 경우 암호화되지 않은 상태로 ETCD 서버에 key-value가 쌓이게됩니다. ETCD 서버에는 Namespace에서 Pod를 만들 수 있다면 누구든 조회해볼 수 있는 공간입니다. 한 번 조회해 보겠습니다. k8s doc에서 Encrypting Secret Data at Rest라는 주제의 문서를 보면 ETCDCTL_API=3라는 것을 볼 수 있습니다. 관련해서 etctctl 명령어로 ETCD 서버에 질의를 할 수 있는데 이를 위해 아래와 같이 etcd-client를 다운로드합니다.

apt-get install etcd-client

 

secret을 보려면 인증서가 필요하고 아래와 같은 명령어로 시크릿을 조회해 볼 수 있습니다.

"ETCDCTL_API=3 etcdctl \
   --cacert=/etc/kubernetes/pki/etcd/ca.crt   \
   --cert=/etc/kubernetes/pki/etcd/server.crt \
   --key=/etc/kubernetes/pki/etcd/server.key  \
   get /registry/secrets/default/my-secret"

 

평문으로 된 데이터를 보려면 아래와 같이 조회합니다.

ETCDCTL_API=3 etcdctl \
   --cacert=/etc/kubernetes/pki/etcd/ca.crt   \
   --cert=/etc/kubernetes/pki/etcd/server.crt \
   --key=/etc/kubernetes/pki/etcd/server.key  \
   get /registry/secrets/default/my-secret | hexdump -C

 

데이터가 실제로 etcd에서 암호화되지 않은 포맷으로 저장된 것을 볼 수 있습니다. 바로 이 부분이 문제입니다. 암호화되지 않고 저장되는 중요 정보가 있다는 점입니다.

 

Encrypting Secret 관련 k8s 문서 가장 상단에 보면 해당 시크릿이 암호화가 돼있는지 안돼있는지를 미리 확인할 수 있는데 확인하는 방법은 아래와 같습니다.

ps -aux | grep kube-apiserver
# 또는 ls /etc/kubernetes/manifests 하위에서 kube-apiserver.yaml에서 아래 옵션이 들어있는지 확인

 

위와 같이 조회했을 때  --encryption-provider-config 옵션이 있는지를 살펴봅니다. 옵션이 없다면 암호화가 활성화 돼있지 않은 것입니다.

 

그러면 암호화를 적용하는 방법을 알아보겠습니다.

EncryptionConfiguration 

Secret을 암호화하는데 필요한 첫번쨰는 EncryptionConfiguration을 구성하는 것입니다. 이를 만들어서 옵션으로 넘기면됩니다. k8s doc에서 EncryptionConfiguration 선언하는 것을  참조합니다.

 

#
# CAUTION: this is an example configuration.
#          Do not use this for your own cluster!
#
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
  - resources:
      - secrets
      - configmaps
      - pandas.awesome.bears.example # a custom resource API
    providers:
      # This configuration does not provide data confidentiality. The first
      # configured provider is specifying the "identity" mechanism, which
      # stores resources as plain text.
      # 여기서 암호화되는 순서가 중요하다. Identity:{}는 암호화를 하지 않겠다는 것이다. 첫번째가 이것이기 떄문에 암호화가 되지 않는 것이다. 데이터를 암호화하고 싶다면 aesgcm, aescbc, secretbox 중 하나가 가장 첫 번쨰에 있어야한다.
      - identity: {} # plain text, in other words NO encryption
      - aesgcm:
          keys:
            - name: key1
              secret: c2VjcmV0IGlzIHNlY3VyZQ==
            - name: key2
              secret: dGhpcyBpcyBwYXNzd29yZA==
      - aescbc:
          keys:
            - name: key1
              secret: c2VjcmV0IGlzIHNlY3VyZQ==
            - name: key2
              secret: dGhpcyBpcyBwYXNzd29yZA==
      - secretbox:
          keys:
            - name: key1
              secret: YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoxMjM0NTY=
  - resources:
      - events
    providers:
      - identity: {} # do not encrypt Events even though *.* is specified below
  - resources:
      - '*.apps' # wildcard match requires Kubernetes 1.27 or later
    providers:
      - aescbc:
          keys:
          - name: key2
            secret: c2VjcmV0IGlzIHNlY3VyZSwgb3IgaXMgaXQ/Cg==
  - resources:
      - '*.*' # wildcard match requires Kubernetes 1.27 or later
    providers:
      - aescbc:
          keys:
          - name: key3
            secret: c2VjcmV0IGlzIHNlY3VyZSwgSSB0aGluaw==

 

아래는 예시입니다.

enc.yaml파일을 아래와 같이 만들어줍니다.

apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
  - resources:
      - secrets
    providers:
      - aescbc:
          keys:
            - name: key1
              secret: <BASE 64 ENCODED SECRET>
      - identity: {}

이렇게 만들고 위에서 얘기한대로 vi /etc/kubenetes/manifests/kube-apiserver.yaml 파일의 volume과 command 수정합니다. volumeMounts를 해줘야하고 command에 --encryption-provider-config=/etc/kubenetes/enc/enc.yaml을 추가하면 됩니다. k8s doc 문서를 참조합니다.

 

이 후 아래 명령어로 정사적으로 설정이 들어갔는 지 확인합니다.

ps aux | grep kube-api | grep encry

 

이제 또 다른 Secret을 만들고 이 것이 잘 암호화가 됐는지 확인해봅니다. 암호화 설정을 해줬기 때문에 암호화가 돼있어야 정상입니다.

ETCDCTL_API=3 etcdctl \
   --cacert=/etc/kubernetes/pki/etcd/ca.crt   \
   --cert=/etc/kubernetes/pki/etcd/server.crt \
   --key=/etc/kubernetes/pki/etcd/server.key  \
   get /registry/secrets/default/my-secret2 | hexdump -C

 

위 명령으로 조회하면 my-secret2가 조회가 안되는 것을 볼 수 있습니다. 물론 위 etctctl 명령을 수행한 이후 만든 secret에 한해 암호화가 되는 것도 잊지 말자.

 

 
반응형

댓글