容器生态圈之旅第二章《容器》紫色飞猪

依旧记得初次听到容器这个词是在2018年的8月份。

依旧记得那月发生了两件改变我IT轨迹的事:1.加入小马哥的运维群(骏马金龙:www.junmajinlong.com)2.买了马哥的docker和kubernetes视频。

依旧记得那时候的自己完成了某孩的期末架构就自认为自己已经足够强了。

那时候我大二刚结束。

白驹过隙,又是一年,2019年的8月份到了,我大三刚结束。

2019年的7月29号,我开始动笔写下我对于容器这块的总结性笔记。

我对容器总结性的一句话概括:容器等于镜像加进程,镜像是应用程序及其依赖环境的封装。

统称来说,容器是一种工具,指的是可以装下其它物品的工具,以方便人类归纳放置物品、存储和异地运输,具体来说比如人类使用的衣柜、行李箱、背包等可以成为容器,但今天我们所说的容器是一种IT技术。

容器技术是虚拟化、云计算、大数据之后的一门新兴的并且是炙手可热的新技术,容器技术提高了硬件资源利用率、方便了企业的业务快速横向扩容、实现了业务宕机自愈功能,因此未来数年会是一个容器愈发流行的时代,这是一个对于IT行业来说非常有影响和价值的技术,而对于IT行业的从业者来说,熟练掌握容器技术无疑是一个很有前景的的行业工作机会。

知名的容器技术有:Docker(Docker的同名开源容器化引擎适用于大多数后续产品以及许多开源工具),CSDE(Docker公司拥有扩展Docker的所有权。CSDE支持在Windows服务器上运行docker实例),Rkt(rkt的发音为“rocket”,它是由CoreOS开发的。rkt是Docker容器的主要竞争对手),SolarisContainers(Solaris容器架构比Docker更早出现。想必那些已经在Solaris上标准化的IT企业会继续研究它),Microsoft容器(作为Linux的竞争对手,MicrosoftContainers可以在非常特定的情况下支持Windows容器)。

这里我将讲的容器技术是docker,毕竟别的容器技术我都没接触过,招聘简历上也没见过。

Docker是一个在2013年开源的应用程序并且是一个基于go语言编写是一个开源的paas服务(PlatformasaService,平台即服务的缩写),go语言是由google开发,docker公司最早叫dotCloud,后由于Docker开源后大受欢迎就将公司改名为DockerInc,总部位于美国加州的旧金山,Docker是基于linux内核实现,Docker最早采用LXC技术(LinuXContainer的简写,LXC是Linux原生支持的容器技术,可以提供轻量级的虚拟化,可以说docker就是基于LXC发展起来的,提供LXC的高级封装,发展标准的配置方法),源代码托管在Github上,而虚拟化技术KVM(Kernel-basedVirtualMachine)基于模块实现,Docker后改为自己研发并开源的runc技术运行容器。

Docker利用现有的Linux容器技术,以不同方式将其封装及扩展(通过提供可移植的镜像及友好的接口),创建(负责创建与运行容器的docker引擎)及发布方案(用来发布容器的云服务dockerhub)。

Docker的基本组成:dockerclient客户端、dockerdaemon守护进程、dockerimage镜像、dockercontainer容器、dockerregistry仓库、docker主机。

Docker相比虚拟机的交付速度更快,资源消耗更低,Docker采用客户端/服务端架构,使用远程API来管理和创建Docker容器,其可以轻松的创建一个轻量级的、可移植的、自给自足的容器,docker的三大理念是build(构建)、ship(运输)、run(运行),Docker遵从apache2.0协议,并通过(namespace及cgroup等)来提供容器的资源隔离与安全保障等(安全和隔离可以使你可以同时在机器上运行多个容器),所以Docke容器在运行时不需要类似虚拟机(空运行的虚拟机占用物理机6-8%性能)的额外资源开销,因此可以大幅提高资源利用率,总而言之Docker是一种用了新颖方式实现的轻量级虚拟机.类似于VM但是在原理和应用上和VM的差别还是很大的,并且docker的专业叫法是应用容器(ApplicationContainer)。

IDC/IAAS/PAAS/SAAS对比

IAAS:基础架构即服务

PAAS:平台即服务

SAAS:软件即服务

构建-->运输-->运行

总架构图

主要模块:

DockerClient(与Daemon建立通信,发起容器的管理请求)

DockerDaemon(接收Client请求,处理请求)

DockerRegisrty(镜像管理)

Graph(存储镜像)

Drvier(镜像管理驱动)

libcontainer(系统内核特性,提供完整、明确的接口给Daemon)

DockerContainer

各模块功能及实现

DockerClient

Docker架构中用户与DockerDaemon建立通信的客户端。

用户可以使用可执行文件docker作为DockerClient,发起Docker容器的管理请求。

三种方式建立通信:

tcp://host:port

unix://path_to_socket

fd://socketfd

DockerClient发送容器管理请求后,请求由DockerDaemon接收并处理,当DockerClient接收到返回的请求响应并做简单处理后,DockerClient一次完整的生命周期就结束了。

DockerDaemon

常驻在后台的系统进程。

主要作用:

接收并处理DockerClient发送的请求

管理所有的Docker容器

DockerDaemon运行时,会在后台启动一个Server,Server负责接收DockerClient发送的请求;接收请求后,Server通过路由与分发调度,找到相应的Handler来处理请求。

三部分组成:

A.DockerServer

专门服务于DockerClient,接收并调度分发Client请求。

Server通过包gorilla/mux创建mux。Router路由器,提供请求的路由功能,每一个路由项由HTTP请求方法(PUT、POST、GET、DELETE)、URL和Handler组成。

每一个Client请求,Server均会创建一个全新的goroutine来服务,在goroutine中,Server首先读取请求内容,然后做请求解析工作,接着匹配相应的路由项,随后调用相应的Handler来处理,最后Handler处理完请求后给Client回复响应。

B.Engine

核心模块,运行引擎。

存储着大量容器信息,管理着Docker大部分Job的执行。

handlers对象:

存储众多特定Job各自的处理方法handler。

例如:

{"create":daemon.ContainerCreate,}

当执行名为"create"的Job时,执行的是daemon.ContainerCreate这个handler。

C.Job

Engine内部最基本的执行单元,Daemon完成的每一项工作都体现为一个Job。

DockerRegistry

存储容器镜像(DockerImage)的仓库。

DockerImage是容器创建时用来初始化容器rootfs的文件系统内容。

搜索镜像

下载镜像

上传镜像

方式:

公有Registry

私有Registry

Graph

容器镜像的保管者。

Driver

驱动模块,通过Driver驱动,Docker实现对Docker容器运行环境的定制,定制的维度包括网络、存储、执行方式。

作用:

将与Docker容器有关的管理从Daemon的所有逻辑中区分开。

实现:

A.graphdriver

用于完成容器镜像管理。

初始化前的四种文件系统或类文件系统的驱动在Daemon中注册:

aufs、btrfs、devmapper用于容器镜像的管理

vfs用于容器volume的管理

B.networkdriver

完成Docker容器网络环境的配置。

C.execdriver

执行驱动,负责创建容器运行时的命名空间,负责容器资源使用的统计与限制,负责容器内部进程的真正运行等。

Daemon启动过程中加载ExecDriverflag参数在配置文件中默认设为native。

libcontainer

服务交付的最终体现。

用户对Docker容器的配置:

通过指定容器镜像,使得Docker容器可以自定义rootfs等文件系统;

通过指定物理资源的配额,使得Docker容器使用受限的资源;

通过配置容器网络及其安全策略,使得Docker容器拥有独立且安全的网络环境;

通过指定容器的运行命令,使得Docker容器执行指定的任务;

Docker使用C/S架构,Client通过接口与Server进程通信实现容器的构建,运行和发布。client和server可以运行在同一台集群,也可以通过跨主机实现远程通信。

Docker客户端会与Docker守护进程进行通信。Docker守护进程会处理复杂繁重的任务,例如建立、运行、发布你的Docker容器。

Docker客户端和守护进程可以运行在同一个系统上,当然也可以使用Docker客户端去连接一个远程的Docker守护进程。

Docker客户端和守护进程之间通过socket或者RESTfulAPI进行通信。

Docker客户端(Client):Docker客户端,实际上是docker的二进制程序,是主要的用户与Docker交互方式。它接收用户指令并且与背后的Docker守护进程通信,如此来回往复。

Docker服务端(Server):Docker守护进程,运行docker容器。Docker守护进程运行在一台主机上。用户并不直接和守护进程进行交互,而是通过Docker客户端间接和其通信。

Docker镜像(Images):Docker镜像是Docker容器运行时的只读模板,每一个镜像由一系列的层(layers)组成。Docker使用UnionFS来将这些层联合到单独的镜像中。UnionFS允许独立文件系统中的文件和文件夹(称之为分支)被透明覆盖,形成一个单独连贯的文件系统。正因为有了这些层的存在,Docker是如此的轻量。当你改变了一个Docker镜像,比如升级到某个程序到新的版本,一个新的层会被创建。因此,不用替换整个原先的镜像或者重新建立(在使用虚拟机的时候你可能会这么做),只是一个新的层被添加或升级了。现在你不用重新发布整个镜像,只需要升级层使得分发Docker镜像变得简单和快速。

Docker容器(Container):容器是从镜像生成对外提供服务的一个或一组服务。Docker容器和文件夹很类似,一个Docker容器包含了所有的某个应用运行所需要的环境。每一个Docker容器都是从Docker镜像创建的。Docker容器可以运行、开始、停止、移动和删除。每一个Docker容器都是独立和安全的应用平台,Docker容器是Docker的运行部分。

Docker主机(Host):一个物理机或虚拟机,用于运行Docker服务进程和容器。

资源利用率更高:一台物理机可以运行数百个容器,但是一般只能运行数十个虚拟机。

开销更小:容器与主机的操作系统共享资源,提高了效率,性能损耗低

启动速度更快:可以在做到秒级完成启动。

容器具有可移植性

容器是轻量的,可同时运行数十个容器,模拟分布式系统

区别:

A.容器只能运行与主机一样的内核B.容器程序库可以共用C.容器中执行的进程与主机的进程等价(没有虚拟机管理程序的损耗)D.隔离能力,虚拟机更高(将容器运行在虚拟机中)

E.使用虚拟机是为了更好的实现服务运行环境隔离,但是一个虚拟机只运行一个服务,很明显资源利用率比较低

优势

高效虚拟化:不需要额外的hypervisor支持,直接基于linux实现应用虚拟化,相比虚拟机大幅提高性能和效率。

节省开支:提高服务器利用率,降低IT支出。

简化配置:将运行环境打包保存至容器,使用时直接启动即可。

快速迁移和扩展:可夸平台运行在物理机、虚拟机、公有云等环境,良好的兼容性可以方便将应用从A宿主机迁移到B宿主机,甚至是A平台迁移到B平台。

缺点:

隔离性:各应用之间的隔离不如虚拟机。

除了docker之外的docker技术,还有coreOS的rkt,还有阿里的Pouch,为了保证容器生态的标志性和健康可持续发展,包括Google、Docker等公司共同成立了一个叫opencontainer(OCI)的组织,其目的就是制定开放的标准的容器规范,目前OCI一共发布了两个规范,分别是runtimespec和imageformatspec,有了这两个规范,不同的容器公司开发的容器只要兼容这两个规范,就可以保证容器的可移植性和相互可操作性。

容器runtime:

runtime是真正运行容器的地方,因此为了运行不同的容器runtime需要和操作系统内核紧密合作相互在支持,以便为容器提供相应的运行环境。

目前主流的三种runtime:

Lxc:linux上早期的runtime,Docker早期就是采用lxc作为runtime。

runc:目前Docker默认的runtime,runc遵守OCI规范,因此可以兼容lxc。

rkt:是CoreOS开发的容器runtime,也符合OCI规范,所以使用rktruntime也可以运行Docker容器。

容器管理工具:

管理工具连接runtime与用户,对用户提供图形或命令方式操作,然后管理工具将用户操作传递给runtime执行。

