赞
踩
前情提要:轻量级开源 docker私服管理工具部署实践
之前有做过registry-admin用docker-compose进行部署的实践,在此基础之上,今日进行该项目在k8s上部署的实践,以便于有需要的同学门参考。
至于如何搭建k8s集群,这部分网上有很详尽的说明,我在这里就不再写了,有需要在单开一章
我编写了部署文件,通过kubectl apply -f ${部署文件}.yaml来进行部署即可,所以接下来主要介绍一下部署文件的各部分内容。
定义一个registry-admin资源归属的命名空间,没啥特别可说的
# # registry-admin 部署文件 # 查看全部资源 # kubectl get svc,deploy,pod,configmaps,pv,pvc -n registry-admin # 部署后测试 # registry-admin container curl http://$podip:5000/v2/_catalog # registry container curl http://$podip:5080 # registry-admin svc http://$registry-admin-svcip # registry svc curl http://$registry-svcip # apiVersion: v1 kind: Namespace #命名空间 metadata: name: registry-admin ---
这里我使用的事本地存储,storageClass用的是racher版的local-path-provisioner实现
官方地址
相比于静态的hostPath或local volume优势是可以支持“动态申请”,“自动回收”
具体安装较为简单,网上有不少详细说明,我就不赘述了,有需要的话再补充,特别说明的是:
rancher的local-path-provisioner 支持2种回收策略
reclaimPolicy: Delete(容器销毁自动删除存储)默认
reclaimPolicy: Retain (容器销毁不自动删除,手动处理)
其本身也是k8s部署的,修改部署yaml进行容器发布即可,建议可以安装2个PV使用不同的回收策略,以便于在不同的应用场景中使用。我就安装了2个PV
storageClass: local-path-retain (保留)
storageClass:local-path (自动清除)
此处我使用的是默认策略的local-path
#声明存储使用 #local存储 storage_class:local-path-provisioner(rancher) apiVersion: v1 kind: PersistentVolumeClaim metadata: namespace: registry-admin name: local-path-pvc spec: accessModes: - ReadWriteOnce #本地存储只支持ReadWriteOnce storageClassName: local-path #local-path: 容器删除,存储local-path动态删除,local-path-retain: 容器删除,存储local-path保留,local-path|local-path-retain为手动安装的storageclass resources: requests: storage: 10Gi #声明最少要使用存储空间,不足则无法创建 Gi=G Mi=M #persistentVolumeReclaimPolicy: Delete # PVC 回收策略 Retain 保留| Delete 清除 | PV: local-path-provisioner(rancher) 不支持设置该属性 ---
我们用到2个容器
regisrty-admin:管理界面应用
registry:私服应用
为了能够通过服务名称访问而不是绑定ip,因此我们也需要对应创建2个service,配置service的name分别为:regisrty-admin,registry,这样在k8s集群内部就可以通过http://regisrty-admin与http://registry访问,而我们实际使用时,是从k8s集群之外的节点发起请求(比如自己用的笔记本电脑),因此还需要给2个service 暴露nodePort 以便通过k8s-nodeip:nodeport访问应用界面以及进行私服仓库的pull与push。
如下配置发布成功后,我们便可以通过
http://k8s-node:30580 访问管理界面
http://k8s-node:30500 访问私服的api
kind: Service apiVersion: v1 metadata: #自定义标签属性列表 labels: k8s-app: registry-admin name: registry-admin namespace: registry-admin #自定义注解属性列表 annotations: desc : registry-admin http服务访问入口 spec: type: NodePort ports: - port: 80 targetPort: 5080 nodePort: 30580 selector: # abel selector配置,将选择具有label标签的Pod作为管理 k8s-app: registry-admin status: loadBalancer: {} #当spce.type=LoadBalancer时,设置外部负载均衡器的地址 #status: # loadBalancer: #外部负载均衡器 # ingress: #外部负载均衡器 # ip: string #外部负载均衡器的Ip地址值 # hostname: string #外部负载均衡器的主机名 --- kind: Service apiVersion: v1 metadata: #自定义标签属性列表 labels: k8s-app: registry-admin name: registry namespace: registry-admin #自定义注解属性列表 annotations: desc : registry-admin http服务访问入口 spec: type: NodePort ports: - port: 80 targetPort: 5000 nodePort: 30500 selector: # abel selector配置,将选择具有label标签的Pod作为管理 k8s-app: registry-admin status: loadBalancer: {} ---
还是2个配置,1个是registry-admin的,1个是registry的,进行basic验证的文件.htpasswd不需要提前创建了,在使用中发现registry-admin会自动创建,只需要通过容器的volumeMounts让2个容器共享这个部分存储即可(在接下来的deployment配置中会有)
特别说明: ${ base64 encode string } 这个值是个base64的加密串,其原始值为admin:{basic-ra-config.yml中定义的registry.password}
apiVersion: v1 kind: ConfigMap #配置信息 metadata: name: registry-admin-config namespace: registry-admin data: basic-ra-config.yml: | hostname: 127.0.0.1 port: 5080 registry: host: http://registry port: 80 auth_type: basic htpasswd: /access/.htpasswd login: admin password: $password gc_interval: 20 store: type: embed admin_password: $password embed: path: /app/data/store.db logger: enabled: true filename: /app/log/access.log # mount the directory to a docker host folder for get access for fail2ban max_size: 10M max_backups: 3 --- apiVersion: v1 kind: ConfigMap #配置信息 metadata: name: registry-config namespace: registry-admin data: config.yml: | version: 0.1 log: accesslog: disabled: false level: info formatter: text fields: service: registry storage: filesystem: rootdirectory: /registry/lib maxthreads: 100 delete: enabled: true http: addr: ":5000" net: tcp auth: htpasswd: realm: basic-realm path: /access/.htpasswd notifications: events: includereferences: true endpoints: - name: ra-listener disabled: false url: http://registry-admin/api/v1/registry/events headers: Authorization: [ Basic ${base64 encode string} ] # 'admin:$password' base64 encode string timeout: 1s threshold: 5 backoff: 3s ignoredmediatypes: - application/octet-stream ignore: mediatypes: - application/octet-stream ---
以下为核心2个StatefulSet的定义,其中要特别说明的有:
- name: registry-admin-volume #挂载共享文件,同个pod内容器可以同时读写
mountPath: /access
subPath: ./access
registry-admin 作者并未实现分布式session,因此应用只能单点跑,replicas=1
registry-admin-app 设置了环境变量,除了指定应用文件的位置外(RA_CONFIG_FILE),还设定了APP_UID 让其使用root用户启动(经尝试配置privileged: true也不行,必须通过应用环境变量设定),最后添加 POD_NAME环境变量,如果启动多个副本(这里我只部署了1个),可以把日志写入到各自目录中,这也是同个deployment运行多个副本时,避免写串日志的常用方式。
env: #环境变量配置
- name: RA_CONFIG_FILE # key-value 配置
value: /app/config/basic-ra-config.yml
- name: APP_UID #指定使用root用户启动registry-admin 否则无法将Rest bind 80端口
value: "0"
- name: POD_NAME
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.name
在上面配置了POD_NAME的基础之上,通过subPathExpr,动态指定日志目录的物理存放位置
- name: registry-admin-volume
mountPath: /app/log
#subPath: ./registry-admin/log
subPathExpr: log/$(POD_NAME)/ #使用$(POD_NAME) 挂载动态目录
- name: host-time #挂载本地时区
mountPath: /etc/localtime
readOnly: true
name: host-time
hostPath: #指向到当前node本地时区配置
path: /etc/localtime
type: ""
值得注意的是即便执行了垃圾回收,通过 http://registry/v2/_catalog 查看,仍然能看到被删除的镜像目录(目录是空的),好像必须手动删除才行(我感觉官方压根就不想让你好好用免费的私服,想让你买收费服务。。。。)
lifecycle: postStart: #通过postStart 钩子函数做初始化|如果失败会重启容器 exec: command: #在钩子函数中command也支持多行命令只是不支持args - "sh" - "-c" - > # 设置registry垃圾回收器的定时任务,清理日志的定时任务并启动调度服务 echo '#!/bin/sh' > /etc/periodic/15min/registry-gc; echo 'echo $(date +%Y%m%d_%H%M%S) registry" gc start " >> /var/log/registry-gc.log' >> /etc/periodic/15min/registry-gc; echo 'registry garbage-collect /etc/docker/registry/config.yml' >> /etc/periodic/15min/registry-gc; echo 'echo $(date +%Y%m%d_%H%M%S) registry" gc executed " >> /var/log/registry-gc.log' >> /etc/periodic/15min/registry-gc; echo '#!/bin/sh' > /etc/periodic/weekly/clean-log; echo 'cat /dev/null > /var/log/registry-gc.log' >> /etc/periodic/weekly/clean-log; echo 'cat /dev/null > /var/log/cron.log' >> /etc/periodic/weekly/clean-log; chmod a+x /etc/periodic/15min/registry-gc; chmod a+x /etc/periodic/weekly/clean-log; crond -L /var/log/cron.log
以下为deployment部署的完整片段
apiVersion: apps/v1 kind: StatefulSet # Deployment | StatefulSet | DaemonSet | JobSet metadata: name: registry-admin-sts namespace: registry-admin spec: replicas: 1 #运行副本数 selector: matchLabels: k8s-app: registry-admin #与下方template节点中的 labels 保持一致 revisionHistoryLimit: 10 #设定保留最近的几个revision 用于回滚,默认10 #serviceName: "nginx-headless" #设置绑定的service,以支持内部dns访问 <pod-name>.<svc-name>.<namespace>.svc.cluster.local updateStrategy: #更新策略 type: RollingUpdate # RollingUpdate (滚动更新) | OnDelete (删除时更新) rollingUpdate: #maxSurge: 1 #[Deployment]支持-升级过程中可以启动超过原先设置的POD数量的上限:数量 或 百分比 1 | 20% #maxUnavailable: 1 #[Deployment]支持-升级过程中无法提供服务的POD数量的上限:数量 或 百分比 1 | 20%,最好与maxSurge保持一致,这样能确保更新过程中的服务能力不会下降 partition: 0 #灰度发布控制器,每次只更新部署的pod序号 >= partition的pod,如果有5个pod[0-4],0=更新所有,4=更新1pod,3=更新2pod template: metadata: labels: k8s-app: registry-admin spec: restartPolicy: Always #默认即为 Always | OnFailure | Never terminationGracePeriodSeconds: 30 #容器被删除变为Terminating状态的等待时间,默认是30s,以便于做一些容器删除前的处理工作 containers: - name: registry-app image: registry:2.8.3 imagePullPolicy: IfNotPresent securityContext: ###添加参数启用容器root权限 privileged: true ports: - containerPort: 5000 protocol: TCP #requests: #cpu: "300m" # 容器所需cpu资源 | 可以超过 单位milicpu,500mcpu=0.5cpu #memory: "64Mi" # 容器所需内存资源 | 可以超过;但如果超过,容器可能会在Node内存不足时清理 单位则包括E, P, T, G, M, K, Ei, Pi, Ti, Gi, Mi, Ki等 #limits: #cpu: "500m" # 容器cpu上限 | 可以短暂超过,容器也不会被停止 #memory: "128Mi" # 容器内存上限 | 不可以超过;如果超过,容器可能会被停止或调度到其他资源充足的机器上 #command: ["/bin/sh","-c"] #添加registry垃圾回收定时任务,并启动系统定时调度服务 #args: #可以设置多行命令,不过启动后初始化还是推荐使用postStart钩子函数来执行 #- | #registry垃圾回收器的定时任务 #echo '#!/bin/sh' > /etc/periodic/15min/registry-gc #echo 'echo $(date +%Y%m%d_%H%M%S) registry" gc start " >> /var/log/registry-gc.log' >> /etc/periodic/15min/registry-gc #echo 'registry garbage-collect /etc/docker/registry/config.yml' >> /etc/periodic/15min/registry-gc #echo 'echo $(date +%Y%m%d_%H%M%S) registry" gc executed " >> /var/log/registry-gc.log' >> /etc/periodic/15min/registry-gc #清理日志的定时任务 #echo '#!/bin/sh' > /etc/periodic/weekly/clean-log #echo 'cat /dev/null > /var/log/registry-gc.log' >> /etc/periodic/weekly/clean-log #echo 'cat /dev/null > /var/log/cron.log' >> /etc/periodic/weekly/clean-log #chmod a+x /etc/periodic/15min/registry-gc #chmod a+x /etc/periodic/weekly/clean-log #crond -L /var/log/cron.log #registry serve /etc/docker/registry/config.yml lifecycle: postStart: #通过postStart 钩子函数做初始化|如果失败会重启容器 exec: command: #在钩子函数中command也支持多行命令只是不支持args - "sh" - "-c" - > # 设置registry垃圾回收器的定时任务,清理日志的定时任务并启动调度服务 echo '#!/bin/sh' > /etc/periodic/15min/registry-gc; echo 'echo $(date +%Y%m%d_%H%M%S) registry" gc start " >> /var/log/registry-gc.log' >> /etc/periodic/15min/registry-gc; echo 'registry garbage-collect /etc/docker/registry/config.yml' >> /etc/periodic/15min/registry-gc; echo 'echo $(date +%Y%m%d_%H%M%S) registry" gc executed " >> /var/log/registry-gc.log' >> /etc/periodic/15min/registry-gc; echo '#!/bin/sh' > /etc/periodic/weekly/clean-log; echo 'cat /dev/null > /var/log/registry-gc.log' >> /etc/periodic/weekly/clean-log; echo 'cat /dev/null > /var/log/cron.log' >> /etc/periodic/weekly/clean-log; chmod a+x /etc/periodic/15min/registry-gc; chmod a+x /etc/periodic/weekly/clean-log; crond -L /var/log/cron.log; volumeMounts: - name: registry-config mountPath: /etc/docker/registry/ #subPath: /registry-admin/conf #通过subpath指定子目录控制数据区分与共享 - name: registry-admin-volume mountPath: /certs subPath: ./certs - name: registry-admin-volume #挂载共享文件,同个pod内容器可以同时读写 mountPath: /access subPath: ./access - name: registry-admin-volume mountPath: /registry/lib subPath: ./registry/lib/ - name: host-time #挂载本地时区 mountPath: /etc/localtime readOnly: true #env: #环境变量配置 #- name: RA_CONFIG_FILE # key-value 配置 # value: /app/config/basic-ra-config.yml #valueFrom: #fieldRef: #fieldPath: metadata.namespace # 以当前行容器运行参数信息为数据来源 - name: registry-admin-app image: zebox/registry-admin:latest #image: registry:80/registry-admin:latest #使用私服 #imagePullSecrets: #私服认证信息 #- name: myregistrykey #私服账号secret资源名称,需要单独创建:kubectl create secret generic... 详见:https://kubernetes.io/zh-cn/docs/tasks/configure-pod-container/pull-image-private-registry/ imagePullPolicy: IfNotPresent # IfNotPresent | Always | Never securityContext: ###添加参数启用容器root权限 privileged: true ports: - containerPort: 5080 protocol: TCP #requests: #cpu: "300m" # 容器所需cpu资源 | 可以超过 单位milicpu,100mcpu=0.1cpu(0.1核) #memory: "64Mi" # 容器所需内存资源 | 可以超过;但如果超过,容器可能会在Node内存不足时清理 单位则包括E, P, T, G, M, K, Ei, Pi, Ti, Gi, Mi, Ki等 #limits: #cpu: "500m" # 容器cpu上限 | 可以短暂超过,容器也不会被停止 #memory: "128Mi" # 容器内存上限 | 不可以超过;如果超过,容器可能会被停止或调度到其他资源充足的机器上 #command: #- /bin/sh #- -c #- chown -R 1001:1001 /app #startupProbe: #容器启动探针,失败自动重启 结果Success:表示通过检测| Failure:表示未通过检测|Unknown:表示检测没有正常进行。 #exec: #支持3种方式 httpGet http url | tcpSocket tcp 端口建连 | exec 执行命令 # command: # - cat # - /tmp/healthy #initialDelaySeconds: 0 #容器启动后要等待多少秒后就探针开始工作,单位“秒”,默认是 0 秒,最小值是 0 #periodSeconds: 10 #执行探测的时间间隔(单位是秒),默认为 10s,单位“秒”,最小值是 1 #timeoutSeconds: 2 #探针执行检测请求后,等待响应的超时时间,默认为 1s,单位“秒”,最小值是 1 #successThreshold: 1 #探针检测失败后认为成功的最小连接成功次数,默认为 1s,在 Liveness 探针中必须为 1,最小值为 1。 #failureThreshold: 3 #探测失败的重试次数,重试一定次数后将认为失败,在 readiness 探针中,Pod会被标记为未就绪,默认为 3,最小值为 1 #livenessProbe: #容器存活探针,失败自动重启 #httpGet: #host: #要连接的主机名,默认为Pod IP,可以在http request head中设置host头部。 #port: 80 #容器上要访问端口号或名称. * #path: /health #http服务器上的访问URI。* #scheme: http #用于连接host的协议,默认为HTTP。 #httpHeaders: #自定义HTTP请求headers,HTTP允许重复headers。 #- name: Custom-Header # value: Awesome #initialDelaySeconds: 30 #periodSeconds: 10 #timeoutSeconds: 2 #successThreshold: 1 #failureThreshold: 3 #readinessProbe: #容器就绪探针,失败不容器,容器不会ready,一般有startup与liveness就够了 #tcpSocket: #port: 8080 #initialDelaySeconds: 10 #periodSeconds: 10 #timeoutSeconds: 2 #successThreshold: 1 #failureThreshold: 3 volumeMounts: - name: registry-admin-config mountPath: /app/config #通过configMap 挂载配置文件(只读) #subPath: /registry-admin/conf #通过subpath指定子目录控制数据区分与共享 - name: registry-admin-volume mountPath: /certs subPath: ./certs - name: registry-admin-volume #挂载共享文件,同个pod内容器可以同时读写 mountPath: /access subPath: ./access - name: registry-admin-volume mountPath: /app/data subPath: ./registry-admin/data - name: registry-admin-volume mountPath: /app/log #subPath: ./registry-admin/log subPathExpr: log/$(POD_NAME)/ #使用$(POD_NAME) 挂载动态目录 - name: host-time #挂载本地时区 mountPath: /etc/localtime readOnly: true env: #环境变量配置 - name: RA_CONFIG_FILE # key-value 配置 value: /app/config/basic-ra-config.yml - name: APP_UID #指定使用root用户启动registry-admin 否则无法将Rest bind 80端口 value: "0" #- name: RA_REGISTRY_GC_INTERVAL # value: "15" - name: POD_NAME valueFrom: fieldRef: apiVersion: v1 fieldPath: metadata.name #valueFrom: #fieldRef: #fieldPath: metadata.namespace # 以当前行容器运行参数信息为数据来源 volumes: - name: registry-admin-config configMap: #使用configMap name: registry-admin-config - name: registry-config configMap: #使用configMap name: registry-config - name: registry-admin-volume #使用pvc persistentVolumeClaim: claimName: local-path-pvc - name: host-time hostPath: #挂载本地时区 path: /etc/localtime type: "" ---
部署应用
kubectl apply -f ${部署描述文件).yaml
卸载应用 (按默认配置会自动回收存储空间)
kubectl delete -f ${部署描述文件).yaml
查看部署情况
kubectl get deploy,pod,svc,pv,pvc,configmap -n registry-admin(命名空间) -o wide
进入容器
kubectl exec ${pod-name} -it -n registry-admin(命名空间) -c $容器名称(registry-app或者registry-admin-app) sh
部署成功后可以通过
http://k8s-node:30580 访问管理界面
http://k8s-node:30500 访问私服的api
我额外做了tengine配置,用80做了方向代理,建议大家也配置一下,后续使用更方便。
完整部署文件我已通过资源绑定上传,通过文件顶部下载即可。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。