Docker Compose 자습서로 기술력 향상

만약 Docker Compose로 재현 가능한 Docker 컨테이너를 생성하는 방법에 대해 궁금하다면, 당신은 올바른 곳에 왔습니다. 이 단계별 Docker Compose 튜토리얼에서는 간단한 컨테이너를 만드는 방법부터 Docker Compose를 사용하여 포트를 매핑하고 복잡한 다중 컨테이너 시나리오까지 모두 배우게 될 것입니다.

준비물

단계별로 따라 하려면 이 튜토리얼에서 다음 사항을 준비하십시오:

다음이 필요합니다:

  1. A fresh install of Ubuntu Server LTS with SSH Enabled. This guide will use Ubuntu Server LTS 20.04.1 as the Docker host machine.
  2. A computer with VS Code installed (optional). This guide will use Visual Studio Code 1.52.1 to SSH to the Docker host and run commands.
  3. VS Code 공식 SSH 확장 프로그램이 설치되어 Docker 호스트에 연결되어 있어야 합니다. (선택 사항)

Docker Compose란?

도커에서 명령이 길어질 수 있습니다. 아래 예를 살펴보십시오. 이 예제는 bookstack이라는 소프트웨어 응용 프로그램을 위한 컨테이너를 생성합니다.

docker create \
   --name=bookstack \
   -e PUID # 응용 프로그램/파일 소유권을 가져올 사용자의 UID 
   -e PGID # 응용 프로그램/파일 소유권을 가져올 사용자의 GID 
   -e DB_USER # 데이터베이스 사용자 
   -e DB_PASS # 데이터베이스 비밀번호 
   -e DB_HOST # 데이터베이스 호스트 
   -e DB_DATABASE # 사용할 데이터베이스 
   -e APP_URL # 응용 프로그램에 접근할 URL (리버스 프록시의 올바른 작동을 위해 필요) 
   -v /host/path/to/config:/config # 업로드된 데이터의 위치 
   -p 80:80/tcp # 웹 UI 포트 
   --restart unless-stopped \
   linuxserver/bookstack:version-v0.31.4

도커 환경의 복잡성이 증가함에 따라 작동하는 컨테이너 설정을 위해 필요한 플래그와 조건의 양도 증가합니다. 도커 명령 줄은 점점 불편하며 문제 해결이 어려워집니다. 특히, 다중 컨테이너 설정이 추가되면 더욱 그렇습니다.

도커 컴포즈는 매우 긴 도커 명령어 대신 구성 파일을 사용하여 재현 가능한 도커 컨테이너를 만드는 방법입니다. 구조화된 구성 파일을 사용하면 실수를 더 쉽게 발견하고 컨테이너 간의 상호작용을 더 쉽게 정의할 수 있습니다.

컨테이너 종속성이나 다중 컨테이너 환경을 다룰 때 도커 컴포즈는 매우 유용해집니다.

Docker Compose는 인프라스트럭처를 코드로서 구성하기 위한 복잡한 분산 시스템인 Kubernetes 없이도 사용할 수 있는 훌륭한 도구입니다.

Docker Compose는 YAML이라는 구성 파일 구조를 사용합니다. YAML은 JSON이나 HTML과 유사한 구조화된 기계 판독 가능한 언어입니다. YAML은 사람이 읽기 쉬우면서도 구조화된 기능을 유지하기 위해 노력하고 있습니다.

YAML은 탭과 다른 공백이 중요하며 올바르게 포맷되어야 하는 단점이 있습니다. VS Code는 이러한 어려운 작업을 대신 처리해주므로 많은 예제가 VS Code에서 수행되는 이유입니다.

Docker Compose 설치

이제 손을 더럽히기 시작해봅시다. Docker 호스트에 연결되어 있다고 가정하고 Docker Compose를 설치해봅시다.

Docker Compose는 Docker 런타임과 별개의 패키지입니다. 하지만 Docker Compose를 설치하면 Docker 런타임도 함께 설치되므로 한 번에 두 마리의 새를 잡을 수 있습니다!

Docker Compose와 Docker 런타임을 설치하려면 다음 두 명령어를 실행하세요.

# 소프트웨어 목록(저장소로 알려진)을 업데이트한 다음 도커 컴포즈를 설치합니다.
# 필요한 종속성이 있는 경우. -y 플래그는 확인을 건너뛰기 위해 사용됩니다.
sudo apt update -y
sudo apt install docker-compose -y
The installation command for Docker Compose

설치되면 이제 컨테이너를 저장할 폴더 구조를 만들어야 합니다.

도커 컴포즈를 위한 폴더 구조 만들기

