개요

DevOps 방식에서는 정기적으로 여러 배포를 사용하여 '지속적 배포', 'Blue/Green 배포', 'Canary 배포'와 같은 애플리케이션 배포 시나리오를 관리합니다. 이 실습에서는 여러 이기종 배포가 사용되는 일반적인 시나리오를 처리할 수 있도록 컨테이너를 확장 및 관리하는 연습이 제공됩니다.

실습할 내용

  • kubectl 도구 사용 연습
  • 배포 yaml 파일 만들기
  • 배포 시작, 업데이트 및 확장
  • 배포 및 배포 스타일 업데이트 연습

기본 요건

학습을 극대화하려면 이 실습에서 다음을 권장합니다.

  • 다음 Google Cloud Skills Boost 실습을 수강했습니다.

  • Docker 소개

  • Hello Node Kubernetes

  • Linux 시스템 관리 기술이 있습니다.

  • DevOps 이론: 지속적인 배포의 개념을 이해합니다.

배포 소개

이기종 배포에서는 일반적으로 특정한 기술적 요구 또는 운영상의 요구를 충족하기 위해 2개 이상의 상이한 인프라 환경 또는 리전을 연결합니다. 이기종 배포는 배포 특성에 따라 '하이브리드', '다중 클라우드' 또는 '공용/사설'이라고 부릅니다.

이 실습에서 이기종 배포에는 단일 클라우드 환경이나 다중 공용 클라우드 환경(다중 클라우드), 또는 온프레미스와 공용 클라우드가 조합된 환경(하이브리드 또는 공용/사설)에서 진행하는 다수의 리전에 걸친 배포가 포함됩니다.

단일 환경 또는 리전에 한정된 배포에서는 다양한 비즈니스 및 기술적 난점이 발생할 수 있습니다.

  • 여유 리소스 부족: 단일 환경, 특히 온프레미스 환경에서는 프로덕션 요구를 충족시킬 수 있는 컴퓨팅, 네트워킹, 저장소 리소스가 모자랄 수 있습니다.
  • 제한된 지리적 범위: 단일 환경에서의 배포를 위해서는 지리적으로 서로 멀리 떨어진 사용자들이 하나의 배포에 액세스해야 합니다. 이러한 사용자의 트래픽은 특정 위치까지 전 세계를 돌아서 이동합니다.
  • 제한된 가용성: 웹 규모의 트래픽 패턴에서는 애플리케이션의 내결함성 및 탄력성이 상당히 요구됩니다.
  • 공급업체 고착화: 공급업체 수준의 플랫폼 및 인프라 추상화로 인해 애플리케이션 이식이 어려울 수 있습니다.
  • 유연하지 않은 리소스: 특정 컴퓨팅, 저장소 또는 네트워킹 오퍼링 집합으로 리소스가 제한될 수 있습니다.

이기종 배포는 이러한 문제를 해결하는 데 도움이 될 수 있지만, 프로그래매틱하며 결정론적인 프로세스와 절차를 사용해서 아키텍처를 구성해야 합니다. 일회성 또는 임시 배포 절차는 배포 또는 프로세스의 취약성을 높이고 내결함성을 저하시킬 수 있습니다. 임시 프로세스는 데이터 손실 또는 트래픽 누락을 일으킬 수 있습니다. 올바른 배포 프로세스는 반복 가능해야 하며, 입증된 프로비저닝, 구성, 유지 관리 방식을 사용해야 합니다.

이기종 배포를 위한 일반적인 시나리오는 다중 클라우드 배포, 온프레미스 데이터 프론팅, CI/CD(지속적 통합/지속적 배포) 프로세스입니다.

다음 실습에서는 Kubernetes 및 다른 인프라 리소스를 사용한 잘 구성된 접근 방법과 함께 이기종 배포를 위한 몇 가지 일반적인 사용 사례를 연습합니다

영역 설정

  • 다음 명령어를 실행하여 GCP 영역을 설정하고, 로컬 영역을 로 대체합니다.
    $ gcloud config set compute/zone us-east4-a
  1. 샘플코드 가져오기
    $ gsutil -m cp -r gs://spls/gsp053/orchestrate-with-kubernetes .
    $ cd orchestrate-with-kubernetes/kubernetes
  2. 노드 3개로 클러스터를 만듭니다. 이 작업은 완료하는 데 몇 분 정도 걸릴 수 있습니다.
    $ gcloud container clusters create bootcamp \
    --machine-type e2-small \
    --num-nodes 3 \
    --scopes "https://www.googleapis.com/auth/projecthosting,storage-rw"

작업 1. 배포 객체에 관해 알아보기

배포를 시작해 보겠습니다. 먼저 배포 객체를 살펴보겠습니다.

  1. kubectlexplain 명령어를 통해 배포 객체에 관해 알 수 있습니다.
    $ kubectl explain deployment
    

KIND: Deployment
VERSION: apps/v1

DESCRIPTION:
Deployment enables declarative updates for Pods and ReplicaSets.

