컨테이너 기술이 등장하면서 많은 사람들이 컨테이너 이미지를 기반으로 서비스를 제공하기 시작했습니다.
이에 더해 현재는 도커, 도커스웜, 쿠버네티스.. 등의 플랫폼들로 컨테이너 이미지 기반의 워크로드를 수행하고 있습니다.
하지만 이 컨테이너 이미지를 로컬 저장소에 저장해서 사용하면, 외부에서 해당 이미지를 사용하기 어렵기 때문에 이미지를 쉽게 가져올 수 있는 방법이 필요했습니다.
이에 따라 등장한 것이 바로 컨테이너 레지스트리(Container Registry)입니다.
컨테이너 레지스트리는 소스 코드를 담는 Source repository(ex: github) 나 War,Jar 등의 바이너리 파일을 담는 Artifact registry(ex: Nexus) 와 같이 컨테이너를 담는 컨테이너 전용 저장소입니다.
개발자 및 운영자들은 이 컨테이너 레지스트리를 이용해 이미지를 외부에서 쉽게 가져올 수 있기 때문에 컨테이너 이미지의 중앙화(Centralization)가 가능해졌습니다.
이 기술을 이용해 Docker의 Docker hub나, AWS,GCP,Azure 등의 클라우드 벤더 사에서 컨테이너 레지스트리를 서비스로 제공하고 있어 이제는 컨테이너 레지스트리를 직접 구축하지 않아도 레지스트리를 이용할 수 있게 되었습니다.
하지만 선택지가 많아짐에 따라 기존에 이용하던 컨테이너 레지스트리 서비스에서 다른 서비스로 이미지들을 옮겨야할 필요성도 발생하게 되었습니다.
지금까지 이런 니즈를 완벽히 충족시켜주는 도구가 존재하지 않았는데요. 다행히도 오픈소스 컨테이너 레지스트리인 Harbor를 통해 컨테이너 레지스트리간의 레플리케이션을 수행할 수 있게 되었습니다.
그래서 이번 포스팅에서는 Harbor를 통해 컨테이너 레지스트리 간 레플리케이션을 수행하는 방법을 알아보겠습니다.
1. Harbor란?
Harbor는 오픈소스 컨테이너 레지스트리 도구입니다.
시중에서 사용할 수 있는 컨테이너 레지스트리 서비스는 보통 클라우드, 혹은 외부 네트워크에서 이미지를 저장해야 하기 때문에 보통 격리된 환경에서 직접 컨테이너 레지스트리를 구축해야 할 니즈가 있는 경우 선택하는 도구입니다.
Harbor는 CNCF 제단에 속한 프로젝트이며 얼마안되는 Graduated 등급의 프로젝트이기 때문에 많은 레퍼런스를 소유하고 신뢰도가 높은 도구이기도 합니다.
현재 Harbor는 RBAC 기반의 접근 제어, 컨테이너 취약성 검사, 웹 UI, 레지스트리 레플리케이션 등의 다양한 컨테이너 레지스트리를 관리 기능들을 제공하고 있습니다.
그 중에서도 이번 포스팅에서는 컨테이너 레지스트리 간의 복제를 수행할 수 있는 레플리케이션(Replication) 기능을 주로 사용해 레지스트리 간 이미지 복제를 수행해보겠습니다.
2. Harbor 들여다보기
Harbor를 본격적으로 사용해보기 전에 Harbor가 어떤 구성 요소들로 이루어져 있는지 알아보겠습니다.
우선 Harbor의 아키텍쳐는 다음과 같습니다. 아키텍쳐의 구성요소들을 하나씩 살펴봅시다.
- Core Service : Harbor의 API와 인증, 웹 GUI를 담당하는 Component입니다. 이미지의 Pull/Push에 필요한 Token을 발행하며 웹 환경의 GUI 및 Webhook을 제공합니다.
- Job Service : 이미지의 Replication와 같은 작업을 담당하는 Components입니다. 이미지 Pull/Push 작업을 시작,중단,재시도하는 역할을 합니다.
- Databases : Harbor의 모든 Components들은 Stateless입니다. 때문에 저장해야 할 데이터들은 Redis와 PostgreSQL같은 외부 저장소에 저장합니다. 메타데이터같은 정보들을 저장하는 역할을 합니다.
- Image Registry : Harbor에서 실제 컨테이너 레지스트리 역할을 하는 Component입니다. 이미지를 저장하는 저장소 역할을 해 가져온 이미지를 저장 및 관리합니다.
- Vulnerability Scanning : Harbor는 이미지의 취약점을 검사하기 위한 Scanner를 제공하고 있습니다. 이에 사용되는 Vulnerability Scanner는 오픈소스 프로젝트인 Trivy와 Clair를 사용합니다.
- Trusted Content : 이미지의 Integrity와 Freshness를 증명하기 위한 Image signing을 맡는 Component입니다. 이를 위해 Notary라는 도구를 사용하며, 이미지의 신뢰성을 증명하기 위해 Metadata에 새긴 Sign을 관리하는 역할을 합니다.
3. Harbor 설치 및 구성하기
이제 본격적으로 Harbor를 사용하기 위한 준비를 해보겠습니다.
이번 포스팅에서 Harbor는 Kubernetes 플랫폼 위에서 MSA 구조로 설치할 것입니다.
이를 위해 우선 Kubernetes 클러스터를 하나 준비해야 합니다. 본 포스팅에서는 GCP의 GKE를 사용해 클러스터를 하나 생성했습니다.
Kubernetes에 Harbor를 설치하는 가장 쉬운 방법은 패키지 매니저 도구인 Helm을 사용하는 것입니다.
Helm을 설치한 뒤 아래 명령어로 Harbor를 레포지토리에 추가합니다.
1
|
helm repo add harbor https://helm.goharbor.io
|
cs |
이후 Harbor를 위한 네임스페이스를 생성한 뒤 harbor 패키지를 설치합니다.
1
2
|
kubectl create ns hb
helm install harbor harbor/harbor -n hb
|
cs |
Pod, Service, Ingress가 정상적으로 동작하는지 체크해 생성을 확인합니다.
Pod
Service
Ingress
이제 harbor-ingress의 IP address를 통해 Harbor Web UI에 접속할 수 있습니다. 하지만 IP address를 브라우저에 입력해 접속하면 아래와 같은 에러가 발생하는 것을 볼 수 있습니다.
response 404 (backend NotFound), service rules for the path non-existent
이는 harbor-ingress의 Ingress rule이 IP address가 아닌 호스트네임을 통해 접속하도록 되어있기 때문입니다.
Helm을 이용해 설치했을시 호스트네임의 default값은 core.harbor.domain입니다.
호스트네임은 helm 설치 시 자신이 소유한 도메인 네임으로 변경해서 사용할 수 있지만, 현재 소유한 도메인이 없기 때문에 로컬 DNS 파일을 수정함으로써 해당 호스트네임으로 접속하도록 해보겠습니다.
윈도우OS의 경우에는 C:\Windows\System32\drivers\etc 디렉토리, MacOS의 경우에는 /etc/hosts 디렉토리의 hosts 파일을 아래와 같이 수정함으로써 DNS Lookup 규칙을 바꿀 수 있습니다.
hosts 파일을 수정한 후에 https://core.harbor.domain 주소로 접속하면 Harbor의 웹 UI를 볼 수 있습니다.
기본적으로 Harbor Web UI는 HTTPS 연결을 통해서 접속하도록 되어 있습니다. Helm을 이용해서 설치할 시 SSL 연결에 필요한 ca cert, Public key, Private key를 자동으로 생성합니다.
단 접속하려는 로컬 브라우저에서는 해당 ca를 신뢰하지 않으니 브라우저에서 HTTPS 접속을 허용하지 않습니다.
브라우저의 경고를 무시하고 HTTP 접속을 할 수는 있지만 Production 환경에서는 HTTPS를 통해 Harbor로 접근하는 것을 권장합니다.
이를 위해 Harbor Helm에서는 직접 생성한 Cert를 이용해 tls를 구성할 수 있게끔 했는데요. 이를 이용해 self-signed certificate로 Harbor에 HTTPS 접속하는 방법에 대해 알아보겠습니다.
우선 다음 명령어를 실행해 Self-signed certificate의 인증에 필요한 CA 인증서와 키를 생성합니다.
1
2
3
4
|
openssl genrsa -out ca.key 2048
openssl req -x509 -new -nodes \
-key ca.key -sha256 \
-days 1825 -out ca.crt
|
cs |
다음으로 Server의 CSR을 생성하기 위해 사용할 CSR 컨피그 파일을 생성합니다. 컨피그 생성에 들어가는 값들은 사용자가 임의로 변경할 수 있습니다. HARBOR_IP는 Harbor Ingress가 노출한 IP address로 대체합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
cat > csr.conf <<EOF
[ req ]
default_bits = 2048
prompt = no
default_md = sha256
req_extensions = req_ext
distinguished_name = dn
[ dn ]
C = KR
ST = Seoul
L = Seocho
O = YourOrg
OU = YourOU
CN = Yourhost.com
[ req_ext ]
subjectAltName = @alt_names
[ alt_names ]
DNS.1 = core.harbor.domain
IP.1 = HARBOR_IP
EOF
|
cs |
이제 서버 측에서 사용할 Private key를 생성합니다.
이후 생성한 Private key와 CSR 컨피그 파일을 기반으로 CSR을 생성하고 이를 기반으로 Certificate 파일을 생성합니다.
1
2
3
4
5
|
openssl genrsa -out server.key 2048
openssl req -new -key server.key -out server.csr -config csr.conf
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key \
-CAcreateserial -out server.crt -days 10000 \
-extfile csr.conf -extensions req_ext
|
cs |
이제 생성된 ca.crt, server.key, server.crt 파일을 기반으로 Kubernetes tls secret을 생성합니다. 각각의 파일을 base64로 인코딩한 값을 data값에 넣어야 합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
apiVersion: v1
data:
ca.crt: LS0tLS1CRUdJTiB...
tls.crt: LS0tLS1CRUdJTi...
tls.key: LS0tLS1CRUdJTi...
kind: Secret
metadata:
creationTimestamp: "2022-05-16T05:11:09Z"
name: harbor-ingress
namespace: hb
resourceVersion: "14738526"
uid: c39c912f-c597-4bf9-ab60-8f9c52bb6eee
type: kubernetes.io/tls
|
cs |
이제 helm install 시 아래와 같이 플래그를 설정해 미리 생성한 tls secret을 기반으로 Ingress를 구성하도록 합니다.
1
|
helm install harbor harbor/harbor -n hb --set expose.tls.certSource=secret=harbor-ingress
|
cs |
마지막으로 생성한 ca를 신뢰하도록 설정을 변경하면 HTTPS 접속을 통해 Harbor Web UI에 접근할 수 있습니다. 해당 부분에 대한 자세한 설명은 아래 링크에서 볼 수 있습니다.
https://superuser.com/questions/1359755/trust-self-signed-cert-in-chrome-macos-10-13
4. Harbor를 이용해 레지스트리 간 복사하기
이제 설치한 Harbor를 이용해서 레지스트리 간 Replication을 수행해보겠습니다.
본 포스팅에서는 GCP의 컨테이너 레지스트리 서비스인 Google Container Registry(이하 GCR)에서 Docker의 컨테이너 레지스트리인 Docker hub로 이미지를 복제하는 시나리오로 진행하겠습니다.
우선 Harbor 웹 UI를 로그인합니다. 기본 ID/Password는 admin/Harbor12345입니다.
로그인에 성공하면 아래와 같은 Harbor의 Project 페이지를 볼 수 있습니다.
좌측 탭에서 많은 기능들을 제공하고 있는 것을 볼 수 있습니다. 탭에서 Replication, User 관리, GC, 라벨링 등의 다양한 기능들에 접근할 수 있습니다.
하지만 우리가 해야 할 것은 레지스트리 간의 Replication이므로 필요한 탭만 이용하도록 하겠습니다. 우선 Registries 탭으로 접근합니다.
Registries 페이지로 오면 아래와 같은 화면을 볼 수 있습니다.
Harbor는 여기서 생성한 Endpoint를 기반으로 이미지를 가져오거나 밀어넣을 레지스트리를 결정하는 방식으로 Replication을 수행합니다.
NEW ENDPOINT 버튼을 클릭해서 새 엔드포인트를 생성하겠습니다.
우선 이미지를 가져올 컨테이너 레지스트리인 GCR 엔드포인트를 생성합니다. 위와 같이 Provider 란에 Google GCR을 선택 후 엔드포인트 이름을 정하면 됩니다.
Access Secret란에는 GCR 레지스트리에 접근할 수 있는 서비스 어카운트의 JSON 키 내용을 입력합니다.
TEST CONNECTION 버튼을 클릭해 접근이 정상적으로 되는지 확인 후 OK 버튼을 눌러 엔드포인트 생성을 마칩니다.
다음으로 이미지를 가져올 레지스트리인 Docker hub 엔드포인트를 생성합니다.
Access ID와 Access Secret에는 Docker hub의 ID/Password를 입력합니다.
마찬가지로 TEST CONNECTION 버튼을 눌러 접근을 확인한 뒤 OK 버튼을 눌러 생성을 마칩니다.
이렇게 2개의 레지스트리 엔드포인트를 생성했습니다.
이제 이 엔드포인트들을 기반으로 레플리케이션 작업을 생성하겠습니다. 좌측 탭에서 Replications를 클릭해 Replication페이지로 진입합니다.
Replications 페이지에서는 Replication rule을 생성해 복제 작업을 생성할 수 있습니다. 복제 작업은 Push 방식과 Pull 방식이 존재합니다.
Push 방식은 Harbor 레지스트리에서 지정한 엔드포인트 레지스트리로 이미지를 넣는 복제 타입이고,
Pull 방식은 지정한 엔드포인트 레지스트리에서 Harbor 레지스트리로 이미지를 가져오는 복제 타입입니다.
그래서 엔드포인트 간의 레지스트리 복제는 최종적으로
GCR 엔드포인트 레지스트리 -> Harbor 레지스트리 -> Docker hub 엔드포인트 레지스트리
시나리오로 진행되겠습니다.
NEW REPLICATION RULE 버튼을 클릭해 새 복제 룰을 생성합니다.
먼저 Docker hub 엔드포인트로 Push하는 rule을 생성합니다.
Replication mode에서 Push-based를 선택하면 Push 타입 복제를 수행할 수 있습니다.
Destination registry에서 미리 생성한 Docker hub 엔드포인트를 선택합니다.
Destination의 namespace 란에 이미지를 넣을 Docker hub 네임스페이스를 입력합니다. 보통 자신의 계정명이 root 디렉토리입니다.
Trigger Mode는 Manual, Schedule, Event Based 3가지를 선택할 수 있습니다. 각각 수동, 시간 기준 수행, Harbor 레지스트리 감지 수행을 의미합니다.
이번 포스팅에서는 GCR 레지스트리에서 이미지를 가져오는 순간 바로 Docker hub로 넣을 수 있도록 자동화 수준이 높은 Event Based 방식을 선택하겠습니다.
다음으로 GCR 레지스트리에서 Harbor 레지스트리로 이미지를 가져오는 Replication rule을 생성합니다.
마찬가지로 Source registry에서 미리 생성한 GCR 엔드포인트를 지정하고 Source resources filter에서 가져올 GCR 레지스트리의 네임스페이스를 입력합니다. 와일드카드(*)를 사용해 레지스트리의 전체 이미지를 가져올 수 있습니다.
Pull 방식의 Trigger mode는 Manual과 Schedule 방식을 지원합니다.
Replication rule들을 생성했다면 REPLICATION 버튼을 눌러서 복제를 진행합니다.
복제 결과 아래와 같이 GCR 레지스트리에 존재하던 컨테이너 이미지들이 Harbor 레지스트리와 Docker hub 레지스트리에도 복사된 것을 볼 수 있습니다.
GCR Registry
Harbor Registry
Docker hub Registry
작업 실행 경과 및 로그를 해당 Replication rule 아래에서 볼 수 있습니다. 경과를 보면 몇몇 이미지는 복제에 실패한 것을 볼 수 있습니다. 우측의 Logs 아이콘을 클릭하면 복제 로그를 확인할 수 있어 실패 원인을 분석할 수 있습니다.
에러 로그를 보니 컨테이너 볼륨의 특정 디렉토리에서 mkdir 명령어를 실패한 것이 원인으로 보입니다. 아마 호스트 리소스의 부족이 원인이 아닐까 싶습니다.
5. 마무리
지금까지 Harbor를 사용해서 컨테이너 레지스트리 간 Replication을 하는 방법에 대해 알아봤습니다.
직접 사용해보니 Replication의 Push 모드에서 Event-based 트리거를 사용할 시 원본 레지스트리에 이미지를 푸시해도 자동으로 복제가 된다는 장점이 있었습니다.
하지만 원본 레지스트리에서 이미지의 삭제가 복제 레지스트리에는 반영이 안된다는 점, 모든 복제가 모든 복제가 성공한다는 보장이 없다는 단점도 있습니다.
그래서 이런 장단점을 잘 파악해서 필요한 워크로드에 이러한 방식을 사용하시면 될 것 같습니다.
이런 방식 외에도 Harbor는 Private Container Registry를 구축할 수 있는 대표적인 도구이기 때문에 이러한 목적으로도 Harbor를 잘 이용해보시는 것도 좋을 것 같습니다.
'Devops' 카테고리의 다른 글
Kubernetes에 존재하는 Metrics Server란 무엇일까? 그리고 어떻게 해야 잘 사용할 수 있을까? (1) | 2022.06.26 |
---|---|
Apache Kafka란? Apache Kafka를 Kubernetes에서 구성해보자 (1) | 2022.06.16 |
Mysql Operator로 Kubernetes 환경에서 Mysql DB 운영하기 (3) | 2022.05.31 |
Skaffold + buildpack 으로 쉽게 CI/CD Kubernetes pipeline 구성하기 (0) | 2021.07.30 |
Gitlab CI/CD + Terraform 연동으로 IaaC 자동화 파이프라인 구축하기 (3) | 2021.05.30 |