docker用到的主要技术:
1.namespace 命名空间,内核级别,环境隔离
PID NameSpace: Linux 2.6.24, PID隔离
Network NameSpace: Linux 2.6.29,网络设备、网络栈、端口等网络资源隔离
User NameSpace: Linux 3.8,用户和用户组资源隔离
IPC NameSpace: Linux 2.6.19,信号量、消息队列和共享内存的隔离
UTS NameSpace: Linux 2.6.19,主机名和域名的隔离;
Mount NameSpace: Linux 2.4.19,挂载点(文件系统)隔离;
2.CGroup :内核级别,限制控制与一个进程组群的资源。(cpu,memory,IO)
功能:
Resource limitation:资源限制;
Prioritization:优先级控制;
Accounting:审计和统计,主要为计费;
Control:挂起进程,恢复进程;
3.AUFS(把不同的物理位置的目录合并到同一个目录下)Device Mapper
Docker的五大优势:
①持续集成 ②版本控制 ③可移植性 ④隔离性 ⑤安全性
Docker的组件
核心组件:
docker client: docker的客户端工具,是用户使用docker的主要接口, docker client与dockerdaemon通信并将结果返回给用户;
docker deamon:运行于宿主机上, Docker守护进程,用户可通过docker client与其交互;
image:镜像文件是只读的;用来创建container,一个镜像可以运行多个container;镜像文件可以通过Dockerfile文件创建,也可以从docker hub/registry下载;
repository:
公共仓库: Docker hub/registry
私有仓库: docker registry
docker container: docker的运行实例,容器是一个隔离环境;另外两个重要组件:docker link:docker volume:
docker关于容器、镜像、仓库的定义
Docker 镜像是Docker容器运行时的只读模板,每一个镜像由一系列的层 (layers) 组成。Docker 使用 UnionFS 来将这些层联合到单独的镜像中。UnionFS 允许独立文件系统中的文件和文件夹(称之为分支)被透明覆盖,形成一个单独连贯的文件系统。正因为有了这些层的存在,Docker 是如此的轻量。当你改变了一个 Docker 镜像,比如升级到某个程序到新的版本,一个新的层会被创建。因此,不用替换整个原先的镜像或者重新建立(在使用虚拟机的时候你可能会这么做),只是一个新 的层被添加或升级了。现在你不用重新发布整个镜像,只需要升级,层使得分发 Docker 镜像变得简单和快速。(包含了启动docker容器所需要的文件系统层级及其内容;基于UnionFS采用分层结构实现)
Docker 仓库
Docker 仓库用来保存镜像,可以理解为代码控制中的代码仓库。同样的,Docker 仓库也有公有和私有的概念。公有的 Docker 仓库名字是 Docker Hub。Docker Hub 提供了庞大的镜像集合供使用。这些镜像可以是自己创建,或者在别人的镜像基础上创建。Docker 仓库是 Docker 的分发部分。
Docker 容器
Docker 容器和文件夹很类似,一个Docker容器包含了所有的某个应用运行所需要的环境。每一个 Docker 容器都是从 Docker 镜像创建的。Docker 容器可以运行、开始、停止、移动和删除。每一个 Docker 容器都是独立和安全的应用平台,Docker 容器是 Docker 的运行部分。
Docker镜像是Docker容器运行时的只读模板,每一个镜像由一系列的层(layers)组成;
Docker使用UnionFS(联合文件系统)来将这些层联合到一二镜像中,UnionFS文件系统允许独立文件系统中的文件和文件夹(称之为分支)被透明覆盖,形成一个单独连贯的文件系统。
正因为有了这些层(layers)的存在,Docker才会如此的轻量。当你改变了一个Docker镜像,比如升级到某个程序到新的版本,一个新的层会被创建。因此,不用替换整个原先的镜像或者重新建立(在使用虚拟机的时候你可能会这么做),只是一个新的层被添加或升级了。所以你不用重新发布整个镜像,只需要升级。层使得分发Docker镜像变得简单和快速。
每个镜像都是从一个基础的镜像开始的,比如ubuntu,一个基础的Ubuntu镜像,或者是Centos,一个基础的Centos镜像。你可以使用你自己的镜像作为新镜像的基础,例如你有一个基础的安装了Nginx的镜像,你可以使用该镜像来建立你的Web应用程序镜像。(Docker通常从Docker Hub获取基础镜像)
Docker镜像从这些基础的镜像创建,通过一种简单、具有描述性的步骤,我们称之为 指令(instructions)。
每一个指令会在镜像中创建一个新的层,指令可以包含这些动作:
1)运行一个命令。
2)增加文件或者文件夹。
3)创建一个环境变量。
4)当运行容器的时候哪些程序会运行。
这些指令存储在Dockerfile文件中。当你需要建立镜像的时候,Docker可以从Dockerfile中读取这些指令并且运行,然后返回一个最终的镜像。
Docker容器是如何工作的?
当运行docker容器时发生了什么?一个Docker容器包含了一个操作系统、用户添加的文件和元数据(meta-data)。每个容器都是从镜像建立的,镜像告诉Docker容器内包含了什么,当容器启动时运行什么程序,还有许多配置数据。
Docker镜像是只读的,当Docker运行一个从镜像建立的容器,它会在镜像顶部添加一个可读写的层,应用程序可以在这里运行。
使用docker命令时,Docker客户端都告诉Docker守护进程运行一个容器。
$ sudo docker run -i -t ubuntu /bin/bash
可以来分析这个命令,Docker客户端使用docker命令来运行,run参数表明客户端要运行一个新的容器。
Docker客户端要运行一个容器需要告诉Docker守护进程的最小参数信息是:
1)这个容器从哪个镜像创建,这里是ubuntu,基础的Ubuntu镜像。
2)在容器中要运行的命令,这里是/bin/bash,在容器中运行Bash shell。
那么运行这个命令之后在底层发生了什么呢?
按照顺序,Docker做了这些事情:
1)拉取ubuntu镜像: Docker检查ubuntu镜像是否存在,如果在本地没有该镜像,Docker会从Docker Hub下 载。如果镜像已经存在,Docker会使用它来创建新的容器。
2)创建新的容器: 当Docker有了这个镜像之后,Docker会用它来创建一个新的容器。
3)分配文件系统并且挂载一个可读写的层: 容器会在这个文件系统中创建,并且一个可读写的层被添加到 镜像中。
4)分配网络/桥接接口: 创建一个允许容器与本地主机通信的网络接口。
5)设置一个IP地址: 从池中寻找一个可用的IP地址并且服加到容器上。
6)运行你指定的程序: 运行指定的程序。
7)捕获并且提供应用输出: 连接并且记录标准输出、输入和错误让你可以看到你的程序是如何运行的。
由此你就可以拥有一个运行着的Docker容器了!从这里开始你可以管理你的容器,与应用交互,应用完成之后, 可以停止或者删除你的容器。
Docker的底层技术
Docker使用Go语言编写,并且使用了一系列Linux内核提供的性能来实现我们已经看到的这些功能。
命名空间(Namespaces)(这个上面也详细说明了)
Docker充分利用了一项称为namespaces的技术来提供隔离的工作空间,我们称之为 container(容器)。当你运行一个容器的时候,Docker为该容器创建了一个命名空间集合。
这样提供了一个隔离层,每一个应用在它们自己的命名空间中运行而且不会访问到命名空间之外。
一些Docker使用到的命名空间有:
pid命名空间: 使用在进程隔离(PID: Process ID)。
net命名空间: 使用在管理网络接口(NET: Networking)。
ipc命名空间: 使用在管理进程间通信资源 (IPC: InterProcess Communication)。
mnt命名空间: 使用在管理挂载点 (MNT: Mount)。
uts命名空间: 使用在隔离内核和版本标识 (UTS: Unix Timesharing System)。
群组控制
Docker还使用到了cgroups技术来管理群组。使应用隔离运行的关键是让它们只使用你想要的资源。这样可以确保在机器上运行的容器都是良民(good multi-tenant citizens)。群组控制允许Docker分享或者限制容器使用硬件资源。例如,限制指定的容器的内容使用。
联合文件系统
联合文件系统(UnionFS)是用来操作创建层的,使它们轻巧快速。Docker使用UnionFS提供容器的构造块。Docker可以使用很多种类的UnionFS包括AUFS, btrfs, vfs, and DeviceMapper。
容器格式
Docker连接这些组建到一个包装中,称为一个 container format(容器格式)。默认的容器格式是libcontainer。Docker同样支持传统的Linux容器使用LXC。在未来,Docker也许会支持其它的容器格式,例如与BSD Jails 或 Solaris Zone集成。
Docker的网络配置
Dokcer 通过使用Linux桥接提供容器之间的通信,docker0桥接接口的目的就是方便Docker管理。当Docker daemon启动时需要做以下操作:
1)如果docker0不存在则创建
2)搜索一个与当前路由不冲突的ip段
3)在确定的范围中选择 ip
4)绑定 ip 到 docker0
Docker四种网络模式
docker run创建Docker容器时,可以用--net选项指定容器的网络模式,Docker有以下4种网络模式:
1)host模式,使用 --net=host指定。
2)container模式,使用 --net=container:NAMEorID 指定。
3)none模式,使用 --net=none指定。
4)bridge模式,使用 --net=bridge指定,默认设置。
host 模式
如果启动容器的时候使用 host 模式,那么这个容器将不会获得一个独立的 Network Namespace,而是和宿主机共用一个 Network Namespace。容器将不会虚拟出自己的网卡,配置自己的 IP 等,而是使用宿主机的 IP 和端口。
比如:我们在 10.10.101.105/24 的机器上用 host 模式启动一个含有 web 应用的 Docker 容器,监听 tcp 80 端口。当我们在容器中执行任何类似 ifconfig 命令查看网络环境时,看到的都是宿主机上的信息。而外界访问容器中的应用,则直接使用 10.10.101.105:80 即可,不用任何 NAT 转换,就如直接跑在宿主机中一样。但是,容器的其他方面,如文件系统、进程列表等还是和宿主机隔离的。
container 模式
这个模式指定新创建的容器和已经存在的一个容器共享一个 Network Namespace,而不是和宿主机共享。新创建的容器不会创建自己的网卡,配置自己的 IP,而是和一个指定的容器共享 IP、端口范围等。同样,两个容器除了网络方面,其他的如文件系统、进程列表等还是隔离的。两个容器的进程可以通过 lo 网卡设备通信。
none模式
这个模式和前两个不同。在这种模式下,Docker 容器拥有自己的 Network Namespace,但是,并不为 Docker容器进行任何网络配置。也就是说,这个 Docker 容器没有网卡、IP、路由等信息。需要我们自己为 Docker 容器添加网卡、配置 IP 等。
bridge模式
bridge 模式是 Docker 默认的网络设置,此模式会为每一个容器分配 Network Namespace、设置 IP 等,并将一个主机上的 Docker 容器连接到一个虚拟网桥上。当 Docker server 启动时,会在主机上创建一个名为 docker0 的虚拟网桥,此主机上启动的 Docker 容器会连接到这个虚拟网桥上。虚拟网桥的工作方式和物理交换机类似,这样主机上的所有容器就通过交换机连在了一个二层网络中。接下来就要为容器分配 IP 了,Docker 会从 RFC1918 所定义的私有 IP 网段中,选择一个和宿主机不同的IP地址和子网分配给 docker0,连接到 docker0 的容器就从这个子网中选择一个未占用的 IP 使用。如一般 Docker 会使用 172.17.0.0/16 这个网段,并将 172.17.42.1/16 分配给 docker0 网桥(在主机上使用 ifconfig 命令是可以看到 docker0 的,可以认为它是网桥的管理接口,在宿主机上作为一块虚拟网卡使用)