K8S 笔记

Kubernetes 笔记

初始化

kubeadm config images list 下载必要的镜像

k8s集群搭建(ubuntu 20.04 + k8s 1.28.3 + calico + containerd1.7.8)-CSDN博客

K8S集群搭建(多master多node节点)_多主节点-CSDN博客

准备工作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# 关闭swap
swapoff -a && sed -i '/swap/ s/^\(.*\)$/#\1/' /etc/fstab

# 将containerd配置成systemd服务,方便使用systemctl命令管理
mkdir -p /usr/local/lib/systemd/system/ ; cd /usr/local/lib/systemd/system/
curl -s -o containerd.service http://raw.githubusercontent.com/containerd/containerd/main/containerd.service
这里要检查一下/usr/local/lib/systemd/system/containerd.service 是否有内容,如果没有直接手动复制进去:
----------------------------------------------------------------------------------------------------------------
# Copyright The containerd Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

[Unit]
Description=containerd container runtime
Documentation=https://containerd.io
After=network.target local-fs.target

[Service]
ExecStartPre=-/sbin/modprobe overlay
ExecStart=/usr/local/bin/containerd

Type=notify
Delegate=yes
KillMode=process
Restart=always
RestartSec=5

# Having non-zero Limit*s causes performance problems due to accounting overhead
# in the kernel. We recommend using cgroups to do container-local accounting.
LimitNPROC=infinity
LimitCORE=infinity

# Comment TasksMax if your systemd version does not supports it.
# Only systemd 226 and above support this version.
TasksMax=infinity
OOMScoreAdjust=-999

[Install]
WantedBy=multi-user.target

----------------------------------------------------------------------------------------------------------------
# 加载配置文件
systemctl daemon-reload
# 设置开启自启并立即启动
systemctl enable --now containerd

# 配置containerd的config.toml
mkdir /etc/containerd
containerd config default > /etc/containerd/config.toml

# 修改sandbox_image
vim /etc/containerd/config.toml
"registry.aliyuncs.com/google_containers/pause:3.9"

# 修改"SystemdCgroup"【重要,需要与kubelet的cGroupDriver匹配】
SystemdCgroup = true # 原来是false

# 重启containerd
sudo systemctl restart containerd

ps: 该步骤是基于已有虚拟机环境上进行的,其他的要从头开始,具体看博客https://blog.csdn.net/zpsimon/article/details/134388092

正式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
kubeadm init --kubernetes-version=v1.29.0 --pod-network-cidr=10.222.0.0/16 --apiserver-advertise-address=192.168.66.142 --image-repository registry.aliyuncs.com/google_containers

--apiserver-advertise-address 集群通告地址
--image-repository 由于默认拉取镜像地址k8s.gcr.io国内无法访问,这里指定阿里云镜像仓库地址
--kubernetes-version K8s版本,与上面安装的一致
--service-cidr 集群内部虚拟网络,Pod统一访问入口
--pod-network-cidr Pod网络,与下面部署的CNI网络组件yaml中保持一致

kubeadm join 192.168.66.142:6443 --token u0l6v1.2w4ttig5vi5mm2ss \
--discovery-token-ca-cert-hash sha256:52a38ec9f3d98f6a1f77bc6eb47be3e0435e2f921a0cd5444ef17b8c31c1d1d

kubeadm token create --print-join-command # 重新获取令牌

kubectl get cm -n kube-system kubeadm-config -o yaml | grep Subnet
# 查看一下子网

mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config

export KUBECONFIG=/etc/kubernetes/admin.conf

