1. Docker 初体验:从“装软件”到“开箱子”

如果你刚开始接触Docker,可能会觉得它又是一堆复杂的新概念和命令。别急,咱们换个角度想。以前在服务器上部署一个应用,比如MySQL,你得经历:下载安装包、解决系统依赖、修改配置文件、设置开机启动……整个过程就像在宜家买了个需要自己组装的复杂柜子,螺丝刀、扳手、图纸缺一不可,一不小心就装歪了。

而Docker的做法是,直接把一个装好、配置好、测试好的“柜子”(我们称之为镜像)整个搬过来。你只需要一条命令,这个“柜子”就能在你家(服务器)里立起来直接用了。这个立起来的、正在运行的“柜子”,就是容器

所以,我最喜欢的一个类比是:Docker镜像就像是软件安装包(或者一个装好软件的虚拟机模板),而Docker容器就是安装好后正在运行的那个软件进程。更妙的是,这个“安装包”里不仅包含软件本身,还包含了它运行所需的所有环境(比如特定版本的Java、配置文件、库文件),确保了在任何地方运行效果都一模一样。

那么,Docker能帮你做什么呢?如果你是开发者,它能让你本地开发的环境(比如Redis 7.0, Node.js 18)和测试、生产环境保持绝对一致,再也不会出现“我电脑上好好的”这种问题。如果你是运维,它能让你用秒级的速度部署应用,并且相互隔离,一个应用挂了不会影响其他。学习Docker命令,就是学习如何高效地“搬运”、“打开”和“管理”这些神奇的“软件箱子”。

咱们先从最基础的,和Docker服务本身打交道的命令开始。安装Docker的过程网上教程很多,这里假设你已经安装好了。安装完成后,第一件事就是启动Docker服务,它相当于管理所有“箱子”的后台大管家。

# 启动Docker服务
sudo systemctl start docker

# 设置Docker开机自启动,避免每次重启服务器都要手动开
sudo systemctl enable docker

# 查看Docker服务的运行状态,看到绿色的"active (running)"就说明它在欢快地工作
sudo systemctl status docker

# 如果修改了配置需要重启服务
sudo systemctl restart docker

# 彻底停止Docker服务(谨慎操作,会停掉所有容器)
sudo systemctl stop docker

启动服务后,验证一下是否安装成功,顺便看看版本信息。docker version会输出客户端和服务端的版本,确保两者都正常。而docker info则是一份更详细的报告,它会告诉你Docker使用的存储驱动、镜像仓库地址、容器和镜像的总数等核心信息,这是排查问题时第一个要看的命令。

# 查看版本
docker version

# 查看详细信息
docker info

最后,记住你最好的朋友——帮助命令。Docker命令和参数很多,记不住太正常了。不用去死记硬背,用--help就行。比如你隐约记得拉取镜像有个过滤星星数的功能,但参数忘了,直接docker pull --help,所有选项和说明一目了然。养成查帮助的习惯,比记住所有命令更重要。

# 查看所有顶层命令
docker --help

# 查看某个具体命令的帮助,比如 run
docker run --help

2. 镜像管理:获取、管理和定制你的“软件模板”

镜像就是容器的模板。你可以从公共仓库(比如Docker Hub)拉取现成的,也可以自己创建。管理好镜像是高效使用Docker的第一步。

2.1 查找与拉取镜像

首先,我们得知道有哪些镜像可用。docker search命令就像在应用商店里搜索软件。不过,默认搜索Docker Hub,结果可能很多。我通常会用--filter来筛选,比如只找官方镜像(is-official=true)或者星星数超过一定数量的。

# 搜索名为mysql的镜像
docker search mysql

# 搜索官方出品的mysql镜像
docker search mysql --filter=is-official=true

# 搜索星星数超过10000的nginx镜像
docker search nginx --filter=stars=10000

找到想要的镜像后,就用docker pull把它下载到本地。这里有个关键点:标签(Tag)。标签通常表示版本,如果你不指定,Docker会默认拉取标签为latest的镜像。但在生产环境,我强烈建议永远不要使用latest标签,因为它会变。今天拉取的latest是8.0,明天可能就是8.1,会导致环境不一致。一定要指定具体版本。

