TECH PLAY

ニフティ株式会社

ニフティ株式会社 の技術ブログ

487

はじめに ニフティでWEBサービスの開発・運用を担当している渡邊です。 2024年9月に@nifty天気予報のフルリニューアルを行いました。 このプロジェクトでは従来のインフラ構成を刷新し、サーバレスアーキテクチャを採用しました。 従来のWEB三層構造からサーバレス構成への移行によるコストの比較と、効果について紹介していきます。 旧環境の構成と問題点 構成 WEBサーバ、アプリケーションサーバ、DBサーバからなるWEB三層構造のアーキテクチャを、EC2とRDSを使って構築していました。 問題点 この構成には以下のような問題点がありました。 固定費用が高く、トラフィックが少ない時期でも一定のコストが発生 サーバーの保守・運用に多くの工数が必要 サーバレスの採用 従来の問題点を解決するため、トラフィックに応じた柔軟なスケーリングと、運用負荷の大幅な削減が見込める、サーバレスアーキテクチャの採用を決めました。 また、データ構造の変更にも柔軟に対応できる構成も目指しました。 採用したサービス AWSをクラウドに採用しているので、AWSが提供するサーバレスサービスを活用しました。 WEB Amazon ECS(Fargate) API AWS AppSync(GraphQL) バッチ処理 Amazon S3 AWS Lambda AWS Step Functions DB Amazon Aurora Serverless v2(PostgreSQL) 効果 約4ヶ月運用してみての効果を見ていきます。 インフラコスト サービス 旧環境 新環境 削減率 WEB 100USD 10USD 90% アプリケーション 400USD 110USD 72.5% DB 1,200USD 230USD 80.8% その他 400USD 300USD 合計 2,100USD 740USD 64.8% ※金額は実数とは変えてあります 旧環境はバッチ処理が複雑だったため、要求スペックが高い状況での運用を行っていました。 加えて、WEBサーバをECSにしたことで負荷に応じてスケールするようになったため、常時起動のときと比較すると削減率が非常に高くなりました。 アプリケーション(API)に関しては、AppSyncがフルマネージドサービスということもありますが、redisが標準で搭載されているため、キャッシュによるコスト最適化が大きな要因だと考えています。 加えて、バッチ処理もデータが更新されたタイミングのみしか起動しないため、削減効果が大きかったようです。 DBに関しては、AppSyncを使う以上、Aurora Serverless v2しか選択肢がなかったのですが、こちらもECSと同様に負荷に応じてスケールするため削減率が大きかったようです。 これは思わぬ副産物でした。 実装コスト サーバレスを採用したことでインフラコストだけでなく、実装コストも削減できました。 フロントエンドとバッチの実装では、コンテナを基盤として実装し、GitHub から ECR を経由してデプロイ先へという構成を構築することで、デプロイプロセスを簡略化できました。 アプリケーションサーバにAppSyncを採用したことで、GraphQLを実行するサーバが不要になり、スキーマとリゾルバのみの実装でAPIを構築することができました。 リリース以降、データ更新の遅延などの問題も特には発生しておらず、システムは安定稼働し続けています。 注意点 今回のリニューアルではサーバレス化により大幅なコスト削減を実現できましたが、これが全てのサービスに適しているわけではありません。 一般的にサーバレスはトラフィックが予測可能で、変動が大きいサービスに特に効果的と言われています。 一方で、常時高負荷が発生するサービスや、レイテンシーを重視するようなサービスでは従来型のサーバ構成の方が適している場合もあります。 また、サーバレスサービスはベンダーロックインが発生しやすく、細かな調整が制限されるので、サービス要件を満たしているかは十分に検討する必要があります。 最後に 今回はWEB三層構造で作られていた従来の構成から完全なサーバレス構成にインフラを切り替えた効果について書いてきました。 サーバレス化によって大幅なインフラコスト削減を実現できました。 当初予測していた以上の削減効果が得られたのは大きかったです。 今回のように全てをサーバレスにするのではなく、部分的にサーバレスを活用するだけでもインフラコスト削減は可能ですのでぜひ検討してみてください。 今回のプロジェクトの詳細については、以前行ったNIFTY Teck Talkにて話していますのでこちらをご確認ください。
アバター
はじめに 宮永です。2025年1月のJANOG55 meeting(以下JANOG55)に参加してきました。前回のJANOG54に引き続き、2回目の参加です。 感想などを書いていきます。 JANOG Meetingとは JANOGとはJApan Network Operators’ Groupを意味し、 インターネットに於ける技術的事項、および、それにまつわるオペレーションに関する事項を議論、検討、紹介することにより 日本のインターネット技術者、および、利用者に貢献することを目的としたグループです。 https://www.janog.gr.jp/information/  より引用 年に2回meetingが開かれ、日本全国から様々なバックグラウンドを持つ人が集まります。インターネットに関する技術やノウハウについての発表や議論が行われます。 JANOGは「カンファレンス」ではなく「ミーティング」と呼ぶことを大切にしているようです。ただ発表を聞く会ではなく、関係者と積極的なコミュニケーションを取る場として全国から大勢のインターネット技術者たちが集まります。 どうして参加したのか? 私は社内の「ISPオペレーションサブチーム」に所属しており、日々ニフティのお客様が快適にインターネットを使えるようにするための業務を担当しています。現在の業務にぴったりのコミュニティであると思い、参加しました。 参加にあたっての社内説得方法 私「前回のJANOG楽しかった。参加したいです」 上司「いいですよ」 これだけです。いやぁ、ニフティって本当にいい会社ですねぇ(ダイレクトマーケティング)(ここで採用ページのリンクを貼る) https://recruit.nifty.co.jp/ JANOG55現地 業務の都合上、全3日間のうち2日目夕方から参加しました。 JANOG55は京都府にある「京都市勧業館みやこめっせ」で開催されました。平安神宮のすぐ横にあります。立派な鳥居がありました。 建物内はおしゃれな装飾がされているところもあります。京都はどこに行っても京都らしさが見えて良いですね。 参加者はスーツの人2割、襟付きシャツ&カーディガンくらいの格好が4割、企業ロゴの入ったパーカー等を着ている人が2割、その他2割といった感じです。普段働くときに着ている服で来ているようで、企業の色もみえてきます。 参加者の所属企業も様々で、私のようなISP事業者だけではなくネットワーク機器メーカー、コンテンツ事業者などなど、様々な人が集まっています。また、インターネット関連企業でも技術者だけでなく、営業といった非技術者が勉強のために参加する方も多いようです。 プログラム 大きな会場3か所で様々な発表が行われます。どれも楽しそうな発表ばかりです。興味のある発表が二部屋同時に行われることもあり、どこに行くか迷います。 発表部屋はかなり広いです。奥まで探すと大体空席があるので、座席不足で困る感じではありませんでした。ただし、後ろの方に座るとスライドが見えず結構困るため、どうしても参加したい発表の際は早めに座席を確保したほうが良さそうです。 また、ストリーミングとアーカイブ配信のある発表もある中で、 オフレコ(撮影/録画/SNS投稿禁止)のプログラム もあります。同じ問題を抱える技術者同士で、大きな声では言えないけど困っている事やノウハウなどを共有・議論する場となっています。コミュニケーションを大切とするJANOGならではの仕組みですね。 JANOG Slack オンライン・現地参加問わず参加できるSlackがあります。発表会場ごとのチャンネルがあり、実況や質問・感想が飛び交っています。現地でマイク使って質問するよりハードルは低いかもしれまん。 より多くの意見を知ることもできるので、参加する際はSlackもチェックしたほうが良いと感じました。会場Wi-Fiはかなり整備されているので、インターネット接続も問題ありません。 会場NW不調について参加者が運営へslackで問い合わせしている様子も見れます。参加者がネットワークのプロなので、質問の仕方が相当専門的でおもしろかったです。 また、発表や会場についてのアナウンスだけではなく、現地のネットワーク情報現地の美味しいご飯の情報といった雑談も飛び交っています。slackで話題になっていたラーメン屋に行ってきました。とてもおいしかったです。 協賛ブース JANOGは他のカンファレンスよりも協賛ブースが充実しているのではないでしょうか。今回は158企業が協賛ブースを出していたそうです。 私のようなISP事業者だけではなく、インターネット関連企業・ネットワーク機器メーカー・コンテンツ事業者・サーバ関連企業などなど、様々な企業が展示を行っています。 協賛ブースでは普段の業務でお世話になっている方と何名かお会いすることができました。いつもはメールでしか接点が無い方と色々な会話をする機会になりました。(お声かけくださった皆様、ありがとうございました!) 展示を行っている多くの企業が特色ある展示・グッズ配布を行っており、どこを見ていいのか迷ってしまいます。プログラム発表だけではなく、しっかり協賛ブースを見る時間も確保したほうが良いです。 この広い会場がすべて協賛ブースです。 懇親会 2日目終了後、懇親会が近くのホテル会場で開催されました。 私は運よくチケットを購入できましたが、受付開始後すぐに売り切れになるほど大人気です。毎年早々に売り切れるそうなので、参加したい方は発売開始当日に粘る必要があります。 参加者は1000人を超えるそうです。相当大きな部屋にもかかわらず、まっすぐ歩けないほど人が沢山います。 会場には美味しそうな食事やお酒が沢山おいてあります。好きなだけ飲み食いしてよいスタイルです。学生の方も多く参加するので、ノンアルコールのお茶やジュースもありました。飲まない人も安心です。 知り合いのいない状態で参加し不安でしたが、名札の会社名を見て多くの方が話しかけてくださいました。話してみると意外なつながりのある人も多く、大変有意義な時間でした。(お声かけくださった皆様、ありがとうございました!) 反省点 もっともっと積極的にコミュニケーションをとればよかった 前回の反省を生かし、会場の名前を知っている人何名かに話しかけてみました。一度お話してみたい人と話せたり、業務上の疑問が解消できたりと、非常に有意義な時間となりました。 しかし、会場にはまだまだ同じような業務をしている人がいるはずです。今後のエンジニア人生のためにも、より多くのコミュニティに触れたいなと思いました。 大き目のカバンを持参するべきだった 協賛ブースでは多くの企業がたくさんグッズを配っています。どれも面白く、たくさん受け取ってしまうと持ち帰りが大変でした。 社名のわかる服装をすると話すきっかけになりそう ありがたいことに、 JANOGでお話した方はほぼ全員ニフティを知ってくださっていました。せっかく知名度のある会社に勤めているので、社名のわかるパーカー等を着たらより話しかけるきっかけが生まれたかな?と思いました。 着脱しやすい上着にするべきだった 会場は部屋や時間によって寒暖差が大きかったです。かさばる上着を何度も脱ぎ着する必要があり、後悔しました。夏のJANOGでも同じような後悔をしたので、季節問わず気を付けたほうが良さそうです。 まとめ インターネット技術者とのコミュニケーションや発表により、様々な知見を得たり、新たなコミュニティを知ることができたり、普段の仕事中ではなかなかできない経験ができました。 次回は2025年7月に島根県で開催されるそうです。みなさんもぜひ参加してみてください!
アバター
以前の記事 ではティザーサイトの公開をお伝えしていた「NIFTY Tech Day 2025」について、登録サイトを公開いたしました。 また今回、Xでのプレゼントキャンペーンもございます! 概要 登録サイト https://techday.nifty.co.jp/2025/ 日程 2月8日(土)13:00〜18:40        18:40~ 懇親会(オフライン) 開催方式 ハイブリッド開催 会場参加:ニフティ株式会社 本社(新宿フロントタワー 18F) オンライン:YouTubeライブ配信(配信URLは準備次第公式サイトに掲載予定) ※ 対面、オンラインのハイブリッド開催です。いずれかの方法で参加ください。 ※ 本イベントは、対面・オンラインどちらも登録制になります。登録は無料になります。 ※ 会場参加の方には、NIFTY Tech Day 2025 オリジナルトートバッグ、ステッカー、NIFTY Tech Book#2のほか、各種体験ブース設置など来場者特典を用意しています。 プログラム抜粋 セッション AIと協働する次世代開発スタイル – 求められる新たなスキルと役割の変化 スクラムマスター入門者のための学習マップ 効果的な学びと実践 ニフティLT大会大公開スペシャル など、7つのセッションを実施いたします。 その他のセッションにつきましては、 公式サイト をご確認ください。 体験ブース 「スクラムプチOST」 スクラムに関する悩みを共有し、みんなで解決策を探るインタラクティブなブース 「もじこえ体験」 テキストを読み上げてくれる画期的なチャットツール「もじこえ」を体験できます。 「ニフティキッズにおける生成AI活用展示」 「ニフティキッズ×生成AI」の活用をサービス、システム、アーキテクチャ含め大公開! 「エンジニア謎解き」 ニフティ社員が謎を出題します! 参加方法 公式サイト の「Tech Dayに参加する」からEventRegistにて、登録をお願いいたします。 プレゼントキャンペーンについて 現在、NIFTYDevelopers (@NIFTYDevelopers)のX公式アカウントにて、 抽選でAmazonギフトカードが当たるフォロー&リポストキャンペーンで行っています! 皆様のご参加をお待ちしております。 ■対象ポスト https://x.com/NIFTYDevelopers/status/1880086586844672323
アバター
この記事は、 ニフティグループ Advent Calendar 2024 23日目の記事です。 はじめに こんにちは、新卒1年目の後藤です。 今回は、S3内にあるALBのアクセスログをAthenaで確認する方法について、記載していこうと思います。 Amazon Athenaとは クラウドインフラの運用において、ログ分析は不可欠です。 AWSでは、多くのサービスがログをAmazon CloudWatchとAmazon S3で保存しています。 しかし、蓄積された大量のログデータを効率的に分析することは、いずれも困難です。 ここで登場するのがAmazon Athenaです。 Amazon Athenaは、標準SQLを使用してS3のデータを直接クエリできるサーバーレスの対話型クエリサービスです。 Athenaを使用することで、複雑なインフラ設定なしに、S3の大規模なログデータを効率的に分析できます。 環境設定 Athenaを使用するには、以下のAWSサービスへのアクセス権限が必要です: Amazon S3 S3バケットへの読み取り権限 Amazon Athena Athenaクエリの実行権限 S3バケットの設定 1. ログを保存するS3バケットを作成または確認します。 2. バケットポリシーを適切に設定します。 Athenaからの読み取り権限を許可する { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "athena.amazonaws.com" }, "Action": [ "s3:GetBucketLocation", "s3:GetObject", "s3:ListBucket" ], "Resource": [ "arn:aws:s3:::your-bucket-name", "arn:aws:s3:::your-bucket-name/*" ] } ] } 必要に応じて、特定のIAMロールやユーザーからのアクセスのみを許可する { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::123456789012:role/YourRoleName" }, "Action": [ "s3:GetObject", "s3:PutObject" ], "Resource": [ "arn:aws:s3:::your-bucket-name/*" ] } ] } 注意: 上記のポリシーは一例なので、実際の環境に合わせて適切に調整してください。 3. 必要な権限を付与します。 s3:GetBucketLocation – バケットの場所を取得する権限 s3:GetObject – オブジェクトを読み取る権限 s3:ListBucket – バケット内のオブジェクトを一覧表示する権限 s3:PutObject – クエリ結果を保存する権限(結果を S3 に保存する場合) 設定 ログを保存するS3バケットを指定 Location of query result でログを保存するS3バケットを指定します。 Browse S3で選択することも可能です。 データベースの作成 以下コマンドをクエリで実行します。 CREATE DATABASE your_database; [Run]  (実行) をクリックするか、 Ctrl+ENTER  キーを押します。 左側の  [Database]  (データベース) リストから、現在のデータベースとして  your_database を選択します。 テーブルの作成 今回はALBのアクセスログ用のテーブルを作成します。 使用したいログやデータに合わせて変更してください。 作成したデータベース( your_database )を選択 クエリで以下コマンドを実行 CREATE EXTERNAL TABLE IF NOT EXISTS alb_access_logs ( type string, time string, elb string, client_ip string, client_port int, target_ip string, target_port int, request_processing_time double, target_processing_time double, response_processing_time double, elb_status_code int, target_status_code string, received_bytes bigint, sent_bytes bigint, request_verb string, request_url string, request_proto string, user_agent string, ssl_cipher string, ssl_protocol string, target_group_arn string, trace_id string, domain_name string, chosen_cert_arn string, matched_rule_priority string, request_creation_time string, actions_executed string, redirect_url string, lambda_error_reason string, target_port_list string, target_status_code_list string, classification string, classification_reason string, conn_trace_id string ) PARTITIONED BY ( year string, month string, day string ) ROW FORMAT SERDE 'org.apache.hadoop.hive.serde2.RegexSerDe' WITH SERDEPROPERTIES ( 'serialization.format' = '1', 'input.regex' = '([^ ]*) ([^ ]*) ([^ ]*) ([^ ]*):([0-9]*) ([^ ]*)[:-]([0-9]*) ([-.0-9]*) ([-.0-9]*) ([-.0-9]*) (|[-0-9]*) (-|[-0-9]*) ([-0-9]*) ([-0-9]*) \\"([^ ]*) (.*) (- |[^ ]*)\\" \\"([^\\"]*)\\" ([A-Z0-9-_]+) ([A-Za-z0-9.-]*) ([^ ]*) \\"([^\\"]*)\\" \\"([^\\"]*)\\" \\"([^\\"]*)\\" ([-.0-9]*) ([^ ]*) \\"([^\\"]*)\\" \\"([^\\"]*)\\" \\"([^ ]*)\\" \\"([^\\\\s]+?)\\" \\"([^\\\\s]+)\\" \\"([^ ]*)\\" \\"([^ ]*)\\" ?([^ ]*)?' ) LOCATION 's3://your-bucket-name/access-log-folder-path/' TBLPROPERTIES ( 'projection.enabled'='true', 'projection.year.type' = 'integer', 'projection.year.range' = '2024,2050', 'projection.year.interval' = '1', 'projection.month.type' = 'integer', 'projection.month.digits'='2', 'projection.month.range' = '1,12', 'projection.day.type' = 'integer', 'projection.day.digits'='2', 'projection.day.range' = '1,31', 'storage.location.template' = 's3://your-bucket-name/access-log-folder-path/year=${year}/month=${month}/day=${day}' ); [Run]  (実行) を選択。 テーブル  alb_access_logs が作成され、 your_database  データベースの [ Tables ] (テーブル) リストに表示されます。 公式から追加した箇所の説明 PARTITIONED BYの説明 PARTITIONED BY テーブルをパーテーション化することを宣言しています。 パーテーション化は、大規模なデータセットを小さな、管理しやすい部分に分割することです。 year string , month string , day string パーテーションキーとして使用され、それぞれがstring型で定義されています。年、月、日の順で階層的にパーティションが作成されます。 TBLPROPERTIESの説明 'projection.enabled'='true' Partition Projectionを有効にします。クエリ時にPartitionを動的に生成できます。 'projection.year.type' = 'integer' , 'projection.month.type' = 'integer' , 'projection.day.type' = 'integer' 年、月、日のPartitionがそれぞれ整数型であることを指定します。 'projection.year.range' = '2024,2050' 年の範囲を2024年から2050年までと指定します。 'projection.year.interval' = '1' 年のPartitionが1年ごとに増加することを指定します。 'projection.month.digits'='2' , 'projection.day.digits'='2' 月と日のPartitionが2桁の数字で表されることを指定します。 'projection.month.range' = '1,12' 月のPartitionが1から12までの範囲であることを指定します。 'projection.day.range' = '1,31' 日のパーティションが1から31までの範囲であることを指定します。 'storage.location.template'  S3内のデータの実際の格納場所のテンプレートを指定します。 ${year} , ${month} , ${day} はそれぞれ年、月、日の値に置き換えられます。 データの取得 テーブルを作成したら、実際にデータを取得してみます。 基本的なクエリ 最新のログエントリを10件取得する: SELECT * FROM alb_access_logs WHERE year = 2024 AND month = 12 AND day = 12 LIMIT 10; コンソールの下に結果が表示されるので確認します。 パフォーマンスとコスト最適化 Athenaにデータを設定する際の最適化 1. パーテーションの活用 適切なパーティションにより、スキャンするデータ量を減らし、クエリ速度を向上させ、コストを削減することが可能です。 今回はs3://~/YYYY/MM/DDのようにデータが保存されているので、以下のように PARTITIONED BY でパーテーションを設定することが可能です。 PARTITIONED BY ( year string, month string, day string ) 2. 圧縮の使用 ログファイルをgzipなどで圧縮することで、ストレージコストとクエリ時間を削減できます。 gzipに圧縮した際は、テーブル定義の TBLPROPERTIES 内に 'compression.type'='gzip' を追加することを忘れないようにしましょう。 実際に検索する際の最適化 必要な列のみを選択 SELECT * の使用は避け、必要な列のみを指定します。 例: SELECT time, elb_status_code, request_url FROM alb_access_logs WHERE ... LIMIT句の使用 大量のデータを扱う場合、LIMIT句を使用して結果セットを制限します。 例: SELECT * FROM alb_access_logs LIMIT 100; これらの最適化技術を適用することで、Athenaの使用をより効率的にし、コストを抑えることができます。定期的にクエリのパフォーマンスとコストを監視し、必要に応じて調整を行うことが重要です。 まとめ Amazon Athenaを使用してS3のログを分析することで、大規模なデータセットに対して効率的かつ柔軟なクエリが可能になります。 主要なポイントは以下の通りです: Athenaの基本設定 クエリ結果の保存場所の指定 データベースとテーブルの作成 テーブル設計の重要性 適切なカラム定義 パーティショニングの活用 効率的なクエリ実行 基本的なSQLクエリの構造 パーティションを利用したデータフィルタリング パフォーマンスとコスト最適化 必要な列のみの選択 LIMIT句の使用 データの圧縮 Athenaを活用することで、複雑なインフラストラクチャを管理することなく、S3に保存されたログデータから迅速に得ることができます。 パフォーマンスとコストの最適化テクニックを適用することで、より効率的にデータ分析を行うことが可能です。 最後に、常にAWSの ベストプラクティス と セキュリティガイドライン に従い、コストとパフォーマンスのバランスを取りながら、Athenaを活用しましょう。 参考文献 https://docs.aws.amazon.com/ja_jp/athena/latest/ug/what-is.html https://docs.aws.amazon.com/athena/latest/ug/create-alb-access-logs-table.html 次の記事もお楽しみに!
アバター
ニフティは創業以来37年間、時代の変化に応じて挑戦を続け、最新技術を活用しながら社員とともに成長してきました。 「NIFTY Tech Day 2025」では、エンジニアたちがこれまでに積み重ねてきた知識や経験、これからの展望について発表します。 昨年度もご好評をいただきましたゲストスピーカーのセッションに加え、今年は「With Us, You Can (Learn). エンジニアの学習と成長」というコンセプトのもと、当社エンジニア社員が登壇するセッション、体験型ブース(会場参加者限定)等もご用意いたします。 開催に先立ち、先行登録受付のためのティザーサイトを公開いたしました。ご登録いただくことで登壇内容のアップデート時のご連絡や、参加申し込み開始時に優先的にご連絡させていただきます。 概要 ティザーサイト https://techday.nifty.co.jp/2025/ 日程 2月8日(土)13:00〜18:40        18:40~ 懇親会(オフライン) 開催方式 ハイブリッド開催 会場参加:ニフティ株式会社 本社(新宿フロントタワー 18F) オンライン:YouTubeライブ配信(配信URLは準備次第公式サイトに掲載予定) ※ 対面、オンラインのハイブリッド開催です。いずれかの方法で参加ください。  ※ 会場参加の方には来場者特典を用意しています。 まずは通知登録を! ティザーサイト の「開始通知を登録する」から、通知のご登録をお願いいたします。 ※ ご登録いただけますと、参加申し込み開始時にご連絡いたします。 ※ またセッションやノベルティなどの最新情報も随時お届けします。
アバター
この記事は、 ニフティグループ Advent Calendar 2024 21日目の記事です。 もうすぐクリスマスですね。みなさんはプレゼントをもらえるなら何が欲しいですか? 私は最近寝ているときに乾燥で喉がやられるので強靭な喉が欲しいです。 SREチームの島です。 普段はサービスの信頼性向上のため障害数の削減やSRE推進をメインに活動しています。 私事ですがニフティに転職して来年の3月で丸2年になります。 今回は私がニフティでこの2年間でやってきたことや成長したと思うことを振り返ってみようと思います。 私の経歴について はじめに私の経歴について紹介します。 前職ではずっとプログラマーやチームリーダーとしてソフトウェアエンジニアリングに従事していました。そして2社目となるニフティでSREのポジションに就きました。 1社目(2009〜2023) 主にSES、SI事業に従事し、お客様先に常駐したり、自社のプロジェクトチームでソフトウェアエンジニアリングに携わっていた 入社〜8年ほどは開発工程をメインに、それ以降はプロジェクトリーダーとしてプロジェクト管理を経験 技術としてはJava、PHP、C#、TypeScript、MySQLなどなど。フロントエンド、バックエンド開発がメイン インフラやクラウド環境の構築経験はなし(クラウド環境での開発経験はあり) 2社目(2023〜現職) ニフティにSREとしてジョイン ニフティでやったこと SREとして 下記のSRE本のプラクティスに沿って私が主にやってきたことをあげていきます。 https://sre.google/sre-book/part-III-practices/ Monitoring SLI/SLO、アラート、ダッシュボードの作成 @nifty天気予報 のリニューアルプロジェクトにEmbedded SREとして参画しました。その際にAmazon CloudWatch Application Signalsを使ったSLI/SLOの計装、アラート設定、CloudWatch Dashboardの構築を行いました。モニタリングをSREが責任を持って担当することで開発者が開発に専念できたと思っています。 FourKeysの計装 FourKeysを計装し、社内のDevOpsチームへ導入を行いました。 社外カンファレンスで登壇 もさせていただきました。 こちらはその後全社的な取り組みへと発展し、現在はシステム・プロダクトごとの運用コストの可視化・最適化を行うプロジェクトに参画しています。 Postmortem / Root Cause Analytics ポストモーテムの実施支援 ポストモーテムの実施が滞っているチームに声をかけ、私がファシリテーションしながら一緒にポストモーテムの実施や再発防止アクションの促進を進めています。 Testing + Release producers 継続的デリバリの実装 こちらも @nifty天気予報 のリニューアルプロジェクトに参画したときの話で、ecspressoを使ったECSへのCDを実装しました。 Development IaCによるインフラ構築 いくつかのプロジェクトでIaC(Terraform)によるAWS環境の構築を経験しました。最初はコードでインフラ構築ができることに感動したことを覚えています。 その他 弊社にはチームを横断した活動を行うワーキンググループ(WG)という活動があり、私は下記の2チームに参画しています。 システム安定化WG 障害の検知、復旧までにかかる時間を短くする・障害数を減らすことを目的としたWGです。 このWGの取り組みとしては下記のようなことを行いました。 障害発生時のエスカレーションフローの見直し 障害報告書テンプレートの改善 ポストモーテムの実施支援 採用ブランディングWG ニフティのエンジニアリングカルチャーを知ってもらうためにエンジニアが主体となってテックやカルチャーの発信、ブランディング活動を行うWGです。 個人的な活動も含め、ブランディング活動としては下記のようなことに携わりました。 昨年開催した弊社テックイベント「NIFTY Tech Day 2023」の運営スタッフ 開発チームへのインタビューの実施・記事の作成 社外カンファレンスに登壇 弊社のテックトークイベント「 NIFTY Tech Talk 」に2回登壇 やってよかったこと ワーキンググループに入ったことで横断活動がしやすくなった 弊SREチームは社内のチームを横断した活動を多く行なっています。ただ、中途入社の身としては全く関係性のないチームに横からズカズカ入っていって「おい、SREしているか」と言いに行くのは抵抗感がありました。 しかし上に挙げたWGの活動を通して徐々に存在を認知してもらえるようになり、私自身も知っている人が増えてきたことで横断活動への抵抗感も薄れていきました。 そういった意味でもWGで活動したことはとても大きかったと思います。 社外のカンファレンスに参加するようになった 実はニフティに入って初めてエンジニアによる勉強会やカンファレンスが開催されていることを知りました。それまでは業務以外でエンジニアと関わることはなく、非常にもったいないことをしていたなと思います。 社外のカンファレンスに参加するようになって視野を広く持てるようになったと思います。例えば「こういう課題がある、どう解決したらいいんだろう。そういえばこの前のカンファレンスで他社のSREが同じような事例を紹介していたな」という感じで課題解決のヒントを探せるようになりました。 入社当初、分からないことだらけで絶望していた自分にとって社外カンファレンスは大きな支えになっていました。 成長したと思うこと 未経験の技術を習得した 前職とは技術スタックが全く違っていたので未経験の技術を多く学ぶことができました。学ぶだけでなく実務で使えたので身についていきました。実務で初めて使った技術、ツールだけでも以下のようなものがあります。 AWS Lambda CloudWatch StepFunctions EventBridge S3 IAM GCP BigQuery Cloud Run GitHub GitHub Actions ecspresso Terraform Python Grafana Tableau Looker Studio Notion SREingできるようになった まだまだできないこともありますが入社時と比べると多くのSREingができるようになりました。当時はSLIもSLOも机上の知識だけで、いざ設定、運用しろと言われてもできませんでした。今では目標値の設定から計算式の決定、計装まで実践できるようになりました。 ポストモーテムも入社前に学習はしていましたが実際に書いたことはありませんでした。ある日、障害が発生した際にポストモーテムをおこなう機会がありました。そのときの経験から、開発を進めながら障害対応を行う大変さを知りました。自身がDevOpsを経験することで開発者の思いを知ることができました。 IaCによるインフラ構築も、以前はインフラエンジニアが構築してくれた環境を使って開発をするだけでした。今ではすべてではないですがIaCを使って自分でインフラを構築できるようになりました。 まとめ 普段は「自分、成長しているな〜」と思うことはあまりないですが、改めて振り返ってみると色々できることが増えているなと感じることができました。その反面、まだまだプロダクトに貢献できていない、信頼性を向上できているのだろうかと悩むこともありますが年末くらいは今年やってきたことを自分で褒めたい思います。 みなさんも今年の振り返りをして自分を讃えてみてはいかがでしょうか。 明日は、@ryugakさんです! それではMerry Christmas
アバター
この記事は、 ニフティグループ Advent Calendar 2024  15日目の記事です。 こんにちは! ニフティでエンジニアをしている西根です! 普段はニフティポイントクラブの開発運用保守をしています。 皆さんはgit revertは普段使用していますか? 私は最近までほとんど使用したことがありませんでした。 この記事はマージした後にrevertしたけど、その後どうすれば良いか何もわからない!困った!という人向けに書いていきたいと思います。 背景 先日ステージング環境へデプロイしたところ不具合が見つかり、一度切り戻しを実施することになりました。 今までのリリースではほとんど切り戻しを実施することがなく、初めてGitHubのrevertを使用しました。 ステージング環境へ反映した内容に少し修正を加えるだけだったので、revertして切り戻し実施後に元々developブランチにマージしたfeatureブランチにそのまま変更を加えました。 が、再リリースでdevelopへのマージ時に見えないコンフリクトが発生し、どうしたら良いかわからなくなってしまったので、同じような人がいたら助けになれば幸いです。 そもそもrevertとは? 公式ドキュメント では以下のように説明されています。 1 つ以上の既存のコミットがある場合、関連するパッチによって導入された変更を元に戻し、それを記録するいくつかの新しいコミットを記録します。これには、作業ツリーがクリーンであること (HEAD コミットからの変更がない) が必要です。 https://git-scm.com/docs/git-revert.html つまり、一言で言うと既存のコミットを打ち消すコミットを作成するコマンドです。 自分がいるブランチの最新のコミット(HEAD)からコミットしていない変更がない場合に使用できます。 なぜ再度マージする時にハマってしまったのか 前提としてブランチの運用ルールは以下の通りです main:本番稼働しているコード。 develop:ステージング環境で稼働しているコードで、本番反映時にmainにマージする。 feature:developブランチから切っており、機能開発時に使用する。ステージング反映時にdevelopブランチにマージする。 切り戻しを実施する時にdevelopブランチにマージしたPRをGitHub上でrevertし、 ローカルにリモート上のrevertを取り込まないまま featureブランチで修正を行なってしまいました。 今回はここが問題で、修正後のfeatureブランチをdevelopブランチにマージする時に、Gitが以下の状態を解決しようとしますが、同じ箇所に変更を加えている場合にはどちらの状態を採用すべきかを自動で判断することができず、コンフリクトとして扱われます。 featureブランチの変更(一度developブランチにマージされた変更とrevert後の修正) developブランチのrevertによる打ち消し コードの内容自体は同じように見えるのですが、Gitとしては変更履歴が異なるため、コンフリクト判定されてしまいました。 結論 今回のようなケースを避けるためには以下の手順でrevert後の修正をするのが良さそうです。 revertしたブランチをpullしてリモートとローカルの差分をなくす git pull origin { revertしたブランチ } revertしたブランチでrevertのマージコミットのコミットハッシュを調べる git log --graph * がついているものがコミット 以下の場合は コミットハッシュ3 のコミットがrevertのマージコミットになる revertしたブランチで再度revertする git revert -m 1 { コミットハッシュ } -m : mainlineのm。マージコミットは親コミットが2つあるのでどちらに戻すのかを指定してあげる必要がある。親コミットの番号は1, 2で指定でき、1はマージされた側・2はマージする側を表している 今回はマージされたdevelopブランチの方に戻したいので、1を指定する そこから修正ブランチを切る git switch -c { 修正ブランチ } 修正ブランチで修正しリモートにpushする git add { ファイル } git commit -m "{ コミットメッセージ }" git push origin { 修正ブランチ } GitHubでターゲットブランチにマージする まとめ (当たり前といえば当たり前ですが)リモートとの差分があるままローカルで作業しないことは常に心に留めておきたいですね。 目には見えないコンフリクトには皆さんもお気をつけください
アバター
この記事は、 ニフティグループ Advent Calendar 2024 20日目の記事です。 はじめに こんにちは。会員システムグループに所属しています、たかたか と申します。 最近、健康への意識が高まり、運動を始めました。ただし、食事制限はまだ行っていません。無理をせず徐々に健康体へ近づいていければと思っています。 さて、本ブログでは、最近起こしてしまった失敗について書いていこうと思います。エンジニアとしての仕事や日常生活の中で、もしかしたら起こるかもしれないミスや失敗を共有することで、皆さんの参考になればと思います。 起こったこと とあるシステムのリプレイスのメンテナンスが終了した後、それは起こってしまいました。 なんと、HTTPSでの接続で証明書の検証がうまくいかず、サーバー間の通信ができなくなってしまったのです。 まあ、そんなこともあるよね、と感じていただけている方もいるかと思いますがその諸々について書いていこうと思います。 まずは証明書の検証について確認 まず、証明書の検証について、簡単に説明させていただきます まず、クライアントがサーバーにHTTPS接続をお願いします サーバーは自分の身分証明書(デジタル証明書)をクライアントに見せます クライアントは以下のようなチェックを行います: 信頼できる機関が発行した証明書かどうか確認 証明書の親子関係(証明書チェーン)が正しいか確認 証明書の期限が切れていないか確認 証明書が無効になっていないか確認 証明書に書かれている名前と接続先が同じか確認 すべての確認がOKだった場合、安全な通信路を作ります 暗号化のための鍵を作って交換 安全な通信をスタート このチェックの中で一つでも問題があると、接続できなくなります。 経緯 AWSのマネジメントコンソールから必要なリソースを作成し、システムの構築を進めていました。その中で、サーバー間の通信にはHTTPSを使用することになっていたのですが、中間証明書とサーバ証明書の設定を間違えてしまいました。 この設定画面です 本番環境へのデプロイ前の検証では、curlコマンドで動作確認を行ってしまい、問題に気付くことができませんでした。 仕組み的にどうすればよかったのか 証明書の検証について、今回原因となったのは設定を間違えてしまったというところが根本的な原因です。しかし、ちゃんと動いているかどうかの確認で、HTTPのリクエストを送るツールが証明書をどのように、どのような環境で検証しているかというところも大事なポイントだと思っています。 サーバー証明書はPEM形式で サーバー自身のサーバー証明書 中間証明書 ルート証明書(通常はサーバーには含めません) の内容を記載することができます。 # サーバー証明書 -----BEGIN CERTIFICATE----- hogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehoge hogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehoge -----END CERTIFICATE----- # 中間証明書 -----BEGIN CERTIFICATE----- hogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehoge hogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehoge -----END CERTIFICATE----- # ルート証明書(通常はサーバーには含めません) -----BEGIN CERTIFICATE----- hogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehoge hogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehoge -----END CERTIFICATE----- webブラウザなどは中間証明書の内容がなくてもサーバー証明書の内容から補完することができたりしますが、スクリプトからhttpリクエストを送信しようとすると失敗することがよくあります。 サーバー証明書の設定は、使用するツールや環境によって検証結果が異なる場合があります。実際の運用環境では、複数のツールで検証を行うことが重要です。それでは、具体的な検証例を見ていきましょう。 検証 試しに、記載されているチェーンが不完全な証明書を用意し、検証してみましょう。 設定を間違った証明書 # サーバー証明書だけを書く -----BEGIN CERTIFICATE----- hogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehoge hogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehoge -----END CERTIFICATE----- 以下のツールを使用して、私の端末で検証したいと思います curl 何かhttpリクエストを送りたいときよく使いますよね。 今回は動作検証をこれだけで実施してしまったので、失敗に気づけなかったです。 基本的に、ローカルに保存されている証明書ストアの情報と照合してちゃんと検証が行われるはずですが、私の使用している端末ではたまたまラッキーで証明書の検証が通過してしまいました。 pythonのrequestsモジュール pythonでhttp通信を行うときの鉄板ですよね。certifiというライブラリに保存されている証明書群に依存しており、ブラウザのように証明書の情報を自動で補完したりすることはないです。誤った証明書が設定されている等の原因で通信ができなくなったりします。 openssl 証明書の検証といえばこれが使用されます。 証明書のチェーンが間違っていれば自動補完とかはせず、検証に失敗しますので、ここがOKなら大丈夫でしょう。 ※入力・出力結果は一部改変しています curlの実行結果 $ curl -D - <https://example.com> HTTP/2 200 server: Apache/2.4.6 (CentOS) cache-control: no-store, no-cache content-type: text/html; charset=UTF-8 date: Mon, 16 Dec 2024 07:40:40 GMT pragma: no-cache set-cookie: dnzHashcmd=fin; content-length: 6879 コンテンツ ... 普通に返ってきましたね。 何か異常は見受けられません requestsの実行結果 Python 3.11.0 (main, Mar 15 2024, 14:30:35) [Clang 15.0.0 (clang-1500.0.40.1)] on darwin Type "help", "copyright", "credits" or "license" for more information. >>> import requests >>> requests.get("<https://example.com>") requests.exceptions.SSLError: HTTPSConnectionPool(host='example.com', port=443): Max retries exceeded with url: / (Caused by SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:992)'))) 失敗しておりますね。 エラー内容を見ると、SSLCertVerificationErrorの文字が。 opensslの実行結果 $ openssl s_client -connect example.com:443 -showcerts 0 s:/C=JP/ST=Tokyo/L=Minato-ku/O=Example Corporation/CN=www.example.co.jp i:/C=JP/O=Example CA/CN=Example Intermediate CA -----BEGIN CERTIFICATE----- hoge(サーバー証明書の内容)hoge -----END CERTIFICATE----- Server certificate subject=/C=JP/ST=Tokyo/L=Minato-ku/O=Example Corporation/CN=www.example.co.jp issuer=/C=JP/O=Example CA/CN=Example Intermediate CA --- SSL handshake has read 2482 bytes and written 757 bytes Verification error: unable to verify the first certificate --- New, TLSv1.3, Cipher is TLS_AES_128_GCM_SHA256 Protocol: TLSv1.3 Server public key is 2048 bit This TLS version forbids renegotiation. Compression: NONE Expansion: NONE No ALPN negotiated Early data was not sent Verify return code: 21 (unable to verify the first certificate) ... Verification errorが発生してますね。 このような結果から 次のような教訓を得ることができました。 第一に、サーバー証明書の検証は使用するツールによって結果が異なる可能性があるため、複数のツールで確認することが重要。 第二に、curlコマンドだけでの検証は不十分で、より厳密な検証ツール(opensslなど)を併用する必要があります。 最後に 使用している技術への理解を深め、起こりうるミスのパターンを網羅できれば完璧なのですが、中々そうはいきませんよね。 エンジニアとしての活動がまだまだ短い私ではありますが、これから沢山経験を積んで成長していければと思っています。 明日は、swimland0306 さんの「ニフティにSREとして入社して2年間でやってきたこと」です。 お楽しみに!
アバター
前編の記事ではニフティのサーバーストレージチームの業務について色々伺いました。 後編では素顔に迫ります! 自己紹介 T.Yさん ■入社年度 2018年01月 ■主担当サービス 仮想化基盤/仮想デスクトップ環境の設計・構築・運用/クラウドサービスの契約管理・コスト管理 ■趣味 休日は朝Netflixを観て午後に出かけるのが日課です。 D.Nさん ■入社年度 2022年12月 ■主担当サービス 仮想デスクトップ環境の構築・運用/クラウドアカウントや証明書発行の運用/全社クラウドコストの管理 ■趣味 ランニングと休日のはしご酒です。2025年は大阪マラソン走ります。 S.Sさん ■入社年度 2019年4月 ■主担当サービス クラウドサービス関連の運用 ■趣味 辻村深月が好きです。 過去に頑張ったエピソードを教えてください! T.Yさん 仮想化基盤のリプレイスですね。旧基盤がどう動いているかの情報もなく、全く分からない状態でした。そこから要件定義から構築まで一通りやったのでとても大変でしたね。業務を止めてはいけないのに、冗長構成になっていない環境もあったりしましたがトラブルなくやり切りました。 D.Nさん まったく問題が起きなかったですもんね。すごい。 S.Sさん 仮想化基盤は当時の構築者がいなくなってしまっていて、ナレッジも残っておらず本当に大変そうでした。トラブルゼロはすごいですね。 T.Yさん しっかりと仮説を立てて取り組みました。既存を踏襲せず、ゼロベースで要件定義からやったのが成功の鍵になったと思います。 D.Nさん サポートセンターの方が業務で利用する仮想化基盤を使っているのですが、リプレイスを実施したことですね。仮想化基盤上で動くツールが移行後もちゃんと動くようにするために走り回りました。ニフティは歴史が長い会社なので、もう社内に分かる人がいないシステムもあったりして大変でしたがどうにか無事に終えることができました。 T.Yさん 仮想化基盤も従来の構成から新しい構成に変えることのメリットの説明などもやりましたね。 クラウドチームはどうですか? S.Sさん 私はAzureの導入ですね。完全に0から始めて、Azureが何かも分からない状況だったところをニフティで使えるようにしたところです。実際に触ったり、Microsoftの方に聞いてたり、資格(Microsoft Fundamentals)をとってみたりして学習からはじめました。 T.Yさん もともとクラウドのナレッジがあるチームではなかったので、かなりハードルは高かったと思いますがやりきったのは凄いなと思います。誇らしいです! S.Sさん ふりかえってみれば、イチから始める機会をいただけてよかったなと思います。 D.Nさん ベンダーさんとの調整もカウンターでやってくださってたので凄いなと思います。 休日はどう過ごしていますか? D.Nさん ランニングがシュミなので10㎞走っています。フルマラソンにも出ています。お酒が好きなので家や外で飲んだりします。 T.Yさん 会社のブカツのマラソン部(アクティ部)に入っていますよね。 D.Nさん はい、皇居ランを2週に一回くらいやっています。 T.Yさん 平日の夜はD.Nさんや、他のメンバーを誘ってNIF BAR(定時後オープンする社員用の無料バー)にいったりします。そうじゃない日は、僕はお酒が大好きなので場末の飲み屋に一人で突撃したりしますね!休日は映画を観に行ったり美術館にいったりします。 S.Sさん 映画や美術館に出かけられるんですね!T.Yさんはとにかくお酒好きなイメージが強くて意外でした。笑 最近別のチームのマネージャーからT.Yさんは飲むとすごいよ!て聞きました。笑 T.Yさん そんなことないですよ!笑 D.Nさんはそのマネージャーや、中途入社の方と飲みに行ったりされてますもんね。 D.Nさん 新しく入社された方がいたら飲みにいったりしますね。中途入社で社歴が浅い方同士のネットワーキングの場があります。 S.Sさん 皆さん活発ですね!私は休日は家にいることが多いですね。 D.Nさん S.Sさんは本が好きなんですよね。 S.Sさん はい、辻村深月さんの著作が好きです。 T.Yさん ボードゲームも好きなんじゃなかったっけ?西新宿にボドゲカフェあったと思いますよ。あと、会社のブカツにボードゲーム部もあるみたいです。 S.Sさん 今度行ってみます! いま楽しみなことは何ですか? D.Nさん 旅行とフルマラソンを兼ねることが多いですが、来年は大阪マラソンにいって大阪グルメを満喫したいと思います。 T.Yさん 音楽グループをやっている友人のライブの単独公演にいくのが楽しみです。 S.Sさん 半年後に海外旅行に行こうと思っています。20代最後の思い出作りにいってきます! T.Yさん 海外旅行いいなあ。私も行きたいんですけどね… D.Nさん クラウドもそうですけど、円安でなんでも高いですよね…。でも、海外旅行はいけるときに行っておきたいですよね。お土産待ってます! 前編もご覧ください! 今回はニフティのサーバーストレージチームのインタビューの様子をお届けしました。 あわせて前編もご覧ください。 【インタビュー】ニフティのサーバーストレージチームに業務について色々聞いてみた! ニフティでは、さまざまなプロダクトへ挑戦するエンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトよりお気軽にご連絡ください! このインタビューに関する求人情報 /ブログ記事 サーバーストレージチームの求人情報 .is-style-rounded + .has-text-color{ font-size:95%; }
アバター
自己紹介 T.Yさん ■入社年度 2018年01月 ■主担当サービス 仮想化基盤/仮想デスクトップ環境の設計・構築・運用/クラウドサービスの契約管理・コスト管理 ■趣味 休日は朝Netflixを観て午後に出かけるのが日課です。 D.Nさん ■入社年度 2022年12月 ■主担当サービス 仮想デスクトップ環境の構築・運用/クラウドアカウントや証明書発行の運用/全社クラウドコストの管理 ■趣味 ランニングと休日のはしご酒です。2025年は大阪マラソン走ります。 S.Sさん ■入社年度 2019年4月 ■主担当サービス クラウドサービス関連の運用 ■趣味 辻村深月が好きです。 担当業務について 担当業務について教えてください。 T.Yさん 社内システムのインフラを仮想化している環境(以下、仮想化基盤)と、クラウドの管理を実施しており、それぞれについてチームを分けています。 仮想化基盤チーム 保守 ベンダー調整 機器の調達 仮想化基盤上で動いているシステム クラウドチーム マルチクラウド化推進 コスト管理 システムの開発 クラウドチームでコスト管理のためのシステム開発を行っているそうですが、使用している技術を教えてください。 S.Sさん AWSでPythonを使っています。コストはSlackやスプレッドシートに連携し、各クラウド担当者に入力してもらってデータを収集しています。 集めたデータはどう活用しているんですか? S.Sさん 先々の予測を行い、改善を図っています。過剰な運用を行っている担当者に直接働きかけも行っています。 T.Yさん 各担当者にコスト意識を根付かせたいという目的があります。予測と実績の乖離をなくしていきたいですね。 コスト削減の効果はどれくらいでしたか? D.Nさん 2年間で25%削減できています。 T.Yさん 着手してすぐに予実の乖離が億単位で起きていることが分かり、会社としての課題として挙がりました。コスト管理を本格的に始めたところ、キャッシュアウトが抑制できたうえ、担当者のコストへの意識も高まりました。 円安の影響はありましたか? T.Yさん 円安の影響は多少ありますが、AWSは長期で利用すると割安で使えるプランもあってそれでもなお削減はできています。 D.Nさん 為替予約なども活用しています。経理と連携しながら円安と戦っています! コストダウンをしてもらうにあたって、各チームに施策をしてもらうために苦労していることや工夫した点はありますか? S.Sさん まずマネージャーにコスト意識を持ってもらい、マネージャーから社員に働きかけてもらうようにしました。 予実5% or 20,000 乖離が出たらSlackでアラートが鳴るようにしています。 ツンデレちゃんが怒ってくれます。 T.Yさん コスト管理を行うまでに、そもそもコスト意識がなかったと思いますが金額の乖離の報告や予測を毎月あらかじめ登録してもらうことで自然と意識が高まるようにしました。情報が可視化されたというところが大きいですね。 業務をやっていてやりがいを感じたり、嬉しかったことがあれば教えてください。 T.Yさん コスト管理の業務でいうと、結果が反映されてきて会社に貢献できているところですね。チームメンバー全員感じていることだと思います! D.Nさん コストのグラフが下がっていると嬉しいですね。 S.Sさん クラウドの管理を通じて、AWS、Azureなどクラウドの基盤側の深い部分を触って詳しくなれるのが嬉しいですね。 MultiAccountの仕組みなどは普段AWSを使っていると見えない部分なので、そのあたりが知られて良かったです。 クラウドの知識が必要そうですが、どう工夫しているんですか? T.Yさん カテゴリ別に体制を分けています。仮想化基盤、クラウドチームで分けていて、チーム間で情報共有や、システムの仕様に関する理解の向上目的で実機を動かしたりと業務共有を進めていて、いずれは人材交流できたらいいなと思っています。 メンバーごとに理解度が異なるので、それを可視化して理解度に応じたカリキュラムを作りたいですね。 D.Nさん 私はどちらのチームにも属しています。 仮想化基盤のことは、入社してからT.Yさんに99%くらいは教えていただきました! T.Yさん 仮想化基盤の経験がない方でもサポートします。 D.Nさん 個人的にいいなと思ったのが、検証環境を設けていただいて色々実験できるのが嬉しいです。 仮想化基盤チームで伸ばせるスキルは? T.Yさん クラウドがどういう仕組みで動いているかが体系的に分かります。クラウドを使っていても、その仕組みまで理解している人はあまりいないのではないでしょうか。 チームについて チームの構成を教えてください。 T.Yさん サーバーストレージチームのサブチームリーダーは私ですが、6年目のS.Sさんが開発チームのリーダーとして立ち回ってくれています。 S.Sさん メンバーは中途1年目の方と派遣社員の方と社歴が浅いので、ドメイン知識の共有や業務のサポートをしています。 インフラエンジニアの役割は会社によって違いがありますよね。中途採用の場合、応募者のスキルセットと求める人材像にギャップを感じることはありますか? T.Yさん 全く違ったというケースはないですね。ただ、サーバーストレージチームという名前と実際の業務との乖離があるかもしれないですね。笑 D.Nさん そうですね。私は「コスト管理までやるんだ!」という驚きがありました。 現在のチームにはどういった課題がありますか?それに対してどうやって取り組んでいますか? T.Yさん プロダクトと業務が多すぎて、課題にスピード感もって取り組めていないことですね。仲間が増えたらあんなことやこんなこともやりたいな…と思っています。 D.Nさん Azureの利用も始まっていて、AzureもAWSと同じようにコスト管理していく必要があったり、課題はまだありますね。 T.Yさん 業務に注力できるように、プロダクトの優先順位付けや、体制を分けて、やる業務を限定することでタスクの消費速度を上げています!一緒にがんばってくれる仲間を募集しています。 後編に続きます! 今回はニフティのサーバーストレージチームのインタビューの様子をお届けしました。続きは後編の記事をご覧ください。 【インタビュー】ニフティのサーバーストレージチームってどんな人がいるの?本人たちに聞いてみた!【後編】 このインタビューに関する求人情報 /ブログ記事 サーバーストレージチームの求人情報 .is-style-rounded + .has-text-color{ font-size:95%; }
アバター
この記事は、 ニフティグループ Advent Calendar 2024  18日目の記事です 1. はじめに こんにちは。ニフティ新卒1年目のSREチームに所属している滝川です。 AWS環境でTerraformを使ったCI/CDパイプラインを構築する際、認証情報の管理は避けて通れない課題です。これまで、”AWS Access Key方式”を使用する方法が一般的でしたが、セキュリティや運用の面から、最近ではIAMロールを活用する方法が注目されています。 この記事をご覧の方は、アクセスキー方式ではなくIAMロールを用いて認証を行いたい方、もしくは以下のようなエラーの解消を試みている方々だと思います。 Error: Credentials could not be loaded, please check your action inputs: Could not load credentials from any providers このエラーはGitHub ActionsがAWSリソースにアクセスするための適切な認証情報を見つけられない状況を示しています。 上記の課題はIAMロールを用いた認証方法の実装することで解決すると思います。手順を詳細に説明しているのでぜひ最後までお読みください。 2. 従来の認証方法: AWS Access Keyの使用 1. 仕組み AWS Access Keyは、AWSアカウントのアクセス許可を設定するためのキーです。 Access Key ID と Secret Access Key をGitHub Secretsに登録し、GitHub Actionsで環境変数として使用します。 以下は典型的なGitHub Actionsの設定例です。 - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v2 with: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-region: ap-northeast-1 GitHub Secretsには以下を保存します。 AWS_ACCESS_KEY_ID : IAMユーザーで作成したAccess Key ID AWS_SECRET_ACCESS_KEY :IAMユーザーで作成したSecrets Access Key 2. 課題 セキュリティリスク 長期的に有効なクレデンシャルであるため、流出した場合のリスクが大きい。 一度流出すると、取り消すまで悪用される可能性がある。 権限の制御が難しい IAMユーザー単位での権限付与になるため、CI/CD専用の細かな権限設定が難しい。 必要以上の権限が付与されやすく、最小権限の原則に反する可能性がある。 3. 新しい認証方法: IAMロールとGitHub Secrets 1. 仕組み AWSのIAMロールを使えば、長期的なアクセスキーを使わずに安全に認証を行えます。GitHub ActionsがAWSに一時的な権限をリクエストすることで、最小限の権限で操作を行います。 以下のように設定します。 - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v2 with: role-to-assume: ${{ secrets.AWS_ROLE_ARN }} aws-region: ap-northeast-1 GitHub Secretsには以下を保存します。 AWS_ROLE_ARN : 作成したIAMロールのARN 3.2 メリット セキュリティ向上 GitHub Actionsでのみ有効な一時的な権限を使用するため、クレデンシャル流出のリスクが低減。 一時的な認証情報は短期間で失効するため、万が一流出しても影響を最小限に抑えられる。 最小権限の適用が容易 IAMロールに対して必要最小限の権限を設定可能。 Assume Roleを使用することで、特定のリソースやサービスに対する権限を細かく制御できる。 CI/CD処理に必要な操作のみを許可するIAMポリシーを柔軟に設定可能。 4. 比較: AWS Access Key と IAMロール 項目 AWS Access Key IAMロール 運用負荷 高い 低い 追跡 難しい 容易 最小権限の適用 難しい 容易 5. 実装手順 Step 1: IAM OIDC IDプロバイダを追加 OpenID Connectを選択 プロバイダのURLに https://token.actions.githubusercontent.com 対象者に sts.amazonaws.com プロバイダを追加をクリック Step 2: IAMロールを作成 AWS CLIまたはAWSコンソールでIAMロールを作成します。以下の設定を行います。 IAMロールの信頼関係を設定 ロールを信頼するエンティティとしてGitHub Actionsを指定します。 { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Federated": "arn:aws:iam::xxxxx:oidc-provider/token.actions.githubusercontent.com" }, "Action": "sts:AssumeRoleWithWebIdentity", "Condition": { "StringEquals": { "token.actions.githubusercontent.com:aud": "sts.amazonaws.com" }, "StringLike": { "token.actions.githubusercontent.com:sub": [ "repo:xxxxx/*" ] } } } ] } ポリシーのアタッチ Terraformに必要な権限(例: S3, EC2, IAM)のみを付与します。 Step 3: GitHub Secretsに登録 GitHubのsetting→Actions secrets and variables→actionsからsercretsに以下の値を登録 AWS_ROLE_ARN : Step2で作成したIAMロールのARN Step 4: GitHub Actionsの設定 .github/workflows/terraform.yml (任意のファイル)で環境変数を定義する。 下記はterraformのCIの実装例 name: Terraform CI on: push: branches: - main jobs: terraform: runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v3 - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v2 with: role-to-assume: ${{ secrets.AWS_ROLE_ARN }} - name: Setup Terraform uses: hashicorp/setup-terraform@v2 with: terraform_version: 1.9.8 - name: Terraform Init run: terraform init - name: Terraform Plan run: terraform plan 6. まとめ 従来のAWS Access Keyを用いた認証方法には、セキュリティと運用の課題がありましたが、IAMロールを活用することで、認証情報を容易でセキュアに設定することができます! ぜひ、この方法を試してみてください!
アバター
この記事は、 ニフティグループ Advent Calendar 2024  19日目の記事です。 はじめに おはようございます。IWSです。 少しいきなりにはなるのですが、みなさんはずんだもんは好きですか?好きですよね? 前々からゲーム実況の動画などで活躍していたり、「Cevio AI Song」 や 「NEUTRINO」 なども出て歌うずんだもんがいたりと見かける機会が多くなったのではないかと思います。 私も 「VOICEPEAK 東北きりたん」 を購入して一緒についてきた ずんだもん を喋らせて遊んだりしました。 ですが、喋らせて終わりでは少し寂しいし、私もずんだもんでなにかしたいなぁ〜と思ったのでちょっとやってみました。 やりたいこと ニフティには「もじこえ」 というテキストを投稿すると音声に変換してコミュニケーションが取れる社内Webアプリがあります。(もじこえについてはぜひ こちらのブログ をご覧ください) 音声は Amazon Polly を使って生成しているのですが、ここにみんなが大好きな ずんだもん を追加しようというのが目標です。 イメージとしてはこんな感じでしょうか もじこえが Node.js で作られているのでこちらもそれに合わせます。 クライアント側からテキストを送信してもらいサーバー側へ渡す。サーバー側はそれをVOICEVOXのコンテナへさらにリクエストを飛ばして音声データを生成してもらうという形です。 VOICEVOX 喋るずんだもんには「Cevio AI」や「VOICEPEAK」などありますが今回はAPIで利用したいというのもあるので「VOICEVOX」を使用します。 https://voicevox.hiroshiba.jp/ core , engine , editor の3種類がありますがAPIとして呼び出して音声合成ができればいいので VOICEVOX_engine を選んでいます。 https://github.com/VOICEVOX/voicevox/blob/main/docs/全体構成.md Docker Hubでイメージが公開されているのがありがたいですね https://hub.docker.com/r/voicevox/voicevox_engine docker-compose.yml にこれを追加するだけでコンテナを立ち上げられます voicevox: image: voicevox/voicevox_engine:0.20.0 container_name: voicevox ports: - '50021:50021' ずんだもんボイスを生成しよう VOICEVOXのAPIにリクエストを投げることで音声を生成することができます。 APIには「テキストを音声クエリに変換するAPI」と「音声クエリを音声データに変換するAPI」の2種類があり、2つのAPIをそれぞれ呼ぶことでテキストから音声データの生成までを行います https://github.com/VOICEVOX/voicevox_engine?tab=readme-ov-file#http-リクエストで音声合成するサンプルコード // テキストから音声クエリを作成 const query = axios.create({ baseURL: `http://voicevox:50021`, responseType: 'json', }); const queryResponse = await query .post(`/audio_query?text=${text}&speaker=3`) .catch((error) => { console.error(error); }); if (!queryResponse) { return ''; } // 音声クエリから音声を合成 const synthesis = axios.create({ baseURL: `http://voicevox:50021`, headers: { 'Content-Type': 'application/json', }, responseType: 'arraybuffer', }); const jsonString = JSON.stringify(queryResponse.data); const synthesisResponse = await synthesis .post(`/synthesis?speaker=3`, jsonString) .catch((error) => { console.error(error); }); if (!synthesisResponse) { console.log('音声の合成に失敗しました'); return ''; } APIを呼ぶ際にクエリパラメーターで speaker={id} とすることで音声ライブラリの好きなキャラクターに喋らせることができます。3 でずんだもん、8 で 春日部つむぎ に喋ってもらうことができ、一部キャラは喋り方を変えたりもできます。(セクシーずんだもんにしたりささやきずんだもんにしたり……) 音声ライブラリの一覧はコンテナを立ち上げて /speakers にアクセスすると見ることができます。 音声合成ができたらファイルに保存しておきます。 // 一意のトークンを生成 const token = uuidv4(); // Pathの指定と音声ファイルの保存 const audioPath = path.join( process.cwd(), 'public', 'audio', `${token}.mp3` ); fs.writeFileSync( audioPath, new Uint8Array(synthesisResponse.data as Buffer) ); const speakUrl = `/audio/${token}.mp3`; return speakUrl; トークンをつかって適当な名前で保存しているだけです。ファイルの保存ができたらそのファイルへのPathを返しています。 Pathを返しているのはクライアント側で /audio/${token}.mp3 を再生するという実装をしているからです。返ってきた speakUrl のURLを使用して const music = new Audio(speakUrl); music.play(); のようにしてあげればブラウザ側で音声が再生ができます。 コード全体 // VOICEVOX export const voicevoxTextToSpeakUrl = async (text: string): Promise<string> => { /** * テキストをVOICEVOXで音声に変換し音声ファイルのURLを返します。 * * @param text 音声に変換するテキスト * @returns 音声ファイルのURL、失敗した場合は空文字列 */ try { // 音声クエリを作成 const query = axios.create({ baseURL: `http://voicevox:50021`, responseType: 'json', }); const queryResponse = await query .post(`/audio_query?text=${text}&speaker=3`) .catch((error) => { console.error(error); }); if (!queryResponse) { return ''; } // クエリから音声合成 const synthesis = axios.create({ baseURL: `http://voicevox:50021`, headers: { 'Content-Type': 'application/json', }, responseType: 'arraybuffer', }); const jsonString = JSON.stringify(queryResponse.data); const synthesisResponse = await synthesis .post(`/synthesis?speaker=3`, jsonString) .catch((error) => { console.error(error); }); if (!synthesisResponse) { console.log('音声の合成に失敗しました'); return ''; } // 一意のトークンを生成 const token = uuidv4(); // Pathの指定と音声ファイルの保存 const audioPath = path.join( process.cwd(), 'public', 'audio', `${token}.mp3` ); fs.writeFileSync( audioPath, new Uint8Array(synthesisResponse.data as Buffer) ); const speakUrl = `/audio/${token}.mp3`; // 一度再生したら音声ファイルはもう使わないため削除 setTimeout(() => { fs.unlink(audioPath, (err) => { if (err) { console.error(`ファイル削除エラー: ${audioPath}`, err); } else { console.log(`ファイルが削除されました: ${audioPath}`); } }); }, 20000); return speakUrl; } catch (e) { console.error('error: ', e); return ''; } }; まとめ VOICEVOXをつかってずんだもんを触ってみました。APIが提供されているおかげでもじこえに簡単にずんだもんを組み込むことができましたね。これでずんだもんライフが充実します! ぜひ皆さんもVOICEVOXを使ってなにかやってみてください! 明日は @takatakanian さんの記事です!お楽しみに! クレジット VOICEVOX:ずんだもん
アバター
エンジニアリングマネージャーをしています芦川です。2024年12月16日に GitHub Universe 2024 Recap in ZOZO に参加、登壇させていただきましたので、そのご報告いたします。 イベント内容としては、冒頭、GitHub 服部様による GitHub Universe 2024 のRecap(めちゃくちゃ丁寧な説明、copilot初心者の私でも分かる内容!)、続いて株式会社ZOZO 山田様によるiOS開発におけるCopilot For XcodeとCode Completion(3番勝負が非常に面白かったです!copilotの勝ち!)、同社佐藤様によるGitHub Copilot のテクニック集(すぐにでも使えるショートカットやプロンプトの書き方満載でした!)というところで、短時間ながらも非常に濃密な時間でした。 私の方からは、GitHubの活用事例として、インナーソースの取り組みをメインに話させていただきました。Ask the speakerの時間では、「偶然、今日、社内でインナーソース部を立ち上げてインナーソースを始めたところです!」という方もいまして、神の力か何かで引き合わせてくれたのかな、という、すごい引力を持った場であり、有意義な時間を過ごすことができました。ありがとうございます。 スライド全体はこちらになります。 どんな会社?エンジニア組織の構造?GitHub導入状況? ここからは、スライドをピックアップしていき、話した内容を文章にしていこうと思います。 コラボレーション文化の話をするので、まず会社の組織構造についてお話しました。実際の職制とは異なりますが、チームトポロジーのチームタイプにわけるとざっくりこのようなエンジニア組織の構造になります。エンジニアは全部で約160名いまして、7-10名ほどのサブチームにわかれておりまして、内製開発がメインです。 注目するところとしては、ストリームアラインドがドメインの違いでざっくり2分割されている点だったり、あとは、コンプリケイテッドサブシステムでは、ISP独自のネットワーク部分を扱っているようなところでしょうか? 次にGitHub利用状況です。organizationは1つであり、Repository creationルールは、internalとしていて、組織内の誰でもリポジトリ閲覧可能をデフォルトにしています。一方、Copilot Metrics Viewerからみたcopilotの利用状況です。こちらの導入は全員ではまだありませんが、コード受け入れ率は31%であり、割といい感じなのかな、と思っております。言語はpython、エディタはvscodeが多いですね。 ここからインナーソースの話 ここからインナーソースの話です。 2022年の下期だったとおもいますが、私含めた周辺のマネージャーの中である悩みがありました。事業部からはもっとこうしたいという要望はたくさんでるのですが、開発リソースも限られている。外部発注もありますが、でも、キャッシュアウトせずにもっと内部でうまく回せるやり方や特定のチームへ開発依頼が集中してしまうような状況を打開するものはないかと。で、いろいろググったのですが、ここは衝撃的な出会いでした。この世の中には「インナーソース」というやり方があることを知りました。 最初に私が読んだのは、右のcodezineに乗っているという東芝の小林さんが書いた記事「 オープンソースの開発スタイルを企業内で実践するインナーソースとは? メリットとポイントを理解する 」です。ものすごくわかりやすかったです。で、これ導入したら余力のある人がほかのチームの開発手伝えるようになるのでは?と安易に思いまして、組織に「インナーソース」とやらを導入したいと強く思ったわけです。 でいろいろとやり始めました。そのあたりのくだりは、以下の記事のほうが詳しいですので割愛しますが、なかなかうまくやれてきたのでは?と思っています。 インナーソースを導入してみた その① お試し導入編 インナーソースを導入してみた その② 土台作り編 また、今回の発表では、組織にこういう新しい流れをいれるときにエンジニアリングマネージャーとしてどのように動いたかについて新しく触れました。生々しい話にはなりますが、私としては、以下のようなことをしてきました。 トップダウンで進んでいるようには見られたくなくて、1人現場メンバーの推進者を決め、そのメンバーから全社へ発信するように仕向けたこと 周りのマネージャーを巻き込み、事業計画としてインナーソース推進活動を公式化したこと インナーソースガイドラインでは、コントリビューターの評価や工数の考え方を含めたこと 業務の一環としてインナーソースコミュニティ運営に携われるようにしたこと このあたりもうまくいったのかなあ、と考えています。もう少し、細かい話が書けそうなので、いつか「新しい取り組みをボトムアップで組織に導入するには?」みたいに抽象化したお題で書いてみたいと思います。 インナーソースがうまくいっているところ 個人で作ったツールへのコントリビュートや、業務に少し踏み込んだところだと、API利用者定義ファイルの追加について明らかにリードタイムを短縮できた事例などを紹介しました。このあたりの話は、小松の下記の登壇内容でもありますので、ここでは割愛いたします。 インナーソースがうまくいっていないところ このブログでは、これまであまり語っていなかった、いまのところうまくいってないところを文章化しておきます。やってみていくと、ハマらないパターンもあることが体感としてわかってきました。例えば、ドメイン知識が違う同士のストリームアラインドチームのコラボレーションです。 ドメインが同じチーム同士であれば、ビジネスの方向が同じチームどうしてあれば、どちらのチームが提供しているAPIをお互い利用しながら、そこにコントリビュートが生まれる余地があるかと思うのですが、例えば、弊社の例でいうと、WEBサービスと、ISPサービスはかなり属性が違うというか、土俵が違うというか、ドメインがかなり異なるチームであり、そこにはコントリビュートするためのモチベーションがうまれないので、厳しいかな、と正直に思っています。(もちろん個人のスキルアップやチャレンジ精神というところは大歓迎ですが、ベースとして、という意味です。) 考えてみれば、コントリビュートは己や己のチームのためにまずやってみよう、と思うはずなので、そもそも協働するチームでなければ、やっぱりうまれないよな、と思ったところです。 つづいてはドキュメントへのコントリビュートです。ソースコードにPRを出すよりは圧倒的にドキュメントにPRを出すほうがハードルは低いと思います。が、弊社では、すべてのドキュメントがリポジトリで管理されてはなく、どうここを進めていこうか、と現在悩んでいるところです。どなたか助けてください。。もしかして、生成AIできれいなテキスト化ができるといい感じになるのでしょうか? ここは用語が社内に先に広まった分、理解が実はあまり追いついていなかった?という想像です。新規プロダクト開発や、なにかの事情がありブラックボックス・運用保守コスト増になってしまったシステムの改善に関して、インナーソースは寄与しません!誤解なさらずように。 まとめ で、まとめになります。 組織内でコラボレーション文化がどう作られるか、というところがインナーソースにチャレンジして見えてきました。 まず、土台となるプラットフォームが必要です。つまりGitHubです。 次に、コラボレーションする方法の明確化が必要で、つまりインナーソースです。 プラットフォームやコラボレーション方法だけではコラボレーションは生まれません。最後に、人間が必要ですが、 大事な点として同じ方向を向いていること、つまりお互いがメリットを享受できる間柄 である必要があります。 以上、3点が組織の後ろ盾(評価制度、工数管理など会社の動きの中で公式的に動けるか、という点)を行うことで達成されるのではなかろうか、というのが現時点での結論になります。 まだ道半ばでの途中の状態ですが、また進捗あり次第アップデートしていきたいと思います! 以上です。
アバター
この記事は、 ニフティグループ Advent Calendar 2024 16日目の記事です。 はじめに 今月は二本目のブログ投稿をする宮本です。ひと月の間にブログ二本書くのはなかなかしんどいですね。一人アドベントカレンダーをやっている方とか凄いと思います。 今回は、ReactのErrorBoundaryのログ出力についてです。なお、React単体で実装できるクラスコンポーネントを利用するものではなく、関数コンポーネントで利用できる react-error-boundary を対象としています。 ログを簡単に出したい。あとついでに使いまわせるようにしたい。 平たく言えばReactコンポーネントのtry-catchのような役割をするErrorBoundaryですが、実施できることは結構限られます。 エラー発生時の代替コンポーネントの表示 代替コンポーネント内でのリセット処理の追加 エラー発生時にあらかじめ指定したログ出力用の関数を実行 他にも明示的にErrorBoundaryの呼び出し・再レンダリングができるhookが用意されていたりしますが、応用になるため今回は割愛。とりあえずErrorBoundaryを入れよう!となった場合に使うのは、代替コンポーネントの表示とエラー時のログ表示だと思います。 さて、代替コンポーネントの中身自体はアプリケーションによりまちまちだと思いますが、エラーログの表示となると表示内容は割と限られます。エラーの種類ごとにエラー内容を変えるとなると細かく分岐を入れる必要がありますが、「最低限エラーがわかれば……」というレベルならできるだけ楽に書きたいです。 ErrorBoundaryでログ出力をする例として、onErrorで実行する関数内でログ出力をする方法が公式で紹介されています。 // https://github.com/bvaughn/react-error-boundary?tab=readme-ov-file#logging-errors-with-onerror より一部修正 "use client"; import { ErrorInfo } from 'react'; import { ErrorBoundary } from "react-error-boundary"; // 変更点)infoの型はreact-error-boundary 4.0からReactのErrorInfo型を利用 const logError = (error: Error, info: ErrorInfo) => { // Do something with the error, e.g. log to an external API }; const ui = ( <ErrorBoundary FallbackComponent={ErrorFallback} onError={logError}> <ExampleApplication /> </ErrorBoundary> ); しかしこのonErrorで実行する関数、引数があらかじめ決まっているエラーオブジェクトとコンポーネントのスタックしか渡されません。一応スタックは渡されるのでどこで発生したエラーかはわかるようになっています。が、せめてErrorBoundaryを仕掛ける場所が決まっているのであれば、ログ中に一発で「エラー発生箇所はここだ!」とわかるような文言を仕込みたいです。 // こんな感じにErrorBoundaryがたくさんあると、いざエラーが発生した場合もどこで発生したエラーなのか分かりづらい const logError = (error: Error, info: { componentStack: string }) => { console.error("エラー発生!", Error, componentStack) }; const uiFirst = ( <ErrorBoundary FallbackComponent={ErrorFallback} onError={logError}> <ExampleApplicationFirst /> </ErrorBoundary> ); const uiSecond = ( <ErrorBoundary FallbackComponent={ErrorFallback} onError={logError}> <ExampleApplicationSecond /> </ErrorBoundary> ); そこで、次のようにログ出力用の関数を作成するcreateLogError関数を用意して、createLogErrorの引数で与えた値(logId)をログ出力で利用するようにします。 const createLogError = (logId: string) => (error: Error, info: ErrorInfo): void => { console.error(`${logId} エラー発生!`, error, info.componentStack); }; const uiFirst = ( <ErrorBoundary FallbackComponent={ErrorFallback} onError={createLogError("ui-first")}> <ExampleApplicationFirst /> </ErrorBoundary> ); const uiSecond = ( <ErrorBoundary FallbackComponent={ErrorFallback} onError={createLogError("ui-second")}> <ExampleApplicationSecond /> </ErrorBoundary> ); 上記の例を実際に使うと以下のようになります。 export default function Home() { return ( <ErrorBoundary FallbackComponent={FallbackComponent} onError={createLogError('ErrorItem')}> {/* 内部で `throw new Error('なんか凄いエラー');` を実行するコンポーネント */} <ErrorItem /> </ErrorBoundary> ); } 上記の例ではコンポーネント名をそのままIDに入れていますが、機能に即した名前が入っているともっとわかりやすくなると思います。ゆるくログ出力するだけであれば、これで十二分ですね。 おわりに 今回はErrorBoundaryを利用したログ出力について紹介しました。Reactのコンポーネントで発生したエラーは、止める場所がなければどこまでも遡り最終的にページ全体が落ちてしまいます。最低限エラーが発生しそうな箇所ではErrorBoundaryを設定しておき、余裕があればログ出力できるようにしておくと調査がスムーズですね。 また、そもそもエラーが発生したコンポーネントを再レンダリングするトリガーをFallbackComponent内に用意することもできるので、エラー処理といえどもなかなか奥が深いです。 明日は、kiwi26さんの記事です。 お楽しみに! 参考 react-error-boundary – npm
アバター
この記事は、 ニフティグループ Advent Calendar 2024 17日目の記事です。 はじめに こんにちは!新卒1年目の佐藤です。 現在ジョブローテの2期目で、基幹システムグループにて代理店様向けの申込ツールの開発・運用を主に行っています。 私が担当している開発の1つに ドキュメントのMarkdown化 があります。 この開発ではStarlightというフレームワークを使用したので、これについて紹介したいと思います! 背景 チームの設計書管理について 私が現在所属しているチームでは前までExcelだけでツールの設計書を管理していました。 その後、Notionに移行しつつあるのですが、一部はExcelに残ったままになっています。 ツールの改善案件が来た際に設計書修正をするのですが、Excel・Notionともにいつ、誰が、どこを修正したのかが非常に分かりにくくなっていました。 文 字 の 色 を人によって変えたりしたのですが、確認洩れを完全に回避することはできません。 そこで、設計書もGitHubで管理しようという話になりました。開発と同じようにブランチを切り、修正を加えて、プルリクエストを投げることで、修正箇所を簡単に把握することができます。また、履歴管理を気にする必要もありません。 サイトのホスティングについて だったらGitHub Pagesがあるじゃん!となりますが、今回選択肢には入りませんでした。 なぜなら、設計書は「社内限定公開にして、開発以外の方も見れるようにする」という要件があったからです。 GitHub pagesは公開範囲を絞れるのですが、その中で GitHubアカウントを持つ人に限り 社内限定公開ができます。しかし開発以外の方は GitHubアカウントを持っていない ので閲覧することができません。 サイト1つを見るためだけに有償のアカウントを発行するのは得策とは言えません。 となると、第2の策としてAmazon S3を使った静的webサイトホスティングが挙がりました。AWSの他のサービスと組み合わせることで適切な公開範囲を設定することができます。 どうやってサイトを作っていくか Webフレームワークで有名どころだとNext.jsやDocusaurus, Nuxt.jsがありますが、私はAstroを選びました。 https://astro.build/ 理由1:高速なWebサイトが作れること ドキュメント量が非常に多いので、できれば軽いサイトにしたいところです。Astro公式からの 2023 Web Framework Performance Report によると、Astroは他のフレームワークに比べてパフォーマンスが良いことが分かります。 理由2:Markdownで記述できること AstroはデフォルトでMarkdownに対応しています。ドキュメントページなので、見た目にあまりこだわる必要がなく、技術的な壁が高くないので誰でもすぐに書けるという点で優れています。私のような新人でも少し調べれば書き方を理解することができます。 以上の理由でAstroを採用しました。Astroでドキュメントページを作るにはStarlightというフレームワークを使用します。 Starlightとは https://starlight.astro.build/ https://starlight.astro.build/ https://starlight.astro.build/ StarlightとはAstro製のドキュメントサイトに特化した静的サイトの生成フレームワークになります。 公式ドキュメント は日本語訳されています。 なんと、Markdownを書くことで簡単に1ページが作れちゃいます。 また、.md形式のファイルだけでなく、.mdx形式のファイルにも対応しています。 となると、StarlightはAstroを基盤としているため、MDXファイルで サポートされているUIフレームワーク (React、Svelte、Vueなど)で構築されたコンポーネントが使えます!!! Starlight自体にもコンポーネントが付属されており、他のUIフレームワークを使わなくとも十分なサイトが作れます。 他にもディレクトリ構造が自動でページ構造になってくれたり、サイト内検索や画面のダークモード・ライトモード切替がデフォルトで付いています。 やってみよう! 新しいプロジェクトを作成するには次のコマンドを叩きます。 事前にNode.jsとnpmのインストールをしておいてください。 npm create astro@latest -- --template starlight プロジェクト名設定と依存関係をインストールするか否かなどが聞かれるので答えます。 すると、数分で新しいプロジェクトが出来上がります。 cdコマンドで作成したディレクトに移動し、次のコマンドを叩きます。 npm run dev するとlocalhostで開発環境が立ち上がります。 Example Guide → のボタンを押すとGuidesとReferenceの2つが出てきます。 Guides直下にページを追加したい場合は、guidesフォルダ内に新しく.mdファイルを作成し、他ファイル同様にtitleを設定します。 あとは astro.config.mjs にページの追加設定をすることで、ページ追加ができます。 Reference直下に新しいページを作りたい場合は、referenceフォルダ内に新しいファイルをつくるところまでは同じです。 astro.config.mjs を見ると、このフォルダは autogenerate を有効としているため、ここで設定をしなくともフォルダ内のファイルを自動で読み取ってページ追加してくれます。 GuidesとExample Guideみたいに親子関係を作りたい場合は、新しくフォルダとその中にファイルを作り、 astro.config.mjs にも追加してあげると出来上がります。 他にも サイドバーのタイトルをページタイトルとは別の名前にする ページごとに数値を付けて、小さい順にページを並べる(デフォルトだとABC順) バッジをつけて強調表示にする ページ内目次を設定して、h2の見出しのみ目次表示する ソーシャルメディアアカウントへのリンクを追加する など色々と設定ができます。 ぜひ公式ドキュメントを読んでカスタマイズしてみてください。 最後にビルドするには、 npm run build でdistディレクトリを生成してくれます。 あとはこのディレクトリを例えば、S3に置き、静的webサイトホスティングの設定をすることで公開できます! おわりに 今回はStarlightを使って簡単なドキュメントページを作ってみました。ポートフォリオや趣味紹介などのページ作りでぜひ検討してみてください! 明日は Kazuhiro-27 さんの記事です。 お楽しみに! ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する
アバター
この記事は、 ニフティグループ Advent Calendar 2024  16日目の記事です。 はじめに こんにちは。ニフティ新卒1年目の藤岡です。 上京して少し経ち、満員電車にも慣れてきた今日この頃です。 今回は、コンテナ化されたアプリケーションを開発する際に発生した「VScodeでRancherDesktopを用いて開発コンテナが起動できない」問題について取り上げ、その原因を調査し、解決に至るまでの手順を共有します。 似たような問題に直面した方の参考になれば幸いです。 DockerDesktopからRancherDesktopへ移行 私たちのチームでは、以前からDockerDesktopを使ってコンテナの管理を行ってきました。 しかし、DockerDesktopの利用料が値上がりしたため、先日RancherDesktopに移行しました。 RancherDesktopは、DockerDesktop同様にコンテナの管理を簡単にするためのツールです。 DockerDesktopとの違いについて詳しく知りたい方は、以下の記事が参考になります。 Rancher Desktopの使い方|Docker Desktopとの違いや移行手順を解説 – カゴヤのサーバー研究室 開発コンテナが開けない問題 移行自体はスムーズでしたが、従来通りVScodeでコンテナを起動しようとしたところ次のようなエラーが発生しました。 [2024-12-06T08:03:03.507Z] Error response from daemon: failed to create task for container: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: error during container init: error mounting "/run/docker/containerd/daemon/io.containerd.runtime.v2.task/moby/32b5120f8d4e4e470a4e14b2d729750846f48652b9c01073e6fed43a8bc573a2/\\wsl.localhost\Ubuntu\mnt\wslg\runtime-dir\wayland-0" to rootfs at "/tmp/vscode-wayland-8287cccf-e6ca-4539-a913-14586d6ff8ee.sock": create mount destination for /tmp/vscode-wayland-8287cccf-e6ca-4539-a913-14586d6ff8ee.sock mount: bind mount source stat: stat /run/docker/containerd/daemon/io.containerd.runtime.v2.task/moby/32b5120f8d4e4e470a4e14b2d729750846f48652b9c01073e6fed43a8bc573a2/\wsl.localhostUbuntumntwslgruntime-dirwayland-0: no such file or directory: unknown エラー内容を読み解こうと試みましたが、見慣れない用語が多くなかなか理解できませんでした。 コンテナ系のエラーは「とりあえず万物再起動」ということで docker prune を実行するも改善せず。 AIに相談してみても、エラー内容は教えてくれましたが解決はできず。 このエラーメッセージは、Dockerコンテナの起動中に発生した問題を示しています。具体的には、コンテナの初期化中に必要なファイルやディレクトリが見つからないため、マウントに失敗しています。 以下の手順で問題を解決できるか試してみてください: 1.WSLの再起動: 2.Dockerデーモンの再起動: 3.必要なディレクトリの存在確認: 4.DockerとWSLの更新: WaylandとVScodeの関係 先輩に相談したところ、「エラー文中の wayland が原因では?」との指摘を受けました。 調べた結果、 wayland はディスプレイサーバプロトコルの一種で、GUIアプリケーションの画面描画を管理する役割を果たしていることが分かりました。 簡単に言うと、「GUIを表示するために必要な準備をする仕組み」です。 wayland についてはもっと分かりやすく説明してくれている記事がいくつもあるので調べてみてください。 ディスプレイサーバとは – わかりやすく解説 Weblio辞書 VScodeでは開発コンテナを起動する際、自動的に wayland のソケットをマウントします。 しかし、RancherDesktop環境ではそのマウントパスがサポートされていないため、エラーが発生していたようです。 解決方法 対処は簡単で、VScodeに「 wayland ソケットをマウントしないように設定」するだけです。 方法はCLIとGUIの二通りあります。 CLI Ctrl + Shift + P で「ユーザー設定(JSON)」を開き、次のコードを追記します。 "dev.containers.mountWaylandSocket": false GUI VScode左下の歯車マーク→設定→「wayland」で検索→「Mount Wayland Socket」のチェックを外します。 これでVScodeは自動的に wayland ソケットをマウントしなくなり、開発コンテナの起動が可能になります。 【参考】 VSCode Dev ContainerとRancher Desktopで作るコンテナ環境【WSL】| SIOS Tech.Lab RancherDesktopとDockerDesktopの違い 今回のエラーはRancherDesktop特有のもので、DockerDesktopでは発生していませんでした。 DockerDesktopは複雑な環境設定をうまく処理してくれる一方、RancherDesktopはその点で少々不便な部分があるようです。 とはいえ、RancherDesktopにはDockerDesktopにはないメリットもあり、今後のアップデートでさらに使いやすくなることを期待しています。 おわりに 今回は、VScodeでRancherDesktopを使って開発コンテナを起動する際に発生するエラーの原因と解決方法を紹介しました。複数のツールを組み合わせる開発環境では、仕様の違いによる問題が起こりがちです。 環境差による問題を防ぐためにコンテナ技術が広まっていますが、その周辺ツールによっては依然として問題が発生することを痛感しました。システムの動作原理を理解しながら開発を進める重要性を再認識する機会になりました。 少しでも同様の問題で悩む方の助けになれば幸いです。 明日は、[佐藤] さんの[Starlightを使った簡単なドキュメントページの作り方]です。 お楽しみに!
アバター
💡 はじめに 🪄 Maestroについて 💭 事前準備 💻 ローカルでの実装 🐱 GitHub Actionsでの実装 😎 成果 💁‍ ️ 補足 💡 はじめに こんにちは。ニフティ株式会社のLinです。 台湾出身のモバイルアプリエンジニアとして、社内で「マイ ニフティ( Android ・ iOS )」の開発を担当しております。 今回は、マイニフティ Android 2.8.1 から導入したMaestroとGitHub Actionsによるテスト自動化についてご紹介します。 🪄 Maestroについて Maestro は、Android・iOS・React Native・Flutterなどの各種モバイルプラットフォームに対応した、オープンソースの軽量UIテストフレームワークです。 基本利用は無料ですが、CIを導入すると Maestro Cloud は有料サービスです。ただし、GitHub Actionsを利用すれば無料で同様の機能を実現できます。 (節約できる費用は こちら で計算できます) 今回はこの裏技を紹介します。  💭 事前準備 まずは下記のコマンドを使ってMaestroをインストールしましょう。 curl -fsSL "https://get.maestro.mobile.dev" | bash そして、やりたいことを整理しましょう。 実現できたらいいなと思うことを下記の通り並べてみました: GitHub Actionsのプルリクで変更があれば自動的にテストフローを実行 複数端末のテスト結果を確認できる テスト結果をGitHubのコメントに自動投稿 エビデンスとしてテストの録画を確認できる こういうことは本当にできるのか?! 答えはまさかの YES ! どうやって実現するのか、一緒にやってみましょう! 🙌 💻 ローカルでの実装 CIによるテスト自動化を実現するため、下記の三つのものが必要です。 テストしたいアプリ Maestroテストフロー Github Actionsワークフロー 今回の例は弊社の会員アプリの「 マイ ニフティ 」で実装しました。 最初にアプリのbuild.gradleにアプリバージョンを取得するタスクを追加します。 (これは後のGitHub Actionsコメント機能で使われています) // build.gradle.kts (Module :app) ... tasks.register("printVersionName") { println(project.android.defaultConfig.versionName) } Maestroテストフローは下記の通り追加していきます。 重複操作はoperationsにまとめ、テストフローはtestsに集約します。 . └── maesto/ ├── operations/ │ ├── init.yaml │ ├── login.yaml │ └── skip_tutorial.yaml └── tests/ └── test_flow.yaml 使われたoperationsフローは下記の通り: // init.ymal appId: ... --- - clearState - launchApp: permissions: notifications: allow - runFlow: when: visible: "ログイン" file: "../operations/login.yaml" - runFlow: when: visible: "マイ ニフティへようこそ" file: "../operations/skip_tutorial.yaml" // login.ymal appId: ... --- - tapOn: "@nifty ID または @niftyユーザー名" - inputText: ${USER_ID} - tapOn: "パスワード" - inputText: label: Enter password text: ${PASSWORD} - tapOn: "ログイン" // skip_tutorial.ymal appId: ... --- - tapOn: "チュートリアルをスキップ" テスト用のtestsフロー例は下記の通り: // test_flow.yaml appId: ... env: USER_ID: ${USER_ID} # init.yamlから上書きが必要な場合は設定する PASSWORD: ${PASSWORD} onFlowStart: - runFlow: file: "../operations/init.yaml" --- - startRecording: test_flow_recording # ここにテストを書く - stopRecording ローカルで下記のコメントを実行すると maestro test maestro/tests/test_flow.yaml -e USER_ID="XXX" -e PASSWORD="XXX" テストフローが自動的に動き始めます。 startRecordingとstopRecordingが設定されているので、実行画面の録画も保存されています。 🐱 GitHub Actionsでの実装 動作できるMaestroフローがあれば、次はGitHub Actionsのワークフローを作りましょう。 .githubのworkflowsでandroid_maestro_tests.ymlを追加しよう。 . └── .github/ └── workflows/ └── android_maestro_test.yml // android_maestro_tests.yml name: Maestro Test on: pull_request: branches: - master paths-ignore: - "**.md" jobs: test: name: Run Maestro Tests (API ${{ matrix.api-level }}) runs-on: ubuntu-latest timeout-minutes: 30 strategy: matrix: api-level: [ 28, 33, 34 ] include: - target: default arch: x86_64 profile: pixel_6 ram-size: 8192M disk-size: 4096M heap-size: 4096M steps: - name: Checkout code uses: actions/checkout@v4 - name: Set up JDK uses: actions/setup-java@v4 with: java-version: "17" distribution: "zulu" - name: Enable KVM run: | echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules sudo udevadm control --reload-rules sudo udevadm trigger --name-match=kvm - name: AVD cache uses: actions/cache@v4 id: avd-cache with: path: | ~/.android/avd/* ~/.android/adb* key: avd-${{ matrix.api-level }} - name: Create AVD and generate snapshot for caching if: steps.avd-cache.outputs.cache-hit != 'true' uses: reactivecircus/android-emulator-runner@v2 with: api-level: ${{ matrix.api-level }} target: ${{ matrix.target }} arch: ${{ matrix.arch }} profile: ${{ matrix.profile }} ram-size: ${{ matrix.ram-size }} disk-size: ${{ matrix.disk-size }} heap-size: ${{ matrix.heap-size }} emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none force-avd-creation: false script: echo "Generated AVD snapshot for caching." - name: Build app with Gradle (Production) run: ./gradlew assembleProductionDebug - name: Get apk path id: apk-path run: echo "path=$(find . -regex '^.*/build/outputs/apk/.*\.apk$' -type f | head -1)" >> $GITHUB_OUTPUT - name: Get app version run: | VERSION_NAME=$(${{ github.workspace }}/gradlew -q printVersionName) echo "APP_VERSION=${VERSION_NAME}" >> $GITHUB_ENV - name: Set up Maestro run: | curl -fsSL "https://get.maestro.mobile.dev" | bash ~/.maestro/bin/maestro --version || echo "Failed to execute maestro" - name: Run Maestro tests id: run-maestro-tests uses: reactivecircus/android-emulator-runner@v2 with: api-level: ${{ matrix.api-level }} target: ${{ matrix.target }} arch: ${{ matrix.arch }} profile: ${{ matrix.profile }} ram-size: ${{ matrix.ram-size }} disk-size: ${{ matrix.disk-size }} heap-size: ${{ matrix.heap-size }} emulator-options: -no-window -no-snapshot-save -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none script: | $ANDROID_HOME/platform-tools/adb install ${{ steps.apk-path.outputs.path }} ~/.maestro/bin/maestro test --format junit maestro/tests/ -e USER_ID="${{ secrets.TEST_USER_ID }}" -e PASSWORD="${{ secrets.TEST_PASSWORD }}" -e APP_VERSION=${{ env.APP_VERSION }} - name: Upload test record id: upload-test-record if: ${{ steps.run-maestro-tests.outcome == 'success' }} uses: actions/upload-artifact@v4 with: name: Android_SDK_API_${{ matrix.api-level }}(${{ env.APP_VERSION }}_Succeeded) path: "**.mp4" - name: Add test record to PR comment if: ${{ steps.upload-test-record.outcome == 'success' }} env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | # Prepare GitHub CLI if ! command -v gh &> /dev/null; then sudo apt-get install gh -y fi # Prepare PR comment comment="### ${{ env.APP_VERSION }}テスト成功(Android SDK API ${{ matrix.api-level }}) : [レコードをダウンロードする](${{ steps.upload-test-record.outputs.artifact-url }})" # Post PR comment gh pr comment ${{ github.event.pull_request.number }} --body "$comment" - name: Upload test report id: upload-test-report if: ${{ steps.run-maestro-tests.outcome == 'failure' }} uses: actions/upload-artifact@v4 with: name: Android_SDK_API_${{ matrix.api-level }}(${{ env.APP_VERSION }}_Failed) path: | **.mp4 report.xml - name: Add test report to PR comment if: ${{ steps.upload-test-report.outcome == 'success' }} env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | # Prepare GitHub CLI if ! command -v gh &> /dev/null; then sudo apt-get install gh -y fi # Prepare PR comment comment="### ${{ env.APP_VERSION }}テスト失敗(Android SDK API ${{ matrix.api-level }}) : [レポートをダウンロードする](${{ steps.upload-test-report.outputs.artifact-url }})" # Post PR comment gh pr comment ${{ github.event.pull_request.number }} --body "$comment" このワークフローで下記の機能が実装されました: エミュレーターはテストしたいAndroid SDKバージョンのPixel 6を指定、実行安定性を保証するためにram-size、disk-sizeとheap-sizeもデフォルト値より高い数値に設定しました エミュレーターを利用するためKVMを有効化し、Productionのapkをビルドしてインストールパスを記録し、エミュレーターにapkをインストールしてMaestroテストフローを実行します 実行成功したら、エミュレーターのキャッシュが保存され、次回実行する時に再利用されます 先ほど追加したprintVersionNameタスクによりテスト対象アプリのバージョンを自動取得し、実行録画もartifactに自動保存、ダウンロードリンクを自動生成して、コメントにエビデンスを残します 成功したら成功コメントを自動投稿、失敗したら失敗コメントを自動投稿 ワークフローで使われたIDとパスワードも GitHub → Settings → Secrets and variables → Actions のsecretsに登録しましょう。 そして、自動テスト君が爆誕。 😎 成果 次回、リリースプルリクが作成された際に、呪文を詠唱すれば自動テスト君を召喚できます。 集いしテストフローが新たな力を呼び起こす!自動化の道となれ! いでよ、自動テスト君! 冗談です (・ω・) 何もしなくても自動テスト君が自動的に召喚され動作します。 テスト成功・失敗した場合、実行エビデンスが自動生成され、ダウンロードして確認できます。 素晴らしいですね! テスト結果は一目で確認でき、レコードをダウンロードすると実行録画も確認できます。 💁‍♂️ 補足 Maestroは便利ですが、よくある落とし穴もいくつかあります: Maestro 1.33.1以上では、度々「emulator not found」の問題が発生します 詳細 ram-size、disk-sizeとheap-sizeを高めに設定するか、GitHub Actions Runners のスペックを増強することで、ある程度改善できます MaestroはWebViewに弱く、assertVisibleは度々失敗します extendedWaitUntil を追加すると、ある程度改善できます 度々ボタン・文字を認識できない問題があります 対象の contentDescription を追加したら改善できます(null にしないでください)
アバター
エンジニアリングマネージャーをしています芦川です。2024年12月7日に Developers CAREER Boost 2024 の懇親会でLTをしました。当日、結構焦って発表してたなあ、あれ言うの忘れたなあ、というのもあったので、内容について、ここで改めて書いてみたいと思います。 また、計画的偶発性について語られている方が当日多く、たしかに、私もこれまでの1つの会社の中であるにせよ、予期しないことに柔軟に前向きに取り組めたおかげで楽しくなっているんだな、ということもわかりました。非常に有意義なイベントでした! プロセスを楽しんでいこう!というメッセージとともに、私のニフティ新卒から20年のエンジニアキャリアを通じて培ってきた自分戦略についてお話しさせていただきます。 自己紹介 ニフティ株式会社でエンジニアリングマネージャーを務めている芦川亮と申します。2004年に新卒入社してから、バックエンドエンジニア、スクラムマスター、そして現在のエンジニアリングマネージャーと、様々な役割を経験してきました。バックエンド後半では主にOAuth2.0/OpenID Connectなどの認証・認可系の開発に携わっていました。 自分戦略 私の自分戦略は、「そのときの状況や環境において自分が積極的に楽しめる方法、学べる行動をする」というものです。この戦略について、私のキャリアの各フェーズに沿って詳しく説明していきたいと思います。 バックエンドエンジニア時代 エンジニアとして最初のキャリアでは、日常的な手作業や運用業務を自動化のチャンスとして捉えることを心がけました。面倒な作業があっても、「これをどうやって自動化できるか」と考えることで、仕事にワクワク感を見出すことができました。もう一回言います、手動作業は自動化してやるチャンスです。 ログ調査を自動化したり、ブラウザでの手動ポチポチをスクレイピングをしたり。バックエンド時代と書きましたが、実は今でもマネージャー特有の承認作業の省力化など密かにやっております。なぜなら、楽しいから。 たとえ1分の省力化であっても、それを500人規模の組織で展開すれば、1日8時間分、つまり、ざっくり人間1人を雇ったことと等価。小さな改善の積み重ねが、大きな価値を生み出すのです。 スクラムマスター時代 さて、つづいては、スクラムマスター時代です。 皆さんご経験があるかもしれません。環境や自分のロールを変えることで、成長というか、経験値が飛躍的にあがった体験があるのではないでしょうか? 環境やロールの変更は、自己成長の大きなジャンプアップのチャンスです。スクラムマスターとして、チームマネジメント、ファシリテーション、心理的安全性など、それまで未知だった分野に挑戦することが新鮮で楽しかったです。未知なものを学ぶこと自体楽しいですが、楽しさが倍増したのは「私は今学んだことをすぐに実践できるポジションにいる」ということだったと思います。学んですぐに試せる場があるということは幸せです。 図は一例です。レトロスペクティブの方法にKPTがあると思いますが、メンバーの気持ちにくすぶっている「PROBLEMまで昇格できていないもやもやや愚痴」のようなものを吐き出せるように、KPTに「BOYAKI」を追加しまして、実践していた例です。 で、改めて振り返ると、「自分対メンバーという構図」ができ始めた時代であり、そういうつながりになって初めて得られる学びってあるんだな、と実感しました。 エンジニアリングマネージャー時代1 最後は、エンジニアリングマネージャー時代です。 エンジニアリングマネージャーというと最初は何をしたらいいのだろう、というところでしたが、右の図( engineering-management-triangle )が教えてくれました。これは、エンジニアリングマネージャートライアングルというもので、EMは、プロダクトとチームとテクノロジーの間を埋める存在であると。例えば採用活動であれば、テクノロジーとチームの間をうめるものになります。 現在のエンジニアリングマネージャーの役割は、私の20年のキャリアの中で最も楽しいと感じています。というのは、EMって何をしたらいいのだろう、ではなくて、「何をやってもこの中にはてはまっていくんだな」、ということがわかり、心地よいある種のコミット感の中で、学習や実践ができることができている、という実感あります。コミット感、というのは自身の成長を感じる部分において重要ですね。皆様は、コミット感、感じてらっしゃいますか? というところで、今が、20年いた中で1番自由であり、仕事が楽しいと感じています。EMおすすめ。 エンジニアリングマネージャー時代2 さらに、人同士をつなげる役割も楽しいプロセスなのかと気づいた時代です。私の場合はたまたまそれがEM時代でしたが、もっと早くからこのようにネットワーキングされている方はいらっしゃると思います。正直、羨ましい! きっかけは会社関係でしたが、大学の教授とかコワーキングスペースにいた全く異なる業種の方と話したり、外部コミュニティで人と人をつなぐ役割に入ったりして、人同士がつながって、そこでうまれるコラボレーションに感動したりします。そういう楽しみ方があったか。 右の図は今、属しているインナーソースコモンズジャパンです。これは、OSSでの開発プラクティスを企業内に持ち込んで、オープンなエンジニア文化を根ざそう、というものであり、ご興味ありましたら、お声がけください。 宣伝1 というところで宣伝させてください。 InnerSource Commons Community です! 宣伝2 人と人がつながると言えば、ニフティが主催するテックイベントがあります。 NIFTY Tech Day 2025 今回で3回目、ハイブリッド開催を考えております!来る2025年2月8日 土曜日開催です!テーマは「学び」。 NIFTY DevelopersのX より更新情報をお待ち下さい! まとめ 1行でまとめてみると、仕事のWhatについては事業起点で会社の中でうまれたり、状況によって作られたり(自分が作ったり)しますが、Howについては、個人の意思で変えていけるところだと思います。 ということで、日々、これからも「どうやれば、この仕事は楽しくなる?学びが増える?」ということを自分に問い続け、モチベーション高くやっていきたいと思います。 どうやれば、この仕事は楽しくなる? エンジニア人生楽しんでいますか? 以上、私の自分戦略でした!
アバター
この記事は、 ニフティグループ Advent Calendar 2024 11日目の記事です。 こんにちは! 今回は、JavaScriptとTypeScriptの三大ランタイム環境であるDeno、Bun、Node.jsについて比較していこうと思います。 概要 Node.js: 2009年に登場した最も成熟したJavaScriptランタイム Deno: 2018年にNode.jsの創始者によって開発された、より安全でモダンなランタイム Bun: 2022年に登場した高速で全機能を備えたJavaScript/TypeScriptランタイム 主な特徴 Node.js 広大なエコシステムと豊富なnpmパッケージ 長年の実績と安定性 非同期I/Oに最適化 Deno セキュリティ重視(デフォルトで安全) TypeScriptのネイティブサポート URLベースのモジュールインポート Bun 高速な起動とランタイム実行 Node.jsとの高い互換性 内蔵のパッケージマネージャとバンドラー 比較 パフォーマンス比較簡単なベンチマークとして、各ランタイムで1から1000万までの数を数える処理の実行時間を比較してみましょう。 Node.js Homebrewや公式サイト( https://nodejs.org/en )などでインストールする benchmark.jsというテストファイルを作成する。 console.log('Starting benchmark...'); const start = process.hrtime.bigint(); let count = 0; for (let i = 0; i < 10000000; i++) { count++; } const end = process.hrtime.bigint(); const duration = Number(end - start) / 1_000_000; // ナノ秒からミリ秒に変換 console.log(`Counting completed in ${duration.toFixed(3)} ms`); console.log(`Final count: ${count}`); 実行する node benchmark.js 実行結果 1回目 $ node benchmark.js Starting benchmark... Counting completed in 7.381 ms Final count: 10000000 2回目 $ node benchmark.js Starting benchmark... Counting completed in 8.529 ms Final count: 10000000 3回目 $ node benchmark.js Starting benchmark... Counting completed in 8.092 ms Final count: 10000000 Deno Homebrewなどでインストールする https://docs.deno.com/runtime/getting_started/installation/ benchmark.jsというテストファイルを作成する。 console.log('Starting benchmark...'); const start = performance.now(); let count = 0; for (let i = 0; i < 10000000; i++) { count++; } const end = performance.now(); const duration = end - start; console.log(`Counting completed in ${duration.toFixed(3)} ms`); console.log(`Final count: ${count}`); 実行する deno run benchmark.js 実行結果 1回目 $ deno run benchmark.js Starting benchmark... Counting completed in 10.852 ms Final count: 10000000 2回目 $ deno run benchmark.js Starting benchmark... Counting completed in 10.905 ms Final count: 10000000 3回目 $ deno run benchmark.js Starting benchmark... Counting completed in 9.019 ms Final count: 10000000 Bun Homebrewなどでインストールする https://bun.sh/docs/installation benchmark.jsというテストファイルを作成する。 console.log('Starting benchmark...'); const start = process.hrtime.bigint(); let count = 0; for (let i = 0; i < 10000000; i++) { count++; } const end = process.hrtime.bigint(); const duration = Number(end - start) / 1_000_000; // ナノ秒からミリ秒に変換 console.log(`Counting completed in ${duration.toFixed(3)} ms`); console.log(`Final count: ${count}`); 実行する bun benchmark.js 実行結果 1回目 $ bun benchmark.js Starting benchmark... Counting completed in 7.849 ms Final count: 10000000 2回目 $ bun benchmark.js Starting benchmark... Counting completed in 9.821 ms Final count: 10000000 3回目 $ bun benchmark.js Starting benchmark... Counting completed in 14.227 ms Final count: 10000000 結果 すべてほぼ同等のパフォーマンスを示しています。 HTTPサーバー作成 今度は各ランタイムで簡単なHTTPサーバーを作成してみます。 Node.js server.jsというファイルを作成します。 const http = require('http'); const server = http.createServer((req, res) => { res.writeHead(200, { 'Content-Type': 'text/plain' }); res.end('Hello Worldn'); }); server.listen(3000, () => { console.log('Server running at http://localhost:3000/'); }); 実行 $ node server.js Server running at http://localhost:3000/ TypeScriptファイルを試してみる server.tsを作成する import * as http from 'http'; const server: http.Server = http.createServer((req: http.IncomingMessage, res: http.ServerResponse) => { res.writeHead(200, { 'Content-Type': 'text/plain' }); res.end('Hello Worldn'); }); server.listen(3000, () => { console.log('Server running at http://localhost:3000/'); }); 実行 実行するとエラー $ node server.ts (node:38672) Warning: To load an ES module, set "type": "module" in the package.json or use the .mjs extension. (Use `node --trace-warnings ...` to show where the warning was created) /Users/server.ts:1 import * as http from 'http'; ^^^^^^ SyntaxError: Cannot use import statement outside a module at wrapSafe (node:internal/modules/cjs/loader:1350:18) at Module._compile (node:internal/modules/cjs/loader:1379:20) at Module._extensions..js (node:internal/modules/cjs/loader:1518:10) at Module.load (node:internal/modules/cjs/loader:1249:32) at Module._load (node:internal/modules/cjs/loader:1065:12) at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:158:12) at node:internal/main/run_main_module:30:49 Node.js v22.2.0 TypeScriptをネイティブサポートしていないことが分かります。 Deno server.tsというファイルを作成します。 DenoはTypeScriptをネイティブサポートしています。 import { serve } from "https://deno.land/std@0.181.0/http/server.ts"; const handler = (req: Request): Response => { return new Response("Hello Worldn", { status: 200 }); }; console.log("Server running at http://localhost:3000/"); await serve(handler, { port: 3000 }); 実行 Denoはセキュリティを重視しているため、ファイルアクセスやネットワークアクセスにはパーミッションが必要です。 例えば、ファイル読み書きの場合は以下のようにします。 deno run --allow-read --allow-write file_io.ts $ deno run --allow-net server.ts Server running at http://localhost:3000/ Listening on http://localhost:3000/ ネットワークアクセス許可(--allow-net)を与えないで実行すると、許可してよいかと聞かれます。 $ deno run server.ts Server running at http://localhost:3000/ ┏ Deno requests net access to "0.0.0.0:3000". ┠─ Requested by `Deno.listen()` API. ┠─ To see a stack trace for this prompt, set the DENO_TRACE_PERMISSIONS environmental variable. ┠─ Learn more at: https://docs.deno.com/go/--allow-net ┠─ Run again with --allow-net to bypass this prompt. ┗ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all net permissions) > Bun server.tsというファイルを作成します。 BunはTypeScriptをネイティブサポートしています。 const server = Bun.serve({ port: 3000, fetch(req) { return new Response("Hello Worldn"); }, }); console.log(`Listening on http://localhost:${server.port}`); 実行 $ bun run server.ts Listening on http://localhost:3000 DenoとBunの比較 1. モジュールのインポート:    Deno: 外部モジュールをURLから直接インポートします。 import { serve } from "https://deno.land/std@0.181.0/http/server.ts"; Bun: 組み込みのグローバル  Bun  オブジェクトを使用し、追加のインポートは不要です。 2. サーバーの設定:    Deno:  serve  関数を使用し、ハンドラー関数と設定オブジェクトを渡します。 await serve(handler, { port: 3000 }); Bun:  Bun.serve  メソッドを使用し、設定オブジェクトを渡します。 const server = Bun.serve({ ... }); 3. リクエストハンドラー:   Deno: 独立した関数として定義します。 const handler = (req: Request): Response => { ... }; Bun: 設定オブジェクト内の  fetch  メソッドとして定義します。 fetch(req) { ... } 比較ポイント 上記の比較結果では、各ランタイムの HTTP サーバー実装の違いがありました。 以下に、主要な比較ポイントをまとめました。 1. TypeScript サポート : Node.js: 追加のツール (ts-node) とタイプ定義 (@types/node) が必要。 Deno: ネイティブサポート。 Bun: ネイティブサポート。 2. セキュリティモデル : Node.js: デフォルトですべてのシステムリソースにアクセス可能。 Deno: 明示的な権限付与が必要( --allow-net フラグ)。 Bun: Node.js 互換のモデルを採用(デフォルトでアクセス可能)。 3. API設計 : Node.js: 標準的な http モジュールを使用。 Deno: モダンな Web 標準に基づいた API。 Bun: シンプルなオブジェクトベースの API。 4. モジュールシステム : Node.js: ES Modules (import 構文) を使用。 Deno: ES Modules と URL ベースのインポートを使用。 Bun: 組み込みの Bun オブジェクトを使用。 5. 実行の簡単さ : Node.js: TypeScript を直接実行するには追加のセットアップが必要。 Deno: 追加のセットアップなしで TypeScript を直接実行可能。 Bun: 追加のセットアップなしで TypeScript を直接実行可能。 結論 結論として、Node.js、Deno、Bunの3つのランタイムは、それぞれ独自の強みを持ち、JavaScriptエコシステムの多様化と進化に大きく貢献しています。 また、これらのランタイムは急速に進化を続けているため、定期的に最新の動向をチェックし、必要に応じて選択を見直すことをお勧めします。 本ブログが、読者の皆様のプロジェクト選択や技術的な意思決定の一助となれば幸いです。 より詳細な情報や具体的な使用例については、各ランタイムの公式ドキュメントを参照することをお勧めします。 明日は、Tsan0409さんの記事です。お楽しみに。
アバター
この記事は、 ニフティグループ Advent Calendar 2024 10日目の記事です。 CLIツールを社内にプライベートtapリポジトリを作成し、社内に配布しました。 意外と記事がなかったのでその方法をご紹介します。 アプリケーションのリリースに使っているのはGoReleaserです。 きっかけ ニフティ株式会社では現在Oneloginを使っています。 AWSのIAM RoleにAssumeRoleする際は以下のOneloginが提供しているツールを使っている社員がほとんどです。 https://github.com/onelogin/onelogin-python-aws-assume-role このツールはjava版もあるのですが、どちらにせよランタイムのインストールが必要です。 また比較的頻繁に更新されているわけではないため、バージョンによっては依存ライブラリの問題でセットアップが失敗するなどの問題がありました。 そこで、これをGoで作り直すことでシングルバイナリかつセットアップ不要にしつつ、brewで配布することでMacやWSL環境への導入をより簡単にしようと決めました。 サードパーティ製の他のツールを使うという手段もありましたが、私自身がインフラ管理者ではない(全社向けの選定権がない)ことや、現在使っているツールからの移行モチベーションを上げるため設定ファイルを流用することが重要だったこと、インナーソース化することでより柔軟に開発者自身が必要な機能を追加していけることを重視し、自作することに優位性があると判断しました。 Tapリポジトリの作成 バイナリを配布するためのtapリポジトリを作成します。 このリポジトリの名前は homebrew-<repo> の形式である必要があります。 https://docs.brew.sh/Taps#repository-naming-conventions-and-assumptions プライベートなtapリポジトリを作成する場合、GitHubのトークンを使ってダウンロードURLを作る必要があります。 以下のGistが非常に参考になるため、これを使います。 https://gist.github.com/ZPascal/b21c652b811872b3f56db9d54d61d6c6 GitHubPrivateRepositoryDownloadStrategy というクラスがダウンロード戦略となります。 指示通り、tapリポジトリのlib/配下に配置します。 アプリケーションの作成 今回はHomebrewで配布する部分がメインなので、ここではGoReleaserの設定について紹介します。 GoReleaserは簡単に複数のOSやCPUアーキテクチャ向けにBuildしたり、パブリッシュすることができるツールです。 https://github.com/goreleaser/goreleaser このツールを使ってGitHub Actionsでリリースしています。 アプリケーションリポジトリからtapリポジトリにpushしてPRを作成する必要があるため、PrivateAccessTokenが必要になります。 ダウンロード戦略として先ほど作成した GitHubPrivateRepositoryDownloadStrategy を使うよう指定します。 .goreleaser.yaml version: 2 before: hooks: - go mod tidy - go generate ./... builds: - main: ./cmd/ binary: onelogin env: - CGO_ENABLED=0 goos: - linux - windows - darwin archives: - format: tar.gz name_template: >- {{ .ProjectName }}_ {{- title .Os }}_ {{- if eq .Arch "amd64" }}x86_64 {{- else if eq .Arch "386" }}i386 {{- else }}{{ .Arch }}{{ end }} {{- if .Arm }}v{{ .Arm }}{{ end }} format_overrides: - goos: windows format: zip changelog: sort: asc filters: exclude: - "^docs:" - "^test:" brews: - name: onelogin download_strategy: GitHubPrivateRepositoryReleaseDownloadStrategy custom_require: lib/private_strategy repository: owner: niftycorporation name: homebrew-tap branch: "{{ .ProjectName }}" token: "{{ .Env.PRIVATE_GITHUB_TOKEN }}" pull_request: enabled: true draft: false base: branch: master workflow name: Upload Release Asset on: push: tags: - 'v*' permissions: contents: write jobs: build: name: Upload Release Asset runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 with: fetch-depth: 0 - name: Set up Go uses: actions/setup-go@v5 with: go-version-file: go.mod cache: true - uses: goreleaser/goreleaser-action@v6 with: distribution: goreleaser version: "~> v2" args: release --clean env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} PRIVATE_GITHUB_TOKEN: ${{ secrets.PRIVATE_GITHUB_TOKEN }} このWorkflowが実行されるとreleasesが作成され、tapリポジトリにPRが作成されます。 一部変更していますが、以下のようなファイルが生成されます。 GoReleaserを使わなくてもこのようなファイルがあれば配布すること自体は可能です。 onelogin.rb # typed: false # frozen_string_literal: true # This file was generated by GoReleaser. DO NOT EDIT. require_relative "lib/private_strategy" class Onelogin < Formula desc "" homepage "" version "1.0.2" on_macos do on_intel do url "https://github.com/niftycorporation/onelogin-go-aws-assume-role/releases/download/v1.0.2/onelogin-go-aws-assume-role_Darwin_x86_64.tar.gz", using: GitHubPrivateRepositoryReleaseDownloadStrategy sha256 "hash" def install bin.install "onelogin" end end on_arm do url "https://github.com/niftycorporation/onelogin-go-aws-assume-role/releases/download/v1.0.2/onelogin-go-aws-assume-role_Darwin_arm64.tar.gz", using: GitHubPrivateRepositoryReleaseDownloadStrategy sha256 "hash" def install bin.install "onelogin" end end end on_linux do on_intel do if Hardware::CPU.is_64_bit? url "https://github.com/niftycorporation/onelogin-go-aws-assume-role/releases/download/v1.0.2/onelogin-go-aws-assume-role_Linux_x86_64.tar.gz", using: GitHubPrivateRepositoryReleaseDownloadStrategy sha256 "hash" def install bin.install "onelogin" end end end on_arm do if Hardware::CPU.is_64_bit? url "https://github.com/niftycorporation/onelogin-go-aws-assume-role/releases/download/v1.0.2/onelogin-go-aws-assume-role_Linux_arm64.tar.gz", using: GitHubPrivateRepositoryReleaseDownloadStrategy sha256 "hash" def install bin.install "onelogin" end end end end end brew installする まずローカルからプライベートリポジトリにアクセスするためにPrivateAccessTokenを作成し、 HOMEBREW_GITHUB_API_TOKEN という環境変数に指定します。 tapリポジトリを指定し、インストールします。 このとき、リポジトリ名の homebrew- 部分は省略することができます。 brew tap niftycorporation/tap brew install niftycorporation/tap/onelogin Brewfileには以下のように記載します。 tap "niftycorporation/tap" brew "niftycorporation/tap/onelogin" これでプライベートのtapリポジトリでバイナリを配布することができました。 まとめ ここまでプライベートのtapリポジトリを作成し、配布する方法を紹介してきました。 企業内で独自のソフトウェアを配布したいことはそれなりに多いんじゃないかと思っています。 特に以下はかなりハマりポイントでした。 tapリポジトリの命名規則 PATを使ったダウンロード戦略の作成 PATを使ったダウンロード戦略の指定 今ハマっている方や今後やろうと思っている方はぜひこれらに気をつけていただければと思います。 ここからはタイトルに関係のない今後の展望についてですが、このリポジトリは現在インナーソースとして社内公開していて利用者も比較的多いプロジェクトになっていると考えています。 しかしコントリビュータがなかなか増えず、敷居の高さや関心を引けていないことが問題かと思っています。 ツール自体メイン機能はほぼ完成していますが、爆速で作ったためリファクタリングや処理の効率化、ドキュメントの整備などやりたい事自体はまだまだある状況です。 また、普段使わない言語や領域に触れるなどエンジニアとしての成長も望めるため、コントリビュータを増やしインナーソースやOSSに興味を持ってもらえるよう活動していく予定です。 tapリポジトリについても現在配布されているのがこのツールのみということもあり寂しいので、こちらも敷居を下げより簡単に追加できるようになればいいなと想像しています。 最後に、この記事がどなたかのお役に立てば幸いです。 明日は、@hayabusa01 さんの記事です。 お楽しみに!
アバター