安装kubernetes

环境

主机名 IP 角色
harbor 172.25.254.200 私有仓库
k8s-master.org 172.25.254.100 主节点
k8s-node1.org 172.25.254.11 从节点1
k8s-node2.org 172.25.254.22 从节点2

部署

同步解析

操作对象:全体主机

vim /etc/hosts
...
172.25.254.100 k8s-master.org
172.25.254.200 ooovooo.org
172.25.254.200 harbor
172.25.254.11 k8s-node1.org
172.25.254.22 k8s-node2.org

在这里插入图片描述

docker

# 均安装同一版本的Docker
vim /etc/yum.repos.d/docker.repo
[Docker]
name=Docker
baseurl=https://mirrors.aliyun.com/docker-ce/linux/rhel/9/x86_64/stable/
gpgcheck=0
enabled=1
---
yum install docker-ce -y
# 除harbor外,均设定iptables网络
vim /usr/lib/systemd/system/docker.service
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock --iptables=true
---
# 均开启服务
systemctl enable --now docker.service
# 建立harbor认证目录(certs.d+harbor域名)
mkdir /etc/docker/certs.d/ooovooo.org/ -p
# 查看版本
docker info | grep Version

在这里插入图片描述
image-20250824172810520
image-20250824172956484

harbor

下载地址:https://github.com/goharbor/harbor/releases

下载harbor
# 下载
wget https://github.com/goharbor/harbor/releases/download/v2.5.4/harbor-offline-installer-v2.5.4.tgz
# 安装harbor
tar zxf harbor-offline-installer-v2.5.4.tgz
mv harbor /root/harbor/
cd /root/harbor/
# 生成认证
mkdir /data/docker/certs/ -p
openssl req -newkey rsa:4096 -addext "subjectAltName = DNS:ooovooo.org" -x509 -days 365 -out /data/docker/certs/ovo.org.crt -nodes -sha256 -keyout /data/docker/certs/ono.org.key
CN
Shanxi
Xi'an
ovo
kubernetes
ooovooo.org
admin@moon.org

在这里插入图片描述

安装及传递认证

/data/docker/certs/ — harbor仓库存放证书目录
/etc/docker/certs.d/ooovooo.org/ — 登录harbor仓库时,docker默认检验证书目录
/etc/docker/certs.d/ooovooo.org/路径下没有证书,在登录harbor时,会出现certificate signed by unknown authority错误

# 生成仓库所需认证(名字与路径均不能改变)
cp /data/docker/certs/ovo.org.crt /etc/docker/certs.d/ooovooo.org/ca.crt
# 修改认证路径
cp harbor.yml.tmpl harbor.yml
vim harbor.yml
5 hostname: ooovooo.org		# 与subjectAltName = DNS:ooovooo.org保持一致
17   certificate: /data/docker/certs/ovo.org.crt
18   private_key: /data/docker/certs/ono.org.key
34 harbor_admin_password: aaa	# 账户:admin	密码:aaa
---
# 开始安装
cd /root/harbor/
./install.sh --with-chartmuseum
# 若报错,请检查文件路径下是否有crt和key文件,请检查hostname是否保持一致

image-20250824174705222
image-20250824174838333

# 浏览器登录harbor
172.25.254.200
# 创建项目(均公开)
1.kubernetes
2.flannel
# 传递ca文件
cd /etc/docker/certs.d/ooovooo.org/
scp ca.crt root@172.25.254.100:/etc/docker/certs.d/ooovooo.org/
scp ca.crt root@172.25.254.11:/etc/docker/certs.d/ooovooo.org/
scp ca.crt root@172.25.254.22:/etc/docker/certs.d/ooovooo.org/
# 验证(kubernetes集群)
ls /etc/docker/certs.d/ooovooo.org/
# kubernetes集群登录harbor仓库
docker logout
docker login ooovooo.org -u admin
---
# harbor启停
cd /root/harbor/
docker compose stop		# 停止(不删除容器和网络)
docker compose up -d	# 后台模式运行
docker compose down		# 停止并删除容器和网络(保留卷和镜像)

在这里插入图片描述
在这里插入图片描述

# 开机自启harbor(systemd方式管理)
vim /etc/systemd/system/harbor.service
[Unit]
Description=Harbor Container Registry
After=docker.service
Requires=docker.service
[Service]
Type=oneshot
RemainAfterExit=yes
WorkingDirectory=/root/harbor/
ExecStart=/usr/bin/docker compose up -d
ExecStop=/usr/bin/docker compose down
ExecReload=/usr/bin/docker compose down && /usr/bin/docker compose up -d
[Install]
WantedBy=multi-user.target
# 重新加载 systemd 配置
systemctl daemon-reload
# 设置开机自启
systemctl enable harbor
# 验证
systemctl stop harbor.service
systemctl status harbor.service
docker ps -a
systemctl start harbor.service
docker ps -a

在这里插入图片描述

kubernetes

操作对象:kubernetes集群

前置工作
# 禁用swap
# 查看swap状态
swapon -s	
vim /etc/fstab
# /dev/mapper/rl-swap	...
---
# 临时关闭
swapoff -a
# 重载配置
systemctl daemon-reload
# 彻底禁用
systemctl mask swap.target
# 验证
swapon -a

在这里插入图片描述

# 修改资源管理模式
vim /etc/docker/daemon.json
{
    "registry-mirrors": ["https://ooovooo.org"],
    # "exec-opts": ["native.cgroupdriver=systemd"],
    # "log-driver": "json-file",
    # "log-opts":
    # {
    #     "max-size": "100m"
    # },
    #     "storage-driver": "overlay2"
}
# 注释内容,仅在主机为rhel8.x版本中做;若其他版本,请删除 , 符号
# 激活内核网络
# 开机自动激活
echo br_netfilter > /etc/modules-load.d/docker_mod.conf
# 加载模块
modprobe br_netfilter
# 优化内核文件
echo net.ipv4.ip_forward=1 >> /etc/sysctl.d/kubernetes.conf
# 若主机版本为8.x,需要额外执行下面两条
net.bridge.bridge-nf-call-ip6tables=1 >> /etc/sysctl.d/kubernetes.conf
net.bridge.bridge-nf-call-iptables=1 >> /etc/sysctl.d/kubernetes.conf
# 验证
sysctl -p /etc/sysctl.d/kubernetes.conf
# 重启docker
systemctl restart docker

在这里插入图片描述

安装kubernetes管理工具
vim /etc/yum.repos.d/kubernetes.repo
[Kubernetes]
name=Kubernetes
baseurl=https://mirrors.aliyun.com/kubernetes-new/core/stable/v1.30/rpm/
gpgcheck=0
enabled=1
---
dnf install kubelet kubeadm kubectl -y

在这里插入图片描述

设置kubectl命令补全
# 安装bash扩展
dnf install bash-completion -y
# 添加环境变量
echo "source <(kubectl completion bash)" >> ~/.bashrc
# 重新加载
source ~/.bashrc
# 验证
ku
# 尝试命令补齐
ku --> kube --> kubec --> kubectlc
安装cri-docker插件

操作对象:kubetnetes集群主机

kubetnetes从1.24版本开始移除dockershim

高版本需要安装cri-docker插件,才能使用docker

cri-docker下载:https://github.com/Mirantis/cri-dockerd/releases

# 获取软件包
wget https://github.com/Mirantis/cri-dockerd/releases/download/v0.3.14/cri-dockerd-0.3.14-3.el7.x86_64.rpm
# 传递安装包
scp /packags/libcgroup-0.41-19.el8.x86_64.rpm root@172.25.254.11:/packags/
scp /packags/libcgroup-0.41-19.el8.x86_64.rpm root@172.25.254.22:/packags/
---
scp /packags/cri-dockerd-0.3.16-3.fc35.x86_64.rpm root@172.25.254.11:/packags/
scp /packags/cri-dockerd-0.3.16-3.fc35.x86_64.rpm root@172.25.254.22:/packags/
# 均进行安装
dnf install /packags/libcgroup-0.41-19.el8.x86_64.rpm -y
dnf install /packags/cri-dockerd-0.3.16-3.fc35.x86_64.rpm -y
# 下载根容器(仅需一台主机下载、打标签、上传)
docker pull registry.aliyuncs.com/google_containers/pause:3.9
# 验证
docker images | grep pause
# 打标签
docker tag registry.aliyuncs.com/google_containers/pause:3.9 ooovooo.org/kubernetes/pause:3.9
# 上传根容器
docker push ooovooo.org/kubernetes/pause:3.9
# 指定网络插件名称及根容器(与harbor仓库路径对应!!!)
vim /lib/systemd/system/cri-docker.service
ExecStart=/usr/bin/cri-dockerd --container-runtime-endpoint fd:// --network-plugin=cni --pod-infra-container-image=ooovooo.org/kubernetes/pause:3.9
# 开启服务
systemctl enable --now cri-docker
systemctl enable --now kubelet.service

cir-docker套接字路径:/var/run/cri-dockerd.sock
在这里插入图片描述

安装flannel插件

操作对象:k8s-master

flannel+yml文件下载地址:https://github.com/flannel-io/flannel/releases

flannel-cni-plugin下载地址:https://docker.aityp.com/r/docker.io/flannel/flannel-cni-plugin

# 拉取镜像
docker pull swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/flannel/flannel-cni-plugin:v1.5.1-flannel1
# 打标签
docker tag swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/flannel/flannel-cni-plugin:v1.5.1-flannel1 ooovooo.org/flannel/flannel-cni-plugin:v1.5.1-flannel1
# 上传镜像
docker push ooovooo.org/flannel/flannel-cni-plugin:v1.5.1-flannel1
---
# 获取安装包
wget https://github.com/flannel-io/flannel/releases/download/v0.25.5/flanneld-v0.25.5-amd64.docker
wget https://github.com/flannel-io/flannel/releases/download/v0.25.5/kube-flannel.yml
# 加载镜像
docker load -i flanneld-v0.25.5-amd64.docker
# 打标签
docker tag quay.io/coreos/flannel:v0.25.5-amd64 ooovooo.org/flannel/flannel:v0.25.5
# 上传镜像
docker push ooovooo.org/flannel/flannel:v0.25.5
# 配置yml文件
vim kube-flannel.yml
146	image: ooovooo.org/flannel/flannel:v0.25.5
173	image: ooovooo.org/flannel/flannel-cni-plugin:v1.5.1-flannel1
184	image: ooovooo.org/flannel/flannel:v0.25.5
# 验证
sed -n '146p;173p;184p' kube-flannel.yml
# 记录下此yml文件的路径,初始化完成后,需要加载

在这里插入图片描述

拉取kubernetes镜像

操作对象:k8s-master

# 拉取kubernetes集群所需镜像
kubeadm config images pull --image-repository registry.aliyuncs.com/google_containers --kubernetes-version v1.30.0 --cri-socket=unix:///var/run/cri-dockerd.sock
# 打标签
docker images | awk '/google/{ print $1":"$2}' | awk -F "/" '{system("docker tag "$0" ooovooo.org/kubernetes/"$3)}'
# 上传镜像
docker images | awk '/ooovooo.org.*kubernetes/{system("docker push "$1":"$2)}'
# 重启服务
systemctl daemon-reload
systemctl restart cri-docker
主节点初始化

主节点初始化时,内存不能低于1700MB

# 初始化命令
kubeadm init --pod-network-cidr=10.244.0.0/16 --image-repository ooovooo.org/kubernetes --kubernetes-version v1.30.0 --cri-socket=unix:///var/run/cri-dockerd.sock
# 初始化成功标志
Your Kubernetes control-plane has initialized successfully!
# 记录下从节点加入集群命令(记不住也没事)
kubeadm join 172.25.254.100:6443 --token bhbjzu.cl927t8ag2ff2qqi --discovery-token-ca-cert-hash sha256:3891fb5626c6f00b9e6cf9603799f2e64db625a95298c9ce497943e8cd3fe952
# 若初始化失败,请检查
1.swap是否禁用
2.kubelet服务是否启动
3.cri-docker.service,根容器路径是否正确
4.flannel插件是否启用,镜像拉取路径是否正确
# 若失败后,重新初始化,需要先执行命令
kubeadm reset --cri-socket=unix:///var/run/cri-dockerd.sock
---
# 初始化成功后
# 指定主配置文件
echo "export KUBECONFIG=/etc/kubernetes/admin.conf" >> ~/.bash_profile
# 重载配置
source ~/.bash_profile
# 获取主节点状态
kubectl get nodes
# 应用yml文件,当有新加入主机时,会自动加载此插件
kubectl apply -f kube-flannel.yml
# 等待一会后,若为ready即为成功初始化

在这里插入图片描述

在这里插入图片描述

从节点初始化
# 若不记得token
# 主节点重新生成token
kubeadm token create --print-join-command
# 从节点初始化(加上cri-docker的sock路径)
kubeadm join 172.25.254.100:6443 --token bhbjzu.cl927t8ag2ff2qqi --discovery-token-ca-cert-hash sha256:3891fb5626c6f00b9e6cf9603799f2e64db625a95298c9ce497943e8cd3fe952 --cri-socket=unix:///var/run/cri-dockerd.sock
# 初始化成功标志
Run 'kubectl get nodes' on the control-plane to see this node join the cluster.
---
# 若初始化失败,重新初始化,执行命令
# 主节点(剔除失败主机)
kubectl delete node k8s-node1.org
# 从节点
kubeadm reset --cri-socket=unix:///var/run/cri-dockerd.sock
---
# 查看所有节点状态(均处于ready状态即成功)
kubectl get nodes

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

测试

# harbor登录harbor仓库
docker logout
docker login ooovooo.org -u admin
# 加载镜像
docker load -i nginx-latest.tar.gz
docker tag nginx:latest ooovooo.org/library/nginx:latest
docker push ooovooo.org/library/nginx:latest
# master
docker run nginx --image nginx
# 查看Pod状态
kubectl get pods -o wide
# 在k8s-node1查看镜像状态
docker ps
# 删除pod
kubectl delete pod test

在这里插入图片描述

Pod管理

在kubernetes中,一切皆资源

用户通过操作资源来使用kubernetes

kubernetes中最小管理单元是Pod,容器只能放在Pod中

用户通过Pod控制器来管理Pod

Pod中数据持久化是由kubernetes的存储系统实现的

Pod中的服务访问是由kubernetes的service资源实现的

资源管理

命令式对象管理:直接使用命令去操作kubernetes资源
命令式对象配置:通过命令配置和配置文件去操作kubernetes资源
声明式对象管理:通过apply命令应用配置文件去操作kubernetes资源

类型 适用环境 优点 缺点
命令式对象管理 测试 简单 只能操作活动对象,无法审计、跟踪
命令式对象配置 开发 可以审计、跟踪 项目大时,配置文件多,操作麻烦
声明式对象配置 开发 支持目录操作 意外情况下难以调试

命令式对象管理

kubectl是kubernetes集群的命令行工具,能通过它对集群本身进行管理
语法:kubectl [command] [type] [name] [flags]
comand:对资源执行的操作,create\delete\get
type:明确资源的类型,pod\deployment\service
name:资源创建时的名称(大小写敏感)
flags:额外的可选参数

资源类型

# 列出集群可用的资源
kubectl api-resources
资源分类 资源名称 缩写 资源作用
集群级别资源 nodes no 集群组成部分
namespaces ns 隔离 Pod
pod 资源 pods po 装载容器
pod 资源控制器 replicationcontrollers rc 控制 pod 资源
replicasets rs 控制 pod 资源
deployments deploy 控制 pod 资源
daemonsets ds 控制 pod 资源
jobs 控制 pod 资源
cronjobs cj 控制 pod 资源
horizontalpodautoscalers hpa 控制 pod 资源
statefulsets sts 控制 pod 资源
服务发现资源 services svc 统一 pod 对外接口
ingress ing 统一 pod 对外接口
存储资源 volumeattachments 存储
persistentvolumes pv 存储
persistentvolumeclaims pvc 存储
配置资源 configmaps cm 配置
secrets 配置