# 拉取最新版(不推荐用于生产)
docker pull nginx

# 拉取指定版本,这才是好习惯
docker pull nginx:1.23-alpine
docker pull mysql:8.0.33

拉取镜像时,你会看到分层下载的过程。Docker镜像由多个只读层叠加而成,如果两个镜像共享相同的基础层(比如都是基于Alpine Linux),那么这一层只需要下载一次,非常节省空间和带宽。

2.2 查看、清理与导出镜像

下载了一堆镜像后,怎么管理呢?docker images命令列出本地所有镜像,显示它们的仓库名、标签、ID、创建时间和大小。有时候你会看到一些没有仓库名和标签,显示为<none>:<none>的镜像,这些通常是构建中间层或者旧版本残留,可以清理掉。

# 列出本地镜像
docker images
# 或者更简洁的格式
docker images --format "table {{.Repository}}\t{{.Tag}}\t{{.ID}}\t{{.Size}}"

# 删除一个镜像,-f表示强制删除
docker rmi -f nginx:latest

# 删除所有标签为<none>的悬空镜像(非常实用的清理命令)
docker image prune

# 强制删除所有未被任何容器使用的镜像(清理得更彻底,小心使用)
docker rmi -f $(docker images -aq)

有时候我们需要把镜像分享给没有外网环境的服务器,或者做备份。docker savedocker load就是干这个的。它把镜像保存成一个tar包,可以像普通文件一样拷贝。

# 将镜像保存为tar文件
docker save -o ./my_nginx_backup.tar nginx:1.23-alpine

# 在另一台机器上,从tar文件加载镜像
docker load -i ./my_nginx_backup.tar

2.3 镜像的标签与提交

你可能会问,如果我对一个运行中的容器做了修改(比如安装了额外的软件),想保存成新镜像怎么办?这就需要docker commit命令。但请注意,这不是制作镜像的推荐方式,因为这种修改是黑箱操作,无法追溯和复用。标准做法是使用Dockerfile构建。不过,commit在临时调试和保存现场时非常有用。

# 1. 运行一个基础容器
docker run -it --name my_temp_container ubuntu:22.04 /bin/bash
# (在容器内执行一些操作,比如 apt update && apt install -y curl)

# 2. 退出容器后,将其提交为新镜像
docker commit -m "Added curl tool" -a "YourName" my_temp_container my-ubuntu:with-curl

另一个常用命令是docker tag,它用于给镜像打上一个新的标签,类似于创建了一个别名或软链接,并不产生新的存储占用。这在版本管理和推送到不同仓库时非常有用。

# 为本地镜像创建一个新的标签
docker tag nginx:1.23-alpine my-registry.com/my-team/nginx:v1.23

# 之后就可以用新标签推送镜像
# docker push my-registry.com/my-team/nginx:v1.23

3. 容器生命周期:从创建到销毁的完整操控

容器是镜像的运行实例。如果说镜像是静态的“食谱”,那么容器就是根据食谱做出来的、热气腾腾的“菜肴”。管理容器,就是管理这些正在运行的“菜肴”。

3.1 创建与运行容器

docker run是其中最核心也最复杂的命令。它从镜像创建一个新的容器并启动它。我刚开始用的时候,被它一堆参数搞晕了,其实最常用的就几个组合。

# 最简单的运行,但这样运行后,容器会占用你的终端,按Ctrl+C会停止容器
docker run nginx:alpine

# 更常用的方式:-d 让容器在后台运行(守护进程)
# --name 给容器起个名字,方便后续管理
docker run -d --name my_web nginx:alpine

# 交互式运行一个容器,比如进入一个Ubuntu容器内部操作
# -it 是两个参数:-i(保持标准输入打开)和 -t(分配一个伪终端),合起来让你可以交互
docker run -it --name my_ubuntu ubuntu:22.04 /bin/bash

但上面这样运行的容器,我们无法从外部访问。因为容器有自己的独立网络空间。要想通过宿主机的IP和端口访问容器内的服务,必须做端口映射,使用-p参数。

# 将容器的80端口映射到宿主机的8080端口
# 格式:-p <宿主机端口>:<容器端口>
docker run -d --name my_nginx -p 8080:80 nginx:alpine
# 现在访问 http://你的服务器IP:8080 就能看到Nginx欢迎页了

