[17주차 Day3] Docker를 활용한 컨테이너화 실습

📚 Part 5 인수 테스트 자동화
📁 이미지 레지스트리 구성
📌 인수 테스트 (UAT; User Acceptance Test)
- 요구사항 (requirement) 대로 기능이 구현되었는지를 확인하는 과정
- 전체 시스템을 사용자 관점에서 시험하는 블랙박스 테스트를 포함
- 이것을 자동화하는 것은 쉽지 않은 일이지만 CI/CD의 구축을 위해 반드시 필요
- 전통적으로는 QA 담당자나 사용자의 수작업에 의존해 왔음
- 사용자 인수 테스트를 자동화하는 것이 어려운 요인
- 사용자 참여: 기술적 측면과 비기술적 측면에 대한 요구사항의 최종 확인은 실사용자여야 함
- 의존성 통합: 테스트할 애플리케이션은 모든 의존성을 포함하여 실행되어야 함
- 스테이징 환경: 프로덕션 환경과 동일한 스테이징 (테스트) 환경에서 이루어져야 함
- 애플리케이션 동일성: 한 번만 빌드하여 프로덕션에서와 동일한 바이너리를 이용해야 함
- 틸리스 준비: 인수 테스트를 통과한 애플리케이션은 즉시 릴리스 준비가 되어야 함
📌 학습 목표
- 도커 레지스트리의 구성
- 빌드한 이미지를 저장 및 관리할 수 있는 저장소를 셋업해 봄
- 여러 가지의 다른 공개/상용 서비스를 이용할 수도 있으나, 이 과정을 통해 CI/CD의 과정과 k8s 클러스터의 운용에 대하여 좀 더 깊이 이해하는 것이 목표
- 애플리케이션 패키지 빌드 및 이미지 푸시
- 소스 수준에서 테스트 (UT)가 완료된 애플리케이션을 독립 및 통일된 환경에서 실행될 수 있도록 컨테이너화
- 응용 소프트웨어를 (및 전체 의존성을) 컨테이너에 포함하여 이미지로 만들고 레지스트리에 저장
- UAT 프레임워크 적용
- 보다 체계적이고 완성도가 높은 인수 테스트를 위한 프레임워크 활용
- 응용 소프트웨어의 개발 환경에 따라 크게 달라지는 부분이지만 예제를 중심으로 시나리오 이해
📌 아티팩트 리포지토리 (Artifact Repository)
- 버전 관리, 접근 제어 등의 기능을 가지는 소프트웨어 개발 산출물(예: 패키지)을 발행(푸시)하거나 인출 (풀) 할 수 있는 저장소 및 관리 기법이 필요
- 파이프라인의 모든 단계에서 동일한 바이너리가 사용되는 것을 보장함으로써 지속적 인도 프로세스에서 매우 중요한 역할을 함
📌 도커 레지스트리 (Docker Registry)
- 컨테이너화된 소프트웨어의 산출물인 도커 이미지를 관리할 수 있는 리포지토리-
- 클라우드 방식 레지스트리
- Docker Hub (및 이와 유사한 기능을 제공하는 레지스트리 서비스들)
- 상용 클라우드에서 제공하는 서비스 (예: AWS ECR, GCP Artifact Registry, Azure Container Registry, ...)
- 자체 호스팅 방식 레지스트리
- 사내 네트워크가 아닌 외부에 소프트웨어를 보관하는 것을 금지하는 정책을 갖고 있는 경우에는 유일한 해결 방법
- 직접 관리해야 하는 부담이 있고 접근 제어 및 인증서 설정 등의 번거로운 작업이 수반됨
📌 데이터 볼륨과 SSL 인증서
- 호스트의 디렉토리를 레지스트리에 볼륨으로 공유
- 레지스트리에 저장된 데이터는 컨테이너 및 포드 등이 사멸하는 경우에도 유지할 수 있도록
- 실제 개발 환경에서는 어딘가 저장 장소를 마련해 두고 주기적으로 백업하는 방법을 택할 것이겠으나, 우리 실습에서는 호스트의 파일시스템 내 특정 위치에 데이터가 저장되도록 설정
- PV를 정의하고 PVC를 설정하여 레지스트리 서버 컨테이너에서 이용하도록 볼륨 마운트
- 자가 서명된 (self-signed) 인증서 발급하여 레지스트리 서버에 설치
- 실제 운용 환경에서는 CA (certificate authority) 로부터 발급받은 인증서를 설치하고 주기적으로 갱신하여 공인할 수 있는 인증을 이용할 것
- 우리 실습에서는 간이로 자가 서명 인증서를 발급하고 이것을 레지스트리 서버에 설치
- 다음 실습에서는 이 레지스트리를 이용하려는 쪽의 컨테이너에도 인증 설정
📌 접근 제어
- 보통은 login id와 passwd의 쌍으로 사용자 인증
- 로그인하는 사용자에 따라 서로 다른 권한을 적용
📁 간단한 인수 테스트
📌 Jenkins 파이프라인에서의 인수 테스트
- 개발자가 변경한 코드를 GitHub 에 푸시
- Jenkins 가 변경을 감지하고 코드를 인출해 빌드를 시작, 코드를 점검 (단위테스트 수행)
- Jenkins 가 빌드를 완료하여 도커 이미지를 생성
- Jenkins 가 생성한 이미지를 레지스트리로 푸시
- Jenkins 가 스테이징 환경을 구성하고 도커 컨테이너 실행
- 스테이징 환경의 도커 호스트가 이미지를 가져다가 (풀) 컨테이너를 실행
- Jenkins 가 스테이징 환경에서 실행 중인 애플리케이션을 대상으로 인수 테스트를 실행
📁 인수 테스트 프레임워크 적용
📌 사용자-대면 테스트
- REST API 의 경우에는 curl 에 의한 테스트로도 (기술적으로는) 어느 정도 블랙박스 테스트가 가능
- 그러나, 일반적으로는 이런 테스트에 의존할 수 없음
- 읽고 이해하기가 어려움 (특히 비기술 영역 사용자에게)
- 유지보수에 불리함
- 사용자와 함께 작성할 수 있고 사용자가 이해할 수 있는 테스트 작성 방법이 필요
- 특정한 목적 (사업적 가치)을 반영하는 인수 테스트
📌 BDD (Behavior-Driven Development)
- 사용자 (또는 프로덕트 오너) 가 인수 기준을 제시
- 위 인수 기준으로부터 개발자는 픽스처 (fixture) 또는 스텝 정의 (step definitions) 라고
부르는 사용자 친화적인 DSL (domain-specific language) 와 프로그래밍 언어를 통합해서
테스트를 작성
- 이를 도와주는 도구들
- Cucumber (이 실습에서 이용)
- Selenium (사용자 인터페이스를 포함하는 애플리케이션에 널리 이용, 여기에서는 다루지 않음)
📌 Cucumber를 이용한 인수 테스트 생성
- 인수 기준 생성
- 스텝 정의 작성
- 기능 사양을 실행할 수 있는 Java 바인딩을 생성
- 자동 인수 테스트 실행
- Gradle 설정에 라이브러리 의존성 명세 추가
- Gradle 태스크와 Junit 테스트 러너를 추가
- (중요) 여기에서는 Cucumber를 이용한 테스트가 아니라 인수 테스트 자동화에 관심과 중점
📌 파이프라인 설정 복원
- 앞 페이지의 내용을 참고로 Jenkinsfile 을 완성
- agent 에 총 세 개의 서로 다른 컨테이너를 포함하는 포드의 스펙 작성
- environment 에 커스텀 레지스트리의 접근 URI 와 artifact (도커 이미지)에 붙일 태그를 지정
- 총 9개의 스테이지 ("Compile" 부터 "Acceptance Test" 까지)를 포함
- 후처리 (post) 작업으로 "docker stop" 을 통해 스테이징 환경을 정리
- 원래 있었던 Jenkins 아이템 ("간단한 인수 테스트" 실습의 "작업용 임시 파이프라인 만들기" 참고)의 "Poll SCM" 설정을 복원
- 매 5분 경과 후 GitHub repo에 변경사항 있었는지를 검사, 있는 경우 빌드 작업을 트리거
- 위 Jenkinsfile 과 그 사이 신규 작성 또는 변경한 파일들을 GitHub repo에 push
📌 TDD (Test-Driven Development)
- (중요) 인수 테스트는 기술보다 사람이 중심
- 지속적 인도 프로세스의 다른 단계들도 마찬가지
- 소프트웨어 개발 수명 주기 중 어느 시점에 테스트 작성?
- 코딩 전에? 코딩 후에?
- TDD는 특히 인수 테스트에 적합
- 인수 기준 사양을 먼저 작성
- 인수 테스트 통과를 기능 구현 완료로 간주
- 이슈 추적 도구의 요청 티켓에 기능 사양을 첨부하는 방법도