OpenFaaS接口

翻译自: https://blog.alexellis.io/the-power-of-interfaces-openfaas/ 2019年5月28日
首发于:http://hiknowledge.top/2019/12/27/openfaas%e6%8e%a5%e5%8f%a3/

在这篇文章中,我将探索如何将接口的思想引入OpenFaaS,以使项目适应不断变化的容器环境。您将了解如何通过提供程序模型和faas-provider SDK扩展OpenFaaS。然后,我将向您介绍为什么我们要构建faas-cli generate命令,它的作用,以及如何使用它来为Kubernetes生成CRD。

OpenFaaS架构介绍

一开始有OpenFaaS, OpenFaaS只在Docker Swarm上运行。

四个月后,一个名为Kubernetes的容器编配平台的需求也出现了,该平台的吸引力和业界的接受度都在增长。回顾历史,很难想象 Kubernetes 并不总是容器战争的赢家。

当时,OpenFaaS网关和API接口的集群实现是紧密耦合的。

Provider模型

因此,我将OpenFaaS网关现有的REST API分为三个组件——网关、提供者接口和faas-swarm。 其思想是,提供者可以接入任何容器编制器,并了解如何在不更改接口或工具的情况下执行CRUD、伸缩和调用

这意味着网关组件不需要像Kubernetes的client-go包那样有大量的依赖关系而膨胀起来,这意味着我们可以更紧密地遵循单一责任原则(SRP)。

从现在开始,OpenFaaS网关只作为配置选项(如验证、跟踪和度量)的中间件

faas-provider

今天,有一个Golang SDK允许任何人引导OpenFaaS提供者——你只需填写“http.Handler”函数即可,简化了整个体验。

OpenDNS的Alex Boten领导的团队在不到24小时内创建了他们自己的OpenFaaS provider

faas-memory

这是一个 in-memory provider实现的例子。

import (
	bootstrap "github.com/openfaas/faas-provider"
	bootTypes "github.com/openfaas/faas-provider/types"
)

func main() {

	bootstrapHandlers := bootTypes.FaaSHandlers{
		FunctionProxy:  handlers.MakeProxy(),
		DeleteHandler:  handlers.MakeDeleteHandler(),
		DeployHandler:  handlers.MakeDeployHandler(),
		FunctionReader: handlers.MakeFunctionReader(),
		ReplicaReader:  handlers.MakeReplicaReader(),
		ReplicaUpdater: handlers.MakeReplicaUpdater(),
		UpdateHandler:  handlers.MakeUpdateHandler(),
		HealthHandler:  handlers.MakeHealthHandler(),
		InfoHandler:    handlers.MakeInfoHandler(version.BuildVersion(), version.GitCommitSHA),
	}

	readConfig := types.ReadConfig{}
	osEnv := types.OsEnv{}
	cfg := readConfig.Read(osEnv)

	bootstrapConfig := bootTypes.FaaSConfig{
		ReadTimeout:     cfg.ReadTimeout,
		WriteTimeout:    cfg.WriteTimeout,
		TCPPort:         &cfg.Port,
		EnableHealth:    true,
		EnableBasicAuth: false,
	}

	log.Infof("listening on port %d ...", cfg.Port)
	bootstrap.Serve(&bootstrapHandlers, &bootstrapConfig)
}

您可以看到,我们在 bootTypes.FaaSHandlers 中有效地设置了各Handler结构,然后运行 bootstrap.Serve 用于启动provider。

通过遵循provider模型,任何容器编配器或其他系统(如内存存储)都可以作为接入对象,整个OpenFaaS生态系统仍然可用且兼容。

保持兼容

为了保持提供者的兼容性,我们引入了在构建时为Swarm和Kubernetes提供者运行的certifier项目,以确保它们支持API接口。

当Hashicorp的Nic Jackson启动faas-nomad提供程序时,他使用这个工具进行了一种测试驱动的开发。

规则的一个例外:Operator和CRD

2017年,我和Stefan Prodan在KubeCon讨论了将我们的faas-netes改造成一使用自己的Function CRD的Operator。

在Stefan使用来自faas-netes控制器的代码将一个工作操作符组合在一起后不久,它就工作了,这意味着用户可以进行选择。他们可以使用REST API或CRD,实际上他们可以两者都使用。REST API创建了CRD条目。

我们已经有了一个OpenFaaS provider的生态系统和用于不同的容器编制器的 后端,如:Docker Swarm、Kubernetes、AWS Fargate和Hashicorp Nomad。这意味着我们现在有两种正交的方式来在OpenFaaS中部署函数。

所有现有的提供程序都使用stack.yml文件和REST API,但是OpenFaaS Operator现在有了一个CRD。

进入faas-cli generate

我提出了以下建议:特性:通过CLI生成Kubernetes CRD YAML文件

假设我们有一个名为webhook-responder的函数,它接收HTTP消息,然后在Slack中向传入的webhook URL发送消息。

它看起来是下面这样的,由于我们的faas-provider接口,我们可以将它部署到任何兼容的OpenFaaS Provider。

#stack.yml
provider:
  name: openfaas
  gateway: http://127.0.0.1:8080

