본문 바로가기

DEV_BACKEND/Docker

Docker Compose(도커 컴포즈)

compose 뜻

  1.구성하다 2.작곡하다

 

  • 여러 개의 도커 컨테이너를 한 번에 정의하고 실행하기 위한 도구
  • 도커(Docker)가 '악기 하나(컨테이너 하나)'를 다루는 것이라면, 도커 컴포즈는 '오케스트라 지휘자'처럼 여러 악기가 조화롭게 연주되도록 총괄하는 역할

 

1. Docker Compose의 핵심 개념

일반적으로 현대의 애플리케이션은 하나로만 동작하지 않는다. 예를 들어 웹 서비스 하나를 띄우려면 다음과 같은 요소들이 필요할 수 있다.

  • Web Server (Nginx 등)
  • Backend App (Python, Node.js 등)
  • Database (MySQL, PostgreSQL 등)
  • Cache (Redis 등)

 이 4가지를 그냥 도커 명령어로 실행하려면 docker run 명령어를 아주 길게 4번 입력해야 하고, 서로 통신하기 위해 네트워크 설정도 복잡하게 해줘야 한다.

 Docker Compose는 이 모든 설정을 docker-compose.yml이라는 파일 하나에 적어두고, 명령어 한 번으로 이 모든 컨테이너를 띄우고, 연결하고, 관리한다.

 

2. Docker Compose가 하는 일 (주요 기능)

가장 중요한 역할은 "인프라를 코드로 정의(IaC)하여 자동화하는 것"이다.

1) 다중 컨테이너의 일괄 실행 및 관리

  • 명령어 하나(docker compose up)로 웹 서버, DB, 캐시 서버 등 애플리케이션에 필요한 모든 서비스를 동시에 실행한다.
  • 반대로 docker compose down 한 번으로 관련된 모든 컨테이너, 네트워크, 볼륨을 정리(삭제)할 수 있다.

2) 서비스 간의 네트워크 연결 자동화

  • 도커 컴포즈로 실행된 컨테이너들은 자동으로 하나의 네트워크에 묶인다.
  • 복잡한 IP 주소 대신 서비스 이름(예: db, web)만으로 서로 통신할 수 있게 해준다.
    • 예: 웹 서버 설정 파일에서 DB 주소를 적을 때 IP 대신 db라고만 적으면 연결된다.

3) 실행 환경의 표준화 (일관성 유지)

  • 개발자 A의 컴퓨터, 개발자 B의 컴퓨터, 그리고 테스트 서버 등 어디서든 docker-compose.yml 파일만 있으면 똑같은 환경을 1초 만에 구축할 수 있다. "내 컴퓨터에서는 되는데?"라는 문제가 사라진다.

4) 컨테이너 의존성 관리

  • "DB가 먼저 켜진 다음에 백엔드 앱이 켜져야 한다"와 같은 순서나 의존 관계(depends_on)를 설정할 수 있다.

3. 동작 원리 (3단계 워크플로우)

Docker Compose를 사용하는 과정은 보통 다음 3단계로 이루어진다.

  1. Dockerfile 정의: 각 앱(컨테이너)의 이미지를 어떻게 만들지 정의한다.
  2. docker-compose.yml 작성: 서비스들을 어떻게 엮을지, 포트는 몇 번을 쓸지, 저장소(Volume)는 어디로 할지 정의한다.
  3. 실행: docker compose up 명령어로 전체 시스템을 가동한다.

간단한 docker-compose.yml 예시:

version: '3.8'
services:
  web: # 웹 서버 서비스
    image: nginx
    ports:
      - "80:80"
  database: # 데이터베이스 서비스
    image: mysql
    environment:
      MYSQL_ROOT_PASSWORD: password

4. 요약 : 왜 써야 하는가

구분 Docker만 쓸 때 (docker run) Docker Compose를 쓸 때
실행 방법 긴 명령어를 컨테이너 개수만큼 입력 docker compose up 한 번
네트워크 일일이 네트워크 생성 및 연결 필요 자동 연결 (서비스 이름으로 통신)
관리 편의성 옵션을 기억하거나 스크립트로 관리해야 함 YAML 파일로 문서화되어 관리 용이
용도 단일 컨테이너 실행 테스트 실제 개발 환경 구축, 다중 컨테이너 앱

 

  • docker-compose.yml 예시
