Gurumee 2021. 7. 9. 20:41
반응형

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

큐브 시스템 컴포넌트란

큐브 시스템 컴포넌트란 쿠버네티스(kubenetes) 시스템을 구성하는 중요 컴포넌트입니다. 그림으로 나타내면 다음과 같다.

마스터 노드(Control Plane Node)는 다음과 같이 구성되어 있다.

  • etcd : 모든 클러스터 데이터를 담는 쿠버네티스의 저장소이다.
  • kube-apiserver : Kubernetes API를 노출한다. 모든 컴포넌트는 이 컴포넌트를 통해서 통신한다.
  • kube-scheduler : 생성된 Pod를 어떤 Node에 배치하고 실핼할 것인지를 선택한다.
  • kube-controller-manager : 실제 리소스를 관리하는 컴포넌트 다음과 같이 크게 4가지로 구성된다.
    1. 노드 컨트롤러 : 노드 상태 관리
    2. 레플리케이션 컨트롤러 : Pod 개수를 조절
    3. 엔드포인트 컨트롤러 : Service와 Pod을 연결
    4. 서비스 어카운트 & 토큰 컨트롤러 : 인증/인가를 담당

슬레이브 노드(Data Node)는 다음과 같이 구성되어 있다.

  • kubelet : 마스터 노드의 명령에 따라서 CRT(Container Run Time - Docker)를 관리한다.
  • kube-proxy : 노드 - 컨테이너, 컨테이너-컨테이너간 통신을 담당한다.

이번 장에서는 VM에서 실습을 진행한다. 마스터 노드에서 다음 명령어를 실행해보자.

$ kubectl get pod -n kube-system
NAME                             READY   STATUS    RESTARTS   AGE
coredns-558bd4d5db-5z4jv         1/1     Running   1          5d
coredns-558bd4d5db-g9nhg         1/1     Running   1          5d
etcd-master                      1/1     Running   2          5d
kube-apiserver-master            1/1     Running   2          5d
kube-controller-manager-master   1/1     Running   2          5d
kube-proxy-hvpz5                 1/1     Running   2          5d
kube-proxy-kdtb5                 1/1     Running   1          5d
kube-proxy-qthkm                 1/1     Running   1          5d
kube-scheduler-master            1/1     Running   2          5d
weave-net-46bsq                  2/2     Running   3          5d
weave-net-fgxbv                  2/2     Running   4          5d
weave-net-qr5v6                  2/2     Running   5          5d

 

-n 옵션은 네임스페이스를 지정하는 옵션이다. Kubernetes 클러스터를 구성하면 기본적으로 큐브 시스템이 뒤에서 Pod의 형태로 돌아가게 된다. etcd, kube-apiserver, kube-controller-manager, kube-scheduler가 동작되는 것을 확인할 수 있다.

 

이제 /etc/kubernetes/manifests/ 경로로 이동해서 어떤 파일들을 가지고 있는지 확인해보자.

$ cd /etc/kubernetes/manifests/

$ ll
total 24
drwxr-xr-x 2 root root 4096  7월  3 18:50 ./
drwxr-xr-x 4 root root 4096  7월  3 18:50 ../
-rw------- 1 root root 2126  7월  3 18:50 etcd.yaml
-rw------- 1 root root 3947  7월  3 18:50 kube-apiserver.yaml
-rw------- 1 root root 3350  7월  3 18:50 kube-controller-manager.yaml
-rw------- 1 root root 1384  7월  3 18:50 kube-scheduler.yaml

 

이 디렉토리에 존재하는 파일을 가지고 마스터 노드에서 동작하는 큐브 시스템에 대한 커스텀한 설정을 할 수 있다는 것을 알아두자. 이번엔 슬레이브 노드에서 다음을 입력해보자.

$ ps -ef | grep "kube"
root         698       1  1 19:06 ?        00:00:18 /usr/bin/kubelet --bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf --kubeconfig=/etc/kubernetes/kubelet.conf --config=/var/lib/kubelet/config.yaml --network-plugin=cni --pod-infra-container-image=k8s.gcr.io/pause:3.4.1
root        2562    2523  0 19:07 ?        00:00:00 /usr/local/bin/kube-proxy --config=/var/lib/kube-proxy/config.conf --hostname-override=slave2
root        3174    2796  0 19:07 ?        00:00:00 /home/weave/kube-utils -run-reclaim-daemon -node-name=slave2 -peer-name=6e:85:ad:af:a7:bb -log-level=debug
gurumee    11764    5641  0 19:25 pts/0    00:00:00 grep --color=auto kube

 

