내가 일하는 조직에서는 Teamcity 를 통해 CI 환경을 구성하여 사용하고 있다. 그리고 깃허브와 연동하여 작업 브랜치에 push 할 때마다 큐에 enqueue 되도록 설정하여 자동으로 테스트가 돌아가게끔 하였다. 그러던 어느날 팀시티에 보고되는 CI 빌드의 결과를 보니 통과되는 테스트 개수가 브랜치마다 모두 다르게 나오는 것이였다. 테스트를 병렬로 처리해서 그런가 싶어 parallel_test gem 을 쓰지않고 단일 프로세스에서 테스트를 실행시켜 보았으나 이전과 동일하게 테스트 개수가 다르게 나왔다. 빌드 로그를 살펴보니 특정 프로세스에서 에러가 발생하면 해당 프로세스가 담당하는 테스트는 모두 스킵되고 나머지 노드(프로세스)에서 실행한 테스트 결과를 토대로 레포트하기 때문에 개수가 달라지는 것이였다. 예를 들어, 노드가 10개이고 테스트가 100개라고 가정하자. 노드 별로 테스트 10개씩 담당할 때, 특정 한 노드에서 에러가 발생할 경우 해당 노드가 담당하는 10개를 제외한 나머지 90개에 대해서만 테스트를 돌리고 레포트를 한다. 해당 이슈의 원인을 아직까지 정확하게 파악하지 못하였으나 팀시티의 레포트 쪽에 문제가 있지 않을까 싶어 이참에 비용도 절감할 겸 GHA 로 이전할 생각을 하게됐다.
ref: https://docs.github.com/en/actions/using-containerized-services/about-service-containers
GHA 에서는 service container 라는 것을 통해 테스트에 필요한 서비스를 컨테이너로 쉽게 띄울 수 있다.
예를 들어, postgresql 인 경우
jobs:
build:
runs-on: [self-hosted]
services:
postgres:
image: postgres:13.7-alpine
env:
POSTGRES_HOST_AUTH_METHOD: trust # password 를 요구하지 않도록 설정한 환경변수
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 5432:5432
steps:
...
위와 같이 간단하게 12줄 정도만 적어도 빌드 시작시 컨테이너 초기화해주고, 빌드 끝나면 컨테이너를 내려 다음 빌드 시 영향을 안주도록 처리한다. (전, 후처리 알아서 해주는게 너무 좋았다)
위와 같이 redis, dynamodb 등도 서비스를 추가할 수 있으며 만약 private repo 를 사용할 경우 해당 문서를 참고하면 좋을 거 같다. 참고로 필자는 AWS ECR 을 사용하는데 password 수명 주기마다 업데이트를 해야된다길래 self-hosted runner AMI 에 해당 이미지를 미리 pull 받아놓았다.
다시 본론으로 돌아가 GHA 에서 검증하고자 했던 이슈 "error 를 잡아 빌드 실패 상태의 결과를 레포트하는가" 를 실험해볼 차례다. 위 service container 로 테스트에 필요한 서비스를 모두 띄운 뒤 총 두 번의 실험을 하였다.
(코드 보안을 위해 build summary 이미지만 올림...)
1. 정상 테스트
아무런 에러가 없는 코드로 테스트
2. 비정상 테스트
특정 테스트 코드에 syntax error 를 추가하여 테스트
GHA 에서는 에러 검출을 함으로써 빌드 실패 결과를 레포트하는 것을 확인하였다. (그럼 팀시티의 레포트가 문제있는건가?...)
위 작업은 PoC 를 위해 예광탄(실용주의 프로그래머 책에 나온 용어) 형태로 제작하여 실험하였다. 다행이 내가 원하는 결과가 나왔으므로, 이제는 지금까지 만든 것을 다듬어 골격을 만들고 살을 덧붙일 차례다. 또한 팀시티를 사용하면서 AMI 를 만들 때 이전 AMI 로 인스턴스 띄운 뒤 필요한 패키지를 설치하고 AMI 를 재생성하였는데, packer 를 통해 AMI 를 관리한다면 불필요한 패키지는 없어지므로 빠르게 CI 를 돌릴 수 있지 않을까 싶다.