이번 글에서는 드디어 docker compose를 다루게 되었습니다. 도커를 사용하면 사실상 compose를 사용하는게 당연하게 생각 되듯이 널리 사용되고 있습니다. 이번 글에서 기본적인 사용법과 실습을 해보면서 익숙해 지도록 해봅시다.
멀티 컨테이너 서비스 구성
[실습] wordpress와 mysql을 이용한 web application
cd ~/fastcampus/ch10/my-webdb
docker volume create mydb_data
docker volume create myweb_data
docker network create my-webdb-net
docker run -itd --name=mysql_app -v mydb_data:/var/lib/mysql --restart=always -p 3306:3306 --net=my-webdb-net -e MYSQL_ROOT_PASSWORD=password# -e MYSQL_DATABASE=wpdb -e MYSQL_USER=wpuser -e MYSQL_PASSWORD=wppassword arm64v8/mysql:8.1.0
docker run -itd --name=wordpress_app -v myweb_data:/var/www/html -v ${PWD}/myweb-log:/var/log --restart=always -p 8888:80 --net=my-webdb-net -e WORDPRESS_DB_HOST=mysql_app:3306 -e WORDPRESS_DB_NAME=wpdb -e WORDPRESS_DB_USER=wpuser -e WORDPRESS_DB_PASSWORD=wppassword --link mysql_app:arm64v8/mysql wordpress:5.7
docker exec -it mysql_app bash
mysql -uroot -p
show databases;
use wpdb;
show tables
select * from wp_users;
볼륨 두개를 생성하고 네트워크를 하나 생성해서 mysql, wordpress를 각각 run명령어로 실행하고 글 하나를 인서트하고 디비에서 확인하는 내용입니다. 아래에서는 이걸 compose로 한번에 하는 방법을 실습으로 하겠습니다.












[docker compose installation or version update]
DOCKER_CONFIG=${DOCKER_CONFIG:-$HOME/.docker}
mkdir -p $DOCKER_CONFIG/cli-plugins
curl -SL https://github.com/docker/compose/releases/download/v2.23.1/docker-compose-linux-aarch64 -o $DOCKER_CONFIG/cli-plugins/docker-compose
chmod +x $DOCKER_CONFIG/cli-plugins/docker-compose
docker compose version
m1 실리콘이라서 강의와는 다른 버전의 compose를 설치했습니다.https://docs.docker.com/compose/install/linux/
링크를 참고해서 진행하시면 설치가 됩니다. docker compose version을 실행시 버전 표기가 안되면 잘못된 버전을 설치한거니 다른 버전으로 재시도 하시면 됩니다.

[실습2] docker compose를 이용한 web application(wordpress+mysql)
docker stop mysql_app wordpress_app
docker rm mysql_app wordpress_app
vi docker compose.yaml
version: "3.9"
services:
# 첫 번째 서비스 정의.
mydb:
# 도커 허브에서 제공하는 mysql:8.0 이미지 선택.
image: arm64v8/mysql:8.1.0
# 서비스 컨테이너 이름 지정.
container_name: mysql_app
# 최상위 레벨에 정의 mydb_data 볼륨 지정.
volumes:
- mydb_data:/var/lib/mysql
# 수동 제어를 제외한 컨테이너 종료 시 자동 재시작.
restart: always
# 호스트 운영체제와 컨테이너의 3306 포트를 바인드한다.
# workbench 같은 클라이언트 도구와 연결하기 위해 필요하다.
ports:
- "3306:3306"
# 최상위 레벨에 정의한 backend-net을 기본 네트워크로 지정한다.
networks:
- backend-net
# 서비스가 사용할 환경 변수 지정한다.
environment:
MYSQL_ROOT_PASSWORD: password#
MYSQL_DATABASE: wpdb
MYSQL_USER: wpuser
MYSQL_PASSWORD: wppassword
# 두 번째 서비스를 작성한다.
myweb:
# myweb 서비스가 실행되기 전에 mydb 서비스를 먼저 실행하는 의존성을 설정한다.
depends_on:
- mydb
# wordpress:5.7 이미지 지정한다.
image: wordpress
# 서비스 컨테이너 이름을 지정한다.
container_name: wordpress_app
# 호스트 운영체제의 8888 포트와 컨테이너의 80 포트를 바인드한다.
ports:
- "8888:80"
# backend-net으로 mydb 서비스와 동일 네트워크로 지정하고,
# 외부 연결을 위한 네트워크를 위해 fronetend-net 지정을 가정한다.
# docker network connect ~ 명령으로 두 번째 네트워크를 추가하는 것과 같다.
networks:
- backend-net
- frontend-net
# 컨테이너 데이터 지속성을 위해 도커 볼륨기법과 바인드 마운트 기법을 사용한다.
volumes:
- myweb_data:/var/www/html
- ${PWD}/myweb-log:/var/log \
# 수동 제어를 제외한 컨테이너 종료 시 자동 재시작한다.
restart: always
# 서비스가 사용할 환경 변수를 지정한다.
environment:
WORDPRESS_DB_HOST: mydb:3306
WORDPRESS_DB_USER: wpuser
WORDPRESS_DB_PASSWORD: wppassword
WORDPRESS_DB_NAME: wpdb
# 도커 컴포즈 애플리케이션이 사용할 네트워크 생성. docker network create와 동일하다.
networks:
frontend-net: {}
backend-net: {}
# 도커 컴포즈 애플리케이션이 사용할 볼륨 생성. docker volume create와 동일하다.
volumes:
mydb_data: {}
myweb_data: {}
docker compose up
첫번째 실습을 compose로 옮겨 놓았다. 주의 사항으로는 파이선처럼 띄어쓰기로 계층을 구분하므로 띄어쓰기를 신경써야 하고 탭 대신 스페이스바 2번을 추천한다. OS에 때라 탭은 다르게 적용되는 경우도 있기때문이다.





