컴공생의 발자취

Docker를 활용한 React 프로젝트 배포 (feat. Nginx, Jenkins) 본문

🐳 Docker

Docker를 활용한 React 프로젝트 배포 (feat. Nginx, Jenkins)

MNY 2024. 10. 15. 15:59
728x90
반응형
Keyword
  1. docker
  2. dockerfile
  3. compose(app, db, nginx)
  4. nginx(react deploy)
  5. nginx(reverse proxy)

 

Dockerfile 만들기 

프로젝트 디렉토리 구조

: 프로젝트의 루트 디렉토리에 위치

today-space-front/
├── package-lock.json
├── package.json
├── public
├── src/
│   └── main/
│       ├── component
│       ├── pages
│       └── redux
└── Dockerfile

 

Dockerfile 만들기

: 검색 keyword → react Dockerfile

# 1. Node.js 환경에서 빌드 수행
FROM node:14 AS build

WORKDIR /app

# package.json과 package-lock.json 복사
COPY package*.json ./

# 종속성 설치
RUN npm install

# 애플리케이션 소스 코드 복사
COPY . .

# 애플리케이션 빌드
RUN npm run build

# 2. Nginx 이미지에 빌드 결과물 포함
FROM nginx:alpine

# 빌드 결과물 복사
COPY --from=build /app/build /usr/share/nginx/html

# Nginx 실행
CMD ["nginx", "-g", "daemon off;"]

 

프로젝트 빌드 및 이미지 푸시

// 1. Docker image build
docker build -t [docker account ID]/[project name] .
ex) docker build -t ysy561356/today-space-front .
// -t, --tag: 빌드된 이미지에 태그를 지정. 이미지이름:태그 형식으로 사용.

// 2. Docker image push
docker push [docker account ID]/[project name]
ex) docker push ysy561356/today-space-front
/*
denied: requested access to the resource is denied 발생
참고 자료: https://nirsa.tistory.com/53
*/

 

1. 인텔리제이에서 터미널 열고 프로젝트 빌드(window & mac)

 

2 & 3. Dockerhub 확인

 

 프로젝트(Docker image) 업로드 

* 해당 과정은 EC2 인스턴스에서 진행

Docker image pull

Docker image pull:

docker pull [docker account ID]/[project name]:tag
ex) docker pull ysy561356/today-space-front:latest
// tag를 따로 지정하지 않았다면 기본적으로 latest

 

Docker image 확인:

docker images

 

 docker-compose 업데이트 

* 해당 과정은 EC2 인스턴스에서 진행

docker-compose 업데이트

docker-compose 업데이트:

vi docker-compose.yml // app.conf 파일 작성하기

i // 작성 모드

// docker-compose.yml 파일 내용

version: '3.8'

services:
  back:
    image: today-space-back:latest
    container_name: today-space-back
    env_file:
	    - ./config/.env
    ports:
      - "8080:8080"
    depends_on:
      - db

  db:
    image: postgres:14
    container_name: today-space-db
    env_file:
	    - ./config/.env
    volumes:
      - db_data:/var/lib/postgresql/data
    ports:
      - "5432:5432"
      
  front:
		image: today-space-front:latest
    container_name: today-space-front
	  ports:
		  - "80:80"
		  - "443:443"
	  volumes:
		  - ./data/nginx/app.conf:/etc/nginx/conf.d/default.conf
		  - ./data/certbot/conf:/etc/letsencrypt 
      - ./data/certbot/www:/var/www/certbot
	  depends_on:
		  - back

	certbot:
    image: certbot/certbot
    restart: unless-stopped
    volumes:
      - ./data/certbot/conf:/etc/letsencrypt
      - ./data/certbot/www:/var/www/certbot
    entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h & wait $${!}; done;'"

volumes:
  db_data:
  
------------------------------------------------------------------------------------
  
ESC(버튼) + :wq // 작성 모드 종료 후 저장하고 나가기
w // 저장
q // 나가기

 

 프론트 Jenkins(CI/CD ) 

프론트용 Jenkins pipeline 생성

  • Jenkins 세팅 참고

2024.10.11 - [🐳 Docker] - EC2의 Docker 안에 Jenkins Container 올리기 (feat. Webhook CI/CD 구축 & DooD)

 

EC2의 Docker 안에 Jenkins Container 올리기 (feat. Webhook CI/CD 구축 & DooD)

Keyword1. DooD2. docker network Flow1. 새로운 EC2 인스턴스 생성 -> 젠킨스만2. docker 설치3. docker 컨테이너 젠킨스 실행4. 서브 도메인 세팅5. 젠킨스 Webhook 연결6. 파이프라인 작성 후 배포 확인  EC2 인스

moonnight0.tistory.com

 

Jenkins Script 작성

