# Docker 笔记Day01:仓库配置与软件安装,基础命令,数据卷,Dockerfile构建镜像(附核心命令速查表)
一、Docker 介绍
1.1 相关资源
| 资源 | 地址 |
|---|---|
| Docker 官网 | https://docs.docker.com/ |
| Docker GitHub | https://github.com/moby/moby |
| Docker Hub(国内) | https://registry.hub.docker.com |
| Docker Hub(国际,需科学上网) | https://hub.docker.com/ |
1.2 Docker 是什么?
Docker 是一个开源项目,诞生于 2013 年初,最初是 dotCloud 公司内部的一个业余项目。它基于 Google 公司推出的 Go 语言实现。项目后来加入了 Linux 基金会,遵从 Apache 2.0 协议,项目代码在 GitHub 上进行维护。
核心定义: Docker 是一个开源的引擎,可以轻松地为任何应用创建一个轻量级的、可移植的、自给自足的容器。开发者可以打包他们的应用以及依赖包到一个可移植的镜像中,然后发布到任何支持 Docker 的机器上运行。
设计思想: Docker 的思想来自于集装箱。集装箱解决了什么问题?在一艘大船上,可以把货物规整地摆放起来,各种各样的货物被装在集装箱里,集装箱之间不会互相影响。这样就不需要专门运送蔬菜的船和专门运送货物的船了。只要这些货物在集装箱里封装得好好的,就可以用一艘大船把他们都运走。Docker 就是类似的理念——云计算就好比大货轮,Docker 就是集装箱。
编排工具类比:
- PVE / KVM 搭档:PVE 是 KVM 的编排工具
- OpenStack / KVM 搭档:OpenStack 是 KVM 的编排工具
- K8s / Docker 搭档:K8s 是 Docker 的编排工具
1.3 Docker 的架构
严格来说,Docker是一个进程。Docker 采用 C/S(客户端/服务器)架构:
- Docker Client:客户端命令行工具,向服务端发送请求,提供命令行工具
- Docker Daemon:服务端守护进程,负责管理容器、镜像等,是真正干活的守护进程
- Registry:镜像仓库(如 Docker Hub、Harbor、阿里云容器镜像服务,也可以运行在本地),镜像仓库,用于存储和分发镜像
Docker 与虚拟机的区别
| 对比项 | 虚拟机 | Docker 容器 |
|---|---|---|
| 操作系统 | 每个虚拟机运行完整的客户操作系统 | 所有容器共享宿主机内核 |
| 启动时间 | 分钟级 | 秒级或毫秒级 |
| 资源占用 | GB 级 | MB 级 |
| 隔离性 | 完全隔离(硬件虚拟化) | 进程级隔离(Namespace + Cgroups) |
虚拟机架构
┌─────────────────────────────────────────────────────────────┐
│ 物理服务器 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 宿主机操作系统 (Host OS) │ │
│ └─────────────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Hypervisor (VMware/KVM) │ │
│ └─────────────────────────────────────────────────────┘ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ 虚拟机 1 │ │ 虚拟机 2 │ │ 虚拟机 3 │ │
│ │ ┌──────────┐ │ │ ┌──────────┐ │ │ ┌──────────┐ │ │
│ │ │ Guest OS │ │ │ │ Guest OS │ │ │ │ Guest OS │ │ │
│ │ │ (完整内核)│ │ │ │ (完整内核)│ │ │ │ (完整内核)│ │ │
│ │ ├──────────┤ │ │ ├──────────┤ │ │ ├──────────┤ │ │
│ │ │ Bins/ │ │ │ │ Bins/ │ │ │ │ Bins/ │ │ │
│ │ │ Libs │ │ │ │ Libs │ │ │ │ Libs │ │ │
│ │ ├──────────┤ │ │ ├──────────┤ │ │ ├──────────┤ │ │
│ │ │ App │ │ │ │ App │ │ │ │ App │ │ │
│ │ └──────────┘ │ │ └──────────┘ │ │ └──────────┘ │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────────────┘
每台虚拟机:独立的操作系统 + 独立的内核 → 资源占用大,启动慢,但隔离性好,安全性佳
Docker 容器架构
┌─────────────────────────────────────────────────────────────┐
│ 物理服务器 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 宿主机操作系统 (Host OS) │ │
│ │ (Linux 内核) │ │
│ └─────────────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Docker Engine (守护进程) │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ 容器 1 │ │ 容器 2 │ │ 容器 3 │ │ 容器 4 │ │
│ │ ┌──────┐ │ │ ┌──────┐ │ │ ┌──────┐ │ │ ┌──────┐ │ │
│ │ │Bins/ │ │ │ │Bins/ │ │ │ │Bins/ │ │ │ │Bins/ │ │ │
│ │ │Libs │ │ │ │Libs │ │ │ │Libs │ │ │ │Libs │ │ │
│ │ ├──────┤ │ │ ├──────┤ │ │ ├──────┤ │ │ ├──────┤ │ │
│ │ │ App │ │ │ │ App │ │ │ │ App │ │ │ │ App │ │ │
│ │ └──────┘ │ │ └──────┘ │ │ └──────┘ │ │ └──────┘ │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
│ │
│ 所有容器共享同一个宿主机内核,只打包应用和依赖 │
└─────────────────────────────────────────────────────────────┘
每个容器:没有独立内核,直接使用宿主机内核 → 轻量级,毫秒级启动。但隔离性差,安全性不及虚拟机
Docker 底层原理(核心知识点)
Namespace 实现资源隔离,Cgroups 实现资源限制。
Cgroup(Control Groups)提供的主要功能如下:
| 功能 | 说明 |
|---|---|
| 资源限制 | 限制任务使用的资源总额,并在超过这个配额时发出提示 |
| 优先级分配 | 分配 CPU 时间片数量及磁盘 IO 带宽大小、控制任务运行的优先级 |
| 资源统计 | 统计系统资源使用量,如 CPU 使用时长、内存用量等 |
| 任务控制 | 对任务执行挂起、恢复等操作 |
Docker逻辑流程图:
初始的Docker ce软件
容器 ──基于──▶ 镜像 ──从──▶ Docker Hub(官方仓库)
│ 拉取镜像
│ 但国内访问慢/超时
▼
配置镜像加速器
│
▼
阿里云/ DaoCloud 等国内加速节点
│
│ 从 Docker Hub 同步缓存
▼
docker pull 速度大幅提升
1.4 Docker 的优点
| 优点 | 说明 |
|---|---|
| 快 | 运行时的性能快,管理操作(启动、停止、开始、重启等)都以秒或毫秒为单位 |
| 敏捷 | 像虚拟机一样敏捷,而且会更便宜,在裸机上部署像点按钮一样简单 |
| 灵活 | 将应用和系统“容器化”,不添加额外的操作系统 |
| 轻量 | 在一台服务器上可以部署 100~1000 个容器 |
| 便宜 | 开源的,免费的,低成本的 |
1.5 Docker 的缺点
所有容器共用 Linux 内核资源,资源能实现最大限度利用,但资源隔离性差,所以在安全上也可能会存在漏洞。
版本说明
docker主要分为企业版(Docker EE)和社区版(Docker CE )
| 版本 | 全称 | 费用 | 特点 | 适用场景 |
|---|---|---|---|---|
| Docker CE | Community Edition(社区版) | 免费 | 开源,包含 Docker 核心功能,由社区维护 | 个人开发者、小型团队、学习实验 |
| Docker EE | Enterprise Edition(企业版) | 商业付费 | CE 基础上增加安全扫描、镜像签名、企业级支持等 | 中大型企业的核心生产环境 |

