웹사이트 검색

Docker Compose로 개발용 Node.js 애플리케이션 컨테이너화


소개

애플리케이션을 적극적으로 개발 중인 경우 Docker를 사용하면 작업 흐름과 애플리케이션을 프로덕션에 배포하는 프로세스를 간소화할 수 있습니다. 개발 중인 컨테이너로 작업하면 다음과 같은 이점이 있습니다.

  • 환경이 일관적입니다. 즉, 시스템 충돌에 대한 걱정 없이 프로젝트에 원하는 언어와 종속성을 선택할 수 있습니다.
  • 환경이 격리되어 문제를 해결하고 새 팀원을 온보딩하기가 더 쉽습니다.
  • 환경은 이식 가능하므로 코드를 패키징하고 다른 사람과 공유할 수 있습니다.

이 튜토리얼은 Docker Compose를 위한 개발 환경을 설정하는 방법을 보여줍니다. 이 애플리케이션은 Node 및 MongoDB와 함께 작동하므로 설정은 다음을 수행합니다.

  • 호스트의 애플리케이션 코드를 컨테이너의 코드와 동기화하여 개발 중에 쉽게 변경할 수 있습니다.
  • 애플리케이션 코드에 대한 변경 사항이 다시 시작하지 않고 작동하는지 확인합니다.
  • 애플리케이션 데이터를 위한 사용자 및 암호로 보호된 데이터베이스를 생성합니다.
  • 이 데이터를 유지합니다.

이 자습서를 마치면 Docker 컨테이너에서 실행되는 작동하는 상어 정보 애플리케이션을 갖게 됩니다.

전제 조건

이 자습서를 따르려면 다음이 필요합니다.

  • sudo 권한 및 활성 방화벽이 있는 루트가 아닌 사용자와 함께 Ubuntu 18.04를 실행하는 개발 서버. 이를 설정하는 방법에 대한 지침은 이 초기 서버 설정 가이드를 참조하세요.
  • Ubuntu 18.04에서 Docker를 설치하고 사용하는 방법의 1단계와 2단계에 따라 서버에 Docker를 설치했습니다.
  • Ubuntu 18.04에 Docker Compose를 설치하는 방법의 1단계에 따라 서버에 Docker Compose를 설치했습니다.

1단계 - 프로젝트 복제 및 종속성 수정

이 설정을 빌드하는 첫 번째 단계는 프로젝트 코드를 복제하고 해당 devDependencies를 수정하여 개발 중에 사용할 것임을 지정하는 것입니다. nodemon로 애플리케이션을 실행하면 코드를 변경할 때마다 애플리케이션이 자동으로 다시 시작됩니다.

먼저 몽구스를 복제합니다.

리포지토리를 node_project라는 디렉터리에 복제합니다.

  1. git clone https://github.com/do-community/nodejs-mongo-mongoose.git node_project

node_project 디렉터리로 이동합니다.

  1. cd node_project

nano 또는 좋아하는 편집기를 사용하여 프로젝트의 package.json 파일을 엽니다.

  1. nano package.json

프로젝트 종속성 아래와 닫는 중괄호 위에 nodemon을 포함하는 새 devDependencies 개체를 만듭니다.

...
"dependencies": {
    "ejs": "^2.6.1",
    "express": "^4.16.4",
    "mongoose": "^5.4.10"
  },
  "devDependencies": {
    "nodemon": "^1.18.10"
  }    
}

편집을 마치면 파일을 저장하고 닫습니다. nano를 사용하는 경우 CTRL+X, Y, ENTER를 차례로 누릅니다.

프로젝트 코드가 제자리에 있고 해당 종속성이 수정되면 컨테이너화된 워크플로에 대한 코드 리팩터링으로 이동할 수 있습니다.

2단계 — 컨테이너와 함께 작동하도록 애플리케이션 구성

