Docker 学习笔记
本笔记是个人收集整理的,后面的实操内容是看B站狂神老师的Docker教程做到笔记,【狂神说Java】Docker最新超详细版教程通俗易懂,对于入门的小白来说,看完确实是可以快速上手,不过有一些概念原理说的不是很清楚,我会在笔记整理docker相关的概念和技术原理,从而对Docker会有一个更深入的了解。
参考Github的入门教程
- 链接:https://github.com/jaywcjlove/docker-tutorial
- Docker学习思维导图:

- 相关链接:
- https://github.com/eon01/DockerCheatSheet
- http://www.dockerinfo.net/document
- https://github.com/llitfkitfk/docker-tutorial-cn
Docker简介
Docker 是一个开源的应用容器引擎,而一个容器containers其实是一个虚拟化的独立的环境,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化。容器是完全使用沙箱机制,相互之间不会有任何接口。
Docker 的局限性之一,它只能用在 64 位的操作系统上。
定义:
Docker是一个虚拟环境容器,可以将你的开发环境、代码、配置文件等一并打包到这个容器中,并发布和应用到任意平台中。 原理: docker底层使用了LXC来实现,LXC将linux进程沙盒化,使得进程之间相互隔离,并且能够协调各进程的资源分配。 LXC: LXC为Linux Container的简写。可以提供轻量级的虚拟化,以便隔离进程和资源,而且不需要提供指令解释机制以及全虚拟化的其他复杂性。
Docker三个重要概念:
- 镜像(Image):类似于虚拟机中的镜像,是一个包含有文件系统的面向Docker引擎的只读模板。任何应用程序运行都需要环境,而镜像就是用来提供这种运行环境的。
- 容器(Container):类似于一个轻量级的沙盒,可以将其看作一个极简的Linux系统环境(包括root权限、进程空间、用户空间和网络空间等),以及运行在其中的应用程序。Docker引擎利用容器来运行、隔离各个应用。容器是镜像创建的应用实例,可以创建、启动、停止、删除容器,各个容器之间是是相互隔离的,互不影响。注意:镜像本身是只读的,容器从镜像启动时,Docker在镜像的上层创建一个可写层,镜像本身不变。
- 仓库(Repository):类似于代码仓库,这里是镜像仓库,是Docker用来集中存放镜像文件的地方。注意与注册服务器(Registry)的区别:注册服务器是存放仓库的地方,一般会有多个仓库;而仓库是存放镜像的地方,一般每个仓库存放一类镜像,每个镜像利用tag进行区分。
容器和虚拟机的区别:
- 容器是应用程序层的抽象,将代码和依赖项打包在一起。多个容器可以在同一台计算机上运行,并与其他容器共享OS内核,每个容器在用户空间中作为隔离的进程运行。容器占用的空间少于VM(容器映像的大小通常为几十MB),可以处理更多的应用程序,并且需要的VM和操作系统更少。
- 虚拟机(VM)是将一台服务器转变为多台服务器的物理硬件的抽象。系统管理程序允许多个VM在单台计算机上运行。每个VM包含操作系统,应用程序,必要的二进制文件和库的完整副本-占用数十GB。VM也可能启动缓慢。
- 容器不是一台机器。Docker 利用的是 Linux 的资源分离机制,例如 cgroups,以及 Linux 核心命名空间(namespaces),来建立独立的容器(containers)。容器看上去是一台机器,实际上是一个进程。
- 相比于虚拟机,容器的优势主要有:
- 资源占用少
- 启动速度快
- 本身体积小
| Docker容器 | LXC | VM | |
|---|---|---|---|
| 虚拟机类型 | OS虚拟化 | OS虚拟化 | 硬件虚拟化 |
| 性能 | =物理机性能 | =物理机性能 | 5%-20%损耗 |
| 隔离性 | NS隔离 | NS隔离 | 强 |
| QoS | Cgroup弱 | Cgroup弱 | 强 |
| 安全性 | 中 | 差 | 强 |
| GuestOS | 全部 | 只支持Linux | 全部 |
安装并运行第一个Docker镜像
- 在Ubuntu系统安装Docker驱动:官方教程
- 卸载旧版本
$ sudo apt-get remove docker docker-engine docker.io containerd runc
apt-get如果报告没有安装这些软件包,那也没关系。
使用存储库安装
在新主机上首次安装 Docker Engine 之前,您需要设置 Docker 存储库。之后,您可以从存储库安装和更新 Docker。
- 更新apt包索引并安装包以允许apt通过 HTTPS 使用存储库:
$ sudo apt-get update
$ sudo apt-get install \
ca-certificates \
curl \
gnupg \
lsb-release
- 添加 Docker 的官方 GPG 密钥:
$ sudo mkdir -p /etc/apt/keyrings
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
- 使用以下命令设置存储库:
$ echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
安装 Docker 引擎
- 更新apt包索引,安装最新版本的 Docker Engine、containerd 和 Docker Compose,或者进入下一步安装特定版本:
$ sudo apt-get update
$ sudo apt-get install docker-ce docker-ce-cli containerd.io docker-compose-plugin
- 要安装特定版本的 Docker Engine,请在 repo 中列出可用版本,然后选择并安装:
一个。列出您的存储库中可用的版本:
$ apt-cache madison docker-ce
docker-ce | 5:20.10.17~3-0~ubuntu-focal | https://download.docker.com/linux/ubuntu focal/stable amd64 Packages
docker-ce | 5:20.10.16~3-0~ubuntu-focal | https://download.docker.com/linux/ubuntu focal/stable amd64 Packages
docker-ce | 5:20.10.15~3-0~ubuntu-focal | https://download.docker.com/linux/ubuntu focal/stable amd64 Packages
docker-ce | 5:20.10.14~3-0~ubuntu-focal | https://download.docker.com/linux/ubuntu focal/stable amd64 Packages
docker-ce | 5:20.10.13~3-0~ubuntu-focal | https://download.docker.com/linux/ubuntu focal/stable amd64 Packages
使用第二列中的版本字符串安装特定版本,例如5:20.10.17~3-0~ubuntu-focal
$ sudo apt-get install docker-ce=<VERSION_STRING> docker-ce-cli=<VERSION_STRING> containerd.io docker-compose-plugin
# 安装5:20.10.17~3-0~ubuntu-focal
$ sudo apt-get install docker-ce=5:20.10.17~3-0~ubuntu-focal docker-ce-cli=5:20.10.17~3-0~ubuntu-focal containerd.io docker-compose-plugin
hello-world通过运行映像来验证 Docker 引擎是否已正确安装。
$ sudo docker run hello-world
结果如下:
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
2db29710123e: Pull complete
Digest: sha256:80f31da1ac7b312ba29d65080fddf797dd76acfb870e677f390d5acba9741b17
Status: Downloaded newer image for hello-world:latest
Hello from Docker!
This message shows that your installation appears to be working correctly.
卸载 Docker 引擎
- 卸载 Docker Engine、CLI、Containerd 和 Docker Compose 软件包:
$ sudo apt-get purge docker-ce docker-ce-cli containerd.io docker-compose-plugin
- 主机上的映像、容器、卷或自定义配置文件不会自动删除。要删除所有映像、容器和卷:
$ sudo rm -rf /var/lib/docker
$ sudo rm -rf /var/lib/containerd
您必须手动删除任何已编辑的配置文件。
设置非root用户不用sudo运行docker命令
- 创建名为docker的组,如果之前已经有该组就会报错,可以忽略这个错误:
$ sudo groupadd docker
- 将当前用户加入组docker:
$ sudo gpasswd -a ${USER} docker
- 重新注销用户再登录,打开终端运行docker命令可以不用加
sudo了。
Docker命令
帮助命令
$ docker version # 显示docker的版本信息
$ docker info # 显示docker系统信息,包括镜像和容器的数量
$ docker 命令 --help # 帮助命令
镜像命令
docker images查看所有本地主机上的镜像,docker images命令
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
new_dingbot_msg latest cde233707f86 14 hours ago 437MB
dingmsg_service latest 269caf0cc7eb 21 hours ago 437MB
ubuntu 20.04 20fffa419e3a 3 days ago 72.8MB
httpd latest 98f93cd0ec3b 13 days ago 144MB
hello-world latest feb5d9fea6a5 8 months ago 13.3kB
# 解释
REPOSITORY 镜像的仓库源
TAG 镜像的标签
IMAGE ID 镜像的id
CREATED 镜像的创建时间
SIZE 镜像的大小
# 可选项
-a, --all # 显示所有镜像
-q, --quiet # 显示镜像的id
docker search搜索镜像,docker search
$ docker search mysql
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
mysql MySQL is a widely used, open-source relation… 12708 [OK]
mariadb MariaDB Server is a high performing open sou… 4878 [OK]
percona Percona Server is a fork of the MySQL relati… 579 [OK]
phpmyadmin phpMyAdmin - A web interface for MySQL and M… 553 [OK]
bitnami/mysql Bitnami MySQL Docker Image 71 [OK]
# 可选项,通过搜索来过滤
--filter , -f=STARS=3000 #搜索镜像的STARS大于3000的
$ docker search mysql -f=stars=3000
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
mysql MySQL is a widely used, open-source relation… 12708 [OK]
mariadb MariaDB Server is a high performing open sou… 4878 [OK]
docker pull下载镜像,docker pull
# 下载镜像 docker pull 镜像名[:tag]
$ docker pull mysql
Using default tag: latest # 如果不写tag,默认的是latest
latest: Pulling from library/mysql
c1ad9731b2c7: Pull complete # 分层下载 docker image 的核心 联合文件系统
54f6eb0ee84d: Pull complete
cffcf8691bc5: Pull complete
89a783b5ac8a: Pull complete
6a8393c7be5f: Pull complete
af768d0b181e: Pull complete
810d6aaaf54a: Pull complete
2e014a8ae4c9: Pull complete
a821425a3341: Pull complete
3a10c2652132: Pull complete
4419638feac4: Pull complete
681aeed97dfe: Pull complete
Digest: sha256:548da4c67fd8a71908f17c308b8ddb098acf5191d3d7694e56801c6a8b2072cc # 签名信息
Status: Downloaded newer image for mysql:latest
docker.io/library/mysql:latest # 真实地址
# 等价于
$ docke pull mysql
$ docke pull docker.io/library/mysql:latest
# 指定版本下载
$ docker pull mysql:5.7
5.7: Pulling from library/mysql
c1ad9731b2c7: Already exists # 之前下载过的镜像是可以共用的,大大节省内存 联合文件系统最高明之处
54f6eb0ee84d: Already exists
cffcf8691bc5: Already exists
89a783b5ac8a: Already exists
6a8393c7be5f: Already exists
af768d0b181e: Already exists
810d6aaaf54a: Already exists
81fe6daf2395: Pull complete
5ccf426818fd: Pull complete
68b838b06054: Pull complete
1b606c4f93df: Pull complete
Digest: sha256:7e99b2b8d5bca914ef31059858210f57b009c40375d647f0d4d65ecd01d6b1d5
Status: Downloaded newer image for mysql:5.7
docker.io/library/mysql:5.7
# 查看镜像
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
mysql 5.7 2a0961b7de03 13 days ago 462MB
mysql latest 65b636d5542b 13 days ago 524MB
docker rmi删除镜像,docker rmi
$ docker rmi -f 容器id # 删除指定的容器
$ docker rmi -f 容器id 容器id 容器id 容器id 容器id # 删除多个容器
$ docker rmi -f $(docker images -aq) # 删除全部容器
容器命令
- 说明:我们有了镜像才能创建容器,下载centos镜像来测试
$ docker pull centos
- 新建容器并启动,docker run
$ docker run [可选参数] image
# 参数选项
--name "Name" # 容器名字 centos1 centos2 用于区分容器
-d # 后台运行方式
-it # 使用交互方式执行,进入容器查看内容
-p # 指定容器端口 -p 8080:80
-p ip:主机端口:容器端口
-p 主机端口:容器端口(常用)
-p 容器端口
容器端口
-p # 随机指定端口3
# 测试,启动进入容器
$ docker run -it centos /bin/bash
[root@f766035e7769 /]# ls
bin dev etc home lib lib64 lost+found media mnt opt proc root run sbin srv sys tmp usr var
# 从容器退回主机
$ [root@f766035e7769 /]# exit
exit
docker ps列出所有运行的容器
$ docker ps 命令
# 列出当前真在运行的的容器
-a # 列出当前正在运行的容器+历史运行的容器
-n=? # 显示最近创建的容器
-q # 显示容器的编号
# 列出当前真在运行的的容器
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
7e61c738bcf4 dingmsg_service "python3 app/lemumsg…" 22 hours ago Up 22 hours 0.0.0.0:6655->6655/tcp, :::6655->6655/tcp dingbot-test
986f882220ef httpd "httpd-foreground" 40 hours ago Up 17 hours 0.0.0.0:8877->80/tcp, :::8877->80/tcp apache-test
# 列出当前正在运行的容器+历史运行的容器
$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
f766035e7769 centos "/bin/bash" 20 minutes ago Exited (0) 5 minutes ago beautiful_proskuriakova
7e61c738bcf4 dingmsg_service "python3 app/lemumsg…" 22 hours ago Up 22 hours 0.0.0.0:6655->6655/tcp, :::6655->6655/tcp dingbot-test
286484b695e4 20fffa419e3a "/bin/sh -c 'apt-get…" 25 hours ago Exited (127) 25 hours ago nervous_kilby
# 显示最近创建的容器
$ docker ps -n=1
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
f766035e7769 centos "/bin/bash" 18 minutes ago Exited (0) 3 minutes ago beautiful_proskuriakova
# 显示容器的编号
$ docker ps -aq
f766035e7769
7e61c738bcf4
286484b695e4
- 退出容器
$ exit # 直接容器停止并退出
$ Ctrl + P + Q # 容器不停止推出
- 删除容器
$ docker rm 容器id # 删除制定的容器,不能删除正在运行的容器,如果要强制删除需要使用 rm -f
$ docker rm -f $(docker ps -aq) # 删除所有容器
$ docker ps -a -q | xargs docker rm # 删除所有的容器
- 启动和停止容器的操作
$ docker start 容器id # 启动容器
$ docker restart 容器id # 重启容器
$ docker stop 容器id # 停止当前正在运行的的容器
$ docker kill 容器id # 强制停止当前容器
- 常用的其他命令
# 命令:docker run -d 镜像名
$ docker run -d centos
# 问题docker ps ,发现centos停止了
# 常见的坑:docker 容器使用后台运行,就必须要有一个前台进程,docker发现没有应用就会自动停止
# apache ,容器启动后,发现自己没有提供服务,就会停止,就没有程序了。
- 查看日志,docker logs
$ docker logs -tf --tail 10 容器id 没有日志
# 如果docker镜像启动本来就没有日志,可以编写一个脚本进行测试
$ docker run -d centos /bin/sh -c "while true;do echo hello docker;sleep 1;done"
8095f554dbd16c29ef61182a12813756458a0a24d09bdbe761142dac1c971e7c
# 查看运行docker
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
be72714b0e60 centos "/bin/sh -c 'while t…" 16 seconds ago Up 15 seconds jovial_zhukovsky
# 显示日志
-tf # 显示时间和日志
--tail num # 显示日志条数
# 显示10行的be72714b0e60的日志
$ docker logs -tf --tail 10 be72714b0e60
2022-06-10T12:52:09.983041830Z hello docker
2022-06-10T12:52:10.984397462Z hello docker
2022-06-10T12:52:11.985690607Z hello docker
2022-06-10T12:52:12.986983999Z hello docker
2022-06-10T12:52:13.988244570Z hello docker
2022-06-10T12:52:14.989404639Z hello docker
2022-06-10T12:52:15.990945974Z hello docker
2022-06-10T12:52:16.992115773Z hello docker
2022-06-10T12:52:17.993503637Z hello docker
2022-06-10T12:52:18.995010360Z hello docker
- 查看容器进程信息
$ docker top 容器id # 查看指定容器的进程信息
$ $ docker top be72714b0e60
UID PID PPID C STIME TTY TIME CMD
root 650475 650452 0 20:50 ? 00:00:00 /bin/sh -c while true;do echo hello docker;sleep 1;done
root 682489 650475 0 21:17 ? 00:00:00 /usr/bin/coreutils --coreutils-prog-shebang=sleep /usr/bin/sleep 1
- 查看镜像的元数据
# 命令
$ docker inspect 容器id
# 查看centos 容器的元数据
$ docker inspect be72714b0e60
[
{
"Id": "be72714b0e60f4911bc807bbb4597e139f6eaa829c9ac117b76e2f13b87c1bf2",
"Created": "2022-06-10T12:50:51.582969979Z",
"Path": "/bin/sh",
"Args": [
"-c",
"while true;do echo hello docker;sleep 1;done"
],
"State": {
"Status": "running",
"Running": true,
"Paused": false,
"Restarting": false,
"OOMKilled": false,
"Dead": false,
"Pid": 650475,
"ExitCode": 0,
"Error": "",
"StartedAt": "2022-06-10T12:50:51.87753439Z",
"FinishedAt": "0001-01-01T00:00:00Z"
},
"Image": "sha256:5d0da3dc976460b72c77d94c8a1ad043720b0416bfc16c52c45d4847e53fadb6",
"ResolvConfPath": "/var/lib/docker/containers/be72714b0e60f4911bc807bbb4597e139f6eaa829c9ac117b76e2f13b87c1bf2/resolv.conf",
"HostnamePath": "/var/lib/docker/containers/be72714b0e60f4911bc807bbb4597e139f6eaa829c9ac117b76e2f13b87c1bf2/hostname",
"HostsPath": "/var/lib/docker/containers/be72714b0e60f4911bc807bbb4597e139f6eaa829c9ac117b76e2f13b87c1bf2/hosts",
"LogPath": "/var/lib/docker/containers/be72714b0e60f4911bc807bbb4597e139f6eaa829c9ac117b76e2f13b87c1bf2/be72714b0e60f4911bc807bbb4597e139f6eaa829c9ac117b76e2f13b87c1bf2-json.log",
"Name": "/jovial_zhukovsky",
"RestartCount": 0,
"Driver": "overlay2",
"Platform": "linux",
"MountLabel": "",
"ProcessLabel": "",
"AppArmorProfile": "docker-default",
"ExecIDs": null,
"HostConfig": {
"Binds": null,
"ContainerIDFile": "",
"LogConfig": {
"Type": "json-file",
"Config": {}
},
"NetworkMode": "default",
"PortBindings": {},
"RestartPolicy": {
"Name": "no",
"MaximumRetryCount": 0
},
"AutoRemove": false,
"VolumeDriver": "",
"VolumesFrom": null,
"CapAdd": null,
"CapDrop": null,
"CgroupnsMode": "host",
"Dns": [],
"DnsOptions": [],
"DnsSearch": [],
"ExtraHosts": null,
"GroupAdd": null,
"IpcMode": "private",
"Cgroup": "",
"Links": null,
"OomScoreAdj": 0,
"PidMode": "",
"Privileged": false,
"PublishAllPorts": false,
"ReadonlyRootfs": false,
"SecurityOpt": null,
"UTSMode": "",
"UsernsMode": "",
"ShmSize": 67108864,
"Runtime": "runc",
"ConsoleSize": [
0,
0
],
"Isolation": "",
"CpuShares": 0,
"Memory": 0,
"NanoCpus": 0,
"CgroupParent": "",
"BlkioWeight": 0,
"BlkioWeightDevice": [],
"BlkioDeviceReadBps": null,
"BlkioDeviceWriteBps": null,
"BlkioDeviceReadIOps": null,
"BlkioDeviceWriteIOps": null,
"CpuPeriod": 0,
"CpuQuota": 0,
"CpuRealtimePeriod": 0,
"CpuRealtimeRuntime": 0,
"CpusetCpus": "",
"CpusetMems": "",
"Devices": [],
"DeviceCgroupRules": null,
"DeviceRequests": null,
"KernelMemory": 0,
"KernelMemoryTCP": 0,
"MemoryReservation": 0,
"MemorySwap": 0,
"MemorySwappiness": null,
"OomKillDisable": false,
"PidsLimit": null,
"Ulimits": null,
"CpuCount": 0,
"CpuPercent": 0,
"IOMaximumIOps": 0,
"IOMaximumBandwidth": 0,
"MaskedPaths": [
"/proc/asound",
"/proc/acpi",
"/proc/kcore",
"/proc/keys",
"/proc/latency_stats",
"/proc/timer_list",
"/proc/timer_stats",
"/proc/sched_debug",
"/proc/scsi",
"/sys/firmware"
],
"ReadonlyPaths": [
"/proc/bus",
"/proc/fs",
"/proc/irq",
"/proc/sys",
"/proc/sysrq-trigger"
]
},
"GraphDriver": {
"Data": {
"LowerDir": "/var/lib/docker/overlay2/0fc9cdacc3b9f7bb1a6445173c604dafba45749458ba4c8500dad1a8aac09b3a-init/diff:/var/lib/docker/overlay2/643b7419a5e24b28b4b201c15accae5d5b1a0caf84229d9cff1d8c068f18ac8e/diff",
"MergedDir": "/var/lib/docker/overlay2/0fc9cdacc3b9f7bb1a6445173c604dafba45749458ba4c8500dad1a8aac09b3a/merged",
"UpperDir": "/var/lib/docker/overlay2/0fc9cdacc3b9f7bb1a6445173c604dafba45749458ba4c8500dad1a8aac09b3a/diff",
"WorkDir": "/var/lib/docker/overlay2/0fc9cdacc3b9f7bb1a6445173c604dafba45749458ba4c8500dad1a8aac09b3a/work"
},
"Name": "overlay2"
},
"Mounts": [],
"Config": {
"Hostname": "be72714b0e60",
"Domainname": "",
"User": "",
"AttachStdin": false,
"AttachStdout": false,
"AttachStderr": false,
"Tty": false,
"OpenStdin": false,
"StdinOnce": false,
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
],
"Cmd": [
"/bin/sh",
"-c",
"while true;do echo hello docker;sleep 1;done"
],
"Image": "centos",
"Volumes": null,
"WorkingDir": "",
"Entrypoint": null,
"OnBuild": null,
"Labels": {
"org.label-schema.build-date": "20210915",
"org.label-schema.license": "GPLv2",
"org.label-schema.name": "CentOS Base Image",
"org.label-schema.schema-version": "1.0",
"org.label-schema.vendor": "CentOS"
}
},
"NetworkSettings": {
"Bridge": "",
"SandboxID": "2e1d253296845eb7e8e5dc418d9a2b9fa7a39b38e470f632270f3bb3612db632",
"HairpinMode": false,
"LinkLocalIPv6Address": "",
"LinkLocalIPv6PrefixLen": 0,
"Ports": {},
"SandboxKey": "/var/run/docker/netns/2e1d25329684",
"SecondaryIPAddresses": null,
"SecondaryIPv6Addresses": null,
"EndpointID": "98e8d239576f58070b4898d4ef16dc89a9afdb7f07d96f9980b52efeb028f2af",
"Gateway": "172.17.0.1",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"IPAddress": "172.17.0.6",
"IPPrefixLen": 16,
"IPv6Gateway": "",
"MacAddress": "02:42:ac:11:00:06",
"Networks": {
"bridge": {
"IPAMConfig": null,
"Links": null,
"Aliases": null,
"NetworkID": "d64c7bbc42f591f3d4e7ce51fdbf9a93ac1bcf29459995c66db327dfce79003b",
"EndpointID": "98e8d239576f58070b4898d4ef16dc89a9afdb7f07d96f9980b52efeb028f2af",
"Gateway": "172.17.0.1",
"IPAddress": "172.17.0.6",
"IPPrefixLen": 16,
"IPv6Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"MacAddress": "02:42:ac:11:00:06",
"DriverOpts": null
}
}
}
}
]
- 进入当前正在运行的容器
# 我们通常容器都是使用后台方式运行的,需要进入容器,修改一些配置
# 命令
$ docker exec -it 容器id bashshell
# 测试
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
be72714b0e60 centos "/bin/sh -c 'while t…" 51 minutes ago Up 51 minutes jovial_zhukovsky
lemu-devops@lemudevops:~$ docker exec -it be72714b0e60 /bin/bash
[root@be72714b0e60 /]# ps -ef
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 12:50 ? 00:00:00 /bin/sh -c while true;do echo hello docker;sleep 1;done
root 3133 0 0 13:43 pts/0 00:00:00 /bin/bash
root 3153 1 0 13:43 ? 00:00:00 /usr/bin/coreutils --coreutils-prog-shebang=sleep /usr/bin/sleep 1
root 3154 3133 0 13:43 pts/0 00:00:00 ps -ef
# 方式二
$ docker attach 容器id
# 测试
$ docker attach be72714b0e60
正在执行当前的代码..
# docker exec # 进入容器开始一个新的终端,可以在里面操作(常用)
# docker attach # 进入容器正在执行的终端,不会启动新的进程!
- 从容器拷贝文件到主机上
$ docker cp 容器id:容器内路径 目的主机路径
# 测试
# 查看当前主机目录的文件
root@lemudevops:/home/lemu-devops/docker_test# ls
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
24f8d1099201 centos "/bin/bash" 5 seconds ago Up 4 seconds
```$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
24f8d1099201 centos "/bin/bash" 5 seconds ago Up 4 seconds
35 [OK]
# 进入容器内部
$ docker exec -it 24f8d1099201 /bin/bash
[root@24f8d1099201 /]# cd /home/
# 在容器内部创建一个文件
[root@24f8d1099201 home]# touch kizai.py
[root@24f8d1099201 home]# exit
exit
# 将文件拷贝到主机上
docker cp 24f8d1099201:/home/kizai.py /home/lemu-devops/docker_test
root@lemudevops:/home/lemu-devops/docker_test# ls
kizai.py
# 后面使用数据卷的技术才能把数据打通
- docker命令小结

| 命令 | 解释 | 中文解释 |
|---|---|---|
| attach | Attach to a running container | # 当前she11下attach连接指定运行镜像 |
| build | Build an image from a Dockerfile | # 通过Dockerfile定制镜像 |
| commit | Create a new image from a container changes | # 提交当前容器为新的镜像 |
| cp | Copy files/folders from the containers filesystem to the host path | # 从容器中拷贝指定文件或者目 录到宿主机中 |
| create | Create a new container | # 创建一个新的容器,同run,但不启动容器 |
| diff | Inspect changes on a container’s filesystem | # 查看docker容器变化 |
| events | Get real time events from the server | # 从docker服务获取容器实时事件 |
| exec | Run a command in.an existing container | # 在已存在的容器上运行命令 |
| export | Stream the contents of a container as a tar archive | # 导出容器的内容流作为一个tar归档文件[对应 |
| import | history Show the history of an image | # 展示一个镜像形成历史 |
| images | List images | # 列出系统当前镜像 |
| import | Create a new filesystem image from the contents of a tarbal1 | # 从tar包中的内容创建一个新的文件系统映像[对应export] |
| info | Display system-wide information | # 显示系统相关信息 |
| inspect | Return low-level information on a container | # 查看容器详细信息 |
| kill | Kill a running container | # kill指定docker容器 |
| load | Load an image from a tar archive | # 从一个tar包中加载一个镜像[对应save] |
| login | Register or Login to the docker registry server | # 注册或者登陆一个docker源服务器 |
| logout | Log out from a Docker registry server | # 从当前Docker registry退出 |
| logs | Fetch the logs of a container | # 输出当前容器日志信息 |
| port | Lookup the public-facing port which is NAT-ed to PRIVATE_PORT | # 查看映射端口对应的容器内部源端 |
| pause | Pause all processes within a container | # 暂停容器 |
| ps | List containers | # 列出容器列表 |
| pull | Pull an image or a repository from the docker registry server | # 从docker镜像源服务器拉取指定镜像或者库镜像 |
| push | Push an image or a repository to the docker registry server | # 推送指定镜像或者库镜像至docker源服务器 |
| restart | Restart a running container | # 重启运行的容器 |
| rm | Remove one or more containers | # 移除一个或者多个容器 |
| rmi | Remove one or more images | # 移除一个或多个镜像[无容器使用该镜像才可删除,否则需删除相关容器才可继续或 -f 强制删除] |
| run | Run a command in a.new container | # 创建一个新的容器并运行一个命令 |
| save | Save an image to a tar archive | # 保存一个镜像为一个tar包[对应load] |
| search | Search for an image on the Docker Hub | # 在docker hub中搜索镜像 |
| start | Start a stopped containers | # 启动容器 |
| stop | Stop a running containers | # 停止容器 |
| tag | Tag an image into a repository | # 拾源中镜像打标签 |
| top | Lookup the running processes of a container | # 查看容器中运行的进程信息 |
| unpause | Unpause a paused container | # 取消暂停容器 |
| version | Show the docker version information | # 查看docker版本号 |
| wait | Block until a container stops,then print its exit code | # 截取容器停止时的退出状态值 |
Docker镜像讲解
UnionFS(联合文件系统)
- UnionFS(联合文件系统):Union文件系统(UnionFS)是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改 作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下(unite several directories into a single virtual filesystem)。**Union文件系统是Docker镜像的基础。**镜像可以通过分层来进行继承,基于基础镜像(设有父镜像),可以制作各 种具体的应用镜像。
- 特性:一次同时加载多个文件系统,但从外面看起来,只能看到一个文件系统,联合加载会把各层文件系统叠加起来,这样最终的 文件系统会包含所有底层的文件和目录
Dockerk镜像加载原理
- docker的镜像实际上由一层一层的文件系统组成,这种层级的文件系统UnionFS。
- bootfs(boot file system)主要包含bootloader和kernel,bootloader主要是引导加载kernel,Linuxl刚启动时会加载bootfs文件系 统,在Docker镜像的最底层是oootfs。这一层与我们典型的Linux/Unix系统是一样的,包含boot加载器和内核。当boot加载完成 之后整个内核就都在内存中了,此时内存的使用权已由oootfs转交给内核,此时系统也会卸载bootfs。
- rootfs(root file system),在bootfs之上。包含的就是典型Linux系统中的/dev,/proc,/bin,/etc等标准目录和文件。rootfs就是 各种不同的操作系统发行版,比如Ubuntu,Centos等等。

平时我们安装进虚拟机的CentOS都是好几个G,为什么Docker这里才200M?

- 对于个精简的OS,rootfs可以很小,只需要包合最基本的命令,工具和程序库就可以了,因为底层直接用Host的kernel,自己只需要提供rootfs就可以了。由此可见对于不同的Linux发行版, boots基本是一致的, rootfs会有差別,因此不同的发行版可以公用bootfs.
- 虚拟机是分钟级别,容器是秒级!
分层理解
下载redis镜像看看

- 思考:为什么Docker镜像要采用这种分层的结构呢?
- 最大的好处是资源共享了!比如有多个镜像都从相同的Base镜像构建而来,那么宿主机只需在磁盘上保留一份base镜像,同时内存中也只需要加载一份base镜像,这样就可以为所有的容器服务了,而且镜像的每一层都可以被共享。
- 查看镜像分层的方式可以通过
docker image inspect命令
$ docker image inspect redis:latest
.....
"RootFS": {
"Type": "layers",
"Layers": [
"sha256:ad6562704f3759fb50f0d3de5f80a38f65a85e709b77fd24491253990f30b6be",
"sha256:49cba0f0997b2bb3a24bcfe71c7cbd6e9f6968ef7934e3ad56b0f1f9361b6b91",
"sha256:309498e524b3e2da1f036d00cd5155e0b74cf9e1d964a3636c8ed63ca4a00d43",
"sha256:86efad1273f723462d374c3c3191637ae6598e715312f8f1ec5929c3b70aee35",
"sha256:9a2bd1816e9a1abda14f1acf07ca32ce4d43c52127836704a36d8fd1a1198ff2",
"sha256:a7b30871a925a85530ee4bf40b5b875cc7ab0b452d9d571eb841cf92156083d8"
]
},
.....
理解:
- 所有的 Docker镜像都起始于一个基础镜像层,当进行修改或培加新的内容时,就会在当前镜像层之上,创建新的镜像层。
- 举一个简单的例子,假如基于 Ubuntu Linux16.04创建一个新的镜像,这就是新镜像的第一层;如果在该镜像中添加 Python包,
- 就会在基础镜像层之上创建第二个镜像层;如果继续添加一个安全补丁,就会创健第三个镜像层该像当前已经包含3个镜像层,如下图所示(这只是一个用于演示的很简单的例子)。

在添加额外的镜像层的同时,镜像始终保持是当前所有镜像的组合,理解这一点非常重要。下图中举了一个简单的例子,每个镜像层包含3个文件,而镜像包含了来自两个镜像层的6个文件。

- 上图中的镜像层跟之前图中的略有区別,主要目的是便于展示文件
- 下图中展示了一个稍微复杂的三层镜像,在外部看来整个镜像只有6个文件,这是因为最上层中的文件7是文件5的一个更新版

- 这种情況下,上层镜像层中的文件覆盖了底层镜像层中的文件。这样就使得文件的更新版本作为一个新镜像层添加到镜像当中
- Docker通过存储引擎(新版本采用快照机制)的方式来实现镜像层堆栈,并保证多镜像层对外展示为统一的文件系统
- Linux上可用的存储引撃有AUFS、 Overlay2、 Device Mapper、Btrfs以及ZFS。顾名思义,每种存储引擎都基于 Linux中对应的
件系统或者块设备技术,井且每种存储引擎都有其独有的性能特点。 - Docker在 Windows上仅支持 windowsfilter 一种存储引擎,该引擎基于NTFS文件系统之上实现了分层和CoW [1]。
- 下图展示了与系统显示相同的三层镜像。所有镜像层堆并合井,对外提供统一的视图

- 特点
- Docker 镜像都是只读的,当容器启动时,一个新的可写层加载到镜像的顶部!
- 这一层就是我们通常说的容器层,容器之下的都叫镜像层!
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6LfUIXAp-1655391103197)(https://s2.loli.net/2022/06/11/abmtjKfYWshe4lv.png)]
- 总结:下载不同软件镜像时,分层的资源是可共用的!
如何提交一个自己的镜像
commit 镜像
$ docker commit 提交容器成为一个新的副本
# 命令跟git原理类似
$ docker commit -m="提交的描述信息" -a="作者" 容器id 目标镜像名:[TAG]
- 实战测试
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
new_dingbot_msg latest cde233707f86 2 days ago 437MB
dingmsg_service latest 269caf0cc7eb 3 days ago 437MB
redis latest dd8125f93b94 3 days ago 117MB
ubuntu 20.04 20fffa419e3a 5 days ago 72.8MB
mysql latest 65b636d5542b 2 weeks ago 524MB
httpd latest 98f93cd0ec3b 2 weeks ago 144MB
hello-world latest feb5d9fea6a5 8 months ago 13.3kB
centos latest 5d0da3dc9764 8 months ago 231MB
# 启动httpd服务
$ docker run -it -p 8088:80 --name apache-tt -d httpd
d0f5368beceec8596197b734ff6be012b26eee32b3dae20df2a07ef17525e9a1
# 查看启动状态
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
d0f5368becee httpd "httpd-foreground" 33 seconds ago Up 31 seconds 0.0.0.0:8088->80/tcp, :::8088->80/tcp apache-tt
$ docker exec -it d0f5368becee /bin/bash
root@d0f5368becee:/usr/local/apache2# ls
bin build cgi-bin conf error htdocs icons include logs modules
root@d0f5368becee:/usr/local/apache2# cd htdocs/
root@d0f5368becee:/usr/local/apache2/htdocs# ls
index.html
root@d0f5368becee:/usr/local/apache2/htdocs# cat index.html
<html><body><h1>It works!</h1></body></html>
# 注意httpd默认是没有安装vim的,需要更新一下源再安装
root@d0f5368becee:/usr/local/apache2/htdocs# apt-get update
....
root@d0f5368becee:/usr/local/apache2/htdocs# apt-get install vim
....
# 修改index.html的内容
root@d0f5368becee:/usr/local/apache2/htdocs# vim index.html
<html><body><h1>httpd run in docker!It works!</h1></body></html>
# 保存退出docker终端
# 查看修改httpd后的内容
$ wget 127.0.0.1:8088
--2022-06-12 18:07:58-- http://127.0.0.1:8088/
Connecting to 127.0.0.1:8088... connected.
HTTP request sent, awaiting response... 200 OK
Length: 65 [text/html]
Saving to: ‘index.html’
index.html 100%[=======================================================================================================================================================================================>] 65 --.-KB/s in 0s
2022-06-12 18:07:58 (17.0 MB/s) - ‘index.html’ saved [65/65]
$ cat index.html
<html><body><h1>httpd run in docker!It works!</h1></body></html>
# 提交修改后的镜像
$ docker commit -a="kizai" -m="change the content of a index.html" d0f5368becee httpd-test:0.0.1
sha256:5ef56fce7bbbf2ad631214cc6e78ae497ce466b8fac50ec1b0b1550535f96f5e
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
httpd-test 0.0.1 5ef56fce7bbb 25 seconds ago 199MB

- 如果想要保存当前容器的状态,就可以通过commit来提交获得一个镜像。

docker操作实例
- 拉取官方的镜像
$ docker pull httpd
- 等待下载完成后,我们就可以在本地镜像列表里查到REPOSITORY为httpd的镜像。
$ docker images httpd
REPOSITORY TAG IMAGE ID CREATED SIZE
httpd latest 98f93cd0ec3b 11 days ago 144MB
使用apache镜像
- 运行容器
$ docker run -it -p 8877:80 --name apache-test -d httpd
986f882220ef2e78874d20e6ea7e6dbd2d62877e2026b1e2bbc489482e3291ae
- –name 为容器取一个名字
- -p 参数语法为 -p host port:container port; -p 8877:80 将主机上的8080端口绑定到容器上的80端口,因此在主机中访问8080端口时其实就是访问 nginx 容器的80端口
- -d 后台运行容器
- 查看Docker运行情况
$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
986f882220ef httpd "httpd-foreground" 33 seconds ago Up 32 seconds 0.0.0.0:8877->80/tcp, :::8877->80/tcp apache-test
- 查看容器运行日志
docker logs apache-test
AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using 172.17.0.2. Set the 'ServerName' directive globally to suppress this message
AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using 172.17.0.2. Set the 'ServerName' directive globally to suppress this message
[Wed Jun 08 15:09:22.925340 2022] [mpm_event:notice] [pid 1:tid 139932608978240] AH00489: Apache/2.4.53 (Unix) configured -- resuming normal operations
[Wed Jun 08 15:09:22.925448 2022] [core:notice] [pid 1:tid 139932608978240] AH00094: Command line: 'httpd -D FOREGROUND'
172.17.0.1 - - [08/Jun/2022:15:10:33 +0000] "GET / HTTP/1.1" 200 45
172.17.0.1 - - [08/Jun/2022:15:10:34 +0000] "GET /favicon.ico HTTP/1.1" 404 196
- 打开浏览器输入
127.0.0.0:8877就可以看到docker的httpd服务了。
使用Dockerfile文件来创建docker镜像
- Dockerfile 中的内置命令及其作用
| 内置命令 | 作用 |
|---|---|
| FROM | 指明基础镜像的名称和版本。必填项 |
| MAINTAINER | 可用于提供作者、版本及其其他信息。可选项 |
| RUN | 用于执行后面的指令。当RUN执行完毕后,将产生一个新的文件层。可选项 |
| CMD | 指定此镜像启动默认执行的命令。可选项 |
| LABEL | 用于在镜像中添加元数据。例如版本号、构建日期等。可选项 |
| EXPOSE | 用于指定需要暴露的网络端口号。可选项 |
| ENV | 用于在镜像添加环境变量。可选项 |
| ADD | 向镜像添加新文件或者新目录。可选项 |
| COPY | 从主句向镜像复制文件。可选项 |
| ENTRYPOINT | 在镜像中设定默认执行的二进制程序。可选项 |
| VOLUME | 向镜像中挂载一个卷组。可选项 |
| USER | 在镜像构建过程中,生成或者切换到另外一个用户。可选项 |
| WORKDIR | 设定此镜像后续操作默认的工作目录。可选项 |
| ONBUILD | 配置构建触发指令集。可选项 |
创建钉钉机器人查询服务的API服务镜像
- 创建文件构建镜像的文件夹,拷贝API服务代码到app目录下。
$ cd ~
$ mkdir -p Docker_Project/Query_DingBot/app
# 此时的文件结构如下:
.
├── app
│ ├── config.ini
│ └── lemumsg_run.py
└── Dockerfile
1 directory, 3 files
- 在
Query_DingBot创建Dockerfile文件
$ vim Dockerfile
# 添加以下内容
# 基于哪个镜像
FROM ubuntu:20.04
# 维护者信息
LABEL maintainer="kizai@foxmmail.com"
# 设置工作空间,后续命令会在此目录下执行
WORKDIR /app
# 添加文件到容器中
ADD . /app/
# 安装运行环境
RUN apt-get update && apt-get install -y python3 && apt-get install -y python3-pip && pip install DingtalkChatbot==1.5.3 && pip install flask==2.1.2 && pip install flask_json==0.3.4
# 开放6655端口
EXPOSE 6655
# 配置容器启动后执行的命令
ENTRYPOINT ["python3"]
CMD ["app/lemumsg_run.py"]
FROM指令指定初始镜像。- 所有
Dockerfile一定要有FROM指令作为第一个非注释指令。 - RUN 指令指定的
shell命令,是将要在镜像里执行的。
- 在同一目录下执行docker build 命令:
$ docker build -t dingmsg_service .
- 查看构建成功的镜像:
$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
dingmsg_service latest cd4570fa31e6 2 minutes ago 437MB
- 运行
dingmsg_service镜像
$ docker run -it --restart=always -p 6655:6655 --name dingbot-test -d dingmsg_service
# Docker 容器的重启策略如下:
--restart具体参数值详细信息:
no // 默认策略,容器退出时不重启容器;
on-failure // 在容器非正常退出时(退出状态非0)才重新启动容器;
on-failure:3 // 在容器非正常退出时重启容器,最多重启3次;
always // 无论退出状态是如何,都重启容器;
unless-stopped // 在容器退出时总是重启容器,但是不考虑在 Docker 守护进程启动时就已经停止了的容器
- 查看Docker运行的log
$ docker logs dingbot-test
* Serving Flask app 'lemumsg_run' (lazy loading)
* Environment: production
WARNING: This is a development server. Do not use it in a production deployment.
Use a production WSGI server instead.
* Debug mode: on
* Running on all addresses (0.0.0.0)
WARNING: This is a development server. Do not use it in a production deployment.
* Running on http://127.0.0.1:6655
* Running on http://172.17.0.2:6655 (Press CTRL+C to quit)
* Restarting with stat
* Debugger is active!
* Debugger PIN: 915-478-794
10.0.0.1 - - [09/Jun/2022 09:35:07] "POST /api/v1/text HTTP/1.0" 200 -
INFO:werkzeug:10.0.0.1 - - [09/Jun/2022 09:35:07] "POST /api/v1/text HTTP/1.0" 200 -
10.0.0.1 - - [09/Jun/2022 09:35:28] "POST /api/v1/text HTTP/1.0" 200 -
INFO:werkzeug:10.0.0.1 - - [09/Jun/2022 09:35:28] "POST /api/v1/text HTTP/1.0" 200 -
Docker的日志一般保存在/var/lib/docker/. 每个容器都有一个特定于其 ID 的日志(完整 ID,而不是通常显示的缩短的 ID),您可以像这样访问它:
/var/lib/docker/containers/ID/ID-json.log
- 重启电脑后查看docker进程,dingbot-test进程服务依旧运行着。
- 进入dingbot-test终端进行交互:
$ docker exec -i -t dingbot-test /bin/bash
root@7e61c738bcf4:/app# ls
Dockerfile app
root@7e61c738bcf4:/app# cd app/
root@7e61c738bcf4:/app/app# ls
config.ini lemumsg_run.py
进到dingbot-test进程服务终端,其实可以发现里面的文件架构跟创建镜像时的是一样的。
导出/载入Docker镜像,以dingmsg_service镜像为例
使用 export 和 import
- 查看本机的容器。
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
7e61c738bcf4 dingmsg_service "python3 app/lemumsg…" 7 hours ago Up 6 hours 0.0.0.0:6655->6655/tcp, :::6655->6655/tcp dingbot-test
986f882220ef httpd "httpd-foreground" 25 hours ago Up About an hour 0.0.0.0:8877->80/tcp, :::8877->80/tcp apache-test
- 使用 docker export 命令根据容器 ID 将镜像导出成一个文件。
# 创建文件夹来保存镜像文件
$ mkdir -p ~/Docker_Project/images
# 导出镜像
$ docker export 7e61c738bcf4 > ~/Docker_Project/images/dingbot_msg_01.tar
# 导出成功不会有提示,可以查看该文件夹是否tar的镜像包
$ ls
dingbot_msg_01.tar
上面命令执行后,可以看到文件已经保存到当前的~/Docker_Project/images终端目录下。
3. 使用 docker import 命令则可将这个镜像文件导入进来。
$ docker import - new_dingbot_msg < dingbot_msg_01.tar
sha256:cde233707f864c1578cb521d4ba88eee38f185b6435cc3478e49ec8e7aa07705
- 执行
docker images ls命令可以看到镜像确实已经导入进来了。
$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
new_dingbot_msg latest cde233707f86 59 seconds ago 437MB
dingmsg_service latest 269caf0cc7eb 7 hours ago 437MB
使用 save 和 load
- 查看本机的容器。
$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
new_dingbot_msg latest cde233707f86 15 minutes ago 437MB
dingmsg_service latest 269caf0cc7eb 7 hours ago 437MB
ubuntu 20.04 20fffa419e3a 2 days ago 72.8MB
httpd latest 98f93cd0ec3b 12 days ago 144MB
hello-world latest feb5d9fea6a5 8 months ago 13.3kB
- 下面使用
docker save命令根据 ID 将镜像保存成一个文件。
$ docker save 269caf0cc7eb > dingbot_msg_02.tar
- 载入镜像,使用
docker load命令则可将这个镜像文件载入进来。
docker load < dingbot_msg_02.tar
特别注意:两种方法不可混用。
- 如果使用
import导入save产生的文件,虽然导入不提示错误,但是启动容器时会提示失败,会出现类似docker: Error response from daemon: Container command not found or does not exist的错误。
两种方案的差别
- 文件大小不同:export 导出的镜像文件体积小于 save 保存的镜像
- 是否可以对镜像重命名
docker import可以为镜像指定新名称docker load不能对载入的镜像重命名
- 是否可以同时将多个镜像打包到一个文件中
docker export不支持docker save支持
- 是否包含镜像历史
- export 导出(import 导入)是根据容器拿到的镜像,再导入时会丢失镜像所有的历史记录和元数据信息(即仅保存容器当时的快照状态),所以无法进行回滚操作。
- 而 save 保存(load 加载)的镜像,没有丢失镜像的历史,可以回滚到之前的层(layer)。
- 应用场景不同
- docker export 的应用场景:主要用来制作基础镜像,比如我们从一个 ubuntu 镜像启动一个容器,然后安装一些软件和进行一些设置后,使用 docker export 保存为一个基础镜像。然后,把这个镜像分发给其他人使用,比如作为基础的开发环境。
- docker save 的应用场景:如果我们的应用是使用
docker-compose.yml编排的多个镜像组合,但我们要部署的客户服务器并不能连外网。这时就可以使用 docker save 将用到的镜像打个包,然后拷贝到客户服务器上使用 docker load 载入。
Docker数据卷
什么是数据卷
-
数据卷是一个可供一个或多个容器使用的特殊目录,它绕过
UFS(联合文件系统[Union File System],把其他文件系统联合到一个联合挂载点的文件系统服务),可以提供很多有用的特性:- 数据卷可以在容器之间共享和重用
- 对数据卷的修改会立马生效
- 对数据卷的更新,不会影响镜像
- 卷会一直存在,直到没有容器使用
*数据卷的使用,类似于 Linux 下对目录或文件进行 mount。
-
docker的理念回顾
- 将应用和环境打包成一个镜像!
-
数据?如果数据都在容器中,那么我们容器删除,数据就会丢失!需求:数据可以持久化
-
MySQL,容器删了,删库跑路!需求:MySQL数据可以存储在本地!
-
容器之间可以有一个数据共享的技术!Docker容器中产生的数据,同步到本地!
-
这就是卷技术!目录的挂载,将我们容器内的目录,挂载到Liux上面!

- 总结:容器的持久化和同步操作!容器见是可以数据共享的!
使用数据卷
方式一:使用命令来挂载 -v
$ docker run -it -v 主机目录:容器目录 -p
# 在本机创建一个映射文件夹
$ mkdir -p /home/lemu-devops/Docker_Project/share
# 运行并挂载
$ docker run -it -v /home/lemu-devops/Docker_Project/share/centos:/home centos /bin/bash
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b7d2b3ee3d32 centos "/bin/bash" About a minute ago Up About a minute thirsty_napier
# 启动起来之后可以通过docker inspect查看容器的数据
$ docker inspect b7d2b3ee3d32
...
"Mounts": [
{
"Type": "bind",
"Source": "/home/lemu-devops/Docker_Project/share/centos",
"Destination": "/home",
"Mode": "",
"RW": true,
"Propagation": "rprivate"
}
],
...

- 测试文件的同步

- 再来测试
- 停止容器
- 在宿主机上修改wenj
- 启动容器
- 容器内的数据依旧是同步的。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Cn9yyN7x-1655391103199)(https://s2.loli.net/2022/06/12/1gthMQw3YodbuOm.png)]
好处:以后修改只需要在本都修改即可,容器内会自动同步。
实战:安装MySQL
- 思考:数据持久化的问题
# 获取镜像
$ docker pull mysql:5.7
# 运行容器,需要做数据挂载! 安装启动mysql,需要配置密码
# 官方测试配置:$ docker run --name some-mysql -v /my/custom:/etc/mysql/conf.d -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql:tag
# 启动mysql
-d 后台运行
-p 端口映射
-v 卷挂载
-e 环境配置
$ docker run -d -p 3310:3306 -v /home/lemu-devops/Docker_Project/share/mysql/conf:/etc/mysql/conf.d -v /home/lemu-devops/Docker_Project/share/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 --name mysql01 mysql:5.7
# 启动成功之后使用,mysql插件来测试一下
# mysql插件链接到服务器的3310 --- 3310和容器内的3306映射,这个时候就可以连上了

- 挂载文件成功的实例

- 删除mysql容器查看主机的文件是否会删除呢?

- 发现,我们挂载到本地的数据依旧没有丢失,这就实现了容器数据持久化的功能。
具名和匿名挂载
# 匿名挂载
-v 容器内路径
$ docker run -d -P --name httpd02 -v /usr/local/apache2 httpd
# 查看所有volume 的情况
$ docker volume ls
DRIVER VOLUME NAME
local 3baac0bfac36eca270cb3935e83f95db4140e3a2c85bf00d848ec12817b0f7c4 (匿名卷)
local 79de0d956d465f57a3731395041ff2c566d15c2b8a837c2f992c1d15f1cdaef0 (匿名卷)
# 这里发现,这种就是匿名挂载,我们在-v 只写了容器内的路径,没有写容器外的路径
# 具名挂载
$ docker run -d -P --name htppd03 -v juming-apache:/usr/local/apache2 httpd
b484b2f53946b46ce3320ef070af0fa9f935e2da5c13f571a46e58a35752d603
$ docker volume ls
DRIVER VOLUME NAME
local 3baac0bfac36eca270cb3935e83f95db4140e3a2c85bf00d848ec12817b0f7c4
local 79de0d956d465f57a3731395041ff2c566d15c2b8a837c2f992c1d15f1cdaef0
local juming-apache
# 通过 -v 卷名:容器内路径
# 查看一下这个卷
$ docker volume inspect juming-apache
[
{
"CreatedAt": "2022-06-12T23:00:39+08:00",
"Driver": "local",
"Labels": null,
"Mountpoint": "/var/lib/docker/volumes/juming-apache/_data",
"Name": "juming-apache",
"Options": null,
"Scope": "local"
}
]
$ cd /var/lib/docker/
root@lemudevops:/var/lib/docker# ls
buildkit containers image network overlay2 plugins runtimes swarm tmp trust volumes
root@lemudevops:/var/lib/docker# cd volumes/
root@lemudevops:/var/lib/docker/volumes# ls
3baac0bfac36eca270cb3935e83f95db4140e3a2c85bf00d848ec12817b0f7c4 backingFsBlockDev metadata.db
79de0d956d465f57a3731395041ff2c566d15c2b8a837c2f992c1d15f1cdaef0 juming-apache
root@lemudevops:/var/lib/docker/volumes# cd juming-apache/
root@lemudevops:/var/lib/docker/volumes/juming-apache# ls
_data
root@lemudevops:/var/lib/docker/volumes/juming-apache# cd _data/
root@lemudevops:/var/lib/docker/volumes/juming-apache/_data# ls
bin build cgi-bin conf error htdocs icons include logs modules
- 所有docker容器的卷,没有指定目录的情况下都是在
/var/lib/docker/volumes/xxx/_data路径下 - 我们通过具名挂载可以方便的找到我们的一个卷,大多数情况都是使用
具名挂载。
# 如何确定是具名挂载还是匿名挂载,还是指定路径挂载
-v 容器内路径 # 匿名挂载
-v 卷名:容器内路径 # 具名挂载
-v /宿主机路径:/容器内路径 # 指定路径挂载
- 拓展
ro readonly # 只读
rw readwrite # 可读可写
# 一旦设置了容器权限,容器对我们挂载出来的内容就有限定了
$ docker run -d -P --name htppd04 -v juming-apache:/usr/local/apache2:ro httpd
$ docker run -d -P --name htppd04 -v juming-apache:/usr/local/apache2:rw httpd
# ro 只要看到ro就说明这个路径只能通过宿主机来操作,容器内部是无法操作的!!!
- Dockerfile就是用来构建docker镜像的构建文件!命令脚本!
- 通过这个脚本可以生成镜像,镜像是一层一层的,脚本一个个的命令,每个命令都是一层。
# 创建一个dockerfile文件,名字可以随机,建议使用Dockerfile
# 文件内容指令(大写) 参数
$ mkdri -p /home/lemu-devops/Docker_Project/docker-test-volume
$ cd Docker_Project/docker-test-volume/
$ vim dockerfile1
# 写入dockerfile 命令,这里的每个命令都是镜像的一层
FROM centos
VOLUME ["volume01","volume02"]
CMD echo "----end----"
CMD /bin/bash
# 保存退出
# 构建镜像
$ docker build -f dockerfile1 -t kizai/centos .

数据卷容器
- 多个mysql同步数据

# 启动3个容器,通过自己写的镜像启动
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4SDSL7D6-1655391103200)(https://s2.loli.net/2022/06/13/AULRn3jqYSD9crv.png)]
- 启动自定义的容器
$ docker run -it b0248e10a05e /bin/bash

- 这个卷的外部一定有一个同步的目录
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OY3yT1Ek-1655391103201)(https://s2.loli.net/2022/06/13/lmyktKOe92VG8pu.png)]
- 查看卷挂载的路径

- 测试一下文件是否同步过去了

-
假设构建镜像时没有挂载卷,要手动镜像挂载
-v 卷名:容器内路径 -
启动第docker02数据卷继承于docker01,在这里docker01就叫做数据卷容器。
$ docker run -it --name docker02 --volumes-from docker01 b0248e10a05e
- docker01创建的数据同步到docker02上

- 启动docker03数据卷继承于docker01

- 测试:可以删除docker01,查看docker02和docker03是否可以访问这个文件?
- 经过测试:删除docker01后,docker02和docker03一样是可以访问文件的。

- 多个mysql实现数据共享
# 启动mysql01
$ docker run -d -p 3310:3306 -v /etc/mysql/conf.d -v /var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 --name mysql01 mysql:5.7
# 启动mysql02
$ docker run -d -p 3310:3306 -e MYSQL_ROOT_PASSWORD=123456 --name mysql02 --volumes-from mysql01 mysql:5.7
# 这个时候就可以实现两个容器数据同步
结论
容器之间配置信息的传递,数据卷容器的生命周期一直持续到没有容器使用为止。
但是一旦持久化到本地,这个时候,本地是数据是不会删除的。
DockerFile
dockerfile 是用来构建docker镜像的文件,命令参数脚本!
构建步骤:
1. 编写一个dockerfile文件
2. docker build构建成为一个镜像
3. docker run运行镜像
4. docker push发布到(DockerHub、私有DockerHub服务)
查看一下官方是怎么做到?


很多官方的镜像都是基础包,很多功能没有,我们通常会自己搭建自己的镜像。
DockerFile构建过程
基础知识:
- 每个保留关键字(指令)都必须是大写字母
- 执行从上到下顺序执行
#表示注释- 每一个指令都会创建提交一个新的镜像层并提交。

dockerfile是面向开发的,以后要发布项目做镜像,就需要编写dockerfile文件,这个文件比较简单!
Docker镜像逐渐成为企业交互的标准!
步骤:开发、部署、运维。。。
- Dockerfile:构建文件,定义了一切的步骤,源代码。
- DockerImage:通过Dockerfile构建生成的镜像,最终发布和运行的产品
- Docker容器:容器就是镜像运行起来提供服务器
DockerFile的指令
FROM # 基础镜像
MAINTAINER # 镜像谁写的,姓名+邮箱
RUN # 镜像构建的时候需要运行的命令
ADD # 步骤:centos镜像,centos压缩包!添加内容
WORKDIR # 镜像的工作目录
VOLUME # 挂载的目录
EXPOSE # 暴露端口配置
CMD # 指定这个容器启动的时候运行的命令,只有最后一个会生效,可以被替代
ENTRYPOINT # 指定这个容器启动的时候要运行的命令,可以追加命令
ONBUILD # 当构建一个被继承 DockerFile 这个时候就会运行 ONBUILD 的指令。触发指令
COPY # 类似ADD,将我们文件拷贝到镜像中
ENV # 构建的时候设置环境变量

实战测试
Docker Hub中99%镜像都是从这个基础镜像过来的FROM scratch,然后配置需要的软件和配置来进行构建。
- 创建一个自己的centos
# 1.编写dockerfile文件
$ vim dockerfile-centos
FROM centos:latest
MAINTAINER kizai<kizai@foxmail.com>
ENV MYPATH /usr/local
WORKDIR $MYPATH
EXPOSE 80
CMD echo $MYPATH
CMD echo "-----end-----"
CMD /bin/bash
# 2.通过这个文件构建镜像
# 命令: docker build -f dockerfile文件路径 -t 镜像名:[tag]
Successfully built 602aa38c9a1a
Successfully tagged mycentos:0.1
# 测试运行

CMD 和 ENTRYOINT 的区别
CMD # 指定这个容器启动的时候运行的命令,只有最后一个会生效,可以被替代
ENTRYPOINT # 指定这个容器启动的时候要运行的命令,可以追加命令
测试CDM
# 编写dockerfile文件
$ vim dockerfile-cmd-test
FROM centos
CMD ["ls","-a"]
# 构建镜像
$ docker build -f dockerfile-cmd-test -t cmdtest .
# run运行,发现我们的ls -a命令生效了
$ docker run 933abee7baa1
.
..
.dockerenv
bin
dev
etc
home
lib
lib64
# 想追加一个命令-l ls -al
$ docker run 933abee7baa1 -l
docker: Error response from daemon: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: exec: "-l": executable file not found in $PATH: unknown.
# cmd的命令下 -l 替换了CMD ["ls","-a"] 命令,-l 不是命令所以报错!
测试ENTRYPOINT
$ vim dockerfile-cmd-entrypoint
FROM centos
CMD ["ls","-a"]
# 构建镜像
$ docker build -f dockerfile-cmd-entrypoint -t entrypoint-test .
Sending build context to Docker daemon 4.608kB
Step 1/2 : FROM centos
---> 5d0da3dc9764
Step 2/2 : ENTRYPOINT ["ls","-a"]
---> Running in 6ee8bc2055a2
Removing intermediate container 6ee8bc2055a2
---> e2414dc3a0c9
Successfully built e2414dc3a0c9
Successfully tagged entrypoint-test:latest
# 查看镜像
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
entrypoint-test latest e2414dc3a0c9 9 seconds ago 231MB
cmdtest latest 933abee7baa1 11 minutes ago 231MB
mycentos 0.1 0f0d9b408fbd About an hour ago 231MB
kizai/centos latest b0248e10a05e 9 hours ago 231MB
httpd-test 0.0.1 5ef56fce7bbb 29 hours ago 199MB
new_dingbot_msg latest cde233707f86 3 days ago 437MB
dingmsg_service latest 269caf0cc7eb 4 days ago 437MB
redis latest dd8125f93b94 4 days ago 117MB
ubuntu 20.04 20fffa419e3a 6 days ago 72.8MB
mysql 5.7 2a0961b7de03 2 weeks ago 462MB
mysql latest 65b636d5542b 2 weeks ago 524MB
httpd latest 98f93cd0ec3b 2 weeks ago 144MB
hello-world latest feb5d9fea6a5 8 months ago 13.3kB
centos latest 5d0da3dc9764 9 months ago 231MB
# 运行镜像
$ docker run e2414dc3a0c9
.
..
.dockerenv
bin
dev
etc
home
lib
lib64
lost+found
media
mnt
opt
proc
root
run
sbin
srv
sys
tmp
usr
var
# 追加命令运行镜像,是直接拼接我们的 ENTRYOINT 命令后面
$ docker run e2414dc3a0c9 -l
total 56
drwxr-xr-x 1 root root 4096 Jun 13 15:12 .
drwxr-xr-x 1 root root 4096 Jun 13 15:12 ..
-rwxr-xr-x 1 root root 0 Jun 13 15:12 .dockerenv
lrwxrwxrwx 1 root root 7 Nov 3 2020 bin -> usr/bin
drwxr-xr-x 5 root root 340 Jun 13 15:12 dev
drwxr-xr-x 1 root root 4096 Jun 13 15:12 etc
drwxr-xr-x 2 root root 4096 Nov 3 2020 home
lrwxrwxrwx 1 root root 7 Nov 3 2020 lib -> usr/lib
lrwxrwxrwx 1 root root 9 Nov 3 2020 lib64 -> usr/lib64
drwx------ 2 root root 4096 Sep 15 2021 lost+found
drwxr-xr-x 2 root root 4096 Nov 3 2020 media
drwxr-xr-x 2 root root 4096 Nov 3 2020 mnt
drwxr-xr-x 2 root root 4096 Nov 3 2020 opt
dr-xr-xr-x 643 root root 0 Jun 13 15:12 proc
dr-xr-x--- 2 root root 4096 Sep 15 2021 root
drwxr-xr-x 11 root root 4096 Sep 15 2021 run
lrwxrwxrwx 1 root root 8 Nov 3 2020 sbin -> usr/sbin
drwxr-xr-x 2 root root 4096 Nov 3 2020 srv
dr-xr-xr-x 13 root root 0 Jun 13 15:12 sys
drwxrwxrwt 7 root root 4096 Sep 15 2021 tmp
drwxr-xr-x 12 root root 4096 Sep 15 2021 usr
drwxr-xr-x 20 root root 4096 Sep 15 2021 var
Dockerfile中很多命令都十分相似,我们需要了解他们的区别,最后的方法就是做对比测试。
小结

Docker网络
理解Docker0
清空所有的容器和镜像
# 删除所有容器
$ docker rm -f $(docker ps -aq)
# 删除所有镜像
$ docker image rm -f $(docker image -aq)

- 回环地址:不属于任何一个有类别地址类。它代表设备的本地虚拟接口,所以默认被看作是永远不会宕掉的接口,一般都会用来检查本地网络协议、基本数据接口等是否正常的。
- lo:local的简写,一般指本地环回接口。
- docker0:Docker使用的是Linux的桥接,在宿主机中是一个Docker容器的网桥docker0。每启动一个docker容器,docker就会给docker容器分配一个ip,只要安装了docker,就会有一个网卡docker0。
- oray_vnc:蒲公英路由组网生成的oray_vnc网关地址
- lft代表终身。如果您通过 DHCP 获取此地址,则它指的是相对于 IP 地址的租用时间。
- enp1s0: 为什么是 enp1s0 而不是 eth0?
- 新的命名方案被称为"可预测的网络接口Predictable Network Interface"。 它已经在基于systemd 的 Linux 系统上使用了一段时间了。 接口名称取决于硬件的物理位置。
en仅仅就是ethernet的意思,就像eth用于对应eth0一样。p是以太网卡的总线编号,s是插槽编号。 所以enp1s0告诉我们很多我们正在使用的硬件的信息。 - 使用状态
# BROADCAST 该接口支持广播 # MULTICAST 该接口支持多播 # UP 网络接口已启用 # LOWER_UP 网络电缆已插入,设备已连接至网络 # DOWN 网络接口已关闭 # UNKNOWN 暂不知道网络接口的状态 - 新的命名方案被称为"可预测的网络接口Predictable Network Interface"。 它已经在基于systemd 的 Linux 系统上使用了一段时间了。 接口名称取决于硬件的物理位置。
- 其他命令解释:
mtu 1500 最大传输单位(数据包大小)为1,500字节
qdisc pfifo_fast 用于数据包排队
state UP 网络接口已启用
group default 接口组
qlen 1000 传输队列长度
link/ether d8:bb:c1:17:ae:68 接口的 MAC(硬件)地址
brd ff:ff:ff:ff:ff:ff 广播地址
inet 10.0.0.131/23 IPv4 地址
brd 10.0.1.255 广播地址
scope global 全局有效
dynamic enp1s0 地址是动态分配的
valid_lft 75239sec IPv4 地址的有效使用期限
preferred_lft 75239sec IPv4 地址的首选生存期
inet6 fd5c:a4db:ced8:0:681:9546:4ec:c846/64 IPv6 地址
scope link 仅在此设备上有效
valid_lft forever IPv6 地址的有效使用期限
preferred_lft forever IPv6 地址的首选生存期
docker重启自动运行的原理?怎么让它自动运行?
- 原理:Docker 容器的自动重启是由 Docker 守护进程完成的。
# 运行命令:
$ docker run -i -t -d --name dockername --restart=always imageId
# --restart的参数
no - container #不重启
on-failure - container # 退出状态非0时重启
on-failure:n # 在容器非正常退出时重启容器,并且指定重启次数。n 为正整数。如果不指定次数,则会一直重启。
always # 始终重启
unless-stopped # 在容器退出时总是重启容器,但是 Docker 守护进程启动之前就已经停止运行的容器不算在内。
- 容器启动时忘记使用
--restart=always如何补救- 使用
update命令
$ docker container update --restart=always 容器id- 修改容器的配置文件
# vim /var/lib/docker/containers/容器ID/hostconfig.json,找到关键字 RestartPolicy,将 no 改为 always修改前: "RestartPolicy:{"Name":"no","MaximumRetryCount":0} # 修改后: "RestartPolicy:{"Name":"always","MaximumRetryCount":0} # 重启容器即可。如果无法修改容器的配置,可先将容器停止,修改配置文件后再启动。 - 使用
文件系统是怎么挂载的?
AUFS 是联合文件系统,意味着它在主机上使用多层目录存储,每一个目录在 AUFS 中都叫作分支,而在 Docker 中则称之为层(layer),但最终呈现给用户的则是一个普通单层的
文件系统,我们把多层以单一层的方式呈现出来的过程叫作联合挂载。

如上图,每一个镜像层和容器层都是 /var/lib/docker 下的一个子目录,镜像层和容器层都在 aufs/diff 目录下,每一层的目录名称是镜像或容器的 ID 值,联合挂载点在 aufs/mnt 目录下,mnt 目录是真正的容器工作目录。
下面我们针对 aufs 文件夹下的各目录结构,在创建容器前后的变化做详细讲述。
- 当一个镜像未生成容器时,AUFS 的存储结构如下。
- diff 文件夹:存储镜像内容,每一层都存储在以镜像层 ID 命名的子文件夹中。
- layers 文件夹:存储镜像层关系的元数据,在 diff 文件夹下的每个镜像层在这里都会有一个文件,文件的内容为该层镜像的父级镜像的 ID。
- mnt 文件夹:联合挂载点目录,未生成容器时,该目录为空。
- 当一个镜像已经生成容器时,AUFS 存储结构会发生如下变化。
- diff 文件夹:当容器运行时,会在 diff 目录下生成容器层。
- layers 文件夹:增加容器层相关的元数据。
- mnt 文件夹:容器的联合挂载点,这和容器中看到的文件内容一致。
如何理解“UNIX里一切都是文件这句话”
-
在LINUX系统中有一个重要的概念:一切都是文件。 其实这是UNIX哲学的一个体现,而Linux是重写UNIX而来,所以这个概念也就传承了下来。在UNIX系统中,把一切资源都看作是文件,包括硬件设备。UNIX系统把每个硬件都看成是一个文件,通常称为设备文件,这样用户就可以用读写文件的方式实现对硬件的访问。这样带来优势也是显而易见的:
- 实现了设备无关性。
- UNIX 权限模型也是围绕文件的概念来建立的,所以对设备也就可以同样处理了。
-
这一关键设计原则提供了一个统一的范式,用于访问各种输入输出资源:文档、目录、磁盘驱动器、CD-ROM、调制解调器、键盘、打印机、显示器、终端,甚至是一些进程间通信和网络通信。所有这些资源拥有一个通用的抽象,UNIX 之父将其称为“文件”。因为每个“文件”都通过相同的 API 暴露出来,所以你可以使用同一组基本命令来读取和写入磁盘、键盘、文档或网络设备。
-
Linux文件系统的层次

-
由上图可以知道,整个文件系统体系分为了三个层面,用户层,内核层,硬件层,用户层是通过API通过系统调用调用的方式访问虚拟文件系统。在内核层,我们可以看到虚拟文件系统下连接了各种类型的文件系统,其是对不同的文件系统的抽象,为上层应用提供了统一的 API 接口;上图内核层还有一层是各个文件系统之下的一层,这一层的作用是隐藏了不同硬件设备之间的细节,为内核提供了统一的 IO 操作接口。下面我们对整个文件系统从下到上对各个层的作用进行一个阐述:
-
Device Driver(硬盘驱动):常见的硬盘类型有PATA,SATA,在Linux中,对于硬盘提供的驱动模块一般都存放在内核目录树drivers/ata中,而对于一般通用的硬盘驱动,可能会直接被编译到内核中。
-
通用块设备层(General Block Device Layer):不同的硬盘,会提供不同的 IO 接口,对于内核来讲,这种杂乱的接口是不利于管理的,因此就把这些接口进行了抽象,形成了一个统一的对外接口,这样就不管你是什么存储设备,操作他们的IO接口并没有什么区别。
-
文件系统层:目前大多数Linux使用的是ex4,与此同时,btrfs也呼之欲出
-
虚拟文件系统:正如不同的存储设备具有不同的 IO 接口,那么不同的文件系统也具有不用的 API,内核想实现的是不管是什么文件系统,都采用的是相同的 API 进行操作,所以 VFS 就做了一个抽象,提供了统一的 API 接口,使之可以对不同的文件系统采用同样的操作。
-
Linux广义文件分类

docker引擎的log是在什么位置?
# 使用以下命令查看
$ journalctl -u docker.service
三个网络
# 问题:docker 是如何处理处理容器网络访问的?

$ docker run -d -P --name centos-test centos
# 查看容器的内部网络地址: ip addr
2110

被折叠的 条评论
为什么被折叠?