위의 명령어 두 줄을 보면, kubeletkube-proxy가 실행되고 있음을 확인할 수 있다.

Etcd란?

EtcdKubernetes 클러스터의 저장소로 사용되며, 원래는 키-값 쌍으로 데이터를 저장하는 NoSQL 데이터베이스이다. 분산 환경에서 자주 쓰인다고 한다.

그냥 어떻게 쓰는지 확인하기 위해서 이를 간단하게 조작할 수 있는 etcdctl을 설치한다.

# 압축 파일 다운로드
$ wget https://github.com/etcd-io/etcd/releases/download/v3.5.0/etcd-v3.5.0-linux-amd64.tar.gz

# 압축 파일 해제
$ tar -xf etcd-v3.5.0-linux-amd64.tar.gz

 

이제 압축된 파일로 들어가보자. etcd, etcdctl 등이 설치된 것을 확인할 수 있다.

$ cd etcd-v3.5.0-linux-amd64

$ ll
total 56312
drwxr-xr-x 3 gurumee gurumee     4096  6월 16 06:51 ./
drwxrwxr-x 3 gurumee gurumee     4096  7월  8 19:44 ../
drwxr-xr-x 3 gurumee gurumee     4096  6월 16 06:51 Documentation/
-rwxr-xr-x 1 gurumee gurumee 23560192  6월 16 06:51 etcd*
-rwxr-xr-x 1 gurumee gurumee 17969152  6월 16 06:51 etcdctl*
-rwxr-xr-x 1 gurumee gurumee 16048128  6월 16 06:51 etcdutl*
-rw-r--r-- 1 gurumee gurumee    42066  6월 16 06:51 README-etcdctl.md
-rw-r--r-- 1 gurumee gurumee     7359  6월 16 06:51 README-etcdutl.md
-rw-r--r-- 1 gurumee gurumee     9394  6월 16 06:51 README.md
-rw-r--r-- 1 gurumee gurumee     7896  6월 16 06:51 READMEv2-etcdctl.md

 

etcd 바이너리 파일을 이용해서 실행할 수 있지만 이미 kubernetes 클러스터를 운영하면서 실행되고 있는 상태이다. 클러스터가 이용하고 있는 etcd를 이용해서 키를 조회할 수 있다. 터미널에 다음을 입력한다.

$ sudo ETCDCTL_API=3 ./etcdctl --endpoints 127.0.0.1:2379 --cacert /etc/kubernetes/pki/etcd/ca.crt --cert /etc/kubernetes/pki/etcd/server.crt --key /etc/kubernetes/pki/etcd/server.key get / --prefix --keys-only

...
/registry/services/endpoints/default/kubernetes

/registry/services/endpoints/kube-system/kube-dns

/registry/services/specs/default/kubernetes

/registry/services/specs/kube-system/kube-dns

 

그럼 가지고 있는 키 목록이 쭉 나열될 것이다. 위 명령어에서 뒤에 get 부터가 실제 명령어다. 이제 이 etcd에 키-값 쌍을 한 번 저장해보고 꺼내보도록 하자 매우. 쉽다. 먼저 터미널에 다음을 입력한다.

$ sudo ETCDCTL_API=3 ./etcdctl --endpoints 127.0.0.1:2379 --cacert /etc/kubernetes/pki/etcd/ca.crt --cert /etc/kubernetes/pki/etcd/server.crt --key /etc/kubernetes/pki/etcd/server.key put test foo
OK

 

키 "test" 값 "foo"를 etcd에 저장했다. 이를 가져와보자. 터미널에 다음을 입력한다.

$ sudo ETCDCTL_API=3 ./etcdctl --endpoints 127.0.0.1:2379 --cacert /etc/kubernetes/pki/etcd/ca.crt --cert /etc/kubernetes/pki/etcd/server.crt --key /etc/kubernetes/pki/etcd/server.key get test 
test
foo

 

첫 번째 줄에 키가, 두 번째 줄에 값이 출력될 수 있음을 확인할 수 있다. 우리는 etcd가 키-값 쌍으로 저장하는 데이터베이스이며 kubernetes가 저장소로 사용하는 것만 알고 있으면 된다.

Pod이란?

Pod이란 Kubernetes과 관리하는 가장 기본이 되는 요소이다.

