はじめに 接続会員向けアプリ「マイ ニフティ」のスクラムマスターをしている西野です。 私は2024年時点でスクラムマスター4年目になりますが、どのチームでも前回とは異なるレトロスペクティブをやるように取り組んできました。 いろいろなレトロスペクティブを実施する中で、ある程度レトロスペクティブの手法(アクティビティ)のカテゴリーが見えてきたので、テーマ選びと振り返り方のコツをまとめてみました。 スクラム以外のスタイルでも参考になる部分があると思うので、ぜひ活用してみてください。 なぜレトロスペクティブをするのか あなたのチームは世界一良いチームですか? もしそう言い切れないなら、レトロスペクティブをやる価値はあります。 スクラムガイドによれば、レトロスペクティブはそのスプリントの「検査」の場です。振り返りをしない(検査しない)というのは、テストせずに本番リリースするくらい組織にとって危険なことだと思います。 チームのよかった点や問題点を検査することで、仕事や人に対する不安を解消し、心理的安全性を確保します。そして、検査を重ねることでより良いプロダクトをつくるチームになるためには何が必要かを割り出し、成長に繋げます。 難しく考えずに、もし「最高のチームワーク」が欲しいなら、どうやったら協力しやすいか過去の活動をもとに考えるのではないでしょうか。それがレトロスペクティブです。 振り返りのフォーカスの当て方 「これについて振り返りたい」とチームメンバーからアイデアが出てくるのが理想ですが、ある程度狙いどおりに進んだスプリントが続くと、良いことも悪いことも浮かびにくくなります。振り返りのテーマを探るためのヒントを挙げてみます。 スプリント固有のイベントにフォーカスする 例えば、障害が起きた、今までできたことが今回はうまくいかなかった、新しいメンバーが増えた(減った)というようなスプリントであればテーマは選びやすいでしょう。 思い当たる出来事がなくても、ベロシティが著しく変わってないか、PRの回数、レビューの滞留時間など客観的な数字から普段との違いを見つけることもできます。 もし前のレトロスペクティブで上げたアクションが今スプリントで機能しなかった場合は、引き続きその課題について話し合ったほうが良いかもしれません。 スプリントで何が起きたかを把握する スプリント固有のイベントが思い当たらない場合、そのスプリントで何が起きていたかを把握すること自体をテーマにしても良いでしょう。 チームのあるべき姿を考える もし特に目立つイベントがない、または取り上げるべき問題がないと感じられる場合、あるべき姿をイメージし、その差分を見つけることをテーマにしてみましょう。 チームの状況をメタファーで捉えることで、改善点が見えてくることもあります。 今後のことにフォーカスする 今起きていないが「今後」起きるかもしれない心配ごとがある場合、それについてどんな出来事が過去にあったか、どんな未来を招く可能性があるかをイメージし、対策を考えることも効果的です。 振り返り方法の選び方 振り返りの方法には多くの種類があり、どれを選べば良いか迷ってしまうことも多いです。私が参考にしている書籍やサイトを一部紹介します。 アジャイルレトロスペクティブズ (書籍) FunRetrospectives Easy Retro Miro – Retrospective templates Retrium 少し見ただけでも、膨大な数があり迷ってしまうかもしれません。 これらの振り返り方法は大きく5種類に分けて把握できると考えています。GOOD/BAD系、連想、時系列、メタファー、数値化です。 GOOD/BAD系 特定の観点に基づいて、各々が思う出来事を挙げていきます。 観点を少し変えるだけでアクティビティの内容も変わるため、最もパターンが多い振り返りの種類です。 オススメのシーン スプリントで起きたことを幅広く知りたい時 意見が出やすいチームやテーマの時 オススメではないシーン 意見が出にくいチームやテーマの時 発言する人・しない人が偏ってしまう時 アクティビティの例 KPT YWT Start-Stop-Continue Mad Sad Glad Easy As Pie 象、死んだ魚、嘔吐 Starfish Retrospective 連想 前の人の意見を受けて順に意見出しをするやり方です。 連想形式のため、GOOD/BAD系で意見が出にくくても、このスタイルだと意見が出やすくなることが多いです。 オススメのシーン 意見が出にくいチームやテーマの時 発言する人・しない人の偏りをなくしたい時 オススメではないシーン 活発なディスカッションをしたい時 アクティビティの例 555 質問の輪 5つのなぜ 時系列 出来事を時系列で挙げていきます。 その時のチームメンバーそれぞれの感情を合わせて記載することで、出来事の評価がしやすくなります。 オススメのシーン プロダクト開発が走り始めた時 波乱万丈なスプリント(出来事が複雑な時) オススメではないシーン 平穏無事なスプリント(何が起きたかある程度わかっている時) アクティビティの例 Timeline メタファー チームの状況を別のものに例えることで客観的に見つめて、追い風となるもの、今負担となるもの、これから障害になりそうなものなどを洗い出します。 オススメのシーン チームの状況を俯瞰して捉えたい時 グラフィカルで楽しい振り返りをしたい時 オススメではないシーン とくにありませんが、結果がある程度似てしまうことが多いので、1-2ヵ月に1度くらいの頻度がオススメです。 アクティビティの例 熱気球 スピードカー 三匹の子豚 数値化 チームがどの程度できているかを項目別に点数をつけることで、理想的な状態との差分を知ります。 オススメのシーン チームの短所がぱっと思いつかない時 オススメではないシーン 心理的安全性が低く、数値化することに恐れを感じる場合 アクティビティの例 チームレーダー アジャイルの車輪 【その他】チームメンバーを知る 振り返りとは異なりますが、チームメンバーが増えた時にレトロスペクティブの時間を相互理解の時間にあてることがあります。(スプリントの検査としては機能しないので、別の時間でやったほうが良いと思います) アクティビティの例 価値観ポーカー ムービング・モチベーターズ ある程度パターンと長所短所がわかれば、具体的にどの手法を選ぶかはテーマだけでなく季節感で選んでも良いと思います。例えば夏や海がテーマの振り返りだけでも何種類もあります。季節的な要素を加えると楽しさもアップします。 レトロスペクティブの手法を毎回変える意味とは レトロスペクティブのやり方を毎回変えていると、なぜ常にKPTなどで振り返ってはいけないのでしょうか、と尋ねられることがしばしばあります。 以前 ケース別!リモートでのおすすめレトロスペクティブ で「毎回同じ手法では観点に偏りが出てしまい、新たな問題に気づきにくくなる可能性がある」と書きましたが、最近は「お祝いは、工夫を凝らしたほうが楽しくて盛り上がる」という答えがよりしっくりきています。 そのスプリントが無事に、またはトラブルはあったものの終えられたということは、お祝いしていいことだと思います。 スプリントの最後のイベントこそ、どのスクラムイベントよりも楽しく、ポジティブに終えることができたほうが次のスプリントが良くなりそうな気がしませんか。 実際、チームメンバーが楽しんでレトロスペクティブに参加している姿や、今回のレトロスペクティブは面白かったと言ってもらえるとスクラムマスターとしてもやる気がでます。 逆に、今回のレトロスペクティブはやりにくかったというフィードバックでも、次の振り返りにつなげることができます。 毎回同じ振り返り手法では、このようなフィードバックは得にくいです。 レトロスペクティブをスプリントの中でもっとも素敵な時間にするために、今回はどんなアクティビティが盛り上がりそうか、この「GOOD/BAD系」「連想」「時系列」「メタファー」「数値化」という5種類の分類を手がかりに、工夫を凝らしてみてください。 ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 カジュアル面談も随時受付中! ニフティに興味をお持ちの方は キャリア登録をぜひお願いいたします! キャリア登録 connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する
はじめに 会員システムグループのkiqkiqです。最近PySparkというライブラリを触ってみたので紹介したいと思います。 Apache Spark・PySparkとは PySparkは、Pythonを使ってApache Sparkを操作するためのライブラリです。そのApache Sparkというのは、オープンソースの大規模データ処理フレームワークで、高速で汎用的なデータ処理エンジンです。Sparkには主に4つの特徴があります。 分散処理 Sparkはクラスター上で分散処理を行うことができ、大量のデータを効率的に処理することができる 高速 メモリ内で処理を行い、複数の並列操作でジョブのステップ数を減らすことでデータを再利用することができる 汎用性 Sparkはバッチ処理だけではなく、ストリーミング処理、機械学習、グラフ処理などのデータ処理に対応 多様なAPI Sparkは、Scala、Java、Python、R など、さまざまなプログラミング言語からアクセスできる これらの特徴から、Spark関連のものは大規模データ処理、機械学習、ストリーミング処理など、さまざまな用途で利用されています。このブログではPySparkの基本的な機能や簡単な使い方などを紹介していきます。 機能紹介 Spark SQL Spark SQL は、SQLライクなクエリ言語を使ってデータ処理ができる。 SQLを使ったデータ分析や 抽出、変換、ロードのようなタスクを簡単に実行できるもの。 Spark Streaming Spark Streamingは、リアルタイムのデータストリーミング処理を可能にするコンポーネント。Spark Streamingでは数秒ほどの短い間隔に区切られたバッチ処理を繰り返し行い、ストリームデータ処理機能を実現されている。 MLlib MLlibは、Sparkに組み込まれている機械学習のライブラリで、分類、回帰、クラスタリング、共起分析などの主要な機械学習アルゴリズムが用意されている。 GraphX GraphXは、グラフ処理のための Spark のコンポーネントで、ソーシャルネットワーク分析、推薦システム、ルーティングアルゴリズムなど、グラフ構造のデータを扱うことに特化した機能。また、組み込まれているグラフ処理用の API を使って、グラフの作成、変換、分析などの処理を行える。 使い方 環境構築 今回は jupyter notebook と pyspark がインストールされたコンテナイメージ jupyter/pyspark-notebook 上で実行していきます。以下の docker-compose.yml を実行するだけで pyspark を実行できる環境を構築できます。 version: '3' services: jupyter: image: jupyter/pyspark-notebook ports: - "8888:8888" # notebook のポート - "4090:4040" # Spark UI のポート environment: - JUPYTER_ENABLE_LAB=yes - CHOWN_HOME=yes - CHOWN_HOME_OPTS=-R - GRANT_SUDO=yes - NB_UID=1000 - NB_GID=100 command: start-notebook.sh --NotebookApp.token='' また、この記事では気象庁が公開している神戸市の気象データを使用します。 https://www.data.jma.go.jp/gmd/risk/obsdl/ 実行 PySparkのDataFrameを使用しSpark SQLの使い方を紹介します。 SparkSessionの作成とPySparkのDataFrameへの変換は以下のように行います (また、SparkSessionの作成時にワーカーノード上のExecutorの設定もできる) from pyspark.sql import SparkSession spark = SparkSession.builder.appName("spack test").getOrCreate() PySparkのDataFrameはpandasと同じような操作ができます df = spark.read.format("csv").option("inferSchema","True").option('encoding', 'shift_jis').option("sep",",").option("header","True").load("../data/data.csv") PySparkのDataFrameに対してSpark SQLを用いてSQLの操作を行うことができます (普通のselect3件) spark.sql("select * from data limit 3").show() +---------+--------+---------+---------+--------+---------+---------------+---------+---------+----+----------+---------------+----------+----------+ | 日時|最低気温|品質情報2|均質番号3|最高気温|品質情報5| _c6|品質情報7|均質番号8| _c9|品質情報10| _c11|品質情報12|均質番号13| +---------+--------+---------+---------+--------+---------+---------------+---------+---------+----+----------+---------------+----------+----------+ |2020/6/20| 22.7| 8| 1| 26.5| 8|2020/6/20 14:20| 8| 1|20.0| 8|2020/6/20 05:19| 8| 1| |2020/6/21| 23.7| 8| 1| 28.2| 8|2020/6/21 15:03| 8| 1|18.5| 8|2020/6/21 04:39| 8| 1| |2020/6/22| 25.8| 8| 1| 29.2| 8|2020/6/22 15:57| 8| 1|23.2| 8|2020/6/22 05:53| 8| 1| +---------+--------+---------+---------+--------+---------+---------------+---------+---------+----+----------+---------------+----------+----------+ このように普通のSQLの実行結果のように出力することができます。 結果の確認 Spark UIの「SQL / DataFrame」タブで実行情報を確認できます また、SparkSession作成時に設定したExecutor(ワーカーノード上で実行されるプロセス)などの確認はEnvironmentタブやExecutorsタブで確認することができます。 ブログ内で使用した程度のデータの規模だとSparkを使用する意味はほとんどありませんが、大きいデータになれば分散処理による工夫が必要ななるため、Sparkなどが有効になると思います。 おわりに PySparkを使用すればPandasやSQLを使うように簡単に分散処理を実行することができるので、分散処理に興味がある方や大規模なデータ分析をする方は試してみてください。 ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する
はじめに 2024年6月21日に、Anthropicの新モデル「Claude 3.5 Sonnet」がリリースされました。 Claudeの最新バージョンということで、とても注目を集めています。 Claudeシリーズは他の生成AIと比較しても引け目を取らない回答精度や速度を出していますが、 3.5になりさらに回答精度や速度が向上したようです。 引用元: https://www.anthropic.com/news/claude-3-5-sonnet また、同時にArtifactsという機能も追加されました。 詳しくはAnthropic社のリリースをご参照くだ さい 今回はArtifactsを使って遊んでみたいと思います。 Artifactsとは 2024/6/21にClaude 3.5 Sonnetと一緒に追加された新機能です。 こちらはチャットの回答を拡張する機能のようです。 Claudeにコンテンツの生成を依頼すると右側に専用ウィンドウが表示され、作成物をリアルタイムで表示することができるようになりました。 また、インタラクティブにコンテンツを生成してくれるので、 生成されたコンテンツを会話をしながら確認、修正することができます。 メリット Artifactsを使うことでのメリットは以下のようになります。 簡単なコンテンツ確認: リアルタイムで生成され、即座に結果を確認することができる 専用のウィンドウに表示されるので、会話の流れを妨げることなく内容を確認できる 柔軟な編集が可能: 生成されたコンテンツについて、コメントをすることで修正や調整ができる 再利用可能な出力: コード、文書、HTMLなど多様な形式で出力されるため、様々な用途に適用可能 上記で解説した特徴やメリットをまとめると以下のようになります。(この画像もArtifactsを使って作成しました) 今回はArtifactsを使って簡単なTodoアプリを作ってみます。 2024年6月現在、Artifactsを用いたリアルタイム生成でのプレビューはHTML、CSS、JavaScriptのみのようです。 Pythonなど他の言語で実行すると、コードのみが表示され、プレビューすることができないようです。 ここは、今後のアップデートに期待ですね! 設定 Feature PreviewからONにすれば良さそうです。簡単ですね。 Todoアプリを作ってもらう 雑にプロンプトを投げてみる まずは雑に投げてみます。Claude3.5になったということで応答精度も向上していると思います。 HTML/CSS、JavaScriptを使ってTodoアプリを作ってください 生成されたWebアプリの画面を実際に触ることができるので確認しやすいですし、 うまくいっていないところを見つけやすいです! ここで生成されたコンテンツではinputのテキストボックスに入力しても タスクを追加できませんでした。なので、追加できるように修正してもらいます。 追加ボタンを押してもタスクが追加されません inputにテキストを入力した状態で、追加ボタンを押すと、タスクが下に表示されるようにしてください。 追加できるようにしてくれました。 もうこれで簡単なTodoアプリとしての機能は完成しました! 一瞬で作れてしまうので便利ですね! ただ、このままだとデザインがあまりイケてないので、 デザインをいい感じにしてくれってお願いしてみます。 デザインをもっといい感じにしてください * チェックボックスとタイトル、削除ボタンの間隔が狭すぎます * タスク間の間隔が狭いです * 削除ボタンが大きすぎます * チェックボックスが小さすぎます * タイトルが小さすぎます いい具合に綺麗にしてくれました。 この状態でも良いんですが、タスクのステータス(Todo、In Progress、Done)があると もっと良いですよね。なのでコードを修正してもらいます。 ステータスでTodo, In Progress, Doneを持てるようにしてください。 なので、チェックボックスでの管理はやめて、ステータスごとにまとめて3レーンで表示するようにしてください。 ちゃんと作ってくれました。 ステータスの状態遷移は矢印ボタンを押して遷移できるようです。 生成されたテキストを見てみると、以下のように言っているのでこちらもやってもらいましょう。 さらに機能を追加したり、デザインを調整したりする必要がある場合は、お知らせください。例えば、ドラッグアンドドロップ機能の追加や、タスクの編集機能の実装なども可能です。 ドラッグアンドドロップ機能の追加と、タスクの編集機能の実装をお願いします ドラッグ&ドロップと編集ができるようになりました! 簡単なWebアプリであればめちゃめちゃ早く作ることができますね! (あと内部のコードなど気にしなければ…) SVG画像とかも作れるらしい 冒頭の公式YouTubeでも最初に作っていましたが、SVG画像が作れるようです。 ということで、このWebアプリのアイコンを作ってもらいましょう。 ポメラニアンを使ったSVGアイコンを作ってください ポメラニアンの特徴を活かしたデザインをお願いします まぁまぁまぁまぁ、許容できる範囲ですね。プロンプトも雑なのでこの程度だと思っています。 これを作成したWebサイトに埋め込んでもらいます。 先ほどのTodoリストのWebサイトにこのアイコンを埋め込んでください。 タイトルの左側に丸く表示するようにしてください。 いい感じに装飾できました! デモ 完成したTodoアプリを実際に動かしてみたいと思います! まとめ 今回はClaude 3.5とArtifactsを使ってWebアプリを作ってみました。 ArtifactsはWebアプリだけでなく、スライド作成やSVG画像作成などさまざまな用途に使用することができます。 公式サイトにも書いてありますが、Artifacts機能が追加されたことで、Claudeがただのチャット用AIから共同作業環境へと進化したと感じました。 さまざまなアイデアを会話を通じてリアルタイムで迅速で形にすることができるのは見ていて楽しいですし、効率的にアウトプットできるなぁと思いました。 今回紹介したArtifactsはFeature Previewということで、正式リリースされていません。 こちらのブログも2024年6月末時点での情報となっております。 今後、機能変更が入る可能性がありますので注意が必要です。 ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 カジュアル面談も随時受付中! ニフティに興味をお持ちの方は キャリア登録をぜひお願いいたします! キャリア登録 connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する
はじめに こんにちは。ニフティ株式会社入会システムチームのtakahataです。 GitHub ActionsからCodeBuildをAWS CLIで呼ぶ構成から、GitHub Actions self-hosted runnerでCodeBuildを呼ぶ構成とECS on Fargateを実行する構成への変更を検証した話を共有します。また、ECSを利用したself-hosted runnerの検証も実施しました。 背景 所属しているチームのWebサービスではデプロイ前のリグレッションテストとして、E2EライブラリであるPlaywrightを利用して検証環境上でブラウザテストを実行しています。 検証環境では外部からのアクセスを遮断していますが、GitHub ActionsのIPアドレスはIPアドレスリストのどのIPアドレスからアクセスされるかわからないため( 参考 )、GitHub Actionsが持つホストからテストを実施することができません。 そのため、現在は、GitHub Actions から AWS CLI を使ってAWS CodeBuildを実行することによって、Playwrightを実行していました。CodeBuildはVPC内で実行できるため検証環境にアクセスすることができます。 課題 現状の構成ではGitHub Actionsから見るとCodeBuildの実行に成功したことしかわからず、テストに成功したかどうかはCodeBuild上でしか確認できませんでした。 ニフティではAWSをマルチアカウントで運用しているため、テストが終わるとAWSアカウントにログインしてCodeBuildからテスト結果のレポートを確認する必要があります。 また、テスト結果をGitHub側に連携することができていないため、テスト結果を元にプルリクエストのマージを許可するといった運用ができていませんでした。 解決手段 上記課題について2通りの方法で検証を実施しましたので、以降は概要と手順について説明します。 CodeBuildのマネージド型のself-hosted runnerを利用する ECS on Fargateの実行環境を構築する CodeBuildのマネージド型のself-hosted runnerを利用する GitHub Actionsのself-hosted runnerとは、GitHubがホストしているサーバを使わず利用者が用意したサーバ上でGitHub Actionsのワークフローを実行する仕組みです。2024年4月にマネージドなself-hosted runnerの実行環境としてCodeBuildを容易に利用できるようになりました( 参考 )。 この対応を受け、AWS CLIでCodeBuildを実行する構成からself-hosted runnerで実行する構成に変更することを検証しました。 VPC上で動作するCodeBuildを実行環境としてGitHub Actionsのワークフローを実行することにより、GitHubのプルリクエスト等の機能と連携しながらGitHub ActionsとしてVPC内でテストを実行することができます。 対応手順 https://docs.aws.amazon.com/ja_jp/codebuild/latest/userguide/action-runner.html を元に設定をしました。対応手順は大きく2つあります。 ①CodeBuildプロジェクトを作成する ②GitHubのワークフローを作成する ①CodeBuildプロジェクトを作成する CodeBuildのソースに利用したいGitHub Actionsのリポジトリを指定します。 ウェブフックイベントを以下のように設定します。ここでは複数のリソースは指定できずプライマリリソースとして上記で指定したGitHubのイベントを設定できるようです。 公式ドキュメントによるとGitHub Actionsで実行されるワークフローは workflow_jobのqueuedイベントによって実行されますので( 参考 )、フィルタグループのイベントタイプに WORKFLOW_JOB_QUEUED を指定します。 その他設定としてイメージファイルはPlaywrightの実行要件に指定されているUbuntuを指定しました。 それ以外にはVPC経由で実行したいためVPCやセキュリティグループを指定しました。 ②GitHubのワークフローを作成する CodeBuildのbuildspec.ymlファイルをGitHub Actions用のyamlファイルに移植しました。 self-hosted runnerの指定をするためにrunnerにCodeBuildのプロジェクト名を指定します。 name: Playwright Tests (Self-Hosted Runner) on: push: ... jobs: test: runs-on: codebuild-Codebuildのプロジェクト名-${{ github.run_id }}-${{ github.run_attempt }} ... 実行結果 GitHubが提供するホストと特に違いなくワークフローを実行することができ、VPC内部のサーバにもアクセスすることができました。 GitHub-hosted runnerとの違いとしては、CodeBuildがself-hosted runnerの実行環境を構成するために90秒ほど待ち時間が発生していました。 CodeBuildがself-hosted runnerを構成する実行ログの様子 Downloading GHA self-hosted runner binary % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0 13 179M 13 24.9M 0 0 63.5M 0 0:00:02 --:--:-- 0:00:02 63.4M 55 179M 55 100M 0 0 73.7M 0 0:00:02 0:00:01 0:00:01 73.7M 100 179M 100 179M 0 0 80.5M 0 0:00:02 0:00:02 --:--:-- 80.6M Configuring GHA self-hosted runner -------------------------------------------------------------------------------- | ____ _ _ _ _ _ _ _ _ | | / ___(_) |_| | | |_ _| |__ / \\ ___| |_(_) ___ _ __ ___ | | | | _| | __| |_| | | | | '_ \\ / _ \\ / __| __| |/ _ \\| '_ \\/ __| | | | |_| | | |_| _ | |_| | |_) | / ___ \\ (__| |_| | (_) | | | \\__ \\ | | \\____|_|\\__|_| |_|\\__,_|_.__/ /_/ \\_\\___|\\__|_|\\___/|_| |_|___/ | | | | Self-hosted runner registration | | | -------------------------------------------------------------------------------- # Authentication √ Connected to GitHub # Runner Registration √ Runner successfully added √ Runner connection is good # Runner settings √ Settings Saved. Running GHA self-hosted runner binary √ Connected to GitHub GitHub ActionsがCodeBuildの実行を待っている様子 ECS on Fargateの実行環境を構築する 続いてECS on Fargateを実行する方法になります。こちらの方法では上記CodeBuildで指定していたWebhookの向き先となるサービスを自身で構築し、構築したサービス上でECSタスクの起動とself-hosted runnerへの登録をする必要があります。 また、常時ECSタスクを実行する必要はないため、コスト削減のために必要に応じてタスクを実行するように設定しています。 対応手順 AWSが提供するサービスを組み合わせて構築しています。手順は以下のようになります。 ワークフロー実行に合わせてAPI GatewayにWebhookでHTTPリクエストを実行するようGitHub上で設定 API GatewayのAWS統合でStep Functionsを実行 ※Step Functionsを利用することでタスク起動失敗時に再実行できるようにしています Lambdaを実行しWebhook検証とself-hosted runnerへの登録に必要なトークンを取得 ECSタスクを実行 ECSタスク上のコンテナでself-hosted runnerへの登録 ECSタスク上のコンテナを利用してブラウザテストを実行 ①ワークフロー実行に合わせてAPI GatewayにWebhookでHTTPリクエストを実行するようGitHub上で設定 リポジトリのSettings→WebhooksからAdd Webhookで追加できます。 Payload URLにAPI Gatewayのエンドポイント、Content typeにapplication/json、Secretに後にWebhook検証をするためのランダムな文字列を入力します。 トリガーにWorkflow jobsのみ選択します。 ②APIGatewayのAWS統合でStep Functionsを実行 Step Functionsを実行するようAPIGatewayを実行します。 ここで、統合リクエストのマッピングテンプレートを適切に設定する必要があります。ここではHTTPリクエストヘッダに含まれる「X-Hub-Signature-256」とHTTPリクエストボディを文字列にエスケープしたJSONに変換しています。「X-Hub-Signature-256」はWebhookの検証に必要です。 { "stateMachineArn": "arn:aws:states:[Step FunctionsのARN]", "input" : "{\\"secret\\": \\"$input.params().get('header').get('X-Hub-Signature-256')\\", \\"body\\": $util.escapeJavaScript($input.json('$')) }" } Step Functionsは以下のようなフローになっています。Webhookからイベントを特定し、イベントがqueued(ワークフロー作成された際に発行されるイベント)である場合のみLambda実行に分岐し、それ以外のイベントの場合はなにもせずに終了します。 ③Lambdaを実行しWebhook検証とself-hosted runnerへの登録に必要なトークンを取得 作成したAPI Gatewayはどこからでもアクセスできてしまうため、最悪の場合大量アクセスにより大量にECSタスクが実行できてしまいます。そのため、WebhookがGitHub Actionsから呼ばれたものか検証する必要があります。 詳細については https://docs.github.com/ja/webhooks/using-webhooks/validating-webhook-deliveries を参照ください。 API Gatewayから取得されたHTTPリクエストボディに ①で設定したSecretを利用してハッシュ値を計算し、「X-Hub-Signature-256」と一致しているか検証しています。SecretはSSM Parameter Store等で管理します。 ここで単純にLambdaのeventでHTTPリクエストボディを渡してしまいますと各Lambdaランタイム言語の辞書型に変換されてしまいますので、文字列として渡してあげる必要があります。一度辞書型になってしまうと文字列に再変換したときに検証がうまくいきませんでした。 また、self-hosted runnerに登録するには一時的なトークンを取得する必要があります。トークンを発行するにはREST APIを実行する必要があります( 参考 )。REST APIの実行に必要なGitHubのPersonal Access TokenもSSM Parameter Storeから取得します。 ④ECSタスクを実行 ECSタスクを実行します。この際に③で取得したトークンを連携します。 ⑤ECSタスク上のコンテナでself-hosted runnerへの登録 ECSタスク上でトークンを利用してself-hosted runnerへ登録を行います。 詳細については https://docs.github.com/ja/actions/hosting-your-own-runners/managing-self-hosted-runners/adding-self-hosted-runners を参照ください。 コンテナイメージ上で依存関係のインストール、設定更新、self-hosted runnerへの登録を用意されたシェルスクリプトを使って実行します。 詰まったポイントとしては実行者をrootユーザーと一般ユーザーで実行を分ける必要があること、今後GitHub Actions上で利用するコマンドが使えるようにしておくことです。 今回のケースではgit checkoutをするためにgitのパッケージが必要でした。また、一般ユーザーからsudoする場合にパスワードが求められないようにするためNOPASSWDの設定であったり、ホームディレクトリの作成が必要であったりしました。 # サンプル FROM ubuntu:24.04 RUN apt update && apt-get -y install curl sudo git RUN mkdir /actions-runner RUN useradd runner -m RUN echo 'runner ALL=NOPASSWD: ALL' >> /etc/sudoers RUN chown runner /actions-runner USER runner WORKDIR /actions-runner RUN curl -o actions-runner-linux-x64-2.317.0.tar.gz -L <https://github.com/actions/runner/releases/download/v2.317.0/actions-runner-linux-x64-2.317.0.tar.gz> RUN tar xzf ./actions-runner-linux-x64-2.317.0.tar.gz USER root RUN ./bin/installdependencies.sh USER runner COPY ./register.sh /actions-runner/ CMD [ "bash", "/actions-runner/register.sh" ] register.shでは以下のように設定しています。必要なオプションを指定しないとインタラクティブに入力を求められますのでコマンド内で全て指定します。 ここで「 --ephemeral 」オプションをつけることでタスク実行完了時に自動でself-hosted runnerからの登録を解除してくれるため、使い回されることなく使い捨てでコンテナを利用できます。 #!/bin/sh NOW=`date '+%Y%m%d%H%M%S'` ./config.sh \\ --url $GITHUB_URL \\ --token $TOKEN \\ --ephemeral \\ --disableupdate \\ --name runner_$NOW \\ --runnergroup Default \\ --labels self-hosted \\ --work _work ./run.sh ⑥ECSタスク上のコンテナを利用してブラウザテストを実行 ブラウザテストの実行はCodeBuildと同じですので割愛します。 実行結果 結果についてもCodeBuild同様特に問題なく動作することができましたので割愛します。 まとめ 今回VPC内部でPlaywrightをしたいという課題に対して、「CodeBuildのマネージド型のself-hosted runnerを利用する」「ECS on Fargateの実行環境を構築する」という2通りの手段を検証しました。 これまでself-hosted runnerをVPC内で実行するためにはEC2やECS等で環境を用意する必要がありましたが、self-hosted runnerにマネージドで対応したCodeBuildを利用することで簡単に実行環境を構築することができましたので、構築の手軽さという点ではCodeBuildを使う利点があると思いました。 一方で、ECS on Fargateの実行環境を構築する手法は構築が大変であるものの、コストの点で利点があります。東京リージョンのx86アーキテクチャで比較するとCodeBuildの最も安価であるgeneral1.smallが1分あたり0.005USDであるのに対して( 参考 )、同等スペックの2vCPU、メモリ4GBのFargateは(0.05056×2vCPU+0.005534×4GB)/60分 = 0.002054USDと単純計算ではありますが半額程度のコストで実行できます。またFargateの場合はより低いスペックでも稼働できますのでスペックを必要としない場合はさらにコスト削減できると思います。 実際にself-hosted runnerを構築することでマネージド型のありがたみを時間しました。両手法についてぜひ試してみてください。 ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 カジュアル面談も随時受付中! ニフティに興味をお持ちの方は キャリア登録をぜひお願いいたします! キャリア登録 connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する
この記事は、リレーブログ企画「チーム紹介」の記事です。 ニフティでシニアエンジニアをしている伊達です。今回は、私がEMをしている第一開発チームを紹介いたします(伊達については 社員インタビュー をご覧ください)。 第一開発チームとは 第一開発チームはいくつかの役割を持っています。 一つ目は、 @niftyトップページ 、 @nifty天気予報 、 @niftyビジネス といった@niftyの接続会員のお客様向けのポータルサイトや有償コンテンツ、あるいは ニフティキッズ のような広く子どもたちのためのサイトの開発と運用をしています。 二つ目は、静的なサイトを運用するためのCMS基盤の提供です。@niftyトップページはNext.jsのアプリケーションとして開発しているのに対して、こちらはコンテンツの管理や静的ページの生成の機能をニフティ社内のサイト運用者向けに提供しています。 こちらは旧来のCMS基盤から新基盤への移行が進んでおり、以下の記事に詳しいです。 自社製CMSで動かしていたサイトをmicroCMSへ移行した話 三つ目は、エンジニア向けのツールや基盤の提供です。GitHub、Notion、Zapier、Pingdomといったエンジニアの業務に必要なSaaSの管理や利用促進を行ってます。 最後に四つ目として、全社へのSRE推進も行っています。詳しくはインタビュー記事をご覧ください。最近はPagerDutyを重要なサービスに導入して障害からの復旧を早くする活動などを行っています。 【インタビュー】ニフティのSREに聞く!インターネット黎明期から安心・安全を体現してきたニフティが2023年に取り組んでいるSREとは?【SRE前編】 レガシーとの戦い やっていることは多岐に渡っていますが、ここ数年行っていることは「レガシーなシステムや仕組みを新しいシステムや仕組みに変えていくことで、素早く開発してお客様に価値を届けられるチームにしよう」ということです。 @niftyトップページ、 @search 、@nifty天気予報などは、2000年代に作られたシステムを適宜メンテナンスしながら使ってきました。サイトを運用するという面では問題なかったのですが、Core Web Vitalsのようなユーザ体験の指標を改善するには最近のフロントエンド技術の採用が近道ですし、機能開発を素早く行うにはGitHub/GitHub Actionsを活用した開発・CI/CDフローに適したシステムである必要がありました。こういった技術的負債の解消のため、チーム内で要件定義や技術選定から行って順次新しいシステムに置き換えています。 結果、クラウドにはAWS、フロントエンドにはReact/Next.js/TypeScript、バックエンドにはGo、IaCにはTerraformを採用することが多く、チーム内での共通の技術スタックが徐々に整いつつあります。 システムは作ったときからレガシー化が始まります。せっかく刷新したシステムが誰もわからない、手を入れられない代物にならないようにする取り組みもしています。 ドキュメントの面ではDesign Docs、ADRを作るようにしており、なるべくドキュメント作成を軽量にしながら意図を残そうとしています。 ADRについては以下の記事を参照ください。 意思決定を記録するArchitecture Decision Record (ADR)の話 さて、今回はチームの役割と最近の取り組みについて紹介しました。また機会があればプロダクトやメンバーについて紹介したいと思います。 次回のチーム紹介記事は同じ会員システムグループの第二開発チームの松尾さんです。 ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 カジュアル面談も随時受付中! ニフティに興味をお持ちの方は キャリア登録をぜひお願いいたします! キャリア登録 connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する
はじめに Windows 運用しているとPowersShellでスクリプトを書くことがあります。 スクリプトを書くなら、ユニットテストは自動化したいですよね? そこで、PowerShellの標準モジュールであるPesterについて紹介します。 現在Pesterはv5 が最新バージョンですが、古いPowersShell(5.x.x系など)はv3がインストールされています。 構文や使える機能が若干異なるので、利用する前に必ず確認しましょう。 Pesterについて 公式ドキュメントは以下を参照してください。 V5: https://pester.dev/docs/quick-start V4: https://pester.dev/docs/v4/quick-start V3: https://github.com/pester/Pester/wiki/Should-v3 ちなみにPesterは「しつこくせがむ」「うるさくせがむ」みたいなニュアンスらしいです。 「全てがgreenになるまで根気強くテストせよ」みたいな意味なんですかね。 Pesterの使い方 pesterのバージョン確認のコマンドは以下の通りです。 今回は5系を使用します。 ❯ Get-Module -ListAvailable -Name Pester Directory: C:\Users\sci02118\Documents\PowerShell\Modules ModuleType Version PreRelease Name PSEdition ExportedCommands ---------- ------- ---------- ---- --------- ---------------- Script 5.6.0 Pester Desk {Invoke-Pester, Describe, Context, It…} ディレクトリ構成は以下の通りです。 │ ├ Functions.ps1 └ Functions.Tests.ps1 テスト実行時は以下のようにします。 Invoke-Pester .\Functions.Tests.ps1 各ファイルの中身は以下の通りです。 ・Functions.ps1 # 関数やオブジェクトを宣言するファイル function Get-Planet ([string]$Name = '*') { $planets = @( @{ Name = 'Mercury' } @{ Name = 'Venus' } @{ Name = 'Earth' } @{ Name = 'Mars' } @{ Name = 'Jupiter' } @{ Name = 'Saturn' } @{ Name = 'Uranus' } @{ Name = 'Neptune' } ) | ForEach-Object { [PSCustomObject] $_ } $planets | Where-Object { $_.Name -like $Name } } ・Functions.Tests.ps1 # 同じディレクトリにある同名ファイルを呼び出して関数をImportする BeforeAll { . $PSScriptRoot\Functions.ps1 } # テスト定義 Describe 'Get-Planet' { It 'Given no parameters, it lists all 8 planets' { $allPlanets = Get-Planet $allPlanets.Count | Should -Be 8 } } テスト定義については、書き方の構文が決まっています。 # Desctibe はテストの説明です。基本的にはどのメソッドをテストするかが書かれます。 Describe 'Get-Planet' { # It はテストケースの説明です。 It 'Given no parameters, it lists all 8 planets' { $allPlanets = Get-Planet # テスト結果を比較します。 # Should の右側に期待する値を書きます。 # 今回の場合、実行結果として8つのオブジェクトが返ってくることを期待しています。 $allPlanets.Count | Should -Be 8 } } テスト結果を取得するshoudについてはバージョンによって構文が異なります。 公式ドキュメントを確認して、適切な値で書いてください。 実行するときは Invoke-Pester コマンドを使います。 Invoke-Pester .\Functions.Tests.ps1 Describing Get-Planet [+] Given no parameters, it lists all 8 planets 52ms Tests completed in 52ms 試しに失敗するテストを書いてみます。 Functions.Tests.ps1を以下の通り修正します。 # 同じディレクトリにある同名ファイルを呼び出して関数をImportする BeforeAll { . $PSScriptRoot\Functions.ps1 } # テスト定義 Describe 'Get-Planet' { It 'Given no parameters, it lists all 8 planets' { $allPlanets = Get-Planet $allPlanets.Count | Should -Be 8 } } Describe 'Get-Planet-Failed' { It 'Given no parameters, it lists all 6 planets' { $allPlanets = Get-Planet # 要素数を6に変更(本来の返り値は8) $allPlanets.Count | Should -Be 6 } } 実行してみます。 Invoke-Pester .\Functions.Tests.ps1 Describing Get-Planet [+] Given no parameters, it lists all 8 planets 71ms Describing Get-Planet-Failed [-] Given no parameters, it lists all 8 planets 92ms Expected: {6} But was: {8} 19: $allPlanets.Count | Should Be 6 at <ScriptBlock>, C:\Users\sci02118\dev\pester\Functions.Tests.ps1: line 19 Tests completed in 164ms Passed: 1 Failed: 1 Skipped: 0 Pending: 0 Inconclusive: 0 2個目のテストが失敗していることがわかりますね。 コードブロックだとわかりにくいですが、実際のpowershellはcolor表示してくれるのでだいぶ見やすくなっています。 おわりに 今回はpowershellの標準モジュールであるPesterの使い方について紹介しました。 ユニットテストを実施する時にぜひ使ってみてください。 ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 カジュアル面談も随時受付中! ニフティに興味をお持ちの方は キャリア登録をぜひお願いいたします! キャリア登録 connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する
ニフティ株式会社でシニアエンジニアしています芦川です。今回は、最近調べた開発生産性の中で語られるフロー状態について書きます。 要約 エンジニアの開発生産性を向上させるためには、「開発集中時間」を確保することが不可欠です。この集中時間とは、エンジニアが中断なく作業を遂行できる時間のことで、これにより作業に没頭できているフロー状態に入ることができます。フロー状態に入るには、コンテキストスイッチをなくすことが重要であり、1つの実践例では、slackやメールの未読表示をOFFにすることで集中時間を確保できます。 開発集中時間とは 開発集中時間とは、エンジニアが中断なく作業を遂行でき、フロー状態となる時間のことを指します。フロー状態とは、作業に没頭し、時間の経過を忘れるほどの集中状態を意味します。 開発者の生産性は複数の指標を持って測るべきと書いてある、 「SPACE」フレームワーク の中でもフロー状態の重要性について触れられています。 Some research associates productivity with the ability to get complex tasks done with minimal distractions or interruptions. 2 This conceptualization of productivity is echoed by many developers when they talk about “getting into the flow” when doing their work —or the difficulty in finding and optimizing for it, 一部の研究では、開発生産性を『気を散らせず中断を最小限に抑えながら複雑なタスクを完了する能力』と関連付けています。この生産性の概念については、多くの開発者が、フロー状態に入り最適化することの難しさを共感しています。」 h ttps://queue.acm.org/detail.cfm?id=3454124 この状態に入るためには、少なくとも1時間以上の連続した作業時間が必要です。例えば、「1時間の作業+1時間のMTG+1時間の作業」と「2時間の作業+1時間のMTG」では、後者の方が集中度が高くなります。つまり、作業時間を1日の中でどれだけ確保できるか、ではなく、どれだけ連続した時間で確保できるか、ということになります。 「 なぜ、エンジニアの”フロー状態”は見落とされるのか? 継続的なフロー状態が開発生産性を高める 」の中でさらにまとめられている記述があるのでご参考ください。 つまり、ミーティングとミーティングの間に1時間が2つある状態の開発生産性と、2時間まとまった空きがある状態での開発生産性は等価ではありません。その時間の連続性が非常に重要です。 https://codezine.jp/article/detail/19022 コンテキストスイッチの影響 エンジニアが現在取り掛かっているタスクの作業中に他のタスクを行うと、コンテキストスイッチが発生し、脳にストレスがかかります。これにより集中が途切れたり、あとあと疲労を感じやすくなったりするかと思います。以下のような要因がコンテキストスイッチを引き起こします。考えられる要因としては数限りなくあると思います。 Slackやメールの未読数表示を気にしてしまい、ついついタスクを中断し、そちらを読んでしまう。 デスクトップ通知に気を取られてしまう。 オフラインで急に声をかけられてしまい、タスクが中断する。 システムから通知されるメッセージが常に多く気になってしまう。 急なMTG招集があり、タスクを中断する。 トラブルが発生し、そちらの対応を優先することになる コンテキストスイッチをまるでなくすには、1人でそもそも完結する仕事の仕方をしていなくてはなりませんが、チームで開発していたり、システムの運用保守もする立場では、ある程度その時に必要なコミュニケーションや、急なアラート対応は見過ごすわけにはいきません。 とはいえ、日々、 工夫することで、自身やチームの開発集中時間を確保することはできる と思います。 コンテキストスイッチを減らす方法 ここでは、私が実践している、しようとしている方法をいくつか紹介します。 Slackの未読表示や通知の無効化 デスクトップアプリのSlackの未読表示や通知を無効にします。これにより、作業中に通知が気になって中断することがなくなりました。何名かのメンバーにも試してもらいましたが反応は上々です。Windowsでは以下のように設定できます。 注意として、数時間全くSlackを見ないようになってしまっては、緊急のシステムアラートや連絡について無頓着になってしまうので、それはだめということです。私は30分-1時間の作業の間に意図的な休憩時間を5分設け、その間にSlack通知を確認するようにしています。(フロー状態と言っても適切な休憩時間は取ってくださいね!) チームでの改善策 システムアラートを見て常に作業が中断してしまうようであれば、システムアラートの量やレベルの最適化がもしかしたらできてないこともありそうです。つまり、INFOレベルの通知の量が多かったり(都度見る必要はない)、調査や対応不要なシステムアラートもCRITICALレベルで通知しているかもしれません。(WARNレベルでよい)このあたりは、SREでのトイルマネジメントにも共通するところであり優先度をあげて対応したほうがよいと思います。 もう1つ、これから実践してみようと思うことに、MTGのクラスタリング化があります。こちらはまだ実施前ですが、会議を 2 ~ 3 時間の枠にブロックし、残りの時間はMTGを入れずに作業時間に充てるようにします。これをチームとして意図的に設定します。例えば、スクラム開発では、プランニングやタスクリファインメントなどイベントが数多くありますが、それらは小分けにせず(適切な休憩時間を持って)集中的に行うようにし、また、開発以外のチーム会など開催時間も午後のど真ん中にやるのではなく、1日の最後にするなど、連続する作業時間の確保については組織的に工夫できるところはあると思います。 まとめ 今回はフロー状態について書きました。エンジニアの開発生産性を向上させるためには、開発集中時間を確保し、コンテキストスイッチを減らすことが重要です。個人としてできる対策や、チーム全体での改善策を実践することで、より効率的で満足度の高い開発環境を作りましょう。そして生産的で充実したエンジニアライフを送りましょう! We are hiring! ニフティでは、さまざまなプロダクトへ挑戦するエンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトよりお気軽にご連絡ください! ニフティ株式会社採用情報 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering
こんにちは。NIFTY engineeringブログ運用チームです。 ブログ運用チームでは、ニフティのエンジニアについての情報を世の中に広めるための活動をしています。 その活動の一環として、リレーブログを実施します! リレーブログ第一弾のテーマは「チーム紹介」です。 本記事に、チーム紹介記事のリンクをまとめていきますので、ぜひチェックしてください。 また、既にチーム紹介記事が投稿されているチームもありますので、そちらもあわせてチェックしていただければと思います! 基幹システムグループ 入会システムチーム サービスインフラチーム 課金システムチーム 会員システムグループ 第三開発チーム インフラシステムグループ インフラチーム チーム紹介記事は以下のスケジュールで投稿予定です。お楽しみに! 投稿予定 チーム名 2024年6月 会員システムグループ 第一開発チーム 2024年7月 会員システムグループ 第二開発チーム 2024年8月 インフラシステムグループ 業務支援チーム 2024年9月 インフラシステムグループ 情報システムチーム ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 カジュアル面談も随時受付中! ニフティに興味をお持ちの方は キャリア登録をぜひお願いいたします! キャリア登録 connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する
参加登録はこちらから (connpass)2つのスクラムチームの調和的な協働・連携について ニフティのスクラムトーク vol. 3 イベント概要 NIFTY Tech Talkは、ニフティ株式会社の社員が主催するトークイベントです。 本イベントでは、ニフティグループの社員が業務を通じて学んだことを発信しています! テーマ NIFTY Tech Talkでスクラムにテーマを絞った「ニフティのスクラムトーク」シリーズが始まりました。 Vol 3となる今回は、社内のスクラムチームが協力会社との連携を円滑に行うためのノウハウを紹介します。 スクラム開発では、自律的で生産性の高いチーム運営が重要です。 コミュニケーションの取り方、優先順の決め方、POとの連携など、協力会社との協働を最大限に活かして、より生産性を高められるよう実践していることについてお話します。 協力会社との融和を図り、高パフォーマンスを発揮するチーム作りのヒントが得られるはずです。 スクラム開発に携わる方・SES契約を検討している方は、ぜひご参加ください。 過去のニフティのスクラムトークはこちら スクラムマスターの技を磨く! ニフティのスクラムトーク vol 1 / 動画 ( Youtube ) スクラムマスターによるチーム改善LT! ニフティのスクラムトーク vol 2 / 動画 ( Youtube ) 開催概要 ※オンライン配信のみです 日程:6月18日(火)12:00〜13:00 YouTube Liveで配信 こんな方におすすめ チームの体制について興味がある方 協力会社とのスクラムの進め方に興味がある方 スクラムイベントのやり方・連携について興味がある方 タイムテーブル 時間 コンテンツ 12:00 – 12:10 オープニング 12:10 – 12:50 LT1 ニフティと協力会社のスクラムの形 12:50 – 13:00 クロージング 登壇者プロフィール 南 奈歩(登壇者) ニフティ株式会社 基幹システムグループ 入会システムチーム 新卒入社5年目。@nifty光をはじめとする光コラボシステムの開発・運用・運用改善・刷新などを担当し、スクラムマスター兼開発者として日々活動しています。 高田 渉(登壇者) ニフティ株式会社 基幹システムグループ 入会システムチーム @nifty光などの開発チームのサブチームリーダーを担当しています。 ニフティグループでは一緒に働く仲間を募集中です 新卒採用、キャリア採用を実施しています。ぜひ リクルートサイト をご覧ください。 ニフティエンジニアが業務で学んだことやイベント情報を エンジニアブログ にて発信しています! ニフティエンジニアのX(旧Twitter)アカウント NIFTY Tech Talkのことや、ニフティのエンジニアの活動を発信していきます。 https://twitter.com/NIFTYDevelopers アンチハラスメントポリシー 私たちは下記のような事柄に関わらずすべての参加者にとって安全で歓迎されるような場を作ることに努めます。 社会的あるいは法的な性、性自認、性表現(外見の性)、性指向 年齢、障がい、容姿、体格 人種、民族、宗教(無宗教を含む) 技術の選択 そして下記のようなハラスメント行為をいかなる形であっても決して許容しません。 不適切な画像、動画、録音の再生(性的な画像など) 発表や他のイベントに対する妨害行為 これらに限らない性的嫌がらせ 登壇者、主催スタッフもこのポリシーの対象となります。 ハラスメント行為をやめるように指示された場合、直ちに従うことが求められます。ルールを守らない参加者は、主催者の判断により、退場処分や今後のイベントに聴講者、登壇者、スタッフとして関わることを禁止します。 もしハラスメントを受けていると感じたり、他の誰かがハラスメントされていることに気がついた場合、または他に何かお困りのことがあれば、すぐにご連絡ください。 ※本文章はKotlinFest Code of Conductとして公開された文章( https://github.com/KotlinFest/KotlinFest2018/blob/master/CODE-OF-CONDUCT.md )を元に派生しています。 ※本文章はCreative Commons Zero ライセンス( https://creativecommons.org/publicdomain/zero/1.0/ ) で公開されています。
はじめに 相変わらずCDKを触る機会が多めな宮本です。今回は比較的最近リリースされたばかりのCloudFront KeyValueStoreのCDK管理についての記事です。待望のCloudFront Functionsで使えるデータストアということで無邪気に喜んでましたが、IaCで管理しようとすると結構癖がありました。 CloudFront KeyValueStore Amazon CloudFront KeyValueStoreは、CloudFront Functions内で利用可能なその名の通りキーバリュー方式のデータストアです。リリースされたのも昨年と、かなり新しいサービスです。 今までCloudFront Functions上で扱うデータは、CloudFrontから渡されるリクエストまたはレスポンス情報を除きコードに直書きする以外に保持する術がありませんでした。そのため、たとえばパスごとのリダイレクト情報を複数書こうとすればそれだけコード行数が膨大になり、コードの挙動そのものがどんどん下の方に追いやられ可読性が落ちるという問題がありました。 KeyValueStoreが登場したことで、CloudFront Functionsでもロジックとデータを完全に分離することが可能になりました。データとロジックを混在させなければならないという部分に正直辟易としていたので、この機能追加はとてもありがたいです。 詳しくは 公式ドキュメント や こちらの記事 をご覧ください。 コード 実装してみたコードの紹介です。PythonのCDKを用いて記述しています。 また、省略のためにCloudFront FunctionとKeyValueStoreの作成のみ実施しています。 import json import random import string from aws_cdk import aws_cloudfront as cloudfront # functionのコードにKVSのIDを埋め込むためにファイル読み込み cloudfornt_function_src_path = "src/index.js" with open(cloudfornt_function_src_path, "r") as file: file_content = file.read() # json形式のvalueを文字列化するためにファイル読み込み kvs_file_path = "kvs.json" with open(kvs_file_path, "r") as file: row_kvs_items = json.load(file) kvs_items = [ { "key": kvs_item["key"], "value": json.dumps(kvs_item["value"], separators=(",", ":")), } for kvs_item in row_kvs_items ] def rand_suffix(seed: str): """ kvs名のsuffix用ランダム文字列を生成する関数 seedを与えることでkvsのデータが変わらない場合は同じsuffixが生成されるようにする """ random.seed(seed) suffix = "".join(random.choices(string.ascii_letters + string.digits, k=4)) # seedをリセット random.seed() return suffix class CloudFrontStack(Stack): def __init__( self, scope: Construct, construct_id: str, **kwargs ) -> None: super().__init__(scope, construct_id, **kwargs) # KVSの初期値文字列作成 kvs_source = json.dumps({"data": kvs_items}) keyValueStore = cloudfront.KeyValueStore( self, "KeyValueStore", # KVSのsuffixとしてKVSに格納する値をseedとしたランダム文字列を生成 # 格納する値が変わった場合にKVSの名称を変更して再デプロイする key_value_store_name=f"kvs-{rand_suffix(kvs_source)}", source=cloudfront.ImportSource.from_inline(kvs_source), ) cf_func = cloudfront.Function( self, "CFFunc", key_value_store=keyValueStore, code=cloudfront.FunctionCode.from_inline( code=file_content.replace( "<KEY_VALUE_STORE_ID>", keyValueStore.key_value_store_id ) ), runtime=cloudfront.FunctionRuntime.JS_2_0, ) function_associations = [ cloudfront.FunctionAssociation( event_type=cloudfront.FunctionEventType.VIEWER_REQUEST, function=cf_func, ) ] CloudFront Functionsのコード import cf from "cloudfront"; const kvsId = "<KEY_VALUE_STORE_ID>"; // This fails if the key value store is not associated with the function const kvsHandle = cf.kvs(kvsId); async function handler(event) { const request = event.request; const key = request.uri; let value; try { value = await kvsHandle.get(key, { format: "json" }); } catch (err) { value = ""; } console.log(value) // 処理いろいろ return request; } KeyValueStoreの初期値を入れたjsonファイルの例 [ { "key": "/hoge", "value": { "url": "https://example.com/hoge", "statusCode": 301, } }, { "key": "/fuga", "value": { "url": "https://example.com/fuga", "statusCode": 302, } }, ] CDKによる管理 他のリソースと同じく、CDKを用いてCloudFront KeyValueStoreの操作が可能です。が、いくつか欠点もとい注意点があります。先に示したコードでは、その対策を入れています。 CloudFront Functions側からの指定がコード直書き以外不可 CloudFront Functions側からvalueをjson文字列として読み込むことはできるが、初期値のファイル投入時には必ず文字列として投入する必要がある CDKからはKeyValueStoreに保存されたデータの更新をする方法がない CloudFront Functions側からの指定がコード直書き以外不可 これについては、すでに クラスメソッドさんの記事 に対策がまとまっています。Cloudfront Functions内からKeyValueStoreにアクセスするために、IDを指定する必要があります。しかしこのIDはKeyValueStoreのリソースが作成されなければわからず、その上でコードに直書きする必要があるので、デプロイ時にコードに埋め込むように実装する必要があります。 CloudFront Functions側からvalueをjson文字列として読み込むことはできるが、初期値のファイル投入時には必ず文字列として投入する必要がある KeyValueStoreのValueがjsonとして解釈することができる場合、CloudFront Functions側でjsonを解釈したobjectとして読み込むことができます。しかし初期値として投入する場合は、あくまでvalue部分は文字列である必要があります。 文字で書くとわかりづらいですが↓ということです。 // 以下のようなデータをKeyValueStoreの初期値として入れたい場合は {"hoge":{"fuga":"hogefuga"}} // この形式のjsonファイルを用意する必要がある { "data": [ { "key": "hoge", "value": "{\"fuga\":\"hogefuga\"}" } ] } jsonファイルを使ってKeyValueStoreの初期値を管理することができるのは便利なのですが、valueにjsonを入れたい場合は文字列化する必要があるのが厄介です。折角jsonファイルで管理できると思ったのに、見づらくなってしまいます。このあたりはCDKなので、前処理としてjsonファイルを読み込んで文字列化することで解決できます。 一方で、valueに入れるのがただの文字列である場合は以下の変換は不要です。 ImportSource.from_asset() を使うことで、直にファイルを指定し読み込むことができます。ただし、この初期値を入れたファイルの内容を変更する予定があるのであれば、後述のエラー対策として、ファイルデータ自体は読み込みハッシュ値などを使ってseedとして利用することが良いと思います。 kvs_items = [ { "key": kvs_item["key"], "value": json.dumps(kvs_item["value"], separators=(",", ":")), } for kvs_item in row_kvs_items ] CDKからはKeyValueStoreに保存されたデータの更新をする方法がない これはさらに厄介です。例えばKeyValueStoreでリダイレクトルールを管理しようとした場合、IaCで管理するならルール自体もファイルに保存しておきたいです。しかし、このKeyValueStoreの初期値を決めたファイルに変更を加えた場合、次のようなエラーが発生して更新ができません。 CloudFormation cannot update a stack when a custom-named resource requires replacing. Rename <KVS名> and update the stack again. これについては、少々強引ですが初期値を変えるたびに毎回KeyValueStoreの名称を変えて作り直すと解決するようです。 参考にしたコード では毎回ランダムに名前を割り振っていましたが、初期値を定義するjsonファイルの内容をseedにすることで、値が変わらない限り無駄なデプロイが走らないようにすることが可能です。 ただし、この時完全にKeyValueStoreは作り直しになるため、もしコンソールやSDKから内部の値を変更していた場合は完全に失われてしまうためご注意ください。 また、今回はPythonでCDKを書いているため簡単にseedの指定ができます。一方でJavaScriptではデフォルトで乱数のseedを固定することができないため、追加でライブラリを入れる必要がありさらに一手間必要です。 さいごに 現時点でCloudFront KeyValueStoreをCDKで管理する方法についてまとめました。機能としてはありがたいのですが、若干引っかかるポイントが多く今後改善されると嬉しいです。 ちなみに今回はCDKで紹介しましたが、一応TerraformでもKeyValueStore自体の作成は可能です。ただし、初期値として投入できるファイルを指定することはできません。 issue は出ているので、早めに追加されると嬉しいです。 参考 Amazon CloudFront KeyValueStore – Amazon CloudFront CloudFront KeyValueStoreがリリース。CloudFront Functionsからキーバリューストアを利用可能に! | DevelopersIO AWS CDK v2.124.0 で CloudFront KeyValueStore の Functions への関連付けがサポートされました | DevelopersIO [New Resource]: Amazon CloudFront KeyValueStore · Issue #34512 · hashicorp/terraform-provider-aws Replacing custom paths results in CFN update error · Issue #2 · ssennettau/CloudFrontRedirector-Construct [Enhancement]: Add support for import_source for aws_cloudfront_key_value_store · Issue #36524 · hashicorp/terraform-provider-aws ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 カジュアル面談も随時受付中! ニフティに興味をお持ちの方は キャリア登録をぜひお願いいたします! キャリア登録 connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する
💡はじめに 🐘 Gradleの進化 🧩 課題解決 💻 Groovy DSL → Kotlin DSL → Version Catalog 💫 成果 💡はじめに こんにちは。ニフティ株式会社のLinです。 台湾出身のモバイルアプリエンジニアとして、社内で「マイ ニフティ」の Android および iOS 版の開発を担当しております。 今回は、Gradle の Groovy DSL → Kotlin DSL → Version Catalogへの移行をご紹介します。 🐘 Gradleの進化 Gradleは、Androidアプリの構築とビルドを管理するビルドシステムで、下記の機能があります: プロジェクトの構成管理 ライブラリの依存関係管理 リソースファイルの管理 プロダクションビルド、デバッグビルドの切り替え APKファイルのビルド マスコットはGradle君という名前の可愛い象です。 Android 開発における Gradle Script の記述方法には、Groovy DSL と Kotlin DSL の2つのパターンがあります。 Groovy DSL は従来からよく使われている記法で、シングルクォーテーションと括弧なしのダブルクォーテーションが特徴的です。 implementation 'androidx.room:room-rxjava2:$room_version' implementation "androidx.datastore:datastore-preferences:1.0.0" そして、2020年4月の Android Gradle Plugin 4.0.0 から、Kotlin DSL が導入されました。 括弧付きのダブルクォーテーションが特徴で、静的型付けによる型安全などのメリットがあります。 implementation("androidx.room:room-rxjava2:$room_version") しかし、Android の発展に伴い、Gradle の既存機能にはいくつかの課題がありました: 異なる記法がbuild.gradleファイルに混在しており、保守運用がやや複雑になってしまっている マルチモジュールの場合、複数のbuild.gradleに重複するコードを記述しなければならない バージョンアップする際に、変更箇所が散在してしまう どうしよう… Gradle君の悩みはどんどん大きくなった。 🧩 課題解決 課題解決のため、Gradle君はライブラリバージョン管理の改善を試し、二つ改善方法を見つけました: BuildSrc buildSrc/build.gradle に共通の部分を集約することができ、その中身は以前の build.gradle と同様の書き方です どのGradleバージョンにも使えます ただし、下記理由のため、一時対策として使われています 機能ごとの複数の依存関係をまとめるのは手間がかかります pluginsの集約はできません モジュール間のコンフリクトを完全に避けるのは難しい 拡張性に制限があります Version Catalog libs.versions.toml に共通のバージョン、依存関係、プラグインを集約することができ、その中身は以前の build.gradle とは異なります Gradle 7.4以上で導入された機能のため、バージョン制限があります Gradle 7.0 – 7.4 では Feature Preview、Gradle 7.0 以下では使えません 下記理由のため、恒久対策として使われています 機能ごとの複数の依存関係をまとめることができます [bundles] pluginsの集約もできます [plugins] Kotlin DSLのみで記述できるため、型安全などの利点があります Android Studioの新規プロジェクトでもVersion Catalogがデフォルトになったため、これからも業界の標準となっていくでしょう では、Gradle君と一緒にVersion Catalogを使ってみよう! 💻 Groovy DSL → Kotlin DSL → Version Catalog 移行作業には以下の2つのステップがあります: Kotlin DSLへの移行 下記ファイルの書き換え settings.gradle → settings.gradle.kts Groovy DSL → Kotlin DSL include ':app' ↓ include(":app") pluginManagementの追加 @Incubatingの警告が出る場合は、 @file:Suppress("UnstableApiUsage") も追加してください build.gradle(Project層) → build.gradle.kts(Project層) Groovy DSL → Kotlin DSL kotlin_version = '1.9.23' ↓ val kotlinVersion: String by extra("1.9.23") build.gradle(Module層) → build.gradle.kts(Module層) Groovy DSL → Kotlin DSL buildConfigの移行(AGP 9.0で廃止予定) Version Catalogへの移行 [versions] :すべてのライブラリのバージョンを集約します [libraries] :すべてのライブラリのパスを集約します BOMの集約もできます [bundles] :機能ごとの複数のライブラリをまとめます module (group:name) より group + name の方がおすすめです bundle = { group = "...", name = "...", version.ref = "..." } [plugins] :pluginsのclasspathとidを集約します [bundles] を活用すれば、必要な依存関係を1回のimplementationで一括して指定できます。 // libs.versions.toml [versions] room = "2.6.1" [libraries] androidx-room-compiler = { group = "androidx.room", name = "room-compiler", version.ref = "room" } androidx-room-ktx = { group = "androidx.room", name = "room-ktx", version.ref = "room" } androidx-room-runtime = { group = "androidx.room", name = "room-runtime", version.ref = "room" } [bundles] room = ["androidx-room-runtime", "androidx-room-ktx"] // build.gradle.kts(Module層) ... dependencies { ... // Room implementation(libs.bundles.room) ksp(libs.androidx.room.compiler) } そして、移行作業が完了したら、 Gradle Sync を忘れずに行いましょう。 💫 成果 これまでは各 Gradle ファイルに散在していたライブラリバージョン情報は、下記のようにVersion Catalogに集約しました: // libs.versions.toml [versions] activityCompose = "1.9.0" agp = "8.4.0" appcompat = "1.6.1" ... [libraries] androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" } androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" } ... [bundles] android-test = ["androidx-junit", "androidx-espresso-core"] androidx = ["androidx-core-ktx", "androidx-appcompat", "material", "androidx-constraintlayout-compose", "androidx-activity-compose", "androidx-lifecycle-runtime-ktx", "androidx-lifecycle-runtime-compose", "androidx-lifecycle-viewmodel-ktx", "androidx-lifecycle-viewmodel-compose", "androidx-navigation-compose", "androidx-datastore-preferences", "androidx-core-splashscreen", "androidx-swiperefreshlayout", "androidx-browser"] ... [plugins] android-application = { id = "com.android.application", version.ref = "agp" } dagger-hilt-android-plugin = { id = "dagger.hilt.android.plugin", version.ref = "hilt" } firebase-crashlytics = { id = "com.google.firebase.crashlytics", version.ref = "firebaseCrashlyticsGradle" } ... // build.gradle.kts(Project層) ... // buildscript { ... repositories { google() mavenCentral() } ... } plugins { alias(libs.plugins.android.application) apply false alias(libs.plugins.dagger.hilt.android.plugin) apply false alias(libs.plugins.firebase.crashlytics) apply false ... } ... // build.gradle.kts(Module層) ... plugins { id("com.android.application") id("org.jetbrains.kotlin.android") id("org.jetbrains.kotlin.plugin.serialization") id("dagger.hilt.android.plugin") id("com.google.devtools.ksp") ... } ... dependencies { // Kotlin implementation(libs.bundles.kotlin) // Compose implementation(platform(libs.androidx.compose.bom)) implementation(libs.bundles.compose) debugImplementation(libs.bundles.compose.debug) androidTestImplementation(libs.ui.test.junit4) // AndroidX implementation(libs.bundles.androidx) ... // Room implementation(libs.bundles.room) ksp(libs.androidx.room.compiler) ... } 今後は Version Catalog の [versions] セクションにバージョン情報を集約しているため、ライブラリのアップデートと管理がより簡単になりました。 ぜひVersion Catalogの便利さを体験してみましょう! ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する
はじめに 本番用Dockerfileには軽いからAlpine Linuxを使おう!という話はよく聞きますが、リスクがあるので何も考えずに使うのは避けましょう、というお話です。 Alpine Linuxを使う意味 イメージサイズの減少 「軽量」LinuxなのでこれがAlpine Linuxを使う最大かつ唯一の理由だと思います。 ただし現実的にイメージサイズに差があるか?というと、大きな差はないです。 debian-slim alpine ベース 28.77MB 3.09MB node:18 65.82MB 48.75MB python:3.12 46.7MB 19.59MB 確かにサイズ差はあるものの、その差は約20MB程度。 イメージサイズが影響するのは主にデプロイ速度ですが、この程度で現実的に影響が出るとは考えづらいです。 アプリケーションを載せるとそちらの方が大きなサイズを占めるため、より比率は低くなります。 Alpine Linuxの欠点 glibcがない ほとんどのLinuxディストリビューションは標準Cライブラリとしてglibcを採用していますが、Alpine Linuxはglibcを採用しておらず、代わりにmusl-libcを利用しています。 このため、以下のような問題を引き起こします。 バイナリが動作しない glibcに依存するバイナリは動作しない ex) 開発環境の豊富なDebianイメージでビルドしてAlpineイメージにコピーすると動作しない場合がある バイナリのビルドに失敗する glibcに依存するソースのビルドに失敗する PythonやNode.jsパッケージなどの内部でビルドするバイナリでも発生しえる パフォーマンス劣化の可能性がある glibcを使う場合より有意に低速になることがある 参考(少し古いですが…): https://superuser.com/questions/1219609/why-is-the-alpine-docker-image-over-50-slower-than-the-ubuntu-image 平たくいうと 互換性・パフォーマンスの担保ができない ということです。 これらの問題は個別に対応が必要で、例えばNext.jsのDockerサンプルでは互換レイヤーとしてlibc6-compatを別途導入しています。 https://github.com/vercel/next.js/blob/canary/examples/with-docker/Dockerfile またパッケージやランタイムをアップデートした後に新しく問題が発生することもあるため、アップデート時のリスクを増やすことにつながります。 apkパッケージを固定できない DockerイメージにOS提供パッケージを追加する場合、動作の再現性のためにパッケージバージョンは固定したいですよね。 しかしAlpine Linuxでは実質的に パッケージバージョンの固定が不可能です 。 Alpine Linuxの採用するapk(alpine package keeper)ではパッケージバージョンを指定することができるので、これを使えば良さそうですが、、、 RUN apk --no-cache add git=2.43.0-r0 jq=1.7.1-r0 しかしAlpineのパッケージは 最新版しか維持されません 。 リポジトリを見ると、同一パッケージは基本的に1バージョンしかないことがわかります。 http://dl-cdn.alpinelinux.org/alpine/v3.19/main/x86_64/ このため、バージョンを指定してしまうとある日突然イメージビルドに失敗するようになります。 つまり固定は実質不可能です。 GitHub Actionsで使用していたサードパーティアクションがこの問題を踏んでいて、突然動かなくなることがありました。 開発環境として使いづらい Alpine LinuxはDebian等と比べるとOS提供パッケージが少ないです。 このため開発環境としてはUbuntu/Debianベースイメージが使われることが多く、本番用イメージとの差分が大きくなってしまいます。これにより本番イメージでのみ発生する問題が多くなってしまう危険があります。 どうするのが良いか debian-slimを使う 上記の通りalpineとdebian-slimベースイメージの差はわずかなので、基本的にdebian-slimを使うようにすると良いかと思います。 開発環境ではdebian(full)ベースイメージを使うことで利便性との両立も可能です。パッケージバージョンを固定する場合の問題も起こりません。 distrolessを使う よりアグレッシブにイメージサイズを削りたい場合は、distrolessベースイメージを使用します。 これはalpineよりイメージサイズが小さく、かつglibcを採用しているので互換性問題が起こりづらいです。 ただし パッケージマネージャが存在しない シェルすらない コンテナ内へのログインが不可能 という割り切りがされているので、マルチステージビルドしたアプリをコピーしてくるのが前提のイメージになります。 scratchを使う Go限定ですが、scratchを使う方法もあります。 Goで CGO_ENABLED=0 でビルドしたバイナリは単独で動作可能なので、何も入っていないscratchイメージに追加すればそれだけで動作します。 こちらもシェルが入っておらず、コンテナ内に入れないので注意が必要です。 まとめ Alpine Linuxについて、イメージサイズの減少幅の小ささ、それに対する種々のリスク・課題について解説しました。 debian-slimイメージが十分サイズが小さいため、長期的な運用において不確定要素を抱えるAlpineを採用する採用するメリットは薄くなっているのではないかと考えています。 特に開発用コンテナですでにdebianを使っているような環境では、debian-slimに乗り換えてみるのも考えてみてください。 ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する
はじめに はじめまして。ニフティ株式会社の鹿野です。 この記事ではニフティのデータ基盤をご紹介いたします。 データ基盤とは データ基盤は、部署を横断して複数のデータを集約する場所です。 サービスやプロダクトを横断してデータを活用することで、顧客体験やビジネス価値を向上させることを目的として活動しています。 データ基盤の構成 採用している技術スタックを以下に示します。 Data integration Embulk Data transformation, catalog dbt Data lake Amazon S3 Cloud Storage 1 Data warehouse Amazon Redshift BigQuery 2 Data orchestration Digdag Data uses Tableau Redash 補足としまして、弊社のデータ基盤はETL・ELTパイプラインが混在する環境となっており、Embulkはこの両方に関与しています。 dbtを導入してからはETLの方が望ましいケースを除き、順次ELTへの移行を進めています。(dbtは特にデータモデルの管理機能がとても素晴らしいと感じています。) また、上記構成図には出てこない部分として、データウェアハウスへのデータロードのほとんどはAmazon Reshift Spectrum が担っています。 ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 カジュアル面談も随時受付中! ニフティに興味をお持ちの方は キャリア登録をぜひお願いいたします! キャリア登録 connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する Google Cloudのオブジェクトストレージサービス ︎ Google Cloudのデータウェアハウスサービス ︎
インナーソースコミュニティであるInnerSource Commons Japanが2024年8月8日にInnerSource Gatheringを日本初開催いたします。 イベントの詳細、参加につきましては下記ページを参照ください。 https://gatherings.innersourcecommons.org/tokyo-2024/ 当社では、今回運営参加する基幹システムグループの芦川、小松を中心にインナーソースの推進活動をしております。活動の様子につきましては、 インナーソースを導入してみた その① お試し導入編 をぜひご一読ください。 また、芦川がチームリーダーをしているサービスインフラチームの紹介記事もあります。インナーソースのような活動を尊重するチーム文化が窺い知ることができますので、こちらも合わせてお読みいただけると幸いです。 基幹システムグループ サービスインフラチームの紹介です
はじめに 先日、Amazon RDS for MySQLのマイナーバージョンアップグレードに失敗したのですが、調べても情報が見つからなかったので情報共有のために記事にしました。 経緯 先日、AWSからこんな通知がありました。 [アクションが必要です] Amazon RDS for MySQL は、2024 年 3 月 29 日にマイナーバージョン 8.0.31、8.0.29、8.0.28 を廃止します | [Action Required] Amazon RDS for MySQL is deprecating minor versions 8.0.31, 8.0.29 and 8.0.28 on March 29, 2024 [AWS Account: xxxxxxxxxxxx] [AP-NORTHEAST-1] 今使っているMySQLのバージョンが廃止されるため、期日までにアップグレードが必要という通知です。 特に引っかかることもなかったのでアップグレードしました。 しかし、開始してしばらく待ち続けたのですがなかなか終わらず、1時間ほど経ったときに Database instance is in a state that cannot be upgraded: PreUpgrade checks failed: RDS detected incompatibilities when upgrading to MySQL 8.0.36. For details, see the PrePatchCompatibility.log file in the Logs section. というメッセージが表示されました。 そのまま訳すと「MySQL 8.0.36へのアップグレード時にRDSが非互換性を検出したのでアップグレードに失敗しました。詳細はPrePatchCompatibility.logを見てください。」という内容のようです。 しかしPrePatchCompatibility.logというログは生成されていませんでした。 今回は8.0.31から8.0.36へのマイナーバージョンのアップグレードなので互換性に問題が起きることはあまりなさそうなのですが…。 エラーログ(mysql-error-running.log)を見ると [ERROR] [MY-013180] [Server] Function ‘auth_socket’ already exists. というエラーが出ていましたが、いまいち原因の特定はできませんでした。 原因 調べても手掛かりとなるような情報はなく原因が分からなかったのでAWSサポートに問い合わせてみました。 その結果、 テーブルへのトランザクションが実行中だった ことが原因と分かりました。 アップグレード前に互換性検証のために対象DBの全テーブルに対してロックを取得しているらしく、その際にトランザクションが実行中のテーブルがありロックに失敗していたようです。 対策 対策としてはアップグレードする際に 一旦アプリケーションを停止する プロセス停止、ロールバックなどでトランザクションを終了する などしてトランザクションがない状態でアップグレードする必要があります。 結果 その後、失敗した2日後に再度アップグレードをしたところ、無事に成功しました。 今後はアプリケーションを停止するか、トランザクションがないことを確認してからアップグレードしたいと思います。 ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する
はじめに こんにちは。ニフティ株式会社の添野です。 AWS Amplify で配信している静的ページに対する定期リビルド機構を組む必要があったので、その際に Amazon EventBridge API 送信先 を利用した話を共有します。 背景 社内のとあるサービスで利用する静的ページの配信基盤にAWS Amplifyを採用しており、そのページの中には定期的に変わるコンテンツが存在します。その仕様のため定期的にリビルドすることが必要になりました。最初は AWS Lambda を利用してAWS AmplifyのIncomingWebHookを定期的に叩こうかなと思いましたが、AWS Lambda上のコードの管理やAWS LambdaランタイムのEOL対応、AWS Lambdaに対するメモリ設定など気にする項目が増えて、手間だなと思いました。そこでもう少し良さげな機能が無いかなと思い探してみたら、 Amazon EventBridge の中にあるAPI 送信先を見つけました。 API 送信先とは? 公式ドキュメント によれば、API送信先(あるいはAPI Destinationとも呼ばれる)とは、 AWS サービスまたはリソースをターゲットとして呼び出す方法と同様に、ルールのターゲットとして呼び出すことができる HTTP エンドポイントです。 とのことで、レートリミットなどの制御やセキュリティの機能なども提供します。 API 送信先を利用した定期リビルド機構について ここではTerraformのコードを示しつつ、組んだ話について説明します。 まずは、対象となるAWS Amplifyのアプリケーションに対して、Incoming WebHookを設定します。叩くとAmplifyでビルドと公開処理が走ります。 resource "aws_amplify_webhook" "master" { app_id = aws_amplify_app.static_content.id branch_name = "master" description = "triggermaster" } 次に、毎日07:30に定期的にリビルドを走らせるルールを作成し、ターゲットとしてAWS AmplifyのIncoming WebHookに前置するAPI送信先を指定します。ここで、Amazon EventBridgeの接続先(aws_cloudwatch_event_connection)では提供されている認証方式(APIキー認証、Basic認証、OAuth認証)を必ず選択しないといけない点に注意する必要があります。今回はBasic認証を選択することにしました。 resource "aws_cloudwatch_event_rule" "regular_rebuild" { name = "${var.application_name}-regular-rebuild-${var.environment_name}" description = "${var.application_name} Regular Rebuild ${var.environment_name}" schedule_expression = "cron(30 22 * * ? *)" # NOTE: 毎日 07:30 JST (静的ページリビルド) } resource "aws_cloudwatch_event_target" "regular_rebuild" { target_id = "RegularRebuild" arn = aws_cloudwatch_event_api_destination.regular_rebuild.arn rule = aws_cloudwatch_event_rule.regular_rebuild.name role_arn = aws_iam_role.regular_rebuild.arn } resource "aws_cloudwatch_event_api_destination" "regular_rebuild" { name = "${var.application_name}-regular-rebuild-${var.environment_name}" description = "${var.application_name} Regular Rebuild ${var.environment_name}" invocation_endpoint = aws_amplify_webhook.master.url http_method = "POST" invocation_rate_limit_per_second = 1 connection_arn = aws_cloudwatch_event_connection.regular_rebuild.arn } resource "aws_cloudwatch_event_connection" "regular_rebuild" { name = "regular_reguild_webhook" description = "A connection description" authorization_type = "BASIC" # NOTE: Amazon EventBridgeが利用するBasic認証情報 auth_parameters { basic { username = "user" password = "Pass1234567!" } } } 実際に定期リビルドされている様子を示します。Updatedが07:31、Last commitがIncoming WebHook起動であることが示されています。 料金(東京リージョンを仮定する)は、 API 送信先 を利用する方式では0.24USD/100万呼び出しであり、対して AWS Lambda を利用する方式では、メモリを128MB指定して、呼び出しに300ミリ秒が掛かると、0.0000000021 x 300USD/呼び出しであり、AWS Lambdaを利用する方式の方が若干高くなります。 おわりに 今回は、API 送信先を利用してAmplifyで配信している静的ページに対する定期リビルド機構を組んだ話を共有しました。AWS Amplifyのリビルド機構を組みたい方の参考になれば幸いです。 ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 カジュアル面談も随時受付中! ニフティに興味をお持ちの方は キャリア登録をぜひお願いいたします! キャリア登録 connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する
はじめに こんにちは。ニフティ株式会社の並木です。 今回は「Zapier」でAPIを呼び出し、実行結果をSlackに通知する方法についてご紹介いたします。 背景 以前「 Zapierを使ってRSSフィードの更新をトリガーにしたTwitterへの自動投稿機能を作ってみた 」で紹介した方法で、RSSフィードの更新をトリガーにしたX(旧Twitter)への自動投稿機能を作成したのですが、上記記事の「④Twitterに文章を投稿したい」で紹介した機能が2023年9月に使用できなくなってしまいました。(詳細: https://help.zapier.com/hc/en-us/articles/18657531069965 ) 別の方法で自動投稿機能を復活できないかという話になり、前から使用していた「Zapier」を流用しつつ、新たに「Amazon API Gateway」「AWS Lambda」「X API」を使用して、自動投稿を実現することにしました。 構成図は以下の通りです。 構成図の【1】【2】については、「 Zapierを使ってRSSフィードの更新をトリガーにしたTwitterへの自動投稿機能を作ってみた 」の①~③の処理をそのまま流用します。 【4】の手順は「 API GatewayとLambdaでX投稿するAPIを作ってみた 」に記載しています。 【5】の手順は「 LambdaでX APIを呼び出してみた 」に記載しています。 「【3】ZapierからAPI Gatewayを呼び出す」と「【6】SlackにXの投稿結果を通知」が今回の説明範囲になります。 前準備(必要な材料をそろえる) Zapierを使用するにあたって、必要となるものを準備します。 ■ 呼び出すAPIのURLを取得する 「 API GatewayとLambdaでX投稿するAPIを作ってみた 」で作成したAPIのURLを取得します。 URLの取得方法は公式ドキュメントの「 API の呼び出し URL の取得 」に記載があります。 画像の赤枠部分をコピーすればOKです。 ■ 呼び出すAPIのAPIキーを取得する 「 API GatewayとLambdaでX投稿するAPIを作ってみた 」で作成したAPIのAPIキーを取得します。 API Gatewayのコントロールパネルの左メニューの「APIキー」から確認できます。 「Zapier」から「Amazon API Gateway」を呼び出す 前準備で取得したURLとAPIキーを使って、ZapierからAPIを呼び出します。 ■ App&Event Appは「Webhooks by Zapier」、Eventは「POST」を選択します。 ■ Action URLには、前準備で取得したAPIのURLを入力します。 「 API GatewayとLambdaでX投稿するAPIを作ってみた 」の中で、JSON形式で「hoge」というリクエストパラメータをAPIで受け取れるようにしました。 なので、Payload Typeは「JSON」を選択します。 Dataには「hoge」というキー名と、Xにポストする文章を設定します。 認証周りですが、Basic Authは今回使わないため空欄でOKです。 今回はAPIキーによる認証を行うため、Headersに「x-api-key」というキー名と、前準備で取得したAPIキーを設定します。 これでZapierからXに投稿できるようになりました! SlackにXの投稿結果を通知する 「 API GatewayとLambdaでX投稿するAPIを作ってみた 」で作成したAPIから、以下の通りJSON形式で値が返却されます。 { "statusCode": 200, "headers": { "Content-Type": "application/json" }, "body": { "result": "Xの投稿結果", "api_response": "XAPIからのレスポンス内容" } } 上記の中から "result": "Xの投稿結果" を取り出してSlackに投稿し、Xの投稿の成功/失敗を通知できるようにします。 ■ App&Event、 Appは「Webhooks by Zapier」、Eventは「POST」を選択します。 ■ Account 自分のSlackアカウントを選択します。 ■ Action Channelに通知したいチャンネル名を選択します。 MessageTextに通知したい内容を入力します。ここに "result": "Xの投稿結果" を埋め込みます。 これでSlackにXの投稿結果が通知されるようになりました! おわりに 「 Zapierを使ってRSSフィードの更新をトリガーにしたTwitterへの自動投稿機能を作ってみた 」で紹介した方法が、2023年9月に使用できなくなり焦りましたが、無事に自動投稿機能を復活させることができ一安心しました。 APIを自作することで、以前は無かった「Xの投稿結果を通知する」という機能を付けることができたので、結果的に良かったのではないかと思います。 参考記事 https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/how-to-call-api.html#apigateway-how-to-call-rest-api ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 カジュアル面談も随時受付中! ニフティに興味をお持ちの方は キャリア登録をぜひお願いいたします! キャリア登録 connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する
はじめに こんにちは。ニフティに新卒で入社して多分6年目の佐々木です。今回はAWSのサービスの一つである EC2 についてコスト削減を行う方法を紹介します。 以前ご紹介したコスト削減手法ついては、以下のブログ記事をご参考ください。 CloudWatch編 S3編 CloudFront編 背景 ニフティではサービス基盤にAWSを活用しており、コスト削減のためにサービスのインフラ効率を追求する取り組みや、有志で取り組んでいる社内でのコスト削減勉強会なども実施しています。 今回は、EC2のコスト削減においてのポイントや注意点について簡単にご紹介したいと思います。 この記事の内容 触れること EC2のコスト削減方法 EC2のコストを削減する上での注意点 実際に試した社内でのコスト削減事例とTips 触れないこと EC2自体の詳細の説明 詳細のインフラ構成(コスト削減が目的のため、今回は一般的なインフラ構成のご紹介に限らせていただきます) EC2の基本 EC2(Amazon Elastic Compute Cloud)は言わずとしれたAWSの代表的なサービスです。 必要に応じて容易にサーバーの追加・削除が行える仮想サーバーの役割を果たしています。 EC2のコスト削減 課金形態 さっそくEC2のコストを確認していきましょう。 2024年4月現在では主に以下のようになっています。(ap-northeast-1の場合で無料利用枠は除きます) EC2自体の主な課金要素は最低限この3つを押さえておけば問題ないかと思います。(他にも様々な細かい課金要素はあります) インスタンスタイプ 利用するEC2のインスタンスタイプにより課金される金額が変わります 例えば、t3.microの場合は毎時0.0104USDの課金が発生します データ転送 「送信(アウト)」に対して課金が発生します。受信(イン)には課金されません 例えば、最初の 10 TB/月の場合はUSD 0.114/GBの課金が発生します 料金プランによる課金形態 後述するEC2の料金プランを選択することで様々な割引を受けることができます 詳細はこちらの AWS公式の資料 をご参考ください。 コスト削減方法 インスタンスタイプの見直し 当然ですが、どの場合においてもサービスの要件を満たす最低限のスペックのインスタンスであればコストを最小限に抑えることができます。 こちらは後述する「インスタンスサイズの見直し」の項で実際の削減例を交えて詳しく紹介しようと思います。 インスタンスのリソースの増減を考慮する こちらは起動しているインスタンスの数を状況に応じて変更することでコストを抑える方法です。 実際の稼働しているサービスによっては、繁忙期の利用増加や時間帯ごとの需要の変化などが生じる場合があると思います。そのような場合はピークの時間帯に備えて常にフルでサーバーを稼働しておく必要がない場合もあります。 そのような場合は、 Auto Scaling を組むことで正常なインスタンスの数を維持しつつトラフィックの増減に合わせてサーバー台数を自動で変化させることができます。また、時間台によって起動させるサーバーの台数を予め指定することもできます。 また別の要因として、開発環境であればそもそも利用する時間帯が限られているかと思います。そちらの場合には、夜間休日にインスタンスを自動停止するようなスクリプトを組むことで無駄なコストの発生を抑えることができます(塵も積もれば山となる…)。 EC2インスタンスの自動停止・起動方法については、Lambda関数を活用して組むことができます。 https://repost.aws/ja/knowledge-center/start-stop-lambda-eventbridge EC2の料金プランによる費用最適化 課金形態の3つ目「料金プランによる課金形態」にあたります EC2にはいくつか料金プランがあり、特定の条件下で利用することでサービスの料金が割引になる仕組みが存在します。 Amazon EC2 オンデマンド 従量課金制の料金形態 デフォルトでインスタンスを立てると通常この費用が発生する Amazon EC2 リザーブドインスタンス(RI) 「一定期間の継続 」を契約することで大幅な割引(最大72%)ができる料金形態 1年間または3年間の期間存在する、当然3年間の方が割引率は高い 「キャパシティの予約」という形で契約を行うことで、任意のインスタンスタイプのEC2インスタンスを予約期間中は自由に起動できるようになる 2つのプランが提供されている スタンダードRI: 特定のインスタンスタイプについての割引が受けられるプラン 割引率は高いが原則途中で変更はできない コンバーチブルRI: インスタンスファミリー・インスタンスタイプ・プラットフォームなどの変更が可能 なプラン 割引率はスタンダードRIに比べると低。 ただし元のコンバーチブルRIと同等価格以上のインスタンスタイプでなければ交換することができない 1年間または3年間の期間存在する、当然3年間の方が割引率が高い 3種類の前払いの支払いオプションが提供されている 全額前払い 一部前払い 前払いなし 常に稼働させておく必要があるシステムの場合に有用 Savings Plans 「 一定期間の継続、一定量の使用 」を契約することで大幅な割引(最大72%)ができる料金形態 「コミットメント額 = SPの料金として支払う額」という形で契約を行うことで、使用量に対する割引が受けられるようになる リザーブドインスタンスと比較して 高い柔軟性 がある 2つのプランが提供されている Compute Savings Plans 割引率はやや低いが柔軟性があるプラン インスタンスファミリー、サイズ、アベイラビリティーゾーン、リージョン、OSに関わらずEC2インスタンスの使用に自動的に適用される EC2 Instance Savings Plans 割引率が高いがやや適用条件があるプラン アベイラビリティゾーン、インスタンスタイプ、OSに関わらずそのリージョン内で選択されたインスタンスファミリーのコストを削減できる Amazon EC2 スポットインスタンス AWS側の空いているキャパシティを活用することで大幅な割引(最大90%)ができる料金形態 空きがなくなると中断が発生する可能性があるので、処理が途中であっても中断可能なサービスや機能に適していると言える 結局どう選べば良いのか? EC2の料金プランは種類も多く条件もやや複雑です…。 なので個人的にはまずはオンデマンドを利用し、実際に少し稼働させてみて想定したスペックで耐えられることが確認できたら、要件に合わせて割引可能なプランの選択を検討する、といった流れが良いのかなと思います。ひとまずは、実際のコンソール画面を見ながら料金プランのイメージを掴めると良さそうです。 また余談として、SP/RIはAWS Organizationの管理アカウント単位で買うこともできるのですが、こちらも残念ながら非常にややこしい仕様となっています…。ただ、上手く活用することで各プロダクトチーム単位で買うよりも大きなメリットがあるので、できれば検討したいところです。 管理アカウントでの購入について以下の記事で詳しく解説しているので、良ければ合わせてご参照ください。 AWS Organizationの管理アカウントでSPs/RIを一括購入してカバレッジを上げる 実際に社内で試したこと インスタンスサイズの見直し 開発環境にあるEC2インスタンスのスペックが過剰なものがあり、そちらの見直しを行いました。 方針 事前にインスタンスタイプについての知識があまりなかったので、いきなり作業に取り掛かる前に以下の方針を立てました。 使用量を調べて、全体的にインスタンスタイプを下げても問題ないかを把握する CPU使用率 を見て上限に張り付いていないかを確認する メモリ使用率はCloudWatch Agentを導入しないと把握できないので考慮からは除外 同じインスタンスファミリー内で、ベースライン使用率を超えないと思われるスペックを選択する 世代が新しいほどコストパフォーマンスが向上する傾向があるため、なるべく最新のインスタンスファミリーを選ぶ ただし「汎用バースト型のT系」から「グラフィック処理に特化したG系」にするなどアーキテクチャそのものの変更なので、要件によってそもそも変更不可能な場合がありその点を考慮する アプリケーションやミドルウェアの要件によって世代が新しいインスタンスを利用できない可能性がある (詳細は割愛しますが、ミドルウェアのバージョンが古いとG系で扱う ENAドライバ に対応していない場合があり、今回それに該当しているものがありました…) 洗い出し 上記の方針を元に、各インスタンスで変更できそうなスペックの候補を洗い出していきます。 今回はNotion DBを使って以下のような表を作りました。 各インスタンスファミリーの特徴については 公式ページ を見ながら把握していきます 今回はほぼT系からT系への変更だったこともあるので、T系インスタンスが前提の話になります。 現行スペックと変更後のスペックの対比として今回は以下の項目を考慮に入れています 各インスタンスタイプの料金 vCPU CPU使用率(%) ベースラインパフォーマンス(%) 前提として、T系インスタンスでは CPU クレジット を絶えず一定の割合で獲得しています。これによりある程度負荷が急増した際にも蓄積されたクレジットを消費することで、ベースラインレベルを超えてCPU使用率をバーストできるという仕様になっています。 「通常はベースラインまで性能を落として上で起動しておいて、緊急時には100%の性能で稼働する」というとっても便利な機能ですね。 また、 公式ドキュメント にもありますが、EC2インスタンスではベースライン使用率(ベースラインパフォーマンス)というものが定義されています。「CPUクレジットの獲得数とCPUクレジットの使用率が一致する場合に、正味のクレジットの残高が0の状態でCUPを使用できるレベル」とのことです。つまり「このラインの使用率を超えるといずれクレジットが枯渇するよ、枯渇したらこのレベルまでしか性能を出せないよ」というのを数値化したものだと思われます。 今回のT系インスタンスの場合では、このベースライン使用率を超え始めるとクレジットを消化し始める(=パフォーマンスが悪化する可能性がある)ので、CPU使用率が常にこの値を下回るのであればスペックを下げても問題ないと考えました。 しかし、インスタンスタイプを変更するに当たってもう一つ罠があります…。 簡単に言うと、vCPUとCPU使用率(CloudWatchのコンソール上で見れる数値)には反比例の関係性があるようです。 CPUクレジットの概念 の項にもありますが、1クレジットあたりの定義が以下になっています。 1 CPU クレジット = 1 vCPU × 100% 使用率 × 1 分 1 CPU クレジット = 1 vCPU × 50% 使用率 × 2 分 1 CPU クレジット = 2 vCPU × 25% 使用率 × 2 分 上記の計算式から、今回は「インスタンスタイプを変更してvCPUが半分の値になった場合はCloudWatchメトリクス上で表示されるCPU使用率は倍の値になる」と見積もって計算を行いました。 また、同じく CPUクレジット獲得 の項では、「表内のベースライン使用率は vCPU 別の割合です。CloudWatch では、CPU 使用率は vCPU 別に表示されます」とあるので、そう読み取って良さそうです。 またここでは考慮していませんが、実際にはインスタンスの世代による差(新しい世代だとパフォーマンスの部分で改善されていたりする)もあるので、そちらも影響してくると思います。 変更 最後に先ほど決めた変更後のスペックに手動で変えていきます。 今回は開発環境なのでダウンタイムを考慮せずに簡単に変えることができました。 これで削減完了です。 徐々に見直しを行った結果以下のようにEC2の使用量を半分以下に減らすことができました! Cost Explorer などで実際に削減されているのを見るとモチベーションが上がって嬉しいですね。 まとめ 今回インスタンスタイプの見直しやEC2の割引仕様を調べるにあたっては、ドキュメントから判別がつかない点も多く、実際の事例を参考に多分こういう仕様だろう、という推定をしながらとりあえずやってみる部分がそこそこありました。 一通り各インスタイプの特徴や割引プランを把握しておくことで、実際の運用にも役立てると思うので、まずはドキュメント化などをしつつチーム内での認知不可を下げて、少しずつ社内の共通知にしていきたいなと思っています。 検証環境とはいえ、初めてインスタンスタイプを変えたときはこれでよいのかな?と不安になったので、実際に変更したり買って学べる環境があると良いなと思います。 We are hiring! ニフティでは、さまざまなプロダクトへ挑戦するエンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトよりお気軽にご連絡ください! ニフティ株式会社採用情報 カジュアル面談も受け付けています! カジュアル面談 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering ニフティ株式会社 – connpass
はじめに 「春からエンジニアだけど何もわからなくて不安。どんな準備をしたらいいのだろう?」 そんなお悩みを抱えている人達に向けて、ニフティのエンジニアが書き上げたブログの中から新米エンジニアに是非読んで頂きたいブログを厳選しました。 このブログには以下の内容が書かれています。 新米エンジニアにおすすめな汎用的な技術書 業務効率化方法 新人エンジニアとしての心構え 【初心者向け】エンジニアになりたい/なりたての人におすすめの本3選 【初心者向け】エンジニアになりたい/なりたての人におすすめの本3選 こちらのブログではネットワーク、DB、Linuxといった様々な観点で初学者向けの書籍を紹介しています。 特定の言語に依存しない汎用的な内容なので、まずはこれらの中から何か手に取ってみるのはいかがでしょうか。 技術初心者の私が業務中に得た、仕事の効率化の方法の話 技術初心者の私が業務中に得た、仕事の効率化の方法の話 当時入社1年目だった社員が実務を通して役に立った業務効率化Tipsについてまとめているブログです。 ツール導入だけでなくCLIのコマンド単位のTipsもあります。これは!と思ったものがあれば明日から取り入れてみましょう。 効率化小ネタ集 効率化小ネタ集 以下のような便利なツールの導入方法や活用方法についてまとめています。 アプリケーションの定期実行を可能にする「タスクスケジューラ」 特定のページからのみ検索を行う「サイト内検索」 現在開いているウェブページに対してJavaScriptの処理を適用する「ブックマークレット」 これらは応用が効く内容のため、覚えたら早速業務に活用できるかもしれません。 新人のすゝめ ~文系卒がエンジニアとして過ごした入社1年目の気付き~ 新人のすゝめ ~文系卒がエンジニアとして過ごした入社1年目の気付き~ 新人エンジニアの時に感じた不安や悩みを語りつつ、そこから今に至るまでに得た気付きを語っています。 入社して間もない時期は仕事の進め方や質問の仕方などで困っていませんか? そういった場面でこちらのブログに書いてある内容を思い出してみてください。 トラブル対応で私はいっぱい成長してきた トラブル対応で私はいっぱい成長してきた 長年エンジニアをしていた社員が、「エンジニアの学習と成長」について自身の経験を通してお話しています。 学習を行っていく中で何を大切にすべきか、成長できる人はどういった状況にある人なのか興味がある場合は是非読んでみてください。 さいごに 今回は新米エンジニアに向けたブログたちを紹介しました。 興味を引く点や新たな発見はありましたか。 NIFTY engineeringでは様々な技術の話題・情報を取り上げています。 どれも素敵なブログばかりですので是非覗いていってください。 このブログが、新たな挑戦を始めるための一歩になればと考えています。 また、貴方がエンジニアの一人としてご活躍できることを心から祈っております! ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 カジュアル面談も随時受付中! ニフティに興味をお持ちの方は キャリア登録をぜひお願いいたします! キャリア登録 connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する
マイ ニフティ というニフティ会員向けアプリのチームで、スクラムマスターを務めている西野です。 スクラムマスターとしてPO(プロダクトオーナー)のサポートが十分にできているか課題を感じており、去年PO向けの研修を受けてきました。研修中、POとして振る舞ってみると「意思決定の怖さ」を感じるシーンが多くあり、それをきっかけとして「効果的な意思決定」について考えてみました。 POでなくとも、小さなものから大きなものまで何かを決定するときに、少し不安な気持ちになったりしませんか。 例えば、外食時のメニュー選択は、意思決定の個人的な傾向が現れる一例です。自分はわりとすぐにメニューを決められるのですが、注文を取る時に突然変更してしまい、友達に「ええ……?」と言われるタイプです。 マイ ニフティでは、意思決定の際に少しずつデータ面での理由づけができるような環境を整えてきました。今回は、データでどのように意思決定を行うかについて紹介します。 意思決定のプロセス なんとなくする意思決定も、理由をつけてする意思決定も、等しく意思決定ではあります。意思決定には様々な方法がありますが、代表的なものについて挙げてみました。まずはそれぞれの特徴を把握しましょう。 直感 合理的意思決 多数決 コンセサスをとる データドリブンで行う 直感で決める 「肌感でこっちがいい」「えいやで決めましょう」「経験上こちらがいいと思う」というものです。 GOOD 即断できる BAD 意思決定者のバイアスがかかる 周囲への説明や説得が難しい 決断者も自分を説得しきれておらず、選ばなかった選択肢で迷いだす 効果があっても再現性がない 直感は、経験者が一番多い意思決定方法といえます。BADポイントも多いですが、最短で意思決定ができるメリットが大きく多用されがちです。 強力なリーダーシップ下でないと機能しにくく、もし機能してもそのリーダーがいなくなると意思決定ができなくなってしまうという課題があります。 合理的意思決定 すべての選択肢を比較検討し最良を選ぶ方法です。 GOOD 再現性が高い 関係者への説得がしやすい BAD とても時間がかかる すべての選択肢を出せる情報があるとは限らない 比較検討の過程で「どのような理由で選択に至ったか」という理論がうまれるため、もしその意思決定が効果的だった場合、その成功は再現できる可能性が高いです。 しかし、現実問題として「すべて」の選択肢を出せたという証明の困難さ、それを出すまでにかかる時間は膨大になりやすいです。また、ここまでの合理性を求められるような意思決定のシーンは限られます。 多数決 多数決原理です。賛同者が多い方で決定します。 GOOD 即断できる BAD 多数決に参加していない人への説明や説得が難しい 再現性がない 少数派の意見が無視されてしまう 多数決で決める際に重要なことは、関係者全員がその場に参加していることです。もし一部の人だけで決をとり「多くがそう思った」という説明をしたとして、関係者は納得がいくでしょうか。 選択の理論がないので、メンバーの入れ替わりや状況・気分などが影響し、うまくいったときの再現性もありません。 少数意見を支持した人との分断を招いてしまうことも課題です。 コンセサスをとる 関係者全員の合意形成をします。 GOOD 関係者の分断が起きない 合意をとる課程である程度の理論が生まれれば、再現性がある BAD 時間がかかる コンセサスをとるべき範囲を決めにくい みんなが納得するまでやりきることができればいいですが、そうではない場合、妥協による同調を招きやすいです。 もし納得できるまでやりきれたとしても、合意してない人・すでに合意している人の間に温度差がうまれ、モチベーション維持が課題となります。 データドリブンで行う データを起点として意思決定を行います。 GOOD データを正確に解釈できれば再現性が高い 関係者への説得がしやすい(客観性をもちやすい) BAD 一定の質のデータを事前に集めないといけない データを正確に解釈しないと再現性が低くなる 革新的なアイデアは出にくい ユーザーの行動データなどを元にした意思決定方法です。 データを恣意的に解釈してしまわないかといった課題はあるものの、データソースを開示していればそれが正しいかという検証も可能です。 意思決定の根拠がはっきりとしているので、意思決定後の迷いや変更がおきにくい利点があります。 意思決定のポイント 意思決定は、プロダクトやチームを良い方向にも悪い方向にも動かすことがあります。何かを動かすためには、意思決定を行うことが重要です。しかし、頻繁に意思決定を変更することは、プロダクトやチームを不安定にする可能性があります。 しかし、理由もなく頻繁に意思決定を変更するような行為は、プロダクトやチームを崩壊させる方へどんどん動かしてしまいます。 意思決定の方法をいくつか紹介しましたが、意思決定のポイントは「選んだ理由」だけでなく、それ以外の選択肢を最良だと「選ばなかった理由」がわかるかどうかを重視すると良いでしょう。 データドリブンが意思決定に有効だと思う理由のひとつに、決定に至る論理の立てやすさがありますが、最も効果的な点は「意思決定を変えにくい」ことにあると思います。 人の意思というものは、想像以上に脆く、変わりやすいものです。そのために、主観ではなく客観的な情報をひとつでも多く持っておくことで、決定の変化や選ばなかった選択肢について思い悩むことを防ぐことができます。 データを集める、共有する では、実際にニフティの会員向けアプリである「マイ ニフティ」で、どのようにデータドリブンによる意思決定を行なっているかを紹介したいと思います。 基本データ「のみ」をダッシュボードに表示する アプリをリリースする前の時点、つまりデータを集める前の意思決定は、実データに基づいた判断ができません。現実が見えていない段階では、あまり確認するデータ範囲を欲張らないことが大切です。 データ収集を開始するにあたり、最も重要なのは、必要最低限の条件を満たしているかを確認することです。 データ収集が可能な必要最低限のデータを特定できるか ダッシュボードの内容が必要最低限の範囲に絞られているか 上では必要最低限のデータと書きましたが、本当はリリース初期から可能な限りデータの蓄積をしておいたほうが良いです。これは、適切なデータの欠如が正確なデータ解釈を妨げ、効果的な意思決定を遅らせる可能性があるためです。 しかし、どれだけ網羅的にデータを収集しようと努力しても、リリース後には必ずと言っていいほど必要なデータが不足していることに気づきます。 いち早くデータ計測を開始することと、リリース前にデータ収集点を網羅することは相反することに注意してください。適切な妥協点を探る必要があります。 データは意思決定に活用されるため、意思決定者だけでなく、関係者全員にとって意味のある指標として可視化されるべきです。 マイ ニフティの場合、最低限の可視化として、ログインユーザーの累計と日・週・月のアクティブユーザー数をダッシュボードに表示することに決めました。 ダッシュボードに表示する内容を決めるコツは「もしかしたらいるかも」レベルの情報は省いてしまうことです。プロダクトの主要KPIや、成長にとって本当に必要な数字だけに絞ります。 画面表示やボタンタップのイベントなども取っていますが、初期段階では毎日見るダッシュボードにはあえて載せていません。毎日見る必要があるデータと、意思決定をするために必要なデータは粒度や質が異なるからです。 フィードバックを反映する いったん最低限の情報が見られるようになったダッシュボードができたら、それを関係者に見せ、デイリーで知りたい情報についてフィードバックをもらいます。 フィードバックは、ダッシュボードの運用開始後、主要メンバー全員が1-2週間は毎日見たうえでの要望を採用したほうがより効果的です。 私たちスクラムチームの場合、デイリースクラムでダッシュボードを毎日見て、気づいたことを共有するようにしています。 例えば、デイリーでアクティブユーザーを観察する中で「回線の契約コースによってユーザー行動が異なるのでは」というフィードバックがありました。 コース別の継続率をダッシュボードに追加してからは、会員全体向けの施策だけでなく、コース別のニーズに応えるための施策も動き出した実感があります。 (なかなか見せられない情報が多いので、ダッシュボードの一部分の雰囲気だけ……) ダッシュボードを成長させる ダッシュボードを活用することで、データドリブンによる意思決定プロセスが促進されます。 プロダクトの成長や施策の効果を視覚的にリアルタイムで評価し、継続的な改善につなげることができるからです。 継続的な改善施策において、チームは施策立案時にその施策が数値で効果測定可能であるかを確認し、ダッシュボード上でその施策効果が可視化されるようにします。 ダッシュボードは、施策やプロダクトの成長や方向性とともに項目の追加・削除を行い、プロダクトの現状に適合するように管理し続けます。 ダッシュボードの導入・運用フロー図 ここまでの内容をフロー図にするとこうなります。 データドリブンを始めるための工夫 データドリブンは、新規プロダクトの立ち上げや、施策が少ない段階では比較的受け入れやすいです。 一方で、データがあまり取れていない既存プロダクトに今からデータを追加したい場合、「やりたいことがいっぱいあるのに、ユーザーから見えないデータ部分の優先度は上げられない」とPOに受け入れられないケースもあるかもしれません。 この場合、意思決定者は「自分はユーザーを理解しており、データなんかなくても意思決定はできる」と思っています。しかし本当は、施策を絞りきれず、何をしないかを決定できてない状態です。 開発者として、この状態を受け入れていいかは注意しましょう。主観による意思決定は、施策Aをやっている途中で、やっぱり施策Bがいいかも、と言い出しやすい環境をつくっています。 やりたい施策がどれだけたくさんあっても、10個の施策のうち、あえて9個を失敗させたい人はいません。 データがあることで、成功する施策だけでなく失敗する施策もある程度パターン化できます。やらなくてもいい施策を潰し、やるべき施策にいち早くフォーカスするためにも、データ収集と見える化はセットで必要です。 データに基づく意思決定の効果 意思決定のプロセスそれぞれに利点と欠点がある中で、データドリブンによる意思決定の意義は何でしょうか。 開発者は、POなど意思決定者の決断が効果的になるよう、データ収集とビジュアライゼーションで支援することができます。また、意思決定者が決断後に「やっぱり……」と迷わせない仕組みをつくることは、開発フローをシンプルにし、開発者自身の環境を守ることにも繋がります。 データドリブンを開始すると、よく「分析を専門的に学習したわけではないので、データを扱う自信がない」という不安の声を聞きます。 たしかに、データ分析の専門性が高ければデータ解釈の精度が高まります。しかし、どれだけ高い専門性をもっていたとしても、常に正しい意思決定ができるわけではありません。 データ分析の専門家でなくても、データを効果的に活用することは可能です。「後戻りをしない」「後から迷わない」ための意思決定であれば、もし迷う気持ちがあってもデータがその決定の妥当性を裏付けてくれます。 データを活用した戦略的アプローチ ここまでデータの重要性を語ってきましたが、データを集めるだけで自ずと効果的な施策になるわけではないことに注意してください。 データを効果的に活用するためには、データの解釈と仮説の検証、ひいてはPOと開発の協力が不可欠です。 POが戦略を立てる際は、事実と推測を分けることに気をつけてください。そしてその推測をできるだけ推測でなくなるように検証し、プロダクト戦略を組織の目標と合致させていきます。 開発チームは、ABテスト、アンケート回答内容といったさまざまなデータを組み合わせることで、POの仮説検証を支援します。 このような多角的なデータ分析により、推測の精度を高め、戦略の破綻リスクを軽減することが可能です。 データドリブンなアプローチを成功させるには、単にデータを集めるだけでなく、そのデータを基に仮説を立て、検証し、そして戦略を調整するという継続的なプロセスが必要です。 データを基に、明確で説得力のある意思決定を行うことでプロダクトの成長に繋げていきましょう! 私たちマイ ニフティチームも、データドリブンによる意思決定を行なっています。2024年4月現在、アプリエンジニア(特にiOS)を募集中です! カジュアル面談もやってます (5月中旬ごろから面談可能です) ぜひお気軽にお問い合わせいただければ幸いです。 求人情報は以下よりご確認ください。 ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 カジュアル面談も随時受付中! ニフティに興味をお持ちの方は キャリア登録をぜひお願いいたします! キャリア登録 connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する