📚 Study

Nginx 연동하여 Spring Boot 프로젝트를 배포해보자 (Docker 기반)

date
Feb 8, 2023
slug
nginx-spring-boot-deploy
author
status
Public
category
📚 Study
tags
CI/CD
summary
EC2 환경에서 Docker 기반으로 Nginx + Spring Boot 연결하기
type
Post
thumbnail
아키텍처_복사본-001.png
notion image

목표

  1. EC2 내부에 Spring Boot 프로젝트를 배포(docker)한 후 Nginx를 연동(docker)하여, EC2 서버의 80포트가 8080포트를 사용하는 Spring Boot 서버로 리다이렉트(포트포워딩) 되도록 한다.
  1. 나아가 EC2 내부에서 돌아가는 프론트서버(리액트, 3000포트 사용)와 백엔드서버(스프링부트, 8080포트 사용)에 접근할 때 동일한 포트(우리의 경우 80포트)로 접근하여도 미리 설정해둔 {uri} 를 분기점으로 하여 알아서 각 서버로 갈 수 있도록 한다. 즉, 여러 개의 서버가 같은 웹서버에 있을 수 있도록 한다. (API gateway)
  1. 또한 각 서버의 docker container를 여러 개 생성한 후 Nginx에 각 host 포트를 등록함으로써, WAS 서버가 부담하는 부하를 줄이는 로드밸런싱의 기능을 수행하도록 한다. (Load balancer)
    1. 또한 이 과정에서 여러 개의 서버를 구동함에 따라, 무중단 배포를 구현할 수 있다.
 
결론적으로 우리가 구현하고자 하는 구조는 다음과 같다. 리액트로 구현한 프론트엔드 웹 페이지 서버스프링부트로 구현한 백엔드 api 서버가 EC2 내에서 실행되고 있고, 이 서버들은 각각 다른 포트(3000, 8080)를 갖고 있다. 사용자가 domain 을 요청했을 때 이를 알아채고 적절한 서버를 선택하여 요청을 보내는 포트포워딩(port forwarding) 기능을 nginx 웹서버가 수행한다. 또한 각 서버를 여러 개의 포트로 실행하여 로드밸런싱을 수행하게 한다.

개발환경

  • OS : Amazon EC2 Ubuntu
  • EC2 보안 그룹 인바운드 규칙: 8080, 80, 443, 22 port
  • docker 설치되어있음
  • Spring Boot 프로젝트 배포 및 docker image 빌드되어 있음

Spring Boot 프로젝트 배포

(알고 있다는 가정 하에 자세한 내용은 생략하겠음)

1. Spring boot 프로젝트 .jar 파일 생성 ( $ ./gradlew build )

2. 프로젝트 파일 내에 Dockerfile 생성

3. Spring boot 프로젝트 도커 이미지 생성

$ docker build -t {만들고자 하는 image_name} .

4. Spring boot 컨테이너 빌드 및 실행

$ docker run -d -p 8081:8080 —name {만들고자 하는 container_name} {컨테이너를 생성할 image_name}
❗️스프링부트 컨테이너를 8081:8080 포트로 설정했음을 주목하자 8081 포트가 나중에 다시 등장할 예정!

Nginx 컨테이너 빌드 및 실행

1. nginx 컨테이너 생성 및 실행

$ docker run -d -p 80:80 --name nginx nginx
❗️nginx 컨테이너의 포트를 80:80 로 설정하였음을 주목하자
원래는 nginx docker image가 빌드되어 있어야 하지만, docker run 명령어를 이용하면 image 다운로드, container 생성, container 구동이 모두 자동으로 실행되기 때문에 nginx image 를 따로 다운로드하거나 빌드하지 않고 바로 docker run 을 실행한다.
만약 image 다운로드, container 생성, container 실행을 모두 각각 하고 싶다면?
  1. image 다운로드
    1. $ sudo docker pull nginx $ sudo docker images // 이미지 목록
  1. image로 container 생성
    1. $ sudo docker create --name nginx nginx $ sudo docker container ls -a // 컨테이너 목록
  1. container 실행(start)
    1. $ sudo docker dontainer start nginx $ sudo docker ps // 실행중인 컨테이너 목록
 

2. nginx 컨테이너 내부 진입

nginx 컨테이너가 실행중인 상태에서!! 컨테이너 내부로 진입
컨테이너가 실행중이 아니라면 내부 진입이 불가하다
$ docker rm nginx // 를 통해 해당 컨테이너를 삭제한 후 다시 run 진행해야 함
$ docker exec -itu 0 nginx /bin/bash

3. nginx 컨테이너의 default.conf 파일 수정

# nginx 컨테이너 진입한 상태 @ cd /etc/nginx/conf.d @ vim default.conf # 만약 컨테이너에 vim이 설치되어있지 않다면 @ apt-get update @ apt-get install vim
# default.conf upstream backend { # backend 라는 도메인을 호출(?)하면 아래에 등록된 도메인으로 연결된다 server localhost:8081; # localhost:8081 즉 8081 포트로 리다이렉팅(포트포워딩)한다 } # localhost:8082 ... 등 여러 개의 서버를 등록하여 로드밸런싱의 기능 # 을 수행하도록 할 수 있다 server { listen 80; # nginx 컨테이너 실행시 연결했던 포트 listen [::]:80; server_name localhost; ... ... # 여기에서 포트포워딩!! location /web { # localhost:80/web 으로 들어오는 요청에 대하여 아래로 처리한다 proxy_pass http://backend; # backend : upstream backend에 등록해놓은' # 포트 중 하나로 리다이렉팅 proxy_set_header Connection ""; # upstream 을 사용하기 위함 proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $http_host; } ... ... }

4. nginx 컨테이너 재실행

$ docker stop nginx $ docker start nginx

결과

http://{domain}/web/~~ 으로 들어오는 요청에 대해선 스프링부트 서버로 리다이렉팅됨을 확인할 수 있다

+) 시행착오

  1. /api 가 없는데 계속 호출함
  1. proxy_set_header Connection “” ?? 이거였나 하여튼 각종 애들
    1. proxy_set_header Connection ""; # upstream 을 사용하기 위함 proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $http_host;