1. 为什么需要自建Kubernetes集群
在云原生技术大行其道的今天,Kubernetes已成为容器编排的事实标准。但很多开发者对Kubernetes的理解仅停留在使用云厂商提供的托管服务层面,这就像只会开车却不懂发动机原理的司机。自建集群的过程能让你真正理解Kubernetes的底层架构和工作机制。
我曾在生产环境遇到过因对集群原理不熟悉导致的严重故障:某个节点意外宕机后,整个服务雪崩。后来通过自建集群的实践,才深刻理解了控制器管理器、调度器这些核心组件的工作机制。这种认知在排查复杂问题时显得尤为重要。
自建集群主要适用于以下场景:
- 需要深度定制Kubernetes组件的企业级环境
- 对成本敏感且具备运维能力的中小团队
- 开发测试环境需要模拟真实集群行为的场景
- 需要将Kubernetes与特定硬件或网络环境集成的特殊需求
提示:虽然各大云平台都提供了托管Kubernetes服务,但自建集群能让你获得对系统更全面的掌控权,这种经验在故障排查和性能优化时非常宝贵。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 环境准备:打造坚实的底层基础
2.1 硬件资源配置方案
我推荐使用至少3台物理机或虚拟机组成集群:
- 控制平面节点:2核CPU/4GB内存/50GB磁盘(生产环境建议翻倍)
- Worker节点:根据工作负载调整,建议4核CPU/8GB内存/100GB磁盘
最近在为某AI初创公司搭建训练集群时,我们使用了如下配置:
bash复制# 控制节点 x3
CPU: 4核 Intel Xeon
内存: 16GB
磁盘: 200GB SSD
# Worker节点 x5(带GPU)
CPU: 16核
内存: 64GB
GPU: NVIDIA A100 x2
磁盘: 1TB NVMe
2.2 操作系统选择与优化
CentOS 7/8和Ubuntu 20.04/22.04是最稳定的选择。以CentOS 7为例,需要执行以下优化:
- 关闭Swap:
bash复制swapoff -a
sed -i '/ swap / s/^/#/' /etc/fstab
- 配置内核参数:
bash复制cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
vm.swappiness = 0
EOF
sysctl --system
- 安装基础依赖:
bash复制yum install -y conntrack-tools curl ebtables ethtool socat ipvsadm
2.3 容器运行时选型对比
| 运行时 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| Docker | 生态完善,文档丰富 | 已被K8s弃用 | 已有Docker环境的迁移 |
| Containerd | 轻量级,K8s原生支持 | 调试工具较少 | 生产环境首选 |
| CRI-O | 专为K8s设计 | 社区支持相对较弱 | OpenShift环境 |
我建议使用containerd:
bash复制yum install -y containerd
containerd config default > /etc/containerd/config.toml
sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml
systemctl enable --now containerd
3. 集群部署:从零搭建控制平面
3.1 使用kubeadm初始化控制节点
kubeadm是官方推荐的集群部署工具,以下是详细步骤:
- 配置Kubernetes仓库:
bash复制cat <<EOF | sudo tee /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://packages.cloud.google.com/yum/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg
EOF
- 安装kubelet/kubeadm/kubectl:
bash复制yum install -y kubelet-1.25.0 kubeadm-1.25.0 kubectl-1.25.0
systemctl enable --now kubelet
- 初始化控制平面(重要参数说明):
bash复制kubeadm init \
--apiserver-advertise-address=192.168.1.100 \
--pod-network-cidr=10.244.0.0/16 \
--service-cidr=10.96.0.0/12 \
--kubernetes-version=1.25.0 \
--image-repository=registry.aliyuncs.com/google_containers
初始化成功后,你会看到如下关键信息:
code复制Your Kubernetes control-plane has initialized successfully!
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
3.2 网络插件安装与配置
Flannel是最简单的选择,但Calico提供了更丰富的网络策略:
bash复制# Flannel安装
kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
# 或者使用Calico
kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.25.0/manifests/tigera-operator.yaml
kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.25.0/manifests/custom-resources.yaml
验证网络插件状态:
bash复制kubectl get pods -n kube-system
# 应该看到flannel或calico相关pod处于Running状态
3.3 Worker节点加入集群
在控制节点上获取join命令:
bash复制kubeadm token create --print-join-command
# 输出类似:kubeadm join 192.168.1.100:6443 --token xxxx --discovery-token-ca-cert-hash sha256:xxxx
在Worker节点上执行该命令,完成后验证节点状态:
bash复制kubectl get nodes
# 应该看到所有节点状态为Ready
4. 集群验证与关键组件检查
4.1 基础功能测试
创建测试Pod验证集群基本功能:
bash复制kubectl create deployment nginx --image=nginx
kubectl expose deployment nginx --port=80 --type=NodePort
kubectl get svc nginx
访问测试(假设NodePort为32000):
bash复制curl http://<任意节点IP>:32000
# 应该看到Nginx欢迎页面
4.2 核心组件健康检查
检查控制平面组件状态:
bash复制kubectl get componentstatus
# 所有组件应为Healthy
详细检查API Server:
bash复制kubectl get --raw='/readyz?verbose'
# 应该看到所有检查项通过
4.3 关键指标监控
安装Metrics Server获取基础监控数据:
bash复制kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml
查看节点资源使用情况:
bash复制kubectl top nodes
5. 应用部署实战:从简单到复杂
5.1 部署有状态应用:MySQL集群
使用StatefulSet部署MySQL:
yaml复制apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql
spec:
serviceName: "mysql"
replicas: 3
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
initContainers:
- name: init-mysql
image: mysql:5.7
command:
- bash
- "-c"
- |
set -ex
[[ `hostname` =~ -([0-9]+)$ ]] || exit 1
ordinal=${BASH_REMATCH[1]}
echo [mysqld] > /mnt/conf.d/server-id.cnf
echo server-id=$((100 + $ordinal)) >> /mnt/conf.d/server-id.cnf
if [[ $ordinal -eq 0 ]]; then
echo "binlog-format=ROW" >> /mnt/conf.d/master.cnf
echo "log-bin=mysql-bin" >> /mnt/conf.d/master.cnf
else
echo "server-id=$((100 + $ordinal))" > /mnt/conf.d/replica.cnf
echo "relay-log=mysql-relay-bin" >> /mnt/conf.d/replica.cnf
fi
volumeMounts:
- name: conf
mountPath: /mnt/conf.d
containers:
- name: mysql
image: mysql:5.7
env:
- name: MYSQL_ROOT_PASSWORD
value: "password"
ports:
- containerPort: 3306
name: mysql
volumeMounts:
- name: data
mountPath: /var/lib/mysql
- name: conf
mountPath: /etc/mysql/conf.d
volumes:
- name: conf
emptyDir: {}
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 10Gi
5.2 部署Web应用:三阶部署法
- 开发环境部署(使用Deployment):
yaml复制apiVersion: apps/v1
kind: Deployment
metadata:
name: web-dev
spec:
replicas: 1
selector:
matchLabels:
app: web
env: dev
template:
metadata:
labels:
app: web
env: dev
spec:
containers:
- name: web
image: my-web-app:latest
ports:
- containerPort: 8080
- 预发布环境(增加HPA和资源限制):
yaml复制apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: web-staging
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: web-staging
minReplicas: 2
maxReplicas: 5
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 50
- 生产环境(完整配置示例):
yaml复制apiVersion: apps/v1
kind: Deployment
metadata:
name: web-prod
spec:
replicas: 3
strategy:
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
selector:
matchLabels:
app: web
env: prod
template:
metadata:
labels:
app: web
env: prod
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- web
topologyKey: "kubernetes.io/hostname"
containers:
- name: web
image: my-web-app:v1.2.3
ports:
- containerPort: 8080
resources:
requests:
cpu: "500m"
memory: "512Mi"
limits:
cpu: "1000m"
memory: "1Gi"
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
5.3 配置管理最佳实践
使用ConfigMap和Secret管理配置:
bash复制# 创建ConfigMap
kubectl create configmap app-config \
--from-literal=DB_HOST=mysql \
--from-literal=DB_PORT=3306
# 创建Secret(base64编码)
echo -n 'supersecret' | base64
kubectl create secret generic db-credentials \
--from-literal=username=admin \
--from-literal=password='supersecret'
在Deployment中引用:
yaml复制env:
- name: DB_HOST
valueFrom:
configMapKeyRef:
name: app-config
key: DB_HOST
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-credentials
key: password
6. 集群运维与故障排查指南
6.1 日常维护命令速查
| 场景 | 命令 | 说明 |
|---|---|---|
| 查看Pod日志 | kubectl logs -f <pod-name> |
实时查看日志 |
| 进入Pod调试 | kubectl exec -it <pod-name> -- bash |
进入容器内部 |
| 查看资源使用 | kubectl top pods -A |
显示所有Pod的资源消耗 |
| 查看事件 | kubectl get events --sort-by=.metadata.creationTimestamp |
按时间排序查看集群事件 |
| 强制删除卡住资源 | kubectl delete pod <pod-name> --grace-period=0 --force |
强制删除Pod |
6.2 常见故障排查流程
- Pod一直处于Pending状态:
bash复制kubectl describe pod <pod-name> # 查看事件
kubectl get nodes # 检查节点状态
kubectl get pv,pvc # 检查存储卷
- Service无法访问:
bash复制kubectl get endpoints <service-name> # 检查Endpoint
kubectl get pods -l app=<label> # 检查后端Pod
kubectl get svc <service-name> -o yaml # 检查服务配置
- 节点NotReady:
bash复制journalctl -u kubelet -f # 查看kubelet日志
systemctl status kubelet # 检查kubelet状态
docker ps (或 crictl ps) # 检查容器运行时状态
6.3 关键指标监控告警
安装Prometheus和Grafana:
bash复制helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm install prometheus prometheus-community/kube-prometheus-stack
关键告警规则示例:
- 节点CPU使用率 > 80%持续5分钟
- Pod内存使用量 > 申请量的90%
- API Server错误率 > 5%
- 节点磁盘空间 < 15%
7. 集群安全加固实践
7.1 RBAC权限控制
创建最小权限ServiceAccount:
yaml复制apiVersion: v1
kind: ServiceAccount
metadata:
name: ci-deployer
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: deployer-role
rules:
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["get", "list", "watch", "create", "update", "patch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: deployer-binding
subjects:
- kind: ServiceAccount
name: ci-deployer
roleRef:
kind: Role
name: deployer-role
apiGroup: rbac.authorization.k8s.io
7.2 网络策略配置
使用NetworkPolicy限制Pod间通信:
yaml复制apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: db-isolation
spec:
podSelector:
matchLabels:
app: database
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: web
ports:
- protocol: TCP
port: 3306
7.3 安全上下文配置
在Pod中配置安全上下文:
yaml复制securityContext:
runAsNonRoot: true
runAsUser: 1000
fsGroup: 2000
capabilities:
drop:
- ALL
add:
- NET_BIND_SERVICE
8. 性能优化与高可用配置
8.1 控制平面高可用
使用多个控制节点实现高可用:
bash复制# 在第一个控制节点初始化时添加参数
kubeadm init --control-plane-endpoint "LOAD_BALANCER_DNS:LOAD_BALANCER_PORT" --upload-certs
# 添加额外控制节点
kubeadm join LOAD_BALANCER_DNS:LOAD_BALANCER_PORT --token ... \
--discovery-token-ca-cert-hash sha256:... \
--control-plane --certificate-key ...
8.2 节点亲和性与反亲和性
优化Pod调度策略:
yaml复制affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: node-type
operator: In
values:
- high-memory
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- web
topologyKey: kubernetes.io/hostname
8.3 资源请求与限制优化
基于实际负载调整资源配置:
bash复制# 使用Vertical Pod Autoscaler自动调整资源
kubectl apply -f https://github.com/kubernetes/autoscaler/releases/download/vertical-pod-autoscaler-0.12.0/vertical-pod-autoscaler-crd.yaml
配置示例:
yaml复制resources:
requests:
cpu: "500m"
memory: "512Mi"
limits:
cpu: "1000m"
memory: "1Gi"