常见命令

命令分类 命令 翻译 命令作用
基本命令 create 创建 创建一个资源
edit 编辑 编辑一个资源
get 获取 获取一个资源
patch 更新 更新一个资源
delete 删除 删除一个资源
explain 解释 展示资源文档
run 运行 在集群中运行一个指定的镜像
expose 暴露 暴露资源为 Service
describe 描述 显示资源内部信息
运行和调试 logs 日志 输出容器在 pod 中的日志
attach 缠绕 进入运行中的容器
exec 执行 执行容器中的一个命令
cp 复制 在 Pod 内外复制文件
rollout 首次展示 管理资源的发布
scale 规模 扩(缩)容 Pod 的数量
autoscale 自动调整 自动调整 Pod 的数量
高级命令 apply rc 通过文件对资源进行配置
label 标签 更新资源上的标签
其他命令 cluster-info 集群信息 显示集群信息
version 版本 显示当前 Server 和 Client 的版本
基础命令
# 查看集群版本
kubectl version
# 显示集群信息
kubectl cluster-info
# 查看资源帮助
kubectl explain deployment
# 查看资源参数帮助
kubectl explain deployment.spec
# 运行时编辑资源配置
kubectl edit deployments.apps moon
# 删除资源
kubectl delete deployments.apps web
# 运行pod
kubectl run mypod --image nginx
# 查看pod状态
kubectl get pods
# 端口暴漏
kubectl expose pod mypod --port 80 --target-port 80
# 查看服务状态
kubectl get services
# 查看资源详细信息
kubectl describe pods mypod
# 查看资源具体日志
kubectl logs pods/mypod
# 运行交互式pod
kubectl run -it moon --image busybox
# 与docker类似,同时按ctrl+p+q退出但不停止pod
# 运行非交互式pod
kubectl run nginx --image nginx
# 进入可交互式容器内
kubectl attach pods/moon  -it
# 在容器外部执行命令
kubectl exec -it pods/nginx /bin/bash
# 文件写入容器内
kubectl cp anaconda-ks.cfg nginx:/
# 容器文件写入主机
kubectl cp nginx:/anaconda-ks.cfg anaconda-ks.cfg
高级命令
# 命令生成yaml模板文件
kubectl create deployment --image nginx webcluster --dry-run=client -o yaml > webcluster.yaml
# 通过模板制作自定义配置文件
vim webcluster.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  creationTimestamp: null
  labels:
    app: webcluster
  name: webcluster
spec:
  replicas: 4
  selector:
    matchLabels:
      app: webcluster
  template:
    metadata:
      labels:
        app: webcluster
    spec:
      containers:
      - image: nginx
        name: nginx
# 声明式对象配置
kubectl apply -f webcluster.yml
# 获取状态
kubectl get deployments.apps
# 查看运行pod状态
kubectl get pods
# 删除方式一(推荐)
kubectl delete -f webcluster.yml
# 删除方式二(根据资源类型及名字删除)
kubectl delete deployment webcluster

在这里插入图片描述

# 管理资源标签
kubectl run nginx --image nginx
# 查看资源标签
kubectl get pods --show-labels
# 添加标签
kubectl label pods nginx app=lee
# 验证
kubectl get pods --show-labels
# 修改标签(--overwrite)
kubectl label pods nginx app=webcluster --overwrite
# 删除标签(-)
kubectl label pods nginx app-

自主式Pod

优点:
灵活性高、学习和调试方便、适用于特殊场景(快速验证)
缺点:
管理复杂、缺乏高级功能、可维护性差

# 查看pod
kubectl get pods
# 创建Pod
kubectl run nginx --image nginx
# 查看状态
kubectl get pods
# 查看更详细信息(IP、运行节点)
kubectl get pods -o wide

控制器管理Pod

具有高可用性和可靠性、可扩展性、版本管理和更新、声明式配置、服务发现和负载均衡、多环境一致性等优点

# 建立控制器及pod
kubectl create deployment nginxs --image nginx
# 为控制器扩容(scale\--replicas)
kubectl scale deployment nginxs --replicas 6
# 验证
kubectl get pods
# 为控制器缩容
kubectl scal deployment nginxs --replicas 2
# 验证
kubectl get pods
# 删除
kubectl delete deployment nginxs

更新pod版本

# 控制器建立pod
kubectl create  deployment nginxs --image myapp:v1 --replicas 2
# 暴露端口
kubectl expose deployment nginxs --port 80 --target-port 80
# 查看控制器信息
kubectl get services
# 验证
curl 10.110.195.120
# 查看控制器版本
kubectl rollout history deployment nginxs
# 更新控制器pod
kubectl set image deployments/nginxs myapp=myapp:v2
# 验证
kubectl rollout history deployment nginxs
curl 10.110.195.120
# 版本回滚
kubectl rollout undo deployment nginxs --to-revision 1
# 验证
curl 10.110.195.120
# 删除
kubectl delete deployment nginxs

利用yaml文件部署

具有声明式配置、灵活性和可扩展性、与工具集成等优点

简单演示

单个pod内运行多个容器
# 命令生成pod模板
kubectl run moon --image myapp:v1 --dry-run=client -o yaml > moon.yml
vim moon.yml
apiVersion: v1
kind: Pod
metadata:
  labels:
    run: moon
  name: moon
spec:
  containers:
    - image: nginx:latest
      name: web1
    - image: nginx:latest
      name: web2
# 应用
kubectl apply -f moon.yml
# 获取状态
kubectl get pods
# 查看某个状态(排错)
kubectl logs moon web1
# 删除
kubectl delete -f moon.yml

需要注意的是,单个pod开启多个容器,容器之间会资源共享,使用相同的资源(如端口)会产生干扰,如上例

同个pod内的容器共用一个网络
# 修改
vim moon.yml
apiVersion: v1
kind: Pod
metadata:
  labels:
    run: moon
  name: moon
spec:
  containers:
    - image: myapp:v1
      name: myapp1
    - image: busyboxplus:latest
      name: busyboxplus
      command: ["/bin/sh","-c","sleep 1000000"]
# 应用
kubectl apply -f moon.yml
# 获取状态
kubectl get pods
# 验证(在没有web服务的容器内访问localhost)
kubectl exec test -c busyboxplus -- curl -s localhost
# 删除
kubectl delete -f moon.yml

高级应用

端口映射
# 修改
vim moon.yml
apiVersion: v1
kind: Pod
metadata:
  labels:
    run: moon
  name: moon
spec:
  containers:
    - image: myapp:v1
      name: myapp1
      ports:
      - name: http
        containerPort: 80
        hostPort: 80
        protocol: TCP
# 应用
kubectl apply -f moon.yml
# 查看状态
kubectl get pods -o wide
# 验证
curl k8s-node1.org
# 删除
kubectl delete -f moon.yml
设定容器环境变量
# 修改
vim moon.yml
apiVersion: v1
kind: Pod
metadata:
  labels:
    run: moon
  name: moon
spec:
  containers:
    - image: busybox:latest
      name: busybox
      command: ["/bin/sh","-c","echo $NAME;sleep 3000000"]
      env:
      - name: NAME
        value: sun
# 应用
kubectl apply -f moon.yml
# 验证
kubectl logs pods/moon busybox
# 删除
kubectl delete -f moon.yml
pod资源限制

资源限制会影响pod的Qos Class资源优先级(QoS即服务质量)
资源优先级分为Guaranteed > Burstable > BestEffort

资源设定 优先级类型
资源限定未设定 BestEffort
资源限定设定且最大和最小不一致 Burstable
资源限定设定且最大和最小一致 Guaranteed
# 修改
vim moon.yml
apiVersion: v1
kind: Pod
metadata:
  labels:
    run: moon
  name: moon
spec:
  containers:
    - image: myapp:v1
      name: myapp
      resources:
        limits:
          cpu: 500m
          memory: 100M
        requests:
          cpu: 500m
          memory: 100M
# 应用
kubectl apply -f moon.yml
# 查看
kubectl get pods
# 验证
kubectl describe pods moon
容器启动管理
# 修改
vim moon.yml
apiVersion: v1
kind: Pod
metadata:
  labels:
    run: timinglee
  name: test
spec:
  restartPolicy: Always
  containers:
    - image: myapp:v1
      name: myapp
# 应用
kubectl apply -f moon.yml
# 查看
kubectl get pods -o wide
# 验证
kubectl delete pods moon
kubectl get pods -o wide
运行节点
# 修改
vim moon.yml
apiVersion: v1
kind: Pod
metadata:
  labels:
    run: moon
  name: moon
spec:
  nodeSelector:
    kubernetes.io/hostname: k8s-node1
  restartPolicy: Always
  containers:
    - image: myapp:v1
      name: myapp
# 应用
kubectl apply -f moon.yml
#验证
kubectl get pods -o wide
共享宿主机网络
# 修改
vim moon.yml
apiVersion: v1
kind: Pod
metadata:
  labels:
    run: timinglee
  name: test
spec:
  hostNetwork: true
  restartPolicy: Always
  containers:
    - image: busybox:latest
      name: busybox
      command: ["/bin/sh","-c","sleep 100000"]
# 应用
kubectl apply -f moon.yml
# 验证
kubectl exec -it pods/moon -c busybox -- /bin/sh
ifconfig
eth0:......

Pod的生命周期

官方文档:https://kubernetes.io/zh/docs/concepts/workloads/

init容器

Pod可以有一个或多个先于应用容器启动的Init容器

init容器与其他容器的区别

  • 总是运行到完成
  • 必须在Pod就绪前运行完成,上一个运行完成,下一个才能开始运行

如果init容器运行失败,kubernetes会不断重启Pod,直到init容器运行成功为止

如果Pod的restartPolicy值为Nerver,则它不会重新启动

适用于:等待其他服务或资源准备就绪后,再启动当前应用,确保应用启动时所需的依赖已经满足

# 监控Pod状态
watch -n1 kubectl get pods -o wide
# 生成生成Pod
vim init-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  labels:
    run: ovo
  name: ovo
spec:
  containers:
  - image: myapp:v1
    name: ovo
  # init 容器
  initContainers:
  - name: init-myservice
    image: busybox
    command: ["sh","-c","until test -e /testfile;do echo wating for myservice; sleep 2;done"]
# 应用yaml文件
kubectl apply -f init-pod.yaml
# 验证
kubectl logs pods/ovo init-myservice
# Pod一直处于init:0/1状态,应用容器无法启动
# 生成检测文件
kubectl exec pods/init-pod -c init-myservice -- /bin/sh -c "touch /testfile"
kubectl get pods -o wide
# init运行完毕,应用容器允许运行

image-20250813103221174
image-20250813103302182
image-20250813103344007

探针

探针由kubelet对容器执行的定期诊断

  • ExecAction:在容器内执行指定命令;如果命令退出时返回码为0则认为诊断成功
  • TCPSocketAction:对指定端口上的容器的IP地址进行TCP检查;如果端口打开,则诊断被认为是成功的
  • HTTPGetAction:对指定的端口和路径上的容器的IP地址执行HTTP Get请求;如果响应的状态码大于等于200且小于400,则诊断被认为是成功的

每次探测的结果只有三种

  • 成功:容器通过了诊断
  • 失败:容器未通过诊断
  • 未知:诊断失败,因此不会采取任何行动

三种探针

kubelte根据探测结果自动执行相应操作

livenessProbe(存活探针):探测容器是否正在运行;如果存活探测失败,则kubelet会杀死容器,并且容器将受到其重启策略的影响;如果容器不提供存活探针,则默认状态为Success

readinessProbe(就绪探针):探测容器是否准备好服务请求。如果就绪探测失败,端点控制器将从与Pod匹配的所有 Service的端点中删除该Pod的IP地址;初始延迟之前的就绪状态默认为 Failure;如果容器不提供就绪探针,则默认状态为Success

startupProbe(启动探针):探测容器中的应用是否已经启动。如果提供启动探测(startup probe),则禁用所有其他探测,直到它成功为止。如果启动探测失败,kubelet将杀死容器,容器服从其重启策略进行重启。如果容器没有提供启动探测,则默认状态为成功Success

readinessProbe与livenessProbe的区别
readinessProbe:失败后,将ip:port从对应的endpoint列表中删除
livenessProbe:失败后,将杀死容器并根据pod的重启策略选择是否重启容器


三种探针同时存在时,先执行startupProbe探针,其他探针被展示禁用,直到Pod通过startupProbe检测,在容器启动后,进行检测,如果不满足规则,则重启容器

容器只需通过一次startupProbe探针检测,后续将不再进行startupProbe检测

而其他两种探针,会定期检测,直到容器消亡才停止探测

存活探针

vim li-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  labels:
    run: ovo
  name: ovo
spec:
  containers:
  - image: myapp:v1
    name: ovo
    livenessProbe:
      tcpSocket:
        port: 80
      initialDelaySeconds: 3
      periodSeconds: 1
      timeoutSeconds: 1
# 应用yaml文件
kubectl apply -f li-pod.yaml
# 查看pod状态,注意restart次数(0)
kubectl get pods -o wide
# 停止nginx运行,即关闭80端口
kubectl exec -it ovo -- /bin/sh
nginx -s stop
# 等待一段时间后,容器restart次数加1
kubectl get pods -o wide
# 显示80连接拒绝,探测失败,杀死容器,重启容器	
kubectl describe pods
8080: connect: connection refused

image-20250813110324218
image-20250813110400540
image-20250813110451410
image-20250813110635335

就绪探针

# 创建
vim pod.yml
apiVersion: v1
kind: Pod
metadata:
  labels:
    name: readiness
  name: readiness
spec:
  containers:
    - image: myapp:v1
      name: myapp
      readinessProbe:
        httpGet:
          path: /test.html
          port: 80
        initialDelaySeconds: 1
        periodSeconds: 3
        timeoutSeconds: 1
# 暴露端口
kubectl expose pod readiness --port 80 --target-port 80
# 查看
kubectl get pods
# 验证
kubectl describe pods readiness
kubectl describe services readiness
# 测试页
kubectl exec pods/readiness -c myapp -- /bin/sh -c "echo test > /usr/share/nginx/html/test.html"
# 查看
kubectl get pods
# kubectl describe services readiness

启动探针

在这里插入代码片

控制器

官网文档:https://v1-30.docs.kubernetes.io/zh-cn/docs/concepts/workloads/controllers/
自主pod:pod退出或意外关闭后不会被重新创建
控制器管理的pod:在控制器的生命周期内,始终要维持Pod的副本数目

类型

控制器名称 介绍
Replication Controller 比较原始的Pod控制器,已经被废弃,由ReplicaSet替代
ReplicaSet ReplicaSet 确保任何时间都有指定数量的 Pod 副本在运行(副本数量)
Deployment 一个 Deployment 为 Pod 和 ReplicaSet 提供声明式的更新能力(版本更新)
DaemonSet DaemonSet 确保全指定节点上运行一个 Pod 的副本
StatefulSet StatefulSet 是用来管理有状态应用的工作负载 API 对象
Job 执行批处理任务,仅执行一次任务,保证任务的一个或多个Pod成功结束
CronJob Cron Job 创建基于时间调度的 Jobs
HPA(Horizontal Pod Autoscaler) 根据资源利用率自动调整service中Pod数量,实现Pod水平自动缩放(自动扩容)

ReplicaSet控制器

ReplicaSet 是下一代的 Replication Controller,官方推荐使用ReplicaSet

# 生成yml文件
kubectl create deployment replicaset --image myapp:v1 --dry-run=client -o yaml > replicaset.yml
# 配置
vim replicaset.yml
apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: replicaset
spec:
  replicas: 2
  selector:
    matchLabels:	
      app: myapp
  template:	
    metadata:
      labels:
        app: myapp
    spec:
      containers:
      - image: myapp:v1
        name: myapp
