02.为何使用K8s

为何使用K8s

K8s的功能特性

Kubernates是建立在扩展性的具备二次开发的功能层次丰富的体系化系统,首先其最核心的功能是管理容器集群,能管理容器化的集群(包括存储,计算,当然这个是建立在对容器运行时(CRI,网络接口(CNI),存储服务接口(CSI/FV)的基础上;其次是面向应用(包括无状态/有状态,批处理/服务型应用)的部署和路由能力,特别是基于微服务架构的应用管理,具备了其服务定义和服务发现,以及基于configmap的统一配置能力;在基础资源(主要是抽象底层IaaS的资源)和应用层的抽象模型之上是治理层,包含弹性扩容,命名空间/租户,等。当然,基于其原子内核的基础能力,在K8s的核心之上搭建统一的日志中心和全方位监控等服务是水到渠成的,CNCF更是有其认定推荐。

功能

  • 服务发现和负载平衡:K8s可以使用DNS名称或使用自己的IP地址暴露容器。如果容器的流量很高,K8s能够负载均衡并分配网络流量,以便部署稳定。

  • 存储编排:K8s允许您自动安装您选择的存储系统,例如本地存储,公共云提供商等。

  • 自动部署和回滚:您可以使用K8s描述已部署容器的所需状态,并且可以以受控速率将实际状态更改为所需状态。例如,您可以自动化K8s为您的部署创建新容器,删除现有容器并将所有资源用于新容器。

  • 自动装箱:K8s允许您指定每个容器需要多少CPU和内存(RAM。当容器指定了资源请求时,K8s可以更好地决定管理容器的资源。

  • 自我修复:K8s重新启动失败的容器,替换容器,杀死不响应用户定义的运行状况检查的容器,并且在它们准备好服务之前不会将它们通告给客户端。

  • 密钥和配置管理:K8s允许您存储和管理敏感信息,例如密码,OAuth令牌和ssh密钥。您可以部署和更新机密和应用程序配置,而无需重建容器映像,也不会在堆栈配置中暴露机密。

优势

  • 它的速度很快:在不停机的情况下持续部署新功能时,K8s是一个完美的选择。K8s的目标是以恒定的正常运行时间更新应用程序。它的速度通过您每小时可以运送的许多功能来衡量,同时保持可用的服务。以正确的方式使用K8s可帮助DevOps即服务团队自动扩展应用程序并以零停机时间进行更新。

  • 遵循不可变基础架构的原则:以传统方式,如果多个更新出现任何问题,您就没有任何记录显示您部署了多少更新以及发生了哪个错误。在不可变基础结构中,如果您希望更新任何应用程序,则需要使用新标记构建容器映像并进行部署,从而使用旧映像版本终止旧容器。通过这种方式,您将获得一份记录,并了解您所做的事情以及是否有任何错误;您可以轻松回滚到上一个图像。

  • 提供声明性配置:用户可以知道系统应该处于什么状态以避免错误。作为传统工具的源代码控制,单元测试等不能与命令式配置一起使用,但可以与声明性配置一起使用。

  • 大规模部署和更新软件:由于K8s具有不可变的声明性,因此扩展很容易。K8s提供了一些用于扩展目的的有用功能:

    • 水平基础架构缩放:在单个服务器级别执行操作以应用水平缩放。可以毫不费力地添加或分离atest服务器。
    • 自动扩展:根据CPU资源或其他应用程序指标的使用情况,您可以更改正在运行的容器数
    • 手动缩放:您可以通过命令或界面手动缩放正在运行的容器的数量
    • 复制控制器:复制控制器确保群集在运行条件下具有指定数量的等效窗格。如果存在太多Pod,则复制控制器可以删除额外的Pod,反之亦然。
  • 处理应用程序的可用性:K8s检查节点和容器的运行状况,并在由于错误导致的盒中崩溃时提供自我修复和自动替换。此外,它在多个Pod之间分配负载,以便在意外流量期间快速平衡资源。

  • 存储卷:在K8s中,数据在容器之间共享,但如果Pod被杀死,则会自动删除卷。此外,数据是远程存储的,因此如果将Pod移动到另一个节点,数据将保留,直到用户删除为止。

不足

  • 初始过程需要时间:创建新进程时,您必须等待应用程序开始,然后才能供用户使用。如果要迁移到K8s,则需要对代码库进行修改,以使启动过程更有效,这样用户就不会有糟糕的体验。

  • 迁移到无状态需要付出很多努力:如果您的应用程序是群集或无状态的,则不会配置额外的Pod,并且必须在应用程序中重新配置。

  • 安装过程繁琐:如果您不使用AzureGoogleAmazon等任何云提供商,则很难在群集上设置K8s;不过在有Rancher这样的工具辅助下,安装效率也有了大幅度的提高。

云操作系统

Kubernetes诞生之前,很多产品也做过此类尝试,例如MesosMesos早期甚至并不支持容器,主要设计的目标也是短任务(后通过Marathon Framework支持长服务,更像一个分布式的工作流和任务管理(或者是分布式进程管理)系统,但是已经体现了Workload和硬件资源分离的思想。在前Kubernetes时代,Mesos的设计更像是传统的系统工程师对分布式任务调度的思考和实践;K8s从设计之初就是要在硬件层之上去抽象所有类型的workload,构建自己的生态系统。如果说Mesos还是个工具的话,那么K8s的目标其实是奔着做一个分布式操作系统去的。

简单做个类比:整个集群的计算资源统一管控起来就像一个单机的物理计算资源,容器就像一个个进程,Overlay network就像进程通信,镜像就像一个个可执行文件,Controller就像SystemdKubectl就像Shell等。从另一方面看,Kubernetes为各种IaaS层提供了一套标准的抽象,不管你底层是自己的数据中心的物理机,还是某个公有云的VM,只要你的服务是构建在K8s之上,那么就获得了无缝迁移的能力。

微服务

现在我们都习惯了使用微服务来构建服务端架构,K8s它不仅仅提供了对于基础云资源的管控,它还为我们在设计微服务之中所需的设计要素提供了对应的解决方案:

  • API网关:Ingress
  • 无状态化:区分有状态与无状态的应用,K8s中无状态对应Deployment,有状态对应StatefulSet
  • 数据库的横向扩展:Headless Service指向PaaS服务,或者StatefulSet部署
  • 缓存:Headless Service指向PaaS服务,或者StatefulSet部署
  • 服务拆分与服务发现:Service
  • 服务编排与弹性伸缩:DeploymentReplicas
  • 统一配置中心:ConfigMap
  • 统一的日志中心:DaemonSet部署日志Agent
  • 熔断,限流,降级:Service Mesh
  • 全方位的监控:Cadvisor,DaemonSet部署监控

Kubernetes、Service MeshServerless三者共同演绎不同层次的封装和向上屏蔽下面的细节。Kubernetes引入了不同的设计模式,实现对各种云资源全新、有效和优雅的抽象和管理模式,让集群的管理和应用发布变成了件相当轻松且不易出错的事。被广泛采用的微服务软件架构将分布式应用的各种复杂度迁移到了服务之间,如何通过全局一致、体系化、规范化和无侵入的手段进行治理就变成了微服务软件架构下至关重要的内容。Kubernetes细化的应用程序的分解粒度,同时将服务发现、配置管理、负载均衡和健康检查等作为基础设施的功能,简化了应用程序的开发。而Kubernetes这种声明式配置尤其适合CI/CD流程,况且现在还有如Helm、Draft、Spinnaker、Skaffold等开源工具可以帮助我们发布Kuberentes应用。

CI/CD Pipeline Workflow with K8s

Service Mesh通过将各服务所共用和与环境相关的内容剥离到部署于每个服务边上的Sidecar进程而轻松地做到了。这一剥离动作使得服务与平台能充分解耦而方便各自演进与发展,也使得服务变轻而有助于改善服务启停的及时性。Service Mesh因为将那些服务治理相关的逻辑剥离到了Sidecar中且作为独立进程,所以Sidecar所实现的功能天然地支持多语言,为上面的服务采用多语言开发创造了更为有利的条件。通过Service Mesh对整个网络的服务流量进行技术收口,让异地多活这样涉及流量调度的系统工程实现起来更加优雅、简洁与有效,也能更加方便地实现服务版本升级时的灰度、回滚而改善安全生产质量。由于技术收口,给服务流量的治理和演进、排错、日志采集的经济性等疑难问题创造了新的发展空间。

基础设施使用YAML表达

PuppetChef的世界里走出来,Kubernetes的一个重要转变是,从以代码为基础的基础架构转向了以数据为基础的基础架构。具体来说,就是YAMLKubernetes中的所有资源,包括Pod、Configurations、Deployments、Volumes等,都可以简单地用YAML文件来表达。比如:

apiVersion: v1
kind: Pod
metadata:
  name: site
  labels:
    app: web
spec:
  containers:
    - name: front-end
      image: nginx
      ports:
        - containerPort: 80

这种表示方式使DevOpsSRE工程师更容易完全表达工作负载,而不需要在PythonRubyJavascript等语言中用代码表示。将基础架构作为数据的好处还包括:

  • GitOpsGit操作版本控制。使用这种方法,你可以将所有的Kubernetes YAML文件都保存在git仓库下,这样你就可以准确地知道何时进行了更改,谁做了更改,以及具体更改了什么。这使得整个组织的透明度更高,并且通过避免团队成员弄不清楚去哪里寻找配置,从而提高了效率。同时,只需合并一个拉动请求,就可以更方便地自动化更改Kubernetes资源。

  • 可扩展性。将资源定义为YAML,使得集群操作人员在Kubernetes资源中改变一两个数字来扩容或者缩容集群。Kubernetes有水平pod自动缩放器,可以指定给定部署所需要的最小和最大pod数量,以便能自动缩容/扩容应对流量低谷/高峰。例如,如果你正在运行的部署可能因为流量突增而需要更多的容量,你可以将maxReplicas10个改为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用户运行。为此,用户可以使用一个简单的Open Policy Agent 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中运行某应用程序,并希望该应用程序可以通过服务访问,云供应商会帮助自动创建一个LoadBalancer服务,该服务会自动提供一个Amazon Elastic Load Balancer来将流量转发到应用程序的pods

可扩展性

Kubernetes的可扩展性很强,开发者很喜欢这一点。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,这是一个在Kubernetes集群中运行的特定进程,遵循控制循环模式。一个Operator允许用户通过与Kubernetes API对话来自动管理CRD(自定义资源定义。社区有几个工具,允许开发者创建自己的Operator。其中一个工具是Operator Framework及其Operator SDK。该SDK为开发者提供了一个框架,让他们可以非常快速地开始创建Operator。例如,您可以通过以下命令行开始创建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

添加API和控制器的方式如下。

$ 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 KafkaOperator就是这样定义的,它允许用户在Kubernetes上面安装一个Kafka集群,只需要几条命令就可以了。

$ kubectl kudo install zookeeper
$ kubectl kudo install kafka

然后用另一个命令来调整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
上一页
下一页