Docker Compose로 여러 도커 컨테이너 실행 및 최적화 방법
Docker Compose 기능을 이용하면, 여러 컨테이너를 하나로 묶어서 실행하고 관리할 수 있습니다.
멀티 컨테이너 별도 구성 방법
도커 네트워크 생성
docker network create 도커네트워크명
위 명령어를 실행하여 도커 네트워크를 생성합니다.
MySQL 데이터베이스 컨테이너 실행
docker run -d \
--network 도커네트워크명 --network-alias mysql \
-v 호스트도커볼륨명:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=패스워드 \
-e MYSQL_DATABASE=DB명 \
mysql:8.0
로컬에 mysql:8.0 이미지가 없으면 도커 허브 등 컨테이너 레지스트리에서 이미지를 pull 받습니다.
생성한 네트워크에 연결된 MySQL 컨테이너를 실행하고, 네트워크 내 컨테이너 별칭을 mysql으로 설정합니다.
호스트 도커 볼륨을 MySQL 컨테이너 내부 데이터 저장 경로 /var/lib/mysql에 마운트합니다.
MySQL 컨테이너 접속
docker exec -it MySQL컨테이너ID mysql -u root -p
또는
docker exec -it MySQL컨테이너ID mysql -u root -p DB명
실행 중인 MySQL 컨테이너 내부에서 MySQL 클라이언트를 실행하여 root 계정으로 접속합니다.
MySQL 컨테이너 실행 시 옵션으로 입력한 패스워드를 입력하면 MySQL 서버에 접속할 수 있습니다.
데이터베이스 목록 확인 쿼리
SHOW DATABASES;
MySQL 서버에 생성되어 있는 데이터베이스 목록을 확인할 수 있습니다.
DB 내부 테이블 데이터 조회 쿼리
SELECT * FROM 테이블명;
MySQL 컨테이너의 데이터 저장 경로에 호스트의 Docker 볼륨을 마운트하면,
컨테이너를 삭제하더라도 데이터들을 유지할 수 있습니다.
네트워크 진단용 컨테이너 실행
docker run -it --network 도커네트워크명 nicolaka/netshoot
도커/쿠버네티스 네트워크 문제를 진단하기 위한 도구들이 들어있는 이미지를 내려받아,
같은 도커 네트워크에 연결된 신규 컨테이너를 실행하고 접속합니다.
DNS 조회 명령어
dig mysql
ANSWER SECTION을 확인하면, mysql이라는 네트워크 별칭이 해석되는 컨테이너 IP 주소를 확인할 수 있습니다.
컨테이너 접속 종료
exit
또는 Ctrl + D를 사용하면 컨테이너 셸에서 빠져나올 수 있습니다.
MySQL 컨테이너를 사용하는 신규 앱 컨테이너 실행
docker run -dp 127.0.0.1:3000:3000 \
-w /app -v "$(pwd):/app" \
--network 도커네트워크명 \
-e MYSQL_HOST=mysql \
-e MYSQL_USER=root \
-e MYSQL_PASSWORD=secret \
-e MYSQL_DB=todos \
node:18-alpine \
sh -c "yarn install && yarn run dev"
node:18-alpine 이미지 기반으로 같은 도커 네트워크에 연결된 신규 앱 컨테이너를 실행합니다.
MySQL 접속 정보를 환경 변수로 전달하여 MySQL 컨테이너 연결에 이용합니다.
도커 컴포즈로 멀티 컨테이너 구성 방법
도커 컴포즈 파일 생성
services:
app:
image: node:18-alpine
command: sh -c "yarn install && yarn run dev"
ports:
- 127.0.0.1:3000:3000
working_dir: /app
volumes:
- ./:/app
environment:
MYSQL_HOST: mysql
MYSQL_USER: root
MYSQL_PASSWORD: 패스워드
MYSQL_DB: DB명
mysql:
image: mysql:8.0
volumes:
- 호스트도커볼륨명:/var/lib/mysql
environment:
MYSQL_ROOT_PASSWORD: 패스워드
MYSQL_DATABASE: DB명
volumes:
호스트도커볼륨명:
프로젝트 경로에 compose.yaml 파일을 생성하고 각 컨테이너 정보를 명세합니다.
volumes에 정의한 도커 볼륨은 도커 컴포즈 실행 시 자동으로 생성됩니다.
별도로 networks:를 작성하지 않아도 도커 컴포즈가 기본 네트워크를 자동으로 생성하고
app, mysql 서비스를 같은 네트워크에 연결하여 서비스명을 호스트명으로 사용할 수 있습니다.
도커 컴포즈 실행
docker compose up -d
현재 경로의 도커 컴포즈 파일을 기반으로 필요한 네트워크와 볼륨을 준비하고,
각 서비스 컨테이너를 생성하여 백그라운드에서 실행합니다.
도커 컴포즈 로그 확인 방법
docker compose logs -f
MySQL 서버가 실행되면 Unix 소켓 파일이 생성되며, 로그에서 소켓 파일 경로를 확인할 수 있습니다.
같은 컨테이너 내부에서는 파일 경로를 이용한 Unix 소켓 방식으로 통신도 가능합니다.
이미지 빌드 최적화 방법
도커 이미지 레이어 이력 확인
docker image history 도커이미지명
도커 이미지의 레이어 생성 이력을 최신 레이어순으로 확인할 수 있습니다.
각 레이어 생성 명령을 파악하여, 어떤 빌드 단계에서 이미지 용량이 증가했는지 알 수 있습니다.
레이어 캐싱
도커는 이미지 빌드 시 이전에 생성된 레이어를 캐시로 재사용하여 빌드 시간을 단축할 수 있습니다.
동일한 빌드 단계의 캐시가 존재하면 캐시 히트가 발생하여 재사용하고,
캐시를 사용할 수 없는 단계부터 이후 단계를 실행하여 새로운 레이어를 생성합니다.
기존 Dockerfile
# syntax=docker/dockerfile:1
FROM node:lts-alpine
WORKDIR /app
COPY . .
RUN yarn install --production
CMD ["node", "src/index.js"]
기존 빌드 구조는 소스 코드 파일 하나만 바뀌어도 COPY . . 레이어가 변경되고,
그 다음 단계인 yarn install도 캐시를 재사용하지 못할 수 있습니다.
레이어 캐시를 효율적으로 활용하도록 개선한 Dockerfile
# syntax=docker/dockerfile:1
FROM node:lts-alpine
WORKDIR /app
COPY package.json yarn.lock ./
RUN yarn install --production
COPY . .
CMD ["node", "src/index.js"]
package.json 또는 yarn.lock이 그대로면 yarn install 단계의 캐시를 재사용합니다.
소스 코드만 변경되면 이후 COPY . . 단계부터 다시 처리됩니다.
도커 이미지 빌드
docker build -t 도커이미지명 .
개선한 Dockerfile이 적용되면 의존성 파일이 변경되지 않은 경우
yarn install 단계의 캐시를 재사용하므로, 빌드 시간이 훨씬 단축됩니다.
멀티 스테이지 빌드 예시
# 1단계 (빌드용 이미지)
FROM node:18 AS build
WORKDIR /app
COPY package*.json ./
# 개발 의존성까지 모두 설치
RUN npm install
COPY . .
# 빌드
RUN npm run build
# 2단계 (실행용 가벼운 이미지)
FROM node:18-alpine
WORKDIR /app
# 실행에 필요한 의존성 설치
COPY package*.json ./
RUN npm install --omit=dev
# 1단계 빌드 결과물만 2단계로 가져오기
COPY --from=build /app/dist ./dist
# 실행
CMD ["node", "dist/index.js"]
하나의 Dockerfile을 여러 개의 빌드 단계로 나누어 구성하는 방식입니다.
각 빌드 스테이지는 독립적인 이미지 파일시스템을 가지며,
앞 단계 결과물 중 최종 실행에 필요한 파일만 다음 단계로 복사하여 최종 이미지 크기를 줄일 수 있습니다.
Spring은 Maven 또는 Gradle을 이용해 빌드한 결과물을 Tomcat 기반으로 실행하고,
React는 Node.js로 빌드한 정적 파일을 Nginx를 통해 제공할 수 있습니다.