Docker Compose를 쓰는 이유
지금까지 여러 컨테이너를 빌드하고 실행하는 실습을 진행해보았다.
근데 이 실습을 할 때마다 터미널에 명령어를 일일이 입력하는 게 은근히 번거롭고 시간이 꽤 걸렸다.
또 이런 명령어를 CLI 에 입력하다 보면 오타나 명령어를 빼먹는 등 실수를 하기도 쉽다.
Docker Compose는 이런 문제점을 해결하기 위해 등장했다.
한 걸음 더 나아가서 docker compose는 "Automation of multi-container setups", 즉 다중 컨테이너 설정을 자동화하고 편리하게 관리하기 위해 등장했다고 할 수 있다.
docker compose라는 단 하나의 구성 파일(yaml)만 가지고 여러 컨테이너들의 편성(orchestration)을 진행한다.
비유하자면 터미널에 명령어를 일일이 치기 어려우니까 이걸 셸 스크립트로 만든 다음 써먹는 거라고 할 수 있겠는데
여기에 여러 개의 컨테이너 설정을 한 파일에 담을 수 있다는 면에서 좀 차이가 있다고 할 수 있겠다.
docker compose와 dockerfile 사이 관계
Docker compose는 dockerfile을 대체하지 않는다. dockerfile은 여전히 필요하다. docker compose는 이미지나 컨테이너 관리를 편리하게 하기 위해 존재할 뿐이다.
docker compose는 서로 다른 호스트에서도 작동하나요?
docker compose는 다른 호스트에서 다중 컨테이너를 관리하는 데 쓰는 게 아니다. 같은 호스트에서 써야 한다.
실습: Docker Compose 파일 작성해보기
docker compose 파일 작성 시 유의사항
- docker-compose 파일은 확장자가 yaml이다. yaml에서는 indentation(backspace 2번)으로 종속 관계를 구분한다.
- yaml에선 하위 요소가 key-value pair를 이룰 때 따로 -(hyphon)을 안 써도 되지만(yaml object으로 인식), key-value pair가 아닌 list의 element이면 -를 써줘야 한다. 이게 무슨 소린지 모르겠으면 아래 예시를 보자.
- version이나 services 같은 키워드 맞춤법이 틀리지 않게 주의한다. Docker에서 이미 정해진 키워드를 읽어들여 찾기 때문. VS Code에서 Docker extension을 쓰면(docker compose 파일 작성은 무조건 VS Code + 익스텐션 설치된 환경을 권장) 자동으로 완성해주는 것도 그 이유다.
yaml 파일 예시
여기서 key-value pair는 key=name, value='Martin Developer'와 같이 :으로 구분된 것을 말하고,
list는 Apple", "Orange", "Strawberry", "Mango"와 같이 key-value 쌍으로 묶여있지 않고 단일한 요소로 이루어져 있다.
- key-value pair -> -(hyphen) 필요 없음
- list -> -(hyphen)으로 표시
# An employee record
name: "Martin Developer"
job: "Developer"
skill: "Elite"
employed: True
foods:
- "Apple"
- "Orange"
- "Strawberry"
- "Mango"
languages:
perl: "Elite"
python: "Elite"
pascal: "Lame"
Docker Compose 파일 예시
# docker-compose.yaml
# 파일 버전이 아닌, Docker Compose 의 버전을 명시함
version: '3.8'
# 각 컨테이너를 service로 지칭
services:
mongodb:
image: 'mongo'
volumes:
- data:/data/db
# environment:
# MONGO_INITDB_ROOT_USERNAME: my_user_name
# MONGO_INITDB_ROOT_PASSWORD: my_password
# 보안을 위해 외부 환경변수(env)로 설정
env_file:
- ./env/mongo.env
# 명명 볼륨(named volume)을 여기에 명시한다. service끼리 명명된 볼륨을 공유할 수 있다.
volumes:
data:
logs:
컨테이너(service) 구성
강의에서는 서비스를 컨테이너와 일대일 대응하는 개념으로 사용하고 있다.
물론 여러 개의 컨테이너가 하나의 서비스를 구성하기도 하지만, 여기선 그냥 컨테이너가 하나의 기능(역할)만을 수행한다는 측면에서
하나의 서비스에 대응한다고 생각하자.
- 이미지에는 도커 허브에서 가져온 공식 이미지('mongo'가 예시) 이름이 들어갈 수도 있고, 내가 로컬에 저장해놓은 이미지의 경로를 넣을 수도 있다.
rm 옵션은 따로 명시해줄 필요 없다. docker compose는 컨테이너 중지되면 알아서 제거하기 때문이다. - 강의에선 detached mode도 따로 명시할 필요 없다고 했는데, 사실 docker compose 버전이 바뀌면서 detached 옵션을 붙여줘야 detached 모드로 진입할 수 있다.
환경변수 설정
비밀번호나 이메일 주소같은 개인 정보는 직접 compose 파일에 쓴느 대신 환경 변수 파일로 따로 만들어놓고 compose에 불러오는 게 낫다.
# env > mongo.env
MONGO_INITDB_ROOT_USERNAME=sehyun
MONGO_INITDB_ROOT_PASSWORD=ilovedocker2024!
services:
mongodb:
image: 'mongo'
volumes:
- my_volume_name:/container/internal/path
env_file:
- ./env/mongo.env
frontend:
네트워크
따로 설정해줄 필요 없다. 어차피 같은 docker compose 파일 안에 적힌 서비스(컨테이너)들은 모두 같은 네트워크에 묶이도록 docker compose가 자동으로 설정해주기 때문이다.
물론 네트워크 따로 compose 파일에 명시했다고 해서 문제는 생기지 않는다. 네트워크 이름을 직접 설정해줄 수도 있다.
볼륨 설정
명명 볼륨(named volumes)을 쓸 거면 services와 같은 수준(indentation의 수준)에다 적어야 한다.
그리고, 항상 : 을 볼륨 이름 뒤에 붙여야 한다.
services:
mongodb:
image: 'mongo'
volumes:
- my_volume_name:/container/internal/path
env_file:
- ./env/mongo.env
volumes:
# 볼륨의 이름 뒤에 콜론(:)을 붙여야 한다.
data:
docker compose up, down 명령어로 compose 파일 실행 또는 중지하기
다음 명령을 통해 docker-compose.yaml 파일에 명시된 service를 실행할 수 있다.
보다 구체적으로 말하자면 서비스에 해당하는 이미지(기존 이미지가 없다면)를 pull하고 build한 다음, 컨테이너를 실행한다.
-d 옵션은 detached mode를 의미한다.
docker compose up service1 service2 service 3
docker compose down -v
compose down은 컨테이너를 중지한 후, 알아서 삭제한다.(--rm 옵션 붙일 필요 X)
컨테이너끼리 공유하던 네트워크도 알아서 삭제한다. 하지만 볼륨을 삭제하지는 않는다.
그래서 볼륨을 삭제하는 옵션이 따로 있는데, -v 옵션을 붙이면 service를 down 명령어로 실행 중지할 때 볼륨도 함께 삭제한다.
docker-compose 대신 docker compose?
version 1에서 version 2로 넘어오면서 docker-compose 대신 docker compose, 즉 -(hyphen)을 공백(space)로 바꿔서 명령어 쓰면 된다.
참고 글: https://docs.docker.com/compose/migrate/
With Docker Desktop, Compose V2 is always accessible as docker compose. Additionally, the Use Compose V2 setting is turned on by default, which provides an alias from docker-compose.
Update scripts to use Compose V2 by replacing the hyphen (-) with a space, using docker compose instead of docker-compose.
Docker Compose 파일, DB + 백엔드까지 작성한 예시
# docker-compose.yaml
# Docker Compose의 버전
version: '3.8'
services:
mongodb:
image: 'mongo'
volumes:
- data:/data/db
env_file:
- ./env/mongo.env
backend:
# build: ./backend
build:
context: ./backend
dockerfile: Dockerfile
args:
some-arg: 1
ports:
- '80:80'
volumes:
- logs:/app/logs
# 바인드 마운트
- ./backend:/app
# 익명 볼륨
- /app/node_modules
env_file:
- ./env/backend.env
depends_on:
# 의존하고 있는 다른 컨테이너 (서버의 경우 DB 에 의존)를 명시해서 컨테이너 생성 순서 보장
- mongodb
volumes:
data:
logs:
build 키워드로 dockerfile 경로를 명시하기
- build만 쓴다면 Dockerfile(파일 이름도 그대로 'Dockerfile'이어야 함)이 존재하는 경로를 써준다. 이때, docker compose 파일에 대한 상대 경로로 써준다.
- 또는 git 리포 URL를 써줄 수도 있다. 다음은 도커 공식 docs에서 가져온 예시다.
services:
webapp:
build: https://github.com/mycompany/example.git#branch_or_tag:subdirectory
context
context는 Dockerfile을 포함하는 경로나 폴더를 정의한다. 또는 git repository URL을 넣을 수도 있다.
Dockerfile 이름을 dockerfile이라고 그대로 지었다면 그냥 build 명령어에 Dockerfile 있는 경로를 적어주면 된다.
context에는 절대 경로나 상대 경로 둘 다 들어갈 수 있다.
context 키워드를 명시 안하면 그냥 현재 작업 디렉토리(project directory, `.` )로 알아서 읽어들인다.
dockerfile
같은 폴더 안에 Dockerfile이 여러 개 있을 수도 있지 않은가?
이때, 만약에 메인으로 쓰고자 하는 dockerfile 이름이 'Dockerfile'이고
이 Dockerfile이 에러 날 경우를 대비해서 따로 만들어놓은 dockerfile 이름이 'tmp.Dockerfile'라면
services:
...
backend:
# build: ./backend
build:
context: ./backend
dockerfile: tmp.Dockerfile
이런 식으로 대체용(alternate) Dockerfile 이름을 적어놓을 수 있다. 파일 이름을 정확하게 적어주어야 한다.
volumes, env_file, dependency
bind mount를 이제 상대 경로로 줄여서 지정할 수 있다.
그리고 명명 볼륨은 서비스 항목 아래에도 추가해야 하지만, 밑에도 추가해줘야 한다.
익명 볼륨은 그냥 항목 아래에만 추가하면 된다.
그리고 mongoDB 컨테이너 다음에 백엔드 컨테이너가 만들어져야 하는 의존성을 compose에 명시하기 위해
depends_on 키워드를 달아준다.
docker compose 이후 네트워크 설정
여전히 서비스 이름(컨테이너 이름)은 그대로 기억한다.
코드 안에 HTTP 요청 보낼 때 docker-complete이라는 prefix를 이름 앞에 붙일 필요가 없다는 뜻이다.
stdin_open, tty
말 그대로다. 표준 입력(터미널에 입력받는 것)을 컨테이너에 열어둔다는 뜻이다.
그래서 docker compose에서
stdin_open: true
로 설정해두면 커맨드 창에서 docker run -i MY_CONTAINER 명령어 실행한 것과 똑같다.
여기에 하나만 더 추가해서
stdin_open: true
tty: true
로 설정하면 docker run -it MY_CONTAINER 명령어를 실행한 것과 똑같아진다.
Docker Compose 파일 + 백엔드 + 프론트엔드 파트까지
# docker-compose.yaml
version: '3.8'
services:
mongodb:
image: 'mongo'
volumes:
- data:/data/db
env_file:
- ./env/mongo.env
backend:
# build: ./backend
build:
context: ./backend
dockerfile: Dockerfile
args:
some-arg: 1
ports:
- '80:80'
volumes:
- logs:/app/logs
# 바인드 마운트
- ./backend:/app
# 익명 볼륨
- /app/node_modules
env_file:
- ./env/backend.env
depends_on:
- mongodb
frontend:
build:
context: ./frontend
dockerfile: Dockerfile
ports:
- '3000:3000'
volumes:
- ./frontend/src:/app/src
depends_on:
- backend
# 아래 두 옵션은 React 16 버전에서 인터렉티브 모드로 실행하지 않으면 앱이 종료되는 문제를 해결하기 위한 옵션이다.
# docker run -it 옵션과 같은 역할을 한다.
stdin_open: true
tty: true
volumes:
data:
logs:
docker compose는 컨테이너 이름을 자동으로 짓는다
{현재 작업 디렉토리}_{service 이름}_{숫자}
만약 현재 작업 디렉토리가 docker-complet이고 서비스 이름을 frontend라고 지었다면,
docker-complete_frontend_1, docker-complete_backend_1, 이런 식으로 컨테이너 이름이 자동으로 생성된다.
그런데 이 이름이 마음에 안 들면 직접 compose 파일에 지정하는 법도 있다.
services:
...
backend:
build:
...
frontend:
build: ./frontend
ports:
- '3000:3000'
volumes:
- ./frontend/src:/app/src
stdin_open: true
depends_on:
- backend
container_name: frontend
'도커와 쿠버네티스' 카테고리의 다른 글
[6주차] Laravel & PHP 프로젝트 도커화하기 (0) | 2024.01.31 |
---|---|
[5주차] Part 2: 유틸리티 컨테이너 다루기 (0) | 2024.01.22 |
[3주차] Part 2: 볼륨의 읽기 제한, 볼륨의 관리, docker ignore (1) | 2024.01.09 |
[3주차] Part 1: 데이터와 볼륨(volume) 이해하기 (0) | 2024.01.09 |
[2주차] Part 4: 도커 이미지 공유하기(push, pull) (0) | 2024.01.09 |