容器最开始是一种Linux内核进程隔离构造,包含2007年的cgroups和2002年的namespaces。自LXC在2008年面世后,容器变得更为重要,谷歌也开发了自己内部的“在容器中运行一切”的机制,名为Borg。快进到2013年,Docker发布,彻底普及了容器。当时Mesos是编排容器的主要工具,然而它并没有被广泛采用。Kubernetes在2015年发布,并迅速成为容器编排的事实标准。
为了了解Kubernetes的流行度,我们来思考一些问题。开发人员最后一次就部署生产应用程序的方式达成一致是什么时候?你知道有多少开发者是按开箱即用的方式运行工具的?如今有多少云操作工程师不了解应用程序的工作原理?我们将在本文中探讨答案。
基础设施即YAML
从Puppet和Chef的世界来看,Kubernetes的一大转变就是从基础设施即代码转向为基础架构即YAML。Kubernetes中所有的资源,包括Pod、配置、部署、数据卷等,都可以简单地用YAML文件来表示。例如:
- apiVersion: v1
- kind: Pod
- metadata:
- name: site
- labels:
- app: web
- spec:
- containers:
- - name: front-end
- image: nginx
- ports:
- - containerPort: 80
通过这种表示形式,DevOps或SRE无需使用Python、Ruby或JavaScript等编程语言编写代码,即可更轻松且充分表达其工作负载。
将基础设施作为数据的其他好处包括:
GitOps或Git操作版本控制。通过这种方法,你可以将所有的Kubernetes YAML文件都保存在Git仓库下,这样你就可以精确地知道什么时候进行了更改,谁进行了更改,以及到底更改了什么。这使得整个组织更加透明,避免了成员需要到哪里去寻找所需内容的歧义,提高了效率。同时,只需合并一个拉取请求就可以更容易地自动对Kubernetes资源进行更改。
可伸缩性。将资源定义为YAML使群集操作员能够非常轻松地更改Kubernetes资源中的一个或两个数字来更改缩放行为。Kubernetes有Pod水平自动缩放控制器来帮助你确定一个特定部署需要拥有的最小和最大的Pod数量,以便能够处理低流量和高流量时间。例如,如果你正在运行的部署可能因为流量突然增加而需要更多的容量,你可以将maxReplicas从10改为20:
- apiVersion: autoscaling/v2beta2
- kind: HorizontalPodAutoscaler
- metadata:
- name: myapp
- namespace: default
- spec:
- scaleTargetRef:
- apiVersion: apps/v1
- kind: Deployment
- name: myapp-deployment
- minReplicas: 1
- maxReplicas: 20
- metrics:
- - type: Resource
- resource:
- name: cpu
- target:
- type: Utilization
- averageUtilization: 50
安全和控制。YAML是验证在Kubernetes中部署了什么以及如何部署的好方法。例如,在安全性方面,其中一个主要关注点是工作负载是否以非root用户身份运行。我们可以利用conftest这样的工具,一个YAML/JSON验证器,加上Open Policy Agent这个策略验证器,来检查你的工作负载的SecurityContext是否允许容器以root身份运行。为此,用户可以使用一个简单的OPA rego策略表示,例如:
- package main
-
- deny[msg] {
- input.kind = "Deployment"
- not input.spec.template.spec.securityContext.runAsNonRoot = true
- msg = "Containers must not run as root"
- }
云提供商集成。科技行业的主要趋势之一是在公共云提供商中运行工作负载。在云提供商组件的帮助下,Kubernetes允许每个集群与它所运行的云提供商进行集成。例如,如果用户在AWS中的Kubernetes中运行应用程序,并希望该应用程序可以通过Service访问,云提供商可帮助自动创建一个LoadBalancer服务,该服务将自动配置一个Amazon Elastic Load Balancer,以将流量转发到应用程序Pod中。
扩展
Kubernetes的可扩展性很强,开发者很喜欢这一点。现有的资源有例如Pods、Deployments、StatefulSets、Secrets、ConfigMaps等。但是,用户和开发者可以通过自定义资源定义的形式添加更多的资源。例如如果我们想定义一个CronTab资源,我们可以用这样的方式来实现:
- apiVersion: apiextensions.k8s.io/v1
- kind: CustomResourceDefinition
- metadata:
- name: crontabs.my.org
- spec:
- group: my.org
- versions:
- - name: v1
- served: true
- storage: true
- Schema:
- openAPIV3Schema:
- type: object
- properties:
- spec:
- type: object
- properties:
- cronSpec:
- type: string
- pattern: '^(\d+|\*)(/\d+)?(\s+(\d+|\*)(/\d+)?){4}$'
- replicas:
- type: integer
- minimum: 1
- maximum: 10
- scope: Namespaced
- names:
- plural: crontabs
- singular: crontab
- kind: CronTab
- shortNames:
- - ct
之后我们可以用类似以下方式来创建一个CronTab资源:
- apiVersion: "my.org/v1"
- kind: CronTab
- metadata:
- name: my-cron-object
- spec:
- cronSpec: "* * * * */5"
- image: my-cron-image
- replicas: 5
Kubernetes可扩展性的另一种形式是它能够让开发人员编写自己的Operator,Operator是运行在Kubernetes集群中的特定进程,遵循控制循环模式。Operator允许用户通过与Kubernetes API通信自动管理CRD(自定义资源定义)。
社区有一些工具允许开发者创建自己的Operator。其中一个工具是Operator Framework及其Operator SDK。SDK为开发者提供了一个框架,以便快速开始创建Operator。例如,你可以使用它的命令行用这样的方法开始入门:
- $ operator-sdk new my-operator --repo github.com/myuser/my-operator
这将为你的Operator创建整个模版,包括YAML文件和Golang代码:
- .
- |____cmd
- | |____manager
- | | |____main.go
- |____go.mod
- |____deploy
- | |____role.yaml
- | |____role_binding.yaml
- | |____service_account.yaml
- | |____operator.yaml
- |____tools.go
- |____go.sum
- |____.gitignore
- |____version
- | |____version.go
- |____build
- | |____bin
- | | |____user_setup
- | | |____entrypoint
- | |____Dockerfile
- |____pkg
- | |____apis
- | | |____apis.go
- | |____controller
- | | |____controller.go
然后你可以这样添加APIs和控制器:
- $ operator-sdk add api --api-version=myapp.com/v1alpha1 --kind=MyAppService
- $ operator-sdk add controller --api-version=myapp.com/v1alpha1 --kind=MyAppService
最后构建并将Operator镜像推送到你的容器注册表。
- $ operator-sdk build your.container.registry/youruser/myapp-operator
如果开发者需要有更多的控制权,他们可以修改Golang文件中的模板代码。例如,要修改控制器的具体细节,他们可以对controller.go文件进行修改。
另一个项目,KUDO,允许你只使用声明性的YAML文件来创建Operator。例如,一个Apache Kafka的Operator将像这样定义,它允许用户通过几个命令在Kubernetes之上安装一个Kafka集群。
- $ kubectl kudo install zookeeper
- $ kubectl kudo install kafka
也可以使用另一个命令调整它。
- $ kubectl kudo install kafka --instance=my-kafka-name \
- -p ZOOKEEPER_URI=zk-zookeeper-0.zk-hs:2181 \
- -p ZOOKEEPER_PATH=/my-path -p BROKER_CPUS=3000m \
- -p BROKER_COUNT=5 -p BROKER_MEM=4096m \
- -p DISK_SIZE=40Gi -p MIN_INSYNC_REPLICAS=3 \
- -p NUM_NETWORK_THREADS=10 -p NUM_IO_THREADS=20
创新
在过去的几年里,Kubernetes每隔三四个月就会有一个重要的版本,也就是说每年都会有三四个重要的版本。推出新功能的数量并没有放缓,在上一个版本中,有超过30个不同的新增功能和变化就是证明。此外,正如Kubernetes项目GitHub活动所显示的那样,即使在这些困难时期,贡献也没有放缓的迹象。
新功能让集群操作人员在运行各种不同的工作负载时更加灵活。软件工程师也喜欢拥有更多的控制权,可以直接将他们的应用程序部署到生产环境中。
社区
Kubernetes受欢迎的另一大方面是其强大的社区。首先,Kubernetes在2015年打出1.0版本时,就被捐给了一个厂商中立的机构:云原生计算基金会。
随着项目的推进,还有大量针对Kubernetes的不同领域的社区SIG(特殊兴趣小组),他们不断增加新的功能,使其更加用户友好。
云原生基金会还组织了CloudNativeCon/KubeCon,截至目前,该活动是世界上有史以来最大的开源活动。该活动通常每年最多举办三次,聚集了成千上万的技术专家和专业人士,他们希望改进Kubernetes及其生态系统,并利用每三个月发布的一些新功能。
此外,云原生基金会还有一个技术监督委员会,该委员会与其SIG一起研究基金会在云原生生态系统中的新项目和现有项目。大多数项目都有助于提升Kubernetes的价值主张。
最后,我相信,如果没有社区有意识的努力,互相彼此并欢迎任何新来者,Kubernetes就不会取得现在这样的成功。
未来
开发者在未来面临的主要挑战之一是如何更加关注代码的细节,而非代码运行的基础设施。为此,无服务器正在成为解决这一挑战的领先架构范例之一。目前已经有一些非常先进的框架,如Knative和OpenFaas,它们使用Kubernetes将基础设施从开发者中抽象出来。
在本文中,我们简单地介绍了Kubernetes,但这仅是冰山一角,用户还可以利用更多的资源、功能和配置。我们将继续看到新的开源项目和技术来增强和发展Kubernetes,正如我们提到的,贡献和社区占据了很大地位。