電通総研のブログ - TECH PLAY

TECH PLAY

電通総研

電通総研 の技術ブログ

856

初めまして。 エンタープライズ 第一本部の佐藤悠です。 本記事では AWS Solution Architect Associate(以下:SAA)を受験料のみで取得した 経験談 を紹介します。 読者想定としてはCloud Practitionerを取得した後にSAAの取得を考えているかつ コスパ 良く学習したい人向けです。 目次 著者スペック 受験の理由 学習方法 学習教材(無料の教材を知りたい人向け) テスト当日 まとめ 著者の経歴 大学や院ではほとんど情報をやっていない 基本情報取得(入社後) 新人1年目 インフラ業務にまつわる OJT 課題を1か月間実施 Cloud Practitionerを取得 勉強は苦手 資格取得の勉強にかかる金額は安く済ませたい! OJT 課題は、EC2/RDS インスタンス を作成しCloudWatchで監視して SNS で通知するというような環境をInfrastructure as a Code(以下:IaC)で作成した内容ですが、この経験で大きく合格に近づくかといったらそうでもないです。2~3日、初心者向けハンズオンで GUI を触るとすぐに相応の知識は取得できます。 どんなハンズオンかというと以下が良いと思います。 AWS ハンズオン チュートリアル Create and Connect to a Microsoft SQL Server Database with Amazon RDS https://aws.amazon.com/jp/getting-started/hands-on/create-microsoft-sql-db/?ref=gsrchandson&id=updated AWS 環境のセットアップ https://aws.amazon.com/jp/getting-started/guides/setup-environment/?ref=gsrchandson 当然 無料 です。 Cloud Practitionerは可能なら取得しておいてください。後述しますがSAAに対しては段階的なアプローチが必須だと思います。 IaC 余談ですがIaCはその可搬性や再現性に意味があり、コード一発で同質のインフラが構築できたり、修正もコードベースで管理しているので簡単にできたりなどの強みがあります。このロジックを理解することはインフラを理解するにあたって重要です。ただ、再三ですがこの内容はまれにCloudFormationの部分で似た概念が出題されるかも?のレベルなのでIaCに関する知識を深堀りすることが合格の要因にはならないです。 受験の理由 Cloud Practitionerは点的な知識の連続であり、相互の連携が要求されるインフラ業務にはもう一段踏み込んだ学習が必要だなと感じたためです。 特に AWS ではすべてをハンズオンでやっていると無料枠や、個人利用の規模感から逸脱してしまって大変なので試験対策を通じて体系的に理解したいと思いました。 合格後は自分が期待した通りの知識を獲得できていたので取得して正解でした。 あれ?案件のインフラ構成図が理解できる!!!(感動) 火垂るの墓 くらい泣けます。 学習方法 試験概要 内容 サービス間連携と以下を満たすソリューションの選択 コスト最適化 運用上の優秀性 セキュリティ 信頼性 出題形式 選択肢から単一または複数の選択をする形式 時間 130分 問題数 65問 合格点 7割 公式に従うと1年の実務期間が取得者の目安らしいです。 これを学習のみで詰め込むと最低50時間は絶対に確保する必要があります。試験のバージョンがC-03になってからはSAAの難しさが段違いで現在勉強中のSAProとの差が小さいように感じます。 先述したCloud Practitioner取得を勧めるのはこの理由からです。実務経験がない場合は、多分全てがわからない状態で始まるので、最低限の知識なしにいきなりこのレベルにあたるのはハードルが高いです。 学習教材(無料の教材を知りたい人向け) 以下のサイトがとても良いです。 ping -t AWS SAA 最強問題集 https://mondai.ping-t.com/g 無料の会員登録で勉強できるのですが、出題数を絞ったり進行度合いを表示してくれたりと、なんで無料なんだろうと思うくらいです。丁寧な解説も回答の途中で見られるので適当にチェックつけて正解したから、あとで見返せないという心配も必要ない。 これは最強の名にふさわしいです。 ありがとう...「 ping -t」...本当にそれ以外の言葉が見つからない。 こちらの「【試験レベル】Well-Architected Frameworkに基づいた設計」の部分を5問単位で切って解きなおしのスパンを短くすることでストレスなくできます。 私は間違いをメモに丁寧にまとめながら2周で合格しました。 テスト当日 テストセンタで受けました。自宅受験を考えている人はおすすめしません(1敗)。 試験時間は長いのでトイレに行くことと、問題を半分解いたら休憩することをお勧めします。 あと解答に自信がない場合はチェックマークを付けて最後に見直すと他の問題の文脈で語られていたサービスをヒントに解けることがあるので、やってみてください。 まとめ この試験は AWS サービス間の連携など、体系的で意味ある単位での学びになるいい機会だと思います。日常のアイディアが AWS のこのサービスで実現できそう!とかなんで動かないの?に対して予測をつけるのにかなり役立ちます。 試験取得後に自分が実感した変化は、困りごと解決の選択肢が浮かぶようになったことです。 例として、今持っているPCの GPU のスペックが足りないんだけど、これ クラウド 側で レンダリング できないかな?とか、EventBridgeがStepFunctionをキックしないのは権限許可あたりかな?など着想や思考の幅が広がりました。 AWS について詳しくなりたい人は是非、取得を検討してみてください! 最後に ping -tは最強です。これは繰り返し言いたいです。 ここまで読んでいただきありがとうございます。 一人でも多く AWS に興味を持っていただけたら嬉しいです。 そして試験合格を祈ります。 私たちは一緒に働いてくれる仲間を募集しています! 電通総研 新卒採用サイト 執筆: @sato.yu 、レビュー: @yamashita.tsuyoshi ( Shodo で執筆されました )
こんにちは、株式会社 電通 総研の新卒2年目の宮崎 博寿 です。 クロス イノベーション 本部 クラウド イノベーション センターに所属しております。 24年の12月上旬に、世界最大規模の AWS カンファレンス 「 AWS re:Invent」 に参加しましたので、現地で体験した学びや感想、 GameDay の話を中心にレポートします。 re:Inventとは? 一言でいうと、 AWS のすべてが詰まった祭典です。数多くのセッションやワークショップ、ゲーム化された学習イベント(GameDay)、IT各社主催のExpoなどが同時多発的に行われます。 現地の熱気や規模感は想像以上で、 AWS がどれだけ世界に インパク トを与えているかを肌で感じることができました。 概要 場所: アメリ カ・ ネバダ 州ラスベガス 期間:例年5日間(例年11月下旬〜12月上旬) 参加者数:世界中から6万人以上、日本からは2000人超と現地で聞きました。( SIer や事業会社のIT部門の方など幅広い業界の方々) コンテンツ数:5日間で約3500以上(re:Invent公式サイトのカタログで確認) 参加した主なコンテンツ 1. 基調講演 AWS のCEOをはじめとした幹部陣が、最新サービスの発表や将来的なロードマップを紹介します。 講演前にはバンド演奏などのライブパフォーマンスが行われ、ラスベガスらしい華やかな雰囲気の中で開幕しました。 ※主な新機能についてはこちら ・ https://aws.amazon.com/jp/blogs/news/top-announcements-of-aws-reinvent-2024/ ・ https://www.youtube.com/live/XhUemgp7LHo 2. ブレイクアウト セッション 約1時間の講義形式で進行される専門セッションです。興味のある技術分野を深掘りできます。 こちらは AWS 公式 YouTube チャンネル( AWS Events)に アーカイブ として公開されているためご興味のある方はご覧ください。 AWS Events公式チャネル 3. ワークショップ 2時間程度のハンズオン型セッションです。各自のPCで新機能やサービスを実際に触りながら学びます。 短時間で実践的に習得できるため、普段使わない技術に初めて挑戦する絶好の機会でした。 参加したワークショップの内容を1つピックアップして別の記事でご紹介します。 4. Certification Lounge AWS 認定資格 保有 者向けの専用ラウンジです。 無料の飲食や休憩スペースを利用できるので、会場を走り回る間の良いリフレッシュになりました。 ※右の方は他社の方で掲載の許可は得ています。 5. 5Kマ ラソン 朝早起きして、皆で5キロ走るというシンプルなコンテンツです。 朝食にスムー ジー や果物などの軽食が用意されており、充実した1日をスタートすることができました。 6. ネットワーキング(懇親会) 近畿日本ツーリスト ( AWS re:Inventのパッケージツアー主催会社)の前日懇親会 JapanNight(総勢200名以上が参加されていました) AWS パートナー限定ネットワーキング 普段そもそもリモートで多くの人になかなか出会えない中、多くの他社・他業種の方々との名刺交換、そこからの交流が生まれました。 特に、同世代との出会いは大きな刺激になり、今後のモチベーションにもなりました。 7. GameDay AWS サービスを組み合わせて技術的課題をチームでクリアしつつ、スコアを競うゲーム化された学習イベントです。 参加者同士が少人数のチームを組み、模索しながら実際の AWS サービスを用いてコミュニケーションを取りながら行うのが特徴です。 合計3つに参加し、現地で会った日本人の方や、外国の方に混ざってチームを組んで参加しました。 GameDayとは? 公式引用 3〜4人のチームで与えられた課題をクリアし、スコアを競い合う 手探りで AWS リソースを使いこなしながら、実践的なスキルを身につけられる トラブルを解決するスピード感やチームメンバーとのコミュニケーションが重要になる 参加したGameDay2つ抜粋 実際に参加して感じたこと等 アプリケーションの監視・処理エラー対応、既存 インスタンス タイプからGravitonを使用したタイプへの移行、 アーキテクチャ 見直し等と、多種多様な課題が様々でてきました。 それぞれ得意分野が違うケースもあるため、担当を決めてスタート。後半になると協力しつつ取り組む等チームワークも必要な部分が楽しかったです。 今回現地のGameDayでは Amazon Qという生成AI機能が使用でき、質問するとヒントやエラーの解決手順を、コンソール上のリソースのパラメータを踏まえて提案してくれました。実践的な課題をこなしながら、最新機能にも触れられたのが面白かったです。 チームでスコアを競うため、ゲーム感覚で盛り上がります。学びつつ楽しめるのは魅力でした。 結果は?? 日本人チームとしては、全体で40位台でした。中盤までいいペースだったのですが、後半にスコアが伸び悩み… とはいえ、学びが多くあり非常に充実した時間でした。 外国の方々ともチームを組ませていただいた際は約70チーム中19位とまずまずの結果を残すことができました。 私が1つの項目ををクリアしてcomplete!nice!!ぐらいの会話をしたことを覚えています(笑) AWS GameDayの魅力や学べること スキルの向上 一般的な認定試験や座学では得づらい AWS 上のリアルなトラブルに短時間で向き合うことができます。 自分で考える、時にはチームで協力しながら対応策を練ることで、問題の切り分けや クラウド における実践力が伸びると思います。 GameDayでは横で実際に課題に対処している姿を目にしつつ、時には助言をいただくことができ、特に若手にとっては目で見て学べるいい機会でもあると感じました。 モチベーション 自由度が高い分、なかなか課題が解決できず打ちひしがれる場面もあります。 それでも課題を解決する道筋を考え実際に点数が取れた時の達成感があり、今後もっと経験を積み頑張ろうと前向きになりました。 交流 re:Inventのような大型イベントでは、様々な参加者が集まるため、普段接点がない海外の方や他社のエンジニアと交流するきっかけが生まれます。 技術者同士の情報共有として「こんなトラブルがあった」というリアルな声を聞き合えるのも、GameDayの大きなメリットだと思います。 GameDayまとめ 一言で言えば、 AWS のゲーム化された実践ト レーニン グ です。 認定資格や座学の知識を超えて、実際の クラウド の課題を短時間で体験しながらスキルを養えます。 AWS re:Invent のGameDay に加えて、 AWS Summit等でも開催されることがあるので、特に若手エンジニアの皆さんはぜひ一緒に参加しましょう。 短い時間でも多くの学びと交流、そして新たなモチベーションが得られるはずです。 AWS に注力されている企業さんだと、会社内でチームを組んで出場するというケースもよくあるみたいです。 終わりに AWS re:Invent は、世界規模で AWS の最新情報や技術トレンドを発信する超大規模イベントです。現地に行くと想像を超えた情報量と熱気が待っていました。 新サービスや新機能の発表のみならず、GameDay をはじめとした実践的な学習イベントでの スキルアップ の場、5キロランやre:Playという最後の打ち上げパーティの開催等、一度に様々な体験ができる貴重な場でした。 今後 AWS をはじめとした クラウド 関連の技術を今以上に深めていきたい!といった大きなモチベーションになりました。 個人的には普段の業務での活用はもちろんですが、 Amazon Bedrockや Amazon Qの今後の活用が気になっており、自身で触れたり動向を追ったりしていければなと思っています。 もし AWS に興味を持たれた方は、まずは AWS 認定資格の取得や、毎年日本でも開催される AWS SummitやGameDayへの参加を検討してみてはいかがでしょうか。 AWS Summit2024 私たちは一緒に働いてくれる仲間を募集しています! 株式会社 電通総研 新卒採用サイト 執筆: @miyazaki.hirotoshi 、レビュー: @miyazawa.hibiki ( Shodo で執筆されました )
初めまして。 エンタープライズ 第一本部の1年目の新人、佐藤悠です。 本記事ではやりたいことがない新人やIT企業を志望する学生に向けた AWS 学習のすすめと資格取得の勉強法について紹介する。 目次 なぜAWSを勉強するのか AWSとは 個人レベルでの使用感 Cloud Practionerの取得方法 AWS Cloud Practitionerとは 受験の理由 著者スペック 学習方法 学習教材 テスト当日 まとめ なぜ AWS を勉強するのか ITサービスにはインフラが必ず存在し、インフラの需要はそれに伴って存在する。近年は クラウド 化の潮流もあり クラウド 技術を学ぶこと自体がインフラを理解することに近似してきている。 AWS は クラウド の市場シェアがもっとも高いためニーズある人材になるには AWS を理解することが最短である。やりたいことのない人は来たるときにそれを実現できる力をつけられるという意味でも AWS をやると良いと私は考える。 AWS とは Amazon Web Services の略。 クラウド サービスを提供しておりそのサービスは200種類を超え、多種多様なニーズにこたえることができる。 個人レベルでの使用感 発信する力もエンジニアの力といわれる昨今、私もブログを作成したいと思い、サーバーの立ち上げを試みたが一向にできない。しかし、すでにあるプラットフォームを利用するのはプライドが許さない。そんな時に AWS Lightsailである。様々なテンプレートの中から GUI の操作で好きなものを選び、ブログサーバーが5分でできた。最高である。 このように業務から個人的なものまで大小問わず、さまざまな環境をサクッと作れるのが実感できる良さである。 Cloud Practionerの取得方法 AWS Cloud Practitionerとは AWS の認定資格の1つ。入門資格に位置づけられるが AWS クラウド の基本やサービス、用語を網羅した全体観を知るにはピッタリの資格である。 受験の理由 冒頭に述べた通り。インフラの知識に実務経験があれば飯を食うのに困らなさそうだから。 著者スペック 学部は非情報系 大学院は情報系であるがAIが研究対象なのでインフラの知識はない 新人でインフラの業務は一切していない(取得当時) 基本情報は3か月かけて取得した 記憶力は弱い。インフラのイの字も知らなかった。 学習方法 試験概要 内容 AWS サービスの名称と内容 ベストプ ラク ティスの概念 出題形式 選択肢から1つ正解を選ぶ形式が多数 複数選択が一定数出題される 時間 90分 合格点 7割 一般的には取得のために50時間とするサイトが多いがいらない。 なぜならば選択式だから。 複数選択式は全体割合のうち少数なので、単一選択の4~5択のうち一つを選ぶなら20~25%の確率で正答できる。つまり確実に分かるレベルの知識は過去問に対して半分あればいいのだ。あと合格するかは 正規分布 に従うだろう。 実際に1日1時間を2週間の継続で想定される時間に対し、半分と少しの28時間で合格できた。 学習教材 Udemyで以下の問題集を解いた。 【CLF-C02版】この問題だけで合格可能! AWS 認定 クラウド プ ラク ティショナー 模擬試験問題集(6回分390問) https://www.udemy.com/course/aws-4260 先述の通りこの問題を1周した後、これは確実に正解できると思える問題を50%に引き上げるだけ。2周で十分。 テスト当日 自宅で受験したがこれはおすすめしない。 不正防止のための部屋を整備するのに時間を要した。 ポスターをはがしたり、手の届く範囲にものが存在しない状態にしたりと大変である。 また、受験中に画角から外れることもできないので集中できなかった。 このような手間がかかるので、受験に集中するためにはテストセンターで受験した方がいい。 まとめ CloudPractitionerの受験にあたり効率を重視した手法を紹介したが、これはあくまで全体感をつかむためであり、ここで得た点的知識を 有機 的に紐づけるのは興味を持った分野を自分で触ってみることであったり、資格ベースで進めたい人は次のレベルの取得で検討できたりする。 何よりもやってみることが大事である。試験設計自体がそう言っていると思う。 次回はSolutions Architect Associate取得の勉強方法の記事を書く。 ご覧いただきありがとうございました。 私たちは一緒に働いてくれる仲間を募集しています! 電通総研 新卒採用サイト 執筆: @sato.yu 、レビュー: @nakamura.toshihiro ( Shodo で執筆されました )
祝3周年 こんにちは。 X(クロス) イノベーション 本部プロダクト イノベーション センターの中村です。 本テックブログは12月をもって開設3周年を迎えました! 私たちは、この3年間で毎週1本以上の記事を継続的に公開してきました。総数は390本を超え、単純計算で1週あたり2、3本の記事を公開したことになります。手前味噌ではありますが、3年間継続できていることを誇らしく思います。 この記事では、テックブログを継続するために「編集部」が取り組んでいる活動の一端を飾らずに紹介したいと思います。 本記事は、 電通総研 Advent Calendar 2024 の25日目の記事となります。 編集部とは? テックブログ継続のための工夫 レビュープロセス トレンド入りやランキング入りの称賛 アドベントカレンダーの企画 まとめ 編集部とは? 最初に「編集部」について説明します。テックブログの運営には旗振り役の組織が必須です。私たちはその組織を「編集部」と呼び、部門横断で参画希望者を募り現在14人で運営しています。 編集部の活動には、例えば次のようなものがあります。 ブログサービス利用料など各種運営に必要な予算の管理 採用や広報との連携 執筆者へのフィードバックとサポート 編集部の人員入れ替えを意図的に実施することで多くの方に編集部に関わってもらう工夫もしています。これにより、新しい視点やア イデア を取り入れ、継続的な改善の促進を狙っています。 テックブログ継続のための工夫 以下、テックブログを継続するために実施している3つの工夫を紹介します。 レビュープロセス 記事のレビューは編集部と広報担当部署が中心となって実施します。私たちはレビューに Shodo というツールを活用しレビュー効率を高めています。Shodoは誤字脱字や表記ゆれなどを自動検出してくれるため、レビュアーは本質的な部分に集中してレビューが可能です。基本的には執筆者の意思を尊重したいので、レビューアーは、「非開示情報が含まれていないことの確認」や「より多くの人に読まれるための助言」といったフィードバックを中心に行います。 また、あらかじめ「質より量」や「等身大の自分たちを表現する」といったポリシーを定めているため、記事の品質や技術的な優劣を問題にしてレビューが紛糾することはありません。 ツールの活用と事前に定めたポリシーの存在がレビューの負荷低減に繋がっています。レビューでレビューアーとレビューイーの双方が消耗しないことは重要なポイントだと考えています。 トレンド入りやランキング入りの称賛 執筆記事が はてなの企業技術ブログのトレンド や何らかのランキングに掲載された際は、執筆者コミュニティ内で共有と称賛を行っています。トレンド入りを目標にしているわけではないのですが、多くの方に注目されたということの証左の一つだと思いますので、素直に「素晴らしい!」という想いを皆で共有しています。 ささやかなものではありますが、称賛を表明する機会や場は、モチベーションの向上にポジティブな効果があると感じます。 アドベントカレンダー の企画 12月は毎回 アドベントカレンダー (25日まで毎日記事を公開する企画)を実施しています。今回を含めてこれまで4回実施していますが、いずれも執筆者募集から数日で応募枠が埋まるほどの人気ぶりです。 アドベントカレンダー は編集部からすると少し負荷が掛かる取り組みではありますが、年に1度くらいであれば日常のスパイスとなって皆で楽しめますし、テックブログ参加者の活気を維持するのに役立ちます。 多くの アドベントカレンダー では、12月1日から25日まで休みなく継続することが多いと思いますが、私たちは土日祝日を休むという選択をしています。休みの日に無理して執筆する・公開するほどまでに頑張らなくてもいいよね、というスタンスです。しっかり休むことで執筆者と編集部の負担を軽くしています。 多くの人が無理なく一緒に盛り上がれるようなイベントを定期的に行うことは、テックブログのような活動を続ける上で大切だと考えています。次のイベントに向かってまた頑張ろうという活力が生まれやすくなるからです。 アドベントカレンダー は来年も継続できればと考えています。 まとめ テックブログを継続するためには、いくつかの重要なポイントがあります。 まず、無理をしないこと。私たちは、執筆者が無理なく記事を書ける環境やポリシーを整えることを大切にしています。また、執筆者だけでなく編集部も無理なく運営に携われることを常に意識しています。 次に、執筆者へのフィードバックや称賛も重要です。公開前に適度なレビューを行うことで「この情報を公開して良いのだろうか?」といった不安は除去されますし、称賛により努力が認められればモチベーションの向上に繋がります。 さらに、 アドベントカレンダー のようなイベントを楽しむことも大事にしています。ちょっとしたことではありますが、皆でワイワイと記事が途切れないように協力するのは面白いだけでなく一体感を生む良い機会にもなります。 私たちは今後も週に1本以上の記事の公開を目標に活動を続けていきます。引き続き、本テックブログをよろしくお願いします! 私たちは一緒に働いてくれる仲間を募集しています! 電通総研 キャリア採用サイト 電通総研 新卒採用サイト 執筆: @nakamura.toshihiro ( Shodo で執筆されました )
皆さんMerry Christmas!コーポレート本部 サイバーセキュリティ推進部 セキュアシステムデザイングループの福山です。 本記事は、 電通総研 Advent Calendar 2024 の24日目の記事となります。 今回は、私の好きな AWS サービスの一つである Amazon Detectiveについてお届けしたいと思います。 はじめに Amazon Detectiveとは Detectiveを有効化してみよう! 管理アカウントでの作業 メンバーアカウントでの作業 GuardDutyアラートの生成 Detective管理アカウントによる調査方法 検出結果グループ(finding groups) 調査機能(Detective Investigation) IAMユーザーを調査 ロールを調査 検索機能(search) アカウントIDで検索 インスタンスIDで検索 インスタンスに紐づくロールで検索 IPアドレスで検索 IAMユーザー名で検索 まとめ 最後にちょっとだけ補足 はじめに AWS 上で発生した セキュリティインシデント を調査する際、GuardDuty の検出結果、CloudTrailや VPC フローログ等を読み解く作業が発生します。 しかし、これらのログ調査には知見と労力が必要です。 そんな課題を解決し得るサービスが AWS には存在します。 Amazon Detectiveとは Amazon Detectiveは、 AWS 上で発生した セキュリティインシデント の調査を容易にするサービスです。 CloudTrail/ VPC フローログ/GuardDutyの検出結果などのログデータを自動で収集し、"動作グラフ"と呼ばれる各アカウントに紐づくデー タセット を生成し、 機械学習 ・統計分析を用いて可視化します。 下図は セキュリティインシデント の調査のプロセスを示しています。 Detectiveを使わない従来の調査方法では、それぞれのプロセスで利用するサービスが異なるため、時間と手間が発生していました。 Detectiveを使うことで、各サービスのログがDetectiveに集約・分析され、 セキュリティインシデント の調査を 一気通貫 で進めることができます。 引用: https://www.youtube.com/watch?v=Vf-s3ZQmJhc Detectiveを有効化してみよう! と言いたいところですが、その前に重要なポイントがあります。 それは、Detectiveでは マルチアカウント連携が可能 であるということです。 例えば、複数の AWS アカウントを抱える組織のSOCやCSIRTが、Detective管理アカウント(ログの集約アカウント)を使って、メンバーアカウント(ログを吐くアカウント)で起きた セキュリティインシデント を調査する、といった構成を組むことができます。 詳細は下記のユーザーガイドを確認してください。 https://docs.aws.amazon.com/ja_jp/detective/latest/userguide/accounts.html 以降の説明では、マルチアカウント連携を行う前提で進めます。 管理アカウントでの作業 まずはDetective管理アカウントとなるアカウントでDetectiveを有効化しましょう。 必要に応じてオプションデータソースを有効化します。 " AWS セキュリティ検出結果"を有効にすると、メンバーアカウントのSecurity Hubの検出結果を収集することが可能です。 メンバーアカウントでEKSを利用している場合はEKS監査ログも収集できますが、ログのデータ量によってはメンバーアカウント側に想定外の課金が生じるため注意してください。 30日間の無料期間がありますので、その間に費用感をチェックすることをお勧めします。 各アカウントを招待しましょう。DetectiveはOrganizations連携にも対応しています。 組織アカウントを Detective メンバーアカウントとして有効にする 個々のアカウントを動作グラフに招待する メンバーアカウントでの作業 メンバーアカウント側で設定すべき内容は以下です。 GuardDutyの有効化(必須) Detectiveに取り込まれるためには、GuardDuty有効化後、48時間が経過している必要があります。 GuardDutyの検出結果のエクスポートオプション で更新結果の取り込み頻度を15分に変更する(推奨) デフォルトは6時間になっており、GuardDutyの更新結果がDetectiveに反映されるまでに時間がかかります。 15分に設定することをお勧めします。 招待を承諾 (マルチアカウント連携の場合は必須) 招待を承諾してから、管理アカウントが情報を取り込むまでに最大24時間を要します。 メンバーアカウント側で事前にDetectiveを有効化してしまうと、メンバーアカウントの動作グラフが管理アカウント内およびメンバーアカウント自身に存在するため、それぞれに課金が発生します。 メンバーアカウント側でDetectiveを使って調査する必要がなければ、メンバーアカウントでDetectiveを明示的に有効化する必要はありません。 アカウント連携前のログはDetectiveに取り込まれません。 Security Hubの有効化(必要に応じて) Inspectorの有効化(必要に応じて) GuardDutyアラートの生成 続いて AWS 上にリソースを立てて、擬似攻撃を起こしてGuardDutyアラートを生成しましょう。 今回は、コマンド1つでサンプルではない実際のGuardDutyアラートを生成できるツール"GuardDuty Findings Tester"を用います。 https://github.com/awslabs/amazon-guardduty-tester GuardDuty Findings Testerでは、攻撃元となるリソース(Kali Linux )と、攻撃対象となるリソースをCDKでデプロイします。 アラート生成方法としては、FindingType指定をはじめ、リソース種類別、ログソース別、MITRE ATT&CKの Tactics (攻撃戦術)別などがあります。 今回は下記のFindingTypeを指定して検出させてみました。 UnauthorizedAccess:EC2/RDPBruteForce UnauthorizedAccess:EC2/SSHBruteForce なお、以下の点は予め留意した上で、デプロイ先のアカウント、GuardDutyの通知先を事前に考慮することをお勧めします。 本環境はTorノードにアクセスする仕様になっているため、CDKをデプロイした後にUnauthorizedAccess:EC2/TorClientが勝手に検出される FindingTypeを指定して検出させても、複数のアラートが発生する場合がある Detective管理アカウントによる調査方法 お待たせしました。Detectiveを使って調査を始めましょう! Detectiveで利用できる機能は大きく分けて3つあります。 検出結果グループ(finding groups) 関連性のありそうな検出結果を検出結果グループとしてまとめてくれる機能です。 検出結果グループの概要を要約してくれたり、各エンティティ間の関連性を可視化してくれます。 注意点として、GuardDutyアラートが生成されて検出結果グループが作成されるのに最大48時間を要するということ、 利用できるリージョン に限りがあります。 検出結果グループの一覧 検出結果グループの概要を要約 生成AIによって個々の検出結果グループを要約する機能で、以下のような内容を確認できる i-0dc6330b23830eb27は、ポートスキャン、他のIPに対する SSH /RDP ブルートフォース攻撃 、Torとの通信を実行した。 また、悪意のある IPアドレス から API コールを行った。 i-010e2e28e9486f520はポートスキャンと SSH ブルートフォース アタックを実行した。 この インスタンス では悪意のあるファイルも検出された。 172.16.0.169は、i-0dc6330b23830eb27とi-010e2e28e9486f520からのポートスキャンと SSH ブルートフォース アタックの標的となった。 関連する以下の検出結果を一覧で確認できる GuardDuty Security Hub Inspector(ネットワーク到達可能性とソフトウェアの 脆弱性 ) 各エンティティ間の関連性を可視化(Finding group visualization) 攻撃の起点や関連しているリソースを図から読み取れる ブルートフォース 系のアラートはインバウンド/アウトバウンドで色分けされる Finding group visualizationから特定のエンティティを選択してドリルダウン的に検索することが可能(→ インスタンスIDで検索 に遷移) 調査機能(Detective Investigation) IAMユーザーまたはロールに関連する IoC (Indicator of Compromise; 侵害の証跡)を調査し、レポートを作成します。 対象のIAMユーザーまたはロールが セキュリティインシデント に関与しているかの判断に役立つ機能です。 リソース(IAMユーザーまたはロール)を選択し、調査を実行するとレポートが生成され、影響度に基づいて調査結果の重要度が割り当てられる IAMユーザーを調査 MITRE ATT&CKのTTP(攻撃手順)によって マッピング され、重要度が判定された例 ロールを調査 重要度が高いアクティビティがある IPが表示され、関連するGuardDuty検出結果が表示された例 IPアドレス でドリルダウンしたい場合は IPアドレス のリンクをクリック(→ IPアドレスで検索 に遷移) 検索機能(search) アカウントレベル、またはアカウント内の各エンティティレベルで分析できる機能です。 いくつか例をあげてみました。 アカウントIDで検索 GuardDuty、Security Hub、Inspectorの検出結果を一覧で確認できる(各エンティティレベルでも確認可能) 新しい動作では、新たに観測された位置情報を確認できる(各エンティティレベルでも確認可能) 時間範囲内に観測された位置情報はオレンジの丸で表示される 位置情報を一覧でも表示でき、全体の API 呼び出しに占める割合なども確認できる インスタンス IDで検索 対象 インスタンス の詳細(作成者や作成時刻など)を確認できる ロールをドリルダウンしたい場合はロール名のリンクをクリック(→ インスタンスに紐づくロールで検索 に遷移) インスタンス に紐づく IPアドレス を確認できる VPC フローログに基づいて観測されたリモートの IPアドレス を一覧で確認できる リモートの IPアドレス の通信の方向、allow/denyについてフィルターできる インスタンス に紐づくロールで検索 検出結果グループや調査レポートとの紐付けがあれば確認できる 調査(Detective Investigation)が未実施であればその場で実施できる(→ ロールを調査 に遷移) IPアドレス 別 -> サービス別の API の呼び出し結果を一覧で確認できる IPアドレス で検索 IPアドレス の詳細を表示。逆引きが出力されたりはしない(3.x.x.xなので AWS という想像はつきますが)。 リソースとのインタ ラク ションを確認できる IAMユーザー名で検索 検出結果グループや調査レポートとの紐付けがあれば確認できる 調査(Detective Investigation)が未実施であればその場で実施できる(→ IAMユーザーを調査 に遷移) 対象のIAMユーザーによって呼び出されたサービス別の API の結果を一覧で確認できる(キャプチャは割愛) まとめ Detectiveを活用することで、 セキュリティインシデント に関連したリソースが可視化され、調査が容易になります。 以下のポイントを押さえ、効果的な活用を目指しましょう。 Detective管理アカウント/メンバーアカウントにて事前に必要な設定を実施する。 Detectiveの各種機能を活用 検出結果グループ: 複数のセキュリティイベントの関連性を俯瞰的に把握する。 調査機能: IAMユーザーやリソース単位での分析を行い、侵害の痕跡( IoC )を特定し、 セキュリティインシデント への関与を判断する。 検索機能: エンティティレベルで詳細に分析できるようになっており、問題の根本原因を特定する。 コストへの配慮 Detectiveの利用においては、30日間の無料期間中にデータ量や費用感を確認する。 メンバーアカウント側で予め明示的にDetectiveを有効化しないこと。 オプションデータソースであるEKS監査ログは必要なければ有効化しないこと。 最後にちょっとだけ補足 Detectiveの使い方としては、検出結果グループで概要を掴み、その後は調査機能でインシデントの有無を判断したり、検索機能で詳細調査する流れが理想的かと考えています。 ただし、検出結果グループが反映されるまでにGuardDutyアラート生成後、最大48時間を要するという制約があり、検出結果グループを見たい時にまだ見られないということが起こり得ます。 上記について改善要望を挙げていますが、より早く検出結果グループが生成されるように改修されることを期待したいと思います。 また、Detectiveがその効果を最も発揮するのは、 Amazon Detectiveとは で図示されている通り、Inspector、Security Hub、GuardDutyの検出結果から、それぞれの関連性を読み解く必要性が生じた時だと思います。 例えば、利用している OSS にSSRFの 脆弱性 があり、 インスタンス IMDS v2が適用されておらず、GuardDutyから"UnauthorizedAccess:IAMUser/InstanceCredentialExfiltration.OutsideAWS"が検出された。みたいなケースです。 参考: https://aws.amazon.com/jp/blogs/news/defense-in-depth-open-firewalls-reverse-proxies-ssrf-vulnerabilities-ec2-instance-metadata-service/ 今回利用したGuardDuty Findings Testerではそのようなシナリオを作ることが難しいため、あくまで機能紹介という形でお伝えしました。 私たちは一緒に働いてくれる仲間を募集しています! 電通総研 キャリア採用サイト 電通総研 新卒採用サイト 執筆: @fukuyama.kenta 、レビュー: @nagamatsu.yuji ( Shodo で執筆されました )
はじめに Iceberg とは? Iceberg Table for BigQuery Iceberg Table を作る 用意するもの テーブル作成 データを入れる Time Travelできないし、なんだかメタデータがめっちゃ少ない!!! 次へ はじめに 当記事を開いていただきありがとうございます。 電通 総研 エンジニアリングオフィスの徳山です。 この記事は、テーブルフォーマットの Iceberg についての初歩的な内容を説明します。 BigQuery で Iceberg 形式のデータを作成し、実体ファイルの中身をお見せすることで、Iceberg が一体どういうものなのか実感を持つきっかけになればと意図しています。 ただし、タイトルのとおり期待どおりにはいきませんでした。私の事前調査不足でございます。 Iceberg とは? Apache Iceberg は、分析向けに作られたフォーマットの1つで、テーブルフォーマットと呼ばれます。 データ分析基盤のデータレイクに格納するデータファイルのフォーマットに使用されます。 Iceberg などのテーブルフォーマットを使用して機能的に統合されたデータレイクはレイクハウスと呼ばれ、データウェアハウスやETLツールなどと組み合わせて相互補完的に使用されます。 データレイクへ格納されるデータファイルは、 CSV などのファイルフォーマットや Parquet などのデータフォーマットで格納されます。さらにそれらデータファイルを Iceberg などのテーブルフォーマットが梱包するようにフォーマットし、様々な機能が加わります。 Iceberg ( Apache Iceberg) を使用することで以下の機能がデータレイクで利用できるようになります。 Schema evolution : スキーマ の変更 Hidden partitioning : Icebergの内部で パーティション 値を生成・保持し、ユーザは意識せずに利用可能 Partition layout evolution : パーティション の改変を実現 Time travel : データのスナップショットを取得し、 スキーマ の変更の追跡や ロールバック などを実現 これらの機能はリレーショナルデータベースやデータウェアハウスなどではお馴染みのものに見えます。しかし、データレイクではデータはファイルで格納されるため、それらファイルをテーブルのように スキーマ 変更したり パーティション を組み替えたりできるようになることが画期的なものと理解しています。 Iceberg Table for BigQuery Google Cloud の BigQuery は、Iceberg Table というBigQuery用のテーブルフォーマットとして Iceberg をサポートしています。 テーブル作成時に、 Iceberg 形式を指定し、データの格納先として Google Cloud Storage (GCS) のフォルダパスを指定することで Iceberg Table のテーブルが作成されます。 これは External Table (外部テーブル) に似ていますが、BigQueryにおいては、 External Table ではありません。 External Table とは、ストレージ上のデータファイルをデータウェアハウスなどからテーブルとしてフォーマットする技術です。 クラウド サービスや Apache Spark 、Hive などで以前から存在し、データレイクに対して使用されてきました。 一般的に External Table はデータファイルを読み取ってテーブルのようにクエリすることはできますが、Iceberg のような機能はありません。 BigQuery には External Table と同類の機能で BigLake External table というテーブル形式があり、Iceberg 用の BigLake External Table が別途存在します。 Iceberg Table を作る 用意するもの Google Cloud Storage の バケット とフォルダ : Iceberg Tableのデータファイル格納先 BigQuery のデー タセット : BigQuery 側のテーブル BigQuery の外部接続 : BigQuery から Google Cloud Storage への接続で使用するサービスアカウント テーブル作成 以下のクエリでテーブルを作成します。 # BigQueryのプロジェクトIDやデータセット名、カラムなどの記述を省略しています。 CREATE TABLE IF NOT EXISTS <プロジェクトIDやデータセット名>.call_center_iceberg ( PRIMARY KEY (cc_call_center_sk) NOT ENFORCED, cc_call_center_sk STRING NOT NULL, cc_call_center_id STRING NOT NULL, < カラムの指定省略 > cc_tax_percentage DECIMAL(7, 2), processing_date DATE, ingestion_date DATE ) CLUSTER BY processing_date WITH CONNECTION <外部接続ID> OPTIONS ( file_format = 'PARQUET', table_format = 'ICEBERG', storage_uri = 'gs://<GCSバケット名>/raw-data/store-sales-iceberg') ; 作成されたテーブル (Iceberg Table) 通常のテーブル スキーマ のように見えます。通常のテーブルは "プレビュー" というタブが表示されますが、Icheberg Table の場合は表示されません。 テーブル作成時に指定のデータファイルの格納先フォルダ metadata というフォルダが作成されて、中に json ファイルが入っています。 データを入れる insert into を実行してデータを入れてみました。 テーブル (Iceberg Table) 側で見えるデータ テーブル形式のデータのように見えます。 テーブル作成時に指定のデータファイルの格納先フォルダ data というフォルダが作成されて、中に parquet フォーマットのファイルが入っており、これが BigQuery の画面へ表示のデータです。 Time Travelできないし、なんだか メタデータ がめっちゃ少ない!!! 次にデータや スキーマ を更新して Time Travel の機能を使ってスナップショットを見ていきたかったのですが、BigQuery の Iceberg Table では使えないことが分かりました。 そういえば、前述の metadata に格納された json に メタデータ がほぼ記録されていないことに気づきました。 記録されていた メタデータ (v0.metadata. json ) { "properties": { "bigquery-table-id": "xxxxxx.xxxxxx.call_center_iceberg" }, "current-snapshot-id": -1 } 本来の Iceberg では、データや スキーマ が更新されるとスナップショットが作成されて、この metadata. json へ記録されます。 BigQuery の Iceberg Table では更新しても metadata. json は特に変化がありませんでした。 次へ Iceberg Table on BigQuery はまだプレビュー版のため今後に期待したいと思います。 その間、 AWS や Databricks、 Snowflake 、DuckDBなどで Iceberg を使って深掘りしていきたいと思います。 ここまで読んでいただき本当にありがとうございました!!! どうぞ良いお年をお迎えください。 私たちは一緒に働いてくれる仲間を募集しています! 電通総研 キャリア採用サイト 電通総研 新卒採用サイト 執筆: @shikarashika 、レビュー: @nagamatsu.yuji ( Shodo で執筆されました )
こんにちは。 電通 総研の多田圭佑です。 本記事は 電通 総研 Advent Calendar 2024 の12月 20日 の記事です! 前日の記事は平岡恵里さんの「 Azure SOCで活躍している便利なサービスたち 」でした。 はじめに axumとActix Web axumの概要 Actix Webの概要 Hello, world! mainマクロ axumの場合 Actix Webの場合 ルーティング リクエストとレスポンス コンパイル後のバイナリについて まとめ はじめに みなさんはRustのWebフレームワークを使用したことはありますか? RustにはいくつかのWebフレームワークがありますが、中でもaxumとActix Webは機能が豊富で開発も活発なことから人気があります。 どのような違いがあるのか、今回は簡単に「Hello, world」を比較しながら違いを見ていきたいと思います。 使用した環境およびバージョンは以下です。 項目 情報 OS Windows 11 Rust 1.83.0 axum 0.7.9 Actix Web 4.9.0 axumとActix Web 冒頭で述べた通りRustのWebフレームワークはいくつかあります。 以下がよくまとめられていて参考になります。 https://github.com/flosse/rust-web-framework-comparison?tab=readme-ov-file#server-frameworks 掲題の2つのWebフレームワークについては GitHub から引用しつつ概要を記載します。 axum の概要 axum is a web application framework that focuses on ergonomics and modularity. 「axumは人間工学とモジュール性に重点を置いたWebアプリケーション フレームワーク である」 とでも訳せるでしょうか。axumの特徴は以下のとおりです。 Rustの非同期ランタイムである Tokio とHyper(HTTPライブラリ)で構成されている 設計の中心にRustの強力な型システムを据え、リク エス トやレスポンスの処理を安全かつ明確に行える Tower ミドルウェア を活用することで、柔軟で再利用可能な構成が可能となっている また、axumを人間工学やモジュール性の観点から見た特徴は以下のとおりです。 人間工学 シンプルな API と型安全性、Rustエコシステムとの統合により、直感的でミスを減らす設計がなされている モジュール性 Towerの利用や、ハンドラや ミドルウェア を独立した構成単位として扱える設計により、拡張性と再利用性の高い設計がなされている ちなみにdocs.rsはaxumで動いています。 https://docs.rs/ Cargo.toml あと余談ですがaxumのaは小文字が正式です(私はActix Webに引きずられてずっと大文字だと思ってました)。 Actix Web の概要 Actix Web is a powerful, pragmatic, and extremely fast web framework for Rust 「Actix WebはRustで開発された高性能で拡張性の高いWebフレームワーク」といったところでしょうか。 概要は以下のとおりです。 Actorモデルに基づいた非同期 フレームワーク であるActixがベースとなっている 高速かつ安全なHTTPサーバー構築が可能で、大規模なアプリケーションにも対応している 非同期処理のために Tokio ランタイムを使用している 全文検索 で有名なmeilisearchはActix Webを使用していますね。 https://www.meilisearch.com/ Cargo.toml Hello, world! さて、本題です。さっそくコードを見ていきましょう。 まずはaxumです。 use axum :: { extract :: Path, response :: IntoResponse, routing :: get, Router}; async fn greet ( Path (name): Path < String > ) -> impl IntoResponse { format! ( "Hello, {name}!" ) } #[tokio::main] async fn main () { let app = Router :: new (). route ( "/hello/:name" , get (greet)); let listener = tokio :: net :: TcpListener :: bind ( "127.0.0.1:3000" ) .await . unwrap (); axum :: serve (listener, app).await. unwrap (); } 続いてActix Webです。 use actix_web :: {get, web :: Path, App, HttpServer, Responder}; #[get( "/hello/{name}" )] async fn greet (name: Path < String > ) -> impl Responder { format! ( "Hello, {name}!" ) } #[actix_web::main] async fn main () -> std :: io :: Result < () > { HttpServer :: new ( || App :: new (). service (greet)) . bind (( "127.0.0.1" , 8080 )) ? . run () .await } いかがでしょうか。雰囲気はとても似ていますね。 両者の細かな違いとして以下をピックアップします。 mainマクロ ルーティング リク エス トとレスポンス mainマクロ axum: #[tokio::main] Actix Web: #[actix_web::main] となっています。 それぞれmain関数を非同期化するためのマクロを使用していますが、利用しているライブラリが違います。 マクロを展開した後のmain関数を見てみましょう。 cargo expand を使用すると展開されたコードを簡単に確認できます。 まずはaxumです。 fn main () { let body = async { let app = Router :: new (). route ( "/hello/:name" , get (greet)); let listener = tokio :: net :: TcpListener :: bind ( "127.0.0.1:3000" ).await. unwrap (); axum :: serve (listener, app).await. unwrap (); }; #[allow(clippy::expect_used, clippy::diverging_sub_expression)] { return tokio :: runtime :: Builder :: new_multi_thread () . enable_all () . build () . expect ( "Failed building the Runtime" ) . block_on (body); } } 続いてActix Webです。 fn main () -> std :: io :: Result < () > { < :: actix_web :: rt :: System > :: new () . block_on (async move { { HttpServer :: new ( || App :: new (). service (greet)) . bind (( "127.0.0.1" , 8080 )) ? . run () .await } }) } axumの #[tokio::main] は Tokio のmain関数を非同期化するマクロでaxum固有のものではありません。 そのため詳細は Tokio の公式ドキュメントなどを参照していただければと思いますが 今回のコードはサーバーを起動し、サーバーが停止するまでmain関数の終了をブロックしている、というイメージです。 Actix Web側も同様に block_on(...) がありますね。 実はこちらも裏で Tokio が使用されているため、コードのイメージはaxumとほぼ同じです。 Actix Webはactix-rtというActixエコシステムの非同期ランタイム(以降、Actixランタイム)を使用しています。 Actixランタイムはもともと Tokio を使用しておらず、独自の非同期ランタイムでした。 しかし、 Tokio がRustエコシステムの標準ランタイムとして確立されたことなどから、 Tokio ベースにリライトされました。 そのためActix Webは Tokio に対応しており、実は #[tokio::main] も使用できます。 ただしActixのActorの機能を使う場合はActixランタイムが必要なため、 #[actix_web::main] を使用する必要があります。 各サーバー処理( axum::serve と HttpServer::new... )の裏側はかなり異なるのですが、長くなってしまうため今回は軽くご紹介する程度にとどめます。 別の機会に当ブログでご紹介したいと思っていますが、興味のある方は調べてみてください。 axumの場合 axumのサーバーはシンプルな構成で Tokio ランタイムとHyperがリク エス トを非同期タスクとして処理します。 コードもかなりシンプルです。 https://github.com/tokio-rs/axum/blob/9983bc1da460becd3a0f08c513d610411e84dd43/axum/src/serve.rs#L224 Actix Webの場合 Actix Webのサーバーは起動時にワーカースレッド(OSスレッド)を用意します。 ワーカースレッドの数はデフォルトだと論理CPU分ですが、コードで数を設定することも可能です。 このワーカースレッドたちがリク エス トを非同期タスクとして処理する形になります。 Actixランタイムの場合と Tokio ランタイムの場合で内部的な動作が若干異なりますが、こちらも詳細に入ると長くなってしまうため別の機会にします。 コードは以下のあたりです。 Actixランタイムの場合 Tokioランタイムの場合 axumも new_multi_thread() でOSスレッドが起動しますが、管理は Tokio ランタイムにお任せでActix Webのように独自の管理はしていません。 ルーティング Actix Webはマクロベースのルーティングを提供していますが、実はaxumのようなルーティングも可能です。 axumと比較すると、axumの方が to(...) が無い分ほんのわずかにシンプルですが、ほぼ変わりませんね。 use actix_web :: { web :: Path, web :: get, App, HttpServer, Responder}; async fn greet (name: Path < String > ) -> impl Responder { format! ( "Hello {name}!" ) } #[actix_web::main] async fn main () -> std :: io :: Result < () > { // HttpServer::new(|| App::new().service(web::resource("/hello/{name}").to(greet))) // ↑も可能 HttpServer :: new ( || App :: new (). route ( "/hello/{name}" , get (). to (greet))) . bind (( "127.0.0.1" , 8080 )) ? . run () .await } リク エス トとレスポンス リク エス トはどちらも関数の引数として受けています。 axumは デストラクチャリング する形で受け取っています。 ( JavaScript のスプレッド構文と同じような感じです) パスパラメーターが増えた場合は以下のように拡張可能です。 async fn greet ( Path ((name, friend_name)): Path < ( String , String ) > ) -> impl IntoResponse { format! ( "Hello {name} and {friend_name}!" ) } タプル構造体である Path はメンバーを公開しているため( pub T の部分)、上記のようにデスト ラク チャリングできます。 // axumのPath pub struct Path < T > ( pub T); あるいは構造体を使用することも可能です。 #[derive(Deserialize)] pub struct PathInfo { pub name: String , pub friend_name: String , } async fn greet ( Path (p): Path < PathInfo > ) -> impl IntoResponse { format! ( "Hello {} and {}!" , p.name, p.friend_name) } 一方、Actix Webはaxumのようにデスト ラク チャリングする形で増やせません。 下記は コンパイル 時にエラーとなります。 // コンパイルできません #[get( "/hello/{name}/{friend_name}" )] async fn greet ( Path ((name, friend_name)): Path < ( String , String ) > ) -> impl Responder { format! ( "Hello {} and {}!" , name, friend_name) } これはActix Webの Path がメンバーをaxumのように公開していないためです。 // Actix WebのPath pub struct Path < T > (T); しかしできなくても全く問題ありません。構造体は問題なく使用できるからです。 // 構造体はaxumと同様 #[get( "/hello/{name}/{friend_name}" )] async fn greet (p: Path < PathInfo > ) -> impl Responder { format! ( "Hello {} and {}!" , p.name, p.friend_name) } p は Path<PathInfo> 型であるものの、Rustの 型推論 などにより自動的にPathInfo型への参照に変換されます。 そのため PathInfo のフィールドにアクセスできています。 レスポンスはほぼ同じですね。 レスポンスのトレイトである IntoResponse (axum)、 Responder (Actix Web)を実装すればOKのようです。 主要な型(今回のStringなど)はWebフレームワーク側でだいたい 実装済 です。 https://docs.rs/axum/latest/axum/response/trait.IntoResponse.html https://docs.rs/actix-web/latest/actix_web/trait.Responder.html コンパイル 後のバイナリについて ここまでコードの違いを見てきましたが、最後に コンパイル 後のバイナリについて触れます。 それぞれ コンパイル 後のバイナリサイズを比較したところ、Actix Webはaxumと比べて大きくなりました。 これはActix Webで使用しているライブラリがaxumに比べて多いためと推測されます。 ただ、Hello, worldで使用しているルーティングのマクロだけに機能を絞るとほぼ同じになりました。 Cargo.tomlで default-features を無効化し、 features を設定することで絞れます。 最適化なし 最適化あり axum 2476kb 486kb Actix Web 9417kb 1798kb Actix Web(絞った後) 3642kb 592kb 使用した最適化オプションは以下のとおりです。 [profile.release] strip = true opt-level = 's' lto = true codegen-units = 1 panic = "abort" まとめ 今回はaxumとActix WebのHello, worldを比較してみました。 雰囲気は似ているものの、細かな部分で違いがありました。 mainマクロで触れた各 フレームワーク のサーバー処理の詳細についてはまた別途ブログにまとめたいと思います。 コードは以下にまとめました。 https://github.com/keisuketada/axum-actixweb-hello ここまでお読みいただきありがとうございました。 私たちは一緒に働いてくれる仲間を募集しています! 電通総研 キャリア採用サイト 電通総研 新卒採用サイト 執筆: @tada.keisuke 、レビュー: @nagamatsu.yuji ( Shodo で執筆されました )
こんにちは。コーポレート本部サイバーセキュリティ推進部セキュアシステムデザイングループの平岡です。 弊社のAzure クラウド SOC(以下Azure SOC)では、各プロジェクトのAzure環境のセキュリティ設定の可視化と改善、および セキュリティインシデント の早期発見と調査支援を行っています。 私はAzure SOC運営メンバーとして主に Microsoft Defender for Cloudを活用し、各プロジェクトのAzure環境のセキュリティ設定の監視を行っています。 1年以上経ってしまいましたが、 前回書いたブログ ではAzure Lighthouseを利用して他テナント( サブスクリプション )の情報を権限委譲により集中管理する方法を紹介しました。 今回はAzure SOCで活躍している Microsoft Defender for Cloudのサービスや機能、 Microsoft Defender for Cloudと連携することで更なるセキュリティの向上を見込めるAzureサービスについてご紹介したいと思います。 なお、 AWS SOCサービス に関するブログは既に同じグループの耿さんがまとめているので、興味がある方はこちらも是非ご覧ください。 Azure SOCが提供しているサービス Azure SOCは社内のプロジェクトに対し主に以下のサービスを提供しています。 Azure環境のセキュリティ設定不備のチェック(主に Microsoft Defender for CloudのCSPM機能を利用) Azure環境での脅威検知(主に Microsoft Defender for CloudのCWP機能を利用) 使用しているAzureのサービスは構成図のとおりです。 Azure Lighthouse を用いてマルチテナント間の Micorosoft Defender for Cloud のセキュリティ情報を集中管理し、セキュリティ設定不備状況をグラフや表で一覧にして可視化するために Azure Workbook を利用しています。 また、 Microsoft Entra Privileged Identity Management (PIM) を活用し、プロジェクトの サブスクリプション に対する権限を最小限に抑えるようにアクセス制御を実施しています。 今回は主な構成要素である4つのAzureサービスを紹介したいと思います。 構成図 主な構成要素 1. Azure Lighthouse 2. Microsoft Defender for Cloud 3. Azure Workbooks 4. Microsoft Entra Privileged Identity Management (PIM) 1.Azure Lighthouse Azure Lighthouse は、リソース間でのスケーラビリティ、自動化の向上、ガバナンスの強化など、 マルチテナントの管理 を実現します。 各プロジェクトが使用している サブスクリプション から、Azure SOCの サブスクリプション へAzure Lighthouseを用いて権限の付与を行います。この権限付与により、各プロジェクトの サブスクリプション のセキュリティ設定やアラートを取得できます。ただし、Azure Lighthouseはポータルの画面操作だけではデプロイを完結することができず、委任元環境で ARMテンプレートを使ってデプロイ してもらう必要があります。 (Azure Lighthouseの活用方法や詳細なデプロイ方法を詳しく知りたい場合は、 前回のブログ をご確認ください。) 2. Microsoft Defender for Cloud Azure Lighthouseを使用することで、各プロジェクトの サブスクリプション の Microsoft Defender for Cloudの情報を集中管理できるようになりました。次は、Azure SOCでよく利用している Microsoft Defender for Cloudの機能について紹介します。 2-1.推奨事項 推奨事項画面では、デフォルトで無償のFoundational CSPMプランが有効化されているため、 クラウド 環境のセキュリティを強化するための改善点 や、 対応すべき事柄が一覧化 されています。 更に有償のDefender CSPMプランを有効化すると、表示されている Microsoft Defender for Cloudによってモニターされる組み込みのポリシー(以下、組み込みポリシー) のリスクレベルが表示されます。 Microsoft がリスクレベルを定めており、何から着手しようか迷った場合はリスクレベルに基づいて 重要な組み込みポリシーから対処 できます。 また、画面の上方に"Azure サブスクリプション 31"とありますが、これがAzure Ligthouseによって参照権限を与えられた サブスクリプション の数です。さらにその横には" AWS アカウント"、" GCP プロジェクト"とあるように Azure以外の クラウド のセキュリティ情報を集中管理 することもできます。 組み込みポリシーを選択すると 詳細情報 が表示され、具体的な説明や修復方法を確認できます。 組み込みポリシーの中には クイック修正機能 を利用できるものがあり、修正ボタンを押すだけでセキュアな設定に変更できます。クイック修正ができない組み込みポリシーもありますが、手動での修正手順も掲載されているので調査の手間を削減することができます。 似たような機能で 規制 コンプライアンス がありますが、この画面では推奨事項とは異なる観点で組み込みポリシーが整理されています。デフォルトの Microsoft cloud security benchmarkイニシアティブ (組み込みポリシーをまとめたグループのようなもの)には、ネットワークセキュリティ、ID管理など関連する組み込みポリシーが分類されてまとめられているため、どのような組み込みポリシーがあったか確認するのに便利です。 イニシアティブは自分で作成することもできるため、特に 注視したい組み込みポリシーをまとめたイニシアティブを作成 するのも良いと思います。 2-2.セキュリティ警告 セキュリティ警告画面では、 クラウド ワークロード保護(CWP)を有効 にすることで、 クラウド 環境内での脅威が検知 されるようになります。エージェントベースもしくはエージェントレスの監視が可能で、 クラウド 環境で稼働する 仮想マシン やコンテナイメージの 脆弱性 をスキャンしてくれます。表示されている各アラートの重要度は Microsoft が定めており、複数アラートが出てしまった場合は重要度が高いアラートから対処できます。 有効にするプランによりカバー範囲が異なる ため、環境設定画面でどのような機能を使いたいか事前に確認するといいと思います。 環境設定画面の上部に"すべてのプランを有効にします"というボタンがありますが、全てを有効化するとコストが多く掛かるため、 必要なプランをよく検討すること をおすすめします。 © 2024 Microsoft Corporation. Microsoft Microsoft Defender for Cloudの価格 の スクリーンショット (2024/12/19) 原典: https://azure.microsoft.com/ja-jp/pricing/details/defender-for-cloud/ 2-3.ワークフローの自動化 ワークフローの自動化では、上述の組み込みポリシーやアラートを見逃さないために、これらが検出されたときの対応を 自動化 することができます。 例えば、監視している サブスクリプション 内でアラートが検出された際に、 Logic Appと連携 しておくことで Slackや Microsoft Teamsなどにアラートの内容を即時に通知する ことができます。これにより、定期的に Microsoft Defender for Cloud画面を見なくても、ワークフローの自動化を設定することで イベントの見落としを減らし、効率的で迅速な対応 ができます。 また、ワークフローの自動化およびLogic Appの設定はノーコード・ローコードでできてしまうので、 プログラミング経験がなくても簡単に設定できる のも良いところです。 3.Azure Workbooks Microsoft Defender for Cloudの基本機能を上述しましたが、もっと Microsoft Defender for Cloudを使い倒したいという方はAzure Workbookを利用することをおすすめします。 Azure Workbookでは、 クラウド 環境のセキュリティ情報を分析しやすいように カスタマイズ して可視化することができます。 Microsoft Defender for CloudやAzure Monitorで収集したデータを グラフ、テーブル、チャート にして視覚的にわかりやすく表示できます。 とても便利な機能ではあるのですが、 KQL(Kusto Query Language)の知識が全くない状態だとやりたいことが出来ない可能性がある ので、KQLの勉強も兼ねてチャレンジしてみてください。 Kusto documentation: https://learn.microsoft.com/ja-jp/kusto/?view=azure-data-explorer 4. Microsoft Entra Privileged Identity Management (PIM) 最後に Microsoft Entra Privileged Identity Management (PIM)について紹介します。 PIMは、 Microsoft Entra ID の機能の一つで、 組織内の重要なリソースへのアクセスを管理、制御、監視 を行うことができます。 Azure SOCでは、プロジェクトの サブスクリプション から"セキュリティ閲覧者"や"セキュリティ管理者"のアクセス権限を付与してもらっているため、各 サブスクリプション を監視することができていますが、基本的に"セキュリティ管理者"の権限はワークフローの自動化を設定する時しか使用しません。 そのためPIMを利用して権限制御を行い、 "最低限必要な権限" を "最低限必要なユーザー" に "最低限の期間" 付与するようにしています。 Azure SOCのリソース(Logic AppやAzure Workbook等)を編集する場合なども同様です。 日常的にこれらのリソースを編集する必要はないので、"所有者"権限はもちろんのこと、"ロジックアプリの共同作成者"や"ブック共同作成者"等の権限も PIMを用いた承認制 で運用しています。 実際にPIMを使ってみると 権限周りの運用が楽 になり、権限解除を忘れてしまうこともなくなったので 心の負担も軽くなった と思います。ただし、こちらの PIMも利用にあたりライセンス契約が必要 となります。使用するユーザー全員分のEntra ID Premium P2 ライセンスが必要となり、現時点(2024年12月)では¥1,349 ユーザー/月の費用がかかります。 Microsoft Entra Privileged Identity Management とは: https://learn.microsoft.com/ja-jp/entra/id-governance/privileged-identity-management/pim-configure PIMを利用せずにダブルチェック等を行って運用できればお金もかかりませんが、定期的なアクセス権限の棚卸を忘れてしまったり、所有者権限などの強い権限の解除を忘れてしまった…といったリスクが高まります。 アクセス制御と心の平和のためにも、個人的には是非一度利用を検討していただきたい一押しサービスです。 終わりに 今回はAzure SOCで活躍している Microsoft Defender for Cloudのサービスや機能、 Microsoft Defender for Cloudと連携することで更なるセキュリティの向上を見込めるAzureサービスの一例をご紹介しました。 私たちは日々セキュリティに関する知見のキャッチアップを行っており、Azure SOCサービスもこれからどんどん改善していく予定です。 今後も検証してみて便利だと感じたAzureのサービスや機能について発信していきたいと思いますのでお楽しみに! 私たちは一緒に働いてくれる仲間を募集しています! 株式会社 電通総研 新卒採用サイト 執筆: @hiraoka.eri2 、レビュー: Ishizawa Kento (@kent) ( Shodo で執筆されました )
こんにちは、 電通 総研の金融ソリューション事業部の上野です。 普段の業務ではアーキテクトで案件に関わっており、現在は Java 、React、Next.jsを利用したソリューション開発に従事しています。 業務で利用する OSS のバージョンアップや技術選定はアーキテクトの重要な業務の一つであると考えており、今回は直近リリースされたReact19について調査した内容を一部記事にしました。 本記事は、 電通総研 Advent Calendar 2024 の18日目の記事となります。 はじめに Reactは、バージョンアップのたびに レンダリング 戦略や非同期処理周りの仕様が進化してきました。直近では、2022年3月に登場したReact 18では並行 レンダリング (Concurrent Features)やSuspenseの実用化による非同期処理の新しい書き方が可能となりました。さらに2024年12月に登場したReact 19では、useフックをはじめとしたより自然で宣言的な非同期パターンへと進化することが期待されています。 非同期処理は、ユーザエクス ペリエ ンス(UX)向上に直結する極めて重要な要素です。データ取得や状態の変化を ブロッキング することなく画面を表示し、スムーズな操作体験を提供するためには、React側が標準で非同期 レンダリング やデータフェッチをサポートしていく必要があります。またReact本体の GitHub Issueや議論でも、非同期処理やSuspenseをめぐる活発なやり取りが続いており、コミュニティとしてもこの分野への関心は非常に高まっています。 そこで本記事では、React 17から18、そして19へと進むにつれて非同期処理の書き方がどのように変わってきたのかを解説します。 (参考) RFC: First class support for promises and async/await [Suspense - React])( https://ja.react.dev/reference/react/Suspense ) use - React Reactの各バージョンの非同期処理 非同期処理記載方法のまとめ まず初めに、それぞれのReactバージョンにおける非同期処理を記載した際の課題についてまとめました。 〇は課題が解決された状態を表し、React19ではReact17の記法における課題はすべて解決しています。 課題 React17 React17(+useQuery) React18(+useQuery) React19 (1) hooksによる状態管理が複雑 × 〇 〇 〇 (2)ifの分岐が多くなる(宣言的ではない) × × 〇 〇 (3) コンポーネント の責務が多い × × 〇 〇 (4)外部 OSS に依存 〇 × × 〇 次章から、各バージョンにおけるサンプルコードを載せます。 React17以前の非同期処理(Reactプリミティブな方法) useEffect および useState をラップしたカスタムフックを作成し、非同期にフェッチを行う。 フェッチ後のステータスに応じて、 コンポーネント のだし分けをする。 import { useState, useEffect } from 'react' ; // いずれかのデータフェッチを非同期で行うカスタムフック function useFetchData ( url ) { const [ data , setData ] = useState( null ); const [ loading , setLoading ] = useState( true ); const [ error , setError ] = useState( null ); useEffect(() => { async function fetchData () { try { const response = await fetch (url); if (!response.ok) throw new Error ( 'Failed to fetch data' ); const result = await response.json(); setData(result); } catch (err) { setError(err); } finally { setLoading( false ); } } fetchData(); } , [ url ] ); return { data , loading , error } ; } // 非同期データのfetch後にレンダリングを行うコンポーネント function MyComponent () { const { data , loading , error } = useFetchData( 'https://api.example.com/data' ); if (loading) return < p > Loading... </ p > ; if (error) return < p > Error: { error.message } </ p > ; return ( < div > < p > { data. title } </ p > </ div > ); } export default MyComponent; このコードの課題としては、以下が挙げられます。 (1)hooksによる状態管理が複雑。 useStateによるdata,error,loadingの状態管理が必要。 useEffectによる レンダリング 後の非同期 API コールが必要。 (2)ifの分岐が多くなる(宣言的ではない)。 loading状態を判定する、ifによる分岐。 error状態を判定する、ifによる分岐。 (3) コンポーネント の責務が多い。 loading待ち、エラーハンドルは業務処理と直接関係がない。やりたいことは <p>data.title</p> を表示したいだけ。 React17以前の非同期処理( OSS の利用) カスタムhook部分を tasntack/react-query に任せることで、fetchに応じた結果をより短いコード量で受け取ることが可能。 フェッチ後のステータスに応じて、 コンポーネント のだし分けをする。 import { useQuery } from '@tanstack/react-query' ; // 非同期なデータfetch async function fetchData ( url ) { const response = await fetch (url); if (!response.ok) { throw new Error ( 'Failed to fetch data' ); } return response.json(); } // 非同期データのfetch後にレンダリングを行うコンポーネント // Promiseの扱いを`tanstack/react-query`に委譲 function MyComponent () { const url = 'https://api.example.com/data' ; const { data , isLoading , error } = useQuery( [ 'data' ] , () => fetchData(url)); if (isLoading) return < p > Loading... </ p > ; if (error) return < p > Error: { error.message } </ p > ; return ( < div > < p > { data. title } </ p > </ div > ); } export default MyComponent; このコードの課題としては、以下が挙げられます。 (2)ifの分岐が多くなる(宣言的ではない)。 loading状態を判定する、ifによる分岐。 error状態を判定する、ifによる分岐。 (3) コンポーネント の責務が多い。 loading待ち、エラーハンドルは業務処理と直接関係がない。やりたいことは <p>data.title</p> を表示したいだけ。 (4)外部 OSS に依存。 できればReactのプリミティブな機能だけで実現したい。 React18の非同期処理 React18で登場したSuspenseと tanstack/react-query が連携することで、Suspense状態を管理することが可能に。 fetch中、fetchの成功、fetchの失敗を分岐処理を設けずに宣言的に記載することが可能に。 import React, { Suspense } from 'react' ; import { useQuery } from '@tanstack/react-query' ; import { ErrorBoundary } from 'react-error-boundary' ; // 非同期なデータfetch async function fetchData ( url ) { const response = await fetch (url); if (!response.ok) { throw new Error ( 'Failed to fetch data' ); } return response.json(); } // 非同期データのfetch後にレンダリングを行うコンポーネント // Promiseの扱いを`tanstack/react-query`に委譲 function MyComponentContent () { const url = 'https://api.example.com/data' ; const { data } = useQuery( [ 'data' ] , () => fetchData(url), { suspense : true } ); return ( < div > < p > { data. title } </ p > </ div > ); } // エラー時に表示するフォールバックコンポーネント function ErrorFallback ( { error } ) { return < p > Error: { error.message } </ p > ; } // fetch中、fetch成功時、fetch失敗時にそれぞれ適切なコンポーネントを表示する export default function MyComponent () { return ( < ErrorBoundary FallbackComponent = { ErrorFallback } > < Suspense fallback = { < p > Loading... </ p > } > < MyComponentContent /> </ Suspense > </ ErrorBoundary > ); } このコードの課題としては、以下が挙げられます。 (4)外部 OSS に依存。 できればReactのプリミティブな機能だけで実現したい。 React19の非同期処理 React19で登場した use と cache の組み合わせにより、Reactプリミティブな API のみでSuspense状態を管理することが可能に。 import { use, Suspense, cache } from 'react' ; import { ErrorBoundary } from 'react-error-boundary' ; // 非同期なデータfetch const cachedFetchData = cache( async ( url ) => { const response = await fetch (url); if (!response.ok) { throw new Error ( 'Failed to fetch data' ); } return response.json(); } ); // 非同期データのfetch後にレンダリングを行うコンポーネント // Promiseの扱いをはReactプリミティブな`use`を利用 function MyComponentContent () { const url = 'https://api.example.com/data' ; const data = use(cachedFetchData(url)); return ( < div > < p > { data. title } </ p > </ div > ); } // エラー時に表示するフォールバックコンポーネント function ErrorFallback ( { error } ) { return < p > Error: { error.message } </ p > ; } // fetch中、fetch成功時、fetch失敗時にそれぞれ適切なコンポーネントを表示する export default function MyComponent () { return ( < ErrorBoundary FallbackComponent = { ErrorFallback } > < Suspense fallback = { < p > Loading... </ p > } > < MyComponentContent /> </ Suspense > </ ErrorBoundary > ); } 終わりに Reactに限らず、昨今のフロントエンド界隈は群雄割拠であり、目まぐるしい進化を遂げています。その中でも今回は個人的に注目している非同期処理に特化して解説してみました。 書ききれなかった内容については、参考ブログやリンクを記載しておきます。 use API に関する補足 最速攻略! ReactのuseRFC Suspenseに関する補足 Suspenseはどのような機能なのか 実際の案件ではNext.jsを利用することも多いため、Reactの非同期処理の進化でNext.js13以降どのような破壊的Updateが行われたのか、機会がありましたらまた筆をとろうと思います。最後まで読んでいただきありがとうございました。 私たちは一緒に働いてくれる仲間を募集しています! 新卒採用ページ クラウドアーキテクト 執筆: @kamino.shunichiro 、レビュー: @yamashita.tsuyoshi ( Shodo で執筆されました )
こんにちは。X イノベーション 本部 AI トランスフォーメーションセンター 所属の山田です。 本記事は 電通総研 Advent Calendar 2024 の 12 月 17 日の記事です。 はじめに 私達は普段、 Python を使ってアプリケーション開発をしています。 本記事では、チームでアプリケーション開発を進めていく中でテスタビリティを上げるために意識していることを紹介したいと思います。 なおコード例は Python ではありますが、採用言語に関わらず品質を高めたいと考えているエンジニアやリーダーにとって参考になる内容を目指しました。 テスタビリティとはなにか 「テスタビリティ」はプロダクションコードをどれだけ容易にテストできるかを指します。 テスタビリティが低いコードは ユニットテスト や統合テストの作成に時間がかかるだけでなく、コードのメンテナンス性が低下し デバッグ 、新機能の追加が難しくなる傾向にあります。 テスタビリティそのものを 定量 的な指標で測ることは難しいため、本記事ではテスタビリティを下げてしまう事例から、どのように改善するとテスタビリティを高められるかを紹介したいと思います。 テスタビリティが低くなる事例とその対応策 1. インスタンス 化が困難なクラス テスト対象クラスのコンスト ラク タを確認することで、そのクラスのテスタビリティについてはある程度把握できます。 テスト対象のクラスの インスタンス 化が困難な場合、そのクラスのテスタビリティは低い傾向にあります。 インスタンス 化を困難にする要因としては以下のことが考えられます。 多すぎる引数 これは ビジネスロジック を記述するサービスクラス側で問題となることが多いものです。 コンスト ラク タに渡すべき引数が多いと、テスト対象クラスを組み立てる際の設定が煩雑になります。 またテスト時に多くのパラメータを準備する必要があり、設定ミスも発生しやすくなります。 不適切な型の定義 これはデータを保持するデータクラス側で問題となることが多いものです。 特定の外部ライブラリの型( pandas.DataFrame 、 numpy.array など)を直接利用した設計は、テスタビリティを大きく下げる可能性があります。 対応策 クラス設計の見直し インスタンス 化が困難なクラスを解決するには、多くの場合はクラス設計を見直す必要があります。 コンスト ラク タで多くの引数を受けるクラスは大抵の場合、責務を持ちすぎています。クラスを分割できないかを考えてみましょう。 外部依存を隔離して適切な型を利用する 外部ライブラリの型に依存しないプリミティブな型や ドメイン 固有型のクラスによる設計をしましょう。 pandas.DataFrame 、 numpy.array などからデータを変換する必要がある場合は、定義した ドメイン 固有型のクラスにファクトリーメソッドを用意するなどのアプローチを取りましょう。 import numpy as np from dataclasses import dataclass @ dataclass class Vector : values: np.ndarray # 外部ライブラリ依存 @ dataclass class Vector : values: list [ float ] # Python標準のfloat型のリストを利用する 2. 関数・メソッド内に依存関係が含まれている 関数・メソッド内に依存関係が含まれているコードもテスタビリティを下げます。 例えば以下のコードを考えてみましょう。 from dataclasses import dataclass # データクラスとしてのUser型 @ dataclass class User : name: str email: str class UserRepository : def save (self, user: User) -> User: # ユーザーデータをデータベースに保存する処理(詳細は省略) # ... return user class UserService : def register_user (self, user: User) -> User: # メソッド内で直接リポジトリをインスタンス化 repository = UserRepository() saved_user = repository.save(user) return saved_user 関数・メソッド内で依存関係が生成されると、テスト時に差し替えることが困難になります。 テスティングライブラリ側で差し替え方法が用意されている場合もありますが、そのようなコードはトリッキーで可読性が低くなりがちです。 対応策 関数・メソッド内に依存関係が含まれている場合は、外部から依存を注入できるようにパラメータ化した設計にします。 いわゆる、 Dependency Injection: DI の考え方です。 上記のコード例はコンスト ラク タで UserRepository を注入するように変更することでテスタビリティを高めることができます。 from typing import Protocol # リポジトリのインターフェースを定義 class UserRepository (Protocol): def save (self, user: User) -> User: ... # リポジトリ実装 class DatabaseUserRepository : def save (self, user: User) -> User: # ユーザーデータをデータベースに保存する処理(詳細は省略) # ... return user # サービスクラス class UserService : def __init__ (self, repository: UserRepository) -> None : self.repository = repository def register_user (self, user: User) -> User: saved_user = repository.save(user) return saved_user これによってテストコードを書く際に UserRepository の実装を置き換えることも容易になります。 3. 返り値のない関数・メソッド 返り値のない関数・メソッドのテストは大抵、困難になります。 プロダクションコードにおいて、返り値のない関数・メソッドは副作用を伴う処理を実施している場合がほとんどだからです。 例えば、ファイルを クラウド ストレージなどにアップロード処理をするコードに返り値がない場合を考えてみましょう。 class FileUploader : def upload (self, file_content: str ) -> None : # 実際のアップロード処理 # 返り値はない ... この処理が完了したかを確認するためには、アップロード先の クラウド ストレージの状態を確認する操作が必要になり厄介です。 対応策 このような場合は関数・メソッドを返り値を持つ設計にし、動作結果をテストできるようにしましょう。 from dataclasses import dataclass @ dataclass class UploadResult : success: bool url: str | None class FileUploader : def upload (self, file_content: str ) -> UploadResult: try : # アップロード先のURL uploaded_url = f "https://..." return UploadResult(success= True , url=uploaded_url) except Exception as e: print (f "Failed to upload: {e}" ) return UploadResult(success= False , url= None ) 返り値があることによって、ファイルのアップロードの成否を検証しやすくなります。 また返り値があることで該当クラスの FileUploader だけでなく、 FileUploader クラスに依存している他のクラスでは返り値を変化させたモックを使うことで容易にテストを行うことができるようになります。 4. 時間に関する処理 時間に関する処理もテスタビリティを下げる要因となります。 これは時間に関する処理を記述する場合、冪等性のない処理になる可能性が高いからです。 例えば、ユーザーの誕生日の情報をもとに年齢を算出する場合を考えてみましょう。 from datetime import date from dataclasses import dataclass @ dataclass class User : name: str birth_date: date # 誕生日 def get_age (self) -> int : # 現在の日付を取得 today = date.today() return today.year - self.birth_date.year - ((today.month, today.day) < (self.birth_date.month, self.birth_date.day)) get_age メソッドは現在の日付を取得して、ユーザーの誕生日情報との差分から年齢を算出します。 現在の日付情報を取得する date.today() の結果は実行するタイミングで変化するので、このままでは冪等性がなくテストが難しくなります。 対応策 テスティングライブラリ側の機能で時間を固定する機能が提供されている場合もありますが、可能であれば時間情報をパラメーターとして受けるようにしましょう。 時間情報をパラメータとして受け入れるようにすることでメソッドの冪等性を担保できます。 @ dataclass class User : name: str birth_date: date # 誕生日 def get_age (self, today: date) -> int : # 引数で受け取った日付情報をもとに年齢を算出 return today.year - self.birth_date.year - ((today.month, today.day) < (self.birth_date.month, self.birth_date.day)) 引数から時間情報を渡す設計にすることで、安定したテストを行うことができます。 実際の 開発プロセス において価値を引き出すために ここまではプロダクションコードのテスタビリティを高めるために意識するポイントを紹介しました。 しかし実際の 開発プロセス では、ここまで紹介した個別の事例と対応策だけではテスタビリティの高いコードを整備し続けることは難しいでしょう。 ここからは、テスタビリティの高いコードを整備し続けるために、実際の 開発プロセス で重要な要素について紹介します。 テスト戦略の共有 そもそもなぜテストをするのかやテストは何を担保するものなのかを含めてチームでテストに関する全体的な方針を立てることが重要です。 テスト戦略への意識が希薄だと形式的にテストを追加するだけになり、本質的な品質向上には繋がりません。 テスト戦略の共有では、テストレベルに合わせたテストのスコープを定めるのが良いです。 出発点としてはテストピラミッドをベースにすると良いでしょう。 テストピラミッドは「E2E テスト」「インテグレーションテスト」「 ユニットテスト 」の 3 つのテストレベルの関係性についてピラミッド図で表したものです。 ピラミッド上位のテストレベルほどテストの実行にコストを要するため、 ユニットテスト のテストケースを重要であることを示します。 ただし戦略を考えるに当たって重要なのは、やみくもに ユニットテスト を増やすのではありません。 ユニットテスト 、インテグレーションテスト、E2E テストで何をカバーするのかを明確にし、どのようにしたら効果的なテストを実現できるかを示すことです。 またテストピラミッドは機能要件をテストするための概念であるため、非機能要件のテストについては直接的に考慮されていません。 非機能要件のテスト戦略については機能テストとは別で考える必要があります。 非機能要件のテストは個別の機能ではなくシステム全体に関わる特性を評価するものです。 そのためリソースや時間が限られる中では優先度を決めて、現実的に可能な範囲で実行することが重要になります。 パフォーマンステストなどの負荷テストではシステムが提供する ユースケース やビジネスフローに基づいて、特に重要なシナリオから始めることが重要です。 高度な専門知識が必要となるセキュリティテストは外部の専門チームに依頼することも有効なテスト戦略の 1 つです。 テストコードの書き方についての ガイドライン プロダクションコードの書き方についてはよく取り上げられますが、テストコードの書き方については意外と軽視される部分です。 しかしテストコードは開発において品質保証の基盤になるため、プロダクションコードと同様に整備すべきものです。 テストコードの書き方についてもチームに共有しておくことが重要です。 以下はテストコードの書き方についての ガイドライン の一例です。 構造化されたテストコード 「given」「when」「then」を意識してテストを記述をしましょう。 given: テストの前提条件となる準備部分 when: テスト対象の振る舞いの呼び出し then: 実施結果の検証 明瞭なテストケース名をつける テストケース名には日本語を使って何をテストしているかが一目でわかるようなテストケース名を付けましょう。 テストコードが書きにくいと思った場合のアクション テストコードが書きにくい場合、それはプロダクションコードに改善の余地があるサインです。テストが難しい箇所を見つけたらプロダクションコードを見直してテスタビリティを下げている要因を探しましょう。 以下はガイドに基づきながら書いた、先の例で取り上げたユーザーの誕生日から年齢を算出するメソッドのテストコード例です。 from datetime import date @ dataclass class User : name: str birth_date: date # 誕生日 def get_age (self, today: date) -> int : # 引数で受け取った日付情報をもとに年齢を算出 return today.year - self.birth_date.year - ((today.month, today.day) < (self.birth_date.month, self.birth_date.day)) # テストコード def test_user_ 誕生日の算出ができること() -> None : # given birth_date = date( 2000 , 4 , 4 ) user = User(name= "テストユーザー" , birth_date=birth_date) today = date( 2024 , 12 , 15 ) expected_age = 24 # when actual_age = user.get_age(today=today) # then assert expected_age == actual_age また ガイドライン を整備するだけでなく、テストコードの記述も含めてモブプロで取り組むのも良いアプローチです。 モブプロを活用してテストコードを記述することで、チーム全員のスキル向上や設計改善につなげることができます。 テストコードは単なる補助的な存在ではなく、システムの品質を支える基盤として、適切に整備することを目指しましょう。 テストケースの洗い出し テスタビリティが高くなったコードを最大限に活用するには、テストケースの設計が不可欠です。 テストケース設計の際はテスト技法を押さえておくことで網羅性を高めながら効果的にテストケースの設計を進めることができます。 以下は基本的なものですが、個人的には効果が大きいと感じているテスト技法です。 境界値分析 境界値分析は入力値や条件の境界に着目して動作を確認するテスト技法です。 主に入力値の上限/下限が決まっているフォームバリデーションのテスト項目の設計に有効です。 同値分割 同値分割は入力データや条件をグループ化し、代表的なケースをテストすることで効率的にケースをカバーするテスト技法です。 データのパターンが多い場合に代表値を使って効率的な検証が求められるテスト項目の設計に有効です。 テストマトリクス( デシジョンテーブル ) テストマトリクスは デシジョンテーブル とも呼ばれ、複数の入力値や条件の組み合わせを整理しテスト項目を設計するテスト技法です。 複数条件の組み合わせを網羅的に検証する際のテスト項目の設計に有効です。 ユーザーストーリーテスト(シナリオテスト) 実際にシステムを使うユーザーのフローを再現しシステムの動作を確認するテスト技法です。 E2E テストなどテストピラミッドの上位のテストレベルにおいて効果的なテスト項目を設計するにのに有効です。 これらのテスト技法を組み合わせて活用することで、テストレベルに応じたテストケースの設計をスムーズに進めることができます。 テスト カバレッジ の計測と指標の理解 テストの品質を評価するためにテスト カバレッジ の計測が用いられることが多いです。 テスト カバレッジ は、テストケースがテスト対象のプロダクションコード上でどの程度実行されたかを示す指標です。 しかし単に カバレッジ といっても計測方法にはいくつかの種類があり、それぞれ異なる視点からコードの網羅性を評価します。 ここでは一般的に使用される C0、C1、C2 と呼ばれる カバレッジ の計測方法について紹介します。 C0: ステートメント カバレッジ プログラム内の ステートメント (命令)が少なくとも 1 回実行されたかを計測します。 C1: ブランチ カバレッジ プログラム内の条件分岐の各分岐が少なくとも 1 回実行されたかを計測します。 C2: 条件 カバレッジ プログラム内の条件式内の個々の条件が True/False の両方を少なくとも 1 回満たしたかを計測します。 テスト カバレッジ を導入する際には、まずは C1 のブランチ カバレッジ の指標を採用することがおすすめします。 C1 のブランチ カバレッジ は多くのテスティングライブラリでサポートされている計測が簡単です。 また分岐漏れに起因するバグを発見しやすい指標であり、C0 よりも効果的で、C2 よりも現実的なコストで導入が可能です。 また カバレッジ を計測し運用するに当たっては、数値目標に依存しないことが重要です。 テスト カバレッジ の値を単なる数字目標として設定されると、見せかけの カバレッジ 向上のために C0 の指標を採用するなどのハックが行われてしまうことがあります。 重要なのは指標を理解した上で カバレッジ を計測し、 計測時点から カバレッジ を下げないように開発を進めることです。 計測開始から カバレッジ が下がる場合、新機能を追加する際にテストが不足していたり修正や リファクタリング によって既存のテストが削除されていることを意味するからです。 カバレッジ の不足部分から追加すべきテストケースの議論材料として活用しましょう。 まとめ 本記事では、テスタビリティを低くしてしまう事例をベースにテスタビリティを向上させるための方法を紹介しました。 そして実際の 開発プロセス において、それらの方法が価値を発揮するための要素を取り上げました。 チーム全体でこれらのポイントを意識することで、テスタビリティを意識した開発を進められるのではないでしょうか。 本記事で紹介した内容によって、より堅牢で信頼性の高い開発活動への一助となれば幸いです。 私たちは一緒に働いてくれる仲間を募集しています! 株式会社 電通総研 新卒採用サイト 電通総研グループ キャリア採用サイト:電通総研 執筆: @yamada.y ( Shodo で執筆されました )
はじめに こんにちは。金融ソリューション事業部エンジニアリングオフィスの加藤です。 仕事でGKEや AKS といったマネージド Kubernetes サービスを利用したインフラ構築を経験してきました。 この記事では、 Raspberry Pi 5で K8s クラスタ を構築しながら、マネージド K8s のありがたみについて学んでいきます。 ゴール 以下の作業を通して、 Kubernetes クラスタ のオンプレ環境における構築手順の全体的な流れと、設計判断のポイントについてざっくり理解する Raspberry Pi をセットアップする Kubernetes クラスタ を作る Prometheus, Grafanaを入れて クラスタ の監視環境をセットアップする Cloudflare tunnelを利用して、セキュアにインターネット経由からのアクセスをできるようにする 構成 Raspberry Pi 5は手元に3台あります。以下の戦略で構築します。 マスター 1, ノード 2 の IPv4 シングルスタックの Kubernetes クラスタ をkubeadmで構築する あとでマスターにもノードを追加できるように、高可用性の構成とする Host Name IP Address Description PC 192.168.3.x 作業用のPC master-1 192.168.3.81 マスター1: Raspberry pi に Ubuntu 24.04 LTS node-1 192.168.3.82 ノード1: Raspberry pi に Ubuntu 24.04 LTS node-2 192.168.3.83 ノード2: Raspberry pi に Ubuntu 24.04 LTS kubernetes -frontend 192.168.3.90 コン トロール プレーンの API サーバ負荷分散のための仮想IP (LoadBalancer Service用) 192.168.3.91-99 Service Type:Loadbalacerで使用するIPレンジ 構成イメージは以下のとおりです。 大まかな流れ 準備: Raspberry Pi を台数分セットアップ Raspberry Pi のセットアップ カーネル の設定 コンテナランタイムの設定 ツールのインストール クラスタの構築 HA構成のAPIServer クラスタ 作成 Nodeの追加 テスト用のPodをデプロイして確認 PrometheusとGrafanaのセットアップ(オプション) kube-prometheus-stack Helmチャートによる簡単なセットアップ インターネット経由でアクセスできるようにする 1. 準備 記事のボリュームの関係上、 Raspberry Pi そのものについての記述は少し端折ります。 Raspberry Pi Imager を利用して、 Ubuntu 24.04 LTSのイメージをSDカードに焼きます。 複数台ありますので、自宅環境の要件を満たすための汎用的な設定はuser-dataに書いた上で起動したりshell スクリプト にしたりすると、セットアップが捗ります。 user-dataのサンプル を一番下に書きましたので、一つの参考としてご参照ください。 環境 SDカードを入れて起動します。以下のようになりました。 # 各種情報確認 $ cat /etc/os-release PRETTY_NAME="Ubuntu 24.04 LTS" NAME="Ubuntu" VERSION_ID="24.04" VERSION="24.04 LTS (Noble Numbat)" VERSION_CODENAME=noble ID=ubuntu ID_LIKE=debian HOME_URL="https://www.ubuntu.com/" SUPPORT_URL="https://help.ubuntu.com/" BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/" PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy" UBUNTU_CODENAME=noble LOGO=ubuntu-logo $ uname -a Linux node-1 6.8.0-1004-raspi #4-Ubuntu SMP PREEMPT_DYNAMIC Sat Apr 20 02:29:55 UTC 2024 aarch64 aarch64 カーネル の設定 K8s クラスタ のシステム要件を確認しながら、設定します。 # カーネルの設定 ## カーネルモジュールのロード $ cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf overlay br_netfilter EOF $ sudo modprobe overlay $ sudo modprobe br_netfilter ## カーネルパラメータの設定 $ cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf net.bridge.bridge-nf-call-iptables = 1 net.bridge.bridge-nf-call-ip6tables = 1 net.ipv4.ip_forward = 1 EOF # 確認 $ sudo sysctl net.bridge.bridge-nf-call-iptables $ sudo sysctl net.bridge.bridge-nf-call-ip6tables $ sudo sysctl net.ipv4.ip_forward $ sudo sysctl --system コンテナランタイムの設定 コンテナランタイム、CNI Pluginを入れていきます。今回は cotainerd + runc の一般的な構成としました。また、コンテナ管理にcgroupを利用するように設定します。 # containerdのインストール $ ARCH="arm64" $ CONTAINERD_VERSION="1.6.32" $ curl -fsSL https://github.com/containerd/containerd/releases/download/v${CONTAINERD_VERSION}/containerd-${CONTAINERD_VERSION}-linux-${ARCH}.tar.gz -o containerd-${CONTAINERD_VERSION}-linux-${ARCH}.tar.gz $ curl -fsSL https://github.com/containerd/containerd/releases/download/v${CONTAINERD_VERSION}/containerd-${CONTAINERD_VERSION}-linux-${ARCH}.tar.gz.sha256sum -o containerd-${CONTAINERD_VERSION}-linux-${ARCH}.tar.gz.sha256sum $ sha256sum -c containerd-${CONTAINERD_VERSION}-linux-${ARCH}.tar.gz.sha256sum $ sudo tar -C /usr/local -xzvf containerd-${CONTAINERD_VERSION}-linux-${ARCH}.tar.gz $ rm containerd-${CONTAINERD_VERSION}-linux-${ARCH}.tar.gz containerd-${CONTAINERD_VERSION}-linux-${ARCH}.tar.gz.sha256sum # containerdの設定 $ sudo mkdir -p /etc/containerd $ containerd config default | sudo tee /etc/containerd/config.toml $ curl https://raw.githubusercontent.com/containerd/containerd/main/containerd.service | sudo tee /etc/systemd/system/containerd.service > /dev/null # containerdをサービスとして起動 $ sudo systemctl daemon-reload $ sudo systemctl enable --now containerd $ sudo systemctl status containerd # runcのインストール $ sudo apt-get update -y $ sudo apt-get install -y libseccomp2 gpg $ curl -fsSL https://github.com/opencontainers/runc/raw/main/runc.keyring -o runc.keyring $ gpg --import runc.keyring $ RUNC_VERSION="1.1.12" $ curl -fsSL https://github.com/opencontainers/runc/releases/download/v${RUNC_VERSION}/runc.${ARCH} -o runc $ curl -fsSL https://github.com/opencontainers/runc/releases/download/v${RUNC_VERSION}/runc.${ARCH}.asc -o runc.asc $ gpg --verify runc.asc runc $ sudo install -m 755 runc /usr/local/sbin/runc $ rm runc runc.asc runc.keyring # インストール確認 $ containerd -v $ runc -v # CNI Pluginのインストール $ sudo mkdir -p /opt/cni/bin CNI_VERSION="1.1.1" $ curl -fsSL https://github.com/containernetworking/plugins/releases/download/v${CNI_VERSION}/cni-plugins-linux-${ARCH}-v${CNI_VERSION}.tgz -o cni-plugins-linux-${ARCH}-v${CNI_VERSION}.tgz $ curl -fsSL https://github.com/containernetworking/plugins/releases/download/v${CNI_VERSION}/cni-plugins-linux-${ARCH}-v${CNI_VERSION}.tgz.sha256 -o cni-plugins-linux-${ARCH}-v${CNI_VERSION}.tgz.sha256 $ sha256sum -c cni-plugins-linux-${ARCH}-v${CNI_VERSION}.tgz.sha256 $ sudo tar -C /opt/cni/bin -xzvf cni-plugins-linux-${ARCH}-v${CNI_VERSION}.tgz $ rm cni-plugins-linux-${ARCH}-v${CNI_VERSION}.tgz cni-plugins-linux-${ARCH}-v${CNI_VERSION}.tgz.sha256 # systemdを利用するようにcontainerdの設定を変更 $ sudo cp /etc/containerd/config.toml /etc/containerd/config.toml.bak $ sudo sed -i '/\[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options\]/,/\[/ s/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml $ diff /etc/containerd/config.toml /etc/containerd/config.toml.bak 127c127 < SystemdCgroup = true --- > SystemdCgroup = false $ sudo systemctl restart containerd # swapをOFFにする $ sudo swapoff -a Kubernetes 関連のツールのインストール 今回は クラスタ の構築に kubeadm を利用するので、 kubeadm をインストールします。その他、 kubelet も必要になるのでインストールしておきます。 kubectl は必要に応じて入れます。 $ sudo apt-get install -y apt-transport-https ca-certificates curl gpg $ curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.30/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.30/deb/ /' | sudo tee /etc/apt/sources.list.d/kubernetes.list $ sudo apt-get update -y $ sudo apt-get install -y kubelet kubeadm kubectl $ sudo apt-mark hold kubelet kubeadm kubectl # 確認 $ kubeadm version $ kubectl version --client=true $ kubelet --version 2. クラスタ の構築 今回は以下の戦略とします。 Kubernetes v1.30を入れる 高可用性 クラスタ として構成する ※現時点ではマスターノードは1台だけですが、将来的に追加できるようにするため。途中で変更できないため、 クラスタ 作成時にこの構成にしておく必要があります マスターノード上で以下のように構成する HAProxyでコン トロール プレーンの API Server宛のリク エス トをリバースプロキシする Keepalived で仮想IPを設定する 各ノードのkubelet,kubectlからはこの仮想IPを使用するようにする 上記の名前解決は簡易的にhostsを用いる HA構成のAPIServer まず、 Keepalived をインストールし、固定したIP(今回は192.168.3.90)でアクセスできるように設定します。 #Keepalivedのインストール/設定 $ sudo apt update $ sudo apt install keepalived $ K8S_VIP="192.168.3.90" $ cat <<_EOF_ | sudo tee /etc/keepalived/keepalived.conf vrrp_instance VI_1 { state MASTER interface eth0 virtual_router_id 51 priority 100 advert_int 1 authentication { auth_type PASS auth_pass 1111 } virtual_ipaddress { ${K8S_VIP} } } _EOF_ # keepalivedを利用するためのカーネルモジュールのロード設定: https://access.redhat.com/documentation/ja-jp/red_hat_enterprise_linux/7/html/load_balancer_administration/s1-initial-setup-forwarding-vsa $ echo "net.ipv4.ip_nonlocal_bind = 1" | sudo tee -a /etc/sysctl.d/k8s.conf $ sudo sysctl net.ipv4.ip_nonlocal_bind $ sudo systemctl start keepalived $ sudo systemctl enable keepalived # 確認 $ ip addr show eth0 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000 link/ether 2c:cf:67:2c:e4:9a brd ff:ff:ff:ff:ff:ff inet 192.168.3.81/24 brd 192.168.3.255 scope global eth0 valid_lft forever preferred_lft forever inet 192.168.3.90/32 scope global eth0 # ←これができる valid_lft forever preferred_lft forever ...中略... # hostsを編集して名前解決できるようにしておく # /etc/cloud/templates/hosts.debian.tmplは起動時にhostsを生成するテンプレートのため、一旦リブートするか、hostsにも直接書くかどちらが必要 $ cat <<_EOF_ | sudo tee -a /etc/cloud/templates/hosts.debian.tmpl # These entry is for testing keepalived configuration for k8s apiserver ${K8S_VIP} kubernetes-frontend.local _EOF_ 続いて、HAProxyを設定します。 # HAProxyのインストール $ sudo apt update $ sudo apt install haproxy $ sudo cp /etc/haproxy/haproxy.cfg /etc/haproxy/haproxy.cfg_bak # frontendにKeepalived, backendに後で作成するKubernetes API Serverを設定 $ cat <<_EOF_ | sudo tee /etc/haproxy/haproxy.cfg global log /dev/log local0 log /dev/log local1 notice user haproxy group haproxy daemon defaults log global mode tcp timeout connect 5000 timeout client 50000 timeout server 50000 frontend kubernetes-frontend bind *:8443 default_backend kubernetes-backend backend kubernetes-backend mode tcp balance roundrobin server master-1 127.0.0.1:6443 check verify none _EOF_ $ sudo systemctl reload haproxy クラスタ 作成 K8s クラスタ を作成し、Podをデプロイできる状態にします。以下のような流れになります。 コン トロール プレーンの設定 Pod Network アドオンの設定 Nodeを クラスタ に追加 MetalLBの設定 テスト用のPodをデプロイして確認 コン トロール プレーンの設定 kubeadm init を使います。コン トロール プレーンの作成は、以下のような処理が行われることで実現しているようです。 # https://kubernetes.io/docs/reference/setup-tools/kubeadm/kubeadm-init/#synopsis # 以下の順序で処理される preflight Run pre-flight checks certs Certificate generation /ca Generate the self-signed Kubernetes CA to provision identities for other Kubernetes components /apiserver Generate the certificate for serving the Kubernetes API /apiserver-kubelet-client Generate the certificate for the API server to connect to kubelet /front-proxy-ca Generate the self-signed CA to provision identities for front proxy /front-proxy-client Generate the certificate for the front proxy client /etcd-ca Generate the self-signed CA to provision identities for etcd /etcd-server Generate the certificate for serving etcd /etcd-peer Generate the certificate for etcd nodes to communicate with each other /etcd-healthcheck-client Generate the certificate for liveness probes to healthcheck etcd /apiserver-etcd-client Generate the certificate the apiserver uses to access etcd /sa Generate a private key for signing service account tokens along with its public key kubeconfig Generate all kubeconfig files necessary to establish the control plane and the admin kubeconfig file /admin Generate a kubeconfig file for the admin to use and for kubeadm itself /super-admin Generate a kubeconfig file for the super-admin /kubelet Generate a kubeconfig file for the kubelet to use *only* for cluster bootstrapping purposes /controller-manager Generate a kubeconfig file for the controller manager to use /scheduler Generate a kubeconfig file for the scheduler to use etcd Generate static Pod manifest file for local etcd /local Generate the static Pod manifest file for a local, single-node local etcd instance control-plane Generate all static Pod manifest files necessary to establish the control plane /apiserver Generates the kube-apiserver static Pod manifest /controller-manager Generates the kube-controller-manager static Pod manifest /scheduler Generates the kube-scheduler static Pod manifest kubelet-start Write kubelet settings and (re)start the kubelet upload-config Upload the kubeadm and kubelet configuration to a ConfigMap /kubeadm Upload the kubeadm ClusterConfiguration to a ConfigMap /kubelet Upload the kubelet component config to a ConfigMap upload-certs Upload certificates to kubeadm-certs mark-control-plane Mark a node as a control-plane bootstrap-token Generates bootstrap tokens used to join a node to a cluster kubelet-finalize Updates settings relevant to the kubelet after TLS bootstrap /experimental-cert-rotation Enable kubelet client certificate rotation addon Install required addons for passing conformance tests /coredns Install the CoreDNS addon to a Kubernetes cluster /kube-proxy Install the kube-proxy addon to a Kubernetes cluster show-join-command Show the join command for control-plane and worker node kubeadm init を実行する際のオプションは、コマンド引数として渡すか、 yaml で書いて渡すことができます。今回は以下のとおり kubeadm-config.yaml を書きました。デフォルトから変更する設定項目は以下のとおりです。 コン トロール プレーンを高可用性構成にするための controlPlaneEndpoint / apiServer.certSANs Podに割り当てるサブネットを設定するための networking.podSubnet $ cat <<_EOF_ > ~/kubeadm-config.yaml apiVersion: kubeadm.k8s.io/v1beta3 kind: ClusterConfiguration kubernetesVersion: 1.30.0 controlPlaneEndpoint: "kubernetes-frontend.local:8443" apiServer: certSANs: - kubernetes-frontend.local # hostname of keepalived - 192.168.3.90 # VIP of keepalived - master-1.local # hostname of master-1 - 192.168.3.81 # IP of master-1 networking: podSubnet: "10.64.0.0/16" _EOF_ # kubeadm initを実行する # 他のノードを構成する際の手順を簡略化するために `--upload-certs` オプションをオプションを追加 $ sudo kubeadm init \ --config kubeadm-config.yaml \ --upload-certs # 以下が出力されたら完了 Your Kubernetes control-plane has initialized successfully! To start using your cluster, you need to run the following as a regular user: mkdir -p $HOME/.kube sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config sudo chown $(id -u):$(id -g) $HOME/.kube/config Alternatively, if you are the root user, you can run: export KUBECONFIG=/etc/kubernetes/admin.conf You should now deploy a pod network to the cluster. Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at: https://kubernetes.io/docs/concepts/cluster-administration/addons/ You can now join any number of the control-plane node running the following command on each as root: kubeadm join kubernetes-frontend.local:8443 --token {token} \ --discovery-token-ca-cert-hash sha256:{discovery-token-ca-cert-hash} \ --control-plane --certificate-key {certificate-key} Please note that the certificate-key gives access to cluster sensitive data, keep it secret! As a safeguard, uploaded-certs will be deleted in two hours; If necessary, you can use "kubeadm init phase upload-certs --upload-certs" to reload certs afterward. Then you can join any number of worker nodes by running the following on each as root: $ kubeadm join kubernetes-frontend.local:8443 --token {token} \ --discovery-token-ca-cert-hash sha256:{discovery-token-ca-cert-hash} # 完成したらkubectlを使えるようにしておく $ mkdir -p $HOME/.kube $ sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config $ sudo chown $(id -u):$(id -g) $HOME/.kube/config $ source <(kubectl completion bash) $ echo "source <(kubectl completion bash)" >> ~/.bashrc Pod Network アドオン この時点では、まだcorednsが起動してきません。Podが動作するようになるには、Pod Network アドオンをインストールします。 Pod Network アドオンは、主に以下のような責務を負う コンポーネント です。 コンテナに対する NIC の設定 IPアドレス の管理、動的割り当て ネットワークポリシーによる分離(Calicoの場合) クロスホスト通信 Pod Network アドオンはCalicoやFlannelなど複数の選択肢がありますが、今回はCalicoを使用します。 現時点で、Calicoは、KubeadmプロジェクトがE2Eテストを行っている唯一のアドオンだそうです 今回はCalicoをオペレータを使ってインストールします。 # Calicoをインストールするためのマニフェストを一旦ダウンロード $ curl https://raw.githubusercontent.com/projectcalico/calico/v3.28.0/manifests/tigera-operator.yaml -O $ curl https://raw.githubusercontent.com/projectcalico/calico/v3.28.0/manifests/custom-resources.yaml -O kubeadm init 時と同様、Podサブネットをこちらにも設定する必要があります。 # custom-resource.yaml # クラスタ構築時に指定したPod SubnetのCIDRを指定する # This section includes base Calico installation configuration. # For more information, see: https://docs.tigera.io/calico/latest/reference/installation/api#operator.tigera.io/v1.Installation apiVersion: operator.tigera.io/v1 kind: Installation metadata: name: default spec: # Configures Calico networking. calicoNetwork: ipPools: - name: default-ipv4-ippool blockSize: 26 - cidr: 192.168.0.0/16 + cidr: 10.64.0.0/16 $ kubectl create -f tigera-operator.yaml $ kubectl create -f custom-resources.yaml Nodeの追加 クラスタ へのノードの追加は、先ほどの kubeadm init を実行した後に出力されていた内容に従うだけで簡単にできます。 今回はワーカーノードを2台追加します。 # 先に、API Serverの仮想IPに解決されるホスト名をhostsに追記しておく(リブートして反映) $ cat <<_EOF_ | sudo tee -a /etc/cloud/templates/hosts.debian.tmpl # These entry is for testing keepalived configuration for k8s apiserver ${K8S_VIP} kubernetes-frontend.local _EOF_ # kubeadm init時の出力に従ってノードごとに実行 $ sudo kubeadm join kubernetes-frontend.local:8443 --token {token} \ --discovery-token-ca-cert-hash sha256:{discovery-token-ca-cert-hash} # nodeが上がっている $ kubectl get node NAME STATUS ROLES AGE VERSION master-1 Ready control-plane 6d11h v1.30.1 node-1 Ready <none> 6d11h v1.30.1 node-2 Ready <none> 6d11h v1.30.1 # Calicoの状態を確認 # Calicoでは、各ノードがBGPピアリングして経路情報を交換し、L3のレイヤでルーティングする $ sudo ./calicoctl node status Calico process is running. IPv4 BGP status +--------------+-------------------+-------+------------+-------------+ | PEER ADDRESS | PEER TYPE | STATE | SINCE | INFO | +--------------+-------------------+-------+------------+-------------+ | 192.168.3.82 | node-to-node mesh | up | 2024-06-07 | Established | | 192.168.3.83 | node-to-node mesh | up | 2024-06-07 | Established | +--------------+-------------------+-------+------------+-------------+ IPv6 BGP status No IPv6 peers found. MetalLBの設定 オンプレ環境の K8s クラスタ 環境では、Type: LoadBalancer のServiceを公開するためにL4のLBを用意する必要があります。今回はMetalLBをLayer2モードで使用し、また、特定の IPアドレス が自動的にアタッチされるように設定します。 # MetalLBのインストール kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.14.5/config/manifests/metallb-native.yaml # ServiceのExternal-IPに割り当てるIPアドレスを設定する cat <<_EOF_ | kubectl apply -f - apiVersion: metallb.io/v1beta1 kind: IPAddressPool metadata: name: first-pool namespace: metallb-system spec: addresses: - 192.168.3.91-192.168.3.99 --- apiVersion: metallb.io/v1beta1 kind: L2Advertisement metadata: name: example namespace: metallb-system _EOF_ テスト用のPodをデプロイして確認 準備ができたので、テスト用のNginxをデプロイし、複数のタイプのService経由で疎通できるかどうかを確認します。 #テスト用Nginx kubectl create deployment test-nginx --image=nginx --replicas 3 kubectl get pod -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES test-nginx-56cc9db4bc-26955 1/1 Running 1 (6d19h ago) 34d 10.64.84.143 node-1 <none> <none> test-nginx-56cc9db4bc-67ddw 1/1 Running 1 (6d19h ago) 34d 10.64.247.16 node-2 <none> <none> test-nginx-56cc9db4bc-6gffj 1/1 Running 1 (6d19h ago) 34d 10.64.84.142 node-1 <none> <none> cat <<_EOF_ | kubectl apply -f - # Service type: ClusterIP apiVersion: v1 kind: Service metadata: name: test-nginx-clusterip spec: type: ClusterIP ports: - name: "http-port" protocol: "TCP" port: 8080 targetPort: 80 selector: app: test-nginx --- # Service type: NodePort apiVersion: v1 kind: Service metadata: name: test-nginx-nodeport spec: type: NodePort ports: - name: "http-port" protocol: "TCP" port: 8080 targetPort: 80 nodePort: 30080 selector: app: test-nginx --- # Service type: LoadBalancer apiVersion: v1 kind: Service metadata: name: test-nginx-metallb spec: type: LoadBalancer ports: - name: "http-port" protocol: "TCP" port: 8080 targetPort: 80 selector: app: test-nginx _EOF_ Pod / Node / PCなどいろいろな場所から疎通確認してみます # Pod間通信をテストする用のPodを作成 kubectl run test --image=ubuntu -it --rm # From test pod ## -> APIServer curl https://kubernetes/healthz #OK ## -> Type: ClusterIP / NodePort / LoadBalancer curl http://test-nginx-clusterip:8080 #OK curl http://test-nginx-nodeport:8080 #OK curl http://test-nginx-metallb:8080/ #OK # From Node ## -> Type: LoadBalancer curl http:/192.168.3.91:8080/ #OK # From PC ## -> Type: LoadBalancer curl http:/192.168.3.91:8080/ #OK 3. PrometheusとGrafanaのセットアップ kube-prometheus-stack Helmチャートによる簡単なセットアップ 最後に、PrometheusとGrafanaを設定して監視基盤を構築します。 AlertManager、各種Monitor、Ruleなどの他にも、NodeExporter、Grafanaなど必要な コンポーネント がたくさんあるので、今回は一括構築・管理が可能なHelmチャートである kube-prometheus-stack を使います。 ※ いくつか手段 が提供されていますが、設定のカスタマイズや管理をする上ではHelmチャートを使うのが融通が利きそうでした ※ Raspberry Pi 5にもM2. SSD HATが発売されたので、master-1を NFS サーバとしても設定していました。今回、取得した各メトリクスを共有 ディレクト リに保存したくなったのですが・・・ PrometheusはNFSをサポートしていない ようでした。知りませんでした。 # https://github.com/prometheus-community/helm-charts/tree/main/charts/kube-prometheus-stack helm repo add prometheus-community https://prometheus-community.github.io/helm-charts helm repo update # GrafanaダッシュボードとPrometheusのUIはPort-ForwardなしでPCから確認したいので、ServiceのTypeを変更 cat <<_EOF_ > values.yaml prometheus: service: type: LoadBalancer grafana: service: type: LoadBalancer _EOF_ # monitoring Namespaceを作ってそこにデプロイ kubectl create ns monitoring helm install prom-stack prometheus-community/kube-prometheus-stack -n monitoring -f values.yaml ダッシュ ボードは初期の状態でたくさん設定されていますが、追加で、 Raspberry Pi & Docker Monitoring というのを入れてみました。 Raspberry Pi は温度を気にしたくなりますので、良い感じに表示されていて嬉しいです。 インターネット経由のアクセス (オプション) この K8s クラスタ のメンテナンスを外出時にもしたい、Grafana ダッシュ ボードを外出先からも確認したい、となった場合、どのようにすれば良いでしょうか。 Cloudflare を使ってみて、非常に体験が良かったので最後に紹介します。 簡単にいうと、オンプレ側のマシンにcloudflaredをインストールして、そこからCloudflareが管理するエッジ環境に対してトンネルを張り、その経路を利用してセキュアに通信できる、かつその仕組みを利用する際、認証機能、 サブドメイン 用の自動証明書管理、セキュリティを維持するための各種機能も提供されているというものです。自分の ドメイン さえ持っていれば、今回の用途では無料で利用できます。 Cloudflare Tunnel の作成 UIでの作成方法 に従うと、簡単に設定できます。 cloudflaredは、すべてのサーバに入れる必要はなく、トンネルを作成したいマシンに対して入れれば足ります。残りの設定はUIから行っていきます(今回はmaster-1に入れました) Public Hostname( サブドメイン として利用できるHostnameになります)とService(ローカル側からアクセス可能なIP/ポートの組み合わせ)を関連付けます。今回はGrafana ダッシュ ボードと、 SSH アクセスを有効にしたいので、以下をサービスとして登録し、それぞれに対して サブドメイン を割り当てました。 http://prom-stack-grafana.monitoring.svc.cluster.local:80 ssh://localhost:22 Application の設定 上記で設定したPublic Hostnameに対して、アクセスポリシー、認証の設定などを有効化できます。 自分のメールアドレスだけアクセスできるようにしつつ、パスワードをメール ワンタイムパスワード に設定しました。 Browser SSH タイプでApplication設定を作成すると、ブラウザ上でターミナルが表示されます。 まとめ Raspberry Pi で K8s クラスタ ーを作ってみました。これから育てていきます HA構成や家庭のLAN環境特有の問題に対処することで、L2/L3周りの学びを得る機会になりました オンプレ Kubernetes を業務運用する際の難易度の高さを改めて実感しました(N/W、マシン、ストレージ、可用性・信頼性の確保、継続的な改善とメンテナンス、大規模運用・・・) Cloudflareはもっと色々な用途に使ってみたいです user-data  のサンプル 初期設定系はすべてこちらに突っ込みたくなりますが、うまくいかない時の デバッグ が難しいので、簡単な最低限の設定項目だけをuser-dataに、それ以外は スクリプト を書くなどして初回起動後に実行する方が個人的には好きです。 #cloud-config hostname: { HOST_NAME } # Configured by Raspberry Pi Imager manage_etc_hosts: true +package_update: true +package_upgrade: true packages: - avahi-daemon + - libseccomp2 + - gpg + - apt-transport-https + - ca-certificates + - curl + - raspi-config apt: ...中略... users: ...中略... +write_files: + # Configure IP Addrress Fixation + - path: /etc/netplan/99_manual_config.yaml + owner: root:root + permissions: "0600" + content: | + network: + version: 2 + ethernets: + eth0: + dhcp4: true + addresses: + - 192.168.3.83/24 + nameservers: + addresses: + - 192.168.3.1 runcmd: - localectl set-x11-keymap "us" pc105 - setupcon -k --force || true + - netplan apply + - | + cat <<_EOF_ | tee -a /etc/cloud/templates/hosts.debian.tmpl + # These entry is for testing keepalived configuration for k8s apiserver + 192.168.3.90 kubernetes-frontend.local + + _EOF_ 参考 kubeadmを使用したクラスターの作成 Pod Networking kubeadm kubeadm: High Availability Considerations containerd Install Calico networking and network policy for on-premises deployments CalicoによるKubernetesピュアL3ネットワーキング MetalLB 執筆: @kato.shota 、レビュー: @kobayashi.hinami ( Shodo で執筆されました )
はじめまして、X(クロス) イノベーション 本部プロダクト イノベーション センターの瀧川亮弘です。 本記事は、 電通総研 Advent Calendar 2024 10日目の投稿です。 もともとネイティブアプリを主戦場とするFlutterですが、Web開発にどれほど活用できるのか調査したため共有します。 Flutter とは Flutter for Web とは Flutter for Web の注意点 注意点1. ネイティブアプリがメインターゲット 注意点2. 実装言語がDartに限定される 注意点3. SEOに弱い 注意点4. 初期ロードが重い 対策方法 Flutter for Web の使いどころ 終わりに Flutter とは Flutterは、2017年に Google が発表した オープンソース のUI開発 フレームワーク です。 プログラミング言語 には コンパイル 型の Dart を採用しており、高いパフォーマンスと開発効率を実現しています。 単一のコードベースから iOS 、 Android 、デスクトップ、Webといった 複数のプラットフォーム向けにアプリを開発できる点が特徴 です。 また、 マテリアルデザイン に準拠した多彩な ウィジェット (部品)が標準で提供されており、美しく直感的なUIを比較的簡単に作成できます。 Flutter for Web とは Flutterは当初、 iOS および Android 向けのネイティブ アプリ開発 をターゲットとしてスタートしましたが、現在ではデスクトップやWebにも対応しています。 ここではFlutterによるWeb アプリ開発 のことを Flutter for Web と呼びます。 Flutter for Web は、モバイルアプリやデスクトップアプリと 同じコードでWebアプリも一緒に開発したい という場合に便利です。 コストや開発期間を削減できるだけでなく、 プラットフォーム間でのUIの差異を最小限に抑える こともできます。 また、Webアプリのパフォーマンス向上のためにWebAssembly(Wasm)へのビルドもサポートされています。 Flutter for Web の注意点 FlutterでWebアプリを開発する場合、以下のような注意点があります。 注意点1. ネイティブアプリがメインターゲット Web向けのサポートも進んでいるものの、モバイル向けと比較するとやはり劣る部分があります。 たとえば、モバイル向けパッケージにはWeb非対応なものが多く、Web対応であっても、 Web専用の機能改善やバグ修正は劣後となる傾向 にあります。 また、ネイティブ アプリ開発 では Hot Reload が利用可能ですが、Web開発では Hot Restart しか利用できず、開発体験が落ちます。 Hot Reload:アプリを停止せずにコード変更を即座に反映する。状態が保持される。 Hot Restart:アプリを再起動してコード変更を反映する。状態はリセットされる。 注意点2. 実装言語が Dart に限定される 通常のWeb技術(HTML, CSS , JavaScript )と異なり、 全てのロジックやUIを Dart のみで実装する必要があるため、実装難易度が高い です。 また、 サードパーティ の JavaScript ライブラリを型安全に利用したい場合、 Dart 側で独自に型定義を行う必要があります。 TypeScriptのように標準で豊富な型定義が存在しない点も、開発のハードルを上げる要因のひとつです。 注意点3. SEO に弱い Flutterはシングルページアプリケーション(SPA)の構成を採用しており、初期ロード後にクライアント側で動的にDOMを生成します。 標準ではサーバーサイド レンダリング ( SSR )やマルチページアプリケーション( MPA )に対応していないため、 検索エンジン のクロールやインデックスが難しくなり、 SEO に弱くなります 。 さらに、 Canvas による画面描画では、 検索エンジン による解析が困難となります。これも SEO に弱くなる要因のひとつです。 また、スクリーンリーダーによる読み上げも困難となるため、 アクセシビリティ の低下にもつながります。 注意点4. 初期ロードが重い Flutterでは ビルド時に比較的大きなファイルが生成されるため、初期ロードが重くなります 。 空のプロジェクトでも数MBになり、ページ数や機能が増えるとさらに肥大化します。 標準ではNext.jsのような自動コード分割機能も備わっていないため、ユーザー操作とは関係のない画面ソースもブラウザにダウンロードする必要があり、非効率です。 対策方法 いくつか対策方法はあります。 Webサーバーで gzip 圧縮を行い、転送時のファイルサイズを小さくする。 ユーザー操作に応じて必要最小限のライブラリを遅延ロードする。 手動でコード分割を行い、画面単位で必要最小限のファイルを取得する。 根本解決ではありませんが、初期ロード時にローディングアニメーションを表示してユーザー体験を改善する。 Flutter for Web の使いどころ 一般的なWeb開発には従来のWeb技術(HTML、 CSS 、 JavaScript など)を用いるのが望ましいです。 しかし、モバイルアプリやデスクトップアプリと同じコードベースでWebアプリも開発する必要がある場合、本記事で紹介した注意点を認識したうえでFlutterを選択するのが良いでしょう。 終わりに 最後までご覧いただきありがとうございました。 私たちは一緒に働いてくれる仲間を募集しています! 株式会社 電通総研 新卒採用サイト 電通総研グループ キャリア採用サイト:電通総研 執筆: @takigawa.akihiro 、レビュー: @nakamura.toshihiro ( Shodo で執筆されました )
こんにちは! X(クロス) イノベーション 本部 プロダクト イノベーション センターの佐藤です。 電通総研 Advent Calendar 2024 、12日目の今日は、DRY 原則の再解釈を通して、設計上の落とし穴である「誤った共 通化 」に陥らないためにはどうするべきかを考察していきたいと思います。 本稿は、設計の世界に足を踏み入れたばかりの方から鍛え抜かれた達人 プログラマー まで、幅広い方々に気づきを提供できるような内容を目指しました。 初学者の方は手元に参考書を、達人の方はくつろぎのコーヒーを携えながら、気軽にお読みいただければ幸いです。 DRY SRP 再び、DRY 誤った共通化 DRY をコードで捉えない まとめ 参考文献 DRY DRY 原則は、プログラミングや設計の初学者が最初期に学ぶ考えだと思います。 そして、それはしばしば 「 コピペ をしてはいけない」「 同じコード を繰り返し重複して書いてはいけない」 といった説明がなされることから、「DRY 原則は コードそれ自体 を対象とする」と理解されている方々は決して少なくないのではないでしょうか。 しかし、筆者としては「この理解は、ともすれば誤解につながるケースが多いのではないか」と考えています。 実は、DRY 原則を世に広めた名著 達人プログラマー の第2版においても、この点について言及されています。 本書の初版では、「DRY 原則」が意味することについて書き足りない部分がありました。多くの人々はこれがコードの話だと受け取ってしまったのです。つまり、DRY を「 ソースコード のコピー&ペーストをしてはいけない」と解釈してしまったのです。 これも DRY 原則の一部ですが、ほんの些細な一部でしかありません。 [1], p.40 確かに、コードのコピペや繰り返しが好ましくない結果をもたらす可能性があることは事実です。 しかし、この「コード」に着眼した理解は、その分かり易さがゆえに 「であれば、同じコードは 共 通化 しよう 」 といった拡大解釈を招いている可能性が、一定以上あるように思います。 達人 プログラマー は以下のように続けます。 DRY 原則は「知識」や「意図」の二重化についての原則です。 [1], p.40 本記事では、この忘れ去られがちな DRY の本質について「知識」や「意図」とは別のもう一つの観点、「 責任 」の観点から捉え直してみたいのです。 SRP ここで一度 DRY から離れて、SOLID 原則のイニシャルを務める、SRP(単一責任の原則) を思い出してみます。 クラスを変更する理由は1つ以上存在してはならない。 [2], p.122 ここではクラスと書かれていますが、設計思想としてはクラスに限ったことではありません。 なぜ、1つ以上(厳密には2つ以上ですが、ここでは引用元の訳に倣います)存在してはいけないのでしょうか。 それは、設計に硬さと脆さを与えることにつながるからです。 エンティティ (本記事では、メソッド、クラス、モジュール、 コンポーネント といったシステムの構成要素を意味します)が独立した複数の責任を担っている時、ほとんどの場合において、そのエンティティを 変更する理由もまた、責任ごとに独立して生じます 。 すると、ある責任に関する変更により他の責任が意図せず果たせなくなり、予想外の欠陥が発生する(=脆さ)可能性があります。そして、往々にしてこのような実装は変更し辛い(=硬さ)ものです。 一般に、責任と変更理由は対応します。 アジャイルソフトウェア開発の奥義 (以下、奥義本) は、そこに一つ「役割」という概念も合わせて、SRP における言葉を以下のように定義しています。 役割(責任)= 変更理由 [2], p.123 再び、DRY 奥義本は、「腐敗するソフトウェアの兆候」の一つ「不必要な繰り返し」について、以下のように説明しています。 システム内に重複するコードがあると、システムの変更はとても骨の折れる作業になる。[...] コード中にある同じような部分をすべて修正しなければならないからだ。 [2], p.111 また、 プリンシプル オブ プログラミング にも、DRY を適用すべき理由として同様のことが記載されています。 同じようなコードが複数あると、その複数箇所を正確に修正しなければ、全体としての整合が取れません。慎重に作業しないと、修正漏れの危険性があります。 [3], p.50 これら、DRY 適用の動機(=DRYを破った場合の問題)から逆算すると、そこには「それらが同じ 変更理由 である」という前提が浮かび上がってきます。 すなわち、DRY とは「同じ理由で変更されてしまうエンティティは、重複して存在するべきではない」と説明されるのが、より適切ではないでしょうか。 筆者は、ここであえて飛躍してコードをエンティティと言い換えましたが、なぜコードという記述を避けるのかについては後述します。 奥義本は、SRP における言葉を以下のように定義しています。 役割(責任)= 変更理由 [2], p.123 これは、重要さを伝えるための意図的な繰り返しです。 DRY と SRP の間に、 責任 を軸とした関連を見出だせる気がします。 誤った共 通化 SRP と DRY。 この二つの原則は、いずれも設計における構成要素としてのエンティティについて、責任の観点から、その凝集性と一意性を論ずるものだと言えます。 SRPは エンティティは単一の責任を担うべきである ということを説いています。 一方で DRY は 同じ責任を担うエンティティを重複して定義すべきでない(繰り返してはならない) と説いています。 DRY の表層だけを捉え、責任の考慮なしに安易な共 通化 に走ると、それは SRP に違反しかねません 。 共 通化 されるエンティティは、SRP に準拠、すなわち単一の責任のみを担っている必要があります。そして本来、責任というのは意図的な設計のうえに見出され与えられるものです。 したがって、「ここはこの前実装したロジックと(コード的に)同じだから、メソッドに切り出して双方から使おう」といった後づけの共 通化 や再利用は、高確率で失敗すると思われます。 EC サービスを例に考えてみましょう。 このサービスには会員限定割引が存在し、その割引率の計算について責任を担う calcMemberDiscountRate メソッドが存在するとします。その計算ロジックの一部が、後から実装される クリスマスセール 割引率計算ロジックと偶然にも重複することが分かりました。 この時、 calcMemberDiscountRate メソッドの該当コードをコピペして、 calcXmasDiscountRate メソッドを実装することは、果たして悪でしょうか。 むしろ「それは DRY に違反している」として共 通化 された場合に誕生する calcMemberAndXmasDiscountRate メソッドの方が、よっぽど恐ろしく感じられます。 これは説明のための極端な例に過ぎません。実際にはもっと巧妙に、例えばそれらしい抽象的な名前を隠れ蓑にして、複数の責任を担うエンティティが人知れず実装されるのです。 DRY をコードで捉えない 前節では、DRY を以下のように捉えました。 同じ責任を担うエンティティを重複して定義すべきでない(繰り返してはならない) ところで、 オブジェクト指向 の世界において、責任を担えるエンティティの最小単位は何でしょうか。 レガシーコードからの脱却 に、以下のような記載があります。 デジタルの領域では、ものごとはラベルによって定義されるのではなく、ふるまいによって定義されるのである。 [4], p.144 これは、凝集性の文脈におけるオブジェクト(クラス)に関する説明ではありますが、この「ふるまい」(いわばインターフェース)こそ責任の基本であり、その最小単位はメソッドではないでしょうか。 だとすれば、そもそもそれ以下のコード断片については、実装の詳細のそのまた一部である可能性が高く、そういった責任の伴わない単なる欠片としてのコードについては、DRY 適用を論ずるには時期尚早だとも思えるのです。これが、「再び、DRY」節にてコードをエンティティと言い換えた理由であり、そのようなコードのコピペの良し悪しは、その断片だけからは判断が難しいでしょう。 前節で述べたような誤った共 通化 に陥らないためには、常に責任の観点から DRY を捉え、以下のように自問自答することが有効です。 「今、自分が実装しているこのエンティティの責任は、既にほかのエンティティが担ってくれていないか、あるいは他にこの責務の適任者はいないだろうか」 そして、特に何かを共 通化 し始めようという時には、まずは一呼吸おいて、そのエンティティが担う責任を注意深く観察し、隠れ蓑をまとった意図しない責任(変更理由)がいないかを、慎重に確認することが重要だと言えるでしょう。 まとめ 本記事では、DRY 原則の本質を SRP における責任の観点から紐解き、誤った共 通化 に陥らないための新たな DRY 原則の捉え方を検討しました。 設計原則は奥深く、たとえ表面上は単純なルールのように思えても、深く検討することで新しい気づきや解釈を与えてくれます。 本記事の解説が、皆様が今後取り組む設計やコーディングの一助となれば、嬉しい限りです。 参考文献 [1] David Thomas 他, 村上訳. 達人 プログラマー 第2版 熟達に向けたあなたの旅. オーム社 , 2020. [2] Robert C. Martin 他, 瀬谷訳. アジャイル ソフトウェア開発の奥義 第2版 オブジェクト指向 開発の神髄と匠の技. SBクリエイティブ 株式会社, 2008. [3] 上田勲. プリンシプル オブ プログラミング 3年目までに身につけたい 一生役立つ101の原理原則. 株式会社 秀和システム , Kindle 版. [4] David Scott Bernstein, 吉羽 他 訳. レガシーコードからの脱却 ソフトウェアの寿命を延ばし価値を高める9つのプ ラク ティス. 株式会社 オライリー・ジャパン , 2019. ※ 本稿に掲載された画像は AI により生成されました 私たちは一緒に働いてくれる仲間を募集しています! 電通総研グループ キャリア採用サイト:電通総研 執筆: @satorin 、レビュー: @nakamura.toshihiro ( Shodo で執筆されました )
こんにちは、 電通 総研の金子大地です。 本記事は 電通総研 Advent Calendar 2024 の12月11日の記事です! 前編 に引き続き、今回も学生さんに読んでいただくことを想定して、実プロジェクトを進める「現場」で行っているPM育成活動について投稿します。もちろん、 電通 総研に興味のある社会人の方も大歓迎です。 前編では以下を説明しました。 電通 総研にはプロジェクトマネージャー(以降、「PM」)を育成する文化があること 会社全体での取り組みのほか、開発の現場でPM育成活動が行われていること 私自身がその活動をしたこと 今回の後編では、私が資料化して社内共有した「PMノウハウ」 の概要と、PMに求められるスキルを紹介します。 1. 私が社内で共有した「PMノウハウ」の概要 1-1. PMの役割 1-2. マスタースケジュール・成果物体系 1-3. PM活動の難所 1-4. その他 2. PMに求められるスキル 2-1. タレント・トライアングル 2-2. 私の考える「PMに求められるスキル」 ①踏み出す力 ②言語化能力 ③論理的思考能力 ④言葉のキャッチボール能力 ⑤もう一歩踏み込む力 おわりに 1. 私が社内で共有した「PMノウハウ」の概要 私がどのような「PMノウハウ」を資料化して所属部署(コミュニケーションIT事業部)に共有した、テーマ別の概要を一部紹介します。具体的なノウハウそのものには触れませんが、"どのようなことが書いてあるか" のイメージは沸くでしょうか。 1-1. PMの役割 「1-2」以降の説明の前提となる、一般的な用語や 電通 総研に特有の情報を資料化しました。 ちなみに、 電通 総研では「いわゆるPM作業」に留まらず、業務・ITスキルの領域に深く踏み込むPMが多い印象です。私は「業務 スペシャ リスト → PM」というキャリアを歩んだこともあり、「業務に強いPMスタイル」と言えるかもしれません。プロジェクト規模・体制にはよりますが、PMを担当しつつ、私自身が設計書の作成・レビューも行うことが多々あります。 ※ ちなみにここで言う「業務に強い」というのは、下記を意味しています。 顧客業務の内容・背景・意図を早期に理解して、プロジェクトメンバーと技術リスク・持っていきたい方向性などを認識合せし、顧客と仕様調整して設計に落とし込むスキルがある 上記をふまえたテスト・移行の方針策定、および テスト設計・移行設計ができる ※ 電通総研の社員紹介ページ も、ぜひご参照ください。 <コンテンツ概要> PMとは ※ 前編 をご参照ください 電通 総研におけるPMに至るキャリア 電通 総研では入社後からPMに至るまで、どのようなキャリアを歩むことが多いか。 プロジェクト体制の組み方 社内・パートナー会社さんのメンバーを集めて、どのように体制を組むか。 PMの具体的な作業内容 PMが担当する成果物・作業のラインナップと、PMと各メンバー(チームリーダー等)との役割分担。 例)要件定義工程では、「マスタースケジュール、テスト計画書、移行計画書」をPMが作成する。 1-2. マスタースケジュール・成果物体系 プロジェクト個別の事情を考慮してスケジューリングをします。そして、各工程(要件定義・設計・テスト・移行等)で何を成果物とするかを定め、計画的に作業を進めます。 <コンテンツ概要> マスタースケジュールの作成方法 マスタースケジュールは、プロジェクトの全体スケジュールのこと。 大きいレベルでのスケジュール管理をマスタースケジュールで行い、詳細作業(数100~1,000程度がよく見られる)を「 WBS (Work Breakdown Structure)」で管理する。 作成のためのテクニックはあれど、一番大事なことは「開始から終了まで、このスケジュールで進められそう」という感覚を持てるまで考え抜き、それをスケジュールに反映すること。 成果物体系の作成方法 成果物体系は、各工程の成果物を洗い出し、成果物間の関係性を示すもの。 基本的には、「その工程で何を作成すれば、 ステークホルダー 間で必要な認識合せができ、後続工程のインプットが完成するか」を考える。 1-3. PM活動の難所 私が難しいと思うPM作業に、「要件定義」・「テスト計画」・「移行計画」があります。若手PMはまずこの3つの壁にぶち当たるので、その壁を乗り越え易くするためのノウハウをまとめました。実務経験が無いと理解しづらい内容なので、ここでは簡易な説明に留めます。 <コンテンツ概要> 要件定義の進め方 要件定義の「開始前準備・実施中・終盤」それぞれの進め方 例えば「終盤」では、”スコープ定義” という大事なプロセスの進め方を紹介 テスト計画・移行計画の作成方法 計画段階で決めるべきことや、決めるための考え方。 移行タイムチャートの作成方法 お客様・ 電通 総研・その他 ステークホルダー (関係ベンダーさん等)の各移行作業の前後関係をどのように整理してタイムスケジュールに落とし込むか。 ※ 移行とは プロジェクトで開発・テストしてきたプログラムや準備したデータ等を、システムの本番環境に反映する作業のことです。本番移行が完了すると、開発してきた新規機能をエンドユーザが利用できるようになります。 本番移行は限られた時間内に完了させる必要があるため、あらかじめ各作業の前後関係を明確化したタイムチャートを作成して作業の予定・実績時刻を管理します。また、本番移行前に移行リハーサルをして、作業手順や移行データに不備が無いかチェックし、作業時間を計測することで、本番移行を時間通り正確に進められるようにします。 1-4. その他 工程共通のPM活動である「コミュニケーション管理」・「課題管理」・「ドキュメント管理」や、本稼働後の「保守運用」、PMが関わる「契約・手続」についてもノウハウを整理しました。ここでは説明を割愛します。 2. PMに求められるスキル 2-1. タレント・トライアングル PMに求められるスキルは、PMI(Project Management Institute)、IPMA(International Project Management Association)、 IPA ( 情報処理推進機構 )等のPMに関する団体が定義しています。例えばPMIでは、「プロジェクトマネジメントに携わる人にとっての理想的なスキルセット」として、 タレント・トライアングル を提唱しています。 私が行ったPM育成活動は、 暗黙知 だった「Ways of Working」および「Power Skills」を実践的な 形式知 にし、それらをメンタリングにより強化する活動、という位置付けでした。 トライアングル要素 説明 Ways of Working プロジェクト管理手法などのPMに関するテクニカルスキル。 Power Skills リーダーシップなどのヒューマン系のスキル。ソフトスキルと呼ばれることもある。 Business Acumen ビジネス感覚や業務知識。 ※ PMIのページから画像を引用しています 2-2. 私の考える「PMに求められるスキル」 さて、ここからは「イチ現場担当者(私個人)の意見」として捉えてください。 電通 総研の新卒採用の方針とすり合わせなどはしていません。 以下のとおり、PMに求められるスキルの1つである「Power Skills」について、学生さんに伝わる言葉で表現することにトライしてみました。 大前提として①が必要であり、そのうえで②③④が求められます。さらに⑤を発揮することで、もうイチ段高い②③④が発揮される、と考えています。なお、②③を除き、一般用語ではありませんのでご注意ください。 では、それぞれのスキルがどういうものかを見ていきましょう。 ①踏み出す力 自分が分からないこと・未経験のことを「やってみる力」、未知のことに「飛び込む力」、案件・組織等の「壁を越える力」です。明るく前向きであろうとするマインドや、できない理由を探すのではなくどうやったらできるかを考える思考特性が重要です。 入社後はぜひ、所属の「部」という枠組みを超えて知り合いを増やし、話しやすい関係性を築きましょう。それが、“会社には味方がたくさんいる” という気持ちに繋がり、「踏み出す力」への追い風となります。 ② 言語化 能力 事実・自分の考えを、プラス・マイナス無く、ありのまま・誤解なく伝える力です。文章・口頭説明のいずれにも求められます。日常でも「説明が難しいこと」はあると思いますが、諦めずに 言語化 に取り組んでみてください。また、判断が分かれる話題になった時に、「自分は〇〇がいいと思う。なぜならば△△だから」というように、自分の考えを明確にしてください。それがこの力の強化に繋がります。 ③論理的思考能力 前提・事実・考え 等を、分かりやすく組立てて説明する力です。例えば、お客様との限られた会議時間で様々なテーマを扱いますので、急に「〇〇ですか?」と問いかけても、「何の話?」と言われてしまいます。したがって、情報を整理して筋道立てて認識合せする必要があります。 論理的思考能力は、他のスキルよりも書籍・研修が多く存在するため、業務外でも学びやすいと言えるでしょう。 ④言葉のキャッチボール能力 相手の理解度を確認しながら話したり、相手の質問を理解して噛み合った返答をしたりする力です。会議前の準備段階で考え抜き、「こう説明したらこういう意見が出てきそうだな」と想定問答を考えておくことで、短時間に有意義なコミュニケーションを取れます。 ちなみに、コロナ禍を経てリモート会議が増え、相手の表情が読み取れなくなったので、「ここまでの説明でご不明点・ご意見などはありますか」といった意識的な問い掛けで理解度を確認する重要性が高まりました。 ⑤もう一歩踏み込む力 「答えらしきモノ」を得た安堵・小さな達成感を乗り越えて、あるいは会話に登場した「思考停止ワード」で止まらずに、もうイチ段階 深掘り、具体化して、考え・話し・聞く力です。例えば「お客様に確認して得られた回答に基づき、後続作業(例. 設計作業)を進められるか」を考え、不足があればさらに踏み込んで確認します。 必要なことを突き詰める・深掘りする「粘り強さ」とも言えそうですね。スキルというより、行動特性と呼べるかもしれません。 おわりに 前編 ・後編を読んでいただき、「PM」という仕事の解像度が少し上がったでしょうか。また、「PM」をやってみたくなりましたか。 誰しも、初めてPMを担当する時は不安がいっぱいです。でも、 電通 総研には勉強する材料はたくさんありますし、フォローしてくれる先輩もたくさんいます。お客様に説明予定の資料に不備があれば、事前にチェックして改善できるよう寄り添ってくれることでしょう。お客様への説明が言葉足らずならば、あなたの横にいる先輩が「補足ですが、・・」とフォローしてくれることでしょう。 PMという仕事は簡単ではありませんが、だからこそやりがいがあり達成感を得られる仕事だと思います。 私は、方針・計画・仕様等の合意形成の舵取りをしてプロジェクトを推進していくという、「大きなものを動かす感覚」を持てることに、PMとしてのやりがいを感じます。様々な作業における進め方の自由度が高いので、自分の考えを詰め込んだ ”たたき台" で道を切り拓き、関係者と認識を合わせて、慎重にまたは大胆に進めるプロセスは "腕の見せ所" と思って取組んでいます。そして、お客様との協同作業をスムーズに進められた時、課題が解決した時、システムが無事にサービスインした時、メンバーに感謝しながら達成感を噛み締めます。 電通 総研でPMをやってみたいと思った方は、ぜひ下記もご参照ください。 私たちは同じ事業部で共に働いていただける仲間を募集しています! みなさまのご応募、お待ちしています! 株式会社 電通総研 新卒採用サイト 株式会社 電通総研 キャリア採用サイト 執筆: @kaneko.taichi 、レビュー: @miyazawa.hibiki ( Shodo で執筆されました )
こんにちは、X イノベーション 本部の 米久 保です。 こちらは、 電通 総研テックブログ アドベントカレンダー 2024の12月10日の記事です。 はじめに 認知負荷 認知負荷理論 ソフトウェア設計と認知負荷理論 認知バイアス エラー まとめ 参考文献リスト はじめに ITエンジニア同士で会話するときに、「メンタルモデル」や「認知負荷」などの 認知科学 に由来する用語をよく耳にするようになりました。ソフトウェア開発は結局のところ人間によって行われる社会的な営みなので、人間の心のクセ、つまり認知特性に注目することはその活動をより良いものにする上で重要です。 デザインの領域では人間中心デザイン(Human-centered design, HCD)に代表されるように、人間の認知特性を考慮に入れたアプローチが主流となっており、ユーザーのニーズ、能力、行動に合わせたデザインを行います。 ソフトウェアの品質は外部品質と内部品質とに分かれます。外部品質は性能や使い勝手のようにユーザーが利用時に認識できるもので、内部品質は保守のしやすさのようにユーザーには見えないものです。実行時の実体としてのソフトウェアは、UIを通してその存在を知覚します。ソフトウェアの利用者に対して提供するUIとUXは、デザイナーが中心となりユーザーのニーズを満たすようにデザインします。 さて、ソフトウェアの物理的な実体である ソースコード のユーザーは誰でしょうか。それを書いたり読んだり変更したりする開発者ですね。 内部品質はソフトウェアが素早くかつ持続的に価値を提供するために非常に重要 ですが、 ソースコード を日々取り扱うユーザーである開発者に焦点を当てて考える必要があるでしょう。 そのためには人間の認知特性を理解した上で、ソフトウェア 開発プロセス や個々のアクティビティをデザインする必要があります。この記事では、「認知負荷」「 認知バイアス 」「エラー」を題材に、ソフトウェア開発活動の中でも特に設計にどう活用できるかを考察します。 認知負荷 認知負荷理論 認知負荷理論は、オーストラリアの 教育心理学 者、ジョン・スウェラー氏によって1980年代に提供されたものです。脳内の情報処理を行う作業場所、ワーキングメモリに対してかかる負荷のことを認知負荷と呼びます。ワーキングメモリの容量は大きくないため、認知負荷が高すぎるとオーバーフローを引き起こしてしまいます。ワーキングメモリをいかに効率に使うかが重要です。 認知負荷理論において、認知負荷は3つに分類されます(参考文献1,2)。 分類 説明 課題内在性負荷 対象そのものが本質的に持つ複雑さによる認知負荷 課題外在性負荷 対象とは直接関係しない、外的要因による認知負荷 学習関連負荷 学習のための認知活動で発生する負荷 課題内在性負荷は、対象の本質的な複雑さに由来するものなので、その総量を減らすことは基本的にできません。「分割して統治せよ(Divide and conquer)」に従い、一度に取り扱う範囲を小さくすることは可能でしょう。 課題外在性負荷は、本来不必要な複雑さが持ち込まれたことによる負荷であり、ノイズと言えます。可能な限り除去するべきです。 学習関連負荷は対象を理解し、知識を活用して問題解決のための推論を行っていく上で適切な認知負荷と言えます。この負荷に割り当てるワーキングメモリが不足すると、「脳がパンクしてフリーズ」してしまうのです。 ソフトウェア設計と認知負荷理論 ソフトウェアが解決すべき問題は、たいてい複雑です。複雑なものを複雑なまま取り扱うのは大変なので、分割して小さくすることが大切です。 ただし、やみくもに分割すればいいというわけではありません。分割の仕方を間違えると、要素同士が複雑に絡まりあい、全体として「complicated」な構造になってしまいます。要素の数は多く多層的であっても、一貫性のある「complex」な構造を目指して分割する必要があります。 そのような構造化を行うためには、モジュールや コンポーネント に割り当てる責務を明確にした上で、それを実現するために必要な関連性の高いデータや操作を一箇所に集めた「高凝集」な設計が大切になります。そして、他の責務を担うモジュールや コンポーネント とのやり取りは明確に定義されたインターフェースを介した「 疎結合 」な設計とし、開発者が一度に取り扱う関心事を限定すべきです。 関心事の分離により、一度に取り扱う課題内在性負荷を適切なサイズにコン トロール できます。では課題外在性負荷についてはどうでしょうか。負荷を軽減するために考慮すべき設計原則やプ ラク ティスをいくつか挙げます。 方針と詳細の分離 インターフェースをうまく使い、方針と詳細を分離すること、依存性の向きを詳細から方針側へと向けることは重要です。データベース製品や物理的なテーブル構造、O/Rマッパーの実装制約といったテク ノロ ジー の詳細に関する知識が、 ビジネスロジック に入りこまないようにするべきです。 適切なネーミング 関数名や変数名は一貫性があり、明確に意味を読み取れるネーミングを心がけましょう。プロジェクトで定められた規約に沿うことも重要です。一貫性のないネーミングはノイズとなり、読み手に混乱を与えます。 抽象度の統一 publicメソッド、そこから呼ばれるprivateメソッド、さらにその先のprivateメソッド、と構造化する際は各メソッド内に記述する処理の抽象度をそろえることが大事です。異なる抽象度の処理が混じっていると、それに気を取られて認知負荷が高まってしまいます。 開発者にかかる認知負荷をできる限り低減することが重要です。 認知バイアス 認知バイアス とは思考の偏りを表す心理学の用語で、この存在によって人間はしばしば非合理な選択や判断をしてしまいます。近年注目されている 行動経済学 は、 認知バイアス に基づく人間の合理的でない行動が経済活動や社会現象に与える影響を研究する学問です。 認知バイアス によるトラップは日常生活のさまざまな場面に潜んでいますが、ソフトウェア開発においても例外ではありません。 ある仕様を満たすソフトウェアを実装するとしましょう。皆さんは、コードを書き始める前にどの程度設計を行いますか? 20年以上前に UML が流行した当初、詳細なクラス図やシーケンス図、ステートチャート図などを作成することが多くの現場で推奨されていました。コーディング前に事前に完全な設計を行うことをBDUF( Big Design Up Front )と呼びます。 物事は想定通りには運ばないものです。実装を進めるにつれ、考慮漏れ、エッジケースなどさまざまな問題が発見されます。その場しのぎの継ぎ接ぎの対応を積み重ねていくと、設計はどんどん歪んでいきます。 リファクタリング の必要性は感じるものの、納期のプレッシャーに負けてそのままコミットしてしまった、という経験は誰しもあるのではないでしょうか。 このような現象には、どのような 認知バイアス が影響しているでしょうか(参考文献 3)。 計画錯誤 過去に失敗をしていたとしても、今回はうまくいくだろうという楽観主義によって、甘い計画を立ててしまうバイアスを計画錯誤と言います。今回のケースでは、事前に設計をしたのだからうまく実装が進められるだろう、と楽観的に考えてしまうことです。 確証バイアス 自分の信念や仮説を支持する情報ばかりに注目し、逆の証拠を軽視したり無視したりしてしまうのが確証バイアスです。事前設計の不完全さを示す兆候があってもそれを無視し、軌道修正することなく実装を進めていってしまいます。 サンクコスト効果 このまま続けたら損を出すことがわかっているのに、これまで注ぎ込んだ労力や費用を考えるとやめることができない現象をサンクコスト効果と呼びます。設計のまずさに気づいてはいるが、今さら引き返すことができないという状態です。 このような 認知バイアス のトラップを避け、設計をうまく進めるにはどうしたらよいでしょうか。 まず、事前に完全な設計をできるという考えを捨てることです。ベテランほど、解くべき問題に対する完成形がある程度イメージできてしまうので、それに 固執 しがちです。しかし、それはあくまで仮説としての設計と捉えましょう。 また、 テスト駆動開発 (TDD)のプ ラク ティスも役立つでしょう。シンプルなテストケースから始めて、オンデマンドで漸進的に設計を進めるので、途中の軌道修正も容易です。Red - Green - Refactor のサイクルで頻繁に リファクタリング の機会が訪れるため、サンクコストからも解放されます。 エラー 人間はエラー(誤り)を起こします。D.A.ノーマン博士は、人間がエラーを起こす原因を理解しその原因が少なくなるようなデザインをすること、生じたエラーの発見と訂正が容易となるデザインをすることの重要性を説いています(参考文献 4)。また、エラーを以下のように分類しています。 エラー ミステーク:間違ったゴールまたはプラン形成による、不適切な行為 ルールベース:誤った規則に従ってしまう 知識ベース:誤った知識や不完全な知識により問題を間違って捉えてしまう 記憶ラプス:忘却によりゴール設定やプラン設定を誤ってしまう スリップ:意図とは異なる行為をしてしまうこと 行為ベース:行為を間違えてしまう 記憶ラプス:行為を忘れてしまう デザイン上の工夫としては、たとえば以下の方法が挙げられています。 制約 物理的な制約によってユーザーが行える操作に制限を加え、エラーを起こりにくくする。 アンドゥー システムにおいてアンドゥー(undo)コマンドを提供し、行った操作を元に戻せるようにする。 さて、ソフトウェア開発に目を移すと、 JSTQB の シラバス には以下のように書かれています(参考文献 5)。 人間はエラー(誤り)を犯す。そのエラーが ソースコード や他の関連する作業成果物の欠陥(フォールトまたはバグ)となる。 ソフトウェア開発においても、人間のエラーの防止、発見、訂正を行うための仕組みが重要と言えるでしょう。具体例としては以下が挙げられます。 リンタ(Linter) ESLintのようなリンタを導入することで、プログラム上の誤りを早期に発見し訂正できます。構文エラーの他にも、未使用の変数など将来のエラーにつながる問題点を事前に発見できます。 テスト駆動開発 Red - Green - Refactor のサイクルで小さな振る舞いを少しずつ実装していくため、誤った実装をした瞬間にそれを検出し、訂正できます。 契約による設計(Design by Contract, DbC ) 契約による設計は、 コンポーネント の提供者とクライアントとの間で、双方が満たすべき条件を明確に表明するという設計手法です。具体的には、事前条件、事後条件、不変条件を明確に定義し、それが満たされない場合には処理を中止します。 他の具体例を見てみましょう。以下のTypeScriptのサンプルコードでは、 Project オブジェクトを生成しています。 const departmentId: number = 1 ; const managerUserId: number = 2 ; // of(departmentId: number, managerUserId: number, projectName: string): Project // 引数の順番を間違えてしまっている! const project = Project.of(managerUserId, departmentId, 'Sample Project' ); of メソッドの引数には、本来「部署ID」「PMのユーザーID」の順番に指定する必要があるのですが、その順番を間違えてしまっています。どちらのIDも number 型であるため、 コンパイル 時にこの誤りは検出できません。 筆者は過去に実際の開発プロジェクトにおいて、このような誤りを見たことがあります。不幸なことに、開発者がテストで用いた部署とユーザーとがどちらも同じID値であったために欠陥検出ができずに、後工程まで持ち越されてしまいました。(これは、テストデータ設計においても誤り防止の観点が必要であることを示しています)。 このような誤りは、型チェックにより検出をしたいものです。 number のようなプリミティブ型を直接使うのではなく、 ドメイン プリミティブ型を導入しましょう。 const departmentId = new DepartmentId( 1 ); const managerUserId = new UserId( 2 ); // of(departmentId: DepartmentId, managerUserId: ManagerUserId, projectName: string): Project // 以下はコンパイルエラー const project = Project.of(managerUserId, departmentId, 'Sample Project' ); ※脇道に逸れてしまいますが、TypeScriptの型は構造の一致によって判定されるため、単純に id をプロパティに持つクラスを定義しただけでは上記のような型チェックをかけることができません。ブランド型という実装テクニックを使って、 DepartmentId と UserId が異なる型と判定されるようにする必要があります。 export class DepartmentId { private _departmentIdBrand !: never ; constructor ( public id : number ) {} } export class UserId { private _userIdBrand !: never ; constructor ( public id : number ) {} } アンダースコアのついたプロパティはダミーであり実際に使用されることはありません(そのため never 型で定義します)。このプロパティの存在によって構造に差異が生じ、異なる型と見なされるわけです。 このように、人間が起こしてしまうエラーに対して、仕組みとして対処できないか検討してみるとよいでしょう。 まとめ 人間の認知の仕組みに注目し、開発者をユーザーと捉えて人間中心の設計を行うことは、開発者体験の向上に繋がります。開発者体験は内部品質の向上に寄与し、ひいてはソフトウェアが顧客やユーザーに提供する価値が増大するのではないでしょうか。 参考文献リスト 『 認知負荷および認知負荷理論 (Cognitive Load Theory) をもう少し正確に理解するための心理学研究・知見の紹介 』(Zenn記事) 『 「快適な」学習のために〜認知負荷理論入門 』(note記事) 『 イラストでサクッとわかる!認知バイアス 誰もが陥る思考の落とし穴 』(池田まさみ、森 津太子、高比良美詠子、宮本康司 プレジデント社) 『 誰のためのデザイン? 増補・改訂版 認知科学者のデザイン原論 』(D.A.ノーマン、 新曜社 ) 『 テスト技術者資格制度 Foundation Level シラバス 』( JSTQB認定テスト技術者資格 認定委員会) 執筆: @tyonekubo 、レビュー: @kobayashi.hinami ( Shodo で執筆されました )
これは 電通総研 アドベントカレンダー 2024 の6日目の記事です。 はじめに 造語/隠語/謎用語とは? 実物たち ライトなもの ガッチャンコ グレーアウト ブランク 穴をあける 打鍵 暗号化 ~感 道なり 行って来い 正式用語だが注意を要するもの マイクロサービス 手順・ガイド 初見殺し 入れポン出しポン ガサーっと持ってくる 無理矢理突っ込む 謎の強調 いまいま ほぼほぼ フルフル すぐすぐ 終わりに はじめに みなさんこんにちは。 電通 総研 金融ソリューション事業部 エンジニアリングオフィスの水野です。 今回は、 システム開発 において使用される不思議な言葉たちをご紹介します。 もしかしたら弊社固有ワーディングかもしれませんが、「そんなのあるんだ」と緩く捉えていただければ幸いです。 造語/隠語/謎用語とは? 同音異義語 や、世間一般での使われ方とは大きく異なる単語、言い回したちです。 意味が分かりにくいのもそうですが、設計・実装作業に支障をきたすレベルの認識齟齬を生むこともあり、相当に侮れません。私はその手の用語が出てきたらまず疑ってかかり、詳細を聞き出すことを心がけています。 実物たち 早速実物たちを紹介します。 あくまで使われることがあるというだけで、普段のコミュニケーションで日常的に使用している訳ではないことをお伝えしておきます。 ライトなもの ワード自体短く、説明も軽量なグループです。 注意が必要な用語は ブランク ですが、昨今聞くことは減ってきたのでまず大丈夫だと思われます。 ガッチャンコ 大抵は集合演算の結合です。つまりJOIN。理由は不明ですが、内部結合を指すことが大半です。 「ガッチャンコする」という動詞で使うことが多いです。 グレーアウト UI部品を非活性にするという意味です。 Webページの場合は CSS を適用するなど実装方法はいくつかありますが、disabledにするということですね。 例えばボタンだったら、クリックやSubmitなどのアクションを受け付けなくしつつ、見た目も灰色など、反応しなそうなUIに変えることです。 認知負荷は高くなく、認識違いも発生しにくいですが、是非普通に「非活性化」と言っていただきたいです。 尚、 活性化 を ホワイトイン とは言いません(少なくとも私の周辺では)。 ブランク Nullと文字列型における空文字を区別しない使い方で、是非ともやめていただきたいです。 口語でもブランクブランク連呼されることがありますが、大体は入力値無しのことを言っています。 「要件定義や基本設計では意識しないで良いのでは?」という話はあるのですが、であれば「値無し」で良いです。 汎用機時代バリバリの方は、スペースや半角スペースと言うこともあります。固定長時代の名残り? 昔、設計書に スペース と書いてあったのでvarchar型のDBカラムにスペースを設定したら、バグって且つ設計した人に怒られました。 穴をあける 閉じたネットワークで、特定の プロトコル /エンドポイント( IPアドレス or ドメイン 、ポート番号) だけインバウンドを許容するという意味です。特定の宛先だけアウトバウンド通信を許容する際にも使用され、 穴あけ という名詞形もあります。 パブリッククラウド 上のアプリケーションでは頻繁に使われる印象です。 打鍵 テストケースをただひたすらに実施することです。 モンキー試験と呼ばれる、とにかく何でもかんでも入力する体を張ったケース消化の意で使う流派もありますが、私の周辺では粛々とケースをこなす意味で使っています。 語源としては、タイプライターの鍵盤を叩くであったり、銀行業務の打鍵(システムに諸々入力する)であったりと諸説あるそうですが、何にせよテストケース実施にそれっぽい単語を割り当てただけです。 暗号化 ハッシュ関数 を適用して ハッシュ値 を得ているケースの誤用です。頻出ではなく、本当に稀です。 新規に作る場合はアーキテクトの裁量で処理方式を決めれば良いのですが、既存システムで 実装済 みの場合には注意を要します。 「パスワードを暗号化している」と聞いたものの、実際はパスワードから得た ハッシュ値 を突き合わせていただけだったというのは、過去数回ありました。 ~感 いろいろな名詞と組み合わせて使います。 スケジュール感 や 費用感 、 コスト感 (費用感と同じ)、 期限感 などです。 これはかなり分かりやすいメッセージで、「この内容で合意したわけではないです。後で変わることもありえますし、責任は負えません」と言う意味が込められています。実際、決裁権も裁量もないのかもしれません。 であれば、 仮スケジュール や 想定スケジュール 、 概算費用 で良いのではないでしょうか。~感を付けるのは、更に確度が低く自信がない、なんらかの不安めいた気持ちのアピールなのかもしれません。 「大まかに捉えたいのです」と言うなら、やはり想定スケジュールが適しているでしょう。もっと粗い粒度で知りたいのだ等ありそうですが、そのあたりを汲み取るための労力が無意味なので、想定スケジュールで良いです。 スケジュール 、 工数 で会話したいです。感と言われても……感です。 ちなみに、スケジュールかんについては、 スケジュール観 が正しい漢字かもしれません。数回見たことがあります。 感・・・・感。 道なり 「道なりのスケジュールで」という使い方が多いです。 「道なりに進む」とは、曲がったりせずまっすぐ進むと言う意味なので、想像通り何も条件を変更せずに真っすぐ進んだ場合という意味です。 オンデマンドの割り込みが無い前提、且つ稼働を増やさずに今までの生産性を保った場合のスケジュール が近しい表現でしょうか。とは言え、具体的な期日や従前のスケジュールと一切変更がないかは確認すべきです。 道なり と言うと、 アントニオ猪木 氏の引退スピーチである、 「この道を行けばどうなるものか、(中略)迷わず行けよ。行けばわかるさ。」 が思い出されます。このように、 「不安はあるが新しい一歩を踏み出そう。迷わず行こう、行けばスケジュールが分かるよ。」 的な意味合いなのかもしれません。 ちなみに、上記の猪木氏の言葉は商標登録(文字商標)されています。 行って来い 金融業界では普通に使われます。売りと買いで相殺して、差引で金額の差が出ないことです。 例えば、「影響調査漏れで 工数 増ですが、類似機能の実装を効率化したので、行って来いで同じ 工数 です」のような使い方をします。 正式用語だが注意を要するもの マイクロサービス 本来のマイクロサービス アーキテクチャ でないケースがあります。これは、私の狭い観測範囲だけかもしれません。 API エンドポイントを持つコンテナは複数ありつつ、 RDB が中央に一つというパターンが多いです。 例えば以下のような構成で、これはテーブル変更が発生すると全てのサービスが止まるし、データベースがSPOFになります。マイクロサービスの良さを殺し、デメリットを強調していると言っても過言ではないでしょう。 API 毎に トランザクション 境界があると、結果整合性なり補償 トランザクション なりが必要になってきますが、このケースは大体最後に呼ぶ API だけで トランザクション が完結しているので、表立って問題にはならないです(それもおかしい)。 無理矢理コンテナ分けてみました系が多く、モジュラー モノリス で API エンドポイントを複数持たせる方が良いことも多いです。 サービスベースアーキテクチャ が最も近いので、そのように呼んで欲しいです。 手順・ガイド マイクロサービスと同じように一般的な用語なのですが、 どこまで求められているか が人によってバラバラなのが手ごわいポイントです。 そのドキュメントを見て手順に従えば、誰でもモノが作れる レベルが期待されることもあります。 手順通りにやれば、プログラミングスキルがなくても実装できるというのなら、自動生成など別のアプローチを選択すべきです。 フレームワーク やライブラリの使い方では、 GitHub や公式ページに記載されている内容を求められるケースもあり、マニュアルを読んでくださいという気持ちになります。ガイド提供側とされる側には大きな前提知識の差があるので、最初Draftを作って粒度を確認した方が良いでしょう。 書き過ぎても実装のアップデートに追従していくのは大変だし、そのうちドキュメントとの乖離が発生するので、コンセプトや設計指針など、なるべく普遍的な部分に留めたさがあります。 しかし、「ガイドが無い(あるいは薄い)から実装効率が悪かった」と言われることもあり、バランスが難しいです。 初見殺し 難度が高く、初見では意味不明なグループです。 暗黙の アーキテクチャ 特性が潜んでいたり、非機能要件のリスクが隠れていたりと、注意を要するのもポイントです。 ミステリアスアンニュイワーディングに慣れ親しんだ歴戦の猛者であっても、うっかり足元をすくわれかねない危険性の高さを孕んでいます。 入れポン出しポン 意味するところは 「データの更新も選択も単純で簡単、UIも簡便」 です。 暗黙的に、難易度が低くて 工数 がかからず、リスクも低いというメッセージがあります。画面とテーブルが1対1で紐づいている、マスターメンテナンス画面などでよく使われる印象です。 仮に上述の画面であれば正確に表現すべきで、例えば この顧客メンテナンス画面は、テーブルと1対1且つ画面項目とテーブル項目が完全に紐づいており、型や属性も同じです。 更に、月に1回程度しか使わない上に、例えバグで止まっても手動投入でシステム運用が回るワークアラウンドがあります。 SQLも単一テーブルへの挿入・更新・削除のみ、選択は1テーブルからで、述語は2, 3項目のフィルタ条件があるのみです。 データ量は最大100件程度、既存システムからのデータ移行もありません。 など、具体的に言う方が良いです。蓋を開けてみたら、選択時は他のテーブル含め非常に複雑なクエリが要る、データ量が大量などあるかもしれません。ある一項目だけ、取得時に超難儀な計算をやっていて、それは共通 コンポーネント が無いと実装出来ないなどあるかもしれません。または、1画面1テーブルで超単純だが、秒間 20,000ユーザのリク エス トを100ミリ秒以内に捌く必要があるかもしれません。 「かもしれない」 のですが、入れポン出しポンという用語で真実が分からないのは誰も幸せでは無いです。プロジェクトの近しいメンバーと、公式でない緩い トーク で共通認識の下で使うのは良いですが、公式の説明の場では避けるのが賢明です。 ガサーっと持ってくる 専らデータベースからデータを選択する際に用いられます。 「ガサっと」や「バサっと」など変形バージョンもあり、なかなかにバリエーションは多彩です。 意味は、「大量のデータを複雑な述語を伴わずに抽出する」 という表現が近しいです。 RDB を用いている場合、 SQL が単純だとしても、大量のデータを選択するのでパフォーマンスや アプリケーションサーバ のコンピューティングリソースが問題になることがあります。 特にパフォーマンスが顕著で、具体的な集合演算は何か、1レコードのサイズはどのくらいか、実際何行くらいを抽出するのかを具体的に確認することを推奨します。「ガサーっと持ってくるだけ」 と言いつつ、述語に関数を使っていたり、実は結合をしていたり、重い集合演算のケースもあります。 うっかり放置していると後で痛い目に合うので、この表現が出たときには一歩踏み込んで内容を確認しましょう。 無理矢理突っ込む これも亜流や派生形はありますが、「 ビジネスロジック でデータを加工して永続化する(大抵は挿入)」 という意味です。 無理矢理 を付ける意味は大抵無いのですが、業務ロジックを強引に適用する(数値を丸めないといけなかったり、デコードが必要だったり、演算が必要だったり)みたいなニュアンスの時に付与すると理解しています。 「突っ込む」もレベルは高いのですが、これは SQL のINSERT文など、データストアへの追記決め打ちでOKです。 謎の強調 これはIT業界固有ではないかもしれません。同じ言葉を重ねて意味を強調しているグループです。 いまいま 「いまいま」単体だとjust nowというだけなのですが、他の言葉を修飾しているケースで問題が顕在化します。 「いまいま必要ない」と言った場合、例えば以下の意味のどれかを、コンテキストや背景から判断しないといけません。 必要だと思うが、今は作る計画はない。且つ、いつ頃作るかも不明 言ってはみたものの未来永劫必要ない、きっと 次の基盤更改のタイミング (3年や5年)までは必要ない 向こう数年は必要ない 向こう1年は必要ない 向こう数か月は必要ない あたりです。 「それが何なのですか?」という話ですが、5か6なら近い将来を見越して アーキテクチャ やデータモデルに拡張性を持たせるべきかもしれません。 1.は突っ込んだ ヒアリ ングが必要かもしれません。「この瞬間は不要」という意味なのですが、であれば判明している期限やタイミングの把握に努めましょう。決まっていないなら、決まっていないことが分かれば良いです。 3.であれば、数年後の飯のタネになる仕込みや、別ソリューションの提案が可能かもしれません。 うっかり口癖で「いまいま~」はお控えいただきたいです。 ほぼほぼ 「ほぼ」が仮に8割方みたいな確率だとすると、「ほぼほぼ」は9割9分9厘くらいの確率なのでしょうか。 「可能性としてはまず有り得ないが、システムに絶対はないので、超微少な確率を表現した。ただし、リスクは皆無なのでバッファは不要」 みたいなケースで使えそうです。 ただ、そこまで分かっているなら、普通に 言語化 したほうがきっと良いです。 フルフル 「フルフルで アサイ ンしています」などで聞きます。 Full (=100%) なのですが、これが120%になっているとかでしょうか? つまり、残業を見込んでいるということで、稼働時間は2割増しみたいな話かもしれません。 フルと同じ意味で「フルフルで稼働」 というなら、普通に「フル稼働」で良いのではと思います。 すぐすぐ ASAP みたいな意味と捉えており、「一刻も早く」の最上位形です。 「すぐなんです!! 本当にすぐ、今すぐ!!!」みたいな、何らかの気合い的なやつを込めた魂の叫びを表したいのだろうと思っています。 終わりに 今回は様々な摩訶不思議な用語の紹介でした。 これらの用語を組み合わせた応用バージョンもあり、例えば 「こちらは入れポン出しポンなので、エースのA君を高難度の本機能にフルフルでアテンドすれば、行って来いで道なりのスケジュール感です」 などの合体奥義です。 全ての表現に対し、厳密な解釈を可能にさせることは過剰です。その塩梅は、それこそ空気を読む世界です。 「どこまで厳密な表現を使うか」は一様の基準を持ち得ないので、「どの用語や言い回しを使ってはならないか」というDeny Listを作る方が有効だと思います。設計書など、一定の記述レベルが求められる際にご検討ください。 ここまでお読みいただき本当にありがとうございました。 私たちは同じチームで働いてくれる仲間を大募集しています!たくさんのご応募をお待ちしています。 データエンジニア システムアーキテクト 執筆: @mizuno.kazuhiro 、レビュー: @handa.kenta ( Shodo で執筆されました )
STEP1:ライブラリのインストール STEP2:APIキーの設定 STEP3:データセットのダウンロード STEP4:和訳処理 STEP5:トレーニングデータの作成 STEP6:ファイルのアップロード STEP7:ファインチューニングジョブの開始 STEP8:検証 ケース0:「あなたは何が得意ですか?どんな振る舞いをしますか?」 ケース1:「仕事でのプレッシャーが大きく、ストレスがたまり何をしても楽しめません。どうすれば良いでしょうか?」 ケース2:「人前で話すときに緊張してしまい、自分に自信が持てません。克服する方法を教えてください。」 ケース3:「恋人との関係に悩んでいて、彼女の機嫌が悪いです。どう接していいか分からなくなっています。」 検証結果について考察 注意事項 反省点 さいごに こんにちは。コミュニケーションIT事業部 ITソリューション部の英です。 普段はWebアプリや スマホ アプリの案件などを担当しています。あと、趣味でAIを勉強しています。 皆さんいかがお過ごしでしょうか。しばらく育休を取っていたので久々の執筆です。 久々の執筆が Advent Calendar 2024 です。よろしくお願いいたします。 さて、タイトルにもある通り、今回はAIを使ってバーチャルカウンセラーを作りましょう。 ユーザーからの深刻な悩み相談に対して、AIが心理カウンセラーとして共感したりアド バイス をしたりします。 本検証では、 counsel-chat デー タセット を利用しました。このデー タセット は、CounselChat.com の相談者と認定カウンセラーによる回答から成る高品質な オープンソース のカウンセリングデー タセット です。 バーチャルカウンセラーとしての振る舞いを学習させるにはちょうど良さそうですね。 作業の全体の流れはこんな感じです。 データの取得 ノイズの除去 ChatGPTで自然な日本語に和訳 和訳したデー タセット でト レーニン グデータを作成 ChatGPTでファインチューニング デモアプリに組み込む 会話してみる 最初からデー タセット が綺麗なので、前処理はシンプルです。 工夫したところと言えば、和訳処理にChatGPTを使ったところでしょうか。 他の 機械翻訳 サービスも試したのですが、やはりChatGPTの和訳精度がダントツに良いです。 また、自然な日本語でファインチューニングしないとAIの回答が不自然な日本語になってしまいます。 日本語対応のAIチャットを作るうえで、実はこれが鍵だったりします。 直訳を学習データにすると、AIがカタコトになります。 作成したモデルをチャットアプリに組み込んで、実際にカウンセリングを受けてみましょう。 STEP1:ライブラリのインストール !pip install openai pandas datasets STEP2: API キーの設定 API キーは事前に発行しておいてください。 環境変数 に設定します。 import os import openai import random import pandas as pd from datasets import load_dataset # OpenAI APIキーの設定 os.environ['OPENAI_API_KEY'] = 'sk-(ご自身で発行したAPIキー)' client = openai STEP3:デー タセット のダウンロード デー タセット をダウンロードして、先頭行を出力してみます。 事前に確認したところ、questionTextとanswerTextには中身が空のデータが存在したので、ここで除去しておきます。 また、和訳処理とト レーニン グコストを削減するためにデー タセット から50%をサンプリングします。 # CounselChatデータセットのロード dataset = load_dataset("nbertagnolli/counsel-chat") train_data = dataset['train'] # 有効なエントリのみをフィルタリング valid_data = [entry for entry in train_data if entry["questionText"] is not None and entry["answerText"] is not None] # データ量を50%に縮小(コスト削減のため) reduced_filtered_data = random.sample(valid_data, int(len(valid_data) * 0.5)) # reduced_filtered_dataの先頭の1行を表示 print(reduced_filtered_data[0]) デー タセット は以下の構成であることが分かりました。 カラム名 説明 questionID 質問の一意の識別子 questionTitle 質問のタイトル stringlengths_questionTitle questionTitleの文字数 questionText 質問の本文 stringlengths_questionText questionTextの文字数 questionLink 質問へのリンクURL stringlengths_questionLink questionLinkの文字数 topic 質問のトピック stringlengths_therapistInfo therapistInfoの文字数 therapistURL セラピストのプロフィールURL stringlengths_therapistURL therapistURLの文字数 answerText 回答のテキスト stringlengths_answerText answerTextの文字数 upvotes 質問に対するアップボート数 views 質問の閲覧数 STEP4:和訳処理 2024年11月時点ではGPT-4o mini(gpt-4o-mini)が最適でしょう。 以下の通り、プロンプト+英語の原文を渡して翻訳データを作成します。 ト レーニン グコストを抑えるために、長文は300文字以内に要約して トーク ン数を削減します。 # ChatGPT APIを使って日本語に翻訳する関数 def translate_to_japanese(text): response = client.chat.completions.create( model="gpt-4o-mini", #これを読んでいる読者の時代で最も最適なモデルを選択してください messages=[ {"role": "system", "content": "あなたはプロの翻訳家です。次の英文をとても自然で流暢な日本語に翻訳してください。もし、文章が長くなってしまう場合には全角300文字以内に要約してください。"}, {"role": "user", "content": text} ], max_tokens=500, temperature=0.6 ) return response.choices[0].message.content # 和訳処理 translated_data = [] total_entries = len(reduced_filtered_data) for index, entry in enumerate(reduced_filtered_data): question_text = entry['questionText'] answer_text = entry['answerText'] # 翻訳の進捗を表示 print(f"Processing entry {index + 1} of {total_entries}...") # ChatGPT APIを使って和訳 translated_question = translate_to_japanese(question_text) translated_answer = translate_to_japanese(answer_text) # 翻訳結果を表示 print(f"Translated question: {translated_question}") print(f"Translated answer: {translated_answer}") # 和訳データを保存 translated_data.append({ 'questionText': translated_question, 'answerText': translated_answer }) # 各エントリの翻訳が完了したことを表示 print(f"Entry {index + 1} translated successfully.") print("-" * 50) # 区切り線を表示 # 全てのエントリの翻訳が完了 print("All entries have been translated successfully.") STEP5:ト レーニン グデータの作成 Fine-turningに与える学習データを作成します。 userからの相談内容に対して、assistantが回答するやり取りをmessagesに含めて、jsonlファイルに加工します。 目視での確認用に csv ファイルにも落としておきます。 # CSV用のデータに変換 import json df = pd.DataFrame(translated_data) df.to_csv("translated_fine_tune_data.csv", index=False) # JSONLファイルに変換して保存 output_path = "translated_fine_tune_data.jsonl" with open(output_path, "w") as f: for entry in translated_data: messages = [ {"role": "user", "content": entry["questionText"]}, {"role": "assistant", "content": entry["answerText"]} ] json.dump({"messages": messages}, f) f.write("\n") print(f"和訳されたデータが {output_path} に保存されました。") STEP6:ファイルのアップロード 作成したファイルをOpenAIのサーバーにアップロードします。 from pathlib import Path # ファイルをアップロード response = client.files.create( file=Path("translated_fine_tune_data.jsonl"), purpose="fine-tune" ) file_id = response.id print(f"Uploaded file ID: {file_id}") STEP7:ファインチューニングジョブの開始 # ファインチューニングジョブの作成 response = client.fine_tuning.jobs.create( model="gpt-4o-mini-2024-07-18", #これを読んでいる読者の時代で最も最適なモデルを選択してください training_file="file-(STEP6で出力されたファイルID)" ) fine_tune_id = response.id print(f"Fine-tune ID: {fine_tune_id}") 最初にト レーニン グファイルのバリデーションチェックが走り、ファイルに問題がなければ自動的にト レーニン グに移行します。ト レーニン グが完了すると以下のような表示になります。(Succeeded) メールでも以下の通知メールが自動送信されます。 STEP8:検証 FlutterFlowで作ったデモアプリに組み込んでみます。 ベースモデル と ファインチューニングモデル に同じ相談をして振る舞いの違いを確認しましょう。 以下のようにモデルIDを埋め込みます。 相談内容は以下の3つで試します。 「仕事でのプレッシャーが大きく、ストレスがたまり何をしても楽しめません。どうすれば良いでしょうか?」 「人前で話すときに緊張してしまい、自分に自信が持てません。克服する方法を教えてください。」 「恋人との関係に悩んでいて、彼女の機嫌が悪いです。どう接していいか分からなくなっています。」 ケース0:「あなたは何が得意ですか?どんな振る舞いをしますか?」 まずはジャブです。相手の出方を伺いましょう。 よかった。ちゃんとセラピストでした。 ファインチューニングの内容が反映されていることが期待できそうですね。 では、さっそく比較検証に入りましょう。 ケース1:「仕事でのプレッシャーが大きく、ストレスがたまり何をしても楽しめません。どうすれば良いでしょうか?」 ケース2:「人前で話すときに緊張してしまい、自分に自信が持てません。克服する方法を教えてください。」 ケース3:「恋人との関係に悩んでいて、彼女の機嫌が悪いです。どう接していいか分からなくなっています。」 検証結果について考察 ファインチューニングモデルでは セラピー 、 セラピスト 、 フラストレーション など学習データに含まれる単語を駆使して回答している ファインチューニングモデルの方が 温かさ を感じる。ベースモデルは 冷たさ を感じる。ベースモデルは「○○しましょう。」で終わっていることが多く、ファインチューニングモデルは「応援しています!」や「頑張ってください!」のような 相談者を励ますような内容 が含まれる傾向にあった。心理カウンセラーとしての 振る舞い をAIが模倣できていると感じる。 ファインチューニングモデルの方が 自分と向き合う系のコメント が多かった。 ※すべて個人の感想です 注意事項 実際の開発案件での利用について :日本の 臨床心理士 や 公認心理師 と米国のLicensed Clinical Psychologist(ライセンス取得 臨床心理士 )では資格の基準が異なります。つまり、価値観の基準が違います。日本人向けの心理カウンセラーをAIで実現したい場合は、日本国内の心理カウンセラーと密に連携して、日本独自の 心理的 ・文化的背景を反映させたカウンセラーAIシステムを設計する必要があります。単に米国の方法論をそのまま適用するのではなく、日本人の価値観や行動様式を深く理解し、日本人ユーザーに最適化されたサービスを提供するのがよいでしょう。 MITライセンスについて :学習データに使用したcounsel-chatはMITライセンスで管理されています。開発で利用する際には、ライセンスの規約に従ってください。具体的には、複製、改変、配布、商業利用が可能ですが、 著作権 表示と免責条項を残す必要があります。詳細については右記のリンクをご確認ください。 counsel-chat MIT License Copyright (c) 2020 nbertagnolli Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. @misc{bertagnolli2020counsel, title={Counsel chat: Bootstrapping high-quality therapy data}, author={Bertagnolli, Nicolas}, year={2020}, publisher={Towards Data Science. https://towardsdatascience. com/counsel-chat~…} } 反省点 翻訳処理の際に人名や組織名、URLを除去すべきだった。せっかくChatGPTを翻訳に使うのであれば、フィルタリングを施して、学習データをもっとクリーンにしてあげるべきでした。検証中に何度か人名(セラピストの氏名)を喋ることがあって焦りました。 プロンプトにも気を配るべきだった。最終的な振る舞いの決定はプロンプトで調整できます。今回の記事では解説しませんが、もっとポップなAIに仕上げることも可能だったりします。例えばこのバーチャルカウンセラーを二頭身のかわいいキャ ラク ターとしてサービス化する場合、現在の喋り口調は丁寧すぎて、受け手はギャップを感じてしまいます。ファインチューニングとプロンプトの調整のバランスまで時間をかけて検証するべきでした。 さいごに さて、今回は 電通 総研Advent Calendar 2024として記事を書きました。 他のかたも面白い記事をたくさん書いているので、興味があれば覗いてみてください。 これからも AWS ×AI関連の検証記事をたくさん書いていきます。 ↓ のスターを押していただけると嬉しいです。励みになります。 最後まで読んでいただき、ありがとうございました。 コミュニケーションIT事業部では一緒に働いてくださる仲間を募集中です。以下のリンクからお願いします。 私たちは一緒に働いてくれる仲間を募集しています! 中途採用-コミュニケーションIT事業部 新卒採用-コミュニケーションIT事業部 執筆: 英 良治 (@hanabusa.ryoji) 、レビュー: @kobayashi.hinami ( Shodo で執筆されました )
こんにちは。X(クロス) イノベーション 本部 クラウド イノベーション センターの柴田です。この記事では Rego の Linter である Regal を紹介します。 1 この記事は 電通総研 Advent Calendar 2024 の 5 日目の投稿です。前日の記事は井手さんの「入社 3 年目の業務内容紹介」でした。 Open Policy Agent / Rego とは? Regal とは? Regal を実行する Lint を実行する ローカルで実行する インストールする 実行する CI で実行する エディタと組み合わせて実行する コードを自動修正する ルール 組み込みルール カスタムルール Regal を設定する 設定ファイル 格納場所 サンプル 設定ファイルの書き方 rules ignores capabilities project デフォルトの設定値 ルールを抑制する inline directive CLI フラグ 設定ファイル プロジェクトルート カスタムルールを実装する カスタムルールの種類 雛形を作成する サンプルコード ファイル単位で検査する 複数のファイルをまとめて検査する テストコード カスタムルールの書き方 格納場所 パッケージ メタデータ description (必須) related_resources (任意) schema (任意) ルール report aggregate aggregate_report Regal が提供する主な補助関数 result.fail(metadata, details) result.location(x) result.aggregate(chain, aggregate_data) regal.parse_module(filename, policy) テストを実行する おわりに 参考資料 Open Policy Agent / Rego とは? Open Policy Agent (OPA) と Rego の概要については以下の記事をご参照ください。 2 Policy as Code を実現する Open Policy Agent / Rego の紹介 - 電通総研 テックブログ Regal とは? Regal は Styra 社が開発している Rego の Linter です。 3 Regal を使用して ベストプ ラク ティスに反したコード よくある間違い、バグ、非効率的な処理を含むコード プロジェクト独自のコーディング規約に反したコード などを 機械的 に発見することで Rego コードの品質や可読性を向上できます。 Regal を実行する Lint を実行する ローカルで実行する インストールする 以下の 3 つの方法があります。 パッケージマネージャーを使ってインストールする。 4 GitHub の release asset をダウンロード&インストールする。 Docker を使う。 具体的なインストール手順はここでは省略します。詳しくは以下の公式ドキュメントをご参照ください。 Download Regal | Styra Documentation Packaging | Styra Documentation 実行する 以下のような Rego の ソースコード が policy/authz.rego にあるとします。 package authz import rego.v1 default allow = false allow if { isEmployee "developer" in input.user.roles } isEmployee if regex.match("@acmecorp\\.com$", input.user.email) 以下のコマンドで Lint を実行します。 5 regal lint policy/ するとルールに違反している箇所が表示されます。 Rule: opa-fmt Description: File should be formatted with `opa fmt` Category: style Location: policy/authz.rego:1:1 Text: package authz Documentation: https://docs.styra.com/regal/rules/style/opa-fmt Rule: directory-package-mismatch Description: Directory structure should mirror package Category: idiomatic Location: policy/authz.rego:1:9 Text: package authz Documentation: https://docs.styra.com/regal/rules/idiomatic/directory-package-mismatch Rule: non-raw-regex-pattern Description: Use raw strings for regex patterns Category: idiomatic Location: policy/authz.rego:12:27 Text: isEmployee if regex.match("@acmecorp\\.com$", input.user.email) Documentation: https://docs.styra.com/regal/rules/idiomatic/non-raw-regex-pattern Rule: use-assignment-operator Description: Prefer := over = for assignment Category: style Location: policy/authz.rego:5:15 Text: default allow = false Documentation: https://docs.styra.com/regal/rules/style/use-assignment-operator Rule: prefer-snake-case Description: Prefer snake_case for names Category: style Location: policy/authz.rego:12:1 Text: isEmployee if regex.match("@acmecorp\\.com$", input.user.email) Documentation: https://docs.styra.com/regal/rules/style/prefer-snake-case 1 file linted. 5 violations found. Hint: 2/5 violations can be automatically fixed (directory-package-mismatch, use-assignment-operator) Run regal fix --help for more details. CI で実行する CI で Regal を実行することもできます。例えば GitHub Actions であれば StyraInc/setup-regal action を使って以下のようなワークフローを設定できます。 name : Regal Lint on : pull_request : jobs : lint-rego : runs-on : ubuntu-latest steps : - uses : actions/checkout@v4 - uses : StyraInc/setup-regal@v1 with : # For production workflows, use a specific version, like v0.22.0 version : latest - name : Lint run : regal lint --format=github ./policy 詳しくは以下の公式ドキュメントをご参照ください。 Using Regal in your build pipeline | Styra Documentation エディタと組み合わせて実行する Regal は Language Server および Debug Adapter の機能を提供します。普段使っているエディタと Regal を連携させることで Linter からの素早いフィードバック コード補完 マウスオーバー時の ツールチップ の表示 定義への移動 などの恩恵を受けることが可能です。 本記事ではこれらの詳細は扱いません。詳しくは以下の公式ドキュメントをご参照ください。 Editor support | Styra Documentation Language Server | Styra Documentation Debug Adapter | Styra Documentation コードを自動修正する Regal は以下のルールに関してコードを自動的に修正できます。 opa-fmt use-rego-v1 use-assignment-operator no-whitespace-comment directory-package-mismatch 先ほどのサンプルコード policy/authz.rego を修正してみましょう。以下のコマンドでコードを自動的に修正します。 6 なお --dry-run フラグを設定して dry run することもできます。 regal fix policy/ 2 fixes applied: In project root: /home/ubuntu/projects/regal/policy authz.rego -> authz/authz.rego: - directory-package-mismatch - use-rego-v1 サンプルコードがルールに従って以下のように修正されました。 7 directory-package-mismatch に従ってファイルが policy/authz.rego から policy/authz/authz.rego へ移動されました。 opa-fmt と use-assignment-operator に従ってファイルの中身が以下のように修正されました。 package authz import rego.v1 -default allow = false +default allow := false allow if { - isEmployee - "developer" in input.user.roles + isEmployee + "developer" in input.user.roles } isEmployee if regex.match("@acmecorp\\.com$", input.user.email) 詳しくは以下の公式ドキュメントをご参照ください。 Fixing Violations | Styra Documentation ルール 組み込みルール Regal にはデフォルトで複数のルールが組み込まれています。 組み込みルールは以下の 7 つのカテゴリに分類されています。custom カテゴリ以外のルールはデフォルトで有効です。 ^6 bus : 一般的な間違い、 潜在的 なバグ、非効率的な書き方に関するルール。 idiomatic : 慣用的な書き方に関する imports : インポートに関するルール。 performance : パフォーマンスに関するルール。 style : スタイルに関するルール( Rego Style Guide )。 testing : テストに関するルール。 custom : ニーズに合わせてカスタマイズすべきルール。 具体的なルールの内容は以下の公式ドキュメントをご参照ください。 Rules | Styra Documentation カスタムルール 独自のルールを定義することもできます。詳しくは後ほど説明します。 Regal を設定する 設定ファイル 格納場所 Regal は設定ファイルを以下のように探索します。 カレント ディレクト リからプロジェクトルートへトラバースしつつファイル .regal/config.yaml を探します。最初に見つかったファイルを設定ファイルとして参照します。 --config-file フラグにファイルパスが指定されている場合はそれを参照します。 サンプル rules : style : todo-comment : # don't report on todo comments level : ignore line-length : # custom rule configuration max-line-length : 100 # warn on too long lines, but don't fail level : warning opa-fmt : # not needed as error is the default, but # being explicit won't hurt level : error # files can be ignored for any individual rule # in this example, test files are ignored ignore : files : - "*_test.rego" custom : # custom rule configuration naming-convention : level : error conventions : # ensure all package names start with "acmecorp" or "system" - pattern : '^acmecorp\.[a-z_\.]+$|^system\.[a-z_\.]+$' targets : - package capabilities : from : # optionally configure Regal to target a specific version of OPA # this will disable rules that has dependencies to e.g. built-in # functions or features not supported by the given version # # if not provided, Regal will use the capabilities of the latest # version of OPA available at the time of the Regal release engine : opa version : v0.58.0 ignore : # files can be excluded from all lint rules according to glob-patterns files : - file1.rego - "*_tmp.rego" project : roots : # declares the 'main' and 'lib/jwt' directories as project roots - main - lib/jwt 設定ファイルの書き方 rules rules には各ルールに関する設定を記述します。主な設定項目を以下に示します。 キー 説明 rules.default.level すべてのルールのデフォルトの level を指定します。 rules.<category>.default.level あるカテゴリのデフォルトの level を指定します。 rules.<category>.<rule>.level あるルールの level を指定します。 rules.<category>.<rule>.ignore.files あるルールの適用対象除外とするファイルを指定します。 level には以下の値を指定できます。 error : 違反を表示して lint コマンドをゼロ以外の終了コードで終了します。 warning : 違反を表示して lint コマンドをゼロの終了コードで終了します。 ignore : ルールを無効化します。 ルールによっては上記に加えて固有の設定項目があります。 詳しくは以下の公式ドキュメントに記載された各ルールの説明文をご参照ください。 Rules | Styra Documentation ignores ignores にはすべてのルールの適用対象外とするファイルを指定します。 capabilities capabilities には OPA のランタイムバージョンなどを指定します。 指定したランタイムバージョンでは利用できない機能や関数に依存したルールは無効化されます。 8 主な設定項目を以下に示します。 キー 説明 capabilities.from OPA のランタイムバージョンを指定します。このバージョンでは利用できない機能や関数に依存したルールは無効化されます。[^8]指定しない場合は Regal がリリースされた時の最新の OPA のランタイムバージョンが使用されます。 capabilities.plus 利用できる機能や関数を追加で指定します。 capabilities.minus 利用できない機能や関数を追加で指定します。 詳しくは以下の公式ドキュメントをご参照ください。 Capabilities | Styra Documentation project プロジェクトのルート ディレクト リを指定します。詳しくは後ほど説明します。 デフォルトの設定値 ユーザが設定ファイルで明示的に値を設定していない設定項目については以下の値が使用されます。 data.yaml ルールを抑制する Regal のルールがプロジェクトに適さない場合はそのルールを無効化できます。ルールを無効化する方法は以下の 3 種類があります。 inline directive ソースコード に以下のようなコメントを記述することで、 ソースコード の特定の箇所に対する特定のルールの適用を無効化できます。 # regal ignore:<rule> 利用例は以下のとおりです。 package policy import rego.v1 # regal ignore:prefer-snake-case camelCase := "yes" list_users contains user if { # regal ignore:avoid-get-and-list-prefix some user in data.db.users # ... } CLI フラグ 以下のフラグで指定したルールを有効化または無効化できます。 --enable , --disable : 指定したルールを有効化または無効化します。 --enable-category , --disable-category : 指定したカテゴリのルールを有効化または無効化します。 --enable-all , --disable-all : すべてのルールを有効化または無効化します。 また指定したファイルを無視することもできます。 --ignore-files : 指定したファイルを無視します。 設定ファイル 指定したルールを無効化します。 rules : style : prefer-snake-case : level : ignore 指定したカテゴリのルールを無効化します。 rules : style : default : level : ignore すべてのルールを無効化します。 rules : default : level : ignore 指定したファイルに対して指定したルールを無効化します。 rules : style : line-length : level : error ignore : files : - "*_test.rego" - "scratch.rego" 指定したファイルに対してすべてのルールを無効化します。 ignore : files : - file1.rego - "*_tmp.rego" プロジェクトルート Regal は以下の ディレクト リをプロジェクトルートとして認識します。 設定ファイルの .project.roots で指定された ディレクト リ .manifest ファイルが存在する ディレクト リ .regal ディレクト リが存在する ディレクト リ カレント ディレクト リ 一部のルール(例えば directory-package-mismatch )を正しく評価するためにはプロジェクトルートを正しく設定する必要があります。 もしプロジェクトルートが通常と異なる場合はプロジェクトルートを明示的に設定しておきましょう。 詳しくは以下の公式ドキュメントをご参照ください。 Project Roots | Styra Documentation カスタムルールを実装する カスタムルールの種類 カスタムルールには以下の 2 種類の実装方法があります。 ファイル単位で検査する方法 複数のファイルをまとめて検査する方法 ファイル単位ではルール違反の有無を判断できない場合は後者の実装方法を採用します。 雛形を作成する regal new rule コマンドを実行します。 regal new rule --category <category> --name <rule> すると Regal はカスタムルールの ソースコード の雛形を以下に作成します。 .regal/rules/custom/regal/rules/<category>/<rule>/<rule>.rego .regal/rules/custom/regal/rules/<category>/<rule>/<rule>_test.rego サンプルコード ファイル単位で検査する 例えば各ファイルの package が acme.corp system.log のいずれかで始まっていることを検査するカスタムルールは以下のようになります。 # METADATA # description: All packages must use "acme.corp" base name # related_resources: # - description: documentation # ref: https://www.acmecorp.example.org/docs/regal/package # schemas: # - input: schema.regal.ast package custom.regal.rules.naming["acme-corp-package"] import rego.v1 import data.regal.result report contains violation if { not acme_corp_package not system_log_package violation := result.fail(rego.metadata.chain(), result.location(input["package"].path[1])) } acme_corp_package if { input["package"].path[1].value == "acme" input["package"].path[2].value == "corp" } system_log_package if { input["package"].path[1].value == "system" input["package"].path[2].value == "log" } 複数のファイルをまとめて検査する 例えば default allow := false のように 名前が allow デフォルト値が false なルールがプロジェクト全体で 1 つ以上存在することを検査するカスタムルールは以下のようになります。 # METADATA # description: | # There must be at least one boolean rule named `allow`, and it must # have a default value of `false` # related_resources: # - description: documentation # ref: https://www.acmecorp.example.org/docs/regal/aggregate-allow # schemas: # - input: schema.regal.ast package custom.regal.rules.organizational["at-least-one-allow"] import rego.v1 import data.regal.ast import data.regal.result aggregate contains entry if { # ast.rules is input.rules with functions filtered out some rule in ast.rules # search for rule named allow ast.ref_to_string(rule.head.ref) == "allow" # make sure it's a default assignment # ideally we'll want more than that, but the *requirement* is only # that such a rule exists... rule["default"] == true # ...and that it defaults to false rule.head.value.type == "boolean" rule.head.value.value == false # if found, collect the result into our aggregate collection # we don't really need the location here, but showing for demonstration entry := result.aggregate(rego.metadata.chain(), {"package": input["package"]}) # optional metadata here } # METADATA # description: | # This is called once all aggregates have been collected. Note the use of a # different schema here for type checking, as the input is no longer the AST # of a Rego policy, but our collected data. # schemas: # - input: schema.regal.aggregate aggregate_report contains violation if { # input.aggregate contains only the entries collected by *this* aggregate rule, # so you don't need to worry about counting entries from other sources here! count(input.aggregate) == 0 # no aggregated data found, so we'll report a violation # another rule may of course want to make use of the data collected in the aggregation violation := result.fail(rego.metadata.chain(), {"message": "At least one rule named `allow` must exist, and it must have a default value of `false`"}) } テストコード 例えば先ほどの custom.regal.rules.naming["acme-corp-package"] パッケージをテストするテストコードは以下のようになります。 package custom.regal.rules.naming["acme-corp-package_test"] import rego.v1 import data.custom.regal.rules.naming["acme-corp-package"] as rule test_acme_corp_package_allowed if { module := regal.parse_module("example.rego", "package acme.corp.foo") r := rule.report with input as module count(r) == 0 } test_system_log_package_allowed if { module := regal.parse_module("example.rego", "package system.log.foo") r := rule.report with input as module count(r) == 0 } test_foo_bar_baz_package_not_allowed if { module := regal.parse_module("example.rego", "package foo.bar.baz") r := rule.report with input as module r == {{ "category": "naming", "description": "All packages must use \"acme.corp\" base name", "level": "error", "location": { "col": 9, "end": { "col": 12, "row": 1, }, "file": "example.rego", "row": 1, "text": "package foo.bar.baz", }, "related_resources": [{ "description": "documentation", "ref": "https://www.acmecorp.example.org/docs/regal/package", }], "title": "acme-corp-package", }} } カスタムルールの書き方 格納場所 カスタムルールの ソースコード は .regal/rules 配下に格納します。特別な要件がなければ regal new rule コマンドに倣って以下のパスに格納するのがよいでしょう。 .regal/rules/custom/regal/rules/<category>/<rule>/<rule>.rego もし regal/rules 以外の場所に格納する場合は regal の実行時に --rules フラグでパスを指定します。 パッケージ カスタムルールのパッケージは以下の 命名規則 に従う必要があります。 custom.regal.rules.<category>.<rule> メタデータ メタデータ については以下の公式ドキュメントをご参照ください。 Open Policy Agent | Policy Language | Metadata description (必須) カスタムルールの概要を記述します。 description : All packages must use "acme.corp" base name related_resources (任意) カスタムルールのドキュメントの URL を記載します。 description を documentation にしないと regal lint の実行結果には表示されません。 related_resources : - description : documentation ref : https://www.acmecorp.example.org/docs/regal/package schema (任意) スキーマ については以下の公式ドキュメントをご参照ください。 Open Policy Agent | Policy Language | Schema Regal では以下の スキーマ が提供されています。 schema.regal.ast : report , aggregate ルールの input の スキーマ schema.regal.aggregate : aggregate_report ルールの input の スキーマ 詳しくは後ほど説明します。 schemas : - input : schema.regal.ast ルール 実装方法ごとに必要なルールを実装します。 ファイル単位で検査する場合は以下のルールが必要です。 report 複数のファイルをまとめて検査する場合は以下のルールが必要です。 aggregate aggregate_report report ファイル単位でカスタムルールの検査を行います。 report contains violation if { # 1. ファイル単位でカスタムルールの検査を行う。 # ... # 2. 違反が発見された場合は report の要素に result.fail が生成するオブジェクトを加える。 violation := result.fail(rego.metadata.chain(), result.location(obj)) } input には各ファイルの Rego コードの抽象 構文木 を Regal 用に最適化したものが格納されます。 スキーマ は schema.regal.ast です。具体的な内容は以下のコマンドで確認できます。 regal parse <検査対象ファイル> ルール違反が発見された場合は report ルールの要素に result.fail 関数が生成するオブジェクトを加えます。 aggregate 各ファイルからデータを収集します。 aggregate contains entry if { # 1. 各ファイルからデータを収集する。 # ... # 2. aggregate の要素に result.aggregate が生成するオブジェクトを加える。 entry := result.aggregate(rego.metadata.chain(), {...}) } input は report ルールと同じです。 必要に応じて aggregate ルールの要素に result.aggregate 関数が生成するオブジェクトを加えます。このデータは後述する aggregate_report ルールに渡されます。 aggregate_report aggregate ルールの結果をもとにカスタムルールの検査を行います。 aggregate_report contains violation if { # 1. aggregate の結果に対してカスタムルールの検査を行う。 # ... # 2. 違反が発見された場合は aggregate_report の要素に result.fail が生成するオブジェクトを加える。 violation := result.fail(rego.metadata.chain(), {...}) } input の スキーマ は schema.regal.aggregate です。 input.aggregate には aggregate ルールの結果が配列として格納されます。 ルール違反が発見された場合は aggregate_report ルールの要素に result.fail 関数が生成するオブジェクトを加えます。 Regal が提供する主な補助関数 Regal が提供する補助関数のうち主に利用するものを以下に紹介します。 result.fail(metadata, details) # METADATA # description: | # helper function to call when building the "return value" for the `report` in any linter rule — # recommendation being that both built-in rules and custom rules use this in favor of building the # result by hand # scope: document fail(metadata, details) := ... report , aggregate_report ルールの要素となるオブジェクトを生成します。第一引数 metadata には rego.metadata.chain() を、第二引数 details には result.location が生成するオブジェクトを指定します。 result.location(x) # METADATA # description: | # returns a "normalized" location object from the location value found in the AST. # new code should most often use one of the ranged_ location functions instea, as # that will also include an `"end"` location attribute # scope: document location(x) := ... fail の第二引数に渡すオブジェクトを生成します。第一引数 x には input のうちルール違反が発見された要素を指定します。 result.aggregate(chain, aggregate_data) # METADATA # description: | # The result.aggregate function works similarly to `result.fail`, but instead of producing # a violation returns an entry to be aggregated for later evaluation. This is useful in # aggregate rules (and only in aggregate rules) as it provides a uniform format for # aggregate data entries. Example return value: # # { # "rule": { # "category": "testing", # "title": "aggregation", # }, # "aggregate_source": { # "file": "policy.rego", # "package_path": ["a", "b", "c"], # }, # "aggregate_data": { # "foo": "bar", # "baz": [1, 2, 3], # }, # } # aggregate(chain, aggregate_data) := ... aggregate ルールの要素となるオブジェクトを生成します。第一引数 chain には rego.metadata.chain を、第二引数 aggregate_data には任意のオブジェクトを指定します。 regal.parse_module(filename, policy) Rego コードの抽象 構文木 を Regal 用に最適化したものを返します。第一引数 filename にはファイル名、第二引数 policy には Rego コードを指定します。 この補助関数は主にテストで使用します。この補助関数の戻り値に対してカスタムルールが期待した結果を生成するかテストします。 rule.report == want with input as regal.parse_module(filename, policy) テストを実行する カスタムルールのテストコードを評価するには以下のコマンドを実行します。 regal test <path> カスタムルールおよびそのテストコードは Regal が提供するライブラリ data.regal.* や スキーマ schema.regal.ast を利用しています。そのため opa test コマンドではテストを実行できません。 おわりに この記事では Rego の Linter である Regal を紹介しました。 個人的にはプロジェクトで Rego を採用する際は Regal も一緒に導入することを強く推奨します。Rego は事例が少なく、実装経験が豊富なエンジニアはあまり多くありません。Regal を導入することで Rego の経験の浅いエンジニアでもベストプ ラク ティスに則ったコードを書くことができます。 私自身、 OPA / Rego を使って Kubernetes の マニフェスト ファイルや Terraform の構成ファイルに対するポリシーを書くことがあるのですが、今までは経験が浅く我流で Rego コードを書いていたため以下のような課題がありました。 よくある間違い、バグ、非効率的な処理を含むコードを誤って書いてしまう。 他のメンバーにとって可読性の低いコードを書いてしまう。 現在はプロジェクトに Regal を導入してベストプ ラク ティスやコーディング規約への準拠を強制することで Rego コードの品質や可読性に関するこれらの問題を改善できています。 最後までお読みいただき、ありがとうございました。 参考資料 Regal | Styra Documentation 私たちは一緒に働いてくれる仲間を募集しています! クラウドアーキテクト 執筆: @shibata.takao 、レビュー: Ishizawa Kento (@kent) ( Shodo で執筆されました ) この記事では Regal v0.28.0 を使用します。 ↩ 3 年前に書かれた記事のため記載内容の一部は古くなっています(特に文法など)。最新の情報は 公式ドキュメント をご参照ください。 ↩ Regal は β 版のため仕様が予告なく変更される可能性があります。 ↩ 公式ドキュメントでは Homebrew , asdf , pkgsrc , Nix , mason.vim が紹介されています。 ↩ Regal は Rego コードが構文的に正しく コンパイル 可能であることを前提としています。そのため regal lint コマンドの実行前に opa check --strict コマンドで Rego コードが構文的に正しく コンパイル 可能か検証することが推奨されています。詳しくは OPA Check and Strict Mode | Styra Documentation をご参照ください。 ↩ コードの自動修正を実行する際は、何か問題が発生した場合に簡単に変更を元に戻せるよう、Git などの VCS を使って変更をコミットまたはスタッシュしておくことを推奨します。 ↩ regal fix コマンドの実行結果を見ると directory-package-mismatch , use-rego-v1 の 2 つに関する修正が行われたと出力されていますが、実際には directory-package-mismatch , opa-fmt , use-assignment-operator の 3 つに関する修正が行われています。なぜこうなっているかは不明です。 ↩ ルールが capabilities に応じて有効化または無効化されるように実装されている必要があります。 ↩
こんにちは、 電通 総研IT、文系出身 新卒3年目の井手です。 普段は、金融機関の ポータルサイト の保守を担当しています。 この記事では、私の業務内容や1週間の過ごし方など、日々の働き方についてわかりやすくご紹介します。 IT企業への就職をお考えの学生の皆さんや、 電通 総研ITにご関心をお持ちの方の参考になれば幸いです。 自己紹介 私は文系学部の出身で、学生時代には公共政策やITについて幅広く学んでいました。 そのため開発経験がないままIT企業に入社しました。 そんな私がIT企業を志すきっかけとなったのは、学生時代の研究室での活動を通じて、 ITの持つ可能性の広さに触れたことです。 ITを活用することで、作業の効率化が進み、 データを活用して過去の経験を次に生かすことができると実感しました。 仕事内容 ここからは、自分がどのような内容の仕事をしているかご紹介します。 仕事内容を紹介するにあたり3つの視点でお伝えします。 まず1つ目は、使用しているツールについてです。 私が担当する案件では、ローコード開発ツールを用いてアプリケーションの開発を行っています。 ローコード開発とは、最小限のコードと ドラッグ&ドロップ 操作やテンプレートを活用することで、 効率的にアプリケーションを構築する手法です。 具体的には「 Salesforce 」を使用し、企業と顧客をつなぐ クラウド 型の顧客管理( CRM )や営業支援( SFA )を 実装しています。このアプリケーションでは、営業活動や マーケティング 、問い合わせ対応など、 さまざまな場面で顧客情報を一元管理できるため、開発未経験の私でも柔軟かつ効率的に開発と保守を行えています。 2つ目は、携わっているプロジェクトについてです。 現在、私は金融機関向けの ポータルサイト の保守プロジェクトに参画しています。 このプロジェクトは、顧客である金融機関のエンドユーザー(金融機関の顧客)の利便性と満足度の向上を目指しており、同時に金融機関の業務効率化と収益性の向上も視野に入れています。 3つ目は、私の役割についてです。 私は作業担当者として、依頼されたタスクを実施しています。 このプロジェクトは 電通 総研と私の所属する 電通 総研ITの協業で進めており、 電通 総研の保守PM(プロジェクトマネージャー)や自社の上長の指示に基づいて作業を行っています。 以下に、1週間のスケジュールをご紹介します。 配属からこれまでの流れ 入社後の半年間は、 電通 グループや 電通 総研グループの同期と交流しながら、研修を受ける期間でした。 研修内容は、開発に必要な基礎知識やビジネスマナーといった、業務に必要な基本的なスキルの習得が中心です。 1年目後半には、 OJT 研修が始まり、現場で先輩社員と一緒に業務を実践しながら必要なスキルを身につけます。 この研修では、個々のスキルや成長スピードに応じたサポートが行われます。 2年目の4月から正式に現場に配属され、私はまず、金融機関向け ポータルサイト の開発チームに参加しました。 ここで、特定機能の開発において実装からテスト工程までを経験しました。 その後、現在の金融機関向け ポータルサイト の保守チームに異動し、現在もそこで業務を続けています。 保守チームでは、既存システムの細かな機能の修正や定期的なバージョンアップといった業務を担当しています。 現在の働き方は、週1回の出社で、残業は月に7時間程度となっています。 入社前後のギャップについて ここでは、入社前に抱いていたイメージと入社後の実際のギャップについてご紹介します。 結論から言うと、私は良い意味でのギャップを感じています。 まず、入社前に抱いていた「 電通 総研ITは研修が手厚い会社」というイメージです。 面接の際、開発経験がないことへの不安を伝えたところ、研修期間が十分確保されており、 開発経験がない状態で入社した先輩社員も多いと説明がありました。 実際に研修を受けた感想としては、期待以上に手厚く、 結果的に1年間も研修を受けることができ、不安をしっかり解消することができました。 次に、入社前は「 電通 総研ITは技術者集団で、少し堅い会社」というイメージもありました。 これは、 電通 総研ITのホームページで大手企業との実績を見たことがきっかけで、 「大手との実績が多い=堅い会社」という印象を持っていたためです。 しかし、実際には 電通 総研ITは技術力に裏打ちされた堅実な会社でありつつも、活気にあふれた面も多くあります。 たとえば、自由に参加できるサークル活動や、年に1度の全社員が参加するイベントがあり、 普段の業務で接点がない先輩方とも交流できる場が設けられています。 こうした機会を通じて、風通しがよく、活気ある社風だと実感しています。 私自身もサークル活動に参加し、スポーツを通じてさまざまな社員と交流を深めています。 このような活動のおかげで、仕事以外の面でも充実した関係を築けていると感じています。 最後に 就職活動中の皆さんの中には、私と同じくIT系の専攻ではないけれど、 電通 総研ITに興味を持っている方もいるかもしれません。 この会社は未経験者から経験者まで、それぞれのスキルや適性に応じた働き方ができる環境が整っています。 私の働き方の紹介が、皆さんの将来を考える際の一助になれば嬉しく思います。 興味を持ってくださった方は、ぜひ以下の採用ページもご覧いただき、応募をご検討ください。 https://www.it.dentsusoken.com/recruit 執筆: @ide.wakana 、レビュー: @kinjo.ryuki ( Shodo で執筆されました )
はいどーもー! コミュニケーションIT事業部の宮澤響です! 本記事は 電通総研 Advent Calendar 2024 2日目の記事です! 記念すべき1日目である昨日の記事は、小林日菜美さんの「 【Salesforce認定アソシエイト試験】知識0から1ヶ月で合格した話 」でした! こちらは単なる学習方法や受験方法の紹介記事ではなく、試験前後に小林さんが実際に見舞われた(誰にでも起こり得そうな)トラブルとその回避方法についても分かりやすくまとまっている記事ですので、同試験を受験予定の方もそうでない方も、ぜひご一読ください! ということで、本記事では、 Power Automate を用いて、Backlogで特定の条件のチケットが起票・更新されたときにだけSlackに通知する方法をご紹介します! はじめに まずやってみる SlackへのPower Platform Connectorsの追加 Power Automateワークフローの作成 BacklogへのWebhookの追加 Power Automateワークフローの修正 実行結果 おまけ(Teamsへの通知) おわりに はじめに ああ…Backlogで特定の条件のチケットが起票・更新されたときにだけ通知がほしい…! そう思ったこと、ありませんか? ありますよね? 私はあります! ですが、以下の公式ドキュメントに記載されている方法では、対象チケットの条件を指定することができず、プロジェクト内のあらゆるチケットの起票・更新が通知されてしまいます。 BacklogのSlack連携 BacklogのMicrosoft Teams連携 そのため、例えば「自分が担当者に設定されたチケット」や「優先度が高のチケット」のみを対象としたいケースであっても、Backlogの公式連携機能では実現できません。 とはいえ、上記のようなケースは少なくないと考えています。 そこで、今回は、Power Automateを利用して、上記のようなケースへの対応策をご紹介します。 まずやってみる 大まかな実現方針は以下のとおりです。 最終的に、 Backlog -> Power Automate -> Slack の処理フローを実現します。 Backlog Webhookを利用して、BacklogからPower Automateにチケット情報を送信する Power Automateのワークフローで条件分岐を設定することで、特定の条件のチケット情報のみをフィルタリングする Power Platform Connectorsを利用して、Power AutomateからSlackにメッセージを送信する なお、以降の説明は、設定の都合上、上記3項目の逆順で記載します。 SlackへのPower Platform Connectorsの追加 まずはSlackのチャンネルにPower Platform Connectorsを追加します。 (私自身は「チャネル」表記派ですが、Slackは「チャンネル」表記、Power AutomateやTeamsは「チャネル」表記のようですので、本記事ではそれらに合わせて表記します。) Power Platform Connectorsとは、簡単に言えば、Power Automateをはじめとする Microsoft 社のローコードツールと、Slackなどの外部アプリケーションとを連携させるためのツールです。 なお、 ワークスペース にPower Platform Connectorsがインストールされていない場合は、 Slack ワークスペースにアプリを追加する を参考に、事前にインストールする必要があります。 Power Platform Connectorsの追加 チャンネル右上の「︙」から「チャンネル詳細を開く」を押下します。 「インテグレーション」タブにある「アプリを追加する」を押下します。 「 Microsoft Power Platform Connectors」を探し、「追加」を押下します。 Power Automateワークフローの作成 次に、Power Automateでワークフローを作成します。 なお、このステップではワークフローの大枠を作成するのみであり、詳細な条件などは後続のステップで作成します。 トリガーの追加 「要求」コネクタの「HTTP 要求の受信時」というトリガーを追加します。 少々見つけにくいトリガーなのですが、「HTTP」などと検索すればヒットするかなと思います。 設定が必要なパラメーターは以下のとおりです。 フローをトリガーできるユーザー: 誰でも ※ここでは 誰でも を選択していますが、必要に応じて、セキュリティを考慮したアクセス制限を行うことを推奨します。 アクションの追加 「Slack」コネクタの「メッセージの投稿 (V2)」というアクションを追加します。 なお、対象となるSlack ワークスペース に対して、これまでに一度もPower Automateを連携させたことがない場合には、こちらのアクションを設定する際に、併せて「接続」も新規作成します。(参考: Power Automate での接続の管理 ) 設定が必要なパラメーターは以下のとおりです。 チャネル名:通知を送信したいチャンネル(プライベートチャンネルの場合はカスタム値を入力) メッセージ テキスト:任意(例: チケットきたでー。 ) ワークフローの保存 ここまでの設定で、ワークフローを一旦保存します。 すると、このタイミングで「HTTP 要求の受信時」トリガーの名称が何故か「manual」に変化し、「HTTP URL」に値が設定されます。 「HTTP URL」は後続のステップで利用しますので、「URLのコピー」ボタンでコピーしておいてください。 ワークフローの有効化 「フローの管理」画面に戻り、保存したワークフローを有効化します。 画面上部の「オンにする」ボタンを押下します。 BacklogへのWebhookの追加 続いて、Backlog側の設定を行います。 Webhookの追加 対象となるBacklogプロジェクトの「プロジェクト設定」から「インテグレーション」を選択し、「Webhook」の「設定」ボタンを押下します。 「Webhookを追加する」ボタンを押下します。 Webhookの設定値を入力します。 今回は、以下のように設定します。 Webhook名:任意(例: 宮澤テスト ) Webhook URL:先ほどコピーした「HTTP URL」 通知するイベント: 課題の追加 、 課題の更新 デフォルト(「課題の追加」が選択された状態)のまま、実行テスト欄の「実行」ボタンを押下します。 ダイアログが表示されるので、「OK」ボタンを押下します。 ここまでの設定に不備がなければ、Slackの指定したチャンネルに以下のような通知が送信されるはずです。 「Webhookを追加する」ボタンを押下します。 リク エス ト スキーマ の取得 Webhook一覧に戻り、「送信履歴」タブを押下します。 先ほど実行ボタンを押下した時刻の項目の「すべて表示」を押下します。 最下行の JSON (リク エス ト)をコピーします。 Power Automateワークフローの修正 再びPower Automate側に戻ってきます。 リク エス ト スキーマ の生成 「manual」(トリガー)の「要求本文の JSON スキーマ 」の「サンプルの ペイロード を使用して スキーマ を生成する」を押下し、先ほどコピーした JSON を貼り付けます。 「完了」ボタンを押下します。 すると、「要求本文の JSON スキーマ 」が自動で生成されます。 後続のステップで担当者のユーザーIDを利用するため、 assignee の {} 内に以下の JSON を挿入します。 " type ": " object ", " properties ": { " userId ": { " type ": " string " } } 条件の指定 ここでは、「自分が担当者に設定されていること」を条件としたいと思います。 「manual」と「メッセージの投稿 (V2)」の間に、「Control」コネクタの「条件」というアクションを追加します。 そのまま検索してもヒットしない場合には、ランタイムを「組み込み」に絞った状態で検索するとヒットしやすいかなと思います。 設定が必要なパラメーターは以下のとおりです。 Condition expression:(後続のステップで詳しく説明します。) 左側の「値を選択してください」にカーソルを合わせた際に表示される雷マークのボタンを押下します。 「See more」を押下します。 assignee や category の上にある userId を押下します。 userId は複数存在するため、上記位置の userId を選択するようにしてください。 右側の「値を選択してください」欄に、自分のBacklogのユーザーIDを入力します。 なお、ユーザーIDはBacklogの個人設定画面から確認可能です。 設定内容を確認します。 ミスなく設定できていれば、以下のような状態となっているはずです。 特に、Code viewの7行目が @triggerBody()?['content']?['assignee']?['userId'] となっているかを確認しておいてください。 (異なる userId を選択してしまった場合にはこちらの値が異なって表示されているかと思います。) 「メッセージの投稿 (V2)」を「True」ブロックの中に移動します。 これで全ての設定は完了です! 実行結果 自分を担当者に設定したチケットを起票したところ、無事に通知が届きました! なお、 スクリーンショット などは省略しますが、具体的な通知条件の一例については以下のとおりです。 (通知するイベント: 課題をまとめて更新 については、リク エス ト スキーマ の形式が大きく異なるため、ここまでの設定では対応できていません。) 条件 通知の有無 「担当者:自分」のチケットを起票した場合 あり 「担当者:自分以外」のチケットを起票した場合 なし 「担当者:自分」のチケットの内容(詳細、種別、状態、など)を更新した場合 あり 「担当者:自分以外」のチケットの内容(詳細、種別、状態、など)を更新した場合 なし 「担当者:自分」のチケットを「担当者:自分以外」に更新した場合 なし 「担当者:自分以外」のチケットを「担当者:自分」に更新した場合 あり おまけ(Teamsへの通知) 「 Microsoft Teams」コネクタの「チャットまたはチャネルでメッセージを投稿する」というアクションを利用することで、Teamsに通知を送ることも可能です。 おわりに 本記事では、Power Automateを用いて、Backlogで特定の条件のチケットが起票・更新されたときにだけSlackに通知する方法をご紹介しました。 工夫した点、というほどでもないですが、ポイントとしては、 Power Automateワークフローの修正 のところで assignee の情報を追記したように、フィルタリングに利用したい条件に応じてリク エス ト スキーマ を修正する点が挙げられるかなと思います。 なお、私自身も、現在携わっている案件において、実際にこちらの方法を個人的に利用しています。 (以下のようにメンションとリンクを付与した上で通知させています。) (余談ですが語尾は「ぇー」「しー」「でー」「えふー」「じぇー」「ぉー」「ゅー」「わい」の中からランダムに選択されます。) さて、 電通 総研 Advent Calendar 2024 3日目となる明日の記事は、井手稚菜さんの「 電通 総研IT 文系出身 新卒3年目社員の働き方紹介」です! お楽しみに! 最後までお読みいただき、本当にありがとうございました! 私たちは同じ事業部で共に働いていただける仲間を募集しています! みなさまのご応募、お待ちしています! 株式会社 電通総研 新卒採用サイト <電通×IT>電通グループ基幹システムプロジェクトマネージャー <電通×IT>顧客DX案件プロジェクトマネージャー 政府・自治体向けシステム構築におけるプロジェクトマネージャー/リーダー デジタルマーケティング領域におけるプロジェクトマネージャー/リーダー 新規事業開発領域におけるプロジェクトマネージャー/リーダー エンタープライズ向けDX推進リーダー/エンジニア◆電通グループ協業◆提案活動~開発・運用まで <電通×IT>クラウドアーキテクト <電通×IT>アプリケーションアーキテクト 執筆: @miyazawa.hibiki 、レビュー: @nakamura.toshihiro ( Shodo で執筆されました )