ETC

[CKA] Kubernetes init & infra & static container

코린이s 2023. 8. 24. 20:19
728x90

pod 안에 init container, main container 를 같이 두고 반드시 실행 되어야 하는 컨테이너는 init 컨테이너로 지정하여 init 컨테이너가 정상일때 main container가 실행 되도록 해야할때 사용한다.

init 컨테이너는 아래와 같이 spec.initContainers 에 컨테이너를 지정한다.

apiVersion: v1
kind: Pod
metadata:
  name: myapp
  labels:
    app: myapp
spec:
  containers:
  - name: main-app
    image:
  initContainers:
  - name: init-app
    image:

쿠버네티스 홈페이지에 예시가 있어 해당 예시로 실습을 진행해보도록 하자!

https://kubernetes.io/docs/concepts/workloads/pods/init-containers/

 

Init Containers

This page provides an overview of init containers: specialized containers that run before app containers in a Pod. Init containers can contain utilities or setup scripts not present in an app image. You can specify init containers in the Pod specification

kubernetes.io

아래와 같은 test.yaml 을 만든다.

# test.yaml

apiVersion: v1
kind: Pod
metadata:
  name: myapp-pod
  labels:
    app.kubernetes.io/name: MyApp
spec:
  containers:
  - name: myapp-container
    image: busybox:1.28
    command: ['sh', '-c', 'echo The app is running! && sleep 3600']
  initContainers:
  - name: init-myservice
    image: busybox:1.28
    command: ['sh', '-c', "until nslookup myservice.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo waiting for myservice; sleep 2; done"]
  - name: init-mydb
    image: busybox:1.28
    command: ['sh', '-c', "until nslookup mydb.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo waiting for mydb; sleep 2; done"]

init 컨테이너 보면 until 이라는것은 ~까지 라는 뜻으로 그 뒤에 나오는 조건이 맞을때 까지 do-done 안에 구문을 반복한다는 뜻이다.

오브젝트가 서비스인 파드를 띄우면 해당 서비스는 Core-DNS를 통해 DNS가 부여되고 해당 DNS를 통해 통신한다.

이를 이용하여 오브젝트가 Service인 myservice를 띄우면 관련 DNS가 myservice.[namespace].svc.cluster.local로 셋팅 되므로 nslookup DNS 를 통해 해당 DNS가 생성 되었는지 확인하여 해당 서비스를 띄울때까지 init 컨테이너는 시작 완료 되지 않도록 한다.

이제 해당 파드를 띄워보면 아래와 같이 STATUS 에 Init 컨테이너가 2개 인데 현재 까지 띄워지지 않음을 알 수 있다.

[node1 ~]$kubectl create -f test.yaml 
pod/myapp-pod created
[node1 ~]$ kubectl get pods
NAME        READY   STATUS     RESTARTS   AGE
myapp-pod   0/1     Init:0/2   0          6s

이제 myservice 관련 yaml 파일을 생성하고

# myservice.yaml

apiVersion: v1
kind: Service
metadata:
  name: myservice
spec:
  ports:
  - protocol: TCP
    port: 80
    targetPort: 9376

해당 yaml 파일을 통해 서비스를 띄운다.

[node1 ~]$ kubectl create -f myservice.yaml 
service/myservice created

이제 상태 정보를 다시 보면 Init 컨테이너 하나가 성공 되었음을 알 수 있다.

[node1 ~]$ kubectl get pods
NAME        READY   STATUS     RESTARTS   AGE
myapp-pod   0/1     Init:1/2   0          2m26s

이제 mydb 까지 띄워 보자

# mydb.yaml

apiVersion: v1
kind: Service
metadata:
  name: mydb
spec:
  ports:
  - protocol: TCP
    port: 80
    targetPort: 9377
[node1 ~]$ kubectl create -f mydb.yaml 
service/mydb created

파드 상태를 보면 PodInitializing 으로 변경 된것을 볼 수 있다.

[node1 ~]$ kubectl get pods
NAME        READY   STATUS            RESTARTS   AGE
myapp-pod   0/1     PodInitializing   0          3m48s

조금 더 기다려보면 메인 컨테이너가 Running 상태임을 볼 수 있다.

[node1 ~]$ kubectl get pods
NAME        READY   STATUS    RESTARTS   AGE
myapp-pod   1/1     Running   0          5m16s

이제 infra container을 알아보자!
우리가 파드를 만들면 기본으로 pause 컨테이너가 생성되며 파드에 대한 IP Hostname을 생성/관리하는 컨테이너이다.

우선 우리가 사용하고 있는 컨테이너 런타임을 알아야 하는데 