docker compose YAML 코드 작성과 CLI
docker compose
- docker compose는 KUBERNETES와 같이 컨테이너 오케스트레이션 및 컨테이너화된 애플리케이션 관리에 사용되는 널리 사용되는 도구다.
- docker compose YAML 코드는 여러 개의 docker run 실행과 유사하며 네트웤, 볼륨 등을 한번에 생성할 수 있다.
- docker compose 3단계 프로세스
- Dockerfile: 배포하고자 하는 애플리케이션 환경 정의(선택)
- docker-compose.yaml(yml): 하나 이상의 컨테이너 서비스를 실행 할수 있도록 서비스 정의
- docker compose up: 명령으로 YAML코드로 정의된 서비스를 시작하고 실행한다.
- 장단점
- 장점
- 서로 다른 OS 환경이라도 동일한 환경구성이 가능하다.(이식성)
- 동일한 환경을 사용하기 때문에 개발환경에 이슈가 발생해도 팀간 소통이 쉽다.
- 복잡한 환경도 YAML 코드로 스크립트화 할 수 있기 때문에 자동화가 가능하다.
- docker compose CLI를 이용하여 쉽게 “멀티 컨테이너 애플리케이션”을 관리할 수 있다.
- 단점
- 동시에 다수의 컨테이너 서비스를 수행하는 경우(MSA) 자원 활용률이 순간 높아질 수 있다.
- docker container기술에 대한 이해도가 필요하다.
- 장점

[실습] docker compose CLI 활용을 위한 “flask+redis” 서비스 배포
cd ~/fastcampus/ch10/flask-redis tree docker compose up docker compose up --scale server_db=3 --scale server_web=3 -d docker compose ps docker compose up --scale server_db=1 --scale server_web=1 -d docker compose ps docker compose down --rmi all
docker compose up –scale server_db=3 –scale server_web=3 -d 이 명령어를 사용해서 쉽게 컨테이너의 크케일을 확장할수 있는것을 알수 있다.








