9 분 소요

📌 Topic

  • Docker Compose란 무엇인가?
  • 애플리케이션 소스 작성하기
  • Dockerfile 작성하기
  • Docker Container간 통신 할 때 나타나는 에러
  • Docker Compose 파일 작성하기
  • Docker Compose로 컨테이너 멈추기

01. Docker Compose란 무엇인가?

Docker compose란 여러 개의 docker 컨테이너를 정의하고 한 번에 실행하는 툴이다.
Kubernetes처럼 yaml 파일로 여러 개의 docker 내부 속성을 설정하고 한 번에 실행 시킨다.
(마치 docker를 배치로 한 번에 실행시키는거라 생각하면 좋을 것 같다)

01-1. Docker compose

  • 복수 개의 컨테이너를 실행시키는 도커 애플리케이션을 정의하기 위한 툴
  • Compose를 사용하면 yaml 파일을 사용하여 애플리케이션의 서비스 구성이 가능

01-2. 프로세스

  1. 만들고자 하는 앱의 환경을 어디에서나 재사용하기 위해 Dockerfile 정의
  2. docker-compose.yml 에서 앱을 구성할 수 있는 서비스 정의
  3. docker-compose up -d 명령어를 실행한다. 후에 전체 앱을 실행 시킨다

01-3. 애플리케이션 전체 구성 보기

application.PNG

  • 실습은 2개의 컨테이너에 node.js app과 redis를 설치하여 진행한다
  • 다음 02장에서는 이러한 컨테이너를 구성하기 위한 서비스 소스 2개를 작성한다

02. 애플리케이션 소스 작성하기

이전 시간에 2개의 컨테이너에 node.js app과 redis를 각각 설치하기로 하였다.
이번에는 docker-compose를 사용하기 위한 애플리케이션 소스 작성을 진행 해보자.

02-1. docker-compose-app 프로젝트 생성

npm init
  • docker-compose-app 이라는 프로젝트를 생성한 후에 위 명령어를 입력 한다.

02-2. package.json 파일 수정

