7 여러 컨테이너 앱 실행하기

seohan·2022년 3월 18일
0

대부분의 앱은 분산된 구성 요소에서 실행되는 프런트엔드 및 백엔드 구성 요소로 구축됩니다. 도커는 n 계층 모놀리스에서 최신 마이크로서비스에 이르기까지 분산 앱을 실행하는 데 적합합니다. 각 구성 요소는 자체 경량 컨테이너에서 실행되고 도커는 표준 네트워크 프로토콜을 사용하여 이들을 연결합니다. 도커 Compose를 사용하여 이와 같은 다중 컨테이너 앱을 정의하고 관리합니다.

Compose는 분산된 도커 앱을 표현하기 위한 파일 형식이며 이를 관리하기 위한 도구입니다.

7.1 Compose 파일의 구조

프론트엔드 웹 사이트, 백엔드 API 및 데이터베이스가 있는 앱의 경우 각 요소마다 하나씩 3개의 Dockerfile이 있을 수 있습니다.

도커 CLI를 사용하여 앱이 올바르게 실행되도록 모든 옵션을 지정하여 차례로 각 컨테이너를 시작할 수 있습니다. 이는 실패 가능성이 있는 수동 프로세스입니다. 옵션이 잘못되면 앱이 제대로 작동하지 않거나 컨테이너가 통신하지 못할 수 있기 때문입니다. 대신 Compose 파일을 사용하여 앱의 구조를 설명할 수 있습니다.

Compose 파일은 앱이 실행 중일 때의 모습을 표현합니다. docker container run 명령에 넣을 모든 옵션을 설정하는 파일 포맷입니다. 그런 다음 docker-compose 명령으로 앱을 실행합니다. 컨테이너, 네트워크, 볼륨 등 필요한 리소스를 파악하고 이를 생성하기 위해 도커 API에 요청을 보냅니다.

목록 7.1은 네트워크에 한 컨테이너를 연결하는 앱을 설명하는 docker-compose 파일

version: '3.7'
services:
   todo-web:
       image: diamol/ch06-todo-list
       ports:
           - "8020:80"
       networks:
           - app-net
 
networks:
   app-net:
       external:
             name: nat
  • services: 앱을 구성하는 모든 요소를 나열합니다. Compose는 한 이미지를 여러개의 컨테이너로 서비스를 실행 가능하므로 서비스라는 개념을 사용합니다.

  • networks: 사용 가능한 모든 도커 네트워크를 나열합니다.

Compose를 사용하여 이 앱을 실행할 수 있으며 한 컨테이너를 시작하여 원하는 상태에 도달할 수 있습니다. 그림 7.1은 앱 리소스의 아키텍처 다이어그램을 보여줍니다.

todo-web 서비스는 diamol/ch06-todo-list 이미지로부터 한 컨테이너를 실행합니다. 호스트의 포트 8020을 컨테이너의 포트 80에 게시하고 컨테이너를 app-net 네트워크에 연결합니다.

최종 결과는 다음을 실행하는 것과 같습니다.

docker container run -p 8020:80 --name todo-web --network nat diamol/ch06-todo-list

그림 7.1 간단한 컨테이너 아키텍처

서비스 이름은 다른 컨테이너가 도커 네트워크 연결에 사용할 수 있는 컨테이너 이름과 컨테이너의 DNS 이름이 됩니다. services의 네트워크 이름은 app-net 이지만 networks 섹션에서 해당 네트워크는 nat 라는 외부 네트워크에 대한 매핑으로 지정됩니다. external 옵션은 nat 네트워크가 이미 존재하므로 생성하지 않는다는 의미입니다.

TRY ch07/exercises/todo-list 폴더에서 다음을 실행합니다.

 docker network create nat
 docker-compose up

docker-compose 명령은docker-compose.yml 파일에서 todo-list 정의를 로드합니다. todo-web서비스에 대해 원하는 상태와 일치하는 컨테이너가 없으므로 하나의 컨테이너를 시작합니다. 또한 컨테이너를 실행할 때 나오는 모든 로그를 컨테이너 별로 그룹화하여 표시합니다.