도커 컴포즈를 사용하여 컨테이너를 생성하기 전에 먼저 컨테이너를 저장할 폴더를 만들어야 합니다. 컨테이너를 저장할 폴더 구조를 만들뿐만 아니라 다양한 도커 명령이 다양한 구성 파일의 위치에 민감하다는 것을 알게 될 것입니다. 도커 컴포즈도 마찬가지입니다.

도커 컴포즈의 가장 중요한 구성 요소는 docker-compose.yaml이라고 불리는 구성 파일입니다. 이 구성 파일은 위에서 설명한대로 도커 실행 환경이 컨테이너를 빌드하는 방식을 지시합니다.

도커 컴포즈를 실행하면 명령은 구성 파일을 해당 명령이 실행되는 폴더와 동일한 폴더에서 찾습니다. 이 요구 사항 때문에 도커 컴포즈를 실행할 때는 항상 별도의 폴더를 만드는 것이 가장 좋습니다.

폴더 당 하나의 도커 컴포즈 구성 파일만 있을 수 있습니다.

도커 컴포즈를 사용하여 도커 컨테이너를 생성하는 방법을 보여주기 위해 먼저 미래의 컨테이너와 해당 구성 파일을 저장할 폴더 구조를 작성하십시오. 이 작은 파일 서버의 경우 Caddy를 사용합니다.

Caddy는 Go 언어로 작성된 아파치 HTTP 서버 또는 엔진엑스와 유사한 파일서버입니다. Caddy는 구성 없이도(index.html 파일을 자동으로 생성하거나 제공함) 사용 편의성을 목표로 특별히 설계되었습니다. 이러한 조합으로 caddy는 초보자에게 좋은 선택지가 됩니다.

Docker 호스트에 로그인한 상태라고 가정하고 다음과 같은 폴더 구조를 생성합니다:

  1. 홈 디렉토리에서 containers라는 폴더를 만듭니다. 이 폴더는 이와 다른 컨테이너들을 위한 좋은 자리 표시자가 될 것입니다.
  2. containers 폴더 안에 caddy라는 하위 폴더를 만듭니다. 이 폴더에는 Docker Compose 구성 파일과 Caddy 컨테이너 자체가 포함될 것입니다.
  3. 마지막으로 컨테이너 폴더인 caddy 안에 docker-compose.yaml라는 빈 텍스트 파일을 만듭니다. 이 파일이 Docker Compose 구성 파일이 될 것입니다.

폴더 구조와 Docker Compose 구성 파일을 생성했으므로 이제 해당 구성 파일을 Docker Compose 구성으로 채우기 시작할 수 있습니다.

Docker Compose 구성 파일 생성

caddy 컨테이너를 위한 가장 기본적인 형태의 docker-compose.yaml 파일은 다음과 같습니다. 좋아하는 Linux 텍스트 편집기나 VS Code에서 이전에 생성한 Docker Compose 구성 파일에 아래 코드를 복사하여 붙여넣으십시오.

version: "3.7"
services:
  caddy:
    container_name: "caddy"
    image: "caddy:latest"
    ports:
      - "80:80"

아래에 표시된 각 옵션을 하나씩 살펴보겠습니다:

  • version는 docker-compose 파일의 버전을 지정합니다. Docker Compose의 각 새로운 정의는 명세의 파괴적인 변경 사항을 포함합니다. 따라서 버전은 Docker Compose가 사용해야 하는 기능을 알 수 있도록 중요합니다. 버전 3.7은 Ubuntu 20.04.1 LTS가 지원하는 최신 버전입니다.

Docker Compose 3.x의 전체 명세는 여기에서 찾을 수 있습니다. 링크된 문서에는 Docker Compose 내에서 사용할 수 있는 모든 옵션이 나와 있습니다.

  • services에는 실제 컨테이너의 사양이 포함됩니다. 이 섹션에서 여러 컨테이너를 정의할 수 있습니다.
  • caddy는 첫 번째 컨테이너의 이름입니다 (참고용).
  • container_name은 Docker에 의해 컨테이너에 지정된 실제 이름을 정의하며 고유해야 합니다.
  • image는 이미지의 이름입니다. 이 경우, caddyDocker Hub에서 정의되어 있습니다. 콜론으로 구분된 태그 뒤의 이름 또는 숫자는 버전입니다.

Port Mapping

특히 마지막 옵션은 특별한 언급이 필요합니다:

ports:
  - "80:80"

