ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Docker 和 Kubernetes 到底在忙什么?从一个小服务开始讲

Docker 和 Kubernetes 到底在忙什么?从一个小服务开始讲 你写了一个 HTTP 服务在自己电脑上运行得很好。把代码发给同事后他说“启动失败依赖版本不对。”你修好依赖又把服务部署到服务器。过了几天访问量上来了于是你启动了三个副本。现在新的问题来了三个副本该放在哪些机器上某台机器坏了谁来补一个副本发布新版本时怎样让服务持续可用副本的地址变了用户该访问谁Docker 主要帮我们处理“怎样把应用及其运行环境交付出去”。Kubernetes通常简称 K8s主要帮我们处理“怎样让这些应用在一组机器上持续运行”。这篇文章就沿着这个小服务的部署过程把两者串起来。一、先别急着学命令容器是什么假设你的服务依赖 Python、几个第三方库和一份配置文件。传统部署方式是先准备一台服务器再逐项安装、配置。只要服务器上的版本与开发环境有差异就可能出现“本地正常线上报错”。容器提供了另一种交付方式把运行应用需要的文件、依赖和配置方式打包成镜像再用镜像启动容器。这里有三个词需要分清名词可以怎样理解镜像 Image一份用于启动应用的打包结果容器 Container镜像运行起来后的实例镜像仓库 Registry存放、分发镜像的地方一个镜像可以启动多个容器就像同一份程序可以运行多个进程。不过容器并不只是“换了个名字的进程”它通常拥有隔离的文件系统、网络视图等环境也可以被限制使用多少 CPU 和内存。容器和虚拟机最大的区别是虚拟机通常运行自己的客户操作系统Linux 容器共享宿主机内核隔离的是进程的运行环境。因此容器一般更轻启动也更快。这里说的是 Linux 容器在 Windows 或 macOS 上使用 Docker Desktop 运行它们时底层通常还有一个 Linux 虚拟机。你暂时不用记住 namespace、cgroups 等内核术语。先记住一句话容器让应用以相对隔离、可重复的环境运行但它仍然依赖宿主机提供的内核能力。Docker 官方容器入门二、Docker 是怎样把代码变成容器的假设我们有一个监听8080端口的小服务入口文件叫app.py。我们可以写一个 Dockerfile告诉 Docker 怎样制作镜像FROM python:3.13-slim WORKDIR /app COPY . . EXPOSE 8080 CMD [python, app.py]逐行看其实没有什么神秘的FROM以哪个现成镜像为基础。WORKDIR后续操作在哪个目录执行。COPY把代码复制进镜像。EXPOSE记录应用使用的端口它本身不会把端口开放给宿主机。CMD容器启动时默认运行什么命令。接着构建并运行dockerbuild-thello-api:1.0.dockerrun--rm-p8080:8080 hello-api:1.0-p 8080:8080的左边是宿主机端口右边是容器端口。访问宿主机的8080请求才能转发到容器的8080。还要确保应用在容器里监听的是0.0.0.0而不只是127.0.0.1。至此我们完成了一条最基本的链路代码 → Dockerfile → 镜像 → 容器 → 对外提供服务如果要把镜像交给另一台机器还需要给它标记仓库地址、推送到镜像仓库然后在目标机器拉取并运行。镜像让交付更一致但不保证应用一定正确环境变量写错、数据库连不上照样会启动失败。Docker 官方镜像构建指南一个很常见的困惑容器里的localhost是谁假设应用容器需要访问数据库容器。你在应用配置里写localhost:5432通常会连不上。因为站在应用容器内部看localhost指的是应用容器自己不是数据库容器更不是宿主机。这也是容器网络要解决的问题让容器能够通过合适的网络和名称互相访问。用 Docker Compose 管理一组本地容器时应用通常可以通过数据库的服务名找到它例如db:5432。另一个常见困惑容器删除后数据去哪了容器适合被创建、停止和替换。如果把数据库数据只写在容器自身的可写层里删除容器时这些数据通常也会跟着消失。所以数据库这类需要长期保存的数据要放在容器生命周期之外例如使用 Docker volume。你可以把它理解为容器负责运行程序持久化存储负责留住数据。到这里Docker 已经能很好地帮助我们制作镜像、运行容器以及在一台机器上组织几个相关服务。接下来问题转向多台机器。三、为什么有了 Docker还需要 K8s现在老板要求我们的服务始终保持三个副本。你可以手动在几台服务器上执行docker run但随后就要持续回答这些问题三个副本各跑在哪台机器一个副本退出后谁发现并重启它整台机器失联后谁在别处补齐副本发布新镜像时怎样逐步替换旧副本副本随时可能更换流量该发往哪里K8s 把这些事情组织成一套机制。你告诉它想要的状态例如“运行三个副本使用这个镜像”它持续观察实际状态并设法缩小两者的差距。这叫声明式管理。可以这样理解两者的分工Docker把应用制成镜像并能运行容器 K8s在集群里安排、维护和连接容器化应用这里有一个面试中容易被问到的细节**K8s 可以运行 Docker 构建的镜像但节点不一定使用 Docker Engine 来运行容器。**K8s 通过容器运行时接口与兼容的运行时协作常见选择包括 containerd如果使用 Docker Engine则需要相应的适配组件。Kubernetes 官方容器运行时文档四、K8s 集群里谁负责什么先看全局。一个 K8s 集群通常由控制平面和工作节点 Node组成。工作节点是实际运行应用的机器。控制平面负责接收你的要求、记录集群状态、决定任务放到哪里并持续检查实际情况。不用一开始就背所有组件先顺着一次部署看你提交一份配置“运行三个hello-api副本。”API Server接收这个请求。集群把期望状态记录下来etcd是保存集群数据的关键存储。控制器发现“期望三个实际零个”于是创建相应的工作负载。Scheduler为尚未分配节点的 Pod 选择合适的 Node。Node 上的kubelet配合容器运行时把容器真正运行起来。如果后来一个副本消失相关控制器会继续尝试让实际数量回到三个。注意“尝试”二字如果集群资源不足、镜像拉取失败或配置有误K8s 不可能凭空让服务正常运行。它会暴露状态和事件供你排查。Kubernetes 官方集群组件文档五、Pod、Deployment、Service最重要的三个对象初学 K8s 容易被大量名词劝退。先抓住三个已经能解释一条基本部署链路。Pod应用实际运行的地方**Pod 是 K8s 部署和调度的基本单位。**最常见的 Pod 里只有一个主要业务容器一个 Pod 也可以包含多个需要紧密协作的容器。同一个 Pod 内的容器共享网络环境可以通过localhost相互访问。Pod 不适合被当成一台永久不变的小服务器。它可能被替换地址也可能变化。因此别把“某个 Pod 的 IP”写死在客户端配置里。Deployment保持副本数量管理版本更新如果你直接创建一个 Pod它消失后谁来确保业务还保持三个副本通常我们会创建Deployment。它描述使用哪个镜像、运行多少副本以及怎样更新。例如apiVersion:apps/v1kind:Deploymentmetadata:name:hello-apispec:replicas:3selector:matchLabels:app:hello-apitemplate:metadata:labels:app:hello-apispec:containers:-name:hello-apiimage:your-registry/hello-api:1.0ports:-containerPort:8080先忽略 YAML 的层级只看两处replicas: 3我们希望有三个副本。image: ...:1.0这些副本运行哪个镜像。template则是在说新建出来的 Pod 应该长什么样。发布新版时修改镜像版本Deployment 可以逐步创建新 Pod、替换旧 Pod出现问题时也可以回滚。它适合管理我们这个无状态的 HTTP 服务。Kubernetes 官方 Deployment 文档Service给变化的 Pod 一个稳定入口假设三个 Pod 的地址分别是 A、B、C。B 故障后被替换为 D。客户端不应该每次都重新认识这些地址。Service提供相对稳定的访问方式并把请求导向符合条件的后端 Pod。它通过标签选择目标apiVersion:v1kind:Servicemetadata:name:hello-apispec:selector:app:hello-apiports:-port:80targetPort:8080这里的selector要与 Pod 的标签对应。port: 80是 Service 提供的端口targetPort: 8080是应用容器监听的端口。集群内的其他应用可以通过 Service 名称访问它而不需要记住每个 Pod 的 IP。Kubernetes 官方 Service 文档把三个对象连起来就是Deployment我想保持 3 个副本 ↓ Pod三个实际运行应用的单位 ↑ Service为访问这些 Pod 提供稳定入口如果要让集群外的用户通过域名访问通常还要配置面向外部流量的入口例如 Gateway API、Ingress或者在支持的环境中使用LoadBalancer类型的 Service。Service 解决稳定访问后端的问题不意味着应用自动拥有公网域名。Kubernetes 官方网络概念六、应用跑起来了为什么还是会出故障到这里我们已经可以发布服务但“进程存在”不等于“服务可用”。服务还没准备好就开始接请求应用可能要加载配置、连接数据库启动需要十几秒。此时容器虽然运行了却还不能处理请求。**就绪探针readiness probe**回答的是“现在能接流量吗”未就绪的 Pod 不会通过对应的 Service 接收流量。**存活探针liveness probe**回答的是“这个容器是否已经卡死需要重启”两者不是一回事。把一个启动很慢的服务错误地配置为“没准备好就重启”反而可能造成反复重启。Kubernetes 官方探针文档副本数量写了三个却只跑起来一个可能是节点没有足够 CPU 或内存也可能是镜像拉取失败。K8s 中的资源请求会参与调度应用至少需要多少资源资源限制则约束它最多能使用多少。填写这些数值需要依据应用的实际运行情况而不是随意复制示例。数据库也直接按同样方法部署三个副本先停一下。我们的 HTTP 服务可以把任意请求交给任意一个副本副本之间通常可以互换这叫无状态应用。数据库有持久数据、身份和复制关系部署方式复杂得多。理解了 Deployment不等于所有服务都应该套用同一份 YAML。配置也要单独考虑普通配置可以使用 ConfigMap密码、令牌等敏感数据可以使用 Secret同时仍需正确设置访问权限。数据持久化则涉及卷和持久卷声明等机制。Kubernetes 官方配置概念七、遇到故障时怎样查只背对象名面试时很容易露馅。更有用的是知道排查顺序。假设发布后用户访问失败可以沿着请求路径问外部入口 → Service → Pod → 容器进程 → 应用依赖逐层看**Pod 根本没创建**看 Deployment 的期望数量和事件。**Pod 一直 Pending**看是否无法调度例如资源不足。**Pod 在重启**看容器日志、退出原因和探针配置。**Pod 正常Service 却访问不到**检查 Service 的标签选择器、端口和就绪状态。**容器正常但接口报错**再看应用日志、配置和数据库连接。常用命令不多先熟悉这些就够了kubectl get pods kubectl describe podpod-namekubectl logspod-namekubectl get deployment kubectl getservice关键不是背命令而是知道每条命令要验证哪个猜想。get看总体状态describe看详细信息和事件logs看应用输出。八、现在用两分钟把 Docker 和 K8s 讲给面试官听你可以这样说但最好换成自己的表达我理解 Docker 首先解决的是应用交付和运行环境一致性的问题。开发者用 Dockerfile 把代码、依赖和启动方式构建成镜像镜像可以推送到仓库再在其他机器上启动为容器。镜像是一份打包结果容器是运行中的实例。当应用需要在多台机器上运行多个副本时还需要处理调度、故障恢复、更新和服务发现。Kubernetes 就是管理这些容器化应用的平台。我们通常用 Deployment 声明镜像和副本数量它管理 Pod 的创建与更新Pod 是实际运行容器的基本单位Service 则为可能不断变化的 Pod 提供稳定的访问入口。比如我要发布三个 HTTP 服务副本会先构建并推送镜像再创建一个副本数为三的 Deployment 和一个 Service。某个 Pod 消失后控制器会尝试补齐发布新版本时可以通过 Deployment 逐步替换旧 Pod。我会用就绪探针控制何时接流量并通过 Pod 事件和日志排查启动问题。如果你能讲清这段话再回答“容器和虚拟机有什么区别”“Pod 和容器有什么区别”“为什么需要 Service”就已经具备和同事、面试官展开基础讨论的能力了。最后把整篇文章压缩成一张脑图写好应用 ↓ Dockerfile 定义怎样打包 ↓ 构建镜像推送到镜像仓库 ↓ K8s 的 Deployment 声明镜像和副本数量 ↓ Pod 在各个 Node 上运行容器 ↓ Service 把请求送到可用的 Pod ↓ 控制器持续检查实际状态是否符合期望状态Docker 让“把应用带过去并运行”变得可重复K8s 让“在多台机器上持续运行这些应用”变得可管理。学到这里不必马上钻进网络插件、调度算法或集群安装细节。先亲手完成一次“构建镜像 → 运行容器 → 部署三个 Pod → 通过 Service 访问 → 删除一个 Pod 看它恢复”这些概念就会从名词变成你亲眼见过的过程。
返回列表