직접 실행하면 도커 Hub에서 가져오는 이미지도 볼 수 있지만 명령을 실행하기 전에 이미 이미지를 가져왔습니다.

브라우저에서 확인하려면:

http://localhost:8020

docker-compose.yml 파일은 앱의 모든 런타임 속성을 설명하는 유일한 곳입니다. 앱을 구성하는 데 필요한 모든 속성과 volumesecret과 같은 루트 리소스도 기록할 수 있습니다. 이 파일은 다중 컨테이너 앱을 실행할 때 더욱 유용합니다.

7.2 다중 컨테이너 앱

4장에서 우리는 NASA의 image-of-the-day API의 이미지를 보여주는 분산 앱을 만들었습니다. Java 프론트엔드 웹사이트, Go로 작성된 REST API, Node.js로 작성된 로그 수집기가 있었습니다. 각 컨테이너를 차례로 시작하여 앱을 실행했으며 구성 요소가 서로를 찾을 수 있도록 컨테이너를 동일한 네트워크에 연결하고 올바른 컨테이너 이름을 사용해야 했습니다.

목록 7.2에서 image-gallaery 앱에 대한 Compose 파일의 services 섹션입니다. 서비스 nat 네트워크에 연결됩니다.

Listing 7.2 image-gallary 앱을 위한 services 섹션

services:
  accesslog:
     image: diamol/ch04-access-log 
  iotd:
     image: diamol/ch04-image-of-the-day
     ports:
        - "80"
  image-gallery:
     image: diamol/ch04-image-gallery
     ports:
        - "8010:80"
     depends_on:
        - accesslog
        - iotd
     networks:
     	- app-net
  • accesslog는 속성이 없으므로 이미지 이름만 설정합니다.
  • iotd는 REST API입니다.
  • image-gallery: 이미지 이름과 게시된 특정 포트가 있습니다.
  • depends_on: 다른 두 서비스에 종속되어 있으므로 서비스 시작 전에 해당 서비스가 실행 중인지 확인합니다.

그림 7.3 같은 네트워크에 연결된 세 개의 서비스를 지정

그림 7.3은 Compose 파일로부터 아키텍처의 PNG 이미지를 생성하는 도구에서 다이어그램을 생성했습니다. 다이어그램 도구는 물론 컨테이너에서 실행됩니다.

TRY ch07/exercises/image-of-the-day에서 다음을 실행합니다. 이번에는 분리 모드에서 실행합니다.

 docker-compose up --detach

브라우저에서http://localhost:8010으로 확인할 수 있습니다.

이제 컨테이너가 함께 작동하도록 구성해야 하는 방법을 정의했습니다. 또한 앱을 전체적으로 관리할 수도 있습니다. API 서비스는 사실상 상태 비저장이므로 여러 컨테이너에서 실행하도록 확장할 수 있습니다. 웹 컨테이너가 API에서 데이터를 요청하면 도커는 실행 중인 API 컨테이너에서 해당 요청을 공유합니다.

TRY iotd 서비스의 규모를 늘린 다음 웹 페이지를 몇 번 새로고침하고 iotd 컨테이너의 로그를 확인합니다.

docker-compose up -d --scale iotd=3

브라우저에서 http://localhost:8010에서 리프레시 합니다.

iotd 서비스의 로그를 확인합니다

docker-compose logs --tail=1 iotd
image-of-the-day-iotd-3 --- [ main] iotd.Application : Started Application in 3.126 seconds (JVM running for 3.48)
image-of-the-day-iotd-1 iotd.ImageController : Fetched new APOD image from NASA
image-of-the-day-iotd-2 iotd.ImageController : Fetched new APOD image from NASA

그림 7.5 Compose를 사용하여 앱 구성 요소 확장 및 해당 로그 확인

