pushwing을aivance-kr로 바꾸고, CI 러너를 macOS ARM64에서 self-hosted Linux X64로 옮겼다. 한 번 겪으면 끝날 줄 알았는데 AICassa, AICura, AILicet, AITessera, AIFid에서 같은 실패가 같은 순서로 반복됐다. 저장소 여러 개를 한꺼번에 고치며 남은 기록을 정리한다.
1. 시작: 이름 하나 바꿨을 뿐인데
조직 이름을 pushwing에서 aivance-kr로 바꾸면서 CI/CD 러너도 같이 바꿨다. 개인 맥에서 돌던 macOS ARM64 러너를 걷어내고, 조직 공용 self-hosted Linux X64 러너(runner-01~03)로 모았다.
작업 자체는 단순해 보였다.
git remote와 문서의pushwing을aivance-kr로 치환한다.- 워크플로의
runs-on을 새 러너 라벨로 바꾼다.
2026-09-04에 AICassa(#72), AICura(#225), AILicet(#129), AITessera(#80)가 거의 같은 시각(12:36~12:47)에 같은 커밋 메시지로 올라갔다. 아키텍처 다이어그램에 박혀 있던 조직 이름까지 다시 생성했다. 여기까지는 순조로웠다.
2. 함정: CI는 dev → main에서만 돈다
우리 저장소들은 feature → dev 머지에서는 CI를 돌리지 않는다. CI는 dev → main 배포 PR에서 처음 돈다. 러너 교체 PR은 머지되는 순간 "검증됐다"고 착각하기 쉽지만, 사실 아무것도 검증되지 않은 상태였다.
그래서 이 이야기의 진짜 시작은 배포 PR이었다. 빨간불이 나올 때마다 한 줄씩 고쳤고, AIFid에서는 그 한 줄이 PR 열다섯 개로 늘었다(별도 글: 「CI 고치다 PR이 15개 쌓인 날」). 문제는 같은 일이 다른 저장소에서 처음부터 다시 일어난다는 점이었다.
3. 10월 4일: 라벨에 docker 추가
러너 라벨이 [self-hosted, Linux, X64]에서 [self-hosted, Linux, X64, docker]로 바뀌었다. 조직 러너가 컨테이너로 재구성되면서 라벨이 달라진 것이다. 워크스페이스 아래에서 github.com/aivance-kr를 쓰는 저장소를 전부 훑어 runs-on을 일괄 변경했다.
일괄 변경이 가능했던 건 단순 치환이었기 때문이다. 그리고 일부러 건드리지 않은 것도 있었다.
- AIFid
ios-release.yml: CocoaPods 배포는 macOS가 필요하다. - AICella 데스크톱 릴리스처럼 macOS 빌드가 본질인 워크플로.
"전부 Linux로"가 아니라 "Linux로 갈 수 있는 것만"이 기준이다. 이걸 놓치면 iOS 릴리스가 조용히 깨진다.
4. 10월 5일 아침: 같은 실패의 합창
라벨을 바꾸고 나니 러너가 컨테이너라는 사실이 줄줄이 드러났다. 아래는 AICassa 커밋 시각이다. AICura, AILicet, AITessera, AIFid도 몇 분 차이로 같은 순서를 밟았다.
| 시각 | 증상 | 원인과 수정 |
|---|---|---|
| 07:55 | (라벨 변경) | docker 라벨 추가 |
| 08:09 | MySQL Connection refused |
호스트에 게시한 포트는 러너 컨테이너에서 127.0.0.1로 닿지 않는다. 실행별 도커 네트워크를 만들어 러너와 DB 컨테이너를 붙이고 컨테이너 이름으로 접속 |
| 08:22 | PHP 버전 미달 | runner-01은 PHP 8.5, runner-03은 네이티브 Ubuntu 24.04(PHP 8.3.6). composer.json 요구 미만이면 ondrej PPA로 8.5 설치하는 "PHP 보장" 단계 추가 |
| 08:33 | composer install이 확장 때문에 실패 |
composer.lock의 ext-* 요구(gd, sqlite3 등)를 설치 목록에 반영 |
| 08:40 | PHPUnit이 5분 넘게 멈춤 | MySQL 컨테이너를 --tmpfs + 내구성 완화 옵션으로 기동 |
| 08:47 | 여전히 느림 | paratest 병렬화 |
AICassa는 직렬 5분 이상이던 테스트가 46초로 줄었다.
배운 점은 순서다. 첫 저장소에서 한 번 고친 뒤 나머지 저장소에 같은 패치를 옮겼다면 시간이 훨씬 덜 들었을 것이다. 실제로는 저장소마다 증상을 만나고서야 고쳤다. 그래서 이번 정리의 가장 큰 산출물은 코드가 아니라 공통 가이드다. claude-works/CLAUDE.md와 전역 testing.md에 "컨테이너 러너 주의사항"을 적어, 다음 저장소는 증상을 만나기 전에 처음부터 올바르게 쓰도록 했다.
5. 저장소마다 달랐던 변주
반복되는 큰 줄기 위에 저장소별로 한두 개씩 다른 지뢰가 있었다.
- AITessera: MySQL뿐 아니라 Redis도 같은 도커 네트워크로 붙여야 했다. 커버리지 코멘트에 쓰던
irongut/CodeCoverageSummary는 Docker 컨테이너 액션이라서, 컨테이너 안의/github/workspace에 러너 컨테이너의 작업 디렉터리가 보이지 않아No files found matching glob pattern으로 죽었다. 셸 스텝에서 build/coverage.txt로 마크다운을 직접 만들도록 바꿨다. - AICura: Flutter가 같이 있어서
fatal: detected dubious ownership(exit 128)이 났다. 러너는 root인데 SDK 소유자가 달랐다.flutter-action직후git config --global --add safe.directory "$FLUTTER_ROOT"로 해결했다. 또 macOS와 sed 문법이 달라서(sed -i ''vssed -i) 9월 5일에 호환 수정이 한 번 있었다. - AILicet: 9월 5일
setup-phpPPA 의존을 걷어내고 사전 설치된 PHP를 검증하는 방식으로 먼저 바꿨다가, 10월 5일에 러너마다 이미지가 다르다는 걸 알고 "없을 때만 PPA로 설치"로 다시 바꿨다. 한 번 틀린 가정을 두 번 고친 셈이다. - AIFid: 배포용 SSH 호스트(
DEPLOY_HOST)가 Cloudflare 프록시(주황 구름) 도메인이면 SSH 포트가 프록시를 통과하지 못해i/o timeout이 난다. 프록시 없는 직접 접속 호스트로 바꿔야 한다. - Pagus: 이쪽은 워크플로 정리 대상이 아니었다. 원격 주소가
aivance-kr로 바뀐 것을 반영하는 정도로 끝났다. 조직 이름 변경은 코드 쪽 변경이 없어도git remote -v는 한 번씩 확인해야 한다.
6. 병합하고 보니 또 충돌
main에 직접 머지된 CI 변경이 있으면 다음 dev → main PR이 ci.yml에서 충돌한다. AICura에서는 main을 dev로 동기화하는 별도 PR(#231)을 만들어 충돌을 풀었다. 이때 지켜야 할 규칙이 하나 있다. 동기화 PR은 squash가 아니라 merge commit으로 머지한다. squash하면 main이 dev의 조상이 아니게 되어 다음 배포에서 똑같은 충돌이 되살아난다.
또 하나. PR이 이미 머지된 뒤 같은 브랜치에 푸시한 커밋은 dev에 들어가지 않는다. 추가 수정은 브랜치 상태를 확인하고 새 브랜치, 새 PR로 올려야 한다. 이 함정에는 이번에도 한 번 걸렸다.
7. 정리: 다음에 조직을 또 옮긴다면
- 치환은 기계적으로, 검증은 배포 PR 전에. CI가
dev → main에서만 돈다면, 러너 변경은 로컬 docker로 DB 연결과 테스트 명령을 먼저 재현해 보고, YAML은ruby -ryaml -e 'YAML.load_file(...)'정도로라도 문법을 확인한다. - 컨테이너 러너 체크리스트를 먼저 적용한다. 실행별 도커 네트워크, PHP 보장 단계,
composer.lock기반 확장 목록,XDEBUG_MODE=coverage, Docker 컨테이너 액션 금지,safe.directory, 프록시 없는 SSH 호스트. - macOS가 본질인 잡은 남긴다. iOS 릴리스처럼 Linux로 못 가는 워크플로를 구분해 둔다.
- 한 저장소에서 풀면 바로 나머지에 옮긴다. 같은 패치를 다섯 번 다시 발견하는 비용이 가장 크다.
- 고친 내용은 공통 가이드에 남긴다. 이번에 가장 오래 쓸 결과물은 이 체크리스트다.
이름 하나 바꾸는 작업은 사실 "우리 CI가 얼마나 환경에 기대고 있었는가"를 드러내는 작업이었다. 개발자 맥에서는 우연히 초록불이던 것들이 새 러너에서 전부 이유를 드러냈다.
댓글 0개