7.9 Docker 基础入门
简介
Docker 是一套用于开发、交付与运行应用的容器化平台。可以把应用及其依赖打进镜像(Image),再以容器(Container)的方式在任意装有 Docker 的主机上以接近一致的环境运行。与完整虚拟机相比,容器共享宿主机内核、启动快、资源占用相对小,适合作为本地依赖服务(如 7.8 中的 MySQL)、微服务打包与 CI 流水线中的标准化运行单元。
本章介绍 Docker 的核心概念、安装与环境检查、镜像仓库(Docker Hub 与镜像加速)、常用 CLI、数据持久化、最小 Dockerfile 与网络入门,并可选了解 Compose 编排与若干实践注意点,便于在 Go / Web 项目中快速落地容器环境。
核心概念
| 概念 | 说明 |
|---|---|
| 镜像(Image) | 只读模板,由多层文件系统叠加而成;包含应用、运行时、库与配置等。 |
| 容器(Container) | 镜像的运行实例,在镜像之上有一层可写容器层;删除容器会丢掉未持久化到卷中的可写数据。 |
| Dockerfile | 描述「如何从基础镜像一步步构建出目标镜像」的文本文件。 |
| 仓库(Registry) | 存放镜像的服务;默认使用 Docker Hub 等。镜像名常写作 仓库/镜像:标签,如 mysql:8.0。 |
| Docker Engine | 宿主机上的守护进程,负责拉取镜像、创建与管理容器。 |
| Docker CLI | 命令行客户端(docker),通过 API 与 Engine 通信。 |
与虚拟机的简要对比:虚拟机带完整客户机操作系统;容器共享宿主机内核,隔离粒度更轻,但内核版本与平台仍受宿主机约束(例如在 Linux 容器里跑的是 Linux 用户态)。
安装与环境检查
常见安装方式
- macOS / Windows:通常安装 Docker Desktop(内含 Engine 与图形界面),适合本机开发。
- Linux:安装 Docker Engine(
docker.io或官方仓库包),服务端与 CI 常见。
具体安装步骤随发行版与版本变化,以 Docker 官方安装文档 为准。
自检命令
docker version # 客户端与服务端版本
docker info # Engine 信息、存储驱动、镜像加速等若 docker version 中 Server 段报错,说明本机 Engine 未启动或未正确连接(Desktop 未运行、Linux 上服务未启动等)。
Linux 权限说明
默认只有 root 或 docker 用户组成员可访问 Docker 套接字。将当前用户加入组后需重新登录生效:
sudo usermod -aG docker "$USER"镜像仓库
镜像仓库(Image Registry) 负责存放与分发镜像;docker pull / docker push 的远端地址由镜像名中的仓库主机决定。建议先完成上一节 「安装与环境检查」,确认 docker version 可用;若从 Docker Hub 拉取较慢,可在本节配置 Registry Mirror(镜像加速) 后再使用下文 docker pull。以下内容说明命名规则、Docker Hub,以及镜像加速配置。
仓库与镜像命名
| 情况 | 含义 |
|---|---|
| 带主机前缀 | 如 ghcr.io/org/app:1.0,拉取/推送到对应 Registry。 |
仅 镜像:标签 | 如 mysql:8.0,默认从 Docker Hub 拉取;完整解析名常写作 docker.io/library/mysql:8.0(library 为官方库默认命名空间)。 |
| 用户/组织命名空间 | 如 yourname/myapp:1.0,表示 Hub 上 yourname 空间下的镜像。 |
企业内网还常用 私有仓库(Harbor、云厂商容器镜像服务、自建 registry:2 等),与公共 Hub 互补,便于权限与审计。
Docker Hub 是什么
Docker Hub 是 Docker 官方提供的公共镜像仓库,作用上可类比代码界的 GitHub:集中托管大量镜像,便于检索与复用。
- 使用
docker pull下载镜像到本机;使用docker push上传到自己的命名空间(需docker login与写权限)。 - 与上文「核心概念」表中的 仓库(Registry) 对应;下文「拉取与查看镜像」里不带主机前缀的
docker pull mysql:8.0,在未改默认 Registry 时即访问 Docker Hub。
Docker Hub 能做什么
| 作用 | 说明 |
|---|---|
| 镜像存储与分发 | 托管镜像层,供下载;未指定其他 Registry 时,docker pull 默认对接 Hub(若配置了镜像加速,则由守护进程按策略走加速地址)。 |
| 官方镜像 | 如 Nginx、MySQL、Redis、Go、Node 等,由可信方维护,文档相对齐全,适合作依赖服务或 Dockerfile 的 FROM 基座。 |
| 社区镜像 | 各命名空间下的第三方镜像,覆盖广;生产关键路径应结合维护情况自行评估,优先官方或自建/审计后的镜像。 |
| 版本(Tag) | 用标签区分版本或变体(如 mysql:8.0.36、redis:7-alpine)。latest 会变化,生产宜固定具体标签(见「最佳实践与注意点」)。 |
| 搜索 | 在 Hub 网页搜索;命令行可用 docker search(信息较简略,复杂筛选以网页为准)。 |
配置镜像加速(Registry Mirror)
从 Docker Hub 拉取镜像时,若网络较慢,可为 Docker Engine 配置 Registry Mirror:守护进程在拉取仍指向 Hub 的镜像时,优先经镜像站转发/缓存,通常能明显缩短 docker pull 时间。镜像站地址由各云厂商或单位提供,以对方当前文档为准,勿照搬过期示例 URL。
Docker Desktop(macOS / Windows)
- 打开 Settings(设置)→ Docker Engine。
- 在 JSON 中增加或合并
registry-mirrors数组,例如:
{
"registry-mirrors": [
"https://<按云厂商文档填写的加速域名>"
]
}- 点击 Apply & restart 使配置生效。
Linux(Docker Engine)
- 编辑或创建
/etc/docker/daemon.json(需 root),写入与上类似的 JSON。 - 重载并重启 Docker,例如:
sudo systemctl daemon-reload
sudo systemctl restart docker自检:执行 docker info,在输出中查看 Registry Mirrors 是否已列出所配地址。
说明:镜像加速不改变镜像名写法,仍为 mysql:8.0 等;仅影响 Engine 解析 Hub 请求时的实际访问路径。公司内网若统一出口,也可能改用内部 Registry 或 HTTP 代理,与 Mirror 二选一或组合使用,以运维规范为准。
最常用命令(日常操作)
拉取与查看镜像
docker images # 列出本地镜像
docker search nginx # 搜索镜像
docker pull mysql:8.0 # 默认自 Docker Hub 拉取(若已配置镜像加速则由 Engine 走加速)
docker images # 列出本地镜像配置了 Docker 镜像后,执行docker search mysql仍然是从https://index.docker.io搜索。
Docker 的镜像源配置(registry-mirrors)主要用于docker pull操作,会让拉取镜像时优先从配置的镜像源获取。
而docker > > search命令默认是向 Docker Hub 的公开 API 发起请求,查询镜像元数据,与镜像源配置无关。
运行容器
# 后台运行,命名,映射端口,设置环境变量
docker run -d \
--name mysql-dev \
-e MYSQL_ROOT_PASSWORD=secret \
-e MYSQL_DATABASE=appdb \
-p 3306:3306 \
mysql:8.0常用参数含义:
-d:detach,后台运行。--name:容器名,便于stop/rm/logs。-p 宿主机端口:容器端口:端口映射;本机访问localhost:宿主机端口即进入容器内对应端口。-e KEY=value:环境变量,官方镜像常以此传入密码、库名等。
前台交互运行示例(退出后容器默认停止,可加 --rm 自动删除):
docker run -it --rm alpine:3 sh列出、停止、删除
docker ps # 运行中的容器
docker ps -a # 含已停止
docker stop mysql-dev
docker rm mysql-dev
docker rmi mysql:8.0 # 删除本地镜像(无依赖容器时)日志与进入容器
docker logs mysql-dev
docker logs -f mysql-dev # 持续跟随
docker exec -it mysql-dev bash # 若镜像无 bash 可改用 shexec 在已运行的容器里启动新进程;与 docker run 新开容器不同。
镜像与容器数据
容器层与数据丢失
容器在镜像之上有一层可写容器层。在容器内写入文件系统(未挂载卷时)的数据,在删除容器后会随容器层一起丢弃。适合无状态进程;数据库、上传文件等需要持久化时要用卷或绑定挂载。
卷(Volume)与绑定挂载(Bind Mount)
- 命名卷(named volume):由 Docker 管理存储位置,适合「把数据交给 Docker 管」的场景。
docker run -d --name mysql-data \
-e MYSQL_ROOT_PASSWORD=secret \
-v mysql-data:/var/lib/mysql \
-p 3306:3306 \
mysql:8.0- 绑定挂载(bind mount):将宿主机某目录映射进容器,适合开发时挂载源码或明确路径的数据目录。
docker run -d --name dev-app \
-v "$(pwd)":/app \
-w /app \
golang:1.22 \
sleep infinity选用建议:生产或本地数据库持久化常用命名卷;本地调试代码、配置文件常用 bind mount。敏感数据勿提交到镜像层,优先环境变量或挂载只读配置(结合密钥管理更佳)。
Dockerfile 入门
Dockerfile 描述构建步骤;在包含该文件的目录执行 docker build 生成镜像。
常用指令(简表)
| 指令 | 作用 |
|---|---|
FROM | 基础镜像,必须是第一条有效指令(除解析器指令外)。 |
WORKDIR | 设置工作目录,后续 RUN/CMD 的默认路径。 |
COPY | 从构建上下文复制文件到镜像(推荐优先于 ADD)。 |
RUN | 构建时执行命令(安装依赖、编译等)。 |
ENV | 设置环境变量。 |
EXPOSE | 声明容器监听端口(文档性质,不自动发布到宿主机)。 |
CMD | 容器默认启动命令,可被 docker run 末尾参数覆盖。 |
ENTRYPOINT | 入口;与 CMD 组合时常用作固定可执行文件,CMD 只传参数。 |
最小示例:Node.js
以下用 Node.js 内置 http 模块 起一个最小 HTTP 服务(无额外 npm 依赖),演示 Dockerfile + 单文件 的构建与运行。目录中至少包含 server.js 与 Dockerfile。
server.js
const http = require('http');
const port = Number(process.env.PORT) || 3000;
http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8' });
res.end('Hello from Node.js in Docker\n');
}).listen(port, () => {
console.log(`Listening on http://0.0.0.0:${port}`);
});Dockerfile
FROM node:20-alpine
WORKDIR /app
COPY server.js .
ENV PORT=3000
EXPOSE 3000
CMD ["node", "server.js"]构建与运行:
docker build -t node-hello:1.0 .
docker run --rm -p 3000:3000 node-hello:1.0浏览器访问 http://localhost:3000 应看到输出文案。
说明
-t node-hello:1.0:指定镜像名与标签;.:构建上下文为当前目录(COPY只能复制上下文内的文件)。
常用 Dockerfile 指令详解
| 指令 | 用途描述与用法举例 |
|---|---|
FROM | 指定基础镜像;必须为首。例如:FROM golang:1.22 |
WORKDIR | 设置工作目录并自动创建,无则新建。例如:WORKDIR /app |
COPY | 拷贝文件/目录到镜像。COPY <源> <目标>,常用于复制源码、配置等。 |
ADD | 类似 COPY,但支持自动解压 tar 文件和远程 URL。一般不推荐,仅特殊需求用。 |
RUN | 构建阶段执行命令,如安装依赖/编译:RUN go mod download、RUN npm ci 等。 |
ENV | 设置环境变量。格式:ENV KEY=value,可被 RUN/CMD/ENTRYPOINT 使用,也可运行时覆盖。 |
EXPOSE | 声明容器监听端口,仅用于文档/提示,无实际端口映射作用。例:EXPOSE 3000 |
CMD | 容器启动时默认执行命令,可被 docker run <image> <cmd> 覆盖。例:CMD ["node", "server.js"] |
ENTRYPOINT | 配置容器主入口,常与 CMD 配合,CMD 作为参数。例:ENTRYPOINT ["./app"] |
ARG | 构建参数,仅 build 阶段有效。--build-arg 传递。例:ARG VERSION=latest |
USER | 指定执行用户(UID),默认 root。安全加固常用。 |
VOLUME | 声明数据卷挂载点,建议用于持久化数据。 |
LABEL | 添加元数据,便于追踪、自动化管理。例:LABEL maintainer="me@example.com" |
⚠️ 多数指令每步生成一层,合理顺序和合并可优化镜像体积与构建效率。
Dockerfile 优化技巧
1. 利用构建缓存
构建时,Docker 将每条 可缓存指令(如 RUN、COPY)的结果存为一层;若该指令及其之前所有层未变,则复用缓存,跳过重复执行。顺序与拆分直接影响速度:
- 把「变化频率低」的步骤放在前面:例如先只复制依赖描述文件并安装依赖,再复制易变的业务源码。Go 模块示例中先
COPY go.mod go.sum再RUN go mod download,最后COPY . .——这样只改代码时不必反复下载模块。 - 避免无谓的缓存失效:同一层里任意文件变化都会使该层及之后层失效;不要把「安装依赖」与「复制整个项目」写在同一行
COPY里,除非刻意为之。 - 合并
RUN与缓存的权衡:多条RUN会产生多层;用&&合并为一条可减少层数与镜像碎片,但整行任一命令变化都会使该层整段重建。依赖安装层与编译层可分开,便于单独命中缓存。
对比示例(Go 单阶段构建)
不推荐:先整仓 COPY,再下载模块并编译。任意源码改动都会使 COPY . . 这一层失效,其后的 RUN 全部重新执行,go mod download 每次都会再跑一遍。
FROM golang:1.22
WORKDIR /src
COPY . .
RUN go mod download && CGO_ENABLED=0 go build -o /out/app ./cmd/server推荐:先只复制模块锁文件,固定「依赖层」;源码变更时仅最后一条 RUN go build 重建。
FROM golang:1.22
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -o /out/app ./cmd/serverNode 项目同理:先复制 package.json / lock,再 npm ci 或 npm install,最后 COPY . . 与构建,避免改一行业务代码就重装全部依赖。
FROM node:20-alpine
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build合并 RUN 的典型写法(整段作为一层,适合系统包安装;改包列表会整层重建):
RUN apt-get update \
&& apt-get install -y --no-install-recommends ca-certificates \
&& rm -rf /var/lib/apt/lists/*显式不使用缓存时可用 docker build --no-cache 调试;日常开发依赖默认缓存即可。
2. 多阶段构建减小镜像体积
多阶段构建在 Dockerfile 中写多个 FROM,前一阶段用于编译、装工具,最终镜像用最后一个 FROM,只 COPY --from=上一阶段 拷贝二进制或静态资源。这样发布镜像里不包含 Go 编译器、go mod 缓存等,体积可从数百 MB 降到数十 MB 以内,攻击面也更小。
示意(具体 CGO、入口包路径按项目调整):
FROM golang:1.22 AS builder
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -o /out/app ./cmd/server
FROM gcr.io/distroless/static-debian12:nonroot
COPY --from=builder /out/app /app
USER nonroot:nonroot
ENTRYPOINT ["/app"]与上一节「构建缓存」结合:go mod download 仅在 go.mod / go.sum 变化时重建;业务代码变更时一般只重跑最后的 go build 阶段。
3. 使用 .dockerignore
构建上下文是 docker build 末尾传入目录下的所有文件(默认包含子目录)。Docker 会先把上下文打包发给守护进程;未忽略的大目录会拖慢传输、撑大缓存键,且易让无关文件参与 COPY,导致缓存频繁失效。
在项目根目录添加 .dockerignore,语法与 .gitignore 类似,排除不需要进镜像或不必参与构建的文件,例如:
.git
.gitignore
.idea
.vscode
*.md
**/tmp
**/testdata
node_modules
dist
bin按需增减:若 COPY . . 会误排除业务文件,应收窄规则。敏感文件(密钥、.env)不要打进镜像,除 .dockerignore 外仍应避免出现在构建上下文中或配合 CI 密钥注入。
网络入门
- 默认 bridge:
docker run -p将宿主机端口转发到容器端口;从宿主机访问用localhost:宿主机端口。同一默认 bridge 上的容器可通过容器 IP 互访,但 IP 易变,不推荐硬编码。 - 用户自定义网络:同一网络上的容器可通过容器名作为 DNS 名称互访,更适合多容器协作。
docker network create app-net
docker run -d --name api --network app-net myapi:1.0
docker run -d --name mysql --network app-net -e MYSQL_ROOT_PASSWORD=secret mysql:8.0
# 在 api 容器内可用主机名 mysql 访问数据库端口注意:localhost 在容器内指容器自己,不是宿主机或其他容器;跨主机访问需端口映射或服务发现等机制。
组合多容器:Compose 入门
当依赖多个服务(应用 + 数据库 + Redis 等),逐条 docker run 易乱。Docker Compose 用一份 YAML 描述多服务,一条命令启停整个栈(V2 起推荐 docker compose,旧独立二进制为 docker-compose)。
示例 compose.yml(示意):
services:
db:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: secret
MYSQL_DATABASE: appdb
ports:
- "3306:3306"
volumes:
- db-data:/var/lib/mysql
web:
image: myweb:1.0
ports:
- "8080:8080"
depends_on:
- db
environment:
# Go 中常见 DSN 形式,与 7.8 / 7.6 一致;实际键名以应用读取为准
DATABASE_DSN: "root:secret@tcp(db:3306)/appdb?charset=utf8mb4&parseTime=True&loc=Local"
volumes:
db-data:常用命令:
docker compose up -d # 后台启动
docker compose ps
docker compose logs -f
docker compose down # 停止并删除容器;默认不删 volume,可加 -v 慎用与单条 docker run 的取舍:临时试镜像用 run 即可;项目级多服务、统一环境变量与卷名,用 Compose 更清晰。
最佳实践与注意点
- 镜像标签:生产尽量固定版本(如
mysql:8.0.36),避免长期依赖无保证的latest。 - 镜像加速地址:
registry-mirrors中的 URL 以云厂商或单位当前文档为准,失效后及时更换;勿在文档或仓库中硬编码他人私有加速域名。 - 敏感信息:密码、密钥优先环境变量、密钥文件挂载或密钥管理服务,避免
Dockerfile里写死导致进入镜像层历史。 .dockerignore:与「Dockerfile 优化技巧」第 3 点一致,排除无关文件,减小上下文并利于缓存命中。- 资源限制(共享宿主机时):可用
docker run --memory=512m --cpus=1等防止单个容器占满机器(生产编排常在 Kubernetes 等层配置)。 - 非 root:运行服务镜像尽量使用非特权用户(如上文 distroless 示例),降低容器逃逸后的影响面。
小结
本章介绍了 Docker 的镜像与容器、安装与环境检查(docker version / docker info)、镜像仓库(Docker Hub、命名规则、Registry Mirror 镜像加速)、run/ps/stop/logs/exec 等常用命令、卷与绑定挂载、Dockerfile 基本指令与优化技巧(构建缓存、多阶段构建、.dockerignore)、自定义网络与 Compose 入门,以及标签、密钥等实践要点。可自行对照下列清单自检:
- 理解镜像仓库与 Docker Hub 的作用,知道无仓库前缀时
docker pull默认解析到 Hub;能说明 镜像加速 的配置位置,并用docker info核对 Registry Mirrors。 - 能区分 官方镜像 与 社区镜像 的使用风险,生产优先固定 Tag。
- 能拉取镜像并成功
docker run,理解-p、-e、-d、--name。 - 知道容器删除与数据丢失的关系,会用
-v命名卷或 bind mount。 - 能编写最小 Dockerfile 并完成
docker build/docker run。 - 能安排 Dockerfile 指令顺序以利用缓存,理解多阶段构建缩小最终镜像的作用,会编写
.dockerignore。 - 理解容器内
localhost的含义,知道用 网络 + 服务名 做多容器互联。 - 能读懂简单的
compose.yml并用docker compose up启动栈。