📕 목차
1. Deploy Multiple Applications
2. Override file
3. Environment variation & Secret value
4. Extended Field
5. Setting workflow
6. Practice
1. Deploy Multiple Applications
📌 As-is
- Docker가 운영 환경에 상관 없이 Application을 실행할 수는 있지만, 운영 환경마다 다르게 Application을 동작시키는 것은 다른 문제다.
- Application을 개발 환경과 테스트 환경, 운영 환경 다르게 구동해야 하는 경우가 있다.
- 운영 환경은 1.5 버전
- 핫픽스 테스트 환경은 1.5.1 버전
- 사용자 테스트 환경은 1.6 버전
- 시스템 테스트 환경은 1.7 버전
참고로 Scaling 기능이 필요 없고, 운영 환경만큼의 성능을 요구하지 않는 비운영 환경이야 말로 Docker compose가 활약할 수 있는 환경이다.
📌 Docker resource가 종속된 Application 구분
💡 Docker compose는 Lable 명명 규칙에 따라 Application에 속한 Docker resource를 구분한다.
(project_name)_(service_name)_(number)
- 프로젝트(Project) : Docker resource가 어떤 Application의 일부인지 아닌지를 판단하기 위해 Docker compose가 사용하는 개념
- project name : 기본값으로 Docker compose 파일이 들어있던 디렉토리명
- service name : Compose 파일에 정의된 service 이름으로 DNS 상의 domain 이름으로도 쓰인다.
- number : Container의 경우 번호를 부여
예를 들어, Docker compose가 Docker resource를 만들 때 다음과 같은 과정을 거치게 된다.
- project name을 resource 이름의 접두사로 붙인다.
- directory가 app1이고, compose에 정의된 service가 web, volume이 disk인 경우
- app1_web
- app1_disk
- directory가 app1이고, compose에 정의된 service가 web, volume이 disk인 경우
- Container인 경우 번호를 접미사로 붙여 scaling에 대응할 수 있다.
- app1_web_1, 다음에 추가되는 Container는 app1_web_2가 된다.
🟡 프로젝트 이름 바꾸어 실행하기
docker-compose -f .\todo-list\docker-compose.yml -p todo-test up -d
docker container ls
# Container가 공개된 port 번호 확인
docker container port todo-test-todo-web-1 80
- -p : Docker compose에서 project name을 명시해주는 옵션
- 하나의 Compose 파일로 project name만 바꾸면 여러 개의 Application을 실행할 수 있다.
- docker container port 부명령으로 임의로 공개된 port 번호를 알아낼 수 있다.
- 무작위로 정해진 공개 port를 일일이 찾아내야 해서 번거롭다.
2. Override file
📌 Override
# docker-compose.yml
services:
todo-web:
image: diamol/ch06-todo-list:v2
PS C:\Users\qud12\docker\080258\ch10\exercises> cat .\todo-list\docker-compose.yml
version: "3.7"
services:
todo-web:
image: diamol/ch06-todo-list
ports:
- 80
environment:
- Database:Provider=Sqlite
networks:
- app-net
# Override file
services:
todo-web:
image: diamol/ch06-todo-list:v2
- 각 설정 또는 환경마다 Compose 파일을 두는 방법은 유지보수 측면에서 바람직하지 않다.
- 모든 환경에서 공통으로 쓰이는 속성이 정의된 docker-compose.yml을 Overriding 하는 방식을 사용할 수 있다.
- Docker compose는 여러 파일을 합쳐서 Compose 파일을 구성하여, 나중에 지정된 파일 내용을 기존 파일에 덮어쓴다.
📌 Compose 파일 병합
docker-compose -f .\todo-list\docker-compose.yml -f .\todo-list\docker-compose-v2.yml config
- Override할 Compose 파일에는 해당 환경에서 변경할 항목만 기술하면 된다.
- 기존 Compose 파일 구조를 유지해야 Dockcer compose가 두 정의를 연결지을 수 있다.
- Docker compose는 하나 이상의 파일이 인자로 지정되면 두 파일을 병합한다.
- 인자로 받은 순서대로 병합한다.
- config : 입력 파일 내용이 유효한 경우에만 최종 출력을 보여준다. (실행하지는 않는다.)
📌 실전 응용 사례
services:
numbers-api:
image: diamol/ch08-numbers-api:v3
networks:
- app-net
numbers-web:
image: diamol/ch08-numbers-web:v3
environment:
- RngApi__Url=http://numbers-api/rng
networks:
- app-net
networks:
app-net:
- docker-compose.yml
- 기본이 되는 Compose 파일
- 웹 및 API 정의가 되어있으나 port나 Docker network 정의는 없다.
services:
numbers-api:
ports:
- "8087:80"
healthcheck:
disable: true
numbers-web:
entrypoint:
- dotnet
- Numbers.Web.dll
ports:
- "8088:80"
networks:
app-net:
name: numbers-dev
- docker-compose-dev.yml
- 개발 환경 대상 설정
- Dokcer network와 Service 공개 port를 정의
- health & dependency check 비활성화
- 개발자들이 빠르게 Application을 실행하는 것을 목적으로 한다.
services:
numbers-api:
healthcheck:
interval: 20s
start_period: 15s
retries: 4
numbers-web:
ports:
- "8080:80"
restart: on-failure
healthcheck:
test: ["CMD", "dotnet", "Utilities.HttpCheck.dll", "-t", "250"]
interval: 20s
timeout: 10s
retries: 4
start_period: 10s
networks:
app-net:
name: numbers-test
- docker-compose-test.yml
- 테스트 환경 대상 설정
- Docker network를 정의
- health check를 설정하고, Web service 공개 port를 정의
- API service port는 공개하지 않는다.
version: "3.7"
services:
numbers-api:
healthcheck:
interval: 10s
retries: 2
restart: always
ports:
- "8090:80"
numbers-web:
restart: always
ports:
- "80:80"
healthcheck:
interval: 10s
retries: 2
networks:
app-net:
name: numbers-uat
- docker-compose-uat.yml
- 사용자 인수 테스트 환경 대상 설정
- Docker network, Web service 80 port 설정
- Service 에러 시, 항상 재시작하도록 설정
- health check를 더 꼼꼼하게 하도록 설정
🟡 실행해보기
환경마다 다른 port 번호를 사용하므로 여러 개의 Application을 단일 Docker Host에서 실행할 수 있다.
docker-compose -f .\numbers\docker-compose.yml -f .\numbers\docker-compose-dev.yml -p numbers-dev up -d
docker-compose -f .\numbers\docker-compose.yml -f .\numbers\docker-compose-test.yml -p numbers-test up -d
docker-compose -f .\numbers\docker-compose.yml -f .\numbers\docker-compose-uat.yml -p numbers-uat up -d
- 개발 팀에서는 이들 모두를 한 Server에서 실행할 수 있다.
- port를 다르게 하여 자신의 업무와 관련된 환경에 접근해서 작업하면 된다.
- 자신의 Network에 속한 API Container와만 통신 가능하므로 세 환경의 Application은 서로 독립적이다.
- 같은 Docker network에 속해있지 않으면 Domain name으로 식별할 수 없다.
🟡 Container 제거
# 실패
docker-compose down
# 실패
docker-compose -f .\numbers\docker-compose.yml -f .\numbers\docker-compose-test.yml down
# 성공
docker-compose -f .\numbers\docker-compose.yml -f .\numbers\docker-compose-test.yml -p numbers-test down
- project가 기본값으로 지정한 resource 식별값에 매칭되는 값을 찾지 못하므로 실패한다.
- 'numbers-numbers-web-1'과 'numbers-numbers-api-1'이라는 Container가 없으므로 제거 되었다고 판단하여 명시적으로 이름이 지정된 Network를 제거하려 시도하지만, 아직 Container가 연결되어 있으므로 실패한다.
- 'number-test-numbers-web-1' Container와 API Container 또한 정상적으로 제거하고 Network를 제거한다.
- 테스트 환경의 Application을 종료하기 위해서는 해당 환경의 Container와 Network를 제거해야 한다.
- Project name을 명시적으로 변경했으므로 모든 파일과 project 정보를 정확히 지정해야 한다.
- Override file 관리에 드는 오버헤드를 고려하여, Application 배포 및 폐기 스크립트 작성과 자동화를 익혀라.
3. Environment variation & Secret value
📌 Application setting
# secret.js
{
"ConnectionStrings": {
"ToDoDb": "Server=todo-db;Database=todo;User Id=postgres;Password=postgres;"
}
}
# 코어 Compose 파일
services:
todo-web:
image: diamol/ch06-todo-list
secrets:
- source: todo-db-connection
target: /app/config/secrets.json
- Container의 환경 차이는 Override file을 통해 기술할 수 있지만, Application setting은 Environment variation이나 setting file의 값으로 주입할 수 있다.
- secret value는 Docker compose, Docker swarm, Kubernetes 모두 지원하는 기능이다.
- secret value 원본 위치 : Container Runtime이 secret value를 읽어오는 경로
- secret value 대상 위치 : Container 안에서 secret value가 위치할 경로
- secret source를 명시했으면 해당 secret value가 Compose 파일에 명시되어 있어야 한다.
🟡 Override file
# Override file
services:
todo-web:
ports:
- 8089:80
environment:
- Database:Provider=Sqlite
env_file:
- ./config/logging.debug.env
secrets:
todo-db-connection:
file: ./config/empty.json
# ./config/logging.debug.env
Logging__LogLevel__Default=Debug
Logging__LogLevel__System=Debug
Logging__LogLevel__Microsoft=Debug
- environment
- Container 안에서만 사용되는 Envirionment variation을 직접 추가한다.
- 해당 환경 변수 값을 적용하면 데이터베이스로 Sqlite를 사용한다.
- 평문 텍스트로 작성되므로 API key나 DB 접속 정보 같은 민감한 정보는 사용하지 않는 것이 좋다.
- env_file
- 텍스트 파일의 경로를 값으로 읽는다.
- 변수 이름과 값을 등호(=)로 구분해 한 줄에 하나씩 정의한다.
- 같은 Environment variation을 여러 Component에서 공유해 사용할 수 있다.
- Service 간에 공유하는 설정이 많은 경우 유용하다.
- 원격 컴퓨터에서 실행 중인 Docker engine을 다룰 때도 Local PC 설정값을 적용할 수 있다.
- secrets
- Compose 파일 최상위 프로퍼티 중 하나
- secret source로 명시한 실제 값 혹은 경로가 정의된다.
- Container Runtime 과정에서 적용 가능하다.
- 민감한 정보가 유출될 우려가 없기 때문에 유연성 면에서 가장 뛰어나다.
🟡 실행해보기
docker-compose -f .\todo-list-configured\docker-compose.yml \
-f .\todo-list-configured\docker-compose-dev.yml \
-f .\todo-list-configured\docker-compose-dev-windows.yml -p todo-dev up -d
curl http://localhost:8089/list
docker container logs --tail 10 todo-dev-todo-web-1
📌 Host PC의 환경변수 전달하기
# override file
services:
todo-web:
ports:
- "${TODO_WEB_PORT}:80"
environment:
- Database:Provider=Postgres
env_file:
- ./config/logging.information.env
networks:
- app-net
todo-db:
image: diamol/postgres:11.5
ports:
- "${TODO_DB_PORT}:5432"
networks:
- app-net
networks:
app-net:
name: todo-test
secrets:
todo-db-connection:
file: ./config/secrets.json
- Compose 파일을 수정하지 않아도 환경 변수만 바꾸면 다른 테스트 환경을 만들어낼 수 있다. (이식성 증가)
✨ .env
# 컨테이너 설정값 - 공개포트
TODO_WEB_PORT=8877
TODO_DB_PORT=5432
# 도커 컴포즈 실행 옵션 - 컴포즈 파일 지정, 프로젝트 이름
COMPOSE_PATH_SEPARATOR=;
COMPOSE_FILE=docker-compose.yml;docker-compose-test.yml
COMPOSE_PROJECT_NAME=todo_ch10
- Compose가 Application을 실행할 때 대상 directory에서 .env 파일을 발견하면 환경 파일로 간주한다.
- Compose는 .env 파일로부터 환경 변수를 읽어 Application을 실행하기 전에 먼저 적용한다.
- 내용을 조금만 바꾸면 다른 환경에도 적용 가능하다.
4. Extended Field
📌 확장 필드(Extended Field)
- Service 간 많은 설정값을 공유하는 Compose file이 너무 커지는 문제를 해소하기 위해 쓴다.
- YAML의 여러 블록을 한곳에서 정의하는 기능이다.
- Compose 파일 전체에 걸쳐 해당 블록을 재사용하는 효과를 얻을 수 있다.
- 최상위 블록 외부라면 어디서든 정의할 수 있으며, 이름에 앰퍼샌드 문법을 사용한다.
- 일종의 사용자 정의 필드다.
그런데 그리 널리 쓰이는 기능은 아니라고 한다.
x-logging: &logging
logging:
options:
max-size: '100m'
max-file: '10'
x-labels: &labels
app-name: image-gallery
- 확장 필드 블록은 관습적으로 기존 블록에 'x-'를 접두어로 붙여 쓴다.
- logging 블록은 logging 속성을 명시했으므로 serivce 정의에 바로 사용할 수 있다.
- labels 블록은 labels 속성을 명시하지 않았으므로 labels 필드 내에서 사용할 수 있다.
services:
accesslog:
<<: *logging
labels:
<<: *labels
iotd:
ports:
- 8080:80
<<: *logging
labels:
<<: *labels
public: api
- <<:*필드명 : YAML 병합 문법
- 해당 위치에 확장 필드값이 병합된다.
하지만 이 또한 여러 Compose 파일에 한꺼번에 적용할 수는 없다. (Override해서 확장 필드를 쓸 수는 없다.)
이는 Compose의 한계라기 보다는 YAML 포맷의 한계에 가깝다.
5. Setting workflow
📌 환경을 분리하는 핵심 목적
1️⃣ Application 구성 요소의 조합
- 모든 환경에서 전체 스택을 실행할 필요는 없으니, 필요한 요소만 선택할 수 있다.
- 개발자는 모니터링을 위한 대시보드를 제외할 수 있다.
- 테스트 환경에서는 Container에서 실행한 DB를 사용하고, 운영 환경에서는 Cloud DB를 사용하도록 선택할 수 있다.
- Override 파일을 사용하면 공통된 Service를 제외하고, 환경마다 Service를 다르게 설정할 수 있다.
2️⃣ Container 설정
- 각 환경의 상황과 요구 사항에 맞추어 설정을 바꿀 수 있다.
- 공개 포트는 다른 Container와 충돌하지 않아야 한다.
- Volume 경로는 테스트 환경에선 Local drive를 사용하겠지만, 운영 환경에서는 공유 Storage가 될 것이다.
- Override 파일과 Docker network로 각 Application을 분리하여 단일 서버에 여러 개의 Application을 실행할 수 있다.
3️⃣ Application 설정
- Environment 별로 Container 내부 동작을 다르게 할 수 있다.
- Override 파일과 secret value를 이용해 상황에 맞는 Application 설정 값을 Container에 주입할 수 있다.
📌 Workflow
💡 같은 Image가 모든 환경에서 Container Platform에서 제공된 설정값에 따라 동작한다.
- Build process를 거친다.
- 자동화 테스트를 수행하여 통과하면 특정 태그가 부여된 Container Image가 생성된다.
- 해당 Image를 Compose 파일에 설정된 값으로 빌드 검증 테스트(sanity test) 환경에 배포한다.
- 빌드 검증 테스트에 통과하면 다음 환경(다른 compose 파일 설정값 주입)으로 넘어간다.
- 모든 테스트를 통과하면 Docker Swarm이나 Kubernetes 배포 manifest로 운영 환경에 Image를 배포한다.
6. Practice
개발 환경과 테스트 환경을 하나의 Host PC에서 실행하라
개발 환경을 docker-compose up 명령의 기본값으로 삼아라
- 로컬 파일 데이터베이스 사용
- 8089번 포트 공개
- to-do application v2 버전 실행
테스트 환경은 프로젝트 이름과 특정 컴포즈 파일을 지정해 실행하라
- 별도의 데이터베이스 Container 사용
- 데이터베이스 스토리지를 위한 볼륨 사용
- 8080번 포트 공개
- to-do Application 최신 버전 실행
# dev env
docker-compose up -d
# test env
docker-compose -f .\docker-compose.yml -f .\docker-compose-test.yml -p ch10-lab-test up -d
🟡 docer-compose.yml
services:
todo-web:
image: diamol/ch06-todo-list
secrets:
- source: todo-db-connection
target: /app/config/secrets.json
🟡 docker-compose-dev.yml
services:
todo-web:
image: diamol/ch06-todo-list:v2
ports:
- 8089:80
environment:
- Database:Provider=Sqlite
secrets:
todo-db-connection:
file: empty.json
🟡 .env
# 컨테이너 설정값 - 공개포트
TODO_WEB_PORT=8089
TODO_DB_PORT=5432
# 도커 컴포즈 실행 옵션 - 컴포즈 파일 지정, 프로젝트 이름
COMPOSE_PATH_SEPARATOR=;
COMPOSE_FILE=docker-compose.yml;docker-compose-dev.yml
COMPOSE_PROJECT_NAME=todo
🟡 docker-compose-test.yml
services:
todo-web:
ports:
- "8080:80"
environment:
- Database:Provider=Postgres
networks:
- app-net
todo-db:
image: diamol/postgres:11.5
environment:
- PGDATA=/data
ports:
- "5433:5432"
volumes:
- "todo-database:/data"
networks:
- app-net
networks:
app-net:
name: todo-test
secrets:
todo-db-connection:
file: postgres-connection.json
volumes:
todo-database:
🟡 postgres-connection.json
{
"ConnectionStrings": {
"ToDoDb": "Server=todo-db;Database=todo;User Id=postgres;Password=postgres;"
}
}