# 可以映射多个端口
docker run -d --name my_app -p 8080:80 -p 8443:443 my-web-app:latest

3.2 容器的状态管理与操作

容器运行后,我们需要知道它的状态。docker ps是最常用的命令,默认只显示正在运行的容器。加上-a可以看到所有容器,包括已停止的。

# 查看运行中的容器
docker ps

# 查看所有容器(包含已停止的)
docker ps -a

# 显示更简洁的信息,只显示容器ID
docker ps -aq

对容器进行停止、启动、重启和删除操作,是日常运维的一部分。

# 停止一个运行中的容器(发送SIGTERM信号,允许优雅退出)
docker stop my_nginx

# 强制立即停止容器(发送SIGKILL信号)
docker kill my_nginx

# 启动一个已停止的容器
docker start my_nginx

# 重启容器
docker restart my_nginx

# 删除一个已停止的容器
docker rm my_nginx

# 强制删除一个容器(无论是否在运行)
docker rm -f my_nginx

# 清理所有已停止的容器(非常实用的清理命令)
docker container prune

3.3 进入容器与查看日志

当容器运行不符合预期时,我们需要“进入”容器内部看个究竟,或者查看它的日志。有两种主要方式进入容器:docker execdocker attach我强烈推荐始终使用docker exec,因为它会打开一个新的终端会话,不会影响容器内主进程的运行。而docker attach是连接到容器的主进程终端,如果你在里面按了Ctrl+C,可能会导致容器停止。

# 在正在运行的容器中执行一个命令,比如启动一个bash shell
docker exec -it my_nginx /bin/bash

# 在容器中执行一次性命令,比如查看配置文件
docker exec my_nginx cat /etc/nginx/nginx.conf

查看日志是排查问题的另一大利器。docker logs可以查看容器内标准输出(STDOUT)和标准错误(STDERR)的内容。

# 查看容器全部日志
docker logs my_nginx

# 实时跟踪日志输出(类似 tail -f)
docker logs -f my_nginx

# 查看最后20行日志
docker logs --tail 20 my_nginx

# 查看最近10分钟的日志
docker logs --since 10m my_nginx

# 组合使用:实时查看最后50行日志
docker logs -f --tail 50 my_nginx

4. 数据持久化与容器互联:让状态“活下去”

容器默认是无状态的,文件系统的改动只在容器生命周期内存在。一旦容器被删除,所有数据都会丢失。这对于数据库、用户上传的文件等是灾难。因此,数据持久化是Docker运维的关键。

4.1 数据卷:持久化数据的首选

数据卷(Volume)是Docker推荐的持久化数据方式。它是一个独立于容器生命周期的存储区域,可以挂载到容器内的一个或多个目录。

# 创建一个名为`my_data`的数据卷
docker volume create my_data

# 运行容器时,将数据卷挂载到容器内的`/app/data`目录
docker run -d --name my_db -v my_data:/var/lib/mysql mysql:8.0

# 查看所有数据卷
docker volume ls

# 查看某个数据卷的详细信息,包括其存储在宿主机的具体路径
docker volume inspect my_data

# 删除未使用的数据卷
docker volume prune

更常见的一种方式是使用绑定挂载,直接将宿主机的某个目录挂载到容器内。这在开发时特别方便,你可以在宿主机上修改代码,容器内立即生效。

# 将宿主机的`/home/user/app`目录挂载到容器的`/app`目录
# 注意:宿主机路径必须是绝对路径
docker run -d --name my_dev_app -v /home/user/app:/app -p 3000:3000 node:18

4.2 容器间的网络与通信

默认情况下,每个容器都有自己的网络命名空间,相互隔离。但微服务应用通常需要多个容器协作(比如Web应用容器需要连接MySQL容器)。Docker提供了几种网络模式。

最简单的是使用Docker默认创建的桥接网络。所有使用默认网络的容器可以通过容器名互相通信。但更佳实践是创建自定义网络,它能提供更好的隔离和DNS自动发现功能。

# 创建一个自定义的桥接网络
docker network create my_app_network

