- 여러 개의 Docker 컨테이너를 정의하고, 함께 실행할 수 있도록 도와주는 도구
- 주로 마이크로서비스 아키텍처나 개발 환경 구축에서 유용하게 사용됨
- 여러 서비스를 한 번에 띄울 수 있음
- 개발 환경 자동화에 용이
- Docker 명령어보다 간결하고 가독성 좋음
- 설정 파일로 구성 관리 (버전 관리도 쉬움)
- 설정은 YAML 파일(
docker-compose.yml
)에 작성
docker
명령어 여러 개를 하나로 통합할 수 있음
- 같은 컴퓨터여야 결합이 가능하다 - pc가 다르면 결합이 안돔
- 배포용이 아니라 로컬에서 연습할 때 - 이걸로 배포 못함
- 로컬 지휘자라고 할 수 있음
실습1
docker-compose
version: '3.8'
services:
my_server:
build:
context: ./server
ports:
- "8080:80"
restart: always
version
→docker-compose
파일의 버전- 버전마다 문법이 조금씩 다르다
- 최신 버전은 문서 확인 필요
docker-compose
는 컨테이너를 하나의 서비스로 본다
services
→ 컨테이너 단위로 정의되는 실행 단위
my_server
→ 이 서비스(컨테이너)의 이름,docker-compose
명령 시 이 이름으로 구분됨.
build: context
→ 이 컨테이너의 이미지를 직접 빌드하겠다는 의미.context: ./server
→./server
폴더 안의Dockerfile
을 기준으로 빌드한다는 뜻images
문법은 사용하지 않고,Dockerfile
을 통해서 하는게 좋다
restart
→ 컨테이너가 꺼지면 자동으로 다시 시작하도록 설정
값 | 의미 |
no (기본값) | 자동 재시작 안 함 |
always | 항상 재시작 |
on-failure | 에러 코드(0이 아님)일 때만 재시작 |
unless-stopped | 사용자가 중지하지 않는 한 항상 재시작 |
docker-compose
를 실행하면,./server
폴더 내에 있는Dockerfile
을 기준으로 이미지를 자동으로 빌드하고, 해당 이미지를 사용해 컨테이너를 자동으로 실행할 수 있다
Dockerfile
FROM httpd
EXPOSE 80
CMD ["httpd-foreground"]
docker-compose 실행
docker-compose up -d

컨테이너 확인
docker ps

접속 확인

종료
docker-compose down