Lxd是lxc的管理工具。

Runc的管理工具是dockerengine,dockerengine包含后台deamon和cli两部分,大家经常提到的Docker就是指的dockerengine。

Rkt的管理工具是rktcli。

容器定义工具:

容器定义工具允许用户定义容器的属性和内容,以方便容器能够被保存、共享和重建。

Dockerimage:是docker容器的模板,runtime依据dockerimage创建容器。

Dockerfile:包含N个命令的文本文件,通过dockerfile创建出dockerimage。

ACI(Appcontainerimage):与dockerimage类似,是CoreOS开发的rkt容器的镜像格式。

Registry:

统一保存共享镜像的地方,叫做镜像仓库。

Imageregistry:docker官方提供的私有仓库部署工具。

Dockerhub:docker官方的公共仓库,已经保存了大量的常用镜像,可以方便大家直接使用。

Harbor:vmware提供的自带web的镜像仓库,目前有很多公司使用。

编排工具:

当多个容器在多个主机运行的时候,单独管理每个容器是相当复杂而且很容易出错,而且也无法实现某一台主机宕机后容器自动迁移到其他主机从而实现高可用的目的,也无法实现动态伸缩的功能,因此需要有一种工具可以实现统一管理、动态伸缩、故障自愈、批量执行等功能,这就是容器编排引擎。

容器编排通常包括容器管理、调度、集群定义和服务发现等功能。

Dockerswarm:docker开发的容器编排引擎。

Kubernetes:google领导开发的容器编排引擎,内部项目为Borg,且其同时支持docker和rkt。

Mesos+Marathon:通用的集群组员调度平台,mesos与marathon一起提供容器编排引擎功能。

容器网络:

docker自带的网络dockernetwork仅支持管理单机上的容器网络,当多主机运行的时候需要使用第三方开源网络,例如calico、flannel等。

服务发现:

容器的动态扩容特性决定了容器IP也会随之变化,因此需要有一种机制开源自动识别并将用户请求动态转发到新创建的容器上,kubernetes自带服务发现功能,需要结合kube-dns服务解析内部域名。【现在是Core-dns】

容器监控:

可以通过原生命令dockerps/top/stats查看容器运行状态,另外也可以使heapster/Prometheus等第三方监控工具监控容器的运行状态。

数据管理:

日志收集:

docker原生的日志查看工具dockerlogs,但是容器内部的日志需要通过ELK等专门的日志收集分析和展示工具进行处理。

实现内核级虚拟化(容器)服务,让同一个Namespace下的进程可以感知彼此的变化,同时又能确保对外界的进程一无所知,以达到独立和隔离的目的。

通过查看/proc目录下以进程ID作为名称的子目录中的信息,能了解该进程的一组NamespaceID

pidnamespace

不同用户的进程就是通过pidnamespace隔离开的,且不同namespace中可以有相同PID。

具有以下特征:

每个namespace中的pid是有自己的pid=1的进程(类似/sbin/init进程)

每个namespace中的进程只能影响自己的同一个namespace或子namespace中的进程

因为/proc包含正在运行的进程,因此在container中的pseudo-filesystem的/proc目录只能看到自己namespace中的进程

因为namespace允许嵌套,父namespace可以影响子namespace的进程,所以子namespace的进程可以在父namespace中看到,但是具有不同的pid

mntnamespace

类似chroot,将一个进程放到一个特定的目录执行。mntnamespace允许不同namespace的进程看到的文件结构不同,这样每个namespace中的进程所看到的文件目录就被隔离开了。同chroot不同,每个namespace中的container在/proc/mounts的信息只包含所在namespace的mountpoint。

netnamespace

网络隔离是通过netnamespace实现的,每个netnamespace有独立的networkdevices,IPaddresses,IProutingtables,/proc/net目录。这样每个container的网络就能隔离开来。docker默认采用veth的方式将container中的虚拟网卡同host上的一个dockerbridge连接在一起。

utsnamespace

UTS("UNIXTime-sharingSystem")namespace允许每个container拥有独立的hostname和domainname,使其在网络上可以被视作一个独立的节点而非Host上的一个进程。

ipcnamespace

container中进程交互还是采用Linux常见的进程间交互方法(interprocesscommunication-IPC),包括常见的信号量、消息队列和共享内存。然而同VM不同,container的进程间交互实际上还是host上具有相同pidnamespace中的进程间交互,因此需要在IPC资源申请时加入namespace信息-每个IPC资源有一个唯一的32bitID。

usernamespace

每个container可以有不同的user和groupid,也就是说可以以container内部的用户在container内部执行程序而非Host上的用户。

有了以上6种namespace从进程、网络、IPC、文件系统、UTS和用户角度的隔离,一个container就可以对外展现出一个独立计算机的能力,并且不同container从OS层面实现了隔离。然而不同namespace之间资源还是相互竞争的,仍然需要类似ulimit来管理每个container所能使用的资源。

cgroups是Linux内核提供的一种可以限制、记录、隔离进程组所使用的物理资源(包括CPU、内存、磁盘I/O速度等)的机制,也是容器管理虚拟化系统资源的手段。

查看任意进程在/proc目录下的内容,可以看到一个名为cgroup的文件,每个挂载点都是一个CGroup子系统的根目录

实现了对资源的配额和度量。cgroups的使用非常简单,提供类似文件的接口,在/cgroup目录下新建一个文件夹即可新建一个group,在此文件夹中新建task文件,并将pid写入该文件,即可实现对该进程的资源控制。具体的资源配置选项可以在该文件夹中新建子subsystem,{子系统前缀}.{资源项}是典型的配置方法,如memory.usageinbytes就定义了该group在subsystemmemory中的一个内存限制选项。

另外,cgroups中的subsystem可以随意组合,一个subsystem可以在不同的group中,也可以一个group包含多个subsystem-也就是说一个subsystem。

在Linux4.7.1内核中,已经支持了10类不同的子系统,分别如下所示:

hugetlb:

限制进程对大页内存(Hugepage)的使用

memory:

限制进程对内存和Swap的使用,并生成每个进程使用的内存资源报告

pids:

限制每个CGroup中能够创建的进程总数

cpuset:

在多核系统中为进程分配独立CPU和内存

devices:

允许或拒绝进程访问特定设备

net_cls和net_prio:

标记每个网络包,并控制网卡优先级

cpu和cpuacct:

限制进程对CPU的用量,并生成每个进程所使用的CPU报告

freezer:

挂起或恢复特定的进程

blkio:

为进程对块设备(如磁盘、USB等)限制输入/输出

perf_event:

监测属于特定的CGroup的所有线程以及运行在特定CPU上的线程

1)可以建立一个容纳应用程序的容器。

2)可以从Docker镜像创建Docker容器来运行应用程序。

docker镜像是如何工作的

Docker镜像是Docker容器运行时的只读模板,每一个镜像由一系列的层(layers)组成;

Docker使用UnionFS(联合文件系统)来将这些层联合到一二镜像中,UnionFS文件系统允许独立文件系统中的文件和文件夹(称之为分支)被透明覆盖,形成一个单独连贯的文件系统。

正因为有了这些层(layers)的存在,Docker才会如此的轻量。当你改变了一个Docker镜像,比如升级到某个程序到新的版本,一个新的层会被创建。因此,不用替换整个原先的镜像或者重新建立(在使用虚拟机的时候你可能会这么做),只是一个新的层被添加或升级了。所以你不用重新发布整个镜像,只需要升级层,使得分发Docker镜像变得简单和快速。

每个镜像都是从一个基础的镜像开始的,比如ubuntu,一个基础的Ubuntu镜像,或者是Centos,一个基础的Centos镜像。你可以使用你自己的镜像作为新镜像的基础,例如你有一个基础的安装了Nginx的镜像,你可以使用该镜像来建立你的Web应用程序镜像。(Docker通常从DockerHub获取基础镜像)

Docker镜像从这些基础的镜像创建,通过一种简单、具有描述性的步骤,我们称之为指令(instructions)。

每一个指令会在镜像中创建一个新的层,指令可以包含这些动作:

1)运行一个命令。

2)增加文件或者文件夹。

3)创建一个环境变量。

4)当运行容器的时候哪些程序会运行。

这些指令存储在Dockerfile文件中。当你需要建立镜像的时候,Docker可以从Dockerfile中读取这些指令并且运行,然后返回一个最终的镜像。

docker仓库是如何工作的

Docker仓库是Docker镜像的存储仓库。可以推送镜像到Docker仓库中,然后在Docker客户端,可以从Docker仓库中搜索镜像。

Docker容器是如何工作的

一个Docker容器包含了一个操作系统、用户添加的文件和元数据(meta-data)。每个容器都是从镜像建立的,镜像告诉Docker容器内包含了什么,当容器启动时运行什么程序,还有许多配置数据。

Docker镜像是只读的,当Docker运行一个从镜像建立的容器,它会在镜像顶部添加一个可读写的层,应用程序可以在这里运行。

当运行docker容器时发生了什么

使用docker命令时,Docker客户端都告诉Docker守护进程运行一个容器。

dockerrun-i-tubuntu/bin/bash

可以来分析这个命令,Docker客户端使用docker命令来运行,run参数表明客户端要运行一个新的容器。

Docker客户端要运行一个容器需要告诉Docker守护进程的最少参数信息是:

1)这个容器从哪个镜像创建,这里是ubuntu,基础的Ubuntu镜像。

2)在容器中要运行的命令,这里是/bin/bash,在容器中运行Bashshell。

那么运行这个命令之后在底层发生了什么呢?

按照顺序,Docker做了这些事情:

1)拉取ubuntu镜像:Docker检查ubuntu镜像是否存在,如果在本地没有该镜像,Docker会从DockerHub下载。如果镜像已经存在,Docker会使用它来创建新的容器。

2)创建新的容器:当Docker有了这个镜像之后,Docker会用它来创建一个新的容器。

3)分配文件系统并且挂载一个可读写的层:容器会在这个文件系统中创建,并且一个可读写的层被添加到镜像中。

4)分配网络/桥接接口:创建一个允许容器与本地主机通信的网络接口。

5)设置一个IP地址:从池中寻找一个可用的IP地址并且附加到容器上。

6)运行你指定的程序:运行指定的程序。

7)捕获并且提供应用输出:连接并且记录标准输出、输入和错误让你可以看到你的程序是如何运行的。

由此你就可以拥有一个运行着的Docker容器了!从这里开始你可以管理你的容器,与应用交互,应用完成之后,可以停止或者删除你的容器。

简单配置、代码流水线管理、开发效率、应用隔离、服务器整合、调试能力、多租户、快速部署

官方网址:

系统版本选择:

Docker目前已经支持多种操作系统的安装运行,比如Ubuntu、CentOS、Redhat、Debian、Fedora,甚至是还支持了Mac和Windows,在linux系统上需要内核版本在3.10或以上,docker版本号之前一直是0.X版本或1.X版本,但是从2017年3月1号开始改为每个季度发布一次稳版,其版本号规则也统一变更为YY.MM,例如18.09表示是2018年9月份发布的,本次演示的操作系统使用Centos7.6为例,内核4.4。

[root@docker~]#cat/etc/redhat-releaseCentOSLinuxrelease7.6.1810(Core)[root@docker~]#uname-r4.4.186-1.el7.elrepo.x86_64Docker版本选择:

下载rpm包安装:

通过yum源安装:【常用】

#验证docker0网卡[root@docker~]#ifconfigdocker0:flags=4099mtu1500inet172.17.0.1netmask255.255.0.0broadcast172.17.255.255ether02:42:22:7f:4a:b1txqueuelen0(Ethernet)RXpackets0bytes0(0.0B)RXerrors0dropped0overruns0frame0TXpackets0bytes0(0.0B)TXerrors0dropped0overruns0carrier0collisions0eth0:flags=4163mtu1500inet20.0.0.209netmask255.255.255.0broadcast20.0.0.255ether00:0c:29:da:d7:53txqueuelen1000(Ethernet)RXpackets79925bytes109388735(104.3MiB)RXerrors0dropped0overruns0frame0TXpackets14335bytes1002691(979.1KiB)TXerrors0dropped0overruns0carrier0collisions0#查看docker网络[root@docker~]#dockernetworklistNETWORKIDNAMEDRIVERSCOPE064e0098dd15bridgebridgelocal48cf42c3b371hosthostlocal4c1006be1ea1nonenulllocaldocker存储引擎

