Amazon Web Services ブログ

CDK for Kubernetes のご紹介



Kubernetes を採用してアプリケーションをグローバルにデプロイし、機械学習モデルを大規模にトレーニングし、データセンターやクラウド全体において新技術の導入方法を標準化しているお客様が急速に増えてきているのを、AWS は目の当たりにしています。Kubernetes では、手作業による処理を置き換える自動ツールの構築、インフラストラクチャのあらゆる部分での運用パイプラインの実装、アプリケーションの実行方法をきめ細かく制御できるよう開発チームの支援が可能です。

従来であれば、Kubernetes アプリケーションは人間が読める静的 YAML データファイルで定義され、これを作成し管理するのは開発者の仕事でした。新しいアプリケーションの構築には、大量のボイラープレート設定を記述したり、他のプロジェクトからコードをコピーしたり、手作業で細かい調整やカスタマイズの適用を行ったりする必要があります。アプリケーションが進化し、チームが大きくにつれて、これらの YAML ファイルの管理は難しくなります。ベストプラクティスの共有または更新には、手作業による変更と複雑な移行が含まれます。

YAML はクラスターの望ましい状態を記述するための優れた形式ではありますが、ロジックや再利用可能な抽象化を表現するためのプリミティブはありません。Kubernetes のエコシステムには、これらの課題にさまざまな方法で対処する複数のツール (kustomizejsonnetjkcfgkubecfgkubegenPulumi など) があります。

これは、お客様が CloudFormation テンプレートを使用してアプリケーションを定義する際に直面する問題とまったく同じで、AWS CDK で解決できる問題でもありました。つまり、AWS CDK の同じ設計概念を適用して、すべての Kubernetes ユーザーを支援できるだろうと AWS は考えたのです。

そこで 2018 年に AWS クラウド開発キット (AWS CDK) を導入し、上記のような理由で YAML に代わるものを求めていた AWS CloudFormation のお客様をサポートしてきました。AWS CDK は、TypeScript、Python、Java、.NET などの使い慣れたプログラミング言語を使用して、クラウドインフラストラクチャを定義するためのオープンソースフレームワークです。AWS CDK を使って既存のツールとワークフローでインフラストラクチャを作成およびプロビジョニングできるようにすることで開発プロセスを簡素化できると、お客様には大変気に入っていただいています。AWS CDK はコンポーザブルなため、設定やボイラープレートの詳細を簡単に抽象化できる点も好評です。さらに、Kubernetes 空間に同じ概念と技術を適用できないかと、多くのお客様から尋ねられました。

Kubernetes 向け CDK の導入

今日は、CDK for Kubernetes (cdk8s) についてお伝えしたいと思います。これは、使い慣れたプログラミング言語で Kubernetes アプリケーションと再利用可能なコンポーネントを定義できる新しいオープンソースプロジェクトです。cdk8s (「シーディーケイツ」と発音します) を使用すると、TypeScript や Python などのプログラミング言語で標準 Kubernetes YAML を作成できます。つまり、オンプレミスとクラウドの両方で実行している任意の Kubernetes クラスターのアプリケーションを定義できるのです。

cdk8s を使えば、コア Kubernetes API オブジェクトとカスタムリソース (CRD) の両方を、「コンストラクト」と呼ばれる強く型付けされたクラスとしてインポートできます。つまり、オブジェクト指向プログラミングのあらゆる強力なプリミティブを活用して、Kubernetes アプリケーションを定義できることを意味します。独自の抽象化を作成する機能は、最も強力な機能の 1 つです。

cdk8s を使用することで、一般的な Kubernetes パターンをコードライブラリとして公開し、これらのライブラリを任意のアプリケーションで参照できます。このため、その機能と柔軟性を基本的に優先しつつ、すべての Kubernetes ユーザーのアプリケーションの定義や管理が簡素化し、Kubernetes の宣言型 API アプローチの上に構築します。また、使い慣れた言語、IDE、ツール、テクニックを使用して Kubernetes アプリケーションを作成できます。

cdk8s について知っていただきたい事柄は以下のとおりです。

どのクラスターでも機能: cdk8s は環境に依存しません。マシン上でローカルに実行し、標準の Kubernetes YAML データを作成するため、オンプレミスやクラウドなどどこにおいても実行中の任意の Kubernetes クラスターで使用できます。

