轻量级开源 docker私服管理工具registry-admin【进阶】 k8s部署
轻量化开源 docker私服 registry 管理工具 registry-admin 如何在k8s集群中进行部署
前情提要:轻量级开源 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
---
定义存储PVC
这里我使用的事本地存储,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) 不支持设置该属性
---
定义service
我们用到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
---
StatefulSet定义
以下为核心2个StatefulSet的定义,其中要特别说明的有:
- 2个容器同时挂载了/access目录,并指向同个物理存储目录,以便于共享.htpasswd文件,这种处理方式也是常用的共享文件使用方式,主要是由registry-admin控制账号,而2者启动后都会自动往.htpasswd中初始化用户,因此一定要按照先registry,再registry-admin的顺序启动容器。
- 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) 挂载动态目录
- k8s 容器默认会使用utc时间,为了让其使用本地时区的时间设置,需要挂载本地时区的配置文件
- name: host-time #挂载本地时区
mountPath: /etc/localtime
readOnly: true
name: host-time
hostPath: #指向到当前node本地时区配置
path: /etc/localtime
type: ""
- 添加registry垃圾回收定时任务并启动定时调度
在实际使用中发现,通过registry-admin删除已上传的镜像后,registry中的仓库存储空间没有变化,一开始以为是registry-admin的bug,经过查看源代码发现并不是,实际registry-admin通过调用registry的delete api完成的删除,是没问题的。然后查阅docker registry官网发现,按照官方设计,再删除后比手动指定垃圾回收才会删除物理文件(让我有点无语),即执行:
registry garbage-collect /etc/docker/registry/config.yml
为此我在registry容器启动后,通过lifecycle的postStart钩子函数,创建垃圾回收的定时任务,日志清除的定期任务,并启动crond调度服务(对,没错,官方的镜像实际连系统调度服务都不会自动起!!!),以下为配置片段。
值得注意的是即便执行了垃圾回收,通过 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做了方向代理,建议大家也配置一下,后续使用更方便。
完整部署文件我已通过资源绑定上传,通过文件顶部下载即可。
更多推荐
所有评论(0)