[DOCKER] 멀티 컨테이너 서비스를 위한 docker compose-14

이번 글에서는 드디어 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

 






Leave A Reply

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다