目前docker的默认存储引擎为overlay2,需要磁盘分区支持d-type文件分层功能,因此需要系统磁盘的额外支持。

[root@docker~]#xfs_info/meta-data=/dev/sda3isize=512agcount=4,agsize=1179584blks=sectsz=512attr=2,projid32bit=1=crc=1finobt=0spinodes=0data=bsize=4096blocks=4718336,imaxpct=25=sunit=0swidth=0blksnaming=version2bsize=4096ascii-ci=0ftype=1log=internalbsize=4096blocks=2560,version=2=sectsz=512sunit=0blks,lazy-count=1realtime=noneextsz=4096blocks=0,rtextents=0如果docker数据目录是一块单独的磁盘分区而且是xfs格式的,那么需要在格式化的时候加上参数-nftype=1,否则后期在启动容器的时候会报错不支持d-type。

报错界面:

这里需要明确一个问题,就是Mirror与PrivateRegistry的区别。二者有着本质的差别:

1)PrivateRegistry(私有仓库)是开发者或者企业自建的镜像存储库,通常用来保存企业内部的Docker镜像,用于内部开发流程和产品的发布、版本控制。

2)Mirror是一种代理中转服务,我们(比如daocloud)提供的Mirror服务,直接对接DockerHub的官方Registry。DockerHub上有数以十万计的各类Docker镜像。

3)在使用PrivateRegistry时,需要在DockerPull或Dockerfile中直接键入PrivateRegistry的地址,通常这样会导致与PrivateRegistry的绑定,缺乏灵活性。

5)简单来说,Mirror类似CDN(内容分发网络),本质是官方的cache;PrivateRegistry类似私服,跟官方没什么关系。对用户来说,由于用户是要拖dockerhub上的image,对应的是Mirror。yum/apt-get的Mirror又有点不一样,它其实是把官方的库文件整个拖到自己的服务器上做镜像(不管有没有用),并定时与官方做同步;而DockerMirror只会缓存曾经使用过的image。

目前国内访问dockerhub速度上有点尴尬,使用dockerMirror势在必行。

现有国内提供docker镜像加速服务的商家有不少,下面重点介绍几家:

(1).ustc的镜像

ustc是老牌的linux镜像服务提供者了,还在遥远的ubuntu5.04版本的时候就在用。之前在blog里有提到可以用ustc的docker仓库镜像.

使用方法参考ustcdocker镜像使用帮助

ustc的docker镜像加速器速度很不错,一直用的挺happy。ustcdockermirror的优势之一就是不需要注册,真正是公共服务啊。

(2).daocloud镜像

DaoCloud也提供了docker加速器,但是跟ustc不同,需要用户注册后才能使用,并且每月限制流量10GB。linux上使用比较简单,一条脚本命令搞定:

(3).alicloud

阿里云也提供了docker加速器,不过比daocloud更麻烦:不光要注册为阿里云的用户,还得加入开发者平台。

不过虽然麻烦,但是它的服务还真是不错,pull速度很溜!配置方法跟daocloud类似,也是开通加速器以后给一个url。

(4).网易镜像

网易也提供了Docker镜像服务:网易蜂巢

我在本次学习中使用的是如下:

docker命令是最常使用的命令,其后面可以加不同的参数以实现相应的功能,常用的命令如下:

dockerrun[选项][镜像名][shell命令][参数]

进入容器的方法对比

方法一:dockerattachcontainer_NAME/container_ID

注:当多个窗口使用该命令进入该容器时,所有窗口都会显示同步。如果一个窗口阻塞了,其他窗口无法再进行操作;。因此dockerattach命令不太适合于生产环境。且该命令有点古老,不太建议使用

方法二:使用ssh进入docker容器

docker应用容器是一个Linux虚拟主机,那么就可以在该主机上面安装一个sshserver就可以通过ssh协议来链接该容器

注:这种出力不讨好的方法,了解一下就可以了。

方法三:使用nsenter进入docker容器

对于nsenter网上有比较多且详细的介绍,这里我大致写一下操作过程

nsenter命令需要通过PID进入到容器内部,不过可以使用dockerinspect获取到容器的PID

因此:nsenter需要在宿主机安装而非容器或者镜像

方法四:dockerexec命令

[root@docker~]#dockerexec--helpUsage:dockerexec[OPTIONS]CONTAINERCOMMAND[ARG...]RunacommandinarunningcontainerOptions:-d,--detachDetachedmode:runcommandinthebackground--detach-keysstringOverridethekeysequencefordetachingacontainer-e,--envlistSetenvironmentvariables-i,--interactiveKeepSTDINopenevenifnotattached--privilegedGiveextendedprivilegestothecommand-t,--ttyAllocateapseudo-TTY-u,--userstringUsernameorUID(format:[:])-w,--workdirstringWorkingdirectoryinsidethecontainer[root@docker~]#dockerpsCONTAINERIDIMAGECOMMANDCREATEDSTATUSPORTSNAMES02379c036216busybox:latest"sh"33minutesagoUp12minutesb1[root@docker~]#dockerexec-itb1sh注:最常用的方法!!!

#删除运行中的容器即使容器正在运行中,也会被强制删除[root@docker~]#dockerpsCONTAINERIDIMAGECOMMANDCREATEDSTATUSPORTSNAMES02379c036216busybox:latest"sh"39minutesagoUp2secondsb1[root@docker~]#dockercontainerrm-fb1b1[root@docker~]#dockerps-aCONTAINERIDIMAGECOMMANDCREATEDSTATUSPORTSNAMES端口映射

#随机映射端口[root@docker~]#dockerimagelsREPOSITORYTAGIMAGEIDCREATEDSIZEbusyboxlatestdb8ee88ad75f11daysago1.22MB[root@docker~]#dockerps-aCONTAINERIDIMAGECOMMANDCREATEDSTATUSPORTSNAMES[root@docker~]#dockerpullnginxdocker.io/library/nginx:latest

注:随机端口映射,其实默认是从1024开始

浏览器访问

#指定端口映射方式1:本地端口81映射到容器80端口:#dockerrun-d-p81:80--namenginx-test-port1nginx-->-d后台启动容器方式2:本地IP:本地端口:容器端口#dockerrun-d-p20.0.0.209:82:80--namenginx-test-port2docker.io/nginx方式3:本地IP:本地随机端口:容器端口#dockerrun-d-p20.0.0.209::80--namenginx-test-port3docker.io/nginx方式4:本机ip:本地端口:容器端口/协议,默认为tcp协议#dockerrun-d-p20.0.0.209:83:80/udp--namenginx-test-port4docker.io/nginx方式5:一次性映射多个端口+协议:#dockerrun-d-p86:80/tcp-p443:443/tcp-p53:53/udp--namenginx-test-port5docker.io/nginx查看容器的日志

[root@docker~]#dockerportnginx-test-port380/tcp->20.0.0.209:1024容器退出后自动删除

[root@docker~]#dockerrun-it--rm--namenginxnginxbash传递运行命令

容器需要有一个前台运行的进程才能保持容器的运行,通过传递运行参数是一种方式,另外也可以在构建镜像的时候指定容器启动时运行的前台命令。[root@docker~]#dockerrun-dcentos/usr/bin/tail-f'/etc/hosts'[root@docker~]#dockerps-lCONTAINERIDIMAGECOMMANDCREATEDSTATUSPORTSNAMESc8a7605ede61centos"/usr/bin/tail-f/e…"32secondsagoUp31secondsstrange_curie容器的启动和关闭

dockerstop/startcontainer_NAME/container_ID批量操作

#批量关闭正在运行的容器[root@docker~]#dockerstop$(dockerps-a-q)--->正常关闭所有运行中的容器#批量强制关闭正在运行的容器[root@docker~]#dockerkill$(dockerps-a-q)--->强制关闭所有运行中的容器#批量删除已退出的容器[root@docker~]#dockerrm-f`dockerps-aq-fstatus=exited`#批量删除所有容器[root@docker~]#dockerrm-f`dockerps-a-q`一个例子

[root@docker~]#dockerrun--nametext-it--networkbridge-hzisefeizhu.com--dns223.6.6.6--rmbusybox:latest/#hostnamezisefeizhu.com/#cat/etc/resolv.confnameserver223.6.6.6/#exit[root@docker~]#dockerps-aCONTAINERIDIMAGECOMMANDCREATEDSTATUSPORTSNAMES2.5docker镜像技术一个镜像是一个惰性的,不可变的文件,它本质上是一个容器的快照。它只是一个模板,其中包含有关创建Docker容器的说明。

镜像存储在Docker仓库中,例如registry.hub.docker.com。因为它们可能变得非常大,所以镜像被设计为由其他镜像的层组成,允许在通过网络传输图像时发送最少量的数据。

docker镜像是一个只读的docker容器模板,含有启动docker容器所需的文件系统结构及其内容,因此是启动一个docker容器的基础。docker镜像的文件内容以及一些运行docker容器的配置文件组成了docker容器的静态文件系统运行环境:rootfs。可以这么理解,docker镜像是docker容器的静态视角,docker容器是docker镜像的运行状态。我们可以通过下图来理解dockerdaemon、docker镜像以及docker容器三者的关系(此图来自互联网):

rootfs

rootfs是docker容器在启动时内部进程可见的文件系统,即docker容器的根目录。rootfs通常包含一个操作系统运行所需的文件系统,例如可能包含典型的类Unix操作系统中的目录系统,如/dev、/proc、/bin、/etc、/lib、/usr、/tmp及运行docker容器所需的配置文件、工具等。

在传统的Linux操作系统内核启动时,首先挂载一个只读的rootfs,当系统检测其完整性之后,再将其切换为读写模式。而在docker架构中,当dockerdaemon为docker容器挂载rootfs时,沿用了Linux内核启动时的做法,即将rootfs设为只读模式。在挂载完毕之后,利用联合挂载(unionmount)技术在已有的只读rootfs上再挂载一个读写层。这样,可读写的层处于docker容器文件系统的最顶层,其下可能联合挂载了多个只读的层,只有在docker容器运行过程中文件系统发生变化时,才会把变化的文件内容写到可读写层,并隐藏只读层中的旧版本文件。

为了更好的理解docker镜像的结构,下面介绍一下docker镜像设计上的关键技术。

分层

docker镜像是采用分层的方式构建的,每个镜像都由一系列的"镜像层"组成。分层结构是docker镜像如此轻量的重要原因。当需要修改容器镜像内的某个文件时,只对处于最上方的读写层进行变动,不覆写下层已有文件系统的内容,已有文件在只读层中的原始版本仍然存在,但会被读写层中的新版本所隐藏。当使用dockercommit提交这个修改过的容器文件系统为一个新的镜像时,保存的内容仅为最上层读写文件系统中被更新过的文件。分层达到了在不的容器同镜像之间共享镜像层的效果。

分层技术——aufs

Aufs是AnotherUnionFileSystem的缩写,支持将多个目录挂载到同一个虚拟目录下。

已构建的镜像会设置成只读模式,read-write写操作是在read-only上的一种增量操作,固不影响read-only层。

这个研究有一个好处,比如我们现在可以看到手机里面的APP,在命令里面都会用APP字段下回来,在下回来之前它就是一个静态的,我们没有往里面写东西,但是我们启动起来以后,我们就可以往里面写东西,进行各种各样的操作。但是如果我们把它关掉了以后,或者删除了以后,它的这个镜像是存在远端的,所以在这个镜像里面是不会去修改的。并且这样也会有一个非常好的地方,这个场景非常适合我们去实现测试环境,因为我们的测试环境经常会有一个操作就是灌数据,我们可以提前把这个镜像数据打包到测试里面,那么这个镜像软件里面包含了,最上面是nginx,比如它里面会有一些数据,我们可以在往上面打一层数据,打完之后把它起成一个容器就可以去测试,测试完之后这个容器里面会生成各种各样的数据,也就是脏数据,这样的话,我们就可以把这个容器删掉,删掉以后我们镜像里面的容器是不会受影响的。如果说它想再创建一套,我们可以把这个镜像再启一个容器,就可以是一个一模一样的,并且是一个干净的环境。

