Overview

本文提出了一个通用的基于插件的Linux容器网络解决方案,容器网络接口,CNI。它脱胎于旨在满足大多数rtk网络设计的rtk Networking Proposal。

首先,我们对如下两个名词进行具体的定义:

  • container可以认为是与Linux network namespace是同义的。而一个network namespace具体对应什么,则与具体的容器运行时有关:例如,在rtk中,每个pod运行在一个单独的network namespace中。但是在docker中,network namespace存在于每个独立的Docker容器中
  • network代表了一组可以独立寻址并且可以互相交互的实体。这些实体既可以是一个单独的容器(如上所述),一台机器,或者其他什么网络设备(例如,一台路由器)。container可以加入一个或多个network,也可以从一个或多个network中移除。

本文旨在说明容器运行时和插件之间的接口。同时,可能还有些众所周知的字段,runtime也想传递给底层的插件,不过这些内容并不在本文中进行描述。

General consideration

首先容器运行时需要为container新建一个network namespace。之后,它需要决定该container属于哪些network,对于每个network还需要确定对应执行哪些插件。network configuration是以JSON格式存在的,很容易被存储在文件中。配置中需要包含一些必须的字段,例如,"name",“type”以及相应的插件必须的字段。同时,network configuration允许其中的字段在不同的调用间发生改变。因此,存在一个可选的"args"字段用于存放异变的信息。最后,容器运行时通过顺序地调用相应的插件来创建相应的network。当container的生命周期结束时,运行时再以相反的顺序调用插件,将它们从networks中移除。

CNI Plugin

Overview

每个CNI插件都是以一个能被容器管理系统(比如,rkt或者Docker)调用的可执行文件的形式存在的。

CNI插件负责将一个network interface插入container network namespace(比如,veth pair的其中一端)并且在宿主机中做一些必要的配置(例如将veth的另一端加入bridge中)。接着通过调用适当的IPAM插件,将IP赋给interface并且设置路由。

Parameters

CNI插件支持如下三种操作:

  • 将container加入network(Add):

    • Parameters:

      • Version. 调用者使用的CNI 配置的版本信息
      • Container ID. 这个字段是可选的,但是建议使用,在容器活着的时候要求该字段全局唯一的。比如,存在IPAM的环境可能会要求每个container都分配一个独立的ID,这样每一个IP的分配都能和一个特定的容器相关联。例如,在appc implementations中,container ID其实就是pod ID
      • Network namespace path. 这个字段表示要加入的network namespace的路径。例如,/proc/[pid]/ns/net或者对于该目录的bind-mount/link。
      • Network configuration. 这是一个JSON文件用于描述container可以加入的network,具体内容在下文中描述
      • Extra arguments. 该字段提供了可选的机制,从而允许基于每个容器进行CNI插件的简单配置
      • Name of the interface inside the container. 该字段提供了在container (network namespace)中的interface的名字;因此,它也必须符合Linux对于网络命名的限制
    • Result:
      • Interface list. 根据插件的不同,这个字段可以包括sandbox (container or hypervisor) interface的name,以及host interface的name,每个interface的hardware address,以及interface所在的sandbox(如果存在的话)的信息。
      • IP configuration assigned to each interface. IPv4和/或者IPv6地址,gateways以及为sandbox或host interfaces中添加的路由
      • DNS inormation. 包含nameservers,domains,search domains和options的DNS information的字典
  • 将container从network中删除(Delete):
    • Parameter:

      • Version. 调用者使用的CNI 配置的版本信息
      • ContainerID. 定义同上
      • Network namespace path. 定义同上
      • Network configuration. 定义同上
      • Extra argument. 定义同上
      • Name of the interface inside the container. 定义同上
  • 版本信息
    • Parameter: 无
    • Result: 返回插件支持的所有CNI版本  
{
"cniVersion": "0.3.1", // the version of the CNI spec in use for this output
"supportedVersions": [ "0.1.0", "0.2.0", "0.3.0", "0.3.1" ] // the list of CNI spec versions that this plugin supports
}

  

