KVM创建ubuntu20.04虚机,部署K8S,再克隆出二份,做为Worker节点加入集群,通过Helm创建2个Pod,让它们之间通过域名互访
- 一.背景
- 二.操作步骤
-
- 1.安装KVM
-
- A.在BIOS中开启VT-d
- B.修改grub,开启iommu
-
- [在/etc/default/grub 中 GRUB_CMDLINE_LINUX行 添加 intel_iommu=on iommu=pt](#在/etc/default/grub 中 GRUB_CMDLINE_LINUX行 添加 intel_iommu=on iommu=pt)
- 重新创建引导
- 重启服务器
- 验证iommu是否开启
- C.SSH开启X11-forwarding,用于可交互式安装
- D.安装依赖
- 2.创建第一台KVM虚拟机,做为Master节点
-
- A.创建存储目录
- [B.下载ubuntu 20.04镜像](#B.下载ubuntu 20.04镜像)
- C.创建虚拟机
-
- 通过virt-install创建虚机,在界面上配置ubutnu20.04
- 重启虚机
- 通过远程桌面登录ubuntu20.04,查看IP(如:`192.168.122.140`)
- 在物理机上通过SSH登录虚机
- 修改为静态IP
- 更新apt源
- 关闭swap
- 开启网络转发
- 安装docker-ce
- 设置docker国内镜像
- 安装kubelet
- 创建K8S集群
- 关闭虚机
- 在物理机上,克隆出二台虚机,设置成不同的MAC
- 依次启动三台虚机
- 通过ssh登录ubuntu20节点,获取join命令
- 通过ssh登录ubuntu20_node1,将其加入集群
- 通过ssh登录ubuntu20_node2,将其加入集群
- 回到ubuntu20节点,获取node列表,设置node1、node2为工作节点
- 在ubuntu20上安装Helm
- 在ubuntu20上创建Helm工程
- 在ubuntu20上通过Helm命令创建pod
- 获取K8S集群的DNS服务器地址
- 进入第一个pod,配置DNS,使其能通过域名访问pod1
- 进入第二个pod,配置DNS,使能通过域名访问pod0
- 删除资源
- 3.遇到的问题及解决办法
- 三.补充知识点
-
- [**1. Deployment**](#1. Deployment)
- [**2. StatefulSet**](#2. StatefulSet)
- [**3. 主要区别**](#3. 主要区别)
- [**4. 具体示例**](#4. 具体示例)
-
- [**Deployment 示例**](#Deployment 示例)
- [**StatefulSet 示例**](#StatefulSet 示例)
- [**5. 选择使用 Deployment 还是 StatefulSet 的指南**](#5. 选择使用 Deployment 还是 StatefulSet 的指南)
-
- [**使用 Deployment:**](#使用 Deployment:)
- [**使用 StatefulSet:**](#使用 StatefulSet:)
- [**6. 常见误区**](#6. 常见误区)
- [**7. 总结**](#7. 总结)
一.背景
- 在centos7.9上安装KVM ubuntu20.04虚拟机
- 在该虚机中部署K8S单节点
- 将该虚拟机克隆出二份,设置MAC和IP,并加入到K8S集群
- 在Mastet节点安装Helm
- 通过Helm模板创建二个Pod,每个Pod一个容器,Pod之间可通过固定的域名访问
二.操作步骤
1.安装KVM
A.在BIOS中开启VT-d
B.修改grub,开启iommu
在/etc/default/grub 中 GRUB_CMDLINE_LINUX行 添加 intel_iommu=on iommu=pt
修改如下
bash
GRUB_TIMEOUT=5
GRUB_DISTRIBUTOR="$(sed 's, release .*$,,g' /etc/system-release)"
GRUB_DEFAULT=saved
GRUB_DISABLE_SUBMENU=true
GRUB_TERMINAL_OUTPUT="console"
GRUB_DISABLE_RECOVERY="true"
GRUB_CMDLINE_LINUX="crashkernel=384M rd.lvm.lv=centos/root rd.lvm.lv=centos/swap rhgb quiet intel_iommu=on iommu=pt"
重新创建引导
bash
*EFI模式: grub2-mkconfig -o /boot/efi/EFI/bclinux/grub.cfg
*BIOS模式: grub2-mkconfig -o /boot/grub2/grub.cfg
重启服务器
bash
sync;sync;ipmitool power cycle
验证iommu是否开启
bash
dmesg | grep iommu -i
C.SSH开启X11-forwarding,用于可交互式安装
修改/etc/ssh/sshd_config
如下:
bash
AddressFamily inet
AllowTcpForwarding yes
X11Forwarding yes
重启SSH服务
bash
systemctl restart sshd
D.安装依赖
bash
yum -y install qemu qemu-kvm libvirt libvirt-python libguestfs- \
tools virt-install tigervnc virt-viewer virt-manager
systemctl enable libvirtd
systemctl start libvirtd
2.创建第一台KVM虚拟机,做为Master节点
A.创建存储目录
bash
mkdir -p /var/lib/libvirt/boot/
mkdir -p /var/lib/libvirt/images/
B.下载ubuntu 20.04镜像
bash
cd /var/lib/libvirt/boot/
wget https://mirrors.tuna.tsinghua.edu.cn/ubuntu-releases/20.04/ubuntu-20.04.5-live-server-amd64.iso
C.创建虚拟机
通过virt-install创建虚机,在界面上配置ubutnu20.04
bash
virt-install \
--virt-type=kvm \
--name ubuntu20 \
--ram 21920 \
--vcpus=16 \
--os-type linux \
--os-variant ubuntu20.04 \
--console pty,target_type=serial\
--connect qemu:///system \
--cdrom=/var/lib/libvirt/boot/ubuntu-20.04.5-live-server-amd64.iso \
--network=bridge=virbr0,model=virtio \
--graphics vnc \
--disk path=/var/lib/libvirt/images/ubuntu20.qcow2,size=20,bus=virtio,format=qcow2
注意事项
- 开启SSH
- Configure Proxy可选:http://mirrors.aliyun.com/ubuntu/
重启虚机
bash
virsh shutdown ubuntu20
virsh start ubuntu20
通过远程桌面登录ubuntu20.04,查看IP(如:192.168.122.140
)
bash
virt-viewer
在物理机上通过SSH登录虚机
bash
ssh kvm@192.168.122.140
sudo su
修改为静态IP
bash
cat >/etc/netplan/00-installer-config.yaml <<-'EOF'
network:
version: 2
renderer: networkd
ethernets:
ens3:
addresses:
- 192.168.122.140/24
gateway4: 192.168.122.1
nameservers:
addresses: [8.8.8.8,8.8.4.4]
EOF
netplan apply
更新apt源
bash
sed -i "s@http://.*archive.ubuntu.com@http://repo.huaweicloud.com@g" /etc/apt/sources.list
sed -i "s@http://.*security.ubuntu.com@http://repo.huaweicloud.com@g" /etc/apt/sources.list
apt update
apt install net-tools -y
关闭swap
bash
sudo sed -i 's/.*swap.*/#&/' /etc/fstab
swapoff -a
开启网络转发
bash
bash -c "cat >> /etc/sysctl.conf" << EOF
net.ipv4.ip_forward = 1
net.bridge.bridge-nf-call-iptables = 1
EOF
安装docker-ce
bash
sudo apt install -y apt-transport-https ca-certificates software-properties-common
sudo curl -fsSL https://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg |sudo apt-key add -
sudo add-apt-repository "deb [arch=amd64] https://mirrors.aliyun.com/docker-ce/linux/ubuntu $(lsb_release -cs) stable"
sudo apt-cache madison docker-ce
dpkg -l | grep docker
apt remove --purge docker-ce*
sudo apt install docker-ce=5:19.03.15~3-0~ubuntu-focal docker-ce-cli=5:19.03.15~3-0~ubuntu-focal containerd.io -y
设置docker国内镜像
bash
bash -c "cat >> /etc/docker/daemon.json" << EOF
{
"dns": ["8.8.8.8", "8.8.4.4"],
"registry-mirrors": [
"https://docker.m.daocloud.io/",
"https://huecker.io/",
"https://dockerhub.timeweb.cloud",
"https://noohub.ru/",
"https://dockerproxy.com",
"https://docker.mirrors.ustc.edu.cn",
"https://docker.nju.edu.cn",
"https://xx4bwyg2.mirror.aliyuncs.com",
"http://f1361db2.m.daocloud.io",
"https://registry.docker-cn.com",
"http://hub-mirror.c.163.com"
]
}
EOF
systemctl restart docker
安装kubelet
bash
sudo curl https://mirrors.aliyun.com/kubernetes/apt/doc/apt-key.gpg |sudo apt-key add -
sudo bash -c "cat > /etc/apt/sources.list.d/kubernetes.list" <<EOF
deb https://mirrors.aliyun.com/kubernetes/apt kubernetes-xenial main
EOF
sudo apt install -y kubeadm=1.20.0-00 kubelet=1.20.0-00 kubectl=1.20.0-00
systemctl enable kubelet && sudo systemctl start kubelet
创建K8S集群
bash
rm /var/lib/etcd -rf
kubeadm reset
kubeadm init --image-repository registry.aliyuncs.com/google_containers --kubernetes-version v1.20.0 --pod-network-cidr=10.244.0.0/16
mkdir -p $HOME/.kube
sudo cp -f /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
kubectl taint nodes --all node-role.kubernetes.io/master-
kubectl get pods --all-namespaces
#等待所有pod为running状态
kubectl get node
输出
bash
NAMESPACE NAME READY STATUS RESTARTS AGE
kube-flannel kube-flannel-ds-jvlns 1/1 Running 0 17m
kube-system coredns-7f89b7bc75-558sb 0/1 Running 0 17m
kube-system coredns-7f89b7bc75-5mhgm 0/1 Running 0 17m
kube-system etcd-111111 0/1 Running 1 17m
kube-system kube-apiserver-111111 1/1 Running 1 17m
kube-system kube-controller-manager-111111 0/1 Running 1 17m
kube-system kube-proxy-g7nsd 1/1 Running 1 17m
kube-system kube-scheduler-111111 0/1 Running 1 17m
关闭虚机
bash
poweroff
在物理机上,克隆出二台虚机,设置成不同的MAC
bash
virt-clone -o ubuntu20 -n ubuntu20_node1 -f /var/lib/libvirt/images/ubuntu20_node1.qcow2 --mac 52:54:00:3b:d9:17
virt-clone -o ubuntu20 -n ubuntu20_node2 -f /var/lib/libvirt/images/ubuntu20_node2.qcow2 --mac 52:54:00:3b:d9:27
依次启动三台虚机
bash
virsh start ubuntu20_node1
virsh start ubuntu20_node2
virsh start ubuntu20
每启动一台node节点,执行以下步骤
- 修改
/etc/netplan/00-installer-config.yaml
中的IP地址,之后执行netplan apply
生效
通过ssh登录ubuntu20节点,获取join命令
bash
kubeadm token create --print-join-command
输出
bash
kubeadm join 192.168.122.140:6443 --token v0wuxs.cqduc5dw0t50fis6 \
--discovery-token-ca-cert-hash sha256:aa14d393d6e693d6a1bea6e6b6d75c55e3bb38a29071fe7166ce9d49606b6b0e
通过ssh登录ubuntu20_node1,将其加入集群
bash
# 修改主机名
hostnamectl set-hostname node1
# join到K8S集群
kubeadm reset
rm -f /etc/kubernetes/pki/ca.crt /etc/kubernetes/kubelet.conf
kubeadm join 192.168.122.140:6443 --token v0wuxs.cqduc5dw0t50fis6 \
--discovery-token-ca-cert-hash sha256:aa14d393d6e693d6a1bea6e6b6d75c55e3bb38a29071fe7166ce9d49606b6b0e
通过ssh登录ubuntu20_node2,将其加入集群
bash
# 修改主机名
hostnamectl set-hostname node2
# join到K8S集群
kubeadm reset
rm -f /etc/kubernetes/pki/ca.crt /etc/kubernetes/kubelet.conf
kubeadm join 192.168.122.140:6443 --token v0wuxs.cqduc5dw0t50fis6 \
--discovery-token-ca-cert-hash sha256:aa14d393d6e693d6a1bea6e6b6d75c55e3bb38a29071fe7166ce9d49606b6b0e
回到ubuntu20节点,获取node列表,设置node1、node2为工作节点
bash
kubectl get nodes
kubectl label nodes node1 node-role.kubernetes.io/worker=
kubectl label nodes node2 node-role.kubernetes.io/worker=
kubectl get nodes --show-labels
在ubuntu20上安装Helm
bash
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
在ubuntu20上创建Helm工程
bash
# 创建工程
helm create llama-training
cd llama-training
# 删除无用的文件
rm templates/deployment.yaml
rm templates/service.yaml
rm templates/ingress.yaml
rm -rf templates/tests
rm templates/hpa.yaml
rm templates/serviceaccount.yaml
rm templates/NOTES.txt
# 创建 values.yaml 文件,包含可变参数
cat > values.yaml << EOF
replicaCount: 2
image:
repository: ubuntu
tag: "20.04"
pullPolicy: IfNotPresent
masterPort: "29500"
worldSize: "16"
EOF
# 创建 StatefulSet 模板,为每个 Pod 分配稳定的主机名
cat > templates/statefulset.yaml <<-'EOF'
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: {{ include "llama-training.fullname" . }}
labels:
{{- include "llama-training.labels" . | nindent 4 }}
spec:
serviceName: "{{ include "llama-training.fullname" . }}-headless"
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
{{- include "llama-training.selectorLabels" . | nindent 6 }}
template:
metadata:
labels:
{{- include "llama-training.selectorLabels" . | nindent 8 }}
spec:
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
securityContext:
allowPrivilegeEscalation: false
env:
- name: MASTER_ADDR
value: "{{ include "llama-training.fullname" . }}-headless"
- name: MASTER_PORT
value: "{{ .Values.masterPort }}"
- name: WORLD_SIZE
value: "{{ .Values.worldSize }}"
command: [ "/bin/bash", "-c", "--" ]
args: [ "while true; do sleep 30; done;" ]
EOF
# 创建 Headless Service,让 Pod 之间可以通过域名访问,解决 IP 地址不固定的问题
cat > templates/headless-service.yaml <<-'EOF'
apiVersion: v1
kind: Service
metadata:
name: {{ include "llama-training.fullname" . }}-headless
labels:
{{- include "llama-training.labels" . | nindent 4 }}
spec:
clusterIP: None # 使其成为 Headless Service
selector:
{{- include "llama-training.selectorLabels" . | nindent 4 }}
EOF
# 设置网络策略,让 Pod 可以访问外网
cat > templates/networkpolicy.yaml <<-'EOF'
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: {{ include "llama-training.fullname" . }}-egress
namespace: {{ .Release.Namespace }}
labels:
{{- include "llama-training.labels" . | nindent 4 }}
spec:
podSelector:
matchLabels:
{{- include "llama-training.selectorLabels" . | nindent 6 }}
policyTypes:
- Egress
egress:
- {}
EOF
在ubuntu20上通过Helm命令创建pod
bash
helm install llama-training .
kubectl get pods -o wide
输出
bash
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
llama-training-0 1/1 Running 0 15s 10.244.2.194 node1 <none> <none>
llama-training-1 1/1 Running 0 14s 10.244.1.106 master <none> <none>
获取K8S集群的DNS服务器地址
bash
kubectl get endpoints -n kube-system -o wide|grep kube-dns
bash
kube-dns 10.244.1.104:53,10.244.1.105:53,10.244.1.104:53 + 3 more... 63m
进入第一个pod,配置DNS,使其能通过域名访问pod1
bash
# 进入pod
kubectl exec -it llama-training-0 -- bash
# 配置DNS
cat > /etc/resolv.conf <<-'EOF'
nameserver 10.244.1.104
search default.svc.cluster.local svc.cluster.local cluster.local
options ndots:5
nameserver 8.8.8.8
EOF
# 下载相关工具
apt update
apt install net-tools iputils-ping -y
# 通过域名ping二个pod
ping llama-training-0.llama-training-headless.default.svc.cluster.local -c 2
ping llama-training-1.llama-training-headless.default.svc.cluster.local -c 2
# 退出
exit
输出
bash
64 bytes from llama-training-0.llama-training-headless.default.svc.cluster.local (10.244.2.194): icmp_seq=1 ttl=64 time=0.035 ms
64 bytes from llama-training-0.llama-training-headless.default.svc.cluster.local (10.244.2.194): icmp_seq=2 ttl=64 time=0.032 ms
64 bytes from llama-training-1.llama-training-headless.default.svc.cluster.local (10.244.1.106): icmp_seq=1 ttl=62 time=0.319 ms
64 bytes from llama-training-1.llama-training-headless.default.svc.cluster.local (10.244.1.106): icmp_seq=2 ttl=62 time=0.457 ms
进入第二个pod,配置DNS,使能通过域名访问pod0
bash
# 进入pod
kubectl exec -it llama-training-1 -- bash
# 配置DNS
cat > /etc/resolv.conf <<-'EOF'
nameserver 10.244.1.104
search default.svc.cluster.local svc.cluster.local cluster.local
options ndots:5
nameserver 8.8.8.8
EOF
# 下载相关工具
apt update
apt install net-tools iputils-ping -y
# 通过域名ping二个pod
ping llama-training-0.llama-training-headless.default.svc.cluster.local -c 2
ping llama-training-1.llama-training-headless.default.svc.cluster.local -c 2
# 退出
exit
输出
bash
64 bytes from llama-training-0.llama-training-headless.default.svc.cluster.local (10.244.2.194): icmp_seq=1 ttl=64 time=0.035 ms
64 bytes from llama-training-0.llama-training-headless.default.svc.cluster.local (10.244.2.194): icmp_seq=2 ttl=64 time=0.032 ms
64 bytes from llama-training-1.llama-training-headless.default.svc.cluster.local (10.244.1.106): icmp_seq=1 ttl=62 time=0.319 ms
64 bytes from llama-training-1.llama-training-headless.default.svc.cluster.local (10.244.1.106): icmp_seq=2 ttl=62 time=0.457 ms
删除资源
bash
helm uninstall llama-training
kubectl get pods -o wide
# 等待资源释放
3.遇到的问题及解决办法
- "cni0" already has an IP address different from
bash
ifconfig cni0 down
ip link delete cni0
- DNC异常,重启 CoreDNS
bash
kubectl delete pod -n kube-system -l k8s-app=kube-dns
kubectl get pods -n kube-system
三.补充知识点
在 Kubernetes(k8s)中,Pod 是最小的可部署单位 ,它代表了在集群中运行的一个或多个容器。每个 Pod 都运行在单个节点(服务器)上 ,无法跨越多个节点。也就是说,一个 Pod 不能同时分布在多个服务器上运行。
如果您需要在多个服务器上运行应用程序,以实现高可用性和负载均衡,您可以使用 Deployment、ReplicaSet 或 StatefulSet 等更高级别的控制器来管理多个 Pod 实例。Kubernetes 会根据资源情况和调度策略,将这些 Pod 分布到集群中的不同节点上运行。
总结来说,Pod 本身不能跨服务器,但您可以通过创建多个 Pod,并利用 Kubernetes 的调度和管理功能,让这些 Pod 分布在不同的节点上,以满足跨服务器的需求。
在Kubernetes中,Pod之间可以通过主机名进行访问,通常是通过以下几种方式实现的:
-
直接使用Pod的DNS:每个Pod在Kubernetes集群中都有一个DNS条目,你可以直接使用Pod的名称来进行访问,不过这种方法不推荐用在生产环境,因为Pod的IP地址会随着重启发生变化。
-
使用Service进行访问:
-
ClusterIP:这是Kubernetes中Service的默认类型。Service为一组Pod提供了一个稳定的IP地址和DNS名称。你可以通过Service的名称来访问Pod,Service会自动进行负载均衡。
-
Headless Service :如果需要直接获取Pod的IP地址,可以创建一个Headless Service。通过将Service的
clusterIP
字段设置为None
来创建。这样,Kubernetes会为这个Service的每个Pod创建一个DNS记录。
-
-
StatefulSets:
- 对于有状态应用,比如ZooKeeper、Cassandra等,可以使用StatefulSets来部署。这种资源类型会为每个Pod分配稳定的主机名,格式通常为
<podname>.<headless-service-name>.<namespace>.svc.cluster.local
。
- 对于有状态应用,比如ZooKeeper、Cassandra等,可以使用StatefulSets来部署。这种资源类型会为每个Pod分配稳定的主机名,格式通常为
在 Kubernetes 中,Deployment 和 StatefulSet 是两种常用的工作负载(Workload)资源类型,它们用于管理 Pod 的部署和伸缩。虽然它们都可以用于管理应用程序的副本,但它们在处理 Pod 的方式和适用的场景上有着显著的区别。
以下将详细解释 Deployment 和 StatefulSet 的主要区别,以及它们各自的适用场景。
1. Deployment
特点:
- 无状态服务(Stateless):Deployment 主要用于管理无状态的应用程序,如 Web 前端、API 服务器等。
- Pod 的可互换性:Deployment 中的所有 Pod 都是可互换的,没有特定的身份。
行为:
- 滚动更新(Rolling Update):支持无中断地更新应用程序到新版本。
- Pods 的命名:Pod 的名称是随机生成的,不保证稳定的主机名(Hostname)或网络标识(Network Identity)。
- 副本数(Replica)管理:可以轻松地水平伸缩 Pod 的数量。
适用场景:
- 无需持久化存储的无状态应用。
- 对于应用程序的实例间没有严格的顺序或身份需求。
2. StatefulSet
特点:
- 有状态服务(Stateful):StatefulSet 用于管理有状态的应用程序,如数据库(例如 MySQL、MongoDB)、分布式系统(例如 Kafka、Zookeeper)等。
- 稳定的网络标识:每个 Pod 都有一个稳定的、唯一的网络标识(主机名),该标识在 Pod 重建或调度到其他节点时保持不变。
- 持久化存储:可以为每个 Pod 关联一个持久化的存储卷(PersistentVolume),即使 Pod 被删除或重新调度,数据也不会丢失。
行为:
- 有序部署和更新:Pod 的创建、更新和删除按照指定的顺序进行(从 0 到 N-1)。
- Pods 的命名 :Pod 的名称遵循固定的模式:
<StatefulSet 名称>-<序号>
,例如my-app-0
、my-app-1
。 - 持久化存储卷的绑定 :通过
VolumeClaimTemplates
,每个 Pod 都有自己的持久化存储卷。
适用场景:
- 需要持久化数据的有状态应用程序。
- 应用程序实例需要稳定的身份(例如,需要特定的主机名或 IP 地址)。
- 应用程序实例之间需要有序部署或终止。
3. 主要区别
特性 | Deployment | StatefulSet |
---|---|---|
适用应用类型 | 无状态应用程序 | 有状态应用程序 |
Pod 可互换性 | Pods 可互换,无需稳定身份 | Pods 有稳定的身份,不可互换 |
网络标识 | Pod 名称随机,无稳定的主机名或网络标识 | 每个 Pod 有稳定的主机名,网络标识长期保持不变 |
存储卷 | 通常使用共享的存储卷,或不需要存储 | 每个 Pod 关联一个专属的持久化存储卷 |
部署和更新顺序 | 无序,Pods 可并行创建、更新或删除 | 有序,按照序号顺序创建、更新或删除 Pods |
滚动更新 | 支持,更新时可同时更新多个实例 | 更新时按顺序进行,一个接一个地更新 Pods |
扩缩容 | 支持,同时创建或删除多个 Pods | 按序号增减 Pods,扩容时创建新的序号的 Pod |
4. 具体示例
Deployment 示例
yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 3 # 副本数,可以随意调整
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.21
说明:
- 部署了 3 个 Nginx 实例,Pod 名称随机生成,例如
nginx-deployment-xxxxxxxxxx-yyyyy
。 - Pods 之间没有稳定的身份或顺序,可以随意扩缩容。
StatefulSet 示例
yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql-statefulset
spec:
serviceName: "mysql"
replicas: 3 # 副本数,按照序号创建
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
containers:
- name: mysql
image: mysql:8.0
volumeMounts:
- name: mysql-data
mountPath: /var/lib/mysql
volumeClaimTemplates:
- metadata:
name: mysql-data
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: "standard"
resources:
requests:
storage: 10Gi
说明:
- 部署了 3 个 MySQL 实例,Pod 名称为
mysql-statefulset-0
、mysql-statefulset-1
、mysql-statefulset-2
。 - 每个 Pod 都有一个名为
mysql-data
的持久化存储卷,数据不会因 Pod 重建而丢失。 - Pods 的创建、更新和删除按序号进行,确保顺序性和稳定性。
5. 选择使用 Deployment 还是 StatefulSet 的指南
使用 Deployment:
- 无状态应用程序:应用程序不需要持久化数据。
- Pod 可互换:实例之间没有区别,可以随意替换。
- 无需稳定网络标识:Pods 的主机名和 IP 地址可变。
- 需要快速扩缩容:可以迅速增加或减少 Pods 数量。
- 快速滚动更新:可同时更新多个实例,减少更新时间。
使用 StatefulSet:
- 有状态应用程序:如数据库、缓存系统等,需要持久化数据。
- Pod 需要稳定身份:实例需要固定的主机名或网络标识。
- 需要持久化存储:每个 Pod 需要绑定独立的存储卷。
- 有序部署和更新:需要按照特定顺序启动或停止实例。
- 对实例间关系有要求:实例之间存在关联,需要稳定的通信。
6. 常见误区
- 误用 Deployment 管理有状态服务:可能导致数据丢失、网络通信失败等问题。
- 误用 StatefulSet 管理无状态服务:增加了不必要的复杂性,降低了部署效率。
7. 总结
- Deployment:适用于无状态、可快速扩展的应用程序,管理简单,部署效率高。
- StatefulSet:适用于有状态、需要持久化存储和稳定网络标识的应用程序,确保应用程序的可靠性和一致性。