IT STUDY LOG

[Docker] 04. 애플리케이션 컨테이너화 본문

devops bootcamp 4/클라우드 서비스 운영

[Docker] 04. 애플리케이션 컨테이너화

roheerumi 2023. 4. 13. 11:14

# 학습 목표

  • 컨테이너 기술이 무엇인지, Docker가 왜 필요한지 알 수 있다.
  • 컨테이너와 이미지, 레지스트리가 무엇인지 이해할 수 있다.
  • 대표적인 레지스트리인 Docker Hub에서 이미지를 검색하고, 사용할 수 있다.
  • 한 개의 이미지를 이용해서 컨테이너를 구축할 수 있다.
  • 두 개 이상의 이미지를 이용해서 컨테이너를 구축하고 서로가 어떻게 연결되는 지 알 수 있다.
  • Docker CLI에서 명령어를 사용해서 이미지를 생성/수정/배포하고, 컨테이너를 생성/삭제할 수 있다.
  • Dockerfile을 이용해 이미지를 생성할 수 있다.
  • 애플리케이션을 컨테이너화할 수 있다.

 


# 학습 내용

1.  Node.js 웹 앱의 컨테이너화 (Node.js 웹 앱의 도커라이징)

Node.js 앱 생성

Step 1 : package.json 파일 생성

# package.json
{
  "name": "docker_web_app",
  "version": "1.0.0",
  "description": "Node.js on Docker",
  "author": "roheerumi  <roheerumi@example.com>",
  "main": "server.js",
  "scripts": {
    "start": "[start] roheerumi node server.js"
  },
  "dependencies": {
    "express": "^4.16.1"
  }
}

Step 2 : npm install

Step 3 :  Express.js 프레임워크로 웹앱을 정의하는 server.js 생성

$ vim server.js
$ cat server.js 
'use strict';

const express = require('express');

// 상수
const PORT = 8080;
const HOST = '0.0.0.0';

// 앱
const app = express();
app.get('/', (req, res) => {
  res.send('헬로우 월드');
});

app.listen(PORT, HOST, () => {
  console.log(`(!) Running on http://${HOST}:${PORT}`);
});

 

Dockerfile 생성

Step 1 : Dockerfile 이름으로 빈 파일을 생성

$ touch Dockerfile

Step 2 : Dockerfile 설정 - layered architecture로 작성 내용이 순차적으로 실행

# Dockerfile 내용
# 사용할 이미지 지정
FROM node:16

# 이미지 안에 애플리케이션 코드를 넣기 위한 디렉토리 설정
WORKDIR /usr/src/app

# 이 이미지에는 이미 Node.js와 NPM이 설치되어 있으므로 npm 바이너리로 앱의 의존성을 설치하기만 하면 됨
# 앱 의존성 설치
# 가능한 경우(npm@5+) package.json과 package-lock.json을 모두 복사하기 위해 와일드카드를 사용
COPY package*.json ./

# 현재는 개발 목적으로 빌드
RUN npm install
# 프로덕션을 위한 코드를 빌드하는 경우 : RUN npm ci --omit=dev

# 도커 이미지 안에 앱의 소스 코드를 추가
COPY . .

# 앱이 바인딩 되어 있는 포트와 docker 데몬 매핑
EXPOSE 8080

# 런타임을 정의하는 CMD로 앱 실행 명령어를 정의(서버를 구동하도록 node server.js을 실행하는 기본 npm start을 사용)
CMD [ "node", "server.js" ]

(1) 어떤 이미지를 사용해 빌드할 것인지 정의 

# 사용할 이미지 지정
FROM node:16

(2) 이미지 안에 애플리케이션 코드를 넣기 위한 디렉터리를 생성(애플리케이션의 작업 디렉터리)

# 이미지 안에 애플리케이션 코드를 넣기 위한 디렉토리 설정
WORKDIR /usr/src/app

(3) COPY 명령어를 사용해 파일을 복사하고 앱 의존성 설치 명령어 작성

# 이 이미지에는 이미 Node.js와 NPM이 설치되어 있으므로 npm 바이너리로 앱의 의존성을 설치하기만 하면 됨
# 앱 의존성 설치
# 가능한 경우(npm@5+) package.json과 package-lock.json을 모두 복사하기 위해 와일드카드를 사용
COPY package*.json ./

# 현재는 개발 목적으로 빌드
RUN npm install
# 프로덕션을 위한 코드를 빌드하는 경우 : RUN npm ci --omit=dev

- 작업 디렉터리 전체가 아닌 package.json 파일만을 복사하는 이유는 캐시된 Docker 레이어의 장점을 활용하기 위함(참고 레퍼런스 : 여기)

 - npm ci 커맨드는 프로덕션 환경을 위한 더 빠르고, 신뢰할 수 있고, 재현 가능한 빌드(참고 레퍼런스 : 여기)

(4) 빌드 이후 Docker 이미지 안에 앱 소스코드 넣기

# 도커 이미지 안에 앱의 소스 코드를 추가
COPY . .

(5) 포트 바인딩을 통해 docker 데몬에 매핑

