CI CD

Circle CI의 Self-Hosted Runner (Docker) 실전 예제

주코식딩 2023. 8. 6. 01:29

나는 단순히 제로타임배포를 하고 싶었을 뿐인데 어쩌다보니 circleci의 self-hosted runner를 설치하게 되었다...

자체서버를 사용하는 젠킨스를 이용해서 구현하려 했으나 circle ci라는 좋은 cicd툴이 있으니 써보라는 말을 많이 들었고 도전해보게 되었다.

 

runner는 젠킨스와 같이 서버에 직접 설치할 수 있다. gitlab또한 runner가 존재하지만 빌드 속도면에성 circle ci가 우세하다는 의견이 많아서 circleci를 채택하게 되었다. (아마 캐싱능력이 더 뛰어나지 않을까 예상해본다.)

 

처음에는 linux용 runner를 설치하려다가 알 수 없는 오류로 실패했다..

이 과정은 굉장히 복잡하고 어려워서 그냥 docker를 이용한 runner를 설치하기로 했다.

 

docker를 이용한 self-hosted runner의 설치 방식은 굉장히 쉽다.

이 과정을 더 쉽게 만들기 위해 아예 sh파일로 만들었다.

 


make_self_hosted_runner.sh

해당 파일에 필요한 변수는 2가지다.

Circle CI에서 만들수 있는 Resource-class이름과 Token만 입력해주면 되게끔 설정해두었다.

해당 변수는 아래 사진처럼 Self-Hosted Runners텝의 화면에서 오른쪽 상단 Create Resource Class 버튼을 누른뒤 생성 가능하다.

 

 

중간에 만드는 Dockerfile.runner.extended 파일은 circleci 공식문서에 나와있는 파일인데 우리 회사는 프로젝트 특성상 docker in docker 전략을 사용해야 하기 때문에 docker와 다른 의존성을 추가해 주었다.

단순히 docker build push만 사용하고자 했으므로 어떻게든 외부의 docker에 접근해보려했으나 실패했다.

컨테이너를 띄울것도 아니고 docker를 설치해서 큰 손해는 보지 않겠다는 생각이 들어서 docker in docker를 채택했다.

runner설치 중 가장 많은 시간을 소요한 부분은 docker-compose의 volume 부분인데 Docker Deamon과 통신하기위한 Unix Socket 이라고 한다.. Docker Deamon과 연결되어 있지 않으면 push를 못한다..

volume없이 runner 컨테이너 내부에서 docker info 명령어를 쳐보면 Client 정보는 잘 나오지만 Server 정보가 나오지않는다.

 

docker는 이미 로그인이 되어있어도 재 로그인시 2~3초정도 소요되기에 빌드시간을 줄이기 위해 docker-compose 파일에서 로그인 하게 했다.

#!/bin/bash

# Set your variables
CIRCLECI_RESOURCE_CLASS= #<리소스 클래스명>
CIRCLECI_API_TOKEN= #<토큰>

# Check if variables are set
if [ -z "$CIRCLECI_RESOURCE_CLASS" ] || [ -z "$CIRCLECI_API_TOKEN" ]; then
  echo "Both CIRCLECI_RESOURCE_CLASS and CIRCLECI_API_TOKEN must be set."
  exit 1
fi

echo "Variables set: CIRCLECI_RESOURCE_CLASS=$CIRCLECI_RESOURCE_CLASS"

# Set your Dockerfile name
echo "Creating Dockerfile..."

DOCKERFILE_NAME=Dockerfile.runner.extended
cat << EOF > $DOCKERFILE_NAME
FROM circleci/runner:launch-agent
RUN sudo apt-get update; \
    sudo apt-get install --no-install-recommends -y \
        python3
RUN sudo apt-get install -y apt-transport-https ca-certificates curl gnupg lsb-release && \
    curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg && \
    echo "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu focal stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null && \
    sudo apt-get update && \
    sudo apt-get install -y docker-ce docker-ce-cli containerd.io && \
    sudo apt-get install -y openjdk-8-jdk openjdk-17-jdk openssh-client
    CMD ["bash"]
EOF

echo "Dockerfile created."

# Step 1: Build Docker image and get Image ID
echo "Building Docker image..."
docker build -f $DOCKERFILE_NAME . | tee build.log
IMAGE_ID=$(tail -1 build.log | awk '{print $3}')
echo "Docker image built with ID: $IMAGE_ID"


# Step 2: Create docker-compose.yml with the Image ID and other variables
echo "Creating docker-compose.yml file..."
cat << EOF > docker-compose.yml
version: '3'
services:
  circleci-self-host:
    image: $IMAGE_ID
    restart: always
    command: >
      echo "xxx" | sudo docker login --username xxx --password-stdin
    environment:
      - CIRCLECI_RESOURCE_CLASS=$CIRCLECI_RESOURCE_CLASS
      - CIRCLECI_API_TOKEN=$CIRCLECI_API_TOKEN
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
EOF

echo "docker-compose.yml file created."

# Step 3: Start the service
echo "Starting service with docker-compose..."
docker-compose up -d
echo "Service started."

Config.yml

config.yml에서 runner에 접근하는 방법은 다음과 같다.

jobs:
  main:
    machine: true
    resource_class: <리소스 클래스명>
    steps: