Kubernetes部署与核心 —— 容器编排的"交响乐指挥家"
一、为什么需要Kubernetes?——从"管理10个集装箱"到"管理10000个"
问题场景
学校的信息技术社团开发了一套校园服务系统,包括选课系统、成绩查询、社团管理、在线考试等8个微服务。最初每个服务跑一个容器,用 docker run 手动管理,还算轻松。但随着用户增长:
- 选课高峰期某个服务需要临时扩容到10个实例
- 某个容器挂了需要自动重启
- 流量需要在多个实例间负载均衡
- 8个服务需要滚动更新而不中断服务
- 需要在多台服务器间调度容器
一个人手动管理这一切?——就像一个人同时指挥100个人搬家具,嗓子喊哑了也管不过来。
类比:想象一场交响乐演出——小提琴、大提琴、长笛、小号、定音鼓……几十种乐器、上百位演奏者。如果没有指挥家,每个人各弹各的,就是一场噪音灾难。Kubernetes就是容器世界的交响乐指挥家——它知道什么时候该让哪个乐器加入、什么时候该调高音量、某个乐手生病了找谁替补。
互动提问:Kubernetes这个名字来自希腊语,意思是"舵手"或"飞行员"。你觉得为什么Google要用这个名字?(提示:想想谁在掌舵、谁在导航?)
二、Kubernetes架构:两大阵营
2.1 整体架构图
┌──────────────────── Master 节点(大脑/指挥中心)────────────────────┐
│ │
│ ┌─────────────┐ ┌───────────┐ ┌──────────────┐ ┌───────────┐ │
│ │ API Server │ │Scheduler │ │ Controller │ │ etcd │ │
│ │ (前台接待) │ │(调度员) │ │ Manager │ │(记事本) │ │
│ │ │ │ │ │(巡检员) │ │ │ │
│ └─────────────┘ └───────────┘ └──────────────┘ └───────────┘ │
│ │
└────────────────────────────────────────────────────────────────────┘
↕ ↕ ↕
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
│ Node 1(工人) │ │ Node 2(工人) │ │ Node 3(工人) │
│ ┌──────────────┐ │ │ ┌──────────────┐ │ │ ┌──────────────┐ │
│ │ kubelet │ │ │ │ kubelet │ │ │ │ kubelet │ │
│ │ (监工) │ │ │ │ (监工) │ │ │ │ (监工) │ │
│ ├──────────────┤ │ │ ├──────────────┤ │ │ ├──────────────┤ │
│ │ kube-proxy │ │ │ │ kube-proxy │ │ │ │ kube-proxy │ │
│ │(交通指挥) │ │ │ │(交通指挥) │ │ │ │(交通指挥) │ │
│ ├──────────────┤ │ │ ├──────────────┤ │ │ ├──────────────┤ │
│ │ 容器运行时 │ │ │ │ 容器运行时 │ │ │ │ 容器运行时 │ │
│ │(Docker/cri-o)│ │ │ │(Docker/cri-o)│ │ │ │(Docker/cri-o)│ │
│ ├──────────────┤ │ │ ├──────────────┤ │ │ ├──────────────┤ │
│ │ Pod Pod │ │ │ │ Pod Pod │ │ │ │ Pod Pod │ │
│ └──────────────┘ │ │ └──────────────┘ │ │ └──────────────┘ │
└──────────────────┘ └──────────────────┘ └──────────────────┘
2.2 Master节点组件(指挥中心)
| 组件 | 类比 | 职责 |
|---|---|---|
| API Server | 公司前台 | 所有操作的唯一入口,接收并处理所有请求(增删改查资源) |
| etcd | 档案室/记事本 | 分布式键值存储,保存整个集群的所有状态数据 |
| Scheduler | 调度员 | 决定新Pod应该运行在哪个Node上(考虑资源、亲和性等) |
| Controller Manager | 巡检员 | 持续监控集群状态,确保"实际状态"与"期望状态"一致 |
etcd详解:
etcd —— "集群的唯一真相来源"
所有K8s的状态信息都存在etcd中:
- 有哪些Pod在运行?
- 每个Service的配置是什么?
- 集群的证书和密钥
- 当前的ConfigMap和Secret
etcd使用Raft协议实现分布式一致性:
多个etcd节点通过投票达成共识,即使部分节点故障,数据也不会丢失。
互动提问:为什么API Server是"唯一入口"?如果Scheduler直接操作etcd会怎样?——所有组件都通过API Server交互,这样可以统一管理认证、授权、审计日志,就像公司所有访客都要经过前台登记,不能直接闯进总裁办公室。
2.3 Node节点组件(工人)
| 组件 | 类比 | 职责 |
|---|---|---|
| kubelet | 车间监工 | 接收Master指令,管理本节点上Pod的生命周期 |
| kube-proxy | 交通指挥 | 管理节点上的网络规则,实现Service的负载均衡 |
| Container Runtime | 工人 | 实际运行容器的软件(Docker/containerd/cri-o) |
三、YAML语法速查:K8s的"指令语言"
Kubernetes使用YAML文件来描述资源定义。掌握YAML语法是使用K8s的前提。
# ===== YAML基础语法 =====
# 键值对(冒号后必须有空格)
name: my-app
version: "1.0" # 字符串加引号
replicas: 3 # 数字不加引号
enabled: true # 布尔值
# 列表(用 - 开头)
ports:
- 80
- 443
- 8080
# 嵌套对象(用缩进表示层级,只能用空格,不能用Tab!)
metadata:
name: my-pod
labels:
app: web
version: v1
# 列表中的对象
containers:
- name: nginx
image: nginx:1.25
ports:
- containerPort: 80
- name: sidecar
image: busybox
command: ["sh", "-c", "while true; do echo hello; sleep 10; done"]
# 多行文本
description: |
这是一个多行文本,
保留换行符。
# 单行折叠
summary: >
这段文字虽然写成了多行,
但会被折叠成一行。
# 注释
# 这是注释,YAML中用#号
YAML常见坑:
| 坑 | 错误示例 | 正确写法 |
|---|---|---|
| 缩进用Tab | →→name: app | name: app(用空格) |
| 冒号后没空格 | name:app | name: app |
| 数字被当字符串 | port: "80" | port: 80 |
| 布尔值陷阱 | version: on | version: "on"("on"会被解析为true) |
四、kubeadm部署Kubernetes集群
4.1 环境要求
| 项目 | 最低要求 |
|---|---|
| 操作系统 | CentOS 7/8、Ubuntu 20.04+、Fedora 36+、麒麟V10 |
| CPU | Master: 2核+, Node: 2核+ |
| 内存 | Master: 2GB+, Node: 2GB+ |
| 网络 | 节点间互通,关闭Swap |
| 容器运行时 | Docker/containerd/cri-o |
4.2 通用准备工作(所有节点)
# ===== 第一步:关闭Swap(K8s要求)=====
sudo swapoff -a # 临时关闭
sudo sed -i '/ swap / s/^/#/' /etc/fstab # 永久关闭(注释掉swap行)
free -h # 验证Swap为0
# ===== 第二步:关闭防火墙(测试环境)=====
# RHEL系
sudo systemctl stop firewalld && sudo systemctl disable firewalld
# Debian系
sudo ufw disable
# ===== 第三步:关闭SELinux(测试环境)=====
sudo setenforce 0
sudo sed -i 's/^SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config
# ===== 第四步:配置内核参数 =====
cat << EOF | sudo tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF
sudo modprobe overlay
sudo modprobe br_netfilter
cat << EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
EOF
sudo sysctl --system
# ===== 第五步:配置hosts文件(确保节点间可通过主机名通信)=====
cat >> /etc/hosts << EOF
192.168.1.100 master
192.168.1.101 node1
192.168.1.102 node2
EOF
4.3 安装容器运行时(containerd,推荐)
# RHEL系(CentOS/Fedora/麒麟)
sudo yum install -y containerd.io
# 或Debian系(Ubuntu)
sudo apt-get install -y containerd
# 生成默认配置
sudo mkdir -p /etc/containerd
sudo containerd config default | sudo tee /etc/containerd/config.toml
# 修改关键配置:使用systemd作为cgroup驱动
sudo sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml
# 启动containerd
sudo systemctl restart containerd
sudo systemctl enable containerd
4.4 安装kubeadm、kubelet、kubectl
RHEL系(CentOS 7/8、Fedora、麒麟V10)
# 添加Kubernetes仓库(使用阿里云镜像)
cat << EOF | sudo tee /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://mirrors.aliyun.com/kubernetes-new/core/stable/v1.29/rpm/
enabled=1
gpgcheck=1
gpgkey=https://mirrors.aliyun.com/kubernetes-new/core/stable/v1.29/rpm/repodata/repomd.xml.key
EOF
# 安装
sudo yum install -y kubelet kubeadm kubectl
sudo systemctl enable kubelet
Debian系(Ubuntu 20.04/22.04)
# 添加GPG密钥和仓库
curl -fsSL https://mirrors.aliyun.com/kubernetes-new/core/stable/v1.29/deb/Release.key | \
sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
echo "deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://mirrors.aliyun.com/kubernetes-new/core/stable/v1.29/deb/ /" | \
sudo tee /etc/apt/sources.list.d/kubernetes.list
# 安装
sudo apt-get update
sudo apt-get install -y kubelet kubeadm kubectl
sudo apt-mark hold kubelet kubeadm kubectl # 锁定版本,防止自动升级
4.5 初始化Master节点
# 预拉取镜像(使用阿里云镜像仓库)
kubeadm config images list
kubeadm config images pull --image-repository=registry.cn-hangzhou.aliyuncs.com/google_containers
# 初始化Master
sudo kubeadm init \
--image-repository=registry.cn-hangzhou.aliyuncs.com/google_containers \
--pod-network-cidr=10.244.0.0/16 \
--service-cidr=10.96.0.0/12 \
--apiserver-advertise-address=192.168.1.100
# 初始化成功后,配置kubectl(以普通用户身份)
mkdir -p $HOME/.kube
sudo cp /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
# 验证Master状态
kubectl get nodes
# 此时Master状态为NotReady,因为还没安装网络插件
4.6 安装网络插件(Calico)
# 安装Calico(最流行的K8s网络方案)
kubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/v3.27.0/manifests/calico.yaml
# 等待Calico Pod就绪
kubectl get pods -n kube-system -w
# 看到calico-node-* 变为Running即表示成功
# 再次检查节点状态
kubectl get nodes
# Master应该变为Ready
4.7 Node节点加入集群
# 在Node节点上执行(token从kubeadm init的输出中获取)
sudo kubeadm join 192.168.1.100:6443 \
--token abcdef.0123456789abcdef \
--discovery-token-ca-cert-hash sha256:xxxxxxx
# 如果token过期了,在Master上重新生成
kubeadm token create --print-join-command
# 在Master上验证
kubectl get nodes
# 应该能看到所有节点状态为Ready
互动提问:
--pod-network-cidr=10.244.0.0/16是什么意思?这是Pod的IP地址范围——集群中每个Pod都会从这个网段中获得一个唯一IP。就像学校给每个班级分配教室号段:高一1-20号,高二21-40号……
五、kubectl常用命令:与K8s对话
5.1 资源查看
# ===== 获取资源列表 =====
kubectl get pods # 查看Pod
kubectl get pods -o wide # 显示更多信息(IP、所在节点)
kubectl get pods -A # 查看所有命名空间
kubectl get pods -n kube-system # 查看指定命名空间
kubectl get pods -l app=nginx # 按标签筛选
kubectl get pods -w # 持续监控(watch模式)
kubectl get deployments # 查看Deployment
kubectl get services # 查看Service(简写svc)
kubectl get nodes # 查看节点
kubectl get all # 查看所有资源
kubectl get events --sort-by='.metadata.creationTimestamp' # 查看事件
# ===== 格式化输出 =====
kubectl get pods -o json # JSON格式
kubectl get pods -o yaml # YAML格式
kubectl get pods -o jsonpath='{.items[*].status.podIP}' # 只提取IP
kubectl get pods -o custom-columns=NAME:.metadata.name,STATUS:.status.phase # 自定义列
5.2 资源详情与调试
# ===== 描述资源(排查问题的利器)=====
kubectl describe pod my-pod # Pod详情(事件、状态、资源)
kubectl describe node node1 # 节点详情
kubectl describe svc my-service # Service详情
# ===== 日志 =====
kubectl logs my-pod # 查看Pod日志
kubectl logs -f my-pod # 实时跟踪日志
kubectl logs my-pod -c container-name # 多容器Pod指定容器
kubectl logs --previous my-pod # 查看上一次崩溃前的日志
kubectl logs --since=1h my-pod # 最近1小时日志
# ===== 进入容器 =====
kubectl exec -it my-pod -- /bin/bash # 进入Pod中的容器
kubectl exec -it my-pod -c container-name -- /bin/bash # 多容器时指定
# ===== 端口转发(本地调试)=====
kubectl port-forward pod/my-pod 8080:80 # 本地8080转发到Pod的80
kubectl port-forward svc/my-service 8080:80 # 转发到Service
5.3 资源创建与管理
# ===== 创建资源 =====
kubectl apply -f pod.yaml # 从YAML文件创建/更新(推荐)
kubectl apply -f ./config-dir/ # 应用目录下所有YAML
kubectl apply -f https://url/to/file.yaml # 从URL应用
# ===== 命令行快速创建(不写YAML)=====
kubectl create deployment nginx --image=nginx:1.25
kubectl create service clusterip my-svc --tcp=80:8080
kubectl run debug --image=busybox -it --rm -- /bin/sh # 临时调试Pod
# ===== 修改资源 =====
kubectl scale deployment nginx --replicas=5 # 扩容/缩容
kubectl edit deployment nginx # 在线编辑(打开vim)
kubectl patch deployment nginx -p '{"spec":{"replicas":3}}' # 补丁更新
# ===== 删除资源 =====
kubectl delete pod my-pod # 删除Pod
kubectl delete -f pod.yaml # 按文件删除
kubectl delete pods,services -l app=nginx # 按标签批量删除
kubectl delete pod my-pod --force --grace-period=0 # 强制删除
5.4 命令速查表
| 操作 | 命令 | 说明 |
|---|---|---|
| 查看 | kubectl get | 列出资源 |
| 详情 | kubectl describe | 资源详细信息 |
| 创建 | kubectl apply -f | 声明式创建/更新 |
| 删除 | kubectl delete | 删除资源 |
| 日志 | kubectl logs | 查看容器日志 |
| 执行 | kubectl exec | 在容器内执行命令 |
| 扩容 | kubectl scale | 修改副本数 |
| 标签 | kubectl label | 添加/修改标签 |
| 注释 | kubectl annotate | 添加注释 |
| 调试 | kubectl debug | 调试运行中的资源 |
六、Dashboard安装:K8s的图形化管理界面
# ===== 安装Dashboard =====
kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.7.0/aio/deploy/recommended.yaml
# ===== 创建管理员账号 =====
cat << EOF | kubectl apply -f -
apiVersion: v1
kind: ServiceAccount
metadata:
name: admin-user
namespace: kubernetes-dashboard
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: admin-user
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- kind: ServiceAccount
name: admin-user
namespace: kubernetes-dashboard
EOF
# ===== 获取登录Token =====
kubectl -n kubernetes-dashboard create token admin-user
# ===== 启动代理访问 =====
kubectl proxy
# 浏览器访问:
# http://localhost:8001/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/
七、核心资源详解
7.1 Pod —— K8s的最小调度单元
什么是Pod?
类比:Pod就像一间"宿舍"——里面可以住1个人(单容器Pod)或多个人(多容器Pod)。同一个Pod中的容器共享网络(同一个IP)和存储(可以共享数据卷),就像室友共用一个WiFi和一个卫生间。
为什么不直接管理容器? 因为容器之间的协作关系很重要——Nginx和日志采集器总是成对出现,它们需要共享网络和存储,所以把它们"打包"成一个Pod来管理。
Pod生命周期
Pending(等待调度)
↓
ContainerCreating(拉取镜像、创建容器)
↓
Running(运行中)──── 探针检查 ────→ Failed(失败)/ Succeeded(正常退出)
↓
Terminating(正在终止)
↓
Unknown(未知,通常是节点失联)
Pod YAML示例
apiVersion: v1
kind: Pod
metadata:
name: my-web-pod
labels:
app: web
version: v1
spec:
containers:
- name: nginx
image: nginx:1.25
ports:
- containerPort: 80
# 资源限制
resources:
requests: # 最少需要的资源
memory: "64Mi"
cpu: "250m" # 0.25个CPU核心
limits: # 最多能用的资源
memory: "128Mi"
cpu: "500m" # 0.5个CPU核心
# 存活探针
livenessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 10 # 启动后10秒开始检查
periodSeconds: 5 # 每5秒检查一次
# 就绪探针
readinessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 5
periodSeconds: 3
# 启动探针(K8s 1.16+)
startupProbe:
httpGet:
path: /
port: 80
failureThreshold: 30
periodSeconds: 10
三种探针详解
| 探针类型 | 作用 | 类比 | 失败后果 |
|---|---|---|---|
| livenessProbe | 检查容器是否存活 | 查房:人还在不在? | 重启容器 |
| readinessProbe | 检查容器是否就绪 | 考核:能不能接客了? | 从Service中摘除 |
| startupProbe | 检查容器是否已启动 | 闹钟:醒没醒? | 启动完成前其他探针不生效 |
多容器Pod模式
apiVersion: v1
kind: Pod
metadata:
name: web-with-sidecar
spec:
# 共享存储卷
volumes:
- name: shared-logs
emptyDir: {}
containers:
# 主容器:Nginx
- name: nginx
image: nginx:1.25
volumeMounts:
- name: shared-logs
mountPath: /var/log/nginx
# Sidecar容器:日志采集
- name: log-collector
image: busybox
command: ['sh', '-c', 'tail -f /var/log/nginx/access.log']
volumeMounts:
- name: shared-logs
mountPath: /var/log/nginx
互动提问:
emptyDir类型的Volume在Pod被删除后数据还在吗?(答案:不在,emptyDir随Pod一起删除。如果需要持久化,要用PVC。)
7.2 ReplicaSet —— 保证副本数
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: nginx-rs
spec:
replicas: 3 # 始终保持3个副本
selector:
matchLabels:
app: nginx
template: # Pod模板
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.25
ports:
- containerPort: 80
注意:实际使用中很少直接创建ReplicaSet,而是通过Deployment来管理(Deployment会自动创建和管理ReplicaSet)。
7.3 Deployment —— 生产环境的主力
基本Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 3 # 副本数
selector:
matchLabels:
app: nginx
strategy:
type: RollingUpdate # 滚动更新策略
rollingUpdate:
maxSurge: 1 # 更新时最多多出1个Pod
maxUnavailable: 0 # 更新时不允许有Pod不可用
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.25
ports:
- containerPort: 80
resources:
requests:
memory: "64Mi"
cpu: "250m"
limits:
memory: "128Mi"
cpu: "500m"
扩容与缩容
# 命令行扩容
kubectl scale deployment nginx-deployment --replicas=5
# 缩容
kubectl scale deployment nginx-deployment --replicas=2
# 自动扩容(HPA,后面详解)
kubectl autoscale deployment nginx-deployment --min=2 --max=10 --cpu-percent=80
滚动更新
# 更新镜像版本
kubectl set image deployment/nginx-deployment nginx=nginx:1.26
# 观察更新进度
kubectl rollout status deployment/nginx-deployment
# 查看更新历史
kubectl rollout history deployment/nginx-deployment
# 查看特定版本的详情
kubectl rollout history deployment/nginx-deployment --revision=3
回滚
# 回滚到上一个版本
kubectl rollout undo deployment/nginx-deployment
# 回滚到指定版本
kubectl rollout undo deployment/nginx-deployment --to-revision=2
# 暂停和恢复更新(批量修改时使用)
kubectl rollout pause deployment/nginx-deployment
kubectl set image deployment/nginx-deployment nginx=nginx:1.26
kubectl set resources deployment/nginx-deployment -c=nginx --limits=cpu=200m,memory=256Mi
kubectl rollout resume deployment/nginx-deployment # 一次性应用所有修改
滚动更新原理:
初始状态:3个旧版本Pod
[旧v1] [旧v1] [旧v1]
更新开始(maxSurge=1, maxUnavailable=0):
[旧v1] [旧v1] [旧v1] [新v2] ← 先创建1个新Pod
新Pod就绪后,删除1个旧Pod:
[旧v1] [旧v1] [新v2] [新v2] ← 再创建1个新,删1个旧
继续滚动:
[旧v1] [新v2] [新v2] [新v2]
完成:
[新v2] [新v2] [新v2] ← 全部更新,服务从未中断!
互动提问:如果更新到一半发现新版本有bug怎么办?用
kubectl rollout undo一键回滚到之前的版本——就像游戏的"读档"功能。
7.4 Service —— 稳定的访问入口
为什么需要Service?
问题:Pod的IP是动态的——每次重启、扩容、迁移,IP都会变。客户端怎么找到一个稳定的地址?
类比:Pod就像外卖小哥,每天都在换人(IP变化)。但外卖平台(Service)给你一个固定的订单号,不管哪个小哥来送,你都能收到餐。
四种Service类型
# ===== ClusterIP(默认,集群内访问)=====
apiVersion: v1
kind: Service
metadata:
name: nginx-clusterip
spec:
type: ClusterIP # 默认类型
selector:
app: nginx # 通过标签选择后端Pod
ports:
- port: 80 # Service端口
targetPort: 80 # Pod端口
# 只能通过集群内部IP访问,外部无法直接访问
---
# ===== NodePort(节点端口,外部访问)=====
apiVersion: v1
kind: Service
metadata:
name: nginx-nodeport
spec:
type: NodePort
selector:
app: nginx
ports:
- port: 80 # Service端口(集群内访问)
targetPort: 80 # Pod端口
nodePort: 30080 # 节点端口(30000-32767,外部访问)
# 访问方式:http://任意节点IP:30080
---
# ===== LoadBalancer(云厂商负载均衡器)=====
apiVersion: v1
kind: Service
metadata:
name: nginx-lb
spec:
type: LoadBalancer
selector:
app: nginx
ports:
- port: 80
targetPort: 80
# 云厂商(AWS/GCP/阿里云)自动创建一个外部负载均衡器
---
# ===== ExternalName(外部服务别名)=====
apiVersion: v1
kind: Service
metadata:
name: external-db
spec:
type: ExternalName
externalName: mysql.school.edu.cn # 指向外部域名
# 集群内访问 external-db 会自动解析到 mysql.school.edu.cn
Service类型对比
| 类型 | 访问范围 | 使用场景 |
|---|---|---|
| ClusterIP | 集群内部 | 后端微服务间通信 |
| NodePort | 集群外部(通过节点IP+端口) | 开发测试、小型部署 |
| LoadBalancer | 集群外部(通过负载均衡器) | 生产环境、云环境 |
| ExternalName | 指向外部服务 | 对接集群外的数据库等 |
# Service常用命令
kubectl get svc # 查看所有Service
kubectl describe svc nginx-nodeport # 查看详情
kubectl get endpoints nginx-clusterip # 查看后端Pod列表
7.5 Volume —— Pod的存储方案
emptyDir(临时共享存储)
apiVersion: v1
kind: Pod
metadata:
name: cache-pod
spec:
containers:
- name: app
image: myapp:latest
volumeMounts:
- name: cache
mountPath: /cache
- name: sidecar
image: busybox
command: ['sh', '-c', 'watch -n5 ls /cache']
volumeMounts:
- name: cache
mountPath: /cache
volumes:
- name: cache
emptyDir: {}
# emptyDir:
# medium: Memory # 可选:使用内存(tmpfs)
# sizeLimit: 100Mi # 可选:大小限制
# Pod删除时,emptyDir数据也删除
hostPath(宿主机目录)
volumes:
- name: host-data
hostPath:
path: /data/myapp
type: DirectoryOrCreate # 目录不存在时自动创建
# 注意:hostPath绑定到特定节点,Pod调度到其他节点就读不到了
PVC/PV(持久化存储,推荐方案)
概念:
- PersistentVolume(PV):管理员预先准备的存储资源(像一块"硬盘")
- PersistentVolumeClaim(PVC):用户提出的存储需求(像一份"存储申请单")
- K8s自动将PVC绑定到合适的PV上
# ===== PV定义(管理员创建)=====
apiVersion: v1
kind: PersistentVolume
metadata:
name: mysql-pv
spec:
capacity:
storage: 10Gi # 10GB存储
accessModes:
- ReadWriteOnce # 单节点读写
persistentVolumeReclaimPolicy: Retain # 释放后保留数据
hostPath: # 使用宿主机目录(测试用)
path: /data/mysql-pv
---
# ===== PVC定义(用户创建)=====
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mysql-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi # 需要5GB
---
# ===== 在Pod中使用PVC =====
apiVersion: v1
kind: Pod
metadata:
name: mysql-pod
spec:
containers:
- name: mysql
image: mysql:8.0
env:
- name: MYSQL_ROOT_PASSWORD
value: "123456"
volumeMounts:
- name: mysql-storage
mountPath: /var/lib/mysql
volumes:
- name: mysql-storage
persistentVolumeClaim:
claimName: mysql-pvc # 引用上面创建的PVC
ConfigMap和Secret(配置与密钥)
# ===== ConfigMap(存储配置信息)=====
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
APP_ENV: production
APP_DEBUG: "false"
nginx.conf: |
server {
listen 80;
root /usr/share/nginx/html;
}
---
# ===== Secret(存储敏感信息,Base64编码)=====
apiVersion: v1
kind: Secret
metadata:
name: db-secret
type: Opaque
data:
username: cm9vdA== # echo -n 'root' | base64
password: c2VjcmV0MTIz # echo -n 'secret123' | base64
---
# ===== 在Pod中使用 =====
apiVersion: v1
kind: Pod
metadata:
name: app-pod
spec:
containers:
- name: app
image: myapp:latest
env:
- name: APP_ENV
valueFrom:
configMapKeyRef:
name: app-config
key: APP_ENV
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-secret
key: password
volumeMounts:
- name: config-volume
mountPath: /etc/config
volumes:
- name: config-volume
configMap:
name: app-config
快速创建ConfigMap和Secret:
# 从文件创建ConfigMap
kubectl create configmap nginx-conf --from-file=nginx.conf
# 从环境变量创建ConfigMap
kubectl create configmap app-env --from-literal=APP_ENV=production --from-literal=APP_DEBUG=false
# 从文件创建Secret
kubectl create secret generic tls-secret --from-file=tls.crt --from-file=tls.key
# 从字符串创建Secret
kubectl create secret generic db-secret --from-literal=username=root --from-literal=password=123456
互动提问:ConfigMap和Secret有什么区别?本质相同,但Secret的数据经过Base64编码(注意:不是加密!),并且可以设置更严格的访问权限。生产环境应配合RBAC和加密存储。
7.6 Job和CronJob —— 一次性与定时任务
Job(一次性任务)
apiVersion: batch/v1
kind: Job
metadata:
name: data-migration
spec:
completions: 1 # 需要成功完成的Pod数
parallelism: 1 # 并行运行的Pod数
backoffLimit: 3 # 失败后最多重试3次
activeDeadlineSeconds: 3600 # 超时时间1小时
template:
spec:
containers:
- name: migrate
image: myapp:latest
command: ["python", "migrate.py"]
restartPolicy: Never # Job的Pod失败后不自动重启,由Job控制器重试
CronJob(定时任务)
apiVersion: batch/v1
kind: CronJob
metadata:
name: db-backup
spec:
schedule: "0 2 * * *" # cron表达式:每天凌晨2点
# ┌─── 分钟(0-59)
# │┌─── 小时(0-23)
# ││┌─── 日期(1-31)
# │││┌─── 月份(1-12)
# ││││┌─── 星期几(0-7,0和7都是周日)
concurrencyPolicy: Forbid # 禁止并发执行
successfulJobsHistoryLimit: 3 # 保留最近3次成功记录
failedJobsHistoryLimit: 1 # 保留最近1次失败记录
jobTemplate:
spec:
template:
spec:
containers:
- name: backup
image: mysql:8.0
command:
- sh
- -c
- "mysqldump -h mysql-service -uroot -p'$MYSQL_ROOT_PASSWORD' --all-databases > /backup/full_$(date +\\%Y\\%m\\%d).sql"
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: db-secret
key: password
volumeMounts:
- name: backup-storage
mountPath: /backup
volumes:
- name: backup-storage
persistentVolumeClaim:
claimName: backup-pvc
restartPolicy: OnFailure
7.7 DaemonSet —— 每个节点一个Pod
场景:日志采集、监控代理、网络插件——需要在每个节点上运行一个实例。
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: node-exporter
labels:
app: node-exporter
spec:
selector:
matchLabels:
app: node-exporter
template:
metadata:
labels:
app: node-exporter
spec:
containers:
- name: exporter
image: prom/node-exporter:latest
ports:
- containerPort: 9100
hostPort: 9100 # 绑定到宿主机端口
hostNetwork: true # 使用宿主机网络(可选)
类比:DaemonSet就像宿舍楼里的"清洁阿姨"——每层楼都有一个,确保每层都被打扫到。
7.8 HPA —— 水平自动扩缩容
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: web-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: nginx-deployment
minReplicas: 2 # 最少2个副本
maxReplicas: 10 # 最多10个副本
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70 # CPU使用率超过70%则扩容
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80 # 内存使用率超过80%则扩容
# 命令行快速创建HPA
kubectl autoscale deployment nginx-deployment --min=2 --max=10 --cpu-percent=70
# 查看HPA状态
kubectl get hpa
# 查看HPA详情(可以看到当前指标和建议副本数)
kubectl describe hpa web-hpa
HPA工作原理:
监控指标(CPU/内存/自定义)
↓
Metrics Server 采集数据
↓
HPA控制器每15秒检查一次
↓
┌───────────────────────────────┐
│ 当前CPU 85% > 目标 70% │ → 扩容(增加Pod)
│ 当前CPU 30% < 目标 70% │ → 缩容(减少Pod,等待稳定期)
│ 当前CPU 65% ≈ 目标 70% │ → 保持不变
└───────────────────────────────┘
类比:餐厅的"自动加桌系统"
- 客人多了(CPU升高)→ 自动开更多桌子(加Pod)
- 客人少了(CPU降低)→ 等一阵确认不是暂时的(稳定期),然后收桌子(减Pod)
互动提问:为什么缩容要设置"稳定期"(默认5分钟)?——防止"抖动":如果流量刚降下来又涨上去,频繁扩缩容反而会影响服务稳定性。
八、综合实战:部署一个完整的Web应用
# ===== 1. ConfigMap:应用配置 =====
apiVersion: v1
kind: ConfigMap
metadata:
name: school-app-config
data:
APP_MODE: production
DB_HOST: mysql-service
DB_NAME: school
---
# ===== 2. Secret:数据库密码 =====
apiVersion: v1
kind: Secret
metadata:
name: school-db-secret
type: Opaque
data:
password: U2Nob29sMjAyNCE= # echo -n 'School2024!' | base64
---
# ===== 3. PVC:数据库持久存储 =====
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mysql-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
---
# ===== 4. Deployment:MySQL数据库 =====
apiVersion: apps/v1
kind: Deployment
metadata:
name: mysql
spec:
replicas: 1 # 数据库通常单实例
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
containers:
- name: mysql
image: mysql:8.0
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: school-db-secret
key: password
- name: MYSQL_DATABASE
valueFrom:
configMapKeyRef:
name: school-app-config
key: DB_NAME
ports:
- containerPort: 3306
volumeMounts:
- name: mysql-data
mountPath: /var/lib/mysql
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
volumes:
- name: mysql-data
persistentVolumeClaim:
claimName: mysql-pvc
---
# ===== 5. Service:MySQL内部服务 =====
apiVersion: v1
kind: Service
metadata:
name: mysql-service
spec:
type: ClusterIP
selector:
app: mysql
ports:
- port: 3306
targetPort: 3306
---
# ===== 6. Deployment:Web应用 =====
apiVersion: apps/v1
kind: Deployment
metadata:
name: school-web
spec:
replicas: 3
selector:
matchLabels:
app: school-web
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
template:
metadata:
labels:
app: school-web
spec:
containers:
- name: web
image: school-webapp:latest
envFrom:
- configMapRef:
name: school-app-config
env:
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: school-db-secret
key: password
ports:
- containerPort: 80
livenessProbe:
httpGet:
path: /health
port: 80
initialDelaySeconds: 15
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 80
initialDelaySeconds: 5
periodSeconds: 5
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "256Mi"
cpu: "200m"
---
# ===== 7. Service:Web外部访问 =====
apiVersion: v1
kind: Service
metadata:
name: school-web-service
spec:
type: NodePort
selector:
app: school-web
ports:
- port: 80
targetPort: 80
nodePort: 30080
---
# ===== 8. HPA:自动扩缩容 =====
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: school-web-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: school-web
minReplicas: 3
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
---
# ===== 9. CronJob:定时数据库备份 =====
apiVersion: batch/v1
kind: CronJob
metadata:
name: mysql-backup
spec:
schedule: "0 3 * * *"
jobTemplate:
spec:
template:
spec:
containers:
- name: backup
image: mysql:8.0
command:
- sh
- -c
- "mysqldump -h mysql-service -uroot -p'School2024!' --all-databases > /backup/backup_$(date +\\%Y\\%m\\%d).sql"
volumeMounts:
- name: backup-vol
mountPath: /backup
volumes:
- name: backup-vol
hostPath:
path: /data/backup
type: DirectoryOrCreate
restartPolicy: OnFailure
# 一键部署
kubectl apply -f school-app.yaml
# 查看部署状态
kubectl get all
kubectl get pvc
kubectl get hpa
# 访问应用
# http://任意节点IP:30080
九、常见故障排查
| 问题 | 排查命令 | 可能原因 |
|---|---|---|
| Pod一直Pending | kubectl describe pod <name> | 资源不足、调度约束不满足 |
| Pod不断CrashLoopBackOff | kubectl logs <pod> --previous | 应用启动失败、配置错误 |
| ImagePullBackOff | kubectl describe pod <name> | 镜像不存在、认证失败、网络问题 |
| Service无法访问 | kubectl get endpoints <svc> | 标签不匹配、后端Pod未就绪 |
| PVC一直Pending | kubectl describe pvc <name> | 没有可用的PV、存储类不存在 |
| Node状态NotReady | kubectl describe node <name> | kubelet异常、网络不通、资源耗尽 |
# 排查三板斧
kubectl get pods -o wide # 1. 看状态和所在节点
kubectl describe pod <pod-name> # 2. 看事件和详情
kubectl logs <pod-name> [-c container] # 3. 看应用日志
运维常用命令精选
# === 集群信息 ===
kubectl cluster-info # 集群信息
kubectl get nodes -o wide # 节点详情
kubectl get componentstatuses # 组件健康状态(旧版)
kubectl get cs # 同上(简写)
# === 资源管理 ===
kubectl get all -A # 所有命名空间的所有资源
kubectl apply -f . # 应用当前目录所有YAML
kubectl delete -f . # 删除当前目录所有YAML定义的资源
kubectl get events --sort-by='.metadata.creationTimestamp' --field-selector type=Warning # 只看告警事件
# === Deployment操作 ===
kubectl rollout status deployment/<name> # 查看更新进度
kubectl rollout history deployment/<name> # 更新历史
kubectl rollout undo deployment/<name> # 回滚
kubectl rollout restart deployment/<name> # 重启(滚动重启所有Pod)
kubectl scale deployment/<name> --replicas=5 # 扩容
# === 调试工具 ===
kubectl run debug --image=busybox -it --rm -- sh # 临时调试Pod
kubectl port-forward svc/<svc-name> 8080:80 # 端口转发
kubectl cp <pod>:/path/file ./local-file # 文件拷贝
kubectl top pods # 查看Pod资源使用
kubectl top nodes # 查看节点资源使用
# === 标签管理 ===
kubectl label pod my-pod env=production # 添加标签
kubectl label pod my-pod env- # 删除标签
kubectl get pods -l 'env in (production, staging)' # 集合选择器
kubectl get pods -l 'app,env=production' # 存在性+等值选择
# === 命名空间 ===
kubectl create namespace dev # 创建命名空间
kubectl get namespaces # 列出命名空间
kubectl config set-context --current --namespace=dev # 切换默认命名空间
趣味命令
# 1. 看看K8s集群有多少API资源
kubectl api-resources | wc -l
# 看看都有哪些资源类型
kubectl api-resources --sort-by=name
# 2. 查看K8s的版本信息(像看汽车铭牌)
kubectl version --short 2>/dev/null || kubectl version
# 3. 用kubectl跑一个临时Web服务器
kubectl run httpd --image=httpd:alpine --port=80 --expose
kubectl get svc httpd
# 然后 port-forward 访问
# 4. 看看etcd里存了多少数据(仅了解,不建议直接操作)
kubectl -n kube-system get pods -l component=etcd
# 5. 查看节点的"能力"(Allocatable资源)
kubectl get nodes -o custom-columns=\
NAME:.metadata.name,\
CPU:.status.allocatable.cpu,\
MEMORY:.status.allocatable.memory,\
PODS:.status.allocatable.pods
# 6. 一键查看所有Pod的镜像版本
kubectl get pods -A -o jsonpath='{range .items[*]}{.metadata.namespace}/{.metadata.name}{"\t"}{range .spec.containers[*]}{.image}{", "}{end}{"\n"}{end}'
# 7. 在集群里养一只"牛"(cowsay)
kubectl run cow --image=docker/whalesay --restart=Never -- cowsay "K8s is fun!"
kubectl logs cow
kubectl delete pod cow
# 8. 查看集群的DNS是否工作正常
kubectl run dns-test --image=busybox --restart=Never -- nslookup kubernetes.default
kubectl logs dns-test
kubectl delete pod dns-test
# 9. 看看K8s的"心电图"——最近的事件流
kubectl get events --all-namespaces --sort-by='.lastTimestamp' | tail -20
# 10. 给集群"体检"
kubectl get nodes -o json | python3 -c "
import json, sys
data = json.load(sys.stdin)
for node in data['items']:
name = node['metadata']['name']
conditions = {c['type']: c['status'] for c in node['status']['conditions']}
ready = conditions.get('Ready', 'Unknown')
print(f'{name}: Ready={ready}')
"
小结:Kubernetes是容器编排的事实标准,它通过Master-Node架构管理整个集群。Pod是最小调度单元,Deployment管理Pod的副本和更新,Service提供稳定的访问入口,PVC/PV解决存储持久化,HPA实现自动扩缩容。YAML是描述这些资源的"语言",kubectl是与K8s对话的"话筒"。