컨테이너화된 워크플로에 맞게 애플리케이션을 수정한다는 것은 코드를 더 모듈화한다는 의미입니다. 컨테이너는 환경 간 이식성을 제공하며 코드는 기본 운영 체제에서 가능한 한 분리된 상태를 유지하여 이를 반영해야 합니다. 이를 달성하기 위해 Node의 process.env 속성을 더 많이 사용하도록 코드를 리팩터링합니다. 런타임 시 사용자 환경에 대한 정보가 포함된 개체를 반환합니다. 코드에서 이 개체를 사용하여 환경 변수를 사용하여 런타임에 구성 정보를 동적으로 할당할 수 있습니다.

기본 애플리케이션 진입점인 app.js로 시작합니다. 파일 열기:

  1. nano app.js

내부에는 이 상수를 사용하여 애플리케이션이 청취할 포트를 지정하는 port listen 함수에 대한 정의가 표시됩니다.

...
const port = 8080;
...
app.listen(port, function () {
  console.log('Example app listening on port 8080!');
});

process.env 개체를 사용하여 런타임에 동적 할당을 허용하도록 port 상수를 재정의합니다. 상수 정의 및 listen 함수를 다음과 같이 변경합니다.

...
const port = process.env.PORT || 8080;
...
app.listen(port, function () {
  console.log(`Example app listening on ${port}!`);
});

새 상수 정의는 런타임에 전달된 값 또는 8080을 사용하여 동적으로 포트를 할당합니다. 마찬가지로 연결을 수신 대기할 때 포트 값을 보간하는 템플릿 리터럴을 사용하도록 listen 함수를 다시 작성했습니다. 포트를 다른 위치에 매핑하게 되므로 이러한 개정으로 인해 환경이 변경됨에 따라 이 파일을 계속해서 개정하지 않아도 됩니다.

편집이 끝나면 파일을 저장하고 닫습니다.

다음으로 데이터베이스 연결 정보를 수정하여 구성 자격 증명을 제거합니다. 다음 정보가 포함된 db.js 파일을 엽니다.

  1. nano db.js

현재 이 파일은 다음 작업을 수행합니다.

  • 애플리케이션 데이터의 스키마와 모델을 생성하는 데 사용하는 개체 문서 매퍼(ODM)인 Mongoose를 가져옵니다.
  • 사용자 이름과 암호를 포함하여 데이터베이스 자격 증명을 상수로 설정합니다.
  • mongoose.connect 메서드를 사용하여 데이터베이스에 연결합니다.

파일에 대한 자세한 내용은 MongoDB를 노드 응용 프로그램과 통합하는 방법을 참조하십시오.

파일 수정의 첫 번째 단계는 민감한 정보를 포함하는 상수를 재정의하는 것입니다. 현재 이러한 상수는 다음과 같습니다.

...
const MONGO_USERNAME = 'sammy';
const MONGO_PASSWORD = 'your_password';
const MONGO_HOSTNAME = '127.0.0.1';
const MONGO_PORT = '27017';
const MONGO_DB = 'sharkinfo';
...

이 정보를 하드코딩하는 대신 process.env 개체를 사용하여 이러한 상수의 런타임 값을 캡처할 수 있습니다. 다음과 같이 블록을 수정합니다.

...
const {
  MONGO_USERNAME,
  MONGO_PASSWORD,
  MONGO_HOSTNAME,
  MONGO_PORT,
  MONGO_DB
} = process.env;
...

편집을 마치면 파일을 저장하고 닫습니다.

이 시점에서 애플리케이션의 환경 변수와 함께 작동하도록 db.js를 수정했지만 여전히 이러한 변수를 애플리케이션에 전달할 방법이 필요합니다. 런타임 시 애플리케이션에 전달할 수 있는 값으로 .env 파일을 생성합니다.

파일 열기:

  1. nano .env

이 파일에는 db.js에서 제거한 정보(애플리케이션 데이터베이스의 사용자 이름과 암호, 포트 설정 및 데이터베이스 이름)가 포함됩니다. 여기에 나열된 사용자 이름, 암호 및 데이터베이스 이름을 자신의 정보로 업데이트해야 합니다.

