본문 바로가기
docker

Docker compose를 통한 Nginx, Spring boot, React, Mysql 연동 및 배포

by khds 2024. 4. 7.

 

서론

 

최근 프로젝트에서 도커를 통해 Spring boot와 React 프로젝트를 진행하면서 리버스 프록시 역할을 수행하는 Nginx를 같이 실행해 보았다. 추가로 Mysql까지 실행하며 모든 컨테이너는 Docker compose에 의해 실행될 수 있도록 구현하였다. 

Spring boot는 비즈니스 로직을 처리하며 Mysql과 연동하여 데이터를 저장, 처리, 조회하는 기능을 담당한다. React는 사용자와 상호작용하는 부분으로 Nginx 서버를 통해 구동되며, Nginx 서버는 사용자 요청을 url에 따라 Spring boot로 보낼지, React로 보낼지 정하는 리버스 프록시 역할을 수행한다.

 

이 글에서는 docker-compose를 통해 각 서비스를 배포한 과정을 기록한 것이다.

 

 

본론

 

우선 실행을 위한 폴더구조는 아래와 같다.(배포를 하는데 필요한 정보 위주로만 간략하게 작성하였다.)

📜docker-compose.yaml
📦frontend
 ┣ 📂node_modules
 ┣ 📂public
 ┣ 📂src
 ┣ 📜default.conf
 ┣ 📜Dockerfile
 ┣ 📜package.json
 ┣ 📜package-lock.json
📦backend
 ┣ 📂build
 ┣ 📂db
 ┣ 📂src
 ┣ 📜.env
 ┣ 📜.gitignore
 ┣ 📜build.gradle
 ┣ 📜Dockifile
 ┣ 📜gradlew
 📦conf
 ┣ 📜nginx.conf

 

db 폴더는 mysql 컨테이너 내부의 데이터와 연동되는 폴더로 따로 생성하지 않아도 컨테이너 실행 시 자동으로 생성된다. 이후 해당 폴더를 제거하지만 않으면 컨테이너를 다시 실행할 때마다 mysql 내 데이터는 유지된다.

 

이제 핵심이 되는 docker-compose.yaml 파일을 봐보자.

version: '3'
name: test
services:
  mysql:
    image: mysql:8.0.34
    networks:
      - khds_network
    volumes:
      - ./backend/db/conf.d:/etc/mysql/conf.d
      - ./backend/db/data:/var/lib/mysql
      - ./backend/db/initdb.d:/docker-entrypoint-initdb.d
    env_file: ./backend/.env
    ports:
      - "3306:3306"
    restart: always
  backend:
    build:
      context: ./backend
      dockerfile: Dockerfile
    container_name: backend
    networks:
      - khds_network
    ports:
      - "8080:8080"
    depends_on:
      - mysql
    environment:
      - SPRING_DATASOURCE_URL=jdbc:mysql://mysql:3306/khds_pay?useSSL=false&allowPublicKeyRetrieval=true
      - SPRING_DATASOURCE_USERNAME=mysqluser
      - SPRING_DATASOURCE_PASSWORD=mysqlpw
      - SPRING_JPA_PROPERTIES_HIBERNATE_DIALECT=org.hibernate.dialect.MySQL8Dialect
      - SPRING_JPA_HIBERNATE_DDL_AUTO=validate
    restart: always
  frontend:
    depends_on:
      - backend
    build:
      context: ./frontend
      dockerfile: Dockerfile
    ports:
      - "3000:3000"
    networks:
      - khds_network
  nginx:
    image: nginx:latest
    networks:
      - khds_network
    restart: always
    volumes:
      - ./conf/:/etc/nginx/conf.d
    ports:
      - 80:80
    depends_on:
      - backend
      - frontend
networks:
  khds_network:
    driver: bridge

 

 

1. Mysql 

 

mysql:
    image: mysql:8.0.34
    networks:
      - khds_network
    volumes:
      - ./backend/db/conf.d:/etc/mysql/conf.d
      - ./backend/db/data:/var/lib/mysql
      - ./backend/db/initdb.d:/docker-entrypoint-initdb.d
    env_file: ./backend/.env
    ports:
      - "3306:3306"
    restart: always

 

Mysql:8.0.34 이미지로 부터 생성되는 Mysql 서비스는 volumes를 설정하여 컨테이너가 종료돼도 데이터가 유지되도록 하였다. 

port는 3306을 그대로 사용하였고, 환경변수를 .env 파일 내에 작성해 보았다. 

.env 파일 내용은 아래와 같다.

