자습서: MySQL 및 Docker Compose를 사용하여 다중 컨테이너 앱 만들기

이 자습서에서는 다중 컨테이너 앱을 만드는 방법에 대해 알아봅니다. 이 자습서는 Docker 및 Visual Studio Code 시작 자습서를 기반으로 합니다. 이 고급 자습서에서는 이 다이어그램에 설명된 대로 작동하도록 애플리케이션을 업데이트하고 다음 방법을 알아봅니다.

  • MySQL을 시작합니다.
  • MySQL을 사용하여 앱을 실행합니다.
  • Compose 파일을 만듭니다.
  • 애플리케이션 스택을 실행합니다.

Diagram shows two containers labeled Todo App and MySQL connected with a line.

여러 컨테이너를 사용하면 특수 작업 용도로만 컨테이너를 사용할 수 있습니다. 각 컨테이너가 한 가지 작업을 잘 수행하는 것이 좋습니다.

다중 컨테이너 앱을 사용하려는 몇 가지 이유는 다음과 같습니다.

  • 데이터베이스와 다르게 API 및 프런트 엔드를 관리하기 위해 별도의 컨테이너를 사용합니다.
  • 컨테이너를 사용하면 격리된 상태로 버전을 관리하고 업데이트할 수 있습니다.
  • 로컬에서는 데이터베이스에 컨테이너를 사용할 수 있지만, 프로덕션 환경에서는 데이터베이스에 관리형 서비스를 사용하는 것이 좋습니다.
  • 여러 프로세스를 실행하려면 프로세스 관리자가 필요하며, 이로 인해 컨테이너 시작/종료와 관련된 복잡성이 추가됩니다.

필수 조건

이 자습서에서는 컨테이너 앱 만들기부터 시작하여 일련의 자습서를 계속 진행합니다. 필수 구성 요소가 포함된 해당 자습서로 시작합니다. 그런 다음, 자습서를 수행하여 앱에서 데이터를 유지합니다.

다음 항목도 필요합니다.

  • Docker Compose

    Windows 또는 Mac용 Docker Desktop에는 Docker Compose가 포함되어 있습니다. 다음 명령을 실행하여 확인합니다.

    docker-compose version
    

    Linux 운영 체제를 사용하는 경우 Docker Compose를 설치합니다.

이전 자습서와 마찬가지로 VS Code 탐색기 뷰 또는 DOCKER 뷰에서 대부분 작업을 수행할 수 있습니다. VS Code에서 터미널>새 터미널을 선택하여 명령줄 창을 열 수 있습니다. Bash 창에서 명령을 실행할 수도 있습니다. 달리 지정되지 않은 경우 Bash로 레이블이 지정된 모든 명령은 Bash 창 또는 VS Code 터미널에서 실행할 수 있습니다.

MySQL을 시작합니다.

컨테이너는 기본적으로 격리된 상태로 실행됩니다. 컨테이너는 같은 컴퓨터의 다른 프로세스 또는 컨테이너에 대해 아무것도 알지 못합니다. 한 컨테이너가 다른 컨테이너와 통신할 수 있도록 하려면 네트워킹을 사용합니다.

두 컨테이너가 같은 네트워크에 있으면 서로 통신할 수 있습니다. 그러지 않으면 통신할 수 없습니다.

네트워크에 컨테이너를 배치하는 방법에는 두 가지가 있습니다. 시작할 때 할당하거나 기존 컨테이너를 연결하는 것입니다. 이 예제에서는 먼저 네트워크를 만든 후 시작 시 MySQL 컨테이너를 연결합니다.

  1. 다음 명령을 사용하여 네트워크를 만듭니다.

    docker network create todo-app
    
  2. MySQL 컨테이너를 시작하고 네트워크에 연결합니다.

    docker run -d 
        --network todo-app --network-alias mysql 
        -v todo-mysql-data:/var/lib/mysql 
        -e MYSQL_ROOT_PASSWORD=<your-password> 
        -e MYSQL_DATABASE=todos 
        mysql:5.7
    

    이 명령은 환경 변수도 정의합니다. 자세한 내용은 MySQL Docker Hub 목록을 참조하세요.

    이 명령은 네트워크 별칭 mysql을 지정합니다.

  3. docker ps 명령을 사용하여 컨테이너 ID를 가져옵니다.

  4. 데이터베이스가 시작되어 실행되고 있는지 확인하려면 데이터베이스에 연결합니다.

    docker exec -it <mysql-container-id> mysql -p
    

    메시지가 표시되면 위에서 사용한 암호를 입력합니다.

  5. MySQL 셸에서 데이터베이스 목록을 표시하고 todos 데이터베이스가 표시되는지 확인합니다.

    SHOW DATABASES;
    

    다음 출력이 표시되어야 합니다.

    +--------------------+
    | Database           |
    +--------------------+
    | information_schema |
    | mysql              |
    | performance_schema |
    | sys                |
    | todos              |
    +--------------------+
    5 rows in set (0.00 sec)
    
  6. 터미널 명령 프롬프트로 돌아갈 준비가 되면 입력 exit 합니다.

