포스트

[기본편] Docker

[기본편] Docker
시리즈 [Docker] 개념정리 및 실습 3편
  1. [기초편] Docker
  2. [기본편] Docker
  3. [심화편] Docker

기초편에서 컨테이너 하나를 띄우는 방법을 알아봤다.
이번엔 실제 서비스처럼 여러 컨테이너를 함께 운영하는 방법을 알아본다.


Docker Compose

왜 필요한가?

기초편에서 MySQL 컨테이너를 띄울 때 이렇게 했다.

1
2
3
4
5
6
docker run -d \
  --name my-mysql \
  -e MYSQL_ROOT_PASSWORD=1234 \
  -e MYSQL_DATABASE=testdb \
  -p 3306:3306 \
  mysql:8.0

Spring Boot 컨테이너도 따로 띄워야 하고, Redis가 추가되면 또 따로 띄워야 한다.
MySQL 따로, Spring Boot 따로, Redis까지 붙이기 시작하면 명령어 치다가 진짜 현타 온다.

이러한 문제를 해결하기 위해 등장한게 Docker Compose이다.

“여러 컨테이너를 하나의 파일로 정의하고, 명령어 하나로 한 번에 관리”

docker-compose.yml 작성

프로젝트 루트에 docker-compose.yml 파일을 만든다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
services:
  mysql:
    image: mysql:8.0
    container_name: my-mysql
    environment:
      MYSQL_ROOT_PASSWORD: 1234
      MYSQL_DATABASE: testdb
    ports:
      - "3306:3306"

  app:
    build: .
    container_name: my-app
    ports:
      - "8080:8080"
    depends_on:
      - mysql

docker run 명령어에서 쓰던 옵션들이 그대로 들어간다.

docker run 옵션docker-compose.yml
--namecontainer_name
-eenvironment
-pports
--imageimage

depends_on은 실행 순서를 지정한다. appmysql이 먼저 실행된 다음에 뜬다.

자주 쓰는 Compose 명령어

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 컨테이너 전체 시작 (백그라운드)
docker compose up -d

# 컨테이너 전체 종료
docker compose down

# 로그 확인
docker compose logs -f

# 특정 컨테이너 로그만 보기
docker compose logs -f app

# 컨테이너 상태 확인
docker compose ps

docker compose up -d 하나로 docker-compose.yml에 정의된 컨테이너가 전부 뜬다.


네트워크: 컨테이너끼리 어떻게 통신할까?

문제: localhost가 안 된다

다음과 같이 DB를 설정하면 로컬 환경에서는 잘 작동하지만, 컨테이너로 실행하는 순간 연결이 실패한다.

1
spring.datasource.url=jdbc:mysql://localhost:3306/testdb  # 위험

왜 실패할까?

컨테이너 안에서 localhost컨테이너 자기 자신을 가리킨다.
그런데 MySQL은 다른 컨테이너에서 실행 중이다.
결국, 존재하지 않는 DB에 접속하려고 시도하는 것이다.

1
2
3
4
[ app 컨테이너 ]           [ mysql 컨테이너 ]
localhost = 나 자신         172.17.0.3:3306
     ↓
연결 실패

Docker Compose의 기본 네트워크

Docker Compose로 컨테이너를 띄우면 자동으로 같은 네트워크에 묶인다.
같은 네트워크 안에서는 컨테이너 이름으로 서로를 찾을 수 있다.

1
2
3
4
[ app 컨테이너 ]           [ mysql 컨테이너 ]
  "mysql:3306"    ────→    172.17.0.3:3306
       ↓
  연결 성공 

Docker가 mysql 이라는 이름을 해당 컨테이너의 IP로 자동으로 변환해준다.

application.properties 수정

1
2
3
4
5
# 변경 전
spring.datasource.url=jdbc:mysql://localhost:3306/testdb

# 변경 후
spring.datasource.url=jdbc:mysql://mysql:3306/testdb

