Skip to main content

Command Palette

Search for a command to run...

在 Knative 中进行应用程序的构建和部署

Updated
6 min read

原文:Building and deploying applications to Knative

作者:Dr Nic Williams

Knative 有三个高级子系统:Serving 用来协调服务 Pod 的自动伸缩以及路由;Build 提供了将代码转换为镜像的工具链;Eventing 则会使用事件的发布订阅来触发松耦合服务。

前一篇文章中我们将一个构件好的容器镜像发布到了 Knative Serving 中。

本篇文章将使用 Knative Build 把我们的应用通过 Dockerfile 以及 Cloud Foundry buildpack 在发布过程中转化为容器镜像。我们还会尝试从本地文件系统以及远程 Git 仓库中进行部署的方式。

前面的操作都很简单,看得出在 Kubernetes 环境中安装 Knative、运行现有容器镜像、使用 curl 与应用进行交互都不难。总结一下就是:

knctl install [--node-ports] [--exclude-monitoring]
knctl deploy --service <service-name> --image <image-name>
knctl curl --service <service-name>

后面的两步 - 把镜像部署为 Kubernetes 并运行,为每个部署进行版本化处理,使用 HTTP 路由进行交互,都是 Serving 子系统的功劳。

Knative 还支持用于构建容器镜像的弹性子系统,构建生成的镜像将会以 Kubernetes Pod 的形式运行。

Knative Build 子系统非常有弹性。我们会探讨集中用例:

  • 上传一个本地目录,使用 Dockerfile 进行构建。

  • 上传一个本地目录,使用 Buildpack 进行构建。

  • 用上面两种方式来构建来自于远程 Git 仓库的代码。

命名空间

前一篇文章中我们在每个 knctl 命令中使用了 --namespace helloworld 参数来显式的指定命名空间。对我来说这样显得更清晰。可能有别的用户希望设置一个缺省命名空间,从而能够缩短命令。

可以给 knctl 配置一个当前命名空间:

kubectl create ns my-simple-app
export KNCTL_NAMESPACE=my-simple-app

所有的 knctl 命令都会使用这一命名空间。

$ knctl service list
Services in namespace 'my-simple-app'

Name  Domain  Annotations  Age

0 services

可以在 kubectl 命令中复用 $KNCTL_NAMESPACE

kubectl get pods -n $KNCTL_NAMESPACE

上传一个带有 Dockerfile 的本地目录

在所有 Knative Build 的示例中,都会产生一个副产品——容器镜像。这些镜像必须放在什么地方,例如 Docker HubGCP 容器库Azure 容器库,或者借助 Harbor 之类的软件自建的私库。

我们需要在每个应用所在的 Kubernetes 命名空间中给 Knative 配置一个镜像库,然后 knctl basic-auth-secret create 给 Knative 配置一个 Secret。

以 Docker Hub 为例,使用 --docker-hub

knctl basic-auth-secret create -s registry --docker-hub -u <username> -p <password>

GCP 容器仓库可以使用 --gcr 选项:

knctl basic-auth-secret create -s registry --gcr -u <username> -p <password>

其它私库可以使用 --type--url

knctl basic-auth-secret create -s registry --type docker --url https://registry.domain.com/ -u <username> -p <password>

下一步把镜像库 Secret 映射到 Kubernetes Service account,它会在 Knative Build 的 Pod 中提供上面的登录信息。

knctl service-account create --service-account build -s registry

这个操作会体现在 Kubernetes 的 Service account 中:

$ kubectl get serviceaccount -n $KNCTL_NAMESPACE
NAME      SECRETS   AGE
build     2         37s
default   1         3h

这样我们就准备好使用 Knative Build 创建新容器镜像所需的认证凭据了。

Clone 一个 Go 应用作为样例,并从它的本地目录推送到 Docker hub:

git clone https://github.com/cppforlife/simple-app
cd simple-app

DOCKER_IMAGE=index.docker.io/<your hub.docker.com org or user>/knative-simple-app