도커 컴포즈에서 ports 지시문을 사용하면 호스트에서 컨테이너로의 하나 이상의 매핑을 설정할 수 있습니다. 예를 들어, 위의 예제에서는 호스트의 포트 80을 컨테이너의 포트 80에 매핑했습니다. 그러나 포트 번호를 일치시킬 필요는 없습니다. 아래 예제에서는 호스트의 포트 8800을 컨테이너의 포트 80에 매핑합니다.

ports:
  - "8800:80"

아래와 같이 여러 포트를 정의할 수도 있습니다.

ports:
  - "80:80"
  - "443:443"

이렇게하면 웹 서버의 일반적인 구성으로 호스트에 포트 80443을 모두 매핑할 수 있습니다 (HTTP 및 HTTPS 모두 제공하기 위해).

도커 이미지 작성자는 생성 시 사용 가능한 포트를 정의합니다. 사용 중인 도커 허브의 이미지 또는 유지 관리자 웹 사이트에서 매핑 가능한 포트에 대한 문서를 확인하십시오. 포트가 사용되지 않는 경우 포트를 매핑하는 것은 의미가 없습니다!

이를 염두에 두고 컨테이너를 실행해 보겠습니다.

컨테이너 실행하기

지금까지 docker-compose.yaml 파일이 ~\containers\caddy 폴더 안에 있어야 합니다. 이제 Caddy 컨테이너를 생성하고 시작할 때입니다.

터미널에서 다음 명령을 실행하면 docker-compose.yaml 파일에 정의된 도커 컨테이너가 실행됩니다.

# 이 명령은 파일이 있는 폴더와 동일한 폴더에서 실행되어야 합니다. -d 플래그는
# 컨테이너를 백그라운드에서 실행하는 *떨어진* 명령입니다.
sudo docker-compose up -d

docker-compose.yaml 파일을 실행할 때 sudo docker-compose up -d를 실행할 때 docker-compose.yaml 파일의 위치를 지정할 필요가 없었음을 알 수 있습니다. Docker Compose는 docker-compose.yaml 파일이 포함된 폴더 내에서 모든 명령을 실행할 것으로 예상합니다. 왜냐하면 많은 명령이 해당 폴더를 기준으로 상대적이기 때문입니다.

http://<your ip>로 이동하여 컨테이너가 작동 중인지 확인하십시오. 이 가이드는 참조를 위해 http://homelab-docker를 사용하고 있습니다.

이 처리 과정을 아래 애니메이션에서 Docker 호스트에 SSH로 연결된 VS Code에서 확인할 수 있습니다.

Demonstrating a container created with Docker Compose

성공입니다! 이제 Docker Compose를 사용하여 구성 파일에서 컨테이너를 시작하는 데 성공했습니다. 이 첫 번째 중요한 단계를 완료한 후에는 컨테이너의 상태를 어떻게 관리하는지 살펴보겠습니다.

분리된 컨테이너 관리 명령어

이전 섹션에서 -d 플래그를 사용하여 caddy 컨테이너를 시작했습니다. 이렇게 하면 컨테이너가 분리된 상태에서 실행됩니다. 컨테이너가 분리된 상태에 있으면 컨테이너가 백그라운드에서 계속 실행됩니다. 그러나 이것은 문제가 발생합니다. 더 이상 직접 제어할 수 없는 경우에는 어떻게 해당 컨테이너를 관리해야 합니까?

이 문제를 해결하기 위해 Docker Compose에는 docker-compose.yaml 파일로 시작된 컨테이너를 관리하는 일련의 명령이 있습니다:

  • docker-compose restart은 현재 실행 중인 컨테이너를 재시작하기 위해 사용됩니다. 이 작업은 실제로 docker-compose up -d를 다시 실행하는 것과 다릅니다. 재시작 명령은 기존 컨테이너를 간단히 재시작하고, docker-compose up -d 명령을 다시 실행하며, (구성 파일이 변경되었을 경우) 컨테이너를 처음부터 다시 생성합니다.
  • docker-compose stop은 컨테이너를 중지시키지만 컨테이너를 파괴하지는 않습니다. 마찬가지로, docker-compose start는 컨테이너를 다시 시작합니다.
  • docker-compose down은 실행 중인 컨테이너를 중지하고 파괴합니다. 이때 볼륨의 바인드 마운트가 필요한 이유가 나타납니다 (자세한 내용은 아래에서 읽을 수 있습니다).
  • docker-compose pull은 현재 버전의 도커 이미지(또는 이미지)를 저장소에서 가져옵니다. latest 태그를 사용하는 경우, docker-compose down && sudo docker-compose up -d를 사용하여 컨테이너를 최신 버전으로 대체할 수 있습니다. docker-compose pull을 사용하면 최소한의 다운타임으로 컨테이너를 빠르게 업데이트할 수 있습니다.
  • docker-compose logs는 실행 중인 (또는 중지된) 컨테이너의 로그를 표시합니다. 컴포즈 파일에서 여러 개의 컨테이너가 정의된 경우에는 docker-compose logs <컨테이너 이름>으로 개별 컨테이너에 접근할 수도 있습니다.

