[DEV] 11주차. Docker & K8s (4)
1. Docker-compose
- 다수의 Container로 소프트웨어가 구성되는 경우 사용할 수 있는 툴 + 환경 설정 파일
- 여기서 다양한 테스트 등도 수행 가능
- 다양한 버전을 만드는 것도 일반적 (dev, test, prod 등)
- 개별 컨테이너를 따로 관리하는 것보다 훨씬 생산성이 높음
-
환경 설정 파일: docker-compose.yml / docker-compose.yaml
- 복잡도가 올라가지만 이점이 더 큼
- 명령어 -> 소프트웨어를 구성하는 모든 컨테이너에 적용됨
build
,up
(run),pull
,ps
,down
(컨테이너 삭제까지),start
,stop
,rm
등
- Docker Desktop의 일부로 설치가 됨
- Docker Engine을 실행하고 먼저 터미널에서 버전 확인
- Docker Compose v.1.27부터 v2와 v3가 합쳐짐
docker-compose --version
# Docker Compose version 2.23.3
docker-compose.yml
services
: 프로그램을 구성하는 서비스들을 지정- 각 서비스는 별개의 Docker Image 지정과 Docker Container 실행으로 구성됨
- 즉, 각 서비스는 자신의 Dockerfile을 갖고 있거나, docker hub 등에서 이미지를 다운로드 받아야 함
- 서비스 별로 포트번호, 환경변수, 디스크 볼륨 등을 지정해야 함
- 서비스 이름은 아무거나 가능
volumes
: 앞서(services
에서) 사용된 docker volume들을 지정networks
: 앞서 사용된 network들을 지정- 모든 서비스에 하나의 네트워크를 사용할 것이라면 따로 지정하지 않아도 됨
volumes
,networks
에서 지정한 볼륨, 네트워크 이름을services
안에서 사용한다는 것이 더 정확!
기타
1) docker-compose vs. docker compose
- v1: docker-compose
- v2: docker compose
- docker compose를 쓰는 것이 더 좋음, 하지만 아직 대부분의 문서가 docker-compose 중심으로 만들어져 있음
2) docker-compose.yml vs. docker-compose.yaml
- docker compose 명령 -> 위 둘 중 하나 파일을 찾음
- 둘 다 존재하면 에러 발생
- 만약 다른 이름의 파일을 사용하고 싶다면
-f
옵션 사용docker-compose -f docker-compose.dev.yml up
docker-compose.yml 예시
services:
frontend:
build: ./frontend
ports:
- 3000:3000
backend:
build: ./backend
ports:
- 3001:3001
environment:
DB_URL: mongodb://database/vidly
database:
image: mongo:4.0-xenial
ports:
- 27017:27017
volumes:
- vidly: /data/db
volumes:
vidly:
- service 이름: 각각의 호스트 이름
- 서로 간의 연결을 보장해줌
- 컨테이너를 각각 실행시킬 때와 달리 네트워크를 따로 설정하지 않아도 이름을 가지고 바로 연결이 가능함
mongodb://database/vidly
- database
- 데이터베이스 서비스
- mongodb host를 가리킴
- 데이터베이스 이름을 vidly라고 설정
- database
이미지 생성과 관리
docker-compose build
- build 키로 지정된 것들 대상
docker-compose pull
- docker hub에서 이미지들을 읽어오려고 함
docker images
- 로컬 시스템에 있는 모든 이미지 리스트
- 각 개별 이미지 앞에 폴더 이름을 prefix로 붙임 (docker hub에서 읽어온 것 제외)
docker-compose images
- docker-compose로 실행된 컨테이너에 의해 실행되고 있는 이미지들만 보여줌
docker-compose push
- docker hub로 이미지들을 푸시하려고 함
- 권한이 있는 것들만 푸시됨 (official image 등은 X)
소프트웨어 시작과 중단
docker-compose up
- build -> create -> start
docker-compose create
docker-compose start
docker-compose down
- stop -> rm
docker-compose stop
docker-compose rm
docker-compose ls
- docker-compose를 그룹별로 보여줌
docker-compose ps
- docker-compose로 실행중인 컨테이너들을 개별로 보여줌
네트워킹
- docker끼리 네트워크 연결이 필요한 경우
- services에 준 이름으로 호스트 이름이 생성됨
- 내부에 DNS 서버가 하나 생성되어 이름을 내부 IP로 변환해줌
- 별도로 네트워크를 구성하고 싶담면
networks
에 네트워크를 나열하고, 네트워크를 적절하게 서비스에 지정해주어야 함
docker network ls
2. voting application을 docker-compose로 실행해보기
앞서 일일히 실행했을 때의 문제점
- Postgres를 실행하는 부분이 제대로 동작하지 않았음
docker run -d --name=db -e POSTGRES_PASSworD=password --network mynetwork postgres
- worker/Program.cs
- worker가 redis에 기록된 투표 결과를 postgres에 저장하는 부분
var pgsql = OpenDbConnection("Server=db;Username=postgres;Password=postgres;");
var redisConn = OpenRedisConnection("redis");
- 이를 해결하려면 Container를 실행할 때 아래 2개의 환경 변수를 넘겨주어야 함
- POSTGRES_USER: “postgres”
- POSTGRES_PASSWORD: “postgres”
- 이것을 docker-compose 환경 설정 파일을 통해 넘기면서 해결!
docker-compse.yml
services:
redis:
image: redis:alpine
db:
image: postgres:15-alpine
environments:
POSTGRES_USER: "postgres"
POSTGRES_PASSWORD: "postgres"
vote:
build: ./vote
# use python rather than gunicorn for local dev
# cmd override
command: python app.py
ports:
- 5001:80
result:
build: ./result
# entrypoint override
entrypoint: nodemon server.js
ports:
- 5002:80
worker:
- build: ./worker
실행
git clone https://github.com/learndataeng/example-voting-app.git
cd example-voting-app
# 청소
docker container rm -f $(docker container ls -aq)
docker image rm -f $(docker image ls -q)
# 확인
docker ps -a
docker images
docker compose -f docker-compose.mac.yml build
docker compose -f docker-compose.mac.yml pull # official images
docker compose -f docker-compose.mac.yml up
- redis, postgres가 제대로 동작해야 함 (dependecies)
- postgres -> volume을 걸어야 지속적으로 운영 가능
- backend, frontend에 network를 각각 걸어주는 것이 좋음 -> 보안 강화
postgres 접속
- 위 docker-compose가 실행중인 상태에서
docker exec -it --user=postgres example-voting-app-db-1 sh
- postgres 쉘 접속
psql #postgres shell 실행
\c
# You are now connected to database "postgres" as user "postgres".
# postgres db에 postgres user로 연결
\dt
# postgres db 밑의 테이블 보여줌
3. docker-compose.yml 개선
networks 정의
networks:
back-tier:
# redis, worker, db, vote, result
front-tier:
# vote, result
# frontend의 애플리케이션은 backend에서도 접근할 수 있어야 함
- services들에 네트워크 지정
services:
redis:
image: redis:alpine
networks:
- back-tier
db:
image: postgres:15-alpine
environments:
POSTGRES_USER: "postgres"
POSTGRES_PASSWORD: "postgres"
networks:
- back-tier
vote:
build: ./vote
# use python rather than gunicorn for local dev
# cmd override
command: python app.py
ports:
- 5001:80
networks:
- back-tier
- front-tier
result:
build: ./result
# entrypoint override
entrypoint: nodemon server.js
ports:
- 5002:80
networks:
- back-tier
- front-tier
worker:
build: ./worker
networks:
- back-tier
networks:
back-tier:
front-tier:
volumes 정의
- PostgreSQL 서비스의 데이터가 계속해서 저장되어야 함 (persistent)
volumes:
db-data:
services:
redis:
image: redis:alpine
networks:
- back-tier
db:
image: postgres:15-alpine
environments:
POSTGRES_USER: "postgres"
POSTGRES_PASSWORD: "postgres"
networks:
- back-tier
volumes:
- db-data:/var/lib/postgresql/data
vote:
build: ./vote
# use python rather than gunicorn for local dev
# cmd override
command: python app.py
ports:
- 5001:80
networks:
- back-tier
- front-tier
result:
build: ./result
# entrypoint override
entrypoint: nodemon server.js
ports:
- 5002:80
networks:
- back-tier
- front-tier
worker:
build: ./worker
networks:
- back-tier
networks:
back-tier:
front-tier:
volumes:
db-data:
기타
command
: 이미지 Dockerfile의 CMD를 덮어쓰는데 사용entrypoint
: 이미지 Dockerfile의 ENTRYPOINT를 덮어쓰는데 사용
healthcheck
- Dockerfile에서 기술가능한 기능
- docker-compose에서 덮어쓰기 가능
- 0이 성공, 1 (0이 아닌 값)이 실패
depends_on
- 해당 서비스가 실행되기 위해서 먼저 실행되어야 하는 서비스들을 기술
- 하나 이상 기술 가능
- 옵션으로 condition을 추가할 수 있음
condition: service_healty
: 서비스에 지정된 healthcheck이 건강한가condition: service_started
: 시작condition: service_completed_successfully
: dependency가 있는 모듈이 성공적으로 성공적으로 끝난 다음에 실행
최종 개선
vote
- 기존
vote:
build: ./vote
ports:
- 5001:80
networks:
- back-tier
- front-tier
- 개선
vote:
build: ./vote
# cmd override
command: python app.py
# 서비스들 간의 의존성이 있을 경우 먼저 실행되어야 하는 서비스들을 여기 기술해야 함
depends_on:
redis:
condition: service_healthy # option
# 해당 서비스의 건강을 나타내주는 체크를 기술 (override)
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost"]
interval: 15s
timeout: 5s
retries: 3
start_period: 10s
volumes:
- ./vote:/app
ports:
- 5001:80
networks:
- back-tier
- front-tier
db
- 기존
db:
image: postgres:15-alpine
networks:
- back-tier
- 개선
db:
image: postgres:15-alpine
envirionment:
POSTGRES_USER: "postgres"
POSTGRES_PASSWORD: "postgres"
# host volume과 named volume을 사용하고 있음
volumes:
- "db-data:/var/lib/postgresql/data"
- "./healthchecks:/healthchecks"
healthcheck:
test: /healthchecks/postgres.sh
interval: "5s"
networks:
- back-tier
volumes:
db-data:
redis
- 기존
redis:
image: redis:alpine
networks:
- back-tier
- 개선
redis:
image: redis:alpine
volumes:
- "./healthchecks:/healthchecks"
healthcheck:
test: /healthchecks/redis.sh
interval: "5s"
networks:
- back-tier
worker
- 기존
worker:
build: ./worker
networks:
- back-tier
- 개선
worker:
build:
context: ./worker
depends_on:
redis:
condition: service_healthy
db:
condition: service_healthy
networks:
- back-tier
- 위 빌드 방식을 쓰는 경우 더 많은 빌드 관련 정보를 넘겨줄 수 있음
build:
context: ./myapp
dockerfile: Dockerfile.dev
args:
ENVIRONMENT: development
result
- 기존
result:
build: ./result
ports:
- 5002:80
networks:
- front-tier
- back-tier
- 개선
result:
build: ./result
entrypoint: nodemon server.js
depends_on:
db:
condition: service_healthy
volumes:
- ./result:/app
ports:
- 5002:80
- 5858:5858
networks:
- front-tier
- back-tier
4. Airflow Docker-compose
docker-compose.yaml
- version
- x-airflow-common
airflow-common
이라는 별칭 정의- 여러 서비스에서 공유하는 공통 구성을 정의한 것
- 보통 yaml에서 anchor라고 부르며, YML 파일 블록을 나중에 계승이라는 형태로 재사용 가능하게 해줌
- version, services, volumes, networks를 제외한 최상위 레벨 키워드는 모두 anchor
- services
- postgres
- redis
- airflow-webserver
- airflow-scheduler
- airflow-worker
- airflow-triggerer
- airflow-init
- volumes
- postgres-db-volume
x-airflow-common
x-airflow-common:
&airflow-common
image: ${AIRFLOW_IMAGE_NAME:-apache/airflow:2.5.1}
environment:
&airflow-common-env
AIRFLOW__CORE__EXECUTOR: CeleryExecutor
AIRFLOW__DATABASE__SQL_ALCHEMY_CONN: postgresql+psycopg2://airflow:airflow@postgres/airflow
# For backward compatibility, with Airflow <2.3
AIRFLOW__CORE__SQL_ALCHEMY_CONN: postgresql+psycopg2://airflow:airflow@postgres/airflow
AIRFLOW__CELERY__RESULT_BACKEND: db+postgresql://airflow:airflow@postgres/airflow
AIRFLOW__CELERY__BROKER_URL: redis://:@redis:6379/0
AIRFLOW__CORE__FERNET_KEY: ''
AIRFLOW__CORE__DAGS_ARE_PAUSED_AT_CREATION: 'true'
AIRFLOW__CORE__LOAD_EXAMPLES: 'true'
AIRFLOW__API__AUTH_BACKENDS: 'airflow.api.auth.backend.basic_auth,airflow.api.auth.backend.session'
_PIP_ADDITIONAL_REQUIREMENTS: ${_PIP_ADDITIONAL_REQUIREMENTS:- yfinance}
volumes:
- ${AIRFLOW_PROJ_DIR:-.}/dags:/opt/airflow/dags
- ${AIRFLOW_PROJ_DIR:-.}/logs:/opt/airflow/logs
- ${AIRFLOW_PROJ_DIR:-.}/plugins:/opt/airflow/plugins
user: "${AIRFLOW_UID:-50000}:0"
depends_on:
&airflow-common-depends-on
redis:
condition: service_healthy
postgres:
condition: service_healthy
&airflow-common
- 별칭
&airflow-common
아래에 있는 모든 내용을 이제 뒤에서부터airflow-common
이라고 부르겠다는 뜻
image:
- 같은 airflow 이미지가 모든 서비스 기본 이미지로 사용됨
volumes:
- 모두 세 개의 volume을 공유함 (host volumes)
depends_on:
- 모두 redis와 postgres가 정상동작할 때까지 대기
airflow-scheduler 서비스
airflow-scheduler:
<<: *airflow-common
command: scheduler
healthcheck:
test: ["CMD-SHELL", 'airflow jobs check --job-type SchedulerJob --hostname "$${HOSTNAME}"']
interval: 10s
timeout: 10s
retries: 5
restart: always
depends_on:
<<: *airflow-common-depends-on
airflow-init:
condition: service_completed_successfully
<<: *
으로 anchor 사용