-
Notifications
You must be signed in to change notification settings - Fork 0
[BE] CI CD
Continuous Integration (지속적인 통합) / Continuous Deployment (지속적인 배포)
-
client
와server
각각에 Dockerfile을 작성하여 애플리케이션이 컨테이너에서 실행될 수 있도록 설정합니다.
FROM node:20 AS build
WORKDIR /app
COPY packages/client/package.json .
RUN yarn install
COPY packages/client .
RUN yarn build
FROM nginx:alpine
COPY --from=build /app/dist /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
고성능의 웹 서버이자 리버스 프록시 서버, 로드 밸런서, HTTP 캐시로 널리 사용되는 소프트웨어입니다. 웹 서버로 주로 사용되며, 많은 웹사이트에서 HTTP 요청을 처리하기 위해 사용됩니다. 다음은 Nginx의 주요 기능과 특징입니다.
- Nginx는 웹 서버로서 정적 파일(HTML, CSS, JavaScript, 이미지 등)을 클라이언트에게 서빙하는 데 사용됩니다.
- Apache와 같은 다른 웹 서버와 비교할 때, Nginx는 비동기 방식으로 요청을 처리하므로 더 많은 트래픽을 처리할 수 있습니다.
- Nginx는 리버스 프록시 서버로도 많이 사용됩니다. 클라이언트의 요청을 받아서 내부 서버에 전달하고, 그 응답을 다시 클라이언트에게 반환합니다.
- 로드 밸런싱: 여러 서버 간에 트래픽을 분배하는 기능을 통해 서버의 부하를 분산할 수 있습니다.
- Nginx는 여러 서버에 걸쳐 트래픽을 분배하는 로드 밸런서 역할을 할 수 있습니다. 이를 통해 트래픽을 효율적으로 처리하고, 하나의 서버에 트래픽이 집중되는 문제를 방지할 수 있습니다.
- Nginx는 캐싱 기능을 제공하여, 자주 요청되는 콘텐츠를 메모리에 저장하고, 동일한 요청에 대해서는 캐시된 데이터를 제공함으로써 서버의 부하를 줄이고 응답 속도를 높입니다.
- Nginx는 SSL 인증서를 관리하고 HTTPS 요청을 처리하는 데에도 사용됩니다. 이를 통해 SSL 종료를 할 수 있으며, 내부 서버와의 연결은 HTTP로 할 수 있습니다.
- Nginx는 비동기 이벤트 기반 아키텍처를 사용하여 높은 성능을 발휘합니다. 즉, 여러 클라이언트의 요청을 동시에 처리할 수 있으며, 리소스를 효율적으로 사용합니다. 이로 인해 많은 트래픽을 처리해야 하는 대규모 서비스에서도 사용됩니다.
- 빠른 성능: 비동기 방식으로 높은 트래픽을 처리할 수 있음.
- 가벼운 리소스 사용: 낮은 메모리와 CPU 사용으로 리소스를 효율적으로 처리.
- 설정 파일의 간단한 구성: 설정이 간단하고 직관적인 방식으로 작성됨.
- 높은 확장성: 로드 밸런싱과 캐싱, 리버스 프록시 기능을 통해 웹 애플리케이션의 성능을 향상시킬 수 있음.
FROM node:20
WORKDIR /app
# 패키지 설치
COPY packages/server/package.json .
RUN yarn install
# 소스 파일 복사
COPY packages/server .
# 포트 노출
EXPOSE 3000
# 컨테이너 시작 시 애플리케이션 실행
CMD ["yarn", "start:prod"]
- start:prod : NODE_ENV를 production으로 설정 > 환경변수가 prod용으로 바뀜
-
docker-compose.yml
을 사용하여 프론트엔드, 백엔드, MySQL 컨테이너를 함께 실행합니다. - 네트워크를 공유하고 각 컨테이너가 필요한 환경 변수를 설정합니다.
services:
server:
build:
# 현재 디렉토리를 빌드 컨텍스트로 사용한다.
context: .
# 사용할 도커 파일 이름(빌드 전 이름)
dockerfile: dockerfile-server
# 이미지를 빌드하고 컨테이너 생성 시 사용할 이름
image: seunggwan/corinee-server
# 3000포트(앞)를 외부에 열고, 해당 포트를 컨테이너 내의 3000포트(뒤)에 매핑
# localhost:3000으로 요청을 보내면 앞의 포트에서 받아서 뒤의 포트에 매핑해준다.
ports:
- "3000:3000"
# 같은 service내의 컨테이너들이 상호 연결되는 네트워크를 설정하는 부분
networks:
- app-network
client:
build:
context: .
dockerfile: dockerfile-client
image: seunggwan/corinee-client
ports:
- "80:80"
# server가 먼저 실행 된 후 client가 실행된다.
# client가 server에 의존하는 관계 설정
depends_on:
- server
networks:
- app-network
networks:
app-network:
- 프로젝트 루트에서 다음 명령어를 실행하여 모든 컨테이너를 시작합니다.
docker-compose up --build
-
http://localhost
에서 프론트엔드(React)를 확인할 수 있고, API 요청은 백엔드(NestJS)로 전달됩니다. - 데이터베이스는 MySQL 컨테이너에서 실행되며,
server
와 연결된 상태로 API 작업이 가능합니다.
# Docker 설치
sudo apt update
sudo apt install -y docker.io
# Docker Compose 설치
sudo curl -L "https://github.com/docker/compose/releases/download/2.0.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
로컬에서 작성한 docker-compose.yml
, Dockerfile, 소스 코드 등을 가상 서버로 전송합니다. scp
명령어 또는 Git을 사용할 수 있습니다.
# 로컬에서 가상 서버로 프로젝트 파일 전송
scp -r /path/to/your/project user@your-server-ip:/home/user/project
$ scp -r * [email protected]:project
서버에서 docker-compose.yml
파일이 있는 디렉터리로 이동한 후, Docker Compose를 사용하여 컨테이너를 빌드하고 실행합니다.
# 프로젝트 디렉터리로 이동
cd /project
# Docker Compose로 컨테이너 빌드 및 실행
sudo docker-compose up --build -d
가상 서버의 IP 주소와 설정한 포트를 통해 프론트엔드와 백엔드 애플리케이션이 실행되는지 확인합니다.
- 예:
http://your-server-ip
에서 프론트엔드 확인 - 예:
http://your-server-ip:3000
에서 백엔드 API 확인 (백엔드를 다른 포트로 설정한 경우)
가상환경에서 docker로 배포성공
docker-compose.yaml
에 명시된 모든 서비스 컨테이너를 생성하고 실행시켜주는 명령어
모든 서비스 컨테이너를 한 번에 정지시키고 삭제한다.
이미지를 다시 빌드해서 컨테이너를 띄워야 하는데 docker-compose
는 멍청해서 이미지 이름만 같아도 새롭게 빌드하지 않는다. 기존 이미지와 컨테이너를 stale 시키고 다시 빌드하기 위해 —build
옵션을 사용한다.
flowchart TD
subgraph DockerCompose
FE[프론트엔드 컨테이너]
BE[백엔드 컨테이너]
end
User[사용자]
FE --- BE
BE --> DB[(데이터베이스)]
User -->|HTTP 요청| FE
FE -->|API 요청| BE
Dockerfile을 build로 이미지 생성 → Dockerhub에 이미지 push → scp로 docker-compose 이동 → Dockerhub에서 이미지 다운로드 → 컨테이너 생성 후 실행
name: Build and Deploy
# 워크플로 트리커
on:
push:
branches:
- dev-be
- dev-fe
pull_request:
branches:
- main
# job 설정
jobs:
build_and_deploy:
runs-on: ubuntu-latest
steps:
# 저장소의 코드를 가져오는 과정
- name: Checkout repository
uses: actions/checkout@v4
# Docker hub 로그인
- name: Docker Hub login
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_PASSWORD }}
# Docker 이미지 생성 및 hub에 업로드
- name: Build and Push Docker images
run: |
docker build -t ${{ secrets.DOCKERHUB_USERNAME }}/corinee-server -f ./dockerfile-server .
docker push ${{ secrets.DOCKERHUB_USERNAME }}/corinee-server
docker build -t ${{ secrets.DOCKERHUB_USERNAME }}/corinee-client -f ./dockerfile-client .
docker push ${{ secrets.DOCKERHUB_USERNAME }}/corinee-client
# scp로 compose.yml 이동
- name: Send files & deploy script
uses: appleboy/scp-action@master
with:
host: ${{ secrets.SSH_HOST }}
username: ${{ secrets.SSH_USERNAME }}
password: ${{ secrets.SSH_PASSWORD }}
port: ${{ secrets.SSH_PORT }}
source: ${{ secrets.DOCKER_IMAGE }}
target: /corinee
overwrite: true
# hub에서 이미지 다운로드 후 배포
- name: Docker run
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.SSH_HOST }}
username: ${{ secrets.SSH_USERNAME }}
password: ${{ secrets.SSH_PASSWORD }}
port: ${{ secrets.SSH_PORT }}
script: |
docker pull ${{ secrets.DOCKERHUB_USERNAME }}/corinee-server
docker pull ${{ secrets.DOCKERHUB_USERNAME }}/corinee-client
cd /corinee
# 기존에 실행 중인 컨테이너들을 중지하고 정리
# 관련된 네트워크, 볼륨, 이미지등을 삭제
docker-compose down
# 새로운 상태로 서비스를 실행
# -d는 detached모드를 의미하며 백그라운드에서 실행하겠다는 뜻
docker-compose up -d
- name: Generate Error Report
if: failure()
run: |
echo "Deployment Report" > report.txt
echo "===================" >> report.txt
echo "Commit SHA: ${{ github.sha }}" >> report.txt
echo "Branch: ${{ github.ref }}" >> report.txt
echo "Deployment Status: Failed" >> report.txt
echo "Error Details: ${{ job.status }}" >> report.txt
echo "===================" >> report.txt
cat report.txt
- name: Upload Error Report
if: failure()
uses: actions/upload-artifact@v3
with:
name: deployment-error-report
path: report.txt
yaml
코드 복사
name: Build and Deploy
on:
push:
branches:
- dev-be
- dev-fe
pull_request:
branches:
- main
-
name: Build and Deploy
: 이 워크플로우의 이름입니다. -
on
: 이 워크플로우가 실행되는 조건을 정의합니다.-
push
:dev-be
또는dev-fe
브랜치에 푸시될 때 실행됩니다. -
pull_request
:main
브랜치로의 풀 리퀘스트가 생성될 때 실행됩니다.
-
jobs:
build_and_deploy:
runs-on: ubuntu-latest
-
jobs
: 워크플로우에서 수행할 작업들을 정의합니다. -
build_and_deploy
: 작업의 이름이며,ubuntu-latest
환경에서 실행됩니다.
각 단계는 특정 작업을 수행합니다.
-
코드 체크아웃
name: Checkout repository uses: actions/checkout@v4
- 저장소의 코드를 가져오는 단계입니다. 현재 브랜치의 코드를 체크아웃합니다.
-
Docker Hub 로그인
- name: Docker Hub login uses: docker/login-action@v1 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_PASSWORD }}
- Docker Hub에 로그인하여 이미지를 푸시할 수 있도록 합니다. 사용자 이름과 비밀번호는 GitHub Secrets에 저장되어 있습니다.
-
Docker 이미지 빌드 및 푸시
- name: Build and Push Docker images run: | docker build -t ${{ secrets.DOCKERHUB_USERNAME }}/corinee-server -f ./dockerfile-server . docker push ${{ secrets.DOCKERHUB_USERNAME }}/corinee-server docker build -t ${{ secrets.DOCKERHUB_USERNAME }}/corinee-client -f ./dockerfile-client . docker push ${{ secrets.DOCKERHUB_USERNAME }}/corinee-client
- 서버 및 클라이언트 이미지를 빌드하고 Docker Hub에 푸시합니다. 각 이미지에 대해 Dockerfile을 지정하여 빌드합니다.
-
파일 전송 및 배포 스크립트 실행
- name: Send files & deploy script uses: appleboy/scp-action@master with: host: ${{ secrets.SSH_HOST }} username: ${{ secrets.SSH_USERNAME }} password: ${{ secrets.SSH_PASSWORD }} port: ${{ secrets.SSH_PORT }} source: ${{ secrets.DOCKER_IMAGE }} target: /corinee overwrite: true
- SCP를 사용하여 필요한 파일을 원격 서버로 전송합니다. 이때 SSH 정보를 GitHub Secrets에서 가져옵니다.
-
Docker 컨테이너 실행
- name: Docker run uses: appleboy/ssh-action@master with: host: ${{ secrets.SSH_HOST }} username: ${{ secrets.SSH_USERNAME }} password: ${{ secrets.SSH_PASSWORD }} port: ${{ secrets.SSH_PORT }} script: | docker pull ${{ secrets.DOCKERHUB_USERNAME }}/corinee-server docker pull ${{ secrets.DOCKERHUB_USERNAME }}/corinee-client cd /corinee docker-compose down docker-compose up -d
- 원격 서버에 SSH로 접속하여 Docker 이미지를 풀(pull)하고, Docker Compose를 사용하여 기존 컨테이너를 중지하고 새로 시작합니다.
-
오류 보고서 생성
- name: Generate Error Report if: failure() run: | echo "Deployment Report" > report.txt echo "===================" >> report.txt echo "Commit SHA: ${{ github.sha }}" >> report.txt echo "Branch: ${{ github.ref }}" >> report.txt echo "Deployment Status: Failed" >> report.txt echo "Error Details: ${{ job.status }}" >> report.txt echo "===================" >> report.txt cat report.txt
- 배포가 실패한 경우 오류 보고서를 생성합니다. 커밋 SHA, 브랜치, 배포 상태 및 오류 세부 정보를 포함합니다.
-
오류 보고서 업로드
- name: Upload Error Report if: failure() uses: actions/upload-artifact@v3 with: name: deployment-error-report path: report.txt
- 생성된 오류 보고서를 아티팩트로 업로드합니다. 나중에 CI/CD 파이프라인에서 분석할 수 있도록 합니다.