FIELDS:
apiVersion
APIVersion defines the versioned schema of this representation of an
object. Servers should convert recognized schemas to the latest internal
value, and may reject unrecognized values. More info:
https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources

kind
Kind is a string value representing the REST resource this object
represents. Servers may infer this from the endpoint the client submits
requests to. Cannot be updated. In CamelCase. More info:
https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds

metadata
Standard object's metadata. More info:
https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata

spec
Specification of the desired behavior of the Deployment.

status
Most recently observed status of the Deployment.

2.  `--recursive` 옵션을 사용하여 모든 필드를 볼 수도 있습니다.
```bash
$ kubectl explain deployment --recursive
  1. 실습을 진행하는 과정에서 explain 명령어를 사용하면 배포 객체의 구조를 이해하고 개별 필드의 기능을 이해하는 데 도움이 됩니다.
    $ kubectl explain deployment.metadata.name
    

KIND: Deployment
VERSION: apps/v1

FIELD: name

DESCRIPTION:
Name must be unique within a namespace. Is required when creating
resources, although some resources may allow a client to request the
generation of an appropriate name automatically. Name is primarily intended
for creation idempotence and configuration definition. Cannot be updated.
More info: http://kubernetes.io/docs/user-guide/identifiers#names


## 작업 2. 배포 만들기

1.  `deployments/auth.yaml` 구성 파일을 업데이트합니다.
```bash
vi deployments/auth.yaml
  1. 배포의 containers 섹션에 있는 image를 다음과 같이 변경합니다.
    ```vim
    ...
    containers:
  • name: auth
    image: kelseyhightower/auth:1.0.0
    ...
  1. 이제 간단한 배포를 만들겠습니다. 배포 구성 파일을 검사합니다.
    $ cat deployments/auth.yaml
    

apiVersion: apps/v1
kind: Deployment
metadata:
name: auth
spec:
replicas: 1
selector:
matchLabels:
app: auth
template:
metadata:
labels:
app: auth
track: stable
spec:
containers:
- name: auth
image: "kelseyhightower/auth:1.0.0"
ports:
- name: http
containerPort: 80
- name: health
containerPort: 81
...


배포를 통해 어떻게 하나의 복제본이 생성되고 버전 1.0.0의 인증 컨테이너를 사용하는지 확인하세요.

`kubectl create` 명령어를 실행하여 인증 배포를 만들면 배포 매니페스트의 데이터에 따라 하나의 포드가 생성됩니다. 즉, `replicas` 필드에 지정된 숫자를 변경하여 포드의 수를 조정할 수 있습니다.

6.  `kubectl create`를 사용하여 배포 객체를 만듭니다.
```bash
$ kubectl create -f deployments/auth.yaml
deployment.apps/auth created
  1. 배포를 만들면 생성 여부를 확인할 수 있습니다.
    $ kubectl get deployments
    NAME   READY   UP-TO-DATE   AVAILABLE   AGE
    auth   1/1     1            1           23s
    

$ kubectl get deployments -o wide
NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR
auth 1/1 1 1 33s auth kelseyhightower/auth:1.0.0 app=auth

8.  배포가 생성되면, Kubernetes에서는 배포에 관한 ReplicaSet를 만듭니다. 배포에 관한 ReplicaSet가 생성되었는지 확인할 수 있습니다.
```bash
$ kubectl get replicasets
NAME             DESIRED   CURRENT   READY   AGE
auth-5c65b6d58   1         1         1       103s

이름이 auth-xxxxxxx인 ReplicaSet가 표시되어야 합니다.

  1. 마지막으로, 배포의 일부로 생성된 포드를 볼 수 있습니다. ReplicaSet가 생성될 때 Kubernetes에서 단일 포드를 생성합니다.
    $ kubectl get pods
    NAME                   READY   STATUS    RESTARTS   AGE
    auth-5c65b6d58-tqwbn   1/1     Running   0          2m3s
  2. 이제 인증을 배포하기 위한 서비스를 만들 차례입니다. 서비스 매니페스트 파일은 이미 살펴보았으므로 여기서는 자세히 설명하지 않겠습니다. kubectl create 명령어를 사용하여 인증 서비스를 만듭니다.
    $ kubectl create -f services/auth.yaml
    service/auth created
  3. 이제 같은 방법으로 hello 배포를 만들고 노출합니다.
    $ kubectl create -f deployments/hello.yaml
    deployment.apps/hello created
    $ kubectl create -f services/hello.yaml
    service/hello created
  4. 한번 더 frontend 배포를 만들고 노출합니다.
    $ kubectl create secret generic tls-certs --from-file tls/
    $ kubectl create configmap nginx-frontend-conf --from-file=nginx/frontend.conf
    $ kubectl create -f deployments/frontend.yaml
    $ kubectl create -f services/frontend.yaml
    

configmap/nginx-frontend-conf created
deployment.apps/frontend created
service/frontend created

> **참고:** 프런트엔드용 ConfigMap을 만들었습니다.

13.  외부 IP를 가져와서 프런트엔드와 연결함으로써 프런트엔드와 상호작용합니다.
```bash
$ kubectl get services frontend
NAME       TYPE           CLUSTER-IP    EXTERNAL-IP     PORT(S)         AGE
frontend   LoadBalancer   10.88.6.204   35.199.58.119   443:30354/TCP   38s