knctl deploy \
    --service simple-app \
    --directory=$PWD \
    --service-account build \
    --image ${DOCKER_IMAGE:?required} \
    --env SIMPLE_MSG="Built from local directory using Dockerfile"

容器的显式命名事实上只是从 Build 到 Serve 的工作过程中的一个中间步骤的副产品,但是必须提供。

knctl deploy 的输出大概是这样的:

Name  simple-app

Waiting for new revision to be created...

Tagging new revision 'simple-app-00001' as 'latest'

Tagging new revision 'simple-app-00001' as 'previous'

[2018-10-15T13:18:31+10:00] Uploading source code...

[2018-10-15T13:19:59+10:00] Finished uploading source code...

Watching build logs...

build-step-build-and-push | INFO[0000] Downloading base image golang:1.10.1
build-step-build-and-push | ERROR: logging before flag.Parse: E1015 03:20:01.547607       1 metadata.go:142] while reading 'google-dockercfg' metadata: http status code: 404 while fetching url http://metadata.google.internal./computeMetadata/v1/instance/attributes/google-dockercfg
build-step-build-and-push | ERROR: logging before flag.Parse: E1015 03:20:01.550268       1 metadata.go:159] while reading 'google-dockercfg-url' metadata: http status code: 404 while fetching url http://metadata.google.internal./computeMetadata/v1/instance/attributes/google-dockercfg-url
build-step-build-and-push | INFO[0001] Executing 0 build triggers
build-step-build-and-push | INFO[0001] Extracting layer 0
build-step-build-and-push | INFO[0003] Extracting layer 1
build-step-build-and-push | INFO[0004] Extracting layer 2
build-step-build-and-push | INFO[0004] Extracting layer 3
build-step-build-and-push | INFO[0007] Extracting layer 4
build-step-build-and-push | INFO[0010] Extracting layer 5
build-step-build-and-push | INFO[0015] Extracting layer 6
build-step-build-and-push | INFO[0015] Taking snapshot of full filesystem...
build-step-build-and-push | INFO[0027] WORKDIR /go/src/github.com/mchmarny/simple-app/
build-step-build-and-push | INFO[0027] cmd: workdir
build-step-build-and-push | INFO[0027] Changed working directory to /go/src/github.com/mchmarny/simple-app/
build-step-build-and-push | INFO[0027] Creating directory /go/src/github.com/mchmarny/simple-app/
build-step-build-and-push | INFO[0027] COPY . .
build-step-build-and-push | INFO[0027] RUN CGO_ENABLED=0 GOOS=linux go build -v -o app
build-step-build-and-push | INFO[0027] cmd: /bin/sh
build-step-build-and-push | INFO[0027] args: [-c CGO_ENABLED=0 GOOS=linux go build -v -o app]
build-step-build-and-push | net
build-step-build-and-push | vendor/golang_org/x/net/lex/httplex
build-step-build-and-push | vendor/golang_org/x/net/proxy
build-step-build-and-push | net/textproto
build-step-build-and-push | crypto/x509
build-step-build-and-push | crypto/tls
build-step-build-and-push | net/http/httptrace
build-step-build-and-push | net/http
build-step-build-and-push | github.com/mchmarny/simple-app
build-step-build-and-push | INFO[0030] Taking snapshot of full filesystem...
build-step-build-and-push | INFO[0034] Storing source image from stage 0 at path /kaniko/stages/0
build-step-build-and-push | INFO[0038] trying to extract to /kaniko/0
build-step-build-and-push | INFO[0038] Extracting layer 0
build-step-build-and-push | INFO[0040] Extracting layer 1
build-step-build-and-push | INFO[0041] Extracting layer 2
build-step-build-and-push | INFO[0041] Extracting layer 3
build-step-build-and-push | INFO[0043] Extracting layer 4
build-step-build-and-push | INFO[0046] Extracting layer 5
build-step-build-and-push | INFO[0051] Extracting layer 6
build-step-build-and-push | INFO[0051] Extracting layer 7
build-step-build-and-push | INFO[0051] Deleting filesystem...
build-step-build-and-push | INFO[0053] No base image, nothing to extract
build-step-build-and-push | INFO[0053] Taking snapshot of full filesystem...
build-step-build-and-push | INFO[0062] COPY --from=0 /go/src/github.com/mchmarny/simple-app/app .
build-step-build-and-push | INFO[0063] Taking snapshot of files...
build-step-build-and-push | INFO[0063] EXPOSE 8080
build-step-build-and-push | INFO[0063] cmd: EXPOSE
build-step-build-and-push | INFO[0063] Adding exposed port: 8080/tcp
build-step-build-and-push | INFO[0063] ENTRYPOINT ["/app"]
build-step-build-and-push | ERROR: logging before flag.Parse: E1015 03:21:04.751338       1 metadata.go:142] while reading 'google-dockercfg' metadata: http status code: 404 while fetching url http://metadata.google.internal./computeMetadata/v1/instance/attributes/google-dockercfg
build-step-build-and-push | ERROR: logging before flag.Parse: E1015 03:21:04.753927       1 metadata.go:159] while reading 'google-dockercfg-url' metadata: http status code: 404 while fetching url http://metadata.google.internal./computeMetadata/v1/instance/attributes/google-dockercfg-url
build-step-build-and-push | 2018/10/15 03:21:06 pushed blob sha256:72a682eea3309941d5e8e6f993a07ae4d33a413b8b7fa2762f8e969310b5996a
build-step-build-and-push | 2018/10/15 03:21:07 pushed blob sha256:9c24aa788ba416c5e1e631d8af3e3115519ad7ca0f659ac10f40682524c6d9cd
build-step-build-and-push | 2018/10/15 03:21:07 index.docker.io/drnic/knative-simple-app:latest: digest: sha256:b5823ead77d9544998b5bc844f049d1a7dfb0aefe7461b74b3e4f67fb5481fa1 size: 428
nop | Nothing to push

