쿠버네티스 배포를 편하게 해준다는 도구를 도입했다가, 몇 달 뒤 걷어냈습니다. 도구가 나빠서가 아니라, 그 도구가 전제하는 운영 방식이 우리가 가려던 방향과 반대였기 때문입니다.
무엇이 문제였나
GitOps 의 전제는 하나입니다. Git 에 있는 것이 곧 클러스터의 상태다. 클러스터를 보고 싶으면 저장소를 보면 되고, 바꾸고 싶으면 커밋하면 됩니다.
도입했던 도구는 설정을 자기 데이터베이스에 두고 있었습니다. 배포할 때마다 거기서 매니페스트를 렌더링해 별도 저장소에 커밋하고, 배포 도구가 그것을 바라보는 구조였습니다. 저장소에 YAML 이 쌓이니 겉보기에는 GitOps 처럼 보였습니다.
문제는 그 저장소가 입력이 아니라 출력이었다는 점입니다. 사람이 그 파일을 고쳐도 다음 배포가 데이터베이스에서 다시 렌더링하면서 덮어씁니다. 그것도 같은 경로를 덮어쓰는 게 아니라 새 버전 디렉터리를 만들고 참조를 옮기는 방식이라, 고친 내용은 조용히 버려집니다. 되돌려졌다는 흔적조차 남지 않습니다.
왜 지금 문제가 되었나
두 가지가 겹쳤습니다.
첫째, 재현이 안 됩니다. 클러스터가 지금 상태인 이유를 저장소만 봐서는 알 수 없습니다. 답은 데이터베이스 안에 있고, 그건 리뷰할 수도 감사할 수도 없습니다.
둘째, 도구가 인프라를 읽지 못합니다. 인프라 작업에 AI 를 붙이려면 현재 상태를 텍스트로 읽을 수 있어야 합니다. 설정이 데이터베이스 안에 있으면 읽을 수 있는 건 산출물뿐이고, 산출물을 고쳐봐야 다음 배포에 지워집니다.
어떻게 했나
새 클러스터를 세우지 않았습니다. 클러스터는 그대로 두고 구성만 옮겼습니다. 이미 수십 개 앱이 돌아가는 중이었으므로 재구축은 선택지가 아니었고, 인수만 가능했습니다.
- 진입 전에 신·구 배포 도구의 CRD 필드 집합을 비교해 사라지는 필드가 없음을 먼저 증명했습니다. 이게 안 되면 인수 도중 설정이 소리 없이 증발합니다.
- 배포 도구를 저장소 기준으로 다시 세우고, 클러스터 권한을 명시적으로 부여했습니다.
- 손으로 관리되던 오브젝트를 앱 단위로 하나씩 저장소에 편입했습니다. 한 번에 하지 않았습니다.
- 편입이 끝난 앱부터 자동 동기화를 켜되, 삭제 동기화는 전 앱에서 금지했습니다.
- 외부 공개 저장소를 참조하는 부분은 커밋 단위로 고정했습니다.
왜 4번과 5번이 중요한가
이 둘이 실제로 사고를 막는 부분입니다.
자동 동기화에 삭제까지 허용하면, 저장소에서 실수로 빠진 오브젝트가 클러스터에서도 사라집니다. 편의 기능 하나가 장애 유발 장치가 됩니다.
업스트림 차트를 태그로 참조하면 남의 저장소 변경이 그대로 우리 운영계에 들어옵니다. 공급망 문제이고, 태그는 옮길 수 있으므로 고정이 아닙니다. 커밋 단위로 못 박아야 합니다.
여기에 더해, 운영 환경을 대상으로 삼으면 렌더 단계에서 실패하도록 차트에 가드를 넣었습니다. 개발용 설정을 복사해 고치다가 실수로 운영을 건드리는 사고가 가장 흔하기 때문입니다.
남은 것
기존 도구가 만들어 둔 산출물 저장소들은 지우지 않고 남겼습니다. 지우면 과거 배포의 근거가 사라집니다. 대신 저장소 문서에 "이것은 산출물이므로 고치지 말 것"을 명시했습니다. 사람도 AI 도 그 경고를 먼저 읽게 됩니다.
무엇을 배웠나
도구를 고를 때 기능만 보면 안 됩니다. 그 도구가 진실을 어디에 두는지를 봐야 합니다.
진실이 Git 밖에 있으면, 아무리 Git 처럼 보여도 GitOps 가 아닙니다.