MONGO_USERNAME=sammy
MONGO_PASSWORD=your_password
MONGO_PORT=27017
MONGO_DB=sharkinfo

원래 db.js에 나타난 호스트 설정을 제거했습니다. 이제 서비스 및 컨테이너에 대한 기타 정보와 함께 Docker Compose 파일 수준에서 호스트를 정의합니다.

편집을 마치면 이 파일을 저장하고 닫습니다.

.env 파일에는 민감한 정보가 포함되어 있으므로 프로젝트의 .dockerignore.gitignore 파일에 포함되어 있는지 확인해야 합니다. 버전 제어 또는 컨테이너에 복사되지 않습니다.

.dockerignore 파일을 엽니다.

  1. nano .dockerignore

파일 맨 아래에 다음 줄을 추가합니다.

...
.gitignore
.env

편집을 마치면 파일을 저장하고 닫습니다.

이 저장소의 .gitignore 파일에는 이미 .env가 포함되어 있지만 있는지 확인하십시오.

  1. nano .gitignore
...
.env
...

이 시점에서 프로젝트 코드에서 중요한 정보를 성공적으로 추출하고 이 정보가 복사되는 방법과 위치를 제어하기 위한 조치를 취했습니다. 이제 데이터베이스 연결 코드에 추가하여 컨테이너화된 워크플로에 맞게 최적화할 수 있습니다.

3단계 - 데이터베이스 연결 설정 수정

다음 단계는 애플리케이션이 데이터베이스에 연결하지 못하는 경우를 처리하는 코드를 추가하여 데이터베이스 연결 방법을 보다 강력하게 만드는 것입니다. 애플리케이션 코드에 이 수준의 복원력을 도입하는 것은 Compose를 사용하여 컨테이너로 작업할 때 권장되는 방법입니다.

편집을 위해 db.js를 엽니다.

  1. nano db.js

Mongo의 연결 URI 및 Mongoose connect 메서드에 대한 url 상수와 함께 이전에 추가한 코드를 확인하십시오.

...
const {
  MONGO_USERNAME,
  MONGO_PASSWORD,
  MONGO_HOSTNAME,
  MONGO_PORT,
  MONGO_DB
} = process.env;

const url = `mongodb://${MONGO_USERNAME}:${MONGO_PASSWORD}@${MONGO_HOSTNAME}:${MONGO_PORT}/${MONGO_DB}?authSource=admin`;

mongoose.connect(url, {useNewUrlParser: true});

현재 connect 메서드는 Mongoose가 Mongo의 새 URL 파서를 사용하도록 지시하는 옵션을 허용합니다. 이 방법에 옵션을 추가하여 재연결 시도에 대한 매개변수를 정의할 수 있습니다. 새 URL 구문 분석기 옵션 외에도 관련 정보를 포함하는 options 상수를 생성하여 이를 수행하십시오. Mongo 상수 아래에 options 상수에 대한 다음 정의를 추가합니다.

...
const {
  MONGO_USERNAME,
  MONGO_PASSWORD,
  MONGO_HOSTNAME,
  MONGO_PORT,
  MONGO_DB
} = process.env;

const options = {
  useNewUrlParser: true,
  reconnectTries: Number.MAX_VALUE,
  reconnectInterval: 500, 
  connectTimeoutMS: 10000,
};
...

reconnectTries 옵션은 Mongoose에게 무기한 연결 시도를 계속하도록 지시하는 반면, reconnectInterval은 연결 시도 간격을 밀리초 단위로 정의합니다. connectTimeoutMS는 Mongo 드라이버가 연결 시도에 실패하기 전에 대기하는 기간으로 10초를 정의합니다.

이제 Mongoose connect 메서드에서 새로운 options 상수를 사용하여 Mongoose 연결 설정을 미세 조정할 수 있습니다. 잠재적인 연결 오류를 처리하기 위한 약속도 추가합니다.