我们先来看一个Ubuntu系统的镜像

我们看见镜像可以分层很多个layer,并且他们都有大小和ID,我们可以看到这里有4个layerID号,最终这个镜像是由他们layer组合而成,并且这个镜像它是只读的,它不能往里面写数据,如果想写数据怎么办呢?我们会在镜像上启一层containlayer,其实就是相当于把镜像启动成一个容器,那么在容器这一层,我们是可写的。

比如我们想在Ubuntu这个系统上加一层,只能在上面继续叠加,这些工作其实都是由cow,写字库下的机制来实现的。

写时复制

内容寻址

在docker1.10版本后,docker镜像改动较大,其中最重要的特性便是引入了内容寻址存储(content-addressablestorage)的机制,根据文件的内容来索引镜像和镜像层。与之前版本对每个镜像层随机生成一个UUID不同,新模型对镜像层的内容计算校验和,生成一个内容哈希值,并以此哈希值代替之前的UUID作为镜像层的唯一标识。该机制主要提高了镜像的安全性,并在pull、push、load和save操作后检测数据的完整性。另外,基于内容哈希来索引镜像层,在一定程度上减少了ID的冲突并且增强了镜像层的共享。对于来自不同构建的镜像层,主要拥有相同的内容哈希,也能被不同的镜像共享。

联合挂载

通俗地讲,联合挂载技术可以在一个挂载点同时挂载多个文件系统,将挂载点的原目录与被挂载内容进行整合,使得最终可见的文件系统将会包含整合之后的各层的文件和目录。实现这种联合挂载技术的文件系统通常被称为联合文件系统(unionfilesystem)。以下图所示的运行Ubuntu:14.04镜像后的容器中的aufs文件系统为例:

由于初始挂载时读写层为空,所以从用户的角度看,该容器的文件系统与底层的rootfs没有差别;然而从内核的角度看,则是显式区分开来的两个层次。当需要修改镜像内的某个文件时,只对处于最上方的读写层进行了变动,不复写下层已有文件系统的内容,已有文件在只读层中的原始版本仍然存在,但会被读写层中的新版本文件所隐藏,当dockercommit这个修改过的容器文件系统为一个新的镜像时,保存的内容仅为最上层读写文件系统中被更新过的文件。

联合挂载是用于将多个镜像层的文件系统挂载到一个挂载点来实现一个统一文件系统视图的途径,是下层存储驱动(aufs、overlay等)实现分层合并的方式。所以严格来说,联合挂载并不是docker镜像的必需技术,比如在使用devicemapper存储驱动时,其实是使用了快照技术来达到分层的效果。

综合考虑镜像的层级结构,以及volume、init-layer、可读写层这些概念,一个完整的、在运行的容器的所有文件系统结构可以用下图来描述:

从图中我们不难看到,除了echohello进程所在的cgroups和namespace环境之外,容器文件系统其实是一个相对独立的组织。可读写部分(read-writelayer以及volumes)、init-layer、只读层(read-onlylayer)这3部分结构共同组成了一个容器所需的下层文件系统,它们通过联合挂载的方式巧妙地表现为一层,使得容器进程对这些层的存在一无所知。

我们知道,每个docker容器都要依赖docker镜像。那么当我们第一次使用dockerrun命令启动一个容器时,是从哪里获取所需的镜像呢?

答案是,如果是第一次基于某个镜像启动容器,且宿主机上并不存在所需的镜像,那么docker将从registry中下载该镜像并保存到宿主机。如果宿主机上存在该镜像,则直接使用宿主机上的镜像完成容器的启动。

那么registry是什么呢?

registry用以保存docker镜像,其中还包括镜像层次结构和关于镜像的元数据。可以将registry简单的想象成类似于Git仓库之类的实体。

用户可以在自己的数据中心搭建私有的registry,也可以使用docker官方的公用registry服务,即DockerHub。它是由Docker公司维护的一个公共镜像库。DockerHub中有两种类型的仓库,即用户仓库(userrepository)与顶层仓库(top-levelrepository)。用户仓库由普通的DockerHub用户创建,顶层仓库则由Docker公司负责维护,提供官方版本镜像。理论上,顶层仓库中的镜像经过Docker公司验证,被认为是架构良好且安全的。

分类:

SponsorRegistry:第三方的registry,供客户和Docker社区使用

MirrorRegistry:第三方的registry,只让客户使用

VendorRegistry:由发布Docker镜像的供应商提供的registry

PrivateRegistry:通过设有防火墙和额外的安全层的私有实体提供的registry

repository

repository由具有某个功能的docker镜像的所有迭代版本构成的镜像组。Registry由一系列经过命名的repository组成,repository通过命名规范对用户仓库和顶层仓库进行组织。所谓的顶层仓库,其其名称只包含仓库名,如:

而用户仓库的表示类似下面:

可以看出,用户仓库的名称多了"用户名/"部分。

比较容易让人困惑的地方在于,我们经常把mysql视为镜像的名称,其实mysql是repository的名称。repository是一个镜像的集合,其中包含了多个不同版本的镜像,这些镜像之间使用标签进行版本区分,如mysql:5.6、mysql:5.7等,它们均属于mysql这个repository。

简单来说,registry是repository的集合,repository是镜像的集合。

manifest

manifest(描述文件)主要存在于registry中作为docker镜像的元数据文件,在pull、push、save和load过程中作为镜像结构和基础信息的描述文件。在镜像被pull或者load到docker宿主机时,manifest被转化为本地的镜像配置文件config。在我们拉取镜像时显示的摘要(Digest):就是对镜像的manifest内容计算sha256sum得到的。

image和layer

layer(镜像层)是docker用来管理镜像层的一个中间概念。我们前面提到,镜像是由镜像层组成的,而单个镜像层可能被多个镜像共享,所以docker将layer与image的概念分离。docker镜像管理中的layer主要存放了镜像层的diff_id、size、cache-id和parent等内容,实际的文件内容则是由存储驱动来管理,并可以通过cache-id在本地索引到。

现在docker官方公有仓库里面有大量的镜像,所以最基础的镜像,我们可以在公有仓库直接拉取,因为这些镜像都是原厂维护,可以得到及时的更新和修护。

Dockerfile:

我们如果想去定制这些镜像,我们可以去编写Dockerfile,然后重新bulid,最后把它打包成一个镜像,这种方式是最为推荐的方式包括我们以后去企业当中去实践应用的时候也是推荐这种方式。

Commit:

当然还有另外一种方式,就是通过镜像启动一个容器,然后进行操作,最终通过commit这个命令commit一个镜像,但是不推荐这种方式,虽然说通过commit这个命令像是操作虚拟机的模式,但是容器毕竟是容器,它不是虚拟机,所以大家还是要去适应用Dockerfile去定制这些镜像这种习惯。

[root@docker~]#dockercommit--helpUsage:dockercommit[OPTIONS]CONTAINER[REPOSITORY[:TAG]]Createanewimagefromacontainer'schangesOptions:-a,--authorstringAuthor(e.g.,"JohnHannibalSmith")-c,--changelistApplyDockerfileinstructiontothecreatedimage-m,--messagestringCommitmessage-p,--pausePausecontainerduringcommit(defaulttrue)

在开始的时候就有提过,现在的linux内核已经支持六种名称空间:

UTS:主机名和域名

USER:用户

Mount:挂载文件系统

IPC:进程间通信

Pid:进程id

Net:网络

网络作为docker容器化实现的6个名称空间的其中之一,是必不可少的。其在Linux内核2.6时已经被加载进内核支持了。

网络名称空间主要用于实现网络设备和协议栈的隔离。

网络虚拟化相对计算、存储虚拟化来说是比较抽象的,以我们在学校书本上学的那点网络知识来理解网络虚拟化可能是不够的。

在我们的印象中,网络就是由各种网络设备(如交换机、路由器)相连组成的一个网状结构,世界上的任何两个人都可以通过网络建立起连接。

带着这样一种思路去理解网络虚拟化可能会感觉云里雾里——这样一个庞大的网络如何实现虚拟化?

在传统网络环境中,一台物理主机包含一个或多个网卡(NIC),要实现与其他物理主机之间的通信,需要通过自身的NIC连接到外部的网络设施,如交换机上,如下图所示。

这种架构下,为了对应用进行隔离,往往是将一个应用部署在一台物理设备上,这样会存在两个问题

1)是某些应用大部分情况可能处于空闲状态

2)是当应用增多的时候,只能通过增加物理设备来解决扩展性问题。不管怎么样,这种架构都会对物理资源造成极大的浪费。

为了解决这个问题,可以借助虚拟化技术对一台物理资源进行抽象,将一张物理网卡虚拟成多张虚拟网卡(vNIC),通过虚拟机来隔离不同的应用。

这样对于上面的问题

针对问题1),可以利用虚拟化层Hypervisor(系统管理程序)的调度技术,将资源从空闲的应用上调度到繁忙的应用上,达到资源的合理利用;

针对问题2),可以根据物理设备的资源使用情况进行横向扩容,除非设备资源已经用尽,否则没有必要新增设备。

这种架构如下所示:

其中虚拟机与虚拟机之间的通信,由虚拟交换机完成,虚拟网卡和虚拟交换机之间的链路也是虚拟的链路,整个主机内部构成了一个虚拟的网络,如果虚拟机之间涉及到三层的网络包转发,则又由另外一个角色——虚拟路由器来完成。

一般,这一整套虚拟网络的模块都可以独立出去,由第三方来完成,如其中比较出名的一个解决方案就是OpenvSwitch(OVS)。

OVS的优势在于它基于SDN的设计原则,方便虚拟机集群的控制与管理,另外就是它分布式的特性,可以「透明」地实现跨主机之间的虚拟机通信。

如下是跨主机启用OVS通信的:

总结下来,网络虚拟化主要解决的是虚拟机构成的网络通信问题,完成的是各种网络设备的虚拟化,如网卡、交换设备、路由设备等。

为了完成虚拟机在同主机和跨主机之间的通信,需要借助某种“桥梁”来完成用户态到内核态(Guest到Host)的数据传输,这种桥梁的角色就是由虚拟的网络设备来完成,上面介绍了一个第三方的开源方案——OVS,它其实是一个融合了各种虚拟网络设备的集大成者,是一个产品级的解决方案。

但Linux本身由于虚拟化技术的演进,也集成了一些虚拟网络设备的解决方案,主要有以下几种:

TAP/TUN/VETH

TAP/TUN是Linux内核实现的一对虚拟网络设备,TAP工作在二层,TUN工作在三层。Linux内核通过TAP/TUN设备向绑定该设备的用户空间程序发送数据,反之,用户空间程序也可以像操作物理网络设备那样,向TAP/TUN设备发送数据。

基于TAP驱动,即可实现虚拟机vNIC的功能,虚拟机的每个vNIC都与一个TAP设备相连,vNIC之于TAP就如同NIC之于eth。

当一个TAP设备被创建时,在Linux设备文件目录下会生成一个对应的字符设备文件,用户程序可以像打开一个普通文件一样对这个文件进行读写。

比如,当对这个TAP文件执行write操作时,相当于TAP设备收到了数据,并请求内核接受它,内核收到数据后将根据网络配置进行后续处理,处理过程类似于普通物理网卡从外界收到数据。当用户程序执行read请求时,相当于向内核查询TAP设备是否有数据要发送,有的话则发送,从而完成TAP设备的数据发送。

VETH设备总是成对出现,一端连着内核协议栈,另一端连着另一个设备,一个设备收到内核发送的数据后,会发送到另一个设备上去,这种设备通常用于容器中两个namespace之间的通信。