functions:
  webhook-responder:
    lang: go
    handler: ./webhook-responder
    image: alexellisuk/webhook-responder:latest
    environment:
      write_debug: true
    secrets:
     - webhook-token
     - slack-incoming-url

我们可以使用faas-cli deployfaas-cli up来部署这个函数。

对于Kubernetes的操作人员和开发人员来说,使用REST API提供了一些价值,但是在本质上,我们更喜欢我们最喜欢的工具kubectlkubectl工具允许本地Kubernetes策略和与RBAC和其他工具的深度集成。

我提出的解决方案是创建一个生成命令,它将用一个stack.yml文件作为输入,然后生成一个CRD。下面是它实际的样子:

$ faas-cli generate
# or
$ faas-cli generate --api=openfaas.com/v1alpha2

注意:您可以运行faas-cli generate --help来获得更多的选项。

#webhook-responder.yaml
---
apiVersion: openfaas.com/v1alpha2
kind: Function
metadata:
  name: webhook-responder
  namespace: openfaas-fn
spec:
  name: webhook-responder
  image: alexellisuk/webhook-responder:latest
  environment:
    write_debug: "true"
  secrets:
  - webhook-token
  - slack-incoming-url

这个函数可以用: kubectl apply -f webhook-responder.yaml 来部署。

如果您想了解faas-cli如何生成,那么请查看这里的命令文件:generate.go。最初的版本是由社区的Vivek Syngh编写的。

为Knative扩展faas-cli generate

在2018年夏天,谷歌宣布了一个新的无服务器项目,名为Knative。我立即着手评估这个已经饱和的市场的新进入者,发现用OpenFaaS构建的任何功能都可以部署,而不需要 到他们的平台上修改。

Portability with Knative (gist)

OpenFaaS函数商店允许社区和最终用户共享、发现和协作函数。

在我的Gist,我展示了来自函数存储的函数可以部署到Knative。这启发了Michael Hausenblas, RedHat的开发者倡导者,在他的第一篇Knative博客文章中展示了着色功能 colorise

就在今年KubeCon Barcelona的无服务器从业者峰会(SPS)之前,我开始更新faas-cli以生成Knative服务定义。

Pull request: Add knative serving v1alpha1 to generate CRD command.

$ faas-cli generate --api=serving.knative.dev/v1alpha1

---
apiVersion: serving.knative.dev/v1alpha1
kind: Service
metadata:
  name: webhook-responder
  namespace: openfaas-fn
spec:
  runLatest:
    configuration:
      revisionTemplate:
        spec:
          container:
            image: alexellisuk/webhook-responder:latest
            env:
            - name: write_debug
              value: "true"
            volumeMounts:
            - name: webhook-token
              mountPath: /var/openfaas/secrets/webhook-token
              readOnly: true
            - name: slack-incoming-url
              mountPath: /var/openfaas/secrets/slack-incoming-url
              readOnly: true
          volumes:
          - name: webhook-token
            secret:
              secretName: webhook-token
          - name: slack-incoming-url
            secret:
              secretName: slack-incoming-url

您甚至可以将这个命令传递到kubectl apply,甚至在stack.yml中也可以使用它包含多个函数。

faas-cli generate --api=serving.knative.dev/v1alpha1 | kubectl apply -f -

你可以这样搜索商店:

$ faas-cli store list --verbose
FUNCTION                    DESCRIPTION
Colorization                Turn black and white photos to color ...
Inception                   This is a forked version of the work ...
Have I Been Pwned           The Have I Been Pwned function lets y...
SSL/TLS cert info           Returns SSL/TLS certificate informati...
Face blur by Endre Simo     Blur out faces detected in JPEGs. Inv...
Figlet                      OpenFaaS Figlet image. This repositor...
...

然后将一个函数从商店部署到Knative,例如figlet(生成ASCII标志):

$ faas-cli generate --api=serving.knative.dev/v1alpha1 --from-store="figlet"

包装

OpenFaaS提供者模型及其REST API意味着该项目可以针对各种容器编制器,甚至是AWS Lambda这样的专有SaaS产品,而无需对接口进行任何额外的更改。

faas-cli生成命令还为我们提供了一种方法,可以在不抛弃原始设计的情况下深入集成Kubernetes当前和未来的生态系统。

您可能想知道OpenFaaS REST API和Kubernetes或Knative上OpenFaaS的CRD是否互相正交?这可能是事实,但是OpenFaaS Operator表明我们可以两全其美。

本设计之所以成功,是因为OpenFaaS的目标是Docker或OCI镜像格式和端口8080上的简单HTTP接口。我称之为“Serverless 2.0运行时契约”或“一个Serverless工作负载”。

Serverless 2.0 Landscape

在Serverless 2.0世界中,您可以在任何列出的模板系统中构建函数,然后在选择的任何服务平台和列出的可安装或托管的基础设施产品上运行它们。

我的 其他相关工作:

未来的工作

OpenFaaS的使命是:简化无服务器函数。因此,在我看来,未来的工作是扩展OpenFaaS提供商和CRD生态系统,以便将开发人员的体验带到所有平台。

在2019年,我们的社区将对Kubernetes生态系统的目标进行双重调整:性能、安全性和企业特性。我们也超越了Kubernetes和faas-lambda。Edward Wilde在SPS活动中谈到了他如何创建一个直接部署到AWS Lambda的faas-provider,而不需要对为OpenFaaS构建的图像进行额外的修改。订阅博客获取更多信息。