docker compose 활용, 다양한 애플리케이션 배포
[실습1] nginx proxy와 연결되는 docker compose 애플리케이션 배포하기
tree -a
vi Dockerfile.alb
FROM nginx:1.25.1-alpine
RUN rm /etc/nginx/conf.d/default.conf
COPY nginx.conf /etc/nginx/conf.d/default.conf
vi nginx.conf
upstream web-alb {
server 172.17.0.1:5001;
server 172.17.0.1:5002;
server 172.17.0.1:5003;
}
server {
location / {
proxy_pass http://web-alb;
}
}
vi Dockerfile.app1
FROM python:3.10-slim
COPY requirements.txt /requirements.txt
WORKDIR /
RUN pip install -r requirements.txt
COPY . /
ENTRYPOINT [ "python3" ]
CMD [ "/pyfla_app1.py" ]
vi pyfla_app1.py
from flask import request, Flask
import json
app1 = Flask(__name__)
@app1.route('/')
def hello_world():
return 'Flask Web Application [1]' + '\n'
if __name__ == '__main__':
app1.run(debug=True, host='0.0.0.0')
vi requirements.txt
Flask
vi docker-compose.yaml
version: '3.8'
services:
pyfla_app1:
build:
context: ./pyfla_app1
dockerfile: Dockerfile.app1
ports:
- "5001:5000"
pyfla_app2:
build:
context: ./pyfla_app2
dockerfile: Dockerfile.app2
ports:
- "5002:5000"
pyfla_app3:
build:
context: ./pyfla_app3
dockerfile: Dockerfile.app3
ports:
- "5003:5000"
nginx:
build:
context: ./nginx_alb
dockerfile: Dockerfile.alb
ports:
- "8080:80"
depends_on:
- pyfla_app1
- pyfla_app2
- pyfla_app3
docker compose up --build
docker compose ps
docker compose down --rmi all
새
새로고침을 하면 라운드로빙으로 3개의 서버로 분배 되는 것을 알 수 있다.
[실습2] 웹 페이지 방문 count하는 docker compose 애플리케이션 배포하기
cd ~/fastcampus/ch10/web-count
tree -a
vi app.py
from flask import Flask, render_template
from redis import Redis
import os,random
app = Flask(__name__)
redis = Redis(host='redis', port=6379)
images = [
"cloud-01.png",
"cloud-02.png",
"cloud-03.png",
"cloud-04.png",
"cloud-05.png",
"docker_logo.png",
"k8s_logo.png"
]
@app.route('/')
def index():
image_path = "/static/images/" + random.choice(images)
count = redis.incr('count')
return render_template('index.html', image_path=image_path, visit_count=count)
if __name__ == "__main__":
app.run(host="0.0.0.0", port=8899)
vi Dockerfile
FROM python:3.10-slim
LABEL maintainer "kevin.lee"
RUN pip install --upgrade pip
RUN mkdir -p /cloud-web/image
ENV APP_PATH /cloud-web/image
COPY requirements.txt $APP_PATH/
RUN pip install --no-cache-dir -r $APP_PATH/requirements.txt
COPY app.py $APP_PATH/
COPY templates/ $APP_PATH/templates/
COPY static/ $APP_PATH/static/
EXPOSE 8899
CMD ["python", "/cloud-web/image/app.py"]
vi docker-compose.yml
version: '3.8'
services:
webserver:
build: .
ports:
- "8899:8899"
depends_on:
- redis
redis:
image: redis:6.0
vi templates/index.html
<html>
<head>
<meta charset="utf-8">
<title>Fastcampus Docker Container Service</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<link rel="stylesheet" href="./css/bootstrap.css">
</head>
<body>
<nav class="navbar navbar-expand-sm" style="background-color:#2684FF;">
<a class="navbar-brand" href="https:///hub.docker.com" target="_blank" style="color:#fff;">
<img src="/static/images/fastcampus.png">Docker Container Application by kevin.lee "hylee@dshub.cloud"</a>
</nav>
<div class="container" style="padding:20px 0 0 0">
<div class="row">
<div class="alert alert-danger" role="alert">
[Currently you have {{visit_count}} visits.]
</div>
</div>
<div class="row">
<img class="img-fluid" src="{{image_path}}" />
</div>
</div>
</body>
</html>
docker compose up --build [-d]
docker compose ps
docker compose down --rmi all
docker compose 활용한 3-tier 애플리케이션 배포
[실습] docker CLI를 이용한 3-tier web application(나의 수강 일기)

cd my-diary-front/ docker build -t mydiary-front:1.0 . cd my-diary-back/ docker build -t mydiary-back:1.0 . docker network create fastapp-net docker run -d --name rolling-db --net fastapp-net -p 13306:3306 -e MYSQL_ROOT_PASSWORD=pass123# -e MYSQL_DATABASE=paperdb -e MYSQL_ROOT_HOST=% -e MYSQL_USER=user -e MYSQL_PASSWORD=user arm64v8/mysql:8.1.0 --character-set-server=utf8 --collation-server=utf8_general_ci docker run -d --name rolling-server --net fastapp-net -p 8080:8080 -e SPRING_DATASOURCE_URL=jdbc:mysql://rolling-db:3306/paperdb?serverTimezone=Asia/Seoul -e SPRING_DATASOURCE_USERNAME=user -e SPRING_DATASOURCE_PASSWORD=user mydiary-back:1.0 docker run -d --name rolling-front --net fastapp-net -p 3000:3000 mydiary-front:1.0
run 명령어를 사용해서 db, server, front 컨테이너를 올렸다.





[실습2] docker compose를 이용한 3-tier web application(나의 수강 일기)
vi docker-compose.yaml
version: '3.3'
services:
mydiary-db:
image: arm64v8/mysql:8.1.0
container_name: rolling-db
environment:
MYSQL_ROOT_PASSWORD: pass123
MYSQL_DATABASE: paperdb
MYSQL_ROOT_HOST: '%'
MYSQL_USER: user
MYSQL_PASSWORD: user
ports:
- '13306:3306'
networks:
- rolling-be-db
restart: always
command:
- --character-set-server=utf8
- --collation-server=utf8_general_ci
mydiary-back:
image: mydiary-back:1.0
container_name: rolling-server
restart: always
depends_on:
- mydiary-db
ports:
- '8080:8080'
environment:
SPRING_DATASOURCE_URL: jdbc:mysql://rolling-db:3306/paperdb?serverTimezone=Asia/Seoul
SPRING_DATASOURCE_USERNAME: user
SPRING_DATASOURCE_PASSWORD: user
networks:
- rolling-be-db
- rolling-fe-be
mydiary-front:
image: mydiary-front:1.0
container_name: rolling-front
restart: always
depends_on:
- mydiary-back
ports:
- '3000:3000'
networks:
- rolling-fe-be
networks:
rolling-be-db: {}
rolling-fe-be: {}
docker compose up
위에서 docker build로 만든 이미지를 사용해서 docker-compose.yaml를 작성하고 실행해 보았다.








