前
第N次,和 K3S打上了交道。因为K8S的技术的学习曲线较为陡峭。也因为自己基础不牢。 基础部署都没熟练直攻Helm。
然后导致集群就处于失控状态,从而导致了最终的学习/研究失败。
现在重新捡起来,使用基础的例子一步步的构建K3S homelab体系。
最终的目的是可以把目前所有的 docker-compose 的项目都迁移到K3S下面来。
Workshop 说明
需求背景
因为国内的网络环境问题,拉取Docker的gcr/dockerio 之类的镜像时有失败。而且因为集群的多节点。导致 ImagePullErr
问题时有发生。
所以这里需要建立起一个 在本地提供镜像缓存以及管理功能的 Registry。
选型
官方的Registry镜像中提供了一个 remote_porxy
的配置,可以直接实现对某一个远程仓库的 代理和 缓存功能。
不过这里有一个进行二次打包的版本 yangchuansheng/registry-proxy 把一些配置直接写到了环境变量中去。简化了我们的使用流程。
在有提供 代理服务的 仓库镜像之后,还需要一个管理工具,用来对我们的镜像进行可视化的webui管理。这里找到了下面的那这个项目
可以直接进行部署用来Registry的资源管理。
实践过程
持久化存储
因为需要进行镜像的缓存,所以对于拉去的镜像需要进行持久化存储。这里原计划是使用NFS来提供存储,不过这种基础的服务不希望降低系统的整体性。所以就在Master 的节点拓展了磁盘。直接进行磁盘的localpath存储。
多个的镜像代理都是公用的一个PV存储,这样便于管理且不会冲突。下面的yaml就是对这个 PV 的定义
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: master-local-storage
spec:
accessModes:
- ReadWriteOnce
- ReadWriteMany
capacity:
storage: 15Gi
local:
path: /data
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: node-role.kubernetes.io/storage
operator: In
values:
- storage
persistentVolumeReclaimPolicy: Retain
storageClassName: local-path
volumeMode: Filesystem
这里需要注意的是两点,其中一个是 persistentVolumeReclaimPolicy
Reclaim Policy
Current reclaim policies are:
- Retain — manual reclamation
- Recycle — basic scrub (
rm -rf /thevolume/*
)- Delete — associated storage asset such as AWS EBS or GCE PD volume is deleted
Currently, only NFS and HostPath support recycling. AWS EBS and GCE PD volumes support deletion.
另一个是 nodeAffinity,通过下面的scheme 来进行。
小技巧使用 kubectl explain svc –recursive 来进行API的递归列出
或者使用 api reference 来进行查询 Kubernetes API/Config and Storage Resources/PersistentVolume
rms@k3s-master:~$ kubectl explain PersistentVolume.spec.nodeAffinity --recursive
KIND: PersistentVolume
VERSION: v1
FIELD: nodeAffinity <VolumeNodeAffinity>
DESCRIPTION:
nodeAffinity defines constraints that limit what nodes this volume can be
accessed from. This field influences the scheduling of pods that use this
volume.
VolumeNodeAffinity defines constraints that limit what nodes this volume can
be accessed from.
FIELDS:
required <NodeSelector>
nodeSelectorTerms <[]NodeSelectorTerm> -required-
matchExpressions <[]NodeSelectorRequirement>
key <string> -required-
operator <string> -required-
values <[]string>
matchFields <[]NodeSelectorRequirement>
key <string> -required-
operator <string> -required-
values <[]string>
PVC 对PV 来进行 Claim,以提供给POD 来进行使用。
apiVersion: v1
kind: Namespace
metadata:
name: container-basis
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: registry-pvc
namespace: container-basis
spec:
accessModes:
- ReadWriteOnce
storageClassName: local-path
volumeMode: Filesystem
volumeName: master-local-storage
resources:
requests:
storage: 10Gi
多个站点的Proxy Deploy部署&UI 部署
这里简单的就是一个 Deployment 的部署方式,这里的yaml 定义了Proxy 的Deploy ,配置都是比较浅显易懂的。 这个是根据官方进行进行二次打包的镜像。其中的一些配置值可以直接通过环境变量来直接传入。
其中需要注意的是,Resources.limits 是推荐进行设置的否则存在容器对资源的过度使用的问题。
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: k8s-gcr-registry
namespace: container-basis
labels:
app: k8s-gcr-registry
spec:
replicas: 1
selector:
matchLabels:
app: k8s-gcr-registry
template:
metadata:
labels:
app: k8s-gcr-registry
spec:
containers:
- name: k8s-gcr-registry
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
memory: "512Mi"
cpu: "500m"
image: yangchuansheng/registry-proxy:latest
ports:
- containerPort: 5000
protocol: TCP
volumeMounts:
- name: local
mountPath: /hub
env:
- name: REGISTRY_HTTP_ADDR
value: :5000
- name: REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY
value: /hub
- name: PROXY_REMOTE_URL
value: https://k8s.gcr.io
volumes:
- name: local
persistentVolumeClaim:
claimName: registry-pvc
关于多个缓存站点的定义,其yaml文件的大多数都是相同的。其具体的差异在Env 的部分。比如下面这个就是代理Gcr 的配置 。
env:
- name: REGISTRY_HTTP_ADDR
value: :5000
- name: REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY
value: /hub
- name: PROXY_REMOTE_URL
value: https://gcr.io
Service 以及 Ingress
在Registry运行之后,就需要考虑到前面的接入部份,定义servic和对应的ingress 来作为七层的路由转发。从而实现对集群外提供访问。具体的配置文件如下。
service的几个类型,不指定默认是 ClusterIP 的模式。
---
apiVersion: v1
kind: Service
metadata:
name: k8s-gcr-registry-svc
namespace: container-basis
labels:
run: k8s-gcr-registry
spec:
selector:
app: k8s-gcr-registry
ports:
- protocol: TCP
port: 5000
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: registry-ingress-k8s-gcr
namespace: container-basis
annotations:
kubernetes.io/ingress.class: "traefik"
spec:
rules:
- host: k8s-gcr-k3s.io
http:
paths:
- path: /
pathType: ImplementationSpecific
backend:
service:
name: k8s-gcr-registry-svc
port:
number: 5000
代理接入配置
在前面的部分已经完成了Registry 的基础部署,后面就是使用上的配置。这里直接使用 ansible来进行配置文件的部署。
先写Host到所有的节点中,用来劫持几个镜像域名的指向到我们自建的registry的IP。
然后重启节点服务来进行应用生效
For K3s
- hosts: k3s
remote_user: ep
become: yes
gather_facts: False
vars_prompt:
- name: "registryIP"
prompt: "Please input Registry Server IP"
private: no
tasks:
- name: add hosts
when: registryIP is defined
shell: |
sudo sed -i '/^.*hub-k3s\.io.*/d' /etc/hosts
sudo echo "{{ registryIP }} hub-k3s.io gcr-k3s.io k8s-gcr-k3s.io" >> /etc/hosts
- name: add remote private registry
when: registryIP is defined
shell: |
ls -als /etc/rancher/k3s/registries.yaml > /dev/null 2>&1 || sudo mkdir /etc/rancher/k3s/
sudo echo > /etc/rancher/k3s/registries.yaml
sudo cat <<EOT >> /etc/rancher/k3s/registries.yaml
mirrors:
docker.io:
endpoint:
- "http://hub-k3s.io"
gcr.io:
endpoint:
- "http://gcr-k3s.io"
k8s.gcr.io:
endpoint:
- "http://k8s-gcr-k3s.io"
EOT
- hosts: k3s-master
remote_user: ep
become: yes
tasks:
- name: restart server
shell: sudo systemctl restart k3s
- hosts: k3s-workers
remote_user: ep
become: yes
tasks:
- name: restart workers
shell: sudo systemctl restart k3s-agent
For Docker
普通的Docker服务直接修改 /etc/docker/daemon.json
加上下面的配置。因为Docker 默认的修改的只有 Dockerio 的地址。
"registry-mirrors": ["http://hub-k3s.io"],
"insecure-registries" : ["http://hub-k3s.io"]
对于其他的镜像仓库的域名。需要使用自定义的域名来代替代理前的域名。
后
一步步的来,在体系树上一片片叶子的补全