Compose는 이제 5개의 컨테이너를 관리합니다. Compose를 사용하여 전체 앱을 제어할 수 있습니다. 모든 컨테이너를 중지하여 컴퓨팅 리소스를 절약하고 앱을 실행해야 할 때 모든 컨테이너를 다시 시작할 수 있습니다. 그러나 이들은 도커 CLI를 사용하여 작업할 수도 있는 컨테이너입니다. Compose는 컨테이너 관리를 위한 별도의 명령줄 도구이지만 도커 CLI와 동일한 방식으로 도커 API를 사용합니다.

TRY 앱을 stop한 다음 start한 다음 실행 중인 모든 컨테이너를 확인합니다.

docker-compose stop
docker-compose start
docker container ls

Compose는 Compose 파일을 기반으로 도커 API에 지침을 보내는 명령줄입니다. 도커 자체는 컨테이너만 실행합니다. 많은 컨테이너가 단일 앱을 나타낸다는 것을 인식하지 못합니다. Compose만이 이를 알고 있으며, Compose는 docker-compose.yml 파일을 보고 앱의 구조만 알고 있으므로 해당 파일을 사용하여 앱을 관리할 수 있어야 합니다.

Compose 파일이 변경되거나 실행 중인 앱을 업데이트하는 경우와 같이 앱이 Compose 파일과 동기화되지 않을 수 있습니다. Compose를 사용하여 앱을 관리하기 위해 돌아갈 때 예기치 않은 동작이 발생할 수 있습니다. iotd 서비스를 3개의 컨테이너로 확장했지만 해당 구성은 Compose 파일에 기록되지 않았습니다. 앱을 종료한 다음 다시 만들면 Compose가 원래 규모로 되돌립니다.

TRY Compose를 사용하여 앱을 중단했다가 다시 복구하십시오. 그런 다음 실행 중인 컨테이너의 목록을 확인합니다.

docker-compose down
docker-compose up -d
docker container ls

docker-compose down은 앱을 제거하므로 Compose는 컨테이너를 중지하고 제거합니다. Compose 파일에 기록되고 외부 플래그가 지정되지 않은 경우 네트워크 및 볼륨도 제거합니다. 그런 다음 docker-compose up이 앱을 시작하고 실행 중인 컨테이너가 없기 때문에 Compose는 모든 서비스를 생성하지만 Compose 파일의 앱 정의를 사용하므로 API 서비스는 컨테이너 하나만 시작합니다.

앱을 다시 시작하는 것이 목표지만 실수로 API 서비스 규모도 축소했습니다.

Compose는 사용이 간편하고 강력하지만 클라이언트 측 도구이므로 앱 정의 파일을 잘 관리해야 합니다. Compose로 앱을 배포하면 도커 리소스가 생성되지만 도커 엔진은 이러한 리소스가 관련되어 있는지 알지 못합니다.

7.3 Docker가 컨테이너를 함께 연결하는 방법

컨테이너는 자체 네트워크 공간이 있는 가상 환경입니다. 각 컨테이너에는 도커에서 할당한 가상 IP 주소가 있으며 동일한 도커 네트워크에 연결된 컨테이너는 해당 IP 주소를 사용하여 서로 연결할 수 있습니다. 그러나 컨테이너는 앱 수명 주기 동안 교체되고 새 컨테이너는 새 IP 주소를 갖게 되므로 도커는 DNS를 통한 서비스 검색도 지원합니다.

브라우저에서 blog.sixeyed.com을 가리키면 도메인 이름을 사용하고 있으며 이 이름은 내 블로그를 호스팅하는 도커 서버 중 하나의 IP 주소로 확인됩니다. 컴퓨터는 실제로 IP 주소를 사용하여 콘텐츠를 가져오지만 사용자는 훨씬 더 친숙한 도메인 이름으로 작업합니다.

