前言
本文初次写于 2020 年 9 月,因工作需要接收和维护项目的 K8S 平台,所以学习 Docker,这里记录了学习 Docker 的相关过程。介绍了 Docker 的演化背景、安装、镜像、容器、容器卷、和 Dockerfile 的基本使用。
因文档编写时间较久部分内容可能会有所变化,如果有不对的地方,也欢迎同学指正,文末有 PDF 可以获取,本文基于文档转换而来,有部分删减。
什么是Docker
Docker是一个开源的引擎,可以轻松的为任何应用创建一个轻量级的、可移植的、自给自足的容器。开发者在电脑上编译测试通过的容器可以批量地在生产环境中部署,包括VMs(虚拟机)、集群环境和其他的基础应用平台。
为什么会有Docker
一款产品从开发到上线(操作系统、运行环境、应用配置)。开发和运维之间的协作需要关心很多东西,比如说面对跨环境(操作系统)的部署,特别是产品版本迭代以后,不同版本环境的兼容性问题,都是一个考验。Docker的诞生就是对这个问题提供一个标准化的解决方案。
- 针对复杂的环境配置,能否实现软件可以带环境安装(安装的时候,把开发时的原始环境一模一样地复制过来),而Docker的出现就是解决了在开发时没有任何毛病,换一台机器,程序可能就无法正常运行的棘手问题。
- 当需求发生扩充,必须原来的单台服务器可能无法满足业务的访问需求时,可能就需要搭建负载均衡或者集群环境,传统的部署是单台部署,在部署中可能还会发生部署失败的问题,如果是集群环境下(几十台甚至上百台的服务器的情况下),如果是单台部署,那么耗时又耗力,此时如果软件+环境直接用Docker 构建成镜像,就可以在集群环境中批量部署,以满足业务访问的需求。如果Java语言是一次编译到处运行的话,那么Docker就是一次封装(构建),到处运行。
总结:Docker解决了开发环境和线上环境的配置问题,解决了跨平台(不同操作系统)的软件部署的难题。
虚拟技术的发展

虚拟机
虚拟机(Virtual Machine)指通过软件模拟的具有完整硬件系统功能的、运行在一个完全隔离环境中的完整计算机系统。简单的说就是在一个操作系统中,通过软件技术同时模拟运行多个操作系统的软件。在网络环境中,它就是一台可独立的机器。虚拟机看上去跟真实的系统一模一样,对于底层系统而言,虚拟机就是一个普通的文件,不需要的就直接删掉,不会影响其他操作系统的正常运行。而虚拟机也可以作为一种环境安装的解决方案 。
- 缺点:资源占用多、步骤繁琐、启动慢;
Linux容器
Linux容器(Linux Containers,简称LXC):Linux容器不是模拟一个完整的操作系统,而是对进程隔离。通过容器,可以将软件运行时所需要的所有资源打包到一个隔离的容器中。容器和虚拟机不同,不需要像虚拟机一样捆绑一整套操作系统,只需要软件工作时所需的资源文件和配置信息。系统因此而变得轻量并保证部署在任何环境中的软件都能正常运行。
- 优点:
- 容器内的应用进程直接运行于宿主内核,比传统的虚拟机更为轻便;
- 容器之间相互隔离,不会互相影响;
DevOps
开发/运维(DevOps):DevOps(Development和Operations的组合词)是一种重视"软件开发人员(Dev)"和"IT运维技术人员(Ops)"之间沟通合作的文化、运动或惯例。透过自动化"软件交付"和"架构变更"的流程,来使得构建、测试、发布软件能够更加地快捷、频繁和可靠。
- 优点:
- 更快速的应用交付和部署;
- 更便捷的升级和扩容、缩容;
- 更简单的系统运维;
- 更高效的计算资源利用;
Docker的优势
- 轻量、秒级的快速启动速度;
- 简单、易用、活跃的社区;
- 标准统一的打包/部署/运行方案;
- 镜像支持增量分发,易于部署;
- 易于构建,良好的REST API,也很适合自动化测试和持续集成
- 性能,尤其是内存和IO的开销;
Docker的应用场景(能干什么)
- Web应用的自动化打包和发布
- 自动化测试和持续集成、发布
- 在服务型环境中部署和调整数据库或其他的后台应用
- 从头编译或者扩展现有的红帽OpenShift或Cloud Foundry平台来搭建自己的PaaS环境
获取Docker及文档资源
- Docker 入门官方文档(英文版,可通过Chrome进行翻译阅读):docs.docker.com/get-started...
- Docker 入门中文文档(包含Docker的入门及安装):www.docker.org.cn/book/
- 仓库是集中存放镜像的地方,分公共仓库和私有仓库。大部分镜像需求,都可以通过在Docker Hub中直接下载镜像来实现。
Docker公共镜像仓库:hub.docker.com
Docker 架构

