问题描述
由于Kubernetes默认情况下只开放30000-32767这些端口,但是docker-registry在部署在Kubernetes中的时候conrainerd访问就会出现端口不匹配的问题,主要原因是containerd默认情况下HTTPS方式的访问端口为443,但是按照Kubernetes默认的设置,docker-registry在部署的时候对外暴露的端口在30000-32767之间,因此要么修改Kubernetes默认的端口开启443端口,要么让containerd在访问HTTPS的默认端口为30000-32767之间。
解决方案
一. 修改Kubernetes默认端口范围
#解决方法为修改apiserver的启动参数
vim /etc/kubernetes/manifests/kube-apiserver.yaml
# 添加如下配置
- --service-node-port-range=1-65535
上述配置为将默认端口都开放,但是这种解决方案存在安全性问题,因此不推荐使用。
二. 修改containerd源码使得HTTPS默认端口为指定值
1. 在Kubernetes中部署docker registry的时候向外暴露的端口设置为30443(也可以设置其他值)
部署过程看我另外一篇文章:
docker-registry.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: docker-registry
namespace: default
spec:
selector:
matchLabels:
app: docker-registry
spec:
nodeSelector:
kubernetes.io/hostname: master1
containers:
- name: docker-registry
image: docker.io/library/registry:2.7.1
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8443
env:
- name: REGISTRY_HTTP_ADDR
value: "0.0.0.0:8443"
- name: REGISTRY_HTTP_TLS_CERTIFICATE
value: "/certs/Yuan.crt"
- name: REGISTRY_HTTP_TLS_KEY
value: "/certs/Yuan.key"
- name: REGISTRY_AUTH
value: "htpasswd"
- name: REGISTRY_AUTH_HTPASSWD_PATH
value: "/auth/htpasswd"
- name: REGISTRY_AUTH_HTPASSWD_REALM
value: "Registry Realm"
volumeMounts:
- name: reg-data
mountPath: /vat/lib/registry
- name: reg-auth
mountPath: /auth
- name: reg-certs
mountPath: /certs
volumes:
- name: reg-data
hostPath:
path: /home/docker-registry/images
- name: reg-auth
hostPath:
path: /home/docker-registry/auth
- name: reg-certs
hostPath:
path: /home/docker-registry/certs
---
apiVersion: v1
kind: Service
metadata:
name: docker-registry
namespace: default
spec:
type: NodePort
ports:
- port: 8443
targetPort: 8443
nodePort: 30443
protocol: TCP
selector:
app: docker-registry
# 部署
$ kubectl apply -f docker-registry.yaml
# 查看Pod
$ kubectl get po
# 查看svc,这里就可以看到docker registry对外暴露的端口号为30443
$ kubectl get svc
2. 尝试拉取镜像
通过命令:
# 修改containerd的配置文件中的端口号
$ vim /etc/containerd/config.toml
endpoint = ["https://10.131.82.53:30443"]
# 可以拉取到镜像的命令
ctictl pull Yuan.com/nginx:v1 --creds Yuan:Abcd123456
ctr i pull Yuan.com:30443/nginx:v1 --user Yuan:Abcd123456
# 不能拉取镜像的命令
ctictl pull Yuan.com:30443/nginx:v1 --creds Yuan:Abcd123456
ctr i pull Yuan.com/nginx:v1 --user Yuan:Abcd123456
通过yaml部署文件
pod-pull-test.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-pull-test
spec:
nodeSelector:
kubernetes.io/hostname: master1
containers:
- name: pod-pull-test
image: Yuan.com/nginx:v1
imagePullPolicy: Always
部署测试
$ kubectl apply -f pod-pull-test.yaml
$ kubectl describe po pull-pod
failed to do request: Head "https://Yuan.com:443/v2/nginx:v1"
# 通过以上输出信息可以看出containerd在拉取镜像的时候还是走的443端口,因此拉取镜像失败
# 同时可以推断出来Kubernetes在拉取镜像的时候的命令与`ctr i pull Yuan.com/nginx:v1 --user Yuan:Abcd123456`效果类似
修改yaml文件再次拉取
pod-pull-test.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-pull-test
spec:
nodeSelector:
kubernetes.io/hostname: master1
containers:
- name: pod-pull-test
image: Yuan.com:30443/nginx:v1
imagePullPolicy: Always
部署测试
$ kubectl apply -f pod-pull-test.yaml
$ kubectl describe po pull-pod
failed to do request: Head "https://Yuan.com:443/v2/nginx:v1" x509: certificate signed by unknown authority
# 通过以上输出信息可以看出containerd在拉取镜像的时候走的还是443端口
# 同时还报了证书签名的问题导致镜像拉取失败
3.修改containerd源码HTTPS默认端口号
# 在containerd源码目录下面执行如下命令
$ grep -rwnI "443"
# 找到于Kubernetes的相关的代码,路径如下
containerd/vendor/k8s.io/apimachinery/third_party/forked/golang/netutil/adr.go:16: https: 443
# 将这一行修改为30443后编译部署containerd
4.修改证书信息
cd certs
# 生成2048位的私钥,也可以生成4096位的,看自己需求
openssl genrsa -out Yuan.key 2048
# 生成证书请求文件
openssl req -new -key Yuan.key -subj "/CN=Yuan.com" -out Yuan.csr
# 将DNS地址写入一个文件,为了解决上述所说的那个问题
echo subjectAltName = DNS:Yuan.com>extfile.cnf
# 将IP地址写入一个文件,为了解决上述所说的那个问题
echo subjectAltName = IP:10.131.82.54>>extfile.cnf
# 这里的ca我就使用了集群自带的,在/etc/kubernetes/pki/下
openssl x509 -req -in Yuan.csr -CA ca.crt -CAkey ca.key -CAcreateserial -extfile extfile.cnf -out Yuan.crt -days 5000
# 查看证书信息
openssl x509 -in Yuan.crt -noout -text
# 将生存的证书信息追加到系统的证书管理文件后
cat Yuan.crt >> /etc/pki/tls/certs/ca-bundle.crt
上述IP地址修改为你部署docker registry机器的IP地址
修改yaml文件再次拉取
pod-pull-test.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-pull-test
spec:
nodeSelector:
kubernetes.io/hostname: master1
containers:
- name: pod-pull-test
image: Yuan.com/nginx:v1
imagePullPolicy: Always
部署测试
$ kubectl apply -f pod-pull-test.yaml
$ kubectl get po
# 通过上面的命令就可以看到pod成功拉起了
总结
为了符合Kubernetes的端口要求并且可以通过containerd成功部署业务镜像就必须要修改containerd的HTTPS默认端口号,在生成docker reigstry证书的时候需要将IP信息和DNS信息写入才能成功拉取到镜像,这样修改完的好处是不用修改Kubernetes的相关默认配置,在私有仓库拉取镜像的时候不用指定docker registry服务的端口号。