- Docker 通常在一台机器上构建映像并运行容器。
- Kubernetes 跨机器集群调度和管理容器。
- 它们通常一起使用:Docker 构建镜像,Kubernetes 在生产中运行它。
- 更公平的比较是 Kubernetes 与 Docker Swarm 或 Compose,许多小型项目根本不需要 Kubernetes。
搜索 Docker vs Kubernetes 表明您必须选择一个,但该框架会导致很多混乱。这有点像将一个集装箱与容纳数千个集装箱的港口进行比较。它们在不同的级别上运行,并且经常同时使用。
本指南解释了每个应用程序的用途,在两个应用程序中显示了相同的小应用程序,并以一种实用的方法结束。此处的命令和文件是我们在本文中未执行的标准示例,因此请根据您安装的版本检查它们。
简短的回答
Docker 是一个平台,用于将应用程序及其依赖项打包到映像中,然后将该映像作为容器运行。它回答了“如何让这个应用程序在任何地方都以相同的方式运行?”的问题。
Kubernetes,通常写作 K8s,是一个容器编排器。它决定容器在多台机器上运行的位置,在发生故障时重新启动它们,并根据需求进行扩展。它回答了“如何保持数百个容器可靠运行?”的问题。
Docker 与 Kubernetes 对比
| 方面 | 码头工人 | 库伯内斯 |
|---|---|---|
| 主要工作 | 构建并运行容器 | 编排容器 |
| 适用范围 | 通常一台主机 | 由许多节点组成的集群 |
| 构建图像? | 是的 | 否 |
| 基本单位 | 集装箱 | Pod(一个或多个容器) |
| 配置 | Dockerfile, compose.yaml |
YAML 清单 |
| 失败的容器 | 该主机上的可选重启策略 | 自动替换,即使在另一个节点上也是如此 |
| 缩放 | 手动,在一台主机上使用 Compose | 声明式,具有自动缩放选项 |
| 网络 | 主机上的桥接网络 | 集群范围的服务和 DNS |
| 学习曲线 | 温柔 | 陡峭 |
| 最适合 | 开发和小型部署 | 大型生产系统 |
Docker 是做什么的
在容器出现之前,“它可以在我的机器上运行”是一个日常问题,因为每个服务器都有不同的版本。 Docker 将您的代码、运行时和库捆绑到一个 映像 中。该映像的运行副本是一个容器,它共享主机的内核,因此与完整的虚拟机相比,它启动速度更快,使用的内存更少。
工作流程分为三个步骤:
- Build:a
Dockerfile描述如何创建映像。 - Ship: 将映像推送到注册表,例如 Docker Hub。
- 运行: 在任何兼容计算机上从该映像启动容器。
要进行实践,请从我们的 Docker 初学者教程 开始,然后阅读我们的 Dockerfile 最佳实践 。
库伯内特斯是做什么的
一台服务器上的 Docker 运行良好,直到该服务器发生故障、流量超出其容量或者您有数十个服务需要协调。 Kubernetes 负责这些操作工作:
- 调度:将容器放置在有足够可用容量的机器上。
- 自我修复:替换故障容器并将工作从故障节点移开。
- 扩展:手动或基于指标添加或删除副本。
- 服务发现:为一组不断变化的容器提供一个稳定的名称。
- 滚动更新:逐步发布新版本,并进行简单的回滚。
- 配置:与图像分开管理设置和机密。
核心思想是期望状态。您声明应该存在三个副本,控制者会不断将其与现实进行比较并纠正任何差异。我们的 Kubernetes 初学者教程 在本地集群上演练了这一过程。
Docker 和 Kubernetes 如何协同工作
从开发人员的笔记本电脑到生产环境的典型路径如下所示:
- 开发人员编写
Dockerfile并在本地测试容器。 - CI/CD 管道 构建映像并运行测试。
- 管道将版本化映像推送到注册表。
- Kubernetes 清单已更新以使用该映像版本。
- Kubernetes 提取镜像并将其部署到集群中。
所以Docker的工作在Kubernetes启动之前就基本完成了。该图像是它们之间的交接点。
一个应用程序,两种方式
这是一个小型网络服务。无论哪个工具运行该映像,其构建方式都相同:
# Dockerfile
FROM python:3.13-slim
WORKDIR /app
COPY . .
RUN pip install -r requirements.txt
CMD ["python", "app.py"]
使用 Docker 运行它
docker build -t shop-api:1.0 .
docker run -d --name shop -p 8080:8000 --restart unless-stopped shop-api:1.0
两个命令,它正在运行。这很简单,适合许多小型项目。但一台机器上只有一份副本,如果机器死机,服务也会随之消失。
使用 Kubernetes 运行它
# shop.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: shop
spec:
replicas: 3
selector:
matchLabels:
app: shop
template:
metadata:
labels:
app: shop
spec:
containers:
- name: api
image: registry.example.com/shop-api:1.0
ports:
- containerPort: 8000
---
apiVersion: v1
kind: Service
metadata:
name: shop
spec:
selector:
app: shop
ports:
- port: 80
targetPort: 8000
kubectl apply -f shop.yaml
kubectl get pods -l app=shop
还有很多东西要写,并且您需要一个集群来运行它。作为回报,您将获得三份副本、稳定的内部地址和自动替换。这种权衡是整个决策的缩影:更多的复杂性换取更多的弹性。
当容器发生故障时会发生什么?
| 活动 | 一台主机上的 Docker | 库伯内斯 |
|---|---|---|
| 应用程序崩溃 | 重新启动(如果设置了重新启动策略) | 自动重启 |
| 应用程序挂起但仍保持运行 | 除非您为其添加处理,否则保持挂起状态 | 活性探针可以重新启动它 |
| 整个服务器死掉 | 服务会一直中断,直到有人采取行动为止 | Pod 在健康节点上重新创建 |
| 流量增加三倍 | 您可以在一台主机上手动扩展 | 自动缩放器可以添加副本 |
| 糟糕的发布 | 您手动重新部署旧映像 | kubectl 部署撤消 |
dockershim 的混乱
您可能已经读过“Kubernetes 抛弃了 Docker”。这在狭义上是正确的,并且不必要地吓坏了很多人。 Kubernetes 通过称为容器运行时接口的标准来运行容器。 Docker Engine 没有直接实现它,因此 Kubernetes 包含了一个名为 dockershim 的适配器。该适配器在 1.24 版本中被删除。
集群现在通常使用 containerd 或 CRI-O 等运行时。 使用 Docker 构建的映像仍然有效,因为它们遵循开放容器计划 (OCI) 映像标准。因此,您可以继续使用 Docker 进行构建,并且您的集群会使用不同的运行时来运行结果。
Docker Compose、Swarm 和 Kubernetes
更有意义的比较是协调容器的工具之间的比较:
| 工具 | 适用范围 | 设置工作量 | 适合 |
|---|---|---|---|
| Docker 组合 | 一台主机 | 很低 | 本地开发、小型应用程序 |
| 码头工人群 | 小簇 | 低 | 简单的多服务器设置 |
| 库伯内斯 | 大集群 | 高 | 复杂的大型系统 |
Docker Compose 从一个文件启动多个相关容器,它非常适合 Web 应用程序和笔记本电脑上的数据库。 Swarm 内置于 Docker 中,比 Kubernetes 更容易学习,但它的生态系统要小得多。 Kubernetes 拥有最大的社区和工具,这也是它成为大型部署默认设置的重要原因。
提示:比较实际成本。除了学习之外,Kubernetes 还意味着持续升级、安全工作和监控。 Amazon EKS、Google GKE 或 Azure AKS 等托管服务可以减少这项工作,但不会消除它。
你需要哪一个?
在以下情况下,单独使用 Docker 通常就足够了:
- 您正在本地开发或在一台服务器上运行小型应用程序。
- 您的团队很小,没有专门的运营人员。
- 偶尔短暂的停机是可以接受的。
当满足以下条件时,Kubernetes 就开始有意义:
- 您运行不同团队自行部署的许多服务。
- 您需要跨多个计算机或区域的高可用性。
- 流量变化很大,自动扩展可以节省真正的资金。
- 您拥有操作它的技能或预算。
还有一个中间立场。托管容器平台(例如 AWS Fargate、Google Cloud Run 或 PaaS)可以运行 Docker 映像,无需维护集群。对于许多成长型公司来说,这是正确的下一步,而不是直接跳到 Kubernetes。
如果您出于职业原因想学习两者,那么顺序很明确:首先是 Docker,然后是 Kubernetes。如果不先了解镜像、端口和容器日志,就无法轻松调试 Pod。
常见错误
- 过早采用 Kubernetes。 复制大公司的架构可能会让小团队埋头于运营工作。
- 在生产中使用
latest标签。您无法可靠地判断什么正在运行或回滚。使用特定版本标签。 - 在容器内存储数据。 容器是一次性的,因此请使用卷或托管数据库。
- 跳过运行状况检查。 如果没有就绪性和活跃性探测,Kubernetes 就无法对您的应用程序做出正确的决策。
- 忘记资源限制。 一个贪婪的容器可能会使节点上的其他所有容器都挨饿。
最后的话
Docker 打包您的软件,然后 Kubernetes 大规模运行它。与其问哪个更好,不如问你今天遇到了什么问题。如果是“我的应用程序很难安装”,请使用 Docker。如果是“我的 40 个服务需要在服务器故障中幸存下来”,请看看 Kubernetes。从解决实际问题的最简单的工具开始,当您看到需要时再升级。
常见问题
Kubernetes 能取代 Docker 吗?
不完全是,因为他们从事不同的工作。 Kubernetes 不构建镜像,因此团队仍然使用 Docker 或类似的工具,然后 Kubernetes 运行结果。
我可以在没有 Docker 的情况下使用 Kubernetes 吗?
是的。集群通常使用 containerd 或 CRI-O 来运行容器。使用 Docker 构建的镜像仍然有效,因为它们遵循 OCI 标准。
我应该先学习 Docker 还是 Kubernetes?
首先学习 Docker。 Kubernetes 假设您已经了解图像、容器、端口和日志,因此这些基础知识将使学习变得更加容易。
Docker Swarm 死了吗?
不,它仍然可用并且适合简单的集群。然而,它的生态系统和采用率比 Kubernetes 小得多,因此请检查它是否满足您的长期需求。
小公司需要 Kubernetes 吗?
通常一开始不会。 Docker Compose 或托管容器平台通常可以很好地为他们服务,直到规模或团队结构使 Kubernetes 物有所值。
继续学习
需要测试、WordPress 或 SEO 方面的帮助?
我们帮助团队交付更好的软件并发展他们的网站。告诉我们您正在做什么,我们将建议明确的下一步。
更多指南 测试、人工智能和现代开发
喜欢这篇文章吗?选择一个主题并直接跳至我们最新的教程。
人工智能幻觉解释:语言模型为何会产生幻觉以及如何减少它
人工智能幻觉解释:它们是什么,为什么语言模型产生它们,常见类型,如何通过基础和提示来减少它们,以及我们进行的简单检查。
阅读文章 →DevOps环境变量解释:.env 文件、秘密和配置正确完成
使用我们运行的 Python 示例解释环境变量:读取和设置它们、为什么每个值都是字符串、.env 文件、进程继承、秘密和验证。
阅读文章 →软件开发API 速率限制解释:算法、标头和处理 429 错误
API 速率限制通过我们运行的模拟进行了解释:令牌桶、固定窗口、滑动窗口、429 响应、速率限制标头、带回退的重试和测试限制。
阅读文章 →测试自动化Python 中的模拟:unittest.mock、补丁以及导致测试谎言的错误
使用unittest.mock 在Python 中进行模拟,经过测试:模拟和return_value、side_effect、修补使用名称的位置、spec 和autospec、模拟日期以及常见错误。
阅读文章 →软件测试代码覆盖率解释:行覆盖率与分支覆盖率以及 100% 的真正含义
用真实的coverage.py运行来解释代码覆盖率:行、分支和条件覆盖率,阅读报告,为什么100%覆盖率仍然会错过错误,以及合理的目标。
阅读文章 →数据库与 SQLSQL 中的 DELETE、TRUNCATE 和 DROP:差异、回滚以及何时使用它们
DELETE、TRUNCATE 和 DROP 通过经过测试的 SQL 进行了解释:各自删除什么、回滚、触发器、身份重置、速度,以及 PostgreSQL、MySQL、SQL Server 和 SQLite 有何不同。
阅读文章 →数据库与 SQLSQL 视图解释:创建、使用和避免陷阱(附示例)
SQL 视图通过我们运行的查询进行解释:创建视图、实时结果、隐藏列、性能、可更新和物化视图、破坏的依赖关系和最佳实践。
阅读文章 →软件开发TypeScript 接口与类型:真正的差异,使用编译器进行测试
TypeScript 接口与类型,使用真实编译器进行检查:两者都可以做什么,只有一个可以做什么,声明合并,扩展与交集以及要遵循的简单规则。
阅读文章 →软件开发Python *args 和 **kwargs 解释:灵活的函数和清晰的示例
Python *args 和 **kwargs 用我们运行的代码进行了解释:打包和解包、参数顺序、仅关键字和仅位置参数、装饰器和常见错误。
阅读文章 →软件开发Python Lambda 函数:语法、实际用途以及何时不使用它们
Python lambda 函数用我们运行的代码进行了解释:语法、使用键排序、映射和过滤器、闭包、后期绑定陷阱,以及何时普通 def 是更好的选择。
阅读文章 →人工智能什么是人工智能代理?它们是如何工作的,只需运行一个简单的代理循环
什么是人工智能代理?关于代理循环、工具、内存和护栏的简单指南,以及我们运行的一个小型 Python 循环、真正的风险以及何时简单的聊天机器人是更好的选择。
阅读文章 →DevOps监控与可观察性:真正的区别,包括日志、指标和跟踪
监控与可观察性解释:已知和未知的故障、日志、指标和跟踪、检查事件演练、黄金信号、OpenTelemetry 和有用的警报。
阅读文章 →DevOpsDocker Volumes 与 Bind Mounts:以正确的方式保留容器数据
Docker 卷与绑定挂载解释:为什么容器数据消失、命名卷、绑定挂载、tmpfs、Compose 示例、备份、权限以及何时使用它们。
阅读文章 →测试自动化TDD 与 BDD:主要区别、以两种方式构建的一个功能以及何时使用每种方式
TDD 与 BDD 的解释是通过一种双向构建的折扣功能:使用我们运行的代码进行红绿重构、Given-When-Then 场景、工具、权衡以及何时使用每种方案。
阅读文章 →软件测试用户验收测试 (UAT):流程、清单和真实示例
用户验收测试解释:谁运行它、它与 QA 有何不同、七步流程、UAT 测试用例模板、清单、签核标准和常见错误。
阅读文章 →数据库与 SQLSQL NULL 值解释:IS NULL、COALESCE、NULLIF 和要避免的陷阱
通过我们运行的查询解释了 SQL NULL 值:三值逻辑、IS NULL、COALESCE、NULLIF,聚合、连接和排序如何处理 NULL,以及它悄悄引起的错误。
阅读文章 →数据库与 SQLSQL CASE WHEN 语句:语法、实例和常见错误
SQL CASE WHEN 解释了我们运行的查询:简单和搜索表单、条件顺序、SUM 透视、COUNT 技巧、自定义排序、更新和要避免的 NULL 陷阱。
阅读文章 →软件开发什么是智威汤逊?通过工作示例解释 JSON Web 令牌
什么是智威汤逊? JSON Web Tokens 解释:这三个部分,在我们运行的代码中构建和篡改的令牌,JWT 与会话,存储,刷新令牌和安全错误。
阅读文章 →软件开发JavaScript 事件循环解释:调用堆栈、微任务和计时器以及示例
JavaScript 事件循环用我们运行的代码进行了解释:调用堆栈、任务和微任务队列、为什么 setTimeout 0 不是立即执行、阻塞、饥饿和面试测验。
阅读文章 →软件开发Python 生成器和 Yield 解释:通过真实示例节省内存
Python 生成器和yield 用我们运行的代码进行了解释:它们如何暂停和恢复、测量内存节省、管道、无限序列、yield from、发送和常见陷阱。
阅读文章 →人工智能什么是嵌入?向量和余弦相似度简单解释
什么是嵌入?关于向量、余弦相似度和语义搜索的简单语言指南,其中包含我们运行的一个小示例、常见用途、选择模型和已知限制。
阅读文章 →DevOps蓝绿部署与金丝雀部署:差异、权衡以及如何选择
蓝绿部署与金丝雀部署解释:每种部署的工作原理、成本、风险和回滚、我们测试的稳定用户分桶、数据库迁移、滚动更新以及如何选择。
阅读文章 →测试自动化不稳定的测试:造成这些问题的原因以及如何彻底解决它们
我们运行的演示解释了不稳定的测试:主要原因、如何确认和诊断、正确的时间修复、共享状态和日期以及隔离过程。
阅读文章 →软件测试负载测试与压力测试:差异、指标和 k6 示例
负载测试与压力测试的解释:各自证明什么、峰值和浸泡测试、重要的指标、为什么平均值会误导、k6 个示例以及如何计划测试。
阅读文章 →软件测试测试用例与测试场景:主要区别、示例以及何时使用它们
测试用例与测试场景的解释:清晰的定义、并排的表格、从场景到案例的密码重置功能、模板以及何时使用每个功能。
阅读文章 →数据库与 SQLSQL 查询优化:读取执行计划并修复慢速查询(带计时)
对一百万行进行 SQL 查询优化测试:读取执行计划、添加正确的索引、修复不可控制的过滤器、使用复合索引和键集分页。
阅读文章 →数据库与 SQLSQL UNION 与 UNION ALL:差异、示例和性能测试
SQL UNION 与 UNION ALL 通过我们运行的查询进行了解释:重复、都遵循的规则、真实的数据错误、测量的性能、INTERSECT 和 EXCEPT 以及何时使用它们。
阅读文章 →软件开发JavaScript Fetch API 教程:GET、POST、错误处理和超时
针对本地服务器测试的 JavaScript Fetch API 教程:GET 和 POST、为什么不抛出 404、可重用的帮助程序、查询字符串、超时、CORS 和常见错误。
阅读文章 →软件开发Python 文件处理:读取、写入、CSV、JSON 和 pathlib 以及示例
使用我们运行的代码解释了 Python 文件处理:打开模式、使用、读取大文件、CSV、JSON、pathlib、二进制文件、安全写入、错误处理和常见错误。
阅读文章 →软件开发Python 类和对象:带有实际示例的 OOP 教程
用我们运行的代码解释 Python 类和对象:__init__、方法、类与实例属性、继承、属性、dunder 方法、数据类和常见错误。
阅读文章 →人工智能什么是大语言模型?法学硕士如何运作,在没有炒作的情况下进行解释
什么是大语言模型?法学硕士用简单的语言解释:标记、嵌入、注意力、训练、温度和上下文窗口、一个微小的下一个单词演示和真正的限制。
阅读文章 →DevOps适合初学者的 Linux 命令:带有示例的基本终端指南
适合初学者的 Linux 命令,按任务分组:导航、文件、查看日志、grep、管道、权限、进程和档案,并附有实用的日志分析示例。
阅读文章 →测试自动化Selenium 与 Playwright:主要区别、代码比较以及选择
Selenium 与 Playwright 比较:架构、等待、浏览器和语言支持、调试、两种工具中的相同测试、迁移建议以及如何选择。
阅读文章 →软件测试等价划分:通过实例解释测试设计技术
等效分区解释:如何找到有效和无效分区,年龄、密码和优惠券的工作示例,我们运行的测试,以及为什么将其与边界配对。
阅读文章 →软件测试软件测试中的严重性与优先级:差异、矩阵和实例
软件测试中的严重性与优先级解释:清晰的定义、带有示例的四向矩阵、级别尺度、谁设置每个级别以及如何运行错误分类。
阅读文章 →数据库与 SQLSQL 子查询教程:类型、相关子查询和 EXISTS 以及示例
SQL 子查询教程,其中包含我们运行的查询:标量、IN、FROM 和 SELECT 子查询、相关子查询、EXISTS、NOT IN NULL 陷阱以及子查询与 JOIN 与 CTE。
阅读文章 →数据库与 SQLSQL 事务和 ACID 解释:COMMIT、ROLLBACK 和隔离级别
SQL 事务和 ACID 通过我们运行的银行转账进行了解释:COMMIT、ROLLBACK、保存点、锁、隔离级别、脏读、死锁和最佳实践。
阅读文章 →软件开发Git 命令备忘单:您实际使用的命令,以及示例
Git 命令备忘单按任务分组:设置、暂存、分支、远程、存储、历史记录和撤消错误,以及来自真实演示存储库的输出。
阅读文章 →软件开发JavaScript Promise 解释:then、catch、Promise.all 和常见错误
JavaScript 承诺用我们运行的代码进行了解释:状态、then 和 catch 链、错误处理、Promise.all、allSettled、race 和 any、事件循环和常见错误。
阅读文章 →软件开发Python 字典教程:方法、示例和常见模式
Python 字典教程,包含我们运行的代码:创建、读取和更新字典、关键方法、合并、理解、计数和分组模式以及常见错误。
阅读文章 →人工智能什么是 RAG?通过工作示例解释检索增强生成
什么是RAG?检索增强生成逐步解释:索引、分块、嵌入、检索、一个小型的 Python 工作示例、评估和常见故障。
阅读文章 →DevOpsTerraform 初学者教程:从第一个文件到模块的基础设施即代码
适合初学者的 Terraform 教程:从没有云帐户开始,学习初始化、计划和应用,然后是变量、输出、状态、模块和安全的团队工作流程。
阅读文章 →DevOpsDocker 与 Kubernetes:主要区别、它们如何协同工作以及您需要哪些
Docker 与 Kubernetes 的解释:各自的作用、同一个应用程序同时运行、Docker Compose 和 Swarm 的比较、dockershim 的变化以及如何决定您需要什么。
阅读文章 →测试自动化Pytest 初学者教程:夹具、参数化和真实示例
面向初学者的 pytest 教程,其中包含我们在 pytest 9.2 上运行的测试:断言、固定装置、参数化、conftest、标记、tmp_path、monkeypatch、有用的标志和 CI。
阅读文章 →软件测试软件测试生命周期 (STLC):6 个阶段、进入和退出标准、示例
软件测试生命周期解释:六个 STLC 阶段,包括进入和退出标准、可交付成果、有效的登录功能示例、STLC 与 SDLC 以及敏捷中的 STLC。
阅读文章 →软件开发Git merge 与 rebase 的区别:示例及如何选择
用真实的 log 输出、对比表、rebase 黄金法则和安全的团队工作流,讲清 Git merge 与 rebase 的区别。
阅读文章 →软件开发Python 列表推导式:语法、示例与何时避免使用
Python 列表推导式详解:语法、过滤、if-else、嵌套循环、实测耗时,以及何时普通 for 循环更合适。
阅读文章 →数据库与 SQLSQL JOIN 详解:附示例(INNER、LEFT、RIGHT、FULL)
通过示例表和查询,一步步讲解 SQL JOIN:INNER、LEFT、RIGHT、FULL、CROSS 和 SELF,并介绍常见错误与性能技巧。
阅读文章 →