참고: 서비스에 대해 ExternalIP 필드가 채워지는 데 몇 초 정도 걸릴 수 있습니다. 이것은 정상입니다. 필드가 채워질 때까지 몇 초마다 위의 명령을 다시 실행하십시오.

$ curl -ks https://<EXTERNAL-IP>
{"message":"Hello"}
  1. kubectl의 출력 템플릿 기능을 사용하여 curl을 한 줄 명령어로 사용할 수도 있습니다.
    $ curl -ks https://`kubectl get svc frontend -o=jsonpath="{.status.loadBalancer.ingress[0].ip}"`

배포 확장

이제 배포가 생성되었으므로 확장할 수 있습니다. spec.replicas 필드를 업데이트하면 됩니다.

  1. kubectl explain 명령어를 다시 사용하여 이 필드에 관한 설명을 볼 수 있습니다.
    $ kubectl explain deployment.spec.replicas
    KIND:     Deployment
    VERSION:  apps/v1
    

FIELD: replicas

DESCRIPTION:
Number of desired pods. This is a pointer to distinguish between explicit
zero and not specified. Defaults to 1.

2.  replicas 필드를 가장 쉽게 업데이트하는 방법은 `kubectl scale` 명령어를 사용하는 것입니다.
```bash
$ kubectl scale deployment hello --replicas=5
deployment.apps/hello scaled

참고: 새 pod가 모두 시작되는 데는 1~2분 정도 걸릴 수 있습니다.

배포가 업데이트된 후, Kubernetes는 연결된 ReplicaSet를 자동으로 업데이트하고 새로운 pod를 시작하여 pod의 총 개수를 5로 만듭니다.

  1. 현재 hello 포드가 5개 실행되고 있는지 확인합니다.
    $ kubectl get pods | grep hello- | wc -l
    5
  2. 이제 애플리케이션을 다시 축소합니다.
    $ kubectl scale deployment hello --replicas=3
  3. 포드 개수가 맞는지 다시 확인합니다.
    $ kubectl get pods | grep hello- | wc -l
    3
    지금까지 Kubernetes 배포와 포드 그룹을 관리하고 확장하는 방법을 알아보았습니다.

작업 3. 순차적 업데이트

배포는 순차적 업데이트 메커니즘을 통해 이미지를 새 버전으로 업데이트하도록 지원합니다. 배포가 새 버전으로 업데이트되면 새 ReplicaSet가 만들어지고, 이전 ReplicaSet의 복제본이 감소하면서 새 ReplicaSet의 복제본 수가 천천히 증가합니다.

8d107e36763fd5c1.png

순차적 업데이트 트리거하기

  1. 배포를 업데이트하려면 다음 명령어를 실행합니다.
    $ kubectl edit deployment hello
  2. 배포의 containers 섹션에 있는 image를 다음과 같이 변경합니다.
    ...
    containers:
    image: kelseyhightower/hello:2.0.0
    ...
    편집기에서 저장하면, 업데이트된 배포가 클러스터에 저장되고 Kubernetes에서 순차적 업데이트가 시작됩니다.
  3. Kubernetes에서 생성한 새로운 ReplicaSet를 확인합니다.
    $ kubectl get replicaset
    NAME                 DESIRED   CURRENT   READY   AGE
    auth-5c65b6d58       1         1         1       30m
    frontend-886c96b4d   1         1         1       9m55s
    hello-7c575694fc     1         1         1       10m
    hello-8654fb85d      3         3         2       33s
  4. 출시 기록에 새로운 항목이 표시될 수도 있습니다.
    $ kubectl rollout history deployment/hello
    eployment.apps/hello
    REVISION  CHANGE-CAUSE
    1         <none>
    2         <none>

    순차적 업데이트 일시중지하기

실행 중인 출시에 문제가 발생하면 일시중지하여 업데이트를 중지합니다.

  1. 지금 중지해보세요.
    $ kubectl rollout pause deployment/hello
    deployment.apps/hello paused
  2. 현재 출시 상태를 확인합니다.
    $ kubectl rollout status deployment/hello
    deployment "hello" successfully rolled out
  3. 포드에서 직접 확인할 수도 있습니다.
    $ kubectl get pods -o jsonpath --template='{range .items[*]}{.metadata.name}{"\t"}{"\t"}{.spec.containers[0].image}{"\n"}{end}'
    auth-5c65b6d58-tqwbn            kelseyhightower/auth:1.0.0
    frontend-886c96b4d-72rxf                nginx:1.9.14
    hello-8654fb85d-jmh7p           kelseyhightower/hello:2.0.0
    hello-8654fb85d-njgcb           kelseyhightower/hello:2.0.0
    hello-8654fb85d-rqfht           kelseyhightower/hello:2.0.0

    순차적 업데이트 재개하기

출시가 일시중지되었으므로 일부 포드는 새 버전이고 일부 포드는 이전 버전입니다.

  1. resume 명령어를 사용하여 출시를 계속 진행할 수 있습니다.
    $ kubectl rollout resume deployment/hello
    deployment.apps/hello resumed
  2. 출시가 완료되면 status 명령어를 실행할 때 다음이 표시됩니다.
    $ kubectl rollout status deployment/hello
    deployment "hello" successfully rolled out

    업데이트 롤백하기

새 버전에서 버그가 발견되었다고 가정해 보겠습니다. 새 버전에 문제가 있는 것으로 간주되므로 새 포드에 연결된 모든 사용자가 문제를 경험하게 됩니다.

이전 버전으로 롤백하여 문제를 조사한 다음 제대로 수정된 버전을 출시할 수 있습니다.

  1. rollout 명령어를 사용하여 이전 버전으로 롤백합니다.
    $ kubectl rollout undo deployment/hello
    deployment.apps/hello rolled back
  2. 기록에서 롤백을 확인합니다.
    $ kubectl rollout history deployment/hello
    deployment.apps/hello
    REVISION  CHANGE-CAUSE
    2         <none>
    3         <none>
  3. 마지막으로, 모든 포드가 이전 버전으로 롤백되었는지 확인합니다.
    $ kubectl get pods -o jsonpath --template='{range .items[*]}{.metadata.name}{"\t"}{"\t"}{.spec.containers[0].image}{"\n"}{end}'
    auth-5c65b6d58-tqwbn            kelseyhightower/auth:1.0.0
    frontend-886c96b4d-72rxf                nginx:1.9.14
    hello-7c575694fc-2lmt5          kelseyhightower/hello:1.0.0
    hello-7c575694fc-6sxmd          kelseyhightower/hello:1.0.0
    hello-7c575694fc-rjs57          kelseyhightower/hello:1.0.0

    작업 4. Canary 배포

프로덕션 환경에서 일부 사용자를 대상으로 새 배포를 테스트하려면 Canary 배포를 사용하세요. Canary 배포를 사용하면 작은 규모의 일부 사용자에게만 변경 사항을 릴리스하여 새로운 릴리스와 관련된 위험을 완화할 수 있습니다.

Canary 배포 만들기

Canary 배포는 새 버전의 별도 배포와 함께 기존 안정화 배포 및 Canary 배포를 동시에 대상으로 삼는 서비스로 구성됩니다.

48190cf58fdf2eeb.png

  1. 먼저 새 버전의 새로운 Canary 배포를 만듭니다.
    $ cat deployments/hello-canary.yaml
  2. 이제 Canary 배포를 만듭니다.
    $ kubectl create -f deployments/hello-canary.yaml
    deployment.apps/hello-canary created
  3. Canary 배포를 만들면 hellohello-canary의 두 가지 배포가 생깁니다. 다음 kubectl 명령어로 확인하세요.
    $ kubectl get deployments
    NAME           READY   UP-TO-DATE   AVAILABLE   AGE
    auth           1/1     1            1           38m
    frontend       1/1     1            1           18m
    hello          3/3     3            3           19m
    hello-canary   1/1     1            1           32s
    hello 서비스에서 선택기는 프로덕션 배포 및 Canary 배포의 pod에 모두 맞는 app:hello 선택기를 사용합니다. 그러나 Canary 배포가 포드 수가 더 적기 때문에 더 적은 수의 사용자에게 표시됩니다.

Canary 배포 확인하기

  1. 요청에서 제공되는 hello 버전을 확인할 수 있습니다.
    curl -ks https://`kubectl get svc frontend -o=jsonpath="{.status.loadBalancer.ingress[0].ip}"`/version
    {"version":"1.0.0"}

    프로덕션 환경의 Canary 배포 - 세션 어피니티

이 실습의 사례에서는 Nginx 서비스로 전송된 모든 요청이 Canary 배포에서 처리될 가능성이 있었습니다. 어떤 사용자가 Canary 배포를 통해 서비스를 받지 못하도록 하려면 다른 접근 방식이 필요합니다. 예를 들어, 애플리케이션의 UI가 변경되어 특정 사용자에게 혼동을 주지 않으려는 경우가 있을 수 있습니다. 이와 같은 경우에는 해당 사용자를 한 배포 또는 다른 배포에 '고정'해야 합니다.

서비스와 함께 세션 어피티니를 만들면 동일한 사용자에게 항상 동일한 버전을 제공할 수 있습니다. 아래 예제에서 서비스는 이전과 동일하지만 새로운 sessionAffinity 필드가 추가되어 ClientIP로 설정됩니다. IP 주소가 동일한 모든 클라이언트는 동일한 버전의 hello 애플리케이션으로 요청을 보냅니다.

kind: Service
apiVersion: v1
metadata:
  name: "hello"
spec:
  sessionAffinity: ClientIP
  selector:
    app: "hello"
  ports:
    - protocol: "TCP"
      port: 80
      targetPort: 80

이를 테스트하기 위한 환경을 설정하기가 어렵기 때문에 여기에서는 테스트할 필요가 없지만, 프로덕션 환경의 Canary 배포에는 sessionAffinity를 사용할 수 있습니다.

작업 5. Blue/Green 배포

순차적 업데이트는 최소한의 오버헤드, 최소한의 성능 영향, 최소한의 다운타임으로 애플리케이션을 배포할 수 있기 때문에 가장 좋은 업데이트 방식입니다. 그러나 배포를 모두 완료한 후에 부하 분산기를 수정하여 새 버전을 가리키도록 하는 것이 유리한 경우가 있습니다. 이 경우에는 Blue/Green 배포가 도움이 됩니다.

Kubernetes에서는 이전의 'blue' 버전용 배포와 새로운 'green' 버전용 배포를 만들어 업데이트할 수 있습니다. 'blue' 버전에 기존 hello 배포를 사용하면 라우터 역할을 하는 서비스를 통해 배포에 액세스하게 됩니다. 새 'green' 버전이 가동 및 실행되면 서비스를 업데이트하여 이 버전을 사용하도록 전환하게 됩니다.

9e624196fdaf4534.png

Blue/Green 배포의 주요 단점은 애플리케이션을 호스팅하려면 클러스터에 최소 2배의 리소스가 필요하다는 점입니다. 한 번에 두 버전의 애플리케이션을 배포하려면 먼저 클러스터에 충분한 리소스가 있는지 확인하세요.

서비스

기존의 hello 서비스를 사용하되 app:hello, version: 1.0.0으로 선택기를 업데이트하세요. 선택기는 기존의 'blue' 배포를 선택하지만 그러나 다른 버전을 사용할 것이기 때문에 'green' 배포는 선택하지 않습니다.

먼저 서비스를 업데이트합니다.

$ kubectl apply -f services/hello-blue.yaml
Warning: resource services/hello is missing the kubectl.kubernetes.io/last-applied-configuration annotation which is required by kubectl apply. kubectl apply should only be used on resources created declaratively by either kubectl create --save-config or kubectl apply. The missing annotation will be patched automatically.
service/hello configured

참고: resource service/hello is missing이라는 경고는 자동으로 패치되므로 무시하십시오.

Blue/Green 배포를 사용하여 업데이트하기

Blue/Green 배포 스타일을 지원하기 위해 새 버전용으로 새로운 'green' 배포를 만들 것입니다. Green 배포에서 버전 라벨과 이미지 경로를 업데이트합니다.

  1. Green 배포를 만듭니다.
    $ kubectl create -f deployments/hello-green.yaml
    deployment.apps/hello-green created
  2. Green 배포가 있고 제대로 시작된 경우 현재 1.0.0 버전이 아직 사용되고 있는지 확인합니다.
    $ curl -ks https://`kubectl get svc frontend -o=jsonpath="{.status.loadBalancer.ingress[0].ip}"`/version
    {"version":"1.0.0"}
  3. 이제 서비스가 새 버전을 가리키도록 업데이트합니다.
    $ kubectl apply -f services/hello-green.yaml
    service/hello configured
  4. 서비스가 업데이트되면 'green' 배포가 즉시 사용됩니다. 이제 항상 새 버전이 사용되고 있는지 확인할 수 있습니다.
    $ curl -ks https://`kubectl get svc frontend -o=jsonpath="{.status.loadBalancer.ingress[0].ip}"`/version
    {"version":"2.0.0"}

    Blue/Green 롤백

필요한 경우 같은 방법으로 이전 버전으로 롤백할 수 있습니다.

  1. 'blue' 배포가 아직 실행 중일 때 서비스를 이전 버전으로 다시 업데이트하면 됩니다.
    $ kubectl apply -f services/hello-blue.yaml
  2. 서비스를 업데이트하면 롤백이 성공적으로 완료됩니다. 사용 중인 버전이 정확한지 다시 확인합니다.
    $ curl -ks https://`kubectl get svc frontend -o=jsonpath="{.status.loadBalancer.ingress[0].ip}"`/version
    {"version":"1.0.0"}
반응형

이 실습은 입문용이며, Docker 및 컨테이너 사용 경험이 거의 없거나 전혀 없는 사용자를 대상으로 합니다. Cloud Shell 및 명령줄을 잘 안다면 좋지만 필수 사항은 아닙니다. 이 서비스 영역에 관해 좀 더 고급 단계의 자료를 찾고 있는 경우 다음 실습을 확인하세요.

준비가 됐으면 아래로 스크롤하여 실습 환경을 설정하세요.

  1. qwiklabs-gcp-03-b18cb4608533

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:

  1. The Docker client contacted the Docker daemon.
  2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
    (amd64)
  3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
  4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
$ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
https://hub.docker.com/

For more examples and ideas, visit:
https://docs.docker.com/get-started/


### docker build
```bash
cat > Dockerfile <<EOF
# Use an official Node runtime as the parent image
FROM node:lts
# Set the working directory in the container to /app
WORKDIR /app
# Copy the current directory contents into the container at /app
ADD . /app
# Make the container's port 80 available to the outside world
EXPOSE 80
# Run app.js using node when the container launches
CMD ["node", "app.js"]
EOF

