TECH PLAY

電通総研

電通総研 の技術ブログ

836

電通国際情報サービス 、オープン イノベーション ラボの 比嘉康雄 です。 Stable Diffusionシリーズ、今回のテーマは、Stable Diffusion 2.1-金髪美女写真です。 Stability AI が Prompt Bookという呪文の解説スライド を出しているので、それを研究します。今回は、 Prompt Book の Photorealism (写真のようなリアルな描写法)の金髪美女の呪文を解説します。解説スライドでは、14, 15ページの内容になります。 Prompt Book は更新が入っているようなので、最新版では、内容が変わっている可能性もありますが、本質的な部分は変わっていないはずです。 Stable Diffusionのおすすめコンテンツはこちら。 Waifu Diffusion 1.3.5_80000 v2.1 金髪美女写真 v2.1 美少女アニメ画 v2.1 AUTOMATIC1111 v2.0 美少女イラスト v1.5 美少女画検証 美少女アニメ画改善版 美少女を高確率で出す呪文 美少女アニメ画 美少女写真 女性イラスト 長い呪文は切り捨てられる AUTOMATIC1111のインストール AUTOMATIC1111のセッティング 金髪美女写真の出力結果 金髪美女写真の通常呪文 portrait of a blonde woman in a white dress posing for a picture tumblr digital art glowing with colored light ethereal lighting forest ray light taken in 2022 handsome girl brooke ashling diffused natural skin glow 金髪美女写真のネガティブ呪文 gingerbread candy village, on a plate in a busy diner ice - t drinking beer and laughing cartoon, 1990s cartoon black show room breaking bad scene animal shaped bread broken down grey wall cheese and pepperoni black hair duplicate 仲間募集 Stable Diffusionの全コンテンツ AUTOMATIC1111のインストール Stable Diffusion 2.1 を実行する環境として、 AUTOMATIC1111 を使います。 AUTOMATIC1111 は、 Stable Diffusion を Google Colab 直接ではなく、 UI 経由で実行できるようにしています。似たような機能のものはいくつかありますが、 AUTOMATIC1111 は人気が高く、情報量も多いのでおすすめです。 Google Colab で動かすための公式のノートブックは こちら になります。 このノートブックを実行すると、下の方に Running on public URL: https://2c76db068be0e79a.gradio.app のようなリンクが表示されるので、クリックしましょう。 AUTOMATIC1111のセッティング パラメータは以下のようになります。 Sampling Steps: 50 Sampling Method: DPM2 Width: 768 Height: 768 CFG Scale: 7.5 Seed: -1 Sampling Stepsは、デフォルトの 20 だと少ないので、 50 くらいにしておきましょう。 Sampling Methodは DPM2 をお勧めしますが、 DDIM も悪くはありません。 Width と Height は 768 をお勧めします。今回使っているモデルは、 768 用のためです。 512 だとかを使うと画像が崩れることがあります。 CFG Scale は入力した呪文にどれだけ近い画像を生成するかのパラメータです。デフォルトの 7 でも問題ありませんが、僕はなんとなく 7.5 を指定しています。 金髪美女写真の出力結果 金髪美女写真の出力結果の例です。 金髪美女写真の通常呪文 金髪美女写真の通常呪文は次のようになります。 portrait of a blonde woman in a white dress posing for a picture, tumblr, digital art, glowing with colored light, ethereal lighting, forest ray light, taken in 2022, handsome girl, brooke ashling, diffused natural skin glow 見やすいように改行すると次のようになります。 portrait of a blonde woman in a white dress posing for a picture, tumblr, digital art, glowing with colored light, ethereal lighting, forest ray light, taken in 2022, handsome girl, brooke ashling, diffused natural skin glow 個々の呪文を解説しましょう。 portrait of a blonde woman portrait of a blonde woman は、「金髪女性の 肖像画 」という意味です。 in a white dress in a white dress は、「白いドレスを着て」という意味です。 posing for a picture posing for a picture は、「写真撮影のためにポーズを取る」という意味です。正面から撮った棒立ちの写真はつまらないので、この呪文は覚えておきましょう。 tumblr tumblr は、 SNS の一つです。指定するとなんとなくクオリティが上がる気がする呪文です。たぶん、外してもそれほど違いはないと思います。 digital art digital art は、「デジタルアート」という意味です。たぶん、外してもそれほど違いはないと思います。 glowing with colored light glowing with colored light は、「色付きの光で輝く」という意味です。この効果は、金髪女性に影響します。 ethereal lighting ethereal lighting は、「優美な照明」の意味です。 forest ray light forest ray light は、「森の光」という意味です。木々の間から木漏れ日が見えるような効果になることが多いです。単なる forest よりずっと美しくなるので、この呪文は覚えておきましょう。 taken in 2022 taken in 2022 は、「2022年に撮られた」という意味です。たぶん、外してもそれほど違いはないと思います。 handsome girl handsome girl は、「キリッとして目鼻立ちの整った少女」の意味です。 brooke ashling brooke ashling は、人の名前のような気がしますが、なんの意味があるのかわかりません。たぶん、外してもそれほど違いはないと思います。 diffused natural skin glow diffused natural skin glow は、「分散した自然な肌の輝き」の意味です。 金髪美女写真のネガティブ呪文 通常呪文を打ち消すのがネガティブ呪文です。 金髪美女写真のネガティブ呪文は次のようになります。 gingerbread candy village, on a plate in a busy diner, ice - t, drinking beer and laughing, cartoon, 1990s cartoon, black show room, breaking bad scene, animal - shaped bread, broken down grey wall, cheese and pepperoni, black hair, duplicat 見やすいように改行すると次のようになります。 gingerbread candy village, on a plate in a busy diner, ice - t, drinking beer and laughing, cartoon, 1990s cartoon, black show room, breaking bad scene, animal - shaped bread, broken down grey wall, cheese and pepperoni, black hair, duplicate 個々の呪文を解説しましょう。 gingerbread candy village, on a plate in a busy diner 結構意味不明ですが、「繁盛している食堂の皿の上のお菓子」くらいの意味でしょうか。通常呪文とほとんど関係がないので、たぶん、外してもそれほど違いはないと思います。 ice - t これも意味不明ですが、「アイス ティー 」のことなのかもしれません。通常呪文とほとんど関係がないので、たぶん、外してもそれほど違いはないと思います。 drinking beer and laughing drinking beer and laughing は、「ビールを飲んで笑う」の意味です。通常呪文とほとんど関係がないので、たぶん、外してもそれほど違いはないと思います。 cartoon, 1990s cartoon cartoon は、「漫画」、 1990s cartoon は、「1990年代の漫画」の意味です。リアルな写真が目的なので、漫画の要素を打ち消しているのでしょう。 black show room black show room は、「黒い展示室」の意味です。通常呪文とほとんど関係がないので、たぶん、外してもそれほど違いはないと思います。 breaking bad scene breaking bad scene は、「ハメを外したシーン」の意味です。ハメを外したシーンを打ち消します。 animal animal は、「動物」の意味です。動物を打ち消します。 shaped bread shaped bread は、「成形されたパン」の意味です。通常呪文とほとんど関係がないので、たぶん、外してもそれほど違いはないと思います。 broken down grey wall broken down grey wall は、「壊れた灰色の壁」の意味です。たまに、背景が灰色になってしまうことがあるので、それを打ち消しているんだと思いますが、打ち消しきれていない気がします。 cheese and pepperoni cheese and pepperoni は、「チーズとパペロニ」の意味です。通常呪文とほとんど関係がないので、たぶん、外してもそれほど違いはないと思います。 black hair black hair は、「黒い髪」の意味です。金髪にするために、黒い髪を打ち消します。 duplicate duplicate は、「複製」の意味です。体のパーツが複製されるのを打ち消したいんじゃないかと思います。例えば、鼻が2つあるとか。 仲間募集 私たちは同じグループで共に働いていただける仲間を募集しています。 現在、以下のような職種を募集しています。 ソリューションアーキテクト AIエンジニア Stable Diffusionの全コンテンツ 人物写真編 レンズ編 画像タイプ編 美少女アニメ画編 美少女写真編 女性イラスト編 美しい夜空を見渡す男編 魅惑的な女アニメ画(トゥーンレンダリング)編 美少女を高確率で出す呪文編 長い呪文は切り捨てられる編 蒸気機関が高度に発達したレトロなアニメ(スチームパンク)の世界観編 A as Bの呪文による画像合成編 かわいい動物の擬人化編 バベルの塔のイラスト編 TPU版の使い方 美少女アニメ画改善版 v1.5 美少女画検証 東京タワーの写真 折り紙合体変形ロボ v2.0 美少女イラスト v2.1 AUTOMATIC1111 v2.1 美少女アニメ画 v2.1 金髪美女写真 Waifu Diffusion 1.3.5_80000 執筆: @higa 、レビュー: Ishizawa Kento (@kent) ( Shodo で執筆されました )
こんにちは、X(クロス) イノベーション 本部 ソフトウェアデザインセンター セキュリティグループ 新卒入社2年目の大西です。これは、 電通国際情報サービス Advent Calendar 2022 12/21の記事です。今日は、私が所属するセキュリティグループについて記事を書いてみました!配属されて1年余りですが、グループの仕事内容や雰囲気、魅力などをお伝えできればと思います。 どうしてセキュリティグループに入ったの?? どんな仕事をしているの?? やりがいや達成感は?? 大変なところは?? グループの雰囲気はどんなかんじ?? ブログを見てくださった方に向けて どうしてセキュリティグループに入ったの?? 昨年セキュリティグループに配属された際、いろんな人に「どうしてセキュリティの仕事がしたいの?」と聞かれました。私は、入社する前からセキュリティに興味があり、セキュリティに関わる仕事がしたいと思っていました。その理由は、いくつかあります。 セキュリティの仕事=必要な仕事 世の中にはいろんな便利なものがありますが、便利なだけでは人は満足しないと思います。便利さ以前に、「安心」がないと、誰もそれを使ってくれません。例えば、音楽のストリーミングサービスで、いろんな曲が定額で聴き放題だけど、クレジットカードなどの支払い情報が外部に ダダ漏れ だったら誰もそれを使いたいとは思いません。 どんな便利なアプリやシステムでも絶対に必要なもの...安心とかセキュリティとか...それらを守る仕事がしたいとずっと思っていて、それを新人の時に人事の方に伝え、無事セキュリティグループに配属されました!(ISIDは、できるだけ希望の部署に入れてあげたいという思いの強い会社だと思います。) セキュリティエンジニア、足りてない... ニュースで セキュリティインシデント を目にすることはたまにしかないかもしれませんが、 セキュリティインシデント は毎日起こっています。 Security Next というサイトでは、情報セキュリティに関するニュースを日刊で届けてくれるのですが、これを見ていると本当に毎日インシデントだらけだなぁと思います。しかし、それらインシデントに対応できるようなセキュリティエンジニアは不足していて、多くの企業で課題となっています。セキュリティ人材が足りていない中で、少しでも貢献したいという気持ちがありました。今年もセキュリティグループに新たに4人の方が採用されて、やはりニーズは高まっているんだなと感じます。 セキュリティエンジニア、かっこいい! 私がセキュリティに興味を持ち始めたのは、ハッキングをしている人たちをTVで見て、ハッキングって面白そう!と思ったところからでした。そこから、 IPUSIRON さんの 「ハッキング・ラボの作り方」 を読みながら簡単なハッキングをしてみたり、CTF(Capture The Flag)と呼ばれるセキュリティコンテストの問題を解いてみたりして、ますます面白いと思うようになりました。 ハッキングの知識を悪いことに使えば犯罪ですが、いいことに使えば世の中のいろんなシステムを攻撃から守れます。セキュリティエンジニアは、プログラミングの知識だけでなく、ネットワーク、暗号化・認証技術、 クラウド 技術など様々なことに精通している必要があります。システムやアプリに存在するセキュリティの穴をどう塞ぐか、いろんな知識を持ち寄って考えなければなりません。それが、大変ではあるけれど、とてもかっこいいなと感じます! どんな仕事をしているの?? 現在9名のメンバーがセキュリティグループに所属し、部門横断で システム開発 ・運用におけるセキュリティを確保する支援を行っています。SRB(Security Review Board), SOC(Security Operation Center), IRG(Incident Response Group)という3つのグループがあり、図にすると以下のような感じです。 これまではSRB中心の業務でしたが、今年SOCとIRGチームが立ち上がりました。それぞれのグループの業務内容については以下のとおりです。 SRB: ISIDの事業部が担当している案件においてセキュリティに関する懸念事項を確認し、リリース前までに修正した状態にします。具体的には、案件の提案段階や設計段階でセキュリティに関する懸念事項がないか確認したり、セキュリティツールを用いた検査を行ったりし、事業部側に改善案を提案します。 SOC: クラウド 環境のセキュリティ対策を可視化し、被害の拡大を防止して問題の早期検知を行います。各案件のセキュリティ設定基準に対する準拠状況を把握し、チェックした結果を案件担当に伝え、改善を進めていきます。脅威検知ではアラートに対して、案件担当と連携し問題調査を行います。また、案件担当者に対し、 クラウド 専門の部署と合同でセキュリティ勉強会を行ったりもします。 IRG: セキュリティインシデント が発生した際に、状況を把握して被害の拡大を防止したり、被害の範囲を特定するなど適切に事後対応を行います。また、インシデントが起きた原因を突き止めそれを教訓化することで、将来のインシデントのリスクを低減させます。 この中で私はSRBチームに所属しており、セキュリティツールを用いた 脆弱性 検査(動的検査)の担当しています(実は、担当になったばかりで先月キャッチアップを終え、実際に案件を担当するのはこれからです!)。他にも、社内向けWeb アプリ開発 や、新人研修のセキュリティ講義、また、ISIDで使用しているソフトウェアやハードウェアなどの 脆弱性 が公開されればそれを全社に通知する業務なども担当しています。来年からは、セキュリティツールでは見つけにくい 脆弱性 を発見するため、マニュアル検査が本格的に始まります。実際に擬似的な攻撃コードなどを投げ、返ってきたレスポンスを見て脆弱かどうか検査していくのですが、経験と専門性がより問われるので、これから頑張って技術を習得していきたいと思います! やりがいや達成感は?? 業務によってやりがいや達成感を感じる瞬間は違いますが、例えばWeb アプリ開発 では、一つの機能を作り終えた時にすごくやりがいを感じますし、新人研修のセキュリティ講義をしたときは、講義やハッキング演習を通して新人がセキュリティに興味を持ってくれた時にもすごくやりがいを感じました。 これから 脆弱性 検査を本格的に担当していくと、リリース前に 脆弱性 が見つけられた時などにも、 セキュリティインシデント を未然に防ぐことができて大きなやりがいを感じるのではないかと思います。セキュリティ要件の高い案件や大きな規模の案件のセキュリティレビューや 脆弱性 検査をすることもあり、金融機関や官公庁の案件を担当することもあります。それらの案件のシステムがもし サイバー攻撃 を受けると、影響を受ける範囲がとても大きくなるので、それを未然に防ぐ仕事というのはとてもやりがいがあるのではないでしょうか。 大変なところは?? 身に付けなければならない知識・スキルが多いことです。それは最初から覚悟していたことなのですが、覚えても覚えても新しい技術が出てくるので、心が折れそうになることもあります。しかし、一歩ずつやっていくしかないと思うので、毎日の業務で新しいことを覚えたり、資格の勉強をしたりして地道に知識を増やしていきます。資格の勉強は、体系的にセキュリティのことを学べるのでいいなと思います。ISIDは資格試験や研修の費用を会社がお金を出してくれるのでとてもありがたいです。資格勉強に限らずですが、勉強を進めていると、初め聞いた時はよくわからなかった技術も他に新しいことを覚えていくうちに、あ、あの時のあれはそういうことだったんだ!!みたいな、点と点が繋がる瞬間がたまにあって、自分の成長を感じる瞬間がとても嬉しいです。この仕事は、本当に日々勉強です。 グループの雰囲気はどんなかんじ?? セキュリティグループの雰囲気は、「個」というよりは「チーム」、「上下関係」というよりは「フラット」、「静か」というよりは「にぎやか」という感じです。みんなで助け合いという感じで、今は助けられていることの方が多いんですが、もっと力がついてきたら先輩や後輩を助けられるようになりたいです。年代は20代~50代と幅広いですが、思ったことは割と上の人にもぶつけている印象で、発言しやすいなと感じます。テレワークが中心でほとんど出社することはないですが、オンラインで雑談できたりするので、寂しくなることもありません。また、今はTypeScriptという言語の勉強会をグループ中心でやっていて、みんなで学ぼうという姿勢がいいなと思います。雰囲気ではないですが、残業時間は平均20時間くらいで少なめで、総合的に見てとても働きやすいです。 ブログを見てくださった方に向けて このブログ記事は学生に向けた働き方紹介ということだったんですが、もしかしたら学生以外の方も見てくださっているかもしれません。セキュリティってちょっと気になるな、という方がいたら、ぜひカジュアル面談などを受けてみてください。グループの雰囲気などはやはり実際にメンバーに会ってみないと分からなかったりするし、こちらももっとここに書ききれていない弊社の魅力を伝えられたらと思います。 学生の方に向けたメッセージとしては、自分がいちばん何をしている時に感動したり、またこれをやってみたい!と思うかを大事にしてほしいです。イメージで会社を選ぶのではなく、その感動したことを仕事にできる会社を選ぶと、仕事をしていても楽しいなと思うことが多いはずです。自分の例で言うと、プログラミングしたものが自分の思い通りに動くとすごく感動するし、またコードを書いてみたい!と思います。また、攻撃者の目線に立ってハッキングしてみようとデモアプリにハッキングしたり、この攻撃を防ぐには・・・?と考えだすと、面白い!!となります。会社のイメージではなく、仕事内容をぜひ重視して会社を選んでみてください。もし、ワクワクどきどきするのがセキュリティという分野だったら、ぜひ一緒に働きましょう!! www.isid.co.jp 中途の方向け:セキュリティエンジニア(セキュリティ設計) 執筆: @onishi.mayu 、レビュー: @yamada.y ( Shodo で執筆されました )
みなさんこんにちは! ISID 金融ソリューション事業部、新卒2年目の松崎です。 本記事は 電通国際情報サービス Advent Calendar 2022 12月 20日 の記事になります。 今回は、フォトグラメトリソフトウェアの RealityCapture と ゲームエンジン の UnrealEngine を使って 現実空間の物体から作成した3Dモデルを仮想空間に設置する手順 を紹介します。 はじめに 「 メタバース 」とは、どのようなものでしょうか。 語源を調べると、超越・高次を意味する「Meta」と世界を意味する「 Universe 」を組み合わせた造語であると分かります。 つまり、 超越した世界 と解釈できるわけですが、なかなか抽象的で理解し辛いですね。 実際のところ メタバース の正確な定義は定まっておらず、現状は「インターネット上に存在する3D仮想空間」と理解しておけば問題ないと考えています。 今回は、そんな メタバース の構築ツールであるUnrealEngineのマップに現実空間の物体を置き、リアルタイム レンダリング してみた! ということでその実施手順を紹介します。 手順 RealityCaptureを用いて、現実空間の物体から3Dモデルを作成する 作成した3DモデルをFBX形式でエクスポートし、UnrealEngineへインポートする UnrealEngineのマップ上に、3Dモデルを設置する 使用機材 PC(3Dモデル構築、仮想空間構築) OS:Windows10 Home CPU: Intel Core i9 -10900K   @3.70GHz  GPU : NVIDIA GeForce RTX 3080 RealityCapture 1.2.1 インストール済み UnrealEngine 5.1.0 インストール済み iPhone12mini(写真撮影) OS: iOS 15.6 ZY Cami 1.3.4(写真アプリ)インストール済み Zhiyun Smooth Q4( スマートフォン 用ジンバル、三脚) 手順1.現実空間の物体から3D モデルを作成 まず初めに、 RealityCapture を用いて3Dモデルを作成します。 RealityCapture は、 Epic Games 社が提供するフォトグラメトリソフトウェアです。 写真から3Dモデルを作成することを強みとしており、仕上がり品質の高さが特徴的です。 また、LiDARなどによるレーザースキャンデータとの組み合わせも可能で、地形の測量等にも活用されています。 スマートフォン 用無料アプリもリリースされましたので、ご興味ある方は是非お試しください! 本記事ではPC版RealityCaptureを使用し、 写真だけを用いて 3Dモデル作成を行います。 ①写真撮影 高精細な3Dモデルを作成する場合、インプットに使う写真の質が重要になります。 写真の質を上げる方法として、今回は以下の観点を意識しました。 撮影方法:対象物体を取り囲むように周回移動しながら撮影 画質:iPhone12mini 最高画質(12MP) 画像重なり:前後の画像にて、70%以上の重なりを維持(計140枚撮影) ブレ軽減:三脚とジンバルを用いて、撮影時のブレを最小化 距離:可能な限り対象物体に近づいて撮影(写真内で物体が占める ピクセル 数を最大化) また、カメラ設定は以下になります。 ISO 100 ズーム:等倍(1.0) 写真サイズ:2750 × 3664 (縦撮り) ②RealityCaptureで3Dモデル構築 撮影した写真をRealityCaptureへインポートし、3Dモデルを作成します。 2.1 写真を取込み、アライメントを実施(アライメント処理時間:1分) アライメントとは、オーバーラップ撮影された写真から、複数の写真の共通点を点として抽出し、低密度の点群データを作成する処理になります。同時にカメラの位置や対象物との距離を計算しており、下記画像の白い小さな四角形は、写真1枚1枚の撮影位置推定結果を示しています。 今回は三脚を用いて高さ固定で撮影をしていますが、RealityCapture上においても高さが一定であると推定されています。 2.2 コン トロール ポイントを用いて距離を定義し、再アライメント 最初のアライメント時に物体の大きさが自動定義されますが、ズレが大きい時は自分で再定義します。 再定義の際はコン トロール ポイントを利用します。コン トロール ポイントとは、写真上に目印となるポイントを設定できる機能です。アライメント処理で写真同士のオーバーラップ判定が上手く行かなかった時などに、写真間の共通ポイントをコン トロール ポイントとして指定することで、オーバーラップ判定の手助けを行うことができます。 また、コン トロール ポイントを2つ設定し、2点間の距離を再定義することで大きさを再定義できます。(定義後、アライメントを再実施する必要があります。) 今回は自動定義で11.6mとなってしまっていた為、おおよその実測値として0.2mを再定義しました。 2.3 メッシュモデルを作成 RealityCaptureではメッシュモデル作成時に、モデル解像度のオプションとして「通常精細」「高精細」を選ぶことができます。それぞれで作成したメッシュモデルを以下に示します。  ・通常精細(所要時間:6分 ポリゴン数:330万)  ・高精細(所要時間:23分 ポリゴン数:1360万) 通常精細でも綺麗なメッシュモデルが出来ていますが、高精細ではさらに細かい凹凸まで再現されていることが確認出来ます。以降は、高精細モデルを用いてモデル構築を行います。 2.4 テクスチャを作成 テクスチャ作成時の設定は以下になります。 テクスチャ最大解像度:16K×16K スタイル:テクセルサイズを固定 テクセルサイズ:最適化 以下に、上記設定にてテクスチャを貼りつけた3Dモデルを示します。 作成した3Dモデルと、インプットに用いた写真を並べてみます。 元画像と比較しても遜色ない程の高精細3Dモデルが作成出来ました。 (一方、左の枝が切れていたり花の周辺に茶色の端部分が残ったりと、何点か微修正の必要な箇所が見受けられます。 本記事では割愛しますが、このような箇所は Blender 等の3DCGツールを用いて対処します。) 手順2.作成した3DモデルをFBX形式でエクスポートし、UnrealEngineへインポートする ①RealityCaptureからのエクスポート 3DモデルをRealityCaptureからエクスポートします。 エクスポート形式はFBXを指定します。 エクスポート時の設定値は以下になります。 頂点法線のエクスポート:はい 頂点カラーのエクスポート:いいえ テクスチャのエクスポート:はい 座標系:グリッド平面 変換プリセット:カスタム XYZ移動:0 XYZ回転:0 XYZスケール:100 (UEインポート時に適切な大きさに変換する為) X座標の反転:いいえ YZ座標の反転:はい(UEインポート時に正面を向かせる為) 下記のように3Dモデルがエクスポートされます。 ②UnrealEngineへインポート 3Dモデルを取込むための仮想空間を構築します。 本記事では、建築用のBlankプリセットを用いて仮想空間構築を行いました。 続いてモデルのインポートを行います。 3Dモデルのインポート先フォルダを作成し、モデルファイルをドロップします。 インポート時の設定値は以下になります。 Skeltal Mesh:OFF Build Nanaite:ON Generate Missing Collision:ON Convert Scene:ON Force Front XAxis:OFF Convert Scene Unit:OFF 下記のように3Dモデルがインポートされます。 手順3.UnrealEngineのマップ上に、3Dモデルを配置する マテリアルのベー スカラー にテクス チャフ ァイルを接続し、UnrealEngine上の3Dモデルにテクスチャを貼りつけます。 3Dモデルを ドラッグ&ドロップ し、マップ上へ配置します。 以下は、配置した3Dモデルを一人称視点で確認したものです。 3Dモデルのみならず、影まで正確に表示されていることが確認出来ます。(影はUnrealEngineの機能です) 一方、RealityCapture上での3Dモデルと比べると全体的にツヤが失われており、花が固まっているように見えます。 RealityCaptureのアウトプット設定やUnrealEngineのインプット設定を見直し調整する必要がありそうです。 こちらに関しては、今後の課題となります。 Visualizationモードで見てみますと、3D モデルがリアルタイムで レンダリング されている様子を確認出来ます。 余談となりますが、UnrealEngine上で3Dモデルの明るさと粗さ(光の反射量)を調整してみます。 まず、明るさから調整します。 手順3.の最初では3Dマテリアルのベー スカラー とテクスチャを直接繋ぎましたが、この間に「Multiply」と「Blightness(Param)」を追加します。 これにより、「Blightness(Param)」に設定した値で明るさの調整ができるようになります。(デフォルト:1) 次に粗さです。 3D マテリアルのラフネスノードに「Roughness(Param)」を直接繋ぎます。 これにより、「Roughness(Param)」に設定した値で粗さの調整ができるようになります。(デフォルト:0.5) 調整準備が整いましたので、試しに以下の設定値にしてみます。 Blightness(Param):0.4 Roughness(Param):0.4 全体的に暗めな印象になりました。 仮想空間のスタイルに合わせて、自由に調整することが出来ます。 フォトグラメトリのデメリットとして、生成するテクスチャが、写真撮影時のライティングに影響を受けてしまうことが挙げられます。撮影時と違う印象にしたい場合は、上記手順を行うことで異なるライティング状態を再現できます。 おわりに 本記事では、フォトグラメトリを用いた3Dモデルの作成、またそれを仮想空間へ取り込んで設置する手順を紹介しました。UnrealEngineはゲーム用エンジンですが、RealityCapture等による高精細3Dモデルと組み合わせることにより” ノンゲーム ”領域での活用可能性が広がっていくと考えています。 また、今回は小さな物体を対象に3Dモデル化を行いましたが、次は大きな物体や部屋の内装/外装の3Dモデル化にも挑戦し、 ノンゲーム 領域でのフォトグラメトリ活用を目指します! ISIDの金融事業部では、金融直結の仕事に限らず様々な新規領域活動を行っており、本活動もその一端となります。また、現在ISIDでは web3領域のグループ横断組織 を立ち上げており、その中で メタバース やWeb3との連携も見据えた活動を進めていく予定です。 私たちと同じチームで働いてくれる仲間を、是非お待ちしております! ISID採用ページ     参考資料 RealityCapture 日本語マニュアル - 前編- はじめてのRealityCapture-完全なモデルを作成する手順 RealityCapture to UnrealEngine5 lileaLab 執筆: @matsuzaki.shota 、レビュー: @wakamoto.ryosuke ( Shodo で執筆されました )
みなさんこんにちは! ISID 金融ソリューション事業部、新卒2年目の松崎です。 本記事は 電通国際情報サービス Advent Calendar 2022 12月 20日 の記事になります。 今回は、フォトグラメトリソフトウェアの RealityCapture と ゲームエンジン の UnrealEngine を使って 現実空間の物体から作成した3Dモデルを仮想空間に設置する手順 を紹介します。 はじめに 「 メタバース 」とは、どのようなものでしょうか。 語源を調べると、超越・高次を意味する「Meta」と世界を意味する「 Universe 」を組み合わせた造語であると分かります。 つまり、 超越した世界 と解釈できるわけですが、なかなか抽象的で理解し辛いですね。 実際のところ メタバース の正確な定義は定まっておらず、現状は「インターネット上に存在する3D仮想空間」と理解しておけば問題ないと考えています。 今回は、そんな メタバース の構築ツールであるUnrealEngineのマップに現実空間の物体を置き、リアルタイム レンダリング してみた! ということでその実施手順を紹介します。 手順 RealityCaptureを用いて、現実空間の物体から3Dモデルを作成する 作成した3DモデルをFBX形式でエクスポートし、UnrealEngineへインポートする UnrealEngineのマップ上に、3Dモデルを設置する 使用機材 PC(3Dモデル構築、仮想空間構築) OS:Windows10 Home CPU: Intel Core i9 -10900K   @3.70GHz  GPU : NVIDIA GeForce RTX 3080 RealityCapture 1.2.1 インストール済み UnrealEngine 5.1.0 インストール済み iPhone12mini(写真撮影) OS: iOS 15.6 ZY Cami 1.3.4(写真アプリ)インストール済み Zhiyun Smooth Q4( スマートフォン 用ジンバル、三脚) 手順1.現実空間の物体から3D モデルを作成 まず初めに、 RealityCapture を用いて3Dモデルを作成します。 RealityCapture は、 Epic Games 社が提供するフォトグラメトリソフトウェアです。 写真から3Dモデルを作成することを強みとしており、仕上がり品質の高さが特徴的です。 また、LiDARなどによるレーザースキャンデータとの組み合わせも可能で、地形の測量等にも活用されています。 スマートフォン 用無料アプリもリリースされましたので、ご興味ある方は是非お試しください! 本記事ではPC版RealityCaptureを使用し、 写真だけを用いて 3Dモデル作成を行います。 ①写真撮影 高精細な3Dモデルを作成する場合、インプットに使う写真の質が重要になります。 写真の質を上げる方法として、今回は以下の観点を意識しました。 撮影方法:対象物体を取り囲むように周回移動しながら撮影 画質:iPhone12mini 最高画質(12MP) 画像重なり:前後の画像にて、70%以上の重なりを維持(計140枚撮影) ブレ軽減:三脚とジンバルを用いて、撮影時のブレを最小化 距離:可能な限り対象物体に近づいて撮影(写真内で物体が占める ピクセル 数を最大化) また、カメラ設定は以下になります。 ISO 100 ズーム:等倍(1.0) 写真サイズ:2750 × 3664 (縦撮り) ②RealityCaptureで3Dモデル構築 撮影した写真をRealityCaptureへインポートし、3Dモデルを作成します。 2.1 写真を取込み、アライメントを実施(アライメント処理時間:1分) アライメントとは、オーバーラップ撮影された写真から、複数の写真の共通点を点として抽出し、低密度の点群データを作成する処理になります。同時にカメラの位置や対象物との距離を計算しており、下記画像の白い小さな四角形は、写真1枚1枚の撮影位置推定結果を示しています。 今回は三脚を用いて高さ固定で撮影をしていますが、RealityCapture上においても高さが一定であると推定されています。 2.2 コン トロール ポイントを用いて距離を定義し、再アライメント 最初のアライメント時に物体の大きさが自動定義されますが、ズレが大きい時は自分で再定義します。 再定義の際はコン トロール ポイントを利用します。コン トロール ポイントとは、写真上に目印となるポイントを設定できる機能です。アライメント処理で写真同士のオーバーラップ判定が上手く行かなかった時などに、写真間の共通ポイントをコン トロール ポイントとして指定することで、オーバーラップ判定の手助けを行うことができます。 また、コン トロール ポイントを2つ設定し、2点間の距離を再定義することで大きさを再定義できます。(定義後、アライメントを再実施する必要があります。) 今回は自動定義で11.6mとなってしまっていた為、おおよその実測値として0.2mを再定義しました。 2.3 メッシュモデルを作成 RealityCaptureではメッシュモデル作成時に、モデル解像度のオプションとして「通常精細」「高精細」を選ぶことができます。それぞれで作成したメッシュモデルを以下に示します。  ・通常精細(所要時間:6分 ポリゴン数:330万)  ・高精細(所要時間:23分 ポリゴン数:1360万) 通常精細でも綺麗なメッシュモデルが出来ていますが、高精細ではさらに細かい凹凸まで再現されていることが確認出来ます。以降は、高精細モデルを用いてモデル構築を行います。 2.4 テクスチャを作成 テクスチャ作成時の設定は以下になります。 テクスチャ最大解像度:16K×16K スタイル:テクセルサイズを固定 テクセルサイズ:最適化 以下に、上記設定にてテクスチャを貼りつけた3Dモデルを示します。 作成した3Dモデルと、インプットに用いた写真を並べてみます。 元画像と比較しても遜色ない程の高精細3Dモデルが作成出来ました。 (一方、左の枝が切れていたり花の周辺に茶色の端部分が残ったりと、何点か微修正の必要な箇所が見受けられます。 本記事では割愛しますが、このような箇所は Blender 等の3DCGツールを用いて対処します。) 手順2.作成した3DモデルをFBX形式でエクスポートし、UnrealEngineへインポートする ①RealityCaptureからのエクスポート 3DモデルをRealityCaptureからエクスポートします。 エクスポート形式はFBXを指定します。 エクスポート時の設定値は以下になります。 頂点法線のエクスポート:はい 頂点カラーのエクスポート:いいえ テクスチャのエクスポート:はい 座標系:グリッド平面 変換プリセット:カスタム XYZ移動:0 XYZ回転:0 XYZスケール:100 (UEインポート時に適切な大きさに変換する為) X座標の反転:いいえ YZ座標の反転:はい(UEインポート時に正面を向かせる為) 下記のように3Dモデルがエクスポートされます。 ②UnrealEngineへインポート 3Dモデルを取込むための仮想空間を構築します。 本記事では、建築用のBlankプリセットを用いて仮想空間構築を行いました。 続いてモデルのインポートを行います。 3Dモデルのインポート先フォルダを作成し、モデルファイルをドロップします。 インポート時の設定値は以下になります。 Skeltal Mesh:OFF Build Nanaite:ON Generate Missing Collision:ON Convert Scene:ON Force Front XAxis:OFF Convert Scene Unit:OFF 下記のように3Dモデルがインポートされます。 手順3.UnrealEngineのマップ上に、3Dモデルを配置する マテリアルのベー スカラー にテクス チャフ ァイルを接続し、UnrealEngine上の3Dモデルにテクスチャを貼りつけます。 3Dモデルを ドラッグ&ドロップ し、マップ上へ配置します。 以下は、配置した3Dモデルを一人称視点で確認したものです。 3Dモデルのみならず、影まで正確に表示されていることが確認出来ます。(影はUnrealEngineの機能です) 一方、RealityCapture上での3Dモデルと比べると全体的にツヤが失われており、花が固まっているように見えます。 RealityCaptureのアウトプット設定やUnrealEngineのインプット設定を見直し調整する必要がありそうです。 こちらに関しては、今後の課題となります。 Visualizationモードで見てみますと、3D モデルがリアルタイムで レンダリング されている様子を確認出来ます。 余談となりますが、UnrealEngine上で3Dモデルの明るさと粗さ(光の反射量)を調整してみます。 まず、明るさから調整します。 手順3.の最初では3Dマテリアルのベー スカラー とテクスチャを直接繋ぎましたが、この間に「Multiply」と「Blightness(Param)」を追加します。 これにより、「Blightness(Param)」に設定した値で明るさの調整ができるようになります。(デフォルト:1) 次に粗さです。 3D マテリアルのラフネスノードに「Roughness(Param)」を直接繋ぎます。 これにより、「Roughness(Param)」に設定した値で粗さの調整ができるようになります。(デフォルト:0.5) 調整準備が整いましたので、試しに以下の設定値にしてみます。 Blightness(Param):0.4 Roughness(Param):0.4 全体的に暗めな印象になりました。 仮想空間のスタイルに合わせて、自由に調整することが出来ます。 フォトグラメトリのデメリットとして、生成するテクスチャが、写真撮影時のライティングに影響を受けてしまうことが挙げられます。撮影時と違う印象にしたい場合は、上記手順を行うことで異なるライティング状態を再現できます。 おわりに 本記事では、フォトグラメトリを用いた3Dモデルの作成、またそれを仮想空間へ取り込んで設置する手順を紹介しました。UnrealEngineはゲーム用エンジンですが、RealityCapture等による高精細3Dモデルと組み合わせることにより” ノンゲーム ”領域での活用可能性が広がっていくと考えています。 また、今回は小さな物体を対象に3Dモデル化を行いましたが、次は大きな物体や部屋の内装/外装の3Dモデル化にも挑戦し、 ノンゲーム 領域でのフォトグラメトリ活用を目指します! ISIDの金融事業部では、金融直結の仕事に限らず様々な新規領域活動を行っており、本活動もその一端となります。 現在ISIDは web3領域のグループ横断組織 を立ち上げ、Web3および メタバース 領域のR&Dを行っております(カテゴリー「3DCG」の記事は こちら )。 もし本領域にご興味のある方や、一緒にチャレンジしていきたい方は、ぜひお気軽にご連絡ください! 私たちと同じチームで働いてくれる仲間を、是非お待ちしております! ISID採用ページ     参考資料 RealityCapture 日本語マニュアル - 前編- はじめてのRealityCapture-完全なモデルを作成する手順 RealityCapture to UnrealEngine5 lileaLab 執筆: @matsuzaki.shota 、レビュー: @wakamoto.ryosuke ( Shodo で執筆されました )
みなさんこんにちは! ISID 金融ソリューション事業部、新卒2年目の松崎です。 本記事は 電通国際情報サービス Advent Calendar 2022 12月 20日 の記事になります。 今回は、フォトグラメトリソフトウェアの RealityCapture と ゲームエンジン の UnrealEngine を使って 現実空間の物体から作成した3Dモデルを仮想空間に設置する手順 を紹介します。 はじめに 「 メタバース 」とは、どのようなものでしょうか。 語源を調べると、超越・高次を意味する「Meta」と世界を意味する「 Universe 」を組み合わせた造語であると分かります。 つまり、 超越した世界 と解釈できるわけですが、なかなか抽象的で理解し辛いですね。 実際のところ メタバース の正確な定義は定まっておらず、現状は「インターネット上に存在する3D仮想空間」と理解しておけば問題ないと考えています。 今回は、そんな メタバース の構築ツールであるUnrealEngineのマップに現実空間の物体を置き、リアルタイム レンダリング してみた! ということでその実施手順を紹介します。 手順 RealityCaptureを用いて、現実空間の物体から3Dモデルを作成する 作成した3DモデルをFBX形式でエクスポートし、UnrealEngineへインポートする UnrealEngineのマップ上に、3Dモデルを設置する 使用機材 PC(3Dモデル構築、仮想空間構築) OS:Windows10 Home CPU: Intel Core i9 -10900K   @3.70GHz  GPU : NVIDIA GeForce RTX 3080 RealityCapture 1.2.1 インストール済み UnrealEngine 5.1.0 インストール済み iPhone12mini(写真撮影) OS: iOS 15.6 ZY Cami 1.3.4(写真アプリ)インストール済み Zhiyun Smooth Q4( スマートフォン 用ジンバル、三脚) 手順1.現実空間の物体から3D モデルを作成 まず初めに、 RealityCapture を用いて3Dモデルを作成します。 RealityCapture は、 Epic Games 社が提供するフォトグラメトリソフトウェアです。 写真から3Dモデルを作成することを強みとしており、仕上がり品質の高さが特徴的です。 また、LiDARなどによるレーザースキャンデータとの組み合わせも可能で、地形の測量等にも活用されています。 スマートフォン 用無料アプリもリリースされましたので、ご興味ある方は是非お試しください! 本記事ではPC版RealityCaptureを使用し、 写真だけを用いて 3Dモデル作成を行います。 ①写真撮影 高精細な3Dモデルを作成する場合、インプットに使う写真の質が重要になります。 写真の質を上げる方法として、今回は以下の観点を意識しました。 撮影方法:対象物体を取り囲むように周回移動しながら撮影 画質:iPhone12mini 最高画質(12MP) 画像重なり:前後の画像にて、70%以上の重なりを維持(計140枚撮影) ブレ軽減:三脚とジンバルを用いて、撮影時のブレを最小化 距離:可能な限り対象物体に近づいて撮影(写真内で物体が占める ピクセル 数を最大化) また、カメラ設定は以下になります。 ISO 100 ズーム:等倍(1.0) 写真サイズ:2750 × 3664 (縦撮り) ②RealityCaptureで3Dモデル構築 撮影した写真をRealityCaptureへインポートし、3Dモデルを作成します。 2.1 写真を取込み、アライメントを実施(アライメント処理時間:1分) アライメントとは、オーバーラップ撮影された写真から、複数の写真の共通点を点として抽出し、低密度の点群データを作成する処理になります。同時にカメラの位置や対象物との距離を計算しており、下記画像の白い小さな四角形は、写真1枚1枚の撮影位置推定結果を示しています。 今回は三脚を用いて高さ固定で撮影をしていますが、RealityCapture上においても高さが一定であると推定されています。 2.2 コン トロール ポイントを用いて距離を定義し、再アライメント 最初のアライメント時に物体の大きさが自動定義されますが、ズレが大きい時は自分で再定義します。 再定義の際はコン トロール ポイントを利用します。コン トロール ポイントとは、写真上に目印となるポイントを設定できる機能です。アライメント処理で写真同士のオーバーラップ判定が上手く行かなかった時などに、写真間の共通ポイントをコン トロール ポイントとして指定することで、オーバーラップ判定の手助けを行うことができます。 また、コン トロール ポイントを2つ設定し、2点間の距離を再定義することで大きさを再定義できます。(定義後、アライメントを再実施する必要があります。) 今回は自動定義で11.6mとなってしまっていた為、おおよその実測値として0.2mを再定義しました。 2.3 メッシュモデルを作成 RealityCaptureではメッシュモデル作成時に、モデル解像度のオプションとして「通常精細」「高精細」を選ぶことができます。それぞれで作成したメッシュモデルを以下に示します。  ・通常精細(所要時間:6分 ポリゴン数:330万)  ・高精細(所要時間:23分 ポリゴン数:1360万) 通常精細でも綺麗なメッシュモデルが出来ていますが、高精細ではさらに細かい凹凸まで再現されていることが確認出来ます。以降は、高精細モデルを用いてモデル構築を行います。 2.4 テクスチャを作成 テクスチャ作成時の設定は以下になります。 テクスチャ最大解像度:16K×16K スタイル:テクセルサイズを固定 テクセルサイズ:最適化 以下に、上記設定にてテクスチャを貼りつけた3Dモデルを示します。 作成した3Dモデルと、インプットに用いた写真を並べてみます。 元画像と比較しても遜色ない程の高精細3Dモデルが作成出来ました。 (一方、左の枝が切れていたり花の周辺に茶色の端部分が残ったりと、何点か微修正の必要な箇所が見受けられます。 本記事では割愛しますが、このような箇所は Blender 等の3DCGツールを用いて対処します。) 手順2.作成した3DモデルをFBX形式でエクスポートし、UnrealEngineへインポートする ①RealityCaptureからのエクスポート 3DモデルをRealityCaptureからエクスポートします。 エクスポート形式はFBXを指定します。 エクスポート時の設定値は以下になります。 頂点法線のエクスポート:はい 頂点カラーのエクスポート:いいえ テクスチャのエクスポート:はい 座標系:グリッド平面 変換プリセット:カスタム XYZ移動:0 XYZ回転:0 XYZスケール:100 (UEインポート時に適切な大きさに変換する為) X座標の反転:いいえ YZ座標の反転:はい(UEインポート時に正面を向かせる為) 下記のように3Dモデルがエクスポートされます。 ②UnrealEngineへインポート 3Dモデルを取込むための仮想空間を構築します。 本記事では、建築用のBlankプリセットを用いて仮想空間構築を行いました。 続いてモデルのインポートを行います。 3Dモデルのインポート先フォルダを作成し、モデルファイルをドロップします。 インポート時の設定値は以下になります。 Skeltal Mesh:OFF Build Nanaite:ON Generate Missing Collision:ON Convert Scene:ON Force Front XAxis:OFF Convert Scene Unit:OFF 下記のように3Dモデルがインポートされます。 手順3.UnrealEngineのマップ上に、3Dモデルを配置する マテリアルのベー スカラー にテクス チャフ ァイルを接続し、UnrealEngine上の3Dモデルにテクスチャを貼りつけます。 3Dモデルを ドラッグ&ドロップ し、マップ上へ配置します。 以下は、配置した3Dモデルを一人称視点で確認したものです。 3Dモデルのみならず、影まで正確に表示されていることが確認出来ます。(影はUnrealEngineの機能です) 一方、RealityCapture上での3Dモデルと比べると全体的にツヤが失われており、花が固まっているように見えます。 RealityCaptureのアウトプット設定やUnrealEngineのインプット設定を見直し調整する必要がありそうです。 こちらに関しては、今後の課題となります。 Visualizationモードで見てみますと、3D モデルがリアルタイムで レンダリング されている様子を確認出来ます。 余談となりますが、UnrealEngine上で3Dモデルの明るさと粗さ(光の反射量)を調整してみます。 まず、明るさから調整します。 手順3.の最初では3Dマテリアルのベー スカラー とテクスチャを直接繋ぎましたが、この間に「Multiply」と「Blightness(Param)」を追加します。 これにより、「Blightness(Param)」に設定した値で明るさの調整ができるようになります。(デフォルト:1) 次に粗さです。 3D マテリアルのラフネスノードに「Roughness(Param)」を直接繋ぎます。 これにより、「Roughness(Param)」に設定した値で粗さの調整ができるようになります。(デフォルト:0.5) 調整準備が整いましたので、試しに以下の設定値にしてみます。 Blightness(Param):0.4 Roughness(Param):0.4 全体的に暗めな印象になりました。 仮想空間のスタイルに合わせて、自由に調整することが出来ます。 フォトグラメトリのデメリットとして、生成するテクスチャが、写真撮影時のライティングに影響を受けてしまうことが挙げられます。撮影時と違う印象にしたい場合は、上記手順を行うことで異なるライティング状態を再現できます。 おわりに 本記事では、フォトグラメトリを用いた3Dモデルの作成、またそれを仮想空間へ取り込んで設置する手順を紹介しました。UnrealEngineはゲーム用エンジンですが、RealityCapture等による高精細3Dモデルと組み合わせることにより” ノンゲーム ”領域での活用可能性が広がっていくと考えています。 また、今回は小さな物体を対象に3Dモデル化を行いましたが、次は大きな物体や部屋の内装/外装の3Dモデル化にも挑戦し、 ノンゲーム 領域でのフォトグラメトリ活用を目指します! ISIDの金融事業部では、金融直結の仕事に限らず様々な新規領域活動を行っており、本活動もその一端となります。 現在ISIDは web3領域のグループ横断組織 を立ち上げ、Web3および メタバース 領域のR&Dを行っております(カテゴリー「3DCG」の記事は こちら )。 もし本領域にご興味のある方や、一緒にチャレンジしていきたい方は、ぜひお気軽にご連絡ください! 私たちと同じチームで働いてくれる仲間を、是非お待ちしております! ISID採用ページ     参考資料 RealityCapture 日本語マニュアル - 前編- はじめてのRealityCapture-完全なモデルを作成する手順 RealityCapture to UnrealEngine5 lileaLab 執筆: @matsuzaki.shota 、レビュー: @wakamoto.ryosuke ( Shodo で執筆されました )
こんにちは。 XI本部 AIトランスフォーメーションセンター の徳原光です。ISID2022年 アドベントカレンダー の12月19日の記事を投稿します。 皆さん、仕事のいやしってありますか? お仕事にはストレスはつきもの。仕事の合間にメンタルをコン トロール する自分なりの方法があるって大事なことだと思うんですよね。 自分はこれです。 小さな温室に入った小さなジャングル。日々刻々と変化する熱帯植物の様子を眺めると、今頭の中でグルグル回っている悩みなんてどうでもいいなって気持ちになります。 温室を運用する上で一番大切なことは、当たり前ですが温度を保つことです(あと、湿度も大事)。なので、温室内の温度変化に注意しつつ、季節や天候によって設定温度を微妙に変更したくなるんですよね。 すでに、 Raspberry Pi に温湿度センサーDHT22を接続して温度遷移の記録を取りつつ、スマートプラグを利用して温室に設置したパネルヒーターの制御を行っていたのですが、パネルヒーターの稼働温度の変更や、温度遷移を確認するのにわざわざRsapberry Piに接続するのは面倒でした。 ということで個人で使用するために、 スマホ でかんたんに温度の監視やパネルヒーターの設定温度を変更するモバイルアプリを作成したので、そのレシピを紹介したいと思います。 同様のことに挑戦される方のご参考になればと思います。今回実装したコードはすべてこちらの リポジトリ に上げたので活用してください。 Pickerdot/smartterrarium 作成したもの できること 温室内の現在温度・湿度の取得 日中と夜間のパネルヒーターの稼働温度設定 4日分の温度・湿度遷移記録(15分おき)の確認 動作環境 Raspberry Pi Android スマートフォン 材料 Raspberry Pi (4 Model Bを推奨)) 温湿度センサーDHT22 スマートプラグHS105(p105でも可) パネルヒーター お手持ちの スマホ (今回は Android 機を使用) FastAPI Flutter 手順 Raspberry Pi に温湿度センサーDHT22を接続 スマートプラグHS105をLAN内に接続 パネルヒーターの電源プラグをスマートプラグHS105に接続 温湿度センサーとパネルヒーターを温室内に設置 温度によるパネルヒーターの制御プログラムを作成 温度・湿度記録用のデータベースと設定値保持用のデータベースをSQLite3で作成 制御プログラムをFastAPIを利用して、 Raspberry Pi 上でweb API 化 Flutterで操作画面を作成し、パネルヒーター制御のための API を叩く Flutterで作ったアプリを Android にUSB デバッグ でインストール 詳細 Raspberry Pi に温湿度センサーを接続 一番、つまづいたポイントはここかもしれません。GPIOピンに温湿度センサーを接続します。使用したDHT22はVCC 、DAT、GNDと3つの端子が存在しますが、VCCは3.3Vピン、DATがGPIOピン、GNDはGNDピンに接続する必要があります。 おすすめは配線しやすいので、VCCは1番ピン、DATは37番ピン(GPIO26)、GNDは29番ピンに接続することです。これらはすべて端に配置されているピンなので間違えずに配線できると思います。 ピンの配置がわからなくなったら、 Raspberry Pi のターミナルで $ pinout と実行するとこのようにピンの配置が表示されます 3V3 (1) (2) 5V GPIO2 (3) (4) 5V GPIO3 (5) (6) GND GPIO4 (7) (8) GPIO14 GND (9) (10) GPIO15 GPIO17 (11) (12) GPIO18 GPIO27 (13) (14) GND GPIO22 (15) (16) GPIO23 3V3 (17) (18) GPIO24 GPIO10 (19) (20) GND GPIO9 (21) (22) GPIO25 GPIO11 (23) (24) GPIO8 GND (25) (26) GPIO7 GPIO0 (27) (28) GPIO1 GPIO5 (29) (30) GND GPIO6 (31) (32) GPIO12 GPIO13 (33) (34) GND GPIO19 (35) (36) GPIO16 GPIO26 (37) (38) GPIO20 GND (39) (40) GPIO21 つまづいた理由は手元の Raspberry Pi 4がDHT22の出力を認識できなかったことでした。GPIOの設定は一通り確認したのですが認識せず、最初はDHT22の故障を疑っていたので arduino で動作確認したらすんなり動作しました。 しょうがないので、学生時代に買った Raspberry Pi 3 B+を引っ張り出してきて、動作確認することになりました。今度はDHT22から温度と湿度を取得できたものの、RAMが1GBの環境は快適にプログラミングできる環境でないので、その後かなりコーディングに苦戦しました。 まずは、必要なパッケージをインストールします。 $ sudo apt-get update $ sudo apt-get install build-essential python-dev さらに、DHT22の出力を認識するためのライブラリをインストールします(適当なフォルダ内で実行)。 $ sudo git clone https://github.com/adafruit/Adafruit_Python_DHT.git $ cd Adafruit_Python_DHT $ sudo python setup.py install DHT22読み取り用のプログラムを実装します。 import Adafruit_DHT as DHT import RPi.GPIO as GPIO # センサータイプの設定 SENSOR_TYPE = DHT.DHT22 # 使用するGPIOのピン番号 DHT_GPIO = 26 def read_dht(): try: humidity, temperature = DHT.read_retry( SENSOR_TYPE, DHT_GPIO) message_temp = "Temp= {0:0.1f} deg C".format(temperature) message_humidity = "Humidity= {0:0.1f} %".format(humidity) message = message_temp + ". " + message_humidity print(message) except: # センサから温度が取得できなかった際に、Nullが返るとエラーが起こるので応急処置 humidity = 0.0 temperature = 0.0 return temperature, humidity スマートプラグをLAN内に接続 こちらは公式のアプリを操作してください HS105初期設定 p105初期設定 Raspberry Pi が接続されているLANに接続できればOKです パネルヒーターの電源プラグをスマートプラグHS105に接続 パネルヒーターの電源プラグをスマートプラグに接続してください。 「 Raspberry Pi があるんだったら、リレー回路を組んで Raspberry Pi のIOから直接制御すればいいのでは?」と突っ込まれそうですが、リレー素子を購入したものの家庭用電源を自分が組んだ電気回路で制御するのは恐ろしくできませんでした。 スマートプラグは2000円程度で購入できるので、回路を組む手間を考えると素直にスマートプラグを使用したほうがいいと思います。 温湿度センサーとパネルヒーターを温室内に設置 お好きな様に設置してください。 温湿度センサーと Raspberry Pi を配線するので、 Raspberry Pi を温室の近くに設置する必要があり、温室から漏れた水で Raspberry Pi が故障しないか若干心配です。 どうせ近く設置するなら、 Raspberry Pi からの排熱も温室の加温に利用したいですね・・・。 温度によるパネルヒーターの制御プログラムを書く 今回は日中(7:00~20:00)と夜間(20:00~7:00)で設定温度を変えます。市販の サーモス タッドではなく、わざわざ Raspberry Pi でパネルヒーターを制御している理由はここにあります。ある程度夜間に温度を下げたいので、時間帯によってパネルヒーターの稼働温度を変えています( サーモス タッドも高いやつは時間帯によって設定温度を変えられますが・・・)。 というのも、温室だからといって24時間365日常夏にしたいわけじゃなくて、ある程度一日の寒暖差があった上で、冬は冬らしく枯れない程度に寒さを感じさせる必要があります。なので、この設定温度も寒さが厳しくなる1月や2月にはさらに下げようと考えています。冬に花が咲く品種もありますし、春に向けてちゃんと今が冬であることを理解できるように日中と夜間に温度差が生じるように設定します。 こちらはプラグの制御に用いたクラスの抜粋です。完成版は以下の レポジトリ で公開しています。 import Adafruit_DHT as DHT from tp_plug import * import sqlite3 # 温度センサーの出力を受けてスマートプラグを制御するためのクラス class ControlPlug: def __init__(self, threshold_day_temperature, threshold_night_temperature, over_temperature): # パネルヒーターのオンオフを管理するフラグ(0:off、1:on) self.heat_flag = 0 # 日中のパネルヒーターの稼働温度 self.threshold_day_temperature = threshold_day_temperature # 夜間のパネルヒーターの稼働温度 self.threshold_night_temperature = threshold_night_temperature # 超過温度(一度パネルヒーターが稼働すると稼働温度+超過温度を超えるまで稼働し続ける) self.over_temperature = over_temperature # 日中、夜間の切り替え温度 self.day_start_hour = 7 self.day_end_hour = 20 # 起動時に一度IOTプラグの電源を落とす print(TPLink_Plug("192.168.11.23").off()) # スマートプラグ(HS105)のスイッチング def switch_plug(self, temperature, dt_now): print("dt_now.hour:", dt_now.hour) # 夜間、ヒーターオフ時、温度低下でヒーターオン if (dt_now.hour < 7 or 19 < dt_now.hour) and temperature < self.threshold_night_temperature and self.heat_flag == 0: self.heat_flag = 1 print("night_on") print(TPLink_Plug("192.168.11.23").on()) # 昼間、ヒーターオフ時、温度低下でヒーターオン if (7 < dt_now.hour < 19) and temperature < self.threshold_day_temperature and self.heat_flag == 0: self.heat_flag = 1 print("day_on") print(TPLink_Plug("192.168.11.23").on()) # 夜間、ヒーターオン時、温度上昇でヒーターオフ if (dt_now.hour < 7 or 19 < dt_now.hour) and temperature > (self.threshold_night_temperature + self.over_temperature) and self.heat_flag == 1: self.heat_flag = 0 print("night_off") print(TPLink_Plug("192.168.11.23").off()) # 昼間、ヒーターオン時、温度上昇でヒーターオフ if (7 < dt_now.hour < 19) and temperature > (self.threshold_day_temperature + self.over_temperature) and self.heat_flag == 1: self.heat_flag = 0 print("day_off") print(TPLink_Plug("192.168.11.23").off()) return self.heat_flag over_temperature は温度が稼働温度を下回って、パネルヒーターがonになった後に温度が稼働温度を超えても加温し続けるための超過温度の設定値です。 あくまで稼働温度はこれ以上温度が下がると困るという値なので、パネルヒーターにより、温度が稼働温度を超えても余分に加温し続けるように設定します(パネルヒーターがoffになったあとすぐに温室内の温度が設定値を下回るのを防ぐためです)。 パネルヒーターが稼働しているかの判断はスマートプラグより情報を取得するのが難しかったので、 heat_flag という変数でパネルヒーターの稼働状況を保持しています。 ということで、スイッチングのif文の条件式がやや複雑になってます。 スマートプラグの制御モジュール(tp_plug.py)は、こちらのページの コードー を使用させていただきました。 コスパ最強IoT家電!TPLink製品をRaspberryPiから操作 温度・湿度記録用のデータベースと設定値保持用のデータベースをSQLite3で作成 半分終了しました。疲れていませんか?自分は9月の中旬に材料を買ったのですが、ここまでたどり着いたときには11月になっていました。 アドベントカレンダー にこのネタを投稿することが決まったので、少しペースを上げましたが、それでも今ギリギリになって記事を書いています。 このアプリの目玉機能にこれまでの温度・湿度記録を確認できるというものがあります。パネルヒーターがちゃんと動作しているのか確認するという意味合いもありますが、温室内の植物がある日突然調子を崩した際に原因を推測する手がかりになるので、個人的には必ず盛り込みたい機能でした。 今回は物理的な加湿機構を温室内に作成するのが間に合わなかったので湿度の調整はできませんが、温室内の植物は70%~に湿度を保つ必要があり、一瞬でも下回るとそれだけでダメージを負ってしまうので24時間の監視が重要になります。 データを保持するテーブルはシンプルなものにしました( カラム名 の 命名 がイケてない気がしますが気にしないことにします)。 温度・湿度記録テーブル(environmental_record) カラム名 内容 データ型 制約 measurement_time 計測日時 TEXT PRIMARY KEY temperature 計測温度 float NOT NULL humidity 計測湿度 float NOT NULL heat_flag パネルヒーター稼働フラグ int NOT NULL 設定値テーブル(parameter_set) カラム名 内容 データ型 制約 update_at 更新日時 TEXT PRIMARY KEY threshold_day_temperature 日中稼働温度 float NOT NULL threshold_night_temperature 夜間稼働温度 float NOT NULL diff_temperature 超過温度 float NOT NULL 作業的にはまず、 Raspberry Pi にSQLite3をインストールするところから始めます。 $ sudo apt install sqlite3 -y 次に、pipでSQLite3のモジュールをインストール。 $ pip install pysqlite3 次に タイムゾーン 設定のためのモジュールをインストール。 $ pip install pytz データベースの初期化用に以下のプログラムを作成しました。こちらのプログラムを一回実行すれば、同じ階層にSQLite3のデータベースが作成されます。 init_sql.py import sqlite3 import datetime import pytz dbname = 'smart-terrarium.db' # コネクタ作成。dbnameの名前を持つDBへ接続する。 conn = sqlite3.connect(dbname) cur = conn.cursor() # 温度・湿度記録用テーブルをリセットする # cur.execute('DROP TABLE IF EXISTS environmental_record') # テーブルの作成 cur.execute('CREATE TABLE environmental_record(measurement_time TEXT PRIMARY KEY,temperature float NOT NULL,humidity float NOT NULL,heat_flag int NOT NULL);') cur.execute('CREATE TABLE parameter_set(update_at TEXT PRIMARY KEY,threshold_day_temperature float NOT NULL,threshold_night_temperature float NOT NULL,diff_temperature float NOT NULL)') dt_now = datetime.datetime.now(pytz.timezone('Asia/Tokyo')) measurement_time = dt_now.strftime('%Y-%m-%d %H:%M:%S') parameter_data = [(measurement_time, 25.0, 22.0, 3.0)] cur.executemany("insert into parameter_set values (?, ?, ?, ?)", parameter_data) # 処理をコミット conn.commit() conn.close() これで、データベースの準備が整いました。 ここから、データの読み書き用のクラスを作成します。 実装するのは設定値の読み出し、更新。温度・湿度記録の読み出し(1レコード、複数レコード)、追加のためのプログラムです。 全部ここに書くと長いので、一部抜粋して紹介します。 utility.py # データの読み書き用のクラス class ReadWriteDB(): def __init__(self): self.dbname = 'smart-terrarium.db' # parameter_set読み出し def read_parameter_set(self): conn = sqlite3.connect(self.dbname) cur = conn.cursor() data = cur.execute( 'select * from parameter_set order by update_at desc limit 1') latest_record = data.fetchall() threshold_day_temperature = latest_record[0][1] threshold_night_temperature = latest_record[0][2] over_temperature = latest_record[0][3] conn.commit() conn.close() return threshold_day_temperature, threshold_night_temperature, over_temperature # 温度・湿度記録追加 def update_temperature_humidity(self, measurement_time, temperature, humidity, heat_flag): conn = sqlite3.connect(self.dbname) cur = conn.cursor() environmental_data = [ (measurement_time, temperature, humidity, heat_flag)] cur.executemany( "insert into environmental_record values (?, ?, ?, ?)", environmental_data).fetchall() conn.commit() conn.close() # 1 レコード取得 def read_one_records(self): # コネクタ作成 conn = sqlite3.connect(self.dbname) cur = conn.cursor() # 値の取得 data = cur.execute( 'select * from environmental_record order by measurement_time desc limit 1') records = data.fetchall() print(records[0][1]) return_value = { 'timestamp': records[0][0], 'temperature': records[0][1], 'humidity': records[0][2], 'heat_flag': records[0][3] } return return_value 制御プログラムをFastAPIを利用して、 Raspberry Pi 上でwebAPI化 いよいよ実装も佳境に入ってきました。 これまでに実装した Python 側の制御プログラムをwebAPIとして実装するためのプログラムをFastAPIで作成します。 FastAPIは Python でRESTful API を構築するためのマイクロ フレームワーク です。 FastAPIを使用するのは今回が初めてなのですが、RAMが1GBしかない Raspberry Pi 3 B+で実装しながら動かすので、 とにかく軽いこと、そして日本語のドキュメントが存在していること、最近 SNS 界隈で噂になっていることがFastAPIの選定理由でした。 実際利用してみると、 Raspberry Pi 3 B+でも快適に動かすことができました。これが、同時にリク エス トが飛んできた場合どうなるかわかりませんが、今回はLAN内で自分しか使用しないアプリを作るので問題ないです。 これまでに同じ Raspberry Pi で Django とSpringで作ったwebアプリを動かしたことがありました。普通に起動はしてくれるものの、サーバーが立ち上がっている際に、同時にエディターでコードの編集をしているともっさりしてまともにコーディングできませんでしたが、FastAPIはwebサーバーが起動していることを感じさせない使用感でした。 軽さよりも気に入ったのは、実装の簡単さですね。デ シリアライズ もルーティングも直感的に実装できるのが良かったです。きっと何十個もエンドポイントを用意したり、複雑な JSON をやり取りするには物足りないこともあるのかもしれませんが、日曜プログラミングにはぴったりな フレームワーク だと思います。 FastAPI公式ドキュメント まずはFastAPIと必要なモジュールをインストールします pip install fastapi pip install fastapi-utils uvicorn もインストール。 pip install "uvicorn[standard]" 実装は例の リポジトリ にまとめてあります。 ここからはFastAPIの気に入ったところだけまとめます。 smartterrarium.py # FastAPI app = FastAPI() @app.get("/") async def root(): return {"message": "This is the smart terrarium"} webサーバを立ち上げるのに必要な記述はこれだけ。あとは、 uvicorn smart-terrarium:app --reload とコマンドを実行するだけなのがいいですね。 フルスタックフレームワーク では、こうは行きませんよね。 15分おきに温度と湿度を出力する部分は以下のように実装しています。 from fastapi_utils.tasks import repeat_every # 定期実行用の関数 @app.get("/rest/") @app.on_event("startup") @repeat_every(seconds=60*15) def heater_control(): # 温度・湿度取得 temperature, humidity = control_plug.read_dht() リク エス トボディーのデ シリアライズ 。 # parameter_setのリクエストボディー class ParameterSet(BaseModel): day_temperature: int night_temperature: int diff_temperature: int # 設定値テーブル更新 @app.post("/update_parameter_set/") async def update_parameter_set(parameter_set: ParameterSet): dt_now = datetime.datetime.now(pytz.timezone('Asia/Tokyo')) measurement_time = dt_now.strftime('%Y-%m-%d %H:%M:%S.%f') read_write_db.update_parameter_set( measurement_time, int(parameter_set.day_temperature), int(parameter_set.night_temperature), int(parameter_set.diff_temperature)) return {"message": "ok", "day_temperature": parameter_set.day_temperature, "night_temperature": parameter_set.night_temperature, " diff_temperature": parameter_set.diff_temperature} ここらへんは、マイクロ フレームワーク としては標準的な機能だと思いますが(他のマイクロ フレームワーク を触ったことがないのでわからない)、AI系のプロジェクトにはかなり相性がいいなと感じました。 機械学習 モデルを実運用するとなると、だいたいPoCでモデルを作ってからあとで、webAPI化という話になりますが、あとからデータの入り口と出口としてさくっと API を実装できる感覚がすごく気に入りました。 あと、自動ドキュメント生成や今回は使用しませんでしたがデータベースとの接続もサポートされているのもいいですね。 自分がデータサイエンティストとして、日ごろ関わっているプロジェクトでも最近PoCを終えて実運用に入ろうとしているプロジェクトがありますが、その際に必要なのは既存のシステムに組み込まれて、モデルやデータ処理 スクリプト を運用するだけの API なのでFastAPIで十分です。 今回は初歩的な機能しか使用しませんでしたが、もうちょっと重厚なアプリケーションを実装すればその恩恵をもっと感じられるのではないでしょうか? Flutterで操作画面を作成し、パネルヒーター制御 API を叩く ここまで読んでいただいた方は、すでにお腹いっぱいだと思いますが、書いてる僕はもうヘトヘトです。ただ、最後に一番重いやつが残ってるんですよね。何から説明すればいいんでしょか? 何をしたかというと、 Android Studio でFlutterを使ってモバイルアプリを実装しました。画面は3つの本当にシンプルなアプリです。 モバイルアプリにした理由は Raspberry Pi でフロントエンドのwebサーバーを立ち上げたくなかったからです。気軽に温室の管理ができるのが今回のアプリのコンセプトなので、 スマホ から使う ユースケース を想定していたのですが、描画処理をRAMが1 MBの Raspberry Pi で行うよりは、豊富な計算リソースを活用できる スマートフォン を利用したほうがいいと判断しました。 Flutterを使った理由はいままで使ったことがなかったからです。React Nativeは以前 チュートリアル レベルのアプリを実装したことがあったのですが、Flutterは全く触ったことがありませんでした。ということで、ちょうどいいので触ってみようと思ったわけです。 Flutterのアプリのコードは こちら にあります。 現在温度表示画面 シンプルな現在温度と湿度の表示画面です。 こだわったところはとにかくUIの可愛さです。自分しか使わないアプリなので自分が可愛いと感じるデザインにしました。 ここの実装は特に困った部分はなかったです。Flutterのデフォルトのプロジェクトをいじるだけで実装できると思います。 パネルヒーター稼働温度設定画面 左右のプラス、マイナスボタンでパネルヒーターが稼働する設定温度を変更できます。 この画面が一番苦労した画面かもしれません。 プラスorマイナスボタンが押される ↓ Raspberry Pi 上の API が叩かれて、設定温度が変更される ↓ 現在の設定温度を Raspberry Pi 上の API から取得 ↓ 再描画 という順番で処理が進むのですが、再描画の際に現在の設定温度が取得されずにエラーを吐くという事態が頻発して、自分は全然非同期処理についてわかってないなと痛感しました。 温度遷移履歴確認画面 これまでの温度と湿度の記録をスクロールして確認できます。 ここが一番ざんねんな画面です。本当は取得期間を設定したり、折れ線グラフを描画したりしたかったんですが、力尽きました。ここについては年末にでも時間があったら実装します。 Flutterで作ったアプリを Android にUSB デバッグ でインストール 最後のステップです。 作ったアプリを手元の スマホ にインストールします。 あくまで個人利用が目的なので、USB デバッグ でインストールするという一番お手軽な方法を取りました。 やり方はかんたんで、 Android スマホ を開発者モードにしてからUSB デバッグ を有効にします。 そしたら、アプリを開発している スマホ に接続して Android Studio の実行先として スマホ を選ぶだけです。 インストールには10分程度時間がかかりました。 最後に 今回作ったアプリですが、個人的にものすごく重宝しています。 自分が前々から欲しかったアプリを形にしたので当たり前ですが、毎日使用してますね。やっぱり気軽に触れるUIがあることはアプリケーションの利便性を格段に向上させますし、何より頻繁に使ってもらえるアプリになりますね。 ただ追加したい機能はもっとあって、まずは加湿や送風の制御がしたいです。さらに、 Raspberry Pi をインターネット側からアクセス可能にして、カメラで中の様子を視覚的に確認できるようにすれば、旅行先から温室の様子を確認することが可能になります。 あと個人向けには需要がないと思いますが、農業まで視野を広げれば、画像認識AIを導入して、調子を崩した株の早期発見もできると思いますし、温室から取得できるデータを活用して収穫物の品質を向上させるなどの応用ができます。 このアプリを実装していて、就活時に農業向けのITプロジェクトを実施している会社に就職したいと考えていたことを思い出しました。当時は農業案件を実施している企業が少なくて、諦めてしまいましたが、いつかISIDで農業系のプロジェクトに挑戦したいですね。 ということで、この件についてお話がある社内外の方は、ぜひ徳原にお話しください。お役に立てることがあるかもしれません。 この長々とした記事をここまで読んでくださった方、本当にありがとうございます。植物×ITのネタはまたいつか投稿したいと思うので、その際も読んでいただければと思います(AITCwebサイトではAIやデータサイエンスに関して コラム を書いています)。 また、AITCでは新卒、中途問わず一緒に働いてくださる方を絶賛募集中です。 こちらの 採用情報ページ からカジュアル面談の申込みを受け付けています。 それでは。 参考文献 温度/湿度センサーDHT22をRaspberry Piで使用する方法 コスパ最強IoT家電!TPLink製品をRaspberryPiから操作 FastAPI公式ドキュメント 執筆: @tokuhara.hikaru 、レビュー: @yamashita.tsuyoshi ( Shodo で執筆されました )
こんにちは。 XI本部 AIトランスフォーメーションセンター の徳原光です。ISID2022年 アドベントカレンダー の12月19日の記事を投稿します。 皆さん、仕事のいやしってありますか? お仕事にはストレスはつきもの。仕事の合間にメンタルをコン トロール する自分なりの方法があるって大事なことだと思うんですよね。 自分はこれです。 小さな温室に入った小さなジャングル。日々刻々と変化する熱帯植物の様子を眺めると、今頭の中でグルグル回っている悩みなんてどうでもいいなって気持ちになります。 温室を運用する上で一番大切なことは、当たり前ですが温度を保つことです(あと、湿度も大事)。なので、温室内の温度変化に注意しつつ、季節や天候によって設定温度を微妙に変更したくなるんですよね。 すでに、 Raspberry Pi に温湿度センサーDHT22を接続して温度遷移の記録を取りつつ、スマートプラグを利用して温室に設置したパネルヒーターの制御を行っていたのですが、パネルヒーターの稼働温度の変更や、温度遷移を確認するのにわざわざRsapberry Piに接続するのは面倒でした。 ということで個人で使用するために、 スマホ でかんたんに温度の監視やパネルヒーターの設定温度を変更するモバイルアプリを作成したので、そのレシピを紹介したいと思います。 同様のことに挑戦される方のご参考になればと思います。今回実装したコードはすべてこちらの リポジトリ に上げたので活用してください。 Pickerdot/smartterrarium 作成したもの できること 温室内の現在温度・湿度の取得 日中と夜間のパネルヒーターの稼働温度設定 4日分の温度・湿度遷移記録(15分おき)の確認 動作環境 Raspberry Pi Android スマートフォン 材料 Raspberry Pi (4 Model Bを推奨)) 温湿度センサーDHT22 スマートプラグHS105(p105でも可) パネルヒーター お手持ちの スマホ (今回は Android 機を使用) FastAPI Flutter 手順 Raspberry Pi に温湿度センサーDHT22を接続 スマートプラグHS105をLAN内に接続 パネルヒーターの電源プラグをスマートプラグHS105に接続 温湿度センサーとパネルヒーターを温室内に設置 温度によるパネルヒーターの制御プログラムを作成 温度・湿度記録用のデータベースと設定値保持用のデータベースをSQLite3で作成 制御プログラムをFastAPIを利用して、 Raspberry Pi 上でweb API 化 Flutterで操作画面を作成し、パネルヒーター制御のための API を叩く Flutterで作ったアプリを Android にUSB デバッグ でインストール 詳細 Raspberry Pi に温湿度センサーを接続 一番、つまづいたポイントはここかもしれません。GPIOピンに温湿度センサーを接続します。使用したDHT22はVCC 、DAT、GNDと3つの端子が存在しますが、VCCは3.3Vピン、DATがGPIOピン、GNDはGNDピンに接続する必要があります。 おすすめは配線しやすいので、VCCは1番ピン、DATは37番ピン(GPIO26)、GNDは29番ピンに接続することです。これらはすべて端に配置されているピンなので間違えずに配線できると思います。 ピンの配置がわからなくなったら、 Raspberry Pi のターミナルで $ pinout と実行するとこのようにピンの配置が表示されます 3V3 (1) (2) 5V GPIO2 (3) (4) 5V GPIO3 (5) (6) GND GPIO4 (7) (8) GPIO14 GND (9) (10) GPIO15 GPIO17 (11) (12) GPIO18 GPIO27 (13) (14) GND GPIO22 (15) (16) GPIO23 3V3 (17) (18) GPIO24 GPIO10 (19) (20) GND GPIO9 (21) (22) GPIO25 GPIO11 (23) (24) GPIO8 GND (25) (26) GPIO7 GPIO0 (27) (28) GPIO1 GPIO5 (29) (30) GND GPIO6 (31) (32) GPIO12 GPIO13 (33) (34) GND GPIO19 (35) (36) GPIO16 GPIO26 (37) (38) GPIO20 GND (39) (40) GPIO21 つまづいた理由は手元の Raspberry Pi 4がDHT22の出力を認識できなかったことでした。GPIOの設定は一通り確認したのですが認識せず、最初はDHT22の故障を疑っていたので arduino で動作確認したらすんなり動作しました。 しょうがないので、学生時代に買った Raspberry Pi 3 B+を引っ張り出してきて、動作確認することになりました。今度はDHT22から温度と湿度を取得できたものの、RAMが1GBの環境は快適にプログラミングできる環境でないので、その後かなりコーディングに苦戦しました。 まずは、必要なパッケージをインストールします。 $ sudo apt-get update $ sudo apt-get install build-essential python-dev さらに、DHT22の出力を認識するためのライブラリをインストールします(適当なフォルダ内で実行)。 $ sudo git clone https://github.com/adafruit/Adafruit_Python_DHT.git $ cd Adafruit_Python_DHT $ sudo python setup.py install DHT22読み取り用のプログラムを実装します。 import Adafruit_DHT as DHT import RPi.GPIO as GPIO # センサータイプの設定 SENSOR_TYPE = DHT.DHT22 # 使用するGPIOのピン番号 DHT_GPIO = 26 def read_dht(): try: humidity, temperature = DHT.read_retry( SENSOR_TYPE, DHT_GPIO) message_temp = "Temp= {0:0.1f} deg C".format(temperature) message_humidity = "Humidity= {0:0.1f} %".format(humidity) message = message_temp + ". " + message_humidity print(message) except: # センサから温度が取得できなかった際に、Nullが返るとエラーが起こるので応急処置 humidity = 0.0 temperature = 0.0 return temperature, humidity スマートプラグをLAN内に接続 こちらは公式のアプリを操作してください HS105初期設定 p105初期設定 Raspberry Pi が接続されているLANに接続できればOKです パネルヒーターの電源プラグをスマートプラグHS105に接続 パネルヒーターの電源プラグをスマートプラグに接続してください。 「 Raspberry Pi があるんだったら、リレー回路を組んで Raspberry Pi のIOから直接制御すればいいのでは?」と突っ込まれそうですが、リレー素子を購入したものの家庭用電源を自分が組んだ電気回路で制御するのは恐ろしくできませんでした。 スマートプラグは2000円程度で購入できるので、回路を組む手間を考えると素直にスマートプラグを使用したほうがいいと思います。 温湿度センサーとパネルヒーターを温室内に設置 お好きな様に設置してください。 温湿度センサーと Raspberry Pi を配線するので、 Raspberry Pi を温室の近くに設置する必要があり、温室から漏れた水で Raspberry Pi が故障しないか若干心配です。 どうせ近く設置するなら、 Raspberry Pi からの排熱も温室の加温に利用したいですね・・・。 温度によるパネルヒーターの制御プログラムを書く 今回は日中(7:00~20:00)と夜間(20:00~7:00)で設定温度を変えます。市販の サーモス タッドではなく、わざわざ Raspberry Pi でパネルヒーターを制御している理由はここにあります。ある程度夜間に温度を下げたいので、時間帯によってパネルヒーターの稼働温度を変えています( サーモス タッドも高いやつは時間帯によって設定温度を変えられますが・・・)。 というのも、温室だからといって24時間365日常夏にしたいわけじゃなくて、ある程度一日の寒暖差があった上で、冬は冬らしく枯れない程度に寒さを感じさせる必要があります。なので、この設定温度も寒さが厳しくなる1月や2月にはさらに下げようと考えています。冬に花が咲く品種もありますし、春に向けてちゃんと今が冬であることを理解できるように日中と夜間に温度差が生じるように設定します。 こちらはプラグの制御に用いたクラスの抜粋です。完成版は以下の レポジトリ で公開しています。 import Adafruit_DHT as DHT from tp_plug import * import sqlite3 # 温度センサーの出力を受けてスマートプラグを制御するためのクラス class ControlPlug: def __init__(self, threshold_day_temperature, threshold_night_temperature, over_temperature): # パネルヒーターのオンオフを管理するフラグ(0:off、1:on) self.heat_flag = 0 # 日中のパネルヒーターの稼働温度 self.threshold_day_temperature = threshold_day_temperature # 夜間のパネルヒーターの稼働温度 self.threshold_night_temperature = threshold_night_temperature # 超過温度(一度パネルヒーターが稼働すると稼働温度+超過温度を超えるまで稼働し続ける) self.over_temperature = over_temperature # 日中、夜間の切り替え温度 self.day_start_hour = 7 self.day_end_hour = 20 # 起動時に一度IOTプラグの電源を落とす print(TPLink_Plug("192.168.11.23").off()) # スマートプラグ(HS105)のスイッチング def switch_plug(self, temperature, dt_now): print("dt_now.hour:", dt_now.hour) # 夜間、ヒーターオフ時、温度低下でヒーターオン if (dt_now.hour < 7 or 19 < dt_now.hour) and temperature < self.threshold_night_temperature and self.heat_flag == 0: self.heat_flag = 1 print("night_on") print(TPLink_Plug("192.168.11.23").on()) # 昼間、ヒーターオフ時、温度低下でヒーターオン if (7 < dt_now.hour < 19) and temperature < self.threshold_day_temperature and self.heat_flag == 0: self.heat_flag = 1 print("day_on") print(TPLink_Plug("192.168.11.23").on()) # 夜間、ヒーターオン時、温度上昇でヒーターオフ if (dt_now.hour < 7 or 19 < dt_now.hour) and temperature > (self.threshold_night_temperature + self.over_temperature) and self.heat_flag == 1: self.heat_flag = 0 print("night_off") print(TPLink_Plug("192.168.11.23").off()) # 昼間、ヒーターオン時、温度上昇でヒーターオフ if (7 < dt_now.hour < 19) and temperature > (self.threshold_day_temperature + self.over_temperature) and self.heat_flag == 1: self.heat_flag = 0 print("day_off") print(TPLink_Plug("192.168.11.23").off()) return self.heat_flag over_temperature は温度が稼働温度を下回って、パネルヒーターがonになった後に温度が稼働温度を超えても加温し続けるための超過温度の設定値です。 あくまで稼働温度はこれ以上温度が下がると困るという値なので、パネルヒーターにより、温度が稼働温度を超えても余分に加温し続けるように設定します(パネルヒーターがoffになったあとすぐに温室内の温度が設定値を下回るのを防ぐためです)。 パネルヒーターが稼働しているかの判断はスマートプラグより情報を取得するのが難しかったので、 heat_flag という変数でパネルヒーターの稼働状況を保持しています。 ということで、スイッチングのif文の条件式がやや複雑になってます。 スマートプラグの制御モジュール(tp_plug.py)は、こちらのページの コードー を使用させていただきました。 コスパ最強IoT家電!TPLink製品をRaspberryPiから操作 温度・湿度記録用のデータベースと設定値保持用のデータベースをSQLite3で作成 半分終了しました。疲れていませんか?自分は9月の中旬に材料を買ったのですが、ここまでたどり着いたときには11月になっていました。 アドベントカレンダー にこのネタを投稿することが決まったので、少しペースを上げましたが、それでも今ギリギリになって記事を書いています。 このアプリの目玉機能にこれまでの温度・湿度記録を確認できるというものがあります。パネルヒーターがちゃんと動作しているのか確認するという意味合いもありますが、温室内の植物がある日突然調子を崩した際に原因を推測する手がかりになるので、個人的には必ず盛り込みたい機能でした。 今回は物理的な加湿機構を温室内に作成するのが間に合わなかったので湿度の調整はできませんが、温室内の植物は70%~に湿度を保つ必要があり、一瞬でも下回るとそれだけでダメージを負ってしまうので24時間の監視が重要になります。 データを保持するテーブルはシンプルなものにしました( カラム名 の 命名 がイケてない気がしますが気にしないことにします)。 温度・湿度記録テーブル(environmental_record) カラム名 内容 データ型 制約 measurement_time 計測日時 TEXT PRIMARY KEY temperature 計測温度 float NOT NULL humidity 計測湿度 float NOT NULL heat_flag パネルヒーター稼働フラグ int NOT NULL 設定値テーブル(parameter_set) カラム名 内容 データ型 制約 update_at 更新日時 TEXT PRIMARY KEY threshold_day_temperature 日中稼働温度 float NOT NULL threshold_night_temperature 夜間稼働温度 float NOT NULL diff_temperature 超過温度 float NOT NULL 作業的にはまず、 Raspberry Pi にSQLite3をインストールするところから始めます。 $ sudo apt install sqlite3 -y 次に、pipでSQLite3のモジュールをインストール。 $ pip install pysqlite3 次に タイムゾーン 設定のためのモジュールをインストール。 $ pip install pytz データベースの初期化用に以下のプログラムを作成しました。こちらのプログラムを一回実行すれば、同じ階層にSQLite3のデータベースが作成されます。 init_sql.py import sqlite3 import datetime import pytz dbname = 'smart-terrarium.db' # コネクタ作成。dbnameの名前を持つDBへ接続する。 conn = sqlite3.connect(dbname) cur = conn.cursor() # 温度・湿度記録用テーブルをリセットする # cur.execute('DROP TABLE IF EXISTS environmental_record') # テーブルの作成 cur.execute('CREATE TABLE environmental_record(measurement_time TEXT PRIMARY KEY,temperature float NOT NULL,humidity float NOT NULL,heat_flag int NOT NULL);') cur.execute('CREATE TABLE parameter_set(update_at TEXT PRIMARY KEY,threshold_day_temperature float NOT NULL,threshold_night_temperature float NOT NULL,diff_temperature float NOT NULL)') dt_now = datetime.datetime.now(pytz.timezone('Asia/Tokyo')) measurement_time = dt_now.strftime('%Y-%m-%d %H:%M:%S') parameter_data = [(measurement_time, 25.0, 22.0, 3.0)] cur.executemany("insert into parameter_set values (?, ?, ?, ?)", parameter_data) # 処理をコミット conn.commit() conn.close() これで、データベースの準備が整いました。 ここから、データの読み書き用のクラスを作成します。 実装するのは設定値の読み出し、更新。温度・湿度記録の読み出し(1レコード、複数レコード)、追加のためのプログラムです。 全部ここに書くと長いので、一部抜粋して紹介します。 utility.py # データの読み書き用のクラス class ReadWriteDB(): def __init__(self): self.dbname = 'smart-terrarium.db' # parameter_set読み出し def read_parameter_set(self): conn = sqlite3.connect(self.dbname) cur = conn.cursor() data = cur.execute( 'select * from parameter_set order by update_at desc limit 1') latest_record = data.fetchall() threshold_day_temperature = latest_record[0][1] threshold_night_temperature = latest_record[0][2] over_temperature = latest_record[0][3] conn.commit() conn.close() return threshold_day_temperature, threshold_night_temperature, over_temperature # 温度・湿度記録追加 def update_temperature_humidity(self, measurement_time, temperature, humidity, heat_flag): conn = sqlite3.connect(self.dbname) cur = conn.cursor() environmental_data = [ (measurement_time, temperature, humidity, heat_flag)] cur.executemany( "insert into environmental_record values (?, ?, ?, ?)", environmental_data).fetchall() conn.commit() conn.close() # 1 レコード取得 def read_one_records(self): # コネクタ作成 conn = sqlite3.connect(self.dbname) cur = conn.cursor() # 値の取得 data = cur.execute( 'select * from environmental_record order by measurement_time desc limit 1') records = data.fetchall() print(records[0][1]) return_value = { 'timestamp': records[0][0], 'temperature': records[0][1], 'humidity': records[0][2], 'heat_flag': records[0][3] } return return_value 制御プログラムをFastAPIを利用して、 Raspberry Pi 上でwebAPI化 いよいよ実装も佳境に入ってきました。 これまでに実装した Python 側の制御プログラムをwebAPIとして実装するためのプログラムをFastAPIで作成します。 FastAPIは Python でRESTful API を構築するためのマイクロ フレームワーク です。 FastAPIを使用するのは今回が初めてなのですが、RAMが1GBしかない Raspberry Pi 3 B+で実装しながら動かすので、 とにかく軽いこと、そして日本語のドキュメントが存在していること、最近 SNS 界隈で噂になっていることがFastAPIの選定理由でした。 実際利用してみると、 Raspberry Pi 3 B+でも快適に動かすことができました。これが、同時にリク エス トが飛んできた場合どうなるかわかりませんが、今回はLAN内で自分しか使用しないアプリを作るので問題ないです。 これまでに同じ Raspberry Pi で Django とSpringで作ったwebアプリを動かしたことがありました。普通に起動はしてくれるものの、サーバーが立ち上がっている際に、同時にエディターでコードの編集をしているともっさりしてまともにコーディングできませんでしたが、FastAPIはwebサーバーが起動していることを感じさせない使用感でした。 軽さよりも気に入ったのは、実装の簡単さですね。デ シリアライズ もルーティングも直感的に実装できるのが良かったです。きっと何十個もエンドポイントを用意したり、複雑な JSON をやり取りするには物足りないこともあるのかもしれませんが、日曜プログラミングにはぴったりな フレームワーク だと思います。 FastAPI公式ドキュメント まずはFastAPIと必要なモジュールをインストールします pip install fastapi pip install fastapi-utils uvicorn もインストール。 pip install "uvicorn[standard]" 実装は例の リポジトリ にまとめてあります。 ここからはFastAPIの気に入ったところだけまとめます。 smartterrarium.py # FastAPI app = FastAPI() @app.get("/") async def root(): return {"message": "This is the smart terrarium"} webサーバを立ち上げるのに必要な記述はこれだけ。あとは、 uvicorn smart-terrarium:app --reload とコマンドを実行するだけなのがいいですね。 フルスタックフレームワーク では、こうは行きませんよね。 15分おきに温度と湿度を出力する部分は以下のように実装しています。 from fastapi_utils.tasks import repeat_every # 定期実行用の関数 @app.get("/rest/") @app.on_event("startup") @repeat_every(seconds=60*15) def heater_control(): # 温度・湿度取得 temperature, humidity = control_plug.read_dht() リク エス トボディーのデ シリアライズ 。 # parameter_setのリクエストボディー class ParameterSet(BaseModel): day_temperature: int night_temperature: int diff_temperature: int # 設定値テーブル更新 @app.post("/update_parameter_set/") async def update_parameter_set(parameter_set: ParameterSet): dt_now = datetime.datetime.now(pytz.timezone('Asia/Tokyo')) measurement_time = dt_now.strftime('%Y-%m-%d %H:%M:%S.%f') read_write_db.update_parameter_set( measurement_time, int(parameter_set.day_temperature), int(parameter_set.night_temperature), int(parameter_set.diff_temperature)) return {"message": "ok", "day_temperature": parameter_set.day_temperature, "night_temperature": parameter_set.night_temperature, " diff_temperature": parameter_set.diff_temperature} ここらへんは、マイクロ フレームワーク としては標準的な機能だと思いますが(他のマイクロ フレームワーク を触ったことがないのでわからない)、AI系のプロジェクトにはかなり相性がいいなと感じました。 機械学習 モデルを実運用するとなると、だいたいPoCでモデルを作ってからあとで、webAPI化という話になりますが、あとからデータの入り口と出口としてさくっと API を実装できる感覚がすごく気に入りました。 あと、自動ドキュメント生成や今回は使用しませんでしたがデータベースとの接続もサポートされているのもいいですね。 自分がデータサイエンティストとして、日ごろ関わっているプロジェクトでも最近PoCを終えて実運用に入ろうとしているプロジェクトがありますが、その際に必要なのは既存のシステムに組み込まれて、モデルやデータ処理 スクリプト を運用するだけの API なのでFastAPIで十分です。 今回は初歩的な機能しか使用しませんでしたが、もうちょっと重厚なアプリケーションを実装すればその恩恵をもっと感じられるのではないでしょうか? Flutterで操作画面を作成し、パネルヒーター制御 API を叩く ここまで読んでいただいた方は、すでにお腹いっぱいだと思いますが、書いてる僕はもうヘトヘトです。ただ、最後に一番重いやつが残ってるんですよね。何から説明すればいいんでしょか? 何をしたかというと、 Android Studio でFlutterを使ってモバイルアプリを実装しました。画面は3つの本当にシンプルなアプリです。 モバイルアプリにした理由は Raspberry Pi でフロントエンドのwebサーバーを立ち上げたくなかったからです。気軽に温室の管理ができるのが今回のアプリのコンセプトなので、 スマホ から使う ユースケース を想定していたのですが、描画処理をRAMが1 MBの Raspberry Pi で行うよりは、豊富な計算リソースを活用できる スマートフォン を利用したほうがいいと判断しました。 Flutterを使った理由はいままで使ったことがなかったからです。React Nativeは以前 チュートリアル レベルのアプリを実装したことがあったのですが、Flutterは全く触ったことがありませんでした。ということで、ちょうどいいので触ってみようと思ったわけです。 Flutterのアプリのコードは こちら にあります。 現在温度表示画面 シンプルな現在温度と湿度の表示画面です。 こだわったところはとにかくUIの可愛さです。自分しか使わないアプリなので自分が可愛いと感じるデザインにしました。 ここの実装は特に困った部分はなかったです。Flutterのデフォルトのプロジェクトをいじるだけで実装できると思います。 パネルヒーター稼働温度設定画面 左右のプラス、マイナスボタンでパネルヒーターが稼働する設定温度を変更できます。 この画面が一番苦労した画面かもしれません。 プラスorマイナスボタンが押される ↓ Raspberry Pi 上の API が叩かれて、設定温度が変更される ↓ 現在の設定温度を Raspberry Pi 上の API から取得 ↓ 再描画 という順番で処理が進むのですが、再描画の際に現在の設定温度が取得されずにエラーを吐くという事態が頻発して、自分は全然非同期処理についてわかってないなと痛感しました。 温度遷移履歴確認画面 これまでの温度と湿度の記録をスクロールして確認できます。 ここが一番ざんねんな画面です。本当は取得期間を設定したり、折れ線グラフを描画したりしたかったんですが、力尽きました。ここについては年末にでも時間があったら実装します。 Flutterで作ったアプリを Android にUSB デバッグ でインストール 最後のステップです。 作ったアプリを手元の スマホ にインストールします。 あくまで個人利用が目的なので、USB デバッグ でインストールするという一番お手軽な方法を取りました。 やり方はかんたんで、 Android スマホ を開発者モードにしてからUSB デバッグ を有効にします。 そしたら、アプリを開発している スマホ に接続して Android Studio の実行先として スマホ を選ぶだけです。 インストールには10分程度時間がかかりました。 最後に 今回作ったアプリですが、個人的にものすごく重宝しています。 自分が前々から欲しかったアプリを形にしたので当たり前ですが、毎日使用してますね。やっぱり気軽に触れるUIがあることはアプリケーションの利便性を格段に向上させますし、何より頻繁に使ってもらえるアプリになりますね。 ただ追加したい機能はもっとあって、まずは加湿や送風の制御がしたいです。さらに、 Raspberry Pi をインターネット側からアクセス可能にして、カメラで中の様子を視覚的に確認できるようにすれば、旅行先から温室の様子を確認することが可能になります。 あと個人向けには需要がないと思いますが、農業まで視野を広げれば、画像認識AIを導入して、調子を崩した株の早期発見もできると思いますし、温室から取得できるデータを活用して収穫物の品質を向上させるなどの応用ができます。 このアプリを実装していて、就活時に農業向けのITプロジェクトを実施している会社に就職したいと考えていたことを思い出しました。当時は農業案件を実施している企業が少なくて、諦めてしまいましたが、いつかISIDで農業系のプロジェクトに挑戦したいですね。 ということで、この件についてお話がある社内外の方は、ぜひ徳原にお話しください。お役に立てることがあるかもしれません。 この長々とした記事をここまで読んでくださった方、本当にありがとうございます。植物×ITのネタはまたいつか投稿したいと思うので、その際も読んでいただければと思います(AITCwebサイトではAIやデータサイエンスに関して コラム を書いています)。 また、AITCでは新卒、中途問わず一緒に働いてくださる方を絶賛募集中です。 こちらの 採用情報ページ からカジュアル面談の申込みを受け付けています。 それでは。 参考文献 温度/湿度センサーDHT22をRaspberry Piで使用する方法 コスパ最強IoT家電!TPLink製品をRaspberryPiから操作 FastAPI公式ドキュメント 執筆: @tokuhara.hikaru 、レビュー: @yamashita.tsuyoshi ( Shodo で執筆されました )
最近、朝目覚めた時の外の光がキラキラとしていて、空気が澄んでいるのを感じています。UXデザインセンターのふじさきです。 先日、このISIDのテックブログがリニューアルしました! テックブログを刷新するにあたり、デザイン担当としてお手伝いをしました。 デザインを変えるときは、色やフォントを思いつきのままに描いていくのではなく、意味や理由があって成り立つものだと考えています。 今回は、リニューアルにあたって押さえておくべきデザインの事前整理ポイントに注力しながら進めていきました。その一部始終をお伝えしたいと思います。 ことのはじまり ターゲットの解像度を上げる 現状の悩みごと/現状のテックブログをつくるときに考えたことを整理 テックブログで重視したいことを整理 競合調査とベンチマーク デザインを形にする 最後に ことのはじまり 「ISIDのテックブログが、特にシンプルすぎるので、ぜひご協力を……」 ある日、社内からこんな声かけがありました。 (当時のテックブログサンプル画面↓) 「よっしゃ、やってやろうじゃないの」と意気揚々と参戦した私は、まず事前情報をインプットするために状況整理からはじめました。 やったことは以下の4点。 ターゲットの整理 現状の悩みごとを整理 現状のテックブログをつくるときに考えたことを整理 テックブログで重視したいことを整理 これらをテックブログ事務局の4名と、miroを使いながら MTG 形式で対話していきました。 ターゲットの解像度を上げる ISIDテックブログの主なユーザーターゲットはこちらの3つです。 採用候補者 参照するISID社員 執筆するISID社員 ここからさらにターゲットたちの解像度を上げることで、ブログそのものの存在意義につながり、デザインの根拠にもひもづきます。 どんなテックブログであるべきなのか、以下の質問を投げかけながら 言語化 を進めていきました。 このターゲットは何を知りたくてテックブログを見るのか? テックブログを通して、ターゲットに何を持ち帰ってもらえたら嬉しいか? 執筆するISID社員が執筆したくなるモチベーションは何か? この整理の中で、事務局のみなさんの思いに触れることができたのは、私にとって大きな発見でした。 1つめの問いで、「 等身大のISID 」というキーワードがでてきました。ここから派生し、「ISIDのテックブログでは新しいことを書かなくてもいい、最新性でなく、仕事への熱量、やりがい、楽しさが伝わるといい」という話が展開されました。 私はてっきりテックブログ=最新技術を研究して、その中身を発信しているばかりだと思ってました(もちろんそういう記事もあります)が、それよりも 社員自身が楽しんでいる様子やオリジナルな部分を大事にしている 、という思いを知ることができたのです。 そのおかげで、この後に続くデザインの方針やコンテンツ提供のスタイルに関して、軸がぶれないように進めることができました。 現状の悩みごと/現状のテックブログをつくるときに考えたことを整理 次に、現状の悩みごとを洗い出していってもらいました。その中身は、「 アイキャッチ 画像」「ヘッダー画像」といった画像まわりのお悩みが目立ちました。 その後、現状のテックブログをつくるときに考えていたことを洗い出してみると・・・ スマホ でひまつぶしにちょっと読む はてなブログ の標準機能でつくる(運用や実装負荷の軽減) JavaScript をゴリゴリ書かない 画像の作りこみはしない などなど といったように、お悩みとつくるときの方針が表裏していたんです。 つまり、サクサクと記事の内容を読むことができる、記事内容に集中できるようにするために、凝った表現を極力削ぎ落とし、読み込み速度を最適にするためにつくられていました。 この方針はとても大事な観点です。画像やギミックで華やかに演出することもできますが、それで記事の内容が見づらかったり、読み込みに待たされるブログを、ターゲットユーザーは果たして望んでいるのでしょうか・・・? テックブログで重視したいことを整理 そんな問いかけについてみなさんで議論し、改めてターゲットユーザーについて考え、届けたいことを見直しながら、最終的には 「理解しやすい」「ISIDらしさ」 といった点を重視してテックブログを発信していこうという結論になりました。 これまでの話を踏まえて整理した、テックブログで重視したいこと 見つけやすくて理解しやすい 他の読者の反応がわかる いずれにも魅力的(継続的にアクセスしたい、かつ、ISIDらしさ・独自性がある)と思われたい 独自性・ISIDらしさ=自由さ、楽しんで仕事してる 視認性・検索性・カテゴライズ 見た目がモダンで親しみやすい などなど 具体的なやりざまとしては、以下のような方向性でリニューアルを進めていきました。 コンテンツの内容を極力わかりやすい形にレイアウトしなおす ISIDのコーポレートカラーであるグレーをつかいながらも、単調にならないデザインにする 画像による演出はするものの、表示速度や目にやさしい画面デザインにする 競合調査と ベンチマーク テックブログを新しくするにあたり、他社動向を把握しておくことも大事なプロセスのひとつです。巷のテックブログを マッピング し、我々のテックブログはどのあたりをターゲットとするか議論しました。 ここで主に焦点があたったのは情報のレイアウトについて。 ISIDのもともとのテックブログは記事が長めに掲載された状態で縦並びになっていました。 2列がいいのか?3列がいいのか?そもそも ウィジェット 類はどのくらい載せる?といった検討事項がたくさん飛び交いました。 ここでとった整理の方法は、 コンテンツの優先度を決める ことです。 テックブログ事務局のみなさんと議論を交わしながら、「ISIDのテックブログは 記事命 でいく」「その 記事がたくさんあることを表現 していく」という大方針になりました。 ここが決まれば、取捨選択は円滑に進みます。情報はなるべく多く見えるように3列表示にする、 ウィジェット はページの後半に載せる、と具体的に実施するレイアウト方針もすんなり決まっていきました。 デザインを形にする 方針が決まれば、あとは画面のデザインです。いままでの内容を考慮しデザインを複数案作成し、どの案がいいかを決定し、細かい部分を調整していきました。 そうしてできあがったのが現状のデザインです。(下のデザイン案だと左案が近しい形です) 今回のデザインで気をつけたことは、なるべく標準に合わせ開発の負担をへらす(ようにしたけど実際はどうだったかな)、ISIDらしい誠実さもありながら記事ひとつひとつを丁寧に扱うデザインにする、という2点です。 前段の4つの整理がなければ、この点にたどり着けませんでした。 みんなの意見をまとめ、整理して納得しながら進めていくことで、最終的にたどり着いた形も満足度の高いものに仕上げることができたと思います。 実装に関する具体的な内容については こちらの記事 でご紹介しています。 最後に 今回はデザインの整理ポイントを中心に、リニューアルの経緯をお話させていただきました。 ISIDのテックブログはとにかく中身で勝負!しております。きっと等身大のISIDを垣間見れると思いますので、是非ご一読くださいませ。 執筆: @g-uxdc 、レビュー: @higa ( Shodo で執筆されました )
最近、朝目覚めた時の外の光がキラキラとしていて、空気が澄んでいるのを感じています。UXデザインセンターのふじさきです。 先日、このISIDのテックブログがリニューアルしました! テックブログを刷新するにあたり、デザイン担当としてお手伝いをしました。 デザインを変えるときは、色やフォントを思いつきのままに描いていくのではなく、意味や理由があって成り立つものだと考えています。 今回は、リニューアルにあたって押さえておくべきデザインの事前整理ポイントに注力しながら進めていきました。その一部始終をお伝えしたいと思います。 ことのはじまり ターゲットの解像度を上げる 現状の悩みごと/現状のテックブログをつくるときに考えたことを整理 テックブログで重視したいことを整理 競合調査とベンチマーク デザインを形にする 最後に ことのはじまり 「ISIDのテックブログが、特にシンプルすぎるので、ぜひご協力を……」 ある日、社内からこんな声かけがありました。 (当時のテックブログサンプル画面↓) 「よっしゃ、やってやろうじゃないの」と意気揚々と参戦した私は、まず事前情報をインプットするために状況整理からはじめました。 やったことは以下の4点。 ターゲットの整理 現状の悩みごとを整理 現状のテックブログをつくるときに考えたことを整理 テックブログで重視したいことを整理 これらをテックブログ事務局の4名と、miroを使いながら MTG 形式で対話していきました。 ターゲットの解像度を上げる ISIDテックブログの主なユーザーターゲットはこちらの3つです。 採用候補者 参照するISID社員 執筆するISID社員 ここからさらにターゲットたちの解像度を上げることで、ブログそのものの存在意義につながり、デザインの根拠にもひもづきます。 どんなテックブログであるべきなのか、以下の質問を投げかけながら 言語化 を進めていきました。 このターゲットは何を知りたくてテックブログを見るのか? テックブログを通して、ターゲットに何を持ち帰ってもらえたら嬉しいか? 執筆するISID社員が執筆したくなるモチベーションは何か? この整理の中で、事務局のみなさんの思いに触れることができたのは、私にとって大きな発見でした。 1つめの問いで、「 等身大のISID 」というキーワードがでてきました。ここから派生し、「ISIDのテックブログでは新しいことを書かなくてもいい、最新性でなく、仕事への熱量、やりがい、楽しさが伝わるといい」という話が展開されました。 私はてっきりテックブログ=最新技術を研究して、その中身を発信しているばかりだと思ってました(もちろんそういう記事もあります)が、それよりも 社員自身が楽しんでいる様子やオリジナルな部分を大事にしている 、という思いを知ることができたのです。 そのおかげで、この後に続くデザインの方針やコンテンツ提供のスタイルに関して、軸がぶれないように進めることができました。 現状の悩みごと/現状のテックブログをつくるときに考えたことを整理 次に、現状の悩みごとを洗い出していってもらいました。その中身は、「 アイキャッチ 画像」「ヘッダー画像」といった画像まわりのお悩みが目立ちました。 その後、現状のテックブログをつくるときに考えていたことを洗い出してみると・・・ スマホ でひまつぶしにちょっと読む はてなブログ の標準機能でつくる(運用や実装負荷の軽減) JavaScript をゴリゴリ書かない 画像の作りこみはしない などなど といったように、お悩みとつくるときの方針が表裏していたんです。 つまり、サクサクと記事の内容を読むことができる、記事内容に集中できるようにするために、凝った表現を極力削ぎ落とし、読み込み速度を最適にするためにつくられていました。 この方針はとても大事な観点です。画像やギミックで華やかに演出することもできますが、それで記事の内容が見づらかったり、読み込みに待たされるブログを、ターゲットユーザーは果たして望んでいるのでしょうか・・・? テックブログで重視したいことを整理 そんな問いかけについてみなさんで議論し、改めてターゲットユーザーについて考え、届けたいことを見直しながら、最終的には 「理解しやすい」「ISIDらしさ」 といった点を重視してテックブログを発信していこうという結論になりました。 これまでの話を踏まえて整理した、テックブログで重視したいこと 見つけやすくて理解しやすい 他の読者の反応がわかる いずれにも魅力的(継続的にアクセスしたい、かつ、ISIDらしさ・独自性がある)と思われたい 独自性・ISIDらしさ=自由さ、楽しんで仕事してる 視認性・検索性・カテゴライズ 見た目がモダンで親しみやすい などなど 具体的なやりざまとしては、以下のような方向性でリニューアルを進めていきました。 コンテンツの内容を極力わかりやすい形にレイアウトしなおす ISIDのコーポレートカラーであるグレーをつかいながらも、単調にならないデザインにする 画像による演出はするものの、表示速度や目にやさしい画面デザインにする 競合調査と ベンチマーク テックブログを新しくするにあたり、他社動向を把握しておくことも大事なプロセスのひとつです。巷のテックブログを マッピング し、我々のテックブログはどのあたりをターゲットとするか議論しました。 ここで主に焦点があたったのは情報のレイアウトについて。 ISIDのもともとのテックブログは記事が長めに掲載された状態で縦並びになっていました。 2列がいいのか?3列がいいのか?そもそも ウィジェット 類はどのくらい載せる?といった検討事項がたくさん飛び交いました。 ここでとった整理の方法は、 コンテンツの優先度を決める ことです。 テックブログ事務局のみなさんと議論を交わしながら、「ISIDのテックブログは 記事命 でいく」「その 記事がたくさんあることを表現 していく」という大方針になりました。 ここが決まれば、取捨選択は円滑に進みます。情報はなるべく多く見えるように3列表示にする、 ウィジェット はページの後半に載せる、と具体的に実施するレイアウト方針もすんなり決まっていきました。 デザインを形にする 方針が決まれば、あとは画面のデザインです。いままでの内容を考慮しデザインを複数案作成し、どの案がいいかを決定し、細かい部分を調整していきました。 そうしてできあがったのが現状のデザインです。(下のデザイン案だと左案が近しい形です) 今回のデザインで気をつけたことは、なるべく標準に合わせ開発の負担をへらす(ようにしたけど実際はどうだったかな)、ISIDらしい誠実さもありながら記事ひとつひとつを丁寧に扱うデザインにする、という2点です。 前段の4つの整理がなければ、この点にたどり着けませんでした。 みんなの意見をまとめ、整理して納得しながら進めていくことで、最終的にたどり着いた形も満足度の高いものに仕上げることができたと思います。 実装に関する具体的な内容については こちらの記事 でご紹介しています。 最後に 今回はデザインの整理ポイントを中心に、リニューアルの経緯をお話させていただきました。 ISIDのテックブログはとにかく中身で勝負!しております。きっと等身大のISIDを垣間見れると思いますので、是非ご一読くださいませ。 執筆: @g-uxdc 、レビュー: @higa ( Shodo で執筆されました )
はじめに BIツールで使い易いテーブルについて 設計ドキュメントからBIツールのモデルファイルを自動生成する LookMLのラベル機能 caseによるラベリング LookMLの継承機能 Viewファイル自動生成ツールの実装 ビルドスクリプト main関数の実装 Excelのパーズ処理 Viewファイルの書き出し処理 まとめ はじめに このエントリは、 Looker Advent Calendar 2022 における17日目の記事です。 本日は、基幹システムのような歴史的な蓄積がある既存のデータを後付けで可視化する際に問題となる項目のラベリングに関するノウハウを説明します。 蓄積のあるデータベースに対してLookerのようなBIツールを直接接続するだけではうまく分析できません。 それには、様々な理由があります。その中の一つはテーブル名や カラム名 が システム開発 にとって都合の良い表現になっており、BIツールにとって都合の良い表現にはなっていないことです。 例えば、以下のように既存のデータベースから何も変更せずにLookerにテーブルをインポートすると以下のようになります。 テーブル名や カラム名 にアルファベットとアンダースコアのみを使ってテーブルを定義することになっているシステムは多いんじゃないでしょうか。これは、現代的なウェブシステムの多くがエンドユーザーに対して直接データベースへのアクセスを許可するような構造にはなっていないからです。 BIツールで使い易いテーブルについて BIツールを使ってデータベースをエンドユーザーが直接見に行くことを前提に、使い易いテーブルとはどんなものか考えてみましょう。 まずテーブル名や カラム名 が問題領域に即した日本語表記になっていることは必須でしょう。そして、 カラム名 については、その意味や意図を簡易的に参照できるとより望ましいですね。 また、実装の都合で場合分けするために保存されている項目については、無味乾燥な数字やアルファベットではなく論理的な名称になっていることが望ましいでしょう。 例えば、私たちのシステムには、 ms_biz_prtnr というテーブルがあり、 hojin_kbn というカラムが含まれています。そこには、 01 、 02 、 03 などの値が保存されます。正直言って、このままでは意味が分かりませんよね。 これは 取引先マスタ というテーブルにある 法人区分 というカラムの話をしています。そして三つの値は、それぞれ 株式会社 、 有限会社 、 財団法人 を意味しています。 使い込まれた基幹システムには、恐らく数百から数千のテーブルがあり、その中に含まれるカラムは延べ数千にも及ぶでしょう。それをBIツールで分析できるようにするために、このような変換処理を手動で行っていると時間がいくらあっても足りません。 当然、基幹システム側は変化する会社の状況に応じて変更されていくわけですから、BIツールの都合でデータベースに対する変更を止められるはずもありません。 では、どうするのがいいでしょうか? 設計ドキュメントからBIツールのモデルファイルを自動生成する あまりに多くのテーブルを手作業でBIツール用に再定義していては、データ分析による価値を生みだす前にコストがかかりすぎてしまいます。分析環境の構築に大きなコストをかけてしまうと、それだけ大きな期待がかかります。 小さく始めて、徐々に大きな成果に結びつけるならできるかぎりコストをかけずに環境を作るのが望ましいでしょう。また、様々なBIツールが市場にはありますので、最適なものを目指して乗り換えていけるようにしたいものです。 というわけで、既存の設計成果物であるデータベース設計書からBIツール用のモデルファイルを自動生成しましょう。ISIDの場合は、テーブル設計書という Excel で記述された大量のファイルがありましたので、これを使います。 このファイルフォーマットについては詳細を説明しませんが、何かを感じ取って貰えると嬉しいです。 モデルファイルのフォーマットはできるかぎりテキスト形式のもので、その形式に関して仕様が公開されているものを使うのが良いでしょう。この条件を満たすBIツールとしてLookerがあります。 LookerのLookMLは yaml によく似たテキスト形式のファイルなので、gitや GitHub を使って簡単に構成管理できます。 特に説明はしませんが、LookMLに対するイメージを持ってもらうために少しだけ例をお見せしましょう。 ecommercestore.model.lkml connection: order_database include: "*.view.lkml" explore: orders { join: customers { sql_on: ${orders.customer_id} = ${customers.id} ;; } } orders.view.lkml view: orders { dimension: id { primary_key: yes type: number sql: ${TABLE}.id ;; } dimension: customer_id { sql: ${TABLE}.customer_id ;; } dimension: amount { type: number value_format: "0.00" sql: ${TABLE}.amount ;; } dimension_group: created { type: time timeframes: [date, week] sql: ${TABLE}.created_at ;; } measure: count { type: count # creates sql COUNT(orders.id) sql: ${id} ;; } } 見ての通りごく単純な構造のテキストファイルです。単純な文字列処理ができれば、このファイルを自動生成できることがお分かりいただけることでしょう。 LookMLのさらに詳細な説明は公式のドキュメントが分かり易いのでおすすめです。 LookMLの紹介 LookMLのラベル機能 自動生成するにあたって、押さえておくべきLookMLの機能を紹介しましょう。 まずは、ラベリング機能です。 といっても、それほど難しいことはありません。viewやdimensionには label というフィールドがあるので、そこに分かり易い名前を指定するだけです。 view: ms_biz_prtnr { label: "取引先マスタ" sql_table_name: `MS_BIZ_PRTNR`;; dimension: biz_prtnr_cd { label: "取引先コード" type: string sql: ${TABLE}.biz_prtnr_cd;; } ラベルを使うとこのようになります。分かり易くなりますね。 ラベルについてより詳細な仕様を知りたい方は公式ドキュメントをご覧ください。 label (for fields) caseによるラベリング テーブル名や カラム名 のラベリングが分かったら今度は値のラベリングです。 ここで使うのは、caseとwhenを組み合わせた条件分岐です。 view: ms_biz_prtnr { label: "取引先マスタ" sql_table_name: `MS_BIZ_PRTNR`;; dimension: hojin_kbn { label: "法人区分" type: string case: { when: { sql: ${TABLE}.hojin_kbn = "01";; label: "株式会社" } when: { sql: ${TABLE}.hojin_kbn = "02";; label: "有限会社" } when: { sql: ${TABLE}.hojin_kbn = "03";; label: "財団法人" } } } それぞれの値に対して、どんなラベルを表示するのか設定していますね。これを元にLookerは SQL のcase句を生成します。 ラベルのありなしで、どんな風に表示が変わるのか見てみましょう。データを見る人が良く使うであろう検索フィルターの表示を抜き出してあります。 ラベルを設定しない場合には、データベースに保存されている具体的な値である 01 や 02 といった値がどんな意味を持つのか詳細に理解していないとデータをフィルターできません。 一方でラベルを設定してあれば、候補の中から選ぶだけなので使い易くなります。 caseによるラベリングについてより詳細な仕様を知りたい方は公式ドキュメントをご覧ください。 case LookMLの継承機能 既存の設計ドキュメントからviewファイルを自動生成する際には、手書きするファイルと自動生成するファイルは分割するべきです。 そういう時に使える機能が継承(extends)です。継承を使うと複数のview定義を合成して一つのviewにできます。 まずは、継承元となるviewを /generated/views/__ms_biz_prtnr.view というパスに定義します。 内容としては、ラベルの宣言だけを並べたものです。この例ではviewの名前にすこし癖がありますが、自動生成を意図して奇妙な印象を受けるようにしてあります。 view: __ms_biz_prtnr { label: "取引先マスタ" # 1. dimension: biz_prtnr_cd { label: "取引先コード" # 2. } } 継承する側のview定義を見てみましょう。以下のようなコードになります。 include: "/generated/views/__ms_biz_prtnr.view" # 1. view: ms_biz_prtnr { extends: [__ms_biz_prtnr] # 2. sql_table_name: `MS_BIZ_PRTNR`;; dimension: biz_prtnr_cd { type: string sql: ${TABLE}.biz_prtnr_cd;; } } 継承元となるviewのファイルパスを指定してincludeします。これによって、分かれたファイルが単一のファイルであるかのように扱われます。 継承元となるviewの名前を指定します。 これによって、 ms_biz_prtnr と __ms_biz_prtnr を一つのviewとして混ざり合った状態になりました。結果的に以下のような宣言のviewになります。 view: ms_biz_prtnr { label: "取引先マスタ" # __ms_biz_prtnr の 1. から取りこまれる sql_table_name: `MS_BIZ_PRTNR`;; dimension: biz_prtnr_cd { label: "取引先コード" # __ms_biz_prtnr の 2. から取りこまれる type: string sql: ${TABLE}.biz_prtnr_cd;; } 継承についてより詳細な仕様を知りたい方は公式ドキュメントをご覧ください。 extends (for views) Viewファイル自動生成ツールの実装 今回は Java を使ってviewを定義するファイルを自動生成します。 なお、 Java を使った開発環境の構築方法については以前に書いた KotlinとGradleで始めるモダンなビルド環境 を参考にしてください。 ビルド スクリプト 依存ライブラリについて説明するために、ビルド スクリプト を紹介しましょう。 plugins { application } repositories.mavenCentral() dependencies { implementation("org.apache.poi:poi:5.2.3") // 1. implementation("info.picocli:picocli:4.7.0") // 2. annotationProcessor("info.picocli:picocli-codegen:4.7.0") // 2. } application { mainClass.set("jp.co.isid.example.looker.App") // 3. } Java で Excel ファイルを読みだすのであれば、実績のある Apache POIがおすすめです。 コマンドライン アプリケーションの引数をパーズするライブラリとしてはpicocliを使うと アノテーション を付けるだけでやりたいことが実現できるので非常におすすめです。 main関数を定義したクラスのクラス名を設定しています。 main関数の実装 picocliを使ったアプリケーションのブートストラップ部分の実装を紹介しましょう。 package jp.co.isid.example.looker; import picocli.CommandLine; import java.nio.file.*; import java.util.List; import java.util.concurrent.Callable; @CommandLine.Command(name = "modelgen", // 1. description = "generate looker view models") public class App implements Callable<Integer> { @CommandLine.Option(names = {"-o", "--output"}, // 2. description = "output dir of generated resources", // 2. defaultValue = "build/generated/views/") // 2. Path outputDir; @CommandLine.Parameters(paramLabel = "INPUTS", defaultValue = "tableDesc") // 3. List<Path> inputs; // 3. @Override public Integer call() throws Exception { var tables = new TableLoader().load(this.inputs); Files.createDirectories(this.outputDir); tables.stream().forEach(model -> new ViewWriter().writeTo(this.outputDir, model)); return 0; } public static void main(String[] args) { System.exit(new CommandLine(App.class).execute(args)); // 4. } } CommandLine.Command アノテーション は、picocli に対してこのクラスが コマンドライン アプリケーションのエントリポイントであることを知らせるための アノテーション です。nameにアプリケーションの名前、descriptionに説明を設定します。 CommandLine.Option アノテーション は、 コマンドライン 引数のうち name 属性で指定したオプションの内容をフィールドに格納するための アノテーション です。文字列からの型変換はpicocliが自動的に行います。 CommandLine.Parameters アノテーション は、 コマンドライン 引数のうちオプションとしては解釈されなかったものが全て格納されるフィールドを指定するための アノテーション です。ここでも、型変換は自動的に行われます。 Excel のパーズ処理 Excel ファイルのパーズ処理は、細かく追っていくと膨大になりますので、いくつかポイントになる部分だけ紹介します。 まずは、パーズした結果を格納するデータ構造の紹介です。Java16から使えるようになったrecordを使うと驚くほどシンプルに書けますね。正直言って、 Java でアクセサを明示的に書かずに済む日がくるとは思っていませんでした。 package jp.co.isid.example.looker; import java.nio.file.Path; import java.util.List; public record TableModel(String logicalName, String physicalName, Path sourceFilepath, List<ColumnModel> columns) { public record ColumnModel(int no, String logicalName, String physicalName, String dataType) { } } どうあっても、小綺麗にはなりえないということを、確認して貰える Excel をパーズするコードです。いくつか、ポイントになる部分だけ選んで説明します。 ここで読んでいる Excel ファイルは、一度お見せしたものですが分かり易いよう、もう一度お見せします。 package jp.co.isid.example.looker; import org.apache.poi.hssf.usermodel.*; import org.apache.poi.poifs.filesystem.POIFSFileSystem; import org.apache.poi.ss.usermodel.*; import java.io.*; import java.nio.file.*; import java.util.*; import java.util.function.BiFunction; import java.util.stream.*; public class TableLoader { public List<TableModel> load(List<Path> roots) { var matcher = FileSystems.getDefault() .getPathMatcher("glob:**/*.xls"); // 1. return roots.stream().flatMap(path -> { try (var stream = Files.walk(path)) { return stream.filter(matcher::matches) .collect(Collectors.toList()).stream(); } catch (IOException e) { throw new UncheckedIOException(e); } }).map(this::parse).collect(Collectors.toList()); } public TableModel parse(Path target) { try (var fs = new POIFSFileSystem(target.toFile())) { var book = new HSSFWorkbook(fs); var sheet = book.getSheet("テーブル設計書"); var names = parseHeaders(sheet); var columns = parseColumns(sheet); return new TableModel(names.get(0), names.get(1), target, columns); // 2. } catch (IOException e) { throw new UncheckedIOException(e); } } List<String> parseHeaders(HSSFSheet sheet) {} // 3. 省略 List<TableModel.ColumnModel> parseColumns(HSSFSheet sheet) { var evaluator = sheet.getWorkbook() .getCreationHelper().createFormulaEvaluator(); // 4. var format = new DataFormatter(); BiFunction<Row, Integer, String> read = (row, index) -> // 5. format.formatCellValue(row.getCell(index), evaluator); return StreamSupport.stream(sheet.spliterator(), false) .filter(row -> { // 6. var no = read.apply(row, 0); var dd = read.apply(row, 1); return no != null && dd != null && no.matches("\\d+") && dd.matches("\\d+"); }) .map(row -> new TableModel.ColumnModel( Integer.parseInt(read.apply(row, 0)), read.apply(row, 3), read.apply(row, 4), read.apply(row, 5))) .sorted(Comparator.comparing(TableModel.ColumnModel::no)) // 7. .collect(Collectors.toList()); } } Java で ファイルシステム をトラバースしながらGLOB形式に一致したファイルを取りだす処理が、ここから始まる一連のコードです。Java1.7からある仕組みですが意外と使われていない API だと感じています。 recordを使って宣言したTableModelのコンスト ラク タを呼び出しています。呼び出し側は今まで通りのクラスに宣言されたコンスト ラク タを呼びだす時と同じですね。 私たちのテーブル設計書では、この部分にオートシェイプを使うなど煩雑なコードになっているので、説明を省略させてください。 Excel のセルに書かれている式を解決済みの状態で読みだすためには FormulaEvaluator を使います。 Excel のセルに書かれている日付書式を使ってフォーマット済みのデータを読みだすためには、 DataFormatter と組み合わせて使います。現代の Java で ラムダ式 を使うことでローカルスコープの関数をこのように定義できるので便利になりましたね。 Excel のシートの行を上から読みながらカラム定義が書いてある部分まで読み飛ばす処理です。実に泥臭いコードですね。 recordを使って宣言したColumnModelに自動生成されたアクセサをメソッド参照しています。recordではデータの読み出し用のメソッドに get が付いていないのでコードがシンプルですね。 Viewファイルの書き出し処理 最後は、ファイルを書きだすコードです。ここでは特別な事を何もしていません。 package jp.co.isid.example.looker; import java.io.*; import java.nio.charset.StandardCharsets; import java.nio.file.Path; public class ViewWriter { public void writeTo(Path dir, TableModel table) { try (var printer = new PrintWriter(dir.resolve(toFilename(table)).toFile(), StandardCharsets.UTF_8)) { printer.println(); printer.format("view: %s {%n", toViewName(table)); printer.format(" label: \"%s\"%n", table.logicalName()); table.columns().forEach(model -> printColumn(printer, model)); printer.println("}"); } catch (IOException e) { throw new UncheckedIOException(e); } } void printColumn(PrintWriter printer, TableModel.ColumnModel model) { printer.println(); printer.format(" dimension: %s {%n", model.physicalName().toLowerCase()); printer.format(" label: \"%s\"%n", model.logicalName()); printer.println(" }"); } String toViewName(TableModel model) { return "__" + model.physicalName().toLowerCase(); } String toFilename(TableModel model) { return toViewName(model) + ".view.lkml"; } } 標準 API を使って単にテキストを出力していますね。 まとめ このエントリでは、BIツールで使い易いデータベースと一般的な基幹システムにおけるデータベースにはギャップがあることを指摘しました。 既存の設計ドキュメントからコードの自動生成をすることで、このギャップを埋められることを実際に動作するコードと併せて説明しました。 このエントリを読んだ皆様が、よりよいデータの前処理が出来れば幸いです。 私たちは同じチームで働いてくれる仲間を探しています。今回のエントリで紹介したような仕事に興味のある方、ご応募をお待ちしています。 社内SE(DX推進エンジニア) 執筆: @sato.taichi 、レビュー: @handa.kenta ( Shodo で執筆されました )
はじめに BIツールで使い易いテーブルについて 設計ドキュメントからBIツールのモデルファイルを自動生成する LookMLのラベル機能 caseによるラベリング LookMLの継承機能 Viewファイル自動生成ツールの実装 ビルドスクリプト main関数の実装 Excelのパーズ処理 Viewファイルの書き出し処理 まとめ はじめに このエントリは、 Looker Advent Calendar 2022 における17日目の記事です。 本日は、基幹システムのような歴史的な蓄積がある既存のデータを後付けで可視化する際に問題となる項目のラベリングに関するノウハウを説明します。 蓄積のあるデータベースに対してLookerのようなBIツールを直接接続するだけではうまく分析できません。 それには、様々な理由があります。その中の一つはテーブル名や カラム名 が システム開発 にとって都合の良い表現になっており、BIツールにとって都合の良い表現にはなっていないことです。 例えば、以下のように既存のデータベースから何も変更せずにLookerにテーブルをインポートすると以下のようになります。 テーブル名や カラム名 にアルファベットとアンダースコアのみを使ってテーブルを定義することになっているシステムは多いんじゃないでしょうか。これは、現代的なウェブシステムの多くがエンドユーザーに対して直接データベースへのアクセスを許可するような構造にはなっていないからです。 BIツールで使い易いテーブルについて BIツールを使ってデータベースをエンドユーザーが直接見に行くことを前提に、使い易いテーブルとはどんなものか考えてみましょう。 まずテーブル名や カラム名 が問題領域に即した日本語表記になっていることは必須でしょう。そして、 カラム名 については、その意味や意図を簡易的に参照できるとより望ましいですね。 また、実装の都合で場合分けするために保存されている項目については、無味乾燥な数字やアルファベットではなく論理的な名称になっていることが望ましいでしょう。 例えば、私たちのシステムには、 ms_biz_prtnr というテーブルがあり、 hojin_kbn というカラムが含まれています。そこには、 01 、 02 、 03 などの値が保存されます。正直言って、このままでは意味が分かりませんよね。 これは 取引先マスタ というテーブルにある 法人区分 というカラムの話をしています。そして三つの値は、それぞれ 株式会社 、 有限会社 、 財団法人 を意味しています。 使い込まれた基幹システムには、恐らく数百から数千のテーブルがあり、その中に含まれるカラムは延べ数千にも及ぶでしょう。それをBIツールで分析できるようにするために、このような変換処理を手動で行っていると時間がいくらあっても足りません。 当然、基幹システム側は変化する会社の状況に応じて変更されていくわけですから、BIツールの都合でデータベースに対する変更を止められるはずもありません。 では、どうするのがいいでしょうか? 設計ドキュメントからBIツールのモデルファイルを自動生成する あまりに多くのテーブルを手作業でBIツール用に再定義していては、データ分析による価値を生みだす前にコストがかかりすぎてしまいます。分析環境の構築に大きなコストをかけてしまうと、それだけ大きな期待がかかります。 小さく始めて、徐々に大きな成果に結びつけるならできるかぎりコストをかけずに環境を作るのが望ましいでしょう。また、様々なBIツールが市場にはありますので、最適なものを目指して乗り換えていけるようにしたいものです。 というわけで、既存の設計成果物であるデータベース設計書からBIツール用のモデルファイルを自動生成しましょう。ISIDの場合は、テーブル設計書という Excel で記述された大量のファイルがありましたので、これを使います。 このファイルフォーマットについては詳細を説明しませんが、何かを感じ取って貰えると嬉しいです。 モデルファイルのフォーマットはできるかぎりテキスト形式のもので、その形式に関して仕様が公開されているものを使うのが良いでしょう。この条件を満たすBIツールとしてLookerがあります。 LookerのLookMLは yaml によく似たテキスト形式のファイルなので、gitや GitHub を使って簡単に構成管理できます。 特に説明はしませんが、LookMLに対するイメージを持ってもらうために少しだけ例をお見せしましょう。 ecommercestore.model.lkml connection: order_database include: "*.view.lkml" explore: orders { join: customers { sql_on: ${orders.customer_id} = ${customers.id} ;; } } orders.view.lkml view: orders { dimension: id { primary_key: yes type: number sql: ${TABLE}.id ;; } dimension: customer_id { sql: ${TABLE}.customer_id ;; } dimension: amount { type: number value_format: "0.00" sql: ${TABLE}.amount ;; } dimension_group: created { type: time timeframes: [date, week] sql: ${TABLE}.created_at ;; } measure: count { type: count # creates sql COUNT(orders.id) sql: ${id} ;; } } 見ての通りごく単純な構造のテキストファイルです。単純な文字列処理ができれば、このファイルを自動生成できることがお分かりいただけることでしょう。 LookMLのさらに詳細な説明は公式のドキュメントが分かり易いのでおすすめです。 LookMLの紹介 LookMLのラベル機能 自動生成するにあたって、押さえておくべきLookMLの機能を紹介しましょう。 まずは、ラベリング機能です。 といっても、それほど難しいことはありません。viewやdimensionには label というフィールドがあるので、そこに分かり易い名前を指定するだけです。 view: ms_biz_prtnr { label: "取引先マスタ" sql_table_name: `MS_BIZ_PRTNR`;; dimension: biz_prtnr_cd { label: "取引先コード" type: string sql: ${TABLE}.biz_prtnr_cd;; } ラベルを使うとこのようになります。分かり易くなりますね。 ラベルについてより詳細な仕様を知りたい方は公式ドキュメントをご覧ください。 label (for fields) caseによるラベリング テーブル名や カラム名 のラベリングが分かったら今度は値のラベリングです。 ここで使うのは、caseとwhenを組み合わせた条件分岐です。 view: ms_biz_prtnr { label: "取引先マスタ" sql_table_name: `MS_BIZ_PRTNR`;; dimension: hojin_kbn { label: "法人区分" type: string case: { when: { sql: ${TABLE}.hojin_kbn = "01";; label: "株式会社" } when: { sql: ${TABLE}.hojin_kbn = "02";; label: "有限会社" } when: { sql: ${TABLE}.hojin_kbn = "03";; label: "財団法人" } } } それぞれの値に対して、どんなラベルを表示するのか設定していますね。これを元にLookerは SQL のcase句を生成します。 ラベルのありなしで、どんな風に表示が変わるのか見てみましょう。データを見る人が良く使うであろう検索フィルターの表示を抜き出してあります。 ラベルを設定しない場合には、データベースに保存されている具体的な値である 01 や 02 といった値がどんな意味を持つのか詳細に理解していないとデータをフィルターできません。 一方でラベルを設定してあれば、候補の中から選ぶだけなので使い易くなります。 caseによるラベリングについてより詳細な仕様を知りたい方は公式ドキュメントをご覧ください。 case LookMLの継承機能 既存の設計ドキュメントからviewファイルを自動生成する際には、手書きするファイルと自動生成するファイルは分割するべきです。 そういう時に使える機能が継承(extends)です。継承を使うと複数のview定義を合成して一つのviewにできます。 まずは、継承元となるviewを /generated/views/__ms_biz_prtnr.view というパスに定義します。 内容としては、ラベルの宣言だけを並べたものです。この例ではviewの名前にすこし癖がありますが、自動生成を意図して奇妙な印象を受けるようにしてあります。 view: __ms_biz_prtnr { label: "取引先マスタ" # 1. dimension: biz_prtnr_cd { label: "取引先コード" # 2. } } 継承する側のview定義を見てみましょう。以下のようなコードになります。 include: "/generated/views/__ms_biz_prtnr.view" # 1. view: ms_biz_prtnr { extends: [__ms_biz_prtnr] # 2. sql_table_name: `MS_BIZ_PRTNR`;; dimension: biz_prtnr_cd { type: string sql: ${TABLE}.biz_prtnr_cd;; } } 継承元となるviewのファイルパスを指定してincludeします。これによって、分かれたファイルが単一のファイルであるかのように扱われます。 継承元となるviewの名前を指定します。 これによって、 ms_biz_prtnr と __ms_biz_prtnr を一つのviewとして混ざり合った状態になりました。結果的に以下のような宣言のviewになります。 view: ms_biz_prtnr { label: "取引先マスタ" # __ms_biz_prtnr の 1. から取りこまれる sql_table_name: `MS_BIZ_PRTNR`;; dimension: biz_prtnr_cd { label: "取引先コード" # __ms_biz_prtnr の 2. から取りこまれる type: string sql: ${TABLE}.biz_prtnr_cd;; } 継承についてより詳細な仕様を知りたい方は公式ドキュメントをご覧ください。 extends (for views) Viewファイル自動生成ツールの実装 今回は Java を使ってviewを定義するファイルを自動生成します。 なお、 Java を使った開発環境の構築方法については以前に書いた KotlinとGradleで始めるモダンなビルド環境 を参考にしてください。 ビルド スクリプト 依存ライブラリについて説明するために、ビルド スクリプト を紹介しましょう。 plugins { application } repositories.mavenCentral() dependencies { implementation("org.apache.poi:poi:5.2.3") // 1. implementation("info.picocli:picocli:4.7.0") // 2. annotationProcessor("info.picocli:picocli-codegen:4.7.0") // 2. } application { mainClass.set("jp.co.isid.example.looker.App") // 3. } Java で Excel ファイルを読みだすのであれば、実績のある Apache POIがおすすめです。 コマンドライン アプリケーションの引数をパーズするライブラリとしてはpicocliを使うと アノテーション を付けるだけでやりたいことが実現できるので非常におすすめです。 main関数を定義したクラスのクラス名を設定しています。 main関数の実装 picocliを使ったアプリケーションのブートストラップ部分の実装を紹介しましょう。 package jp.co.isid.example.looker; import picocli.CommandLine; import java.nio.file.*; import java.util.List; import java.util.concurrent.Callable; @CommandLine.Command(name = "modelgen", // 1. description = "generate looker view models") public class App implements Callable<Integer> { @CommandLine.Option(names = {"-o", "--output"}, // 2. description = "output dir of generated resources", // 2. defaultValue = "build/generated/views/") // 2. Path outputDir; @CommandLine.Parameters(paramLabel = "INPUTS", defaultValue = "tableDesc") // 3. List<Path> inputs; // 3. @Override public Integer call() throws Exception { var tables = new TableLoader().load(this.inputs); Files.createDirectories(this.outputDir); tables.stream().forEach(model -> new ViewWriter().writeTo(this.outputDir, model)); return 0; } public static void main(String[] args) { System.exit(new CommandLine(App.class).execute(args)); // 4. } } CommandLine.Command アノテーション は、picocli に対してこのクラスが コマンドライン アプリケーションのエントリポイントであることを知らせるための アノテーション です。nameにアプリケーションの名前、descriptionに説明を設定します。 CommandLine.Option アノテーション は、 コマンドライン 引数のうち name 属性で指定したオプションの内容をフィールドに格納するための アノテーション です。文字列からの型変換はpicocliが自動的に行います。 CommandLine.Parameters アノテーション は、 コマンドライン 引数のうちオプションとしては解釈されなかったものが全て格納されるフィールドを指定するための アノテーション です。ここでも、型変換は自動的に行われます。 Excel のパーズ処理 Excel ファイルのパーズ処理は、細かく追っていくと膨大になりますので、いくつかポイントになる部分だけ紹介します。 まずは、パーズした結果を格納するデータ構造の紹介です。Java16から使えるようになったrecordを使うと驚くほどシンプルに書けますね。正直言って、 Java でアクセサを明示的に書かずに済む日がくるとは思っていませんでした。 package jp.co.isid.example.looker; import java.nio.file.Path; import java.util.List; public record TableModel(String logicalName, String physicalName, Path sourceFilepath, List<ColumnModel> columns) { public record ColumnModel(int no, String logicalName, String physicalName, String dataType) { } } どうあっても、小綺麗にはなりえないということを、確認して貰える Excel をパーズするコードです。いくつか、ポイントになる部分だけ選んで説明します。 ここで読んでいる Excel ファイルは、一度お見せしたものですが分かり易いよう、もう一度お見せします。 package jp.co.isid.example.looker; import org.apache.poi.hssf.usermodel.*; import org.apache.poi.poifs.filesystem.POIFSFileSystem; import org.apache.poi.ss.usermodel.*; import java.io.*; import java.nio.file.*; import java.util.*; import java.util.function.BiFunction; import java.util.stream.*; public class TableLoader { public List<TableModel> load(List<Path> roots) { var matcher = FileSystems.getDefault() .getPathMatcher("glob:**/*.xls"); // 1. return roots.stream().flatMap(path -> { try (var stream = Files.walk(path)) { return stream.filter(matcher::matches) .collect(Collectors.toList()).stream(); } catch (IOException e) { throw new UncheckedIOException(e); } }).map(this::parse).collect(Collectors.toList()); } public TableModel parse(Path target) { try (var fs = new POIFSFileSystem(target.toFile())) { var book = new HSSFWorkbook(fs); var sheet = book.getSheet("テーブル設計書"); var names = parseHeaders(sheet); var columns = parseColumns(sheet); return new TableModel(names.get(0), names.get(1), target, columns); // 2. } catch (IOException e) { throw new UncheckedIOException(e); } } List<String> parseHeaders(HSSFSheet sheet) {} // 3. 省略 List<TableModel.ColumnModel> parseColumns(HSSFSheet sheet) { var evaluator = sheet.getWorkbook() .getCreationHelper().createFormulaEvaluator(); // 4. var format = new DataFormatter(); BiFunction<Row, Integer, String> read = (row, index) -> // 5. format.formatCellValue(row.getCell(index), evaluator); return StreamSupport.stream(sheet.spliterator(), false) .filter(row -> { // 6. var no = read.apply(row, 0); var dd = read.apply(row, 1); return no != null && dd != null && no.matches("\\d+") && dd.matches("\\d+"); }) .map(row -> new TableModel.ColumnModel( Integer.parseInt(read.apply(row, 0)), read.apply(row, 3), read.apply(row, 4), read.apply(row, 5))) .sorted(Comparator.comparing(TableModel.ColumnModel::no)) // 7. .collect(Collectors.toList()); } } Java で ファイルシステム をトラバースしながらGLOB形式に一致したファイルを取りだす処理が、ここから始まる一連のコードです。Java1.7からある仕組みですが意外と使われていない API だと感じています。 recordを使って宣言したTableModelのコンスト ラク タを呼び出しています。呼び出し側は今まで通りのクラスに宣言されたコンスト ラク タを呼びだす時と同じですね。 私たちのテーブル設計書では、この部分にオートシェイプを使うなど煩雑なコードになっているので、説明を省略させてください。 Excel のセルに書かれている式を解決済みの状態で読みだすためには FormulaEvaluator を使います。 Excel のセルに書かれている日付書式を使ってフォーマット済みのデータを読みだすためには、 DataFormatter と組み合わせて使います。現代の Java で ラムダ式 を使うことでローカルスコープの関数をこのように定義できるので便利になりましたね。 Excel のシートの行を上から読みながらカラム定義が書いてある部分まで読み飛ばす処理です。実に泥臭いコードですね。 recordを使って宣言したColumnModelに自動生成されたアクセサをメソッド参照しています。recordではデータの読み出し用のメソッドに get が付いていないのでコードがシンプルですね。 Viewファイルの書き出し処理 最後は、ファイルを書きだすコードです。ここでは特別な事を何もしていません。 package jp.co.isid.example.looker; import java.io.*; import java.nio.charset.StandardCharsets; import java.nio.file.Path; public class ViewWriter { public void writeTo(Path dir, TableModel table) { try (var printer = new PrintWriter(dir.resolve(toFilename(table)).toFile(), StandardCharsets.UTF_8)) { printer.println(); printer.format("view: %s {%n", toViewName(table)); printer.format(" label: \"%s\"%n", table.logicalName()); table.columns().forEach(model -> printColumn(printer, model)); printer.println("}"); } catch (IOException e) { throw new UncheckedIOException(e); } } void printColumn(PrintWriter printer, TableModel.ColumnModel model) { printer.println(); printer.format(" dimension: %s {%n", model.physicalName().toLowerCase()); printer.format(" label: \"%s\"%n", model.logicalName()); printer.println(" }"); } String toViewName(TableModel model) { return "__" + model.physicalName().toLowerCase(); } String toFilename(TableModel model) { return toViewName(model) + ".view.lkml"; } } 標準 API を使って単にテキストを出力していますね。 まとめ このエントリでは、BIツールで使い易いデータベースと一般的な基幹システムにおけるデータベースにはギャップがあることを指摘しました。 既存の設計ドキュメントからコードの自動生成をすることで、このギャップを埋められることを実際に動作するコードと併せて説明しました。 このエントリを読んだ皆様が、よりよいデータの前処理が出来れば幸いです。 私たちは同じチームで働いてくれる仲間を探しています。今回のエントリで紹介したような仕事に興味のある方、ご応募をお待ちしています。 社内SE(DX推進エンジニア) 執筆: @sato.taichi 、レビュー: @handa.kenta ( Shodo で執筆されました )
みなさんこんにちは。 電通国際情報サービス (ISID) 金融ソリューション事業部の水野です。 これは 電通国際情報サービス Advent Calendar 2022 の16日目の記事です。 今回は、ISID金融事業部で運用しているスキルマップについてご紹介します。 テッ クリード とは 実は、ISIDの少なくとも金融事業部にテッ クリード と言うポジションはありません。 実在するのはチーフアーキテクトと言う職種のみで、各プロジェクトでリードエンジニアやテッ クリード という仮想的なロールがあるのが実態です。 一時期は フルスタ ックエンジニアと呼んでいる時期もありましたが、近年このワーディングが好まれない印象なので、大々的に使っていません。 主観ですが、 フルスタ ックエンジニアはインフラ知識/運用系の知識のウェイトが高いエンジニアで、テッ クリード はソフトウェア アーキテクチャ 、Webアプリケーション実装技術寄りのエンジニアという印象を持っているので、 フルスタ ックエンジニアと使い分けても良いと考えてはいます。 フルスタ ックエンジニアより、最近はフルサイクルエンジニア( 1 )の方が一般的かもしれません。 アーキテクトやテッ クリード などいろいろ呼び名はありますが、以下の職務をこなすエンジニアと定義しています。 システム アーキテクチャ ( 2 )、ソフトウェア アーキテクチャ の規定 技術的な難易度、重要度が高い箇所の実装 フレームワーク 部分やコア機能の実装など、システムの根幹部分の実装 プロトタイピング 技術課題の解消 技術的な意思決定 テク ノロ ジスタックの選定 開発手法、プロセスの策定 品質保証戦略立案(例えば ユニットテスト ) 要件、仕様面の技術的な見地からの判断 技術系チームの組成、運営、醸成 エンジニア育成 守備範囲はアプリケーション基盤チームやインフラチーム 技術者代表として内外の ステークホルダー との合意形成 クラウド 領域を主戦場にするシニアエンジニアは、 クラウド アーキテクトと呼んだりすることもありますね。 スキルマップは、上述の職務をこなす為にどの程度の習熟度が必要なのかを測ることを目的としています。 スキルマップ 前提 項目ごとに個別具体的な説明を付与するのは辛いので、以下の前提を置いています。 コンピュータサイエンス 、 ソフトウェア工学 の基礎は、 IPA応用情報試験 取得による担保 システムアーキテクト 取得が望ましいが、必須取得は 応用情報技術者 具体的な言語、プラットフォームにフォーカスせず、根本的な知識・理解度を図る 一部、特定の言語や クラウド ベンダーに触れている箇所はある ステークホルダー との調整など、技術者であっても必要なコミュニケーションスキルは存在するが、相対的にウェイトが低いので マネジメントスキル に全て押し込む どの項目をどれだけ習得すればテッ クリード という明確な基準はないが、テッ クリード とは深く広範な知識があるもの 上記の前提のほか、ISID金融の業務に依存した構成になっている部分もあります。 なお、資格試験と言うと敬遠しがちな方も多いですが、意外と良問が多く、基本的・体系的なITスキルを身に付けるには有用です。 問題もアップデートされており、例えば最近では クラウド や 機械学習 に関する出題もあり、化石過ぎて実戦で使えない知識と言う訳ではなく、むしろエンジニアなら最低限習得しておくべき素養と考えます。 スキルマップ プログラミングスキル 構造化プログラミング 変数の概念(整数、文字列、真偽、配列) 演算 制御構造(順次、分岐、反復) 関数 オブジェクト指向 プログラミング カプセル化 ポリモーフィズム 差分プログラミングによる効率化 関数型プログラミング 参照透過性 クロージャ 関数合成、部分適用 高階関数 実践プログラミング基礎 DBアクセス 業務モデルを実装に落とし込むスキル エラー処理一般 Webアプリケーション実装 プロトタイピングによる ユースケース の具現化スキル プログラミング運用スキル Linting(text lintを含む) ビルド デバッグ 、ログ参照/解析からの問題解決 IDE の知識を含む、開発環境構築スキル プラグイン の選定と導入、設定 Docker Composeによる仮想化やDocker Desktop利用など フォーマッティング、lint設定、保存時アクション等、開発効率を高める工夫 リファ レンシング (公式リファレンスへ辿り着く能力、参照情報の本質的な理解と問題解決能力) 主要 プログラミング言語 のツールチェインの知見と導入スキル レビュー 静的解析ツールを使いこなし、レポートの評価/運用/フィードバック コードレビュー C/Iとの統合スキル SaaS型C/Iの利活用と運用 プロジェクト特性に合わせた応用力 中級 プログラマー レベル 利用頻度高の言語に対する一定の習熟度(文法、特性、利用シーンの想像力) マルチスレッディング 再帰 等の アルゴリズム による効率化 依存性低減力 AOP 、MIXIN 可読性、保守性の高いコードを書くスキル 安定性重視(副作用がないコーディングの理解、意識と実践) 上級 プログラマ メタプログラミング 非同期ノン ブロッキング I/O 高度な並列性に対する素養、実装力、 OSS や手法に関する知見 マイクロバッチ、ストリーム処理 コンテナベースの並列化 並列化を備えたプログラミングモデル Rustなどの比較的新しいが将来性がありそうな要素技術へのアンテナ ビッグデータ の取り扱いを可能にするプログラミングスキル オーケストレーション レイヤの勘所や、サービスメッシュなどの伸び盛りな分野のキャッチアップ 論理型言語の知識 フロントエンド UIデザイン知識(専業デザイナーとコンテキストを共有し、同じ言語で会話できるレベル) SPA実装スキル CSS 関連テク ノロ ジ/運用 パラダイム テク ノロ ジスタックの選定センス、運用組込み。例えばnpmライブラリの選択など WASMに典型的な、注目度が高い新技術領域のキャッチアップ トランスパイル、ミニファイ、広義のビルド、デプロイを中心としたC/Iスキル SEO に関する専門知識、スコアを上げるテクニック 機械学習 教師あり学習 /なし学習の理解 回帰、決定木、 SVM 、 クラスタリング 等、基本的な アルゴリズム の理解 アルゴリズム が明確であれば、 Python 等である程度実装可能な能力 データサイエンティストとの会話が成立する程度の知識 機械学習 をシステムに組み込む際の アーキテクチャ 策定および導入計画立案力 業務ではおそらく使わないが、何かで強みになるかもしれない ギーク 領域 アセンブラ FPGA カーネル 実装力 HDL 大凡常人が会得していないであろうコア感/レア感溢れるダークサイド・テク ノロ ジ アーキテクティング オニオン アーキテクチャ 、クリーン アーキテクチャ などのソフトウェア アーキテクチャ に関する知識 マイクロサービス アーキテクチャ 、ミニサービス アーキテクチャ などの アーキテクチャパターン Micro Frontends 処理方式毎の類型化能力 近代的なアーキテクチャ 設計スキル 共 通化 /再利用性を念頭に置いた全体設計 オブジェクト指向設計 デザインパターン に関する知識 DDDなどのモデル駆動設計 SaaS を効率的に利活用するデザインスキル テスト ユニットテスト 言語毎の ユニットテスト 実装力 ユニットテスト フレームワーク に関する知見 ユニットテスト 戦略・計画立案/ プログラマー への浸透推進力 リグレッション テストと リファクタリング ケース設計 JSTQB 同値分割 境界値分析 CFD技法 エラー推測/異常系の考慮 非機能テスト 性能テスト 計画、特に準備 ツール選定 シナリオ作成 ボトルネック 抽出 メモリリーク テスト 負荷試験 長期運用安定性 システム運用テスト 耐障害性試験 自動化 バックエンド E2E 結合テスト は別の素養という整理 永続化層 集合演算 RDB 系知識 KVS系知識 論理ER、物理ER インデキシング等のパフォーマンスの考慮 スキーマ マイグレーション アルゴリズム 、データ構造 データ構造:ツリー、キュー、スタック ソート( IPA 試験で最低限は充足される) 探索( IPA 試験で最低限は充足される) システム基盤(インフラスト ラク チャ/システム アーキテクチャ ) IPA非機能要求グレード の理解および定義スキル 可用性( 稼働率 向上、耐障害性、災害対策、回復等の基礎スキル) 性能・拡張性(処理量、目標値設計、リソース拡張) 運用・保守性(バックアップ、パッチ適用、時刻同期、監視、障害時運用等の基礎素養) 移行性(移行方式設計、データ量設計、インフラ移行計画) セキュリティ(アクセス制限、データ秘匿、 マルウェア 対策等基本的な対策に関する知識) システム環境・ エコロジー (制約/特性/適合/設置条件/環境マネジメント) HWに関する基礎知識 CPU、メモリ、ディスク( RAID 込み)の IPA 応用情報試験レベルの知識 GPU 、MRAM、ReRAM、NRAMなど、比較的新しめのハードウェアに関する知見 ルータなどのネットワーク機器 OW/MW UNIX 系サーバ Windows サーバ RDB 製品導入および管理スキル ジョブスケジューラ 初見のMWでも、マニュアルを読んで理解し、設計/インテグレーション可能なスキル ネットワーク TCP/IP ルーティング スイッチング OSI基本参照モデル と対応HW、 プロトコル の理解および仕様を調査可能な能力 CDN 仮想化 ハイパーバイザ OCIランタイム サーバレス DNS ドメイン (名前解決、ゾーン、ネームサーバ) リ ゾル バ、 フォワ ーダ 再帰 問い合わせ/反復問い合わせ DNS サーバ実装(BIND等)の理解 DNS サーバ構築力 インフラレイヤの高度なセキュリティ知識 ネットワークセキュリティ(FW、Proxy、 DMZ 、IPS) DDoS対策 暗号化 セキュリティ製品/ SaaS の選定力・導入スキル 認証、アクセス制限 情報セキュリティ(資産選定、管理、保護に関する知見) 監視(ログ監視、不正検知、追跡等) 監視/通知に関する各種手段の知識と実装力 不正アクセス 検知 トレーサビリティ確保の仕組み構築能力 SaaS (Datadog、New Relic等を想定)の知見 監視に関わる運用設計および導入推進力 クラウド 基礎 AWS であれば、IAM/ VPC /EC2/Lambda/RDS/ECS等の基本知識 GCP 、Azureなど、 AWS 以外の主要 クラウド プラットフォームに対する概要レベルの素養 Kubernetesおよびエコシステム 設計・構築 運用 エコシステムに関する知見 MLOps, DevOps Gitブランチモデル(git-flow| github -flow|gitlab-flow)の理解と設計、導入 IaC(メジャーどころではTerraform, AWS CDK | Cloudformation, Google Cloud Deployment Manager) コンテナ基盤知識、構築 CI/CDパイプライン ジョブフロー設計、バッチ制御 ロギング/モニタリング MLワークフロー設計、構築 Auto ML領域 開発プロセス 共通フレームに関する知見 SI開発向け 開発プロセス 策定スキル( ウォーターフォール 想定) 品質の作り込みおよび品質保証を念頭に置いたプロセス設計力 開発標準化能力(ガイド、運用推進/自動化、工程作業計画立案) 成果物体系構築スキルおよび設計ツール アジャイル 開発に関する知識 マネジ メントス キル 最低限のテンプレPM力 チームビルディング チーム運営 成果物体系策定および成果物定義 指導/スキルトランスファ ウォーターフォール 、 アジャイル 開発の一定の理解(実践力は別) 補足 ビルド 様々な言語のビルドの仕組み、ツールチェインに関する知見に加え、Bazelと言ったこの先必要になりそうなツールの知識も含みます。 SaaS型C/Iの利活用と運用 最近は専らGitHubActionを利用しています。一方、今後 クラウド プラットフォームのC/Iサービス利用の増加も考えられるので、 クラウド ベンダーのマネージドサービスの知識も含みます。 利用頻度高の言語に対する一定の習熟度 金融事業部では Java 、.NET( C# )が多く、次点で Python という肌感覚で、これらを利用頻度高に位置付けています。 最近では一定の規模の開発にgoが採用されることも増え、研究開発ではRustの利用も始めました。 依存性低減力 参照が循環していないなど、モジュール依存関係が複雑過ぎないプログラム設計および実装を意味します。 モジュール間の依存性を極力排すことを目的とし、DIコンテナによる実現などが考えられます。 トランスパイル、ミニファイ、広義のビルド、デプロイを中心としたC/Iスキル フロントエンドのビルド周りは苦手な人が多い印象で、Webpack、Turbopackなどツールチェインの習熟度を含みます。Romeなど新しめのツールチェインの知見も含みます。 近代的なアーキテクチャ 相当に弊社弊事業部の主観に満ち溢れたものですが、例えば Cloudflare workers+D1+Postgres WASMで、 CDN エッジサイドだけで何かを実装するみたいなものをイメージしています。 CDN FastlyやCloudflareなど3rd Partyを含みます。 OCIランタイム 開発ではDockerを利用するケースが多いですが、OCI界隈の動向や各種OCIランタイム実装の知見です。 Kubernetesおよびエコシステム どの クラウド プラットフォームを使おうとも、 Kubernetes が選択肢の一つとして上がる事が普通になりました。 インフラを専門領域とするエンジニアは、周辺エコシステムを含めて深い知識・理解が必要な時代です。 成果物体系構築スキルおよび設計ツール 設計ツールとは、 MarkDown を書くエディタや画像描画ツールなどが該当します。 画像描画ツールはdraw.ioが大半ですが、最近ではmermaidも徐々に増えてきています。 アーキテクチャ 系ドキュメントを書く際は、アーキチームのみでPlantUMLを採用するケースもあります。 成果物体系に見合った設計ツールを選定し、プロジェクト内で統一します。 運用 上記スキルマップの最下層の項目に対し、個人個人が自己申告でレベルを入力します。 レベル(習熟度)は4段階で、以下が基準です。 Lv1 触ったことがない Lv2 業務で触ったことがある Lv3 本質を理解し、使いこなしている Lv4 本質を深淵まで理解し、適切なシーンで応用可能 自己申告したレベルは承認制で、上位技術者あるいは上席がOKならば確定、NGならばフィードバックします。 とは言え、実際これを継続的に運用するのは難しく、課題も多いです。 運用上の課題で私が認識しているものは下記です。 事業部の全員がレベル設定している訳ではなく、技術系業務が中心の2割程度のエンジニアのみ実施 年度によっては実施していない これが一番最悪なので、2023年度は絶対にやる レベル設定は申告ベースのため、主観的になりがち。ただ、上位技術者によってある程度是正は可能 スキルマップの習熟度と技術系のポジションを、具体的にどう結びつけるかの解に至っていない どの領域をどのレベルまで修めればテッ クリード かなどの基準がない スキルマップ自体の更新は半年、出来れば3カ月程度で見直したい 個別要素技術に因らず、なるべく普遍的なスキルマップとなるよう志向しているので負荷は軽い まとめ テッ クリード は技術力で現場を牽引するのがミッションですが、技術に詳しい人が担う仮想的な職種であることが多いのではないでしょうか。 非常に重要な役割であるにもかかわらず、 インセンティブ や裁量の範囲など未定義なケースもあるかと思います。 組織によって「テッ クリード とは何か」は異なりますが、我々ISID金融ではその重要性を認識しており、可能な限りミッションとスキルセットを可視化しようと試みています。 また、テッ クリード に至るまでの道のりと、そもそも技術者が習得すべきスキルについての定義も必要と認識しています。 まだまだ道半ばではありますが、今回はスキルマップのご紹介でした。 最後までご覧になっていただき、誠にありがとうございました。 執筆: @mizuno.kazuhiro 、レビュー: @yamashita.tsuyoshi ( Shodo で執筆されました ) https://netflixtechblog.com/full-cycle-developers-at-netflix-a08c31f83249 ↩ クラウド などのインフラスト ラク チャを含む、システム全体の構成 ↩
みなさんこんにちは。 電通国際情報サービス (ISID) 金融ソリューション事業部の水野です。 これは 電通国際情報サービス Advent Calendar 2022 の16日目の記事です。 今回は、ISID金融事業部で運用しているスキルマップについてご紹介します。 テッ クリード とは 実は、ISIDの少なくとも金融事業部にテッ クリード と言うポジションはありません。 実在するのはチーフアーキテクトと言う職種のみで、各プロジェクトでリードエンジニアやテッ クリード という仮想的なロールがあるのが実態です。 一時期は フルスタ ックエンジニアと呼んでいる時期もありましたが、近年このワーディングが好まれない印象なので、大々的に使っていません。 主観ですが、 フルスタ ックエンジニアはインフラ知識/運用系の知識のウェイトが高いエンジニアで、テッ クリード はソフトウェア アーキテクチャ 、Webアプリケーション実装技術寄りのエンジニアという印象を持っているので、 フルスタ ックエンジニアと使い分けても良いと考えてはいます。 フルスタ ックエンジニアより、最近はフルサイクルエンジニア( 1 )の方が一般的かもしれません。 アーキテクトやテッ クリード などいろいろ呼び名はありますが、以下の職務をこなすエンジニアと定義しています。 システム アーキテクチャ ( 2 )、ソフトウェア アーキテクチャ の規定 技術的な難易度、重要度が高い箇所の実装 フレームワーク 部分やコア機能の実装など、システムの根幹部分の実装 プロトタイピング 技術課題の解消 技術的な意思決定 テク ノロ ジスタックの選定 開発手法、プロセスの策定 品質保証戦略立案(例えば ユニットテスト ) 要件、仕様面の技術的な見地からの判断 技術系チームの組成、運営、醸成 エンジニア育成 守備範囲はアプリケーション基盤チームやインフラチーム 技術者代表として内外の ステークホルダー との合意形成 クラウド 領域を主戦場にするシニアエンジニアは、 クラウド アーキテクトと呼んだりすることもありますね。 スキルマップは、上述の職務をこなす為にどの程度の習熟度が必要なのかを測ることを目的としています。 スキルマップ 前提 項目ごとに個別具体的な説明を付与するのは辛いので、以下の前提を置いています。 コンピュータサイエンス 、 ソフトウェア工学 の基礎は、 IPA応用情報試験 取得による担保 システムアーキテクト 取得が望ましいが、必須取得は 応用情報技術者 具体的な言語、プラットフォームにフォーカスせず、根本的な知識・理解度を図る 一部、特定の言語や クラウド ベンダーに触れている箇所はある ステークホルダー との調整など、技術者であっても必要なコミュニケーションスキルは存在するが、相対的にウェイトが低いので マネジメントスキル に全て押し込む どの項目をどれだけ習得すればテッ クリード という明確な基準はないが、テッ クリード とは深く広範な知識があるもの 上記の前提のほか、ISID金融の業務に依存した構成になっている部分もあります。 なお、資格試験と言うと敬遠しがちな方も多いですが、意外と良問が多く、基本的・体系的なITスキルを身に付けるには有用です。 問題もアップデートされており、例えば最近では クラウド や 機械学習 に関する出題もあり、化石過ぎて実戦で使えない知識と言う訳ではなく、むしろエンジニアなら最低限習得しておくべき素養と考えます。 スキルマップ プログラミングスキル 構造化プログラミング 変数の概念(整数、文字列、真偽、配列) 演算 制御構造(順次、分岐、反復) 関数 オブジェクト指向 プログラミング カプセル化 ポリモーフィズム 差分プログラミングによる効率化 関数型プログラミング 参照透過性 クロージャ 関数合成、部分適用 高階関数 実践プログラミング基礎 DBアクセス 業務モデルを実装に落とし込むスキル エラー処理一般 Webアプリケーション実装 プロトタイピングによる ユースケース の具現化スキル プログラミング運用スキル Linting(text lintを含む) ビルド デバッグ 、ログ参照/解析からの問題解決 IDE の知識を含む、開発環境構築スキル プラグイン の選定と導入、設定 Docker Composeによる仮想化やDocker Desktop利用など フォーマッティング、lint設定、保存時アクション等、開発効率を高める工夫 リファ レンシング (公式リファレンスへ辿り着く能力、参照情報の本質的な理解と問題解決能力) 主要 プログラミング言語 のツールチェインの知見と導入スキル レビュー 静的解析ツールを使いこなし、レポートの評価/運用/フィードバック コードレビュー C/Iとの統合スキル SaaS型C/Iの利活用と運用 プロジェクト特性に合わせた応用力 中級 プログラマー レベル 利用頻度高の言語に対する一定の習熟度(文法、特性、利用シーンの想像力) マルチスレッディング 再帰 等の アルゴリズム による効率化 依存性低減力 AOP 、MIXIN 可読性、保守性の高いコードを書くスキル 安定性重視(副作用がないコーディングの理解、意識と実践) 上級 プログラマ メタプログラミング 非同期ノン ブロッキング I/O 高度な並列性に対する素養、実装力、 OSS や手法に関する知見 マイクロバッチ、ストリーム処理 コンテナベースの並列化 並列化を備えたプログラミングモデル Rustなどの比較的新しいが将来性がありそうな要素技術へのアンテナ ビッグデータ の取り扱いを可能にするプログラミングスキル オーケストレーション レイヤの勘所や、サービスメッシュなどの伸び盛りな分野のキャッチアップ 論理型言語の知識 フロントエンド UIデザイン知識(専業デザイナーとコンテキストを共有し、同じ言語で会話できるレベル) SPA実装スキル CSS 関連テク ノロ ジ/運用 パラダイム テク ノロ ジスタックの選定センス、運用組込み。例えばnpmライブラリの選択など WASMに典型的な、注目度が高い新技術領域のキャッチアップ トランスパイル、ミニファイ、広義のビルド、デプロイを中心としたC/Iスキル SEO に関する専門知識、スコアを上げるテクニック 機械学習 教師あり学習 /なし学習の理解 回帰、決定木、 SVM 、 クラスタリング 等、基本的な アルゴリズム の理解 アルゴリズム が明確であれば、 Python 等である程度実装可能な能力 データサイエンティストとの会話が成立する程度の知識 機械学習 をシステムに組み込む際の アーキテクチャ 策定および導入計画立案力 業務ではおそらく使わないが、何かで強みになるかもしれない ギーク 領域 アセンブラ FPGA カーネル 実装力 HDL 大凡常人が会得していないであろうコア感/レア感溢れるダークサイド・テク ノロ ジ アーキテクティング オニオン アーキテクチャ 、クリーン アーキテクチャ などのソフトウェア アーキテクチャ に関する知識 マイクロサービス アーキテクチャ 、ミニサービス アーキテクチャ などの アーキテクチャパターン Micro Frontends 処理方式毎の類型化能力 近代的なアーキテクチャ 設計スキル 共 通化 /再利用性を念頭に置いた全体設計 オブジェクト指向設計 デザインパターン に関する知識 DDDなどのモデル駆動設計 SaaS を効率的に利活用するデザインスキル テスト ユニットテスト 言語毎の ユニットテスト 実装力 ユニットテスト フレームワーク に関する知見 ユニットテスト 戦略・計画立案/ プログラマー への浸透推進力 リグレッション テストと リファクタリング ケース設計 JSTQB 同値分割 境界値分析 CFD技法 エラー推測/異常系の考慮 非機能テスト 性能テスト 計画、特に準備 ツール選定 シナリオ作成 ボトルネック 抽出 メモリリーク テスト 負荷試験 長期運用安定性 システム運用テスト 耐障害性試験 自動化 バックエンド E2E 結合テスト は別の素養という整理 永続化層 集合演算 RDB 系知識 KVS系知識 論理ER、物理ER インデキシング等のパフォーマンスの考慮 スキーマ マイグレーション アルゴリズム 、データ構造 データ構造:ツリー、キュー、スタック ソート( IPA 試験で最低限は充足される) 探索( IPA 試験で最低限は充足される) システム基盤(インフラスト ラク チャ/システム アーキテクチャ ) IPA非機能要求グレード の理解および定義スキル 可用性( 稼働率 向上、耐障害性、災害対策、回復等の基礎スキル) 性能・拡張性(処理量、目標値設計、リソース拡張) 運用・保守性(バックアップ、パッチ適用、時刻同期、監視、障害時運用等の基礎素養) 移行性(移行方式設計、データ量設計、インフラ移行計画) セキュリティ(アクセス制限、データ秘匿、 マルウェア 対策等基本的な対策に関する知識) システム環境・ エコロジー (制約/特性/適合/設置条件/環境マネジメント) HWに関する基礎知識 CPU、メモリ、ディスク( RAID 込み)の IPA 応用情報試験レベルの知識 GPU 、MRAM、ReRAM、NRAMなど、比較的新しめのハードウェアに関する知見 ルータなどのネットワーク機器 OW/MW UNIX 系サーバ Windows サーバ RDB 製品導入および管理スキル ジョブスケジューラ 初見のMWでも、マニュアルを読んで理解し、設計/インテグレーション可能なスキル ネットワーク TCP/IP ルーティング スイッチング OSI基本参照モデル と対応HW、 プロトコル の理解および仕様を調査可能な能力 CDN 仮想化 ハイパーバイザ OCIランタイム サーバレス DNS ドメイン (名前解決、ゾーン、ネームサーバ) リ ゾル バ、 フォワ ーダ 再帰 問い合わせ/反復問い合わせ DNS サーバ実装(BIND等)の理解 DNS サーバ構築力 インフラレイヤの高度なセキュリティ知識 ネットワークセキュリティ(FW、Proxy、 DMZ 、IPS) DDoS対策 暗号化 セキュリティ製品/ SaaS の選定力・導入スキル 認証、アクセス制限 情報セキュリティ(資産選定、管理、保護に関する知見) 監視(ログ監視、不正検知、追跡等) 監視/通知に関する各種手段の知識と実装力 不正アクセス 検知 トレーサビリティ確保の仕組み構築能力 SaaS (Datadog、New Relic等を想定)の知見 監視に関わる運用設計および導入推進力 クラウド 基礎 AWS であれば、IAM/ VPC /EC2/Lambda/RDS/ECS等の基本知識 GCP 、Azureなど、 AWS 以外の主要 クラウド プラットフォームに対する概要レベルの素養 Kubernetesおよびエコシステム 設計・構築 運用 エコシステムに関する知見 MLOps, DevOps Gitブランチモデル(git-flow| github -flow|gitlab-flow)の理解と設計、導入 IaC(メジャーどころではTerraform, AWS CDK | Cloudformation, Google Cloud Deployment Manager) コンテナ基盤知識、構築 CI/CDパイプライン ジョブフロー設計、バッチ制御 ロギング/モニタリング MLワークフロー設計、構築 Auto ML領域 開発プロセス 共通フレームに関する知見 SI開発向け 開発プロセス 策定スキル( ウォーターフォール 想定) 品質の作り込みおよび品質保証を念頭に置いたプロセス設計力 開発標準化能力(ガイド、運用推進/自動化、工程作業計画立案) 成果物体系構築スキルおよび設計ツール アジャイル 開発に関する知識 マネジ メントス キル 最低限のテンプレPM力 チームビルディング チーム運営 成果物体系策定および成果物定義 指導/スキルトランスファ ウォーターフォール 、 アジャイル 開発の一定の理解(実践力は別) 補足 ビルド 様々な言語のビルドの仕組み、ツールチェインに関する知見に加え、Bazelと言ったこの先必要になりそうなツールの知識も含みます。 SaaS型C/Iの利活用と運用 最近は専らGitHubActionを利用しています。一方、今後 クラウド プラットフォームのC/Iサービス利用の増加も考えられるので、 クラウド ベンダーのマネージドサービスの知識も含みます。 利用頻度高の言語に対する一定の習熟度 金融事業部では Java 、.NET( C# )が多く、次点で Python という肌感覚で、これらを利用頻度高に位置付けています。 最近では一定の規模の開発にgoが採用されることも増え、研究開発ではRustの利用も始めました。 依存性低減力 参照が循環していないなど、モジュール依存関係が複雑過ぎないプログラム設計および実装を意味します。 モジュール間の依存性を極力排すことを目的とし、DIコンテナによる実現などが考えられます。 トランスパイル、ミニファイ、広義のビルド、デプロイを中心としたC/Iスキル フロントエンドのビルド周りは苦手な人が多い印象で、Webpack、Turbopackなどツールチェインの習熟度を含みます。Romeなど新しめのツールチェインの知見も含みます。 近代的なアーキテクチャ 相当に弊社弊事業部の主観に満ち溢れたものですが、例えば Cloudflare workers+D1+Postgres WASMで、 CDN エッジサイドだけで何かを実装するみたいなものをイメージしています。 CDN FastlyやCloudflareなど3rd Partyを含みます。 OCIランタイム 開発ではDockerを利用するケースが多いですが、OCI界隈の動向や各種OCIランタイム実装の知見です。 Kubernetesおよびエコシステム どの クラウド プラットフォームを使おうとも、 Kubernetes が選択肢の一つとして上がる事が普通になりました。 インフラを専門領域とするエンジニアは、周辺エコシステムを含めて深い知識・理解が必要な時代です。 成果物体系構築スキルおよび設計ツール 設計ツールとは、 MarkDown を書くエディタや画像描画ツールなどが該当します。 画像描画ツールはdraw.ioが大半ですが、最近ではmermaidも徐々に増えてきています。 アーキテクチャ 系ドキュメントを書く際は、アーキチームのみでPlantUMLを採用するケースもあります。 成果物体系に見合った設計ツールを選定し、プロジェクト内で統一します。 運用 上記スキルマップの最下層の項目に対し、個人個人が自己申告でレベルを入力します。 レベル(習熟度)は4段階で、以下が基準です。 Lv1 触ったことがない Lv2 業務で触ったことがある Lv3 本質を理解し、使いこなしている Lv4 本質を深淵まで理解し、適切なシーンで応用可能 自己申告したレベルは承認制で、上位技術者あるいは上席がOKならば確定、NGならばフィードバックします。 とは言え、実際これを継続的に運用するのは難しく、課題も多いです。 運用上の課題で私が認識しているものは下記です。 事業部の全員がレベル設定している訳ではなく、技術系業務が中心の2割程度のエンジニアのみ実施 年度によっては実施していない これが一番最悪なので、2023年度は絶対にやる レベル設定は申告ベースのため、主観的になりがち。ただ、上位技術者によってある程度是正は可能 スキルマップの習熟度と技術系のポジションを、具体的にどう結びつけるかの解に至っていない どの領域をどのレベルまで修めればテッ クリード かなどの基準がない スキルマップ自体の更新は半年、出来れば3カ月程度で見直したい 個別要素技術に因らず、なるべく普遍的なスキルマップとなるよう志向しているので負荷は軽い まとめ テッ クリード は技術力で現場を牽引するのがミッションですが、技術に詳しい人が担う仮想的な職種であることが多いのではないでしょうか。 非常に重要な役割であるにもかかわらず、 インセンティブ や裁量の範囲など未定義なケースもあるかと思います。 組織によって「テッ クリード とは何か」は異なりますが、我々ISID金融ではその重要性を認識しており、可能な限りミッションとスキルセットを可視化しようと試みています。 また、テッ クリード に至るまでの道のりと、そもそも技術者が習得すべきスキルについての定義も必要と認識しています。 まだまだ道半ばではありますが、今回はスキルマップのご紹介でした。 最後までご覧になっていただき、誠にありがとうございました。 執筆: @mizuno.kazuhiro 、レビュー: @yamashita.tsuyoshi ( Shodo で執筆されました ) https://netflixtechblog.com/full-cycle-developers-at-netflix-a08c31f83249 ↩ クラウド などのインフラスト ラク チャを含む、システム全体の構成 ↩
こんにちは!X イノベーション 本部  エンタープライズ XRセンターの加納です。 この記事は 電通国際情報サービス Advent Calendar 2022 の15日目の記事です。 もうすぐクリスマスですが、プレゼントの用意は出来ていますか?まだですか?まだですよね? そんな方のために大貫サンタさんが靴下から取り出したのは… HMD です! 「 HMD 」という単語にピンとくる方は少数派かもしれません。 HMD とはHead-Mount-Displayの略で、つまりは VR 体験をする際に頭に被る、アレのことです。 加納と大貫サンタは3D・ VR 関係の部署に在籍しているため、今日はそんな部署ならではの機材を紹介していこうと思います。 個人で使用されているものだけではなく、事業者向けによく使用される機種も比較に入れてご紹介いたします。 比較していく機種は以下です。 Meta Quest2 Meta Quest Pro VIVE Pro2 Varjo Aero Varjo XR-3 以上の5機種が今回被り比べの対象となります。 性能面ではさまざまな記事が出ていますので、この記事での主な比較点は 重さ サイズ 価格、入手難易度 メガネを装着したまま被れるか? を重視して被り比べをしていきたいと思います。ではいきましょう! Meta Quest2 Meta Quest Pro Vive Pro2 Varjo Aero Varjo XR-3 メガネとHMD 被りやすさ4位タイ 被りやすさ3位 被りやすさ2位 被りやすさ1位 まとめ Meta Quest2 世界で最も普及している HMD と言ったらコレなのではないでしょうか。上の画像ではデフォルトのゴムバンドをEliteストラップに付け替えています。 スタンドアローン ・外付けセンサー不要で稼働し、被った状態で外部を確認することもできます(白黒画像)。 スタンドアローン ではありますがPCに接続して動かすこともでき、Steamのゲームもプレイできます。 デフォルトのゴムバンド装着での重さは 529g (参考値) Eliteストラップ装着での重さは 650g (参考値) と、非常に軽量なのが特徴です。 被ってみるとこんな感じです。 大貫サンタ「いつものって感じ」 ExRCではメンバー全員がQuest2を所有しているため、被るとあーこれだこれーと思ってしまうんですよね。一番 稼働率 の高い HMD です。 お値段は 59,400円 (2022年12月現在/公式価格)とお求めになりやすい価格、かつ家電量販店でも販売しており入手難易度も低いため、クリスマスプレゼントの第一候補になるのではないでしょうか。 Meta Quest Pro 去る2022年10月に発売された HMD で、上に出てきたMeta Quest2の上位機種です。 重さは 718g (参考値) 似たような形状のEliteストラップ装着時から比較しても70g程度の増量ですが、被った感じはいかがでしょう? 大貫サンタ「前が薄い!帽子のままでも被りやすい!あとQuest2より軽く感じる。前後のバランスがいいのかな」 Quest Proでは、Quest2にあった頭頂部をおさえるゴムバンドがなくなったことで格段に被りやすくなりました。髪型も崩れにくくなってありがたいですね。 スタンドアローン ・外付けセンサー不要・PC接続可能なのは同じですが、外部確認カメラがカラーになったのが大きな変化です。これによりAR(拡張現実)的な使い方がしやすくなりました。 加えてアイト ラッキング (視点を読み取る)・フェイスト ラッキング (表情を読み取る)などが搭載され、できることの幅が広がりました。 顔と画面の隙間は広めで、隙間からは外の光が入ってきます。オプションパーツで埋めることも出来ますが、パススルーの精度が高く外部が鮮明に見えるためかARゴーグルのような感覚があり、個人的には開いたままでいいなあという印象です。 お値段は 226 ,800円 (2022年12月現在/公式価格)とお高め。ですが装着感もできることも大幅に変わっているため、高すぎ!といった印象はありません。 まだ家電量販店で現物を手に取ることは出来ませんが、Metaの公式通販から購入できるため入手難易度は低め。クリスマスに間に合うかは…お住いの地域次第かな?といったところです。 Vive Pro2 VR の先駆者、HTC Viveの上位製品です。Meta Quest(当時はOculus Quest)が登場するまでは、 VR といったらVive!という感じでしたね。Pro2は耳に可動式のヘッドホンがついているのが特徴的なデザインです。 重さは 846g (参考値/コードが机についた状態) Vive Pro2はPC接続で稼働し、稼働には外付けセンサー( ベースステーション )を必要とします。 大貫サンタ「Meta系と比べるとかなり大きく感じる。でも重さに偏りがなくて安定してる」 外付けセンサーと言うと煩雑な感じがするかもしれませんが、フルト ラッキング をしたい場合にはVive公式でトラッカーが別売りされていますので、外付けセンサーありの製品を選択するのがおすすめです。 お値段は 178,990円 (2022年12月現在/フルキット/公式価格)でセンサー( ベースステーション 2.0)が2台同梱されています。 家電量販店で見ることは少ないですが、公式通販で入手が可能です。クリスマスに間に合うかは…Meta Quest Proと同じく、お住いの地域次第でしょうか。 Varjo Aero フィンランド に本社を構えるVarjoの製品です。ここからは エンタープライズ 色が濃くなってくるため、個人で持っている方がぐっと減るのではないでしょうか。 重さは 720g (参考値/コードが机についた状態) と、Meta Quest Proとおよそ同じくらいの重さです。 こちらもPC接続で稼働し、稼働には外付けセンサーを必要とします。 大貫サンタ「前に重心がある感じ、でも全体がそんなに重くないから大丈夫」 Varjo Aeroは軽量ながらVive Pro2よりも表示解像度が高いです。解像度が高いだけではなく、中心に向かって27PPD~35PPD(PPD…1度あたりの ピクセル 数)へと解像度が高くなっていきます。 Varjo製品は自動でPD(瞳孔間距離)を調整してくれます。これにより鮮明に映像が見えるほか、 VR酔い もしにくくなります。 お値段は 316,800円 (2022年12月現在/公式価格)とお高め。ですがこの解像度を味わうと他の VR にはなかなか戻れないかもしれませんね。 注意していただきたいのが、Varjo Aeroは紹介する5機種のうち唯一外部確認(パススルー表示)ができないタイプの HMD だということです。解像度は高いですが、用途を十分に確認した上で検討してみてください。 家電量販店では見かけないですが、公式通販で購入が可能です。ただし後述のVarjo XR-3と同じく外部センサーの入手がしづらいため、トータルでの入手難易度はちょっと高めです。 Varjo XR-3 こちらもVarjo社の製品で、紹介する中では最もハイエンドな機種になります。 重さは 1008g (参考値/コードが机についた状態) 堂々の1㎏超え。見えますか、ごっつい排気口がきらめいているのを。XR-3は作動するとかなりの熱を持ちます。 大貫サンタ「ずしっと来る。圧倒的に重い…けど画質も圧倒的にいい」 外部確認できるカメラの精度が高く、一度被るとついつい外さずにカメラであれこれ済ませてしまいがちですが、これだけ重いものを長時間被っていると首が筋肉痛になりますので気を付けましょう。 Aeroは解像度が徐々に高くなる構造でしたが、XR-3は視野の中心にあたる部分に別のディスプレイを搭載しており、中心部は71PPDを誇ります。 なお視力2.0の人の目は、60PPDを超えると ピクセル を見分けることが出来なくなるそうです。ですのでXR-3は人間の目の解像度を超えた HMD であるといえます。とんでもないですね。 こちらもPC接続で稼働し、外付けセンサーがなくても稼働しますが精度を高めるためにはセンサーが必要です。 問題の入手難易度についてですが…XR-3は エンタープライズ 用途が主であるため 一般での販売はされていません 。また昨今の円安や 半導体 不足もあり、価格もはっきり示すことが出来ません。 だいたいコンパクトカーと同じくらいの値段 だと思っておいていただけると良いかと思います。恐ろしいですね。 Varjo Aeroの項目でも出てきたセンサー( ベースステーション )についてですが、現在単体での入手が困難となっています。ですのでセンサーを同梱しないがセンサーを必要とするタイプの HMD を購入される際には、Vive Pro2などHTC製品のフルキットを購入し ベースステーション を入手する必要があります。これも入手難易度を上げる一因となっています。 ExRC に問い合わせていただけたらまるごとご相談に乗れるんですけどね!(宣伝です!) メガネと HMD ここからは横道にそれて、メガネ使用者に優しい HMD はどれなのか?を紹介させていただけたらと思います。 ※完全に主観です。個人差やメガネの形状によって変わるものですので、参考程度にご覧ください。 被りやすさ4位タイ Varjo Aero&Varjo XR-3 ストラップの構造なのでしょうか?装着した後は楽なのですが、装着するのが割と大変です。 具体的に言うと、こうやって後ろから装着するとメガネがあたってしまうので、 前側を先に被る必要があります。メガネをはめ込むような感じです。 コツが必要であることと、かぶった後の位置修正のしにくさから、ちょっとメガネに優しくない HMD だなと判定させていただきました。 被りやすさ3位 Meta Quest2 Meta Quest2にはメガネ装着用アタッチメントが付属しています。 左右の差が分かるでしょうか? 左がアタッチメントを装着したQuest2です。 ばらしてみるとこんな感じで、顔にあたる部分と本体の間に噛ませるパーツがあるのです。 なので正直メガネでも快適に装着できるのですが、全体的に小ぶりなので大きめのメガネはちょっとひっかかります。ということで3位にさせていただきました。 被りやすさ2位 Vive Pro2 こちらはメガネが入る内部のスペースが大きいです。クッションが柔らかくメガネのつるが圧迫されないため、痛くなりません。 クッションの柔らかさが伝わるでしょうか? ※メガネのつるが歪んでいるのは元からです。踏みました。 被りやすさ1位 Meta Quest Pro Meta Quest Proは顔と画面の隙間が大きく、また主におでこで HMD を固定するのでメガネに干渉する部分がほとんどありません。 指さしているところをくるくる回すことで画面と顔の距離を離すことができ、鼻当てが押さえつけられることもなく快適に使用できます。ですが、光を遮断するパーツを付けるとまた変わると思います。 まとめ 今回紹介した内容を総括した図はこのようになります。 なお価格面、有線無線等は使用状況によって優劣が変わるため順位付けは行っていません。 また繰り返しになりますがメガネとの相性は個人差がありますので、あくまで筆者の場合と思っていただけると幸いです。 個人的にはメガネユーザーとしてMeta Quest Proが好みで、大貫サンタは持ち運びやすさからMeta Quest2が好みだとのことです。 どんどん新しい製品が出てくる HMD 。ARグラスの噂も色々気になるものがありますね。 新しい製品は積極的に触って調べて、お客様に適した機材やソリューションを紹介できるよう、頑張らなきゃな!と思いを新たにしたサンタたちでした。 執筆: @kano.nanami 、レビュー: @yamada.y ( Shodo で執筆されました )
こんにちは!X イノベーション 本部  エンタープライズ XRセンターの加納です。 この記事は 電通国際情報サービス Advent Calendar 2022 の15日目の記事です。 もうすぐクリスマスですが、プレゼントの用意は出来ていますか?まだですか?まだですよね? そんな方のために大貫サンタさんが靴下から取り出したのは… HMD です! 「 HMD 」という単語にピンとくる方は少数派かもしれません。 HMD とはHead-Mount-Displayの略で、つまりは VR 体験をする際に頭に被る、アレのことです。 加納と大貫サンタは3D・ VR 関係の部署に在籍しているため、今日はそんな部署ならではの機材を紹介していこうと思います。 個人で使用されているものだけではなく、事業者向けによく使用される機種も比較に入れてご紹介いたします。 比較していく機種は以下です。 Meta Quest2 Meta Quest Pro VIVE Pro2 Varjo Aero Varjo XR-3 以上の5機種が今回被り比べの対象となります。 性能面ではさまざまな記事が出ていますので、この記事での主な比較点は 重さ サイズ 価格、入手難易度 メガネを装着したまま被れるか? を重視して被り比べをしていきたいと思います。ではいきましょう! Meta Quest2 Meta Quest Pro Vive Pro2 Varjo Aero Varjo XR-3 メガネとHMD 被りやすさ4位タイ 被りやすさ3位 被りやすさ2位 被りやすさ1位 まとめ Meta Quest2 世界で最も普及している HMD と言ったらコレなのではないでしょうか。上の画像ではデフォルトのゴムバンドをEliteストラップに付け替えています。 スタンドアローン ・外付けセンサー不要で稼働し、被った状態で外部を確認することもできます(白黒画像)。 スタンドアローン ではありますがPCに接続して動かすこともでき、Steamのゲームもプレイできます。 デフォルトのゴムバンド装着での重さは 529g (参考値) Eliteストラップ装着での重さは 650g (参考値) と、非常に軽量なのが特徴です。 被ってみるとこんな感じです。 大貫サンタ「いつものって感じ」 ExRCではメンバー全員がQuest2を所有しているため、被るとあーこれだこれーと思ってしまうんですよね。一番 稼働率 の高い HMD です。 お値段は 59,400円 (2022年12月現在/公式価格)とお求めになりやすい価格、かつ家電量販店でも販売しており入手難易度も低いため、クリスマスプレゼントの第一候補になるのではないでしょうか。 Meta Quest Pro 去る2022年10月に発売された HMD で、上に出てきたMeta Quest2の上位機種です。 重さは 718g (参考値) 似たような形状のEliteストラップ装着時から比較しても70g程度の増量ですが、被った感じはいかがでしょう? 大貫サンタ「前が薄い!帽子のままでも被りやすい!あとQuest2より軽く感じる。前後のバランスがいいのかな」 Quest Proでは、Quest2にあった頭頂部をおさえるゴムバンドがなくなったことで格段に被りやすくなりました。髪型も崩れにくくなってありがたいですね。 スタンドアローン ・外付けセンサー不要・PC接続可能なのは同じですが、外部確認カメラがカラーになったのが大きな変化です。これによりAR(拡張現実)的な使い方がしやすくなりました。 加えてアイト ラッキング (視点を読み取る)・フェイスト ラッキング (表情を読み取る)などが搭載され、できることの幅が広がりました。 顔と画面の隙間は広めで、隙間からは外の光が入ってきます。オプションパーツで埋めることも出来ますが、パススルーの精度が高く外部が鮮明に見えるためかARゴーグルのような感覚があり、個人的には開いたままでいいなあという印象です。 お値段は 226 ,800円 (2022年12月現在/公式価格)とお高め。ですが装着感もできることも大幅に変わっているため、高すぎ!といった印象はありません。 まだ家電量販店で現物を手に取ることは出来ませんが、Metaの公式通販から購入できるため入手難易度は低め。クリスマスに間に合うかは…お住いの地域次第かな?といったところです。 Vive Pro2 VR の先駆者、HTC Viveの上位製品です。Meta Quest(当時はOculus Quest)が登場するまでは、 VR といったらVive!という感じでしたね。Pro2は耳に可動式のヘッドホンがついているのが特徴的なデザインです。 重さは 846g (参考値/コードが机についた状態) Vive Pro2はPC接続で稼働し、稼働には外付けセンサー( ベースステーション )を必要とします。 大貫サンタ「Meta系と比べるとかなり大きく感じる。でも重さに偏りがなくて安定してる」 外付けセンサーと言うと煩雑な感じがするかもしれませんが、フルト ラッキング をしたい場合にはVive公式でトラッカーが別売りされていますので、外付けセンサーありの製品を選択するのがおすすめです。 お値段は 178,990円 (2022年12月現在/フルキット/公式価格)でセンサー( ベースステーション 2.0)が2台同梱されています。 家電量販店で見ることは少ないですが、公式通販で入手が可能です。クリスマスに間に合うかは…Meta Quest Proと同じく、お住いの地域次第でしょうか。 Varjo Aero フィンランド に本社を構えるVarjoの製品です。ここからは エンタープライズ 色が濃くなってくるため、個人で持っている方がぐっと減るのではないでしょうか。 重さは 720g (参考値/コードが机についた状態) と、Meta Quest Proとおよそ同じくらいの重さです。 こちらもPC接続で稼働し、稼働には外付けセンサーを必要とします。 大貫サンタ「前に重心がある感じ、でも全体がそんなに重くないから大丈夫」 Varjo Aeroは軽量ながらVive Pro2よりも表示解像度が高いです。解像度が高いだけではなく、中心に向かって27PPD~35PPD(PPD…1度あたりの ピクセル 数)へと解像度が高くなっていきます。 Varjo製品は自動でPD(瞳孔間距離)を調整してくれます。これにより鮮明に映像が見えるほか、 VR酔い もしにくくなります。 お値段は 316,800円 (2022年12月現在/公式価格)とお高め。ですがこの解像度を味わうと他の VR にはなかなか戻れないかもしれませんね。 注意していただきたいのが、Varjo Aeroは紹介する5機種のうち唯一外部確認(パススルー表示)ができないタイプの HMD だということです。解像度は高いですが、用途を十分に確認した上で検討してみてください。 家電量販店では見かけないですが、公式通販で購入が可能です。ただし後述のVarjo XR-3と同じく外部センサーの入手がしづらいため、トータルでの入手難易度はちょっと高めです。 Varjo XR-3 こちらもVarjo社の製品で、紹介する中では最もハイエンドな機種になります。 重さは 1008g (参考値/コードが机についた状態) 堂々の1㎏超え。見えますか、ごっつい排気口がきらめいているのを。XR-3は作動するとかなりの熱を持ちます。 大貫サンタ「ずしっと来る。圧倒的に重い…けど画質も圧倒的にいい」 外部確認できるカメラの精度が高く、一度被るとついつい外さずにカメラであれこれ済ませてしまいがちですが、これだけ重いものを長時間被っていると首が筋肉痛になりますので気を付けましょう。 Aeroは解像度が徐々に高くなる構造でしたが、XR-3は視野の中心にあたる部分に別のディスプレイを搭載しており、中心部は71PPDを誇ります。 なお視力2.0の人の目は、60PPDを超えると ピクセル を見分けることが出来なくなるそうです。ですのでXR-3は人間の目の解像度を超えた HMD であるといえます。とんでもないですね。 こちらもPC接続で稼働し、外付けセンサーがなくても稼働しますが精度を高めるためにはセンサーが必要です。 問題の入手難易度についてですが…XR-3は エンタープライズ 用途が主であるため 一般での販売はされていません 。また昨今の円安や 半導体 不足もあり、価格もはっきり示すことが出来ません。 だいたいコンパクトカーと同じくらいの値段 だと思っておいていただけると良いかと思います。恐ろしいですね。 Varjo Aeroの項目でも出てきたセンサー( ベースステーション )についてですが、現在単体での入手が困難となっています。ですのでセンサーを同梱しないがセンサーを必要とするタイプの HMD を購入される際には、Vive Pro2などHTC製品のフルキットを購入し ベースステーション を入手する必要があります。これも入手難易度を上げる一因となっています。 ExRC に問い合わせていただけたらまるごとご相談に乗れるんですけどね!(宣伝です!) メガネと HMD ここからは横道にそれて、メガネ使用者に優しい HMD はどれなのか?を紹介させていただけたらと思います。 ※完全に主観です。個人差やメガネの形状によって変わるものですので、参考程度にご覧ください。 被りやすさ4位タイ Varjo Aero&Varjo XR-3 ストラップの構造なのでしょうか?装着した後は楽なのですが、装着するのが割と大変です。 具体的に言うと、こうやって後ろから装着するとメガネがあたってしまうので、 前側を先に被る必要があります。メガネをはめ込むような感じです。 コツが必要であることと、かぶった後の位置修正のしにくさから、ちょっとメガネに優しくない HMD だなと判定させていただきました。 被りやすさ3位 Meta Quest2 Meta Quest2にはメガネ装着用アタッチメントが付属しています。 左右の差が分かるでしょうか? 左がアタッチメントを装着したQuest2です。 ばらしてみるとこんな感じで、顔にあたる部分と本体の間に噛ませるパーツがあるのです。 なので正直メガネでも快適に装着できるのですが、全体的に小ぶりなので大きめのメガネはちょっとひっかかります。ということで3位にさせていただきました。 被りやすさ2位 Vive Pro2 こちらはメガネが入る内部のスペースが大きいです。クッションが柔らかくメガネのつるが圧迫されないため、痛くなりません。 クッションの柔らかさが伝わるでしょうか? ※メガネのつるが歪んでいるのは元からです。踏みました。 被りやすさ1位 Meta Quest Pro Meta Quest Proは顔と画面の隙間が大きく、また主におでこで HMD を固定するのでメガネに干渉する部分がほとんどありません。 指さしているところをくるくる回すことで画面と顔の距離を離すことができ、鼻当てが押さえつけられることもなく快適に使用できます。ですが、光を遮断するパーツを付けるとまた変わると思います。 まとめ 今回紹介した内容を総括した図はこのようになります。 なお価格面、有線無線等は使用状況によって優劣が変わるため順位付けは行っていません。 また繰り返しになりますがメガネとの相性は個人差がありますので、あくまで筆者の場合と思っていただけると幸いです。 個人的にはメガネユーザーとしてMeta Quest Proが好みで、大貫サンタは持ち運びやすさからMeta Quest2が好みだとのことです。 どんどん新しい製品が出てくる HMD 。ARグラスの噂も色々気になるものがありますね。 新しい製品は積極的に触って調べて、お客様に適した機材やソリューションを紹介できるよう、頑張らなきゃな!と思いを新たにしたサンタたちでした。 執筆: @kano.nanami 、レビュー: @yamada.y ( Shodo で執筆されました )
こんにちは。X(クロス) イノベーション 本部 ソフトウェアデザインセンター セキュリティグループの耿です。これは 電通国際情報サービス Advent Calendar 2022 12/14の記事です。 プログラミング言語 で クラウド インフラをIaC化できるライブラリとして、 AWS Cloud Development Kit (CDK) が使われることが増えています。CDKではリソースのデフォルト値が設定され、便利に短く記述できる一方で、デフォルト値が必ずしも推奨されるセキュリティ構成になっていない場合があります。 Security Hubのセキュリティ基準 を利用していると、リソースをデプロイした後にアラートに気付いて修正をすることもあるのではないでしょうか。 そこでこの記事では、Security Hubのセキュリティ基準でアラートを出さないためのCDKによるリソースの記述方法を、主要なサービスの種類ごとにまとめました。CDKでリソースを作成するときに、どのような設定を気にした方が良いのか、Security Hubアラートとの マッピング として使っていただけると幸いです。 なお、矛盾しているように聞こえるかもしれませんが、Security Hubのチェックルールは 全てクリアすること自体を目的とするものではない と考えています。 AWS も全てのルールをクリアすることを期待していないでしょう。例えばS3 バケット については、 アクセスログ を出力するようにチェックするルールがありますが(S3.9)、 アクセスログ を格納する バケット の アクセスログ は出力しないため、全くアラートが上がらない状態にはできませんね。闇雲に全てのSecurity Hubアラートをクリアしようとするのではなく、 アラートからリスクを評価し、対応するかどうかを総合的に判断する プロセスが必要です。「知らずに設定していなかった」ではなく、「知った上で設定するかどうか判断する」状態にするために、Security Hubを活用するのが良いと思います。 おことわり セキュリティ基準として、記事執筆時点の AWS Foundational Security Best Practices を対象とします AWS Foundational Security Best Practices では、30以上の AWS サービスについてのコン トロール (チェックルール)がありますが、この記事ではよく使われるサービスで、CDKで作成することが多いと思われる13種のサービスのみを取り上げます 全てのコン トロール を網羅していません。CDKのデフォルト設定で対応できていないコン トロール を中心に扱います。また可用性に関わるコン トロール や、要件に応じて特別な構成が必要なコン トロール は除外しているものがあります 設定自体の意味、設定することによる影響は割愛しているため、公式ドキュメントなどでご確認ください 設定することによって追加でかかる費用については、記事内では言及していません 記載しているコードは実装方法の一つの例であり、他にも実装方法がある場合があります コード例は分かりやすさのため、関連する最小限のリソースプロパティのみ記載しています CDKで対応できる範囲、かつSecurity Hubのセキュリティ基準の範囲のセキュリティ対策であるため、当然ながらこれらの設定だけをすれば他にセキュリティ対策が不要というわけではありません CDK v2.50.0 で検証しています。今後のアップデートにより実装方法が変更になったり、デフォルトの挙動が変わる可能性があります。 AWS ドキュメントでは一部不自然な日本語訳があるため、わかりやすさのため独自の日本語訳にしているコン トロール があります。 おことわり VPC EC2.2 EC2.6 EC2.18、EC2.19 ELB ELB.4 ELB.5 ELB.6 CloudFront CloudFront.1 CloudFront.2 CloudFront.3 CloudFront.4 CloudFront.5 CloudFront.6 CloudFront.7、CloudFront.8 EC2 EC2.3 EC2.8 SSM.1 ECS ECS.5 ECS.12 ECR ECR.1 ECR.2 ECR.3 RDS RDS.3 RDS.7、RDS.8 RDS.9 RDS.10、RDS.12 RDS.11 RDS.13 RDS.14 RDS.23 RDS.24、RDS.25 DynamoDB DynamoDB.2 S3 S3.4 S3.5 S3.8 S3.9 S3.13 EFS EFS.1 SNS SNS.1 SNS.2 SQS SQS.1 API Gateway APIGateway.1 APIGateway.4 APIGateway.5 VPC EC2.2 英語タイトル:The VPC default security group should not allow inbound and outbound traffic 日本語訳: VPC のデフォルトのセキュリティグループでは、インバウンド トラフィック とアウトバウンド トラフィック を許可しないようにする必要があります https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-ec2-2 (2023/6/27更新). CDK v2.78.0より、 Vpc で restrictDefaultSecurityGroup プロパティが利用できるようになったため、対応が簡単になりました。 new Vpc ( this , "Vpc" , { restrictDefaultSecurityGroup: true , } ); ======以下は過去の対応方法です。====== CDKでは簡単にデフォルトのセキュリティグループのルールを変更できないため、現時点では この投稿 の通り、カスタムリソースを利用してルールを削除することになります。 まずは Vpc を継承した BaseVpc というクラスを作り(名前自由)、セキュリティグループのルールを削除するカスタムリソースを含めます。 コードを表示 import { Stack } from "aws-cdk-lib" ; import { CfnVPC , Vpc , VpcProps } from "aws-cdk-lib/aws-ec2" ; import { AwsCustomResource , AwsCustomResourcePolicy , PhysicalResourceId } from "aws-cdk-lib/custom-resources" ; import { Construct } from "constructs" ; export class BaseVpc extends Vpc { constructor( scope: Construct , id: string , props: VpcProps ) { super( scope , id , props ); const cfnVpc = this .node.defaultChild as CfnVPC ; const stack = Stack. of( this ); const ingressParameters = { GroupId: cfnVpc.attrDefaultSecurityGroup , IpPermissions: [ { IpProtocol: "-1" , UserIdGroupPairs: [ { GroupId: cfnVpc.attrDefaultSecurityGroup , } , ] , } , ] , } ; new AwsCustomResource ( this , "RestrictSecurityGroupIngress" , { onCreate: { service: "EC2" , action: "revokeSecurityGroupIngress" , parameters: ingressParameters , physicalResourceId: PhysicalResourceId. of( `restrict-ingress- ${ this .vpcId } - ${ cfnVpc.attrDefaultSecurityGroup } ` ), } , onDelete: { service: "EC2" , action: "authorizeSecurityGroupIngress" , parameters: ingressParameters , } , policy: AwsCustomResourcePolicy.fromSdkCalls ( { resources: [ `arn:aws:ec2: ${ stack.region } : ${ stack.account } :security-group/ ${ cfnVpc.attrDefaultSecurityGroup } ` ] , } ), } ); const egressParameters = { GroupId: cfnVpc.attrDefaultSecurityGroup , IpPermissions: [ { IpProtocol: "-1" , IpRanges: [ { CidrIp: "0.0.0.0/0" , } , ] , } , ] , } ; new AwsCustomResource ( this , "RestrictSecurityGroupEgress" , { onCreate: { service: "EC2" , action: "revokeSecurityGroupEgress" , parameters: egressParameters , physicalResourceId: PhysicalResourceId. of( `restrict-egress- ${ this .vpcId } - ${ cfnVpc.attrDefaultSecurityGroup } ` ), } , onDelete: { service: "EC2" , action: "authorizeSecurityGroupEgress" , parameters: egressParameters , } , policy: AwsCustomResourcePolicy.fromSdkCalls ( { resources: [ `arn:aws:ec2: ${ stack.region } : ${ stack.account } :security-group/ ${ cfnVpc.attrDefaultSecurityGroup } ` ] , } ), } ); } } それを Vpc の代わりに利用するだけです。 new BaseVpc ( this , "Vpc" , {} ); ======過去の対応方法ここまで====== EC2.6 英語タイトル: VPC flow logging should be enabled in all VPCs 日本語訳:すべての VPC で VPC フローログ記録を有効にする必要があります https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-ec2-6 VPCフローログ を有効にします。以下の例ではS3 バケット に出力します。 const logBucket = new s3.Bucket ( this , "LogBucket" , {} ); const vpc = new ec2.Vpc ( this , "Vpc" , {} ); new ec2.FlowLog ( this , "VpcFlowLog" , { resourceType: ec2.FlowLogResourceType.fromVpc ( vpc ), destination: ec2.FlowLogDestination.toS3 ( logBucket , "vpc/" ), trafficType: ec2.FlowLogTrafficType.ALL , } ); EC2.18、EC2.19 英語タイトル:[EC2.18] Security groups should only allow unrestricted incoming traffic for authorized ports 日本語訳:セキュリティグループは、許可されたポートのみに対して制限されていない着信 トラフィック を許可してください https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-ec2-18 英語タイトル:[EC2.19] Security groups should not allow unrestricted access to ports with high risk 日本語訳:セキュリティグループは、リスクの高いポートへの無制限アクセスを許可してはいけません https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-ec2-19 この2つは関連するコン トロール であり、チェック対象となるポート番号だけが異なるので一緒に扱います。 セキュリティグループにHTTP(80)、 HTTPS (443)以外のポートへのインバウンドルールを追加する時は無制限アクセス(0.0.0.0/0)を許可するのではなく、送信元 IPアドレス か特定のセキュリティグループに制限します。 const sshSecurityGroup = new ec2.SecurityGroup ( this , "SecurityGroup" , { vpc , } ); const anotherSecurityGroup = new ec2.SecurityGroup ( this , "AnotherSecurityGroup" , { vpc , } ); // 許可するIPアドレスレンジを制限する sshSecurityGroup.addIngressRule ( ec2.Peer.ipv4 ( "xx.xx.xx.xx/32" ), ec2.Port.tcp ( 22 )); // 別のセキュリティグループに制限する sshSecurityGroup.addIngressRule ( anotherSecurityGroup , ec2.Port.tcp ( 22 )); ELB ELB.4 英語タイトル:Application load balancers should be configured to drop HTTP headers 日本語訳:Application Load Balancer は、HTTP ヘッダーを削除するように設定する必要があります https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-elb-4 dropInvalidHeaderFields プロパティ(CDK v2.47.0~)を利用し、 無効なHTTPヘッダーの削除設定 を有効にします。 new elbv2.ApplicationLoadBalancer ( this , "Alb" , { vpc: vpc , dropInvalidHeaderFields: true , } ); ELB.5 英語タイトル:Application and Classic Load Balancers logging should be enabled 日本語訳:Application Load Balancer および Classic Load Balancer のログ記録を有効にする必要があります https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-elb-5 ロードバランサーのアクセスログ をS3 バケット に保存します。以下の例はALBの場合です。 const logBucket = new s3.Bucket ( this , "LogBucket" , {} ); const alb = new elbv2.ApplicationLoadBalancer ( this , "Alb" , { vpc: vpc , } ); alb.logAccessLogs ( logBucket , "alb" ); ELB.6 英語タイトル:Application Load Balancer deletion protection should be enabled 日本語訳:Application Load Balancer で削除保護を有効にする必要があります https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-elb-6 deletionProtection プロパティを利用し、 削除保護 を有効にします。 const alb = new elbv2.ApplicationLoadBalancer ( this , "Alb" , { vpc: vpc , deletionProtection: true , } ); CloudFront CloudFront.1 英語タイトル:CloudFront distributions should have a default root object configured 日本語訳:CloudFront ディストリビューション では、デフォルトのルートオブジェクトが設定されている必要があります https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-cloudfront-1 以下の例はS3 バケット がオリジンの場合です。あらかじめ ルートオブジェクト をアップロードした上で、 defaultRootObject プロパティでオブジェクトパスを指定します。 new cloudfront.Distribution ( this , "Distribution" , { defaultBehavior: { origin: new cloudfrontOrigins.S3Origin ( originBucket ), } , defaultRootObject: "index.html" , } ); CloudFront.2 英語タイトル:CloudFront distributions should have origin access identity enabled 日本語訳:CloudFront ディストリビューション では、オリジンアクセス アイデンティティ を有効にする必要があります https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-cloudfront-2 CDKではCloudFront ディストリビューション のオリジンをS3 バケット に指定するだけで、 OAI が自動的に設定され、特別な実装は必要ありません。 なお、OAIに代わる OAC については、記事執筆時点では CDKのL2コンストラクトでは未対応 です。 new cloudfront.Distribution ( this , "Distribution" , { defaultBehavior: { origin: new cloudfrontOrigins.S3Origin ( originBucket ), } , } ); CloudFront.3 英語タイトル:CloudFront distributions should require encryption in transit 日本語訳:CloudFront ディストリビューション では、転送中に暗号化が必要となります https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-cloudfront-3 viewerProtocolPolicy を REDIRECT_TO_HTTPS もしくは HTTPS_ONLY にすることで対応できます。 new cloudfront.Distribution ( this , "Distribution" , { defaultBehavior: { origin: new cloudfrontOrigins.S3Origin ( originBucket ), viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS , } , } ); CloudFront.4 英語タイトル:CloudFront distributions should have origin failover configured 日本語訳:CloudFront ディストリビューション では、オリジンフェイルオーバーが設定されている必要があります https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-cloudfront-4 フェイルオーバー 用のオリジンを別途作成し、 OriginGroup で primaryOrigin と fallbackOrigin を設定します。(「フェイルオーバー」と「フェイルバック」の用語が統一されていませんが、同じものを指していると考えて良いでしょう) const fallbackBucket = new s3.Bucket ( this , "FallbackBucket" , {} ); new cloudfront.Distribution ( this , "Distribution" , { defaultBehavior: { origin: new cloudfrontOrigins.OriginGroup ( { primaryOrigin: new cloudfrontOrigins.S3Origin ( originBucket ), fallbackOrigin: new cloudfrontOrigins.S3Origin ( fallbackBucket ), } ), } , } ); CloudFront.5 英語タイトル:CloudFront distributions should have logging enabled 日本語訳:CloudFront ディストリビューション では、ログ記録を有効にする必要があります https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-cloudfront-5 logBucket と logFilePrefix プロパティを指定し、 CloudFrontのログ を出力します。 const logBucket = new s3.Bucket ( this , "LogBucket" , {} ); new cloudfront.Distribution ( this , "Distribution" , { defaultBehavior: { origin: new cloudfrontOrigins.S3Origin ( originBucket ), } , logBucket: logBucket , logFilePrefix: "cloudfront/" , } ); CloudFront.6 英語タイトル:CloudFront distributions should have AWS WAF enabled 日本語訳:CloudFront ディストリビューション では、 AWS WAF を有効にする必要があります https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-cloudfront-6 AWS WAFのWeb ACL を作成し、CloudFront ディストリビューション に関連付けます。 コードを表示 const wafWebAcl = new wafv2.CfnWebACL ( this , "WafV2WebAclCloudFront" , { defaultAction: { allow: {} } , // scopeは CLOUDFRONT とする scope: "CLOUDFRONT" , visibilityConfig: { cloudWatchMetricsEnabled: true , sampledRequestsEnabled: true , metricName: "WafV2WebAclCloudFront" , } , rules: [ // このサンプルではルールは AWSManagedRulesCommonRuleSet のみ { name: "AWSManagedRulesCommonRuleSet" , priority: 1 , statement: { managedRuleGroupStatement: { vendorName: "AWS" , name: "AWSManagedRulesCommonRuleSet" , } , } , overrideAction: { none: {} } , visibilityConfig: { cloudWatchMetricsEnabled: true , sampledRequestsEnabled: true , metricName: "AWSManagedRulesCommonRuleSet" , } , } , ] , } ); new cloudfront.Distribution ( this , "Distribution" , { defaultBehavior: { origin: new cloudfrontOrigins.S3Origin ( originBucket ), } , webAclId: wafWebAcl.attrArn , } ); CloudFront.7、CloudFront.8 英語タイトル:[CloudFront.7] CloudFront distributions should use custom SSL / TLS certificates 日本語訳:CloudFront ディストリビューション では、カスタム SSL / TLS 証明書を使用する必要があります https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-cloudfront-7 英語タイトル:[CloudFront.8] CloudFront distributions should use SNI to serve HTTPS requests 日本語訳:CloudFront ディストリビューション では、SNI を使用して HTTPS リク エス トを処理する必要があります https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-cloudfront-8 TLS 証明書を発行し、CloudFront ディストリビューション に関連付けます。 こうすることで自動的にSNIも有効になります。 コードを表示 const hostedZone = route53.PublicHostedZone.fromHostedZoneAttributes ( this , "MyHostedZone" , { hostedZoneId: hostedZoneId , // ホストゾーンID zoneName: hostedZoneName , // ホストゾーン名 } ); const cloudfrontCertificate = new certificatemanager.DnsValidatedCertificate ( this , "CloudFrontCertificate" , { domainName: "my-domain.com" , hostedZone: hostedZone , validation: certificatemanager.CertificateValidation.fromDns ( hostedZone ), } ); new cloudfront.Distribution ( this , "Distribution" , { defaultBehavior: { origin: new cloudfrontOrigins.S3Origin ( originBucket ), } , certificate: cloudfrontCertificate , domainNames: [ "my-domain.com" ] , } ); EC2 EC2.3 英語タイトル:Attached EBS volumes should be encrypted at rest 日本語訳:アタッチされた EBS ボリュームは、保管時の暗号化を有効に必要があります https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-ec2-3 AWS マネージドキーでの暗号化であれば blockDevices の encrypted プロパティで実現できます( 参考 )。 なお、リージョンで EBSボリュームのデフォルト暗号化 を設定しておくことによって、 blockDevices の encrypted プロパティを明示的に指定しなくても暗号化されるようになるので楽です。 コードを表示 const ami = new ec2.AmazonLinuxImage ( { generation: ec2.AmazonLinuxGeneration.AMAZON_LINUX_2 , cpuType: ec2.AmazonLinuxCpuType.X86_64 , } ); new ec2.Instance ( this , "Instance" , { vpc , instanceType: ec2.InstanceType. of( ec2.InstanceClass.T2 , ec2.InstanceSize.MICRO ), machineImage: ami , blockDevices: [ { deviceName: "/dev/xvda" , mappingEnabled: true , volume: ec2.BlockDeviceVolume.ebs ( 30 , { deleteOnTermination: true , volumeType: ec2.EbsDeviceVolumeType.GP2 , encrypted: true , } ), } , ] , } ); EC2.8 英語タイトル:EC2 instances should use IMDSv2 日本語訳:EC2 インスタンス は IMDSv2 を使用する必要があります https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-ec2-8 requireImdsv2 プロパティで IMDSv2 のみを有効にします。EC2 インスタンス の通信内容によっては動作に問題が発生する可能性があるため、慎重な検証が必要です。 const ami = new ec2.AmazonLinuxImage ( { generation: ec2.AmazonLinuxGeneration.AMAZON_LINUX_2 , cpuType: ec2.AmazonLinuxCpuType.X86_64 , } ); new ec2.Instance ( this , "Instance" , { vpc , instanceType: ec2.InstanceType. of( ec2.InstanceClass.T2 , ec2.InstanceSize.MICRO ), machineImage: ami , requireImdsv2: true , } ); SSM.1 英語タイトル:EC2 instances should be managed by AWS Systems Manager 日本語訳:EC2 インスタンス は AWS Systems Manager により管理される必要があります https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-ssm-1 SSMでEC2 インスタンス を管理するためには いくつか条件 がありますが、特にハマりやすいのは以下の3点だと思います。( 参考 ) インスタンス にSSM エージェントがインストールされている SSMサービスへの通信ルートがある( VPC エンドポイント or インターネット経由) VPC エンドポイントの場合、 ssm 、 ec2messages 、 ssmmessages の3つが必要 インスタンス ロールが AmazonSSMManagedInstanceCore ポリシーを含んでいる 以下は SSM エージェントがインストールされている Amazon Linux 2を使用し、 VPC エンドポイントでSSMサービスに接続している例です。 コードを表示 const vpc = new ec2.Vpc ( this , "Vpc" , { subnetConfiguration: [ { name: "private-with-egress" , subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS , cidrMask: 20 , } , ] , } ); vpc.addInterfaceEndpoint ( "SSMEndpoint" , { service: ec2.InterfaceVpcEndpointAwsService.SSM , subnets: { subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS } , } ); vpc.addInterfaceEndpoint ( "SSMMessagesEndpoint" , { service: ec2.InterfaceVpcEndpointAwsService.SSM_MESSAGES , subnets: { subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS } , } ); vpc.addInterfaceEndpoint ( "EC2MESSAGESEndpoint" , { service: ec2.InterfaceVpcEndpointAwsService.EC2_MESSAGES , subnets: { subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS } , } ); const ami = new ec2.AmazonLinuxImage ( { generation: ec2.AmazonLinuxGeneration.AMAZON_LINUX_2 , cpuType: ec2.AmazonLinuxCpuType.X86_64 , } ); const role = new iam.Role ( this , "Role" , { assumedBy: new iam.ServicePrincipal ( "ec2.amazonaws.com" ), managedPolicies: [ iam.ManagedPolicy.fromAwsManagedPolicyName ( "AmazonSSMManagedInstanceCore" ) ] , } ); new ec2.Instance ( this , "Instance" , { vpc , role , instanceType: ec2.InstanceType. of( ec2.InstanceClass.T2 , ec2.InstanceSize.MICRO ), machineImage: ami , } ); ECS ECS.5 英語タイトル:ECS containers should be limited to read-only access to root filesystems 日本語訳:ECS コンテナは、ルート ファイルシステム への読み取り専用アクセスに制限する必要があります。 https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-ecs-5 ECSタスク定義のコンテナ定義で、 readonlyRootFilesystem を true とすることで ルートファイルシステムへのアクセスを読み取り専用 にします。ただしこの設定をするとコンテナの ファイルシステム に書き込むことができなくなり、 ECS Exec も使用できなくなります。 const taskDefinition = new ecs.FargateTaskDefinition ( this , "TaskDefinition" , { family: "my-task-definition" , runtimePlatform: { operatingSystemFamily: ecs.OperatingSystemFamily.LINUX } , } ); taskDefinition.addContainer ( "Container" , { containerName: "my-container" , image: ecs.ContainerImage.fromRegistry ( "my-ecr" ), readonlyRootFilesystem: true , } ); ECS.12 英語タイトル:ECS clusters should have Container Insights enabled 日本語訳:ECS クラスタ ーでは、Container Insights を有効にする必要があります https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-ecs-12 ECS クラスタ ーの containerInsights プロパティを true にすることで、 Container Insights を有効にできます。 new ecs.Cluster ( this , "Cluster" , { vpc: vpc , containerInsights: true , } ); ECR ECR.1 英語タイトル:ECR private repositories should have image scanning configured 日本語訳:ECR プライベー トリポジ トリでは、イメージスキャニングが設定されている必要があります https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-ecr-1 ECR リポジトリ の imageScanOnPush プロパティを true にすることで、 プッシュ時の基本スキャン を有効にできます。 new ecr.Repository ( this , "Repository" , { repositoryName: "my-repository" , imageScanOnPush: true , } ); ECR.2 英語タイトル:ECR private repositories should have tag immutability configured 日本語訳:ECR プライベー トリポジ トリでは、タグのイミュータビリティが設定されている必要があります https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-ecr-2 imageTagMutability を IMMUTABLE にすることで、 タグを変更不可 にします。 new ecr.Repository ( this , "Repository" , { repositoryName: "my-repository" , imageTagMutability: ecr.TagMutability.IMMUTABLE , } ); ECR.3 英語タイトル:ECR repositories should have at least one lifecycle policy configured 日本語訳:ECR リポジトリ には、少なくとも 1 つのライフサイクルポリシーが設定されている必要があります https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-ecr-3 lifecycleRules プロパティを設定することで、 ECRリポジトリのライフライクルポリシー を設定します。 new ecr.Repository ( this , "Repository" , { repositoryName: "my-repository" , lifecycleRules: [{ maxImageCount: 5 }] , } ); RDS RDS.3 英語タイトル:RDS DB instances should have encryption at rest enabled 日本語訳:RDS DB インスタンス では、保管時の暗号化が有効になっている必要があります https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-rds-3 storageEncrypted プロパティでストレージの暗号化を有効化します。 new rds.DatabaseInstance ( this , "DB" , { vpc , engine: rds.DatabaseInstanceEngine.MYSQL , vpcSubnets: { subnetType: ec2.SubnetType.PRIVATE_ISOLATED , } , storageEncrypted: true , } ); RDS.7、RDS.8 英語タイトル:[RDS.7] RDS clusters should have deletion protection enabled 日本語訳:RDS クラスタ ーでは、削除保護が有効になっている必要があります https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-rds-7 英語タイトル:[RDS.8] RDS DB instances should have deletion protection enabled 日本語訳:RDS DB インスタンス で、削除保護が有効になっている必要があります https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-rds-8 deletionProtection か removalPolicy のどちらかのプロパティを使用して、削除保護を有効にします。 // Aurora new rds.DatabaseCluster ( this , "Aurora" , { vpc , engine: rds.DatabaseClusterEngine.AURORA_MYSQL , instanceProps: { vpcSubnets: { subnetType: ec2.SubnetType.PRIVATE_ISOLATED , } , } , // deletionProtection か removalPolicy のどちらかを使用する deletionProtection: true , removalPolicy: cdk.RemovalPolicy.RETAIN , } ); // Aurora以外 new rds.DatabaseInstance ( this , "DB" , { vpc , engine: rds.DatabaseInstanceEngine.MYSQL , vpcSubnets: { subnetType: ec2.SubnetType.PRIVATE_ISOLATED , } , // deletionProtection か removalPolicy のどちらかを使用する deletionProtection: true , removalPolicy: cdk.RemovalPolicy.RETAIN , } ); RDS.9 英語タイトル:Database logging should be enabled 日本語訳:データベースログ記録を有効にする必要があります https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-rds-9 MySQL と PostgreSQL でルールをクリアするために有効にしなければならないログが異なります。 試した際には、 MySQL では「エラーログ」「一般ログ」「スロークエリログ」の3つを最低限出力していればルールはクリアになりました。 PostgreSQL では「 PostgreSQL ログ」と「アップグレードログ」の両方を出力する必要がありました。 const mysqlParameterGroup = new rds.ParameterGroup ( this , "MysqlParameterGroup" , { engine: rds.DatabaseInstanceEngine.mysql ( { version: rds.MysqlEngineVersion.VER_8_0 , } ), parameters: { general_log: "1" , slow_query_log: "1" , log_output: "FILE" , } , } ); new rds.DatabaseInstance ( this , "Mysql" , { vpc , engine: rds.DatabaseInstanceEngine.MYSQL , vpcSubnets: { subnetType: ec2.SubnetType.PRIVATE_ISOLATED , } , parameterGroup: mysqlParameterGroup , // 監査ログも含める場合は "audit" を追加 cloudwatchLogsExports: [ "error" , "general" , "slowquery" ] , } ); const postgresParameterGroup = new rds.ParameterGroup ( this , "PostgresqlParameterGroup" , { engine: rds.DatabaseInstanceEngine.postgres ( { version: rds.PostgresEngineVersion.VER_14 , } ), parameters: { log_statement: "all" , } , } ); new rds.DatabaseInstance ( this , "Postgresql" , { vpc , engine: rds.DatabaseInstanceEngine.POSTGRES , vpcSubnets: { subnetType: ec2.SubnetType.PRIVATE_ISOLATED , } , parameterGroup: postgresParameterGroup , cloudwatchLogsExports: [ "postgresql" , "upgrade" ] , } ); (2023/4/19追記)ログ出力に際し、パラメータグループの設定が抜けていたため追記しました。詳細は以下。 https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/UserGuide/USER_LogAccess.MySQL.LogFileSize.html https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/UserGuide/USER_LogAccess.Concepts.PostgreSQL.html RDS.10、RDS.12 英語タイトル:[RDS.10] IAM authentication should be configured for RDS instances 日本語訳:IAM 認証は RDS インスタンス 用に設定する必要があります https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-rds-10 英語タイトル:[RDS.12] IAM authentication should be configured for RDS clusters 日本語訳:IAM 認証は RDS クラスタ ー用に設定する必要があります https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-rds-12 iamAuthentication プロパティを true にすることで、RDSへの接続に IAM認証 を利用できます。 // Aurora new rds.DatabaseCluster ( this , "Aurora" , { vpc , engine: rds.DatabaseClusterEngine.AURORA_MYSQL , instanceProps: { vpcSubnets: { subnetType: ec2.SubnetType.PRIVATE_ISOLATED , } , } , iamAuthentication: true , } ); // Aurora以外 new rds.DatabaseInstance ( this , "DB" , { vpc , engine: rds.DatabaseInstanceEngine.MYSQL , vpcSubnets: { subnetType: ec2.SubnetType.PRIVATE_ISOLATED , } , iamAuthentication: true , } ); RDS.11 英語タイトル: Amazon RDS instances should have automatic backups enabled 日本語訳: Amazon RDS インスタンス では、自動バックアップが有効になっている必要があります https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-rds-11 backupRetention プロパティを使用して 自動バックアップ を設定し、7日以上にすることでルールをクリアできます。 new rds.DatabaseInstance ( this , "DB" , { vpc , engine: rds.DatabaseInstanceEngine.MYSQL , vpcSubnets: { subnetType: ec2.SubnetType.PRIVATE_ISOLATED , } , // 7日以上にする backupRetention: cdk.Duration.days ( 7 ), } ); RDS.13 英語タイトル:RDS automatic minor version upgrades should be enabled 日本語訳:RDS 自動マイナーバージョンアップグレードを有効にする必要があります https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-rds-13 CDKでRDS インスタンス を作成すると、DBエンジンのマイナーバージョンアップグレードはデフォルトで有効ですが、メンテナンスウィンドウは自動的に選択されるため、 preferredMaintenanceWindow で指定すると良いでしょう。なお、 UTC で記載する必要があることにご注意ください。 new rds.DatabaseInstance ( this , "DB" , { vpc , engine: rds.DatabaseInstanceEngine.MYSQL , vpcSubnets: { subnetType: ec2.SubnetType.PRIVATE_ISOLATED , } , autoMinorVersionUpgrade: true , // デフォルトでも true preferredMaintenanceWindow: "sat:18:00-sat:18:30" , // メンテナンスウィンドウ (UTC) } ); RDS.14 英語タイトル: Amazon Aurora clusters should have backtracking enabled 日本語訳: Amazon Aurora クラスタ ーはバックト ラッキング を有効にする必要があります https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-rds-14 Aurora クラスタ ーの backtrackWindow プロパティでバックトラックウィンドウを指定することで、 バックトラック を有効にできます。 new rds.DatabaseCluster ( this , "Aurora" , { engine: rds.DatabaseClusterEngine.AURORA_MYSQL , instanceProps: { vpc , vpcSubnets: { subnetType: ec2.SubnetType.PRIVATE_ISOLATED , } , } , backtrackWindow: cdk.Duration.hours ( 24 ), } ); RDS.23 英語タイトル:RDS databases and clusters should not use a database engine default port 日本語訳:RDS データベースと クラスタ ーはデータベースエンジンのデフォルトポートを使用しないでください https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-rds-23 port プロパティにより接続ポートをデフォルト値以外に変更できます。RDS インスタンス / クラスタ ー作成後の変更は不可です。 // Aurora new rds.DatabaseCluster ( this , "Aurora" , { engine: rds.DatabaseClusterEngine.AURORA_MYSQL , instanceProps: { vpc , vpcSubnets: { subnetType: ec2.SubnetType.PRIVATE_ISOLATED , } , } , port: 13306 , } ); // Aurora以外 new rds.DatabaseInstance ( this , "DB" , { vpc , engine: rds.DatabaseInstanceEngine.MYSQL , vpcSubnets: { subnetType: ec2.SubnetType.PRIVATE_ISOLATED , } , port: 13306 , } ); RDS.24、RDS.25 英語タイトル:[RDS.24] RDS database clusters should use a custom administrator username 日本語訳:RDS データベース クラスタ ーはカスタム管理者ユーザーネームを使用する必要があります https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-rds-24 英語タイトル:[RDS.25] RDS database instances should use a custom administrator username 日本語訳:RDS データベース インスタンス はカスタム管理者ユーザーネームを使用する必要があります https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-rds-25 credentials プロパティで、管理者ユーザー名をデフォルト値以外に変更できます。RDS インスタンス / クラスタ ー作成後の変更は不可です。 // Aurora new rds.DatabaseCluster ( this , "Aurora" , { engine: rds.DatabaseClusterEngine.AURORA_MYSQL , instanceProps: { vpc , vpcSubnets: { subnetType: ec2.SubnetType.PRIVATE_ISOLATED , } , } , credentials: rds.Credentials.fromUsername ( "customName" ), } ); // Aurora以外 new rds.DatabaseInstance ( this , "DB" , { vpc , engine: rds.DatabaseInstanceEngine.MYSQL , vpcSubnets: { subnetType: ec2.SubnetType.PRIVATE_ISOLATED , } , credentials: rds.Credentials.fromUsername ( "customName" ), } ); DynamoDB DynamoDB.2 英語タイトル:DynamoDB tables should have point-in-time recovery enabled 日本語訳:DynamoDB テーブルでは、ポイントインタ イムリ カバリが有効になっている必要があります。 https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-dynamodb-2 pointInTimeRecovery プロパティで ポイントインタイムリカバリ を有効にできます。 new dynamodb.Table ( this , "Table" , { partitionKey: { name: "pk" , type : dynamodb.AttributeType.STRING } , sortKey: { name: "sk" , type : dynamodb.AttributeType.STRING } , pointInTimeRecovery: true , } ); S3 S3.4 英語タイトル:S3 buckets should have server-side encryption enabled 日本語訳:S3 バケット では、サーバーサイドの暗号化を有効にする必要があります https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-s3-4 以下の例はS3マネージドの鍵(SSE-S3)を使ってS3 バケット の サーバサイド暗号化 を設定する例です。 new s3.Bucket ( this , "Bucket" , { encryption: s3.BucketEncryption.S3_MANAGED , } ); S3.5 英語タイトル:S3 buckets should require requests to use Secure Socket Layer 日本語訳:S3 バケット では、Secure Socket Layer を使用するためのリク エス トの要求が必要です https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-s3-5 暗号化されていない通信を拒否する にはS3 バケット ポリシーを設定しますが、CDKでは enforceSSL プロパティで簡単に バケット ポリシーを追加してくれます。 new s3.Bucket ( this , "Bucket" , { enforceSSL: true , } ); S3.8 英語タイトル:S3 Block Public Access setting should be enabled at the bucket level 日本語訳:S3 ブロックパブリックアクセス設定は、 バケット レベルで有効にする必要があります https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-s3-8 4つの ブロックパブリックアクセス 設定を全て有効にするには、 blockPublicAccess プロパティに BLOCK_ALL を指定します。 new s3.Bucket ( this , "Bucket" , { blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL , } ); S3.9 英語タイトル:S3 bucket server access logging should be enabled 日本語訳:S3 バケット サーバー アクセスログ 記録を有効にする必要があります https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-s3-9 serverAccessLogsBucket プロパティでS3 バケット の サーバーアクセスログ を出力する先のS3 バケット を、 serverAccessLogsPrefix プロパティで プレフィックス を指定します。 const logBucket = new s3.Bucket ( this , "LogBucket" , {} ); new s3.Bucket ( this , "Bucket" , { serverAccessLogsBucket: logBucket , serverAccessLogsPrefix: "prefix" , } ); S3.13 英語タイトル:S3 buckets should have lifecycle policies configured 日本語訳:S3 バケット では、ライフサイクルポリシーを設定する必要があります https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-s3-13 lifecycleRules プロパティでS3 バケット の ライフサイクルポリシー を設定します。以下の例は30日後にIAクラスにオブジェクトを移動し、90日後にオブジェクトを削除するポリシーです。 new s3.Bucket ( this , "Bucket" , { lifecycleRules: [ { id: "log lifecycle" , transitions: [ { storageClass: s3.StorageClass.INFREQUENT_ACCESS , transitionAfter: cdk.Duration.days ( 30 ), } , ] , expiration: cdk.Duration.days ( 90 ), } , ] , } ); EFS EFS.1 英語タイトル: Amazon EFS should be configured to encrypt file data at rest using AWS KMS 日本語訳: Amazon EFS は、 AWS KMS を使用して保管中のファイルデータを暗号化するように設定する必要があります https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-efs-1 encrypted プロパティで暗号化を設定しますが、CDK v2 ではデフォルト true なので、特別な実装は必要ありません。 (ドキュメントでは @aws-cdk/aws-efs:defaultEncryptionAtRest フィーチャーフラグを有効にしない限り encrypted のデフォルトは false である、とありますが、CDK v2ではこのフィーチャーフラグは廃止されており、有効になっているのと同じ動きになっています) new efs.FileSystem ( this , "FileSystem" , { vpc , encrypted: true , // CDK v2 ではデフォルトで true } ); SNS SNS .1 英語タイトル: SNS topics should be encrypted at rest using AWS KMS 日本語訳: SNS トピックは、 AWS KMS を使用して保管時に暗号化する必要があります。 https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-sns-1 次のように暗号化に利用するKMSキーを取得し、 masterKey プロパティに渡すことで暗号化されます。 ※トピックがCloudWatch AlarmやEventbridgeルールのターゲットの場合、デフォルトキー(alias/ aws / sns )を利用できないため要注意です。 https://repost.aws/ja/knowledge-center/cloudwatch-receive-sns-for-alarm-trigger https://repost.aws/ja/knowledge-center/sns-not-getting-eventbridge-notification const key = kms.Key.fromLookup ( this , "SNSKey" , { aliasName: "alias/aws/sns" } ); new sns.Topic ( this , "Topic2" , { masterKey: key , } ); SNS .2 英語タイトル:Logging of delivery status should be enabled for notification messages sent to a topic 日本語訳:トピックに送信される通知メッセージでは、配信ステータスのログ記録を有効にする必要があります https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-sns-2 配信ステータスのログ記録 はCloudFormationでもサポートされておらず、CDKで実装するにはカスタムリソースで SDK 呼び出しをする必要があり( 参考 )、大変です。今後のアップデートに期待します。 SQS SQS.1 英語タイトル: Amazon SQS queues should be encrypted at rest 日本語訳: Amazon SQS キューは保管中に暗号化する必要があります https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-sqs-1 2022年10月より、新しく作成されるSQSキューは デフォルトで暗号化される ようになったため、特別な実装は不要になりました。 new sqs.Queue ( this , "Queue" , { // 指定しなくても新規作成されるキューはデフォルトで暗号化される encryption: sqs.QueueEncryption.SQS_MANAGED , } ); API Gateway APIGateway.1 英語タイトル: API Gateway REST and WebSocket API logging should be enabled 日本語訳: API Gateway REST および WebSocket API ログ記録を有効にする必要があります https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-apigateway-1 CloudWatch Logs ロググループを作成し、 loggingLevel 、 accessLogDestination 、 accessLogFormat プロパティで ログの出力 を設定します。 const log = new logs.LogGroup ( this , "ApiGatewayLog" , { logGroupName: "api-gateway-log" , retention: logs.RetentionDays.SIX_MONTHS , } ); new apigateway.RestApi ( this , "RestApi" , { deployOptions: { stageName: "dev" , loggingLevel: apigateway.MethodLoggingLevel.INFO , accessLogDestination: new apigateway.LogGroupLogDestination ( log ), accessLogFormat: apigateway.AccessLogFormat.clf (), } , } ); APIGateway.4 英語タイトル: API Gateway should be associated with an AWS WAF web ACL 日本語訳: API Gateway は、 AWS WAF ウェブ ACL に関連付けられている必要があります https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-apigateway-4 AWS WAFのWeb ACL を作成し、 API Gateway のステージに関連付けます。 コードを表示 const api = new apigateway.RestApi ( this , "RestApi" , { deployOptions: { stageName: "dev" , } , } ); const wafWebAcl = new wafv2.CfnWebACL ( this , "WafV2WebAcl" , { defaultAction: { allow: {} } , // scopeは REGIONAL とする scope: "REGIONAL" , visibilityConfig: { cloudWatchMetricsEnabled: true , sampledRequestsEnabled: true , metricName: "wafWebAcl" , } , rules: [ { // このサンプルではルールは AWSManagedRulesCommonRuleSet のみ name: "AWSManagedRulesCommonRuleSet" , priority: 1 , statement: { managedRuleGroupStatement: { vendorName: "AWS" , name: "AWSManagedRulesCommonRuleSet" , } , } , overrideAction: { none: {} } , visibilityConfig: { cloudWatchMetricsEnabled: true , sampledRequestsEnabled: true , metricName: "AWSManagedRulesCommonRuleSet" , } , } , ] , } ); const webAclAssociation = new wafv2.CfnWebACLAssociation ( this , "WebAclAssociation" , { resourceArn: `arn:aws:apigateway: ${ region } ::/restapis/ ${ api4.restApiId } /stages/dev` , webAclArn: wafWebAcl.attrArn , } ); webAclAssociation.addDependsOn ( wafWebAcl ); webAclAssociation.addDependsOn ( api.deploymentStage.node.defaultChild as cdk.CfnResource ); APIGateway.5 英語タイトル: API Gateway REST API cache data should be encrypted at rest 日本語訳: API Gateway REST API のキャッシュデータは、保管中に暗号化する必要があります https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-apigateway-5 cachingEnabled を true にすることでこのルールの検出対象になり、 cacheDataEncrypted プロパティでキャッシュデータの暗号化を有効にできます。 new apigateway.RestApi ( this , "RestApi" , { deployOptions: { stageName: "dev" , cachingEnabled: true , cacheDataEncrypted: true , } , } ); 大変長い記事になりました。読んでいただき、ありがとうございました。 私たちは同じチームで働いてくれる仲間を大募集しています!たくさんのご応募をお待ちしています。 セキュリティエンジニア(セキュリティ設計) 執筆: @kou.kinyo 、レビュー: Ishizawa Kento (@kent) ( Shodo で執筆されました )
こんにちは。X(クロス) イノベーション 本部 ソフトウェアデザインセンター セキュリティグループの耿です。これは 電通国際情報サービス Advent Calendar 2022 12/14の記事です。 プログラミング言語 で クラウド インフラをIaC化できるライブラリとして、 AWS Cloud Development Kit (CDK) が使われることが増えています。CDKではリソースのデフォルト値が設定され、便利に短く記述できる一方で、デフォルト値が必ずしも推奨されるセキュリティ構成になっていない場合があります。 Security Hubのセキュリティ基準 を利用していると、リソースをデプロイした後にアラートに気付いて修正をすることもあるのではないでしょうか。 そこでこの記事では、Security Hubのセキュリティ基準でアラートを出さないためのCDKによるリソースの記述方法を、主要なサービスの種類ごとにまとめました。CDKでリソースを作成するときに、どのような設定を気にした方が良いのか、Security Hubアラートとの マッピング として使っていただけると幸いです。 なお、矛盾しているように聞こえるかもしれませんが、Security Hubのチェックルールは 全てクリアすること自体を目的とするものではない と考えています。 AWS も全てのルールをクリアすることを期待していないでしょう。例えばS3 バケット については、 アクセスログ を出力するようにチェックするルールがありますが(S3.9)、 アクセスログ を格納する バケット の アクセスログ は出力しないため、全くアラートが上がらない状態にはできませんね。闇雲に全てのSecurity Hubアラートをクリアしようとするのではなく、 アラートからリスクを評価し、対応するかどうかを総合的に判断する プロセスが必要です。「知らずに設定していなかった」ではなく、「知った上で設定するかどうか判断する」状態にするために、Security Hubを活用するのが良いと思います。 おことわり セキュリティ基準として、記事執筆時点の AWS Foundational Security Best Practices を対象とします AWS Foundational Security Best Practices では、30以上の AWS サービスについてのコン トロール (チェックルール)がありますが、この記事ではよく使われるサービスで、CDKで作成することが多いと思われる13種のサービスのみを取り上げます 全てのコン トロール を網羅していません。CDKのデフォルト設定で対応できていないコン トロール を中心に扱います。また可用性に関わるコン トロール や、要件に応じて特別な構成が必要なコン トロール は除外しているものがあります 設定自体の意味、設定することによる影響は割愛しているため、公式ドキュメントなどでご確認ください 設定することによって追加でかかる費用については、記事内では言及していません 記載しているコードは実装方法の一つの例であり、他にも実装方法がある場合があります コード例は分かりやすさのため、関連する最小限のリソースプロパティのみ記載しています CDKで対応できる範囲、かつSecurity Hubのセキュリティ基準の範囲のセキュリティ対策であるため、当然ながらこれらの設定だけをすれば他にセキュリティ対策が不要というわけではありません CDK v2.50.0 で検証しています。今後のアップデートにより実装方法が変更になったり、デフォルトの挙動が変わる可能性があります。 AWS ドキュメントでは一部不自然な日本語訳があるため、わかりやすさのため独自の日本語訳にしているコン トロール があります。 おことわり VPC EC2.2 EC2.6 EC2.18、EC2.19 ELB ELB.4 ELB.5 ELB.6 CloudFront CloudFront.1 CloudFront.2 CloudFront.3 CloudFront.4 CloudFront.5 CloudFront.6 CloudFront.7、CloudFront.8 EC2 EC2.3 EC2.8 SSM.1 ECS ECS.5 ECS.12 ECR ECR.1 ECR.2 ECR.3 RDS RDS.3 RDS.7、RDS.8 RDS.9 RDS.10、RDS.12 RDS.11 RDS.13 RDS.14 RDS.23 RDS.24、RDS.25 DynamoDB DynamoDB.2 S3 S3.4 S3.5 S3.8 S3.9 S3.13 EFS EFS.1 SNS SNS.1 SNS.2 SQS SQS.1 API Gateway APIGateway.1 APIGateway.4 APIGateway.5 VPC EC2.2 英語タイトル:The VPC default security group should not allow inbound and outbound traffic 日本語訳: VPC のデフォルトのセキュリティグループでは、インバウンド トラフィック とアウトバウンド トラフィック を許可しないようにする必要があります https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-ec2-2 (2023/6/27更新). CDK v2.78.0より、 Vpc で restrictDefaultSecurityGroup プロパティが利用できるようになったため、対応が簡単になりました。 new Vpc ( this , "Vpc" , { restrictDefaultSecurityGroup: true , } ); ======以下は過去の対応方法です。====== CDKでは簡単にデフォルトのセキュリティグループのルールを変更できないため、現時点では この投稿 の通り、カスタムリソースを利用してルールを削除することになります。 まずは Vpc を継承した BaseVpc というクラスを作り(名前自由)、セキュリティグループのルールを削除するカスタムリソースを含めます。 コードを表示 import { Stack } from "aws-cdk-lib" ; import { CfnVPC , Vpc , VpcProps } from "aws-cdk-lib/aws-ec2" ; import { AwsCustomResource , AwsCustomResourcePolicy , PhysicalResourceId } from "aws-cdk-lib/custom-resources" ; import { Construct } from "constructs" ; export class BaseVpc extends Vpc { constructor( scope: Construct , id: string , props: VpcProps ) { super( scope , id , props ); const cfnVpc = this .node.defaultChild as CfnVPC ; const stack = Stack. of( this ); const ingressParameters = { GroupId: cfnVpc.attrDefaultSecurityGroup , IpPermissions: [ { IpProtocol: "-1" , UserIdGroupPairs: [ { GroupId: cfnVpc.attrDefaultSecurityGroup , } , ] , } , ] , } ; new AwsCustomResource ( this , "RestrictSecurityGroupIngress" , { onCreate: { service: "EC2" , action: "revokeSecurityGroupIngress" , parameters: ingressParameters , physicalResourceId: PhysicalResourceId. of( `restrict-ingress- ${ this .vpcId } - ${ cfnVpc.attrDefaultSecurityGroup } ` ), } , onDelete: { service: "EC2" , action: "authorizeSecurityGroupIngress" , parameters: ingressParameters , } , policy: AwsCustomResourcePolicy.fromSdkCalls ( { resources: [ `arn:aws:ec2: ${ stack.region } : ${ stack.account } :security-group/ ${ cfnVpc.attrDefaultSecurityGroup } ` ] , } ), } ); const egressParameters = { GroupId: cfnVpc.attrDefaultSecurityGroup , IpPermissions: [ { IpProtocol: "-1" , IpRanges: [ { CidrIp: "0.0.0.0/0" , } , ] , } , ] , } ; new AwsCustomResource ( this , "RestrictSecurityGroupEgress" , { onCreate: { service: "EC2" , action: "revokeSecurityGroupEgress" , parameters: egressParameters , physicalResourceId: PhysicalResourceId. of( `restrict-egress- ${ this .vpcId } - ${ cfnVpc.attrDefaultSecurityGroup } ` ), } , onDelete: { service: "EC2" , action: "authorizeSecurityGroupEgress" , parameters: egressParameters , } , policy: AwsCustomResourcePolicy.fromSdkCalls ( { resources: [ `arn:aws:ec2: ${ stack.region } : ${ stack.account } :security-group/ ${ cfnVpc.attrDefaultSecurityGroup } ` ] , } ), } ); } } それを Vpc の代わりに利用するだけです。 new BaseVpc ( this , "Vpc" , {} ); ======過去の対応方法ここまで====== EC2.6 英語タイトル: VPC flow logging should be enabled in all VPCs 日本語訳:すべての VPC で VPC フローログ記録を有効にする必要があります https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-ec2-6 VPCフローログ を有効にします。以下の例ではS3 バケット に出力します。 const logBucket = new s3.Bucket ( this , "LogBucket" , {} ); const vpc = new ec2.Vpc ( this , "Vpc" , {} ); new ec2.FlowLog ( this , "VpcFlowLog" , { resourceType: ec2.FlowLogResourceType.fromVpc ( vpc ), destination: ec2.FlowLogDestination.toS3 ( logBucket , "vpc/" ), trafficType: ec2.FlowLogTrafficType.ALL , } ); EC2.18、EC2.19 英語タイトル:[EC2.18] Security groups should only allow unrestricted incoming traffic for authorized ports 日本語訳:セキュリティグループは、許可されたポートのみに対して制限されていない着信 トラフィック を許可してください https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-ec2-18 英語タイトル:[EC2.19] Security groups should not allow unrestricted access to ports with high risk 日本語訳:セキュリティグループは、リスクの高いポートへの無制限アクセスを許可してはいけません https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-ec2-19 この2つは関連するコン トロール であり、チェック対象となるポート番号だけが異なるので一緒に扱います。 セキュリティグループにHTTP(80)、 HTTPS (443)以外のポートへのインバウンドルールを追加する時は無制限アクセス(0.0.0.0/0)を許可するのではなく、送信元 IPアドレス か特定のセキュリティグループに制限します。 const sshSecurityGroup = new ec2.SecurityGroup ( this , "SecurityGroup" , { vpc , } ); const anotherSecurityGroup = new ec2.SecurityGroup ( this , "AnotherSecurityGroup" , { vpc , } ); // 許可するIPアドレスレンジを制限する sshSecurityGroup.addIngressRule ( ec2.Peer.ipv4 ( "xx.xx.xx.xx/32" ), ec2.Port.tcp ( 22 )); // 別のセキュリティグループに制限する sshSecurityGroup.addIngressRule ( anotherSecurityGroup , ec2.Port.tcp ( 22 )); ELB ELB.4 英語タイトル:Application load balancers should be configured to drop HTTP headers 日本語訳:Application Load Balancer は、HTTP ヘッダーを削除するように設定する必要があります https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-elb-4 dropInvalidHeaderFields プロパティ(CDK v2.47.0~)を利用し、 無効なHTTPヘッダーの削除設定 を有効にします。 new elbv2.ApplicationLoadBalancer ( this , "Alb" , { vpc: vpc , dropInvalidHeaderFields: true , } ); ELB.5 英語タイトル:Application and Classic Load Balancers logging should be enabled 日本語訳:Application Load Balancer および Classic Load Balancer のログ記録を有効にする必要があります https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-elb-5 ロードバランサーのアクセスログ をS3 バケット に保存します。以下の例はALBの場合です。 const logBucket = new s3.Bucket ( this , "LogBucket" , {} ); const alb = new elbv2.ApplicationLoadBalancer ( this , "Alb" , { vpc: vpc , } ); alb.logAccessLogs ( logBucket , "alb" ); ELB.6 英語タイトル:Application Load Balancer deletion protection should be enabled 日本語訳:Application Load Balancer で削除保護を有効にする必要があります https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-elb-6 deletionProtection プロパティを利用し、 削除保護 を有効にします。 const alb = new elbv2.ApplicationLoadBalancer ( this , "Alb" , { vpc: vpc , deletionProtection: true , } ); CloudFront CloudFront.1 英語タイトル:CloudFront distributions should have a default root object configured 日本語訳:CloudFront ディストリビューション では、デフォルトのルートオブジェクトが設定されている必要があります https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-cloudfront-1 以下の例はS3 バケット がオリジンの場合です。あらかじめ ルートオブジェクト をアップロードした上で、 defaultRootObject プロパティでオブジェクトパスを指定します。 new cloudfront.Distribution ( this , "Distribution" , { defaultBehavior: { origin: new cloudfrontOrigins.S3Origin ( originBucket ), } , defaultRootObject: "index.html" , } ); CloudFront.2 英語タイトル:CloudFront distributions should have origin access identity enabled 日本語訳:CloudFront ディストリビューション では、オリジンアクセス アイデンティティ を有効にする必要があります https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-cloudfront-2 CDKではCloudFront ディストリビューション のオリジンをS3 バケット に指定するだけで、 OAI が自動的に設定され、特別な実装は必要ありません。 なお、OAIに代わる OAC については、記事執筆時点では CDKのL2コンストラクトでは未対応 です。 new cloudfront.Distribution ( this , "Distribution" , { defaultBehavior: { origin: new cloudfrontOrigins.S3Origin ( originBucket ), } , } ); CloudFront.3 英語タイトル:CloudFront distributions should require encryption in transit 日本語訳:CloudFront ディストリビューション では、転送中に暗号化が必要となります https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-cloudfront-3 viewerProtocolPolicy を REDIRECT_TO_HTTPS もしくは HTTPS_ONLY にすることで対応できます。 new cloudfront.Distribution ( this , "Distribution" , { defaultBehavior: { origin: new cloudfrontOrigins.S3Origin ( originBucket ), viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS , } , } ); CloudFront.4 英語タイトル:CloudFront distributions should have origin failover configured 日本語訳:CloudFront ディストリビューション では、オリジンフェイルオーバーが設定されている必要があります https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-cloudfront-4 フェイルオーバー 用のオリジンを別途作成し、 OriginGroup で primaryOrigin と fallbackOrigin を設定します。(「フェイルオーバー」と「フェイルバック」の用語が統一されていませんが、同じものを指していると考えて良いでしょう) const fallbackBucket = new s3.Bucket ( this , "FallbackBucket" , {} ); new cloudfront.Distribution ( this , "Distribution" , { defaultBehavior: { origin: new cloudfrontOrigins.OriginGroup ( { primaryOrigin: new cloudfrontOrigins.S3Origin ( originBucket ), fallbackOrigin: new cloudfrontOrigins.S3Origin ( fallbackBucket ), } ), } , } ); CloudFront.5 英語タイトル:CloudFront distributions should have logging enabled 日本語訳:CloudFront ディストリビューション では、ログ記録を有効にする必要があります https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-cloudfront-5 logBucket と logFilePrefix プロパティを指定し、 CloudFrontのログ を出力します。 const logBucket = new s3.Bucket ( this , "LogBucket" , {} ); new cloudfront.Distribution ( this , "Distribution" , { defaultBehavior: { origin: new cloudfrontOrigins.S3Origin ( originBucket ), } , logBucket: logBucket , logFilePrefix: "cloudfront/" , } ); CloudFront.6 英語タイトル:CloudFront distributions should have AWS WAF enabled 日本語訳:CloudFront ディストリビューション では、 AWS WAF を有効にする必要があります https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-cloudfront-6 AWS WAFのWeb ACL を作成し、CloudFront ディストリビューション に関連付けます。 コードを表示 const wafWebAcl = new wafv2.CfnWebACL ( this , "WafV2WebAclCloudFront" , { defaultAction: { allow: {} } , // scopeは CLOUDFRONT とする scope: "CLOUDFRONT" , visibilityConfig: { cloudWatchMetricsEnabled: true , sampledRequestsEnabled: true , metricName: "WafV2WebAclCloudFront" , } , rules: [ // このサンプルではルールは AWSManagedRulesCommonRuleSet のみ { name: "AWSManagedRulesCommonRuleSet" , priority: 1 , statement: { managedRuleGroupStatement: { vendorName: "AWS" , name: "AWSManagedRulesCommonRuleSet" , } , } , overrideAction: { none: {} } , visibilityConfig: { cloudWatchMetricsEnabled: true , sampledRequestsEnabled: true , metricName: "AWSManagedRulesCommonRuleSet" , } , } , ] , } ); new cloudfront.Distribution ( this , "Distribution" , { defaultBehavior: { origin: new cloudfrontOrigins.S3Origin ( originBucket ), } , webAclId: wafWebAcl.attrArn , } ); CloudFront.7、CloudFront.8 英語タイトル:[CloudFront.7] CloudFront distributions should use custom SSL / TLS certificates 日本語訳:CloudFront ディストリビューション では、カスタム SSL / TLS 証明書を使用する必要があります https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-cloudfront-7 英語タイトル:[CloudFront.8] CloudFront distributions should use SNI to serve HTTPS requests 日本語訳:CloudFront ディストリビューション では、SNI を使用して HTTPS リク エス トを処理する必要があります https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-cloudfront-8 TLS 証明書を発行し、CloudFront ディストリビューション に関連付けます。 こうすることで自動的にSNIも有効になります。 コードを表示 const hostedZone = route53.PublicHostedZone.fromHostedZoneAttributes ( this , "MyHostedZone" , { hostedZoneId: hostedZoneId , // ホストゾーンID zoneName: hostedZoneName , // ホストゾーン名 } ); const cloudfrontCertificate = new certificatemanager.DnsValidatedCertificate ( this , "CloudFrontCertificate" , { domainName: "my-domain.com" , hostedZone: hostedZone , validation: certificatemanager.CertificateValidation.fromDns ( hostedZone ), } ); new cloudfront.Distribution ( this , "Distribution" , { defaultBehavior: { origin: new cloudfrontOrigins.S3Origin ( originBucket ), } , certificate: cloudfrontCertificate , domainNames: [ "my-domain.com" ] , } ); EC2 EC2.3 英語タイトル:Attached EBS volumes should be encrypted at rest 日本語訳:アタッチされた EBS ボリュームは、保管時の暗号化を有効に必要があります https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-ec2-3 AWS マネージドキーでの暗号化であれば blockDevices の encrypted プロパティで実現できます( 参考 )。 なお、リージョンで EBSボリュームのデフォルト暗号化 を設定しておくことによって、 blockDevices の encrypted プロパティを明示的に指定しなくても暗号化されるようになるので楽です。 コードを表示 const ami = new ec2.AmazonLinuxImage ( { generation: ec2.AmazonLinuxGeneration.AMAZON_LINUX_2 , cpuType: ec2.AmazonLinuxCpuType.X86_64 , } ); new ec2.Instance ( this , "Instance" , { vpc , instanceType: ec2.InstanceType. of( ec2.InstanceClass.T2 , ec2.InstanceSize.MICRO ), machineImage: ami , blockDevices: [ { deviceName: "/dev/xvda" , mappingEnabled: true , volume: ec2.BlockDeviceVolume.ebs ( 30 , { deleteOnTermination: true , volumeType: ec2.EbsDeviceVolumeType.GP2 , encrypted: true , } ), } , ] , } ); EC2.8 英語タイトル:EC2 instances should use IMDSv2 日本語訳:EC2 インスタンス は IMDSv2 を使用する必要があります https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-ec2-8 requireImdsv2 プロパティで IMDSv2 のみを有効にします。EC2 インスタンス の通信内容によっては動作に問題が発生する可能性があるため、慎重な検証が必要です。 const ami = new ec2.AmazonLinuxImage ( { generation: ec2.AmazonLinuxGeneration.AMAZON_LINUX_2 , cpuType: ec2.AmazonLinuxCpuType.X86_64 , } ); new ec2.Instance ( this , "Instance" , { vpc , instanceType: ec2.InstanceType. of( ec2.InstanceClass.T2 , ec2.InstanceSize.MICRO ), machineImage: ami , requireImdsv2: true , } ); SSM.1 英語タイトル:EC2 instances should be managed by AWS Systems Manager 日本語訳:EC2 インスタンス は AWS Systems Manager により管理される必要があります https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-ssm-1 SSMでEC2 インスタンス を管理するためには いくつか条件 がありますが、特にハマりやすいのは以下の3点だと思います。( 参考 ) インスタンス にSSM エージェントがインストールされている SSMサービスへの通信ルートがある( VPC エンドポイント or インターネット経由) VPC エンドポイントの場合、 ssm 、 ec2messages 、 ssmmessages の3つが必要 インスタンス ロールが AmazonSSMManagedInstanceCore ポリシーを含んでいる 以下は SSM エージェントがインストールされている Amazon Linux 2を使用し、 VPC エンドポイントでSSMサービスに接続している例です。 コードを表示 const vpc = new ec2.Vpc ( this , "Vpc" , { subnetConfiguration: [ { name: "private-with-egress" , subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS , cidrMask: 20 , } , ] , } ); vpc.addInterfaceEndpoint ( "SSMEndpoint" , { service: ec2.InterfaceVpcEndpointAwsService.SSM , subnets: { subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS } , } ); vpc.addInterfaceEndpoint ( "SSMMessagesEndpoint" , { service: ec2.InterfaceVpcEndpointAwsService.SSM_MESSAGES , subnets: { subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS } , } ); vpc.addInterfaceEndpoint ( "EC2MESSAGESEndpoint" , { service: ec2.InterfaceVpcEndpointAwsService.EC2_MESSAGES , subnets: { subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS } , } ); const ami = new ec2.AmazonLinuxImage ( { generation: ec2.AmazonLinuxGeneration.AMAZON_LINUX_2 , cpuType: ec2.AmazonLinuxCpuType.X86_64 , } ); const role = new iam.Role ( this , "Role" , { assumedBy: new iam.ServicePrincipal ( "ec2.amazonaws.com" ), managedPolicies: [ iam.ManagedPolicy.fromAwsManagedPolicyName ( "AmazonSSMManagedInstanceCore" ) ] , } ); new ec2.Instance ( this , "Instance" , { vpc , role , instanceType: ec2.InstanceType. of( ec2.InstanceClass.T2 , ec2.InstanceSize.MICRO ), machineImage: ami , } ); ECS ECS.5 英語タイトル:ECS containers should be limited to read-only access to root filesystems 日本語訳:ECS コンテナは、ルート ファイルシステム への読み取り専用アクセスに制限する必要があります。 https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-ecs-5 ECSタスク定義のコンテナ定義で、 readonlyRootFilesystem を true とすることで ルートファイルシステムへのアクセスを読み取り専用 にします。ただしこの設定をするとコンテナの ファイルシステム に書き込むことができなくなり、 ECS Exec も使用できなくなります。 const taskDefinition = new ecs.FargateTaskDefinition ( this , "TaskDefinition" , { family: "my-task-definition" , runtimePlatform: { operatingSystemFamily: ecs.OperatingSystemFamily.LINUX } , } ); taskDefinition.addContainer ( "Container" , { containerName: "my-container" , image: ecs.ContainerImage.fromRegistry ( "my-ecr" ), readonlyRootFilesystem: true , } ); ECS.12 英語タイトル:ECS clusters should have Container Insights enabled 日本語訳:ECS クラスタ ーでは、Container Insights を有効にする必要があります https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-ecs-12 ECS クラスタ ーの containerInsights プロパティを true にすることで、 Container Insights を有効にできます。 new ecs.Cluster ( this , "Cluster" , { vpc: vpc , containerInsights: true , } ); ECR ECR.1 英語タイトル:ECR private repositories should have image scanning configured 日本語訳:ECR プライベー トリポジ トリでは、イメージスキャニングが設定されている必要があります https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-ecr-1 ECR リポジトリ の imageScanOnPush プロパティを true にすることで、 プッシュ時の基本スキャン を有効にできます。 new ecr.Repository ( this , "Repository" , { repositoryName: "my-repository" , imageScanOnPush: true , } ); ECR.2 英語タイトル:ECR private repositories should have tag immutability configured 日本語訳:ECR プライベー トリポジ トリでは、タグのイミュータビリティが設定されている必要があります https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-ecr-2 imageTagMutability を IMMUTABLE にすることで、 タグを変更不可 にします。 new ecr.Repository ( this , "Repository" , { repositoryName: "my-repository" , imageTagMutability: ecr.TagMutability.IMMUTABLE , } ); ECR.3 英語タイトル:ECR repositories should have at least one lifecycle policy configured 日本語訳:ECR リポジトリ には、少なくとも 1 つのライフサイクルポリシーが設定されている必要があります https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-ecr-3 lifecycleRules プロパティを設定することで、 ECRリポジトリのライフライクルポリシー を設定します。 new ecr.Repository ( this , "Repository" , { repositoryName: "my-repository" , lifecycleRules: [{ maxImageCount: 5 }] , } ); RDS RDS.3 英語タイトル:RDS DB instances should have encryption at rest enabled 日本語訳:RDS DB インスタンス では、保管時の暗号化が有効になっている必要があります https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-rds-3 storageEncrypted プロパティでストレージの暗号化を有効化します。 new rds.DatabaseInstance ( this , "DB" , { vpc , engine: rds.DatabaseInstanceEngine.MYSQL , vpcSubnets: { subnetType: ec2.SubnetType.PRIVATE_ISOLATED , } , storageEncrypted: true , } ); RDS.7、RDS.8 英語タイトル:[RDS.7] RDS clusters should have deletion protection enabled 日本語訳:RDS クラスタ ーでは、削除保護が有効になっている必要があります https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-rds-7 英語タイトル:[RDS.8] RDS DB instances should have deletion protection enabled 日本語訳:RDS DB インスタンス で、削除保護が有効になっている必要があります https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-rds-8 deletionProtection か removalPolicy のどちらかのプロパティを使用して、削除保護を有効にします。 // Aurora new rds.DatabaseCluster ( this , "Aurora" , { vpc , engine: rds.DatabaseClusterEngine.AURORA_MYSQL , instanceProps: { vpcSubnets: { subnetType: ec2.SubnetType.PRIVATE_ISOLATED , } , } , // deletionProtection か removalPolicy のどちらかを使用する deletionProtection: true , removalPolicy: cdk.RemovalPolicy.RETAIN , } ); // Aurora以外 new rds.DatabaseInstance ( this , "DB" , { vpc , engine: rds.DatabaseInstanceEngine.MYSQL , vpcSubnets: { subnetType: ec2.SubnetType.PRIVATE_ISOLATED , } , // deletionProtection か removalPolicy のどちらかを使用する deletionProtection: true , removalPolicy: cdk.RemovalPolicy.RETAIN , } ); RDS.9 英語タイトル:Database logging should be enabled 日本語訳:データベースログ記録を有効にする必要があります https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-rds-9 MySQL と PostgreSQL でルールをクリアするために有効にしなければならないログが異なります。 試した際には、 MySQL では「エラーログ」「一般ログ」「スロークエリログ」の3つを最低限出力していればルールはクリアになりました。 PostgreSQL では「 PostgreSQL ログ」と「アップグレードログ」の両方を出力する必要がありました。 const mysqlParameterGroup = new rds.ParameterGroup ( this , "MysqlParameterGroup" , { engine: rds.DatabaseInstanceEngine.mysql ( { version: rds.MysqlEngineVersion.VER_8_0 , } ), parameters: { general_log: "1" , slow_query_log: "1" , log_output: "FILE" , } , } ); new rds.DatabaseInstance ( this , "Mysql" , { vpc , engine: rds.DatabaseInstanceEngine.MYSQL , vpcSubnets: { subnetType: ec2.SubnetType.PRIVATE_ISOLATED , } , parameterGroup: mysqlParameterGroup , // 監査ログも含める場合は "audit" を追加 cloudwatchLogsExports: [ "error" , "general" , "slowquery" ] , } ); const postgresParameterGroup = new rds.ParameterGroup ( this , "PostgresqlParameterGroup" , { engine: rds.DatabaseInstanceEngine.postgres ( { version: rds.PostgresEngineVersion.VER_14 , } ), parameters: { log_statement: "all" , } , } ); new rds.DatabaseInstance ( this , "Postgresql" , { vpc , engine: rds.DatabaseInstanceEngine.POSTGRES , vpcSubnets: { subnetType: ec2.SubnetType.PRIVATE_ISOLATED , } , parameterGroup: postgresParameterGroup , cloudwatchLogsExports: [ "postgresql" , "upgrade" ] , } ); (2023/4/19追記)ログ出力に際し、パラメータグループの設定が抜けていたため追記しました。詳細は以下。 https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/UserGuide/USER_LogAccess.MySQL.LogFileSize.html https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/UserGuide/USER_LogAccess.Concepts.PostgreSQL.html RDS.10、RDS.12 英語タイトル:[RDS.10] IAM authentication should be configured for RDS instances 日本語訳:IAM 認証は RDS インスタンス 用に設定する必要があります https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-rds-10 英語タイトル:[RDS.12] IAM authentication should be configured for RDS clusters 日本語訳:IAM 認証は RDS クラスタ ー用に設定する必要があります https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-rds-12 iamAuthentication プロパティを true にすることで、RDSへの接続に IAM認証 を利用できます。 // Aurora new rds.DatabaseCluster ( this , "Aurora" , { vpc , engine: rds.DatabaseClusterEngine.AURORA_MYSQL , instanceProps: { vpcSubnets: { subnetType: ec2.SubnetType.PRIVATE_ISOLATED , } , } , iamAuthentication: true , } ); // Aurora以外 new rds.DatabaseInstance ( this , "DB" , { vpc , engine: rds.DatabaseInstanceEngine.MYSQL , vpcSubnets: { subnetType: ec2.SubnetType.PRIVATE_ISOLATED , } , iamAuthentication: true , } ); RDS.11 英語タイトル: Amazon RDS instances should have automatic backups enabled 日本語訳: Amazon RDS インスタンス では、自動バックアップが有効になっている必要があります https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-rds-11 backupRetention プロパティを使用して 自動バックアップ を設定し、7日以上にすることでルールをクリアできます。 new rds.DatabaseInstance ( this , "DB" , { vpc , engine: rds.DatabaseInstanceEngine.MYSQL , vpcSubnets: { subnetType: ec2.SubnetType.PRIVATE_ISOLATED , } , // 7日以上にする backupRetention: cdk.Duration.days ( 7 ), } ); RDS.13 英語タイトル:RDS automatic minor version upgrades should be enabled 日本語訳:RDS 自動マイナーバージョンアップグレードを有効にする必要があります https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-rds-13 CDKでRDS インスタンス を作成すると、DBエンジンのマイナーバージョンアップグレードはデフォルトで有効ですが、メンテナンスウィンドウは自動的に選択されるため、 preferredMaintenanceWindow で指定すると良いでしょう。なお、 UTC で記載する必要があることにご注意ください。 new rds.DatabaseInstance ( this , "DB" , { vpc , engine: rds.DatabaseInstanceEngine.MYSQL , vpcSubnets: { subnetType: ec2.SubnetType.PRIVATE_ISOLATED , } , autoMinorVersionUpgrade: true , // デフォルトでも true preferredMaintenanceWindow: "sat:18:00-sat:18:30" , // メンテナンスウィンドウ (UTC) } ); RDS.14 英語タイトル: Amazon Aurora clusters should have backtracking enabled 日本語訳: Amazon Aurora クラスタ ーはバックト ラッキング を有効にする必要があります https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-rds-14 Aurora クラスタ ーの backtrackWindow プロパティでバックトラックウィンドウを指定することで、 バックトラック を有効にできます。 new rds.DatabaseCluster ( this , "Aurora" , { engine: rds.DatabaseClusterEngine.AURORA_MYSQL , instanceProps: { vpc , vpcSubnets: { subnetType: ec2.SubnetType.PRIVATE_ISOLATED , } , } , backtrackWindow: cdk.Duration.hours ( 24 ), } ); RDS.23 英語タイトル:RDS databases and clusters should not use a database engine default port 日本語訳:RDS データベースと クラスタ ーはデータベースエンジンのデフォルトポートを使用しないでください https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-rds-23 port プロパティにより接続ポートをデフォルト値以外に変更できます。RDS インスタンス / クラスタ ー作成後の変更は不可です。 // Aurora new rds.DatabaseCluster ( this , "Aurora" , { engine: rds.DatabaseClusterEngine.AURORA_MYSQL , instanceProps: { vpc , vpcSubnets: { subnetType: ec2.SubnetType.PRIVATE_ISOLATED , } , } , port: 13306 , } ); // Aurora以外 new rds.DatabaseInstance ( this , "DB" , { vpc , engine: rds.DatabaseInstanceEngine.MYSQL , vpcSubnets: { subnetType: ec2.SubnetType.PRIVATE_ISOLATED , } , port: 13306 , } ); RDS.24、RDS.25 英語タイトル:[RDS.24] RDS database clusters should use a custom administrator username 日本語訳:RDS データベース クラスタ ーはカスタム管理者ユーザーネームを使用する必要があります https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-rds-24 英語タイトル:[RDS.25] RDS database instances should use a custom administrator username 日本語訳:RDS データベース インスタンス はカスタム管理者ユーザーネームを使用する必要があります https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-rds-25 credentials プロパティで、管理者ユーザー名をデフォルト値以外に変更できます。RDS インスタンス / クラスタ ー作成後の変更は不可です。 // Aurora new rds.DatabaseCluster ( this , "Aurora" , { engine: rds.DatabaseClusterEngine.AURORA_MYSQL , instanceProps: { vpc , vpcSubnets: { subnetType: ec2.SubnetType.PRIVATE_ISOLATED , } , } , credentials: rds.Credentials.fromUsername ( "customName" ), } ); // Aurora以外 new rds.DatabaseInstance ( this , "DB" , { vpc , engine: rds.DatabaseInstanceEngine.MYSQL , vpcSubnets: { subnetType: ec2.SubnetType.PRIVATE_ISOLATED , } , credentials: rds.Credentials.fromUsername ( "customName" ), } ); DynamoDB DynamoDB.2 英語タイトル:DynamoDB tables should have point-in-time recovery enabled 日本語訳:DynamoDB テーブルでは、ポイントインタ イムリ カバリが有効になっている必要があります。 https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-dynamodb-2 pointInTimeRecovery プロパティで ポイントインタイムリカバリ を有効にできます。 new dynamodb.Table ( this , "Table" , { partitionKey: { name: "pk" , type : dynamodb.AttributeType.STRING } , sortKey: { name: "sk" , type : dynamodb.AttributeType.STRING } , pointInTimeRecovery: true , } ); S3 S3.4 英語タイトル:S3 buckets should have server-side encryption enabled 日本語訳:S3 バケット では、サーバーサイドの暗号化を有効にする必要があります https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-s3-4 以下の例はS3マネージドの鍵(SSE-S3)を使ってS3 バケット の サーバサイド暗号化 を設定する例です。 new s3.Bucket ( this , "Bucket" , { encryption: s3.BucketEncryption.S3_MANAGED , } ); S3.5 英語タイトル:S3 buckets should require requests to use Secure Socket Layer 日本語訳:S3 バケット では、Secure Socket Layer を使用するためのリク エス トの要求が必要です https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-s3-5 暗号化されていない通信を拒否する にはS3 バケット ポリシーを設定しますが、CDKでは enforceSSL プロパティで簡単に バケット ポリシーを追加してくれます。 new s3.Bucket ( this , "Bucket" , { enforceSSL: true , } ); S3.8 英語タイトル:S3 Block Public Access setting should be enabled at the bucket level 日本語訳:S3 ブロックパブリックアクセス設定は、 バケット レベルで有効にする必要があります https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-s3-8 4つの ブロックパブリックアクセス 設定を全て有効にするには、 blockPublicAccess プロパティに BLOCK_ALL を指定します。 new s3.Bucket ( this , "Bucket" , { blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL , } ); S3.9 英語タイトル:S3 bucket server access logging should be enabled 日本語訳:S3 バケット サーバー アクセスログ 記録を有効にする必要があります https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-s3-9 serverAccessLogsBucket プロパティでS3 バケット の サーバーアクセスログ を出力する先のS3 バケット を、 serverAccessLogsPrefix プロパティで プレフィックス を指定します。 const logBucket = new s3.Bucket ( this , "LogBucket" , {} ); new s3.Bucket ( this , "Bucket" , { serverAccessLogsBucket: logBucket , serverAccessLogsPrefix: "prefix" , } ); S3.13 英語タイトル:S3 buckets should have lifecycle policies configured 日本語訳:S3 バケット では、ライフサイクルポリシーを設定する必要があります https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-s3-13 lifecycleRules プロパティでS3 バケット の ライフサイクルポリシー を設定します。以下の例は30日後にIAクラスにオブジェクトを移動し、90日後にオブジェクトを削除するポリシーです。 new s3.Bucket ( this , "Bucket" , { lifecycleRules: [ { id: "log lifecycle" , transitions: [ { storageClass: s3.StorageClass.INFREQUENT_ACCESS , transitionAfter: cdk.Duration.days ( 30 ), } , ] , expiration: cdk.Duration.days ( 90 ), } , ] , } ); EFS EFS.1 英語タイトル: Amazon EFS should be configured to encrypt file data at rest using AWS KMS 日本語訳: Amazon EFS は、 AWS KMS を使用して保管中のファイルデータを暗号化するように設定する必要があります https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-efs-1 encrypted プロパティで暗号化を設定しますが、CDK v2 ではデフォルト true なので、特別な実装は必要ありません。 (ドキュメントでは @aws-cdk/aws-efs:defaultEncryptionAtRest フィーチャーフラグを有効にしない限り encrypted のデフォルトは false である、とありますが、CDK v2ではこのフィーチャーフラグは廃止されており、有効になっているのと同じ動きになっています) new efs.FileSystem ( this , "FileSystem" , { vpc , encrypted: true , // CDK v2 ではデフォルトで true } ); SNS SNS .1 英語タイトル: SNS topics should be encrypted at rest using AWS KMS 日本語訳: SNS トピックは、 AWS KMS を使用して保管時に暗号化する必要があります。 https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-sns-1 次のように暗号化に利用するKMSキーを取得し、 masterKey プロパティに渡すことで暗号化されます。 ※トピックがCloudWatch AlarmやEventbridgeルールのターゲットの場合、デフォルトキー(alias/ aws / sns )を利用できないため要注意です。 https://repost.aws/ja/knowledge-center/cloudwatch-receive-sns-for-alarm-trigger https://repost.aws/ja/knowledge-center/sns-not-getting-eventbridge-notification const key = kms.Key.fromLookup ( this , "SNSKey" , { aliasName: "alias/aws/sns" } ); new sns.Topic ( this , "Topic2" , { masterKey: key , } ); SNS .2 英語タイトル:Logging of delivery status should be enabled for notification messages sent to a topic 日本語訳:トピックに送信される通知メッセージでは、配信ステータスのログ記録を有効にする必要があります https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-sns-2 配信ステータスのログ記録 はCloudFormationでもサポートされておらず、CDKで実装するにはカスタムリソースで SDK 呼び出しをする必要があり( 参考 )、大変です。今後のアップデートに期待します。 SQS SQS.1 英語タイトル: Amazon SQS queues should be encrypted at rest 日本語訳: Amazon SQS キューは保管中に暗号化する必要があります https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-sqs-1 2022年10月より、新しく作成されるSQSキューは デフォルトで暗号化される ようになったため、特別な実装は不要になりました。 new sqs.Queue ( this , "Queue" , { // 指定しなくても新規作成されるキューはデフォルトで暗号化される encryption: sqs.QueueEncryption.SQS_MANAGED , } ); API Gateway APIGateway.1 英語タイトル: API Gateway REST and WebSocket API logging should be enabled 日本語訳: API Gateway REST および WebSocket API ログ記録を有効にする必要があります https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-apigateway-1 CloudWatch Logs ロググループを作成し、 loggingLevel 、 accessLogDestination 、 accessLogFormat プロパティで ログの出力 を設定します。 const log = new logs.LogGroup ( this , "ApiGatewayLog" , { logGroupName: "api-gateway-log" , retention: logs.RetentionDays.SIX_MONTHS , } ); new apigateway.RestApi ( this , "RestApi" , { deployOptions: { stageName: "dev" , loggingLevel: apigateway.MethodLoggingLevel.INFO , accessLogDestination: new apigateway.LogGroupLogDestination ( log ), accessLogFormat: apigateway.AccessLogFormat.clf (), } , } ); APIGateway.4 英語タイトル: API Gateway should be associated with an AWS WAF web ACL 日本語訳: API Gateway は、 AWS WAF ウェブ ACL に関連付けられている必要があります https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-apigateway-4 AWS WAFのWeb ACL を作成し、 API Gateway のステージに関連付けます。 コードを表示 const api = new apigateway.RestApi ( this , "RestApi" , { deployOptions: { stageName: "dev" , } , } ); const wafWebAcl = new wafv2.CfnWebACL ( this , "WafV2WebAcl" , { defaultAction: { allow: {} } , // scopeは REGIONAL とする scope: "REGIONAL" , visibilityConfig: { cloudWatchMetricsEnabled: true , sampledRequestsEnabled: true , metricName: "wafWebAcl" , } , rules: [ { // このサンプルではルールは AWSManagedRulesCommonRuleSet のみ name: "AWSManagedRulesCommonRuleSet" , priority: 1 , statement: { managedRuleGroupStatement: { vendorName: "AWS" , name: "AWSManagedRulesCommonRuleSet" , } , } , overrideAction: { none: {} } , visibilityConfig: { cloudWatchMetricsEnabled: true , sampledRequestsEnabled: true , metricName: "AWSManagedRulesCommonRuleSet" , } , } , ] , } ); const webAclAssociation = new wafv2.CfnWebACLAssociation ( this , "WebAclAssociation" , { resourceArn: `arn:aws:apigateway: ${ region } ::/restapis/ ${ api4.restApiId } /stages/dev` , webAclArn: wafWebAcl.attrArn , } ); webAclAssociation.addDependsOn ( wafWebAcl ); webAclAssociation.addDependsOn ( api.deploymentStage.node.defaultChild as cdk.CfnResource ); APIGateway.5 英語タイトル: API Gateway REST API cache data should be encrypted at rest 日本語訳: API Gateway REST API のキャッシュデータは、保管中に暗号化する必要があります https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-apigateway-5 cachingEnabled を true にすることでこのルールの検出対象になり、 cacheDataEncrypted プロパティでキャッシュデータの暗号化を有効にできます。 new apigateway.RestApi ( this , "RestApi" , { deployOptions: { stageName: "dev" , cachingEnabled: true , cacheDataEncrypted: true , } , } ); 大変長い記事になりました。読んでいただき、ありがとうございました。 私たちは同じチームで働いてくれる仲間を大募集しています!たくさんのご応募をお待ちしています。 セキュリティエンジニア(セキュリティ設計) 執筆: @kou.kinyo 、レビュー: Ishizawa Kento (@kent) ( Shodo で執筆されました )
電通国際情報サービス Advent Calendar 2022 の12/13の記事です。 XI本部 オープン イノベーション ラボの飯田です。 今回は、ヘルスケア×ITっぽい領域の論文の紹介になります。 最近、デジタルヘルスやデジタルセラピューティクス(DTx)といった、デジタル技術で病気の予防や診断・治療を支援することがトレンドになってきています。 ISIDでも、 VR をつかった 幻肢痛 に対するセラピーや 錐体外路症状 (EPS)重症度判定など、ヘルスケア×ITの取り組みを進めています。 www.isid.co.jp www.isid.co.jp 業務インプットのために医療×ITの論文を調べていて、面白く・身近に感じられた論文を紹介します。 内容は「 ポケモンGO によってうつに効果があるのか?」です。 DISCLAIMER 記載内容の信頼性については可能な限り十分注意をしていますが、正確性・妥当性等を担保するものではありません。 該当論文の内容を参照しておりますが、医学的な内容も含んでおり、ご自身の判断にてご覧ください。一切の責任を負いかねますので、ご了承ください。 情報の誤りや不適切な表現があった場合には記事の編集・削除を行うこともございます。 論文情報・Abstract 研究のアイディア Google Trendsから地域のうつ傾向を推定する ポケモンGOの段階的リリースを利用し、統計的因果推論(DID)を行った ポケモンGO治療効果のモデリングとその結果 理論的背景・メカニズムについて まとめ 論文情報・Abstract Cheng, Z., Greenwood, B. N., & Pavlou, P. A. (2022). Location-based mobile gaming and local depression trends: a study of Pokémon Go. Journal of Management Information Systems , 39(1), 68-101. https://www.tandfonline.com/doi/full/10.1080/07421222.2021.2023407 位置情報ベースのモバイルゲー ム( ポケモンGO )が公衆衛生の大きな問題である うつ とどのような関連するかを統計的因果推論DIDを使って調べた論文です。 モデリング の結果、 ポケモンGO の導入が短期的にうつを減少させることを示唆しています。 研究のアイディア Google Trendsから地域のうつ傾向を推定する この研究では、地域のうつ傾向を捉えるために、 Google Trendsのデータを用いて うつ病 の地域傾向を推定しています。 先行研究を参考に、各地域の うつ病 関連の用語(例、depression、stressなど)に対する Google トレンドのデータを使用して、地域のうつ傾向を0から100のスケールで推定しています(100が最大・0は最低)。 2016年1月1日から12月12日までの50週間を対象に算出しています。 ポケモンGO の段階的リリースを利用し、統計的因果推論(DID)を行った Google Trendsから地域のうつ傾向を時系列で算出したのち、その時系列データに対してDIDを行い、 ポケモンGO の治療効果・介入効果を推定しています。 DIDとは、差分の差分分析(Difference-in-differences design)であり、統計的因果推論の手法の1つです。 RTCの実験ができない(介入を行ったグループのデータは取得可能だが、非介入グループとなるデータが得られない)場合に有効な手法です。 ※DIDについての詳細は、津川先生の解説ページ等をご覧ください 差分の差分分析(Difference-in-differences design) – 医療政策学×医療経済学 ポケモンGO は、2016年に 英語圏 の12カ国166地域に段階的にリリースされました。 リリースタイミングが時間的・地理的にずれているため、DIDの考え方に則り、 ポケモンGO の治療効果・介入効果を推定しています。 リリースされていない地域を” ポケモンGO の治療・介入を受けていない対象群”とみなし、 ポケモンGO リリース前後でのうつ傾向の変化を捉えます。そして、同時にリリースされていない地域のうつ傾向の変化を考慮することで、治療効果・介入効果を推定します。 ポケモンGO 治療効果の モデリング とその結果 ポケモンGO の処理の前後で週ごとに効果を観察・推定するために以下のような式で モデリング を行っています。 - yjt:t週目のj地域での地域のうつ傾向 - 本研究での従属変数 - γj :地域の固定効果 - λt:週の固定効果 - Pre-PokemonGo jt (k):地域j でのゲーム発売から介入前 t 週までの期間が k 週である場合に 1 に等しくなる指標 - ポケモンGOのリリース前の週をベースラインとするためn Pre-PokemonGo jt (−1)を0で正規化を行った - β1 :係数(β1が負であれば、ポケモンGOの導入がうつ減少を示す) - PokemonGo jt (k):t週の時点で地域jでゲームがリリースされているかどうかを示す(リリース済みであれば1、なければ0) - Post-PokemonGo:地域j でのゲーム発売から介入後 t 週までの期間が m 週である場合に 1 に等しくなる指標 - 推定は最小二乗法でおこなう その結果は以下のようになっています。 ポケモンGO 導入直後に、 β1の値を見ると、地域のうつ傾向が有意に減少していることが読みとれます。 そして、その効果は数週間持続していて、6週間ほどでこの効果は消失しています。 これは、 ポケモンGO の人気と連動しています(ピークが2ヶ月弱で、ゲームから離脱する人が多くいた)。 一方で、 ポケモンGO のリリースと、自殺関連の検索の間には相関がないそうです。 つまり、 ポケモンGO は、重度のうつには効果がない可能性が高いと考えられます。 まとめると、 ポケモンGO は軽度なうつに対して、短期的な効果があると示唆されました。 理論的背景・メ カニ ズムについて ポケモンGO がうつを減少させるメ カニ ズムとして、3つのメ カニ ズムが考えられるそうです。 プレーヤーの身体的活動の増加 別の研究では、 ポケモンGO のプレイヤーが1日に1,500歩程度、 身体活動 を増加させたことを報告しています。 身体的活動によって、精神衛生上の利点をもたらすと多くの医学研究が指摘しています。 ゲームプレイを通じてオフラインで社会的つながりを持つこと 人々が交流し、積極的に経験(ゲームのノウハウ等)を共有すると、その後ストレスや不安が減少するとされています。 屋外に出て自然と触れ合うこと 公園などの自然に触れるだけでも、反芻(はんすう:自分自身のネガティブな側面について繰り返し考えること)が減少し、 うつ病 を緩和するとされています。 ポケモン を捕まえるためには屋外を歩き、周囲を探索しなければならないため、 身体活動 が増加する。 ゲームをプレイする中で、交流することがあり、社会的つながりを持つことができる。 ゲームの主な機能( ポケストップ やポケジムなど)は、公園などの自然がある場所に多くあり、自然と触れ合うことができる。 この3つの条件・メ カニ ズムによって、うつが減少していると考えられます。 逆に、この上記3つのメ カニ ズムを持たないゲームを行った場合、逆のにうつが増加することも観察されたそうです。 まとめ 今回、 ポケモンGO はうつに効果があるという論文を紹介いたしました。 スマートシティに代表されるように、 IT技術 やサービスがより生活と密接するようになり、公衆衛生や市民の生活のためになる機会は今後増えていくと思います。 先端 IT技術 をうまく活用し、社会課題解決とビジネス両方で価値を出せるモノを作っていければと思っています。 私たちは同じグループで共に働いていただける仲間を募集しています。 ヘルスケア×ITのように、デジタル技術を社会課題解決につなげるようなプロジェクトを推進していきたいプロジェクトマネージャーやエンジニアを募集しています。 ソリューションアーキテクト スマートシティ導入コンサルタント/スマートシティ戦略コンサルタント 執筆: @iida.michitaka 、レビュー: @yamada.y ( Shodo で執筆されました )
電通国際情報サービス Advent Calendar 2022 の12/13の記事です。 XI本部 オープン イノベーション ラボの飯田です。 今回は、ヘルスケア×ITっぽい領域の論文の紹介になります。 最近、デジタルヘルスやデジタルセラピューティクス(DTx)といった、デジタル技術で病気の予防や診断・治療を支援することがトレンドになってきています。 ISIDでも、 VR をつかった 幻肢痛 に対するセラピーや 錐体外路症状 (EPS)重症度判定など、ヘルスケア×ITの取り組みを進めています。 www.isid.co.jp www.isid.co.jp 業務インプットのために医療×ITの論文を調べていて、面白く・身近に感じられた論文を紹介します。 内容は「 ポケモンGO によってうつに効果があるのか?」です。 DISCLAIMER 記載内容の信頼性については可能な限り十分注意をしていますが、正確性・妥当性等を担保するものではありません。 該当論文の内容を参照しておりますが、医学的な内容も含んでおり、ご自身の判断にてご覧ください。一切の責任を負いかねますので、ご了承ください。 情報の誤りや不適切な表現があった場合には記事の編集・削除を行うこともございます。 論文情報・Abstract 研究のアイディア Google Trendsから地域のうつ傾向を推定する ポケモンGOの段階的リリースを利用し、統計的因果推論(DID)を行った ポケモンGO治療効果のモデリングとその結果 理論的背景・メカニズムについて まとめ 論文情報・Abstract Cheng, Z., Greenwood, B. N., & Pavlou, P. A. (2022). Location-based mobile gaming and local depression trends: a study of Pokémon Go. Journal of Management Information Systems , 39(1), 68-101. https://www.tandfonline.com/doi/full/10.1080/07421222.2021.2023407 位置情報ベースのモバイルゲー ム( ポケモンGO )が公衆衛生の大きな問題である うつ とどのような関連するかを統計的因果推論DIDを使って調べた論文です。 モデリング の結果、 ポケモンGO の導入が短期的にうつを減少させることを示唆しています。 研究のアイディア Google Trendsから地域のうつ傾向を推定する この研究では、地域のうつ傾向を捉えるために、 Google Trendsのデータを用いて うつ病 の地域傾向を推定しています。 先行研究を参考に、各地域の うつ病 関連の用語(例、depression、stressなど)に対する Google トレンドのデータを使用して、地域のうつ傾向を0から100のスケールで推定しています(100が最大・0は最低)。 2016年1月1日から12月12日までの50週間を対象に算出しています。 ポケモンGO の段階的リリースを利用し、統計的因果推論(DID)を行った Google Trendsから地域のうつ傾向を時系列で算出したのち、その時系列データに対してDIDを行い、 ポケモンGO の治療効果・介入効果を推定しています。 DIDとは、差分の差分分析(Difference-in-differences design)であり、統計的因果推論の手法の1つです。 RTCの実験ができない(介入を行ったグループのデータは取得可能だが、非介入グループとなるデータが得られない)場合に有効な手法です。 ※DIDについての詳細は、津川先生の解説ページ等をご覧ください 差分の差分分析(Difference-in-differences design) – 医療政策学×医療経済学 ポケモンGO は、2016年に 英語圏 の12カ国166地域に段階的にリリースされました。 リリースタイミングが時間的・地理的にずれているため、DIDの考え方に則り、 ポケモンGO の治療効果・介入効果を推定しています。 リリースされていない地域を” ポケモンGO の治療・介入を受けていない対象群”とみなし、 ポケモンGO リリース前後でのうつ傾向の変化を捉えます。そして、同時にリリースされていない地域のうつ傾向の変化を考慮することで、治療効果・介入効果を推定します。 ポケモンGO 治療効果の モデリング とその結果 ポケモンGO の処理の前後で週ごとに効果を観察・推定するために以下のような式で モデリング を行っています。 - yjt:t週目のj地域での地域のうつ傾向 - 本研究での従属変数 - γj :地域の固定効果 - λt:週の固定効果 - Pre-PokemonGo jt (k):地域j でのゲーム発売から介入前 t 週までの期間が k 週である場合に 1 に等しくなる指標 - ポケモンGOのリリース前の週をベースラインとするためn Pre-PokemonGo jt (−1)を0で正規化を行った - β1 :係数(β1が負であれば、ポケモンGOの導入がうつ減少を示す) - PokemonGo jt (k):t週の時点で地域jでゲームがリリースされているかどうかを示す(リリース済みであれば1、なければ0) - Post-PokemonGo:地域j でのゲーム発売から介入後 t 週までの期間が m 週である場合に 1 に等しくなる指標 - 推定は最小二乗法でおこなう その結果は以下のようになっています。 ポケモンGO 導入直後に、 β1の値を見ると、地域のうつ傾向が有意に減少していることが読みとれます。 そして、その効果は数週間持続していて、6週間ほどでこの効果は消失しています。 これは、 ポケモンGO の人気と連動しています(ピークが2ヶ月弱で、ゲームから離脱する人が多くいた)。 一方で、 ポケモンGO のリリースと、自殺関連の検索の間には相関がないそうです。 つまり、 ポケモンGO は、重度のうつには効果がない可能性が高いと考えられます。 まとめると、 ポケモンGO は軽度なうつに対して、短期的な効果があると示唆されました。 理論的背景・メ カニ ズムについて ポケモンGO がうつを減少させるメ カニ ズムとして、3つのメ カニ ズムが考えられるそうです。 プレーヤーの身体的活動の増加 別の研究では、 ポケモンGO のプレイヤーが1日に1,500歩程度、 身体活動 を増加させたことを報告しています。 身体的活動によって、精神衛生上の利点をもたらすと多くの医学研究が指摘しています。 ゲームプレイを通じてオフラインで社会的つながりを持つこと 人々が交流し、積極的に経験(ゲームのノウハウ等)を共有すると、その後ストレスや不安が減少するとされています。 屋外に出て自然と触れ合うこと 公園などの自然に触れるだけでも、反芻(はんすう:自分自身のネガティブな側面について繰り返し考えること)が減少し、 うつ病 を緩和するとされています。 ポケモン を捕まえるためには屋外を歩き、周囲を探索しなければならないため、 身体活動 が増加する。 ゲームをプレイする中で、交流することがあり、社会的つながりを持つことができる。 ゲームの主な機能( ポケストップ やポケジムなど)は、公園などの自然がある場所に多くあり、自然と触れ合うことができる。 この3つの条件・メ カニ ズムによって、うつが減少していると考えられます。 逆に、この上記3つのメ カニ ズムを持たないゲームを行った場合、逆のにうつが増加することも観察されたそうです。 まとめ 今回、 ポケモンGO はうつに効果があるという論文を紹介いたしました。 スマートシティに代表されるように、 IT技術 やサービスがより生活と密接するようになり、公衆衛生や市民の生活のためになる機会は今後増えていくと思います。 先端 IT技術 をうまく活用し、社会課題解決とビジネス両方で価値を出せるモノを作っていければと思っています。 私たちは同じグループで共に働いていただける仲間を募集しています。 ヘルスケア×ITのように、デジタル技術を社会課題解決につなげるようなプロジェクトを推進していきたいプロジェクトマネージャーやエンジニアを募集しています。 ソリューションアーキテクト スマートシティ導入コンサルタント/スマートシティ戦略コンサルタント 執筆: @iida.michitaka 、レビュー: @yamada.y ( Shodo で執筆されました )
こんにちは、ISID 金融ソリューション事業部の若本です。 本記事は 電通国際情報サービス Advent Calendar 2022 の12日目の記事となります!前日の記事は徳山さんの「フロントエンド開発にちょっと慣れてきた方におすすめしたいPatterns.dev」でした。 この記事では、今年発表されたAIモデルである「nvdiffrec」を使って、 iPhone で撮影した動画から3Dモデルを作成する手順 をご紹介します。 今回作成したのは、上記画像の「青い遊具」になります! はじめに 近年、NeRFをはじめとして リアルの物体や景色を3Dに落とし込む技術 が盛り上がりを見せています。 先日の記事 でもNeRFから3Dのシーンを生成しました。3Dの視点がサクッと生成できるのはすごいですよね。 上記のようなNeRFの結果は、そのまま3Dアプリケーションで使えるものでしょうか?現在、 答えは「No」 です。 NeRFの出力は「輝度場」と呼ばれるものであり、カメラから見える光の反射を推定して出力しています。一方、 Blender をはじめとする3DCG制作エンジンでは、メッシュなどを取り込むのが一般的です。メッシュは形状として落とし込まれたデータになっており、NeRFのように曖昧な箇所をぼかした表現はできません。 そのため、既存の3DCG制作エンジンにNeRFの結果を取り込むには、一工夫が必要になります。 たとえば、先日ご紹介したInstant NGP 1 では、Instant NeRFの出力から古典的な手法を用いてメッシュ化する試験的な機能が備わっています。この機能を使うと、下記のような結果が得られます。 大枠はNeRFの結果と変わりませんが、印象がかなり異なるかと思います。出力されたメッシュの凹凸が激しく、メッシュについている色がfloater(周りに浮いている雲のようなもの)の色になっていたり、元の画像と全く異なる結果となっています。NeRFではうまくぼかせていたものを無理やりメッシュにしたために 見た目の印象がかなり変わってしまいます 。 そこで今回は、3Dメッシュを直接生成するAIモデルを検証することにしました。 nvdiffrecとは? nvdiffrec 2 は、 3Dメッシュを直接生成するAIモデル です。 NeRF同様、 nvidia が開発したAIモデルであり、画像処理のトップカンファレンスであるCVPR2022にも採択されています。 nvdiffrecでは上記の課題に対し、3DCG制作エンジンに直接取り込めるアウトプットを直接学習します。このとき、形状・素材・照明環境をそれぞれ最適化することによって、画像の見え方から「対象の素材や画像内の照明環境」を推定できます。下記はnvdiffrecの公式の紹介動画になります。 結果として、上記動画内のように元の形状や色に近い3Dメッシュが出力されます。 自分で撮影した動画でnvdiffrecを動かす方法 本記事では、 nvdiffrecのリポジトリ を用いて、 自分で撮影した動画から3Dメッシュを作成する手順 をご紹介します。 なお、本記事で使用するコードは こちら に配置しています。 基本的にはポチポチ「Shift+Enter」を押すだけで動くようになっております のでご活用ください。 0. 事前準備 Instant NGP の実行環境(別環境) Google drive の準備 実行コード のダウンロードと Google drive への配置 ※撮影機材はiPhone11、縦動画撮影での動作を確認済みです。 1. 動画を撮影 (可能であれば)360度から対象物を撮影しましょう。カメラの位置情報を推定するため、対象物は動かさないほうがよいです。この際、筆者は 物体が見切れないように撮影を行いました 。 撮影の基本的なコツは 以前の記事 を参考にしていただけると幸いです。 ここでは、対象物を2周する形で計20秒間の動画撮影を行っています。筆者はiPhone11で撮影したため、解像度は1920×1080です。リソースの都合上、後段の処理で512pixelまで解像度を落とすため、512pixel以上の解像度であれば問題はありません。 また、必須ではありませんが、撮影後に一度NeRFで出力してみることをおすすめします。NeRFがうまく出力できていない場合、以降のプロセスでの結果が期待できなくなります。 2. Google Drive 上で画像に変換 撮影した動画を Google Drive にアップし、 こちらのコード で画像に変換します。 Google Colaboratory(以下Colabと呼称)上でコードを開くと下記のような画面が現れますので、ファイル名を設定して実行します。 下記のように「<フォルダ名>/images/<切り出した画像名>」のフォルダ階層になっていれば方法は特に問いません。 3. カメラ情報の推定&物体のマスク推定(ローカル環境) nvdiffrecを動作させる前に、以下の情報が必要になります。 imagesと同じ階層に「masks」フォルダを配置し、その中に「images」内の画像に対応したマスク画像 imagesと同じ階層にカメラ位置の推定情報(pose_bounds.npy) 今回のコードにはその処理を含めていないため、別の環境で用意することにします。 筆者は、nvdiffrec内の issue を参考に、有志の方が作成されている colmap2poses.py を使用しました(2022年11月時点で公式からカスタムデー タセット への適用手順は示されていません)。Instant NeRFが動作する環境であれば、 rembg を追加でインストールするだけで動作します。 pip install rembg 「images」が入ったフォルダとこの スクリプト をInstant NeRFが動作する環境に配置し、下記のコマンドで動かしました。 python colmap2poses.py --mask "imagesを含むフォルダへのpath" --colmap_path "colmapのpath" 上記の スクリプト では、カメラ位置の推定をcolmapを用いて行うとともに、AIモデル「U 2Net 3 」を用いた物体の切り抜きを行っています。 実行後、下記のようなフォルダ構成になっていればOKです。 ただし、「masks」に格納されているマスク画像はrembgで自動推定されたものですので、影が予測結果に入ってしまったり、予測結果が欠けていたりしています。 そこで、筆者はより精度を高めるために、 アノテーション ツール「 EISeg 4 」を用いて直接マスク画像の作成を行いました。 600枚弱の画像があったため、4時間ほどかけて アノテーション を行いました。手間がかかるため、少しでも良い結果を得たい場合のみおすすめします。 4. nvdiffrecの実行 3.の結果を Google Drive にアップし、 こちらのコード に沿ってColab上でnvdiffrecを学習させます。学習の手順は下記の通りです。 ①:関連ライブラリをインストールする ②:前処理を行う ③:nvdiffrecのパラメータを設定して実行する それぞれについて、簡単に見てみましょう。 4-①:関連ライブラリをインストールする Google drive を同期した後、関連ライブラリをインストールします。 初回のみ、nvdiffrecを Google drive 上にcloneする必要があります。下記はColab上で コード を開いた画面です。cloneしていない場合のみ、「nvdiffrec_install」のチェックが必要になります。 4-②:前処理を行う こちらは コード の下記3つのセルを順番に実行すると完了します。 上図内のコードでは、基本的にcroppingとscalingの2つの処理を行いました。 croppingでは、学習しやすいように正方形の画像をもとの画像から切り出しています。本来であれば、画像をそのままモデルに入れることが望ましいですが、今回は時間の都合上断念しました。さらに、見切れている画像を学習に使わない処理も事前に実施しています。 scalingでは、学習に用いる画像のリサイズを行いました。筆者は512pixelで動作確認しましたが、こちらを大きくするほど高い品質が見込めるため、リソースに余裕のある方は大きくすることを推奨します。なお、scalingのコードはnvdiffrec/data/derd/scale_images.pyとほぼ同様です。 4-③:nvdiffrecのパラメータを設定して実行する 最後に、パラメータを設定して実行してみましょう。colab上ではいくつか GUI で設定できるようにしてありますが、それ以外の設定項目もありますので、適宜nvdiffrecの リポジトリ をご確認ください。 !python train.py --config $config_path 上記のコードを実行すると、nvdiffrecの学習が開始されます。学習が完了すると、nvdiffrec/out下に.obj形式、.mtl形式のメッシュとテクス チャフ ァイルが生成されます。 今回使用したデータでは、下記のような結果が得られました。 光の反射が特殊だった上部については推定がうまくいきませんでしたが、それらしきメッシュは作成することができました ! 形状と色が改善できており、そのままNeRFでメッシュを出力するよりも良い結果が得られているといえそうです。メッシュの出力は、マシンスペックやデータ量の増加、チューニングの工夫によってさらに改善することが見込めます。 最終的に、作成したメッシュは、 もちろん Blender などで取り込むことができます ! せっかくの アドベントカレンダー なので、クリスマス仕様にしてみました! おわりに この記事ではnvdiffrecを用いて、 iPhone で撮影した動画から3Dメッシュを作成する手順についてご紹介しました。 3DCG技術に関するAIの発展は目覚ましく、実用的な3DモデルをAIで手軽に生成する未来は着実に近づいていると感じています。ワクワクするような技術が次々と現れる今日ですが、引き続き楽しんでキャッチアップしていきたいと思います。 最後までお読みいただき、ありがとうございました。 現在ISIDは web3領域のグループ横断組織 を立ち上げ、Web3および メタバース 領域のR&Dを行っております(カテゴリー「3DCG」の記事は こちら )。 もし本領域にご興味のある方や、一緒にチャレンジしていきたい方は、ぜひお気軽にご連絡ください! 私たちと同じチームで働いてくれる仲間を、是非お待ちしております! ISID採用ページ 執筆: @wakamoto.ryosuke 、レビュー: @yamada.y ( Shodo で執筆されました ) NVIDIA Instant NeRF NGP ↩ Extracting Triangular 3D Models, Materials, and Lighting From Images ↩ U 2 -Net: Going Deeper with Nested U-Structure for Salient Object Detection ↩ EISeg: An Efficient Interactive Segmentation Tool based on PaddlePaddle ↩