도커에는 자체 DNS 서비스가 내장되어 있습니다. 컨테이너에서 실행되는 앱은 다른 구성 요소에 액세스하려고 할 때 도메인을 조회합니다. 도커의 DNS 서비스는 해당 조회를 수행합니다. 도메인 이름이 실제로 컨테이너 이름인 경우 도커는 컨테이너의 IP 주소를 반환하고 소비자는 도커 네트워크에서 직접 작업할 수 있습니다. 도메인 이름이 컨테이너가 아닌 경우 도커가 실행 중인 서버에 요청을 전달하므로 표준 DNS 조회를 통해 조직의 네트워크 또는 공용 인터넷에서 IP 주소를 찾습니다.

image-gallery 앱을 사용하면 실제로 작동하는 것을 볼 수 있습니다. 도커 DNS 서비스의 응답에는 단일 컨테이너에서 실행되는 서비스에 대한 IP 주소가 포함되거나 서비스가 여러 컨테이너에서 대규모로 실행 중인 경우 여러 IP 주소가 포함됩니다.

TRY Use Compose to bring the application up with the API running at a scale of three. Then connect to a session in the web container and perform a DNS lookup:

$ docker-compose up -d --scale iotd=3
# for Linux containers:
$ docker container exec -it image-of-the-day_image-gallery_1 sh
nsl
/web # nslookup accesslog
nslookup: can't resolve '(null)': Name does not resolve

Name:      accesslog
Address 1: 172.29.0.3 image-of-the-day-accesslog_1.nat

/web # exit

같은 도커 네트워크에 연결된 컨테이너는 같은 네트워크 범위의 IP 주소를 가져오고 해당 네트워크를 통해 연결됩니다. DNS를 사용하면 컨테이너가 교체되고 IP 주소가 변경될 때 도커의 DNS 서비스가 항상 도메인 조회에서 현재 컨테이너의 IP 주소를 반환하므로 앱이 계속 작동한다는 것을 의미합니다.

도커 CLI를 사용하여 accesslog 컨테이너를 수동으로 제거한 다음 Compose를 사용하여 앱을 다시 복구하여 확인할 수 있습니다. Compose는 실행 중인 accesslog 컨테이너가 없음을 확인하므로 새 컨테이너를 시작합니다. 해당 컨테이너는 생성되는 다른 컨테이너에 따라 도커 네트워크의 새 IP 주소를 가질 수 있으므로 도메인 조회를 실행할 때 다른 응답을 볼 수 있습니다.

TRY 도커 CLI를 사용하여 accesslog 컨테이너를 제거한 다음 Compose를 사용하여 앱을 원하는 상태로 되돌립니다. 그런 다음 Linux에서 sh에서 웹 컨테이너에 다시 연결하고 DNS 조회를 실행합니다.

$ docker container rm -f image-of-the-day_accesslog_1
$ docker-compose up -d --scale iotd=3
 
# for Linux containers:
$ docker container exec -it image-of-the-day_image-gallery_1 sh

accesslog의 DNS를 확인하면

/web # nslookup accesslog

결과는

nslookup: can't resolve '(null)': Name does not resolve

Name:      accesslog
Address 1: 172.29.0.3 image-of-the-day-accesslog-1.nat

iotd의 DNS를 확인하려면

/web # nslookup iotd

결과는

nslookup: can't resolve '(null)': Name does not resolve

Name:      iotd
Address 1: 172.29.0.4 image-of-the-day-iotd-3.nat
Address 2: 172.29.0.5 image-of-the-day-iotd-1.nat
Address 3: 172.29.0.2 image-of-the-day-iotd-2.nat

7.4 Compose의 앱 구성

6장의 todo-list 앱은 한 컨테이너로 실행할 수 있으며, 이 경우 컨테이너 내부에 SQLite 데이터베이스 파일에 데이터를 저장합니다. 볼륨을 사용하여 데이터베이스 파일을 관리하는 방법을 6장에서 보았습니다. SQLite는 소규모 프로젝트에 적합하지만 원격 Postgres 데이터베이스를 사용하도록 구성할 수 있습니다.

앱이 한 컨테이너에서 실행되고 Postgres가 다른 컨테이너에서 실행되는 분산 앱을 실행할 수 있습니다. todo-list 앱용 도커 이미지는 개발 환경에 대한 기본 구성을 패키징하지만 구성 설정을 적용하여 다른 환경과 함께 작동할 수 있습니다.