이 파일에서는 Docker 데몬에 이미지를 빌드하는 방법을 안내합니다.

  • 첫 번째 줄은 기본 상위 이미지를 지정합니다. 이 경우 기본 상위 이미지는 노드 버전 장기적 지원(LTS)의 공식 Docker 이미지입니다.
  • 두 번째 줄에서 컨테이너의 (현재) 작업 디렉터리를 설정합니다.
  • 세 번째 줄에서는 현재 디렉터리의 콘텐츠("."로 표시)를 컨테이너에 추가합니다.
  • 그런 다음 컨테이너의 포트를 공개하여 해당 포트에서의 연결을 허용하고 마지막으로 노드 명령어를 실행하여 애플리케이션을 시작합니다.

참고: 시간을 내어 Dockerfile 명령어 참조를 검토하고 Dockerfile의 각 줄을 숙지하세요.

  1. 다음 명령어를 실행하여 노드 애플리케이션을 생성합니다.
$ cat > app.js <<EOF
const http = require('http');
const hostname = '0.0.0.0';
const port = 80;
const server = http.createServer((req, res) => {
    res.statusCode = 200;
    res.setHeader('Content-Type', 'text/plain');
    res.end('Hello World\n');
});
server.listen(port, hostname, () => {
    console.log('Server running at http://%s:%s/', hostname, port);
});
process.on('SIGINT', function() {
    console.log('Caught interrupt signal and will exit');
    process.exit();
});
EOF