Succeeded

Knative Build 的调试

目前 knctl deploy 没有显示任何来自 Knative Build 系统的内部错误或者警告。只需要看着 Waiting for new revision to be created... 坐享其成就可以了。

一个调试方法就是使用 kail 工具处理来自 Knative Build 子系统的消息:

kail -n knative-build

这样就会看到大量的日志,可以再其中查找错误信息,例如 "msg":"Failed the resource specific validation{error 25 0 serviceaccounts \"build\" not found}"

使用 Buildpack 进行构建

我本人很喜欢 Cloud Foundry 和 Heroku 的镜像构建方式,幸运的是,Knative Build 通过自定义构建模板的方式提供了这种支持。

首先用 buildpack 这个名字在活动命名空间中注册一个构建模板

kubectl -n $KNCTL_NAMESPACE apply -f \
https://raw.githubusercontent.com/knative/build-templates/master/buildpack/buildpack.yaml

加入 --template buildpack 就可以使用这一自定义模板了。构建模板所需的附加环境变量都可以用 --template-env NAME=value 的方式进行植入。

例如 Cloud Foundry Go Buildpack 需要 $GOPACKNAME参考文档):

knctl deploy \
    --service simple-app \
    --directory=$PWD \
    --service-account build \
    --image ${DOCKER_IMAGE:?required} \
    --env SIMPLE_MSG="Built from local directory using Buildpack template" \
    --template buildpack \
    --template-env GOPACKAGENAME=main

输出内容和 Cloud Foundry buildpack 是一致的:

Name  simple-app

Waiting for new revision (after revision 'simple-app-00001') to be created...

Tagging new revision 'simple-app-00002' as 'latest'

Tagging older revision 'simple-app-00001' as 'previous'

[2018-10-15T13:40:41+10:00] Uploading source code...

[2018-10-15T13:42:08+10:00] Finished uploading source code...

Watching build logs...

