티스토리 뷰

Kubernetes

25 Kubernetes 201

zerobig-k8s 2018. 9. 27. 07:40

<출처> 

https://kubernetes.io/docs/tutorials/k8s201/



K8S 201에서는, 101에서 남겨둔 것을 마저 진행하고 애플리케이션 생산, 디플로이먼트 그리고 스케일링에 관한 K8S의 다소 보다 상급 수준의 토픽를 다루게 된다.

Kubernetes 101을 살펴 보았다면, kubectl, 파드, 볼륨 그리고 여러 개의 컨테이너에 대해 배웠을 것이다.





Objectives

  • 파드에 레이블 추가
  • 디플로이먼트 다루기
  • 서비스 다루기
  • 헬스체크의 정의





Before you begin

K8S 클러스터가 필요하며, 클러스터와 커뮤니케이션할 수 있도록 구성되어진 kubctl 커맨드-라인 툴도 필요하다. 아직 클러스터를 보유하지 않고 있다면, Minikube 를 이용하여 생성할 수 있으며, 또는 다음 두 개의 (웹 기반  대화형 가상 터미널 환경의) K8S 실습 도구를 이용할 수 있다.



버전 확인을 위해 kubectl version 을 수행한다.

수행 할 kubectl 용례 확인을 위해서는, 로컬에 예시 디렉토리가 있어야 하며, release 또는 여기에 있는 최신 .yaml  파일이 존재한다.  




Labels

이미 파드에 대해 배웠고 어떻게 생성하는지 알고 있기 때문에, 충동적으로 많은 파드를 생성하다가 난처한 상황에 빠질 수도 있다. 그렇게 해보라! 그러나 결국 파드를 그룹안으로 (배치해) 조직화 시켜줄 시스템이 필요할 것이다. K8S에서 이 역할을 수행해주는 시스템이 레이블(Label)이다. 레이블은 키-밸류 쌍으로 K8S의 각 오브젝트에 부착된다. 레이블 셀렉터와 일치하는 오브젝트 리스트를 검색하기 위해 API 서버에 RESTFUL 리스트 요청과 함께 레이블 셀렉터가 건네질 수 있다. 

레이블을 추가하기 위해, 파드 정의 내 metadata 아래 레이블 섹션에 레이블을 추가한다.

1
2
labels:
  env: test
cs


예를들어, 다음은 레이블을 가진 nignx 파드 정의다.

pods/pod-nginx.yaml

1
2
3
4
5
6
7
8
9
10
11
12
13
apiVersion: v1
kind: Pod
metadata:
  name: nginx
  labels:
    env: test
spec:
  containers:
  - name: nginx
    image: nginx
    imagePullPolicy: IfNotPresent
  nodeSelector:
    disktype: ssd
cs


레이블된 파드를 생성해 본다.

1
2
[root@docker-registry k8s]# kubectl create -f https://k8s.io/examples/pods/pod-nginx.yaml
pod/nginx created
cs


"env=test" 레이블을 가진 모든 파드를 리스트 해본다.

1
2
3
[root@docker-registry k8s]# kubectl get pods -l env=test
NAME      READY     STATUS    RESTARTS   AGE
nginx     1/1       Running      0       1m
cs

레이블로 파드를 삭제한다. 

1
2
[root@docker-registry k8s]# kubectl delete pod -l env=test
pod "nginx" deleted
cs






Deployments

이제 멋진, 여러 개의 컨테이너, 레이블을 갖는 파드 등을 만드는 방법에 대해 알게 되었으니. 애플리케이션을 제작하는데 이것들을 이용해 보고 싶을 것이다. 많은 양의 개별 파드를 막 제작하고 픈 생각이 들 테지만, 그리하면, 전체 호스트에 대한 운용 이슈가 발생하게 된다. 예를들어, 어떻게 많은 수의 파드를 스케일 업 또는 다운시킬 것인가? 어떻게 새로운 릴리즈를 출시 할 것인가?

이 질문들을 포함한 더 많은 이슈에 관한 대답은 운영중인 파드를 유지하고 업데이트하기 위해 디플로이먼트를 이용하라는 것이다.

디플로이먼트 오브젝트는 파드 생성 템플릿("쿠키-커터" 처럼 만들 수 있다면)과 의도한 레플리카 카운트를 정의한다. 디플로이먼트는 관리하는 파드를 식별하기 위해 레이블 셀렉터를 이용하고 레플리카 카운트에 맞추기 위해 필요할 경우 파드를 생성 또는 삭제할 것이다. 또한 디플로이먼트는 안전하게 동작 상태의 파드에 변경사항을 반영하도록 관리하는데 이용된다. 

