Docker 教程:从镜像容器概念到 Dockerfile 与 compose 实战
“我这儿能跑啊”——部署最老的敌人就是环境差异:你机器上有的依赖、库版本、系统配置,服务器上不一定有。Docker 的解法是把程序连同它的整个运行环境(系统库、依赖、配置)打包成一个可移植的”盒子”,在哪台机器上跑,环境都一模一样。
Docker 是什么:三个核心概念
Docker 是容器引擎——用比虚拟机轻得多的方式隔离和运行程序。上手前必须分清三个概念,绝大多数新手困惑都源于把前两个混为一谈:
- 镜像(image):只读的模板,包含程序和它的完整运行环境——类比面向对象里的”类”
- 容器(container):镜像跑起来的实例——类比”对象”,同一个镜像可以同时跑出多个互不干扰的容器
- 仓库(registry):集中存放镜像的服务,默认是 Docker Hub,
docker pull从这里拉镜像
flowchart LR
D[Dockerfile] -->|docker build| I[镜像 image]
I -->|docker run| C1[容器 1]
I -->|docker run| C2[容器 2]
R[(仓库 registry)] -->|docker pull| I
I -->|docker push| R
安装:一条命令
1 | curl -fsSL https://get.docker.com | sh |
macOS/Windows 用 Docker Desktop 图形化安装(macOS 上轻量替代 OrbStack 也很流行),装完命令行用法完全一致。
docker run:参数拆解
跑一个 nginx,把最常用的参数一次讲清:
1 | docker run -d -p 8080:80 --name web nginx |
容器生命周期:从跑起来到删干净
1 | docker ps |
一个必须建立的认知:容器不是轻量虚拟机,它的生命周期跟着主进程走——docker run 启动时指定(或镜像默认)的那个进程退出了,容器就停了。跑个 docker run ubuntu 发现容器”秒退”,不是坏了,是它的默认命令执行完就结束了。
数据坑:容器删了,数据跟着没了
容器内部写的文件属于容器自己的可写层,docker rm 之后整层蒸发。数据库这类有状态的服务,必须把数据目录挂载到宿主机:
1 | docker run -d --name pg \ |
判断标准很简单:这个容器产生的数据丢了心不心疼?心疼就必须 -v 挂出来。
镜像管理与 tag
1 | docker images |
tag 有个约定俗成的坑:不写 tag 默认拉 latest,但 latest 只是个普通标签,指向的版本会随时间漂移——今天构建和明天构建拉到的可能不是同一个东西。生产环境永远写死具体版本号,postgres:17 而不是 postgres:latest。
Dockerfile:把自己的 Go 服务打成镜像
Dockerfile 是构建镜像的”配方”。Go 程序编译成单个静态二进制,特别适合多阶段构建——编译在带完整工具链的大镜像里做,运行只带二进制本身:
1 | # 阶段一:编译。golang 官方镜像自带完整编译工具链,体积几百 MB(Debian 底座的接近 1 GB),但只在构建时用 |
1 | docker build -t myapp:1.0 . |
多阶段构建的效果立竿见影:单阶段直接用 golang 镜像跑,成品接近 1 GB;两阶段下来通常只有十几 MB。
为什么差距这么大?关键在于镜像是一层层叠出来的,最终镜像 = 基础镜像的所有层 + 你新加的层,叠上去的东西删不掉。单阶段构建时基础镜像是 golang——里面装着 Go 编译器、标准库、git、gcc 这一整套构建工具(Debian 底座解压后就有 800 MB 上下),再叠上 go mod download 拉下来的依赖缓存和你的源码,成品轻松逼近 1 GB。可服务跑起来真正需要的只有那个十几 MB 的静态二进制,剩下全是”编译完就没用了”的东西。多阶段构建的本质就是把这些甩掉:FROM alpine 另起一个只有几 MB 的干净底座,只把二进制 COPY 进去——builder 阶段的所有层都不会进入最终镜像。
docker compose:多容器一把梭
服务一多(web + 数据库 + 缓存),逐个 docker run 又长又难维护。compose 把整套服务声明在一个 compose.yaml 里:
1 | services: |
1 | docker compose up -d |
compose 文件里服务名(如 redis)自动成为容器间互相访问的主机名——web 里连 redis:6379 就通,不用关心容器 IP。
排查与查看:network、inspect 与 docker info
容器出问题时光看 logs 不够,还得能看它的网络接在哪、完整配置是什么、引擎本身的状态如何。这几个都是只读的查看类命令,放心随便敲:
1 | docker network ls |
常用命令速查表
| 命令 | 作用 |
|---|---|
docker run -d 镜像 |
后台启动容器 |
docker run -p 宿主:容器 |
端口映射,冒号左边是宿主机 |
docker run -v 宿主:容器 |
目录挂载,有状态数据必挂 |
docker run -e KEY=VAL |
设置容器内环境变量 |
docker run --name 名字 |
给容器命名 |
docker run --rm |
退出后自动删除,临时容器用 |
docker run -it 镜像 sh |
交互式进入容器 shell |
docker ps / ps -a |
运行中的容器 / 含已退出的 |
docker logs -f 容器 |
实时跟踪容器日志 |
docker exec -it 容器 sh |
进入运行中的容器 |
docker stop / rm |
停止 / 删除容器 |
docker images |
本地镜像列表 |
docker pull 镜像:tag |
拉取镜像,生产写死具体 tag |
docker rmi 镜像 |
删除本地镜像 |
docker build -t 名:tag . |
按 Dockerfile 构建镜像 |
docker network ls |
列出网络 |
docker network inspect 网络 |
网络详情:网段、网关、容器 IP |
docker inspect 容器/镜像 |
完整配置 JSON |
docker inspect -f 模板 |
只取 JSON 里指定的字段 |
docker port 容器 |
只看端口映射 |
docker info |
引擎配置与状态 |
docker stats |
实时资源占用,容器版 top |
docker system df |
镜像/容器/数据卷磁盘占用 |
docker compose up -d |
按 compose.yaml 拉起整套服务 |
docker compose down |
整套停掉并删容器 |
docker compose logs -f |
聚合跟踪所有服务日志 |
写到这里
Docker 上手的关键是概念先行:镜像是模板、容器是实例、容器生死跟着主进程走、心疼的数据必须挂载出来。这四句话立住之后,命令都只是查表的事。Dockerfile 多阶段构建和 compose 是从”会用”到”用好”的两步——前者让镜像从 GB 瘦到 MB,后者让多服务环境一条命令起停。再往深走就是网络模型和编排(Kubernetes)的领域了,日常开发部署,本文这套已经够用。









