Docker、Docker Compose 与 Kubernetes:分别解决什么问题?

刚接触容器技术时,Docker、Docker Compose 和 Kubernetes 经常一起出现,很容易让人误以为它们是三种差不多的工具。其实,它们解决的问题处在不同层次:

  • Docker 负责把一个应用装进容器,并运行起来。
  • Docker Compose 负责在一台机器上组织多个容器。
  • Kubernetes 负责在多台机器上管理大量容器,并让系统持续稳定运行。

可以先记住一句简单的话:

Docker 管一个容器,Docker Compose 管一组容器,Kubernetes 管一个容器集群。

一、为什么需要 Docker?

假设你开发了一个 Java 网站,在自己的电脑上运行正常。交给同事或部署到服务器后,却出现了各种问题:

  • 服务器没有安装 Java。
  • Java 版本不一致。
  • 缺少某些系统依赖。
  • 配置文件路径不同。
  • 同事不知道该执行哪些启动命令。

这就是开发中常见的"在我电脑上明明能运行"。Docker 的做法是:把应用程序、运行环境、依赖和启动命令一起打包成镜像。Docker 官方将镜像描述为运行容器所需文件、程序库和配置的标准化软件包。拿日常外卖举个例子:

  • 应用代码是食物。
  • Java、Node.js 等运行环境是餐具。
  • 配置和依赖是调料。
  • Docker 镜像是封装好的外卖套餐。
  • 容器是这份套餐真正被打开并使用的状态。

同一个镜像可以启动多个容器,就像同一份套餐模板可以制作出很多份外卖。

1、镜像和容器有什么区别?

镜像是一份只读模板,容器是镜像运行后的实例。可以把它们理解为:

镜像 = 程序安装包

容器 = 已经启动的程序

例如,通过下面的命令启动一个 Nginx 容器:

bash 复制代码
docker run -d -p 8080:80 --name my-nginx nginx

这条命令做了几件事:

  1. 获取 nginx 镜像。
  2. 根据镜像创建一个名为 my-nginx 的容器。
  3. 在后台运行容器。
  4. 把本机的 8080 端口映射到容器的 80 端口。

访问 http://localhost:8080,就能看到 Nginx 页面。

2、Dockerfile 是什么?

如果要把自己的应用制作成镜像,通常需要编写 Dockerfile。下面是一个简单的 Java 应用示例:

FROM eclipse-temurin:17-jre

WORKDIR /app

COPY target/demo.jar app.jar

EXPOSE 8080

CMD "java", "-jar", "app.jar"

它表达的意思很直接:

  • 使用带有 Java 17 的基础镜像。
  • 把工作目录设为 /app
  • 将项目生成的 JAR 文件复制进去。
  • 声明应用使用 8080 端口。
  • 容器启动时执行 java -jar app.jar

然后构建并运行:

bash 复制代码
docker build -t demo-app:1.0 .
docker run -d -p 8080:8080 demo-app:1.0

这样,其他人不需要手动安装 Java,也不必研究项目的启动方式。只要能运行容器,就能按照同样的方式启动这个应用。

二、为什么还需要 Docker Compose?

只有一个容器时,使用 docker run 并不麻烦。实际项目通常不止一个服务。例如,一个普通网站可能包含:

前端 Nginx

Java 后端

MySQL 数据库

Redis 缓存

如果全部使用 docker run,需要分别配置端口、网络、环境变量和数据卷。启动命令会越来越长,而且很容易漏掉参数。Docker Compose 就是用来处理这种情况的。它允许我们在一个 YAML 文件中定义多个服务、网络和数据卷,再通过一条命令统一启动。Docker 官方把 Compose 定义为"用于定义和运行多容器应用的工具"。

举一个简单示例:下面的 compose.yaml 定义了一个 Java 应用和一个 MySQL 数据库:

bash 复制代码
services:
  app:
    build: .
    ports:
      - "8080:8080"
    environment:
      DB_HOST: mysql
      DB_PORT: 3306
      DB_NAME: demo
      DB_USER: root
      DB_PASSWORD: "123456"
    depends_on:
      - mysql

  mysql:
    image: mysql:8.4
    environment:
      MYSQL_ROOT_PASSWORD: "123456"
      MYSQL_DATABASE: demo
    volumes:
      - mysql-data:/var/lib/mysql

volumes:
  mysql-data:

在这个文件所在目录执行:

bash 复制代码
docker compose up -d

Compose 会完成这些工作:

  • 构建并启动 Java 应用。
  • 下载并启动 MySQL 镜像。
  • 建立容器之间的网络。
  • 创建数据库存储卷。
  • 把后端服务的 8080 端口暴露给本机。

查看日志可以执行:

bash 复制代码
docker compose logs -f

停止并删除这些容器:

bash 复制代码
docker compose down
docker compose down -v  //-v 会删除数据库使用的数据卷,已有数据可能随之消失。

注:早期版本使用带连字符的独立命令:docker-compose up -d,现在主流版本已经把 Compose 集成进 Docker CLI,通常使用:docker compose up -d,两者只是写法不同罢了。

三、为什么又出现了 Kubernetes?

Docker Compose 很适合开发、测试和单机部署。不过,当系统运行在多台服务器上时,事情会迅速变复杂。

假设一个电商网站原来只有一个后端容器。促销开始后,访问量突然增加,需要运行十个后端实例。此时会遇到一连串问题:

  • 十个容器应该分配到哪些服务器?
  • 某台服务器宕机后,容器怎么办?
  • 某个容器崩溃后,谁负责重启?
  • 用户请求怎样分发到不同容器?
  • 发布新版本时,怎样避免整个网站停机?
  • 访问量下降后,怎样减少实例,节省资源?