services:
  mysql:
    image: mysql:8.0
    container_name: my-service-mysql
    restart: unless-stopped
    environment:
      MYSQL_ROOT_PASSWORD: "your_root_password"
      MYSQL_DATABASE: "your_database_name"
      MYSQL_USER: "your_username"
      MYSQL_PASSWORD: "your_password"
    ports:
      - "3306:3306"
    command: ["--default-authentication-plugin=mysql_native_password"]
    volumes:
      - dbdata:/var/lib/mysql
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 10s
      timeout: 5s
      retries: 5
volumes:
  dbdata:

 


 

처음 내가 겪었던 상황 : AWS에 도커로 서비스를 띄웠는데, 도커  v2버전의 명령이 안 먹었고 v1버전의 명령만 인식이 됐었다.(그래서 도커 컴포즈 버전이 v2가 아닌 v1로 설치가 된줄 알았다.)

 

추후 알게됐던 정확한 내 상황 : EC2에는 Docker Compose v2가 standalone 바이너리(/usr/local/bin/docker-compose)로만 설치되어 있고 Docker CLI 플러그인으로 등록되어 있지 않아서, docker compose 명령은 동작하지 않고 docker-compose 명령만 사용할 수 있는 상태였다.

 

내부 상황 : 하지만 실제로는 V2의 통합 엔진이 사용되고 있었다.

Docker Compose V2가 설치될 때, V1 명령어 형식(docker-compose)을 V2 실행 파일로 연결하는 심볼릭 링크가 같이 생성됨.

따라서 사용자가 레거시 명령을 입력하더라도 시스템은 그 링크를 따라 실제로는 V2의 통합 엔진을 구동하게 된다.

 

원인 : Compose v2를 docker-compose라는 옛날 엔트리(진입점)로 사용해서 배포됐다. 따라서 설치된 Compose V2의 엔진은 레거시 명령 호출 방식인 docker-compose라는 옛날 엔트리(Entrypoint)를 통해서만 실행되었고, 이로 인해 의도와 달리 v1 버전의 애플리케이션 이미지가 배포되는 결과를 낳았다. 설치 경로 때문에 docker compose 서브커맨드를 못 찾는 상태였던 것.

 

docker compose는 “docker라는 프로그램의 서브커맨드”라서 도커가 정해둔 특정 폴더 에 있어야 하고,
docker-compose는 그냥 “독립 실행파일”이라서 PATH에만 있으면 된다.
이 차이 때문에 설치 경로가 크게 영향을 준 것.

 

좀 더 파고 들어가면 복잡한데

터미널에서 docker-compose up을 치면,

  1. 쉘이 PATH에 있는 실행파일 이름을 찾음
  2. /usr/local/bin/docker-compose 발견 → 이 파일 실행
  3. 이 프로그램이 Docker API를 호출해서 컨테이너 띄움

여기서는 “이게 도커 플러그인인지, v1인지 v2인지” 같은 건 쉘 입장에선 전혀 상관없고, 그냥 “이름이 docker-compose인 실행파일 하나”일 뿐이다.

 

반대로 docker compose up을 치면,

  1. 쉘은 먼저 이름이 docker인 실행파일만 찾음 → /usr/bin/docker 같은 거
  2. 그 다음은 docker 프로그램 내부 세계:
    • docker가 argv를 보고:
      docker [compose] [up]
    • “compose라는 서브커맨드를 내가 아냐?” 검사

여기서부터는 docker 프로그램이 자기 규칙으로 결정:

  • 내장 서브커맨드인가?
  • 아니면 cli-plugin 폴더에 있는 docker-compose 플러그인인가?

만약 여기서 못 찾으면 docker: 'compose' is not a docker command. (정확히 이렇게 떴음)

 

즉,

  • docker-compose → “쉘이 PATH에서 찾은 독립 실행파일”
  • docker compose → “docker 프로그램이 인식한 서브커맨드(+플러그인)”

라서 경로 규칙이 완전히 다름.

 

사실 도커 엔진의 작동방식을 세세하게 알아둘 필요는 없고

 

요약하자면

 

Docker Compose v2 바이너리가 /usr/local/bin/docker-compose 에 독립 실행파일 방식으로만 설치되어 있고
Docker가 찾는 CLI 플러그인 디렉터리(…/cli-plugins/) 에는 없어서
쉘 레벨에서 호출하는 docker-compose 는 잘 동작하지만,
Docker 내부 서브커맨드인 docker compose 는 플러그인을 못 찾아서 “없는 명령”이 되어버린 상태였다.

 

