AWS EC2로 구성한 GitHub Actions Runner 복제하기

2021-12-16

들어가며

천만년만의 기술 글이다. 평소에 회사에서 사용하는 기술은 회사 내에만 적용하는 경우가 많아서 글로 적을만 한 것이 별로 없었는데, 이번 경험은 꽤 범용적이라고 생각하여 정리해본다.

GitHub Actions의 Self-hosted runner를 사용하고 있는데, 회사 내에 1대만 있어 오래 걸리는 테스트가 하나 들어가면 그 뒤가 한참 밀려버리는 문제가 있었다. 그래서 일단 2개로 늘려보자고 생각했고, 이를 다시 처음부터 세팅하기는 뭐가 있는지도 잘 모르고 여러모로 귀찮아서 이미 있던 인스턴스를 복제하여 사용하고자 했다. 새 환경이 되면 Git Repository나 Maven Repository, Gradle Cache 같은 것이 다 날아가는 것에 대한 속도 고려도 있었다.

사실은 Java 같은 것도 actions/setup-java 같은걸 사용해서 처음부터 세팅하는 것도 어렵지 않게 되어야 할 테지만 프로젝트가 많다 보니 일괄 적용은 아무래도 무리가 있다.

EC2 인스턴스 복제

복제는 어렵지 않다. AMI 생성, 템플릿 생성하며 AMI 연결, 템플릿으로 인스턴스 생성, AMI 제거 순으로 하면 된다.

AMI 생성

그림 1 AMI 생성

볼륨도 함께 다 스냅샷으로 만들어주는 기능이 제일 필요하다. '재부팅 안 함' 기능이 궁금하지만 '일관된 상태' 인지 잘 모르겠어서 그냥 기본 값으로 뒀다.

템플릿 생성하며 AMI 연결

템플릿으로 인스턴스 생성

그냥 하면 된다. 스크린샷이 의미 있는지 잘 모르겠어서 넘긴다.

AMI 제거

AMI 자체는 요금이 없는 것으로 보이지만, EBS 스냅샷은 확실히 과금이 있다.# 더 만들 거면 그때 가서 또 뜨면 되므로 그냥 삭제한다.

확실히 완전 복제는 아니고 템플릿을 이용한 신규 생성이라, 켜보면 hostname이 ip-XXX-XXX-XXX-XXX 형태로 되어 있다. hostnamectl set-hostname 으로 변경하면 된다.

설정

이제 서비스를 등록해야 하는데, 해보면 이미 설정이 되어있다고만 나온다.

그림 2 It is already configured.

remove는 실행해보면 토큰을 받아서 지우는 형태로, 아예 등록된 Self-hosted runner에서 삭제하는 것이라 기존 인스턴스에서도 다시 등록해야 하므로 원하는 동작이 아니다.

문서를 찾아봐도 client 쪽에서만 인증 정보를 초기화하는 방법은 찾을 수 없었다. 오류 메시지가 있고, runner가 오픈소스이므로 추적해보기로 했다.

해결 방법

Self-hosted runner 위 메시지를 검색해보면 다음 위치를 찾을 수 있다.

actions/runner@da79ef4/src/Runner.Listener/Configuration/ConfigurationManager.cs#L83-L86

if (IsConfigured())
{
    throw new InvalidOperationException("Cannot configure the runner because it is already configured. To reconfigure the runner, run 'config.cmd remove' or './config.sh remove' first.");
}

따라가 보자.

actions/runner@da79ef4/src/Runner.Listener/Configuration/ConfigurationManager.cs#L45-L47

public bool IsConfigured()
{
    bool result = _store.IsConfigured();

actions/runner@7ffd9af/src/Runner.Common/ConfigurationStore.cs#L168-L171

public bool IsConfigured()
{
    Trace.Info("IsConfigured()");
    bool configured = new FileInfo(_configFilePath).Exists;

actions/runner@7ffd9af/src/Runner.Common/ConfigurationStore.cs#L145

_configFilePath = hostContext.GetConfigFile(WellKnownConfigFile.Runner);

actions/runner@a519f96/src/Runner.Common/HostContext.cs#L294-L297

case WellKnownConfigFile.Runner:
    path = Path.Combine(
        GetDirectory(WellKnownDirectory.Root),
        ".runner");

actions/runner@a519f96/src/Runner.Common/HostContext.cs#L237-L238

case WellKnownDirectory.Root:
    path = new DirectoryInfo(GetDirectory(WellKnownDirectory.Bin)).Parent.FullName;

actions/runner@a519f96/src/Runner.Common/HostContext.cs#L221-L222

case WellKnownDirectory.Bin:
    path = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);

대충 정보는 다 나왔고 이제 어셈블리가 어디에 있는지만 알면 된다.

actions/runner@3ec20e9/src/Misc/layoutroot/config.sh#L80

./bin/Runner.Listener configure "$@"

bin의 상위 폴더, 즉 config.sh 파일이 있는 경로에서 .runner 파일을 지워주면 되는 것이다. 진작에 ls -al 해 볼걸...

나오며

Kubernetes 안에 actions-runner-controller 설정해서 쓰고 싶다.

돌아가기