最终executable command-line API会以network的type作为名字去调用相应的插件。它首先会在一系列预先定义好的目录中查找该可执行文件。一旦找到,它就会用以下的环境变量作为参数去调用该可执行文件:

  • CNI_COMMAND: 表示进行的操作;ADD, DEL或者VERSION
  • CNI_CONTAINERID: Container ID
  • CNI_NETNS: network namespace文件的路径
  • CNI_IFNAME: 创建的interface的名字,插件必须使用这个名字,否则返回错误
  • CNI_ARGS: 在调用时用户传入的额外的参数,由以分号分割的,字母数字键值对组成,例如,"FOO=BAR;ABC=123"
  • CNI_PATH:用于查找CNI插件的可执行文件的路径列表,在Linux中,路径之间由":"分割,在Windows中用";"分割

以JSON形式存在的network configuration将以stdin的方式进入插件。这意味着它并不和磁盘上某个特定的文件绑定,因此它所包含的信息也能在每次调用之后发生改变

Result

需要注意的是IPAM插件返回的是一个精简的Result结构,对它的描述放在IP Allocation中

当执行的是ADD命令时,如果返回值是0,并且有如下的JSON输出到stdout,那么说明执行成功了。在IPAM插件返回的结果中同样需要对ips和dns字段进行适当的填充,但是interface字段除外,因为IPAM插件并不应该意识到interface的存在

{
"cniVersion": "0.3.1",
"interfaces": [ (this key omitted by IPAM plugins)
{
"name": "<name>",
"mac": "<MAC address>", (required if L2 addresses are meaningful)
"sandbox": "<netns path or hypervisor identifier>" (required for container/hypervisor interfaces, empty/omitted for host interfaces)
}
],
"ips": [
{
"version": "<4-or-6>",
"address": "<ip-and-prefix-in-CIDR>",
"gateway": "<ip-address-of-the-gateway>", (optional)
"interface": <numeric index into 'interfaces' list>
},
...
],
"routes": [ (optional)
{
"dst": "<ip-and-prefix-in-cidr>",
"gw": "<ip-of-next-hop>" (optional)
},
...
]
"dns": {
"nameservers": <list-of-nameservers> (optional)
"domain": <name-of-local-domain> (optional)
"search": <list-of-additional-search-domains> (optional)
"options": <list-of-options> (optional)
}
}

cniVersion以Semantic Version 2.0的格式指定了插件使用的CNI版本。interfaces描述了插件创建的network interfaces。如果指定了CNI_IFNAME,那么插件必须用该名字对sandbox/hypervisor interface进行命名,否则返回错误

  • mac (string):interface的hardware address。如果对于插件来说L2地址是没有意义的,那个该字段是可选的
  • sandbox (string):container/namespace-based environment需要返回sandbox所在的network namespace的完整路径。Hypervisor/VM-based插件需要返回一个唯一的ID,代表新建的interface所在的virtualized sandbox。

ips字段包含了一系列的IP配置信息,详情参见IP well-known structure。dns字段包含了一个由通用的DNS信息组成的字典。详情参见DNS well-known structure。specification中并没有这些信息到底应该被如何使用。例如产生一个/etc/resolv.conf文件插入容器文件系统中,或者在宿主机运行一个DNS forwarder。

如果遇到错误将得到一个非零的返回值以及如下形式的JSON输出:

{
"cniVersion": "0.3.1",
"code": <numeric-error-code>,
"msg": <short-error-message>,
"details": <long-error-message> (optional)
}

cniVersion以Semantic Version 2.0的格式指定了插件使用的CNI版本。Error codes的0-99用于一些众所周知的错误(详情参见Well-known Error Codes)。超过100的值可以用于插件特定的错误。

另外,stderr可以用于输出一些unstructured output,例如logs。

Network Configuration

network configuration以JSON格式进行描述。configuration可以被存储在磁盘中或者通过容器运行时以其他方式产生。接下来是一些比较重要的字段:

  • cniVersion(string):cniVersion以Semantic Version 2.0的格式指定了插件使用的CNI版本
  • name (string):Network name。这应该在整个管理域中都是唯一的
  • type (string):代表了CNI插件可执行文件的文件名
  • args (dictionary):由容器运行时提供的可选的参数。比如,可以将一个由label组成的dictionary传递给CNI插件,通过在args下增加一个labels字段
  • ipMasqs (boolean):可选项(如果插件支持的话)。为network在宿主机创建IP masquerade。这个字段是必须的,如果需要将宿主机作为网关,从而能够路由到容器分配的IP
  • ipam:由特定的IPAM值组成的dictionary
    • type (string):表示IPAM插件的可执行文件的文件名
  • dns:由特定的DNS值组成的dictionary
    • nameservers (list of strings):一系列对network可见的,以优先级顺序排列的DNS nameserver列表。列表中的每一项都包含了一个IPv4或者一个IPv6地址
    • domain (string):用于查找short hostname的本地域
    • search (list of strings):以优先级顺序排列的用于查找short domain的查找域。对于大多数resolver,它的优先级比domain更高
    • options(list of strings):一系列可以被传输给resolver的可选项

