Kubernetes Cluster 에 Docker Private Registry 설치하기(TLS, Self Signed Certificate)
목차
- Virtual Box 에 Ubuntu 20.04 LTS 설치하기
- VirtualBox VM 에 고정 IP 설정하기(IPTIME 공유기의 DHCP 설정 기반)
- VirtualBox VM 외부에서 접속하기(IPTIME 공유기의 DDNS, Port-Fowarding 설정 기반)
- Virtualbox VM(Ubuntu 20.04 LTS)에 Kubernetes cluster 설치하기
- Raspberry pi 4B 에 Ubuntu 20.04 LTS 설치하기
- Raspberry pi 4B 에 Kubernetes Cluster 설치하기
- Kubernetes Cluster 에 MySQL 설치하기(Deployment, Local Persistent Volume)
- Scrapy 기반 Daum News Crawler 구현하기
이제 본격적으로 Application 을 개발함과 동시에 해당 Application 을 Kubernetes Cluster 에 Pod 의 형태로 배포해야 할 단계에 왔습니다. Pod 가 생성되기 위해서는 Image 가 필요하고 해당 Image 를 저장하기 위해서는 Image 저장소가 필요합니다. Docker Hub 을 사용해도 되지만 Docker Hub 에 이미지를 올릴땐 모두 공개해야 하기 때문에 Credential 정보를 포함하는 Image 를 올리기에는 적당하지 않습니다. 이를 위해 Docker Private Registry 를 사용할 수 있습니다. 이번 글에서는 Docker Private Registry 를 Kubernetes Cluster 위에 설치해보도록 하겠습니다.
- 작업폴더 설정
$ sudo mkdir -p /workspace/kubernetes/registry
$ sudo chown -R truelifer:truelifer /workspace/kubernetes
$ cd /workspace/kubernetes/registry
먼저 docker private registry 배포를 위한 폴더를 생성 후 적절한 권한을 설정해줍니다.
- StorageClass 생성
이전 글에서 이미 만들어 놓은 StorageClass 를 사용합니다.
- Persistent Volume 생성
$ sudo mkdir -p /workspace/data/kubernetes/pv/registry
$ sudo chmod 777 /workspace/data/kubernetes/pv/registry
우선 worker1, worker2 각각 위의 명령을 통해 docker private registry의 데이터가 저장될 폴더를 생성하고 적절한 권한을 부여합니다. 우선은 777으로 권한을 설정했으나 향후 RBAC 기반으로 전체적인 권한체계를 구성할 예정입니다.
$ vi pv-registry.yaml
vi 편집기로 pv 생성을 위한 yaml 파일을 생성합니다.
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-registry
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: local-storage
local:
path: /workspace/data/kubernetes/pv/registry
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- worker2
10GB 의 용량으로 설정하고 하나의 pod 만 접근하기 때문에 ReadWriteOnce, pod 가 삭제되더라도 데이터 보존을 위해 Retain 전략으로 설정합니다. NodeAffinity 설정을 통해 worker2 에 pv 를 생성하도록 합니다. 즉, docker private registry pv 는 worker2 노드에 만들어지고 docker private registry pod 역시 worker2 노드에 생성되게 됩니다.
$ kubectl apply -f ./pv-registry.yaml
kubectl 명령을 통해 pv 를 생성해 줍니다.
persistentvolume/pv-registry created
정상적으로 생성이 되었다면 위와 같은 메세지가 출력됩니다.
- Persistent Volume Claim 생성
$ vi pvc-registry.yaml
vi 편집기로 pvc 생성을 위한 yaml 파일을 생성합니다.
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: pvc-registry
spec:
accessModes:
- ReadWriteOnce
storageClassName: local-storage
resources:
requests:
storage: 10Gi
위에서 생성한 local-storage 타입으로 10GB 의 용량의 pvc 를 생성합니다.
$ kubectl apply -f ./pvc-registry.yaml
kubectl 명령을 통해 pv 를 생성해 줍니다.
persistentvolumeclaim/pvc-registry created
정상적으로 생성이 되었다면 위와 같은 메세지가 출력됩니다.
$ kubectl get pvc
생성된 pvc 의 상태를 조회해봅니다.
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
pvc-registry Pending local-storage 25s$ kubectl get pv
생성된 pv 의 상태도 조회해봅니다.
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pv-registry 10Gi RWO Retain Available local-storage 3m11s
VolumeBindingMode 를 WaitForFirstConsumer로 설정했기 때문에 Status 가 아직 Available 인 점을 확인할 수 있습니다.
- TLS 인증서 생성
Docker Private Registry 는 간단한 설정( — insecure-registry)을 통해 TLS 인증없이 http 로만 통신이 가능합니다. 하지만 실무에서 repository 를 보안없이 구성한다는 것은 매우 위험한 일입니다. 인증서를 만들기 위해서는 private key 를 만들고 CSR(Certificate Signing Request)을 생성 후 공인된 발급기관(CA) 의 서명을 받아야 하지만 이를 위해서는 매년 갱신하는 프로세스를 거쳐야 하고 비용도 발생합니다. 개인프로젝트 레벨이므로 Self-Sign 을 통해 인증서를 무료로 생성해보도록 하겠습니다.
$ mkdir -p /workspace/certs/registry
$ cd /workspace/certs/registry
먼저 master 노드에서 인증서 작업을 할 폴더를 만듭니다.
$ openssl req \
-newkey rsa:4096 -nodes -keyout /workspace/certs/registry/truelifer-registry.com.key \
-x509 -days 3650 -out /workspace/certs/registry/truelifer-registry.com.crt
openssl 을 사용해 4096비트의 Private Key 를 생성 후 3650일짜리 Certificate 를 생성합니다. Private Registry 의 도메인이름은 truelifer-registry.com 으로 합니다.
Generating a RSA private key
..............................................++++
....................................................................................................................................................................................++++
writing new private key to '/workspace/certs/registry/truelifer-registry.com.key'
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:
State or Province Name (full name) [Some-State]:
Locality Name (eg, city) []:
Organization Name (eg, company) [Internet Widgits Pty Ltd]:
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []:truelifer-registry.com
Email Address []:
다른 부분은 생략가능하지만 CN 에 truelifer-registry.com 을 입력합니다. 향후 각 노드에서 이 도메인으로 접속하기 위해서는 DNS 사용 대신 /etc/hosts 을 이용하도록 할 예정입니다.
$ ls /workspace/certs/registry
정상적으로 실행되었다면 truelifer-registry.com.crt truelifer-registry.com.key 두 파일이 생성되어 있을 것입니다. 이 두 파일을 worker1 노드와 worker2 노드에 복사해주어야 합니다.
$ sudo mkdir -p /workspace/certs/registry
$ sudo chown -R truelifer:truelifer /workspace/certs
$ cd /workspace/certs/registry
worker1, worker2 노드에 위의 명령어을 실행하여 인증서와 key파일이 저장될 폴더를 생성합니다.
저는 winscp 라는 툴을 통해 master 노드에 있는 인증서와 key 파일을 worker1, worker2 노드에 복사해주었습니다.
$ sudo mkdir -p /etc/docker/certs.d/truelifer-registry.com:8090
$ sudo cp /workspace/certs/registry/truelifer-registry.com.crt /etc/docker/certs.d/truelifer-registry.com:8090/ca.crt
- TLS 인증서 적용
위에서 생성한 TLS 인증서를 기반으로 docker private registry 가 kubernetes cluster 상에 띄워질 것입니다. 이 registry 에 접속하려는 Client 는 위에서 생성한 TLS 인증서를 가지고 접속요청을 해야 합니다. 이를 위해 TLS 인증서를 OS(Ubuntu) 레벨과 Docker Daemon 레벨에 적용하는 과정을 진행하겠습니다. 이 과정은 master, worker1, worker2 모든 노드에 동일하게 적용해야합니다.
$ sudo cp /workspace/certs/registry/truelifer-registry.com.crt /usr/share/ca-certificates
먼저 인증서를 /usr/share/ca-certificates 폴더에 복사합니다. 폴더 경로는 os 에 따라 다를 수 있습니다.
$ sudo vi /etc/ca-certificates.conf
ca-certificates.conf 파일의 맨 마지막에 truelifer-registry.com.crt 라인을 추가한 후 저장합니다.
$ sudo update-ca-certificates
인증서를 업데이트 해줍니다.
Updating certificates in /etc/ssl/certs...
1 added, 0 removed; done.
Running hooks in /etc/ca-certificates/update.d...
done.
정상적으로 추가되었다면 위와 같은 메세지가 출력됩니다.
인증서는 os 레벨 뿐 아니라 docker daemon 에게도 적용해주어야 합니다.
$ sudo mkdir -p /etc/docker/certs.d/truelifer-registry.com:8090
$ sudo cp /workspace/certs/registry/truelifer-registry.com.crt /etc/docker/certs.d/truelifer-registry.com:8090/ca.crt
위의 명령어를 실행하여 docker daemon 이 인증서를 신뢰하도록 합니다. 이 메커니즘이 작동되는 원리는 이 링크를 참조하면 됩니다. 경로는 반드시 /etc/docker/certs.d/{도메인이름:port} 이어야 하며 root 인증서 파일이름은 ca.crt 이어야 합니다.
- Configmap 생성
docker private registry 이미지는 몇 가지 환경변수로 설정할 수 있는 방법을 제공합니다. 이 설정들을 Deployment yaml 파일에 설정해도 되지만 배포 Context 라던지 상황에 따라 Variation 이 존재할 수 있으므로 유연한 설정을 위해 Configmap 으로 관리하도록 하겠습니다.
$ kubectl create configmap configmap-registry --from-literal REGISTRY_HTTP_ADDR=0.0.0.0:8090 --from-literal REGISTRY_HTTP_TLS_CERTIFICATE=/certs/truelifer-registry.com.crt --from-literal REGISTRY_HTTP_TLS_KEY=/certs/truelifer-registry.com.key
http address, certificate와 private key 의 위치를 지정하고 Configmap 으로 생성합니다.
configmap/configmap-registry created
정상적으로 생성이 되었다면 위와 같은 메세지가 출력됩니다.
- Deployment 생성
$ vi deployment-registry.yaml
vi 편집기로 deployment 생성을 위한 yaml 파일을 생성합니다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: registry
spec:
selector:
matchLabels:
app: registry
strategy:
type: Recreate
template:
metadata:
labels:
app: registry
spec:
containers:
- image: registry:latest
name: registry
env:
- name: REGISTRY_HTTP_ADDR
valueFrom:
configMapKeyRef:
name: configmap-registry
key: REGISTRY_HTTP_ADDR
- name: REGISTRY_HTTP_TLS_CERTIFICATE
valueFrom:
configMapKeyRef:
name: configmap-registry
key: REGISTRY_HTTP_TLS_CERTIFICATE
- name: REGISTRY_HTTP_TLS_KEY
valueFrom:
configMapKeyRef:
name: configmap-registry
key: REGISTRY_HTTP_TLS_KEY
ports:
- containerPort: 8090
name: registry
volumeMounts:
- name: volume-registry
mountPath: /var/lib/registry
- name: certs
mountPath: /certs
volumes:
- name: volume-registry
persistentVolumeClaim:
claimName: pvc-registry
- name: certs
hostPath:
path: /workspace/certs/registry
위에서 생성한 pv, pvc, configmap, secret 을 기반으로 deployment yaml 파일을 작성합니다. 추가로, TLS 적용을 위해 certs 라는 hostPath 기반 volume 을 생성하여 마운트해 줍니다.
$ kubectl apply -f ./deployment-registry.yaml
kubectl 명령을 통해 deployment 를 생성해 줍니다.
deployment.apps/registry created
정상적으로 생성이 되었다면 위와 같은 메세지가 출력됩니다.
$ kubectl get pods
생성된 pod 를 조회해봅니다.
NAME READY STATUS RESTARTS AGE
registry-78b9dbcf7b-hzsjz 1/1 Running 0 108s
Status 가 Running 상태로 정상적으로 배포되었음을 확인할 수 있습니다.
- Service 생성
Deployment 로 생성한 Pod 에 접근하기 위해 Service 를 생성합니다.
$ vi service-registry.yaml
vi 편집기로 service 생성을 위한 yaml 파일을 생성합니다.
apiVersion: v1
kind: Service
metadata:
name: registry
spec:
type: ClusterIP
ports:
- port: 8090
targetPort: 8090
selector:
app: registry
보안을 위해 registry 를 Kubernetes Cluster 내부에서만 접근 가능하도록 Service 의 Type 을 ClusterIP 로 설정합니다.
$ kubectl apply -f ./service-registry.yaml
kubectl 명령을 통해 service 를 생성해 줍니다.
service/service-registry created
정상적으로 생성이 되었다면 위와 같은 메세지가 출력됩니다.
$ kubectl get services
생성된 service를 조회해봅니다.
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
registry ClusterIP 10.105.202.1 <none> 8090/TCP 35s
Cluster IP 타입으로 service 가 정상적으로 생성되었음을 확인할 수 있습니다.
- Host 파일 수정
위에서 Docker Private Registry 의 접속도메인을 truelifer-registry.com 으로 설정했습니다. 이는 도메인이름으로 실제로 접속하기 위해서는 IP 로 변환해주는 DNS 서비스가 필요합니다. 하지만 현재 별도의 DNS 서버가 셋업되어있지 않아 간단히 host 파일을 수정하여 truelifer-registry.com 에 해당하는 IP 를 매핑해주도록 하겠습니다.
$ sudo vi /etc/hosts
vi 편집기를 통해 host 파일을 열고 맨 아랫줄에 10.105.202.1 truelifer-registry.com 라인을 추가합니다. 10.105.202.1 은 위에서 생성한 registry 서비스의 cluster ip 입니다.
- 이미지 Push 테스트
이제 모든 설정이 완료되었습니다. curl 을 이용해 각 노드에서 정상적으로 접속이 되는지 확인해보도록 하겠습니다.
$ curl https://truelifer-registry.com:8090/v2/_catalog
curl 명령어를 통해 https 프로토콜을 사용하여 truelifer-registry.com 도메인의 8090 포트로 접속하여 repository 의 리스트를 반환하는 api 를 호출합니다.
{"repositories":[]}
아직 생성한 repository 가 없으므로 blank array 를 반환하는 위의 메세지는 정상입니다.
접속자체는 확인했으니 테스트로 이미지를 push 해 보겠습니다.
$ sudo docker pull busybox
$ sudo docker tag docker.io/busybox:latest truelifer-registry.com:8090/busybox:latest
$ sudo docker push truelifer-registry.com:8090/busybox:latest
정상적으로 push 가 되는지 확인합니다.
$ curl https://truelifer-registry.com:8090/v2/_catalog
정상적으로 push 가 완료되었다면 위 명령어를 실행합니다. 아래의 메세지가 표시된다면 정상적으로 완료된 것입니다.
{"repositories":["busybox"]}
- 마무리
Kubernetes Cluster 내에서 사용할 Docker Private Repository 를 구성해보았습니다. 다음 글에서는 이전에 Scrapy 기반으로 구현한 Daum News Crawler 를 Kubernetes Cluster 상에 배포를 해보록 하겠습니다.