포트 80에서 수신 대기하고 'Hello World'를 반환하는 간단한 HTTP 서버입니다.

이제 이미지를 빌드합니다.

  1. 다음 명령어를 Dockerfile이 있는 디렉터리에서 실행해야 합니다. 현재 디렉터리를 의미하는 "."에 다시 한번 유의하세요.
$ docker build -t node-app:0.1 .

[+] Building 12.3s (4/7)
 => [internal] load build definition from Dockerfile                                                                                                                                  0.1s
 => => transferring dockerfile: 393B                                                                                                                                                  0.0s
 => [internal] load .dockerignore                                                                                                                                                     0.1s
 => => transferring context: 2B
...

-tname:tag 문법을 사용하여 이미지의 이름과 태그를 지정하는 역할을 합니다. 이미지 이름은 node-app이고 태그0.1입니다. Docker 이미지를 빌드할 때는 태그를 사용하는 것이 좋습니다. 태그를 지정하지 않으면 태그가 기본값인 latest로 지정되어 최신 이미지와 기존 이미지를 구분하기 어려워집니다. 또한 이미지를 빌드할 때 위 Dockerfile의 각 행을 통해 중간 컨테이너 레이어가 만들어지는 방식을 확인하세요.

  1. 이제 다음 명령어를 실행하여 빌드한 이미지를 봅니다.