在我看来,这是社区未来的一些工作:

  • 一个以Knative Serving CRD为接入目标的OpenFaaS provider,它将被(想象地)称为faas-knative,并将OpenFaaS REST API、CLI、函数存储和UI带给Knative用户,以及在无服务器环境下的最佳开发体验之一。
  • Rancher的首席架构师Darren Shepherd表示有兴趣构建一个OpenFaaS Provider,为名为Rio的新Rancher microPaaS平台带来开发者体验。
  • 一个 faas-cli generate --api=services.rio.cattle.io 命令为Rio生成自己的Service定义,以支持实验和快速反馈。看issue

Get involved

我们拥有强大的核心团队和200多名贡献者,现在是参与项目的最佳时机。欢迎贡献和反馈,以及任何其他问题或意见。

不知道如何贡献?加入Slack,然后跟上我们最新的社区视频简报:如何贡献,2019年

Follow @alexellisuk and @openfaas on Twitter.

[翻译]Nomad运行时驱动接口

URL: https://www.nomadproject.io/docs/internals/plugins/task-drivers.html

创作任务驱动程序插件

在Nomad中编写任务驱动程序(在本文档中简称为驱动程序)包括实现DriverPlugin接口和添加主包以启动插件。驱动程序插件的寿命很长,并且其寿命不受Nomad客户端的约束。这意味着Nomad客户端可以在不重新启动驱动程序的情况下重新启动。 Nomad将确保驱动程序的一个实例正在运行,这意味着如果驱动程序崩溃或终止,Nomad将启动它的另一个实例。

驱动程序应保持尽可能少的状态。任务状态由Nomad客户端在任务创建时存储。这将启用一种模式,其中驱动程序可以维护正在运行的任务的内存状态,并且如有必要,Nomad客户端可以将任务恢复到驱动程序状态。

驱动程序插件骨架项目,可以帮助引导新驱动程序插件的开发。它提供了驱动程序插件所需的大部分样板,以及详细的注释。 我写了另一篇博客解析。

继续阅读[翻译]Nomad运行时驱动接口

[issue翻译]提议:为CNI支持虚拟机监控程序容器运行时

URL: https://github.com/containernetworking/cni/issues/251

feiskyer
Overview
CNI旨在为Linux上的应用程序容器提供基于通用插件的联网解决方案。今天有许多容器运行时实现。为了将容器网络与主机隔离,其中一些基于Linux网络 命名空间(例如docker,没有lkvm的rkt),而其他的则基于管理程序(例如rkt带有lkvmHyperContainer)。 当前的CNI设计已经支持基于网络名称空间的运行时,但不支持虚拟机管理程序。因此,该提案旨在为CNI添加虚拟机监控程序运行时支持。 当前的CNI设计已经支持基于网络命名空间的运行时,但不支持虚拟机管理程序。因此,该提案旨在为CNI添加虚拟机监控程序运行时支持。
Considerations
网络上的hypervisor和linux网络名称空间之间有很多区别:
- 虚拟机监控程序具有自己的网络栈,无需为虚拟机监控程序创建网络名称空间。相反,CNI应该为管理程序创建一个Tap设备。
- 由于网络是在系统管理程序内部配置的,因此CNI网络插件无法直接在外部配置IP地址。相反,CNI应该准备网络设备(例如,创建网桥并连接tap设备),然后将网络信息(包括设备,IP地址,网关,路由等)传递回运行时,并让运行时完成剩余工作配置工作。
Design
- 为了支持基于管理程序的运行时,应在CNI中进行一些更改:
- 让CNI知道运行时类型( hypervisor 或netns) 为基于 hypervisor 的运行时配置tap设备而非veth对
- 将网络信息传递回运行时

1) 知道运行时类型
在RuntimeConf中添加一个新字段UsesTapDevice, 例如:

type RuntimeConf struct {
    // The ID of the container
    ContainerID string
    // If set, a tap device instead of veth pair should be created
    UsesTapDevice bool
    // The network namespace path. 
    // Optional. Only need if IsHypervisor is not set.
    NetNS       string
    // Name of the interface inside the container. 
    IfName      string
    // Extra arguments
    Args        [][2]string
}

2) 配置tap设备,而不是veth对
这将是网络插件的工作。由于没有网络名称空间,因此网络插件应直接在主机上创建Tap设备。现有插件将被重构以创建用于虚拟机管理程序运行时的tap设备。
网络配置和IPAM可以保持不变,因此可以像以前一样分配容器的IP地址。

3) 将网络信息传递回运行时
Dan已经从#145开始了

feiskyer:CNI不在乎虚拟机监控程序的实现细节,它只是知道运行时基于虚拟机监控程序,并且希望使用Tap设备而不是veth对。容器运行时应将此Tap设备连接到虚拟机监控程序。

lukasredynk :我研究了 #145 ,它在接口更改方面显得更改量很大,添加像“ CNI_VIRT”这样的env并返回tap索引还不够吗?

