Docker Compose

artp·2024년 11월 4일

docker

목록 보기
5/6
post-thumbnail

Docker Compose란?

Docker Compose는 여러 개의 Docker 컨테이너들을 하나의 서비스로 정의하고 구성하여, 복잡한 애플리케이션을 하나의 설정 파일로 관리할 수 있도록 도와주는 도구입니다.

예를 들어, 웹 애플리케이션을 구성할 때, 웹 서버, 데이터베이스, 캐시 등 여러 서비스가 필요할 수 있습니다. 이때 각 서비스를 별도의 컨테이너로 실행하고 관리하려면 여러 명령어를 각각 입력해야 하고, 설정이 복잡해질 수 있습니다. Docker Compose를 사용하면, 각 컨테이너의 설정과 네트워크, 볼륨, 환경 변수 등을 하나의 설정 파일(docker-compose.yml)에 작성하여 한 번에 구성하고 실행할 수 있습니다.

Docker Compose를 사용하는 이유

1. 여러 개의 컨테이너를 관리하는 데 용이

Docker Compose는 여러 개의 컨테이너로 구성된 복잡한 애플리케이션을 쉽게 관리할 수 있도록 도와줍니다. 각각의 컨테이너를 일일이 관리할 필요 없이, 한 곳에 정의된 설정 파일을 통해 모든 컨테이너를 일괄적으로 실행할 수 있습니다. 예를 들어, 웹 서버, 데이터베이스, 캐시 서버 등 여러 서비스가 필요한 경우, Docker Compose로 각 서비스를 하나의 환경에서 간편하게 구성하고 관리할 수 있습니다.

2. 복잡한 명령어를 간소화

기존에는 MySQL 같은 이미지를 실행할 때 다음과 같은 긴 명령어를 입력해야 했습니다:

$ docker run -e MYSQL_ROOT_PASSWORD=password123 -p 3306:3306 -v /Users/admin/Documents/docker-mysql/mysql_data:/var/lib/mysql -d mysql

이와 같은 긴 명령어를 기억하고 입력하는 것은 번거롭고, 오타가 날 확률도 높아집니다. 하지만 Docker Compose에서는 설정 파일에 필요한 옵션을 미리 정의해 두기 때문에, 매번 복잡한 명령어를 입력할 필요 없이, 단순히 다음과 같이 명령어 한 줄만 입력하면 됩니다:

$ docker-compose up

이 명령어 하나로 모든 컨테이너가 설정된 환경에 맞춰 자동으로 실행됩니다. 복잡한 설정을 한 번에 정의하고, 단순한 명령어로 실행할 수 있어, 배포 및 관리를 더욱 효율적으로 할 수 있습니다.

Docker Compose 명령어

자주 사용하는 Docker Compose 주요 명령어 정리

  1. 현재 경로 확인

    • ls: 현재 디렉터리에 docker-compose.yml 파일이 있는지 경로와 파일을 확인합니다. Docker Compose 명령어는 이 파일이 위치한 경로에서 실행해야 올바르게 작동합니다.

      ls
  2. 컨테이너 상태 확인

    • docker compose ps: 현재 Docker Compose 파일로 실행 중인 컨테이너의 상태를 확인합니다. 각 컨테이너의 이름, 상태, 포트 매핑 정보 등을 확인할 수 있습니다.

      docker compose ps
    • docker compose ps -a: 모든 컨테이너(실행 중이거나 종료된 것 포함)의 상태를 표시합니다. 종료된 컨테이너까지 모두 확인하고자 할 때 사용합니다.

      docker compose ps -a
  3. 컨테이너 로그 확인

    • docker compose logs: 실행 중인 모든 컨테이너의 로그를 출력하여 확인합니다. 이를 통해 컨테이너에서 발생하는 로그를 모니터링할 수 있습니다.

      docker compose logs
  4. 컨테이너 실행

    • docker compose up: Docker Compose 파일에 정의된 컨테이너를 실행합니다. 처음 실행 시 이미지가 없으면 자동으로 빌드합니다. -d 옵션 없이 실행하면 터미널에 로그가 출력됩니다.

      docker compose up
    • docker compose up -d: 백그라운드에서 컨테이너를 실행합니다. -d 옵션을 추가하여 터미널이 실행 상태에서 자유롭게 유지되도록 합니다.

      docker compose up -d
  5. 이미지 새로 빌드 후 실행

    • docker compose up --build: 기존에 빌드된 이미지가 있더라도 이미지를 새로 빌드한 후 컨테이너를 실행합니다. 코드나 설정이 변경된 후 새로 빌드된 이미지를 적용하고자 할 때 사용합니다.

      docker compose up --build
  6. 이미지 다운로드

    • docker compose pull: Docker Compose 파일에 정의된 이미지를 미리 다운로드합니다. 이미지가 최신 상태인지 확인하거나, 실행 전에 필요한 이미지를 준비할 때 유용합니다.

      docker compose pull
  7. 컨테이너 종료 및 환경 정리

    • docker compose down: 현재 실행 중인 모든 컨테이너를 종료하고, 네트워크와 볼륨도 함께 삭제하여 환경을 정리합니다. 이후 재실행할 준비를 할 수 있습니다.

      docker compose down

