docker compose 란
docker compose에 대해 알아보려고 한다. (참조: docs.docker.com/compose/)
docker compose는 여러 개의 docker를 정의하고 실행하는 툴이다. 그래서 kubernetes 처럼 YAML 파일로 여러 개의 docker 내부 속성을 설정하고 한 번에 실행시킨다. 마치 docker를 배치로 한 번에 실행시키는 것과 같다.
(아래 docker compose 실행 순서 참조)
1. Define your app’s environment with a Dockerfile so it can be reproduced anywhere.
2. Define the services that make up your app in docker-compose.yml so they can be run together in an isolated environment.
3. Run docker-compose up and Compose starts and runs your entire app.
그렇다면, docker compose의 내부 YAML은 어떻게 구성되는지 대략 느낌만 보자.
version: "3.9" # optional since v1.27.0
services:
web:
build: .
ports:
- "5000:5000"
volumes:
- .:/code
- logvolume01:/var/log
links:
- redis
redis:
image: redis
volumes:
logvolume01: {}
YAML 구성에 대해서는 이후에 좀 더 자세히 설명하기로 하고, 왜?! docker-compose 쓰는지 장점에 대해 알아보자.
1. Multiple isolated environments on a single host
docker 자체가 isolated environment에서 동작하며 docker-compose가 여러 docker를 한 번에 실행시키는 것을 고려해보면, 당연한 장점 아닌 장점이다.
2. Preserve volume data when containers are created
볼륨을 보존한다. 이 말은 즉, 기존 컨테이너에 의해 관리되는 volume이 있을 때 새로운 컨테이너가 기존 컨테이너가 사용한 volume을 그대로 사용함으로써 데이터를 보존한다. (기존 컨테이너가 사용한 volume을 새로운 컨테이너에게 복사한다.
3. Only recreate containers that have changed
docker-compose는 설정값을 캐싱한다. 때문에 docker-compose를 재시작할 때 설정이 변경되지 않았으면 캐싱된 값을 그대로 사용한다.
4. Variables and moving a composition between environments
파일 내부에 변수를 설정하여 다양한 환경에서 적용할 수 있다.
그럼 공식 문서를 바탕으로 간단하게 실행시켜 보자. (아래 참조)
docs.docker.com/compose/gettingstarted/
version: "3.9"
services:
web:
build: .
ports:
- "5000:5000"
redis:
image: "redis:alpine"
docker-compose.yml 파일을 확인해보면 위와 같다. version은 문서 릴리즈 버전으로 제외하고, service 부분을 보자.
(github.com/compose-spec/compose-spec/blob/master/spec.md#services-top-level-element)
여기서 service는 컴퓨팅 리소스에 대한 정의로 docker-compose에서 동작하는 컨테이너는 service에서 설정해야 한다.
따라서 위의 예제에서 web, redis가 서비스 이름이 된다.
그런데!! service 내의 각 이름에서 docker를 만들기 위해서는 docker image가 필요한데 redis만 정의되어 있다.
이것은 web.build가 있기 때문에 build의 path에서 가리키는 디렉토리 위치의 dockerfile를 사용하기 때문이다. 제공된 예시를 자세히 살펴보면, dockerfile은 아래와 같다.
FROM python:3.7-alpine
WORKDIR /code
ENV FLASK_APP=app.py
ENV FLASK_RUN_HOST=0.0.0.0
RUN apk add --no-cache gcc musl-dev linux-headers
COPY requirements.txt requirements.txt
RUN pip install -r requirements.txt
EXPOSE 5000
COPY . .
CMD ["flask", "run"]
이를 통해 docker-compose up 명령어를 사용해 실행해보면, 빌드가 되면서 실행이 된다.
그리고 아래처럼 상태 확인까지 하면...docker-componse를 통해 redis와 web 컨테이너가 실행된 것을 알 수 있다.
마지막으로 네트워크 부분을 살펴보자.
By default Compose sets up a single network for your app. Each container for a service joins the default network and is both reachable by other containers on that network, and discoverable by them at a hostname identical to the container name.
docker-compose도 역시 docker의 네트워크 특성을 그대로 가진다. 그렇지만 docker-compose는 하나의 묶음으로 여러 docker를 실행시키기 때문에 네트워크도 하나의 묶음이다. 즉, docker-compose에서 구성하는 네트워크를 다 같이 사용한다.
(때문에 docker와 docker의 통신은 포트로만 하면된다.)
한번 예를 들어보면,
version: "3.9"
services:
web:
build: .
ports:
- "8000:8000"
db:
image: postgres
ports:
- "8001:5432"
postgres를 외부에서 접근하기 위해서는 포트 8001번을 사용해야 포트포워딩에 의해 5432의 포트를 사용하게 된다.
반면 docker-compose 내부에서 postgres에 접근하기 위해서는 5432 포트를 통하면 된다. 이는 docker-compose가 같은 네트워크를 사용하기 때문이다.