# 运行MySQL容器,加入这个网络
docker run -d --name mysql_db --network my_app_network -e MYSQL_ROOT_PASSWORD=secret mysql:8.0

# 运行Web应用容器,也加入同一个网络
# 在Web应用的代码中,就可以直接用`mysql_db`这个主机名连接到数据库,而不是IP地址
docker run -d --name web_app --network my_app_network -p 8080:80 my-web-app:latest

使用自定义网络后,容器间通信既安全又方便,不需要暴露数据库端口到宿主机,也无需担心容器重启IP变化的问题。

5. 高效运维实战:性能、日志与资源管理

当容器数量多起来之后,运维工作就需要更精细的工具和命令。这部分命令能帮你洞察容器运行状态,快速定位问题。

5.1 监控容器资源使用情况

docker stats命令可以实时查看所有容器的CPU、内存、网络IO和磁盘IO使用情况,就像是一个容器级别的top命令。

# 实时查看所有容器的资源占用
docker stats

# 只查看指定容器的状态
docker stats my_nginx mysql_db

# 以流式JSON格式输出,方便被其他工具处理
docker stats --no-stream --format "{{.Name}}: {{.CPUPerc}}"

对于更深入的分析,docker top可以查看容器内运行的进程,docker inspect则可以获取容器(或镜像、网络、卷)的底层详细信息,包括配置、状态、网络设置、挂载点等,这是一个非常强大的诊断工具。

# 查看容器内进程
docker top my_nginx

# 获取容器的所有详细信息(输出为JSON)
docker inspect my_nginx

# 只提取特定的信息,比如IP地址
docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' my_nginx

5.2 系统级清理与空间管理

Docker用久了,磁盘空间可能会被不再使用的镜像、停止的容器、无用的数据卷和构建缓存占满。Docker提供了一整套清理命令。

# 查看Docker磁盘使用概况(镜像、容器、数据卷、构建缓存各占多少)
docker system df

# 一键清理:删除所有已停止的容器、未被任何容器使用的网络、所有悬空镜像、所有构建缓存
# 这个命令很安全,也是我最常用的清理命令
docker system prune

# 更激进的清理:加上 `-a` 会删除所有未被容器使用的镜像(不仅仅是悬空镜像),小心使用
docker system prune -a

# 单独清理数据卷(删除未被任何容器引用的数据卷)
docker volume prune

5.3 容器自启动与更新配置

对于需要长期运行的服务,比如数据库,我们希望服务器重启后容器能自动启动。这可以通过--restart策略实现。

# 创建容器时指定自启动策略
# `always`: 无论容器退出代码是什么,总是重启(除非手动停止)
# `unless-stopped`: 只有非手动停止导致的退出才重启(更常用)
# `on-failure`: 仅在非0退出代码时才重启
docker run -d --name redis --restart=unless-stopped -p 6379:6379 redis:alpine

如果容器已经创建了,但忘了加这个参数,可以用docker update来修改。

# 更新一个已存在容器的重启策略
docker update --restart=unless-stopped redis

5.4 文件拷贝与容器备份

有时候我们需要在容器和宿主机之间拷贝文件,或者将容器当前状态备份出来。

# 从宿主机拷贝文件到容器内
docker cp /host/path/file.txt my_nginx:/container/path/

# 从容器内拷贝文件到宿主机
docker cp my_nginx:/container/path/log.txt /host/path/

# 将运行中的容器导出为一个tar包(导出的是容器文件系统快照,不是镜像)
# 这个命令不常用,因为导出的内容不包含镜像历史层,通常用`docker commit`或`docker export`+`docker import`组合
docker export my_nginx > my_container_backup.tar

掌握了这些命令,你基本上就能应对日常开发运维中80%的Docker使用场景了。关键不在于记住所有命令,而是理解每个命令背后的意图:创建、运行、观察、交互、持久化、连接、清理。剩下的20%复杂场景,比如使用Docker Compose编排多容器应用、编写高效的Dockerfile、配置私有镜像仓库、安全扫描等,都是建立在这些基础之上的组合与深化。我自己的经验是,把这些基础命令敲熟练,形成肌肉记忆,然后在实际项目中遇到问题再去查文档或搜索解决方案,这样学习效率最高,印象也最深。

更多推荐