💻
プログラムからkubernetesYAMLを生成できるcdk8sを試してみた
cdk8sとは
Kubernetes の厄介なところの一つであるYAMLをプログラムから生成しちゃうやつです。
現在対応している言語は TypeScript, Python, JavaScript があります。
将来的には、Java、.NET、Go に対応するそうです。
できること・できないこと
- できること
- YAMLファイルをプログラムから生成できます。
- クラスターに依存しません。(オンプレ、クラウドどちらもで動く)
- Kubernetes APIを任意選択できます。
- できないこと
- このツールからデプロイはできません。(GitOpsを介してやる必要があります。)
セットアップ
今回はTypeScriptを使ってさくっとやってみます。
とりあえず当たり前にインストールします。
$ npm install -g cdk8s-cli
or
$ yarn global add cdk8s-cli
helpはこんな感じで表示されます。今回はcdk8s init TYPE
を叩いてみましょう。
$ cdk8s -h
cdk8s [コマンド]
コマンド:
cdk8s import [SPEC] Imports API objects to your app by generating constructs.
cdk8s init TYPE Create a new cdk8s project from a template.
cdk8s synth Synthesizes Kubernetes manifests for all charts in your app.
オプション:
--version バージョンを表示
--help ヘルプを表示
Options can be specified via environment variables with the "CDK8S_" prefix (e.g. "CDK8S_OUTPUT")
TypeScript で開発するときにやっておきたいこと。
今回は TypeScript で作成するので、watch mode
をオンにしておきましょう。
$ yarn watch
Kubernetes オブジェクトを定義
Kubernetes API オブジェクトを定義していきます。
今回は paulbouwer 氏の hello-kubernetes を元に、 Service と Deployment を定義しましょう。
定義先はmain.ts
です。
import { Construct } from 'constructs';
import { App, Chart, ChartProps } from 'cdk8s';
import { KubeService, IntOrString, KubeDeployment } from './imports/k8s';
export class MyChart extends Chart {
constructor(scope: Construct, id: string, props: ChartProps = { }) {
super(scope, id, props);
// define resources here
const label = { app: 'hello-k8s' }
new KubeService(this, 'service', {
spec: {
type: 'LoadBalancer',
ports: [ { port: 80, targetPort: IntOrString.fromNumber(8080) } ],
selector: label
}
})
new KubeDeployment(this, 'deployment', {
spec: {
replicas: 2,
selector: {
matchLabels: label
},
template: {
metadata: { labels: label },
spec: {
containers: [
{
name: 'hello-kubernetes',
image: 'paulbouwer/hello-kubernetes:1.8',
ports: [ { containerPort: 8080 } ]
}
]
}
}
}
})
}
}
出来上がったら以下のyarn synth
と叩いてみましょう。
dist 配下にファイルが作成されます。
$ cat dist/hello-world.k8s.yaml
apiVersion: "v1"
kind: "Service"
metadata:
name: "hello-world-service-c82ef75b"
spec:
ports:
- port: 80
targetPort: 8080
selector:
app: "hello-ks8s"
type: "LoadBalancer"
---
apiVersion: "apps/v1"
kind: "Deployment"
metadata:
name: "hello-world-deployment-c83a8861"
spec:
replicas: 2
selector:
matchLabels:
app: "hello-ks8s"
template:
metadata:
labels:
app: "hello-ks8s"
spec:
containers:
- image: "paulbouwer/hello-kubernetes:1.8"
name: "hello-kubernetes"
ports:
- containerPort: 8080
Constructs
Helm Chart とほぼ同じです。
今回作ったものは単一のもになりますが、別のサービスを追加したいくなるときもあります。
Service を追加してみましょう。
まずはlib
ディレクトリを作成し、その中にWebServiceを作っていきましょう。
libディレクトリにファイルを置くことで再利用可能なコンポーネントを作成していきます。
import {IntOrString, KubeDeployment, KubeService} from 'cdk8s-plus-17/lib/imports/k8s'
import { Construct } from 'constructs'
export interface WebServiceProps {
readonly image: string
readonly replicas?: number
readonly port?: number
readonly containerPort?: number
}
export class WebService extends Construct {
constructor(scope: Construct, id: string, props: WebServiceProps) {
super(scope, id)
const port = props.port || 80
const containerPort = props.containerPort || 8080
const label = { app: 'hello-k8s' }
const replicas = props.replicas ?? 1
new KubeService(this, 'service', {
spec: {
type: 'LoadBalancer',
ports: [ { port, targetPort: IntOrString.fromNumber(containerPort) } ],
selector: label
}
})
new KubeDeployment(this, 'deployment', {
spec: {
replicas,
selector: {
matchLabels: label
},
template: {
metadata: { labels: label },
spec: {
containers: [
{
name: 'app',
image: props.image,
ports: [ { containerPort } ]
}
]
}
}
}
})
}
}
できたら、main.ts
をこのように書き換えましょう。
import { Construct } from 'constructs';
import { App, Chart, ChartProps } from 'cdk8s';
import { WebService } from './lib/WebService';
export class MyChart extends Chart {
constructor(scope: Construct, id: string, props: ChartProps = { }) {
super(scope, id, props);
/**
* ここを書き換える
* 今回はhelloとghostを追加してみた
* ghost: https://hub.docker.com/_/ghost
*/
new WebService(this, 'hello', { image: 'paulbouwer/hello-kubernetes:1.7', replicas: 10 });
new WebService(this, 'ghost', { image: 'ghost', containerPort: 2368 });
}
}
const app = new App();
new MyChart(app, 'hello-world');
app.synth();
cdk8s synth
を叩くと以下のように追加されます。
apiVersion: "v1"
kind: "Service"
metadata:
name: "hello-world-hello-service-c8847821"
spec:
ports:
- port: 80
targetPort: 8080
selector:
app: "hello-k8s"
type: "LoadBalancer"
---
apiVersion: "apps/v1"
kind: "Deployment"
metadata:
name: "hello-world-hello-deployment-c8f0bd0b"
spec:
replicas: 10
selector:
matchLabels:
app: "hello-k8s"
template:
metadata:
labels:
app: "hello-k8s"
spec:
containers:
- image: "paulbouwer/hello-kubernetes:1.7"
name: "app"
ports:
- containerPort: 8080
---
apiVersion: "v1"
kind: "Service"
metadata:
name: "hello-world-ghost-service-c81e52c6"
spec:
ports:
- port: 80
targetPort: 2368
selector:
app: "hello-k8s"
type: "LoadBalancer"
---
apiVersion: "apps/v1"
kind: "Deployment"
metadata:
name: "hello-world-ghost-deployment-c8c14ddd"
spec:
replicas: 1
selector:
matchLabels:
app: "hello-k8s"
template:
metadata:
labels:
app: "hello-k8s"
spec:
containers:
- image: "ghost"
name: "app"
ports:
- containerPort: 2368
まとめ
kubernetesYAMLの管理は大変ですがこのようにコードで管理できると便利ですね。
良ければいいねよろしくお願います!
参考
Discussion