插件可能会定义它们自己能接收的额外的字段,但是遇到一个未知的字段可能会产生错误。例外的是args字段,它可以被用于传输一些额外的字段,但可能会被插件忽略

Example configurations

{
"cniVersion": "0.3.1",
"name": "dbnet",
"type": "bridge",
// type (plugin) specific
"bridge": "cni0",
"ipam": {
"type": "host-local",
// ipam specific
"subnet": "10.1.0.0/16",
"gateway": "10.1.0.1"
},
"dns": {
"nameservers": [ "10.1.0.1" ]
}
}

  

{
"cniVersion": "0.3.1",
"name": "pci",
"type": "ovs",
// type (plugin) specific
"bridge": "ovs0",
"vxlanID": 42,
"ipam": {
"type": "dhcp",
"routes": [ { "dst": "10.3.0.0/16" }, { "dst": "10.4.0.0/16" } ]
}
// args may be ignored by plugins
"args": {
"labels" : {
"appVersion" : "1.0"
}
}
}

  

{
"cniVersion": "0.3.1",
"name": "wan",
"type": "macvlan",
// ipam specific
"ipam": {
"type": "dhcp",
"routes": [ { "dst": "10.0.0.0/8", "gw": "10.0.0.1" } ]
},
"dns": {
"nameservers": [ "10.0.0.1" ]
}
}

  

Network Configuration Lists

Network configuration lists能够以指定顺序允许多个CNI插件,并且将每个插件的允许结果传递给下一个插件。列表中包含了一些众所周知的字段以及由一个或多个标准的CNI network configuration组成的列表(如上所示)。

列表以JSON格式描述,可以储存在磁盘中,也可以由容器运行时以其他方式产生。接下来的这些字段是众所周知的并且有对应的含义:

  • cniVersion(string):以Semantic Version 2.0描述的CNI版本,对此整个configuration list以及每个单独的configuraion必须遵从
  • name (string):Network name。这应该在整个管理域中都是唯一的
  • plugins (lists):一系列标准的CNI network configuration dictionary (如上所示)

当执行插件列表时,运行时必须用列表的name和cniVersion字段替代每个network configuraion的name和cniVersion字段。这确保了列表中插件的name和CNI版本都是一致的,从而避免插件之间产生版本冲突。如果插件通过network configuration的capability字段说明它支持某种specific capability,那么运行时必须将capability-based keys以map的形式插入插件的config JSON的runtimeConfig字段中。同时,传给runtimeConfig的key必须和network configuration的capabilities key的名字相同。

对于ADD操作,运行时必须添加一个prevResult字段到下一个插件的configuration JSON中,并且它的内容就是上一个插件的以JSON格式描述的结果。并且每个插件都必须将preResult的内容输出到stdout从而让后续的插件或者运行时可以获取该结果,除非,它们想要修改或限制之前的结果。插件是允许修改或限制全部或者部分的prevResult内容的。然而对于支持包含prevResult的CNI版本的插件,它必须显式地通过,修改或者限制prevResult,但是忽略该字段是不允许的。

同时,运行时必须在同一环境下执行列表中的每个插件

对于DEL操作,运行时必须以相反的顺序执行插件列表

Network Configuration List Error Handling

当在执行插件列表时发生了错误,那么运行时必须停止执行。如果ADD操作执行失败了,当运行时要处理错误时,它需要以和ADD相反的顺序对列表中的插件执行DEL操作,即使其中某些插件在ADD操作中还为被调用。

插件必须完整地执行DEL操作并不报错,即使有些资源缺失了。比如,对于IPAM插件,即使container network namespace 已经不存在了,它仍然会释放IP allocation并且成功返回,除非network namespace对于IPAM特别重要。尽管DHCP会向container network interface发送一个'release' message,但是因为DHCP leases都是有生命周期的,因此release操作并没有那么重要,也就不应该返回错误。另外,对于bridge插件即使container network namespace和/或者container network interface已经不存在了,它也要调用IPAM插件的DEL操作并且删除相应的资源(如果有的话)。

Example network configuration lists

