[Gitlab CI/CD 1] CI 적용하고 CD는 비동기 처리하기
인턴십에서 개발에 참여한 프로젝트의 배포 자동화 업무를 담당하게 되었다. 처음에는 기존에 회사에서 사용하고 있던 방식으로 배포했는데, 이 방식이 온프레미스 서버에 수동으로 배포하는 작업이었다. 이를 위해서 애플리케이션을 로컬에서 빌드하고, 빌드한 이미지를 서버에 보낸 후 서버에 접속해서 run하는 절차를 거쳐야 했다.
불필요하게 반복적인 작업을 자주하는 것도 불편했고, 특히 빌드하는 시간동안 유의미한 작업을 할 수가 없어서 Gitlab을 이용한 CI/CD 배포 자동화에 도전하게 되었다. 적용해보고 - 피드백 받고 - 개선하는 일련의 과정을 기록해두려고 한다!
gitlab-ci.yml
(빌드 부분만 가져왔다.)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
build:
image: docker:20.10.16
stage: build
services:
- docker:20.10.16-dind
variables:
IMAGE_TAG: $CI_REGISTRY_IMAGE:image-$CI_COMMIT_BRANCH
before_script:
- cp ${QA_ENV_FILE} .env.qa
script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD
- docker build -t $IMAGE_TAG -f Dockerfile.qa .
- docker push $IMAGE_TAG
only:
- qa
except:
- develop
- docker build가 Dockerfile.qa를 이용해서 이미지를 빌드하는데, Dockerfile.qa는 .env.qa를 필요로 한다. Gitlab > Settings > CI/CD > Variables에서 설정이 필요하다.
- before_script에서 .env.qa를 만들도록 하고 script에서 빌드할 때 이것을 이용하도록 했다. 도움이 되었던 스택오버플로우의 글을 첨부한다.
How do I add a .env file in gitlab ci during deployment stage?
Variables
- Gitlab (적용할 프로젝트 선택) > Settings > CI/CD > Variables에 위치한다.
- job script에서 사용할 변수들을 저장할 수 있고, 8000개까지 정의할 수 있다.
- 단일 변수를 지정할 때는 Type을 Variable(default)로 지정해도 되는데, 현재 프로젝트처럼 .env 파일을 정의한 경우에는 Type을 File로 변경하는 것을 잊지 말자. (이 부분을 놓치면 이후에 변수나 파일이 정의되지 않았다, 찾을 수 없다는 오류와 함께 잡이 종료한다.)
- Flag를 Expand variable reference로 지정해서 script 내에서 $로 참조할 수 있다.
IMAGE_TAG
- 위와 같이 스크립트를 설정하고 job을 수행하면,
docker push $IMAGE_TAG
로 인해 Deploy > Container Registry에 이미지가 push 된다. - ✅ 이미지의 이름에 경로가 포함되어 있으므로, 회사 내부 온프레미스 서버에 스크립트를 작성하여 이 경로를 통해 이미지를 pull 받아서 실행할 수 있도록 한다!
Predefined CI/CD variables refernece
이미지 이름에서 앞부분 REPOSITORY는 경로를 의미하고, TAG는 gitlab-ci 스크립트에서 IMAGE_TAG로 설정한 부분이다.
$CI_REGISTRY_IMAGE
: image가 저장된 registry의 경로$CI_COMMIT_REF_SLUG
: 현재 커밋 sha$CI_COMMIT_BRANCH
: 커밋이 발생한 브랜치
Gitlab 공식 문서의 Predefined CI/CD variables reference를 참고하여 적절히 설정하자!
only, except
배포 목적이라서 push 할 때마다 빌드할 필요는 없다. push할 때마다 build를 하면 container registry에 지나치게 이미지가 많아진다. qa 브랜치에 머지될 때만 파이프라인이 동작하도록 설정한다.
- only를 이용하면 설정한 조건에서만 동작, except를 이용하면 설정한 조건 외에서 항상 동작한다.
deploy token
gitlab-ci를 통해 이미지를 빌드하면 Container Registry에 이미지가 저장된다. 사내 온프레미스 서버에서 Gitlab 서버에 접근하여 이미지를 Pull 받기 위해서는, 먼저 로그인이 필요하다. 이를 위해 Deploy Token을 발급한다.
deploy_qa.sh
내가 직접 짠 스크립트이지만, 아무래도 회사에 있는 코드이기 때문에 주석만 남기려고 한다.
1
2
3
4
5
6
7
8
9
# config
# gitlab access
# docker pull image
# docker stop old container
# docker run new container
- image-qa 태그의 웹 이미지를 만들면 container registry에서 충돌이 날 것 같지만, 업데이트 하며 새로 Published 하기 때문에 문제 없다.
- 현재 배포 버전에 문제가 생기면 예전 이미지로 롤백 시키고 작업을 해야하기 때문에, 가장 최신 버전의 이미지가 아닌 이전 버전의 이미지도 저장할 필요가 있다. 이를 old 태그를 붙여서 관리했다.
crontab
1
30 17 * * * /home/mint/@@@/web/deploy_qa.sh >> /home/mint/@@@/web/logs/deploy_qa.log 2>&1
1
2
3
4
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
@@@@@@-web staging-current a0740f41@@@@ 18 hours ago 698MB
@@@@@@-web staging-old 94486940@@@@ 19 hours ago 698MB
1
2
3
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
f3eefb3b5130 @@@@@@-web:staging-current "docker-entrypoint.s…" About an hour ago Up About an hour 0.0.0.0:80->3000/tcp, :::80->3000/tcp safety-web
피드백
여기까지 작업을 마치고 주간회의 시간에 일련의 처리 과정을 발표했다. 받은 피드백은 다음과 같다.
- 빌드 부분을 자세히 설명하길래 gitlab-ci.yml 파일을 봤는데, 실행에 필요한 환경 변수까지 빌드 단계에서 주입하는 것은 지양해야 한다. 현재는 Dockerfile 자체가 .env의 모든 내용을 이용하도록 설계되어 있는데, 변수를 분리해라.
- Gitlab은 CI/CD 도구인데, 현재는 CI까지만 Gitlab의 도움을 받는다. 서버에서 크론탭으로 스크립트를 실행 시킬 필요없이, 내부 서버에 접속해서 스크립트를 실행하는 것까지 Gitlab의 도움을 받아서 해결할 수 있다. 여기까지 시도해보는 것은 어떤지?