[Jenkins] CI/CD 파이프라인

ttaho·2024년 2월 4일
6
post-thumbnail

싸피의 2학기의 공통 프로젝트를 하며 처음으로 CI/CD를 적용해본 2주간의 삽질을 공유해보고자 한다.
우선, CI/CD란 무엇인지 알아보자.

CI/CD 란?

  1. CI(Continuous Integration)
    CI = 지속적인 통합
    개발자들이 어플리케이션의 코드를 일상적으로 통합하는 프로세스를 의미한다.
    여러 개발자가 동시에 작업하거나 다른 기능을 개발하더라도, CI는 코드 변경 사항을 자동으로 통합하여 충돌을 예방하고 오류를 조기에 발견할 수 있도록 한다.
    CI는 한마디로 빌드와 테스트 자동화 이다.

  2. CD(Continuous Delivery/Deployment)
    CD = 지속적인 전달 또는 지속적인 배포
    CD는 CI의 확장으로, 어플리케이션의 지속적인 배포를 가능하게 한다.
    자동화된 테스트와 배포 과정을 통해 어플리케이션을 신속하게 사용자에게 전달한다.
    모든 코드 변경 사항이 자동으로 프로덕션 환경에 배포되는 것을 의미한다.
    CD는 한마디로 배포 자동화 이다.

CI/CD는 현대 시대의 시장 변화 및 고객 요구에 신속하고 유연하게 대응할 수 있게 속도효율이라는 큰 장점이 있다.

알아야 할것들

CI/CD 파이프라인을 구성하기 위해 기본적으로 알아야 할 것들을 살펴보자.

1. Jenkins

이번 공통 프로젝트의 CI/CD를 위해 선택한 도구는 Jenkins(젠킨스)이다.

젠킨스는 오늘날 시장에서 가장 널리 사용되는 CI/CD 도구이며 Windows, macOS 및 기타 Unix 계열 운영 체제용 패키지가 포함된 독립형 Java 기반 프로그램이다.
Jenkins는 소프트웨어 개발 프로젝트의 빌드 파이프라인 구성, Build 자동화의 확립, 배포 및 테스트 자동화 등을 지원한다.

2. Docker

이번 프로젝트에서 Jenkins, Openvidu, SpringBoot, Redis, Flask를 Docker 컨테이너로 띄워서 사용했다.

Docker(도커)는 리눅스 컨테이너에 리눅스 어플리케이션을 프로세스 격리기술을 사용하여 더 쉽게 컨테이너로 실행하고 관리할 수 있게 해주는 오픈소스 프로젝트이다.

Docker Engine(도커 엔진)은 컨테이너를 생성하고 관리하는 주체로서 이 자체로도 컨테이너를 제어할 수 있고 다양한 기능을 제공하는 도커의 프로젝트이다. 도커의 생태계에 있는 여러 프로젝트들은 도커 엔진을 좀 더 효율적으로 사용하기 위한 것에 불과하기 때문에 도커의 핵심은 도커 엔진이다.

도커 이미지와 도커 컨테이너

도커 엔진에서 사용하는 기본 단위는 이미지와 컨테이너이며 도커 엔진의 핵심이다.

도커 이미지와 컨테이너는 1:N 관계이다.

ex) 웹 서버 도커 이미지로부터 여러개의 도커 컨테이너를 생성하면 컨테이너 개수만큼 웹 서버가 생성되고, 이 컨테이너들은 외부에 웹 서비스를 제공하는 데에 사용된다.

도커 이미지와 컨테이너의 관계는 객체지향 프로그래밍에서의 클래스 <-> 인스턴스의 관계와 비슷하다고 생각하면 이해가 더 편하다.

Docker File -> Docker Image : Docker File은 도커 이미지를 만들때 사용하는 파일.
docker build 명령어를 실행시키면 도커 이미지를 만들 수 있다.

Docker Image -> Docker Container : 도커 이미지를 docker run 명령어를 실행시키면 도커 컨테이너를 만들 수 있다.

컨테이너는 이미지를 읽기 전용으로 사용하되 이미지에서 변경된 사항만 컨테이너 계층에 저장하므로 컨테이너에서 무엇을 하든지 원래 이미지는 영향을 받지 않는다.

