一.环境配置
1.构建harbor镜像仓库
habor,master,node1,node2都要做"1.安装docker"
1.安装docker
bash
[root@harbor ~]# cat > /etc/yum.repos.d/docker.repo <<EOF
[docker]
name = docker
baseurl = https://mirrors.aliyun.com/docker-ce/linux/rhel/9.6/x86_64/stable/
gpgcheck = 0
EOF
[root@harbor ~]# dnf install docker-ce-3:28.5.2-1.el9 -y
[root@harbor ~]# echo br_netfilter > /etc/modules-load.d/docker_mod.conf
[root@harbor ~]# modprobe -a br_netfilter
[root@harbor ~]# vim /etc/sysctl.d/docker.conf
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
[root@harbor ~]# sysctl --system
[root@harbor ~]# vim /lib/systemd/system/docker.service
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock --iptables=true
[root@harbor ~]# systemctl daemon-reload
[root@harbor ~]# systemctl enable --now docker
2.在harbor上生成key
bash
[root@harbor ~]# mkdir /data/certs -p
[root@harbor ~]# mkdir /data/certs -p
[root@harbor ~]# openssl req -newkey rsa:4096 \
-nodes -sha256 -keyout /data/certs/timinglee.org.key \
-addext "subjectAltName = DNS:reg.timinglee.org" \
-x509 -days 365 -out /data/certs/timinglee.org.crt
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) [XX]:CN
State or Province Name (full name) []:Shannxi
Locality Name (eg, city) [Default City]:Xi'an
Organization Name (eg, company) [Default Company Ltd]:kubernetes
Organizational Unit Name (eg, section) []:harbor
Common Name (eg, your name or your server's hostname) []:reg.timinglee.org
Email Address []:admin@timinglee.org
3.编辑harbor配置文件
bash
[root@harbor ~]# tar zxf harbor-offline-installer-v2.5.4.tgz -C /opt/
[root@harbor ~]# cd /opt/harbor/
[root@harbor harbor]# ls
common.sh harbor.v2.5.4.tar.gz harbor.yml.tmpl install.sh LICENSE prepare
[root@harbor harbor]# cp harbor.yml.tmpl harbor.yml
[root@harbor harbor]# vim harbor.yml
hostname: reg.timinglee.org
certificate: /data/certs/timinglee.org.crt
private_key: /data/certs/timinglee.org.key
harbor_admin_password: lee
[root@harbor harbor]# ./install.sh --with-chartmuseum
4.启动并验证
bash
[root@harbor harbor]# mkdir /etc/docker/certs.d/reg.timinglee.org/ -p
[root@harbor harbor]# cp /data/certs/timinglee.org.crt /etc/docker/certs.d/reg.timinglee.org/ca.crt
[root@harbor harbor]# vim /etc/hosts
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
172.25.254.200 harbor reg.timinglee.org
#切换到harbor目录下
[root@harbor harbor]# systemctl restart docker
[root@harbor harbor]# docker compose up -d
[root@harbor harbor]# docker login reg.timinglee.org -u admin
Password:
WARNING! Your credentials are stored unencrypted in '/root/.docker/config.json'.
Configure a credential helper to remove this warning. See
https://docs.docker.com/go/credential-store/
Login Succeeded
2.构建部署kubernetes所需主机
| 主机名 | ip | 配置 | 角色 |
|---|---|---|---|
| harbor.timinglee.org | 172.25.254.254 | cpu1、mem 1G | harbor仓库 |
| master-node | 172.25.254.100 | cpu4、mem>4G | master,k8s集群控制节点 |
| node1 | 172.25.254.10 | cpu2、mem 2G | worker,k8s集群工作节点 |
| node2 | 172.25.254.20 | cpu2、mem 2G | worker,k8s集群工作节点 |
1.所有主机配置
关闭swap
bash
systemctl disable --now swap.target
systemctl mask swap.target
sed '/swap/s/^/#/g' -i /etc/fstab
安装docker
配置可以使用harbor仓库
bash
mkdir /etc/docker/certs.d/reg.timinglee.org/ -p
#在harbor主机中分发证书到所有主机
[root@harbor ~]# for i in 100 10 20
> do
> scp /data/certs/timinglee.org.crt root@172.25.254.$i:/etc/docker/certs.d/reg.timinglee.org/ca.crt
> done
systemctl enable docker
systemctl restart docker
所有主机配置docker加速器
bash
cat >/etc/docker/daemon.json <<EOF
{
"registry-mirrors":["https://reg.timinglee.org"]
}
EOF
systemctl restart docker
docker info
可以看到
Registry Mirrors:
https://reg.timinglee.org/
所有主机彼此建立解析
bash
vim /etc/hosts
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
172.25.254.100 master
172.25.254.10 node1
172.25.254.20 node2
172.25.254.200 reg.timinglee.org
所有主机配置kubernetes安装源
bash
vim /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name = kubernetes
baseurl = https://mirrors.aliyun.com/kubernetes-new/core/stable/v1.35/rpm/
gpgcheck = 0
#检测
dnf list kubelet
以上操作完成后重启主机检测swap分区
bash
swapoff -a
swapon -s 没有任何输出表示ok
3.kubernetes的部署
1.安装cri-dockerd(所有主机中安装)
bash
[root@master ~]# ls
anaconda-ks.cfg cri-dockerd-0.3.14-3.el8.x86_64.rpm libcgroup-0.41-19.el8.x86_64.rpm
[root@master ~]# rpm -ivh *.rpm
警告:libcgroup-0.41-19.el8.x86_64.rpm: 头V4 RSA/SHA256 Signature, 密钥 ID 6d745a60: NOKEY
Verifying... ################################# [100%]
准备中... ################################# [100%]
正在升级/安装...
1:libcgroup-0.41-19.el8 ################################# [ 50%]
2:cri-dockerd-3:0.3.14-3.el8 ################################# [100%]
#添加这个才能让集群里的node节点添加上网路插件
vim /lib/systemd/system/cri-docker.service
ExecStart=/usr/bin/cri-dockerd --container-runtime-endpoint fd:// --network-plugin=cni --pod-infra-container-image=reg.timinglee.org/k8s/pause:3.10.1
# 重新加载 systemd 配置并启动服务
systemctl daemon-reload
systemctl enable --now cri-docker
ll /var/run/cri-dockerd.sock
2.安装构建kubernetes 集群所需软件
master节点
bash
dnf install kubelet kubeadm kubectl -y
systemctl enable --now kubelet.service
node节点
bash
dnf install kubelet kubeadm -y
systemctl enable --now kubelet.service
master节点中 kubectl 和kubeadm 补齐
bash
[root@master ~]# echo "source <(kubectl completion bash)" >> ~/.bashrc
[root@master ~]# echo "source <(kubeadm completion bash)" >> ~/.bashrc
[root@master ~]# source ~/.bashrc
3.下载kubernetes集群所需镜像
下载镜像
bash
[root@master ~]# kubeadm config images pull \
--image-repository registry.aliyuncs.com/google_containers \
--kubernetes-version v1.35.3 \
--cri-socket=unix:///var/run/cri-dockerd.sock
[config/images] Pulled registry.aliyuncs.com/google_containers/kube-apiserver:v1.35.3
[config/images] Pulled registry.aliyuncs.com/google_containers/kube-controller-manager:v1.35.3
[config/images] Pulled registry.aliyuncs.com/google_containers/kube-scheduler:v1.35.3
[config/images] Pulled registry.aliyuncs.com/google_containers/kube-proxy:v1.35.3
[config/images] Pulled registry.aliyuncs.com/google_containers/coredns:v1.13.1
[config/images] Pulled registry.aliyuncs.com/google_containers/pause:3.10.1
[config/images] Pulled registry.aliyuncs.com/google_containers/etcd:3.6.6-0
上传镜像到本地harbor
bash
[root@master ~]# docker login reg.timinglee.org -u admin
Password:
WARNING! Your credentials are stored unencrypted in '/root/.docker/config.json'.
Configure a credential helper to remove this warning. See
https://docs.docker.com/go/credential-store/
Login Succeeded
#提前在harbor上创建k8s目录
[root@master ~]# docker images --format "{{.Repository}}:{{.Tag}}" | awk -F "/" '/google/{system("docker tag "$0" reg.timinglee.org/k8s/"$3)}'
[root@master ~]# docker images --format "{{.Repository}}:{{.Tag}}" | awk -F "/" '/timinglee/{system("docker push "$0)}'
4.在master中初始化kubernetes集群
在master中完成集群初始化
bash
[root@master ~]# kubeadm init --pod-network-cidr=10.244.0.0/16 \
--image-repository reg.timinglee.org/k8s \
--kubernetes-version v1.35.3 \
--cri-socket=unix:///var/run/cri-dockerd.sock
。。。
#每个人不一样,其他主机加入本集群的凭证
kubeadm join 172.25.254.100:6443 --token tdwjoc.8d1yw3wl4r4tm6c4 \
--discovery-token-ca-cert-hash sha256:6b5950ef2cdba85d6dfdb564ee90d4187fa3d341767dc9852cbdd5c9dee4f927
#如果忘记
[root@master ~]# kubeadm token create --print-join-command
kubeadm join 172.25.254.100:6443 --token jl4ztx.cax3iysvu7onsh5s --discovery-token-ca-cert-hash sha256:6b5950ef2cdba85d6dfdb564ee90d4187fa3d341767dc9852cbdd5c9dee4f927
#如果初始化出问题
[root@master ~]# systemctl daemon-reload
[root@master ~]# systemctl restart cri-docker [root@master ~]# systemctl restart docker
[root@master ~]# systemctl restart kubelet
[root@master ~]# kubeadm reset --cri-socket=unix:///var/run/cri-dockerd.sock #可以重置集群设定
如果还是不行看看是不是/usr/lib/systemd/system/cri-docker.service里的ExecStart=/usr/bin/cri-dockerd --container-runtime-endpoint fd://后面是不是没有加 --network-plugin=cni --pod-infra-container-image=reg.timinglee.org/k8s/pause:3.10.1或者是加错了
添加kubernets环境变量到本机
bash
[root@master ~]# echo "export KUBECONFIG=/etc/kubernetes/admin.conf" >> ~/.bash_profile
[root@master ~]# source ~/.bash_profile
[root@master ~]# kubectl get nodes
NAME STATUS ROLES AGE VERSION
master NotReady control-plane 102s v1.35.3
添加node节点到本集群
bash
[root@node1 ~]# kubeadm join 172.25.254.100:6443 --token jl4ztx.cax3iysvu7onsh5s --discovery-token-ca-cert-hash sha256:6b5950ef2cdba85d6dfdb564ee90d4187fa3d341767dc9852cbdd5c9dee4f927 --cri-socket=unix:///var/run/cri-dockerd.sock
#测试
[root@master ~]# kubectl get nodes #可以看到集群中主机但是因为网络插件问题状态是NotReady
NAME STATUS ROLES AGE VERSION
master NotReady control-plane 5m16s v1.35.3
node1 NotReady <none> 66s v1.35.3
node2 NotReady <none> 58s v1.35.3
5.安装网络插件
bash
[root@master ~]# ls
anaconda-ks.cfg flannel-0.28.1.tar libcgroup-0.41-19.el8.x86_64.rpm
cri-dockerd-0.3.14-3.el8.x86_64.rpm kube-flannel.yml
[root@master ~]# docker load -i flannel-0.28.1.tar
# 在harbor里面创建flannel-io文件
[root@master ~]# docker tag ghcr.io/flannel-io/flannel-cni-plugin:v1.9.0-flannel1 reg.timinglee.org/flannel-io/flannel-cni-plugin:v1.9.0-flannel1
[root@master ~]# docker push reg.timinglee.org/flannel-io/flannel-cni-plugin:v1.9.0-flannel1
The push refers to repository [reg.timinglee.org/flannel-io/flannel-cni-plugin]
2c8aa52d4746: Pushed
5aa68bbbc67e: Pushed
v1.9.0-flannel1: digest: sha256:b3d30c221113b30fea3e8a7fccb145e929b097d0319b9eeb6b5a591b10b5c671 size: 739
[root@master ~]# docker tag ghcr.io/flannel-io/flannel:v0.28.1 reg.timinglee.org/flannel-io/flannel:v0.28.1
[root@master ~]# docker push reg.timinglee.org/flannel-io/flannel:v0.28.1
The push refers to repository [reg.timinglee.org/flannel-io/flannel]
5668da16a30b: Pushed
5f70bf18a086: Pushed
00012e17b6cc: Pushed
9738bb9596cf: Pushed
b6233bc105d7: Pushed
03767449b95b: Pushed
9a7d8cf9ff51: Pushed
13a60c10faeb: Pushed
70dc5e033175: Pushed
256f393e029f: Pushed
v0.28.1: digest: sha256:e671adbc267460164555159210066d3304a43e3b5dd85cc0b5b6ad62e83aab52 size: 2414
#使用/image:然后回车,把后面的删掉一些,因为docker info配置了https://reg.timinglee.org/
[root@master ~]# vim kube-flannel.yml
image: flannel-io/flannel:v0.28.1
image: flannel-io/flannel-cni-plugin:v1.9.0-flannel1
image: flannel-io/flannel:v0.28.1
[root@master ~]# docker info
Registry Mirrors:
https://reg.timinglee.org/
Live Restore Enabled: false
Firewall Backend: iptables
[root@master ~]# kubectl apply -f kube-flannel.yml
namespace/kube-flannel created
serviceaccount/flannel created
clusterrole.rbac.authorization.k8s.io/flannel created
clusterrolebinding.rbac.authorization.k8s.io/flannel created
configmap/kube-flannel-cfg created
daemonset.apps/kube-flannel-ds created
#测试,之后过一会就能看见了
[root@master ~]# kubectl get nodes
NAME STATUS ROLES AGE VERSION
master Ready control-plane 13m v1.35.3
node1 Ready <none> 9m44s v1.35.3
node2 Ready <none> 9m36s v1.35.3
二.资源使用的方法pods
1.命令式
bash
#先下载nginx:latest镜像
[root@master ~]# kubectl run webpod --image nginx:latest --port 80
[root@master ~]# kubectl get pods
webpod 1/1 Running 0 33s
[root@master ~]# kubectl describe pods webpod
Name: webpod
Namespace: default
Priority: 0
Service Account: default
Node: node2/172.25.254.20
Start Time: Sun, 29 Mar 2026 10:18:15 +0800
Labels: run=webpod
Annotations: <none>
Status: Running
IP: 10.244.2.2
IPs:
IP: 10.244.2.2
Containers:
webpod:
Container ID: docker://688436b733ca8843f6946814f78bf59f865877081820ca43dda1c33f409ac10d
Image: nginx:latest
Image ID: docker-pullable://nginx@sha256:127262f8c4c716652d0e7863bba3b8c45bc9214a57d13786c854272102f7c945
Port: 80/TCP
Host Port: 0/TCP
State: Running
Started: Sun, 29 Mar 2026 10:18:30 +0800
Ready: True
Restart Count: 0
Environment: <none>
Mounts:
/var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-nt87d (ro)
Conditions:
Type Status
PodReadyToStartContainers True
Initialized True
Ready True
ContainersReady True
PodScheduled True
Volumes:
kube-api-access-nt87d:
Type: Projected (a volume that contains injected data from multiple sources)
TokenExpirationSeconds: 3607
ConfigMapName: kube-root-ca.crt
Optional: false
DownwardAPI: true
QoS Class: BestEffort
Node-Selectors: <none>
Tolerations: node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 24s default-scheduler Successfully assigned default/webpod to node2
Normal Pulling 24s kubelet spec.containers{webpod}: Pulling image "nginx:latest"
Normal Pulled 20s kubelet spec.containers{webpod}: Successfully pulled image "nginx:latest" in 3.7s (3.7s including waiting). Image size: 187694648 bytes.
Normal Created 9s kubelet spec.containers{webpod}: Container created
Normal Started 9s kubelet spec.containers{webpod}: Container started
[root@master ~]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
webpod 1/1 Running 0 2m46s 10.244.2.2 node2 <none> <none>
[root@master ~]# kubectl delete pods webpod
pod "webpod" deleted from default namespace
2.yaml文件方式
bash
[root@master ~]# kubectl create deployment test --image nginx --replicas 1 --dry-run=client -o yaml > test.yml
[root@master ~]# vim test.yml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: test
name: test
spec:
replicas: 1
selector:
matchLabels:
app: test
template:
metadata:
labels:
app: test
spec:
containers:
- image: nginx
name: nginx
#建立式
[root@master ~]# kubectl create -f test.yml
deployment.apps/test created
[root@master ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
test-56848fd9dc-h2sct 1/1 Running 0 8s
[root@master ~]# kubectl delete -f test.yml
deployment.apps "test" deleted from default namespace
[root@master ~]# kubectl get pods
No resources found in default namespace.
#声明式
[root@master ~]# kubectl apply -f test.yml
deployment.apps/test created
[root@master ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
test-56848fd9dc-cxtnp 1/1 Running 0 1s
#注意建立只能建立不能更新,声明可以
[root@master ~]# vim test.yml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: test
name: test
spec:
replicas: 2 #只修改pod数量
。。。。。。。。。。。。。。。。。。
[root@master ~]# kubectl create -f test.yml
Error from server (AlreadyExists): error when creating "test.yml": deployments.apps "test" already exists
[root@master ~]# kubectl apply -f test.yml
deployment.apps/test configured
[root@master ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
test-56848fd9dc-9sw95 1/1 Running 0 8s
test-56848fd9dc-cxtnp 1/1 Running 0 2m42s
3.K8S 核心资源 & 常用 kubectl 命令实操
环境:K8s v1.35.3 三节点集群(1master+2node)
节点是 K8s 底层宿主机,全部 Ready 代表集群基础正常
bash
# 查看所有节点
kubectl get nodes
# 产出
master Ready control-plane 18h v1.35.3
node1 Ready <none> 17h v1.35.3
node2 Ready <none> 17h v1.35.3
# 生成节点加入集群命令
kubeadm token create --print-join-command
# 查看所有默认ns
kubectl get ns
# 查看网络组件专属ns的pod
kubectl -n kube-flannel get pods
# 自定义业务ns
kubectl create namespace timinglee
# 指定ns创建nginx pod
kubectl -n timinglee run testpod --image nginx:latest
# 查看指定ns pod
kubectl -n timinglee get pods
# 同名pod重复创建会报错:已存在
# 交互式启动busybox
kubectl run testpod -it --image busybox
# Ctrl+P+Q 后台退出
# 重新附着终端
kubectl attach pods/testpod -it
# 进入容器shell(生产常用)
kubectl exec -it pods/testpod -c testpod -- /bin/sh
# 本地文件拷入pod
kubectl cp testpod.yml testpod:/ -c testpod
# 查看部署
kubectl get deployments
# 编辑修改副本数
kubectl edit deploy test
# 补丁修改副本
kubectl patch deploy test -p '{"spec":{"replicas":1}}'
# 命令行快速扩缩容
kubectl scale deployment test --replicas=6
kubectl scale deployment test --replicas=1
# 暴露deployment,内部访问80
kubectl expose deployment test --port 80 --target-port 80
# 查看service详情
kubectl describe svc test
# 集群内访问测试
curl 10.104.29.36
# 查看pod日志
kubectl logs pod/xxx
# 查看pod及标签
kubectl get pods --show-labels
# 给pod打标签
kubectl label pods testpod name=lee
# 删除指定标签(key后加-)
kubectl label pods testpod name-
4.Pod应用
1.自助式管理pod
bash
#先在harbor上推送myapp:v1,myapp:v2
[root@master pod]# kubectl run myappv2 --image myapp:v2 --port 80
pod/myappv2 created
[root@master pod]# kubectl get pods
NAME READY STATUS RESTARTS AGE
myappv2 0/1 ContainerCreating 0 8s #创建中
[root@master pod]# kubectl get pods
NAME READY STATUS RESTARTS AGE
myappv2 0/1 ErrImagePull 0 20s #镜像拉取失败
[root@master pod]# kubectl get pods
NAME READY STATUS RESTARTS AGE
myappv2 0/1 ImagePullBackOff 0 3m48s #尝试从新拉去镜像
[root@master pod]# kubectl get pods
NAME READY STATUS RESTARTS AGE
myappv2 1/1 Running 0 4m20s
[root@master pod]# kubectl delete pods myappv2
pod "myappv2" deleted from default namespace
[root@master pod]# kubectl get pods
No resources found in default namespace.
2.利用控制器管理pod
bash
[root@master pod]# kubectl create deployment webcluster --image myapp:v2 --replicas 1
deployment.apps/webcluster created
[root@master pod]# kubectl get deployments.apps -o wide
NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR
webcluster 1/1 1 1 14s myapp myapp:v2 app=webcluster
#修改pods里的容器数量
[root@master pod]# kubectl scale deployment webcluster --replicas 2
deployment.apps/webcluster scaled
[root@master pod]# kubectl scale deployment webcluster --replicas 1
#把 Pod webcluster-6c8b4bb9d7-jsjws 的 app 标签删掉
[root@master pod]# kubectl label pods webcluster-6c8b4bb9d7-jsjws app-
pod/webcluster-6c8b4bb9d7-jsjws unlabeled
#把 Pod webcluster-6c8b4bb9d7-jsjws 的 app 标签改为app=webcluster
[root@master pod]# kubectl label pods webcluster-6c8b4bb9d7-jsjws app=webcluster
pod/webcluster-6c8b4bb9d7-jsjws labeled
#暴漏控制器(设定访问pod的vip)
[root@master pod]# kubectl expose deployment webcluster --port 80 --target-port 80
[root@master pod]# kubectl describe svc webcluster | tail -n 10
IP Family Policy: SingleStack
IP Families: IPv4
IP: 10.98.36.168
IPs: 10.98.36.168
Port: <unset> 80/TCP
TargetPort: 80/TCP
Endpoints: 10.244.1.12:80
Session Affinity: None
Internal Traffic Policy: Cluster
Events: <none>
[root@master pod]# curl 10.98.36.168
Hello MyApp | Version: v2 | <a href="hostname.html">Pod Name</a>
#更新版本
[root@master pod]# kubectl set image deployments webcluster myapp=myapp:v1
deployment.apps/webcluster image updated
[root@master pod]# curl 10.98.36.168
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
[root@master pod]# kubectl rollout history deployment webcluster
deployment.apps/webcluster
REVISION CHANGE-CAUSE
1 <none>
2 <none>
[root@master pod]# kubectl rollout undo deployment webcluster --to-revision 1
deployment.apps/webcluster rolled back
[root@master pod]# curl 10.98.36.168
Hello MyApp | Version: v2 | <a href="hostname.html">Pod Name</a>
#删除控制器管理的pod
[root@master ~]# kubectl get pods -o wide --show-labels
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES LABELS
webcluster-6c8b4bb9d7-gcb2p 1/1 Running 0 4m54s 10.244.2.7 node1 <none> <none> app=webcluster,pod-template-hash=6c8b4bb9d7
webcluster-6c8b4bb9d7-wqj69 1/1 Running 0 4m43s 10.244.1.4 node2 <none> <none> app=webcluster,pod-template-hash=6c8b4bb9d7
[root@master ~]# kubectl delete deployment webcluster
deployment.apps "webcluster" deleted from default namespace
[root@master ~]# kubectl get pods
No resources found in default namespace.
4.利用yaml文件部署应用
运行单个容器
bash
#运行单个容器
[root@master pod]# kubectl run lee1 --image myapp:v1 --dry-run=client -o yaml > 1test.yml
[root@master pod]# vim 1test.yml
apiVersion: v1
kind: Pod
metadata:
labels:
name: lee1
name: lee1
spec:
containers:
- image: myapp:v1
name: myappv1
[root@master pod]# kubectl apply -f 1test.yml
pod/lee1 created
[root@master pod]# kubectl get pods
NAME READY STATUS RESTARTS AGE
lee1 1/1 Running 0 2s
[root@master pod]# kubectl describe pods
Name: lee1
Namespace: default
Priority: 0
Service Account: default
Node: node2/172.25.254.20
Start Time: Sun, 29 Mar 2026 15:15:50 +0800
Labels: run=lee1
Annotations: <none>
Status: Running
IP: 10.244.2.23
IPs:
IP: 10.244.2.23
Containers:
myappv1:
Container ID: docker://3b8b569e18a5d8dcfa55fe7fb6b3abecde985e38ea1eaf72e5c5f160e638ca42
Image: myapp:v1
Image ID: docker-pullable://myapp@sha256:9eeca44ba2d410e54fccc54cbe9c021802aa8b9836a0bcf3d3229354e4c8870e
Port: <none>
Host Port: <none>
State: Running
Started: Sun, 29 Mar 2026 15:15:50 +0800
Ready: True
Restart Count: 0
Environment: <none>
Mounts:
/var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-x26c2 (ro)
Conditions:
Type Status
PodReadyToStartContainers True
Initialized True
Ready True
ContainersReady True
PodScheduled True
Volumes:
kube-api-access-x26c2:
Type: Projected (a volume that contains injected data from multiple sources)
TokenExpirationSeconds: 3607
ConfigMapName: kube-root-ca.crt
Optional: false
DownwardAPI: true
QoS Class: BestEffort
Node-Selectors: <none>
Tolerations: node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 16s default-scheduler Successfully assigned default/lee1 to node2
Normal Pulled 16s kubelet spec.containers{myappv1}: Container image "myapp:v1" already present on machine and can be accessed by the pod
Normal Created 16s kubelet spec.containers{myappv1}: Container created
Normal Started 16s kubelet spec.containers{myappv1}: Container started
[root@master pod]# kubectl get pods
NAME READY STATUS RESTARTS AGE
lee1 1/1 Running 0 78s
[root@master pod]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
lee1 1/1 Running 0 82s 10.244.2.23 node2 <none> <none>
[root@master pod]# kubectl delete -f 1test.yml
pod "lee1" deleted from default namespace
运行多个容器
bash
[root@master pod]# cp 1test.yml 2test.yml
[root@master pod]# vim 2test.yml
apiVersion: v1
kind: Pod
metadata:
labels:
run: lee1
name: lee1
spec:
containers:
- image: myapp:v1
name: myappv1
- image: busybox:latest
name: busybox
command:
- /bin/sh
- -c
- sleep 20000
[root@master pod]# kubectl apply -f 2test.yml
pod/lee1 created
[root@master pod]# kubectl get pods
NAME READY STATUS RESTARTS AGE
lee1 2/2 Running 0 4s
[root@master pod]# kubectl delete -f 2test.yml --force
理解pod间的网络整合
bvash
[root@master pod]# cp 2test.yml 3test.yml
[root@master pod]# vim 3test.yml
apiVersion: v1
kind: Pod
metadata:
labels:
run: lee1
name: lee1
spec:
containers:
- image: myapp:v1
name: myappv1
- image: busyboxplus:latest
name: busybox
command:
- /bin/sh
- -c
- sleep 20000
[root@master pod]# kubectl apply -f 3test.yml
pod/lee1 created
[root@master pod]# kubectl get pods
NAME READY STATUS RESTARTS AGE
lee1 2/2 Running 0 17s
[root@master pod]# kubectl exec -it pods/lee1 -c busybox -- /bin/sh
[ root@lee1:/ ]$ curl localhost
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
端口映射
b
[root@master pod]# cp 1test.yml 4test.yml
[root@master pod]# vim 4test.yml
apiVersion: v1
kind: Pod
metadata:
labels:
run: lee1
name: lee1
spec:
containers:
- image: myapp:v1
name: myappv1
ports:
- name: webport
containerPort: 80
hostPort: 80
protocol: TCP
[root@master pod]# kubectl apply -f 4test.yml
pod/lee1 created
[root@master pod]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
lee1 1/1 Running 0 7s 10.244.2.31 node2 <none> <none>
[root@master pod]# curl 172.25.254.20
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
选择运行节点
bash
[root@master pod]# cp 4test.yml 5test.yml
#选择两个node不同的标签来区分
[root@master ~]# kubectl get nodes --show-labels
NAME STATUS ROLES AGE VERSION LABELS
master Ready control-plane 19h v1.35.3 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=master,kubernetes.io/os=linux,node-role.kubernetes.io/control-plane=,node.kubernetes.io/exclude-from-external-load-balancers=
node1 Ready <none> 19h v1.35.3 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=node1,kubernetes.io/os=linux #io/hostname=node1
node2 Ready <none> 19h v1.35.3 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=node2,kubernetes.io/os=linux #io/hostname=node2
[root@master pod]# vim 5test.yml
apiVersion: v1
kind: Pod
metadata:
labels:
run: lee1
name: lee1
spec:
nodeSelector:
kubernetes.io/hostname: node1
containers:
- image: myapp:v1
name: myappv1
ports:
- name: webport
containerPort: 80
hostPort: 80
protocol: TCP
[root@master pod]# kubectl apply -f 5test.yml
pod/lee1 created
[root@master pod]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
lee1 1/1 Running 0 5s 10.244.1.14 node1 <none> <none>
共享宿主机网络
bash
root@master pod]# cp 5test.yml 6test.yml
[root@master pod]# vim 6test.yml
apiVersion: v1
kind: Pod
metadata:
labels:
run: lee1
name: lee1
spec:
hostNetwork: true
nodeSelector:
kubernetes.io/hostname: node1
containers:
- image: busybox:latest
name: busybox
command:
- /bin/sh
- -c
- sleep 1000
[root@master pod]# kubectl apply -f 6test.yml
pod/lee1 created
[root@master pod]# kubectl exec -it pods/lee1 -c busybox -- /bin/sh
/ #
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq qlen 1000
link/ether 00:0c:29:67:23:76 brd ff:ff:ff:ff:ff:ff
inet 172.25.254.10/24 brd 172.25.254.255 scope global noprefixroute eth0
valid_lft forever preferred_lft forever
inet6 fe80::7a35:2bf3:8ff4:9419/64 scope link noprefixroute
valid_lft forever preferred_lft forever
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue
link/ether 3a:0c:33:9f:d9:36 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
valid_lft forever preferred_lft forever
4: flannel.1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue
link/ether 06:c7:70:fe:6f:e6 brd ff:ff:ff:ff:ff:ff
inet 10.244.1.0/32 scope global flannel.1
valid_lft forever preferred_lft forever
inet6 fe80::4c7:70ff:fefe:6fe6/64 scope link
valid_lft forever preferred_lft forever
5: cni0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue qlen 1000
link/ether 7a:22:fc:84:3f:e2 brd ff:ff:ff:ff:ff:ff
inet 10.244.1.1/24 brd 10.244.1.255 scope global cni0
valid_lft forever preferred_lft forever
inet6 fe80::7822:fcff:fe84:3fe2/64 scope link
valid_lft forever preferred_lft forever
/ #
5.pod的生命周期
1.init 容器
bash
#init 容器的核心是:主容器要等它执行完、成功退出,才能启动!
[root@master pod]# cp 1test.yml init.yml
[root@master pod]# vim init.yml
apiVersion: v1
kind: Pod
metadata:
labels:
run: lee1
name: lee1
spec:
initContainers:
- name: init-myservice
image: busybox
command: ["sh","-c","until test -e /testfile;do echo wating for myservice; sleep 2;done"]
containers:
- image: myapp:v1
name: myappv1
[root@master pod]# kubectl apply -f init.yml
pod/lee1 created
[root@master pod]# watch -n 1 kubectl get pods #监控命令
NAME READY STATUS RESTARTS AGE
lee1 0/1 Init:0/1 0 3s
[root@master pod]# kubectl exec -it pods/lee1 -c init-myservice -- /bin/sh
/ #
/ #
[root@master pod]# kubectl exec -it pods/lee1 -c init-myservice -- /bin/sh
/ #
/ # touch /testfile #执行完指定的命令之后才启动容器
/ # command terminated with exit code 137
[root@master pod]# kubectl get pods
NAME READY STATUS RESTARTS AGE
lee1 1/1 Running 0 2m32s
2.livenessprobe(存活探针)
bash
#创建一个 Deployment(无状态应用控制器,,用来管理 Pod,pod的标识),名字叫 webcluster
#replicas 1(创建pod数量一个)
#--dry-run=client 只模拟,不执行
[root@master pod]# kubectl create deployment webcluster --image myapp:v1 --replicas 1 --
[root@master pod]# kubectl create deployment webcluster --image myapp:v1 --replicas 1 --dry-run=client -o yaml > liveness.yml #"帮我生成一个 YAML 文件,不真正创建资源"
[root@master pod]# kubectl expose deployment webcluster --port 80 --target-port 80 --dry-run=client -o yaml >> liveness.yml
[root@master ~]# vim liveness.yml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: webcluster
name: webcluster
spec:
replicas: 1
selector:
matchLabels:
app: webcluster
template:
metadata:
labels:
app: webcluster
spec:
containers:
- image: myapp:v1
name: myapp
livenessProbe:
exec:
command:
- /bin/bash
- -c
- killall -0 nginx
---
apiVersion: v1
kind: Service
metadata:
labels:
app: webcluster
name: webcluster
spec:
ports:
- port: 80
protocol: TCP
targetPort: 80
selector:
app: webcluster
#删除由liveness。yml创建的资源
[root@master pod]# kubectl delete -f liveness.yml
deployment.apps "webcluster" deleted from default namespace
Error from server (NotFound): error when deleting "liveness.yml": services "webcluster" not found
[root@master pod]# kubectl apply -f liveness.yml
deployment.apps/webcluster created
service/webcluster created
#测试liveness
[root@master pod]# watch -n 1 "kubectl get pods ;kubectl describe svc webcluster | tail -n 10"
NAME READY STATUS RESTARTS AGE
webcluster-584fddd575-4ttz9 1/1 Running 3 (2s ago) 92s
IP Family Policy: SingleStack
IP Families: IPv4
IP: 10.105.47.234
IPs: 10.105.47.234
Port: <unset> 80/TCP
TargetPort: 80/TCP
Endpoints: 10.244.2.36:80
Session Affinity: Non.e
Internal Traffic Policy: Cluster
Events: <none>
[root@master pod]# kubectl exec -it pods/webcluster-7fd94cc55b-pgdx6 -c myapp -- /bin/sh
/ # nginx -s stop
2026/03/29 08:50:02 [notice] 59#59: signal process started
/ # command terminated with exit code 137
3.ReadinessProbe( 就绪探针)
bash
[root@master pod]# cp liveness.yml ReadinessProbe.yml
[root@master pod]# vim ReadinessProbe.yml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: webcluster
name: webcluster
spec:
replicas: 1
selector:
matchLabels:
app: webcluster
template:
metadata:
labels:
app: webcluster
spec:
containers:
- image: myapp:v1
name: myapp
readinessProbe:
httpGet:
path: /test.html
port: 80
initialDelaySeconds: 1
periodSeconds: 3
timeoutSeconds: 1
#initialDelaySeconds: 1 #容器启动后要等待多少秒后就探针开始工作,默认是 0
#periodSeconds: 1 #执行探测的时间间隔,默认为 10s
#timeoutSeconds: 1 #探针执行检测请求后,等待响应的超时时间,默认为 1s
#如果有/test.html这个发布目录,就启动容器,否者就每间隔3秒查看一次,直到出现它然后再启动容器,期间不会删除容器
---
apiVersion: v1
kind: Service
metadata:
labels:
app: webcluster
name: webcluster
spec:
ports:
- port: 80
protocol: TCP
targetPort: 80
selector:
app: webcluster
[root@master pod]# kubectl apply -f ReadinessProbe.yml
deployment.apps/webcluster configured
service/webcluster unchanged
#监控
[root@master pod]# watch -n 1 "kubectl get pods ;kubectl describe svc webcluster | tail -n 10"
6
NAME READY STATUS RESTARTS AGE
webcluster-6bc85dfc84-zk4mn 0/1 Running 0 7s
IP Family Policy: SingleStack
IP Families: IPv4
IP: 10.107.142.60
IPs: 10.107.142.60
Port: <unset> 80/TCP
TargetPort: 80/TCP
Endpoints:
Session Affinity: None
Internal Traffic Policy: Cluster
Events: <none>
#查看上方监控变化
[root@master pod]# kubectl exec -it pods/webcluster-6bc85dfc84-zk4mn -c myapp -- /bin/sh
/ # echo timinglee > /usr/share/nginx/html/test.html
/ # rm -fr /usr/share/nginx/html/test.html
/ # echo timinglee > /usr/share/nginx/html/test.html
/ #
三.控制器
超简总结:
1.ReplicaSet:保证有 N 个 Pod 活着
2.Deployment:管理 ReplicaSet,负责升级、回滚
3.DaemonSet:每台机器跑一个 Pod
4.Job:一次性任务,跑完就结束
5.CronJob:定时触发一次性任务
1.Replicaset控制器
- ReplicaSet(副本集)
一句话:管 "同时运行几个 Pod" 的保安。
它只干一件事:
保证永远有 N 个一模一样的 Pod 在跑。
挂了一个,它立刻补一个;多了,它杀掉多余的。
它不负责更新版本,只负责 "数量够不够"。
比喻
你开餐厅,要求必须同时有 3 个厨师在岗。
少一个立刻补,多一个就赶走。
这就是 ReplicaSet。
1.建立控制器
bash
[root@master controler]# kubectl create deployment webcluster --image myapp:v1 --dry-run=client -o yaml > repset.yml
[root@master controler]# vim repset.yml
apiVersion: apps/v1
kind: ReplicaSet
metadata:
labels:
app: webcluster
name: webcluster
spec:
replicas: 2
selector:
matchLabels:
app: webcluster
# strategy: {}
template:
metadata:
labels:
app: webcluster
spec:
containers:
- image: myapp:v1
name: myapp
[root@master controler]# kubectl apply -f repset.yml
#打开一个新的shell
[root@master ~]# watch -n 1 kubectl get pods --show-labels
Every 1.0s: kubectl get pods --show-labels master: Sat Apr 11 10:08:32 2026
NAME READY STATUS RESTARTS AGE LABELS
webcluster-tbxq4 1/1 Running 0 6m49s app=webcluster
webcluster-jna23 1/1 Running 0 6m49s app=webcluster
2.测试功能
bash
[root@master controler]# vim repset.yml
。。。。
spec:
replicas: 4
。。。。
[root@master controler]# kubectl apply -f repset.yml
[root@master ~]# watch -n 1 kubectl get pods --show-labels
Every 1.0s: kubectl get pods --show-labels master: Sat Apr 11 10:11:06 2026
NAME READY STATUS RESTARTS AGE LABELS
webcluster-4sxz4 1/1 Running 0 13s app=webcluster
webcluster-lvtsn 1/1 Running 0 13s app=webcluster
webcluster-tbxq4 1/1 Running 0 9m23s app=webcluster
webcluster-tr5qk 1/1 Running 0 13s app=webcluster


2.deployment
- Deployment(部署)
一句话:管理 ReplicaSet 的大管家,负责升级、回滚、扩缩容。
Deployment 不直接管 Pod,它管理 ReplicaSet。
你要更新版本时,Deployment 会:
新建一个新版本 ReplicaSet
慢慢把旧版本 Pod 杀掉,新版本起来
保证服务不中断
还支持回滚、暂停发布、弹性扩缩。
比喻
Deployment 是店长。
店长不亲自炒菜,但他管厨师团队(ReplicaSet),
要换厨师、加厨师、减厨师、换菜单,都是店长干。
1.监控
bash
[root@master ~]# watch -n 1 " kubectl get pods --show-labels;echo ====;kubectl get replicasets.apps"
2.建立deployment控制器
bash
[root@master controler]# kubectl create deployment webcluster --image myapp:v1 --dry-run=client -o yaml > dep.yml
[root@master controler]# vim dep.yml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: webcluster
name: webcluster
spec:
minReadySeconds: 5
replicas: 2
selector:
matchLabels:
app: webcluster
# strategy: {}
template:
metadata:
labels:
app: webcluster
spec:
containers:
- image: myapp:v1
name: myapp
# resources: {}
[root@master controler]# kubectl apply -f dep.yml
#监控部分显示效果
Every 1.0s: kubectl get pods --show-labels;echo ====;kubectl get replicase... master: Sat Apr 11 10:43:24 2026
NAME READY STATUS RESTARTS AGE LABELS
webcluster-77c87d9946-49kh5 1/1 Running 0 45s app=webcluster,pod-template-hash=77c87d9946
webcluster-77c87d9946-m2x2d 1/1 Running 0 45s app=webcluster,pod-template-hash=77c87d9946
====
NAME DESIRED CURRENT READY AGE
webcluster-77c87d9946 2 2 2 45s
#发布服务
[root@master controler]# kubectl expose deployment webcluster --port 80 --target-port 80
[root@master controler]# kubectl describe services webcluster
Name: webcluster
Namespace: default
Labels: app=webcluster
Annotations: <none>
Selector: app=webcluster
Type: ClusterIP
IP Family Policy: SingleStack
IP Families: IPv4
IP: 10.107.142.60
IPs: 10.107.142.60
Port: <unset> 80/TCP
TargetPort: 80/TCP
Endpoints: 10.244.2.6:80,10.244.1.9:80
Session Affinity: None
Internal Traffic Policy: Cluster
Events: <none>
#访问:
[root@master controler]# curl 10.107.142.60
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
3.升级和回滚
bash
#升级
[root@master controler]# vim dep.yml
。。。。
spec:
minReadySeconds: 5
replicas: 2
selector:
matchLabels:
app: webcluster
# strategy: {}
template:
metadata:
labels:
app: webcluster
spec:
containers:
- image: myapp:v2 #升级为版本2
name: myapp
。。。。
[root@master controler]# kubectl apply -f dep.yml
deployment.apps/webcluster configured
[root@master controler]# curl 10.107.142.60
Hello MyApp | Version: v2 | <a href="hostname.html">Pod Name</a>
#回滚
[root@master controler]# vim dep.yml
。。。。
spec:
minReadySeconds: 5
replicas: 2
selector:
matchLabels:
app: webcluster
# strategy: {}
template:
metadata:
labels:
app: webcluster
spec:
containers:
- image: myapp:v1 #回滚为版本1
name: myapp
。。。。
[root@master controler]# kubectl apply -f dep.yml
[root@master controler]# curl 10.107.142.60
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
三.版本更新管理及优化
bash
[root@master controler]# vim dep.yml
spec:
minReadySeconds: 5
replicas: 6 #把pod数量设定为6方便观察
selector:
[root@master controler]# kubectl apply -f dep.yml
deployment.apps/webcluster configured
1.查看更新策略信息
b
[root@master controler]# kubectl describe deployments.apps webcluster
RollingUpdateStrategy: 25% max unavailable, 25% max surge
2.设定更新策略
bash
[root@master controler]# vim dep.yml
,,,,,,,
spec:
minReadySeconds: 5
replicas: 6
selector:
matchLabels:
app: webcluster
strategy:
rollingUpdate:
maxSurge: 1 #更新时pod数量最多比期望值多一个
maxUnavailable: 0 #不能使用pod数量比期望值数量多0
template:
metadata:
labels:
app: webcluster
spec:
containers:
- image: myapp:v1
name: myapp
[root@master controler]# kubectl apply -f dep.yml
3.更新暂停和恢复
bash
[root@master controler]# kubectl rollout history deployment webcluster
deployment.apps/webcluster
REVISION CHANGE-CAUSE
7 <none>
8 <none>
[root@master controler]# kubectl rollout pause deployment webcluster #暂停更新
deployment.apps/webcluster paused
[root@master controler]# vim dep.yml
。。。
containers:
- image: myapp:v2
name: myapp
。。。
[root@master controler]# kubectl apply -f dep.yml #执行成功。但更新过程在监控中没出现
deployment.apps/webcluster configured
[root@master controler]# kubectl rollout history deployment webcluster
deployment.apps/webcluster
REVISION CHANGE-CAUSE
7 <none>
8 <none>
[root@master controler]# kubectl rollout resume deployment webcluster #开启更新
deployment.apps/webcluster resumed
[root@master controler]# kubectl rollout history deployment webcluster
deployment.apps/webcluster
REVISION CHANGE-CAUSE
8 <none>
9 <none>
3.DaemonSet
- DaemonSet(守护进程集)
一句话:每台节点上必须且只跑 一个 Pod。
不管集群有多少节点,每个节点 恰好 一个。
新增节点自动跑一个,删除节点自动消失。
典型用途:日志采集、监控 agent、网络插件。
比喻
每栋楼必须配一个保安。
楼越多,保安越多;
但一栋楼永远只配一个。
bash
[root@master controler]# kubectl create deployment daemonset --image myapp:v1 --dry-run=client -o yaml > daemonset.yml
[root@master controler]# vim daemonset.yml
apiVersion: apps/v1
kind: DaemonSet
metadata:
labels:
app: daemonset
name: daemonset
spec:
selector:
matchLabels:
app: daemonset
template:
metadata:
labels:
app: daemonset
spec:
containers:
- image: myapp:v1
name: myapp
#另外开启一个主机node3,并设定在初始化集群时的所有设定确保所有服务的开启
#在master中重新生成集群主机注册时需要的token
[root@master controler]# kubeadm token create --print-join-command
kubeadm join 172.25.254.100:6443 --token lqcz14.6f4krq91w75h58bt --discovery-token-ca-cert-hash sha256:6b5950ef2cdba85d6dfdb564ee90d4187fa3d341767dc9852cbdd5c9dee4f927
[root@node3 ~]# kubeadm join 172.25.254.100:6443 --token lqcz14.6f4krq91w75h58bt --discovery-token-ca-cert-hash sha256:6b5950ef2cdba85d6dfdb564ee90d4187fa3d341767dc9852cbdd5c9dee4f927 --cri-socket unix:///var/run/cri-dockerd.sock
#当node3加入集群后会在node3中立即群星指定的pod,其原因是因为运行了daemonset
#可以在master中观察pod的状态
4.Job控制器
- Job(一次性任务)
一句话:跑一次就结束的任务,跑完就歇。
不是长期服务,是一次性工作。
保证任务成功跑完一次。
跑完 Pod 就退出,不会重启。
比喻
请人搬家。
搬完一次,任务结束,人就走了。
不会一直住在你家。
bash
[root@master ~]# docker load -i perl-5.34.tar.gz
[root@master ~]# docker tag perl:5.34.0 reg.timinglee.org/library/perl:5.34.0
[root@master ~]# docker login reg.timinglee.org -u admin
Password:
Login Succeeded
[root@master ~]# docker push reg.timinglee.org/library/perl:5.34.0
[root@master controler]# kubectl create job job --image perl:5.34.0 --dry-run=client -o yaml > job.yml
[root@master controler]# vim job.yml
apiVersion: batch/v1
kind: Job
metadata:
name: job
spec:
completions: 6
parallelism: 2
template:
spec:
containers:
- image: perl:5.34.0
name: job
command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"]
restartPolicy: Never
backoffLimit: 4
[root@master controler]# kubectl apply -f job.yml
job.batch/job created
[root@master controler]# kubectl logs job-4b45g
3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679821480865132823066470938446095505822317253594081284811174502841027019385211055596446229489549303819644288109756659334461284756482337867831652712019091456485669234603486104543266482133936072602491412737245870066063155881748815209209628292540917153643678925903600113305305488204665213841469519415116094330572703657595919530921861173819326117931051185480744623799627495673518857527248912279381830119491298336733624406566430860213949463952247371907021798609437027705392171762931767523846748184676694051320005681271452635608277857713427577896091736371787214684409012249534301465495853710507922796892589235420199561121290219608640344181598136297747713099605187072113499999983729780499510597317328160963185950244594553469083026425223082533446850352619311881710100031378387528865875332083814206171776691473035982534904287554687311595628638823537875937519577818577805321712268066130019278766111959092164201989380952572010654858632788659361533818279682303019520353018529689957736225994138912497217752834791315155748572424541506959508295331168617278558890750983817546374649393192550604009277016711390098488240128583616035637076601047101819429555961989467678374494482553797747268471040475346462080466842590694912933136770289891521047521620569660240580381501935112533824300355876402474964732639141992726042699227967823547816360093417216412199245863150302861829745557067498385054945885869269956909272107975093029553211653449872027559602364806654991198818347977535663698074265425278625518184175746728909777727938000816470600161452491921732172147723501414419735685481613611573525521334757418494684385233239073941433345477624168625189835694855620992192221842725502542568876717904946016534668049886272327917860857843838279679766814541009538837863609506800642251252051173929848960841284886269456042419652850222106611863067442786220391949450471237137869609563643719172874677646575739624138908658326459958133904780275901
5.Cronjob控制器
- CronJob(定时任务)
一句话:按时间表定时执行 Job。
就是 Linux 的 crontab。
比如:每天凌晨 2 点备份数据库、每周清理日志。
它会按计划自动创建 Job,Job 去跑任务。
比喻
设置一个闹钟:
每天早上 7 点叫人去倒垃圾。
到点自动触发一次一次性工作(Job)。
bash
[root@master controler]# kubectl create cronjob cronjob --image busybox --schedule "* * * * *" --dry-run=client -o yaml > cronjob.yml
[root@master controler]# vim cronjob.yml
apiVersion: batch/v1
kind: CronJob
metadata:
name: cronjob
spec:
jobTemplate:
metadata:
name: cronjob
spec:
template:
spec:
containers:
- image: busybox
name: cronjob
command:
- /bin/sh
- -c
- echo "hello timinglee"
restartPolicy: OnFailure
schedule: '* * * * *'
[root@master controler]# kubectl apply -f cronjob.yml #整分运行
[root@master controler]# kubectl get cronjobs.batch
NAME SCHEDULE TIMEZONE SUSPEND ACTIVE LAST SCHEDULE AGE
cronjob * * * * * <none> False 0 17s 70s
[root@master controler]# kubectl logs cronjob-29598241-pxggh
hello timinglee
四.微服务
1.创建一个clusterIP微服务
bash
[root@master services]# kubectl create deployment webcluster --image myapp:v1 --replicas 2 --dry-run=client -o yaml > clusterIP.yml
[root@master services]# kubectl apply -f clusterIP.yml
[root@master services]# kubectl expose deployment webcluster --port 80 --target-port 80 --dry-run=client -o yaml >> clusterIP.yml
[root@master services]# vim clusterIP.yml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: webcluster
name: webcluster
spec:
replicas: 2
selector:
matchLabels:
app: webcluster
template:
metadata:
labels:
app: webcluster
spec:
containers:
- image: myapp:v1
name: myapp
---
apiVersion: v1
kind: Service
metadata:
labels:
app: webcluster
name: webcluster
spec:
ports:
- port: 80
protocol: TCP
targetPort: 80
selector:
app: webcluster
type: ClusterIP
[root@master services]# kubectl apply -f clusterIP.yml
#检控官效果
[root@master ~]# watch -n 1 " kubectl get pods -o wide ; kubectl describe svc webcluster "
Every 1.0s: kubectl get pods -o wide ; kubectl describe svc webcluster master: Sat Apr 11 16:41:55 2026
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS
GATES
webcluster-77c87d9946-bxtlz 1/1 Running 0 56s 10.244.1.47 node1 <none> <none>
webcluster-77c87d9946-ppqc5 1/1 Running 0 56s 10.244.2.39 node2 <none> <none>
Name: webcluster
Namespace: default
Labels: app=webcluster
Annotations: <none>
Selector: app=webcluster
Type: ClusterIP
IP Family Policy: SingleStack
IP Families: IPv4
IP: 10.105.28.64
IPs: 10.105.28.64
Port: <unset> 80/TCP
TargetPort: 80/TCP
Endpoints: 10.244.1.47:80,10.244.2.39:80
Session Affinity: None
Internal Traffic Policy: Cluster
Events: <none>
[root@master services]# curl 10.105.28.64/hostname.html
webcluster-77c87d9946-bxtlz
[root@master services]# curl 10.105.28.64/hostname.html
webcluster-77c87d9946-ppqc5
#测试
[root@master services]# kubectl delete pods webcluster-77c87d9946-bxtlz
[root@master services]# kubectl get pods --show-labels
NAME READY STATUS RESTARTS AGE LABELS
webcluster-77c87d9946-4s2qc 1/1 Running 0 62s app=webcluster,pod-template-hash=77c87d9946
webcluster-77c87d9946-ks5rz 1/1 Running 0 39s app=webcluster,pod-template-hash=77c87d9946
[root@master services]# kubectl label pods webcluster-77c87d9946-4s2qc app-
pod/webcluster-77c87d9946-4s2qc unlabeled
[root@master services]# kubectl label pods webcluster-77c87d9946-4s2qc app=webcluster
pod/webcluster-77c87d9946-4s2qc labeled
===============================================
#解释
第一部分:Deployment(部署)
Deployment 负责管理应用的副本数量和滚动更新。
apiVersion: apps/v1
含义:API 版本号
版本号不能乱写,要与kind对应,可以用以下命令查看
kubectl explain deployment
说明:apps/v1 是 Kubernetes 中管理工作负载(如 Deployment、StatefulSet)的稳定版本
kind: Deployment
含义:资源类型
说明:告诉 Kubernetes 我们要创建一个 Deployment 资源
metadata:
含义:元数据
说明:用于标识这个资源的描述信息
labels:
app: webcluster
含义:标签
说明:键值对,用于给这个 Deployment 打标签,方便后续通过标签筛选和管理
name: webcluster
含义:名称
说明:这个 Deployment 的名字,在命名空间内必须唯一
spec:
含义:规格/期望状态
说明:描述这个 Deployment 想要的最终状态是什么
replicas: 2
含义:副本数
说明:希望运行 2 个 Pod 实例。Kubernetes 会确保始终有 2 个 Pod 在运行
selector:
matchLabels:
app: webcluster
含义:标签选择器
说明:Deployment 通过这个规则找到它管理的 Pod。只有标签 app: webcluster 的 Pod 才会被这个 Deployment 管理
template:
含义:Pod 模板
说明:定义如何创建 Pod(相当于 Pod 的"蓝图")
metadata:
labels:
app: webcluster
含义:Pod 的元数据
说明:给创建的 Pod 打上标签 app: webcluster。这个标签必须与上面的 selector 匹配
spec:
含义:Pod 的规格
说明:描述 Pod 内部容器的配置
containers:
含义:容器列表
说明:Pod 里可以运行多个容器,这里是一个数组
- image: myapp:v1
含义:容器镜像
说明:使用 myapp:v1 这个镜像来启动容器。如果镜像不在本地,会从 Docker Hub 拉取
name: myapp
含义:容器名称
说明:这个容器的名字,在 Pod 内唯一
第二部分:Service(服务)
Service 为 Pod 提供稳定的网络访问入口。
apiVersion: v1
含义:API 版本号
说明:Service 是核心 API 组,版本是 v1
kind: Service
含义:资源类型
说明:告诉 Kubernetes 我们要创建一个 Service
metadata:
labels:
app: webcluster
name: webcluster
含义:Service 的元数据
说明:给 Service 打标签,并命名为 webcluster
spec:
含义:Service 的规格
说明:描述 Service 的行为
ports:
含义:端口映射列表
说明:定义如何将端口从 Service 转发到 Pod
- port: 80
含义:Service 的端口
说明:这个 Service 会在集群内部监听 80 端口。其他 Pod 访问这个 Service 时,要访问这个端口
protocol: TCP
含义:协议类型
说明:使用 TCP 协议(也可以是 UDP)
targetPort: 80
含义:目标端口
说明:流量会被转发到 Pod 容器的 80 端口。也就是说,你的应用必须在容器内监听 80 端口
selector:
app: webcluster
含义:Pod 选择器
说明:Service 会把流量转发给所有标签为 app: webcluster 的 Pod。这正好匹配了上面 Deployment 创建的 Pod
type: ClusterIP
含义:Service 类型
说明:ClusterIP 是默认类型,只能在集群内部访问。如果需要从外部访问,可以改成 NodePort 或 LoadBalancer
2.优化service工作模式
bash
[root@master services]# dnf install ipvsadm -y
[root@master services]# kubectl -n kube-system edit cm kube-prox
59 mode: "ipvs"
[root@master services]# kubectl -n kube-system get pods
NAME READY STATUS RESTARTS AGE
coredns-697886855d-d6kwd 1/1 Running 2 (23h ago) 13d
coredns-697886855d-k92r9 1/1 Running 2 (23h ago) 13d
etcd-master 1/1 Running 2 (23h ago) 13d
kube-apiserver-master 1/1 Running 2 (23h ago) 13d
kube-controller-manager-master 1/1 Running 2 (23h ago) 13d
kube-proxy-7qr9n 1/1 Running 2 (23h ago) 13d
kube-proxy-8sm6w 1/1 Running 2 (23h ago) 13d
kube-proxy-q8q55 1/1 Running 2 (23h ago) 13d
kube-scheduler-master 1/1 Running 2 (23h ago) 13d
[root@master services]# kubectl -n kube-system delete pods kube-proxy-7qr9n
pod "kube-proxy-7qr9n" deleted from kube-system namespace
[root@master services]# kubectl -n kube-system delete pods kube-proxy-8sm6w
pod "kube-proxy-8sm6w" deleted from kube-system namespace
[root@master services]# kubectl -n kube-system delete pods kube-proxy-q8q55
pod "kube-proxy-q8q55" deleted from kube-system namespace
#测试
[root@master ~]# watch -n 1 ipvsadm -Ln
#更改前面的yml文件
[root@master services]# vim clusterIP.yml
replicas: 2
[root@master services]# kubectl apply -f clusterIP.yml
#查看lvs的轮询列表中的信息即可
==============================================
解释
通俗易懂的解释:把 Kubernetes 的网络模式从 iptables 换成 ipvs
你做的这些操作,核心目的是把 Kubernetes 的网络代理模式从 iptables 换成 ipvs,然后用 ipvsadm 命令查看负载均衡效果。
我用大白话给你解释每一步:
第1步:安装 ipvsadm 工具
dnf install ipvsadm -y
翻译成人话:
安装一个叫 ipvsadm 的"监控工具",它可以让我们查看 IPVS 里面的负载均衡规则和统计信息。
第2步:修改 kube-proxy 配置模式
kubectl -n kube-system edit cm kube-proxy
# 找到 mode: "" 改成 mode: "ipvs"
翻译成人话:
kube-proxy 是 Kubernetes 的"网络交通警察",负责把流量分发到不同的 Pod。
它有几种工作模式:
iptables 模式(默认):用 Linux 的防火墙规则来转发流量,规则多了就慢
ipvs 模式(你改的):用 Linux 内置的负载均衡器(LVS)来转发,性能更好、规则更多
你这是在告诉 Kubernetes:"我要用 ipvs 模式,不要用 iptables 模式了。"
第3步:重启 kube-proxy Pod
kubectl -n kube-system delete pods kube-proxy-7qr9n kube-proxy-8sm6w kube-proxy-q8q55
翻译成人话:
修改配置后,需要重启"交通警察"(kube-proxy Pod)才能生效。
删除旧的 Pod,Kubernetes 会自动创建新的 Pod,新的 Pod 就会使用 ipvs 模式了。
第4步:查看 ipvs 规则
watch -n 1 ipvsadm -Ln
翻译成人话:
watch -n 1 = 每1秒刷新一次
ipvsadm -Ln = 查看 ipvs 的负载均衡规则列表
合起来就是:实时监控 ipvs 里面的负载均衡规则变化,看看流量是怎么分配到不同 Pod 的。
第5步:创建应用
vim clusterIP.yml
# 把 replicas: 2 改成 replicas: 2(保持2个副本)
kubectl apply -f clusterIP.yml
翻译成人话:
创建一个有 2 个副本的应用(webcluster),然后创建一个 Service 作为它的入口。
Service 的类型是 ClusterIP,只能在集群内部访问。
3.微服务解析
b
[root@master ~]# kubectl -n kube-system get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kube-dns ClusterIP 10.96.0.10 <none> 53/UDP,53/TCP,9153/TCP 14d
[root@master ~]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 14d
webcluster ClusterIP 10.99.217.241 <none> 80/TCP 17h
# 从 Master 节点测试 DNS 是否正常
[root@master ~]# dig webcluster.default.svc.cluster.local @10.96.0.10
; <<>> DiG 9.16.23-RH <<>> webcluster.default.svc.cluster.local @10.96.0.10
;; global options: +cmd
;; Got answer:
;; WARNING: .local is reserved for Multicast DNS
;; You are currently testing what happens when an mDNS query is leaked to DNS
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 6731
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; WARNING: recursion requested but not available
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
; COOKIE: e7905dbdbb637f4a (echoed)
;; QUESTION SECTION:
;webcluster.default.svc.cluster.local. IN A
######## "webcluster 这个服务的 IP 地址是 10.99.217.241"
;; ANSWER SECTION:
webcluster.default.svc.cluster.local. 30 IN A 10.99.217.241
;; Query time: 2 msec
;; SERVER: 10.96.0.10#53(10.96.0.10)
;; WHEN: Sun Apr 12 09:58:53 CST 2026
;; MSG SIZE rcvd: 129
=====================================
解释
[root@master ~]# kubectl -n kube-system get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kube-dns ClusterIP 10.96.0.10 <none> 53/UDP,53/TCP,9153/TCP 14d
查看 kube-system 命名空间下的所有 Service。
部分 含义 大白话
kubectl Kubernetes 命令行工具 "指挥 K8s 的工具"
-n kube-system 指定命名空间 "去 kube-system 这个区域找"
get svc 获取 Service 列表 "把所有的服务列出来给我看"
字段 值 大白话
NAME kube-dns 这个服务叫"kube-dns"
TYPE ClusterIP 只能在集群内部访问
CLUSTER-IP 10.96.0.10 它的固定"门牌号"
EXTERNAL-IP <none> 从集群外面访问这个服务的入口地址,这个服务没有外部访问地址,只能在集群内部使用。。
PORT(S) 53/UDP,53/TCP 监听的端口(53是DNS专用端口)
AGE 14d 已经运行14天了
[root@master ~]# kubectl get svc
命令 查看位置 能看到什么
kubectl get svc default 命名空间 你的应用(webcluster)
kubectl -n kube-system get svc kube-system 命名空间 系统服务(kube-dns)
[root@master ~]# dig webcluster.default.svc.cluster.local @10.96.0.10
部分 你的值 含义
服务名 webcluster 你给 Service 起的名字
命名空间 default Service 所在的命名空间
svc svc 固定写法,表示这是 Service 类型
cluster.local cluster.local 集群的默认域名后缀
@10.96.0.10
完整格式: <服务名>.<命名空间>.svc.cluster.local
4.clusterIP中的无头服务headless
bash
[root@master services]# kubectl delete -f clusterIP.yml
[root@master services]# vim clusterIP.yml
。。。。。
apiVersion: v1
kind: Service
metadata:
labels:
app: webcluster
name: webcluster
spec:
ports:
- port: 80
protocol: TCP
targetPort: 80
selector:
app: webcluster
type: ClusterIP
clusterIP: None #指定此service使用headless模式
[root@master services]# kubectl apply -f clusterIP.yml
deployment.apps/webcluster created
service/webcluster created
[root@master services]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 14d
webcluster ClusterIP None <none> 80/TCP 20s
#测试
[root@master ~]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
test 1/1 Running 0 112s 10.244.1.53 node1 <none> <none>
webcluster-77c87d9946-rps74 1/1 Running 0 4m4s 10.244.1.51 node1 <none> <none>
webcluster-77c87d9946-vnrw9 1/1 Running 0 4m4s 10.244.2.42 node2 <none> <none>
[root@master services]# dig webcluster.default.svc.cluster.local @10.96.0.10
; <<>> DiG 9.16.23-RH <<>> webcluster.default.svc.cluster.local @10.96.0.10
;; global options: +cmd
;; Got answer:
;; WARNING: .local is reserved for Multicast DNS
;; You are currently testing what happens when an mDNS query is leaked to DNS
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 28846
;; flags: qr aa rd; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1
;; WARNING: recursion requested but not available
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
; COOKIE: 9b9afd2328a52dfc (echoed)
;; QUESTION SECTION:
;webcluster.default.svc.cluster.local. IN A
;; ANSWER SECTION:
webcluster.default.svc.cluster.local. 30 IN A 10.244.2.42
webcluster.default.svc.cluster.local. 30 IN A 10.244.1.51
;; Query time: 0 msec
;; SERVER: 10.96.0.10#53(10.96.0.10)
;; WHEN: Sun Apr 12 10:14:03 CST 2026
;; MSG SIZE rcvd: 181
=========================================
这段操作的核心是:创建了一个"无头服务"(Headless Service),并用DNS命令验证了它的特殊效果。
下面用通俗的方式拆解一下:
1. 你做了什么?(创建Headless Service)
你写了一个Service配置文件,关键点是 clusterIP: None。这就像:
普通Service:相当于公司的总机号码(一个虚拟IP)。你打这个电话,接线员随机转给一个员工(Pod)。
你创建的(Headless):相当于直接拿到所有员工的手机号列表。没有总机,没有虚拟IP,你直接看到每个Pod的真实IP。
2. 命令 dig ... @10.96.0.10 在测什么?
dig:DNS查询命令,就像问"这个域名对应什么IP?"
webcluster.default.svc.cluster.local:要查询的完整域名。
@10.96.0.10:指定Kubernetes内部的DNS服务器(kube-dns)来回答。
3. 结果说明了什么?
关键看 ANSWER SECTION(答案部分):
text
webcluster... 30 IN A 10.244.2.42
webcluster... 30 IN A 10.244.1.51
返回了2个IP,正好对应你两个Pod的真实IP(10.244.1.51 和 10.244.2.42)。
如果是普通Service,这里只会返回一个虚拟IP(ClusterIP),比如 10.96.0.100。
总结一句话
Headless Service 让DNS直接返回所有Pod的真实IP列表,而不是一个虚拟IP。 这样适合需要直接与每个Pod单独通信的场景(比如StatefulSet、自己实现服务发现)
5.nodeport
1.nodeport的建立
bash
[root@master services]# kubectl delete -f clusterIP.yml
deployment.apps "webcluster" deleted from default namespace
service "webcluster" deleted from default namespace
[root@master services]# cp clusterIP.yml nodeport.yml
[root@master services]# vim nodeport.yml
---
apiVersion: v1
kind: Service
metadata:
labels:
app: webcluster
name: webcluster
spec:
ports:
- port: 80
protocol: TCP
targetPort: 80
selector:
app: webcluster
type: NodePort #改变类型
[root@master services]# kubectl apply -f nodeport.yml
deployment.apps/webcluster created
service/webcluster created
[root@master services]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 14d
webcluster NodePort 10.96.25.134 <none> 80:31670/TCP 5s
[root@master services]# curl 172.25.254.100:31670/hostname.html
webcluster-77c87d9946-s24sf
[root@master services]# curl 172.25.254.100:31670/hostname.html
webcluster-77c87d9946-62bsl
[root@master services]# curl 172.25.254.100:31670/hostname.html
webcluster-77c87d9946-s24sf
[root@master services]# curl 172.25.254.100:31670/hostname.html
webcluster-77c87d9946-62bsl
==================================================
之前(ClusterIP + None):
像公司内部电话本,只有内部人员(集群内部)能看到员工直拨号码
外部客户(集群外)根本不知道这个号码
现在(NodePort):
像在公司前台开了一个对外窗口(端口31670)
外部客户打这个窗口电话,前台会自动转接到某个员工
[root@master services]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 14d
webcluster NodePort 10.96.25.134 <none> 80:31670/TCP 5s
10.96.25.134:Service的虚拟IP(集群内部用)
80:Service内部监听的端口
31670:对外暴露的端口(重点!)
NodePort:类型,表示每个节点都会监听这个端口
[root@master services]# curl 172.25.254.100:31670/hostname.html
你的请求 → 任意节点的 31670 端口 → 转发到 Service 的 80 端口 → 随机转发到某个 Pod 的 80 端口
可以访问 master、node1、node2 任意一个IP的 31670 端口,效果完全一样
2.nodeport端口管理(默认范围30000~32767)
bash
[root@master services]# kubectl delete -f nodeport.yml
deployment.apps "webcluster" deleted from default namespace
service "webcluster" deleted from default namespace
[root@master services]# vim nodeport.yml #在范围之内设定
---
apiVersion: v1
kind: Service
metadata:
labels:
app: webcluster
name: webcluster
spec:
ports:
- port: 80
protocol: TCP
targetPort: 80
nodePort: 31111 #设定端口
selector:
app: webcluster
type: NodePort
[root@master services]# kubectl apply -f nodeport.yml
deployment.apps/webcluster created
service/webcluster created
[root@master services]# kubectl get svc webcluster
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
webcluster NodePort 10.108.146.142 <none> 80:31111/TCP 8s
[root@master services]# kubectl delete -f nodeport.yml
deployment.apps "webcluster" deleted from default namespace
service "webcluster" deleted from default namespace
[root@master services]# vim nodeport.yml #在范围外内设定
---
apiVersion: v1
kind: Service
metadata:
labels:
app: webcluster
name: webcluster
spec:
ports:
- port: 80
protocol: TCP
targetPort: 80
nodePort: 33333 #设定端口
selector:
app: webcluster
type: NodePort
[root@master services]# kubectl apply -f nodeport.yml
deployment.apps/webcluster created
The Service "webcluster" is invalid: spec.ports[0].nodePort: Invalid value: 33333: provided port is not in the valid range. The range of valid ports is 30000-32767
#解锁默认端口范围
[root@master services]# vim /etc/kubernetes/manifests/kube-apiserver.yaml
42 - --service-node-port-range=30000-40000
#上述文件修改后集群会自动重启,等待120秒左右看集群状态
[root@master services]# kubectl get nodes
NAME STATUS ROLES AGE VERSION
master Ready control-plane 14d v1.35.3
node1 Ready <none> 14d v1.35.3
node2 Ready <none> 14d v1.35.3
[root@master services]# kubectl apply -f nodeport.yml
deployment.apps/webcluster unchanged
service/webcluster created
[root@master services]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 14d
webcluster NodePort 10.104.45.175 <none> 80:33333/TCP 6s
=============================================
解释
vim /etc/kubernetes/manifests/kube-apiserver.yaml修改k8s的主配置文件,更改NosePort模式的端口范围。
6.loadbalancer
1.创建loadbalancer
b
[root@master services]# kubectl delete -f nodeport.yml
deployment.apps "webcluster" deleted from default namespace
service "webcluster" deleted from default namespace
[root@master services]# cp nodeport.yml loadbalance.yml
[root@master services]# vim loadbalance.yml
---
apiVersion: v1
kind: Service
metadata:
labels:
app: webcluster
name: webcluster
spec:
ports:
- port: 80
protocol: TCP
targetPort: 80
selector:
app: webcluster
type: LoadBalancer #指定services模式
[root@master services]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 14d
webcluster LoadBalancer 10.109.27.140 <pending> 80:33804/TCP 7s
[root@master services]# curl 172.25.254.100:33804
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
[root@master services]# curl 10.109.27.140
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
2.部署metallb
对比
| 访问方式 | 类型 | 能否对外服务 | 能否负载均衡 |
|---|---|---|---|
| 172.25.254.100:33804 | NodePort | ❌ 需要知道节点IP+随机端口 | ✅ 节点间轮询 |
| 10.109.27.140 | ClusterIP | ❌ 只能在集群内部访问 | ✅ Pod间轮询 |
| 172.25.254.50 | LoadBalancer (MetalLB) | ✅ 固定IP + 标准端口(80) | ✅ 完整负载均衡 |
关键区别:
- 端口问题
bash
# NodePort 方式:需要记住随机端口 33804
curl 172.25.254.100:33804 # 用户:还要加端口?难受
# LoadBalancer 方式:标准80端口
curl 172.25.254.50 # 用户:完美!就像访问网站
- 对外服务能力
NodePort:需要把 172.25.254.100(节点IP)暴露给外部 ,但这个IP可能变化
LoadBalancer:172.25.254.50 是固定的服务IP,节点挂了也不影响
- 生产环境真实场景:
云厂商(阿里云/AWS)的 LoadBalancer:
- 自动创建真正的负载均衡器(如阿里云SLB)
- 分配公网IP:1.2.3.4
- 用户访问 http://1.2.3.4 → 自动负载到后端Pod
你的测试环境没有云厂商,MetalLB 就是"模拟"这个功能
部署
bash
[root@master services]# kubectl edit configmap -n kube-system kube-proxy
40 ipvs:
41 excludeCIDRs: null
42 minSyncPeriod: 0s
43 scheduler: ""
44 strictARP: true #arp响应禁止
[root@master services]# kubectl -n kube-system get pods
NAME READY STATUS RESTARTS AGE
coredns-697886855d-d6kwd 1/1 Running 2 (41h ago) 14d
coredns-697886855d-k92r9 1/1 Running 2 (41h ago) 14d
etcd-master 1/1 Running 2 (41h ago) 14d
kube-apiserver-master 1/1 Running 0 30m
kube-controller-manager-master 1/1 Running 3 (30m ago) 14d
kube-proxy-52zdz 1/1 Running 0 18h #删掉
kube-proxy-5t7vl 1/1 Running 0 18h #删掉
kube-proxy-8s6vj 1/1 Running 0 18h #删掉
kube-scheduler-master 1/1 Running 3 (30m ago) 14d
#让配置生效。删除后 Kubernetes 会自动创建新的 Pod,读取新的配置
[root@master services]# kubectl -n kube-system delete pods kube-proxy-52zdz
pod "kube-proxy-52zdz" deleted from kube-system namespace
[root@master services]# kubectl -n kube-system delete pods kube-proxy-5t7vl
pod "kube-proxy-5t7vl" deleted from kube-system namespace
[root@master services]# kubectl -n kube-system delete pods kube-proxy-8s6vj
pod "kube-proxy-8s6vj" deleted from kube-system namespace
#下载metallb的yml文件
[root@master services]# wget https://raw.githubusercontent.com/metallb/metallb/v0.15.3/config/manifests/metallb-native.yaml
[root@master ~]# docker pull quay.io/metallb/controller:v0.15.3
[root@master ~]# docker pull quay.io/metallb/speaker:v0.15.3
[root@master ~]# docker tag quay.io/metallb/controller:v0.15.3 reg.timinglee.org/metallb/controller:v0.15.3
[root@master ~]# docker push reg.timinglee.org/metallb/controller:v0.15.3
[root@master ~]# docker tag quay.io/metallb/speaker:v0.15.3 reg.timinglee.org/metallb/speaker:v0.15.3
[root@master ~]# docker push reg.timinglee.org/metallb/speaker:v0.15.3
[root@master services]# grep -n image: metallb-native.yaml #修改文件下述两行内容
2001: image: metallb/controller:v0.15.3
2098: image: metallb/speaker:v0.15.3
#安装
[root@master services]# kubectl apply -f metallb-native.yaml
[root@master services]# kubectl -n metallb-system get all
NAME READY STATUS RESTARTS AGE
pod/controller-5fbf6546f9-9568r 1/1 Running 0 52s
pod/speaker-976v8 1/1 Running 0 52s
pod/speaker-nm898 1/1 Running 0 52s
pod/speaker-x68v4 1/1 Running 0 52s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/metallb-webhook-service ClusterIP 10.100.120.24 <none> 443/TCP 52s
NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
daemonset.apps/speaker 3 3 3 3 3 kubernetes.io/os=linux 52s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/controller 1/1 1 1 52s
NAME DESIRED CURRENT READY AGE
replicaset.apps/controller-5fbf6546f9 1 1 1 52s
#配置metallb
[root@master services]# vim configmap.yml
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
name: first-pool
namespace: metallb-system
spec:
addresses:
- 172.25.254.50-172.25.254.80
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
name: example
namespace: metallb-system
spec:
ipAddressPools:
- first-pool
[root@master services]# kubectl apply -f configmap.yml
ipaddresspool.metallb.io/first-pool created
l2advertisement.metallb.io/example created
[root@master services]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 14d
webcluster LoadBalancer 10.109.27.140 172.25.254.50 80:33804/TCP 24m
[root@master services]# curl 172.25.254.50
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
[root@master services]# curl 172.25.254.50
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
[root@master services]# curl 172.25.254.50
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
[root@master services]# curl 172.25.254.50
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
[root@master services]# curl 172.25.254.50
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
==============================================================
1.
44 strictARP: true #启用严格ARP响应规则(只有授权的节点才能响应)
没有 strictARP 时的问题:
外部用户问:"谁有 172.25.254.50?"
可能多个节点都回答:"我有!我有!" → IP冲突,乱套了
设置 strictARP: true 后:
kube-proxy 会约束:只有被 MetalLB 指定的那个节点才能应答这个IP
就像一个严格的"发言人规则",避免多个节点抢着回答
2.
1. Pod 层面(运行的进程)
Pod 数量 角色比喻
controller 1个 总调度员 - 负责分配IP地址,管理整个系统
speaker 3个 广播员 - 每个节点一个,负责对外宣布"这个IP归我管"
为什么有3个 speaker?
你有3个节点(master、node1、node2)
每个节点都需要一个 speaker 来响应 ARP 请求
保证任意节点收到访问请求都能正确处理
3.
[root@master services]# vim configmap.yml
kind: IPAddressPool # 类型:IP地址池
name: first-pool # 池子名字:第一个池子
namespace: metallb-system #指定这些配置资源存放在哪个命名空间
addresses: # 可用的IP范围
- 172.25.254.50-172.25.254.80
kind: L2Advertisement # 类型:二层网络广播
name: example # 配置名字:示例
ipAddressPools: # 使用哪个IP池
- first-pool # 使用上面定义的"第一个池子"
7.externalname
一句话理解
ExternalName 就像给外部网站起了一个"内部小名",让集群内的 Pod 可以用内部域名去访问外部服务。
类比理解
想象你在公司里:
真实情况:你要访问百度 www.baidu.com(外部地址)
ExternalName 作用:你在公司通讯录里登记一个"内部别名"叫 webcluster
使用效果:同事只要说"找 webcluster",前台就会自动转接到真正的百度
bash
[root@master services]# vim externalname.yml
apiVersion: v1
kind: Service
metadata:
labels:
app: webcluster
name: webcluster
spec:
type: ExternalName
externalName: www.baidu.com
[root@master services]# dig webcluster.default.svc.cluster.local @10.96.0.10
; <<>> DiG 9.16.23-RH <<>> webcluster.default.svc.cluster.local @10.96.0.10
;; global options: +cmd
;; Got answer:
;; WARNING: .local is reserved for Multicast DNS
;; You are currently testing what happens when an mDNS query is leaked to DNS
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 21915
;; flags: qr aa rd; QUERY: 1, ANSWER: 4, AUTHORITY: 0, ADDITIONAL: 1
;; WARNING: recursion requested but not available
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
; COOKIE: 631489b7b37a6594 (echoed)
;; QUESTION SECTION:
;webcluster.default.svc.cluster.local. IN A
;; ANSWER SECTION:
webcluster.default.svc.cluster.local. 30 IN CNAME www.baidu.com.
www.baidu.com. 30 IN CNAME www.a.shifen.com.
www.a.shifen.com. 30 IN A 183.2.172.17
www.a.shifen.com. 30 IN A 183.2.172.177
# 问:webcluster 是什么?
# 答:它是 www.baidu.com 的别名(CNAME)
# 问:www.baidu.com 是什么?
# 答:它又是 www.a.shifen.com 的别名
# 问:www.a.shifen.com 的 IP 是?
# 答:183.2.172.17 和 183.2.172.177
;; Query time: 36 msec
;; SERVER: 10.96.0.10#53(10.96.0.10)
;; WHEN: Sun Apr 12 14:09:30 CST 2026
;; MSG SIZE rcvd: 247
8.ingress-nginx
一、什么是 Ingress?
一句话理解:Ingress 是 K8s 的"七层路由器",根据域名和路径把外部流量转发到不同的内部服务。
类比理解:
想象你有一栋大楼(K8s 集群):
传统方式:每个公司(Service)都有自己的大门(NodePort),访客要记很多门牌号
Ingress 方式:大楼只有一个前台(Ingress Controller),访客说"找 A 公司"或"找 B 公司",前台帮你转接
1.安装
bash
[root@master services]# wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.15.1/deploy/static/provider/baremetal/deploy.yaml
[root@master services]# vim deploy.yaml
444: image: ingress-nginx/controller:v1.15.1
547: image: ingress-nginx/kube-webhook-certgen:v1.6.9
603: image: ingress-nginx/kube-webhook-certgen:v1.6.9
[root@master ~]# docker load -i ingress-nginx-1.15.1.tar
#上传所有ingres所需镜像到harbor仓库的ingress-nginx项目
# 先查看加载进来的镜像名称和标签
docker images | grep ingress-nginx
# 假设查到的镜像名为 ingress-nginx/controller,标签为 v1.15.1
# 为它打上 Harbor 的标签
docker tag ingress-nginx/controller:v1.15.1 reg.timinglee.org/ingress-nginx/controller:v1.15.1
# 登录 Harbor (需要输入用户名密码)
docker login reg.timinglee.org
# 推送镜像
docker push reg.timinglee.org/ingress-nginx/controller:v1.15.1
[root@master services]# kubectl apply -f deploy-1.15.1.yaml
namespace/ingress-nginx created
serviceaccount/ingress-nginx created
serviceaccount/ingress-nginx-admission created
role.rbac.authorization.k8s.io/ingress-nginx created
role.rbac.authorization.k8s.io/ingress-nginx-admission created
clusterrole.rbac.authorization.k8s.io/ingress-nginx created
clusterrole.rbac.authorization.k8s.io/ingress-nginx-admission created
rolebinding.rbac.authorization.k8s.io/ingress-nginx created
rolebinding.rbac.authorization.k8s.io/ingress-nginx-admission created
clusterrolebinding.rbac.authorization.k8s.io/ingress-nginx created
clusterrolebinding.rbac.authorization.k8s.io/ingress-nginx-admission created
configmap/ingress-nginx-controller created
service/ingress-nginx-controller created
service/ingress-nginx-controller-admission created
deployment.apps/ingress-nginx-controller created
job.batch/ingress-nginx-admission-create created
job.batch/ingress-nginx-admission-patch created
ingressclass.networking.k8s.io/nginx created
validatingwebhookconfiguration.admissionregistration.k8s.io/ingress-nginx-admission created
# 5. 修改 Service 类型为 LoadBalancer
# 把 type: ClusterIP 改为 type: LoadBalancer
root@master services]# kubectl -n ingress-nginx edit svc ingress-nginx-controller
49 type: LoadBalancer、
[root@master services]# kubectl -n ingress-nginx get all
NAME READY STATUS RESTARTS AGE
pod/ingress-nginx-controller-6bcbfdbd4b-rf9zm 1/1 Running 0 2m44s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/ingress-nginx-controller LoadBalancer 10.103.58.119 172.25.254.50 80:32683/TCP,443:32543/TCP 2m44s
service/ingress-nginx-controller-admission ClusterIP 10.107.149.85 <none> 443/TCP 2m44s
# EXTERNAL-IP: 172.25.254.50 ← 这就是 Ingress 的入口 IP
# PORT: 80:32683/TCP, 443:32543/TCP ← 80和443端口
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/ingress-nginx-controller 1/1 1 1 2m44s
NAME DESIRED CURRENT READY AGE
replicaset.apps/ingress-nginx-controller-6bcbfdbd4b 1 1 1 2m44s
[root@master services]# kubectl get ingressclasses.networking.k8s.io
NAME CONTROLLER PARAMETERS AGE
nginx k8s.io/ingress-nginx <none> 4m10s
2.首个ingress
准备2个明显区分的svc和控制器
bash
[root@master services]# vim myapp1.yml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: myapp1
name: myapp1
spec:
replicas: 1
selector:
matchLabels:
app: myapp1
template:
metadata:
labels:
app: myapp1
spec:
containers:
- image: myapp:v1
name: myapp
---
apiVersion: v1
kind: Service
metadata:
labels:
app: myapp1
name: myapp1
spec:
ports:
- port: 80
protocol: TCP
targetPort: 80
selector:
app: myapp1
type: ClusterIP
[root@master services]# vim myapp2.yml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: myapp2
name: myapp2
spec:
replicas: 1
selector:
matchLabels:
app: myapp2
template:
metadata:
labels:
app: myapp2
spec:
containers:
- image: myapp:v2
name: myapp
---
apiVersion: v1
kind: Service
metadata:
labels:
app: myapp2
name: myapp2
spec:
ports:
- port: 80
protocol: TCP
targetPort: 80
selector:
app: myapp2
type: ClusterIP
[root@master services]# kubectl apply -f myapp1.yml
[root@master services]# kubectl apply -f myapp2.yml、
[root@master services]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 14d
myapp1 ClusterIP 10.101.12.140 <none> 80/TCP 3m41s
myapp2 ClusterIP 10.107.83.65 <none> 80/TCP 104s
创建ingress代理
bash
[root@master services]# kubectl create ingress webcluster --rule='*/=myapp1:80' --dry-run=client -o yaml > 1-ingress.yml
[root@master services]# vim 1-ingress.yml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: webcluster
spec:
ingressClassName: nginx
rules:
- http:
paths:
- backend:
service:
name: myapp1
port:
number: 80
path: /
pathType: Prefix
[root@master services]# kubectl -n ingress-nginx get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ingress-nginx-controller LoadBalancer 10.103.58.119 172.25.254.50 80:32683/TCP,443:32543/TCP 21m
ingress-nginx-controller-admission ClusterIP 10.107.149.85 <none> 443/TCP 21m
[root@master services]# curl 172.25.254.50
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
3.基于域名的ingress
bash
[root@master services]# vim 2-ingress.yml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
name: webcluster
spec:
ingressClassName: nginx
rules:
- host: myapp1.timinglee.org
http:
paths:
- backend:
service:
name: myapp1
port:
number: 80
path: /
pathType: Prefix
- host: myapp2.timinglee.org
http:
paths:
- backend:
service:
name: myapp2
port:
number: 80
path: /
pathType: Prefix
[root@master services]# kubectl describe ingress webcluster
Name: webcluster
Labels: <none>
Namespace: default
Address:
Ingress Class: nginx
Default backend: <default>
Rules:
Host Path Backends
---- ---- --------
myapp1.timinglee.org
/ myapp1:80 (10.244.1.60:80)
myapp2.timinglee.org
/ myapp2:80 (10.244.2.50:80)
Annotations: nginx.ingress.kubernetes.io/rewrite-target: /
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Sync 15s nginx-ingress-controller Scheduled for sync
#测试:
[root@master services]# vim /etc/hosts
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
172.25.254.100 master
172.25.254.10 node1
172.25.254.20 node2
172.25.254.30 node3
172.25.254.200 reg.timinglee.org
172.25.254.50 myapp1.timinglee.org myapp2.timinglee.org
[root@master services]# curl myapp1.timinglee.org
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
[root@master services]# curl myapp2.timinglee.org
Hello MyApp | Version: v2 | <a href="hostname.html">Pod Name</a>
4.建立tls加密
bash
[root@master services]# openssl req -newkey rsa:2048 -nodes -keyout tls.key -x509 -days 365 -subj "/CN=nginxsvc/O=nginxsvc" -out tls.crt
[root@master services]# kubectl create secret tls web-tls-secret --key tls.key --cert tls.crt
[root@master services]# kubectl get secrets
NAME TYPE DATA AGE
web-tls-secret kubernetes.io/tls 2 3m7s
[root@master services]# vim 3-ingress.yml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
name: webcluster
spec:
tls:
- hosts:
- myapp1.timinglee.org
secretName: web-tls-secret
ingressClassName: nginx
rules:
- host: myapp1.timinglee.org
http:
paths:
- backend:
service:
name: myapp1
port:
number: 80
path: /
pathType: Prefix
[root@master services]# kubectl apply -f 3-ingress.yml
ingress.networking.k8s.io/webcluster created
[root@master services]# kubectl describe ingress
Name: webcluster
Labels: <none>
Namespace: default
Address: 172.25.254.20
Ingress Class: nginx
Default backend: <default>
TLS:
web-tls-secret terminates myapp1.timinglee.org
Rules:
Host Path Backends
---- ---- --------
myapp1.timinglee.org
/ myapp1:80 (10.244.1.60:80)
Annotations: nginx.ingress.kubernetes.io/rewrite-target: /
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Sync 5s (x2 over 15s) nginx-ingress-controller Scheduled for sync
#访问测试
[root@master services]# curl -k https://myapp1.timinglee.org
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
5.建立auth认证
bash
[root@master services]# dnf install httpd-tools -y
[root@master services]# htpasswd -cm auth lee
New password:
Re-type new password:
Adding password for user lee
[root@master services]# cat auth
lee:$apr1$hNTmKmxS$qyM7y53AAzPaIUyCNJcgy.
[root@master services]# kubectl get secrets auth-web -o yaml
apiVersion: v1
data:
auth: bGVlOiRhcHIxJGhOVG1LbXhTJHF5TTd5NTNBQXpQYUlVeUNOSmNneS4K
kind: Secret
metadata:
creationTimestamp: "2026-04-12T08:00:40Z"
name: auth-web
namespace: default
resourceVersion: "163922"
uid: e28c67f8-5465-4ef2-a076-e205bcaf6887
type: Opaque
[root@master services]# echo "bGVlOiRhcHIxJGhOVG1LbXhTJHF5TTd5NTNBQXpQYUlVeUNOSmNneS4K" | base64 -d
lee:$apr1$hNTmKmxS$qyM7y53AAzPaIUyCNJcgy.
[root@master services]# cat auth
lee:$apr1$hNTmKmxS$qyM7y53AAzPaIUyCNJcgy.
[root@master services]# vim 4-ingress.yml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
nginx.ingress.kubernetes.io/auth-type: basic
nginx.ingress.kubernetes.io/auth-secret: auth-web
nginx.ingress.kubernetes.io/auth-realm: "Please input username and password"
nginx.ingress.kubernetes.io/rewrite-target: /
name: webcluster
spec:
tls:
- hosts:
- myapp1.timinglee.org
secretName: web-tls-secret
ingressClassName: nginx
rules:
- host: myapp1.timinglee.org
http:
paths:
- backend:
service:
name: myapp1
port:
number: 80
path: /
pathType: Prefix
[root@master services]# kubectl apply -f 4-ingress.yml
ingress.networking.k8s.io/webcluster created
#测试:
[root@master services]# kubectl describe ingress
Name: webcluster
Labels: <none>
Namespace: default
Address: 172.25.254.20
Ingress Class: nginx
Default backend: <default>
TLS:
web-tls-secret terminates myapp1.timinglee.org
Rules:
Host Path Backends
---- ---- --------
myapp1.timinglee.org
/ myapp1:80 (10.244.1.60:80)
Annotations: nginx.ingress.kubernetes.io/auth-realm: Please input username and password
nginx.ingress.kubernetes.io/auth-secret: auth-web
nginx.ingress.kubernetes.io/auth-type: basic
nginx.ingress.kubernetes.io/rewrite-target: /
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Sync 2s (x2 over 17s) nginx-ingress-controller Scheduled for sync
[root@master services]# curl -k https://myapp1.timinglee.org
<html>
<head><title>401 Authorization Required</title></head>
<body>
<center><h1>401 Authorization Required</h1></center>
<hr><center>nginx</center>
</body>
</html>
[root@master services]# curl -k https://myapp1.timinglee.org -u lee:lee
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
6.网页重写
示例1
bash
[root@master services]# vim 5-ingress.yml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
nginx.ingress.kubernetes.io/app-root: /hostname.html #设定默认测试也
nginx.ingress.kubernetes.io/auth-type: basic
nginx.ingress.kubernetes.io/auth-secret: auth-web
nginx.ingress.kubernetes.io/auth-realm: "Please input username and password"
nginx.ingress.kubernetes.io/rewrite-target: /
name: webcluster
spec:
tls:
- hosts:
- myapp1.timinglee.org
secretName: web-tls-secret
ingressClassName: nginx
rules:
- host: myapp1.timinglee.org
http:
paths:
- backend:
service:
name: myapp1
port:
number: 80
path: /
pathType: Prefix
[root@master services]# kubectl apply -f 5-ingress.yml
ingress.networking.k8s.io/webcluster created
[root@master services]# kubectl describe ingress webcluster
Name: webcluster
Labels: <none>
Namespace: default
Address:
Ingress Class: nginx
Default backend: <default>
TLS:
web-tls-secret terminates myapp1.timinglee.org
Rules:
Host Path Backends
---- ---- --------
myapp1.timinglee.org
/ myapp1:80 (10.244.1.60:80)
Annotations: nginx.ingress.kubernetes.io/app-root: /hostname.html
nginx.ingress.kubernetes.io/auth-realm: Please input username and password
nginx.ingress.kubernetes.io/auth-secret: auth-web
nginx.ingress.kubernetes.io/auth-type: basic
nginx.ingress.kubernetes.io/rewrite-target: /
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Sync 12s nginx-ingress-controller Scheduled for sync
#测试:
[root@master services]# curl -lk https://myapp1.timinglee.org -u lee:lee
<html>
<head><title>302 Found</title></head>
<body>
<center><h1>302 Found</h1></center>
<hr><center>nginx</center>
</body>
</html>
[root@master services]# curl -Lk https://myapp1.timinglee.org -u lee:lee
myapp1-8456b584d6-7g4lq
示例2
b
[root@master services]# vim 5-ingress.yml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$2
nginx.ingress.kubernetes.io/use-regex: "true"
nginx.ingress.kubernetes.io/auth-type: basic
nginx.ingress.kubernetes.io/auth-secret: auth-web
nginx.ingress.kubernetes.io/auth-realm: "Please input username and password"
nginx.ingress.kubernetes.io/rewrite-target: /
name: webcluster
spec:
tls:
- hosts:
- myapp1.timinglee.org
secretName: web-tls-secret
ingressClassName: nginx
rules:
- host: myapp1.timinglee.org
http:
paths:
- backend:
service:
name: myapp1
port:
number: 80
path: /lee(/|$)(.*)
pathType: ImplementationSpecific
[root@master services]# kubectl apply -f 5-ingress.yml
ingress.networking.k8s.io/webcluster created
[root@master services]# kubectl describe ingress webcluster
Name: webcluster
Labels: <none>
Namespace: default
Address:
Ingress Class: nginx
Default backend: <default>
TLS:
web-tls-secret terminates myapp1.timinglee.org
Rules:
Host Path Backends
---- ---- --------
myapp1.timinglee.org
/lee(/|$)(.*) myapp1:80 (10.244.1.60:80)
Annotations: nginx.ingress.kubernetes.io/auth-realm: Please input username and password
nginx.ingress.kubernetes.io/auth-secret: auth-web
nginx.ingress.kubernetes.io/auth-type: basic
nginx.ingress.kubernetes.io/rewrite-target: /
nginx.ingress.kubernetes.io/use-regex: true
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Sync 13s nginx-ingress-controller Scheduled for sync
[root@master services]# curl -Lk https://myapp1.timinglee.org/lee/hostname.html -u lee:lee
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
[root@master services]# curl -Lk https://myapp1.timinglee.org/lee/aaaa/ -u lee:lee
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
[root@master services]# curl -Lk https://myapp1.timinglee.org/lee/aaaa/ -u lee:lee
9.Canary金丝雀发布
一句话理解:先让一小部分用户使用新版本,验证没问题后再逐步扩大到所有人。
1.基于head的灰度发布
bash
[root@master services]# vim 6-ingress.yml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
name: webcluster
spec:
ingressClassName: nginx
rules:
- host: myapp1.timinglee.org
http:
paths:
- backend:
service:
name: myapp1
port:
number: 80
path: /
pathType: Prefix
[root@master services]# kubectl apply -f 6-ingress.yml
ingress.networking.k8s.io/webcluster created
[root@master services]# kubectl describe ingress webcluster
Name: webcluster
Labels: <none>
Namespace: default
Address:
Ingress Class: nginx
Default backend: <default>
Rules:
Host Path Backends
---- ---- --------
myapp1.timinglee.org
/ myapp1:80 (10.244.1.60:80)
Annotations: nginx.ingress.kubernetes.io/rewrite-target: /
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Sync 7s nginx-ingress-controller Scheduled for sync
[root@master services]# curl myapp1.timinglee.org
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
#建立新的ingress
[root@master services]# vim 7-ingress.yml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
nginx.ingress.kubernetes.io/canary: "true"
nginx.ingress.kubernetes.io/canary-by-header: "version"
nginx.ingress.kubernetes.io/canary-by-header-value: "2"
nginx.ingress.kubernetes.io/rewrite-target: /
name: webcluster-new
spec:
ingressClassName: nginx
rules:
- host: myapp1.timinglee.org
http:
paths:
- backend:
service:
name: myapp2
port:
number: 80
path: /
pathType: Prefix
root@master services]# kubectl apply -f 7-ingress.yml
ingress.networking.k8s.io/webcluster-new created
[root@master services]# kubectl get ingress
NAME CLASS HOSTS ADDRESS PORTS AGE
webcluster nginx myapp1.timinglee.org 172.25.254.20 80 3m38s
webcluster-new nginx myapp1.timinglee.org 80 6s
[root@master services]# curl myapp1.timinglee.org
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
[root@master services]# curl -H "version:2" myapp1.timinglee.org
Hello MyApp | Version: v2 | <a href="hostname.html">Pod Name</a>
2.基于权重的灰度发布
bash
[root@master services]# vim 7-ingress.yml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
nginx.ingress.kubernetes.io/canary: "true"
nginx.ingress.kubernetes.io/canary-weight: "10"
nginx.ingress.kubernetes.io/canary-weight-total: "100"
nginx.ingress.kubernetes.io/rewrite-target: /
name: webcluster-new
spec:
ingressClassName: nginx
rules:
- host: myapp1.timinglee.org
http:
paths:
- backend:
service:
name: myapp2
port:
number: 80
path: /
pathType: Prefix
[root@master services]# kubectl apply -f 7-ingress.yml
ingress.networking.k8s.io/webcluster-new created
[root@master services]# kubectl get ingress
NAME CLASS HOSTS ADDRESS PORTS AGE
webcluster nginx myapp1.timinglee.org 172.25.254.20 80 7m37s
webcluster-new nginx myapp1.timinglee.org 172.25.254.20 80 6s
#书写测试脚本
[root@master services]# vim check.sh
#!/bin/bash
v1=0
v2=0
for (( i=0; i<100; i++))
do
response=`curl -s myapp1.timinglee.org |grep -c v1`
v1=`expr $v1 + $response`
v2=`expr $v2 + 1 - $response`
done
echo "v1:$v1, v2:$v2"
[root@master services]# sh check.sh
v1:89, v2:11
五.ConfigMap
ConfigMap = K8s 配置中心
专门存配置信息:比如 IP、端口、数据库地址、配置文件内容
跟容器、代码分开管理,改配置不用改镜像、不用重新打包
Pod 可以直接 "读" 这个小本本,拿来当环境变量、当配置文件用
1.ConfigMap的创建
通过字符方式(通过键值对的方式)
bash
[root@master storage]# kubectl create cm timinglee --from-literal fname=timing --from-literal lname=lee
configmap/timinglee created
#查看有哪些ConfigMap
[root@master storage]# kubectl get cm
NAME DATA AGE
kube-root-ca.crt 1 20d
timinglee 2 13s
timinglee 2 13s
#查看timinglee的详细信息
[root@master storage]# kubectl describe cm timinglee
Name: timinglee
Namespace: default
Labels: <none>
Annotations: <none>
Data
====
fname:
----
timing
lname:
----
lee
BinaryData
====
Events: <none>
#以yaml的格式查看完整结构
[root@master storage]# kubectl get cm timinglee -o yaml
apiVersion: v1
data:
fname: timing
lname: lee
kind: ConfigMap
metadata:
creationTimestamp: "2026-04-18T01:53:54Z"
name: timinglee
namespace: default
resourceVersion: "198048"
uid: 317a3043-d4bd-4d13-8ad7-376daf66e3ba
=======================================
解释:
[root@master storage]# kubectl create cm timinglee --from-literal fname=timing --from-literal lname=lee
#kubectl create cm:创建一个 ConfigMap
#timinglee:给这个配置本起个名字
#--from-literal 键=值:直接写一条配置
#相当于在小本本上写:
#fname = timing
#lname = lee
通过文件方式
bash
[root@master storage]# echo hello timinglee > timinglee
[root@master storage]# kubectl create cm timinglee2 --from-file timinglee
configmap/timinglee2 created
[root@master storage]# kubectl describe cm timinglee2
Name: timinglee2
Namespace: default
Labels: <none>
Annotations: <none>
Data
====
timinglee:
----
hello timinglee
BinaryData
====
Events: <none>
======================================
#--from-file 文件名:把这个文件整个塞进 ConfigMap
#key 就是文件名,value 就是文件内容
通过目录方式
bash
[root@master storage]# mkdir test
[root@master storage]# echo timinglee > test/tfile
[root@master storage]# echo lee > test/lfile
[root@master storage]# ls test/
lfile tfile
[root@master storage]# cat test/*
lee
timinglee
[root@master storage]# kubectl create cm timinglee3 --from-file test/
configmap/timinglee3 created
[root@master storage]# kubectl describe cm timinglee3
Name: timinglee3
Namespace: default
Labels: <none>
Annotations: <none>
Data
====
lfile:
----
lee
tfile:
----
timinglee
BinaryData
====
Events: <none>
[root@master storage]#
==================================
#--from-file 目录/:把目录下所有文件都塞进 ConfigMap
#每个文件名 = key,文件内容 = value
通过yaml方式
bash
[root@master storage]# kubectl create cm timinglee4 --from-literal timinglee=abc --dry-run=client -o yaml > timinglee4.yaml
[root@master storage]# vim timinglee4.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: timinglee4
data:
timinglee: abc
lee: def
=====================================
#--dry-run=client:只模拟创建,不真创建
#-o yaml:输出 yaml 格式
#最后用 kubectl apply -f xxx.yaml 生效
2.ConfigMap的使用方法
使用configmap填充环境变量,当作环境注入到Pod里面
bash
#填充自定义变量
[root@master storage]# vim timinglee4.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: timinglee
data:
ipaddress: "172.25.254.50"
port: "3306"
[root@master storage]# kubectl describe cm timinglee
Name: timinglee
Namespace: default
Labels: <none>
Annotations: <none>
Data
====
ipaddress:
----
172.25.254.50
port:
----
3306
BinaryData
====
Events: <none>
[root@master storage]# kubectl run testpod --image busybox --dry-run=client -o yaml > testpod.yml
apiVersion: v1
kind: Pod
metadata:
labels:
run: testpod
name: testpod
spec:
containers:
- image: busybox
name: testpod
command:
- /bin/sh
- -c
- env
env:
- name: key1 #设定key1的值
valueFrom:
configMapKeyRef:
name: timinglee
key: ipaddress
- name: key2 #设定key2的值
valueFrom:
configMapKeyRef:
name: timinglee
key: port
restartPolicy: Never
[root@master storage]# kubectl apply -f testpod.yml
[root@master storage]# kubectl logs pods/testpod
KUBERNETES_SERVICE_PORT=443
KUBERNETES_PORT=tcp://10.96.0.1:443
HOSTNAME=testpod
SHLVL=1
HOME=/root
KUBERNETES_PORT_443_TCP_ADDR=10.96.0.1
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
KUBERNETES_PORT_443_TCP_PORT=443
key1=172.25.254.50 #key1
KUBERNETES_PORT_443_TCP_PROTO=tcp
key2=3306 #key2
KUBERNETES_PORT_443_TCP=tcp://10.96.0.1:443
KUBERNETES_SERVICE_PORT_HTTPS=443
KUBERNETES_SERVICE_HOST=10.96.0.1
PWD=/
[root@master storage]# kubectl delete pods testpod
pod "testpod" deleted from default namespace
===================================
解释
env:
- name: key1
valueFrom:
configMapKeyRef:
name: timinglee
key: ipaddress
意思:把 ConfigMap timinglee 里的 ipaddress 读到容器里,名字叫 key1
一次性把 ConfigMap 所有键值都变成环境变量
yaml
envFrom:
- configMapRef:
name: timinglee
容器里直接能用:ipaddress、port 这些变量
容器里可以直接 echo ${ipaddress} 调用
直接把configMap中的变量填充到系统给变量
bash
[root@master storage]# vim testpod.yml
apiVersion: v1
kind: Pod
metadata:
labels:
run: testpod
name: testpod
spec:
containers:
- image: busybox
name: testpod
command:
- /bin/sh
- -c
- env
envFrom:
- configMapRef:
name: timinglee
restartPolicy: Never
[root@master storage]# kubectl apply -f testpod.yml
pod/testpod created
[root@master storage]# kubectl logs pods/testpod
KUBERNETES_SERVICE_PORT=443
KUBERNETES_PORT=tcp://10.96.0.1:443
HOSTNAME=testpod
SHLVL=1
HOME=/root
KUBERNETES_PORT_443_TCP_ADDR=10.96.0.1
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
port=3306
KUBERNETES_PORT_443_TCP_PORT=443
KUBERNETES_PORT_443_TCP_PROTO=tcp
ipaddress=172.25.254.50
KUBERNETES_SERVICE_PORT_HTTPS=443
KUBERNETES_PORT_443_TCP=tcp://10.96.0.1:443
KUBERNETES_SERVICE_HOST=10.96.0.1
PWD=/
变量的表示方式
bash
[root@master storage]# vim testpod.yml
apiVersion: v1
kind: Pod
metadata:
labels:
run: testpod
name: testpod
spec:
containers:
- image: busybox
name: testpod
command:
- /bin/sh
- -c
- echo ${ipaddress}_${port}
envFrom:
- configMapRef:
name: timinglee
restartPolicy: Never
[root@master storage]# kubectl apply -f testpod.yml
pod/testpod created
[root@master storage]# kubectl logs pods/testpod
172.25.254.50_3306
通过数据卷使用configmap
bash
[root@master storage]# vim testpod.yml
apiVersion: v1
kind: Pod
metadata:
labels:
run: testpod
name: testpod
spec:
containers:
- image: busybox
name: testpod
command:
- /bin/sh
- -c
- sleep 100000
volumeMounts:
- name: config-volume
mountPath: /config
volumes:
- name: config-volume
configMap:
name: timinglee
restartPolicy: Never
[root@master storage]# kubectl exec -it pods/testpod -- /bin/sh
/ #
/ # ls
bin config dev etc home lib lib64 proc root sys tmp usr var
/ # config/
/bin/sh: config/: Permission denied
/ # cd config/
/config # ls
ipaddress port
/config # cat ipaddress
/config # cat port
[root@master storage]# kubectl delete -f testpod.yml --force
Warning: Immediate deletion does not wait for confirmation that the running resource has been terminated. The resource may continue to run on the cluster indefinitely.
pod "testpod" force deleted from default namespace
==============================
解释
volumeMounts:
- name: config-volume
mountPath: /config
volumes:
- name: config-volume
configMap:
name: timinglee
把 ConfigMap 变成一个数据卷
挂载到容器里的 /config 目录
进入容器 /config 下,能看到一个个文件:
ipaddress
port
内容就是 ConfigMap 里的值
通过cm来配置pod
b
[root@master storage]# vim nginx.conf
server {
listen 8000;
server_name _;
root /usr/share/nginx/html;
index index.html;
}
[root@master storage]# kubectl create cm nginx --from-file nginx.conf
configmap/nginx created
[root@master storage]# kubectl describe cm nginx
Name: nginx
Namespace: default
Labels: <none>
Annotations: <none>
Data
====
nginx.conf:
----
server {
listen 8000;
server_name _;
root /usr/share/nginx/html;
index index.html;
}
BinaryData
====
Events: <none>
[root@master storage]# vim testpod.yml
apiVersion: v1
kind: Pod
metadata:
labels:
run: nginx
name: nginx
spec:
containers:
- image: nginx
name: nginx
volumeMounts:
- name: config-volume
mountPath: /etc/nginx/conf.d
volumes:
- name: config-volume
configMap:
name: nginx
restartPolicy: Never
[root@master storage]# kubectl apply -f testpod.yml
pod/nginx created
[root@master storage]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx 1/1 Running 0 4s 10.244.1.8 node1 <none> <none>
[root@master storage]# curl 10.244.1.8:8000
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
通过修改该cm的内容来更新配置
bash
# 直接改cm的配置
[root@master storage]# kubectl edit cm nginx
apiVersion: v1
data:
nginx.conf: |
server {
listen 8080;
server_name _;
root /usr/share/nginx/html;
index index.html;
}
kind: ConfigMap
metadata:
creationTimestamp: "2026-04-18T03:00:42Z"
name: nginx
namespace: default
resourceVersion: "205203"
uid: 7719bbed-19b4-40a4-8b13-2888e8404dbd
[root@master storage]# kubectl delete -f testpod.yml
[root@master storage]# kubectl apply -f testpod.yml
[root@master storage]# curl 10.244.1.9:8080
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
2.Secrets
Secret 超简单介绍
Secret 就是 K8s 里用来存敏感信息的 ConfigMap,专门放不能明文暴露的内容。
一句话记住
ConfigMap:存普通配置(IP、端口、配置文件)
Secret:存密码、密钥、Token、证书等私密数据
3 个特点
加密存储,不会明文暴露
使用方式和 ConfigMap 几乎一样:
当环境变量注入
挂载成文件目录
1.secrets建立
通过命令方式建立
bash
[root@master storage]# kubectl create secret generic timinglee --from-literal userlist=timinglee --from-literal password=lee
[root@master storage]# kubectl get secrets
NAME TYPE DATA AGE
auth-web Opaque 1 5d19h
timinglee Opaque 2 2m48s
web-tls-secret kubernetes.io/tls 2 5d19h
[root@master storage]# kubectl describe secrets timinglee
Name: timinglee
Namespace: default
Labels: <none>
Annotations: <none>
Type: Opaque
Data
====
password: 3 bytes
userlist: 9 bytes
[root@master storage]# kubectl get secrets -o yaml timinglee
apiVersion: v1
data:
password: bGVl
userlist: dGltaW5nbGVl
kind: Secret
metadata:
creationTimestamp: "2026-04-18T03:40:21Z"
name: timinglee
namespace: default
resourceVersion: "208583"
uid: 40fed44d-a1a9-4d31-8841-d55c7fd2c901
type: Opaque
[root@master storage]# echo -n "dGltaW5nbGVl" | base64 -d
timinglee[root@master storage]#
[root@master storage]# kubectl delete secrets timinglee
secret "timinglee" deleted from default namespace
通过文件方式建立
bash
[root@master storage]# echo timinglee > userlist
[root@master storage]# echo lee > password
[root@master storage]# cat userlist
timinglee
[root@master storage]# cat password
lee
[root@master storage]# kubectl create secret generic timinglee --from-file userlist --from-file password
secret/timinglee created
[root@master storage]# kubectl get secrets
NAME TYPE DATA AGE
auth-web Opaque 1 5d19h
timinglee Opaque 2 2m48s
web-tls-secret kubernetes.io/tls 2 5d19h
通过yaml的方式建立
bash
[root@master storage]# kubectl create secret generic timinglee --from-literal userlist=timinglee --dry-run=client -o yaml > timinglee_secrets.yml
[root@node1 ~]# echo -n "timinglee" | base64
dGltaW5nbGVl
[root@node1 ~]# echo -n "123" | base64
MTIz
[root@master storage]# vim timinglee_secrets.yml
apiVersion: v1
kind: Secret
metadata:
name: timinglee
data:
userlist: dGltaW5nbGVl
password: MTIz
[root@master storage]# kubectl apply -f timinglee_secrets.yml
secret/timinglee created
[root@master storage]# kubectl describe secrets timinglee
Name: timinglee
Namespace: default
Labels: <none>
Annotations: <none>
Type: Opaque
Data
====
password: 3 bytes
userlist: 9 bytes
2.secrets用法
注入pod文件中
bash
[root@master storage]# vim testpod.yml
apiVersion: v1
kind: Pod
metadata:
labels:
run: busybox
name: busybox
spec:
containers:
- image: busybox
name: busybox
command:
- /bin/sh
- -c
- sleep 10000000
volumeMounts:
- name: config-volume
mountPath: /userlist
volumes:
- name: config-volume
secret:
secretName: timinglee
restartPolicy: Never
[root@master storage]# kubectl apply -f testpod.yml
[root@master storage]# kubectl exec -it pods/busybox -- /bin/sh
/ # cat userlist/userlist
====================================
解释
volumeMounts:
- name: config-volume # 卷的名字(要和下面对应)
mountPath: /userlist # 把Secret挂载到容器这个目录
volumes: # 定义卷
- name: config-volume # 卷名字(和上面一致)
secret: # 卷类型:Secret
secretName: timinglee # 使用的Secret名字叫 timinglee
restartPolicy: Never # 挂了不重启
注入pod指定文件中
bash
[root@master storage]# vim testpod.yml
apiVersion: v1
kind: Pod
metadata:
labels:
run: busybox
name: busybox
spec:
containers:
- image: busybox
name: busybox
command:
- /bin/sh
- -c
- sleep 10000000
volumeMounts:
- name: config-volume
mountPath: /userlist
volumes:
- name: config-volume
secret:
secretName: timinglee
items:
- key: userlist
path: my-users/username
restartPolicy: Never
[root@master storage]# kubectl apply -f testpod.yml
/ # ls /userlist/my-users/username
/userlist/my-users/username
/ # cat /userlist/my-users/username
timinglee/ #
将Secret设置为环境变量
b
[root@master storage]# vim testpod.yml
apiVersion: v1
kind: Pod
metadata:
labels:
run: busybox
name: busybox
spec:
containers:
- image: busybox
name: busybox
command:
- /bin/sh
- -c
- env
env:
- name: USERNAME
valueFrom:
secretKeyRef:
name: timinglee
key: userlist
- name: PASSWD
valueFrom:
secretKeyRef:
name: timinglee
key: password
restartPolicy: Never
[root@master storage]# kubectl apply -f testpod.yml
[root@master storage]# kubectl logs pods/busybox busybox
KUBERNETES_PORT=tcp://10.96.0.1:443
KUBERNETES_SERVICE_PORT=443
HOSTNAME=busybox
SHLVL=1
HOME=/root
USERNAME=timinglee
KUBERNETES_PORT_443_TCP_ADDR=10.96.0.1
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
KUBERNETES_PORT_443_TCP_PORT=443
KUBERNETES_PORT_443_TCP_PROTO=tcp
PASSWD=123
KUBERNETES_PORT_443_TCP=tcp://10.96.0.1:443
KUBERNETES_SERVICE_PORT_HTTPS=443
KUBERNETES_SERVICE_HOST=10.96.0.1
PWD=/
存储docker registry的认证信息
建立harbor私有仓库



上传镜像到私有仓管库
b
[root@master storage]# docker tag timinglee/myapp:v1 reg.timinglee.org/timinglee/myapp:v1
[root@master storage]# docker push reg.timinglee.org/timinglee/myapp:v1
The push refers to repository [reg.timinglee.org/timinglee/myapp]
a0d2c4392b06: Pushed
05a9e65e2d53: Mounted from library/myapp
68695a6cfd7d: Mounted from library/myapp
c1dc81a64903: Mounted from library/myapp
8460a579ab63: Mounted from library/myapp
d39d92664027: Mounted from library/myapp
v1: digest: sha256:9eeca44ba2d410e54fccc54cbe9c021802aa8b9836a0bcf3d3229354e4c8870e size: 1569
使用私有仓库镜像时的问题
b
[root@master storage]# vim testpod.yml
apiVersion: v1
kind: Pod
metadata:
labels:
run: myapp
name: myapp
spec:
containers:
- image: reg.timinglee.org/timinglee/myapp:v1
name: myapp
[root@master storage]# kubectl apply -f testpod.yml
pod/myapp created
[root@master storage]# kubectl get pods
NAME READY STATUS RESTARTS AGE
myapp 0/1 ImagePullBackOff 0 26s
[root@master storage]# kubectl describe pods myapp
。。。。。。
/timinglee/myapp:v1"
Warning Failed 23s (x2 over 39s) kubelet spec.containers{myapp}: Failed to pull image "reg.timinglee.org/timinglee/myapp:v1": Error response from daemon: unauthorized: unauthorized to access repository: timinglee/myapp, action: pull: unauthorized to access repository: timinglee/myapp, action: pull
制作用户docker认证的secrets
bash
[root@master storage]# kubectl create secret docker-registry docker-auth --docker-server reg.timinglee.org --docker-username admin --docker-password lee --docker-email timinglee@timinglee.org
secret/docker-auth created
[root@master storage]# kubectl get secrets docker-auth
NAME TYPE DATA AGE
docker-auth kubernetes.io/dockerconfigjson 1 9s
[root@master storage]# kubectl describe secrets docker-auth
Name: docker-auth
Namespace: default
Labels: <none>
Annotations: <none>
Type: kubernetes.io/dockerconfigjson
Data
====
.dockerconfigjson: 125 bytes
[root@master storage]# kubectl get secrets docker-auth -o yaml
apiVersion: v1
data:
.dockerconfigjson: eyJhdXRocyI6eyJyZWcudGltaW5nbGVlLm9yZyI6eyJ1c2VybmFtZSI6ImFkbWluIiwicGFzc3dvcmQiOiJsZWUiLCJlbWFpbCI6InRpbWluZ2xlZUB0aW1pbmdsZWUub3JnIiwiYXV0aCI6IllXUnRhVzQ2YkdWbCJ9fX0=
kind: Secret
metadata:
creationTimestamp: "2026-04-18T06:50:55Z"
name: docker-auth
namespace: default
resourceVersion: "227424"
uid: 18c3b7d1-352b-4108-ac3c-68067646f091
type: kubernetes.io/dockerconfigjson
[root@master storage]# echo -n "eyJhdXRocyI6eyJyZWcudGltaW5nbGVlLm9yZyI6eyJ1c2VybmFtZSI6ImFkbWluIiwicGFzc3dvcmQiOiJsZWUiLCJlbWFpbCI6InRpbWluZ2xlZUB0aW1pbmdsZWUub3JnIiwiYXV0aCI6IllXUnRhVzQ2YkdWbCJ9fX0=" | base64 -d
{"auths":{"reg.timinglee.org":{"username":"admin","password":"lee","email":"timinglee@timinglee.org","auth":"YWRtaW46bGVl"}}}
====================================
解释
[root@master storage]# kubectl create secret docker-registry docker-auth --docker-server reg.timinglee.org --docker-username admin --docker-password lee --docker-email timinglee@timinglee.org
docker-registry docker-auth secret名字
--docker-server reg.timinglee.org 仓库地址
--docker-username admin --docker-password lee --docker-email timinglee@timinglee.org 仓库账号,密码,邮箱(必须写,可以随便写)
使用docker的secrets
bash
apiVersion: v1
kind: Pod
metadata:
labels:
run: myapp
name: myapp
spec:
containers:
- image: reg.timinglee.org/timinglee/myapp:v1
name: myapp
imagePullSecrets:
- name: docker-auth
[root@master storage]# kubectl get pods
NAME READY STATUS RESTARTS AGE
myapp 1/1 Running 0 3s
六. Volumes
1.empty卷
emptyDir = Pod 专用的临时文件夹
跟着 Pod 创建而创建,Pod 删除就一起删掉
同一个 Pod 里的多个容器可以共享这个目录
不持久,Pod 没了数据就没了
b
[root@master ~]# kubectl run empty --image=busybox --dry-run=client -o yaml > empty.yml
[root@master volumes]# vim empty.yml
kind: Pod
metadata:
labels:
run: empty
name: empty
spec:
containers:
- image: busybox
name: busybox
command:
- /bin/sh
- -c
- sleep 100000
volumeMounts:
- mountPath: /cache
name: cache-vol
- image: nginx
name: nginx
volumeMounts:
- mountPath: /usr/share/nginx/html
name: cache-vol
volumes:
- name: cache-vol
emptyDir:
medium: Memory
sizeLimit: 100Mi
[root@master volumes]# kubectl apply -f empty.yml
[root@master volumes]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
empty 2/2 Running 0 66s 10.244.1.16 node1 <none> <none>
[root@master volumes]# curl 10.244.1.16
<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx/1.27.1</center>
</body>
</html>
[root@master volumes]# kubectl exec -it pods/empty -c busybox -- /bin/sh
/ #
/ # ls
bin cache dev etc home lib lib64 proc root sys tmp usr var
/ # cd cache/
/cache # ls
/cache # echo hello timinglee > index.html
/cache # dd if=/dev/zero of=bigfile bs=1M count=99
99+0 records in
99+0 records out
103809024 bytes (99.0MB) copied, 0.246898 seconds, 401.0MB/s
/cache # dd if=/dev/zero of=bigfile bs=1M count=100
dd: error writing 'bigfile': No space left on device
100+0 records in
99+0 records out
104853504 bytes (100.0MB) copied, 0.029183 seconds, 3.3GB/s
[root@master ~]# curl 10.244.1.16
hello timinglee
==============================
解释
volumeMounts:
- name: cache-vol # 卷的名字(要和下面对应)
mountPath: /cache # 把 emptyDir 挂载到容器这个目录
volumes: # 定义卷
- name: cache-vol # 卷名字(和上面一致)
emptyDir: # 卷类型:emptyDir(临时共享目录)
medium: Memory # 存储介质:内存(速度快,不持久)
sizeLimit: 100Mi # 最大限制 100MB
restartPolicy: Never # 挂了不重启
2.hostPath
把 K8s 节点本机的文件夹 挂载到 Pod 里
数据存在 当前节点的硬盘上
换个节点跑 Pod,数据就看不到了
适合:单机测试、日志、节点本地文件
bash
[root@master volumes]# kubectl run hostpath --image nginx --dry-run=client -o yaml > hostpath.yml
[root@master volumes]# vim hostpath.yml
apiVersion: v1
kind: Pod
metadata:
labels:
run: hostpath
name: hostpath
spec:
containers:
- image: nginx
name: hostpath
volumeMounts:
- mountPath: /usr/share/nginx/html
name: timinglee
volumes:
- name: timinglee
hostPath:
path: /data
type: DirectoryOrCreate
[root@master volumes]# kubectl apply -f hostpath.yml
pod/hostpath created
[root@master volumes]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
hostpath 1/1 Running 0 14s 10.244.1.17 node1 <none> <none>
[root@master volumes]# curl 10.244.1.17
\<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx/1.27.1</center>
</body>
</html>
ssh -l root node1
[root@node1 ~]# ll /data/
总用量 0
[root@node1 ~]# echo hello timinglee > /data/index.html
[root@node1 ~]# exit
注销
Connection to node1 closed.
[root@master volumes]# curl 10.244.1.17
hello timinglee
3.nfs卷
把 一台独立服务器的共享文件夹 挂载到 Pod
数据存在 NFS 服务器,所有节点都能访问
Pod 飘到任何节点,数据都一样
适合:真正持久化、多节点共享、数据库 / 文件存储
建立nfs共享存储
b
#node3不在k8s里面,在集群之外
[root@node3 ~]# mkdir /share
[root@node3 ~]# dnf install nfs-utils -y
[root@node3 ~]# systemctl enable --now nfs-server.service
Created symlink /etc/systemd/system/multi-user.target.wants/nfs-server.service → /usr/lib/systemd/system/nfs-server.service.
[root@node3 ~]# vim /etc/exports
/share *(sync,rw,no_root_squash)
[root@node3 ~]# exportfs -rv
exporting *:/share
[root@node3 ~]# showmount -e
Export list for node3:
/share *
在所有work节点安装nfs-utils
bash
[root@master volumes]# for i in 10 20
> do
> ssh -l root 172.25.254.$i dnf install nfs-utils -y
> done
[root@master volumes]# for i in 10 20; do ssh -l root 172.25.254.$i showmount -e 172.25.254.30 ; done
Export list for 172.25.254.30:
/share *
Export list for 172.25.254.30:
/share *
建立nfs卷
b
[root@master volumes]# kubectl run web --image nginx --dry-run=client -o yaml >> nfs.yml
[root@master volumes]# vim nfs.yml
apiVersion: v1
kind: Pod
metadata:
labels:
run: web1
name: web1
spec:
nodeName: node1
containers:
- image: nginx
name: web1
volumeMounts:
- mountPath: /usr/share/nginx/html
name: cache-vol
volumes:
- name: cache-vol
nfs:
server: 172.25.254.30
path: /share
[root@master volumes]# kubectl apply -f nfs.yml
pod/web1 created
[root@master volumes]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
web1 1/1 Running 0 7s 10.244.1.18 node1 <none> <none>
[root@master volumes]# curl 10.244.1.18
<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx/1.27.1</center>
</body>
</html>
[root@node3 ~]# echo hello timinglee > /share/index.html
[root@master volumes]# curl 10.244.1.18
hello timinglee
[root@master volumes]# kubectl delete -f nfs.yml
pod "web1" deleted from default namespace
[root@master volumes]# vim nfs.yml
apiVersion: v1
kind: Pod
metadata:
labels:
run: web1
name: web1
spec:
nodeName: node2 #运行主机更改
containers:
- image: nginx
name: web1
volumeMounts:
- mountPath: /usr/share/nginx/html
name: cache-vol
volumes:
- name: cache-vol
nfs:
server: 172.25.254.30
path: /share
[root@master volumes]# kubectl apply -f nfs.yml
pod/web1 created
[root@master volumes]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
web1 1/1 Running 0 13s 10.244.2.5 node2 <none> <none>
[root@master volumes]# curl 10.244.2.5
hello timinglee
=========================
解释
volumes:
- name: cache-vol # 卷名字(和上面 volumeMounts 对应)
nfs: # 卷类型:NFS 网络共享存储
server: 172.25.254.30 # NFS 服务器的 IP 地址
path: /share # NFS 服务器上共享出来的目录
4.PersistentVolume持久卷
PV(PersistentVolume 持久卷):是管理员预先配置好的集群级存储空间,用于为容器提供持久化数据存储。
PVC(PersistentVolumeClaim 持久卷申请):是用户向集群申请使用存储的声明,用于让 Pod 绑定并使用 PV 中的存储。
1.静态持久卷
管理员提前手动创建好一批 PV(固定大小、固定类型),用户创建 PVC 时,K8s 从已有的 PV 里匹配绑定。
建立存储目录
bash
[root@node3 ~]# mkdir /share/pv{1..3} -p
[root@node3 ~]# dnf install nfs-utils -y
[root@node3 ~]# systemctl enable --now nfs-server
[root@node3 ~]# vim /etc/exports
/share *(sync,rw,no_root_squash)
[root@node3 ~]# exportfs -rv
exporting *:/share
#### 编写pv建立的yaml文件
bash
[root@master ~]# kubectl api-resources | less
[root@master volumes]# vim pv.yml
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv1
spec:
capacity:
storage: 5Gi
volumeMode: Filesystem
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: nfs
nfs:
path: /share/pv1
server: 172.25.254.30
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv2
spec:
capacity:
storage: 10Gi
volumeMode: Filesystem
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain
storageClassName: nfs
nfs:
path: /share/pv2
server: 172.25.254.30
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv3
spec:
capacity:
storage: 15Gi
volumeMode: Filesystem
accessModes:
- ReadOnlyMany
persistentVolumeReclaimPolicy: Retain
storageClassName: nfs
nfs:
path: /share/pv3
server: 172.25.254.30
[root@master volumes]# kubectl apply -f pv.yml
[root@master volumes]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE
pv1 5Gi RWO Retain Available nfs <unset> 117s
pv2 10Gi RWX Retain Available nfs <unset> 29s
pv3 15Gi ROX Retain Available nfs <unset> 29s
[root@master volumes]# vim pvc.yml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc1
spec:
storageClassName: nfs
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc2
spec:
storageClassName: nfs
accessModes:
- ReadWriteMany
resources:
requests:
storage: 10Gi
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc3
spec:
storageClassName: nfs
accessModes:
- ReadOnlyMany
resources:
requests:
storage: 15Gi
[root@master ~]# kubectl apply -f pvc.yml
[root@master volumes]# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
pvc1 Bound pv1 5Gi RWO nfs <unset> 66s
pvc2 Bound pv2 10Gi RWX nfs <unset> 66s
pvc3 Bound pv3 15Gi ROX nfs <unset> 66s
#Bound表示绑定成功
[root@master ~]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE
pv1 5Gi RWO Retain Bound default/pvc1 nfs <unset> 5m42s
pv2 10Gi RWX Retain Bound default/pvc2 nfs <unset> 4m14s
pv3 15Gi ROX Retain Bound default/pvc3 nfs <unset>
[root@master volumes]# vim checkpvpod.yml
apiVersion: v1
kind: Pod
metadata:
name: timinglee
spec:
containers:
- image: nginx
name: nginx
volumeMounts:
- mountPath: /usr/share/nginx/html
name: vol1
volumes:
- name: vol1
persistentVolumeClaim:
claimName: pvc1
[root@master volumes]# kubectl apply -f checkpvpod.yml
pod/timinglee created
#节点必须全部启动,包括harbor
[root@master volumes]# kubectl get pods
NAME READY STATUS RESTARTS AGE
timinglee 1/1 Running 0 3s
[root@master volumes]# curl 10.244.1.19
<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx/1.27.1</center>
</body>
</html>
[root@node3 pv1]# echo hello timinglee > index.html
#需要反应一会
[root@master volumes]# curl 10.244.1.19
hello timinglee
===============================
解释
# 定义第一个持久化存储卷 PV1
apiVersion: v1 # 使用K8s核心API版本
kind: PersistentVolume # 资源类型:持久化卷 PV
metadata:
name: pv1 # PV名称:pv1(全局唯一)
spec:
capacity:
storage: 5Gi # PV容量:5GB
volumeMode: Filesystem # 卷模式:文件系统(默认,最常用)
accessModes:
- ReadWriteOnce # 访问模式:只能被**单个节点**读写
- ReadWriteMany # 访问模式:**多节点同时读写**
- ReadOnlyMany # 访问模式:**多节点只读**
persistentVolumeReclaimPolicy: Retain # 回收策略:保留(删除PVC后,PV数据不删)
storageClassName: nfs # 存储类名称:nfs(用于和PVC匹配绑定)
nfs: # 使用NFS网络存储
path: /share/pv1 # NFS服务端共享的目录路径
server: 172.25.254.30 # NFS服务端IP地址
# 第一个 PVC:申请 1G 存储,用于绑定 PV
apiVersion: v1 # Kubernetes API 版本
kind: PersistentVolumeClaim # 资源类型:持久化存储申请(PVC)
metadata:
name: pvc1 # PVC 名字:pvc1
spec:
storageClassName: nfs # 存储类:必须和 PV 的 nfs 一致,才能绑定
accessModes:
- ReadWriteOnce # 访问模式:单节点读写 → 自动绑定 pv1
resources:
requests:
storage: 1Gi # 申请容量:1GB(只要 ≤ PV 容量就能绑定)
2.动态持久卷
上传所需镜像

bash
[root@master volumes]# docker load -i /root/nfs-subdir-external-provisioner-4.0.2.tar
1a5ede0c966b: Loading layer [==================================================>] 3.052MB/3.052MB
ad321585b8f5: Loading layer [==================================================>] 42.02MB/42.02MB
Loaded image: registry.k8s.io/sig-storage/nfs-subdir-external-provisioner:v4.0.2
[root@master volumes]# docker tag registry.k8s.io/sig-storage/nfs-subdir-external-provisioner:v4.0.2 reg.timinglee.org/sig-storage/nfs-subdir-external-provisioner:v4.0.2
[root@master volumes]# docker push reg.timinglee.org/sig-storage/nfs-subdir-external-provisioner:v4.0.2
The push refers to repository [reg.timinglee.org/sig-storage/nfs-subdir-external-provisioner]
ad321585b8f5: Pushed
1a5ede0c966b: Pushed
v4.0.2: digest: sha256:f741e403b3ca161e784163de3ebde9190905fdbf7dfaa463620ab8f16c0f6423 size: 739
[root@master volumes]# kubectl delete pvc pvc1
persistentvolumeclaim "pvc1" deleted from default namespace
[root@master volumes]# kubectl delete pvc pvc2
persistentvolumeclaim "pvc2" deleted from default namespace
[root@master volumes]# kubectl delete pvc pvc3
persistentvolumeclaim "pvc3" deleted from default namespace
[root@master volumes]# kubectl delete pv pv1
persistentvolume "pv1" deleted
[root@master volumes]# kubectl delete pv pv2
persistentvolume "pv2" deleted
[root@master volumes]# kubectl delete pv pv3
persistentvolume "pv3" deleted
建立授权
b
[root@master volumes]# vim storagesa.yml
apiVersion: v1
kind: Namespace
metadata:
name: nfs-client-provisioner
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: nfs-client-provisioner
namespace: nfs-client-provisioner
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: nfs-client-provisioner-runner
rules:
- apiGroups: [""]
resources: ["nodes"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["persistentvolumes"]
verbs: ["get", "list", "watch", "create", "delete"]
- apiGroups: [""]
resources: ["persistentvolumeclaims"]
verbs: ["get", "list", "watch", "update"]
- apiGroups: ["storage.k8s.io"]
resources: ["storageclasses"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["events"]
verbs: ["create", "update", "patch"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: run-nfs-client-provisioner
subjects:
- kind: ServiceAccount
name: nfs-client-provisioner
namespace: nfs-client-provisioner
roleRef:
kind: ClusterRole
name: nfs-client-provisioner-runner
apiGroup: rbac.authorization.k8s.io
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: leader-locking-nfs-client-provisioner
namespace: nfs-client-provisioner
rules:
- apiGroups: [""]
resources: ["endpoints"]
verbs: ["get", "list", "watch", "create", "update", "patch"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: leader-locking-nfs-client-provisioner
namespace: nfs-client-provisioner
subjects:
- kind: ServiceAccount
name: nfs-client-provisioner
namespace: nfs-client-provisioner
roleRef:
kind: Role
name: leader-locking-nfs-client-provisioner
apiGroup: rbac.authorization.k8s.io
[root@master volumes]# kubectl apply -f storagesa.yml
namespace/nfs-client-provisioner created
serviceaccount/nfs-client-provisioner created
clusterrole.rbac.authorization.k8s.io/nfs-client-provisioner-runner created
clusterrolebinding.rbac.authorization.k8s.io/run-nfs-client-provisioner created
role.rbac.authorization.k8s.io/leader-locking-nfs-client-provisioner created
rolebinding.rbac.authorization.k8s.io/leader-locking-nfs-client-provisioner created
[root@master volumes]# kubectl -n nfs-client-provisioner get sa
NAME AGE
default 2m23s
nfs-client-provisioner 2m23s
=======================================
解释
# 1. 创建命名空间:nfs-client-provisioner
# 作用:把自动供给程序放在独立空间,方便管理
apiVersion: v1
kind: Namespace
metadata:
name: nfs-client-provisioner
---
# 2. 创建服务账号
# 作用:给 NFS 自动供给程序一个"身份"
apiVersion: v1
kind: ServiceAccount
metadata:
name: nfs-client-provisioner
namespace: nfs-client-provisioner
---
# 3. 集群角色:赋予全局权限
# 作用:让程序有权管理 PV、PVC、StorageClass
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: nfs-client-provisioner-runner
rules:
- apiGroups: [""]
resources: ["nodes"] # 查看节点
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["persistentvolumes"] # 管理持久卷
verbs: ["get", "list", "watch", "create", "delete"]
- apiGroups: [""]
resources: ["persistentvolumeclaims"] # 管理存储申请
verbs: ["get", "list", "watch", "update"]
- apiGroups: ["storage.k8s.io"]
resources: ["storageclasses"] # 管理存储类
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["events"] # 记录事件
verbs: ["create", "update", "patch"]
---
# 4. 集群角色绑定
# 作用:把上面的全局权限 绑定 给服务账号
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: run-nfs-client-provisioner
subjects:
- kind: ServiceAccount
name: nfs-client-provisioner
namespace: nfs-client-provisioner
roleRef:
kind: ClusterRole
name: nfs-client-provisioner-runner
apiGroup: rbac.authorization.k8s.io
---
# 5. 普通角色:用于主节点锁(防止多实例冲突)
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: leader-locking-nfs-client-provisioner
namespace: nfs-client-provisioner
rules:
- apiGroups: [""]
resources: ["endpoints"]
verbs: ["get", "list", "watch", "create", "update", "patch"]
---
# 6. 普通角色绑定
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: leader-locking-nfs-client-provisioner
namespace: nfs-client-provisioner
subjects:
- kind: ServiceAccount
name: nfs-client-provisioner
namespace: nfs-client-provisioner
roleRef:
kind: Role
name: leader-locking-nfs-client-provisioner
apiGroup: rbac.authorization.k8s.io
建立 NFS 动态供给控制器(Deployment)
bash
[root@master volumes]# vim storageclassdep.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nfs-client-provisioner
namespace: nfs-client-provisioner
spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
app: nfs-client-provisioner
template:
metadata:
labels:
app: nfs-client-provisioner
spec:
serviceAccountName: nfs-client-provisioner
containers:
- name: nfs-client-provisioner
image: sig-storage/nfs-subdir-external-provisioner:v4.0.2
volumeMounts:
- name: nfs-client-root
mountPath: /persistentvolumes
env:
- name: PROVISIONER_NAME
value: k8s-sigs.io/nfs-subdir-external-provisioner
- name: NFS_SERVER
value: 172.25.254.30
- name: NFS_PATH
value: /share
volumes:
- name: nfs-client-root
nfs:
server: 172.25.254.30
path: /share
[root@master volumes]# kubectl apply -f storageclassdep.yml
[root@master volumes]# kubectl -n nfs-client-provisioner get pods
NAME READY STATUS RESTARTS AGE
nfs-client-provisioner-6b445b9454-xdt7v 1/1 Running 0 99s
=======================
解释
spec:
replicas: 1 # 运行 1 个实例
strategy:
type: Recreate # 重建更新策略
selector:
matchLabels:
app: nfs-client-provisioner # 匹配 Pod 标签
template:
metadata:
labels:
app: nfs-client-provisioner
spec:
serviceAccountName: nfs-client-provisioner # 使用配置好的权限账号
containers:
- name: nfs-client-provisioner
image: sig-storage/nfs-subdir-external-provisioner:v4.0.2 # NFS 自动供给镜像
volumeMounts:
- name: nfs-client-root
mountPath: /persistentvolumes # 容器内挂载 NFS 的路径
env:
- name: PROVISIONER_NAME # 供给者名称(必须与 StorageClass 一致)
value: k8s-sigs.io/nfs-subdir-external-provisioner
- name: NFS_SERVER # NFS 服务器 IP
value: 172.25.254.30
- name: NFS_PATH # NFS 共享目录
value: /share
volumes:
- name: nfs-client-root
nfs:
server: 172.25.254.30 # 绑定 NFS 服务器
path: /share # 绑定 NFS 共享路径
建立存储类
bash
[root@master volumes]# vim storageclass.yml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: nfs-client
#与前面的控制器的供给者相同
provisioner: k8s-sigs.io/nfs-subdir-external-provisioner
parameters:
archiveOnDelete: "false"
[root@master volumes]# kubectl apply -f storageclass.yml
storageclass.storage.k8s.io/nfs-client created
[root@master volumes]# kubectl get storageclasses.storage.k8s.io
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
nfs-client k8s-sigs.io/nfs-subdir-external-provisioner Delete Immediate false 20s
==========================
解释
apiVersion: storage.k8s.io/v1
kind: StorageClass # 资源类型:存储类
metadata:
name: nfs-client # 存储类名称(PVC 中引用)
provisioner: k8s-sigs.io/nfs-subdir-external-provisioner # 调用的供给器(必须与控制器一致)
parameters:
archiveOnDelete: "false" # 删除 PVC 时,不归档数据,直接删除
建立pvc
b
[root@master volumes]# vim pvc.yml
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: test-claim
spec:
storageClassName: nfs-client
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1G
[root@master volumes]# kubectl get pv
No resources found
[root@master volumes]# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
test-claim Bound pvc-db1f2443-b2d8-4c41-a3e5-241650680ed4 1G RWX nfs-client <unset> 4s
[root@master volumes]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE
pvc-db1f2443-b2d8-4c41-a3e5-241650680ed4 1G RWX Delete Bound default/test-claim nfs-client <unset>
[root@master volumes]# kubectl delete -f pvc.yml
persistentvolumeclaim "test-claim" deleted from default namespace
[root@master volumes]# kubectl get pvc
No resources found in default namespace.
[root@master volumes]# kubectl get pv
No resources found
=========================
解释
# 动态存储 PVC 测试
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: test-claim # PVC 名称
spec:
storageClassName: nfs-client # 绑定你创建的动态存储类(核心!)
accessModes:
- ReadWriteMany # 多节点读写(NFS 支持)
resources:
requests:
storage: 1Gi # 申请 1G 存储
设定默认存储类
bash
[root@master volumes]# kubectl edit sc nfs-client
# Please edit the object below. Lines beginning with a '#' will be ignored,
# and an empty file will abort the edit. If an error occurs while saving this file will be
# reopened with the relevant failures.
#
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"storage.k8s.io/v1","kind":"StorageClass","metadata":{"annotations":{},"name":"nfs-client"},"parameters":{"archiveOnDelete":"false"},"provisioner":"k8s-sigs.io/nfs-subdir-external-provisioner"}
storageclass.kubernetes.io/is-default-class: "true" #设定默认存储类参数
creationTimestamp: "2026-04-19T03:03:22Z"
name: nfs-client
resourceVersion: "252163"
uid: 5a04c176-4a8e-4ca6-8c22-21bac23033cb
parameters:
archiveOnDelete: "false"
provisioner: k8s-sigs.io/nfs-subdir-external-provisioner
reclaimPolicy: Delete
volumeBindingMode: Immediate
[root@master volumes]# kubectl get sc
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
nfs-client (default) k8s-sigs.io/nfs-subdir-external-provisioner Delete Immediate false 34m
[root@master volumes]# vim pvc.yml
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: test-claim
spec:
#storageClassName: nfs-client
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1G
[root@master volumes]# kubectl apply -f pvc.yml
persistentvolumeclaim/test-claim created
[root@master volumes]# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
test-claim Bound pvc-34b2238c-93f9-4e82-b643-c33c30d7e75e 1G RWX nfs-client <unset> 4s
=========================
解释
[root@master volumes]# kubectl edit sc nfs-client
意思:
打开 nfs-client 这个存储类的配置文件,让我修改。
metadata:
annotations:
# 设定为默认存储类
storageclass.kubernetes.io/is-default-class: "true"
作用
把 nfs-client 设置为 Kubernetes 的默认存储类
以后创建 PVC 时,不用再写 storageClassName,系统会自动使用它。
[root@master volumes]# kubectl get sc
意思:
查看集群里有哪些存储类。
kubectl apply -f pvc.yml
意思:
创建这个存储申请。
你的 PVC 里 注释掉了存储类:
# storageClassName: nfs-client
意思:我不指定用哪个存储,让系统自动给我分配默认的。
5. 查看 PVC 是否成功
kubectl get pvc
你看到:
plaintext
STATUS:Bound
STORAGECLASS:nfs-client
意思:自动绑定成功!
statfulset控制器整合动态卷
1.存储流程(全自动)
StatefulSet 创建 Pod → webserver-0
自动创建 PVC → www-webserver-0
自动调用 NFS 存储类
自动创建 PV
自动挂载到 Pod
2. 特点(必须背)
每个 Pod 拥有独立的存储空间
webserver-0 用自己的 1G
webserver-1 用自己的 1G
Pod 删除重建,存储还在,数据不丢
完全动态,不用手动建 PV/PVC
bash
#建立无头服务
[root@master ~]# kubectl create service clusterip timinglee --tcp 80:80 --clusterip="None" --dry-run=client -o yaml > headless.yml
[root@master ~]# vim headless.yml
apiVersion: v1
kind: Service
metadata:
labels:
app: timinglee
name: timinglee
spec:
clusterIP: None
ports:
- name: webport
port: 80
protocol: TCP
targetPort: 80
selector:
app: webserver
type: ClusterIP
#创建statefulset
[root@master ~]# kubectl create deployment webserver --image nginx --replicas 1 --dry-run=client -o yaml > statefulset.yml
[root@master ~]# vim statefulset.yml
apiVersion: apps/v1
kind: StatefulSet
metadata:
labels:
app: webserver
name: webserver
spec:
serviceName: "timinglee"
replicas: 1
selector:
matchLabels:
app: webserver
template:
metadata:
labels:
app: webserver
spec:
containers:
- image: nginx
name: nginx
volumeMounts:
- name: www
mountPath: /usr/share/nginx/html
volumeClaimTemplates:
- metadata:
name: www
spec:
storageClassName: nfs-client
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
[root@master controler]# kubectl apply -f statefulset.yml
statefulset.apps/webserver created
[root@master controler]# kubectl get statefulsets.apps
NAME READY AGE
webserver 1/1 5s
[root@master controler]# kubectl get pods
NAME READY STATUS RESTARTS AGE
webserver-0 1/1 Running 0 9s
[root@master controler]# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
test-claim Bound pvc-34b2238c-93f9-4e82-b643-c33c30d7e75e 1G RWX nfs-client <unset> 164m
www-webserver-0 Bound pvc-445dcc78-2a7b-49a8-900d-7f7435547153 1Gi RWO nfs-client <unset> 27s
[root@master controler]# kubectl scale statefulset webserver --replicas 2
statefulset.apps/webserver scaled
[root@master controler]# kubectl get pods
NAME READY STATUS RESTARTS AGE
webserver-0 1/1 Running 0 2m6s
webserver-1 1/1 Running 0 9s
[root@master controler]# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
www-webserver-0 Bound pvc-445dcc78-2a7b-49a8-900d-7f7435547153 1Gi RWO nfs-client <unset> 2m12s
www-webserver-1 Bound pvc-a0d74911-600d-4297-86e7-767710e0dcb5 1Gi RWO nfs-client <unset> 15s
[root@master controler]# kubectl scale statefulset webserver --replicas 3
statefulset.apps/webserver scaled
[root@master controler]# kubectl get pods
NAME READY STATUS RESTARTS AGE
webserver-0 1/1 Running 0 2m23s
webserver-1 1/1 Running 0 26s
webserver-2 1/1 Running 0 3s
[root@master controler]# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
www-webserver-0 Bound pvc-445dcc78-2a7b-49a8-900d-7f7435547153 1Gi RWO nfs-client <unset> 2m27s
www-webserver-1 Bound pvc-a0d74911-600d-4297-86e7-767710e0dcb5 1Gi RWO nfs-client <unset> 30s
www-webserver-2 Bound pvc-712009f1-2dda-4dc8-befc-0477cc31ec01 1Gi RWO nfs-client <unset> 7s
[root@node3 ~]# cd /share/
[root@node3 share]# ll
总用量 4
drwxrwxrwx 2 root root 6 4月 19 14:22 default-www-webserver-0-pvc-445dcc78-2a7b-49a8-900d-7f7435547153
-rw-r--r-- 1 root root 16 4月 18 16:30 index.html
drwxr-xr-x 2 root root 24 4月 19 10:09 pv1
drwxr-xr-x 2 root root 6 4月 19 09:46 pv2
drwxr-xr-x 2 root root 6 4月 19 09:46 pv3
[root@node3 share]# ll
总用量 4
drwxrwxrwx 2 root root 6 4月 19 14:22 default-www-webserver-0-pvc-445dcc78-2a7b-49a8-900d-7f7435547153
drwxrwxrwx 2 root root 6 4月 19 14:24 default-www-webserver-1-pvc-a0d74911-600d-4297-86e7-767710e0dcb5
drwxrwxrwx 2 root root 6 4月 19 14:25 default-www-webserver-2-pvc-712009f1-2dda-4dc8-befc-0477cc31ec01
[root@node3 share]# echo webserver1 > default-www-webserver-0-pvc-445dcc78-2a7b-49a8-900d-7f7435547153/index.html
[root@node3 share]# echo webserver2 > default-www-webserver-1-pvc-a0d74911-600d-4297-86e7-767710e0dcb5/index.html
[root@node3 share]# echo webserver3 > default-www-webserver-2-pvc-712009f1-2dda-4dc8-befc-0477cc31ec01/index.html
#测试
[root@master ~]# kubectl run -it testpod --image busyboxplus
[ root@testpod:/ ]$ curl webserver-0.timinglee
webserver1
[ root@testpod:/ ]$ curl webserver-1.timinglee
webserver2
[ root@testpod:/ ]$ curl webserver-2.timinglee
webserver3
[root@master ~]# kubectl delete -f statefulset.yml
[root@master ~]# kubectl apply -f statefulset.yml
[ root@testpod:/ ]$ curl webserver-0.timinglee
webserver1
[ root@testpod:/ ]$ curl webserver-1.timinglee
webserver2
[ root@testpod:/ ]$ curl webserver-2.timinglee
webserver3
====================================
解释
[root@master ~]# vim headless.yml
apiVersion: v1
kind: Service # 类型:服务
metadata:
labels:
app: timinglee
name: timinglee # 服务名称:timinglee(最重要)
spec:
clusterIP: None # ✅ 关键:设为 None = 无头服务
ports:
- name: webport
port: 80 # 服务端口 80
protocol: TCP
targetPort: 80 # 容器端口 80
selector:
app: webserver # 绑定标签为 app=webserver 的 Pod
type: ClusterIP # 默认为集群内部访问
[root@master ~]# vim statefulset.yml
apiVersion: apps/v1
kind: StatefulSet # 🔥 关键:这是有状态应用控制器(不是普通Deployment)
metadata:
labels:
app: webserver
name: webserver # 名字:webserver
spec:
serviceName: "timinglee" # 🔥 绑定无头服务(必须写!)
replicas: 1 # 运行1个Pod
selector:
matchLabels:
app: webserver
template:
metadata:
labels:
app: webserver
spec:
containers:
- image: nginx
name: nginx
volumeMounts:
- name: www
mountPath: /usr/share/nginx/html # 挂载到nginx网页目录
# 🔥 重点来了:动态存储模板
volumeClaimTemplates:
- metadata:
name: www
spec:
storageClassName: nfs-client # 使用的是前面配置的的默认NFS动态存储类
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi # 每个Pod自动申请1G存储
这个无头服务的域名规则是什么?
pod名称.service名称
[root@master ~]# kubectl run -it testpod --image busyboxplus
[ root@testpod:/ ]$ curl webserver-0.timinglee
webserver1
[ root@testpod:/ ]$ curl webserver-1.timinglee
webserver2
[ root@testpod:/ ]$ curl webserver-2.timinglee
webserver3
七.网络插件
fannel插件转换为calico插件
## 1.删除fannel插件
bash
[root@master ~]# kubectl delete -f kube-flannel.yml
namespace "kube-flannel" deleted
serviceaccount "flannel" deleted from kube-flannel namespace
clusterrole.rbac.authorization.k8s.io "flannel" deleted
clusterrolebinding.rbac.authorization.k8s.io "flannel" deleted
configmap "kube-flannel-cfg" deleted from kube-flannel namespace
daemonset.apps "kube-flannel-ds" deleted from kube-flannel namespace
[root@master ~]# for i in 100 10 20; do ssh -l root 172.25.254.$i rm -fr /etc/cni/net.d/10-flannel.conflist; done
[root@master ~]# for i in 100 10 20; do ssh -l root 172.25.254.$i ls /etc/cni/net.d; done
## 2.部署calico
### 下载yaml文件
b
[root@k8s-master calico]# curl https://raw.githubusercontent.com/projectcalico/calico/v3.31.4/manifests/calico-typha.yaml -o calico.yaml
### 建立calico的harbor项目

### 上传镜像
bash
[root@master ~]# docker tag quay.io/calico/cni:v3.31.4 reg.timinglee.org/calico/cni:v3.31.4
[root@master ~]# docker push reg.timinglee.org/calico/cni:v3.31.4
[root@master ~]# docker tag quay.io/calico/node:v3.31.4 reg.timinglee.org/calico/node:v3.31.4
[root@master ~]# docker push reg.timinglee.org/calico/node:v3.31.4
[root@master ~]# docker tag quay.io/calico/kube-controllers:v3.31.4 reg.timinglee.org/calico/kube-controllers:v3.31.4
[root@master ~]# docker push reg.timinglee.org/calico/kube-controllers:v3.31.4
[root@master ~]# docker tag quay.io/calico/typha:v3.31.4 reg.timinglee.org/calico/typha:v3.31.4
[root@master ~]# docker push reg.timinglee.org/calico/typha:v3.31.4
### 修改配置文件
bash
[root@master ~]# vim calico
7116: image: calico/cni:v3.31.4
7144: image: calico/cni:v3.31.4
7188: image: calico/node:v3.31.4
7214: image: calico/node:v3.31.4
7443: image: calico/kube-controllers:v3.31.4
7536: - image: calico/typha:v3.31.4
7252 - name: CALICO_IPV4POOL_IPIP
7253 value: "Never"
7281 - name: CALICO_IPV4POOL_CIDR
7282 value: "10.244.0.0/16"
7284 - name: CALICO_AUTODETECTION_METHOD
7285 value: "interface=eth0" #网络设备是什么就写什么
[root@master ~]# kubectl apply -f calico.yaml
[root@master ~]# kubectl -n kube-system get pods
NAME READY STATUS RESTARTS AGE
calico-kube-controllers-76cf8c6dbf-6wjwf 1/1 Running 0 41s
calico-node-d29h8 1/1 Running 0 41s
calico-node-hwr6k 1/1 Running 0 41s
calico-node-kb946 1/1 Running 0 41s
calico-typha-797496df67-gm49m 1/1 Running 0 41s
### 测试:
bash
[root@master ~]# kubectl run testpod --image nginx
pod/testpod created
[root@master ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
testpod 1/1 Running 0 2s
[root@master ~]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
testpod 1/1 Running 0 5s 10.244.166.128 node1 <none> <none>
[root@master ~]# curl 10.244.166.128
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
八.控制器
1.nodeName
直接指定调度到后端的哪一个node节点
bash
[root@master ~]# watch -n 1 kubectl get pods -o wide
#在其他的shell中建立pod
[root@master Scheduler]# kubectl run nginx --image nginx --dry-run=client -o yaml > nginx.yml
[root@master Scheduler]# vim nginx.yml
apiVersion: v1
kind: Pod
metadata:
labels:
run: nginx
name: nginx
spec:
# nodeName: #未指定调度阶段
containers:
- image: nginx
name: nginx
[root@master Scheduler]# kubectl apply -f nginx.yml
pod/nginx created
监控信息
Every 1.0s: kubectl get pods -o wide master: Sat Apr 25 09:59:03 2026
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx 1/1 Running 0 56s 10.244.166.131 node1 <none> <none>
[root@master Scheduler]# kubectl delete -f nginx.yml
pod "nginx" deleted from default namespace
[root@master Scheduler]# vim nginx.yml
apiVersion: v1
kind: Pod
metadata:
labels:
run: nginx
name: nginx
spec:
nodeName: node2 #自定调度的节点
containers:
- image: nginx
name: nginx
[root@master Scheduler]# kubectl apply -f nginx.yml
pod/nginx created
Every 1.0s: kubectl get pods -o wide master: Sat Apr 25 10:01:21 2026
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx 1/1 Running 0 7s 10.244.104.2 node2 <none> <none>
2.nodeselector
调度到指定标签的节点
bash
[root@master Scheduler]# vim nginx.yml
apiVersion: v1
kind: Pod
metadata:
labels:
run: nginx
name: nginx
spec:
nodeSelector:
app: timinglee ####添加
containers:
- image: nginx
name: nginx
Every 1.0s: kubectl get pods -o wide master: Sat Apr 25 10:24:01 2026
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx 0/1 Pending 0 8s <none> <none> <none> <none>
#查看节点标签
[root@master Scheduler]# kubectl get nodes --show-labels
NAME STATUS ROLES AGE VERSION LABELS
master Ready control-plane 27d v1.35.3 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=master,kubernetes.io/os=linux,node-role.kubernetes.io/control-plane=,node.kubernetes.io/exclude-from-external-load-balancers=
node1 Ready <none> 27d v1.35.3 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=node1,kubernetes.io/os=linux
node2 Ready <none> 27d v1.35.3 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=node2,kubernetes.io/os=linux
[root@master Scheduler]# kubectl label nodes
master node1 node2
#添加标签
[root@master Scheduler]# kubectl label nodes node2 app=timinglee
node/node2 labeled
[root@master Scheduler]# kubectl get nodes --show-labels
NAME STATUS ROLES AGE VERSION LABELS
master Ready control-plane 27d v1.35.3 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=master,kubernetes.io/os=linux,node-role.kubernetes.io/control-plane=,node.kubernetes.io/exclude-from-external-load-balancers=
node1 Ready <none> 27d v1.35.3 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=node1,kubernetes.io/os=linux
node2 Ready <none> 27d v1.35.3 app=timinglee,beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=node2,kubernetes.io/os=linux
Every 1.0s: kubectl get pods -o wide master: Sat Apr 25 10:25:29 2026
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx 1/1 Running 0 96s 10.244.104.3 node2 <none> <none>
#删除标签
[root@master Scheduler]# kubectl label nodes node2 app-
node/node2 unlabeled
Every 1.0s: kubectl get pods -o wide master: Sat Apr 25 10:26:10 2026
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx 1/1 Running 0 2m17s 10.244.104.3 node2 <none> <none>
3.节点亲和
pod满足哪个node节点的标签条件就调度到该节点,不满足就按照原来的调度方式调度
1.倾向满足
bash
[root@master Scheduler]# vim nginx.yml
apiVersion: v1
kind: Pod
metadata:
labels:
run: nginx
name: nginx
spec:
containers:
- image: nginx
name: nginx
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- preference:
matchExpressions:
- key: disk
operator: In
values:
- ssd
- iscsi
weight: 50
[root@master Scheduler]# kubectl apply -f nginx.yml
#在不符合条件情况下也可以调度pod
Every 1.0s: kubectl get pods -o wide master: Sat Apr 25 10:53:43 2026
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx 1/1 Running 0 2m3s 10.244.166.132 node1 <none> <none>
[root@master Scheduler]# kubectl delete -f nginx.yml
[root@master Scheduler]# kubectl label nodes node2 disk=ssd
node/node2 labeled
#满足条件后
Every 1.0s: kubectl get pods -o wide master: Sat Apr 25 10:55:11 2026
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx 1/1 Running 0 5s 10.244.104.4 node2 <none> <none>
b
[root@master Scheduler]# vim nginx.yml
apiVersion: v1
kind: Pod
metadata:
labels:
run: nginx
name: nginx
spec:
containers:
- image: nginx
name: nginx
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: disk
operator: In
values:
- ssd
- iscsi
[root@master Scheduler]# kubectl apply -f nginx.yml
pod/nginx created
#pod状态
Every 1.0s: kubectl get pods -o wide master: Sat Apr 25 10:38:36 2026
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx 0/1 Pending 0 26s <none> <none> <none> <none>
2.必须满足
bash
[root@master Scheduler]# vim nginx.yml
apiVersion: v1
kind: Pod
metadata:
labels:
run: nginx
name: nginx
spec:
containers:
- image: nginx
name: nginx
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: disk
operator: In
values:
- ssd
- iscsi
[root@master Scheduler]# kubectl apply -f nginx.yml
pod/nginx created
#条件必须满足
Every 1.0s: kubectl get pods -o wide master: Sat Apr 25 10:59:01 2026
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx 1/1 Running 0 50s 10.244.104.5 node2 <none> <none>
[root@master Scheduler]# vim nginx.yml
apiVersion: v1
kind: Pod
metadata:
labels:
run: nginx
name: nginx
spec:
containers:
- image: nginx
name: nginx
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: disk
operator: NotIn #反向选择
values:
- ssd
- iscsi
[root@master Scheduler]# kubectl apply -f nginx.yml
pod/nginx created
Every 1.0s: kubectl get pods -o wide master: Sat Apr 25 11:00:25 2026
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx 1/1 Running 0 8s 10.244.166.133 node1 <none> <none>
4.POD亲和
Pod 亲和(Pod Affinity)与反亲和(Pod Anti-Affinity)是 Kubernetes 高级调度规则,基于已有 Pod 的标签与位置,决定新 Pod 该 "跟谁放一起" 或 "跟谁分开",用于优化性能、保障高可用。
bash
[root@master Scheduler]# kubectl create deployment webcluster --image nginx --replicas 2 --dry-run=client -o yaml > webcluster.yml
[root@master Scheduler]# vim webcluster.yml
kind: Deployment
metadata:
labels:
app: webcluster
name: webcluster
spec:
replicas: 2
selector:
matchLabels:
app: webcluster
template:
metadata:
labels:
app: webcluster
spec:
containers:
- image: nginx
name: nginx
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- webcluster
topologyKey: "kubernetes.io/hostname"
[root@master Scheduler]# kubectl apply -f webcluster.yml
deployment.apps/webcluster created
Every 1.0s: kubectl get pods -o wide master: Sat Apr 25 11:31:13 2026
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS
GATES
webcluster-65b86bcdbb-jhb2b 1/1 Running 0 35s 10.244.166.136 node1 <none> <none>
webcluster-65b86bcdbb-z4mt8 1/1 Running 0 35s 10.244.166.135 node1 <none> <none>
5.POD反亲和
bash
[root@master Scheduler]# kubectl create deployment webcluster --image nginx --replicas 2 --dry-run=client -o yaml > webcluster.yml
[root@master Scheduler]# vim webcluster.yml
kind: Deployment
metadata:
labels:
app: webcluster
name: webcluster
spec:
replicas: 3
selector:
matchLabels:
app: webcluster
template:
metadata:
labels:
app: webcluster
spec:
containers:
- image: nginx
name: nginx
affinity:
podAntiAffinity: #改这里
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- webcluster
topologyKey: "kubernetes.io/hostname"
[root@master Scheduler]# kubectl apply -f webcluster.yml
deployment.apps/webcluster configured
Every 1.0s: kubectl get pods -o wide master: Sat Apr 25 11:35:30 2026
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINES
S GATES
webcluster-85f7868466-srhgf 1/1 Running 0 74s 10.244.104.7 node2 <none> <none>
webcluster-85f7868466-t8gk6 1/1 Running 0 74s 10.244.166.137 node1 <none> <none>
webcluster-85f7868466-x24wj 0/1 Pending (#反亲和条件生效,没有可用node节点) 0 22s <none> <none> <none> <none>
6.节点的污点设定
1.开启监控并启动该一个deployment控制器
b
[root@master ~]# watch -n 1 kubectl get pods -o wide
[root@master Scheduler]# kubectl create deployment webcluster --image nginx --replicas 2 --dry-run=client -o yaml > dep.yml
[root@master Scheduler]# vim dep.yml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: webcluster
name: webcluster
spec:
replicas: 2
selector:
matchLabels:
app: webcluster
template:
metadata:
labels:
app: webcluster
spec:
containers:
- image: nginx
name: nginx
[root@master Scheduler]# kubectl apply -f dep.yml
Every 1.0s: kubectl get pods -o wide master: Sat Apr 25 14:21:00 2026
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS
GATES
webcluster-77f8775f47-4bgj6 1/1 Running 0 7m57s 10.244.104.8 node1 <none> <none>
webcluster-77f8775f47-zrw2r 1/1 Running 0 4m11s 10.244.104.9 node2 <none> <none>
2.设定污点并购观察
NoExecute
介绍
最强硬
新 Pod 禁止调度进来
已经在节点上跑的旧 Pod 直接驱逐赶走
一般用于:节点维护、节点故障下线
口诀:新来不让进,老的直接踢走
b
[root@master Scheduler]# kubectl taint node node1 nodetype=badnode:NoExecute
Every 1.0s: kubectl get pods -o wide master: Sat Apr 25 14:21:55 2026
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS
GATES
webcluster-77f8775f47-4bgj6 1/1 Running 0 8m52s 10.244.104.8 node2 <none> <none>
webcluster-77f8775f47-zrw2r 1/1 Running 0 5m6s 10.244.104.9 node2 <none> <none>
#删除污点,在key值后面加一个减号
[root@master Scheduler]# kubectl taint node node1 nodetype-
NoSchedule
介绍
新的 Pod 绝对不能调度到这个节点
已经在这个节点上正在运行的 Pod 不受影响,不会赶走
属于硬性规则
口诀:新来不让进,老的不动
bash
[root@master Scheduler]# kubectl taint node node2 nodetype=badnode:NoSchedule
node/node1 tainted
[root@master Scheduler]# kubectl delete -f dep.yml
deployment.apps "webcluster" deleted from default namespace
[root@master Scheduler]# kubectl apply -f dep.yml
deployment.apps/webcluster created
Every 1.0s: kubectl get pods -o wide master: Sat Apr 25 14:25:02 2026
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS
GATES
webcluster-77f8775f47-6mkkr 1/1 Running 0 16s 10.244.166.140 node1 <none> <none>
webcluster-77f8775f47-c84z9 1/1 Running 0 16s 10.244.166.139 node1 <none> <none>
PreferNoSchedule
介绍
尽量不要把 Pod 调度到这个节点
但是!集群没别的节点可用时,还是会调度过来
属于软性建议
口诀:能不来就不来,实在没地方也可以来
bash
[root@master Scheduler]# kubectl delete -f dep.yml
deployment.apps "webcluster" deleted from default namespace
[root@master Scheduler]# kubectl taint node node2 nodetype=badnode:PreferNoSchedule
node/node2 tainted
[root@master Scheduler]# kubectl apply -f dep.yml
deployment.apps/webcluster created
Every 1.0s: kubectl get pods -o wide master: Sat Apr 25 14:26:30 2026
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS
GATES
webcluster-77f8775f47-fqr9d 1/1 Running 0 8s 10.244.166.141 node1 <none> <none>
webcluster-77f8775f47-wgkdr 1/1 Running 0 8s 10.244.166.142 node1 <none> <none>
[root@master Scheduler]# vim webcluster.yml #pod反亲和
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: webcluster
name: webcluster
spec:
replicas: 3
selector:
matchLabels:
app: webcluster
template:
metadata:
labels:
app: webcluster
spec:
containers:
- image: nginx
name: nginx
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- webcluster
topologyKey: "kubernetes.io/hostname"
[root@master Scheduler]# kubectl apply -f webcluster.yml
Every 1.0s: kubectl get pods -o wide master: Sat Apr 25 14:28:25 2026
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINES
S GATES
webcluster-85f7868466-5kw8x 0/1 Pending 0 31s <none> <none> <none> <none>
webcluster-85f7868466-cbbld 1/1 Running 0 31s 10.244.104.12 node2 <none> <none>
webcluster-85f7868466-qq4sc 1/1 Running 0 31s 10.244.166.143 node1 <none> <none>
7.污点容忍
1.设置节点不同类型给的污点
b
#创建污点
[root@master Scheduler]# kubectl taint node node1 name=lee:NoSchedule
node/node1 tainted
[root@master Scheduler]# kubectl taint node node2 nodetype=badnode:NoSchedule
node/node2 tainted
#删除污点
[root@master ~]# kubectl taint node node1 name-
node/node1 untainted
[root@master ~]# kubectl taint node node2 nodetype-
node/node2 untainted
[root@master ~]#
2.运行deployment控制器
bash
[root@master Scheduler]# kubectl apply -f dep.yml
Every 1.0s: kubectl get pods -o wide master: Sat Apr 25 14:53:51 2026
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
webcluster-77f8775f47-fln2k 0/1 Pending 0 33s <none> <none> <none> <none>
webcluster-77f8775f47-k9bv8 0/1 Pending 0 33s <none> <none> <none> <none>
[root@master Scheduler]# kubectl delete -f dep.yml
deployment.apps "webcluster" deleted from default namespace
3.精确容忍指定污点
bash
[root@master Scheduler]# vim dep.yml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: webcluster
name: webcluster
spec:
replicas: 2
selector:
matchLabels:
app: webcluster
template:
metadata:
labels:
app: webcluster
spec:
containers:
- image: nginx
name: nginx
tolerations: #污点容忍,指定键值和污点类型,用到Equal
- operator: Equal
key: nodetype
value: badnode
effect: NoSchedule
[root@master Scheduler]# kubectl apply -f dep.yml
deployment.apps/webcluster created
Every 1.0s: kubectl get pods -o wide master: Sat Apr 25 14:57:58 2026
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS
GATES
webcluster-597586678c-9qvp8 1/1 Running 0 12s 10.244.104.14 node2 <none> <none>
webcluster-597586678c-d2wvv 1/1 Running 0 12s 10.244.104.13 node2 <none> <none>
4.容忍所有标签的NoSchedule污点模式
bash
[root@master Scheduler]# vim dep.yml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: webcluster
name: webcluster
spec:
replicas: 3
selector:
matchLabels:
app: webcluster
template:
metadata:
labels:
app: webcluster
spec:
containers:
- image: nginx
name: nginx
tolerations: #不管键值,只看污点类型,用Exists
- operator: Exists
effect: NoSchedule
Every 1.0s: kubectl get pods -o wide master: Sat Apr 25 15:00:46 2026
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINES
S GATES
webcluster-549c6784f6-58zh9 1/1 Running 0 8s 10.244.104.15 node2 <none> <none>
webcluster-549c6784f6-d88tz 1/1 Running 0 8s 10.244.219.67 master <none> <none>
webcluster-549c6784f6-zv2pm 1/1 Running 0 8s 10.244.166.144 node1 <none> <none>
5.容忍所有污点
bash
[root@master Scheduler]# vim dep.yml
kind: Deployment
metadata:
labels:
app: webcluster
name: webcluster
spec:
replicas: 3
selector:
matchLabels:
app: webcluster
template:
metadata:
labels:
app: webcluster
spec:
containers:
- image: nginx
name: nginx
tolerations: #容忍所有
- operator: Exists
[root@master Scheduler]# kubectl apply -f dep.yml
deployment.apps/webcluster created
Every 1.0s: kubectl get pods -o wide master: Sat Apr 25 15:02:14 2026
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINES
S GATES
webcluster-5df57d554b-6gdcc 1/1 Running 0 12s 10.244.219.68 master <none> <none>
webcluster-5df57d554b-cj8fd 1/1 Running 0 12s 10.244.166.145 node1 <none> <none>
webcluster-5df57d554b-mwcfx 1/1 Running 0 12s 10.244.104.16 node2 <none> <none>
九.认证与授权
ServiceAccount
给谁用?
给 Pod 里面的程序用
不是给人用!
作用
让容器里的程序访问 Kubernetes API
拉取私有镜像
程序在集群内部的身份
特点
属于 某个命名空间
集群用户
给谁用?
给 人 用!
就是 你、我、运维、开发 用 kubectl 操作集群
作用
人登录集群
操作资源
受权限控制
特点
用 证书(crt/key) 认证
身份是证书里的 CN=xxx
不属于命名空间(全局)
1.ServiceAccount
1.现在私有仓库时遇到的授权问题
bash
[root@master auth]# kubectl run testpod --image nginx --dry-run=client -o yaml > testpod.yaml
[root@master auth]# vim testpod.yaml
apiVersion: v1
kind: Pod
metadata:
labels:
run: testpod
name: testpod
spec:
containers:
- image: nginx
name: testpod
[root@master auth]# kubectl apply -f testpod.yaml
pod/testpod created
#实时查看
Every 1.0s: kubectl get pods -o wide master: Sat Apr 25 16:06:37 2026
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
testpod 1/1 Running 0 23s 10.244.166.138 node1 <none> <none>
[root@master auth]# kubectl describe pods testpod | grep Service
Service Account: default
[root@master auth]# kubectl delete -f testpod.yaml
pod "testpod" deleted from default namespace
#指定使用过私有仓库镜像
[root@master auth]# vim testpod.yaml
apiVersion: v1
kind: Pod
metadata:
labels:
run: testpod
name: testpod
spec:
containers:
- image: reg.timinglee.org/timinglee/myapp:v1 #私有仓库镜像
name: testpod
imagePullPolicy: Always
#状态
Every 1.0s: kubectl get pods -o wide master: Sat Apr 25 15:46:01 2026
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
testpod 0/1 ErrImagePull 0 55s 10.244.166.151 node1 <none> <none>
2.解决授权问题
bash
#创建了一个服务账号
[root@master auth]# kubectl create serviceaccount timinglee
serviceaccount/timinglee created
#创建了一个docker仓库的登陆密钥
[root@master auth]# kubectl create secret docker-registry docker-login --docker-username admin --docker-password lee --docker-server reg.timinglee.org --docker-email lee@timinglee.org
[root@master auth]# kubectl describe sa timinglee
Name: timinglee
Namespace: default
Labels: <none>
Annotations: <none>
Image pull secrets: <none> #默认不调用该secret资源
Events: <none>
[root@master auth]# kubectl edit sa timinglee
apiVersion: v1
imagePullSecrets:
- name: docker-login #指定授权读取的内容
kind: ServiceAccount
metadata:
creationTimestamp: "2026-04-25T07:49:09Z"
name: timinglee
namespace: default
resourceVersion: "337771"
uid: f6711f5a-85a2-45c9-8f20-3f45387ebd54
[root@master auth]# kubectl describe sa timinglee
Name: timinglee
Namespace: default
Labels: <none>
Annotations: <none>
Image pull secrets: docker-login
Events: <none>
[root@master auth]# vim testpod.yaml
apiVersion: v1
kind: Pod
metadata:
labels:
run: testpod
name: testpod
spec:
serviceAccountName: timinglee ##添加账号
containers:
- image: reg.timinglee.org/timinglee/myapp:v1
name: testpod
imagePullPolicy: Always
[root@master auth]# kubectl apply -f testpod.yaml
#实时查看
Every 1.0s: kubectl get pods -o wide master: Sat Apr 25 16:06:37 2026
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
testpod 1/1 Running 0 23s 10.244.166.138 node1 <none> <none>
2.建立集群用户
1.建立用户证书
b
[root@k8s-master auth]# cd /etc/kubernetes/pki/
#生成一个私钥
[root@k8s-master pki]# openssl genrsa -out timinglee.key 2048
#用刚才的timinglee.key生成一个timinglee.csr证书请求文件,我的名字叫timinglee
[root@k8s-master pki]# openssl req -new -key timinglee.key -out timinglee.csr -subj "/CN=timinglee"
#让 K8s 根证书(CA)给你签发证书
[root@k8s-master pki]# openssl x509 -req -in timinglee.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out timinglee.crt -days 365
Certificate request self-signature ok
#查看证书内容
[root@k8s-master pki]# openssl x509 -in timinglee.crt -text -noout
Certificate:
Data:
Version: 1 (0x0)
Serial Number:
76:06:6c:a7:36:53:b9:3f:5a:6a:93:3a:f2:e8:82:96:27:57:8e:58
Signature Algorithm: sha256WithRSAEncryption
Issuer: CN = kubernetes
Validity
Not Before: Sep 8 15:59:55 2024 GMT
Not After : Sep 8 15:59:55 2025 GMT
Subject: CN = timinglee
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (2048 bit)
Modulus:
00:a6:6d:be:5d:7f:4c:bf:36:96:dc:4e:1b:24:64:
f7:4b:57:d3:45:ad:e8:b5:07:e7:78:2b:9e:6e:53:
2f:16:ff:00:f4:c8:41:2c:89:3d:86:7c:1b:16:08:
2e:2c:bc:2c:1e:df:60:f0:80:60:f9:79:49:91:1d:
9f:47:16:9a:d1:86:c7:4f:02:55:27:12:93:b7:f4:
07:fe:13:64:fd:78:32:8d:12:d5:c2:0f:be:67:65:
f2:56:e4:d1:f6:fe:f6:d5:7c:2d:1d:c8:90:2a:ac:
3f:62:85:9f:4a:9d:85:73:33:26:5d:0f:4a:a9:14:
12:d4:fb:b3:b9:73:d0:a3:be:58:41:cb:a0:62:3e:
1b:44:ef:61:b5:7f:4a:92:5b:e3:71:77:99:b4:ea:
4d:27:80:14:e9:95:4c:d5:62:56:d6:54:7b:f7:c2:
ea:0e:47:b2:19:75:59:22:00:bd:ea:83:6b:cd:12:
46:7a:4a:79:83:ee:bc:59:6f:af:8e:1a:fd:aa:b4:
bd:84:4d:76:38:e3:1d:ea:56:b5:1e:07:f5:39:ef:
56:57:a2:3d:91:c0:3f:38:ce:36:5d:c7:fe:5e:0f:
53:75:5a:f0:6e:37:71:4b:90:03:2f:2e:11:bb:a1:
a1:5b:dc:89:b8:19:79:0a:ee:e9:b5:30:7d:16:44:
4a:53
Exponent: 65537 (0x10001)
Signature Algorithm: sha256WithRSAEncryption
Signature Value:
62:db:0b:58:a9:59:57:91:7e:de:9e:bb:20:2f:24:fe:b7:7f:
33:aa:d5:74:0e:f9:96:ce:1b:a9:65:08:7f:22:6b:45:ee:58:
68:d8:26:44:33:5e:45:e1:82:b2:5c:99:41:6b:1e:fa:e8:1a:
a2:f1:8f:44:22:e1:d6:58:5f:4c:28:3d:e0:78:21:ea:aa:85:
08:a5:c8:b3:34:19:d3:c7:e2:fe:a2:a4:f5:68:18:53:5f:ff:
7d:35:22:3c:97:3d:4e:ad:62:5f:bb:4d:88:fb:67:f4:d5:2d:
81:c8:2c:6c:5e:0e:e2:2c:f5:e9:07:34:16:01:e2:bf:1f:cd:
6a:66:db:b6:7b:92:df:13:a1:d0:58:d8:4d:68:96:66:e3:00:
6e:ce:11:99:36:9c:b3:b5:81:bf:d1:5b:d7:f2:08:5e:7d:ea:
97:fe:b3:80:d6:27:1c:89:e6:f2:f3:03:fc:dc:de:83:5e:24:
af:46:a6:2a:8e:b1:34:67:51:2b:19:eb:4c:78:12:ac:00:4e:
58:5e:fd:6b:4c:ce:73:dd:b3:91:73:4a:d6:6f:2c:86:25:f0:
6a:fb:96:66:b3:39:a4:b0:d9:46:c2:fc:6b:06:b2:90:9c:13:
e1:02:8b:6f:6e:ab:cf:e3:21:7e:a9:76:c1:38:15:eb:e6:2d:
a5:6f:e5:ab
2.建立用户
bash
[root@master pki]# kubectl config view
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: DATA+OMITTED
server: https://172.25.254.100:6443
name: kubernetes
contexts:
- context:
cluster: kubernetes
user: kubernetes-admin #当前是集群管理员身份
name: kubernetes-admin@kubernetes
current-context: kubernetes-admin@kubernetes
kind: Config
users:
- name: kubernetes-admin
user:
client-certificate-data: DATA+OMITTED
client-key-data: DATA+OMITTED
#给 kubectl 添加一个新用户 → timinglee(与前面的/CN=timinglee名字一致)
#把你刚才生成的证书(crt) 和私钥(key) 写入 kubectl 配置
#--embed-certs=true:把证书内容直接塞进配置里,不用再依赖文件
[root@master auth]# kubectl config set-credentials timinglee --client-certificate /etc/kubernetes/pki/timinglee.crt --client-key /etc/kubernetes/pki/timinglee.key --embed-certs=true
User "timinglee" set.
#创建一个 "登录方式",这个 "登录方式" 名字叫:timinglee@kubernetes,用 timinglee 用户 连接 kubernetes 集群
[root@k8s-master pki]# kubectl config set-context timinglee@kubernetes --cluster kubernetes --user timinglee
[root@master auth]# kubectl config use-context timinglee@kubernetes
[root@master pki]# kubectl config view
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: DATA+OMITTED
server: https://172.25.254.100:6443
name: kubernetes
contexts:
- context:
cluster: kubernetes
user: kubernetes-admin
name: kubernetes-admin@kubernetes
- context:
cluster: kubernetes
user: timinglee
name: timinglee@kubernetes
current-context: timinglee@kubernetes
kind: Config
users:
- name: kubernetes-admin
user:
client-certificate-data: DATA+OMITTED
client-key-data: DATA+OMITTED
- name: timinglee
user:
client-certificate-data: DATA+OMITTED
client-key-data: DATA+OMITTED
3.授权
- Role(角色)
只在 1 个命名空间里生效
只能管理当前命名空间(比如 default)
不能跨命名空间
必须配合 RoleBinding 使用
作用
给用户 / SA 授权 某个命名空间内 的权限。 - ClusterRole(集群角色)
整个集群都生效
跨所有命名空间
能管理集群级资源(nodes、namespaces 等)
可以配合 ClusterRoleBinding(全局) 或 RoleBinding(指定命名空间)
作用
给用户 / SA 授权 整个集群 的权限。
3.总结
Role 局限在一个命名空间,ClusterRole 作用于整个集群。
## 1.role和rolebinding
### 切换回管理员
bash
[root@master auth]# kubectl config use-context kubernetes-admin@kubernetes
Switched to context "kubernetes-admin@kubernetes".
### 建立role授权
bash
[root@master auth]# kubectl create role timingleerole --dry-run=client --verb=get --resource pods -o yaml > timingleerole.yaml
[root@master auth]# vim timingleerole.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: timingleerole
rules:
- apiGroups:
- ""
resources:
- pods
verbs:
- get
- watch
- list
- create
- update
- path
- delete
- apiGroups:
- "apps"
resources:
- deployments
verbs:
- get
- list
- watch
- create
[root@master auth]# kubectl apply -f timingleerole.yaml
role.rbac.authorization.k8s.io/timingleerole created
[root@master auth]# kubectl get roles.rbac.authorization.k8s.io
NAME CREATED AT
timingleerole 2026-04-25T08:38:39Z
[root@master auth]# kubectl get roles.rbac.authorization.k8s.io
NAME CREATED AT
timingleerole 2026-04-25T08:38:39Z
[root@master auth]# kubectl describe roles.rbac.authorization.k8s.io timingleerole
Name: timingleerole
Labels: <none>
Annotations: <none>
PolicyRule:
Resources Non-Resource URLs Resource Names Verbs
--------- ----------------- -------------- -----
deployments.apps [] [] [get watch list create]
pods [] [] [get watch list create update path delete]
### 调用role授权(rolebinding)
kubectl create rolebinding
创建角色绑定(RBAC 授权)
timingleerole-binding
绑定的名字,随便起,方便识别
--role timingleerole
绑定的角色(你之前创建的角色)
--namespace default
权限只在 default 命名空间 生效
--user timinglee
授权给哪个用户
👉 这里必须 = 证书里的 CN=timinglee
bash
[root@master auth]# kubectl create rolebinding timingleerole-binding --role timingleerole --namespace default --user timinglee --dry-run=client -o yaml > timingleerole-binding.yml
[root@master auth]# vim timingleerole-binding.yml
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: timingleerole-binding
namespace: default
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: timingleerole
subjects:
- apiGroup: rbac.authorization.k8s.io
kind: User
name: timinglee
[root@master auth]# kubectl apply -f timingleerole-binding.yml
rolebinding.rbac.authorization.k8s.io/timingleerole-binding created
[root@master auth]# kubectl describe rolebindings.rbac.authorization.k8s.io timingleerole-binding
Name: timingleerole-binding
Labels: <none>
Annotations: <none>
Role:
Kind: Role
Name: timingleerole
Subjects:
Kind Name Namespace
---- ---- ---------
User timinglee
#测试
[root@master auth]# kubectl config use-context timinglee@kubernetes
Switched to context "timinglee@kubernetes".
[root@master auth]# kubectl get svc
Error from server (Forbidden): services is forbidden: User "timinglee" cannot list resource "services" in API group "" in the namespace "default"
[root@master auth]# kubectl get svc
Error from server (Forbidden): services is forbidden: User "timinglee" cannot list resource "services" in API group "" in the namespace "default"
[root@master auth]# kubectl get deployments.apps
No resources found in default namespace.
[root@master auth]# kubectl get pods
NAME READY STATUS RESTARTS AGE
testpod 1/1 Running 0 54m
[root@master auth]# kubectl create deployment test --image nginx --replicas 2
deployment.apps/test created
[root@master auth]# kubectl get deployments.apps
NAME READY UP-TO-DATE AVAILABLE AGE
[root@master auth]# kubectl delete deployments.apps test
Error from server (Forbidden): deployments.apps "test" is forbidden: User "timinglee" cannot delete resource "deployments" in API group "apps" in the namespace "default"
3.clusterrole和clusterrolebind
1.建立clusterrole
b
[root@master auth]# kubectl create clusterrole timingleeclusterrole --resource=deployment --verb get --dry-run=client -o yaml > timingleeclusterrole.yml
[root@master auth]# vim timingleeclusterrole.yml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: timingleeclusterrole
rules:
- apiGroups:
- ""
resources:
- pods
verbs:
- get
- watch
- list
- create
- update
- path
- delete
- apiGroups:
- "apps"
resources:
- deployments
verbs:
- get
- watch
- list
- create
- apiGroups:
- ""
resources:
- services
verbs:
- get
- watch
- list
- create
[root@master auth]# kubectl apply -f timingleeclusterrole.yml
clusterrole.rbac.authorization.k8s.io/timingleeclusterrole created
[root@master auth]# kubectl describe clusterrole timingleeclusterrole
Name: timingleeclusterrole
Labels: <none>
Annotations: <none>
PolicyRule:
Resources Non-Resource URLs Resource Names Verbs
--------- ----------------- -------------- -----
services [] [] [get watch list create delete]
pods [] [] [get watch list create update path delete]
deployments.apps [] [] [get watch list create]
2.调用该集群授权
bash
[root@master auth]# kubectl create clusterrolebinding clusterrolebind-timinleeclusterrole --clusterrole timingleeclusterrole --user timinglee
[root@master auth]# kubectl describe clusterrolebindings.rbac.authorization.k8s.io clusterrolebind-timinleeclusterrole
Name: clusterrolebind-timinleeclusterrole
Labels: <none>
Annotations: <none>
Role:
Kind: ClusterRole
Name: timingleeclusterrole
Subjects:
Kind Name Namespace
---- ---- ---------
User timinglee
#测试
[root@master auth]# kubectl config use-context timinglee@kubernetes
[root@master auth]# kubectl get pv
Error from server (Forbidden): persistentvolumes is forbidden: User "timinglee" cannot list resource "persistentvolumes" in API group "" at the cluster scope
[root@master auth]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 28d
timinglee ClusterIP None <none> 80/TCP 6d19h
[root@master auth]# kubectl get deployments.apps
No resources found in default namespace.
[root@master auth]# kubectl get pods
No resources found in default namespace.
[root@master auth]# kubectl get pods -A
NAMESPACE NAME READY STATUS RESTARTS AGE
ingress-nginx ingress-nginx-controller-6bcbfdbd4b-rf9zm 1/1 Running 5 (37m ago) 13d
kube-system calico-kube-controllers-76cf8c6dbf-6wjwf 1/1 Running 2 (37m ago) 6d17h
kube-system calico-node-d29h8 1/1 Running 2 (37m ago) 6d17h
kube-system calico-node-hwr6k 1/1 Running 2 (37m ago) 6d17h
kube-system calico-node-kb946 1/1 Running 2 (37m ago) 6d17h
kube-system calico-typha-797496df67-gm49m 1/1 Running 2 (37m ago) 6d17h
kube-system coredns-697886855d-d6kwd 1/1 Running 7 (37m ago) 28d
kube-system coredns-697886855d-k92r9 1/1 Running 7 (37m ago) 28d
kube-system etcd-master 1/1 Running 7 (37m ago) 28d
kube-system kube-apiserver-master 1/1 Running 5 (37m ago) 13d
kube-system kube-controller-manager-master 1/1 Running 8 (37m ago) 28d
kube-system kube-proxy-dzdmt 1/1 Running 5 (37m ago) 13d
kube-system kube-proxy-tmq9m 1/1 Running 5 (37m ago) 13d
kube-system kube-proxy-zjbgd 1/1 Running 5 (37m ago) 13d
kube-system kube-scheduler-master 1/1 Running 8 (37m ago) 28d
metallb-system controller-5fbf6546f9-9568r 1/1 Running 5 (37m ago) 13d
metallb-system speaker-976v8 1/1 Running 5 (37m ago) 13d
metallb-system speaker-tzp7n 1/1 Running 1 (37m ago) 19h
metallb-system speaker-x68v4 1/1 Running 5 (37m ago) 13d
nfs-client-provisioner nfs-client-provisioner-6b445b9454-c2tls 1/1 Running 0 19h
十.部署helm
一、Helm 是什么?
Helm = Kubernetes 的包管理器,Helm 帮你一键安装、管理、升级 K8s 应用,不用手写一堆 YAML!
二、Helm 核心概念(你必须懂)
- Chart(最重要)
Chart = 应用安装包
里面包含:
所有 K8s yaml 模板
配置文件 values.yaml
版本信息
就像 .zip、.tar.gz、.exe 安装包。 - values.yaml
配置文件
你想改端口、改副本数、改域名、改资源,只改这个文件。 - Release
Helm 安装后的运行实例
一个 Chart 可以安装 N 次,每次叫一个 Release
可以升级、回滚、卸载 - Repo
应用商店
Helm 官方、Bitnami 都有公共 Repo,里面全是现成软件。
bash
#安装helm
[root@master ~]# wget https://get.helm.sh/helm-v4.1.3-linux-amd64.tar.gz
[root@master ~]# tar zxf helm-v4.1.1-linux-amd64.tar.gz
[root@master ~]# ls
linux-amd64
#配置helm命令补齐
[root@master linux-amd64]# cp -p helm /usr/local/bin/
[root@master linux-amd64]# echo "source <(helm completion bash)" >> ~/.bashrc
[root@master linux-amd64]# source ~/.bashrc
[root@master linux-amd64]# helm
completion (generate autocompletion scripts for the specified shell)
create (create a new chart with the given name)
dependency (manage a chart's dependencies)
env (helm client environment information)
get (download extended information of a named release)
help (Help about any command)
history (fetch release history)
install (install a chart)
lint (examine a chart for possible issues)
list (list releases)
package (package a chart directory into a chart archive)
plugin (install, list, or uninstall Helm plugins)
pull (download a chart from a repository and (optionally) unpack it in local directory)
push (push a chart to remote)
registry (login to or logout from a registry)
repo (add, list, remove, update, and index chart repositories)
rollback (roll back a release to a previous revision)
search (search for a keyword in charts)
show (show information of a chart)
status (display the status of the named release)
template (locally render templates)
test (run tests for a release)
uninstall (uninstall a release)
upgrade (upgrade a release)
verify (verify that a chart at the given path has been signed and is valid)
version (print the helm version information)
#安装过项目上传插件
#在线安装
[root@master helm]# dnf install git -y
[root@master ~]# helm plugin install https://github.com/chartmuseum/helm-push --verify=false
[root@master ~]# helm plugin list
NAME VERSION TYPE APIVERSION PROVENANCE SOURCE
cm-push 0.11.1 cli/v1 v1
#离线安装过
[root@master ~]# tar zxf helm-push_0.11.1.tar.gz
[root@master ~]# helm
cm-push (Push chart package to ChartMuseum)
1.helm操作
bash
#在官方仓库中搜索
[root@master ~]# helm search hub nginx
URL CHART VERSION APP VERSION DESCRIPTION
https://artifacthub.io/packages/helm/cloudpirat... 0.11.1 1.30.0 Nginx is a high-performance HTTP server and rev...
https://artifacthub.io/packages/helm/wiremind/n... 2.1.1 An NGINX HTTP server
https://artifacthub.io/packages/helm/shubhamtat... 0.1.12 1.19.6 Nginx Helm chart for Kubernetes
#添加阿里云仓库
[root@master ~]# helm repo add aliyun https://kubernetes.oss-cn-hangzhou.aliyuncs.com/charts
"aliyun" has been added to your repositories
[root@master ~]# helm repo list
NAME URL
aliyun https://kubernetes.oss-cn-hangzhou.aliyuncs.com/charts
#在本地仓库aliyun中搜索
[root@master ~]# helm search repo aliyun/nginx
NAME CHART VERSION APP VERSION DESCRIPTION
aliyun/nginx-ingress 0.9.5 0.10.2 An nginx Ingress controller that uses ConfigMap...
aliyun/nginx-lego
[root@master ~]# helm show chart aliyun/nginx-ingress
apiVersion: v1
appVersion: 0.10.2
description: An nginx Ingress controller that uses ConfigMap to store the nginx configuration.
icon: https://upload.wikimedia.org/wikipedia/commons/thumb/c/c5/Nginx_logo.svg/500px-Nginx_logo.svg.png
keywords:
- ingress
- nginx
maintainers:
- email: jack.zampolin@gmail.com
name: jackzampolin
- email: mgoodness@gmail.com
name: mgoodness
- email: chance.zibolski@coreos.com
name: chancez
name: nginx-ingress
sources:
- https://github.com/kubernetes/ingress-nginx
version: 0.9.5
#添加bitnami仓库
[root@k8s-master helm]# helm repo add bitnami https://charts.bitnami.com/bitnami
"bitnami" has been added to your repositories
#查看仓库信息
[root@k8s-master helm]# helm repo list
NAME URL
aliyun https://kubernetes.oss-cn-hangzhou.aliyuncs.com/charts
bitnami https://charts.bitnami.com/bitnami
#查看仓库存储helm清单
[root@k8s-master helm]# helm search repo aliyun
NAME CHART VERSION APP VERSION DESCRIPTION #应用名称 封装版本 软件版本 软件描述
aliyun/acs-engine-autoscaler 2.1.3 2.1.1 Scales worker nodes within agent pools
aliyun/aerospike 0.1.7 v3.14.1.2 A Helm chart for Aerospike in Kubernetes
#删除第三方存储库
[root@k8s-master helm]# helm repo list
NAME URL
aliyun https://kubernetes.oss-cn-hangzhou.aliyuncs.com/charts
bitnami https://charts.bitnami.com/bitnami
[root@k8s-master helm]# helm repo remove aliyun
"aliyun" has been removed from your repositories
#在本地仓库bitnami中搜索
[root@master ~]# helm search repo bitnami/nginx
NAME CHART VERSION APP VERSION DESCRIPTION
bitnami/nginx 23.0.3 1.30.0 NGINX Open Source is a web server that can be a...
bitnami/nginx-ingress-controller 12.0.7 1.13.1 NGINX Ingress Controller is an Ingress controll...
bitnami/nginx-intel 2.1.15 0.4.9 DEPRECATED NGINX Open Source for Intel is a lig...
# 查看chart信息
[root@k8s-master helm]# helm show chart bitnami/nginx
annotations:
category: Infrastructure
images: |
- name: git
image: docker.io/bitnami/git:2.46.0-debian-12-r0
- name: nginx
image: docker.io/bitnami/nginx:1.27.1-debian-12-r2
- name: nginx-exporter
image: docker.io/bitnami/nginx-exporter:1.3.0-debian-12-r2
licenses: Apache-2.0
# 安装chart 包
[root@master ~]# helm install webserver bitnami/nginx
NAME: webserver
LAST DEPLOYED: Sun Apr 26 10:56:43 2026
NAMESPACE: default
STATUS: deployed
REVISION: 1
DESCRIPTION: Install complete
TEST SUITE: None
NOTES:
CHART NAME: nginx
CHART VERSION: 23.0.3
APP VERSION: 1.30.0
⚠ WARNING: Since August 28th, 2025, only a limited subset of images/charts are available for free.
Subscribe to Bitnami Secure Images to receive continued support and security updates.
More info at https://bitnami.com and https://github.com/bitnami/containers/issues/83267
** Please be patient while the chart is being deployed **
NGINX can be accessed through the following DNS name from within your cluster:
webserver-nginx.default.svc.cluster.local (port 80)
To access NGINX from outside the cluster, follow the steps below:
1. Get the NGINX URL by running these commands:
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
Watch the status with: 'kubectl get svc --namespace default -w webserver-nginx'
export SERVICE_PORT=$(kubectl get --namespace default -o jsonpath="{.spec.ports[0].port}" services webserver-nginx)
export SERVICE_IP=$(kubectl get svc --namespace default webserver-nginx -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
echo "http://${SERVICE_IP}:${SERVICE_PORT}"
WARNING: Rolling tag detected (bitnami/nginx:latest), please note that it is strongly recommended to avoid using rolling tags in a production environment.
+info https://techdocs.broadcom.com/us/en/vmware-tanzu/bitnami-secure-images/bitnami-secure-images/services/bsi-doc/apps-tutorials-understand-rolling-tags-containers-index.html
WARNING: Rolling tag detected (bitnami/git:latest), please note that it is strongly recommended to avoid using rolling tags in a production environment.
+info https://techdocs.broadcom.com/us/en/vmware-tanzu/bitnami-secure-images/bitnami-secure-images/services/bsi-doc/apps-tutorials-understand-rolling-tags-containers-index.html
WARNING: Rolling tag detected (bitnami/nginx-exporter:latest), please note that it is strongly recommended to avoid using rolling tags in a production environment.
+info https://techdocs.broadcom.com/us/en/vmware-tanzu/bitnami-secure-images/bitnami-secure-images/services/bsi-doc/apps-tutorials-understand-rolling-tags-containers-index.html
WARNING: There are "resources" sections in the chart not set. Using "resourcesPreset" is not recommended for production. For production installations, please set the following values according to your workload needs:
- cloneStaticSiteFromGit.gitSync.resources
- resources
+info https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
#查看项目的发布状态
[root@master ~]# helm status webserver
[root@master ~]# helm list
NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION
webserver default 1 2026-04-26 10:56:43.861108531 +0800 CST deployed nginx-23.0.31.30.0
#卸载项目
[root@master ~]# helm uninstall webserver
release "webserver" uninstalled
[root@master ~]# helm list
NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION
# 安装项目前预定义项目选项
[root@master ~]# helm pull bitnami/nginx
[root@master ~]# ls
auth helm-v4.1.1-linux-amd64.tar.gz nginx-23.0.3.tgz
[root@master ~]# tar zxf nginx-23.0.3.tgz
[root@master ~]# cd nginx
[root@master nginx]# vim values.yaml
global:
imageRegistry: "reg.timinglee.org"
image:
registry: registry-1.docker.io
repository: library/nginx
tag: latest
digest: ""
enableDefaultInitContainers: false
livenessProbe:
enabled: false
readinessProbe:
enabled: false
containerSecurityContext:
enabled: false
seLinuxOptions: {}
runAsUser: 1001
runAsGroup: 1001
runAsNonRoot: true
privileged: false
readOnlyRootFilesystem: false
containerPorts:
http: 80
https: 443
[root@master nginx]# helm install webserver /root/nginx --set global.security.allowInsecureImages=true
NAME: webserver
LAST DEPLOYED: Sun Apr 26 11:17:49 2026
NAMESPACE: default
STATUS: deployed
REVISION: 1
DESCRIPTION: Install complete
TEST SUITE: None
NOTES:
CHART NAME: nginx
CHART VERSION: 23.0.3
APP VERSION: 1.30.0
⚠ WARNING: Since August 28th, 2025, only a limited subset of images/charts are available for free.
Subscribe to Bitnami Secure Images to receive continued support and security updates.
More info at https://bitnami.com and https://github.com/bitnami/containers/issues/83267
** Please be patient while the chart is being deployed **
NGINX can be accessed through the following DNS name from within your cluster:
webserver-nginx.default.svc.cluster.local (port 80)
To access NGINX from outside the cluster, follow the steps below:
1. Get the NGINX URL by running these commands:
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
Watch the status with: 'kubectl get svc --namespace default -w webserver-nginx'
export SERVICE_PORT=$(kubectl get --namespace default -o jsonpath="{.spec.ports[0].port}" services webserver-nginx)
export SERVICE_IP=$(kubectl get svc --namespace default webserver-nginx -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
echo "http://${SERVICE_IP}:${SERVICE_PORT}"
WARNING: Rolling tag detected (bitnami/git:latest), please note that it is strongly recommended to avoid using rolling tags in a production environment.
+info https://techdocs.broadcom.com/us/en/vmware-tanzu/bitnami-secure-images/bitnami-secure-images/services/bsi-doc/apps-tutorials-understand-rolling-tags-containers-index.html
WARNING: Rolling tag detected (bitnami/nginx-exporter:latest), please note that it is strongly recommended to avoid using rolling tags in a production environment.
+info https://techdocs.broadcom.com/us/en/vmware-tanzu/bitnami-secure-images/bitnami-secure-images/services/bsi-doc/apps-tutorials-understand-rolling-tags-containers-index.html
WARNING: There are "resources" sections in the chart not set. Using "resourcesPreset" is not recommended for production. For production installations, please set the following values according to your workload needs:
- cloneStaticSiteFromGit.gitSync.resources
- resources
+info https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
⚠ SECURITY WARNING: Original containers have been substituted. This Helm chart was designed, tested, and validated on multiple platforms using a specific set of Bitnami and Tanzu Application Catalog containers. Substituting other containers is likely to cause degraded security and performance, broken chart features, and missing environment variables.
Substituted images detected:
- registry-1.docker.io/library/nginx:latest
⚠ SECURITY WARNING: Verifying original container images was skipped. Please note this Helm chart was designed, tested, and validated on multiple platforms using a specific set of Bitnami and Bitnami Secure Images containers. Substituting other containers is likely to cause degraded security and performance, broken chart features, and missing environment variables.
[root@master nginx]# curl 172.25.254.51
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
2.helm项目构建
1.生成项目
bash
[root@master mnt]# helm create timinglee
Creating timinglee
2.设定项目内容
bash
[root@master mnt]# vim timinglee/Chart.yaml
apiVersion: v2
name: timinglee
description: use nginx create webserver
# A chart can be either an 'application' or a 'library' chart.
#
# Application charts are a collection of templates that can be packaged into versioned archives
# to be deployed.
#
# Library charts provide useful utilities or functions for the chart developer. They're included as
# a dependency of application charts to inject those utilities and functions into the rendering
# pipeline. Library charts do not define any templates and therefore cannot be deployed.
type: application
# This is the chart version. This version number should be incremented each time you make changes
# to the chart and its templates, including the app version.
# Versions are expected to follow Semantic Versioning (https://semver.org/)
version: 1.0.0
# This is the version number of the application being deployed. This version number should be
# incremented each time you make changes to the application. Versions are not expected to
# follow Semantic Versioning. They should reflect the version the application is using.
# It is recommended to use it with quotes.
appVersion: "v1"
[root@master mnt]# vim timinglee/values.yaml
6 replicaCount: 2
9 image:
10 repository: myapp
60 ingress:
61 enabled: true
62 className: "nginx"
63 annotations: {}
64 # kubernetes.io/ingress.class: nginx
65 # kubernetes.io/tls-acme: "true"
66 hosts:
67 - host: myappv1.timinglee.org
68 paths:
69 - path: /
70 pathType: ImplementationSpecific
[root@master mnt]# helm lint timinglee/
==> Linting timinglee/
[INFO] Chart.yaml: icon is recommended
1 chart(s) linted, 0 chart(s) failed
[root@master mnt]# helm package timinglee/
Successfully packaged chart and saved it to: /mnt/timinglee-1.0.0.tgz
[root@master mnt]# ls
timinglee timinglee-1.0.0.tgz
[root@master mnt]# helm install timinglee timinglee-1.0.0.tgz
NAME: timinglee
LAST DEPLOYED: Sun Apr 26 14:41:42 2026
NAMESPACE: default
STATUS: deployed
REVISION: 1
DESCRIPTION: Install complete
NOTES:
1. Get the application URL by running these commands:
http://myapp1.timinglee.org/
[root@master mnt]# curl myapp1.timinglee.org
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
3.helm仓库的使用
1.oci模式
### 建立在harbor中helm仓库

bash
#登录仓库
[root@master anchors]# helm registry login reg.timinglee.org --username admin --password lee --ca-file /etc/docker/certs.d/reg.timinglee.org/ca.crt
level=WARN msg="using --password via the CLI is insecure. Use --password-stdin"
Login Succeeded
#添加认证
[root@master mnt]# cp /etc/docker/certs.d/reg.timinglee.org/ca.crt /etc/pki/ca-trust/source/anchors/
[root@master mnt]# update-ca-trust
[root@master mnt]# helm push timinglee-1.0.0.tgz oci://reg.timinglee.org/helm-charts
Pushed: reg.timinglee.org/helm-charts/timinglee:1.0.0
Digest: sha256:cba01e0211f589941e2f6438b448633436c2a969d6946398eac4ab09d5b8db96
[root@master mnt]# helm pull oci://reg.timinglee.org/helm-charts/timinglee --version 1.0.0
[root@master mnt]# helm install timinglee oci://reg.timinglee.org/helm-charts/timinglee --version 1.0.0
Pulled: reg.timinglee.org/helm-charts/timinglee:1.0.0
Digest: sha256:cba01e0211f589941e2f6438b448633436c2a969d6946398eac4ab09d5b8db96
NAME: timinglee
LAST DEPLOYED: Sun Apr 26 15:08:44 2026
NAMESPACE: default
STATUS: deployed
REVISION: 1
DESCRIPTION: Install complete
NOTES:
1. Get the application URL by running these commands:
http://myapp1.timinglee.org/
2.helm charts
重新构建一个harbor仓库
#### 安装docker-ce
b
[root@node3 yum.repos.d]# vim /etc/yum.repos.d/docker.repo
[docker]
name = docker
baseurl = https://mirrors.aliyun.com/docker-ce/linux/rhel/9.6/x86_64/stable/
gpgcheck = 0
[root@node3 yum.repos.d]# dnf install docker-ce-29.3.1-1.el9 -y
[root@node3 yum.repos.d]# vim /etc/modules-load.d/docker_mod.conf
br_netfilter
[root@node3 yum.repos.d]# modprobe -a br_netfilter
[root@node3 yum.repos.d]# vim /etc/sysctl.d/docker.conf
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
[root@node3 yum.repos.d]# systemctl enable --now docker
安装harbor的2.5.4版本并设定安装模式为helm-charts
b
#生成给证书
[root@node3 ~]# mkdir /data/certs -p
[root@node3 ~]# openssl req -newkey rsa:4096 \
-nodes -sha256 -keyout /data/certs/timinglee.org.key \
-addext "subjectAltName = DNS:charts.timinglee.org" \
-x509 -days 365 -out /data/certs/timinglee.org.crt
Country Name (2 letter code) [XX]:CN
State or Province Name (full name) []:Shannxi
Locality Name (eg, city) [Default City]:Xi'an
Organization Name (eg, company) [Default Company Ltd]:helm
Organizational Unit Name (eg, section) []:charts
Common Name (eg, your name or your server's hostname) []:charts.timinglee.org
Email Address []:charts@timinglee.org
[root@node3 ~]# tar zxf harbor-offline-installer-v2.5.4.tgz -C /opt/
[root@node3 ~]# cd /opt/harbor/
[root@node3 harbor]# cp harbor.yml.tmpl harbor.yml
[root@node3 harbor]# vim harbor.yml
hostname: charts.timinglee.org
# http related config
http:
# port for http, default is 80. If https enabled, this port will redirect to https port
port: 80
# https related config
https:
# https port for harbor, default is 443
port: 443
# The path of cert and key files for nginx
certificate: /data/certs/timinglee.org.crt
private_key: /data/certs/timinglee.org.key
harbor_admin_password: lee
[root@node3 harbor]# ./install.sh --with-chartmuseum
登录harbor建立charts项目



添加仓库到本地helm的repo中
b
[root@node3 harbor]# scp /data/certs/timinglee.org.crt root@172.25.254.100:/etc/pki/ca-trust/source/anchors/ca.crt
timinglee.org.crt
[root@master mnt]# update-ca-trust
[root@master nginx]# cat /etc/hosts
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
192.168.58.10 master
192.168.58.20 node1
192.168.58.30 node2
192.168.58.40 node3
192.168.58.200 reg.timinglee.org
192.168.58.40 charts.timinglee.org #添加解析
[root@master mnt]# helm repo add timinglee https://charts.timinglee.org/chartrepo/charts
"timinglee" has been added to your repositories
上传项目
b
[root@master mnt]# helm cm-push timinglee-1.0.0.tgz timinglee -u admin -p lee
Pushing timinglee-1.0.0.tgz to timinglee...
Done.
[root@master mnt]# helm search repo timinglee
No results found
[root@master mnt]# helm repo update timinglee
Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "timinglee" chart repository
Update Complete. ⎈Happy Helming!⎈
[root@master mnt]# helm search repo timinglee
NAME CHART VERSION APP VERSION DESCRIPTION
timinglee/timinglee 1.0.0 v1 use nginx create webserver
[root@master mnt]# helm search repo timinglee/timinglee -l
NAME CHART VERSION APP VERSION DESCRIPTION
timinglee/timinglee 1.0.0 v1 use nginx create webserver、
[root@master mnt]# helm install timinglee timinglee/timinglee --version 1.0.0
NAME: timinglee
LAST DEPLOYED: Sun Apr 26 15:43:45 2026
NAMESPACE: default
STATUS: deployed
REVISION: 1
DESCRIPTION: Install complete
NOTES:
1. Get the application URL by running these commands:
http://myapp1.timinglee.org/
十一.Prometheus
Prometheus是一个开源的服务监控系统和时序数据库
其提供了通用的数据模型和快捷数据采集、存储和查询接口
它的核心组件Prometheus服务器定期从静态配置的监控目标或者基于服务发现自动配置的目标中进行拉
取数据
新拉取到数据大于配置的内存缓存区时,数据就会持久化到存储设备当中
1.在k8s中部署Prometheus
bash
2.1 下载部署Prometheus所需资源
#在helm中添加Prometheus仓库
[root@k8s-master helm]# helm repo add prometheus-community https://prometheuscommunity.github.io/helm-charts
"prometheus-community" has been added to your repositories
#下载Prometheus项目
[root@k8s-master helm]# helm pull prometheus-community/kube-prometheus-stack
[root@k8s-master helm]# ls
kube-prometheus-stack-62.6.0.tgz
#解压项目包
[root@k8s-master helm]# tar zxf kube-prometheus-stack-62.6.0.tgz
[root@k8s-master helm]# ls
kube-prometheus-stack kube-prometheus-stack-62.6.0.tgz
[root@k8s-master helm]# cd kube-prometheus-stack/
[root@k8s-master kube-prometheus-stack]# ls
Chart.lock charts Chart.yaml CONTRIBUTING.md README.md templates
values.yaml
#根据所有项目中的values.yaml中指定的image路径下载容器镜像并上传至harbor仓库
#利用helm安装Prometheus
[root@k8s-master kube-prometheus-stack]# kubectl create namespace kubeprometheus-stack
#注意,在安装过程中千万别ctrl+c
[root@k8s-master kube-prometheus-stack]# helm -n kube-prometheus-stack install
kube-prometheus-stack .
NAME: kube-prometheus-stack
LAST DEPLOYED: Tue Sep 10 20:56:53 2024
NAMESPACE: kube-prometheus-stack
STATUS: deployed
REVISION: 1
NOTES:
kube-prometheus-stack has been installed. Check its status by running:
kubectl --namespace kube-prometheus-stack get pods -l "release=kubeprometheus-stack"
Visit https://github.com/prometheus-operator/kube-prometheus for instructions on
how to create & configure Alertmanager and Prometheus instances using the
Operator.
#查看所有pod是否运行
[root@k8s-master kube-prometheus-stack]# kubectl --namespace kube-prometheusstack get pods
NAME READY STATUS
RESTARTS AGE
alertmanager-kube-prometheus-stack-alertmanager-0 2/2 Running 0
103s
kube-prometheus-stack-grafana-74ff665878-psdvh 3/3 Running 0
107s
kube-prometheus-stack-kube-state-metrics-7974795889-8kj6j 1/1 Running 0
107s
kube-prometheus-stack-operator-7966d67576-fh6ld 1/1 Running 0
107s
kube-prometheus-stack-prometheus-node-exporter-stzn2 1/1 Running 0
106s
kube-prometheus-stack-prometheus-node-exporter-vrw2g 1/1 Running 0
107s
kube-prometheus-stack-prometheus-node-exporter-zxtdw 1/1 Running 0
106s
prometheus-kube-prometheus-stack-prometheus-0 2/2 Running 0
103s
Note
各个svc的作用
alertmanager-operated 告警管理
kube-prometheus-stack-grafana 展示prometheus采集到的指标
kube-prometheus-stack-prometheus-node-exporter 收集节点级别的指标的工具
kube-prometheus-stack-prometheus 主程序
2.登陆grafana
bash
#查看svc
[root@k8s-master kube-prometheus-stack]# kubectl -n kube-prometheus-stack get
svc
NAME TYPE CLUSTER-IP
EXTERNAL-IP PORT(S) AGE
alertmanager-operated ClusterIP None
<none> 9093/TCP,9094/TCP,9094/UDP 38s
kube-prometheus-stack-alertmanager ClusterIP 10.109.139.4
<none> 9093/TCP,8080/TCP 42s
kube-prometheus-stack-grafana ClusterIP 10.106.144.184
<none> 80/TCP 42s
kube-prometheus-stack-kube-state-metrics ClusterIP 10.104.168.19
<none> 8080/TCP 42s
kube-prometheus-stack-operator ClusterIP 10.104.99.75
<none> 443/TCP 42s
kube-prometheus-stack-prometheus ClusterIP 10.106.220.91
<none> 9090/TCP,8080/TCP 42s
kube-prometheus-stack-prometheus-node-exporter ClusterIP 10.100.199.149
<none> 9100/TCP 42s
prometheus-operated
#修改暴漏方式
[root@k8s-master kube-prometheus-stack]# kubectl -n kube-prometheus-stack edit
svc kube-prometheus-stack-grafana
type: LoadBalancer
#查看grafana密码
[root@k8s-master helm]# kubectl -n kube-prometheus-stack get secrets kubeprometheus-stack-grafana -o yaml
apiVersion: v1
data:
admin-password: cHJvbS1vcGVyYXRvcg==
admin-user: YWRtaW4=
ldap-toml: ""
kind: Secret
metadata:
annotations:
meta.helm.sh/release-name: kube-prometheus-stack
meta.helm.sh/release-namespace: kube-prometheus-stack
creationTimestamp: "2024-09-10T12:57:03Z"
labels:
app.kubernetes.io/instance: kube-prometheus-stack
app.kubernetes.io/managed-by: Helm
app.kubernetes.io/name: grafana
app.kubernetes.io/version: 11.2.0
helm.sh/chart: grafana-8.5.1
name: kube-prometheus-stack-grafana
namespace: kube-prometheus-stack
resourceVersion: "332682"
uid: fc50f5e2-ebc7-48b0-b3b6-960b8e077d03
type: Opaque
#查看密码
[root@k8s-master helm]# echo -n "cHJvbS1vcGVyYXRvcg==" | base64 -d
prom-operator #密码
[root@k8s-master helm]# echo "YWRtaW4=" | base64 -d
admin #用户


3.导入面板
官方监控模板:https://grafana.com/grafana/dashboards




导入后效果如下

bash
2.5 访问Prometheus 主程序
[root@k8s-master helm]# kubectl -n kube-prometheus-stack edit svc kubeprometheus-stack-prometheus
type: LoadBalancer
[root@k8s-master helm]# kubectl -n kube-prometheus-stack get svc kubeprometheus-stack-prometheus
NAME TYPE CLUSTER-IP EXTERNAL-IP
PORT(S) AGE
kube-prometheus-stack-prometheus LoadBalancer 10.106.220.91 172.25.254.52
9090:34249/TCP,8080:34990/TCP 65m


4.监控使用示例
3.1 建立监控项目
bash
#下载示例所需helm项目
[root@k8s-master test]# helm pull bitnami/nginx --version 18.1.11
[root@k8s-master test]# ls
nginx-18.1.11.tgz nginx-exporter-1.3.0-debian-12-r2.tar
[root@k8s-master test]# tar zxf nginx-18.1.11.tgz
[root@k8s-master test]# cd nginx/
#修改项目开启监控
[root@k8s-master nginx]# vim values.yaml
*** ***
metrics:
## @param metrics.enabled Start a Prometheus exporter sidecar container
##
enabled: true
## Bitnami NGINX Prometheus Exporter image
## ref: https://hub.docker.com/r/bitnami/nginx-exporter/tags/
*** ***
serviceMonitor:
## @param metrics.serviceMonitor.enabled Creates a Prometheus Operator
ServiceMonitor (also requires `metrics.enabled` to be `true`)
##
enabled: true
## @param metrics.serviceMonitor.namespace Namespace in which Prometheus is
running
##
namespace: "kube-prometheus-stack"
*** ***
labels:
release: kube-prometheus-stack #指定监控标签
[root@k8s-master ~]# kubectl -n kube-prometheus-stack get
servicemonitors.monitoring.coreos.com --show-labels
#安装项目,在安装之前一定要上传镜像到仓库中
[root@k8s-master nginx]# helm install timinglee .
NAME: timinglee
LAST DEPLOYED: Tue Sep 10 23:18:19 2024
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
CHART NAME: nginx
CHART VERSION: 18.1.11
APP VERSION: 1.27.1
[root@k8s-master nginx]# kubectl get pods
NAME READY STATUS RESTARTS AGE
timinglee-nginx-68dfdd6c-6gc69 2/2 Running 0 15s
root@k8s-master nginx]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S)
AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP
7d5h
timinglee-nginx LoadBalancer 10.99.134.151 172.25.254.53
80:34867/TCP,443:31734/TCP,9113:34171/TCP 61s
[root@k8s-master nginx]# curl 172.25.254.53
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
3.2 监控调整
#压力测试
[root@k8s-master nginx]# ab -c 5 -n 100 http://172.25.254.53/index.html
3.2 监控调整



十二.Zabbix
Zabbix 是一款开源的企业级监控解决方案,主要用于实时监控网络、服务器、应用程序等 IT 基础设施
的状态和性能,帮助管理员及时发现并解决问题,保障系统的稳定运行
1.部署zabbix
###1.通过官方网站下载zabbix资源



2.部署zabbix安装源
bash
#安装官方源到系统中
[root@zabbix ~]# rpm -Uvh https://repo.zabbix.com/zabbix/7.0/rhel/10/x86_64/zabbix-release-latest-7.0.el10.noarch.rpm
[root@zabbix ~]# dnf install zabbix-server-mysql zabbix-web-mysql zabbix-apache-conf zabbix-sql-scripts zabbix-selinux-policy zabbix-agent
#查看信息
[root@zabbix ~]# cd /etc/yum.repos.d/
[root@zabbix yum.repos.d]# ls
redhat.repo rhel9.repo zabbix.repo zabbix-tools.repo
3.安装zabbix服务端
bash
[root@zabbix ~]# dnf install zabbix-agent.x86_64 zabbix-server-mysql.x86_64 -y
4.安装zabbix前端及客户端
bash
[root@zabbix ~]# dnf install zabbix-web-mysql zabbix-nginx-conf zabbix-sql-scripts zabbix-agent -y
5.安装数据库
mysql数据库官网:www.mysql.com



bash
#下载mysql8的rpm包
[root@zabbix ~]# wget https://downloads.mysql.com/archives/get/p/23/file/mysql-8.0.39-1.el9.x86_64.rpm-bundle.tar
#解压mysql8软件包归档文件
[root@zabbix ~]# tar xf mysql-8.0.45-1.el9.x86_64.rpm-bundle.tar
#安装mysql8
[root@zabbix ~]# dnf install mysql-community-client-8.0.39-1.el9.x86_64.rpm \
mysql-community-client-plugins-8.0.39-1.el9.x86_64.rpm \
mysql-community-common-8.0.39-1.el9.x86_64.rpm \
mysql-community-icu-data-files-8.0.39-1.el9.x86_64.rpm \
mysql-community-server-8.0.39-1.el9.x86_64.rpm \
mysql-community-libs-8.0.39-1.el9.x86_64.rpm -y
#启动数据库
[root@zabbix ~]# systemctl enable --now mysqld
#查看数据库密码
[[root@zabbix ~]# grep password /var/log/mysqld.log
2026-05-10T06:26:37.360106Z 6 [Note] [MY-010454] [Server] A temporary password is generated for root@localhost: JMFd.amNY9Qm
#数据库安全初始化
[root@zabbix ~]# mysql_secure_installation
Securing the MySQL server deployment.
Enter password for user root:
The 'validate_password' component is installed on the server.
The subsequent steps will run with the existing configuration
of the component.
Using existing password for root.
Estimated strength of the password: 100
Change the password for root ? ((Press y|Y for Yes, any other key for No) : no
... skipping.
By default, a MySQL installation has an anonymous user,
allowing anyone to log into MySQL without having to have
a user account created for them. This is intended only for
testing, and to make the installation go a bit smoother.
You should remove them before moving into a production
environment.
Remove anonymous users? (Press y|Y for Yes, any other key for No) :
... skipping.
Normally, root should only be allowed to connect from
'localhost'. This ensures that someone cannot guess at
the root password from the network.
Disallow root login remotely? (Press y|Y for Yes, any other key for No) :
... skipping.
By default, MySQL comes with a database named 'test' that
anyone can access. This is also intended only for testing,
and should be removed before moving into a production
environment.
Remove test database and access to it? (Press y|Y for Yes, any other key for No) :
... skipping.
Reloading the privilege tables will ensure that all changes
made so far will take effect immediately.
Reload privilege tables now? (Press y|Y for Yes, any other key for No) :
... skipping.
All done!
6.为zabbix建立数据库
bash
#建立zabbix所需用户并授权
[root@zabbix ~]# mysql -uroot -p6751515ming+ZLN
password
mysql> create database zabbix character set utf8mb4 collate utf8mb4_bin;
mysql> create user zabbix@localhost identified by '6751515ming+ZLN';
mysql> grant all privileges on zabbix.* to zabbix@localhost;
mysql> set global log_bin_trust_function_creators = 1;
mysql> quit;
#向数据库中导入zabbix的数据
[root@zabbix ~]# zcat /usr/share/zabbix-sql-scripts/mysql/server.sql.gz | mysql --default-character-set=utf8mb4 -uzabbix -p6751515ming+ZLN zabbix
7.设置zabbix服务端配置
bash
#编辑主配置文件
[root@zabbix ~]# vim /etc/zabbix/zabbix_server.conf
DBPassword=6751515ming+ZLN
#启动zabbix
[root@zabbix ~]# systemctl enable --now zabbix-server zabbix-agent httpd php-fpm
8.设置zabbix server







9.修改字体文件
在zabbix中如果使用中文字体图形显示会有错误,需要手动调整字体来解决问题



解决字体问题方法:
bash
[root@zabbix zabbix]# cd /usr/share/zabbix/assets/fonts/
[root@zabbix fonts]# cp /mnt/zabbix-6/simkai.ttf .
[root@zabbix fonts]# rm -rf graphfont.ttf
[root@zabbix fonts]# ln -s /usr/share/zabbix/assets/fonts/simkai.ttf /usr/share/zabbix/assets/fonts/graphfont.ttf
10.zabbix agent 部署与使用
Zabbix Agent 是 Zabbix 监控系统的重要组件,主要部署在被监控的目标设备(如服务器、虚拟机、网
络设备等)上,负责收集本地系统的各类数据(如 CPU 使用率、内存占用、磁盘空间、进程状态等),
并将数据发送给 Zabbix Server 或 Zabbix Proxy 进行处理和存储。
Zabbix Agent 的核心作用
- 数据采集:主动主动或被动方式收集被监控设备的性能指标、系统状态、应用程序数据等。
- 数据传输:将采集到的数据发送给 Zabbix Server/Proxy,确保监控数据的实时性和准确性。
- 执行命令:接收并执行 Zabbix Server 下发的远程命令(如脚本执行、服务启停等),支持监控场
景的自动化操作。root@zabbix zabbix\]# cd /usr/share/zabbix/assets/fonts/ \[root@zabbix fonts\]# cp /mnt/zabbix-6/simkai.ttf . \[root@zabbix fonts\]# rm -rf graphfont.ttf \[root@zabbix fonts\]# ln -s /usr/share/zabbix/assets/fonts/simkai.ttf /usr/share/zabbix/assets/fonts/graphfont.ttf 两种运行模式
由 Zabbix Server 主动向 Agent 发起数据请求,Agent 被动响应并返回数据。
优势:节省 Agent 端资源,适合监控大规模设备时降低客户端压力。
劣势:Server 端请求压力可能较大,需合理配置超时时间和并发数。 - 主动模式
Agent 主动向 Server/Proxy 发送采集到的数据,无需 Server 发起请求。
优势:减轻 Server 端请求压力,适合跨网段、广域网等网络条件复杂的场景。
配置:需在 Agent 配置文件中指定 ServerActive (目标 Server/Proxy 地址),并在 Server
端将监控项类型设为 "主动式"。
1.被动模式如何添加监控节点
bash
#在要被监控节点中安装agent
[root@servera ~]# rpm -Uvh
https://repo.zabbix.com/zabbix/7.0/rhel/9/x86_64/zabbix-release-latest-
7.0.el9.noarch.rpm
[root@servera ~]# dnf install zabbix-agent.x86_64 -y
[root@servera ~]# vim /etc/zabbix/zabbix_agentd.conf
Server=172.25.254.100 #zabbix server主机,允许谁来取监控数据(主动模式)
ServerActive=172.25.254.100 #允许讲监控数据提交给谁(被动模式)
Hostname=servera #指定主机名
UnsafeUserParameters=1 #是否限制用户定义key时是同特殊字符,1表示不限制
AllowKey=system.run[*] #是否接收远程操作命令
LogRemoteCommands=1 #把远程执行动作记录在日志中
#启动agent
[root@servera ~]# systemctl enable --now zabbix-agent.service
[root@servera ~]# netstat -antlupe | grep zabbix
tcp 0 0 0.0.0.0:10050 0.0.0.0:* LISTEN
980 59055 32814/zabbix_agentd
tcp6 0 0 :::10050 :::* LISTEN
980 59056 32814/zabbix_agentd
创建主机群组
Zabbix 主机群组(Host Group)的核心作用是按业务 / 环境 / 用途对主机做逻辑归类,从而实现权限、配置、告警、可视化的批量与分层管理,是大规模监控的基础组织单元Zabbix

2.在zabbix的server端添加要被监控的主机


!NOTE
此时 被监控主机可用性为铅色,意味着没有取到监控数据,这是因为没有添加监控项的原因
创建监控项
要在Zabbix管理页面创建一个监控项,请执行以下操作:
进入到: 数据采集→ 主机
在主机所在的行单击 监控项
点击屏幕右上角的创建监控项
输入表单中监控项的参数
你也可以打开一个已经存在的监控项,点击克隆 按钮,然后重命名保存。
任何一个被监控项,如果想要能够被监控,一定要在zabbix-server端定义了能够连接至zabbix-agent
端,并且能够获取命令。或者在agent端定义了能够让server端获取命令。一般都是内建的命令,都对应
的有其名字,被我们称之为 key
的有其名字,被我们称之为key。



创建不带参数监控项



bash
[root@zabbix ~]# zabbix_get -s 172.25.254.10 -k system.uptime -p 10050
1518
带参数的监控项
示例1:监控一个文件大小
bash
#在要被监控的主机中建立一个大文件
[root@servera ~]# dd if=/dev/zero of=/mnt/timinglee bs=1M count=1024






示例2.监控cpu负载

示例3.监控端口

3.创建触发器
触发器简介
- 触发器就是用来判断监控项监控到的数据是否是在一个合理的范围之内
- 当我们的采集的值定义完了以后,就可以来定义触发器了。
- 我们触发器的定义是:界定某特定的监控项采集到的数据的非合理区间或非合理状态。通常为逻辑表达式。
!NOTE
一般,我们评定采样数值是否为合理区间的比较稳妥的方法是------根据最后N次的平均值来判定结果;这个最后N几分钟通常有两种定义方式:
- 最近N分钟所得结果的平均值
- 最近N次所得结果的平均值
触发器表达式
{<server>:<key>.<function>(<parameter>)}<operator><constant>
server: 主机名称;
key: 主机上关系的相应监控项的key;
function: 评估采集到的数据是否在合理范围内时所使用的函数,目前触发器所支持的函数有
avg(平均)、count(计数)、change(变化)、date(日期)、dayofweek(星期)、delta(增量)
diff、iregexp、last()、max(最大值)、min(最小值)、nodata(无数据)、now(现在)
sum(总和)等
parameter: 函数参数;大多数数值函数可以接受秒数为其参数,而如果在数值参数之前使用"#"做为前缀
则表示为最近几次的取值,如sum(300)表示300秒内所有取值之和
而sum(#10)则表示最近10次取值之和
配置一个触发器
- 进入: 数据采集 → 主机
- 点击主机一行的 触发器
- 点击右上角的 创建触发器 (或者点击触发器名称去修改一个已存在的触发器)
- 在窗口中输入触发器的参数




模拟问题观察数据
bash
[root@servera ~]# systemctl stop sshd
[root@servera ~]# netstat -antlupe | grep sshd
tcp 0 0 172.25.254.10:22 172.25.254.1:1576 ESTABLISHED 0 28165 1563/sshd: root [pr
tcp 0 64 172.25.254.10:22 172.25.254.1:1575 ESTABLISHED 0 28133 1559/sshd: root [pr

自动执行脚本动作(action)
创建脚本并提权zabbix用户

bash
[root@servera ~]# echo "zabbix ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers
[root@servera ~]# sudo -u zabbix sudo whoami
root
创建触发器动作
bash
#记录远程动作执行情况
[root@servera ~]# vim /etc/zabbix/zabbix_agentd.conf
LogRemoteCommands=1
创建动作脚本


创建触发器动作



测试问题

[root@servera ~]# systemctl stop sshd

4.报警设置
一旦发生问题,我们添加了第一步需要做的事情,也就是重启服务,如果重启不成功怎么办呢?
可以是发送邮件,让我们及时知道问题仍然存在,然后人工介入紧急处理;
邮箱报警,需要创建媒介
邮箱类型有很多种,比如QQ邮箱,阿里云邮箱,163邮箱等;想用邮箱接收报警,要开启邮箱的SMTP服务
我们这里用163邮箱:
163邮箱官网:https://mail.163.com/
报警设置
一旦发生问题,我们添加了第一步需要做的事情,也就是重启服务,如果重启不成功怎么办呢?
可以是发送邮件,让我们及时知道问题仍然存在,然后人工介入紧急处理;
邮箱报警,需要创建媒介
邮箱类型有很多种,比如QQ邮箱,阿里云邮箱,163邮箱等;想用邮箱接收报警,要开启邮箱的SMTP服务
我们这里用163邮箱:
163邮箱官网:https://mail.163.com/
开启邮箱POP3/SMTP服务



设置告警媒介






添加告警


触发问题时发送的邮件

恢复时发送的邮件





测试:
bash
[root@servera ~]# systemctl stop sshd


中文告警模板
问题 模板配置如下
主题: 故障发生 {EVENT.NAME}
消息:
**************错误**************
告警地址:{HOSTNAME1}
告警主机:{HOST.NAME}
告警时间:{EVENT.DATE} {EVENT.TIME}
告警等级:{TRIGGER.SEVERITY}
告警信息: {TRIGGER.NAME}
告警项目:{TRIGGER.KEY1}
问题详情:{ITEM.NAME}:{ITEM.VALUE1}
当前状态:{TRIGGER.STATUS}
事件ID:{EVENT.ID}
问题恢复 模板配置如下
主题: 故障恢复 {TRIGGER. NAME} {EVENT.DURATION}: {EVENT.NAME}
消息:
**************恢复**************
恢复地址:{HOSTNAME1}
恢复主机:{HOST.NAME}
恢复时间:{EVENT.DATE} {EVENT.TIME}
告警等级:{TRIGGER.SEVERITY}
恢复信息: {TRIGGER.NAME}
恢复项目:{TRIGGER.KEY1}
问题详情:{ITEM.NAME}:{ITEM.VALUE1}
当前状态:{TRIGGER.STATUS}
持续时间:{EVENT.AGE}
事件ID:{EVENT.ID}

11.zabbix创建自定义模板
zabbix创建自定义模板的意义
- 告别一台一台手动配置
- 统一监控标准,杜绝配置混乱
- 后期维护只改模板,所有主机自动更新
- 可打包全套监控能力
- 区分业务类型,分类管理
- 不碰官方内置模板,升级不被覆盖
- 支持导入导出,备份 / 迁移超方便
创建自定义模板



创建磁盘监控项



fs 是文件系统的具体实例。一个具体的挂载点,如/home或者/boot等等;
<mode> 参数决定了如何计算文件系统的大小。通常,以下是一些常见的模式:
total: 返回文件系统的总大小,包括所有已使用的和未使用的空间。
used:返回文件系统中已经使用的空间大小。
free: 返回文件系统中剩余的可用空间大小。
available: 返回文件系统中可以分配给普通用户的空间。
创建监控项图形



添加自定义模板到主机





12.主动模式如何添加监控节点
bash
#在要被监控节点中安装agent
[root@servera ~]# rpm -Uvh https://repo.zabbix.com/zabbix/7.0/rhel/9/x86_64/zabbix-release-latest-7.0.el9.noarch.rpm
[root@serverb ~]# dnf install zabbix-agent.x86_64 -y
[root@serverb ~]# vim /etc/zabbix/zabbix_agentd.conf
Server=172.25.254.100 #zabbix server主机,允许谁来取监控数据(主动模式)
ServerActive=172.25.254.100 #允许讲监控数据提交给谁(被动模式)
Hostname=serverb #指定主机名
UnsafeUserParameters=1 #是否限制用户定义key时是同特殊字符,1表示不限制
AllowKey=system.run[*] #是否接收远程操作命令
#启动agent
[root@serverb ~]# systemctl enable --now zabbix-agent.service
[root@serverb ~]# netstat -antlupe | grep zabbix
tcp 0 0 0.0.0.0:10050 0.0.0.0:* LISTEN 980 59055 32814/zabbix_agentd
tcp6 0 0 :::10050 :::* LISTEN 980 59056 32814/zabbix_agentd
#每台主机在配置地址解析
配置自动发现动作
当 Zabbix Server 通过自动发现规则发现新的设备(服务器、交换机等)或资源(如磁盘、端口等)时,自动发现动作会根据预设的条件和操作,自动完成以下任务:
- 将新设备添加到 Zabbix 监控中
- 为新设备关联对应的监控模板(如 Linux 模板、网络设备模板)
- 加入指定的主机组
- 发送发现通知(邮件、短信等)
- 执行自定义脚本(如初始化监控配置)






13.自动注册
在 Zabbix 中,自动注册(Auto-registration) 是一种让 Zabbix Agent 主动向 Zabbix Server 报到并请求被监控的机制。与 "自动发现"(Server 主动扫描)不同,自动注册是 Agent 主动发起注册请求,适用于动态环境(如容器、云服务器)中快速纳入新部署的设备。
核心原理
- 当 Zabbix Agent 启动时,会根据配置文件中的
ServerActive参数,向指定的 Zabbix Server/Proxy 发送注册请求(包含自身 Hostname、IP 等信息)。 - Zabbix Server 接收请求后,根据预设的 "自动注册动作" 判断是否接受该 Agent,并自动执行配置操作(如添加主机、关联模板等)。
!NOTE
在做自动注册实验时确保自动发现动作已经全部停止




十三.CD/CI持续集成和持续交互
一.CI/CD是什么
CI/CD 是指持续集成(Continuous Integration)和持续部署(Continuous Deployment)或持续交付
(Continuous Delivery)
1.1 持续集成(Continuous Integration)
持续集成是一种软件开发实践,团队成员频繁地将他们的工作集成到共享的代码仓库中。其主要特点包
括:
- 频繁提交代码:开发人员可以每天多次提交代码,确保代码库始终保持最新状态。
- 自动化构建:每次提交后,自动触发构建过程,包括编译、测试、静态分析等。
- 快速反馈:如果构建失败或测试不通过,能够快速地向开发人员提供反馈,以便及时修复问题。
1.2 持续部署(Continuous Deployment)
持续部署是在持续集成的基础上,将通过所有测试的代码自动部署到生产环境中。其特点如下: - 自动化流程:从代码提交到生产环境的部署完全自动化,无需人工干预。
- 高频率部署:可以实现频繁的部署,使得新功能能够快速地提供给用户。
- 风险控制:需要有强大的测试和监控体系来确保部署的稳定性和可靠性。
1.3 持续交付(Continuous Delivery)
持续交付与持续部署类似,但不一定自动部署到生产环境,而是随时可以部署。其重点在于确保软件随
时处于可发布状态。
CI/CD 的好处包括: - 提高开发效率:减少手动操作和等待时间,加快开发周期。
- 尽早发现问题:通过频繁的集成和测试,问题能够在早期被发现和解决。
- 降低风险:减少了大规模部署时可能出现的问题,提高了软件的质量和稳定性。
- 增强团队协作:促进团队成员之间的沟通和协作,提高团队的整体效率。
常见的 CI/CD 工具包括 Jenkins、GitLab CI/CD、Travis CI 等。这些工具可以帮助团队实现自动化的构
建、测试和部署流程
二.git工具使用
2.1 git简介
Git 是一个分布式版本控制系统,被广泛用于软件开发中,以管理代码的版本和变更。
主要特点:
分布式
每个开发者都有完整的代码仓库副本,这使得开发者可以在离线状态下进行工作,并且在网络
出现问题时也不会影响开发。
即使中央服务器出现故障,开发者仍然可以在本地进行开发和查看项目历史。
高效的分支管理
Git 中的分支创建和切换非常快速和简单。开发人员可以轻松地创建新的分支来进行新功能的
开发或修复 bug,而不会影响主分支。
合并分支也相对容易,可以使用多种合并策略来满足不同的需求。
快速的版本回退
如果发现某个版本存在问题,可以快速回退到之前的版本。
可以查看每个版本的详细变更记录,方便了解代码的演进过程。
强大的提交管理
每个提交都有一个唯一的标识符,可以方便地引用和查看特定的提交。
提交可以包含详细的提交信息,描述本次提交的更改内容。
支持协作开发
开发者可以将自己的更改推送到远程仓库,供其他开发者拉取和合并。
可以处理多个开发者同时对同一文件进行修改的情况,通过合并冲突解决机制来确保代码的完
整性。
Git必看秘籍:https://git-scm.com/book/zh/v2
2.2 git 工作流程
Git 有三种状态:已提交(committed)、已修改(modified) 和 已暂存(staged)。
已修改表示修改了文件,但还没保存到数据库中。
已暂存表示对一个已修改文件的当前版本做了标记,使之包含在下次提交的快照中。
已提交表示数据已经安全地保存在本地数据库中。
这会让我们的 Git 项目拥有三个阶段:工作区、暂存区以及 Git 目录。
三.部署git
3.1 安装git
bash
#在rhel9的系统中默认自带git
[root@CICD-node1 ~]# dnf install git -y
#设定命令补全功能
[root@CICD-node1 timinglee]# echo "source /usr/share/bashcompletion/completions/git" >> ~/.bashrc
[root@CICD-node1 timinglee]# source ~/.bashrc
3.2 初始化
获取 Git 仓库通常有两种方式:
将尚未进行版本控制的本地目录转换为 Git 仓库。
从其它服务器克隆 一个已存在的 Git 仓库。比如: git clone
初始化版本库
bash
[root@CICD-node1 ~]# mkdir timinglee
[root@CICD-node1 timinglee]# git init
提示:使用 'master' 作为初始分支的名称。这个默认分支名称可能会更改。要在新仓库中
提示:配置使用初始分支名,并消除这条警告,请执行:
提示:
提示: git config --global init.defaultBranch <名称>
提示:
提示:除了 'master' 之外,通常选定的名字有 'main'、'trunk' 和 'development'。
提示:可以通过以下命令重命名刚创建的分支:
提示:
提示: git branch -m <name>
已初始化空的 Git 仓库于 /root/timinglee/.git/
[root@CICD-node1 timinglee]# ls -a
. .. .git
[root@CICD-node1 timinglee]# ls .git/
branches config description HEAD hooks info objects refs
#设定用户信息
[root@CICD-node1 timinglee]# git config --global user.name "timinglee"
[root@CICD-node1 timinglee]# git config --global user.email
"timinglee@timinglee.org"
#查看当前文件状态
[root@CICD-node1 timinglee]# git status
位于分支 master
尚无提交
无文件要提交(创建/拷贝文件并使用 "git add" 建立跟踪)
[root@CICD-node1 timinglee]# git status -s #简化输出
.git目录是git跟踪管理版本库的,没事别瞎溜达
四.git的使用方法
4.1 常用方法
bash
[root@CICD-node1 timinglee]# echo timnglee > README.md
[root@CICD-node1 timinglee]# git status
位于分支 master
尚无提交
未跟踪的文件:
(使用 "git add <文件>..." 以包含要提交的内容)
README.md
提交为空,但是存在尚未跟踪的文件(使用 "git add" 建立跟踪)
[root@CICD-node1 timinglee]# git status -s
?? README.md #?? 新建文件未添加到版本库
[root@CICD-node1 timinglee]# git add README.md
[root@CICD-node1 timinglee]# git status -s
A README.md #A 已添加到暂存区
#提交暂存区的数据
[root@CICD-node1 timinglee]# git commit -m "add README.md"
[master(根提交) 74625b0] add README.md
1 file changed, 1 insertion(+)
create mode 100644 README.md
[root@CICD-node1 timinglee]# git status -s #无任何显示,标识已经提交到版本库
#再次修改
[root@CICD-node1 timinglee]# vim README.md
timnglee
timnglee
[root@CICD-node1 timinglee]# git status -s
M README.md #右M 表示文件在工作区被修改
#撤销修改
[root@CICD-node1 timinglee]# git checkout -- README.md
从索引区更新了 1 个路径
[root@CICD-node1 timinglee]# cat README.md
timnglee
#从新修改
[root@CICD-node1 timinglee]# echo timinglee> README.md
[root@CICD-node1 timinglee]# git add README.md
[root@CICD-node1 timinglee]# git status -s
M README.md #左M表示文件已经在版本库中并被跟踪,
#从暂存区撤销
[root@CICD-node1 timinglee]# git restore --staged README.md
[root@CICD-node1 timinglee]# git status -s
M README.md
#从新提交
[root@CICD-node1 timinglee]# git add README.md
[root@CICD-node1 timinglee]# git status -s
M README.md
#更新
[root@CICD-node1 timinglee]# git commit -m "update v1"
[master 6a14bb5] update v1
1 file changed, 1 insertion(+), 1 deletion(-)
[root@CICD-node1 timinglee]# git status -s
#更新文件
[root@CICD-node1 timinglee]# echo timinglee >> README.md
[root@CICD-node1 timinglee]# git add README.md
[root@CICD-node1 timinglee]# echo timinglee >> README.md
[root@CICD-node1 timinglee]# git status -s
MM README.md #MM表示有一部分在暂存区,还有一部分没有提
交
#如果现在提交只能提交在暂存区中的部分
[root@CICD-node1 timinglee]# git commit -m "update v2"
[master dc9b45f] update v2
1 file changed, 1 insertion(+)
[root@CICD-node1 timinglee]# git status -s
M README.md #右M还在
#查看已暂存和未暂存的修改变化
[root@CICD-node1 timinglee]# echo timinglee >> README.md
[root@CICD-node1 timinglee]# git diff
diff --git a/README.md b/README.md
index 62be538..87bd0f6 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,3 @@
timinglee
timinglee
+timinglee
#跳过使用暂存区,只能在提交过的在版本库中存在的文件使用如果文件状态是"??"不能用此方法
[root@CICD-node1 timinglee]# git commit -a -m "update v3"
[master 3579560] update v3
1 file changed, 1 insertion(+)
#撤销工作区中删除动作
[root@CICD-node1 timinglee]# touch lee.txt
[root@CICD-node1 timinglee]# git add lee.txt
[root@CICD-node1 timinglee]# git commit -m "add lee.txt"
[master 16141e7] add lee.txt
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 lee.txt
[root@CICD-node1 timinglee]# git status -s
D lee.txt #右D表示文件在工作区被删除
[root@CICD-node1 timinglee]# git checkout -- lee.txt
[root@CICD-node1 timinglee]# ls
dir1 lee.txt README.md
#从版本库中删除文件
[root@CICD-node1 timinglee]# git rm lee.txt
rm 'lee.txt'
[root@CICD-node1 timinglee]# git status -s
D lee.txt #左D表示文件删除动作被提交到暂存区
[root@CICD-node1 timinglee]# git commit -m "delete lee.txt"
[master 85483db] delete lee.txt
1 file changed, 0 insertions(+), 0 deletions(-)
delete mode 100644 lee.txt
[root@CICD-node1 timinglee]# git status -s
#恢复从版本库中被删除的文件
[root@CICD-node1 timinglee]# git log #查看操作日志
commit 85483db3cb7f543950f678b7d04b85daef96c248 (HEAD -> master)
Author: timinglee <timinglee@timinglee.org>
Date: Wed Sep 11 01:52:49 2024 +0800
delete lee.txt
commit 16141e793a06cdce042e203e5c4a78f8fc92736b
Author: timinglee <timinglee@timinglee.org>
Date: Wed Sep 11 01:48:45 2024 +0800
add lee.txt
commit 3579560e8307005cc26cf51f4decfe2024762d4c
Author: timinglee <timinglee@timinglee.org>
Date: Wed Sep 11 01:46:38 2024 +0800
update v3
commit dc9b45fea7284911e24733dce044ca18624e791b
Author: timinglee <timinglee@timinglee.org>
Date: Wed Sep 11 01:32:43 2024 +0800
update v2
commit 6a14bb58e424a96c3b18c694fbe444bd1e662605
Author: timinglee <timinglee@timinglee.org>
Date: Wed Sep 11 01:28:35 2024 +0800
update v1
commit 74625b07475241cea36cc23565ebd0eb7a95142c
Author: timinglee <timinglee@timinglee.org>
Date: Wed Sep 11 01:18:56 2024 +0800
add README.md
[root@CICD-node1 timinglee]# git reflog #查看提交动作
85483db (HEAD -> master) HEAD@{0}: commit: delete lee.txt
16141e7 HEAD@{1}: commit: add lee.txt
3579560 HEAD@{2}: commit: update v3
dc9b45f HEAD@{3}: commit: update v2
6a14bb5 HEAD@{4}: commit: update v1
74625b0 HEAD@{5}: commit (initial): add README.md
#版本回退到删除之前
[root@CICD-node1 timinglee]# git reset --hard 16141e7
HEAD 现在位于 16141e7 add lee.txt
[root@CICD-node1 timinglee]# ls
dir1 lee.txt README.md
4.2 git对于文件如何忽略
在做软件开发时对源码编译会产生一些临时文件,我们在提交时需要忽略这些临时文件
bash
[root@CICD-node1 timinglee]# mkdir dir1/
[root@CICD-node1 timinglee]# touch dir1/.file2
[root@CICD-node1 timinglee]# git status -s
?? .file1
?? dir1/
[root@CICD-node1 timinglee]# echo .file1 > .gitignore
[root@CICD-node1 timinglee]# git status -s
?? .gitignore
?? dir1/
[root@CICD-node1 timinglee]# echo ".*" > .gitignore
[root@CICD-node1 timinglee]# git status -s
五.gitlab代码仓库
5.1 gitlab简介
GitLab 是一个用于仓库管理系统的开源项目,使用 Git 作为代码管理工具,并在此基础上搭建起来
的 web 服务。
GitLab 具有很多功能,比如代码托管、持续集成和持续部署(CI/CD)、问题跟踪、合并请求管理
等。它可以帮助开发团队更好地协作开发软件项目,提高开发效率和代码质量。
官网:https://about.gitlab.com/install/
中文站点: https://gitlab.cn/install/
官方包地址:https://packages.gitlab.com/gitlab/gitlab-ce
5.2 gitlab 的部署实施
5.2.1 部署gitlab
部署gitlab需要内存大于4G
bash
#在安装包之前需配置好软件仓库来解决依赖性
[root@CICD-node1 ~]# dnf install -y curl policycoreutils-python-utils opensshserver perl
[root@CICD-node1 ~]# dnf install gitlab-ce-17.1.6-ce.0.el9.x86_64.rpm -y

5.2.2 配置gitlab
bash
#修改配置文件
[root@CICD-node1 ~]# cd /etc/gitlab/
[root@CICD-node1 gitlab]# ls
gitlab.rb
[root@CICD-node1 gitlab]# vim gitlab.rb
32 external_url 'http://172.25.254.80'
#修改配置文件后需利用gitlab-crt来生效,
[root@CICD-node1 gitlab]# gitlab-ctl reconfigure
#执行命令成功后会把所有组件全部启动起来
5.2.3 登陆gitlab
用户名默认为 root 。如果在安装过程中指定了初始密码,则用初始密码登录,如果未指定密码,则系统会随机生成一个密码并存储在 /etc/gitlab/initial_root_password 文件中, 查看随机密码并使用 root 用户名登录。
注意:出于安全原因,24 小时后,/etc/gitlab/initial_root_password 会被第一次 gitlab-ctl reconfigure 自动删除,因此若使用随机密码登录,建议安装成功初始登录成功之后,立即修改初始密码。
bash
#查看原始密码
[root@CICD-node1 gitlab]# cat /etc/gitlab/initial_root_password
# WARNING: This value is valid only in the following conditions
# 1. If provided manually (either via `GITLAB_ROOT_PASSWORD` environment variable or via `gitlab_rails['initial_root_password']` setting in `gitlab.rb`, it was provided before database was seeded for the first time (usually, the first reconfigure run).
# 2. Password hasn't been changed manually, either via UI or via command line.
#
# If the password shown here doesn't work, you must reset the admin password following https://docs.gitlab.com/ee/security/reset_user_password.html#reset-your-root-password.
Password: jN9lq6NSP8a2V+4n57djzWlEGP7RZ43DSIse8sXJGTQ= #密码
# NOTE: This file will be automatically deleted in the first reconfigure run after 24 hours.
登陆

设置语言


设置密码


5.3 在gitlab中新建项目




#生成sshd密钥
root@CICD-node1 \~\]# ssh-keygen