MySQL을 사용하여 앱 실행

todo 앱에서는 환경 변수를 설정하여 MySQL 연결 설정을 지정할 수 있도록 지원합니다.

  • MYSQL_HOST MySQL 서버의 호스트 이름
  • MYSQL_USER 연결에 사용할 사용자 이름
  • MYSQL_PASSWORD 연결에 사용할 암호
  • MYSQL_DB 연결된 후 사용할 데이터베이스

Warning

환경 변수를 사용하여 연결 설정을 지정하는 것은 개발에 적합합니다. 프로덕션 환경에서는 애플리케이션 실행에 이 방식을 사용하지 않는 것이 좋습니다. 자세한 내용은 Why you shouldn’t use environment variables for secret data(비밀 데이터에 환경 변수를 사용하면 안 되는 이유)를 참조하세요.

더 안전한 메커니즘은 컨테이너 오케스트레이션 프레임워크에서 제공하는 비밀 지원을 사용하는 것입니다. 해당 비밀은 대부분의 경우 실행 중인 컨테이너에 파일로 탑재됩니다.

이 절차는 앱을 시작하고 해당 컨테이너를 MySQL 컨테이너에 연결합니다.

  1. 다음 docker 실행 명령을 사용합니다. 위의 환경 변수가 지정됩니다.

    docker run -dp 3000:3000 
      -w /app -v ${PWD}:/app 
      --network todo-app 
      -e MYSQL_HOST=mysql 
      -e MYSQL_USER=root 
      -e MYSQL_PASSWORD=<your-password> 
      -e MYSQL_DB=todos 
      node:20-alpine 
      sh -c "yarn install && yarn run dev"
    
  2. VS Code의 Docker 뷰에서 앱 컨테이너를 마우스 오른쪽 단추로 클릭하고 로그 보기를 선택합니다. 명령줄에서 로그를 보려면 docker logs 명령을 사용합니다.

    결과에는 앱이 MySQL 데이터베이스에 연결되어 있음을 나타내는 줄이 포함됩니다.

    # Previous log messages omitted
    $ nodemon src/index.js
    [nodemon] 1.19.2
    [nodemon] to restart at any time, enter `rs`
    [nodemon] watching dir(s): *.*
    [nodemon] starting `node src/index.js`
    Connected to mysql db at host mysql
    Listening on port 3000
    
  3. 브라우저에 http://localhost:3000을 입력합니다. todo 목록에 몇 가지 항목을 추가합니다.

  4. 이전 섹션에서 한 것처럼 MySQL 데이터베이스에 연결합니다. 다음 명령을 실행하여 항목이 데이터베이스에 기록되고 있는지 확인합니다.

    docker exec -ti <mysql-container-id> mysql -p todos
    

    MySQL 셸에서 다음 명령을 실행합니다.

    use todos;
    select * from todo_items;
    

    결과는 다음 출력과 같습니다.

    +--------------------------------------+--------------------+-----------+
    | id                                   | name               | completed |
    +--------------------------------------+--------------------+-----------+
    | c906ff08-60e6-44e6-8f49-ed56a0853e85 | Do amazing things! |         0 |
    | 2912a79e-8486-4bc3-a4c5-460793a575ab | Be awesome!        |         0 |
    +--------------------------------------+--------------------+-----------+
    

이때 외부 데이터베이스에 데이터를 저장하는 애플리케이션이 있습니다. 해당 데이터베이스는 별도의 컨테이너에서 실행됩니다. 컨테이너 네트워킹에 대해 알아봤습니다.

Docker Compose 파일 만들기

Docker Compose는 다중 컨테이너 애플리케이션을 정의하고 공유하는 데 도움이 됩니다. Docker Compose를 사용하면 서비스를 정의하는 파일을 만들 수 있습니다. 단일 명령으로 모든 항목을 실행하거나 삭제할 수 있습니다.