TZ=Asia/Seoul
MYSQL_HOST=localhost
MYSQL_PORT=3306
MYSQL_ROOT_PASSWORD=rootpassword
MYSQL_DATABASE=khds_pay
MYSQL_USER=mysqluser
MYSQL_PASSWORD=mysqlpw

 

 

2. Spring boot

 

backend:
    image: backend:0.0.1-SNAPSHOT
    networks:
      - khds_network
    ports:
      - "8080:8080"
    depends_on:
      - mysql
    environment:
      - SPRING_DATASOURCE_URL=jdbc:mysql://mysql:3306/khds_pay?useSSL=false&allowPublicKeyRetrieval=true
      - SPRING_DATASOURCE_USERNAME=mysqluser
      - SPRING_DATASOURCE_PASSWORD=mysqlpw
      - SPRING_JPA_PROPERTIES_HIBERNATE_DIALECT=org.hibernate.dialect.MySQL8Dialect
      - SPRING_JPA_HIBERNATE_DDL_AUTO=validate
    restart: always

 

backend 서비스는 springboot를 실행한 이미지인데, 이 이미지는 backend 폴더 내 Docker 파일을 통해 생성할 수 있다. 

아래는 backend 폴더 내에 있는 Dockerfile이다. 

FROM gradle:7.6-jdk as builder

WORKDIR /app
COPY ./ ./
RUN gradle clean build --no-daemon

FROM openjdk:17.0-slim
WORKDIR /app
COPY --from=builder /app/build/libs/backend-0.0.1-SNAPSHOT.jar .

EXPOSE 8080
USER nobody
ENTRYPOINT ["java", "-jar", "backend-0.0.1-SNAPSHOT.jar"]

 

빌드 후 8080 포트로 배포하도록 되어있다. 주의할 점은 sprnigboot버전에 따라 gradle 버전이 다를 수 있다는 점이다.

환경변수(environment)는 application.properties or yml 파일 내부에 작성했던 것들인데, 이번엔 따로 지정해 보았다.

 

 

3. React

 

frontend:
    depends_on:
      - backend
    build:
      context: ./frontend
      dockerfile: Dockerfile
    ports:
      - "3000:3000"
    networks:
      - khds_network

 

frontend 서비스는 frontend 폴더 내 Dockerfile을 실행시킴으로써 동작한다. 

아래는 Dockerfile 내부코드이다.

FROM node:16-alpine as builder
WORKDIR /usr/src/app
COPY package.json .
RUN npm install
COPY ./ ./
RUN npm run build

FROM nginx
EXPOSE 3000
COPY ./default.conf /etc/nginx/conf.d/default.conf 
COPY --from=builder usr/src/app/build  /usr/share/nginx/html

 

node를 통해 build를 한 후 nginx를 통해 서버를 실행하도록 하였다.

'COPY --from=builder usr/src/app/build  /usr/share/nginx/html'를 작성해줌으로서 빌드된 react 내부 파일들이 nginx 위에서 실행될 수 있게 해 준다.

추가로 확인할 사항은 default.conf로 내용은 아래와 같다. 

server {
    listen 3000; 
    location / {
        root /usr/share/nginx/html; 
        index index.html index.htm; 
        try_files $uri  $uri/ /index.html; 
    }
}

 

 

4. Nginx 

 

  nginx:
    image: nginx:latest
    networks:
      - khds_network
    restart: always
    volumes:
      - ./conf/:/etc/nginx/conf.d
    ports:
      - 80:80
    depends_on:
      - backend
      - frontend

 

80 포트로 열어놨으며, 주로 살펴볼 내용은 conf 폴더 내 있는 nginx.conf 파일이다. volume 기능을 통해 외부의 파일을 내부와 연동하므로, 외부에서 작성한 nginx.conf 파일을 기반으로 서비스가 동작한다.

아래는 nginx.conf 파일 내부 코드이다.

server {
       listen 80;
       client_max_body_size 20M;
       
       location /api {
               
	   proxy_pass http://backend:8080;
	   proxy_set_header Host $host;
               proxy_set_header X-Real-IP $remote_addr;
               proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
               proxy_set_header X-Forwarded-Proto $scheme;
               }
       location / {
               proxy_pass http://frontend:3000;
               proxy_set_header Host $host;
               proxy_set_header X-Real-IP $remote_addr;
               proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
               proxy_set_header X-Forwarded-Proto $scheme;
       }
}

 

