三台机器的IP:
192.168.217.128
192.168.217.129
192.168.217.130

集群节点信息表

主机名IP 地址角色
k8s-master192.168.217.128Master 节点
k8s-node1192.168.217.129Worker 节点
k8s-node2192.168.217.130Worker 节点

更新系统并安装依赖

sudo apt update
sudo apt upgrade -y
sudo apt install -y curl wget vim net-tools ssh

1.安装Docker

# 安装 Docker 所需依赖
sudo apt install -y apt-transport-https ca-certificates curl software-properties-common
# 添加 Docker 官方 GPG 密钥
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
# 添加 Docker 仓库
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo tee /etc/docker/daemon.json <<EOF
{
  "registry-mirrors": [
  ]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker
sudo docker run hello-world

安装 Kubernetes 组件

master 管理端或控制器端
node 真正运行的业务的机器

K8S的微观架构
架构图

MASTER节点:

api server :提供接口;与集群交互的接口会将数据存储到etcd服务器内部。
api server遵循restful风格。
etcd:键值对数据库 key-value 基于Go语言 并使用 RAFT协议。由于RAFT选举算法,所以官方建议etcd的数量为 3579 大于1的奇数个。
scheduler:调度器,获取当前运行的任务和当前节点进行绑定关联。
replication controller/controller manager:控制器,保障整体集群的可用。

Node结点

kublet:通过接口调用Docker等CRI,去完成容器创建、删除。
kube proxy:网络以及负载均衡。
上面两者都是会监听api server

插件

Docker、Ingres controller:7层的负载均衡的功能、coreDNS:私有域名访问
calico:主流网络插件
LVS:四层负载均衡;官方给提供好了service;

附件

Prometheus、dashboard、federation

Pod概念

可以理解为一个容器组
逻辑分组Pod

Pause容器:

Pod内部第一个启动的容器
初识化网络栈
挂载需要的存储卷
回收僵尸进程:1号进程

与Pause容器共享名字空间:network、PID、IPC
共享网络:localhost或者回环访问
共享PID和IPC:保证自己是1号进程

kubeadm
组件通过容器化方式运行
优势:简单、自愈
二进制安装
组件变为系统进程的方式运行

安装虚拟机

使用Rocky Linux
配置 4G内存 100G硬盘 SCSI
系统建议选择中文
选择分区
boot分区 800MB
swap分区 4GB
boot
根分区 剩余全部

设置root账户

环境初始化

网卡配置

# cat /etc/NetworkManager/system-connections/ens160.nmconnection
[ipv4]
method=manual
address1=192.168.66.12/24,192.168.66.200
dns=114.114.114.114;8.8.8.8

# cat /etc/NetworkManager/system-connections/ens192.nmconnection
[connection]
autoconnect=false

调用 nmcli 重启设备和连接配置

nmcli d down ens192
nmcli d disconnect ens160
nmcli c reconnect ens160

之后重启网络设置

systemctl restart networkmanager

在这里插入图片描述

sed -e 's| ^mirrorlist=|#mirrorlist=|g' \
-e 's|^#baseurl=http://dl.rockylinux.org/$contentdir|baseurl=https://mirrors.aliyun.com/rockylinux|g' \
-i.bak \
/etc/yum.repos.d/[Rr]ocky*.repo
dnf makecache
# 防火墙修改 firewalld 为 iptables
systemctl stop firewalld
systemctl disable firewalld
yum -y install iptables-services
systemctl start iptables
#显示所有的规则
iptables -L
#删除所有的规则
iptables -F
#保存规则
service iptables save
#开机自启
systemctl enable iptables

在这里插入图片描述

# 禁用 Selinux
setenforce 0
sed -i "s/SELINUX=enforcing/SELINUX=disabled/g" /etc/selinux/config
grubby --update-kernel ALL --args selinux=0
# 查看是否禁用,grubby -- info DEFAULT
# 回滚内核层禁用操作,grubby -- update-kernel ALL -- remove-args selinux
# 设置时区
timedatectl set-timezone Asia/Shanghai

安装Docker的准备工作
#加载 bridge
#安装epoll源

yum install -y epel-release
yum install -y bridge-utils
#加载一个模块
modprobe br_netfilter
#所有经过网桥的流量都需要被防火墙所处理
#把这个模块添加到开机自启动文件中
echo 'br_netfilter' >> /etc/modules-load.d/bridge.conf
#再在内核配置中添加三个选项
#在ipv4下,所有经过网桥的流量都必须被防火墙所回调
echo 'net.bridge.bridge-nf-call-iptables=1' >> /etc/sysctl.conf
#ipv6下也同样
echo 'net.bridge.bridge-nf-call-ip6tables=1' >> /etc/sysctl.conf
#开启路由转发
echo 'net.ipv4.ip_forward=1' >> /etc/sysctl.conf
#刷新生效
sysctl -p
#添加中科大源
sudo dnf config-manager --add-repo https://mirrors.ustc.edu.cn/docker-ce/linux/centos/docker-ce.repo

cd /etc/yum.repos.d
cat docker-ce.repo
sed -e 's|download.docker.com|mirrors.ustc.edu.cn/docker-ce|g' docker-ce.repo
#把当前替换文件
sed -e 's|download.docker.com|mirrors.ustc.edu.cn/docker-ce|g' docker-ce.repo > docker-ce-ustc.repo
#把官方文档作为备份
mv docker-ce.repo docker-ce.repo.back
#安装Docker-ce
yum -y install docker-ce
cat > /etc/docker/daemon.json <<EOF
{
	"default-ipc-mode":"shareable",
	"data-root":"/data/docker",
	"exec-opts":["native.cgroupdriver=systemd"],
	"log-dirver":"json-file",
	"log-opts":{
		"max-size":"100m",
		"max-file":"100"
	},
	"insecure-registries":["harbor.xinxainghf.com"],
	"registry-mirrors":["https://kfp63jaj.mirror.aliyuncs.com"]
}
EOF
#IPC模式允许共享
#当前Docker的根目录
#启动的额外参数:官方建议制定
#当前的日志驱动,单文件最大大小,保存最大的文件数量
#当前信任的仓库地址,当前官方的镜像仓库地址
#检查语法
jq . /etc/docker/daemon.json
#查最近的条日志
journalctl -u docker.service --no-pager -n 50

mkdir -p /etc/systemd/system/docker.service.d
systemctl daemon-reload && systemctl restart docker 
systemctl enable docker

通过Docker模拟pod

#编写Nginx 配置文件
cat <<EOF>>nginx.conf
error_log stderr;
events { worker_connections 1024;}
http {
access_log /dev/stdout combined;
server {
listen 80 default_server;
server_name example.com www.example.com;
location / {
proxy_pass http://127.0.0.1:2368;
}
}
}
EOF

在这里插入图片描述

#使用轩辕镜像
docker run --name pause -p 8080:80 -d k8s.gcr.io/pause:3.1 

docker run --name nginx -v `pwd`/nginx.conf:/etc/nginx/nginx.conf --net=container:pause --ipc=container:pause --pid=container:pause -d nginx

#要添加数据库,这里使用sqlite3
docker run -d --name ghost --net=container:pause --ipc=container:pause --pid=container:pause -e database__client=sqlite3 -e database__connection__filename=/var/lib/ghost/content/data/ghost.db ghost

-d 后台运行
-v 卷挂载:挂载配置

这三个组件组成了一个pod;使用Pause去共享网络栈、IPC和PID;Pause可以回收僵尸进程;

K8S网络

K8S网络模型假定了所有的Pod都在同一个可以直接连通的扁平的网络空间中。在GCE(Google Computer Engine)中是现成的网络模型。
但对于大部分场景需要自己实现这个网络假设。

网络模型原则

  1. 在不使用网络地址转换(NAT)的情况下,集群中的Pod能与任意的其他Pod通信。
  2. 在不使用NAT情况下,集群节点运行的程序能与同一节点上的任一Pod通信。
  3. 每个Pod都有自己的IP地址,并且任意其他Pod都可以通过相同的地址访问他。

CNI

容器网络接口(CNI)。通过插件化的方式集成各种网络插件,实现集群内部网络相互通信。只要实现CNI标准定义的核心接口操作即可。CNI插件使用在容器到容器的网络通信中。
CNI接口是指对可执行程序的调用可执行程序。kubernetes节点默认的CNI插件路径是 /opt/cni/bin。
CNI通过JSON配置文件来描述网络配置信心。容器运行时负责执行CNI插件,并通过CNI插件的标准输输入传递配置信心,通过标准输出接受插件的执行结果。

网络插件分类

五类:Main、Windows specific、IPAM、Meta、容器网络插件
在这里插入图片描述
PTP:创建一个虚拟以太网对,Veth Pair。一端放到Pod内部(host-device来完成),一端在物理机中。
在这里插入图片描述

提供商网络模型路由分发网络策略网格外部数据存储加密Ingress/Egress 策略
Canal封装 (VXLAN)K8s API
Flannel封装 (VXLAN)K8s API
Calico封装(VXLAN, IPIP)或未封装Etcd 和 K8s API
Weave封装
Cilium封装 (VXLAN)Etcd 和 K8s API
  • 网络模型: 封装或未封装。
  • 路由分发: 一种外部网关协议,用于在互联网上交换路由和可达性信息。BGP 可以帮助进行跨集群 pod 之间的网络。此功能对于未封装的 CNI 网络插件是必须的,并且通常由 BGP 完成。如果你想构建跨网段拆分的集群,路由分发是一个很好的功能。
  • 网络策略: Kubernetes 提供了强制执行规则的功能,这些规则决定了哪些 service 可以使用网络策略进行相互通信。这是从 Kubernetes 1.7 起稳定的功能,可以与某些网络插件一起使用。
  • 网格: 允许在不同的 Kubernetes 集群间进行 service 之间的网络通信。
  • 外部数据存储: 具有此功能的 CNI 网络插件需要一个外部数据存储来存储数据。
  • 加密: 允许加密和安全的网络控制和数据平面。
  • Ingress/Egress 策略: 允许你管理 Kubernetes 和非 Kubernetes 通信的路由控制。
    网络模型:
    overlay和underlay:看报文是否被封装。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

calico架构

calico架构图

  1. Felix
    这个是网络信息实际落地的组件。
    四个功能:编写网络结构、路由、ACL和报告状态。
  2. bird
    非封装网络的实现组件。
    BGP client将通过BGP协议广播告诉剩余calico节点,从而实现网络互通。
  3. confd
    读取配置组件。
    通过监听etcd以了解BGP配置和默认值的更改。confd根据ETCD中的数据更新,动态生成BIRD配置文件,当配置文件更改时,confd触发BIRD重新加载新的配置。

通过以上三种组件就实现了网络扁平化的一种架构。

calico 网络模式-VXLAN

首先什么是VXLAN?VXLAN是Virtual Extensible LAN(虚拟可扩展局域网),是Linux本身支持的网络虚拟化技术。VXLAN可以完全在内核态试下封装和解封装工作,从而通过“隧道”机制(加一个外围),构建出覆盖网络(Overlay Network)。这个网络模式和Flannel是不同的,Flannel在用户态启动了一个用户线程Flanneld来实现封装和解封装的工作,用户空间和内核空间在数据交换是会发生拷贝,影响性能。

  • 特性:基于三层的“二层”通信
    VXLAN包封装在UDP数据包中,要求UDP在K8s节点间三层可达。
    二层可达网络:工作于数据链路层,基于Mac地址实现通信。
    三层可达网络:工作于网络层,基于IP地址实现通信。
    下面是两节点通信过程,正常物理机持有局域网IP+UDP协议过交换机进行通信,而Pod间正是基于VXLAN,虚拟出一个局域网,各个Pod的网络信息使用ETCD来做存储。并且通过PTP插件,实现一个虚拟以太网对,一端在物理机中,一端通过host-device插件移入到Pod中。Pod间通过原始报文进行通信,然后通过VXLAN封装,借助物理机通信信道与部署在其他物理机节点的Pod进行通信。
    VXLAN结构通信实现图
    下面是数据包的封包模型:
    在这里插入图片描述
    总结;
    在这里插入图片描述
    这里的FDB表示Mac和IP的对照表。
Colico配置VXLAN

在这里插入图片描述
在这里插入图片描述
会看到不使用BGP协议来获取节点间的邻接关系。

calico网络模式-IPIP

在这里插入图片描述
通信模式示意图:

通信封包如图所示:
在这里插入图片描述
IPIP模式的优缺点:可以跨IPV4和IPV6网络。
在这里插入图片描述

calico配置IPIP

在这里插入图片描述

calico网络模式-BGP

在这里插入图片描述
矢量路由协议:在协议里会有距离的概念,使用最短路径。
在这里插入图片描述
通过路由记录实现跨机器的通信。这样就相当于实现了非封装网络。所有的路由器均需要开启BGP协议。
优缺点如下图:
在这里插入图片描述

calico配置BGP

在这里插入图片描述

Kubernetes安装

Kubeadm:组件通过容器化方式运行
一主两从架构
二进制安装:组件变为系统进程的方式运行

网络配置:
关闭第二张网卡:autoconnection=false
添加网关:192.168.253.200
添加DNS:1114.114.114.114;8.8.8.8

使用nmcli重启设备和连接配置

nmcli d d ens192
nmcli d r ens160
nmcli c r ens160
# 关闭 swap 分区
swapoff -a
#注释掉分区挂载命令
sed -i 's:/dev/mapper/rl-swap:#/dev/mapper/rl-swap:g' /etc/fstab
#修改主机名
hostnamectl set-hostname k8s-node01

#修改hosts文件
192.168.253.11 k8s-master01 m1
192.168.253.12 k8s-node01 n1
192.168.253.13 k8s-node02 n2
192.168.253.14 harbor
#安装ipvs
yum install -y ipvsadm
#开启路由转发
echo 'net.ipv4.ip_forward=1' >> /etc/sysctl.conf
#刷新生效
sysctl -p
#直至安装Docker

CRI关系图

CRI-docker OCRI 开发容器运行时接口
CRI RKT Podman Containerd

tar -zxvf cri-dockerd-0.3.9.amd64.tgz
scp /usr/bin/cri-dockerd root@n1:/usr/bin/
scp /usr/bin/cri-dockerd root@n2:/usr/bin/
chmod a+x /usr/bin/cri-dockerd
cat << "EOF" > /usr/lib/systemd/system/cri-docker.service
[Unit]
Description=CRI Interface for Docker Application Container Engine
Documentation=https://docs.mirantis.com
After=network-online.target firewalld.service docker.service
Wants=network-online.target
Requires=cri-docker.socket
[Service]
Type=notify
ExecStart=/usr/bin/cri-dockerd --network-plugin=cni --pod-infra-container-image=registry.aliyuncs.com/google_containers/pause:3.8
ExecReload=/bin/kill -s HUP $MAINPID
TimeoutSec=0
RestartSec=2
Restart=always
StartLimitBurst=3
StartLimitInterval=60s
LimitNOFILE=infinity
LimitNPROC=infinity
LimitCORE=infinity
TasksMax=infinity
Delegate=yes
KillMode=process
[Install]
WantedBy=multi-user.target
EOF
# 添加 cri-docker 套接字
cat << "EOF" > /usr/lib/systemd/system/cri-docker.socket
[Unit]
Description=CRI Docker Socket for the API
PartOf=cri-docker.service
[Socket]
ListenStream=%t/cri-dockerd.sock
SocketMode=0660
SocketUser=root
SocketGroup=docker
[Install]
WantedBy=sockets.target
EOF
# 启动 cri-docker 对应服务
systemctl daemon-reload
systemctl enable cri-docker
systemctl start cri-docker
systemctl is-active cri-docker
# 添加 kubeadm yum 源
cat <<EOF > /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://pkgs.k8s.io/core:/stable:/v1.29/rpm/
enabled=1
gpgcheck=1
gpgkey=https://pkgs.k8s.io/core:/stable:/v1.29/rpm/repodata/repomd.xml.key
EOF

# 安装 kubeadm 1.29 版本
yum install -y kubelet-1.29.0 kubectl-1.29.0 kubeadm-1.29.0
#开机自启
systemctl enable kubelet.service

只靠Docker无法保证k8s所有的系统组件正常启动。
kubelet需要以系统进程安装,并且设置为开机自启动

# 初始化主节点
kubeadm init --apiserver-advertise-address=192.168.253.11 --image-repository registry.aliyuncs.com/google_containers --kubernetes-version 1.29.0 --service-cidr=10.10.0.0/12 --pod-network-cidr=10.244.0.0/16 --ignore-preflight-errors=all --cri-socket unix:///var/run/cri-dockerd.sock

# work token 过期后,重新申请
kubeadm token create --print-join-command

# worker JAX
kubeadm join 192.168.253.11:6443 --token 7jtr4f.whp4k2cwjwibjjwb \
        --discovery-token-ca-cert-hash sha256:27ccc494c65e21aa0c913bc065f13b2f6722b993a664c3339710fb3da307f563 --cri-socket unix:///var/run/cri-dockerd.sock

在这里插入图片描述

docker load -i  cni/node/kube-controllers
#发送文件夹到其他节点
scp -r 
#将calico导入到Docker中
docker load -i /root/release-v3.30.3/images/calico-cni.tar
docker load -i /root/release-v3.30.3/images/calico-node.tar
docker load -i /root/release-v3.30.3/images/calico-kube-controllers.tar
docker load -i /root/release-v3.30.3/images/calico-typha.tar
#修改配置
CALICO_IPV4POOL_CIDR
CALICO_IPV4POOL_IPIP
#执行kubernetes命令
kubectl apply -f calico-typha.yaml

资源清单

资源类别

名称空间级别
工作负载型资源:Pod、ReplicaSet、Deployment
服务发现及负载均衡资源:Service、Ingress
配置与存储型资源:Volume、CSI
特殊类型的存储卷:ConfigMap、Secre
集群级资源
Namespace、Node、ClusterRole、ClusterRoleBinding
元数据型资源
HPA、PodTemplate、LimitRange

资源清单编写

在这里插入图片描述
apiVersion:接口组/版本
kind:类别
metadata:元数据
spec:期望
status:转态

apiVersion: vl
kind: Pod
metadata:
  name: pod-demo
  namespace: default
  labels:
    app:myapp
spec:
  containers:
  - name: myapp-1
    image: wangyanglinux/myapp:vl.0
  - name: busybox-1
    image: wangyanglinux/tools:busybox
    command:
    - "/bin/sh"
    - "-c"
    - "sleep 3600"
kubectl create -f 1.pod.yaml

-f 基于文件进行创建

kubectl常用命令

#创建service
kubectl create svc clusterip myservice --tcp=80:80

#查看全部命名空间下的Pod
kubectl get pod -A
kubectl get pod --all-namespace
	-n 指定命名空间,默认值是default
	--show-labels 查看当前标签
	-l 筛选 key/key=value
	-o wide 详细信息
	-o yaml 资源对象的所有信息
#查看Pod的更多信息
kubectl get pod -o wide
#查看当前运行的全部docker
docker ps -a
#进入到docker中 
docker exec -it k8s_coredns_coredns-857d9ff4c9-mjgtp_kube-system_b7127858-1124-4aeb-b50a-ebde51de4d0d_3 /usr/
#k8s进入容器 -i 表示交互模式 t表示打开TTY终端 node名称 -c表示制定当前的容器名 容器名称 -- 分隔符 后面跟命令
kubectl exec -it pod-demo -c myapp-1 -- /bin/bash
	-c 可以省略,默认进入唯一的容器内部

#查看资源的描述情况
kubectl explain pod.spec

#查看当前pod内部容器的日志
kubectl logs podName -c CName

#查看更详细的Pod信息
kubectl describe pod podName
#在Events 事件标签下

#删除资源对象
kubectl delete pod podName/--all
kubectl delete svc --all

#获取部署清单命令
kubectl create deployment myapp --image=docker/myapp:v1.0 --dry-run -o yaml >  deployment.yaml.temp

Pod的生命周期

Pause容器:
intiC -> mainC
intic 阻塞特性、线性执行、回滚特性、返回码必须为0;
所以一些具有安全风险的操作可以放在这里做执行。
mianC 并发执行
钩子方法:当前Pod所在节点的kubelet执行的
启动后钩子:在初始化之后,执行。但并不保证和MainC启动命令的执行顺序。
关闭前钩子:关闭前运行方法。执行后释放关闭信号。

探针方法:当前的Pod节点的kubelet执行的
就绪探测:会有一定的延迟时间。,就绪探测是伴随整个的生命周期的,之前不是。
存活探测:如果死了就会销毁并且重建容器。
启动探测:就绪探测和存活探测都会在启动探测之后。

就绪探测:Pod内部的容器,不添加就绪探测,默认就绪,如果添加了就绪探测,只有就绪通过才标记为就绪状态。当Pod中所有的容器都就绪,Pod才会被标记为就绪状态。如果就绪探针探测失败或者未知,就静默不做处理,维持之前的标记状态。

存活探测:(livenessProbe)
如果Pod内部不指定存活探测,可能会发生容器运行但是无法提供服务的情况。
成功:静默
失败:重建容器
未知:静默
HTTP GET 请求进行探测;

启动探测(startupProbe)

保障存活探针在执行时,不会因为启动时间问题,一致死亡重启。

成功:允许存活探针,就绪探测开始执行
失败:静默

Pod的调度运行 Pod控制器

控制器定义:保证集群的当前状态和期望状态一致。
ReplicaSet控制器负责维护集群中运行的Pod数量
Node控制器负责监控节点状态,并在节点故障时,执行自动化修复流程,确保集群始终处于预期的工作状态。

控制循环

预期状态:使用yaml定义Deployment
调谐:
检查当前状态
对比预期状态
调整资源部署

Pod控制器

ReplicationController / RC 和 ReplicaSet / RS

RC用来确保容器应用的副本始终保持在用户定义的副本数,如果有容器异常退出,会自动创建新的Pod来替代,如果多出的容器也会自动回收,会优先删除最新产生的容器。

并且保证的副本数量是对应的标签子集。

rc.yaml

apiVersion: v1
kind: ReplicationController
metadata:
  name: rc-demo
spec:
  replicas: 3
  selector:
    app: rc-demo
  template:
    metadata:
      labels:
        app: rc-demo
    spec:
      containers:
      - name: rc-demo-container
        image: wangyanglinux/myapp:v1.0
        env:
        - name: GET_HOSTS_FROM
          value: dns
        - name: zhangsan
          value: "123"
        ports:
        - containerPort: 80

RC的作用:
当前Node损坏,可以迁移到其他Node。

#修改rc管理的副本数量
kubectl scale rc rc-demo --replicas=5

但是在新版本中kubernetes建议使用ReplicaSet替代RC。因为RS不仅可以匹配标签,还带有匹配运算符。所以更灵活。

RS作用:与RC特性类似,但更灵活。
一个使用场景是,通过标签使用一个RS来创建针对不同环境的服务。生产环境和测试环境通过标签做区分,然后通过svc向外提供服务。
删除特性:由于RC和RS都会自动创建预期Pod,所以直接删除Pod依旧会被控制器自动创建,所以应该删除对应的RC和RS才可以。

支持的标签选择器操作

matchExpressions 字段支持以下操作符,用于定义更灵活的标签选择条件:

In
指定标签的值必须存在于给定的列表中。
示例:

matchExpressions:
  - key: "environment"
    operator: "In"
    values: ["production", "staging"]

NotIn
指定标签的值不能存在于给定的列表中。
示例:

matchExpressions:
  - key: "environment"
    operator: "NotIn"
    values: ["development", "test"]

Exists
指定标签必须存在,不检查其值。
示例:

matchExpressions:
  - key: "tier"
    operator: "Exists"

DoesNotExist
指定标签必须不存在。
示例:

matchExpressions:
  - key: "tier"
    operator: "DoesNotExist"

rs.yaml

apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: rs-ml-demo
spec:
  replicas: 3
  selector:
    matchLabels:
      app: rs-ml-demo
      domain: lz
  template:
    metadata:
      labels:
        app: rs-ml-demo
        domain: lz
    spec:
      containers:
      - name: rs-ml-demo-container
        image: wangyanglinux/myapp:v1.0
        env:
        - name: GET_HOSTS_FROM
          value: dns
        ports:
        - containerPort: 80

Deployment

Deployment为Pod和ReplicaSet提供了一个声明式

声明式与命令式
声明式是对最终结果的陈述,表明意图而不是实现它的过程。
命令式是主动直接的,是对过程的陈述。

replace与apply命令对比
kubectl replace:使用心得配置完全替换掉现有资源的配置,新配置将配置现有所有资源的所有字段和属性。
kubectl apply:使用新配置部分地更新现有资源的配置,不会覆盖整个资源的配置。

diff命令:查看两个不同部署yaml文件的不同。

对于选择器:除RC外其他的所有选择器,都会选择器的运算操作。

depoly.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: myapp-deploy
  name: myapp-deploy
spec:
  replicas: 1
  selector:
    matchLabels:
      app: myapp-deploy
  template:
    metadata:
      labels:
        app: myapp-deploy
    spec:
      containers:
      - image: wangyanglinux/myapp:v1.0
        name: myapp
#使用apply的声明式方式将上述的yaml给应用起来
kubectl apply -f deploy.yaml
#创建deploy命令式方式创建,可以防止覆盖
kubectl create -f deployment --record
#查看当前Deployment在kubernetes中的配置信息
kubectl get deploy myapp-deploy -o yaml
#扩容缩容
kubectl scale deployment deploy-demo --replicas 10
#自动扩缩容
kubectl autoscale deployment deploy-demo --min=10 --max=15 --cpu-percent=80
#修改镜像
kubectl set image deployment/deploy-demo deploy-demo-container=docker/deploy-demo:v2.0
#基于文件删除对应资源
kubectl delete -f deploy.yaml
滚动更新和回滚

deployment通过管理RS来实现对Pod的管理。并且当有新的配置yaml文件对deploy做设置时,Deployment的做法是新建一个RS2,然后从1逐步添加对RS的期望,对于之前旧的RS1会从先前的期待值逐步降低至0。这就是滚动更新的流程。但是旧的RS1并不会被删除,而是继续存储在ETCD中,这样做的目的是,方便回滚之前的部署。就是如果RS2部署的有问题,那么就执行回滚操作,把RS2的期望逐步减为0,把RS1的期望再从1逐步添加到期望值。

deployment
RS1
Pod1
Pod2
Pod3
RS2
Pod4
Pod5
Pod6
更新策略

缩减Pod:deployment可以保证在升级时,只有一定数量的Pod是down的。默认是,确保至少有比期望值少一个的状态。最多一个不可用。
添加Pod:Deployment可以确保只创建超出期望值数量的一定数量的Pod。默认会确保,比期望低Pod数量多一个的Pod是up的。最多一个surge。
现在的kubernetes,将1-1变为了25%-25%。
下面的可以通过命令去查看字段如何配置

kubectl explain deploy.spec.startegy.type

其中有两个参数
- "Recreate" Kill all existing pods before creating new ones.
- "RollingUpdate" Replace the old ReplicaSets by new one using rolling
update i.e gradually scale down the old ReplicaSets and scale up the new one.
RollingUpdate:
maxSurge:最大添加Pod量
maxUnavaliable:最大可削减量

金丝雀部署

英国矿工使用金丝雀来检测少量的瓦斯气体。引申为小部分用户先进入新系统进行测试。大部分系统用户仍旧使用旧系统。

#打补丁
#如果要做金丝雀发布,需要在对应的策略中,将滚动更新的允许增量改为对应金丝雀部署的主机数,也就是新版本对应机器,将允许最大不可用数修改为0,如此设置就可以保证旧版本机器不会被滚动更新。也就实现了新版本和旧版本共存的状态。
kubectl patch deployment nginx-deployment -p '{"spec":{"strategy":{"rollingUpdate":{"maxSurge":1,"maxUnavailable":0}}}}'
#并将当前的滚动更新暂停
kubectl patch deployment nginx-deployment --patch '{"spec": {"template": {"spec": {"containers": [{"name": "nginx-deployment-container","image":"wangyanglinux/myapp:v2.0"}]}}}}' && kubectl rollout pause deploy nginx-deployment
#统计当前的容器数量
kubectl get pod | grep deployment | wc -l
#恢复滚动更新命令 全部Pod滚动更新到新版
kubectl rollout resume deploy nginx-deployment
#返回0表示上一条操作成功
echo $?

#创建svc
kubectl apply -f 1.svc.yaml
#上面的svc中最重要的是需要将Pod的标签要一致
spec:
  selector:
    app: myapp-deploy
#如何查看当前sevice下挂载的pod
kubectl describe svc myapp-service
#查看endpoint标签
Endpoints:                10.244.58.223:80,10.244.58.224:80,10.244.58.225:80 + 6 more...

这种情况看似是实现了金丝雀部署,但是由于滚动更新被Pause了,接下来的操作只能是resume提交这一更新,也就是无法直接做回滚,这样原始的方式在生产上是并不可取的。

部署回滚
#回滚操作
kubectl rollout undo deploy myapp-deploy
kubectl rollout undo deploy myapp-deploy --to-reversion=2
#查看对应的记录
kubectl rollout status deploy myapp-deploy
#查看更新记录
kubectl rollout history deployment/myapp-deploy
#下面是运行结果
REVISION  CHANGE-CAUSE
1         <none>
2         <none>
# 这里要强调,CHANGE-CAUSE只会保存添加了--record命令的执行命令。如果更改了Deployment但是当前命令没有写--record,会将上一条命令照搬到下一条命令上。
#建议将配置文件复制下来做备份
cp deploy.yaml deploy-lz-version2.yaml
清理策略

spec.reversionHistoryLimit 为限制 reversion 历史记录。如果为0,那么就是不保留任何的记录。

DaemonSet / DS

确保 全部/一些 的Node上运行一个Pod副本。DaemonSet

用法
  1. 运行集群存储daemon,例如在每个Node上运行。
  2. 在每个Node上运行日志收集daemon。
  3. 在每个Node上运行的监控daemon。

以上的控制器都是做守护进程的。 守护进程:nginx 和 MySQL,持续运行的进程。 RC/RS、Deployment、DS
以下的控制器是做批处理任务的。 批处理:确认了退出逻辑。

Job

负责批处理任务,即仅执行一次的任务。保证批处理任务的一个或多个Pod成功结束。
spec.template 格式和Pod相同。
RestartPolicy仅支持never和OnFailure
单个Pod时。默认Pod成功运行后Job结束。
spec.completions标志Job结束需要成功运行的Pod数量,相当于当前任务需要重复运行几次结束。
spec.parallelism标志并行运行的Pod数量,默认是1.
spec.activeDeadlineSeconds标志Pod充实的最大时间,超过这个时间不会继续重试。

CronJob

基于时间的Job:规定时间只运行一次,周期性的在给定时间点运行。
应用:
给定时间点调度Job运行
创建周期性运行的Job:数据库备份、发送邮件。

CronJob期望字段

spec.schedule:调度,指任务运行周期。分 时 日 月 周
spec.jobTemplate:Job模板,指定需要运行的任务。
spec.startingDeadlineSeconds:启动Job的期限,如果因为任何资源而错过了被调度的时间,那么错过执行时间的Job会被认为是失败的。如果没有指定,则没有期限。
spec.concurrencyPolicy:并发策略,指定了如何处理被CronJob创建的Job并发执行。
Allow:允许并发运行Job,当前还没完成,再创建一个新的。
场景:发送邮件。
Forbid:禁止并发运行,如果前一个还没完成,直接跳过下一个。
场景:数据库备份。
Replace:取消当前正在运行的Job,用一个新的来替换。
场景:调用远程API获取数据。
spec.suspend:挂起,可选字段。设置为 TRUE ,后续所有的执行都会被挂起。
spec.successfulJobsHistoryLimit
spec.failedJobsHistoryLimit

Service概念、原理及使用

Service定义了一种抽象: 一个Pod的逻辑分组,一种可以访问微服务的策略。这一组Pod都能被Service访问到,通常是通过Label Selector(标签选择器,需要通过就绪探测才可以)。
负载均衡对外提供服务。
nginx处理静态资源处理更优秀,动态资源给tomcat来处理。
nginx对7层探测机制,可以对故障容器不再发送请求,但是无法对Deployment新生成的容器进行动态添加,如果实现需要重新写配置文件,并且做重启才可以。

Service的底层原理

在kubernetes集群中,每个Node都运行了一个kube-proxy的进程,负责为Service实现一种虚拟IP方式。

  • 1.0版,代理完全在userspace空间下
  • 1.1版,新增了iptables的代理
  • 1.2版,iptable变为默认代理
  • 1.8版,添加了ipvs代理。
#查看ipvs中的规则,确定当前集群的工作方式
#ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
userspace代理模式

在这里插入图片描述
每一个机器中的kube-proxy都需要监听当前的kube-apiSever,监听负载均衡需要改变的信息–> service对象。

kube-proxy的功能:

  • 监听:需要监听apiServer的Service对象,修改对应的防火墙规则,实现负载均衡的分发。
  • 代理:需要代理来自客户端的请求,并返回给客户端响应。

这个问题在于:kube-proxy本身压力比较大,并且功能都集中在proxy中,耦合度较高。

IPtables代理模式

在这里插入图片描述
这里kube-proxy的功能变为仅监听apiserver中的Service对象,并修改iptables的规则。转发请求的功能交由给防火墙来处理。

ipvs代理模式

在这里插入图片描述
ipvs是四层负载均衡规则。所有ipvs是优于iptables。
官方未使用ipvs规则的原因是应为,在Linux机器中,ipvs需要在内核中手动开启,如果官方设置ipvs为默认规则,是无法正常工作的。
大部分云服务厂商都移除了ipvs模块。

#kubectl edit命令,可以在线编辑配置
kubectl edit deploy myapp-deploy
#修改proxy的配置文件
kubectl edit configmap kube-proxy -n kube-system
#通过标签去筛选Pod
kubectl get pod -n kube-system -l k8s-app=kube-proxy
#通过标签去删除对应Pod
kubectl delete pod -n kube-system -l k8s-app=kube-proxy

Service工作模式

类型:
Pod的IP是虚拟的扁平化的IP,外部系统是无法访问到Pod的。

  • clusterIp:默认类型,自动分配一个仅在Cluster内部可以被访问的虚拟IP。

  • NodePort:在ClusterIP的基础上为每个Service在每台机器上绑定一个端口,这样可以通过NODEIP:NODE PORT来访问该服务。创建的Service会在当前的物理网卡上绑定一个大于30000物理端口,并绑定到IPVS集群。所以一般这个NodePort会绑定到nginx的Pod上,然后NginxPod连接对应的模式为ClusterIP的集群,这个集群绑定到tomcat上。这样一整套的逻辑,向外提供对应的服务访问。
    在这里插入图片描述

  • LoadBalancer:在NodePort基础上,借助cloud provider 创建一个外部负载均衡器,并将请求转发到NODEIP:NODE PORT。
    上图中的30001会在所有的机器的网卡上都会生成占用。
    所有会在机器之前再加一层负载均衡(手动搭建IPVS/云服务的LAAS),这样保证Node结点故障仍旧能保证服务可用。
    在对应的云服务商,对应的就是LAAS,负载均衡即服务。

  • ExternalName:将集群外部的服务引入到集群内部中。利用coreDNS组件通过DNS别名机制来解析对应的服务域名与IP的对应关系。
    在这里插入图片描述

使用nginx代理tomcat:

upstream {
  server 10.100.1.23;
  server 10.100.1.24;
}
server {
  location / {
    proxy_pass http://tomcat;
  }
}

IPVS工作模式
NAT(Network Address Translation)模式:可以做端口映射。
DR(Direct Routing):直接路由模式,性能高。
TUN(Tunneling):隧道模式:可以跨公网网络。

service在内部集群会有一个域名,通过kubernetes中的dns可以解析为对应IP。

yum -y install bind-utils
dig -t A myapp-service.default.svc.cluster.local. @10.0.0.10

解析结果

; <<>> DiG 9.18.33 <<>> -t A myapp-service.default.svc.cluster.local. @10.0.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: 57437
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; WARNING: recursion requested but not available

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
; COOKIE: 40bb0399d004d1ee (echoed)
;; QUESTION SECTION:
;myapp-service.default.svc.cluster.local. IN A

;; ANSWER SECTION:
myapp-service.default.svc.cluster.local. 30 IN A 10.8.142.55

;; Query time: 28 msec
;; SERVER: 10.0.0.10#53(10.0.0.10) (UDP)
;; WHEN: Fri Sep 19 14:26:08 CST 2025
;; MSG SIZE  rcvd: 135
kubectl explain svc.spec.internalTrafficPolicy
  • kind: Service
    InternalTrafficPolicy 描述了节点如何分发它们在 ClusterIP 上接收到的服务流量。如果设置为 "Local",代理会假设 Pod 只希望与同一节点上的服务端点进行通信,如果没有本地端点,则会丢弃流量。默认值 "Cluster" 使用标准行为,将流量均匀地路由到所有端点(可能会受到拓扑和其他功能的影响)。
  • "Cluster": 将流量路由到所有端点。
  • "Local": 仅将流量路由到与客户端 Pod 在同一节点上的端点(如果没有本地端点,则丢弃流量)。
ipvs/lvs 持久化连接

lvs的SH算法,通过哈希把不同的用户散列到对应的机器上,但是此方法如果Hash表未被清楚,那么对应的连接就一直是同一台服务器。
将用户请求定向在同一台机器上。

应用场景:HTTPS建立过程,可以通过ipvs来做持久化连接

ipvsadm -A -t 192.168.66.100:80 -s rr -p 120
#-s 制定算法
#120定向客户端的时间

基于客户端连接
基于端口连接
基于防火墙标记连接

会话亲和性/会话保持

sessionAffinity
Possible enum values:
- "ClientIP" is the Client IP based.
- "None" - no session affinity.

LoadBalancer:使用云服务商的模板进行配置,之后那招云服务商给的具体的模板进行配置,从而获得公网地址,这里的负载均衡是对应的在机器的物理网卡外围再添加一个loadbalancer。

Service的底层模型-endpoint

Service和endpoint关系:
自动关联体系:配置selector
自动创建一个同名的endpoints资源对象
endpoints匹配对应是当前命名空间中的Pod:通过标签子集运算选择就绪的节点。
手动关联体系:无关联selector。 未定义标签对象。
不会创建同名的endpoints资源对象,需要管理员手动创建。
匹配对象,管理员填写的断点信息。灵活性更强。

K8s内部
通过svc请求
Service
Endpoint
Pod1
Pod2
Pod3
标签选择器
动态监测Pod
将Pod的访问地址
维护到ep中
真实请求
按规则获取ep中的其中一个地址

Service是endpoint的动态性与合理性.

公开未就绪的Pod

Serivce 必须有两个指标:标签选择器的子集,当前Pod为就绪状态。
在spec标签下:对应的添加:publishNotReadyAddresses:true

总结

Service的意义是什么?
Service底层的工作原理
从用户空间 到 IPTABLE 到 ipvs
工作类型切换:ClusterIP类型、NodePort类型、loadbalancer类型、externalName类型
endpoints:Service和Pod的中间态
如何开启未就绪Service的捕获:StatefulSet 中的 Pod 自发现

如有侵权请联系删除

Logo

码道开发者社区,聚焦华为云码道 CodeArts 代码智能体,沉淀 Agent、Skill、鸿蒙开发实战内容,供开发者查阅资料、交流技术、分享工程实践

更多推荐