Enjoy My Posts

CI / CD

Posted on By Geunwon Lim

CI / CD란?

CI(Continuous Integration)

지속적 통합은 자동화된 빌드 및 테스트가 수행된 후, 개발자가 코드 변경 사항을 중앙 리포지토리에 정기적으로 병합하는 소프트웨어 개발 방식입니다. 지속적 통합의 핵심 목표는 버그를 신속하게 찾아 해결하고, 소프트웨어 품질을 개선하고, 새로운 소프트웨어 업데이트를 검증 및 릴리스하는 데 걸리는 시간을 단축하는 것입니다.

CD(Continuous Delivery)

지속적 전달은 프로덕션에 릴리스하기 위한 코드 변경이 자동으로 빌드, 테스트 및 준비되는 소프트웨어 개발 방식입니다. 빌드 단계 이후의 모든 코드 변경 사항을 테스트 환경 또는 프로덕션 환경에 배포함으로써 지속적 통합을 확장합니다. 즉, 머지를 너머 배포한다는 점에서 CI의 연장선이라고 생각하셔도 될 것 같습니다. 지속적 전달이 적절하게 구현되면, 개발자는 언제나 즉시 배포할 수 있고 표준화된 테스트 프로세스를 통과한 빌드 아티팩트를 보유하게 됩니다.

CI / CD의 전반적인 과정

CI(Continuous Integration)

  1. 개발자는 소스 코드 저장소에 코드를 커밋한다.
  2. 빌드 도구는 소스 제어 저장소의 변경 여부를 모니터링하고 변경이 탐지되면 빌드를 시작한다.
  3. 빌드 동안 애플리케이션에 대한 단위 테스트와 통합 테스트가 실행되고 모두 통과하면 배포 가능한 소프트웨어 산출물이 생성된다(JAR, WAR 등).
  4. 위의 과정을 거치고 나면 JAR, WAR 등을 서버에 배포할 수 있다.

CD(Continuous Delivery)

  1. 개발자는 소스 코드 저장소에 코드를 커밋한다.
  2. 빌드 도구는 소스 제어 저장소의 변경 여부를 모니터링하고 변경이 탐지되면 빌드를 시작한다.
  3. 빌드 및 배포 프로세스는 코드를 컴파일하고 단위 테스트와 통합 테스트를 실행한 후 서비스를 실행 가능한 산출물로 컴파일한다(JAR, WAR 등).
  4. 실행 가능한 JAR 파일을 빌드한 후 이미지를 굽는다. 이미지 굽기 단계에서 가상 머신 이미지나 컨테이너(도커)를 생성하고 서비스를 설치한다. 가상 머신 이미지를 시작하면 서비스가 시작하고 요청을 받을 준비가 된다. 컴파일된 JAR나 WAR를 애플리케이션과 별개로 관리하며, 애플리케이션 서버에 배포하는 전통적 CI 빌드 프로세스와 달리 CI / CD 프로세스를 사용하면 애플리케이션용 런타임 엔진, 머신 이미지 전부를 하나의 상호 의존 단위로 배포하고 그 소프트웨어를 작성한 개발 팀이 관리한다.
  5. 새 환경에 배포하기 전 머신 이미지를 시작해 실행 중인 이미지에 대해 일련의 플랫폼 테스트를 수행하고 정상적으로 동작하는지 확인한다. 플랫폼 테스트를 통과하면 이미지는 새로운 환경으로 승격되어 사용할 수 있다.

CD에서는 애플리케이션을 완전한 머신 이미지로 배포합니다. 서버가 생성된 후 설치된 소프트웨어(운영 체제 포함)은 절대 변경하지 않습니다. 동일한 머신 이미지를 승격하고 항상 사용함으로써 한 환경에서 다른 환경으로 승격될 때 서버의 불변성을 보장합니다.