- Client:Docker的客户端,Docker用户与Docker交流的主要方式,当用户通过Docker客户端发送指令时,比如执行docker run,Docker 会将指令发送给Docker的守护进程(Docker daemon);
- Docker守护进程 ( Docker daemon):用于监听Docker客户端的请求并管理Docker的镜像、容器、网络信息;
- Docker 注册仓库:Docker的注册仓库中存储着大量的Docker镜像,Docker Hub是任何人都可以使用的公共注册中心,当本地镜像不存在时,Docker会默认在Docker Hub上寻找所需的镜像资源。
-
- 镜像(image):Docker镜像(Image)是一个只读的模板。镜像可以用来创建Docker 容器,一个镜像可以创建很多容器。容器与镜像的关系类似于面向对象编程中的对象与类;
- 容器(container):Docker利用容器(Container)独立运行的一个或一组应用。容器是用镜像创建的运行实例,容器可以被启动、开始,停止、删除,每个容器都是互相隔离,保证安全的平台,可以把容器看做是一个精简版的Linux环境和运行在其中的应用程序;
- 仓库(repository):集中存放镜像文件的场所。仓库和仓库注册服务器是有区别的,仓库注册服务器上往往存放着多个仓库,每个仓库中又包含了多个镜像,每个镜像有不同的标签(tag)。仓库分为公开仓库(Public)和私有仓库(Private)两种格式,最大的公共仓库是Docker Hub,其他的仓库包括国内的阿里云、网易云等;
Docker 工作原理
Docker是一个Client-Server结构的系统,Docker守护进程运行在主机上,然后通过Socket 连接从客户端访问,守护进程从客户端接受命令并管理运行在主机上的机器。容器,是一个运行时的环境,可以把它理解为一个集装箱。

为什么Docker比虚拟机快?
- Docker 不需要 Hypervisor 实现硬件资源虚拟化,运行在Docker容器上的程序直接使用的都是实际物理机的硬件资源。因此在CPU、内存利用率上Docker将会在效率上有明显优势。
- Docker 利用的是宿主机的内核,而不需要GuestOS。因此,当新建一个容器时,Docker 不需要和虚拟机一样重新加载一个操作系统内核。可避免引导寻址、加载操作系统内核返回这个比较费时费资源的过程,当新建一个虚拟机时,虚拟机软件需要加载GuestOS,返回新增过程是分钟级别的。而Docker由于利用宿主机的操作系统,则省略了返回的过程,因此新建一个Docker容器只需要几秒钟。

