TECH PLAY

電通総研

電通総研 の技術ブログ

822

こんにちは!金融ソリューション事業部の山下です。 前回の 「バーチャルヒューマンをリアルタイムフェイスリグで動かす」 の記事に引き続き、最新の3DCG技術を紹介します。 本記事では、特に映画/ゲーム業界等で VFX に活用されている Houdini を使って、トップ画像のようなフォトリアルな雲を レンダリング します。 雲のような3次元空間上に密度分布を持った現象をシミュレーションする為に、 オープンソース の OpenVDB ライブラリを利用します。 前提知識 1. Voxel 3次元データを表す最小単位です。Voxelが集まったデータをVolumeと呼びます。 座標以外にも様々なデータ(濃度、温度、速度)を持たせることで、水、火、煙、雲などの物理シミュレーションにも活用されています。 2. OpenVDB (画像: https://www.openvdb.org ) 3次元データを操作する為のデータ規格およびモジュールライブラリです。今回用いるHoudiniや、 Blender など様々なCGソフトに採用されています。 Academy Software Foundation が オープンソース として管理しており、2014年に アカデミー賞 (科学技術部門)も受賞しています。 NVIDIA を中心に活発に開発が進められており、直近では以下の新機能が追加されています: NanoVDB (2021):データ構造の改善により、 GPU 処理が可能になりました。これまで計算負荷の高かったボリュームのシミュレーションが高速に可能になったことで、去年ごろからリアルタイムにボリュームデータを レンダリング する ユースケース も増えました。 NeuralVDB (2022): GPU 上の処理に 機械学習 を取り入れることで、メモリ使用量を1/100に削減!現時点で3DCGツールへの導入は限定的だが、既に NVIDIA Omniverse では導入されている模様です。 3. Houdini (画像: https://www.sidefx.com/ja/products/houdini) ) Side Effects Software社が開発する3DCGソフトウェアです。ノードベー スプログ ラミングを採用しており、プロシージャル(手続き的)に可変的なシミュレーションが可能です。(VEXコードというテキストベースの プログラミング言語 も利用可能) 映画、ゲーム、科学などの業界で、20年以上前から複雑なシミュレーションに活用されており、その功績として2018年に アカデミー賞 (科学技術部門)も受賞しています。 Houdiniでボリュームを扱う際には、Houdini独自VolumeとOpenVBDの2種類の処理方式が利用可能です。 4. Redshift (画像: https://docs.redshift3d.com/display/redshift3d/redshift3d+Home ) Redshift Rendering Technologies社(2019年にMaxon社が買収)が開発する3DCG レンダリング ソフトウェアです。高速処理が可能になる GPU レンダリング を2014年に世界で始めて商用化しました。 Maxon社の提供する3DCGツールであるCinema4Dに加えて、Autodesk社のMayaなどでも利用可能です。 元は NVIDIA GPU のみに対応しておりましたが、2021年には macOS にも対応しました。 シミュレーションの実施 実行環境 macOS 12.1 Monterey( Apple M1 Max) Houdini FX 19.5.303 Redshift4houdini 3.0.53 手順 1. 雲の元となるポリゴンを作成 2. ポリゴンをボリュームに変換した後、ノイズをかけてディテールを付与 3. マテリアル、ライトを設定して、Redshiftで レンダリング HoudiniやRedshiftのインストール手順は割愛します。 それでは、始めていきます。 手順1. 雲の元となるポリゴンを作成 ここでは、3つのノードを使用します。 1-1. 球体の作成 最初に、 Sphere ノードで球体を作成します。 主な設定値は、以下です。 Primitive Type:「Polygon」 Frequency:24 ポリゴンの数が少ないとこの後かけるノイズが荒くなってしまうので、適当な数に上げています。 Houdiniの場合、最後になってから増やすのも簡単にできるので、パラメータチューニングは最後に行っても大丈夫です。 1-2. ノイズの付与 次に、Attribute Noise(旧名:mountain)ノードでノイズをかけていきます。 このノードでは様々な種類のノイズを、任意のデータに対してかけることができます。 今回は、先ほど作成したポリゴンの各座標Pに対して、「Sparse Convolution」というノイズをかけています。 主な設定値は、以下です。 Noise Value Amplitude:0.61 Nise Pattern Noise Type:「Sparse Convolution」 Element Size:0.37 Offset:4.42 ジオメトリのデータを参照するViewであるGeometry Spreadsheetを見ると、各ポイントの座標が変化しているのが分かります。 変換前:3-1. sphere ノードの値 変換後:3-2. mountainノードの値 1-3. Y軸方向に変形 雲のような形にする為、Y軸方向を0.5倍にスケールして押し潰します。 Transformノード内に「Scale」という項目があるので、Y軸の値を0.5に設定します。 これで、雲の元となるポリゴンの作成は完了です。 手順2. ポリゴンをボリュームに変換した後、ノイズをかけてディテールを付与 次に、作成したポリゴンをボリュームデータ(OpenVDB)に変換します。 以下3種類のノードを使用します。 2-1. Cloudノードでボリュームに変換 ポリゴンのボリュームへの変換は、Cloudノードを使用します。 主な設定値は、以下です。 Source:「Polygon Model」 Uniform Smpling Divs:「100」 ここで重要なのが、項目「Uniform Smpling Divs」です。この数値によって、Voxelの分割数が決まります。正方形のポリゴンであれば、10倍にすると単純計算では千から100万にVoxelが増える為、適切な数を設定しましょう。 例えばデフォルトの10のままだと、かなりぼやっとした感じになるのが分かります。 CloudノードはOpenVDBを採用しておりますが、Houdiniでは独自のVolumeノードも用意しております。 試しに、両者を比較してみましょう。 先ほどのTransformノードのアウトプットを新しく作成したVolumeノードにも繋ぎます。 それぞれのデータを見てみす。 VolumeノードではVoxel数が40万程度にあるのに対して、 CloudノードではVoxel数が14万強と、大幅に削減されているのが分かります。 Houdini独自のVolumeは、3次元格子内の全てのVoxelデータを 保有 する為にVoxel数が大きくなっています。 このノードが残っている理由としては、Volume表面の精緻な検出に用いる用途などが挙げられます。 2-2. Cloud Noiseノードで、ノイズを付与 次に、Cloud Noiseノードでノイズをかけていきます。 主な設定値は、以下です。 Amplitude:0.129 Element Size:0.6 様々な設定値によって結果が大きく変わるので、自分の気に入る形になるまで調整を繰り返します。 2-3. Volume VOPノードでさらに細かいノイズを部分的に付与 最後に、Volume VOPノードで最終的なアウトプットを出力します。 houdiniでは、ビジュアルプログラミングが可能になるVOPというノードも提供されています。 この中では、値の変換処理や条件分岐など、少し複雑な処理も簡単に記述できます。 Cloudノードによって、雲の濃さを表すdensityという値(Float型)が作成されているので、この値に対して処理を行います。 VOP内の処理詳細は割愛しますが、変換前はほぼ一律になっていたdensityの値を、Perlinノイズを用いて自然なグラデーションに変換します。また外周部分の厚みが薄い部分の値を、条件分岐を用いて0にしています。 実際に結果を確認する為に、Volume Sliceというノードに接続して確認します。 Volumeは、先に挙げたGeometrySpreadsheetのような2次元データとは異なる3次元データですので、 デバッグ には断面を確認する手法が便利です。 今回はXY平面にスライスして確認します。 各Voxel内でdensityの値は数値(float型)として保持されているので、濃い部分を白く、薄い部分を暗くしたグレイスケールで可視化してみます。 変換前:densityがほぼ一定になっており、全体的に色の濃さが均一になっています。 変換後:densityにPerlinノイズによるばらつきが加わった為、色の濃さも自然にまばらになっています。 以上で、雲のVolumeデータが完成しました。 完成したデータはopenVDBファイルとしてアウトプットします。これでHoudini以外の3DCGソフト( Blender など)へimport可能です。 前回記事にて用いた Unreal Engine では執筆時点(2022年8月)でopenVDBの対応はされていないようですが、 オープンソース で プラグイン が開発されておりそちらを利用して利用も可能です。 手順3. マテリアル、ライトを設定して、Redshiftで レンダリング 最後に、カメラ、マテリアル、 レンダリング の設定を行い完成画像を出力します。 3DCGソフトに不慣れな方は戸惑うかもしれませんが、3Dモデルを実際に2次元の画像や動画に変換する処理は レンダリング と呼ばれ、 モデリング やアニメーションとは別のソフトを用いることが一般的です。 HoudiniにもMantraというデフォルトの レンダリング 機能が用意されていますが、これは GPU レンダリング に対応していない為、 GPU レンダリング 対応のレンダラーを用います。 HoudiniではRedshift以外にも様々な レンダリング ソフトに対応しており、OTOY社の Octane Render なども有名です。 レンダリング の設定を行う際に注意すべき点としては、ライト、マテリアルに関しては レンダリング エンジン固有のモジュールを用いる必要がある、という点です。 設定内容が膨大になるので、詳細は省き主要な設定内容のみ記載します。 3-1. カメラ Houdiniのカメラを使用します。解像度やFocul Lengthの設定など、様々な可能です。 Enable Photogrametric Exposure:ON Enable Depth of Field:ON 3-2. ライト 今回は、Redshiftの提供するRSLightDomeというライトを用います。 ライトには点(Point light)、面(Area light)、太陽光(Directional light)など様々な種類のライトがあります。 今回は、360°で撮影したHDRIテクスチャ画像を光源として使用できるRSLightDomeノードを使用します。 光源として使用するテクスチャ画像は、CC0の Poly Heaven を使用しました。 3-3.テクスチャ 雲の質感を設定します。 RS Volumeノードで、拡散(Scatter)、吸収(Absorption)、発光(Emission)などの値を設定します。 3-4. レンダラー レンダリング に関する値を設定します。 Global Illumination: Enabled:ON Primary GI Engine:Brute Force Trace Depth:3 Sample Filtering Filter:「Gauss」 Size:4 Redshift → Advanced → Bucket Size:512 今回はBucketRenderingを使用しました。 バケット サイズの設定をしないと処理速度が20倍くらい変わるため注意しましょう。 完成イメージ 完成画像です(LightDomeの背景画像もそのまま残しています)。 私の環境( Apple M1 Max)では、 Bucket Renderingで1~10sec程度、ProgressiveRenderingだと1~2分ほどかかりました。 終わりに いかがでしょうか? 今回はvbdを作成した後にHoudini内で レンダリング まで行いましたが、 Blender などnanoVDBやneuralVDBに対応している3DCGソフト上であればどこでも扱えます。複数シーケンスとして出力することで、アニメーションがついた表現も可能ですし、OpenVDBはデータ規格だけでなく処理ライブラリも提供されている為、ダイナミックな物理シミュレーションも可能です。 これまで特に映画産業では、雲や炎、水や霧などボリュームデータを用いたシミュレーションは膨大な時間とコンピューティングリソースをかけてプリ レンダリング されてきました。 が、OpenVDBの登場により今後はゲームや メタバース などのリアルタイムインタ ラク ションが求められる環境でも処理が可能になってくると思われます。 現在ISIDは web3領域のグループ横断組織 を立ち上げ、Web3および メタバース 領域のR&Dを行っております(カテゴリー「3DCG」の記事は こちら )。 もし本領域にご興味のある方や、一緒にチャレンジしていきたい方は、ぜひお気軽にご連絡ください! 私たちと同じチームで働いてくれる仲間を、是非お待ちしております! ISID採用ページ(Web3/メタバース/AI) 参考 OpenVDB nanoVDB Houdini: What’s new Pyro and FLIP fluids 執筆: @yamashita.yuki 、レビュー: @sato.taichi ( Shodo で執筆されました )
アバター
こんにちは! ISID X(クロス) イノベーション 本部 オープン イノベーション ラボの奥野正寛です。 Unityを使ったARアプリの開発において基本となるAR Foundationと SDK の1つであるVuforia Engineの組み合わせについて紹介します。 はじめに 環境 Vuforia EngineとAR Foudationのターゲット トラッキングに利用するターゲット AR Foundationの構成 Vuforia Engineの構成 動かしてみた!! Vuforia Engineのターゲットへオクルージョン反映 完成! おまけ 最後に・・ はじめに 基本的にUnityのAR アプリ開発 では AR Foundation もしくは Vuforia Engine 等の SDK のどちらかだけを使います。 平面ト ラッキング や画像ト ラッキング などはどちらにも備わっていますが、AR FoundationのオクルージョンやVuforia Engineの Area Target といったそれぞれ独自の機能があり、組み合わせて使いたいことがあります。 今回はVuforia EngineとAR Foundationの組み合わせについて以下の構成を試しました。 Vuforia EngineとAR Foundationそれぞれのターゲットを組み合わせる Vuforia Engineのターゲットで表示したオブジェクトにAR Foundationのオクルージョンを適用 ※本記事ではVuforia EngineとAR Foudationの組み合わせについて記載し、環境構築については割愛します。 環境 PC: macOS Monterey 12.5 Unity 2020.3.37 Vuforia Engine 10.9.3 AR Foundation 4.1.10 ARKit XR Plugin 4.1.10 mobile: iPad Pro (LiDAR搭載) iOS 15.6 Vuforia EngineとAR Foudationのターゲット ト ラッキング に利用するターゲット Vuforia Developer Libraryのドキュメント を参考に、3つのターゲットを組み合わせました。 Vuforia Engine : Area Target , Image Target AR Foundation : Image ト ラッキング 用の画像として、本の表紙を利用させていただきました。(どちらもUnity勉強にオススメです) Unityの教科書 Unity2019 Unityではじめる ROS・人工知能 ロボットプログラミング実践入門 AR Foundationの構成 以下手順でAR Foundationによる画像ト ラッキング を構成します。 ヒエラルキー にXR -> AR Session OriginとXR -> AR Sessionをそれぞれ追加 AR Session Originへ コンポーネント 「AR Tracked Image Manager」を追加 プロジェクトのアセットより作成 -> XR -> Reference Image Library Reference Image Libraryへ ト ラッキング 用画像を登録(事前に画像ファイルはUnityへインポートしておきます) Physical Sizeより実物のサイズを入力 表示させたいオブジェクトのPrefabを作成(今回はCubeという名のPrefabを作成) AR Tracked Image Managerへ以下を登録 Serialized LibraryにReference Image Library Tracked Image PrefabにオブジェクトのPrefab Vuforia Engineの構成 事前に今回利用するImage TargetとArea Targetのデータをインポートしておきます。 Image Targetは Vuforia Engine developer portal より、 Area Targetは Vuforia Area Target Generator でデータ作成できます。 以下手順でVuforia Engineを構成します。 ヒエラルキー よりそれぞれのターゲットオブジェクトをAR Foundatinoの構成で追加した AR Session Originの子として追加。ここで大事なのは、本来Vuforai Engineの構成で追加する Vuforia EngineのAR Cameraは追加しない ことです。 それぞれのターゲットでインポートしておいたターゲットデータを登録 シーン上にArea Targetによる空間データ、Image Targetによる画像データが表示されれればOKです。 Area Targetのデータとして筆者の部屋を、 iPad のArea Target Generatorでスキャンしたものを使いました。 ヒエラルキー よりそれぞれのターゲットで表示させるオブジェクトを ターゲットの子として追加。今回はArea Tagertに緑色のキューブ、Image Targetに青色のキューブを追加しました。 動かしてみた!! それぞれのターゲットに紐づくオブジェクトが表示されました。 Vuforia EngineとAR Foundationのターゲットが一緒に動くことを確認できました。 Vuforia Area Target:緑色キューブ Vuforia Image Target:青色キューブ AR Foundation Image:赤色キューブ Vuforia Engineのターゲットへオクルージョン反映 現実に存在する人や物体によってARオブジェクトが隠れるように見せるため、AR Foudationのオクルージョンを使用します。 AR Session Originの子であるAR Cameraへ コンポーネント 「AR Occlusion Manager」を追加して、それぞれの設定を変更します。今回は全て最適としました。 完成! 全てのオブジェクトに対して人(動画では手)と物体(動画ではポーチ)にオクルージョンが効いています。 Vuforia Engineでト ラッキング している緑色、青色のキューブにもAR Foudationのオクルージョンが反映されることを確認できました。 おまけ Vuforia EngineのAR Cameraを使った構成にして、AR FoundationのAR Occlusion Manager コンポーネント を追加しても、オクルージョンが反映されるのではないか?と思い試してみましたが、反映されない結果でした。AR Occlusion ManagerはAR FoudationのAR Cameraのみに使えるようです。 最後に・・ 今回はVuforia EngineとAR Foundationを組み合わせたAR アプリ開発 について紹介いたしました。 開発プラットフォームを組み合わせることで、より高度な アプリ開発 が可能となりそうですね。 ただし Vuforia Developer Library に書かれている通りオーバーヘッドや レイテンシー 増加の可能性があるため、両方の機能が必要でない限り1つのシーン内ではどちらかのみ利用することが推奨されます。 最後までお読みいただき、ありがとうございました!! 執筆: @okuno.takahiro 、レビュー: @wakamoto.ryosuke ( Shodo で執筆されました )
アバター
こんにちは! ISID X(クロス) イノベーション 本部 オープン イノベーション ラボの奥野正寛です。 Unityを使ったARアプリの開発において基本となるAR Foundationと SDK の1つであるVuforia Engineの組み合わせについて紹介します。 はじめに 環境 Vuforia EngineとAR Foudationのターゲット トラッキングに利用するターゲット AR Foundationの構成 Vuforia Engineの構成 動かしてみた!! Vuforia Engineのターゲットへオクルージョン反映 完成! おまけ 最後に・・ はじめに 基本的にUnityのAR アプリ開発 では AR Foundation もしくは Vuforia Engine 等の SDK のどちらかだけを使います。 平面ト ラッキング や画像ト ラッキング などはどちらにも備わっていますが、AR FoundationのオクルージョンやVuforia Engineの Area Target といったそれぞれ独自の機能があり、組み合わせて使いたいことがあります。 今回はVuforia EngineとAR Foundationの組み合わせについて以下の構成を試しました。 Vuforia EngineとAR Foundationそれぞれのターゲットを組み合わせる Vuforia Engineのターゲットで表示したオブジェクトにAR Foundationのオクルージョンを適用 ※本記事ではVuforia EngineとAR Foudationの組み合わせについて記載し、環境構築については割愛します。 環境 PC: macOS Monterey 12.5 Unity 2020.3.37 Vuforia Engine 10.9.3 AR Foundation 4.1.10 ARKit XR Plugin 4.1.10 mobile: iPad Pro (LiDAR搭載) iOS 15.6 Vuforia EngineとAR Foudationのターゲット ト ラッキング に利用するターゲット Vuforia Developer Libraryのドキュメント を参考に、3つのターゲットを組み合わせました。 Vuforia Engine : Area Target , Image Target AR Foundation : Image ト ラッキング 用の画像として、本の表紙を利用させていただきました。(どちらもUnity勉強にオススメです) Unityの教科書 Unity2019 Unityではじめる ROS・人工知能 ロボットプログラミング実践入門 AR Foundationの構成 以下手順でAR Foundationによる画像ト ラッキング を構成します。 ヒエラルキー にXR -> AR Session OriginとXR -> AR Sessionをそれぞれ追加 AR Session Originへ コンポーネント 「AR Tracked Image Manager」を追加 プロジェクトのアセットより作成 -> XR -> Reference Image Library Reference Image Libraryへ ト ラッキング 用画像を登録(事前に画像ファイルはUnityへインポートしておきます) Physical Sizeより実物のサイズを入力 表示させたいオブジェクトのPrefabを作成(今回はCubeという名のPrefabを作成) AR Tracked Image Managerへ以下を登録 Serialized LibraryにReference Image Library Tracked Image PrefabにオブジェクトのPrefab Vuforia Engineの構成 事前に今回利用するImage TargetとArea Targetのデータをインポートしておきます。 Image Targetは Vuforia Engine developer portal より、 Area Targetは Vuforia Area Target Generator でデータ作成できます。 以下手順でVuforia Engineを構成します。 ヒエラルキー よりそれぞれのターゲットオブジェクトをAR Foundatinoの構成で追加した AR Session Originの子として追加。ここで大事なのは、本来Vuforai Engineの構成で追加する Vuforia EngineのAR Cameraは追加しない ことです。 それぞれのターゲットでインポートしておいたターゲットデータを登録 シーン上にArea Targetによる空間データ、Image Targetによる画像データが表示されれればOKです。 Area Targetのデータとして筆者の部屋を、 iPad のArea Target Generatorでスキャンしたものを使いました。 ヒエラルキー よりそれぞれのターゲットで表示させるオブジェクトを ターゲットの子として追加。今回はArea Tagertに緑色のキューブ、Image Targetに青色のキューブを追加しました。 動かしてみた!! それぞれのターゲットに紐づくオブジェクトが表示されました。 Vuforia EngineとAR Foundationのターゲットが一緒に動くことを確認できました。 Vuforia Area Target:緑色キューブ Vuforia Image Target:青色キューブ AR Foundation Image:赤色キューブ Vuforia Engineのターゲットへオクルージョン反映 現実に存在する人や物体によってARオブジェクトが隠れるように見せるため、AR Foudationのオクルージョンを使用します。 AR Session Originの子であるAR Cameraへ コンポーネント 「AR Occlusion Manager」を追加して、それぞれの設定を変更します。今回は全て最適としました。 完成! 全てのオブジェクトに対して人(動画では手)と物体(動画ではポーチ)にオクルージョンが効いています。 Vuforia Engineでト ラッキング している緑色、青色のキューブにもAR Foudationのオクルージョンが反映されることを確認できました。 おまけ Vuforia EngineのAR Cameraを使った構成にして、AR FoundationのAR Occlusion Manager コンポーネント を追加しても、オクルージョンが反映されるのではないか?と思い試してみましたが、反映されない結果でした。AR Occlusion ManagerはAR FoudationのAR Cameraのみに使えるようです。 最後に・・ 今回はVuforia EngineとAR Foundationを組み合わせたAR アプリ開発 について紹介いたしました。 開発プラットフォームを組み合わせることで、より高度な アプリ開発 が可能となりそうですね。 ただし Vuforia Developer Library に書かれている通りオーバーヘッドや レイテンシー 増加の可能性があるため、両方の機能が必要でない限り1つのシーン内ではどちらかのみ利用することが推奨されます。 最後までお読みいただき、ありがとうございました!! 執筆: @okuno.takahiro 、レビュー: @wakamoto.ryosuke ( Shodo で執筆されました )
アバター
こんにちは。 XI 本部 AI トランスフォーメンションセンター 所属の山田です。 今回は、 Python の Web アプリケーション開発環境において Poetry を利用し始めたことについて紹介します。 背景 私たちのチームではデータ分析、 システム開発 ともに Python を使用しています。 これまで、 Python を業務で使う際にはパッケージ管理ツールに pip を利用してきました。 しかしながら、継続的に開発・更新を繰り返すことを見越した システム開発 ではたびたび問題を引き起こすことがあり、見直しを図ることにしました。 pip で感じていた課題 まず pip を利用時に感じていた課題を整理します。 pip install パッケージ名 で導入した際に requirements.txt への記載漏れというヒューマンエラーが発生する。 requirements.txt においてパッケージバージョンの記載漏れというヒューマンエラーが発生する。 pip でのパッケージ間の依存関係解決は弱く、インストールの順序関係によってはエラーが発生する。 pip freeze によるバージョン固定ファイルを手動で実施する必要があり、開発者間で異なるバージョンがインストールされ、環境差異が発生する。 開発環境特有のパッケージ管理問題。 requirements-dev.txt のように別ファイルで管理するコストが辛い。 上記の課題の多くは、パッケージ管理ツールを見直すことで解決可能であると判断し、見直しを実施しました。 Poetry そして結果として、Poetry を使うことにしました。 Poetry の公式ドキュメントと GItHub リポジトリ へのリンクを載せておきます。 公式ドキュメント、 https://python-poetry.org/ GitHub リポジトリ 、 https://github.com/python-poetry/poetry Poetry を採用した経緯としては、もともとチーム内でも関心があったツールであり、他社でも導入事例 1 や利点 2 が紹介されていたことが大きいです。 また開発ロードマップが示されており、今後のアップデートに伴って拡充される機能が魅力的でもありました。 Poetry - Feature roadmap、 https://github.com/python-poetry/poetry/issues/1856 pipとPoetryの比較 pip と Poetry の比較を表にまとめたものが以下になります。 # pip poetry 設定ファイル requirements.txt pyproject.toml 設定ファイルのサンプル django==4.0.1 [tool.poetry] name = "sample" version = "0.1.0" description = "" authors = ["username <*****@*******>"] [tool.poetry.dependencies] python = ">=3.8,<3.9" Django = "4.0.1" [tool.poetry.dev-dependencies] django-stubs = "^1.9.0" [build-system] requires = ["poetry-core>=1.0.0"] build-backend = "poetry.core.masonry.api" 設定ファイルに基づく依存解決 pip install -r requirements.txt poetry install パッケージの追加方法 pip install <package-name> requirements.txt には手動で追記。 poetry add <package-name> pyproject.toml には自動で追記。 パッケージのlockファイル requirements.lock pip freeze コマンドでの手動生成 poetry.lock 自動生成。 poetry lock コマンドでの手動生成・更新も可能。 開発環境向けのパッケージ管理 requirements-dev.txt などで管理。 pyproject.toml 内の [tool.poetry.dev-dependencies] で管理。 開発環境向けのパッケージ追加 pip install -r requirements-dev.txt requirements-dev.txt などに手動で追記する必要あり。 poetry add -D <package-name> pyproject.toml には自動で追記。 Poetry 導入のメリット もともと pip で感じていた課題と対応する形でメリットについて整理します。 pip install パッケージ名 で導入した際に requirements.txt への記載漏れというヒューマンエラーが発生する。 requirements.txt においてパッケージバージョンの記載漏れというヒューマンエラーが発生する。 上記2つの課題については、Poetry によってインストール時のパッケージとバージョンが自動で追記されるため、ヒューマンエラーの発生を防げるようになりました。 pip でのパッケージ間の依存関係解決は弱く、インストールの順序関係によってはエラーが発生する。 これまで依存関係解決について触れませんでしたが、Poetry は pip と異なるより強固な依存関係リ ゾル バが採用されており、順序関係によるような依存関係解決のエラーは発生しにくいようになっています。 これは Poetry の README でも強みとして記載されています。 Poetry - Why?、 https://github.com/python-poetry/poetry#why pip freeze によるバージョン固定ファイルを手動で実施する必要があり、開発者間で異なるバージョンがインストールされ、環境差異が発生する。 Poetry では poetry.lock が自動生成されます。 また poetry.lock が存在すれば poetry install 時は優先して読み込まれるため、開発者簡で異なるバージョンがインストールされる事象を防ぐことができます。 開発環境特有のパッケージ管理問題。 requirements-dev.txt のように別ファイルで管理するコストが辛い。 Poetryでは、 pyproject.toml 内の [tool.poetry.dev-dependencies] で開発環境固有のパッケージを管理できます。 これにより、開発環境固有のパッケージも同一ファイルで管理でき、コストを削減できます。 pip から Poetry に移行するに当たって 実際に pip から Poetry へ移行するに当たって考慮すべきポイントをまとめておきます。 requirements.txt から pyproject.toml への移行について まず requirements.txt から pyproject.toml に移行する必要があります。 現時点では、Poetry 側では依存関係を既存の requirements.txt から取り入れる方法はサポートされていません。 そのため、既存の開発環境で Poetry を適用する際には、移行作業に充てる時間を多く見積もっておいた方が良いでしょう。 シェルスクリプト を使うことである程度、自動化は図れますが、 pyproject.toml を準備するコストがかかると認識しておいて損はないでしょう。 参考までに既存プロジェクトにおける Poetry への移行方法について、 Stack Overflow での質問リンクを載せておきます。 How to import requirements.txt from an existing project using Poetry、 https://stackoverflow.com/questions/62764148/how-to-import-requirements-txt-from-an-existing-project-using-poetry チームメンバーの開発環境の整備 チームで使えるように開発環境構築のドキュメントなどを整え、設定などを共有する必要があります。 例えば、私たちのチームでは Poetry によって作成される仮想環境の venv がプロジェクト内になるように設定するよう周知しています。 # プロジェクト内に`.venv`を置くように設定変更 poetry config virtualenvs.in-project true 新しいツールを導入する際は、このあたりの設定の意図がチームメンバーに伝わるようにすることは開発をスムーズに進める上でも重要でしょう。 CI/CD環境の更新 すでに CI/CD パイプラインがあるプロジェクトでは、Poetry を利用する方式に変更する必要があります。 GitHub Actions 上では Poetry の Actions が公開されているのでそちらを利用するのが良いでしょう。 Python Poetry Action、 https://github.com/marketplace/actions/python-poetry-action 以下は、 GitHub Actions において Poetry を使って依存関係解決を行う際の設定ファイルの記述例になります。 - name: Setup Poetry uses: abatilo/actions-poetry@v2.0.0 with: poetry-version: "1.1.14" - name: Install dependencies run: poetry install まとめ 今回は、私たちのチームで Python のパッケージ管理ツールとして Poetry を利用し始めたことについて紹介しました。 似たような課題をお持ちの方はぜひ Poetry の導入を検討してみてはいかがでしょうか。 私たちは同じチームで働いてくれる仲間を探しています。AI 製品開発に興味がある方のご応募をお待ちしております。 AI製品エンジニアリーダー AIエンジニア/コンサルタント 執筆: @yamada.y 、レビュー: @sato.taichi ( Shodo で執筆されました ) サーバー アプリ開発 環境( Python /FastAPI)、 https://future-architect.github.io/articles/20210611a/ ↩ pipとpipenvとpoetryの技術的・歴史的背景とその展望、 https://vaaaaaanquish.hatenablog.com/entry/2021/03/29/221715 ↩
アバター
こんにちは。 XI 本部 AI トランスフォーメンションセンター 所属の山田です。 今回は、 Python の Web アプリケーション開発環境において Poetry を利用し始めたことについて紹介します。 背景 私たちのチームではデータ分析、 システム開発 ともに Python を使用しています。 これまで、 Python を業務で使う際にはパッケージ管理ツールに pip を利用してきました。 しかしながら、継続的に開発・更新を繰り返すことを見越した システム開発 ではたびたび問題を引き起こすことがあり、見直しを図ることにしました。 pip で感じていた課題 まず pip を利用時に感じていた課題を整理します。 pip install パッケージ名 で導入した際に requirements.txt への記載漏れというヒューマンエラーが発生する。 requirements.txt においてパッケージバージョンの記載漏れというヒューマンエラーが発生する。 pip でのパッケージ間の依存関係解決は弱く、インストールの順序関係によってはエラーが発生する。 pip freeze によるバージョン固定ファイルを手動で実施する必要があり、開発者間で異なるバージョンがインストールされ、環境差異が発生する。 開発環境特有のパッケージ管理問題。 requirements-dev.txt のように別ファイルで管理するコストが辛い。 上記の課題の多くは、パッケージ管理ツールを見直すことで解決可能であると判断し、見直しを実施しました。 Poetry そして結果として、Poetry を使うことにしました。 Poetry の公式ドキュメントと GItHub リポジトリ へのリンクを載せておきます。 公式ドキュメント、 https://python-poetry.org/ GitHub リポジトリ 、 https://github.com/python-poetry/poetry Poetry を採用した経緯としては、もともとチーム内でも関心があったツールであり、他社でも導入事例 1 や利点 2 が紹介されていたことが大きいです。 また開発ロードマップが示されており、今後のアップデートに伴って拡充される機能が魅力的でもありました。 Poetry - Feature roadmap、 https://github.com/python-poetry/poetry/issues/1856 pipとPoetryの比較 pip と Poetry の比較を表にまとめたものが以下になります。 # pip poetry 設定ファイル requirements.txt pyproject.toml 設定ファイルのサンプル django==4.0.1 [tool.poetry] name = "sample" version = "0.1.0" description = "" authors = ["username <*****@*******>"] [tool.poetry.dependencies] python = ">=3.8,<3.9" Django = "4.0.1" [tool.poetry.dev-dependencies] django-stubs = "^1.9.0" [build-system] requires = ["poetry-core>=1.0.0"] build-backend = "poetry.core.masonry.api" 設定ファイルに基づく依存解決 pip install -r requirements.txt poetry install パッケージの追加方法 pip install <package-name> requirements.txt には手動で追記。 poetry add <package-name> pyproject.toml には自動で追記。 パッケージのlockファイル requirements.lock pip freeze コマンドでの手動生成 poetry.lock 自動生成。 poetry lock コマンドでの手動生成・更新も可能。 開発環境向けのパッケージ管理 requirements-dev.txt などで管理。 pyproject.toml 内の [tool.poetry.dev-dependencies] で管理。 開発環境向けのパッケージ追加 pip install -r requirements-dev.txt requirements-dev.txt などに手動で追記する必要あり。 poetry add -D <package-name> pyproject.toml には自動で追記。 Poetry 導入のメリット もともと pip で感じていた課題と対応する形でメリットについて整理します。 pip install パッケージ名 で導入した際に requirements.txt への記載漏れというヒューマンエラーが発生する。 requirements.txt においてパッケージバージョンの記載漏れというヒューマンエラーが発生する。 上記2つの課題については、Poetry によってインストール時のパッケージとバージョンが自動で追記されるため、ヒューマンエラーの発生を防げるようになりました。 pip でのパッケージ間の依存関係解決は弱く、インストールの順序関係によってはエラーが発生する。 これまで依存関係解決について触れませんでしたが、Poetry は pip と異なるより強固な依存関係リ ゾル バが採用されており、順序関係によるような依存関係解決のエラーは発生しにくいようになっています。 これは Poetry の README でも強みとして記載されています。 Poetry - Why?、 https://github.com/python-poetry/poetry#why pip freeze によるバージョン固定ファイルを手動で実施する必要があり、開発者間で異なるバージョンがインストールされ、環境差異が発生する。 Poetry では poetry.lock が自動生成されます。 また poetry.lock が存在すれば poetry install 時は優先して読み込まれるため、開発者簡で異なるバージョンがインストールされる事象を防ぐことができます。 開発環境特有のパッケージ管理問題。 requirements-dev.txt のように別ファイルで管理するコストが辛い。 Poetryでは、 pyproject.toml 内の [tool.poetry.dev-dependencies] で開発環境固有のパッケージを管理できます。 これにより、開発環境固有のパッケージも同一ファイルで管理でき、コストを削減できます。 pip から Poetry に移行するに当たって 実際に pip から Poetry へ移行するに当たって考慮すべきポイントをまとめておきます。 requirements.txt から pyproject.toml への移行について まず requirements.txt から pyproject.toml に移行する必要があります。 現時点では、Poetry 側では依存関係を既存の requirements.txt から取り入れる方法はサポートされていません。 そのため、既存の開発環境で Poetry を適用する際には、移行作業に充てる時間を多く見積もっておいた方が良いでしょう。 シェルスクリプト を使うことである程度、自動化は図れますが、 pyproject.toml を準備するコストがかかると認識しておいて損はないでしょう。 参考までに既存プロジェクトにおける Poetry への移行方法について、 Stack Overflow での質問リンクを載せておきます。 How to import requirements.txt from an existing project using Poetry、 https://stackoverflow.com/questions/62764148/how-to-import-requirements-txt-from-an-existing-project-using-poetry チームメンバーの開発環境の整備 チームで使えるように開発環境構築のドキュメントなどを整え、設定などを共有する必要があります。 例えば、私たちのチームでは Poetry によって作成される仮想環境の venv がプロジェクト内になるように設定するよう周知しています。 # プロジェクト内に`.venv`を置くように設定変更 poetry config virtualenvs.in-project true 新しいツールを導入する際は、このあたりの設定の意図がチームメンバーに伝わるようにすることは開発をスムーズに進める上でも重要でしょう。 CI/CD環境の更新 すでに CI/CD パイプラインがあるプロジェクトでは、Poetry を利用する方式に変更する必要があります。 GitHub Actions 上では Poetry の Actions が公開されているのでそちらを利用するのが良いでしょう。 Python Poetry Action、 https://github.com/marketplace/actions/python-poetry-action 以下は、 GitHub Actions において Poetry を使って依存関係解決を行う際の設定ファイルの記述例になります。 - name: Setup Poetry uses: abatilo/actions-poetry@v2.0.0 with: poetry-version: "1.1.14" - name: Install dependencies run: poetry install まとめ 今回は、私たちのチームで Python のパッケージ管理ツールとして Poetry を利用し始めたことについて紹介しました。 似たような課題をお持ちの方はぜひ Poetry の導入を検討してみてはいかがでしょうか。 私たちは同じチームで働いてくれる仲間を探しています。AI 製品開発に興味がある方のご応募をお待ちしております。 AI製品エンジニアリーダー AIエンジニア/コンサルタント 執筆: @yamada.y 、レビュー: @sato.taichi ( Shodo で執筆されました ) サーバー アプリ開発 環境( Python /FastAPI)、 https://future-architect.github.io/articles/20210611a/ ↩ pipとpipenvとpoetryの技術的・歴史的背景とその展望、 https://vaaaaaanquish.hatenablog.com/entry/2021/03/29/221715 ↩
アバター
こんにちは。ISID 金融ソリューション事業部の若本です。 1ヶ月ほど前にOura Ringという スマートデバイス を購入しまして、生体データを色々取り溜めていました。せっかくデータを取るなら何かに使いたいということで、 機械学習 モデルと組み合わせて就寝時間をレコメンドしてみます。 Oura Ringとは? Oura Ring は 指輪型の健康トラッカー です。多種のセンサーを内蔵しており、運動や睡眠、心拍数に関するデータを日々蓄積しています。完全に個人の感想ですが、充電持ちがよく着用の違和感もないので重宝しています。 なお、蓄積したデータは API を使ってアクセスできます。 API は2種類ありますが、今回は Oura API V1 のデータのみを使いました。より詳細なデータを取得できる Oura API V2 もありますが、こちらは運動や心拍のデータがメインとなっており、コンディションや睡眠に関するデータは記事執筆時点で提供されていません。今回は API V1で取得できるデータのうち、 機械学習 モデルへの入力として「前日の睡眠時間・起床時間・就寝時間」を、出力として「翌日のReadinessスコア」を使用しました。 ※V1の機能はV2に順次移行していくようですので、仕様は変わる可能性があります。適宜 API リファレンスをご参照ください。 Oura Ringでやりたいこと Oura Ringでは平均的な就寝時間の範囲を把握することはできますが、就寝時間を変えることによって体調がどれほど変化するのかはわかりません。そこで、Oura Ringのデータから「何時に寝ればいいのか」「それによってどれくらい次の日のコンディションがよくなるのか」を教えてくれるAIモデルを作成します。 Oura Ringでは体が準備できているかを表す指標としてReadinessスコアを算出しており、これを疑似的にコンディションとみなすことができそうです。このReadinessスコアを高くするために、 前日の睡眠実績から「今日は何時に寝るべきか」を推論 することがゴールです。 機械学習 モデルによる予測 Oura Ringで取得している睡眠実績データを基に、同じくOura Ringで算出されるReadinessスコアを予測します。取り貯めた1ヶ月のデータをOura API V1で取得して得られた前日の睡眠時間、起床時間、就寝時間を基に、翌日のReadinessスコアを予測する 機械学習 モデルを作成しましょう。 ここで、Oura API で取得したデータについて前処理を行います。取得した起床時間/就寝時間は タイムゾーン なのでそのまま 機械学習 モデルで扱うことができません。floatへ型変換を行いましょう。具体的には、「何時何分か」の情報を数値情報に変換します。一番簡単なのは1日を0~1の数値に落とし込むことですが、単純に24で除算してしまうと24時以降の値が不連続になってしまうので、14時までのデータは24を足すことで連続するようにしました。睡眠時間とスコアについても適当にスケーリングします。 def time_to_float_converter (x): time = pd.to_datetime(x) hour = (time.hour + 24 if time.hour < 14 else time.hour) / 24 - 0.5 minute = time.minute / ( 24 * 60 ) return (hour+minute) info_df[ 'sleep_bedtime_start' ] = info_df[ 'sleep_bedtime_start' ].apply( lambda x: time_to_float_converter(x)) info_df[ 'sleep_bedtime_end' ] = info_df[ 'sleep_bedtime_end' ].apply( lambda x: time_to_float_converter(x)) info_df[ 'sleep_duration' ] = info_df[ 'sleep_duration' ] / 3600 / 12 info_df[ 'readiness_score' ] /= 100 前処理後のデータは以下のようになります。全てのデータをfloat型に落とし込めていることが確認できました。 Readinessスコアの説明 によると、Readinessスコアは運動・睡眠・心拍数の長期的な推移で算出されているそうですが、今回は睡眠データの一部しか使わないため、正確にReadinessスコアを予測することは不可能です。さらに期間も短いため、外れ値がモデルの学習に影響を及ぼしやすい条件になっています。そこで、予測モデルは外れ値の影響を受けづらい Huber Regressor を選定しました。なお、モデルの作成時には PyCaret でチューニングしたパラメータを用いています。 X_train = info_df.drop([ 'readiness_score' ], axis= 1 ) y_train = info_df[ 'readiness_score' ] model = HuberRegressor(**params) model.fit(X_train, y_train) DiCEで就寝時間をレコメンド 上記のモデルで翌日のReadinessスコアを予測したい場合、就寝時間の入力を使うことができません。なぜなら、予測時点でまだ就寝時間が決まっていないからです。 なので、モデルの理想の出力に合わせて就寝時間を計算し、就寝時間をレコメンドします。反実仮想説明(CE: Counterfactual Explanation)に用いられるDiCE 1 でこれを実現します。 DiCEの詳細な説明は省きますが、DiCEでは「もしも~の入力とその予測結果」(反実仮想サンプル)を生成します。詳細は 原著論文 をご参照ください。DiCEを使用することで、予測を変えるために入力をどれだけ変えるべきかがわかります。 まず先ほど学習済みの 機械学習 モデルをDiCEに取り込みます。 d = dice_ml.Data(dataframe=info_df, continuous_features = list (info_df.drop([ 'readiness_score' ], axis= 1 ).columns), # 連続変数の指定(今回はすべて該当) outcome_name = 'readiness_score' ) m = dice_ml.Model(model=model, backend= "sklearn" , model_type= 'regressor' ) exp = dice_ml.Dice(d, m, method= "random" ) 次にDiCEを用いて、学習データから反実仮想サンプルを生成してみます。今回は2つ生成してみましょう。21時~27時の間に就寝するという制約を付け、Readinessスコアが80点以上となるようなサンプルを生成します。 index = 0 counterfactuals_num = 2 conf = exp.generate_counterfactuals(X_train.iloc[index:index+ 1 , :], total_CFs=counterfactuals_num, features_to_vary=[ "sleep_bedtime_start" ], permitted_range={ 'sleep_bedtime_start' : [ 0.417 , 0.625 ]}, # 21時~27時の間 desired_range=[ 0.8 , 1.0 ]) # スコアが80~100点ならOK conf.visualize_as_dataframe(show_only_changes= True ) 上記のような結果が得られました。sleep_duration(前日の睡眠時間)やsleep_bedtime_end(起床時間)は変わらず、sleep_bedtime_start(就寝時間)のみ変化していることが分かります。最後に、得られた反実仮想サンプルを時刻に戻して出力してみます。 def float_to_time_converter (x: float ): time_tmp = (x + 0.5 ) * 24 f, i = math.modf(time_tmp) pred_hour = int (i) pred_minites = int (f * 60 ) return pred_hour, pred_minites def messenger (df): for i, row in df.iterrows(): pred_hour, pred_minites = float_to_time_converter(row[ 'sleep_bedtime_start' ]) score = row[ 'readiness_score' ] print (f '<候補{i+1}> {pred_hour}:{pred_minites}に就寝すると{score*100:.1f}点のReadinessスコアが見込めます。' ) conf_df = conf.cf_examples_list[ 0 ].final_cfs_df_sparse # DiCEの結果をpandasに変換 messenger(conf_df) 前日の睡眠状態から、「24時過ぎに寝ること」をレコメンドされました。現実的な数値なので納得感はあります。23時過ぎの就寝もレコメンドされていますが、1時間早く寝ても翌日の予測Readinessスコアは0.2点しか上がらないようです。 思ったよりも就寝時間の差による影響が少なかった印象ですが、より長い期間のデータを基にモデルを作ると顕著な傾向が見られるかもしれません。もしくは就寝時間だけでなく、今回使用しなかった運動データなどについてもレコメンド対象とすれば、より大幅にReadinessスコアをコン トロール できそうです。 おわりに 今回は、Oura Ringで取得したデータを用いた就寝時間のレコメンドについて紹介しました。今回は簡易的な検証として睡眠データとReadinessスコアのみを用いましたが、好みに合わせて入力を変えるのも面白そうです。予測したい変数についても1日の総タイピング数、Gitのcommit行数などに変えてもいいかもしれません。 さらに、 LINE Notify や Heroku などのサービスを組み合わせることで就寝時間を毎日通知してくれるようにもできます。この指輪1つで色々と面白いことができそうですね。 おまけ 今回使用しているコードは こちら に公開しています。Oura Ringをお持ちの方はぜひお試しください。 執筆: @wakamoto.ryosuke 、レビュー: @higa ( Shodo で執筆されました ) DiCE: Diverse Counterfactual Explanations( https://github.com/interpretml/DiCE ) ↩
アバター
こんにちは。ISID 金融ソリューション事業部の若本です。 1ヶ月ほど前にOura Ringという スマートデバイス を購入しまして、生体データを色々取り溜めていました。せっかくデータを取るなら何かに使いたいということで、 機械学習 モデルと組み合わせて就寝時間をレコメンドしてみます。 Oura Ringとは? Oura Ring は 指輪型の健康トラッカー です。多種のセンサーを内蔵しており、運動や睡眠、心拍数に関するデータを日々蓄積しています。完全に個人の感想ですが、充電持ちがよく着用の違和感もないので重宝しています。 なお、蓄積したデータは API を使ってアクセスできます。 API は2種類ありますが、今回は Oura API V1 のデータのみを使いました。より詳細なデータを取得できる Oura API V2 もありますが、こちらは運動や心拍のデータがメインとなっており、コンディションや睡眠に関するデータは記事執筆時点で提供されていません。今回は API V1で取得できるデータのうち、 機械学習 モデルへの入力として「前日の睡眠時間・起床時間・就寝時間」を、出力として「翌日のReadinessスコア」を使用しました。 ※V1の機能はV2に順次移行していくようですので、仕様は変わる可能性があります。適宜 API リファレンスをご参照ください。 Oura Ringでやりたいこと Oura Ringでは平均的な就寝時間の範囲を把握することはできますが、就寝時間を変えることによって体調がどれほど変化するのかはわかりません。そこで、Oura Ringのデータから「何時に寝ればいいのか」「それによってどれくらい次の日のコンディションがよくなるのか」を教えてくれるAIモデルを作成します。 Oura Ringでは体が準備できているかを表す指標としてReadinessスコアを算出しており、これを疑似的にコンディションとみなすことができそうです。このReadinessスコアを高くするために、 前日の睡眠実績から「今日は何時に寝るべきか」を推論 することがゴールです。 機械学習 モデルによる予測 Oura Ringで取得している睡眠実績データを基に、同じくOura Ringで算出されるReadinessスコアを予測します。取り貯めた1ヶ月のデータをOura API V1で取得して得られた前日の睡眠時間、起床時間、就寝時間を基に、翌日のReadinessスコアを予測する 機械学習 モデルを作成しましょう。 ここで、Oura API で取得したデータについて前処理を行います。取得した起床時間/就寝時間は タイムゾーン なのでそのまま 機械学習 モデルで扱うことができません。floatへ型変換を行いましょう。具体的には、「何時何分か」の情報を数値情報に変換します。一番簡単なのは1日を0~1の数値に落とし込むことですが、単純に24で除算してしまうと24時以降の値が不連続になってしまうので、14時までのデータは24を足すことで連続するようにしました。睡眠時間とスコアについても適当にスケーリングします。 def time_to_float_converter (x): time = pd.to_datetime(x) hour = (time.hour + 24 if time.hour < 14 else time.hour) / 24 - 0.5 minute = time.minute / ( 24 * 60 ) return (hour+minute) info_df[ 'sleep_bedtime_start' ] = info_df[ 'sleep_bedtime_start' ].apply( lambda x: time_to_float_converter(x)) info_df[ 'sleep_bedtime_end' ] = info_df[ 'sleep_bedtime_end' ].apply( lambda x: time_to_float_converter(x)) info_df[ 'sleep_duration' ] = info_df[ 'sleep_duration' ] / 3600 / 12 info_df[ 'readiness_score' ] /= 100 前処理後のデータは以下のようになります。全てのデータをfloat型に落とし込めていることが確認できました。 Readinessスコアの説明 によると、Readinessスコアは運動・睡眠・心拍数の長期的な推移で算出されているそうですが、今回は睡眠データの一部しか使わないため、正確にReadinessスコアを予測することは不可能です。さらに期間も短いため、外れ値がモデルの学習に影響を及ぼしやすい条件になっています。そこで、予測モデルは外れ値の影響を受けづらい Huber Regressor を選定しました。なお、モデルの作成時には PyCaret でチューニングしたパラメータを用いています。 X_train = info_df.drop([ 'readiness_score' ], axis= 1 ) y_train = info_df[ 'readiness_score' ] model = HuberRegressor(**params) model.fit(X_train, y_train) DiCEで就寝時間をレコメンド 上記のモデルで翌日のReadinessスコアを予測したい場合、就寝時間の入力を使うことができません。なぜなら、予測時点でまだ就寝時間が決まっていないからです。 なので、モデルの理想の出力に合わせて就寝時間を計算し、就寝時間をレコメンドします。反実仮想説明(CE: Counterfactual Explanation)に用いられるDiCE 1 でこれを実現します。 DiCEの詳細な説明は省きますが、DiCEでは「もしも~の入力とその予測結果」(反実仮想サンプル)を生成します。詳細は 原著論文 をご参照ください。DiCEを使用することで、予測を変えるために入力をどれだけ変えるべきかがわかります。 まず先ほど学習済みの 機械学習 モデルをDiCEに取り込みます。 d = dice_ml.Data(dataframe=info_df, continuous_features = list (info_df.drop([ 'readiness_score' ], axis= 1 ).columns), # 連続変数の指定(今回はすべて該当) outcome_name = 'readiness_score' ) m = dice_ml.Model(model=model, backend= "sklearn" , model_type= 'regressor' ) exp = dice_ml.Dice(d, m, method= "random" ) 次にDiCEを用いて、学習データから反実仮想サンプルを生成してみます。今回は2つ生成してみましょう。21時~27時の間に就寝するという制約を付け、Readinessスコアが80点以上となるようなサンプルを生成します。 index = 0 counterfactuals_num = 2 conf = exp.generate_counterfactuals(X_train.iloc[index:index+ 1 , :], total_CFs=counterfactuals_num, features_to_vary=[ "sleep_bedtime_start" ], permitted_range={ 'sleep_bedtime_start' : [ 0.417 , 0.625 ]}, # 21時~27時の間 desired_range=[ 0.8 , 1.0 ]) # スコアが80~100点ならOK conf.visualize_as_dataframe(show_only_changes= True ) 上記のような結果が得られました。sleep_duration(前日の睡眠時間)やsleep_bedtime_end(起床時間)は変わらず、sleep_bedtime_start(就寝時間)のみ変化していることが分かります。最後に、得られた反実仮想サンプルを時刻に戻して出力してみます。 def float_to_time_converter (x: float ): time_tmp = (x + 0.5 ) * 24 f, i = math.modf(time_tmp) pred_hour = int (i) pred_minites = int (f * 60 ) return pred_hour, pred_minites def messenger (df): for i, row in df.iterrows(): pred_hour, pred_minites = float_to_time_converter(row[ 'sleep_bedtime_start' ]) score = row[ 'readiness_score' ] print (f '<候補{i+1}> {pred_hour}:{pred_minites}に就寝すると{score*100:.1f}点のReadinessスコアが見込めます。' ) conf_df = conf.cf_examples_list[ 0 ].final_cfs_df_sparse # DiCEの結果をpandasに変換 messenger(conf_df) 前日の睡眠状態から、「24時過ぎに寝ること」をレコメンドされました。現実的な数値なので納得感はあります。23時過ぎの就寝もレコメンドされていますが、1時間早く寝ても翌日の予測Readinessスコアは0.2点しか上がらないようです。 思ったよりも就寝時間の差による影響が少なかった印象ですが、より長い期間のデータを基にモデルを作ると顕著な傾向が見られるかもしれません。もしくは就寝時間だけでなく、今回使用しなかった運動データなどについてもレコメンド対象とすれば、より大幅にReadinessスコアをコン トロール できそうです。 おわりに 今回は、Oura Ringで取得したデータを用いた就寝時間のレコメンドについて紹介しました。今回は簡易的な検証として睡眠データとReadinessスコアのみを用いましたが、好みに合わせて入力を変えるのも面白そうです。予測したい変数についても1日の総タイピング数、Gitのcommit行数などに変えてもいいかもしれません。 さらに、 LINE Notify や Heroku などのサービスを組み合わせることで就寝時間を毎日通知してくれるようにもできます。この指輪1つで色々と面白いことができそうですね。 おまけ 今回使用しているコードは こちら に公開しています。Oura Ringをお持ちの方はぜひお試しください。 執筆: @wakamoto.ryosuke 、レビュー: @higa ( Shodo で執筆されました ) DiCE: Diverse Counterfactual Explanations( https://github.com/interpretml/DiCE ) ↩
アバター
こんにちは。X(クロス) イノベーション 本部 ソフトウェアデザインセンター セキュリティグループの耿です。 クラウド インフラをIaC化すると、静的セキュリティスキャンができるようになります。インフラをデプロイする前にセキュリティ上の問題や、ベストプ ラク ティスに沿っていない構成を知ることができるため、ぜひスキャンしておきたいところです。 今までのスキャンツールとしては、以下のようなものがありました。 tfsec OSS Terraformに対応 cfn_nag OSS CloudFormationテンプレートに対応 terrascan OSS Terraform、CloudFormationテンプレート、Azure Resource Managerなどに対応 Snyk IaC 有償(無償プランあり) Terraform、CloudFormationテンプレート、Azure Resource Managerなどに対応 最近、 AWS CDKで使いやすい、 AWS 開発の OSS ツール cdk-nag の存在を知り、非常にテンションが上がっているためブログを書いております! 前半では従来のツールでCDKコードをスキャンするときの問題点とcdk-nagの導入方法、後半では実用する時に欠かせない、cdk-nagエラーのサプレス方法を書いていきます。 TL;DR CDKのセキュリティスキャンはcdk-nagが使いやすい 意味のあるCIにするためには正しいサプレスが重要。細かい粒度でサプレスするべき appliesTo でリソースレベルよりもさらに細かい粒度でサプレスできる CDKが勝手に作成するリソースについては、 applyToChildren を true にするか、 addResourceSuppressionsByPath でサプレスできる サプレスはCDKコードの最後にまとめて書くのが良さそう 既存のCDKに導入する時、 コメントアウト して少しずつ対応すると良い TL;DR cfn_nagでCDKをスキャンするときの問題点 問題点1:CDKコードとの関連が分かりにくい 問題点2:サプレスがイケてない cdk-nagセットアップ スキャンしてみる cdk-nagのサプレス方法 なぜサプレスが重要なのか 基本的なサプレス スタック全体で特定のルールをサプレス より細かい粒度のサプレス CDKが作成するリソースのサプレス interfaceEndpoints作成時のWarningのサプレス サプレスはどこに書くべきか まとめ cfn_nagでCDKをスキャンするときの問題点 cdk-nagの存在を知る前までは、cfn_nagとterrascanを GitHub Actionsのワークフローに組み込んで使っていました。どちらのツールも直接CDKをサポートしていませんが、 cdk synth で生成されるCloudFormationテンプレートに対してスキャンをかけることができます。 例として、cfn_nagのスキャン結果は次のようになります(一部抜粋、編集済) ------------------------------------------------------------ cdk.out/MyStack.template.json ------------------------------------------------------------ | WARN W89 | | Resource: ["AWS679f53fac002430cb0da5b7982bd22872D164C4C", "SomeTrivialFunction4CC789DF"] | Line Numbers: [647, 2395] | | Lambda functions should be deployed inside a VPC これには次のような問題があることがわかりました。 問題点1:CDKコードとの関連が分かりにくい スキャン結果では行番号を出力してくれていますが、CloudFormationテンプレート内の行番号なので、CDKで探す際の参考にはなりません。 またリソースの論理IDも出力されますが、こちらもCloudFormationテンプレートに変換した後のIDなので、CDKで記述したIDとは少し異なります。明示的にCDKで定義したリソースならば、IDの先頭の文字列を見ればどのリソースかなんとなく分かりますが、CDKが自動で作成するリソースについてはIDが勝手に割り振られるため、何によって作成されたリソースかは判断つきません。 問題点2:サプレスがイケてない こちらが一番重要です。cfn_nagでは特定のリソースに対して特定のルールをサプレスしたい場合、CloudFromationテンプレートのMetadataに記載することになります。 CDKからMetadataを追加する場合、一度 CloudFormationリソースとして取得する 必要があり、コードが煩雑になります。(CDKが自動で作成するリソースのルールをサプレスする方法は、まだ試していませんがさらに大変そうです) const cfnFunction = someTrivialFunction.node.defaultChild as lambda.CfnFunction ; cfnFunction.cfnOptions.metadata = { cfn_nag: { rules_to_suppress: [{ id: "W89" , reason: "No need to deploy this function inside VPC" }] } , } ; cdk-nagセットアップ ここからはcdk-nagを使っていきます。 AWSのブログ や cdk-nagのREADME に従ってセットアップします。 cdk-nagパッケージをインストールします。 npm install -D cdk-nag v2.15.32時点でルールパックは5種類ありますが、今回は AWS Solutions を利用します。またエラー詳細を出力するように verbose: true を指定します。 import * as cdk from "aws-cdk-lib" ; import { AwsSolutionsChecks } from "cdk-nag" ; import { ExampleStack } from "../lib/example-stack" ; const app = new cdk.App (); new ExampleStack ( app , "ExampleStack" , {} ); cdk.Aspects. of( app ) .add (new AwsSolutionsChecks ( { verbose: true } )); cfn_nagと異なり、cdk-nagはCDKと直接統合されており、CDKの Aspects を利用し、Synthesize前のPrepareの段階でコードを実行する仕組みです。サプレスされていないルール違反がある場合はフローの途中でエラーが返るため、 cdk deploy を実行しても後続のデプロイ処理がされません。CI/CDに組み込みやすいです。 スキャンしてみる まずは簡単なS3 バケット を作って、 cdk synth を実行します。 import { Stack , StackProps } from "aws-cdk-lib" ; import * as s3 from "aws-cdk-lib/aws-s3" ; import { Construct } from "constructs" ; export class ExampleStack extends Stack { constructor( scope: Construct , id: string , props?: StackProps ) { super( scope , id , props ); const myBucket = new s3.Bucket ( this , "MyBucket" , {} ); } } するとエラーが4つ出ました。 [Error at /ExampleStack/MyBucket/Resource] AwsSolutions-S1: The S3 Bucket has server access logs disabled. The bucket should have server access logging enabled to provide detailed records for the requests that are made to the bucket. [Error at /ExampleStack/MyBucket/Resource] AwsSolutions-S2: The S3 Bucket does not have public access restricted and blocked. The bucket should have public access restricted and blocked to prevent unauthorized access. [Error at /ExampleStack/MyBucket/Resource] AwsSolutions-S3: The S3 Bucket does not default encryption enabled. The bucket should minimally have SSE enabled to help protect data-at-rest. [Error at /ExampleStack/MyBucket/Resource] AwsSolutions-S10: The S3 Bucket or bucket policy does not require requests to use SSL. You can use HTTPS (TLS) to help prevent potential attackers from eavesdropping on or manipulating network traffic using person-in-the-middle or similar attacks. You should allow only encrypted connections over HTTPS (TLS) using the aws:SecureTransport condition on Amazon S3 bucket policies. 内容は以下のとおりです AwsSolutions-S1:サーバー アクセスログ が有効でない AwsSolutions-S2:ブロックパブリックアクセスが有効でない AwsSolutions-S3:デフォルト暗号化が有効でない AwsSolutions-S10: TLS 通信の強制が有効でない 同時に cdk.out/AwsSolutions-ExampleStack-NagReport.csv にも結果レポートが出力されます。違反のあったルールだけではなく、クリアしたルールとサプレスしたルールについても出力されています。 サーバー アクセスログ 以外の3つのエラーに対応してみます。 const myBucket = new s3.Bucket ( this , "MyBucket" , { blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL , encryption: s3.BucketEncryption.S3_MANAGED , enforceSSL: true , } ); 再び cdk synth を実行すると、エラーが1つだけになりました。 [Error at /ExampleStack/MyBucket/Resource] AwsSolutions-S1: The S3 Bucket has server access logs disabled. The bucket should have server access logging enabled to provide detailed records for the requests that are made to the bucket. 今回、この バケット ではサーバー アクセスログ を取得しないものとして、このルールをサプレスしようと思います。 cdk-nagのサプレス方法 なぜサプレスが重要なのか スキャン結果を必要に応じて簡単にサプレスできることは、意味のあるCIをするために非常に重要です。 スキャンルールはあくまでもベストプ ラク ティス集であり、その全てをクリアできないケース(あるいはする必要がないケース)が多いでしょう。不要なルールがサプレスされずに結果に残り続けると、確認する際のノイズになり、差分も分かりにくくなります。CIに組み込んでいる場合、ノイズが多いとそのうち結果を確認しなくなります。 また、なるべく細かい粒度(リソースレベルかそれ以下)でサプレスすることも重要です。プロジェクト全体で絶対に対応しない方針としたものであれば良いのですが、不用意にグローバルでルールをサプレスしてしまうと、リソースを追加した際に気にかけておくべきベストプ ラク ティスを見逃すことになります。 基本的なサプレス サプレスは id にルールIDを渡し、次のように行います。 import { NagSuppressions } from "cdk-nag" ; ... NagSuppressions.addResourceSuppressions ( myBucket , [ { id: "AwsSolutions-S1" , reason: "Bucket storing logs. No need to export logs itself" } , ] ); cdk synth を実行するとエラーがなくなり、CloudFormationテンプレートが出力されました。 cdk-nagでは reason の記載が必須であり、さらには文字数が10文字未満だと次のようなエラーになります。将来読んでも理由がよくわかるように正確に書きましょう。 Error: MyBucket: Error(s) detected in suppression with 'id' AwsSolutions-S1. The suppression must have a 'reason' of 10 characters or more. サプレスのために記載した reason は、出力したCloudFormationテンプレートの Metadata に書き込まれます。 CloudFormationテンプレートに入力されたマルチバイト文字は文字化けしてしまい、 cdk diff として間違って検出されてしまうため、今のところ reason 欄は英語で記載するのが良さそうです。 (2022/9/2追記)cdk-nagに送った PR がマージされ、v2.18.0からは reason に日本語を記載しても問題にならなくなりました! スタック全体で特定のルールをサプレス 特定のルールをスタック全体でサプレスしたい場合は、次のように書けますが、あまり濫用しない方が良いでしょう。 NagSuppressions.addStackSuppressions ( this , [ { id: "AwsSolutions-S1" , reason: "Enabling server access logs is not requiered" } , ] ); より細かい粒度のサプレス S3 バケット へのアクセスを許可するIAMポリシーを作ってみます。 import * as iam from "aws-cdk-lib/aws-iam" ; ... const myIamPolicy = new iam.ManagedPolicy ( this , "MyIAMPolicy" , { statements: [ new iam.PolicyStatement ( { resources: [ myBucket.bucketArn , ` ${ myBucket.bucketArn } /*` ] , actions: [ "s3:Get*" , "s3:ListBucket" ] , effect: iam.Effect.ALLOW , } ), ] , } ); エラーが2つ出ました。 [Error at /ExampleStack/MyIAMPolicy/Resource] AwsSolutions-IAM5[Action::s3:Get*]: The IAM entity contains wildcard permissions and does not have a cdk-nag rule suppression with evidence for those permission. Metadata explaining the evidence (e.g. via supporting links) for wildcard permissions allows for transparency to operators. This is a granular rule that returns individual findings that can be suppressed with 'appliesTo'. The findings are in the format 'Action::<action>' for policy actions and 'Resource::<resource>' for resources. Example: appliesTo: ['Action::s3:*']. [Error at /ExampleStack/MyIAMPolicy/Resource] AwsSolutions-IAM5[Resource::<MyBucketF68F3FF0.Arn>/*]: The IAM entity contains wildcard permissions and does not have a cdk-nag rule suppression with evidence for those permission. Metadata explaining the evidence (e.g. via supporting links) for wildcard permissions allows for transparency to operators. This is a granular rule that returns individual findings that can be suppressed with 'appliesTo'. The findings are in the format 'Action::<action>' for policy actions and 'Resource::<resource>' for resources. Example: appliesTo: ['Action::s3:*']. IAMポリシーに ワイルドカード ( * )が使われており、広いアクセス権限を付与していることに対するエラーです。ActionとResourceの両方で ワイルドカード を使っているため、両方について指摘されています。今回は問題ないこととしてサプレスするとします。 このIAMポリシー全体に対してルール AwsSolutions-IAM5 をサプレスすることも可能ですが、そうすると将来的に追加するあらゆる ワイルドカード も許可してしまいます。そうではなく、特定のActionやResourceに対してのみ ワイルドカード を許可する方が良いでしょう。 エラーをよく見ると、今回はルールIDの後に括弧がついており( [Action::s3:Get*] [Resource::<MyBucketF68F3FF0.Arn>/*] )、問題となった具体的なActionやResourceがわかるようになっています。 appliesTo を使うことで、特定の記述のみをサプレスできます。 NagSuppressions.addResourceSuppressions ( myIamPolicy , [ { id: "AwsSolutions-IAM5" , reason: "Necessary to grant Get access to all objects in the bucket" , appliesTo: [ "Action::s3:Get*" , "Resource::<MyBucketF68F3FF0.Arn>/*" ] , } , ] ); Resourceは前に作成したS3 バケット への参照となっていますが、ルールIDの後の括弧にある Resource::<MyBucketF68F3FF0.Arn>/* をそのまま appliesTo に追加して構いません。これでサプレスされました。 ちなみに、 appliesTo の中に 正規表現 を書くこともできます。 NagSuppressions.addResourceSuppressions ( myIamPolicy , [ { id: "AwsSolutions-IAM5" , reason: "Necessary to grant Get access to all objects in the bucket" , appliesTo: [ "Action::s3:Get*" , { regex: "/^Resource::<MyBucketF68F3FF0.Arn>(.*)$/g" , } , ] , } , ] ); CDKが作成するリソースのサプレス 今度はLambda関数を作ります。 import { Runtime } from "aws-cdk-lib/aws-lambda" ; import * as lambdaNodejs from "aws-cdk-lib/aws-lambda-nodejs" ; import * as logs from "aws-cdk-lib/aws-logs" ; ... const myTrivialFunction = new lambdaNodejs.NodejsFunction ( this , "MyTrivialFunction" , { entry: "aws-cdk/functions/my-trivial-function.ts" , runtime: Runtime.NODEJS_16_X , logRetention: logs.RetentionDays.SIX_MONTHS , bundling: { forceDockerBundling: false } , } ); 3つのエラーが出ました。まずは1つ目です。 [Error at /ExampleStack/MyTrivialFunction/ServiceRole/Resource] AwsSolutions-IAM4[Policy::arn:<AWS::Partition>:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole]: The IAM user, role, or group uses AWS managed policies. An AWS managed policy is a standalone policy that is created and administered by AWS. Currently, many AWS managed policies do not restrict resource scope. Replace AWS managed policies with system specific (customer) managed policies.This is a granular rule that returns individual findings that can be suppressed with 'appliesTo'. The findings are in the format 'Policy::<policy>' for AWS managed policies. Example: appliesTo: ['Policy::arn:<AWS::Partition>:iam::aws:policy/foo']. このLambda関数が明示的に実行ロールを指定していないため、実行ロールが自動で作成されており、 AWSLambdaBasicExecutionRole ポリシーが付与されています。 AWS マネージドポリシーはリソースレベルで権限を絞っていないため、独自のポリシーを使うべきという指摘です。今回は問題ないとし、サプレスします。 しかし今までに述べた方法ではうまくいきません。エラーになっているリソースは関数そのものではなく、関数が使用しているロールだからです。この場合、 addResourceSuppressions の3つ目の引数の applyToChildren に true を渡すことでサプレスできます。(合わせ技で appliesTo には具体的なポリシーを指定しています) NagSuppressions.addResourceSuppressions ( myTrivialFunction , [ { id: "AwsSolutions-IAM4" , reason: "OK to use AWS managed AWSLambdaBasicExecutionRole" , appliesTo: [ "Policy::arn:<AWS::Partition>:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" ] , } , ] , true ); 2つ目、3つ目のエラーは次の通りです。 [Error at /ExampleStack/LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8a/ServiceRole/Resource] AwsSolutions-IAM4[Policy::arn:<AWS::Partition>:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole]: The IAM user, role, or group uses AWS managed policies. An AWS managed policy is a standalone policy that is created and administered by AWS. Currently, many AWS managed policies do not restrict resource scope. Replace AWS managed policies with system specific (customer) managed policies.This is a granular rule that returns individual findings that can be suppressed with 'appliesTo'. The findings are in the format 'Policy::<policy>' for AWS managed policies. Example: appliesTo: ['Policy::arn:<AWS::Partition>:iam::aws:policy/foo']. [Error at /ExampleStack/LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8a/ServiceRole/DefaultPolicy/Resource] AwsSolutions-IAM5[Resource::*]: The IAM entity contains wildcard permissions and does not have a cdk-nag rule suppression with evidence for those permission. Metadata explaining the evidence (e.g. via supporting links) for wildcard permissions allows for transparency to operators. This is a granular rule that returns individual findings that can be suppressed with 'appliesTo'. The findings are in the format 'Action::<action>' for policy actions and 'Resource::<resource>' for resources. Example: appliesTo: ['Action::s3:*']. リソースパスが長くランダム風の文字列になっており、CDKが自動で作成したリソースであることがわかります。Lambda関数を作る時に logRetention を指定した場合、ログリテンションポリシーを設定するためのLambda関数が1つ作られ、その実行ロールに対する指摘です。 明示的にCDKに記述したLambda関 数の子 リソースではないため、 applyToChildren を true にしてもサプレスできません。エラーメッセージに出力されているリソースパスを使い、 addResourceSuppressionsByPath でスタックからこのリソースを見つけてもらいます。 NagSuppressions.addResourceSuppressionsByPath ( this , "/ExampleStack/LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8a/ServiceRole/Resource" , [ { id: "AwsSolutions-IAM4" , reason: "CDK managed resource" , appliesTo: [ "Policy::arn:<AWS::Partition>:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" ] , } , ] ); NagSuppressions.addResourceSuppressionsByPath ( this , "/ExampleStack/LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8a/ServiceRole/DefaultPolicy/Resource" , [ { id: "AwsSolutions-IAM5" , reason: "CDK managed resource" , appliesTo: [ "Resource::*" ] , } , ] ); ちなみにCDKでカスタムリソースを作成する場合もLambda関数が作られますが、そのサプレスにもこの方法を利用できます。 interfaceEndpoints作成時のWarningのサプレス VPC とセキュリティグループ、 VPC エンドポイントを作ります。 import * as ec2 from "aws-cdk-lib/aws-ec2" ; ... const vpc = new ec2.Vpc ( this , "VPC" , { flowLogs: { s3: { destination: ec2.FlowLogDestination.toS3 ( myBucket , "vpc" ), trafficType: ec2.FlowLogTrafficType.ALL , } , } , } ); const appSecurityGroup = new ec2.SecurityGroup ( this , "appSecurityGroup" , { vpc: vpc , allowAllOutbound: true , } ); const httpsSecurityGroup = new ec2.SecurityGroup ( this , "httpsSecurityGroup" , { vpc: vpc , allowAllOutbound: true , } ); httpsSecurityGroup.addIngressRule ( appSecurityGroup , ec2.Port.tcp ( 443 )); vpc.addInterfaceEndpoint ( "SSMEndpoint" , { service: ec2.InterfaceVpcEndpointAwsService.SSM , subnets: { subnets: vpc.publicSubnets } , securityGroups: [ httpsSecurityGroup ] , privateDnsEnabled: true , } ); Cloudformationテンプレートは問題なく生成されますが、実はチェック失敗のWarningが出力されています。 [Warning at /ExampleStack/httpsSecurityGroup/Resource] CdkNagValidationFailure: 'AwsSolutions-EC23' threw an error during validation. This is generally caused by a parameter referencing an intrinsic function. For more details enable verbose logging.' The parameter resolved to to a non-primitive value "{"Fn::GetAtt":["VPCB9E5F0B4","CidrBlock"]}", therefore the rule could not be validated. こちらのIssue に記載がある通り、想定通りの挙動でサプレスして良いとのことなので、次のように id に CdkNagValidationFailure を渡すことでサプレスできます。 NagSuppressions.addResourceSuppressions ( httpsSecurityGroup , [ { id: "CdkNagValidationFailure" , reason: "https://github.com/cdklabs/cdk-nag/issues/817" } , ] ); サプレスはどこに書くべきか プロジェクトの対応方針によっては大量にサプレスが発生する場合があります。cdk-nagはCDKコード内にサプレスを記載するため、どこに書くべきかについて考えてみます。2つのパターンが考えられます。 エラーが発生したリソース付近に書く サプレスのみをまとめて書く 「1. エラーが発生したリソース付近に書く」場合、どのリソースに対するサプレスかがわかりやすい一方で、インフラを定義するコードとセキュリティスキャンのサ プレスコ ードが入り混じるため、見通しの悪いコードになってしまう印象があります。IaCコードを読む時は、サプレス済みのセキュリティスキャンルールは意識の外に置きたいです。 そこで個人的には「2. サプレスのみをまとめて書く」のが良いのではないかと思います。スタックの最後にサプレスをまとめて記述すれば、コードを読む際の邪魔にはなりません。それでもリソースの数が増えるとどこで何をサプレスしているのか探しにくくなるので、CDKを書く際には1つのスタックにリソースを詰め込みすぎず、適度な長さに分割するのが良いでしょう。 まとめ さまざまなケースを取り上げてサプレスする方法を説明しました。ハードルは下がったと思うので、ぜひcdk-nagを導入してセキュリティチェックをしていきましょう。 既存のCDKコードにcdk-nagを入れる場合、たくさんのエラーが出る可能性があります。まずはコードを全て コメントアウト した上で少しずつコメントを外し、 cdk synth でエラーをステップ・バイ・ステップで対応していくのが良いと思います。 今回は AWS Solutionsルールパックのみでしたが、そのうち他のルールパックについても試してみたいと思います。ここまで読んでいただきありがとうございました。 私たちは同じチームで働いてくれる仲間を大募集しています!たくさんのご応募をお待ちしています。 - セキュリティエンジニア(セキュリティ設計) 執筆: @kou.kinyo2 、レビュー: @kou.kinyo2 ( Shodo で執筆されました )
アバター
こんにちは。X(クロス) イノベーション 本部 ソフトウェアデザインセンター セキュリティグループの耿です。 クラウド インフラをIaC化すると、静的セキュリティスキャンができるようになります。インフラをデプロイする前にセキュリティ上の問題や、ベストプ ラク ティスに沿っていない構成を知ることができるため、ぜひスキャンしておきたいところです。 今までのスキャンツールとしては、以下のようなものがありました。 tfsec OSS Terraformに対応 cfn_nag OSS CloudFormationテンプレートに対応 terrascan OSS Terraform、CloudFormationテンプレート、Azure Resource Managerなどに対応 Snyk IaC 有償(無償プランあり) Terraform、CloudFormationテンプレート、Azure Resource Managerなどに対応 最近、 AWS CDKで使いやすい、 AWS 開発の OSS ツール cdk-nag の存在を知り、非常にテンションが上がっているためブログを書いております! 前半では従来のツールでCDKコードをスキャンするときの問題点とcdk-nagの導入方法、後半では実用する時に欠かせない、cdk-nagエラーのサプレス方法を書いていきます。 TL;DR CDKのセキュリティスキャンはcdk-nagが使いやすい 意味のあるCIにするためには正しいサプレスが重要。細かい粒度でサプレスするべき appliesTo でリソースレベルよりもさらに細かい粒度でサプレスできる CDKが勝手に作成するリソースについては、 applyToChildren を true にするか、 addResourceSuppressionsByPath でサプレスできる サプレスはCDKコードの最後にまとめて書くのが良さそう 既存のCDKに導入する時、 コメントアウト して少しずつ対応すると良い TL;DR cfn_nagでCDKをスキャンするときの問題点 問題点1:CDKコードとの関連が分かりにくい 問題点2:サプレスがイケてない cdk-nagセットアップ スキャンしてみる cdk-nagのサプレス方法 なぜサプレスが重要なのか 基本的なサプレス スタック全体で特定のルールをサプレス より細かい粒度のサプレス CDKが作成するリソースのサプレス interfaceEndpoints作成時のWarningのサプレス サプレスはどこに書くべきか まとめ cfn_nagでCDKをスキャンするときの問題点 cdk-nagの存在を知る前までは、cfn_nagとterrascanを GitHub Actionsのワークフローに組み込んで使っていました。どちらのツールも直接CDKをサポートしていませんが、 cdk synth で生成されるCloudFormationテンプレートに対してスキャンをかけることができます。 例として、cfn_nagのスキャン結果は次のようになります(一部抜粋、編集済) ------------------------------------------------------------ cdk.out/MyStack.template.json ------------------------------------------------------------ | WARN W89 | | Resource: ["AWS679f53fac002430cb0da5b7982bd22872D164C4C", "SomeTrivialFunction4CC789DF"] | Line Numbers: [647, 2395] | | Lambda functions should be deployed inside a VPC これには次のような問題があることがわかりました。 問題点1:CDKコードとの関連が分かりにくい スキャン結果では行番号を出力してくれていますが、CloudFormationテンプレート内の行番号なので、CDKで探す際の参考にはなりません。 またリソースの論理IDも出力されますが、こちらもCloudFormationテンプレートに変換した後のIDなので、CDKで記述したIDとは少し異なります。明示的にCDKで定義したリソースならば、IDの先頭の文字列を見ればどのリソースかなんとなく分かりますが、CDKが自動で作成するリソースについてはIDが勝手に割り振られるため、何によって作成されたリソースかは判断つきません。 問題点2:サプレスがイケてない こちらが一番重要です。cfn_nagでは特定のリソースに対して特定のルールをサプレスしたい場合、CloudFromationテンプレートのMetadataに記載することになります。 CDKからMetadataを追加する場合、一度 CloudFormationリソースとして取得する 必要があり、コードが煩雑になります。(CDKが自動で作成するリソースのルールをサプレスする方法は、まだ試していませんがさらに大変そうです) const cfnFunction = someTrivialFunction.node.defaultChild as lambda.CfnFunction ; cfnFunction.cfnOptions.metadata = { cfn_nag: { rules_to_suppress: [{ id: "W89" , reason: "No need to deploy this function inside VPC" }] } , } ; cdk-nagセットアップ ここからはcdk-nagを使っていきます。 AWSのブログ や cdk-nagのREADME に従ってセットアップします。 cdk-nagパッケージをインストールします。 npm install -D cdk-nag v2.15.32時点でルールパックは5種類ありますが、今回は AWS Solutions を利用します。またエラー詳細を出力するように verbose: true を指定します。 import * as cdk from "aws-cdk-lib" ; import { AwsSolutionsChecks } from "cdk-nag" ; import { ExampleStack } from "../lib/example-stack" ; const app = new cdk.App (); new ExampleStack ( app , "ExampleStack" , {} ); cdk.Aspects. of( app ) .add (new AwsSolutionsChecks ( { verbose: true } )); cfn_nagと異なり、cdk-nagはCDKと直接統合されており、CDKの Aspects を利用し、Synthesize前のPrepareの段階でコードを実行する仕組みです。サプレスされていないルール違反がある場合はフローの途中でエラーが返るため、 cdk deploy を実行しても後続のデプロイ処理がされません。CI/CDに組み込みやすいです。 スキャンしてみる まずは簡単なS3 バケット を作って、 cdk synth を実行します。 import { Stack , StackProps } from "aws-cdk-lib" ; import * as s3 from "aws-cdk-lib/aws-s3" ; import { Construct } from "constructs" ; export class ExampleStack extends Stack { constructor( scope: Construct , id: string , props?: StackProps ) { super( scope , id , props ); const myBucket = new s3.Bucket ( this , "MyBucket" , {} ); } } するとエラーが4つ出ました。 [Error at /ExampleStack/MyBucket/Resource] AwsSolutions-S1: The S3 Bucket has server access logs disabled. The bucket should have server access logging enabled to provide detailed records for the requests that are made to the bucket. [Error at /ExampleStack/MyBucket/Resource] AwsSolutions-S2: The S3 Bucket does not have public access restricted and blocked. The bucket should have public access restricted and blocked to prevent unauthorized access. [Error at /ExampleStack/MyBucket/Resource] AwsSolutions-S3: The S3 Bucket does not default encryption enabled. The bucket should minimally have SSE enabled to help protect data-at-rest. [Error at /ExampleStack/MyBucket/Resource] AwsSolutions-S10: The S3 Bucket or bucket policy does not require requests to use SSL. You can use HTTPS (TLS) to help prevent potential attackers from eavesdropping on or manipulating network traffic using person-in-the-middle or similar attacks. You should allow only encrypted connections over HTTPS (TLS) using the aws:SecureTransport condition on Amazon S3 bucket policies. 内容は以下のとおりです AwsSolutions-S1:サーバー アクセスログ が有効でない AwsSolutions-S2:ブロックパブリックアクセスが有効でない AwsSolutions-S3:デフォルト暗号化が有効でない AwsSolutions-S10: TLS 通信の強制が有効でない 同時に cdk.out/AwsSolutions-ExampleStack-NagReport.csv にも結果レポートが出力されます。違反のあったルールだけではなく、クリアしたルールとサプレスしたルールについても出力されています。 サーバー アクセスログ 以外の3つのエラーに対応してみます。 const myBucket = new s3.Bucket ( this , "MyBucket" , { blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL , encryption: s3.BucketEncryption.S3_MANAGED , enforceSSL: true , } ); 再び cdk synth を実行すると、エラーが1つだけになりました。 [Error at /ExampleStack/MyBucket/Resource] AwsSolutions-S1: The S3 Bucket has server access logs disabled. The bucket should have server access logging enabled to provide detailed records for the requests that are made to the bucket. 今回、この バケット ではサーバー アクセスログ を取得しないものとして、このルールをサプレスしようと思います。 cdk-nagのサプレス方法 なぜサプレスが重要なのか スキャン結果を必要に応じて簡単にサプレスできることは、意味のあるCIをするために非常に重要です。 スキャンルールはあくまでもベストプ ラク ティス集であり、その全てをクリアできないケース(あるいはする必要がないケース)が多いでしょう。不要なルールがサプレスされずに結果に残り続けると、確認する際のノイズになり、差分も分かりにくくなります。CIに組み込んでいる場合、ノイズが多いとそのうち結果を確認しなくなります。 また、なるべく細かい粒度(リソースレベルかそれ以下)でサプレスすることも重要です。プロジェクト全体で絶対に対応しない方針としたものであれば良いのですが、不用意にグローバルでルールをサプレスしてしまうと、リソースを追加した際に気にかけておくべきベストプ ラク ティスを見逃すことになります。 基本的なサプレス サプレスは id にルールIDを渡し、次のように行います。 import { NagSuppressions } from "cdk-nag" ; ... NagSuppressions.addResourceSuppressions ( myBucket , [ { id: "AwsSolutions-S1" , reason: "Bucket storing logs. No need to export logs itself" } , ] ); cdk synth を実行するとエラーがなくなり、CloudFormationテンプレートが出力されました。 cdk-nagでは reason の記載が必須であり、さらには文字数が10文字未満だと次のようなエラーになります。将来読んでも理由がよくわかるように正確に書きましょう。 Error: MyBucket: Error(s) detected in suppression with 'id' AwsSolutions-S1. The suppression must have a 'reason' of 10 characters or more. サプレスのために記載した reason は、出力したCloudFormationテンプレートの Metadata に書き込まれます。 CloudFormationテンプレートに入力されたマルチバイト文字は文字化けしてしまい、 cdk diff として間違って検出されてしまうため、今のところ reason 欄は英語で記載するのが良さそうです。 (2022/9/2追記)cdk-nagに送った PR がマージされ、v2.18.0からは reason に日本語を記載しても問題にならなくなりました! スタック全体で特定のルールをサプレス 特定のルールをスタック全体でサプレスしたい場合は、次のように書けますが、あまり濫用しない方が良いでしょう。 NagSuppressions.addStackSuppressions ( this , [ { id: "AwsSolutions-S1" , reason: "Enabling server access logs is not requiered" } , ] ); より細かい粒度のサプレス S3 バケット へのアクセスを許可するIAMポリシーを作ってみます。 import * as iam from "aws-cdk-lib/aws-iam" ; ... const myIamPolicy = new iam.ManagedPolicy ( this , "MyIAMPolicy" , { statements: [ new iam.PolicyStatement ( { resources: [ myBucket.bucketArn , ` ${ myBucket.bucketArn } /*` ] , actions: [ "s3:Get*" , "s3:ListBucket" ] , effect: iam.Effect.ALLOW , } ), ] , } ); エラーが2つ出ました。 [Error at /ExampleStack/MyIAMPolicy/Resource] AwsSolutions-IAM5[Action::s3:Get*]: The IAM entity contains wildcard permissions and does not have a cdk-nag rule suppression with evidence for those permission. Metadata explaining the evidence (e.g. via supporting links) for wildcard permissions allows for transparency to operators. This is a granular rule that returns individual findings that can be suppressed with 'appliesTo'. The findings are in the format 'Action::<action>' for policy actions and 'Resource::<resource>' for resources. Example: appliesTo: ['Action::s3:*']. [Error at /ExampleStack/MyIAMPolicy/Resource] AwsSolutions-IAM5[Resource::<MyBucketF68F3FF0.Arn>/*]: The IAM entity contains wildcard permissions and does not have a cdk-nag rule suppression with evidence for those permission. Metadata explaining the evidence (e.g. via supporting links) for wildcard permissions allows for transparency to operators. This is a granular rule that returns individual findings that can be suppressed with 'appliesTo'. The findings are in the format 'Action::<action>' for policy actions and 'Resource::<resource>' for resources. Example: appliesTo: ['Action::s3:*']. IAMポリシーに ワイルドカード ( * )が使われており、広いアクセス権限を付与していることに対するエラーです。ActionとResourceの両方で ワイルドカード を使っているため、両方について指摘されています。今回は問題ないこととしてサプレスするとします。 このIAMポリシー全体に対してルール AwsSolutions-IAM5 をサプレスすることも可能ですが、そうすると将来的に追加するあらゆる ワイルドカード も許可してしまいます。そうではなく、特定のActionやResourceに対してのみ ワイルドカード を許可する方が良いでしょう。 エラーをよく見ると、今回はルールIDの後に括弧がついており( [Action::s3:Get*] [Resource::<MyBucketF68F3FF0.Arn>/*] )、問題となった具体的なActionやResourceがわかるようになっています。 appliesTo を使うことで、特定の記述のみをサプレスできます。 NagSuppressions.addResourceSuppressions ( myIamPolicy , [ { id: "AwsSolutions-IAM5" , reason: "Necessary to grant Get access to all objects in the bucket" , appliesTo: [ "Action::s3:Get*" , "Resource::<MyBucketF68F3FF0.Arn>/*" ] , } , ] ); Resourceは前に作成したS3 バケット への参照となっていますが、ルールIDの後の括弧にある Resource::<MyBucketF68F3FF0.Arn>/* をそのまま appliesTo に追加して構いません。これでサプレスされました。 ちなみに、 appliesTo の中に 正規表現 を書くこともできます。 NagSuppressions.addResourceSuppressions ( myIamPolicy , [ { id: "AwsSolutions-IAM5" , reason: "Necessary to grant Get access to all objects in the bucket" , appliesTo: [ "Action::s3:Get*" , { regex: "/^Resource::<MyBucketF68F3FF0.Arn>(.*)$/g" , } , ] , } , ] ); CDKが作成するリソースのサプレス 今度はLambda関数を作ります。 import { Runtime } from "aws-cdk-lib/aws-lambda" ; import * as lambdaNodejs from "aws-cdk-lib/aws-lambda-nodejs" ; import * as logs from "aws-cdk-lib/aws-logs" ; ... const myTrivialFunction = new lambdaNodejs.NodejsFunction ( this , "MyTrivialFunction" , { entry: "aws-cdk/functions/my-trivial-function.ts" , runtime: Runtime.NODEJS_16_X , logRetention: logs.RetentionDays.SIX_MONTHS , bundling: { forceDockerBundling: false } , } ); 3つのエラーが出ました。まずは1つ目です。 [Error at /ExampleStack/MyTrivialFunction/ServiceRole/Resource] AwsSolutions-IAM4[Policy::arn:<AWS::Partition>:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole]: The IAM user, role, or group uses AWS managed policies. An AWS managed policy is a standalone policy that is created and administered by AWS. Currently, many AWS managed policies do not restrict resource scope. Replace AWS managed policies with system specific (customer) managed policies.This is a granular rule that returns individual findings that can be suppressed with 'appliesTo'. The findings are in the format 'Policy::<policy>' for AWS managed policies. Example: appliesTo: ['Policy::arn:<AWS::Partition>:iam::aws:policy/foo']. このLambda関数が明示的に実行ロールを指定していないため、実行ロールが自動で作成されており、 AWSLambdaBasicExecutionRole ポリシーが付与されています。 AWS マネージドポリシーはリソースレベルで権限を絞っていないため、独自のポリシーを使うべきという指摘です。今回は問題ないとし、サプレスします。 しかし今までに述べた方法ではうまくいきません。エラーになっているリソースは関数そのものではなく、関数が使用しているロールだからです。この場合、 addResourceSuppressions の3つ目の引数の applyToChildren に true を渡すことでサプレスできます。(合わせ技で appliesTo には具体的なポリシーを指定しています) NagSuppressions.addResourceSuppressions ( myTrivialFunction , [ { id: "AwsSolutions-IAM4" , reason: "OK to use AWS managed AWSLambdaBasicExecutionRole" , appliesTo: [ "Policy::arn:<AWS::Partition>:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" ] , } , ] , true ); 2つ目、3つ目のエラーは次の通りです。 [Error at /ExampleStack/LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8a/ServiceRole/Resource] AwsSolutions-IAM4[Policy::arn:<AWS::Partition>:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole]: The IAM user, role, or group uses AWS managed policies. An AWS managed policy is a standalone policy that is created and administered by AWS. Currently, many AWS managed policies do not restrict resource scope. Replace AWS managed policies with system specific (customer) managed policies.This is a granular rule that returns individual findings that can be suppressed with 'appliesTo'. The findings are in the format 'Policy::<policy>' for AWS managed policies. Example: appliesTo: ['Policy::arn:<AWS::Partition>:iam::aws:policy/foo']. [Error at /ExampleStack/LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8a/ServiceRole/DefaultPolicy/Resource] AwsSolutions-IAM5[Resource::*]: The IAM entity contains wildcard permissions and does not have a cdk-nag rule suppression with evidence for those permission. Metadata explaining the evidence (e.g. via supporting links) for wildcard permissions allows for transparency to operators. This is a granular rule that returns individual findings that can be suppressed with 'appliesTo'. The findings are in the format 'Action::<action>' for policy actions and 'Resource::<resource>' for resources. Example: appliesTo: ['Action::s3:*']. リソースパスが長くランダム風の文字列になっており、CDKが自動で作成したリソースであることがわかります。Lambda関数を作る時に logRetention を指定した場合、ログリテンションポリシーを設定するためのLambda関数が1つ作られ、その実行ロールに対する指摘です。 明示的にCDKに記述したLambda関 数の子 リソースではないため、 applyToChildren を true にしてもサプレスできません。エラーメッセージに出力されているリソースパスを使い、 addResourceSuppressionsByPath でスタックからこのリソースを見つけてもらいます。 NagSuppressions.addResourceSuppressionsByPath ( this , "/ExampleStack/LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8a/ServiceRole/Resource" , [ { id: "AwsSolutions-IAM4" , reason: "CDK managed resource" , appliesTo: [ "Policy::arn:<AWS::Partition>:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" ] , } , ] ); NagSuppressions.addResourceSuppressionsByPath ( this , "/ExampleStack/LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8a/ServiceRole/DefaultPolicy/Resource" , [ { id: "AwsSolutions-IAM5" , reason: "CDK managed resource" , appliesTo: [ "Resource::*" ] , } , ] ); ちなみにCDKでカスタムリソースを作成する場合もLambda関数が作られますが、そのサプレスにもこの方法を利用できます。 interfaceEndpoints作成時のWarningのサプレス VPC とセキュリティグループ、 VPC エンドポイントを作ります。 import * as ec2 from "aws-cdk-lib/aws-ec2" ; ... const vpc = new ec2.Vpc ( this , "VPC" , { flowLogs: { s3: { destination: ec2.FlowLogDestination.toS3 ( myBucket , "vpc" ), trafficType: ec2.FlowLogTrafficType.ALL , } , } , } ); const appSecurityGroup = new ec2.SecurityGroup ( this , "appSecurityGroup" , { vpc: vpc , allowAllOutbound: true , } ); const httpsSecurityGroup = new ec2.SecurityGroup ( this , "httpsSecurityGroup" , { vpc: vpc , allowAllOutbound: true , } ); httpsSecurityGroup.addIngressRule ( appSecurityGroup , ec2.Port.tcp ( 443 )); vpc.addInterfaceEndpoint ( "SSMEndpoint" , { service: ec2.InterfaceVpcEndpointAwsService.SSM , subnets: { subnets: vpc.publicSubnets } , securityGroups: [ httpsSecurityGroup ] , privateDnsEnabled: true , } ); Cloudformationテンプレートは問題なく生成されますが、実はチェック失敗のWarningが出力されています。 [Warning at /ExampleStack/httpsSecurityGroup/Resource] CdkNagValidationFailure: 'AwsSolutions-EC23' threw an error during validation. This is generally caused by a parameter referencing an intrinsic function. For more details enable verbose logging.' The parameter resolved to to a non-primitive value "{"Fn::GetAtt":["VPCB9E5F0B4","CidrBlock"]}", therefore the rule could not be validated. こちらのIssue に記載がある通り、想定通りの挙動でサプレスして良いとのことなので、次のように id に CdkNagValidationFailure を渡すことでサプレスできます。 NagSuppressions.addResourceSuppressions ( httpsSecurityGroup , [ { id: "CdkNagValidationFailure" , reason: "https://github.com/cdklabs/cdk-nag/issues/817" } , ] ); サプレスはどこに書くべきか プロジェクトの対応方針によっては大量にサプレスが発生する場合があります。cdk-nagはCDKコード内にサプレスを記載するため、どこに書くべきかについて考えてみます。2つのパターンが考えられます。 エラーが発生したリソース付近に書く サプレスのみをまとめて書く 「1. エラーが発生したリソース付近に書く」場合、どのリソースに対するサプレスかがわかりやすい一方で、インフラを定義するコードとセキュリティスキャンのサ プレスコ ードが入り混じるため、見通しの悪いコードになってしまう印象があります。IaCコードを読む時は、サプレス済みのセキュリティスキャンルールは意識の外に置きたいです。 そこで個人的には「2. サプレスのみをまとめて書く」のが良いのではないかと思います。スタックの最後にサプレスをまとめて記述すれば、コードを読む際の邪魔にはなりません。それでもリソースの数が増えるとどこで何をサプレスしているのか探しにくくなるので、CDKを書く際には1つのスタックにリソースを詰め込みすぎず、適度な長さに分割するのが良いでしょう。 まとめ さまざまなケースを取り上げてサプレスする方法を説明しました。ハードルは下がったと思うので、ぜひcdk-nagを導入してセキュリティチェックをしていきましょう。 既存のCDKコードにcdk-nagを入れる場合、たくさんのエラーが出る可能性があります。まずはコードを全て コメントアウト した上で少しずつコメントを外し、 cdk synth でエラーをステップ・バイ・ステップで対応していくのが良いと思います。 今回は AWS Solutionsルールパックのみでしたが、そのうち他のルールパックについても試してみたいと思います。ここまで読んでいただきありがとうございました。 私たちは同じチームで働いてくれる仲間を大募集しています!たくさんのご応募をお待ちしています。 - セキュリティエンジニア(セキュリティ設計) 執筆: @kou.kinyo2 、レビュー: @kou.kinyo2 ( Shodo で執筆されました )
アバター
こんにちは、X(クロス) イノベーション 本部 ソフトウェアデザインセンター・セキュリティグループの大西です。現在、DockerとTypeScriptを使ってシステムを開発中です。DockerのDistrolessイメージの中で、ORMの Prisma を使おうとするとエラーが出てハマってしまったので、エラー解消の方法についてお話ししたいと思います。 まずは少し、Distrolessイメージと Prisma について説明します。 Distrolessイメージとは Google が公開している Distrolessイメージ とは、アプリケーションの実行に必要な最小限のファイルのみが入っている超軽量なDockerイメージです。それゆえ、普通のOSに入っているようなパッケージマネージャーやシェルなどは入っていません。最も小さいサイズのもので gcr.io/distroless/static-debian11 はたったの2MiBほどしかなく、 alpine (5Mib)の約半分、 debian (124MiB)の2%のサイズしかありません。メリットとしては、不要なファイルを含まないことで攻撃対象領域(Attack Surface )を最小限に抑えており、不要なバグや 脆弱性 を埋め込みにくいという点が挙げられます。また、イメージが軽いことで リポジトリ の容量を抑えられ、起動時の読み込みが軽いのでスケーリングにかかる時間が減るという利点もあります。 Prisma とは Prisma とは、Node.jsとTypeScriptの環境下で動く オープンソース のORMです。 MySQL 、 PostgreSQL 、 SQLite 、 SQL Server などに対応しており、NextJS、NestJS、GraphQLなど多くの フレームワーク にも対応しています。新しいリリースは約2週間に一度行われ、先日 v4.0.0 がリリースされました。 Prisma でよく使われる機能は以下の2つです。 - Prisma Client : 型安全なデータベー スクライア ントでアプリに合わせたタイプの Prisma スキーマ から自動生成される - Prisma Migrate : カスタマイズ可能な SQL データベース マイグレーション を自動的に生成し、移行ファイルを生成せずにデータベースに変更を加えることができる Prisma とTypeORMの比較 今回、開発を進める中で Prisma とTypeORMどちらも使用してみましたが、 Prisma の方が直感的にDBを操作できるなと感じました。書くコードの量もTypeORMよりも少なくてすみ、使いやすい印象です。個人的に便利だと思ったのは、 スキーマ の自動生成機能です。 npx prisma db pull コマンドを実行するだけで、すでにあるDBから スキーマ を読み出し Prisma 用の スキーマ (schema. prisma )を自動で作成してくれます。最初はTypeORMでモデルを実装していたので、TypeORMから出力されるデータベースの スキーマ がすでに存在していました。そのため、 Prisma が スキーマ を自動生成する機能によってTypeORMから Prisma に変更するときも割と簡単に移行できました。既存のプロジェクトに Prisma を導入するときの手順は こちら にあります。また、 Prisma とTypeORMの比較は こちら です。今回は使用していませんが、TypeORMからの マイグレーション の方法も こちら で紹介されています。 今回ハマったところ まず背景として、DockerのDistrolessイメージを使いたいという開発者の気持ちがありました。その理由は、やはりセキュアなイメージだからです。それゆえ、Alpineイメージで Prisma は動くけれど、頑張ってDistrolessで動かしたいという思いがありました。今回ハマったところは、Distroless上で Prisma を使ってDB操作を実施しようとするとき、 PrismaClientInitializationError: Unable to load Node-API Library from /usr/app/node_modules/.prisma/client/libquery_engine-debian-openssl-1.1.x.so.node, Library may be corrupt というエラーが出て Prisma を使えなくなったところです。ライブラリが壊れているかもしれない、と言われても・・・。このエラーメッセージで検索しても解決策は見つからず、どこに原因要素があるのか調べるためいろいろ試したところ、alpineイメージでは Prisma が動くことが分かりました。 解決策 alpineイメージで動くことは分かったものの、alpineイメージにはあり、Distrolessイメージにはない何かを見つけることができません。そんなとき、同じ部の先輩が、Distrolessコンテナの中に入るためシェルのある distroless-debug イメージに変えることを思いつきました。そして、もう一度 GitHub の Prisma の コード をよく見ると、 Library may be corrupt のエラーメッセージが出ている箇所のエラーがサプレスされていることに気づきました。 この e を見れば何かわかるかもしれないということで、 next start された後のシェルに入ってトランスパイルされたJSを直接書き換えてみましたが、 e の内容は表示されません。つまり、 next start する前に ソースコード を書き換えないといけないので、コンテナを作る時に書き換えるためDockerfile上で sed することを思いつきます。 RUN sed -i -e '41147i console.log(e);' node_modules/@prisma/client/runtime/index.js すると Error: libz.so.1: cannot open shared object file: No such file or directory というエラーが出ていることが分かりました!libzをDistrolessイメージに入れるため、マルチステージングビルドを使うことにしました。以下のように、libzの入っているalpineイメージからDistrolessイメージにlibzをコピーしてみるとエラーが変わり、 Error: libc.musl-x86_64.so.1: cannot open shared object file: No such file or directory となったのでzlibと同様にlibc.musl- x86 _64.so.1というCのライブラリもコピーしました。最終的なDockerfileは以下のようになりました。 # libzの入ったalpineイメージを作成 FROM alpine as lib # 上記のalpineイメージからdistrolessにzlibとmusl clibを提供する FROM gcr.io/distroless/nodejs:16 COPY --from=lib /lib/libz.so.1 /lib/libz.so.1 COPY --from=lib /lib/libc.musl-x86_64.so.1 /lib/libc.musl-x86_64.so.1 まとめ セキュアで軽量なイメージだからこそ、必要なものは自分で入れていかないといけないDistrolessイメージ。その必要なものがなかなか分からず、今回は時間がかかってしまいました。エラーに遭遇したとき、とりあえずそのエラーを検索することはしますが、それでも方法が見つからないこともあります。そんな時はソースが公開されていればソースを眺めて、ここにはどんな値が入っているのだろう、ここはどんなエラー文が出ているのだろうと、自分でソースを開拓していくことも技の一つだと知りました。先輩に感謝です! 私たちは同じチームで働いてくれる仲間を大募集しています!たくさんのご応募をお待ちしています。 - セキュリティエンジニア(セキュリティ設計) 執筆: @onishi.mayu 、レビュー: @handa.kenta ( Shodo で執筆されました )
アバター
こんにちは、X(クロス) イノベーション 本部 ソフトウェアデザインセンター・セキュリティグループの大西です。現在、DockerとTypeScriptを使ってシステムを開発中です。DockerのDistrolessイメージの中で、ORMの Prisma を使おうとするとエラーが出てハマってしまったので、エラー解消の方法についてお話ししたいと思います。 まずは少し、Distrolessイメージと Prisma について説明します。 Distrolessイメージとは Google が公開している Distrolessイメージ とは、アプリケーションの実行に必要な最小限のファイルのみが入っている超軽量なDockerイメージです。それゆえ、普通のOSに入っているようなパッケージマネージャーやシェルなどは入っていません。最も小さいサイズのもので gcr.io/distroless/static-debian11 はたったの2MiBほどしかなく、 alpine (5Mib)の約半分、 debian (124MiB)の2%のサイズしかありません。メリットとしては、不要なファイルを含まないことで攻撃対象領域(Attack Surface )を最小限に抑えており、不要なバグや 脆弱性 を埋め込みにくいという点が挙げられます。また、イメージが軽いことで リポジトリ の容量を抑えられ、起動時の読み込みが軽いのでスケーリングにかかる時間が減るという利点もあります。 Prisma とは Prisma とは、Node.jsとTypeScriptの環境下で動く オープンソース のORMです。 MySQL 、 PostgreSQL 、 SQLite 、 SQL Server などに対応しており、NextJS、NestJS、GraphQLなど多くの フレームワーク にも対応しています。新しいリリースは約2週間に一度行われ、先日 v4.0.0 がリリースされました。 Prisma でよく使われる機能は以下の2つです。 - Prisma Client : 型安全なデータベー スクライア ントでアプリに合わせたタイプの Prisma スキーマ から自動生成される - Prisma Migrate : カスタマイズ可能な SQL データベース マイグレーション を自動的に生成し、移行ファイルを生成せずにデータベースに変更を加えることができる Prisma とTypeORMの比較 今回、開発を進める中で Prisma とTypeORMどちらも使用してみましたが、 Prisma の方が直感的にDBを操作できるなと感じました。書くコードの量もTypeORMよりも少なくてすみ、使いやすい印象です。個人的に便利だと思ったのは、 スキーマ の自動生成機能です。 npx prisma db pull コマンドを実行するだけで、すでにあるDBから スキーマ を読み出し Prisma 用の スキーマ (schema. prisma )を自動で作成してくれます。最初はTypeORMでモデルを実装していたので、TypeORMから出力されるデータベースの スキーマ がすでに存在していました。そのため、 Prisma が スキーマ を自動生成する機能によってTypeORMから Prisma に変更するときも割と簡単に移行できました。既存のプロジェクトに Prisma を導入するときの手順は こちら にあります。また、 Prisma とTypeORMの比較は こちら です。今回は使用していませんが、TypeORMからの マイグレーション の方法も こちら で紹介されています。 今回ハマったところ まず背景として、DockerのDistrolessイメージを使いたいという開発者の気持ちがありました。その理由は、やはりセキュアなイメージだからです。それゆえ、Alpineイメージで Prisma は動くけれど、頑張ってDistrolessで動かしたいという思いがありました。今回ハマったところは、Distroless上で Prisma を使ってDB操作を実施しようとするとき、 PrismaClientInitializationError: Unable to load Node-API Library from /usr/app/node_modules/.prisma/client/libquery_engine-debian-openssl-1.1.x.so.node, Library may be corrupt というエラーが出て Prisma を使えなくなったところです。ライブラリが壊れているかもしれない、と言われても・・・。このエラーメッセージで検索しても解決策は見つからず、どこに原因要素があるのか調べるためいろいろ試したところ、alpineイメージでは Prisma が動くことが分かりました。 解決策 alpineイメージで動くことは分かったものの、alpineイメージにはあり、Distrolessイメージにはない何かを見つけることができません。そんなとき、同じ部の先輩が、Distrolessコンテナの中に入るためシェルのある distroless-debug イメージに変えることを思いつきました。そして、もう一度 GitHub の Prisma の コード をよく見ると、 Library may be corrupt のエラーメッセージが出ている箇所のエラーがサプレスされていることに気づきました。 この e を見れば何かわかるかもしれないということで、 next start された後のシェルに入ってトランスパイルされたJSを直接書き換えてみましたが、 e の内容は表示されません。つまり、 next start する前に ソースコード を書き換えないといけないので、コンテナを作る時に書き換えるためDockerfile上で sed することを思いつきます。 RUN sed -i -e '41147i console.log(e);' node_modules/@prisma/client/runtime/index.js すると Error: libz.so.1: cannot open shared object file: No such file or directory というエラーが出ていることが分かりました!libzをDistrolessイメージに入れるため、マルチステージングビルドを使うことにしました。以下のように、libzの入っているalpineイメージからDistrolessイメージにlibzをコピーしてみるとエラーが変わり、 Error: libc.musl-x86_64.so.1: cannot open shared object file: No such file or directory となったのでzlibと同様にlibc.musl- x86 _64.so.1というCのライブラリもコピーしました。最終的なDockerfileは以下のようになりました。 # libzの入ったalpineイメージを作成 FROM alpine as lib # 上記のalpineイメージからdistrolessにzlibとmusl clibを提供する FROM gcr.io/distroless/nodejs:16 COPY --from=lib /lib/libz.so.1 /lib/libz.so.1 COPY --from=lib /lib/libc.musl-x86_64.so.1 /lib/libc.musl-x86_64.so.1 まとめ セキュアで軽量なイメージだからこそ、必要なものは自分で入れていかないといけないDistrolessイメージ。その必要なものがなかなか分からず、今回は時間がかかってしまいました。エラーに遭遇したとき、とりあえずそのエラーを検索することはしますが、それでも方法が見つからないこともあります。そんな時はソースが公開されていればソースを眺めて、ここにはどんな値が入っているのだろう、ここはどんなエラー文が出ているのだろうと、自分でソースを開拓していくことも技の一つだと知りました。先輩に感謝です! 私たちは同じチームで働いてくれる仲間を大募集しています!たくさんのご応募をお待ちしています。 - セキュリティエンジニア(セキュリティ設計) 執筆: @onishi.mayu 、レビュー: @handa.kenta ( Shodo で執筆されました )
アバター
X(クロス) イノベーション 本部 クラウド イノベーション センターの田村です。 普段の業務では、 Microsoft Azure のアップデートや新サービスの調査検証をしており、その一環として Azure Managed Grafana の調査を実施しました。 本記事では、Azure Managed Grafana の概要と機能について、調査結果を絡めてご紹介します。 Azure Managed Grafana とは 可視化機能の検証 Azure Managed Grafana のリソース作成 データソースの登録 ダッシュボードの作成 サポートしているデータソース 他の Grafana サービスとの違い Power BI との違い まとめ Azure Managed Grafana とは Azure Managed Grafana は、 Microsoft と Grafana Lab 社が開発したフルマネージドの可視化サービスです。 Grafana は Grafana Lab 社が提供するサービスでしたが、このたび Microsoft とパートナーシップを結び、Azure のマネージドサービスとして公開されました。 もともと Grafana は オープンソース の可視化サービスとして知られており、Kibana や Metabase といったサービスとよく比較されている印象があります。 Azure Managed Grafana の特徴としては、下記の 4 点が紹介されています。 Azure に最適化 Azure Monitor や Azure Data Explorer などの Azure サービスの可視化を行うために最適化 ダッシュ ボード共有 複数人で Grafana ダッシュ ボードを共有する際に、管理者/編集者/閲覧者ロールの設定が可能 ユーザーロールとロール管理は後述する Azure AD と統合されている ID 管理 Azure AD( Active Directory )による ID 管理とユーザーロール管理の統合 マネージド ID による Azure サービスへのアクセス 簡単な操作 Azure Portal 上からワンクリックで Grafana ダッシュ ボードへ移動 グラフのテンプレート等を活用することで直感的な ダッシュ ボード作成が可能 詳細は下記のリンクをご参照ください。 https://azure.microsoft.com/ja-jp/services/managed-grafana/ 可視化機能の検証 実際に Azure Managed Grafana で ダッシュ ボードを作成し、可視化機能を検証してみました。 今回は可視化対象のデータソースとして、Azure の監視サービスである Azure Monitor を採用しました。 作業の流れは大まかに下記の通りとなります。 Azure Managed Grafana のリソース作成 データソースの登録 ダッシュ ボードの作成 Azure Managed Grafana のリソース作成 Azure Portal と Azure CLI から作成できますが、今回は GUI の Azure Portal から作成しました。 作成時に設定を変更しない限り、デフォルトで管理者ロールの割り当てやマネージド ID の作成が実行されるので、権限周りの設定も簡単です。 クイック スタート: Azure portal を使用して Azure Managed Grafana プレビュー インスタンスを作成する 作成が完了すると、リソース概要のエンドポイント項目に Grafana へのリンク先が表示されます。 こちらの URL を選択することで Grafana へアクセスでき、 ダッシュ ボード作成が可能になります。 データソースの登録 今回可視化する Azure Monitor を登録します。 Grafana にアクセスし、左側のメニューから Configuration > Data sources と選択すると、データソースの設定画面に遷移します。 Azure Monitor がデフォルトで表示されているので選択します。 他のデータソースは Add data source から追加できます。 Authentication にて、認証方式と サブスクリプション を設定します。 認証方式はマネージド ID(Maneged Identity)またはサービス プリンシパル (App Registration)を選択できますが、今回はマネージド ID による認証を設定しました。 Save & test を実行し、成功すればデータソースの登録は完了です。 ダッシュ ボードの作成 データソースの登録が完了したので、Azure Monitor で監視しているデータを可視化する ダッシュ ボードを作成します。 作成方法は大きく 2 種類あり、1 から自分で作成するパターンと、テンプレートを利用するパターンがあります。 まずは 1 から作成してみます。 左側のメニューから Create > Dashboard > Add panel と選択すると、 ダッシュ ボードの編集画面に遷移します。 デフォルトで表示されているグラフに意味はありません。 データソースに先ほど登録した Azure Monitor を設定し、グラフを作成してみます。 今回は 仮想マシン の CPU 使用率を可視化してみました。 作業は単純で、 サブスクリプション > リソースグループ > リソース > 可視化するメトリクス(Percentage CPU)の順に選択するだけです。 今回は CPU 使用率を例にしましたが、Azure Monitor をデータソースにすると、各リソースのさまざまなメトリクスを可視化できます。 仮想マシン だけでも、メモリの 稼働率 やディスクの使用率、インバウンド/アウトバウンドフローなどに対応しています。 Azure Monitor によって監視される内容 このようにしてどんどん ダッシュ ボードを作りこんでいくのですが、慣れない人ではどうしても手間がかかります。 そこで、用意されているテンプレートによる ダッシュ ボード作成も試してみました。 左側のメニューから Dashboard > Browse > Azure Monitor と選択すると、 ダッシュ ボードのテンプレートが一覧で表示されます。 下記の 9 種類がありますが、今回はストレージアカウントの可視化テンプレートを選択しました。 Azure Monitor アラート アプリケーションに関する インサイト Azure Cosmos DB Azure Data Explorer クラスタ Azure Key Vault ストレージアカウント (今回選択) リソースグループ内の 仮想マシン Log Analytics ワークスペース に紐づいた 仮想マシン 各リソース数 ストレージアカウントのテンプレートを選択すると、構成済みの ダッシュ ボードが表示されます。 上記画像では表示しきれていませんが、ストレージアカウントのテンプレートは 20 以上のパネル(グラフやカード)で構成されています。 これにより、リソースに関するほぼすべてのメトリクスを 1 枚の ダッシュ ボードで確認できます。 ここから編集を加えたり、新たなテンプレートを追加することも可能なので、ユーザーにとって非常に便利な機能です。 サポートしているデータソース Azure Managed Grafana では、Grafana Enterprise でサポートされているサービスをデータソースとして設定できます。 デフォルトで使用可能なデータソースは 17 種類あり、 クラウド サービスや各種 DB サービスに対応しています。 Alertmanager AWS CloudWatch Azure Monitor Elasticsearch Google Cloud Monitoring Graphite InfluxDB Loki Microsoft SQL Server ( MSSQL ) MySQL OpenTSDB PostgreSQL Prometheus Jaeger Zipkin Tempo Testdata 上記以外にも、 プラグイン を設定することでさまざまなデータソースの可視化が可能です。 プラグイン も含めると、サポートされているデータソースは 132 種類になります。 https://grafana.com/docs/grafana/latest/datasources/ https://grafana.com/grafana/plugins/?type=datasource&utm_source=grafana_add_ds 他の Grafana サービスとの違い 今回調査した Azure Managed Grafana の他に、Grafana には OSS /Grafana Cloud *1 /Grafana Enterprise といった提供形態があります。 料金やサポートといった点の差異を調査し、下記の表にまとめました。[^4] Azure Managed Grafana Grafana( OSS ) Grafana Cloud Grafana Enterprise データソース 132 132 132 132 料金 従量課金 *2 無料 従量課金 *3 要問い合わせ セキュリティ Azure による保護 なし 専用ツール内包 専用ツール内包 サポート Azure サポート なし チケット制 24 時間 365 日 Power BI との違い Microsoft の代表的な可視化サービスとして Power BI が挙げられます。 Azure における可視化という点で比較すると、Azure Managed Grafana と Power BI の最大の違いは、サポートしている Azure サービスの種類です。 Azure Managed Grafana で可視化できる Azure サービスは、 プラグイン 設定込みでも 3 つ(Azure Monitor/Azure Data Explorer /Azure DevOps)のみです。 Azure Monitor など、Azure Managed Grafana がサポートしているデータソースで取得できるデータは、ログやメトリクスといった監視情報が多くを占めています。 Grafana ダッシュ ボード上ではアラートの設定が可能なため、Azure PaaS を可視化のデータソースとした場合は取得した監視情報の可視化に特化している印象です。 一方で、Power BI は Azure SQL Database や Azure Synapse Analytics をはじめ、Azure のデータサービスをほぼ網羅しています。 そのため、Azure 上に構築したデータウェアハウスやデータマートに蓄積したデータを可視化したい、というシナリオには Power BI の方が適しているといえます。 Power BI データ ソース まとめ 本記事では、Azure Managed Grafana の概要と機能について、筆者の調査結果をもとにご紹介しました。 Azure Managed Grafana は 4 月にパブリックプレビューが開始されたばかりのサービスのため、今後もアップデートに注目したいと思います。 X(クロス) イノベーション 本部 クラウド イノベーション センターでは、共に働いてくれる仲間を探しています。 本記事でご紹介したような調査検証をはじめ、 クラウド アーキテクトとしての業務に興味がある方のご応募お待ちしております。 https://groupcareers.isid.co.jp/pgisid/u/job.phtml?job_code=243&company_code=1 執筆: @tamura.kohei 、レビュー: @sato.taichi ( Shodo で執筆されました ) *1 : Grafana Lab 社が独自の クラウド リソースとして提供しているサービス *2 : 米国東部リージョンでの見積もり:リソース 1 つにつき $0.069/時間に加え、ユーザー 1人あたり $6/月(ただし 3 人まで無料) *3 : 使用したデータ量による従量課金に加え、ユーザー 1人あたり $8/月(ただし 3 人まで無料)
アバター
X(クロス) イノベーション 本部 クラウド イノベーション センターの田村です。 普段の業務では、 Microsoft Azure のアップデートや新サービスの調査検証をしており、その一環として Azure Managed Grafana の調査を実施しました。 本記事では、Azure Managed Grafana の概要と機能について、調査結果を絡めてご紹介します。 Azure Managed Grafana とは 可視化機能の検証 Azure Managed Grafana のリソース作成 データソースの登録 ダッシュボードの作成 サポートしているデータソース 他の Grafana サービスとの違い Power BI との違い まとめ Azure Managed Grafana とは Azure Managed Grafana は、 Microsoft と Grafana Lab 社が開発したフルマネージドの可視化サービスです。 Grafana は Grafana Lab 社が提供するサービスでしたが、このたび Microsoft とパートナーシップを結び、Azure のマネージドサービスとして公開されました。 もともと Grafana は オープンソース の可視化サービスとして知られており、Kibana や Metabase といったサービスとよく比較されている印象があります。 Azure Managed Grafana の特徴としては、下記の 4 点が紹介されています。 Azure に最適化 Azure Monitor や Azure Data Explorer などの Azure サービスの可視化を行うために最適化 ダッシュ ボード共有 複数人で Grafana ダッシュ ボードを共有する際に、管理者/編集者/閲覧者ロールの設定が可能 ユーザーロールとロール管理は後述する Azure AD と統合されている ID 管理 Azure AD( Active Directory )による ID 管理とユーザーロール管理の統合 マネージド ID による Azure サービスへのアクセス 簡単な操作 Azure Portal 上からワンクリックで Grafana ダッシュ ボードへ移動 グラフのテンプレート等を活用することで直感的な ダッシュ ボード作成が可能 詳細は下記のリンクをご参照ください。 https://azure.microsoft.com/ja-jp/services/managed-grafana/ 可視化機能の検証 実際に Azure Managed Grafana で ダッシュ ボードを作成し、可視化機能を検証してみました。 今回は可視化対象のデータソースとして、Azure の監視サービスである Azure Monitor を採用しました。 作業の流れは大まかに下記の通りとなります。 Azure Managed Grafana のリソース作成 データソースの登録 ダッシュ ボードの作成 Azure Managed Grafana のリソース作成 Azure Portal と Azure CLI から作成できますが、今回は GUI の Azure Portal から作成しました。 作成時に設定を変更しない限り、デフォルトで管理者ロールの割り当てやマネージド ID の作成が実行されるので、権限周りの設定も簡単です。 クイック スタート: Azure portal を使用して Azure Managed Grafana プレビュー インスタンスを作成する 作成が完了すると、リソース概要のエンドポイント項目に Grafana へのリンク先が表示されます。 こちらの URL を選択することで Grafana へアクセスでき、 ダッシュ ボード作成が可能になります。 データソースの登録 今回可視化する Azure Monitor を登録します。 Grafana にアクセスし、左側のメニューから Configuration > Data sources と選択すると、データソースの設定画面に遷移します。 Azure Monitor がデフォルトで表示されているので選択します。 他のデータソースは Add data source から追加できます。 Authentication にて、認証方式と サブスクリプション を設定します。 認証方式はマネージド ID(Maneged Identity)またはサービス プリンシパル (App Registration)を選択できますが、今回はマネージド ID による認証を設定しました。 Save & test を実行し、成功すればデータソースの登録は完了です。 ダッシュ ボードの作成 データソースの登録が完了したので、Azure Monitor で監視しているデータを可視化する ダッシュ ボードを作成します。 作成方法は大きく 2 種類あり、1 から自分で作成するパターンと、テンプレートを利用するパターンがあります。 まずは 1 から作成してみます。 左側のメニューから Create > Dashboard > Add panel と選択すると、 ダッシュ ボードの編集画面に遷移します。 デフォルトで表示されているグラフに意味はありません。 データソースに先ほど登録した Azure Monitor を設定し、グラフを作成してみます。 今回は 仮想マシン の CPU 使用率を可視化してみました。 作業は単純で、 サブスクリプション > リソースグループ > リソース > 可視化するメトリクス(Percentage CPU)の順に選択するだけです。 今回は CPU 使用率を例にしましたが、Azure Monitor をデータソースにすると、各リソースのさまざまなメトリクスを可視化できます。 仮想マシン だけでも、メモリの 稼働率 やディスクの使用率、インバウンド/アウトバウンドフローなどに対応しています。 Azure Monitor によって監視される内容 このようにしてどんどん ダッシュ ボードを作りこんでいくのですが、慣れない人ではどうしても手間がかかります。 そこで、用意されているテンプレートによる ダッシュ ボード作成も試してみました。 左側のメニューから Dashboard > Browse > Azure Monitor と選択すると、 ダッシュ ボードのテンプレートが一覧で表示されます。 下記の 9 種類がありますが、今回はストレージアカウントの可視化テンプレートを選択しました。 Azure Monitor アラート アプリケーションに関する インサイト Azure Cosmos DB Azure Data Explorer クラスタ Azure Key Vault ストレージアカウント (今回選択) リソースグループ内の 仮想マシン Log Analytics ワークスペース に紐づいた 仮想マシン 各リソース数 ストレージアカウントのテンプレートを選択すると、構成済みの ダッシュ ボードが表示されます。 上記画像では表示しきれていませんが、ストレージアカウントのテンプレートは 20 以上のパネル(グラフやカード)で構成されています。 これにより、リソースに関するほぼすべてのメトリクスを 1 枚の ダッシュ ボードで確認できます。 ここから編集を加えたり、新たなテンプレートを追加することも可能なので、ユーザーにとって非常に便利な機能です。 サポートしているデータソース Azure Managed Grafana では、Grafana Enterprise でサポートされているサービスをデータソースとして設定できます。 デフォルトで使用可能なデータソースは 17 種類あり、 クラウド サービスや各種 DB サービスに対応しています。 Alertmanager AWS CloudWatch Azure Monitor Elasticsearch Google Cloud Monitoring Graphite InfluxDB Loki Microsoft SQL Server ( MSSQL ) MySQL OpenTSDB PostgreSQL Prometheus Jaeger Zipkin Tempo Testdata 上記以外にも、 プラグイン を設定することでさまざまなデータソースの可視化が可能です。 プラグイン も含めると、サポートされているデータソースは 132 種類になります。 https://grafana.com/docs/grafana/latest/datasources/ https://grafana.com/grafana/plugins/?type=datasource&utm_source=grafana_add_ds 他の Grafana サービスとの違い 今回調査した Azure Managed Grafana の他に、Grafana には OSS /Grafana Cloud *1 /Grafana Enterprise といった提供形態があります。 料金やサポートといった点の差異を調査し、下記の表にまとめました。[^4] Azure Managed Grafana Grafana( OSS ) Grafana Cloud Grafana Enterprise データソース 132 132 132 132 料金 従量課金 *2 無料 従量課金 *3 要問い合わせ セキュリティ Azure による保護 なし 専用ツール内包 専用ツール内包 サポート Azure サポート なし チケット制 24 時間 365 日 Power BI との違い Microsoft の代表的な可視化サービスとして Power BI が挙げられます。 Azure における可視化という点で比較すると、Azure Managed Grafana と Power BI の最大の違いは、サポートしている Azure サービスの種類です。 Azure Managed Grafana で可視化できる Azure サービスは、 プラグイン 設定込みでも 3 つ(Azure Monitor/Azure Data Explorer /Azure DevOps)のみです。 Azure Monitor など、Azure Managed Grafana がサポートしているデータソースで取得できるデータは、ログやメトリクスといった監視情報が多くを占めています。 Grafana ダッシュ ボード上ではアラートの設定が可能なため、Azure PaaS を可視化のデータソースとした場合は取得した監視情報の可視化に特化している印象です。 一方で、Power BI は Azure SQL Database や Azure Synapse Analytics をはじめ、Azure のデータサービスをほぼ網羅しています。 そのため、Azure 上に構築したデータウェアハウスやデータマートに蓄積したデータを可視化したい、というシナリオには Power BI の方が適しているといえます。 Power BI データ ソース まとめ 本記事では、Azure Managed Grafana の概要と機能について、筆者の調査結果をもとにご紹介しました。 Azure Managed Grafana は 4 月にパブリックプレビューが開始されたばかりのサービスのため、今後もアップデートに注目したいと思います。 X(クロス) イノベーション 本部 クラウド イノベーション センターでは、共に働いてくれる仲間を探しています。 本記事でご紹介したような調査検証をはじめ、 クラウド アーキテクトとしての業務に興味がある方のご応募お待ちしております。 https://groupcareers.isid.co.jp/pgisid/u/job.phtml?job_code=243&company_code=1 執筆: @tamura.kohei 、レビュー: @sato.taichi ( Shodo で執筆されました ) *1 : Grafana Lab 社が独自の クラウド リソースとして提供しているサービス *2 : 米国東部リージョンでの見積もり:リソース 1 つにつき $0.069/時間に加え、ユーザー 1人あたり $6/月(ただし 3 人まで無料) *3 : 使用したデータ量による従量課金に加え、ユーザー 1人あたり $8/月(ただし 3 人まで無料)
アバター
こんにちは。X イノベーション 本部 クラウド イノベーション センターの柴田です。 本記事では Renovate を使った Kubernetes エコシステムの自動バージョンアップを紹介します。 なお本記事の内容は Kubernetes Meetup Tokyo #48 で紹介された 個人運用k8sクラスタの構成要素の技術選定 - でこてっくろぐ ねお にインスパイアされています。 背景 Renovateとは Platform Manager Datasource Versioning RenovateでKubernetesエコシステムを自動更新する 更新対象のKubernetesクラスタ Renovateを実行する Renovateの設定 実行結果 おわりに 背景 Kubernetes では、 Kubernetes と連携して動作する様々な OSS やサービスを活用することで、アプリケーションの開発・運用に関する生産性向上や効率化を図ることができます。 本記事ではそれらを Kubernetes エコシステムと呼びます。 多くの Kubernetes エコシステムは開発元が提供しているコンテナイメージや マニフェスト ファイルを自分たちの Kubernetes クラスタ へデプロイして利用します。 開発が盛んな Kubernetes エコシステムでは、頻繁に新機能やセキュリティ改善が実装され、新しいバージョンとしてリリースされます。 Kubernetes エコシステムの恩恵を最大限に享受するにはなるべく新しいバージョンを使用することが望ましいです。 また Kubernetes では、 マニフェスト ファイルを GitOps と呼ばれる手法で管理・デプロイすることが多いです。 GitOpsはWeaveworks社が提唱した継続的デプロイの方法であり、以下の特徴があります。 システム全体が宣言的に記述されていること。 マニフェスト がgitで管理され、それが信頼できる唯一の情報源(Single Source of Truth)であること。 承認された マニフェスト の変更が自動的にデプロイされること。またその際 Kubernetes クラスタ への認証情報を外部(例えばCIサーバなど)に持たせる必要がないこと。 マニフェスト と Kubernetes クラスタ の間に差分がある場合、それを検知したり、自動的に修正したりできること。 GitOpsを実現するツールには例えば Argo CD 、 Flux 、 PipeCD があります。 以上から、 Kubernetes エコシステムの新しいバージョンがリリースされる度に、git リポジトリ で管理されたそれらの マニフェスト を最新の内容に更新できることが望ましいです。 また、運用負荷がかからないよう、 マニフェスト の更新はなるべく自動的に行えることが望ましいです。 Renovateとは Renovate はプロジェクトの依存関係の更新を自動化するツールです。 似たようなツールに Dependabot などがあります。 Renovateはアプリケーションのライブラリの自動バージョンアップに使われることが多いですが、アプリケーション開発以外の用途でも利用できます。 ここからはRenovateで何ができるかを簡単に説明します。 なお本記事では Renovate v32.97.0 を前提とします。 Platform Renovateは以下に格納された依存関係ファイルを更新できます。 Azure DevOps Azure DevOps Server Bitbucket Cloud Bitbucket Server Gitea GitHub GitHub Enterprise Server GitLab Manager Renovateは以下の依存関係を更新できます。 docker : ansible , docker-compose , dockerfile , droneci , gitlabci , kubernetes dotnet : cake , nuget elixir : mix golang : gomod java : gradle , maven js : meteor , npm node : nodenv , nvm , travis php : composer python : pip-compile , pip_requirements , pip_setup , pipenv , poetry , pyenv , setup-cfg ruby : bundler , ruby-version rust : cargo other : ansible-galaxy , argocd , azure-pipelines , batect , batect-wrapper , bazel , bitbucket-pipelines , buildkite , cdnurl , circleci , cloudbuild , cocoapods , conan , deps-edn , flux , fvm , git-submodules , github-actions , gitlabci-include , gradle-wrapper , helm-requirements , helm-values , helmfile , helmsman , helmv3 , homebrew , html , jenkins , jsonnet-bundler , kustomize , leiningen , pre-commit , pub , regex , sbt , swift , terraform , terraform-version , terragrunt , terragrunt-version , velaci Datasource Renovateは以下の依存先の更新を検知できます。 adoptium-java , artifactory , aws-machine-image , bitbucket-tags , cdnjs , clojure , conan , conda , crate , dart , docker , flutter-version , galaxy , galaxy-collection , git-refs , git-tags , github-releases , github-tags , gitlab-packages , gitlab-releases , gitlab-tags , go , golang-version , gradle-version , helm , hex , jenkins-plugins , maven , node , npm , nuget , orb , packagist , pod , pypi , repology , ruby-version , rubygems , sbt-package , sbt-plugin , terraform-module , terraform-provider Versioning 依存関係を更新するためにはバージョン表記を比較できる必要があります。 Renovateでは以下のフォーマットのバージョン表記を扱えます。 aws-machine-image , cargo , composer , conan , debian , docker , git , gradle , hashicorp , helm , hex , ivy , loose , maven , node , npm , nuget , pep440 , poetry , regex , rez , ruby , semver , semver-coerced , swift , ubuntu Renovateで Kubernetes エコシステムを自動更新する では実際にRenovateを使って Kubernetes エコシステムの更新作業を自動化したいと思います。 更新対象の Kubernetes クラスタ 今回は私が検証用途で使用している Kubernetes クラスタ を対象にします。 この Kubernetes クラスタ には以下の特徴があります。 すべての マニフェスト は GitHub リポジトリ で管理しています。 マニフェスト を Kubernetes クラスタ へデプロイするために Argo CD と Flux を使用しています。 通常は片方だけで十分ですが、検証用途の環境のため両方とも動かしています。 Kubernetes エコシステムの マニフェスト は、以下のいずれかの方法で管理しています。 Helm : 公式や3rd partyが提供するHelm Chartを利用します。 具体的にはArgo CDの Application リソースやFluxの HelmRelease リソースとして Kubernetes クラスタ へデプロイします。 またローカル環境で マニフェスト を生成できるよう helmfile も使用しています。 Kustomize : kustomize を使って公式が提供する マニフェスト を https 経由で参照します。 イメージタグを書き換えることもあります。 以下は amazon-vpc-cni-k8s の マニフェスト を生成する kustomization.yaml の例です。 apiVersion : kustomize.config.k8s.io/v1beta1 kind : Kustomization resources : - https://raw.githubusercontent.com/aws/amazon-vpc-cni-k8s/v1.10.3/config/master/aws-k8s-cni.yaml images : - name : 602401143452.dkr.ecr.us-west-2.amazonaws.com/amazon-k8s-cni newName : 602401143452.dkr.ecr.ap-northeast-1.amazonaws.com/amazon-k8s-cni newTag : v1.10.3 - name : 602401143452.dkr.ecr.us-west-2.amazonaws.com/amazon-k8s-cni-init newName : 602401143452.dkr.ecr.ap-northeast-1.amazonaws.com/amazon-k8s-cni-init newTag : v1.10.3 Renovateを実行する Renovateには大まかに2種類の実行方法があります。 GitHub App を使用する 自分たちでRenovateを実行する(Self-Hosting) 今回は GitHub Actions renovatebot/github-action を使用して自分たちでRenovateを実行します。 その他のSelf-Hostingの実行方法は Self-Hosting Renovate を参照してください。 以下は GitHub Actionsのワークフローです。 # .github/workflows/renovate.yaml name : Renovate on : schedule : - cron : '0 0 * * *' # Run workflow at 09:00 AM JST every day jobs : renovate : name : Renovate runs-on : ubuntu-latest steps : - uses : actions/checkout@v3 - name : Run renovate uses : renovatebot/github-action@v32.97.0 with : token : ${{ secrets.GH_TOKEN }} configurationFile : renovate.json GH_TOKEN は https://github.com/renovatebot/github-action#token に従って作成した GitHub のPersonal Access Tokenです。 GitHub Actionsでは GITHUB_TOKEN が利用できますが、 https://github.com/renovatebot/github-action#token に Note that the GITHUB_TOKEN secret can't be used for authenticating Renovate. とあるためこのようにしています。 Renovateの設定 次にRenovateの設定ファイル renovate.json を作成します。 今回使用した設定は以下のとおりです。 { " repositories ": [ " ISID/renovate-sample " ] , " extends ": [ " config:base ", " :prHourlyLimitNone ", " :prConcurrentLimitNone " ] , " enabledManagers ": [ " argocd ", " flux ", " helmfile ", " kustomize ", " regex " ] , " labels ": [ " renovate " ] , " assignees ": [ " @ShibataTakao " ] , " argocd ": { " fileMatch ": [ " ^manifests/argo-cd-apps/.* \\ .ya?ml$ " ] } , " flux ": { " fileMatch ": [ " ^manifests/flux/.* \\ .ya?ml$ ", " ^manifests/flux-ks/.* \\ .ya?ml$ " ] } , " regexManagers ": [ { " fileMatch ": [ " (^|/)kustomization \\ .ya?ml$ " ] , " matchStrings ": [ " https://raw \\ .githubusercontent \\ .com/(?<depName>[^/]+/[^/]+)/(?<currentValue>[^/]+)/.* ", " https://github \\ .com/(?<depName>[^/]+/[^/]+)/releases/download/(?<currentValue>[^/]+)/.* " ] , " datasourceTemplate ": " github-releases ", " versioningTemplate ": " semver " } ] , " packageRules ": [ { " matchPackageNames ": [ " kubernetes/autoscaler " ] , " matchDatasources ": [ " github-tags ", " github-releases " ] , " versioning ": " regex:^cluster-autoscaler-(?<major> \\ d+) \\ .(?<minor> \\ d+) \\ .(?<patch> \\ d+)?$ ", " allowedVersions ": " < 1.23.0 " } , { " matchPackageNames ": [ " k8s.gcr.io/autoscaling/cluster-autoscaler " ] , " matchDatasources ": [ " docker " ] , " allowedVersions ": " < 1.23.0 " } ] } 各設定を順に見ていきましょう。 " repositories ": [ " ISID/renovate-sample " ] repositories にはRenovateの対象とする リポジトリ を指定します。 リポジトリ 名を直接指定するかわりに autodiscover と autodiscoverFilter を設定してRenovateの対象とする リポジトリ を自動的に検出することもできます。 " extends ": [ " config:base ", " :prHourlyLimitNone ", " :prConcurrentLimitNone " ] extends には Preset を指定します。 Presetは定義済みの設定の集合です。 デフォルトで定義されたPresetを使うだけでなく、独自のPresetを定義することもできます。 今回は以下のPresetを使用します。 config:base :prHourlyLimitNone :prConcurrentLimitNone " enabledManagers ": [ " argocd ", " flux ", " helmfile ", " kustomize ", " regex " ] enabledManagers を設定して明示的に指定したManagerのみを有効化します。 多くのManagerはデフォルトで有効になっています。 各Managerの有効/無効は <manager>.enabled の値を true または false に設定することで変更できます。 または enabledManagers に有効にするManagerを明示的に指定することで、指定したManagerのみを有効にできます。 詳細は Enabling and disabling managers を参照してください。 今回は以下のManagerのみを有効化します。 argocd flux helmfile kustomize regex 今回の更新対象のうち Helm : 公式や3rd partyが提供するHelm Chartを利用します。 具体的にはArgo CDの Application リソースやFluxの HelmRelease リソースとして Kubernetes クラスタ へデプロイします。 またローカル環境で マニフェスト を生成できるよう helmfile も使用しています。 は argocd と flux と helmfile を使ってHelm ChartをバージョンアップするためのPRを自動成します。 また Kustomize : kustomize を使って公式が提供する マニフェスト を https 経由で参照します。 イメージタグを書き換えることもあります。 は kustomize と regex を使って参照先の マニフェスト のURLやイメージタグをバージョンアップするためのPRを自動作成します。 regex については後ほどもう少し詳しく説明します。 " labels ": [ " renovate " ] , " assignees ": [ " @ShibataTakao " ] labels と assignees にはRenovateが依存関係を更新するために作成するPRのLabelsとAssigneesを設定します。 " argocd ": { " fileMatch ": [ " ^manifests/argo-cd-apps/.* \\ .ya?ml$ " ] } , " flux ": { " fileMatch ": [ " ^manifests/flux/.* \\ .ya?ml$ ", " ^manifests/flux-ks/.* \\ .ya?ml$ " ] } fileMatch には各Managerの依存関係ファイルの追加パスを 正規表現 で記述します。 多くのManagerにはデフォルトの fileMatch が設定されていますが、デフォルトの fileMatch が設定されていない場合や独自のファイルパスを追加で設定したい場合には、 fileMatch にそれらを追加設定できます。 " regexManagers ": [ { " fileMatch ": [ " (^|/)kustomization \\ .ya?ml$ " ] , " matchStrings ": [ " https://raw \\ .githubusercontent \\ .com/(?<depName>[^/]+/[^/]+)/(?<currentValue>[^/]+)/.* ", " https://github \\ .com/(?<depName>[^/]+/[^/]+)/releases/download/(?<currentValue>[^/]+)/.* " ] , " datasourceTemplate ": " github-releases ", " versioningTemplate ": " semver " } ] regexManagers には regex Managerの設定を記述します。 regex を用いることで 正規表現 を使用して依存関係を検出・自動更新するユーザー独自の仕組みを構築できます。 今回は Kustomize : kustomize を使って公式が提供する マニフェスト を https 経由で参照します。 イメージタグを書き換えることもあります。 以下は amazon-vpc-cni-k8s の マニフェスト を生成する kustomization.yaml の例です。 yaml apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization resources: - https://raw.githubusercontent.com/aws/amazon-vpc-cni-k8s/v1.10.3/config/master/aws-k8s-cni.yaml images: - name: 602401143452.dkr.ecr.us-west-2.amazonaws.com/amazon-k8s-cni newName: 602401143452.dkr.ecr.ap-northeast-1.amazonaws.com/amazon-k8s-cni newTag: v1.10.3 - name: 602401143452.dkr.ecr.us-west-2.amazonaws.com/amazon-k8s-cni-init newName: 602401143452.dkr.ecr.ap-northeast-1.amazonaws.com/amazon-k8s-cni-init newTag: v1.10.3 で述べた参照先の マニフェスト のURLを自動更新するためにこの仕組みを利用します。 fileMatch には更新対象のファイル kustomization.yaml のファイルパスを 正規表現 で設定します。 matchStrings には kustomization.yaml 内の更新対象の文字列を設定します。 今回は https://raw.githubusercontent.com/<user>/<repo>/<tag>/<filepath> ( GitHub リポジトリ のコンテンツ) https://github.com/<user>/<repo>/releases/download/<release>/<filepath> ( GitHub リポジトリ のリリースのアセット) が更新対象です。 regex では更新対象の依存関係に関する以下の設定をする必要があります。 これらは、該当する設定項目に値を設定するか、 matchStrings の 正規表現 に名前付きキャプチャグループを用いることで設定できます。 Capture Group Config Field 必須 説明 currentValue なし 必須 現在のバージョン。Renovateによって更新される。 depName depNameTemplate 必須 依存関係の名前。 packageName packageNameTemplate オプション パッケージ名。省略した場合 depName と同じになる。 datasource datasourceTemplate 必須 依存関係の参照先( Datasource )。 depType depTypeTemplate オプション 依存関係の種類。 versioning versioningTemplate オプション 依存関係のバージョン表記( Versioning )。デフォルトは semver 。 extractVersion extractVersionTemplate オプション キャプチャしたバージョンと Datasource のバージョンの表記が異なる場合に使用する。詳細は extractVersion を参照。 currentDigest なし オプション 現在のDigest。Renovateによって更新される。 registryUrl registryUrlTemplate オプション 依存関係の参照先のURL。詳細は registryUrls を参照。 " packageRules ": [ { " matchPackageNames ": [ " kubernetes/autoscaler " ] , " matchDatasources ": [ " github-tags ", " github-releases " ] , " versioning ": " regex:^cluster-autoscaler-(?<major> \\ d+) \\ .(?<minor> \\ d+) \\ .(?<patch> \\ d+)?$ ", " allowedVersions ": " < 1.23.0 " } , { " matchPackageNames ": [ " k8s.gcr.io/autoscaling/cluster-autoscaler " ] , " matchDatasources ": [ " docker " ] , " allowedVersions ": " < 1.23.0 " } ] packageRules には特定の依存関係の更新における追加ルールを設定します。 今回は Cluster Autoscaler に関する以下の追加ルールを設定します。 Cluster AutoscalerのGiHub リポジトリ におけるタグやリリースの 命名規則 は cluster-autoscaler-<major>.<minor>.<patch> です(例: cluster-autoscaler-1.22.3 )。それを解釈できるよう versioning に regex の 正規表現 を指定します。 Cluster Autoscalerは対象の Kubernetes クラスタ に対応したバージョンを動かす必要があります。 今回の対象である Kubernetes クラスタ は現時点でv1.22です。 よってCluster Autoscalerのバージョンもv1.22.Xである必要があります。 RenovateがCluster Autoscalerをv1.23.X以降に更新しないよう allowedVersions を指定します。 実行結果 設定に従い GitHub Actionsが毎朝9時にRenovateを実行します。 Helm Chartに更新がある場合は以下のようなPRが自動的に作成されます。 Kustomizeが参照する マニフェスト のURLやイメージタグに更新がある場合は以下のようなPRが自動的に作成されます。 PRの説明文には公式 GitHub リポジトリ のReleaseからRelease Notesが転記されており、具体的な変更内容を知ることができます。 また Dependency Dashboard を有効にしておくと、Renovateが作成したPRを一覧して管理できるIssueが作成されます。 Dependency Dashboard を有効化する設定はPreset config:base に含まれています。 あとはこれらのPRをマージし、Argo CDやFluxを使って Kubernetes クラスタ へデプロイするだけで、 Kubernetes エコシステムの更新は完了です。 なお今回は全てのPRの承認・マージを手動で行う形にしましたが、特定の更新のみ承認・マージを必要にしてそれ以外はPRを自動的にマージするよう設定することも可能です。 特定の更新として、例えば特定のパッケージやメジャーバージョンの更新などが設定できます。 おわりに 本記事ではRenovateを使った Kubernetes エコシステムの自動バージョンアップを紹介しました。 開発が盛んな Kubernetes エコシステムでは、頻繁に新機能やセキュリティ改善が実装され、新しいバージョンとしてリリースされます。 Renovateを活用し、 Kubernetes エコシステムを頻繁かつ自動的にバージョンアップすることで、それらの恩恵を最大限に享受できます。 本記事が少しでも Kubernetes クラスタ を運用している方のお役に立てば幸いです。 私たちは同じチームで働いてくれる仲間を探しています。 クラウド アーキテクトの業務に興味がある方のご応募をお待ちしています。 クラウドアーキテクト 執筆: @shibata.takao 、レビュー: @yamashita.tsuyoshi ( Shodo で執筆されました )
アバター
こんにちは。X イノベーション 本部 クラウド イノベーション センターの柴田です。 本記事では Renovate を使った Kubernetes エコシステムの自動バージョンアップを紹介します。 なお本記事の内容は Kubernetes Meetup Tokyo #48 で紹介された 個人運用k8sクラスタの構成要素の技術選定 - でこてっくろぐ ねお にインスパイアされています。 背景 Renovateとは Platform Manager Datasource Versioning RenovateでKubernetesエコシステムを自動更新する 更新対象のKubernetesクラスタ Renovateを実行する Renovateの設定 実行結果 おわりに 背景 Kubernetes では、 Kubernetes と連携して動作する様々な OSS やサービスを活用することで、アプリケーションの開発・運用に関する生産性向上や効率化を図ることができます。 本記事ではそれらを Kubernetes エコシステムと呼びます。 多くの Kubernetes エコシステムは開発元が提供しているコンテナイメージや マニフェスト ファイルを自分たちの Kubernetes クラスタ へデプロイして利用します。 開発が盛んな Kubernetes エコシステムでは、頻繁に新機能やセキュリティ改善が実装され、新しいバージョンとしてリリースされます。 Kubernetes エコシステムの恩恵を最大限に享受するにはなるべく新しいバージョンを使用することが望ましいです。 また Kubernetes では、 マニフェスト ファイルを GitOps と呼ばれる手法で管理・デプロイすることが多いです。 GitOpsはWeaveworks社が提唱した継続的デプロイの方法であり、以下の特徴があります。 システム全体が宣言的に記述されていること。 マニフェスト がgitで管理され、それが信頼できる唯一の情報源(Single Source of Truth)であること。 承認された マニフェスト の変更が自動的にデプロイされること。またその際 Kubernetes クラスタ への認証情報を外部(例えばCIサーバなど)に持たせる必要がないこと。 マニフェスト と Kubernetes クラスタ の間に差分がある場合、それを検知したり、自動的に修正したりできること。 GitOpsを実現するツールには例えば Argo CD 、 Flux 、 PipeCD があります。 以上から、 Kubernetes エコシステムの新しいバージョンがリリースされる度に、git リポジトリ で管理されたそれらの マニフェスト を最新の内容に更新できることが望ましいです。 また、運用負荷がかからないよう、 マニフェスト の更新はなるべく自動的に行えることが望ましいです。 Renovateとは Renovate はプロジェクトの依存関係の更新を自動化するツールです。 似たようなツールに Dependabot などがあります。 Renovateはアプリケーションのライブラリの自動バージョンアップに使われることが多いですが、アプリケーション開発以外の用途でも利用できます。 ここからはRenovateで何ができるかを簡単に説明します。 なお本記事では Renovate v32.97.0 を前提とします。 Platform Renovateは以下に格納された依存関係ファイルを更新できます。 Azure DevOps Azure DevOps Server Bitbucket Cloud Bitbucket Server Gitea GitHub GitHub Enterprise Server GitLab Manager Renovateは以下の依存関係を更新できます。 docker : ansible , docker-compose , dockerfile , droneci , gitlabci , kubernetes dotnet : cake , nuget elixir : mix golang : gomod java : gradle , maven js : meteor , npm node : nodenv , nvm , travis php : composer python : pip-compile , pip_requirements , pip_setup , pipenv , poetry , pyenv , setup-cfg ruby : bundler , ruby-version rust : cargo other : ansible-galaxy , argocd , azure-pipelines , batect , batect-wrapper , bazel , bitbucket-pipelines , buildkite , cdnurl , circleci , cloudbuild , cocoapods , conan , deps-edn , flux , fvm , git-submodules , github-actions , gitlabci-include , gradle-wrapper , helm-requirements , helm-values , helmfile , helmsman , helmv3 , homebrew , html , jenkins , jsonnet-bundler , kustomize , leiningen , pre-commit , pub , regex , sbt , swift , terraform , terraform-version , terragrunt , terragrunt-version , velaci Datasource Renovateは以下の依存先の更新を検知できます。 adoptium-java , artifactory , aws-machine-image , bitbucket-tags , cdnjs , clojure , conan , conda , crate , dart , docker , flutter-version , galaxy , galaxy-collection , git-refs , git-tags , github-releases , github-tags , gitlab-packages , gitlab-releases , gitlab-tags , go , golang-version , gradle-version , helm , hex , jenkins-plugins , maven , node , npm , nuget , orb , packagist , pod , pypi , repology , ruby-version , rubygems , sbt-package , sbt-plugin , terraform-module , terraform-provider Versioning 依存関係を更新するためにはバージョン表記を比較できる必要があります。 Renovateでは以下のフォーマットのバージョン表記を扱えます。 aws-machine-image , cargo , composer , conan , debian , docker , git , gradle , hashicorp , helm , hex , ivy , loose , maven , node , npm , nuget , pep440 , poetry , regex , rez , ruby , semver , semver-coerced , swift , ubuntu Renovateで Kubernetes エコシステムを自動更新する では実際にRenovateを使って Kubernetes エコシステムの更新作業を自動化したいと思います。 更新対象の Kubernetes クラスタ 今回は私が検証用途で使用している Kubernetes クラスタ を対象にします。 この Kubernetes クラスタ には以下の特徴があります。 すべての マニフェスト は GitHub リポジトリ で管理しています。 マニフェスト を Kubernetes クラスタ へデプロイするために Argo CD と Flux を使用しています。 通常は片方だけで十分ですが、検証用途の環境のため両方とも動かしています。 Kubernetes エコシステムの マニフェスト は、以下のいずれかの方法で管理しています。 Helm : 公式や3rd partyが提供するHelm Chartを利用します。 具体的にはArgo CDの Application リソースやFluxの HelmRelease リソースとして Kubernetes クラスタ へデプロイします。 またローカル環境で マニフェスト を生成できるよう helmfile も使用しています。 Kustomize : kustomize を使って公式が提供する マニフェスト を https 経由で参照します。 イメージタグを書き換えることもあります。 以下は amazon-vpc-cni-k8s の マニフェスト を生成する kustomization.yaml の例です。 apiVersion : kustomize.config.k8s.io/v1beta1 kind : Kustomization resources : - https://raw.githubusercontent.com/aws/amazon-vpc-cni-k8s/v1.10.3/config/master/aws-k8s-cni.yaml images : - name : 602401143452.dkr.ecr.us-west-2.amazonaws.com/amazon-k8s-cni newName : 602401143452.dkr.ecr.ap-northeast-1.amazonaws.com/amazon-k8s-cni newTag : v1.10.3 - name : 602401143452.dkr.ecr.us-west-2.amazonaws.com/amazon-k8s-cni-init newName : 602401143452.dkr.ecr.ap-northeast-1.amazonaws.com/amazon-k8s-cni-init newTag : v1.10.3 Renovateを実行する Renovateには大まかに2種類の実行方法があります。 GitHub App を使用する 自分たちでRenovateを実行する(Self-Hosting) 今回は GitHub Actions renovatebot/github-action を使用して自分たちでRenovateを実行します。 その他のSelf-Hostingの実行方法は Self-Hosting Renovate を参照してください。 以下は GitHub Actionsのワークフローです。 # .github/workflows/renovate.yaml name : Renovate on : schedule : - cron : '0 0 * * *' # Run workflow at 09:00 AM JST every day jobs : renovate : name : Renovate runs-on : ubuntu-latest steps : - uses : actions/checkout@v3 - name : Run renovate uses : renovatebot/github-action@v32.97.0 with : token : ${{ secrets.GH_TOKEN }} configurationFile : renovate.json GH_TOKEN は https://github.com/renovatebot/github-action#token に従って作成した GitHub のPersonal Access Tokenです。 GitHub Actionsでは GITHUB_TOKEN が利用できますが、 https://github.com/renovatebot/github-action#token に Note that the GITHUB_TOKEN secret can't be used for authenticating Renovate. とあるためこのようにしています。 Renovateの設定 次にRenovateの設定ファイル renovate.json を作成します。 今回使用した設定は以下のとおりです。 { " repositories ": [ " ISID/renovate-sample " ] , " extends ": [ " config:base ", " :prHourlyLimitNone ", " :prConcurrentLimitNone " ] , " enabledManagers ": [ " argocd ", " flux ", " helmfile ", " kustomize ", " regex " ] , " labels ": [ " renovate " ] , " assignees ": [ " @ShibataTakao " ] , " argocd ": { " fileMatch ": [ " ^manifests/argo-cd-apps/.* \\ .ya?ml$ " ] } , " flux ": { " fileMatch ": [ " ^manifests/flux/.* \\ .ya?ml$ ", " ^manifests/flux-ks/.* \\ .ya?ml$ " ] } , " regexManagers ": [ { " fileMatch ": [ " (^|/)kustomization \\ .ya?ml$ " ] , " matchStrings ": [ " https://raw \\ .githubusercontent \\ .com/(?<depName>[^/]+/[^/]+)/(?<currentValue>[^/]+)/.* ", " https://github \\ .com/(?<depName>[^/]+/[^/]+)/releases/download/(?<currentValue>[^/]+)/.* " ] , " datasourceTemplate ": " github-releases ", " versioningTemplate ": " semver " } ] , " packageRules ": [ { " matchPackageNames ": [ " kubernetes/autoscaler " ] , " matchDatasources ": [ " github-tags ", " github-releases " ] , " versioning ": " regex:^cluster-autoscaler-(?<major> \\ d+) \\ .(?<minor> \\ d+) \\ .(?<patch> \\ d+)?$ ", " allowedVersions ": " < 1.23.0 " } , { " matchPackageNames ": [ " k8s.gcr.io/autoscaling/cluster-autoscaler " ] , " matchDatasources ": [ " docker " ] , " allowedVersions ": " < 1.23.0 " } ] } 各設定を順に見ていきましょう。 " repositories ": [ " ISID/renovate-sample " ] repositories にはRenovateの対象とする リポジトリ を指定します。 リポジトリ 名を直接指定するかわりに autodiscover と autodiscoverFilter を設定してRenovateの対象とする リポジトリ を自動的に検出することもできます。 " extends ": [ " config:base ", " :prHourlyLimitNone ", " :prConcurrentLimitNone " ] extends には Preset を指定します。 Presetは定義済みの設定の集合です。 デフォルトで定義されたPresetを使うだけでなく、独自のPresetを定義することもできます。 今回は以下のPresetを使用します。 config:base :prHourlyLimitNone :prConcurrentLimitNone " enabledManagers ": [ " argocd ", " flux ", " helmfile ", " kustomize ", " regex " ] enabledManagers を設定して明示的に指定したManagerのみを有効化します。 多くのManagerはデフォルトで有効になっています。 各Managerの有効/無効は <manager>.enabled の値を true または false に設定することで変更できます。 または enabledManagers に有効にするManagerを明示的に指定することで、指定したManagerのみを有効にできます。 詳細は Enabling and disabling managers を参照してください。 今回は以下のManagerのみを有効化します。 argocd flux helmfile kustomize regex 今回の更新対象のうち Helm : 公式や3rd partyが提供するHelm Chartを利用します。 具体的にはArgo CDの Application リソースやFluxの HelmRelease リソースとして Kubernetes クラスタ へデプロイします。 またローカル環境で マニフェスト を生成できるよう helmfile も使用しています。 は argocd と flux と helmfile を使ってHelm ChartをバージョンアップするためのPRを自動成します。 また Kustomize : kustomize を使って公式が提供する マニフェスト を https 経由で参照します。 イメージタグを書き換えることもあります。 は kustomize と regex を使って参照先の マニフェスト のURLやイメージタグをバージョンアップするためのPRを自動作成します。 regex については後ほどもう少し詳しく説明します。 " labels ": [ " renovate " ] , " assignees ": [ " @ShibataTakao " ] labels と assignees にはRenovateが依存関係を更新するために作成するPRのLabelsとAssigneesを設定します。 " argocd ": { " fileMatch ": [ " ^manifests/argo-cd-apps/.* \\ .ya?ml$ " ] } , " flux ": { " fileMatch ": [ " ^manifests/flux/.* \\ .ya?ml$ ", " ^manifests/flux-ks/.* \\ .ya?ml$ " ] } fileMatch には各Managerの依存関係ファイルの追加パスを 正規表現 で記述します。 多くのManagerにはデフォルトの fileMatch が設定されていますが、デフォルトの fileMatch が設定されていない場合や独自のファイルパスを追加で設定したい場合には、 fileMatch にそれらを追加設定できます。 " regexManagers ": [ { " fileMatch ": [ " (^|/)kustomization \\ .ya?ml$ " ] , " matchStrings ": [ " https://raw \\ .githubusercontent \\ .com/(?<depName>[^/]+/[^/]+)/(?<currentValue>[^/]+)/.* ", " https://github \\ .com/(?<depName>[^/]+/[^/]+)/releases/download/(?<currentValue>[^/]+)/.* " ] , " datasourceTemplate ": " github-releases ", " versioningTemplate ": " semver " } ] regexManagers には regex Managerの設定を記述します。 regex を用いることで 正規表現 を使用して依存関係を検出・自動更新するユーザー独自の仕組みを構築できます。 今回は Kustomize : kustomize を使って公式が提供する マニフェスト を https 経由で参照します。 イメージタグを書き換えることもあります。 以下は amazon-vpc-cni-k8s の マニフェスト を生成する kustomization.yaml の例です。 yaml apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization resources: - https://raw.githubusercontent.com/aws/amazon-vpc-cni-k8s/v1.10.3/config/master/aws-k8s-cni.yaml images: - name: 602401143452.dkr.ecr.us-west-2.amazonaws.com/amazon-k8s-cni newName: 602401143452.dkr.ecr.ap-northeast-1.amazonaws.com/amazon-k8s-cni newTag: v1.10.3 - name: 602401143452.dkr.ecr.us-west-2.amazonaws.com/amazon-k8s-cni-init newName: 602401143452.dkr.ecr.ap-northeast-1.amazonaws.com/amazon-k8s-cni-init newTag: v1.10.3 で述べた参照先の マニフェスト のURLを自動更新するためにこの仕組みを利用します。 fileMatch には更新対象のファイル kustomization.yaml のファイルパスを 正規表現 で設定します。 matchStrings には kustomization.yaml 内の更新対象の文字列を設定します。 今回は https://raw.githubusercontent.com/<user>/<repo>/<tag>/<filepath> ( GitHub リポジトリ のコンテンツ) https://github.com/<user>/<repo>/releases/download/<release>/<filepath> ( GitHub リポジトリ のリリースのアセット) が更新対象です。 regex では更新対象の依存関係に関する以下の設定をする必要があります。 これらは、該当する設定項目に値を設定するか、 matchStrings の 正規表現 に名前付きキャプチャグループを用いることで設定できます。 Capture Group Config Field 必須 説明 currentValue なし 必須 現在のバージョン。Renovateによって更新される。 depName depNameTemplate 必須 依存関係の名前。 packageName packageNameTemplate オプション パッケージ名。省略した場合 depName と同じになる。 datasource datasourceTemplate 必須 依存関係の参照先( Datasource )。 depType depTypeTemplate オプション 依存関係の種類。 versioning versioningTemplate オプション 依存関係のバージョン表記( Versioning )。デフォルトは semver 。 extractVersion extractVersionTemplate オプション キャプチャしたバージョンと Datasource のバージョンの表記が異なる場合に使用する。詳細は extractVersion を参照。 currentDigest なし オプション 現在のDigest。Renovateによって更新される。 registryUrl registryUrlTemplate オプション 依存関係の参照先のURL。詳細は registryUrls を参照。 " packageRules ": [ { " matchPackageNames ": [ " kubernetes/autoscaler " ] , " matchDatasources ": [ " github-tags ", " github-releases " ] , " versioning ": " regex:^cluster-autoscaler-(?<major> \\ d+) \\ .(?<minor> \\ d+) \\ .(?<patch> \\ d+)?$ ", " allowedVersions ": " < 1.23.0 " } , { " matchPackageNames ": [ " k8s.gcr.io/autoscaling/cluster-autoscaler " ] , " matchDatasources ": [ " docker " ] , " allowedVersions ": " < 1.23.0 " } ] packageRules には特定の依存関係の更新における追加ルールを設定します。 今回は Cluster Autoscaler に関する以下の追加ルールを設定します。 Cluster AutoscalerのGiHub リポジトリ におけるタグやリリースの 命名規則 は cluster-autoscaler-<major>.<minor>.<patch> です(例: cluster-autoscaler-1.22.3 )。それを解釈できるよう versioning に regex の 正規表現 を指定します。 Cluster Autoscalerは対象の Kubernetes クラスタ に対応したバージョンを動かす必要があります。 今回の対象である Kubernetes クラスタ は現時点でv1.22です。 よってCluster Autoscalerのバージョンもv1.22.Xである必要があります。 RenovateがCluster Autoscalerをv1.23.X以降に更新しないよう allowedVersions を指定します。 実行結果 設定に従い GitHub Actionsが毎朝9時にRenovateを実行します。 Helm Chartに更新がある場合は以下のようなPRが自動的に作成されます。 Kustomizeが参照する マニフェスト のURLやイメージタグに更新がある場合は以下のようなPRが自動的に作成されます。 PRの説明文には公式 GitHub リポジトリ のReleaseからRelease Notesが転記されており、具体的な変更内容を知ることができます。 また Dependency Dashboard を有効にしておくと、Renovateが作成したPRを一覧して管理できるIssueが作成されます。 Dependency Dashboard を有効化する設定はPreset config:base に含まれています。 あとはこれらのPRをマージし、Argo CDやFluxを使って Kubernetes クラスタ へデプロイするだけで、 Kubernetes エコシステムの更新は完了です。 なお今回は全てのPRの承認・マージを手動で行う形にしましたが、特定の更新のみ承認・マージを必要にしてそれ以外はPRを自動的にマージするよう設定することも可能です。 特定の更新として、例えば特定のパッケージやメジャーバージョンの更新などが設定できます。 おわりに 本記事ではRenovateを使った Kubernetes エコシステムの自動バージョンアップを紹介しました。 開発が盛んな Kubernetes エコシステムでは、頻繁に新機能やセキュリティ改善が実装され、新しいバージョンとしてリリースされます。 Renovateを活用し、 Kubernetes エコシステムを頻繁かつ自動的にバージョンアップすることで、それらの恩恵を最大限に享受できます。 本記事が少しでも Kubernetes クラスタ を運用している方のお役に立てば幸いです。 私たちは同じチームで働いてくれる仲間を探しています。 クラウド アーキテクトの業務に興味がある方のご応募をお待ちしています。 クラウドアーキテクト 執筆: @shibata.takao 、レビュー: @yamashita.tsuyoshi ( Shodo で執筆されました )
アバター
こんにちは。ISID コミュニケーションIT事業部 瀧川亮弘(あきひろ)です。 プロジェクトにて Storybook を利用しています。 Storybook の概要と基本的な実装方法をお伝えできればと思います。 Storybookとは? 一言でいうとUIのカタログです。また、それを作成するためのライブラリです。 Storybook を利用することで、 GUI 上でUI コンポーネント のデザインや振る舞いを簡単に確認できます。 百聞は一見にしかずということで、普段大変お世話になっている VSCode の Storybook のリンクを貼っておきます。良かったら触ってみてください! Microsoft VSCode Webview UI Toolkit Storybookの実装 UI コンポーネント を Storybook 上で表示するために .stories.js という拡張子のファイルを作成する必要があります。 本章では、こちらのファイルの基本的な実装について記載します。 尚、いくつか存在する記法のうち、 CSF(Component Story Format) という記法を採用します。以前まで主流だった、 storiesOf 関数や add 関数を利用する storiesOf API は現在非推奨となっています。 余談ですが、筆者は storiesOf API で記載された数十のファイルを CSF に書き換える作業をしたことがあります。個々の作業は簡単でしたが、数が数だけにとても苦労した懐かしい思い出があります。 対象の コンポーネント 対象とする コンポーネント として、簡単なボタンを用意しました。 愛着が湧くように自身の名前をつけて AkiButton と名付けました。 Vue3 の script setup 構文を利用しています。 <template> <button class="button" :class="[getColor]" @click="handleClick"> <slot /> </button> </template> <script setup lang="ts"> import { computed } from "vue"; interface Props { /** * 色 */ color?: "basic" | "primary" | "error"; } const props = withDefaults(defineProps<Props>(), { color: "basic", }); interface Emits { /** * クリックイベント */ (e: "click"): void; } const emit = defineEmits<Emits>(); const handleClick = () => { emit("click"); }; const getColor = computed<string>(() => `button__color--${props.color}`); </script> <style lang="scss" scoped> .button { height: 30px; padding: 0 16px; cursor: pointer; &__color { &--basic { background-color: $color__primary-light; border-color: $color__primary-light; color: $color__primary; } &--primary { background-color: $color__primary; border-color: $color__primary; color: $color__white; } &--error { background-color: $color__error; border-color: $color__error; color: $color__white; } } } </style> 実装 完成形 まずは完成形をお見せします。 次章から順を追って実装を確認します。 import AkiButton from "./AkiButton.vue"; export default { title: "Atoms/AkiButton", component: AkiButton, argTypes: { color: { control: { type: "select", options: ["basic", "primary", "error"], }, }, click: { action: "click", }, }, }; const Template = (args, { argTypes }) => ({ components: { AkiButton }, setup() { return { ...args, }; }, template: ` <AkiButton :color="color" @click="click">あきボタン</AkiButton> `, }); export const Basic = Template.bind({}); Basic.args = { color: "basic", }; export const Primary = Template.bind({}); Primary.args = { color: "primary", }; export const Error = Template.bind({}); Error.args = { color: "error", }; メタデータ の定義 メタデータ を定義します。 title には、メニューに表示される名前を定義します。 /(スラッシュ) で階層を切ると、 GUI 上ツリー表示されます。ちなみに、現在のプロジェクトでは Atomic Design に基づいて階層を切っています。 component には、 対象のコンポーネント で用意した コンポーネント を指定しています。 これらを指定したオブジェクトをデフォルトエクスポートすることで メタデータ として認識してくれます。 import AkiButton from "./AkiButton.vue"; export default { title: "Atoms/AkiButton", component: AkiButton, }; テンプレートの定義 テンプレートを定義します。 次章でいくつかのストーリーを定義しますが、それらのストーリーが共通して利用するテンプレートとなります。各ストーリーで利用する関数なので、テンプレート自体の export は必要ありません。 テンプレートの関数は、引数として各ストーリーごとに定義されたプロパティを受け取ります。ここでは args という名前で受け取ったプロパティを対象 コンポーネント に渡しています。 const Template = (args, { argTypes }) => ({ components: { AkiButton }, setup() { return { ...args, }; }, template: ` <AkiButton :color="color" @click="click">あきボタン</AkiButton> `, }); ストーリーの定義 Storybook のメインであるストーリーを定義します。 コンポーネント を表示するパターン、テストするパターンなどを考慮してストーリーを表現します。ここでは、 color プロパティの値ごとに、ストーリーを作成しています。 テンプレートの定義 で用意したテンプレートの関数をもとにいくつかのストーリーを作成するのが便利です。 ストーリーは関数で表現し、名前付きエクスポートします。 ここでつけた名前がデフォルトではストーリー名となります。 export const Basic = Template.bind({}); Basic.args = { color: "basic", }; export const Primary = Template.bind({}); Primary.args = { color: "primary", }; export const Error = Template.bind({}); Error.args = { color: "error", }; アドオンを利用する アドオンとはStorybookの拡張モジュールのことです。 今回はStorybookが公式でサポートしている3つのアドオンを利用してみます。 Controlsアドオン Controls アドオンにより、 コンポーネント に対するプロパティを GUI 上で動的に切り替えることができます。 argTypes: { color: { control: { type: "select", options: ["basic", "primary", "error"], }, }, }, Actionsアドオン Actions アドオンにより、発火されたイベントをロギングします。 各イベントが発火するタイミングやイベントの内容を確認できます。 argTypes: { click: { action: "click", }, }, Docsアドオン Docs アドオンは、 ソースコード を解析しドキュメントを自動生成してくれます。 Vueファイルに記載したコメントがドキュメントに反映されていることがわかります。 /** * 色 */ color?: "basic" | "primary" | "error"; storybook 実装方法は以上です。 必要に応じて、アドオンを追加するとよいでしょう。 SDD(Storybook Driven Development)のすゝめ Storybookとは? では Storybook をUIカタログとご紹介しましたが、 Storybook をUIの開発環境と捉えることもできます。 UI コンポーネント をアプリケーションから切り離された環境( Storybook 上)で開発することには以下のようなメリットがあると思います。 UI コンポーネント の粒度や責務を適切に保つことができる Storybook 上で開発することで、個々の コンポーネント を独立して開発することになります。親 コンポーネント との依存関係を断ち切ることで、対象の コンポーネント の責務を明確にできます。 開発者が並行して作業を進めやすい 大きな粒度の コンポーネント から着手し、徐々に小さな粒度の コンポーネント を実装するといった トップダウン のアプローチではなく、 ボトムアップ で開発を進めることができます。親 コンポーネント の完成を待たず、子 コンポーネント の実装に着手できるため、分担して作業を進めることができます。 皆さんもよければ、 Storybook 上で開発してみてください。 最後に いかがでしたか? Storybook って面白いなと思っていただけたら嬉しいです! では、よい Storybook 生活を! 執筆: @takigawa.akihiro 、レビュー: @shibata.takao ( Shodo で執筆されました )
アバター
こんにちは。ISID コミュニケーションIT事業部 瀧川亮弘(あきひろ)です。 プロジェクトにて Storybook を利用しています。 Storybook の概要と基本的な実装方法をお伝えできればと思います。 Storybookとは? 一言でいうとUIのカタログです。また、それを作成するためのライブラリです。 Storybook を利用することで、 GUI 上でUI コンポーネント のデザインや振る舞いを簡単に確認できます。 百聞は一見にしかずということで、普段大変お世話になっている VSCode の Storybook のリンクを貼っておきます。良かったら触ってみてください! Microsoft VSCode Webview UI Toolkit Storybookの実装 UI コンポーネント を Storybook 上で表示するために .stories.js という拡張子のファイルを作成する必要があります。 本章では、こちらのファイルの基本的な実装について記載します。 尚、いくつか存在する記法のうち、 CSF(Component Story Format) という記法を採用します。以前まで主流だった、 storiesOf 関数や add 関数を利用する storiesOf API は現在非推奨となっています。 余談ですが、筆者は storiesOf API で記載された数十のファイルを CSF に書き換える作業をしたことがあります。個々の作業は簡単でしたが、数が数だけにとても苦労した懐かしい思い出があります。 対象の コンポーネント 対象とする コンポーネント として、簡単なボタンを用意しました。 愛着が湧くように自身の名前をつけて AkiButton と名付けました。 Vue3 の script setup 構文を利用しています。 <template> <button class="button" :class="[getColor]" @click="handleClick"> <slot /> </button> </template> <script setup lang="ts"> import { computed } from "vue"; interface Props { /** * 色 */ color?: "basic" | "primary" | "error"; } const props = withDefaults(defineProps<Props>(), { color: "basic", }); interface Emits { /** * クリックイベント */ (e: "click"): void; } const emit = defineEmits<Emits>(); const handleClick = () => { emit("click"); }; const getColor = computed<string>(() => `button__color--${props.color}`); </script> <style lang="scss" scoped> .button { height: 30px; padding: 0 16px; cursor: pointer; &__color { &--basic { background-color: $color__primary-light; border-color: $color__primary-light; color: $color__primary; } &--primary { background-color: $color__primary; border-color: $color__primary; color: $color__white; } &--error { background-color: $color__error; border-color: $color__error; color: $color__white; } } } </style> 実装 完成形 まずは完成形をお見せします。 次章から順を追って実装を確認します。 import AkiButton from "./AkiButton.vue"; export default { title: "Atoms/AkiButton", component: AkiButton, argTypes: { color: { control: { type: "select", options: ["basic", "primary", "error"], }, }, click: { action: "click", }, }, }; const Template = (args, { argTypes }) => ({ components: { AkiButton }, setup() { return { ...args, }; }, template: ` <AkiButton :color="color" @click="click">あきボタン</AkiButton> `, }); export const Basic = Template.bind({}); Basic.args = { color: "basic", }; export const Primary = Template.bind({}); Primary.args = { color: "primary", }; export const Error = Template.bind({}); Error.args = { color: "error", }; メタデータ の定義 メタデータ を定義します。 title には、メニューに表示される名前を定義します。 /(スラッシュ) で階層を切ると、 GUI 上ツリー表示されます。ちなみに、現在のプロジェクトでは Atomic Design に基づいて階層を切っています。 component には、 対象のコンポーネント で用意した コンポーネント を指定しています。 これらを指定したオブジェクトをデフォルトエクスポートすることで メタデータ として認識してくれます。 import AkiButton from "./AkiButton.vue"; export default { title: "Atoms/AkiButton", component: AkiButton, }; テンプレートの定義 テンプレートを定義します。 次章でいくつかのストーリーを定義しますが、それらのストーリーが共通して利用するテンプレートとなります。各ストーリーで利用する関数なので、テンプレート自体の export は必要ありません。 テンプレートの関数は、引数として各ストーリーごとに定義されたプロパティを受け取ります。ここでは args という名前で受け取ったプロパティを対象 コンポーネント に渡しています。 const Template = (args, { argTypes }) => ({ components: { AkiButton }, setup() { return { ...args, }; }, template: ` <AkiButton :color="color" @click="click">あきボタン</AkiButton> `, }); ストーリーの定義 Storybook のメインであるストーリーを定義します。 コンポーネント を表示するパターン、テストするパターンなどを考慮してストーリーを表現します。ここでは、 color プロパティの値ごとに、ストーリーを作成しています。 テンプレートの定義 で用意したテンプレートの関数をもとにいくつかのストーリーを作成するのが便利です。 ストーリーは関数で表現し、名前付きエクスポートします。 ここでつけた名前がデフォルトではストーリー名となります。 export const Basic = Template.bind({}); Basic.args = { color: "basic", }; export const Primary = Template.bind({}); Primary.args = { color: "primary", }; export const Error = Template.bind({}); Error.args = { color: "error", }; アドオンを利用する アドオンとはStorybookの拡張モジュールのことです。 今回はStorybookが公式でサポートしている3つのアドオンを利用してみます。 Controlsアドオン Controls アドオンにより、 コンポーネント に対するプロパティを GUI 上で動的に切り替えることができます。 argTypes: { color: { control: { type: "select", options: ["basic", "primary", "error"], }, }, }, Actionsアドオン Actions アドオンにより、発火されたイベントをロギングします。 各イベントが発火するタイミングやイベントの内容を確認できます。 argTypes: { click: { action: "click", }, }, Docsアドオン Docs アドオンは、 ソースコード を解析しドキュメントを自動生成してくれます。 Vueファイルに記載したコメントがドキュメントに反映されていることがわかります。 /** * 色 */ color?: "basic" | "primary" | "error"; storybook 実装方法は以上です。 必要に応じて、アドオンを追加するとよいでしょう。 SDD(Storybook Driven Development)のすゝめ Storybookとは? では Storybook をUIカタログとご紹介しましたが、 Storybook をUIの開発環境と捉えることもできます。 UI コンポーネント をアプリケーションから切り離された環境( Storybook 上)で開発することには以下のようなメリットがあると思います。 UI コンポーネント の粒度や責務を適切に保つことができる Storybook 上で開発することで、個々の コンポーネント を独立して開発することになります。親 コンポーネント との依存関係を断ち切ることで、対象の コンポーネント の責務を明確にできます。 開発者が並行して作業を進めやすい 大きな粒度の コンポーネント から着手し、徐々に小さな粒度の コンポーネント を実装するといった トップダウン のアプローチではなく、 ボトムアップ で開発を進めることができます。親 コンポーネント の完成を待たず、子 コンポーネント の実装に着手できるため、分担して作業を進めることができます。 皆さんもよければ、 Storybook 上で開発してみてください。 最後に いかがでしたか? Storybook って面白いなと思っていただけたら嬉しいです! では、よい Storybook 生活を! 執筆: @takigawa.akihiro 、レビュー: @shibata.takao ( Shodo で執筆されました )
アバター
X イノベーション 本部 ソフトウェアデザインセンター セキュリティグループの福山です。 AWS セキュリティの認定資格である「 AWS Certified Security - Specialty(SCS-C01)」を受験し、合格しました。 この資格を取得することで、 AWS クラウド におけるデータやワークロードのセキュリティ確保に関する 専門知識を 保有 することが認定されます。 今後受験する方のお役に立てればと思い、取り組んだことを共有します。 受験するきっかけ 私は2022年3月にISIDに入社しました。全社横断的にセキュリティを推進していくチームで、 クラウド 環境のSOC、IRという役割をいただいています。 まずは当資格を取得することで、 AWS のセキュリティを網羅的に理解することを目標としました。 前提知識 前職はインフラエンジニアで、 AWS のインフラ構築・保守の経験が2~3年 AWS SAAは取得済み(1回目は不合格、2回目は732点で何とか合格) 現在は業務で Security Hub、GuardDuty等のセキュリティ系サービスを頻繁に触っている 勉強方法 勉強期間は2ヶ月程度を要しました。以下に勉強の流れを記載します。 ➀試験内容を把握する 試験ガイドに目を通す  試験範囲は170分、合格ラインは750点となっています。  ⇒ 試験ガイド AWS 公式ト レーニン グを試す  こちら無償です(要ログイン)。各章と最後に練習問題があり、試験難易度もざっくり把握できるのでおすすめです。  ⇒ Exam Readiness: AWS Certified Security - Specialty (Japanese) 日本語実写版 AWS 公式のサンプル問題を解く  ⇒ AWS 認定セキュリティ — 専門知識 AWS Certified Security – Specialty (SCS-C01) 認定試験の質問例 ②各 AWS サービスとベストプ ラク ティスを理解する 参考書を購入する  ⇒ 要点整理から攻略する『AWS認定 セキュリティ-専門知識』  タイトルの通り、要点が整理されて、非常にわかりやすい内容になっております。 IAMとKMSのBlackbeltを閲覧する  上記2サービスは頻出するとの情報が散見されるため、BlackBeltを閲覧しました。  ⇒ BlackBelt 主なサービスをおさえる  各サービスの概要、他サービスとの関連性、ベストプ ラク ティスをおさえます。 ■分野1:インシデントへの対応 ・Security Hub、Config ・GuardDuty、Detective ・ Amazon Macie ■分野2:ログ記録とモニタリング ・CloudWatch Events、 SNS ・S3、CloudTrail ・ Kinesis 、 OpenSearch Service ■分野3:インフラスト ラク チャのセキュリティ ・SG、NACL ・ AWS WAF、Shield、CloudFront ・ AWS Artifact ■分野4: アイデンティティ 管理とアクセス管理 ・IAM(頻出) ・Organizations ■分野5:データ保護 ・KMS(頻出) ・ AWS Config ・Secrets Manager、Parameter Store  また、以下ドキュメントには、 AWS の定めるSecurity Hubのセキュリティ基準が記載されており参考になります。   ⇒ AWS Foundational Security Best Practices ③試験を申し込む 以下サイトからログインの上、試験を申し込む  試験の1ヶ月前に申し込みました。  なお、ISIDの社内制度で、受験料は補償されました。  ⇒ 試験のスケジュールを立てる ④問題を解く Udemyを受講する  以下のコースを受講しました。ISIDには、Udemy Businessを利用できる社内サービスがあるため、  無償で受講することができました。  なお、こちらのコースはボリュームが多いため、まずは各章の練習問題を実施し、  間違えた箇所は本編を視聴して消化するスタイルで進めました。  ⇒ AWS Certified Security – Specialty SCS-C01 AWS 公式の模擬試験を受ける  こちら無償になっています(要ログイン)。  ⇒ AWS Certified Security - Specialty Official Practice Question Set (SCS-C01 - Japanese) 時間の許す限り、①②③( AWS 公式ト レーニン グ、サンプル問題、参考書、Udemy、模擬試験)の練習問題を繰り返す 試験を終えて 結果は767点と合格ラインギリギリでした。。 点数を落とした要因は、IAMポリシーやKMSキーポリシーの記述方法を問われる問題が頻出し、 自信を持って解けなかったことが大きかったと思われます。 また、クロスアカウントやサービスへのアクセスに必要な記述内容なども理解しておくと良いと思いました。 次回更新時は上記を重点的に学習したいと思います。 最後に、参考書やブログで散見される、IAMユーザーにMFAを強制するIAMポリシーの記述方法を紹介して、 締めとさせていただきます。 それでは、行ってらっしゃい!! {    "Sid": "DenyAllExceptListedIfNoMFA",    "Effect": "Deny",    "NotAction": [       "iam:CreateVirtualMFADevice",       "iam:DeleteVirtualMFADevice",       "iam:EnableMFADevice",       "iam:GetUser",       "iam:ListMFADevices",       "iam:ListVirtualMFADevices",       "iam:ResyncMFADevice",       " sts :GetSessionToken"    ],    "Resource": "*",    "Condition": {       "BoolIfExists": {          " aws :MultiFactorAuthPresent": "false"       }    } } 参考: MFAで認証されたIAM ユーザーが「My Security Credentials」ページで自分の MFA デバイスを管理できるようにする     IAMユーザにMFA設定を強制するにあたりiam:ListUsersが必須では無くなった話 私たちは同じチームで働いてくれる仲間を大募集しています!たくさんのご応募お待ちしています。 - セキュリティエンジニア(セキュリティ設計) 執筆: @fuku.dancho 、レビュー: @sato.taichi ( Shodo で執筆されました )
アバター
X イノベーション 本部 ソフトウェアデザインセンター セキュリティグループの福山です。 AWS セキュリティの認定資格である「 AWS Certified Security - Specialty(SCS-C01)」を受験し、合格しました。 この資格を取得することで、 AWS クラウド におけるデータやワークロードのセキュリティ確保に関する 専門知識を 保有 することが認定されます。 今後受験する方のお役に立てればと思い、取り組んだことを共有します。 受験するきっかけ 私は2022年3月にISIDに入社しました。全社横断的にセキュリティを推進していくチームで、 クラウド 環境のSOC、IRという役割をいただいています。 まずは当資格を取得することで、 AWS のセキュリティを網羅的に理解することを目標としました。 前提知識 前職はインフラエンジニアで、 AWS のインフラ構築・保守の経験が2~3年 AWS SAAは取得済み(1回目は不合格、2回目は732点で何とか合格) 現在は業務で Security Hub、GuardDuty等のセキュリティ系サービスを頻繁に触っている 勉強方法 勉強期間は2ヶ月程度を要しました。以下に勉強の流れを記載します。 ➀試験内容を把握する 試験ガイドに目を通す  試験範囲は170分、合格ラインは750点となっています。  ⇒ 試験ガイド AWS 公式ト レーニン グを試す  こちら無償です(要ログイン)。各章と最後に練習問題があり、試験難易度もざっくり把握できるのでおすすめです。  ⇒ Exam Readiness: AWS Certified Security - Specialty (Japanese) 日本語実写版 AWS 公式のサンプル問題を解く  ⇒ AWS 認定セキュリティ — 専門知識 AWS Certified Security – Specialty (SCS-C01) 認定試験の質問例 ②各 AWS サービスとベストプ ラク ティスを理解する 参考書を購入する  ⇒ 要点整理から攻略する『AWS認定 セキュリティ-専門知識』  タイトルの通り、要点が整理されて、非常にわかりやすい内容になっております。 IAMとKMSのBlackbeltを閲覧する  上記2サービスは頻出するとの情報が散見されるため、BlackBeltを閲覧しました。  ⇒ BlackBelt 主なサービスをおさえる  各サービスの概要、他サービスとの関連性、ベストプ ラク ティスをおさえます。 ■分野1:インシデントへの対応 ・Security Hub、Config ・GuardDuty、Detective ・ Amazon Macie ■分野2:ログ記録とモニタリング ・CloudWatch Events、 SNS ・S3、CloudTrail ・ Kinesis 、 OpenSearch Service ■分野3:インフラスト ラク チャのセキュリティ ・SG、NACL ・ AWS WAF、Shield、CloudFront ・ AWS Artifact ■分野4: アイデンティティ 管理とアクセス管理 ・IAM(頻出) ・Organizations ■分野5:データ保護 ・KMS(頻出) ・ AWS Config ・Secrets Manager、Parameter Store  また、以下ドキュメントには、 AWS の定めるSecurity Hubのセキュリティ基準が記載されており参考になります。   ⇒ AWS Foundational Security Best Practices ③試験を申し込む 以下サイトからログインの上、試験を申し込む  試験の1ヶ月前に申し込みました。  なお、ISIDの社内制度で、受験料は補償されました。  ⇒ 試験のスケジュールを立てる ④問題を解く Udemyを受講する  以下のコースを受講しました。ISIDには、Udemy Businessを利用できる社内サービスがあるため、  無償で受講することができました。  なお、こちらのコースはボリュームが多いため、まずは各章の練習問題を実施し、  間違えた箇所は本編を視聴して消化するスタイルで進めました。  ⇒ AWS Certified Security – Specialty SCS-C01 AWS 公式の模擬試験を受ける  こちら無償になっています(要ログイン)。  ⇒ AWS Certified Security - Specialty Official Practice Question Set (SCS-C01 - Japanese) 時間の許す限り、①②③( AWS 公式ト レーニン グ、サンプル問題、参考書、Udemy、模擬試験)の練習問題を繰り返す 試験を終えて 結果は767点と合格ラインギリギリでした。。 点数を落とした要因は、IAMポリシーやKMSキーポリシーの記述方法を問われる問題が頻出し、 自信を持って解けなかったことが大きかったと思われます。 また、クロスアカウントやサービスへのアクセスに必要な記述内容なども理解しておくと良いと思いました。 次回更新時は上記を重点的に学習したいと思います。 最後に、参考書やブログで散見される、IAMユーザーにMFAを強制するIAMポリシーの記述方法を紹介して、 締めとさせていただきます。 それでは、行ってらっしゃい!! {    "Sid": "DenyAllExceptListedIfNoMFA",    "Effect": "Deny",    "NotAction": [       "iam:CreateVirtualMFADevice",       "iam:DeleteVirtualMFADevice",       "iam:EnableMFADevice",       "iam:GetUser",       "iam:ListMFADevices",       "iam:ListVirtualMFADevices",       "iam:ResyncMFADevice",       " sts :GetSessionToken"    ],    "Resource": "*",    "Condition": {       "BoolIfExists": {          " aws :MultiFactorAuthPresent": "false"       }    } } 参考: MFAで認証されたIAM ユーザーが「My Security Credentials」ページで自分の MFA デバイスを管理できるようにする     IAMユーザにMFA設定を強制するにあたりiam:ListUsersが必須では無くなった話 私たちは同じチームで働いてくれる仲間を大募集しています!たくさんのご応募お待ちしています。 - セキュリティエンジニア(セキュリティ設計) 執筆: @fuku.dancho 、レビュー: @sato.taichi ( Shodo で執筆されました )
アバター
こんにちは。ISID CIT事業部の熊倉です。 昨年7月、 Google が提供しているBIツール「Looker」でExtension Frameworkという新しい機能が追加されました。 今回の記事ではExtension Frameworkの開発方法について検証する目的で作成したアプリケーションについて紹介していきます。 本記事がExtension Frameworkの開発イメージやメリットについての理解の手助けになれば幸いです。 1. Lookerとは? 2. Extension Frameworkとは? 2.1 Extension Frameworkの利用例 3. Extension Frameworkの開発方法 3.1 開発に必要なスキル 3.2 ライブラリ/ツール 4. アプリケーションを作成してみた 4.1 想定したユースケース 4.1.1 LINEWORKSについての紹介 4.1.2 スキーマ 4.2 アーキテクチャ 4.3 作成したアプリケーションの紹介 4.3.1 CRM顧客情報を参照 4.3.2 LINEWORKS顧客情報とCRM顧客情報を連携 4.2 実際に作成してみての所感 5. まとめ 1. Lookerとは? 「Looker」は Google Cloudが提供しているBIサービスです。 Lookerは純粋な分析機能の他に 様々なデータ活用の基となるデータプラットフォームとして利用できる機能 が多く提供されており、他のBIサービスにはない特徴があります。 具体的には、 豊富な API GUI で操作できること(もしくはそれ以上のこと)が API から実行可能 外部アプリケーションに分析機能を埋め込める「組み込み分析」機能の提供 Lookerの ダッシュ ボードや分析画面を外部アプリケーションに組み込むことができます Looker Action 分析して得た結果や条件に応じて、Action Hubと呼ばれるサーバを経由して外部サービスにデータを連携できます。 連携できる外部サービス(TwilioやSlack等々)は数十種類公開されております。 Teamsのアクションについては自分が作成した経緯があったりします :) 等々の機能が提供されています。     データベースと接続したLookerを通して、分析(Modern BI & Analytics)、既存の分析との融合(Integrated Insight)、データを起点としたワークフローの実行(Data-driven Workflows)、データアプリケーションの作成(Custom Application)を実現できます。 (引用: Advanced Data Analytics Platform ) 2. Extension Frameworkとは? Extension Frameworkとは 2021年7月22日に発表された Looker上で JavaScript のアプリケーションを ホスティング できる機能です。 前述しているように、Lookerは以前より外部連携機能( API 連携、組込み機能)が充実していました。 しかし、アプリケーションを開発する際には(当然ですが)インフラの用意やネットワークの設定が必要で、開発者は機能開発以外に環境構築を行うハードルがありました。 Looker環境上でアプリケーションが実行できるExtension Frameworkの登場で、そのようなインフラの構築をLookerが担保してくれるようになり、開発者がより機能の開発に集中できるようになりました。 (引用: 拡張フレームワークを使用して Looker での構築を簡単にする ) Extension Frameworkでは以下のようなことが可能になっております。 外部 API へのリク エス ト(情報の取得/更新) データベースにないデータの取得、表示 Lookerの分析機能の埋め込み サードパーティ のライブラリを利用 標準で表現できない描写( ガントチャート 、ツリー表示)   また、Looker上で ホスティング されることによる利点も享受でき、 ホスティング したアプリケーションは Lookerのデフォルトの認証機能( LDAP や SAML 、OIDCなど)を利用したアプリケーションの認証 Lookerの権限管理機能を利用したアプリケーションの利用制御 といったことが利用可能です。 2.1 Extension Frameworkの利用例 Extension Frameworkを利用する事で、Lookerの分析で得た知見を次のアクションにつなげることができたり、通常の ダッシュ ボードでは表現が難しかったリッチなビジュアライズの提供ができます。 BIによる分析結果を利用したデータの更新 Extension Frameworkから外部の API を叩く事で、BIによる分析結果を次のアクションにつなげることが可能です Lookerに統合されたデータベースのマスタを管理 することも可能に アプリケーション例)予実管理アプリケーション 予算マスタをExtension Frameworkで管理。Looker上で分析から管理まで業務を完結できる。 リッチなビジュアライズの提供 Lookerがデフォルトで表現できない描写( ガントチャート やツリー表示等)も JavaScript のライブラリをExtension Frameworkにインポートすることで描写が可能です 例えば特定の ユースケース に絞った ダッシュ ボードを作成したり、フィルター機能をマスクし分析をシンプルかつ簡単に分析するようなアプリケーションが作成可能です アプリケーション例)顧客シングルビュー Extension Frameworkから外部 API を叩くことで既存のアプリケーションから情報を取得し、 マッシュアップ 的にLookerの分析内容と既存アプリケーションを統合した顧客シングルビューを構築 また、Action連携などLookerの外部連携機能も組み合わせることでCDP(カスタマー・データ・プラットフォーム)として利用可能 3. Extension Frameworkの開発方法 この章では具体的にExtension Frameworkを利用した開発を行う際にどのようなスキルが必要になるのか、また、 Google から提供されているライブラリ/ツールについて紹介をします。 3.1 開発に必要なスキル Extension Frameworkは JavaScript (or TypeScript)を使用し開発します。 また、アプリケーション構築に必要な SDK も提供されております。純粋な JavaScript のライブラリとして提供されている他に、それらをReactから利用できる形でラップした SDK も提供されています。 開発スキルのパターンをまとめると、 JavaScript + raw JavaScript SDK TypeScript + raw JavaScript SDK JavaScript + React SDK TypeScript + React SDK から選択することとなります。 JavaScript / TypeScriptの選択は好き嫌いがあると思いますので自由に選択していただいて良いかと思います。 Reactを利用するかどうかについてはLookerからReact用のUI コンポーネント が提供されていたり、参照できるサンプルの数に差があったりするため Reactの利用をお勧めします 。 (と言ってもスキルセットの問題もあると思うので、最終的には自由に選択して問題ないです) 3.2 ライブラリ/ツール Extension Frameworkを利用したアプリケーションを開発するために、公式から以下のようなライブラリ/モジュールが用意されています。 Looker Extension SDK Extension FrameworkからLooker環境内部へアクセスするインターフェースとして SDK が提供されています。 SDK を通してLooker public API にアクセスし、 API 経由でLooker環境内部とやり取りをします。 Reactに最適化されたライブラリ も提供されています。 Looker components Reactで動作するUI コンポーネント ライブラリです この後に紹介する作成したアプリケーションでは、こちらの コンポーネント は使用せずに MUI を利用しました。 Embed SDK Extension Frameworkを利用して構築したアプリケーション内でLookerのLookや Explorer 、 Dashboard を表示するためにはEmbeded SDK 経由で描写を行います。 また、Extension Framework自体もEmbeded SDK を利用することで外部のWebサイトから参照することが可能とのことです(やってみたい) create-looker-extension utility よくある対話形式で環境構築してくれる便利ツールです。 コンソールから yarn create looker-extension または( npx create-looker-extension )で利用可能です。 Looker extension framework examples Google が用意してくれているサンプル集です。Extension Frameworkについて参照できる情報が少ないため、実際に開発してく際はサンプルを参照し、開発していくことになると思います。 本記事では具体的な開発方法について詳細を記述しませんが、より詳しく知りたい方は以下のブログの内容等が参考になるかと思います。 Extension Frameworkを利用してPDTの依存関係を表示させる 4. アプリケーションを作成してみた 開発方法について検証する目的で実際にExtension Frameworkを利用したアプリケーションを開発してみました。 4.1 想定した ユースケース アプリケーションを作成する際の題材としてLINEとつながる唯一のビジネスチャットツール『LINE WORKS』を今回は採用し、 LINE WORKSの顧客情報と CRM の顧客情報を連携できる アプリケーションをExtension Frameworkで作成しました。 アプリケーションの紹介の前にまず題材として取り上げたLINE WORKSについて紹介をします。 4.1.1 LINEWORKSについての紹介 ビジネスチャットツール「LINE WORKS」はLINEユーザとコミュニケーションできる「LINE連携機能」が提供されており、 特に小売業で顧客のLINEとのやり取りで「LINE連携機能」が活用されています。 https://line.worksmobile.com/jp/solutions/retail/ 利用することで スタッフや営業(LINE WORKSユーザ) と お客さん(LINEユーザ) との間で交わされた トーク 内容や顧客の情報を管理できます。 ただし、LINE WORKSの顧客情報は独立して管理されている為、もし既存の CRM の顧客情報と一元的に管理したいとなった場合、ユーザがある程度情報を確認しながら実際に紐づけ操作を行う必要があります。 今回は CRM で管理している顧客情報とLINE WORKS上で管理されている顧客情報を参照し、それらの紐づけを管理できるアプリケーションを開発しました。 4.1.2 スキーマ DBの スキーマ として以下のような構造を想定しました。 user LINEWORKSのユーザ(上記のスタッフや営業) LINE WORKSの API から取得できる情報を基にカラムを定義 https://developers.worksmobile.com/jp/reference/user-list?lang=ja lw_contact LINEWORKSの顧客情報 LINE WORKSの API から取得できる情報を基にカラムを定義 https://developers.worksmobile.com/jp/reference/contact-list?lang=ja talk LINEWORKS上で交わされたコミュニケーション内容 LINE WORKSの API から取得できる情報を基にカラムを定義 https://developers.worksmobile.com/jp/reference/audit-log-download?lang=ja contact CRM で管理している顧客情報 カラムについては特定のサービスを意識せず想定で用意しました。 lw_contact_link contact と lw_contact を紐付けるレコードが格納 実際に紐付けを管理しているのは lw_contact_link テーブルです。 アプリケーションについて 「 CRM の顧客情報とLINE WORKSの顧客情報を連携する」 アプリケーションと紹介しましたが、言い換えれば 「 contact 、 lw_contact の内容を参照し、 lw_contact_link のレコードを更新できる」 アプリケーションを今回作成したことになります。 4.2 アーキテクチャ アプリケーション全体の アーキテクチャ を紹介します。 今回のアプリケーションではLookerに接続するDWHとしてBigQueryを選択しました。 まず、LookerとBigQueryを接続し、あらかじめLook(テーブル / ビジュアライゼーションをレポートとして保存したもの)を作成しています。 そのLookをExtension Frameworkの SDK 経由で取得し、アプリケーションに表示するという構成を取っております。 (図の「テーブル取得」→「Lookの取得」→「LINEWORKSと CRM の顧客情報を参照」の部分) 更新する際は、Extension Framework( JavaScript )側でBigQueryにて実行したいクエリを作成し、Lookerと既に接続しているコネクションを利用し、テーブルにUpdateをかけています。 今回のアプリケーションでは直接BigQueryに対してUpdateするクエリを実施するのではなく、Lookerを経由してデータの更新を行っています。 Extension Frameworkでは外部に対して API を叩くこともできますので、例えばBigQueryのクライアントライブラリをExtension Framework側でインポートし直接BigQueryに対して API を叩くといった設計も可能です。 今回はデータの保存場所としてBigQuery ストレージのみを使用していますが、例えばデータ取り込み元としてGCSを利用している想定をし、GCSに対してデータの更新を行うといった設計ももちろん可能です。 4.3 作成したアプリケーションの紹介 アプリケーションは以下の操作フローを想定し、作成を行いました。 CRM 顧客の一覧が表示される 一覧から1人を選択する CRM 顧客詳細画面が表示される CRM 顧客詳細画面から『LINEWORKSユーザ紐付け』ボタンをクリック。紐付けモーダルが表示される。 LINEWORKSユーザの一覧が表示される LINEWORKSユーザを1名選択し、 CRM 顧客と連携(または連携解除)をする 以下、アプリケーションの機能についてアニメーションを使用しながら紹介します。 4.3.1 CRM 顧客情報を参照 (動画では、上記フローの1,2,3まで実施しています) 左のサイドメニューから「Contact」を選択する事で顧客の一覧が表示され、その中で一名を選択するとその詳細画面に遷移します。 一覧で表示している情報や詳細画面の「Profile Information」で表示している情報は全てあらかじめ作成したLookから情報を取得しているため、「一覧に表示させるカラムを変更したい」「順番を変更したい」という場合は元のLookを編集する事で変更可能です。 4.3.2 LINEWORKS顧客情報と CRM 顧客情報を連携 (動画では上記フローの4,5,6まで実施しています) 顧客情報詳細のヘッダーにある『LINEWORKSユーザ紐付け』ボタンを押下する事で、モーダルが表示されます。 モーダルではLINE WORKSの顧客情報一覧が確認でき、行右側の「連携」(または連携解除)ボタンを押す事で「参照していた CRM の顧客情報」と「LINEWORKSの顧客情報」が連携されます。 アプリケーションの機能紹介は以上になります。 4.2 実際に作成してみての所感 開発ではTypeScript + Reactを利用しました。 開発した所感として通常のReact開発と同じ感覚で開発できると感じました。 フレームワーク 特有の記述方法も多少はありますが基本的に自由に開発できるかと思います。 React Routerによるルーティングも可能ですし、(今回の開発では使用していませんが)Reduxによる状態管理も可能です。 開発に必要なスキルセットとして JavaScript / TypeScript / Reactの知識の他に、Looker API の知識が必要となりますが、 API を実際の環境のリソースに対して GUI から叩くことができるアプリケーション『 API Exploer 』が公式で用意されていたりするのでLookerの仕様に明るくない方でも十分開発ができるのではないかと思います。 5. まとめ 以上のように、Extension Frameworkを利用することでLookerの集計/分析機能を利用したアプリケーションをインフラ構築といった作業を経ずに簡単に素早く実装できます。 今回紹介したアプリケーションのように、ある意味汎用的に作られているパッケージ製品では実現が難しかった特別な要件 / 業種/ 職種に最適化されたアプリケーションを構築できます。 Extension Frameworkの登場で「データの集計/分析(ビジュアライズ)」から「意思決定」といった データドリブンなプロセス を Looker環境上で完結 して提供できるようになりました。 「データドリブンを実現したいが、具体的な方法がわからない」といった場合にLookerとExtension Frameworkは1つの回答になるのではないかと思います。 最後までお読みいただきありがとうございました。 執筆: @kumakura.koki.isid 、レビュー: @sato.taichi ( Shodo で執筆されました )
アバター