Bridge

Bridge也是Linux内核实现的一个工作在二层的虚拟网络设备,但不同于TAP/TUN这种单端口的设备,Bridge实现为多端口,本质上是一个虚拟交换机,具备和物理交换机类似的功能。

Bridge可以绑定其他Linux网络设备作为从设备,并将这些从设备虚拟化为端口,当一个从设备被绑定到Bridge上时,就相当于真实网络中的交换机端口上插入了一根连有终端的网线。

如下图所示,Bridge设备br0绑定了实际设备eth0和虚拟设备tap0/tap1,当这些从设备接收到数据时,会发送给br0,br0会根据MAC地址与端口的映射关系进行转发。

因为Bridge工作在二层,所以绑定到它上面的从设备eth0、tap0、tap1均不需要设IP,但是需要为br0设置IP,因为对于上层路由器来说,这些设备位于同一个子网,需要一个统一的IP将其加入路由表中。

这里有人可能会有疑问,Bridge不是工作在二层吗,为什么会有IP的说法?其实Bridge虽然工作在二层,但它只是Linux网络设备抽象的一种,能设IP也不足为奇。

对于实际设备eth0来说,本来它是有自己的IP的,但是绑定到br0之后,其IP就生效了,就和br0共享一个IP网段了,在设路由表的时候,就需要将br0设为目标网段的地址。

多节点:另一台主机上的一个容器,与1号主机上的容器进行通信,vmware实现不同主机上的虚拟机之间的通讯可以使用桥接的方式,就是把物理网卡当作交换机来使用,所有一台主机上的容器都到一个物理网卡来,通过MAC地址来确定交给那个容器,如果是到物理机的,就给物理机,也就是虚拟机里也有自身的独特的MAC地址,所以数据包来时可以区别各个设备,把物理网卡当作交换机来使用,把报文转发给各容器,如果报文目标是物理网卡时,需要虚拟出一个软网卡作为物理网卡的使用,这样就没有虚拟交换机概念,所以两台主机上的虚拟机要使用桥接通讯时,都是连接到各自主机上的物理网卡的的。但是这种通讯方式要实现有很大的代价,因为所有容器的桥接都在同一个平面中,很容易产生风暴,所以在大规模的虚拟机或容器的使用场景中使用桥接不太好,除非能隔离得很好(桥接)。

基于实现方式的分类

隧道方案(OverlayNetworking):

Weave:UDP广播,本机建立新的BR,通过PCAP互通。

OpenvSwitch(OVS):基于VxLAN和GRE协议,但是性能方面损失比较严重。

Flannel:UDP广播,VxLan。

路由方案:

Calico:基于BGP协议的路由方案,支持很细致的ACL控制,对混合云亲和度比较高。

Macvlan:从逻辑和Kernel层来看隔离性和性能最优的方案,基于二层隔离,所以需要二层路由器支持,大多数云服务商不支持,所以混合云上比较难以实现。

基于网络模型分类

DockerLibnetworkContainerNetworkModel(CNM):

DockerSwarmoverlay

Macvlan&IPnetworkdrivers

Calico

Contiv(fromCisco)

#DockerLibnetwork的优势就是原生,而且和Docker容器生命周期结合紧密;缺点也可以理解为是原生,被Docker“绑架”。

ContainerNetworkInterface(CNI):

Kubernetes

Weave

Macvlan

Flannel

Contiv

MesosCNI

#CNI的优势是兼容其他容器技术(e.g.rkt)及上层编排系统(Kuberneres&Mesos),而且社区活跃势头迅猛,Kubernetes加上CoreOS主推;缺点是非Docker原生。

既然聊到了k8s简单阐述一下:dockerdshimcontainerdkubeletrunc的关系

dockerd是docker的守护进程

containerd是处理dockerd收到的请求的

shim是处理信号量的

runc是启动容器的

kubelet是k8s中用来管理节点容器生命周期的

它们的关系应该是kubelet--->pod--->dockerd--->containerd--->shim--->runc--->container

详解:

以Flannel方案为例

Flannel之前的名字是Rudder,它是由CoreOS团队针对Kubernetes设计的一个重载网络工具,它的主要思路是:预先留出一个网段,每个主机使用其中一部分,然后每个容器被分配不同的ip;让所有的容器认为大家在同一个直连的网络,底层通过UDP/VxLAN等进行报文的封装和转发

下面这张是Flannel网络的经典架构图:

注:关于“散尽浮华”前辈,我有必要说一下:此乃我IT路上的良师和精神支柱!

关于docker网络解决方案,我本人更推荐使用calico方案。关于calico网络插件会在kubernetes章节重点阐述

docker网络是深入学习docker的重点、难点,强烈建议:一定要耐着性子深入学习这部分。

[root@docker~]#dockernetworklsNETWORKIDNAMEDRIVERSCOPE1fbc952b8c69bridgebridgelocal48cf42c3b371hosthostlocal4c1006be1ea1nonenulllocalbridge:默认网络驱动程序。当你的应用程序在需要通信的独立容器中运行时,通常会使用桥接网络。host:对于独立容器,删除容器和Docker主机之间的网络隔离,并直接使用主机的网络。none:对于此容器,禁用所有网络。container:Container网络模式是Docker中一种较为特别的网络的模式。处于这个模式下的Docker容器会共享其他容器的网络环境,因此,至少这两个容器之间不存在网络隔离,而这两个容器又与宿主机以及除此之外其他的容器存在网络隔离。bridge模式【默认网络模式】

桥接时网络,并不是物理桥,在本机上创建一个纯粹的软交换机docker0,也可以当作网卡来使用,每启动一个容器就可以给容器分配一段网卡的地址,一半在容器上,一半在docker0桥上,veth176661b这种在机器可以看到的无论容器还是KVM时,每次创建网卡时,都是创建一对的,一半放在虚拟机上,一半放在软交换机上,相当于一根网线连接着两个设备一样。

bridge网络的特点

使用一个linuxbridge,默认为docker0

使用veth对,一头在容器的网络namespace中,一头在docker0上

该模式下DockerContainer不具有一个公有IP,因为宿主机的IP地址与vethpair的IP地址不在同一个网段内

Docker采用NAT方式,将容器内部的服务监听的端口与宿主机的某一个端口进行“绑定”,使得宿主机以外的世界可以主动将网络报文发送至容器内部

外界访问容器内的服务时,需要访问宿主机的IP以及宿主机的端口port

NAT模式由于是在三层网络上的实现手段,故肯定会影响网络的传输效率。

容器拥有独立、隔离的网络栈;让容器和宿主机以外的世界通过NAT建立通信

同一个宿主机中,使用同一个docker0中的软交换机进行通讯。

在同一个宿方机之间的容器通讯可以实现,但是跨主机就会产生问题,因为docker本身就是一个natbridge,对外来说是不可见的,要实现不同主机之间的容器的实现通讯,就要做dnat,把接口中发布出来的,假设物理主机上有一个物理网卡,开通一个端口然后提供对外服务,外部主机访问容器中的服务时,使用dnat的方式转到容器中的虚拟网卡中,提供服务。

但是存在一个问,如果在同一台宿主机上,起了两个容器分别是两个nginx的web服务,但是对外的IP只有一个,只能使用端口来区分,假设nginx1使用80,另一个nginx2就只能使用非80的端口,这时client访问的出现问题,因为默认访问就要给80,如果是非80端口就请求不到。

如查使用ovetlaynetwork叠加网络方式就可以直接使用隧道来承载,直接访问就可以了,可以不用对地址进行映射。一般的跨主机之间的虚拟机访问方式桥接、nat的。

host模式

Host模式并没有为容器创建一个隔离的网络环境。该模式下的Docker容器会和host宿主机共享同一个网络namespace,所以容器可以和宿主机一样,使用宿主机的eth0,实现和外界的通信。

host网络特点

这种模式下的容器没有隔离的networknamespace

容器的IP地址同Docker主机的IP地址

需要注意容器中服务的端口号不能与Docker主机上已经使用的端口号相冲突

host模式能够和其它模式共存

none模式

网络模式为none,即不为Docker容器构造任何网络环境,不会为容器创建网络接口,一旦Docker容器采用了none网络模式,那么容器内部就只能使用loopback网络设备,不会再有其他的网络资源。

container模式

joined容器是另一种实现容器间通信的方式。它可以使两个或多个容器共享一个网络栈,共享网卡和配置信息,joined容器之间可以通过127.0.0.1直接通信。

不能在宿主机上很方便地访问容器中的文件。

无法在多个容器之间共享数据。

当容器删除时,容器中产生的数据将丢失。

为了解决这些问题,docker引入了数据卷(volume)机制。数据卷是存在于一个或多个容器中的特定文件或文件夹,这个文件或文件夹以独立于docker文件系统的形式存在于宿主机中。数据卷的最大特定是:其生存周期独立于容器的生存周期。

在多个容器之间共享数据,多个容器可以同时以只读或者读写的方式挂载同一个数据卷,从而共享数据卷中的数据。

当宿主机不能保证一定存在某个目录或一些固定路径的文件时,使用数据卷可以规避这种限制带来的问题。

当你想把容器中的数据存储在宿主机之外的地方时,比如远程主机上或云存储上。

当你需要把容器数据在不同的宿主机之间备份、恢复或迁移时,数据卷是很好的选择。

背景:一个程序,对于容器来说,启动时依赖于可能不止一层的镜像,联合挂载启动而成,使用overlay2文件系统,引导最上层的可写层,对于读写层来说,所有在容器中可执行的操作,包括对数据和内容的修改,都是保存在最上层之上的,对于下层内容的操作,假设要删除一个文件,需要使用写时复制。

docker镜像由多个只读层叠加面成,启动容器时,docker会加载只读镜像层并在镜像栈顶部加一个读写层

如果运行中的容器修改了现有的一个已经存在的文件,那该文件将会从读写层下面的只读层复制到读写层,该文件版本仍然存在,只是已经被读写层中该文件的副本所隐藏,此即“写时复制(COW)”机制

描述:如果一个文件在最底层是可见的,如果在layer1上标记为删除,最高的层是用户看到的Layer2的层,在layer0上的文件,在layer2上可以删除,但是只是标记删除,用户是不可见的,总之在到达最顶层之前,把它标记来删除,对于最上层的用户是不可见的,当标记一删除,只有用户在最上层建一个同名一样的文件,才是可见的。

对于这类的操作,修改删除等,一般效率非常低,如果对一于I/O要求比较高的应用,如redis在实现持久化存储时,是在底层存储时的性能要求比较高。

假设底层运行一个存储库mysql,mysql本来对于I/O的要求就比较高,如果mysql又是运行在容器中自己的文件系统之上时,也就是容器在停止时,就意味着删除,其实现数据存取时效率比较低,要避免这个限制要使用存储卷来实现。

存储卷:可以想象来在各全局的名称空间中,也就是理解为在宿主机中找一个本地的文件系统,可能存在某一个目录中,直接与容器上的文件系统中的某一目录建立绑定关系。

类似于挂载一样,宿主机的/data/web目录与容器中的/container/data/web目录绑定关系,然后容器中的进程向这个目录中写数据时,是直接写在宿主机的目录上的,绕过容器文件系统与宿主机的文件系统建立关联关系,使得可以在宿主机和容器内共享数据库内容,让容器直接访问宿主机中的内容,也可以宿主机向容器供集内容,两者是同步的。

mount名称空间本来是隔离的,可以让两个本来是隔离的文件系统,在某个子路径上建立一定程度的绑定关系,从而使得在两个容器之间的文件系统的某个子路径上不再是隔离的,实现一定程度上共享的效果。

在宿主机上能够被共享的目录(可以是文件)就被称为volume。

优点:容器中进程所生成的数据,都保存在存储卷上,从而脱离容器文件系统自身后,当容器被关闭甚至被删除时,都不用担心数据被丢失,实现数据可以脱离容器生命周期而持久,当再次重建容器时,如果可以让它使用到或者关联到同一个存储卷上时,再创建容器,虽然不是之前的容器,但是数据还是那个数据,特别类似于进程的运行逻辑,进程本身不保存任何的数据,数据都在进程之外的文件系统上,或者是专业的存储服务之上,所以进程每次停止,只是保存程序文件,对于容器也是一样,

容器就是一个有生命周期的动态对象来使用,容器关闭就是容器删除的时候,但是它底层的镜像文件还是存在的,可以基于镜像再重新启动容器。

但是容器有一个问题,一般与进程的启动不太一样,就是容器启动时选项比较多,如果下次再启动时,很多时候会忘记它启动时的选项,所以最好有一个文件来保存容器的启动,这就是容器编排工具的作用。一般情况下,是使用命令来启动操作docker,但是可以通过文件来读,也就读文件来启动,读所需要的存储卷等,但是它也只是操作一个容器,这也是需要专业的容器编排工具的原因。

另一个优势就是容器就可以不置于启动在那台主机之上了,如几台主机后面挂载一个NFS,在各自主机上创建容器,而容器上通过关联到宿主机的某个目录上,而这个目录也是NFS所挂载的目录中,这样容器如果停止或者是删除都可以不限制于只能在原先的宿主机上启动才可以,可以实现全集群范围内调试容器的使用,当再分配存储、计算资源时,就不会再局限于单机之上,可以在集群范围内建立起来,基本各种docker的编排工具都能实现此功能,但是后面严重依赖于共享存储的使用。

考虑到容器应用是需要持久存储数据的,可能是有状态的,如果考虑使用NFS做反向代理是没必要存储数据的,应用可以分为有状态和无状态,有状态是当前这次连接请求处理一定此前的处理是有关联的,无状态是前后处理是没有关联关系的,大多数有状态应用都是数据持久存储的,如mysql,redis有状态应用,在持久存储,如nginx作为反向代理是无状态应用,tomcat可以是有状态的,但是它有可能不需要持久存储数据,因为它的session都是保存在内存中就可以的,会导致节点宕机而丢失session,如果有必要应该让它持久,这也算是有状态的。

应用状态:是否有状态或无状态,是否需要持久存储,可以定立一个正轴坐标系,第一象限中是那些有状态需要存储的,像mysql,redis等服务,有些有有状态但是无需进行存储的,像tomcat把会话保存在内存中时,无状态也无需要存储的数据,如各种反向代理服务器nginx,lvs请求连接都是当作一个独立的连接来调度,本地也不需要保存数据,第四象限是无状态,但是需要存储数据是比较少见。

运维起来比较难的是有状态且需要持久的,需要大量的运维经验和大量的操作步骤才能操作起来的,如做一个Mysql主从需要运维知识、经验整合进去才能实现所谓的部署,扩展或缩容,出现问题后修复,必须要了解集群的规模有多大,有多少个主节点,有多少个从节点,主节点上有多少个库,这些都要一清二楚,才能修复故障,这些就强依赖于运维经验,无状态的如nginx一安装就可以了,并不复杂,对于无状态的应用可以迅速的实现复制,在运维上实现自动化是很容易的,对于有状态的现状比较难脱离运维人员来管理,即使是k8s在使用上也暂时没有成熟的工具来实现。

总之:对于有状态的应用的数据,不使用存储卷,只能放在容器本地,效率比较低,而导致一个很严重问题就是无法迁移使用,而且随着容器生命周期的停止,还不能把它删除,只能等待下次再启动状态才可以,如果删除了数据就可能没了,因为它的可写层是随着容器的生命周期而存在的,所以只要持久存储数据,存储卷就是必需的。

docker存储卷难度:对于docker存储卷运行起来并不太麻烦,如果不自己借助额外的体系来维护,它本身并没有这么强大,因为docker存储卷是使用其所在的宿主机上的本地文件系统目录,也就是宿主机有一块磁盘,这块磁盘并没有共享给其他的docker主要,然后容器所使用的目录,只是关联到宿主机磁盘上的某个目录而已,也就是容器在这宿主机上停止或删除,是可以重新再创建的,但是不能调度到其他的主机上,这也是docker本身没有解决的问题,所以docker存储卷默认就是docker所在主机的本地,但是自己搭建一个共享的NFS来存储docker存储的数据,也可以实现,但是这个过程强依赖于运维人员的能力。

关闭并重启容器,其数据不受影响,但是删除docker容器,则其更改将会全部丢失

存在的问题

存储于联合文件系统中,不易于宿主机访问

容器间数据共享不便

删除容器其数据会丢失

解决方案:卷

卷是容器上一个或多个"目录“,此类目录可绕过联合文件系统,与宿主机上的某目录绑定(关联)

volume于容器初始化之时会创建,由baseimage提供的卷中的数据会于此期间完成复制

volume的初意是独立于容器的生命周期实现数据持久化,因此删除容器之时既不会删除卷,也不会对哪怕未被引用的卷做垃圾回收操作

卷为docker提供了独立于容器的数据管理机制

可以把“镜像”想像成静态文件,例如“程序”,把卷类比为动态内容,例如“数据”,于是,镜像可以重用,而卷可以共享

卷实现了“程序(镜像)"和”数据(卷)“分离,以及”程序(镜像)“和"制作镜像的主机”分离,用记制作镜像时无须考虑镜像运行在容器所在的主机的环境

描述:有了存储卷,如果写在/上,还是存在联合挂载文件系统中,如果要写到卷上,就会写到宿主机关联的目录上,程序运行过程生成的临时数据会写到tmp目录中,也就会在容器的可写层中存储,随着容器被删除而删除,并没太大的影响,只有关键型的数据才会保存在存储卷上。

Volumetypes

Docker有两种类型的卷,每种类型都在容器中存在一个挂载点,但其在宿主机上位置有所不同:

绑定挂载卷:在宿主机上的路径要人工的指定一个特定的路径,在容器中也需要指定一个特定的路径,两个已知的路径建立关联关系

docker管理卷:只需要在容器内指定容器的挂载点是什么,而被绑定宿主机下的那个目录,是由容器引擎daemon自行创建一个空的目录,或者使用一个已经存在的目录,与存储卷建立存储关系,这种方式极大解脱用户在使用卷时的耦合关系,缺陷是用户无法指定那些使用目录,临时存储比较适合

docker专门提供了volume子命令来操作数据卷:

create创建数据卷inspect显示数据卷的详细信息ls列出所有的数据卷prune删除所有未使用的volumes,并且有-f选项rm删除一个或多个未使用的volumes,并且有-f选项2.7.3.4使用mount语法挂载数据卷使用--volume(-v)选项来挂载数据卷,现在docker提供了更强大的--mount选项来管理数据卷。mount选项可以通过逗号分隔的多个键值对一次提供多个配置项,因此mount选项可以提供比volume选项更详细的配置。使用mount选项的常用配置如下:

如果挂载一个非空的数据卷到容器中的一个目录中,那么容器中的目录中会显示数据卷中的数据。如果原来容器中的目录中有数据,那么这些原始数据会被隐藏掉。

这两个规则都非常重要:

灵活利用第一个规则可以帮助我们初始化数据卷中的内容。

掌握第二个规则可以保证挂载数据卷后的数据总是你期望的结果

为dockerrun命令使用-v选项可使用volume

[root@docker~]#dockerrun--namevolume02-it--rm-v/data/volumes/volume02:/data/web/htmbusybox/#cat/data/web/htm/index.html

hellozhujingxing

[root@dockervolume02]#dockerrun--namevolume03-it--rm-v/data/volumes/volume02:/databusybox/#cat/data/index.html

hellozhujingxing

[root@docker~]#cat/data/volumes/volume02/index.html

hellozhujingxing

场景:需要多个容器同进使用多个卷,卷在那里写每次初始化时都要使用-v来指定,如果不想记录这个路径,docker还支持复制其他的存储卷路径

让它们共享网络名称空间中的uts,net,ipc,还可以共享存储卷,ngInx处理静态,tomcat处理动态的,在同一个目录下,使用存储卷来解决这个问题,这种组织方式使用构建应用。

之前做法:启动容器dockerexec连进容器,在内部执行vi,再reload重启

另外一种方式:假设我们把它对应的那个配置文件的路径做存储卷,从我们宿主机上加载文件,在宿主机上进行编辑,也能让它立即生效,启动容器之前先把它编辑好(容器启动之前,我们事先找一目录把配置文件准备好,然后启动容器时,把容器内的应用程序默认加载配置文件的路径与宿主机上的目录进行建立关联关系,然后去启动容器,也能加载到在宿主机上定制的配置文件)

缺点:我们在宿主上做的编辑,能不能让它立即生效呢?比如我们启动以后发现,有些参数还是需要改,改完以后依然需要重载才能生效。

还有一种方式:自制镜像

基于容器:先启动起来,交互式连入进来,做修改,改完以后,改的结果一定时保存在最上层的可写层的。这个时候我们把可写层保存在一个新镜像中,而后,我们再去创建容器时,根据我们自己所创建的镜像来使用。

缺点:做的镜像也是直接把文件直接备进镜像中的,直接写死在镜像中的就是,如果我们想在改,还是改不了,运行过程当中去修改配置的需求可能对于做运维来讲,变更不就是日常操作吗?很多时候,也有可能需要随时进行修改。那依然解决不了问题。而且这种备进镜像的设计方式,最悲惨的地方在于:一次更新,维护复杂。环境简单可以使用。

基于dockerfile:dockerfile,相当于是一个文档,客户可以基于dockerfile生成新的容器。dockerfile仅仅是用来制作镜像的源码文件,是构建容器过程中的指令,docker能够读取dockerfile的指定进行自动构建容器,基于dockerfile制作镜像,每一个指令都会创建一个镜像层,即镜像都是多层叠加而成,因此,层越多,效率越低,创建镜像,层越少越好。因此能在一个指令完成的动作尽量通过一个指令定义。

首先需要有一个制作镜像的目录,该目录下有个文件,名称必须为Dockerfile,Dockerfile有指定的格式,#号开头为注释,,指定默认用大写字母来表示,以区分指令和参数,dockerbuild读取Dockerfile是按顺序依次Dockerfile里的配置,且第一条非注释指令必须是FROM开头,表示基于哪个基础镜像来构建新镜像。可以根据已存在的任意镜像来制作新镜像。

Dockerfile可以使用环境变量,用ENV来定义环境变量,变量名支持bash的变量替换,如${variable:-word},表示如果变量值存在,就使用原来的变量,变量为空时,就使用word的值作为变量的值,一般使用这个表示法。${variable:+word},表示如果变量存在了,不是空值,那么变量将会被赋予为word对应的值,如果变量为空,那么依旧是空值。

[root@docker~]#echo${NAME:-zhujingxing}zhujingxing[root@docker~]#NAME=zisefeizhu[root@docker~]#echo${NAME:-zhujingxing}zisefeizhu[root@docker~]#echo${NAME:+zhujingxing}zhujingxing[root@docker~]#unsetNAME[root@docker~]#echo${NAME:+zhujingxing}[root@docker~]#

dockerignorefile:在docker发送上下文给dockerdaemon之前,会寻找.dockerignorefile文件,去排除一些不需要的文件,或者很大的文件,不把这些文件发送给dockerdaemon,提升效率。而如果之后需要用到,则可以使用ADD或者COPY把他们放进image中。

Dockerfile整体就两类语句组成:

#Comment注释信息

Instructionarguments指令参数,一行一个指令。

Dockerfile文件名首字母必须大写。

Dockerfile指令不区分大小写,但是为方便和参数做区分,通常指令使用大写字母。

Dockerfile中指令按顺序从上至下依次执行。

Dockerfile中第一个非注释行必须是FROM指令,用来指定制作当前镜像依据的是哪个基础镜像。

Dockerfile中需要调用的文件必须跟Dockerfile文件在同一目录下,或者在其子目录下,父目录或者其它路径无效

为了减少镜像的大小,减少依赖,仅安装需要的软件包。