宣言状態への命令型アプローチ: cdk8s コードは命令型言語で記述されていますが、最終的には適切な状態を純粋な Kubernetes YAML として出力します。つまり、宣言型の適切な状態アプローチのロバスト性を損なうことなく、命令型プログラミングの表現力とシンプルさを楽しむことができます。

Kubernetes API バージョンとカスタムリソースを使用: cdk8s には優れた CLI ツールが含まれており、Kubernetes API の任意のバージョンをプロジェクトにインポートしたり、必要に応じて新しい API バージョンを利用するために更新したりできます。カスタムリソース定義をインポートすることもできます。

言語サポート: cdk8s では、TypeScript、JavaScript、Python を使用してアプリケーションを定義できます。今後、Go を含むさらに多くの言語をサポートする予定です。

オープンソース: cdk8s はオープンソースで、コミュニティからの協力を歓迎しています。私たちは AWS のお客様だけでなく、Kubernetes コミュニティ全体のために cdk8s を構築しました。

cdk8s の実行

cdk8s を使ってシンプルな Kubernetes アプリを定義する方法を見てみましょう。

const labels = { app: 'guestbook', tier: 'frontend' };

new k8s.Service(this, 'service', {
  metadata: { labels },
  spec: {
    type: 'LoadBalancer',
    ports: [ { port: 80 } ],
    selector: labels,
  }
});

new k8s.Deployment(this, 'deployment', {
  spec: {
    selector: { matchLabels: labels },
    replicas: 3,
    template: {
      metadata: { labels },
      spec: {
        containers: [
          {
            name: 'php-redis',
            image: 'gcr.io/google-samples/gb-frontend:v4',
            ports: [{ containerPort: 80 }],
            resources: { requests: { cpu: '100m', memory: '100Mi' } }
          }
        ]
      }
    }
  }
});

これは Kubernetes ゲストブックフロントエンドの定義です。これを合成すると、クラスターに適用できる使い慣れた YAML 出力が作成されます。

apiVersion: v1
kind: Service
metadata:
  labels:
    app: guestbook
    tier: frontend
  name: guestbook-service-23e79b52
spec:
  ports:
    - port: 80
  selector:
    app: guestbook
    tier: frontend
  type: LoadBalancer
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: guestbook-deployment-8b2b7b76
spec:
  replicas: 3
  selector:
    matchLabels:
      app: guestbook
      tier: frontend
  template:
    metadata:
      labels:
        app: guestbook
        tier: frontend
    spec:
      containers:
        - image: gcr.io/google-samples/gb-frontend:v4
          name: php-redis
          ports:
            - containerPort: 80
          resources:
            requests:
              cpu: 100m
              memory: 100Mi

ネイティブプログラミングの経験

cdk8s には、すべての Kubernetes API の型指定されたクラスが含まれています。つまり、プログラムを書くときに、コード補完、型安全性 (静的言語の場合)、インラインドキュメント、リファクタリングツールなど私が好きな IDE からあらゆるサポートを得ることができるのです。

「kubectl 適用」と GitOps 対応

では、これをどのようにしてクラスターにデプロイするのでしょうか? cdk8s アプリを実行すると、標準の Kubernetes YAML が合成されます。この YAML をクラスターにデプロイするために使用するワークフローとツールは変わりません

