새소식

Js/express

express - Docker + github action CI/CD 구현

  • -

개요

express.js로 토이 프로젝트를 진행하던 중 cicd를 구현하려는데 마땅한 글을 찾지 못해 직접 구현한 후 정리하게 되었다.


준비물

1. ssh 접속이 가능하고 docker, docker-compose가 정상 설치된 서버
2. docker hub 계정
3. 프로젝트 repository

Github Repository Secrets 등록

repository -> Settings -> Secrets and Variables -> Actions

1. SERVER_IP - 서버 ip
2. SERVER_USER - SSH 사용자명
3. SSH_PRIVATE_KEY - 서버 SSH 접속 시 사용하는 private key
4. DOCKER_USERNAME - docker hub 유저명
5. DOCKER_PASSWORD - docker hub 비밀번호
6. PROJECT_NAME - 프로젝트 명(소문자와 '-'으로만 구성 Ex. korea-quote)

프로젝트 디렉토리 구조

project
├── .github/workflows
├── docker-compose.yml
├── Dockerfile
├── package.json
└── server.js

서버 authorized_keys 설정

cd ~.ssh
vi authorized_keys ## 서버 SSH 접속 시 사용하는 public key 복붙 후 저장
chmod 600 authorized_keys

Github Action workflow 등록

name: CI/CD Pipeline

# 파이프라인이 실행될 트리거를 설정
on:
  push:
    branches:
      - master # master 브랜치에 push 이벤트 발생 시 실행
  pull_request:
    branches:
      - master # master 브랜치에 대한 pull request 이벤트 발생 시 실행

jobs:
  build:
    runs-on: ubuntu-latest # 빌드 작업을 실행할 환경 설정 (최신 우분투)

    steps:
    - name: Checkout repository # GitHub repository 체크아웃 
      uses: actions/checkout@v4   

    - name: Set up Docker Buildx # Docker Buildx 설정
      uses: docker/setup-buildx-action@v3

    - name: Login to DockerHub # DockerHub 로그인
      uses: docker/login-action@v3
      with:
        username: ${{ secrets.DOCKER_USERNAME }} # GitHub Secrets DockerHub 사용자명 사용
        password: ${{ secrets.DOCKER_PASSWORD }} # GitHub Secrets DockerHub 비밀번호 사용

    - name: Build Docker image # Docker 이미지 빌드
      run: |
        docker build . -t ${{ secrets.DOCKER_USERNAME }}/${{ secrets.PROJECT_NAME }}:${{ github.sha }} # Docker 이미지 빌드 및 태그
        docker tag ${{ secrets.DOCKER_USERNAME }}/${{ secrets.PROJECT_NAME }}:${{ github.sha }} ${{ secrets.DOCKER_USERNAME }}/${{ secrets.PROJECT_NAME }}:latest # latest 태그 추가

    - name: Push Docker image to Docker Hub # Docker hub에 이미지 push
      run: |
        docker push ${{ secrets.DOCKER_USERNAME }}/${{ secrets.PROJECT_NAME }}:${{ github.sha }} # 빌드된 이미지 push
        docker push ${{ secrets.DOCKER_USERNAME }}/${{ secrets.PROJECT_NAME }}:latest # latest 태그 이미지 push

  deploy:
    runs-on: ubuntu-latest # 배포 작업을 실행할 환경 설정 (최신 우분투)
    needs: build # build 작업이 성공적으로 완료된 후에만 실행

    steps:
    - name: Checkout repository # GitHub repository 체크아웃
      uses: actions/checkout@v4

    - name: Deploy to server # 서버에 SSH로 접속하여 배포 작업 수행
      uses: appleboy/ssh-action@v1.0.3
      with:
        host: ${{ secrets.SERVER_IP }} # GitHub Secrets 서버 IP 사용
        username: ${{ secrets.SERVER_USER }} # GitHub Secrets 서버 사용자명 사용
        key: ${{ secrets.SSH_PRIVATE_KEY }} # GitHub Secrets SSH 개인 키 사용
        port: 22 # SSH 접속 포트 (기본: 22)
        script: |
          sudo docker pull ${{ secrets.DOCKER_USERNAME }}/${{ secrets.PROJECT_NAME }}:${{ github.sha }} # 새로운 Docker 이미지 pull
          sudo docker stop ${{ secrets.PROJECT_NAME }} || true # 기존 Docker 컨테이너 중지 (오류 무시)
          sudo docker rm ${{ secrets.PROJECT_NAME }} || true # 기존 Docker 컨테이너 삭제 (오류 무시)
          sudo docker run -d -p 3000:3000 --name ${{ secrets.PROJECT_NAME }} ${{ secrets.DOCKER_USERNAME }}/${{ secrets.PROJECT_NAME }}:${{ github.sha }} # 새로운 Docker 컨테이너 실행

docker-compose.yml

version: '3.8'

services:
  web:
    build: .
    ports:
      - "3000:3000"
    volumes:
      - .:/usr/src/app
      - /usr/src/app/node_modules
    environment:
      - NODE_ENV=development

Dockerfile

# 베이스 이미지 설정
FROM node:20

# 작업 디렉토리 설정
WORKDIR /app

# 패키지 설치를 위해 package.json 및 package-lock.json 복사
COPY package*.json ./

# 패키지 설치
RUN npm install

# 어플리케이션 코드 복사
COPY . .

# 어플리케이션이 실행될 포트 설정
EXPOSE 3000

# 서버 실행
CMD ["node", "server.js"]

server.js (예시)

// server.js
const express = require('express');
const app = express();
const PORT = 3000;

app.get('/', (req, res) => {
  res.send('Hello World!');
});

app.listen(PORT, () => {
  console.log(`Server is running on http://localhost:${PORT}`);
});

마무리

정상적으로 진행 되었다면 http://ip:3000으로 접속 시 Hello World!라는 문구를 볼 수 있을 것입니다 :)

반응형
Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.