Docker是什么

Docker 是一个用于开发、交付和运行应用程序的容器平台。它可以把程序、运行时和依赖打包成镜像,再从镜像启动相对隔离的容器,从而减少“在我电脑上明明可以运行”的环境差异。

容器不是简化版虚拟机。虚拟机通常包含完整操作系统和独立内核,而容器更接近一个被隔离的进程:它有自己的文件系统、网络和进程空间,但通常与其他容器共享宿主机内核。因此容器启动更快、占用资源更少,不过隔离边界和虚拟机也不完全相同。

Docker 主要解决以下问题:

  • 固定应用及依赖版本,让开发、测试和生产环境尽量一致;
  • 快速创建和销毁隔离环境;
  • 使用镜像仓库分发应用;
  • 通过 Docker Compose 管理由多个容器组成的服务。

Docker的架构

docker-architecture.png

Docker 使用客户端—服务端架构:

  • Docker Client:也就是平时使用的 docker 命令;
  • Docker Daemon:后台的 dockerd,负责构建镜像、运行容器以及管理网络和存储;
  • Registry:保存镜像的仓库,例如 Docker Hub;
  • Docker Compose:使用 YAML 文件描述和管理一组相关容器。

执行 docker run nginx 时,客户端会向 Daemon 发出请求。如果本地没有 nginx 镜像,Daemon 先从镜像仓库拉取它,再创建并启动容器。

基本概念

镜像与容器

镜像(Image)是创建容器的只读模板,容器(Container)是镜像的运行实例。同一个镜像可以启动多个互不影响的容器。

可以将镜像理解为程序安装包加运行环境,但容器运行时会额外增加一个可写层。删除容器时,这个可写层中的数据也会消失,因此数据库和用户文件需要放在 Volume 或挂载目录中。

Volume 与 Bind Mount

  • Volume 由 Docker 管理,适合数据库等需要长期保存的数据;
  • Bind Mount 把宿主机指定目录映射到容器,适合开发时同步源码和配置文件。

Network

容器默认运行在隔离网络中。端口映射让宿主机能够访问容器服务,自定义网络则允许多个容器通过容器名互相访问。

安装与验证

Windows 和 macOS 可以安装 Docker Desktop,Linux 建议按照对应发行版的 Docker Engine 官方安装说明 操作。不要长期沿用来源不明的一键安装脚本。

安装完成后检查版本:

1
2
docker version
docker compose version

运行官方测试容器:

1
docker run --rm hello-world

--rm 表示容器停止后自动删除。能够看到 Hello from Docker 的输出,就说明客户端、Daemon 和镜像拉取均正常。

运行第一个服务

下面使用 Nginx 启动一个 Web 服务:

1
2
3
4
docker run -d \
--name my-nginx \
-p 8080:80 \
nginx:alpine

参数含义:

  • -d:后台运行;
  • --name my-nginx:指定容器名称;
  • -p 8080:80:把宿主机的 8080 端口映射到容器的 80 端口;
  • nginx:alpine:使用 Nginx 的 Alpine 镜像标签。

打开 http://localhost:8080 即可看到 Nginx 页面。查看容器和日志:

1
2
docker ps
docker logs -f my-nginx

停止、再次启动和删除容器:

1
2
3
docker stop my-nginx
docker start my-nginx
docker rm -f my-nginx

常用命令

镜像

1
2
3
4
docker pull nginx:alpine       # 拉取镜像
docker images # 查看本地镜像
docker image inspect nginx:alpine
docker rmi nginx:alpine # 删除未使用的镜像

容器

1
2
3
4
5
6
docker ps                      # 查看运行中的容器
docker ps -a # 查看全部容器
docker inspect my-nginx # 查看容器详细信息
docker stats # 实时查看资源占用
docker exec -it my-nginx sh # 进入运行中的容器
docker cp my-nginx:/etc/nginx/nginx.conf ./nginx.conf

容器的主进程结束后,容器也会停止。如果容器一启动就退出,可以先使用 docker ps -a 找到它,再用 docker logs 容器名 查看原因。

环境变量与端口

很多镜像使用环境变量接收配置:

1
2
3
4
docker run --rm \
-e APP_ENV=production \
-e TZ=Asia/Shanghai \
my-app:latest

端口写法是 宿主机端口:容器端口:

1
docker run -d -p 127.0.0.1:8080:80 nginx:alpine

上面的服务只监听宿主机回环地址。如果写成 -p 8080:80,可能会向局域网或公网暴露端口,具体取决于宿主机防火墙和网络环境。

数据持久化

使用 Volume