$ cdk8s synth -o dist && kubectl apply -f dist/*
dist/guestbook.k8s.yaml
service/guestbook-service-23e79b52 created
deployment.apps/guestbook-deployment-8b2b7b76 created

つまり、cdk8s アプリを標準の GitOps ワークフローにもきちんと統合できるということです。GitOps で cdk8s を使用するということは、アプリケーションを構築する場合と同じワークフロー (コードを記述し、CICD でデプロイする) を、アプリケーションを定義するワークフローに使用できることを意味します。cdk8s とFlux の統合、および Argo CD の統合に関する、Max Brenner が書いた投稿をご覧ください。

より深く掘り下げる: コンストラクト

CDK アプリの構成要素は「コンストラクト」と言われています。(映画「マトリックス」を覚えていますか?).コンストラクトは、ポッドなどの個々の Kubernetes リソースから、マイクロサービスや本格的な MongoDB クラスターなどの複雑なアイデアまで、あらゆるものを表すことができます。クラスター全体をモデル化する完全なシステムを表すことも可能です。

cdk8s は、すべてのコア Kubernetes API オブジェクト (デプロイ、サービス、ReplicaSet、ポッドなど) および任意のカスタムリソース定義から、コンストラクト自動的に作成 (「インポート」) することができます。Helm チャートからコンストラクトをインポートするサポートも、今後追加する予定です。

インポートしたら、これらのコンストラクトを使用してチャートを定義できます。Chart は、1 つの Kubernetes YAML マニフェストを表しています。App は、同じプロジェクトの一部である複数のチャートで構成されています。

次の例では、1 つの Kubernetes ポッドを使用してチャートタイプ HelloChart を宣言しています。

class HelloChart extends Chart {
  constructor(scope: Construct, name: string) {
    super(scope, name);

    new k8s.Pod(this, 'hello', {
      spec: {
        containers: [ { name: 'hello', image: 'world' } ]
      }
    });
  }
}

次に、このチャートの単一インスタンスをアプリで定義します。

const app = new App();
new HelloChart(app, 'hello');

// synthesize all charts (manifests) in the app.
app.synth();

出力ディレクトリには hello.k8s.yaml と呼ばれる単一のファイルが含まれます。

apiVersion: v1
kind: Pod
metadata:
  name: hello-hello-29dae615
spec:
  containers:
    - image: world
      name: hello

チャートは単なる通常のクラスなので、パラメータを渡すなどのことを行います。

interface HelloChartOpts {
  pods: number;
}

class HelloChart extends Chart {
  constructor(scope: Construct, name: string, opts: HelloChartOpts) {
    super(scope, name);

    for (let i = 0; i < opts.pods; ++i) {
      new k8s.Pod(this, 'hello' + i, {
        spec: {
          containers: [ { name: 'hello', image: 'world' } ]
        }
      });
    }
  }
}

私のチャートで任意の数のインスタンスを定義します。

const app = new App();
new HelloChart(app, 'hello-dev', { pods: 1 });
new HelloChart(app, 'hello-prod', { pod: 10000 });
app.synth();

このアプリは 2 つのファイルを出力します。単一のポッドを含む hello-dev.k8s.yaml と、10,000 個のポッドを含む hello-prod.k8s.yaml (私自身の小さな DoS アプリ) です。基本的に cdk8s の 1 行は、5 万行を超える YAML を作成します。

さらに深く見ていきましょう: 抽象化レイヤー

コンストラクトの優れた点は、コンストラクトを上位レベルの抽象化に構成するのが簡単であることです。

たとえば、k8s の前にサービスがあるデプロイがあることは一般的で、「ServiceDeployment」という新しいコンストラクトタイプを使用してこれを表現できます。

class WebService extends Construct {
  constructor(scope: Construct, name: string, opts: ServiceDeploymentOpts) {
    super(scope, name);

      new k8s.Deployment(this, ...);
      new k8s.Service(this, ...);
    }
  }
}

次に、それをインスタンス化します。

new WebService(this, 'service1', { ... });
new WebService(this, 'service2', { ... });

これらの合成は、抽象化レイヤーとも呼ばれます。これらのレイヤーは互いに積み重ねることができます。つまり、cdk8s アプリの一番下のレイヤーは通常、k8s API または CRD が作成したインポートされたコンストラクトで構成されます。そのため、これらのコンストラクトはより高いレベルの抽象化へ構成されます。上位のレイヤーは、API の複雑さを抽象化したり (たとえば、スマートなデフォルトを提供することで)、シンプルなメンタルモデルを優先して主要な詳細を抽象化した独断的なアイデアを表すことができます。

抽象化を作成したので、他のクラスライブラリと同じように、npmPyPIMaven CentralNuGet などのパッケージマネージャー、または内部のパッケージマネージャーを介して、それらを誰とでも共有できるようになりました。これで、cdk8s を使用して会社全体またはコミュニティとベストプラクティスを簡単に共有できます。

スプーンなんてない: 豊富なオブジェクト指向 API で創造力を発揮する

私のコードでは、コンストラクトはオブジェクト指向クラスとして表されています。つまり、オブジェクト指向設計のすべての機能を使用して、美しくリッチな API を自分のコンストラクトに作成できるということです。

以下は、Ambassador API Gateway の架空の API です。

const books = new k8s.Service(this, 'book-collection', ...);
const book = new k8s.Service(this, 'book', ...);

const oauth = new ambassador.Oauth2Filter(this, 'auth', {
  authorizationUrl: 'url',
  // ...
});

const api = new ambassador.Api(this, 'gateway');
api.get('/books', books);
api.get('/books/.*/', book, { prefixRegex: true });
api.post('/books', books, { filter: oauth });
api.put('/books/.*/', book, { filter: oauth, prefixRegex: true });

