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

原文: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 进行构建,并支持不同的镜像仓库。

相关

comments powered by Disqus