# 앱이 바인딩 되어 있는 포트와 docker 데몬 매핑
EXPOSE 8080

(6) 런타임을 정의하는 CMD로 앱을 실행하는 중요 명령어를 정의

# 런타임을 정의하는 CMD로 앱 실행 명령어를 정의(서버를 구동하도록 node server.js을 실행하는 기본 npm start을 사용)
CMD [ "node", "server.js" ]

 

Dockerfile과 같은 디렉터리에 .dockerignore 파일 작성

Step 1 : 로컬 모듈, 디버깅 로그 복사를 방지해 이미지 내에서 설치한 모듈을 덮어쓰지 않도록 설정

# .dockerignore 파일
node_modules
npm-debug.log

 

이미지 빌드

Step 1 : 작성한 Dockerfile이 있는 디렉터리에서 Docker 이미지 빌드

- -t 플래그로 이미지에 태그를 추가하면 나중에 docker images 명령어로 찾는 것이 용이

$ docker build . -t <your username>/node-web-app
[+] Building 381.1s (10/10) FINISHED                                                                                                                                                                                    
 => [internal] load .dockerignore                                                                                                                                                                                  0.5s
 => => transferring context: 68B                                                                                                                                                                                   0.1s
 => [internal] load build definition from Dockerfile                                                                                                                                                               1.1s
 => => transferring dockerfile: 948B                                                                                                                                                                               0.0s
 => [internal] load metadata for docker.io/library/node:16                                                                                                                                                         3.3s
 => [1/5] FROM docker.io/library/node:16@sha256:f6cab97e26064145d6b9bc3a025dbdae84a49d54d4f0ce7f9755a849acab5492                                                                                                 325.9s
 => => resolve docker.io/library/node:16@sha256:f6cab97e26064145d6b9bc3a025dbdae84a49d54d4f0ce7f9755a849acab5492                                                                                                   0.4s
 => => sha256:f6cab97e26064145d6b9bc3a025dbdae84a49d54d4f0ce7f9755a849acab5492 776B / 776B                                                                                                                         0.0s
 => => sha256:2a6a4c07cfbed3ee02ce3ca986af5b896058d0632d0101f64fecb33ccdd11561 2.21kB / 2.21kB                                                                                                                     0.0s
 => => sha256:26ed31aaee8c72a23f99458066bef1b173830b587d33b73e080c444c3a56fa0c 7.56kB / 7.56kB                                                                                                                     0.0s
 => => sha256:4e2befb7f5d18aa27b3619ddf1b93607e62ca82d0c627557537c149893346d86 50.45MB / 50.45MB                                                                                                                   7.3s
 => => sha256:792af667f62688dbef1f3ffeeca2daa6d448a62b6cacd604f1c4fc043d6cc2a6 7.86MB / 7.86MB                                                                                                                     4.4s
 => => sha256:3e37868ebf669334a2dbdb206ac7b84d8f8d184a40dfb8c9bc501b29cda12548 10.00MB / 10.00MB                                                                                                                   3.8s
 => => sha256:591fe17e35ddac86d63495a7708b07af369e0d67d8742880f1e73cf1a205e028 51.87MB / 51.87MB                                                                                                                  15.8s
 => => sha256:5d54aff43b9d5a82025cbae68c26151d48ce3e061018e8547a567ee0de7ca4a3 4.20kB / 4.20kB                                                                                                                     8.0s
 => => extracting sha256:4e2befb7f5d18aa27b3619ddf1b93607e62ca82d0c627557537c149893346d86                                                                                                                         31.3s
 => => sha256:b9cba6e3073a1f2eac2d50c107bba6dbb76de0325854a3a0e7c6f52ae8fc5521 191.85MB / 191.85MB                                                                                                                91.1s
 => => sha256:25065f4ea402b1639bda3d01ad17a90193d0fd96ef621adbab815abca06c2d87 34.79MB / 34.79MB                                                                                                                  33.4s
 => => sha256:af7e68a31189113e65df0a44497cc86862bbc6f277b2333c71417c097280b1b1 2.27MB / 2.27MB                                                                                                                    37.0s
 => => extracting sha256:792af667f62688dbef1f3ffeeca2daa6d448a62b6cacd604f1c4fc043d6cc2a6                                                                                                                         10.1s
 => => sha256:521ff15762da6c44f2f4d25b7960f777503e80685d55b3fe2222df503849e79a 450B / 450B                                                                                                                        93.5s
 => => extracting sha256:3e37868ebf669334a2dbdb206ac7b84d8f8d184a40dfb8c9bc501b29cda12548                                                                                                                          1.7s
 => => extracting sha256:591fe17e35ddac86d63495a7708b07af369e0d67d8742880f1e73cf1a205e028                                                                                                                         16.0s
 => => extracting sha256:b9cba6e3073a1f2eac2d50c107bba6dbb76de0325854a3a0e7c6f52ae8fc5521                                                                                                                        140.1s
 => => extracting sha256:5d54aff43b9d5a82025cbae68c26151d48ce3e061018e8547a567ee0de7ca4a3                                                                                                                          0.0s
 => => extracting sha256:25065f4ea402b1639bda3d01ad17a90193d0fd96ef621adbab815abca06c2d87                                                                                                                         14.4s
 => => extracting sha256:af7e68a31189113e65df0a44497cc86862bbc6f277b2333c71417c097280b1b1                                                                                                                          0.7s
 => => extracting sha256:521ff15762da6c44f2f4d25b7960f777503e80685d55b3fe2222df503849e79a                                                                                                                          0.0s
 => [internal] load build context                                                                                                                                                                                  0.7s
 => => transferring context: 23.58kB                                                                                                                                                                               0.0s
 => [2/5] WORKDIR /usr/src/app                                                                                                                                                                                    10.8s
 => [3/5] COPY package*.json ./                                                                                                                                                                                    0.9s
 => [4/5] RUN npm install                                                                                                                                                                                         20.7s
 => [5/5] COPY . .                                                                                                                                                                                                 7.4s
 => exporting to image                                                                                                                                                                                            10.4s
 => => exporting layers                                                                                                                                                                                            8.4s
 => => writing image sha256:75e71246f7a047a09fa2365bce7e9d831fa4bafb2c5265abe29d5aec71d5cabf                                                                                                                       0.9s
 => => naming to docker.io/<your username>/node-web-app