아래와 같이 노드 정보를 조회하면 container runtime 을 확인할 수 있으며 docker가 아닌 containerd이다. 

[node1 ~]$ kubectl get nodes -o wide
NAME    STATUS   ROLES           AGE   VERSION   INTERNAL-IP    EXTERNAL-IP   OS-IMAGE                KERNEL-VERSION      CONTAINER-RUNTIME
node1   Ready    control-plane   50m   v1.27.2   192.168.0.18   <none>        CentOS Linux 7 (Core)   4.4.0-210-generic   containerd://1.6.21
node2   Ready    <none>          50m   v1.27.2   192.168.0.17   <none>        CentOS Linux 7 (Core)   4.4.0-210-generic   containerd://1.6.21

node2에 가서 컨테이너 런타임을 통해 떠있는 프로세스를 확인 하기 위해서는 아래와 같이 입력한다.

# docker 

$ docker ps

# containerd

[node3 ~]$ ctr -namespace k8s.io -address /var/run/docker/containerd/containerd.sock containers ls
CONTAINER                                                           IMAGE                                           RUNTIME                  
213fca27006914841f104ef6f9daa3cb24442b81e3dc54bfef61f5d0193e5a57    docker.io/cloudnativelabs/kube-router:latest    io.containerd.runc.v2    
747865a326c5945d458e515c36f46b152bd6dcad6f26ff8f10284fa70a18cc96    registry.k8s.io/pause:3.6                       io.containerd.runc.v2    
9bffb4d6108bd330e3a788a3a624009d2fee71f4c1cea4dd17bd285b3996a3bb    registry.k8s.io/pause:3.6                       io.containerd.runc.v2    
b815da08a2ee94b670b94704454c2f657672bc8bab897581986548496c6f3fbe    docker.io/cloudnativelabs/kube-router:latest    io.containerd.runc.v2    
e6285095bdbf5be320983fa63c7ed43febfe9b26c124ef3d44b5612983fe9aff    registry.k8s.io/kube-proxy:v1.27.5              io.containerd.runc.v2

우선 기본 필요에 의해 설치된 파드가 있어 pause 컨테이너가 2개 떠있는것을 확인 하였다.

이제  nginx 파드를 생성한뒤 pause 컨테이너가 추가로 뜨는지 확인해보자!

마스터 노드에서 nginx 파드를 띄우며

[node1 ~]$ kubectl run my-nginx --image=nginx --port=90
pod/my-nginx created

우리가 생성한 nginx의 경우 node2에 생성 되었으며 

[node1 ~]$ kubectl get pod -o wide
NAME        READY   STATUS    RESTARTS   AGE   IP         NODE    NOMINATED NODE   READINESS GATES
my-nginx    1/1     Running   0          76s   10.5.1.3   node2   <none>           <none>

 

해당 파드가 떠있는 node2에 접속해서 containerd 런타임을 통해 컨테이너를 조회하면 pause 컨테이너가 하나더 추가되어 3개가 떠있음을 알 수 있으며 nginx 컨테이너도 추가 된것을 확인 할 수 있다.

[node2 ~]$ ctr -namespace k8s.io -address /var/run/docker/containerd/containerd.sock containers ls
CONTAINER                                                           IMAGE                                           RUNTIME                  
213fca27006914841f104ef6f9daa3cb24442b81e3dc54bfef61f5d0193e5a57    docker.io/cloudnativelabs/kube-router:latest    io.containerd.runc.v2    
5a94cabe9b5826cc2e021c919cbd34603e02bc2bccfc52857eab23beb8a0e833    registry.k8s.io/pause:3.6                       io.containerd.runc.v2    
747865a326c5945d458e515c36f46b152bd6dcad6f26ff8f10284fa70a18cc96    registry.k8s.io/pause:3.6                       io.containerd.runc.v2    
9bffb4d6108bd330e3a788a3a624009d2fee71f4c1cea4dd17bd285b3996a3bb    registry.k8s.io/pause:3.6                       io.containerd.runc.v2    
b815da08a2ee94b670b94704454c2f657672bc8bab897581986548496c6f3fbe    docker.io/cloudnativelabs/kube-router:latest    io.containerd.runc.v2    
b9207ac11abf5140d8248c163e46878d3b129a57fc3d5ee97e0d14033569a47d    docker.io/library/nginx:latest                  io.containerd.runc.v2    
e6285095bdbf5be320983fa63c7ed43febfe9b26c124ef3d44b5612983fe9aff    registry.k8s.io/kube-proxy:v1.27.5              io.containerd.runc.v2

