NLP Blog

23. 지속적 통합

|

23. 지속적 통합

  • 정의 : 팀원들이 작업 결과를 자주 통합하는 소프트웨어 개발 방식이다. 통합할 때마다 자동 빌드 (테스트 포함)하여 통합 오류를 빠르게 찾아낸다
  • 현대적인 분산 애플리케이션에서 자주 통합한다라는 말은 무슨 의미일까요?
    • 요즘 시스템들을 리포지터리 안의 코드 말고도 동적으로 변하는 요소가 많음
    • 마이크로서비스 시스템에서는 문제의 원인이 코드베이스가 아니라 느슨하게 결합된 다른 마이크로서비스와의 네트워크 실패 때문인 경우가 더 많음
    • 전통적인 지속적 빌드에서 확장하면 업스트림 마이크로서비스의 변경들도 테스트 할 수 있음
  • 현대적 어플리케이션에서는 굉장히 많은 주변 요소를 모두 의존석으로 간주하고 이 변경까지 지속적으로 통합 해야함
    • 데이터 -> 머신러닝 모델 갱신
    • 운영체제, 런타임, 클라우드 호스팅 서비스, 디바이스
  • 재정의
    • 빠르게 진화하는 복잡한 생태계 전체를 지속적으로 조립하고 테스트하는 개발 방식
  • CI와 테스트는 강하게 엮여 있음
    • 코드(와 다른요소) 가 변경되어 지속적으로 통합되는 개발/릴리스 워크플로에서 무슨 테스트를 언제 실행하야 하는가?
    • 워크플로의 각 테스트 지점에서 (적절한 충실성을 갖춘) 테스트 대상 시스템(SUT)을 (합리적인 비용으로) 어떻게 구성해야 하는가?
  • 프리서브밋과 포스트 서브밋, 스테이징 배포 전에는 어떤 테스트를 각각 수행해야 할까?

23.1 지속적 통합이란?

23.1.1. 빠른 피드백 루프

  • 코드 변경 과정에서 문제를 발경해낼 수 있는 위치들
    • 편집/컴파일/디버스 -> 프리서브밋 -> 포스트서브밋 -> 릴리스 후보(RC) -> RC 승격 (스테이징 등 임시 환경) -> 최종 RC승격 (프로덕션)
  • 일반적으로 문제가 오른쪽 끝 가까이까지 살아남을수록 비용이 커지는 이유는
    • 문제의 코드에 익숙하지 않은 엔지니어가 분류해야 함
    • 변경 작성자가 무엇을 왜 변경했는지 기억해내고 조사하는 노력이 더 많이 듬
    • 바로 옆 동료부터 최종 사용자까지, 다른 이들에게 부정적인 영향을 줌
  • CI는 우리가 빠른 피드백 루프를 이용하도록 유도하여 버그 비용을 최소로 줄여줌
  • 피드백이 빠른 순서로 보면
    • 로컬 개발시의 편집-컴파일-디버그 루프
    • 프리서브밋 시 변경 작성자에게 주어지는 자동 테스트 결과
    • 두 프로젝트를 변경한 후 통합 시 오류. 양쪽 변경들을 서브밋한 다음 함께 테스트할 때 발견 (예: 포스트서브밋)
    • 내 프로젝트와 섭스트림 마이크로서비스 의존성 사이의 호환성 충돌. 업스트림 서비스의 최신 변경이 스테이징 환경에 배포될 때 QA 엔지니어가 발견
    • 기능을 먼저 이용해본 사내 이용자의 버그 리포트
    • 외부 고객 혹은 언론 매체의 버그 리포트
  • 카나리 배포를 활용하면 프로덕션에서 일어나는 문제가 확실하게 줄어듬
    • 버전 왜곡이 일어날 수 있음
  • 실험과 기능 플래그도 매우 강력한 피드백 루프
    • 변경을 컴포넌트 단위로 격리한 후 프로덕션 환경에서 동적으로 켜고 끌 수 있게 하여 배포 위험을 줄여주는 기법

볼 수 있고 조치할 수 있는 피드백

  • CI가 제공하는 피드백을 많은 사람이 볼 수 있어야 함
  • 테스트 리포트 시스템은 빌드와 테스트의 실패 이력도 자세히 알려줌
  • CI 테스트가 제공하는 피드백은 모두 조치가 가능해야 함