{
"cniVersion": "0.3.1",
"name": "dbnet",
"plugins": [
{
"type": "bridge",
// type (plugin) specific
"bridge": "cni0",
// args may be ignored by plugins
"args": {
"labels" : {
"appVersion" : "1.0"
}
},
"ipam": {
"type": "host-local",
// ipam specific
"subnet": "10.1.0.0/16",
"gateway": "10.1.0.1"
},
"dns": {
"nameservers": [ "10.1.0.1" ]
}
},
{
"type": "tuning",
"sysctl": {
"net.core.somaxconn": "500"
}
}
]
}

  

Network configuration list runtime examples

基于上述的network configuraion list,容器运行时需要执行以下步骤来完成ADD操作。需要注意的是,运行时会将configuration list中的cniVersion和name字段添加到每个插件的configuration中,从而保证列表中所有插件的版本和名字一致。

1、首先以如下格式调用bridge插件

{
"cniVersion": "0.3.1",
"name": "dbnet",
"type": "bridge",
"bridge": "cni0",
"args": {
"labels" : {
"appVersion" : "1.0"
}
},
"ipam": {
"type": "host-local",
// ipam specific
"subnet": "10.1.0.0/16",
"gateway": "10.1.0.1"
},
"dns": {
"nameservers": [ "10.1.0.1" ]
}
}

  

2、接着以如下的JSON调用tuning插件,其中的prevResult字段包含了bridge插件返回的结果

{
"cniVersion": "0.3.1",
"name": "dbnet",
"type": "tuning",
"sysctl": {
"net.core.somaxconn": "500"
},
"prevResult": {
"ips": [
{
"version": "4",
"address": "10.0.0.5/32",
"interface": 0
}
],
"dns": {
"nameservers": [ "10.1.0.1" ]
}
}
}

  

给定同样的network configuraion list,容器运行时会以如下步骤完成DEL操作。需要注意的是,并不需要prevResult字段,因为DEL操作并不返回任何result。另外,插件的执行顺序和ADD是相反的。

1、首先以如下JSON调用tuning插件

{
"cniVersion": "0.3.1",
"name": "dbnet",
"type": "tuning",
"sysctl": {
"net.core.somaxconn": "500"
}
}

  

2、接着以如下JSON调用bridge插件

{
"cniVersion": "0.3.1",
"name": "dbnet",
"type": "bridge",
"bridge": "cni0",
"args": {
"labels" : {
"appVersion" : "1.0"
}
},
"ipam": {
"type": "host-local",
// ipam specific
"subnet": "10.1.0.0/16",
"gateway": "10.1.0.1"
},
"dns": {
"nameservers": [ "10.1.0.1" ]
}
}

  

IP Allocation

作为整个操作的一部分,CNI插件需要给interface分配并维护一个IP地址,并且还要安装一些和该interface有关的必要的路由。这给了CNI插件很大的灵活性同时也给它造成了很大的负担。许多插件需要重复编写多种用户想要的IP管理框架(例如,dhcp, host-local)。为了减轻各个插件的负担,并且将IP管理的功能独立出来,我们定义了第二种插件类型 -- IP Address Management 插件(IPAM插件)。此时,其他插件的任务就是在适当的执行过程中调用相应的IPAM插件。IPAM插件用于确定interface的IP/子网,网关,路由并且将这些信息返回"main" plugin去执行。IPAM插件可以从一个协议(如dhcp)中,或者从本地文件系统存储的数据中,或者network configuration file中的"ipam"字段,或者上述这些方式的组合中获取信息。

IP Address Management (IPAM) Interface

和CNI插件类似,IPAM插件也是以运行可执行文件的方式被调用的。可执行文件将会在一些预先定义的路径列表中查找,通过CNI_PATH指定。IPAM插件将获得所有传输给CNI插件的环境变量,并且和CNI插件一样,IPAM通过stdin获取network configuration

对于ADD命令,如果返回值为0,并且stdout中有如下的JSON格式,说明执行成功

{
"cniVersion": "0.3.1",
"ips": [
{
"version": "<4-or-6>",
"address": "<ip-and-prefix-in-CIDR>",
"gateway": "<ip-address-of-the-gateway>" (optional)
},
...
],
"routes": [ (optional)
{
"dst": "<ip-and-prefix-in-cidr>",
"gw": "<ip-of-next-hop>" (optional)
},
...
]
"dns": {
"nameservers": <list-of-nameservers> (optional)
"domain": <name-of-local-domain> (optional)
"search": <list-of-search-domains> (optional)
"options": <list-of-options> (optional)
}
}

  

