사전 준비
이 글은 기본적으로 EC2 인스턴스 생성 및 인바운드 보안규칙, elastic ip 등이 모두 사전 구성이 완료되어있는 상태에서 작성되었다. 초기 생성 관련 정보는 아래의 블로그를 참고하면 도움이 된다.
추가적으로 public ip와 private ip의 차이, 인바운드 규칙과 ufw 방화벽의 차이, 나아가 도커 이미지와 컨테이너의 작동 방식 정도 또한 파악한 다음 진행한다면 이해에 큰 도움이 될 것이다.
gitlab-runner란?
GitLab Runner는 GitLab과 함께 사용되는 오픈 소스 애플리케이션으로,GitLab CI/CD 파이프라인 내에서 작업을 실행하는 역할을 수행한다. 몇 가지의 사전 설정 및 배포 스크립트 작성을 통한 파이프라인 구축만 해준다면 가벼우면서도 빠르게 배포 자동화를 구축할 수 있다는 점이 가장 큰 장점이다. (github-action과 젠킨스 그 사이 즈음에 위치하는 느낌이랄까)
젠킨스와 달리 gitlab-runner는 다양한 플러그인 설치 혹은 파이프라인 커스터마이징 등에 대해서는 아무래도 뒤처진다. 그러나 젠킨스의 초기 설정 비용이나, 서버 내 젠킨스 컨테이너가 차지하는 상당량의 리소스를 감안해본다면, gitlab-runner는 상당히 좋은 선택지가 될 수 있다고 생각한다.
메모리 스왑
만약 프리티어를 활용중이라면 메모리 스왑을 해주는 것이 정신건강에 이롭다.
프리티어의 기본적인 램 사양은 1기가에 불과하고 여기에 여러 컨테이너를 띄우게 된다면..
아마 십중팔구는 인스턴스 cpu 사용량이 치솟다가 죽어버리는 경험을 하지 않을까 싶다.
이렇기에 프리티어에서의 젠킨스를 더더욱 멀리 했던 것 같다.
젠킨스 권장 최소 사양이 1기가 라는 점을 미루어보았을때.. 프리티어에서 사용하기엔 상당히 무겁다는 걸 알 수 있다.
아무튼! 본론으로 돌아와서, 프리티어에서는 기본적으로 메모리 스왑을 진행해주는게 안정적이다.
메모리 스왑과 관련된 내용은 해당 블로그를 참고하면 도움이 많이 될 것이다.
https://kth990303.tistory.com/361
1. EC2 환경 설치
# root로 전환
sudo su
# 시스템 패키지 리스트 업데이트
apt install update
# curl 설치
apt install curl
# vim 설치 (yml 파일의 원활한 작성 및 수정을 위함)
apt install vim
# 네트워크 도구 설치
apt install net-tools
# 도커 및 도커 컴포즈 설치
apt install docker.io docker-compose
# ec2 public ip 확인
curl ifconfig.me
# private ip 확인
hostname -I | awk '{print $1}'
위의 스크립트는 배포에 사용할 다양한 패키지 설치 명령어이다.
주석에 어떤 설치를 진행하는 것인지 기술해놓았다.
설치를 완료했다면 이후public ip와 private ip를 각각 확인한다.
2. gitlab-runner 설치
혹시라도 이전에 등록해놓은 register를 모두 해제하고 싶다면 해당 명령어를 실행하고 설치를 진행하도록 하자.
gitlab-runner verify --delete
gitlab-runner unregister --all-runners
이제 gitlab-runner install 과 register 단계를 진행한다.
gitlab-runner 는 gitlab의 해당 레포지토리에서, Settings – CI/CD – Runners에 접근하여 설치 가능하다.
(좌측 하단에 보이는 초록색 점은 EC2 와 현재 연결이 되어있는 상태라 파란불이 뜬다)
New project runner 버튼을 눌러서 runner를 생성하도록 한다.
runner 생성 시에는, os는 리눅스를 지정하고, 태그는 따로 지정하지 않았으며, run untagged jobs 체크를 하여 기본적으로 태그가 없는 job 까지도 스크립트에서 실행하도록 하였다.
이후 하단에 이어지는 화면에서 step에 따라 gitlab-runner 설치 및 register를 진행하도록 하자.
상단에 보이는 —-토큰 모자이크—- 부분은 복사해놓도록 하자. 레지스터 단계에서 필요하다..!
빨간색 체크를 해놓은 부분을 누르면 gitlab-runner 설치 스크립트가 나온다. 해당 내용은 하단 코드블럭과 같다.
# Download the binary for your system
sudo curl -L --output /usr/local/bin/gitlab-runner https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-amd64
# Give it permission to execute
sudo chmod +x /usr/local/bin/gitlab-runner
# Create a GitLab Runner user
sudo useradd --comment 'GitLab Runner' --create-home gitlab-runner --shell /bin/bash
# Install and run as a service
sudo gitlab-runner install --user=gitlab-runner --working-directory=/home/gitlab-runner
sudo gitlab-runner start
gitlab-runner 설치를 완료했다면, step에 따라 register를 진행해야한다.
여기서 부터 중요한 내용들이 나오므로 집중하도록 하자
dind vs dood
도커 컨테이너에 gitlab-runner를 올리기 전에 한 번 짚고 넘어가야할 개념이 있다.
바로 dind(docker in docker) 와 dood(docker out of docker) 방식이다.
그림을 보면 이해가 쉽다.
dind는 말 그대로, “도커 안의 도커”이다.
도커 컨테이너 내부에 호스트 도커 데몬과는 별개의 새로운 도커 데몬을 실행시키는 것을 의미한다. 이 방식은 컨테이너 내부에서 도커 커맨드를 실행할 때, 그 커맨드가 외부의 호스트 도커 데몬이 아닌, 컨테이너 내부에 설치된 도커 데몬을 통해 실행되도록 하는 것이다.
이와 달리 dood는 “도커 바깥의 도커”이다.
호스트 도커 데몬이 사용하는 소켓을 공유하여 도커 클라이언트 컨테이너에서 컨테이너를 실행시키는 것을 의미한다. 이는 호스트 시스템의 도커 소켓을 컨테이너에 마운트함으로써 이루어진다. 이 방식을 통해 컨테이너 내부에서 도커 커맨드를 실행할 때 호스트의 도커 데몬을 통해 컨테이너를 생성하고 관리할 수 있게된다.
두 가지 방식에서의 주된 장단점을 간략하게 소개하자면, 성능적으로는 dind는 dood에 비해 추가적인 오버헤드가 발생하는 편이기에 dood가 우세한 편이다. 나아가 dood는 호스트의 도커 데몬을 직접 사용하기에 리소스 사용에 있어서도 보다 효율적이다.
보안적인 관점에서 살펴보자면 dind 방식과 dood 방식 모두 문제점을 가지고 있다고 할 수 있다.
dind 의 경우, –privileged 모드로 실행이 되는데, 이는 컨테이너가 호스트이 커널 기능에 무제한적인 접근이 가능해지는 것이므로 보안 위험을 초래한다.
dood의 경우에는, 호스트의 도커 소켓을 컨테이너와 공유하는 것이기에, 해당 컨테이너가 상당한 제어권을 가지게 되는 것이고 이는 곧 보안적으로 취약해지는 것이라 할 수 있다.
이러한 점들로 미루어보았을 때, dind dood 모두 잠재적인 위험을 가지고 있고, 어느 한 쪽이 절대적으로 더 안전하다고 단정을 지을 수는 없다고 한다. 결국 컨테이너가 필요로 하는 권한 수준, 실행 환경의 보안 정책, 그리고 관리 가능한 위험 정도에 따라 다방면으로 고려해보아야한다.
이를 바탕으로 다시 배포 단계로 돌아가보자.
지금부터 진행하는 방식은 dood 형태를 기반으로한다.
이유는 dind로 진행하다가 수 많은 오류를 경험했고, 많은 블로그들에서 제공하는 정보들 또한 정확하지 않은 정보들이 너무 많았기에 이로 인한 시행착오가 너무 많았다.
나아가, 도커 측에서도 dind 보다는 도커 호스트에 컨테이너를 병렬로 띄우는 dood 방식을 더 권장하기도 하기에 dood 방식으로 진행했다.
3. 도커 컨테이너에 runner 올리기
docker run --detach \
--name gitlab-runner \
--restart always \
--volume /srv/gitlab-runner/config:/etc/gitlab-runner \
--volume /var/run/docker.sock:/var/run/docker.sock \
gitlab/gitlab-runner:latest
# [명령어에 대한 설명]
# 1. 도커 컨테이너를 백그라운드 모드(detached mode)로 실행한다.
# 2. 컨테이너 이름은 "gitlab-runner"
# 3. 컨테이너가 중지되면 항상 자동으로 재시작 되도록 한다.
# 4. 호스트 시스템의 '/srv/gitlab-runner/config' 디렉토리를 컨테이너의 '/etc/gitlab-runner'에 마운트한다.
# -> 이는 GitLab Runner 설정을 보존하고 공유하는 데 사용된다.
# 5. 호스트 시스템의 도커 소켓 파일('/var/run/docker.sock')을 컨테이너 내부의 같은 위치에 마운트한다.
# -> 이를 통해 컨테이너 내부에서 호스트의 도커 데몬을 제어할 수 있게 되며, 이것이 dood 접근 방식의 핵심!
# 6. 이미지 지정 (최신 gitlab-runner 이미지를 사용하도록)
위의 명령을 통해 gitlab-runner를 컨테이너에 올린다. 상세 내용은 하단에 기술해 놓았다.
이후 docker ps 명령을 통해 컨테이너가 잘 올라갔는지 확인한다.
4. gitlab-runner 등록
1. 컨테이너에 접속한다.
# 컨테이너 접근
docker container exec -it gitlab-runner bash
2. gitlab-runner 설치 시 산출된 토큰으로 컨테이너 내부에서 register
# gitlab-runner 컨테이너 내부에서 인증
gitlab-runner register -n \
--url [gitlab 서버주소] \
--registration-token [복사해뒀던 토큰] \
--description gitlab-runner \
--executor docker \
--docker-image docker:latest \
--docker-volumes /var/run/docker.sock:/var/run/docker.sock
# [명령어 설명]
# 1. "-n" 옵션으로 비대화 모드로 등록
# 2. gitlab-runner가 연결할 gitlab 인스턴스의 url 설정 (gitlab 서버 주소)
# 3. gitlab-runner 설치 단계에서 생성된 등록 토큰 사용하여 인증
# 4. runner 설명(식별) 설정
# 5. 실행 주체 = 도커로 지정한다. 도커가 컨테이너 내에서 ci/cd 작업을 실행하도록 한다.
# 6. 기본 도커 이미지 설정 (최신)
# 7. 호스트의 도커 소켓 파일을 runner 컨테이너에 마운트한다.
# -> 이를 통해 runner 컨테이너 내부에서 호스트의 도커 데몬에 접근하여 도커 명령을 실행할 수 있게 된다.
최종적으로 아래의 사진처럼 Settings – CI/CD – Runners 탭에서 runner가 연결되어 초록불이 뜨는 것을 확인한다.