このモックアップでは、ambassador.Api コンストラクトが公開する一連のメソッドにより、ユーザーは厳密に型指定されたわかりやすい構文を使用してルートマッピングと設定を記述できます。

これがどれほどすごいことか、おわかりいただけると思います。

Kubernetes 向けの高レベルのコンストラクトライブラリ

これは cdk8s の初日です。コア Kubernetes API に対応する豊富な高レベルのコンストラクトライブラリを設計することの影響を調べています。優れたクラスライブラリを通じて、Kubernetes の完全な機能セットを公開することを目指しています。

どんなものになるか興味があれば、ConfigMap を使用して設定のコンテンツを保存したり、ボリュームを介してポッドで利用できるようにしたりできる Kubernetes の一般的なパターンを検討してください。

Kubernetes ドキュメントにあるこちらのセクションで、以下について必要なことを説明しています。

  1. kubectl を使用して、ディレクトリで ConfigMap を定義します。
    kubectl create configmap my-config --from-file=./config
  2. この YAML を介してポッドを定義します。
    apiVersion: v1
    kind: Pod
    metadata:
      name: dapi-test-pod
    spec:
      containers:
        - name: test-container
          image: k8s.gcr.io/busybox
          command: [ "/bin/sh", "-c", "ls /etc/config/" ]
          volumeMounts:
          - name: config-volume
            mountPath: /etc/config
      volumes:
        - name: config-volume
          configMap:
            name: my-config
      restartPolicy: Never

現在私たちが設計している高レベルライブラリでは、このようになります。

// define a config map with all the files in a local dir
const config = ConfigMap.fromDirectory(this, './config');

// define a pod
const pod = new Pod(this, 'dapi-test-pod');

// add the config map as a volume
const volume = pod.addConfigMapVolume(config);

// add a container to the pod and mount the files
// to /etc/config, and print them on initialization
const container = pod.addContainer('test-container');
container.image = 'k8s.gcr.io/busybox';
container.mount(volume, '/etc/config'); // <-- NICE!
container.command = [ "/bin/sh", "-c", "ls /etc/config/" ];
container.restartPolicy = PodRestartPolicy.NEVER;

これで、このような高レベルの API の可能性を感じ取れると思います。高レベルの設計プロジェクトの詳細については、GitHub のリサーチセクションをご覧ください。

cdk8s がアルファ版に

本日、cdk8sが「アルファ版」の段階にあることをお知らせします。これで、皆さんが自分たちの環境で使用を始め、AWS と協力してこのプロジェクトをご自分のユースケースで使うことができるようになりました。

安定するまで今後数か月間、cdk8s は大きく変化し続けると予想されますので、リリースごとに変更ログを公開し、重要な変更や新しい機能に関する情報を提供します。

cdk8s は、Kubernetes コミュニティ全体のために構築されたオープンソースプロジェクトです。開発はすべて https://github.com/awslabs/cdk8s で行われています。どなたでも参加し貢献できますので、ぜひご協力ください。

皆さんからのご意見をお待ちしております。また、API、開発者の体験、他のツールとの統合、ドキュメント、機能に関する皆さんのご参加も歓迎いたします。

今回は、GitHub の問題のリンクとともに、AWS が検討してきたいくつかの方向性をご紹介しました。これであなたも仲間に加わり、ディスカッションに参加できるでしょう。

AWS はこのプロジェクトにとても期待しています。Kubernetes を使用する開発者は、生産性が高まるだけでなく、作業を楽しめるようになるはずです。

今すぐ始める

使用開始するには、cdk8s.io にアクセスしてください。cdk8s の雰囲気を少し味わってみたいなら、TypeScript または Python の使用開始ガイドを試してみるとよいでしょう。これらの簡単なチュートリアルでは、cdk8s をインストールし、k8s API を使って、独自のカスタムコンストラクトを作成するための初めの一歩について説明しています。

Slack または Twitter でもご連絡いただけます。

今日も楽しくコーディングしましょう。

EladNate