Docker Compose 실습 #1

Docker Compose로 Nginx 실행

Docker Compose 파일을 하나 생성한 후 다음과 같이 내용을 작성합니다.

services: # 여러 서비스를 정의하는 시작 부분
  my-web-server: # 서비스 이름. 사용자 지정 이름으로, 여러 서비스가 있을 때 구분하는 데 사용
    container_name: web-server # 컨테이너 이름을 'web-server'로 지정 (이름을 직접 설정하여 관리 용이)
    image: nginx # 'nginx' 이미지를 기반으로 컨테이너 생성 (nginx 웹 서버를 사용)
    ports:
      - 80:80 # 호스트의 80번 포트를 컨테이너의 80번 포트와 연결 (외부에서 접근 가능하게 함)

Docker ComposeYAML(yml) 파일 형식을 사용합니다. YAML(yml) 파일에서는 들여쓰기를 통해 계층 구조를 나타냅니다. 각 들여쓰기는 더 깊은 수준의 정보를 의미하며, 이를 통해 데이터 간의 종속 관계나 구조를 표현할 수 있습니다.
YAML에서 들여쓰기가 중요한 이유는 중괄호나 괄호 없이 들여쓰기를 기준으로만 데이터를 구분하기 때문입니다. 잘못된 들여쓰기는 파일 구문을 인식하지 못하게 하고 오류를 발생시킵니다.
이러한 점을 고려하여 Docker Compose 파일을 작성할 때는 들여쓰기에 주의하도록 합니다.

Docker Compose 실습 #2

Docker Compose로 MySQL 실행

Docker Compose 파일을 하나 생성한 후 다음과 같이 내용을 작성합니다.

services:  # Docker Compose의 서비스 정의를 시작
  my-db:  # MySQL 데이터베이스 서비스 이름 정의
    image: mysql  # MySQL 공식 이미지를 사용
    environment:  # 환경 변수 설정
      MYSQL_ROOT_PASSWORD: pwd1234  # MySQL 루트 사용자 비밀번호 설정
    volumes:  # 호스트와 컨테이너 간의 데이터 공유를 위해 볼륨 설정
      - ./mysql_data:/var/lib/mysql  # 호스트의 ./mysql_data 디렉토리를 MySQL 데이터베이스 파일이 저장되는 경로에 마운트
    ports:  # 포트 매핑 설정
      - 3306:3306  # 호스트의 3306 포트를 컨테이너의 3306 포트에 매핑하여 외부에서 MySQL에 접근 가능하도록 설정

docker compose up 명령어는 도커 컴포즈 파일이 있는 디렉터리에서 실행해야 합니다.
이유는 Docker Compose가 현재 경로에 있는 docker-compose.yml 파일을 찾아서 그 내용을 기준으로 컨테이너를 설정하고 실행하기 때문입니다. 따라서, ls 명령어로 파일이 있는지 확인한 후 해당 경로에서 docker compose up을 실행하면 원하는 설정으로 컨테이너를 시작할 수 있습니다.

Docker Compose 실습 #3

Docker Compose로 백엔드(Spring Boot) 실행

스프링부트 프로젝트를 생성한 후 도커 파일과 도커 컴포즈 파일을 모두 생성하여 작성합니다.