$ docker images

node는 기본 이미지이고 node-app은 빌드한 이미지입니다. node를 삭제하려면 우선 node-app을 삭제해야 합니다. 이미지의 크기는 VM에 비해 상대적으로 작습니다. node:slimnode:alpine과 같은 노드 이미지의 다른 버전을 사용하면 더 작은 이미지를 제공하여 이식성을 높일 수 있습니다. 컨테이너 크기 줄이기에 관해서는 고급 주제에서 자세히 설명하겠습니다. 노드의 공식 저장소에서 모든 버전을 확인할 수 있습니다.

docker run

  1. 이 모듈에서는 다음 코드를 사용하여 빌드한 이미지를 기반으로 하는 컨테이너를 실행합니다.
$ docker run -p 4000:80 --name my-app node-app:0.1

--name 플래그를 사용하면 원하는 경우 컨테이너 이름을 지정할 수 있습니다. -p는 Docker가 컨테이너의 포트 80에 호스트의 포트 4000을 매핑하도록 지시하는 플래그입니다. 이제 http://localhost:4000에서 서버에 접속할 수 있습니다. 포트 매핑이 없으면 localhost에서 컨테이너에 접속할 수 없습니다.

  1. 다른 터미널을 열고(Cloud Shell에서 + 아이콘을 클릭) 서버를 테스트합니다.
$ curl http://localhost:4000

초기 터미널이 실행되는 동안 컨테이너가 실행됩니다. 컨테이너를 터미널 세션에 종속시키지 않고 백그라운드에서 실행하려면 -d 플래그를 지정해야 합니다.

  1. 초기 터미널을 닫은 후 다음 명령어를 실행하여 컨테이너를 중지하고 삭제합니다.
$ docker stop my-app && docker rm my-app
  1. 이제 다음 명령어를 실행하여 백그라운드에서 컨테이너를 시작합니다.
$ docker run -p 4000:80 --name my-app -d node-app:0.1
docker ps

