Docker小册
Docker 简介
是什么
Docker 是一种开源的容器化平台,可以让开发者打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化。容器是一种轻量级、独立、可执行的软件包,包含了运行一个应用所需的所有内容:代码、运行时环境、系统工具、系统库等。Docker 可以让开发者快速地构建、测试和部署应用程序,而无需担心跨环境的兼容性问题。
传统虚拟机和容器
传统虚拟机(virtual machine):
传统虚拟机技术基于安装在主操作系统上的虚拟机管理系统(如 VirtualBox、VMware 等),创建虚拟机(虚拟出各种硬件),在虚拟机上安装从操作系统,在从操作系统中安装部署各种应用。
缺点:资源占用多、冗余步骤多、启动慢
Linux 容器(Linux Container,简称 LXC):
Linux 容器是与系统其他部分分隔开的一系列进程,从另一个镜像运行,并由该镜像提供支持进程所需的全部文件。容器提供的镜像包含了应用的所有依赖项,因而在从开发到测试再到生产的整个过程中,它都具有可移植性和一致性。
Linux 容器不是模拟一个完整的操作系统,而是对进程进行隔离。有了容器,就可以将软件运行所需的所有资源打包到一个隔离的容器中。容器与虚拟机不同,不需要捆绑一整套操作系统,只需要软件工作所需的库资源和设置。系统因此而变得高效轻量并保证部署在任何环境中的软件都能始终如一的运行
差异对比:
| 特性 | 容器 | 虚拟机 |
|---|---|---|
| 启动 | 秒级 | 分钟级 |
| 大小 | 一般为 Mb | 一般为 Gb |
| 速度 | 接近原生 | 比较慢 |
| 系统支持数量 | 单机支持上千个容器 | 一般几十个 |
能干嘛
- 应用程序容器化: Docker 可以将应用程序及其依赖项打包到一个容器中,使其在任何环境中都能以相同的方式运行,从而简化部署和维护。
- 开发环境管理: Docker 可以用于创建和管理开发环境,确保团队中的所有成员都使用相同的开发环境,避免由于环境差异而导致的问题。
- 持续集成和持续部署(CI/CD): Docker 可以与 CI/CD 工具集成,实现自动化构建、测试和部署流程,提高开发团队的效率。
- 微服务架构: Docker 可以帮助将应用程序拆分为多个微服务,每个微服务都运行在自己的容器中,从而实现更好的可伸缩性和灵活性。
- 快速部署和扩展: Docker 可以快速部署新的容器实例,并根据需求扩展容器数量,以满足应用程序的需求。
- 资源隔离和安全性: Docker 提供了一定程度的资源隔离,使得不同容器之间的应用程序能够相互独立运行,提高安全性。
总的来说,Docker 是一个非常灵活和强大的工具,可以帮助开发者和运维团队简化应用程序的开发、部署和管理过程。
Docker 架构
三个基本概念
- 镜像(Image):Docker 镜像(Image),就相当于是一个 root 文件系统。比如官方镜像 ubuntu:16.04 就包含了完整的一套 Ubuntu16.04 最小系统的 root 文件系统。
- 容器(Container):镜像(Image)和容器(Container)的关系,就像是面向对象程序设计中的类和实例一样,镜像是静态的定义,容器是镜像运行时的实体。容器可以被创建、启动、停止、删除、暂停等。
- 仓库(Repository):由某特定的 docker 镜像的所有迭代版本组成的镜像仓库。仓库可看成一个代码控制中心,用来保存镜像。
Docker 容器通过 Docker 镜像来创建。容器与镜像的关系类似于面向对象编程中的对象与类。
| Docker | 面向对象 |
|---|---|
| 容器 | 对象 |
| 镜像 | 类 |
基本架构
Docker 使用客户端-服务器 (C/S) 架构模式,使用远程 API 来管理和创建 Docker 容器。
Docker 守护进程运行在主机上,然后通过 Socket 连接从客户端访问,守护进程从容器接收命令并管理运行在主机上的容器

运行流程
这一块刚接触有点难以理解,先有个大概印象就可以,后续再回头补充
-
用户是使用 Docker Client 与 Docker Daemon 建立通信,并发送请求给后者
-
Docker Daemon 作为 Docker 架构的主体部分,首先提供 Docker Server 的功能使其可以接收 Docker Client 的请求
-
Docker Engine 执行 Docker 内部的一系列工作,每一项工作都是以一个 Job 的形式存在
-
Job 的运行过程中,当需要容器镜像时,则从 Docker Registry 中下载镜像,并通过镜像管理驱动 Graph Driver 将下载镜像以 Graph 的形式存储
-
当需要为 Docker 创建网络环境时,通过网络管理驱动 Network driver 创建并配置 Docker 容器网络环境
-
当需要限制 Docker 容器运行资源或执行用户指令等操作时,则通过 Exec driver 来完成
-
Libcontainer 是一项独立的容器管理包,Network driver 以及 Exec driver 都是通过 Libcontainer 来实现具体对容器进行的操作

