[기본편] Docker
- [기초편] Docker
- [기본편] Docker
- [심화편] 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 |
|---|---|
--name | container_name |
-e | environment |
-p | ports |
--image | image |
depends_on은 실행 순서를 지정한다. app은 mysql이 먼저 실행된 다음에 뜬다.
자주 쓰는 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 Volume | Docker가 관리, 경로 신경 안 써도 됨 | 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 안의 컨테이너는 이름으로 통신 가능 |
| 볼륨 | 컨테이너가 삭제돼도 데이터를 호스트에 보존 |
다음 편에서는 이미지 크기 최적화, 멀티스테이지 빌드, 프로덕션 환경 운영 방법을 다룰 예정이다.