# 应用
kubectl apply -f replicaset.yml
# 查看状态
kubectl get pods --show-labels
# ReplicaSet通过标签检测pod的数量
kubectl label pod xxx app=moon --overwrite
kubectl get pods --show-labels
# 恢复标签
kubectl label pod xxx app-
kubectl get pod --show-labels
# 删除并自动维持
kubectl delete pods xxx
# 查看
kubectl get pods --show-labels
# 回收
kubectl delete -f rs-example.yml

Deployment控制器

Deployment控制器并不直接管理pod,而是通过管理ReplicaSet来间接管理Pod

# 生成yml文件
kubectl create deployment deployment --image myapp:v1  --dry-run=client -o yaml > deployment.yml
# 配置
vim deployment.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: deployment
spec:
  replicas: 4
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
    spec:
      containers:
      - image: myapp:v1
        name: myapp
# 应用
kubectl apply -f deployment.yml
# 查看
kubectl get pods --show-labels
# 回收
kubectl delete -f deployment.yml
版本迭代
# 查看
kubectl get pods -o wide
# 设置容器版本v1
curl 10.244.2.14
# 查看
kubectl describe deployments.apps deployment
# 配置容器版本v2
vim deployment.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: deployment
spec:
  replicas: 4
  selector:
    matchLabels:
      app: myapp

  template:
    metadata:
      labels:
        app: myapp
    spec:
      containers:
      - image: myapp:v2
        name: myapp
# 应用
kubectl apply -f deployment-example.yaml
# 查看更新过程
kubectl get pods -w
# 验证
kubectl get pods -o wide
curl 10.244.2.14
# 回收
kubectl delete -f deployment-example.yaml

本质是新建一个RS,此RS将pod重建,然后把老版本RS回收

版本回滚
# 配置
vim deployment.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: deployment
spec:
  replicas: 4
  selector:
    matchLabels:
      app: myapp

  template:
    metadata:
      labels:
        app: myapp
    spec:
      containers:
      - image: myapp:v1
        name: myapp
# 应用
kubectl apply -f deployment.yml
# 查看
kubectl get pods -o wide
# 验证
curl  10.244.2.26
暂停及恢复

将改动做完后,执行一次更新,避免不必要的线上更新

# 暂停
kubectl rollout pause deployment deployment-example
# 配置
vim deployment-example.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: deployment-example
spec:
  minReadySeconds: 5
  strategy:
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0
  replicas: 6				
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
    spec:
      containers:
      - name: myapp
        image: nginx
        resources:
          limits:
            cpu: 0.5
            memory: 200Mi
          requests:
            cpu: 0.5
            memory: 200Mi
# 应用
kubectl apply -f deployment-example.yaml
# 修改和更新资源并没有触发更新
kubectl rollout history deployment deployment-example
# 取消暂停
kubectl rollout resume deployment deployment-example
# 查看更新历史
kubectl rollout history deployment deployment-example
# 回收
kubectl delete -f deployment-example.yaml

daemonset控制器

在每个节点都运行一个Pod,新增节点同样运行此Pod

随着节点的移除,Pod也会被回收

删除DaemonSet同样会回收Pod

典型用法:

运行集群存储 DaemonSet

运行日志收集 DaemonSet(ELK)

运行监控 DaemonSet(Prometheus)

# 生成文件
vim daem.yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
  labels:
    app: daem
  name: daem
spec:
  selector:
    matchLabels:
      app: daemonset
  template:
    metadata:
      labels:
        app: daemonset
    spec:
      containers:
      - image: myapp:v1
        name: myapp
# 加载yaml
kubectl daem.yaml
# 查看
kubectl get pods -o wide
# 每个节点都运行一个容器,但master不运行,因为master属于污点节点
# 修改yaml文件(设置对污点节点的容忍)
apiVersion: apps/v1
kind: DaemonSet
metadata:
  labels:
    app: daem
  name: daem
spec:
  tolerations:
  - effect: NoSchedule
  	operator: Exists
  selector:
    matchLabels:
      app: daemonset
  template:
    metadata:
      labels:
        app: daemonset
    spec:

        name: myapp
# 加载yaml
kubectl daem.yaml
# 查看
kubectl get pods -o wide
# 包括master节点也运行了
# 回收
kubectl delete -f daem.yaml

StatefulSet控制器

管理有状态的服务(如MySQL主从复制,依靠IP或主机名);但在Pod中,默认主机名和IP在故障后重启会改变

# 需要配合微服务中的 无头服务 进行实验
# 创建无头服务
kubectl create service clusterip web --clusterip="None" --dry-run=client -o yaml > service.yaml
vim service.yaml
apiVersion: v1
kind: Service
metadata:
  labels:
    app: web
  name: web
spec:
  clusterIP: None
  selector:
    app: web
  type: ClusterIP
# 创建statefulset
kubectl create deployment web --image myapp:v1 --replicas 2 --dry-run=client -o yaml > web.yaml
vim web.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
  labels:
    app: web
  name: web
spec:
  serviceName: web
  replicas: 2
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
      - image: myapp:v1
        name: myapp
# 应用yaml
kubectl apply -f web.yaml
kubectl apply -f service.yaml
# 查看解析
dig web.default.svc.cluster.local. @10.96.0.10
dig web-0.web.default.svc.cluster.local. @10.96.0.10
dig web-1.web.default.svc.cluster.local. @10.96.0.10
# 查看主机名和IP
watch -n 1 kubectl get pods -o wide
kubectl delete pods web-1
# 回收资源
kubectl delete -f web.yaml
kubectl delete -f service.yaml

image-20250816102621239
image-20250816102919243

job控制器

Job主要用于负责批量处理(一次要处理指定数量任务)短暂的一次性(每个任务仅运行一次就结束)任务

vim job.yaml
apiVersion: batch/v1
kind: Job
metadata:
  name: pi
spec:
  completions: 6
  parallelism: 2
  backoffLimit: 4
  template:
    spec:
      containers:
      - name: pi
        image: perl:5.34.0
        command: ["perl",  "-Mbignum=bpi", "-wle", "print bpi(2000)"]
      restartPolicy: Never
# 加载配置
kubectl apply -f job.yaml
# 查看日志
# 查看运行状态
kubectl get pods
# 选择运行完毕的
kubectl logs pi-6nkb2
# 显示圆周率后2000位
# 回收
kubectl delete jobs.batch pi
重启策略 功能
OnFailure 则job会在pod出现故障时重启容器,而不是创建pod,failed次数不变
Never job会在pod出现故障时创建新的pod,并且故障pod不会消失,也不会重启,failed次数加1
Always 一直重启,意味着job任务会重复去执行了

cronjob控制器

Cron Job 创建基于时间调度的 Jobs

CronJob可以在特定的时间点(反复的)去运行job任务

vim cronjob.yaml
apiVersion: batch/v1
kind: CronJob
metadata:
  name: hello
spec:
  schedule: "* * * * *"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: hello
            image: busybox
            imagePullPolicy: IfNotPresent
            command:
            - /bin/sh
            - -c
            - date; echo Hello from the Kubernetes cluster
          restartPolicy: OnFailure
# 加载yaml文件
kubectl apply -f cronjob.yaml
# 查看cronjob
kubectl get cronjobs.batch hello
# 查看运行的容器
kubectl get pods
# 查看执行情况
kubectl logs hello-29251140-44qsz hello
# 回收
kubectl delete -f cronjob.yaml

网络通信

整体架构

K8S通过CNI接口接入其他插件实现网络通讯,主流的插件有flannel,calico等

CNI插件存放位置:/etc/cni/net.d/

插件的解决方案

  • 虚拟网桥:虚拟网卡,多个容器共用一个虚拟网卡进行通信
  • 多路复用:MacVlan,多个容器共用一个物理网卡进行通信
  • 硬件交换:SR-LOV,一个物理网卡可以虚拟出多个接口,性能最好

Pod-Pod通信

  • Pod是Kubernetes的最小调度单元,依赖**容器网络接口(CNI)**插件实现

  • 每个Pod拥有独立的IP地址,且Pod间可直接通过IP通信(无需NAT)

  • CNI插件作用:为Pod分配IP,配置网络接口,设置路由或网络策略

    • 常见的插件有:Calico、Flannel、WeaveNet等
  • 同节点Pod通信:通过节点内部的CNI网桥转发,无需经过物理网络

  • 跨节点Pod通信:由CNI插件通过路由、隧道或BGP等技术实现,具体取决于插件的网络方案(如Flannel使用VXLAN,Calico支持BGP)

Pod-Service通信

  • 通过iptablesIPVS方式实现
  • iptables模式:通过设置iptables规则,将ClusterIP的请求转发到后端PodIP
  • IPVS模式:基于LVS实现,性能优于iptables,支持更多负载均衡算法

IPVS无法取代iptables,因为IPVS只能做负载均衡,实现不了NAT转换

集群外部-Pod通信

  • iptables的MASQUERADE,即动态IP环境下的源地址转换(SNAT)

集群外部-Service通信

  • NodePort、LoadBalancer、Ingress方式实现
  • NodePort:在每个节点上开放一个静态端口(NodePort),外部客户端通过节点IP:NodePort访问Service,再由Service转发到Pod
  • LoadBalancer:结合云服务商的负载均衡器,自动将外部流量通过负载均衡器转发到Service的NodePort
  • Ingress:通常以Pod形式运行在集群中,暴露给外部(通过NodePort或LoadBalancer)

flannel网络插件

flannel插件

插件 功能
VXLAN Virtual Extensible LAN(虚拟可扩展局域网),是Linux本身支持的一网种网络虚拟化技术
VXLAN可以完全在内核态实现封装和解封装工作,从而通过 “隧道” 机制,构建出覆盖网络
VTEP VXLAN Tunnel End Point(虚拟隧道端点),在Flannel中 VNI的默认值是 1
这也是为什么宿主机的VTEP设备都叫 flannel.1 的原因
Cni0 网桥设备,每创建一个 Pod 都会创建一对 veth pair
其中一端是 Pod 中的 eth0,另一端是 Cni0 网桥中的端口(网卡)
Flannel.1 TUN设备(虚拟网卡),用来进行 vxlan 报文的处理(封包和解包)
不同node之间的Pod数据流量都从overlay设备以隧道的形式发送到对端
Flanneld flannel在每个主机中运行flanneld作为agent,它会为所在主机从集群的网络地址空间中,获取一个小的网段subnet,本主机内所有容器的IP地址都将从中分配
同时Flanneld监听K8s集群数据库,为flannel.1设备提供封装数据时必要的mac、ip等网络数据信息

flannel跨主机通信原理

  • 当容器发送IP包,通过veth pair 发往cni网桥,再路由到本机的flannel.1设备进行处理
  • VTEP设备之间通过二层数据帧进行通信,源VTEP设备收到原始IP包后,在上面加上一个目的MAC地址,封装成一个内部数据帧,发送给目的VTEP设备
  • 内部数据桢,并不能在宿主机的二层网络传输,Linux内核还需要把它进一步封装成为宿主机的一个普通的数据帧,承载着内部数据帧通过宿主机的eth0进行传输
  • Linux会在内部数据帧前面,加上一个VXLAN头,VXLAN头里有一个重要的标志叫VNI,它是VTEP识别某个数据桢是不是应该归自己处理的重要标识
  • flannel.1设备只知道另一端flannel.1设备的MAC地址,却不知道对应的宿主机地址是什么。在linux内核里面,网络设备进行转发的依据,来自FDB的转发数据库,这个flannel.1网桥对应的FDB信息,是由flanneld进程维护的
  • linux内核在IP包前面再加上二层数据帧头,把目标节点的MAC地址填进去,MAC地址从宿主机的ARP表获取
  • 此时flannel.1设备就可以把这个数据帧从eth0发出去,再经过宿主机网络来到目标节点的eth0设备。目标主机内核网络栈会发现这个数据帧有VXLAN Header,并且VNI为1,Linux内核会对它进行拆包,拿到内部数据帧,根据VNI的值,交给本机flannel.1设备处理,flannel.1拆包,根据路由表发往cni网桥,最后到达目标容器
# 默认网络通信路由
ip r
# 桥接转发数据库
bridge fdb
# arp列表
arp -n

image-20250819155814548

image-20250819155830725

image-20250819155851926

flannel后端模式

网络模式 功能
vxlan 报文封装,默认模式
Directrouting 直接路由,跨网段使用vxlan,同网段使用host-gw模式
host-gw 主机网关,性能好,但只能在二层网络中,不支持跨网络
如果有成千上万的Pod,容易产生广播风暴,不推荐
UDP 性能差,不推荐
kubectl -n kube-flannel edit cm kube-flannel-cfg
  net-conf.json: |
    {
      "Network": "10.244.0.0/16",
      "EnableNFTables": false,
      "Backend": {
      # 更改内容
        "Type": "host-gw"
      }
    }
# 重启pod
kubectl -n kube-flannel delete pod -all
# 查看网络通信路由
ip r

calico网络插件

官网:https://docs.projectcalico.org/getting-started/kubernetes/self-managed-onprem/onpremises

calico组件

calicao网络架构

calicao跨主机通信原理

calicao部署

# 删除Flannel插件
kubectl delete  -f kube-flannel.yml

# 删除所有节点上flannel配置文件,避免冲突
rm -rf /etc/cni/net.d/10-flannel.conflist

# 下载部署文件
curl https://raw.githubusercontent.com/projectcalico/calico/v3.28.1/manifests/calico-typha.yaml -o calico.yaml

# 下载镜像上传至仓库
docker pull docker.io/calico/cni:v3.28.1
docker pull docker.io/calico/node:v3.28.1
docker pull docker.io/calico/kube-controllers:v3.28.1
docker pull docker.io/calico/typha:v3.28.1

# 更改yml设置
vim calico.yaml
4835           image: calico/cni:v3.28.1
4835           image: calico/cni:v3.28.1
4906           image: calico/node:v3.28.1
4932           image: calico/node:v3.28.1
5160           image: calico/kube-controllers:v3.28.1
5249         - image: calico/typha:v3.28.1
4973             - name: CALICO_IPV4POOL_VXLAN
4974               value: "Never"
4999             - name: CALICO_IPV4POOL_CIDR
5000               value: "10.244.0.0/16"
5001             - name: CALICO_AUTODETECTION_METHOD
5002               value: "interface=eth0"

kubectl apply -f calico.yaml
kubectl -n kube-system get pods
# 验证
kubectl run  web --image myapp:v1

kubectl get pods  -o wide

curl  10.244.169.129

调度

  • 调度是指将未调度的Pod自动分配到集群中的节点的过程
  • 调度器通过 kubernetes 的 watch 机制来发现集群中新创建且尚未被调度到 Node 上的 Pod
  • 调度器会将发现的每一个未调度的 Pod 调度到一个合适的 Node 上来运行

调度原理

  • 创建Pod
    • 用户通过Kubernetes API创建Pod对象,并在其中指定Pod的资源需求、容器镜像等信息
  • 调度器监视Pod
    • Kubernetes调度器监视集群中的未调度Pod对象,并为其选择最佳的节点
  • 选择节点
    • 调度器通过算法选择最佳的节点,并将Pod绑定到该节点上。调度器选择节点的依据包括节点的资源使用情况、Pod的资源需求、亲和性和反亲和性等
  • 绑定Pod到节点
    • 调度器将Pod和节点之间的绑定信息保存在etcd数据库中,以便节点可以获取Pod的调度信息
  • 节点启动Pod
    • 节点定期检查etcd数据库中的Pod调度信息,并启动相应的Pod。如果节点故障或资源不足,调度器会重新调度Pod,并将其绑定到其他节点上运行

