사실 배포 서버에 배포한지는 꽤 되었지만 이제야 작성한다...
mini-blog-front의 화면이 어느정도 나타나있고 실제 화면를 보고 싶어서 배포하게 되었다.
AWS EC2서버에 jenkins를 이미 설치 및 플로그인 세팅이 된 환경에서 글을 작성한다.
관련 내용이 필요하다면 아래 링크 시리즈를 참고하면 될거 같다.
https://velog.io/@pak4184/series/AWS
프로젝트 아키텍처는 아래 그림과 같이 구현했다.
일단 jenkins와 front repository를 연동하기 위해선 Github webook을 연동해야 한다.
front repository의 main
브랜치의 push 또는 merge 되었다면 jenkins로 신호를 주는 역할을 합니다.
Github webhook을 생성할 repository의 settings에 가셔서 좌측 메뉴의 Webhooks
를 클릭합니다. 그리고 Add webhook
을 클릭합니다.
Payload URL
: http://EC2-ipv4주소:8000/github-webhook/
Content type
: application/json
Secret
: 공란
Which events would you like to trigger this webhook?
: Just the push event.
Active
: 체크
위와 같이 세팅해주면 된다.
jenkins의 Credentials
에 front-end gitHub 계정을 등록해줍니다.
global
을 클릭합니다. 없다면 새롭게 만드시면 됩니다.
우측 상단의 Add Creditials
클릭
Kind
: Username with password
Scope
: (Jenkins, nodes...)
Username
: Github 계정 ID
Password
: Github 계정 ID
ID
: jenkins에서 등록한 Creditials을 구분하는 ID (작성해도 되고 안하면 자동 생성)
Description
: 설명
이렇게 입력하고 Create
버튼을 클릭해줍니다.
이와 똑같은 방법으로 Docker Hub
계정도 추가해주었습니다.
이런식으로 여러개를 생성해놓고 사용 중입니다.
이제 jenkins에 빌드가 될 project를 세팅해보도록 하겠습니다.
새로운 Item
클릭
Freestyle project로 생성해줍니다.
생성한 프로젝트의 Configuration
으로 이동해줍니다.
우선 GitHub project
체크박스를 클릭하여 본인 repository 주소를 입력해줍니다.
소스 코드 관리에서 Git
을 선택 후
본인 Repository URL
을 입력해주고 Creditials
에서 등록한 github 계정을 선택해줍니다.
Branches to build
에선 Github Webhook
이 감지될 브랜치를 작성해줍니다.
Webhook
이 정상적으로 작동할 수 있도록 위와 같이 선택해줍니다.
Docker Image로 빌드 후 Image를 Docker Hub
에 업로드 할거기 때문에 미리 등록해놨던 Docker Hub
계정을 Creditials
에서 가져와 사용할 준비를 해줍니다.
Build Steps
에서 Execute shell
와 Send files or execute commands over SSH
를 선택하여 추가해줍니다.
Execute shell
에선 jenkins로 빌드 후 Docker Image
로 만들고 Docker Hub
에 push하는 작업을 진행합니다.
따라서 Image로 빌드하기 전 프로젝트에 Dockerfile
을 작성해야 Dockerfile
의 내용을 바탕으로 Image를 만들어줍니다.
Dockerfile
은 루트 디렉토리에 작성해주면 됩니다.
# 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/dist /usr/share/nginx/html
# copy the custom nginx configuration file
COPY nginx.conf /etc/nginx/nginx.conf
EXPOSE 8080
CMD ["nginx", "-g", "daemon off;"]
위 스크립트를 설명하자면
이 스크립트는 Node.js 앱을 빌드하고, 그 결과를 Nginx 웹 서버에 제공하는 Docker 이미지를 만든다. 이 스크립트는 두 단계로 나뉘어 있다.
첫 번째 단계(build stage)에서는 Node.js 앱을 빌드한다.
node:18
이라는 Docker 이미지를 기반으로 새로운 빌드 환경을 만든다.- 작업 디렉토리를
/app
으로 설정하고,package*.json
파일들을 작업 디렉토리로 복사한다.- 그 다음에
yarn install
명령어를 실행해서 필요한 패키지들을 설치한다.- 앱의 모든 파일들을 작업 디렉토리로 복사한 다음,
yarn build
명령어를 실행해서 앱을 빌드한다.두 번째 단계(production stage)에서는 Nginx 웹 서버를 설정하고, 첫 번째 단계에서 빌드한 앱을 웹 서버에 추가한다.
nginx:stable-alpine
이라는 Docker 이미지를 기반으로 새로운 프로덕션 환경을 만든다.- 첫 번째 단계에서 빌드한 앱을
/usr/share/nginx/html
디렉토리로 복사한다.- 그리고 커스텀 Nginx 설정 파일을
/etc/nginx/nginx.conf
위치로 복사한다.- 웹 서버를 외부에서 접근할 수 있도록 8080 포트를 열어준다.
- 마지막으로
nginx -g "daemon off;"
명령어를 실행해서 Nginx 웹 서버를 시작한다.
위 설명에서 node:18
이 있는데 이 부분은 본인 node 버전에 맞게 작성을 해야한다.
그리고 nginx
설정 파일을 복사하기 때문에 nginx
파일을 작성해보자.
nginx
파일은 루트 디렉토리에 nginx.conf
파일을 작성해주면 된다.
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
server {
listen 8080; # 프로젝트가 실행될 포트 번호
server_name localhost;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
try_files $uri $uri/ /index.html; # 리액트 라우팅을 위한 설정
}
}
}
위 설정 파일은 Nginx 웹 서버의 작동 방식을 정의하는데, 아래와 같이 해석될 수 있다.
events
섹션:
worker_connections 1024;
: 하나의 worker 프로세스가 동시에 처리할 수 있는 연결 수를 1024로 설정한다.http
섹션:
include mime.types;
: MIME 타입 파일을 포함시킨다. 이 파일은 클라이언트에게 전송되는 파일의 종류를 알려준다.default_type application/octet-stream;
: 알려지지 않은 파일 타입에 대한 기본 MIME 타입을 설정한다.sendfile on;
: sendfile을 활성화해서 정적 파일을 더 빠르게 전송할 수 있게 한다.keepalive_timeout 65;
: keep-alive 연결의 timeout을 65초로 설정한다.server
섹션:
listen 8080;
: 웹 서버가 8080 포트에서 요청을 수신하도록 한다.server_name localhost;
: 서버의 이름을 localhost로 설정한다.location /
섹션:
root /usr/share/nginx/html;
: 웹 서버의 루트 디렉토리를 /usr/share/nginx/html
로 설정한다.index index.html index.htm;
: 웹 서버가 요청받을 때 우선적으로 찾는 파일을 index.html이나 index.htm으로 설정한다.try_files $uri $uri/ /index.html;
: 사용자로부터 요청받은 URI가 파일이나 디렉토리로 존재하지 않는 경우, /index.html
파일로 요청을 리디렉트한다. 이 설정은 SPA(Single Page Application)에서 라우팅을 처리하기 위해 필요하다.Dockerfile
과 nginx
파일 작성이 완료되었다면 빌드가 완료된 후 실행할 script를 작성해준다.
echo docker login
docker login -u ${DOCKER_USER} -p ${DOCKER_PASSWORD} docker.io
echo docker image build
docker build -t pak0426/toy-mini-blog-front:latest .
echo docker image push
docker push pak0426/toy-mini-blog-front:latest
# 기존 도커 이미지 삭제
no_tag_image_ids=$(docker images -f "dangling=true" -q)
if [ -z "$no_tag_image_ids" ]; then
echo "삭제할 이미지가 없습니다."
else
# 각 이미지 ID에 대해 순회하며 삭제하기
for image_id in $no_tag_image_ids; do
docker rmi $image_id
echo "이미지 $image_id 삭제 완료"
done
fi
위 파일은
1. 도커허브 로그인
2. 도커 이미지로 빌드
3. 도커 이미지 push
의 과정이다.
Send files or execute commands over SSH
에서 스크립트를 작성해주는 이유는 CI서버(통합 서버)
에서 빌드 작업이 완료된 후 CD서버(배포서버)
에서 실행할 스크립트를 보내주는 역할을 한다.
CI서버
의EC2 인스턴스
와CD서버
의EC2인스턴스
의 연결 방법은
Jenkins 빌드, DockerHub push 후 배포 서버에서 application 실행 방법
링크를 참고하면 된다.
# 이미지 이름과 태그
IMAGE_NAME="pak0426/toy-mini-blog-front"
TAG="latest"
# 새로운 이미지 pull
docker pull $IMAGE_NAME:$TAG
# 기존 컨테이너 stop 및 remove
docker stop toy-mini-blog-front
docker rm toy-mini-blog-front
# 새로운 이미지로 컨테이너 시작
docker run -d --name toy-mini-blog-front -p 80:8080 $IMAGE_NAME:$TAG
# 기존 도커 이미지 삭제
no_tag_image_ids=$(docker images -f "dangling=true" -q)
if [ -z "$no_tag_image_ids" ]; then
echo "삭제할 이미지가 없습니다."
else
# 각 이미지 ID에 대해 순회하며 삭제하기
for image_id in $no_tag_image_ids; do
docker rmi $image_id
echo "이미지 $image_id 삭제 완료"
done
fi
docker run -d --name toy-mini-blog-front -p 80:8080
에서 추가 설명하자면
docker run
: Docker 이미지를 기반으로 새로운 컨테이너를 생성하고 실행하는 명령어이다.
-d
: 이 옵션은 컨테이너를 백그라운드에서 실행하고, 컨테이너 ID를 출력하도록 설정하는 것이다.
--name toy-mini-blog-front
: 이 옵션은 생성되는 컨테이너의 이름을toy-mini-blog-front
로 지정하는 것이다.
-p 80:8080
: 이 옵션은 호스트의 80 포트와 컨테이너의 8080 포트를 연결하도록 설정하는 것이야. 이렇게 하면, 호스트 머신에서 80 포트로 들어오는 트래픽이 컨테이너의 8080 포트로 전달되게 된다. 즉 클라이언트는 80포트로 접근 가능하다.
$IMAGE_NAME:$TAG
: 이 부분은 실행하려는 Docker 이미지의 이름과 태그를 나타내는 부분이다.$IMAGE_NAME
과$TAG
는 환경 변수이며, 이 명령어가 실행되는 환경에 따라 다른 값을 가질 수 있다.
위와 같은 과정들로 세팅하였다면 저장을 해주면 된다.