Skip to content

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 Enginedocker.io 或官方仓库包),服务端与 CI 常见。

具体安装步骤随发行版与版本变化,以 Docker 官方安装文档 为准。

自检命令

bash
docker version    # 客户端与服务端版本
docker info       # Engine 信息、存储驱动、镜像加速等

docker versionServer 段报错,说明本机 Engine 未启动或未正确连接(Desktop 未运行、Linux 上服务未启动等)。

Linux 权限说明

默认只有 rootdocker 用户组成员可访问 Docker 套接字。将当前用户加入组后需重新登录生效:

bash
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.0library 为官方库默认命名空间)。
用户/组织命名空间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(若配置了镜像加速,则由守护进程按策略走加速地址)。
官方镜像NginxMySQLRedisGoNode 等,由可信方维护,文档相对齐全,适合作依赖服务或 DockerfileFROM 基座。
社区镜像各命名空间下的第三方镜像,覆盖广;生产关键路径应结合维护情况自行评估,优先官方或自建/审计后的镜像。
版本(Tag)用标签区分版本或变体(如 mysql:8.0.36redis:7-alpine)。latest 会变化,生产宜固定具体标签(见「最佳实践与注意点」)。
搜索在 Hub 网页搜索;命令行可用 docker search(信息较简略,复杂筛选以网页为准)。

配置镜像加速(Registry Mirror)

从 Docker Hub 拉取镜像时,若网络较慢,可为 Docker Engine 配置 Registry Mirror:守护进程在拉取仍指向 Hub 的镜像时,优先经镜像站转发/缓存,通常能明显缩短 docker pull 时间。镜像站地址由各云厂商或单位提供,以对方当前文档为准,勿照搬过期示例 URL。

Docker Desktop(macOS / Windows)

  1. 打开 Settings(设置)→ Docker Engine
  2. 在 JSON 中增加或合并 registry-mirrors 数组,例如:
json
{
  "registry-mirrors": [
    "https://<按云厂商文档填写的加速域名>"
  ]
}
  1. 点击 Apply & restart 使配置生效。

Linux(Docker Engine)

  1. 编辑或创建 /etc/docker/daemon.json(需 root),写入与上类似的 JSON。
  2. 重载并重启 Docker,例如:
bash
sudo systemctl daemon-reload
sudo systemctl restart docker

自检:执行 docker info,在输出中查看 Registry Mirrors 是否已列出所配地址。

说明:镜像加速不改变镜像名写法,仍为 mysql:8.0 等;仅影响 Engine 解析 Hub 请求时的实际访问路径。公司内网若统一出口,也可能改用内部 Registry 或 HTTP 代理,与 Mirror 二选一或组合使用,以运维规范为准。

最常用命令(日常操作)

拉取与查看镜像

bash
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 发起请求,查询镜像元数据,与镜像源配置无关。

运行容器

bash
# 后台运行,命名,映射端口,设置环境变量
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 自动删除):

bash
docker run -it --rm alpine:3 sh

列出、停止、删除

bash
docker ps          # 运行中的容器
docker ps -a       # 含已停止
docker stop mysql-dev
docker rm mysql-dev
docker rmi mysql:8.0   # 删除本地镜像(无依赖容器时)

日志与进入容器

bash
docker logs mysql-dev
docker logs -f mysql-dev    # 持续跟随

docker exec -it mysql-dev bash   # 若镜像无 bash 可改用 sh

exec已运行的容器里启动新进程;与 docker run 新开容器不同。

镜像与容器数据

容器层与数据丢失

容器在镜像之上有一层可写容器层。在容器内写入文件系统(未挂载卷时)的数据,在删除容器后会随容器层一起丢弃。适合无状态进程;数据库、上传文件等需要持久化时要用卷或绑定挂载。

卷(Volume)与绑定挂载(Bind Mount)

  • 命名卷(named volume):由 Docker 管理存储位置,适合「把数据交给 Docker 管」的场景。
bash
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):将宿主机某目录映射进容器,适合开发时挂载源码或明确路径的数据目录。
bash
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.jsDockerfile

server.js

javascript
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

dockerfile
FROM node:20-alpine
WORKDIR /app
COPY server.js .
ENV PORT=3000
EXPOSE 3000
CMD ["node", "server.js"]

构建与运行:

bash
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 downloadRUN 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 将每条 可缓存指令(如 RUNCOPY)的结果存为一层;若该指令及其之前所有层未变,则复用缓存,跳过重复执行。顺序与拆分直接影响速度:

  • 把「变化频率低」的步骤放在前面:例如先只复制依赖描述文件并安装依赖,再复制易变的业务源码。Go 模块示例中先 COPY go.mod go.sumRUN go mod download,最后 COPY . .——这样只改代码时不必反复下载模块。
  • 避免无谓的缓存失效:同一层里任意文件变化都会使该层及之后层失效;不要把「安装依赖」与「复制整个项目」写在同一行 COPY 里,除非刻意为之。
  • 合并 RUN 与缓存的权衡:多条 RUN 会产生多层;用 && 合并为一条可减少层数与镜像碎片,但整行任一命令变化都会使该层整段重建。依赖安装层与编译层可分开,便于单独命中缓存。

对比示例(Go 单阶段构建)

不推荐:先整仓 COPY,再下载模块并编译。任意源码改动都会使 COPY . . 这一层失效,其后的 RUN 全部重新执行go mod download 每次都会再跑一遍。

dockerfile
FROM golang:1.22
WORKDIR /src
COPY . .
RUN go mod download && CGO_ENABLED=0 go build -o /out/app ./cmd/server

推荐:先只复制模块锁文件,固定「依赖层」;源码变更时仅最后一条 RUN go build 重建。

dockerfile
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/server

Node 项目同理:先复制 package.json / lock,再 npm cinpm install,最后 COPY . . 与构建,避免改一行业务代码就重装全部依赖。

dockerfile
FROM node:20-alpine
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build

合并 RUN 的典型写法(整段作为一层,适合系统包安装;改包列表会整层重建):

dockerfile
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、入口包路径按项目调整):

dockerfile
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 类似,排除不需要进镜像或不必参与构建的文件,例如:

text
.git
.gitignore
.idea
.vscode
*.md
**/tmp
**/testdata
node_modules
dist
bin

按需增减:若 COPY . . 会误排除业务文件,应收窄规则。敏感文件(密钥、.env不要打进镜像,除 .dockerignore 外仍应避免出现在构建上下文中或配合 CI 密钥注入。

网络入门

  • 默认 bridgedocker run -p 将宿主机端口转发到容器端口;从宿主机访问用 localhost:宿主机端口。同一默认 bridge 上的容器可通过容器 IP 互访,但 IP 易变,不推荐硬编码。
  • 用户自定义网络:同一网络上的容器可通过容器名作为 DNS 名称互访,更适合多容器协作。
bash
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(示意):

yaml
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:

常用命令:

bash
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 启动栈。

参考资料