파일에 애플리케이션 스택을 정의하고 해당 파일을 버전 제어 하에 프로젝트 리포지토리의 루트에 유지할 수 있습니다. 이 접근 방식을 사용하면 다른 사용자가 프로젝트에 기여할 수 있습니다. 리포지토리를 복제하기만 하면 됩니다.

  1. 앱 프로젝트 루트에 docker-compose.yml이라는 파일을 만듭니다.

  2. Compose 파일에서 먼저 스키마 버전을 정의합니다.

    version: "3.7"
    

    대부분의 경우 지원되는 최신 버전을 사용하는 것이 가장 좋습니다. 현재 스키마 버전 및 호환성 매트릭스는 Compose 파일을 참조하세요.

  3. 애플리케이션의 일부로 실행할 서비스 또는 컨테이너를 정의합니다.

    version: "3.7"
    
    services:
    

    .yml 파일에서는 들여쓰기가 중요합니다. VS Code에서 편집하는 경우 Intellisense가 오류를 나타냅니다.

  4. 앱 컨테이너에 사용한 명령은 다음과 같습니다. .yml 파일에 이 정보를 추가합니다.

    docker run -dp 3000:3000 
      -w /app -v ${PWD}:/app 
      --network todo-app 
      -e MYSQL_HOST=mysql 
      -e MYSQL_USER=root 
      -e MYSQL_PASSWORD=<your-password> 
      -e MYSQL_DB=todos 
      node:20-alpine 
      sh -c "yarn install && yarn run dev"
    

    서비스 항목과 컨테이너 이미지를 정의합니다.

    version: "3.7"
    
    services:
      app:
        image: node:20-alpine
    

    서비스 이름은 임의로 선택할 수 있습니다. 이름은 자동으로 네트워크 별칭이 되므로 MySQL 서비스를 정의할 때 유용합니다.

  5. 명령을 추가합니다.

    version: "3.7"
    
    services:
      app:
        image: node:20-alpine
        command: sh -c "yarn install && yarn run dev"
    
  6. 위의 명령에서 -p 3000:3000에 해당하는 서비스 포트를 지정합니다.

    version: "3.7"
    
    services:
      app:
        image: node:20-alpine
        command: sh -c "yarn install && yarn run dev"
        ports:
          - 3000:3000
    
  7. 작업 디렉터리 및 볼륨 매핑 지정

    version: "3.7"
    
    services:
      app:
        image: node:20-alpine
        command: sh -c "yarn install && yarn run dev"
        ports:
          - 3000:3000
        working_dir: /app
        volumes:
          - ./:/app
    

    Docker Compose 볼륨 정의에서 현재 디렉터리의 상대 경로를 사용할 수 있습니다.

  8. 환경 변수 정의를 지정합니다.

    version: "3.7"
    
    services:
      app:
        image: node:20-alpine
        command: sh -c "yarn install && yarn run dev"
        ports:
          - 3000:3000
        working_dir: /app
        volumes:
          - ./:/app
        environment:
          MYSQL_HOST: mysql
          MYSQL_USER: root
          MYSQL_PASSWORD: <your-password>
          MYSQL_DB: todos
    
  9. MySQL 서비스의 정의를 추가합니다. 위에서 사용한 명령은 다음과 같습니다.

    docker run -d 
      --network todo-app --network-alias mysql 
      -v todo-mysql-data:/var/lib/mysql 
      -e MYSQL_ROOT_PASSWORD=<your-password> 
      -e MYSQL_DATABASE=todos 
      mysql:5.7
    

    새 서비스를 정의하고 이름을 mysql로 지정합니다. 같은 들여쓰기 수준에서 app 정의 뒤에 텍스트를 추가합니다.

    version: "3.7"
    
    services:
      app:
        # The app service definition
      mysql:
        image: mysql:5.7
    

    서비스가 자동으로 네트워크 별칭을 가져옵니다. 사용할 이미지를 지정합니다.

  10. 볼륨 매핑을 정의합니다.

    services:와 같은 수준에서 volumes: 섹션에 볼륨을 지정합니다. 이미지 아래에 볼륨 매핑을 지정합니다.

    version: "3.7"
    
    services:
      app:
        # The app service definition
      mysql:
        image: mysql:5.7
        volumes:
          - todo-mysql-data:/var/lib/mysql
    
    volumes:
      todo-mysql-data:
    
  11. 환경 변수를 지정합니다.

    version: "3.7"
    
    services:
      app:
        # The app service definition
      mysql:
        image: mysql:5.7
        volumes:
          - todo-mysql-data:/var/lib/mysql
        environment: 
          MYSQL_ROOT_PASSWORD: <your-password>
          MYSQL_DATABASE: todos
    
    volumes:
      todo-mysql-data:
    

