Docker学习
参考网址:
Docker — 从入门到实践 · Yuque
一,Docker简介
Docker 使用 Google 公司推出的 Go 语言 进行开发实现,基于 Linux 内核的 cgroup,namespace,以及 AUFS 类的 Union FS 等技术,对进程进行封装隔离,属于 操作系统层面的虚拟化技术。由于隔离的进程独立于宿主和其它的隔离的进程,因此也称其为容器。
Docker 在容器的基础上,进行了进一步的封装,从文件系统、网络互联到进程隔离等等,极大的简化了容器的创建和维护。使得 Docker 技术比虚拟机技术更为轻便、快捷。
1.1 Docker的优势
-
更高效的利用系统资源
由于容器不需要进行硬件虚拟以及运行完整操作系统等额外开销,Docker 对系统资源的利用率更高。
-
更快速的启动时间
传统的虚拟机技术启动应用服务往往需要数分钟,而 Docker 容器应用,由于直接运行于宿主内核,无需启动完整的操作系统,因此可以做到秒级、甚至毫秒级的启动时间。
-
一致的运行环境
Docker 的镜像提供了除内核外完整的运行时环境,确保了应用运行环境一致性,从而不会再出现 「这段代码在我机器上没问题啊」 这类问题。
-
持续交付和部署
使用 Docker 可以通过定制应用镜像来实现持续集成、持续交付、部署。开发人员可以通过 Dockerfile 来进行镜像构建,并结合 持续集成(Continuous Integration) 系统进行集成测试,而运维人员则可以直接在生产环境中快速部署该镜像,甚至结合 持续部署(Continuous Delivery/Deployment) 系统进行自动部署。
-
更轻松的迁移
由于 Docker 确保了执行环境的一致性,使得应用的迁移更加容易。
-
更轻松的维护和扩展
Docker 使用的分层存储以及镜像的技术,使得应用重复部分的复用更为容易,也使得应用的维护更新更加简单,基于基础镜像进一步扩展镜像也变得非常简单。
特性 | 容器 | 虚拟机 |
---|---|---|
启动 | 秒级 | 分钟级 |
硬盘使用 | 一般为 MB | 一般为 GB |
性能 | 接近原生 | 弱于 |
系统支持量 | 单机支持上千个容器 | 一般几十个 |
1.2 Docker三大组件
1.2.1 镜像
Docker 镜像是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等)。镜像不包含任何动态数据,其内容在构建之后也不会被改变。
docker镜像采用分层存储的架构,是由一组文件系统组成,或者说,由多层文件系统联合组成。镜像构建时,会一层层构建,前一层是后一层的基础。每一层构建完就不会再发生改变,后一层上的任何改变只发生在自己这一层。
1.2.2 容器
镜像(Image
)和容器(Container
)的关系,就像是面向对象程序设计中的 类
和 实例
一样,镜像是静态的定义,容器是镜像运行时的实体。容器可以被创建、启动、停止、删除、暂停等。
前面讲过镜像使用的是分层存储,容器也是如此。每一个容器运行时,是以镜像为基础层,在其上创建一个当前容器的存储层,我们可以称这个为容器运行时读写而准备的存储层为容器存储层。
容器存储层的生存周期和容器一样,容器消亡时,容器存储层也随之消亡。因此,任何保存于容器存储层的信息都会随容器删除而丢失。
所有的文件写入操作,都应该使用 数据卷(Volume)、或者绑定宿主目录,在这些位置的读写会跳过容器存储层,直接对宿主(或网络存储)发生读写,其性能和稳定性更高。
数据卷的生存周期独立于容器,容器消亡,数据卷不会消亡。因此,使用数据卷后,容器删除或者重新运行之后,数据却不会丢失。
1.2.3 仓库
Docker Registry 是一个集中的存储、分发镜像的服务。一个 Docker Registry 中可以包含多个仓库(Repository
);每个仓库可以包含多个标签(Tag
);每个标签对应一个镜像。
通常,一个仓库会包含同一个软件不同版本的镜像,而标签就常用于对应该软件的各个版本。我们可以通过 <仓库名>:<标签>
的格式来指定具体是这个软件哪个版本的镜像。如果不给出标签,将以 latest
作为默认标签。
-
Docker Registry 公开服务
最常使用的 Registry 公开服务是官方的 Docker Hub,这也是默认的 Registry,并拥有大量的高质量的官方镜像。除此以外,还有 CoreOS 的 Quay.io,CoreOS 相关的镜像存储在这里;Google 的 Google Container Registry,Kubernetes 的镜像使用的就是这个服务。
-
私有 Docker Registry
除了使用公开服务外,用户还可以在本地搭建私有 Docker Registry。Docker 官方提供了 Docker Registry 一节中,会有进一步的搭建私有 Registry 服务的讲解。
二,Docker上手
同时 Docker 划分为 CE 和 EE。CE 即社区版(免费,支持周期三个月),EE 即企业版,强调安全,付费使用。
Docker CE 每月发布一个 Edge 版本 (17.03, 17.04, 17.05…),每三个月发布一个 Stable 版本 (17.03, 17.06, 17.09…),Docker EE 和 Stable 版本号保持一致,但每个版本提供一年维护。
项目 | 说明 |
---|---|
版本格式 | YY.MM |
Stable 版本 | 每个季度发行 |
Edge 版本 | 每个月发行 |
2.1 Docker安装
2.1.1 各类系统下的安装
参考官方文档
2.1.2 加速器
国内从 Docker Hub 拉取镜像有时会遇到困难,此时可以配置镜像加速器。Docker 官方和国内很多云服务商都提供了国内加速器服务,例如:
我们以 Docker 官方加速器为例进行介绍。
- Ubuntu 16.04+、Debian 8+、CentOS 7
对于使用systemd的系统,请在 /etc/docker/daemon.json
中写入如下内容(如果文件不存在请新建该文件)
{
"registry-mirrors": [
"https://registry.docker-cn.com"
]
}
注意,一定要保证该文件符合 json 规范,否则 Docker 将不能启动。
之后重新启动服务:
sudo systemctl daemon-reload
sudo systemctl restart docker
- Windows 10
对于使用 Windows 10 的系统,在系统右下角托盘 Docker 图标内右键菜单选择 Settings,打开配置窗口后左侧导航菜单选择 Daemon。在 Registry mirrors 一栏中填写加速器地址 https://registry.docker-cn.com
,之后点击 Apply 保存后 Docker 就会重启并应用配置的镜像地址了。
- macOS
对于使用 macOS 的用户,在任务栏点击 Docker for mac 应用图标 -> Perferences… -> Daemon -> Registry mirrors。在列表中填写加速器地址 https://registry.docker-cn.com
。修改完成之后,点击 Apply & Restart 按钮,Docker 就会重启并应用配置的镜像地址了。
- 检查加速器是否生效
配置加速器之后,如果拉取镜像仍然十分缓慢,请手动检查加速器配置是否生效,在命令行执行 docker info,如果从结果中看到了如下内容,说明配置成功。
Registry Mirrors:
https://registry.docker-cn.com/
2.2 使用镜像
2.2.1 拉取镜像:docker pull
从 Docker 镜像仓库获取镜像的命令是 docker pull
。其命令格式为:
# 格式
docker pull [选项] [Docker Registry 地址[:端口号]/]仓库名[:标签]
# 举例
docker pull ubuntu:16.04
具体的选项可以通过 docker pull --help
命令看到,这里我们说一下镜像名称的格式。
- Docker 镜像仓库地址:地址的格式一般是
<域名/IP>[:端口号]
。默认地址是 Docker Hub。 - 仓库名:如之前所说,这里的仓库名是两段式名称,即
<用户名>/<软件名>
。对于 Docker Hub,如果不给出用户名,则默认为library
,也就是官方镜像。
使用Docker run
命令运行镜像:
docker run -it --rm \
ubuntu:16.04 \
bash
docker run
参数解读:
it
:这是两个参数,一个是i
:交互式操作,一个是t
终端。我们这里打算进入bash
执行一些命令并查看返回结果,因此我们需要交互式终端。-rm
:这个参数是说容器退出后随之将其删除。默认情况下,为了排障需求,退出的容器并不会立即删除,除非手动docker rm
。我们这里只是随便执行个命令,看看结果,不需要排障和保留结果,因此使用-rm
可以避免浪费空间。ubuntu:16.04
:这是指用ubuntu:16.04
镜像为基础来启动容器。bash
:放在镜像名后的是命令,这里我们希望有个交互式 Shell,因此用的是bash
。
最后通过 exit
退出容器。
2.2.2 查看镜像:docker image ls
-
虚悬镜像
由于新旧镜像同名,旧镜像名称被取消,从而出现仓库名、标签均为 的镜像。这类无标签镜像也被称为 虚悬镜像(dangling image) ,下面会给出命令专门显示这类镜像。一般来说,虚悬镜像已经失去了存在的价值,是可以随意删除的,可以用下面的镜像删除命令来删除。
-
中间层镜像
为了加速镜像构建、重复利用资源,Docker 会利用 中间层镜像。所以在使用一段时间后,可能会看到一些依赖的中间层镜像。查看包括中间层镜像在内的所有镜像的见以下命令。
# 注:这些命令都需要root权限或者sudo命令来执行
# 以下两个命令等效
docker image ls
docker images
# 查看镜像体积
docker system df
# 查看虚悬镜像
docker image ls -f dangling=true
# 查看包括中间层镜像在内的所有镜像
docker image ls -a
# 删除镜像
docker image prune
# 过滤
docker image ls -f <过滤条件>
# 以特定格式输出
docker image ls -q
2.2.3 启动镜像:docker start
# 启动所有镜像
docker start $(docker ps -a -q)
2.2.4 删除镜像:docker image rm
-
Untagged 和 Deleted
如果观察上面这几个命令的运行输出信息的话,你会注意到删除行为分为两类,一类是
Untagged
,另一类是Deleted
。我们之前介绍过,镜像的唯一标识是其 ID 和摘要,而一个镜像可以有多个标签。因此当我们使用上面命令删除镜像的时候,实际上是在要求删除某个标签的镜像。所以首先需要做的是将满足我们要求的所有镜像标签都取消,这就是我们看到的
Untagged
的信息。因为一个镜像可以对应多个标签,因此当我们删除了所指定的标签后,可能还有别的标签指向了这个镜像,如果是这种情况,那么Delete
行为就不会发生。所以并非所有的docker rmi
都会产生删除镜像的行为,有可能仅仅是取消了某个标签而已。当该镜像所有的标签都被取消了,该镜像很可能会失去了存在的意义,因此会触发删除行为。镜像是多层存储结构,因此在删除的时候也是从上层向基础层方向依次进行判断删除。镜像的多层结构让镜像复用变动非常容易,因此很有可能某个其它镜像正依赖于当前镜像的某一层。这种情况,依旧不会触发删除该层的行为。直到没有任何层依赖当前层时,才会真实的删除当前层。这就是为什么,有时候会奇怪,为什么明明没有别的标签指向这个镜像,但是它还是存在的原因,也是为什么有时候会发现所删除的层数和自己
docker pull
看到的层数不一样的原因。除了镜像依赖以外,还需要注意的是容器对镜像的依赖。如果有用这个镜像启动的容器存在(即使容器没有运行),那么同样不可以删除这个镜像。之前讲过,容器是以镜像为基础,再加一层容器存储层,组成这样的多层存储结构去运行的。因此该镜像如果被这个容器所依赖的,那么删除必然会导致故障。如果这些容器是不需要的,应该先将它们删除,然后再来删除镜像。
-
批量删除
像其它可以承接多个实体的命令一样,可以使用
docker image ls -q
来配合使用docker image rm
,这样可以成批的删除希望删除的镜像。
# 注:这些命令都需要root权限或者sudo命令来执行
# 基本命令
docker image rm <镜像名或镜像ID>
# 批量删除举例
docker image rm $(docker image ls -q redis)
# 删除所有镜像
docker rmi $(docker images -q)
2.3 操作容器
2.3.1 启动容器:docker run
启动容器有两种方式,一种是基于镜像新建一个容器并启动,另外一个是将在终止状态(stopped
)的容器重新启动。
-
新建容器并启动:
docker run
当利用
docker run
来创建容器时,Docker 在后台运行的标准操作包括:- 检查本地是否存在指定的镜像,不存在就从公有仓库下载
- 利用镜像创建并启动一个容器
- 分配一个文件系统,并在只读的镜像层外面挂载一层可读写层
- 从宿主主机配置的网桥接口中桥接一个虚拟接口到容器中去
- 从地址池配置一个 ip 地址给容器
- 执行用户指定的应用程序
- 执行完毕后容器被终止
-
启动已终止容器:
docker container start
2.3.2 后台运行:docker run -d
默认直接将容器运行的输出打印到终端,因此要实现后台运行需要添加一个-d
的参数,例如
docker run -d ubuntu:17.10 /bin/sh -c "while true; do echo hello world; sleep 1; done"
使用-d参数后,容器启动会返回一个唯一的id输出到终端,此时可以利用这个id来查看容器的输出信息,命令为:
docker container logs <contain-id or container-name>
此外,也可以通过docker container ls
命令来查看正在运行的容器信息从而获得容器ID。
2.3.3 终止容器:docker container stop
- 可以使用
docker container stop
来终止一个运行中的容器。此外,当 Docker 容器中指定的应用终结时,容器也自动终止。 - 终止状态的容器可以用
docker container ls -a
命令看到。处于终止状态的容器,可以通过docker container start
命令来重新启动。 - 此外,
docker container restart
命令会将一个运行态的容器终止,然后再重新启动它。
2.3.4 进入容器:docker exec -it
在使用 -d
参数时,容器启动后会进入后台。这时候,如果因为某些需要而不得不进入容器进行操作,可以使用 docker attach
命令或 docker exec
命令,推荐使用 docker exec
命令。
# 例子
docker exec -it ubuntu bash
2.3.5 退出容器:exit
- exit :run进去容器,exit退出,容器停止;
- ctrl+p+q :run进去容器,ctrl+p+q退出,容器不停止;
2.3.6 查看容器状态:docker ps -xx
列出当前正在运行的容器:docker ps [OPTIONS]
OPTIONS:
(1) -a : 列出当前所有正在运行的容器 + 历史上运行过的
(2) -l : 显示最近创建的容器
(3) -n : 显示最近n个创建的容器
(4) -q : 静默模式,只显示容器编号
2.3.7 容器快照:docker export/import
-
导出容器快照:docker export
-
导出容器快照:docker import
注意:docker load导入的是镜像存储文件
2.3.8 删除容器:docker container rm
可以使用 docker container rm 来删除一个处于终止状态的容器。用 docker container ls -a 命令可以查看所有已经创建的包括终止状态的容器,用下面的命令可以清理掉所有处于终止状态的容器。
# 注:这些命令都需要root权限或者sudo命令来执行
# 删除特定容器
docker container rm <container-id>
# 删除运行中的容器
docker container rm -f <container-id>
# 删除全部处于终止状态的容器
docker container prune
# stop停止所有容器
docker stop $(docker ps -a -q)
# remove删除所有容器
docker rm $(docker ps -a -q)
如果要删除一个运行中的容器,可以添加 -f 参数。Docker 会发送 SIGKILL 信号给容器。
2.4 访问仓库
2.4.1 DockerHub
这是docker官方维护的一个公共仓库:https://cloud.docker.com。
# 注:这些命令都需要root权限或者sudo命令来执行
# 命令行中登录/退出dockerhub
docker login
docker logout
# 在dockerhub中搜索镜像
docker search <image-name>
# 下载仓库镜像
docker pull <image-name>
# 推送镜像到dockerhub:例子
docker tag ubuntu:17.10 username/ubuntu:17.10
docker push username/ubuntu:17.10
根据是否是官方提供,可将镜像资源分为两类。
一种是类似 centos
这样的镜像,被称为基础镜像或根镜像。这些基础镜像由 Docker 公司创建、验证、支持、提供。这样的镜像往往使用单个单词作为名字。
还有一种类型,比如 tianon/centos
镜像,它是由 Docker 的用户创建并维护的,往往带有用户名称前缀。可以通过前缀 username/
来指定使用某个用户提供的镜像,比如 tianon 用户。
2.4.2 私有仓库
在私有仓库中的docker操作类似DockerHub。
三,高级管理与配置
这一章介绍如何在 Docker 内部以及容器之间管理数据,在容器中管理数据主要有两种方式:
- 数据卷(Volumes)
- 挂载主机目录 (Bind mounts)
3.1 数据管理
3.1.1 数据卷
数据卷 是一个可供一个或多个容器使用的特殊目录,它绕过 UFS,可以提供很多有用的特性:
- 数据卷 可以在容器之间共享和重用
- 对 数据卷 的修改会立马生效
- 对 数据卷 的更新,不会影响镜像
- 数据卷 默认会一直存在,即使容器被删除
注意:数据卷 的使用,类似于 Linux 下对目录或文件进行 mount,镜像中的被指定为挂载点的目录中的文件会隐藏掉,能显示看的是挂载的 数据卷。
选择 -v 还是 -–mount 参数
Docker 新用户应该选择 --mount 参数,经验丰富的 Docker 使用者对 -v 或者 --volume 已经很熟悉了,但是推荐使用 --mount 参数。
创建数据卷:
# 创建一个数据卷
docker volume create my-vol
# 查看所有的数据卷
docker volume ls
# 查看指定数据卷
docker volume inspect my-vol
# 查看指定数据卷的详细信息
docker inspect web
# 删除数据卷
docker volume rm my-vol
# 清楚所有数据卷
docker volume prune
启动一个挂载数据卷的容器:
在用 docker run 命令的时候,使用 --mount 标记来将 数据卷 挂载到容器里。在一次 docker run 中可以挂载多个 数据卷。
下面创建一个名为 web 的容器,并加载一个 数据卷 到容器的 /webapp 目录。
docker run -d -P \
--name web \
# -v my-vol:/wepapp \
--mount source=my-vol,target=/webapp \
training/webapp \
python app.py
3.1.2 挂载主机目录
挂载一个主机目录作为数据卷
# 使用 --mount 标记可以指定挂载一个本地主机的目录到容器中去
docker run -d -P \
--name web \
# -v /src/webapp:/opt/webapp \
--mount type=bind,source=/src/webapp,target=/opt/webapp \
training/webapp \
python app.py
上面的命令加载主机的 /src/webapp 目录到容器的 /opt/webapp目录。这个功能在进行测试的时候十分方便,比如用户可以放置一些程序到本地目录中,来查看容器是否正常工作。本地目录的路径必须是绝对路径,以前使用 -v 参数时如果本地目录不存在 Docker 会自动为你创建一个文件夹,现在使用 --mount 参数时如果本地目录不存在,Docker 会报错。
Docker 挂载主机目录的默认权限是 读写,用户也可以通过增加 readonly 指定为 只读。
# 查看数据卷的具体信息
docker inspect web
# 挂载主机目录 的配置信息在 "Mounts" Key 下面
"Mounts": [
{
"Type": "bind",
"Source": "/src/webapp",
"Destination": "/opt/webapp",
"Mode": "",
"RW": true,
"Propagation": "rprivate"
}
],
挂载一个本地主机文件作为数据卷
# --mount 标记也可以从主机挂载单个文件到容器中
docker run --rm -it \
# -v $HOME/.bash_history:/root/.bash_history \
--mount type=bind,source=$HOME/.bash_history,target=/root/.bash_history \
ubuntu:17.10 \
bash
root@2affd44b4667:/# history
1 ls
2 diskutil list