삽질의 현장/- Docker&Kubernetes

[GCP Study Jam] Kubernetes 노드 소개

shovelman 2019. 1. 13. 18:53

GCP를 활용한 Kubernetes 학습을 진행하며 정리하는 글입니다 (Google Cloud Study Jam 참여 중).


개요

Kubernetes에서 실행되는 복제 애플리케이션으로 변환 해보겠습니다.

(Kubernetes는 노트북에서 고가용성 멀티노드 클러스터, 공용 클라우드에서 내부 배포, 가상 머신에서 베어 메탈에 이르기까지 다양한 환경에서 실행되는 오픈소스 프로젝트입니다.)

실습 내용은 아래와 같습니다.

  1. Node.js 서버 만들기
  2. Docker 컨테이너 이미지 만들기
  3. 컨테이너 클러스터 만들기
  4. Kubernetes 포드 만들기
  5. 서비스의 규모 확장하기

* 공통적으로 실습에서 사용되는 실제 PROJECT ID 는 PROJECT_ID로 대체합니다.

Node.js 서버 만들기

(node.js 설치가 완료되어있다는 전제로 진행합니다.)

Kubernetes Engine에 배포할 Node.js 서버 어플리케이션을 만들어봅니다(server.js).

var http = require('http');
var handleRequest = function(request, response) {
  response.writeHead(200);
  response.end('Hello World');
}
var www = http.createServer(handleRequest);
www.listen(8080);

$ node server.js

Docker 컨테이너 이미지 만들기

빌드할 이미지에 대한 설명이 정의되어있는 Dockerfile을 만듭니다(Dockerfile). Docker 컨테이너 이미지는 기존의 다른 이미지에서 확장할 수 있습니다(현 실습에서는 기존 Node 이미지를 확장).

FROM node:6.9.2 //Docker Hub에 있는 node 이미지에서 시작한다.
EXPOSE 8080 // 포트 8080을 노출시킨다.
COPY server.js . //위에서 만든 server.js 파일을 이미지로 복사한다.
CMD node server.js //노드 서버를 시작한다.

//todo : Image란?

: 쉽게 VM 사용 시 사용되는 '.iso' 파일이라고 보시면 됩니다.

//todo : Dockerfile 이란?

: Docker Image의 설정 정보가 정의되어 있는 파일입니다.


이미지를 빌드하고 생성된 이미지를 테스트 해봅니다.

$ docker build -t gcr.io/PROJECT_ID/hello-node:v1 .
$ docker run -d -p 8080:8080 gcr.io/PROJECT_ID/hello-node:v1
913aeb31ba101b493a169d0cd7c657d03d0fb705000f123f86a0e682c4bfdd3f

실행시킨 이미지에 curl 혹은 wget 을 사용하여 접근해봅니다.

$ curl <http://localhost:8080>
Hello World!

Docker 컨테이너의 ID를 확인합니다.

$ docker ps
CONTAINER ID        IMAGE                                                COMMAND                  CREATED             STATUS              PORTS                    NAMES
913aeb31ba10        gcr.io/PROJECT_ID/hello-node:v1   "/bin/sh -c 'node se…"   57 seconds ago      Up 57 seconds       0.0.0.0:8080->8080/tcp   vibrant_mirzakhani

//todo : docker ps 란?

: 컨테이너 목록을 출력하는 커멘드입니다.


출력된 리스트에 있는 CONTAINER ID를 사용하여 실행중인 컨테이너를 중지시킵니다.

$ docker stop 913aeb31ba10
913aeb31ba10

Docker 이미지의 비공개 저장소인 Google Containter Registry로 이미지를 Push합니다. 해당 저장소는 Google Cloud 프로젝트에서 접근할 수 있습니다.

$ gcloud docker -- push gcr.io/PROJECT_ID/hello-node:v1
WARNING: `gcloud docker` will not be supported for Docker client versions above 18.03.

As an alternative, use `gcloud auth configure-docker` to configure `docker` to
use `gcloud` as a credential helper, then use `docker` as you would for non-GCR
registries, e.g. `docker pull gcr.io/project-id/my-image`. Add
`--verbosity=error` to silence this warning: `gcloud docker
--verbosity=error -- pull gcr.io/project-id/my-image`.

See: <https://cloud.google.com/container-registry/docs/support/deprecation-notices#gcloud-docker>