| 概念 | 说明 |
|---|---|
| Docker 镜像(Images) | Docker 镜像是用于创建 Docker 容器的模板,比如 Ubuntu 系统。 |
| Docker 容器(Container) | 容器是独立运行的一个或一组应用,是镜像运行时的实体。 |
| Docker 客户端(Client) | Docker 客户端通过命令行或者其他工具使用 Docker SDK (https://docs.docker.com/develop/sdk/) 与 Docker 的守护进程通信。 |
| Docker 主机(Host) | 一个物理或者虚拟的机器用于执行 Docker 守护进程和容器。 |
| Docker Registry | Docker 仓库用来保存镜像,可以理解为代码控制中的代码仓库。 Docker Hub(https://hub.docker.com) 提供了庞大的镜像集合供使用。 一个 Docker Registry 中可以包含多个仓库(Repository);每个仓库可以包含多个标签(Tag);每个标签对应一个镜像。 通常,一个仓库会包含同一个软件不同版本的镜像,而标签就常用于对应该软件的各个版本。我们可以通过 <仓库名>:<标签> 的格式来指定具体是这个软件哪个版本的镜像。如果不给出标签,将以 latest 作为默认标签。 |
| Docker Machine | Docker Machine 是一个简化 Docker 安装的命令行工具,通过一个简单的命令行即可在相应的平台上安装 Docker,比如 VirtualBox、 Digital Ocean、Microsoft Azure。 |
安装 Docker
关于 Docker 的安装,不同平台的安装方式不太一样,本文中以CentOS 系统安装为例,若是想在其他平台安装 Docker,可以官网安装教程,或者菜鸟安装教程
先决条件
Docker 并非是一个通用的容器工具,它依赖于已存在并运行的 Linux 内核环境。目前,CentOS 仅发行版本中的内核支持 Docker。Docker 运行在 CentOS 7 (64-bit)上,要求系统为 64 位、Linux 系统内核版本为 3.8 以上,这里选用 Centos7.x
# 查看当前linux系统的发行版本
cat /etc/redhat-release
# uname命令用于打印当前系统相关信息(内核版本号、和操作系统类型等)。
uname -r

卸载旧版本
较旧的 Docker 版本称为 docker 或 docker-engine 。如果已安装这些程序,请卸载它们以及相关的依赖项。
sudo yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-engine
安装 gcc 环境
gcc 几乎是开发环境下的必装,我们在安装 centerOS 系统时,在软件选择界面有勾选安装[[]]开发工具选项,会自动带上 gcc 环境,如果不确定是否应安装了 gcc 环境,可参考我的另一篇文章 Redis 小册
yum -y install gcc-c++
配置 yum 资源库
安装
yum-config-manager:
# yum-util提供yum-config-manager功能
sudo yum install -y yum-utils

配置 docker 的资源库地址:
- 官方地址:(比较慢,不推荐),如果配置的镜像地址是国外的,很容易出现在拉取镜像时链接超时
# 在yum资源库中添加docker资源库
sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
- 阿里云镜像地址:
sudo yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo

阿里云官网提供了很多资源镜像,镜像地址:https://mirrors.aliyun.com,进入之后可以选择自己需要的资源进行配置
创建缓存(可选)
此处好像涉及 linux 的基础知识,执行了以下命令,后续下载会快一些。暂且在这留个坑吧! TODO
yum makecache fast

安装 Docker 引擎
安装最新版本的 Docker 引擎、Docker 客户端:
# docker-ce是Docker引擎,docker-ce-cli是客户端
sudo yum install docker-ce docker-ce-cli containerd.io docker-compose-plugin
如果要安装制定版本:
# 查询版本列表
yum list docker-ce --showduplicates | sort -r
# 指定版本安装17.09.0.ce版
# sudo yum install docker-ce-<VERSION_STRING> docker-ce-cli-<VERSION_STRING> containerd.io docker-compose-plugin
sudo yum install docker-ce-17.09.0.ce docker-ce-cli-17.09.0.ce containerd.io docker-compose-plugin
案例演示:

安装过程中会出现两次是否继续的选项,按照提示输入y,然后点击Enter即可

安装完成的提示:

启动 Docker 引擎
如果没有启动 Docker 引擎,那么执行 docker version查看版本号时,只能看到 Client: Docker Engine(Docker 引擎客户端)的版本号。
# 新版本的Docker就是一个系统服务,可以直接使用启动系统服务方式启动
systemctl start docker
# 此时查看docker版本,可以看到Server: Docker Engine(Docker引擎)版本号
docker version
配置用户权限
案例演示(异常分析):
未启动 Docker 引擎的情况下查看docker version,我们只能看到客户端的版本信息,然而启动 Docker 服务时,我们输入了一次用户密码,发现由于权限问题仍然没有启动 Docker 引擎。

解决方案如下:
- 方案 ①:使用 sudo 获取管理员权限,运行 docker 命令。
- 方案 ②:docker 守护进程启动的时候,会默认赋予名字为 docker 的用户组读写 Unix socket 的权限,因此只要创建 docker 用户组,并将当前用户加入到 docker 用户组中,那么当前用户就有权限访问 Unix socket 了,进而也就可以执行 docker 相关命令。
#添加docker用户组
sudo groupadd docker
#将登陆用户加入到docker用户组中 sudo gpasswd -a $USER docker
sudo gpasswd -a su docker
#更新用户组
newgrp docker
此处使用方案 ② 来解决问题,下图中可以看到docker version 指令已经可以看到能给显示出 Docker 引擎的信息了。

卸载 Docker
会安装就要会卸载,该部分就不做演示了,刚安装就卸载怪麻烦的,卸载 Docker 主要为以下几步:
关闭服务:
systemctl stop docker
使用
yum卸载 Docker 引擎:
sudo yum remove docker-ce docker-ce-cli containerd.io
删除镜像、容器、卷、自定义配置等文件:
sudo rm -rf /var/lib/docker
sudo rm -rf /var/lib/containerd
运行 HelloWorld 测试
docker run hello-world

Docker 下载加速
国内从 DockerHub 拉取镜像有时会遇到困难,此时可以配置镜像加速器。Docker 官方和国内很多云服务商都提供了国内加速器服务,我们此处使用阿里云的镜像加速器,若考虑使用其他的云服务商家,可以参考以下链接:Docker 镜像加速
访问该网址:容器镜像服务 (aliyun.com),登入阿里云,即可看到以下界面

没啥好啰嗦的,按照文档来就行了
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": ["{此地址是上方复制的地址}"]
} EOF
sudo systemctl daemon-reload
sudo systemctl restart docker
Docker 常用命令
启动类命令
启动 docker:
systemctl start docker
停止 Docker:
systemctl stop docker
重启 Docker:
systemctl restart docker
查看状态:
systemctl status docker
设置开机自启:
systemctl enable docker
案例实操
我们在停止 docker 发现给出了一个警告,警告的大概意思为虽然 docker 服务停掉了,但是他仍然可以被docker.socket 服务唤醒。也就是说只要docker.socket 只要没有停止,只要输入 docker 指令都会得到响应,不得不说很银杏

帮助类命令
查看 Docker 版本:
docker version
查看 Docker 概要信息:
docker info
查看 Docker 总体帮助文档:
docker --help
查看 docker 具体命令帮助文档:
docker 具体命令 --help
镜像命令
列出本地主机上的镜像
docker images

参数:
-a:列出所有镜像(含历史镜像)-q:只显示镜像 ID-f:过滤
结果参数:
REPOSITORY:表示镜像的仓库源TAG:镜像的标签(版本号)IMAGE ID:镜像 IDCREATED:镜像创建时间SIZE:镜像大小
同一仓库源可以有多个 TAG,代表这个仓库源的不同个版本,如 ubuntu 仓库源里,有 15.10、14.04 等多个不同的版本,我们使用 REPOSITORY:TAG 来定义不同的镜像。
在远程仓库中搜索镜像
docker search 镜像名称

参数:
-f:过滤--limit 数量:只展示前几项
结果参数:
NAME: 镜像仓库源的名称DESCRIPTION: 镜像的描述OFFICIAL: 是否 docker 官方发布stars: 类似 Github 里面的 star,表示点赞、喜欢的意思。AUTOMATED: 自动构建
下载镜像
docker pull 镜像名称[:tag]
不加 tag 时,默认下载最新的镜像(即 tag 为latest)。镜像下有多少 tag 需要去Docker Hub Container Image Library 查询
案例实操:

查看镜像/容器/数据卷所占的空间
docker system df

结果参数:
Images:总共有多少镜像Containers:运行了多少个实例- TODO 剩下两个待补充
删除镜像
删除单个镜像:
docker rmi 镜像名称/ID
删除多个镜像,可以使用空格分隔:
docker rmi 镜像1 镜像2 镜像3
删除全部镜像:
docker rmi -f $(docker images -qa)
参数:
- -f:强制删除所有镜像,包含运行中的。
案例实操:
由于功能并不复杂,仅仅演示删除单个的即可

查询镜像版本号
登入Docker Hub Container Image Library | App Containerization 网站,搜索希望查找的镜像,需要一点点神秘力量

点击查看详情

点击查看版本号

虚悬镜像
仓库名、标签都是<none>的镜像,俗称虚悬镜像(dangling image)。docker 再进行构建失败的时候可能出现。
容器命令(基础)
执行容器命令的前提是已经有了该容器的镜像。
新建+启动容器
docker run [选项] IMAGE [命令] [参数...]
常用的参数:
--name:为容器指定一个名称-d:后台运行容器并返回容器 ID,也即启动守护式容器-i:以交互模式(interactive)运行容器,通常与-t同时使用-t:为容器重新分配一个伪输入终端(tty),通常与-i同时使用。也即启动交互式容器(前台有伪终端,等待交互)-e:为容器添加环境变量-P:随机端口映射。将容器内暴露的所有端口映射到宿主机随机端口(不太常用)-p:指定端口映射(常用)
-p指定端口映射的几种不同形式:
-p hostPort:containerPort:端口映射,例如-p 8080:80-p ip:hostPort:containerPort:配置监听地址,例如-p 10.0.0.1:8080:80-p ip::containerPort:随机分配端口,例如-p 10.0.0.1::80-p hostPort1:containerPort1 -p hostPort2:containerPort2:指定多个端口映射,例如-p 8080:80 -p 8888:3306
启动交互式容器
所谓启动交互式容器,简单点理解来说就是启动容器后,给你弹出一个输入指令的界面。
# 参数说明
# -i 交互模式
# -t 分配一个伪输入终端tty
# ubuntu 镜像名称
# /bin/bash(或者bash) shell交互的接口, 此参数也可以不写,默认bash
docker run -it ubuntu /bin/bash

启动守护式容器
在大部分的场景下,我们希望 docker 的服务是在后台运行的,我们可以过 -d 指定容器的后台运行模式。下面我们分别以守护式进程的方式启动 ubuntu 和 reids。我们可以发现 ubuntu 的启动其实是有问题的,没有启动起来, 具体的原因参考下文中的 Q&A部分
# -d 代表容器以守护式方式启动
docker run -d ubuntu

# -d 代表容器以守护式方式启动
docker run -d redis:6.0.8

Q&A:
Q: 我们以交互式容器的方式启动 ubuntu,系统明明响应了服务已启动,通过
docker ps -a查看所有容器发现,启动的容器已经退出了,这是为什么呢???
A: Docker 容器后台运行,就必须有一个前台进程。容器运行的命令如果不是那些一直挂起的命令(比如运行 top,tail),就是会自动退出的。
上面那句话讲起来可能有点抽象,下图中是我们执行 docker run -it ubuntu /bin/bash 启动起来的 ubuntu 容器,我们通过 ps -ef 查看系统当前所有进程发现仅有两个进程再执行,一个是咱们的 bash 交互接口,一个是 ps 进程查询。也就是说,守护式启动 ubuntu 时,系统内根本就没有进程在执行,docker 就会认为系统无事可做,所以就退出了。

列出当前容器
docker ps [OPTIONS]

常用参数:
-a:列出当前所有正在运行的容器+历史上运行过的容器-l:显示最近创建的容器-n:显示最近 n 个创建的容器-q:静默模式,只显示容器编号
结果解析:
CONTAINER ID:运行容器的 IDIMAGE:使用的镜像COMMAND:执行的命令,bash和/bin/bash居多,后面可能还会补充CREATED:创建时间STATUS:当前状态PORTS:映射端口 提供-P和-p指定NAMES:当前容器的名称,可以通过--name {希望叫的名字}来指定名称,不然就会随机分配。
退出容器
退出容器其实是指的退出交互式容器的交互界面,通常有以下两种退出方式:① 交互界面输入 exit 指令, 退出后容器会停止; ② 输入 ctrl + P + Q ,退出后容器不会停止。
案例演示:
首先查看所有容器,交代出当前系统无容器在运行。交互式启动一个 ubuntu 容器,exit 退出,发现容器名为 brave_thompson 的容器使用的是 ubuntu 镜像,但是处于停止状态。再交互式启动一个 ubuntu 容器,此时输入快捷键 Ctrl + P +Q 退出,我们通过查看所有容器状态,发现容器名为 epic_ptolemy 的容器正在进行。

启动已经停止的容器
docker start 容器ID或容器名
重启容器
docker restart 容器ID或容器名
停止容器
docker stop 容器ID或容器名
强制停止容器
docker kill 容器ID或容器名
删除容器
删除已经停止的容器
docker rm 容器ID
强制删除未停止的容器
docker rm -f 容器ID
删除全部容器(叼毛高危操作,不学也罢)
docker rm -f $(docker ps -a -q)
# 或者
docker ps -a -q | xargs docker rm
容器命令(重要)
查看容器日志
docker rm -f 容器ID
查看容器内运行的进程
docker top 容器ID
查看容器内部细节
docker inspect 容器ID
进入正在进行的容器
docker exec -it 容器ID bashShell
# 或者
docker attach 容器ID
以上两台命令均能重新进入容器内部,与之交互,区别在于:
docker exec -it 容器ID bashShell命令进入的终端输入exit退出后,容器不会停止,原因是启动了新的进程 (推荐使用)docker attach 容器ID命令进入的容器内部,输入exit退出后,容器停止,并没有启动新的进程
案例演示
ubuntu 案例

redis 案例
疑问点:此处的 redis-cli 也输入 shell 接口吗?

总结:一般用-d 后台启动的程序,再用 exec 进入对应容器实例
从容器内拷贝文件到主机上
容器内文件拷贝到宿主机
docker cp 容器ID:容器内路径 目的主机路径
宿主机文件拷贝到容器中:
docker cp 主机路径 容器ID:容器内路径
案例演示:
第一步:创建并查看 hosttodocker.txt 文件;第二步执行对应的指令,演示中就是有写错指令导致拷贝失败的演示;第三步:拷贝成功后查看复制的文件

导入导出容器
export:导出容器的内容流作为一个 tar 归档文件(对应import命令);
import:从 tar 包中的内容创建一个新的文件系统再导入为镜像(对应export命令);
# 导出 操作对象是容器
docker export 容器ID > tar文件名
docker export aed61408780b > myubuntu.tar
# 导入 cat tar文件的作用是将结果集通过管道符以流的形式传给下一个指令
cat tar文件 | docker import - 自定义镜像用户/自定义镜像名:自定义镜像版本号
案例演示:
导出 ubuntu 容器
docker export aed61408780b > myubuntu.tar

删除当前 ubuntu 容器及镜像
具体删除指令不再展示了,废话已经够多了

导入之前的 tar 文件作为镜像文件
# 由于之前是在/tmp路径下执行的导出,所以tar文件默认导出到了/tmp路径下
cat /tmp/myubuntu.tar | docker import - zhanglei/myubuntu:1.0.1
导入完成后,我们可以发现,之前用于测试 docker cp 命令时的文件也被还原了。由此可以断定,导出的容器包含了当前容器状态下的所有内容。

常用命令总结
所有命令示意图

常用命令表格及中文解释
| 命令 | 全称 | 解释 |
|---|---|---|
| attach | Attach to a running container | 当前 shell 下 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 tarball | 从 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 镜像
参考资料:【云原生 Docker 系列第七篇】浅谈镜像的分层原理(几位?“两位”)-阿里云开发者社区 (aliyun.com)
是什么
镜像是一种轻量级、可执行的独立软件包,它包含运行某个软件所需的所有内容,我们把应用程序和配置依赖打包好行程一个可交付的运行环境(包括代码、运行时需要的库、环境变量和配置文件等),这个打包好的运行环境就是 image 镜像文件
docker 镜像的加载原理
联合文件系统
Docker 中的文件存储驱动叫做 storage driver。Docker 最早支持的 stotage driver 是 AUFS,它实际上由一层一层的文件系统组成,这种层级的文件系统叫 UnionFS。
联合文件系统(UnionFS):Union 文件系统,是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下(unite serveral directories into a single virtual filesystem)。
Union 文件系统是 Docker 镜像的基础。镜像可以通过分层来进行集成,基于基础镜像可以制作具体的应用镜像。
特性:一次同时加载多个文件系统,但从外面看起来,只能看到一个文件系统,联合加载会把各层文件系统叠加起来,这样最终的文件系统会包含所有底层的文件和目录。
后来出现的 docker 版本中,除了 AUFS,还支持 OverlayFS、Btrfs、Device Mapper、VFS、ZFS 等 storage driver。
docker 的镜像实际上由一层一层的文件系统组成,这种层级的文件系统 UnionFS。
bootfs(boot file system)
bootfs(boot file system) 主要包含 bootloader 和 kernel,bootloader 主要是引导加载 kernel,Linux 刚启动时会加载 bootfs 文件系统。
在 Docker 镜像的最底层是引导文件系统 bootfs。这一层与我们典型的 Linux/Unix 系统是一样的,包含 boot 加载器和内核。当 boot 加载完成之后整个内核就都在内存中了,此时内存的使用权已经由 bootfs 转交给内核,此时系统也会卸载 bootfs。
rootfs(root file system)
在 bootfs 之上,包含的就是典型 Linux 系统中的 /dev、/proc、/bin、/etc等标准目录和文件。rootfs 就是各种不同的操作系统发行版,比如 Ubuntu、CentOS 等。
docker 镜像底层层次

对于一个精简的 OS,rootfs 可以很小,只需要包括最基本的命令、工具和程序库就可以了,因为底层直接使用 Host 的 Kernel,自己只需要提供 rootfs 就可以。所以,对于不同的 Linux 发行版,bootfs 基本是一致的,rootfs 会有差别,不同的发行版可以共用 bootfs。
有差别的 rootfs:

优势
镜像分层最大的一个好处就是共享资源,方便复制迁移,就是为了复用。比如说有多个镜像都从相同的 base 镜像构建而来,那么 Docker Host 只需在磁盘上保存一份 base 镜像;同时内存中也只需加载一份 base 镜像,就可以为所有容器服务了。而且镜像的每一层都可以被共享。
容器层与镜像层
当容器启动时,一个新的可写层将被加载到镜像的顶部,这一层通常被称为容器层,容器层之下的都叫镜像层。所有对容器的改动,无论添加、删除、还是修改文件都只会发生在容器层中。只有容器层是可写的,容器层下面的所有镜像层都是只读的

怎么用
docker commit -m="提交的描述信息" -a="作者" 容器 ID 要创建的目标镜像名:
实操案例
目标:创建一个带有 vim 的 ubuntu 镜像
拉取最新的 ubuntu 镜像

运行一个 ubuntu 实例,并进入终端界面

使用 vim 指令,发现指令不存在

安装 vim 指令
执行过程没啥意思,就不截图了,命令附上
# 更新包管理工具
apt-get update
# 安装Vim
apt-get -y install vim
提交镜像
docker commit -m="带有vim的ubuntu" -a="zhanglei" ebd71331125f withvimubuntu:1.0.1

查看已经生成的镜像

运行新构建的镜像,并查看对应的命令

总结
Docker 中的镜像分层,支持通过扩展现有镜像,创建新的镜像。类似 Java 继承于一个 Base 基础类,自己再按需扩展。新镜像是从 base 镜像一层一层叠加生成的。每安装一个软件,就在现有镜像的基础上增加一层

镜像发布阿里云(个人版切勿用于生产环境)
本章节比较偏实操,镜像的生成方法参考上一章节
Dokcer镜像->怎么用章节,下面的案例展示以推送上一步生成的 withvimubuntu 镜像目标
登录阿里云,找到容器镜像服务
点击进入阿里云登录页 (aliyun.com),找到容器镜像服务

能白嫖就不要缴费


设置访问凭证
首次进入,会强制你设置,不设置无法使用其镜像服务,要想更改访问凭证,参考下图即可

设置命名空间,创建仓库
注意创建顺序,阿里的防呆设计做的挺好的,看一下基本就了解了。创建仓库时,代码源选择本地仓库。

查看操作指南
进入仓库的管理页面,查看对应的操作指南。

实操上手
密码其实就是之前设置的访问凭证,其他的就按照教程来就行
推送镜像
docker login --username=啊啊阿磊啊 registry.cn-hangzhou.aliyuncs.com
# docker tag [镜像号] registry.cn-hangzhou.aliyuncs.com/zhanglei_0407/zhanglei_0407:[镜像版本号]
docker tag 07f05e531ec6 registry.cn-hangzhou.aliyuncs.com/zhanglei_0407/zhanglei_0407:1.0.1
# docker push registry.cn-hangzhou.aliyuncs.com/zhanglei_0407/zhanglei_0407:[镜像版本号]
docker push registry.cn-hangzhou.aliyuncs.com/zhanglei_0407/zhanglei_0407:1.0.1

删除本地的镜像

从阿里云拉取镜像
# docker pull registry.cn-hangzhou.aliyuncs.com/zhanglei_0407/zhanglei_0407:[镜像版本及版本号]
docker pull registry.cn-hangzhou.aliyuncs.com/zhanglei_0407/zhanglei_0407:1.0.1

运行镜像,并查看 vim 版本

Docker Registry
是什么
Docker Registry 是官方提供的工具,可以用于构建私有镜像仓库
解决了什么问题
- 官方 Docker Hub 地址:https://hub.docker.com/,中国大陆访问太慢了且准备被阿里云取代的趋势,不太主流。
- Dockerhub、阿里云这样的公共镜像仓库可能不太方便,涉及机密的公司不可能提供镜像给公网,所以需要创建一个本地私人仓库供给团队使用,基于公司内部项目构建镜像。
怎么用
目标: 搭建本地私有仓库,将本地镜像推送到私有库
下载 Docker Registry 镜像
docker registry

运行对应的容器
docker run -d -p 5000:5000 -v /app/myregistry/:/tmp/registry --privileged=true registry

创建带有安装 ifconfig 命令的 ubuntu 镜像
# 下载ubuntu镜像
docker pull ubuntu
# 启动容器
docker run -it -name=withnettools ubuntu bash
# 下载nettools工具
apt-get update
apt-get install net-tools
# 生成镜像
docker commit -m="带有ipconfig的镜像" -a="zhanglei" 473e414d8db9 myubuntu:1.0.1
生成镜像时有点奇怪,一开始预备的镜像名为 withvnettoolsubuntu ,一直显示格式错误......,不知道为什么
查看私服库镜像
# curl -XGET 私服IP端口/v2/_catalog
curl -XGET http://127.0.0.1:5000/v2/_catalog

修改镜像的 Tag
# docker tag 镜像:Tag Host:Port/Repository:Tag
docker tag myubuntu:1.0.1 127.0.0.1:5000/myubuntu:1.0.1

修改配置,使私库支持 http 协议
docker 默认不允许 http 方式推送镜像,通过配置选项来取消这个限制。 修改完后如果不生效,建议重启 docker
# 修改配置文件 (需要拥有写入权限)
vim /etc/docker/daemon.json

推送镜像到私库
# docker push IP:端口/目标镜像名称:版本号
docker push 127.0.0.1:5000/myubuntu:1.0.1

查看此时的私服库镜像
curl -XGET http://127.0.0.1:5000/v2/_catalog

删除本地镜像,从私库拉入
删除指令不做重复
# docker pull 私库IP:端口/镜像名称:版本号
docker pull 127.0.0.1:5000/myubuntu:1.0.1

运行镜像实例,并查看 ip
docker run -it 127.0.0.1:5000/myubuntu:1.0.1 bash

Docker 容器数据卷
是什么
可以想象一下主机的一块磁盘,当做 U 盘插入 docker 的运行实例中。
能干嘛
为了能保存数据在 docker 中我们使用卷。将运行的环境打包镜像,run 后形成容器实例运行 ,但是我们对数据的要求希望是持久化的。Docker 容器产生的数据,如果不备份,那么当容器实例删除后,容器内的数据自然也就没有了。
特点:
- 数据卷可在容器之间共享或重用数据
- 卷中的更改可以直接实时生效(因为是同一块 U 盘)
- 数据卷中的更改不会包含在镜像的更新中
- 数据卷的生命周期一直持续到没有容器使用它为止
怎么用
宿主 vs 容器之间映射添加容器卷
宿主机下的映射路径如果不存在,则会自动创建
# docker run -it --privileged=true -v /宿主机绝对路径目录:/容器内目录 镜像名
docker run -it --privileged=true -v /app/myubuntu/tmp:/tmp ubuntu
查看运行情况
# docker inspect 容器ID
docker inspect 952c46cfdf8c

测试虚拟机与主机数据共享


读写规则映射添加说明
读写(默认)
docker run -it --privileged=true -v /宿主机绝对路径目录:/容器内目录:rw 镜像名
# 或者 什么也不写
docker run -it --privileged=true -v /宿主机绝对路径目录:/容器内目录 镜像名
只读(Read Only)
docker run -it --privileged=true -v /宿主机绝对路径目录:/容器内目录:ro 镜像名
卷的继承和共享
# docker run -it --privileged=true --volumes-from 父容器ID --name=容器名称 镜像:镜像版本
docker run -it --privileged=true --volumes-from jovial_mclaren --name=myubuntu2 ubuntu


Docker 常规安装
- 搜索镜像
- 拉取镜像
- 查看镜像
- 启动镜像:启动镜像时,进行服务端口映射,容器卷实例持久化,环境变量的注入
- 停止容器
- 移除容器
安装 Tomcat
docker hub 上面查找 tomcat 镜像
# 查询前5个满够了
docker search tomcat --limit 5

从 docker hub 上拉取 tomcat 镜像到本地
docker pull tomcat

docker images 查看是否有拉取到的 tomcat
# docker images 指定镜像名
docker images tomcat

使用 tomcat 镜像创建容器实例(也叫运行镜像)
docker run -it -p 8080:8080 tomcat

访问首页
问题:阿偶,启动失败了,报 404

原因分析:
- 检查防火墙是否关闭,端口是否放开
- 新版的
tomcat默认的 webapps 文件夹是空的,原来的 webapps 文件夹内容放入了 webapps.dist 下了
解决办法:
cp -r /usr/local/tomcat/webapps.dist/* /usr/local/tomcat/webapps/

免修改版本
docker run -d -p 8080:8080 --name tomcat8 billygoo/tomcat8-jdk8

安装 Mysql
具体的版本需要到 dockerHub 上进行查询,此处按照 8.0.36 做演示
docker hub 上面查找 mysql 镜像
docker search mysql --limit 5

从 docker hub 上(阿里云加速器)拉取 mysql 镜像到本地
docker pull mysql:5.7
# 或者
docker pull mysql:8.0.36

简单版安装
docker run -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123456 -d mysql:8.0.36
# 或者
docker run -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123456 -d mysql:5.7
进入容器,创建数据库

使用外部工具链接数据库,并插入中文数据
问题分析
8 版本数据库链接数据库时报
Public Key Retrieval is not allowed异常

解决办法: 可参考该文章:完美解决:MySQL8 报错:Public Key Retrieval is not allowed-CSDN 博客
问题的根本原因还是在于,mysql 8.0 默认使用 caching_sha2_password 身份验证机制,客户端默认不支持新的加密方式

8.0 版本插入中文数据在 docker 中查询乱码
问题: 参照下图,插入一条中文数据,并可在连接工具中正常查看,但是在 docker 容器实例下的控制台无法查看


现象原因: 该问题其实是由于 docker 容器中的系统不支持中文导致的,我们可以发现,docker 容器内部终端中,是无法输入中文的,因此会出现此现象
5.7 版本插入中文会报异常
问题: 参照下图,插入中文会报异常

原因: mysql5.7 版本,数据库的默认字符集为拉丁文,导致的

解决办法:修改配置文件,重启 mysql 服务,可以参考实际应用安装
实际应用安装
实际应用安装,以 5.7 为例,-v 的数据,日志,配置所对应的容器卷映射按照实际配置
# docker run -d -p 3306:3306 \
# --privileged=true \
# -v [主机日志存放路径]:/var/log/mysql \
# -v [主机数据存放路径]:/var/lib/mysql \
# -v [主机配置存放路径]:/etc/mysql/conf.d \
# -e MYSQL_ROOT_PASSWORD=root \
# --name [服务名] \
# mysql[:版本号]
docker run -d -p 3306:3306 \
--privileged=true \
-v /app/mysql/log:/var/log/mysql \
-v /app/mysql/data:/var/lib/mysql \
-v /app/mysql/conf:/etc/mysql/conf.d \
-e MYSQL_ROOT_PASSWORD=root \
--name mysql \
mysql:5.7
解决中文插入报错问题:
在/app/mysql/conf下新建 my.cnf,通过容器卷同步给 mysql 实例,解决中文乱码问题:
[client]
default_character_set=utf8
[mysqld]
collation_server = utf8_general_ci
character_set_server = utf8
安装 Redis
不写废话,直接上指令
简单玩具版
docker run -p 6379:6379 -d redis:6.0.8
实际应用版
① 宿主机创建目录/app/redis
② 修改配置文件,在/app/redis下创建文件redis.conf,并修改
# 开启密码验证(可选)
requirepass 123
# 允许redis外地连接,需要注释掉绑定的IP
# bind 127.0.0.1
# 关闭保护模式(可选)
protected-mode no
# 注释掉daemonize yes,或者配置成 daemonize no。因为该配置和 docker run中的 -d 参数冲突,会导致容器一直启动失败
daemonize no
# 开启redis数据持久化, (可选)
appendonly yes
③ 启动容器
docker run -d -p 6379:6379 --name redis --privileged=true \
-v /app/redis/redis.conf:/etc/redis/redis.conf \
-v /app/redis/data:/data \
redis:6.0.8 \
redis-server /etc/redis/redis.conf
安装 Nginx
待更新~~~~~~

浙公网安备 33010602011771号