또한 생성된 각 컨테이너는 각기 독립된 파일시스템을 제공받으며 호스트와 분리돼 있으므로 특정 컨테이너에서 어떤 어플리케이션을 설치하거나 삭제해도 다른 컨테이너와 호스트는 변화가 없다.

3. NGINX

NGINX는 트래픽이 많은 웹사이트의 서버(WAS)를 도와주는 비동기 이벤트 기반구조의 경량화 웹 서버 프로그램이다.

클라이언트로부터 요청을 받았을 때 요청에 맞는 정적 파일을 응답해주는 HTTP Web Server로 활용되기도 하고, 또는 Reverse Proxy Server로 활용하여 WAS의 부하를 줄일 수 있는 로드밸런서 역할을 하기도 한다.

우리는 Front-End인 React 프로젝트의 정적 파일들을 NGINX를 통해 WEB 서버로 띄워주고, 리버스 프록시를 통해 Flask, Spring으로 분기처리 해주었다.

CI/CD 파이프라인 구성하기

우리 팀은 Jenkins를 사용해서 CI/CD 파이프라인을 구성하기로 했다. Jenkins가 실제 참고할만한 자료들이 많고, 싸피에서도 권장하기 때문이다.

순서대로 진행해보겠다.

EC2에 Docker 설치

우리는 Jenkins, Redis, Openvidu, 백엔드 등을 Docker Engine 위에 띄울 예정이기 때문에 Docker를 설치해 주었다.

Docker를 통해 Jenkins 설치

우리는 Jenkins를 통해 CI/CD 파이프라인을 구성할 것이기 때문에 Docker를 통해 Jenkins를 설치했다. 우리의 백엔드 프로젝트가 jdk11을 사용하므로 Jenkins도 jdk11로 설치해주었다.

docker pull jenkins/jenkins:jdk11

해당 이미지를 실행하는 명령어는 아래와 같다.

docker run -d -p 9090:8080 -v /var/jenkins_home --name jenkins -u root jenkins/jenkins:jdk11
// 위 명령어 옵션설명
-d	detached mode 흔히 말하는 백그라운드 모드
-p	호스트와 컨테이너의 포트를 연결 (포워딩)
-v	호스트와 컨테이너의 디렉토리를 연결 (마운트)
–name	컨테이너 이름 설정
-u 실행할 사용자 지정

맨 마지막 jenkins/jenkins:jdk11 는 실행할 이미지의 레포지토리 이름이며 만약 이미지가 없을 경우 이미지를 docker hub 에서 땡겨오므로 주의한다.

현재 실행된 컨테이너를 확인하려면 아래의 명령어를 치면 나온다.

docker ps

이미 나는 CI/CD를 구성해놓아서 많은 컨테이너들이 나온다.

이제 젠킨스에 접속해보자.
나는 9090 포트로 젠킨스를 도커 컨테이너로 띄워줬으므로 http://{싸피에서 지급받은 도메인}:9090 으로 접속을 하면 다음과 같은 화면을 만날 수 있다.

초기 접속을 위해서는 admin password를 입력해야 한다.

// jenkins 컨테이너에 접속
$ sudo docker exec -it jenkins bash
// 초기 관리자 키 확인
$ cat /var/jenkins_home/secrets/initialAdminPassword

키를 입력하고 넘어가면 플러그인 설치 화면을 볼 수 있다.

플러그인 설치가 완료된 후, jenkins 접속할 때 로그인을 위한 유저를 설정해 준다.

해당 과정을 거치면 Jenkins 설정이 완료 된다.

Jenkins에 credential 등록해주기

Jenkins의 파이프라인에 CI/CD를 해줄때, Jenkins에서 여러가지 권한을 얻기 위해 credential을 등록해주어야 한다.

등록하는 위치는 다음과 같다.
Dashboard -> Jenkins 관리 -> Credentials

