위의 문서를 통해 k3s 클러스터의 기본 설정이 완료되었다. 추가 설정 없이 docker hub 를 통해 이미지를 pull 하고 push 할 수 있지만, 여러 제약도 있고 리모트 서버에 읽고 써야 되어서 아무래도 빠릿빠릿한 부분이 덜하다. 그래서 많은 경우 private docker registry 를 구성하고 이를 사용하도록 하는 듯 하다.

docker registry 를 구성하려면 역시 스토리지를 설정해야 하는데, docker registry helm chart 에서 S3 에 저장하는 기능을 제공한다. 물론 private docker registry 를 k8s 클러스터에서 사용하는데 실제 S3 를 다녀와야 되면 비용도 시간도 낭비니까 우리는 MinIO 라는 훌륭한 제품으로 대체한다.
(S3 는 Spark on K8S 에서는 접근성이 괜찮은 스토리지로 볼 수 있으니 설치해두면 이래저래 쓸모가 있다.)

공부해가면서 설치하는 게 목적이 아니니까 helm chart 로 빠르게 설치해 보자. 일단 helm 을 설치하고…

sudo snap install helm --classic

minio 네임스페이스를 만들고 해당 네임스페이스에 설치하기로 한다.

kubectl create namespace miniohelm install minio-release minio/minio --set accessKey=<accesskey>,secretKey=<secretkey>,service.type=LoadBalancer,service.port=9000, --namespace minio --atomic

호스트 서버에서도 MinIO 에 접근할 수 있어야 되어서 service type 은 Load Balancer 로 설정했다. (이렇게 하면 external IP 가 발급되고 호스트 서버에서 접근 가능하다. 기본값은 ClusterIP 이고 internal IP 만 발급되는데 k8s 클러스터 내에서만 접근 가능하다.)

accessKey 와 secretKey 값은 잘 보관하도록 하자. 접근할 때 필요하다.

이전 문서에서 언급했지만 pod 이 4G 노드를 필요로 한다. 워커 노드들의 메모리 설정을 확인하고 안전하게 메모리 5G 정도로 노드 하나를 준비하도록 하자.

실제로 사용하려면 external IP 와 port 를 알아야 한다. 아래 명령을 실행하자.

kubectl get service -w minio-release -n minioNAME            TYPE           CLUSTER-IP    EXTERNAL-IP    PORT(S)          AGE
minio-release LoadBalancer 10.43.51.27 10.38.12.116 9000:31310/TCP 6h23m

위의 결과에서 endpoint URL 은 http://10.38.12.116:9000 이다.

별도로 MinIO CLI 를 호스트 서버에 설치해야 한다. 손쉽게 바이너리를 바로 다운받아 쓰자.

wget https://dl.min.io/client/mc/release/linux-amd64/mc
chmod +x mc
./mc --autocompletion

이 mc 파일은 PATH 에 포함되는 위치로 잘 옮겨 준다.

다음, k8s 에 설치된 MinIO 에 접근할 수 있게 alias 를 걸어 주고, private docker registry 에 사용할 bucket 을 생성하자.

mc alias set minio-k3s http://10.38.12.116:9000 "<access key>" "<secret key>" --api s3v4
mc mb minio-k3s/docker-registry
mc ls minio-k3s

이제 MinIO 는 설정이 완료되었다. 본격적으로 private docker registry 를 설정해보자.

사실 TLS 설정을 하는 게 정석인 듯 한데 생각보다 잘 안되는 부분이 많아서 HTTP 로 설정했다. docker 나 containerd 모두 HTTPS 를 요구하기 때문에 별도 설정이 필요한데, 이 쪽이 삽질이 덜하긴 할 것 같다. TLS 설정이 잘 되면 HTTP 를 사용하기 위한 별도 설정은 제거해도 된다.

모든 문서에서 htpasswd 를 통한 아주 기초적인 인증은 기본적으로 설정하는 듯 해서 여기서도 한다. (htpasswd 를 사용하려면 apache2-utils 를 설치하거나 htpasswd 가 설치된 docker image 를 통해 실행해야 하는데 그냥 설치한다.)

sudo apt-get install apache2-utils
htpasswd -Bbn <docker registry username> <docker registry password> > ./htpasswd
kubectl create namespace docker-registryhelm install registry stable/docker-registry \
--set s3.region=us-east-1 \
--set s3.regionEndpoint=http://10.38.12.116:9000 \
--set s3.secure=false \
--set s3.bucket=docker-registry \
--set secrets.s3.accessKey=<minio access key> \
--set secrets.s3.secretKey=<minio secret key> \
--set secrets.htpasswd=$(cat ./htpasswd) \
--set storage=s3 \
--set service.type=LoadBalancer \
--namespace docker-registry

