1. Docker 前世今生
1.1 Docker 是什么
Docker 最初是 dotCloud 公司创始人 Solomon Hykes 在法国期间发起的一个公司内部项目,它是基于 dotCloud 公司多年云服务技术的一次革新,并于 2013 年 3 月以 Apache 2.0 授权协议开源,主要项目代码在 GitHub 上进行维护。Docker 项目后来还加入了 Linux 基金会,并成立推动 开放容器联盟(OCI)。
Docker 自开源后受到广泛的关注和讨论,至今其 GitHub 项目 已经超过 5 万 7 千个星标和一万多个 fork。甚至由于 Docker 项目的火爆,在 2013 年底,dotCloud 公司决定改名为 Docker。Docker 最初是在 Ubuntu 12.04 上开发实现的;Red Hat 则从 RHEL 6.5 开始对 Docker 进行支持;Google 也在其 PaaS 产品中广泛应用 Docker。
Docker 使用 Google 公司推出的 Go 语言 进行开发实现,基于 Linux 内核的 cgroup,namespace,以及 OverlayFS 类的 Union FS 等技术,对进程进行封装隔离,属于 操作系统层面的虚拟化技术。由于隔离的进程独立于宿主和其它的隔离的进程,因此也称其为容器。最初实现是基于 LXC,从 0.7 版本以后开始去除 LXC,转而使用自行开发的 libcontainer,从 1.11 开始,则进一步演进为使用 runc 和 containerd。
runc
是一个 Linux 命令行工具,用于根据 OCI容器运行时规范 创建和运行容器。
containerd
是一个守护程序,它管理容器生命周期,提供了在一个节点上执行容器和管理镜像的最小功能集。
Docker 在容器的基础上,进行了进一步的封装,从文件系统、网络互联到进程隔离等等,极大的简化了容器的创建和维护。使得 Docker 技术比虚拟机技术更为轻便、快捷。
下面的图片比较了 Docker 和传统虚拟化方式的不同之处。传统虚拟机技术是虚拟出一套硬件后,在其上运行一个完整操作系统,在该系统上再运行所需应用进程;而容器内的应用进程直接运行于宿主的内核,容器内没有自己的内核,而且也没有进行硬件虚拟。因此容器要比传统虚拟机更为轻便。
1.2 为什么要用 Docker
- 更高效的利用系统资源
- 更快速的启动时间
- 一致的运行环境
- 持续交付和部署
- 更轻松的迁移
- 更轻松的维护和扩展
特性 | 容器 | 虚拟机 |
---|---|---|
启动 | 秒级 | 分钟级 |
硬盘使用 | 一般为MB | 一般为GB |
性能 | 接近原生 | 弱于 |
系统支持量 | 单机支持上千个容器 | 一般几十个 |
1.3 Docker 的整个生命周期
Docker daemon
Docker 守护进程(dockerd)侦听 Docker API 请求并管理 Docker 对象,如镜像、容器、网络和数据卷。守护进程还可以与其他守护进程通信来管理 Docker 服务。
Docker Client
Docker 客户端(docker)是许多 Docker 用户与 Docker 交互的主要方式。当你使用诸如 docker run 之类的命令时,客户机将这些命令发送给 dockerd,dockerd 执行这些命令。docker 命令使用 docker API 。Docker 客户端可以与多个守护进程通信。
Docker Registry
Docker 注册服务器存储 Docker 镜像。Docker Hub 是一个任何人都可以使用的公共注册服务器,默认情况下 Docker 被配置为在 Docker Hub 上寻找镜像。你甚至可以运行自己的私有注册服务器。当你使用 docker pull 或 docker run 命令时,所需的镜像将从配置的注册服务器中提取。当你使用 docker push命令时,你的镜像将被推到你配置的注册服务器中。
Docker Objects
当你使用 Docker 时,你就是在创建和使用镜像像、容器、网络、卷、插件和其他对象。
- IMAGES:镜像是一个只读模板,带有创建 Docker 容器的指令。镜像通常基于另一个镜像,还需要进行一些额外的定制。例如,你可以构建一个基于 ubuntu 镜像的镜像,但是安装 Apache Web 服务器和你的应用程序,以及运行应用程序所需的配置细节。你可以创建自己的镜像,也可以只使用其他人创建并发布在注册服务器中的镜像。要构建自己的镜像,需要创建一个 Dockerfile,其中的简单语法用于定义创建和运行镜像所需的步骤。Dockerfile 中的每条指令都会在镜像中创建一个图层。当你改变 Dockerfile 并重建镜像时,只有那些已经改变的图层才会重建。这是使镜像与其他虚拟化技术相比如此轻量级、小巧和快速的部分原因。
- CONTAINERS:容器是镜像的可运行实例。你可以使用 Docker API 或 CLI 创建、启动、停止、移动或删除容器。你可以将一个容器连接到一个或多个网络,将存储附加到它,甚至根据它的当前状态创建一个新镜像。默认情况下,容器与其他容器及其主机相对隔离良好。你可以控制容器网络、存储或其他底层子系统与其他容器或主机的隔离程度。容器是由它的镜像以及你在创建或启动它时提供给它的任何配置选项定义的。当容器被删除时,对其状态的任何未存储在持久存储中的更改都会消失。
- SERVICES:docker service 最重要的管理指令,可以实现部署运行服务、服务扩容缩容、删除服务、滚动更新等功能。
- 底层技术支持:Docker 是用 Go 编程语言编写的,并利用 Linux 内核的几个特性来提供其功能,如:Namespaces(做隔离)、CGroups(做资源限制)、UnionFS(镜像和容器的分层)、libcontainer(容器格式)。
2. Docker 安装
2.1 配置阿里云 Docker Yum 源
# Set up repository
yum install -y yum-utils device-mapper-persistent-data lvm2
# Use Aliyun Docker
yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
2.2 安装 Docker Engine-Community
yum install -y docker-ce docker-ce-cli containerd.io
2.3 启动 Docker 服务
# Start docker service
systemctl enable docker
systemctl start docker
2.4 Docker 镜像加速
mkdir -p /etc/docker
tee /etc/docker/daemon.json <<EOF
{
"registry-mirrors": [
"https://hub-mirror.c.163.com",
"https://mirror.baidubce.com",
"https://1tzcxi92.mirror.aliyuncs.com"
]
}
EOF
systemctl daemon-reload
systemctl restart docker
3. Docker 一图胜千言
3.1 关键指令
Registry
- docker pull:获取镜像
- docker push:推送镜像
- docker search:查询镜像
Images
- docker images:本地镜像列表
- docker rmi:删除镜像
- docker history:镜像构建历史
- docker tag:关联镜像重命名
- docker run:基于镜像启动并运行一个容器
Container
- docker ps:查看正在运行的容器
- docker rm:删除容器
- docker exec:执行容器指令
- docker inspect:查看容器信息
更多
- docker --help
3.2 Docker Commands Diagram
3.3 Hello-World
docker run hello-world
3.4 实践案例
场景描述:Nginx 环境的容器化使用。
# 1. 拉取镜像 -- Docker 官方提供了一个公共的镜像仓库:Docker Hub,我们就可以从这上面获取镜像
docker pull nginx:stable
# 2. 创建容器
# -d: 以守护进程态运行容器
# --name: 容器名称
# -v: 将宿主机目录与容器内目录进行挂载,亦称数据卷
# -p: 将宿主机端口与容器内暴露端口映射
docker run -d --name nginx_web -v /work/www:/usr/local/share/html -p 8080:80 nginx:stable
# 3. 管理容器
docker container --help
# 4. 测试使用
# 将待部署页面丢置宿主机 /work/www 目录下
# 浏览器访问宿主机 IP:PORT 即可
echo "nginx web is ok." >> /work/www/index.html
curl localhost:8080
4. 镜像环境迁移
4.1 From Container
基于容器的修改,会产生大量的冗余操作,不推荐该方式的镜像环境迁移。
# 1. 基于 container 提交为 image
docker commit --help
# 2. 基于 container 导出为 tar 文件,再导入为 image
docker export --help
docker import --help
4.2 From Image
基于镜像的备份还原,使得镜像文件较大,不便于迁移,也不太推荐该方式的镜像环境迁移。
# 基于 image 保存为 tar 文件,再加载为 image
docker save --help
docker load --help
4.3 Dockerfile
镜像的定制实际上就是定制每一层所添加的配置、文件。如果我们可以把每一层修改、安装、构建、操作的命令都写入一个脚本,用这个脚本来构建、定制镜像,那么之前提及的无法重复的问题、镜像构建透明性的问题、体积的问题就都会解决。这个脚本就是 Dockerfile。
4.3.1 镜像分层
4.3.2 Dockerfile 主要指令
Dockerfile 是一个文本文件,其内包含了一条条的 指令(Instruction),每一条指令构建一层,因此每一条指令的内容,就是描述该层应当如何构建。
更多指令:https://docs.docker.com/engine/reference/builder/
4.3.3 实践案例
场景描述:为了满足研发项目的特有环境,便于一致性部署,需定制镜像环境(定义具有 gd, mysql 等扩展功能的 PHP 镜像)。
# 1. 项目目录准备
[root@docker-1 ~]# mkdir /work/lnmp
[root@docker-1 ~]# cd /work/lnmp
[root@docker-1 lnmp]# ll
# 2. Dockerfile
[root@docker-1 lnmp]# vi Dockerfile
FROM php:7.2-fpm
LABEL maintainer="pansf<pansfy@163.com>"
# 替换镜像的 apt-get 源
RUN sed -i 's#http://deb.debian.org#http://mirrors.163.com#g' /etc/apt/sources.list && apt-get update
# 从第三方镜像拷贝文件
COPY --from=mlocati/php-extension-installer /usr/bin/install-php-extensions /usr/bin/
# 安装 php 所需模块
RUN install-php-extensions gd mysqli pdo_mysql
# 3. 构建镜像
[root@docker-1 lnmp]# docker build -f Dockerfile -t php7.2-mysql .
# 4. 运行容器
[root@docker-1 lnmp]# docker run -d --name php7.2 -v /work/lnmp/www:/var/www/html -p 9000:9000 php7.2-mysql
5. Docker Advance
5.1 容器资源配额
docker 通过 cgroup 来控制容器使用的资源配额,包括CPU、内存、磁盘三大方面,基本覆盖了常见的资源配额和使用量控制。
5.1.1 cgroup 简介
cgroup 是 Control Groups 的缩写,是 Linux 内核提供的一种可以限制、记录、隔离进程组所使用的物理资源(如 cpu、memory、磁盘 IO 等等) 的机制,被 LXC、docker 等很多项目用于实现进程资源控制。cgroup 将任意进程进行分组化管理的 Linux 内核功能。cgroup 本身是提供将进程进行分组化管理的功能和接口的基础结构,I/O 或内存的分配控制等具体的资源管理功能是通过这个功能来实现的。
这些具体的资源管理功能称为cgroup子系统,有以下几大子系统实现:
blkio:设置限制每个块设备的输入输出控制。例如:磁盘,光盘以及usb等等。
cpu:使用调度程序为cgroup任务提供cpu的访问。
cpuacct:产生cgroup任务的cpu资源报告。
cpuset:如果是多核心的cpu,这个子系统会为cgroup任务分配单独的cpu和内存。
devices:允许或拒绝cgroup任务对设备的访问。
freezer:暂停和恢复cgroup任务。
memory:设置每个cgroup的内存限制以及产生内存资源报告。
net_cls:标记每个网络包以供cgroup方便使用。
ns:命名空间子系统。
perf_event:增加了对每group的监测跟踪的能力,即可以监测属于某个特定的group的所有线程以及运行在特定CPU上的线程。
目前docker只是用了其中一部分子系统,实现对资源配额和使用的控制。
5.1.2 stress 工具
可以使用stress工具来测试 CPU 和内存。使用下面的 Dockerfile 来创建一个基于 Ubuntu 的 stress 工具镜像。
FROM debian:buster
RUN sed -i 's#http://deb.debian.org#http://mirrors.163.com#g' /etc/apt/sources.list && apt-get update && apt-get -y install stress
构建 stress 镜像
docker build pansf/stress .
具体用法参见使用 stress 进行压力测试
5.1.3 docker run 设置
docker run --help
5.1.4 docker update 设置
# docker update 内存或CPU配额设置默认关闭,需开启
vi /etc/default/grub
# 如果 GRUB_CMDLINE_LINUX= 内有内容,切记不可删除,只需在后面追加 cgroup_enable=memory swapaccount=1 并用空格和前面的内容分隔开,配置如下:
GRUB_CMDLINE_LINUX="net.ifnames=0 cgroup_enable=memory swapaccount=1"
# 更新 grub 适用于 CentOS 7
grub2-mkconfig -o /boot/grub2/grub.cfg
# 重启系统
reboot
# 限制容器内存为 1G,swap内存为 1G,,更多配额可查看 docker update --help
docker update -m 1G --memory-swap 1G container_name