위 그림처럼 노드 내부에 생성이 된다. 단, 노드에 걸쳐서 Pod이 생성되지는 않는다. 하나의 Pod에는 여러 컨테이너가 실행될 수 있으나 모니터링 등의 이유가 아니라면 1 Pod 1 Container가 원칙이다. 또한 PodNAT로 구성된 것 처럼 외부와 격리되어 있다. 외부와 연결하려면 Service가 필요하다.

 

Pod은 명령어로 만들 수 있지만 보통 yaml 파일로 작성하는 것이 일반적이다. 다음과 같이 simple-app-pod-v1.yaml을 작성해보자.

 

src/ch06/simple-app-pod-v1.yaml

apiVersion: v1
kind: Pod
metadata:
  name: simple-app
spec:
  containers:
  - name: simple-app
    image: gurumee92/simple-app
    ports:
    - containerPort: 8080

 

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

$ kubectl create -f simple-app-pod-v1.yaml
job.batch/simple-app created

 

그 다음 다음 명령어로 잘 실행이 되나 확인을 해보자.

$ kubectl get pod -w
NAME               READY   STATUS              RESTARTS   AGE
simple-app-5r4fb   0/1     ContainerCreating   0          3s
simple-app-5r4fb   1/1     Running             0          4s

 

실행이 되면 kubectl describe 명령어를 통해서 더 자세한 내용을 살펴볼 수 있다.

$ kubectl describe pod simple-app-zkh2m 
Name:         simple-app-zkh2m
Namespace:    default
Priority:     0
Node:         slave2/10.0.2.5
Start Time:   Thu, 08 Jul 2021 20:13:08 +0900
Labels:       controller-uid=06d6338a-c41d-4a88-a6c4-fef094c539e0
              job-name=simple-app
Annotations:  <none>
Status:       Running
IP:           10.32.0.3
IPs:
  IP:           10.32.0.3
Controlled By:  Job/simple-app
Containers:
  simple-app:
    Container ID:   docker://20b5995bc8c7339f79740c7a6391b313a789f672dd527b97590598ed1b4f6dca
    Image:          gurumee92/simple-app
    Image ID:       docker-pullable://gurumee92/simple-app@sha256:baf83add38ca5429adb80edc8e1647179d1771e853e00ae95c274bccc3b0dcd1
    Port:           8080/TCP
    Host Port:      0/TCP
    State:          Running
      Started:      Thu, 08 Jul 2021 20:15:40 +0900
    Ready:          True
    Restart Count:  0
    Environment:    <none>
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-xh6pc (ro)
Conditions:
  Type              Status
  Initialized       True 
  Ready             True 
  ContainersReady   True 
  PodScheduled      True 
Volumes:
  kube-api-access-xh6pc:
    Type:                    Projected (a volume that contains injected data from multiple sources)
    TokenExpirationSeconds:  3607
    ConfigMapName:           kube-root-ca.crt
    ConfigMapOptional:       <nil>
    DownwardAPI:             true
QoS Class:                   BestEffort
Node-Selectors:              <none>
Tolerations:                 node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
                             node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
  Type    Reason     Age    From               Message
  ----    ------     ----   ----               -------
  Normal  Scheduled  7m36s  default-scheduler  Successfully assigned default/simple-app-zkh2m to slave2
  Normal  Pulling    7m35s  kubelet            Pulling image "gurumee92/simple-app"
  Normal  Pulled     5m4s   kubelet            Successfully pulled image "gurumee92/simple-app" in 2m31.03551003s
  Normal  Created    5m4s   kubelet            Created container simple-app
  Normal  Started    5m4s   kubelet            Started container simple-app

 

이미지 정보, 어떤 노드에 생성되었는지, 컨테이너 실행 이력 등 여러 정보를 확인할 수 있다. 이제 포트 포워딩을 통해서 외부로 노출시켜보자

$ kubectl port-forward simple-app-zkh2m 8080:8080
Forwarding from 127.0.0.1:8080 -> 8080
Forwarding from [::1]:8080 -> 8080
Handling connection for 8080
Handling connection for 8080
E0708 20:17:48.868587   66425 portforward.go:385] error copying from local connection to remote stream: read tcp4 127.0.0.1:8080->127.0.0.1:36710: read: connection reset by peer
E0708 20:17:48.876150   66425 portforward.go:385] error copying from local connection to remote stream: read tcp4 127.0.0.1:8080->127.0.0.1:36712: read: connection reset by peer
Handling connection for 8080
E0708 20:17:52.900618   66425 portforward.go:385] error copying from local connection to remote stream: read tcp4 127.0.0.1:8080->127.0.0.1:36726: read: connection reset by peer
Handling connection for 8080

 

