[CI / CD] 당신은 정말 지속적인 통합(CI)을 하고 있는가?
안녕하세요? 제이온입니다.
과거 힐링페이퍼 면접 이후 이규원 CTO 님께서 제가 가지고 있던 CI의 오해를 바로 잡기를 권유하시며, 한 가지 포스팅을 추천해 주셨습니다. 이번 시간에는 그 포스팅을 제가 번역한 내용을 작성하고, 스스로 느낀 점과 함께 우아한테크코스 다라쓰 팀에는 어떻게 적용하고 있는지 설명드리겠습니다. (번역은 오역이 있을 수 있으니 원본 포스팅도 꼭 정독하시길 바랍니다.)
번역 내용
서론
ThoughtWorks 기술 레이더는 최근 기술 팀 안티 패턴인 "CI 극장"을 피할 것을 권고했습니다. CI 극장은 실제로 실행하지는 않지만 지속적인 통합(CI)을 실행한다고 착각하는 행위를 뜻합니다. 제 동료인 Emily Luke와 제가 수행한 지속적인 통합 연구를 바탕으로, CI 극장이 어떠한 모습인지, CI 극장을 하면 안되는 이유가 무엇인지, 그리고 CI 극장을 극복하는 방법에 대해 말씀드리겠습니다.
지속적 통합
제가 좋아하는 CI의 정의는 https://continuousdelivery.com/의 Jez Humble에서 따왔습니다.
CI 개발자는 정기적으로 (최소한 매일) 모든 작업을 Trunk (master 브랜치라고 이해하면 됩니다.)에 통합합니다.
이 인용구에는 CI 관행의 두 가지 기본 원칙이 숨어 있습니다. 첫 번째는 "모든 작업을 Trunk에 통합"이고, 두 번째는 "최소한 매일"입니다.
또한 소스 저장소에서 모든 것을 확인, 모든 커밋 구축, 빌드 자동화, 빌드 속도를 빠르게 유지, 자체 테스트 코드 보유, 눈에 보이는 오류를 표시하고 즉각적인 수정과 같은 CI를 만드는 다양한 원칙과 관행도 있습니다. 디테일한 내용은 마틴 파울러님이 작성하신 포스팅을 참고하시길 바랍니다. 개인적으로 저는 적어도 하루에 한 번 마스터 브랜치를 확인하는 것이 좋은 CI를 위한 기본적인 초석이라고 생각합니다. 그리고 그것이 없다면, 여러분은 CI를 연습하는 것이 아니라 CI 극장을 연습하는 것입니다.
CI 극장은 어떻게 생겼는가?
여기 저희 연구에서 나온 이야기가 있습니다. 경험 많은 개발자인 David는 Bay 지역에 있는 중간 규모의 신생 업체 출신으로 일 주일에 두 번 운영 서버에 배포합니다. David는 자신의 조직이 CI를 실천하고 있다고 표현하면서 "네, Cicle CI를 사용합니다."라고 말했습니다. 그는 한 branch에서 작업하고, 100개의 파일과 7000줄의 코드를 바꾼 뒤 코드 리뷰에 들어가 동료들에게 자신의 모든 변경 사항을 설명하기 굉장히 어려웠다고 설명했습니다.
그는 "가장 어려운 것은 한 번의 커밋에 많은 특징들이 쌓이게 되는 것입니다. 왜냐하면 그것들은 모두 한 이야기의 일부이기 때문입니다."라고 말했습니다. 이어서 그는 "모든 것을 머릿속에 간직하는 것이 어렵기 때문에 커밋을 분해하는 더 좋은 방법이 있었으면 좋겠습니다."고 말했습니다.
이 상황이 친숙하게 들린다면, 여러분은 CI 극장의 사례를 경험한 것입니다. 여러분이 아래와 같이 생각하거나 경험했다면, CI 극장을 범한 것입니다
누군가가 CI를 실행하고 있는지 물어봤을 때 다음과 같이 답한다.
- 우리는 CI 서버를 가지고 있어.
- 우리는 Github action, jenkins, ...와 같은 CI 툴을 사용하고 있어.
우리의 실험에서 오직 10%의 참가자만이 CI 서버를 갖고 있다는 것이 CI를 실행하는 것과 같지 않다는 사실을 알고 있었습니다. 반대로, 90%의 사람들은 CI의 기본을 실천하고 있는지 여부에 관계없이 CI를 실천하고 있다고 말했습니다. 따라서 CI 서버가 있다는 것만으로 CI를 수행하고 있다고 생각하는 것은 CI 극장의 명확한 지표입니다.
수명이 긴 branch를 사용하고 정기적으로 master branch를 확인하지 않는다.
David의 이야기에서, 매일 master 브랜치를 확인하지 않는 것은 CI 극장의 신호였습니다. 이것은 팀에서 master에 대해 CI를 실행하지만 매일 커밋하지 않는 패턴이라고 볼 수 있습니다. 또는, 마스터로의 통합 없이 일반 branch에 대해 CI를 실행하는 패턴이라고 볼 수 있는데, feature 브랜치들에 대해서만 CI를 실행하면 지속적인 통합이 아닌, 지속적인 격리로서의 CI를 수행하는 것입니다.
branch들을 병합할 때마다 불안함과 피로를 느낀다.
진정한 지속적인 통합은 코드 소유권을 분산하며, 팀 내 사람들의 시각과 빌드를 실패하는 것에 대한 태도를 변화하게 만듭니다. 더 이상 "내 소중한 branch", "빌드가 실패한 내 잘못"이 아니라, "우리의 코드"와 "우리의 실패"라고 인식하게 합니다. David가 불안과 피로를 경험했다는 사실은 그가 CI의 중요한 이점인 지속적인 피드백과 집단적 소유권을 놓치고 있었다는 명백한 증거입니다.
낮은 테스트 커버리지에 대해 빌드를 수행한다.
오랜 기간 빌드가 실패하는 것을 방치한다.
정리하자면, David의 팀은 잘 알려진 CI 툴과 feature 브랜치로 나누는 행위, 코드 리뷰 같은 공통 프로세스를 보유하고 있었지만, 전체 CI 관행을 실천하고 있지 않았기 때문에 그 이점 중 많은 부분을 놓치고 있었습니다. 안타깝게도 저희 연구에 따르면 90%의 조직에서 이런 일이 일어났습니다.
CI 연극을 실천하고 있는 조직들이 놓치고 있는 주요 이점 다음과 같습니다.
빠른 피드백, 집단적 소유권, 지속적인 배포로 전환할 준비가 되어있다는 것.
어떻게 CI 극장을 피하고, 극복하고, 해결할 수 있는가?
만약 여러분이 CI 극장으로 인해 어려움을 겪고 있는 경우, 다음과 같은 세 가지 방법으로 문제를 해결하고 지속적인 변화를 이끌어낼 수 있습니다.
자주 커밋해라
기본으로 돌아가서, 자주 커밋하세요. 여러분은 최소한 매일 커밋하는 것을 목표로 삼아야 합니다. CI를 수행하지 않는 경우, 이 단계에서 시작할 수 있으며, CI를 수행하고 있더라도 개선할 여지가 항상 있습니다. 무엇이든 자주하는 것은 어려움을 낮출 수 있습니다. 어떤 일을 더 자주 할 때, 그 일은 더 작아지고, 더 쉽게 성취할 수 있게 만들어 줍니다. 지속적인 통합은 더 자주 수행할수록 더 쉬워지는 중요한 예시입니다. 저는 여러분의 저장소에 코드를 더 자주 커밋하고 적어도 매일 branch들을 master 브랜치에 통합하는 것을 조언합니다.
Trunk 기반 개발을 시도해라
Trunk 기반 개발과 branch 기반 개발에 대해 토론하는 포럼이 많지만, 저는 자세한 이야기를 여기서 하고 싶지는 않습니다. 그러나, CI 프로세스에 어려움을 겪고 있는 사람들을 대상으로 한 연구에서 Trunk 기반 개발을 수행하지 않은 팀이 가장 많았습니다. 또한, State of Devops 2016 보고서는 Trunk 기반 개발과 지속적인 통합을 실천하는 것이 지속적인 배포의 중요한 측면이며 팀의 성과 향상에 기여하는 것으로 나타났습니다.
Trunk 기반 개발에는 분명 자체적인 어려움이 있지만, 병합, 코드 검토 또는 배포 지연이라는 문제가 덜 일어납니다. 저는 더 나은 CI와 CD를 구현하려면 Trunk 기반 개발을 수행하거나, 원하는 경우 수명이 매우 짧고 총 3개 미만의 branch를 사용하는 것을 추천합니다.
자동화를 구현해라
자동화는 강력한 지속적 통합의 초석이므로 모든 것을 아직 자동화하지 않았다면, 지금이 바로 시작할 때입니다. 만약 여러분이 할 수 있는 모든 것을 자동화했다고 생각한다면, 팀의 모든 사람이 한 번 이상 수동으로 무언가를 할 때마다, 스스로에게 "왜 이것이 자동화되지 않는가?"라고 물어 보세요. 자동화는 CI를 향상시키는 데 도움이 될 뿐만 아니라 지속적인 배포를 시작하는 데에도 도움이 됩니다
결론
이제 CI 극장이 무엇인지 알게 되었습니다. 팀이 CI 극장을 실행하고 있다면 피할 수 있습니다. 여전히 CI 극장인지 아닌지 헷갈린다면, 마틴 파울러의 블로그에서 "CI 인증 테스트"를 통해 신뢰할 수 있는 CI를 수행하고 있는지 여부를 확인하는 것이 좋습니다. CI 테스트를 통과하셨다면, 좋습니다. 이제 지속적인 배포를 위한 준비가 되었는지 고려해 봅시다.
느낀 점
이 포스팅을 읽기 전에는 전형적인 Git-flow 방식을 팀에서 적용하고 있었고, CI란 단순히 CI 도구를 사용하여 빌드와 테스트를 거쳐서 코드를 통합하는 행위라고 생각했습니다. 하지만, 제가 지금까지 했던 것은 단순히 통합일 뿐이고 지속이라는 말은 들어가기가 어렵다고 생각합니다. main 브랜치에 비로소 통합되는 주기는 굉장히 길었기 때문이죠.
이와 별개로 규원 님과의 면접에서는 Git-flow 방식을 사용하면서 각 브랜치마다 CI 적용하여 안전성 있고 피드백을 받을 수 있다고 답변했습니다. 그 때는 Git-flow와 CI가 왜 상극인지 이해하지 못했지만, 이제는 그 둘이 상극이라는 것이 잘 느껴집니다.
지금부터는 팀에 진정한 CI를 느끼기 위해 어떤 방식을 도입했는지 단계 별로 소개해 드리겠습니다.
우아한테크코스 다라쓰 팀에 도입한 전략
기존에는 main과 develop 브랜치가 있고, 그 때 그 때 팀원들이 이슈에 맞는 feature 브랜치를 만들어서 develop 브랜치에 먼저 pr을 보내는 방식으로 운영되었습니다. 여기서 중간 브랜치인 develop 브랜치를 없애는 Github-flow 전략으로 변경했습니다. 중간 브랜치에 없어진 것만으로도 운영 서버에 배포한다는 부담감이 줄었고, 배포 과정 자체도 간결해졌습니다. 다만 2가지 단점이 있었습니다.
Github-flow 전략을 사용하면서 느낀 문제점
feature 브랜치의 생명 주기가 길어질 수 있다.
팀원이 맡은 하나의 이슈 자체는 하루 만에 끝나면 좋겠지만, 보통 새 기능 개발이면 며칠에서 몇 주까지도 가기도 합니다. 그렇게 되면 팀원에게 빠른 피드백을 받지 못하고, 자신 만의 branch라는 생각 때문에 집단적 코드 소유권 의식도 옅어지며, 코드 리뷰시 굉장히 많은 코드 때문에 팀원들이 효율적인 리뷰를 하기 어렵습니다.
QA를 진행하기가 애매하다.
현재 feature 브랜치에서 main 브랜치로의 pr이 merge되면 자동으로 운영 서버로 배포가 됩니다. 그러면 중간에 QA를 진행할 수 없으므로 테스트와 코드 리뷰로는 잡히지 않는 DB 같은 이슈는 찾아내기가 힘듭니다. 수동으로 pr이 merge 되기 전에 팀원과의 의견을 조율하여 개발 서버를 이용하는 방안이 있습니다만, 자동화하는 방법은 찾지 못했습니다.
두 번째 단점보다는 첫 번째 단점이 크리티컬해서 Github-flow에서 좀 더 발전된 Trunk 기반 개발로 기법을 전환했습니다. Trunk 기반 개발은 "모든 프로그래머가 최소한 하루에 한 번 main 브랜치에 커밋을 한다."라는 원칙을 가지고 있으며, main 브랜치 외에 브랜치는 굉장히 자유롭고 생명 주기가 매우 짧다는 특징이 있습니다.
다라쓰 팀 같은 경우에는 feature와 hotfix라는 명칭을 붙이기는 하지만, 커밋 단위를 작게 쪼개서 이름을 붙이고 pr을 보내서 코드 리뷰를 받고 있습니다. 아직 초기 단계지만, 한 pr의 커밋 개수가 적어서 팀원이 무슨 일을 하고 있는지 파악하기 쉽고 섬세한 코드 리뷰가 가능하다는 장점을 느끼고 있습니다.
Trunk 기반 개발의 단점과 실무 개발자 님들의 조언
그러나, 이 방식도 Github-flow의 2번째 단점이 존재했습니다. 빠른 호흡으로 배포를 가져가는 것은 좋지만, 중간에 최소한 실제 환경에서 테스트를 해야 한다고 생각합니다. 아무래도 다들 실무 경험이 없기 때문에 이러한 고민에 대해서 우아한테크코스 구구 코치님과 힐링페이퍼 이규원 CTO 님께 조언을 구했습니다.
우아한테크코스 구구 코치님께서는 다음과 같이 팀원의 사정에 맞게 조율해야겠지만, QA 과정을 거치는 편이 좋다고 하시면서 어느 단계든 한 번은 수동 배포를 거치라고 말씀해 주셨습니다.
힐링페이퍼 이규원 CTO님께서는 테스트 기법을 더욱 익혀서 테스트를 견고하게 만들고, 코드 리뷰를 통해 버그를 더 잘 잡을 수 있도록 프로세스를 개선하라고 말씀해 주셨습니다. 이어서, 힐링페이퍼는 PR을 날릴 때 커밋이 3개가 넘어가거나 수정된 코드의 양이 200줄이 넘어가면 bot이 경고를 주고 있다고 합니다.
QA도 좋지만, 최대한 버그가 적게 발생하도록 커밋 단위를 쪼개고 버그가 발생하더라도 롤백을 할 수 있도록 장치를 만들어두는 편이 바람직해 보입니다. 특히 힐링페이퍼의 bot 시스템이 흥미로워서 팀에도 도입하자고 의견을 낼 것 같습니다.
Commit-train 기반 배포 전략 도입
저는 두 분의 조언을 종합하였고, 뱅크 샐러드의 '하루에 1000번 배포하는 조직 되기' 포스팅의 commit-train 기반 배포 방식을 채택하기로 결정했습니다. 아무래도 아직 테스트 기법이나 버그 대응에 다들 미숙하기 때문에 중간 QA 과정을 빠르게라도 거치는 과정이 필요하다고 생각했고, 다음과 같이 CI / CD flow를 만들어 보았습니다.
- main에서 브랜치를 딴 후 기능을 개발한다.
- main 브랜치에 PR를 날린다.
- 젠킨스에서 테스트를 포함한 빌드가 수행된다.
- 소나큐브가 정적리포트를 분석한다.
- 코드 리뷰를 반영한다.
- PR을 main에 머지한다.
- 개발 서버로 자동 배포된다.
- 개발 서버를 기준으로 QA를 진행한다.
- 빠른 시간 안에 운영 서버로 수동 배포한다.
각자 PR이 merge가 되면 팀원에게 개발 서버를 사용하겠다고 밝히고 테스트하는 중간 QA 과정이 생긴 것이죠. 문제가 없으면 완료 사인을 내고, 하루 동안 QA에 대해 이상이 없으면 비로소 운영 서버로 수동 배포를 합니다. 바로 바로 main 브랜치에 커밋이 푸쉬될 때마다 배포가 되는 것이 아니라, 마치 기차처럼 커밋이 이어져있다가 한꺼번에 배포되는 것이죠. 이 방법도 도입 초기 단계이므로 시도해보면서 개선해 나가려고 합니다.
정리
진정한 CI의 의미를 이해하고 다들 제대로된 CI를 수행하기 위해 노력하고 있습니다. 아직도 배워야할 내용이 많지만, 올바른 방향을 찾았다고 생각이 듭니다. 여러분이 생각하시는 CI가 무엇인지, 그리고 우리 팀이 고민하고 있는 문제에 대해 조언하고 싶은 내용이 있다고 언제든지 댓글로 의견을 제시해 주시면 감사하겠습니다 🙏
'개발 이야기 > 인프라' 카테고리의 다른 글
[AWS에 Spring 초간단 배포하기] DNS 설정 (0) | 2022.04.30 |
---|---|
[AWS에 Spring 초간단 배포하기] SSH 접속 및 Spring 배포 (0) | 2022.04.30 |
[AWS에 Spring 초간단 배포하기] RDS 설정 (0) | 2022.04.30 |
[AWS에 Spring 초간단 배포하기] EC2 설정 (0) | 2022.04.30 |
[Docker] bind: address already in use (0) | 2021.10.08 |
댓글