在该系列的第一篇文章中,主要介绍了Knative的基本概念及诞生背景,并提到 Knative 主要由 Build、Serving 和 Eventing 三大核心组件组成,本文将重点 Build 的使用方法。

Knative 系列(二):兵马未动粮草先行之 Build 篇_java

Knative 系列(二):兵马未动粮草先行之 Build 篇_java_02

具体来说,Build 提供源码到容器镜像的构建能力;Serving 提供 Serverless 应用或函数的部署能力,并通过 istio 实现服务管理,同时提供了容器扩缩容能力;Eventing:提供事件触发通道。


Knative 的这三个组件提供了一套完善的 Serverless 方案,让开发者可以只关心自己的代码。提交代码以后,Build 会将源码构建成镜像,Serving 会自动实现应用部署,Eventing 提供事件触发入口,接下来就可以访问到最新应用了。对使用者来说,从源码直接到应用,省去了中间复杂的环节。


Build 完全基于 Kubernetes,设计非常灵活,用户可以利用 Build 定制构建流程,虽然官方文档宣称现在 Knaitve 还不能提供完整的 CI/CD 方案,但是利用 Build 的灵活性完全可以实现 CI 平台搭建,这部分内容会在后面的实现中着重介绍。


本文,我们把 Build 组件拆开揉碎,全面分享给读者。如果现在手头刚好有一个K8s集群,可以跟着文档尝试操作,整个过程甚至不需要占用一杯咖啡的时间。


Build 组件安装

Build 可以作为一个独立组件单独安装,只需要在 K8s 的集群中执行以下命令即可:

   kubectl apply --filename https://github.com/knative/build/releases/download/v0.6.0/build.yaml


执行成功以后,可以通过以下命令查看:

kubectl get pods --namespace knative-build


可以看到,在 knative-build 的 namespace 下,新增了两个容器 build-controller 和 build-webhook,build-controller 是 Build 核心模块,其基本业务流程是监控自己创建的 CRD 资源从而实现构建,build-webhook 和大多数的 webhook 功能一样,通过 webhook 的方式对自己的 api 进行校验。

Knative 系列(二):兵马未动粮草先行之 Build 篇_java_03


到这里恭喜你,Build 组件已经安装成功了,接下来就可以利用它来进行构建。


build 和 buildtemplate

构建之前,我们需要先了解两个概念:build 和 buildtemplate。这里的 build 不是 knative 的 Buid 组件,而是 Build 组件通过 CRD 定义的一个资源文件,build 通过自己定义资源文件去控制构建流程,所以说,Build 完全基于 K8s 生态,只要有 K8s 集群就可以完成构建而不需要依赖其他外部组件,同时,yaml 定义的资源文件也可以很轻易的移植到其他集群上去。


为了方便理解,我们先看一个 build 的 yaml 文件:

apiVersion: build.knative.dev/v1alpha1   

kind: Build 

metadata: 

  name: kaniko-build   

spec:   

  serviceAccountName: build-bot   // 如果源码或者仓库为公开可以不用创建 serviceAccount   

  source:

    git:

      url: https://github.com/my-user/my-repo   // 你的源码仓库地址

      revision: master                          // 你的源码分支地址  

  template:   

    name: kaniko                                 

    arguments:

    - name: IMAGE

      value: us.gcr.io/my-project/my-app         // 构建成功以后镜像存放的镜像仓库地址

   



以下是其中的几个关键配置:

如果想在自己的代码仓库尝试用 build 进行构建,只需要修改上面蓝色的部分即可。看起来,build.yaml 非常简单,而且在 yaml 中也没有看到关于构建过程的体现,是因为在这个 build.yaml 里定义了一个 buildtemplate,buildtemplate 中定义了实际构建过程中的 steps(关于 steps 的内容我们稍后会讲到),当我有多个代码库,或者代码库下有多个分支,抑或是代码想用不同的方式去构建,每次都要定义一个完整的 build 文件就显得非常麻烦,这个时候利用 buildtemplate 可以定义很多构建模板,然后只需要像上面的 build.yaml 一样简单指定代码仓库即可。那么,buildtemplate 是必须的么?当然不是,我们可以直接把 steps 写在 buid.yaml 里,但是 buildtemplate 极大的提高了 build 的灵活性和可移植、可重用性。为了方便快速实现构建,knative 的 build-template 代码库中已经预置了大量的 buildtemplate,包括bazel,buildpacks,kaniko等近十个模板,这里我们先用 Kaniko 的 build-template 体验一下 build 的构建过程。