이제 애플리케이션을 수정합니다.

  1. Cloud Shell에서 앞서 실습에서 만든 테스트 디렉터리를 엽니다.
$ cd test
  1. 원하는 텍스트 편집기(예: nano 또는 vim)로 app.js를 편집하고 'Hello World'를 다른 문자열로 바꿉니다.
....
const server = http.createServer((req, res) => {
    res.statusCode = 200;
    res.setHeader('Content-Type', 'text/plain');
    res.end('Welcome to Cloud\n');
});
....
  1. 이 새 이미지를 빌드하고 0.2로 태그를 지정합니다.
$ docker build -t node-app:0.2 .

2단계에서 기존 캐시 레이어를 사용하고 있음을 확인할 수 있습니다. 3단계 이후부터는 app.js를 변경했기 때문에 레이어가 수정되었습니다.

  1. 새 이미지 버전으로 다른 컨테이너를 실행합니다. 이때 호스트 포트를 80 대신 8080으로 매핑하는 방법을 확인하세요. 호스트 포트 4000은 이미 사용 중이므로 사용할 수 없습니다.
$ docker run -p 8080:80 --name my-app-2 -d node-app:0.2
docker ps
  1. 컨테이너를 테스트합니다.
  2. 이제 처음 작성한 컨테이너를 테스트합니다.
    $ curl http://localhost:4000

Debug

  1. docker logs [container_id]를 사용하여 컨테이너의 로그를 볼 수 있습니다. 컨테이너가 실행 중일 때 로그 출력을 확인하려면 -f 옵션을 사용합니다.
$ docker logs -f [container_id]

Server running at http://0.0.0.0:80/

실행 중인 컨테이너에서 대화형 Bash 세션을 시작해야 할 수 있습니다.

  1. 이 경우 docker exec를 사용합니다. 다른 터미널을 열고(Cloud Shell에서 + 아이콘을 클릭) 다음 명령어를 입력합니다.
$ docker exec -it [container_id] bash

root@27baedf72cfe:/app#

-it 플래그는 pseudo-tty를 할당하고 stdin을 열린 상태로 유지하여 컨테이너와 상호작용할 수 있도록 합니다. Dockerfile에 지정된 WORKDIR 디렉터리(/app)에서 bash가 실행된 것을 확인할 수 있습니다. 이제 디버깅할 컨테이너 내에서 대화형 셸 세션을 사용할 수 있습니다.

  1. Bash 세션을 종료합니다.
$ exit
  1. Docker inspect를 통해 Docker에서 컨테이너의 메타데이터를 검토할 수 있습니다.
  2. $ docker inspect [container_id]
  3. --format을 사용하여 반환된 JSON의 특정 필드를 검사합니다. 다음은 예시입니다.
$ docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' [container_id]

172.18.0.3

Deploy

이제 이미지를 Google Artifact Registry로 푸시합니다. 그런 다음 모든 컨테이너와 이미지를 삭제하여 새로운 환경을 시뮬레이션하고 컨테이너를 가져와서 실행합니다. 이를 통해 Docker 컨테이너의 이식성을 확인할 수 있습니다.

Artifact Registry에서 호스팅하는 비공개 레지스트리에 이미지를 푸시하려면 이미지에 레지스트리 이름으로 태그를 지정해야 합니다. 형식은 <regional-repository>-docker.pkg.dev/my-project/my-repo/my-image입니다.

대상 Docker 저장소 만들기

이미지를 푸시하려면 먼저 저장소를 만들어야 합니다. 이미지를 푸시해도 저장소 만들기가 트리거되지 않으며 Cloud Build 서비스 계정에는 저장소를 만들 권한이 없습니다.

  1. 탐색 메뉴의 CI/CD에서 Artifact Registry > 저장소로 이동합니다.
  2. 저장소 만들기를 클릭합니다.
  3. 저장소 이름으로 my-repository를 지정합니다.
  4. 형식으로 Docker를 선택합니다.
  5. 위치 유형에서 리전을 선택한 후 us-central1 (Iowa) 위치를 선택합니다.
  6. 만들기를 클릭합니다.

인증 구성하기

이미지를 푸시하거나 가져오려면 먼저 Docker가 Artifact Registry에 대한 요청을 인증하는 데 Google Cloud CLI를 사용하도록 구성해야 합니다.

  1. us-central1 리전의 Docker 저장소에 인증을 설정하려면 Cloud Shell에서 다음 명령어를 실행합니다.
    $ gcloud auth configure-docker us-central1-docker.pkg.dev
    

WARNING: Your config file at [/home/student_02_32afeda26918/.docker/config.json] contains these credential helper entries:

