EC2, Jenkins를 활용한 CI/CD 구축 기술공유

강혜성·2023년 4월 26일
0
post-thumbnail

CI/CD

  • AWS EC2, Git Lab, Jenkins 환경에서 CI/CD 구성방법.

CI/CD란?

  • CI : Continuous Integration 지속적인 통합
  • CD : Continous Delivery 지속적인 제공 (Deployment로 사용하는 경우가 있다.)
  • CI/CD는 애플리케이션 개발 단계 부터 배포 단계 까지를 자동화하여 애플리케이션을 더욱 짧은 주기로 고객에게 제공하는 방법

CI/CD 개념 참고자료

https://www.redhat.com/ko/topics/devops/what-is-ci-cd
https://www.youtube.com/watch?v=0Emq5FypiMM&t=60s

CI/CD 구축 방법 참고자료

https://dramatic-armchair-97f.notion.site/e85a109829c94c9ea5c841ca8e852ac5

AWS에 CI/CD 구축 하는 방법

  • 순서는 다음과 같다.
    1. EC2 Instance에 Jenkins 설치
    2. Jenkins에 접속해 Git Lab과 Web Hook 연동
    3. Jenkins에 Pipeline Script를 이용해 자동 빌드 및 배포 구현

1. EC2 Instance에 Jenkins 설치

  • Docker를 이용해서 설치한다.
  1. Docker 설치는 공식 Docs 참고 (Install Docker Engine on Ubuntu)

    https://docs.docker.com/engine/install/ubuntu/

  2. EC2에 접속한 후 Docker를 이용해 Jenkins를 설치한다.
    포트의 경우에는 기본으로 8081로 설정했지만, 프로젝트 환경에 맞게 구성하면 된다.
sudo docker run --name jenkins -d -p 8081:8080 -p 50000:50000 jenkins/jenkins:lts

2. Jenkins에 접속해 Git Lab과 Web Hook 연동

  • 젠킨스로 접속한다. 웹 브라우저에 http://[IP]:8081를 입력해서 접속 가능하다.
  • 접속 Password는 EC2에 sudo docker logs jenkins를 입력해서 찾을 수 있다.
  • 추천 pulgin을 설치한다.

Jenkins 플러그인 설치

  • Plugin Manager를 통해서 Avaliable pugin을 설치한다.
    1. Gitlab
    2. Publish Over SSH
    1. Mattermost Notification(MM으로 알림 보낼 경우만 설치)
    2. docker
    3. node.js

Jenkins Publish Over SSH 설정

  • Jenkins와 EC2의 SSH 연결을 설정한다.
  • 젠킨스 관리 > Configure System 으로 들어간다.
  • Publish Over SSH에 .pem키를 붙여넣는다.
  • SSH Server를 설정한다.
  • Remote Dircetory의 경우에는 연결 대상에 미리 존재해야한다.
    (아래의 경우에은 /home/ubuntu/jenkins_build가 remote directory이므로 EC2에 /home/ubuntu/jenkins_build가 존재해야 한다. EC2에서 mkdir 명령어를 통해서 디렉토리 생성 가능하다.)
Name : EC2_Instance
Hostname : [EC2 domain] 
Username : ubuntu
Remote Directory : /home/ubuntu/jenkins_build 

EC2 설정

SSH 연결을 설정한다.

  • /etc/ssh/sshd_config에 vim을 통해 아래 2줄 추가한다.
    sudo vim /etc/ssh/sshd_config
	PubkeyAuthentication yes
	PubkeyAcceptedKeyTypes +ssh-rsa
  • sudo service sshd restart를 입력해서 ssh 서비스를 재시작한다.

SSH 연결에 사용할 Directory를 생성한다.

mkdir ~/jenkins_build

연결 확인

  • Jenkins, EC2 모두 설정한 후, Jenkins에 Publish Over SSH 설정 화면에서 오른쪽 하단 Test Configuration 클릭한다.

