Pulumi初心者が、Kubernetesの勉強も兼ねて評価を行った際のメモ書き。
公式ドキュメントは比較的TypeScriptの例が多いため、Pythonの事例として書き残しておく。
概要
タイトルの通り、PulumiでKubernetesのデプロイをお試し評価。以下は主な感想。
- Kubernetes用のテンプレート(
kubernetes-python
)が提供されているため、比較的導入のしきいは低い(プロジェクトの初期状態からpulumi up
する程度で、シンプルなnginx
のdeploymentが作成できる)。-
kubectl
コマンドが叩ける状態になっていれば、特に追加の設定はなさそう。
-
- PulumiのHelm Chartデプロイ機能は、単純にHelmをKickするだけでなく、独自にServideやDeploymentなどのリソースを展開&解釈してデプロイしている。このため、Helm 2.xでもTiller Serverが不要。
- 今回はHelm 3.xを利用したためこの恩恵は受けられず、逆に
helm list
コマンドで内容を確認できないデメリットもあり。
- 今回はHelm 3.xを利用したためこの恩恵は受けられず、逆に
-
Pulumi.Output
(生成されたリソースの出力用パラメータ)の変換実装には、相応に試行錯誤が必要だった(apply
関数の周りなどの理解がある程度難しい)。- 実装コードは逐次処理のPythonコードに見えるが、内部的には(PreviewやDiff制御など)宣言的な概念も扱っているため、若干理解し辛い側面もありそう。
評価
前提
Kubernetesのクラスタ(AWS EKSを利用)は構築済で、Plumi及びHelmの初期設定も完了状態を前提に置く。
テストに利用するHelm Chart (bitnami/jenkins
)のリポジトリも事前登録した状態(FetchOps
クラスの機能でリポジトリからも取得できそうだが、今回の評価対象からは除外)。
$ pulumi version
v1.8.1
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
ip-10-0-0-145.ap-northeast-1.compute.internal Ready <none> 1h51m v1.14.7-eks-1861c5
ip-10-0-2-96.ap-northeast-1.compute.internal Ready <none> 1h50m v1.14.7-eks-1861c5
$ helm version
version.BuildInfo{Version:"v3.0.2", GitCommit:"19e47ee3283ae98139d98460de796c1be1e3975f", GitTreeState:"clean", GoVersion:"go1.13.5"}
$ helm repo list
NAME URL
bitnami https://charts.bitnami.com/bitnami
Project初期化
kubernetes-python
テンプレートを利用して、Pulumi Projectを新規作成(聞かれる質問はデフォルトで回答)。
$ mkdir python-k8s && cd python-k8s
$ pulumi new kubernetes-python
This command will walk you through creating a new Pulumi project.
Enter a value or leave blank to accept the (default), and press <ENTER>.
Press ^C at any time to quit.
project name: (python-k8s)
project description: (A minimal Kubernetes Python Pulumi program)
Created project 'python-k8s'
Please enter your desired stack name.
To create a stack in an organization, use the format <org-name>/<stack-name> (e.g. `acmecorp/dev`).
stack name: (dev)
Created stack 'dev'
Your new project is ready to go! ✨
To perform an initial deployment, run the following commands:
1. virtualenv -p python3 venv
2. source venv/bin/activate
3. pip3 install -r requirements.txt
Then, run 'pulumi up'
メッセージに表示される通りに、Pythonのvirtualenv
環境の設定(及び依存ライブラリのインストール)を行う。
$ pip3 install virtualenv
$ virtualenv -p python3 venv
$ source venv/bin/activate
$ pip3 install -r requirements.txt
実装
Pythonの実装コード(__main__.py
)を、以下の2種類のリソースを作成するように変更。なお、OutputにはそれぞれのServiceのホスト名(AWS Elastic Load Balancerに割当されたホスト名)を出力するように定義。
- NGINX Deployment / Service
- NGINXのdeploymentと、外部公開用のLoadBalancer TypeのService。
- Jenkins Helm Chart
- bitnami/jenkinsのHelm Chart。
import base64
import pulumi
from pulumi_kubernetes.apps.v1 import Deployment
from pulumi_kubernetes.core.v1 import Service
from pulumi_kubernetes.helm.v2 import Chart, ChartOpts
def deploy_nginx_service():
app_name = "nginx"
app_labels = { "app": app_name }
nginx_deployment = Deployment(
app_name,
spec={
"selector": { "match_labels": app_labels },
"replicas": 1,
"template": {
"metadata": { "labels": app_labels },
"spec": { "containers": [{ "name": app_name, "image": "nginx" }] }
}
})
nginx_service = Service(
app_name,
metadata={
"labels": nginx_deployment.spec["template"]["metadata"]["labels"],
},
spec={
"type": "LoadBalancer",
"ports": [{ "port": 80, "target_port": 80, "protocol": "TCP" }],
"selector": app_labels,
})
return nginx_service
def deploy_jenkins_chart():
return Chart("jenkins", ChartOpts(
chart="jenkins",
repo="bitnami",
values={},
))
nginx_service = deploy_nginx_service()
jenkins_chart = deploy_jenkins_chart()
pulumi.export("nginx_hostname",
nginx_service.status.apply(
lambda x: x["load_balancer"]["ingress"][0]["hostname"]))
pulumi.export("jenkins_hostname",
jenkins_chart.resources.apply(
lambda x: x["v1/Service:jenkins"].status["load_balancer"]["ingress"][0]["hostname"]))
実行
pulumi up
Previewing update (dev):
Type Name Plan
+ pulumi:pulumi:Stack python-k8s-dev create
+ ├─ kubernetes:helm.sh:Chart jenkins create
+ │ ├─ kubernetes:core:PersistentVolumeClaim jenkins create
+ │ ├─ kubernetes:core:Secret jenkins create
+ │ ├─ kubernetes:core:Service jenkins create
+ │ └─ kubernetes:apps:Deployment jenkins create
+ ├─ kubernetes:apps:Deployment nginx create
+ └─ kubernetes:core:Service nginx create
Resources:
+ 8 to create
Do you want to perform this update? yes
Updating (dev):
Type Name Status
+ pulumi:pulumi:Stack python-k8s-dev created
+ ├─ kubernetes:helm.sh:Chart jenkins created
+ │ ├─ kubernetes:core:Secret jenkins created
+ │ ├─ kubernetes:core:Service jenkins created
+ │ ├─ kubernetes:core:PersistentVolumeClaim jenkins created
+ │ └─ kubernetes:apps:Deployment jenkins created
+ ├─ kubernetes:apps:Deployment nginx created
+ └─ kubernetes:core:Service nginx created
Outputs:
jenkins_hostname: "xxx.ap-northeast-1.elb.amazonaws.com"
nginx_hostname : "yyy.ap-northeast-1.elb.amazonaws.com"
Resources:
+ 8 created
Outputs
に出力されたホスト名が、それぞれjenkins
、nginx
に対応するService (Load Balancer)
。それぞれcurl
でアクセスすると応答が返る。
$ curl -s --head xxx.ap-northeast-1.elb.amazonaws.com | grep X-Jenkins:
X-Jenkins: 2.204.1
$ curl -s --head yyy.ap-northeast-1.elb.amazonaws.com | grep Server
Server: nginx/1.17.6
リソースの確認
kubectl
コマンドでも、生成されたリソースを確認可能。ただし、Helm ChartはPulumiが独自展開するため、表示されない(一覧が空のまま)。
$ kubectl get deployments
NAME READY UP-TO-DATE AVAILABLE AGE
jenkins 1/1 1 1 32m
nginx-nziiq5rs 1/1 1 1 32m
$ kubectl get services
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
jenkins LoadBalancer 172.20.125.15 xxx.ap-northeast-1.elb.amazonaws.com 80:32525/TCP,443:31321/TCP 33m
kubernetes ClusterIP 172.20.0.1 <none> 443/TCP 1h42m
nginx-6hbjq6d7 LoadBalancer 172.20.14.82 yyy.ap-northeast-1.elb.amazonaws.com 80:32325/TCP 33m
$ helm list
NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION
参考情報
Kubernetes向けPluginの説明。
API Reference。
In this tutorial, we’ll use the Helm API of @pulumi/kubernetes to deploy v2.1.3 of the Wordpress Helm Chart to a Kubernetes cluster. The Tiller server is not required to be installed. Pulumi will expand the Helm Chart and submit the expanded YAML to the cluster.
PulumiのHelm Providerは独自にChartを展開してデプロイするという説明。これに起因してか、helm list
の結果には出力されない模様。
Ouptutの出力変換についての説明。
Pulumiが出力するOutput (Pulumi.Output
型)を加工する際にはapply
関数を利用する必要がある (内部で特殊な処理をしているのか、単純にオブジェクト変換するとうまく動作しないことがある。例えば、pulumi up
などの処理がブロックされて処理が進まなくなる現象に遭遇)。
The apply method accepts a callback which will be passed the value of the Output when it is available, and which returns the new value. The result of the call to apply is a new Output whose value is the value returned from the callback, and which includes the dependencies of the original Output. If the callback itself returns an Output, the dependencies of that output are unioned into the dependencies of the returned Output.