A full list of docker-compose commands can be seen by running docker-compose with no additional arguments or referenced here in the documentation.

이제 실행 중인 컨테이너가 있으므로, 로컬 기기에 저장된 콘텐츠를 사용하는 방법을 살펴보겠습니다.

도커 컴포즈에서 바인드 마운트 생성하기

바인드 마운트는 Docker가 중요한 사용자 데이터를 서버의 로컬 저장소에 매핑하는 방법입니다. 먼저 컨테이너가 호스팅할 콘텐츠를 생성하세요:

  1. Docker 호스트에서 ~/containers/caddy 폴더 내부에 files라는 새 폴더를 만드세요.

2. 아래와 같이 보이는 index.html이라는 새 파일을 ~/containers/caddy 폴더 내에 생성하세요. 이것은 Caddy 웹서버가 제공할 메인 페이지가 됩니다.

<body><h2>hello world!</h2></body>

3. Docker Compose 구성 파일을 아래와 같이 수정하세요. 아래 예제 파일은 volumes 섹션을 추가하고 방금 만든 files 폴더를 컨테이너에서 사용할 수 있도록 바인드 마운트하고 있습니다.

version: "3.7" services: caddy: container_name: "caddy" image: "caddy:latest" ports: - "80:80" volumes: #the ./ refers a folder relative to the docker-compose file - "./files:/usr/share/caddy"

4. 다시 docker-compose up -d를 실행하세요. Docker Compose는 이제 파일이 변경되었음을 감지하고 컨테이너를 다시 생성합니다.

5. 브라우저로 컨테이너 페이지로 이동하면 이제 “Hello World!” 페이지가 제공되는 것을 확인할 수 있습니다.

다음 애니메이션에서 다음을 확인할 수 있습니다:

Creating a bind mount using Docker Compose

이제 로컬 머신에 저장된 콘텐츠를 호스팅하고 있습니다! 그러나 콘텐츠가 네트워크 공유와 같은 외부 소스에 있는 경우 어떻게 할까요?

Docker Volumes와 함께 Docker Compose 사용

Docker Compose로 간단한 컨테이너를 생성한 후, 컨테이너가 네트워크 공유 디렉터리와 같은 다른 위치의 파일에 액세스해야 할 수도 있습니다. 그렇다면 Docker 볼륨을 사용하도록 컨테이너를 Docker Compose 구성 파일에서 구성할 수 있습니다.

데모를 위해 이 가이드에서는 Docker 호스트에 네트워크 파일 공유 (NFS) 서버를 만들 것입니다. 로컬 콘텐츠를 NFS 마운트로 제공하는 것은 데모 외에는 실용적인 목적이 없습니다. NFS 볼륨을 마운트한다면 일반적으로 NAS나 원격 서버와 같은 외부 소스에서 가져올 것입니다.

NFS 공유 설정

아직 NFS 공유를 설정하지 않았다면, 이 튜토리얼을 위해 Docker 호스트에 공유를 설정하세요. 다음과 같이 진행하세요:

  1. NFS 서버 패키지를 설치하기 위해 apt install nfs-kernel-server -y 명령을 실행하세요.

2. 다음 명령을 실행하여 컨테이너를 NFS 익스포트(Windows CIFS 공유와 유사)로 추가하세요.

# /etc/exports 구성 파일에 NFS 공유를 생성하는 라인을 추가합니다. 이 공유는 localhost에만 노출됩니다. (다른 컴퓨터에서의 액세스를 방지하기 위해) echo '/home/homelab/containers localhost(rw,sync,no_root_squash,no_subtree_check)' | sudo tee -a /etc/exports # 새 구성으로 NFS 서버를 재시작합니다. sudo systemctl restart nfs-kernel-server

3. 이제 호스트가 NFS 공유를 노출하는지 확인하기 위해 showmount -e localhost를 실행합니다. 이 명령은 현재 노출된 NFS 공유와 접근 권한을 보여줍니다.

아래 스크린샷에서 /home/homelab/containers가 노출되어 있지만, 로컬호스트 컴퓨터(도커 호스트와 동일한 서버)에만 접근할 수 있습니다.

Creating a NFS share in Ubuntu 20.04

출력에서 /home/<username>/containers 폴더를 보면, NFS 공유가 설정된 것입니다.