pipeline {
    agent any

    environment {
		    // GitHub credentials
        GIT_CREDENTIALS_ID = 'github-token'
        GIT_URL = 'https://github.com/today-space/today-space-front.git'
        GIT_BRANCH = 'release'
        
        // EC2
        SSH_CREDENTIALS_ID = 'ec2-ssh-key'
        EC2_USER = 'ubuntu'
        EC2_DOMAIN = 'front.today-space.com'
       
        // Docker Hub credentials
        DOCKER_HUB_CREDENTIALS = 'docker-hub-credentials'
        DOCKER_HUB_REPO = 'ysy561356/today-space-front'
        DOCKER_IMAGE_TAG = 'latest'
        
        REACT_APP_API_URL='https://today-space.com'
        
        REACT_APP_KAKAO_AUTH_URL='https://kauth.kakao.com/oauth/authorize'
        REACT_APP_KAKAO_REST_API_KEY='6cd61cbdb917a1046bf437980b1e2ac9'
        REACT_APP_KAKAO_REDIRECT_URL='https://today-space.com/oauth/kakao'
        
        REACT_APP_NAVER_AUTH_URL='https://nid.naver.com/oauth2.0/authorize'
        REACT_APP_NAVER_REST_API_KEY='mgvICujU1zvQrtz_IQOA'
        REACT_APP_NAVER_REDIRECT_URL='https://today-space.com/oauth/naver'
        REACT_APP_NAVER_STATE='reactNaverSocialState'
        
        REACT_APP_GOOGLE_AUTH_URL='https://accounts.google.com/o/oauth2/v2/auth'
        REACT_APP_GOOGLE_REST_API_KEY='878062679505-oh34mk7fm5mpipprk9o139i8if40b3qf.apps.googleusercontent.com'
        REACT_APP_GOOGLE_REDIRECT_URL='https://today-space.com/oauth/google'
    }

    stages {
        stage('Clone Repository') {
            steps {
                script {
                    // GitHub 저장소에서 코드 클론
                    git credentialsId: "${GIT_CREDENTIALS_ID}", branch: "${GIT_BRANCH}", url: "${GIT_URL}"
                }
            }
        }
        stage('Prepare .env') {
            steps {
                script {
                    sh """
                    touch .env
                    echo REACT_APP_API_URL=${REACT_APP_API_URL} >> .env
                    echo REACT_APP_KAKAO_AUTH_URL=${REACT_APP_KAKAO_AUTH_URL} >> .env
                    echo REACT_APP_KAKAO_REST_API_KEY=${REACT_APP_KAKAO_REST_API_KEY} >> .env
                    echo REACT_APP_KAKAO_REDIRECT_URL=${REACT_APP_KAKAO_REDIRECT_URL} >> .env
                    echo REACT_APP_NAVER_AUTH_URL=${REACT_APP_NAVER_AUTH_URL} >> .env
                    echo REACT_APP_NAVER_REST_API_KEY=${REACT_APP_NAVER_REST_API_KEY} >> .env
                    echo REACT_APP_NAVER_REDIRECT_URL=${REACT_APP_NAVER_REDIRECT_URL} >> .env
                    echo REACT_APP_NAVER_STATE=${REACT_APP_NAVER_STATE} >> .env
                    echo REACT_APP_GOOGLE_AUTH_URL=${REACT_APP_GOOGLE_AUTH_URL} >> .env
                    echo REACT_APP_GOOGLE_REST_API_KEY=${REACT_APP_GOOGLE_REST_API_KEY} >> .env
                    echo REACT_APP_GOOGLE_REDIRECT_URL=${REACT_APP_GOOGLE_REDIRECT_URL} >> .env
                    """
                }
            }
        }
        stage('Build') {
            steps {
                script {
                    // Docker 이미지 생성
                    sh 'docker build -t ${DOCKER_HUB_REPO}:${DOCKER_IMAGE_TAG} .'
                }
            }
        }
        stage('Push Docker Image to Docker Hub') {
            steps {
                script {
                    // Docker Hub에 로그인
                    withCredentials([usernamePassword(credentialsId: DOCKER_HUB_CREDENTIALS, usernameVariable: 'DOCKER_USERNAME', passwordVariable: 'DOCKER_PASSWORD')]) {
                        sh 'echo $DOCKER_PASSWORD | docker login -u $DOCKER_USERNAME --password-stdin'
                    }

                    // Docker 이미지 push
                        sh "docker push ${DOCKER_HUB_REPO}:${DOCKER_IMAGE_TAG}"
                }
            }
        }
        stage('Deploy') {
            steps {
                script {
                    withCredentials([sshUserPrivateKey(credentialsId: "${SSH_CREDENTIALS_ID}", keyFileVariable: 'SSH_KEY')]) {
                        def sshKey = env.SSH_KEY
                        
                        // 권한 변경
                        sh 'chmod 400 ' + sshKey
                        
                        // docker-compose down
                        sh 'ssh -v -o StrictHostKeyChecking=no -i ' + sshKey + ' ${EC2_USER}@${EC2_DOMAIN} "docker-compose down"'
                        
                        // docker image pull
                        sh 'ssh -v -o StrictHostKeyChecking=no -i ' + sshKey + ' ${EC2_USER}@${EC2_DOMAIN} "docker pull ${DOCKER_HUB_REPO}:${DOCKER_IMAGE_TAG}"' // 이미지 명시

                        // 새로운 docker-compose 시작
                        sh 'ssh -v -o StrictHostKeyChecking=no -i ' + sshKey + ' ${EC2_USER}@${EC2_DOMAIN} "docker-compose up -d"'

                    }
                }
            }
        }
    }
    post {
        always {
            cleanWs()
        }
        success {
            echo 'Deployment successful!'
        }
        failure {
            echo 'Deployment failed!'
        }
    }
}

 

728x90
반응형