SnykとTiltでKubernetesアプリケーション開発を安全に
2021年1月11日
0 分で読めますKubernetesアプリケーションの開発は簡単ではありません。多くの場合、多くの要素が絡み合うマイクロサービスアーキテクチャを扱い、それらを連携させるためのクラスター設定も開発する必要があります。さらに、迅速な反復とテストのワークフローが複雑になり、エンジニアリングチームにとって管理が難しくなることもあります。
そこで役立つのがTiltのようなツールです。
Tiltとは?
Tiltは、Kubernetesアプリケーションを開発するデベロッパーに最適なツールです。高度にカスタマイズできるローカルデプロイワークフローを提供し、作業中もリアルタイムで更新されます。すべてのデベロッパーが共有する一元管理された環境とは異なり、Tiltでは各デベロッパーが独自のローカル環境を利用できます。デプロイは非常に高速で、各自のワークフローをカスタマイズできます。
TiltはStarlarkベースのTiltfileで設定します。カスタマイズ可能なGUIとCLI環境に加え、充実したAPIも提供します。これにより、ワークフローやアプリケーション開発中に表示する情報を柔軟に定義できます。また、Tiltはカスタム拡張機能にも対応しており、開発環境の設定や実行方法をさらに自由に選べます。
同僚のShai Mendelは、Tiltfile内でSnykを実行できるTilt拡張機能の開発に貢献しました。最近、私もこの拡張機能を更新し、SnykをTiltのワークフローに組み込むさまざまな方法を試してみました。仕組みを見ていきましょう。
始めるには
まず、レジストリとともにKubernetesクラスターをローカルにデプロイします。Tiltには、これを簡単に行うためのツールが用意されています。Ctlptlは、miniKube、Kind、Docker Desktopなど、Kubernetesをローカルで実行するさまざまな方法に対応しています。ここではKindを使います。事前にインストールしておきましょう。
このコマンドを実行すると、Kindを使ってKubernetesクラスターをデプロイし、ローカルのDockerにレジストリもデプロイして、Kubernetesから利用するために必要な接続設定をすべて行います。コマンドの完了後、Dockerで両方がデプロイされていることを確認できます。
次に、テスト用のサンプルアプリケーションをクローンします。Tiltが提供するNode.jsのサンプルを使います。このアプリケーションには、Tiltを使う際のベストプラクティスを示すTiltfileのサンプルが複数含まれており、それぞれを解説するブログ記事もあります。
この記事では、最適化されたDockerfileのサンプルを使います。用意されているTiltfileを見てみましょう。
このTiltfileの内容を詳しく見ていきましょう。docker_build関数で、このフォルダー内のDockerfileからexample-node-js-imageをビルドします。また、kubernetes.yamlファイルを使ってk8s_yamlリソースを作成し、ポート8000へのポートフォワードを設定したk8s_resourceを関連付けます。さらに、現在時刻をファイルに出力するlocal_resourceを定義し、これとk8s_resourceの間に依存関係を作成しています。これにより、リソースの更新前に必ず実行され、デプロイにかかる時間を測定できます。Tiltfile APIでサポートされる関数の詳細は、ドキュメントをご覧ください。
また、アプリに必要な依存関係をyarnでインストールするDockerfileもあります。
アプリケーション自体は非常にシンプルなNode.jsアプリケーションで、ポート8000にウェブページを表示するだけです。
クラスターをデプロイできたので、Tiltを起動しましょう。Tiltfileがあるフォルダーで、次のコマンドを実行します。
ターミナルでTiltを実行したら、スペースキーを押してブラウザーでTilt UIを開きます。

TiltはTiltfileを実行し、定義済みのすべてのリソースを起動します。この例では、イメージをビルドしてKubernetesにデプロイし、アプリケーションにアクセスするためのポートフォワードを作成します。
kubectlを使うと、アプリケーションを実行するPodが稼働していることを確認できます。
http://localhost:8000にアクセスすると、アプリケーションを確認できます。

Tiltの実行中に監視対象のディレクトリやファイルを変更すると、Tiltfileが再実行され、アプリケーションが自動的にビルドされて再デプロイされます。
では、このワークフローにSnykを組み込む方法を見ていきましょう。まだインストールしていない場合は、Snyk CLIをインストールし、無料のSnykアカウントに登録してください。
まず、TiltfileでSnyk拡張機能を読み込みます。
この操作を行うと、TiltがTiltfileを自動的に再実行するのがわかります。アプリケーションのフォルダーにはSnyk拡張機能を含むtilt_modulesディレクトリが作成されます。拡張機能もTiltfileであり、メインのTiltfileに読み込まれます。
Snyk拡張機能を読み込んだら、TiltのワークフローにSnykを追加できます。この拡張機能はSnyk CLIで利用できる3つのモードすべてに対応しているため、コンテナ、パッケージの依存関係、IaCファイル(この例ではKubernetes YAML)をスキャンできます。また、できる限り柔軟に設計されており、Tilt内でさまざまなワークフローを構築できます。
まず、Kubernetes YAMLのスキャンを見てみましょう。TiltfileでSnyk拡張機能を呼び出し、kubernetes.yamlファイルをスキャンするように設定します。
拡張機能ではさまざまな引数を利用できます。詳しくはREADMEをご覧ください。ここでは対象をkubernetes.yamlに指定し、IaCスキャンを選択して、このリソースに名前を付け、モードをinfo、トリガーをmanualに設定しています。modeフラグにはinfoまたはgateを指定できます。infoモードでは、脆弱性が見つかっても常に終了コード0を返すため、Tilt UIで失敗として扱われません。triggerをautoにすると、Tiltがkubernetes.yamlの変更を検知するたびにスキャンが実行されます。manualモードの場合は、Tilt UIから実行する必要があります。
Tiltfileを保存すると、Tilt UIのサイドバーにsnyk-iac-manualという新しいリンクが表示されます。