feiskyer :添加环境可能有效,但不是那么明确。当前所有的cni插件都应该是基于netns的,它需要知道运行时明确是虚拟机管理程序。
仅返回tap索引是不够的,因为我们不希望每个插件都直接与hypervisor交互。取而代之的是,cni将创建的tap设备和相关的IP地址/掩码/网关/dns返回到运行时,并让运行时本身将其配置到虚拟机监控程序中。

lukasredynk : 抱歉,我不确切,是的,我同意。
我正在考虑的方案:

  1. Set CNI_VIRT (optional)
  2. Run plugin (from plugins/main)
  3. Plugin checks if CNI_VIRT set
    • CNI_VIRT not set: no changes in flow
    • CNI_VIRT set: creates tap instead of veth
  4. Plugin executes IPAM plugin (and passes CNI_VIRT if set)
    • CNI_VIRT not set: no changes in flow
    • CNI_VIRT set: only reserves IP and returns information about how to configure iface in VM
  5. Top-most plugin returns:
    • CNI_VIRT not set: no changes in flow
    • CNI_VIRT set: to the usual output would be added additional param "Iface *uint" in types.IPConfig

feiskyer: 确实需要一个新的环境变量(例如CNI_VIRT),但是仅此一个环境变量是不够的:
- 许多外部cni插件都在使用libcni,这当然需要知道运行时是否基于管理程序
- invoke.Args和CmdArgs还需要知道运行时是否基于虚拟机管理程序
顺便说一句,我对IsHypervisor bool不满意,您对此有何建议?也许 Virtualized bool?例如

type RuntimeConf struct {
    // If set, the runtime is based on hypervisor
    Virtualized bool
    ....
}

lukasredynk:我已经开始着手研究可以显示的代码,以进一步推动讨论,但是现在,我已经在pkg / skel / skel.go“ CNI_VIRT”中添加了放入CmdArgs的环境变量列表。 首先,我正在考虑提供ptp和bridge插件的基本实现,但我仍在努力,看来netlink库版本应该受到影响,因为所使用的版本不支持创建tun / tap设备。 感谢您指出invoke.Args,我完全错过了它,我本以为在pkg / skel / skel.go中添加文件就足够了。
关于IsHypervisor bool:也许TunTap bool,默认为false?因为可能会出现这种情况,tap优先于veth,而不与虚拟机管理程序字段相关?

jellonek : 要使用tap设备,您不必使用虚拟化,因此应该使用UsesTapDevice 而不是Virtualized 。

feiskyer: 谢谢。已将提案更新为UsesTapDevice而不是IsHypervisor。

lukasredynk:非常早期且非常WIP的分支,它为ptp插件创建和配置Tap设备:https://github.com/lukasredynk/cni/pull/1/files,这里的大部分更改与更新netlink库有关(供应商的版本不能处理 Tap设备 )。

lukasredynk:我已经使用rkt-kvm进行了测试,并且至少在默认受限网络中有效。

lukasredynk: RFC:应该如何命名接口?我认为它应该与“ veth_”不同,不要混用不同的设备类型,也许是“ tap_”,其中“ *”后缀的生成方式与veth_型接口相同。你怎么看待这件事?

lukasredynk:我更新了PR: 将CNI_VIRT更改为CNI_USE_TAP ;向libcni添加了代码路径;重构tap创建。
唯一缺少的是在netlink存储库中合并挂起的PR。

thockin :我不确定,但是对我来说,这似乎是一个错误的转折。并非所有的CNI插件都将使用veth。感觉就像是在获取内部信息并将其放在外部。您现在使每个网络插件负责了解tap设备吗?
为什么不能在CNI插件中完全完成此操作?我们是否需要在CNI API中提供一些能力以为插件提供线索,我也许可以买单。但总的来说,这对我来说很困难。只是感觉不对。

feiskyer: 是的,该建议将使每个cni插件负责理解tap设备。这是有道理的,因为CNI是所有容器(包括基于管理程序的运行时)的所有容器的通用网络接口。
可以在CNI插件中完全完成此操作 ,但是该插件不再通用。通常,所有内置网络插件(不确定这是否正确,我的意思是这里的插件)应在所有支持CNI的运行时中正常工作。

thockin : 我认为这是一个非常可疑的起点。你不能合理地 希望所有容器供应商都支持这一点。
有人对网络有图表或详尽的解释吗 为虚拟化运行时设置?我想看一个例子 设备链接在一起,以及如何配置IP路由。

lukasredynk : 遵循@jellonek的建议,我已经准备了带有更新的netlink(包括我的更改)的PR(#274),合并之后,我将使用PTP插件对原始PR进行重新设置。

lukasredynk : @thockin CNI插件无法在VM内部设置网络,因为它始终在主机上运行。对于基于systemd-nspawn的容器来说,在主机生成之前先建立网络连接并不是问题,因为主机和容器共享内核空间,但是在VM的情况下,没有直接的方法来创建虚拟接口(它将如何连接到主机上的网络?) ,请通过syscall分配IP地址并设置路由(有效执行CNI的操作),因为在生成VM之前无法执行此操作。系统管理程序也无法将veth设备附加到VM(当前由ptp,bridge和flannel插件创建)。

lukasredynk : 您可以看一下rkt中的网络(在github.com/coreos/rkt/networking:networking.go和kvm.go中):当前实现对nspawn容器使用CNI,对于基于VM的容器,使用自定义路径(使用CNI代码库)设置tap,获取路由,获取IP并将此信息传递回虚拟机监控程序(tap设备名称, 通过cli )。还创建了systemd单元文件,VM的systemd使用该文件来设置所有网络。实际上,所有插件均能通过这种“野路子”支持,但是会a)部分复制了CNI功能 b)使其难以维护。
集装箱供应商对此有何担忧?选择加入创建tap。在将特定的可选标志传递给CNI之前,它们将像以前一样工作。实际上,对于希望通过CNI处理网络配置并且还需要针对特定​​情况需要基于tap的网络的项目而言,这是很大的收获。