23.1.2 자동화

  • 빌드와 릴리스 프로세스를 자동화, 각각을 지속적 빌드와 지속적 배포하고 한다.
  • 지속적 테스트는 두 단계 모두에 자동으로 붙는다.

지속적 빌드

  • 가장 최근의 코드 변경을 헤드(트렁크)에 통합한 다음 자동으로 빌드와 테스트를 수행함
  • 테스트로 빌드의 한 과정으로 보기 때문에 컴파일을 통과하더라도 테스트에 실패하면 ‘빌드 실패’로 간주함
  • true head는 최신 병경이 커밋된 버전
  • green head는 CB가 검증한 최신 변경

지속적 배포

  • 릴리스 자동화 - 헤드로부터 지속해서 최신 코드와 설정 정보를 가져와서 릴리스 후보를 만들어내는 작업
  • 릴리스 후보 (RC) : 자동화된 프로세스가 만든, 서로 밀접하게 관련된 요소들로 구성된 배포 가능한 단위. 시족적 빌드를 통과한 코드, 설정 정보, 기타 의존성들을 조합해 만든다.
  • 정적인 설정 정보들은 모두 릴리스 후보에 묶어 승격시켜서 해당 코드와 함께 테스트 되도록 해야 함
  • 지속적 배포 : 지속해서 릴리스 후보를 조립한 다음 다양한 환경에 차례로 승격시켜 테스트하는 활동. 프로덕션까지 승격시키는 경우도 있고 그렇지 않은 경우도 있다.
  • RC가 운영환경들에서 단계별로 승격될 때 이상적으로는 아티팩트들 (ex: 바이너리, 컨테이너)을 다시 컴파일하거나 빌드하지 않아야함
  • 로컬에서 개발할 때부터 도커 같은 컨테이너를 이용하면 좋음 (구글은 Borg를 이용)

22.1.3 지속적 테스트

  • 그림 23-2 참조
  • 무슨 테스트를 언제 실행하지를 정하는게 CI의 주된 목표
  • 위 그림에서 오른쪽으로 이동할수록 점진적으로 범위가 더 넓은 자동화 테스트를 수행해야 함

프리서브밋만으로 부족한 이유

  • 원점 회귀가 좋다면서 외 모든 테스트를 프리서브밋에 하지 않는가?
    • 비용 때문
    • 프리서브밋 테스트가 수행되는 동안에도 리포지터리는 계속 수정 될 수 있어서, 장시간 걸린 테스트가 쓸모없어질 수 있음

프리서브밋 vs 포스트서브밋

  • 프리서브밋 테스트
    • 빠르고 안정적인 테스트
    • 커버리지를 다소 포기, 포스트때 다 잡아내자
    • 해당 변경이 이루어지는 프로젝트로 한정
    • 밀폐 테스트 권장
    • 불안정안 테스트를 해보되, 실패하면 비활성화하는 것도 방법
  • 포스트서브밋 테스트
    • 더 오래걸리고 약간 불안정한 테스트도 포함 가능

릴리스 후보 테스트

  • RC 전체를 광범위하게 검증하는 더 큰 테스트들을 수행
  • RC는 일련의 테스트 환경에서 다음 단계 환경으로 승격될 때마다 매번 테스트 됨
  • 테스트 환경
    • 샌드박스
    • 임시 환경
    • 공유 테스트 환경…
  • 수동 QA 테스트까지 진행함
  • CB의 포스트서브밋 때 똑같은 테스트를 이미 수행했더라도 RC는 매 단계에서 철저하게 다시 테스트함 이유는
    • 온전성 검사 : 코드를 잘라와 RC용으로 다시 컴파일할 때 예상치 못한 일이 발생하지 않았는지를 재차 확인
    • 검사 용이성 : RC의 테스트 결과를 확인하려는 엔지니어가 지속적 빌드 로그를 파헤쳐보지 않고도 해당 RC와 관련된 결과를 바로 살펴볼 수 있음
    • 체리픽 허용 : RC에 체리픽 수정을 반영한다면 지속적 빌드가 테스트한 코드와 달라짐
    • 비상 배포 : 비상 상황 발생 시, 지속적 배포는 참 헤드의 코드를 잘라와서 배포를 해도 괜찮겠다는 마음의 안정을 가져다주는 데 꼭 필요한 최소한의 테스트만 실행할 수 있음

