24년 11월 이전/데브옵스(DevOps)를 위한 쿠버네티스 마스터

11. 애플리케이션 스케줄링과 라이프사이클 관리 (1)

Gurumee 2021. 8. 17. 07:09
반응형

이 문서는 인프런 강의 "데브옵스를 위한 쿠버네티스 마스터"을 듣고 작성되었습니다. 최대한 요약해서 강의 내용을 최소로 하는데 목표를 두고 있어서, 더 친절하고 정확한 내용을 원하신다면 강의를 구매하시는 것을 추천드립니다. => 강의 링크

컨테이너 환경 변수 전달 (1) YAML

k8s에서 Pod에 실행되는 컨테이너에 환경 변수를 전달하는 방법은 크게 3가지가 있다.

  1. Yaml 파일에 직접 명시 "spec.containers[*].env"
  2. Secret
  3. ConfigMap

이 절에서는 이 중 첫 번째 Yaml로 전달하는 방식을 알아본다. env-yaml.yaml을 다음과 같이 생성한다.

 

src/ch11/k8s/env-yaml.yaml

apiVersion: v1
kind: Pod
metadata:
  name: env-yaml-mysql
spec:
  containers:
    - image: mysql:5.6
      name: mysql
      env:
      - name: MYSQL_ROOT_PASSWORD
        value: root

 

spec.containers[*].env 형식으로 키 이름과 값을 전달한 것을 확인할 수 있다. 이제 터미널에 다음을 입려하여 리소스를 생성한다.

$ kubectl create -f env-yaml.yaml 
pod/env-yaml-mysql created

$ kubectl get pod -w
NAME             READY   STATUS    RESTARTS   AGE
env-yaml-mysql   1/1     Running   0          4s

 

이제 mysql 컨테이너에 다음 명령어를 입력하여, 로그인해보자. 비밀번호는 "root"이다.

$ kubectl exec -it env-yaml-mysql -- mysql -u root -p
Enter password: # root 입력

 

성공적으로 로그인되었다면 다음 글을 확인할 수 있다.

Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 3
Server version: 5.6.51 MySQL Community Server (GPL)

Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql>

컨테이너 환경 변수 전달 (2) Secret

k8s에서 Pod에 실행되는 컨테이너에 환경 변수를 전달하는 방법은 크게 3가지가 있다.

  1. Yaml 파일에 직접 명시 "spec.containers[*].env"
  2. Secret
  3. ConfigMap

이 절에서는 이 중 두 번째 Secret 리소스를 생성하여 값을 전달하는 방식을 알아본다. env-secret.yaml을 다음과 같이 생성한다.

 

src/ch11/k8s/env-secret.yaml

apiVersion: v1
kind: Secret
metadata:
  name: env-secret
type: Opaque
data:
  MYSQL_ROOT_PASSWORD: cm9vdA==
---
apiVersion: v1
kind: Pod
metadata:
  name: env-secret-mysql
spec:
  containers:
    - image: mysql:5.6
      name: mysql
      env:
        - name: MYSQL_ROOT_PASSWORD
          valueFrom:
            secretKeyRef:
              name: env-secret
              key: MYSQL_ROOT_PASSWORD

 

먼저 Secret 리소스를 생성하는 부분을 살펴보자.

apiVersion: v1
kind: Secret
metadata:
  name: env-secret
type: Opaque
data:
  MYSQL_ROOT_PASSWORD: cm9vdA== # root를 인코딩한 값

 

타 리소스 생성할 때와 별로 다를게 없다. 같은 네임스페이스 안의 다른 리소스들이 참조할 수 있도록 metadata.name을 지정해준다. 그 후 data에서 해당 키 이름과 원하는 값을 인코딩한 값을 설정해주면 된다. 리눅스 계열이라면 다음과 같이 쉽게 인코딩할 수 있다.

# echo -n <원하는 값>|base64
$ echo -n root|base64
cm9vdA==