jellonek: 老实说-并非所有插件(ipvlan 不支持)。将这些代码从rkt移到cni 仍然可以解决很多问题-首先消除了提到项目上的代码重复,其次-为其他基于VM的运行时(例如HyperKube或vocker)提供了相同的功能。 而且,这还提供了在容器启动之前配置网络,并在结束后在其他网络上重用它的可能性。

thockin:明确地说-我理解为什么这对于超虚拟化运行时更好。我比较关注的是API的丑陋性以及现有驱动程序的想法:应该实现两种模式。只是感觉不对。
如果我有一个可以处理OVS的CNI插件-我可以将Tap设备链接到我的 OVS 设备中吗 ?
我可以在Calico上使用tap设备吗? Weave ? Contiv 呢?
为什么这比一组明确支持"仅tap设备"的新CNI插件更好 ?是“需要开放的FD”问题吗?

feiskyer : @thockin创建水龙头已启用。现有的驱动程序可以像今天一样选择仅支持基于netns的容器。你能解释为什么它很丑吗?
当然可以支持OVS 。只需稍作改动即可:连接到容器时,创建分接设备而不是veth对。
如果Calico / Weave / Contiv添加了对tap设备的支持,则也是如此。

feiskyer:
1) 减少代码重复 :
- 一个二进制和代码库都可以
- 网络插件可以执行许多步骤来设置网络,例如分配IP地址,创建网桥,设置iptables规则,设置隧道,将网桥连接到容器(通过veth对或Tap设备)。只有最后一步是不同的。两者的所有其他步骤完全相同。
2) 扩展CNI的范围,因此它可以支持所有类型的运行时,无论是netns还是hypervisor。 (否则,我们可能需要另一个专用于Tap设备的CNI)
3) 切换运行时时具有一致的用户体验,因此用户无需重写新插件即可与现有网络基础架构集成。

lukasredynk: 并且只有以下插件是受此变更集影响 :

  • ptp
  • bridge
  • macvlan
  • flannel (because internally wraps bridge plugin)

一些来自主要的CNI存储库。如果外部插件希望支持创建tap,则可以,但这不是强制性的。 AFAIK Calico仅使用ipam进行IP管理(单独管理veth),我不确定Weave,但快速浏览他们的文档后,情况似乎是这样。

thockin :您正在详细说明:
网络设备的特定类型并且使其成为API的一部分。一旦支持,就没有根据了 拒绝下一个不同的想法,而您的APi变成 混乱的条件和可选的if字段。有优雅和 简单到不透明。这感觉不是最简单的 可能的解决方案。
我们可以做一些管理程序存在于网络中的事情吗? 主机os)和net插件在所有情况下都是相同的,并委托 虚拟机管理程序的复杂性仅在管理程序中出现?
MacVLAN? IPVLAN?我的意思是您已经强迫用户 考虑他们的插件是否支持特定的操作模式 。 为什么将其作为API的一部分。
我的观点是,tap是一种非常特殊的技术。立刻 更好的东西来了,此API就过时。可能是一样的 说到netns(实际上,libnetwork甚至不公开netns,如果我 回想一下),但那是一个较低的水平,因此对我来说风险较小。
但实际上他们确实有不同的插件。它可能会使用80% 相同的代码,但是其行为和执行方式有所不同,因此它 是不同的。

thockin : 用户也可以针对不同的运行时使用相同的配置。

rosenhouse : 不要堆得太多,但我还会问我们如何计划在CI系统中测试这种虚拟机监控程序支持? Travis允许我们创建netns并在这些netns中执行插件。是否可以使用CI环境来行使管理程序代码路径?

squeed : 与其让“ ADD”命令具有两种相当不同的语义,不如在规范中添加一个附加命令?诸如“ CONFIGURE”之类的东西,它通过一个已经初始化的接口(例如,由管理程序创建tap)来由CNI插件配置?

