Notice
Recent Posts
Recent Comments
Today
Total
04-26 17:29
Archives
관리 메뉴

Jeongchul Kim

Google Cloud with Docker 본문

Docker

Google Cloud with Docker

김 정출 2018. 3. 6. 02:11


Google Cloud with Docker



Docker란 무엇일까요? Docker는 application 개발과 배포(deploying)와 실행을 위한 개방형 플랫폼입니다. Docker는 application을 보다 빠르게 제공하고 실행하도록 설계되었습니다. Docker를 사용하면 infrastructure에서 application을 분리하고 application을 관리하는 infrastructure로 처리할 수 있습니다. Docker는 코드를 신속하게 옮길 수 있고, 테스트 속도를 높이며, 배포 속도를 높이고, 개발과 실행 사이의 주기를 단축하는데 도움이 됩니다. Dockerkernel containerization(컨테이너) 특징과 workflow 기능을 결합하여, application을 관리하고 배포하는데 도움이 되는 도구입니다.


Docker의 컨테이너(container)는 Kubernetes에서 직접 사용할 수 있으며, 직접 Kubernetes 엔진에서 직접 사용할 수 있습니다. Docker의 핵심을 배우면서 Kubernetes Engine을 사용하여 더 나은 환경을 경험할 수 있습니다.


이 블로그에서 배우게 될 것은

 1. Docker containers를 빌드, 실행, 디버깅하는 방법

 2. Docker hubGoogle Container Registry에서 Docker image를 Pull로 가져오는 방법

 3. Docker image를 Google Container Registry에 Push하는 방법 입니다.


Google Cloud Platform

https://cloud.google.com/?hl=ko

로그인을 진행하고 콘솔로 이동합니다.


콘솔로 이동하면 다음의 사이트로 보입니다.


ACTIVATE GOOGLE CLOUD SHELL

GCP(Google Cloud Platform)에서 오른쪽 상단 툴바의 Cloud Shell 아이콘을 클릭합니다.




"Start Cloud Shell" 버튼을 클릭합니다.



환경을 제공하고 연결하는 데 몇 분이 걸립니다.



이 Virtual Machine에 필요한 모든 개발 도구가 load 되어 있습니다. 영구적으로 5GB 홈 디렉토리를 제공하며, Google Cloud에서 실행되므로, 네트워크 성능과 인증(authentication)이 크게 향상됩니다 여기에서는 단순히 browser에서 실행합니다.


Cloud Shell에 연결되면, 이미 인증이된 상태며, 프로젝트는 PROJECT_ID로 설정되어 있음을 알 수 있습니다.


$ gcloud auth list     

결과는 다음과 같습니다.

Credentialed accounts:             
- <myaccount>@<mydomain>.com (active)


* Tip : gcloud는 Google Cloud Platform을 위한 강력하고 통합된 command-line 입니다.

전체 설명서는 https://cloud.google.com/sdk/gcloud 에 있습니다. Cloud Shell에 사전 설치되어 제고됩니다.

tab-completion(완성)이 제공됩니다.


$ gcloud config list project

결과는 다음과 같습니다.

[core]
project = <PROJECT_ID>


설정이 되어 있지 않다면 다음의 명령어로 프로젝트를 설정할 수 있습니다.


$ gcloud config set project <PROJECT_ID>

결과는 다음과 같습니다.

Updated property [core/project].

Hello World

언제나 그렇든 docker run hello-world를 시작해볼까요


$  docker run hello-world

결과는 다음과 같습니다.


Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world

ca4f61b1923c: Pull complete
Digest: sha256:8072a54ebb3bc136150e2f2860f00a7bf45f13eeb917cca2430fcd0054c8e51b
Status: Downloaded newer image for hello-world:latest
...


이 간단한 container는 Docker에서 Hello from docker를 반환합니다. 명령은 간단하지만 수행된 step의 수를 출력합니다. Docker daemon은 hello-world image를 검색하고, image를 로컬에 찾지 못하면 Docker Hub라는 공개 registry에서 image를 가져와(pull) container를 생성하고 실행합니다.


Docker Hub 에서 pull로 가져온 container image를 살펴봅시다.


$ docker images

결과는 다음과 같습니다.

REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
hello-world         latest              f2a91732366c        3 months ago         1.85 kB


Docker Hub 공개 registry에서 가져온 image입니다. Image ID는 image를 식별하는 SHA256 hash 값입니다. Docker daemon이 local에서 image를 찾을 수 없으면, 기본적으로 공개 registry에서 image를 검색합니다. container를 다시 실행해 봅시다.


$ docker run hello-world


