容器是什么
Docker是一个开源的容器引擎。了解Docker之前,首先需要明白容器是什么。
RedHat的定义是,容器是一组进程;在容器中,这组进程受到资源限制,与容器外的进程相互隔离,一个容器拥有完整的运行环境和全部的运行依赖。Docker可以容器,容器中含有一个或者多个进程,它们不受操作系统其他部分影响。
按照这个定义,虚拟机也属于容器,后续会介绍docker和虚拟机的区别。
下面通过两个例子可以感知下容器的必要性:
- case1:
有一个需求需要部署两个脚本,一个脚本使用php5、另一个脚本使用php7。问题:php5和php7语法不兼容。
- 传统思路:A、B改写成为同一版本php语言,或者部署在不同机器上;
- 容器技术:A、B放置在不同的容器上,每个容器都有独立的php运行环境,二者不相互干扰。
- case2:
同机部署两个进程,A、B分属于不同的微服务。在大型互联网公司中,一般一个机器只能部署一个服务;如果同机部署两个服务,极端情况下,一个服务的运行异常可能导致另一个服务的异常。19年,曾经遇到过同机部署的服务,A服务不断申请内存,导致B服务申请不到内存,同时A的下游是B服务,最终整个服务雪崩。
- 容器技术:容器可以对A、B进行资源限制,A、B之间都有资源数目限制。
Docker是什么
Docker是一个基于Linux x64开源的容器引擎(当前Docker已经支持其余平台和架构),用来创建和管理容器。Docker的核心概念有:
Docker vs 虚拟机
本质上虚拟机也是容器。和docker相比,虚拟机做的更重一些。
当前基于虚拟机的弹性计算产品是云服务厂商的主流产品,比如亚马逊的EC2、阿里云的ECS、腾讯云的CVM。虽然docker的占有的资源小,速度快,但是docker不支持热迁移;而超卖则是弹性计算的商业模式,支持超卖就要做到对负载较高的机器热迁移,因此当前计算产品的主流依然是虚拟机。
Docker Container运行原理
Docker的容器运行充分利用现有的技术。
- 存储虚拟化: AUFS、overplay和overplay2
- 运行时资源隔离(CPU+内存): cgroup
- 网络虚拟化: CNM
Cgroup技术
Cgroup全称是Linux Control Group,是由Google在2006年提出的linux提案,后来被整合进入Linux的内核中。当时虚拟机流行的时候,Google宣称,Google的服务器里面没有一台虚拟机。出于篇幅的考虑,这里仅仅介绍CGroup的作用。
Cgroup被用来限制、控制、分离一个进程组:
- 资源限制,限制整个进程组的资源,包括cpu和内存
- 进程组的优先级控制: 包括IO优先级,调度优先级
- 结算:记录使用的资源数目
- 进程组隔离:属于两个namespace的进程组彼此不知道对方存储
- 进程组控制
在docker中,cgroup完成了:
- cpu资源的限制:通过cgroup,一个容器的总cpu资源消耗不会超过启动时的配置
- 内存管理: cgroup限制容器内存分配数目;
- 信号隔离:容器内的进程无法发送信号给容器外的进程
- 文件路径隔离:不同的容器的进程可以使用相同路径表示不同的资源。
存储虚拟化
Docker运行时使用联合文件系统(UFS, Union File System)进行文件操作。联合文件系统可以将多个物理位置不同的文件目录“联合”起来,挂载到某一个目录下,形成一个抽象的文件系统。UnionFS中的文件或者文件夹都是“cd-rom”,程序读正常,写不会作用到原始文件上。
早期的Docker使用AUFS作为运行和存储镜像时的文件系统。
AUFS 采用分层设计,每层都是一个独立的目录,在运行的时候,写入会保存在最上层;VFS层查找文件的inode时,driver会从上往下一层一层查找,比如查找/a/b,首先会查找第一层如果找不到会继续向下查找,直到在某一层找到或者全部找不到。如果上层和下层存在相同路径的文件,那么查找时只会返回上层的文件inode。当修改文件时,VFS会拷贝文件到最上层。
Docker在创建中,每一行的操作都是一个layer。下图是在一个Golang的镜像中增加可执行文件并保存后,image的变化。
允许中,AUFS存在性能问题,Docker在创建的时候每一个操作都会生成一个新的layer,如果层数过大,文件操作会花费大量的时间。Google提出来Overlay技术,在创建容器的时候,会把AUFS的各个只读层通过硬连接的方式合并成一个image layer,这样查找的时候仅仅需要查找一层即可。
目录挂载
docker容器销毁后,可写层的数据会直接丢失。为了防止这种情况,docker允许在创建容器的时候将本地目录挂载在docker容器中,当docker容器运行时,数据会直接持久化在硬盘上。
网络虚拟化
Linux网络虚拟化
Linux的网络连接,采用网卡+交换机的设计。Linux的每一个网卡都接入了一个交换机,可以通过ifconfig查看网卡和交换机的对应关系。
Linux内核可以虚拟产生网卡和交换机。Linux的network namespace是实现网络虚拟化的重要功能,他能创建多个隔离的网络空间,每个网络空间都有独立的网络栈信息,不论是容器还是虚拟机,运行的时候仿佛在独立的网络中,位于不同namespace的网络协议栈可以通过虚拟网卡接入同一个虚拟交换机,彼此之间实现通信。
虚拟交换机
Docker在安装的时候会产生三个网络(虚拟交换机),可以使用docker network ls
查看docker产生的网络。可以使用docker network相关命令创建新的网络。
Docker一共有四种网络模式
上图分别表示none,bridge、container和host模式。
Docker 常用操作
docker 客户端
Docker 按照后将以守护进程(Daemon)的形式运行在机器上,可以通过docker客户端的形式与docker的守护进程进行交互。在命令行中调用的docker就是客户端,可以和本机的docker后台交互。
docker image操作
镜像(image)是创建容器时需要的文件、启动命令、元数据信息的集合。镜像可以由镜像id,或者镜像名+版本唯一定位。直接使用镜像名docker会检查最新版本。
docker images
列出本地所有的容器列表。第一列为名称,第二列为版本,第三列为镜像id。
docker pull repository[:tag]
或者 docker pull image id
从默认的wavehouse上下载镜像。默认会下载最新的。
docker search keyword
搜索封装好的docker镜像。
docker rmi [--force] imageID
or docker rmi [--force] repository[:tag]
从本地删除镜像
docker save repository[:tag]/imageID
[-o 本地文件名]`
将本地镜像导出到文件或标准输出
docker load [-i 本地文件]
从本地文件或标准输入中加载镜像到本地仓库
docker inspect
查看docker image的创建信息。
docker 运行操作
创建容器
docker run [option1] repository[:tag]/imageID [option2]
上述命令可以通过镜像启动一个容器。默认情况下,容器在前台执行,容器的三个标准流不会重定位到终端,需要通过设置参数修改。
启动容器包含 1:创建运行环境 2:启动进程
option1 设置容器运行时的参数,包括cpu、内存、目录映射、网络端口映射、网络设置、输入流重定向、是否守护进程运行等待。下面是常用的选项:
--cpus=1.5
使用1.5核的cpu
--memory=2g
最多使用2G的内存
-v /data/v:/opt
可以映射多个目录,每次映射都要加-v 把本机的/data/映射为容器的/opt
-p 1234:5678
将容器的5678端口绑定为主机的1234端口
--net=host
使用主机模式占用端口
-it
-it 表示重定向输入输出到当前终端
-d
以守护进程的方式运行
option2 表示在容器中执行的命令
进程操作
docker stop containID
向docker中所有的进程发送SIGTERM信号;
docker kill containID
向docker中所有的进程发送SIGKILL信号;
docker cp
向容器中拷贝文件,或者从容器中考出文件
docker restart
重新启动
docker rm containerID
销毁容器,删除所有非挂载数据
创建镜像
有两种方式可以创建一个镜像。第一种通过DockerFile,执行命令创建镜像,第二种方式可以将正在运行的容器中的数据保存为一个新的镜像。
如果需要自动化构建镜像只能通过dockerfile。
下面通过一个创建Http介绍如何
Dockerfile
默认可以通过Dockerfile创建image。每一行执行一条命令,创建一个docker layer。
From:
从一个基础镜像上创建镜像。
FROM ubuntu:16.04
RUN:
执行一个命令
RUN apt-get update && apt-get install -y --no-install-recommends curl apt-utils openssl ca-certificates
ENV:
设置环境变量,注意不能通过Run Export设置。Run Export的修改不会落盘。
ENV PATH="/opt/gtk/bin:${PATH}"
add/copy
拷贝文件到容器中,注意这条命令仅仅可以复制dockerfile所在的文件夹中。Docker build命令会将文件夹下的所有内容拷贝一份到docker 守护进程,因此避免目录中出现不必要文件。
workdir
设置容器启动时的工作目录
volume
volume /data
表示这个目录需要持久化,默认会保存在默认volume中。