Kubernetes에서 중복 정의된 환경 변수를 삭제하면 Resource 측면의 환경 변수가 사라집니다

26612 단어 Kubernetestech

개시하다


Kubbernetes의 환경 변수 주변에서 뜻밖의 행동을 당해 사고가 났기 때문에 행동·대응법을 공유하고 싶다.

TL;DR

  • Manifest(이번은 Deployment)에서 환경 변수를 반복적으로 정의하고kubectl apply
  • 중복된 환경 변수에서kubectl apply
  • 를 삭제합니다
    Pod에서 환경 변수가 사라짐
  • kubectl apply를 한 번 더 치면 돼요
  • 재현 순서


    환경 구조


    아마도 어느 Kubernetes도 재현할 수 있을 것 같지만, 이번에는 현지에서 간단하게 구축할 수 있을 것 같다. kind
    cluster.yaml
    kind: Cluster
    apiVersion: kind.x-k8s.io/v1alpha4
    name: envvar-cluster
    nodes:
    - role: control-plane
    - role: worker
    
    $ kind create cluster --config cluster.yaml
    (略)
     Have a nice day!
    $ 
    $ 
    $ kubectl cluster-info
    Kubernetes control plane is running at https://127.0.0.1:56066
    CoreDNS is running at https://127.0.0.1:56066/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
    
    To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.
    $ 
    $ 
    $ kubectl get nodes -o wide
    NAME                           STATUS   ROLES                  AGE   VERSION   INTERNAL-IP   EXTERNAL-IP   OS-IMAGE       KERNEL-VERSION     CONTAINER-RUNTIME
    envvar-cluster-control-plane   Ready    control-plane,master   72m   v1.23.4   172.19.0.3    <none>        Ubuntu 21.10   5.10.76-linuxkit   containerd://1.5.10
    envvar-cluster-worker          Ready    <none>                 72m   v1.23.4   172.19.0.2    <none>        Ubuntu 21.10   5.10.76-linuxkit   containerd://1.5.10
    

    동작 확인


    다음의 특이한 점이 없는 Deployment를 토대로 확인합니다.
    deployment.yaml
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: nginx-deployment
      labels:
        app: nginx
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: nginx
      template:
        metadata:
          labels:
            app: nginx
        spec:
          containers:
          - name: nginx
            image: nginx:latest
            ports:
            - containerPort: 80
            env:
            - name: SAMPLE_ENV_VAR1
              value: sample1
            - name: SAMPLE_ENV_VAR2
              value: sample2
    

    1단계: Deployment 만들기


    $ kubectl apply -f deployment.yaml
    deployment.apps/nginx-deployment created
    $ 
    $ 
    $ kubectl get deployment -o wide
    NAME               READY   UP-TO-DATE   AVAILABLE   AGE   CONTAINERS   IMAGES         SELECTOR
    nginx-deployment   1/1     1            1           16s   nginx        nginx:latest   app=nginx
    $ 
    $ 
    $ kubectl get replicasets -o wide
    NAME                        DESIRED   CURRENT   READY   AGE   CONTAINERS   IMAGES         SELECTOR
    nginx-deployment-dfc74d86   1         1         1       60s   nginx        nginx:latest   app=nginx,pod-template-hash=dfc74d86
    $ 
    $ 
    $ kubectl get pods -o wide
    NAME                              READY   STATUS    RESTARTS   AGE   IP            NODE                    NOMINATED NODE   READINESS GATES
    nginx-deployment-dfc74d86-qmck4   1/1     Running   0          79s   10.244.1.14   envvar-cluster-worker   <none>           <none>
    
    다음과 같이 환경 변수를 설정합니다.
    $ kubectl get deployments -o json | jq '.items[].spec.template.spec.containers[].env'
    [
      {
        "name": "SAMPLE_ENV_VAR1",
        "value": "sample1"
      },
      {
        "name": "SAMPLE_ENV_VAR2",
        "value": "sample2"
      }
    ]
    $ kubectl get replicasets -o json | jq '.items[].spec.template.spec.containers[].env'
    [
      {
        "name": "SAMPLE_ENV_VAR1",
        "value": "sample1"
      },
      {
        "name": "SAMPLE_ENV_VAR2",
        "value": "sample2"
      }
    ]
    $ kubectl get pods -o json | jq '.items[].spec.containers[].env'
    [
      {
        "name": "SAMPLE_ENV_VAR1",
        "value": "sample1"
      },
      {
        "name": "SAMPLE_ENV_VAR2",
        "value": "sample2"
      }
    ]
    

    2단계: 반복 환경 변수


    방금 전의 Manifest는 다음과 같이 응용 프로그램을 편집합니다.
            env:
            - name: SAMPLE_ENV_VAR1
              value: sample1
            - name: SAMPLE_ENV_VAR2
              value: sample2
    +       - name: SAMPLE_ENV_VAR2
    +         value: sample2
    
    착오가 생길 줄 알았는데 특별히 성공할 줄은 몰랐다.
    디프도 안 나오니까 이 단계에서 눈치채기 힘들죠?
    $ kubectl diff -f deployment.yaml
    $ 
    $ kubectl apply -f deployment.yaml
    deployment.apps/nginx-deployment configured
    
    diff가 없기 때문에 Resource의 환경 변수도 당연히 변화가 없다.
    (Deployment, ReplicaSet도 마찬가지이지만 중복되어 로그가 생략됨)
    $ kubectl get pods -o json | jq '.items[].spec.containers[].env'
    [
      {
        "name": "SAMPLE_ENV_VAR1",
        "value": "sample1"
      },
      {
        "name": "SAMPLE_ENV_VAR2",
        "value": "sample2"
      }
    ]
    

    3단계: 중복 제거


    중복된 SAMPLE_ENV_VAR2 을 삭제하려면 추가 시 나타나지 않는 diff가 발생합니다.
            env:
            - name: SAMPLE_ENV_VAR1
              value: sample1
            - name: SAMPLE_ENV_VAR2
              value: sample2
    -       - name: SAMPLE_ENV_VAR2
    -         value: sample2
    
    $ kubectl diff -f deployment.yaml
    diff -u -N /var/folders/wm/yhjlm5nd3vg1g_x433204z7c0000gp/T/LIVE-948535760/apps.v1.Deployment.default.nginx-deployment /var/folders/wm/yhjlm5nd3vg1g_x433204z7c0000gp/T/MERGED-3665042967/apps.v1.Deployment.default.nginx-deployment
    --- /var/folders/wm/yhjlm5nd3vg1g_x433204z7c0000gp/T/LIVE-948535760/apps.v1.Deployment.default.nginx-deployment 2022-03-16 00:25:43.000000000 +0900
    +++ /var/folders/wm/yhjlm5nd3vg1g_x433204z7c0000gp/T/MERGED-3665042967/apps.v1.Deployment.default.nginx-deployment      2022-03-16 00:25:43.000000000 +0900
    @@ -6,7 +6,7 @@
         kubectl.kubernetes.io/last-applied-configuration: |
           {"apiVersion":"apps/v1","kind":"Deployment","metadata":{"annotations":{},"labels":{"app":"nginx"},"name":"nginx-deployment","namespace":"default"},"spec":{"replicas":1,"selector":{"matchLabels":{"app":"nginx"}},"template":{"metadata":{"labels":{"app":"nginx"}},"spec":{"containers":[{"env":[{"name":"SAMPLE_ENV_VAR1","value":"sample1"},{"name":"SAMPLE_ENV_VAR2","value":"sample2"},{"name":"SAMPLE_ENV_VAR2","value":"sample2"}],"image":"nginx:latest","name":"nginx","ports":[{"containerPort":80}]}]}}}}
       creationTimestamp: "2022-03-15T15:18:24Z"
    -  generation: 2
    +  generation: 3
       labels:
         app: nginx
       managedFields:
    @@ -46,10 +46,6 @@
                         .: {}
                         f:name: {}
                         f:value: {}
    -                  k:{"name":"SAMPLE_ENV_VAR2"}:
    -                    .: {}
    -                    f:name: {}
    -                    f:value: {}
                     f:image: {}
                     f:imagePullPolicy: {}
                     f:name: {}
    @@ -130,8 +126,6 @@
           - env:
             - name: SAMPLE_ENV_VAR1
               value: sample1
    -        - name: SAMPLE_ENV_VAR2
    -          value: sample2
             image: nginx:latest
             imagePullPolicy: Always
             name: nginx
    
    "대략 움직일 것 같으니까 내주세요"이렇게 앱리하면...?
    $ kubectl apply -f deployment.yaml
    deployment.apps/nginx-deployment configured
    
    diff와 같이 무자비하게 SAMPLEENV_VAR2가 사라졌습니다.
    참고로 value의 값은 기존의 값과 같고 중복되지 않고 중복되지 않는다.
    $ kubectl get pods -o json | jq '.items[].spec.containers[].env'
    [
      {
        "name": "SAMPLE_ENV_VAR1",
        "value": "sample1"
      }
    ]
    

    Extra: 새로 만들 때 반복되는 동작


    이렇게 중복된 값으로 새로 만들면 경고가 발생하지만 바로 적용되며 특별한 오류가 발생하지 않고 Pod가 정상적으로 상승합니다.
            env:
            - name: SAMPLE_ENV_VAR1
              value: sample1
            - name: SAMPLE_ENV_VAR2
              value: sample2
            - name: SAMPLE_ENV_VAR2
              value: sample2-duplicated
    
    $ kubectl apply -f deployment.yaml
    Warning: spec.template.spec.containers[0].env[2].name: duplicate name "SAMPLE_ENV_VAR2"
    deployment.apps/nginx-deployment created
    $ 
    $ 
    $ kubectl get pods
    NAME                                READY   STATUS    RESTARTS   AGE
    nginx-deployment-66759f486b-4qrrr   1/1     Running   0          42s
    
    와 Pod의 설정은 configurd가 중복될 때와 달리 2개의 같은 값이 존재하지만 실제로Pod 내부에서 볼 수 있는 값은 Manifest 뒤에 쓴 값인 것 같다.
    $ kubectl get pods -o json | jq '.items[].spec.containers[].env'
    [
      {
        "name": "SAMPLE_ENV_VAR1",
        "value": "sample1"
      },
      {
        "name": "SAMPLE_ENV_VAR2",
        "value": "sample2"
      },
      {
        "name": "SAMPLE_ENV_VAR2",
        "value": "sample2-duplicated"
      }
    ]
    $ 
    $ 
    $ kubectl exec -it nginx-deployment-66759f486b-4qrrr -- env | grep SAMPLE
    SAMPLE_ENV_VAR2=sample2-duplicated
    SAMPLE_ENV_VAR1=sample1
    
    Manifest에서 교체SAMPLE_ENV_VAR2의 순서는 다음과 같다.
    $ kubectl exec -it nginx-deployment-74f47d648d-nb87h -- env | grep SAMPLE
    SAMPLE_ENV_VAR1=sample1
    SAMPLE_ENV_VAR2=sample2
    
    중요한 중복된 부분을 삭제할 때의 행동이지만 아까와 마찬가지로 이 환경 변수는 사라졌다.
    $ kubectl exec -it nginx-deployment-55569b7c8b-vdcvc -- env | grep SAMPLE
    SAMPLE_ENV_VAR1=sample1
    

    대응법


    이런 사태에 빠지지 않기 위해(예비)

  • 일부 Litter 도입
  • 하지만 존재하지 않을 수 있습니다.가볍게 구글에서 나온 kube-linter,kubeval은 경고를 보내지 않았다
  • CI/CD 흐름 어딘가에서 diff 검사
  • 환경 변수의 사라짐을 방지할 수 있으나 중복 발생을 방지할 수 없다
  • 워낙 인력이라 잘 못 타요
  • 출시 시간 증가
  • ConfigMap과kustoomize 등을 효과적으로 활용하기 위해 직접 편집하는 환경 변수
  • 를 줄인다

    실수로 어플리케이션이 환경 변수를 제거한 경우(소화)


    다시 한 번 같은 매니페스트 앱리에서 한다면 현재와의 차별을 메우기 위해 원래 상태로 돌아간다.
    당황하지 말고, 당황하지 말고, kubectl apply를 실행하거나, CI/CD 흐름을 다시 한 번 돌려서 다시 디자인하세요.

    중복 정의 발견 시(지뢰 처리)


    앞에서 말한 바와 같이 임의로 삭제하면 환경 변수가 부족한 팟이 개발돼 사고가 발생하기 때문에 약간의 회피 조치가 필요하다.
    갑자기 떠오르는 방법은 별명으로 Deployment을 만들고 서비스의 Label Selector를 잠시 전환해 업무를 놓치게 하고 그 사이 복구하는 것이다.
    (이번에는 검증되지 않았지만 kubectl edit로 뭔가를 할 수 있을 것 같다.)

    1. 중복 발견


    flowchart LR
        Service-->Dep1["Deployment"]-->RS1["ReplicaSet"]-->Po1["Pod"]-->Database

    2. 대피용 Deployment 제작

    flowchart LR
        Service-->Dep1["Deployment"]-->RS1["ReplicaSet"]-->Po1["Pod"]-->Database
        x["no Service"]-.-Dep2["Deployment(temp)"]-.-RS2["ReplicaSet(temp)"]-.-Po2["Pod(temp)"]-.-Database
        style x fill:#ccc

    3. 업무 전환

    $ kubectl diff -f service.yaml
    (略)
    @@ -47,7 +47,7 @@
         protocol: TCP
         targetPort: 80
       selector:
    -    app: nginx
    +    app: nginx-temp
       sessionAffinity: None
       type: ClusterIP
     status:
    
    flowchart LR
        x["no Service"]-.-Dep1["Deployment"]-.-RS1["ReplicaSet"]-.-Po1["Pod"]-.-Database
        Service-->Dep2["Deployment(temp)"]-->RS2["ReplicaSet(temp)"]-->Po2["Pod(temp)"]-->Database
        style x fill:#ccc

    4. 복구

    flowchart LR
        x["no Service"]-.-Dep1["Deployment(fixed)"]-.-RS1["ReplicaSet(fixed)"]-.-Po1["Pod(fixed)"]-.-Database
        Service-->Dep2["Deployment(temp)"]-->RS2["ReplicaSet(temp)"]-->Po2["Pod(temp)"]-->Database
        style x fill:#ccc

    4. 업무 리베이트

    $ kubectl diff -f service.yaml
    (略)
    @@ -47,7 +47,7 @@
         protocol: TCP
         targetPort: 80
       selector:
    +    app: nginx
    -    app: nginx-temp
       sessionAffinity: None
       type: ClusterIP
     status:
    
    flowchart LR
    Service-->Dep1["Deployment(fixed)"]-->RS1["ReplicaSet(fixed)"]-->Po1["Pod(fixed)"]-->Database
    x["no Service"]-.-Dep2["Deployment(temp)"]-.-RS2["ReplicaSet(temp)"]-.-Po2["Pod(temp)"]-.-Database
    style x fill:#ccc

    5. 회피용 Deployment 삭제


    flowchart LR
    Service-->Dep1["Deployment(fixed)"]-->RS1["ReplicaSet(fixed)"]-->Po1["Pod(fixed)"]-->Database

    고찰하다.

  • env: KeyValue가 아닌 정렬이기 때문에 데이터 구조상 중복 정의가 가능한 것도 어쩔 수 없습니다.
  • .items[].spec.template.spec.containers[].env[]
  • Kubbernetes 측에서 검증 및 중복 금지 가능
  • 왜 안 했는지 (안 하는 것의 좋은 점) 생각이 안 나.
  • 나도 이슈와 슬랙을 찾아봤는데 비슷한 토론을 발견하지 못했나?
  • 총결산

  • 환경 변수의 중복을 없애려는 시도를 소홀히 하면 사고가 발생할 수 있으니 주의하십시오
  • 중복된 경우(value에 오류가 없으면) 이것만으로는 나쁜 영향이 없으니 신중하게 복구하세요
  • 좋은 웹페이지 즐겨찾기