Docker技术入门指南
1. Docker简介
Docker 把应用和依赖打进可移植的镜像,再在隔离的容器里跑。和虚拟机比,它不带完整客户机内核,启动快、占盘少;隔离靠 Linux 的 namespace、cgroup 和联合文件系统,不是再开一台 OS。
日常开发里,它解决三件事:环境一致(我机器能跑、你机器也能跑)、依赖收拢(不再污染宿主机)、多服务并排(MySQL、Redis、Nginx 各一个容器)。
1.1 核心概念
| 名词 | 一句话 | 类比 |
|---|---|---|
| 镜像 Image | 只读分层模板,含文件系统和启动命令 | 安装盘 / 类 |
| 容器 Container | 镜像加上可写层后的一次运行实例 | 装好并开机的那台 |
| 仓库 Registry | 存镜像、按名字和标签取 | 应用商店 |
| 数据卷 Volume | 容器删了还能留下的数据 | 外接盘 |
| 网络 Network | 容器之间、容器和宿主机怎么连通 | 网段 / 交换机 |
镜像用 名字:标签 标识,例如 nginx:1.27。不写标签等于 latest,生产里不要依赖这个浮动标签。
1.2 技术栈与架构
你敲的 docker 只是客户端。真正干活的是一条栈:
docker / docker compose 客户端,说话
│ Unix socket 或 TCP
dockerd(Docker Engine) API、编排镜像/容器/网络/卷
│
containerd 管容器生命周期
│
runc 按 OCI 规范把进程拉起来
│
Linux 内核 namespace / cgroup / overlayfs
安装包里的 docker-ce 是引擎,docker-ce-cli 是命令行,containerd.io 是运行时,docker-compose-plugin 提供 docker compose。缺哪一块,对应层就罢工。
再往上,不是引擎本身,但是同一套工作流里会碰到:
- Dockerfile + BuildKit:从源码做出镜像。
- Registry:Docker Hub、Harbor、阿里云 ACR,负责分发。
- Compose:一份 YAML 拉起多个容器,本机和单机部署够用。
- 编排:多机、滚动发布、自愈一般交给 Kubernetes;Swarm 还在,新项目很少首选。
对象之间的关系可以记成:Dockerfile 构建出镜像 → 镜像推到仓库 → 仓库拉下来 run 成容器 → 容器挂卷、进网络。后面几节按这个顺序走。
2. 安装Docker
Linux 用官方仓库装 CE,不要用十年前的 docker.io 包凑合。macOS / Windows 用 Docker Desktop,底层仍是 Linux 虚拟机 + 同一套引擎。国内机器拉官方 GPG 慢时,可改用发行版文档里的镜像站,命令结构不变。
2.1 Ubuntu安装
sudo apt-get update
sudo apt-get install -y ca-certificates curl gnupg
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg \
| sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo $VERSION_CODENAME) stable" \
| sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
2.2 CentOS / 类 RHEL
sudo yum install -y yum-utils
sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
sudo yum install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
Rocky / Alma 同样走这套仓库。装完必须启动守护进程,见下一小节。
2.3 装完立刻做的三件事
sudo systemctl enable --now docker
sudo docker version
sudo docker run --rm hello-world
docker version 能同时看到 Client 和 Server,只有一边有字,说明守护进程没起来。把当前用户加进 docker 组后,才可以去掉 sudo,步骤见 将用户添加到 Docker 用户组。能进这个组等于能控宿主机,只给可信账号。
3. Docker基本操作
3.1 服务管理
sudo systemctl start docker
sudo systemctl enable docker
sudo systemctl status docker
sudo systemctl restart docker
配置在 /etc/docker/daemon.json(镜像加速、日志驱动、insecure-registries)。改完要 restart docker。
3.2 镜像操作
docker search nginx
docker pull nginx:1.27
docker images
docker image ls
docker image inspect nginx:1.27
docker image history nginx:1.27
docker tag nginx:1.27 myregistry.example.com/web/nginx:1.27
docker rmi nginx:1.27
pull 默认走 Docker Hub。国内常要配镜像加速,或直接从阿里云等仓库拉,见 推送到阿里云镜像仓库。
3.3 镜像导出和导入
内网机器上不了 Hub、机房间搬家、给别人一个 U 盘离线包,都用这一对命令。搬镜像用 save / load,分层和标签都在;不要和容器的 export / import 搞混。
# 导出:一个或多个镜像打成 tar,层还在
docker save -o nginx.tar nginx:1.27
docker save nginx:1.27 redis:7 | gzip > stack.tar.gz
# 导入到另一台(先 scp / rsync 把文件拷过去)
docker load -i nginx.tar
gunzip -c stack.tar.gz | docker load
# 看导入后有没有标签
docker images
另一台执行 load 后,名字和标签与导出时一致,可以直接 docker run。文件可以很大,传输用 rsync 或分卷压缩。
容器文件系统另有一对命令,用途不同:
# 把「正在跑 / 已停止的容器」拍成一层根文件系统,丢掉构建历史
docker export -o nginx-fs.tar mynginx
docker import nginx-fs.tar myapp:from-container
save / load | export / import | |
|---|---|---|
| 对象 | 镜像 | 容器的文件系统快照 |
| 分层与历史 | 保留 | 拍扁成一层 |
| 标签 | 一起带走 | 要在 import 时自己起名 |
| 典型用途 | 离线分发、备份、换机 | 从脏容器抠一份根盘,很少当常规发布 |
日常说「导出镜像」,指 save。export 出来的包不能当可复现构建,也没有 Dockerfile 里那些层缓存。
3.4 容器操作
docker run -d --name mynginx --restart unless-stopped -p 8080:80 nginx:1.27
docker ps
docker ps -a
docker logs -f --tail 100 mynginx
docker exec -it mynginx sh
docker stop mynginx
docker start mynginx
docker restart mynginx
docker rm mynginx
docker rm -f mynginx
常用 run 参数:-d 后台,--name 好记的名字,-p 宿主:容器 端口,-e 环境变量,-v 挂载,--network 指定网络,--memory / --cpus 限额,--restart 重启策略(no / on-failure / always / unless-stopped)。
看资源:docker stats。拷文件:docker cp ./index.html mynginx:/usr/share/nginx/html/。容器是无状态的,改里面的文件不提交的话,删容器就没了;要固化应写回 Dockerfile 或挂卷。
4. 用 Dockerfile 构建镜像
自己的应用不要只 docker commit。用文本把构建写死,别人才能复现。
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8000
CMD ["python", "-m", "http.server", "8000"]
# 同目录放 .dockerignore,把 .git、.venv、__pycache__ 排除掉
docker build -t myapp:0.1 .
docker run --rm -p 8000:8000 myapp:0.1
要点:基础镜像尽量小且钉死版本;能合并的 RUN 合并,善用层缓存(依赖文件先于源码 COPY);多阶段构建把编译器和源码留在 builder,最终镜像只留运行文件。完整例子见 构建自定义 JDK 镜像。
5. Docker网络
| 驱动 | 什么时候用 |
|---|---|
| bridge | 默认。单机多个容器互访,再 -p 暴露到宿主 |
| host | 容器直接用宿主网络栈,少一层 NAT,隔离也少一层 |
| none | 没有网卡,只跑本地计算 |
| macvlan | 给容器独立 MAC,出现在物理网段上(要交换机配合) |
| overlay | 多主机叠加网,Swarm / 部分编排会用 |
docker network create appnet
docker run -d --name web --network appnet nginx:1.27
docker run -d --name api --network appnet myapp:0.1
docker network inspect appnet
同一自定义网络里,容器可以用名字当主机名(http://web)。不要都挤在默认 bridge 上只靠 -p 绕一圈。Compose 会自动建项目网络,见第 7 节。
6. 数据管理
容器可写层一删就没。数据库、上传文件、配置必须放到卷里。
| 方式 | 写法 | 适合 |
|---|---|---|
| 命名卷 | -v mydata:/var/lib/mysql | 数据库等「只要持久、不管落在哪」 |
| 绑定挂载 | -v /opt/site:/usr/share/nginx/html | 开发时改宿主文件立刻生效 |
| tmpfs | --tmpfs /tmp | 敏感临时文件,不落盘 |
docker volume create mydata
docker volume ls
docker volume inspect mydata
docker run -d --name db -v mydata:/var/lib/mysql mysql:8.0
docker run -d --name web -v /srv/www:/usr/share/nginx/html nginx:1.27
命名卷在 Linux 上一般在 /var/lib/docker/volumes/。备份卷可以再开一个容器把目录打 tar,或停库后拷宿主目录。MySQL / Redis 的卷怎么挂,见 快速部署 MySQL 和 Redis。
7. Docker Compose
两个以上容器、还要共用网络和卷,就不要手敲一串 docker run。Compose V2 是插件,命令是 docker compose(中间有空格)。YAML 里不必再写已废弃的 version:。
services:
web:
image: nginx:1.27
ports:
- "8080:80"
volumes:
- ./html:/usr/share/nginx/html
depends_on:
- api
api:
build: ./api
environment:
DATABASE_URL: mysql://root:secret@db:3306/app
db:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: secret
volumes:
- dbdata:/var/lib/mysql
volumes:
dbdata:
docker compose up -d
docker compose ps
docker compose logs -f api
docker compose down
语法和排障见 Compose 基础使用;PHP 整套栈见 Compose 部署 PHP 环境。
8. 镜像仓库
本机 docker images 只是缓存。团队共享、CI 构建产物、生产拉取,都要经过 Registry:docker login → docker tag → docker push,另一边 docker pull。
地址长这样:registry.example.com/项目/镜像:标签。阿里云 ACR 的登录和推送步骤在 推送镜像到阿里云。离线、不能打通仓库时,退回第 3.3 节的 save / load。
9. 最佳实践
- 安全:守护进程默认仍是 root。加入
docker组不是「降权」,是把等效 root 交给该用户。镜像选官方或自建小镜像,跑业务尽量在 Dockerfile 里USER成非 root。别把密钥写进镜像层,用环境变量、文件挂载或后期的 secrets。 - 构建:写
.dockerignore;钉死基础镜像版本;多阶段构建减小体积;CI 里打开 BuildKit(DOCKER_BUILDKIT=1)。 - 运行:生产容器加
--restart和资源上限;日志用json-file并限制大小,或交给 journald;一个容器一个主进程。 - 数据:库和用户上传走命名卷;源码热重载才用绑定挂载。
- 清理:定期
docker system df,确认后docker system prune。悬空镜像、停掉的容器、不用的网络会默默吃盘。
10. 常见问题排查
容器立刻退出:前台进程结束了。看 docker logs;docker inspect 里的 State.ExitCode 和 Error。
docker logs --tail 200 mynginx
docker inspect mynginx --format '{{.State.Status}} {{.State.ExitCode}} {{.State.Error}}'
磁盘被吃满:
docker system df
docker system prune
docker volume prune # 会删未挂载的卷,先确认没有要留的库
网络不通:是否在同一 docker network;-p 是否写反;宿主机防火墙是否放行;docker exec 进去做连通性检查。
docker network inspect appnet
docker exec -it mynginx sh
权限 / 连不上 daemon:Cannot connect to the Docker daemon 多半是服务没起,或当前用户不在 docker 组且没用 sudo。