다중 컨테이너 앱Multi-container apps

지금까지는 단일 컨테이너 앱을 사용했습니다.Up to this point, you've been working with single-container apps. 이제 애플리케이션 스택에 MySQL을 추가하겠습니다.But, you'll now add MySQL to the application stack. “MySQL은 어디서 실행되나요?The following question often arises - "Where will MySQL run? 같은 컨테이너에 설치하나요, 아니면 별도로 실행하나요?”라는 질문을 받는 경우가 많습니다.Install it in the same container or run it separately?" 일반적으로 각 컨테이너에서 한 가지 작업을 잘 수행하는 것이 좋습니다.In general, each container should do one thing and do it well. 다음과 같은 몇 가지 이유가 있습니다.A few reasons:

  • API와 프런트 엔드의 크기를 데이터베이스와 다르게 스케일링해야 할 가능성이 있습니다.There's a good chance you'd have to scale APIs and front-ends differently than databases
  • 개별 컨테이너를 사용하면 격리된 상태로 버전을 관리하고 업데이트할 수 있습니다.Separate containers let you version and update versions in isolation
  • 로컬에서는 데이터베이스에 컨테이너를 사용할 수 있지만, 프로덕션 환경에서는 데이터베이스에 관리형 서비스를 사용하는 것이 좋습니다.While you may use a container for the database locally, you may want to use a managed service for the database in production. 그러면 앱과 함께 데이터베이스 엔진을 제공하지 않아도 됩니다.You don't want to ship your database engine with your app then.
  • 여러 프로세스를 실행하는 경우 프로세스 관리자가 필요하며(컨테이너는 하나의 프로세스만 시작함), 컨테이너 시작/종료가 복잡해집니다.Running multiple processes will require a process manager (the container only starts one process), which adds complexity to container startup/shutdown.

그 밖의 다른 이유도 있습니다.And there are more reasons. 따라서 애플리케이션을 다음과 같이 업데이트하겠습니다.So, you will update your application to work like this:

MySQL 컨테이너에 연결된 todo 앱

컨테이너 네트워킹Container networking

기본적으로 컨테이너는 격리된 상태로 실행되며, 같은 머신의 다른 프로세스나 컨테이너에 대해 아무것도 모릅니다.Remember that containers, by default, run in isolation and don't know anything about other processes or containers on the same machine. 컨테이너 간 통신을 허용하려면 어떻게 해야 할까요?So, how do you allow one container to talk to another? 정답은 네트워킹 입니다.The answer is networking. 네트워크 엔지니어가 아니어도 괜찮습니다.Now, you don't have to be a network engineer (hooray!). 다음 규칙만 기억하면 됩니다Simply remember this rule...

참고

두 컨테이너가 같은 네트워크에 있으면 서로 통신할 수 있습니다.If two containers are on the same network, they can talk to each other. 그러지 않으면 통신할 수 없습니다.If they aren't, they can't.

MySQL을 시작합니다.Start MySQL

네트워크에 컨테이너를 배치하는 방법에는 두 가지가 있습니다. 시작할 때 할당하거나 기존 컨테이너를 연결하는 것입니다.There are two ways to put a container on a network: assign it at start or connect an existing container. 지금은 먼저 네트워크를 만든 다음, 시작 시 MySQL 컨테이너를 연결하겠습니다.For now, you will create the network first and attach the MySQL container at startup.

  1. 네트워크를 만듭니다.Create the network.

    docker network create todo-app
    
  2. MySQL 컨테이너를 시작하고 네트워크에 연결합니다.Start a MySQL container and attach it the network. 또한 데이터베이스를 초기화하는 데 사용할 몇 개의 환경 변수를 정의해 보겠습니다(MySQL Docker Hub 목록에서 “환경 변수” 섹션 참조)(Windows PowerShell에서 \ 문자를 `로 바꿈).We're also going to define a few environment variables that the database will use to initialize the database (see the "Environment Variables" section in the MySQL Docker Hub listing) (replace the \ characters with ` in Windows PowerShell).

    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 플래그도 지정했습니다.You'll also see you specified the --network-alias flag. 잠시 후에 살펴보겠습니다.We'll come back to that in just a moment.

    여기서는 todo-mysql-data라는 볼륨 이름을 사용하고, MySQL에서 데이터를 저장하는 위치인 /var/lib/mysql에 볼륨을 탑재합니다.You'll notice you're using a volume name todo-mysql-data here and mounting it at /var/lib/mysql, which is where MySQL stores its data. 그러나 docker volume create 명령을 실행한 적은 없습니다.However, you never ran a docker volume create command. Docker에서 명명된 볼륨을 사용하려는 의도를 인식하고 자동으로 볼륨을 만듭니다.Docker recognizes that you want to use a named volume and creates one automatically for you.

  3. 데이터베이스가 시작되어 실행되고 있는지 확인하려면 데이터베이스에 연결하여 연결 상태를 확인합니다.To confirm you have the database up and running, connect to the database and verify it connects.

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

    암호 프롬프트가 표시되면 secret 을 입력합니다.When the password prompt comes up, type in secret. MySQL 셸에서 데이터베이스 목록을 표시하고 todos 데이터베이스가 표시되는지 확인합니다.In the MySQL shell, list the databases and verify you see the todos database.

    mysql> SHOW DATABASES;
    

    결과는 다음과 같습니다.You should see output that looks like this:

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

    축하합니다.Hooray! todos 데이터베이스가 있으며 사용할 준비가 되었습니다.You have your todos database and it's ready for use!