이때 전체 docker-compose.yml은 다음과 같습니다.

version: "3.7"

services:
  app:
    image: node:20-alpine
    command: sh -c "yarn install && yarn run dev"
    ports:
      - 3000:3000
    working_dir: /app
    volumes:
      - ./:/app
    environment:
      MYSQL_HOST: mysql
      MYSQL_USER: root
      MYSQL_PASSWORD: <your-password>
      MYSQL_DB: todos

  mysql:
    image: mysql:5.7
    volumes:
      - todo-mysql-data:/var/lib/mysql
    environment: 
      MYSQL_ROOT_PASSWORD: <your-password>
      MYSQL_DATABASE: todos

volumes:
  todo-mysql-data:

애플리케이션 스택 실행

이제 docker-compose.yml 파일이 있으므로 다음을 시도합니다.

  1. 앱과 데이터베이스의 다른 복사본이 실행되고 있지 않은지 확인합니다. Docker 확장에서 실행 중인 컨테이너를 마우스 오른쪽 단추로 클릭하고 제거를 선택합니다. 또는 명령줄에서 이전 예제와 같이 docker rm 명령을 사용합니다.

  2. VS Code 탐색기에서 docker-compose.yml을 마우스 오른쪽 단추로 클릭하고 Compose 시작을 선택합니다. 또는 명령줄에서 다음 docker 명령을 사용합니다.

    docker-compose up -d
    

    -d 매개 변수는 명령이 백그라운드에서 실행되도록 합니다.

    다음 결과와 같은 출력이 표시됩니다.

    [+] Building 0.0s (0/0)
    [+] Running 2/2
    ✔ Container app-app-1    Started                                                                                                       0.9s 
    ✔ Container app-mysql-1  Running
    

    네트워크뿐만 아니라 볼륨도 만들어졌습니다. 기본적으로 Docker Compose는 애플리케이션 스택 전용 네트워크를 만듭니다.

  3. Docker 확장에서 앱 컨테이너를 마우스 오른쪽 단추로 클릭하고 로그 보기를 선택합니다. 명령줄에서 로그를 보려면 docker logs 명령을 사용합니다.

    mysql_1  | 2019-10-03T03:07:16.083639Z 0 [Note] mysqld: ready for connections.
    mysql_1  | Version: '5.7.27'  socket: '/var/run/mysqld/mysqld.sock'  port: 3306  MySQL Community Server (GPL)
    app_1    | Connected to mysql db at host mysql
    app_1    | Listening on port 3000
    

    각 서비스의 로그가 단일 스트림으로 인터리브되어 표시됩니다. 이 동작을 통해 타이밍 관련 이슈를 감시할 수 있습니다.

    메시지를 구분하기 쉽도록 서비스 이름이 줄 시작 부분에 표시됩니다. 특정 서비스에 대한 로그를 보려면 로그 명령의 끝부분에 서비스 이름을 추가합니다.

  4. 이때 앱을 열 수 있어야 합니다. 브라우저에 http://localhost:3000을 입력합니다.

이러한 컨테이너에 대한 작업을 완료하면 모든 컨테이너를 간단히 제거할 수 있습니다.

  • VS Code 탐색기에서 docker-compose.yml을 마우스 오른쪽 단추로 클릭하고 Compose 종료를 선택합니다.
  • 명령줄에서 .를 실행합니다 docker-compose down.

컨테이너가 중지됩니다. 네트워크가 제거됩니다.

기본적으로 Compose 파일의 명명된 볼륨은 제거되지 않습니다. 볼륨을 제거하려면 docker-compose down --volumes를 실행합니다.

리소스 정리

이 자습서 시리즈에서 사용한 필수 구성 요소는 향후 Docker 개발에 사용할 수 있습니다. 아무것도 삭제하거나 제거할 이유가 없습니다.

다음 단계

이 자습서에서는 다중 컨테이너 앱 및 Docker Compose에 대해 알아봤습니다. Docker Compose를 사용하면 다중 서비스 애플리케이션의 정의 및 공유를 크게 간소화할 수 있습니다.

유용할 수 있는 몇 가지 리소스는 다음과 같습니다.