開發與維運

以應用為中心:開放應用模型(OAM)初探

前言

不久前,Kubernetes 也迎來了他 6 歲的生日,在這 6 年中,從孵化之初的三足鼎立,到後來的一統天下,Kubernetes 成為容器編排領域的事實標準已經有段時間了。在這期間,雲原生的概念開始深入人心,越來越的公司組織和開發者開始接受、瞭解、實踐雲原生。如今,已有無數的應用以容器的形式運行在各種版本 Kubernetes 中了。

應用管理之惑

然而我們慢慢發現,隨著應用和服務數量、使用場景以及承載業務的增加,Kubernetes 資源越來越難以管理。比如,有時候可能多個運維人員重複為一個 Deployment 配置了多個 Service 或 Ingress,而在一個 namespace 中動輒就有上百個 Service,在這些 Service 中找到那些重複、無效、甚至錯誤的 Service 可不是一件容易的事情。

上面描述的只是運維人員內部可能存在的衝突,更多的衝突來自開發與運維人員之間,由於各自關注的角度不同,出現了對 Deployment 配置權的爭奪,他們各自關心的字段不盡相同,但同時還要面對同一份 deployment.yaml,這就是衝突的根源。我們的做法是使用 kustomize 將一份 deployment.yaml 分成不同的 overlays,將開發和運維關注的字段分開管理,而這只是緩兵之計,依舊沒有一個統一的配置文件來描述整個應用,比如這個應用由幾個 Deployment、Service、 Ingress 組成,一個新手如果想要查看一個資源相關的其他資源,只能通過 label 和“相似”的名稱去找或者猜。而這樣做顯然是很危險的,這也是為什麼我不敢輕易清理生產環境中無用的 Service 和 ConfigMap 的原因,你永遠也想不到有什麼地方可能引用了他們。

相對標準 Kubernetes 資源,Operator 的管理難度就更大了,各式各樣的 Operator 存在於我的 Kubernetes 集群中,kubectl get crd 命令輸出的結果更是長的可怕。

而開放應用模型(OAM)可能是助我脫離苦海的一味良藥。

開放應用模型(OAM)

OAM 是阿里雲與 Azure 在 2019 年末聯合推出的標準化雲原生應用管理模型。相比於傳統 PaaS 封閉、不能同“以 Operator 為基礎的雲原生生態”銜接的現狀,基於 OAM 和 Kubernetes 構建的現代雲原生應用管理平臺,本質上是一個 “以應用為中心” 的 Kubernetes ,保證了這個應用平臺在能夠無縫接入整個雲原生生態。同時,OAM 可以進一步屏蔽掉容器基礎設施的複雜性和差異性,為平臺的使用者帶來低心智負擔的、標準化的、一致的應用管理與交付體驗。

所謂 “應用模型”,其實是一個專門用來對雲原生應用本身和它所需運維能力進行定義與描述的標準開源規範。所以對於 Kubernetes 來說,OAM 即是一個標準的“應用定義”項目(類比已經不再活躍的 Kubernetes Application CRD 項目),同時也是一個專注於封裝、組織和管理 Kubernetes 中各種 “運維能力”、以及連接 “運維能力” 與 “應用” 的平臺層項目。而通過 “定義應用” 和 “組織管理應用的運維能力” 這兩大核心功能,我們可以構建一個更容易管理、維護和發展的雲原生平臺。

以下是 OAM 的一些基本概念:

Component

在 OAM 中,Component(組件) 就是一個完全面向業務研發人員設計的、用於定義應用程序而不必考慮其運維詳細信息的載體。一個應用程序包含一個或多個 Component 。例如,一個網站應用可以由一個 Java web 組件和一個數據庫組件組成。

OAM 中的 Component 包含兩個部分:

  • 工作負載描述 —— 如何運行此 Component,以及它的運行內容,實際上就是一個完整的 K8s CR;
  • 可重寫參數列表 —— 研發通過這個字段表示該 Component 的哪些字段後續可以被運維或者系統覆蓋。

Trait

在 OAM 中,我們通過 Trait(運維特徵) 來描述和構建具備可發現性和可管理性的平臺層能力。

Trait 是與 Component 綁定的,一個 Component 可以綁定多個 Trait,從而把運維能力也加入到應用描述中,方便底層基礎設施統一管理。

Application Configuration

最終,通過引用 Component 名稱並對它綁定 Trait ,運維人員就可以使用 ApplicationConfiguration(應用配置) 來實例化應用程序。ApplicationConfiguration 的主要功能,就是讓應用運維人員(或系統)瞭解和使用業務研發人員傳達的信息,然後自由的為 Component 組合綁定不同的運維能力以相應實現其最終的運維目的。

下面這張圖很好的描述了 OAM 架構的使用場景,開發與運維的關注點分離,而最終都由一份 ApplicationConfiguration 來描述整個應用:

OAM 架構

上手實踐

上面只是對 OAM 進行了簡單的介紹,由於篇幅有限,如 Scope 這樣的概念並沒有進行介紹,更多內容歡迎加入 OAM 社區

下面就以一個簡單的示例,開啟我們的 OAM 之旅:

前提條件

本示例為官方示例,使用 OAM 部署一個 nginx 應用,該應用包含 Deployment、Service 和 Ingress。

安裝控制端

安裝 Crossplane 和 OAM

注意,這裡的 crossplane-oam-sample 是官方維護的一個 crossplane 示例,只是用作開發和演示,並不是生產可用,關於 crossplane 的更多內容,請見項目官網

$ helm repo add oam https://oam-dev.github.io/crossplane-oam-sample/archives/
$ kubectl create namespace oam-system
$ helm install crossplane --namespace oam-system oam/crossplane-oam