현재 Mongoose connect 메서드는 다음과 같습니다.

...
mongoose.connect(url, {useNewUrlParser: true});

기존 connect 메서드를 삭제하고 options 상수와 약속을 포함하는 다음 코드로 바꿉니다.

...
mongoose.connect(url, options).then( function() {
  console.log('MongoDB is connected');
})
  .catch( function(err) {
  console.log(err);
});

연결에 성공하면 함수가 적절한 메시지를 기록합니다. 그렇지 않으면 오류를 잡아 기록하여 문제를 해결할 수 있습니다.

완성된 파일은 다음과 같습니다.

const mongoose = require('mongoose');

const {
  MONGO_USERNAME,
  MONGO_PASSWORD,
  MONGO_HOSTNAME,
  MONGO_PORT,
  MONGO_DB
} = process.env;

const options = {
  useNewUrlParser: true,
  reconnectTries: Number.MAX_VALUE,
  reconnectInterval: 500,
  connectTimeoutMS: 10000,
};

const url = `mongodb://${MONGO_USERNAME}:${MONGO_PASSWORD}@${MONGO_HOSTNAME}:${MONGO_PORT}/${MONGO_DB}?authSource=admin`;

mongoose.connect(url, options).then( function() {
  console.log('MongoDB is connected');
})
  .catch( function(err) {
  console.log(err);
});

편집을 마치면 파일을 저장하고 닫습니다.

이제 애플리케이션 코드에 복원력을 추가하여 애플리케이션이 데이터베이스에 연결하지 못하는 경우를 처리했습니다. 이 코드를 사용하면 Compose로 서비스를 정의할 수 있습니다.

4단계 — Docker Compose로 서비스 정의

코드를 리팩터링하면 서비스 정의로 docker-compose.yml 파일을 작성할 준비가 된 것입니다. Compose의 서비스는 실행 중인 컨테이너이며 docker-compose.yml 파일에 포함할 서비스 정의에는 각 컨테이너 이미지가 실행되는 방식에 대한 정보가 포함되어 있습니다. Compose 도구를 사용하면 여러 서비스를 정의하여 다중 컨테이너 애플리케이션을 구축할 수 있습니다.

서비스를 정의하기 전에 netcat이라는 도구를 프로젝트에 추가하여 특정 호스트와 포트가 TCP 연결을 허용하는지 폴링합니다. 이를 사용하면 데이터베이스가 연결을 수락할 준비가 되었는지 테스트하여 애플리케이션의 데이터베이스 연결 시도를 제어할 수 있습니다.

Compose를 사용하면 Compose 설명서의 권장 사항을 사용하여 서비스 간의 종속성을 지정할 수 있습니다.

wait-for.sh라는 파일을 엽니다.

  1. nano wait-for.sh

파일에 다음 코드를 입력하여 폴링 함수를 만듭니다.

#!/bin/sh

# original script: https://github.com/eficode/wait-for/blob/master/wait-for

TIMEOUT=15
QUIET=0

echoerr() {
  if [ "$QUIET" -ne 1 ]; then printf "%s\n" "$*" 1>&2; fi
}

usage() {
  exitcode="$1"
  cat << USAGE >&2
Usage:
  $cmdname host:port [-t timeout] [-- command args]
  -q | --quiet                        Do not output any status messages
  -t TIMEOUT | --timeout=timeout      Timeout in seconds, zero for no timeout
  -- COMMAND ARGS                     Execute command with args after the test finishes
USAGE
  exit "$exitcode"
}