Docker 명명된 볼륨 정의

NFS 공유를 생성한 후, 이제 Docker가 해당 공유에 액세스하는 방법을 지정해야 합니다. Docker Compose를 사용하여 Docker Compose 구성 파일에서 명명된 볼륨을 정의할 수 있습니다.

A named volume is a way for Docker to abstract network-based file shares. Network file sharing comes in all sorts of shapes and sizes these days: CIFS (windows) shares, NFS (Linux) shares, AWS S3 Buckets, and more. By creating a Named Volume, Docker does the hard part of figuring out how to talk to the network share and lets the container just treat the share as if it is local storage.

명명된 볼륨을 생성하려면 다음을 수행합니다.

  1. Docker Compose 구성 파일(docker-compose.yaml)을 엽니다. 따라하고 있다면, 파일은 ~/containers/caddy 폴더에 위치해야 합니다.

2. Docker Compose 구성 파일 내에서 services 섹션 다음에 volumes 섹션을 추가합니다. 구성 파일은 아래와 같아야 합니다. volumes 섹션은 MyWebsite라는 명명된 볼륨을 생성합니다. 해당 명명된 볼륨 내에서 필요한 매개변수(IP, NFS 설정 및 경로)가 지정됩니다. services 섹션 내의 volumes 매개변수도 수정되어 로컬 폴더 대신 명명된 볼륨을 가리킵니다.

version: "3.7"
 services:
   caddy:
     container_name: "caddy"
     image: "caddy:latest"
     ports:
       - "80:80"
     volumes:
       - "MyWebsite:/usr/share/caddy"
 volumes:
   MyWebsite:
     driver_opts:
       type: "nfs"
       o: "addr=localhost,nolock,soft,rw"
       device: ":/home/homelab/containers/caddy/files"

3. 이름이 지정된 볼륨을 NFS 공유를 가리키도록 정의한 후 Docker Compose 구성 파일에서 docker-compose up -d를 실행하여 컨테이너를 생성하고 시작합니다. 모든 것이 잘 되면 컨테이너와 웹 사이트가 다시 시작됩니다.

Setting NFS client settings within Docker Compose in VS Code

4. 컨테이너 페이지로 이동합니다. index.html 내용은 로컬로 마운트되는 것처럼 나타날 것입니다. 그러나 해당 파일은 네트워크에 설정된 NFS 서버를 통해 마운트됩니다.

Demonstrating access to the index.html file through an NFS share in Docker

외부 Docker 볼륨을 Docker Compose에 마운트할 수 있으므로 이제 컨테이너에 여러 종류의 네트워크 저장소를 가져올 수 있습니다. 그러나 Docker Compose는 단일 컨테이너나 볼륨만 정의하는 것 이상의 작업을 수행할 수 있습니다. 더 복잡한 다중 컨테이너 시나리오로 들어가 봅시다.

이제 더 이상 caddy 컨테이너를 사용하지 않을 것이므로 docker-compose down을 사용하여 컨테이너를 제거할 수 있습니다.

Docker Compose에서 여러 컨테이너 정의하기

대부분의 Docker 컨테이너는 고립된 환경에서 작동하지 않습니다. Docker 컨테이너에는 일반적으로 데이터베이스나 별도의 웹 서비스와 같은 서비스 종속성이 있으며 이들은 API를 통해 통신합니다.

Docker Compose를 사용하면 하나의 파일 내에서 정의된 컨테이너를 그룹화할 수 있습니다. 단일 파일 내에서 여러 컨테이너를 정의함으로써 컨테이너는 종속 서비스 간에 통신할 수 있으며 복잡한 컨테이너 레이아웃의 조직화를 단순화할 수 있습니다.

이러한 시나리오를 보여주기 위해 인기 있는 위키 애플리케이션인 BookStack을 설정해 봅시다.

은 사용 편의성과 등급적 레이아웃으로 유명한 위키 소프트웨어입니다 (예: 와 달리 평면 레이아웃).

은 많은 웹 응용 프로그램과 마찬가지로 제대로 작동하려면 별도의 데이터베이스와 데이터베이스와 통신하기 위한 정보가 필요합니다. 이러한 상황을 설정하는 것이 도커 컴포즈의 장점입니다.

<도커 컴포즈 구성 파일 생성하기>

은 내부 유지 관리 도커 이미지를 갖고 있지 않지만, 을 대신하여 신뢰할 수 있는 <도커 허브 이미지>를 유지 관리합니다. 도커 허브 사이트의 설명서에는 권장하는 도커 컴포즈 구성 파일이 있지만, 이 튜토리얼에서는 개념을 설명하면서 새로운 구성 파일을 작성할 것입니다.