Docker的安装
说明:本次安装操作,基于CentOS7下去完成的,版本信息和内核信息以uname-r和Isb_release-a的信息为准,Docker为社区版;
前提条件
Docker运行在CentOS7上,要求系统位64位,系统内核版本3.10以上(官方推荐)
Docker运行在CentOS6.5或更高的版本的CentOS上,要求系统为64位,系统内核版本为2.6.32-431或者更高版本
注:其他操作系统版本可参考Docker中文文档(Docker安装手册了解)
安装 Docker Engine
- 安装gcc、gcc-c++
bash
yum install -y gcc gcc-c++
# 测试gcc是否安装成功
gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/libexec/gcc/x86_64-redhat-linux/4.8.5/lto-wrapper
Target: x86_64-redhat-linux
Configured with:../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info--with-bugurl=http://bugzilla.redhat.com/bugzilla--enable-bootstrap--enable-shared--enable-threads=posix--enable-checking=release--with-system-zlib--enable-_cxa_atexit--disable-libunwind-exceptions--enable-gnu-unique-object--enable-linker-bu ild-id--with-linker-hash-style=gnu --enable-languages=c,c++,objc,obj-c++,java,fortran,ada,go,lto--enable-plugin--enable-initfini-array--disable-libgcj --with-isl=/builddir/build/BUILD/gcc-4.8.5-20150702/obj-x86_64-redhat-linux /isl-install--with-cloog=/builddir/build/BUILD/gcc-4.8.5-20150702/obj-x86_64-redhat-linux/cloog-install--enable-g nu-indirect-function --with-tune=generic--with-arch_32=x86-64--build=x86_64-redhat-linux
Thread model:pocix
gcc version 4.8.5 20150623_(Red Hat 4.8.5-36) (GCC)
- 卸载旧版本的DockerEngine
arduino
yum remove docker \
docker-client\
docker-client-latest\
docker-common\
docker-latest\
docker-latest-logrotate\
docker-logrotate\
docker-engine
- 配置 yum 存储库
bash
# 使用阿里 yum 源
yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
# 更新 yum 软件包索引
yum makecache fast
- 安装Docker Engine
lua
yum install -y docker-ce docker-ce-cli containerd.io
- 启动Docker服务
sql
systemctl start docker
- 设置Docker开机自启动
bash
systemctl enable docker
7.查看 docker 版本信息
yaml
[root@iZikh2x5epj8p6Z ~]# docker version
Client: Docker Engine -Community
Version: 19.03.1
API version: 1.40
Go version: go1.12.5
Git commit: 74b1e89
Built: Thu Jul 25 21:21:07 2019
OS/Arch: linux/amd64
Experimental: false
Server: Docker Engi Engine: ne -Community
Version: 19.03.1
API version: 1.40 (minimum version 1.12)
Go version: go1.12.5
Git commit: 74b1e89
Built: Thu Jul 25 21:19:36 2019
OS/Arch: linux/amd64
Experimental:
containerd:
Version: false
1.2.6
GitCommit:
runc:
Version:
GitCommit:
docker-init: 894b81a4b802e4eb2a91d1ce216b8817763c29fb
1.0.0-rc8
425e105d5a03fabd737a126ad93d62a9eeede87f
Version: 0.18.0
GitCommit:
[root@iZikh2x5epj8p fec3683
6Z ~]#
配置镜像加速
关于获取阿里云Docker镜像加速的地址可参考:blog.csdn.net/qq_37495786...
- 创建/etc/docker目录
bash
mkdir/etc/docker/
- 创建
/etc/docker/daemon.json
文件,并编辑
csharp
[root@iZikh2x5epj8p6Z ~]# vi /etc/docker/daemon.json
# 网易云
{
"registry-mirrors": ["http://hub-mirror.c.163.com"]
}
# 阿里云
{
"registry-mirrors": ["https://fix4nmlz.mirror.aliyuncs.com"]
}
-
重新加载daemon.json文件
systemctl daemon-reload
-
重启docker服务,使配置文件信息生效
systemctl restart docker.service
重新加载daemon.json文件后,重启服务出错怎么办?
通过systemctl status docker.service
命令查询docker服务状态,发现只剩下Client info,没有 Server 信息
perl
[root@iZikh2x5epj8p6Z ~]# systemctl restart docker.service
Job for docker.service failed because start of the service was attempted too often.See "systemctl status docker.se rvice" and "journalctl -xe" for details.To force a start use "systemctl reset-failed docker.service" followed by "systemctl start docker.service" again.
[root@iZikh2x5epj8p6Z ~]# docker version
Client:Docker Engine-Community
Version: 19.03.1
API version: 1.40
Go version: go1.12.5
Git commit: 74b1e89
Built: Thu Jul 25 21:21:07 2019
OS/Arch: linux/amd64
Experimental: false
error during connect: Get http://%2Fvar%2Frun%2Fdocker.sock/v1.40/version: read unix @->/var/run/docker.sock: read:connection reset by peer
- 解决方案 :
参考:www.e-learn.cn/content/qit...
将 /etc/docker
目录下的 daemon.json 文件使用重命名为 daemon.conf ,然后重新加载 daemon.conf 配置文件。
HelloWorld
vbnet
[root@iZikh2x5epj8p6Z ~]# docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
1b930d010525: Pull complete
Digest:sha256:451ce787d12369c5df2a32c85e5a03d52cbcef6eb3586dd03075f3034f10adcd
Status: Downloaded newer image for hello-world:latest
Hello from Docker!
This message shows that your installation appears to be working correctly.
To generate this message, Docker took the following steps:
1. The Docker client contacted the Docker daemon.
2.The Docker daemon pulled the "hello-world" image from the Docker Hub.
(amd64)
3. The Docker daemon created a new container from that image which runs the
executable that produces the output you are currently reading.
4.The Docker daemon streamed that output to the Docker client, which sent it
to your terminal.
To try something more ambitious, you can run an Ubuntu container with:
$ docker run -it ubuntu bash
Share images, automate workflows, and more with a free Docker ID:
https://hub.docker.com/
For more examples and ideas, visit:
https://docs.docker.com/get-started/
docker run 的工作流程图