{
  "name": "docker-compose-app",
  "version": "1.0.0",
  "description": "",
  "main": "server.js",
  "scripts": {
    "start": "node server.js",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "dependencies": {
    "express": "4.17.1", // express 모듈
    "redis": "3.0.2" // redis 모듈
  },
  "author": "",
  "license": "ISC"
}
  • package.json 파일에 의존성에 관한 부분을 설정한다

02-3. server.js 파일 생성

// Import module
const express = require("express");
const redis = require("redis");

// Create new express application
const app = express();
app.listen(8080);
console.log(`Server is running`);
  • express, redis 모듈 import
  • express 모듈 실행

이제 2개의 컨테이너 서비스를 만들기 위한 모듈은 준비가 되었다.
멀티 컨테이너 서비스를 구축하기 전에 redis에 대해 간략히 정리하고 넘어가자.

02-4. redis란?

  • Key - Value 구조의 데이터 관리 시스템
  • 모든 데이터를 메모리에 저장하고 빠르게 조회할 수 있다
  • 비관계형 DB(NoSQL)

redis데이터를 메모리에 저장하기 때문에 MySQL과 같은 관계형 DB(RDB)에 비해 훨씬 빠르게 데이터를 처리할 수 있다. 또한 데이터를 메모리에 저장하지만 해당 데이터는 영속적으로 보관이 가능하여 서버를 재부팅 하여도 데이터를 유지할 수 있다는 장점이 있다.

02-5. Node.js 환경에서 Redis 사용 방법

  1. redis-server를 작동시킨다
  2. redis 모듈을 다운 받는다
  3. redis 모듈을 받은 후 redis-client를 생성하기 위해 Redis가 제공하는 createClient() 함수 이용
  4. 만약 redist server가 작동하는 곳과 Node.js 앱이 작동하는 곳이 다르면 host, port 명시 필요
  5. redis의 기본 포트는 6379

02-6. 도커 환경에서 레디스 클라이언트 생성 시 주의사항

const client = redis.createCLient({
  host: "redis-server", // docker-compose.yml에 명시한 redis image명
  port: 6379,
});

보통 도커를 사용하지 않는 환경에서는 Redis 서버가 작동되고 있는 곳의 host 옵션을 URL로 주면 되지만, docker compose를 사용할 때는 host 옵션을 docker-compose.yml 파일에 명시한 컨테이너 이름으로 줘야 한다.

02-7. 애플리케이션 소스 작성

// server.js
// Import module
const express = require("express");
const redis = require("redis");

// Create redis client
const client = redis.createClient({
  host: "redis-server", //  docker 환경 일때의 host는 다르다
  port: 6379,
});

// Create new express application
const app = express();

// 숫자는 0 부터 시작한다
client.set("number", 0);

app.get("/", (req, res) => {
  client("number", (err, number) => {
    // 현재 가져온 숫자를 1씩 증가 시킨다
    client.set("number", parseInt(number) + 1);
    res.send("숫자가 1씩 올라갑니다. 숫자: " + number);
  });
});

app.listen(8080);
console.log(`Server is running`);
  • docker compose를 사용하기 위한 간단한 app 작성

03. Dockerfile 작성하기

Node.js 이미지를 만들기 위한 Dockerfile 작성 시간을 갖는다.

03-1. Dockerfile 작성

# Dockerfile
# 베이스 이미지 지정
FROM node:10

# 컨테이너 상에서의 Work Dir 지정
WORKDIR /usr/src/app

# 현재 로컬 경로에 있는 모든 파일을 -> 컨테이너에 복사
COPY ./ ./

# 이미지 생성 전에 실행할 CMD
RUN npm install

# 컨테이너 시작시 시작될 CMD
CMD ["node", "server.js"]
  • 이전에 진행 하였던 것처럼 위와 같은 Dockerfile 생성

04. Docker Container 간 통신할 때 나타나는 에러

지금까지 docker-compose를 사용하기 위한 소스, Dockerfile 작성을 완료 하였다.
이번 시간에는 Docker Container간의 통신 시에 발생할 수 있는 에러에 대해 알아보자.

04-1. 컨테이너 구성

applicatiopn_02.PNG

우선 레디스 클라이언트를 실행 시키기 위해서는 레디스 서버가 먼저 구동 되 있어야 한다.
그러므로 일단 레디스 서버를 먼저 켠 후에 애플리케이션을 실행 해보자.

# redis 서버를 구동
$ docker run redis
1:C 09 Jul 2022 10:39:10.702 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
1:C 09 Jul 2022 10:39:10.702 # Redis version=7.0.2, bits=64, commit=00000000, modified=0, pid=1, just started
1:C 09 Jul 2022 10:39:10.702 # Warning: no config file specified, using the default config. In order to specify a config file use redis-server /path/to/redis.conf
1:M 09 Jul 2022 10:39:10.702 * monotonic clock: POSIX clock_gettime
1:M 09 Jul 2022 10:39:10.705 * Running mode=standalone, port=6379.
1:M 09 Jul 2022 10:39:10.705 # Server initialized
1:M 09 Jul 2022 10:39:10.705 # WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect.
1:M 09 Jul 2022 10:39:10.707 * Ready to accept connections

redis-client를 사용하기 위해 redis server를 docker 기반으로 하여 실행 시켰다.
다음에는 이전에 작성한 Dockerfile을 기반으로 하여 Node.js 이미지를 생성 해보자

$ docker build -t youngmin1085/docker-compose-app ./
Sending build context to Docker daemon  8.704kB
Step 1/5 : FROM node:10
 ---> 28dca6642db8
Step 2/5 : WORKDIR /usr/src/app
 ---> Using cache
 ---> 891f7a3a5ac0
Step 3/5 : COPY ./ ./
 ---> 5d9d8d44b3dd
Step 4/5 : RUN npm install
 ---> Running in 990b4f48dc64
npm notice created a lockfile as package-lock.json. You should commit this file.
npm WARN docker-compose-app@1.0.0 No description
npm WARN docker-compose-app@1.0.0 No repository field.

added 55 packages from 44 contributors and audited 55 packages in 3.821s

1 package is looking for funding
  run `npm fund` for details

found 1 high severity vulnerability
  run `npm audit fix` to fix them, or `npm audit` for details
Removing intermediate container 990b4f48dc64
 ---> 83481677c2b2
Step 5/5 : CMD ["node", "server.js"]
 ---> Running in 0dce1d81f847
Removing intermediate container 0dce1d81f847
 ---> dadadd113144
Successfully built dadadd113144
Successfully tagged youngmin1085/docker-compose-app:latest
SECURITY WARNING: You are building a Docker image from Windows against a non-Windows Docker host. All files and directories added to build context will have '-rwxr-xr-x' permissions. It is recommended to double check and reset permissions for sensitive files and directories.

Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them

docker build -t [도커 허브 계정명:이미지 이름]을 사용하여 이미지 생성
이제 준비가 되었으니 해당 이미지를 구동(run) 해보자.

docker run youngmin1085/docker-compose-app

위 명령어를 통해 Node.js 이미지를 구동 시킨다.

04-2. Node.js 앱 실행 시 redis 오류 발생

$ docker run youngmin1085/docker-compose-app
Server is running...
events.js:174
      throw er; // Unhandled 'error' event
      ^

Error: Redis connection to redis-server:6379 failed - getaddrinfo ENOTFOUND redis-server redis-server:6379
    at GetAddrInfoReqWrap.onlookup [as oncomplete] (dns.js:56:26)
Emitted 'error' event at:
    at RedisClient.on_error (/usr/src/app/node_modules/redis/index.js:341:14)
    at Socket.<anonymous> (/usr/src/app/node_modules/redis/index.js:222:14)
    at Socket.emit (events.js:198:13)
    at emitErrorNT (internal/streams/destroy.js:91:8)
    at emitErrorAndCloseNT (internal/streams/destroy.js:59:3)
    at process._tickCallback (internal/process/next_tick.js:63:19)

applicatiopn_02.PNG

일단 위와 같은 에러가 출력된 이유는 2개의 컨테이너간의 아무런 설정이 없기 때문이다. 즉, 멀티 컨테이너 상황에서 쉽게 네트워크를 연결해주기 위해 Docker Compose를 사용하면 되는데 지금부터 docker-compose 파일을 작성 해보자.

05. Docker Compose 파일 작성하기

위에서 멀티 컨테이너 간의 통신을 위해서는 Docker Compose를 사용하면 된다고 말했다.
바로 docker-compose.yml 파일을 작성 해보자.

05-1. docker-compose.yml

docker-compose.PNG

  • 2개의 컨테이너가 존재하는 상황
  • redis-server, node-js app이 존재하는 상황
    • 해당 app들은 Service로 구성이 된다

docker-compose.yml

version: "3"
services:
  redis-server:
    image: "redis"
  node-app:
    build: .
    ports:
      - "5000:8080"
  • version : 도커 컴포즈의 버전
  • services : 실행하려는 컨테이너들을 정의
    • redis-server : 컨테이너명
      • image: 컨테이너에서 사용하는 이미지
    • node-app: 컨테이너명
      • build: 현 디렉토리에 있는 Dockerfile 사용
      • ports: 포트 맵핑 로컬 포트 : 컨테이너 포트

05-2. docker compose cmd

# docker-compose up -d
docker-compose up
docker-compose up -- build
Building node-app
Sending build context to Docker daemon  10.75kB
Step 1/5 : FROM node:10 # Base Image 지정
 ---> 28dca6642db8
Step 2/5 : WORKDIR /usr/src/app # Container 내에 Work dir 지정
 ---> Using cache
 ---> 891f7a3a5ac0
Step 3/5 : COPY ./ ./ # Container에 현재 로컬 위치의 파일 복사
 ---> af92fc580667
Step 4/5 : RUN npm install # node.js 의존성 주입(package.json)
 ---> Running in 2b95a9cbef09
npm notice created a lockfile as package-lock.json. You should commit this file.
npm WARN docker-compose-app@1.0.0 No description
npm WARN docker-compose-app@1.0.0 No repository field.

added 55 packages from 44 contributors and audited 55 packages in 1.774s

1 package is looking for funding
  run `npm fund` for details

found 1 high severity vulnerability
  run `npm audit fix` to fix them, or `npm audit` for details
Removing intermediate container 2b95a9cbef09
 ---> 89eca81a8313
Step 5/5 : CMD ["node", "server.js"] # server.js 파일 실행
 ---> Running in e84867766bb6
Removing intermediate container e84867766bb6
 ---> ef6c37378ee6
Successfully built ef6c37378ee6
Successfully tagged docker-compose-app_node-app:latest
SECURITY WARNING: You are building a Docker image from Windows against a non-Windows Docker host. All files and
directories added to build context will have '-rwxr-xr-x' permissions. It is recommended to double check and reset permissions for sensitive files and directories.

Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them
Recreating docker-compose-app_node-app_1   ... done
Starting docker-compose-app_redis-server_1 ... done
Attaching to docker-compose-app_redis-server_1, docker-compose-app_node-app_1
redis-server_1  | 1:C 09 Jul 2022 11:10:51.179 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
redis-server_1  | 1:C 09 Jul 2022 11:10:51.179 # Redis version=7.0.2, bits=64, commit=00000000, modified=0, pid=1, just started
redis-server_1  | 1:C 09 Jul 2022 11:10:51.179 # Warning: no config file specified, using the default config. In order to specify a config file use redis-server /path/to/redis.conf
redis-server_1  | 1:M 09 Jul 2022 11:10:51.179 * monotonic clock: POSIX clock_gettime
redis-server_1  | 1:M 09 Jul 2022 11:10:51.180 * Running mode=standalone, port=6379.
redis-server_1  | 1:M 09 Jul 2022 11:10:51.180 # Server initialized
redis-server_1  | 1:M 09 Jul 2022 11:10:51.180 # WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect.
redis-server_1  | 1:M 09 Jul 2022 11:10:51.181 * Loading RDB produced by version 7.0.2
redis-server_1  | 1:M 09 Jul 2022 11:10:51.181 * RDB age 22 seconds
redis-server_1  | 1:M 09 Jul 2022 11:10:51.181 * RDB memory usage when created 0.87 Mb
redis-server_1  | 1:M 09 Jul 2022 11:10:51.181 * Done loading RDB, keys loaded: 1, keys expired: 0.
redis-server_1  | 1:M 09 Jul 2022 11:10:51.181 * DB loaded from disk: 0.000 seconds
redis-server_1  | 1:M 09 Jul 2022 11:10:51.181 * Ready to accept connections
node-app_1      | Server is running...
  • docker-compose를 통해 2개의 서비스를 구동
docker-compose ps # 현재 프로세스 확인
              Name                             Command               State           Ports
---------------------------------------------------------------------------------------------------
docker-compose-app_node-app_1       docker-entrypoint.sh node  ...   Up      0.0.0.0:5000->8080/tcp
docker-compose-app_redis-server_1   docker-entrypoint.sh redis ...   Up      6379/tcp
  • 2개의 서비스를 ps 명령어를 통해 확인 가능

06. Docker Compose로 컨테이너 멈추기

이번에는 멀티 컨테이너를 중지 시키는 방법을 알아보자.

06-1. Docker Compose 컨테이너 중지

docker-compose down
Stopping docker-compose-app_node-app_1     ... done
Stopping docker-compose-app_redis-server_1 ... done
Removing docker-compose-app_node-app_1     ... done
Removing docker-compose-app_redis-server_1 ... done
Removing network docker-compose-app_default
  • docker-compose up
    • 이미지가 없을 때 이미지를 빌드하고 컨테이너 시작
  • docker-compose up -d —build
    • 이미지가 있든 없든 이미지를 빌드하고 컨테이너 시작

참고 자료

댓글남기기