一个容器只做一件事。解耦复杂的应用,分成多个容器,而不是所有东西都放在一个容器内运行。如一个PythonWeb应用,可能需要Server、DB、Cache、MQ、Log等几个容器。一个更加极端的说法:Oneprocesspercontainer。

减少镜像的图层。不要多个Label、ENV等标签。

对续行的参数按照字母表排序,特别是使用apt-getinstall-y安装包的时候。

使用构建缓存。如果不想使用缓存,可以在构建的时候使用参数--no-cache=true来强制重新生成中间镜像。

细心的道友一定发现,我在第一章的集群搭建中并没有部署仓库集群。原因是:我个人想把镜像仓库做成kubernetes集群的pod,这样的话必然牵扯到后端存储。而存储是我要单独用一章来讲的。为了docker章节的完整性,不得不阐述镜像仓库,但这并不代表我集群中的仓库部署。仅仅是为了学习,为了章节完整性。

前面的章节有讲过公有仓库的使用,如Docker.Hub和阿里云镜像仓库。这种方式有明显的缺陷:push和pull的速度很慢,假若实际环境有上百台机器,那需要多大带宽才能hold住。所以多数时候还是需要创建自己的私有仓库。工作中的生产环境主机选择基本有三种:自建机房、IDC机房托管和阿里公有云,前两种情况最好是将docker私有仓库建立在局域网内,而第三种使用阿里云镜像仓库无非是最恰当的选择。

搭建私有仓库有两种种方式:

使用Docker官方提供的docker-distribution。可以通过dockercontainer或者yum的方式安装。dockercontainer的方式需要把镜像存储目录挂载到宿主机的某目录下,防止容器意外中止或者删除导致仓库不可用。此种registry功能比较单一。

使用harbor,这是VMware基于docker-distribution二次开发的软件,现在已经加入了CNCF。功能强大,界面美观。值得一提的是harbor支持中文,是不是很happy,道友们。因为二次开发此软件的主力是VMware中国区团队。另外,原本的harbor部署是非常困难的,因此harbor官网直接把harbor做成了可以在容器中运行的应用,且harbor容器启动时要依赖于其它一些容器协同工作,所以它在部署和使用时需要用到docker的单机编排工具dockercompose。

在生产实际中,现阶段的镜像仓库还是harbor占据明显优势,so,这节,我将直接阐述harbor仓库。

Harbor的所有组件都在Docker中部署,所以Harbor可使用DockerCompose快速部署。(由于Harbor是基于DockerRegistryV2版本,所以docker版本至少1.10.0、docker-compose版本至少1.6.0)

组件

(1)proxy:nginx前端代理,分发前端页面ui访问和镜像上传和下载流量;

(2)ui:提供前端页面和后端API,底层使用mysql数据库;

(3)registry:镜像仓库,负责存储镜像文件,当镜像上传完毕后通过hook通知ui创建repository,registry的token认证也是通过ui组件完成;

(4)adminserver是系统的配置管理中心附带检查存储用量,ui和jobserver启动时候回需要加载adminserver的配置

(5)jobsevice:负责镜像复制工作,和registry通信,从一个registrypull镜像然后push到另一个registry,并记录job_log;

(6)log:日志汇总组件,通过docker的log-driver把日志汇总到一起。

Harbor介绍

Harbor是一个用于存储和分发Docker镜像的企业级Registry服务器,通过添加一些企业必需的功能特性,例如安全、标识和管理等,扩展了开源DockerDistribution。作为一个企业级私有Registry服务器,Harbor提供了更好的性能和安全。提升用户使用Registry构建和运行环境传输镜像的效率。Harbor支持安装在多个Registry节点的镜像资源复制,镜像全部保存在私有Registry中,确保数据和知识产权在公司内部网络中管控。另外,Harbor也提供了高级的安全特性,诸如用户管理,访问控制和活动审计等。

Harbor特性

基于角色的访问控制:用户与Docker镜像仓库通过“项目”进行组织管理,一个用户可以对多个镜像仓库在同一命名空间(project)里有不同的权限。

镜像复制:镜像可以在多个Registry实例中复制(同步)。尤其适合于负载均衡,高可用,混合云和多云的场景。

图形化用户界面:用户可以通过浏览器来浏览,检索当前Docker镜像仓库,管理项目和命名空间。

AD/LDAP支持:Harbor可以集成企业内部已有的AD/LDAP,用于鉴权认证管理。

审计管理:所有针对镜像仓库的操作都可以被记录追溯,用于审计管理。

国际化:已拥有英文、中文、德文、日文和俄文的本地化版本。更多的语言将会添加进来。

RESTfulAPI:RESTfulAPI提供给管理员对于Harbor更多的操控,使得与其它管理软件集成变得更容易。

部署简单:提供在线和离线两种安装工具,也可以安装到vSphere平台(OVA方式)虚拟设备。

接下来我们开始创建私有仓库。

先创建一个普通的账户

切换上面的普通账户,新建立一个私有项目

推送镜像到baseimages项目中

到这私有仓库也就搭建完成了,也可以在/data目录下查看数据

[root@docker02harbor]#ll/data/registry/docker/registry/v2/repositories/baseimages/总用量0drwxr-xr-x51000010000558月321:35busy如果要对harbor服务做一些操作,需要使用docker-compose命令

#其实前面的./install.sh也是使用的docker-composecreate和docker-composestart命令启动的harbor。注意,命令执行需要再harbor的目录下,否则会报错找不到配置文件。[root@docker02harbor]#docker-compose--help[root@docker02~]#docker-composepauseERROR:Can'tfindasuitableconfigurationfileinthisdirectoryoranyparent.AreyouintherightdirectorySupportedfilenames:docker-compose.yml,docker-compose.yaml[root@docker02~]#cd-/usr/local/harbor[root@docker2harbor]#docker-composepausePausingharbor-log...donePausingredis...donePausingharbor-db...donePausingregistry...donePausingregistryctl...donePausingharbor-core...donePausingharbor-portal...donePausingharbor-jobservice...donePausingnginx...done#开机自启动harbor[root@docker02harbor]#cp/etc/rc.d/rc.local{,.bak}[root@docker02harbor]#vim/etc/rc.d/rc.local[root@docker02harbor]#diff/etc/rc.d/rc.local{,.bak}14,15d13<#harbor

在此章节,我只是围绕harbor仓库进行阐述,详细了解请看我提供的链接。

在此章节,我并没有阐述harbor仓库的高可用,安全等问题,这算几处坑吧,在kubernetes编排工具章,我会进行填补。

默认情况下,容器没有资源限制,可以使用主机内核调度程序允许的尽可能多的给定资源。

Docker同LXC一样,其对资源的隔离和管控是以Linux内核的namespaces和cgroup为基础。Docker的资源隔离使用了Linux内核Kernel中的Namespaces功能来实现,隔离的对象包括:主机名与域名、进程编号、网络设备、文件系统的挂载点等,namespace中的IPC隔离docker并未使用,docker中使用TCP替代IPC。

在使用Namespaces隔离资源的同时,Docker使用了Linux内核Kernel提供的cgroup来对Container使用的CPU、内存、磁盘IO资源进行配额管控。换句话说,在docker的容器启动参数中,像--cpu*、--memory*和--blkio*的设置,实际上就是设置cgroup的相对应cpu子系统、内存子系统、磁盘IO子系统的配额控制文件,只不过这个修改配额控制文件的过程是docker实例代替我们做掉罢了。因此,我们完全可以直接修改docker容器所对应的cgroup子系统中的配额控制文件来达到控制docker容器资源配额的同样目的。

内存风险

不允许容器消耗宿主机太多的内存是非常重要的。在Linux主机上,如果内核检测到没有足够的内存来执行重要的系统功能,它会抛出OOME或OutofMemory异常,并开始终止进程以释放内存。任何进程都会被杀死,包括Docker和其他重要的应用程序。如果杀错进程,可能导致整个系统瘫痪。

Docker通过调整Dockerdaemon上的OOM优先级来降低这些风险,以便它比系统上的其他进程更不可能被杀死。容器上的OOM优先级未调整,这使得单个容器被杀死的可能性比Dockerdaemon或其他系统进程被杀死的可能性更大。你不应试图通过在daemon或容器上手动设置--oom-score-adj到极端负数,或通过在容器上设置--oom-kill-disable来绕过这些安全措施。

可以通过以下方式降低OOME导致系统不稳定的风险:

确保应用程序仅在具有足够资源的主机上运行;

限制容器可以使用的内存,如下所述;

在Docker主机上配置Swap时要小心,Swap比内存更慢且性能更低,但可以提供缓冲以防止系统内存耗尽;

考虑将Container转换部署为Service,并使用服务级别约束和节点标签来确保应用程序仅在具有足够内存的主机上运行。

限制容器内存

下述选项中的大多数采用正整数,后跟b/k/m/g的后缀,代表单位:字节/千字节/兆字节/千兆字节。

关于--memory-swap

--memory-swap是一个修饰符标志,只有在设置了--memory时才有意义。使用swap允许容器在容器耗尽所有可用的RAM时,将多余的内存需求写入磁盘。对于经常将内存交换到磁盘的应用程序,性能会受到影响。

它的设置会产生复杂的影响:

如果--memory-swap设置为正整数,则必须设置--memory和--memory-swap。--memory-swap表示可以使用的memory和swap总量,--memory控制no-swap的用量。所以,如果设置--memory="300m"和--memory-swap="1g",容器可以使用300mmemory和700m(1g-300m)swap。

如果--memory-swap设置为0,该设置被忽略,该值被视为未设置。

如果--memory-swap的值等于--memory的值,并且--memory设置为正整数,则容器无权访问swap。这是因为--memory-swap是可以使用组合的Memory和Swap,而--memory只是可以使用的Memory。

如果--memory-swap不设置,并且--memory设置了值,容器可以使用--memory两倍的Swap(如果主机容器配置了Swap)。

示例:设置--memory="300m"并且不设置--memory-swap,容器可以使用300mmemory和600mswap。

如果--memory-swap设置为-1,允许容器无限制使用Swap。

在容器内部,像free等工具报告的是主机的可用Swap,而不是容器内可用的。不要依赖于free或类似工具的输出来确定是否存在Swap。

关于--memory-swappiness

值为0时,关闭匿名页交换。

值为100时,将所有匿名页设置为可交换。

默认情况下,如果不设置--memory-swappiness,该值从主机继承。

关于--kernel-memory

内核内存限制是就分配给容器的总内存而言的,考虑一下方案:

无限内存,无限内核内存:这是默认行为。

无限内存,有限内核内存:当所有cgroup所需的内存量大于主机上实际存在的内存量时,它是合适的。可以将内核内存配置为永远不会超过主机上可用的内存,而需求更多内存的容器需要等待它。

有限内存,无限内核内存:整体内存有限,但内核内存不是。

当你打开任何内核内存限制时,主机会根据每个进程跟踪“高水位线”统计信息,因此你可以跟踪哪些进程正在使用多余的内存。通过查看主机上的/proc//status,可以在每个进程中看到这一点。

默认情况下,每个容器对主机CPU周期的访问权限是不受限制的,你可以设置各种约束来限制给定容器访问主机的CPU周期。大多数用户使用和配置默认CFS调度程序。在Docker1.13及更高版本中,还可以配置实时调度程序。

配置默认CFS调度程序

CFS是用于普通Linux进程的Linux内核CPU调度程序。通过以下设置,可以控制容器的CPU资源访问量,使用这些设置时,Docker会修改主机上容器的cgroup的设置。

示例:如果你有1个CPU,则以下每个命令都会保证容器每秒最多占CPU的50%。

Docker1.13或更高版本:

[root@docker~]#dockerrun-it--cpus=".5"busybox配置实时调度程序

在Docker1.13或更高版本,你可以配置容器使用实时调度程序。在配置Dockerdaemon或配置容器之前,需要确保正确配置主机的内核。

警告:CPU调度和优先级是高级内核级功能,大多数用户不需要从默认值更改这些值,错误地设置这些值可能会导致主机系统变得不稳定或无法使用。