3. Jenkins에 Pipeline Script를 이용해 자동 빌드 및 배포 구현

  • Jenkiins에 Script를 설정해 Build 및 Deploy를 수행한다.
  • Jenkins에서 Build하기 위해 Java와 NodeJS를 설치한다.
  • Build된 결과를 EC2에 전달하고, 원격으로 해당 결과를 실행시킨다.
  • 빌드되는 순서는 다음과 같다.
    1. Jenkins에서 Vue 프로젝트를 clone해 온다.
    1. npm install 명령어를 통해 빌드한다.
    2. Spring Boot 프로젝트를 clone해 온다.
    3. 생성된 결과를 Spring Boot 프로젝트에 옮긴다.
    4. Spring Boot 프로젝트를 빌드한다.
    5. 빌드한 결과를 EC2에 전달한다.
    6. 기존 서버를 없앤 후, 새로 빌드한 프로젝트를 구동한다.

1. Jenkins 환경 구성

  • npm을 사용하기 위해 nodeJS를 설치한다.
  • Spring Project를 빌드하기 위해 Java를 설치한다.

1. 환경 설정

  1. apt 업데이트, sudo, vim 설치
apt-get update
apt-get install sudo
apt-get install vim
  1. sudo 설정, 설정 파일 열기
    sudo visudo

  2. 해당 파일에 아래 내용 추가
    jenkins ALL=(ALL) NOPASSWD: ALL