wait_for() {
  for i in `seq $TIMEOUT` ; do
    nc -z "$HOST" "$PORT" > /dev/null 2>&1
    
    result=$?
    if [ $result -eq 0 ] ; then
      if [ $# -gt 0 ] ; then
        exec "$@"
      fi
      exit 0
    fi
    sleep 1
  done
  echo "Operation timed out" >&2
  exit 1
}

while [ $# -gt 0 ]
do
  case "$1" in
    *:* )
    HOST=$(printf "%s\n" "$1"| cut -d : -f 1)
    PORT=$(printf "%s\n" "$1"| cut -d : -f 2)
    shift 1
    ;;
    -q | --quiet)
    QUIET=1
    shift 1
    ;;
    -t)
    TIMEOUT="$2"
    if [ "$TIMEOUT" = "" ]; then break; fi
    shift 2
    ;;
    --timeout=*)
    TIMEOUT="${1#*=}"
    shift 1
    ;;
    --)
    shift
    break
    ;;
    --help)
    usage 0
    ;;
    *)
    echoerr "Unknown argument: $1"
    usage 1
    ;;
  esac
done

if [ "$HOST" = "" -o "$PORT" = "" ]; then
  echoerr "Error: you need to provide a host and port to test."
  usage 2
fi

wait_for "$@"

코드 추가를 마쳤으면 파일을 저장하고 닫습니다.

스크립트를 실행 가능하게 만듭니다.

  1. chmod +x wait-for.sh

다음으로 docker-compose.yml 파일을 엽니다.

  1. nano docker-compose.yml

먼저 파일에 다음 코드를 추가하여 nodejs 애플리케이션 서비스를 정의합니다.

version: '3'

services:
  nodejs:
    build:
      context: .
      dockerfile: Dockerfile
    image: nodejs
    container_name: nodejs
    restart: unless-stopped
    env_file: .env
    environment:
      - MONGO_USERNAME=$MONGO_USERNAME
      - MONGO_PASSWORD=$MONGO_PASSWORD
      - MONGO_HOSTNAME=db
      - MONGO_PORT=$MONGO_PORT
      - MONGO_DB=$MONGO_DB 
    ports:
      - "80:8080"
    volumes:
      - .:/home/node/app
      - node_modules:/home/node/app/node_modules
    networks:
      - app-network
    command: ./wait-for.sh db:27017 -- /home/node/app/node_modules/.bin/nodemon app.js

