[NGINX] Ubuntu 에서 NGINX Docker 로 React 배포

STEVELOPER·2023년 2월 8일
0

NGINX

목록 보기
1/1

서론

Ubuntu 에서 NGINX 를 통해 React 앱 두개를 443 port 로
ssl 적용을 해서 Domain 으로 접속 가능하게 해야하는 상황이었다.
즉 같은 port(443) 을 사용하지만 Domain 을 통해 다른 앱에 접속하도록 해야하는 것이다.

Prerequisites

  • 우선 ubuntu 에 docker 와 docker-compose 를 설치하도록 한다.
    설치 방법은 검색하면 여러 자세한 방법들이 나와있다.
  • build 된 앱. React 의 경우 npm run build 를 통해 build 폴더가 생성된다.

사실 docker-compose 는 필수는 아니지만 사용해본 결과 Dockerfile 만을 사용했을 때보다
편리한 느낌이 들었다.

본론

docker 를 사용했을 때의 이슈중 하나는 호스팅하는 서버에 있는 소스를 사용하려면
마운팅 또는 소스를 docker 에 옮기는 작업을 해야한다.
NGINX 를 docker 로 띄우는 순간 이 docker image 를 통해 생성된
NGINX container 는 독립적인 공간을 가지게 되기 때문이다.
그렇기 때문에 이 NGINX docker image 를 build 할 때 소스를 옮기는 작업을 해야하는데,
이 작업을 Dockerfile 을 통해 처리할 수 있다.
하기는 NGINX docker build 를 위한 Dockerfile 의 내용이다.

FROM nginx # nginx image 사용.
RUN mkdir /app # root 디렉토리에 app 디렉토리 생성.
WORKDIR /app # app 디렉토리로 고정. cd 처럼 해당 디렉토리로 이동한다.
RUN mkdir ./app1_build # 첫번째 앱의 build 폴더가 위치할 디렉토리 생성
RUN mkdir ./app1_build/build # 첫번째 앱의 build 폴더 content를 복사할 디렉토리 생성
# 로컬에 있는 첫번째 앱의 build 의 content 를 docker의 첫번째 앱의 build 폴더에 복사
ADD ./app1_build/build ./app1_build/build 
RUN mkdir ./app2_build # 두번째 앱의 build 폴더가 위치할 디렉토리 생성
RUN mkdir ./app2_build/build # 두번째 앱의 build 폴더 content를 복사할 디렉토리 생성
# 로컬에 있는 두번째 앱의 build 의 content 를 docker의 두번째 앱의 build 폴더에 복사
ADD ./app2_build/build ./app2_build/build 
RUN mkdir ./bind_mount # 로컬에 있는 폴더를 마운팅할 디렉토리 생성(필수 X)
RUN mkdir /etc/nginx/ssl # ssl 인증서가 위치할 디렉토리 생성
COPY ./ssl /etc/nginx/ssl # 로컬에 있는 ssl 폴더의 content 를 docker 의 디렉토리에 복사
RUN rm /etc/nginx/conf.d/default.conf # 기존 NGINX 설정 파일 삭제
COPY ./apps.conf /etc/nginx/conf.d # 로컬에서 작성한 설정 파일을 docker 의 디렉토리에 복사
CMD ["nginx", "-g", "daemon off;"] # NGINX 실행

EXPOSE 443 # docker container 의 443 port 개방(yml 에서 ports 설정할 경우 불필요)

여기서 주의할 점은 상기 내용에서 로컬파일을 docker container 내부로 복사할 때
로컬파일 위치가 모두 상대경로로 작성했다는 것이다.
절대경로로 작성할 경우 지정된 위치를 못찾는 문제가 발생했었다.
상기파일대로 작성시 문제없이 진행하기 위해서는 복사하는 파일들을
Dockerfile 과 같은 디렉토리 또는 하위 디렉토리에 위치시켜야 한다.
app1_build, app2_build, ssl, apps.conf 이 모든 폴더와 파일들은
Dockerfile 과 같은 위치에 위치시키고 진행했다.