<도커 호스트에서:

  1. 먼저, BookStack을 위한 폴더를 만듭니다. 이전 섹션의 튜토리얼을 따라왔다면 ~/containers 폴더가 있을 것입니다. 거기에 bookstack이라는 폴더를 만듭니다.

2. 그런 다음 bookstack 폴더 내에 docker-compose.yaml이라는 빈 도커 컴포즈 구성 파일을 만듭니다.

Creating the folder structure for Bookstack in VS Code

3. 이제 도커 컴포즈 구성 파일을 열고 두 개의 컨테이너, bookstack 컨테이너와 bookstack_db (mariadb) 컨테이너를 정의합니다.

version: "3.7"
 services:
   bookstack:
     container_name: "bookstack"
     image: "ghcr.io/linuxserver/bookstack"
     ports:
       - "8080:80"
     volumes:
       - "./files:/usr/share/caddy"
     depends_on:
       - "bookstack_db"
   bookstack_db:
     container_name: "bookstack_db"
     image: "mariadb"
     volumes:
       - "./db:/var/lib/mysql"

지금까지이 docker-compose.yaml 파일은 대부분 이미 소개 된 개념을 사용합니다. 두 개의 서비스 (bookstackbookstack_db)가 이미지와 바인드 마운트와 함께 있습니다. bookstack 컨테이너는 호스트 포트 8080에서 내부 포트 80으로의 포트 매핑을 가지고 있습니다.

Docker 컨테이너의 오버 헤드가 매우 낮기 때문에 웹 응용 프로그램마다 별도의 데이터베이스 컨테이너를 정의하는 것이 일반적입니다. 이렇게 함으로써 역할을 더욱 분리 할 수 있습니다. 이는 단일 데이터베이스 설치가 수백 개의 웹 응용 프로그램을 제공하는 전통적인 데이터베이스 설정과는 명확히 다릅니다.

위의 파일에서 볼 수있는 새로운 옵션은 depends_on 명령입니다. 이 명령은 컨테이너가 시작되는 순서를 Docker에게 알려줍니다. depends_on 명령을 정의하면 Docker에게 bookstack_db 컨테이너가 먼저 시작되어야한다고 알려줍니다.

환경 변수를 사용하여 컨테이너 간 통신 설정

이전 섹션에서 생성된 구성 파일은 아직 완성되지 않았습니다. 두 개의 서비스 (컨테이너)를 정의했지만, 서로 통신하지 않습니다! bookstack 컨테이너는 bookstack_db 컨테이너와 통신하는 방법을 알지 못합니다. 환경 변수를 사용하여 이 문제를 해결해 보겠습니다.

환경 변수는 Docker 컨테이너에 변수를 제공하는 가장 일반적인 방법입니다. 이러한 변수는 컨테이너가 수행해야하는 작업에 대한 정보를 런타임에서 (또는 docker-compose.yaml 구성 파일에서 정의 된대로) 제공하는 변수입니다.

환경 변수는 도커 이미지를 생성하는 사람에 의해 정의됩니다. 사용하는 도커 이미지에 따라 다르며, 어떤 환경 변수를 사용해야 하는지에 대한 정보는 작성자의 문서를 참조해야 합니다.

환경 변수를 정의하는 두 가지 방법이 있습니다. docker-compose.yaml 파일 자체에 직접 정의하거나 별도의 파일로 정의할 수 있습니다.

A separate file is, typically, the recommended method, especially if variables contain sensitive data such as passwords. A docker-compose.yaml file is designed to be shared or even uploaded to a public-facing GitHub repo. Having a separate file for sensitive data reduces the chance of an accidental security breach.

도커 호스트에서 bookstack 컨테이너와 bookstack_db 컨테이너를 위해 두 개의 환경 변수를 생성하세요.

  1. ~/containers/bookstack 폴더에 bookstack.env라는 이름의 새 파일을 만들고 다음 내용을 포함시킵니다:
APP_URL is the IP address or hostname of your server. This article is using homelab-docker
 APP_URL=http://homelab-docker:8080
 DB_HOST is the container name you gave your container
 DB_HOST=bookstack_db
 DB_USER is defined in the bookstack_DB environment file
 DB_USER=bookstack_user
 DB_PASS is also defined in the bookstack_DB environment file
 DB_PASS=MySecurePassword
 DB_DATABASE is the name of the database within mariadb
 DB_DATABASE=bookstack

2. ~/containers/bookstack 폴더에 bookstack_db.env라는 이름의 새 파일을 만들고 다음 내용을 포함시킵니다:

The root password for our database, keep it secret, keep it safe
 MYSQL_ROOT_PASSWORD=MySecureRootPassword
 The database bookstack will be using
 MYSQL_DATABASE=bookstack
 the user bookstack will be using
 MYSQL_USER=bookstack_user
 the password bookstack will be using
 MYSQL_PASSWORD=MySecurePassword

3. 보안 상의 이유로 env 파일을 다른 사용자가 읽을 수 없도록 설정하세요.

chmod 600 bookstack.env bookstack_db.env

bookstack.env와 bookstack_db.env 파일에는 민감한 데이터가 포함되어 있으므로 읽기 권한을 변경해야 합니다.

4. ~/containers/bookstack/docker-compose.yaml 도커 컴포즈 파일을 업데이트하여 아래에 표시된 두 환경 파일을 참조하세요.

version: "3.7"
 services:
   bookstack:
     container_name: "bookstack"
     image: "ghcr.io/linuxserver/bookstack"
     ports:
       - "8080:80"
     volumes:
       - "./files:/usr/share/caddy"
     depends_on:
       - "bookstack_db"
     env_file:
       - "./bookstack.env"
   bookstack_db:
     container_name: "bookstack_db"
     image: "mariadb"
     volumes:
       - "./db:/var/lib/mysql"
     env_file:
       - "./bookstack_db.env"

5. 이제 도커 컴포즈를 사용하여 bookstackbookstack_db 컨테이너를 시작하세요.

sudo docker-compose up -d

이 섹션에서 각각의 단계를 VS Code에서 수행한 내용을 아래에서 확인할 수 있습니다.

Setting up environment variables and the Docker Compose file with VS Code

도커 컴포즈 로그 모니터링

도커 엔진은 도커 컴포즈와 함께 여러 가지 작업을 백그라운드에서 수행합니다. 특히 여러 컨테이너를 동시에 작업할 때는 무엇이 일어나고 있는지 모니터링할 수 있는 것이 도움이 됩니다.

예를 들어, bookstack 컨테이너를 모니터링하려면 logs 명령어를 사용하세요. 이 튜토리얼에서는 로그에 [services.d] done이 표시되면 bookstack URL로 이동할 수 있습니다.

sudo docker-compose logs bookstack
Using the docker-compose logs command
The bookstack welcome screen. Default login is [email protected]/password

이 단계에서는 독립적인 컨테이너 내에서 완전히 작동하는 완전한 기능의 위키가 있어야 합니다. 이 위키는 도커 내에서 자체 데이터베이스를 가지고 있습니다!

bookstack 및 bookstack_db 폴더만 있으면 bookstack 환경을 처음부터 다시 만들 수 있습니다.

도커 컴포즈와 네트워킹

지금까지 컨테이너가 함께 작동하는 통신 및 네트워킹 측면에 대해 크게 배우지 않았습니다. 이를 변경해보겠습니다.

이전 섹션에서 수행한 것처럼 단일 docker-compose.yaml 파일 내에서 여러 컨테이너를 생성할 때, 이들은 모두 동일한 네트워크(부모 폴더 이름_default로 일반적으로 불림)에 할당됩니다.

아래와 같이 docker-compose up -d를 실행할 때 생성된 컨테이너용 네트워크를 확인할 수 있습니다.

The default network created with docker-compose comes up

모든 컨테이너가 동일한 네트워크에 할당되면 도커는 내부적으로 이들을 위한 DNS 항목을 생성합니다. 따라서 이전 예제에서 환경 변수에서 데이터베이스를 bookstack_db로 참조했습니다. 이 bookstack_db 이름은 실제로 데이터베이스 컨테이너의 IP 주소를 가리키는 DNS 항목입니다.

또한 Docker Compose에 의존하지 않고 네트워크를 자동으로 생성할 수도 있습니다. 내부 또는 외부 네트워크를 수동으로 정의할 수 있습니다. 컨테이너가 다른 docker-compose.yaml 파일의 컨테이너와 통신해야 할 때 수동으로 네트워크를 정의하는 것이 좋습니다. 포트를 공개하거나 둘 다 참여할 수 있는 네트워크를 생성할 수 있습니다!

네트워크를 명시적으로 정의하기 시작하면 기본 네트워크도 명시적으로 정의해야 합니다. Docker Compose는 네트워크를 명시적으로 정의하기 시작하면 해당 네트워크를 자동으로 생성하지 않습니다.

이제 bookstack docker-compose.yaml을 수정하여 외부에서 생성된 네트워크를 포함시킵니다.

  1. docker network create my_external_network로 외부 네트워크를 생성합니다.

2. docker-compose.yaml에서 외부 네트워크를 정의합니다.