build-step-build | -----> Go Buildpack version 1.8.26
build-step-build | -----> Installing godep 80
build-step-build |        Download [https://buildpacks.cloudfoundry.org/dependencies/godep/godep-v80-linux-x64-cflinuxfs2-06cdb761.tgz]
build-step-build | -----> Installing glide 0.13.1
build-step-build |        Download [https://buildpacks.cloudfoundry.org/dependencies/glide/glide-v0.13.1-linux-x64-cflinuxfs2-aab48c6b.tgz]
build-step-build | -----> Installing dep 0.5.0
build-step-build |        Download [https://buildpacks.cloudfoundry.org/dependencies/dep/dep-v0.5.0-linux-x64-cflinuxfs2-52c14116.tgz]
build-step-build | -----> Installing go 1.8.7
build-step-build |        Download [https://buildpacks.cloudfoundry.org/dependencies/go/go1.8.7.linux-amd64-cflinuxfs2-fff10274.tar.gz]
build-step-build |        **WARNING** Installing package '.' (default)
build-step-build | -----> Running: go install -tags cloudfoundry -buildmode pie .
build-step-export | 2018/10/15 03:47:58 mounted blob: sha256:1124eb40dd68654b8ca8f5d9ec7e439988a4be752a58c8f4e06d60ab1589abdb
build-step-export | 2018/10/15 03:47:58 mounted blob: sha256:6be38da025345ffb57d1ddfcdc5a2bc052be5b9491825f648b49913d51e41acb
build-step-export | 2018/10/15 03:47:58 mounted blob: sha256:a5733e6358eec8957e81b1eb93d48ef94d649d65c69a6b1ac49f616a34a74ac1
build-step-export | 2018/10/15 03:47:58 mounted blob: sha256:21324a9f04e76c93078f3a782e3198d2dded46e4ec77958ddd64f701aecb69c0
build-step-export | 2018/10/15 03:47:59 pushed blob sha256:efa2d34b82bc07588a1a8fd4526322257408109547ee089a792b3f51c383f8e6
build-step-export | 2018/10/15 03:47:59 pushed blob sha256:d495696b33936c79216ec8178726b9fbe915fafbffdd0911a7fdabce4297d9a4
build-step-export | 2018/10/15 03:48:00 index.docker.io/drnic/knative-simple-app:latest: digest: sha256:e5ef1d4d255b4bcbb38d4b43bb6302423c33e6eeabd0e20d5fda4e5ce4c46668 size: 1082
nop | Nothing to push

现在就能看到应用已经部署成功:

$ knctl curl -s simple-app
<h1>Built from local directory using Buildpack template</h1>

私有 Git Secret

前面两节我们从本地上传了源码然后构建了 Docker 镜像(使用 Dockerfile 或 Cloud Foundry buildpack),最后运行应用。

Knative 还能从 Git 仓库获取源码(正式的说法是,Knative Build 只支持从 Git 仓库获取源码,本地代码的支持是 knctl 提供的)。

让 Knative Build 获取 Git 仓库中的代码,需要用 --git-url--git-revision 来取代 --directory=$PWD

如果你的 Git 仓库是私有的,那就还需要在 Service account(在上面的例子中的 build)里包含 Git ssh 凭据。knctl ssh-auth-secret create 能够协助用户创建一个 kubernetes.io/ssh-auth secret。

$ knctl ssh-auth-secret create --secret git --github --private-key "$(cat ~/.ssh/id_rsa)"
Name  git
Type  kubernetes.io/ssh-auth

$ kubectl get secrets -n $KNCTL_NAMESPACE
NAME                  TYPE                                  DATA   AGE
...
git                   kubernetes.io/ssh-auth                1      5m
registry              kubernetes.io/basic-auth              2      3h

现在需要把 git secret 加入到我们的 build Service account 之中了。

在成文之时,knctl 还没有提供 knctl serviceaccounts update 这样的命令,所以需要删除重新创建:

kubectl delete serviceaccounts -n $KNCTL_NAMESPACE build
knctl service-account create --service-account build -s registry -s git

从 Git 部署

--git-url--git-revision 替代 --directory 来进行 Git 部署:

knctl deploy \
    --service simple-app \
    --git-url git@github.com:cppforlife/simple-app.git \
    --git-revision master \
    --service-account build \
    --image ${DOCKER_IMAGE:?required} \
    --env SIMPLE_MSG="Built from Git repo using Buildpack template" \
    --template buildpack \
    --template-env GOPACKAGENAME=main

总结

knctl deploy 命令在 Knative 的基础上提供了创建新镜像的良好体验,可以从本地目录或者 Git 仓库开始,使用 Dockerfile 或 Cloud Foundry buildpack 进行构建,并支持不同的镜像仓库。

More from this blog

龙虾恐慌:AIOps 又要改名了?

ChatGPT 开始,把 AI 拉近到普罗大众的面前,让无数人感受到 AI 的亲民魅力。而龙虾,则把大模型驱动的自动化能力,突然间变得水灵灵、活泼泼地走进千家万户。它不只是“风口上的猪”,而是风口本身。热度高到让 Mac mini 一度断货,不知道这在不在库克的预料之内。 每代人都有每代人的鸡蛋,春节期间,我就领了我的鸡蛋。翻出古老的 MacBook Air M1,充值各种大模型。当然了,这个工具

Mar 9, 20261 min read

再见 2025

我猜不少人以为这个号废了吧?并没有,只是今年变化有点大,一直有种抄起键盘,无从说起的感觉,所以一直偷懒到今天,2025 的最后一天。 今年是我的第四个本命年,去年末一期播客里,大内说本命年不是灾年,是变化年,有危也有机。可是讲真啊,只看到危,没看到机。 各种因缘际会,从鹅厂跳槽到前东家,已经接近四年,第一个合同期已经进入尾声。除了前两年还在云原生领域嗷嗷叫,后两年基本都是些鸡零狗碎的东西了,用老东家的术语说是——偏离主航道,可谓是前景暗淡了。 一旦确定要滚蛋,反倒心思轻松起来,每天骑着我的小红车...

Jan 5, 20261 min read

辅助编程?dora 说:我知道你很急可是请你别急

从 OpenGPT 把大模型的火烧旺了之后,这三年来,相信很多组织或摩拳擦掌、或躬身入局,希望借助聪明能干的大模型,或想偿还技术宅,或想降本增效,或想弯道超车。一时间,沉寂许久的 AIxx 又活过来了,LLM Ops、Vibe Coding、中医大模型、GPT 算命等等,全都老树发新芽,焕发了勃勃生机。那么视角拉回从业者最关注的饭碗相关的领域之一——AI 辅助开发,产生了什么触动,应该如何拥抱呢? DORA 的年度报告中给出了很有意思的结论——强者恒强。 执行摘要部分总结了几个有趣的点: 问题...

Oct 6, 20251 min read

[译]dora:ai 辅助软件开发状态报告

执行摘要 在 2025 年,科技领导者面临的核心问题已不再是“是否要采用 AI”,而是“如何实现其价值”。 DORA 的研究基于超过 100 小时的定性访谈和来自全球近 5,000 名技术专业人士的问卷调查。研究揭示了一个关键事实:AI 在软件开发中的主要角色是“放大器”。它会放大高效能组织的优势,也会凸显组织的缺陷。 关键结论:AI 是放大器 AI 投资的最大回报并非来自工具本身,而是来自组织底层系统的战略性建设: 高质量的内部平台 清晰的工作流 团队的协同能力 缺少这些基础,AI ...

Oct 2, 202514 min read

僭越了,有人在用 Rust 写 Kubernetes

一个新语言问世,最爱做的事情之一,就是重写存量软件了。 云原生喝酒 SIG 重点扶持项目——rk8s(https://github.com/rk8s-dev/rk8s) 也可以归在这个范畴里,只不过这个项目重写的东西比较大,是 Kubernetes。 从 2025 年 1 月第一个 Commit 开始,到现在有了 200 多次 Commit,十几万行代码。当然距离 Kubernetes 的几百万行代码还差得远——老马就是喜欢整这种大无畏项目。 另外该项目也是国内第一个脱离 Cargo 转向使用 ...

Sep 27, 20253 min read

【伪】架构师

342 posts