전체적인 디렉토리 구조를 확인하기 전에 우선 NGINX 에서 사용할 apps.conf 파일을 작성할 것이다. 이름은 뭐로 하든 상관없으나 .conf 확장자이어야 한다.

server {
    listen 443 ssl;
    listen 80;
    server_name your.domain.com your.domain2.com;

    ssl_certificate_key /etc/nginx/ssl/app1_ssl/app1_ssl.key;
    ssl_certificate /etc/nginx/ssl/app1_ssl/app1_ssl__bundle.pem;

    add_header 'Access-Control-Allow-Origin' "*" always;
    add_header 'Access-Control-Allow-Credentials' 'true' always;
    add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS' always;
    add_header 'Access-Control-Allow-Headers' 'Accept,Authorization,Cache-Control,Content-Type,DNT,If-Modified-Since,Keep-Alive,Origin,User-Agent,X-Requested-With' always;

    location / {
      root /app/app1_build/build;
      index index.html;
      try_files $uri $uri/ /index.html;
    }
}
server {
    listen 443 ssl;
    listen 80;
    server_name other.domain.com other.domain2.com;

    ssl_certificate_key /etc/nginx/ssl/app2_ssl/app2_ssl.key;
    ssl_certificate /etc/nginx/ssl/app2_ssl/app2_ssl__bundle.pem;

    add_header 'Access-Control-Allow-Origin' "*" always;
    add_header 'Access-Control-Allow-Credentials' 'true' always;
    add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS' always;
    add_header 'Access-Control-Allow-Headers' 'Accept,Authorization,Cache-Control,Content-Type,DNT,If-Modified-Since,Keep-Alive,Origin,User-Agent,X-Requested-With' always;

    location / {
      root /app/app2_build/build;
      index index.html;
      try_files $uri $uri/ /index.html;
    }
}

