从Docker到Kubernetes:构建云原生应用交付心智模型

从Docker到Kubernetes:构建云原生应用交付心智模型
30款热门AI模型一站整合DeepSeek/GLM/Claude 随心用限时 5 折。 点击领海量免费额度上周一个刚转行做运维的朋友深夜发来消息说公司让他接手一个老项目里面一堆docker-compose.yml和k8s的yaml文件他看得一头雾水问我有没有“速成”的办法。我给他发了几篇文档他看完更困惑了每个命令都认识但连起来就不知道为什么要这么做更别提出问题怎么查了。这让我想起很多“零基础到实战”的教程它们往往把 Docker 和 Kubernetes 拆解成一个个孤立的命令和配置文件却很少讲清楚一个核心问题这套技术栈真正解决的不是“怎么启动一个容器”而是如何把一次性的、依赖环境的手工部署变成一套可重复、可观测、可自愈的标准化交付流程。如果你只学会了docker run和kubectl apply而没有理解背后从“手工运维”到“声明式运维”的思维转变那么面对复杂的生产环境你依然会寸步难行。所以这篇文章不会仅仅是命令的罗列。我想和你一起从“为什么要用容器”这个最根本的问题出发穿越 Docker 的单机隔离最终抵达 Kubernetes 的集群编排世界。我们会重点关注那些在教程里一笔带过但在实战中决定成败的细节比如容器网络互通背后的原理、持久化存储的选型逻辑、以及如何从“能跑起来”到“能稳定运行”。我们的目标不是“看完视频”而是建立一套属于自己的、从开发到部署的云原生应用交付心智模型。1. 起点为什么是容器重新理解“环境一致”这个老问题几乎所有 Docker 教程的开篇都会说“它解决了环境一致性问题”。但这句话太轻了轻到我们常常低估了“环境”二字的复杂性。在容器出现之前我们是怎么保证环境一致的虚拟机能做到硬件隔离但笨重且资源利用率低手动写install.sh脚本但无法保证宿主机库版本差异更常见的是运维人员拿着一份“标准环境清单”逐台服务器去比对和调整。这种模式下应用和它的运行环境是深度耦合的——应用的成功部署高度依赖于某位资深运维的“肌肉记忆”和“祖传文档”。Docker 带来的核心转变是将应用及其完整的运行时依赖包括库、环境变量、配置文件打包成一个不可变的交付物——镜像。这个镜像可以在任何安装了 Docker 引擎的宿主机上以几乎相同的方式运行起来。这里的“环境一致”指的是从内核之上的用户空间userspace开始的一致性。1.1 Docker 的基石镜像、容器与仓库让我们用更工程化的视角来看这三个核心概念镜像一个只读的模板。它不仅是你的应用代码更是一个包含了运行所需一切的文件系统快照。你可以把它理解为一个“应用程序的装机盘”。它的分层存储机制是关键每一层代表一次修改如安装一个软件包这些层是共享和复用的。这带来了两个直接影响1) 拉取和传输镜像更快只需拉取你本地没有的层2) 构建镜像时合理的分层能显著提升构建和部署效率。容器镜像的一个运行实例。容器在镜像的最上层添加了一个可写的“容器层”所有运行时产生的数据变化都发生在这里。当容器被删除这个可写层也随之消失。这就引出了容器化应用的第一个重要设计原则容器应该是无状态的Stateless。业务产生的需要持久化的数据必须通过挂载卷Volume或绑定挂载Bind Mount的方式存放到容器外部。仓库存放镜像的地方。Docker Hub 是公共仓库而企业内通常会搭建私有仓库如 Harbor。仓库管理不仅仅是存储还涉及镜像的版本控制、安全扫描和访问权限。1.2 第一个实战命令从docker run到理解容器生命周期很多教程会让你直接docker run nginx。我们稍微慢一点拆解这个过程# 1. 拉取镜像如果本地没有 # Docker 会检查本地是否有 nginx:latest 镜像没有则从默认仓库拉取。 # 你可以指定版本如 nginx:1.21-alpine这是生产环境的好习惯。 docker pull nginx:latest # 2. 创建并启动容器 # 这个简单的命令背后发生了很多事 # - Docker 引擎以 nginx:latest 镜像为模板创建一个新的容器层。 # - 为容器分配一个唯一的ID和名称可自定义。 # - 设置一个隔离的网络命名空间默认桥接网络。 # - 启动镜像中定义的进程对于nginx是 nginx -g daemon off;。 docker run -d --name my-nginx -p 8080:80 nginx:latest现在访问http://localhost:8080就能看到 Nginx 欢迎页。但更重要的是你需要知道如何与这个运行中的实体互动# 查看运行中的容器 docker ps # 查看所有容器包括已停止的 docker ps -a # 查看容器日志排障第一步 docker logs my-nginx # 进入容器内部就像ssh进一台虚拟机常用于调试 docker exec -it my-nginx /bin/bash # 停止容器 docker stop my-nginx # 启动已停止的容器 docker start my-nginx # 删除容器必须先停止 docker rm my-nginx # 删除镜像必须先删除依赖它的容器 docker rmi nginx:latest关键理解docker run是一个复合命令包含了create和start。在自动化脚本中有时需要将它们分开以进行更精细的控制。容器的生命周期管理创建、启动、停止、重启、删除是后续 Kubernetes Pod 生命周期管理的基础。2. 进阶用 Dockerfile 与 Compose 固化你的部署流程会运行现成的容器只是第一步。真正的价值在于将你自己的应用容器化。这就需要Dockerfile。2.1 Dockerfile你的“应用装机说明书”Dockerfile是一个文本文件包含了一系列构建镜像的指令。一个典型的 Python Web 应用 Dockerfile 可能长这样# 第一阶段构建阶段 FROM python:3.9-slim as builder WORKDIR /app COPY requirements.txt . # 使用国内源加速这是实战中的小技巧 RUN pip install --no-cache-dir -i https://pypi.tuna.tsinghua.edu.cn/simple -r requirements.txt # 第二阶段运行阶段 FROM python:3.9-slim WORKDIR /app # 从构建阶段只复制安装好的依赖减小最终镜像体积 COPY --frombuilder /usr/local/lib/python3.9/site-packages /usr/local/lib/python3.9/site-packages COPY . . # 声明容器运行时监听的端口 EXPOSE 8000 # 定义容器启动时执行的命令 CMD [gunicorn, -w, 4, -b, 0.0.0.0:8000, app:app]构建并运行它docker build -t my-python-app . docker run -d -p 8000:8000 my-python-app这个简单的文件体现了多个最佳实践使用特定版本的基础镜像(python:3.9-slim)而非latest保证确定性。多阶段构建将编译/安装依赖的“构建环境”和最终运行的“运行时环境”分离能极大减小镜像体积提升安全性因为构建工具不会留在运行时镜像中。合理使用.dockerignore文件避免将node_modules、__pycache__、.git等不必要的文件复制进镜像加速构建。一个容器只运行一个主进程这是微服务架构和容器编排的基本要求。2.2 Docker Compose定义和运行多容器应用的利器单容器应用很少见。一个典型的 Web 应用至少需要应用容器 数据库容器。手动用docker run管理它们之间的网络、依赖关系非常繁琐。Docker Compose 通过一个docker-compose.yml文件来解决这个问题。version: 3.8 services: web: build: . ports: - 8000:8000 depends_on: - db environment: - DATABASE_URLpostgresql://user:passworddb:5432/mydb volumes: - ./app/logs:/app/logs # 挂载日志目录 db: image: postgres:13 environment: POSTGRES_USER: user POSTGRES_PASSWORD: password POSTGRES_DB: mydb volumes: - postgres_data:/var/lib/postgresql/data # 使用命名卷持久化数据库数据 volumes: postgres_data: # 声明一个命名卷一键启动整个应用栈docker-compose up -dCompose 的价值在于它**将本地开发环境“代码化”**了。新成员克隆代码后一个docker-compose up就能获得一个完整、一致的应用运行环境无需在本地安装和配置 PostgreSQL、Redis 等中间件。它也是通往 Kubernetes 的很好过渡因为docker-compose.yml的语法和思维与 K8s 的 YAML 有相似之处。3. 飞跃从单机 Docker 到集群 Kubernetes 的核心思维转变当你能够在单机上熟练使用 Docker 和 Compose 后自然会遇到它的天花板如何管理成百上千个容器容器挂了如何自动重启如何实现滚动更新而不中断服务如何根据 CPU/内存使用情况自动扩容缩容如何统一管理配置和密钥这就是 Kubernetes 出场的时候。但请先忘掉那些复杂的 API 对象理解一个根本性的思维转变从“命令式”到“声明式”。命令式你告诉系统具体的执行步骤。“启动 3 个 Nginx 容器把它们放在这 3 台机器上把端口映射出来。”声明式你告诉系统你期望的最终状态。“我需要一个名为my-web的服务它始终有 3 个副本在运行使用nginx:1.21镜像暴露 80 端口。” Kubernetes 的控制器会持续观察当前状态并驱动集群向你所声明的目标状态无限逼近。3.1 Kubernetes 核心对象模型Pod, Deployment, Service这是初学者必须跨越的三个核心概念。PodKubernetes 中最小的可部署和管理单元。一个 Pod 可以包含一个或多个紧密关联的容器它们共享网络命名空间、IPC、UTS 以及有时可以共享存储卷。你可以把 Pod 想象成一个“逻辑主机”里面的容器就像这个主机上运行的多个进程。但在 90% 的情况下我们遵循“一个 Pod 一个容器”的最佳实践。# pod.yaml apiVersion: v1 kind: Pod metadata: name: my-nginx-pod spec: containers: - name: nginx image: nginx:1.21 ports: - containerPort: 80但你不会直接创建 Pod因为 Pod 本身是脆弱的节点故障Pod 就没了。我们使用Deployment。Deployment定义 Pod 的期望状态。它是管理无状态应用的核心对象。你声明“我需要 3 个这样的 Pod”Deployment 控制器就会确保任何时候都有 3 个健康的 Pod 在运行。它还负责滚动更新和回滚。# deployment.yaml apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment spec: replicas: 3 # 期望的副本数 selector: matchLabels: app: nginx template: # Pod 模板 metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:1.21 ports: - containerPort: 80应用它kubectl apply -f deployment.yaml。Kubernetes 会创建 3 个 Pod。ServicePod 是动态的可以被销毁、重建、调度到不同节点。Service 提供了一个稳定的网络端点IP 地址和 DNS 名称来访问一组具有相同标签的 Pod。它是服务的抽象负责负载均衡。# service.yaml apiVersion: v1 kind: Service metadata: name: nginx-service spec: selector: app: nginx # 选择所有标签为 app: nginx 的 Pod ports: - protocol: TCP port: 80 # Service 对外暴露的端口 targetPort: 80 # Pod 内容器监听的端口 type: ClusterIP # 默认类型仅在集群内部可访问应用后在集群内部其他 Pod 就可以通过nginx-service这个 DNS 名称来访问这组 Nginx Pod。一个最简单的应用部署流程就是编写 Deployment 和 Service 的 YAML 文件然后kubectl apply。3.2 实战入门在本地搭建 Kubernetes 实验环境在生产环境Kubernetes 集群由多台物理机或虚拟机组成。但对于学习和开发我们可以在本地快速启动一个单节点集群。推荐以下工具Minikube最经典的本地 K8s 工具会在本地虚拟机中启动一个单节点集群。Docker Desktop内置了 Kubernetes 功能一键启用最为方便。Kind使用 Docker 容器作为“节点”来运行 K8s 集群轻量快速。以Docker Desktop为例打开 Docker Desktop 设置进入 “Kubernetes” 选项卡。勾选 “Enable Kubernetes”点击 “Apply Restart”。等待几分钟集群就绪。在终端验证kubectl cluster-info和kubectl get nodes。现在你可以将上一节的deployment.yaml和service.yaml应用到你的本地集群中并通过端口转发来访问服务kubectl apply -f deployment.yaml kubectl apply -f service.yaml # 查看 Pod 状态 kubectl get pods # 将集群内的 Service 端口映射到本地 kubectl port-forward service/nginx-service 8080:80访问http://localhost:8080你就通过本地 Kubernetes 集群访问到了 Nginx。4. 深入生产就绪必须考虑的三大支柱——网络、存储与配置让应用在 K8s 里“跑起来”只是第一步。要让它“跑得稳”、“跑得好”必须理解并处理好网络、存储和配置管理。4.1 网络Pod 之间如何通信Kubernetes 网络模型要求每个 Pod 都拥有一个唯一的 IP 地址Pod IP。所有 Pod 可以在不经过 NAT 的情况下直接与其他所有 Pod 通信。所有节点可以在不经过 NAT 的情况下与所有 Pod 通信。这是通过CNI实现的。当你部署 Minikube 或 Kind 时它们已经集成了 CNI 插件如 Flannel、Calico。作为应用开发者你主要和Service打交道。Service 有几种类型ClusterIP默认仅在集群内部访问。NodePort在每个节点上开放一个静态端口将流量转发到 Service。适合本地测试或简单暴露。LoadBalancer在云平台上会自动创建一个外部负载均衡器将流量导入 Service。这是暴露服务到公网的常用方式。Ingress它不是一种 Service 类型而是一个 API 对象用于管理外部 HTTP/HTTPS 访问。Ingress 需要配合 Ingress Controller如 Nginx Ingress Controller使用可以提供基于域名和路径的路由、SSL 终止等功能是生产环境暴露 Web 服务的标准方式。4.2 存储容器重启后数据如何不丢失重申容器的重要原则容器本身是无状态的。Pod 的生命周期也可能很短暂。因此任何需要持久化的数据数据库文件、上传的图片、日志都必须存储在容器之外。Kubernetes 通过Volume和PersistentVolume (PV) / PersistentVolumeClaim (PVC)抽象来管理存储。Volume与 Pod 生命周期绑定。Pod 消失Volume 也可能消失取决于类型。适合临时数据共享。PV PVC解耦存储细节与应用需求。PersistentVolume集群中的一块存储资源由管理员预先配置如 NFS 服务器、云硬盘。PersistentVolumeClaim用户对存储的“申请单”。Pod 通过 PVC 来使用 PV。示例为之前的 Nginx Deployment 添加一个持久化存储用于存放日志。# pvc.yaml apiVersion: v1 kind: PersistentVolumeClaim metadata: name: nginx-logs-pvc spec: accessModes: - ReadWriteOnce # 访问模式单节点读写 resources: requests: storage: 1Gi # 申请1G空间# deployment-with-pvc.yaml (部分) apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment spec: ... template: ... spec: containers: - name: nginx image: nginx:1.21 volumeMounts: - name: log-volume mountPath: /var/log/nginx # 将卷挂载到容器内的日志目录 volumes: - name: log-volume persistentVolumeClaim: claimName: nginx-logs-pvc # 使用上面创建的PVC这样即使 Pod 被重建新的 Pod 挂载同一个 PVC就能访问到之前的日志文件。4.3 配置与密钥如何管理环境变量和敏感信息将配置硬编码在镜像或 YAML 文件中是糟糕的做法。Kubernetes 提供了ConfigMap和Secret。ConfigMap用于存储非敏感的配置数据如配置文件、环境变量。# configmap.yaml apiVersion: v1 kind: ConfigMap metadata: name: app-config data: app.properties: | server.port8080 logging.levelINFO database.url: jdbc:mysql://db-host:3306/mydbSecret用于存储敏感信息如密码、令牌、密钥。数据默认以 Base64 编码存储仅防君子不防小人真正的安全需要配合 RBAC 和加密等。# secret.yaml apiVersion: v1 kind: Secret metadata: name: db-secret type: Opaque data: username: YWRtaW4 # admin password: cGFzc3dvcmQ # password在 Deployment 中引用它们env: - name: DB_USERNAME valueFrom: secretKeyRef: name: db-secret key: username - name: LOG_LEVEL valueFrom: configMapKeyRef: name: app-config key: logging.level volumeMounts: - name: config-volume mountPath: /etc/app/config volumes: - name: config-volume configMap: name: app-config5. 走向实战从部署到观测与维护当你掌握了以上核心概念就可以开始规划一个真实应用的部署了。这里提供一个从开发到部署的简化流程框架5.1 应用容器化与部署清单设计编写 Dockerfile遵循多阶段构建、使用非 root 用户等最佳实践。设计 Kubernetes 清单这是核心设计环节。Deployment定义副本数、资源请求与限制、健康检查、滚动更新策略。Service定义内部访问方式。Ingress定义外部访问路由和 TLS。ConfigMap/Secret定义配置和密钥。PersistentVolumeClaim定义存储需求。使用 Kustomize 或 Helm 进行管理当环境开发、测试、生产增多配置差异化时需要工具来管理这些 YAML 文件的覆盖和组合。Helm 是事实上的包管理标准。5.2 不可或缺的“观测性”三板斧应用上线后你必须知道它是否健康。Kubernetes 提供了基础能力但需要你主动定义。存活探针告诉 K8s 容器是否还在“活着”。如果失败K8s 会重启容器。livenessProbe: httpGet: path: /healthz port: 8080 initialDelaySeconds: 30 # 容器启动后30秒开始探测 periodSeconds: 10 # 每10秒探测一次就绪探针告诉 K8s 容器是否已准备好接收流量。如果失败Service 会将该 Pod 从负载均衡端点中移除。readinessProbe: httpGet: path: /ready port: 8080 initialDelaySeconds: 5 periodSeconds: 5日志与监控日志确保应用日志输出到标准输出和标准错误这样kubectl logs和日志收集系统才能抓取。不要将日志只写入容器内的文件。监控部署 Prometheus 来收集集群和应用的指标使用 Grafana 进行可视化。这是洞察系统性能、发现问题瓶颈的眼睛。5.3 日常运维与排障命令速查最后附上一组最常用、也最能体现你理解深度的kubectl命令它们构成了你与集群交互的主要界面# 查看资源状态 kubectl get pods,svc,deploy,ing -n namespace # 获取指定命名空间的各种资源 kubectl describe pod pod-name # 查看Pod的详细事件和状态排障第一选择 kubectl get pods -w # 实时观察Pod状态变化 # 与Pod交互 kubectl logs -f pod-name [-c container-name] # 查看并跟随日志输出 kubectl exec -it pod-name -- /bin/sh # 进入Pod内的容器 # 应用与更新 kubectl apply -f file.yaml # 声明式应用配置 kubectl rollout status deployment/deploy-name # 查看部署状态 kubectl rollout undo deployment/deploy-name # 回滚到上一版本 # 诊断与调试 kubectl top pod/node # 查看资源使用情况 kubectl port-forward resource-name [local-port]:pod-port # 端口转发用于本地访问集群内服务学习 Docker 和 Kubernetes 的过程很像学习一门新的编程语言。一开始你会被语法各种命令和 YAML 字段困扰但真正的突破发生在你理解了它的“编程范式”——声明式、控制器模式、最终一致性。当你不再机械地复制 YAML而是能根据应用的需求在脑海中勾勒出 Deployment、Service、Ingress、ConfigMap 如何协同工作时你就真正从“入门”走向了“实战”。这条路没有捷径但每一步的深入都会让你对现代软件交付的掌控力提升一个维度。 30款热门AI模型一站整合DeepSeek/GLM/Claude 随心用限时 5 折。 点击领海量免费额度