Docker
├── Docker CE(社区版,免费)
│ ├── nightly(日更版)← 更新最频繁,内容最新,最不稳定
│ ├── test(测试版)← 预发布版本,包含新功能
│ ├── Stable(稳定版)← 生产环境推荐,稳定性最佳
│ └── Edge(边缘版)← 仅供测试/尝鲜。目前已经淡出,被test取代
└── Docker EE(企业版,付费)
二、安装 Docker
注意!本次项目全程使用欧拉系统进行操作,版本为openEuler-22.03,可前往镜像网站进行下载:https://mirror.accum.se/mirror/openeuler.org/openEuler-22.03-LTS-SP2/ISO/x86_64/
欧拉系统可在VMware 正常安装,过程不再赘述。
此外,本次实验全程使用MobaXterm/WindTerm进行远程连接
2.1 实验环境
| 项目 | 配置 |
|---|---|
| 主机 IP | 192.168.1.11 |
| 内存 | 4GiB |
| CPU | 2vCPU |
2.2 配置时间同步
# 安装时间同步工具
[root@hd1 ~]# yum install -y ntp ntpdate
[root@hd1 ~]# ntpdate cn.pool.ntp.org
# 编写计划任务,每小时同步一次
[root@hd1 ~]# crontab -e
* */1 * * * /usr/sbin/ntpdate cn.pool.ntp.org
# 重启 crond 服务使配置生效
[root@hd1 ~]# systemctl restart crond
2.3 安装 Docker-CE
配置 Docker-CE 国内 YUM 源(阿里云)
这里我们需要指定 Docker-CE 这个软件的安装地址
# 下载 Docker-CE 仓库文件配置文件docker-ce.repo,里面写的是 Docker 仓库的地址和配置信息
[root@hd1 ~]# wget http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
[root@hd1 ~]# mv docker-ce.repo /etc/yum.repos.d/
# 修改 docker-ce.repo 的内容如下(只保留第一个[docker-ce-stable]仓库项,并修改baseurl为稳定版本的仓库地址)
[root@hd1 yum.repos.d]# cat docker-ce.repo
[docker-ce-stable]
name=Docker CE Stable - $basearch
baseurl=https://mirrors.aliyun.com/docker-ce/linux/centos/8.0/x86_64/stable/ #镜像仓库地址
enabled=1
gpgcheck=1
gpgkey=https://mirrors.aliyun.com/docker-ce/linux/centos/gpg
# 安装 Docker 依赖包
[root@hd1 ~]# yum install -y device-mapper-persistent-data lvm2
# 安装 Docker-CE
[root@hd1 ~]# yum install docker-ce -y
# 启动 Docker 服务并设置开机自启
[root@hd1 ~]# systemctl start docker && systemctl enable docker
[root@hd1 ~]# systemctl status docker
# 查看 Docker 版本信息
[root@hd1 ~]# docker version
wget拉取了软件仓库的repo配置文件(或者说是仓库文件的模板,这个软件仓库用来下载docker ce软件),我们根据实际情况修改这个配置文件。
其实可以不拉取也可以,我们自己创建repo文件写入对应配置,也可以达到同样的效果。
2.4 开启包转发功能
开启包转发(IP Forward)是让 Linux 内核具备“三层路由转发”能力,即允许内核将一个网络接口(如 docker0 网桥)收到的数据包,根据路由表转发到另一个网络接口(如宿主机的 eth0 物理网卡)。
Docker 的默认网络模式(bridge)下,宿主机充当了容器的路由器/网关。如果不开启转发,这个“路由器”功能就被禁用了。
开启包转发后,宿主机实际上变成了一个路由器。为了防止容器被当作跳板攻击外部网络,Docker 会同时在 iptables 的 FORWARD 链中插入严格的过滤规则(默认 DROP 所有非 Docker 网络的转发流量),所以开启转发本身不会引入严重安全风险,但运维时需注意不要轻易 iptables -F 清空规则,否则容器会变成“肉鸡”
# 开启 IP 转发
[root@hd1 ~]# cat /etc/sysctl.conf
......
kernel.sysrq=0
net.ipv4.ip_forward=1 #修改本行为1,其余不变即可
net.ipv4.conf.all.send_redirects=0
net.ipv4.conf.default.send_redirects=0
net.ipv4.conf.all.accept_source_route=0
net.ipv4.conf.default.accept_source_route=0
net.ipv4.conf.all.accept_redirects=0
net.ipv4.conf.default.accept_redirects=0
net.ipv4.conf.all.secure_redirects=0
net.ipv4.conf.default.secure_redirects=0
net.ipv4.icmp_echo_ignore_broadcasts=1
net.ipv4.icmp_ignore_bogus_error_responses=1
net.ipv4.conf.all.rp_filter=1
net.ipv4.conf.default.rp_filter=1
net.ipv4.tcp_syncookies=1
#重新加载内核参数配置文件,让其中的设置立即生效
[root@hd1 ~]# sysctl -p
# 重启 Docker 服务
[root@hd1 ~]# systemctl restart docker
2.5 配置 Docker 镜像加速器
登录阿里云镜像仓库:https://cr.console.aliyun.com/cn-hangzhou/instances/mirrors
如果没有开通,可先开通阿里云的镜像服务。

