
서론
- 우리 프로젝트의 front는 Nextjs로 되어있고, back은 Springboot로 되어있다. 앞에 proxy server로 Nginx를 두었다.
- 각 어플리케이션마다 도커 이미지로 만들어 하나의 명령어로 관리하고 싶었다.
- 그 과정에서 참 많은 일들이 있었다.
- 순 개발시간은 50시간이 넘어갔고, 기간으로는 2.5주 정도 걸린 것 같다.
- 여태까지의 실패하고 좌절하고 다시 시작하고 여러 사람들의 도움으로 완성하는 과정을 설명하겠다.
본론
- 사실 처음에는 Nginx가 무엇인지, 왜 붙어야하는지 이해하지 못했는데 같은 회사 동료분께서 보안은 절대적으로 중요하며 내부포트(3000, 8080)를 개방하는 것이 아니라 외부포트(80) 하나로만 통신을 해야한다고 말씀해주셔서 도입하게 되었다.
- 일단 지금 상태로는 back-end(Springboot)는 Jenkins를 이용하여 CICD를 끝내둔 상태이다.
- 간략하게 설명하자면
- Code를 Github에 Push
- Jenkins Webhook으로 Github에 Push됨을 감지하여 Commit 내역을 가져온다.
- Jenkins 안에서 도커 이미지를 빌드하고 도커 허브에 푸쉬해둔다.
- EC2 접속 정보를 받아서 서버에 도커 허브에 푸쉬한 도커 이미지를 Pull 받아온다.
- 그 후 docker run 명령어를 이용하여 도커 컨테이너를 띄운 상태이다.
- 간략하게 설명하자면
- back-end는 이미 CICD가 완료된 상태이므로 잠시 냅두고 front-end에 대한 CICD를 작업할 예정이다.
실행과정
- front-end(Nextjs)도 back-end에서 작업했던 것과 마찬가지로 root directory에 Dockerfile을 생성한다.
- 생성한 Dockerfile 안에는 밑과 같이 작성을 하였다.
# Node.js 이미지로 사용
FROM node:14-alpine as build
# 작업 디렉토리를 /app으로 설정한다
WORKDIR /app
# 컨테이너에 package.json와 package-lock.json 파일을 복사합니다.
COPY package*.json ./
# npm 설치
RUN npm install
COPY . .
# Next.js를 빌드한다
RUN npm run build
# 새로운 단독의 nginx 이미지 생성
FROM nginx
# 오픈할 포트를 적어둔다
EXPOSE 3000
# default.conf을 /etc/nginx/conf.d/ 경로에 있는 default.conf에 복사한다.
COPY ./default.conf /etc/nginx/conf.d/default.conf
# nextjs build한 결과물을 /usr/share/nginx/html에 복사한다.
COPY --from=build app/out /usr/share/nginx/html
- 한줄한줄 주석처리는 해두었지만 내가 처음 경험했을 때 어려웠던 부분만 디테일하게 설명을 하겠다.
- 지금 위 dockerfile을 보면 FROM이 두개 있는 것을 확인할 수 있다.
- FROM node → node 이미지를 가져와서 사용한다는 뜻
- FROM nginx → nginx 이미지를 가져와서 사용한다는 뜻
- 위 두가지가 있는데 이미지를 두개를 만드는 것이 아니고 FROM 기준으로 마지막에 있는 stage작업이 도커 이미지로 생성이 된다. → Multi stage
- 이것에 이점으로는 도커 이미지를 줄일 수 있다.
- COPY ./default.conf /etc/nginx/conf.d/default.conf
- 해당 부분은 dockerfile이 위치한 경로 기준으로 동일한 계층에 있는 default.conf 파일을 빌드 될 이미지안에 nginx 설정 중 /etc/nginx/conf.d/default.conf에 복사한다.
- 처음에 나는 당연하게도 nginx 이미지 파일 받아오니까 nginx 이미지에 들어있는 줄 알았다…당연히 아니고 이건 nextjs를 이미지로 만들기위한 도커파일 이므로 nextjs 이미지에 복사되는 부분이다! 도커에 대한 지식이 부족하여 며칠을 허비하였다…
- default.conf → nextjs application안에 있는 파일
server {
listen 3000; // 내부 포트 개방
location / {
root /usr/share/nginx/html; // 위에서 빌드한 파일 위치 지정
index main.html;
try_files $uri $uri/ /main.html;
}
}
- COPY --from=build app/out /usr/share/nginx/html
- 위 코드중 --from=build는 첫번째 줄 코드인 node에서 별칭을 둔 build와 동일하게 사용하면 된다.
- 코드 중간에 npm run build를 하게 되면 nextjs는 .next 라는 폴더 하나를 생성하는데 out은 어디서 나왔을까? 라는 생각을 했었다. 알고보니 package.json에 scripts 중 build에 next export를 추가 하게 되면 out이라는 파일이 생성되고 바로 위 COPY와 같이 nextjs 이미지에 복사된다.
"scripts": {
"dev": "next dev",
"build": "next build && next export",
"start": "next start",
"lint": "next lint"
}
3. Nextjs CICD
- Nextjs CICD는 Github Action으로 작성하였다.
- 그 이유는 Jenkins를 사용해본 결과 접근성이 어려움점이 있었다. 하지만 Github Action은 좀 더 직관적이며 가격적인 측면에서도 메리트가 있었다.
name: Docker
on:
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [16.14.x]
steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- name: web docker build and push
run: |
docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }}
docker build -t ${{ secrets.DOCKER_REPO }}/yeh_front .
docker push ${{ secrets.DOCKER_REPO }}/yeh_front
- name: aws ec2 docker image pull
uses: fifsky/ssh-action@master
with:
command: |
docker pull ${{ secrets.DOCKER_REPO }}/yeh_front
host: ${{ secrets.HOST }}
user: ${{ secrets.USERNAME }}
key: ${{ secrets.KEY }}
- Github Action을 한번이라도 써본 사람들이라면 위 내용이 그렇게 어렵지 않을 것이다.
- 간략하게 설명하자면 :
- ubuntu서버에서 실행한다.
- node-version을 지정하여 사용하겠다.
- 도커 로그인, 빌드, 푸쉬를 진행하겠다.
- ec2 서버에 접속하여 푸쉬된 도커 이미지를 pull 받겠다.
- 이제 github에 code를 푸쉬하면 ec2 서버에 front-end 이미지가 생성된걸 볼 수 있다.

