[cdk8s] CDK で Kubernetes を定義してみる
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 アプリケーション定義を記述します。
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を使います。
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
に以下の内容が出力されます。
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
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
も書き換えましょう。
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
が以下のように書き換えられます。
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 のチュートリアルをご紹介しました。 他のサンプルはこちらで紹介されていますので参考にしてみてください。