调度器种类

  • 默认调度器(Default Scheduler):
    • 是Kubernetes中的默认调度器,负责对新创建的Pod进行调度,并将Pod调度到合适的节点上
  • 自定义调度器(Custom Scheduler):
    • 是一种自定义的调度器实现,可以根据实际需求来定义调度策略和规则,以实现更灵活和多样化的调度功能
  • 扩展调度器(Extended Scheduler):
    • 是一种支持调度器扩展器的调度器实现,可以通过调度器扩展器来添加自定义的调度规则和策略,以实现更灵活和多样化的调度功能
  • kube-scheduler是kubernetes中的默认调度器,在kubernetes运行后会自动在控制节点运行

调度方法

nodename

  • nodeName 是节点选择约束的最简单方法,但一般不推荐
  • 如果 nodeName 在 PodSpec 中指定了,则它优先于其他的节点选择方法
  • 使用 nodeName 来选择节点的一些限制
    • 如果指定的节点不存在。
    • 如果指定的节点没有资源来容纳 pod,则pod 调度失败。
    • 云环境中的节点名称并非总是可预测或稳定的
# 建立Pod 文件
kubectl run testpod  --image myapp:v1 --dry-run=client -o yaml > pod1.yml
# 设置调度
vim pod1.yml
apiVersion: v1
kind: Pod
metadata:
  labels:
    run: testpod
  name: testpod
spec:
  nodeName: k8s-node2
  containers:
  - image: myapp:v1
    name: testpod
# 建立Pod
kubectl apply -f pod1.yml
kubectl get pods  -o wide

找不到节点pod会出现pending,优先级最高,其他调度方式无效

nodeselector

  • nodeSelector 是节点选择约束的最简单推荐形式

  • 通过标签控制节点

  • 给选择的节点添加标签:

    • kubectl label nodes k8s-node1 lab=lee
  • 可以给多个节点设定相同标签

# 查看节点标签
kubectl get nodes --show-labels
# 设定节点标签
kubectl label nodes k8s-node1 lab=timinglee
kubectl get nodes k8s-node1 --show-labels
# 调度设置
vim pod2.yml
apiVersion: v1
kind: Pod
metadata:
  labels:
    run: testpod
  name: testpod
spec:
  nodeSelector:
    lab: timinglee
  containers:
  - image: myapp:v1
    name: testpod

kubectl apply -f pod2.yml
kubectl get pods  -o wide

节点标签可以给N个节点添加

affinity(亲和性)

官方文档:https://kubernetes.io/zh/docs/concepts/scheduling-eviction/assign-pod-node

  • nodeSelector提供了一种非常简单的方法来将pod约束到具有特定标签的节点上
  • 亲和/反亲和功能极大地扩展了你可以表达约束的类型
  • 使用节点上的pod的标签来约束,而不是使用节点本身的标签,来允许哪些 pod 可以或者不可以被放置在一起
nodeAffinity(节点亲和)
  • 那个节点服务指定条件就在那个节点运行
  • requiredDuringSchedulingIgnoredDuringExecution 必须满足,但不会影响已经调度
  • preferredDuringSchedulingIgnoredDuringExecution 倾向满足,在无法满足情况下也会调度pod
    • IgnoreDuringExecution 表示如果在Pod运行期间Node的标签发生变化,导致亲和性策略不能满足,则继续运行当前的Pod。
  • nodeaffinity还支持多种规则匹配条件的配置如下
匹配规则 功能
ln label 的值在列表内
Notln label 的值不在列表内
Gt label 的值大于设置的值,不支持Pod亲和性
Lt label 的值小于设置的值,不支持pod亲和性
Exists 设置的label 存在
DoesNotExist 设置的 label 不存在
# 配置
vim pod3.yml
apiVersion: v1
kind: Pod
metadata:
  name: node-affinity
spec:
  containers:
  - name: nginx
    image: nginx
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
           nodeSelectorTerms:
           - matchExpressions:
             - key: disk
               operator: In
               values:
                 - ssd
# 将pod运行在具有disk=ssd标签的k8s节点上,即pod根据节点的标签选择运行
Podaffinity(Pod的亲和)
  • 那个节点有符合条件的POD就在那个节点运行
  • podAffinity 主要解决POD可以和哪些POD部署在同一个节点中的问题
  • podAntiAffinity主要解决POD不能和哪些POD部署在同一个节点中的问题。它们处理的是Kubernetes集群内部POD和POD之间的关系。
  • Pod 间亲和与反亲和在与更高级别的集合(例如 ReplicaSets,StatefulSets,Deployments 等)一起使用时
  • Pod 间亲和与反亲和需要大量的处理,这可能会显著减慢大规模集群中的调度
vim example4.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
      affinity:
        podAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - nginx
            topologyKey: "kubernetes.io/hostname"

kubectl get pods  -o wide
Podantiaffinity(Pod反亲和)
vim example5.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
      affinity:
        podAntiAffinity:
        # 反亲和
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - nginx
            topologyKey: "kubernetes.io/hostname"

kubectl get pods  -o wide

Taints(污点模式,禁止调度)

Taints(污点)是Node的一个属性

当节点存在Taints时,默认不会将Pod调度到此节点上

假如Pod设置Tolerations(容忍),只要Pod能根据规则容忍Node上的污点,那么K8s就会忽略节点污点,将Pod调度到此节点上

# 创建/追加(key相同时,覆盖)
kubectl taint nodes <your-nodename> key=string:effect
# 查询
kubectl describe nodes <your-nodename> | grep Taints:
# 删除
kubectl taint nodes <your-nodename> key-

其中 effect 的取值

effect值 解释
NoSchedule 禁止新Pod调度到该节点,但不影响已在节点上运行的Pod
如果Pod没有匹配的容忍,则无法被调度到带有此污点的节点;已运行的Pod不受影响,会继续运行
PreferNoSchedule 尽量避免新Pod调度到该节点,但不是绝对禁止
K8s会优先将Pod调度到没有污点的节点;若没有其他合适节点,仍可调度到此节点(需Pod容忍此污点)
NoExecute 既禁止新Pod调度,也会驱逐已运行的 Pod
如果Pod没有匹配的容忍,不仅无法调度到该节点,已在节点上运行的Pod也会被立即驱逐
# 建立控制器并运行
vim example6.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: web
  name: web
spec:
  replicas: 2
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
      - image: nginx
        name: nginx

kubectl apply -f example6.yml
kubectl get pod -o wide

# 设定污点未 NoSchedule
kubectl taint node k8s-node1 name=lee:NoSchedule
kubectl describe nodes k8s-node1 | grep Tain

# 控制器增加Pod
kubectl get pod -o wide

# 设定污点为 NoExecute
kubectl taint node k8s-node1 name=lee:NoExecute
kubectl describe nodes k8s-node1 | grep Tain
kubectl get pod -o wide

# 删除污点
kubectl taint node k8s-node1 name-
kubectl describe nodes k8s-node1 | grep Tain

tolerations(污点容忍)

  • tolerations中定义的key、value、effect,要与node上设置的taint保持一直:
    • 如果 operator 是 Equal ,则key与value之间的关系必须相等。
    • 如果 operator 是 Exists ,value可以省略
    • 如果不指定operator属性,则默认值为Equal。
  • 还有两个特殊值:
    • 当不指定key,再配合Exists 就能匹配所有的key与value ,可以容忍所有污点。
    • 当不指定effect ,则匹配所有的effect
# 设定节点污点
kubectl taint node k8s-node1 name=lee:NoExecute
kubectl taint node k8s-node2 nodetype=bad:NoSchedule

vim example7.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: web
  name: web
spec:
  replicas: 6
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
      - image: nginx
        name: nginx
      
      tolerations:
      # 容忍所有污点
      - operator: Exists
    
	  tolerations:
	  # 容忍effect为Noschedule的污点
      - operator: Exists
        effect: NoSchedule
	
	  tolerations:
	  # 容忍指定kv的NoSchedule污点
      - key: nodetype
        value: bad
        effect: NoSchedule

微服务

内部控制器完成集群的工作负载,外部通过微服务暴露端口进行访问

Service是一组提供相同服务的Pod对外开放的接口

service默认只支持4层负载均衡能力,没有7层功能(可通过Ingress支持)

iptables调度

# 生成控制器文件
kubectl create deployment dep --image myapp:v1 --replicas 2 --dry-run=client -o yaml > dep.yaml
# 建立控制器
kubectl apply -f dep.yaml
# 生成微服务文件
kubectl expose deployment dep --port 80 --target-port 80 --dry-run=client -o yaml >> dep.yaml
# 修改yaml文件
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: dep
  name: dep
spec:
  replicas: 2
  selector:
    matchLabels:
      app: dep
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: dep
    spec:
      containers:
      - image: myapp:v1
        name: myapp
---
apiVersion: v1
kind: Service
metadata:
  labels:
    app: dep
  name: dep
spec:
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
  selector:
    app: dep
# 应用yaml文件
kubectl apply -f dep.yaml
# 查看微服务
kubectl get service -o wide
# 微服务默认使用iptables调度
iptables -t nat -nL
# 调度
curl 10.109.234.4/hostname.html
# 回收
kubectl delete -f dep.yaml

IPVS调度

  • Service 是由 kube-proxy 组件,加上 iptables 来共同实现的
  • kube-proxy 通过 iptables 处理 Service 的过程,需要在宿主机上设置相当多的 iptables 规则,如果宿主机有大量的Pod,不断刷新iptables规则,会消耗大量的CPU资源
  • IPVS模式的service,可以使K8s集群支持更多量级的Pod
# 所有节点安装ipvsadm
yum install ipvsadm -y
# 修改master节点配置
kubectl -n kube-system edit cm kube-proxy
    metricsBindAddress: ""
    mode: "ipvs"
    nftables:
# 重启Pod(回收旧的)
kubectl -n kube-system get pods | awk '/kube-proxy/{system("kubectl -n kube-system delete pods "$1)}'
# 检查
kubectl -n kube-system get pods
# 查看ipvs策略
ipvsadm -Ln

切换ipvs模式后,kube-proxy会在宿主机上添加一个虚拟网卡:kube-ipvs0,并分配所有service IP
ip a | tail

微服务类型