두 번째 실행시, Docker daemon은 local registry에서 image를 찾고, image에서 container를 실행합니다. 다시 Docker Hub 에서 image를 가져올 필요가 없습니다.


실행중인 container를 살펴봅시다.


$ docker ps

결과는 다음과 같습니다.

CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES


실행중인 container가 없습니다. 이전의 hello-world container는 이미 종료가 되었습니다.  실행을 완료한 container를 포함하여 모든 container를 보려면 docker ps -a 를 실행해 봅시다.

$ docker ps -a

실행된 결과는 다음과 같습니다.


CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                      PORTS               NAMES
6027ecba1c39        hello-world         "/hello"            2 seconds ago       Exited (0) 1 seconds ago                        nostalgic_lamport
358d709b8341        hello-world         "/hello"            17 seconds ago      Exited (0) 16 seconds ago                       determined_jones


여기에서 보이는 Container ID는 Dockercontainer를 식별하기 위한 UUID가 만들어지며, 실행에 대한 metadata가 표시됩니다. container 이름은 무작위로 생성되지만, Docker 실행에서 지정할 수 있습니다. 특정 이름은 --name 옵션을 주어 만들 수 있습니다.


$ docker run --name [container-name] hello-world

Build

여기에서는 간단한 node application을 기반으로하는 고유한 Docker image를 빌드(build)합니다. test라는 디렉토리를 만들고, 이동합니다.


$ mkdir test && cd test


Dockefile 파일을 생성합니다.

$ vi Dockerfile


FROM node:6

WORKDIR /app
ADD . /app

EXPOSE 80

CMD ["node", "app.js"]


이 파일은 Docker daemon에게 image build를 지시합니다.


첫 행은 기본 상위 image를 지정합니다. 이 경우에는 node의 버전 6의 공식 Docker image 입니다.

두 번째 행은 container의 현재 작업 디렉토리를 설정합니다.

세 번째 행은 현재 디렉토리의 내용(“.”) 을 container에 추가합니다.

그런 다음 container의 포트(port)를 공개하여 해당 포트에 연결을 허용하며, 마지막으로 node 명령을 실행하여 프로그램을 시작합니다.


Dockerfile command references 을 확인하여 Dockerfile의 각 행을 이해해봅시다.


자 이제, image가 빌드된 이후에 실행될 node application을 작성해봅시다.


다음의 명령어를 입력하여 node application을 생성합니다.


$ vi app.js

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();
});


이것은 간단한 HTTP server로 port 80로 접속되며, Hello World를 반환합니다.


자 이제 image를 빌드해봅시다.

여기서 “.” 현재 디렉토리를 의미하며 Dockerfile 에 있는 디렉토리에서 이 명령을 실행해야 하는 위치를 지정합니다.


$ docker build -t node-app:0.1 .

결과는 다음과 같습니다.


Sending build context to Docker daemon 3.072 kB
Step 1 : FROM node:6
6: Pulling from library/node
...
...
...
Step 5 : CMD node app.js
---> Running in b677acd1edd9
---> f166cd2a9f10
Removing intermediate container b677acd1edd9
Successfully built f166cd2a9f10


-t  옵션은 image의 이름을 지정하고, : 는 tag 구문으로 image에 tag를 지정합니다. image 이름은 node-app이고 tag는 0.1 입니다. Docker image를 작성할 때 tag를 사용하는 것이 좋습니다. tag를 지정하지 않으면, tag의 기본값은 최신이며, 최신 image와 이전 iamge를 구별하기 어렵습니다. 또한 Dockerfile의 각 행이 image가 빌드 될 때 중간에 Container layer를  되는 것을 주목하세요.


우리가 만든 image를 살펴봅시다.

$ docker images

결과는 다음과 같습니다.


REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
node-app            0.1                 f166cd2a9f10        25 seconds ago      656.2 MB
node                6                   5a767079e3df        5 days ago          656.2 MB
hello-world         latest              1815c82652c0        7 weeks ago         1.84 kB


노드는 기본 image이며, node-app는 사용자가 작성한 image입니다. 먼저 node-app를 제거하지 않고, node를 제거할 수 없습니다. image의 크기는 VM에 비해 상대적으로 적습니다. node:slim 그리고 node:alpine 같은 node image의 다른 버전을 사용하면 더 작은 image를 제공하여 보다 쉽게 portability를 제공할 수 있습니다. Container의 크기를 줄이는 주제는 다음 주제에서 자세히 설명하겠습니다. Official 저장소(repository)에서 모든 버전을 볼 수 있습니다.

Run

우리가 빌드(build)한 image를 기반으로 container를 실행해봅시다.


$ docker run -p 4000:80 --name my-app node-app:0.1