この項目には更新ボタンがあり、手動で実行できることを示しています。実行してみましょう。

実行すると、kubernetes.yamlに対してSnyk CLIが呼び出され、スキャン結果がUIに表示されます。このファイルにいくつかの潜在的なセキュリティ問題が見つかっていますが、モードをinfoに設定しているため、テストは成功として表示されています。では、gateに設定するとどうなるか見てみましょう。Tiltfileでこのチェックの設定を変更します。
Tiltfileの再実行後、まずUIで、手動チェックの更新ボタンが強調表示されていることがわかります。他のステージと同期していないため、更新が必要です。

変更した設定で再実行してみましょう。

リソースをgateに設定したため、Tiltはこれをビルド失敗として検出します。
次に、Tiltのワークフローにコンテナスキャンを組み込む方法を見ていきましょう。Tiltでは不変タグが使われる点に注意が必要です。ビルドごとに一意のタグが付けられ、そのタグはKubernetesにも引き継がれます。これはデプロイを高速化するためです。詳しくはこちらをご覧ください。
デフォルトのdocker_build関数を使うと、Docker内のイメージに多くの不変タグが付いているのがわかります。
このタグをスキャンする際には課題もあります。現在、Tiltfile内の別の場所からこれらのタグにアクセスする組み込みの方法がないため、Snykでスキャンするにはイメージタグを把握する必要があります。
ただし、これに対応する方法はいくつかあります。Tiltには独自のビルドステップを定義する関数が用意されているので、これを使ってワークフローを構築できます。
まず、手動スキャンのステップを設定してみましょう。ここではイメージタグを可変にし、タグを固定して、すべてのビルドで同じタグを使用します。その後、Tiltがデプロイ前にビルドへ不変タグを付け直します。
Tiltfileからdocker_build関数を削除し、custom_buildに置き換えます。
ここでは、docker_build関数と同じようにDockerを呼び出しますが、特殊な環境変数$EXPECTED_REFを追加しています。また、tag=’dev’,を指定しています。Tiltはこれを使って環境変数に値を設定します。Tiltfileを保存すると、Tiltが可変タグを使ってイメージをビルドし、その後タグを付け直すことがわかります。
ここでは、既知のタグを対象に指定し、モードをcontainer、スキャン名を設定し、modeをinfoにしています。また、Dockerfileがディレクトリ内にあるため、ベースイメージの推奨を得るためにextra_opts引数にも追加できます。

これでサイドバーに手動実行のコンテナスキャンが表示され、実行できるようになりました。

手動でコンテナスキャンを実行できました。では、Tiltのビルドごとに自動実行するにはどうすればよいでしょうか。Tiltではビルド前後の依存関係の扱いがまだ十分に成熟していないため、少し難しいケースです。custom_build関数内からSnykを直接呼び出すこともできますが、この拡張機能は、ユーザーが柔軟にSnykスキャンをワークフローに組み込めるよう設計されています。
まず、custom_build関数を変更します。tag=’dev’引数を削除すると、$EXPECTED_REFにはTiltが自動生成する不変タグが設定されます。各ビルドでそのタグを取得し、ファイルシステム上のファイルに書き込みます。タグを書き込む際に末尾に改行が入らないよう、ここではprintfを使っています。
また、ファイルからタグを取得する関数も追加します。read_file関数はblobを返すため、文字列にキャストし、改行も取り除く必要があります。
次にSnyk拡張機能を呼び出し、get_tag関数の出力を対象に設定し、トリガーをautoにします。さらに、ファイルシステム上のファイルへの依存関係を作成することで、ファイルが変更されるたびに拡張機能が実行されるようにします。
Tiltfileをもう一度保存すると、ビルドが実行されるたびに、スキャンも自動的に
Snykのスキャンを開始します。

最後に、アプリケーションのソースコードの依存関係をスキャンしてみましょう。これはパッケージマネージャーにyarnを使うNode.jsアプリケーションなので、フォルダー内に使用しているモジュールのバージョンを示すyarn.lockファイルがあります。
Snykで自動的にスキャンし、脆弱性を確認できます。

このアプリケーションは非常にシンプルで、依存関係に脆弱性は見つかりませんでした。問題ありません。
Tiltは、Kubernetes向けのマイクロサービスベースのアプリケーションのローカル開発を大幅に効率化できる優れたツールです。設定の自由度も高く、自分に合ったワークフローを実現できます。
この記事では、Tilt向けのSnyk拡張機能を使って、TiltのワークフローにSnykスキャンを組み込むさまざまな方法を紹介しました。クラウドネイティブアプリケーションと設定を安全に保ちましょう。無料のSnykアカウントに登録して、ぜひお試しください。
CTFを始めよう
オンデマンドのバーチャル入門ワークショップで、CTFチャレンジの解き方を学びましょう。


