치춘짱베리굿나이스

[Rank 5] inception - Docker-compose 본문

42/42s Cursus

[Rank 5] inception - Docker-compose

치춘 2022. 10. 26. 19:19

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]: 빌드된 이미지도 삭제한다
    • typeall (모든 이미지) 또는 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” 과 같이 지정하면 된다
      • 예를 들어, nginxwordpress에 연결하고자 한다면, wordpress 컨테이너 측의 포트를 expose하면 된다
    • dockerfileexpose 설정이 되어 있다면 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 = 포트 전달하기

외부 아이피를 통해 네트워크 바깥에서 패킷이 들어올 때, 이 데이터 패킷이 어느 서비스로 전송되어야 하는지 알 수 없다

  1. 외부 쇼핑몰 (외부 네트워크) 로부터 우리 집 (내부 네트워크) 에 택배 (패킷) 가 도착했다
  2. 이 택배는 우리 집 주소 (외부 IP) 를 이용해서 배달을 했기 때문에, 내부 수신자 (내부 IP) 가 누군지는 모른다
  3. 주문자의 이름이 안 적혀있다면 가족들 중 누가 택배의 주인인 지 알 수가 없다
  4. 운송장에 우리 집 주소 + 택배 수신자명 을 적으면 해당 수신자에게 택배를 전달하는 것으로 합의하자 (포트포워딩)

이처럼 외부 아이피 : 특정 포트 로 접속하면, 내부 아이피 : 내부 포트 로 매핑되어 특정 프로세스에 패킷을 전달하는 것을 포트포워딩이라고 한다

이 그림에서, 외부 인터넷으로부터 날아오는 패킷은 외부 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/79

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
Comments