{
"credHelpers": {
"gcr.io": "gcloud",
"us.gcr.io": "gcloud",
"eu.gcr.io": "gcloud",
"asia.gcr.io": "gcloud",
"staging-k8s.gcr.io": "gcloud",
"marketplace.gcr.io": "gcloud"
}
}
Adding credentials for: us-central1-docker.pkg.dev
After update, the following will be written to your Docker config file located at [/home/student_02_32afeda26918/.docker/config.json]:
{
"credHelpers": {
"gcr.io": "gcloud",
"us.gcr.io": "gcloud",
"eu.gcr.io": "gcloud",
"asia.gcr.io": "gcloud",
"staging-k8s.gcr.io": "gcloud",
"marketplace.gcr.io": "gcloud",
"us-central1-docker.pkg.dev": "gcloud"
}
}

Do you want to continue (Y/n)?

Docker configuration file updated.


2.  메시지가 표시되면 `Y`를 입력합니다.

이 명령어는 Docker 구성을 업데이트합니다. 이제 Google Cloud 프로젝트의 Artifact Registry와 연결하여 이미지를 푸시하고 가져올 수 있습니다.

#### 컨테이너를 Artifact Registry로 푸시하기

1.  다음 명령어를 실행하여 프로젝트 ID를 설정하고 Dockerfile이 포함된 디렉터리로 변경합니다.
```bash
$ export PROJECT_ID=$(gcloud config get-value project)

Your active configuration is: [cloudshell-13525]

$ cd ~/test
  1. 명령어를 실행하여 node-app:0.2에 태그를 지정합니다.
  2. $ docker build -t us-central1-docker.pkg.dev/$PROJECT_ID/my-repository/node-app:0.2 . => [internal] load build definition from Dockerfile 0.0s => => transferring dockerfile: 393B 0.0s => [internal] load .dockerignore 0.0s => => transferring context: 2B 0.0s => [internal] load metadata for docker.io/library/node:lts 0.4s => [1/3] FROM docker.io/library/node:lts@sha256:671ee8d49ce2a691fc3082203c5deb9522e0c80042aa0ff40c07f4a25e63668a 0.0s => [internal] load build context 0.0s => => transferring context: 58B 0.0s => CACHED [2/3] WORKDIR /app 0.0s => CACHED [3/3] ADD . /app 0.0s => exporting to image 0.0s => => exporting layers 0.0s => => writing image sha256:36c2988fda0026b9ea6c7b151eb27f6672a5855ffa0292741c3456c21101b963 0.0s => => naming to us-central1-docker.pkg.dev/qwiklabs-gcp-03-b18cb4608533/my-repository/node-app:0.2
  3. 다음 명령어를 실행하여 빌드된 Docker 이미지를 확인합니다.
  4. $ docker images

REPOSITORY TAG IMAGE ID CREATED SIZE
node-app 0.2 36c2988fda00 15 minutes ago 997MB
us-central1-docker.pkg.dev/qwiklabs-gcp-03-b18cb4608533/my-repository/node-app 0.2 36c2988fda00 15 minutes ago 997MB
node-app 0.1 e863a062c844 21 minutes ago 997MB
hello-world latest feb5d9fea6a5 19 months ago 13.3kB
student_02_32afeda26918@cloudshell:~/test (qwiklabs-gcp-03-b18cb4608533)$ docker push us-central1-docker.pkg.dev/$PROJECT_ID/my-repository


4. 이 이미지를 Artifact Registry로 푸시합니다.

```bash
$ docker push us-central1-docker.pkg.dev/$PROJECT_ID/my-repository/node-app:0.2
  1. 빌드가 완료되면 탐색 메뉴의 CI/CD에서 Artifact Registry > 저장소로 이동합니다.
  2. my-repository를 클릭합니다. node-app Docker 컨테이너가 생성된 것을 볼 수 있습니다.

이미지 테스트하기

새로운 VM을 시작하고 SSH로 새 VM에 접속한 다음 gcloud를 설치할 수도 있지만 여기서는 간단하게 모든 컨테이너와 이미지를 삭제하여 새로운 환경을 시뮬레이션하겠습니다.

  1. 모든 컨테이너를 중지하고 삭제합니다.
    $ docker stop $(docker ps -q)
    $ docker rm $(docker ps -aq)

노드 이미지를 삭제하기 전에 (node:lts의) 하위 이미지를 삭제해야 합니다.

  1. 다음 명령어를 실행하여 모든 Docker 이미지를 삭제합니다.
    $ docker rmi us-central1-docker.pkg.dev/$PROJECT_ID/my-repository/node-app:0.2
    $ docker rmi node:lts
    $ docker rmi -f $(docker images -aq) # remove remaining images
    $ docker images

이제 새로운 환경이나 다름없습니다.

  1. 이미지를 가져와서 실행합니다.
    $ docker pull us-central1-docker.pkg.dev/$PROJECT_ID/my-repository/node-app:0.2
    $ docker run -p 4000:80 -d us-central1-docker.pkg.dev/$PROJECT_ID/my-repository/node-app:0.2
    $ curl http://localhost:4000

완료된 작업 테스트하기

내 진행 상황 확인하기를 클릭하여 실행한 작업을 확인합니다. 컨테이너 이미지를 Artifact Registry에 게시하면 평가 점수가 표시됩니다.

반응형

+ Recent posts