前言

如今是一个云计算与数字化如火如荼的时代,云计算与数字化相得益彰,几乎所有企业都要数字化,都要上云。然而,上云容易下云难、换云也难,企业都害怕在上云的同时,被云厂商给锁定。

所谓厂商锁定,就是明知道有更适合自己的其他云产品,企业并不能自由使用,因为云迁移成本太高,以至于继续使用已经使用的云,哪怕不划算。其实,何止企业,个人也面临同样的问题。Free Arch 一直为个人开发者服务,提供免费架构、自由架构的具体实例,很早以前就提出了狡兔三窟的多云策略。个人也要使用多云策略,以免被厂商绑定,既不自由,也不免费(一旦习惯的云开始收费,只能被割韭菜)。

Serverless 的多云策略

Free 不仅有自由和免费的意思,还有随意的意思。这也是 Free Arch 系列文章比较随意的原因,Free Arch 面向具体的实际问题而随意架构,虽然不系统,不全面,但是每一次都有一个可以运行的案例,非常具体和实在。在进入今天的正题前,先总结(列举)一下以前的 Serverless 系列文章:

薅 AWS Lambda 羊毛

一顿操作猛如虎,部署一个万能 BFF

薅 Azure 羊毛

身份验证哪家强?IdentityServer 初体验

薅 Heroku 羊毛

FreeArch: 一键拥有你自己的身份认证平台 Keycloak,完全免费

薅 Vercel 羊毛

Free Arch:将 Koa 服务部署到 Vercel

今天的正题:在多个 Kubernetes 集群间自由免费地切换

今天再聊一下个人开发者如何通过使用 OAM(开放应用模型),实现将应用自由免费地在各个 Kubernetes 集群之间迁移。这个例子的源代码是: https://github.com/Jeff-Tian/uni-orders,它是一个使用 NestJs 开发的一个微服务,其 Swagger API 见: https://uni-orders-cctsq03nniljeo1bj0ng.apps.playground.napptive.dev/api/static/index.html

它已经完成了容器化,并随时可以部署在一个 Kubernetest 集群中。要部署,只需要执行 kubectl apply -k k8s即可,使用了 Kustomization,其定义在 https://github.com/Jeff-Tian/uni-orders/blob/main/k8s/kustomization.yaml

Okteto

Okteto 提供免费的 Kubernetes 集群供个人开发者使用,当然在 24 小时闲置后会被休眠,但你只需要每 24 小时部署一下就能激活。

ingress

使用 Okteto,可以定义 deployment 和 service,但是 ingress 不能免费地自定义,而只能使用 Okteto 提供的自动 ingress。
image.png

这样虽然让 ingress 自动化了,但是却只能使用 Okteto 的免费域名,形如 uni-orders-jeff-tian.cloud.okteto.net 。

secrets

对于一些保密信息,可以通过 secrets 提供,这通常是一个 yaml 文件,然后通过 kubectl apply -f secrets.yaml即可。要将这些 secrets 以环境变量形式灌入应用运行时,需要在 deployment.yaml 中进行如下格式的 env 定义:

yaml ... spec: containers: - image: jefftian/uni-orders:latest imagePullPolicy: Always name: uni-orders env: - name: GET_HOSTS_FROM value: dns - name: pgHost valueFrom: secretKeyRef: key: pgHost name: uni-orders-secrets - name: pgPort valueFrom: secretKeyRef: key: pgPort name: uni-orders-secrets - name: pgUsername valueFrom: secretKeyRef: key: pgUsername name: uni-orders-secrets - name: pgPassword valueFrom: secretKeyRef: key: pgPassword name: uni-orders-secrets - name: POSTGRES_URL valueFrom: secretKeyRef: key: POSTGRES_URL name: uni-orders-secrets - name: pgDatabase valueFrom: secretKeyRef: key: pgDatabase name: uni-orders-secrets

Napptive

虽然 Okteto 对开发者非常友好,但仍然需要有备胎,并且从第一天开始就要考虑好备胎,以保持随时切换的自由。Okteto 的备胎,就是 Napptive,它提供的服务和 Okteto 类似,所以你要每 24 小时就更新一下应用,以保持激活状态。

Napptive 还允许你绑定自定义域名,这一点比 Okteto 更友好。

Napptive 不只提供一个裸的 Kubernetes 服务,还在上面包了一层,以 OAM 的形式对开发者提供服务,对于应用,需要使用 OAM 的形式定义一个 yaml。

ingress

要使用 Napptive 的 ingress,需要在应用的 yaml 文件中以 napptive-ingress�的 traits结点进行定义: yaml ... traits:

  • type: napptive-ingress properties: name: uni-orders port: 3000 path: /

secrets

secrets 仍然可以通过 yaml 文件的形式,使用 kubectl apply -f secrets.yaml的形式部署进入 Napptive 的 Kubernetes 集群中:
image.png

但是要将定义好的 secrets 灌入应用运行时,方式和在普通的 Kubernetest 集群中的操作不一样,不通过 deployment.yaml 文件,而是和 ingress 类似,使用 service-binding的 traits结点进入声明: yaml ...