런타임이 docker 의 경우 아래와 같이 입력한다.

$ docker ps

이제 해당 파드를 제거하며

[node1 ~]$ kubectl delete pod my-nginx
pod "my-nginx" deleted

파드 제거시 pause 컨테이너도 제거 되며 직접 확인해보면 제거되어 pause 컨테이너가 하나 제거 되었으며 nginx 컨테이너가 제거 되었음을 확인 할 수 있다.

[node3 ~]$ ctr -namespace k8s.io -address /var/run/docker/containerd/containerd.sock containers ls
CONTAINER                                                           IMAGE                                           RUNTIME                  
213fca27006914841f104ef6f9daa3cb24442b81e3dc54bfef61f5d0193e5a57    docker.io/cloudnativelabs/kube-router:latest    io.containerd.runc.v2    
5a94cabe9b5826cc2e021c919cbd34603e02bc2bccfc52857eab23beb8a0e833    registry.k8s.io/pause:3.6                       io.containerd.runc.v2    
9bffb4d6108bd330e3a788a3a624009d2fee71f4c1cea4dd17bd285b3996a3bb    registry.k8s.io/pause:3.6                       io.containerd.runc.v2    
b815da08a2ee94b670b94704454c2f657672bc8bab897581986548496c6f3fbe    docker.io/cloudnativelabs/kube-router:latest    io.containerd.runc.v2    
e6285095bdbf5be320983fa63c7ed43febfe9b26c124ef3d44b5612983fe9aff    registry.k8s.io/kube-proxy:v1.27.5              io.containerd.runc.v2

이제 static Pod 에 대해서 알아보자!

마스터에 있는 api server 컨테이너에게 요청을 보내지 않고 pod를 생성/삭제 하는 파드이다.

어떻게 api server 없이 파드를 생성/삭제 할까 ?

kubelet 데몬이 바라보는 디렉토리에 yaml 파일을 생성하면 kubelet이 알아서 파드를 생성하고 yaml 파일이 없으면 pod를 제거한다.

이를 통해서 파드를 생성/삭제하는 pod 를 static 파드라고 한다!

kubelet 데몬이 바라보는 디렉토리의 위치는 config.yaml 에 저장 되어 있는데 쭉 보다 보면 staticPodPath 부분에 디렉토리 확인 가능하다!

[node1 ~]$ cat /var/lib/kubelet/config.yaml
staticPodPath: /etc/kubernetes/manifests

 

이제 node2에 해당 디렉토리로 간뒤

[node2 ~]$cd /etc/kubernetes/manifests/
[node2 manifests]$ ll
total 0

 nginx.yaml 을 생성한다.

apiVersion: v1
kind: Pod
metadata:
  labels:
    run: webserver
  name: webserver
spec:
  containers:
  - image: nginx:1.14
    name: webserver
    ports:
    - containerPort: 80

정상적으로 파일을 생성하였고

[node2 manifests]$ ll
total 4
-rw-r--r-- 1 root root 179 Aug 24 13:20 nginx.yaml

이제 파드 항목을 보면 [container명]-[서버명] 으로 파드가 생성 된것을 볼 수 있다.

[node1 ~]$ kubectl get pods
NAME              READY   STATUS    RESTARTS   AGE
webserver-node2   1/1     Running   0          55s

우리는 api server 도움 없이 노드에서 직접 파드를 생성한것이다!

이제 해당 파드를 삭제하고 싶다면 node2에 가서 yaml 파일을 삭제하면

[node2 manifests]$ rm -rf nginx.yaml 
[node2 manifests]$

 파드가 삭제 되었다.

[node1 ~]$ kubectl get pods
No resources found in default namespace.
[node1 ~]$

슬레이브 노드인 node2의 static 경로에는 yaml파일이 없었으나

마스터 노드인 node1의 static 디렉토리 경로에 가보면 아래와 같이 우리가 알고 있는 필수 컴포넌트에 대한 yaml 파일이 있다.

[node1 ~]$ cd /etc/kubernetes/manifests/
[node1 manifests]$ 
[node1 manifests]$ ll
total 16
-rw------- 1 root root 2385 Aug 24 13:15 etcd.yaml
-rw------- 1 root root 3361 Aug 24 13:15 kube-apiserver.yaml
-rw------- 1 root root 2875 Aug 24 13:15 kube-controller-manager.yaml
-rw------- 1 root root 1463 Aug 24 13:15 kube-scheduler.yaml

이를 통해 기본 컴포넌트는 static pod 를 통해 생성이 되었구나~ 를 알 수 있다.

끝!

728x90