실습2
docker-compose
docker-compose
에서는services
라고 하나 실제로는pods
라고 한다pods
: 하나 이상의 컨테이너
version: "3.8"
services:
my_db:
build:
context: ./db
ports:
- "3306:3306"
restart: always
networks:
- backend_network
my_server:
build:
context: ./server
ports:
- "8080:8080"
restart: always
depends_on:
- my_db
environment:
- RDS_HOSTNAME=my_db
- RDS_PORT=3306
- RDS_USERNAME=ssar
- RDS_PASSWORD=ssar1234
- RDS_DB_NAME=blogdb
networks:
- backend_network
networks:
backend_network:
driver: bridge
frontend_network:
driver: bridge
- 컨테이너가 여러 개 있으면 실행 시점이 중요하다
- db랑 Spring 있으면 db 먼저 안뜨면 Spring이 db 연결할려다가 터짐
- Spring 애플리케이션이 DB에 접속하려면 DB 컨테이너의 IP 주소(즉, 프로세스 위치)를 알아야 함
docker-compose
환경에서는 DB 컨테이너의 IP를 직접 지정하는 대신, 서비스 이름을 호스트명처럼 사용하여 내부 네트워크에서 해당 컨테이너를 찾아서 연결 → 서비스 이름은 Docker Compose가 제공하는 가상 네트워크 상의 DNS 역할을 하며, DB가 재시작되어 IP가 바뀌더라도 자동으로 연결됨depends_on
설정을 통해 Spring 애플리케이션이 DB 컨테이너가 먼저 실행된 이후 시작되도록 보장
environment
: 환경변수- 컨테이너가 뜨고 서버가 실행되기 직전에
docker-compose.yml
에 작성한 환경변수가 등록된다 - 원래는 환경변수가 디스크에 등록 안 되있고, 스프링을 실행하는게 목적, 스프링을 실행하기 직전에 환경변수를 띄운다
- 실행되기 전에는 환경변수가 없는 상태
- Dockerfile에 환경변수를 적는 건 다르다 → 환경변수가 이미지에 귀속됨
- db가 생성될 때 dockerfile에 있는 환경변수를 읽어서 db를 만들기 위함
RDS_HOSTNAME
→ 서비스 명을 적으면 재시작 되면서 갱신된 ip를 알아서 찾음- 서비스명 = 컨테이너 DNS 이름 →
docker-compose
는 같은 네트워크 내의 서비스끼리는 자동으로 DNS로 서로를 인식할 수 있도록 설정됨 my_db
는 서비스명이자 Docker 내부 DNS 이름- 컨테이너 내부에서
blogdb
로 접근하면, 실제mydb
컨테이너의 현재 IP 주소를 자동으로 찾아 연결해 준다 - 즉,
blogdb
가 재시작되어 IP가 바뀌더라도app
은 서비스명(blogdb
)으로 접근하므로 자동으로 갱신된 IP에 연결할 수 있다 docker-compose
에서는 같은 네트워크에 있는 컨테이너끼리는 서비스명을 도메인처럼 사용하여 통신할 수 있다. DB 컨테이너가 재시작되어 IP가 바뀌어도, 서비스명을 통해 자동으로 갱신된 IP로 연결되므로 문제가 없다.
networks
설정을 명시하지 않으면,default
라는 기본 네트워크에 모든 서비스가 연결됨
networks: backend_network: driver: bridge frontend_network: driver: bridge
bridge
는 컨테이너 간 통신을 위한 가상 네트워크 드라이버- 명시하면
backend_network
,frontend_network
두 개의 사용자 정의 네트워크가 생성됨 - 기본(default) 네트워크를 포함하면 총 3개의 네트워크가 생길 수 있음
- docker-compose up → 1번으로 build / depends_on은 실행 시에 적용됨
db/Dockerfile
FROM mysql
COPY init.sql /docker-entrypoint-initdb.d/init.sql
ENV MYSQL_USER=ssar
ENV MYSQL_PASSWORD=ssar1234
ENV MYSQL_ROOT_PASSWORD=root1234
ENV MYSQL_DATABASE=blogdb
CMD ["--character-set-server=utf8mb4", "--collation-server=utf8mb4_unicode_ci"]
EXPOSE 3306
- 이미지 빌드시 임시 컨테이너가 한 번 실행되고, 이후 실제 컨테이너가 실행되므로 결과적으로 컨테이너가 두 번 실행됨
build
과정에서는CMD
실행 안됨CMD
는 이미지 빌드 시점이 아니라 이미지로부터 컨테이너를 실행(run)할 때 동작함docker build
시점에는CMD
는 무시되고,docker run
또는docker-compose up
때 실제로 적용됨
🔍 각 명령어 실행 시점
명령어 | 실행 시점 |
COPY , ENV , RUN | docker build (이미지 빌드 단계) |
CMD , ENTRYPOINT | docker run (컨테이너 실행 시점) |
db/init.sql
use blogdb;
CREATE TABLE IF NOT EXISTS user_tb (
id integer auto_increment,
created_at timestamp,
email varchar(20) not null,
password varchar(60) not null,
username varchar(20) not null unique,
profile varchar(100),
primary key (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
CREATE TABLE IF NOT EXISTS board_tb (
id integer auto_increment,
content varchar(10000),
created_at timestamp,
title varchar(100) not null,
user_id integer,
primary key (id),
constraint fk_board_user_id foreign key (user_id) references user_tb (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
CREATE TABLE IF NOT EXISTS reply_tb (
id integer auto_increment,
comment varchar(100) not null,
created_at timestamp,
board_id integer,
user_id integer,
primary key (id),
constraint fk_reply_board_id foreign key (board_id) references board_tb (id),
constraint fk_reply_user_id foreign key (user_id) references user_tb (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
FLUSH PRIVILEGES;
entrypoint.sh
- Docker 컨테이너가 시작될 때 실행되는 스크립트 파일
- 주로 컨테이너 초기 설정을 자동화할 때 사용
git clone https://github.com/busanuv/blog-last.git
cd blog-last
chmod +x gradlew
./gradlew build
chmod +x build/libs/*.jar
java -jar -Dspring.profiles.active=docker build/libs/*.jar
- Docker 컨테이너가
docker run
또는docker-compose up
으로 실행될 때entrypoint.sh
는 실행 초기 단계에서 자동으로 실행
- 일반적으로 쉘 스크립트이며, 환경 변수 설정, DB 초기화, 파일 복사 등 준비 작업 수행
docker-compose
에서run
은entrypoint.sh
만 실행하고 스크립트는entrypoint.sh
에 쓴다
server/Dockerfile
FROM openjdk:11-jdk-slim
RUN apt-get update
RUN apt-get install -y git
WORKDIR /app
COPY ./entrypoint.sh ./entrypoint.sh
RUN ["chmod", "+x", "entrypoint.sh"]
EXPOSE 8080
ENTRYPOINT ["/bin/bash", "./entrypoint.sh"]
entrypoint.sh
실행을 위한 이미지 빌드 과정 → entrypoint 전까지가 이미지를 build하는 과정임
apt-get update
→ 패키지 목록(메뉴판) 최신화
apt-get install -y git
→ Git 설치 (-y
는 자동으로 yes 응답) /-y
옵션 필수
RUN ["chmod", "+x", "entrypoint.sh"]
→entrypoint.sh
실행 권한 부여
EXPOSE 8080
→ 어떤 포트로 실행될지 알려줌
- 이미지 빌드만 했을 땐 컨테이너는 아직 실행되지 않음
- 여기까지의 프로비저닝은 Docker Compose에서
my_server: build: context: ./server
이 설정에 해당한다
FROM
, RUN
, COPY
, WORKDIR
, EXPOSE
, ENTRYPOINT
등이 모두 실행되며 이미지가 생성됨ENTRYPOINT
는 설정만 되고 아직 실행되지 않음 → 실제 실행은 docker-compose up
할 때 일어남실행
docker-compose up -d




네트워크 확인
docker ps

docker inspect 5e19

- 컨테이너의 전체 정보를 JSON 형식으로 출력
- 컨테이너의 모든 메타데이터를 확인 가능
✅ 주요 확인 가능한 정보 목록
항목 | 설명 |
Id | 컨테이너 전체 ID |
Name | 컨테이너 이름 ( docker-compose 사용 시 프로젝트_서비스_번호 ) |
Image | 컨테이너가 사용한 이미지 ID |
State | 실행 상태 ( running , exited , paused 등), 시작 시간, 종료 코드 등 |
Mounts | 마운트된 볼륨 정보 (호스트 ↔ 컨테이너 연결) |
Config.Env | 환경 변수 ( environment: 로 설정한 값 포함) |
NetworkSettings.Networks | 네트워크 정보 (IP 주소, 게이트웨이, MAC 등) |
Ports | 포트 매핑 정보 (예: 8080:8080 ) |
LogPath | 로그 파일 저장 경로 (Docker 엔진 내부용) |
Path + Args | 컨테이너가 실행한 명령어 ( ENTRYPOINT , CMD ) |

✅ 라우팅 테이블이란?
- 네트워크에서 데이터를 어디로 보낼지 결정하는 길잡이 역할
- 목적지 IP 주소에 따라 어디로 데이터를 보낼지 결정하는 규칙 목록
- 운영체제나 라우터에 존재하는 IP 기반 경로표
- 목적지 IP가 주어졌을 때, 다음 홉(Next Hop) 또는 인터페이스를 결정함
📦 구성 요소 (예시 항목)
항목 | 설명 |
Destination | 목적지 IP 주소 또는 네트워크 대역 |
Gateway | 패킷을 전달할 다음 라우터의 IP (Next Hop) |
Genmask | 서브넷 마스크 (네트워크 범위 결정) |
Interface | 어느 네트워크 인터페이스로 보낼지 (예: eth0, wlan0 등) |
✅ DNS란?
- 도메인 이름을 IP 주소로 변환해주는 시스템
- 사람이 기억하기 쉬운 주소 → 컴퓨터가 이해할 수 있는 주소로 변환
- 인터넷의 주소록 역할을 하는 아주 중요한 시스템
- DNS는 도메인 이름을 IP 주소로 바꿔주는 시스템으로, 인터넷 통신이 가능하도록 도와주는 주소 해석기
📦 동작 원리 요약
- 클라이언트가 도메인을 입력
- 운영체제는 캐시에 해당 IP가 있는지 확인
- 없다면 로컬 DNS 서버(보통 ISP 또는 공용 DNS)로 요청 전송
- 여러 단계의 권한 있는 DNS 서버들을 거쳐 최종 IP 주소 응답
- 클라이언트는 그 IP로 연결
✅ 도커 환경에서의 DNS란?
- 컨테이너 간 통신을 편리하게 하기 위해 Docker가 자체적으로 DNS 기능을 제공
- 컨테이너 이름이나 Compose 서비스 이름을 도메인처럼 사용하여 자동으로 IP를 찾는 기능
🧠 기본 개념
항목 | 설명 |
내부 DNS | Docker는 사용자 정의 브리지 네트워크를 만들면 자동으로 DNS 서버 역할을 수행함 |
서비스명 또는 컨테이너명으로 접근 가능 | my_server , my_db 같은 이름이 내부 DNS 이름으로 작동함 |
IP 주소는 동적으로 할당되지만, 이름은 항상 고정 | ㅤ |
호스트 파일 수정 불필요 | /etc/hosts 수정 없이 이름 기반으로 통신 가능 |
🔧 동작 조건
Docker 내부 DNS 기능이 작동하려면
- 같은 사용자 정의 네트워크에 컨테이너가 있어야 함
networks:
backend_network:
driver: bridge
docker-compose.yml
에서services
아래에 이름 정의:
services:
my_server:
my_db:
→ 이 경우
my_server
컨테이너에서 my_db
로 DNS 이름으로 접속 가능✅ 서비스 디스커버리(Service Discovery)란?

- 네트워크에 있는 서비스의 위치(IP, 포트 등)를 자동으로 찾아주는 시스템
- 마이크로서비스나 분산 시스템에서 동적으로 다른 서비스의 위치(IP/Port 등)를 찾는 메커니즘
- 정적인 IP 주소 대신, 서비스 이름만으로 통신할 수 있게 해준다
🔍 왜 필요한가?
❌ 기존 방식 (정적 설정)
spring.datasource.url=jdbc:mysql://192.168.0.5:3306/blogdb
- IP가 바뀌면 접속 안 됨
- 마이크로서비스가 많아지면 관리가 어려움
✅ 서비스 디스커버리 방식
spring.datasource.url=jdbc:mysql://my_db:3306/blogdb
my_db
는 실제 IP를 몰라도 자동으로 DNS 조회
- IP가 바뀌어도 항상 연결 가능 (동적 바인딩)
🧭 동작 방식
서비스 디스커버리는 보통 2가지 방식으로 나뉜다
방식 | 설명 |
클라이언트 사이드 디스커버리 | 클라이언트가 직접 등록된 서비스 목록을 보고, 서비스 위치를 선택 (예: Netflix Eureka + Ribbon) |
서버 사이드 디스커버리 | 요청이 먼저 로드밸런서(디스커버리 서버)에 가고, 서버가 위치를 찾아줌 (예: Kubernetes, AWS ELB) |
🧠 요약 정리
서비스 디스커버리는 마이크로서비스 간 통신에서 서로의 IP를 고정하지 않고도 이름만으로 통신할 수 있게 해주는 시스템Docker의 서비스명-DNS, Kubernetes의 CoreDNS, Spring의 Eureka, HashiCorp의 Consul 등이 대표적인 방식
Share article