Docker Get started ( 도커 번역 ) - Part 7 - Mulit-Container Application (다중 컨테이너 애플리케이션 구축하기)

Multi container apps

이전까지 싱글 컨테이너 앱들에 대해서 다뤘습니다. 하지만, 애플리케이션 스택에 MySQL을 추가하길 원합니다. 다음과 같은 질문이 종종 있는데 "어디서 MySQL이 실행되나요? 같은 컨테이너에서 실행되나요 아니면 다른 컨테이너로 실행되나요" 일반적으로 각 컨테이너들은 하나의 일만 합니다. 그 이유는 다음과 같습니다.

  • API나 front-end 를 데이터베이스와는 다르게 확장해야 될 가능성이 높습니다.
  • 컨테이너를 분리하는 것은 버전 그리고 업데이터 버전을 독립적으로 수행하게 해줍니다.
  • 로컬에서 데이터베이스 컨테이너를 사용할 수 있지만, 프로덕션 데이터베이스에 대해 관리 서비스를 사용할 수도 있습니다. 그러면 데이터베이스 엔진을 앱과 함께 배송하지 싶지 않을 것입니다.
  • 여러개의 프로세스를 실행하는 것은 프로세스 매니저가 필요하며, 이는 컨테이너 시작/종료에 복잡성을 가중시킵니다.

이유가 더 있는데, 우리는 앱을 다음처럼 만들겁니다.

Container Networking

기본적으로 컨테이너는 분리되어 실행되며 동일한 시스템의 다른 포스세스 또는 컨테이너에 대해서 같은 머신에 있더라도 전혀 알지 못합니다.

그럼 컨테이너 간 통신은 어떻게 해야 하나요? 바로 네트워킹 입니다. 이것만 기억하세요.

두 개의 컨테이너가 같은 네트워크에 있다면, 서로 통신할 수 있습니다. 그렇지 않으면, 불가능합니다.

Starting MySQL

네트워크에 컨테이너를 넣는 방법은 두 가지가 있습니다.

  1. 시작할때 할당
  2. 이미 존재하는 컨테이너에 연결