2. NodeJS 설치

  1. EC2에 접속한 후, jenkins에 접속하는 명령어를 수행한다.
    sudo docker exec -it -u root jenkins /bin/bash

  2. Jenkins에 NVM을 설치한다.
    curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.3/install.sh | bash

  3. NVM을 활성화한다. . ~/.nvm/nvm.sh

  4. 14버전을 설치한다. (프로젝트에 맞는 버전으로 설치)
    nvm install 14

  5. 모든유저에게 적용한다.
    n=$(which node);n=${n%/bin/node}; chmod -R 755 $n/bin/*; cp -r $n/{bin,lib,share} /usr/local

3. Java 설치

  1. EC2에 접속한 상태라 가정
  2. java를 설치하는 명령어 실행
    sudo apt-get install openjdk-11-jdk

2 . Jenkins Pipeline 생성

3. Webhook 생성

참고자료

https://junhyunny.github.io/information/jenkins/github/jenkins-github-webhook/

  1. Gitlab Webhook 설정, Token 생성
  2. Jenkins 빌드 트리거 설정, Webhook 설정

4. Script 작성

node {  
    
    stage('Build') { 
        checkout scmGit(branches: [[name: '*/dev']], extensions: [], userRemoteConfigs: [[credentialsId: 'comet', url: 'https://lab.ssafy.com/s08-webmobile2-sub2/S08P12C109']])
       dir("front") {
           sh "pwd"
           sh "sudo npm install"
           sh "sudo npm run build"
           sh "sudo cp -r /var/jenkins_home/workspace/zzalu_ci_cd_test/front/dist/* /var/jenkins_home/workspace/zzalu_ci_cd_test/back/src/main/resources/static"
       }
       dir("back") {
           sh "pwd"
           sh "sudo chmod +x gradlew "
           sh "sudo ./gradlew clean"
           sh "sudo ./gradlew build"
       }
        
    }
    stage('Deploy') {
        dir("back/build/libs") {
            sshPublisher(publishers: [sshPublisherDesc(configName: 'zzalu_deploy_server_test', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: '''cd /home/ubuntu/jenkins_build
            kill -9 `cat save_pid.txt`
            rm save_pid.txt
            sudo nohup java -jar zzalu-0.0.1-SNAPSHOT.jar > logs/zzalu.log 2>&1 & echo $! > save_pid.txt''', execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: '', sourceFiles: '*.jar')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: true)])
            mattermostSend color: '#32a852', message: "Dev Branch Deploy End! (${env.JOB_NAME}) #(${env.BUILD_NUMBER}) (<${env.BUILD_URL}|Open>) \n See the (<${env.BUILD_URL}console|console>)"
        }
    }
}
  1. checkout scmGit(branches: [[name: '*/dev']], extensions: [], userRemoteConfigs: [[credentialsId: 'comet', url: 'https://lab.ssafy.com/xxxxxxxxxx/xxxxxx']])

    • gitlab으로 clone하는 부분
    • branches: [[name: '*/dev']] : dev 브랜치를 클론한다.
    • credentialsId에는 사용할 계정을 선택한다.
    • url로는 프로젝트의 gitlab 주소를 사용한다.
  2. dir("front") {
    sh "pwd"
    sh "sudo npm install"
    sh "sudo npm run build"
    sh "sudo cp -r /var/jenkins_home/workspace/zzalu_ci_cd_test/front/dist/* /var/jenkins_home/workspace/zzalu_ci_cd_test/back/src/main/resources/static"
    }

    • front 빌드 시 수행할 영역
    • dir은 해당 dir로 들어가서 수행한다는 의미
    • 현재 프로젝트는 아래 캡처처럼 프로젝트가 구성되어 있다.
    • dir("front") 를 통해서 front 디렉토리 안으로 들어간다는 의미
    • front 폴더 안에 들어가서 npm install, build를 수행한다.
    • 이후 빌드된 결과를 back/src/main/resorces/static에 복사한다.

  1. dir("back") {
    sh "pwd"
    sh "sudo chmod +x gradlew "
    sh "sudo ./gradlew clean"
    sh "sudo ./gradlew build"
    }
    • dir("back")을 통해서 back 디렉토리로 들어간다.
    • 이후 gradle을 이용해서 빌드한다.
  2. dir("back/build/libs")
    • 빌드 된 결과는 back/build/libs에 있으므로, 해당 디렉토리로 진입한다.
  3. sshPublisher
 sshPublisher(publishers: [sshPublisherDesc(configName: 'zzalu_deploy_server_test', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: '''cd /home/ubuntu/jenkins_build
            kill -9 `cat save_pid.txt`
            rm save_pid.txt
            sudo nohup java -jar zzalu-0.0.1-SNAPSHOT.jar > logs/zzalu.log 2>&1 & echo $! > save_pid.txt''', execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: '', sourceFiles: '*.jar')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: true)])
            mattermostSend color: '#32a852', message: "Dev Branch Deploy End! (${env.JOB_NAME}) #(${env.BUILD_NUMBER}) (<${env.BUILD_URL}|Open>) \n See the (<${env.BUILD_URL}console|console>)"
  • sshPublisherDesc(configName: 'zzalu_deploy_server_test' EC2와 연결할 때 사용할 SSH, Publish On SSH에서 설정한 설정 이름을 사용
  • execCommand: 전송한 후 수행할 명령어들을 작성한다.
위의 경우에서 전송후 특정 디렉토리로 이동 (cd), 
기존 프로세스를 종료(kill -9)한 후 
전송한 jar 파일을 실행(nohup)한다.
  1. remoteDirectorySDF: false, removePrefix: '', sourceFiles: '*.jar')]
  • .jar인 파일을 모두 전송한다.
  1. mattermostSend color: '#32a852', message: "Dev Branch Deploy End! ({env.JOB_NAME}) #({env.BUILD_NUMBER}) (<env.BUILDURLOpen>)\nSeethe(<{env.BUILD_URL}|Open>) \n See the (<{env.BUILD_URL}console|console>)"
  • mattermost로 특정 메시지를 전달한다.

Trouble Shooting

  • 트러블 슈팅 하는 방법에 대해서 설명한다.

1. 프로젝트가 정상 빌드 되었는지 확인하는 법

  • 프로젝트가 정상 반영되지 않았을 때, 확인하는 방법
  1. Jenkins 웹에 접속해서 빌드 결과가 Success인지 확인한다.
    => Fail일 경우 발생한 Error를 보고 판단한다.
  2. EC2에 접속해서 Top 명령어를 친 후 jar 파일 구동 시간이 빌드한 시간과 일치하는 지 확인한다.
    => 일치하지 않을 경우 프로세스가 정상적으로 종료되지 않았을 경우일 확률이 높다. Jenkins 스크립트의 kill -9 명령어가 정상적으로 작동되는지 확인한다.
  3. Jenkins cli로 접속해서 jar 파일 변경시간이 올바른지 확인한다.
    => 변경시간이 올바르지 않을 경우, Git에서 제대로 가져오는지 확인한다. 이후 Build에서 문제가 생기는지 CLI에서 직접 빌드해본다.

0개의 댓글