与常规的CNI插件不同的是,IPAM插件返回的是简化的Result结构,其中不包括interfaces字段,因为IPAM插件不应该关注它们的父插件配置的interfaces,那些有特殊要求的IPAM插件除外(例如,dhcp IPAM插件)。

ips字段包含了一系列的IP配置信息,详情参见IP well-known structure

dns字段包含了一个由通用的DNS信息组成的字典。详情参见DNS well-known structure

返回的Errors和logs的和CNI插件相同。详情参见CNI Plugin Result

IPAM插件的例子如下:

  • host-local:在一个特定的范围内选择一个未被其他container使用的IP
  • dhcp:使用DHCP协议获取并且维护一个IP的租用。DHCP request会通过刚创建的container interface发送出来,因此相关的network必须支持广播

Notes:

  • 路由应该被配置为0 metric
  • 默认路由应该配置为"0.0.0.0/0"。因为其他的network可能已经配置了默认路由,CNI插件必须能够跳过已有的默认配置

Well-known Structures

IPs

  "ips": [
{
"version": "<4-or-6>",
"address": "<ip-and-prefix-in-CIDR>",
"gateway": "<ip-address-of-the-gateway>", (optional)
"interface": <numeric index into 'interfaces' list> (not required for IPAM plugins)
},
...
]

  

ips字段是一个由插件决定的IP配置列表。每个条目都是都是一个dictionary,描述了一个network interface的IP配置。多个network interface的IP配置和单个interface上的多个IP配置都将以ips列表中的不同条目返回。插件已知的所有特性都要提供,即使并不是严格必须的:

  • version (string):"4"或者"6",代表了条目中IP地址的版本。所有的IP地址和网关地址都要符合相应的版本
  • address (string):CIDR形式的IP地址(例如,"192.168.1.3/24")
  • gateway (string):对应子网的默认网关,如果存在的话。并不要求CNI插件添加任何与网关相关的路由。路由是单独通过routes字段指定的。一个使用该字段的例子是,CNI bridge插件将该地址添加到Linux bridge中,将其作为网关。
  • interface (uint):该值表示,此IP配置需要作用的interface,在返回的结果JSON中的interfaces字段中对应的下标。IPAM插件不能返回这个值,因为它们没有任何关于network interface的信息

Routes

  "routes": [
{
"dst": "<ip-and-prefix-in-cidr>",
"gw": "<ip-of-next-hop>" (optional)
},
...
]

  

每个routes字段由以下内容组成。所有的IP地址在routes中一定要有相同的IP版本,4或者6

  • dst (string):以CIDR描述的目标子网
  • gw (string):网关的IP地址。如果该字段不存在,则假设为默认网关(由CNI插件决定)

DNS

  "dns": {
"nameservers": <list-of-nameservers> (optional)
"domain": <name-of-local-domain> (optional)
"search": <list-of-additional-search-domains> (optional)
"options": <list-of-options> (optional)
}

  

dns字段包含了由一些通用的DNS信息组成的dictionary

  • nameservers (list of strings):一系列对network可见的,以优先级顺序排列的DNS nameserver列表。列表中的每一项都包含了一个IPv4或者一个IPv6地址
  • domain (string):用于查找short hostname的本地域
  • search (list of strings):以优先级顺序排列的用于查找short domain的查找域。对于大多数resolver,它的优先级比domain更高
  • options (list of strings):一系列可以被传输给resolver的可选项

Well-known Error Codes

  • 1 - CNI版本不兼容
  • 2 - network configuration存在不支持的字段。错误信息中必须包含不支持字段的key和value