卸载Docker引擎
- 停止docker 服务
arduino
systemctl stop docker
- 卸载Docker包
arduino
yum remove -y docker-ce
- 主机上的镜像,容器、卷或者自己创建的配置文件不会被自动删除。要删除所有图像,容器和卷,执行以下操作:
bash
rm -rf/var/lib/docker
- 手动删除相关配置文件:/etc/docker/daemon.conf 文件
Docker 常用命令
帮助命令
获取 docker 的版本信息
yaml
[root@izikh2x5epj8p6z~]# docker version
Client:Docker Engine-Community
Version: 19.03.1
API version: 1.40
Go version: go1.12.5
Git commit: 74b1e89
Built: Thu Jul 25 21:21:07 2019
OS/Arch: linux/amd64
Experimental: false
Server:Docker Engine-Community
Engine:
Version: 19.03.1
API version: 1.40 (minimum version 1.12)
Go version: go1.12.5
Git commit: 74b1e89
Built: Thu Jul 25 21:19:36 2019
OS/Arch: linux/amd64
Experimental: false
containerd:
Version: 1.2.6
GitCommit: 894b81a4b802e4eb2a91d1ce216b8817763c29fb
runc:
Version: 1.0.0-rc8
GitCommit: 425e105d5a03fabd737a126ad93d62a9eeede87f
docker-init:
Version: 0.18.0
GitCommit: fec3683
获取docker 详细信息
ruby
[root@izikh2x5epj8p6z~]# docker info
Client:
Debug Mode:false
Server:
Containers: 1
Running:0
Paused: 0
Stopped:1
Images: 1
Server Version: 19.03.1
Storage Driver:overlay2
Backing Filesystem:extfs
Supports d_type:true
Native Overlay Diff:true
Logging Driver:json-file
Cgroup Driver:cgroupfs
Plugins:
Volume: local
Network: bridge host ipvlan macvlan null overlay
Log:awslogs fluentd gcplogs gelf journald json-file local logentries splunk syslog
Swarm: inactive
Runtimes:runc
Default Runtime:runc
Init Binary:docker-init
containerd version:894b81a4b802e4eb2a91d1ce216b8817763c29fb
runc version: 425e105d5a03fabd737a126ad93d62a9eeede87f
init version: fec3683
Security Options:
seccomp
Profile: default
Kernel Version: 3.10.0-957.27.2.el7.x86_64
Operating System: CentOS Linux 7 (Core)
OSType: linux
Architecture: x86_64
CPUs: 1
Total Memory:1.795GiB
Name: izikh2x5epj8p6z
ID:XQTQ:RWKG:514B:KH5R:ZY6W:CCVB:UW42:FN74:3CJO:22QT:Z22S:FOFD
Docker Root Dir:/var/lib/docker
Debug **Mode:**false
Registry:https://index.docker.io/v1/
Labels:
Experimental:false
Insecure Registries:
127.0.0.0/8
Live Restore Enabled:false
获取docker的命令帮助
bash
docker --help
镜像命令
列出本地主机上的镜像
语法:docker images[参数可选]
csharp
[root@izikh2x5epj8p6z~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
hello-world latest fce289e99eb9 7 months ago 1.84kB
- REPOSITORY:表示镜像的仓库源;
- TAG:镜像的标签
- IMAGE ID:镜像ID
- CREATED:镜像的创建时间
- SIZE:镜像大小
参数说明:
- -a:列出本地所有的镜像(含中间映像层)
- -q:只显示镜像ID
- --digests:显示镜像的摘要信息
- --no-trunc:显示完整的镜像信息
同一仓库源可以有多个 TAG ,代表这个仓库源的不同个版本,使用REPOSITORY:TAG来定义不同的镜像。
如果不指定一个镜像的版本标签,例如只使用ubuntu,docker默认使用ubuntu:latest 镜像。
搜索镜像
语法:docker search[参数可选]镜像名称
Docker查询走的是的 hub.docker.com 网站的信息,而下载是走的是镜像加速的地址;
bash
[root@izikh2x5epj8p6z -]# docker search tomcat
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
tomcat Apache Tomcat is an open source implementati... 2499 [OK]
appsvc/tomcat
picoded/tomcat7 tomcat7 with jre8 and MANAGER_USER / MANAGER... [OK]
NAME:镜像名称
DESCRIPTION:镜像说明
STARS:点赞数
OFFICIAL:是否是官方发布【OK表示是】
AUTOMATED:是否自动构建【OK表示是】
参数说明:
- --no-trunc:显示完整的镜像描述
- -s:列出收藏数不小于指定值的镜像
- --automated:只列出 automatedbuild(自动构建)类型的镜像
下载镜像
语法:docker pull 镜像名称[:TAG]
TAG可选参数,表示版本,比如ubuntu:16.04,如果不加:TAG,则docker默认会下最新版
makefile
[root@izikh2x5epj8p6z /]# docker pull tomcat
Using default tag: latest
latest: Pulling from library/tomcat
9cc2ad81d40d:Pull complete
e6cb98e32a52: Pull complete
ae1b8d879bad: Pull complete
42cfa3699b05: Pull complete
8d27062ef0ea: Pull complete
9b91647396e3: Pull complete
7498c1055ea3: Pull complete
a183d8c2c929:Pull complete
73dd800dda4c: Pull complete
2bc71ef979ec: Pull complete
Digest: sha256:80db17f3efd9cdcd9af7c799097fe0d223bbee8f25aa36234ab56292e3d8bd7b
Status: Downloaded newer image for tomcat:latest
docker.io/library/tomcat:latest
此时,使用 docker images
命令,可查看本地主机的镜像信息
csharp
[root@izikh2x5epj8p6z /]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
tomcat latest 96c4e536d0Oeb 35 hours ago 506MB
hello-world latest fce289e99eb9 7 months ago 1.84kB
删除镜像
- 删除单个镜像
语法:dockerrmi 镜像名称
如果不加:TAG,则表示默认删除:latest(最新版)
csharp
[root@izikh2x5epj8p6z/]# docker rmi hello-world
Error response from daemon: conflict: unable to remove repository reference "hello-world" (must forc e) - container 428c8065f43f is using its referenced image fce289e99eb9
⚠️如果出现这种情况,表示该容器在运行中,如果需要删除,需要通过docker rmi -f
强制执行。
makefile
[root@izikh2x5epj8p6z /]# docker rmi -f hello-world
Untagged: hello-world:latest
Untagged: hello-world@sha256:451ce787d12369c5df2a32c85e5a03d52cbcef6eb3586dd03075f3034f10adcd
Deleted: sha256:fce289e99eb9bca977dae136fbe2a82b6b7d4c372474c9235adc1741675f587e
- 删除多个镜像
语法:docker rmi-f 镜像名1[:TAG]镜像名2[:TAG]...
csharp
[root@izikh2x5epj8p6z ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
tomcat latest 96c4e536d0eb 35 hours ago 506MB
httpd latest 7d85cc3b2d80 38 hours ago 154MB
nginx latest 5a3221f0137b 7 days ago 126MB
redis latest f7302e4ab3a8 8 days ago 98.2MB
hello-world latest fce289e99eb9 7 months ago 1.84kB
# 需求:同时删除 hello-world、httpd、nginx、redis
docker rmi -f hello-world httpd nginx redis
删除全部
语法:docker rmi -f $(docker images -qa)
列出镜像的变更历史
语法:docker history 镜像名称或镜像 ID
常用容器命令
新建并启动容器
语法:docker run[选项参数]镜像名称或镜像ID[COMMAND] [ARG...]
参数说明:
- --name="容器新名字":为容器指定一个名称;
- -d:后台运行容器,并返回容器ID,也即启动守护式容器;
- -i:以交互模式运行容器,通常与-t同时使用;
- -t:为容器重新分配一个伪输入终端(类似与Linux中的shell窗口,或者Windows 下的CMD窗口),通常与-i同时使用;
- -P:随机端口映射
- -p:指定端口映射,有以下四种格式
- ip:hostPort:containerPort
- ip::containerPort
- hostPort:containerPort
- containerPort
示例:使用镜像 centos:latest
以交互模式启动一个容器,在容器内执行 /bin/bash 命令。
csharp
[root@izikh2x5epj8p6z ~]# docker images
REPOSITORY TAG
centos latest IMAGE ID
67fa590cfc1c CREATED
2 days ago SIZE
202MB
[root@izikh2x5epj8p6z ~]#
[root@izikh2x5epj8p6z ~]# docker run -it 67fa590cfc1c
[root@3510ef71ecdc /]# ls mnt proc run
anaconda-post.log dev home lib64 srv tmp var
bin etc lib media opt root sbin sys usr
列出当前所有正在运行的容器
语法:docker ps[可选参数]
csharp
[root@izikh2x5epj8p6z -]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ee0e4e99e13b 67fa590cfclc "/bin/bash" 23 seconds ago Up 22 seconds adoring_lederberg
CONTAINER ID:容器ID
IMAGE:镜像ID,如果以镜像名run,则这里会是镜像名称
COMMAND:启动容器时运行的命令,这里的意思是以"/bin/bash"的方式登录到这个容器
CREATED:容器的创建时间
STATUS:容器状态
PORTS:容器的端口信息和使用的连接类型
NAMES:镜像自动为容器创建的名字
常用参数说明:
- -a: 列出当前所有正在运行的容器+历史上运行过的
:显示最近(上一个)创建的容器
- -n 次数:显示最近n个创建的容器,如:-n10(显示上面10次创建的容器信息)
- -q:静默模式,只显示容器编号
- --no-trunc:不截断输出
退出容器
语法:
- 容器停止退出:exit
- 容器不停止退出: ctrl + p + q
停止容器
语法:docker stop 容器ID或容器名
启动容器
语法:docker start 容器ID或容器名
重启容器
语法:docker restart 容器ID或容器名
强制停止容器
语法:docker kill 容器ID或容器名
删除已停止的容器
语法:docker rm[可选参数] 容器ID或容器名
参数说明:
- -f:表示强制删除
一次性删除多个容器
语法:docker rm -f $(docker ps -qa)
Docker 镜像
什么是镜像?
镜像是一种轻量级、可执行的独立软件包,用来打包软件运行环境和基于运行环境开发的软件,它包含运行某个软件所需的所有内容,包括代码、运行时的库、环境变量和配置文件。
UnionFS(联合文件系统)
Union File System 是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下。
Union 文件系统是 Docker 镜像的基础。镜像可以通过分层来进行继承,基于基础镜像(没有父镜像),可以制作各种具体的应用镜像。
特征:一次同时加载多个文件系统,但从外面看起来,只能看到一个文件系统,联合加载会把各层文件系统叠加起来,这样最终的文件系统会包含所有底层的文件和目录。
Docker 镜像加载原理
Docker 的镜像实际由一层一层的文件系统组成,这种层级的文件系统叫做 UnionFS。
- bootfs(boot file system):主要包含 bootloader 和 kernel,bootloader 主要是引导加载kernel,Linux 刚启动时会加载 bootfs 文件系统,在 Docker 镜像的最底层是 bootfs。包含 boot 加载器和内核。当 boot 加载完成之后整个内核就在内存中了,此时内存的使用权已由 bootfs 转交给内核,此时系统也会卸载 bootfs。
- rootfs(root file system):在 bootfs 之上,包含的是典型 Linux 系统中的/dev,/proc、/bin、/etc 等标准目录和文件。Rootfs 就是各种不同的操作系统发行版,比如 Ubuntu,CentOS等等。
为什么系统镜像那么大一个,而 docker 里面的那么小?
对于一个精简版的 OS,rootfs 可以很小,只需要包括最基本的命令、工具和程序库就可以了,因为底层直接用的主机的内核,自己只需要提供 rootfs 就可以了。
对于不同的 Linux 发行版,bootfs 基本是一致的,rootfs 会有差别,因此不同的发行版可以公用 bootfs。
为什么 docker 镜像要采用这种分层结构?
好处就是共享资源。一个镜像可能是由多个相同的 base 镜像构建的,宿主机只需要保存一份,在需要时加载到内存中,所有的容器就都可以使用了。而且镜像的每一层都可以被共享。
镜像的特点
Docker 镜像都是只读的(当容器启动时,一个新的可写层被加载到镜像的顶部。这一层通常被称作"容器层","容器层"之下的都叫做"镜像层")。

提交镜像
镜像提交就是对一个本地已存在的容器做修改后,根据这个容器创建的本地镜像,这个过程叫做镜像提交。
语法:docker commit [选项参数] 容器 ID 或容器名称 要创建的目标镜像名:[标签名(版本号)]
选项参数
- -a:作者信息
- -m:镜像说明信息
Docker 容器卷
什么是容器数据卷
Docker 容器产生的数据,如果不通过 docker commit 生成新的镜像,使得数据可以做为镜像的一部分保存下来,当容器删除以后,容器中的数据也就没有了,为了使容器中的数据可以保存下来,我们使用卷。
数据卷可以干嘛?
卷是目录或文件,存在于一个或多个容器中,由 docker 挂载到容器,但不属于联合文件系统,因此能绕过联合文件系统提供一些用于持续存储或共享数据的特性;
卷的设计目的就是数据持久化,完全独立于容器的生存周期,因此 docker 不会在容器删除时删除其挂载的数据卷。
特点:
- 数据卷可在容器之间共享或重用数据
- 卷中更改的可以直接生效
- 数据卷中的更改不会包含在镜像的更新中
- 数据卷的生命周期一直持续到没有容器使用它为止
数据卷的挂载
语法:docker run --it --v /宿主机绝对路径目录:/容器内目录[:权限] 镜像名或镜像 ID

可通过 docker inspect
镜像名称或镜像 ID 查看镜像的详细信息

⚠️注意:当容器停止退出后,如果宿主机的卷挂载目录文件有变动,重新启动容器后,容器内的卷挂载目录会和宿主机卷挂载目录的文件内容自动同步。
带权限的挂载
bash
docker run -it -v /hostDataVolume:/dataVolume:ro centos
ro:Read-Only,中文意思就是只读
rw:Read,Write,可读可写
添加数据卷
在 Dockerfile 中使用 VOLUME 指令来给镜像添加一个或多个数据卷
语法:VOLUME["/dataVolumeContainer1","/dataVolumeContainer3","/dataVolumeContainer3"]
说明:
- 出于可移植和分享的考虑,用-v 主机目录:容器目录这种方法不能够直接在Dockerfile 中实现。
- 由于宿主机目录是依赖于特定宿主机的,并不能够保证在所有的宿主机上都存在这样的特定目录。
bash
# 来源于哪个镜像
from centos
# 数据卷名称
VOLUME ["/dataVolumeContainer1","/dataVolumeContainer2"]
# 输出回显信息
CMD echo "finished,------>success"
# 相当于docker run -it -v /宿主机目录1:/dataVolumeContainer1 -v /宿主机目录2:/dataVolumeContainer2 centos /bin/bash
CMD /bin/bash
通过 docker build
构建 Dockerfile 文件,生成新镜像
语法:docker build --f Dockerfile 文件位置 --t 新镜像名称 新镜像生成位置


CentOS7 下的容器卷路径默认地址为:/var/lib/docker/volumes/bf72aebc25e62d53e54121d162085a663e78ffc9e188d58ee567c4f5edf7dc4f/_data
bf72aebc25e62d53e54121d162085a663e78ffc9e188d58ee567c4f5edf7dc4f 为镜像 ID
数据卷容器
什么是数据卷容器
命名的容器挂载数据卷,其他容器通过挂载这个(父容器)实现数据共享,挂载数据卷的容器,称为数据卷容器。
容器间传递共享
说明:--volumes-from 容器名称或容器 ID 表示继承一个容器


Dockerfile
什么是 Dockerfile
Dockerfile 是用来构建 Docker 镜像的文本文件,是由一系列命令和参数构建的脚本。
Dockerfile 构建镜像的流程
-
手动编写一个 Dockerfile 文件,该文件需要符合 Dockerfile 的规范
-
通过编写的 Dockerfile 文件,使用 docker build 命令,获取一个自定义的镜像
-
使用 docker run 来运行这个镜像
简单理解为:编写 Dockerfile 文件 ➡️ docker build ➡️ docker run
Dockerfile 的 Demo(CentOS6.8)
sql
FROM scratch
MAINTAINER The CentOS Project <cloud-ops@centos.org>ADD c68-docker.tar.xz /
LABEL name="CentOS Base Image" \vendor="CentOS" \license="GPLv2" \build-date="2016-06-02"
# Default command
CMD ["/bin/bash"]
Dockerfile 构建过程解析
Dockerfile 基础知识
- 每条保留字指令都必须为大写字母且后面要跟随至少一个参数
- 指令按照从上到下,顺序执行
- #表示注释
- 每条指令都会构建一个新的镜像层,并对镜像进行提交
Docker 执行 Dockerfile 的大致流程
- Docker 从基础镜像运行一个容器(scratch)
- 执行一条指令并对容器作出修改
- 执行类似 docker commit 的操作提交一个新的镜像层
- Docker 再基于刚提交的镜像运行一个新容器
- 执行 dockerfile 中的下一条指令直到所有指令都执行完成

总结:
- Dockerfile 是软件的原材料
- Docker 镜像是软件的交付品
- Docker 容器可以认为是软件的运行态
Dockerfile 体系结构
- FROM:基础镜像,当前新镜像是基于哪个镜像的
- MAINTAINER:镜像维护者的姓名和邮箱地址
- RUN:容器构建时需要运行的命令
- EXPOSE:当前容器对外暴露出的端口号
- WORKDIR:指定在创建容器后,终端默认登录的进来工作目录
- ENV:用来在构建镜像过程中设置环境变量,比如:ENV TEMP_DIR /usr/local,那么在调用该环境变量时,就是$TEMP_DIR
- ADD:将宿主机目录下的文件拷贝进镜像且 ADD 命令会自动处理 URL 和解压 tar 压缩包
- COPY:类似 ADD,拷贝文件和目录到镜像中。将从构建上文目录中<源路径>的文件/目录复制到新的一层镜像内的<目标路径>位置
-
- COPY 源文件路径 目标文件路径
- COPY ["源路径","目标路径"]
- VOLUME:容器数据卷,用于数据保存和持久化工作
- CMD:指定一个容器启动时要运行的命令
-
- Dockerfile 中可以有多个 CMD 命令,但只有最后一个生效,CMD 会被 docker run 之后的参数替换
- Shell 格式:cmd <命令>
- exec 格式:CMD ["参数 1","参数 2","参数 3",...]
- ENTRYPOINT:指定一个容器启动时要运行的命令,ENTRYPOINT 的目的和 CMD 一样,都是在指定容器启动程序及参数
- ONBUILD:当构建一个被继承的 Dockerfile 时运行命令,父镜像在被子类继承后父镜像的 onbuild 被触发
Dockerfile 案例
- Base 镜像(scratch):Docker Hub 中 99%的镜像都是通过在 base 镜像中安装和配置需要的软件构建出来的。可以把它理解为 Java 中的 Object 类;
- 自定义镜像 CentOS
- 编写 dockerfile
bash
# 表示从这个镜像作为模板
FROM centos
# 镜像作者信息
MAINTAINER 自定义 Dockerfile 的 Demo zhao.yang@landasoft.com
CMD echo 功能:自定义构建 CentOS 的 Dockerfile 的 Demo 作者:zhao.yang@landasoft.com
# 配置登陆容器后的工作目录ENV WORK_PATH /use/localWORKDIR $WORK_PATH
# 配置文本编辑器 vim 和 ifconfig 命令的支持
RUN yum install -y vim # 使用 yum 安装 vim
# 控制台打印消息信息
CMD echo 文本编辑器 vim 安装完成
# 安装 net-tools(net-tools 是 ifconfig 命令所在的软件包)RUN yum install -y net-tools
CMD echo net-tools 网络工具安装完成
# 对外暴露 80 端口
EXPOSE 80
# 镜像启动时,默认执行的命令
CMD /bin/bash
- 构建镜像
bash
docker build -f customize_centos7.6 -t gotojava/centos7.6 .