현재 등록한 Credentials 목록이다.
1. gitlab : gitlab의 프로젝트를 clone 해오기위한 credential
2. aws-key : jenkins에서 우리의 aws ec2의 ssh에 접속하기위한 credential
3. dockerhub-jenkins : 내 dockerhub에 있는 백엔드, AI 프로젝트 이미지를 끌어오기 위한 credential
4. application 1,2,3 : 프로젝트 최종 배포시 중요한 정보들이 들어있는 Spring 설정 파일들을 gitlab에 올리지 않을것이기 때문에 Jenkins에 미리 저장을 해주고 이걸 Backend-build 전 단계에 Backend 프로젝트의 resources 에 넣어주기 위함.


위 파일들을 git에 올리지 않기 위해 .gitignore에 추가하자...

# .gitignore

### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
out/
!**/src/main/**/out/
!**/src/test/**/out/
application.properties
application.yml
application.oauth.yml

각각의 credential의 설정 방법을 알아보자.

  1. gitlab : 내 project - settings - Access Tokens으로 가서 Access Token을 발급받아 password에 등록해준다.

  2. aws-key : aws-ec2의 pem 키를 등록해준다 pem 키는 메모장 같은것으로 키고 복사해서 key 부분에 등록해준다.

  3. docker-jenkins : 내 도커허브의 로그인 이메일과 비밀번호를 등록해준다.
    gitlab 처럼 등록해주면 된다.

  4. application 1,2,3 : 내 백엔드 프로젝트의 application.properties,yml 파일을 직접 업로드 해주면 된다.

PlugIn(플러그인) 설치

젠킨스에서 NodeJS, Gradle과 같은 빌드 도구들을 설정을 해주어야 파이프라인에서 React, SpringBoot 같은 프로젝트의 빌드를 정상적으로 진행할 수 있다.

해당 경로로 이동해주자.
Dashboard -> Jenkins 관리 -> PlugIns

젠킨스에서 기본적으로 NodeJS, gitlab, docker 플러그인이 설치되어 있지 않으므로 설치해주자.

빌드 설정 추가

플러그인만 설치한다고 끝나는게 아니다. 추가로 설정해줄 부분을 해주자.

다음 경로로 이동해주자.
Dashboard -> Jenkins 관리 -> Tools

Gradle과 NodeJS 설정을 해주자.

(중요) NodeJS 버전을 꼭 확인하자

dockerfile 생성

우리 프로젝트는 직접 docker image로 만들어주는 프로젝트가 두개가 있다.
1. SpringBoot인 백엔드
2. AI를 위한 flask 프로젝트
두 프로젝트의 dockerfile을 작성해 주었다.
경로는 모두 루트 디렉토리 이다.

FROM openjdk:11-jdk