微服务类型 作用描述
ClusterIP 默认值,k8s系统给service自动分配的虚拟IP,只能在集群内部访问
NodePort 将Service通过指定的Node上的端口暴露给外部,访问任意一个NodeIP:nodePort都将路由到ClusterIP
LoadBalancer 在NodePort的基础上,借助cloud provider创建一个外部的负载均衡器,并将请求转发到 NodeIP:NodePort,此模式只能在云服务器上使用
ExternalName 将服务通过 DNS CNAME 记录方式转发到指定的域名(通过 spec.externlName 设定

clusterip

clusterip模式只能在集群内访问,并对集群内的pod提供健康检测和自动发现功能

mv dep.yaml clu.yaml
vim clu.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: dep
  name: dep
spec:
  replicas: 2
  selector:
    matchLabels:
      app: dep
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: dep
    spec:
      containers:
      - image: myapp:v1
        name: myapp
---
apiVersion: v1
kind: Service
metadata:
  labels:
    app: dep
  name: dep
spec:
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
  selector:
    app: dep
  type: ClusterIP
# 创建后提供解析
# Pod名称.命名空间.svc.集群域名 @10.96.0.10
dig dep.default.svc.cluster.local @10.96.0.10
特殊模式headless

headless(无头服务)

对于无头 Services 并不会分配 Cluster IP,kube-proxy不会处理它们, 而且平台也不会为它们进行负载均衡和路由,集群访问通过dns解析直接指向到业务pod上的IP,所有的调度有dns单独完成

[root@k8s-master ~]# vim .yaml
---
apiVersion: v1
kind: Service
metadata:
  labels:
    app: timinglee
  name: timinglee
spec:
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
  selector:
    app: timinglee
  type: ClusterIP
  clusterIP: None


[root@k8s-master ~]# kubectl delete -f timinglee.yaml
[root@k8s-master ~]# kubectl apply -f timinglee.yaml
deployment.apps/timinglee created

#测试
[root@k8s-master ~]# kubectl get services timinglee
NAME        TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
timinglee   ClusterIP   None         <none>        80/TCP    6s

[root@k8s-master ~]# dig  timinglee.default.svc.cluster.local @10.96.0.10

; <<>> DiG 9.16.23-RH <<>> timinglee.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: 51527
;; 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: 81f9c97b3f28b3b9 (echoed)
;; QUESTION SECTION:
;timinglee.default.svc.cluster.local. IN        A

;; ANSWER SECTION:
timinglee.default.svc.cluster.local. 20 IN A    10.244.2.14		#直接解析到pod上
timinglee.default.svc.cluster.local. 20 IN A    10.244.1.18

;; Query time: 0 msec
;; SERVER: 10.96.0.10#53(10.96.0.10)
;; WHEN: Wed Sep 04 13:58:23 CST 2024
;; MSG SIZE  rcvd: 178


#开启一个busyboxplus的pod测试
[root@k8s-master ~]# kubectl run  test --image busyboxplus -it
If you don't see a command prompt, try pressing enter.
/ # nslookup timinglee-service
Server:    10.96.0.10
Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local

Name:      timinglee-service
Address 1: 10.244.2.16 10-244-2-16.timinglee-service.default.svc.cluster.local
Address 2: 10.244.2.17 10-244-2-17.timinglee-service.default.svc.cluster.local
Address 3: 10.244.1.22 10-244-1-22.timinglee-service.default.svc.cluster.local
Address 4: 10.244.1.21 10-244-1-21.timinglee-service.default.svc.cluster.local
/ # curl timinglee-service
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
/ # curl timinglee-service/hostname.html
timinglee-c56f584cf-b8t6m

nodeport

通过ipvs暴漏端口从而使外部主机通过master节点的对外ip:来访问pod业务

vim timinglee.yaml
---
apiVersion: v1
kind: Service
metadata:
  labels:
    app: timinglee-service
  name: timinglee-service
spec:
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
  selector:
    app: timinglee
  type: NodePort
[root@k8s-master ~]# kubectl apply -f timinglee.yaml
deployment.apps/timinglee created
service/timinglee-service created
[root@k8s-master ~]# kubectl get services  timinglee-service
NAME                TYPE       CLUSTER-IP    EXTERNAL-IP   PORT(S)        AGE
timinglee-service   NodePort   10.98.60.22   <none>        80:31771/TCP   8

nodeport在集群节点上绑定端口,一个端口对应一个服务
[root@k8s-master ~]# for i in {1..5}
> do
> curl 172.25.254.100:31771/hostname.html
> done
timinglee-c56f584cf-fjxdk
timinglee-c56f584cf-5m2z5
timinglee-c56f584cf-z2w4d
timinglee-c56f584cf-tt5g6
timinglee-c56f584cf-fjxdk

nodeport默认端口是30000-32767,超出会报错

[root@k8s-master ~]# vim timinglee.yaml
apiVersion: v1
kind: Service
metadata:
  labels:
    app: timinglee-service
  name: timinglee-service
spec:
  ports:

  - port: 80
    protocol: TCP
    targetPort: 80
    nodePort: 33333
      selector:
    app: timinglee
      type: NodePort

[root@k8s-master ~]# kubectl apply -f timinglee.yaml
deployment.apps/timinglee created
The Service "timinglee-service" 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
# 如果使用范围外的端口
# 需要特殊设定
vim /etc/kubernetes/manifests/kube-apiserver.yaml
- --service-node-port-range=30000-40000

添加“–service-node-port-range=“ 参数,端口范围可以自定义
修改后api-server会自动重启,等apiserver正常启动后才能操作集群
集群重启自动完成在修改完参数后全程不需要人为干预

metalLB

官网:https://metallb.universe.tf/installation/
为LoadBalancer分配vip

# 设置ipvs模式,打开
kubectl -n kube-system edit cm kube-proxy
# 回收Pod,使配置生效
kubectl -n kube-system get pods | awk '/kube-proxy/{system("kubectl -n kube-system delete pods "$1)}'
# 下载部署文件
wget https://raw.githubusercontent.com/metallb/metallb/v0.13.12/config/manifests/metallb-native.yaml
# 私有仓库创建metallb项目(公开)
# 上传metallb-native.yaml、configmap.yml、metalLB.tag.gz
# 加载镜像
docker load -i metalLB.tag.gz
# 打标签
docker tag quay.io/metallb/controller:v0.14.8 ooovooo.org/metallb/controller:v0.14.8
docker tag quay.io/metallb/speaker:v0.14.8 ooovooo.org/metallb/speaker:v0.14.8
# 上传镜像
docker push ooovooo.org/metallb/speaker:v0.14.8
docker push ooovooo.org/metallb/controller:v0.14.8
# 修改配置文件
vim metallb-native.yaml
image: metallb/controller:v0.14.8
image: metallb/speaker:v0.14.8
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.99		# 修改本机主机段,不能有IP被占用
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
  name: example
  namespace: metallb-system
spec:
  ipAddressPools:
  - first-pool						# 使用地址池
# 应用yml
kubectl apply -f metallb-native.yaml
kubectl apply -f configmap.yml
# 等待服务启动
kubectl -n metallb-system get pods
# 测试
# 创建一个services
vim services.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: dep
  name: dep
spec:
  replicas: 2
  selector:
    matchLabels:
      app: dep
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: dep
    spec:
      containers:
      - image: myapp:v1
        name: myapp
---
apiVersion: v1
kind: Service
metadata:
  labels:
    app: dep
  name: dep
spec:
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
  selector:
    app: dep
  type: LoadBalancer
# 查看状态
kubectl get services
# 测试
curl 172.25.254.50

loadbalancer

云平台会为我们分配vip并实现访问,如果是裸金属主机那么需要metallb来实现ip的分配

[root@k8s-master ~]# vim timinglee.yaml

---
apiVersion: v1
kind: Service
metadata:
  labels:
    app: timinglee-service
  name: timinglee-service
spec:
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
  selector:
    app: timinglee
  type: LoadBalancer

[root@k8s2 service]# kubectl apply -f myapp.yml

默认无法分配外部访问IP
[root@k8s2 service]# kubectl get svc
NAME         TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
kubernetes   ClusterIP      10.96.0.1       <none>        443/TCP        4d1h
myapp        LoadBalancer   10.107.23.134   <pending>     80:32537/TCP   4s

LoadBalancer模式适用云平台,裸金属环境需要安装metallb提供支持

externalname

  • 开启services后,不会被分配IP,而是用dns解析CNAME固定域名来解决ip变化问题
  • 一般应用于外部业务和pod沟通或外部业务迁移到pod内时
  • 在应用向集群迁移过程中,externalname在过度阶段就可以起作用了。
  • 集群外的资源迁移到集群时,在迁移的过程中ip可能会变化,但是域名+dns解析能完美解决此问题
# 生成yaml文件
kubectl create service externalname ono --external-name www.baidu.com -dry-run=client -o yaml > ext.yaml
# 编辑文件
vim ext.yaml
apiVersion: v1
kind: Service
metadata:
  labels:
    app: ono
  name: ono
spec:
  externalName: www.baidu.com
  selector:
    app: ono
  type: ExternalName
# 应用yaml
kubectl apply -f ext.yaml
# 查看ExternalName域名
kubectl describe service ono
# 查看解析
dig ono.default.svc.cluster.local @10.96.0.10
# externalName指向www.baidu.com
ono.default.svc.cluster.local. 30 IN    CNAME   www.baidu.com.
# 解析到的百度服务器的IP
www.a.shifen.com.       30      IN      A       183.2.172.177
www.a.shifen.com.       30      IN      A       183.2.172.17
# 测试
kubectl run ono --image busyboxplus -it
ping ono.default.svc.cluster.local

image-20250814101235503

image-20250814101319554
image-20250814101413531
image-20250814101459420

Ingress-nginx

官网:https://kubernetes.github.io/ingress-nginx/deploy/#bare-metal-clusters

  • 一种全局的、为了代理不同后端 Service 而设置的负载均衡服务,支持7层
  • Ingress由两部分组成:Ingress controller和Ingress服务
  • Ingress Controller 会根据你定义的 Ingress 对象,提供对应的代理能力
    需要提前部署metallb

部署

# 打标签
docker tag registry.k8s.io/ingress-nginx/controller:v1.13.1 ooovooo.org/ingress-nginx/controller:v1.13.1
docker tag registry.k8s.io/ingress-nginx/kube-webhook-certgen:v1.6.1 ooovooo.org/ingress-nginx/kube-webhook-certgen:v1.6.1
# 上传镜像
docker push ooovooo.org/ingress-nginx/controller:v1.13.1
docker push ooovooo.org/ingress-nginx/kube-webhook-certgen:v1.6.1
# 修改yaml,注意自己的版本
vim deploy.yaml
444         image: ingress-nginx/controller:v1.13.1
547         image: ingress-nginx/kube-webhook-certgen:v1.6.1
603         image: ingress-nginx/kube-webhook-certgen:v1.6.1
sed -n '444p;547p;603p' deploy.yaml
# 应用
kubectl apply -f deploy.yaml
# 查看状态
watch -n 1 kubectl -n ingress-nginx get pods
# 查看ingress-nginx
kubectl -n ingress-nginx get svc
NAME                                 TYPE
ingress-nginx-controller             NodePort
# 假如没有EXTERNAL-IP,则没有配置好metalLB
# 修改微服务类型
kubectl -n ingress-nginx edit svc ingress-nginx-controller
51 type: LoadBalancer
# 检验
kubectl -n ingress-nginx get svc
NAME                                 TYPE
ingress-nginx-controller             LoadBalancer

image-20250814121905358
image-20250814121956347

测试

# 生成yaml文件
kubectl create ingress uou --rule '*/=ooovooo-svc:80' --dry-run=client -o yaml > ingress.yml
vim ingress.yml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: uou
spec:
  ingressClassName: nginx
  rules:
  - http:
      paths:
      - backend:
          service:
            name: ooovooo-svc
            port:
              number: 80
        path: /
        pathType: Prefix
# 应用yaml
kubectl apply -f ingress.yml
# 查看状态
kubectl get ingress
# 生成后端服务器

# 访问

ingress高级用法

基于路径的访问

# 生成yaml
kubectl create deployment myapp-v1 --image myapp:v1 --dry-run=client -o yaml > myapp-v1.yaml
kubectl create deployment myapp-v2 --image myapp:v2 --dry-run=client -o yaml > myapp-v2.yaml
# 配置v1
vim myapp-v1.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: myapp-v1
  name: myapp-v1
spec:
  replicas: 1
  selector:
    matchLabels:
      app: myapp-v1
  strategy: {}
  template:
    metadata:
      labels:
        app: myapp-v1
    spec:
      containers:
      - image: myapp:v1
        name: myapp
---
apiVersion: v1
kind: Service
metadata:
  labels:
    app: myapp-v1
  name: myapp-v1
spec:
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
  selector:
    app: myapp-v1
# 配置v2
vim myapp-v2.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: myapp-v2
  name: myapp-v2
spec:
  replicas: 1
  selector:
    matchLabels:
      app: myapp-v2
  strategy: {}
  template:
    metadata:
      labels:
        app: myapp-v2
    spec:
      containers:
      - image: myapp:v2
        name: myapp
---
apiVersion: v1
kind: Service
metadata:
  labels:
    app: myapp-v2
  name: myapp-v2
spec:
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
  selector:
    app: myapp-v2
# 
# 暴露端口
kubectl expose deployment myapp-v1 --port 80 --target-port 80 --dry-run=client -o yaml >> myapp-v1.yaml
kubectl expose deployment myapp-v2 --port 80 --target-port 80 --dry-run=client -o yaml >> myapp-v2.yaml
# 查看service状态
kubectl get services

image-20250814122107515

image-20250814122208792

# 建立ingress
vim ingress1.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
  # 将匹配到的请求路径,重写(替换)为 / 后,再转发到后端服务
    nginx.ingress.kubernetes.io/rewrite-target: /
  name: ingress1
spec:
  ingressClassName: nginx
  rules:
  - host: www.ooovooo.org
    http:
      paths:
      - backend:
          service:
            name: myapp-v1
            port:
              number: 80
        path: /v1
        pathType: Prefix

      - backend:
          service:
            name: myapp-v2
            port:
              number: 80
        path: /v2
        pathType: Prefix
# 测试
kubectl apply -f ingress1.yaml
echo 172.25.254.55 www.ooovooo.org >> /etc/hosts
curl www.ooovooo.org/v1
curl www.ooovooo.org/v2
curl www.ooovooo.org/v2/vvip

image-20250814122234968

image-20250814122259273

基于域名的访问

# ingress-nginx-controller的EXTERNAL-IP
echo "172.25.254.50 www.myapp-v1.org" >> /etc/hosts
echo "172.25.254.50 www.myapp-v2.org" >> /etc/hosts

vim host-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
  name: host-ingress
spec:
  ingressClassName: nginx
  rules:
  - host: www.myapp-v1.org
    http:
      paths:
      - backend:
          service:
            name: myapp-v1
            port:
              number: 80
        path: /
        pathType: Prefix
        
  - host: www.myapp-v2.org
    http:
      paths:
      - backend:
          service:
            name: myapp-v2
            port:
              number: 80
        path: /
        pathType: Prefix
# 应用ingress
kubectl apply -f host-ingress.yaml
kubectl describe ingress host-ingress
# 测试
curl www.myapp-v1.org
curl www.myapp-v2.org

image-20250814143428437
image-20250814143503166

image-20250814143520345

建立tls加密

# 建立证书
openssl req -newkey rsa:2048 -nodes -keyout tls.key -x509 -days 365 -subj "/CN=nginxsvc/O=nginxsvc" -out tls.crt
# 建立加密资源
kubectl create secret tls  web-tls-secret --key tls.key --cert tls.crt
# 查看资源
kubectl get secrets

secrets通常在kubetnetes中存放敏感数据

# 建立基于tls认证的yaml文件
vim tls-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
  name: tls-ingress
spec:
  tls:
  - hosts:
    - www.myapp-v1.org
    secretName: web-tls-secret
  ingressClassName: nginx
  rules:
  - host: www.myapp-v1.org
    http:
      paths:
      - backend:
          service:
            name: myapp-v1
            port:
              number: 80
        path: /
        pathType: Prefix
# 需要有172.25.254.50 www.myapp-v1.org的解析
curl www.myapp-v1.org
curl -k https://www.myapp-v1.org

image-20250814154447047

建立auth认证

# 认证文件
dnf install httpd-tools -y
# 注意:文件名必须是auth,否则资源不识别
htpasswd -cm auth ovo
cat auth
# 建立认证资源
kubectl create secret generic auth-web --from-file auth
# 建立基于用户的yaml文件
vim auth-ingress.yaml
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/rewrite-target: /
  name: auth-ingress
spec:
  tls:
  - hosts:
    - www.myapp-v1.org
    secretName: web-tls-secret
  ingressClassName: nginx
  rules:
  - host: www.myapp-v1.org
    http:
      paths:
      - backend:
          service:
            name: myapp-v1
            port:
              number: 80
        path: /
        pathType: Prefix
# 应用yaml
kubectl apply -f auth-ingress.yaml
kubectl describe ingress auth-ingress
# 测试
curl -k https://www.myapp-v1.org
curl -k https://www.myapp-v1.org -u ovo:aaa

image-20250814155026757
image-20250814155046489

image-20250814155105631

rewrite重定向

vim rew-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    nginx.ingress.kubernetes.io/app-root: /hostname.html
  name: rew-ingress
spec:
  ingressClassName: nginx
  rules:
  - host: www.myapp-v1.org
    http:
      paths:
      - backend:
          service:
            name: myapp-v1
            port:
              number: 80
        path: /
        pathType: Prefix
# 应用yaml
kubectl apply -f rew-ingress.yaml
# 查看
kubectl describe ingress rew-ingress
# 测试
curl www.myapp-v1.org
curl www.myapp-v1.org/hostname.html
curl -L www.myapp-v1.org

image-20250814163045021

vim rew-ingress2.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /$2
    nginx.ingress.kubernetes.io/use-regex: "true"
  name: rew-ingress2
spec:
  ingressClassName: nginx
  rules:
  - host: www.myapp-v1.org
    http:
      paths:
      - backend:
          service:
            name: myapp-v1
            port:
              number: 80
        path: /(.*)/(.*)
        pathType: ImplementationSpecific
# 应用yaml
kubectl apply -f rew-ingress2.yaml
# 查看
kubectl describe ingress rew-ingress2
# 测试
curl -L www.myapp-v1.org/hostname.html
curl -L www.myapp-v1.org/ono/
curl -L www.myapp-v1.org/ono/hostname.html

image-20250814163552974

canary金丝雀发布

基于header(http包头)灰度

  • 通过Annotaion扩展
  • 创建灰度ingress,配置灰度头部key以及value
  • 灰度流量验证完毕后,切换正式ingress到新版本
  • 之前我们在做升级时可以通过控制器做滚动更新,默认25%利用header可以使升级更为平滑,通过key 和vule 测试新的业务体系是否有问题。
#建立版本1的ingress
[root@k8s-master app]# vim ingress7.yml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
  name: myapp-v1-ingress
spec:
  ingressClassName: nginx
  rules:
  - host: myapp-tls.ooovooo.org
    http:
      paths:
      - backend:
          service:
            name: myappv1
            port:
              number: 80
        path: /
        pathType: Prefix
        
[root@k8s-master app]# kubectl describe ingress myapp-v1-ingress
Name:             myapp-v1-ingress
Labels:           <none>
Namespace:        default
Address:          172.25.254.10
Ingress Class:    nginx
Default backend:  <default>
Rules:
  Host                 Path  Backends
  ----                 ----  --------
  myapp.timinglee.org
                       /   myapp-v1:80 (10.244.2.31:80)
Annotations:           <none>
Events:
  Type    Reason  Age                From                      Message
  ----    ------  ----               ----                      -------
  Normal  Sync    44s (x2 over 73s)  nginx-ingress-controller  Scheduled for sync


#建立基于header的ingress
[root@k8s-master app]# vim ingress8.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
  name: myapp-v2-ingress
spec:
  ingressClassName: nginx
  rules:
  - host: myapp-tls.ooovooo.org
    http:
      paths:
      - backend:
          service:
            name: myappv2
            port:
              number: 80
        path: /
        pathType: Prefix
[root@k8s-master app]# kubectl apply -f ingress8.yml
ingress.networking.k8s.io/myapp-v2-ingress created
[root@k8s-master app]# kubectl describe ingress myapp-v2-ingress
Name:             myapp-v2-ingress
Labels:           <none>
Namespace:        default
Address:
Ingress Class:    nginx
Default backend:  <default>
Rules:
  Host                 Path  Backends
  ----                 ----  --------
  myapp.timinglee.org
                       /   myapp-v2:80 (10.244.2.32:80)
Annotations:           nginx.ingress.kubernetes.io/canary: true
                       nginx.ingress.kubernetes.io/canary-by-header: version
                       nginx.ingress.kubernetes.io/canary-by-header-value: 2
Events:
  Type    Reason  Age   From                      Message
  ----    ------  ----  ----                      -------
  Normal  Sync    21s   nginx-ingress-controller  Scheduled for sync

#测试:
[root@reg ~]# curl  myapp.timinglee.org
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
[root@reg ~]# curl -H "version: 2" myapp.timinglee.org
Hello MyApp | Version: v2 | <a href="hostname.html">Pod Name</a>

基于权重的灰度发布

  • 通过Annotaion拓展
  • 创建灰度ingress,配置灰度权重以及总权重
  • 灰度流量验证完毕后,切换正式ingress到新版本
#基于权重的灰度发布
[root@k8s-master app]# vim ingress9.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"
  name: myapp-v2-ingress
spec:
  ingressClassName: nginx
  rules:
  - host: myapp-tls.ooovooo.org
    http:
      paths:
      - backend:
          service:
            name: myappv2
            port:
              number: 80
        path: /
        pathType: Prefix

[root@k8s-master app]# kubectl apply -f ingress8.yml
ingress.networking.k8s.io/myapp-v2-ingress created

#测试:
[root@reg ~]# vim check_ingress.sh
#!/bin/bash
v1=0
v2=0

for (( i=0; i<100; i++))
do
    response=`curl -s myapp.timinglee.org |grep -c v1`

    v1=`expr $v1 + $response`
    v2=`expr $v2 + 1 - $response`

done
echo "v1:$v1, v2:$v2"

[root@reg ~]# sh check_ingress.sh
v1:90, v2:10

#更改完毕权重后继续测试可观察变化

存储

存储类型 功能
configmap 用于保存配置数据,以键值对形式存储
secrets 用来保存敏感信息
volumes 存储容器内容
StorageClass 存储容器内容
statefulset 管理有状态服务

configmap

保存配置数据,以键值对形式存储

etcd(键值存储系统)限制了文件大小不能超过1M(超过1M,需要修改数据)

创建方式

字面值
# 键=值方式
kubectl create configmap user --from-literal user1=zhang3 --from-literal user2=li4
# 查看user(可缩写成cm)
kubectl describe cm user
# 删除
kubectl delete configmaps user

image-20250816112359196

文件
# 创建文件
echo ovo > user1.txt
echo ono > user2.txt
# 加载
kubectl create configmap user --from-file ./user1.txt --from-file ./user2.txt
kubectl describe configmaps user
# 删除
kubectl delete configmaps user

image-20250816111541617

目录
# 创建素材
mkdir user
mv user1.txt ./user/user1.txt
mv user2.txt ./user/user2.txt
# 加载
kubectl create configmap user --from-file user/
# 查看
kubectl describe configmaps user
# 删除
kubectl delete configmaps user

image-20250816111744556

ymal文件
# 生成
kubectl create configmap user --from-literal name=zhangn3 --from-literal age=18 --dry-run=client -o yaml > user.yaml
# 应用
kubectl apply -f user.yaml
# 查看
kubectl describe configmaps user
# 删除
kubectl delete configmaps user

image-20250816111939035

image-20250816112125010

使用方法

通过环境变量方式传递给Pod
kubectl create configmap ovo --dry-run=client -o yaml > ovo.yaml
vim ovo.yaml
apiVersion: v1
data:
kind: ConfigMap
metadata:
  creationTimestamp: null
  name: ovo
kubectl run haha --image busyboxplus --dry-run=client -o yaml > ono.yml
apiVersion: v1
kind: Pod
metadata:
  labels:
    run: map
  name: map
spec:
  containers:
  - image: busyboxplus
    name: map
    command:
    - /bin/sh
    - -c
    - env
    env:
    - name: key1
      valueFrom:
        configMapKeyRef:
          name: user1
          key: zhang3
    - name: key2
      valueFrom:
        configMapKeyRef:
          name: age
          key: 18
  restartPolicy: Never
kubectl apply -f ovo.yaml
# 不运行configmap,则报错 Error: configmap "ono" not found
kubectl apply -f ono.yaml
kubectl logs pods/map
kubectl delete pods map
kubectl delete configmaps ovo
通过Pod的命令运行
# 创建configmap,传递root的密码
kubectl create configmap phpmyadmin --dry-run=client -o yaml > phpmyadmin.yaml
vim phpmyadmin.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: phpmyadmin
data:
  MYSQL_ROOT_PASSWORD: aaa
  PMA_ARBITRARY: "1"

image-20250816154014656

# 创建MySQL
vim phpmysqladmin.yaml
apiVersion: v1
kind: Pod
metadata:
  labels:
    run: phpmysqladmin
  name: phpmysqladmin
spec:
  containers:
  - image: mysql/mysql:8.0
    name: mysql
    ports:
      - containerPort: 3306
    envFrom:
    - configMapRef:
        name: phpmyadmin
  - image: mysql/phpmyadmin:latest
    name: phpmyadmin
    ports:
      - containerPort: 80
        protocol: TCP
        hostPort: 80
    envFrom:
    - configMapRef:
        name: phpmyadmin

image-20250816154415265
优化:通过微服务暴露端口

kubectl expose pod phpmysqladmin --port 80 --target-port 80 --dry-run=client -o yaml >> phpmysqladmin.yaml
vim phpmysqladmin.yaml
apiVersion: v1
kind: Service
metadata:
  labels:
    run: phpmysqladmin
  name: phpmysqladmin
spec:
  ports:
  - port: 80
    name: phpadmin
    protocol: TCP
    targetPort: 80
  - port: 3306
    name: mysql
    protocol: TCP
    targetPort: 3306
  selector:
    run: phpmysqladmin
  type: LoadBalancer
# 需要配置metalLB
# 查看服务IP
kubectl get services phpmysqladmin
# 浏览器访问172.25.254.51,服务器localhost:3306,用户root

image-20250816154507237
image-20250816154812339
image-20250816154900927

作为Volume方式挂载到Pod内
[root@k8s-master ~]# vim ono.yml
apiVersion: v1
kind: Pod
metadata:
  labels:
    run: ono
  name: ono
spec:
  containers:
  - image: busyboxplus
    name: ono
    command:
    - /bin/sh
    - -c
    - sleep 1000000
    - cat /config/age
    - cat /config/name
    volumeMounts:
    # 被调用的卷
    - name: haha
    # 被调用卷名
      mountPath: /config
  volumes:
  # 声明卷的配置
  - name: haha
    configMap:
    # 卷的类型
      name: user
      # 卷的名称
  restartPolicy: Never

#查看日志
[root@k8s-master ~]# kubectl logs testpod
172.25.254.100
通过热更新cm修改配置
[root@k8s-master ~]# kubectl edit cm nginx-conf
apiVersion: v1
data:
  nginx.conf: |
    server {
      listen 8080;						#端口改为8080
      server_name _;
      root /usr/share/nginx/html;
      index index.html;
    }
kind: ConfigMap
metadata:
  creationTimestamp: "2024-09-07T02:49:20Z"
  name: nginx-conf
  namespace: default
  resourceVersion: "153055"
  uid: 20bee584-2dab-4bd5-9bcb-78318404fa7a

#查看配置文件
[root@k8s-master ~]# kubectl exec pods/nginx-8487c65cfc-cz5hd -- cat /etc/nginx/conf.d/nginx.conf
server {
  listen 8080;
  server_name _;
  root /usr/share/nginx/html;
  index index.html;
}

配置文件修改后不会生效,需要删除pod后控制器会重建pod,此时才会生效

secrets

用来保存敏感信息,例如密码、OAuth 令牌和 ssh key
敏感信息放在 secret 中比放在 Pod 的定义或者容器镜像中来说更加安全和灵活

  • Pod 可以用两种方式使用 secret:
    • 作为 volume 中的文件被挂载到 pod 中的一个或者多个容器里。
    • 当 kubelet 为 pod 拉取镜像时使用。
  • Secret的类型:
    • Service Account:Kubernetes 自动创建包含访问 API 凭据的 secret,并自动修改 pod 以使用此类型的 secret。
    • Opaque:使用base64编码存储信息,可以通过base64 --decode解码获得原始数据,因此安全性弱。
    • kubernetes.io/dockerconfigjson:用于存储docker registry的认证信息

创建方式

文件创建
[root@k8s-master secrets]# echo -n "timinglee" > username.txt
[root@k8s-master secrets]# echo -n "lee" > password.txt
# 加密
root@k8s-master secrets]# kubectl create secret generic userlist --from-file username.txt --from-file password.txt
# generic
# tls
# dockers-registry
secret/userlist created