결과는 다음과 같습니다.


Server running at http://0.0.0.0:80/


--name 옵션을 사용하면 container의 이름을 지정할 수 있습니다. -p 옵션은 Docker가 host의 port 4000을 container의 port 80에 맵핑하도록 지시합니다. 이제 http://localhost:4000 에서 서버에 연결할 수 있습니다. port mapping이 없으면 localhost에서 container에 연결할 수 없습니다.


+ 아이콘 버튼을 클릭하여 Cloud Shell에서 다른 터미널을 열어보고 Server를 테스트 해봅시다.

$ curl http://localhost:4000

결과는 다음과 같습니다.


Hello World


container는 초기 터미널이 실행되는 동안 실행됩니다. container를 백그라운드(background)에서 실행하려면 (터미널 세션에 연결되지 않음) -d 옵션을 지정해야합니다.


초기 터미널을 닫고, container를 정지하고 제거합니다.


ctrl+c

$ docker stop my-app && docker rm my-app


백그라운드(background)에서 container를 다시 시작합니다.


$ docker run -p 4000:80 --name my-app -d node-app:0.1


$ docker ps

결과는 다음과 같습니다.


CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS                NAMES
xxxxxxxxxxxx        node-app:0.1         "node app.js"       16 seconds ago      Up 15 seconds       0.0.0.0:4000->80/tcp   my-app


$ docker logs my-app

결과는 다음과 같습니다.


Server running at http://0.0.0.0:80/


container가 docker ps의 출력에서 실행 중임을 알 수 있습니다. docker logs [container_id]를 실행하여 log를 볼 수 있습니다.


Tip: container를 고유하게 식별하는 전체 container ID를 쓸 필요는 없습니다. 예를 들어 container ID가 ‘17bcaca6f …’ 인 경우 ‘docker logs 17b’를 실행할 수 있습니다.


application을 수정합시다. 새 terminal에서 이전에 만든 테스트 디렉토리를 엽니다.


$ cd test

app.js을 편집하여, “Hello World”를 다른 문자열로 변경합니다.


$ vi app.js

....
const server = http.createServer((req, res) => {
   res.statusCode = 200;
     res.setHeader('Content-Type', 'text/plain');
       res.end('Welcome to Google Cloud\n');
});
....


이 새로운 image를 만들고 0.2로 tag 합니다. background에서 container를 실행합니다.


$ docker build -t node-app:0.2 .

Step 1 : FROM node:6
---> 5a767079e3df
Step 2 : WORKDIR /app
---> Using cache
---> 4eb63077a999
Step 3 : ADD . /app
---> 2a779c558d11
...


Step 2에서 기존 cache layer를 사용하고 있습니다. Step 3 이상부터는 app.js를 변경했기 때문에 layer가 수정되었습니다.


새 image 버전으로 다른 container를 실행합니다. host port 8080을 80으로 맵핑하는 방법에 유의합니다. host port는 port 4000은 이미 사용 중이므로 사용할 수 없습니다.


$ docker run -p 8080:80 --name my-app-2 -d node-app:0.2

$ docker ps

결과는 다음과 같습니다.

CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS                  NAMES
xxxxxxxxxxxx        node-app:0.2         "node app.js"       53 seconds ago      Up 53 seconds       0.0.0.0:8080->80/tcp   my-app-2
xxxxxxxxxxxx        node-app:0.1         "node app.js"       About an hour ago   Up About an hour    0.0.0.0:4000->80/tcp     my-app


$ curl http://localhost:8080

결과는 다음과 같습니다.

Welcome to Google Cloud


Debug

여기에서는 container debugging에 대한 tip을 배웁니다.


docker logs [container_id]를 사용하여 container의 log를 볼 수 있습니다. container가 실행 중일때 log 출력을 따르려면 -f 옵션을 사용합니다.


$ docker logs -f my-app-2


때로는 실행중인 container에서 대화식 Bash 세션(session)을 시작하기를 원할 것입니다. docker exec를 사용하여 이를 수행할 수 있습니다.


$ docker exec -it my-app-2 bash

$ ls


-it 옵션을 사용하면 pseudo-tty를 할당하고 stdin을 열린 상태로 유지함으로써 container와 상호 작용할 수 있습니다. Dockerfile에 지정된 WORKDIR 디렉토리 (/app)에서 bash가 실행되었음을 알립니다. 여기에서 디버깅 할 컨테이너 내부에 대화식 쉘(interactive shell) 세션(session)이 있습니다.


Docker inspect를 사용하여 Docker에서 container의 metadata를 검사할 수 있습니다.

$ docker inspect my-app-2

결과는 다음과 같습니다.

