瑞克
Published on 2026-07-16 / 2 Visits
0
0

Kubernetes部署与核心

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:appname: app
数字被当字符串port: "80"port: 80
布尔值陷阱version: onversion: "on"("on"会被解析为true)

四、kubeadm部署Kubernetes集群

4.1 环境要求

项目最低要求
操作系统CentOS 7/8、Ubuntu 20.04+、Fedora 36+、麒麟V10
CPUMaster: 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一直Pendingkubectl describe pod <name>资源不足、调度约束不满足
Pod不断CrashLoopBackOffkubectl logs <pod> --previous应用启动失败、配置错误
ImagePullBackOffkubectl describe pod <name>镜像不存在、认证失败、网络问题
Service无法访问kubectl get endpoints <svc>标签不匹配、后端Pod未就绪
PVC一直Pendingkubectl describe pvc <name>没有可用的PV、存储类不存在
Node状态NotReadykubectl 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对话的"话筒"。


Comment