配置主机机器的内核

通过运行zcat/proc/config.gz|grepCONFIG_RT_GROUP_SCHED验证是否在Linux内核中启用了CONFIG_RT_GROUP_SCHED,或者检查是否存在文件/sys/fs/cgroup/cpu.rt_runtime_us。有关配置内核实时调度程序的教程,请参阅操作系统的文档。

配置DOCKERDAEMON

配置个别容器

使用dockerrun启动容器时,可以传递多个参数来控制容器的CPU优先级。有关适当值的信息,请参阅操作系统的文档或ulimit命令。

示例:

[root@docker~]#dockerrun-it--cpu-rt-runtime=95000--ulimitrtprio=99--cap-add=sys_nicedebian:jessie注:如果未正确配置内核或DockerDaemon,则会发生错误。

对于docker仓库,我并没有详细阐述,甚至是一笔带过的,原因我之前已经有讲,我期待的是将重点、难点、复杂点都放在Kubernetes章节。

又是一星期,现在是2019年8月4号12点14分。到此为止docker章第一版算是基本完成。这个星期我熬了两天晚上的夜,倒不是因为写docker,主要是go语言有点难搞哦。7月份工作稳住,8月份该冲刺一波了。未来可期,应约而至,不忘初心,放得本心。

THE END
1.电子商务后台管理亚马逊开店后台亚马逊后台排名摘要近日,全球无卡支付网(cardnotpresent.com)联合国际支付方案提供商Payvision公司,对投资方、商业服务提供方、独立销售组织方、支付服务提供方以及在线商人进行牛津小马哥 · 2021-08-02 PayPal发布"云建站"及中文PassPort网站 2014年9月5日,全球领先的在线支付平台PayPal,在广州举办PayPal 2014中国外贸电子商务大会https://www.cifnews.com/key/sprwh
2.代理加权分销系统后台功能开发及案例代理后台定制开发代理加权分销系统,代理商管理系统 所在地 广州市天河区棠东毓南路11号 手机 15918648642 联系人 路诗 请说明来自顺企网,优惠更多 请卖家联系我产品详细介绍 运输物流对线上线下有效连接,新零售模式的实施和创新也要有新物流支撑,新物流的创新和发展有其不同的方向。比如在既定服务水平条件下要尽量减少运输费用,要http://guangzhou.11467.com/info/9442534.htm
3.广告推广自用搞钱,分享赚钱,推广APP拉新小程序,翻身出人头【 广告推广】自用搞钱,分享赚钱,推广APP拉新小程序,翻身出人头-万事通便民信息平台https://weibanglm.com/fenlei/info-1-18122.html
4.企业办公软件SaaS软件(系统)服务企业服务GrowingIO客户数据平台(CDP)提供数据集成、数据管理、用户标签、单用户360度视图以及群用户画像功能,帮助企业高效集成,打破企业内数据孤岛,智能处理推动数据结构体系化,并为上层应用使用提供高效的计算查询引擎。 GrowingIO是国内领先的一站式数据增长引擎整体方案服务商。创立于2015年,GrowingIO以数据智能分析能力为核心,通过https://36kr.com/project-33/
5.热血重燃!子潇网络老马自助下单(网络老马自助下单选择适合的服务套餐,根据您的需求选择合适的优化项目。 支付费用,并提交您的订单。 完成以上步骤后,子潇网络老马团队会尽快处理您的订单,并开始为您进行网站优化。您可以在后台跟踪订单进度,并随时与团队进行沟通。 子潇网络老马自助下单的效果 子潇网络老马自助下单经过许多客户的验证,已经取得了显著的效果。以下是一http://awkjkt.jbwh668.com/post/16605.html
6.X管家员工任务万象帮助文档1.权限设置:分别给管理后台及B端小程序按需分配好员工任务相关权限 X管家-员工任务 X管家-员工任务 2.任务设置:按需添加任务,并并更新基础设置(初始分数设置后下一个周期才会生效。) X管家-员工任务 X管家-员工任务 二、任务消息接收设置 1.公众号平台--添加类目:①生活服务>休闲娱乐②商业服务>企业管理 设置界https://help.sicent.com/index.php?doc-view-1602.html
7.企业服务大师源码后台代码一键更新 产品价格: 加密版单开独立部署:699元【授权域名+授权IP地址】(适合自用运营) 加密版无限开独立部署:2700元【仅授权IP地址,在授权IP服务器下可无限搭建站点,可以绑定不同的域名】(适合自用运营+销售给其他人) 单开续费价:200元/年,首年免费送; https://blog.csdn.net/u011092458/article/details/144358080
8.电商供应链ERP管理系统PHP+Uniapp货源支持自营和第三方供应商的货源管理。 库存信息统一管理。 多渠道管理 汇集多个销售平台的订单。 整合订单发货,提高发货效率。 税费核算 保税商品按照商品计税规格值准确计算税费。 自动处理税费相关事务。 售后管理 渠道订单支持手动申请售后。 提供完善的售后服务流程。 https://www.163.com/dy/article/JJ0EN03F0556AT86.html
9.网站建设小程序开发APP开发办公系统开发源码交付5、 技术开发(前段制作实现页面的动态展示,后端工程师进行后台功能搭建、数据对接与开发) 6、 项目测试(测试工程师进行全版本测试,功能测、性能接口测试,项目移交给客户测试,试运营) 7、 项目交付(客户验收合格正式上线运营,我们交付源代码和有关资料,免费一年技术支持,售后服务) http://www.paihang8.com/sell/1-61g2pgqd12c1.htm
10.最新版牛总管下载,最新版牛总管下载,全面解析与体验分享最新版牛总管在功能、界面设计、用户体验等方面都表现出色,作为一款综合性软件,牛总管满足了用户在通讯、管理、生活服务等方面的需求,为用户带来便利,牛总管还注重用户的安全与隐私保护,让用户放心使用,建议用户从官方渠道下载牛总管,体验其强大的功能与优质的服务。 http://www.hongchenzaidai.com/post/521.html
11.商城系统商家助手支持会员管理功能说明羽薇网专注于互联网营销一站式助力服务,搭建官网,打造商城,微信小程序,百度小程序,H5微游戏,H5微场景,企业邮箱,公众号助手,设计定制服务,电商运营等多样化的服务。公司致力于服务企业与品牌有条不紊实现互联网布局,开拓全网络渠道是我们发展的愿景!http://www.yw-jz.com/h-nd-3423.html
12.2019年大汇总,第三方和第四方支付公司有哪些?第四方支付公司是指通过第三方支付的授权的服务商,比如获得微信、支付宝授权的支付服务商,有能力和资格对支付通道进行集成的技术型服务企业。目前在全国范围内第四方支付公司的数量高达3万多家,而在这3万多家的微信支付服务商中,规模大、老品牌的支付服务商并不多,接下来大家做个比较全面的总结。 http://www.498.net/gsxw/article-1468.html
13.如何以编程方式在手机(Android)上随时随地从服务(后台)截取屏幕在手机(Android)上以编程方式随时随地从服务(后台)截取屏幕,可以通过以下步骤实现: 1. 获取屏幕截图权限:在AndroidManifest.xml文件中添加`<uses-permissiohttps://cloud.tencent.com/developer/information/%E5%A6%82%E4%BD%95%E4%BB%A5%E7%BC%96%E7%A8%8B%E6%96%B9%E5%BC%8F%E5%9C%A8%E6%89%8B%E6%9C%BA(Android)%E4%B8%8A%E9%9A%8F%E6%97%B6%E9%9A%8F%E5%9C%B0%E4%BB%8E%E6%9C%8D%E5%8A%A1(%E5%90%8E%E5%8F%B0)%E6%88%AA%E5%8F%96%E5%B1%8F%E5%B9%95%EF%BC%9F
14.小马哥说服裁判别给对手红牌以免毁了比赛阿根廷U20最终4球大胜lol比赛怎么竞猜压钱,pop赢博真人是谁,bet9官网登录服务中心,FG电子app地址首页,e尊国际官网网址BET9官方网站App,澳门赌破产的老板,爱游戏app官网ios,澳博足球投注站官网,XPJ线上娱乐NT老虎机正规网络,toto彩票下载,爱博国际开户开户,bjl第三张补牌规则,tp官方正版下载i8cp彩票线路,澳门赌场真人官网,bb体育官网下载http://m.91xhzs.com/20241213/96678465.html
15.马化腾:纯做软件与服务会失去制高点,谈到人工智能就焦虑雷峰网马化腾:过去两年我们谈“互联网+”,更多地是讲连接服务,今年开始,延伸到互联网+制造业、互联网+工业方面,不仅是从传统的手机、电视机或者智能家电和物联网,(包括现在的共享单车,里面其实也是物联网),也包括运营商的窄带物联网,就是 Narrow Band IOT 的技术,也在铺这个网络。 https://www.leiphone.com/news/201703/7UcmtrrBbO4WYk8g.html
16.广汽集团董事长曾庆洪与小马哥讨论推车载版微信,转型出行服务商美国一旦对中国汽车加征25%的关税,对准备进入美国市场的广汽传祺有影响。不过,广汽传祺还是按计划将于明年年底进入美国市场。 11月29日,广汽集团(601238.SH)董事长曾庆洪在2018年《财富》全球科技论坛上谈到,该公司加快朝移动出行服务商转型以及加快国际化步伐。 https://www.yicai.com/news/100069896.html
17.腾讯市值达2790亿美元排名全球第十;三星侵权案成立赔华为8000万元腾讯还在继续创造纪录,小马哥又笑了 艾瑞巴蒂,早上好!Jean Paul Gautier 是法国著名的服装设计大师,静静觉得这件设计还挺有意思的,它把非洲的tattoo和中国的绣片元素结合了在一起,有人说,它更像中国的珐琅彩,还有人说,有点敦煌的气息你觉得呢?PS:然而,静静并不是一个衣服控哦,吼吼~ 好了,话不https://www.iheima.com/zixun/2017/0407/162436.shtml
18.直连商户微信支付报“特约子商户商户号未授权服务商的产品权限小马哥内部资料 商家后台 服务商后台FAQ 商家管理 商户入件审核 通道问题 间1网商通道 间2乐刷通道 间3新大陆通道 直1支付宝通道 直2微信通道 直2微信通道介绍 直2微信FAQ 直连微信API证书到期怎么续期? 直连商户微信支付报“特约子商户商户号未授权服务商的产品权限” 不同类型商户入件 审核报错 如何查看入https://www.kancloud.cn/youyunxiaomage/aduer_all/1459446
19.罗源县市场监督管理局2020年11月份餐饮服务与食品销售许可情况5 罗源县滨海新城小马哥餐饮店 92350123MA33CERA2N 郑冬英 17***088 福建省福州市罗源县滨海新城滨海商业街34-1、34-2号 JY23501230033801 2020-11-27 2025-11-26 6 罗源县滨海新城找茶餐饮店 92350123MA2XUYRT68 林孔鸿 18***937 福建省福州市罗源县世纪金源购物中心B区二层B2-158 JY23501230003545http://www.luoyuan.gov.cn/xjwz/zwgk/zfxxgkzdgz/spypaq/spypscjyba/202012/t20201208_3717775.htm
20.北海市小马哥汽车维修服务有限公司(广西壮族自治区北海市合浦县北海市小马哥汽车维修服务有限公司的地址为广西壮族自治区北海市合浦县廉州镇廉南社区吴屋岭社王旁,位于广西北海合浦县,广西壮族自治区北海合浦县廉南社区,广西北海合浦县廉南社区居委会,注册于2020年6月9日,法人是马宗骏,主要业务范围为一般项目:汽车修理与维护,汽https://gongshang.mingluji.com/guangxi/name/%E5%8C%97%E6%B5%B7%E5%B8%82%E5%B0%8F%E9%A9%AC%E5%93%A5%E6%B1%BD%E8%BD%A6%E7%BB%B4%E4%BF%AE%E6%9C%8D%E5%8A%A1%E6%9C%89%E9%99%90%E5%85%AC%E5%8F%B8