version: "3.7"
 services:
   bookstack:
     container_name: "bookstack"
     image: "ghcr.io/linuxserver/bookstack"
     ports:
       - "8080:80"
     volumes:
       - "./files:/usr/share/caddy"
     depends_on:
       - "bookstack_db"
     env_file:
       - "./bookstack.env"
     networks:
       - "my_external_network"
       - "bookstack_default"
   bookstack_db:
     container_name: "bookstack_db"
     image: "mariadb"
     volumes:
       - "./db:/var/lib/mysql"
     env_file:
       - "./bookstack_db.env"
     networks:
       - "bookstack_default"
 networks:
   bookstack_default:
   my_external_network:
     external: true

3. docker-compose up -d를 실행하여 컨테이너를 다시 생성합니다. 이제 두 개의 컨테이너가 아래에 표시된 것처럼 두 개의 네트워크에 참여합니다.

A highlight of the networks defined within a docker-compose file

bookstack 컨테이너는 이제 외부에서 정의된 네트워크에 또한 참여합니다. 이를 통해 Docker를 떠나기 전에 bookstack HTTP 트래픽을 HTTPS로 변환하는 다른 컨테이너(역방향 프록시라고 함)를 생성할 수 있습니다.

특정 사용자를 실행하는 컨테이너 설정.

기본적으로, 모든 Docker 컨테이너는 격리된 root 사용자로 실행됩니다. 이것은 기본 관리자 사용자로 로그인하여 가상 머신을 실행하는 것과 동일합니다. 이것은 일반적으로 문제가 되지 않지만, 만약 sandbox가 침해당하면 보안 문제가 생길 수 있습니다.

root로 실행하는 또 다른 문제는 파일 권한입니다. bookstack 폴더 내의 db 폴더를 삭제하려고 하면, 실제로 할 수 없을 것입니다. 해당 내용은 root에 의해 소유되어 있습니다.

대부분의 이미지는 비-root 사용자로 실행하는 것을 선호하지 않지만, 특히 linuxserver.io 이미지는 컨테이너 내에서 실행되는 사용자를 설정하는 환경 변수를 제공합니다. bookstack.env 설정에 UID=1000GID=1000을 추가함으로써 이를 수행할 수 있습니다.

1000:1000은 첫 번째 ubuntu 사용자의 기본 사용자 ID와 그룹입니다(당신이 그럴 수도 있고 아닐 수도 있습니다). 사용자 ID와 그룹 ID에 대해 더 자세히 알아볼 수 있습니다(관련: 리눅스 세계의 윈도우즈 사용자: 사용자와 파일 권한)

또한 docker-compose의 user 매개변수를 사용하여 UID와 GID를 강제로 설정할 수 있지만, 이것은 대부분의 컨테이너가 다른 사용자로 강제 설정할 때 잘 작동하지 않기 때문에 권장하지 않습니다.

재시작 정책 설정

restart 정책을 사용하여 Docker Compose로 빌드된 컨테이너가 실패할 경우, restart: <option> 매개변수를 docker-compose.yaml의 컨테이너 설정에 추가하세요.

restart: "no"
restart: always
restart: on-failure
restart: unless-stopped

이 매개변수를 추가하면 예상치 못한 전원 문제로 인한 다운타임을 방지하기 위해 컨테이너가 자동으로 다시 시작됩니다.

컨테이너를 위한 DNS 항목 수동 설정

Windows 및 Linux와 마찬가지로 Docker에도 “호스트 파일”이 있습니다. 구성 파일에서 extra_hosts 매개변수를 사용하여 호스트를 특정 IP로 해결하도록 강제할 수 있습니다. 이는 분할 DNS 또는 일시적으로 상호 작용하려는 테스트 서버와 같은 DNS 제약 조건이 있는 경우 유용합니다.

extra_hosts:
  - "somehost:x.x.x.x"
  - "otherhost:x.x.x.x"

명령 실행하기

컨테이너가 시작되면 docker-compose run을 사용하여 컨테이너 내에서 명령을 실행할 수 있습니다. 예를 들어, bookstack 컨테이너 내에서 Bash 터미널을 시작하려면 아래 명령을 실행하면 됩니다.

docker-compose run web bash

결론

이 단계에서는 웹 상의 대부분의 docker-compose 자습서를 따라갈 충분한 정보를 갖고 있어야 합니다. 이 지식을 갖는 것은 Docker의 세계로 진입하고 인프라를 코드로 웹 앱을 빌드하는 능력을 크게 향상시킬 것입니다.

Source:
https://adamtheautomator.com/docker-compose-tutorial/