1
2
3
4
5
6
7
8
9
docker volume create app-data

docker run -d \
--name app \
-v app-data:/var/lib/app \
my-app:latest

docker volume ls
docker volume inspect app-data

删除容器不会自动删除命名 Volume,因此重新创建容器后仍可以挂载原来的数据。

挂载宿主机目录

1
2
3
4
5
docker run -d \
--name static-site \
-p 8080:80 \
--mount type=bind,source="$(pwd)/html",target=/usr/share/nginx/html,readonly \
nginx:alpine

Windows PowerShell 需要把 $(pwd)/html 换成实际目录,例如 D:/Web/html。只读配置或静态文件建议加上 readonly,避免容器意外修改宿主机文件。

使用 Dockerfile 构建镜像

假设当前目录有一个 Hexo 生成的 public 文件夹,可以编写以下 Dockerfile:

1
2
3
4
5
FROM nginx:alpine

COPY public/ /usr/share/nginx/html/

EXPOSE 80

构建并运行:

1
2
docker build -t my-blog:1.0 .
docker run -d --name my-blog -p 8080:80 my-blog:1.0

建议同时创建 .dockerignore,避免把无关内容发送给构建器:

1
2
3
4
.git
node_modules
.deploy_git
*.log

镜像标签最好使用明确版本,例如 my-blog:1.0,不要在生产环境只依赖会变化的 latest。

使用 Docker Compose

当项目包含 Web、数据库、缓存等多个服务时,逐个执行 docker run 会比较麻烦。可以使用 compose.yaml 统一描述:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
services:
web:
image: nginx:alpine
ports:
- "8080:80"
volumes:
- ./html:/usr/share/nginx/html:ro
restart: unless-stopped

redis:
image: redis:7-alpine
volumes:
- redis-data:/data
restart: unless-stopped

volumes:
redis-data:

常用 Compose 命令:

1
2
3
4
5
6
docker compose up -d              # 创建并启动服务
docker compose ps # 查看服务状态
docker compose logs -f web # 跟踪指定服务日志
docker compose pull # 拉取更新后的镜像
docker compose up -d # 按新配置重建发生变化的服务
docker compose down # 停止并删除容器和默认网络

docker compose down 默认不会删除命名 Volume;使用 docker compose down -v 会同时删除数据卷,执行前必须确认数据已经备份。

网络

创建自定义网络并启动两个容器:

1
2
3
docker network create app-net
docker run -d --name redis --network app-net redis:7-alpine
docker run -it --rm --network app-net redis:7-alpine redis-cli -h redis ping

同一自定义网络中的容器可以通过容器名 redis 访问,不需要固定容器 IP。Compose 会为项目自动创建网络,服务之间直接使用服务名通信。

清理资源

先查看磁盘占用:

1
docker system df

按类型清理未使用资源:

1
2
3
4
docker container prune
docker image prune
docker network prune
docker volume prune

docker system prune 会一次清理多种未使用资源;带 --volumes 时还会清理未使用的数据卷。Volume 可能保存数据库数据,不要在不了解影响时使用 docker system prune --volumes。

常见问题

容器名称冲突

如果出现 container name is already in use,说明同名容器仍然存在。使用 docker ps -a 找到它,然后选择继续使用、改名或删除。

端口已被占用

修改端口映射左侧的宿主机端口,例如把 8080:80 改为 8081:80,或者先停止占用该端口的程序。

Linux 下没有权限访问 Docker

默认情况下 Docker Socket 需要 root 权限。可以暂时使用 sudo docker ...,或按照官方安装后的说明配置用户组。加入 docker 用户组相当于授予较高的系统权限,不应把不可信用户加入该组。

镜像架构不匹配

Apple Silicon、ARM 服务器和部分开发板可能遇到 platform does not match。优先选择支持当前架构的多平台镜像;模拟其他架构虽然可行,但性能会下降。

容器里修改的内容重建后消失

容器可写层不是可靠的数据保存位置。需要长期保存的内容应使用 Volume、Bind Mount,或者写入 Dockerfile 重新构建镜像。

使用建议

  • 为基础镜像指定版本,并定期重新构建以获取安全修复;
  • 不要把密码、Token 和私钥直接写进 Dockerfile 或提交到仓库;
  • 尽量让容器使用非 root 用户运行;
  • 一个容器只承担清晰的职责,多个服务使用 Compose 组织;
  • 为重要数据卷制定备份和恢复方案;
  • 通过日志、健康检查和资源限制观察容器状态。

参考文献

  1. Docker Docs
  2. Docker overview
  3. What is a container?
  4. Docker Compose Quickstart