说到云服务器部署,很多中小企业的IT负责人或者技术栈还停留在“买台机器、装个Nginx、搞个数据库”的老路上。但现在的玩法早就变了。上周有个朋友刚把公司核心业务从传统的独享服务器迁到AWS,结果第一天就遇到了一个让人头秃的问题:Kubernetes集群里的Pod莫名其妙起不来,而AWS EC2实例的监控图表却显示一切正常。这种“看着绿灯行,走着却熄火了”的尴尬,正是今天我们要聊的重点。
其实,对于资源有限、人手紧张的中小企业来说,想同时拥有“低成本”和“高可用”这两个看似矛盾的属性,并不是不可能,但前提是得把坑都避开。今天我们就不讲那些晦涩的理论,而是从一个真实的项目视角出发,聊聊在Ubuntu系统上,如何一步步搭建一个稳健的云端架构,以及当故障发生时,怎么像老中医一样“望闻问切”,快速定位问题。
第一阶段:地基要稳——EC2实例的配置艺术与成本控制
很多人一上来就急着搭Kubernetes,却忽略了最底层的EC2实例才是整个系统的基石。地基不稳,上面的集群就像建在沙堆上的城堡,风一吹就散。
1.1 选对实例类型,是省钱的第一步
在AWS上,EC2实例种类繁多。对于跑Kubernetes节点的应用来说,m5、m6g 或者 c5 系列是比较常见的选择。这里有个误区:很多人觉得“越贵越好”,或者“越便宜越好”。
其实,m6g(Graviton2,ARM架构)往往是性价比之王。如果你的应用容器镜像支持ARM64,或者你用的基础镜像(如Ubuntu Server ARM版)兼容,那么 m6g 通常比同规格的 m5 便宜20%左右,性能却不相上下。但这里有个坑:如果你的某些老依赖库(比如某些闭源的Java库或特定的C++编译产物)只支持x86,那就别强行上ARM,否则后期兼容性排查能把你逼疯。
建议:
- 控制面(Control Plane):至少用2个节点,最好分布在不同的可用区(Availability Zone, AZ)。哪怕是小集群,也别为了省几十块钱搞单点,一旦控制面节点挂了,整个集群就瘫痪了。
- 工作节点(Worker Nodes):初期可以用Spot实例来降低成本,但要注意,Spot实例有被回收的风险。所以,关键业务负载一定要用On-Demand实例,或者混合使用。
1.2 Ubuntu系统的初始化配置,细节决定成败
拿到一台新的EC2实例,登录进去后,千万别急着装软件。先做这些“前戏”:
更新系统并清理无用包:
sudo apt update && sudo apt upgrade -y
sudo apt autoremove && sudo apt autoclean
这一步虽然简单,但能确保你从一个干净、安全的起点开始。很多奇怪的内核问题,往往是因为旧的内核模块和新安装的软件冲突导致的。
调整内核参数(sysctl):
Kubernetes对网络栈有一定要求。你需要修改 /etc/sysctl.conf,添加或确认以下内容:
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
然后执行 sudo sysctl --system 使其生效。如果不做这一步,你可能会发现Pod之间的网络通信时有时无,甚至完全不通,而报错信息却非常晦涩。
禁用交换分区(Swap): Kubernetes官方建议禁用Swap。因为Swap会导致内存管理变得不可预测,影响Pod的资源隔离和调度。
sudo swapoff -a
# 同时注释掉 /etc/fstab 中的swap行,防止重启后重新开启
sudo sed -i '/ swap / s/^\(.*\)$/#\1/g' /etc/fstab
如果你实在舍不得那部分内存,必须开启Swap,那你需要在kubelet的配置文件中显式设置 --fail-swap-on=false,但这通常不推荐在生产环境这样做。
关闭防火墙(或使用UFW谨慎配置): 很多新手喜欢开启UFW(Uncomplicated Firewall)。但在Kubernetes集群中,Pod和Node之间的通信非常频繁,端口也多。如果不熟悉K8s的网络模型,开启UFW很容易导致节点间通信被阻断。 建议: 在云环境中,优先使用AWS的安全组(Security Groups)来控制流量,这在云层面比主机内的防火墙更高效、更易管理。如果必须用UFW,请确保允许所有节点间通信的端口(如10250, 30000-32767等)。
第二阶段:骨架要硬——构建高可用的Kubernetes集群
地基打好了,接下来就是搭建Kubernetes集群。对于中小企业,我推荐两个主流方案:一种是完全自建的( kubeadm ),另一种是使用云厂商托管服务(如AWS EKS)。这里我们主要讲自建,因为成本更低,且更能让你理解底层原理。
2.1 使用kubeadm快速搭建集群
假设你已经准备好了3台Ubuntu EC2实例:1台作为Master(控制面),2台作为Worker。
第一步:安装容器运行时 Kubernetes 1.24+ 已经移除了对Docker的直接支持,推荐使用 containerd。
sudo apt install -y containerd
sudo mkdir -p /etc/containerd
sudo containerd config default | sudo tee /etc/containerd/config.toml
# 修改cgroup驱动,通常与kubelet保持一致,推荐使用systemd
sudo sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml
sudo systemctl restart containerd
第二步:安装kubelet、kubeadm和kubectl
sudo apt install -y apt-transport-https ca-certificates curl
sudo curl -fsSLo /usr/share/keyrings/kubernetes-archive-keyring.gpg https://packages.cloud.google.com/apt/doc/apt-key.gpg
echo "deb [signed-by=/usr/share/keyrings/kubernetes-archive-keyring.gpg] https://apt.kubernetes.io/ kubernetes-xenial main" | sudo tee /etc/apt/sources.list.d/kubernetes.list
sudo apt update
sudo apt install -y kubelet kubeadm kubectl
sudo apt-mark hold kubelet kubeadm kubectl # 防止自动更新导致版本不一致
第三步:初始化控制面 在Master节点上执行:
sudo kubeadm init --pod-network-cidr=10.244.0.0/16 --apiserver-advertise-address=<MASTER_IP>
注意 --pod-network-cidr 要和后面选择的CNI网络插件匹配(这里以Calico为例,它默认使用这个网段)。初始化成功后,你会看到类似这样的提示:
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
按照提示复制配置文件,这样你就可以用 kubectl 了。
第四步:部署网络插件(CNI) Kubernetes裸奔是没法通信的,必须装网络插件。Calico是性能不错且支持策略管理的选择。
kubectl apply -f https://docs.projectcalico.org/manifests/calico.yaml
等待一段时间,确保所有Pod都处于 Running 状态。
第五步:将Worker节点加入集群
在Master节点上执行 kubeadm token create --print-join-command 获取加入命令,然后在每个Worker节点上执行该命令。
2.2 高可用(HA)的关键:多控制面节点
对于中小企业,如果觉得3台机器太贵,至少也要保证控制面的高可用。最简单的HA方案是:
- 准备3台Master节点。
- 使用负载均衡器(如AWS ALB或Nginx)将这些Master节点的API Server端口(6443)代理出去。
- 在每个Master节点上执行
kubeadm init(第一台)和kubeadm join(后续台)。 - 确保etcd数据库在3个节点间同步。
这样,即使一台Master节点挂了,集群依然可用。
第三阶段:血肉丰满——部署应用与 ingress 配置
集群搭好了,接下来就是跑业务了。这里有一个常见的坑:很多开发者习惯用 NodePort 暴露服务,或者直接在Pod里暴露端口。这在开发环境没问题,但在生产环境,你必须使用 Ingress。
3.1 使用Nginx Ingress Controller
Ingress相当于集群的“大门”,它可以根据域名和路径将流量路由到不同的服务。
# ingress-example.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-app-ingress
annotations:
kubernetes.io/ingress.class: "nginx"
spec:
rules:
- host: www.yourdomain.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: my-app-service
port:
number: 80
配合AWS的Route 53,将 www.yourdomain.com 指向Nginx Ingress Controller的LoadBalancer IP,你的应用就真正对外暴露了。
3.2 自动化部署与CI/CD
手工 kubectl apply 是低效且容易出错的。建议搭建一个简易的CI/CD流水线。比如用GitHub Actions:
# .github/workflows/deploy.yml
name: Deploy to K8s
on:
push:
branches: [ main ]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Deploy to K8s
run: |
# 这里配置kubectl,可能需要设置kubeconfig
kubectl apply -f k8s/
kubectl rollout status deployment/my-app
每次代码推送,自动触发部署,既能保证一致性,又能减少人为失误。
第四阶段:眼观六路——常见运维故障排查实战
无论架构设计得多么完美,故障总会发生。以下是一些高频故障及排查思路,亲测有效。
4.1 故障一:Pod一直处于 Pending 状态
现象: kubectl get pods 看到状态是 Pending。
原因: 通常是调度失败。
排查步骤:
kubectl describe pod <pod-name>:查看Events部分。- 常见原因:
- 资源不足:集群没有足够的CPU或内存。解决方案:扩容节点,或优化Pod的资源请求(requests)。
- 节点选择器不匹配:Pod指定了
nodeSelector,但没有符合条件的节点。 - Taints(污点):节点被打上了污点,而Pod没有对应的Toleration。
- PersistentVolume (PV) 未绑定:如果你申请了PVC,但集群中没有满足条件的PV,Pod就会Pending。
4.2 故障二:Pod CrashLoopBackOff
现象: Pod反复重启,状态在 Running 和 Error 之间切换。 原因: 应用启动失败、配置错误、依赖服务不可用等。 排查步骤:
kubectl logs <pod-name>:查看应用日志,这是最直接的线索。kubectl logs <pod-name> -p:查看上一次重启前的日志,有时候问题出在启动瞬间。- 检查环境变量和Secret是否正确挂载。
- 如果是Java应用,检查JVM内存参数是否超过了容器限制。
4.3 故障三:服务无法从集群外访问
现象: 浏览器打开域名,显示连接超时或502错误。 原因: Ingress配置错误、DNS解析问题、安全组拦截等。 排查步骤:
- 检查Ingress资源状态:
kubectl get ingress,确保Address栏有IP。 - 检查Nginx Ingress Controller的Pod是否正常运行:
kubectl get pods -n ingress-nginx。 - 查看Ingress Controller的日志:
kubectl logs -n ingress-nginx <controller-pod>。 - 检查AWS安全组:确保LoadBalancer所在的安全组放行了80和443端口的入站流量。
- 检查DNS:使用
nslookup www.yourdomain.com确认解析到了正确的IP。
4.4 故障四:网络不通,Pod间无法通信
现象: 前端Pod能访问数据库Pod吗? 排查步骤:
- 确认CNI插件正常运行:
kubectl get pods -n kube-system,确保Calico等组件都是Running。 - 测试网络连通性:在一个Pod里执行
curl <database-service-ip>:<port>。 - 检查NetworkPolicy:如果你配置了NetworkPolicy,可能会阻断某些流量。暂时删除所有NetworkPolicy进行测试。
- 检查iptables规则:在节点上执行
sudo iptables -L -n -v,看看是否有规则拒绝了流量。
第五阶段:未雨绸缪——备份与监控
最后,也是中小企业最容易忽视的两点:备份和监控。
5.1 备份!备份!备份!
Kubernetes的etcd存储了集群的所有状态。如果etcd数据丢失,整个集群就没了。
- 工具推荐: Velero。它可以帮你备份集群资源和持久卷(PV)到S3。
- 定期演练: 备份不是为了存着,而是为了在灾难时能恢复。定期测试恢复流程。
5.2 监控与告警
没有监控的集群就是黑盒。
- Prometheus + Grafana: 经典的监控组合。可以监控集群资源使用情况、应用性能指标等。
- Alertmanager: 设置告警规则,比如“CPU使用率超过80%持续5分钟”、“Pod重启次数过多”等,通过邮件或钉钉/企微通知你。
- 云监控: AWS CloudWatch也能提供EC2和VPC层面的监控,别忘了结合使用。
结语:低成本不等于低质量
回到开头的话题,中小企业的IT资源有限,但这并不意味着我们要妥协于低质量的服务。通过合理的架构设计(如多AZ、HA控制面)、精细的资源管理(如Spot实例、ARM架构)、规范的运维流程(如CI/CD、备份恢复)以及主动的故障排查,我们完全可以在AWS云上搭建出一个既省钱又稳定的生产环境。
记住,每一个坑都是成长的机会。当你第一次独立解决了一个复杂的K8s网络问题时,那种成就感,比任何加薪都来得真实。希望这份指南能帮你在云原生道路上少走弯路,早日实现“低成本高可用”的目标。如果在实践中遇到具体问题,欢迎随时交流,毕竟,独乐乐不如众乐乐嘛。