다음은 두 개의 nginx 파드를 예로 들어주는 디플로이먼트이다. 


application/deployment.yaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
apiVersion: apps/v1 # for versions before 1.9.0 use apps/v1beta2
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 2 # tells deployment to run 2 pods matching the template
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.7.9
        ports:
        - containerPort: 80
cs


Deployment Management

nginx 디플로이먼트를 생성한다.

1
2
[root@docker-registry k8s]# kubectl create -f https://k8s.io/examples/application/deployment.yaml
deployment.apps/nginx-deployment created
cs


모든 디플로이먼트를 리스트 해본다.

1
2
3
[root@docker-registry k8s]# kubectl get deployment
NAME               DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   2         2         2            2           51s
cs


디플로이먼트에 의해 생성된 파드를 리스트 해본다.

1
2
3
4
[root@docker-registry k8s]# kubectl get pods -l app=nginx
NAME                                READY     STATUS    RESTARTS   AGE
nginx-deployment-6c54bd5869-55srv   1/1       Running   0          1m
nginx-deployment-6c54bd5869-6rn7b   1/1       Running   0          1m
cs


디플로이먼트를 변경하고 "apply"를 호출하여 nginx 컨테이너를 1.7.9 에서 1.8로 업그레이드 한다. 다음 구성은 의도한 변경을 포함하고 있다.


application/deployment-update.yaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
apiVersion: apps/v1 # for versions before 1.9.0 use apps/v1beta2
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 2
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.8 # Update the version of nginx from 1.7.9 to 1.8
        ports:
        - containerPort: 80
cs


1
2
3
[root@docker-registry k8s]# kubectl apply -f https://k8s.io/examples/application/deployment-update.yaml
Warning: kubectl apply should be used on resource created by either kubectl create --save-config or kubectl apply
deployment.apps/nginx-deployment configured
cs


디플로이먼트가 이전 파드를 지우고 새로운 이름을 가진 파드를 생성한 것을 확인해 본다.

1
2
3
4
[root@docker-registry k8s]# kubectl get pods -l app=nginx
NAME                                READY     STATUS    RESTARTS   AGE
nginx-deployment-7694d679d9-9nwtp   1/1       Running   0          31s
nginx-deployment-7694d679d9-rc2jb   1/1       Running   0          23s
cs


이름으로 디플로이먼트를 삭제한다.

1
2
[root@docker-registry k8s]# kubectl delete deployment nginx-deployment
deployment.extensions "nginx-deployment" deleted
cs


이전 버전으로 디플로이먼트 변경을 롤백하는 방법과 같은 더 자세한 정보는, 디플로이먼트를 참조한다.




Services

일단 파드 셋을 복제했다면, 애플리케이션 레이어 간 연결을 가능하게 해주는 추상화 (오브젝트)가 필요하게 된다. 예를들어, 백엔드 잡을 관리하는 디플로이먼트가 있을 경우, 백엔드를 다시 스케일할 때마다 프론트 엔드를 재구성 해야만 하는 상황을 원하지 않을 것이다. 마찬가지로, 상이한 머신에 벡엔드 내 파드가 스케줄 (또는 재스케줄) 될 경우, 프론트엔드를 재구성하는 것이 필요치 않다. K8S에서, 서비스 추상화는 이러한 목적을 이루어 준다. 하나의 서비스는 하나의 단일 정적 IP 주소를 갖는 (레이블에 의해 선택된) 파드 셋를 참조할 수 있게 해주는 방법을 제공해 준다. 제공사업자에 의해 지원만 된다면, 로드밸런싱을 제공할 수도 있다.  


service/nginx-service.yaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
apiVersion: v1
kind: Service
metadata:
  name: nginx-service
spec:
  ports:
  - port: 8000 # the port that this service should serve on
    # the container on each pod to connect to, can be a name
    # (e.g. 'www') or a number (e.g. 80)
    targetPort: 80
    protocol: TCP
  # just like the selector in the deployment,
  # but this time it identifies the set of pods to load balance
  # traffic to.
  selector:
    app: nginx
cs


Service Management


(먼저 위에서 삭제한 nginx  디플로이먼트를 다시 생성한다.)

1
2
[root@docker-registry ~]# kubectl apply -f https://k8s.io/examples/application/deployment-update.yaml
deployment.apps/nginx-deployment created
cs