참고! data, stringData
"Secret"에서는 인코딩해서 data 필드에 키-값을 할당하는 것이 일반적이다. 하지만 그냥 평문을 전달하고 싶다면 stringData 필드를 쓰면 된다.

 

Secret 리소스를 지정했으면 Pod 내부의 Container들이 이용하게 하려면 spec.containers[*].env[*].valueFrom.secretKeyRef로 지정하면 된다.

apiVersion: v1
kind: Pod
metadata:
  name: env-secret-mysql
spec:
  containers:
    - image: mysql:5.6
      name: mysql
      env:
        - name: MYSQL_ROOT_PASSWORD
          valueFrom:
            secretKeyRef:
              name: env-secret          # 참조하는 Secret 이름
              key: MYSQL_ROOT_PASSWORD  # Secret 내부의 키

 

이제 다음 명령어를 통해서 리소스를 생성한다.

$ kubectl create -f env-secret.yaml 
secret/env-secret created
pod/env-secret-mysql created

$ kubectl get pod -w
NAME               READY   STATUS    RESTARTS   AGE
env-secret-mysql   1/1     Running   0          4s

 

리소스가 생성되었다면 생성된 Secret을 다음 명령어로 확인해보자.

$ kubectl get secret
NAME                  TYPE                                  DATA   AGE
default-token-xs7mw   kubernetes.io/service-account-token   3      4d22h
env-secret            Opaque                                1      89s

$ kubectl describe secret env-secret
Name:         env-secret
Namespace:    default
Labels:       <none>
Annotations:  <none>

Type:  Opaque

Data
====
MYSQL_ROOT_PASSWORD:  4 bytes

 

이제 mysql 컨테이너에 다음 명령어를 입력하여, 로그인해보자. 비밀번호는 "root"이다.

$ kubectl exec -it env-secret-mysql -- mysql -u root -p
Enter password: # root 입력

 

성공적으로 로그인되었다면 다음 글을 확인할 수 있다.

Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 3
Server version: 5.6.51 MySQL Community Server (GPL)

Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql>

컨테이너 환경 변수 전달 (3) ConfigMap

k8s에서 Pod에 실행되는 컨테이너에 환경 변수를 전달하는 방법은 크게 3가지가 있다.

  1. Yaml 파일에 직접 명시 "spec.containers[*].env"
  2. Secret
  3. ConfigMap

이 절에서는 이 중 세 번째 ConfigMap 리소스를 생성하여 값을 전달하는 방식을 알아본다. env-configmap.yaml을 다음과 같이 생성한다.

 

src/ch11/k8s/env-configmap-was.yaml

apiVersion: v1
kind: Service
metadata:
  name: env-configmap-was
spec:
  ports:
    - port: 8080
      targetPort: 8080
  selector:
    name: env-configmap-was
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: env-configmap-was
spec:
  selector:
    matchLabels:
      name: env-configmap-was
  replicas: 3
  template:
    metadata:
      labels:
        name: env-configmap-was
    spec:
      containers:
        - name: env-configmap-was
          image: gcr.io/google-samples/node-hello:1.0
          ports:
            - containerPort: 8080
              protocol: TCP
          env:
            - name: DEMO_GREETING
              valueFrom:
                configMapKeyRef:
                  name: env-configmap-was
                  key: DEMO_GREETING
            - name: DEMO_FAREWELL
              valueFrom:
                configMapKeyRef:
                  name: env-configmap-was
                  key: DEMO_FAREWELL
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: env-configmap-was
data:
  DEMO_GREETING: "Hello from the environment with ConfigMap"
  DEMO_FAREWELL: "Hi Firewall"

 

ConfigMap은 다음과 같이 쉽게 생성할 수 있다.

apiVersion: v1
kind: ConfigMap
metadata:
  name: env-configmap-was
data:
  DEMO_GREETING: "Hello from the environment with ConfigMap"
  DEMO_FAREWELL: "Hi Firewall"

 