그래서 “버전이 옛날이라 안 됐다”가 아니라, “패키징/설치 방식 + 경로가 달라서” 이런 현상이 나온 것.

 


진짜 원인 :  도커는 정상으로 설치했는데, Compose v2는 ‘플러그인 방식’이 아니라 예전식 docker-compose 단독 바이너리로만 설치돼서 docker compose가 안 됐다. 진짜진짜 원인으로는 테라폼에서 main.tf 구성을 어떻게 해야하는지 아예 모르는 상황 이었기 때문에 예전에 강사님이 주셨던 자료를 바탕으로 tf설정을 복붙해왔는데

EC2가 처음 부팅될 때 user_data 스크립트가:

  1. yum install docker -y
    → 도커 설치
  2. curl ... -o /usr/local/bin/docker-compose
    → GitHub에서 Compose v2 바이너리 하나를 받아서
    /usr/local/bin/docker-compose 에 그냥 떨궈 버림
  3. chmod +x /usr/local/bin/docker-compose
    → 실행 권한 부여 → docker-compose 명령어로 사용 가능

이런 방식으로 구성이 되어버렸고

 

한줄로 요약하자면, 

 

Terraform main.tf 의 user_data에서
Docker Compose v2를 /usr/local/bin/docker-compose 에 standalone 방식으로만 설치해서,
Docker CLI 플러그인 경로에는 등록이 안 되었고
그 결과 docker-compose는 되지만 docker compose는 실패하는 상태가 되었다.

 

다시 말하면, 

도커는 정상으로 설치됐는데, Docker Compose v2를 CLI 플러그인 방식이 아니라
/usr/local/bin/docker-compose에 두는 standalone 방식으로만 설치해서
docker compose 서브커맨드가 인식되지 않았다.

 

이렇게 정확한 원인 정리가 되었다.

(참고. Standalone = 옛날 v1이 아니라, v2도 standalone으로 설치될 수 있다)
(이번 케이스는 v2를 옛날 설치 방식(stanalone)으로만 넣어둔 상태)

 

main.tf 중 일부

# 기존 코드
locals {
  ec2_user_data_base = <<-END_OF_FILE
#!/bin/bash
yum install docker -y
systemctl enable docker
systemctl start docker

curl -L https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose

# 여기 두 줄 추가하여 문제 해결
mkdir -p /usr/lib/docker/cli-plugins
ln -sf /usr/local/bin/docker-compose /usr/lib/docker/cli-plugins/docker-compose

새로운 디렉토리(폴더)를 생성하고 파일에 대한 링크를 생성해주는 명령어다.
이렇게 추가 되면 이 서버 기준으로 docker-compose / docker compose 둘 다 쓸 수 있게된다.

새로 뜨는 EC2 인스턴스에서는 docker compose도 바로 동작함.

 

ssh에서 추후 입력해줘도 동일하게 적용된다.

 

더 깔끔하게 가고 싶으면(정석, CLI 플러그인 방식):

  • 아예 /usr/local/bin 다운로드 삭제
  • cli-plugins 디렉터리에만 받아서 쓰면 됨
# 기존
curl -L https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose

# 변경(대체)
mkdir -p /usr/lib/docker/cli-plugins
curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" \
  -o /usr/lib/docker/cli-plugins/docker-compose
chmod +x /usr/lib/docker/cli-plugins/docker-compose

 

지금 기준에서는 굳이 standalone 방식을 새로 도입해서 쓸 일은 거의 없지만,
예전 자료나 블로그에서 curl ... /usr/local/bin/docker-compose 패턴(레거시)이 여전히 많이 나오기 때문에

 

 

추후에는 무언가를 보고 따라하게될 때,

curl ... /usr/local/bin/docker-compose 만 나와 있으면 → “아, 이건 standalone 예제구나” 라고 인지

가능하면 그 바이너리를 플러그인 위치에 두는 식으로 바꿔서 적용하면 된다.

 

요즘은 위와 같이 최신 방식인 CLI 플러그인(표준 v2)을 사용하면 된다.

 

 

 

다음엔 테라폼에 대해서 공부를 해봐야겠다.

'DEV_BACKEND > Docker' 카테고리의 다른 글

Docker(도커) 개념 정리.  (0) 2025.03.27