[실습3] 실습1의 image build + 실습2를 한번에 수행
cd ~/fastcampus/ch10//my-diary-3
vi docker-compose.yaml
version: '3.3'
services:
mydiary-db:
image: arm64v8/mysql:8.1.0
container_name: rolling-db
environment:
MYSQL_ROOT_PASSWORD: pass123
MYSQL_DATABASE: paperdb
MYSQL_ROOT_HOST: '%'
MYSQL_USER: user
MYSQL_PASSWORD: user
ports:
- '13306:3306'
networks:
- rolling-be-db
restart: always
command:
- --character-set-server=utf8
- --collation-server=utf8_general_ci
mydiary-back:
build:
context: ./my-diary-back
dockerfile: Dockerfile
container_name: rolling-server
restart: always
depends_on:
- mydiary-db
ports:
- '8080:8080'
environment:
SPRING_DATASOURCE_URL: jdbc:mysql://rolling-db:3306/paperdb?serverTimezone=Asia/Seoul
SPRING_DATASOURCE_USERNAME: user
SPRING_DATASOURCE_PASSWORD: user
networks:
- rolling-be-db
- rolling-fe-be
mydiary-front:
build:
context: ./my-diary-front
dockerfile: Dockerfile
container_name: rolling-front
restart: always
depends_on:
- mydiary-back
ports:
- '3000:3000'
networks:
- rolling-fe-be
networks:
rolling-be-db: {}
rolling-fe-be: {}
docker compose up
docker compose ps
docker compose down --rmi all
server, front image를 빌드해서 컨테이너를 올리는 작업을 같이 수행하도록 했다.




[실습4] Nginx Proxy(load balancer)를 결합한 애플리케이션 배포
이번 실습에서는 front, backend 앞에 로드밸런서를 두고 round roubin 방식으로작동하도록 실습해보겠습니다.

cd ~/fastcampus/ch10/my-diary-3-proxy
tree
vi docker-compose.yaml
version: '3.8'
services:
mydiary-db:
image: arm64v8/mysql:8.1.0
container_name: rolling-db
environment:
MYSQL_ROOT_PASSWORD: pass123
MYSQL_DATABASE: paperdb
MYSQL_ROOT_HOST: '%'
MYSQL_USER: user
MYSQL_PASSWORD: user
ports:
- '3306:3306'
networks:
- mydiary-net
restart: always
command:
- --character-set-server=utf8
- --collation-server=utf8_general_ci
mydiary-back:
build:
context: ./my-diary-back
dockerfile: Dockerfile
deploy:
replicas: 3
restart: always
depends_on:
- mydiary-db
ports:
- '8081-8083:8080'
environment:
SPRING_DATASOURCE_URL: jdbc:mysql://rolling-db:3306/paperdb?serverTimezone=Asia/Seoul
SPRING_DATASOURCE_USERNAME: user
SPRING_DATASOURCE_PASSWORD: user
networks:
- mydiary-net
mydiary-front:
build:
context: ./my-diary-front
dockerfile: Dockerfile
deploy:
replicas: 3
restart: always
depends_on:
- mydiary-back
ports:
- '3000-3002:3000'
networks:
- mydiary-net
proxy-be:
image: nginx:1.21.5-alpine
container_name: rolling-server-lb
restart: always
depends_on:
- mydiary-back
ports:
- '8080:80'
volumes:
- ${PWD}/proxy/nginx-be.conf:/etc/nginx/nginx.conf
networks:
- mydiary-net
proxy-fe:
image: nginx:1.21.5-alpine
container_name: rolling-front-lb
restart: always
ports:
- '80:80'
volumes:
- ${PWD}/proxy/nginx-fe.conf:/etc/nginx/nginx.conf
networks:
- mydiary-net
networks:
mydiary-net:
driver: bridge
ipam:
driver: default
config:
- subnet: 172.20.0.0/24
ip_range: 172.20.0.0/24
gateway: 172.20.0.1
docker compose up --build
docker compose ps
docker compose down --rmi all






