Secret과 거의 유사한데, Secret과 달리 평문으로 전달되는게 ConfigMap이라고 생각하면 된다. 이렇게 생성된 키-값 쌍은 다음과 같이 불러서 사용하면 된다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: env-configmap-was
spec:
  # ...
  template:
    # ...
    spec:
      containers:
        - name: env-configmap-was
          image: gcr.io/google-samples/node-hello:1.0
          ports:
            - containerPort: 8080
              protocol: TCP
          env:
          - name: DEMO_GREETING
            valueFrom:
              configMapKeyRef:
                name: env-configmap-was
                key: DEMO_GREETING
          - name: DEMO_FAREWELL
            valueFrom:
              configMapKeyRef:
                name: env-configmap-was
                key: DEMO_FAREWELL

 

Secret의 값을 불러 올 때 secretKeyRef으로 불러온 것처럼 ConfigMap의 값을 불러 올 때는 configMapKeyRef를 사용하면 된다. 이제 리소스를 생성해보자.

$ kubectl create -f env-configmap-was.yaml
service/env-configmap-was created
deployment.apps/env-configmap-was created
configmap/env-configmap-was created

$  kubectl get pod -w
NAME                                 READY   STATUS              RESTARTS   AGE
env-configmap-was-77b58fc975-k6s79   0/1     ContainerCreating   0          6s
env-configmap-was-77b58fc975-nvtjr   0/1     ContainerCreating   0          6s
env-configmap-was-77b58fc975-rngql   0/1     ContainerCreating   0          6s
env-configmap-was-77b58fc975-k6s79   1/1     Running             0          31s
env-configmap-was-77b58fc975-nvtjr   1/1     Running             0          32s
env-configmap-was-77b58fc975-rngql   1/1     Running             0          32s 

 

Pod이 모두 생성되면 적당한 것을 골라서 다음 명령어를 입력해보자.

# 환경 변수 초기화 확인
$ kubectl exec -it env-configmap-was-77b58fc975-nvtjr -- printenv | grep "DEMO"
DEMO_GREETING=Hello from the environment with ConfigMap
DEMO_FAREWELL=Hi Firewall

# 서비스 연결 테스트
$  kubectl exec -it env-configmap-was-77b58fc975-nvtjr -- curl env-configmap-was:8080
Hello Kubernetes!

 

우리가 전달한 환경 변수가 잘 전달된 것을 확인할 수 있다. ConfigMap은 또한 볼륨으로써 Pod의 컨테이너들에게 부착될 수 있다. 예를 들어서 설정 파일 같은 것을 ConfigMap으로 만들어서 컨테이너의 볼륨에 올릴 수 있다. 다음과 같이 env-configmap-web.yaml을 생성한다.

 

src/ch11/k8s/env-configmap-web.yaml

apiVersion: v1
kind: Service
metadata:
  name: env-configmap-web
spec:
  ports:
    - port: 80
      targetPort: 80
  selector:
    name: env-configmap-web
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: env-configmap-web
spec:
  selector:
    matchLabels:
      name: env-configmap-web
  replicas: 3
  template:
    metadata:
      labels:
        name: env-configmap-web
    spec:
      containers:
        - name: env-configmap-web
          image: nginx
          ports:
            - containerPort: 80
              protocol: TCP
          volumeMounts:
            - name: nginx
              subPath: nginx.conf
              mountPath: /etc/nginx/nginx.conf
              readOnly: true
      volumes:
        - name: nginx
          configMap:
            name: env-configmap-web
            items:
              - key: nginx.conf
                path: nginx.conf
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: env-configmap-web
data:
  nginx.conf: |
    error_log  /var/log/nginx/error.log warn;
    pid        /tmp/nginx.pid;
    worker_rlimit_nofile 8192;

    events {
        worker_connections  4096;  ## Default: 1024
    }

    http {
        default_type  application/octet-stream;
        log_format  main  '$remote_addr - $remote_user [$time_local] '
                          '"$request" $status $body_bytes_sent '
                          '"$http_referer" "$http_user_agent" "$request_time"';

        access_log  /var/log/nginx/access.log  main;
        sendfile        on;
        tcp_nopush     on;

        server {
            listen 80;

            location  / {
                proxy_pass       http://env-configmap-was:8080;
            }
        }
    }

 

