이전 과정에서 Jenkins를 통해서 react 프로젝트를 배포할때에 nginx.conf파일과 Dockerfile을 작성해줘서 yarn을 통해 빌드를 진행함과 동시에 nginx를 실행하도록 진행했다.
이번에는 Certbot 인증서 요청을 통해 SSL 인증서를 받고 이를 nginx.conf 파일에 적용시켜서 Https를 이용해서 react 서비스에 접근할 수 있도록 진행해보자
발급 블로그에서 친절하게 설명해줍니다.
인증서를 발급받았으면 이를 nginx.conf에 적용시켜보자
인증서가 존재하는 디렉토리에 접근할 수 있는 코드 추가
ssl_certificate /etc/letsencrypt/live/j11a604.p.ssafy.io/fullchain.pem; # SSL 인증서 경로 ssl_certificate_key /etc/letsencrypt/live/j11a604.p.ssafy.io/privkey.pem; # SSL 키 경로
events {
worker_connections 1024; # 동시에 처리할 수 있는 최대 연결 수
}
http {
include mime.types; # MIME 타입을 정의하는 파일 포함
default_type application/octet-stream; # 기본 MIME 타입 설정
sendfile on; # 파일 전송을 최적화하기 위한 설정
keepalive_timeout 65; # 클라이언트와의 연결을 유지하는 시간
server {
listen 443 ssl; # 서버가 수신할 포트 번호
server_name j11a604.p.ssafy.io; # 서버 이름 설정
ssl_certificate /etc/letsencrypt/live/j11a604.p.ssafy.io/fullchain.pem; # SSL 인증서 경로
ssl_certificate_key /etc/letsencrypt/live/j11a604.p.ssafy.io/privkey.pem; # SSL 키 경로
location / {
root /usr/share/nginx/html; # 정적 파일이 위치한 루트 디렉토리
index index.html index.htm; # 기본 인덱스 파일
try_files $uri $uri/ /index.html; # 요청한 파일이 없을 경우 index.html로 포워딩 (리액트 라우팅 지원)
# gzip 압축 설정
gzip on; # gzip 압축 활성화
gzip_types text/css application/javascript application/json; # 압축할 MIME 타입
gzip_min_length 1000; # 최소 크기 이상의 파일만 압축
}
# 에러 페이지 설정 (예시)
error_page 404 /404.html; # 404 에러 발생 시 보여줄 페이지
location = /404.html {
internal; # 내부 요청으로만 접근 가능
}
}
}
현재 Nginx 구동방식
1. Jenkins에서 Webhook을 확인하고 Pipeline Script 실행
2. 프로젝트 Clone 후에 Dockerfile 실행
3. 실행중에 react 프로젝트 빌드하여 정적파일 추출
4. 추출된 정적파일들을 nginx/html로 이동
5. nginx.conf 파일을 nginx/nginx.conf로 이동
6. nginx 실행
위 순서로 nginx를 구동시키기 때문에 Certbot을 통해서 SSL 인증서를 발급받았지만 이 인증서는 EC2에 존재.
즉, Jenkins에서 EC2에 접근할 수 없기때문에 SSL 인증서를 받아오지 못해서 에러가 발생
시도 1 : jenkins를 컨테이너화할때 인증서가 존재하는 디렉토리에 볼륨으로 마운트를 진행한다.
jenkins의 docker-compose 파일에 인증서에 디렉토리 볼륨 설정
version: '3.8'
services:
jenkins:
image: jenkins/jenkins:lts
restart: unless-stopped
container_name: jenkins
ports:
- "8080:8080"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock" # Docker 소켓 공유
- "/home/ubuntu/jenkins-data:/var/jenkins_home" # Jenkins 데이터 저장
- ./jenkins-docker-install.sh:/jenkins-docker-install.sh
- "/etc/letsencrypt/live/j11a604.p.ssafy.io/fullchain.pem"
- "/etc/letsencrypt/live/j11a604.p.ssafy.io/privkey.pem"
user: "root"
Jenkins pipeline Script에서 마운트한 디렉토리에 접근해서 nginx.conf 파일에서 접근할 수 있도록 설정
결과 : 실패
볼륨을 마운트 해줄때 호스트의 어떤 파일 or 디렉토리를 클라이언트의 어떤 파일 or 디렉토리로 마운트를 걸어줄 지 설정을 해줘야했는데
volumes:
- "/etc/letsencrypt/live/j11a604.p.ssafy.io/fullchain.pem"
해당 코드로 마운트를 걸어줄 주소는 작성했지만 어디로 마운트를 보낼지를 작성하지 않아서 에러가 발생했습니다.
volumes:
- "/etc/letsencrypt/live/j11a604.p.ssafy.io/fullchain.pem:/etc/ssl/cert.pem"
시도 2 : Jenkins_docker-compose.yml 코드 수정
version: '3.8'
services:
jenkins:
image: jenkins/jenkins:lts
restart: unless-stopped
container_name: jenkins
ports:
- "8080:8080"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock" # Docker 소켓 공유
- "/home/ubuntu/jenkins-data:/var/jenkins_home" # Jenkins 데이터 저장
- ./jenkins-docker-install.sh:/jenkins-docker-install.sh
- "/etc/letsencrypt/live/j11a604.p.ssafy.io/fullchain.pem:/etc/ssl/cert.pem"
- "/etc/letsencrypt/live/j11a604.p.ssafy.io/privkey.pem:/etc/ssl/key.pem"
user: "root"
Docker Build 하는 과정에서 Docker는 빌드 컨텍스트를 사용합니다. 이과정에서 Dockerfile이 존재하는 해당 디렉토리와 그 하위 디렉토리만 접근할 수 있습니다. 즉, Jenkins 컨테이너에 존재하는 /etc/ssl/ 경로에 접근할 수 없어서 not found 가 발생한 것이다.
error mounting "/etc/ssl/cert.pem" to rootfs at "/etc/ssl/cert.pem": create mount destination for /etc/ssl/cert.pem mount: cannot mkdir in
와 같은 에러가 발생많고 많은 이상한 짓을 해도 해결하지 못했지만, 혹시나 하고 찾아보니 /etc/ssl/ 주소가 충돌이 나는 경우로 제대로 마운트를 연결하지 못해서 발생했던 에러...
steps {
// Run the Nginx container with the cert.pem and key.pem files directly mounted
sh 'ls -l /etc/ssl'
sh 'docker run --name frontend -d -p 443:443 -v /etc/ssl/cert.pem:/ssl/cert.pem -v /ssl/key.pem:/etc/ssl/key.pem qkrtprjs/frontend'
echo 'Run New image'
}
/etc/ssl/ 에서 /ssl로 바로 마운트를 연결해줘서 충돌나지 않는 디렉토리에 연결events {
worker_connections 1024; # 동시에 처리할 수 있는 최대 연결 수
}
http {
include mime.types; # MIME 타입을 정의하는 파일 포함
default_type application/octet-stream; # 기본 MIME 타입 설정
sendfile on; # 파일 전송을 최적화하기 위한 설정
keepalive_timeout 65; # 클라이언트와의 연결을 유지하는 시간
server {
listen 443 ssl; # 서버가 수신할 포트 번호
server_name j11a604.p.ssafy.io; # 서버 이름 설정
ssl_certificate /ssl/cert.pem; # SSL 인증서 경로
ssl_certificate_key /ssl/key.pem; # SSL 키 경로
location / {
root /usr/share/nginx/html; # 정적 파일이 위치한 루트 디렉토리
index index.html index.htm; # 기본 인덱스 파일
try_files $uri $uri/ /index.html; # 요청한 파일이 없을 경우 index.html로 포워딩 (리액트 라우팅 지원)
# gzip 압축 설정
gzip on; # gzip 압축 활성화
gzip_types text/css application/javascript application/json; # 압축할 MIME 타입
gzip_min_length 1000; # 최소 크기 이상의 파일만 압축
}
# 에러 페이지 설정 (예시)
error_page 404 /404.html; # 404 에러 발생 시 보여줄 페이지
location = /404.html {
internal; # 내부 요청으로만 접근 가능
}
}
# HTTP 요청을 HTTPS로 리디렉션
server {
listen 80;
server_name j11a604.p.ssafy.io;
return 301 https://$host$request_uri; # 모든 HTTP 요청을 HTTPS로 리디렉션
}
}
# build stage
FROM node:18 AS build-stage
WORKDIR /app
COPY package*.json ./
# Yarn is already installed
RUN yarn install
COPY . .
RUN yarn build
# production stage
FROM nginx:stable-alpine AS production-stage
COPY --from=build-stage /app/build /usr/share/nginx/html
# copy the custom nginx configuration file
COPY nginx.conf /etc/nginx/nginx.conf
#COPY /etc/ssl/cert.pem /etc/ssl/cert.pem
#COPY /etc/ssl/key.pem /etc/ssl/key.pem
EXPOSE 443
CMD ["nginx", "-g", "daemon off;"]
원인 :
Nginx를 통해서 HTTPS방식으로 프론트 서비스에 접근할 수 있도록 설정하려 했지만, HTTPS를 적용시킬때 SSL 접근을 위한 pem 키를 Nginx.conf 파일에 적용시켜야합니다. Jenkins에서 프론트 서비스를 배포하고있었고 ec2에 SSL 설정관련 pem 키를 저장시켰습니다. EC2에서 Jenkins를 실행시킬때 마운트를 걸어줘서 pem파일을 공유했고 다시 Jenkins에서 실행시킨 Nginx 컨테이너에 마운트를 걸어줘서 pem키에 접근할 수 있도록 설정했지만 파일이 공유되지 않는 문제가 발생했습니다.
결과 :
HTTPS의 적용이 급한 상황이기에 일단 NGINX를 통해서 프론트 서비스를 배포하고 EC2에서 NGINX를 또 띄워서 프론트로 들어오는 요청을 HTTPS로 전달하기 위해 SSL/TLS 인증서를 설정하고, HTTPS 요청을 처리하는 NGINX의 리버스 프록시 기능을 사용합니다.
jenkins 단에서 nginx를 통해 프론트 프로젝트 빌드, 배포 성공(8888 포트)
8888포트로 진행중인 프론트 구역에 https를 적용시키기 위해 ec2에 따로 nginx 실행
ec2에서 실행된 nginx는 443포트로 들어온 요청을 8888포트로 전송
api 주소(80)와 프론트 프로젝트 주소(8888)와 다르기 때문에 nginx.conf 파일에서 서로 분리해서 locations 설정해줌
프론트 환경변수 설정은 백엔드와 같은 과정으로 파이프라인으로 해결