Step 2 : 빌드한 이미지 조회

$ docker images
REPOSITORY               TAG       IMAGE ID       CREATED         SIZE
roheerumi/node-web-app   latest    75e71246f7a0   3 minutes ago   914MB

 

이미지 실행

Step 1 : 이미지 실행

- -d 옵션: 백그라운드 실행

- -p : 공개 포트를 컨테이너 내의 비공개 포트로 리다이렉트

$ docker run -p 49160:8080 -d roheerumi/node-web-app
7d8ae72b31c68d2548d3aae1af7dfccbcf8c3123ef81e517346aeaca15103f9b

Step 2 : 앱 로그 출력

$ docker ps
CONTAINER ID   IMAGE                    COMMAND                   CREATED              STATUS              PORTS                                         NAMES
7d8ae72b31c6   roheerumi/node-web-app   "docker-entrypoint.s…"   About a minute ago   Up About a minute   0.0.0.0:49160->8080/tcp, :::49160->8080/tcp   vigilant_bhabha

$ docker logs 7d8a
(!) Running on http://0.0.0.0:9878

Step 3 : exec 명령어를 통해 컨테이너 안에 들어가 보기

$ docker exec -it vigilant_bhabha /bin/bash
root@7d8ae72b31c6:/usr/src/app# ls
Dockerfile  node_modules  package-lock.json  package.json  server.js

 

테스트

Step 1 : 테스트를 위해 Docker에 매핑된 앱 포트를 확인

-  아래 예시는 Docker가 컨테이너 내의 8080 포트를 머신의 49160 포트로 매핑했다는 뜻

$ docker ps
CONTAINER ID   IMAGE                    COMMAND                   CREATED         STATUS         PORTS                                         NAMES
7d8ae72b31c6   roheerumi/node-web-app   "docker-entrypoint.s…"   4 minutes ago   Up 4 minutes   0.0.0.0:49160->8080/tcp, :::49160->8080/tcp   vigilant_bhabha

Step 2 : curl로 앱을 호출 (필요시 sudo apt-get install curl로 설치)

$ curl -i localhost:49160
HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: text/html; charset=utf-8
Content-Length: 16
ETag: W/"10-CGzrD/CGWCEkEFrrZF1re2kLDRU"
Date: Wed, 12 Apr 2023 08:17:39 GMT
Connection: keep-alive
Keep-Alive: timeout=5

헬로우 월드

 

Docker Container 중지 후 중지된 컨테이너 삭제

$ docker container stop distracted_benz 
distracted_benz

$ docker container prune
WARNING! This will remove all stopped containers.
Are you sure you want to continue? [y/N] y
Deleted Containers:
d2e80d76c127dbeae76ee0c39b554771678d98c055962baeefe86670491294ad
7d8ae72b31c68d2548d3aae1af7dfccbcf8c3123ef81e517346aeaca15103f9b

Total reclaimed space: 8B

$ docker container ps
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES

# References

- Node.js 웹 앱의 도커라이징

 

Node.js 웹 앱의 도커라이징 | Node.js

Node.js® is a JavaScript runtime built on Chrome's V8 JavaScript engine.

nodejs.org

- 공식 Node.js Docker 이미지

 

node - Official Image | Docker Hub

About Official Images Docker Official Images are a curated set of Docker open source and drop-in solution repositories. Why Official Images? These images have clear documentation, promote best practices, and are designed for the most common use cases.

hub.docker.com

- Node.js Docker 사용사례 문서

- 공식 Docker 문서

 

Docker Docs: How to build, share, and run applications

 

docs.docker.com

- Stack Overflow에 Docker 태그로 올라온 질문

 

Newest 'docker' Questions

Stack Overflow | The World’s Largest Online Community for Developers

stackoverflow.com

- Docker 레딧

 

Comments