목록 7.3에서 todo-db 서비스와 todo-web 서비스를 지정합니다.

services:
   todo-db:
       image: diamol/postgres:11.5
       ports:
           - "5433:5432"
       networks:
           - app-net
   todo-web:
       image: diamol/ch06-todo-list
       ports:
           - "8020:80"
       environment:
           - Database:Provider=Postgres
       depends_on:
           - todo-db
       networks:
           - app-net
       secrets:
           - source: postgres-connection
             target: /app/config/secrets.json

secrets은 런타임 환경에서 읽어서 컨테이너 내부의 파일로 채울 수 있습니다. 이 앱은 /app/config/secrets.jsonpostgres-connection 이라는 비밀 내용이 포함된 파일을 갖게 됩니다.

secrets는 일반적으로 클러스터 환경의 컨테이너 플랫폼에서 제공합니다. 클러스터 데이터베이스에 저장되고 암호화될 수 있으므로 데이터베이스 연결 문자열, 인증서 또는 API 키와 같은 민감한 구성 데이터에 유용합니다. 도커를 실행하는 단일 머신에는 secret에 대한 클러스터 데이터베이스가 없으므로 Compose를 사용하면 파일에서 로드할 수 있습니다. 목록 7.4에 표시된 것처럼 이 Compose 파일의 끝에 secrets 섹션이 있습니다.

Listing 7.4 로컬 파일에서 암호 읽기

 secrets:
   postgres-connection:
         file: ./config/secrets.json

이는 호스트의 secrets.json 파일에서 postgres-connection 이라는 비밀을 로드하도록 합니다. 이 시나리오는 6장에서 다룬 바인드 마운트와 비슷합니다. 실제로 호스트의 파일은 컨테이너에 표시됩니다. 그러나 이를 secret으로 정의하면 클러스터 환경에서 암호화된 실제 비밀로 마이그레이션할 수 있는 옵션이 제공됩니다.

앱 구성을 Compose 파일에 연결하면 동일한 이미지를 다양한 방식으로 사용하고 각 환경의 설정을 명시할 수 있습니다. 개발 및 테스트 환경에 대해 별도의 Compose 파일을 보유하고 다양한 포트를 게시하고 앱의 다양한 기능을 트리거할 수 있습니다. 이 Compose 파일은 Postgres 모드에서 todo-list 앱을 실행하기 위한 환경 변수와 비밀을 설정하고 데이터베이스에 연결하기 위한 세부 정보를 제공합니다.

앱을 실행하면 동일한 방식으로 작동하는 것을 볼 수 있지만 이제 데이터는 앱과 별도로 관리할 수 있는 Postgres 컨테이너에 저장됩니다.

TRY ch07/exercises/todo-list-postgres에서 앱을 시작합니다.

$ docker-compose up -d
$ docker-compose ps
CONTAINER ID   IMAGE                          COMMAND                  CREATED          STATUS          PORTS                    NAMES
fe899a9e5051   diamol/ch06-todo-list "dotnet ToDoList.dll"    20 seconds ago   Up 18 seconds   0.0.0.0:8030->80/tcp     todo-list-postgres-todo-web-1
7586af705c3b   diamol/postgres:11.5 "docker-entrypoint.s…"   20 seconds ago   Up 19 seconds   0.0.0.0:5433->5432/tcp   todo-list-postgres-todo-db-1

브라우저에서 http://localhost:8030에서 이 버전의 todo-list 앱을 찾아볼 수 있습니다.

기능은 같지만 데이터가 Postgres 컨테이너에 저장됩니다. 데이터베이스 클라이언트로 확인할 수 있습니다. 저는 Postgres, MySQL 및 SQL Server 데이터베이스에 연결하기 위한 빠른 오픈 소스 크로스 플랫폼 UI인 Sqlectron을 사용합니다. 서버 주소는 localhost:5433 이며, 이는 컨테이너에서 게시한 포트입니다. 데이터베이스는 todo 라고 하고 사용자 이름은 postgres 이며 암호는 없습니다. 그림 7.11에서 웹 앱에 일부 데이터를 추가했으며 Postgres에서 쿼리할 수 있음을 볼 수 있습니다.

