
K8s 위에서 Gitea를 돌리고 있다. GitHub 쓰면 편하지만, 홈랩에서 CI/CD 파이프라인을 직접 구성하고 싶었고, Gitea Actions가 GitHub Actions와 거의 동일한 문법을 지원하기 때문에 선택했다.
운영하면서 두 가지 문제를 만났다. 하나는 ArgoCD webhook이 먹통이 된 것, 다른 하나는 배포할 때마다 pod이 CrashLoop에 빠지는 것. 둘 다 원인을 찾고 나면 "아, 이거였어?" 싶은 설정 문제였는데, 모르면 한참을 헤맨다.
Gitea에서 코드를 push하면 ArgoCD webhook으로 즉시 동기화를 트리거하는 구조다. 그런데 어느 날부터 webhook delivery 내역을 보니 전부 실패. 에러 메시지는 이랬다:
Webhook can only call allowed HTTP serversArgoCD 서버 주소는 argocd-server.argocd.svc.cluster.local이고, K8s 내부에서 이 도메인은 10.x 대역 ClusterIP로 resolve된다. 문제는 Gitea 1.16부터 webhook.ALLOWED_HOST_LIST 기본값이 external로 바뀌었다는 것. 보안 강화 목적인데, 이게 K8s 내부 IP 대역으로의 webhook 전달을 전부 차단한다.
해결은 ConfigMap에 한 줄 추가하면 끝이다:
data:
GITEA__webhook__ALLOWED_HOST_LIST: "private"private으로 설정하면 RFC 1918 사설 IP 대역(10.x, 172.16.x, 192.168.x)을 전부 허용한다. K8s 내부 서비스끼리 통신해야 하는 환경에서는 이게 맞다.
Gitea 릴리스 노트에 분명히 적혀 있었을 텐데, 업그레이드할 때 꼼꼼히 안 읽으면 이런 식으로 당한다. 보안 강화를 위한 기본값 변경이 K8s 내부 통신을 끊어버리는 케이스.
Gitea Deployment를 업데이트할 때마다 새 pod이 뜨자마자 CrashLoop에 빠졌다. 로그를 보면:
resource temporarily unavailable원인은 LevelDB 파일 락이다. Gitea는 내부적으로 LevelDB를 사용하는데, LevelDB는 데이터 디렉토리에 LOCK 파일을 만들어서 배타적 접근을 보장한다. RollingUpdate 전략에서는 새 pod이 기존 pod이 종료되기 전에 먼저 뜨는데, 두 pod이 동시에 같은 PVC의 LOCK 파일에 접근하려다 충돌이 발생한다.
해결은 strategy를 Recreate로 바꾸는 거다:
spec:
replicas: 1
strategy:
type: Recreate # RollingUpdate → RecreateRecreate는 기존 pod을 완전히 내린 뒤 새 pod을 띄운다. 짧은 다운타임이 생기지만, 홈랩 Gitea는 사용자가 나 혼자라 문제없다. 오히려 데이터 정합성이 더 중요하다.
이건 Gitea만의 문제가 아니다. SQLite, LevelDB 등 파일 기반 DB를 사용하는 모든 앱은 K8s에서 반드시 Recreate strategy를 써야 한다. 두 프로세스가 동시에 같은 파일에 쓰면 락 충돌은 가벼운 거고, 데이터 손상까지 갈 수 있다.
위 두 이슈를 해결한 김에 Gitea 버전도 올렸다. 1.23에서 1.25로, 2 minor 버전을 한 번에 건너뛰었다.
containers:
- name: gitea
image: gitea/gitea:1.25 # 1.23 → 1.25업그레이드 이유는 Actions API 개선 때문이다. 1.25에서 job 로그 조회 API가 추가되었고, CI/CD 파이프라인 디버깅이 훨씬 편해졌다.
Gitea는 DB 마이그레이션을 자동으로 처리하기 때문에 버전을 올리면 pod 시작 시 알아서 스키마 변경이 적용된다. 1.23 → 1.25도 로그에 마이그레이션 쿼리가 줄줄 뜨더니 정상 부팅되었다. strategy를 Recreate로 바꿔놓은 덕에 마이그레이션 중 다른 pod이 간섭할 걱정도 없었다.
webhook.ALLOWED_HOST_LIST: K8s 내부 서비스로 webhook을 보내야 한다면 private으로 설정. 기본값 external은 사설 IP를 차단한다.Recreate 필수. LevelDB 파일 락 때문에 RollingUpdate는 CrashLoop을 유발한다.작은 설정 하나가 CI/CD 파이프라인 전체를 멈출 수 있다. 셀프호스팅은 자유도가 높은 만큼, 이런 운영 디테일을 직접 챙겨야 한다. 릴리스 노트를 읽는 게 귀찮지만, 안 읽으면 더 귀찮은 일이 생긴다.