ARG JAR_FILE=build/libs/*.jar

COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java", "-Dspring.profiles.active=docker","-jar","app.jar"]

# Python 공식 이미지를 베이스 이미지로 사용
FROM python:3.9-slim

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

# 현재 디렉토리의 내용을 컨테이너의 /app에 복사
COPY . /app

# 필요한 패키지 설치를 위한 requirements.txt 파일을 컨테이너에 복사
COPY requirements.txt /app/

RUN apt-get update && \
    apt-get install -y \
    build-essential \
    cmake \
    libopenblas-dev \
    liblapack-dev \
    libx11-dev \
    libgtk-3-dev \
    python3-dev \
    python3-pip \
    python3-numpy \
    && rm -rf /var/lib/apt/lists/*

# requirements.txt에 명시된 필요한 패키지들을 설치
RUN pip install -r requirements.txt

# Flask 애플리케이션 실행을 위한 환경변수 설정
ENV FLASK_APP=app.py
ENV FLASK_RUN_HOST=0.0.0.0

# 애플리케이션을 실행하기 위한 포트 설정 (Flask 기본 포트: 5000)
EXPOSE 5000

# Flask 애플리케이션 실행 명령어
CMD ["flask", "run"]

Jenkins 프로젝트 생성

우리 프로젝트만의 CI/CD 파이프라인을 구성하기 위해 먼저, Jenkins의 프로젝트를 만들자.

Dashboard에서 왼쪽 new Items를 눌러 새로운 프로젝트를 생성한다.

나는 파이프라인 스크립트를 직접 작성했다. Pipeline 누르고 OK를 눌러주면 프로젝트가 생성된다.

Gitlab -> Jenkins Webhook 걸어주기

이번에는 위에서 생성한 프로젝트의 CI/CD 파이프라인이 작동하기 위한 트리거인 Webhook을 걸어야 한다.

왼쪽의 구성을 눌러 아래로 내리다 보면 Build Triggers 탭을 만날 수 있다.

여기서 Build when a change is pushed to GitLab 항목을 찾아주자.

이게 없으면 gitlab 플러그인을 설치 안한거 이므로 설치해주자.

URL을 복사하고 Push Events 버튼을 활성화 해준다.
그리고 아래의 고급 탭을 누른다.

Generate를 눌러 Secret token을 생성해주고, 이것을 복사해놓자.

이제, 우리의 gitlab 프로젝트의 setting의 webhook으로 간다.

Add new webhook을 클릭해 새로운 webhook을 등록한다.
이때, 아까 Jenkins에서 복사해놓은 url과 Secret token을 넣어준다.
그리고 어떤 브랜치에 push가 일어나면 webhook을 발동할 것인지 작성해준다.
나는 master 브랜치로 정했다.

저장을 하면 등록이 완료된것이다!
Test의 Push events를 눌러 테스트 해볼수도있다.

FrontEnd CI/CD

젠킨스의 프로젝트를 파고, gitlab에 webhook도 등록시켰으니, 이제부터 프론트엔드 CI/CD 파이프라인을 작성해보자.

다시 상기해보자면, 우리는 ec2에 nginx를 통해 react 프로젝트를 웹서버에 띄울것이다.
그러기 위해 제공받은 ec2에 nginx를 설치하고 설정해주자.

# nginx 설치하기
sudo apt install nginx

# nginx 설정 파일 작성하기
sudo vi /etc/nginx/sites-available/project.conf

nginx를 설치하고 설정파일을 작성해 주었다.

server {

        location / {
                root /home/ubuntu/build;
                index index.html index.htm;
                try_files $uri /index.html;
        }

        location /api {
                proxy_pass http://localhost:8080;
        }

        location /socket {
                proxy_pass http://localhost:8080;
                proxy_http_version 1.1;
                proxy_set_header Upgrade $http_upgrade;
                proxy_set_header Connection "upgrade";
        }

        location /flask {
                proxy_pass http://localhost:5000;
        }

    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/i10e104.p.ssafy.io/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/i10e104.p.ssafy.io/privkey.pem; # managed by Certbot
    # include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    # ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}

설명하자면 이렇다.

  1. /로 들어오는 요청은 /home/ubuntu/build 경로에 있는 index.html를 띄워준다.

  2. /api가 포함된 요청을 8080 포트로 분기처리 해준다. 이 포트는 SpringBoot 프로젝트로, api 서버이다.

  3. /socket 요청은 openvidu를 사용하는 우리의 프로젝트에서 두 개의 Openvidu webrtc 세션간의 통신을 위한 소켓을 열어주었기 때문에 이 요청을 분기처리 해주었다. 밑의 3개의 옵션은 React -> socket 으로의 요청에 CORS 문제를 해결하기 위해 추가해주었다.

  4. /flask가 포함된 얼굴인식과 OCR을 위한 5000포트인 flask 서버에 대한 요청을 분기처리 해준다.

  5. 밑의 listen 443 ssl은 우리의 도메인에 Https를 적용시키기 위함이다. 기본적으로 https는 443 포트를 사용하고, ssl 인증서를 certbot에서 발급받았다. 그리고 fullchain.pem과 privkey.pem의 경로를 알려주어 https를 적용시켰다.

심볼릭 링크 생성

/etc/nginx/sites-enabled/ 경로에는 /etc/nginx/sites-available/ 에 있는 설정 파일에 대해 심볼릭 링크를 만들어야 웹 서버가 동작할 때 sites-enabled에 있는 설정 파일들을 참조할 수 있다.

심볼릭 링크(Symbolic link)란?
윈도우의 바로가기와 같이 링크를 연결하여 원본 파일을 직접 사용하는 것과 같은 효과를 내는 링크이다

아래의 명령어를 통해 심볼릭 링크를 설정했다.

sudo ln -s /etc/nginx/sites-available/project.conf /etc/nginx/sites-enabled/project.conf

Front-End 파이프라인 스크립트

스크립트는 우리가 만들어놓은 pipeline 프로젝트의 구성에서 작성할 수 있다.

젠킨스의 프론트엔드 부분의 스크립트는 아래처럼 작성했다.

 stages {
        stage('Git Clone') {
            steps {
                git branch: 'master', credentialsId: 'gitlab', url: '깃랩 프로젝트 url.git'
            }
        }
        stage('FE-Build') {
            steps {
                dir("./fe"){
                    nodejs(nodeJSInstallationName: 'NodeJS 21.5.0'){
                        //CI: 오류 무시
                        sh 'npm install'
                        sh 'CI=false npm run build'
                    }
                }
            }
        }
        
        stage('Compression'){
            steps{
                dir("./fe"){
                    sh '''
                    rm -rf node_modules
                    tar -cvf build.tar build
                    '''
                }
            }
        }
        
        stage('Frontend Deploy to EC2'){
            steps {
                // EC2 서버에 SSH 접속 크레덴셜 ID
                sshagent(credentials: ['aws-key']) {
                    sh '''
                        ssh -o StrictHostKeyChecking=no ubuntu@도메인 uptime
                        scp /var/jenkins_home/workspace/byeoljali/fe/build.tar ubuntu@도메인:/home/ubuntu
                        ssh -t ubuntu@도메인 "chmod +x /home/ubuntu/deploy.sh && /home/ubuntu/deploy.sh"
                    '''
                }
            }
        }

stage 별로 나누어 살펴보자.
1. Git Clone

우리의 gitlab에 올라가있는 master 브랜치의 프로젝트를 clone 해와서 jenkins의 workspace에 가져온다.

클론해올때, 깃랩의 credential이 필요하므로 위의 단계에 등록했던 깃랩 credential의 id인 'gitlab'을 적어준다.
url엔 우리의 프로젝트 주소를 복사해서 넣으면 된다.

  1. FE-Build

가져온 프로젝트의 /fe 경로로 이동하여 build를 진행한다.

앞서 jenkins의 Dashboard -> Jenkins 관리 -> Tools에서 설정해준 NodeJS의 name을 nodeJSInstallationName에 지정해준다.

jenkins에서 빌드를 진행할때는 warning도 오류로 취급하므로 CI=false를 적용시켜 build를 진행해준다.

  1. Compression

node_modules 디렉토리를 삭제시킨다. node_modules는 프로젝트에 필요한 모든 노드 패키지가 설치되는 곳인데, 빌드 과정에서 생성된 파일들은 필요하지 않으므로 삭제시킨다.

build 경로에있는 파일들을 압축시킨다. 해당 파일들을 우리의 ec2 서버에 옮겨야 하므로 압축을 진행한다.

  1. Frontend Deploy to EC2

가린 부분은 발급받은 ec2 서버의 도메인이다.

이전에 credential로 등록한 aws-key를 이용하여 원격으로 aws ec2에 접속한다.

jenkins의 경로에 있는 프론트엔드 build.tar 압축파일을 ec2의 /home/ubuntu 경로로 복사한다.

그리고 deploy.sh의 실행권한을 부여한 뒤, /home/ubuntu/deploy.sh 배포 스크립트를 실행시켜 배포시킨다.
deploy.sh는 아래와 같다.

#!/bin/bash
sudo tar -xvf build.tar
sudo rm -rf build.tar

sudo service nginx restart

내용은 현재 경로(/home/ubuntu)에 build.tar를 압축 해제하고, build.tar를 삭제하고, nginx를 재시작 하는 스크립트이다.

이로써 /home/ubuntu/build/index.html이 실행된다.

Flask CI/CD

프론트엔드는 완료했으니 AI를 위한 Flask도 진행해보자.

우선, 프론트엔드와 다르게 Flask와 SpringBoot 프로젝트는 모두 도커 이미지를 만들고, 이것을 ec2의 도커엔진에 컨테이너로 띄울것이다. 이렇게 한 이유는 설정관련된 것들을 하나의 이미지로 묶을 수 있고, 배포도 매우 쉽게 할 수 있기 때문이다.

프론트엔드 부분을 작성한 동일한 pipeline 프로젝트의 스크립트에 이어서 작성하자.

flask 프로젝트의 스크립트는 아래와 같다.

stage('Flask-Docker-Build') {
            steps {
                dir("./flask"){
                    script{
                        //현재 작업 디렉토리 확인
                        sh 'pwd'
                        
                        // Docker 이미지 빌드
                        def app = docker.build "도커허브username/byeoljali-flask"
    
                        // Docker Hub에 로그인
                        docker.withRegistry('https://registry.hub.docker.com', 'dockerhub-jenkins') {
                            // Docker 이미지 푸시
                            app.push("1.0") // 1.0 이라는 태그로 image가 푸쉬됨
                        }
                    }
                }
                
            }
        }
        
        stage('Flask Deploy to EC2'){
            steps {
                sshagent(credentials: ['aws-key']) {
                    
                    // 이미 ai라는 컨테이너가 띄워져있으면 그거 멈추고, 지우고, 이미지도 싹다 지우기
                    sh '''
                    if [ -n "$(ssh -o StrictHostKeyChecking=no ubuntu@도메인 'sudo docker ps -q -f name=ai')" ]; then
					ssh -o StrictHostKeyChecking=no ubuntu@도메인 "sudo docker stop ai"
                    ssh -o StrictHostKeyChecking=no ubuntu@도메인 "sudo docker rm ai"
                    ssh -o StrictHostKeyChecking=no ubuntu@도메인 "sudo docker rmi registry.hub.docker.com/도커허브username/byeoljali-flask:1.0"
                    ssh -o StrictHostKeyChecking=no ubuntu@도메인 "sudo docker rmi 도커허브username/byeoljali-flask:1.0"
                    ssh -o StrictHostKeyChecking=no ubuntu@도메인 "sudo docker rmi 도커허브username/byeoljali-flask"
                    
                    fi
                    '''
                }
                
                // flask 이미지 땡겨오고 배포
                sshagent(credentials: ['aws-key']) {
                    sh 'ssh -o StrictHostKeyChecking=no ubuntu@도메인 "sudo docker pull 도커허브username/byeoljali-flask:1.0"'
                    sh 'ssh -o StrictHostKeyChecking=no ubuntu@도메인 "sudo docker run -p 5000:5000 -d --name ai 도커허브username/byeoljali-flask:1.0"'
                }
            }
        }
        

stage 별로 하나씩 살펴보자.

  1. Flask-Docker-Build

flask 프로젝트는 /flask 경로에 있으므로 이동한다.

기존에 flask 루트 디렉토리에 dockerfile을 작성했으므로 그것을 기반으로 이미지로 만든다. 이미지 이름은 나의 도커허브 계정의 username/프로젝트이름-flask 이다.

다음으로, 빌드한 이미지를 나의 dockerhub에 푸쉬한다.
이때, Jenkins의 Credentials에 등록시킨 나의 dockerhub 계정정보가 저장된 credential-id인 'dockerhub-jenkins'를 적어줘야 로그인 할 수 있다. 그리고 해당 이미지를 1.0 이라는 태그로 버전을 명시해주자.

  1. Flask Deploy to EC2

이전에 credential로 등록한 aws-key를 이용하여 원격으로 aws ec2에 접속한다.

이미 ai라는 이름의 컨테이너가 실행중이면 이것을 멈추고, 컨테이너를 지우고 이미지도 다 지운다.

그리고 flask 이미지를 docker-hub에서 pull 해오고 이미지를 ai라는 이름으로 run 시켜 컨테이너화 한다.

이제 도메인:5000을 통해 flask와 통신할 수 있다.

BackEnd CI/CD

이제는 백엔드인 SpringBoot 프로젝트도 진행해보자.

SpringBoot 프로젝트의 스크립트는 아래와 같다.

stage('BE-Build') {
            steps {
                dir("./BE"){
                    sh 'chmod +x gradlew'
                    sh "./gradlew clean build"
                }
                
            }
        }
        
        stage('Docker Build and Push') {
            steps {
                dir("./BE"){
                    script{
                        //현재 작업 디렉토리 확인
                        sh 'pwd'
                        
                        // Docker 이미지 빌드
                        def app = docker.build "도커허브username/byeoljali-spring"
    
                        // Docker Hub에 로그인
                        docker.withRegistry('https://registry.hub.docker.com', 'dockerhub-jenkins') {
                            // Docker 이미지 푸시
                            app.push("1.0") // 1.0 이라는 태그로 image가 푸쉬됨
                        }
                    }
                    
                }
                
            }
        }
        
        stage('BackEnd Deploy to EC2'){
            steps {
                sshagent(credentials: ['aws-key']) {
                    
                    // 이미 back 이라는 컨테이너가 띄워져있으면 그거 멈추고, 지우고, 이미지도 싹다 지우기
                    sh '''
                    if [ -n "$(ssh -o StrictHostKeyChecking=no ubuntu@도메인 'sudo docker ps -q -f name=back')" ]; then
					ssh -o StrictHostKeyChecking=no ubuntu@도메인 "sudo docker stop back"
                    ssh -o StrictHostKeyChecking=no ubuntu@도메인 "sudo docker rm back"
                    ssh -o StrictHostKeyChecking=no ubuntu@도메인 "sudo docker rmi registry.hub.docker.com/도커허브username/byeoljali-spring:1.0"
                    ssh -o StrictHostKeyChecking=no ubuntu@도메인 "sudo docker rmi 도커허브username/byeoljali-spring:1.0"
                    ssh -o StrictHostKeyChecking=no ubuntu@도메인 "sudo docker rmi 도커허브username/byeoljali-spring"
                    
                    fi
                    '''
                }
                
                // 백엔드 이미지 땡겨오고 배포
                sshagent(credentials: ['aws-key']) {
                    sh 'ssh -o StrictHostKeyChecking=no ubuntu@도메인 "sudo docker pull 도커허브username/byeoljali-spring:1.0"'
                    sh 'ssh -o StrictHostKeyChecking=no ubuntu@도메인 "sudo docker run -p 8080:8080 -d --name back 도커허브username/byeoljali-spring:1.0"'
                }
            }
        }

stage 별로 하나씩 보자.

  1. BE-Build

백엔드 경로인 /BE로 이동한 후, gradlew 실행권한을 주고 build 시킨다.

  1. Docker Build and Push

빌드 시킨 .jar 파일과 jdk 11을 함께 도커 이미지화 해준다.

도커 이미지를 도커허브에 push 한다.

  1. BackEnd Deploy to EC2

이미 ec2에 back 이라는 컨테이너가 실행중이면 이것을 멈추고, 지우고, 이미지도 다 지운다.

그리고 dockerhub에서 이미지를 pull 해오고, 이것을 back 이라는 이름으로 run 시켜 컨테이너화 한다.

이제 도메인:8080을 통해 SpringBoot 프로젝트와 통신할 수 있다.

파이프라인 실행

위에서 작성한 파이프라인을 실행해보자.

시간이 조금 걸리지만 모든 빌드테스트 과정과 배포를 master 브랜치의 push 한번으로 자동화 했다.

아쉬운점

  1. docker-compose.yml 미사용
    해당 파이프라인에서는 실제로 우리가 docker 컨테이너로 띄운 Redis, Openvidu의 배포는 자동화 하지 않고 코드의 변경이 잦은 react, flask, springboot 프로젝트만을 파이프라인에 구성했다.

또한, docker-compose를 통해 도커 이미지 풀, 이미지 컨테이너화를 작성해보진 못했다. 다음 프로젝트때는 이 docker-compose를 사용하여 모든 이미지를 컨테이너화 시키는 부분도 작성해보면 좋을 것 같다.

  1. Flask의 Docker Build 시간

사진에서 보이듯이 Flask의 코드가 수정되면 첫번째 도커 빌드시 11분이나 걸리는것을 볼 수 있다.
우리의 프로젝트에 사용된 얼굴인증, OCR 부분을 빌드할때 requirements 파일에 있는것들도 자동으로 설치되게 했는데, 이부분이 상당히 많은 시간이 걸린다.

profile
백엔드 꿈나무

0개의 댓글