런타임 구성에서 앱 패키지를 분리하는 것은 도커의 주요 이점입니다. 앱 이미지는 빌드 파이프라인에서 생성되며 동일한 이미지가 프로덕션 준비가 될 때까지 테스트 환경을 통해 진행됩니다. 각 환경은 환경 변수를 사용하거나 Compose 파일에서 쉽게 캡처할 수 있는 마운트 또는 비밀을 사용하여 자체 구성 설정을 적용합니다. 모든 환경에서 동일한 이미지로 작업하고 있으므로 다른 모든 환경에서 테스트를 통과한 정확히 동일한 바이너리와 의존성을 프로덕션에 릴리스하고 있다는 확신을 가질 수 있습니다.

7.5 Compose가 해결하는 문제

Compose는 복잡한 분산 앱의 설정을 표현하는 매우 깔끔한 방법이며 효과적인 배포 가이드로서 간단하고 실행 가능합니다.

Compose는 컨테이너를 더 많이 사용하기 시작할 때 툴킷의 유용한 부분이지만 용도와 제약 사항을 정확히 이해하는 것이 중요합니다. Compose를 사용하면 앱을 정의하고 도커를 실행하는 단일 시스템에 정의를 적용할 수 있습니다. 해당 시스템의 라이브 도커 리소스를 Compose 파일에 설명된 리소스와 비교하고 업데이트된 리소스를 교체하고 필요한 위치에 새 리소스를 생성하기 위해 도커 API에 요청을 보냅니다.

docker-compose up 을 실행할 때 앱의 원하는 상태를 얻을 수 있지만 Compose가 끝나므로 더 이상 원하는 상태를 유지하도록 지속적으로 실행되지 않습니다. 컨테이너가 실패하거나 컨테이너를 수동으로 제거하는 경우 Compose는 명시적으로 docker-compose up을 다시 실행할 때까지 컨테이너를 다시 시작하거나 교체하지 않습니다. 그림 7.12는 Compose가 앱 수명 주기에 적합한 위치에 대한 좋은 아이디어를 제공합니다.

그림 7.12 개발에서 프로덕션까지 앱 수명 주기에서 Compose를 사용하는 경우

Compose가 프로덕션에 적합하지 않다는 것은 아닙니다. 이제 막 도커로 시작하고 개별 VM에서 컨테이너로 워크로드를 마이그레이션하는 경우 시작점으로 괜찮을 수 있습니다. 해당 도커 시스템에서는 고가용성, 로드 밸런싱 또는 장애 조치를 얻을 수 없지만 개별 앱 VM도 마찬가지입니다. 모든 앱에 대한 일관된 Dockerfiles 및 docker-compose 파일과 앱 배포 및 관리를 위한 일관된 도구를 얻을 수 있습니다. 컨테이너 클러스터 실행을 살펴보기 전에 시작하기에 충분할 수 있습니다.

7.6 Lab

Compose에는 앱 실행에 안정성을 추가하는 몇 가지 유용한 기능이 있습니다. 이 실습에서는 테스트 환경에서 할 일 웹 앱을 보다 안정적으로 실행하기 위해 Compose 정의를 생성해 보겠습니다.

  • 시스템이 재부팅되거나 도커 엔진이 다시 시작되면 앱 컨테이너가 다시 시작됩니다.
  • 데이터베이스 컨테이너는 파일을 저장하기 위해 바인드 마운트를 사용하므로 앱을 다시 가동할 수 있지만 데이터는 유지할 수 있습니다.
  • 웹 앱은 테스트를 위해 표준 포트 80에서 수신 대기해야 합니다.

힌트:

https://github.com/sixeyed/diamol/blob/master/ch07/lab/README.md

profile
코드코드

0개의 댓글