단위테스트 vs 통합 테스트 vs 플랫폼 테스트

  • 단위 테스트: 코드 컴파일 직후, 환경에 배포하기 전에 수행된다. 각 단위 테스트는 크기가 작고 좁은 범위로 제한되며 완전히 격리해서 실행하도록 설계되었다. 단위테스트는 제 3자 인프라스트럭처(데이터베이스 등)에 의존성이 없어야 한다. 일반적으로 단위 테스트의 범위는 단위 메서드나 함수가 해당된다.
  • 통합 테스트: 통합 테스트는 서비스 코드를 패키징한 직후에 수행된다. 통합 테스트 동안, 데이터를 보관하는데 인메모리 데이터베이스를 사용하거나 외부 서비스 호출에 목을 사용할 수 있다. 통합 테스트는 전체 워크플로와 코드 경로를 테스트하며 외부 의존성은 목이나 스텁을 사용하므로 원격 서비스에 대한 호출도 빌드 서버를 벗어나지 않는다.
  • 플랫폼 테스트: 플랫폼 테스트는 서비스가 해당 환경에 배포되기 직전에 수행된다. 이 테스트는 일반적으로 전체 비즈니스 플로를 테스트하고 운영 환경 시스템에서 정상적으로 호출되는 외부 의존성도 모두 호출한다. 플랫폼 테스트는 특정 환경에서 실제 수행되고 목 서비스는 사용하지 않는다. 플랫폼 테스트는 보통 통합 테스트 동안 스텁으로 대신한 외부 서비스에서 검출되지 않은 통합 문제를 확인하기 위해 수행된다.

불변 서버

서버 이미지가 생성되면 서버 구성과 애플리케이션을 프로비저닝 프로세스 이후에 절대 변경할 수 없다. 이는 개발자나 시스템 관리자가 나중에 장애의 원인이 될 수 있는 ‘작은 변경’에 의해 ‘구성 편차’를 겪지 않도록 해 준다. 변경이 필요하면 서버 프로비저닝 스크립트를 변경하고 새 빌드를 시작해야 한다.

deploy-pipeline

  • 깃허브: 깃허브를 활용하면 자체 소스 제어 서버를 관리, 유지하지 않아도 됩니다. 또한 깃허브는 빌드 프로세스에 통합하기 위해 다양한 웹훅과 강력한 REST 기반 api를 제공합니다.
  • Travis CI: 애플리케이션을 빌드 및 배포하고, 배포될 도커 이미지를 프로비저닝하는 데 사용되는 지속적 통합 엔진입니다. Travis CI는 깃허브 및 도커와 강력한 통합 기능을 가진 클라우드와 파일에 기반을 둔 CI 엔진입니다. Travis CI가 젠킨스처럼 CI의 모든 기능을 갖추지는 않았지만, 충분한 기능을 제공하고 간편(프로젝트 루트 디렉터리에 있는 구성파일(.travis.yml) 하나로 구동)하다는 장점이 있습니다. Travis CI가 가진 단순함과 완고함 덕분에 간단한 빌드 파이프라인을 쉽게 시작할 수 있습니다.
  • 도커: 도커는 컨테이너 플롯폼입니다. 도커는 여러 클라우드 공급자에게 이식 가능합니다. 최소한의 작업으로 동일한 도커 컨테이너를 AWS, Azure, 클라우드 파운드리 등에 배포할 수 있습니다. 또한 도커는 가볍습니다. 로컬 데스크톱에 다수의 가상 머신 배포는 이미지 크기와 속도 때문에 쉬운 일이 아닙니다. 도커를 활용하면 수월하게 수행할 수 있습니다.
  • 도커 허브: 도커 허브는 공개적인 이미지 저장소입니다.

깃허브와 Travis CI로 빌드 및 배포

기존에 다음과 같이 애플리케이션을 컴파일하고 시작해왔습니다.

  1. 로컬 머신에서 명령줄 창을 연다.
  2. 메이븐/그래들 등을 활용해 빌드한다.