터미널을 새로 열어 다음을 입력해보자.

$ curl localhost:8080
Hello World simple-app-zkh2

 

굳! 그 후 삭제는 다음으로 할 수 있다.

$ kubectl delete -f simple-app-pod-v1.yaml
job.batch "simple-app" deleted

Probe란?

 

Probe란 컨테이너에서 kubelet에 의해 주기적으로 수행되는 자가진단 및 회복을 작업을 뜻한다. 다음과 같이 세 가지의 Probe가 있다.

  • livenessProbe : 컨테이너가 동작 중인지 여부를 나타냄. 실패하면 컨테이너를 죽이고 다시 재시작.
  • readinessProbe : 컨테이너가 요청을 처리할 준비가 되었는지 여부를 나타냄. 만약 실패하면, 로드 밸런서에서 해당 Pod의 IP 주소를 제거함.
  • startupProbe : 컨테이너 내 애플리케이션이 시작 여부를 나타냄. 이 프로브가 활성화되면 나머지 두 프로브는 비활성화됨. 실패하면 멐ㄴ테이너를 죽이고 재시작함.

여기서는 HTTP 엔드 포인트에 대한 헬스 체크를 하는 livenessProbe를 간단하게 작성해보자. 다음을 작성한다.

 

src/ch06/http-liveness.yaml

apiVersion: v1
kind: Pod
metadata:
  labels:
    test: liveness
  name: liveness-http
spec:
  containers:
  - name: liveness
    image: k8s.gcr.io/liveness
    args:
    - /server
    livenessProbe:
      httpGet:
        path: /healthz
        port: 8080
        httpHeaders:
        - name: Custom-Header
          value: Awesome
      initialDelaySeconds: 3
      periodSeconds: 3

 

이제 터미널에 다음을 입력해서 Pod을 만든다.

$ kubectl create -f http-liveness.yaml

 

그 후 Pod을 쭉 관찰해보자.

$ kubectl get pod -w
NAME            READY   STATUS    RESTARTS   AGE
liveness-http   1/1     Running   0          6s
liveness-http   1/1     Running   1          24s
liveness-http   1/1     Running   2          45s
liveness-http   1/1     Running   3          65s
liveness-http   0/1     CrashLoopBackOff   3          81s
liveness-http   1/1     Running            4          112s
liveness-http   1/1     Running            5          2m12s
liveness-http   0/1     CrashLoopBackOff   5          2m28s
...

 

처음 빼놓고 약 20초 간격으로 재시작하는 것을 확인할 수 있다. 그렇게 3번을 실행하면 Pod이 종료된다. 그 후 다시 재시작한다. 이게 계속 반복된다.

 

실제 k8s.gcr.io/liveness 이미지로 작성된 컨테이너는 다음 코드가 들어있다.

http.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) {
    duration := time.Now().Sub(started)
    if duration.Seconds() > 10 {
        w.WriteHeader(500)
        w.Write([]byte(fmt.Sprintf("error: %v", duration.Seconds())))
    } else {
        w.WriteHeader(200)
        w.Write([]byte("ok"))
    }
})

위 코드는 시작 시 처음 10초간 200 상태코드를 내뱉다가 그 이후부터는 500 상태 코드를 내뱉는 것이다. 그래서 계속 10초 이후에 에러로 판단해서 계속 Pod을 종료했다가 재시작하게 되는 것이다. 다시 한 번 기억하자. 이런 자가 진단 작업 + 자가 회복 과정이 Probe이다.

Label이란?

레이블이란 모든 리소스를 구성하는 매우 간단하면서도 강력한 쿠버네티스의 기능이다. 키-값 쌍으로 리소스에 레이블을 지정해서 부가적인 정보를 나타낼 수 있다. 조회/추가/수정/삭제가 가능하며 심지어 리소스가 생성된 이후에도 추가/수정/삭제를 할 수는 있지만 권장하지는 않는다.

 

보통 이런식으로 붙여서 레이블 단위로 리소스들을 관리할 수 있다.

또한 사내에 정책이 없다면 다음 레이블들의 사용을 고려해보자.

이제 직접 레이블을 만들어보자. 다음과 같이 simple-app-pod-v2.yaml을 만들어보자.

 

src/ch06/simple-app-pod-v2.yaml

apiVersion: v1
kind: Pod
metadata:
  name: simple-app-v2
  labels:
    creation_method: manual
    env: prod
