▤ 목차
도커 컴포즈 파일의 구조
도커 컴포즈 파일?
- 모든 컴포넌트가 실행 중일 때 애플리케이션이 어떤 상태여야 하는지를 기술하는 파일
- docker container run 명령으로 컨테이너를 실행할때 지정하는 모든 옵션을 한데 모아 놓은 단순한 형식의 파일
* 컨테이너 : 실제로 실행되고 있는 도커 인스턴스. 예를 들어, 웹 서버 (예: Nginx)가 실행되고 있는 컨테이너, 애플리케이션 서버(“Node.js 앱)이 실행되고 있는 컨테이너. 즉 웹 서버, 애플리케이션 서버, 데이터베이스 각각이 하나의 컨테이너로 실행되고 있는 것.
* 서비스 : 각 컨테이너는 하나의 역할을 담당하지만, 도커 컴포즈에서는 같은 역할을 하는 컨테이너들을 "서비스"라는 논리적인 단위로 묶어서 관리
도커 컴포즈 파일의 전체 스크립트.( to-do 애플리케이션을 실행하는 내용)
version: '3.7'
services:
todo-web:
image: diamol/ch06-todo-list
ports:
- "8020:80"
networks:
- app-net
networks:
app-net:
external:
name: nat
- YAML 문법으로 기술 , YAML 문법은 들여쓰기를 통해 구조를 정의하므로 들여쓰기가 중요
도커 컴포즈 파일의 구조
- version
- 파일에 사용된 도커 컴포즈 파일 형식의 버전을 나타낸다 : 버전마다 문법과 표현 가능 요소에 많은 변화가 있었으므로 버전 지정 필수
- 버전을 포함하여 실행하면 warnning messge 가 발생하지만 무시해도 된다.
- the attribute `version` is obsolete, it will be ignored, please remove it to avoid potential confusion
- 버전을 포함하여 실행하면 warnning messge 가 발생하지만 무시해도 된다.
- 파일에 사용된 도커 컴포즈 파일 형식의 버전을 나타낸다 : 버전마다 문법과 표현 가능 요소에 많은 변화가 있었으므로 버전 지정 필수
- networks
- 서비스 컨테이너가 연결될 모든 도커 네트워크를 열거하는 부분
- services
- 애플리케이션을 구성하느 모든 컴포넌트를 열거하는 부분
- 도커 컴포즈에서는 실제 컨테이너 대신 서비스 개념을 단위로 삼는다. 하나의 서비스를 같은 이미지로 여러 컨테이너에서 실행할 수 있기 때문이다.
- 컨테이너 대신 서비스 개념을 단위로 삼는 이유
- 도커 컴포즈는 단순히 하나의 컨테이너만 다루는 게 아니라, 여러 컨테이너를 조합해서 큰 애플리케이션을 구성하도록 설계되어 있기 때문
- 하나의 서비스에 같은 역할(웹서버 | 데이터베이스 | 애플리케이션 서버)을 하는 여러 컨테이너를 하나의 서비스로 묶어서 관리가 가능하기 때문
- 같은 이미지를 여러 컨테이너에서 실행한다는 뜻
- 서비스의 설정에서 replicas(복제본) 옵션을 사용하면 같은 이미지를 사용하는 컨테이너 여러 개를 실행 가능
- 예시
- 서비스의 설정에서 replicas(복제본) 옵션을 사용하면 같은 이미지를 사용하는 컨테이너 여러 개를 실행 가능
- 컨테이너 대신 서비스 개념을 단위로 삼는 이유
services:
web:
image: my-web-app:latest
deploy:
replicas: 3
왜 여러 컨테이너를 묶는 걸까? → 확장성(Scalability) 때문
트래픽이 많은 웹 서버를 운영해야 한다고 가정
- 하나의 컨테이너만 실행하면 그 컨테이너가 모든 요청을 처리해야 하므로 과부하가 생길 수 있다.
- 이를 해결하기 위해 같은 이미지를 사용하는 여러 컨테이너를 실행해서 트래픽을 분산 처리해야 한다.
▶ 이때, 도커 컴포즈에서 같은 역할을 하는 여러 컨테이너를 하나의 "서비스"로 묶어서 관리하면 쉽고 효율적
도커 컴포즈 파일의 전체 스크립트 중 아래의 부분이 어떻게 구성이 되는지를 보여주는 그림
services:
todo-web:
image: diamol/ch06-todo-list
ports:
- "8020:80"
networks:
- app-net
① todo-web 이라는 서비스는 diamol/ch06-todo-list 이미지로부터 단일 커테이너로 실행
② 이 컨테이너는 호스트 컴퓨터의 80번 포트로 자신의 8020번 포트를 공개
③ app-net 이라는 이름의 도커 네트워크에 연결
최종적인 결과는 아래의 명령을 실행한 것과 같은 상태가 된다.
docker container run -p 8020:80 --name todo-web --network nat diamol/ch06-todo-list
네트워크 부분 내용 설명
networks:
app-net:
external:
name: nat
nat 이라는 이름의 외부 네트워크로 연결
external = nat 네트워크가 이미 존재하므로 새로 생성하지 말라는 뜻
도커 컴포즈 파일 실행 방법
docker network create nat
cd (ch07/exercises/todo-list 가 있는 경로)
docker-compose up
Container todo-list-todo-web-1 Cre... ← 현재 있는 리소스와 애플리케이션을 구성하는 리소스를 비교해 더 필요한 요소를 생성
Attaching to todo-web-1 아래는 ← to-do 애플리케이션 시동 로그
도커 컴포즈를 사용해 여러 컨테이너로 구성된 애플리케이션 실행하기
4장에서 오늘의 천문 사진을 보여주는 애플리케이션은 여러가지 언어를 이용하여 구현된 분산 애플리케이션이다.
- 웹 프런트엔드 = 자바 (image-gallery)
- 로그 수집 모듈 = Node.js (accesslog)
- REST API = GO (iotd)
4 장에서는 분산 애플리케이션을 실행한 과정
1. 차례로 이들 컨테이너를 실행 시켜서 애플리케이션을 가동
2. 모든 컨테이너를 동일한 도커 가상 네트워크에 미리 약속된 이름으로 접속시켜 애플리케이션의 구성 요소가 서로 통신할 수 있도록 구성
4 장에서 실행한 분산 애플리케이션을 실행하기 위한 도커 컴포즈 스크립트
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
- accesslog = 설정값과 포트가 필요없으므로 이미지 이름만 기술
- iotd = REST API 이므로 포트 번호설정
- image-gallery = depends_on 을 실행하여 의존성을 선언.
- image-gallery 서비스를 실행하기 전에, 이 의존성을 만족하기 위해 iotd, accesslog 를 먼저 실행하여 시도함.
위 애플리케이션의 아키텍쳐를 나타낸 그림
* 참고로 이 그림은 도커 컴포즈 스크립트를 다이어그램으로 변환해 주는 도구를 사용해 만든것
( https://github.com/pmsipilot/docker-compose-viz )
<분리 모드 (detachede mode)로 애플리케이션을 실행하기>
(docker-compse 파일이 있는 폴더로 이동한 후)
docker-compose up --detach
- 컨테이너는 백그라운드에서 실행
- 로그를 확인하려면 명령어를 별도로 실행 docker-compose logs <service_name>
실행 한 결과
http://localhost:8010/ 에 접속하면 4장에서 실행한 애플리케이션과 동일하게 작동
< 도커 컴포즈를 사용해 iotd 서비스의 컨테이너 수를 늘려보기>
이점 : 컴포즈 파일을 이용해여 여러 개의 컨테이너로 구성된 애플리케이션을 마치 한 덩어리 처럼 다룰 수 있음.
특히 API 서비스는 상태가 없으므로 컨테이너를 늘리는 방법으로 스케일 아웃할 수 있다. 즉, 웹 컨테이너가 API에 데이터를 요청하면 도커가 여러 개의 API 컨테이너에 이 요청을 고르게 분배
docker-compose up -d --scale iotd=3
이제 http://localhost:8010/ 페이지를 여러 번 새로고침을 해보자
docker-compose logs --tail=1 iotd
--tail=1 : 각 iotd 컨테이너의 제일 마지막 로그만 출력
출력 된 내용을 보면 새로고침 요청을 늘어난 3개의 컨테이너가 고르게 나눠 처리한 것을 볼수 있다.
docker-compose ls
도커 컴포즈를 이용하여 재시작
// 애플리케이션 중지, 컴포즈 리소스와 컨테이너 삭제
docker-compose down
// 컴포즈 파일에 정의된 대로 리소소를 다시 생성, 애플리케이션 재시작
docker-compose up -d
docker-compose ls
앞에서 iot를 3개로 확장했던 것이 사라진 것을 running(3) 에서 알수 있다.
< 참고 > 도커 컴포즈는 YAML 파일에 정의된 애플리케이션 정의에 의존하는 클라이언트 측 도구임을 잊어서는 안된다.
도커 컨테이너간의 통신
애플리케이션 생애주기동안에 컨테이너가 교체되면 IP 주소도 변경된다.
IP 주소가 변경돼도 문제가 없도록 도커에서 DNS 를 이용해 서비스 디스커버리 기능을 제공한다.
<iot를 3개로 확장한 후 DNS 조회 : nslookup>
docker-compose up -d scale --iotd=3
docker container exec -it image-of-the-day-image-gallery-1 sh
/web # nslookup aceeslog
accesslog 서비스를 DNS에 조회한 결과 해당 컨테이너의 IP 주소가 조회된 것을 볼 수 있다.
도커 네트워크에 연결된 모든 컨테이너는 이 네트워크의 범위에 포함되는 IP 주소를 부여 받는다.
그리고 이 네트워크를 통해 컨테이너 간 통신이 가능하다.
▶ DNS 조회를 사용하면 컨테이너가 교체돼 IP 주소가 변경되더라도 항상 새로 만들어진 컨테이너에 접근할 수 있다.
< accesslog 컨테이너 삭제 후 애플리케이션 재실행할때 컴포즈의 행동 확인 >
만약 도커 명령행에서 accesslog 컨테이너를 직접 삭제하고 도커 컴포즈로 애플리케이션을 재실행할 때
1. accesslog 컨테이너가 없으므로 새로운 accesslog 컨테이너를 생성
2. 애플리케이션 재실행
▶ DNS 조회하면 새로운 IP 주소가 할당된것을 알 수 있다.
accesslog 컨테이너 삭제
docker container rm -f image-of-the-day-accesslog-1
삭제 후 DNS 확인한 결과
docker-compose up -d --scale iotd=3
을 실행한 후 결과
accesslog 컨테이너가 없으므로 새롭게 accesslog 컨테이너를 생성하여 대응
DNS 확인한 결과
DNS 조회 결과를 보면 accesslog 서비스에 새로 배정된 컨테이너가 기존 컨테이너의 IP를 그래도 유지했다.
이유) 기존 컨테이너가 삭제되면서 부여됐던 IP 주소도 재사용이 가능해졌기 때문
도커 컴포즈로 애플리케이션 설정값 지정하기
소규모 프로젝트라면 어떤 데이터 베이스를 사용하더라도 문제가 없다.
일정 규모 이상의 애플리케이션이라면 별도의 데이터베이스를 사용하는 것이 낫다.
이번에는 SQLite 데이터베이스를 사용하는 to-do 앱을 원격 컨테이너에서 동작하는 PostgreSQL 데이터베이스를 이용해보자.
SQLite → PostgreSQL
PostgreSQL을 사용하는 to-do 앱의 서비스를 정의한 컴포즈 파일
services:
todo-db:
image: diamol/postgres:11.5
ports:
- "5433:5432"
networks:
- app-net
todo-web:
image: diamol/ch06-todo-list
ports:
- "8030:80"
environment:
- Database:Provider=Postgres
depends_on:
- todo-db
networks:
- app-net
secrets:
- source: postgres-connection
target: /app/config/secrets.json
networks:
app-net:
secrets:
postgres-connection:
file: ./config/secrets.json
- todo-db 에서 사용할 데이터벵스 PostgresSQL 설정
- todo-wb
- environment 에서 사용할 데이터베이스 지정
- depends_on 에서 todo-db 연결 (의존성 연결)
- secrets:
- postgres-connection 에 설정된 비밀값을 secrets.json 에 기록
- secrets
- postgres-connection 이름으로 로컬에서 비밀값을 읽어오는 부분
위의 도커컴포즈 파일이 있는 폴더에서 실행했을 때 출력된 내용
docker-compose up -d
Tip. docker-compose 로 실행한 애플리케이션 만 보는 명령어
docker-compose ps
도커 컴포즈도 만능은 아니다.
docker-compose 파일을 실행하면 내가 설정한 상태로 애플리케이션을 실행할 수 있다.
하지만, 애플리케이션이 지속적으로 정의된 상태를 유지하도록 하는 기능이 없다.
→ 일부 컨테이너가 오류를 일으키거나 강제로 종료되면 docker-compose up 명령을 다시 실행시켜야 애플리케이션의 상태를 원대로 되돌아 간다.
도커 컴포즈를 사용하기 적합한 시기를 나타내는 그림
- 자신의 컴퓨터에서 앱을 실행하고 e2e 테스트를 수행하는데 사용
- 지속적 통합 프로세스 중 빌드 및 자동화된 테스트에 컴포즈를 사용해 앱을 실행
- 단일 서버에서 컴포즈를 실행해 테스트를 진행하며 테스트 환경을 최소한으로 유지
- 운영환경에는 컴포즈보다는 도커 스윔이나 쿠버네티스를 사용, 하지만 애플리케이션 정의에는 컴포즈 파일을 포맷을 사용
연습문제: 도커 컴포즈를 이용하여 애플리케이션을 좀 더 신뢰성 있게 실행하기
조건
- 호스트 컴퓨터가 재부팅되거나 도커 엔진이 재시작되면 애플리케이션 컨테이너도 재시작되도록 하라.
- 데이터베이스 컨테이너는 바인드 마운트에 파일을 저장해 애플리케이션을 재시작하더라도 데이터를 유지할 수 있도록 하라.
- 테스트를 위해 웹 애플리케이션은 80 번 포트를 주시하도록 하라.
참고
https://docs.docker.com/reference/compose-file/
Compose file reference
Find the latest recommended version of the Docker Compose file format for defining multi-container applications.
docs.docker.com
정답?
version: "3.7"
services:
todo-db:
image: diamol/postgres:11.5
restart: unless-stopped
environment:
- PGDATA=/var/lib/postgresql/data
volumes:
- type: bind
source: /data/postgres
target: /var/lib/postgresql/data
networks:
- app-net
todo-web:
image: diamol/ch06-todo-list
restart: unless-stopped
ports:
- "8050:80"
environment:
- Database:Provider=Postgres
depends_on:
- todo-db
secrets:
- source: postgres-connection
target: /app/config/secrets.json
secrets:
postgres-connection:
file: postgres-connection.json
networks:
app-net:
external:
name: nat
설명
restart: unless-stopped
위 설정의 동작
* 컨테이너가 비정상적으로 종료될 경우 자동 재시작
* 사용자가 컨테이너를 수동으로 중지한 경우에는 재시작하지 않음
* 서버 재부팅 시 다시 시작
-> 비정상 종료되거나 서버 재부팅 시 다시 시작하지만, 수동으로 중지된 경우는 재시작하지 않음.
volumes:
- type: bind
source: /data/postgres
target: /var/lib/postgresql/data
-> bind 를 이용하여 데이터를 보존
source : 호스트 컴퓨터에서 데이터를 저장하는 경로
target : 컨테이너 내부 경로,
컨테이너의 애플리케이션은 target 디렉토리에 데이터를 저장하지만, 실제로 그 데이터는 호스트의 source 경로에 저장
'책 > 도커 교과서' 카테고리의 다른 글
[ 책 ] [ 도커 교과서] 8장 헬스 체크와 디펜던시 체크로 앱의 신뢰성 확보하기 (0) | 2024.12.29 |
---|---|
[책] [ 도커교과서 ] 6장 도커 볼륨을 이용한 퍼시스턴트 스토리지 (0) | 2024.12.15 |
[책] [ 도커교과서 ] 5장 도커 허브 등 레지스트리에 이미지 공유하기 (0) | 2024.12.13 |
[책] [ 도커교과서 ] 4장 애플리케이션 소스코드에서 도커 이미지까지 (0) | 2024.12.13 |
[ 책 ] [ 도커교과서 ] 3장 도커 이미지 만들기 (0) | 2024.12.08 |