[root@k8s-master secrets]# kubectl get secrets userlist -o yaml
  password.txt: bGVl
  username.txt: dGltaW5nbGVl
  
echo -n timinglee | base64
echo -n "dGltaW5nbGVl" | base64 -d
yaml文件创建
[root@k8s-master secrets]# kubectl create secret generic userlist --dry-run=client -o yaml > userlist.yml

[root@k8s-master secrets]# vim userlist.yml
apiVersion: v1
kind: Secret
metadata:
  creationTimestamp: null
  name: userlist
type: Opaque
data:
  username: dGltaW5nbGVl
  password: bGVl

[root@k8s-master secrets]# kubectl apply -f userlist.yml
secret/userlist created

[root@k8s-master secrets]# kubectl describe secrets userlist
Name:         userlist
Namespace:    default
Labels:       <none>
Annotations:  <none>

Type:  Opaque

Data
====
password:  3 bytes
username:  9 byte

使用方法

将Secret挂载到Volume中
# 先创造一个secret
kubectl create secret generic mysecret --dry-run=client -o yaml > mysecret.yaml
# generic:创建通用类型的 Secret
# 补充内容
vim mysecret.yaml
apiVersion: v1
kind: Secret
metadata:
  creationTimestamp: null
  name: userlist
type: Opaque
data:
  username: d2FuZzU=
  password: emhhbzY=
---
# 创建一个pod并将secrets挂载到Pod内
kubectl run mypod --image nginx --dry-run=client -o yaml > mypod.yaml
# 补充内容
vim pod1.yaml
apiVersion: v1
kind: Pod
metadata:
  labels:
    run: nginx
  name: nginx
spec:
  containers:
  - image: nginx
    name: nginx
    volumeMounts:
    # 要挂载的卷的名称
    - name: userlist
    # 指定卷在容器内部的挂载路径
      mountPath: /secret
      # 设置挂载的目录为「只读」模式
      readOnly: true
# 定义一个名为userlist的卷
# 卷的数据来源于名为userlist的Secret资源
  volumes:
  - name: userlist
    secret:
      secretName: userlist
# 应用
kubectl apply -f mysecret.yaml
kubectl apply -f mypod.yaml

image-20250817100832729

image-20250817101021941

# 测试
kubectl exec pods/mypod -it -- /bin/bash
cd /secret/ && ls
cat username
cat password

image-20250817101541911

向指定路径映射secret密钥
#向指定路径映射
[root@k8s-master secrets]# vim pod2.yaml
apiVersion: v1
kind: Pod
metadata:
  labels:
    run: nginx1
  name: nginx1
spec:
  containers:
  - image: nginx
    name: nginx1
    volumeMounts:
    - name: secrets
      mountPath: /secret
      # 默认在同一个路径下
      readOnly: true

  volumes:
  - name: secrets
    secret:
      secretName: userlist
      # 将userlist放置到其他路径下
      items:
      - key: username
        path: my-users/username
      - key: password
      	path: my-users/password

[root@k8s-master secrets]# kubectl apply -f pod2.yaml
pod/nginx1 created
[root@k8s-master secrets]# kubectl exec  pods/nginx1 -it -- /bin/bash
root@nginx1:/# cd secret/
root@nginx1:/secret# ls
my-users
root@nginx1:/secret# cd my-users
root@nginx1:/secret/my-users# ls
username
root@nginx1:/secret/my-users# cat username 
将Secret设置为环境变量
[root@k8s-master secrets]# vim pod3.yaml
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: userlist
          key: username
    - name: PASS
      valueFrom:
        secretKeyRef:
          name: userlist
          key: password
  restartPolicy: Never

[root@k8s-master secrets]# kubectl apply -f pod3.yaml

[root@k8s-master secrets]# kubectl logs pods/busybox
...
USERNAME=timinglee
...
PASS=lee
...
存储docker registry的认证信息

建立私有仓库并上传镜像

当拉取仓库中私有目录内的镜像时,需要登录后,才能进行下载

拉取时,应使用全路径,比如"reg.timinglee.org/lee/nginx"

# 未登录时,Pod无法拉取私有仓库中的镜像
#登陆仓库
[root@k8s-master secrets]# docker login  reg.timinglee.org

#上传镜像
[root@k8s-master secrets]# docker tag timinglee/game2048:latest  reg.timinglee.org/timinglee/game2048:latest
[root@k8s-master secrets]# docker push reg.timinglee.org/timinglee/game2048:latest

#建立用于docker认证的secret
[root@k8s-master secrets]# kubectl create secret docker-registry docker-auth --docker-server ooovooo.org --docker-username admin --docker-password aaa --docker-email ooovooo.org@admin
secret/docker-auth created
# docker-auth:名称
# --docker-server:需要认证的仓库
# --docker-username:用户名
# --docker-password:用户密码
# --docker-email:邮件
# 配置
[root@k8s-master secrets]# vim pod3.yml
apiVersion: v1
kind: Pod
metadata:
  labels:
    run: game2048
  name: game2048
spec:
  containers:
  - image: ooovooo.org/library/game2048:latest
    name: game2048
  imagePullSecrets:
  # 加载认证文件
  - name: docker-auth

[root@k8s-master secrets]# kubectl get pods
NAME       READY   STATUS    RESTARTS   AGE
game2048   1/1     Running   0          4s

volumes

官网:https://kubernetes.io/zh/docs/concepts/storage/volumes/
解决容器生命周期与数据生命周期不一致的问题

仅属于某个具体的 Pod,与 Pod 生命周期绑定(Pod 删除,卷也会被清理,除非使用持久化存储)

当在一个Pod中同时运行多个容器时,常常需要在这些容器之间共享文件

卷不能挂载到其他卷,也不能与其他卷有硬链接

Pod中的每个容器必须独立地指定每个卷的挂载位置

卷比Pod中运行的任何容器的存活期都长,在容器重新启动时数据也会得到保留

当一个Pod不再存在时,卷也将不再存在

卷是 Pod 内的「存储接口」,而 PersistentVolume 是集群级的「持久化存储资源」

卷的类型

k8s支持的卷的类型如下:

  • awsElasticBlockStore 、azureDisk、azureFile、cephfs、cinder、configMap、csi

  • downwardAPI、emptyDir、fc (fibre channel)、flexVolume、flocker

  • gcePersistentDisk、gitRepo (deprecated)、glusterfs、hostPath、iscsi、local、

  • nfs、persistentVolumeClaim、projected、portworxVolume、quobyte、rbd

  • scaleIO、secret、storageos、vsphereVolume

emptyDir卷

当Pod指定到某个节点上时,首先创建的是一个emptyDir卷,并且只要 Pod 在该节点上运行,卷就一直存在。卷最初是空的。 尽管 Pod 中的容器挂载 emptyDir 卷的路径可能相同也可能不同,但是这些容器都可以读写 emptyDir 卷中相同的文件。 当 Pod 因为某些原因被从节点上删除时,emptyDir 卷中的数据也会永久删除

# 创建yaml
kubectl run empty --image nginx --dry-run=client -o yaml > empty.yaml
# kubectl run empty --image nginx --image busybox --dry-run=client -o yaml > empty.yaml
# 修改yaml
vim empty.yaml
apiVersion: v1
kind: Pod
metadata:
  labels:
    run: empty
  name: empty
spec:
  containers:
  - image: busyboxplus
    name: busybox
    command:
      - /bin/sh
      - -c
      - sleep 1000000
    volumeMounts:
    - name: empty
      mountPath: /data

  - image: nginx
    name: nginx
    volumeMounts:
    - name: empty
      mountPath: /usr/share/nginx/html
      
  volumes:
  - name: empty
    emptyDir:
      medium: Memory
      sizeLimit: 100Mi