spec:
  containers:
  - name: simple-app
    image: gurumee92/simple-app
    ports:
    - containerPort: 8080

 

여기서 metadata 필드 밑에 labels 하위 필드들이 모두 레이블들이다. 그리고 이제 실습을 위해서 v1, v2를 모두 생성해두자.

# v1 생성
$ kubectl create -f simple-app-pod-v1.yaml 
pod/simple-app created

# v2 생성
$ kubectl create -f simple-app-pod-v2.yaml 
pod/simple-app-v2 created

# pod 조회
$ kubectl get pod -o wide
NAME            READY   STATUS    RESTARTS   AGE     IP          NODE     NOMINATED NODE   READINESS GATES
simple-app      1/1     Running   0          3m32s   10.32.0.3   slave2   <none>           <none>
simple-app-v2   1/1     Running   0          3m27s   10.32.0.4   slave2   <none>           <none>

 

이제 레이블을 검색해보자.

$ kubectl get pod --show-labels
NAME            READY   STATUS    RESTARTS   AGE     LABELS
simple-app      1/1     Running   0          8m37s   <none>
simple-app-v2   1/1     Running   0          8m32s   creation_method=manual,env=prod

 

v1에는 어떤 레이블도 확인되지 않고 v2에 레이블들이 붙은 것을 확인할 수 있다. 이제 v1에 레이블을 추가해보자.

# v1에 label 추가 env=test, creation_method=manual
$ kubectl label pod simple-app env=test creation_method=manual
pod/simple-app labeled

# 라벨 확인
$ kubectl get pod --show-labels
NAME            READY   STATUS    RESTARTS   AGE   LABELS
simple-app      1/1     Running   0          10m   creation_method=manual,env=test
simple-app-v2   1/1     Running   0          10m   creation_method=manual,env=prod

 

그 후, v2에 creation_method의 값을 automation으로 변경해보자.

# v2 라벨 업데이트
$ kubectl label pod simple-app-v2 creation_method=automation
error: 'creation_method' already has a value (manual), and --overwrite is false

 

이렇게 에러가 뜬다. 수정하려면 --overwrite 옵션을 켜주어야 한다. 다시 한 번 다음과 같이 입력해보자.

# v2 라벨 업데이트
$ kubectl label pod simple-app-v2 creation_method=automation --overwrite
pod/simple-app-v2 labeled

# 업데이트 내용 확인
$ kubectl get pod --show-labels
NAME            READY   STATUS    RESTARTS   AGE   LABELS
simple-app      1/1     Running   0          13m   creation_method=manual,env=test
simple-app-v2   1/1     Running   0          13m   creation_method=automation,env=prod

 

이제 v2의 레이블 creation_method를 제거해보자. 터미널에 다음을 입력한다.

# v2 라벨 제거
$ kubectl label pod simple-app-v2 creation_method-
pod/simple-app-v2 labeled

# 라벨 확인 v2에 creation_method 제거됨
$ kubectl get pod --show-labels
NAME            READY   STATUS    RESTARTS   AGE   LABELS
simple-app      1/1     Running   0          18m   creation_method=manual,env=test
simple-app-v2   1/1     Running   0          18m   env=prod

 

또한 레이블을 필터링해서 검색할 수 있다. 먼저 터미널에 다음을 입력한다.

$ kubectl get pod -l creation_method
NAME         READY   STATUS    RESTARTS   AGE
simple-app   1/1     Running   0          24m 

 

위의 명령어는 creation_method 레이블이 붙어 있는 Pod들을 필터링해서 보여준다. 이와는 반대로 다음과 같이 작성도 가능하다.

$ kubectl get pod -l '!creation_method'
NAME            READY   STATUS    RESTARTS   AGE
simple-app-v2   1/1     Running   0          25m

 

그럼 조건이 반전되서 creation_method 레이블이 붙지 않은 Pod들만 필터링해서 볼 수 있다. 위의 명령어들은 레이블 유무로 필터링했다면, 아래 명령어를 이용하면 다음과 같이 레이블 값 별로 필터링할 수 있다.

$ kubectl get pod -l 'env=prod'
NAME            READY   STATUS    RESTARTS   AGE
simple-app-v2   1/1     Running   0          26m

 

레이블을 여러 조건들로 필터링하고 싶다면, 다음과 같이 작성할 수 있다.

$ kubectl get pod -l "env=test,creation_method=manual"
NAME         READY   STATUS    RESTARTS   AGE
simple-app   1/1     Running   0          28m
728x90
반응형