这些事情靠人工处理并不现实。Kubernetes,简称 K8s ,就是用来自动管理容器化应用的 。Kubernetes 是一个用于管理容器化工作负载和服务的平台,支持声明式配置和自动化

四、理解 Kubernetes 的几个基本概念

Kubernetes 的概念很多,入门时先抓住 Node、Pod、Deployment 和 Service 就够了。

1、Node:运行容器的机器

Node 是 Kubernetes 集群中的工作机器,可以是物理服务器,也可以是云服务器或虚拟机。举个例子:

Kubernetes 集群

├── Node 1:192.168.1.11

├── Node 2:192.168.1.12

└── Node 3:192.168.1.13

Kubernetes 会把应用分配到这些 Node 上运行。一个标准集群由控制平面和一台或多台工作节点组成。

2、Pod:Kubernetes 中最小的部署单位

Kubernetes 通常不直接管理单个容器,而是管理 Pod。一个 Pod 可以包含一个或多个关系非常紧密的容器。大多数业务场景中,一个 Pod 里只有一个主要应用容器。

Pod

└── Java 应用容器
Pod

├── Java 应用容器

└── ELK日志采集容器

同一个 Pod 内的容器共享网络环境,也可以共享存储。可以暂时把 Pod 理解为"给容器套了一层 Kubernetes 管理外壳"。

3、Deployment:管理 Pod 的数量和版本

如果我们希望运行三个 Java 应用实例,可以定义一个 Deployment:

bash 复制代码
apiVersion: apps/v1
kind: Deployment
metadata:
  name: demo-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: demo-app
  template:
    metadata:
      labels:
        app: demo-app
    spec:
      containers:
        - name: demo-app
          image: example/demo-app:1.0
          ports:
            - containerPort: 8080

其中,replicas: 3表示期望运行三个 Pod。Kubernetes 会不断检查实际状态,如果其中一个 Pod 崩溃,Deployment 会重新创建一个。Kubernetes 关心的不是某个具体 Pod 必须永远活着,而是系统要始终保持三个可用副本。

4、Service:给 Pod 提供稳定入口

Pod 可能随时被销毁和重建,IP 地址也可能变化。客户端不能把请求直接绑定到某个 Pod 的 IP。Service 会在一组 Pod 前面提供一个稳定的访问入口:

用户请求

Service

↙ ↓ ↘

Pod Pod Pod

即使后面的 Pod 被替换,Service 的访问方式仍然可以保持不变。它还能把请求分发给多个 Pod。Kubernetes 官方将 Service 描述为向一个或多个 Pod 暴露网络应用的方法。对应的参考配置:

bash 复制代码
apiVersion: v1
kind: Service
metadata:
  name: demo-app-service
spec:
  selector:
    app: demo-app
  ports:
    - port: 80
      targetPort: 8080
  type: ClusterIP

这里的选择器selector:app:demo-app会找到带有相同标签的 Pod。

五、三者之间到底是什么关系?

接下来我们用个餐厅做比喻:

1、Docker:负责标准化每一道菜。Docker 规定菜怎么做、需要哪些原料、做好后装进什么盒子。同一个镜像在不同机器上运行,环境差异会小很多。

2、Docker Compose:负责一桌套餐。一个网站不只有后端,还需要数据库和缓存。Compose 把这些服务写进同一份菜单:

一份套餐

├── 前端

├── 后端

├── MySQL

└── Redis

执行一条命令,整套服务一起启动。它通常管理的是一台主机上的多个容器。

3、Kubernetes:负责经营连锁餐厅。当应用分布在多台服务器上,Kubernetes 开始发挥作用:

  • 哪台服务器有空闲资源,就把 Pod 调度过去。
  • 某个实例坏了,就创建新的。
  • 顾客变多时,可以增加应用副本。
  • 发布新版本时,可以逐批替换旧版本。
  • 对外提供统一入口,把请求分给不同实例。

因此,三者不是简单的替代关系,而是层次不同:

应用代码

Dockerfile 构建镜像

Docker 运行容器

Docker Compose 组织单机上的多个容器

Kubernetes 编排集群中的容器化应用

六、写在最后

三者的定位可以简化为下面这张表:

工具 主要用途 常见使用场景
Docker 构建镜像、运行容器 打包和运行单个应用
Docker Compose 定义并启动多个关联容器 本地开发、测试、单机部署
Kubernetes 在集群中调度和管理容器化应用 多机部署、扩容、故障恢复、滚动更新
相关推荐
AugustSkys43 分钟前
云原生运维实战:ETCD 分布式存储集群运维、Kubernetes 高可用集群部署与版本平滑升级
运维·kubernetes
tryCbest2 小时前
Kubernetes(K8s)容器化部署
云原生·容器·kubernetes
gx23482 小时前
k8spod管理
linux·容器·kubernetes
国医中兴9 小时前
电子病历的时序数据分析:ClickHouse在临床指标监控中的落地
微服务·云原生·容器·kubernetes·k8s
浮生望9 小时前
Docker 入门实战:容器化思想、nginx 反向代理与容器生命周期管理
docker
小谈不敲代码12 小时前
【12-kubenetes的持久化存储】
运维·kubernetes
小谈不敲代码14 小时前
Higress 生产级调优手册
云原生·kubernetes·gateway·负载均衡·higress
MyFreeIT15 小时前
Docker Network
运维·docker·容器
ezreal_pan15 小时前
Docker 多容器日志统一查看
运维·docker·容器