CI/CD를 위한 gitlab pipeline 구축하기

smlee·2022년 8월 25일
5

DevOps

목록 보기
3/3
post-thumbnail

다른 서버 개발자 및 클라이언트 개발자들과 협업을 하면 지속적인 코드 수정 및 코드 추가가 이루어지므로 지속적인 통합과 지속적인 배포가 필요하다. 하지만 매번 EC2 인스턴스에 접속해서 현재 배포되고 있는 무중단 서비스를 정지시키고 git pull을 한 다음 다시 배포를 하는 과정은 귀찮다. 따라서 용이한 CI/CD를 위해 Gitlab CI pipeline을 사용하여 CI/CD 환경을 구축할 것이다.

[이 포스트는 Spring Boot 기준으로 작성되었습니다.]

1. EC2 인스턴스

Gitlab CI pipeline을 구축하기 위해서는 m3a.medium 이상의 aws 인스턴스, 인스턴스 접속을 위한 pem 파일, aws 인스턴스 보안그룹 인바운드 SSH 오픈이 필요하다.

AWS에서 위 3가지의 설정을 모두 마쳤다면 인스턴스에 접속해서 다음과 같은 명령어를 통해 서버 설정을 최신화를 한다. [Spring Boot 기준]

sudo apt-get update

sudo apt-get -y upgrade

sudo apt-get install -y git

sudo apt-get install -y nginx

2. gitlab-runner 설치 및 등록

(1) gitlab-runner 설치

1번에서 서버 설정을 최신화 한 뒤, 다음 명령어들을 통해 서버에서 gitlab-runner를 설치한다.

curl -L https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh | sudo bash

sudo apt-get install -y gitlab-runner

위의 명령어들을 실행하면 gitlab-runner가 설치가 된다.

(2) gitlab-runner 등록

2-(1) 과정에서 gitlab-runner를 설치하였다면 설치한 gitlab-runner를 실행시키고 CI/CD 환경을 구축할 프로젝트를 등록해야 한다.

sudo gitlab-runner register

위의 명령어를 통해 gitlab-runner 등록을 시작한다.


환경 구축할 깃랩 프로젝트의 좌측 사이드바에서 Settings 항목에 마우스를 올리면

여러 옵션들이 나타나는데, 여기서 CI/CD 항목을 클릭한다.

CI/CD 항목에서 Runners에 들어가서 우측의 expand 버튼을 누르면 위와 같은 화면이 뜨는데, 빨간 상자 안에 있는 정보를 쉘에 입력한다. 그리고 주의해야할 점은, 우측에 있는 Shared runners는 반드시 비활성화 해야한다.는 점이다.

URL 항목에는 깃랩 프로젝트 CI/CD - Runner의 URL을 복사해서 넣어주고, registration token 역시 프로젝트 페이지 내에 있는 토큰을 복사해서 넣어준다. 그리고, tags를 입력해야 하는데, 이 태그는 추후 gitlab-ci.yml에 입력해야하므로 기억 해야한다.

그리고 executor를 선택해야하는데, shell로 선택하면 된다.

3. gitlab-runner 사용자에게 sudo 권한 주기

다시 EC2 인스턴스에 접속해서 sudo visudo를 입력하면 nano 에디터가 켜지는데, gitlab-runner ALL = (ALL) NOPASSWD: ALL을 입력하여 gitlab-runner에게 권한을 준다.

4. 자동 배포할 프로젝트에 gitlab CI/CD 코드 작성

gitlab-ci.yml을 작성해서 CI/CD 환경을 만들어야 한다.

(1) 파이프 라인 설정

stages에서 배포할 때의 파이프라인의 개수(단계)를 설정해주어야 한다.

stages:
  - build
  - deploy

빌드를 한 후 빌드한 결과물을 배포해야 하므로 builddeploy 단계로 나누었다. 물론, 추가 과정이 필요하거나 과정이 하나만 필요하다면 여기서 더하거나 빼면 된다.

(2) 캐시 설정

cache:
  paths:
    - .gradle/wrapper
    - .gradle/caches

캐시를 어디에 저장할 지 경로는 cache에서 설정해주면 된다.

(3) 파이프라인 자세한 설정

a. build 파이프라인 설정