nginx 서비스를 생성한다.

1
2
[root@docker-registry k8s]# kubectl create -f https://k8s.io/examples/service/nginx-service.yaml
service/nginx-service created
cs


모든 서비스를 리스트 해본다.

1
2
3
4
[root@docker-registry k8s]# kubectl get services
NAME            TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
kubernetes      ClusterIP   10.51.240.1     <none>        443/TCP    2d
nginx-service   ClusterIP   10.51.249.138   <none>        8000/TCP   17s
cs


대부분의 제공사업자들이 제공하는 있는 네트워크 환경에서, 서비스 IP는 외부에서 접근 불가하다. 서비스가 동작하는지 테스트 해보는 가장 쉬운 방법은 busybox 파드 하나를 생성하여 외부에서 거기에 exec 명령을 수행해 보는 것이다. 보다 자세한 정보는 command execution documentation을 참조한다.

서비스 IP가 접근 가능하다면, 노출된 포트에 대한 wget을 이용하여 http 엔드포인트로 접근할 수 있다.

1
2
[root@docker-registry k8s]# export SERVICE_IP=$(kubectl get service nginx-service -o go-template='{{.spec.clusterIP}}')
[root@docker-registry k8s]# export SERVICE_PORT=$(kubectl get service nginx-service -o go-template='{{(index .spec.ports 0).port}}')
cs


"$SERVICE_IP"와 "$SERVICE_PORT"를 확인해 본다.

1
2
[root@docker-registry k8s]# echo "$SERVICE_IP:$SERVICE_PORT"
10.51.249.138:8000
cs


그리고 나서, busybox 파드를 생성한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
[root@docker-registry k8s]# kubectl run busybox --generator=run-pod/v1 --image=busybox --restart=Never --tty -i --env "SERVICE_IP=$SERVICE_IP" --env "SERVICE_PORT=$SERVICE_PORT"
If you don't see a command prompt, try pressing enter.
/ # wget -qO- http://10.51.249.138:8000
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
 
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
 
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
/ # exit
[root@docker-registry k8s]# kubectl delete pod busybox
pod "busybox" deleted
cs


서비스 정의는 포트 8000 ("$SERVICE_PORT")으로 Nginx 서비스를 노출시켰다. 또한 이 포트를 이용하여 K8S 동작하는 호스트에서 서비스에 접근할 수 있다.

1
[root@docker-registry k8s]# wget -qO- http://$SERVICE_IP:$SERVICE_PORT
cs

(이것은 Weave를 이용한 AWS 상에서 동작한다.)


이름으로 (디플로이먼트와) 서비스를 삭제한다.

1
2
3
4
[root@docker-registry k8s]# kubectl delete deployment nginx-deployment
deployment.extensions "nginx-deployment" deleted
[root@docker-registry k8s]# kubectl delete service nginx-service
service "nginx-service" deleted
cs


각 서비스가 생성될 때, 유일의 IP 주소를 할당 받는다. 이 주소는 해당 서비스의 수명과 함께 하고, 서비스가 살아 있는 동안에는 변경되지 않는다. 파드는 이 서비스와 소통하도록 구성될 수 있고 서비스에 커뮤니케이션 하게되면 몇몇 파드에 자동으로 로드밸런스가 이루어 진다는 것을 알고 있다. 이 파드는 서비스 내 레이블 셀렉터에 의해 식별된 다수의 셋이다.

더 자세한 정보는 서비스를 참조한다.





Health Checking

코드를 작성할 때 절대 오동작이 나지 않는다, 그런가? 안타깝게도 Kubernetes 이슈 리스트에는 이와 달리 나타낸다...

버그 없는 코드를 작성하려 애쓰는 것보다, 더 나은 접근법은 주기적인 헬스체크를 수행하고 애플리케이션을 수선하기 위한 관리 시스템을 이용하는 것이다. 애플리케이션 자체로부터 벗어나 시스템 외부에서 애플리케이션을 모니터링 하고 잘못된 것을 고치도록 조치하는 방식을 말한다. 애플리케이션이 고장나고, 헬스체크 에이전트가 애플리케이션의 일부인 경우, 이 또한 고장이 날 수 있고 우리가 이를 알 도리가 없게되므로, 시스템을 애플리케이션의 외부에 두는 것은 중요하다. K8S에서 헬스체크는 kubelet 에이전트를 모니터 한다.

Process Health Checking