4. EC2 서버에 nginx 이미지 pull 받기.
- EC2 서버에서 docker pull nginx 명령어를 입력한다.
- 입력하게 되면 가장 최신버전에 nginx 이미지를 가져오게 된다.

5. docker-compose.yaml 파일을 작성한다.
- 아래 명령어를 입력해서 docker-compose를 다운한다.
sudo curl -L "https://github.com/docker/compose/releases/download/1.27.4/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
- 아래 명령어를 입력해서 docker-compose.yaml 파일을 만든다.
vi docker-compose.yaml
- 아래와 같이 docker-compose.yaml 파일을 작성한다.
version: '3'
services:
web:
container_name: web
image: {docker ID}/web
expose:
- 8080
ports:
- 8080:8080
next:
container_name: front
image: {docker ID}/front
expose:
- 3000
nginx:
container_name: nginx
image: nginx:latest
restart: always
volumes:
- ./conf/nginx.conf:/etc/nginx/conf.d/nginx.conf
ports:
- 80:80
- 443:443
depends_on:
- web
- next
- 위 docker-compose로 띄우는 컨테이너는 총 3개로 web(springboot), front(nextjs), nginx(proxy server)이다.
- 특이한 점은 next 컨테이너를 보면 port가 열려있지 않은 걸 볼수 있다.
- 왜냐하면 이미 nextjs는 dockerfile에서 빌드할 때 nginx 설정에 3000포트를 개방해줬기 때문이다.
- 그리고 nginx 컨테이너에 보면 volumes로 기존 ec2 서버에 있는 nginx.conf 파일을 nginx 실행 시에 /etc/nginx/conf.d/nginx.conf에 복사해온다.
- 처음에 헷갈렸던 부분은 ./conf/nginx.conf가 nextjs 어플리케이션 안에 들어가있어야하는 파일인 줄 알았다..내가 생각해도 너무 무지했다..
- conf/nginx.conf → ec2에서 docker-compose.yaml파일과 같은 계층에 conf 디렉토리를 만들고 그 안에 nginx파일을 생성한다. 위치는 자기 마음대로 가능하다.
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;
# Load dynamic modules. See /usr/share/doc/nginx/README.dynamic.
include /usr/share/nginx/modules/*.conf;
events {
worker_connections 1024;
}
http {
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 4096;
include /etc/nginx/mime.types;
default_type application/octet-stream;
include /etc/nginx/conf.d/*.conf;
server {
listen 80;
server_name devyeh.com www.devyeh.com;
# redirect https setting
if ($http_x_forwarded_proto != 'https') {
return 301 https://$host$request_uri;
}
location / {
proxy_pass http://front: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;
proxy_read_timeout 300;
proxy_connect_timeout 300;
proxy_send_timeout 300;
}
location /api {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header HOST $http_host;
proxy_set_header X-NginX-Proxy true;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://web:8080;
proxy_redirect off;
}
}
}
- 아마 처음보면 1도 이해가 안 될 것이다. 제가 그랬거든요 머쓱;;
- 다 볼 필요는 없고 중간에 server하고 중괄호가 열리는 부분 부터 보면 된다.
- listen → 내가 사용할 포트이다. 우린 80포트를 열거니까 80만 적어둔다. 여기서 잠깐 443 포트를 안 여는 이유는 이미 aws acm에서 80포트를 443포트로 리다이렉션 해주기에 따른 설정을 안 해줘도 된다.
- server_name → 사용하고 있는 도메인이나 도메인이 없다면 퍼블릭 ip4를 입력하면 된다.
- location → 이건 우리가 흔히 생각하는 url이라고 생각하면 편하다.
- location / → 이거는 url에 뭐가오든 front로 통신하라는 뜻이다.
- location /api → 이거는 url에 /api/~~~ 로 시작하는 건 back으로 통신하라는 뜻이다.
6. 이제 설정할 파일은 다 끝났다.
- 아래 명령어로 docker image들을 실행시키자.
docker-compose up -d
결론
- 막상 회고를 작성하니 별게 없다고 느껴진다.
- 그만큼 내가 발전했다는 거라 생각하고 위안을 하는중이다..
- 중간마다 안 될 때 처음부터 다시 시작하거나 블로그에 나와있는 방법이 아닌 새로운 방법도 많이 시도했다.
- 새로운 도전에는 :
- nginx 이미지에 설정파일이 안 담겨지니(nextjs 이미지에 담겨지는게 맞는데 몰랐을 땐) nginx 이미지를 사용하지 않고 ec2 서버내에 직접 설치해서 하는 방식을 채택하였다.
- 근데 여기서 또 문제는 nginx 설정파일들 중 최상위에 위치한 nginx.conf 파일을 수정할 때 생긴 일이다.
- 기존에는 aws route53에서 도메인을 산 뒤 aws acm(ssl 인증방식)을 사용하여 https 인증과 동시에 로드밸런싱을 통해 80 포트로 접속시에 443 포트로 redirect 되도록 만들었다.
- 근데 나는 또 다시 ssl 인증은 nginx에서 해줘야하는 걸로 인지하여 nginx.conf 파일에 listen 443을 만들어서 letsencrypt를 이용하여 ssl 인증을 받아주었다……
- 이미 aws acm에서 인증을 받은 터라 굳이 두번 받을 이유도 없을 뿐더러 인증이 중복되어 https 인증이 되지 않았다..앞으로는 내가 어떤 기술을 도입했는지 계속 기억하고 이 기술에 대한 목적성을 갖고 있는게 중요하다 생각했다…
- 위 방법 ec2에 nginx를 설치하여 하는 방법이 성공했으니 이제 nginx 이미지를 받아서 오는 걸 다시 도전하였다.
- 그 전에 비해서 dockerfile에 들어가는 명령어 docker-compose에 사용되는 기능들을 이해하고 시작하니 그렇게 어렵지 않게 해결을 하였다. (3일 소요)
- 다시금 3개의 이미지를 만들어서 docker-compose up으로 한번에 띄우는 걸 성공했다.
- nginx 이미지에 설정파일이 안 담겨지니(nextjs 이미지에 담겨지는게 맞는데 몰랐을 땐) nginx 이미지를 사용하지 않고 ec2 서버내에 직접 설치해서 하는 방식을 채택하였다.
- 여태 하면서 프론트의 이해와 백엔드의 이해 그리고 웹 통신이 돌아가는 것에 대해 이해하였다.
- 많은 시간을 소비하였지만 전체적인 틀을 이해하는데 이만한 경험이 없다고 생각한다.
'DevOps' 카테고리의 다른 글
| AWS S3 Bucket (1) | 2023.02.22 |
|---|---|
| Github Action CI/CD (0) | 2023.02.21 |
| AWS CM, ELB, Nginx use HTTPS server building (0) | 2023.01.25 |
| __AWS Route 53 DNS and EC2 linkage__ (0) | 2023.01.25 |
| AWS EC2 mariadb download (1) | 2023.01.25 |