도커 파일의 내용은 다음과 같이 작성합니다.

FROM openjdk:17-jdk

COPY build/libs/*SNAPSHOT.jar /app.jar

EXPOSE 8080

ENTRYPOINT ["java", "-jar", "/app.jar"]

도커 컴포즈 파일의 내용은 다음과 같이 작성합니다.

services:  # Docker Compose 서비스 정의
  my-server:  # Spring Boot 애플리케이션 서버 정의
    build: .  # 현재 디렉터리에 있는 Dockerfile을 기반으로 이미지를 빌드
    ports:  # 포트 매핑 설정
      - 8080:8080  # 호스트의 8080 포트를 컨테이너의 8080 포트에 매핑하여 외부에서 접근 가능하도록 설정

도커 컴포즈 파일 내용 중 build: . 으로 작성하는 이유는 다음과 같습니다.
build: .에서 .현재 디렉터리를 의미합니다. 이 설정은 Docker Compose가 현재 디렉터리에 있는 Dockerfile을 찾아서 그 파일을 기준으로 이미지를 빌드하라는 뜻입니다.
즉, 현재 디렉터리에 Dockerfile이 위치해 있고, 이 파일을 기반으로 my-server 서비스를 빌드하여 실행하기 위해 build: .를 사용한 것입니다.

터미널에서 도커 컴포즈 빌드 명령어는 다음과 같이 작성합니다.

docker compose up -d --build

터미널 실행 명령어에서 --build 부분을 추가한 이유는 다음과 같습니다.
docker compose up -d --build에서 --build 옵션은 컨테이너를 실행하기 전에 이미지를 새로 빌드하라는 뜻입니다.
이 옵션을 추가하는 이유는, Dockerfile이나 프로젝트 파일에 변경 사항이 있을 때 최신 상태로 이미지를 다시 빌드하고 컨테이너를 실행하기 위함입니다.
따라서, --build 옵션을 사용하면 항상 최신 코드와 설정이 반영된 상태에서 컨테이너가 실행됩니다.

결과를 확인해보면, 컨테이너가 잘 실행되고 접속도 잘 되는 것을 확인할 수 있습니다.

Docker CLI ↔ Docker Compose 쉽게 작성하기

Docker CLI 명령어와 compose.yml 파일은 서로 변환이 가능합니다. 이를 통해 CLI로 작성된 명령어를 compose.yml 파일로, 또는 compose.yml 파일 내용을 CLI로 쉽게 변환할 수 있습니다. 이를 편리하게 변환해주는 웹사이트는 다음과 같습니다:

  • Docker CLI → compose.yml로 변환하기

    • 사이트: Composerize
    • 설명: Docker CLI 명령어를 입력하면, docker-compose.yml 형식으로 자동 변환해 줍니다.
  • compose.yml → Docker CLI로 변환하기

    • 사이트: Decomposerize
    • 설명: docker-compose.yml 파일의 내용을 CLI 명령어로 변환하여 Docker 명령어 형태로 나타내 줍니다.

이러한 도구를 활용하면 Docker 환경 설정을 더 쉽게 관리할 수 있습니다.

Docker Compose를 활용해 2개 이상의 컨테이너 관리하기

1. Docker Compose로 MySQL, Redis 실행

1-1. Docker Compose 파일을 작성합니다.

services:
  my-db: # MySQL 컨테이너 이름 정의
    image: mysql # 사용할 이미지 설정 - MySQL 공식 이미지
    environment: # 환경 변수 설정
      MYSQL_ROOT_PASSWORD: pwd1234 # MySQL 루트 사용자 비밀번호 설정
    volumes: # 호스트와 컨테이너 간의 데이터 영구 저장소 설정
      - ./mysql_data:/var/lib/mysql # 호스트의 mysql_data 폴더를 컨테이너의 /var/lib/mysql에 마운트
    ports: # 호스트와 컨테이너 간의 포트 매핑
      - 3306:3306 # 호스트의 3306 포트를 컨테이너의 3306 포트에 연결
  my-cache-server: # Redis 컨테이너 이름 정의
    image: redis # 사용할 이미지 설정 - Redis 공식 이미지
    ports: # 호스트와 컨테이너 간의 포트 매핑
      - 6379:6379 # 호스트의 6379 포트를 컨테이너의 6379 포트에 연결

1-2. Docker Compose 파일을 실행하여 결과를 확인합니다.

2. Docker Compose로 Spring Boot, MySQL 실행

2-1. Dockerfile을 작성합니다.

FROM openjdk:17-jdk

COPY build/libs/*SNAPSHOT.jar /app.jar

ENTRYPOINT ["java", "-jar", "/app.jar"]

2-2. Docker Compose 파일을 작성합니다.

services:
  my-server: # Spring Boot 애플리케이션 컨테이너 정의
    build: . # 현재 디렉토리에서 Dockerfile을 사용해 빌드
    ports:
      - 8080:8080 # 호스트의 8080 포트를 컨테이너의 8080 포트에 연결
    depends_on:
      my-db: # my-db 컨테이너가 먼저 실행되어야 함
        condition: service_healthy # my-db가 'healthy' 상태가 된 후에 my-server 시작

  my-db: # MySQL 데이터베이스 컨테이너 정의
    image: mysql # MySQL 공식 이미지 사용
    environment:
      MYSQL_ROOT_PASSWORD: pwd1234 # MySQL 루트 계정 비밀번호 설정
      MYSQL_DATABASE: mydb # 자동 생성할 데이터베이스 이름 설정
    volumes:
      - ./mysql_data:/var/lib/mysql # 호스트의 mysql_data 폴더를 컨테이너의 MySQL 데이터 폴더와 연결
    ports:
      - 3306:3306 # 호스트의 3306 포트를 컨테이너의 3306 포트에 연결
    healthcheck: # 컨테이너 상태 점검 설정
      test: ["CMD", "mysqladmin", "ping"] # MySQL 서버가 실행 중인지 확인
      interval: 5s # 5초마다 상태 확인
      retries: 10 # 최대 10회 재시도

❗️Docker Compose에서 depends_onhealthcheck 명령어를 사용한 이유
depends_onhealthcheck 명령어는 Docker Compose에서 컨테이너 간의 시작 순서와 연결 상태를 관리하기 위해 사용됩니다. 이를 통해 Spring Boot 애플리케이션이 MySQL 데이터베이스가 준비된 상태일 때 실행되도록 보장할 수 있습니다.
이 설정을 통해 Spring Boot 애플리케이션이 MySQL 데이터베이스가 완전히 실행되고 연결 가능한 상태일 때만 시작하게 되어 오류 발생을 방지할 수 있습니다.

  • 데이터베이스 연결 우선 요구: Spring Boot 애플리케이션은 시작할 때 데이터베이스와 연결이 필요합니다. 만약 MySQL이 완전히 실행되지 않은 상태라면 연결에 실패해 애플리케이션이 오류를 발생시킬 수 있습니다.
  • depends_on 명령어: 이 명령어는 my-server 서비스가 my-db 서비스가 먼저 실행되어야 한다는 의존성을 정의합니다. 하지만 Docker Compose v2에서는 단순히 depends_on으로 컨테이너가 준비된 상태임을 보장하지 않기 때문에 healthcheck와 함께 사용해 더 안정적인 실행을 보장할 수 있습니다.
  • healthcheck 명령어: healthcheckmy-db 컨테이너의 상태를 확인해 애플리케이션이 실행 가능한 상태인지 판단하게 합니다. 위의 설정에서는 mysqladmin ping 명령어를 사용해 MySQL 서버가 OK 상태인지 확인합니다. 만약 my-db 서비스가 준비되지 않은 상태라면 my-server는 기다렸다가 데이터베이스가 준비된 후에 실행됩니다.

2-3. Docker Compose 파일을 실행하여 결과를 확인합니다.

결과를 확인해 보면, MySQL 컨테이너는 정상적으로 실행되었으나, 스프링 부트 애플리케이션 컨테이너는 종료된 것을 확인할 수 있습니다.

docker logs [컨테이너 아이디] 명령어로 에러 내용을 확인해 보니, 스프링 부트 애플리케이션이 MySQL 컨테이너에 접근할 수 없어 연결이 거부된 에러가 발생한 것을 확인할 수 있습니다.

2-4. 문제 발생 원인 및 해결

문제 설명

Docker 환경에서 각 컨테이너는 자신만의 네트워크와 IP 주소를 가지고 있습니다. 이를 통해 서로 독립적인 네트워크 공간을 가지며, 동일한 호스트에서 격리된 상태로 동작하게 됩니다.

  1. 호스트 컴퓨터 입장에서의 localhost: 호스트 컴퓨터에서 localhost호스트 컴퓨터 자체를 가리킵니다.
  2. Spring Boot 컨테이너 입장에서의 localhost: Spring Boot가 실행되는 컨테이너 내부에서 localhostSpring Boot 컨테이너 자체를 가리킵니다.

발생한 문제

application.yml 파일에서 MySQL 데이터베이스 URL을 아래와 같이 설정했습니다:

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/mydb

이 설정에서 localhost:3306을 사용하고 있는데, 이는 Spring Boot 컨테이너 내부의 localhost 3306번 포트를 가리킵니다. 그러나 Spring Boot 컨테이너 내부의 3306번 포트에는 MySQL이 실행되고 있지 않기 때문에 MySQL 데이터베이스와 연결할 수 없게 됩니다.

해결 방법

Docker Compose 네트워크를 활용해, Spring Boot 애플리케이션이 MySQL 컨테이너에 접근할 수 있도록 MySQL 서비스의 이름을 사용해야 합니다.

Docker Compose에서 각 컨테이너는 같은 네트워크 내에서 compose.yml 파일에 정의된 서비스 이름을 통해 서로를 식별하고 통신할 수 있습니다. 즉, application.yml 파일에서 localhost 대신 compose.yml에서 정의한 MySQL 서비스 이름(my-db)을 사용하면 Spring Boot와 MySQL이 같은 네트워크 내에서 통신할 수 있게 됩니다.

  • Docker Compose에서 각 컨테이너는 서비스 이름을 호스트명으로 사용할 수 있기 때문에, my-db라는 이름으로 MySQL을 참조할 수 있습니다.
  • 이를 통해 Spring Boot 애플리케이션의 application.yml 파일에서 localhost 대신 my-db를 사용하면 MySQL과 정상적으로 연결됩니다.

Docker Compose의 네트워크 구조

"Docker 환경에서 각 컨테이너는 자신만의 네트워크와 IP 주소를 가지고 있습니다"라는 설명과 "Docker Compose에서 각 컨테이너는 같은 네트워크 내에서 compose.yml 파일에 정의된 서비스 이름을 통해 서로를 식별하고 통신할 수 있습니다"라는 설명은 논리적으로 맞지 않는 것처럼 보일 수 있습니다. 이를 이해하기 위해 Docker Compse의 네트워크 구조에 대해 이해할 필요가 있습니다.
Docker Compose에서 말하는 "같은 네트워크 내"는 Docker Compose가 자동으로 생성하는 가상 네트워크를 의미합니다.

  • Docker Compose에서 docker-compose up을 실행하면, 모든 서비스는 기본적으로 같은 네트워크에 연결됩니다. 이 네트워크는 컨테이너 간의 통신을 위한 가상 네트워크로, Docker가 자동으로 생성합니다.
  • 이 네트워크 안에서 각 컨테이너는 서비스 이름을 호스트명처럼 사용해 다른 컨테이너에 접근할 수 있게 됩니다.

네트워크와 독립성의 차이

  1. 각 컨테이너의 독립성: 컨테이너는 각각 독립된 IP 주소와 네임스페이스를 가지고 실행됩니다. 따라서 하나의 컨테이너 내부에서 localhost는 그 컨테이너 자체를 가리킵니다.
  2. 공유된 네트워크: Docker Compose는 compose.yml에 정의된 서비스들끼리 자동으로 같은 가상 네트워크에 연결시킵니다. 이 가상 네트워크에서는 컨테이너 간에 서로의 서비스 이름을 DNS 호스트명으로 사용하여 접근할 수 있습니다.

예시

compose.yml에서 두 서비스 my-servermy-db를 정의하면 Docker Compose는 다음과 같은 작업을 수행합니다:

  • 두 컨테이너에 각각 고유한 IP 주소를 할당하지만, 이들을 같은 가상 네트워크에 연결합니다.
  • my-server에서 my-db라는 서비스 이름을 사용해 my-db 컨테이너에 접근할 수 있도록 자동으로 DNS 해석을 제공합니다.

이를 통해 각 컨테이너는 자신만의 환경과 IP를 가지면서도, Docker Compose의 네트워크 내에서 지정된 서비스 이름으로 서로 통신할 수 있게 됩니다.

따라서 Docker Compose의 같은 네트워크는 Docker가 제공하는 컨테이너 간 통신을 위한 네트워크 공간을 의미하며, 이 안에서만 서로의 서비스 이름으로 통신이 가능합니다.

수정된 application.yml 예시:

spring:
  datasource:
    url: jdbc:mysql://my-db:3306/mydb
    username: root
    password: pwd1234
    driver-class-name: com.mysql.cj.jdbc.Driver
  • my-db는 Docker Compose에서 설정한 MySQL 컨테이너의 서비스 이름입니다.
  • 이를 통해 Spring Boot 컨테이너가 같은 네트워크 상에 있는 MySQL 컨테이너에 접근할 수 있게 되어 연결 오류가 해결됩니다.

Docker Compose에서의 DNS와 네트워크 이해하기

DNS에 대한 설명

1. DNS란?

DNS(Domain Name System)는 웹사이트 주소(도메인 이름)를 IP 주소로 변환해 주는 시스템입니다. 예를 들어, 우리가 google.com 같은 주소로 웹사이트에 접속할 때, DNS는 google.com을 실제 서버의 IP 주소(예: 142.250.190.46)로 변환해 줍니다.

  • 왜 필요한가요?
    사람들은 IP 주소(숫자)를 기억하기 어렵기 때문에, DNS가 우리가 기억하기 쉬운 이름(도메인)을 사용해 인터넷에 접속할 수 있도록 도와줍니다.
  • 어떻게 작동하나요?
    우리가 웹사이트 주소를 입력하면, DNS가 이 이름을 서버의 IP 주소로 바꿔주고, 브라우저는 이 IP 주소를 통해 웹 서버에 연결합니다.

2. DNS 해석이란?

DNS 해석(DNS resolution)은 입력된 도메인 이름을 해당하는 IP 주소로 변환하는 과정을 뜻합니다. 쉽게 말해, DNS 서버가 이름을 IP 주소로 "해석"하는 작업입니다.

  1. 도메인 입력: 우리가 google.com을 브라우저에 입력합니다.
  2. 해석 과정: DNS 서버는 google.com이 연결되어 있는 IP 주소를 찾아서 브라우저에 알려줍니다.
  3. 연결 완료: 브라우저가 IP 주소를 사용해 구글 서버에 연결하고 웹 페이지를 보여줍니다.

이 과정을 통해 우리는 도메인 이름으로 쉽게 원하는 웹사이트에 접속할 수 있습니다.

3. DNS 호스트명이란?

호스트명은 특정 서버나 컴퓨터를 식별하기 위해 붙인 이름입니다. DNS 호스트명은 DNS에서 IP 주소 대신 특정 장치나 서버를 가리킬 때 사용하는 이름입니다.

  • 예를 들어, 회사 네트워크에서 "mail-server"라는 호스트명을 가지고 있는 서버가 있다고 가정해 봅시다. 이 호스트명을 통해 회사의 다른 컴퓨터들이 mail-server라는 이름으로 이 서버에 접근할 수 있게 됩니다.
  • Docker Compose의 경우도 비슷한 방식으로 작동합니다. Docker Compose가 생성한 네트워크에서는 각 컨테이너 서비스가 호스트명처럼 사용되는 서비스 이름을 가지고 있어서, 다른 컨테이너들이 이 이름으로 해당 컨테이너에 접근할 수 있습니다.

DNS 요약

  • DNS: 사람들이 기억하기 쉬운 이름을 IP 주소로 변환해주는 시스템입니다.
  • DNS 해석: 도메인 이름을 입력했을 때 이름을 IP 주소로 변환해주는 과정입니다.
  • DNS 호스트명: 특정 서버나 장치를 식별하기 위한 별칭 같은 이름입니다. 이를 통해 같은 네트워크 내에서 이름을 통해 특정 장치나 서버에 연결할 수 있습니다.
profile
donggyun_ee

0개의 댓글