build:
  stage: build
  only:
    - master
  before_script:
    - echo 'build starts'
    - chmod +x ./gradlew
  script:
    - ./gradlew clean build
  artifacts:
    paths:
      - build/libs/*.jar
    expire_in: 1 week 
  tags:
    - gitlab-runner 생성시 작성한 Tag 명
  when: manual 

가장 위에는 파이프 단계명을 적는다. 4-(1)의 파이프 라인 설정에서 builddeploy 단계로 나누었으므로 먼저 build를 적는다.
그리고 밑에 stage를 통해 명시를 해주고, only를 통해 어떤 브랜치에서 변경이 있을 시 생성되는 지를 적어준다.

그리고 before script는 스크립트가 시작되기 전 작업을 의미하고, script는 작업을 의미한다.

여기서는 ./gradlew clean bulid를 진행할 것이므로 before script에서 gradle에 권한을 추가해주었다. 그리고, script에서 본격적인 빌드를 실행하고, when에서 언제 실행할 지 정해준다. 나는 수동 클릭을 통해 빌드 단계를 실행시키고 싶으므로 manual을 선택하였다.

이때, git pull origin master을 하지 않아도 되는 이유는 자동으로 gitlab-runner가 해주기 때문이다.

b. deploy 파이프라인 설정

deploy 단계에서 배포를 하고 싶다. 하지만, 배포를 할 때 dev환경과 실제 환경인 prod환경, proddev의 중간 단계인 stage 환경 3개로 나누려면 어떻게 해야할까?

dev_deploy:
  stage: deploy
  only:
    - master
  before_script:
    - (before script 내용)
  script:
    - (script 내용)
  tags:
    - gitlab-runner 생성시 작성한 Tag 명
  when: manual

stage_deploy:
  stage: deploy
  only:
    - master
  before_script:
    - (before script 내용)
  script:
    - (script 내용)
  tags:
    - gitlab-runner 생성시 작성한 Tag 명
  when: manual
  
  prod_deploy:
  stage: deploy
  only:
    - master
  before_script:
    - (before script 내용)
  script:
    - (script 내용)
  tags:
    - gitlab-runner 생성시 작성한 Tag 명
  when: manual

위와 같이 stage를 deploy로 설정해 주고, 맨 위에는 어떤 이름으로 보일 지를 결정해준다. 위와 같이 설정을 하면

위와 같이 deploy 단계에서 3가지 분기로 나뉜다. 즉 어떤 환경으로 배포할 지를 선택할 수 있는 것이다.

이에 앞서서, gitlab 프로젝트에 민감한 정보들은 변수로 등록해야 한다.

변수를 등록하기 위해서는 2-(2)에서 했듯, 깃랩 프로젝트의 Settings - CI/CD에 들어간다. 그리고 Variables 항목을 expand하여 좌측 하단의 Add variable을 통해 변수를 등록할 수 있다.

  1. DEPLOY_SERVER (variable) : EC2 인스턴스 탄력적 IP [script에서 탄력적 IP를 사용해야 하기 때문]
  2. SSH_KEY (variable) : EC2 인스턴스의 pem 키(개인 키) 파일 값 [EC2 접근에 있어서 꼭 필요한 정보이지만, 노출되면 안 되므로]
  3. SSH_KEY_FILE (file) : EC2 인스턴스의 pem 키로, 파일 형태로 저장한다.
  4. SSH_KNOWN_HOSTS (variable) : 믿을 수 있는 호스트로, 서버의 known_hosts를 가져온다.
dev_deploy:
  stage: deploy
  only:
    - master
  before_script:
    - command -v ssh-agent > /dev/null || (apt-get update -y && apt-get install openssh-client -y)
    - eval $(ssh-agent -s)
    - echo "$SSH_KEY" | tr -d '\r' | ssh-add -
    - mkdir -p ~/.ssh
    - chmod 700 ~/.ssh
    - echo "$SSH_KNOWN_HOSTS" >> ~/.ssh/known_hosts
    - chmod 644 ~/.ssh/known_hosts
  script:
    - scp -o StrictHostKeyChecking=no -i "$SSH_KEY_FILE" ./경로/*.jar ubuntu@"$DEPLOY_SERVER":/경로/*.jar
    - ssh -o StrictHostKeyChecking=no -i "$SSH_KEY_FILE" ubuntu@"$DEPLOY_SERVER" 'bash ~/.ssh/deploy.sh'
  tags:
    - gitlab-runner 생성시 작성한 Tag 명
  when: manual

배포 단계 역시 master 브랜치가 변경되었을 때(머지나 커밋이 일어났을 때) 파이프라인이 생성되게 하였다.

before script에서 ssh 접근을 위한 기본 작업을 해놓았다. 그리고 script에서 build 단계에서 생성된 JAR 파일을 EC2 서버 내부에 있는 경로로 전송하기 위해 scp를 사용하였다. 그리고, ssh로 서버에 접속해서 deploy.sh 파일을 실행하도록 하였다.

deploy.sh 내용은 밑과 같다.

REPOSITORY=파일이 있는 경로
cd $REPOSITORY

CURRENT_PID=$(pgrep -f JAR파일명) # 현재 구동 중인 무중단 서비스의 PID를 가져온다.

if [-z $CURRENT_PID]; then
                echo "> 현재 구동 중인 애플리케이션이 없으므로 종료되지 않습니다."
else
                kill -15 $CURRENT_PID
                sleep 5
fi

nohup java -jar $REPOSITORY/JAR파일명 &>/dev/null &

위의 스크립트는 JAR 파일이 있는 경로에 들어가서 무중단 서비스가 실행 되고 있으면 해당 서비스를 종료 시키고 현재 빌드되어 있는 JAR를 실행시키는 코드이다.

dev 말고도 stage나 prod deploy 단계에서는 위의 deploy.sh 파일의 내용을 달리하여 어떤 환경을 실행시킬지 선택해서 배포하도록 하였다. 이렇게 해서 각자 브랜치에서 작업을 한 후 master에 머지시키면

이러한 파이프 라인들이 생성된다.


이렇게 단계에 따라 배포하고 싶은 것을 선택하여 재생버튼을 누르면 자동으로 CI/CD가 되는 환경이 완성되었다.

0개의 댓글