《CNI specification》翻译的更多相关文章

  1. 《OVN Logical Flows and ovn-trace》翻译

    在本篇文章中,我将解释什么是Logical Flow以及如何使用ovn-trace去更好地理解它们.同时,我也会用一些例子来解释,为什么使用Logical Flow这种抽象模型能让新特性的添加变得出乎 ...

  2. OVN实战---《The OVN Load Balancer》翻译

    Overview 基于前面几篇文章的基础之上,我们接下来将要探索OVN中的load balancingz这一特性.但是在开始之前,我们先来回顾一下上一个lab中创建好的拓扑结构. The lab ne ...

  3. OVN实战---《The OVN Gateway Router》翻译

    Overview 在本文中我将在前文的基础上添加一个OVN gateway router.gateway router将使得lab network能访问我们的overlay network The l ...

  4. ovs ovn 学习资料

    0.A Primer on OVN http://blog.spinhirne.com/2016/09/a-primer-on-ovn.html 1.Open Virtual Networking W ...

  5. OVN实战---《OVN and Containers》翻译

    Overview 在本篇文章中,我们要讨论的是OVN和容器的集成.到本次实验中,我们将会创建一个包含有一对容器的“虚拟机”,这些容器会直接和OVN logical switch相连,并且可以供逻辑网络 ...

  6. OVN实战---《A Primer on OVN》翻译

    overview 在本文中,我们将在三个host之间创建一个简单的二层overlay network.首先,我们来简单看一下,整个系统是怎么工作的.OVN基于分布式的control plane,其中各 ...

  7. OVN架构翻译

    概述 ovn-controller是OVN在虚拟机上的agent,北向连接OVN的南向数据库,学习OVN的配置和状态,并使用虚拟机的状态来填充PN表以及Binding表的Chassis列:南向连接op ...

  8. OVN架构

    原文地址 OVN架构 1.简介 OVN,即Open Virtual Network,是一个支持虚拟网络抽象的系统. OVN补充了OVS的现有功能,增加了对虚拟网络抽象的原生(native)支持,比如虚 ...

  9. 如何借助 OVN 来提高 OVS 在云计算环境中的性能

    众所周知,OpenvSwitch 以其丰富的功能和不错的性能,已经成为 Openstack 部署中最受欢迎的虚拟交换机.由于 Openstack Neutron 的架构引入了一些性能问题,比如 neu ...

  10. OVN入门

    参考链接 如何借助 OVN 来提高 OVS 在云计算环境中的性能 OVN简介 Open vSwitch Documentation OVSDB介绍及在OpenDaylight中的调用 OpenDayl ...

随机推荐

  1. error: Semantic Issue: Interface type cannot be statically allocated

    转自:http://hongmin118.iteye.com/blog/1333524 error: Semantic Issue: Interface type cannot be statical ...

  2. 前端点击删除按钮删除table表格的数据

    table.on('tool(hostTable)', function (obj) { var data = obj.data;//须写 if (obj.event === 'del') { var ...

  3. QT界面 使用QStyledItemDelegate QPainter QStyleOptionViewItem QModelIndex组合实现项的绘制

    QStyledItemDelegate类为来自模型的数据项提供了显示和编辑工具. 当在Qt项视图(例如QTableView)中显示来自模型的数据时,各个项由委托(delegate)绘制.此外,当编辑一 ...

  4. JavaScript学习日志(1)

    javascript用法: 1.HTML中的脚本必须位于<script>与</script>标签之间,可被放置在HTML页面的<body>和<head> ...

  5. 基于Java语言开发jt808、jt809技术文章精华索引

    很多技术开发人员喜欢追逐最新的技术,如Node.js, go等语言,这些语言只是解决了某一个方面,如只是擅长异步高并发等等,却在企业管理后台开发方面提供的支持非常不够,造成项目团队技术选项失败,开发后 ...

  6. js上传控件 plupload 使用记录

    最近一个项目需要使用一个上传控件进行多图片上传,给用户更好的体验,找到了plupload,用了一下感觉还是不错的, 1.从官网上  可以获得例子 ,我集成到了jsp,如下: <%@ page l ...

  7. C运行库和VC对应关系

    ## C运行库和VC对应关系----------------------------------------------------------------Msvcr60.DLL -- VC6Msvc ...

  8. P3P解决cookie跨域

    P3P是什么 P3P(Platform for Privacy Preferences)是W3C公布的一项隐私保护推荐标准,以为用户提供隐私保护.   P3P标准的构想是:Web 站点的隐私策略应该告 ...

  9. nginx调用php-fpm出错解决方法和nginx配置详解

    装完了nginx和php-5.5,配置好了nginx调用php后,就开始启动php-fpm. 使用下面的命令 复制代码 代码如下: /usr/local/php/sbin/php-fpm 就可以启动了 ...

  10. Nexus 5 刷机 - Android 5.0 Lollipop

    Nexus刷机 : 官方地址 刷机步骤 下载相应的安装包 连接USB 重启手机,进入BootLoader界面 : 使用命令 adb reboot bootloader 关机; 音量键下 + 电源键 ...