# 应用yaml
kubectl apply -f empty.yaml
# 查看挂载情况
kubectl describe pods empty
Image:		busyboxplus
Mounts:		/data from empty (rw)
---
Image:		nginx
Mounts:		/usr/share/nginx/html from empty (rw)
# 
kubectl delete -f empty --force

image-20250817110218067
image-20250817110350621
image-20250817110427296

# 测试
# 进入nginx
kubectl exec -it pods/empty -c nginx -- /bin/bash
cd /usr/share/nginx/html && ls
echo "hello busybox" > index.html
# 成功访问
curl localhost
---
# 多开终端,进入busybox中
kubectl exec -it pods/empty -c busybox -- /bin/bash
# 同步文件,成功访问
ls /data/ && curl localhost
# 创建文件,被限制大小(在busybox中好像没被限制???)
dd if=/dev/zero of=bigfile bs=1M count=200
du -sh bigfile
# 仅在挂载目录中同步,其他目录不进行同步
cd / && echo 1 > ono.txt && ls
---
# 在nginx中查看
ls /

image-20250817111002765

hostpath卷

hostPath 卷能将主机节点文件系统上的文件或目录挂载到您的 Pod 中,不会因为pod关闭而被删除
hostPath的安全隐患

  • 具有相同配置(例如从 podTemplate 创建)的多个 Pod 会由于节点上文件的不同而在不同节点上有不同的行为
  • 当 Kubernetes 按照计划添加资源感知的调度时,这类调度机制将无法考虑由 hostPath 使用的资源
  • 基础主机上创建的文件或目录只能由 root 用户写入。您需要在 特权容器 中以 root 身份运行进程,或者修改主机上的文件权限以便容器能够写入 hostPath 卷
[root@k8s-master volumes]# vim 002.yml
apiVersion: v1
kind: Pod
metadata:
  name: vol2
spec:
  containers:
  - image: nginx
    name: vm2
    volumeMounts:
    - mountPath: /usr/share/nginx/html
      name: cache-vo2
  volumes:
  - name: cache-vo2
    hostPath:
      path: /data	# 把/data 挂载到 /usr/share/nginx/html 中
      type: DirectoryOrCreate				#当/data目录不存在时自动建立

#测试:
[root@k8s-master volumes]# kubectl apply -f 002.yml
pod/vol2 created
[root@k8s-master volumes]# kubectl get  pods vol2 -o wide
NAME   READY   STATUS    RESTARTS   AGE   IP            NODE        NOMINATED NODE   READINESS GATES
vol1   1/1     Running   0          10s   10.244.2.48   k8s-node2   <none>           <none>

[root@k8s-master volumes]# curl  10.244.2.48
<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx/1.27.1</center>
</body>
</html>

# 在镜像内/usr/share/nginx/html 创建文件 也会同步到 /data目录中
# 在vol1 的工作节点上 自动创建/data目录
[root@k8s-node2 ~]# echo timinglee > /data/index.html
[root@k8s-master volumes]# curl  10.244.2.48
timinglee

#当pod被删除后hostPath不会被清理
[root@k8s-master volumes]# kubectl delete -f pod2.yml
pod "vol1" deleted
[root@k8s-node2 ~]# ls /data/
index.html

nfs卷

NFS 卷允许将一个现有的 NFS 服务器上的目录挂载到 Kubernetes 中的 Pod 中。这对于在多个 Pod 之间共享数据或持久化存储数据非常有用

# 在所有主机上安装nfs-utils(包括harbor)
dnf install nfs-utils -y
# 将harbor仓库作为nfs服务器
mkdir /nfsdata
systemctl enable --now nfs-server
# 配置共享目录
vim /etc/exports
/nfsdata   *(rw,sync,no_root_squash)
# 刷新并显示共享目录
exportfs -rv
# 在其他主机上挂载
showmount -e 172.25.254.200
kubectl run nfs --image nginx --dry-run=client -o yaml > nfs.yaml
vim nfs.yaml
apiVersion: v1
kind: Pod
metadata:
  labels:
    run: nfs
  name: nfs
spec:
  containers:
  - image: nginx
    name: nginx
    volumeMounts:
    - mountPath: /usr/share/nginx/html
      name: nfs

  volumes:
  - name: nfs
    nfs:
      server: 172.25.254.200
      path: /nfsdata
kubectl apply -f nfs.yaml
kubectl get pods -o wide
curl 10.244.8.14
403 Forbidden
# 在NFS主上
echo hello nginx > /nfsdata/index.html
curl 10.244.8.14

PersistentVolume持久卷

PersistentVolume(持久卷,简称PV)
  • pv是集群内由管理员提供的网络存储的一部分。

  • PV也是集群中的一种资源。是一种volume插件,

  • 但是它的生命周期却是和使用它的Pod相互独立的。

  • PV这个API对象,捕获了诸如NFS、ISCSI、或其他云存储系统的实现细节

  • pv有两种提供方式:静态和动态

    • 静态PV:集群管理员创建多个PV,它们携带着真实存储的详细信息,它们存在于Kubernetes API中,并可用于存储使用

    • 动态PV:当管理员创建的静态PV都不匹配用户的PVC时,集群可能会尝试专门地供给volume给PVC。这种供给基于StorageClass

PersistentVolumeClaim(持久卷声明,简称PVC)
  • 是用户的一种存储请求

  • 它和Pod类似,Pod消耗Node资源,而PVC消耗PV资源

  • Pod能够请求特定的资源(如CPU和内存)。PVC能够请求指定的大小和访问的模式持久卷配置

  • PVC与PV的绑定是一对一的映射。没找到匹配的PV,那么PVC会无限期得处于unbound未绑定状态

volumes访问模式
  • ReadWriteOnce – 该volume只能被单个节点以读写的方式映射

  • ReadOnlyMany – 该volume可以被多个节点以只读方式映射

  • ReadWriteMany – 该volume可以被多个节点以读写的方式映射

  • 在命令行中,访问模式可以简写为:

    • RWO - ReadWriteOnce

    • ROX - ReadOnlyMany

    • RWX – ReadWriteMany

volumes回收策略
  • Retain:保留,需要手动回收

  • Recycle:回收,自动删除卷中数据(在当前版本中已经废弃)

  • Delete:删除,相关联的存储资产,如AWS EBS,GCE PD,Azure Disk,or OpenStack Cinder卷都会被删除

注意:

[!NOTE]

只有NFS和HostPath支持回收利用

AWS EBS,GCE PD,Azure Disk,or OpenStack Cinder卷支持删除操作。

volumes状态说明
  • Available 卷是一个空闲资源,尚未绑定到任何申领

  • Bound 该卷已经绑定到某申领

  • Released 所绑定的申领已被删除,但是关联存储资源尚未被集群回收

  • Failed 卷的自动回收操作失败

静态PV

#在nfs主机中建立实验目录
[root@reg ~]# mkdir  /nfsdata/pv{1..3}

#编写创建pv的yml文件,pv是集群资源,不在任何namespace中
[root@k8s-master pvc]# vim pv.yml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv1
spec:
  capacity:
    storage: 5Gi
  volumeMode: Filesystem
  accessModes:
  - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  storageClassName: nfs
  nfs:
    path: /nfsdata/pv1
    server: 172.25.254.100

---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv2
spec:
  capacity:
    storage: 15Gi
  volumeMode: Filesystem
  accessModes:
  - ReadWriteMany
  persistentVolumeReclaimPolicy: Retain
  storageClassName: nfs
  nfs:
    path: /nfsdata/pv2
    server: 172.25.254.100
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv3
spec:
  capacity:
    storage: 25Gi
  volumeMode: Filesystem
  accessModes:
  - ReadOnlyMany
  persistentVolumeReclaimPolicy: Retain
  storageClassName: nfs
  nfs:
    path: /nfsdata/pv3
    server: 172.25.254.100

[root@k8s-master pvc]# kubectl get  pv
NAME   CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM   STORAGECLASS   VOLUMEATTRIBUTESCLASS   REASON   AGE
pv1    5Gi        RWO            Retain           Available           nfs            <unset>                          4m50s
pv2    15Gi       RWX            Retain           Available           nfs            <unset>                          4m50s
pv3    25Gi       ROX            Retain           Available           nfs            <unset>                          4m50s

#建立pvc,pvc是pv使用的申请,需要保证和pod在一个namesapce中
[root@k8s-master pvc]# 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@k8s-master pvc]# kubectl get pvc
NAME   STATUS   VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   VOLUMEATTRIBUTESCLASS   AGE
pvc1   Bound    pv1      5Gi        RWO            nfs            <unset>                 5s
pvc2   Bound    pv2      15Gi       RWX            nfs            <unset>                 4s
pvc3   Bound    pv3      25Gi       ROX            nfs            <unset>                 4s

#在其他namespace中无法应用
[root@k8s-master pvc]# kubectl -n kube-system  get pvc
No resources found in kube-system namespace.

在pod中使用pvc

[root@k8s-master pvc]# vim pod.yml
apiVersion: v1
kind: Pod
metadata:
  name: zhang3
spec:
  containers:	
  - image: nginx
    name: nginx
    volumeMounts:
    - mountPath: /usr/share/nginx/html
      name: vol1
  volumes:
  - name: vol1
    persistentVolumeClaim:
      claimName: pvc1

[root@k8s-master pvc]# kubectl get pods  -o wide
NAME        READY   STATUS    RESTARTS   AGE   IP            NODE        NOMINATED NODE   READINESS GATES
timinglee   1/1     Running   0          83s   10.244.2.54   k8s-node2   <none>           <none>
[root@k8s-master pvc]# kubectl exec -it pods/timinglee -- /bin/bash
root@timinglee:/# curl  localhost
<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx/1.27.1</center>
</body>
</html>
root@timinglee:/# cd /usr/share/nginx/
root@timinglee:/usr/share/nginx# ls
html
root@timinglee:/usr/share/nginx# cd html/
root@timinglee:/usr/share/nginx/html# ls

[root@reg ~]# echo timinglee > /data/pv1/index.html

[root@k8s-master pvc]# kubectl exec -it pods/timinglee -- /bin/bash
root@timinglee:/# cd /usr/share/nginx/html/
root@timinglee:/usr/share/nginx/html# ls
index.html

存储类storageclass

官网: https://github.com/kubernetes-sigs/nfs-subdir-external-provisioner

  • StorageClass提供了一种描述存储类(class)的方法,不同的class可能会映射到不同的服务质量等级和备份策略或其他策略等。

  • 每个 StorageClass 都包含 provisioner、parameters 和 reclaimPolicy 字段, 这些字段会在StorageClass需要动态分配 PersistentVolume 时会使用到

StorageClass的属性

属性说明:https://kubernetes.io/zh/docs/concepts/storage/storage-classes/

Provisioner(存储分配器):用来决定使用哪个卷插件分配 PV,该字段必须指定。可以指定内部分配器,也可以指定外部分配器。外部分配器的代码地址为: kubernetes-incubator/external-storage,其中包括NFS和Ceph等。

Reclaim Policy(回收策略):通过reclaimPolicy字段指定创建的Persistent Volume的回收策略,回收策略包括:Delete 或者 Retain,没有指定默认为Delete。

存储分配器NFS Client Provisioner

源码地址:https://github.com/kubernetes-sigs/nfs-subdir-external-provisioner

  • NFS Client Provisioner是一个automatic provisioner,使用NFS作为存储,自动创建PV和对应的PVC,本身不提供NFS存储,需要外部先有一套NFS存储服务。

  • PV以 n a m e s p a c e − {namespace}- namespace{pvcName}-${pvName}的命名格式提供(在NFS服务器上)

  • PV回收的时候以 archieved- n a m e s p a c e − {namespace}- namespace{pvcName}-${pvName} 的命名格式(在NFS服务器上)

部署NFS Client Provisioner

创建SA并授权
vim rbac.yaml
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
# 应用yaml
kubectl apply -f rbac.yaml
# 查看
kubectl -n nfs-client-provisioner get sa
上传镜像
# 加载
docker load -i nfs-subdir-external-provisioner-4.0.2.tar
# 打标签
docker tag sig-storage/nfs-subdir-external-provisioner:v4.0.2 ooovooo.org/sig-storage/nfs-subdir-external-provisioner:v4.0.2
# 上传镜像
docker push ooovooo.org/sig-storage/nfs-subdir-external-provisioner:v4.0.2
创建NFS动态存储卷供应器
vim deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nfs-client-provisioner
  labels:
    app: 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.200
            - name: NFS_PATH
              value: /nfsdata
      volumes:
        - name: nfs-client-root
          nfs:
            server: 172.25.254.200
            path: /nfsdata
# 应用ymal
kubectl apply -f storageclass.yaml
# 查看
kubectl -n nfs-client-provisioner get deployments.apps nfs-client-provisioner
创建存储类
vim storageclass.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: nfs-client
provisioner: k8s-sigs.io/nfs-subdir-external-provisioner
parameters:
  archiveOnDelete: "false"
# 应用yaml
kubectl apply -f class.yaml
# 查看
kubectl get storageclasses.storage.k8s.io
创建 PVC
vim storagepvc.yaml
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: test-claim
spec:
  storageClassName: nfs-client
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 1G
# 应用yaml
kubectl apply -f storagepvc.yaml
# 查看
kubectl get pvc
自动创建 PV
# 测试
vim testpod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: test-pod
spec:
  containers:
  - name: test-pod
    image: busybox
    command:
      - "/bin/sh"
    args:
      - "-c"
      - "touch /mnt/SUCCESS && exit 0 || exit 1"
    volumeMounts:
      - name: nfs-pvc
        mountPath: "/mnt"
  restartPolicy: "Never"
  volumes:
    - name: nfs-pvc
      persistentVolumeClaim:
        claimName: test-claim
# 应用yaml
kubectl apply -f teestpod.yaml
# 验证(NFS)
cd /nfsdata/ && ls
ls default-......
自动删除PV
# 查看pv
kubectl get pv
# 解除占用
kubectl delete -f teestpod.yaml
# 删除pvc
kubectl delete pvc test-claim
# 再次查看
kubectl get pv

statefulset控制器

  • Statefulset是为了管理有状态服务的问提设计的

  • StatefulSet将应用状态抽象成了两种情况:

    • 拓扑状态:应用实例必须按照某种顺序启动。新创建的Pod必须和原来Pod的网络标识一样

    • 存储状态:应用的多个实例分别绑定了不同存储数据。

  • StatefulSet给所有的Pod进行了编号,编号规则是: ( s t a t e f u l s e t 名称 ) − (statefulset名称)- (statefulset名称)(序号),从0开始。

  • Pod被删除后重建,重建Pod的网络标识也不会改变,Pod的拓扑状态按照Pod的“名字+编号”的方式固定下来,并且为每个Pod提供了一个固定且唯一的访问入口,Pod对应的DNS记录

StatefulSet的组成部分
  • Headless Service:用来定义pod网络标识,生成可解析的DNS记录

  • volumeClaimTemplates:创建pvc,指定pvc名称大小,自动创建pvc且pvc由存储类供应。

  • StatefulSet:管理pod的

#建立无头服务
[root@k8s-master statefulset]# vim headless.yml
apiVersion: v1
kind: Service
metadata:
 name: nginx-svc
 labels:
  app: nginx
spec:
 ports:
 - port: 80
   name: web
 clusterIP: None
 selector:
  app: nginx
[root@k8s-master statefulset]# kubectl apply -f headless.yml

#建立statefulset
[root@k8s-master statefulset]# vim statefulset.yml
apiVersion: apps/v1
kind: StatefulSet
metadata:
 name: web
spec:
 serviceName: "nginx-svc"
 replicas: 3
 # 副本数量
 selector:
  matchLabels:
   app: nginx
 template:
  metadata:
   labels:
    app: nginx
  spec:
   containers:
   - name: nginx
     image: nginx
     volumeMounts:
       - name: www
         mountPath: /usr/share/nginx/html
 volumeClaimTemplates:
  - metadata:
     name: www
    spec:
     storageClassName: nfs-client
     accessModes:
     - ReadWriteOnce
     resources:
      requests:
       storage: 1Gi
