TECH PLAY

ニフティ株式会社

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

510

はじめに こんにちは。新卒1年目の佐藤、緑川、keyliumです。 本記事では3日間の社内ハッカソンにて私たちのチームが開発したWebアプリケーションについて紹介していきます。 開発合宿の概要記事はこちらをご覧ください。 今年もリアルハッカソン合宿に行ってきました!@ノジマ大磯スクウェア メンバー紹介 普段、私たちはOJTジョブローテの3期目で、それぞれ異なる部署で業務を行っています。 佐藤 普段の業務:会員向けアプリ「マイニフティ」の開発 今回の担当:バックエンド ひとこと :普段触らない技術に悪戦苦闘した楽しい3日間でした! 緑川 普段の業務:IPv6関係のプロジェクトのAWS移行 今回の担当:フロントエンド ひとこと :ギリギリまで粘って欲しい機能が実装できて安心しました!!!! keylium 普段の業務:社内ネットワーク運用 今回の担当:ゲームロジック ひとこと :作りたいものはシンプルでしたが、実際に作ると大変でした! 制作物紹介 概要 複数人で整数を投票し、最小で固有の数字を投票した人が優勝するゲームを遊べるWebアプリケーション 遊び方 HOME画面にて、部屋を選択、ユーザー名を入力し、ENTRYを押します 投票画面へ遷移後、 同じ部屋の他ユーザーと数字が重複しない最小の数値 を予想して入力します 投票画面では数字のスタンプなどが用意されており、駆け引きができます 同じ部屋に3人が集まり、3人全員が投票完了すると結果発表に自動遷移します 3人の中で重複なし かつ 最小の数値に投票した方が優勝となります 投票画面とスタンプ 投票画面とスタンプ 投票画面とスタンプ 4が重複なしで最小なので勝ち 4が重複なしで最小なので勝ち 4が重複なしで最小なので勝ち システム構成 今回は フロントエンド:TypeScript + React バックエンド :AWS で開発を行いました。 バックエンドには AWS Amplify を採用しました。 フロントエンドはAWS Amplifyにホストし、4つのDynamoDBとのやりとりにはAWS AppSyncのGraphQLを使っています。 AWS Amplifyとは? Webアプリケーションやモバイルアプリの開発を高速化してくれるサービスです。 使用することで以下のメリットが得られます。 認証基盤やストレージ、データベースなど、開発に必要なリソースを自動で作成してくれる GitHubのリポジトリと連携することでCI/CDのプロセスを自動化し、環境ごと(本番 / 開発 / ステージング / ローカルなど)・プルリクエストごとに専用のリソースを作成してくれる 今回は開発日数が3日間と短い時間だったので、AWS Amplifyを使うことで開発スピードを早めることを図りました。 AWS Amplifyのおかげで、設定ファイルで必要なリソースを定義するだけで手動で作成することなく、また、PRが来るごとに正しく動作しているのか、実際にアクセスして確認することができました。 一方、デメリットとして、 自動でリソースを作ってくれるため、内部でどんな操作をしているのかが不明 Gen 2のバージョンの日本語記事が少なく、英語記事を読む or 手探りで開発をしていく必要がある の2点が挙げられます。 特に2つ目について、3人ともAWS Amplifyを使うのが初めてだったこともあり、とても苦戦しました。 後ほど、AWS Amplifyについての記事を投稿する予定です。 公式ドキュメント(Gen2 React版)は こちら 工夫した点 AWS Amplifyを使うことでCI/CDやAWSリソース作成の自動化 AWS公式のAWS Amplify用のテンプレート amplify-vite-react-template を使用し、仕様理解を促進 AWS AppSyncのPub/Subによってユーザーなどの状態をリアルタイムで監視 上記3つの技術詳細につきましては、後日投稿予定の記事にて記載いたします。 Notionを活用したスキーマ設計 UIコンポーネントライブラリ( Material UI )を使用することでデザインの統一 GitHubでカンバンを作り、それぞれが行う作業を可視化 アーキテクチャ図を早期に作って全メンバーで認識を共通化 学んだこと・成長したこと 初めて触るAWS AmplifyやReactの理解 公式ドキュメントを読み込むことの重要性 タスクの取捨選択の重要性 PRレビューへの積極的参加の重要性 開発における体力・集中力アップ まとめ 私たちが3日間で開発した制作物について紹介しました。 普段の業務の中でここまで集中して1つのものを開発できる機会はなかなかないと思うので、とても良い経験となりました。 十分なエラーハンドリングが無かったり、テストコードを書いていなかったりと、完璧ではありませんでしたが、3日間で動くものができた!ということを自信に、これからの業務に生かしていこうと思います!
はじめに こんにちは、新卒1年目の滝川、藤岡、山本です。 弊社では新人育成のため、毎年エンジニア定例という社内勉強会が実施されており、その集大成として先日3日間にわたる開発合宿に参加してきました。 合宿の概要については以下をご参照ください。 今年もリアルハッカソン合宿に行ってきました!@ノジマ大磯スクウェア 3つのグループに分かれて開発を行い、今回私たちのチームは「降水量可視化ツール」として降水量を数値ではなく感覚的にわかりやすくするWebアプリを作成したので紹介します。 メンバー 滝川 普段の業務:社内セキュリティの維持、強化 今回の担当:AWSでのインフラ構築、CI/CDの構築 ひとこと :みんなでスマブラをしたことが思い出です 藤岡 普段の業務:メールシステムの運用、問い合わせ対応 今回の担当:AWSでのインフラ構築、CI/CDの構築 ひとこと:合宿中に食べたお菓子の量No.1の自信があります 山本 普段の業務:お客様に提供している回線プランの契約書面作成システム関連 今回の担当:バックエンド、フロントエンド ひとこと:合宿中に食べた食堂の爆盛りご飯が今でも軽いトラウマです(少食人間) 降水量可視化Webアプリ 今回私たちのチームで作成した降水量可視化アプリは以下のような方針で作成しました。 天気予報の降水量をもとに、実際の雨の強さをグラフィックでわかりやすく可視化するWebアプリケーション 日常で「降水量○○mm以上なら傘が必須」など数字だけで判断しづらい部分を、視覚的な演出で補う狙い 100万アクセスに耐えられる設計を追求 背景 雨の強さを数値以上の直感的な形で伝えることで、「傘の準備」「服装の選択」などをわかりやすくサポートする インフラに強いメンバーが集まったのでインフラに力を入れた 技術構成 インフラ: AWS(TerraformによるIaCを採用) フロントエンド: 既存のライブラリrainyday.jsを使用し、雨の見た目を演出 CI/CD:GitHub Actionsによる自動デプロイの実装 天気情報取得:Open-Meteo APIを採用 ※非商用利用の場合に無料で使用が可能です。商用利用の場合は別途登録が必要となります。 バックエンド:Flask(Python)を使用。WebSocketを活用しリアルタイムチャットを実現 デモ サイトにアクセスするとマップが表示される。 位置情報の共有をオンにしている場合、現在地が反映された場所になる。 選択した地域で雨が降っている場合は、マップを閉じると雨粒が窓をつたうような表示される。 雨の強度によって、雨粒の量が変化する。 天気情報がリアルタイムの天候と異なっている可能性があるため、右下にリアルタイムチャットを作成。 同じ市区町村を閲覧しているユーザ同士でリアルタイムにチャットできる。 システム構成 今回のチームの方針である100万アクセスに耐えられるインフラを構築することを目標に以下のポイントについて頑張りました。 CloudFrontとS3を活用したキャッシュ戦略 ALBによる負荷分散 Auto Scaling 開発の流れ 0日目:アイデアソン 作成するアプリケーションの検討および仕様の詳細化 1日目:要件定義 チーム内でアイデアを提案し合い、システムアーキテクチャの決定および利用技術を選定 開発開始 2日目:開発 フロントとバックエンドに分かれて作業 インフラ組はTerraformを用いてAWSの環境構築 フロント組は雨粒表現が正しく動作することを確認しながら段階的に実装 3日目:最終調整・成果発表 動作テストを実施 発表資料の作成 最終成果をプレゼン 工夫した点 本プロジェクトでは、以下の点において特に工夫を重ねました。 インフラの堅牢性 CloudFrontとS3を組み合わせた効率的なキャッシュ戦略の実装 Auto Scalingによる柔軟な負荷対応 Terraformを用いたインフラのコード化による再現性の確保 ユーザビリティの向上 直感的に理解できる雨の強度表現 位置情報との連携による自動地域設定 リアルタイムチャット機能による情報共有の実現 開発プロセス GitHub Actionsを活用した継続的デプロイメントの構築 チーム内での密なコミュニケーションによる進捗管理 各メンバーの強みを活かした効率的なタスク分担 苦労した・学んだこと インフラ構築を通じて、事前の技術調査の重要性を痛感しました。 CDNでのキャッシュ機能の実装とWebSocket通信の実装を同時に行う際には、WebSocket周りのキャッシュを無効化する必要がありました。特にそもそもCDNとは何かのインプットから始まったため、WebSocket通信を使うための設定をするところまで実装するのにかなりの時間を要しました。事前にCDNに関する技術調査を行っておく事で、このような課題により効率的に取り組み、他の部分により注力することができたと感じています。 また、実装したインフラ上にローカルで作成していたWebアプリを載せる、環境統合テストの遅れにより問題の発見が遅くなったため、早期に実施することの重要性を学びました。 加えて、悩むよりも周囲と相談・共有することで、より効率的に課題を解決できる場面があることも実感しました。 さらに、座学だけでなく実際に手を動かすことで理解が深まること、そして計画的に進捗を管理することでトラブルを回避できることの大切さを学びました。 まとめ これまでに得た知見を存分に発揮し、フロントエンド、バックエンド、インフラの各領域において高い完成度の成果物を作成することができました。 チームメンバーそれぞれが自身のタスクに取り組むだけでなく、困難な問題に直面した際には全員で協力して解決にあたりました。その結果、各自の得意分野を活かしながら、苦手な部分を相互にカバーすることができ、非常に効果的なチーム運営ができたと感じています。 しかしながら、実現したい機能やアイデアがあっても、技術力がなく実装できなかった部分もあり、自身の技術力の不足を痛感する機会にもなりました。この経験を通じて、さらなる技術向上の必要性を強く認識しました。 この合宿は、成果を形にする喜びと同時に、今後の成長に向けた課題を明確にする貴重な機会となりました。
こんにちは、ニフティでエンジニアをしている江口です! ニフティでは毎年、エンジニア定例という新卒1年目エンジニア向けの開発研修を行っています。そして開発研修の集大成として、ハッカソン合宿を実施してきました! 私もエンジニア定例の運営として合宿に参加してきたので、その様子を公開いたします! また、以下のブログで今年度の講義内容についても公開しているので、気になる方はぜひ覗いてみてください! ニフティ株式会社 エンジニア新人研修の内容を公開します | 2024年度版 ハッカソン合宿概要 目的 座学で学んだ技術を 実践 することで知識を定着させる。 2泊3日というまとまった時間で 集中したアウトプット を出す経験をする。 既存システムのエンハンスではなく、 0からシステムを作る力 を身につける。 個人開発と チーム開発 の違いを学ぶ。 スケジュール 1日目 2025/01/21(火) 10:00 集合・チェックイン 11:00-12:00 開発スタート! 12:00-13:00 昼食 13:00-18:00 開発 18:00-19:00 夕食 19:00- 自由(開発・入浴など) 2日目 2025/01/22(水) 7:00-8:00 朝食 8:30-12:00 開発 12:00-13:00 昼食 13:00-18:00 開発 18:00-19:00 夕食 19:00- 自由(開発・入浴など) 3日目 2025/01/23(木) 7:00-8:00 朝食 8:30-12:00 開発 12:00-13:00 昼食 13:00-14:00 最終調整 14:00-15:00 現地から成果報告の全社配信 15:00-16:00 終了連絡・撤収作業 16:00 解散 場所 ノジマ大磯スクウェア https://maps.app.goo.gl/qDRevCT8tqmRSnE88 JR東海道線 大磯駅から徒歩2,3分 合宿の様子 1日目 2025/01/21(火) 今回の合宿開催場所であるノジマ大磯スクウェアに到着! 昨年は初日から雪が降っていましたが、今年は快晴でした! 早速開発スタート。各チーム、アイディアソンで考えてきたアイデアを元に手を動かしていきます。 ちなみに合宿のルールとして、 合宿が始まるまではコーディング禁止 です! 皆さん0から作っていきます。 あっという間にお昼ご飯タイム。 私も1年ぶりにノジマ大磯スクウェアのご飯を食べました! 美味しいしボリューミーで最高ですね。 午後はひたすら開発をしていきます、初日からかなりいいペースで進んでいる様子。 こちらのチームはホワイトボードに巨大な構成図を書いていますね、インフラ構築を早い段階から始めている様子。 19時以降は入浴など休憩もしつつ、やりたいチームは残って開発をするというスタイルでした。 皆さん初日からアクセル全開で、全員24時までやっていました(笑) 2日目 2025/01/22(水) 2日目、朝から晩まで開発です! 朝食は7:00-8:00の間に済ませます。 運営陣は朝食後、みんなで海辺に散歩しにいきました。ノジマ大磯スクウェアから徒歩で10分もかからず海に出れます、最高ですね。 皆さん昨日も遅くまでやっていたのに、めちゃくちゃ元気。ガンガン進めていきます。 AWS Amplifyを採用しているこちらのチーム、画面も形になってきましたね。 こちらのチームは2日目に作った構成図を元に、インフラ構築を進めていきます。フロントも順調そう! 美味しそうなラーメンがたくさん並んでいるこちらのチーム。写真ではわからないですが、バックエンドの構成がかなり美しいです、すごい。 2日目も、まだ開発したいチームは残って開発をするというスタイルでしたが、皆さん明日の発表に向けて、しっかり24時まで作業していました! 運営も元気な皆さんに必死で食らいついていきます。 3日目 2025/01/23(木) 最終日です! 午前中はこれまで通り開発で、午後は成果報告会でした。 成果報告会では作成したサービスのデモや工夫点、開発での良かった点と悪かった点、成長したことや学んだことなどを、各チーム10分程で発表します。 運営も配信の準備を進めていきます。 配信スタート! こちらの成果報告会は 全社配信 ということもあり、配信開始してすぐに80名近くの方に参加していただけました。 Slackに実況チャンネルを設けて、配信を視聴していただいている皆さんからの反応や質問なども、リアルタイムで見れるようにしています。 ハッカソン合宿に参加していたメンバーだけではなく、新人のトレーナーをしていた先輩社員や所属しているチームのマネージャー、人事や企画部門の社員など、多くの方に合宿の成果を見ていただける素晴らしい機会です。 では早速発表を見ていきましょう。(本ブログ内では概要のみの紹介となります。開発物の詳細については、今後投稿される各チームのブログをご覧ください!) まずはチームAです。食べ物特化のSNS、見ているだけでお腹が減っちゃいますね。投稿機能やログイン機能なんかもしっかりと整備されています。 次にチームBの発表。最小かつ固有の数字を投票した人が勝ち、という単純そうだけど奥が深そうなゲーム。実況チャンネルも盛り上がっていました! 最後はチームCです。指定した場所の降水量をわかりやすく可視化してくれるといったサービス。インフラも充実していて、本格的なサービスですね。 多くの方にご参加いただき、素晴らしい成果報告が実施できました! ご参加いただいた皆様、ありがとうございました! 終わりに 今年のハッカソン合宿は、どのチームも非常にレベルの高い成果物ばかりでした。 使ったことのない技術なども積極的に取り入れ、運営の私たちも学びのあるハッカソン合宿となりました。 そして昨年に引き続き、快適にハッカソン合宿ができる環境を提供してくださったノジマ大磯スクウェアさん、ありがとうございました! 各チームの開発物のより詳細な仕様や開発秘話など、それぞれブログが公開されますので、お楽しみに!!
はじめに ニフティで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 にしないでください)