将上图框中内容粘贴到/etc/docker/daemon.json文件中(加速地址每个人都不一样,尽量使用自己的)
# /etc/docker/daemon.json 默认是不存在的,需要自己手动创建。这个文件是官方规定的默认配置文件路径和文件名。在其中加入自己的加速地址
[root@hd1 ~]# vim /etc/docker/daemon.json
#这是我找到的能用的几个加速地址
{
"registry-mirrors": [
"https://docker.m.daocloud.io",
"https://docker.1ms.run",
"https://docker.xuanyuan.me"
]
}
# 让配置生效
[root@hd1 ~]# systemctl daemon-reload #重新读取配置文件
[root@hd1 ~]# systemctl restart docker #重启服务
三、Docker 的基本用法
容器的特点
- 镜像:只读的文件
- 容器:根据镜像生成的“应用”(确切说,是 一个轻量级系统+应用程序 ),同一个镜像可以开多个容器
容器是为应用而生的,倘若应用挂掉,容器也会‘自杀’。容器的作用就是让某个应用能以标准化的方式运行。 它的生命周期和主进程是绑定的,这是它和虚拟机最本质的区别之一
容器的本质:
容器是为运行某个特定功能进程而存在的临时环境。它从启动的那一刻起,就围绕着主进程的生命周期展开:主进程启动,容器就运行;主进程结束,容器停止
安装容器之后,宿主机会多一个网卡(与虚拟机有些类似)docker0,术语逻辑网卡,是容器和宿主机通信的桥梁
3.1 镜像管理
docker load -i:解压离线镜像包(-i:指定镜像包)
docker images:查看本地镜像
docker save -o [压缩包名 包名:标签]:将镜像导出为离线压缩包
# 先拉取镜像。此步骤需联网,如果无法拉取,应该是/etc/docker/daemon.json文件中加速地址的问题,此时建议问AI获取当前可用的公共镜像资源)
[root@hd1 ~]# docker pull nginx:latest
latest: Pulling from library/nginx
062e450697fa: Pull complete
82454cdbf456: Pull complete
3c7ab7949321: Pull complete
cacfcdd01f30: Pull complete
b6698f04e005: Pull complete
2bedaf25031a: Pull complete
d26f27cc8c41: Pull complete
Digest: sha256:5a88c9c45479443d7be2eadc894b4ed0a9801bae03d97a5760ae13b5c2005942
Status: Downloaded newer image for nginx:latest
docker.io/library/nginx:latest
#查看本地镜像验证拉取是否成功
[root@hd1 ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx latest 4e5db4761e0f 2 days ago 161MB
# 如果有本地的离线镜像资源包,可以使用load命令加载离线镜像包:
[root@hd1 ~]# docker load -i nginx.tar.gz
# 从docker官方远程仓库下载镜像
[root@hd1 ~]# docker pull redis
# 从个人仓库拉取镜像 仓库路径+要拉取的镜像名(如果有非latest的标签,一定要加上标签名。latest标签的标签名可以省略)
[root@hd1 ~]# docker pull registry.cn-hangzhou.aliyuncs.com/wangmeng100/centos:v1
[root@hd1 ~]# docker pull registry.cn-hangzhou.aliyuncs.com/wangmeng100/nginx:latest
# 查看本地镜像
[root@hd1 ~]# docker images
# 将镜像导出为离线压缩包
[root@hd1 ~]# docker save -o alpine2026.tar.gz alpine:latest
# 删除镜像(如果 tag 是 latest,可以省略)
# 方法1
[root@hd1 ~]# docker rmi -f alpine:latest
[root@hd1 ~]# docker rmi -f alpine:latest
Untagged: alpine:latest
Untagged: alpine@sha256:28bd5fe8b56d1bd048e5babf5b10710ebe0bae67db86916198a6eec434943f8b
Deleted: sha256:d529dd0c6e5597ac7e4a3e2dea65c3fcc6173f4cae713c409265c1dd9914a11b
Deleted: sha256:34884abbe92863fce933ed7c39c0e045631af0ed86d5cc0dfbdf9fdca426ce3c
#再使用images查看,已经被删除
[root@hd1 ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx latest 4e5db4761e0f 2 days ago 161MB
redis latest da3958f512ee 4 days ago 143MB
registry.cn-hangzhou.aliyuncs.com/wangmeng100/centos v1 eeb6ee3f44bd 4 years ago 204MB
# 方法2(如果 tag 是 latest,可以省略标签)
[root@hd1 ~]# docker rmi -f alpine

TAG是标签,我们在官网拉下的镜像标签默认是latest。
无论是个人仓库还是官方仓库,标签默认是latest,同样的latest标签结尾的在拉取时可以省略标签名
3.2 容器的基本操作
交互式启动容器(较少用)
输入 exit 退出容器后,容器也会停止,不会再在前台运行。
# 以交互式方式启动容器并进入容器环境
[root@hd1 ~]# docker run --name=hello -it nginx:latest /bin/bash
root@448549093fae:/#
参数说明:
--name:指定当前容器的名字(仅用来标识)-i:交互式-t:分配终端nginx:latest:指定启动 Docker 需要的镜像/bin/bash:指定你的 Shell(解释器) 类型为 Bash,也就是终端的解释器
注意:交互式进入容器之后,使用exit退出容器的同时,容器会关闭
守护进程方式启动容器(较常用)
docker ps :查看正在运行的容器(-a参数可以查看所有容器,包括已退出的容器)
守护进程方式启动不需要指定解释器,exec登陆进去再指定
# 以守护进程方式启动容器
[root@hd1 ~]# docker run --name=hello1 -td nginx:latest
#这里就可以看到我们的容器了
[root@hd1 ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
89a61bc7b6f7 nginx:latest "/docker-entrypoint.…" 7 seconds ago Up 6 seconds 80/tcp hello1
-d:在后台运行容器(detach 模式)
使用守护进行方式启动的容器,后续登录进去,再exit退出容器,不会让容器关闭。
如果运行的时候就是-it交互式启动容器,再exit,容器会关闭;但是如果一开始就是-td守护进程方式启动容器,再exec登录进容器,再退出,不会关闭容器,容器会回到后台状态
后台运行容器是否需要加
-t,取决于容器的主进程是否依赖伪终端(TTY)。
- 主进程是交互式 Shell(如
/bin/sh、/bin/bash)时:必须加-t,否则 Shell 会因无终端而退出,容器立即停止。(也就是说容器运行交互式进程时必须使用-t)- 主进程是服务类进程(如
nginx、redis)时:不需要-t,直接-d即可正常运行,-t在此场景下无效且多余。一个典型的特例:Alpine 镜像的默认主进程就是
/bin/sh,所以在后台运行时docker run -td alpine才是正确的写法(同时需要-d和-t)。
登录到容器中
注意!docker exec 只能在容器处于 “运行中”(Running) 状态时才能使用,需要与之前的run进行区分。run 是“新建并启动”,exec 是“进入已运行中的”
[root@hd1 ~]# docker exec -it hello1 /bin/bash
root@89a61bc7b6f7:/#
查看容器
# 查看正在运行的容器
[root@hd1 ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
89a61bc7b6f7 nginx:latest "/docker-entrypoint.…" 7 seconds ago Up 6 seconds 80/tcp hello1
# 查看所有容器(包括运行和退出的)
[root@hd1 ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
89a61bc7b6f7 nginx:latest "/docker-entrypoint.…" About a minute ago Up About a minute 80/tcp hello1
448549093fae nginx:latest "/docker-entrypoint.…" 2 minutes ago Exited (0) About a minute ago hello
容器的启停
docker stop 容器名/CONTAINER ID :停止容器
docker start 容器名 :启动容器
注意!docker start 默认是“后台运行”(detach),也就是说会进入守护进程的方式,此时可以通过exec登录容器
也就是说,如果容器1以交互式开启,然后退出容器(此时容器会关闭),使用docker start启动容器之后,容器会变成后台运行,也就是我们之前的守护进程方式启动容器的状态

# 停止容器
[root@hd1 ~]# docker stop hello1
#使用容器名或者容器id都可以
[root@hd1 ~]# docker stop 89a61bc7b6f7
# 启动容器
[root@hd1 ~]# docker start hello1
#同样的,使用容器id也可以
[root@hd1 ~]# docker start 89a61bc7b6f7
3.3 端口映射
docker run:新开并启动一个新容器
-P:将容器内部的端口号映射到物理机的随机端口
-p 宿主机端口号:容器端口号:将容器内部的端口号映射到物理机的指定端口
-d:后台运行
# -P:将容器内部的端口号映射到物理机的随机端口
[root@hd1 ~]# docker run --name=nginx3 -P -d nginx
#本命令会新开一个容器
# 查看端口映射
[root@hd1 ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
9969930fd1b9 nginx "/docker-entrypoint.…" 32 seconds ago Up 30 seconds 0.0.0.0:32768->80/tcp, :::32768->80/tcp nginx3
89a61bc7b6f7 nginx:latest "/docker-entrypoint.…" 20 minutes ago Up 13 minutes 80/tcp hello1
#可以看到,容器的80端口映射到了宿主机的32768端口
# -p:指定映射到固定的端口
[root@hd1 ~]# docker run --name=nginx5 -p 9000:80 -d nginx:latest
[root@hd1 ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
9f7fc50bae33 nginx:latest "/docker-entrypoint.…" 3 seconds ago Up 2 seconds 0.0.0.0:9000->80/tcp, :::9000->80/tcp nginx5
9969930fd1b9 nginx "/docker-entrypoint.…" 2 minutes ago Up 2 minutes 0.0.0.0:32768->80/tcp, :::32768->80/tcp nginx3
89a61bc7b6f7 nginx:latest "/docker-entrypoint.…" 22 minutes ago Up 14 minutes 80/tcp hello1
#可以看到,我们nginx5容器的80端口映射到了宿主机的9000端口
补充:
此时可以在浏览器访问虚拟机映射80端口的端口,如192.168.1.11:9000
但我们的宿主机并没有安装nginx。这就是容器的作用,容器就是一个功能盒子。
#登录进容器
[root@hd1 ~]# docker exec -it nginx5 /bin/bash
#修改网页根目录下的index.html
root@9f7fc50bae33:/# echo 1111 > /usr/share/nginx/html/index.html
root@9f7fc50bae33:/# curl 192.168.1.11:9000
1111
此时在windows浏览器访问,会展示出来修改后的页面
3.4 Docker Commit:将容器提交为镜像
docker commit 容器名/容器id 镜像名:标签:将容器提交为镜像
# 1. 创建一个容器
[root@hd1 ~]# docker run --name=hello200 -d nginx
# 2. 进入容器内部,对容器做出改变
[root@hd1 ~]# docker exec -it hello200 /bin/bash
root@da59b5cb6d3e:/# cd /usr/share/nginx/html/
root@da59b5cb6d3e:/usr/share/nginx/html# echo 1111111111111111111111111 > index.html
# 3. 将容器打包成镜像
[root@hd1 ~]# docker commit hello200 nginx:v2
# 4. 查看生成的镜像
[root@hd1 ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx v2 9f5923b34976 8 seconds ago 231MB
# 5. 用 nginx:v2 运行容器,验证修改
[root@hd1 ~]# docker run --name=hello202 -p 8888:80 -d nginx:v2
[root@hd2 ~]# docker exec -it hello202 /bin/bash
root@69296683ebc8:/# curl 127.0.0.1
1111111111111111111111111
# 6. 将镜像打包,通过 U 盘等设备扩散
[root@hd1 ~]# docker save -o abc.tar.gz nginx:v2
3.5 Docker CP:在宿主机与容器之间拷贝文件
docker cp 宿主机地址 容器名:容器中地址 :将宿主机内容拷贝到容器
docker cp 容器名:容器中地址 宿主机地址 :将容器内容拷贝的宿主机
# 将宿主机上的 11.txt 拷贝到 nginx5 容器的 / 目录
[root@hd1 ~]# docker cp 11.txt nginx5:/
Successfully copied 2.05kB to nginx5:/
# 将容器内部文件拷贝到宿主机上
[root@hd1 ~]# docker cp nginx5:/etc/nginx/nginx.conf .
Successfully copied 2.56kB to /root/.
3.6 其他常用命令
| 命令 | 说明 |
|---|---|
docker history 镜像名[:标签] | 显示镜像的构建历史 |
docker inspect hello202 | 获取 Docker 对象(容器,镜像,网络,卷等)的底层信息 (已经关闭的容器也能看到) |
docker logs hello202 | 获取容器日志(非常重要) |
docker ps [-a] | 显示正在运行容器(-a显示所有容器,包括已经退出的) |
docker rm [-f] hello202 | 删除容器 |
docker top hello202 | 显示正在运行容器的进程 |
docker stats | 查看容器资源占用情况(本命令有几秒钟延迟) |
docker stats hello202 | 查看某个容器的资源占用 |
#显示镜像的构建历史,输出内容需要从下往上看,最顶部是最新构建的内容
[root@hd1 ~]# docker history nginx:V5
#看可以看得到,最顶部是最新内容,显示更改时间为5分钟前
IMAGE CREATED CREATED BY SIZE COMMENT
23abe801deb2 5 minutes ago nginx -g daemon off; 1.34kB
4e5db4761e0f 2 days ago CMD ["nginx" "-g" "daemon off;"] 0B buildkit.dockerfile.v0
<missing> 2 days ago STOPSIGNAL SIGQUIT 0B buildkit.dockerfile.v0
<missing> 2 days ago EXPOSE map[80/tcp:{}] 0B buildkit.dockerfile.v0
<missing> 2 days ago ENTRYPOINT ["/docker-entrypoint.sh"] 0B buildkit.dockerfile.v0
<missing> 2 days ago COPY 30-tune-worker-processes.sh /docker-ent… 4.62kB buildkit.dockerfile.v0
<missing> 2 days ago COPY 20-envsubst-on-templates.sh /docker-ent… 3.03kB buildkit.dockerfile.v0
<missing> 2 days ago COPY 15-local-resolvers.envsh /docker-entryp… 389B buildkit.dockerfile.v0
<missing> 2 days ago COPY 10-listen-on-ipv6-by-default.sh /docker… 2.12kB buildkit.dockerfile.v0
<missing> 2 days ago COPY docker-entrypoint.sh / # buildkit 1.62kB buildkit.dockerfile.v0
<missing> 2 days ago RUN /bin/sh -c set -x && groupadd --syst… 82.7MB buildkit.dockerfile.v0
<missing> 2 days ago ENV DYNPKG_RELEASE=1~trixie 0B buildkit.dockerfile.v0
<missing> 2 days ago ENV PKG_RELEASE=1~trixie 0B buildkit.dockerfile.v0
<missing> 2 days ago ENV ACME_VERSION=0.4.1 0B buildkit.dockerfile.v0
<missing> 2 days ago ENV NJS_RELEASE=1~trixie 0B buildkit.dockerfile.v0
<missing> 2 days ago ENV NJS_VERSION=1.0.0 0B buildkit.dockerfile.v0
<missing> 2 days ago ENV NGINX_VERSION=1.31.3 0B buildkit.dockerfile.v0
<missing> 2 days ago LABEL maintainer=NGINX Docker Maintainers <d… 0B buildkit.dockerfile.v0
<missing> 5 days ago # debian.sh --arch 'amd64' out/ 'trixie' '@1… 78.6MB debuerreotype 0.17
#获取docker对象(容器,镜像,网络,卷等)所有底层详细信息。inspect,检查,探测的意思
[root@hd1 ~]# docker inspect hello202
#输出信息太多,通常我们会用 --format 参数或 grep 来过滤
#获取容器日志(非常重要)
[root@hd1 ~]#docker logs hello202
#列出正在运行的容器
[root@hd1 ~]#docker ps
#列出所有容器,包括退出的
[root@hd1 ~]#docker ps -a
#移除一个或者多个容器(加-f强制删除)
[root@hd1 ~]#docker rm hello202
#显示正在运行容器的进程
[root@hd1 ~]#docker top hello202
UID PID PPID C STIME TTY TIME CMD
root 19155 19133 0 11:03 ? 00:00:00 nginx: master process nginx -g daemon off;
101 19196 19155 0 11:03 ? 00:00:00 nginx: worker process
101 19197 19155 0 11:03 ? 00:00:00 nginx: worker process
101 19198 19155 0 11:03 ? 00:00:00 nginx: worker process
101 19199 19155 0 11:03 ? 00:00:00 nginx: worker process
#查看所有的容器资源占用情况(动态命令)
[root@hd1 ~]#docker stats
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
9f7fc50bae33 nginx5 0.00% 3.891MiB / 3.297GiB 0.12% 8.82kB / 7.94kB 0B / 24.6kB 5
9969930fd1b9 nginx3 0.00% 3.812MiB / 3.297GiB 0.11% 1.19kB / 0B 0B / 16.4kB 5
89a61bc7b6f7 hello1 0.00% 3.781MiB / 3.297GiB 0.11% 1.41kB / 0B 0B / 0B 5
#查看某个容器的资源占用情况
[root@hd1 ~]#docker stats hello202
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
9f7fc50bae33 nginx5 0.00% 3.891MiB / 3.297GiB 0.12% 8.82kB / 7.94kB 0B / 24.6kB 5
验证容器隔离性
在宿主机上可以看到容器内部的进程,说明容器隔离性相对较弱
[root@hd1 ~]# ps -ef | grep nginx
root 18814 18793 0 11:01 ? 00:00:00 nginx: master process nginx -g daemon off;
101 18856 18814 0 11:01 ? 00:00:00 nginx: worker process
101 18857 18814 0 11:01 ? 00:00:00 nginx: worker process
101 18858 18814 0 11:01 ? 00:00:00 nginx: worker process
101 18859 18814 0 11:01 ? 00:00:00 nginx: worker process
root 19155 19133 0 11:03 ? 00:00:00 nginx: master process nginx -g daemon off;
101 19196 19155 0 11:03 ? 00:00:00 nginx: worker process
101 19197 19155 0 11:03 ? 00:00:00 nginx: worker process
101 19198 19155 0 11:03 ? 00:00:00 nginx: worker process
101 19199 19155 0 11:03 ? 00:00:00 nginx: worker process
root 21888 15210 0 11:43 pts/2 00:00:00 grep --color=auto nginx
容器的杀死与停止
#正常停止容器与杀死容器的退出代码是不同的。杀死一个或者多个容器,会导致异常退出,退出代码非0
#杀死一个容器
[root@hd1 ~]# docker kill nginx3
nginx3
#查看退出代码
[root@hd1 ~]# docker inspect nginx3
......
"State": {
"Status": "exited",
"Running": false,
"Paused": false,
"Restarting": false,
"OOMKilled": false,
"Dead": false,
"Pid": 0,
"ExitCode": 137, #退出代码非0。经典代码137,代表强制退出
"Error": "",
"StartedAt": "2026-07-18T03:01:10.08815254Z",
"FinishedAt": "2026-07-18T03:44:33.43035631Z"
},
......
#启动一下刚刚杀死的容器nginx3
[root@hd1 ~]# docker start nginx3
#正常停止容器
[root@hd1 ~]# docker stop hello202
#查看退出代码
[root@hd1 ~]# docker inspect hello202
......
"State": {
"Status": "exited",
"Running": false,
"Paused": false,
"Restarting": false,
"OOMKilled": false,
"Dead": false,
"Pid": 0,
"ExitCode": 0, #退出代码为0,代表正常退出
"Error": "",
"StartedAt": "2026-07-18T03:47:02.417518643Z",
"FinishedAt": "2026-07-18T03:47:55.373019687Z"
},
......
容器与镜像的删除
docker rm [-f] 容器名/容器id :删除容器
docker rmi [-f] 镜像名/镜像id:删除镜像
如果镜像正在被容器使用,直接删除会报错:
Error response from daemon: conflict: unable to remove repository reference “nginx” (must force) - container xxx is using its referenced image xxx
此时要么先删除依赖镜像的容器,要么加-f强行删除
生产环境中不建议强制删除
# 删除一个或多个容器
[root@hd1 ~]# docker rm hello202
# 强制删除正在运行的容器
[root@hd1 ~]# docker rm -f hello202
# 批量删除所有容器
[root@hd1 ~]# docker rm -f $(docker ps -aq)
# 批量删除所有镜像
[root@hd1 ~]# docker rmi -f $(docker images -q)
$() : 将括号中的语句/命令的输出结果作为值
-q:静默输出
-a:表示所有容器(包括已停止的)
docker images -q:输出所有镜像id
[root@hd1 ~]# docker images -q
23abe801deb2
4e5db4761e0f
da3958f512ee
eeb6ee3f44bd
docker ps -aq:输出所有容器id
[root@hd1 ~]# docker ps -aq
9f7fc50bae33
9969930fd1b9
448549093fae
3.7 容器的属性限制
Docker Update可以限制运行中的容器的属性。
语法:docker update 选项=值 容器名/容器id
Docker Update --restart重启策略
| 策略 | 说明 |
|---|---|
no | 默认策略,容器异常退出时不重启 |
on-failure | 容器非正常退出时(退出状态非0)重启 |
on-failure:3 | 非正常退出时重启,最多重启3次 |
always | 容器非正常退出时总是重启 |
# 设置容器开机自启(通过 update 指定重启策略为 always)
[root@hd1 ~]# docker update --restart=always 2ef06a364009
# 限制容器内存(-m 设置内存限制,--memory-swap 设置内存+交换分区限制)
[root@hd1 ~]# docker update -m 64M --memory-swap 64M nginx2
#可以通过docker stats 查看
分配cpu:
0-3 指代第0个到第四个
0,1 :把0和1分配给
此外,docker run 也可以进行容器属性的限制
# 绑定 CPU 核(编号为 5 的 CPU 核)
[root@hd1 ~]# docker run --cpuset-cpus 5 -d nginx:latest
# 限制容器使用 0.1 (10%)的 CPU 算力
[root@hd1 ~]# docker run --name abc --cpus 0.1 -d nginx
# 启动容器时限制内存(run的时候不指定容器名,会自动生成一个容器名)
[root@hd1 ~]# docker run -m 128M -d nginx
–cpuset-cpus:指定的是 物理/逻辑 CPU 核心编号,只能是整数。用于让容器独占特定核心
–cpus : CPU 使用时间份额,表示占用总 CPU 算力的百分比,可以是小数。用于限制容器整体 CPU 使用率
毫核(millicore)是 CPU 资源的逻辑切片单位
1cpu 1核=1000mcpu (毫核)
grep命令解析:grep -i -C 2 cpu
-i :不区分大小写
-C 2:表示查看匹配行的上下两行
补充:
-B N 显示匹配行之前的 N 行(Before)
-A N 显示匹配行之后的 N 行(After)
-C N 显示匹配行前后各 N 行(Context)
3.8 使用容器部署 MySQL
[root@hd1 ~]# docker run --name mysql80 \
-e MYSQL_ROOT_PASSWORD=123456 \ # -e:设置容器的环境变量,这里通过环境变量来设置数据库的密码
-p 33060:3306 \
--privileged \ # --privileged:特权容器,容器内使用真正的 root 用户
-d mysql:8.0
# 通过访问宿主机的 33060 端口来访问数据库
[root@hd1 ~]# dnf -y install mysql #提供mysql命令
[root@hd1 ~]# mysql -uroot -p123456 -P 33060 -h 192.168.1.11
加了 --privileged,可以让容器内的 root 用户获得宿主机 root 的几乎所有权限(实战不建议)
不加 --privileged,容器内的 root 用户在宿主机看来只是一个受限的普通用户
3.9 选择镜像的建议
生产环境,尽可能选体积较小的镜像
下面是两个常用的操作系统镜像:
| 镜像 | 说明 |
|---|---|
| BusyBox | 集成了一百多个最常用 Linux 命令和工具的软件,是 Linux 工具里的“瑞士军刀” |
| Alpine | 面向安全的轻型 Linux 发行版,经典最小镜像,基于 BusyBox,功能比 BusyBox 完善(相当于一个小型的操作系统。不支持yum命令,但支持apt命令) |
最佳实践: 无论是制作镜像还是下载镜像,优先选择 Alpine 类型的镜像。
#需要指定一个 -t 才能在后台运行
[root@hd1 ~]# docker run -t -d alpine:latest
#登录alpine容器不能使用/bin/bash,因为Alpine 为了追求极致小巧,默认不包含 Bash,只带了更轻量的 sh
[root@hd1 ~]# docker exec -it 4a /bin/sh
#注意这里4a是容器id简写,因为之前运行没有指定容器名使用用容器id指定
四、Docker 数据卷管理
4.1 数据卷的概念
创建新容器时,可以在基础层之上添加一个新的可写层,该层通常称为“容器层”。对运行中的容器所做的所有更改(例如写入新文件、修改现有文件和删除文件)都将写入这个薄可写容器层。
什么是数据卷?
数据卷类似于 U 盘,和容器保持松耦合。数据卷是经过特殊设计的目录,可以绕过联合文件系统(UFS),为一个或者多个容器提供访问。数据卷设计的目的在于数据的永久存储,它完全独立于容器的生存周期。因此,Docker 不会在容器删除时删除其挂载的数据卷,也不会存在类似的垃圾收集机制对容器引用的数据卷进行处理。同一个数据卷可以支持多个容器的访问。
两种挂载方式:
bind Bind Mount 宿主机绝对路径挂载
数据流向:
宿主机目录 (/my/html) ──bind mount──▶ 容器 (/usr/share/nginx/html)
│ │
│ 修改文件 │ 实时同步
▼ ▼
宿主机文件变化 ◀─── 双向同步 ───▶ 容器内文件变化
Volume 挂载Docker 卷
数据流向:
宿主机 Docker 区域 (/var/lib/docker/volumes/卷名) ──volume mount──▶ 容器内挂载点
│
▼
Docker 全权管理
不依赖宿主机路径(宿主机删除对应目录也不会影响容器数据)
生产环境推荐使用

container:容器
Memory:容器的临时存储(容器重启即消失,对应 tmpfs 挂载)
Filesystem:宿主机真正的硬盘存储(持久化数据)
Docker area:Docker 管理的专属存储区域
Memory:容器的临时存储(容器重启即消失,对应 tmpfs 挂载)
(/var/lib/docker/volumes/)
4.2 Bind Mount(宿主机绝对路径挂载)
# 将宿主机上的 /my/html 和容器中的 /usr/share/nginx/html 进行映射
# 所有以 "/" 开始的路径都认为是 bind mount
[root@hd1 html]# mkdir /my/html -p
[root@hd1 html]# docker run --name=nginxa -d -p 8000:80 -v /my/html:/usr/share/nginx/html nginx:latest
#注意,我们刚挂载一个空的目录/my/html到容器,所以容器内的 /usr/share/nginx/html 也是空的,此时访问会显示403
#修改index.html文件
[root@hd1 html]# echo 111111 > /my/html/index.html
#访问验证
[root@hd1 html]# curl http://192.168.1.11:8000/
111111
-v 宿主机路径:容器路径 :数据卷挂载(Volume)。将宿主机的目录挂载到容器的目录(Nginx 的网页根目录)。
这样修改宿主机上的网页文件,会实时同步到容器内
Bind Mount 挂载,宿主机路径会直接占据容器路径,如果宿主机目录为空,会清空容器内的目标目录
4.3 Volume 挂载(生产环境推荐)
Volume 挂载分为具名卷(有名称的卷)和匿名卷(没有名称的卷)。无论是具名卷还是匿名卷,默认都在宿主机的 /var/lib/docker/volumes/ 目录之下。
若卷为空,则会复制容器中内容到卷中。若卷中本来就有内容,则不会复制容器内容,避免覆盖
# 具名卷:创建名为 nginx 的卷,并保存容器路径 /usr/share/nginx/html 中的内容
[root@hd1 html]# docker run --name nginx12 -d -P -v nginx:/usr/share/nginx/html nginx:latest
#此时在volume目录下可以看到我们的具名卷
[root@hd1 html]# ls /var/lib/docker/volumes/
backingFsBlockDev metadata.db nginx
#数据就存储在卷目录下的 _data 子目录中
[root@hd1 html]# ls /var/lib/docker/volumes/nginx/
_data
#添加index.html文件
[root@hd1 html]# echo 666666 > /var/lib/docker/volumes/nginx/_data/index.html
#访问验证
[root@hd1 html]# curl http://192.168.1.11:8000/
666666
# 匿名卷:不指定卷名,由 Docker 自动生成
[root@hd1 html]# docker run --name nginx1 -d -P -v /usr/share/nginx/html nginx:latest
# 列出所有卷
[root@hd1 ~]# docker volume ls
402430d5f06b596a4c400136f3bf285e4332877e2643085db1551e784fd63155 metadata.db
backingFsBlockDev nginx
# 查看匿名容器的挂载详情(匿名容器没有名称,但有一个哈希id进行标识)
[root@hd1 ~]# docker inspect nginx1
#注意,只有匿名卷才有这串哈希值卷名
"Mounts": [
{
"Type": "volume",
"Source": ls"/var/lib/docker/volumes/402430d5f06b596a4c400136f3bf285e4332877e2643085db1551e784fd63155/_data",
}
]
# 移除没有被使用的卷。prune命令只会删除未被任何容器使用的匿名卷
[root@hd1 ~]# docker volume prune
#删除匿名卷
[root@hd1 ~]# docker volume rm 402430d5f06b596a4c400136f3bf285e4332877e2643085db1551e784fd63155
#删除具名卷
[root@hd1 ~]# docker volume rm nginx
#注意,如果还有容器在使用这个卷,卷是删不掉的
#查看哪些容器在用这个卷
[root@hd1 html]# docker ps -a --filter volume=nginx
# 删除相关容器(假设容器名叫 nginx12)
[root@hd1 html]# docker rm -f nginx12
#再删卷就行了
[root@hd1 html]# docker volume rm nginx
进行两种挂载之后,即使容器被删除,宿主机对应目录也会保留相应的数据。两种挂载方式,如果在容器中修改内容,宿主机也会同步,宿主机修改内容,容器也会同步。并且在容器内删除文件,宿主机上对应的文件也会被删除,双向同步。
4.4 --mount 参数
我们可以发现,无论是 Volume 还是 Bind Mount,都用 -v 或 –mount 参数,他们的语法基本是一致的。因为 Docker 是通过 “第一个字段的格式” 来区分它们的,带 / 是bind挂载,不带 / 是volume卷
虽然 -v 依然能用,但 Docker 官方现在更推荐使用 --mount 参数,因为它语法更明确,不会混淆
语法: --mount type=<bind/volume> , source=<卷名/宿主机目录> , target=<容器目录> , 镜像名
# Bind Mount 用 --mount
docker run --mount type=bind,source=/my/html,target=/usr/share/nginx/html nginx
# Volume 用 --mount
docker run --mount type=volume,source=nginx,target=/usr/share/nginx/html nginx
#匿名卷省略 source 字段即可
docker run --mount type=volume,target=/usr/share/nginx/html nginx
五、Dockerfile 构建镜像
Dockerfile 是一个用来构建镜像的文本文件,文本内容包含了一条条构建镜像所需的指令和说明。
基于 Dockerfile 构建镜像可以使用 docker build 命令。docker build 命令中使用 -f 可以指定具体的 Dockerfile 文件。
5.1 第一个 Dockerfile 示例
注意:所有构建镜像的命令必须大写
# 创建目录用于存放 Dockerfile
[root@hd1 ~]# mkdir df
[root@hd1 ~]# cd df
# 编写 Dockerfile
[root@hd1 df]# vim df1
# 设置基础镜像为alpine。这个alpine 是本地已有的镜像(如果有),否则会从网上下载
FROM alpine
# 设置一些标签,解释和说明,没有实际意义
LABEL author=wm
# 运行命令。这些命令必须是你的基础镜像支持的
RUN echo 11111
# 利用 df1 构建镜像
[root@hd1 df]# docker build -t myalpine:v1 -f df1 .
# -t:指定构建镜像的名字
# -f:指定 Dockerfile 文件
# .:在当前目录中进行构建
# 运行镜像
[root@hd1 df]# docker run -t -d --name=ap1 myalpine:v1
# 查看是否在运行(如果不加 -t,容器会立刻退出,因为入口命令为 sh,需要一个伪终端)
[root@hd1 df]# docker ps | grep ap1
踩坑记录
在执行docker build的时候,忘记使用 -t 指定镜像名了,但他并没有报错,结果docker images查看的时候显示为:
REPOSITORY TAG IMAGE ID CREATED SIZE
<none> <none> a1b2c3d4e5f6 5 seconds ago 5.6MB
没有仓库名 (Repository) 也没有标签 (Tag),是一个 “悬空镜像” (dangling image),无法通过 docker run 或 docker push 等命令直接引用这个镜像
5.2 Dockerfile 指令详解
| 指令 | 说明 | 备注 |
|---|---|---|
| FROM | 基础镜像,必须是可以下载下来的(或者本地存在),定制的镜像都是基于 FROM 的镜像 | 必需 |
| LABEL | 指定镜像的作者信息 | 可选 |
| RUN | 指定在当前镜像构建过程中要运行的命令 | 功能的核心,可有多条 |
| EXPOSE | 仅仅只是声明端口(无实际作用) | 可选 |
| CMD | 容器运行时的入口命令,可被 docker run 命令行参数覆盖(比如docker run 启动容器结尾的/bin/bash) | 一般放在文件最后,可有多行,但只有最后一条生效 |
| ENTRYPOINT | 容器运行时的入口命令,不被 docker run 参数覆盖 | 只有最后一条生效 |
| COPY | 从宿主机复制文件或目录到容器里指定路径 | 官方推荐 |
| ADD | 复制指令,比 COPY 增加了 tar 包的自动解压和远程文件复制功能 | 功能更强 |
| VOLUME | 定义匿名数据卷,启动容器时忘记挂载则自动挂载到匿名卷 | 可选 |
| WORKDIR | 指定工作目录 | 可选 |
| ENV | 设置环境变量(构建和运行时均有效) | 可选 |
| ARG | 构建参数(仅构建时有效) | 可选 |
RUN,CMD,ENTRYPOINT使用方式是一致的,都有两种模式
Dockerfile 的模式非常固定:FROM → RUN → COPY → ENTRYPOINT
5.3 RUN 命令详解
RUN 指令指定在当前镜像构建过程中要运行的命令,用于在镜像层中安装软件、创建目录、修改配置等
有两种不同的模式:
1. Shell 模式(最常用)
RUN <command>
# 例:
RUN echo hello
2. Exec 模式(官方推荐)
- 语法:
RUN ["命令", "参数1", "参数2"]RUN ["解释器", "-c", "命令"](更推荐)
-c 作用是告诉解释器将后面跟着的那个字符串当作“要执行的命令”来处理
RUN ["executable", "param1", "param2"]
# 例:
RUN ["/bin/bash", "-c", "echo hello"]
RUN ["echo", "hello"]
5.4 CMD 命令详解
类似于 RUN 指令,用于运行程序,但二者运行的时间点不同:
- RUN:在
docker build构建镜像时运行 - CMD:在运行容器时运行,一般作为容器的主程序,需要能够长久运行且在前台运行(如ping这种一直作用的命令)。ENTRYPOINT与CMD要求一致
RUN,CMD,ENTRYPOINT使用方式是一致的
# 编写 Dockerfile
[root@hd1 df]# vim df1
FROM alpine
LABEL author=wm
EXPOSE 80 #EXPOSE没有实际作用
RUN echo 1111
CMD cat # cat 后面没有跟参数,默认是长久命令,且一直占据前端
# 构建镜像
[root@hd1 df]# docker build -t myalpine:v2 -f df1 .
# 运行容器
[root@hd1 df]# docker run -t -d --name=app2 myalpine:v2
# 查看入口命令(COMMAND下就是入口命令)
[root@hd1 df]# docker ps
ID IMAGE COMMAND CREATED STATUS NAMES
851 myalpine:v1 "/bin/sh -c cat" 20 seconds ago Up 20 seconds app1
注意: 如果入口不是长久命令,容器启动执行完毕后会自动退出。容器是为了应用而生,应用执行完毕,容器会自杀。
# 修改 Dockerfile,使用短时命令
[root@hd1 df]# cat df1
FROM alpine
LABEL author=wm
RUN echo 1111
CMD ls / # ls / 是短时命令,执行完毕后容器会自动退出
# 重新构建
[root@hd1 df]# docker build -t myalpine:v2 -f df1 .
[root@hd1 df]# docker run -t -d --name=app2 myalpine:v2
# 查看容器(已经退出)
[root@hd1 df]# docker ps -a
CMD 的重要特性:
多个 CMD 命令以最后一个生效。CMD 指令指定的程序可被 docker run 命令行参数覆盖。
[root@hd1 df]# cat df1
FROM alpine
LABEL author=wm
RUN echo 1111
CMD ["cat"]
#打包镜像
[root@hd1 df]# docker build -t myalpine:v3 -f df1 .
#守护进程方式运行容器
[root@hd1 df]# docker run -t -d --name=app3 myalpine:v3
# COMMAND 显示为 "cat"
[root@hd1 df]# docker ps
# 用 ping 127.0.0.1 覆盖 cat 命令
[root@hd1 df]# docker run -t -d --name=app30 myalpine:v3 ping 127.0.0.1
# COMMAND 显示为 "ping 127.0.0.1"
[root@hd1 df]# docker ps
#持续查看容器日志
[root@hd1 df]# docker logs app30 -f
5.5 ENTRY POINT 命令详解
类似于 CMD 指令,充作容器入口指令,但其不会被 docker run 的命令行参数所覆盖,而且这些命令行参数会被当作参数送给 ENTRYPOINT 指令指定的程序。
注意:
- CMD 和 ENTRYPOINT 最好不要一起使用
- 如果一起使用,CMD 只能作为 ENTRYPOINT 的参数
- 如果 Dockerfile 中存在多个 ENTRYPOINT 指令,仅最后一个生效
命令格式:
ENTRYPOINT ["executable", "param1", "param2"] # 推荐格式
[root@hd1 df]# vim df1
FROM alpine
LABEL author=wm
RUN echo 1111
ENTRYPOINT ["cat"]
[root@hd1 df]# docker build -t myalpine:v5 -f df1 .
[root@hd1 df]# docker run -t -d --name=app5 myalpine:v5
[root@hd1 df]# docker ps # COMMAND 显示为 "cat"
CMD 作为 ENTRYPOINT 的参数:
CMD命令和ENTRYPOINT命令最好不要一起使用,如果一起使用的话,CMD命令只能作为ENTRYPOINT命令的参数
[root@hd1 df]# cat df1
FROM alpine
LABEL author=wm
RUN echo 1111
CMD ["127.0.0.1"]
ENTRYPOINT ["ping"]
#打镜像包
[root@hd1 df]# docker build -t myalpine:v6 -f df1 .
#运行容器
[root@hd1 df]# docker run -t -d --name=app6 myalpine:v6
[root@hd1 df]# docker ps # COMMAND 显示为 "ping 127.0.0.1"
# 查看日志验证
[root@hd1 df]# docker logs app6 -f
64 bytes from 127.0.0.1: seq=51 ttl=64 time=0.041 ms
64 bytes from 127.0.0.1: seq=52 ttl=64 time=0.106 ms
5.6 COPY 和 ADD 命令
COPY
复制指令,从宿主机复制文件或目录到容器里指定路径。
格式:COPY [<src> <dest>]
<src源路径>:源文件或源目录,可以是通配符表达式<dest目标路径>:容器内的指定路径,路径不存在会自动创建
# 将 services 文件复制到当前目录
[root@hd1 df]# cp /etc/services .
# 修改 Dockerfile
[root@hd1 df]# vim df1
FROM alpine
LABEL author=wm
RUN echo 1111
COPY services /
CMD ["127.0.0.1"]
ENTRYPOINT ["ping"]
#构建镜像
[root@hd1 df]# docker build -t myalpine:v7 -f df1 .
#启动容器
[root@hd1 df]# docker run -t -d --name=app7 myalpine:v7
#无需进入容器,直接查看内容
[root@hd1 df]# docker exec -it app6 /bin/sh ls /
#可以看到,services文件已经被拷贝到容器根目录之下
bin etc lib mnt proc run services sys usr
dev home media opt root sbin srv tmp var
ADD
ADD 指令和 COPY 的使用格式一致(同样需求下,官方推荐使用 COPY)。功能类似,不同之处如下:
-
ADD 在执行
<源文件>为 tar 压缩文件时,会自动复制并解压到<目标路径> -
ADD 还支持远程文件的下载,直接从 URL 下载文件(但极其不推荐!)
- ADD http://example.com/bigfile.tar.gz /tmp/
-
因其功能透明性低,可能是复制、可能是解压、可能是下载,所以不建议使用
# 将 nginx.tar.gz 复制到当前目录
[root@hd1 df]# cp /root/nginx.tar.gz .
[root@hd1 df]# vim df1
FROM alpine
LABEL author=wm
RUN echo 1111
ADD nginx.tar.gz /fff/
CMD ["127.0.0.1"]
ENTRYPOINT ["ping"]
[root@hd1 df]# docker build -t myalpine:v8 -f df1 .
[root@hd1 df]# docker run -t -d --name=app8 myalpine:v8
[root@hd1 df]# docker exec -it app8 /bin/sh ls /fff/
5.7 VOLUME:定义匿名数据卷
定义匿名数据卷,在启动容器时如果用户没有挂载数据卷,会自动挂载到匿名卷。
作用:
- 避免重要的数据因容器重启而丢失(这是非常致命的)
- 避免容器不断变大
格式:
VOLUME ["<路径1>", "<路径2>..."]
VOLUME <路径>
VOLUME 指令只需要指定容器内的目录即可(可指定多个),他会挂载到 /var/lib/docker/volumes/ 目录下的一个长哈希随机ID文件夹
[root@hd1 df]# cat df1
FROM alpine
LABEL author=wm
RUN echo 1111
VOLUME ["/data"]
CMD ["127.0.0.1"]
ENTRYPOINT ["ping"]
5.8 WORKDIR:定义工作目录(了解)
指定工作目录。用 WORKDIR 指定的工作目录,会在构建镜像的每一层中都存在(WORKDIR 指定的工作目录必须是提前创建好的)。
注意: Docker build 构建镜像过程中,每一个 RUN 命令都是新建的一层(不存在上下文关系),只有通过 WORKDIR 创建的目录才会一直存在。
格式:
WORKDIR <工作目录路径>
WORKDIR /path/to/workdir
[root@hd1 df]# cat df1
FROM alpine
LABEL author=wm
RUN echo 1111
WORKDIR /abc
CMD ["127.0.0.1"]
ENTRYPOINT ["ping"]
我们登录进入容器,默认的路径就是/abc
5.9 ENV 和 ARG:环境变量设置(了解)
ENV(构建和运行时均有效)
ENV 设置的环境变量在构建镜像时可用,在容器运行时也有效。
[root@hd1 df]# cat df1
FROM alpine
LABEL author=wm
RUN echo 1111
ENV node=127.0.0.1
RUN echo $node
CMD echo $node
[root@hd1 df]# docker build -t myalpine:v16 -f df1 .
[root@hd1 df]# docker run -t -d --name=app16 myalpine:v16
#在构建镜像时的输出内容就可以显示RUN命令生效
#上述命令入口echo $node 是短时命令,会退出,通过docker logs 来看
[root@hd1 df]# docker logs app16
127.0.0.1
ARG(仅构建时有效)
ARG 设置的环境变量仅对 Dockerfile 内有效,即只有在 docker build 过程中有效,(因为 ARG 变量只存在于构建阶段)构建好的镜像内不存在此环境变量。
[root@hd1 df]# cat df1
FROM alpine
LABEL author=wm
RUN echo 1111
ARG node=127.0.0.1
RUN echo $node
CMD echo $node # 运行时 $node 为空,无效
六、常见面试题
RUN 和 CMD 的区别
| 指令 | 执行时机 | 特点 |
|---|---|---|
| RUN | 构建镜像时执行 | 可以有多个,没有上下文关系 |
| CMD | 运行容器时执行 | 可以有多个,但只有最后一条生效 |
ENTRYPOINT同样在运行容器时执行,可以有多个,但只有最后一条生效
CMD 和 ENTRYPOINT 的异同
相同点:
- 都是容器运行的入口
- 要求后面跟着的命令必须能够长久执行
不同点:
- CMD 可被
docker run命令行参数覆盖 - ENTRYPOINT 不被覆盖,命令行参数会被当作参数传给 ENTRYPOINT
- CMD 可以作为 ENTRYPOINT 的参数(两个命令的格式必须是列表形式 exec)
ADD 和 COPY 的区别
- ADD 支持 tar 包的自动解压
- ADD 支持远程文件下载
- 官方推荐使用 COPY(更透明,更可预测)
七、实用技巧
7.1 统一时区(重要)
默认镜像时间与宿主机相差 8 小时,这是时区差异导致的。生产环境下,时区不一致会导致业务出问题。
有两种方法解决这个问题,二选一即可:
# 方法1:挂载宿主机时区文件
[root@hd1 df]# docker run -t -d -v /etc/localtime:/etc/localtime myalpine:v1
# 方法2:将时区文件拷贝到容器内
[root@hd1 df]# docker cp /usr/share/zoneinfo/Asia/Shanghai app7:/etc/localtime
最佳实践: 如果觉得每次挂载时区文件很麻烦,可以自定义时区后保存为新镜像再使用。
7.2 容器退出时自动删除
# --rm:当容器退出时,自动销毁容器自身
docker run --rm --name=haha -it busybox:V1 /bin/sh
八、核心命令速查表
在 Docker 中,容器 ID 和 容器名(Name) 是等价的标识符,绝大多数对容器操作的命令都同时支持这两种输入方式。
(镜像名和镜像ID也基本如此,但镜像需考虑标签等)
| 命令 | 用途 |
|---|---|
docker images | 查看本地镜像列表 |
docker pull | 从仓库拉取镜像 |
docker load -i | 导入离线镜像包 |
docker save -o | 导出镜像为离线包 |
docker rmi | 删除镜像 |
docker run | 创建并启动容器 |
docker ps | 查看运行中的容器 |
docker ps -a | 查看所有容器(含已停止) |
docker stop | 停止容器 |
docker start | 启动已停止的容器 |
docker restart | 重启容器 |
docker kill | 强制杀死容器 |
docker rm | 删除容器 |
docker rm -f | 强制删除运行中的容器 |
docker exec -it | 进入运行中的容器 |
docker logs | 查看容器日志 |
docker inspect | 查看容器/镜像底层信息 |
docker commit | 将容器保存为镜像 |
docker cp | 宿主机与容器间复制文件 |
docker top | 查看容器内进程 |
docker stats | 查看容器资源占用 |
docker update | 修改运行中容器的配置 |
docker volume ls | 列出数据卷 |
docker volume prune | 删除无用数据卷 |
docker build -t | 构建镜像 |
docker history | 查看镜像构建历史 |
docker system prune | 清理未使用的资源 |
九、Docker 踩坑记录
以下是我在实际操作过程中总结的经验教训
9.1 -p 端口映射默认绑定 0.0.0.0,存在安全风险
现象:
执行 docker run -p 33060:3306 mysql,宿主机上任何人都可以通过 公网IP:33060 访问你的数据库,在实验环境还好,但是在真实的生产环境中安全风险极大。
原因:
-p 默认绑定到 0.0.0.0(所有网卡)。若只想本机访问,应绑定到 127.0.0.1。
正确做法:
# 只允许本机访问
docker run -p 127.0.0.1:33060:3306 mysql
9.2 Bind Mount 空目录导致容器内原有文件被“清空”
现象:
执行 docker run --name=nginxa -d -p 8000:80 -v /my/html:/usr/share/nginx/html nginx:latest ,访问http://192.168.1.11:8000/却报 403
原因:
Bind Mount 是直接覆盖(遮蔽),宿主机空目录会让容器内的 /usr/share/nginx/html 变成空白目录,导致 Nginx 找不到配置。
解决方案:
- 确保宿主机目录非空,或先拷贝一份配置文件进去。
- 若只是想持久化数据且不希望覆盖,改用 命名卷(Volume),空卷会自动复制容器原内容。
9.3 Alpine 镜像里没有 /bin/bash,只有 /bin/sh
现象:
执行 docker exec -it alpine容器 /bin/bash 报错 rpc error: code = 2 exec: "/bin/bash": stat /bin/bash: no such file or directory。
原因:
Alpine 为了极致小巧,默认不带 Bash,只提供 sh。
正确做法:
docker exec -it 容器名 /bin/sh
若确实需要 Bash,可在 Dockerfile 中添加 RUN apk add --no-cache bash。
9.4 容器内进程不是长期运行的,容器会立刻退出
现象:
docker run --name test alpine ls / 之后 docker ps 看不到容器,但 docker ps -a 能看到状态 Exited (0)。
原因:
容器的生命周期与主进程绑定,ls / 执行完就结束了,容器自然退出。
解决方案:
- 使用长期运行命令,如
ping、nginx -g 'daemon off;'、tail -f /dev/null(调试用)。 - 若必须用 Alpine 做调试,可加
-t并保持交互:docker run -it --name test alpine /bin/sh
9.5 docker run 时忘记 -t,导致交互式进程瞬间退出
现象:
执行 docker run -d alpine /bin/sh,容器启动后立即退出。
原因:
/bin/sh 需要伪终端(TTY),没有 -t 时,Shell 无法分配终端而直接退出。
(注:nginx、redis 等守护进程不需要 -t,所以不会踩坑)
正确做法:
docker run -td --name alpine1 alpine /bin/sh # -t 和 -d 同时使用
9.6 docker build 未指定 -t 产生悬空镜像,无法直接 run
现象:
docker build -f df1 . 成功,但 docker images 中出现 <none>:<none>。
后果:
无法通过 docker run <镜像名> 引用,只能通过 IMAGE ID 运行(不直观)。
最佳实践:
始终加上 -t,哪怕临时测试:
docker build -t mytemp:test -f df1 .
9.7 docker logs 日志文件无限增长,撑爆磁盘
现象:
创建业务容器不对日志文件进行限制, /var/lib/docker/containers 下某个容器的日志文件会无限增长。
解决方案:
- 启动容器时限制日志大小:
docker run --log-opt max-size=10m --log-opt max-file=3 -d nginx - 或在
/etc/docker/daemon.json中全局配置:{ "log-driver": "json-file", "log-opts": { "max-size": "10m", "max-file": "3" } }
9.8 数据卷权限问题:容器内用户写不进挂载目录
现象:
容器内进程(如 MySQL 以 mysql 用户运行)无法在 Bind Mount 的宿主机目录中创建文件,报 Permission denied的错误。
原因:
容器内 UID(如 999)与宿主机目录的属主(如 root)不匹配,且目录权限不足。
解决方案:
- 方案一:挂载前将宿主目录权限改为
777(不推荐,不安全)。 - 方案二:在 Dockerfile 中创建与宿主机相同 UID 的用户,或使用
--user参数指定运行用户。 - 方案三:使用命名卷(Volume),Docker 会自动处理权限(通常以 root 创建,但容器内用户需有写入能力,必要时加
:Z或:zSELinux 标签)。
–privileged \ # --privileged:特权容器,容器内使用真正的 root 用户
9.9 latest 标签陷阱:并非总是“最新”
现象:
本地已有 nginx:latest(几个月前的),执行 docker pull nginx:latest 发现已是最新(实际有更新但未拉取)。
原因:
latest 只是一个普通标签,Docker 不会自动检查远程更新。若本地已有该标签,docker pull 会认为是已存在而跳过(除非强制拉取)。
最佳实践:
- 生产环境务必使用具体版本号,如
nginx:1.25.3。 - 若确实需要最新,先
docker pull加上--no-cache强制刷新。
9.10 COPY/ADD 的源路径是相对于 构建的上下文,而非 Dockerfile 所在目录
现象:
终端所在目录:/home/user/project/
Dockerfile 实际位置:/path/to/Dockerfile
我要复制的 app 目录:/path/to/app(和 Dockerfile 在同一个目录下)
在/执行 docker build -f /path/to/Dockerfile .,/path/to/Dockerfile的目录下还有:./app,但文件中的 COPY ./app 提示找不到 ./app。
原因:
docker build 的最后一个参数是构建上下文(.),COPY 的源路径是相对于该上下文的,而不是 Dockerfile 所在目录。即使 -f 指定了其他路径的 Dockerfile,上下文仍由 . 决定。
“构建上下文”指的是 Docker 客户端(Client)会把这个目录下的所有文件(不包括 .dockerignore 排除的)打包成一个压缩包(Tarball),发送给 Docker 服务端(Daemon)去构建镜像
正确做法:
把终端 cd 到 Dockerfile 所在的目录,然后再执行 docker build .
9.11 容器内 systemctl / systemd 无法使用
现象:
在基于 CentOS 的容器中执行 systemctl start nginx 报错 Failed to get D-Bus connection。
原因:
容器没有运行 systemd 作为 PID 1,也没有 D-Bus 会话。
解决方案:
- 不使用 systemd,直接运行二进制命令(如
/usr/sbin/nginx)。 - 若必须使用 systemd,需以特权模式运行并挂载相关文件,但强烈不推荐(违背容器单进程原则)。
9.12 容器时间与宿主机相差 8 小时(时区问题)
现象:
容器内 date 显示 UTC 时间,而宿主机是 CST(中国时区)。
解决方案(二选一):
# 1. 挂载宿主机 localtime
docker run -t -d -v /etc/localtime:/etc/localtime myalpine:v1
# 2. 在 Dockerfile 中设置时区(Alpine)
RUN apk add --no-cache tzdata && cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
笔记前文已有提及
9.13 docker rm -f $(docker ps -aq) 误删所有容器,包括重要的
现象:
本想清理测试容器,结果执行了上述命令,所有运行中的容器被强制删除,数据丢失(若未挂载卷)。
教训:
- 操作前先
docker ps确认当前环境没有需保留的业务镜像。 - 生产环境慎用
-f,尽量先stop再rm。 - 重要的数据务必挂载到 Volume 或 Bind Mount。
9.14 docker system prune -a 误删未使用的镜像,导致下次构建拉取缓慢
docker system prune -a是一个磁盘清理命令,但它的作用范围非常广,是一个高危的“大扫除”指令。
它的作用是:删除所有未被使用的资源,包括停止的容器、未使用的网络、构建缓存,以及所有未被任何容器引用的镜像(不仅仅是悬空镜像)
现象:
执行 docker system prune -a -f 清理后,之前缓存的大量中间层镜像被删除,再次构建时又需要重新下载,浪费时间和流量。
建议:
- 定期清理时使用
docker system prune(不加-a),只删除悬空镜像和停止的容器。 - 如需深度清理,评估后再加
-a。
9.15 镜像加速器配置错误,docker pull 总是超时
现象:
docker pull 卡住或报错 net/http: request canceled。
排查:
- 检查
/etc/docker/daemon.json中registry-mirrors的地址是否可用(可访问https://docker.m.daocloud.io/v2/测试)。 - 修改后必须执行
systemctl daemon-reload && systemctl restart docker生效。 - 可以临时切换其他公共加速源(如
dockerproxy.com等)。
更多推荐
所有评论(0)