The push refers to repository [gcr.io/PROJECT_ID/hello-node]
43a58676612f: Pushed
381c97ba7dc3: Pushed
604c78617f34: Pushed
fa18e5ffd316: Pushed
0a5e2b2ddeaa: Pushed
53c779688d06: Pushed
60a0858edcd5: Pushed
b6ca02dfe5e6: Pushed
v1: digest: sha256:b964d02da9cda8a6342f2a431247d002aba0dde00fdc28f06dba538f46c760d2 size: 2002

Push된 Docker 이미지를 확인해보겠습니다. (Google Cloud Platform > Container Registry)



컨테이너 클러스터 만들기

//todo : 클러스터란

: Docker에서 클러스터는 여러 개의 노드를 하나로 묶어서 하나의 시스템같이 동작하게 하는 논리적 그룹을 의미합니다.


하나의 클러스터는 Google에서 호스팅하는 Kubernetes 마스터 API 서버와 일련의 작업자 노드로 구성됩니다. 작업자 노드는 Compute Engine 가상 머신입니다. 하위 커멘드를 사용하여 Kubernetes 클러스터를 만듭니다.

$ gcloud config set project PROJECT_ID
$ gcloud container clusters create hello-world --num-nodes 2 --machine-type n1-standard-1 --zone us-central1-f
WARNING: Starting in 1.12, new clusters will have basic authentication disabled by default. Basic aut
hentication can be enabled (or disabled) manually using the `--[no-]enable-basic-auth` flag.
... (생략) ...
Created [<https://container.googleapis.com/v1/projects/PROJECT_ID/zones/us-central1-f/clusters/hello-world>].
To inspect the contents of your cluster, go to: <https://console.cloud.google.com/kubernetes/workload_/gcloud/us-central1-f/hello-world?project=PROJECT_ID>
kubeconfig entry generated for hello-world.
NAME         LOCATION       MASTER_VERSION  MASTER_IP       MACHINE_TYPE   NODE_VERSION  NUM_NODES  STATUS
hello-world  us-central1-f  1.10.9-gke.5    35.232.148.169  n1-standard-1  1.10.9-gke.5  2          RUNNING

사용자의 어플리케이션을 컨테이너화하여 Kubernetes 클러스터에 배포할 수 있게 되었습니다.

Kubernetes 포드 만들기

Kubernetes 포드는 관리 및 네트워킹 용도로 서로 연결된 컨테이너 그룹입니다. 포드는 하나 이상의 컨테이너를 포함할 수 있습니다. 포드를 생성해보겠습니다.

//todo : 포드(pod)란?

: Kubernetes의 기본 구성 요소입니다. 쿠버네티스 객체 모델 중 만들고 배포할 수 있는 가장 작고 간단한 단위입니다. (https://kubernetes.io/ko/docs/concepts/workloads/pods/pod-overview/)


$ kubectl run hello-node --image=gcr.io/PROJECT_ID/hello-node:v1 --port=8080
deployment.apps "hello-node" created

배포 개체를 만들었습니다(커멘드가 성공적으로 실행될 경우).

//todo : 배포 개체(deployment object)란?

: 어플리케이션의 배포/삭제, scale out의 역활을 합니다. Deployment를 생성하면 Deployment가 Pod와 ReplicaSets를 함께 생성합니다. (http://tech.cloudz-labs.io/posts/kubernetes/getting-start/)


만들어진 배포 개체 및 배포에 의해 만들어진 포드를 확인합니다.

$ kubectl get deployments
NAME         DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
hello-node   1         1         1            0           27s

$ kubectl get pods
NAME                          READY     STATUS    RESTARTS   AGE
hello-node-69b885678d-qxrg4   1/1       Running   0          38s

이 외에도 kubectl은 여러 명령어를 지원합니다. 명령을 실행해도 클러스터의 상태는 변경되지 않으니 참고해주시기 바랍니다.

$ kubectl cluster-info
Kubernetes master is running at <https://35.232.148.169>
GLBCDefaultBackend is running at <https://35.232.148.169/api/v1/namespaces/kube-system/services/default-http-backend:http/proxy>
Heapster is running at <https://35.232.148.169/api/v1/namespaces/kube-system/services/heapster/proxy>
KubeDNS is running at <https://35.232.148.169/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy>
Metrics-server is running at <https://35.232.148.169/api/v1/namespaces/kube-system/services/https:metrics-server:/proxy>

To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.

kubectl config view
apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: REDACTED
    server: <https://35.232.148.169>
  name: gke_PROJECT_ID_us-central1-f_hello-world
contexts:
- context:
    cluster: gke_PROJECT_ID_us-central1-f_hello-world
    user: gke_PROJECT_ID_us-central1-f_hello-world
  name: gke_PROJECT_ID_us-central1-f_hello-world
current-context: gke_PROJECT_ID_us-central1-f_hello-world
kind: Config
preferences: {}
users:
- name: gke_PROJECT_ID_us-central1-f_hello-world
  user:
    auth-provider:
      config:
        access-token: ya29.GqMBkAZpPmhjHdegw-v24yZJUMC-PXea8ZnPUD1DOXktFeA8rRIhruh6IK1LPvO9p6opl9hQJXlpdnc-cMB9T3ReTRviwJ25BWQfxSzmM2iQAuScQR39-IIGFvtK1Y_qIV2HQ2KUVcWdi4HlK8RoTnQY8vpc_rHjJxbbVva5RUAjxlZfTJyWOi38flVlfKe-a8BQB-OfrzvqjF8ZyxYeXJ9YoSiGKw
        cmd-args: config config-helper --format=json
        cmd-path: /google/google-cloud-sdk/bin/gcloud
        expiry: 2019-01-13T09:40:30Z
        expiry-key: '{.credential.token_expiry}'
        token-key: '{.credential.access_token}'
      name: gcp

문제 해결에는 다음과 같은 명령어를 사용할 수 있습니다.

$ kubectl get events
LAST SEEN   FIRST SEEN   COUNT     NAME                                                          KIND         SUBOBJECT                     TYPE      REASON                    SOURCE                                                       MESSAGE
5m          5m           1         gke-hello-world-default-pool-831d97f9-5rnq.15795c8caa4285f1   Node                                       Normal    Starting                  kubelet, gke-hello-world-default-pool-831d97f9-5rnq          Starting kubelet.
... (생략) ...

$ kubectl logs hello-node-69b885678d-qxrg4

외부 트래픽 허용

포드는 기본적으로 클러스터에 포함된 내부 IP로만 엑세스할 수 있습니다. Kubernetes 가상 네트워크 외부에서 hello-node 컨테이너에 엑세스할 수 있도록 하려면 포드를 Kubernetes 서비스로서 노출시켜야 합니다.

$ kubectl expose deployment hello-node --type="LoadBalancer" (외부에서 엑세스 가능한 IP를 만들기 위해 LoadBalancer 플래그를 사용합니다.)
service "hello-node" exposed

LoadBalancer (Compute Engine 부하 분산기)를 사용할 것이라는 명시로, 배포를 노출시키고 있습니다. 이렇게 하면 서비스가 이 배포에서 관리하는 모든 포드에 걸쳐 트래픽의 부하를 분산하게 됩니다.

Kubernetes 마스터가 Google Cloud Platform 외부에서도 서비스 제한 없이 엑세스할 수 있도록 관련 Compute Engine 전달 규칙, 대상 풀, 방화벽 규칙과 LoadBalancer를 생성합니다. 우선 클러스터 서비스를 모두 나열하여 네트워크 외부에서 서비스에 공개적으로 엑세스할 수 있는 IP 주소를 확인합니다.

$ kubectl get services
NAME         TYPE           CLUSTER-IP    EXTERNAL-IP      PORT(S)          AGE
hello-node   LoadBalancer   10.3.254.52   35.193.186.234   8080:31964/TCP   1m
kubernetes   ClusterIP      10.3.240.1    <none>           443/TCP          8m

서비스에서 사용할 수 있는 IP 를 확인할 수 있습니다. IP 주소가 2개(CLUSTER-IP, EXTERNER-IP) 나열되는데 하나는 내부 IP로서 클라우드 가상 네트워크 내부에서만 사용할 수 있고, 다른 하나는 외부 부하 분산 IP입니다.

브라우저에 다음 주소를 입력하여 서비스에 엑세스할 수 있습니다.



작업 부하를 어느 호스트에서 실행할지 지정할 필요가 없고, 서비스 모니터링 및 재시작 기능을 이용할 수 있습니다.

서비스의 규모 확장하기

어플리케이션의 확장성은 Kubernetes가 제공하는 강력한 기능 중 하나입니다.

복제 컨트롤러에 포드의 새로운 복제 여러 개를 관리할 수 있습니다.

$ kubectl scale deployment hello-node --replicas=4
deployment.extensions "hello-node" scaled

$ kubectl get deployment
NAME         DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
hello-node   4         4         4            2           8m

$ kubectl get pods
NAME                          READY     STATUS    RESTARTS   AGE
hello-node-69b885678d-44bbl   1/1       Running   0          31s
hello-node-69b885678d-8xktl   1/1       Running   0          31s
hello-node-69b885678d-ntlhw   1/1       Running   0          31s
hello-node-69b885678d-qxrg4   1/1       Running   0          8m

선언 접근법을 사용하고 있는데, 신규 인스턴스를 시작하거나 중지하는 대신 하시 몇 개의 인스텆스가 실행되어야 할지 선언했습니다. Kubernetes의 조정 루프는 사용자가 요청한 대로 구현되도록 조정하고, 필요시 조취를 취합니다.



서비스의 업그레이드 롤아웃

프로덕션 환경에 배포한 어플리케이션에 버그 수정이나 추가 기능을 적용해야 할 때, 무중단 배포가 가능합니다(server.js).

var http = require('http');
var handleRequest = function(request, response) {
  response.writeHead(200);
- response.end('Hello World');
+ response.end('Hello Kubernetes World');
}
var www = http.createServer(handleRequest);
www.listen(8080);

새로운 태그 번호(v2)를 이용하여 신규 컨테이너 이미지를 빌드하고 저장소에 Push합니다.

$ docker build -t gcr.io/PROJECT_ID/hello-node:v2 .
$ gcloud docker -- push gcr.io/PROJECT_ID/hello-node:v2

Kubernetes가 신규 어플리케이션 버전으로 복제 컨트롤러를 원할하게 업데이트 해줍니다. 실행중인 컨테이너의 이미지 라벨을 변경하려면 기존 deployment를 수정하여 이미지를 변경하면 됩니다. kubectl edit 커멘드를 사용하여 config를 수정 및 업데이트하게 되면 포드를 새로운 이미지로 업데이트하라고 배포에 알려주게 됩니다.

$ kubectl edit deployment hello-node

'image' 항목을 업데이트 합니다.

# Please edit the object below. Lines beginning with a '#' will be ignored,
# and an empty file will abort the edit. If an error occurs while saving this file will be
# reopened with the relevant failures.
#
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  annotations:
    deployment.kubernetes.io/revision: "1"
  creationTimestamp: 2016-03-24T17:55:28Z
  generation: 3
  labels:
    run: hello-node
  name: hello-node
  namespace: default
  resourceVersion: "151017"
  selfLink: /apis/extensions/v1beta1/namespaces/default/deployments/hello-node
  uid: 981fe302-f1e9-11e5-9a78-42010af00005
spec:
  replicas: 4
  selector:
    matchLabels:
      run: hello-node
  strategy:
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 1
    type: RollingUpdate
  template:
    metadata:
      creationTimestamp: null
      labels:
        run: hello-node
    spec:
      containers:
-     - image: gcr.io/PROJECT_ID/hello-node:v1
+     - image: gcr.io/PROJECT_ID/hello-node:v2
        imagePullPolicy: IfNotPresent
        name: hello-node
        ports:
        - containerPort: 8080
          protocol: TCP
        resources: {}
        terminationMessagePath: /dev/termination-log
      dnsPolicy: ClusterFirst
      restartPolicy: Always
      securityContext: {}
      terminationGracePeriodSeconds: 30

성공시 아래의 출력이 발생합니다.

deployment.extensions "hello-node" edited

수정이 완료되면 새로운 이미지로 배포를 업데이트 합니다. 새로운 이미지를 갖는 새로운 포드가 만들어지고, 기존 포드는 삭제됩니다.

$ kubectl get deployments
NAME         DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
hello-node   4         4         4            3           13m

$ kubectl get deployments
NAME         DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
hello-node   4         4         4            4           14m

새로운 버전의 어플리케이션이 배포되는 동안 서비스 사용자들은 어떠한 방해도 받지 않습니다.

Kubernetes 그래픽 대시보드

Kubernetes 최신 버전에서(몇버전 부터?) 그래픽 웹 사용자 인터페이스(대시보드)가 도입되었습니다. 댓보드에서는 CLI에서 사용할 수 있는 기능 중 일부를 빠르게 찾아서 사용하며 시스템과 간편하게 상호작용 할 수 있습니다. Kubernetes 클러스터 대시보드에 엑세스해보겠습니다.

$ gcloud container clusters get-credentials hello-world --zone us-central1-f --project PROJECT_ID
Fetching cluster endpoint and auth data.
kubeconfig entry generated for hello-world.

$ kubectl proxy --port 8081
Starting to serve on 127.0.0.1:8081

이번 실습을 통해 사용된 다양한 부분이 서로 어떤 관계를 갖는지 하위 다이어그램을 통해 이해해보면 좋을 것 같습니다.


학습 내용 출처 : https://run.qwiklabs.com/focuses/564?parent=catalog