Docker 入门

Docker 入门:是什么、怎么用、和虚拟机的区别

一、Docker 是什么

Docker 是一个开源的容器化平台,用于开发、交付和运行应用程序。它的核心价值在于:把应用及其全部依赖(运行时、库、配置)打包成一个轻量、可移植的容器,确保应用在任何支持 Docker 的环境中都以完全相同的方式运行。

Docker 平台由几个关键部分组成:

  • 镜像:一个只读模板,包含运行应用所需的全部文件、依赖和配置,可以理解为容器的"蓝图"或"安装包"。
  • 容器:镜像的运行实例。容器之间相互隔离,拥有自己的文件系统和网络空间,但共享宿主机内核。
  • 仓库:集中存放和分发镜像的地方,最著名的是公共仓库 Docker Hub,也可以搭建私有仓库。
  • Docker 引擎 :客户端-服务器架构。你通过 docker 命令行发送指令,由后台守护进程负责构建、运行和分发容器。

二、Docker 的主要作用

Docker 主要解决以下几类问题:

  • 环境一致性:消除"在我电脑上能跑"的问题,让开发、测试、生产环境保持一致。
  • 快速交付与部署:配合 CI/CD 流程,加速应用的测试和上线。
  • 提高资源利用率:相比虚拟机,同样的硬件能运行更多工作负载。
  • 隔离运行时依赖:宿主机不需要安装 JDK、数据库客户端等应用依赖,依赖被封装在镜像里。

需要澄清一个常见误解:Docker 并不是"到目标机器上自动安装依赖",而是把运行环境预先打包成镜像。目标机器只需要有 Docker,依赖在镜像里已经就位。

三、Docker 和传统虚拟机的区别

两者在"安装环境"这件事上的根本不同在于:虚拟机安装的是一个完整的操作系统,而 Docker 安装的只是应用运行所需的代码和依赖,共享宿主机内核。

传统虚拟机:模拟一台完整的电脑

典型流程是:先安装操作系统(如 CentOS/Ubuntu),更新系统,再用包管理器逐个安装应用所需的依赖(如 Java 运行环境、数据库、Web 服务器),配置防火墙,最后上传应用代码并启动。这相当于手动配置一台全新的电脑,每换一个环境就要重复一遍,容易出现遗漏或版本不一致。

Docker:打包一个"即开即用"的盒子

应用的环境在构建阶段就被写进 Dockerfile,打包成一个只读镜像。部署时,目标机器只需拉取镜像并运行容器,应用的依赖已经全部就位,无需再执行"安装环境"的步骤。

关键对比

维度 传统虚拟机 Docker 容器
部署流程 装 OS → 更新 → 手动装依赖 → 上传代码 → 启动,步骤繁琐 编写 Dockerfile(一次性)→ 构建镜像 → 运行容器,部署时无需安装环境
资源占用 需运行完整客户 OS,内存磁盘占用以 GB 计 共享宿主机内核,镜像通常几十到几百 MB
启动速度 需引导完整 OS,通常以分钟计 本质是启动隔离进程,通常秒级就绪
隔离强度 硬件虚拟化,强隔离,安全性更高 进程级隔离,共享内核,隔离性弱于虚拟机
适用场景 需要不同内核的 OS,或对安全隔离有硬性要求 追求部署确定性、速度和资源效率的应用部署

在实际生产环境中,两者也常结合使用:在虚拟机里运行 Docker,兼顾隔离与效率。

四、Docker 的核心优势

  1. 环境一致性:镜像把应用和依赖绑定在一起,开发、测试、生产环境完全一致。
  2. 部署快、资源省:镜像轻量、启动秒级,一台机器可运行大量容器。
  3. 跨平台部署:在 Windows 上开发、构建 Linux 镜像,部署到 Linux 服务器完全可行。Windows 上的 Docker Desktop 默认使用轻量 Linux 虚拟机运行 Docker 引擎,构建出的镜像天然是 Linux 镜像。
  4. 依赖与宿主机解耦 :宿主机不需要装 JDK、不需要配 JAVA_HOME、不需要担心版本冲突,运行环境由镜像保证。

