다른 서버 개발자 및 클라이언트 개발자들과 협업을 하면 지속적인 코드 수정 및 코드 추가가 이루어지므로 지속적인 통합과 지속적인 배포가 필요하다. 하지만 매번 EC2 인스턴스에 접속해서 현재 배포되고 있는 무중단 서비스를 정지시키고 git pull을 한 다음 다시 배포를 하는 과정은 귀찮다. 따라서 용이한 CI/CD를 위해 Gitlab CI pipeline
을 사용하여 CI/CD 환경을 구축할 것이다.
[이 포스트는 Spring Boot 기준으로 작성되었습니다.]
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
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-(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
로 선택하면 된다.
다시 EC2 인스턴스에 접속해서 sudo visudo
를 입력하면 nano 에디터가 켜지는데, gitlab-runner ALL = (ALL) NOPASSWD: ALL
을 입력하여 gitlab-runner에게 권한을 준다.
gitlab-ci.yml을 작성해서 CI/CD 환경을 만들어야 한다.
stages
에서 배포할 때의 파이프라인의 개수(단계)를 설정해주어야 한다.
stages:
- build
- deploy
빌드를 한 후 빌드한 결과물을 배포해야 하므로 build
와 deploy
단계로 나누었다. 물론, 추가 과정이 필요하거나 과정이 하나만 필요하다면 여기서 더하거나 빼면 된다.
cache:
paths:
- .gradle/wrapper
- .gradle/caches
캐시를 어디에 저장할 지 경로는 cache
에서 설정해주면 된다.
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)의 파이프 라인 설정에서 build
와 deploy
단계로 나누었으므로 먼저 build
를 적는다.
그리고 밑에 stage를 통해 명시를 해주고, only
를 통해 어떤 브랜치에서 변경이 있을 시 생성되는 지를 적어준다.
그리고 before script
는 스크립트가 시작되기 전 작업을 의미하고, script
는 작업을 의미한다.
여기서는 ./gradlew clean bulid
를 진행할 것이므로 before script에서 gradle에 권한을 추가해주었다. 그리고, script에서 본격적인 빌드를 실행하고, when
에서 언제 실행할 지 정해준다. 나는 수동 클릭을 통해 빌드 단계를 실행시키고 싶으므로 manual
을 선택하였다.
이때, git pull origin master
을 하지 않아도 되는 이유는 자동으로 gitlab-runner가 해주기 때문이다.
deploy
단계에서 배포를 하고 싶다. 하지만, 배포를 할 때 dev
환경과 실제 환경인 prod
환경, prod
와 dev
의 중간 단계인 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
을 통해 변수를 등록할 수 있다.
pem
키(개인 키) 파일 값 [EC2 접근에 있어서 꼭 필요한 정보이지만, 노출되면 안 되므로]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가 되는 환경이 완성되었다.