주로 살펴볼 내용은 listen, location이다. listen은 80포트로 실행될 시 작성된 내용으로 동작한다는 것이고, location은 해당 경로로 접근이 들어왔을 시 제어한다. proxy_pass를 설정하여 /api 경로로 도달 시 Springboot 서버로, / 경로 도달 시 react 서버로 연결되도록 하였다.

주의할 점은 http:// 뒤에 'backend', 'frontend'로 설정해 두었는데, 이는 dcoker-compose 내 작성한 network로 모든 컨테이너가 묶여있어 컨테이너 이름으로 서로를 호출할 수 있는 것이다. 

 

화면과 같이 /api로 호출하면 Spring boot가, /로 호출하면 React가 호출되는 것을 확인할 수 있다.

 

 

결론

 

이 글에서는 서버를 이루는 간단한 설정들만을 하였다.

세부적으로 가면 더욱 복잡한 것들이 많을 것이다. 

다음에는 추가적인 사항들을 넣어가며 서버를 운용해 보아야겠다.

아직 공부할 길은 멀었다..!

 

 

참고

 

https://adjh54.tistory.com/420#google_vignette

 

[Docker] Dockerfile을 이용한 Spring Boot App 환경 구성 및 실행방법

해당 글에서는 Dockerfile을 구성하여 Spring Boot App을 컨테이너 환경 구성 이후 배포 방법에 대해 알아봅니다. 💡 [참고] Docker에 대해 더 궁금하시면 아래의 글을 참고하시면 도움이 됩니다. 분류 설

adjh54.tistory.com

 

https://velog.io/@kmw10693/%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-SpringBoot-React-%EC%9B%B9-%EC%84%9C%EB%B9%84%EC%8A%A4-DockerNginx-SSLReverse-Proxy-Redis%EB%A1%9C-%EB%B0%B0%ED%8F%AC%ED%95%98%EA%B8%B0

 

[프로젝트] SpringBoot + React 웹 서비스 Docker(Nginx, SSL/Reverse Proxy, Redis)로 배포하기

기존의 프로젝트에서는 Github Actions를 이용하여 빌드 파일을 압축하여 S3로 전송한 뒤, CodeDeploy를 통하여 EC2 서버 내에서 Nginx를 통해 배포하였다. 하지만, CI/CD가 복잡하고 긴 점이 아쉬워서 도커

velog.io

 

https://www.inflearn.com/questions/393714/eb-%EB%82%B4%EC%97%90%EC%84%9C-%EB%8F%84%EC%BB%A4-%EC%9D%B4%EB%AF%B8%EC%A7%80-%EC%83%9D%EC%84%B1-%EC%8B%9C-%EC%98%A4%EB%A5%98%EA%B0%80-%EB%B0%9C%EC%83%9D%ED%95%A9%EB%8B%88%EB%8B%A4

 

EB 내에서 도커 이미지 생성 시 오류가 발생합니다. - 인프런

안녕하세요 선생님! Travis CI에서 테스트에 성공하고 EB로 디플로이까지는 성공하나, EB 내에서 이미지를 생성하다가 오류가 발생합니다. 오류는 복사 실패 입니다. tep 9/9 : COPY --from=builder /usr/src/ap

www.inflearn.com

 

https://stackoverflow.com/questions/77452312/docker-failed-to-solve-canceled-context-canceled-when-loading-build-context

 

Docker "Failed to solve: Canceled: context canceled" when loading build context

When running docker-compose up --build I'm constantly getting this error. => [web internal] load build definition from Dockerfile 0.0s ...

stackoverflow.com

 

https://bgpark.tistory.com/132

 

Docker COPY failed: no source files were specified 에러

오늘은 aws ecs fargat에 spring boot microservice를 code pipeline을 사용하여 배포하던 중 발생한 에러에 대해서 말해보려고 합니다. 발생 이슈 docker 이미지를 build 하던 중, 계속 COPY failed: no source files were spe

bgpark.tistory.com

 

https://medium.com/@office.yeon/dockerizing-with-multi-stage-builds-in-spring-boot-multi-module-project-1fd3aa886afc

 

Dockerizing with Multi-Stage Builds in Spring Boot Multi Module Project

Introduction

medium.com

 

https://velog.io/@cwangg897/Docker-MultiStage

 

Docker MultiStage - Spring Boot

도커 Multistage빌드를 통해 Spring Boot 이미지를 생성해 보며 겪은 경험을 적었습니다. Multi-stage는 여러 개의 Image Base를 사용하여 Docker이미지를 생성하는 것이다사용하는 이유는? 도커 이미지 크기

velog.io