[root@k8s-master statefulset]# kubectl apply -f statefulset.yml
statefulset.apps/web configured
root@k8s-master statefulset]# kubectl get pods
NAME    READY   STATUS    RESTARTS   AGE
web-0   1/1     Running   0          3m26s
web-1   1/1     Running   0          3m22s
web-2   1/1     Running   0          3m18s


[root@reg nfsdata]# ls /nfsdata/
default-test-claim-pvc-34b3d968-6c2b-42f9-bbc3-d7a7a02dcbac
default-www-web-0-pvc-0390b736-477b-4263-9373-a53d20cc8f9f
default-www-web-1-pvc-a5ff1a7b-fea5-4e77-afd4-cdccedbc278c
default-www-web-2-pvc-83eff88b-4ae1-4a8a-b042-8899677ae854

测试

#为每个pod建立index.html文件

[root@reg nfsdata]# echo web-0 > default-www-web-0-pvc-0390b736-477b-4263-9373-a53d20cc8f9f/index.html
[root@reg nfsdata]# echo web-1 > default-www-web-1-pvc-a5ff1a7b-fea5-4e77-afd4-cdccedbc278c/index.html
[root@reg nfsdata]# echo web-2 > default-www-web-2-pvc-83eff88b-4ae1-4a8a-b042-8899677ae854/index.html

#建立测试pod访问web-0~2
[root@k8s-master statefulset]# kubectl run -it testpod --image busyboxplus
/ # curl  web-0.nginx-svc
web-0
/ # curl  web-1.nginx-svc
web-1
/ # curl  web-2.nginx-svc
web-2

#删掉重新建立statefulset
[root@k8s-master statefulset]# kubectl delete -f statefulset.yml
statefulset.apps "web" deleted
[root@k8s-master statefulset]# kubectl apply  -f statefulset.yml
statefulset.apps/web created

#访问依然不变
[root@k8s-master statefulset]# kubectl attach testpod -c testpod -i -t
If you don't see a command prompt, try pressing enter.
/ # cu
curl  cut
/ # curl  web-0.nginx-svc
web-0
/ # curl  web-1.nginx-svc
web-1
/ # curl  web-2.nginx-svc
web-2
statefulset的弹缩

首先,想要弹缩的StatefulSet. 需先清楚是否能弹缩该应用

# 用命令改变副本数
kubectl scale statefulsets <stateful-set-name> --replicas=<new-replicas>
# 通过编辑配置改变副本数 
kubectl edit statefulsets.apps <stateful-set-name>
# statefulset有序回收
kubectl scale statefulset web --replicas 0
kubectl delete -f statefulset.yml
kubectl delete pvc --all

kubernetes认证授权

Kubernetes API 访问控制

uthentication(认证)

  • 认证方式现共有8种,可以启用一种或多种认证方式,只要有一种认证方式通过,就不再进行其它方式的认证

    • 通常启用 X509 Client CertsService Accout Tokens 两种认证方式
  • Kubernetes集群有两类用户:

    • 由Kubernetes管理的Service Accounts (服务账户)
    • (Users Accounts) 普通账户
    • k8s中账号的概念不是我们理解的账号,它并不真的存在,它只是形式上存在

Authorization(授权)

  • 必须经过认证阶段,才到授权请求,根据所有授权策略匹配请求资源属性,决定允许或拒绝请求
  • 授权方式现共有6种,AlwaysDeny、AlwaysAllow、ABAC、RBAC、Webhook、Node
  • 默认集群强制开启 RBAC

Admission Control(准入控制)

  • 用于拦截请求的一种方式,运行在认证、授权之后,是权限认证链上的最后一环,对请求API资源对象进行修改和校验

整体结构

在这里插入图片描述

运用

ServiceAccount 运用

建立名字为 moon 的 ServiceAccount

[root@k8s-master ~]# kubectl create sa moon
[root@k8s-master ~]# kubectl describe sa moon
Name:                moon
Namespace:           default
Labels:              <none>
Annotations:         <none>
Image pull secrets:  <none>
# 无内容
Mountable secrets:   <none>
Tokens:              <none>
Events:              <none>

建立 secrets

[root@k8s-master ~]# kubectl create secret docker-registry docker-login --docker-username admin --docker-password aaa --docker-server ooovooo.org --docker-email admin@ooovooo.org
secret/docker-login created
[root@k8s-master ~]# kubectl describe secrets docker-login
Name:         docker-login
Namespace:    default
Labels:       <none>
Annotations:  <none>
Type:  kubernetes.io/dockerconfigjson
Data
====
.dockerconfigjson:  113 bytes

将 secrets 注入到 sa 中

[root@k8s-master ~]# kubectl edit sa moon
apiVersion: v1
imagePullSecrets:
  - name: docker-login
kind: ServiceAccount
...
[root@k8s-master ~]# kubectl describe sa moon
Name:                moon
Namespace:           default
Labels:              <none>
Annotations:         <none>
Image pull secrets:  docker-login
# 注入成功
Mountable secrets:   <none>
Tokens:              <none>
Events:              <none>

创建 Harbor 私有仓库并使用 Pod 访问私有仓库

# Harbor 建立名为 sun 私有仓库
[root@ooovooo harbor]# docker tag ooovooo.org/library/nginx:latest ooovooo.org/sun/nginx:latest
[root@ooovooo harbor]# docker push ooovooo.org/sun/nginx:latest
# 拉取私有仓库镜像 使用绝对路径
[root@k8s-master ~]# kubectl run 001 --image ooovooo.org/sun/nginx --dry-run=client -o yaml > 001.yml
[root@k8s-master ~]# kubectl apply -f 001.yml
[root@k8s-master ~]# kubectl describe pods 001
...
Warning  Failed     14s               kubelet            Error: ImagePullBackOff
Normal   Pulling    0s (x2 over 15s)  kubelet            Pulling image "ooovooo.org/sun/nginx"
Warning  Failed     0s (x2 over 15s)  kubelet            Failed to pull image "ooovooo.org/sun/nginx": Error response from daemon: unauthorized: unauthorized to access repository: sun/nginx, action: pull: unauthorized to access repository: sun/nginx, action: pull
Warning  Failed     0s (x2 over 15s)  kubelet            Error: ErrImagePull

[root@k8s-master ~]# kubectl get pods 001
NAME   READY   STATUS             RESTARTS   AGE
001    0/1     ImagePullBackOff   0          60s

[!warning]

创建 Pod 时,镜像下载错误,其 Docker 私有仓库下载镜像需要认证

Pod 绑定 SA

[root@k8s-master ~]# vim 001.yml
apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    run: "001"
  name: "001"
spec:
  serviceAccountName: moon
  # 绑定
  containers:
  - image: ooovooo.org/sun/nginx
    name: "001"
    resources: {}
  dnsPolicy: ClusterFirst
  restartPolicy: Always
status: {}
[root@k8s-master ~]# kubectl delete -f 001.yml
[root@k8s-master ~]# kubectl apply -f 001.yml
[root@k8s-master ~]# kubectl get pods 001
NAME   READY   STATUS    RESTARTS   AGE
001    1/1     Running   0          60s

[!note]

还可以将 secrets 注入到 sa default 中,这样 Pod 不用绑定 sa moon

认证(在K8s中建立认证用户)

创建 UserAccount

# 建立证书
[root@k8s-master ~]# cd /etc/kubernetes/pki/
[root@k8s-master pki]# openssl genrsa -out moon.key 2048
[root@k8s-master pki]# openssl req -new -key moon.key -out moon.csr -subj "/CN=moon"
[root@k8s-master pki]# openssl x509 -req -in moon.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out moon.crt -days 365
Certificate request self-signature ok
subject=CN = moon
[root@k8s-master pki]# openssl x509 -in moon.crt -text -noout
...
# 建立k8s中的用户
[root@k8s-master pki]# kubectl config set-credentials moon --client-certificate /etc/kubernetes/pki/moon.crt --client-key /etc/kubernetes/pki/moon.key --embed-certs=true
User "moon" set.

[root@k8s-master pki]# kubectl config view
...
- name: moon
  user:
    client-certificate-data: DATA+OMITTED
    client-key-data: DATA+OMITTED

# 为用户创建集群的安全上下文
[root@k8s-master pki]# kubectl config set-context moon@kubernetes --cluster kubernetes --user moon
Context "moon@kubernetes" created.
# 删除安全上下文
# kubectl config delete-context xxx

# 切换用户 用户在集群中只有用户身份而没有授权
[root@k8s-master pki]# kubectl config use-context moon@kubernetes
Switched to context "moon@kubernetes".
[root@k8s-master pki]# kubectl get pods
Error from server (Forbidden): pods is forbidden: User "moon" cannot list resource "pods" in API group "" in the namespace "default"

# 切换回集群管理
[root@k8s-master pki]# kubectl config use-context kubernetes-admin@kubernetes
Switched to context "kubernetes-admin@kubernetes".

# 删除用户
# kubectl config delete-user moon

RBAC(Role Based Access Control)

基于角色访问控制授权:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 允许管理员通过Kubernetes API动态配置授权策略

    • RBAC就是用户通过角色与权限进行关联
  • RBAC只有授权,没有拒绝授权,所以只需要定义允许该用户做什么即可

  • RBAC的三个基本概念

    • Subject:被作用者,它表示k8s中的三类主体, user, group, serviceAccount
    • Role:角色,它其实是一组规则,定义了一组对 Kubernetes API 对象的操作权限
    • RoleBinding:定义了“被作用者”和“角色”的绑定关系
  • RBAC包括四种类型:Role、ClusterRole、RoleBinding、ClusterRoleBinding

  • Role 和 ClusterRole

    • Role是一系列的权限的集合,Role只能授予单个namespace 中资源的访问权限

    • ClusterRole 跟 Role 类似,但是可以在集群中全局使用

    • Kubernetes 还提供了四个预先定义好的 ClusterRole 来供用户直接使用

    • cluster-amdin、admin、edit、view

Role 授权应用

# 生成role的yaml文件
[root@k8s-master ~]# kubectl create role myrole --dry-run=client --verb=get --resource pods -o yaml > myrole.yml

# 更改文件内容
[root@k8s-master ~]# vim myrole.yml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  creationTimestamp: null
  name: myrole
rules:
- apiGroups:
  - ""
  resources:
  - pods
  verbs:
  - get
  - watch
  - list
  - create
  - update
  - path
  - delete

# 创建role
[root@k8s-master ~]# kubectl apply -f myrole.yml
role.rbac.authorization.k8s.io/myrole created
[root@k8s-master ~]# kubectl describe role myrole
Name:         myrole
Labels:       <none>
Annotations:  <none>
PolicyRule:
  Resources  Non-Resource URLs  Resource Names  Verbs
  ---------  -----------------  --------------  -----
  pods       []                 []              [get watch list create update path delete]
# 建立角色绑定
[root@k8s-master ~]# kubectl create rolebinding moon --role myrole --namespace default --user moon --dry-run=client -o yaml > rolebinding-myrole.yml

[root@k8s-master ~]# vim rolebinding-myrole.yml

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: moon
  namespace: default
  # 角色绑定必须指定 namespace
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: myrole
subjects:
- apiGroup: rbac.authorization.k8s.io
  kind: User
  name: moon

[root@k8s-master ~]# kubectl apply -f rolebinding-myrole.yml
rolebinding.rbac.authorization.k8s.io/moon created
[root@k8s-master ~]# kubectl get rolebindings.rbac.authorization.k8s.io moon
NAME   ROLE          AGE
moon   Role/myrole   60s
# 切换用户测试授权
[root@k8s-master ~]# kubectl config use-context moon@kubernetes
Switched to context "moon@kubernetes".
[root@k8s-master rbac]# kubectl get pods
[root@k8s-master ~]# kubectl get pods
NAME                       READY   STATUS    RESTARTS   AGE
001                        1/1     Running   0          9m31s
# 只针对pod进行了授权,所以svc依然不能操作
[root@k8s-master rbac]# kubectl get svc
Error from server (Forbidden): services is forbidden: User "moon" cannot list resource "services" in API group "" in the namespace "default"

# 切换回管理员
[root@k8s-master rbac]# kubectl config use-context kubernetes-admin@kubernetes

ClusterRole 授权应用

# 建立集群角色
[root@k8s-master ~]# kubectl create clusterrole myclusterrole --resource=deployment --verb get --dry-run=client -o yaml > myclusterrole.yml
[root@k8s-master ~]# vim myclusterrole.yml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  creationTimestamp: null
  name: myclusterrole
rules:
- apiGroups:
  - apps
  resources:
  - deployments
  verbs:
  - get
  - list
  - watch
  - create
  - update
  - path
  - delete
- apiGroups:
  - ""
  resources:
  - pods
  verbs:
  - get
  - list
  - watch
  - create
  - update
  - path
  - delete

[root@k8s-master ~]# kubectl apply -f myclusterrole.yml
clusterrole.rbac.authorization.k8s.io/myclusterrole created
[root@k8s-master ~]# kubectl describe clusterrole myclusterrole
Name:         myclusterrole
Labels:       <none>
Annotations:  <none>
PolicyRule:
  Resources         Non-Resource URLs  Resource Names  Verbs
  ---------         -----------------  --------------  -----
  pods              []                 []              [get list watch create update path delete]
  deployments.apps  []                 []              [get list watch create update path delete]

# 建立集群角色绑定
[root@k8s-master ~]# kubectl create clusterrolebinding  clusterrolebind-myclusterrole --clusterrole myclusterrole  --user moon --dry-run=client -o yaml > clusterrolebind-myclusterrole.yml
[root@k8s-master ~]# vim clusterrolebind-myclusterrole.yml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: clusterrolebind-myclusterrole
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: myclusterrole
subjects:
- apiGroup: rbac.authorization.k8s.io
  kind: User
  name: moon

[root@k8s-master ~]# kubectl apply -f clusterrolebind-myclusterrole.yml
clusterrolebinding.rbac.authorization.k8s.io/clusterrolebind-myclusterrole created

[root@k8s-master rbac]# kubectl describe clusterrolebindings.rbac.authorization.k8s.io clusterrolebind-myclusterrole
Name:         clusterrolebind-myclusterrole
Labels:       <none>
Annotations:  <none>
Role:
  Kind:  ClusterRole
  Name:  myclusterrole
Subjects:
  Kind  Name  Namespace
  ----  ----  ---------
  User  moon

[root@k8s-master ~]# kubectl config use-context moon@kubernetes

# 用户测试
[root@k8s-master ~]# kubectl get pods
NAME                       READY   STATUS    RESTARTS   AGE
001                        1/1     Running   0          15m
[root@k8s-master rbac]# kubectl get deployments.apps -A
# 没有对svc授权
[root@k8s-master rbac]# kubectl get svc -A
Error from server (Forbidden): services is forbidden: User "moon" cannot list resource "services" in API group "" at the cluster scope

服务账户的自动化

服务账户准入控制器(Service account admission controller)

  • 如果该 pod 没有 ServiceAccount 设置,将其 ServiceAccount 设为 default

  • 保证 pod 所关联的 ServiceAccount 存在,否则拒绝该 pod

  • 如果 pod 不包含 ImagePullSecrets 设置,那么 将 ServiceAccount 中的 ImagePullSecrets 信息添加到 pod 中

  • 将一个包含用于 API 访问的 token 的 volume 添加到 pod 中

  • 将挂载于 /var/run/secrets/kubernetes.io/serviceaccount 的 volumeSource 添加到 pod 下的每个容器中

服务账户控制器(Service account controller)

服务账户管理器管理各命名空间下的服务账户,并且保证每个活跃的命名空间下存在一个名为 “default” 的服务账户

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