Auto CI 설정
코드를 푸시할 때마다 수동으로 빌드하고 배포하는 것은 번거롭고 실수가 발생하기 쉽습니다. Auto CI를 설정하면 GitLab 또는 GitHub에 코드를 푸시하는 순간 자동으로 빌드와 배포가 시작됩니다. 한 번 설정해 두면 개발에만 집중할 수 있습니다.
왜 이 가이드를 읽어야 하는가
이 가이드를 한 번 따라가면 개발자가 Git에 코드를 푸시할 때마다 KIOPS가 자동으로 이미지를 빌드하고 보안 검사까지 수행하도록 만들 수 있습니다. 비개발 운영팀이 Webhook 등록 절차를 처음 접해도, "URL 복사 → Git 저장소에 붙여넣기" 단계만 따라가면 자동화 파이프라인을 켤 수 있도록 단계별로 설명합니다. 시간 절약, 휴먼 에러 감소, 매번 동일한 방식의 빌드/배포, 푸시 직후의 빠른 피드백이 주요 효과입니다.

시작하기 전에: 알아둘 용어
본문에 들어가기 전에 자주 등장하는 용어를 한 줄씩 정리합니다. 본문에서 처음 등장할 때 "(용어 사전 참고)" 안내를 함께 표시합니다.
| 용어 | 정의 + 일상 비유 |
|---|---|
| Webhook | 특정 이벤트가 발생할 때 지정된 URL로 알림을 보내는 방식. 택배가 도착하면 자동으로 휴대폰에 알림이 가는 것과 같습니다. |
| Push 이벤트 | 개발자가 Git 브랜치에 커밋을 올린 시점. 이메일 "받은편지함"에 새 편지가 도착한 것과 같습니다. |
| MR (Merge Request) | GitLab에서 다른 브랜치로 코드를 병합 요청하는 절차(GitHub의 Pull Request와 동일). 회의 안건을 상정하고 검토를 받는 절차와 같습니다. |
| Auto CI | Git 이벤트에 반응해 빌드를 자동 실행하는 KIOPS 기능. 자동 세차장과 비슷합니다. 차(코드)를 넣기만 하면 세차(빌드)가 끝납니다. |
| 빌드 프로필 | 빌드 Pod에 할당될 CPU/메모리 크기를 결정하는 사전 정의 옵션(small ~ 4x-large). 택배 박스 크기와 같습니다. 짐(프로젝트)이 크면 큰 박스가 필요합니다. |
| Registry Secret | K8s가 Private Registry(Harbor 등)에 로그인할 때 쓰는 인증 정보. 사물함 열쇠처럼 K8s가 이 열쇠로 이미지를 꺼내옵니다. |
개요
Auto CI는 GitLab/GitHub의 Webhook 이벤트를 감지하여 자동으로 빌드 파이프라인을 실행합니다. 아래는 전체 흐름입니다:
graph LR
A[Git Push] --> B[Webhook 트리거]
B --> C[KIOPS 빌드 시작]
C --> D[BuildKit 빌드]
D --> E[Harbor 푸시]
E --> F[자동 배포]
지원 이벤트
GitLab Webhook 핸들러는 다음 세 가지 이벤트에 대해서만 빌드를 자동으로 시작합니다. Auto CI를 활성화하면 아래 이벤트에 모두 반응합니다(이벤트별 개별 토글 UI는 제공되지 않음). Auto CI로 트리거되는 모든 빌드는 K8s 클러스터의 고정된 kiwi-builds 네임스페이스에서 실행됩니다.
- Push: 브랜치에 커밋을 푸시할 때 빌드됩니다. 개발 브랜치 자동 빌드, 지속적 통합에 사용합니다.
- Merge Request: MR 생성/업데이트 시 빌드됩니다. 코드 리뷰 전 빌드 검증에 사용합니다.
- Tag: 태그 생성 시 빌드됩니다. 릴리스 버전 빌드에 사용합니다.
사전 요구사항
Auto CI를 설정하기 전에 다음 조건이 충족되어야 합니다:
아래 항목 중 하나라도 누락되면 Auto CI 버튼이 비활성화됩니다. 먼저 모든 항목을 완료하세요.
- 서비스 등록 (필수): [서비스 관리] 페이지에서 서비스가 등록되어 있어야 합니다.
- GitLab/GitHub 토큰 설정 (필수): 관리자가 기관 설정에 등록한 토큰이 자동으로 사용됩니다. 서비스 등록 시 별도 토큰 입력은 필요하지 않습니다.
- Registry 설정 (필수): 서비스 설정에서 Harbor/DockerHub 레지스트리 정보가 입력되어 있어야 합니다.
- Dockerfile 존재 (권장): 없다면 Build Wizard로 자동 생성하세요
- K8s 클러스터 연결 (자동 배포 시 필수): **[런타임 환경]**에서 클러스터가 등록되어 있어야 합니다.
Auto CI 버튼이 회색으로 비활성화되어 있다면 서비스 상세 페이지에서 누락된 설정을 확인하세요. 대부분 Git 토큰이나 Registry 설정이 누락된 경우입니다.
Step 1: Auto CI 설정 모달 열기
1.1 서비스 목록에서 접근하기
- [서비스 관리] 페이지(
/services)로 이동합니다. - Auto CI를 설정할 서비스를 찾습니다.
- 서비스 카드 우측 상단의 번개 아이콘(Auto CI) 버튼을 클릭합니다(마우스를 올리면 "Auto CI" 툴팁이 표시됩니다).
1.2 파이프라인 뷰에서 접근하기
서비스 카드에 표시된 4단계 파이프라인에서도 접근할 수 있습니다:
- Source: Git 연동 상태 확인
- Build: 빌드 설정 및 Auto CI 설정 모달 열기
- Deploy: 배포 설정 확인
- Operate: 배포된 서비스 모니터링
파이프라인의 Build 단계를 클릭하면 빌드 관련 설정에 접근할 수 있습니다.
Step 2: 기본 설정 (Settings 탭)
Auto CI 모달이 열리면 기본 설정 탭이 표시됩니다. 여기서 Auto CI의 핵심 옵션들을 설정합니다.
2.1 Auto CI 활성화
-
Enable Auto CI: Auto CI 기능을 켜고 끄는 토글 스위치입니다.
- 권장: ON (처음 설정 후 바로 활성화하세요)
-
Git Branch: Webhook을 감지할 브랜치를 선택합니다.
- 개발 환경:
develop브랜치 권장 - 운영 환경:
main또는master브랜치 권장
- 개발 환경:
모든 브랜치(*)를 선택하면 feature 브랜치 푸시마다 빌드가 실행되어 리소스가 낭비될 수 있습니다. 가능하면 특정 브랜치만 선택하세요.
2.2 Registry 정보 확인
Registry 정보는 서비스 등록 시 입력한 값이 자동으로 표시됩니다. 빌드된 이미지가 저장되는 곳입니다.
- Registry URL: 이미지가 저장될 레지스트리 주소 (예:
harbor.mipllab.com) - Registry Type: 레지스트리 종류 (Harbor 또는 Docker Hub)
- Project/Namespace: 이미지 저장 위치 (예:
library,myproject) - Image Name: 빌드될 이미지 이름 (기본값: 서비스명과 동일)
- Image Prefix (모노레포용, 선택): 모노레포에서 여러 서브 프로젝트를 빌드할 때 이미지 이름 앞에 붙는 접두어입니다. 예: prefix
kiwi-+ 서브 프로젝트backend→kiwi-backend
Registry 정보가 표시되지 않으면 서비스 카드를 클릭해 상세 페이지로 이동한 뒤 Registry 설정을 먼저 완료하세요.
2.3 빌드 프로필 선택
빌드 Pod에 할당될 리소스 크기를 선택합니다. 프로젝트 규모에 맞는 프로필을 고르면 빌드 시간과 리소스 사용량을 최적화할 수 있습니다. (빌드 프로필 참고)
- auto (기본): 프로젝트 감지 결과를 기반으로 자동 선택
- small: 소규모 정적 사이트/간단한 스크립트 빌드용
- medium: 일반 Node.js/Python/Go API 빌드용
- large: 대형 프론트엔드, 모노레포 단일 서비스 빌드용
- extra-large / 2x-large / 4x-large: 대규모 모노레포 또는 대형 Java/Native 빌드용
처음에는 auto로 시작하고, 빌드가 OOM 으로 실패하거나 너무 오래 걸린다면 한 단계 위 프로필로 올려서 다시 시도해보세요.
과거 Auto CI 모달에 있던 보안 게이트 옵션(Auto SAST / Security Gate / Block on Critical·High)은 제거되었습니다. 빌드 단계의 SAST 단독 차단 대신, 이제 SAST/SCA/DAST/SBOM/IaC/시크릿 8영역을 종합 판정하는 [보안 분석]의 보안 게이트 탭(OPA 정책 기반)에서 배포 차단 정책을 설정합니다.
Step 3: 빌드 대상 서비스 감지
KIOPS는 Git 저장소를 분석하여 프로젝트 정보를 자동으로 감지합니다. 이 단계는 Dockerfile 위치와 프로젝트 구조를 파악하는 데 도움이 됩니다.
3.1 프로젝트 타입 자동 감지
- Detect Services 버튼을 클릭합니다.
- KIOPS가 Git 저장소를 분석하여 프로젝트 정보를 자동 감지합니다(보통 몇 초 소요). 프로젝트 타입에 따라 빌드 방식이 달라지므로(Node.js는
npm install, Python은pip install등), KIOPS가 자동으로 파악해 최적의 빌드 설정을 제안합니다.
3.2 감지 결과 항목
감지가 완료되면 다음 정보가 표시됩니다:
- Language: 사용된 프로그래밍 언어 (예: Node.js, Java, Python, Go)
- Framework: 사용된 프레임워크 (예: React, Spring Boot, Django)
- Package Manager: 패키지 관리 도구 (예: npm, yarn, Maven, pip)
- Build Type: 저장소 구조 (예: Single-repo, Mono-repo)
- TypeScript: TypeScript 사용 여부 (Yes / No)
- Dockerfile: Dockerfile 위치 (예:
./Dockerfile,./docker/Dockerfile)
3.3 모노레포(Monorepo) 프로젝트 처리
모노레포(하나의 Git 저장소에 frontend/backend 등 여러 프로젝트가 함께 있는 구조)의 경우 여러 서비스가 감지됩니다:
- 감지된 서브 프로젝트 목록이 테이블로 표시됩니다.
- 빌드할 서브 프로젝트를 체크박스로 선택합니다.
- 각 프로젝트별로 Dockerfile 경로를 확인/수정합니다.
일반적인 모노레포 구조 예시:
my-project/
├── frontend/ → 이미지: my-project-frontend
│ └── Dockerfile
├── backend/ → 이미지: my-project-backend
│ └── Dockerfile
└── api-gateway/ → 이미지: my-project-gateway
└── Dockerfile
선택한 서브 프로젝트 수만큼 병렬로 빌드가 실행됩니다. 리소스가 제한된 환경에서는 한 번에 빌드할 프로젝트 수를 조절하세요.
Step 4: Registry Secret 자동 생성
Kubernetes에서 Private Registry(예: Harbor)의 이미지를 가져오려면 인증 정보가 필요합니다. KIOPS는 Auto CI 설정을 저장하는 시점에 필요한 Registry Secret을 자동으로 생성합니다(별도의 생성 버튼은 없음). (Registry Secret 참고)
Auto CI를 저장하지 않고 Step 5(Webhook 등록)부터 진행하면 Registry Secret이 만들어지지 않습니다. 이 상태에서 빌드는 성공하지만 K8s 배포에서 ImagePullBackOff가 발생합니다. 반드시 Step 6의 Save 버튼을 먼저 클릭해 Secret이 생성된 뒤에 Webhook을 등록하세요.
자동 생성되는 Secret 정보:
- Secret 이름:
{serviceName}-registry-cred - 네임스페이스: 서비스 배포 네임스페이스 (기본: 서비스명)
- 타입:
kubernetes.io/dockerconfigjson - 내용: Registry 인증 정보 (사용자명, 비밀번호)
4.1 Registry 비밀번호 변경 시
Registry 비밀번호가 변경되었거나 Secret을 갱신해야 하는 경우:
- K8s 클러스터에서 기존 Secret을 삭제합니다.
- Auto CI 모달을 다시 저장하면 새로운 Secret이 생성됩니다.
빌드 또는 배포 시 ImagePullBackOff/권한 오류가 발생한다면 다음을 확인하세요:
- K8s 클러스터 연결이 정상인지
- 해당 네임스페이스에 Secret 생성 권한(RBAC)이 있는지
- 네임스페이스가 존재하는지
권한 문제라면 클러스터 관리자에게 secrets 리소스에 대한 create 권한을 요청하세요.
Step 5: Webhook URL 및 Secret Token 설정
이 단계에서는 GitLab/GitHub이 KIOPS에 이벤트를 전송할 수 있도록 Webhook을 설정합니다. 이 연결을 통해 코드 푸시 시 KIOPS의 Webhook URL로 알림이 전송되고 자동으로 빌드가 시작됩니다. (Webhook, Push 이벤트 참고)
KIOPS 화면의 Copy URL 버튼으로 복사한 URL을 그대로 붙여넣지 않고 직접 타이핑하면 끝의 /api/v1/webhook/gitlab 같은 경로가 빠지거나 도메인이 잘못 적힙니다. Git 저장소 테스트에서 "404 Not Found"가 나오면 URL을 다시 복사하세요. 항상 복사 버튼만 사용하는 것이 안전합니다.
5.1 환경별 Webhook URL
일반 사용자에게는 Production 환경의 Webhook URL이 노출되며, 플랫폼 관리자에게만 추가로 Local URL이 함께 표시됩니다(개발 디버깅용). 일반적인 사용 환경에서는 Production URL만 사용하면 됩니다.
- Production: 운영 환경 KIOPS 백엔드 URL. 예시:
https://kiwi.example.com/api/v1/webhook/gitlab - Local (관리자 전용): 로컬 개발 환경에서 ngrok 등 외부 터널로 노출된 URL.
5.2 Webhook URL 복사하기
- Webhook Environments 섹션에서 사용할 환경(보통 Production)을 찾습니다.
- Copy URL 버튼을 클릭하여 Webhook URL을 복사합니다.
- Copy Token 버튼을 클릭하여 Secret Token도 복사합니다.
5.3 GitLab에 Webhook 등록하기
이제 복사한 정보를 GitLab 프로젝트에 등록합니다.
단계별 가이드:
-
GitLab 프로젝트 페이지로 이동합니다.
-
왼쪽 메뉴에서 Settings → Webhooks 클릭
-
다음 정보를 입력합니다:
- URL: KIOPS에서 복사한 Webhook URL
- Secret token: KIOPS에서 복사한 Secret Token
-
Trigger 섹션에서 이벤트를 선택합니다:
- Push events (필수)
- Merge request events (권장)
- Tag push events (릴리스 빌드 시)
-
SSL verification: Enable SSL verification 체크 (권장)
-
Add webhook 버튼을 클릭합니다.
5.4 GitHub에 Webhook 등록하기
GitHub 저장소의 경우:
- GitHub 저장소 → Settings → Webhooks → Add webhook
- Payload URL에 KIOPS Webhook URL 입력
- Content type:
application/json선택 - Secret에 KIOPS Secret Token 입력
- Which events would you like to trigger this webhook?:
- Just the push event를 선택하는 것을 권장합니다. KIOPS의 GitHub Webhook 핸들러는
push이벤트만 처리하며, Pull request / Release 등 다른 이벤트는 "Event type is not processed"로 무시됩니다. - GitLab과 달리 GitHub은 Push 이벤트만 자동 빌드에 사용됩니다.
- Just the push event를 선택하는 것을 권장합니다. KIOPS의 GitHub Webhook 핸들러는
- Add webhook 클릭
KIOPS 백엔드의 GitHub Webhook 처리기는 push 이벤트만 자동 빌드 트리거로 처리합니다. GitLab은 Push/MR/Tag 세 이벤트에 모두 반응하지만, GitHub은 Push 이벤트만 사용합니다 (MR (Merge Request) 참고). "Pull request"나 "Release" 등 다른 이벤트를 체크해도 무시되므로 굳이 추가할 필요가 없습니다. PR 단계에서 빌드 검증이 필요하다면 PR 브랜치를 임시 브랜치(예: pr-build)로 푸시해서 트리거하거나 GitHub Actions 등 별도 도구를 사용해야 합니다.
Secret Token은 서비스별로 고유하며, 외부에 노출되면 악의적인 빌드 트리거가 발생할 수 있습니다. 절대 공개 저장소나 문서에 노출하지 마세요.
Git 저장소에서 Webhook을 등록한 후 Test 버튼으로 연결을 테스트할 수 있습니다. "Hook executed successfully" 또는 200 OK 응답이 나오면 정상입니다.
Step 6: 설정 저장 및 테스트
설정이 완료되면 저장하고 테스트해 봅시다. 테스트를 통해 모든 연결이 정상인지 확인할 수 있습니다.
6.1 설정 저장
- 모든 설정을 검토합니다.
- Save 버튼을 클릭합니다.
- "Auto CI 설정이 저장되었습니다" 메시지를 확인합니다. 저장 시 Registry Secret이 함께 자동 생성됩니다.
6.2 Webhook 테스트
실제 코드를 푸시하기 전에 Git 저장소의 테스트 기능으로 연결을 확인합니다:
- Git 저장소 → Settings → Webhooks 이동
- 등록한 Webhook 항목을 찾습니다.
- Test 드롭다운 버튼 클릭
- Push events 선택
- Hook executed successfully: HTTP 200 - 정상 연결
- HTTP 401/403 - Secret Token 불일치 또는 권한 문제
- 연결 실패 - KIOPS 서버 접근 불가 (방화벽, URL 오류 확인)
6.3 KIOPS에서 빌드 확인
테스트 이벤트 전송 후 KIOPS에서 확인합니다:
- [서비스 관리] 페이지로 이동합니다.
- 해당 서비스의 파이프라인에서 Build 단계가 "빌드 중" 상태인지 확인합니다.
- 클릭하여 빌드 로그에서 "Triggered by Webhook" 메시지를 확인합니다.
6.4 빌드 진행 상황 모니터링
빌드가 시작되면 실시간으로 진행 상황을 모니터링할 수 있습니다:
- Progress Bar: 빌드 진행률 (0~100%)
- Current Step: 현재 실행 중인 단계 (Clone, Build, Push 등)
- Build Log: 실시간 빌드 로그 (터미널 출력)
- Duration: 빌드 경과 시간
첫 빌드는 이미지 캐시가 없어 오래 걸릴 수 있지만, 두 번째 빌드부터는 캐시를 활용해 빠르게 완료됩니다.
실제 사용 시나리오
다양한 팀의 워크플로우에 맞는 Auto CI 설정 예시입니다. 자신의 상황에 맞는 시나리오를 참고하세요.
시나리오 1: 개발 브랜치 자동 빌드/배포
상황: 개발자가 develop 브랜치에 코드를 푸시하면 개발 서버에 자동 배포되어 바로 확인하고 싶습니다.
설정 방법:
- Auto CI: 활성화
- Git Branch:
develop - 빌드 프로필: auto 또는 medium
결과: develop 브랜치에 푸시하면 자동으로 빌드 → 개발 환경에 배포됩니다.
시나리오 2: 릴리스 태그 빌드
상황: v1.0.0 같은 릴리스 태그를 생성할 때만 프로덕션용 이미지를 빌드하고, 배포는 수동으로 하고 싶습니다.
설정 방법:
- Auto CI: 활성화
- Git Branch:
main
결과: git tag v1.0.0 && git push --tags 시 자동으로 빌드되고, 배포는 별도로 진행합니다. 배포 차단이 필요하면 [보안 분석]의 보안 게이트 탭에서 정책을 설정하세요.
시나리오 3: MR 검증 빌드
상황: 코드 리뷰 전에 빌드가 통과하는지 자동으로 확인하고 싶습니다.
설정 방법:
- Auto CI: 활성화
결과: MR 생성/업데이트 시 자동으로 빌드가 실행됩니다. 빌드 후 보안 검사는 [보안 분석] 페이지에서 SAST/SCA 등을 실행해 확인합니다.
실무에서는 시나리오 1(개발 환경)과 시나리오 2(프로덕션 환경)를 조합하는 팀이 많습니다. 개발은 자동, 프로덕션은 신중하게 운영하는 패턴입니다.
문제 해결
Auto CI 설정이나 빌드 중 문제가 발생하면 아래 가이드를 참고하세요.
Webhook이 트리거되지 않음
- Git 테스트에서 "Connection refused": KIOPS 서버에 접근할 수 없습니다. 방화벽 설정과 KIOPS 서버 상태를 확인하세요.
- Git 테스트에서 "HTTP 401/403": Secret Token이 불일치합니다. KIOPS에서 토큰을 다시 복사하여 Git 저장소에 등록하세요.
- 테스트 성공했는데 빌드 안 됨: 브랜치 필터 문제입니다. 푸시한 브랜치가 Auto CI 브랜치 설정과 일치하는지 확인하세요.
GitLab의 Settings → Webhooks → Recent Deliveries, 또는 GitHub의 Webhook Recent Deliveries 탭에서 각 호출의 요청/응답 내용을 볼 수 있습니다.
Registry Secret 관련 오류
- "Permission denied": K8s 권한이 부족합니다. 클러스터 관리자에게 secrets 리소스 create 권한을 요청하세요.
- "Namespace not found": 네임스페이스가 없습니다. 먼저 네임스페이스를 생성하거나 다른 네임스페이스를 선택하세요.
빌드 실패
- "Dockerfile not found": Dockerfile이 없습니다. Build Wizard로 Dockerfile을 생성하세요.
- "Build timeout": 빌드 시간이 초과되었습니다. 빌드 프로필을 더 큰 단계로 변경하거나 Dockerfile을 최적화하세요.
- "Registry authentication failed": 레지스트리 인증에 실패했습니다. Registry 자격증명(사용자명/비밀번호)을 재확인하세요.
- "Git clone failed": Git 접근에 실패했습니다. 기관에 등록된 GitLab/GitHub 토큰에
read_repository권한이 있는지 확인하세요.
빌드가 계속 대기 중 (Pending)
- 원인: 동시 빌드 제한에 도달했거나 빌드 워커가 부족합니다.
- 해결: 이전 빌드가 완료될 때까지 대기하거나, 불필요한 빌드를 취소하세요.
- 참고: 5분 이상 building 상태가 지속되면 자동으로 실패 처리됩니다.
- 빌드가 반복적으로 timeout되는 경우 (빌드 프로필을 큰 단계로 올려도 해결되지 않을 때)
- Registry Secret 권한 문제가 해결되지 않는 경우
- Webhook URL이 외부에서 접근 불가능한 경우
다음에 할 일
Auto CI 설정과 Webhook 등록까지 마쳤다면, 다음 순서로 동선을 이어가는 것을 권장합니다.
- Push 테스트: 가장 먼저 작은 변경(예: README 한 줄 수정)을 푸시해 Webhook이 정상 동작하는지 확인합니다. KIOPS 빌드 로그에 "Triggered by Webhook" 메시지가 보이면 성공입니다.
- 보안 게이트 설정: 빌드가 안정적으로 동작하면 [보안 분석] 페이지의 보안 게이트 탭에서 위험 등급이 높은 취약점을 가진 이미지의 배포를 차단하도록 정책(OPA, 8영역 종합)을 설정합니다.
- 배포 자동화: 빌드 후 자동 배포가 필요하다면 K8s 배포 또는 Docker 배포 가이드를 참고해 배포 환경을 설정하세요.
관련 가이드
- Build Wizard - Dockerfile 자동 생성
- 수동 빌드 - 직접 빌드 실행
- K8s 배포 - Kubernetes 배포 설정
- Docker 배포 - Docker 환경 배포
참고: 빌드 시스템 아키텍처
┌─────────────┐
│ GitLab / │
│ GitHub │
└──────┬──────┘
│ Webhook (Push/MR/Tag)
▼
┌─────────────┐
│ KIOPS │
│ Backend │
└──────┬──────┘
│ Build Queue
▼
┌─────────────────────────────────────────────┐
│ Build Queue Worker (kiwi-builds) │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ Build 1 │ │ Build 2 │ │ Build 3 │ ... │
│ └────┬────┘ └────┬────┘ └────┬────┘ │
└───────┼────────────┼────────────┼──────────┘
│ │ │
▼ ▼ ▼
┌─────────────────────────────────────────────┐
│ Kubernetes Cluster │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ BuildKit │ │ BuildKit │ │ BuildKit │ │
│ │ Pod 1 │ │ Pod 2 │ │ Pod 3 │ │
│ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
└───────┼────────────┼────────────┼──────────┘
│ │ │
└────────────┼────────────┘
▼
┌─────────────┐
│ Harbor │
│ Registry │
└─────────────┘