Linux Namespaces是一种轻量级的虚拟化形式。操作系统在内存,CPU上,已经使用了虚拟化的技术,让每个进程都认为是自己独占了内存和CPU。但像存储,磁盘,信号等,一些资源,操作系统并没有将其隔离开。
namespace则是将这类资源也隔离开来。使得进程只能看到自己的资源视图。这个功能加上Cgroup,就可以实现一个轻量级的虚拟机。这对于提高主机资源利用率很有用。如果说KVM这类虚拟机是为了隔离,而容器技术更多是为了共享。
默认情况下,每个Linux系统最初仅有一个命名空间。所有系统资源(诸如文件系统、用户ID、网络接口等)属于这一个命名空间
但是你能创建额外的命名空间,以及在它们之间组织资源。对于一个进程, 可以在其中一个命名空间中运行它。进程将只能看到同一个命名空间下的资源。当然,会存在多种类型的多个命名空间,所以一个进程不单单只属于某一个命名空间,而属于每个类型的一个命名空间。
存在以下类型的命名空间:
尽管容器技术已经出现很久,却是随着Docker容器平台的出现而变得广为人知。Docker是第一个使容器能在不同机器之间移植的系统。它不仅简化了打包应用的流程,也简化了打包应用的库和依赖,甚至整个操作系统的文件系统能被打包成一个简单的可移植的包,这个包 可以被用来在任何其他运行Docker的机器上使用。当你用Docker运行一个被打包的应用程序时,它能看见你捆绑的文件系统的内容,不管运行在开发机器还是生产机器上,它都能看见相同的文件,即使生产机器运行的是完全不同的操作系统。应用程序不会关心它所在服务器上的任何东西,所以生产服务器上是否安装了和你开发机完全相同的一组库是不需要关心的。与在虚拟机中安装操作系统得到一个虚拟机镜像,再将应用程序打包到镜像里。
通过分发整个虚拟机镜像到主机,使应用程序能够运行起来类似,Docker也能够达到相同的效果,但不是使用虚拟机来实现应用隔离,而是使用之前提到的Linux容器技术来达到和虚拟机相同级别的隔离。容器也不使用庞大的单个虚拟机镜像,它使用较小的容器镜像。基于Docker容器的镜像和虚拟机镜像的一个很大的不同是容器镜像是由多层构成,它能在多个镜像之间共享和征用。如果某个已经被下载 的容器镜像已经包含了后面下载镜像的某些层,那么后面下载的镜像就 无须再下载这些层
Docker是一个打包、分发和运行应用程序的平台。正如我们所说, 它允许将你的应用程序和应用程序所依赖的整个环境打包在一起。这既可以是一些应用程序需要的库,也可以是一个被安装的操作系统所有可用的文件。Docker使得传输这个包到一个中央仓库成为可能,然后这个 包就能被分发到任何运行Docker的机器上,在那儿被执行(大部分情况是这样的,但并不尽然,后面将做出解释)。
三个主要概念组成了这种情形:
下图显示了这三个概念以及它们之间的关系:
我们都知道,操作系统分为内核和用户空间。对于 Linux 而言,内核启动后,会挂载 root 文件系统为其提供用户空间支持。而 Docker 镜像(Image),就相当于是一个 root 文件系统。比如官方镜像 ubuntu:16.04 就包含了完整的一套 Ubuntu 16.04 最小系统的 root 文件系统。
Docker 镜像是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等)。镜像不包含任何动态数据,其内容在构建之后也不会被改变。
因为镜像包含操作系统完整的 root 文件系统,其体积往往是庞大的,因此在 Docker 设计时,就充分利用 Union FS 的技术,将其设计为分层存储的架构。所以严格来说,镜像并非是像一个 ISO 那样的打包文件,镜像只是一个虚拟的概念,其实际体现并非由一个文件组成,而是由一组文件系统组成,或者说,由多层文件系统联合组成。
镜像构建时,会一层层构建,前一层是后一层的基础。每一层构建完就不会再发生改变,后一层上的任何改变只发生在自己这一层。比如,删除前一层文件的操作,实际不是真的删除前一层的文件,而是仅在当前层标记为该文件已删除。在最终容器运行的时候,虽然不会看到这个文件,但是实际上该文件会一直跟随镜像。因此,在构建镜像的时候,需要额外小心,每一层尽量只包含该层需要添加的东西,任何额外的东西应该在该层构建结束前清理掉。
分层存储的特征还使得镜像的复用、定制变的更为容易。甚至可以用之前构建好的镜像作为基础层,然后进一步添加新的层,以定制自己所需的内容,构建新的镜像。
总结:Docker是第一个使容器成为主流的容器平台。Docker本身并不提供进程隔离,实际上容器隔离是在Linux内核之上使用诸如Linux命名空间和cgroups之类的内核特性完成的,Docker仅简化了这些特性的使用
镜像(Image)和容器(Container)的关系,就像是面向对象程序设计中的 类 和 实例 一样,镜像是静态的定义,容器是镜像运行时的实体。容器可以被创建、启动、停止、删除、暂停等。
容器的实质是进程,但与直接在宿主执行的进程不同,容器进程运行于属于自己的独立的 命名空间。因此容器可以拥有自己的 root 文件系统、自己的网络配置、自己的进程空间,甚至自己的用户 ID 空间。容器内的进程是运行在一个隔离的环境里,使用起来,就好像是在一个独立于宿主的系统下操作一样。这种特性使得容器封装的应用比直接在宿主运行更加安全。也因为这种隔离的特性,很多人初学 Docker 时常常会混淆容器和虚拟机。
前面讲过镜像使用的是分层存储,容器也是如此。每一个容器运行时,是以镜像为基础层,在其上创建一个当前容器的存储层,我们可以称这个为容器运行时读写而准备的存储层为容器存储层。
容器存储层的生存周期和容器一样,容器消亡时,容器存储层也随之消亡。因此,任何保存于容器存储层的信息都会随容器删除而丢失。
按照 Docker 最佳实践的要求,容器不应该向其存储层内写入任何数据,容器存储层要保持无状态化。所有的文件写入操作,都应该使用 数据卷(Volume)、或者绑定宿主目录,在这些位置的读写会跳过容器存储层,直接对宿主(或网络存储)发生读写,其性能和稳定性更高。
数据卷的生存周期独立于容器,容器消亡,数据卷不会消亡。因此,使用数据卷后,容器删除或者重新运行之后,数据却不会丢失。
镜像构建完成后,可以很容易的在当前宿主机上运行,但是,如果需要在其它服务器上使用这个镜像,我们就需要一个集中的存储、分发镜像的服务,Docker Registry 就是这样的服务。
一个 Docker Registry 中可以包含多个仓库(Repository);每个仓库可以包含多个标签(Tag),标签可以理解为版本号;每个标签对应一个镜像。
完整的一个镜像应该是:[Docker Registry 地址[:端口号]/]仓库名[:标签]
通常,一个仓库会包含同一个软件不同版本的镜像,而标签就常用于对应该软件的各个版本。我们可以通过 <仓库名>:<标签> 的格式来指定具体是这个软件哪个版本的镜像。如果不给出标签,将以 latest 作为默认标签。而<仓库名>实际上也是分为二段,即 <用户名>/<软件名>。对于 Docker Hub,如果不给出用户名,则默认为 library,也就是官方镜像。
以 Ubuntu 镜像 为例,ubuntu 是仓库的名字,其内包含有不同的版本标签,如,14.04, 16.04。我们可以通过 ubuntu:14.04,或者 ubuntu:16.04 来具体指定所需哪个版本的镜像。如果忽略了标签,比如 ubuntu,那将视为 ubuntu:latest。
仓库名经常以 两段式路径 形式出现,比如 jwilder/nginx-proxy,前者往往意味着 Docker Registry 多用户环境下的用户名,后者则往往是对应的软件名。但这并非绝对,取决于所使用的具体 Docker Registry 的软件或服务。
Docker Registry 公开服务是开放给用户使用、允许用户管理镜像的 Registry 服务。一般这类公开服务允许用户免费上传、下载公开的镜像,并可能提供收费服务供用户管理私有镜像。
最常使用的 Registry 公开服务是官方的 Docker Hub,这也是默认的 Registry,并拥有大量的高质量的官方镜像。除此以外,还有 CoreOS 的 Quay.io,CoreOS 相关的镜像存储在这里;Google 的 Google Container Registry,Kubernetes 的镜像使用的就是这个服务。
由于某些原因,在国内访问这些服务可能会比较慢。国内的一些云服务商提供了针对 Docker Hub 的镜像服务(Registry Mirror),这些镜像服务被称为加速器。常见的有 阿里云加速器、DaoCloud 加速器 等。使用加速器会直接从国内的地址下载 Docker Hub 的镜像,比直接从 Docker Hub 下载速度会提高很多。
国内也有一些云服务商提供类似于 Docker Hub 的公开服务。比如 时速云镜像仓库、网易云镜像服务、DaoCloud 镜像市场、阿里云镜像库 等。
最左边是docker的客户端,类似我们操作mysql的工具navcat,只不过我们这里的是没有图形化界面的命令终端。docker客户端是用户与docker服务交互的窗口。
中间的是docker后台运行的服务,一个称为docker daemon的守护进程。可以理解为我们mysql的服务,我们的操作命令都是在这部分进行处理!docker deamon监听着客户端的请求,并且管理着docker的镜像、容器、网络、磁盘(图中只列出了镜像与容器)等对象。同样,docker的客户端与服务可以运行在同一机器上,也可以用某台机器上的客户端远程连接另一台机器上的docker服务,这跟我们的mysql一样的呢。
Docker自身的4种网络工作方式,和一些自定义网络模式;
安装Docker时,它会自动创建三个网络,bridge(创建容器默认连接到此网络)、 none 、host。
相当于Vmware中的Nat模式,容器使用独立network Namespace,并连接到docker0虚拟网卡(默认模式)。通过docker0网桥以及Iptables nat表配置与宿主机通信;bridge模式是Docker默认的网络设置,此模式会为每一个容器分配Network Namespace、设置IP等,并将一个主机上的Docker容器连接到一个虚拟网桥上。
1、当Docker Daemon进程启动时,会在主机上创建docker0网桥,并在网桥使用的网段为容器分配IP,此主机上启动的Docker容器会连接到这个虚拟网桥上。虚拟网桥的工作方式和物理交换机类似,这样主机上的所有容器就通过交换机连在了一个二层网络中。
2、从docker0子网中分配一个IP给容器使用,并设置docker0的IP地址为容器的默认网关。在主机上创建一对虚拟网卡veth pair设备(成对出现,通过管道高效共享消息),Docker将veth pair设备的一端放在新创建的容器中,并命名为eth0(容器的网卡),另一端放在主机中,以vethxxx这样类似的名字命名,并将这个网络设备加入到docker0网桥中。可以通过brctl show命令查看。
3、bridge模式是docker的默认网络模式,不写–net参数,就是bridge模式。使用docker run -p时,docker实际是在iptables做了DNAT规则,实现端口转发功能。可以使用iptables -t nat -vnL查看。
如果启动容器的时候使用host模式,那么这个容器将不会获得一个独立的Network Namespace,而是和宿主机共用一个Network Namespace。容器将不会虚拟出自己的网卡,配置自己的IP等,而是使用宿主机的IP和端口。但是,容器的其他方面,如文件系统、进程列表等还是和宿主机隔离的。
使用host模式的容器可以直接使用宿主机的IP地址与外界通信,容器内部的服务端口也可以使用宿主机的端口,不需要进行NAT(即网络地址转换),host最大的优势就是网络性能比较好,但是docker host上已经使用的端口就不能再用了,网络的隔离性不好。
这个模式指定新创建的容器和已经存在的一个容器共享一个 Network Namespace,而不是和宿主机共享。新创建的容器不会创建自己的网卡,配置自己的 IP,而是和一个指定的容器共享 IP、端口范围等。同样,两个容器除了网络方面,其他的如文件系统、进程列表等还是隔离的。两个容器的进程可以通过 IO 网卡设备通信。
使用none模式,Docker容器拥有自己的Network Namespace,但是,并不为Docker容器进行任何网络配置。也就是说,这个Docker容器没有网卡、IP、路由等信息。需要我们自己为Docker容器添加网卡、配置IP等。
这种网络模式下容器只有lo回环网络,没有其他网卡。none模式可以在容器创建时通过–network=none来指定。这种类型的网络没有办法联网,封闭的网络能很好的保证容器的安全性。
None模式示意图:
Compose 项目是 Docker 官方的开源项目,负责实现对 Docker 容器集群的快速编排。 为了完成一个完整项目势必用到N多个容器(一个容器只运行一个进程)配合完成项目中业务开发,一旦引入N多个容器,容器之间就会形成某种依赖,也就意味某个容器或某些容器的运行需要其他容器优先启动之后才能正常运行。容器的编排显得至关重要,容器的运行一定要有先后顺序。
快速编排:站在项目角度将一组相关联容器整合在一起,对这组容器按照指定顺序进行启动。
Compose 在GitHub上的地址:https://github.com/docker/compose
Compose 允许用户通过一个单独的 docker-compose.yml 模板文件(YAML格式)来定义一组相关联的应用容器为一个项目(project)。
两个重要的概念:
Docker Swarm 是一款用来管理多主机上的Docker容器的工具,可以负责帮你启动容器,监控容器状态,如果容器的状态不正常它会帮你重新帮你启动一个新的容器,来提供服务,同时也提供服务之间的负载均衡,而这些东西Docker-Compose 是做不到的
Kubernetes
Kubernetes它本身的角色定位是和Docker Swarm 是一样的,也就是说他们负责的工作在容器领域来说是相同的部分,都是一个跨主机的容器管理平台,当然也有自己一些不一样的特点,k8s是谷歌公司根据自身的多年的运维经验研发的一款容器管理平台。而Docker Swarm则是由Docker 公司研发的。
既然这两个东西是一样的,那就面临选择的问题,应该学习哪一个技术呢?实际上这两年Kubernetes已经成为了很多大公司的默认使用的容器管理技术,而Docker Swarm已经在这场与Kubernetes竞争中已经逐渐失势,如今容器管理领域已经开始已经逐渐被Kubernetes一统天下了。所以建议大家学习的时候,应该多考虑一下这门技术在行业里面是不是有很多人在使用。
需要注意的是,虽然Docker Swarm在与Kubernetes的竞争中败下阵来,但是这个跟Docker这个容器引擎没有太大关系,它还是整个容器领域技术的基石,Kubernetes离开他什么也不是。
Kubernetes,又称为 k8s(首字母为 k、首字母与尾字母之间有 8 个字符、尾字母为 s,所以简称 k8s)或者简称为 “kube” ,是一种可自动实施 Linux 容器操作的开源平台。它可以帮助用户省去应用容器化过程的许多手动部署和扩展操作。也就是说,您可以将运行 Linux 容器的多组主机聚集在一起,由 Kubernetes 帮助您轻松高效地管理这些集群。而且,这些集群可跨公共云、私有云或混合云部署主机。因此,对于要求快速扩展的云原生应用而言(例如借助 Apache Kafka 进行的实时数据流处理),Kubernetes 是理想的托管平台。Kubernetes的目标与Docker swarm的目标非常相似.
Kubernetes 最初由 Google 的工程师开发和设计。Google 是最早研发 Linux 容器技术的企业之一(组建了cgroups),曾公开分享介绍 Google 如何将一切都运行于容器之中(这是 Google 云服务背后的技术)。Google 每周会启用超过 20 亿个容器——全都由内部平台 Borg 支撑。Borg 是 Kubernetes 的前身,多年来开发 Borg 的经验教训成了影响 Kubernetes 中许多技术的主要因素。
趣事:Kubernetes 徽标的七个轮辐代表着项目最初的名称"九之七项目"(Project Seven of Nine)。
2015 年,Google 将 Kubernetes 项目捐赠给新成立的云原生计算基金会。
当然,k8s非常复杂,会在后面专门介绍。
Rancher是业界唯一完全开源的企业级容器管理平台,为企业用户提供在生产环境中落地使用容器所需的一切功能与组件。
总体来说,rancher 和 k8s 都是用来作为容器的调度与编排系统。但是rancher不仅能够管理应用容器,更重的一点是能够管理k8s集群,rancher2.x 底层基于k8s调度引擎,通过rancher 的封装,用户可以在不熟悉k8s概念的情况下轻松的通过rancher 来部署容器到k8s集群当中。为了实现上述功能,rancher自身提供了一套完整的用于管理k8s的组件,包括rancher api server,cluster controller,cluster agent,node agent 等等。组件相互协调使得rancher 能够掌控每个k8s集群,从而将多集群的管理和使用整合在统一的rancher 平台中。rancher 增强了一下k8s的功能,并提供了面向用户友好的使用方式。
Rancher 提供了在产品环境中对 Docker 容器进行全方位管理的平台。它提供的基础架构服务包括多主机网络、全局和局部的负载均衡、卷快照等。集成了原生 Docker 管理能力,包括:Docker Machine 和 Docker Swarm。Rancher 还提供了丰富用户体验的管理功能。
Rancher也很复杂,我们可以挑一个专门对时间来讲讲。