[
   {
       "Id": "xxxxxxxxxxxx....",
       "Created": "2018-03-05T16:11:11.261726726Z",
       "Path": "node",
       "Args": [
           "app.js"
       ],


--format 옵션을 사용하여 반환된 JSON의 특정 필드를 검사합니다. 예는 다음과 같습니다.


$ docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' my-app-2


Publish

GCR(Google Container Registry)로 image를 푸시(push)해봅시다. 그런 다음, 새로운 환경을 시뮬레이션하기 위해 모든 container와 image를 제거한 다음 pull을 하고, container를 다시 실행해봅니다. Docker container의 portability를 보여줍니다.


GCR이 호스팅하는 Private Registry에 image를 push하려면, registry name으로 image에 tag를 지정해야 합니다. 형식은 [hostname]/[project-id]/[image]:[tag] 입니다.


GCR을 위해서는

- [hostname] = gcr.io

- [project-id] = 프로젝트 ID

- [image] =  image 이름

- [tag] = tag 이름 / 기본값으로 latest가 붙습니다.


프로젝트 ID를 확인하기 위해서는 다음의 명령어를 실행합니다.


$ gcloud config list project

결과는 다음과 같습니다.


[core]
project = [project-id]

Your active configuration is: [default]


Tag는 node-app:0.2로 하구 [project-id]를 대체합니다.


$ docker tag node-app:0.2 gcr.io/[project-id]/node-app:0.2
$ docker tag node-app:0.2 gcr.io/qwiklabs-gcp-64babc3c16783418/node-app:0.2

결과는 다음과 같습니다.


REPOSITORY                              TAG                 IMAGE ID            CREATED             SIZE
node-app                                0.2                 76b3beef845e        22 hours ago        656.2 MB
gcr.io/[project-id]/node-app            0.2                 76b3beef845e        22 hours ago        656.2 MB
node-app                                0.1                 f166cd2a9f10        26 hours ago        656.2 MB
node                                    6                   5a767079e3df        7 days ago          656.2 MB
hello-world                             latest              1815c82652c0        7 weeks ago         1.84 kB


GCR로 image를 push 해봅시다. [project-id]를 변경합시다.


https://console.cloud.google.com/apis/library/containerregistry.googleapis.com/

다음의 사이트에서 Google Container Registry API에서 사용 설정 버튼을 클릭합니다.

$ gcloud docker -- push gcr.io/[project-id]/node-app:0.2
$ gcloud docker -- push gcr.io/qwiklabs-gcp-64babc3c16783418/node-app:0.2

결과는 다음과 같습니다.


The push refers to a repository [gcr.io/[project-id]/node-app]
057029400a4a: Pushed
342f14cb7e2b: Pushed
903087566d45: Pushed
99dac0782a63: Pushed
e6695624484e: Pushed
da59b99bbd3b: Pushed
5616a6292c16: Pushed
f3ed6cb59ab0: Pushed
654f45ecb7e3: Pushed
2c40c66f7667: Pushed
0.2: digest: sha256:25b8ebd7820515609517ec38dbca9086e1abef3750c0d2aff7f341407c743c46 size: 2419


웹 브라우저의 image registry를 방문하여 GCR에 이미가 있는 지 확인합니다. 콘솔을 통해서 Tools > Container Registry 로 이동하거나, 다음의 사이트로 이동합니다. http://gcr.io/[project-id]/node-app.



이 image를 테스트해 봅시다. 새 VM을 시작하고, 해당 VM에 ssh를 설치한다음 gcloud를 설치할 수 있습니다. 간단하게 하기 위해, 우리는 새로운 환경을 시뮬레이션 하기 위해 모든 container와 image를 제거합니다.


모든 container를 중지하고 제거합니다.


$ docker stop $(docker ps -q)
$ docker rm $(docker ps -aq)


node image를 제거하기 전에 node(node:6)의 하위 image를 제거해야합니다. [project-id]를 바꿉니다.

$ docker rmi node:6


$ docker rmi $(docker images -aq)


docker image가 지워졌는지 확인합니다.

$ docker images


pull을 이용해 GCR에 올라간 우리의 image를 가져옵니다.


$ gcloud docker -- pull gcr.io/[projects-id]/node-app:0.2


$ docker images


여기서 우리는 container의 portability를 보여주었습니다. Docker가 Host(사내 또는 VM)에 설치되어 있는 공용 또는 개인 registry에서 image를 가져와서 해당 image를 기반으로 container를 실행할 수 있습니다. Docker를 제외하고는 Host에 설치해야하는 application의 종속성(dependency)이 없습니다.


이상 글을 마치겠습니다.



Comments