Kaniko 是 Google 推出的构建工具,它基于 Dockerfile 进行构建但是却不需要 docker daemon,我们先来看一下 Kaniko 的 buildtemplate 是什么样子。

apiVersion: build.knative.dev/v1alpha1   

kind: BuildTemplate   

metadata:   

  name: kaniko   

spec:  

  parameters:

  - name: IMAGE  

    description: The name of the image to push

  - name: DOCKERFILE  

    description: Path to the Dockerfile to build.  

    default: /workspace/Dockerfile  

  steps:

  - name: build-and-push  

    image: gcr.io/kaniko-project/executor   

    args:  

    - --dockerfile=${DOCKERFILE}

    - --destination=${IMAGE} 

    env:   

    - name: DOCKER_CONFIG

      value: /builder/home/.docker


buildtempate 中也有几个关键配置:

Knative 系列(二):兵马未动粮草先行之 Build 篇_java_04

kaniko 的 buildtemplate 十分简单,只有一个 step,然后传入两个参数 DOCKERFILE 和 IMAGE 即可,DOCKERFILE 是工程中 dockerfile 所在的文件路径,IMAGE 是要推送的镜像仓库地址。


这两个文件都准备好以后你只需要执行以下两条命令, 一切顺利的话一段时间时候以后,你就可以在你的镜像仓库里看到你刚刚构建好的镜像了。

kubectl create -f kaniko.yaml   

kubectl create -f build.yaml


如果只是想体验一次 Build 带来的基于原生 K8S 的快捷构建,到这里你的体验之旅就已经结束了,如果想问到底执行完以后发生了什么,那就再花点时间看看到底发生了什么?


Build 构建实现

在上述步骤创建完 build.yaml 以后执行

kubectl get pod


我们发现多出了一个 pod,用 describe 查看 POD 可以看到以下信息:

Name:               kaniko-build-pod-a4f897   

Namespace:          default   

Controlled By:      Build/kaniko-build 

Init Containers:  

build-step-credential-initializer:

Image:          gcr.io/knative-releases/github.com/knative/build/cmd/creds-init@sha256:101f537b53b895b28b84ac3c74ede7d250845e24c51c26516873d8ccb23168ce

  .......

  build-step-git-source-0:

    Image:         gcr.io/knative-releases/github.com/knative/build/cmd/git-init@sha256:ce2c17308e9cb81992be153861c359a0c9e5f69c501a490633c8fe54ec992d53

    Args:

      -url

   https://github.com/lihua871205/2048.git

      -revision

      master 

  ......  

  build-step-build-and-push:

    Image:         gcr.io/kaniko-project/executor

    Args:

      --dockerfile=/workspace/Dockerfile

      --destination=swr.cn-north-1.myhuaweicloud.com/l00283074/sample:latest


POD 里边包含三个 Init Containers:build-step-credential-initializer, build-step-git-source-0 以及 build-step-build-and-push

  • build-step-credential-initializer:如果 git 和 docker 需要认证的对 git 和 docker 进行认证

  • build-step-git-source-0:负责将代码拉取到 workspace 下

  • build-step-build-and-push:是用户自己定义的构建 step,它负责将源码构建成镜像


从这里,我们可能已经猜到 build 的构建实现,利用 Init Containers 的特性,build 先将 source 中的配置生成一个默认的 build-step-git-source-0 接下来再将用户 step 中配置的信息依次解析成多个 Init Containers,当 POD 运行的时候会依次执行所有的 Init Containers 从而实现代码获取,源码构建的流程。这也回答了上面的两个问题:

  1. 为什么每个 step 都要有一个 builder?这是 Init Containers 的特性决定的,即使是 Source 这个步骤其实也是使用了 Build-Controller 内置的一个镜像;

  2. 为什么 build 非常灵活?用户可根据自己的场景,自己定制构建镜像,Build 只提供构建流程,但是不提供构建实现,这也是为什么当前有这么多 buildtemplate 的原因。我们再回顾下 Build 的基本流程:

Knative 系列(二):兵马未动粮草先行之 Build 篇_java_05


总结

Knative 的 Build 利用 init container 实现了一个基于 K8s 生态的构建流程,用户可以自己定制构建场景,或者利用丰富的 build-template 实现构建,它可以作为一个独立的组件减少 K8s 构建的烦恼,更重要的是可以嵌入到 serving 中(偷偷预告一下后面的章节会讲到 build 如何和 serving 集成到一起)形成完整的 Serverless 生态,只有通过 Build 快捷地将源码变成镜像,才能让 serving 有源源不断的“粮草”大展身手。