ConfigMap.data 부분에서 "|" 밑으로 설정 파일을 만들었다. 이를 설정 파일로 취급하여 볼륨으로 마운트할 수 있다.

kind: Deployment
metadata:
  name: env-configmap-web
spec:
  # ...
    spec:
      containers:
        - name: env-configmap-web
          # ...
          volumeMounts:
            - name: nginx
              subPath: nginx.conf # items[*].path와 같아야 한다.
              mountPath: /etc/nginx/nginx.conf
              readOnly: true
      volumes:
        - name: nginx
          configMap:
            name: env-configmap-web # ConfigMap 이름
            items:
              - key: nginx.conf     # ConfigMap에서 가져올 key와 같아야 한다.
                path: nginx.conf

 

이제 이 리소스들을 생성해보자.

$ kubectl create -f env-configmap-web.yaml
service/env-configmap-web created
deployment.apps/env-configmap-web created
configmap/env-configmap-web created

$ kubectl get pod -w
NAME                                 READY   STATUS    RESTARTS   AGE
...
env-configmap-web-79fdfc5c8b-dr45r   1/1     Running   0          19s
env-configmap-web-79fdfc5c8b-fbqkf   1/1     Running   0          19s
env-configmap-web-79fdfc5c8b-wqx5z   1/1     Running   0          19s

 

그 후 하나의 Pod을 골라 /etc/nginx/nginx.confConfigMap이 마운트 되었는지 확인해보자.

$ kubectl exec -it env-configmap-web-79fdfc5c8b-wqx5z -- cat /etc/nginx/nginx.conf
error_log  /var/log/nginx/error.log warn;
pid        /tmp/nginx.pid;
worker_rlimit_nofile 8192;

events {
    worker_connections  4096;  ## Default: 1024
}

http {
    default_type  application/octet-stream;
    log_format  main  '$remote_addr - $remote_user [$time_local] '
                      '"$request" $status $body_bytes_sent '
                      '"$http_referer" "$http_user_agent" "$request_time"';

    access_log  /var/log/nginx/access.log  main;
    sendfile        on;
    tcp_nopush     on;

    server {
        listen 80;

        location  / {
            proxy_pass       http://env-configmap-was:8080;
        }
    }
}

 

잘 연결되었다면, Serviceenv-configmap-web을 호출하면 upstream 설정을 통해서 env-configmap-was:8080에게 전달되어서 다음 메세지가 보이게 될 것이다.

$ kubectl exec -it env-configmap-web-79fdfc5c8b-wqx5z -- curl env-configmap-web
Hello Kubernetes!

한 개의 Pod, 여러 개의 컨테이너

이전에 언급했듯이 한 개의 Pod에 여러 컨테이너를 실행하게 할 수 있다. 권장되는 구조는 아니며 보통 해당 컨테이너의 리소스를 모니터링하기 위해서 이런 구조를 취한다.

 

이를 테면, NGINX의 액세스 로그를 파싱하여 상태 코드, 응답 시간 등에 대한 지표를 제공해야 할 때가 있다. 이 때 nginx 컨테이너와 함께 액세스 로그를 저장할 볼륨과 이 볼륨을 파싱해서 메트릭을 노출시키는 prometheus-nginxlog-exporter를 "SideCar" 형태로 제공하면 훌륭하게 이를 모니터링할 수 있다.

 

다음과 같이 one-pod-multi-container.yaml을 생성한다.

 

src/ch11/k8s/one-pod-multi-container.yaml

apiVersion: v1
kind: Pod
metadata:
  name: one-pod-multi-container
