"Cloud Native Supply Chains"と呼ばれるCartographerがオープンソース化されたので、早速試してみます。 Cartographerの概要を説明した後に、今回はCartographerをkindにインストールし、PaaSのような感覚で"Source to URL"でアプリケーションを簡単デプロイできることを体験します。
目次
Cartographerとは
Cartographerがやってくれることを簡単にいうと、任意のk8sリソースのoutput(status)を使って次のk8sリソースに反映させるという橋渡しを行い、アプリケーションをリリースするための"Supply Chain"を提供することです。 例えばgitにpushしたソースコードをpullしてコンテナイメージを作成し、そのイメージをマニフェストに反映してデプロイするというフローを実現したい場合、
- Flux の GitRepository リソースがgitレポジトリがwatchし、変更があればソースコードをtar.gzファイルに圧縮してファイルサーバーに保存する
- kpack の Image リソースが1.のURLからソースコードを取得し、 Cloud Native Buildpacks を使用してコンテナイメージを作成し、Dockerレジストリにイメージをpushする (Source to Image)
- Knative の Service リソースが2.のimageを使用してアプリケーションをデプロイする (Image to URL)
という連携が考えられますが、Cartographerはこれらのリソースの変更をwatchし、↓の図のようにリソース間の値の受け渡しを自動で行います。
例えば
- watchしているgitリポジトリに対して新しいcommitがpushされることにより 、GitRepositoryリソースの
.status.artifact.url
が変更されれば、その値を自動でImageリソースの.spec.source.blob.url
に設定する - 1.の後や、kpackの ClusterBuilder や ClusterStack (OSのベースイメージ)がアップデートされることにより、Imageリソースの
.status.latestImage
が変更されれば、その値を自動でServiceリソースの.spec.template.spec.containers[0].image
に設定する
ということをCartographerが行います。 これらの組み合わせや値の受け渡し方法は自由にカスタマイズできて、下の図のように使い慣れたコンポーネントを選択して、組織にあった"Supply Chain"を定義することができます。 k8sリソースの入出力がfitすれば、どんなコンポーネントでも間に挟むことができます。例えばコンテナイメージをビルドする前にユニットテストを実行したり、イメージをビルドした後にイメージをスキャンすることもできます。
ClusterSupplyChain リソースにこのSupply Chainを定義できます。またSupply Chainへの入力(e.g. gitリポジトリのURLなど)を Workload リソースに定義できます。
(図は https://tanzu.vmware.com/content/blog/vmware-tanzu-application-platform-beta-2-announcement より)
同じようなことをCI/CDツールを組み合わせて既に行っている組織も多いのではないかと思いますが、 Cartographerの良いところはDeveloperとOperatorの責任の分離を明確に行っている点です。
下の図のように、Operatorが定義したClusterSupplyChainを使ってDeveloperがWorkloadを定義しアプリケーションをデプロイできます。
(図は https://cartographer.sh/docs/v0.0.6/ より)
CI/CDツールの組み合わせの場合は、 "誰がマニフェストやCIパイプラインを作るのか"や"Operatorがテンプレートを配布し、Developerがそれをカスタマイズする運用をするとテンプレートの変更を反映するのが大変"という課題を解決することができます。
Getting Started
前置きが長くなりましたが、早速試してみます。
ここではKnativeの代わりにシンプルに標準のDeployment・Service・Ingressを使用します。Supply Chainは次の図のようになります。
kindクラスタの作成
まずはkindクラスタを作成します。extraPortMappings
を設定してLaptop上の80/443ポートを通じてIngressにアクセスできるようにします。
curl -sL https://github.com/projectcontour/contour/raw/main/examples/kind/kind-expose-port.yaml > kind-expose-port.yaml
kind create cluster --config kind-expose-port.yaml
コンポーネント群のインストール
今回のSupply Chainを作るために必要な次のコンポーネント群をインストールします。
kubectl apply -f https://github.com/jetstack/cert-manager/releases/download/v1.5.4/cert-manager.yaml
kubectl apply -f https://github.com/pivotal/kpack/releases/download/v0.4.0/release-0.4.0.yaml
kubectl create namespace gitops-toolkit
kubectl create clusterrolebinding gitops-toolkit-admin --clusterrole=cluster-admin --serviceaccount=gitops-toolkit:default
kubectl apply -n gitops-toolkit -f https://github.com/fluxcd/source-controller/releases/download/v0.15.4/source-controller.crds.yaml -f https://github.com/fluxcd/source-controller/releases/download/v0.15.4/source-controller.deployment.yaml
kubectl apply -f https://projectcontour.io/quickstart/contour.yaml
インストール後のPod一覧は次の通りです。
$ kubectl get pod -A
NAMESPACE NAME READY STATUS RESTARTS AGE
cert-manager cert-manager-7c6f78c46d-xncwh 1/1 Running 0 77s
cert-manager cert-manager-cainjector-668d9c86df-dskgc 1/1 Running 0 77s
cert-manager cert-manager-webhook-764b556954-zfb7c 1/1 Running 0 77s
gitops-toolkit source-controller-764686cbbd-tjpk2 1/1 Running 0 76s
kpack kpack-controller-98c4fd6c7-qld5f 1/1 Running 0 77s
kpack kpack-webhook-65d59b7ffd-d2x8l 1/1 Running 0 77s
kube-system coredns-558bd4d5db-gjlmc 1/1 Running 0 77s
kube-system coredns-558bd4d5db-xlp85 1/1 Running 0 77s
kube-system etcd-kind-control-plane 1/1 Running 0 86s
kube-system kindnet-zrzgv 1/1 Running 0 77s
kube-system kube-apiserver-kind-control-plane 1/1 Running 0 86s
kube-system kube-controller-manager-kind-control-plane 1/1 Running 0 93s
kube-system kube-proxy-mq9fx 1/1 Running 0 77s
kube-system kube-scheduler-kind-control-plane 1/1 Running 0 86s
local-path-storage local-path-provisioner-547f784dff-qb9jx 1/1 Running 0 77s
projectcontour contour-7cc4786577-gpvgn 1/1 Running 0 75s
projectcontour contour-7cc4786577-pmwch 1/1 Running 0 75s
projectcontour envoy-n7q6l 2/2 Running 0 66s
Cartographerのインストール
次にCartographerをインストールします。
ドキュメント の通りにインストールしますが、 現時点のドキュメントは Carvel を使った手順のみ用意されています。 この方法は Air-gapped環境 にもインストール可能な、応用の効くやり方なのですが、少し手間がかかります。
こちらのissue の通り、 kubectl apply -f cartographer.yaml
でインストールできる方法は今度準備するようです。
Carvel Toolsのインストール
まずは Carvel のツール群をインストールします。
brew tap vmware-tanzu/carvel
brew install ytt kbld kapp imgpkg kwt vendir
あるいは
curl -sL https://carvel.dev/install.sh | bash
を実行してください。
本記事では imgpkg
、kbld
、kapp
を使用します。次のバージョンで動作確認しています。
$ imgpkg version
imgpkg version 0.19.0
Succeeded
$ kbld version
kbld version 0.31.0
Succeeded
$ kapp version
kapp version 0.42.0
Carvelを使ったCartographerのインストール
バージョン 0.0.6 時点では、Cartographerのマニフェスト及びコンテナイメージは imgpkg のtar形式で配布されています。 これはコンテナイメージを別途コンテナレジストリにrelocationする想定です。
ここではコンテナレジストリとして、GitHub Packages(ghcr.io)を使用します。 このコンテナレジストリは後ほど設定するkpackでも使用するので、ユーザー名とパスワードを環境変数に設定します。
export GITHUB_USERNAME=making
export GITHUB_API_TOKEN=*************
docker login ghcr.io -u ${GITHUB_USERNAME} -p ${GITHUB_API_TOKEN}
次のコマンドでCartographerのtarファイルをダウンロードして、imgpkg
コマンドを使用してGitHub Packagesへrelocationします。
curl -sL https://github.com/vmware-tanzu/cartographer/releases/download/v0.0.6/bundle.tar > /tmp/bundle.tar
imgpkg copy --tar /tmp/bundle.tar --to-repo ghcr.io/${GITHUB_USERNAME}/cartographer-bundle --lock-output /tmp/cartographer-bundle.lock.yaml
次のようなログが出力されます。
copy | importing 2 images...
35.20 MiB / 35.27 MiB [===================================================================================================================================================================================================================================================] 99.80% 3.20 MiB/s 10s
copy | done uploading images
Succeeded
GitHub Packagesを確認すると cartographer-bundle
というイメージがアップロードされていることがわかります。
次に、次のコマンドを実行して、relocation後のimageの設定が反映されたmanifestファイル群を取得します。
imgpkg pull --lock /tmp/cartographer-bundle.lock.yaml --output /tmp/cartographer
次のようなログが出力されます。
Pulling bundle 'ghcr.io/making/cartographer-bundle@sha256:f7e402cfa7c9404afdb51f9c6967c688e2595660b9335429ed5a880fc2caa80f'
Extracting layer 'sha256:ec8f865d6051db9570e067cf3604a19face114a6d1e8920b61bae44eae64078d' (1/1)
Locating image lock file images...
The bundle repo (ghcr.io/making/cartographer-bundle) is hosting every image specified in the bundle's Images Lock file (.imgpkg/images.yml)
Succeeded
次のようなファイルが取得されます。imgpkgの bundle形式 です。
$ find /tmp/cartographer
/tmp/cartographer
/tmp/cartographer/.imgpkg
/tmp/cartographer/.imgpkg/images.yml
/tmp/cartographer/config
/tmp/cartographer/config/cartographer.yaml
/tmp/cartographer/config/overlays
/tmp/cartographer/config/overlays/.gitkeep
/tmp/cartographer/config/objects
/tmp/cartographer/config/objects/kapp-secret-ignore.yaml
次のコマンドでこのmanifestをデプロイします。ここではkbld
とkapp
コマンドを使用します。
kubectl create namespace cartographer-system
kubectl create secret -n cartographer-system docker-registry private-registry-credentials --docker-server=ghcr.io --docker-username=${GITHUB_USERNAME} --docker-password=${GITHUB_API_TOKEN}
kbld -f /tmp/cartographer | kapp deploy -a cartographer -c -f - -y
インストールが完了すれば、次のコマンドを実行してどのようなリソースが作成されたか確認できます。
$ kapp inspect -a cartographer -t
Target cluster 'https://127.0.0.1:51332' (nodes: kind-control-plane)
Resources in app 'cartographer'
Namespace Name Kind Owner Conds. Rs Ri Age
(cluster) clusterimagetemplates.carto.run CustomResourceDefinition kapp 2/2 t ok - 30s
(cluster) clustersourcetemplates.carto.run CustomResourceDefinition kapp 2/2 t ok - 30s
cartographer-system cartographer-webhook Certificate kapp 1/1 t ok - 29s
cartographer-system L cartographer-webhook-chq25 CertificateRequest cluster 2/2 t ok - 28s
(cluster) pipelines.carto.run CustomResourceDefinition kapp 2/2 t ok - 30s
(cluster) clustersupplychainvalidator ValidatingWebhookConfiguration kapp - ok - 30s
cartographer-system cartographer-webhook Secret kapp - ok - 30s
(cluster) clustersupplychains.carto.run CustomResourceDefinition kapp 2/2 t ok - 30s
(cluster) clusterconfigtemplates.carto.run CustomResourceDefinition kapp 2/2 t ok - 30s
cartographer-system cartographer-webhook Service kapp - ok - 29s
cartographer-system L cartographer-webhook Endpoints cluster - ok - 29s
cartographer-system L cartographer-webhook-kd7mx EndpointSlice cluster - ok - 29s
(cluster) clustertemplates.carto.run CustomResourceDefinition kapp 2/2 t ok - 30s
(cluster) workloads.carto.run CustomResourceDefinition kapp 2/2 t ok - 30s
(cluster) cartographer-cluster-admin ClusterRoleBinding kapp - ok - 30s
cartographer-system cartographer-controller ServiceAccount kapp - ok - 30s
cartographer-system selfsigned-issuer Issuer kapp 1/1 t ok - 29s
cartographer-system cartographer-controller Deployment kapp 2/2 t ok - 29s
cartographer-system L cartographer-controller-f664d5bc ReplicaSet cluster - ok - 29s
cartographer-system L.. cartographer-controller-f664d5bc-vhg64 Pod cluster 4/4 t ok - 29s
cartographer-system private-registry-credentials Secret kapp - ok - 30s
(cluster) runtemplates.carto.run CustomResourceDefinition kapp 2/2 t ok - 30s
Rs: Reconcile state
Ri: Reconcile information
22 resources
Succeeded
kpackの設定
次にkpackの設定を行います。
imagePullSecretの設定
default
namespaceのdefault
service accountに対してghcr.ioへのimagePullSecret
を設定しておきます。
cat <<EOF > dockerconfig.yaml
apiVersion: v1
kind: Secret
metadata:
name: regsecret
namespace: default
type: kubernetes.io/dockerconfigjson
stringData:
.dockerconfigjson: |
{
"auths": {
"https://ghcr.io": {
"username": "${GITHUB_USERNAME}",
"password": "${GITHUB_API_TOKEN}"
}
}
}
EOF
kubectl apply -f dockerconfig.yaml
kubectl patch -n default serviceaccount default -p "{\"secrets\":[{\"name\":\"regsecret\"}],\"imagePullSecrets\":[{\"name\":\"regsecret\"}]}"
ClusterBuilderの設定
Paketo Buildpacks を使ったClusterBuilder及び、ClusterStore、ClusterStackを定義します。 ここではGo、Java、Node.js、Rubyのみ対応します。
cat <<EOF > clusterbuilder.yaml
apiVersion: kpack.io/v1alpha1
kind: ClusterStore
metadata:
name: default
spec:
sources:
- image: gcr.io/paketo-buildpacks/ruby
- image: gcr.io/paketo-buildpacks/nodejs
- image: gcr.io/paketo-buildpacks/go
- image: gcr.io/paketo-buildpacks/java
---
apiVersion: kpack.io/v1alpha1
kind: ClusterStack
metadata:
name: base
spec:
id: io.buildpacks.stacks.bionic
buildImage:
image: paketobuildpacks/build:base-cnb
runImage:
image: paketobuildpacks/run:base-cnb
---
apiVersion: kpack.io/v1alpha1
kind: ClusterBuilder
metadata:
name: base
spec:
serviceAccountRef:
name: default
namespace: default
tag: ghcr.io/${GITHUB_USERNAME}/clusterbuilder:base
stack:
name: base
kind: ClusterStack
store:
name: default
kind: ClusterStore
order:
- group:
- id: paketo-buildpacks/ruby
- group:
- id: paketo-buildpacks/nodejs
- group:
- id: paketo-buildpacks/go
- group:
- id: paketo-buildpacks/java
EOF
kubectl apply -f clusterbuilder.yaml
しばらくするとClusterBuilderに対応するbuilderのイメージがアップロードされます。
$ kubectl get clusterbuilder
NAME LATESTIMAGE READY
base ghcr.io/making/clusterbuilder:base@sha256:c3640f0f6e1643e692d7f2abacaee766dfb7f76fa84adc1bdc6ecdc5dcc29cfa True
GitHub Packagesでそのイメージを確認できます。
⚠️ GitHub PackagesのFreeプランを使用する場合、private repositoryに対するストーレジと転送量の制約があるため、場合によってはpublic repositoryへ変更した方が良いかもしれません。
Supply Chainの設定
いよいよCartographerのSupply Chainを定義します。再掲になりますが、次のようなSupply Chainを作成します。
CartographerではSupply Chainを構成するテンプレートとして次の4種類を利用できます。
- ClusterSourceTemplate ... outputがソースコードの情報(urlとrevision)であるk8sリソースのテンプレート定義
- ClusterImageTemplate ... outputがコンテナイメージの情報(image name)であるk8sリソースのテンプレート定義
- ClusterConfigTemplate ... outputが修正されたconfigであるk8sリソースのテンプレート定義
- ClusterTemplate ... outputを必要としないk8sリソースのテンプレート定義
上記の例では
- GitRepositoryリソースのテンプレート定義 -> ClusterSourceTemplate
- Imageリソースのテンプレート定義 -> ClusterImageTemplate
- Deploy, Service, Ingressリソースのテンプレート定義 -> ClusterTemplate
に対応します。
Templatesの作成
各種テンプレートを次のように作成します。
cat <<'EOF' | sed "s/CHANGE_ME/${GITHUB_USERNAME}/" > supply-chain-templates.yaml
apiVersion: carto.run/v1alpha1
kind: ClusterSourceTemplate
metadata:
name: source
spec:
urlPath: .status.artifact.url
revisionPath: .status.artifact.revision
template:
apiVersion: source.toolkit.fluxcd.io/v1beta1
kind: GitRepository
metadata:
name: $(workload.metadata.name)$
labels:
app.kubernetes.io/part-of: $(workload.metadata.name)$
spec:
interval: 3m
url: $(workload.spec.source.git.url)$
ref: $(workload.spec.source.git.ref)$
ignore: ""
---
apiVersion: carto.run/v1alpha1
kind: ClusterImageTemplate
metadata:
name: image
spec:
imagePath: .status.latestImage
template:
apiVersion: kpack.io/v1alpha1
kind: Image
metadata:
name: $(workload.metadata.name)$
labels:
app.kubernetes.io/part-of: $(workload.metadata.name)$
spec:
tag: ghcr.io/CHANGE_ME/$(workload.metadata.name)$
serviceAccount: default
builder:
kind: ClusterBuilder
name: base
source:
blob:
url: $(sources.source.url)$
---
apiVersion: carto.run/v1alpha1
kind: ClusterTemplate
metadata:
name: app-deploy-deployment
spec:
template:
apiVersion: apps/v1
kind: Deployment
metadata:
name: $(workload.metadata.name)$
labels:
app.kubernetes.io/part-of: $(workload.metadata.name)$
spec:
template:
metadata:
labels:
app.kubernetes.io/part-of: $(workload.metadata.name)$
spec:
containers:
- name: workload
image: $(images.image.image)$
env: $(workload.spec.env)$
resources: $(workload.spec.resources)$
ports:
- name: http-port
containerPort: 8080
securityContext:
runAsUser: 1000
selector:
matchLabels:
app.kubernetes.io/part-of: $(workload.metadata.name)$
---
apiVersion: carto.run/v1alpha1
kind: ClusterTemplate
metadata:
name: app-deploy-service
spec:
template:
apiVersion: v1
kind: Service
metadata:
name: $(workload.metadata.name)$
labels:
app.kubernetes.io/part-of: $(workload.metadata.name)$
spec:
ports:
- name: http
port: 80
protocol: TCP
targetPort: 8080
selector:
app.kubernetes.io/part-of: $(workload.metadata.name)$
type: ClusterIP
---
apiVersion: carto.run/v1alpha1
kind: ClusterTemplate
metadata:
name: app-deploy-ingress
spec:
template:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: $(workload.metadata.name)$
labels:
app.kubernetes.io/part-of: $(workload.metadata.name)$
spec:
rules:
- host: $(workload.metadata.name)$-127-0-0-1.sslip.io
http:
paths:
- backend:
service:
name: $(workload.metadata.name)$
port:
number: 80
path: /
pathType: Exact
EOF
kubectl apply -f supply-chain-templates.yaml
SupplyChainの定義
定義したテンプレートを組み合わせたSupply ChainをClusterSupplyChainリソースとして次のように定義します。
cat <<'EOF' > supply-chain.yaml
apiVersion: carto.run/v1alpha1
kind: ClusterSupplyChain
metadata:
name: supply-chain
spec:
selector:
app.tanzu.vmware.com/workload-type: web
components:
- name: source-provider
templateRef:
kind: ClusterSourceTemplate
name: source
- name: image-builder
templateRef:
kind: ClusterImageTemplate
name: image
sources:
- component: source-provider
name: source
- name: deployer-deployment
templateRef:
kind: ClusterTemplate
name: app-deploy-deployment
images:
- component: image-builder
name: image
- name: deployer-service
templateRef:
kind: ClusterTemplate
name: app-deploy-service
- name: deployer-ingress
templateRef:
kind: ClusterTemplate
name: app-deploy-ingress
EOF
kubectl apply -f supply-chain.yaml
ここまではOperatorの役目です。
Workloadのデプロイ
このSupply Chainを使って、いよいよアプリケーションをデプロイします。ここからはDeveloperの役目です。
Goアプリのデプロイ
まずはGoのアプリをデプロイします。デプロイするサンプルアプリとして https://github.com/making/hello-golang を使用します。
次のWorkloadを作成します。Developerとして定義する内容はこれだけです。どのようにコンテナイメージが作られてどのようにk8sへデプロイするかを意識する必要がありません。
cat <<EOF > hello-golang.yaml
apiVersion: carto.run/v1alpha1
kind: Workload
metadata:
labels:
app.tanzu.vmware.com/workload-type: web
name: hello-golang
spec:
source:
git:
url: https://github.com/making/hello-golang.git
ref:
branch: master
env:
- name: TARGET
value: World
resources: {}
EOF
kubectl apply -f hello-golang.yaml
実際にどのような処理が行われているかをログで確認します。複数のコンテナで処理が行われるので stern を使用すると良いです。
ソースコード(tar.gz)が取得され、そのソースコードがGoのものであることが検出され、ビルドが行われて、イメージがpushされ、その後アプリケーションが起動していることがわかります。
$ stern 'hello-golang.*'
+ hello-golang-build-1-rqzsd-build-pod › prepare
hello-golang-build-1-rqzsd-build-pod prepare Build reason(s): CONFIG
hello-golang-build-1-rqzsd-build-pod prepare CONFIG:
hello-golang-build-1-rqzsd-build-pod prepare resources: {}
hello-golang-build-1-rqzsd-build-pod prepare - source: {}
hello-golang-build-1-rqzsd-build-pod prepare + source:
hello-golang-build-1-rqzsd-build-pod prepare + blob:
hello-golang-build-1-rqzsd-build-pod prepare + url: http://source-controller.gitops-toolkit.svc.cluster.local./gitrepository/default/hello-golang/1f8f29d92011f329b695223a35bf3f480c05f8a2.tar.gz
hello-golang-build-1-rqzsd-build-pod prepare Loading secret for "https://ghcr.io" from secret "regsecret" at location "/var/build-secrets/regsecret"
hello-golang-build-1-rqzsd-build-pod prepare Downloading source-controller.gitops-toolkit.svc.cluster.local./gitrepository/default/hello-golang/1f8f29d92011f329b695223a35bf3f480c05f8a2.tar.gz...
hello-golang-build-1-rqzsd-build-pod prepare Successfully downloaded source-controller.gitops-toolkit.svc.cluster.local./gitrepository/default/hello-golang/1f8f29d92011f329b695223a35bf3f480c05f8a2.tar.gz in path "/workspace"
- hello-golang-build-1-rqzsd-build-pod › prepare
+ hello-golang-build-1-rqzsd-build-pod › detect
hello-golang-build-1-rqzsd-build-pod detect 4 of 7 buildpacks participating
hello-golang-build-1-rqzsd-build-pod detect paketo-buildpacks/ca-certificates 2.3.2
hello-golang-build-1-rqzsd-build-pod detect paketo-buildpacks/go-dist 0.6.0
hello-golang-build-1-rqzsd-build-pod detect paketo-buildpacks/go-mod-vendor 0.3.1
hello-golang-build-1-rqzsd-build-pod detect paketo-buildpacks/go-build 0.4.1
- hello-golang-build-1-rqzsd-build-pod › detect
+ hello-golang-build-1-rqzsd-build-pod › analyze
hello-golang-build-1-rqzsd-build-pod analyze Previous image with name "ghcr.io/making/hello-golang" not found
- hello-golang-build-1-rqzsd-build-pod › analyze
+ hello-golang-build-1-rqzsd-build-pod › build
hello-golang-build-1-rqzsd-build-pod build
hello-golang-build-1-rqzsd-build-pod build Paketo CA Certificates Buildpack 2.3.2
hello-golang-build-1-rqzsd-build-pod build https://github.com/paketo-buildpacks/ca-certificates
hello-golang-build-1-rqzsd-build-pod build Launch Helper: Contributing to layer
hello-golang-build-1-rqzsd-build-pod build Creating /layers/paketo-buildpacks_ca-certificates/helper/exec.d/ca-certificates-helper
hello-golang-build-1-rqzsd-build-pod build Paketo Go Distribution Buildpack 0.6.0
hello-golang-build-1-rqzsd-build-pod build Resolving Go version
hello-golang-build-1-rqzsd-build-pod build Candidate version sources (in priority order):
hello-golang-build-1-rqzsd-build-pod build go.mod -> ">= 1.16"
hello-golang-build-1-rqzsd-build-pod build <unknown> -> ""
hello-golang-build-1-rqzsd-build-pod build
hello-golang-build-1-rqzsd-build-pod build Selected Go version (using go.mod): 1.17
hello-golang-build-1-rqzsd-build-pod build
hello-golang-build-1-rqzsd-build-pod build Executing build process
hello-golang-build-1-rqzsd-build-pod build Installing Go 1.17
hello-golang-build-1-rqzsd-build-pod build Completed in 7.806s
hello-golang-build-1-rqzsd-build-pod build
hello-golang-build-1-rqzsd-build-pod build Paketo Go Mod Vendor Buildpack 0.3.1
hello-golang-build-1-rqzsd-build-pod build Checking module graph
hello-golang-build-1-rqzsd-build-pod build Running 'go mod graph'
hello-golang-build-1-rqzsd-build-pod build Completed in 6ms
hello-golang-build-1-rqzsd-build-pod build
hello-golang-build-1-rqzsd-build-pod build Skipping build process: module graph is empty
hello-golang-build-1-rqzsd-build-pod build
hello-golang-build-1-rqzsd-build-pod build Paketo Go Build Buildpack 0.4.1
hello-golang-build-1-rqzsd-build-pod build Executing build process
hello-golang-build-1-rqzsd-build-pod build Running 'go build -o /layers/paketo-buildpacks_go-build/targets/bin -buildmode pie .'
hello-golang-build-1-rqzsd-build-pod build Completed in 15.708s
hello-golang-build-1-rqzsd-build-pod build
hello-golang-build-1-rqzsd-build-pod build Assigning launch processes:
hello-golang-build-1-rqzsd-build-pod build web: /layers/paketo-buildpacks_go-build/targets/bin/hello-golang
hello-golang-build-1-rqzsd-build-pod build hello-golang: /layers/paketo-buildpacks_go-build/targets/bin/hello-golang
hello-golang-build-1-rqzsd-build-pod build
- hello-golang-build-1-rqzsd-build-pod › build
+ hello-golang-build-1-rqzsd-build-pod › export
hello-golang-build-1-rqzsd-build-pod export Adding layer 'paketo-buildpacks/ca-certificates:helper'
hello-golang-build-1-rqzsd-build-pod export Adding layer 'paketo-buildpacks/go-build:targets'
hello-golang-build-1-rqzsd-build-pod export Adding 1/1 app layer(s)
hello-golang-build-1-rqzsd-build-pod export Adding layer 'launcher'
hello-golang-build-1-rqzsd-build-pod export Adding layer 'config'
hello-golang-build-1-rqzsd-build-pod export Adding layer 'process-types'
hello-golang-build-1-rqzsd-build-pod export Adding label 'io.buildpacks.lifecycle.metadata'
hello-golang-build-1-rqzsd-build-pod export Adding label 'io.buildpacks.build.metadata'
hello-golang-build-1-rqzsd-build-pod export Adding label 'io.buildpacks.project.metadata'
hello-golang-build-1-rqzsd-build-pod export Setting default process type 'web'
hello-golang-build-1-rqzsd-build-pod export Saving ghcr.io/making/hello-golang...
hello-golang-build-1-rqzsd-build-pod export *** Images (sha256:9cfb82e456bdff7e0b2dea85b48235da56b12667a52fb865de9067132fb2071a):
hello-golang-build-1-rqzsd-build-pod export ghcr.io/making/hello-golang
hello-golang-build-1-rqzsd-build-pod export ghcr.io/making/hello-golang:b1.20211006.005809
hello-golang-build-1-rqzsd-build-pod export Adding cache layer 'paketo-buildpacks/go-dist:go'
hello-golang-build-1-rqzsd-build-pod export Adding cache layer 'paketo-buildpacks/go-build:gocache'
- hello-golang-build-1-rqzsd-build-pod › export
+ hello-golang-566c6c75c7-b6tdr › workload
hello-golang-566c6c75c7-b6tdr workload 2021/10/06 00:59:38 helloworld: starting server...
hello-golang-566c6c75c7-b6tdr workload 2021/10/06 00:59:38 helloworld: listening on port 8080
このWorkloadからSupply Chainを経由して作成されるリソースを次のコマンドで確認します。
$ kubectl get workload,deploy,pod,image,build,gitrepo,service,ingress
NAME AGE
workload.carto.run/hello-golang 2m31s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/hello-golang 1/1 1 1 61s
NAME READY STATUS RESTARTS AGE
pod/hello-golang-566c6c75c7-b6tdr 1/1 Running 0 61s
pod/hello-golang-build-1-rqzsd-build-pod 0/1 Completed 0 2m25s
NAME LATESTIMAGE READY
image.kpack.io/hello-golang ghcr.io/making/hello-golang@sha256:9cfb82e456bdff7e0b2dea85b48235da56b12667a52fb865de9067132fb2071a True
NAME IMAGE SUCCEEDED
build.kpack.io/hello-golang-build-1-rqzsd ghcr.io/making/hello-golang@sha256:9cfb82e456bdff7e0b2dea85b48235da56b12667a52fb865de9067132fb2071a True
NAME URL READY STATUS AGE
gitrepository.source.toolkit.fluxcd.io/hello-golang https://github.com/making/hello-golang.git True Fetched revision: master/1f8f29d92011f329b695223a35bf3f480c05f8a2 2m31s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/hello-golang ClusterIP 10.96.31.196 <none> 80/TCP 61s
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 7h54m
NAME CLASS HOSTS ADDRESS PORTS AGE
ingress.networking.k8s.io/hello-golang <none> hello-golang-127-0-0-1.sslip.io 80 61s
IngressのURL( http://hello-golang-127-0-0-1.sslip.io )にアクセスします。
$ curl http://hello-golang-127-0-0-1.sslip.io
Hello World!
"Hello World!"が返りました。
WorkloadにはソースコードのURLしか書いていないですが、アプリがURLでアクセスなところまで用意されました。 これはPaaSで実現されていた"Source to URL"に近い体験です。
ここでは割愛しますが、ソースコードを変更してgit pushすれば新しいイメージが作成されて、再デプロイされます。
Workloadを削除すれば、各種リソースも削除されます。
kubectl delete -f hello-golang.yaml
Javaアプリのデプロイ
次にJavaのアプリをデプロイします。デプロイするサンプルアプリとして https://github.com/tanzu-japan/hello-tanzu を使用します。
次のWorkloadを作成します。
cat <<EOF > hello-tanzu.yaml
apiVersion: carto.run/v1alpha1
kind: Workload
metadata:
labels:
app.tanzu.vmware.com/workload-type: web
name: hello-tanzu
spec:
source:
git:
url: https://github.com/tanzu-japan/hello-tanzu.git
ref:
branch: main
env: []
resources: {}
EOF
kubectl apply -f hello-tanzu.yaml
同様にsternでログを確認すれば、ソースコード取得後Mavenによるビルドが行われ、イメージ作成後にデプロイされてTomcatが起動していることがわかります。 なお、Mavenによるビルドは2回目移行はキャッシュされます。
stern 'hello-tanzu.*'
ログはこちら
このWorkloadからSupply Chainを経由して作成されるリソースを次のコマンドで確認します。
$ kubectl get workload,deploy,pod,image,build,gitrepo,service,ingress
NAME AGE
workload.carto.run/hello-tanzu 12m
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/hello-tanzu 1/1 1 1 9m43s
NAME READY STATUS RESTARTS AGE
pod/hello-tanzu-54dd5899f4-hsxb6 1/1 Running 0 9m43s
pod/hello-tanzu-build-1-fvt6g-build-pod 0/1 Completed 0 11m
NAME LATESTIMAGE READY
image.kpack.io/hello-tanzu ghcr.io/making/hello-tanzu@sha256:ab5c70080c2b8a73b67d90b1255a1f92a7a6261d03cf35f5812457214b00c626 True
NAME IMAGE SUCCEEDED
build.kpack.io/hello-tanzu-build-1-fvt6g ghcr.io/making/hello-tanzu@sha256:ab5c70080c2b8a73b67d90b1255a1f92a7a6261d03cf35f5812457214b00c626 True
NAME URL READY STATUS AGE
gitrepository.source.toolkit.fluxcd.io/hello-tanzu https://github.com/tanzu-japan/hello-tanzu.git Unknown reconciliation in progress 12m
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/hello-tanzu ClusterIP 10.96.96.239 <none> 80/TCP 9m43s
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 8h
NAME CLASS HOSTS ADDRESS PORTS AGE
ingress.networking.k8s.io/hello-tanzu <none> hello-tanzu-127-0-0-1.sslip.io 80 9m43s
IngressのURL( http://hello-tanzu-127-0-0-1.sslip.io )にブラウザでアクセスして動作確認します。
Workloadを削除します。
kubectl delete -f hello-tanzu.yaml
Node.jsアプリのデプロイ
次にNode.jsのアプリをデプロイします。デプロイするサンプルアプリとして https://github.com/making/hello-nodejs を使用します。
次のWorkloadを作成します。
cat <<EOF > hello-nodejs.yaml
apiVersion: carto.run/v1alpha1
kind: Workload
metadata:
name: hello-nodejs
labels:
app.tanzu.vmware.com/workload-type: web
spec:
source:
git:
url: https://github.com/making/hello-nodejs
ref:
branch: master
EOF
kubectl apply -f hello-nodejs.yaml
同様にsternでログを確認すれば、ソースコード取得後npmによるビルドが行われていることがわかります。 なお、npmによるビルドは2回目移行はキャッシュされます。
stern 'hello-nodejs.*'
ログはこちら
$ kubectl get workload,deploy,pod,image,build,gitrepo,service,ingress
NAME AGE
workload.carto.run/hello-nodejs 7m18s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/hello-nodejs 1/1 1 1 6m23s
NAME READY STATUS RESTARTS AGE
pod/hello-nodejs-7f87556847-kk4bk 1/1 Running 0 6m23s
pod/hello-nodejs-build-1-qxxwq-build-pod 0/1 Completed 0 7m12s
NAME LATESTIMAGE READY
image.kpack.io/hello-nodejs ghcr.io/making/hello-nodejs@sha256:b11ec565f737dda90a76cbfa87aae92cdc348fccdf36de1d0b387c229cb5e4df True
NAME IMAGE SUCCEEDED
build.kpack.io/hello-nodejs-build-1-qxxwq ghcr.io/making/hello-nodejs@sha256:b11ec565f737dda90a76cbfa87aae92cdc348fccdf36de1d0b387c229cb5e4df True
NAME URL READY STATUS AGE
gitrepository.source.toolkit.fluxcd.io/hello-nodejs https://github.com/making/hello-nodejs True Fetched revision: master/9b066f581bd86f6580b73209439bd5cb4b3af523 7m18s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/hello-nodejs ClusterIP 10.96.88.240 <none> 80/TCP 6m23s
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 8h
NAME CLASS HOSTS ADDRESS PORTS AGE
ingress.networking.k8s.io/hello-nodejs <none> hello-nodejs-127-0-0-1.sslip.io 80 6m23s
IngressのURL( http://hello-nodejs-127-0-0-1.sslip.io )にcurlでアクセスして動作確認します。
$ curl http://hello-nodejs-127-0-0-1.sslip.io
Hello World!
Workloadを削除します。
kubectl delete -f hello-nodejs.yaml
Rubyアプリのデプロイ
次にRubyのアプリをデプロイします。デプロイするサンプルアプリとして https://github.com/making/hello-ruby を使用します。
次のWorkloadを作成します。
cat <<EOF > hello-ruby.yaml
apiVersion: carto.run/v1alpha1
kind: Workload
metadata:
name: hello-ruby
labels:
app.tanzu.vmware.com/workload-type: web
spec:
source:
git:
url: https://github.com/making/hello-ruby
ref:
branch: main
env:
- name: PORT
value: "8080"
EOF
kubectl apply -f hello-ruby.yaml
同様にsternでログを確認します。
stern 'hello-ruby.*'
ログはこちら
$ kubectl get workload,deploy,pod,image,build,gitrepo,service,ingress
NAME AGE
workload.carto.run/hello-ruby 8m7s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/hello-ruby 1/1 1 1 7m27s
NAME READY STATUS RESTARTS AGE
pod/hello-ruby-5cbcd9546f-xcf7g 1/1 Running 0 7m27s
pod/hello-ruby-build-1-9nmz8-build-pod 0/1 Completed 0 8m1s
NAME LATESTIMAGE READY
image.kpack.io/hello-ruby ghcr.io/making/hello-ruby@sha256:5c2bcd9182a62c9fcb191f2f1d9639f0eb86836943093a553d9b00d363223cfe True
NAME IMAGE SUCCEEDED
build.kpack.io/hello-ruby-build-1-9nmz8 ghcr.io/making/hello-ruby@sha256:5c2bcd9182a62c9fcb191f2f1d9639f0eb86836943093a553d9b00d363223cfe True
NAME URL READY STATUS AGE
gitrepository.source.toolkit.fluxcd.io/hello-ruby https://github.com/making/hello-ruby True Fetched revision: main/b259d9f2bd6f7da6b0fc266f1d8bb6db56a199fd 8m7s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/hello-ruby ClusterIP 10.96.117.16 <none> 80/TCP 7m27s
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 9h
NAME CLASS HOSTS ADDRESS PORTS AGE
ingress.networking.k8s.io/hello-ruby <none> hello-ruby-127-0-0-1.sslip.io 80 7m27s
IngressのURL( http://hello-ruby-127-0-0-1.sslip.io )にcurlでアクセスして動作確認します。
$ curl http://hello-ruby-127-0-0-1.sslip.io
Hello World!
Workloadを削除します。
kubectl delete -f hello-ruby.yaml
Cartographer 0.0.6をkindにインストールして様々なアプリケーションをデプロイしました。 Cartographerの面白さを少しは感じてもらえたのではないでしょうか。
次はKnativeやTektonとの連携を試そうと思います。
ちなみにVMworld Japan 2021でCarographerについて話します。興味のある方はぜひご登録ください。 https://vmworld.jp/content/MA11160/ 2021/11/26 13:40-14:20です。