지금은, 네트워크를 만들고 시작할때 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=secret \
         -e MYSQL_DATABASE=todos \
         mysql:5.7

    파워셀에서는 아래처럼

     docker run -d `
         --network todo-app --network-alias mysql `
         -v todo-mysql-data:/var/lib/mysql `
         -e MYSQL_ROOT_PASSWORD=secret `
         -e MYSQL_DATABASE=todos `
         mysql:5.7

    --network-alias 플래그는 잠시 뒤에 설명하겠습니다.

Pro-tip

여기서는 데이터를 저장하기 위해서 todo-mysql-data 라는 볼륨을 사용하고 /var/lib/mysql 을 마운트하게 됩니다. 하지만 docker volume create는 실행하지 않았습니다. 도커가 우리가 사용하고자 하는 명명된 볼륨을 기억하고 자동으로 생성해주기 때문입니다.

  1. 데이터베이스가 실행 중인지 확인하려면 데이터베이스에 연결하고 연결되었는지 확인하세요.

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

    패스워드 입력이 요구되면, secret을 입력합니다. MySQL 쉘에서는 데이터베이스의 목록과 todos 데이터베이스를 확인할 수 있습니다.

     mysql> SHOW DATABASES;

    다음과 같은 결과를 확인할 수 있죠

     +--------------------+
     | Database           |
     +--------------------+
     | information_schema |
     | mysql              |
     | performance_schema |
     | sys                |
     | todos              |
     +--------------------+
     5 rows in set (0.00 sec)

Connecting to MySQL

이제 MySQL이 실행되고 있는걸 알겁니다. 여기서 같은 네트워크의 다른 컨테이너가 실행될때, 어떻게 찾아야하나요? 각 컨테이너는 고유한 IP 주소를 가지고 있다는 것을 기억하세요

이를 해결하기 위해서, nicolaka/netshoot 컨테이너를 만들어서 사용할겁니다. 이건 트러블 슈팅 또는 네트워크 이슈 디버깅에 유용한 많은 툴을 보유하고 있습니다.

  1. nicolaka/netshoot 이미지를 이용해서 새로운 컨테이너를 시작하고, 같은 네트워크에 접속되도록 하세요.

    docker run -it --network todo-app nicolaka/netshoot
  2. 컨테이너 내부에서 DNS 툴로 유용한 dig 명령어를 사용할 겁니다. 호스트 이름 mysql의 IP 주소를 확인해보겠습니다.

    dig mysql

    그럼 다음과 같은 결과를 볼 수 있죠

     ; <<>> DiG 9.14.1 <<>> mysql
     ;; global options: +cmd
     ;; Got answer:
     ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 32162
     ;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
    
     ;; QUESTION SECTION:
     ;mysql.				IN	A
    
     ;; ANSWER SECTION:
     mysql.			600	IN	A	172.23.0.2
    
     ;; Query time: 0 msec
     ;; SERVER: 127.0.0.11#53(127.0.0.11)
     ;; WHEN: Tue Oct 01 23:47:24 UTC 2019
     ;; MSG SIZE  rcvd: 44

    ANSWER SECTION에서 172.23.0.2로 결정된 mysql의 A record가 보입니다. mysql이 일반적으로 유효한 호스트 이름이 아니지만 Docker는 네트워크 별칭을 가진 컨테이너의 IP 주소로 mysql을 확인할 수 있었습니다. 간단하게 호스트 이름이 mysql 에 접속하는 것이 필요합니다.

Running our App with MySQL

todo app 은 MySQL 연결 설정을 지정하기 위해서 몇가지 환경 변수를 지원합니다.

  • MYSQL_HOST - 실행 중인 MySQL 서버의 호스트 이름
  • MYSQL_USER - 연결을 위한 사용자 이름
  • MYSQL_PASSWORD - 연결을 위한 패스워드
  • MYSQL_DB - 연결되면 사용할 데이터베이스

연결 설정을 하기 위해 환경 변수를 사용하는 것은 개발 과정에선 괜찮지만, 프로덕션 애플리케이션에서는 좋지 않습니다. 도커의 보안 책임자였던 Diogo Monica 는 그 이유를 설명하는 포스팅을 했으니 보시길 바랍니다.

https://diogomonica.com/2017/03/27/why-you-shouldnt-use-env-variables-for-secret-data/

컨테이너 오케스트레이션 프레임워크를 통해 제방받는 시크릿을 사용하는 것은 더 보안적인 메커니즘입니다. 대부분의 경우, 실해중이 ㄴ컨테이너에 파일로 시크릿들이 마운트됩니다. 많은 앱들이 변수들을 포함하고 있는 파일을 포인팅하기 위한 용도로 _FILE suffix를 사용하는 것을 지원합니다.

예를 들어, MYSQL_PASSWORD_FILE 변수를 세팅하는 것은 연결 패스워드로 파일의 내용을 참고하도록 합니다. 도커는 이러한 환경 변수를 지원하지 않습니다. 앱을 변수들과 파일의 내용을 찾는 방법에 대해서 알 필요가 있습니다.

dev-ready 컨테이너를 시작해봅시다.

  1. 앱 네트워크로 잘 접속할 수 있도록, 각 환경 변수를 지정합시다.

    docker run -dp 3000:3000 \
       -w /app -v "$(pwd):/app" \
       --network todo-app \
       -e MYSQL_HOST=mysql \
       -e MYSQL_USER=root \
       -e MYSQL_PASSWORD=secret \
       -e MYSQL_DB=todos \
       node:12-alpine \
       sh -c "yarn install && yarn run dev"

    파워쉘은 아래처럼 합니다.

     docker run -dp 3000:3000 `
       -w /app -v "$(pwd):/app" `
       --network todo-app `
       -e MYSQL_HOST=mysql `
       -e MYSQL_USER=root `
       -e MYSQL_PASSWORD=secret `
       -e MYSQL_DB=todos `
       node:12-alpine `
       sh -c "yarn install && yarn run dev"
  2. 컨테이너 로그를 보고자 한다면, 메세지에 사용하는 mysql database에 연결된 것을 알 수 있습니다.

     # 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. 브라우저에서 todo 앱을 실행하고 몇가지 아이템을 추가해보겠습니다.

  4. mysql database 에 연결하고 제대로 쓰여졌는지 확인해보겠습니다. 패스워드는 secret입니다.

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

    그리고 한번 확인해보겠습니다.

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

    명확하게, 몇가지 달라진 점을 확인할 수 있습니다.

도커 대시보드를 빠르게 보면, 이미 두개의 앱이 실행되고 있는것을 볼 수 있습니다 하지만, 하나의 앱에서 그룹화된 것은 확인할 수 없습니다. 어떻게 더 좋게 만드는지 추후에 다루도록 하겠습니다.

좋은 웹페이지 즐겨찾기