spec:
  containers:
    - name: nginx
      image: nginx
      ports:
        - containerPort: 80
          protocol: TCP
      volumeMounts:
        - mountPath: /var/log/nginx/
          name: accesslog
    - name: prometheus-nginxlog-exporter
      image: quay.io/martinhelmich/prometheus-nginxlog-exporter
      ports:
        - containerPort: 4040
          protocol: TCP
      volumeMounts:
        - mountPath: /var/log/nginx/
          name: accesslog
          readOnly: true
        - mountPath: /etc/prometheus-nginxlog-exporter.yml
          name: exporter
          subPath: prometheus-nginxlog-exporter.yaml
          readOnly: true
      command: ["/prometheus-nginxlog-exporter", "-config-file", "/etc/prometheus-nginxlog-exporter.yml"]
  volumes:
    - name: accesslog
      emptyDir: {}
    - name: exporter
      configMap:
        name: one-pod-multi-container
        items:
          - key: prometheus-nginxlog-exporter.yaml
            path: prometheus-nginxlog-exporter.yaml
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: one-pod-multi-container
data:
  prometheus-nginxlog-exporter.yaml: |
    listen:
      port: 4040
      metrics_endpoint: "/metrics"

    consul:
      enable: false

    namespaces:
      - name: nginx
        format: "$remote_addr - $remote_user [$time_local] \"$request\" $status $body_bytes_sent \"$http_referer\" \"$http_user_agent\" \"$request_time\""
        source:
          files:
            - /var/log/nginx/access.log
        only_count: true
        relabel_configs:
        - target_label: request_uri
          from: request
          split: 2
          separator: ' '

 

위에처럼 1개의 Pod의 여러 개의 Container를 설정할 수 있다.

apiVersion: v1
kind: Pod
metadata:
  name: one-pod-multi-container
spec:
  containers:
    - name: nginx
      # ...
    - name: prometheus-nginxlog-exporter
      # ...

 

nginxaccesslog라는 볼륨과 prometheus-nginxlog-exporteraccesslog, exporter라는 볼륨과 연결된다. exporterConfigMap과 연결된 설정 파일이다. 결과적으로 이 2개의 컨테이너는 accesslog라는 볼륨을 공유하게 된다.

 

이제 다음 명령어를 이용하여 리소스를 생성해보자.

$  kubectl create -f one-pod-multi-container.yaml
pod/one-pod-multi-container created
configmap/one-pod-multi-container created

$ kubectl get pod
# READY가 2/2가 되야 실행이 된것이다.
NAME                      READY   STATUS    RESTARTS   AGE
one-pod-multi-container   2/2     Running   0          16s

 

그 후 터미널에 다음을 입력해보자.

$ kubectl exec -it one-pod-multi-container -- curl localhost:4040/metrics
Defaulted container "nginx" out of: nginx, prometheus-nginxlog-exporter
# HELP nginx_parse_errors_total Total number of log file lines that could not be parsed
# TYPE nginx_parse_errors_total counter
nginx_parse_errors_total 0

 

원래 nginx 컨테이너는 4040번 포트에 아무것도 연결되지 않기 때문에 "connection refused" 에러가 떠야 정상이지만 현재 Pod에는 prometheus-nginxlog-exporter라는 컨테이너가 같이 실행되고 있으므로 에러는 뜨지 않는다. 현재 발생된 로그가 없기 때문에 메트릭은 수집된 것이 없다. 메트릭 수집을 위해 다음을 입력해보자.

$ for i in {1..5}; do kubectl exec -it one-pod-multi-container -- curl localhost; done

 

이렇게 하면 nginx를 총 5번 호출하는 것과 같다. 먼저 볼륨에 로그 파일이 생성되었는지 확인해보자.