jellonek:这将要求管理程序在调用CNI插件之前运行(这与kvm风格的rkt的当前实现的方式非常相似)。 使用当前的建议(以及  #271 中的初始实现),您可以从kubernetes网络准备过程中运行,然后将结果传递给任何基于虚拟机监控程序的运行时(这可能是kvm风格的rkt,hyperd或其他我现在正在工作的东西; ))。
仍然有可能通过网络设备(可以准备veth / tap / macv(lan | tap))作为附加的CNI“主”插件似乎很容易实现。现有的插件也可以重用它,就像它们已经在调用IPAM插件一样。
这样,我们将拥有像这样的链:
- 准备接口(可以通过插件或外部事物完成),该接口调用:
- 在调用特定设备的设备上分配ipv4 / ipv6所需的配置(地址/路由/ iptables) ,该接口调用:
- IPAM
实际上,前两个步骤在主要插件中组合在一起。 如果有人对此感兴趣-我们可以讨论这个问题,但是我看来在另外一个问题上。

使用云镜像在KVM上创建一个Linux实验环境

参考自: http://giovannitorres.me/create-a-linux-lab-on-kvm-using-cloud-images.html 真可惜,发现是基于CentOS,可我的开发换是Ubuntu。其中的cloud-localds 命令找不到。

发现一个找命令的工具网站 https://command-not-found.com/cloud-localds ,安装 cloud-image-utils。

找到一个cloud-init,这应该是配套云镜像使用:

Ubuntu找到几个有意思的包:有图形化管理qemu的和使用云镜像的,有空试玩

uvtool/xenial,xenial 0~bzr99-0ubuntu1 all
  Library and tools for using Ubuntu Cloud images

uvtool-libvirt/xenial,xenial 0~bzr99-0ubuntu1 all
  Library and tools for using Ubuntu Cloud Images with libvirt

qtemu/xenial 2.0~alpha1-1ubuntu6 amd64
  graphical user interface for QEMU

ubuntu-virt-mgmt/xenial,xenial 1.4 all
  Common packages useful for managing virtual machines graphically

之前参考的文章,脚本更新了,在 https://github.com/giovtorres/kvm-install-vm。Bash包装了virt-install,仅在 Fedora 最新版上测试过。使用的 ssh-authorized-keys 登陆。这是方式一。


好了,正式开始。有一简短的教程可以参考,使用的指定password登陆。这是方式二,可能更方便些。就从这个入手了。

# Ubuntu 16.04
sudo apt install libvirt-dev libvirt0 libvirt-bin virt-top virt-manager virt-what virt-goodies genisoimage cloud-init cloud-image-utils libguestfs-tools qemu qemu-kvm
# reboot

cat > cloud-config <<EOF
#cloud-config
preserve_hostname: False
hostname: centos
fqdn: centos.example.local
password: 123456
chpasswd: { expire: False }
ssh_pwauth: True
runcmd:
  - [ yum, -y, remove, cloud-init ]
EOF

cloud-localds cloud-config.img cloud-config
mv cloud-config.img /var/lib/libvirt/images

# get the qcow2 images
wget http://cloud.centos.org/centos/7/images/CentOS-7-x86_64-GenericCloud.qcow2.xz #下载 img 文件
xz -d CentOS-7-x86_64-GenericCloud.qcow2.xz
cp CentOS-7-x86_64-GenericCloud.qcow2 centos.qcow2 #复制一份
# qemu-img resize centos.qcow2 +10G #增加磁盘大小
mv centos.qcow2 /var/lib/libvirt/images

#导入
virt-install --connect=qemu:///system \
 --name centos \
 --ram 2048 \
 --vcpus=2 \
 --os-type=linux \
 --os-variant=centos7.0 \
 --disk /var/lib/libvirt/images/centos.qcow2,device=disk,bus=virtio \
 --disk /var/lib/libvirt/images/cloud-config.img,device=cdrom \
 --graphics none \
 --import

导入后可使用用户名 centos 密码 123456 登陆

virt-install第一次安装时,硬件地址,获取IP是没有问题的。之后通过Libvirt启动该磁盘镜像时,会遇到dhclient没有运行,获取不到IPv4地址的问题,需要修改镜像中网卡的IP地址,使之与Libvirt分配的硬件地址匹配。

# 手动获取IP
/sbin/dhclient -1 -q -lf /var/lib/dhclient/dhclient--eth0.lease -pf /var/run/dhclient-eth0.pid -H centos eth0

(可选)在虚拟机中安装,之后开发要用,通过该软件来获取宿主机的IP。

sudo yum install qemu-guest-agent

qemu-guest-agent是一个助手守护进程,它安装在guest中。它用于在主机和客户端之间交换信息,并在客户端执行命令。 具体特性

基于QMP实现对qemu虚拟机进行交互,qemu命令参数支持2种方法配置qmp,即-qmp和-mon。Libvirt使用的-mon。

$ virsh qemu-monitor-command centos7.0 --pretty '{ "execute": "query-block" }'
{
    "return": [
        {
            "io-status": "ok",
            "device": "drive-virtio-disk0",
            "locked": false,
            "removable": false,
            "inserted": {
                "iops_rd": 0,
                "detect_zeroes": "off",
                "image": {
                    "virtual-size": 8589934592,
                    "filename": "/var/lib/libvirt/images/centos.qcow2",
                    "cluster-size": 65536,
                    "format": "qcow2",
                    "actual-size": 2079526912,
                    "format-specific": {
                        "type": "qcow2",
                        "data": {
                            "compat": "0.10",
                            "refcount-bits": 16
                        }
                    },
                    "dirty-flag": false
                },
                "iops_wr": 0,
                "ro": false,
                "node-name": "#block155",
                "backing_file_depth": 0,
                "drv": "qcow2",
                "iops": 0,
                "bps_wr": 0,
                "write_threshold": 0,
                "encrypted": false,
                "bps": 0,
                "bps_rd": 0,
                "cache": {
                    "no-flush": false,
                    "direct": false,
                    "writeback": true
                },
                "file": "/var/lib/libvirt/images/centos.qcow2",
                "encryption_key_missing": false
            },
            "type": "unknown"
        }
    ],
    "id": "libvirt-6114"
}

$ virsh qemu-monitor-command centos7.0 --hmp 'info block'
drive-virtio-disk0 (#block155): /var/lib/libvirt/images/centos.qcow2 (qcow2)
    Cache mode:       writeback

学习qmp的简单使用

以及, 使用QEMU客户代理查找客户IP地址

# 查看支持的操作
$ virsh qemu-agent-command centos7.0 --pretty '{ "execute": "guest-info" }'
{
    "return": {
        "version": "2.12.0",
        "supported_commands": [
            {
                "enabled": true,
                "name": "guest-get-osinfo",
                "success-response": true
            },
            {
                "enabled": true,
                "name": "guest-get-timezone",
                "success-response": true
            },
            {
                "enabled": true,
                "name": "guest-get-users",
                "success-response": true
            },
            {
                "enabled": true,
                "name": "guest-get-host-name",
                "success-response": true
            },
            {
                "enabled": false,
                "name": "guest-exec",
                "success-response": true
            },
            {
                "enabled": false,
                "name": "guest-exec-status",
                "success-response": true
            },
            {
                "enabled": true,
                "name": "guest-get-memory-block-info",
                "success-response": true
            },
            {
                "enabled": true,
                "name": "guest-set-memory-blocks",
                "success-response": true
            },
            {
                "enabled": true,
                "name": "guest-get-memory-blocks",
                "success-response": true
            },
            {
                "enabled": true,
                "name": "guest-set-user-password",
                "success-response": true
            },
            {
                "enabled": true,
                "name": "guest-get-fsinfo",
                "success-response": true
            },
            {
                "enabled": true,
                "name": "guest-set-vcpus",
                "success-response": true
            },
            {
                "enabled": true,
                "name": "guest-get-vcpus",
                "success-response": true
            },
            {
                "enabled": true,
                "name": "guest-network-get-interfaces",
                "success-response": true
            },
            {
                "enabled": true,
                "name": "guest-suspend-hybrid",
                "success-response": false
            },
            {
                "enabled": true,
                "name": "guest-suspend-ram",
                "success-response": false
            },
            {
                "enabled": true,
                "name": "guest-suspend-disk",
                "success-response": false
            },
            {
                "enabled": true,
                "name": "guest-fstrim",
                "success-response": true
            },
            {
                "enabled": true,
                "name": "guest-fsfreeze-thaw",
                "success-response": true
            },
            {
                "enabled": true,
                "name": "guest-fsfreeze-freeze-list",
                "success-response": true
            },
            {
                "enabled": true,
                "name": "guest-fsfreeze-freeze",
                "success-response": true
            },
            {
                "enabled": true,
                "name": "guest-fsfreeze-status",
                "success-response": true
            },
            {
                "enabled": false,
                "name": "guest-file-flush",
                "success-response": true
            },
            {
                "enabled": false,
                "name": "guest-file-seek",
                "success-response": true
            },
            {
                "enabled": false,
                "name": "guest-file-write",
                "success-response": true
            },
            {
                "enabled": false,
                "name": "guest-file-read",
                "success-response": true
            },
            {
                "enabled": false,
                "name": "guest-file-close",
                "success-response": true
            },
            {
                "enabled": false,
                "name": "guest-file-open",
                "success-response": true
            },
            {
                "enabled": true,
                "name": "guest-shutdown",
                "success-response": false
            },
            {
                "enabled": true,
                "name": "guest-info",
                "success-response": true
            },
            {
                "enabled": true,
                "name": "guest-set-time",
                "success-response": true
            },
            {
                "enabled": true,
                "name": "guest-get-time",
                "success-response": true
            },
            {
                "enabled": true,
                "name": "guest-ping",
                "success-response": true
            },
            {
                "enabled": true,
                "name": "guest-sync",
                "success-response": true
            },
            {
                "enabled": true,
                "name": "guest-sync-delimited",
                "success-response": true
            }
        ]
    }
}

$ virsh qemu-agent-command centos7.0 --pretty '{ "execute": "guest-network-get-interfaces" }'
{
    "return": [
        {
            "name": "lo",
            "ip-addresses": [
                {
                    "ip-address-type": "ipv4",
                    "ip-address": "127.0.0.1",
                    "prefix": 8
                },
                {
                    "ip-address-type": "ipv6",
                    "ip-address": "::1",
                    "prefix": 128
                }
            ],
            "statistics": {
                "tx-packets": 6,
                "tx-errs": 0,
                "rx-bytes": 416,
                "rx-dropped": 0,
                "rx-packets": 6,
                "rx-errs": 0,
                "tx-bytes": 416,
                "tx-dropped": 0
            },
            "hardware-address": "00:00:00:00:00:00"
        },
        {
            "name": "eth0",
            "ip-addresses": [
                {
                    "ip-address-type": "ipv4",
                    "ip-address": "192.168.122.58",
                    "prefix": 24
                },
                {
                    "ip-address-type": "ipv6",
                    "ip-address": "fe80::5054:ff:fe28:c078",
                    "prefix": 64
                }
            ],
            "statistics": {
                "tx-packets": 247,
                "tx-errs": 0,
                "rx-bytes": 100007,
                "rx-dropped": 12,
                "rx-packets": 1531,
                "rx-errs": 0,
                "tx-bytes": 19704,
                "tx-dropped": 0
            },
            "hardware-address": "52:54:00:28:c0:78"
        }
    ]
}

新建一个会话,弹出 cloud init 初始化镜像

$ virsh list
 Id    Name                           State
----------------------------------------------------
 4     centos                         running

$ virsh change-media centos hda --eject --config
Successfully ejected media.

退出时使用 Ctrl 键+ ] (左方括号)键退出

再次命令行界面登陆 virsh console

virsh start centos
virsh list #确保running
virsh console centos

关闭virsh shutdown

$ virsh list --all
 Id    Name                           State
----------------------------------------------------
 -     centos                         shut off

之后立即禁止开机启动cloud-init,或者卸载。导致虚拟机开机启动贼慢。

$ ls /lib/systemd/system/cloud-*.service
/lib/systemd/system/cloud-config.service  /lib/systemd/system/cloud-init-local.service
/lib/systemd/system/cloud-final.service   /lib/systemd/system/cloud-init.service

$ systemctl disable cloud-config cloud-init-local cloud-final cloud-init
Removed symlink /etc/systemd/system/cloud-init.target.wants/cloud-config.service.
Removed symlink /etc/systemd/system/cloud-init.target.wants/cloud-init.service.
Removed symlink /etc/systemd/system/cloud-init.target.wants/cloud-init-local.service.
Removed symlink /etc/systemd/system/cloud-init.target.wants/cloud-final.service.

提供上述制作的镜像centos7.qcow2下载。

链接:https://pan.baidu.com/s/16PT60usy_HhxmtXD-FO-3A 
提取码:j8v1 
复制这段内容后打开百度网盘手机App,操作更方便哦

用virt-manager来启动,手动qemu启动,可能启动之后就找不到了

qemu-system-x86_64 -hda /var/lib/libvirt/images/centos7.qcow2 -enable-kvm  -vnc 0.0.0.0:0

推荐一本KVM入门书:《KVM Virtualization Cookbook》,网上有电子版


那如何在Windows上Libvirt呢? https://libvirt.org/windows.html

https://blog.csdn.net/weiyuanke/article/details/8020657


如何在Windows上使用qemu启动qcow2系统镜像?

https://www.xiexianbin.cn/windows/2017-02-16-use-qemu-start-qcow2-image-in-window/index.html

qemu-system-x86_64 -m 1024 -smp 1 -drive file="G:\VM\qemu\centos7.qcow2" -boot c -net nic -net tap,ifname=my-tap
Windows下启动的qemu虚拟机

一次永久解决cmd窗口汉字显示乱码:

  1. win+R 输入regedit 进入注册表
  2. 找到 HKEY_CURRENT_USER\Console\%SystemRoot%_system32_cmd.exe 如果 该项下已存在CodePage项,则把值改为十进制”65001”;如果不存在,在该项下新建一个 DWORD(32位值),命名为“CodePage”,值设为“65001”
  3. 重启cmd后生效
  4. 对于Power shell修改同样,只需在第2步修改
    %SystemRoot%_system32_WindowsPowerShell_v1.0_powershell.exe 下的项。

Nomad vs. Terraform

Nomad 和 Terraform 都是HashiCorp公司下的产品。

原文链接

Terraform是一种安全有效地构建,更改和版本化基础架构的工具。配置文件向Terraform描述运行单个应用程序或整个数据中心所需的组件。Terraform生成一个执行计划,描述它将如何做到达到所需的状态,然后执行它来构建所描述的基础架构。随着配置的更改,Terraform能够确定更改的内容并创建可应用的增量执行计划。

Nomad在很多关键方面与Terraform不同。 Terraform旨在支持任何类型的资源,包括计算实例,存储和网络等低级组件,以及DNS条目,SaaS功能等高级组件。Terraform知道如何创建,配置和管理这些资源的生命周期。 Nomad在现有基础架构上运行,并管理在该基础架构上运行的应用程序的生命周期。

另一个主要区别是Terraform是一个运行直至完成的离线工具,而Nomad是一个具有长寿命服务器的在线系统。 Nomad允许提交新作业,更新或删除现有作业,并可以处理节点故障。这需要连续操作而不是像Terraform一样。

对于只有少量服务器或应用程序的小型基础架构,Nomad的复杂性可能不会超过简单地使用Terraform静态地将应用程序分配给机器。在较大规模上,Terraform应该用于为Nomad提供容量,而Nomad用于动态地管理应用程序到机器。

Nomad是一个灵活的工作负载协调器,可用于在多个区域或云提供商之间轻松部署容器化和遗留应用程序。 Nomad易于操作和扩展,并与用于服务发现的HashiCorp Consul、用于秘密管理的HashiCorp Vault无缝集成。基于Nomad可以构建应用调度、扩缩容的弹性基础架构。