치춘짱베리굿나이스
[Rank 5] inception - Docker-compose 본문
Docker-compose
도커 컴포즈란
도커파일을 컴포즈해주는 툴이다 (…) 설명이 너무 대충인 것 같지만 진짜다
도커파일 하나로는 딱 하나의 이미지 (= 컨테이너) 를 만들 수 있었다
근데 서비스 구축이라는 것이 그렇게 호락호락한 녀석이 아니라서 하나의 이미지로는 아무것도 할 수 없다
당장 서버 배포할 때도 데이터베이스 서버도 켜고, nginx도 켜고… 프론트엔드 코드도 올리고… 그러지 않는가
서비스는 여러 컨테이너 (여러 툴) 들의 조합이고, 이 컨테이너들을 한 데 모아 서비스로 엮어주는 것이 도커 컴포즈이다
compose가 구성하다, 짓다, … 등등의 뜻을 가지고 있는 것을 생각하면 적절한 네이밍이다
바이올린, 비올라, 플룻, 클라리넷, 오보에… 등의 악기들을 조합하여 음색을 자아내는 느낌이라고 생각하자
사용하는 이유
쉘 스크립트를 작성해서 실행해도 되지 않나? 싶지만 도커 컴포즈를 사용하면
- 문법이 간결하다
- 서비스 이름을 정의하여 해당 서비스들을 손쉽게 네트워크 설정을 진행할 수 있고, 네트워크 옵션이 다양하다
- 만약에 이걸 도커 명령어로 일일히 설정해준다고 하면 더 어려울 것
- 도커 이미지마다 하나하나 따로따로 설정할 필요 없이, 컴포즈 파일 하나로 한방에 모든 컨테이너를 설정할 수 있어서 매우 편리하다
도커 컴포즈 관련 명령어
up
$> docker-compose up [옵션]
도커 컴포즈 서비스 실행 명령어이다
현재 폴더에서 docker-compose.yml
, docker-compose.yaml
, compose.yml
, compose.yaml
파일을 찾아, 이에 정의된 대로 이미지를 빌드하고, 컨테이너를 올리고 서비스를 실행한다
- 만약 서비스에서 사용하는 컨테이너가 이미 실행중이라면, 관련 옵션을 지정하지 않는 이상 굳이 재실행하지 않는다
- 만약 서비스에서 사용하는 컨테이너가 이미 실행중인데, 해당 컨테이너의 이미지나 설정 파일이 수정되었을 경우 볼륨만 유지시킨 채 컨테이너를 재생성 및 재실행한다 (관련 옵션을 지정하여 재생성을 막을 수 있다)
옵션으로는
-d
,--detach
: detached mode, 백그라운드에서 컨테이너를 생성하고 실행시킨다- 이 옵션을 지정하지 않으면,
docker-compose up
을 실행시킨 쉘은 서비스가 종료되기 전까지 다른 작업을 할 수 없다 (ctrl+C
로 탈출하면 컨테이너도 전부 정지된다)
- 이 옵션을 지정하지 않으면,
--force-recreate
: 모든 컨테이너를 재생성하고 다시 실행시킨다--no-recreate
: 컨테이너의 이미지나 설정 파일이 수정되었더라도, 컨테이너가 이미 실행중이라면 굳이 재생성하지 않는다--build
: 컨테이너들을 실행하기 전에 이미지를 빌드한다--no-build
: 이미지를 빌드하지 않는다--no-start
: 컨테이너를 이용하여 서비스를 생성하였더라도 실행시키진 않는다--no-color
: 서비스 실행 로그에 색상을 다 뺀다--quiet-pull
: 서비스 실행 로그를 출력하지 않는다
down
$> docker-compose down [옵션]
현재 폴더의 컴포즈 파일에 대하여 실행중인 도커 컴포즈 서비스를 중단하고, 빌드된 컨테이너와 네트워크, 볼륨, 이미지를 모두 정지 및 삭제한다
아무 옵션도 지정하지 않았을 때, 삭제되는 요소들로는
- 컴포즈 파일 (
yml
/yaml
) 에 정의된, 서비스를 구동시키는 데에 필요한 컨테이너 - 컴포즈 파일의
networks
섹션에 정의된 네트워크 - 또는 기본 네트워크
가 있다
또한 external
로 선언된 네트워크와 볼륨은 삭제되지 않는다
옵션으로는
--rmi [type]
: 빌드된 이미지도 삭제한다type
은all
(모든 이미지) 또는local
(커스텀 태그가 없는 이미지) 로 지정가능하다
-v, --volumes
: 컴포즈 파일의volumes
섹션에 정의된 이름 있는 볼륨들과, 컨테이너에 부착된 익명 볼륨들을 삭제한다--remove-orphans
: 컴포즈 파일에 정의되어 있지 않지만, 서비스에서 사용하는 모든 컨테이너들을 함께 삭제한다-t
: 셧다임 타임아웃 시간을 지정한다 (초 단위)
ps
$> docker-compose ps [옵션]
현재 폴더의 컴포즈 파일에 정의된 모든 컨테이너 목록을 출력한다
옵션으로는
-q, --quiet
: 컨테이너 정보들은 출력하지 않고 컨테이너 ID만 출력--services
: 서비스 출력-a, --all
: 멈춘 컨테이너까지 출력
start
$> docker-compose start [서비스명]
서비스를 실행시키는 데에 사용된다
docker-compose up
은 컨테이너 빌드 및 실행을 시키는 것이고 이 명령어는 실행만 한다
stop
$> docker-compose stop [서비스명]
서비스를 정지하는 데에 사용된다
exec
$> docker-compose exec [옵션] [명령어]
현재 실행 중인 서비스에 명령어를 입력하고 싶을 때 사용한다
옵션으로는
-d, --detach
:docker-compose up
과 마찬가지로 백그라운드에서 execute한다-u, --user [유저명]
: 명시한 유저로 명령을 실행한다-e, --env [키]=[값]
: 환경변수를 지정하여 명령어를 실행시킨다-w, --workdir [경로]
:workdir
디렉토리를 지정한다
run
$> docker-compose run [옵션] [명령어]
서비스를 새로 실행하고, 입력한 명령어를 한 번 실행한 뒤 종료한다
exec
와 다르게 새로운 컨테이너를 실행한 뒤 명령을 바로 수행한다
옵션으로는
-d, --detach
:docker-compose up
과 마찬가지로 백그라운드에서 execute한다--name [이름]
: 서비스 컨테이너에 이름을 붙인다-u, --user=[유저명]
: 명시한 유저로 명령을 실행한다-e, [키]=[값]
: 환경변수를 지정하여 명령어를 실행시킨다-w, --workdir=[경로]
:workdir
디렉토리를 지정한다--rm
: 명령 수행이 완료되면 컨테이너를 삭제한다
logs
$> docker-compose logs [옵션]
서비스 컨테이너의 출력값 (로그들) 을 보여준다
옵션으로는
--no-color
: 알록달록한 색상 출력을 전부 빼고 흰색으로 만든다-t, --timestamps
: 타임스탬프도 출력해 준다
그 외 명령
$> docker-compose kill [옵션] [서비스명]
서비스 컨테이너를 강제 종료한다
$> docker-compose rm [옵션] [서비스명]
중지된 서비스 컨테이너를 삭제한다
기본 옵션으로 익명 볼륨들은 삭제되지 않는다고 한다
볼륨에 들어있지 않는 모든 값들은 삭제된다
$> docker-compose config [옵션]
도커 컴포즈 파일로 서비스가 어떻게 설정되는지 보여준다
$> docker-compose top [서비스명]
해당 서비스에서 돌아가고 있는 프로세스들을 리스트로 출력한다
도커 컴포즈 파일 작성하기
파일명
docker-compose.yml
docker-compose.yaml
compose.yml
compose.yaml
docker-compose
명령어는 위의 네 파일을 인식한다
가장 많이 쓰이는 것은 위의 2개가 아닌가 싶다
inner-circles:
...
4th-circle:
cub3d: 100
cpp-module:
...
cpp-module-08: 100
5th-circle:
inception: 100
ft_irc: 100
6th-circle:
ft_transcendence: 142
outer-circles:
...
yaml (yml) 은 YAML Ain’t Markup Language라는 뜻이라는데 GNU도 그렇고 왤케 개발자들은 재귀 줄임말을 좋아하는지 모르것다…
들여쓰기와 :
을 통해 항목과 키-값을 구분하는 매우 간결한 형태의 파일 형식이다
쿠버네티스나 도커, 깃허브 액션 등 다양한 서비스에서 설정 파일을 정의할 때 yaml 형식을 많이 쓴다
도커 컴포즈 형식
# 주석
version: [도커 컴포즈 버전]
services:
[서비스명1]:
build:
image:
...
[서비스명2]:
build:
...
volumes:
[볼륨명]:
# 설정값
networks:
[네트워크명]:
# 설정값
각 항목의 내부 항목 값은 들여쓰기로 구분한다
version
에서는 도커 컴포즈 파일의 버전을 명시한다- 이 링크에서 파일 버전과 도커 엔진 버전간 호환성을 체크할 수 있다
- 가장 최신 버전은 도커 컴포즈 파일 3.8 ↔ 도커 엔진 19.03.0+ 인 듯하다?
- 호환성을 잘 체크해서 버전을 명시하자
services
안에는 하위 서비스 컨테이너 이름 - 내부 옵션을 작성한다volumes
안에는 여러 서비스 컨테이너가 공유하는 볼륨 이름과 설정을 지정할 수 있다networks
안에는 이 서비스 내부 네트워크를 설정할 수 있다
서비스 컨테이너 설정
...
services:
[서비스명]:
build: [도커파일의 경로]
ports: [포트]
...
...
nginx:
...
mysql:
...
...
build
- 해당 서비스 컨테이너의 이미지를 빌드하기 위한
Dockerfile
의 위치이다 - 경로를 명시하면 그 안에서
Dockerfile
을 찾는다 - 하위 옵션으로
context
: 도커파일의 위치, 상대경로 또는 절대경로dockerfile
: 도커파일 이름, 따로 설정해주지 않는다면context
경로 (또는build
에서 명시한 경로) 에서dockerfile
이라는 이름의 파일을 찾는다args
: 인수 지정
- 해당 서비스 컨테이너의 이미지를 빌드하기 위한
image
- (로컬 또는 리모트 서버 = 도커허브에 존재하는) 베이스 이미지 태그 또는 ID이다
- 로컬에 파일이 존재하지 않을 경우 리모트 경로에서 해당 이미지를
Pull
한다 - 직접 도커파일을 작성해서 빌드한 이미지를 사용하고 싶으면
build
, 이미 존재하는 이미지를 사용하고 싶으면image
를 쓰면 된다
command
/entrypoint
- 도커파일에 정의된
entrypoint
를 덮어씌울 수 있는 옵션 - 도커파일 정리 때 적었듯
entrypoint
는 이미지를 빌드하여 컨테이너로서 처음 띄워줄 때 가장 처음으로 수행하는 명령이다 - 사전 지정된 값 대신 빌드 시에 필요한 명령으로 재지정할 수 있는 것
- 도커파일에 정의된
ports
- 포트 포워딩을 통해 컨테이너 내부 네트워크끼리가 아닌, 호스트 OS와 연결하기 위해 사용된다
ports: “8000:8000”
와 같이“외부 포트 (호스트OS 포트) : 내부 포트 (컨테이너 네트워크 포트)"
방식으로 정의한다 (따옴표 필수)- 외부에서 외부 포트 (호스트OS 포트) 로 들어온 데이터는 여기서 지정된 내부 포트로 전달되므로, 특정 서비스가 이 데이터를 받아올 수 있다
- 하위 옵션으로
target
: 컨테이너 네트워크 내부 포트published
: 컨테이너 네트워크 외부에서, 호스트 OS와 연결되는 포트protocol
: 사용할 프로토콜
- 하위 옵션을 사용하면,
ports: “[published]:[target]”
과 동일한 효과를 보인다
expose
- 호스트OS와 연결되지 않고, 내부 네트워크에서 컨테이너간 통신을 위한 포트를 열어주는 옵션이다
ports
는 바깥 네트워크 (호스트 OS) 와 연결하는 포트 설정,expose
는 컨테이너 내부 네트워크에서 컨테이너끼리 연결하는 포트 설정이라는 차이점이 있다expose
는 포트를 열고자 하는 컨테이너에expose: “8080”
과 같이 지정하면 된다- 예를 들어,
nginx
가wordpress
에 연결하고자 한다면,wordpress
컨테이너 측의 포트를expose
하면 된다
- 예를 들어,
dockerfile
에expose
설정이 되어 있다면docker-compose
에는 해줄 필요 없다
depends_on
- 각 컨테이너 서비스 간 의존 관계를 설정하기 위해 사용된다
- 에를 들어,
mariadb
컨테이너가 올라간 뒤에wordpress
컨테이너가 올라가야 한다면wordpress
컨테이너 측에depends_on
옵션을mariadb
로 지정한다 - 완벽하게 컨테이너가 켜졌는지 보장은 어렵기 때문에, 종속적인 컨테이너 측에서
ENTRYPOINT
또는CMD
로 지정한 쉘 스크립트에서 핑을 날려 컨테이너의 켜짐 여부를 체크해야 한다
volumes
- 컨테이너에 볼륨을 마운트한다
호스트경로:컨테이너경로
방식으로 마운트되며, 맨 뒤에 :ro 등을 붙여 접근 모드 (읽기 전용, 쓰기 전용 등) 를 지정할 수 있다- 또는 호스트 경로 측을 컴포즈 파일 하단에 설정한 볼륨 이름으로 지정할 수 있다
environment
- 컨테이너에서 사용할 환경변수를 지정할 수 있다
키: 값
쌍으로 환경변수를 지정하면 된다
restart
- 이 컨테이너가 죽었을 때 재시작 여부
그 외에도
links
- 다른 서비스 컨테이너와 연결고리를 만들고 싶을 때 사용한다
- 딱히 권장하는 명령은 아니고 왠만하면 공유 볼륨 공간을 사용하라고 한다
external_links
docker-compse.yml
밖에 존재하는 컨테이너와 연결고리를 만들고 싶을 때 사용한다
volumes_from
- 다른 서비스나 컨테이너로부터 볼륨을 마운트하는 옵션이다
extends
- 다른 파일로부터 서비스를 확장하고자 할 때 사용하는 옵션이다
extra_hosts
- 호스트명 매핑을 추가하는 옵션이다
- 도커에서
--add-host
옵션을 사용할 때와 동일하다
env_file
- 환경변수를 별도의 파일 (파일명.env 등) 에 저장한 뒤 읽어들여 사용하고 싶을 경우 이 옵션을 사용한다
- 그 외에도
log_driver
,pid
,dns
,cap_add
,cap_drop
,dns_search
,devices
,security_opt
,working_dir
,entrypoint
,user
,hostname
,domainname
,mem_limit
,privileged
,stdin_open
,tty
,cpu_shares
,cpuset
,read_only
등의 옵션이 있다고 한다 - 너무 많아서 다 정리하진 않을 것…
볼륨 설정
volumes:
[볼륨명]:
external: [외부 볼륨 여부]
external
- 외부에 존재하는 볼륨 (
docker volume create
로 생성한 볼륨) 을 끌어다 쓸 수 있다 - 해당 이름을 가진 볼륨이 존재하지 않을 경우 오류를 반환한디
- 외부에 존재하는 볼륨 (
[서비스명]:
volumes:
- [볼륨명]:[서비스 안에서의 볼륨 경로]
- [볼륨명]:[서비스 안에서의 볼륨 경로]
위와 같이 volumes 옵션을 통해 볼륨을 컨테이너에 연결하고, 경로를 특정해줄 수 있다
wordpress:
volumes:
- wordpress-db:/usr/src/wp
database:
volumes:
- wordpress-db:/usr/src/db
두 컨테이너가 같은 볼륨을 참조해야 할 경우, 볼륨의 경로 (마지막 폴더명을 제외한 경로) 가 일치해야 한다
네트워크 설정
networks:
[네트워크명]:
driver: [드라이버 설정]
external: [외부 네트워크 여부]
기본적으로 도커에서는 하나의 디폴트 네트워크 ([yml 파일이 위치한 디렉토리명]_default
) 에 모든 컨테이너를 연결하고 있다
커스텀 네트워크를 만들어 특정 컨테이너를 연결하고 싶을 경우, networks
항목 하위에 네트워크들을 추가한다
driver
- 네트워크 드라이버를 설정할 수 있다
- 이 옵션에는
bridge
(기본값),host
,overlay
,ipvlan
,macvlan
,none
, 사용자 정의 드라이버를 설정 가능하다bridge
: 기본값이므로, driver를 명시적으로 지정하지 않으면 bridge로 연결되며, 스탠드얼론 컨테이너로 내부 컨테이너끼리만 통신할 때 이 옵션을 사용한다host
: 컨테이너 - 호스트간 네트워크 독립을 해제하고, 호스트의 네트워크를 바로 사용하는 옵션이다overlay
: 여러 도커 데몬을 연결해서 여러 서비스들이 통신할 수 있도록 하는 옵션이다ipvlan
: ipv4와 ipv6 주소에 대한 관리 권한을 지급한다macvlan
: 각 컨테이너에 맥 주소를 부여하여 네트워크상에 물리 디바이스처럼 동작하게 하고, 도커 데몬이 네트워크를 라우팅해 주도록 한다none
: 네트워크 없음- 그 외에도 도커 허브에 올라가 있는 네트워크 플러그인을 사용할 수도 있다
external
- 외부에 존재하는 네트워크를 끌어다 사용할 수 있다
- 외부에서 해당 이름을 가진 네트워크를 찾아 사용할 수 있다
[서비스명1]:
networks:
- [네트워크명]
[서비스명2]:
networks:
- [네트워크명]
위와 같이 서비스에 networks 옵션을 지정함으로써 컨테이너에 네트워크를 연결할 수 있다
그 외 이슈
포트포워딩?
Port Forwarding = 포트 전달하기
외부 아이피를 통해 네트워크 바깥에서 패킷이 들어올 때, 이 데이터 패킷이 어느 서비스로 전송되어야 하는지 알 수 없다
- 외부 쇼핑몰 (외부 네트워크) 로부터 우리 집 (내부 네트워크) 에 택배 (패킷) 가 도착했다
- 이 택배는 우리 집 주소 (외부 IP) 를 이용해서 배달을 했기 때문에, 내부 수신자 (내부 IP) 가 누군지는 모른다
- 주문자의 이름이 안 적혀있다면 가족들 중 누가 택배의 주인인 지 알 수가 없다
- 운송장에
우리 집 주소 + 택배 수신자명
을 적으면 해당 수신자에게 택배를 전달하는 것으로 합의하자 (포트포워딩)
이처럼 외부 아이피 : 특정 포트
로 접속하면, 내부 아이피 : 내부 포트
로 매핑되어 특정 프로세스에 패킷을 전달하는 것을 포트포워딩이라고 한다
이 그림에서, 외부 인터넷으로부터 날아오는 패킷은 외부 IP만 이용해서 통신을 하므로 공유기까지는 도달을 하지만, 공유기에 연결되어 있는 내부 네트워크에서는 어떤 기기에게 전달되어야 하는지 알 수가 없다
이 패킷이 컴퓨터 패킷인지, 노트북 패킷인지, 스마트폰 패킷인지 내부 IP 주소를 알 수가 없으므로 전달할 방도가 없는 것이다
포트포워딩을 이용하여 12번 포트로 들어오는 패킷은 노트북으로 전달해 주자! 라고 지정하면, 외부에서 들어오는 패킷 중 12번 포트로 들어오는 패킷은 정상적으로 노트북에 전달될 것이다
도커 컴포즈로 컨테이너를 엮을 때에도 일반적인 방법으론 컨테이너끼리의 내부 망에 접근하여 데이터를 주고받을 방법이 없으므로, 포트포워딩을 통해 바깥에 문 (포트) 을 열어 데이터를 받아올 수 있는 통로를 만들어주는 것이다
/var/run/docker.sock: connect: permission denied
$> sudo chmod 666 /var/run/docker.sock
docker.sock
의 권한을 재설정해주면 된다
참고자료
https://www.daleseo.com/docker-compose/
https://docs.docker.com/compose/reference/
https://github.com/Yelp/docker-compose/blob/master/docs/yml.md
https://meetup.toast.com/posts/277/
https://nirsa.tistory.com/80?category=868315
https://junlab.tistory.com/219
https://engineer-mole.tistory.com/223
https://docs.docker.com/network/
https://medium.com/@su_bak/docker-compose에서-서로-다른-container가-같은-volume을-공유하는-방법-5e49430c5282
'42 > 42s Cursus' 카테고리의 다른 글
[Rank 5] ft_irc - 만들어야 하는 명령어 정리 (3) | 2023.01.07 |
---|---|
[Rank 5] ft_irc (작성중) (0) | 2023.01.07 |
[Rank 5] inception - Dockerfile (3) | 2022.10.14 |
[Rank 4] CPP 08 (0) | 2022.09.18 |
[Rank 4] CPP 07 (0) | 2022.09.17 |