마찬가지로 실제로 사용하려면 external IP 와 port 를 알아야 한다. 아래 명령을 실행하자.

kubectl get svc -w registry-docker-registry -n docker-registry
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
registry-docker-registry LoadBalancer 10.43.93.191 10.38.12.80 5000:30968/TCP 5h54m

위의 결과에서 endpoint URL 은 http://10.38.12.80:5000 이다. TLS 를 설정하지 않았다는 점을 염두에 두자.

docker 와 containerd 에서 http endpoint URL 를 허용하도록 설정을 좀 해야 된다.

그 전에, docker 를 호스트 서버에 설치하지 않았다면 설치해 주어야 한다. linuxbrew 로 설치하면 docker client 만 설치되므로, snap 을 활용해서 설치해 준다. 실행할 때마다 sudo 를 필요로 하는 불편함이 있지만 나중에 적당히 alias 를 걸어주자.

sudo snap install docker

설치가 되었다면, 아래와 같이 설정을 변경한다. snap 이 아닌 방법으로 설치했다면 설치 방법에 따라 설정 파일 위치가 다를 것이다. (구글링)

sudo vim /var/snap/docker/current/config/daemon.json

json 내용에 아래 내용을 추가한다.

"insecure-registries":["10.38.12.80:5000"]

그리고 docker daemon 을 재시작한다.

sudo snap restart docker

아래 명령을 실행했을 때,

sudo docker info... Insecure Registries:
10.38.12.80:5000
127.0.0.0/8

가 나오면 제대로 설정된 것이다. 로그인해보자.

sudo docker login http://10.38.12.80:5000 -u <docker registry username>

프롬프트에 docker registry password 로 설정한 값을 입력한다. Login Succeeded 라는 메시지가 나타나야 한다.

몇 가지 테스트를 더 해 보자.

export SERVER="10.38.12.80:5000"
sudo docker pull functions/figlet:latest
sudo docker tag functions/figlet:latest $SERVER/functions/figlet:latest
sudo docker push $SERVER/functions/figlet:latest

이렇게 하면 functions/figlet:latest 가 우리가 설정한 private docker registry 에 push 되어야 한다. 확인해보자.

curl -X GET http://10.38.12.80:5000/v2/_catalog -u <docker registry username> -p

프롬프트가 나타나면 패스워드를 입력한다. 결과는 아래와 같을 것이다.

{"repositories":["functions/figlet"]}

호스트 서버에서는 설정이 완료되었다. 하지만 k8s 노드들은 여전히 HTTP 를 사용하는 private docker registry 를 거부한다. 여기도 설정을 하자.

vim registries.yaml

아래와 같이 채워 넣고…

mirrors:
"10.38.12.80:5000":
endpoint:
- "http://10.38.12.80:5000"
configs:
"10.38.12.80:5000":
auth:
username: <docker registry username>
password: <docker registry password>

마스터 노드와 워커 노드에 실행해야 할 명령이 살짝 다르다. 설정 파일은 같은데 리스타트해야 할 서비스 명이 다르다. 아래를 참조하자.

마스터 노드:

multipass transfer registries.yaml k3s-master:/home/ubuntu/registries.yaml
multipass exec k3s-master -- sudo cp /home/ubuntu/registries.yaml /etc/rancher/k3s/registries.yaml
multipass exec k3s-master -- sudo cat /etc/rancher/k3s/registries.yaml
multipass exec k3s-master -- sudo service k3s restart

워커 노드:

multipass transfer registries.yaml k3s-master:/home/ubuntu/registries.yaml
multipass exec k3s-master -- sudo cp /home/ubuntu/registries.yaml /etc/rancher/k3s/registries.yaml
multipass exec k3s-master -- sudo cat /etc/rancher/k3s/registries.yaml
multipass exec k3s-master -- sudo service k3s-agent restart

전체 노드에 설정해야 한다. 한꺼번에 하지 말고 천천히 한 대씩 해주자.

이제 private docker registry 에 push 한 image 를 사용하는 pod 을 여러 개 배포해서 노드들이 image 를 받는 데 문제가 없는지 확인하자.

(실제로는 Spark 관련 설정을 먼저 해서 Spark image 를 push 하고 테스트에 활용했었다. private docker registry 에 push 가 되어 있다면 어떤 image 를 사용해도 관계없어야 할 것이다.)

--

--