spec: components: - name: uni-orders type: webservice traits: ... - type: service-binding properties: envMappings: pgHost: secret: uni-orders-secrets key: pgHost pgPort: secret: uni-orders-secrets key: pgPort pgUsername: secret: uni-orders-secrets key: pgUsername pgPassword: secret: uni-orders-secrets key: pgPassword POSTGRES_URL: secret: uni-orders-secrets key: POSTGRES_URL pgDatabase: secret: uni-orders-secrets key: pgDatabase

自动化

以上以 Okteto 和 Napptive 为例,免费地验证了在多个 Kubernetes 集群间自由切换应用的可行性,后面就要将这一切自动化了。因为代码托管在 GitHub 上,自然地使用了 GitHub Actions 来将 CICD 的步骤自动化,一次代码提交,自动运行测试、构建,打包容器镜像,然后将打包好的应用同时部署在两个云上。

secrets CD 流水线

但是,应用的保密信息,应该首先完成自动化的 CICD。目前,建议将 secrets 单独放在一个私有仓库中,毕竟 GitHub 的私有仓库也是对个人永久免费的。 image.png

最终实现的效果如上图所示,一次代码提交会同时触发向两朵云部署 secrets,其 workflow 定义如下: yaml name: secrets CICD

on: push: branches: [ main ] pull_request: branches: [ main ]

jobs: deploy-napptive: runs-on: ubuntu-latest env: PLAYGROUND_PAT: ${{ secrets.PLAYGROUND_PAT }} steps: - uses: actions/[email protected] - run: echo Downloading NAPPTIVE Playground CLI - run: curl -O https://storage.googleapis.com/artifacts.playground.napptive.dev/installer.sh && bash installer.sh

  - run: echo Downloading Kubectl
  - run: curl -LO https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl
  - run: chmod +x kubectl
  - run: echo Login into NAPPTIVE Playground
  - run: playground login --pat
  - run: playground get-kubeconfig
  - run: ls ~/.napptive/default/napptive-kubeconfig
  - name: update
    run: |
      export PATH=$PATH:$(pwd)
      kubectl --kubeconfig ~/.napptive/default/napptive-kubeconfig apply -f secrets.yml

deploy-okteto: runs-on: ubuntu-latest steps: - uses: actions/[email protected] - run: curl -LO https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl - run: chmod +x ./kubectl - run: sudo mv ./kubectl /usr/local/bin/kubectl - run: mkdir ${HOME}/.kube - run: npm i -g k8ss - run: echo -e machine github.comn login ${{secrets.GH_TOKEN}} > ~/.netrc - run: git clone https://github.com/Jeff-Tian/k8s-config.git ${HOME}/k8s-config - run: k8ss switch --cluster=okteto --namespace=jeff-tian - run: kubectl apply -f secrets.yml

对于 Napptive 的流水线,需要一个 PLAYGROUND_PAT 环境变量,对于 okteto 流水线,需要 okteto 的 kubeconfig 文件。 image.png

PLAYGROUND_PAT 可以按照其官网文档获取,然后在 GitHub Actions 中存储其值即可。它的值其实是一个 jwt,并且永不过期,所以一定要安全保存:
image.png

对于 okteto 的配置文件,我用了另一个私有仓库 k8s-config 来保存,并且使用自己开发的 k8ss 工具进行管理。这样很方便,但是需要一个额外的 GH_TOKEN 来进行访问(因为 k8s-config 也是私有仓库)。所以最后的 GitHub Action 需要两个 secrets:
image.png

构建应用本身的 CICD 流水线

应用本身的 CICD 流水线,对于 CI 部分也没什么特别。关键在于 CD 部分,如同 secrets 的 CD 一样,需要同时更新两朵云。具体的流水线定义见文件: https://github.com/Jeff-Tian/uni-orders/blob/main/.github/workflows/node.js.yml
image.png
对于 playground apps create方式,只能在第一次使用(应用部署上 napptive 之前)。一旦部署好,再次 create会报应用已存在的错误。所以对于应用的更新,不能再次使用 create命令。希望以后 napptive cli 再提供一个 apps update命令吧。由于 napptive 底层仍然是 Kubernetes,所以在 apps update之前,可以使用 kubectl patch deploy的形式去更新镜像的版本号。

建议先执行 kubectl get deploy uni-orders -o yaml的方式查看一下当前的 deploy 文件,然后找到需要 patch 的内容。比如对如下的 deploy
image.png

对应的 patch 命令是这样的: shell kubectl --kubeconfig ~/.napptive/default/napptive-kubeconfig patch deploy uni-orders --patch {spec:{template:{spec:{containers:[{image:jefftian/uni-orders,name:uni-orders}]}}}} --type=merge

总结

通过 Serverless 框架,可以自由地在各大云厂商提供的 lambda, function, 函数计算间切换。而使用 OAM,则可以自由地在各 Kubernetes 集群间切换。