$ kubectl exec -it one-pod-multi-container -- cat /var/log/nginx/access.log
Defaulted container "nginx" out of: nginx, prometheus-nginxlog-exporter
::1 - - [16/Aug/2021:11:22:17 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.64.0" "-"
::1 - - [16/Aug/2021:11:22:17 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.64.0" "-"
::1 - - [16/Aug/2021:11:22:18 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.64.0" "-"
::1 - - [16/Aug/2021:11:22:18 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.64.0" "-"
::1 - - [16/Aug/2021:11:22:19 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.64.0" "-"

 

액세스 로그가 생성되었다. prometheus-nginxlog-exporter가 이를 파싱해서 메트릭을 토축히키는지 확인해보자.

$ kubectl exec -it one-pod-multi-container -- curl localhost:4040/metrics
Defaulted container "nginx" out of: nginx, prometheus-nginxlog-exporter
# HELP nginx_http_response_count_total Amount of processed HTTP requests
# TYPE nginx_http_response_count_total counter
nginx_http_response_count_total{method="GET",request_uri="/",status="200"} 5
# HELP nginx_http_response_size_bytes Total amount of transferred bytes
# TYPE nginx_http_response_size_bytes counter
nginx_http_response_size_bytes{method="GET",request_uri="/",status="200"} 3060
# HELP nginx_parse_errors_total Total number of log file lines that could not be parsed
# TYPE nginx_parse_errors_total counter
nginx_parse_errors_total 0

이전과 다른 출력문을 확인할 수 있으며 nginx_http_response_count_total가 5개 수집된 것을 알 수 있다.

초기 명령어 및 아규먼트 전달

Docker에서 실행 시 초기 명령어 및 아규먼트 전달이 가능했다. 이미 지난 절을 진행하면서 Podcommand를 주어 초기 명령어 실행을 전달하는 것을 확인할 수 있었다.

apiVersion: v1
kind: Pod
metadata:
  name: one-pod-multi-container
spec:
  containers:
    - name: nginx
      # ...
    - name: prometheus-nginxlog-exporter
      # ...
      command: ["/prometheus-nginxlog-exporter", "-config-file", "/etc/prometheus-nginxlog-exporter.yml"]

 

prometheus-nginxlog-exporter 포드는 실행 시 다음 명령어가 실행된다.

$ /prometheus-nginxlog-exporter -config-file /etc/prometheus-nginxlog-exporter.yml

 

단순 명령어만 실행하는 것도 가능하지만 args를 이용해 아규먼트 전달해서 명령어를 실행하는 것도 가능하다. command-args.yaml을 다음과 같이 만들어보자.

 

src/ch11/k8s/command-args.yaml

apiVersion: v1
kind: Pod
metadata:
  name: command-args
spec:
  containers:
    - name: command-args
      image: debian
      command: ["printenv"]
      args: ["HOSTNAME", "KUBERNETES_PORT"]
  restartPolicy: OnFailure

 

요령은 간단하다. spec.containers[*] 블록 밑에 commandargs를 정의해주면 된다. 위의 Pod은 생성되면 다음 명령어를 실행한다고 보면 된다.

$ printenv HOSTNAME KUBERNETES_PORT

 

이제 리소스를 생성해보자.

$ kubectl create -f command-args.yaml
pod/command-args created

$  kubectl get pod -w
NAME           READY   STATUS              RESTARTS   AGE
command-args   0/1     ContainerCreating   0          5s
command-args   0/1     Completed           0          7s

 

debian 이미지는 리눅스 그 자체이기 때문에 컨테이너를 유지시키는 명령어 없이는 Pod가 유지되지 않는다. 계획대로면 단순히 명령어만 실행하고 종료되기 때문에 "Completed" 상태가 됐다면 컨테이너가 정상적으로 종료된 것으로 보면 된다. 이제 한 번 로그를 확인해보자.

$ kubectl logs command-args
command-args         # HOSTNAME
tcp://10.24.0.1:443  # KUBERNETES_PORT

 

이런 비슷한 모양이 나왔다면 성공이다.

init 컨테이너

k8s에서는 InitContainer라는 특별한 목적을 지닌 컨테이너를 실행할 수 있다. 이 녀석은 Pod의 앱 컨테이너들이 실행되기 전에 실행되는 특수한 컨테이너이며, 앱 이미지에는 없는 유틸리티 또는 설정 스크립트 등을 포함할 수 있다. 한 번 해보자. init-container.yaml을 다음과 같이 만든다.

 

src/ch11/k8s/init-container.yaml

apiVersion: v1
kind: Service
metadata:
  name: init-container
spec:
  ports:
    - port: 80
      targetPort: 80
  selector:
    name: init-container
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: init-container
spec:
  selector:
    matchLabels:
      name: init-container
  replicas: 3
  template:
    metadata:
      labels:
        name: init-container
    spec:
      initContainers:
        - name: init-container-busybox
          image: busybox:1.28
          command: [ 'sh', '-c', "until nslookup env-configmap-was; do echo waiting for env-configmap-was; sleep 2; done" ]
      containers:
        - name: init-container
          image: nginx
          ports:
            - containerPort: 80
              protocol: TCP
          volumeMounts:
            - name: nginx
              subPath: nginx.conf
              mountPath: /etc/nginx/nginx.conf
              readOnly: true
      volumes:
        - name: nginx
          configMap:
            name: init-container
            items:
              - key: nginx.conf
                path: nginx.conf
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: init-container
data:
  nginx.conf: |
    error_log  /var/log/nginx/error.log warn;
    pid        /tmp/nginx.pid;
    worker_rlimit_nofile 8192;

    events {
        worker_connections  4096;  ## Default: 1024
    }

    http {
        default_type  application/octet-stream;
        log_format  main  '$remote_addr - $remote_user [$time_local] '
                          '"$request" $status $body_bytes_sent '
                          '"$http_referer" "$http_user_agent" "$request_time"';

        access_log  /var/log/nginx/access.log  main;
        sendfile        on;
        tcp_nopush     on;

        server {
            listen 80;

            location  / {
                proxy_pass       http://env-configmap-was:8080;
            }
        }
    }

 

Init ContainerPodspec.initContainers에서 설정한다. 작성 요령은 spec.containers와 동일하다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: init-container
spec:
  # ReplicaSet
    # POD
    spec:
      initContainers:
        - name: init-container-busybox
          image: busybox:1.28
          command: [ 'sh', '-c', "until nslookup env-configmap-was; do echo waiting for env-configmap-was; sleep 2; done" ]
      containers:
      # ...

 

위 리소스는 env-configmap-web이랑 동일한 리소스를 생성한다. 거기에 Init Container를 이용하여 env-configmap-was 서비스가 실행되기 전까지 Pod 생성을 기다리는 부분을 추가했다. 이제 리소스를 생성해보자.

$ kubectl create -f init-container.yaml
service/init-container created
deployment.apps/init-container created
configmap/init-container created

$ kubectl get pod -w
NAME                              READY   STATUS     RESTARTS   AGE
init-container-6646dd9cc8-njpzk   0/1     Init:0/1   0          26s
init-container-6646dd9cc8-p4zmf   0/1     Init:0/1   0          26s
init-container-6646dd9cc8-s62f7   0/1     Init:0/1   0          26s

 

위 처럼 STATUS가 "Init:0/1" 상태로 유지되는 것을 확인할 수 있다. env-configmap-was라는 서비스가 존재하지 않기 때문이다.(매 2초마다 nslookup 명령어로 서비스를 찾는다.) 해당 서비스를 생성해보자.

$ kubectl create -f env-configmap-was.yaml
service/env-configmap-was created
deployment.apps/env-configmap-was created
configmap/env-configmap-was created

 

다시 한 번 Pod의 상태를 확인한다.

$ kubectl get pod -w
NAME                                READY   STATUS    RESTARTS   AGE
...
# 상태가 "RUNNING"으로 변경됨
init-container-6646dd9cc8-njpzk     1/1     Running   0          89s
init-container-6646dd9cc8-p4zmf     1/1     Running   0          89s
init-container-6646dd9cc8-s62f7     1/1     Running   0          89s

 

이제 init-container 포드들이 실행되는 것을 확인할 수 있다.

728x90
반응형