localhost 대신 docker-compose.yml에 정의한 서비스 이름(mysql)을 쓰면 된다.

네트워크를 직접 정의하는 경우

Docker Compose를 쓰면 자동으로 네트워크가 만들어지지만, 명시적으로 정의할 수도 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
services:
  mysql:
    image: mysql:8.0
    networks:
      - backend-network

  app:
    build: .
    networks:
      - backend-network

networks:
  backend-network:

같은 networks에 속한 컨테이너끼리만 통신할 수 있다. 서비스가 많아질 때 네트워크를 분리해서 통신 범위를 제한하는 식으로 활용할 수 있다.


볼륨: 데이터를 영구적으로 보존하기

문제: 컨테이너를 지우면 데이터도 사라진다

컨테이너는 삭제하면 그 안의 데이터도 전부 사라진다.

1
2
docker compose down    # MySQL 컨테이너 종료 + 삭제
docker compose up -d   # 다시 띄우면 DB가 텅 비어있음

분명 데이터를 넣어놨는데, 컨테이너를 다시 띄우니까 다 날아가 있다.

MySQL 데이터 업로드 파일처럼 사라지면 안되는 데이터는 컨테이너 밖에 따로 보관해야 한다.
그래서 사용하는 게 볼륨이다.

“볼륨: 컨테이너 밖에 따로 저장하는 공간”

볼륨 설정

1
2
3
4
5
6
7
8
9
10
11
services:
  mysql:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: 1234
      MYSQL_DATABASE: testdb
    volumes:
      - mysql-data:/var/lib/mysql   # (1)

volumes:
  mysql-data:                       # (2)

(1) mysql-data 볼륨을 컨테이너의 /var/lib/mysql 경로에 연결한다. MySQL은 이 경로에 데이터를 저장하는데, 볼륨을 연결하면 실제 데이터는 호스트에 저장된다.

(2) 사용할 볼륨을 선언한다. Docker가 자동으로 관리하는 Named Volume 방식이다.

이렇게 하면 컨테이너를 삭제하고 다시 띄워도 데이터가 그대로 남아있다.


바인드 마운트: 호스트 디렉토리 직접 연결

볼륨 대신 호스트의 특정 경로를 직접 연결할 수도 있다.

1
2
volumes:
  - ./data/mysql:/var/lib/mysql   # 호스트경로:컨테이너경로

개발할 때 설정 파일이나 로그를 로컬에서 바로 확인하고 싶을 때 유용하다.

방식특징용도
Named VolumeDocker가 관리, 경로 신경 안 써도 됨DB 데이터 영구 보존
바인드 마운트호스트 경로 직접 지정개발 중 파일 공유, 로그 확인

전체 구성 예시

Spring Boot + MySQL + Redis를 한 파일로 정의하면 이렇다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
services:
  mysql:
    image: mysql:8.0
    container_name: my-mysql
    environment:
      MYSQL_ROOT_PASSWORD: 1234
      MYSQL_DATABASE: testdb
    volumes:
      - mysql-data:/var/lib/mysql
    networks:
      - backend-network

  redis:
    image: redis:7
    container_name: my-redis
    networks:
      - backend-network

  app:
    build: .
    container_name: my-app
    ports:
      - "8080:8080"
    depends_on:
      - mysql
      - redis
    networks:
      - backend-network

networks:
  backend-network:

volumes:
  mysql-data:

docker compose up -d 한 번으로 세 컨테이너가 같은 네트워크에 묶여서 뜬다.
application.properties에서 mysql, redis 이름으로 각 컨테이너에 연결할 수 있다.


정리

개념한 줄 요약
Docker Compose여러 컨테이너를 하나의 파일로 정의하고 관리
네트워크같은 Compose 안의 컨테이너는 이름으로 통신 가능
볼륨컨테이너가 삭제돼도 데이터를 호스트에 보존

다음 편에서는 이미지 크기 최적화, 멀티스테이지 빌드, 프로덕션 환경 운영 방법을 다룰 예정이다.

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.