docker compose 는 여러 번의 docker CLI를 실행하지 않고, 한번에 관련 애플리케이션들을 YAML 파일로 구성하여 내부 환경 구성과 속성을 실행할 수 있다.
또한 docker compose 는 설정 값을 캐싱하기 때문에 재시작시 변경이 없다면 캐싱된 정보를 그대로 사용하여 빠른 서비스 실행을 보장할 수 있다.
YMAL 코드에 변경된 애플리케이션 들를 동일 네트워크에 포함되게 때무에 복잡한 연결 구성이 없어도 쉽게 API 통신이 가능한 장점도 갖게 된다.
docker compose
는 kubernetes
와 같이 컨테이너 오케스트레이션 및 컨테이너화된 애플리케이션 관리에 사용되는 널리 사용되는 도구다.
docker compose 는 다중 컨테이너
Docker 애플리케이션을 정의하고 실행하기 위한 도구이고, YAML 파일을 사용하여 애플리케이션 서비스를 수행한다.
이후 docker compose CLI를 이용하여 모든 서비스의 라이프 사이클을 관리한다.
응집력 있는 애플리케이션 (cohesive application)으로 함께 작동하는 여러 컨테이너를 실행하려는 로컬 개발 및 테스트 환경을 위해 설계된 도구다.
3-Tier
환경을 예를 들어보자.docker compose YMAL
코드는 여러개의 docker run
실행과 유사하며 네트워크, 볼륨 등을 한번에 생성할 수 있다.YAML
코드로 정의된 서비스를 시작하고 실행한다.멀티 컨테이너 애플리케이션
을 관리할 수 있다.비교 | docker compose | kubernetes |
---|---|---|
사용 범위 | 단일 호스트(로컬)에서의 개발, 테스트 및 소규모 배포에 적합 | 클러스터로 구성된 전체 머신을 기반으로 컨테이너화된 어플리케이션 배포에 적합 |
구조 | 단일 시스템에서 컨테이너 간의 실행, 상호 작용 및 종속성 관리 | 클러스터의 여러 노드에서 컨테이너를 오케스트레이션하고 확장, 로드밸런싱 메커니즘 제공 |
확장성 및 고가용성 | 여러 호스트 서비스 지원하지 않음. 단, docker swarm 클러스터에서는 지원 | 수요에 따른 애플리케이션 자동 확장 및 분산, 컨테이너 재시작 및 자동 장애 처리 기능 제공 |
서비스 검색 및 로드밸런싱 | 자체 네트워크 생성. 컨테이너 연결 지원 및 서비스 간 포트 노출. 자체 로드밸런싱 없음 | DNS 기반 서비스 검색과 다중 컨테이너 간 분산 처리하는 로드밸런싱 제공 |
롤링 업데이트 및 배포 전략 | 수동적 롤링 업데이트만 지원 | 다운 타임 없이 컨테이너 업데이트 가능. Blue/gree 및 카나리아 배포 같은 배포 전략 제공 |
# 첫 번째 mysql:8.0 컨테이너를 생성한다.
~$ 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 \
> mysql:8.0-debian
# 두 번째 wordpress 컨테이너를 첫 번째 컨테이너와 연결하고, 생성한다.
~$ docker run -itd \
> --name=wordpress_app \
> -v myweb_data:/var/www/html \
> -v ${PWD}/myweb-log:/var/log \
> --restart=always \
> -p 8080:80 \
> --net=mywebdb-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:mysql \
> wordpress:5.7
#생성한 컨테이너들을 삭제한다.
~$ docker stop mysql_app wordpress_app
~$ docker rm mysql_app wordpress_app
# 작업을 docker-compose.yaml을 작성해본다.
version: "3.9"
services:
# 첫 번째 서비스 정의.
mydb:
# Docker 허브에서 제공하는 mysql:8.0 이미지 선택
image: mysql:8.0-debian
# 서비스 컨테이너 이름 지정.
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
# docker compose 애플리케이션이 사용할 network 생성.
networks:
frontend-net: {}
backend-net: {}
# docker compose 애플리케이션이 사용할 volume 생성.
volumes:
mydb_data: {}
myweb_data: {}
YAML 코드란?
- YAML(Yet Another Markup Language or YAML ain't markup language)
- 사람이 쉽게 읽을 수 잇는 데이터 질결화 언어(위 -> 아래)로 구성 파일 작성에 주로 사용된다.
- 일반적으로 설계 상 가장 먼저 실행되야 하는 애플리케이션을 먼저 작성하고, 이와 의존성을 갖는 데이터베이스 및 하위 애플리케이션을 작성한다.
- 또는, 클러스터 환경인 경우 마스터 노드를 먼저 작성하고 그다음 데이터 노드들을 이어서 작성하면 된다.
- 그 다음 전체 애플리케이션에 필요한 네트워크, 볼륨, 캐시 등의 기반 환경까지 코드에 모두 설정할 수 있다.
- 따라서, 서비스 전체를 하나의 docker-compose.yaml로 작성이 가능한 것이다.
- 쉽게 읽고 이해할 수 있도록 설계되어 프로그래밍 언어로 사용되거나, 클라우드 자동 환경 배포 도구(IaC 환경 구성)로 많이 사용된다. (예, AWS CloudFormation, Redhat Ansible 등)
YAML | JSON |
---|---|
YAML Ain't Markup Language | JavaScript Object Notation |
주석 사용이 가능 | 주석 사용 불가 |
한글 등의 유니코드를 그대로 사용 가능 | 한글 등의 멀티 바이트 문자는 인코딩 수행 |
주로 환경 구성 등의 설정 파일 작성 시 사용 | 주로 API 작성시 사용 |
version: "3.8"
services:
서비스명1:
#컨테이너에 필요한 옵션들 설정
서비스명2:
#컨테이너에 필요한 옵션들 설정
...
mydiary-front:
build:
context: ./my-diary-front # dockerfile 위치 (동일 경로면 생략 가능)
dockerfile: Dockerfile # 제공하는 Dockerfile 이름
version: "3.8"
services:
mydb:
image: mysql:5.7
volumes:
- db_data:/var/lib/mysql
...
myweb:
image: wordpress:latest
volumes:
- w eb_data:/var/www/html
...
networks:
volumes:
db_data: {} # {} 생략 가능
web_data: {}
version: "3.8"
services:
...
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost"]
interval: 1m00s
timeout: 10s
retries: 3
start_period: 30s
networks:
volumes:
version: "3.8"
services:
server_web:
image: httpd:2
server_db:
image: redis:6-alpine
[root@ip-172-31-39-154 ~]# docker-compose up
[+] Running 14/14
⠿ server_db Pulled 6.6s
⠿ 96526aa774ef Pull complete 2.1s
⠿ 8eae4a4e5454 Pull complete 2.2s
⠿ be9bc35ddfc9 Pull complete 2.5s
⠿ 16979fad3a95 Pull complete 3.3s
⠿ 4fcaecc2605d Pull complete 3.3s
⠿ 4f4fb700ef54 Pull complete 3.3s
⠿ d99f7c88acce Pull complete 3.4s
⠿ server_web Pulled 9.6s
⠿ a803e7c4b030 Pull complete 4.3s
⠿ 053327351b4a Pull complete 4.3s
⠿ de42e9dfbbe1 Pull complete 4.6s
⠿ 9d28e265584b Pull complete 6.5s
⠿ ce95f18e49ae Pull complete 6.5s
[+] Running 3/3
⠿ Network root_default Created 0.2s
⠿ Container root-server_db-1 Created 0.2s
⠿ Container root-server_web-1 Created 0.2s
Attaching to root-server_db-1, root-server_web-1
root-server_web-1 | AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using 172.18.0.3. Set the 'ServerName' directive globally to suppress this message
root-server_web-1 | AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using 172.18.0.3. Set the 'ServerName' directive globally to suppress this message
root-server_web-1 | [Thu Oct 05 06:33:48.364196 2023] [mpm_event:notice] [pid 1:tid 139636192311168] AH00489: Apache/2.4.57 (Unix) configured -- resuming normal operations
root-server_web-1 | [Thu Oct 05 06:33:48.364789 2023] [core:notice] [pid 1:tid 139636192311168] AH00094: Command line: 'httpd -D FOREGROUND'
root-server_db-1 | 1:C 05 Oct 2023 06:33:48.409 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
root-server_db-1 | 1:C 05 Oct 2023 06:33:48.410 # Redis version=6.2.13, bits=64, commit=00000000, modified=0, pid=1, just started
root-server_db-1 | 1:C 05 Oct 2023 06:33:48.410 # Warning: no config file specified, using the default config. In order to specify a config file use redis-server /path/to/redis.conf
root-server_db-1 | 1:M 05 Oct 2023 06:33:48.411 * monotonic clock: POSIX clock_gettime
root-server_db-1 | 1:M 05 Oct 2023 06:33:48.412 * Running mode=standalone, port=6379.
root-server_db-1 | 1:M 05 Oct 2023 06:33:48.413 # Server initialized
root-server_db-1 | 1:M 05 Oct 2023 06:33:48.413 # WARNING Memory overcommit must be enabled! Without it, a background save or replication may fail under low memory condition. Being disabled, it can can also cause failures without low memory condition, see https://github.com/jemalloc/jemalloc/issues/1328. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect.
root-server_db-1 | 1:M 05 Oct 2023 06:33:48.414 * Ready to accept connections
[ec2-user@ip-172-31-39-154 ~]$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
179cb1d5be62 redis:6-alpine "docker-entrypoint.s…" About a minute ago Up About a minute 6379/tcp root-server_db-1
fc6a90aeb374 httpd:2 "httpd-foreground" About a minute ago Up About a minute 80/tcp root-server_web-1
###########################################################
########################### docker compose scale을 사용해보자
[root@ip-172-31-39-154 ~]# docker-compose up --scale server_db=1 --scale server_web=1 -d
[+] Running 2/2
⠿ Container root-server_db-1 Started 1.0s
⠿ Container root-server_web-1 Started 1.0s
[root@ip-172-31-39-154 ~]# docker-compose ps
NAME COMMAND SERVICE STATUS PORTS
root-server_db-1 "docker-entrypoint.s…" server_db running 6379/tcp
root-server_web-1 "httpd-foreground" server_web running 80/tcp
[root@ip-172-31-39-154 ~]# docker-compose up --scale server_web=3 -d
[+] Running 4/4
⠿ Container root-server_db-1 Started 3.0s
⠿ Container root-server_web-3 Started 2.7s
⠿ Container root-server_web-1 Started 2.9s
⠿ Container root-server_web-2 Started 2.3s
[root@ip-172-31-39-154 ~]# docker-compose ps
NAME COMMAND SERVICE STATUS PORTS
root-server_db-1 "docker-entrypoint.s…" server_db running 6379/tcp
root-server_web-1 "httpd-foreground" server_web running 80/tcp
root-server_web-2 "httpd-foreground" server_web running 80/tcp
root-server_web-3 "httpd-foreground" server_web running 80/tcp
[root@ip-172-31-39-154 ~]# docker-compose down --rmi all
[+] Running 7/7
⠿ Container root-server_db-1 Removed 0.3s
⠿ Container root-server_web-3 Removed 1.5s
⠿ Container root-server_web-1 Removed 1.5s
⠿ Container root-server_web-2 Removed 1.5s
⠿ Image redis:6-alpine Removed 0.1s
⠿ Network root_default Removed 0.3s
⠿ Image httpd:2 Removed 0.4s
# 사전에 구성한 소스파일을 활용해서 frontend, backend image를 build한다.
[ec2-user@ip-172-31-39-154 my-diary-front]$ docker build -t mydiary-front:1.0 .
[+] Building 18.8s (9/9) FINISHED docker:default
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 201B 0.0s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [internal] load metadata for docker.io/library/node:17-slim 2.3s
=> [1/4] FROM docker.io/library/node:17-slim@sha256:a180966357fe14cac9dd6c9cd496ed4d85b8a598cb33482705ecebad646145e3 7.2s
=> => resolve docker.io/library/node:17-slim@sha256:a180966357fe14cac9dd6c9cd496ed4d85b8a598cb33482705ecebad646145e3 0.0s
=> => sha256:af868bc40acde9dbbbd7b967b13f257d686cd2c421cd64d2c0768c9b56d19925 7.03kB / 7.03kB 0.0s
=> => sha256:42c077c10790d51b6f75c4eb895cbd4da37558f7215b39cbf64c46b288f89bda 31.38MB / 31.38MB 0.5s
=> => sha256:1d1b4cabe4ab7f16559c16a600d141af1e46a36592606aaf1b9846e9ecaed5d9 4.18kB / 4.18kB 0.6s
=> => sha256:fd2a83b16756304bc95a2f364042afb69936aa8024749af81607272d2efda81a 44.83MB / 44.83MB 1.2s
=> => sha256:a180966357fe14cac9dd6c9cd496ed4d85b8a598cb33482705ecebad646145e3 1.21kB / 1.21kB 0.0s
=> => sha256:924a5b8512a10d428bae66658d59f54d5c33b658edb2a3d718002a190966eb53 1.37kB / 1.37kB 0.0s
=> => extracting sha256:42c077c10790d51b6f75c4eb895cbd4da37558f7215b39cbf64c46b288f89bda 2.6s
=> => sha256:cd666498bb13c8026fce129218693dd9921deac32704b6a0d5c3a0849b9b1afd 2.77MB / 2.77MB 1.0s
=> => sha256:f428e85c51881b0db0fe6c2d4e5abc3b236cc5cf51e60e0c1fe5c5f56191d416 453B / 453B 0.8s
=> => extracting sha256:1d1b4cabe4ab7f16559c16a600d141af1e46a36592606aaf1b9846e9ecaed5d9 0.0s
=> => extracting sha256:fd2a83b16756304bc95a2f364042afb69936aa8024749af81607272d2efda81a 3.4s
=> => extracting sha256:cd666498bb13c8026fce129218693dd9921deac32704b6a0d5c3a0849b9b1afd 0.2s
=> => extracting sha256:f428e85c51881b0db0fe6c2d4e5abc3b236cc5cf51e60e0c1fe5c5f56191d416 0.0s
=> [internal] load build context 0.0s
=> => transferring context: 111.02kB 0.0s
=> [2/4] WORKDIR /home/node 0.1s
=> [3/4] COPY ./ ./ 0.1s
=> [4/4] RUN npm -y install 8.7s
=> exporting to image 0.3s
=> => exporting layers 0.3s
=> => writing image sha256:14694a3e18d7f2e073cf9fdfc23d255ee55c761e4853cd20d6849049c3c009b4 0.0s
=> => naming to docker.io/library/mydiary-front:1.0
[ec2-user@ip-172-31-39-154 my-diary-front]$ cd ../my-diary-back/
[ec2-user@ip-172-31-39-154 my-diary-back]$ ls
Dockerfile rollingpaper
[ec2-user@ip-172-31-39-154 my-diary-back]$ docker build -t mydiary-back:1.0 .
[+] Building 135.6s (14/14) FINISHED docker:default
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 458B 0.0s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [internal] load metadata for docker.io/library/openjdk:11-jdk 2.2s
=> [builder 1/8] FROM docker.io/library/openjdk:11-jdk@sha256:99bac5bf83633e3c7399aed725c8415e7b569b54e03e4599e580fc9cdb7c21ab 17.4s
=> => resolve docker.io/library/openjdk:11-jdk@sha256:99bac5bf83633e3c7399aed725c8415e7b569b54e03e4599e580fc9cdb7c21ab 0.0s
=> => sha256:99bac5bf83633e3c7399aed725c8415e7b569b54e03e4599e580fc9cdb7c21ab 1.04kB / 1.04kB 0.0s
=> => sha256:e81b7f317654b0f26d3993e014b04bcb29250339b11b9de41e130feecd4cd43c 1.79kB / 1.79kB 0.0s
=> => sha256:47a932d998b743b9b0bcce55aa8ede77de94a6a183c8a67dec9d5e3b8ce0faa7 6.26kB / 6.26kB 0.0s
=> => sha256:001c52e26ad57e3b25b439ee0052f6692e5c0f2d5d982a00a8819ace5e521452 55.00MB / 55.00MB 0.8s
=> => sha256:d9d4b9b6e964657da49910b495173d6c4f0d9bc47b3b44273cf82fd32723d165 5.16MB / 5.16MB 0.9s
=> => sha256:2068746827ec1b043b571e4788693eab7e9b2a95301176512791f8c317a2816a 10.88MB / 10.88MB 0.8s
=> => extracting sha256:001c52e26ad57e3b25b439ee0052f6692e5c0f2d5d982a00a8819ace5e521452 6.1s
=> => sha256:66223a710990a0ae7162aeed80417d30303afa3f24aafa57aa30348725e2230b 213B / 213B 1.1s
=> => sha256:9daef329d35093868ef75ac8b7c6eb407fa53abbcb3a264c218c2ec7bca716e6 54.58MB / 54.58MB 2.4s
=> => sha256:d85151f15b6683b98f21c3827ac545188b1849efb14a1049710ebc4692de3dd5 5.42MB / 5.42MB 1.2s
=> => sha256:db38d58ec8ab4111b072f6700f978a51985acd252aabce3be377f25162e68301 202.07MB / 202.07MB 5.9s
=> => extracting sha256:d9d4b9b6e964657da49910b495173d6c4f0d9bc47b3b44273cf82fd32723d165 0.3s
=> => extracting sha256:2068746827ec1b043b571e4788693eab7e9b2a95301176512791f8c317a2816a 0.4s
=> => extracting sha256:9daef329d35093868ef75ac8b7c6eb407fa53abbcb3a264c218c2ec7bca716e6 3.6s
=> => extracting sha256:d85151f15b6683b98f21c3827ac545188b1849efb14a1049710ebc4692de3dd5 0.3s
=> => extracting sha256:66223a710990a0ae7162aeed80417d30303afa3f24aafa57aa30348725e2230b 0.0s
=> => extracting sha256:db38d58ec8ab4111b072f6700f978a51985acd252aabce3be377f25162e68301 5.2s
=> [internal] load build context 0.0s
=> => transferring context: 84.98kB 0.0s
=> [builder 2/8] COPY ./rollingpaper/gradlew . 0.2s
=> [builder 3/8] COPY ./rollingpaper/gradle gradle 0.1s
=> [builder 4/8] COPY ./rollingpaper/build.gradle . 0.0s
=> [builder 5/8] COPY ./rollingpaper/settings.gradle . 0.0s
=> [builder 6/8] COPY ./rollingpaper/src src 0.1s
=> [builder 7/8] RUN chmod +x ./gradlew 1.1s
=> [builder 8/8] RUN ./gradlew bootJar 111.9s
=> [stage-1 2/2] COPY --from=builder build/libs/*.jar app.jar 0.1s
=> exporting to image 0.4s
=> => exporting layers 0.4s
=> => writing image sha256:c491bbb195166e2a3954e989329021048ba482a47ca24a70cb4679394d79b781 0.0s
=> => naming to docker.io/library/mydiary-back:1.0 0.0s
[ec2-user@ip-172-31-39-154 my-diary-back]$ docker images | grep mydiary
mydiary-back 1.0 c491bbb19516 9 seconds ago 694MB
mydiary-front 1.0 14694a3e18d7 4 minutes ago 252MB
# 생성된 frontend, backend iamge를 확인한다.
[ec2-user@ip-172-31-39-154 my-diary-back]$ docker image history mydiary-front:1.0
IMAGE CREATED CREATED BY SIZE COMMENT
14694a3e18d7 5 minutes ago CMD ["npm" "run" "start"] 0B buildkit.dockerfile.v0
<missing> 5 minutes ago EXPOSE map[3000/tcp:{}] 0B buildkit.dockerfile.v0
<missing> 5 minutes ago RUN /bin/sh -c npm -y install # buildkit 10.3MB buildkit.dockerfile.v0
<missing> 5 minutes ago COPY ./ ./ # buildkit 110kB buildkit.dockerfile.v0
<missing> 5 minutes ago WORKDIR /home/node 0B buildkit.dockerfile.v0
<missing> 16 months ago /bin/sh -c #(nop) CMD ["node"] 0B
<missing> 16 months ago /bin/sh -c #(nop) ENTRYPOINT ["docker-entry… 0B
<missing> 16 months ago /bin/sh -c #(nop) COPY file:4d192565a7220e13… 388B
<missing> 16 months ago /bin/sh -c set -ex && savedAptMark="$(apt-… 9.5MB
<missing> 16 months ago /bin/sh -c #(nop) ENV YARN_VERSION=1.22.19 0B
<missing> 16 months ago /bin/sh -c ARCH= && dpkgArch="$(dpkg --print… 152MB
<missing> 16 months ago /bin/sh -c #(nop) ENV NODE_VERSION=17.9.1 0B
<missing> 16 months ago /bin/sh -c groupadd --gid 1000 node && use… 333kB
<missing> 16 months ago /bin/sh -c #(nop) CMD ["bash"] 0B
<missing> 16 months ago /bin/sh -c #(nop) ADD file:134f25aec8adf83cb… 80.4MB
[ec2-user@ip-172-31-39-154 my-diary-back]$ docker image history mydiary-back:1.0
IMAGE CREATED CREATED BY SIZE COMMENT
c491bbb19516 About a minute ago ENTRYPOINT ["java" "-jar" "/app.jar"] 0B buildkit.dockerfile.v0
<missing> About a minute ago EXPOSE map[8080/tcp:{}] 0B buildkit.dockerfile.v0
<missing> About a minute ago COPY build/libs/*.jar app.jar # buildkit 40.3MB buildkit.dockerfile.v0
<missing> 14 months ago /bin/sh -c #(nop) CMD ["jshell"] 0B
<missing> 14 months ago /bin/sh -c set -eux; arch="$(dpkg --print-… 337MB
<missing> 14 months ago /bin/sh -c #(nop) ENV JAVA_VERSION=11.0.16 0B
<missing> 14 months ago /bin/sh -c #(nop) ENV LANG=C.UTF-8 0B
<missing> 14 months ago /bin/sh -c #(nop) ENV PATH=/usr/local/openj… 0B
<missing> 14 months ago /bin/sh -c { echo '#/bin/sh'; echo 'echo "$J… 27B
<missing> 14 months ago /bin/sh -c #(nop) ENV JAVA_HOME=/usr/local/… 0B
<missing> 14 months ago /bin/sh -c set -eux; apt-get update; apt-g… 11.3MB
<missing> 14 months ago /bin/sh -c apt-get update && apt-get install… 152MB
<missing> 14 months ago /bin/sh -c set -ex; if ! command -v gpg > /… 19MB
<missing> 14 months ago /bin/sh -c set -eux; apt-get update; apt-g… 10.7MB
<missing> 14 months ago /bin/sh -c #(nop) CMD ["bash"] 0B
<missing> 14 months ago /bin/sh -c #(nop) ADD file:d0f758e50c654c225… 124MB
# 전용 네트워크 생성
[ec2-user@ip-172-31-39-154 my-diary-back]$ docker network create fastapp-net
f0d37d95651df3d7b5ff1f67202b5114d51c8cc00a9a1d07e49ce25cb6398fb4
[ec2-user@ip-172-31-39-154 my-diary-back]$ docker network ls
NETWORK ID NAME DRIVER SCOPE
1b1f9cb59d91 bridge bridge local
f0d37d95651d fastapp-net bridge local
a6d2dc487604 host host local
5964bb59b326 none null local
...
# DB 컨테이너 생성
[ec2-user@ip-172-31-39-154 my-diary-back]$ 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 \
> mysql:5.7-debian --character-set-server=utf8 --collation-server=utf8_general_ci
Unable to find image 'mysql:5.7-debian' locally
5.7-debian: Pulling from library/mysql
3240fe174df9: Pull complete
ffaa8624d67f: Pull complete
4d8328f9714e: Pull complete
5eb1b5f179ac: Pull complete
656a614f333c: Pull complete
ad1320c6d0c3: Pull complete
a28b57b8a949: Pull complete
6a0cdfac1051: Pull complete
c540032ab5fd: Pull complete
2fe13c62198b: Pull complete
c5129c0e04b1: Pull complete
Digest: sha256:0821f3a5b3ecda79885d8bd40ec353f3d2cc1bc23586f9c367dfc76493737163
Status: Downloaded newer image for mysql:5.7-debian
2c24820e22c7443e6453432be58201a7dbc0cb1ed8cacf4443ee1b3ecf0c6525
# backend 컨테이너 생성. (DB 컨테이너 내부적인 동작이 요구되므로 몇 십 초 뒤에 backend 수행)
[ec2-user@ip-172-31-39-154 my-diary-back]$ 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
08fbd2efc12418e7cda391be174221c0d1313f84fc7d56d809816fff87b025b4
# frontend 컨테이너 생성
[ec2-user@ip-172-31-39-154 my-diary-back]$ docker run -d --name rolling-front --net fastapp-net -p 3000:3000 mydiary-front:1.0
4b77c454d89ff29a3dc972f651bcdd7d5ebd7b6954f7113fd85f06344556383c
[ec2-user@ip-172-31-39-154 my-diary-back]$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
4b77c454d89f mydiary-front:1.0 "docker-entrypoint.s…" 9 seconds ago Up 7 seconds 0.0.0.0:3000->3000/tcp, :::3000->3000/tcp rolling-front
08fbd2efc124 mydiary-back:1.0 "java -jar /app.jar" About a minute ago Up About a minute 0.0.0.0:8080->8080/tcp, :::8080->8080/tcp rolling-server
2c24820e22c7 mysql:5.7-debian "docker-entrypoint.s…" 3 minutes ago Up 3 minutes 33060/tcp, 0.0.0.0:13306->3306/tcp, :::13306->3306/tcp rolling-db
데이터 입력 과정 확인
1. Frontend 화면에서 값을 입력하고 [Add Note]를 클릭한다. 화면에 출력됨을 확인.
2. 1) 번 수행 전에 backend의 로그를 출력한다. ~$ docker exec -it rolling-server bash
3. Mysql DB에 접속하여 입력한 데이터가 테이블에 기록되어 있음을 확인한다.
- docker exec -it rolling-db bash or MYSQL workbech를 통해 수행.
# 컨테이너 중지, 삭제
~$ docker stop rolling-db rolling-server rolling-front
~$ docker rm rolling-db rolling-server rolling-front
[ec2-user@ip-172-31-39-154 my-diary]$ mkdir my-diary-compose && cd $_
[ec2-user@ip-172-31-39-154 my-diary-compose]$ vi docker-compose.yaml
version: '3.3'
services:
mydiary-db:
image: mysql:5.7-debian
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: {}
[ec2-user@ip-172-31-39-154 my-diary-compose]$ docker-compose up -d
[+] Running 3/3
⠿ Container rolling-db Started 1.0s
⠿ Container rolling-server Started 2.5s
⠿ Container rolling-front Started 3.6s
[ec2-user@ip-172-31-39-154 my-diary-compose]$ docker-compose ps -a
NAME COMMAND SERVICE STATUS PORTS
rolling-db "docker-entrypoint.s…" mydiary-db running 0.0.0.0:13306->3306/tcp, :::13306->3306/tcp
rolling-front "docker-entrypoint.s…" mydiary-front running 0.0.0.0:3000->3000/tcp, :::3000->3000/tcp
rolling-server "java -jar /app.jar" mydiary-back running 0.0.0.0:8080->8080/tcp, :::8080->8080/tcp
## DB 데이터 확인
[ec2-user@ip-172-31-39-154 my-diary-compose]$ docker exec -it rolling-db bash
root@852948f8da9d:/# mysql -uroot -p
Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 12
Server version: 5.7.42 MySQL Community Server (GPL)
Copyright (c) 2000, 2023, Oracle and/or its affiliates.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| paperdb |
| performance_schema |
| sys |
+--------------------+
5 rows in set (0.01 sec)
mysql> use paperdb;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
mysql> show tables;
+-------------------+
| Tables_in_paperdb |
+-------------------+
| paper |
+-------------------+
1 row in set (0.02 sec)
mysql> select * from paper;
Empty set (0.00 sec)
docker-compose.yaml 코드 리뷰.
version: '3.3'
services:
mydiary-db:
image: mysql:5.7-debian
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: {}
mydiary-back:
build:
context: ./my-diary-back
dockefile: Dockerfile
mydiary-front:
build:
context: ./my-diary-front
dockerfile: Dockerfile
[ec2-user@ip-172-31-39-154 my-diary-3]$ docker-compose up -d
[+] Running 12/12
⠿ mydiary-db Pulled 15.6s
⠿ 3240fe174df9 Pull complete 3.1s
⠿ ffaa8624d67f Pull complete 3.1s
⠿ 4d8328f9714e Pull complete 3.6s
⠿ 5eb1b5f179ac Pull complete 3.7s
⠿ 656a614f333c Pull complete 3.7s
⠿ ad1320c6d0c3 Pull complete 5.0s
⠿ a28b57b8a949 Pull complete 5.0s
⠿ 6a0cdfac1051 Pull complete 5.0s
⠿ c540032ab5fd Pull complete 12.4s
⠿ 2fe13c62198b Pull complete 12.4s
⠿ c5129c0e04b1 Pull complete 12.5s
[+] Building 1.9s (23/23) FINISHED
=> [my-diary-3_mydiary-back internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [my-diary-3_mydiary-front internal] load .dockerignore 0.1s
=> => transferring context: 2B 0.0s
=> [my-diary-3_mydiary-back internal] load build definition from Dockerfile 0.1s
=> => transferring dockerfile: 458B 0.1s
=> [my-diary-3_mydiary-front internal] load build definition from Dockerfile 0.1s
=> => transferring dockerfile: 201B 0.1s
=> [my-diary-3_mydiary-back internal] load metadata for docker.io/library/openjdk:11-jdk 1.6s
=> [my-diary-3_mydiary-front internal] load metadata for docker.io/library/node:17-slim 1.6s
=> [my-diary-3_mydiary-front 1/4] FROM docker.io/library/node:17-slim@sha256:a180966357fe14cac9dd6c9cd496ed4d85b8a598cb33482705ecebad646145e3 0.0s
=> [my-diary-3_mydiary-front internal] load build context 0.1s
=> => transferring context: 111.02kB 0.1s
=> [my-diary-3_mydiary-back builder 1/8] FROM docker.io/library/openjdk:11-jdk@sha256:99bac5bf83633e3c7399aed725c8415e7b569b54e03e4599e580fc9cdb7c21ab 0.0s
=> [my-diary-3_mydiary-back internal] load build context 0.1s
=> => transferring context: 84.98kB 0.1s
=> CACHED [my-diary-3_mydiary-front 2/4] WORKDIR /home/node 0.0s
=> CACHED [my-diary-3_mydiary-front 3/4] COPY ./ ./ 0.0s
=> CACHED [my-diary-3_mydiary-front 4/4] RUN npm -y install 0.0s
=> [my-diary-3_mydiary-front] exporting to image 0.0s
=> => exporting layers 0.0s
=> => writing image sha256:14694a3e18d7f2e073cf9fdfc23d255ee55c761e4853cd20d6849049c3c009b4 0.0s
=> => naming to docker.io/library/my-diary-3_mydiary-front 0.0s
=> CACHED [my-diary-3_mydiary-back builder 2/8] COPY ./rollingpaper/gradlew . 0.0s
=> CACHED [my-diary-3_mydiary-back builder 3/8] COPY ./rollingpaper/gradle gradle 0.0s
=> CACHED [my-diary-3_mydiary-back builder 4/8] COPY ./rollingpaper/build.gradle . 0.0s
=> CACHED [my-diary-3_mydiary-back builder 5/8] COPY ./rollingpaper/settings.gradle . 0.0s
=> CACHED [my-diary-3_mydiary-back builder 6/8] COPY ./rollingpaper/src src 0.0s
=> CACHED [my-diary-3_mydiary-back builder 7/8] RUN chmod +x ./gradlew 0.0s
=> CACHED [my-diary-3_mydiary-back builder 8/8] RUN ./gradlew bootJar 0.0s
=> CACHED [my-diary-3_mydiary-back stage-1 2/2] COPY --from=builder build/libs/*.jar app.jar 0.0s
=> [my-diary-3_mydiary-back] exporting to image 0.0s
=> => exporting layers 0.0s
=> => writing image sha256:c491bbb195166e2a3954e989329021048ba482a47ca24a70cb4679394d79b781 0.0s
=> => naming to docker.io/library/my-diary-3_mydiary-back 0.0s
[+] Running 5/5
⠿ Network my-diary-3_rolling-be-db Created 0.1s
⠿ Network my-diary-3_rolling-fe-be Created 0.1s
⠿ Container rolling-db Started 1.2s
⠿ Container rolling-server Started 3.1s
⠿ Container rolling-front Started
# 결과 확인.
[ec2-user@ip-172-31-39-154 my-diary-3]$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
my-diary-3_mydiary-back latest c491bbb19516 35 minutes ago 694MB
my-diary-3_mydiary-front latest 14694a3e18d7 40 minutes ago 252MB
mysql 5.7-debian 6dca13361869 2 months ago 463MB
[ec2-user@ip-172-31-39-154 my-diary-3]$ docker network ls
NETWORK ID NAME DRIVER SCOPE
1b1f9cb59d91 bridge bridge local
f0d37d95651d fastapp-net bridge local
a6d2dc487604 host host local
5bd475acccd0 my-diary-3_rolling-be-db bridge local
818a33f1adef my-diary-3_rolling-fe-be bridge local
5964bb59b326 none null local
[ec2-user@ip-172-31-39-154 my-diary-3]$ docker-compose ps
NAME COMMAND SERVICE STATUS PORTS
rolling-db "docker-entrypoint.s…" mydiary-db running 0.0.0.0:13306->3306/tcp, :::13306->3306/tcp
rolling-front "docker-entrypoint.s…" mydiary-front running 0.0.0.0:3000->3000/tcp, :::3000->3000/tcp
rolling-server "java -jar /app.jar" mydiary-back running 0.0.0.0:8080->8080/tcp, :::8080->8080/tcp
#작업 종료. 생성된 이미지까지 모두 삭제 (--rmi all)
[ec2-user@ip-172-31-39-154 my-diary-3]$ docker-compose down --rmi all
[+] Running 8/8
⠿ Container rolling-front Removed 1.7s
⠿ Container rolling-server Removed 0.6s
⠿ Container rolling-db Removed 1.5s
⠿ Image my-diary-3_mydiary-front Removed 0.0s
⠿ Network my-diary-3_rolling-be-db Removed 0.3s
⠿ Network my-diary-3_rolling-fe-be Removed 0.5s
⠿ Image mysql:5.7-debian Removed 0.5s
⠿ Image my-diary-3_mydiary-back Removed
[ec2-user@ip-172-31-39-154 my-diary-3-proxy]$ tree .
.
├── docker-compose.yaml
├── my-diary-back
│ ├── Dockerfile
│ └── rollingpaper
│ ├── build.gradle
│ ├── gradle
│ │ └── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
│ ├── gradlew
│ ├── gradlew.bat
│ ├── settings.gradle
│ └── src
│ ├── main
│ │ ├── java
│ │ │ └── com
│ │ │ └── threetier
│ │ │ └── team1
│ │ │ └── rollingpaper
│ │ │ ├── DTO
│ │ │ │ ├── ApiResponseDTO.java
│ │ │ │ └── PaperDTO.java
│ │ │ ├── RollingpaperApplication.java
│ │ │ ├── controller
│ │ │ │ └── PaperController.java
│ │ │ ├── domain
│ │ │ │ └── Paper.java
│ │ │ ├── repository
│ │ │ │ └── PaperRepository.java
│ │ │ └── service
│ │ │ ├── PaperService.java
│ │ │ └── PaperServiceImpl.java
│ │ └── resources
│ │ └── application.yml
│ └── test
│ └── java
│ └── com
│ └── threetier
│ └── team1
│ └── rollingpaper
│ ├── RollingpaperApplicationTests.java
│ └── service
│ └── PaperServiceImplTest.java
├── my-diary-front
│ ├── Dockerfile
│ ├── Jenkinsfile.txt
│ ├── app.js
│ ├── fastcampus.png
│ ├── package-lock.json
│ ├── package.json
│ └── public
│ ├── fastcampus.png
│ ├── index.ejs
│ ├── index.js
│ └── style.css
└── proxy
├── nginx-be.conf
└── nginx-fe.conf
version: '3.8'
services:
mydiary-db:
image: mysql:5.7-debian
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 ## backend의 복제본 수
restart: always
depends_on:
- mydiary-db
ports:
- '8081-8083:8080' ## 복제본 수 만큼의 Port 범위 지정
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 ## frontend의 복제본 수
restart: always
depends_on:
- mydiary-back
ports:
- '3000-3002:3000' ## 복제본 수 만큼의 Port 지정
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 ## 사전에 구성된 backend용 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 ## 사전에 구성된 frontend용 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
frontend영역에 들어가는 proxy(LB)를 설정해 보자.
events { worker_connections 1024; }
http {
upstream front-servers {
server mydiary-front:3000; ## server에 설정되는 주소를 docker-compose.yaml 파일의 frontend service name 지정
server mydiary-front:3001;
server mydiary-front:3002;
}
server {
listen 80 default_server;
location / {
proxy_pass http://front-servers;
}
}
}
frontend 영역의 app.js 코드에서 backend로 연결되는 주소를 backend service name으로 지정.
const express = require('express');
const app = express();
const http = require('http');
const path = require('path');
const axios = require('axios');
const ejs = require('ejs');
const dest = 'http://mydiary-back:8080'; ## backend service name으로 dest 상수값을 채운다.
const router = express.Router();
const bodyParser = require('body-parser');
*** 실행을 통해 설계한 대로 서비스가 동작하는지 확인해 보자.
*port 공유 오류 발생
Error response from daemon: driver failed programming external connectivity on endpoint my-diary-3-proxy-mydiary-back-3 (9a2939028f306b8d2a34e78db1e5d2f20a634829abaf33f144c60de95cd9aa96): Bind for 0.0.0.0:8083 failed: port is already allocated
docker-compose.yml 파일에서 ports 섹션에 '8081-8083:8080'와 같이 포트 범위를 사용하셨습니다. 이는 8081, 8082, 8083 포트를 8080으로 매핑하려고 시도합니다. 여기서 문제가 발생할 수 있습니다.
이유
docker-compose에서 포트 범위를 사용하는 것은 호스트의 연속적인 포트 범위를 컨테이너의 특정 포트에 매핑하는 경우에 유용합니다. 예를 들어, 호스트의 8081, 8082, 8083 포트를 각각 컨테이너의 8080 포트에 매핑하려는 경우입니다.
하지만, 문제는 여러 개의 replicas가 동시에 실행되면서 같은 8080 포트를 사용하려고 할 때 발생합니다. docker-compose.yml에서 mydiary-back 서비스에 replicas: 3 설정이 되어 있기 때문에, 3개의 복제본(replica)이 모두 동일한 8080 포트를 사용하려고 시도합니다. 이 때, 3개의 복제본 중 하나는 8081 포트에, 다른 하나는 8082 포트에 성공적으로 매핑되지만, 마지막 복제본이 8083 포트에 매핑되려고 시도할 때 이미 8080 포트가 사용 중이므로 에러가 발생합니다.
따라서, 이 문제를 해결하기 위해서는 각 복제본이 사용하는 컨테이너의 포트를 별도로 설정해주어야 합니다. 예를 들면, 첫 번째 복제본이 8080 포트를 사용하도록, 두 번째 복제본이 8081 포트를 사용하도록, 세 번째 복제본이 8082 포트를 사용하도록 설정해주는 방식입니다.
그러나 docker-compose에서는 이러한 세밀한 포트 설정을 자동으로 할 수 없습니다. 따라서, 서비스를 분리하거나 다른 방법을 사용하여 각 복제본의 포트를 별도로 설정해주어야 합니다.
version: '3.8'
services:
mydiary-db:
image: mysql:5.7-debian
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-1:
build:
context: ./my-diary-back
dockerfile: Dockerfile
restart: always
depends_on:
- mydiary-db
ports:
- "8081: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-back-2:
build:
context: ./my-diary-back
dockerfile: Dockerfile
restart: always
depends_on:
- mydiary-db
ports:
- "8082:8080"
mydiary-back-3:
build:
context: ./my-diary-back
dockerfile: Dockerfile
restart: always
depends_on:
- mydiary-db
ports:
- "8083:8080"
mydiary-front-1:
build:
context: ./my-diary-front
dockerfile: Dockerfile
restart: always
depends_on:
- mydiary-back-1
- mydiary-back-2
- mydiary-back-3
ports:
- '3000:3000'
networks:
- mydiary-net
mydiary-front-2:
build:
context: ./my-diary-front
dockerfile: Dockerfile
restart: always
depends_on:
- mydiary-back-1
- mydiary-back-2
- mydiary-back-3
ports:
- '3001:3000'
networks:
- mydiary-net
mydiary-front-3:
build:
context: ./my-diary-front
dockerfile: Dockerfile
restart: always
depends_on:
- mydiary-back-1
- mydiary-back-2
- mydiary-back-3
ports:
- '3002:3000'
networks:
- mydiary-net
proxy-be:
image: nginx:1.21.5-alpine
container_name: rolling-server-lb
restart: always
networks:
mydiary-net:
external: false