MySQL에 연결Connect to MySQL

MySQL이 시작되어 실행되고 있음을 확인했으므로 사용해 봅시다.Now that you know MySQL is up and running, let's use it! 그런데 사용하려면 어떻게 해야 할까요?But, the question is... how? 같은 네트워크에서 다른 컨테이너를 실행하는 경우 컨테이너를 어떻게 찾을 수 있을까요(각 컨테이너에 고유한 IP 주소가 있음)?If you run another container on the same network, how do you find the container (remember each container has its own IP address)?

방법을 파악하기 위해, 네트워킹 문제 해결 또는 디버그에 유용한 ‘많은’ 도구가 포함된 nicolaka/netshoot 컨테이너를 사용하겠습니다.To figure it out, you're going to make use of the nicolaka/netshoot container, which ships with a lot of tools that are useful for troubleshooting or debugging networking issues.

  1. nicolaka/netshoot 이미지를 사용하여 새 컨테이너를 시작합니다.Start a new container using the nicolaka/netshoot image. 같은 네트워크에 컨테이너를 연결해야 합니다.Make sure to connect it to the same network.

    docker run -it --network todo-app nicolaka/netshoot
    
  2. 컨테이너 내에서 유용한 DNS 도구인 dig 명령을 사용합니다.Inside the container, use the dig command, which is a useful DNS tool. 호스트 이름 mysql의 IP 주소를 조회합니다.Look up the IP address for the hostname mysql.

    dig mysql
    

    다음과 같은 출력이 표시됩니다.And you'll get an output like this...

    ; <<>> 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”에서 mysql에 대해 A 레코드가 표시되며, 172.23.0.2로 확인됩니다(사용자 IP 주소는 다른 값일 가능성이 높음).In the "ANSWER SECTION," you will see an A record for mysql that resolves to 172.23.0.2 (your IP address will most likely have a different value). mysql는 일반적으로 유효한 호스트 이름이 아니지만, Docker에서 해당 네트워크 별칭을 가진 컨테이너의 IP 주소로 확인할 수 있었습니다(앞에서 사용한 --network-alias 플래그 참조).While mysql isn't normally a valid hostname, Docker was able to resolve it to the IP address of the container that had that network alias (remember the --network-alias flag you used earlier?).

    따라서 앱이 mysql이라는 호스트에 연결하기만 하면 데이터베이스와 통신할 수 있습니다.What this means is... your app only simply needs to connect to a host named mysql and it'll talk to the database! 매우 간단합니다.It doesn't get much simpler than that!

MySQL을 사용하여 앱 실행Run your app with MySQL

todo 앱에서 몇 개의 환경 변수를 설정하여 MySQL 연결 설정을 지정할 수 있습니다.The todo app supports the setting of a few environment variables to specify MySQL connection settings. 관련 토폴로지는 다음과 같습니다.They are:

  • MYSQL_HOST - 실행 중인 MySQL 서버의 호스트 이름MYSQL_HOST - the hostname for the running MySQL server
  • MYSQL_USER - 연결에 사용할 사용자 이름MYSQL_USER - the username to use for the connection
  • MYSQL_PASSWORD - 연결에 사용할 암호MYSQL_PASSWORD - the password to use for the connection
  • MYSQL_DB - 연결된 후 사용할 데이터베이스MYSQL_DB - the database to use once connected

경고

