在minikube中部署私有Docker镜像仓库¶
备注
本文是早期在minikube中的一个模拟部署,现在看来还是很多地方没有解析清楚。我在完成 基于DNS轮询构建高可用Kubernetes 之后再次 Kubernetes部署registry仓库 (以此为准)
在生产环境中,不论是简单运行Docker容器还是部署Kubernetes集群,都需要使用私有镜像中心来实现中心化都镜像存储和分发:
定制私有镜像,不需要把镜像推送到公共镜像服务器,避免安全隐患
加速本地镜像的分发和容器部署
私有镜像仓库架构¶
准备工作¶
备注
部署需要使用Helm包管理器,分为两部分: 本地客户端安装Helm,远程Kubernetes集群安装Tiller。详细请参考 helm 。
安装Helm和Tiller¶
本地安装helm工具(linux版本)
wget https://get.helm.sh/helm-v2.14.1-linux-amd64.tar.gz tar -zxvf helm-v2.14.1-linux-amd64.tar.gz sudo mv linux-amd64/helm /usr/local/bin/helm
使用
kubectl config view
确认当前连接的是目标 kubeernetes 集群在Kubernetes集群安装服务端组件
tiller
kubectl -n kube-system create serviceaccount tiller kubectl create clusterrolebinding tiller --clusterrole cluster-admin --serviceaccount=kube-system:tiller helm init --service-account tiller
激活Nginx ingress controller¶
备注
本次部署采用的是 部署Nginx Ingress Controller ,所以激活Nginx ingress controller非常简单。
执行以下命令激活NGINX Ingress controller:
minikube addons enable ingress
备注
对于常规Kubernetes集群,可以使用以下 helm
命令安装 Nginx Ingress controller
helm install stable/nginx-ingress --name quickstart
验证NGINX ingress controller已经运行:
kubectl get pods -n kube-system
备注
确认在 kube-system
namespace中正确运行了 nginx-ingress-controller
在DNS provider中创建域名¶
首先需要创建一个域名解析记录指向Kubernetes Cluste,这个IP地址是
nginx-ingress-controller
服务的EXTERNAL-IP
kubectl get svc -n kube-system
备注
这个IP地址我理解是 nginx-ingress 输出的对外IP地址,但是这个IP地址和Node节点的IP地址应该是不绑定的,而是Kubernetes对外输出负载均衡的访问VIP。
待确认
安装 cert-manager
addon¶
构建Docker Registry需要具备一个TLS证书。这个工作可以结合Let’s Encrypt和 cert-manager
Kubernetes addon来完成。这个addon自动管理和发布TLS证书,确保证书周期更新。并且会在证书过期之前尝试renew。
安装
cert-manager
CRDs,这个步骤必须在Helm chart安装cert-manager之前完成:kubectl apply -f https://raw.githubusercontent.com/jetstack/cert-manager/release-0.8/deploy/manifests/00-crds.yaml
将标签添加到
kube-system
namespace (对于已经存在的namespace此步骤必须)kubectl label namespace kube-system certmanager.k8s.io/disable-validation="true"
添加 Jetstack Helm repository 到Helm,这个仓库包含了 cert-manager Helm chart
helm repo add jetstack https://charts.jetstack.io
更新 repo (如果已经存在)
helm repo update
最后安装 chart 到
kube-system
namespacehelm install \ --name cert-manager \ --namespace kube-system \ --version v0.8.1 \ jetstack/cert-manager
备注
详细的解释和遇到过的异常排查过程,请参考 部署Nginx Ingress Controller
获取TLS证书¶
通过 cert-manager 我们可以创建证书,创建以下文件 acme-issuer.yaml
apiVersion: certmanager.k8s.io/v1alpha1
kind: Issuer
metadata:
name: acme-issuer
spec:
acme:
email: huatai@huatai.me
server: https://acme-v02.api.letsencrypt.org/directory
privateKeySecretRef:
name: acme-issuer-account-key
http01: {}
Issuer表示一个证书通过签名的x509证书签发,例如Let’s Encrypt。这里需要设置ACME账号邮件地址,这个地址是从Let’s Encrypt获取过期通知和其他通知的。这允许在丢失证书私钥的时候回收证书。
创建Issuer:
kubectl apply -f acme-issuer.yaml
检查创建的Issuer:
kubectl describe issuer acme-issuer
备注
acme-issuer 申请如果遇到错误,在上述 describe issuer
中会显示错误信息。例如, How to run a Public Docker Registry in Kubernetes 原文使用的配置 server: https://acme-v01.api.letsencrypt.org/directory
现在已经不能使用,显示:
Message: Your ACME server URL is set to a v1 endpoint (https://acme-v01.api.letsencrypt.org/directory). You should update the spec.acme.server field to "https://acme-v02.api.letsencrypt.org/directory"
Reason: InvalidConfig
Status: False
需要修订成 https://acme-v02.api.letsencrypt.org/directory
重新执行。最终成功后显示:
Status:
Acme:
Uri: https://acme-v02.api.letsencrypt.org/acme/acct/59821427
Conditions:
Last Transition Time: 2019-06-23T08:35:08Z
Message: The ACME account was registered with the ACME server
Reason: ACMEAccountRegistered
Status: True
Type: Ready
Events: <none>
备注
对于域名验证,我们有两种选项:
http01 challenge
dns01 challenge
这里使用 http01 challenge机制因为它更简单。而Dns01 challenge需要进一步的DNS provideer来自动创建校验值的DNS记录。
创建
docker-registry-tls-certificate.yaml
配置:apiVersion: certmanager.k8s.io/v1alpha1 kind: Certificate metadata: name: docker-registry spec: secretName: docker-registry-tls-certificate issuerRef: name: acme-issuer dnsNames: - registry.xcloud.huatai.me acme: config: - http01: ingressClass: nginx domains: - registry.xcloud.huatai.me
备注
这里的证书定义基础含义
dnsNames
是Issuer使用的用于签发TLS证书secretName
是TLS获得以后的存储acme config
是域名校验使用(http01 challenge机制)
通过 HTTP-01 challenge 就可以通过一个在域名中的特定文件来提供域名的所有权证明。
通过执行上述
docker-registry-tls-certificate.yaml
,集群的 cert-manager-controller 会启动来验证证书:kubectl apply -f docker-registry-tls-certificate.yaml
检查证书的处理情况:
kubectl describe certificate docker-registry
当前面 issuer acme-issuer
就绪以后,cert-manager正确生成了自签名证书:
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning IssuerNotReady 7m33s (x3 over 62m) cert-manager Issuer acme-issuer not ready
Normal Generated 7m22s cert-manager Generated new private key
Normal GenerateSelfSigned 7m21s cert-manager Generated temporary self signed certificate
Normal OrderCreated 7m20s cert-manager Created Order resource "docker-registry-4118901981"
当所有工作就绪以后,就能获得自己的证书:
kubectl describe secret docker-registry-tls-certificate
设置htpasswd用于基本认证¶
对于Docker Registry基本认证,可以创建一个 htpasswd ,可以使用 apache-tuils 提供的 htpasswd 或者 docker registry contrainer。这里假设使用 admin 密码 admin123
docker run --entrypoint htpasswd --rm registry:2 -Bbn admin admin123 | base64
奇怪 每次执行上述指令获得的BASE64密文是不同的,待学习研究。
创建加上htpasswd的BASE64格式密文的
docker-registry-secret.yaml
apiVersion: v1 kind: Secret metadata: name: docker-registry type: Opaque data: HTPASSWD: YWRtaW46JDJ5JDA1JDI5Ri95VlNhbDJsQkoxNGI1VmRycHVkREdKanV6SXBxU1Q1RTZhdDVZQTdEdWtObEN2RmM2Cgo=
备注
docker run --entrypoint htpasswd --rm registry:2 -Bbn admin admin123 | base64
输出的是2行内容,这里我合在一行,待测试
备注
通过 htpasswd 命令可以不断添加新的用户名和密码,我们通过检查 htpasswd_file 文件中对应行,就可以获得我们所需要的加密后密文。这个密文将用于后续创建 Docker registry时候修订 chart_value.yaml
的密文部分。这部分验证我暂时跳过:
# 先安装 apache2-utils
sudo apt install apache2-utils -y
# 在 htpasswd_file 中添加用户名和密码
htpasswd -B htpasswd_file username
创建
secret/docker-registry
kubectl apply -f docker-registry-secret.yaml
配置Docker Registry¶
配置文件 config.yaml
定义如何挂载 安全文件到我们的Pod:
apiVersion: v1
kind: ConfigMap
metadata:
name: docker-registry
data:
registry-config.yml: |
version: 0.1
log:
fields:
service: registry
storage:
cache:
blobdescriptor: inmemory
filesystem:
rootdirectory: /var/lib/registry
http:
addr: :5000
headers:
X-Content-Type-Options: [nosniff]
auth:
htpasswd:
realm: basic-realm
path: /auth/htpasswd
health:
storagedriver:
enabled: true
interval: 10s
threshold: 3
备注
设置解析:
这里使用了ConfigMap中的存储配置 rootdirectory: /var/lib/docker/registry
是为了能够使用之前使用btrfs存储卷 /var/lib/docker
以便能够更好存储数据:
sudo mkdir /var/lib/docker/registry
执行创建
configmap/docker-registry
kubectl apply -f config.yaml
要验证是否正确:
kubectl get configmap docker-registry
Docker Registry Pod定义¶
定义Docker Registry Pod如下 docker-registry.yaml
apiVersion: v1
kind: Pod
metadata:
name: docker-registry
labels:
name: docker-registry
spec:
volumes:
- name: config
configMap:
name: docker-registry
items:
- key: registry-config.yml
path: config.yml
- name: htpasswd
secret:
secretName: docker-registry
items:
- key: HTPASSWD
path: htpasswd
- name: storage
emptyDir: {}
containers:
- name: docker-registry
image: registry
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 5000
protocol: TCP
volumeMounts:
- name: config
mountPath: /etc/docker/registry
readOnly: true
- name: htpasswd
mountPath: /auth
readOnly: true
- name: storage
mountPath: /var/lib/registry
执行创建Docker Registry Pod命令:
kubectl apply -f docker-registry.yaml
备注
这里 docker-registry.yaml
包含引用了 config.yaml
configMap:
name: docker-registry
items:
- key: registry-config.yml
path: config.yml
并且设置了 key
是 config.yaml
中的 registry-config.yml
部分(嵌套)
请使用 kubectl describe pod docker-registry
来检查创建过程是否报错,并相应做调整。例如,我发现有漏做创建 configmap/docker-registry
的异常。
备注
注意configMap类型卷中 mountPath
是从容器外映射到容器内的目录,而 configMap
则是该目录下的子目录映射。详细参考 Kubernetes存储卷
异常排查¶
kubectl describe pod docker-registry
显示Pod状态CrashLoopBackOff
docker-registry 0/1 CrashLoopBackOff 6 6m19s
通过 describe pod
可以看到最后的消息:
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
...
Warning BackOff 4m35s (x25 over 9m42s) kubelet, minikube Back-off restarting failed container
通过 kubectl get pod docker-registry -o yaml
显示:
message: Back-off 5m0s restarting failed container=docker-registry pod=docker-registry_default(f8a4eb4f-95cb-11e9-9327-b8e85633e48a)
reason: CrashLoopBackOff
通过Kubernetes 的 GUI dashboard检查,发现pod有log报错:
configuration error: open /etc/docker/registry/config.yml: no such file or directory
Usage:
registry serve <config> [flags]
Flags:
-h, --help=false: help for serve
Additional help topics:
这说明前面我的配置存在错误,原来我误以为需要修改的部分实际上是默认配置部分不能修改:
configMap:
name: docker-registry
items:
- key: registry-config.yml
path: config.yml
上述配置我推测应该是从 ConfigMap
中读取 registry-config.yml
注入到容器中成为 config.yml
,所以需要修正之前的 config.yaml
配置。
输出Docker Registry¶
创建一个服务以及相应的端口绑定
docker-registry-service.yaml
apiVersion: v1 kind: Service metadata: name: docker-registry spec: type: ClusterIP ports: - name: http protocol: TCP port: 5000 targetPort: 5000 selector: name: docker-registry
执行创建
docker-registry
服务:kubectl apply -f docker-registry-service.yaml
创建Ingress¶
最后(终于到了最后一步),创建 Ingress 配置
docker-registry-ingress.yaml
apiVersion: extensions/v1beta1 kind: Ingress metadata: name: docker-registry annotations: kubernetes.io/ingress.class: nginx nginx.ingress.kubernetes.io/proxy-body-size: "0" certmanager.k8s.io/issuer: acme-issuer spec: tls: - hosts: - registry.xcloud.huatai.me secretName: docker-registry-tls-certificate rules: - host: registry.xcloud.huatai.me http: paths: - backend: serviceName: docker-registry servicePort: 5000
执行创建
docker-registry
Ingress:kubectl apply -f docker-registry-ingress.yaml
验证¶
测试访问:
curl -u admin:admin123 https://registry.xcloud.huatai.me/v2/_catalog
登陆到私有仓库:
docker login https://registry.xcloud.huatai.me -u admin -p admin123
备注
报错:
WARNING! Using --password via the CLI is insecure. Use --password-stdin.
Error response from daemon: Get https://registry.xcloud.huatai.me/v2/: x509: certificate signed by unknown authority
参考 access private registry: x509: certificate signed by unknown authority #8849 可以采用 --insecure-registry
参数来用于私有没有自签名证书都镜像仓库:
docker --insecure-registry=registry.xcloud.huatai.me login https://registry.xcloud.huatai.me -u admin -p admin123
不过,在新版本 docker 中,不能直接使用这个客户端参数,而是要修改docker启动脚本添加运行参数。例如,对于 CentOS 7.x 修改 /usr/lib/systemd/system/docker.service
,对于 Debian/Ubuntu 则修改 /lib/systemd/system/docker.service
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
修改成:
ExecStart=/usr/bin/dockerd --insecure-registry=registry.xcloud.huatai.me -H fd:// --containerd=/run/containerd/containerd.sock
然后重新加载配置修改,并重启docker:
sudo systemctl daemon-reload
sudo systemctl restart docker
对于不使用systemd的Debian/Ubuntu,可以通过修改 /etc/default/docker
(对于 CentOS/RHEL 则修改 /etc/sysconfig/docker
)
# Use DOCKER_OPTS to modify the daemon startup options.
#DOCKER_OPTS="--dns 8.8.8.8 --dns 8.8.4.4"
DOCKER_OPTS="--insecure-registry=registry.xcloud.huatai.me -H fd:// --containerd=/run/containerd/containerd.sock"
这里遇到一个问题,发现 minikube 的大量 docker 容器crash了,并且minikube无法启动(实际应该和修改无关,因为发现去除参数也无法启动minikube),最后参考 Sorry that minikube crashed. #4137 重新安装了一遍minikube。
另外,可以参考 Docker Private Registry: x509: certificate signed by unknown authority 注册主机的证书方法,还没有测试该方法。
测试推送一个docker镜像:
docker pull busybox:latest docker tag busybox:latest registry.xcloud.huatai.me/busybox:latest docker push registry.xcloud.huatai.me/busybox:latest
WEB UI¶
What are the best docker image private registries? 提供了多种docker镜像仓库管理平台的对比,可以选择进行部署。待实践…