헬스체크의 가장 간단한 형태는 단지 프로세스 레벨의 헬스체크를 하는 것이다. Kubelet은 끊임없이 컨테이너 프로세스가 여전히 동작 중인지 Docker  데몬에게 묻는다. 그리고 동작 중이 아니라면, 컨테이너 프로세스가 잭구동된다. 지금까지 동작시켜 왔던 K8S 예제 모두에서 이 헬스체크는 실제 이미 이용되도록 설정되었었다. K8S에서 동작하는 모든 단일 컨테이너에 대해 활성화 되어 있다.

Application Health Checking

그러나, 여러가지 경우에 대해 이러한 낮은 레벨의 헬스체크는 불충분하다.  예를들어 다음 코드를 보자.

1
2
3
4
5
6
7
8
9
10
11
lockOne := sync.Mutex{}
lockTwo := sync.Mutex{}
 
go func() {
  lockOne.Lock();
  lockTwo.Lock();
  ...
}()
 
lockTwo.Lock();
lockOne.Lock();
cs


이는 “Deadlock”이라 알려진 컴퓨터 공학에 있어 고전적인 문제점에 대한 예시이다. Docker 관점에서, 애플리케이션은 여전히 운영되고 프로세스도 여전히 동작 중이지만, 애플리케이션의 관점에서 코드가 잠겨 절대 올바른 응답을 할 수 없게 될 것이다.

이 문제를 해결하기 위해, K8S는 사용자에 의해 수행되는 애플리케이션 헬스체크를 지원한다. 이러한 체크는 사용자가 제시한 "올바르게"라는 정의에 맞게 애플리케이션이 올바르게 동작 중이라는 것을 보장해주기 위해 Kubelet에 의해 수행된다.

현재, 선택가능한 3가지 애플리케이션 체크방식이 있다.

  • HTTP 헬스 체크 - Kubelet은 웹 훅을 호출할 것이다. 200에서 399 사이의 결과값을 리턴한다면, 성공으로 간주하고 그 외에는 실패로 간주한다. 여기 헬스체크 예시가 있다. 
  • 컨테이너 Exec - Kubelet은 컨테이너 내부에서 임의의 명령을 시행할 것이다. 상태 0로 종료되면, 성공으로 간주 될 것이다. 여기 헬스체크 예시가 있다.
  • TCP 소켓 - Kubelet은 컨테이너에 소켓을 오픈하려 시도할 것이다. 커넥션이 확립되면, 컨테이너는 양호한 것으로 간주되고, 실패가 나면 불량으로 간주된다.

모든 케이스에서, Kubelet이 실패를 발견하면, 컨테이너는 재구동 된다.

컨테이너 헬스체크는 컨테이너 구성에서 "livenessProbe" 섹션에서 구성된다. 또한 "initialDelaySeconds"를 정의할 수 있는데, 이는 컨테이너가 구동되어진 시점으로부터 컨테이너가 임의의 필요한 초기화를 수행할 수 있도록 해주기 위해, 헬스체크를 수행할 때가지의 유예기간을 의미한다.

다음은 HTTP 헬스체크를 수행하는 하나의 파드에 대한 구성 예시이다. (pod-with-http-healthcheck.yaml)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
apiVersion: v1
kind: Pod
metadata:
  name: pod-with-http-healthcheck
spec:
  containers:
  - name: nginx
    image: nginx
    # defines the health checking
    livenessProbe:
      # an http probe
      httpGet:
        path: /_status/healthz
        port: 80
      # length of time to wait for a pod to initialize
      # after pod startup, before applying health checking
      initialDelaySeconds: 30
      timeoutSeconds: 1
    ports:
    - containerPort: 80
cs


그리고 다음은 TCP 소켓 헬스체크를 수행하는 하나의 파드에 대한 구성 예시이다.(pod-with-tcp-socket-healthcheck.yaml)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
apiVersion: v1
kind: Pod
metadata:
  name: pod-with-tcp-socket-healthcheck
spec:
  containers:
  - name: redis
    image: redis
    # defines the health checking
    livenessProbe:
      # a TCP socket probe
      tcpSocket:
        port: 6379
      # length of time to wait for a pod to initialize
      # after pod startup, before applying health checking
      initialDelaySeconds: 30
      timeoutSeconds: 1
    ports:
    - containerPort: 6379
cs


헬스체크에 대한 더 자세한 정보는 Container Probes을 참조한다.




What's next

완성된 애플리케이션을 확인하려면 guestbook example를 참조한다.

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함