기본적인 conf 파일은 NGINX container 내부의 /etc/nginx/nginx.conf 에 위치한다.
해당 파일을 docker cp 로 로컬로 복사해오거나 편집기로 확인해보면
include /etc/nginx/conf.d/*.conf 구문을 확인할 수 있다.
여기서는 배포할 앱과 관련된 추가적인 설정을 하는 것이기 때문에 Dockerfile 에서
apps.conf 를 해당 디렉토리(/etc/nginx/conf.d) 에 복사하는 명령이 작성되어 있는 것이다.
conf 파일 작성 또한 검색해보면 자세한 방법들을 확인할 수 있다.
여기서 주목할 만한 부분은
listen, server_name, ssl_certificate_key, ssl_certificate, location
사실 거의 대부분이다.
listen 은 NGINX 가 구동되면서 해당 앱을 어느 port 에서 실행할 것인지를 지정한다.
443 의 경우 ssl 구문을 통해 ssl 지정을 활성화 할 수 있으며, 활성화 할 시
ssl_certificate_key 와 ssl_certificate 를 통해 ssl 의 경로를 지정해야한다.
ssl_certificate_key 는 ssl 인증키, ssl_certificate 는 인증서 경로를 기입한다.
server_name 은 어떤 domain 으로 진입할 것인지를 나타낸다.
여러개를 공백을 통해 기입할 수 있다.
location 은 경로에 따른 설정인데,
root 로 최상위 경로를 설정할 수 있다.
여기에 docker container 내부에서 앱의 build 폴더 위치를 기입한다.
index 에는 진입 파일을 기입한다.(실제로는 /app/app1_build/build/index.html)
location 을 통해서는 주소에 따라 다른 파일을 보여주거나 보여주고 싶지 않은 파일들을
필터링 할 수 있는데, 여기서는 그런 설정 없이 /, 즉, 최상위로 진입할 경우 index.html 을
보여주도록 설정했다.

이제 전체적인 디렉토리 구조를 확인해보면
로컬의 app1_build 하위에는 첫번째 React 앱의 build 폴더를 위치시켰고,
app2_build 하위에는 두번째 React 앱의 build 폴더를 위치시켰다.
ssl 인증서 또한 디렉토리를 한 번 더 구분해서 위치시켰다.
즉 현재 디렉토리의 상태는 이제 작성할 yml 파일을 포함해서 하기와 같다.

.
├── docker-compose.yml
└── docker_build/
    ├── Dockerfile
    ├── app1_build/
    │   └── build/
    ├── app2_build/
    │   └── build/
    ├── ssl/
    │   ├── app1_ssl/
    │   └── app2_ssl/
    └── apps.conf

이제 docker-compose.yml 을 작성할 차례이다.

version: "3.8"

networks:
  front-connection:
    driver: bridge
  db-connection:
    driver: bridge

services:
  nginx-react: # 생성될 image 명 같은데 실행시 temp_nginx-react 이런식으로 docker image 가 생성된다.
    container_name: react_compose # 생성될 container 명
    build: /location/of/docker_build # Dockerfile 의 위치
    ports: # 호스팅하는 서버와 docker container 에 포트포워딩될 port 들 설정.
      - "80:80/tcp" # 호스팅(로컬) 서버에 80 으로 진입시 docker container 의 80 으로 포워드.
      - "443:443/tcp" # 호스팅(로컬) 서버에 443 으로 진입시 docker container 의 443 으로 포워드.
    volumes: # 현재 내용에서 필수는 아니지만 로컬의 디렉토리를 docker container 에 마운트.
      - type: bind # 마운팅 타입
        source: /location/of/local # 마운팅할 로컬의 디렉토리 위치
        target: /app/bind_mount # 로컬 디렉토리와 연결될 container 내부의 디렉토리 위치

이제 docker-compose.yml 이 있는 디렉토리에서 하기 명령어를 실행하면 된다.

docker-compose up -d

-d 는 daemon 을 의미하며, 백그라운드로 실행된다.
문제없이 실행된다면 하기 명령어를 통해 container 상태를 확인할 수 있다.

docker ps

문제가 발생하여 제대로 동작하지 않는다면 하기 명령어를 통해 확인할 수 있다.

docker logs 컨테이너명
또는
docker-compose logs

.
.
.
docker-compose up 명령어를 실행하면 docker-compose.yml 의 내용대로 실행하게 되는데,
우선 build: 에 기입한 위치에 있는 Dockerfile 을 통해 우선 docker image 를 build 하게된다.
이를 사용해 container 를 생성하고, ports: 에 입력한대로 포워딩 되는것이다.
.
.
.
추가로 마운팅을 사용하게 되면 호스팅(로컬) 서버의 디렉토리를 docker container 내부에 있는 디렉토리와 연결할 수 있는데 여기에도 종류가 있다.
상기 yml 에 기입한 방식은 바인드 마운트 방식으로 로컬 디렉토리를 docker container 내부와 연결하는 방식이고,
볼륨 방식은 docker volume create 로 볼륨을 생성해서 docker container 와 이 볼륨을 연결하는 방식이다.
두개의 장단점이 있지만 자세히는 아직 알지 못하나, 한가지 바인드 마운트 방식의 경우
외부(로컬)에서 파일을 접근할 수 있다는 것이다.
물론 docker cp 명령어를 통해 파일을 외부로 복사해 올 수 있지만 번거롭기도 하고
이 방식을 사용하면 굳이 Dockerfile 에서 build 폴더를 container 내부로
옮기지 않고 마운팅 폴더 경로만 지정하고
마운팅된 폴더에 이 build 를 위치시키는 방법도 되지 않을까 싶다.

profile
JavaScript, Node.js, Express, React, React Native, GraphQL, Apollo, Prisma, MySQL

0개의 댓글