Travis CI에서는 이 과정을 어떻게 반복할까요? 모든 과정은 .travis.yml 파일에서 시작됩니다. .travis.yml 파일은 Travis CI가 빌드를 실행할 때 수행할 액션을 기술한 YAML 기반의 파일입니다. Travis CI는 모니터링하는 깃허브 저장소에서 커밋이 발생하면 .travis.yml 파일을 찾아 빌드 프로세스를 시작합니다. 깃허브 저장소에 커밋이 발생할 때 .travis.yml 파일이 수행하는 단계는 다음과 같습니다.

  1. 개발자가 소스 코드 변경을 커밋한다.
  2. Travis CI는 깃허브에 커밋이 발생했다는 알림을 받는다. 이 알림 구성은 Travis에 등록할 때 깃허브 계정 알림을 제공하면 바로 수행된다. Travis CI는 빌드를 실행하는 데 사용할 가상 머신을 시작한다. 그다음 깃허브에서 소스 코드를 체크 아웃하고 .travis.yml 파일을 사용해 전체 빌드 및 배포 프로세스를 시작한다.
  3. Travis CI는 해당 빌드의 기본 구성을 설정하고 의존성을 설치한다. 빌드에 사용될 언어(자바)와 소프트웨어 설치, sudo 권한의 필요 여부, 빌드에 필요한 보안 환경 변수의 설정, 빌드의 성공/실패 알림 방법 등이 기본 구성에 포함된다.
  4. 실제 빌드가 실행되기 전에 Travis CI는 빌드 프로세스에 필요한 외부 라이브러리나 명령줄 도구를 설치할 수 있다. 두 가지 도구인 travis와 아마존 ecs-cli 명령줄 도구를 사용한다.
  5. 빌드 프로세스는 항상 소스 저장소에 있는 코드에 빌드 태그를 다는 것부터 시작한다. 따라서 향후에 언제든 빌드 태그를 기준으로 소스 코드의 전체 버전을 가져올 수 있다.
  6. 그다음 빌드 프로세스는 서비스에 대한 빌드 스크립트(메이븐 등)를 실행한다. 메이븐 스크립트는 애플리케이션을 컴파일하고, 단위 테스트와 통합 테스트를 수행한 후 빌드 프로세스를 기반으로 도커 이미지를 만든다.
  7. 도커 이미지 생성이 완료되면 빌드 프로세스는 소스 코드 저장소에 붙여진 동일한 태그 이름을 사용해 이미지를 도커 허브에 올린다.
  8. 그다음 빌드 프로세스는 프로젝트의 docker-compose 파일과 아마존의 ecs-cli를 사용해 전체 서비스를 아마존의 도커 서비스인 아마존 ECS에 배포한다.
  9. 서비스 배포가 완료되면 빌드 프로세스는 개발 환경에 대해 플랫폼 테스트를 수행하기 위해 완전히 분리된 Travis CI 프로젝트를 시작한다.

정리

  1. 제대로 작동하는 빌드 및 배포 파이프라인을 통해 새로운 기능과 버그 수정을 몇 분 안에 배포할 수 있습니다.

  2. 빌드 및 배포 파이프라인은 서비스를 제공하는 데 사람의 직접적인 개입 없이 자동화되어야 합니다. 프로세스의 수동 부분은 변동 및 실패 가능성을 나타냅니다.

  3. 빌드 및 배포 파이프라인의 자동화에는 많은 스크립팅과 올바른 구성이 필요합니다. 자동화 구축을 위해 필요한 일의 양을 과소평가해서는 안 됩니다.

  4. 빌드 및 배포 파이프라인은 불변 가상 머신 또는 컨테이너 이미지를 제공해야 합니다. 서버 이미지가 생성된 후에는 절대 변경되면 안 됩니다.

  5. 환경별 서버 구성은 서버가 설정될 때 매개변수로 전달되어야 합니다.