프로덕션 테스트

  • 동일한 테스트 스위트를 프로덕션 환경에서도 그대로 수행함 (prober)
  • 다음의 두 가지 검증을 위함
    • 프로덕션이 테스트 대로 올바르게 동작하는가?
    • 프로덕션을 검증하기에 적합한 테스트인가?
  • CI는 경보와 같다
    • 프로덕션 서비스 가동 시간 목표를 100%로 잡으면 말도안되는 비용이 들듯이, CI 테스트를 모두 녹색으로 유지하겠다도 말이 안됨

23.1.4 CI의 과제

  • 프리서브밋 최적화 : 앞서 언급한 잠재적인 문제를 고려하여 프리서브밋 시 어떤 테스트를 어떻게 수행해야 하나?
  • 범인 찾기 (culprit finding)와 실패 격리 (faliure isolation) : 문제를 일으킨 코드가 어느 것이고, 어느 시스템에서 문제가 발생했을까요?
  • 자원 제약 : 테스트를 실행하기 위해 필요한 자원을 최적화

23.1.5 밀폐 테스트

  • 서비스 중인 백엔드와의 통신은 안정적이지 않을 수 있어서 구글은 범위가 넓은 테스트에는 밀폐된 백엔드를 주로 이용함
  • 밀폐 테스트 (hernetic test) : 모든 것을 갖춘 테스트 환경에서 수행하는 테스트
  • 테스트에 영향을 주는 데이터가 적어도 외부 의존성 때문에 바뀔 일은 없음

23.2 지속적 통합 @ 구글

23.2.1 TAP: 구글의 글로벌 지속적 빌드 시스템

  • TAP : Test Automation Platform
  • 매일같이 5만 개 이상의 변경을 처리하고 40억 개 이상의 테스트 케이스를 싱행함

프리서브밋 최적화

  • 팀별로 빠르게 실행할 수 있는 테스트들을 따로 추려달라고 요청함 -> 보통 유닛테스트
  • 프리서브밋을 통과한 병경은 95% 이사의 매우 높은 확률로 나머지 테스트도 통과
  • 잠재적으로 영향받을 수 있는 모든 테스트를 비동기적으로 수행함ㅁ
  • 팀별로 빌드 경찰 (Build Cop)을 둬서 담당 프로젝트의 테스트를 모두 통과 시키는 일을 책임 짐

범인 찾기

  • 비결정적인 테스트가 만연하고, 테스트 인프라 자체도 간헐적으로 문제가 생겨서 진짜 실패인지를 확신하기가 쉽지않음
  • 실패지 TAP은 자동으로 변경 각각을 독립적으로 다시 테스트 함
  • 이진 검색으로 범인 찾기

실패 관리

  • 롤백
  • 롤백이 없다면 테스트 실패를 빠르게 조치할 수 없어서 시스템을 믿지 못하게 됨

자원 제약

  • 대부분의 테스트는 Forge라는 분산 빌드&테스트 시스템으로 실행 함
  • 어떤 테스트를 언제 실행해야하는지 방법을 고안해내야 함
    • 다운스트림 의존성 그래프 분석
  • 빨리 끝나는 테스트를 먼저 실행함
    • 사람들이 빠른 테스트를 할 수있는 코드 변경을 선호하게 바뀜

23.2.2 사례연구 : 구글 테이크 아웃

23.2.3 하지만 나는 CI를 감당할 여력이 없어

  • 멋지지만 이런 시스템을 구축할 시간도 자금도 없다면?
  • 각자의 조직에서 서비스를 운영하며 생기는 문제 처리에 지불하고 있는 비용을 생각해보자
    • 진짜 돈 뿐만이 아니라, 안좋은 인식 및 사기 문제
    • 비상 대응이 잦아지면 빡친다
  • 원점 회귀하자

23.4 핵심 정리

  • CI 시스템은 무슨 테스트를 언제 실행해야 할지를 결정함
  • 코드베이스가 오래되고 규모가 커질수록 CI 시스템이 더욱 필요해짐
  • 빠르고 더 안정적인 테스트는 프리서브밋 단계에서, 느리고 덜 결정적인 테스트는 포스트 서브밋 단계에서 실행하도록 최적화 해야 함
  • 볼 수 있고 조치할 수 있는 피드백이 CI 시스템을 더효율적으로 만들어줌

Comments