五、核心原理:Docker 如何从镜像仓库拉取预置环境

FROM maven:3.8-openjdk-17-slim AS build 这行命令,实质是让 Docker 去镜像仓库拿到一个已经装好 Maven 和 JDK 的环境。这背后是 Docker 镜像的分层存储和拉取机制。

镜像不是"一个包",而是一叠"层"

Docker 镜像并不是一个巨大的压缩文件。它由多个只读层 叠加而成,每一层代表一次文件系统变更(安装一个软件、复制一批文件、修改一个配置)。maven:3.8-openjdk-17-slim 这个镜像,底层可能是一个精简的 Debian 系统层,上面叠加了 OpenJDK 17 的安装层,再上面是 Maven 的安装层。每一层都有自己独立的、基于内容的哈希值作为标识。

拉取时,Docker 只拿"本地没有的层"

当你执行 docker build,Docker 发现本地没有 maven:3.8-openjdk-17-slim 时,会向镜像仓库(默认 Docker Hub)发起拉取请求。关键机制在于:Docker 会先检查每一层在本地是否已经存在。如果某个基础层(比如 Debian slim 层)你之前拉取其他镜像时已经下载过,Docker 就跳过它,只下载缺失的层。

这就是为什么拉取一个"新"镜像有时会很快:它和本地已有镜像共享了大量底层。Docker 官方文档明确说明,层可以被镜像复用,拉取 debian:bookworm 时如果 debian:latest 的层已经在本地,就"只拉取元数据,不拉取层"。

拉取完成后,层被"叠"起来用

下载下来的各个层被解压到本地存储目录中。当你启动容器时,Docker 通过联合文件系统(union filesystem)把这些只读层按顺序叠加,对外呈现为一个完整的、统一的可读文件系统。容器看到的 java 命令、mvn 工具,就是叠加后的结果。此外,Docker 会在最上面再加一个薄的可写层给容器专用,所有运行时产生的修改都写在这里,底下的只读层保持不变,因此多个容器可以安全地共享同一份镜像层。

所以,FROM 那行到底发生了什么

FROM maven:3.8-openjdk-17-slim AS build 的实质是:Docker 向仓库请求这个镜像的清单 ,清单里列出了构成这个镜像的所有层的哈希值。Docker 逐层检查本地缓存,只下载缺失的层,然后把它们叠加起来,作为后续 COPY、RUN 等指令的执行基础。你不需要写任何安装 Maven 或 JDK 的命令,因为这些层在镜像制作者构建时就已经装好并固化了,你只是把它们"拉"下来复用。

理解了这一点,也就理解了 Docker 镜像高效的原因:层被共享,存储不重复,传输只传增量。这也是为什么你在 Windows 上构建的镜像,拿到 Linux 上运行时,不需要 Linux 宿主机再装一遍 JDK------JDK 所在的层已经跟着镜像一起过去了。

六、Docker 一般怎么用

以 Java 应用为例,典型流程如下。

1. 编写 Dockerfile

推荐使用多阶段构建:在 Docker 内部完成编译,再把编译产物复制到只含运行时的轻量镜像里。

dockerfile

复制代码
# 第一阶段:构建
FROM maven:3.8-openjdk-17-slim AS build
WORKDIR /app
COPY pom.xml .
RUN mvn dependency:go-offline
COPY src ./src
RUN mvn clean package -DskipTests

# 第二阶段:运行
FROM openjdk:17-jre-slim
WORKDIR /app
COPY --from=build /app/target/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]

如果已经有打好的 JAR 包,Dockerfile 可以非常简短:

dockerfile

复制代码
FROM openjdk:8-jre-slim
WORKDIR /app
COPY target/your-app.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]

FROM 指令的含义是让 Docker 去镜像仓库拉取一个已经预装好相应环境的现成镜像(如包含 JDK 8 的基础镜像),而不是在构建时自己安装。这些工具是基础镜像的制作者提前装好并打包进去的,你直接继承这个结果。具体拉取机制见上一节。

