问题描述

由于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服务的端口号。