這裡如果由於牆的原因無法拉取 gcr.io/kubebuilder/kube-rbac-proxy:v0.4.1 鏡像,導致 crossplane-oam-localstack 無法啟動的話,可以使用我提供的替代鏡像 guoxudongdocker/kube-rbac-proxy:v0.4.1

拉取示例倉庫

$ git clone https://github.com/oam-dev/catalog.git
# 進入示例
$ cd catalog/traits/ingresstrait

部署 CRD 並啟動 controller

# 部署 CRD
$ make install
~/go/bin/controller-gen "crd:trivialVersions=true" rbac:roleName=manager-role webhook paths="./..." output:crd:artifacts:config=config/crd/bases
kustomize build config/crd | kubectl apply -f -
customresourcedefinition.apiextensions.k8s.io/ingresstraits.core.oam.dev created
# 啟動 IngressTrait controller
$ go run main.go
I0629 11:15:22.035708     802 request.go:621] Throttling request took 1.000526734s, request: GET:https://192.168.4.210:6443/apis/apiregistration.k8s.io/v1?timeout=32s
2020-06-29T11:15:22.088+0800    INFO    controller-runtime.metrics      metrics server is starting to listen    {"addr": ":8080"}
2020-06-29T11:15:22.089+0800    INFO    setup   starting manager
2020-06-29T11:15:22.089+0800    INFO    controller-runtime.manager      starting metrics server {"path": "/metrics"}
2020-06-29T11:15:22.089+0800    INFO    controller-runtime.controller   Starting EventSource    {"controller": "ingresstrait", "source": "kind source: /, Kind="}
2020-06-29T11:15:22.193+0800    INFO    controller-runtime.controller   Starting Controller     {"controller": "ingresstrait"}
2020-06-29T11:15:22.193+0800    INFO    controller-runtime.controller   Starting workers        {"controller": "ingresstrait", "worker count": 1}

由於這裡只是簡單演示,沒有將 IngressTrait controller 打包成鏡像,而是在本地運行 controller,所以需要 go 環境。

部署應用

配置 RBAC

使用命令:kubectl apply -f rbac.yaml,配置 RBAC。這裡需要注意的是官方 IngressTrait 的 sample 示例中並沒有 rbac.yaml,需要我們自己配置,否則的話會在部署時由於權限原因無法拉起 Deployment。

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: deployment-clusterrole-poc
rules:
- apiGroups:
  - apps
  resources:
  - deployments
  verbs:
  - "*"

---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: component-deployment-workload-poc
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: deployment-clusterrole-poc
subjects:
  - kind: ServiceAccount
    name: crossplane-oam              # Remember to use the actual ServiceAccount name
    namespace: oam-system             # Remember to use the actual ServiceAccount namespace

部署 Component

使用 kubectl apply -f sample_component.yaml 命令部署 Component,該 Component 中的 workload 為 Deployment。

apiVersion: core.oam.dev/v1alpha2
kind: Component
metadata:
  name: example-deploy
spec:
  workload:
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: web
    spec:
      selector:
        matchLabels:
          app: test
      template:
        metadata:
          labels:
            app: test
        spec:
          containers:
            - name: nginx
              image: nginx:1.17
              ports:
                - containerPort: 80
                  name: web

部署 ApplicationConfiguration

可以看到這個 ApplicationConfiguration 中包含一個 Component,而 Component 中又綁定了 一個 IngressTrait 類型的 Trait,由於這只是一個簡單示例,所有隻有一個 Component 和一個 Trait,在實際的生產環境中,一個 ApplicationConfiguration 可由多個 Component 組成,一個 Component 又可綁定多個 Trait 為其提供諸如流量管控、彈性伸縮等運維特性。

使用命令:kubectl apply -f sample_application_config.yaml

apiVersion: core.oam.dev/v1alpha2
kind: ApplicationConfiguration
metadata:
  name: example-appconfig
spec:
  components:
    - componentName: example-deploy
      traits:
        - trait:
            apiVersion: core.oam.dev/v1alpha2
            kind: IngressTrait
            metadata:
              name: example-ingress-trait
            spec:
                rules:
                  - host: nginx.oam.com
                    paths:
                      - path: /
                        backend:
                          serviceName: deploy-test
                          servicePort: 8080

檢查結果

可以看到 Deployment、Service 和 Ingress 已經部署成功:

$ kubectl get deploy,svc,ing
NAME                  READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/web   1/1     1            1           8m29s

NAME                  TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
service/deploy-test   ClusterIP   10.43.170.228   <none>        8080/TCP   8m29s

NAME                                       HOSTS           ADDRESS                       PORTS   AGE
ingress.extensions/example-ingress-trait   nginx.oam.com   192.168.1.129,192.168.4.210   80      8m29s

訪問服務:

$ curl -H "Host: nginx.oam.com"  http://192.168.1.129
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

應用的整體結構如下圖所示:

應用結構

結語

通過上面這個簡單的示例,可以看出如果遵循 OAM 模型來劃分應用,我們可以從 ApplicationConfiguration 入手,看到應用中都包含哪些組件(Component),同時又可以看到每個組件都有哪些運維特性(Trait)來支持這個組件,逐層的查看每個模塊的描述和配置,最終全面瞭解這個應用,而不用像現在這樣使用 label 和 name,漫無目的的靠運氣來理清整個架構,真正的做到以應用為中心

OAM 的本質是將雲原生應用定義中的研發、運維關注點分離,資源對象進行進一步抽象,化繁為簡,包羅萬象。

參考

Leave a Reply

Your email address will not be published. Required fields are marked *