Kubernetes
Kubernetes 是开源的容器编排平台,用于自动化容器化应用的部署、扩展和管理。它解决的核心问题是:容器规模一大,手动管理部署、扩缩容、故障恢复就不现实了——Kubernetes 把"应用应该处于什么状态"声明出来,由系统自动收敛到该状态。
Kubernetes 这个名字源于希腊语,意为"舵手"或"飞行员"。k8s 这个缩写是因为 k 和 s 之间有八个字符的关系。
本目录下还有 Kubernetes存储与配置(PV/PVC、ConfigMap/Secret)和 Kubernetes调度(调度流程、污点容忍、资源限制),可配合阅读。
Kubernetes 能做什么
Kubernetes 提供一个可弹性运行分布式系统的框架,满足扩展、故障转移、部署模式等需求。例如,Kubernetes 可以轻松管理系统的 Canary 部署(金丝雀部署:先让新版本服务一小部分流量,验证无误后再全量替换)。
核心能力分六块:
| 能力 | 解决什么问题 | 典型场景 |
|---|---|---|
| 服务发现和负载均衡 | 容器 IP 会变,调用方不该硬编码地址 | DNS 名或集群 IP 访问服务,流量自动分发 |
| 存储编排 | 容器重建会丢数据,存储要独立于容器 | 自动挂载本地盘、云盘、NFS |
| 自动部署和回滚 | 版本升级要可控,出问题能快速回退 | 滚动更新、Canary 发布 |
| 自动装箱计算 | 节点资源怎么分配最省 | 按容器的 CPU/内存请求合理放置 |
| 自我修复 | 容器挂了、节点挂了不能靠人盯 | 自动重启失败容器、替换故障节点上的 Pod |
| 密钥与配置管理 | 密码不该写死在镜像里 | 密钥与应用配置分离,不重建镜像即可更新 |
核心对象
Kubernetes 管理应用的最小单元是 Pod,围绕它构建了稳定访问(Service)、工作负载管理(Workload)与流量入口(Ingress)三层对象体系。存储与配置对象(PV/PVC、ConfigMap/Secret)见 Kubernetes存储与配置。
标签与选择器
Label(标签)是挂在资源上的键值对,如 app: nginx、env: prod,用于标识和分类资源;Selector(选择器)按标签挑选资源,是各组件协作的纽带:Service 用 spec.selector 圈定后端 Pod,调度用 nodeSelector 选节点,kubectl 也能按标签过滤。
kubectl label pods mynginx app=nginx # 打标签
kubectl get pods -l app=nginx # 按标签过滤
kubectl label pods mynginx app- # 删除标签Label 与 Annotation 的区别
Label 可被选择器查询(-l 过滤、Service 匹配),是“可检索的身份”;Annotation(注解)只能附加说明信息(版本、联系人、配置提示),不能用于选择——区别在于是否参与检索。
Pod
Pod 是 Kubernetes 的最小调度单位,代表一个或多个共享网络和存储的容器。同一 Pod 内的容器共享 localhost 和存储卷,通常一个 Pod 只运行一个主容器。
apiVersion: v1
kind: Pod
metadata:
labels:
run: mynginx
name: mynginx
spec:
containers:
- image: nginx
name: mynginx健康探针
Kubernetes 靠探针判断容器是否健康,三种探针回答三个不同的问题:
| 探针 | 回答的问题 | 失败后果 |
|---|---|---|
| liveness(存活) | 容器还活着吗 | 重启容器 |
| readiness(就绪) | 能接收流量吗 | 从 Service 后端摘除 |
| startup(启动) | 启动完成了吗 | 完成前不执行其他探针,避免慢启动容器被误杀 |
检测方式有三种:HTTP GET(访问指定路径看返回码)、TCP Socket(能否建立连接)、Exec(执行命令看退出码)。以 HTTP 探针为例:
spec:
containers:
- name: app
image: app:v1
livenessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 5 # 启动 5 秒后再探测
periodSeconds: 10 # 每 10 秒探测一次常用命令:
kubectl run mynginx --image=nginx # 创建
kubectl get pod # 查看
kubectl describe pod pod名字 # 描述
kubectl delete pod mynginx # 删除(-n 指定命名空间)
kubectl logs pod名字 # 查看日志
kubectl get pod -o wide # 查看 Pod IP 和所在节点实际生产建议用 Deployment 而非裸 Pod
裸 Pod 被删除或节点故障后不会自动重建;用 Deployment 声明副本数,控制面会自动维持,见下方 Workload。
Service
Service 解决的是Pod 地址不稳定的问题:Pod IP 随重建变化、副本数也会增减,调用方不能硬编码地址。Service 用标签选择器圈定一组 Pod,提供一个稳定的虚拟 IP(ClusterIP),发往该 IP 的流量由节点上的 kube-proxy 转发到后端 Pod。
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
selector:
app: nginx # 选择带该标签的 Pod
ports:
- port: 80 # Service 端口
targetPort: 80 # Pod 端口按对外暴露范围分三类:
| 类型 | 访问范围 | 说明 |
|---|---|---|
| ClusterIP | 集群内 | 默认类型,虚拟 IP 仅集群内可达 |
| NodePort | 节点 IP:端口 | 每个节点开放一个端口,把流量转发到 Service |
| LoadBalancer | 公网 | 云厂商负载均衡器接入 NodePort,对外提供公网 IP |
Workload
工作负载资源用于管理 Pod 的部署和运行,不同形态对应不同管理语义。选择依据是应用的特性:
| 类型 | 说明 | 适用场景 |
|---|---|---|
| Pod | 最小调度单位 | 直接管理容器 |
| Deployment | 无状态应用 | Web 服务、API |
| StatefulSet | 有状态应用 | 数据库、消息队列 |
| DaemonSet | 节点守护进程 | 日志收集、监控 |
| Job | 一次性任务 | 数据处理、批任务 |
| CronJob | 周期性任务 | 定时备份、报告 |
Deployment
Deployment 用于管理无状态应用,通过 ReplicaSet 控制 Pod 副本数量,支持滚动更新和回滚。大部分 Web 服务和 API 都使用 Deployment 部署。
更新镜像(如 kubectl set image deployment/webapp webapp=v2)时默认滚动更新:逐批替换 Pod,全程保持服务可用。节奏由两个参数控制:
| 参数 | 含义 | 示例 |
|---|---|---|
| maxUnavailable | 更新过程中最多允许几个旧副本不可用 | 25%:最多 1/4 副本同时下线 |
| maxSurge | 最多允许超出期望副本数几个 | 25%:先多启动 1/4 新副本再下线旧副本 |
spec:
strategy:
rollingUpdate:
maxUnavailable: 25%
maxSurge: 25%更新或回滚的常用操作:
kubectl rollout status deployment/webapp # 查看更新进度
kubectl rollout undo deployment/webapp # 回滚到上一个版本
kubectl rollout history deployment/webapp # 查看历史版本
kubectl rollout pause deployment/webapp # 暂停更新(配合分批验证)StatefulSet
StatefulSet 用于管理有状态应用(如 MySQL、Kafka),与 Deployment 的核心区别:Pod 有稳定的网络标识(pod-0、pod-1)和持久化存储,且按顺序部署和扩缩。
DaemonSet
DaemonSet 确保每个节点都运行一个 Pod 副本,适用于日志采集(Fluentd)、节点监控(Node Exporter)、网络插件(Calico)等需要每节点运行的守护进程。
Job 与 CronJob
Job 创建一次性任务,运行完成后 Pod 不会重启。CronJob 基于 Cron 表达式创建周期性任务,适合定时备份、报表生成等场景。
Ingress
Ingress 是集群的流量入口:Pod 与 Service 的 IP 只在内网可达,Ingress 把外部的 HTTP/HTTPS 请求按域名、路径路由到集群内部的 Service。
Ingress 本身只是规则声明,真正的转发由 Ingress Controller(如 Nginx Ingress)执行——装好 Controller 后,Ingress 规则才生效。
核心功能:
| 功能 | 说明 |
|---|---|
| 基于域名路由 | 根据请求域名分发到不同后端 |
| 基于路径路由 | 根据 URL 路径分发请求 |
| TLS 终止 | 解密 HTTPS 流量,减轻后端负担 |
| 负载均衡 | 多后端服务时自动分配流量 |
基础配置:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
ingressClassName: nginx
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: nginx-service
port:
number: 80Kubernetes 组件
Kubernetes 集群由控制平面和节点两部分组成:控制平面做决策(把 Pod 放哪、状态对不对),节点负责执行(真正把容器跑起来)。组件间全部通过 kube-apiserver 通信,架构如下:
控制平面组件
控制平面组件对集群做出全局决策(比如调度),并检测和响应集群事件。它们可运行在独立机器上(生产环境建议 3 台以上保障高可用),也可与节点混布(单机集群如 kubeadm 默认如此)。
kube-apiserver
kube-apiserver 是集群的唯一操作入口:kubectl 和所有组件都通过它通信,它不可用时整个集群的读写全部瘫痪。
机制:对外提供 RESTful API,每个请求经过认证(你是谁)、授权(你能做什么)、准入控制(请求内容是否合法)三层检查后,才写入 etcd;它是唯一直接读写 etcd 的组件。
边界:本身无状态,可多副本水平扩展(前面挂负载均衡);集群状态全在 etcd,它崩溃重启后从 etcd 恢复。
etcd
etcd 是集群的状态数据库:节点信息、Pod 状态、配置、期望状态全部存于此,组件崩溃后重启都能从这里恢复。
机制:分布式键值数据库,靠 Raft 共识协议在多个副本间保持一致,任何时刻对任何副本读写结果相同。
边界:存的是集群的"命根子",必须定期备份;它也是性能瓶颈——apiserver 的每次读写都经过它,它慢则整个集群响应慢。
kube-scheduler
kube-scheduler 负责为新 Pod 选择节点:集群节点资源参差不齐,人工分配不现实,调度器按规则自动决定"这个 Pod 放哪"。
机制分两阶段:过滤筛掉不满足条件的节点(资源不足、端口冲突、不容忍污点),评分按资源均衡等策略给剩余节点打分,选最高分者。详细机制见 Kubernetes调度。
边界:只决定"放哪",不负责"跑起来"(那是 kubelet 的事);集群可运行多个自定义调度器,按需接管特定 Pod。
kube-controller-manager
kube-controller-manager 负责维持期望状态:用户声明"我要 3 个副本",它确保实际永远向 3 个收敛。
机制是控制器循环(control loop):观察当前状态 → 对比期望状态 → 执行变更,周而复始。它内部是一组各管一摊的控制器:节点控制器(节点故障时驱逐 Pod)、Job 控制器(创建 Pod 跑一次性任务)、端点控制器(维护 Service 与 Pod 的对应关系)等。
边界:控制器只保证"向期望收敛",不保证瞬时一致;多个控制器并行运行,职责互不重叠。
cloud-controller-manager
cloud-controller-manager 把云厂商差异隔离在控制面之外:让 Kubernetes 核心逻辑不依赖具体云平台,需要负载均衡器、云路由等功能时由它对接云 API。
机制:每个云厂商提供自己的实现,Kubernetes 只定义统一接口。
边界:自建集群(物理机/虚拟机)不需要它;只有用云厂商资源(云负载均衡、云盘等)时才启用。
节点组件
节点组件在每个节点上运行,负责把控制面的决策变成真实运行的容器。
kubelet
kubelet 是节点上的执行者:接收控制面下发的 Pod 规格(PodSpec),调用容器运行时(containerd 等)启停容器,并持续汇报节点与 Pod 状态。
机制:周期性地向 apiserver 拉取分配给本节点的 Pod 清单,执行后上报;同时执行健康检查——liveness 探针失败会重启容器,readiness 探针失败则从 Service 后端摘除。
边界:只认 apiserver 下发的规格,是节点侧的权威;kubelet 故障时该节点的 Pod 无法自愈。
kube-proxy
kube-proxy 实现 Service 的流量转发:Pod 的 IP 会随重建变化,Service 提供一个稳定虚拟 IP,kube-proxy 在节点上维护转发规则,把发往 Service 的流量送到后端 Pod。
机制:监听 apiserver 中的 Service/Endpoint 变化,写入 iptables 或 IPVS 规则;客户端访问 Service IP 时,规则把包转发给真实 Pod。
边界:只做转发,不做服务发现(谁在服务由控制面决定);较老的用户态代理模式(userspace)性能差,已被 iptables/IPVS 取代。
安装 Kubernetes
安装方式取决于集群规模与目的:生产集群用 kubeadm(官方推荐,可管理生命周期),单机体验用 minikube/k3s,托管集群(云厂商容器服务)则无需自己装。下面以 kubeadm 装一套多节点集群为例。
安装前先逐项核对三个前提:
准备工作
确保机器唯一
# 查看主机名
hostname
# 查看网络
ip link
# 或
ifconfig -a
# 查看 product_uuid
cat /sys/class/dmi/id/product_uuid
# 一般情况下都会有唯一地址检查所需端口
端口是组件间通信的基础,控制平面与工作节点需要放行的端口不同:
控制平面节点:
| 协议 | 方向 | 端口范围 | 作用 | 使用者 |
|---|---|---|---|---|
| TCP | 入站 | 6443 | Kubernetes API 服务器 | 所有组件 |
| TCP | 入站 | 2379-2380 | etcd 服务器客户端 API | kube-apiserver, etcd |
| TCP | 入站 | 10250 | Kubelet API | kubelet 自身、控制平面组件 |
| TCP | 入站 | 10251 | kube-scheduler | kube-scheduler 自身 |
| TCP | 入站 | 10252 | kube-controller-manager | kube-controller-manager 自身 |
工作节点:
| 协议 | 方向 | 端口范围 | 作用 | 使用者 |
|---|---|---|---|---|
| TCP | 入站 | 10250 | Kubelet API | kubelet 自身、控制平面组件 |
| TCP | 入站 | 30000-32767 | NodePort 服务 | 所有组件 |
其他端口: 10248 是 kubelet 的健康检查端口(/healthz),排障时先用它确认 kubelet 是否存活。
关闭 swap
swapoff -a
sed -ri 's/.*swap.*/#&/' /etc/fstab为什么必须关 swap
kubelet 的容器内存管理基于 Cgroup 限值,swap 会让"超出内存限制"的容器被换出到磁盘而不是被杀掉,破坏资源保证与健康检查的语义;且 kubeadm 初始化会直接报错要求关闭。
允许 iptables 检查桥接流量
Kubernetes 的网络规则基于 iptables,但默认 iptables 不处理网桥(Linux bridge)上的流量,Pod 间通信会失效,需要显式开启:
cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
br_netfilter
EOF
cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
EOF
sudo sysctl --system将 SELinux 设置为 permissive 模式
RHEL/CentOS 默认开启的 SELinux 与容器运行时兼容性差(曾出现挂载、网络权限异常),官方安装文档要求先设为 permissive:
# 将 SELinux 设置为 permissive 模式(相当于将其禁用)
sudo setenforce 0
sudo sed -i 's/^SELINUX=enforcing$/SELINUX=permissive/' /etc/selinux/config安装 kubeadm、kubelet 和 kubectl
每台机器都需要装这三个工具,分工不同:
kubeadm:用来初始化集群的指令。kubelet:在集群中的每个节点上用来启动 Pod 和容器等。kubectl:用来与集群通信的命令行工具。
安装:
cat <<EOF | sudo tee /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64/
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg https://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg
exclude=kubelet kubeadm kubectl
EOF
sudo yum install -y kubelet kubeadm kubectl --disableexcludes=kubernetes
sudo systemctl enable --now kubelet无法从 /var/lib/rpm 打开软件包数据库(RPM 数据库损坏时):
cd /var/lib/rpm
rm -rf __db.*
rpm --rebuilddb使用 kubeadm 引导集群
引导集群分三步:先在各节点下载镜像,再初始化主节点,最后安装网络插件。
下载各个机器需要的镜像
国内网络拉取 k8s 官方镜像困难,这里用阿里云镜像仓库逐一拉取:
sudo tee ./images.sh <<-'EOF'
#!/bin/bash
images=(
kube-apiserver
kube-proxy
kube-controller-manager
kube-scheduler
coredns
etcd
pause
)
for imageName in ${images[@]} ;do
docker pull registry.aliyuncs.com/google_containers/$imageName
done
EOF
chmod +x ./images.sh && ./images.sh初始化主节点
# 所有机器添加 master 域名映射,以下需要修改为自己的
echo "39.103.233.115 cluster-endpoint" >> /etc/hosts
# 主节点初始化
kubeadm init \
--apiserver-advertise-address=39.103.233.115 \
--control-plane-endpoint=cluster-endpoint \
--image-repository registry.aliyuncs.com/google_containers \
--service-cidr=10.96.0.0/16 \
--pod-network-cidr=192.169.0.0/16
# 所有网络范围不重叠三个网段必须互不重叠
--service-cidr(Service 虚拟 IP 段)与 --pod-network-cidr(Pod IP 段)不能和物理网络、彼此重叠,否则流量路由错乱;初始化后再改 CIDR 需要 reset 重来。
改配置
kubectl -n kube-system edit cm kubeadm-config如果报错连不上容器(dial tcp 127.0.0.1:10248: connect: connection refused 的常见解法):
# 解决 kubeadm init 初始化时 dial tcp 127.0.0.1:10248: connect: connection refused
vim /etc/docker/daemon.json
# 里面加一行
{"exec-opts": ["native.cgroupdriver=systemd"]}
# 重启
systemctl daemon-reload
systemctl restart docker
systemctl restart kubelet
# 重新初始化
kubeadm reset
# 再用上面的命令初始化报错根源:cgroup 驱动不一致
kubelet 与容器运行时(docker)的 cgroup 驱动必须一致(都是 systemd 或都是 cgroupfs),否则 kubelet 反复崩溃、报 10248 连不上。新版本 kubelet 默认 systemd,把 docker 也改成 systemd 即可对齐。
看到 Your Kubernetes control-plane has initialized successfully! 说明初始化成功,保存初始化成功后面的信息,后面要用。完整输出如下:
kubeadm init 成功后的完整提示
To start using your cluster, you need to run the following as a regular user:
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
Alternatively, if you are the root user, you can run:
export KUBECONFIG=/etc/kubernetes/admin.conf
You should now deploy a pod network to the cluster.
Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
https://kubernetes.io/docs/concepts/cluster-administration/addons/
You can now join any number of control-plane nodes by copying certificate authorities
and service account keys on each node and then running the following as root:
kubeadm join cluster-endpoint:6443 --token aedzgz.zl1pio6oo06k7ajn \
--discovery-token-ca-cert-hash sha256:eded5d6f262d427e9ef9df1e248e33ba08ec08d9bbceb6a45e6432851af65186 \
--control-plane
Then you can join any number of worker nodes by running the following on each as root:
kubeadm join cluster-endpoint:6443 --token aedzgz.zl1pio6oo06k7ajn \
--discovery-token-ca-cert-hash sha256:eded5d6f262d427e9ef9df1e248e33ba08ec08d9bbceb6a45e6432851af65186重新创建令牌(token 24 小时过期,过期后用此命令重新生成):
kubeadm token create --print-join-command在主节点运行(配置 kubectl 访问集群的凭据):
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config主节点安装网络插件(Calico,提供 Pod 间网络):
# 下载配置
curl https://docs.projectcalico.org/manifests/calico.yaml -O
# 前面初始化如果 pod-network-cidr 改了的话,这里配置也要改
- name: CALICO_IPV4POOL_CIDR
value: "192.169.0.0/16"
# 添加
kubectl apply -f calico.yaml为什么 Pod 网络必须装
kubeadm 只初始化集群骨架,不装 Pod 网络插件时节点状态会一直 NotReady,Pod 无法跨节点通信。Calico 之外还有 Flannel、Weave 等选择,选一种即可。
部署 dashboard
dashboard 是 kubernetes 官方提供的可视化界面,安装后可在浏览器管理集群:
kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.4.0/aio/deploy/recommended.yaml修改配置:kubectl edit svc kubernetes-dashboard -n kubernetes-dashboard,把 type: 节点值改为 NodePort。
查看端口:kubectl get svc -A | grep kubernetes-dashboard,得到 NodePort 暴露的端口。
访问:https://任意集群ip:端口
创建访问账号(dashboard 默认没有可用账号,需创建管理员绑定):
# 创建访问账号 vi dash.yaml
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-dashboardkubectl apply -f dash.yaml获取访问令牌:
# 获取访问令牌
kubectl -n kubernetes-dashboard get secret $(kubectl -n kubernetes-dashboard get sa/admin-user -o jsonpath="{.secrets[0].name}") -o go-template="{{.data.token | base64decode}}"使用 Kubernetes
日常操作的核心思路是声明式管理:把期望状态写进 YAML,用 kubectl apply 提交,由控制面收敛,而不是一步步执行命令。
命名空间
命名空间对集群资源进行隔离划分,默认只隔离资源,不隔离网络。
# namespaces 可简写为 ns
kubectl get ns # 查看命名空间
kubectl create ns hello # 创建命名空间
kubectl delete ns hello # 删除命名空间(会一起删除资源)YAML 方式创建:
apiVersion: v1
kind: Namespace
metadata:
name: hello网络排查
Kubernetes 使用 Calico 组件实现 Pod 网络。当 Pod 之间无法通信时,使用 calicoctl 工具排查:
# 安装 calicoctl
wget -O /usr/local/bin/calicoctl https://github.com/projectcalico/calicoctl/releases/download/v3.21.2/calicoctl
chmod +x /usr/local/bin/calicoctl
# 查看节点状态
calicoctl node status
# 查看节点数据
calicoctl get node
# 查看 IP 地址池(确认 CIDR 是否与初始化时一致)
calicoctl get ippool -o wide