
从Docker到Kubernetes我的容器云部署与应用实战之旅前言为什么我们需要容器云在云计算和微服务架构大行其道的今天“容器”早已不是一个陌生的词汇。它以其轻量级、可移植、高效能的特点彻底改变了我们开发、交付和运行应用程序的方式。然而当我们的应用从单体架构拆分成数十甚至上百个微服务时一个新的挑战摆在了面前如何高效地管理这些数量庞大的容器如何确保它们的高可用、弹性伸缩和自动化运维答案就是——容器云Container Cloud。容器云并非简单的“容器云”它是一个以容器为核心集成了编排、调度、网络、存储、监控和安全等一系列能力的综合性平台。它以KubernetesK8s为事实标准为我们提供了一个强大的操作系统来管理和调度整个数据中心的资源。本篇博文将基于我近期的学习和实践系统地总结容器云的部署与应用。我将从核心概念入手一步步带你搭建一个属于自己的Kubernetes集群并部署一个完整的微服务应用最后分享一些我在实践中遇到的问题和思考。希望能为正在学习或准备踏入容器云领域的你提供一份有价值的参考。第一部分基石——深入理解容器与Kubernetes核心概念在动手之前我们必须夯实理论基础。知其然更要知其所以然。1. 容器技术的本质容器的本质是一个被隔离的进程。它利用Linux内核提供的两大核心技术实现Namespaces命名空间提供了进程级别的隔离。例如PID Namespace让容器拥有独立的进程ID空间Network Namespace让容器拥有独立的网络栈Mount Namespace让容器拥有独立的文件系统视图。这就像给每个进程盖了一个独立的“单间”。Cgroups控制组负责资源的限制和审计。它可以精确地限制一个进程组即容器可以使用的CPU、内存、磁盘I/O等资源上限防止某个容器耗尽宿主机资源影响其他容器。这就像是给每个“单间”规定了水电用量上限。Docker等容器引擎正是在这两项技术之上封装了镜像Image、仓库Registry等更易用的概念极大地降低了容器的使用门槛。2. Kubernetes容器编排的王者如果说Docker是制造集装箱的工厂那么Kubernetes就是那个调度万千货轮的超级港口。它的核心设计哲学是声明式API和控制器模式。你只需要告诉K8s“我想要什么状态”例如我需要3个Nginx副本K8s内部的控制器就会持续工作确保“实际状态”无限趋近于“期望状态”。以下是K8s中最核心的几个概念必须烂熟于心PodK8s中最小的调度和管理单元。一个Pod可以包含一个或多个紧密耦合的容器通常是主业务容器辅助Sidecar容器它们共享网络和存储卷。Pod是短暂的随时可能被销毁和重建。Deployment用于管理无状态应用的控制器。它通过管理ReplicaSet来保证指定数量的Pod副本始终在运行并支持滚动更新和回滚。这是我们部署Web应用最常用的方式。Service为一组功能相同的Pod提供一个稳定的访问入口ClusterIP和负载均衡。由于Pod的IP是动态变化的Service通过Label Selector找到后端Pod解决了服务发现的问题。Ingress集群外部流量进入集群内部的网关。它定义了HTTP/HTTPS路由规则可以将不同域名的请求转发到集群内不同的Service上通常需要一个Ingress Controller如Nginx Ingress Controller来实现。ConfigMap Secret用于将配置信息与容器镜像解耦。ConfigMap存储非敏感的配置数据如配置文件、环境变量而Secret则专门用于存储密码、Token、密钥等敏感信息。PersistentVolume (PV) PersistentVolumeClaim (PVC)K8s的持久化存储抽象层。管理员预先创建好PV代表实际的存储资源如NFS、云盘开发者通过PVC来申请所需大小和访问模式的存储。这种解耦使得应用无需关心底层存储的具体实现。第二部分实战——从零搭建高可用Kubernetes集群理论准备就绪现在让我们开始动手。本次实战的目标是搭建一个包含1个Master节点和2个Worker节点的K8s集群。1. 环境准备角色IP地址操作系统配置k8s-master192.168.1.10Ubuntu 22.04 LTS2C4Gk8s-node1192.168.1.11Ubuntu 22.04 LTS2C4Gk8s-node2192.168.1.12Ubuntu 22.04 LTS2C4G所有节点都需要执行的初始化操作关闭防火墙和Swapsudo ufw disable sudo swapoff -a # 永久关闭swap注释掉/etc/fstab中的swap行 sudo sed -i / swap / s/^\(.*\)$/#\1/g /etc/fstab设置主机名和Hosts解析# 在master节点执行 sudo hostnamectl set-hostname k8s-master # 在node1节点执行 sudo hostnamectl set-hostname k8s-node1 # ...以此类推 # 在所有节点的/etc/hosts文件中添加 192.168.1.10 k8s-master 192.168.1.11 k8s-node1 192.168.1.12 k8s-node2加载必要的内核模块并设置内核参数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安装容器运行时Containerdsudo apt-get update sudo apt-get install -y containerd # 生成默认配置并修改SystemdCgroup为true sudo mkdir -p /etc/containerd containerd config default | sudo tee /etc/containerd/config.toml sudo sed -i s/SystemdCgroup false/SystemdCgroup true/ /etc/containerd/config.toml sudo systemctl restart containerd安装Kubeadm, Kubelet, Kubectl# 添加阿里云的K8s apt源 curl -fsSL https://mirrors.aliyun.com/kubernetes/apt/doc/apt-key.gpg | sudo gpg --dearmor -o /usr/share/keyrings/kubernetes-archive-keyring.gpg echo deb [signed-by/usr/share/keyrings/kubernetes-archive-keyring.gpg] https://mirrors.aliyun.com/kubernetes/apt/ kubernetes-xenial main | 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 # 防止自动升级2. 初始化Master节点在k8s-master节点上执行sudo kubeadm init \ --apiserver-advertise-address192.168.1.10 \ --image-repository registry.aliyuncs.com/google_containers \ --pod-network-cidr10.244.0.0/16 # Flannel网络插件的默认网段提示--image-repository参数指定了国内镜像源避免因无法访问Google镜像仓库而失败。--pod-network-cidr必须与你后续要安装的CNI网络插件的网段一致。初始化成功后会输出一段kubeadm join命令请务必保存好这段命令它是Worker节点加入集群的凭证。然后按照提示配置kubectlmkdir -p $HOME/.kube sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config sudo chown $(id -u):$(id -g) $HOME/.kube/config3. 安装CNI网络插件这里我们选择简单易用的Flannel。kubectl apply -f https://raw.githubusercontent.com/flannel-io/flannel/master/Documentation/kube-flannel.yml等待片刻直到kube-flannel-ds相关的Pod都处于Running状态。4. 加入Worker节点在k8s-node1和k8s-node2上分别执行之前保存的kubeadm join命令。5. 验证集群状态回到Master节点执行kubectl get nodes如果看到所有节点的状态都是Ready恭喜你一个基础的K8s集群已经搭建成功NAME STATUS ROLES AGE VERSION k8s-master Ready control-plane 10m v1.27.x k8s-node1 Ready none 5m v1.27.x k8s-node2 Ready none 5m v1.27.x第三部分应用——部署一个完整的微服务示例为了演示K8s的应用能力我们将部署一个经典的微服务组合一个前端Vue应用、一个后端Spring Boot API和一个Redis缓存。1. 准备YAML文件我们将所有资源配置写在一个deployment.yaml文件中便于管理。# --- Redis Deployment Service --- apiVersion: apps/v1 kind: Deployment metadata: name: redis spec: replicas: 1 selector: matchLabels: app: redis template: metadata: labels: app: redis spec: containers: - name: redis image: redis:7-alpine ports: - containerPort: 6379 --- apiVersion: v1 kind: Service metadata: name: redis-service spec: selector: app: redis ports: - port: 6379 targetPort: 6379 # --- Backend API Deployment Service --- apiVersion: apps/v1 kind: Deployment metadata: name: backend-api spec: replicas: 2 selector: matchLabels: app: backend-api template: metadata: labels: app: backend-api spec: containers: - name: api image: your-registry/backend-api:v1.0 # 替换为你的镜像地址 env: - name: REDIS_HOST value: redis-service # 使用Service名称作为主机名 ports: - containerPort: 8080 --- apiVersion: v1 kind: Service metadata: name: backend-service spec: selector: app: backend-api ports: - port: 80 targetPort: 8080 # --- Frontend Deployment Service --- apiVersion: apps/v1 kind: Deployment metadata: name: frontend spec: replicas: 2 selector: matchLabels: app: frontend template: metadata: labels: app: frontend spec: containers: - name: web image: your-registry/frontend:v1.0 # 替换为你的镜像地址 ports: - containerPort: 80 --- apiVersion: v1 kind: Service metadata: name: frontend-service spec: selector: app: frontend ports: - port: 80 targetPort: 80 # --- Ingress --- apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: my-app-ingress annotations: nginx.ingress.kubernetes.io/rewrite-target: / spec: ingressClassName: nginx # 假设已安装Nginx Ingress Controller rules: - host: myapp.local # 在你的电脑上修改hosts文件指向Ingress Controller的IP http: paths: - path: /api pathType: Prefix backend: service: name: backend-service port: number: 80 - path: / pathType: Prefix backend: service: name: frontend-service port: number: 802. 部署与验证部署应用kubectl apply -f deployment.yaml检查Pod和服务状态kubectl get pods,svc,ingress确保所有Pod都处于Running状态Service有对应的EndpointsIngress有正确的ADDRESS。本地测试在你的本地电脑上修改/etc/hostsMac/Linux或C:\Windows\System32\drivers\etc\hostsWindows添加一行你的Ingress_Controller_Node_IP myapp.local然后在浏览器中访问http://myapp.local你应该能看到前端页面并且它能正常调用后端API。第四部分进阶——生产环境下的关键考量上面的部署只是一个起点。在生产环境中我们还需要考虑更多问题。可观测性你必须知道集群里发生了什么。日志Logging使用EFKElasticsearch, Fluentd, Kibana或Loki栈来集中收集和分析容器日志。监控Monitoring使用Prometheus Grafana来监控集群节点、Pod的资源使用情况以及应用的业务指标。链路追踪Tracing对于复杂的微服务调用使用Jaeger或SkyWalking来追踪一次请求的完整路径快速定位性能瓶颈。安全性RBAC基于角色的访问控制严格遵循最小权限原则为不同的用户和服务账号分配精确的权限。网络策略NetworkPolicy定义Pod之间的网络访问规则实现微隔离防止攻击者在攻破一个Pod后横向移动。镜像安全定期扫描镜像漏洞只使用来自可信源的镜像。GitOps将集群的配置和应用部署都代码化并存放在Git仓库中。使用ArgoCD或FluxCD等工具实现配置的自动同步和版本化管理。这是目前最先进的K8s运维范式。服务网格Service Mesh当服务间通信变得极其复杂时可以考虑引入Istio或Linkerd等服务网格。它们将流量管理、安全、可观测性等能力从业务代码中剥离出来作为一个独立的基础设施层来处理。第五部分踩坑记录与心得体会在实践中我遇到了不少问题也收获了很多心得。坑1Pod一直处于Pending状态。排查kubectl describe pod pod-name查看事件。原因通常是资源不足CPU/内存或没有匹配的节点NodeSelector/Taint。解决扩容节点或调整Pod的资源请求requests。坑2Pod之间无法通信。排查检查Service的Selector是否与Pod的Label匹配检查CNI插件是否正常工作kubectl get pods -n kube-system。原因Label不匹配是最常见的原因。解决仔细核对YAML文件中的Label定义。坑3Ingress无法访问。排查检查Ingress Controller是否已安装并正常运行检查Ingress规则中的host和path是否正确检查本地hosts文件或DNS解析。原因Ingress Controller未正确配置或未暴露端口。解决确保Ingress Controller的Service类型为LoadBalancer或通过NodePort暴露并正确映射端口。心得体会YAML工程师的自我修养K8s的世界是由YAML文件构成的。熟练掌握YAML语法和各种资源的字段含义是成为一名合格K8s使用者的基本功。善用kubectl explain resource命令来查询字段说明。拥抱声明式思维不要再想着“如何创建一个Pod”而是去想“我需要一个什么样的Deployment”。这种思维的转变至关重要。文档是最好的老师K8s的官方文档非常详尽遇到问题时第一时间查阅官方文档往往比搜索引擎更有效。社区的力量K8s拥有一个庞大而活跃的社区。绝大多数问题都能在社区中找到答案。学会提问善于利用GitHub Issues和Stack Overflow。结语从Docker的单机容器到Kubernetes的集群编排我们走过的是一条追求更高效率、更强韧性和更优体验的技术演进之路。容器云不是终点而是一个新的起点。它为我们构建云原生应用提供了坚实的基础。希望通过这篇博文能让你对容器云的部署与应用有一个更全面、更深入的认识。技术之路道阻且长行则将至。让我们一起在云原生的浪潮中不断探索持续学习