nodejs 서비스 정의에는 다음 옵션이 포함됩니다.

  • build: 이는 Compose가 애플리케이션 이미지를 빌드할 때 적용될 구성 옵션(contextdockerfile 포함)을 정의합니다. 사용자 이름, 리포지토리 및 이미지 태그에 대한 정보와 함께 이미지 명령어와 같은 레지스트리의 기존 이미지를 대신 사용하려는 경우.
  • context: 이미지 빌드에 대한 빌드 컨텍스트를 정의합니다. 이 경우에는 현재 프로젝트 디렉토리입니다.
  • dockerfile: Compose가 애플리케이션 이미지를 빌드하는 데 사용할 파일로 현재 프로젝트 디렉토리의 Dockerfile을 지정합니다. 이 파일에 대한 자세한 내용은 Docker로 Node.js 애플리케이션을 빌드하는 방법을 참조하세요.
  • image, container_name: 이미지와 컨테이너에 이름을 적용합니다.
  • restart: 재시작 정책을 정의합니다. 기본값은 no이지만 중지하지 않는 한 컨테이너를 다시 시작하도록 설정했습니다.
  • env_file: Compose에게 빌드 컨텍스트에 있는 .env라는 파일에서 환경 변수를 추가할 것임을 알려줍니다.
  • environment: 이 옵션을 사용하면 .env 파일에서 정의한 Mongo 연결 설정을 추가할 수 있습니다. NODE_ENVdevelopment로 설정하지 않는다는 점에 유의하세요. 이는 보기 캐싱을 활성화하고 덜 장황한 오류 메시지를 표시하기 때문입니다.\n또한 2단계에서 설명한 대로 db 데이터베이스 컨테이너를 호스트로 지정했습니다.
  • ports: 호스트의 포트 80을 컨테이너의 포트 8080에 매핑합니다.
  • 볼륨: 여기에 두 가지 유형의 마운트가 포함되어 있습니다.\n
    • 첫 번째는 호스트의 애플리케이션 코드를 컨테이너의 /home/node/app 디렉토리에 마운트하는 바인드 마운트입니다. 이렇게 하면 호스트 코드에 대한 모든 변경 사항이 컨테이너에 즉시 채워지므로 신속한 개발이 가능합니다.
    • 두 번째는 응용 프로그램을 실행하는 데 필요한 패키지를 포함하는 컨테이너의 명명된 node_modules 디렉터리입니다. 그러나 방금 생성한 바인드 마운트는 새로 생성된 node_modules 디렉토리를 숨깁니다. 호스트의 node_modules가 비어 있기 때문에 바인드는 빈 디렉토리를 컨테이너에 매핑하여 새 node_modules 디렉토리를 재정의하고 애플리케이션이 시작되지 않도록 합니다. 명명된 node_modules 볼륨은 /home/node/app/node_modules 디렉토리의 내용을 유지하고 이를 컨테이너에 마운트하고 바인드를 숨김으로써 이 문제를 해결합니다.

    이 방법을 사용할 때 다음 사항에 유의하십시오.

    • 바인딩은 컨테이너에 있는 node_modules 디렉토리의 내용을 호스트에 마운트하고 명명된 볼륨이 Docker에 의해 생성되었기 때문에 이 디렉토리는 root가 소유합니다.\n
    • 호스트에 기존 node_modules 디렉터리가 있는 경우 컨테이너에 생성된 node_modules 디렉터리를 재정의합니다. 이 튜토리얼에서 구축하는 설정은 기존 node_modules 디렉토리가 없고 호스트에서 npm으로 작업하지 않을 것이라고 가정합니다. 이는 실행 환경 간의 종속성을 최소화하는 애플리케이션 개발에 대한 12단계 접근 방식과 일치합니다.\n

    • networks: 애플리케이션 서비스가 app-network 네트워크에 가입하도록 지정하며 파일 하단에서 정의합니다.
    • command: 이 옵션을 사용하면 Compose가 이미지를 실행할 때 실행해야 하는 명령을 설정할 수 있습니다. 이는 애플리케이션 Dockerfile에서 설정한 CMD 명령을 재정의합니다. 여기에서 wait-for 스크립트를 사용하여 애플리케이션을 실행하고 있습니다. 이 스크립트는 27017 포트에서 db 서비스를 폴링하여 데이터베이스 서비스가 올바른지 여부를 테스트합니다. 준비되었다. 준비 테스트가 성공하면 스크립트는 설정한 /home/node/app/node_modules/.bin/nodemon app.js 명령을 실행하여 nodemon<으로 애플리케이션을 시작합니다. /코드>. 이렇게 하면 애플리케이션을 다시 시작하지 않고도 향후 코드 변경 사항을 다시 로드할 수 있습니다.

    다음으로 애플리케이션 서비스 정의 아래에 다음 코드를 추가하여 db 서비스를 생성합니다.

    ...
      db:
        image: mongo:4.1.8-xenial
        container_name: db
        restart: unless-stopped
        env_file: .env
        environment:
          - MONGO_INITDB_ROOT_USERNAME=$MONGO_USERNAME
          - MONGO_INITDB_ROOT_PASSWORD=$MONGO_PASSWORD
        volumes:  
          - dbdata:/data/db   
        networks:
          - app-network  
    

    nodejs 서비스에 대해 정의된 일부 설정은 동일하게 유지되지만 이미지, 환경볼륨 정의:

    • 이미지: 이 서비스를 생성하기 위해 Compose는 4.1.8-xenial Dockerfile 모범 사례를 가져옵니다.
    • MONGO_INITDB_ROOT_USERNAME, MONGO_INITDB_ROOT_PASSWORD: mongo 이미지는 데이터베이스 인스턴스에서 이러한 루트 사용자를 만듭니다. 해당 역할의 모든 관리 및 운영 권한. 프로덕션 환경에서 작업할 때 적절하게 범위가 지정된 권한을 가진 전용 애플리케이션 사용자를 만들고 싶을 것입니다.

    참고: 기존 데이터 디렉터리가 있는 상태에서 컨테이너를 시작하면 이러한 변수가 적용되지 않는다는 점에 유의하십시오.

    • dbdata:/data/db: 명명된 볼륨 dbdata는 Mongo의 기본 데이터 디렉토리인 /data/db에 저장된 데이터를 유지합니다. . 이렇게 하면 컨테이너를 중지하거나 제거하는 경우 데이터 손실을 방지할 수 있습니다.

    networks 옵션으로 app-network 네트워크에 db 서비스도 추가되었습니다.

    마지막 단계로 볼륨 및 네트워크 정의를 파일 맨 아래에 추가합니다.

    ...
    networks:
      app-network:
        driver: bridge
    
    volumes:
      dbdata:
      node_modules:  
    

    사용자 정의 브리지 네트워크 app-network는 컨테이너가 동일한 Docker 데몬 호스트에 있기 때문에 컨테이너 간 통신을 가능하게 합니다. 이는 동일한 브리지 네트워크에 있는 컨테이너 사이의 모든 포트를 열면서 외부 세계에 포트를 노출하지 않기 때문에 애플리케이션 내 트래픽 및 통신을 간소화합니다. 따라서 dbnodejs 컨테이너는 서로 통신할 수 있으며 애플리케이션에 대한 프런트 엔드 액세스를 위해 포트 80만 노출하면 됩니다. .

    최상위 volumes 키는 볼륨 dbdatanode_modules를 정의합니다. Docker가 볼륨을 생성할 때 볼륨의 내용은 Docker가 관리하는 호스트 파일 시스템 /var/lib/docker/volumes/의 일부에 저장됩니다. 각 볼륨의 콘텐츠는 /var/lib/docker/volumes/ 아래의 디렉터리에 저장되며 볼륨을 사용하는 모든 컨테이너에 마운트됩니다. 이러한 방식으로 사용자가 생성하는 상어 정보 데이터는 db 컨테이너를 제거하고 다시 생성하더라도 dbdata 볼륨에 유지됩니다.

    완료된 docker-compose.yml 파일은 다음과 같습니다.

    version: '3'
    
    services:
      nodejs:
        build:
          context: .
          dockerfile: Dockerfile
        image: nodejs
        container_name: nodejs
        restart: unless-stopped
        env_file: .env
        environment:
          - MONGO_USERNAME=$MONGO_USERNAME
          - MONGO_PASSWORD=$MONGO_PASSWORD
          - MONGO_HOSTNAME=db
          - MONGO_PORT=$MONGO_PORT
          - MONGO_DB=$MONGO_DB
        ports:
          - "80:8080"
        volumes:
          - .:/home/node/app
          - node_modules:/home/node/app/node_modules
        networks:
          - app-network
        command: ./wait-for.sh db:27017 -- /home/node/app/node_modules/.bin/nodemon app.js 
    
      db:
        image: mongo:4.1.8-xenial
        container_name: db
        restart: unless-stopped
        env_file: .env
        environment:
          - MONGO_INITDB_ROOT_USERNAME=$MONGO_USERNAME
          - MONGO_INITDB_ROOT_PASSWORD=$MONGO_PASSWORD
        volumes:     
          - dbdata:/data/db
        networks:
          - app-network  
    
    networks:
      app-network:
        driver: bridge
    
    volumes:
      dbdata:
      node_modules:  
    

    편집을 마치면 파일을 저장하고 닫습니다.

    서비스 정의가 준비되면 애플리케이션을 시작할 준비가 된 것입니다.

    5단계 - 애플리케이션 테스트

    docker-compose.yml 파일이 있으면 docker-compose 다운으로 서비스를 생성할 수 있습니다.

    먼저 컨테이너 이미지를 빌드하고 -d 플래그와 함께 docker-compose up을 실행하여 서비스를 생성합니다. 그러면 nodejs가 실행되고 백그라운드의 db 컨테이너:

    1. docker-compose up -d

    출력에서 서비스가 생성되었음을 확인합니다.

    Output
    ... Creating db ... done Creating nodejs ... done

    서비스의 로그 출력을 표시하여 시작 프로세스에 대한 자세한 정보를 얻을 수도 있습니다.

    1. docker-compose logs

    모든 것이 올바르게 시작된 경우 출력은 다음과 같습니다.

    Output
    ... nodejs | [nodemon] starting `node app.js` nodejs | Example app listening on 8080! nodejs | MongoDB is connected ... db | 2019-02-22T17:26:27.329+0000 I ACCESS [conn2] Successfully authenticated as principal sammy on admin

    docker-compose ps를 사용하여 컨테이너의 상태를 확인할 수도 있습니다.

    1. docker-compose ps

    출력은 컨테이너가 실행 중임을 나타냅니다.

    Output
    Name Command State Ports ---------------------------------------------------------------------- db docker-entrypoint.sh mongod Up 27017/tcp nodejs ./wait-for.sh db:27017 -- ... Up 0.0.0.0:80->8080/tcp

    서비스가 실행 중이면 브라우저에서 http://your_server_ip를 방문할 수 있습니다.

    Get Shark Info 버튼을 클릭하면 상어 이름과 해당 상어의 일반 특성에 대한 설명을 제출할 수 있는 참가 양식이 있는 페이지로 들어갑니다.

    양식에 선택한 상어를 추가합니다. 이 데모의 목적을 위해 Shark Name 필드에 Megalodon Shark를 추가하고 Shark Character에 Ancient를 추가합니다. 필드:

    제출 버튼을 클릭하면 이 상어 정보가 있는 페이지가 다시 표시됩니다.

    마지막 단계로 데이터베이스 컨테이너를 제거해도 방금 입력한 데이터가 유지되는지 테스트합니다.

    터미널로 돌아가서 다음 명령을 입력하여 컨테이너와 네트워크를 중지하고 제거합니다.

    1. docker-compose down

    --volumes 옵션을 포함하지 않습니다. 따라서 dbdata 볼륨은 제거되지 않습니다.

    다음 출력은 컨테이너와 네트워크가 제거되었음을 확인합니다.

    Output
    Stopping nodejs ... done Stopping db ... done Removing nodejs ... done Removing db ... done Removing network node_project_app-network

    컨테이너를 다시 만듭니다.

    1. docker-compose up -d

    이제 상어 정보 양식으로 돌아가십시오.

    선택한 새 상어를 입력하세요. 이 예에서는 Whale SharkLarge를 사용합니다.

    제출을 클릭하면 이미 입력한 데이터의 손실 없이 새 상어가 데이터베이스의 상어 컬렉션에 추가되었음을 알 수 있습니다.

    이제 애플리케이션이 데이터 지속성과 코드 동기화가 활성화된 Docker 컨테이너에서 실행되고 있습니다.

    결론

    이 자습서를 따라 Docker 컨테이너를 사용하여 Node 애플리케이션에 대한 개발 설정을 만들었습니다. 민감한 정보를 추출하고 애플리케이션 코드에서 애플리케이션의 상태를 분리하여 프로젝트를 모듈화하고 이식성을 높였습니다. 또한 개발 요구 사항 및 요구 사항 변경에 따라 수정할 수 있는 상용구 docker-compose.yml 파일을 구성했습니다.

    개발하면서 이러한 주제에 대한 자세한 내용은 Kubernetes용 컨테이너화된 애플리케이션 및 현대화 애플리케이션을 위한 애플리케이션 설계에 대해 자세히 알아보는 데 관심이 있을 수 있습니다.

    이 튜토리얼에서 사용된 코드에 대해 자세히 알아보려면 Nginx, Let's Encrypt 및 Docker Compose로 컨테이너화된 Node.js 애플리케이션을 보호하는 방법을 참조하세요.