# 注意,在这一步之前要注意是否每个加入的节点都正确了配置了containerd中的config文件中的sandbox镜像是否为阿里云的,否则默认是拉不下来的,会一直pending导致calico配置失败
安装Calico
wget https://docs.projectcalico.org/manifests/calico.yaml --no-check-certificate
vim +4434 calico.yaml
...
- name: CALICO_IPV4POOL_CIDR
value: "10.222.0.0/16"
...
# value值来源于kubeadm init时指定的 --pod-network-cidr 参数
kubectl apply -f calico.yaml
{
如果报错说权限不足:policy "calico-kube-controllers" is forbidden: User "
export KUBECONFIG=/root/.kube/config
chmod g-r /root/.kube/config
kubectl apply -f calico.yaml

安装calicoctl
curl -L https://github.com/projectcalico/calico/releases/download/v3.27.0/calicoctl-linux-amd64 -o kubectl-calico
chmod +x kubectl-calico
mv kubectl-calico /usr/bin/
kubectl calico -h
kubectl calico node status # 查看calico状态

[watch] kubectl get pod -n kube-system
kubectl get node
这里要看到master节点为ready才进行下一步

加入master节点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
kubectl edit cm kubeadm-config -n kube-system

在kubernetesVersion: v1.29.0下面添加

controlPlaneEndpoint: 192.168.66.141 # masterIP

master节点:kubeadm init phase upload-certs --upload-certs 得到certificate-key

master02(待加入节点):
mkdir -p /etc/kubernetes/pki/etcd

master01:
scp /etc/kubernetes/pki/ca.* master02:/etc/kubernetes/pki/
scp /etc/kubernetes/pki/sa.* master02:/etc/kubernetes/pki/
scp /etc/kubernetes/pki/front-proxy-ca.* master02:/etc/kubernetes/pki/
scp /etc/kubernetes/pki/etcd/ca.* master02:/etc/kubernetes/pki/etcd/
scp /etc/kubernetes/admin.conf master02:/etc/kubernetes/

kubeadm join 192.168.66.141:6443 --token abcdef.0123456789abcdef \
--discovery-token-ca-cert-hash sha256:2c515ec7729dbb45a58681e37479c902a470468eaf67a00a60a31445f268f243 --control-plane --certificate-key 64dcd61237d1e61180d42b939b85e69d548965a04fa2954e9cb52d2ce12ff5e8

常用命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
kubectl kubectl label [node/pod] {name} {key}={value} ... [--overwrite=true] # 打标签,可以直接多个key=value,要覆盖的话必须加overwrite
# 驱逐节点:1、先将节点驱逐:2、 删除节点
1、 kubectl drain {nfsserver} --delete-emptydir-data --force --ignore-daemonsets
2、 k delete node {nfsserver}

kubectl describe -n pro pod kps-kube-prometheus-stack-admission-create-l7tnj | grep "Controlled By" # 查看pod被谁控制
kubectl get nodes # 查看所有节点
kubectl get pods -A # 查看所有pods
kubectl get svc -n {ns} # 查看节点的ip
kubectl describe nodes {node_name} | grep Taints # 查看被污染的节点
kubectl taint node {master01} {node-role.kubernetes.io/control-plane}- # 删除某个污点
kubectl taint node master01 node-role.kubernetes.io/control-plane-
kubeadm token create --print-join-command # 重新生成Token
kubectl get pods -n {namespace} -o wide # 查看对应ns的pods,用-o wide显示ip等信息
journalctl -u kubelet # 查看kubelet服务的日志
systemctl status kubelet
kubectl config current-context # 查看当前用户身份和集群
kubectl get cs # 查看健康状态
kubectl describe pod {pod_name} -n {ns} # 查看某个ns的pod
kubectl logs {pod_name} --all-containers -n {ns} # 查看某个ns的pod的日志(一定要指定ns)
kubectl logs kubernetes-dashboard-api-8586787f7-hjh2j --all-containers -n kubernetes-dashboard
kubectl cluster-info # 查看集群信息
kubectl top nodes
kubectl top pods -n {ns}
kubectl get nodes -L {key[,key2,key3]} # 显示所有node的key字段(有或者没有都会显示,不筛选,相当于多一列),用逗号分割显示更多
kubectl get nodes -l {key[!=或者=]value} "[{key} in (xxx, xxx2)]" # 指定标签选择,用in进行范围选择
kubectl create ns {ns} # 创建命名空间
kubectl delete ns {ns} # 删除命名空间,其所含的资源都会被删除
kubectl delete -f {yaml} # 删除yaml文件
kubectl get svc
kubectl describe svc {svc_name}
kubectl get endpoints -n {ns} # 查看endpoints
kubectl run {NAME} --image={image} # 直接命令行运行一个pod
kubectl exec {pod_name} -c {container_name} -- {cmd} # 直接在某个pod里运行命令
kubectl exec -it {pod_name} -- sh # 进入交互式界面
kubectl get [replicaset/rs]
kubectl get [deployment/deploy.apps]
kubectl delete deploy {deployment_name} # 删除deploy
kubectl set image deployment {deployment_name} {container_name}={image_name} --record # 更新
kubectl rollout history deploy {deployment_name} # 查看更新历史
kubectl rollout status deploy {deployment_name} # 查看状态
kubectl rollout history deploy {deployment_name} --revision=1 # 查看指定版本
kubectl rollout undo deploy {deployment_name} --to-revision=1 # 回退到指定版本
kubectl scale deploy/{deployment_name} --replicas=2 # 扩容
kubectl delete pod {pod_name} --grace-period=0 --force # 强制删除pod
kubectl expose deployment.apps {svc_name} --type=NodePort --target-port={pod_port} --port={svc_port} # 手动生成svc
kubectl get endpoints # 直接查看svc暴露的端口
dig -t a headless-svc.default.svc.cluster.local. @10.96.0.10 # headless服务类型的访问
kubectl run -it bbp --image=busyboxplus:curl # 直接命令创建一个pod
kubectl create service clusterip my-svc --dry-run=server -o yaml > xxx.yaml # -o yaml --dry-run=server 不运行只是创建yaml,模拟测试用
k create configmap cm1 --from-literal=host=127.0.0.1 --from-literal port=3306 # 创建cm
k create cm cm2 --from-file ./host --from-file ./port # 文件名是key,内容是value
k create cm cm3 --from-env-file env.txt # 把键值对写入到env.txt中
k apply -f cm4.yaml # 通过资源清单文件的方式写入
apiVersion: v1
kind: ConfigMap
metadata:
name: cm4
data:
host: 127.0.0.1
port: "3306"
k create secret docker-registry harbor-secret --docker-server=192.168.66.23 --docker-username=admin --docker-password=12345
k get secrets harbor-secret -o yaml | grep ".dockerconfigjson:" | awk '{print $2}' | base64 --decode

NoSchedule:如果一个pod没有声明容忍这个Taint,则系统不会把该Pod调度到有这个Taint的node上

NoExecute:定义pod的驱逐行为,以应对节点故障。

NoExecute上面提到的污点会影响节点上已经运行的Pod,如下所示:

  • 立即将不能忍受的污点逐出
  • 容忍污点但未定义tolerationSecondsPod将永远绑定
  • 可以忍受指定污点的Pod在指定的时间内保持绑定。当某些条件为真时,节点控制器会自动为节点添加污点。内置以下污点:

node.kubernetes.io/not-ready:节点尚未准备好。这对应于NodeConditionReady为False。

node.kubernetes.io/unreachable:无法从节点控制器访问节点。这对应于NodeConditionReadyUnknown

node.kubernetes.io/out-of-disk:节点磁盘不足。

node.kubernetes.io/memory-pressure:节点有内存压力。

node.kubernetes.io/disk-pressure:节点有磁盘压力。

node.kubernetes.io/network-unavailable:节点的网络不可用。

node.kubernetes.io/unschedulable:节点不可调度。

node.cloudprovider.kubernetes.io/uninitialized:当kubelet从外部云服务提供程序启动时,在节点上设置此污点以将其标记为不可用。来自cloud-controller-manager的控制器初始化此节点后,kubelet删除此污点。

如果要逐出节点,则节点控制器或kubelet会添加相关的污点NoExecute。如果故障情况恢复正常,则kubelet或节点控制器可以删除相关的污点。具体文档地址,如下所示:https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/

当配置资源不足时的处理方式

使用 kubelet 配置资源不足时的处理方式。

驱逐阈值
kubelet支持指定驱逐阈值,用于触发 kubelet 回收资源。

每个阈值形式如下:

[eviction-signal][operator][quantity]

合法的 eviction-signal 标志如上所示。
operator 是所需的关系运算符,例如 <。
quantity 是驱逐阈值值标志,例如 1Gi。合法的标志必须匹配 Kubernetes 使用的数量表示。 驱逐阈值也可以使用 % 标记表示百分比。
举例说明,如果一个节点有 10Gi 内存,希望在可用内存下降到 1Gi 以下时引起驱逐操作, 则驱逐阈值可以使用下面任意一种方式指定(但不是两者同时)。

memory.available<10%
memory.available<1Gi

1
2
3
4
5
vim /etc/systemd/system/kubelet.service.d/10-kubeadm.conf
修改配置文件增加传参数,添加此配置项
--eviction-hard=nodefs.available<3%
systemctl daemon-reload
systemctl restart kubelet

问题

1、 kubernetes初始化时报错:CRI v1 runtime API is not implemented for endpoint “unix:///var/run/containerd/con

kubernetes初始化时报错:CRI v1 runtime API is not implemented for endpoint “unix:///var/run/containerd/con_greenbeam的技术博客_51CTO博客

1
2
3
vim /etc/containerd/config.toml
将其中disabled_plugins = [“cri”]注释掉
systemctl restart containerd

2、 detected that the sandbox image “registry.k8s.io/pause:3.6”:

1
2
3
vim /etc/containerd/config.toml
registry.k8s.io/pause:3.6 修改为阿里云的镜像: registry.aliyuncs.com/google_containers/pause
sudo systemctl restart containerd

3、 /etc/containerd/config.toml文件内容很少? 没有初始化

1
2
sudo containerd config default > $HOME/config.toml
sudo cp $HOME/config.toml /etc/containerd/config.toml

4、k8s提示certificate signed by unknown authority (possibly because of “crypto/rsa: verification error” while trying to verify candidate authority certificate “kubernetes”

1
2
3
4
5
6
7
8
9
10
原因1:这是之前建过集群了,删除集群后,在重新创建集群之前,原来集群的rm -rf $HOME/.kube文件没有删除,所以导致了认证失去作用。
主要是要对新加入集群的节点完成以下的处理
1、删除这个路径下的文件
rm -rf $HOME/.kube
2、重新执行命令
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config

k8s.dockerproxy.com/ingress-nginx/kube-webhook-certgen:v20230407@sha256:543c40fd093964bc9ab509d3e791f9989963021f1e9e4c9c7b6700b02bfb227b

5、 重启之后连不上集群或重新初始化集群

1
2
3
sudo kubeadm reset
rm -rf /var/lib/cni/ && rm -f $HOME/.kube/config
systemctl daemon-reload && systemctl restart kubelet

6、 Unable to connect to the server: x509: certificate signed by unknown authority (possibly because of “crypto/rsa: verification error” while trying to verify candidate authority certificate “kubernetes”)

1
2
3
4
5
6
# 没配置好,如果没有admin.conf,则要去master节点去取过来
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
kubectl get nodes
export KUBECONFIG=/etc/kubernetes/kubelet.conf
kubectl get nodes

7、 NetworkPluginNotReady message:Network plugin returns error: cni plugin not initialized”

1
systemctl restart containerd.service

8、crictl ps 命令报错

报错信息:

1
2
3
4
WARN[0000] runtime connect using default endpoints: [unix:///var/run/dockershim.sock unix:///run/containerd/containerd.sock unix:///run/crio/crio.sock unix:///var/run/cri-dockerd.sock]. As the default settings are now deprecated, you should set the endpoint instead.
WARN[0000] image connect using default endpoints: [unix:///var/run/dockershim.sock unix:///run/containerd/containerd.sock unix:///run/crio/crio.sock unix:///var/run/cri-dockerd.sock]. As the default settings are now deprecated, you should set the endpoint instead.
E1216 20:44:27.090767 201856 remote_runtime.go:390] "ListContainers with filter from runtime service failed" err="rpc error: code = Unavailable desc = connection error: desc = \"transport: Error while dialing dial unix /var/run/dockershim.sock: connect: no such file or directory\"" filter="&ContainerFilter{Id:,State:&ContainerStateValue{State:CONTAINER_RUNNING,},PodSandboxId:,LabelSelector:map[string]string{},}"
FATA[0000] listing containers: rpc error: code = Unavailable desc = connection error: desc = "transport: Error while dialing dial unix /var/run/dockershim.sock: connect: no such file or directory"

解决:(感觉是设置了代理的原因)

1
2
crictl config runtime-endpoint unix:///run/containerd/containerd.sock
crictl config image-endpoint unix:///run/containerd/containerd.sock

9、pod启动失败

1
2
3
4
5
6
1、 先查看pod描述
kubectl describe pod {pod_name} -n {ns}
不行的话去查看容器日志
crictl ps -a
crictl logs {容器id}
或者直接 kubectl logs -n {ns} {pod_name} --all-containers

10、Calico一直CrashLoopBackOff

calico的CrashLoopBackOff 解决办法_calico crashloopbackoff-CSDN博客

1
2
3
4
5
vim /etc/sysctl.conf
取消下面的注释
net.ipv4.conf.all.rp_filter=1
net.ipv4.ip_forward=1
sudo sysctl --system

11、Calico rpc报错

1
2
3
ailed to create pod sandbox: rpc error: code = Unknown desc = failed to setup network for sandbox "6e9135e75078773ce140f0de52404e22f9d3b4a9e07054b5a61392bb5447e863": plugin type="calico" failed (add): error getting ClusterInformation: connection is unauthorized: Unauthorized

或者caliconode is not ready: BIRD is not ready BGP not established with

解决方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
重新在calico.yaml文件中加入下面这段
- name: CALICO_NETWORKING_BACKEND
valueFrom:
configMapKeyRef:
name: calico-config
key: calico_backend
# Cluster type to identify the deployment type
- name: IP_AUTODETECTION_METHOD
value: "interface=ens33" #eth33是本地网卡名称

在配置IPV4下面加
- name: CALICO_IPV4POOL_CIDR
value: "10.224.0.0/16"

kubectl apply -f calico.yaml # 重新配置一下

12. ontainerd.service: Failed at step EXEC spawning /usr/local/bin/containerd:

1
2
3
如果 containerd 被安装在一个不同的位置,你可以创建一个到 /usr/local/bin/containerd 的符号链接。例如,如果 containerd 实际安装在 /usr/bin/containerd,你可以使用以下命令:

sudo ln -s /usr/bin/containerd /usr/local/bin/containerd

K8S拉取镜像的代理

这个镜像代理服务,帮您在 K8S 中愉快地拉取国外镜像 - 知乎 (zhihu.com)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Docker Hub 官方镜像代理
常规镜像代理
stilleshan/frpc:latest => http://dockerproxy.com/stilleshan/frpc:latest
根镜像代理
nginx:latest => http://dockerproxy.com/library/nginx:latest
GitHub Container Registry
常规镜像代理
http://ghcr.io/username/image:tag => http://ghcr.dockerproxy.com/username/image:tag
Google Container Registry
常规镜像代理
http://gcr.io/username/image:tag => http://gcr.dockerproxy.com/username/image:tag
Google Kubernetes
常规镜像代理
http://k8s.gcr.io/username/image:tag => http://k8s.dockerproxy.com/username/image:tag
http://registry.k8s.io/username/image:tag => http://k8s.dockerproxy.com/username/image:tag
根镜像代理
http://k8s.gcr.io/coredns:1.6.5 => http://k8s.dockerproxy.com/coredns:1.6.5
http://registry.k8s.io/coredns:1.6.5 => http://k8s.dockerproxy.com/coredns:1.6.5
http://Quay.io
常规镜像代理
http://quay.io/username/image:tag => http://quay.dockerproxy.com/username/image:tag
Microsoft Artifact Registry
常规镜像代理
http://mcr.microsoft.com/azure-cognitive-services/diagnostic:latest => http://mcr.dockerproxy.com/azu

在containerd中配置

1
2
3
4
vim /etc/containerd/config.toml
[plugins."io.containerd.grpc.v1.cri".registry.mirrors]
[plugins."io.containerd.grpc.v1.cri".registry.mirrors."registry.k8s.io"]
endpoint = ["k8s.dockerproxy.com"]

Kubernetes 环境中切换代理ipvs模式

service代理默认使用iptables规则通过内核模块netfilter实现流量转发,内核转发效率高,但是iptables不具备更为灵活负载均衡策略,只是将流量随意的转发至后端Pod,当Pod不可用时也无法进行健康检查;就以下是将默认流量转发修改为ipvs。

  • kube-proxy ipvs和iptables的异同

相同点:ipvs和iptables都是通过netfitle内核进行转发。
异同点:iptables只是为防火墙设计的,IPtable只是防火墙,而ipvs是专门用于高性能负载均衡的,并使用更高效的数据结构,如hash表并支持索引。
ipvs与iptables相比较,其优势为:
(1)ipvs为大型集群提供了更好的可扩展性和性能
(2)ipvs支持比iptables更复杂的负载均衡算法,如rr、wrr、lc、wlc
(3)ipvs支持服务健康检查和链接重试等功能
(4)ipvs可以动态修改ipset集合
目前大多数的k8s版本的kube-proxy都是默认使用iptables模式,为什么呢,因为无奈,因为主要是用户安装的Linux操作系统
和k8s集群,没有办法让用户安装ipvs,ipvs需要安装自己的模块。正是因为ipvs的高效性能,所以,将kube-proxy的模式切换为ipvs是很有必要的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
1、 install ipset ipvsadm
2、 首先创建文件/etc/sysconfig/modules/ipvs.modules 并写入以下内容
#!/bin/bash
modprobe -- ip_vs
modprobe -- ip_vs_rr
modprobe -- ip_vs_wrr
modprobe -- ip_vs_sh
modprobe -- nf_conntrack
3、 然后,将配置文件设置为可执行,并运行它,以加载所需的内核模块:
chmod 755 /etc/sysconfig/modules/ipvs.modules
bash /etc/sysconfig/modules/ipvs.modules
4、lsmod | grep -e ip_vs -e nf_conntrack # 查看是否加载成功
5、修改kube-proxy的comfigmaps文件
kubectl get configmaps -n kube-system kube-proxy
kubectl edit cm -n kube-system kube-proxy

mode: "ipvs" # 默认mode: ""为空,使用iptables
ipvs:
strictARP: true # 之前为false

因为kube-prooxy是用daemonsets控制器创建的,直接删除kube-proxy的Pod,让他重建就可以了
(或者直接kubectl rollout restart ds kube-proxy -n kube-system
kubectl get pod --show-labels -n kube-system | grep kube-proxy # 查看一下有哪些
kubectl delete pod -l k8s-app=kube-proxy -n kube-system # 删除之前的pod名称
kubectl get pod --show-labels -n kube-system | grep kube-proxy #删除后发现已经重建完成并运行
验证IPVS功能
kubectl logs kube-proxy-6nglx -n kube-system | grep ipvs
ipvsadm -Ln| grep {本地ip}

Dashboard

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
wget https://raw.githubusercontent.com/kubernetes/dashboard/v2.7.0/aio/deploy/recommended.yaml
在spec下面加一个type:NodePort
加一个nodePort: 30001

----
spec:
ports:
- port: 443
targetPort: 8443
nodePort: 30001
type: NodePort
selector:
k8s-app: kubernetes-dashboard
----
kubectl apply -f dashborad.yaml
kubectl get pods,svc -n kubernetes-dashboard
要通过https:// 去访问,不能用http

创建用户:
wget https://raw.githubusercontent.com/cby-chen/Kubernetes/main/yaml/dashboard-user.yaml
kubectl apply -f dashboard-user.yaml
创建token
kubectl -n kubernetes-dashboard create token admin-user

Metric-Kubernetes

Releases · kubernetes-sigs/metrics-server (github.com)

1
2
3
4
5
6
7
8
9
10
11
12
wget https://github.com/kubernetes-sigs/metrics-server/releases/download/v0.6.4/components.yaml
修改140行
- args:
- --cert-dir=/tmp
- --secure-port=4443
- --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname
- --kubelet-use-node-status-port
- --metric-resolution=15s
- --kubelet-insecure-tls # 加这一行,不然没有证书无法登陆
image: registry.aliyuncs.com/google_containers/metrics-server:v0.6.4 # 不然要使用代理
# 要开启vpn才能拉取镜像
kubectl apply -f components.yaml

yaml 文件详解(pod、deployment、service)

Kubernetes 支持 YAML 和 JSON格式 管理资源对象

  • JSON 格式:主要用于 api 接口之间消息的传递
  • YAML 格式:用于配置和管理,YAML是一种简洁的非标记性语言,内容格式人性化,较易读。

YAML语法格式:

  • 大小写敏感;
  • 使用缩进表示层级关系;不支持Tab键制表符缩进,只使用空格缩进;
  • 缩进的空格数目不重要,只要相同层级的元素左侧对齐即可,通常开头缩进两个空格;
  • 字符后缩进一个空格,如冒号,逗号,短横杆(-) 等
  • "---" 表示YAML格式,一个文件的开始,用于分隔文件; 可以将创建多个资源写在同一个 yaml 文件中,用 -- 隔开,就不用写多个 yaml 文件了。
  • “#” 表示注释;

yaml 文件的学习方法:

  • 多看别人(官方)写的,能读懂
  • 能照着现场的文件改着用
  • 遇到不懂的,善用 kubectl explain ...命令查.

deployment资源

简述

Deployment 为 Pod 和 ReplicaSet 提供了一个声明式定义 (declarative) 方法,用来替代以前的 ReplicationController 更方便的管理应用。

作为最常用的 Kubernetes 对象,Deployment 经常会用来创建 ReplicaSet 和 Pod,我们往往不会直接在集群中使用 ReplicaSet 部署一个新的微服务,一方面是因为 ReplicaSet 的功能其实不够强大,一些常见的更新、扩容和缩容运维操作都不支持,Deployment 的引入就是为了支持这些复杂的操作。

典型用例如下:

  • 使用 Deployment 来创建 ReplicaSet。ReplicaSet 在后台创建 pod。检查启动状态,看它是成功还是失败。
  • 然后,通过更新 Deployment 的 PodTemplateSpec 字段来声明 Pod 的新状态。这会创建一个新的 ReplicaSet,Deployment 会按照控制的速率将 pod 从旧的 ReplicaSet 移动到新的 ReplicaSet 中。
  • 如果当前状态不稳定,回滚到之前的 Deployment revision。每次回滚都会更新 Deployment 的 revision。
  • 扩容 Deployment 以满足更高的负载。
  • 暂停 Deployment 来应用 PodTemplateSpec 的多个修复,然后恢复上线。
  • 根据 Deployment 的状态判断上线是否 hang 住了。
  • 清除旧的不必要的 ReplicaSet。

deployment 原理

  • 控制器模型

在Kubernetes架构中,有一个叫做kube-controller-manager的组件。这个组件,是一系列控制器的集合。其中每一个控制器,都以独有的方式负责某种编排功能。而Deployment正是这些控制器中的一种。它们都遵循Kubernetes中一个通用的编排模式,即:控制循环

用一段go语言伪代码,描述这个控制循环

1
2
3
4
5
6
7
8
9
for {
实际状态 := 获取集群中对象X的实际状态
期望状态 := 获取集群中对象X的期望状态
if 实际状态 == 期望状态 {
什么都不做
}else{
执行编排动作,将实际状态调整为期望状态
}
}

在具体实现中,实际状态往往来自于Kubernetes集群本身。比如Kubelet通过心跳汇报的容器状态和节点状态,或者监控系统中保存的应用监控数据,或者控制器主动收集的它感兴趣的信息,这些都是常见的实际状态的来源;期望状态一般来自用户提交的YAML文件,这些信息都保存在Etcd中

对于Deployment,它的控制器简单实现如下:

  1. Deployment Controller从Etcd中获取到所有携带 “app:nginx”标签的Pod,然后统计它们的数量,这就是实际状态
  2. Deployment对象的replicas的值就是期望状态
  3. Deployment Controller将两个状态做比较,然后根据比较结果,确定是创建Pod,还是删除已有Pod
  • 滚动更新

Deployment滚动更新的实现,依赖的是Kubernetes中的ReplicaSet

Deployment控制器实际操纵的,就是Replicas对象,而不是Pod对象。对于Deployment、ReplicaSet、Pod它们的关系如下图:

ReplicaSet负责通过“控制器模式”,保证系统中Pod的个数永远等于指定的个数。这也正是Deployment只允许容器的restartPolicy=Always的主要原因:只有容器能保证自己始终是running状态的前提下,ReplicaSet调整Pod的个数才有意义。

Deployment同样通过控制器模式,操作ReplicaSet的个数和属性,进而实现“水平扩展/收缩”和“滚动更新”两个编排动作对于“水平扩展/收缩”的实现,Deployment Controller只需要修改replicas的值即可。用户执行这个操作的指令如下:

1
kubectl scale deployment nginx-deployment --replicas=4

Deployment.yaml 文件解析

Deployment yaml文件包含四个部分:

  • apiVersion: 表示版本。版本查看命令:kubectl api-versions
  • kind: 表示资源
  • metadata: 表示元信息
  • spec: 资源规范字段

Deployment yaml 详解:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
apiVersion: apps/v1        # 指定api版本,此值必须在kubectl api-versions中。业务场景一般首选”apps/v1“
kind: Deployment # 指定创建资源的角色/类型
metadata: # 资源的元数据/属性
name: demo # 资源的名字,在同一个namespace中必须唯一
namespace: default # 部署在哪个namespace中。不指定时默认为default命名空间
labels: # 自定义资源的标签
app: demo
version: stable
annotations: # 自定义注释列表
name: string
spec: # 资源规范字段,定义deployment资源需要的参数属性,诸如是否在容器失败时重新启动容器的属性
replicas: 1 # 声明副本数目
revisionHistoryLimit: 3 # 保留历史版本
selector: # 标签选择器
matchLabels: # 匹配标签,需与上面的标签定义的app保持一致
app: demo
version: stable
strategy: # 策略
type: RollingUpdate # 滚动更新策略
# ecreate:删除所有已存在的pod,重新创建新的
# RollingUpdate:滚动升级,逐步替换的策略,同时滚动升级时,支持更多的附加参数,
# 例如设置最大不可用pod数量,最小升级间隔时间等等
rollingUpdate: # 滚动更新
maxSurge: 1 # 滚动升级时最大额外可以存在的副本数,可以为百分比,也可以为整数
maxUnavailable: 0 # 在更新过程中进入不可用状态的 Pod 的最大值,可以为百分比,也可以为整数
template: # 定义业务模板,如果有多个副本,所有副本的属性会按照模板的相关配置进行匹配
metadata: # 资源的元数据/属性
annotations: # 自定义注解列表
sidecar.istio.io/inject: "false" # 自定义注解名字
labels: # 自定义资源的标签
app: demo # 模板名称必填
version: stable
spec: # 资源规范字段
restartPolicy: Always # Pod的重启策略。[Always | OnFailure | Nerver]
# Always :在任何情况下,只要容器不在运行状态,就自动重启容器。默认
# OnFailure :只在容器异常时才自动容器容器。
# 对于包含多个容器的pod,只有它里面所有的容器都进入异常状态后,pod才会进入Failed状态
# Nerver :从来不重启容器
nodeSelector: # 将该Pod调度到包含这个label的node上,仅能基于简单的等值关系定义标签选择器
caas_cluster: work-node
containers: # Pod中容器列表
- name: demo # 容器的名字
image: demo:v1 # 容器使用的镜像地址
imagePullPolicy: IfNotPresent # 每次Pod启动拉取镜像策略
# IfNotPresent :如果本地有就不检查,如果没有就拉取。默认
# Always : 每次都尝试重新拉取镜像
# Never : 每次都不检查(不管本地是否有),仅使用本地镜像
command: [string] # 容器的启动命令列表,如不指定,使用打包时使用的启动命令
args: [string] # 容器的启动命令参数列表
# 如果command和args均没有写,那么用Docker默认的配置
# 如果command写了,但args没有写,那么Docker默认的配置会被忽略而且仅仅执行.yaml文件的command(不带任何参数的)
# 如果command没写,但args写了,那么Docker默认配置的ENTRYPOINT的命令行会被执行,但是调用的参数是.yaml中的args
# 如果如果command和args都写了,那么Docker默认的配置被忽略,使用.yaml的配置
workingDir: string # 容器的工作目录
volumeMounts: # 挂载到容器内部的存储卷配置
- name: string # 引用pod定义的共享存储卷的名称,需用volumes[]部分定义的的卷名
mountPath: string # 存储卷在容器内mount的绝对路径,应少于512字符
readOnly: boolean # 是否为只读模式
- name: string
configMap: # 类型为configMap的存储卷,挂载预定义的configMap对象到容器内部
name: string
items:
- key: string
path: string
ports: # 需要暴露的端口库号列表
- name: http # 端口号名称
containerPort: 8080 # 容器开放对外的端口
# hostPort: 8080 # 容器所在主机需要监听的端口号,默认与Container相同
protocol: TCP # 端口协议,支持TCP和UDP,默认TCP
env: # 容器运行前需设置的环境变量列表
- name: string # 环境变量名称
value: string # 环境变量的值
resources: # 资源管理。资源限制和请求的设置
limits: # 资源限制的设置,最大使用
cpu: "1" # CPU,"1"(1核心) = 1000m。将用于docker run --cpu-shares参数
memory: 500Mi # 内存,1G = 1024Mi。将用于docker run --memory参数
requests: # 资源请求的设置。容器运行时,最低资源需求,也就是说最少需要多少资源容器才能正常运行
cpu: 100m
memory: 100Mi
livenessProbe: # pod内部的容器的健康检查的设置。当探测无响应几次后将自动重启该容器
# 检查方法有exec、httpGet和tcpSocket,对一个容器只需设置其中一种方法即可
httpGet: # 通过httpget检查健康,返回200-399之间,则认为容器正常
path: /healthCheck # URI地址。如果没有心跳检测接口就为/
port: 8089 # 端口
scheme: HTTP # 协议
# host: 127.0.0.1 # 主机地址
# 也可以用这两种方法进行pod内容器的健康检查
# exec: # 在容器内执行任意命令,并检查命令退出状态码,如果状态码为0,则探测成功,否则探测失败容器重启
# command:
# - cat
# - /tmp/health
# 也可以用这种方法
# tcpSocket: # 对Pod内容器健康检查方式设置为tcpSocket方式
# port: number
initialDelaySeconds: 30 # 容器启动完成后首次探测的时间,单位为秒
timeoutSeconds: 5 # 对容器健康检查等待响应的超时时间,单位秒,默认1秒
periodSeconds: 30 # 对容器监控检查的定期探测间隔时间设置,单位秒,默认10秒一次
successThreshold: 1 # 成功门槛
failureThreshold: 5 # 失败门槛,连接失败5次,pod杀掉,重启一个新的pod
readinessProbe: # Pod准备服务健康检查设置
httpGet:
path: /healthCheck # 如果没有心跳检测接口就为/
port: 8089
scheme: HTTP
initialDelaySeconds: 30
timeoutSeconds: 5
periodSeconds: 10
successThreshold: 1
failureThreshold: 5
lifecycle: # 生命周期管理
postStart: # 容器运行之前运行的任务
exec:
command:
- 'sh'
- 'yum upgrade -y'
preStop: # 容器关闭之前运行的任务
exec:
command: ['service httpd stop']
initContainers: # 初始化容器
- command:
- sh
- -c
- sleep 10; mkdir /wls/logs/nacos-0
env:
image: {{ .Values.busyboxImage }}
imagePullPolicy: IfNotPresent
name: init
volumeMounts:
- mountPath: /wls/logs/
name: logs
volumes:
- name: logs
hostPath:
path: {{ .Values.nfsPath }}/logs
volumes: # 在该pod上定义共享存储卷列表
- name: string # 共享存储卷名称 (volumes类型有很多种)
emptyDir: {} # 类型为emtyDir的存储卷,与Pod同生命周期的一个临时目录。为空值
- name: string
hostPath: # 类型为hostPath的存储卷,表示挂载Pod所在宿主机的目录
path: string # Pod所在宿主机的目录,将被用于同期中mount的目录
- name: string
secret: # 类型为secret的存储卷,挂载集群与定义的secre对象到容器内部
scretname: string
items:
- key: string
path: string
imagePullSecrets: # 镜像仓库拉取镜像时使用的密钥,以key:secretkey格式指定
- name: harbor-certification
hostNetwork: false # 是否使用主机网络模式,默认为false,如果设置为true,表示使用宿主机网络
terminationGracePeriodSeconds: 30 # 优雅关闭时间,这个时间内优雅关闭未结束,k8s 强制 kill
dnsPolicy: ClusterFirst # 设置Pod的DNS的策略。默认ClusterFirst
# 支持的策略:[Default | ClusterFirst | ClusterFirstWithHostNet | None]
# Default : Pod继承所在宿主机的设置,也就是直接将宿主机的/etc/resolv.conf内容挂载到容器中
# ClusterFirst : 默认的配置,所有请求会优先在集群所在域查询,如果没有才会转发到上游DNS
# ClusterFirstWithHostNet : 和ClusterFirst一样,不过是Pod运行在hostNetwork:true的情况下强制指定的
# None : 1.9版本引入的一个新值,这个配置忽略所有配置,以Pod的dnsConfig字段为准
affinity: # 亲和性调试
nodeAffinity: # 节点亲和性。满足特定条件的pod对象运行在同一个node上。效果同nodeSelector,但功能更强大
requiredDuringSchedulingIgnoredDuringExecution: # 硬亲和性
nodeSelectorTerms: # 节点满足任何一个条件就可以
- matchExpressions: # 有多个选项时,则只有同时满足这些逻辑选项的节点才能运行 pod
- key: beta.kubernetes.io/arch
operator: In
values:
- amd64
podAffinity: # pod亲和性。满足特定条件的pod对象运行在同一个node上
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- nginx
topologyKey: kubernetes.io/hostname
podAntiAffinity: # pod反亲和性。满足特定条件(相同pod标签)的pod对象不能运行在同一个node上
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- nginx
topologyKey: kubernetes.io/hostname # 反亲和性的节点标签 key
tolerations: # 污点容忍度。允许pod可以运行在匹配的污点上
- operator: "Equal" # 匹配类型。支持[Exists | Equal(默认值)]。Exists为容忍所有污点
key: "key1"
value: "value1"
effect: "NoSchedule" # 污点类型:[NoSchedule | PreferNoSchedule | NoExecute]
# NoSchedule :不会被调度
# PreferNoSchedule:尽量不调度
# NoExecute:驱逐节点

Deployment.yaml 配置项说明

  • livenessProbe:存活指针

用于判断 Pod(中的应用容器)是否健康,可以理解为健康检查。使用 livenessProbe 来定期的去探测,如果探测成功,则 Pod 状态可以判定为 Running;如果探测失败,则kubectl会根据Pod的重启策略来重启容器

如果未给Pod设置 livenessProbe,则默认探针永远返回 Success。

执行 kubectl get pods 命令,输出信息中 STATUS 一列可以看到Pod是否处于Running状态。

livenessProbe使用场景:有些后端应用在出现某些异常的时候会有假死的情况,这种情况容器依然是running状态,但是应用是无法访问的,所以需要加入存活探测livenessProbe来避免这种情况的发生。

参考:https://blog.csdn.net/qq_41980563/article/details/122139923

  • readinessProbe:就绪指针

就绪的意思是已经准备好了,Pod 的就绪可以理解为这个 Pod 可以接受请求和访问。使用 readinessProbe 来定期的去探测,如果探测成功,则 Pod 的 Ready 状态判定为 True;如果探测失败,Pod 的 Ready 状态判定为 False。

与 livenessProbe 不同的是,kubelet 不会对 readinessProbe 的探测情况有重启操作。

当执行 kubectl get pods 命令,输出信息中 READY 一列可以看到 Pod 的 READY 状态是否为 True。

readinessProbe 使用场景:k8s 应用更新虽然是滚动升级方式,但是很多后端程序启动都比较久,容器起来了,但是服务未起来,而 k8s 只要容器起来了就会移除掉旧的容器,这种情况就会导致在更新发版的时候应用访问失败。这时候就需要配置 readinessProbe 就绪检测,保证新的 pod 已经能正常使用了才会移除掉旧的 pod。

  • initContainers:初始化容器

用于主容器启动时先启动可一个或多个初始化容器,如果有多个,那么这几个 Init Container 按照定义的顺序依次执行,只有所有的 Init Container 执行完后,主容器才会启动。由于一个 Pod 里的存储卷是共享的,所以 Init Container 里产生的数据可以被主容器使用到。

Init Container 可以在多种K8S资源里被使用到,如 Deployment、Daemon Set、Pet Set、Job 等,但归根结底都是在 Pod 启动时,在主容器启动前执行,做初始化工作。

  • tolerations:污点容忍度

节点污点:类似节点上的标签或注解信息,用来描述对应节点的元数据信息;污点定义的格式和标签、注解的定义方式很类似,都是用一个 key-value 数据来表示,不同于节点标签,污点的键值数据中包含对应污点的 effect,污点的 effect 是用于描述对应节点上的污点有什么作用;在 k8s 上污点有三种 effect(效用)策略,第一种策略是 NoSchedule,表示拒绝 pod 调度到对应节点上运行;第二种策略是 PreferSchedule,表示尽量不把 pod 调度到此节点上运行;第三种策略是 NoExecute,表示拒绝将 pod 调度到此节点上运行;该效用相比 NoSchedule 要严苛一点;从上面的描述来看,对应污点就是来描述拒绝pod运行在对应节点的节点属性。

pod 对节点污点的容忍度:pod 要想运行在对应有污点的节点上,对应 pod 就要容忍对应节点上的污点;pod 对节点污点的容忍度就是在对应 pod 中定义怎么去匹配节点污点;通常匹配节点污点的方式有两种,一种是等值(Equal)匹配,一种是存在性(Exists)匹配;等值匹配表示对应pod的污点容忍度,必须和节点上的污点属性相等,所谓污点属性是指污点的 key、value 以及 effect;即容忍度必须满足和对应污点的key、value 和 effect 相同,这样表示等值匹配关系,其操作符为 Equal;存在性匹配是指对应容忍度只需要匹配污点的 key 和 effect 即可,value 不纳入匹配标准,即容忍度只要满足和对应污点的 key 和 effect 相同就表示对应容忍度和节点污点是存在性匹配,其操作符为 Exists;

pod yaml文件参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
 apiVersion: v1                  #必选,版本号,例如v1,可以用 kubectl api-versions 查询到
kind: Pod #必选,指yaml文件定义的k8s 资源类型或角色,比如:Pod
metadata: #必选,元数据对象
name: string #必选,元数据对象的名字,自己定义,比如命名Pod的名字
namespace: string #必选,元数据对象的名称空间,默认为"default"
labels: #自定义标签
key1: value1   #自定义标签键值对1
key2: value2   #自定义标签键值对2
annotations: #自定义注解
key1: value1   #自定义注解键值对1
key2: value2   #自定义注解键值对2
spec: #必选,对象【如pod】的详细定义
containers: #必选,spec对象的容器信息
- name: string #必选,容器名称
image: string #必选,要用到的镜像名称
imagePullPolicy: [Always|Never|IfNotPresent] #获取镜像的策略;(1)Always:意思是每次都尝试重新拉取镜像;(2)Never:表示仅使用本地镜像,即使本地没有镜像也不拉取;(3) IfNotPresent:如果本地有镜像就使用本地镜像,没有就拉取远程镜像。默认:Always
command: [string] #指定容器启动命令,由于是数组因此可以指定多个。不指定则使用镜像打包时指定的启动命令。
args: [string] #指定容器启动命令参数,由于是数组因此可以指定多个
workingDir: string #指定容器的工作目录
volumeMounts: #指定容器内部的存储卷配置
- name: string #指定可以被容器挂载的存储卷的名称。跟下面volume字段的name值相同表示使用这个存储卷
mountPath: string #指定可以被容器挂载的存储卷的路径,应少于512字符
readOnly: boolean #设置存储卷路径的读写模式,true或者false,默认为读写模式false
ports: #需要暴露的端口号列表
- name: string #端口的名称
containerPort: int #容器监听的端口号
protocol: string #端口协议,支持TCP和UDP,默认TCP
env: #容器运行前需设置的环境变量列表
- name: string #环境变量名称
value: string #环境变量的值
resources: #资源限制和资源请求的设置(设置容器的资源上线)
limits: #容器运行时资源使用的上线
cpu: string #CPU限制,单位为core数,允许浮点数,如0.1等价于100m,0.5等价于500m;因此如果小于1那么优先选择如100m的形式,精度为1m。这个数字用作 docker run 命令中的 --cpu-quota 参数。
memory: string #内存限制,单位:E,P,T,G,M,K;或者Ei,Pi,Ti,Gi,Mi,Ki;或者字节数。将用于docker run --memory参数
requests: #容器启动和调度时的限制设定
cpu: string #CPU请求,容器启动时初始化可用数量,单位为core数,允许浮点数,如0.1等价于100m,0.5等价于500m;因此如果小于1那么优先选择如100m的形式,精度为1m。这个数字用作 docker run 命令中的 --cpu-shares 参数。
memory: string #内存请求,容器启动的初始化可用数量。单位:E,P,T,G,M,K;或者Ei,Pi,Ti,Gi,Mi,Ki;或者字节数
# 参见官网地址:https://kubernetes.io/zh/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/
livenessProbe:    #对Pod内各容器健康检查的设置,当探测无响应几次后将自动重启该容器,检查方法有exec、httpGet和tcpSocket,对一个容器【只需设置其中一种方法即可】
exec:        #对Pod内容器健康检查方式设置为exec方式
command: [string] #exec方式需要制定的命令或脚本
httpGet:      #对Pod内容器健康检查方法设置为HttpGet,需要制定Path、port
path: string #访问 HTTP 服务的路径
port: number #访问容器的端口号或者端口名。如果数字必须在 1 ~ 65535 之间。
host: string #当没有定义 "host" 时,使用 "PodIP"
scheme: string #当没有定义 "scheme" 时,使用 "HTTP",scheme 只允许 "HTTP" 和 "HTTPS"
HttpHeaders: #请求中自定义的 HTTP 头。HTTP 头字段允许重复。
- name: string
value: string
tcpSocket:      #对Pod内容器健康检查方式设置为tcpSocket方式
port: number
initialDelaySeconds: 5 #容器启动完成后,kubelet在执行第一次探测前应该等待 5 秒。默认是 0 秒,最小值是 0。
periodSeconds: 60    #指定 kubelet 每隔 60 秒执行一次存活探测。默认是 10 秒。最小值是 1
timeoutSeconds: 3    #对容器健康检查探测等待响应的超时时间为 3 秒,默认1秒
successThreshold: 1 #检测到有1次成功则认为服务是`就绪`
failureThreshold: 5 #检测到有5次失败则认为服务是`未就绪`。默认值是 3,最小值是 1。
restartPolicy: [Always|Never|OnFailure] #Pod的重启策略,默认Always。Always表示一旦不管以何种方式终止运行,kubelet都将重启;OnFailure表示只有Pod以非0退出码退出才重启;Nerver表示不再重启该Pod
nodeSelector:   #定义Node的label过滤标签,以key:value的格式指定。节点选择,先给主机打标签kubectl label nodes kube-node01 key1=value1
key1: value1
imagePullSecrets:    #Pull镜像时使用的secret名称,以name:secretKeyName格式指定
- name: string
hostNetwork: false   #是否使用主机网络模式,默认为false。如果设置为true,表示使用宿主机网络,不使用docker网桥
# volumes 和 containers 是同层级 ******************************
# 参见官网地址:https://kubernetes.io/zh/docs/concepts/storage/volumes/
volumes:       #定义了paues容器关联的宿主机或分布式文件系统存储卷列表 (volumes类型有很多种,选其中一种即可)
- name: string      #共享存储卷名称。
emptyDir: {}    #类型为emtyDir的存储卷,与Pod同生命周期的一个临时目录。当Pod因为某些原因被从节点上删除时,emptyDir卷中的数据也会永久删除。
hostPath: string   #类型为hostPath的存储卷,表示挂载Pod所在宿主机的文件或目录
path: string    #在宿主机上文件或目录的路径
type: [|DirectoryOrCreate|Directory|FileOrCreate|File] #空字符串(默认)用于向后兼容,这意味着在安装 hostPath 卷之前不会执行任何检查。DirectoryOrCreate:如果给定目录不存在则创建,权限设置为 0755,具有与 Kubelet 相同的组和所有权。Directory:给定目录必须存在。FileOrCreate:如果给定文件不存在,则创建空文件,权限设置为 0644,具有与 Kubelet 相同的组和所有权。File:给定文件必须存在。
secret:       #类型为secret的存储卷,挂载集群预定义的secre对象到容器内部。Secret 是一种包含少量敏感信息例如密码、token 或 key 的对象。放在一个 secret 对象中可以更好地控制它的用途,并降低意外暴露的风险。
secretName: string #secret 对象的名字
items: #可选,修改key 的目标路径
- key: username #username secret存储在/etc/foo/my-group/my-username 文件中而不是 /etc/foo/username 中。【此时存在spec.containers[].volumeMounts[].mountPath为/etc/foo】
path: my-group/my-username
configMap:      #类型为configMap的存储卷,挂载预定义的configMap对象到容器内部。ConfigMap 允许您将配置文件与镜像文件分离,以使容器化的应用程序具有可移植性。
name: string #提供你想要挂载的 ConfigMap 的名

service资源

简述

Service是Kubernetes的核心概念,通过创建Service,可以为一组具有相同功能的容器应用提供一个统一的入口地址,并将请求负载分发到后端各个容器应用上。

Service.yaml 文件解析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
apiVersion: v1     # 指定api版本,此值必须在kubectl api-versions中
kind: Service # 指定创建资源的角色/类型
metadata: # 资源的元数据/属性
name: demo # 资源的名字,在同一个namespace中必须唯一
namespace: default # 部署在哪个namespace中。不指定时默认为default命名空间
labels: # 设定资源的标签
- app: demo
annotations: # 自定义注解属性列表
- name: string
spec: # 资源规范字段
type: ClusterIP # service的类型,指定service的访问方式,默认ClusterIP。
# ClusterIP类型:虚拟的服务ip地址,用于k8s集群内部的pod访问,在Node上kube-porxy通过设置的iptables规则进行转发
# NodePort类型:使用宿主机端口,能够访问各个Node的外部客户端通过Node的IP和端口就能访问服务器
# LoadBalancer类型:使用外部负载均衡器完成到服务器的负载分发,需要在spec.status.loadBalancer字段指定外部负载均衡服务器的IP,并同时定义nodePort和clusterIP用于公有云环境。
clusterIP: string #虚拟服务IP地址,当type=ClusterIP时,如不指定,则系统会自动进行分配,也可以手动指定。当type=loadBalancer,需要指定
sessionAffinity: string #是否支持session,可选值为ClietIP,默认值为空。ClientIP表示将同一个客户端(根据客户端IP地址决定)的访问请求都转发到同一个后端Pod
ports:
- port: 8080 # 服务监听的端口号
targetPort: 8080 # 容器暴露的端口
nodePort: int # 当type=NodePort时,指定映射到物理机的端口号
protocol: TCP # 端口协议,支持TCP或UDP,默认TCP
name: http # 端口名称
selector: # 选择器。选择具有指定label标签的pod作为管理范围
app: demo
status: # 当type=LoadBalancer时,设置外部负载均衡的地址,用于公有云环境
loadBalancer: # 外部负载均衡器
ingress:
ip: string # 外部负载均衡器的IP地址
hostname: string # 外部负载均衡器的主机名

注:

  • port和nodePort都是service的端口,前者暴露给k8s集群内部服务访问,后者暴露给k8s集群外部流量访问。从上两个端口过来的数据都需要经过反向代理kube-proxy,流入后端pod的targetPort上,最后到达pod内的容器。NodePort类型的service可供外部集群访问是因为service监听了宿主机上的端口,即监听了(所有节点)nodePort,该端口的请求会发送给service,service再经由负载均衡转发给Endpoints的节点。

ingress.yaml 文件详解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
apiVersion: extensions/v1beta1         # 创建该对象所使用的 Kubernetes API 的版本
kind: Ingress # 想要创建的对象的类别,这里为Ingress
metadata:
name: showdoc # 资源名字,同一个namespace中必须唯一
namespace: op # 定义资源所在命名空间
annotations: # 自定义注解
kubernetes.io/ingress.class: nginx # 声明使用的ingress控制器
spec:
rules:
- host: showdoc.example.cn # 服务的域名
http:
paths:
- path: / # 路由路径
backend: # 后端Service
serviceName: showdoc # 对应Service的名字
servicePort: 80 # 对应Service的端口

安装Helm

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3
chmod 700 get_helm.sh
./get_helm.sh

helm repo add elastic https://helm.elastic.co
helm repo add gitlab https://charts.gitlab.io
helm repo add harbor https://helm.goharbor.io
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo add incubator https://kubernetes-charts-incubator.storage.googleapis.com
helm repo add stable https://kubernetes-charts.storage.googleapis.com
helm repo add azure http://mirror.azure.cn/kubernetes/charts/
# 添加国内仓库
helm repo add stable http://mirror.azure.cn/kubernetes/charts
helm repo add aliyun https://kubernetes.oss-cn-hangzhou.aliyuncs.com/charts
helm repo update
helm repo list
1
2
3
4
5
6
helm repo list # 显示仓库
helm list --all-namespaces
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update
helm search repo {prometheus} # 查找
helm uninstall kps --namespace prometheus # 卸载

helm安装prometheus全家桶

1
2
3
4
5
6
7
8
9
10
helm show values prometheus-community/kube-prometheus-stack > kube-prometheus-stack.yaml-default
cp kube-prometheus-stack.yaml-default kube-prometheus-stack.yaml

vim kube-prometheus-stack.yaml

3456 serviceMonitorSelectorNilUsesHelmValues: true

# 把true修改为false

helm install kps prometheus-community/kube-prometheus-stack -f kube-prometheus-stack.yaml --namespace prome --create-namespace --version 57.0.3 --debug

安装metallb

如果直接使用NGINX Ingress Controller,由于默认使用的是LoadBalancer,如果没安装metallb则external ip一直是pending状态

因为Kubernetes默认不支持Type为LoadBalancer的服务,因此我们需要手动安装Service LoadBalancer

由于Kubernetes本身并不提供LoadBalaner Ingress Controller, 因此通常使用云平台本身的loadBalancer,如果你是自己在裸机上搭建Kubernetes,需要手动搭建LoadBalancer Ingress Controller

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.14.3/config/manifests/metallb-native.yaml

kubectl edit configmap -n kube-system kube-proxy
更改:

mode: "ipvs"
ipvs:
strictARP: true

如果报错:MountVolume.SetUp failed for volume "memberlist" : secret "memberlist" not found
直接手动创建:
kubectl create secret generic -n metallb-system memberlist --from-literal=secretkey="$(openssl rand -base64 128)

kubectl apply -f metallb.yaml

# 用于为Service分配IP
kubectl apply -f - <<EOF
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
name: first-pool
namespace: metallb-system
spec:
addresses:
- 192.168.66.230-192.168.66.239
EOF
# 这里要改成自己的
# L2 network mode
kubectl apply -f - <<EOF
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
namespace: metallb-system
name: metallbl2
spec:
ipAddressPools:
- first-pool
EOF

#
kubectl apply -f - <<EOF
apiVersion: metallb.io/v1beta1
kind: BGPAdvertisement
metadata:
namespace: metallb-system
name: metallbbgp
EOF

此时再使用kubectl get svc -n ingress-nginx 则有了正常的external ip

通过数据集挂载的方式创建pod

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
apiVersion: v1
kind: Pod
metadata:
name: pod-cm2
spec:
containers:
- name: busybox
image: busybox
args: [ "/bin/sh", "-c", "sleep 10000" ]
volumeMounts:
- name: vol-cm
mountPath: "/etc/mysql"
readOnly: true

volumes:
- name: vol-cm
configMap:
name: cm

安装Containerd高级命令行工具 nerdctl

Containerd ctr、crictl、nerdctl 客户端命令介绍与实战操作-CSDN博客

更换 Containerd 后,以往我们常用的 docker 命令也不再使用,取而代之的分别是 crictl 和 ctr 两个命令客户端。

  • crictl 是遵循 CRI 接口规范的一个命令行工具,通常用它来检查和管理kubelet节点上的容器运行时和镜像。
  • ctrcontainerd 的一个客户端工具。
  • ctr -v 输出的是 containerd 的版本,crictl -v 输出的是当前 k8s 的版本,从结果显而易见你可以认为 crictl 是用于 k8s 的。
  • 一般来说你某个主机安装了 k8s 后,命令行才会有 crictl 命令。而 ctr 是跟 k8s 无关的,你主机安装了 containerd 服务后就可以操作 ctr 命令。
命令 docker ctr(containerd) crictl(kubernetes)
查看运行的容器 docker ps ctr task ls/ctr container ls crictl ps
查看镜像 docker images ctr image ls [ns] crictl images
查看容器日志 docker logs crictl logs
查看容器数据信息 docker inspect ctr container info crictl inspect
查看容器资源 docker stats crictl stats
启动/关闭已有的容器 docker start/stop ctr task start/kill crictl start/stop
运行一个新的容器 docker run ctr run 无(最小单元为 pod)
打标签 docker tag ctr image tag
创建一个新的容器 docker create ctr container create crictl create
导入镜像 docker load ctr image import
导出镜像 docker save ctr image export
删除容器 docker rm ctr container rm crictl rm
删除镜像 docker rmi ctr image rm crictl rmi
拉取镜像 docker pull ctr image pull ctictl pull
推送镜像 docker push ctr image push
登录或在容器内部执行命令 docker exec crictl exec
清空不用的容器 docker image prune crictl rmi –prune
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
一、安装nerdctl

# 在 GitHub Release 页面下载对应的压缩包解压到 PATH 路径下即可:

wget https://github.com/containerd/nerdctl/releases/download/v1.5.0/nerdctl-1.5.0-linux-amd64.tar.gz

mkdir -p /usr/local/containerd/bin/ && tar -zxvf nerdctl-1.5.0-linux-amd64.tar.gz nerdctl && mv nerdctl /usr/local/containerd/bin/

ln -s /usr/local/containerd/bin/nerdctl /usr/local/bin/nerdctl

# 验证 【有个告警说"buildctl": executable file not found in $PATH】我们再安装另一个buildctl
[root@k8s-master03 ~]# nerdctl version
WARN[0000] unable to determine buildctl version: exec: "buildctl": executable file not found in $PATH
Client:
Version: v1.5.0
OS/Arch: linux/amd64
Git commit: b33a58f288bc42351404a016e694190b897cd252
buildctl:
Version:

Server:
containerd:
Version: 1.6.22
GitCommit: 8165feabfdfe38c65b599c4993d227328c231fca
runc:
Version: 1.1.8
GitCommit: v1.1.8-0-g82f18fe

wget https://github.com/moby/buildkit/releases/download/v0.12.2/buildkit-v0.12.2.linux-amd64.tar.gz

mkdir -p /usr/local/buildctl -p && tar -zxvf buildkit-v0.12.2.linux-amd64.tar.gz -C /usr/local/buildctl

ln -s /usr/local/buildctl/bin/buildkitd /usr/local/bin/buildkitd
ln -s /usr/local/buildctl/bin/buildctl /usr/local/bin/buildctl

# 使用Systemd来管理buildkitd,创建如下所示的systemd unit文件
cat >> /etc/systemd/system/buildkit.service <<EOF
[Unit]
Description=BuildKit
Documentation=https://github.com/moby/buildkit

[Service]
ExecStart=/usr/local/bin/buildkitd --oci-worker=false --containerd-worker=true

[Install]
WantedBy=multi-user.target
EOF

# 启动buildkitd
systemctl daemon-reload
systemctl enable buildkit --now
systemctl status buildkit

# 再次验证
[root@k8s-master03 bin]# nerdctl version
Client:
Version: v1.5.0
OS/Arch: linux/amd64
Git commit: b33a58f288bc42351404a016e694190b897cd252
buildctl:
Version: v0.12.2
GitCommit: 567a99433ca23402d5e9b9f9124005d2e59b8861

Server:
containerd:
Version: 1.6.22
GitCommit: 8165feabfdfe38c65b599c4993d227328c231fca
runc:
Version: 1.1.8
GitCommit: v1.1.8-0-g82f18fe

安装完后:

1
2
3
4
mkdir /etc/nerdctl
vim /etc/nerdctl/nerdctl.toml

namespace = "k8s.io" #让nerdctl是使用k8s.io的namespace

如果用nerdctl login 时候 ,没有自签则要加入参数 –insecure-registry

同理 push时也要

containerd配置私人仓库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
vim /etc/containerd/config.toml

[plugins."io.containerd.grpc.v1.cri".registry]
config_path = ""
[plugins."io.containerd.grpc.v1.cri".registry.mirrors]
[plugins."io.containerd.grpc.v1.cri".registry.mirrors."192.168.66.23"]
endpoint = ["http://192.168.66.23"]

[plugins."io.containerd.grpc.v1.cri".registry.auths]
[plugins."io.containerd.grpc.v1.cri".registry.configs]
[plugins."io.containerd.grpc.v1.cri".registry.configs."192.168.66.23".tls]
insecure_skip_verify = true
[plugins."io.containerd.grpc.v1.cri".registry.configs."192.168.66.23".auth]
username = "admin"
password = "12345"
[plugins."io.containerd.grpc.v1.cri".registry.headers]

注意:如果使用的域名登录的harbor(就是nerdctl login xxx.com)的时候,要把所有的地址192.168.66.23改成对应的xxx.com

1
2
3
systemctl daemon-reload && systemctl restart containerd.service

crictl pull 192.168.66.23/test/nginx

创建公私钥

什么都没有,自己生成的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
openssl genrsa -out ca.key 4096 # 生成根证书私钥(无加密)

# 生成自签名证书(使用已有私钥ca.key自行签发根证书)
openssl req -x509 -new -nodes -sha512 -days 3650 \
-subj "/C=CN/ST=Shanghai/L=Shanghai/O=Weijie/OU=Personal/CN=harbor.kubemsb.com" \
-key ca.key \
-out ca.crt

# 创建服务器端证书 生成服务器端私钥和CSR签名请求

openssl req -newkey rsa:4096 -nodes -sha256 -keyout server.key -out server.csr \
-subj "/C=CN/ST=Shanghai/L=Shanghai/O=Weijie/OU=Personal/CN=harbor.kubemsb.com"

# 签发服务器证书
echo subjectAltName = IP:192.168.66.213 > extfile.cnf

openssl x509 -req -sha512 -days 3650 \
-CA ca.crt -CAkey ca.key -CAcreateserial \
-in server.csr \
-out server.crt \
-extfile extfile.cnf

harbor.xml:
certificate: /usr/local/harbor/harbor/pem/server.crt
private_key: /usr/local/harbor/harbor/pem/server.key

注意,如果是更改的harbor,要重新加载配置文件 ./install.sh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
cd  /etc/kubernetes/pki
(umask 077; openssl genrsa -out wj.key 2048) # 生成私钥

openssl req -new -key wj.key -out wj.csr # 根据这个私钥生成签证(自签)
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:China
String too long, must be at most 2 bytes long
Country Name (2 letter code) [AU]:Beijing
String too long, must be at most 2 bytes long
Country Name (2 letter code) [AU]:CN
State or Province Name (full name) [Some-State]:BeiJing
Locality Name (eg, city) []:dhu
Organization Name (eg, company) [Internet Widgits Pty Ltd]:dhu
Organizational Unit Name (eg, section) []:dhu
Common Name (e.g. server FQDN or YOUR name) []:dhu
Email Address []:1041700406@qq.com

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:
或者直接 openssl req -new -key wj.key -out wj.csr -subj "/O=dhu/CN=dhu"
openssl x509 -req -in wj.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out wj.crt -days 36500 # 基于CA证书对签名请求进行签证
# 如果没有证书,则自己生成证书
---
openssl req -x509 -new -nodes -sha512 -days 3650 \
-subj "/C=CN/ST=Shanghai/L=Shanghai/O=wj/OU=Personal/CN=harbor.kubemsb.com" \
-key wj.key \
-out ca.cr
---

openssl x509 -in wj.crt -noout -text # 查看证书

k config set-credentials wj --client-certificate wj.crt --client-key wj.key --kubeconfig /tmp/wj.conf # 创建 可以加上 --embed-certs=true 参数使得生成的config文件不是明文显示
k config view --kubeconfig /tmp/wj.conf # 查看创建是否成功
k config set-cluster myk8s --server=192.168.66.200:6443 --certificate-authority ca.crt --embed-certs=true --kubeconfig /tmp/wj.conf # 设置集群
k config view --kubeconfig /tmp/wj.conf # 再次查看创建是否成功
k config set-context wj@myk8s --cluster=myk8s --user wj --kubeconfig /tmp/wj.conf # 创建上下文,把用户和集群关联起来
k config use-context wj@myk8s --kubeconfig # 对这个config文件切换集群

k get pods --kubeconfig /tmp/wj.conf # 临时切换集群,要指定config

HTTPS证书管理与双向认证

数字证书基本概念

数字证书的标准:

  • X.509版本号:指出该证书使用了哪种版本的X.509标准,版本号会影响证书中的一些特定信息
  • 序列号:由CA(Certificate Authority)给予每一个证书分配的唯一的数字型编号,当证书被取消时,实际上是将此证书序列号放入由CA签发的CRL(Certificate Revocation List证书作废表,或证书黑名单表)中。这也是序列号唯一的原因
  • 签名算法标识符:用来指定CA签署证书时所使用的签名算法,常见算法如RSA
  • 签发者信息:颁发证书的实体的 X.500 名称信息。它通常为一个 CA
  • 证书的有效期:证书起始日期和时间以及终止日期和时间;指明证书在这两个时间内有效。
  • 主题信息:证书持有人唯一的标识,在 Internet上应该是唯一的
  • 发布者的数字签名:这是使用发布者私钥生成的签名,以确保这个证书在发放之后没有被撰改过。
  • 证书的公钥:包括证书的公钥、算法(指明密钥属于哪种密码系统)的标识符和其他相关的密钥参数

数字证书的常见格式:

  • CSR:Certificate Signing Request(证书签发请求文件),这个并不是证书,而是向证书颁发机构获得签名证书的申请文件
  • CER:Certificate File:证书文件,可以是二进制编码或者BASE64编码
  • CRT:也是证书文件,证书可以是DER编码,也可以是PEM编码,在linux系统中比较常见
  • pem:Privacy Enhanced Mail,该编码格式在RFC1421中定义,但他也同样广泛运用于密钥管理,实质上是 Base64 编码的二进制内容
  • DER:Distinguished Encoding Rules:区别编码规则,用于二进制DER编码的证书。这些证书也可以用CER或者CRT作为扩展名
  • JKS: Java KeyStore:Java密钥库,java的密钥存储文件,二进制格式,是一种 Java 特定的密钥文件格式, JKS的密钥库和私钥可以用不同的密码进行保护
  • p12/PFX: PKCS#12 / Personal Information Exchange:个人信息交换格式,又名PKCS#12,包含所有私钥、公钥和证书。其以二进制格式存储,也称为 PFX 文件,在windows中可以直接导入到密钥区,密钥库和私钥用相同密码进行保护

K8s 中组件之间通信,证书的验证是在协议层通过 TLS 完成的,TLS 验证分为 2 种:

  • 服务器单向认证:服务器端持有证书证明自己身份,用于服务端不关心客户端身份而客户端需要确认服务器身份的场景。例如火车票购票网站,我们必须保证其是官方而非恶意服务器,但网站允许任何客户端进行连接访问;
  • 双向 TLS 认证:双方都要持有证书,并验证对方证书确认身份。一般用于服务端持有信息比较敏感,只有特定客户端才能访问的场景。例如:K8s 内组件提供的接口往往包含集群内部信息,若被非法访问会影响整体安全,所以 K8s 内部组件之间都是双向 TLS 认证。
名称 作用 例子
服务端证书 包含服务端公钥和服务端身份信息 通过根证书手动或者 kubeadm 自动生成的 API Server 服务端证书文件 apiserver.crt
服务器私钥 主要用于 TLS 认证时进行数字签名,证明自己是服务端证书的拥有者 通过根证书手动或者 kubeadm 生成的 API Server 服务端私钥文件 apiserver.key
客户端证书 包含客户端公钥和客户端身份信息 由同一个 CA 根证书签发的.crt 文件
客户端私钥 主要用于 TLS 认证时进行数字签名,证明自己是客户端证书的拥有者 由同一个 CA 根证书签发的.key 文件
服务端 CA 根证书 签发服务端证书的 CA 根证书 通过 openssl 等工具生成的 ca.crt 文件,并在服务端启动时进行指定
客户端 CA 根证书 签发客户端证书的 CA 根证书 通过 openssl 等工具生成的 ca.crt 文件,并在客户端启动时进行指定 (一般与服务端使用一个)

什么是HTTPS单向认证

单向认证,只有一方需要验证对方的身份。通常是客户端验证服务器的身份。这种情况下,客户端会检查服务器提供的数字证书是否有效,以确定服务器是否合法。服务器不会验证客户端的身份。这种情况下,客户端可以确认它正在与合法的服务器进行通信,但服务器不能确定其与合法客户端通信。单向认证通常用于一些对服务器身份验证要求较高,但对客户端身份验证要求相对较低的场景,如网站访问。

单向认证原理:

  1. 客户端向服务端发送SSL协议版本号、加密算法种类、随机数等信息。
  2. 服务端给客户端返回SSL协议版本号、加密算法种类、随机数等信息,同时也返回服务器端的证书,即公钥证书
  3. 客户端使用服务端返回的信息验证服务器的合法性,包括:

-
- 证书是否过期
- 发型服务器证书的CA是否可靠
- 返回的公钥是否能正确解开返回证书中的数字签名
- 服务器证书上的域名是否和服务器的实际域名相匹配

  1. 验证通过后,将继续进行通信,否则,终止通信
  2. 客户端向服务端发送自己所能支持的对称加密方案,供服务器端进行选择
  3. 服务器端在客户端提供的加密方案中选择加密程度最高的加密方式。
  4. 服务器将选择好的加密方案通过明文方式返回给客户端
  5. 客户端接收到服务端返回的加密方式后,使用该加密方式生成产生随机码,用作通信过程中对称加密的密钥,使用服务端返回的公钥进行加密,将加密后的随机码发送至服务器
  6. 服务器收到客户端返回的加密信息后,使用自己的私钥进行解密,获取对称加密密钥。
    在接下来的会话中,服务器和客户端将会使用该密码进行对称加密,保证通信过程中信息的安全全

什么是HTTPS双向认证

双向认证要求通信双方都需要验证对方的身份。即客户端验证服务器的身份,同时服务器也验证客户端的身份。这种情况下,双方都会使用数字证书来证明自己的身份。客户端在连接到服务器时会发送自己的数字证书,服务器会验证该证书的合法性。同时,服务器也会发送数字证书给客户端,客户端会验证服务器的证书。只有在双方都通过了身份验证,通信才会继续进行。双向认证通常用于对通信双方身份验证要求较高的场景,如安全敏感的数据交换、金融交易等。

首先:公钥加密,私钥解密,公钥是任何人都可以用其进行加密,私钥只有自己有,f(私钥) = 公钥, 只有私钥可以推出自己的公钥。

双向认证原理:

  1. 客户端向服务端发送SSL协议版本号、加密算法种类、随机数等信息。
  2. 服务端给客户端返回SSL协议版本号、加密算法种类、随机数等信息,同时也返回服务器端的证书,即公钥证书
  3. 客户端使用服务端返回的信息验证服务器的合法性,包括:

-
- 证书是否过期
- 发型服务器证书的CA是否可靠
- 返回的公钥是否能正确解开返回证书中的数字签名
- 服务器证书上的域名是否和服务器的实际域名相匹配

  1. 验证通过后,将继续进行通信,否则,终止通信
  2. 服务端要求客户端发送客户端的证书,客户端会将自己的证书发送至服务端
  3. 验证客户端的证书,通过验证后,会获得客户端的公钥
  4. 客户端向服务端发送自己所能支持的对称加密方案,供服务器端进行选择
  5. 服务器端在客户端提供的加密方案中选择加密程度最高的加密方式
  6. 将加密方案通过使用之前获取到的公钥进行加密,返回给客户端
  7. 客户端收到服务端返回的加密方案密文后,使用自己的私钥进行解密,获取具体加密方式,而后,产生该加密方式的随机码,用作加密过程中的密钥,使用之前从服务端证书中获取到的公钥进行加密后,发送给服务端
  8. 服务端收到客户端发送的消息后,使用自己的私钥进行解密,获取对称加密的密钥,在接下来的会话中,服务器和客户端将会使用该密码进行对称加密,保证通信过程中信息的安全。

配置HTTPS单向认证

生成CA证书:

1
2
3
4
5
6
7
8
# 生成CA根证书私钥:为保证安全,生成一个4096位的私钥,并使用aes方式加密
$ openssl genrsa -aes256 -out kubesre-ca.key 4096
Enter PEM pass phrase: # 密码:12345678
Verifying - Enter PEM pass phrase:

# 通过CA根私钥签发CA根证书
$ openssl req -new -x509 -days 3650 -sha256 -extensions v3_ca -key kubesre-ca.key -out kubesre-ca.cer -subj "/C=CN/ST=shanghai/L=shanghai/O=kubesre/OU=kubesre/CN=*.kubesre.com"
Enter pass phrase for kubesre-ca.key: # 密码:12345678

生成服务端证书:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 生成服务端证书私钥
$ openssl genrsa -out kubesre-server.key 2048

# 生成签发请求csr
$openssl req -new -key kubesre-server.key -out kubesre-server.csr -subj "/C=CN/ST=shanghai/L=shanghai/O=kubesre/OU=kubesre/CN=demo.kubesre.com"

# 用CA证书签发服务端证书
$ openssl x509 -req -days 3650 -sha256 -CA kubesre-ca.cer -CAkey kubesre-ca.key -in kubesre-server.csr -out kubesre-server.cer
Certificate request self-signature ok
subject=C = CN, ST = shanghai, L = shanghai, O = kubesre, OU = kubesre, CN = demo.kubesre.com
Enter pass phrase for kubesre-ca.key: # 密码:12345678

$ ll
total 40
-rw-r--r--@ 1 chuanzhang staff 2.0K 8 13 15:24 kubesre-ca.cer
-rw-------@ 1 chuanzhang staff 3.4K 8 13 15:22 kubesre-ca.key
-rw-r--r--@ 1 chuanzhang staff 1.6K 8 13 15:31 kubesre-server.cer
-rw-r--r--@ 1 chuanzhang staff 1.0K 8 13 15:28 kubesre-server.csr
-rw-------@ 1 chuanzhang staff 1.7K 8 13 15:27 kubesre-server.key

创建证书Secret(证书基于Kubernetes Secret进行存储):

1
2
3
4
5
$ kubectl create secret tls  kubesre-tls --key kubesre-server.key --cert kubesre-server.cer
secret/kubesre-tls created
$ kubectl get secret
NAME TYPE DATA AGE
kubesre-tls kubernetes.io/tls 2 18s

创建Ingress资源:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
$ cat ingress.yml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: demo
spec:
tls:
- hosts:
- demo.kubesre.com
secretName: kubesre-tls # 创建的证书Secret
rules:
- host: demo.kubesre.com
http:
paths:
- path: /info
pathType: Prefix
backend:
service:
name: demo-svc
port:
number: 8080
ingressClassName: nginx
$ kubectl apply -f ingress.yml
ingress.networking.k8s.io/demo configured

配置HTTPS双向认证

上一步,已经签发了服务端证书,接下来,咱们来签发客户端证书即可!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 生成客户端证书私钥
$ openssl genrsa -out kubesre-client.key 2048
# 生成签发请求csr
$ openssl req -new -key kubesre-client.key -out kubesre-client.csr -subj "/C=CN/ST=shanghai/L=shanghai/O=kubesre/OU=kubesre/CN=client.kubesre.com"

# 用CA证书签发客户端证书
$ openssl x509 -req -days 3650 -sha256 -CA kubesre-ca.cer -CAkey kubesre-ca.key -in kubesre-client.csr -out kubesre-client.cer
Certificate request self-signature ok
subject=C = CN, ST = shanghai, L = shanghai, O = kubesre, OU = kubesre, CN = client.kubesre.com
Enter pass phrase for kubesre-ca.key: # 密码:12345678

$ ls -l | grep client
-rw-r--r--@ 1 chuanzhang staff 1590 8 13 15:46 kubesre-client.cer
-rw-r--r--@ 1 chuanzhang staff 1021 8 13 15:46 kubesre-client.csr
-rw-------@ 1 chuanzhang staff 1704 8 13 15:46 kubesre-client.key

创建CA证书的Secret:

1
2
3
4
5
6
7
kubectl create secret generic ca-secret  --from-file=ca.crt=kubesre-ca.cer
secret/ca-secret created

$ kubectl get secret
NAME TYPE DATA AGE
ca-secret Opaque 1 74s
kubesre-tls kubernetes.io/tls 2 28m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
$ cat ingress.yml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
nginx.ingress.kubernetes.io/auth-tls-verify-client: "on" # 开启客户端认证
nginx.ingress.kubernetes.io/auth-tls-secret: "default/ca-secret" # 配置CA证书
nginx.ingress.kubernetes.io/auth-tls-verify-depth: "1"# 提供的客户证书和证书颁发机构链之间的验证深度
nginx.ingress.kubernetes.io/auth-tls-pass-certificate-to-upstream: "true" # 指示是否应将收到的证书传递给上游服务器。默认情况下是禁用的。
name: demo
spec:
tls:
- hosts:
- demo.kubesre.com
secretName: kubesre-tls
rules:
- host: demo.kubesre.com
http:
paths:
- path: /info
pathType: Prefix
backend:
service:
name: demo-svc
port:
number: 8080
ingressClassName: nginx

$ kubectl apply -f ingress.yml
ingress.networking.k8s.io/demo configured

验证:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 出现400错误代码,表示没有传客户端证书过去
$ curl -k https://demo.kubesre.com/info
<html>
<head><title>400 No required SSL certificate was sent</title></head>
<body>
<center><h1>400 Bad Request</h1></center>
<center>No required SSL certificate was sent</center>
<hr><center>nginx</center>
</body>
</html>

# 带上客户端证书继续访问,则可以访问成功
$ curl ./kubesre-ca.cer --cert ./kubesre-client.cer --key ./kubesre-client.key https://demo.kubesre.com/info
{"message":"云原生运维圈!"}

配置NFS服务器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
如果是虚拟机,需要先分配一个新硬盘
lsblk 查看是否添加成功
格式化该硬盘
mkfs.xsf /dev/sdb
mkdir /sdb
vim /etc/fstab
最后一行添加:
/dev/sdb /sdb xfs defaults 0 0

df -h # 查看是否挂载成功

apt install nfs-server -y

vim /etc/exports 加入下面内容
/data/nfs *(rw,sync,no_root_squash)

/etc/init.d/rpcbind restart
/etc/init.d/nfs-kernel-server restart

systemctl enable --now nfs-server
showmount -e # 显示是否挂载成功

输入“mount -t nfs nfsserver_ip:/home/minilinux /mnt” 命 令 , 这个命令可以把/home/minilinux 挂载到/mnt 目录

要连接的主机需要安装apt install -y nfs-kernel-server
通过 showmount -e 192.168.66.23(或者是域名) 命令可以查看是否连接成功

umount {/mnt/} # 取消挂载

如果输入showmount -e时报错: clnt_create: RPC: Unknown host

则是因为解析过程中nfs不能找到host主机名,解决方法:

vim /etc/hosts

127.0.0.1 localhost nfs(主机名)

k8s中的连接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
在master节点中安装nfs的yaml文件
for file in class.yaml deployment.yaml rbac.yaml ; do wget https://raw.githubusercontent.com/kubernetes-incubator/external-storage/master/nfs-client/deploy/$file ; done

先改class文件,改一个name为nfs-client
先应用class.yaml和rbac.yaml
修改deployment文件
最后几行为:
- name: nfs-client-provisioner
image: registry.cn-beijing.aliyuncs.com/pylixm/nfs-subdir-external-provisioner:v4.0.0 # 镜像地址
volumeMounts:
- name: nfs-client # 改
mountPath: /persistentvolumes
env:
- name: PROVISIONER_NAME
value: fuseim.pri/ifs
- name: NFS_SERVER
value: nfs.kubemsb.com # 改
- name: NFS_PATH
value: /sdb # 改
volumes:
- name: nfs-client # 改
nfs:
server: nfs.kubemsb.com # 改
path: /sdb # 改

GlusterFS配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
查看硬盘
[root@gX ~]# lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
vda 252:0 0 20G 0 disk
├─vda1 252:1 0 1G 0 part /boot
└─vda2 252:2 0 19G 0 part
├─centos-root 253:0 0 17G 0 lvm /
└─centos-swap 253:1 0 2G 0 lvm [SWAP]
sdb 252:16 0 100G 0 disk

(注,如果没有sdb文件则要去虚拟机中新建一个磁盘)
格式化硬盘
[root@gX ~]# mkfs.xfs /dev/sdb
meta-data=/dev/sdb isize=512 agcount=4, agsize=6553600 blks
= sectsz=512 attr=2, projid32bit=1
= crc=1 finobt=0, sparse=0
data = bsize=4096 blocks=26214400, imaxpct=25
= sunit=0 swidth=0 blks
naming =version 2 bsize=4096 ascii-ci=0 ftype=1
log =internal log bsize=4096 blocks=12800, version=2
= sectsz=512 sunit=0 blks, lazy-count=1
realtime =none extsz=4096 blocks=0, rtextents=0

准备挂载目录
[root@g1 ~]# mkdir /glustersdb

[root@g2 ~]# mkdir /glustersdb

[root@g3~]# mkdir /glustersdb

修改/etc/fstab文件实现自动挂载
[root@gX ~]# cat /etc/fstab

......
/dev/sdb /glustersdb xfs defaults 0 0

挂载所有
[root@gX ~]# mount -a

查看文件系统挂载情况
[root@gX ~]# df -h
文件系统 容量 已用 可用 已用% 挂载点
/dev/mapper/centos-root 17G 1.1G 16G 7% /
devtmpfs 988M 0 988M 0% /dev
tmpfs 1000M 0 1000M 0% /dev/shm
tmpfs 1000M 8.6M 991M 1% /run
tmpfs 1000M 0 1000M 0% /sys/fs/cgroup
/dev/vda1 1014M 133M 882M 14% /boot
tmpfs 98M 0 98M 0% /run/user/0
/dev/sdb 100G 33M 100G 1% /glustersdb

安装GLusterFS

1
2
3
4
5
6
7
8
9
10
sudo apt install -y software-properties-common
sudo apt install glusterfs-server -y
# 启动GlusterFS服务,安装完成后,可能没有自动运行,需要手动执行来启动服务.(所有节点均需启动)
sudo service glusterd start
systemctl enable glusterd
# 查看服务运行状态
sudo service glusterd status
# 加入伙伴(ps: 要先在hosts文件里配置hostname)
gluster peer probe g1
gluster peer status # 查看状态

验证可用性

1
2
3
4
5
gluster volume create k8s-test-volume replica 3 g1:/glustersdb/r1 g2:/glustersdb/r2 g3:/glustersdb/r3
ls /glustersdb
gluster volume start k8s-test-volume
gluster volume status k8s-test-volume
gluster volume info k8s-test-volume

如果某一个brick不在线会影响客户端挂载(可选)

设置后,可以允许volume中的某块brick不在线的情况

1
2
3
4
5
[root@g1 glusterfs]# gluster volume set k8s-test-volume cluster.server-quorum-type none
volume set: success

[root@g1 glusterfs]#
volume set: success

限额问题(可选)

1
2
3
4
5
[root@g1 ~]# gluster volume quota k8s-test-volume enable
volume quota : success

[root@g1 ~]# gluster volume quota k8s-test-volume limit-usage / 10GB
volume quota : success

k8s中的使用

1
2
3
4
5
6
7
8
9
# work节点中也需要安装
sudo apt install -y software-properties-common
sudo apt install glusterfs-server -y
mkdir /k8s-glusterfs-test-volume
mount -t glusterfs g1:/k8s-test-volume /k8s-glusterfs-test-volume
# 查看
df -h
# 验证完成后需要卸载
umount /k8s-glusterfs-test-volume

安装heketi

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
wget https://github.com/heketi/heketi/releases/download/v10.4.0/heketi-client-v10.4.0-release-10.linux.amd64.tar.gz
wget https://github.com/heketi/heketi/releases/download/v10.4.0/heketi-v10.4.0-release-10.linux.amd64.tar.gz
tar zxvf heketi-client-v10.4.0-release-10.linux.amd64.tar.gz
tar zxvf heketi-v10.4.0-release-10.linux.amd64.tar.gz
cd heketi
cp heketi /usr/bin
cp heketi-cli /usr/bin
vim /lib/systemd/system/heketi.service

[Unit]
Description=Heketi Server
[Service]
Type=simple
WorkingDirectory=/var/lib/heketi
ExecStart=/usr/bin/heketi --config=/etc/heketi/heketi.json
Restart=on-failure
StandardOutput=syslog
StandardError=syslog
[Install]
WantedBy=multi-user.target

mkdir -p /var/lib/heketi
mkdir -p /etc/heketi
vim /etc/heketi/heketi.json

{
"_port_comment": "Heketi Server Port Number",
"port": "18080",

"_use_auth": "Enable JWT authorization. Please enable for deployment",
"use_auth": true,

"_jwt": "Private keys for access",
"jwt": {
"_admin": "Admin has access to all APIs",
"admin": {
"key": "adminkey"
},
"_user": "User only has access to /volumes endpoint",
"user": {
"key": "just4fun"
}
},

"_glusterfs_comment": "GlusterFS Configuration",
"glusterfs": {
"_executor_comment": [
"Execute plugin. Possible choices: mock, ssh",
"mock: This setting is used for testing and development.",
" It will not send commands to any node.",
"ssh: This setting will notify Heketi to ssh to the nodes.",
" It will need the values in sshexec to be configured.",
"kubernetes: Communicate with GlusterFS containers over",
" Kubernetes exec api."
],
"executor": "ssh",

"_sshexec_comment": "SSH username and private key file information",
"sshexec": {
"keyfile": "/etc/heketi/heketi_key",
"user": "root",
"port": "22",
"fstab": "/etc/fstab"
},

"_kubeexec_comment": "Kubernetes configuration",
"kubeexec": {
"host" :"https://kubernetes.host:8443",
"cert" : "/path/to/crt.file",
"insecure": false,
"user": "kubernetes username",
"password": "password for kubernetes user",
"namespace": "OpenShift project or Kubernetes namespace",
"fstab": "Optional: Specify fstab file on node. Default is /etc/fstab"
},

"_db_comment": "Database file name",
"db": "/var/lib/heketi/heketi.db",

"_loglevel_comment": [
"Set log level. Choices are:",
" none, critical, error, warning, info, debug",
"Default is warning"
],
"loglevel" : "warning"
}
}

ssh-keygen -t rsa -f /root/.ssh/id_rsa -N ''
ssh-copy-id 192.168.66.60
ssh-copy-id 192.168.66.61
ssh-copy-id 192.168.66.62
cd ~
cp .ssh/id_rsa /etc/heketi/heketi_key
systemctl enable heketi
systemctl start heketi
systemctl status heketi

curl 127.0.0.1:18080/hello
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
heketi-cli --user admin --secret adminkey --server http://192.168.66.20:18080 --json cluster create
{"id":"de8edfdd859e290d14d95d0de373d056","nodes":[],"volumes":[],"block":true,"file":true,"blockvolumes":[]}#

# 删除操作
heketi-cli --user admin --secret adminkey --server http://192.168.66.20:18080 --json cluster delete de8edfdd859e290d14d95d0de373d056

# 显示
heketi-cli --user admin --secret adminkey --server http://192.168.66.20:18080 --json cluster list

# 在g1、g2、g3节点上都加入
heketi-cli --user admin --secret adminkey --server http://192.168.66.20:18080 --json node add --cluster "de8edfdd859e290d14d95d0de373d056" --management-host-name 192.168.66.60 --storage-host-name 192.168.66.60 --zone 1

heketi-cli --user admin --secret adminkey --server http://192.168.66.20:18080 --json node add --cluster "de8edfdd859e290d14d95d0de373d056" --management-host-name 192.168.66.61 --storage-host-name 192.168.66.61 --zone 1

heketi-cli --user admin --secret adminkey --server http://192.168.66.20:18080 --json node add --cluster "de8edfdd859e290d14d95d0de373d056" --management-host-name 192.168.66.62 --storage-host-name 192.168.66.62 --zone 1

# 查看每个节点
heketi-cli --user admin --secret adminkey --server http://192.168.66.20:18080 --json node list

记录问题:

Feb 29 20:47:14 master01 heketi[1017187]: [cmdexec] WARNING 2024/02/29 20:47:14 Failed to create SSH connection to 19

Feb 29 20:43:47 master01 heketi[1017187]: [cmdexec] ERROR 2024/02/29 20:43:47 heketi/executors/cmdexec/peer.go:80:cmd
Feb 29 20:43:47 master01 heketi[1017187]: [heketi] ERROR 2024/02/29 20:43:47 heketi/apps/glusterfs/app_node.go:107:gl
Feb 29 20:43:47 master01 heketi[1017187]: [heketi] ERROR 2024/02/29 20:43:47 heketi/apps/glusterfs/app_node.go:108:gl

解决办法:

Heketi只支持旧式的RSA SSH密钥,而这些密钥在现代的SSH安装中默认被拒绝。将RSA密钥添加到SSH接受的密钥类型列表中可以让Heketi正常工作。

在每个gluster节点上的/etc/ssh/sshd_config文件中加入

PubkeyAcceptedKeyTypes=+ssh-rsa

然后systemctl restart sshd重新加入既可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
去三个gluster节点上分别增加一个硬盘
lsblk ─╯
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
loop0 7:0 0 63.4M 1 loop /snap/core20/1974
loop1 7:1 0 63.9M 1 loop /snap/core20/2182
loop2 7:2 0 111.9M 1 loop /snap/lxd/24322
loop3 7:3 0 87M 1 loop /snap/lxd/27037
loop4 7:4 0 53.3M 1 loop /snap/snapd/19457
loop5 7:5 0 40.4M 1 loop /snap/snapd/20671
sda 8:0 0 40G 0 disk
|-sda1 8:1 0 1M 0 part
|-sda2 8:2 0 2G 0 part /boot
`-sda3 8:3 0 38G 0 part
`-ubuntu--vg-ubuntu--lv 252:0 0 38G 0 lvm /
sdb 8:16 0 40G 0 disk /glustersdb
sdc 8:32 0 40G 0 disk
sr0 11:0 1 2G 0 rom

# 分别在三个节点上执行
heketi-cli --user admin --secret adminkey --server http://192.168.66.20:18080 device add --name "/dev/sdc" --node {847076480c3c45f83420225228189355}

heketi-cli --user admin --secret adminkey --server http://192.168.66.20:18080 topology info

heketi-cli --user admin --secret adminkey --server http://192.168.66.20:18080 volume create --size=5 --replica=2

heketi-cli --user admin --secret adminkey --server http://192.168.66.20:18080 volume list

在k8s中使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[root@master01 yaml]# cat storageclass-gluserfs.yaml

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: glusterfs
provisioner: kubernetes.io/glusterfs #表示存储分配器,需要根据后端存储的不同而变更
parameters:
resturl: "http://192.168.66.20:18080" #heketi API服务提供的URL,为k8s集群master节点IP
restauthenabled: "true" #可选参数,默认为"false",heketi服务开启认证时,必须设置为"true"
restuser: "admin" #可选参数,开启认证时设置相应用户名
restuserkey: "adminkey" #可选,开启认证时设置密码
volumetype: "replicate:2" #可选参数,设置卷类型及其参数,如果未分配卷类型,则有分配器决定卷类型;如”volumetype: replicate:3”表示3副本的replicate卷,”volumetype: disperse:4:2”表示disperse卷,其中‘4’是数据,’2’是冗余校验,”volumetype: none”表示distribute卷

k get storageclasses

创建pvc(不可使用,已与v1.25启用storage-class的值glusterfs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[root@master01 yaml]# cat glusterfs-pvc.yaml

kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: glusterfs-mysql
namespace: default
annotations:
volume.beta.kubernetes.io/storage-class: "glusterfs" # 不能使用
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 2Gi

Gluster不支持了,就没学了,如果有需要看笔记https://cloud.fynote.com/share/d/12179

Ceph

Ceph概述

一、Ceph的起源

Ceph是一个统一的分布式存储系统,设计初衷是提供较好的性能、可靠性和可扩展性。Ceph项目最早起源于Sage就读博士期间的工作(最早的成果于2004年发表),并随后贡献给开源社区。在经过了数年的发展之后,目前已得到众多云计算厂商的支持并被广泛应用。RedHat及OpenStack都可与Ceph整合,为物理机及虚拟机提供块存储,对象存储与文件存储。由于AWS的对象存储S3与OpenStack的对象存储swift并不兼容,而Ceph的对象存储RADOS(Reliable Autonomic Distributed Object Store) GW可以同时兼容二者,起到桥接作用。

二、Ceph的特点

  1. 接口类型

    说Ceph是一个统一的分布式存储系统,是因为Ceph底层使用RADOS分布式存储系统,该存储系统对外提供librados接口进行调用,使用者可根据该接口自行开发客户端,考虑到易用性,Ceph官方提供了RDB,RADOS GW和CEPH FS接口,对外提供块存储,对象存储和文件存储,其中RDB和RADOS GW是给予librados的二次封装,而CEPH FS并未使用librados封装,直接使用RADOWS对象存储。

  2. 存储引擎

    ceph后端支持多种存储引擎,以插件化的形式来进行管理使用,目前支持filestore,kvstore,memstore以及bluestore。Filestore因为仍然需要通过操作系统自带的本地文件系统间接管理磁盘,所以所有针对RADOS层的对象操作,都需要预先转换为能够被本地文件系统识别、符合POSIX语义的文件操作。这个转换的过程极其繁琐,效率低下,目前默认使用的是bluestore,即直接使用本地磁盘裸设备。

  3. 层级结构

    对象存储本质上是一个平层结构,不像传统的文件系统倒装树接口,在对象存储下,所有对象位于同一层级,通过K/V方式进行查找,典型的应用就是我们日常见到的云盘,而云盘中不同的目录,在对象存储中是以“/”进行表示,所以对于对象存储而言,云盘中的所有对象都在一个层级目录下。

  4. Ceph存储网络

    Ceph存储网络分为public network和cluster network两个平面,当仅有一张网卡时,两个平面可以共用一个网络出口,生产环境中推荐两个平面分开。
    ● public network:服务端响应客户端请求的流量
    ● cluster network:OSD之间的心跳,object之间的复制和修复流量

  5. Ceph组件

    Ceph 存储集群需要至少一个 Ceph Monitor 、 Ceph Manager和 Ceph OSD ,如果需要对外提供 Ceph FS,需要额外部署 Ceph Metadata。

    ● Monitors:Ceph Monitor (Ceph-mon),负责维护集群状态信息,包括monitor map、manager map、 OSD map、 MDS map和 CRUSH map。这些map是 Ceph 集群进程间彼此通信的关键信息。monitor还负责服务进程和客户端之间的身份验证。通常monitor需要至少三节点部署来保证冗余和高可用性。
    ● Managers:Ceph Manager 守护进程(Ceph-mgr),负责跟踪 Ceph 集群运行时的当前指标状态,包括存储利用率、当前性能指标和系统负载,并且提供 Web 页面的 Ceph Dashboard和 REST API,供监控程序调用,通常mgr为双节点部署。
    ● Ceph OSDs:Ceph OSD (对象存储守护进程,Ceph-Object Storage Daemon),负责存储数据,处理数据之间的复制、恢复、重新平衡,并向 Ceph Monitor 和 Manager 提供其他 OSD 的心跳,一般高可用性场景下至少需要三个 Ceph OSD。
    ● MDSs:Ceph 元数据服务器(MDS,Ceph-Metadata Server),存储 Ceph FS的元数据(块存储和对象存储不需要使用 MDS)。Ceph 元数据服务器允许 POSIX 文件系统用户执行如 ls、 find 等基本命令 ,而不会给 Ceph 存储集群带来巨大负担。

三、Ceph中的几个概念

在弄清Ceph的映射关系前,先搞清楚Ceph中的三个重要概念:object、PG、OSD。

  1. Object

    Ceph本身是一个对象存储,为了和客户端待存储对象进行区分,下文中将待存储的对象统称为file,Ceph切分后的对象统称为object。Ceph存储时,是将一个file切分为多个object(默认4M大小),每个object经过两重映射,最终存放到RADOS对象存储上,所以无论是块存储,对象存储还是文件存储都可以看作是RADOS存储的二次封装。

    image.png

  2. OSD(Object Storage Daemon)

    RADOS存储内,每一个最小存储单元称为一个OSD,通常每一个OSD代表一块物理磁盘,而该磁盘所在的物理节点上,每一个物理磁盘需要有一个对应的OSD进程来相应客户请求。

  3. PG

    PG全称placement group,直译为归置组,他本身是一个逻辑概念。通过前文得知,每一台物理节点上面会有若干个磁盘设备,在ceph集群中,每一个磁盘设备需要运行一个OSD进程管理,而OSD之间会相互监听心跳信息并及时上报给monitor,而monitor也会监视OSD的状态。如果让monitor监视每一个OSD,当OSD发生故障时进行数据恢复,这样物理节点资源开销很大;如果以物理节点为单位,当物理节点发生故障时再进行数据恢复,恢复的数据量又会非常大,为了兼顾性能与速度,所以在中间加入了一个逻辑归置组PG,其特点如下:
    ● PG是一个存储池(pool)的最小基本单元,数据在不同节点之间的同步、恢复时的数据修复也都是依赖PG完成。
    ● PG位于object和RADOS的中间,往上负责接收和处理来自客户端的请求,往下负责将这些数据请求翻译为能够被本地对象存储所能理解的事务。
    ● 在ceph中数据存储到集群后端可以简单的分为两步:object –> PG,PG –> OSD。

四、Ceph的存储映射
客户端将一个数据存储到Ceph集群上,会经过两次映射,object –> PG,PG –> OSD,以下分别对这两次映射进行说明。
1.object –> PG(一致性哈希算法)
Ceph存储池在创建时要求指定PG数量(假设为32),客户端数据在存储时默认会以4M为单位切分成多个object,以一个object为例,该object通过一致性哈希算法对32取模,最终选取一个PG,其余object进行类似操作,所有object可能分布于不同PG,但同一object多次访问会得到相同PG,通过这种伪随机算法提升I/O性能。
2.PG –> OSD(crush算法)
CRUSH算法通过计算数据存储位置来确定如何存储和检索数据。CRUSH使Ceph客户机能够直接与OSDs通信,而不是通过集中的服务器或代理。通过算法确定的数据存储和检索方法,Ceph避免了单点故障、性能瓶颈和对其可伸缩性的物理限制(后续会出一篇专门说明crush算法),此时数据存储过程如下:

  • 客户端向monitor节点发起请求,获取到集群状态信息
  • 客户端经过本地计算,得到数据存放的OSD位置
  • 客户端直接向primaryOSD发起读写请求
  • primary OSD负责向second OSD发起数据同步
  • second OSD复制完成后向primary OSD进行确认

后续看初识Ceph之1——通过Cephadm安装部署大羹不和的技术博客51CTO博客

配置DNS Server

https://cloud.fynote.com/share/d/12061#3-2-%E5%9F%9F%E5%90%8D%E8%A7%A3%E6%9E%90-DNS%E9%85%8D%E7%BD%AE_9

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
vim /etc/netplan/xxx.yaml

network:
version: 2
renderer: NetworkManager
ethernets:
ens33:
dhcp4: no
addresses:
- 192.168.66.211/24
routes:
- to: default
via: 192.168.66.1
nameservers:
addresses: [192.168.66.211, 119.29.29.29]

apt install bind9

vim /etc/named.conf.options

改成:

options {
directory "/var/cache/bind";

listen-on port 53 {192.168.66.211;any;};
// If there is a firewall between you and nameservers you want
// to talk to, you may need to fix the firewall to allow multiple
// ports to talk. See http://www.kb.cert.org/vuls/id/800113

// If your ISP provided one or more IP addresses for stable
// nameservers, you probably want to use them as forwarders.
// Uncomment the following block, and insert the addresses replacing
// the all-0's placeholder.

forwarders {
// 0.0.0.0;
192.168.66.211;
};

//========================================================================
// If BIND logs error messages about the root key being expired,
// you will need to update your keys. See https://www.isc.org/bind-keys
//========================================================================
dnssec-validation auto;

listen-on-v6 { any; };
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
vim named.conf.local
添加如下内容:

zone "kubemsb.com" IN{ // “kubemsb.com”可以理解为监听区域,根据自己的域名设置
type master;
file "/etc/bind/zones/kubemsb.com.zone"; // 区文件路径(稍后在相应位置创建区文件就好,路径不必严格照搬)

}; // zone可以定义多个

mkdir zones
cp -p db.empty /etc/bind/zones/kubemsb.com.zone # 注意 kubemsb.com.zone 必须和上面定义的路径一致,使用-p复制,保证所属权也复制过来
vim kubemsb.com.zone

; BIND reverse data file for empty rfc1918 zone
;
; DO NOT EDIT THIS FILE - it is used for multiple zones.
; Instead, copy it, edit named.conf, and use that copy.
;
$TTL 86400
@ IN SOA kubemsb.com admin.kubemsb.com. (
1 ; Serial
604800 ; Refresh
86400 ; Retry
2419200 ; Expire
86400 ) ; Negative Cache TTL
;
@ IN NS localhost.
;kubemsb.com IN NS 192.168.66.211
;@ IN A 127.0.0.1
;@ IN AAAA ::1

ns A 192.168.66.211
yaml A 192.168.66.212
harbor A 192.168.66.213
nfs A 192.168.66.214

@:表示当前域
NS:域名服务器
“;”:注释

验证

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
vim /etc/resolv.conf
改成 nameserver 192.168.66.211
nameserver 119.29.29.29

named-checkconf # 检测是否配置正确
sudo apt install dnsutils
systemctl enable named && systemctl start named
systemctl status named

nslookup
server
yaml.kubemsb.com
看能否正常解析
查看DNS服务器

改完之后要去其他的主机上修改dns服务器地址


K8S 笔记
http://example.com/2025/05/03/My-New-Post/
作者
weting
发布于
2025年5月3日
许可协议