この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
Overview
Cloud Development Kit for Kubernetes(または cdk8s)は CDK で Kubernetes リソースの定義を行うためのフレームワークです。cdk8s は 2020 年 5 月にアルファ版がリリースされており、2020 年 8 月現在もアルファテストフェーズです。
cdk8s を使うことで今まで YAML
で行なっていたPod
、Service
、Deployment
、ReplicaSet
などの定義をプログラミング言語で行うことができます。具体的には、開発者は任意の数のチャートを定義し、定義されたチャートのそれぞれが Kubernetes マニフェストファイルに合成されます。
尚、cdk8s は Kubernetes アプリケーションの定義を行うツールで、実際にクラスタに適応はしません。
npm run synth
コマンドを実行した際にプロジェクト内で定義されたチャートがdist
ディレクトリへYAML
ファイルとして吐き出されるので、kubectl apply -f dist/<YOUR_FILE_NAME>.k8s.yaml
コマンドや Flux のような GitOps ツールを使って、任意の Kubernetes クラスタに適用してください。
本記事では CDK で Kubernetes アプリケーションの定義を作成し、マニフェストファイルへ出力するところまでの手順をご紹介します。
cdk8s が現在対応している言語はPython
またはTypescript
です。
Installation
参考:Getting Started with TypeScript
インストール
$ npm install -g cdk8s-cli
プロジェクト雛形作成
$ mkdir cdk
$ cd cdk
$ cdk8s init typescript-app
creating a new project from template: typescript-app
...
Transpile
CDK をデプロイする前に Typescript を Javascript へコンパイルする必要があるので watch mode
をオンにしておきます。
$ npm run watch
検証環境
本記事で利用した検証環境は以下です。
cdk8s --version
0.26.0
tsc -v
Version 3.7.4
main.ts
プロジェクトルートに生成されるmain.ts
にアプリケーション定義を記述します。
雛形で生成されたmain.ts
は以下のような構成です。ここに Kubernetes アプリケーション定義を記述します。
main.ts
import { Construct } from 'constructs';
import { Chart, App } from 'cdk8s';
class MyChart extends Chart {
constructor(scope: Construct, name: string) {
super(scope, name);
// define constructs here
}
}
const app = new App();
new MyChart(app, 'cdk');
app.synth();
尚、この段階でnpm run synth
を実行しても、dist/cdk.k8s.yaml
には何も生成されません。
$ npm run synth
$ cat dist/cdk.k8s.yaml
<EMPTY>
Kubernetes オブジェクトを定義する
Kubernetes API オブジェクトをチャートに追加します。
cdk8s では Kubernetes API オブジェクトはconstructs
として表現され、cdk8s init
を実行した際にプロジェクトディレクトリの
imports/k8s.ts
ファイルへインポートされます。
先ほど生成されたデフォルトのmain.ts
へService
とDeployment
リソースを追加します。
image は paulbouwer さんのhello-kubernetesを使います。
main.ts
import { Construct } from 'constructs';
import { App, Chart } from 'cdk8s';
// imported constructs
import { Deployment, Service, IntOrString } from './imports/k8s';
export class MyChart extends Chart {
constructor(scope: Construct, name: string) {
super(scope, name);
const label = { app: 'hello-k8s' };
new Service(this, 'service', {
spec: {
type: 'LoadBalancer',
ports: [ { port: 80, targetPort: IntOrString.fromNumber(8080) } ],
selector: label
}
});
new Deployment(this, 'deployment', {
spec: {
replicas: 2,
selector: {
matchLabels: label
},
template: {
metadata: { labels: label },
spec: {
containers: [
{
name: 'hello-kubernetes',
image: 'paulbouwer/hello-kubernetes:1.7',
ports: [ { containerPort: 8080 } ]
}
]
}
}
}
});
}
}
const app = new App();
new MyChart(app, 'cdk');
app.synth();
この状態でnpm run synth
を実行するとdist/
ディレクトリ下のcdk.k8s.yaml
に以下の内容が出力されます。
cdk.k8s.yaml
apiVersion: v1
kind: Service
metadata:
name: cdk-service-5dd5cffa
spec:
ports:
- port: 80
targetPort: 8080
selector:
app: hello-k8s
type: LoadBalancer
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: cdk-deployment-e4826fd6
spec:
replicas: 2
selector:
matchLabels:
app: hello-k8s
template:
metadata:
labels:
app: hello-k8s
spec:
containers:
- image: paulbouwer/hello-kubernetes:1.7
name: hello-kubernetes
ports:
- containerPort: 8080
これでマニフェストファイルが生成できたので、kubectl
コマンドを実行してクラスターに反映することができます。
$ kubectl apply -f dist/cdk.k8s.yaml
Constructs
Construct はプログラムで定義されたHelm Chartだと思ってください。
例えば、先ほどの定義にhello world service
を追加したい場合には、以下のように記述します。
new WebService(this, 'hello-k8s', {
image: 'paulbouwer/hello-kubernetes:1.7'
});
WebService
を定義するため、以下のディレクトリとファイルを新規作成し、以下の内容を記述します。
lib/web-service.ts
web-service.ts
import { Construct, Node } from 'constructs';
import { Deployment, Service, IntOrString } from '../imports/k8s';
export interface WebServiceOptions {
/**
* The Docker image to use for this service.
*/
readonly image: string;
/**
* Number of replicas.
*
* @default 1
*/
readonly replicas?: number;
/**
* External port.
*
* @default 80
*/
readonly port?: number;
/**
* Internal port.
*
* @default 8080
*/
readonly containerPort?: number;
}
export class WebService extends Construct {
constructor(scope: Construct, ns: string, options: WebServiceOptions) {
super(scope, ns);
const port = options.port || 80;
const containerPort = options.containerPort || 8080;
const label = { app: Node.of(this).uniqueId };
const replicas = options.replicas ?? 1;
new Service(this, 'service', {
spec: {
type: 'LoadBalancer',
ports: [ { port, targetPort: IntOrString.fromNumber(containerPort) } ],
selector: label
}
});
new Deployment(this, 'deployment', {
spec: {
replicas,
selector: {
matchLabels: label
},
template: {
metadata: { labels: label },
spec: {
containers: [
{
name: 'app',
image: options.image,
ports: [ { containerPort } ]
}
]
}
}
}
});
}
}
main.ts
も書き換えましょう。
main.ts
import { App, Chart } from 'cdk8s';
import { Construct } from 'constructs';
import { WebService } from './lib/web-service';
export class MyChart extends Chart {
constructor(scope: Construct, ns: string) {
super(scope, ns);
new WebService(this, 'hello', { image: 'paulbouwer/hello-kubernetes:1.7', replicas: 2 });
new WebService(this, 'ghost', { image: 'ghost', containerPort: 2368 });
}
}
const app = new App();
new MyChart(app, "cdk");
app.synth();
再びnpm run synth
を実行するとcdk.k8s.yaml
が以下のように書き換えられます。
cdk.k8s.yaml
apiVersion: v1
kind: Service
metadata:
name: cdk-cdk-service-2a50c71f
spec:
ports:
- port: 80
targetPort: 8080
selector:
app: cdkBD7FE5AF
type: LoadBalancer
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: cdk-cdk-deployment-32246f9b
spec:
replicas: 2
selector:
matchLabels:
app: cdkBD7FE5AF
template:
metadata:
labels:
app: cdkBD7FE5AF
spec:
containers:
- image: paulbouwer/hello-kubernetes:1.7
name: app
ports:
- containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: cdk-ghost-service-cdbfc1f3
spec:
ports:
- port: 80
targetPort: 2368
selector:
app: cdkghostA3D16120
type: LoadBalancer
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: cdk-ghost-deployment-ac6892bc
spec:
replicas: 1
selector:
matchLabels:
app: cdkghostA3D16120
template:
metadata:
labels:
app: cdkghostA3D16120
spec:
containers:
- image: ghost
name: app
ports:
- containerPort: 2368
More Examples
本記事では cdk8s の Get Started のチュートリアルをご紹介しました。 他のサンプルはこちらで紹介されていますので参考にしてみてください。