환경 변수를 통해 연결 설정 지정 일반적으로 개발 환경에서는 환경 변수로 연결 설정을 지정해도 되지만, 프로덕션 환경에서 애플리케이션을 실행할 때는 이 방법을 사용하지 않는 것이 좋습니다.Setting Connection Settings via Environment Variables While using environment variables to set connection settings is generally okay for development, it is highly discouraged when running applications in production. 이유를 파악하려면 비밀 데이터에 환경 변수를 사용할 수 없는 이유를 참조하세요.To understand why, see Why you shouldn't use environment variables for secret data. 더 안전한 메커니즘은 컨테이너 오케스트레이션 프레임워크에서 제공하는 비밀 지원을 사용하는 것입니다.A more secure mechanism is to use the secret support provided by your container orchestration framework. 해당 비밀은 대부분의 경우 실행 중인 컨테이너에 파일로 탑재됩니다.In most cases, these secrets are mounted as files in the running container. 파일이 포함된 파일을 가리키는 _FILE 접미사가 있는 환경 변수를 지원하는 앱도 많습니다(MySQL 이미지, todo 앱 포함).You'll see many apps (including the MySQL image and the todo app) also support env vars with a _FILE suffix to point to a file containing the file. 예를 들어 MYSQL_PASSWORD_FILE 변수를 설정하면 앱이 참조된 파일 내용을 연결 암호로 사용합니다.As an example, setting the MYSQL_PASSWORD_FILE var will cause the app to use the contents of the referenced file as the connection password. Docker에서는 해당 환경 변수를 지원하는 작업을 수행하지 않습니다.Docker doesn't do anything to support these env vars. 앱이 변수를 찾고 파일 내용을 가져와야 합니다.Your app will need to know to look for the variable and get the file contents.

설명한 모든 사항에 유의하여, 개발 환경용 컨테이너를 시작합니다.With all of that explained, start your dev-ready container!

  1. 위에서 언급한 각 환경 변수를 지정하고 컨테이너를 앱 네트워크에 연결합니다(Windows PowerShell에서 \ 문자를 `로 바꿈).Specify each of the environment variables above, and connect the container to your app network (replace the \ characters with ` in Windows PowerShell).

    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. 컨테이너에 대한 로그(docker logs <container-id>)를 살펴보면 MySQL 데이터베이스를 사용하고 있음을 나타내는 메시지를 볼 수 있습니다.If you look at the logs for the container (docker logs <container-id>), you should see a message indicating it's using the 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 목록에 몇 개의 항목을 추가합니다.Open the app in your browser and add a few items to your todo list.

  4. MySQL 데이터베이스에 연결하여 데이터베이스에 항목이 기록되고 있음을 증명합니다.Connect to the MySQL database and prove that the items are being written to the database. 암호는 secret 입니다.Remember, the password is secret.

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

    MySQL 셸에서 다음을 실행합니다.And in the MySQL shell, run the following:

    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 |
    +--------------------------------------+--------------------+-----------+
    

    사용자 테이블은 해당 항목을 포함하므로 다르게 표시될 것입니다.Obviously, your table will look different because it has your items. 그러나 항목이 테이블에 저장된 것은 확인할 수 있습니다.But, you should see them stored there!

Docker 확장을 잠시 살펴보면 앱 컨테이너 2가 실행되고 있음을 확인할 수 있습니다.If you take a quick look at the Docker extension, you'll see that you have two app containers running. 그러나 단일 앱으로 그룹화되었음을 나타내는 실제 표시는 없습니다.But, there's no real indication that they're grouped together in a single app. 해당 작업을 더 효율적으로 수행하는 방법은 잠시 후에 살펴보겠습니다.You'll see how to make that better shortly!

그룹화되지 않은 앱 컨테이너 2개를 보여 주는 Docker 확장

요약Recap

이제 별도의 컨테이너에서 실행되는 외부 데이터베이스에 데이터를 저장하는 애플리케이션을 만들었습니다.At this point, you have an application that now stores its data in an external database running in a separate container. 컨테이너 네트워킹에 대해 간략하게 배웠으며, DNS를 사용하여 서비스를 검색하는 방법을 살펴보았습니다.You learned a little bit about container networking and saw how service discovery can be performed using DNS.

그러나 애플리케이션을 시작하기 위해 수행해야 하는 모든 작업이 약간 부담스러울 수도 있습니다.But, there's a good chance you are starting to feel a little overwhelmed with everything you need to do to start up this application. 네트워크를 만들고, 컨테이너를 시작하고, 모든 환경 변수를 지정하고, 포트를 노출하고, 그 밖의 다른 작업도 수행해야 합니다.You have to create a network, start containers, specify all of the environment variables, expose ports, and more! 기억해야 할 사항도 많고, 다른 사용자에게 전달하려면 작업은 더욱 어려워질 것입니다.That's a lot to remember and it's certainly making things harder to pass along to someone else.

다음 섹션에서는 Docker Compose에 관해 설명합니다.In the next section, we'll talk about Docker Compose. Docker Compose를 사용하면 애플리케이션 스택을 훨씬 간편하게 공유할 수 있으며, 다른 사용자가 간단한 단일 명령으로 애플리케이션 스택을 실행하게 할 수 있습니다.With Docker Compose, you can share your application stacks in a much easier way and let others spin them up with a single (and simple) command!

다음 단계Next steps

자습서를 계속 진행합니다.Continue with the tutorial!