2. 构建镜像

在 Linux 测试环境(或任意有 Docker 的机器)上执行:

bash

复制代码
docker build -t myapp .

Docker 会拉取基础镜像,把你的 JAR 复制进去,生成 myapp 镜像。

3. 运行容器

bash

复制代码
docker run -p 8080:8080 myapp

容器启动,内部执行 java -jar app.jar。注意:启动方式从"在宿主机上直接敲 java -jar"变成了"用 docker run 启动容器,容器内再执行 Java 命令"。

4. 几个让 Dockerfile 更好用的习惯

  • 先复制依赖文件,再复制源码 :先 COPY pom.xml 再 COPY src,只改代码不改依赖时可利用缓存,跳过下载依赖。
  • 用 ENTRYPOINT 而不是 CMD :主启动命令更明确,不易被 docker run 附加命令覆盖。
  • 加 .dockerignore :排除 target/、.git/、*.log 等,加快构建、减小镜像。
  • 考虑非 root 用户运行:生产环境可创建普通用户运行应用,作为安全加固。

七、一个完整的部署场景

场景:Windows 上开发,JDK 8 + Maven,打成 JAR 包,部署到只有 Docker 的 Linux 测试环境。

  1. Windows 开发环境用 JDK 8 + Maven 打出 JAR 包。
  2. 编写 Dockerfile:FROM openjdk:8-jre-slim,COPY JAR 进去,ENTRYPOINT ["java","-jar","app.jar"]。
  3. 把 JAR 和 Dockerfile 传到 Linux(或在 Windows 上构建镜像后传到 Linux)。
  4. Linux 上执行 docker build -t myapp .:Docker 拉取 JDK 8 基础镜像,复制 JAR,生成镜像。
  5. Linux 上执行 docker run -p 8080:8080 myapp:容器启动,内部执行 java -jar app.jar。

整个过程中,Linux 宿主机不需要安装 JDK 8,不需要配 JAVA_HOME,不需要担心宿主机装的是 JDK 11 还是 17。JDK 8 被封装在镜像里,与应用绑定,到哪台有 Docker 的机器上运行环境都一样。

八、总结

Docker 的本质是把运行环境预先打包成镜像,让应用带着自己的环境到处运行。它不负责在目标机器上"自动安装依赖",而是让目标机器根本不需要安装这些依赖。相比虚拟机,它更轻、更快、部署更确定;它的核心优势是环境一致性、跨平台部署和依赖与宿主机解耦。镜像的分层存储和按层拉取机制,保证了镜像的高效共享与增量传输。对于"开发环境 Windows、部署环境 Linux、测试机只有 Docker"这类场景,Docker 是非常合适的解决方案。

相关推荐
java_logo2 小时前
Docker 部署青龙面板:轻松搭建定时任务管理平台
运维·docker·容器·定时任务·青龙面板·轩辕镜像·qinglong
ytianxia62 小时前
用 Docker 把 Obsidian 搬到浏览器:部署、同步与 SSO 配置笔记
docker
流星白龙3 小时前
【Docker】14.存储卷与管理卷Volume
运维·docker·容器
amcomputer3 小时前
Docker 常用命令总结:从安装到日常使用
运维·docker·容器
一级新生5 小时前
Dify 日常使用手册(小白版)
人工智能·docker·云原生·eureka·dify·ai工具
YuanDaima20486 小时前
系统架构与数据库理论基础:PostgreSQL 与 pgvector
数据库·人工智能·docker·postgresql·系统架构·产品经理
程序员老赵21 小时前
Docker 部署 Hue:轻松搭建数据库与数据仓库 SQL 查询平台
sql·docker·开源
m0_617493941 天前
昇腾 NPU Docker 部署实战:解决 stub library 报错并实现模型正常加载
运维·docker·容器
xixiaoyunya1 天前
Docker 常用命令与实战:从入门到部署一个简单应用
docker·容器·eureka