こんにちは、22卒エンジニアの 川本 です。 先日「 AWS JumpStart 2023 設計編」という研修に参加してきました。 2日間と短い時間でしたが、とても内容が濃くて勉強になったので、本記事では研修の内容や参加した感想を綴っていきたいと思います。 AWS JumpStartとは? AWS公式HP では以下のように書かれております。 「 AWS JumpStart 2023 設計編」は AWS 初学者のエンジニアの方々を対象とした、実践的な研修プログラムです 将来的に AWS 活用をリードする人材になるための第一歩をスムーズに踏み出せるようなプログラムをご提供します 単なる AWS サービスの学習だけでなく、要件に合わせて適切な アーキテクチャ を検討・設計する経験を積む部分にフォーカスした内容となっております 例えば、以下のような方々にもオススメです! ・ AWS の名前は知ってるが使ったことは無い ・ EC2 等の単体サービスは触ったことあるが、全体のアーキテクティングは経験がない ・ クラウド ネイティブなアプリケーションを設計する上で重要な観点を知りたい AWS 初学者向けの クラウド アーキテクチャ を検討・設計するイベントで、参加人数が600人以上いる大規模イベントでした。 プログラム内容 研修は以下の構成で行われました。 事前学習 ハンズオン グループワーク 事前学習 研修参加前に事前学習として、 AWS SAA向けの動画学習教材が送られてくるのでそちらで AWS の基本的なサービスについて学習します。 また、 AWS のサービス説明だけでなく、アーキテクティングのコツみたいな内容も盛り込まれており、研修当日に非常に役に立ちました。 私は完全に AWS 未経験だったため、こちらの教材で事前学習しておいてほんとによかったです。 ハンズオン 1日目にまずハンズオンがあり、 AWS 上にTodo管理アプリを構築するといった内容でした。 以下の図のように ALB + ECS on Fargate + Aurora MySQL の アーキテクチャ を構築しました。 ハンズオンの中で面白くて勉強になったのが、上記の アーキテクチャ でECSタスクを片方停止させたときの挙動や、Aurora MySQL のフェールオーバーを実行したときの挙動を実際に手を動かして確認したことです。 ECSタスクが片方死んでも自動復旧している様子や、フェールオーバーしたことによりAurora MySQL のリーダーとライターが切り替わる様子を確認することができました。 このような実践的なハンズオンのおかげで、可用性やスーケラビリティを意識した アーキテクチャ とはどういうことなのかというイメージがより具体的になりました。 また、ここまでの アーキテクチャ を個人で作成するとお金がかかりますが、その辺りを意識せずに AWS サービスを触ることができるのもうれしいポイントですね! グループワーク 2日目はグループで課題に沿った アーキテクチャ を構築するといった内容でした。 以下のような要件を満たす ECサイト を構築してくださいという課題が与えられました。 提供規模 利用者数:数万人 ピーク時間帯:日本時間の朝夕 提供エリア:日本 提供プラットフォーム:web システム要件 バックエンド: Java /Spring Boot(Dockerで開発中) フロントエンド:TypeScript/ReactJS データベース: MySQL 必須機能 商品一覧ページ 商品情報 商品画像 カート機能、購入機能 決済、在庫管理、配送システムは外部の SaaS API を利用するものとする アカウント管理機能 追加要件 CI/CD BI ダッシュ ボード機能 レコメンド機能 普段から BUYMA の開発をしておりECサービスには馴染みがあったのでイメージしやすかったです。 しかし BUYMA は全ての箇所で AWS 上のサービスを使っているわけではありません。 なので、 BUYMA のこの部分は AWS だとこのサービスが使えるなというような観点で アーキテクチャ を構築していきました。 このような観点で アーキテクチャ を構築することで BUYMA への理解も深まったので、私にとっては一石二鳥な課題内容でした。 最終的に構築した アーキテクチャ は以下構成図です↓ 工夫した点としては、 ECS on Fargateの構成によりコンテナのAutoScalingを実現してサーバーの可用性up RDSはMulti-AZ構成として、障害発生時にフェールオーバーしてリーダー、ライター インスタンス が切り替わるようにして可用性up ElastiCacheを使うことでデータベースクエリのレスポンスを高速化 Cloud Frontを使うことで静的コンテンツ配信の高速化 他にもCI/CDの整備やBIツールの導入、レコメンド機能への対応などを行いましたが、 AWS にはそれぞれに適したサービスが用意されており、本当にサービスの種類が豊富だなと思いました。 研修を終えた感想 参加前は AWS についてはなんとなくサービス名を知っている程度でしたが、研修で実際にハンズオンで手を動かしたり、チームで アーキテクチャ 議論をすることで AWS の全体感を掴むことができました。 また、Webアプリケーションの アーキテクチャ を設計する上で意識すべき可用性やスケーラビリティについて学べたことが大きな収穫だったかなと思います。 AWS のサービス名について知っていても、 アーキテクチャ を設計する上で重要なことがわかっていないと適切なサービスを選択することもできませんし、設計することもできないと感じました。 最後に 今回の研修を機にインフラのことについても少し興味が湧きました。 AWS SAAの資格取得なども目指していきたいです。 最後に、このような素晴らしい研修を無償で開催してくださったアマゾン ウェブ サービス ジャパン 合同会社 の皆さま、ありがとうございました! 株式会社 エニグモ すべての求人一覧 hrmos.co
こんにちは、インフラエンジニアの 加藤( @kuromitsu_ka )です。 はじめに Amazon RDSのEoS対応で、 インスタンス クラスとエンジンバージョンを変更する作業をしました。その際、 インスタンス クラスとエンジンバージョンを同時に変更しようとしたのですが即時適用されず困りました。 EoS関係のドキュメント Amazon RDS for PostgreSQL リリースカレンダー - Amazon Relational Database Service DB instance classes - Amazon Relational Database Service 解決の流れ 社内のサポートを受けて、保留になった変更を取り消す方法を教えて貰いました。保留になっていた インスタンス クラスとエンジンバージョンの変更の内、エンジンバージョンの変更を取り消したところ、 インスタンス クラスの変更が走りました。その後、エンジンバージョンも変更して、無事、メンテナンス対応できました。 わかったこと Amazon RDSの変更が保留に入ってしまった問題ですが、 AWS サポート窓口に質問したところ、既知の問題だったそうです。 Amazon RDSの インスタンス クラスとエンジンバージョンを変更する際は、 インスタンス クラスと、エンジンバージョンとで、2回に分けて変更する手順を推奨しているとのことでした。 以下、作業ログ インスタンス クラス エンジンバージョン 作業前 db.m3.xlarge 11.15 作業後 db.m5.xlarge 11.18 インスタンス クラス変更とエンジンバージョンのバージョンアップを同時に適用しようとしたら、変更が保留になってしまった。 実行したコマンド $ aws rds modify-db-instance \ --db-instance-identifier ${DB名} \ --engine-version ${バージョン} \ --db-instance-class ${インスタンスクラス} \ --no-allow-major-version-upgrade \ --apply-immediately --apply-immediately をつけているのに保留となってしまった。 "PendingModifiedValues": { "DBInstanceClass": "xxx", "EngineVersion": "xxx" }, 保留の取り消し 変更前の値を適用することで、保留状態を脱することができました $ aws rds modify-db-instance \ --db-instance-identifier ${DB名} \ --engine-version ${変更前のエンジンバージョン} \ --apply-immediately 保留されていた インスタンス タイプ変更が始まった。 よかった!! 振り返り 該当の インスタンス クラスが、エンジンバージョンをサポートしていないというわけではありませんでした。 Amazon RDS DB エンジンとインスタンスクラスでサポートされている Performance Insights - Amazon Relational Database Service 今回の問題ですが、開発環境ではうまく行って、本番環境だけ発生しました。 こちらは一度のコマンドで インスタンス クラスもエンジンバージョンも変更できた。(たまたまかもしれない。 インスタンス クラス エンジンバージョン 作業前 db.t2.micro 11.15 作業後 db.t3.micro 11.18 当日は、困りましたが、その場で社内のサポートを受けて解決できました。個人的に、 Amazon RDSの変更取り消し(と言っていいのか微妙ですが)コマンドが、裏コマンドっぽいので気に入りました。 株式会社 エニグモ すべての求人一覧 hrmos.co
~合議制 “Committee体制” の導入~ 目次 新体制の導入の背景について Committee体制の実行に向けて Committee体制の詳細について ビジネスサイドとの連携について 〈インタビュイー経歴・紹介〉 安藤 英男 COO/取締役 最高執行責任者 1997年に株式会社 電通国際情報サービス に入社。2004年株式会社 エニグモ 設立。2005年当社取締役、2010年に当社COO(取締役 最高執行責任者 )に就任。 木村 慎太郎 / エンジニア リングマ ネージャー Committee Head 2013年に エニグモ にWebアプリケーションエンジニアとして入社。 データ・ 機械学習 ・検索の基盤開発・運用を担当するエンジニアチームを立ち上げ、 BUYMA のエンジニア組織のマネージャーおよび、組織の運営方針を決定する合議体のHeadを務める。 山本 浩貴/ エンジニア リングマ ネージャー Committee Vice Head 2016年に エニグモ にWebアプリケーションエンジニアとして入社。 現在は出品者向け機能を開発するチームとフロントエンドチームのマネージャーおよび、組織の運営方針を決定する合議体のVice Headを務める。 〈インタビュアー経歴・紹介〉 大谷彰徳/ コーポレートオペレーション本部 人事総務グループ部長 2002年株式会社 博報堂 入社。アカウントプロデュース職として国内外企業のコミュニケーション施策に携わる。2015年に エニグモ に入社。コーポレートオペレーション本部/人事総務グループの部長に就任。採用、人事企画、 労務 、総務、コーポレートITの領域を統括。 新体制の導入の背景について 大谷: 今回、当社のエンジニア組織のマネジメント体制が大きくリニューアルされましたが、その背景や経緯についてCOOの安藤さん、エンジニア リングマ ネージャー(以下EM)の木村さん・山本さんにお話をお伺いできればと思います。 まずは本題に入る前に安藤さんにお伺いしたいのですが、COOとしてどのようなことを意識して、日頃からエンジニアの組織やメンバーと関わっていらっしゃいますか。 安藤: エンジニア以外にも当てはまることですが、特に意識している部分は、まず「人」の部分になります。 どのメンバーに重要なロール(役割)を担ってもらうかの判断はとても大事なため、そこはしっかりと自分なりの確信を持てる状態にしたいと思っています。 技術的な部分は基本的に自分が戦略を立てるわけではありませんが、会社レベルで決めるべき重要なことは、技術的な領域でも重要なロールを担うメンバーとディスカッションした際に、その良し悪しを自信をもって判断できる状態にしています。 大谷: 「どういう人にどのようなロールを担ってもらうのか」という判断は、まさに組織作りにも大きく関わる部分ですが、今回その組織作りの取組みのひとつとして実施された 「Committee体制」 の導入についてお聞きしたいと思います。 今回、 エンジニア組織をCTOや部長を頂点とするワントップ型ではなく、EM陣で構成する「Committee」という合議体を意思決定のトップ機関として新設し、そのHead、Vice Headを1年交代の輪番制で運営する という体制への移行しましたが、その導入背景を教えてください。 安藤: これまで エニグモ はエンジニア組織に関わらず、会社全体として少数精鋭の筋肉質な組織で成長してきました。その為、当社の技術志向の高いエンジニア達には過度なマネジメント負荷をかけずに開発に専念してもらいたいという思いがありました。それがフラットな組織で フルスタ ックに自分のスキルを発揮できる環境に繋がり、そこを魅力に感じるエンジニアが自然と集まっていました。 そして上場から10年が経ち、技術領域も多岐に広がり、組織やサービスも格段に大きくなり、今までのやり方で組織を最適にマネジメントすることが難しくなってきました。 それまでは経営陣もエンジニアにはマネジメントの負荷をなるべくかけないようにと考えていましたが、当社らしいエンジニア流のマネジメント体制への進化が必要なフェーズになりました。 そしてEMたちからも「組織的な課題を解決したい」「こういう組織にしていきたい」という前向きな意見が自然と出てくるようになりました。そのような中で、これまでの エニグモ のエンジニア組織のカルチャーを大切にしながら最適な方向を検討する中でCommitteeというコンセプトが思い浮かびました。 大谷: 新体制の中でも特徴的な 「EM陣による合議制」 のコンセプトや 「CommitteeのHeadとVice Headを1年交代の輪番制」 にしようと考えた背景はなんでしょうか? 安藤: 社内のエンジニアで組織のトップの経験がないメンバーでも、ハードルが高くならずチャレンジしやすいようにしたかったのが一つです。また、属人化させず色々なメンバーの意見を取り入れて多面的に進化していく組織体制にしたいと考えました。 属人化しすぎないためにトップが行う業務を分解して「ロール(役割)」という抽象的な言葉で定義し、それをいろいろな人の手で育てていくような体制であっても良いのではないかと思いました。重要な判断をともなうロールなどにおいても、それをサポートする他のCommitteeメンバーも次回自分がやる可能性がある状況に置くことで、「自分だったらどうするか」という視点になり、自分ごと化して業務に取り込むことができます。そういった状態が組織として健全ではないかと感じ、輪番制を取り入れました。 さらに、マネージャーやトップの業務も1つのロールであると考えています。 優秀なエンジニアが エニグモ で長く活躍する際に、マネージャーとしてパフォーマンスが高かった人でも、自身がチャレンジしたいことに応じて自発的に スペシャ リスト(技術者)に戻るというキャリアがあっても良いと考えています。 マネージャーになること=唯一のキャリアステップアップにしたくない と思っています。 マネージャーはあくまで組織の中でマネー ジャーロ ールを担っている人という形で定義することで、上下という立ち位置にせず、ある時はマネージャーをやっていたが、ある時は スペシャ リストである。らせん階段を登っていくようなキャリアの選択があっても面白いのではないか、それで組織全体にプラスになることもあり、キャリアアップの多様化にも繋がると考えます。 色々な観点でエンジニアが主体的に自らの組織や専門性を成長させていける体制になることを目指したいと考えたのが背景です。 Committee体制の実行に向けて 大谷: 続いて、木村さんと山本さんにお聞きしたいのですが、部長やCTOを置かないフラットな組織運営であるCommittee体制について、初めて方針を聞いた時にどう感じましたか? 木村: 私も、エンジニア組織全体について「自分的にはこうした方がいい」みたいなところは考えていて、実際に話し始めていたタイミングでした。 フラットな組織体制については、他社でトップにCTOがいる場合でも、CTOを補佐するメンバーがたくさんいるCTO室を設置するような会社もたくさんあります。そういったトップを補佐する仕組みが必要であるというのは理解していました。そういったところから安藤さんから提案についても「いいな」と思いました。輪番制についても、Headになることへの 心理的 ハードルが下がりますし、自分に就任を打診された際もポジティブに受け止めることができました。 山本: 自分も今回の話がある前から、組織的にもう少しやった方が良い部分があるなと感じていました。他社の体制や取り組みを調べてみて、 エニグモ にはどんなやり方が最適なのかを考えていたので、Committee体制についても前向きにとらえました。 ただ、初めは部長やCTOという分かりやすいトップがいないことについては少し不安もありました。 でも実際にCommitteeが発足したおかげで、組織作りについてEM同士で定期的かつ積極的に会話する場ができたことで、今まで以上に組織がクイックに良い方向に向かい始めたことをすぐに実感できました。 Committee体制の詳細について 大谷: 木村さんが初代Headに決まってから、さらに具体的なCommittee運営に向けた体制づくりに着手されましたが、最終的な実施体制について概略を教えていただけますか。 木村: 繰り返しになりますが、新体制はCTO、VPoEという役職を置かず、複数名のEMがCommitteeという合議体を形成して、意思決定のトップ機関となっています。トップは輪番制となります。優先順位が高い組織課題は分科会を配下に設置して議論・決定のスピードを上げていきます。 Committeeが発足してまず行ったことは、トップが単独で担っていたロール・タスクを分解・抽象化していって組織に落とし仕込み、Committeeメンバーの誰が担当するかを決めていきました。 その際に現在足りない役割や、新たに採用すべきポジションなども同時に整理しました。 山本: エンジニア組織のトップがやるべき仕事は非常に多岐に渡ります。 トップの役割は、チームのマネジメント(ピープル〜プロジェクトマネジメント)や技術的意思決定、採用・評価に止まらず、システム監査、インシデント指揮、予算・経費管理といった様々な役割があります。技術職から見ると少し敬遠したくなる"雑務”と感じる業務もトップに集まりがちになるので、そういう業務も含めて、誰が担当するか?今後どのように管理・意思決定するのか?をCommitteeメンバー内で前向きに議論しながら決めることができました。 ビジネスサイドとの連携について 大谷: エンジニアとビジネスサイドが密に連携して業務を推進する当社においては、今回の体制変更はビジネスサイドの特にマネジメント陣にしっかりと理解してもらう必要がありますよね。どのような形でインプットを行ったか教えてください。 木村: はい。今回の組織体制はエンジニアだけでなく、ビジネスサイドにもリンクすることがとても重要だと考えました。経営陣からも、せっかくなのでオフィスから離れた場所で1日ワークショップ形式で時間を取って共有とディスカッションをしようよ、と提案いただいたのはありがたかったです。 安藤: エニグモ はエンジニアとビジネス側、企画メンバーが一緒になってプロダクト、サービスを創り上げていくことを大切にしている風土です。 今回はエンジニア組織のターニングポイントとなったので、ビジネス側の部長や、エンジニアと密に仕事をしているメンバーとは事前にキックオフをし、よく理解してもらい、もし疑問があれば解消しておく必要があると考えました。 やはり、エンジニアとビジネスサイドのメンバーが境界なく同じような価値観で同じような背景を理解した上で、協力・連携して仕事をすることはすごく大切なことです。 木村: 今回のキックオフを通して、エンジニア組織の話にとどまらず、開発側とビジネス側のさらなる連携強化についての議論にも発展し、例えば、全社横断的な重要プロジェクトの推進方針(カンパニーベットの概念)についても自然と議論が湧き起こったりして、非常に有意義な日になりました。 キックオフ終了後、そのビルの屋上でみんなでBBQをやったのもいい思い出です(笑) 今回の記事では新体制(Committee体制)の移行についてお話ししました。後半は新たな開発体制についてお話しします。後半もお楽しみに!
こんにちは、インフラエンジニアの 高山 です。 この記事は Enigmo Advent Calendar 2022 の25日目の記事です。 はじめに エニグモ 入社後に AWS を徐々に触れ始め 数年経過しました。 最初はWebコンソールからポチポチしていたのですが、CloudFormationでコード化を進めたり AWSCLIや SDK を使うようになり、最近は Ruby で AWS リソースの情報取得や操作をすることが多くなってきました。 (以下、 RubyでAWSリソースの情報取得や操作をする ことを AWSをRubyで操作する と表現します) AWS を Ruby で操作するようになった理由は以下です。 そもそもWebコンソールは苦手(というより怖い)で、 CLI で操作したい派である 構造的なデータを扱うのは Shell スクリプト だと厳しい(jqが嫌い) Ruby on Rails (以下、 Rails と表記します)を使ったことがあるので、 Ruby に少しは慣れている BUYMA では Rails を採用しているので、社内で聞ける AWS を操作するには python (+boto3)を使っているユーザの方が圧倒的に多そうな気がしますが、 python と比較しているわけではないので、片手落ち感は否めない記事になってます。 相性が良いと思う理由 AWS と Ruby は相性が良いと思う理由、それはデータの シリアライズ が簡単にできるからです。 この一点に尽きるので、ここで記事終了でも良いくらい。 シリアライズ とは シリアライズ というのは データやオブジェクトをファイルに書き込みできるように変換することです。 反対はデ シリアライズ 。 言いにくいですね。 やってみよう シリアライズ は yaml ライブラリを使い、オブジェクトを yaml 化して、ファイルに保存するだけです。 $ export AWS_REGION = " ap-northeast-1 " $ pry [ 1 ] pry(main)> require ' aws-sdk-ec2 ' => true [ 2 ] pry(main)> require ' yaml ' => true [ 3 ] pry(main)> ec2_client = Aws :: EC2 :: Client .new(); [ 4 ] pry(main)> ec2_data_list = ec2_client.describe_instances(); [ 5 ] pry(main)> File .open( " ec2_data_list.yml " , ' w ' ){|h| h.write ec2_data_list.to_yaml} => 896272 [ 6 ] pry(main)> quit $ ls -l ec2_data_list.yml -rw-rw-r-- 1 ec2-user ec2-user 896272 Dec 21 15 : 49 ec2_data_list.yml デ シリアライズ も同じように yaml ライブラリを使ってloadするだけです ※ aws-sdk-ec2 を require する必要はあります $ pry [ 1 ] pry(main)> require ' aws-sdk-ec2 ' => true [ 2 ] pry(main)> require ' yaml ' => true [ 3 ] pry(main)> ec2_data_list = YAML .load_file( " ec2_data_list.yml " ); [ 4 ] pry(main)> p ec2_data_list.reservations.count; 149 簡単ですね。 シリアライズ できることの何が嬉しいのか 簡単に シリアライズ できることは わかっていただけたと思いますが、それの何が嬉しいのか? データ構造を そのままファイルにして、それを読み込むことができるので 以下を分離することができます。 時間がかかるデータ取得の処理 いろいろ試行錯誤したいデータ整形の処理 👆が嬉しいことなのですが、伝わりませんね 実際にやってみましょう。 スクリプト の例 データを取得する スクリプト まずデータを取得する スクリプト です。 EC2 インスタンス が多い場合は next_token が nil になるまでループする必要があります。 (データが少なければ不要です) ここは できるだけフィルターした方が良いですが、今回は検証なのでしていません。 データ取得は数秒程度ですが、検証のたびに取得し直すは地味にストレスですよね。 $ cat get_all_ec2_data.rb #!/usr/bin/env ruby require ' aws-sdk-ec2 ' require ' yaml ' ec2_client = Aws :: EC2 :: Client .new() all_ec2_data_list = [] token = nil loop do ec2_data_list = ec2_client.describe_instances({ next_token : token}) token = ec2_data_list.next_token all_ec2_data_list += ec2_data_list.reservations break if token.nil? end File .open( " all_ec2_data_list.yml " , ' w ' ){|h| h.write all_ec2_data_list.to_yaml} インタラクティブ シェル(pry)で確認 次はデータ構造や、データをどうやって整形するか確認しましょう。 以下は Nameタグの取得方法を確認した時のログです。 [ 1 ] pry(main)> require ' aws-sdk-ec2 ' => true [ 2 ] pry(main)> require ' yaml ' => true [ 3 ] pry(main)> [ 4 ] pry(main)> all_ec2_data_list = YAML .load_file( " all_ec2_data_list.yml " ); [ 5 ] pry(main)> all_ec2_data_list[ 6 ].instances.first.tags => #<struct Aws::EC2::Types::Tag key="Name", value="Test-BM-Rails-19">, #<struct Aws::EC2::Types::Tag key="role", value="Railsweb">, #<struct Aws::EC2::Types::Tag key="Service", value="Buyma">, #<struct Aws::EC2::Types::Tag key="CreateUser", value="takayama">, [ 6 ] pry(main)> all_ec2_data_list[ 6 ].instances.first.tags.class => Array [ 7 ] pry(main)> all_ec2_data_list[ 6 ].instances.first.tags.find{|tag|tag[ " key " ]== " Name " } => #<struct Aws::EC2::Types::Tag key="Name", value="Test-BM-Rails-19"> [ 8 ] pry(main)> all_ec2_data_list[ 6 ].instances.first.tags.find{|tag|tag[ " key " ]== " Name " }[ " value " ] => " Test-BM-Rails-19 " 各EC2 インスタンス のena_supportが有効になっているどうかをチェックする スクリプト 各EC2 インスタンス の ena_support が有効になっているどうか チェックする スクリプト を書いてみました。 (例として良いものが思いつかなかったです。。) $ cat check_all_ec2_ena_support.rb #!/usr/bin/env ruby require ' aws-sdk-ec2 ' require ' yaml ' all_ec2_data_list = YAML .load_file( " all_ec2_data_list.yml " ) all_ec2_data_list.each do |ec2| name = ec2.instances.first.tags.find{|tag|tag[ " key " ]== " Name " }[ " value " ] puts "#{ name } : #{ ec2.instances.first.ena_support }" end 以下のような感じで出力されます。 $ ./check_all_ec2_ena_support.rb | head -5 Test-Railsweb-Server : true Test-Phpweb-Server : true Test-EC2-Instance : false Test-Airflow-Server : false Test-Redash-Server : false EC2のタグだけ取ってファイルに保存する スクリプト 次は EC2のタグだけ取ってファイルに保存する スクリプト を作成してみました。 $ cat check_all_ec2_tags.rb #!/usr/bin/env ruby require ' aws-sdk-ec2 ' require ' yaml ' def convert_tags_to_hash (tags) hash_tags = {} tags.each{|e| hash_tags[e[ " key " ]] = e[ " value " ]} hash_tags end all_ec2_data_list = YAML .load_file( " all_ec2_data_list.yml " ) all_ec2_tag_info_list = {} all_ec2_data_list.each do |ec2| tags = convert_tags_to_hash(ec2.instances.first.tags) all_ec2_tag_info_list[tags[ " Name " ]] = tags end File .open( " all_ec2_tag_info_list.yml " , ' w ' ){|h| h.write all_ec2_tag_info_list.to_yaml} 実行してみます。 $ ./check_all_ec2_tags.rb $ head all_ec2_tag_info_list.yml --- Test-Railsweb-Server : Name : Test-Railsweb-Server role : Railsweb CreateUser : takayama Test-Phpweb-Server : Name : Test-Phpweb-Server role : Phpweb CreateUser : takayama Test-EC2-Instance : yaml になっていると、可読性が高いところもメリットです! いったん、結論 ということで、 シリアライズ の簡単さと メリットがわかっていただけたのではないかと思います。 メリットまとめ そのままのデータ構造で保存できる インタラクティブ シェルでの確認や検証がやりやすい データの取得とデータ整形などの処理を分けやすい yaml で保存するので 可読性が高い Ruby での認証情報の取得 これまでの検証では わかりやすくするため Read権限のあるEC2上で実行していたので 認証情報の取得は不要でしたが、PC上など他の場所で AWS の SDK を使用する場合は 認証情報の取得が必要です。 $ ls -l ~/.aws/cli/cache/ total 24 -rw------- 1 takayama staff 1421 12 8 20:16 3a9ab49ce67fe5806c5af3d5a378fbbb470561d9.json -rw------- 1 takayama staff 1445 10 24 15:24 65d08609cd764dc442d836e7665fbbd663992aea.json -rw------- 1 takayama staff 1433 8 25 16:07 6c11869d293d59bf5a50ceef707e37b36524415d.json AWSCLIだと、一時認証情報が保存され再利用できますが、 Ruby の SDK を使って スクリプト を作っている場合は再利用できず、 スクリプト を実行するたびに認証情報を取得し直さないといけないのが難点でした。 しかし、それも シリアライズ で解決できます。 さすが シリアライズ さん! 以下のような感じです。 role_credentials = Aws :: AssumeRoleCredentials .new( client : sts_client, ...) role_credentials.client.config.retry_backoff = nil role_credentials.client.config.defaults_mode_config_resolver = nil File .open( " role_credentials.yml " , ' w ' ){|h| h.write role_credentials.to_yaml} そのまま保存すると読み込む時にエラーになるので 一部の不要なデータを削除してから保存する必要がありました。 aws-sdk-core のバージョンによって削除する必要のあるデータが変わるようです。 古いバージョンだと retry_backoff のみ削除で大丈夫でしたが 最新バージョンだと defaults_mode_config_resolver も削除する必要がありました。 削除しても動作には問題なさそうでした。 認証周りの機能はクラスにして それぞれの スクリプト で簡単に使えるようにしているのですが、 AWSCLIも併用して使う場合 認証情報の取得が2回になってしまう問題があります。 AWSCLIを併用する場合の認証 ということで、AWSCLIの一時認証情報ファイルを Ruby でも使う方法を考えました。 簡単に書くと、以下のような感じで 一時認証情報ファイル( json )をパースして 環境変数 として設定するだけです。 jq嫌いだけど、このくらいはね うん。 awscli_credentials_cache=~/.aws/cli/cache/xxxxx.json export AWS_ACCESS_KEY_ID= $( jq -r ".Credentials.AccessKeyId" < $awscli_credentials_cache) export AWS_SECRET_ACCESS_KEY= $( jq -r ".Credentials.SecretAccessKey" < $awscli_credentials_cache) export AWS_SESSION_TOKEN= $( jq -r ".Credentials.SessionToken" < $awscli_credentials_cache) export AWS_REGION=ap-northeast-1 実際に試してみましょう $ awscli_credentials_cache=~/.aws/cli/cache/3a9ab49ce67fe5806c5af3d5a378fbbb470561d9.json $ export AWS_ACCESS_KEY_ID= $( jq -r ".Credentials.AccessKeyId" < $awscli_credentials_cache) $ export AWS_SECRET_ACCESS_KEY= $( jq -r ".Credentials.SecretAccessKey" < $awscli_credentials_cache) $ export AWS_SESSION_TOKEN= $( jq -r ".Credentials.SessionToken" < $awscli_credentials_cache) $ export AWS_REGION=ap-northeast-1 $ pry [ 1 ] pry(main) > require 'aws-sdk-ec2' = > true [ 2 ] pry(main) > ec2_client = Aws::EC2::Client.new(); = > #<Aws::EC2::Client> 新たに認証情報を取得せずに実行できました。 スクリプト スクリプト にしたものはこちらです。( mac を使っているので、dataがgdateになっています) スクリプト(長いので、折り畳んでおく) $ cat create_set_env.sh #!/bin/bash help () { echo -e " \n --- usage $0 <awd_profile_name> \n " exit } get_jq() { jq -r " $2 " < $awscli_credentials_cache } mk_credential_file() { rm -f ~/.aws/cli/cache/* aws sts get-caller-identity --profile $1 > /dev/null awscli_credentials_cache= $( ls -1tr ~/.aws/cli/cache/* | tail -1 ) rm -f $credential_file echo "export AWS_ACCESS_KEY_ID= $( get_jq .Credentials.AccessKeyId ) " >> $credential_file echo "export AWS_SECRET_ACCESS_KEY= $( get_jq .Credentials.SecretAccessKey ) " >> $credential_file echo "export AWS_SESSION_TOKEN= $( get_jq .Credentials.SessionToken ) " >> $credential_file echo "AWS_Expiration= $( get_jq .Credentials.Expiration ) " >> $credential_file echo "export AWS_REGION=ap-northeast-1" >> $credential_file echo -e " \n --- creation of $credential_file was completed!!" echo -e " please exec fllow command" echo -e " source ./ $credential_file \n " exit } check_expiration_time() { local expiration_time= $( awk -F "=" '$0 ~ /AWS_Expiration/{print $NF}' $credential_file) local expiration_unixtime= $( gdate -d " $expiration_time " "+%s" ) local current_unixtime= $( gdate "+%s" ) local diff_time= $(( expiration_unixtime - current_unixtime - 60 )) [ 0 -lt $diff_time ] && echo ok } [ $# -ne 1 ] && help credential_file=set_env.sh [ ! -f $credential_file ] && mk_credential_file $1 [ " $( check_expiration_time ) " != "ok" ] && mk_credential_file $1 echo -e " \n --- no need to update !!" echo -e " please exec fllow command" echo -e " source ./ $credential_file \n " $ ./create_set_env.sh < awd_profile_name > Enter MFA code for arn:aws:iam:: 123456789012 :mfa/takayama: --- creation of set_env.sh was completed!! please exec fllow command source ./set_env.sh $ source ./set_env.sh $ pry [ 1 ] pry(main) > require 'aws-sdk-ec2' = > true [ 2 ] pry(main) > ec2_client = Aws::EC2::Client.new() = > #<Aws::EC2::Client> 有効期限が1分以内に切れる場合は認証情報を再取得するようにしてあります。 テキトーに作った スクリプト で 一時認証情報のファイルを全消ししているのがイケてないですが、参考にしてみてください。 この方法は Ruby スクリプト 以外でも、 AWS の 環境変数 で認証情報取得できる機能なら使えます。 例えば Serverless Framework とか、 Terraform とか (最近 触っていないので、 確認できてませんが) 実際 実際には クラスやモジュールを作成してDRYな感じで スクリプト を作っています。 また、例えば 以下のような機能で Ruby を使用しています。 CloudFormationスタック作成や変更セット作成、変更セットの確認 ELBへのインスタスの組み込み、切り離し セキュリティグループへの追加更新用Webアプリ 各種調査 etc この中から2つ紹介します。 変更セットの確認機能 CloudFormationスタックの変更セットは 以下を確認してから適応するようにしています。 テンプレートの差分 パラメータの変更 タグの変更 リソースが作り直されるか 以下が確認時の内容です。 $ ./scripts/check_change_set.sh Lb-Instance-01.yml === Stack Name: Test-Lb-Instance-01 === Diff Check ================================================================ --- [Info]: Lb-Instance-01.yml Template Difference Exist !!! Instance: Instance: InstanceType: { Type: String } InstanceType: { Type: String } Role: { Type: String } Role: { Type: String } Instance: Instance: Type: AWS::EC2::Instance Type: AWS::EC2::Instance > - { Key: addtag, Value: dummy } - | - | RecordSet: RecordSet: Type: AWS::Route53::RecordSet Type: AWS::Route53::RecordSet === Change-Set Check ================================================================ --- - :logical_resource_id: Instance ------------------------- :resource_type: AWS::EC2::Instance :action: Modify :replacement: Conditional !!!!!!!!!!!!!!!!!!!!!!!!! :details: - :attribute: Tags :name: :change_source: - :attribute: Properties :name: InstanceType :change_source: ParameterReference - :attribute: Properties :name: InstanceType :change_source: DirectModification - :logical_resource_id: RecordSet ------------------------- :resource_type: AWS::Route53::RecordSet :action: Modify :replacement: False :details: - :attribute: Properties :name: ResourceRecords :change_source: ResourceAttribute === Change-Set Params Check ================================================================ NAME | CURRENT | NEW | STATUS -------------|----------|-----------|---------- InstanceType | t3.small | t3.medium | change Role | lb | lb | no change === Change-Set Tags Check ================================================================ NAME | CURRENT | NEW | STATUS -----------|---------------------|---------------------|---------- Name | Test-Lb-Instance-01 | Test-Lb-Instance-01 | no change Role | lb | lb | no change CreateUser | dummy | takayama | change === Validation Check ================================================================ --- [Info]: Lb-Instance-01.yml Validation Check OK === Lint Check ====================================================================== --- [Info]: Lb-Instance-01.yml Lint Check OK 補足 Shell スクリプト を実行してますが、複数のShell スクリプト と Ruby スクリプト をラッピングしています。 テンプレートの差分は 差分と項目名のみ抽出しているのでわかりづらいですが、今回は - { Key: addtag, Value: dummy } の行だけが追加になったという結果です。 セキュリティグループへの追加更新用Webアプリ こちらはWebアプリなのですが、リモートワークが始まった時に 簡易的にセキュリティグループへ各メンバーの IPアドレス を追加したいと思って作成した機能です。 AWS を Ruby で操作することに慣れていたので、 Rails アプリも わりと簡単に作成することができました。 (この機能自体は とりあえずで作成したものです。 最近 他の施策で ほぼ不要になり 役目を終えました。) セキュリティグループへの追加更新用Webアプリ 最後に 今回は Ruby の スクリプト の話をしました。 Ruby の スクリプト って あまり書いている人がいないイメージがあるのですが、けっこう書きやすいのではないかと思います。 AWS で情報取得すると複雑なデータ構造で返ってくることが多いので シリアライズ が簡単にできる Ruby も選択肢に入れても良いかなと思いました。 ただ インフラエンジニアでは さらに書いている人が少ないと思うので、可読性に気を使ったり コメントで説明をしっかり書いたり 他の人の迷惑にならないようにした方が良さそうだなとは思います。(自戒を込めて) どなたかの参考になれれば幸いです。 こちらで Enigmo Advent Calendar 2022 は以上となります。 2023年もよろしくお願いします!
この記事は Enigmo Advent Calendar 2022 の23日目の記事です。 こんにちは、 エニグモ 嘉松です。 データ活用推進室というチームでリーダーをさせていただいています。 チームにはデータアナリスト3名、社内の業務システムを開発するGASエンジニア1名、そして私の計5名が所属しています。 目次 目次 エニグモを取り巻くIT環境 AppSheetとは AppSheetによるアプリの開発 AppSheet開発のフロー 1. データを用意 2. データを取り込み 3. アプリを作成 4. アプリをカスタマイズ 5. アプリを公開(デプロイ) 6. アプリをシェア AppSheet 触ってみた感想 良かった点 今回の検証では分からなかった点 不満・不安に思った点 最後に エニグモ を取り巻くIT環境 エニグモ では提供しているサービスが BUYMA という海外通販サイト( ECサイト )ということもあり、日常的な業務において多種多様なシステム、ITサービス、ツールを活用しています。 使用しているシステムを分類すると、 BUYMA の管理画面 BUYMA の業務を行う中心的なシステムです。 しっかりと要件を詰めた上でエンジニアによって開発されます。 アプリケーションソフト(ツール) ソフトウェア・ベンダーが提供しているツールやフリーで使用できるツール 例えば以下のようなツールがそれにあたります。 Gmail や Outlook などのメールソフト Google Spreadsheetや Excel などの 表計算 ソフト Google SlidesやPower Pointなどのプレゼンテーションツール 秀丸 やCodEditoerなどの テキストエディタ desknet’s や サイボウズ などの グループウェア Google Analytics やRTmetricsなどの アクセス解析 ツール LookerやTableau、RedashなどのBIツール など、など、など。 最近はブラウザ上で利用できるツールが多いですね。 (ちなみに上にあげたツール、私は謎に全部使ったことあります。年の功か?) お助けツール 管理画面ではカバーできず、アプリケーションソフトだけでもカバーできない領域。 人力で行っていた繰り返し作業を自動化(ボタンひとつで実行)するツールや、複数のアプリケーションソフトを組み合わせて行っていた作業を統合するツールなど。 代表的のものとしては Excel と VBA を組み合わせた業務効率化ツール。 サービスの改善や運用・保守で忙しいエンジニアに開発をお願いすることができず、現場のITに詳しいメンバーがネットで調べながら開発することも多い。(私がこれまで所属していた企業や一般論として。) ツールを使わず人力で行っていることも多く、その業務はとても生産性が低い。(日本の 労働生産性 が OECD 37加盟国中21位(2020年度)と低迷している1つの要因かとも密かに思っています。) ということで(?) エニグモ でもこの手のお助けツールの開発に力を入れています。 Google Spreadsheet + GASのように、 Google Workspace(旧 G Suite)や Google Cloud( GCP )といった Google サービスを上手いこと組み合わせてお助けツールを開発することが多いです。 Google サービスを活用する理由としては、 多くの機能(サービス)が揃っていること(下記に記載) それらが安価にかつ素早く(サーバの構築などが必要なく)利用できること そして各機能を Google Apps Scriptで連携することである程度のシステムを短期間に開発できること が挙げられます。 主な Google サービス Gmail (メール) Google Drive (ストレージ) Google Spreadsheet( 表計算 ) Google カレンダー(スケジュール管理) Google Form(アンケート) Google Map(地図) Google Apps Script( プログラミング言語 ) BigQuery(データベース(DWH)) ただ、これらの Google サービスを組み合わせたときに課題になるのがデータの入力です。 多くの場合は Google Spreadsheetにデータを入力しますが、 Google Spraeadsheetは自由すぎるので、行や列の追加や削除が容易に出来てしまいます。プログラム( Google Apps Script)ではどの列(例えばC列)には何の値(例えば商品ID)が入っている、データは何行目から入力される、といったことが決まっていることが前提として作られているので、自由に(勝手に)行や列を追加、削除されるとエラーになったりします。 もちろん、セルの保護(変更できないようにする)やシートの保護などで対応は出来なくはありませんが、Webアプリのように美しく入力を制御することが難しい。更に言えば、入力のチェック(バリデーション・チェック)も出来たら理想です。 ということで(??)目をつけたのがGoogle AppSheet。 前置きがとても長くなりましたが、以下ではGoogle AppSheetについてざっと紹介します。 AppSheetとは さっそく Wiki で調べてみると、 AppSheet is an application that provides a no-code development platform for application software, which allows users to create mobile, tablet , and web applications using data sources like Google Drive , DropBox , Office 365, and other cloud-based spreadsheet and database platforms. The platform can be utilized for a broad set of business use cases including project management, customer relationship management, field inspections, and personalized reporting. これを Google Translate(翻訳)で日本語に訳すと、 AppSheet は、アプリケーション ソフトウェア用のノーコード開発プラットフォームを提供するアプリケーションです。これにより、ユーザーは、 Google ドライブ 、 DropBox 、Office 365、およびその他の クラウド ベースの スプレッドシート およびデータベース プラットフォームなどのデータ ソースを使用して、モバイル、 タブレット 、および Web アプリケーションを作成できます。このプラットフォームは、プロジェクト管理、顧客関係管理、現場検査、パーソナライズされたレポートなど、幅広いビジネス ユース ケースに利用できます。 だいたい意味がわかる翻訳になりますね。(優秀!!) 要約すると、 ノーコードでアプリを開発できるプラットフォーム 「ノーコード」とは、コードを書かなくて良い、プログラミングが不要、ってことです。(一応補足) 多くのデータソースに対応( スプレッドシート やデータベースなど) マルチプラットフォーム (モバイル、 タブレット 、 Webブラウザ で使用できる) また、 AppSheet was acquired by Google in January 2020. というように、2020年に Google に買収され、 Google Cloudに組み込まれました。 「ノーコードでアプリを開発」といっても、どんなアプルでも作れる訳ではないでしょう。いかんせんノーコードですから。 では実際にアプリを作りながらAppSheetでの アプリ開発 について見ていきましょう。 AppSheetによるアプリの開発 まず、AppSheetの開発にはAppSheetの画面をブラウザで開く必要があります。 今回は以下のページから「無料トライアル」で試していきます。 cloud.google.com AppSheet TOPページ 無料トライアルなので出来ることに制限があります。 また、無料トライアルならではの設定が必要になります。 ただ、ざっくりと使用感を試すには十分かと思います。 AppSheet開発のフロー AppSheetでアプリを開発する流れは以下のとおりです。 データを用意 データを取り込み アプリを作成 アプリをカスタマイズ アプリを公開(デプロイ) アプリをシェア 1. データを用意 AppSheetはAppSheet自体にデータを保持するわけではなく、以下のようなアプリをデータの保存先(データ−ソース)として動作します。 (有料版ではAppSheet自体にデータを保持できるようになったようです) Google Sheets( Google スプレッドシート の正式名称ですね。) Microsoft Excel (Office 365や Dropbox など クラウド に保存されているファイルが対象) データーベース( Microsoft SQL Server 、 MySQL 、 PostgreSQL など) 今回は Google Sheetsに以下のようなデータを用意して進めていこうと思います。 id name category brand sale_start_date url pic データを用意 2. データを取り込み AppSheetの画面から用意したデータを取り込みます。 データをAppSheetに取り込み Google SheetsのデータがAppSheetに取り込まれました。 データが取り込まれました。 3. アプリを作成 取り込んだデータを指定してアプリを作成します。 アプリを作成 これで(これだけで)自動的にアプリが作成されます。 右に写っているのが スマホ 版のアプリのプレビュー画面です。 アプリが完成 4. アプリをカスタマイズ 自動で作成されたアプリをカスタマイズします。 取り込まれたデータのデータ型を変更します。 sale_start_date を Data に、 url を Url に、 pic を Image に変更してあげます。 項目のカスタマイズ プレビュー画面では、 sale_start_date が年月日の形式に、 url に右側にリンクボタンが、 pic が画像に変更されました。 項目のデータ方を変更 id の EDITABLE のチェックを外して変更を出来なくします。 idを変更不可に 5. アプリを公開(デプロイ) カスタマイズしたアプリを公開(デプロイ)、つまり利用できるようにします。 Not Deployed デプロイできるかをチェックしています。 ここでエラーがでたらエラーを修正してあげます。 無料トライアルでは、利用制限があるので、いくつかの設定を変更する必要があります。 デプロイチェック 6. アプリをシェア デプロイが無事に完了すると、利用するためのURLを取得できます。 LINK Editor Link このアプリを修正する画面へのURLです。 Browser Link Webブラウザ で利用するときのURLです。 Install Link スマホ で利用するときのURLです。 スマホ に Install Link を転送してクリックすると、最初(まだ「AppSheet」アプリを スマホ にインストールしていない場合)は、ストアに遷移するので「AppSheet」アプリをインストールします。 ちゃんと スマホ でも表示されました。 スマホ 画面 スマホ でデータを変更したら、データソースの スプレッドシート のデータも正しく変更されていました。 AppSheet 触ってみた感想 今回さらっと(約5時間くらい)AppSheetを使ってみました。 その中で感じた良い点と不満・不安に思った点、また今回の検証では分からなかった点を以下にあげます。 良かった点 スプレッドシート にデータを用意して読み込むだけでテータ定義が自動で作成される テーブル定義を1から設定する必要がないので、その手間が省ける 取り込んだデータの型は Image や Url など多くが用意されており、また変更も容易 データの型に応じて表示が自動で変わるので、UIの作成が必要ない データの参照、変更、追加の機能が自動で作成されるので、これも便利。 今回の検証では分からなかった点 データの定義やデータの表示については、多くのパラメータ(設定できる項目)があったが、どのような設定ができるのかまでは分からなかった。 デプロイの設定も多くの設定項目があったが、どのようなカスタマイズが可能なのかは分からなかった。 不満・不安に思った点 AppSheetは Google WorkspaceのEnterpriseプランで利用ができるが、Businessプラン(Business Starter、Business Standard、Business Plus)では利用できない。 Enterpriseプランを契約しておらず、AppSheetを利用する場合は、AppSheet個別の契約が必要となる。 MOST POPULAR のCoreプランで $10 USD/ user / month となかなかのお値段。 AppSheetの画面、ドキュメントが英語。 ドキュメントは Google Translateを利用すればある程度は解決するが、ハマッたときに困りそうな気もする。 Google がAppSheetにどこまでコミットしていくか不安。 2021年にサービス終了したApp Makerのような末路を辿らないか不安。 最後に では、AppSheetは「 Google サービスを組み合わせたときに課題になるのがデータの入力」を解決できるのか? 正直、出来ると思います。 AppSheetでアプリを作成して、その使い方を含めた業務マニュアルを用意すれば業務は回ると思います。 アプリを通して スプレッドシート のデータを触らせる(変更する)ことで、データの保護も出来るので、オペミスによるデータの消失なども防げると思います。 ネックはライセンスですね。多くの企業が契約している(であろう)Businessプランで利用できるようにしてほしい。 そうすればより多くのユーザが増えて、マニュアルの日本語化なども進むのではないかと思いました。 以上、読んでいただきありがとうございました。 株式会社 エニグモ すべての求人一覧 hrmos.co
こんにちは、 エニグモ でデータアナリストをしている井原です。 この記事は Enigmo Advent Calendar 2022 の22日目の記事です。 本記事の概要 この記事は、データ分析案件の進め方に関して、課題の選定からゴールの具体化、分析完了に至るまで、私が今年実際に対応した事例でどのようなことに気をつけながら対応したのか、振り返ってみることを趣旨とした記事になります。 データ分析の案件は必ずしも具体的な目的やゴールがある時ばかりとは限りません。 漠然とした課題意識から始まり、 ヒアリ ングを重ねて具体的な目的やゴールを明確化していくプロセスが入ることがあります。 ことによると、初めから目的やゴールが明確化されている案件の方がレアケースかもしれません。 この分析以外のプロセスの進め方について、分析そのものと同等、あるいはそれ以上に難しいと感じることが多々あります。 しかし、分析の手法や アルゴリズム の解説記事などはよく見かける一方、プロセスの進め方についてはなかなか具体的な対応事例というものを知る機会が少ない印象があります。 プロセスの フレームワーク としてCRISP-DMといった概念は存在しており、それ自体は非常に有用かと思うのですが、概念であるため解説を読むとやや抽象的な印象もあります。 これは致し方ないところもあるとは思いまして、こういったプロセスの進め方は個別状況に応じて柔軟に対応を求められる場合が多く、これをすればよいという銀の弾はないということかなと思っています。 ただ、やはり、具体的な事例でプロセスの進め方について考えてみることも重要で、自分の中で糧にしていくため、自分のやってきたデータ分析のプロセスを振り返ってみること、それをこのような形でアウトプットしてみることは、一定の意味があるのではないかと思いますのでこの機会に振り返りをしてみたいと思います。 この記事ではデータ分析をどのようなプロセスを経て実行していったか、というテーマがメインになります。 具体的な分析手法などの説明は記載しておりませんので、ご了承ください。 解決したい課題を確認する まず初めに実施したことは、そもそもどのような課題を解決することが有益であるかを確認することでした。 状況を軽く説明すると、私は昨年 エニグモ にジョインしたばかりということもあり、業務や課題の理解はまだまだ浅い状態でした。 反対に、今回自分が担当したチームにメインでデータアナリストが アサイ ンされたのも自分が初めてということもあり、ビジネス側でもデータ分析でどのようなことが出来るのか、おそらくイメージが漠然としていたであろうとも思っていました。 そのため、まずはざっくばらんに雑談という名目で、各チームメンバーに ヒアリ ングをさせてもらいました。 この時気を付けたのはデータ分析ということにこだわらず、普段どのような業務を行っていて、メインで対応していることはどのようなことか、困っていることは何か、という相手の業務理解に集中したことです。 これは、イメージが漠然としている中でデータ分析の話に寄せてしまうと、本質的な課題が出づらいのではないかと思ってのことで、話が発散してもよいのでまずは自分の理解を高めようと思ってのことでした。 一人ひとり取れた時間は短いものでしたので、完全に理解ができたというものではなかったですが、それでも、普段チームメンバーが困っている課題をある程度把握することができました。 全ての課題に着手することは出来ないので、雑談の後で見えてきた課題に対してデータ分析でサポート出来そうなことを羅列し、ビジネスサイド側の部長と優先度の確認を行うという作業を行いました。 ちなみに、イメージとして ヒアリ ングした結果を以下のような表にまとめて会話を行いました。 課題 ヒアリ ング後のまとめシート(イメージ) このプロセスを経ていくつか対応するべきテーマを絞り込み、次の ユースケース の具体化に進むこととなりました。 ユースケース を具体化する 対応するテーマは決まりましたが、具体性に欠ける部分がありましたので、ビジネスの ユースケース を具体化していくフェーズに入ります。 ※対応したテーマはいくつかありますが、ここでは「需要予測による売れ筋商品の出品強化」のテーマを例に記載します。 ここでは、実際にビジネスの対応をするチームメンバーと週次の MTG で具体的な内容をすり合わせていきました。 ビジネスでやりたいこととデータ分析のアウトプットに齟齬が生じてしまうと、有用な分析が出来なくなってしまいますので、これはかなり重要なポイントで、念入りに細かく認識合わせをしていくことが肝になります。 一つ一つ詳細に書くことはしませんが、例として具体的に確認した項目を羅列すると、 需要予測のデータをどのように活用するか? 対象としたいカテゴリやブランドはどこか? 商品はどの程度の粒度で確認したいか?(カテゴリ、ブランド、SKUなど) 予測を行いたい期間はいつからいつまでか? 一回きりの対応か?定常的にやりたいか? スケジュール感 分析結果のアウトプットイメージ などの要素があります。 これらは一つずつきれいに決まっていったわけではなく、何度か会話の行き来を繰り返し、徐々にお互いの認識をすり合わせていく流れになりました。 ここで自分が気を付けたこととしては、業務理解が浅いなりにある程度の仮説をもって MTG に臨むようにしたことです。 一例として、最初、自分の中で需要予測の ユースケース は以下のような想定をしていました。 日次で確認する 予測日から30日程度先の予測ができる 需要予測と在庫量(出品商品数)を比較して、供給不足になりそうな商品をピックアップして出品促進(※)する ※ BUYMA は商品の出品もユーザーにしていただくCtoCのサービスですので、 BUYMA で商品を 仕入 れて出品するということは基本的になく、PSさん(出品者)に出品募集を行うというアクションをとります。 しかし、話をする中でまとまっていった ユースケース は以下のようなものでした。 季節ものの商品の 仕入 れ時期を狙って、メールで出品促進を行いたい 特に会話をしていた時期(6月末~7月初旬)は、アウターの 仕入 れが8月ごろから始まるので、8月に一度出品促進メールを送り、初動を見たうえで9月に再度メールを送りたい。 アウターの売れる時期である9月~翌年1月までの需要(どのブランドモデルが特に売れるのか?)が知りたい。 将来的には、季節に応じたカテゴリの需要を定期的に取得して、出品促進メールを送れるようにしたい。 日次での確認や予測期間が30日程度という私の想定は間違っていたわけですが、それ自体は問題ではなく、こういうイメージで問題がないか?ここが分からないのだがどうなるか?と一つずつ確認を進めていったことで、 ユースケース のイメージの齟齬がなくなっていきました。 分析ゴールの設計 ここまでビジネスのイメージが明確になると、分析ゴールは大分設計しやすくなるかと思います。 今回の場合は、 需要予測が業務利用可能なレベルの精度で予測可能か?という検証結果 今期の需要予測結果 という二つのアウトプットが必要になります。 一つ目に関して、データサイエンスに関わったことのある方ならご存知だと思いますが、予測タスクはデータの質などによって必ずしもうまくいくとは限らないというリスクがあります。 そのため、今回も作成するモデルの精度が妥当であるか否か?という検証を行ったうえで、問題がなければ二つ目のアウトプットを出すというステップを踏む必要がありました。 このリスクについてビジネスサイドにも理解をしてもらったうえで、最悪うまくいかなかった場合、ビジネスサイドである程度売れ筋の商品はナレッジとして押さえていること、そのナレッジだけでも出品促進メールの作成・配信には支障がないことの確認はしていました。 もう少し細かい話をすると、人のナレッジだけでは限界もあるだろうという前提で、この需要予測の結果を取り入れることで、より有用な出品促進が行えるとビジネスサイドが思えるかという定性的観点の検証もあわせて確認する必要がありました。 定性的観点はアウトプット結果を見てもらうことで判断するとして、 定量 的に需要予測が業務利用可能なレベルの精度とはどういうものか?という定義をする必要がありました。 ここでは ユースケース を踏まえ、 アウターのブランドモデルにおいて、売上件数上位100件を予測できること をメインの検証観点としました。 また、ある程度前年に比べて伸びるか否かという視点も含まれるとよいという意見があり、副次的ではありますが、 前年比100%、120%、150%、程度の粒度で伸長率の予測が可能か? という点も検証することとしました。 これらの検証観点について、実際に昨年度のデータでどのような結果になるかを検証することとしました。 需要予測タスクではある期間に売れる商品件数を予測しますが、今回の目的は売れそうな商品をピックアップして出品促進を行うというものですので、必要な在庫数の考慮などは不要であり、売上件数の予測精度は(全く見ないということはないですが、)重要視しないことをビジネスサイドと合意しました。 やや余談ですが、モデルの精度をあげようとするには検証期間が短かったことや取得できるデータが限られることから、売上件数の予測精度については不安があったので、ここでハードルを下げられたことは大分ありがたく、この後の結果を見ても期待値コン トロール は大事だなと感じるところでした。 結果 分析のゴールを合意出来たら、最適な予測が出せるモデルの作成を目指し、 アルゴリズム やパラメータの変更、データ量(期間や特徴量)の増減などを時間の許す範囲で試しました。 ちなみに、ベースラインとして単純な直近1ヶ月の売上件数の昨年比をそのまま予測期間にも当てはめる、というルールベースの手法も試したりしています(意外と精度は悪くなかったりしました)。 最終的な結果としては、状態空間モデルを使用した予測がもっとも精度が高く、売上上位100件に入るブランドモデルのうち80件のブランドモデルを予測することが出来ていました。 伸長率の予測は細かく見ると微妙な部分もありましたが、大雑把に昨年から伸びそうか否か程度の予測は悪くなく、ここは副次的な検証観点でもあるため許容としました。 なお、売上件数の予測精度もめちゃくちゃな予測結果でも問題なので確認をしたところ、予測結果が描く期間中の週次売上件数と実績の比較結果では、大雑把に秋~冬にかけて売上件数が伸びていく予測グラフが作成出来ており(下記グラフ参照)、ビジネスサイドの定性評価も踏まえて総合的に業務利用可能であるとの判断に至りました。 あるブランドモデルの実績(青線)と予測結果(オレンジ線) まとめ 私が今年度対応してきた需要予測タスクの企画の始まり~予測モデルの検証まで、簡単にですが振り返ってきました。 この後、ビジネスサイドに今期の需要予測結果を共有し出品促進メールを配信した結果、配信後に対象商品の出品数は増加し、PSさんからも好意的な反響をいただいたとのフィードバックをいただいております。 振り返ってみて、改めて ディレクション やコミュニケーションの大切さを感じることとなりました。 大きな齟齬もなく、また、よくある分析の結果が放置されることもなく、ビジネスサイドのアクションに繋がったのは、時間はかかりながらも(かけたからこそ?)相互理解を進め適切な分析を行えたからではないかと考えています。 細かくは書いていませんが、今回拙いながらプロジェクトマネジメントの知識なども要所で意識しながら案件を進めていきました。 データ分析の手法や アルゴリズム などの勉強はもちろん重要であると思いつつ、同時にプロジェクトマネジメントやCRISP-DMなどプロジェクトを前に進めるための知識もデータアナリストとして磨いていきたいと思う1年でした。 今回の記事は以上になります。 最後まで読んでいただき、ありがとうございました。 明日の記事の担当はデータアナリストチームマネージャーの嘉松さんです。お楽しみに。 株式会社 エニグモ すべての求人一覧 hrmos.co
こんにちは。データエンジニアの谷元です。 この記事は Enigmo Advent Calendar 2022 の21日目の記事です。 目次 はじめに どうしてデータ基盤を最適化する必要があるの? どうしたら改善できるの? 現状のデータ基盤のおさらい 主要なBUYMA基幹データの最新ビューに着目してみる 最新ビューをどう変更するの? システム概要としてはどんな感じ? この方針で思ったこと BQ履歴テーブルの作成方針だけど 本当にその方法で改善するの? 運用保守する上で気になっていたこと 見込み効果はどうなの? 実装する上で意識したところ BQ履歴テーブル作成前提となるDAG依存関係 本番データを使った確認期間をできるだけ長めにとろう データ品質担保はどうしよっかな 今回は見送ったデータ品質対応 既存の手動実行スクリプトをAirflowに移植しようと思ったら そろそろリリース後の話をしよう 効果はどうだったの? BQクエリコストとそのクエリ数を月次単位で比較してみる(課金対象のみ) 効果測定するにあたって BQクエリ平均実行時間と処理量を月次単位で比較してみる(課金対象のみ) とあるLookerダッシュボード内部のクエリ実行時間を日次で見てみる 過去のとある時点のデータを遡ってみる こんな感じのクエリかな タイムリープして思ったこと Looker上でもexploreを作成してタイムリープしてみた 所感 最後に はじめに 皆さんどのようにお過ごしでしょうか。 私は昨年に猫ちゃんの可愛さに目覚めてしまい、リモートワーク中も近くで愛猫に見守られる生活を過ごしております。 今年、ねこ検定初級に合格し、愛猫と仲良くなれた気がします🐱 さて、データ基盤の開発運用保守やBI上でのデータ整備などを対応をしてまして、 今回は "データ基盤の処理最適化によるBigQueryコスト削減" の話をさせていただきたいと思います。 どうしてデータ基盤を最適化する必要があるの? エニグモ は全社的にデータを活用する文化が強く、誰でもデータ活用がしやすいように様々なデータをBigQuery(以下、BQ)に自動集約しております。 規模でいうとデータ基盤上のクエリスキャン量は ペタバイト [PB]規模/月であり、 前年同月比 約300%での増加傾向にあります。 しかし、データ活用が進むにつれて下記の課題を感じていました。 昨今の円安事情も重なり、BQクエリコストが大幅に増加傾向にある データ参照が遅く、シームレスに分析やドリルダウンがしづらい (BIや アドホック クエリ) 思考の妨げにつながってしまい、分析機会の損失につながっていると感じる 連携元で履歴が残っていないテーブルの場合、複数のテーブルを用いた過去のとある時点のデータが遡りにくい さらに下記の現状もあり、データ基盤の最適化を進めようと思いました。 基幹データでも数千万〜数億レコードあるテーブルが複数存在する 現状の仕組みでは時間経過と共にBQクエリスキャン量も増加してしまう どうしたら改善できるの? 現状のデータ基盤のおさらい バッチ処理 でBQ連携しており、データ鮮度は原則、最大1時間遅延です。 BQ連携時は差分更新分を連携し、それらのデータから重複や物理削除などを考慮したビュー(以下、最新ビュー)で連携元と同じ最新状態のデータを再現してます。 参考: Apache Airflow で実現するSQL ServerからBigQueryへのデータ同期 主要な BUYMA 基幹データの最新ビューに着目してみる 汎用的に全ての最新ビューを最適化する方針にしました。理由は下記となります。 BQ連携している基幹データだけでも200テーブル以上あり、今後も含めてテーブルの利用状況はわからない 現状のアクセス内容を調査して個々に性能改善するとなると テーブル数や処理量も多い 調査や対応方法を考えるのも大変だし、運用保守負担も大きそう ビュー内部の改善なので、BQ利用者への影響を考慮する必要がなく、サイレントでリリースできるのも良さそう 最新ビューをどう変更するの? 最新ビューは差分更新データから再現していたため、不要なスキャンが多すぎる状態でした。 なので、基幹DB全体を定期的にテーブル化し、最新ビューでそのテーブルを利用してスキャン量を改善しようと考えました。 最新ビューの変更方針は以下のとおりです。 現状. 月次差分+時間次差分のBQデータを組み合わせる 提案. 日次全件(※)+時間次差分のBQデータを組み合わせる ※ 月次差分+時間次差分でテーブル化、AM0:00固定、以下、BQ履歴テーブルと呼ぶ システム概要としてはどんな感じ? 図にすると下記のようになります。 尚、白色の箇所は既にあり、今回新規に対応したのは黄色の箇所になります。 データ基盤のシステム概要 この方針で思ったこと リアルタイム連携が実現できた場合でもBQに入れる処理の置換で済み移行もスムーズそう 基幹DB全体をBQ履歴テーブルとして残すことで、過去に遡ってデータ活用しやすそう BQなのでデータ量をあまり気にしなくて良いメリットを活かせる BQ履歴テーブルの作成方針だけど 既にBQ上に取得済みの更新差分データを用いることで後からでも過去の任意時点まで遡ってデータを再現できるのも良さそう 基幹DBが古いため、できるだけ影響を与えないようにしたかった 尚、BQのマテビューも少し検討したのですが現状では利用制約に引っかかり見送りました。 本当にその方法で改善するの? 仕組みからいうと性能は改善するとは思ってましたが、以下の懸念がありました。 最新ビューがどの程度使われているのかがわからなかった BQ履歴テーブル作成など、今回の対応により増加するBQクエリコストもある 運用保守する上で気になっていたこと データ基盤の仕組みが現状より複雑になり トラブルシューティング が大変になる BQ履歴テーブルが最新ビューに組み込まれるため、よりデータ品質に注意を払う必要あり データ基盤の運用保守で新規テーブル取り込み時は一部手動運用している 本対応後はオペレーションがより複雑化する 手動 スクリプト 実行や設定ファイル修正、データの初期移行など 愛猫の可愛い顔をみながらだとオペミスするかもしれない 社内外でBQデータが既に多方面で活用されている 対応後のデータ不備は影響が大きそう データの復旧も大変そう 見込み効果はどうなの? あまり時間をかけたくなかったのもあり、下記の確認に留めました。 現状のBQクエリコスト確認 主要テーブルを用いた SQL 単体での効果検証 改善版の最新ビューを用いた効果検証では スキャン量と実行時間共に1/2程度削減 を確認できました。 1/5や1/8程度改善しているケースもあり想定より効果があるのかもと思いました。 尚、定期的なBQ利用状況の効果測定はLooker上のMarketplaceでBQ利用状況が簡単に見れたため、そちらを利用しました。慣れてる方でしたら1日で作成できるかと思います。 実装する上で意識したところ 実際やってみるとたくさんあった気がするのですが、パッと思い出した話をしてみます。 BQ履歴テーブル作成前提となるDAG依存関係 本番データの確認期間をできるだけ長めにとろう 手動 スクリプト をAirflowに移植しようと思ったら データ品質担保はどうしよっかな BQ履歴テーブル作成前提となるDAG依存関係 BQ履歴テーブルを作成開始するにあたり、 その抽出するクエリ内部で参照されているテーブルで必要な情報が揃っている必要があります。 最新ビューは時間軸の異なるデータを複数組み合わせて物理削除も考慮してるため、テーブル毎に依存先DAGや時間軸、依存先のテーブル数も異なります。 これらの情報を設定情報として事前に定義した情報から自動で構成しています。 図にすると下記となります。 左がExternalTaskSensor、右がBigQueryExecuteQueryOperatorとなり、 右の数だけテーブルがあるようなイメージです。 参考: DAG間の依存の話 DAG依存関係 本番データを使った確認期間をできるだけ長めにとろう 本番データでの確認期間を少しでも長めに取るため、 本番影響がないよう部分的に先行リリースを繰り返しました。 そうすることで、本番データで確認を進めつつ開発も進められますし、 別件の対応も並行して進めやすくすることも可能でした。 確認期間は2,3週間ほど動かしてエラー検知されなければ大丈夫だろうぐらいで考えてました。 データ品質担保はどうしよっかな 経験上あまり手を抜かない方が良いと考え、バリデーションを入れました。 現状では気になる所だけにしておき、今後必要に応じて増やしていけば良いと考えました。 主要テーブルの意味合いチェック 新旧の最新ビューで件数比較 など 尚、BigQueryCheckOperatorがシンプルで汎用性が高かったので採用しています。 開発途中でも想定外にエラー検知をしたり、今後の運用でもデータ品質の監視にもなるので、やはり大事だと感じました。 今回は見送ったデータ品質対応 昨今の技術進歩だともっと賢くやる方法があるはずだ!と調べてたら、 Great Expectations というのがありました。 今回、時間の兼ね合いで検証はできなかったのですが、BQテーブルを自動診断してデータの品質診断結果までファイル出力してくれるようです。 GreatExpectationsOperatorもあるようですし、機会があれば触ってみようかなと思います。 既存の手動実行 スクリプト をAirflowに移植しようと思ったら 最新ビューには個人情報列あり/なしの2つ存在しており、今までは新規テーブル追加時のオペレーション時は ruby スクリプト を用いてローカルから手動実行して作成してました。 バリデーションで新旧の最新ビューを比較するのに必要でしたし、手動でやるのも手間でしたので、 これを気にAirflow( python )に移植して自動で実行しようと考えました。 対応途中で、よくみると内部でgsutilコマンド使ってることに気づき、 "現行のAirflow環境だとgsutilコマンド( CLI )が入ってなさそうやから使えへんやん!" となりました。 目標リリース期限の2週間前を切っており、効果測定やこのブログを書くタイミングのこともあったので期限をずらしたくなかったのです。 CLI 入れるとなると「あれ、間に合うかな。。後回しにしすぎた」となりました(汗 ただ、よく考えると全く同じ方法でやらなくてもいいかと頭を切り替え、 BQのINFORMATION_SCHEMAやBigQueryHookを使う方法で対応を進めて乗り切りました。 蓋開けたら CLI が移行先で使えなくて、対応期限も近づいてたので慌てたという話ですねw そろそろリリース後の話をしよう 無事に予定通り、10月末にリリースできまして、その後の話をしたいと思います。 効果はどうだったの? BQクエリコストとそのクエリ数を月次単位で比較してみる(課金対象のみ) BQクエリコストですが米ドル比較で "前月比 約25%削減" となりました。 BQクエリスキャン量だとPB( ペタバイト )レベルでの削減です。 桁が大きすぎてよくわからないですね.. 月別BQクエリコストとクエリ数 効果測定するにあたって もちろん比較月で使われ方が変更になったり増減した分もある エニグモ 内のほぼ全てのプロジェクトを含んだ数値である つまり、今回改善した最新ビュー以外の数値も含んでいる クエリ数ですが、BQ履歴テーブル自体は7月には動かしていた 10月からバリデーションも入れて先行リリースしてた影響もありクエリ数が増えてそう 約14万回は今回追加した処理が原因そう BQクエリ平均実行時間と処理量を月次単位で比較してみる(課金対象のみ) BQクエリ平均実行時間ですが、 "前月比 約40%削減" されておりました。 最新ビューが絡まないクエリも多数あると思うのですが、 単純な平均でこの数値は思ってた以上に効果があったのかもしれません。 後、10月時点で少し下がってるのはなんでだろう。 先行リリースでバリデーションとかで軽いクエリを流し始めた影響か、 または重たいのが別件で改善されたのですかね。 月別BQクエリ平均実行時間 とあるLooker ダッシュ ボード内部のクエリ実行時間を日次で見てみる LookerではSystem Activity が用意されているので、そちらで確認してみました。 やはり半分程度の性能改善がされてそうです。 私も ダッシュ ボードをいくつか触ってみましたが、 体感でも「お、ちょっと早くなったな」とわかるぐらいでした。 とあるLooker ダッシュ ボードの日別クエリ実行時間 過去のとある時点のデータを遡ってみる こんな感じのクエリかな 下記のようにすると、過去のとある時点のデータ参照もできそうでした。 本来のテーブルリレーションだけ意識すれば良いので楽ですね。 DECLARE target_date string; SET target_date = ' 20221201 ' ; WITH _table_a AS ( SELECT _TABLE_SUFFIX AS tabledate, id, value, times FROM table_a__* WHERE target_date <= _TABLE_SUFFIX -- 見たい履歴の期間を指定 ), _table_b AS ( SELECT _TABLE_SUFFIX AS tabledate, id, value2 FROM table_b__* WHERE target_date <= _TABLE_SUFFIX -- 見たい履歴の期間を指定 ) SELECT ta.tabledate, ta.id, ta.value, ta.times, tb.value2 FROM _table_a ta -- BQ履歴テーブル INNER JOIN _table_b tb -- BQ履歴テーブル ON ta.id = tb.id -- 本来のテーブル結合条件 AND ta.tabledate = tb.tabledate -- _TABLE_SUFFIX同士で結合する ORDER BY ta.tabledate, ta.id タイムリープ して思ったこと 見たい履歴の期間を大きく取りすぎると利用するテーブルによってはデータの参照が遅いですし、やはりスキャン量がやばいことになりそうです。 また、日次のAM0:00時点のみしかBQ履歴テーブルを残してないのもありますので、 傾向を掴むような分析や他に見る参照する方法がない時などの利用用途で向いてそうです。 Looker上でもexploreを作成して タイムリープ してみた やはり少し重いですね... こんな感じで使うのが良さそうですかね。 他のexploreで探索的に分析しつつ 過去のあの時のデータはどうだったんだろうと気になる 今回作成したBQ履歴テーブルを用いたexploreで確認する 定期的に確認しそうな場合は ダッシュ ボード化して性能改善もする 所感 全体を通して概ね計画通りに進められ大きな問題も発生せず、ほっとしました。 懸念してたBQ履歴作成分などで増えるコストもありましたが、それ以上に減少してくれたので良かったです。 スキャン量も右肩上がりだったので、実施タイミングも良かったのかなと思います。 また、この機会に手動で実施していた箇所を少し自動化できたのも良かったです。 ただ、今回作成したオペレータ数が約数千あって、AirflowのWebUI画面でログ表示しようとしたら少し重くて開きづらくなってました(ぇ) 今後、データ基盤の活用が進むほど、今回対応したコスト削減効果も増えると思います。 最後に データ基盤の開発運用保守に携われている方に少しでも参考になればと思い、このテーマで記事を書いてみました。本記事を通して少しでもイメージを掴めて頂けますと幸いです。 また、お忙しい中、設計の相談やコードレビューをして頂けた上司には感謝しております。 私からは以上となります。最後までお読み頂きありがとうございました。 明日の記事の担当はデータアナリストの井原さんです。お楽しみに。 株式会社 エニグモ 正社員の求人一覧 hrmos.co
こんにちは、 iOS エンジニア の 池田 です。 この記事は Enigmo Advent Calendar 2022 の 20日 目の記事です。 はじめに 2018年の WWDC で発表されたCreateMLですが、発表当初 Xcode を用いたモデル作成など Mac での利用に限定されていました。 ですが最近では iOS 、iPadOS、tvOSでも利用できるようになっており、オンデ バイス での 機械学習 モデル作成ができます。 (利用可能OSは iOS15.0以降、iPadOS15.0以降、tvOS16.0以降です。) この記事では iOS を用いたオンデ バイス での 機械学習 モデル作成と、作成したモデルの利用をしてみようと思います。 MLStyleTransfer を利用してモデル作成を行い、画像の画風変換をしてみます。 コード内に見やすさのため Forced Unwrapping してる箇所がありますが、実装時はguard文等を使って適宜書き換える想定をしています。 機械学習 モデルの作成 モデルの作成は以下のようなコードで作成できます。コード量も多くなく、簡単に 機械学習 のモデル作成ができます。 必要ライブラリの読み込み import CreateML import CoreML import Combine 学習用のデータ、パラメータの準備 let styleUrl : URL // スタイル画像のURL let contentUrl : URL // 学習用コンテンツ画像のディレクトリURL let data = MLStyleTransfer.DataSource.images(styleImage : styleUrl , contentDirectory : contentUrl ) let sessionParameters = MLTrainingSessionParameters(sessionDirectory : sessionUrl ) 学習実行 let job = try MLStyleTransfer.train(trainingData : data , sessionParameters : sessionParameters ) 学習結果の待ち受けとモデルの保存 let writeToUrl : URL // モデルの書き込み先URL var mlModel : MLModel // 作成した機械学習モデル job.result.sink { result in switch result { case .failure( let error ) : // エラー時の処理 case .finished : // 完了後の処理 } receiveValue : { model in do { try model.write(to : writeToUrl ) let compiledUrl = try MLModel.compileModel(at : writeToUrl ) mlModel = try MLModel(contentsOf : compiledUrl ) } catch { // エラー時の処理 } } .store( in : & cancellables) モデル作成コード実装時の注意事項 シミュレータのビルドでは動かないため、実機ビルドでビルドする必要があります。 シミュレータでビルドしようとすると、CreateMLの フレームワーク 自体が見つからず以下エラーが出ます。 No such module 'CreateML' 機械学習 モデルの利用 上記で作成したモデルは以下のように利用できます。 画風変換対象のUIImageを用意し、出力をUIImageとして利用できるように実装してみます。 必要ライブラリの読み込み import CoreML import Vision import VideoToolbox 入力値の準備 let inputImage : UIImage // 画風変換の適応対象画像 let cgImage = inputImage.cgImage let imageConstraint = mlModel.modelDescription.inputDescriptionsByName[ "image" ]?.imageConstraint let featureValue = try MLFeatureValue(cgImage : cgImage! , constraint : imageConstraint! ) let featureProvider = try MLDictionaryFeatureProvider(dictionary : [ "image": featureValue ] ) 画風変換後の出力データ取り出し let mlModel : MLModel // 作成した機械学習モデル let stylizedImage = try mlModel.prediction(from : featureProvider ) let imgBuffer = stylizedImage.featureValue( for : "stylizedImage" )?.imageBufferValue UIImageに変換 var cgImageForConvert : CGImage? VTCreateCGImageFromCVPixelBuffer(imgBuffer ! , options : nil , imageOut : & cgImageForConvert) let stylizedUIImage = UIImage(cgImage : cgImageForConvert! , scale : 1.0 , orientation : inputImage.imageOrientation ) 結果の確認 変換結果 変換対象の画像に スタイル画 像の画風を適応すると、こういった結果になるようです。 今回はデフォルトの設定をそのまま利用しましたが、モデル作成時にIterations、Strength、Densityなどのパラメータを調整することもできるので、そちらを変更するとまた違った出力を出すことができます。 おわりに モデルの作成・利用が少ないコード量で簡単に実現できました。 この記事では画風変換を扱ったこともあり利用ケースは多くないかもしれませんが、Classfier や Regressor も利用できるものがあるため様々な展開が考えられそうです。 スマートフォン のデ バイス 上で動くので、データをクローズドな状態で管理でき、WebAPIに投げる必要もないので安全に扱うことができるのが大きなメリットだと思います。 反面の懸念事項としては、デ バイス の処理能力に依存するので、快適な利用を考えるとある程度 ユースケース に制限がかかってくるかもしれません。 (今回はチューニングなどは特に加えてないので、 iPhone 11 Pro 端末でモデル作成時に30分ほど時間を要しています。) とはいえ簡単に実装でき、 機械学習 のモデル作成も利用もオンデ バイス で完結して動く、とても強力なFrameworkだと思います。直近の WWDC では Create ML Components が発表されていたり、 iOS でも 機械学習 関連はさらに進化を続けているのでこれからも楽しみです。 明日の記事の担当はデータエンジニアの谷元さんです。お楽しみに。 株式会社 エニグモ すべての求人一覧 hrmos.co
この記事は Enigmo Advent Calendar 2022 の 19 日目の記事です。 こんにちは、Webエンジニアの平井です。普段はSELLチームに所属していて BUYMA における出品者側の機能開発を担当しています。 今回は、最近リリースしたカタログシステムをサーバレス アーキテクチャ で開発したので利用した技術や学びについて書きたいと思います。 目次 目次 サーバレスアーキテクチャ メリット デメリット カタログシステムとは システム設計 技術スタック サーバレスフレームワーク API カタログデータ新規作成、更新処理 メリット デメリット 学び 良かった点 悪かった点 サーバレス アーキテクチャ そもそもサーバレス アーキテクチャ とは何でしょうか。人によって解釈は異なるかと思いますが、私はイベント駆動型のプログラム実行環境を利用してアプリケーションを開発する アーキテクチャ だと理解しています。システムを開発していく上でサーバーを意識する必要が無いという特徴があります。ざっくりメリットとデメリットは以下が挙げられます。 メリット サーバーを構築する手間が省けて、開発者がアプリケーションの開発に専念できる。 リソースの利用料金はコードを実行した時間に対して課金される。アイドル時間は課金されない。 デメリット 利用できる言語が限られている。 実行時間制限 Lambdaだと最大15分 カタログシステムとは 次に今回開発したカタログシステムについて説明します。カタログシステムは BUYMA のカタログ出品機能のためのシステムです。 カタログ出品機能は、出品者に対して BUYMA 側で用意したカタログを提供することで、出品者の出品作業の効率化、高品質な画像の提供を目的としています。 カタログ出品の大まかな流れは以下になります。 撮影チームが商品を撮影、画像を クラウド にアップロード オペレーションチームが色名、型番の確認、サイズの計測を実施 1,2の情報をカタログシステムの API 経由でデータ保存 4で保存したデータを API で BUYMA 側に提供 BUYMA 上のカタログ出品画面から出品者が適当なカタログを選択して、出品 以上の流れの3,4の部分(カタログデータの新規作成/更新、提供)をカタログシステムが担当していて、弊社の クラウド サービス利用実績より AWS 上に構築しました。 システム設計 詳細は省略していますが、以下が アーキテクチャ 図になります。 カタログデータ新規作成/更新は Google App Scriptから API 経由で実行され、図の上部が該当部分です。 カタログデータ提供も API 経由で実現しています。 技術スタック 次にカタログシステムの開発に利用した技術、主に AWS のサービスについて紹介します。 サーバレス フレームワーク まず、サーバレス フレームワーク については SAM を利用しました。 サーバレス フレームワーク とはサーバレスアプリケーションを構築する上でInfrastructure as Codeを実現するためのものです。今回利用したSAMの特徴は以下になります。 サーバレスアプリケーション構築用の オープンソース フレームワーク YAML ファイルでリソース管理 CloudFormation構文を拡張してサーバレスアプリケーションの構築が簡単になっている サーバレス フレームワーク にはSAMを含めて代表的な3つのサービスがあり、それぞれのメリット・デメリットは以下だと考えています。 SAM メリット CloudFormationを抽象化したものなので, CloudFormationを知っていたら書きやすい。 デメリット 構文が若干冗長 serverless メリット AWS , GCP , Azureで利用できる。 デメリット serverless独自の構文を覚える必要がある。 AWS CDK デメリット ブログラミング言語で設定を記載できるが、可読性が yaml に比べると低い ローカルでのテスト実行には、SAMを使う必要がある。 やり方 今回、開発チームにCloudFormationの経験があるエンジニアが居た点、マルチ クラウド に対応する必要がない点、ローカル環境での実行が簡単な点より SAM を選択しました。 API API は一般的な API Gateway とLambdaの構成です。工夫した点としては API Gateway とSwaggerの yaml ファイルを連携して API 定義するようにしました。 以下のようにSwaggerで定義した yaml ファイルのパスを指定することで連携できます。Swaggerで API 定義するようにしたことで API を追加するためには API 定義 yaml ファイルを更新する必要があるため、 API 追加によるドキュメントの更新漏れを防ぐことができます。 Resources : Api : Type : AWS::Serverless::Api Properties : DefinitionBody : 'Fn::Transform' : Name : 'AWS::Include' Parameters : Location : api.yaml カタログデータ新規作成、更新処理 カタログデータの新規作成、更新処理部分には AWS Step Functions を利用し、Lambdaをワークフロー的に実行しています。 AWS Step Functionsはワークフロー管理サービスで json でワークフローを定義します。 AWS Step Functionsを導入してみて感じたメリット、デメリットは以下になります。 メリット Step Functionsの GUI がリッチである。 ワークフローの全体像、各タスクの進行状況、ログ、成功状況がわかりやすい。 Lambdaを分割することで各Lambdaの凝集度が高まり、メンテナンス性があがる。 各Lambdaのリトライ設定が簡単。 デメリット 途中のLambdaで失敗した際のデータ整合性を考慮する必要がある。 個人的には GUI がリッチな点が特に好きで、データ作成、更新処理に何か不具合がある際は基本的にこの GUI から デバッグ を開始するため、 デバッグ が効率的になるようにタスク成功時の出力内容や失敗時の例外メッセージの内容を工夫して実装しました。 以下が処理詳細画面で、左のアイコンをクリックすることで各タスクのInput, Outputも確認できます。 学び 最後に開発して得た学びについて書きたいと思います。まず、サーバレス アーキテクチャ で開発して体感した良かった点、悪かった点は以下になります。 良かった点 開発者だけで構築できるリソースが多く、管理が不要なので開発体験が良かった。 AWS を利用した過去の ユースケース が多く、参考にする情報に困らなかった。 公式ドキュメント Serverless Land 悪かった点 コードの共 通化 が難しい Lambda Layer を使えば可能そう。 今回の場合各Lambdaに共通するモデル定義は共 通化 したかった。 また、今回Lambdaは Python で開発しましたが開発初期は普段利用している Ruby との言語的違いが原因で開発に時間がかかりました。 Lambdaの内 58%がPythonを利用 しているという情報を知り、メインの潮流に乗ったほうが良いと判断して Python を選択しましたが、 特に Python を利用して良かった点が無かったのでスケジュールがかなりタイトな場合は普段慣れている言語を使ってもよいと思いました。 以上になります。 明日の記事の担当はエンジニアの池田さんです。お楽しみに 株式会社 エニグモ すべての求人一覧 hrmos.co
こんにちは、UIUXデザイナーの和田です。 この記事は Enigmo Advent Calendar 2022 の18日目の記事です! 弊社では、 BUYMA のサービスやアプリ・WebサイトのUIUXをよりよくしていくことを目的に、 「より深くユーザーを理解して」「ユーザーにとって価値のある体験」を届けていくために、UXリサーチに基づいた改善に力を入れています。 今回は、 BUYMA の顧客理解のために進めているUXリサーチの取り組みについてご紹介します。 【1】ユーザーセグメント分類|User Segmentation BUYMA の会員について、RFMの指標をベースに「 利用頻度軸 (Recency&Frequency)」「 価格志向軸 (Monetary)」「 年代軸 」 からユーザーセグメントの分類を進めています。 【各セグメントのボリューム】を把握して、 主要なセグメントにおけるユーザーの購買傾向や利用傾向 について、 視覚的に捉えられるようにLookerのツール整備をデータアナリストとUIUXデザイナーが協働で実施しています。 利用頻度軸(Recency&Frequency) 全体の購買傾向から適切な 閾値 を検討して、「利用頻度」と「直近の利用傾向」からセグメントの分類を進めています。 価格志向軸(Monetary) ユーザーにおける純粋な生涯累計購入金額で分類するのではなく、「どのくらいの価格帯のアイテムを中心に購入しているか?」「どんな価格帯のアイテムに興味関心が高いか?」という観点で、F0ユーザーやF1-2といった利用歴の浅いユーザーについても価格(購買行動)に関する志向を把握できるように分類を進めています。 年代軸 年代については、生活スタイルや行動特性の異なりを考慮して分類しています。 上記の3つの軸を考慮して、【社内で主要なセグメントのユーザーモデルについて共通理解できるようにすること】を目標にユーザーセグメントの調査・分類を進めています。 また、これらの 定量 的なユーザーセグメントの分類をもとに、 【2】や【3】の定性調査を進めることで、各セグメントのよりリアルな価値観・行動特性、UXに関する課題を把握して、具体的な改善を進められるようにしています。 【2】ユーザーアンケート|User questionnaire 弊社では、主に Google フォームを用いてユーザーアンケートを運用しています。 主に以下のような手順で、【1】のユーザーセグメント分類をベースに、UXについて深堀り調査したいセグメントに絞ってアンケートを実施しています。 アンケートは、メールやLINEによる配信や、Webサイト上にアンケート募集バナーを出すなど、対象のユーザーセグメントに合ったタイプで実施するように心がけています。 アンケートの計画・実施手順 アンケートのリサーチ目的・検証内容の整理 配信対象者の選定 アンケート設問の設計、 Google フォームの作成 アンケートの配信・公開 インセンティブ (謝礼)の付与 アンケート結果まとめ・共有・ふりかえり 【3】のユーザーインタビューを前提に、 設問の中でインタビューに協力いただける協力者を募るかたちでアンケートを実施することもあります。 会員向けのユーザーアンケートでは、会員データと紐付けて分析できるようにしているため、 ユーザーセグメントやユーザーの購買傾向・利用傾向を踏まえて、ユーザーインタビューにご協力いただきたい方を選定させていただくようにしています。 また、細かく検証したいUXについての設問をアンケートに用意することで、 アンケート回答内容から「あらゆる価値観や行動特性を持つユーザー」に対してインタビューが実施できるように工夫しています。 【3】ユーザーインタビュー|User Interview ユーザーインタビューは【2】のユーザーアンケートから、UXについてより深堀りして調査したい場合に実施しています。 近年のインタビューは、すべてオンライン(Zoom)を用いたインタビューとさせていただいており、さまざまな地域からご参加いただいています。 インタビューは約60分間で実施しており、 ユーザーの価値観や行動特性(日頃の行動や嗜好など)から インサイト を深堀りしていく 【 A. ユーザー モデリング 型の設問 】と、 過去の体験やこれから行いたい事柄を実際の画面を用いてシミュレーションいただきつつ、 その様子を観察・フローに沿ってUX(そのタイミングにおける思考やペイン・ゲインなど)について深堀りしていく 【 B. UX検証型の設問 】の2つから構成しています。 インタビューの主な計画・実施手順について、ご紹介します。 インタビューの計画・実施手順 インタビューの目的・ ヒアリ ングポイントの整理 インタビュー候補者選定 *1 インタビューシナリオの作成 インタビュー打診・調整、スケジュール作成 個別インタビューシートの作成 ユーザーインタビューの実施 インセンティブ (謝礼)の付与 インタビュー結果まとめ・共有・ふりかえり ユーザーインタビューの前に、ユーザーそれぞれの「個別インタビューシート」を用意するようにしています。 「プロフィール」や「購買傾向」、「利用傾向(お気に入りや登録年など)」、「購買や問い合わせの履歴」、「アンケートの回答」などを細かくまとめたシートとなっており、 基本的なインタビューシナリオに基づいた ヒアリ ングの中で、インタビュー対象者それぞれに合った ヒアリ ングをできるように工夫しています。 また、「インタビューの記録」についても同一シート内に収めることで、ユーザー単位で俯瞰できるようにして「傾向の把握」や「振り返りしやすいかたち」にしています。 弊社では、定期的にユーザーインタビューを実施させていただいております。 今後も、ユーザーインタビューを通じて 定量 的な数値からは読み取ることが難しいさまざまなペインや気付きを得ていきたいと考えています。 さいごに 弊社では、UXリサーチから導いたUXにおける課題(AS-IS)から、施策・企画(TO-BE)を検討して、日々サービスとUIUXの改善に取り組んでいます。 仮説通りの効果が得られているのか確認してよりよいかたちにしていくために、ABテストの活用や効果検証を実施して細かいアップデートや方向修正もおこなっています。 UXリサーチを通じて「より深くユーザーを理解して」「ユーザーにとって価値のある体験」を届けられるよう、 BUYMA のサービス・UIUXの改善にご期待いただけますと幸いです。 明日の記事の担当は サーバーサイドエンジニア の 平井 さんです! お楽しみに。 株式会社 エニグモ すべての求人一覧 hrmos.co *1 : 【2】ユーザーアンケートにて、インタビュー協力者を同時に募るようにしています
こんにちは、データサイエンティストの堀部です。 この記事は Enigmo Advent Calendar 2022 の16日目の記事です。 普段の業務では情報検索(検索/レコメンド)、不正検知、ユーザー属性の推定などを BUYMA にプロダクトとして組み込むことを行っています。その中でも モデリング 以前のタスク設計や探索的データ分析( EDA : Explanatory Data Analysis)、データのクレンジング・前処理、特徴量エンジニアリングなどを主に SQL (BigQuery)で行う部分に多くの時間を割いています。 1 今回は、違和感のある予測結果から気づいた傾向を元にデータの前処理を追加したことでモデルの精度改善につながった一例を紹介いたします。 概要 気づいた経緯 検出方法 結果 まとめ おまけ 概要 データから 機械的 な(ボットのような)アクセスを除外したことで、 機械学習 モデルの精度が改善しました。 そもそも気づいた経緯や検出方法、除外した結果について紹介いたします。 気づいた経緯 商品のブランドのitem2vecを閲覧履歴から生成した際に、 BUYMA 内で人気のブランドと直近1年で1件も購入されていないマイナーなブランドの類似度が非常に高く出ているパターンが複数見受けられました。違和感を感じ、学習データでマイナーなブランドに対してアクションを行なっているユーザーのlogを閲覧数が多い順に見たところ、一定間隔で別の商品詳細に遷移するというのを繰り返しているlogが見受けられ、人間ではないアクセスの可能性が高いと判断しました。 ちなみに、item2vecなどのベクトルはarray型でBigQueryに格納し、コサイン類似度などを SQL で計算できる様にしていますがとても便利です。 create temp function cos_sim(vec1 array<float64>, vec2 array<float64>) returns float64 as ( ( select sum (elem1 * elem2) / sqrt ( sum (elem1 * elem1))/ sqrt ( sum (elem2 * elem2)) from unnest(vec1) elem1 with offset pos1 inner join unnest(vec2) elem2 with offset pos2 on pos1 = pos2 ) ); 検出方法 「1度でも1分以内に次の商品詳細への遷移を100回以上繰り返しているユーザー」という条件で検出しました。 2 こちらを SQL でセッショナイズを行うことで実装しました。 3 セッショナイズのイメージ サンプルクエリ with item_action_view_detail as ( select ial.user_id, ial.time, ial.time - lag(ial.time) over ( partition by ial.id order by ial.time ) as interval_action from item_action_log as ial where ial.action = " view_detail " ), session_head as ( select id, time, case when lag(time) over ( partition by user_id order by time ) is null then 1 when time - lag(time) over ( partition by user_id order by time ) > interval 60 second then 1 else 0 end as session_head_flg from item_action_view_detail ), session as ( select user_id, time, concat ( user_id, " - " , sum (session_head_flg) over ( partition by user_id order by time rows between unbounded preceding and current row ) ) as session_id, from session_head ), session_summary as ( select session_id, user_id, count ( 1 ) as cnt_view, from session group by 1 , 2 ) select distinct user_id from session_summary where cnt_view >= 100 結果 ランキング学習を行っているモデルに対し、検出したユーザーIDを除外する前と除外した後のデータそれぞれ学習させたモデルでのテストデータでの精度を比較したところMAP(Mean Average Precision)が約2%改善しました。商品のブランドのitem2vecも除外後のデータで生成し直したところ、直感に合うようなブランド同士が類似度が高く出るようになりました。 機械学習 での推論結果をBigQueryに格納しているため、MAPなどの評価指標も SQL で計算できるようにしています。 (下記、average_precisionの平均をとったものがMAPになります) create temp function precision (y_true array<int64>, y_pred array<int64>, n int64) returns float64 as ( ( select sum ( case when elem1 = elem2 then 1 else 0 end ) / n from unnest(y_true) elem1 with offset pos1, unnest(y_pred) elem2 with offset pos2 where pos2 < n ) ); create temp function average_precision(y_true array<int64>, y_pred array<int64>, n int64) returns float64 as ( ( select sum ( precision (y_true, y_pred, m+ 1 )) / array_length(y_true) from unnest(generate_array( 0 , n -1 )) as m where y_pred[safe_offset(m)] in unnest(y_true) ) ); まとめ やったことは単純ですが、データの前処理の大切さを体感できた事例だったので紹介させていただきました。 Garbage In, Garbage Outを胸に刻みましょう。 おまけ 最近話題のchatGPTに BUYMA について歌詞を書いてもらいました 手にしたいものを 思い浮かべたら BUYMAに行くよ みんなの想いが集まる 世界中の商品が たくさん見つかるよ お求めやすい価格で 今日も新しいものが届くよ ハイブランドの服や靴 おしゃれなバッグやアクセサリー 雑貨や家電まで BUYMAで探してみよう 会員登録すれば さらにお得な特典が たくさんあるよ お気に入りを見つけて 買い物の楽しみを増やそう BUYMAで楽しい買い物 手にしたいものを 思い浮かべたら BUYMAに行くよ みんなの想いが集まる 今すぐBUYMAで 買い物しよう! 宣伝上手ですね。 話題のAIに弊社のサービスを認知されていて少し嬉しかったです。 明日の記事の担当は人事の右川さんです。お楽しみに。 株式会社 エニグモ すべての求人一覧 hrmos.co データクレンジング〜特徴量生成部分で dbt の活用を検討中です ↩ 実際の SQL ではユーザーエージェント( UA )でのボット除外を行ったデータに対して処理しています ↩ SQL でのセッショナイズは、 10年戦えるデータ分析入門 や ビッグデータ分析・活用のためのSQLレシピ に詳しく記載されています ↩
こんにちは、エンジニアの竹田です。 この記事は、 Enigmo Advent Calendar 2022 の15日目の記事です。 さっそくですが、エンジニアのみなさまは一流のエンジニアとはどんなエンジニア像をお持ちでしょうか。 自分は「障害を未然に防ぎ、継続的に安定運用可能なシステムを構築できるエンジニア」を一流のエンジニアだと考えています。 ひとえに障害と言っても、仕様と異なる動作をしない、リソース不足等によるシステム停止が発生しない、などいろいろと定義はあるかなと思います。 今回のエントリでは前者の「仕様と異なる動作をしない」という点について、業務を通じて見識を深められる場面がありましたのでご紹介したいと思います。 検索システムのリプレイス 今期は、とある検索システムのリプレイスに注力していました。 エニグモ では検索システムに Apache Solrを利用しており、今回リプレイスするシステムについても構成としては過去に こちらのブログ で紹介したものと近い構成となります。 自分は検索システムへのデータ投入、および検索リク エス トを行って結果を整形する箇所(主に Rails )の改修を担当しています。 リプレイスに当たり、 Rails からSolrへの直接リク エス トではなく、中間媒体として検索 API を経由する形式に変更しました。 このため、まずは既存コードやログを調査してリク エス ト方法や動作仕様をまとめ、いざコード改修に移りました。 コード改修 調査した仕様を元に一旦コード改修を完了し、ある程度動作するものができました。 その後、既存のテストコードを元に新コードのテストコードに移植を進める中で、細かな動きを実装できていないことに気付きました。 (画像はイメージです) 型が数値なのか文字列なのか nil 許容するのか 調査しきれていなかったリク エス ト方法や利用箇所がある 中には、えっ、そんな動きするの、みたいなものまで... コードやログを目視しただけでは分からなかったことが次々とでてきました。 テストコードの重要性の再認識 テストコードを動作させることで、より詳細な動作を知ることができました。 後から考えると、もっとしっかりとコードチェックをしていれば把握できていた点もあると思いますが、やはり作業しているのが人ですので、どうしても見落としは避けられないと思います。 テストコードがなければ デグレ を引き起こしていた可能性があり、「仕様と異なる動作をしない」点を満足できない状態にしてしまうところでした。 普段コードを書く際、テストコードも意識して書いてはいたものの、作成に当たってはコード カバレッジ 、モックの使用、境界値や複雑なパターンの網羅をどこまで厳密にするか等、、考慮することが多いです。 それなりに時間を要しますし、テストコード自体の視認性に納得がいかなかったりと、正直あまりテストコードを書くことは好きではありませんでした。 ですが、今回、多くの点をテストコードに助けられました。 テストコードの意義などを検索すると様々な方の記事がヒットするためその辺りの言及はしませんが、理屈としては理解できるもののなかなか溜飲を下げられないものだと思っています。 その点においても、実際にテストコードに助けられたという体験は非常に貴重なものでした。 最後に 過去にテストコードを地道に作成いただいた担当の方に感謝をしつつ、今後の誰かの助けになるということを意識して、テストコードを書くことを心掛けていこうと思います。 テストコードを全てパスした状態でのリリースは、精神的な安心感にも繋がりますね。 明日の記事担当は、データサイエンティストの堀部さんです。お楽しみに! 株式会社 エニグモ すべての求人一覧 hrmos.co
こんにちは、サーバーサイドエンジニア の 橋本 です。 この記事は Enigmo Advent Calendar 2022 の 14 日目の記事です。 はじめに エンジニアとして仕事をしているとコードを読むことが多いと思います。例えば、仕様調査、CSからのお問合せ対応、レビュー対応などがあると思います。 今年を振り返るとコードを書く以上に読むことが多く、コードをより正確かつ速く読むにはどうすればいいかを考えることが多くありました。 ということで、この記事では私個人がコードを読む時に意識していることを紹介していこうと思います。 いきなりコードは読まない 1つ目はいきなりコードは読まないことです。業務で使われているコードの量は1つの機能でもとても多いです。いきなり読み始めるととても時間がかかったり、迷子になってしまうかもしれません。なのでまずはシステム全体を把握してインプットとアウトプットが何なのかの理解を進めます。把握の仕方は、システム仕様書を読んだり、担当した方と MTG して処理の大まかな流れを教えてもらっています。 全体の把握と同時並行で、実際に動かしてみることが多いです。実際に動かすと理解が進みます。 読む目的を意識する 2つ目は読む目的を意識することです。コードを読む目的は様々だと思います。表は一例です。 コードを読むケース 知りたいこと レビュー対応 バグが無いか、仕様を満たしているか、設計的に問題ないかを確認する。 CSからのお問合せ対応 バグが発生している原因を知る。 目的が曖昧な状態でコードを読み始めると、本来は読む必要が無いコードも読むことになり時間が足りなくなるケースがあると思います。業務は期限が決まっていることが多いので、効率的に進めるためにも目的を意識することは大事だと考えています。 適度にメモを取る 3つ目は適度にメモを取ることです。業務で使われているコードは量が多く、一度に読んで全てを把握するのは大変です。なので私はメモを取りながら読み進めることが多いです。メモの取り方ですが、 ソースコード 上に直接書いたり、別途テキストファイルに書いたり、slackのスレッドに書いたりなどして流れを整理しています。まだ慣れていない プログラミング言語 の場合は読むのに時間がかかると思うので、メモはかなり有効だと感じています。 おわりに 今年は色々な機能のコードを読むことが多く、 BUYMA の仕様について詳しくなりました。それだけでなくコーディングの技術についても学ぶことが多かったので、コードを読むというのはとても大事だなと改めて感じました。多分来年も読むことが多いので頑張りたいと思います。 明日の記事の担当はエンジニアの竹田さんです。お楽しみに。 株式会社 エニグモ すべての求人一覧 hrmos.co
この記事は Enigmo Advent Calendar 2022 の13日目の記事となります。 お疲れさまです。インフラチームの山口です。 弊社では一部イン フラリ ソースのモニタリングにDatadogを利用しています。 その中で、今回はDatadogの利用開始当初に GUI で作成されたモニターをTerraformerとTerraformを使用して構成管理した際の事例について報告します。 同様の技術スタックを使用したインポートや構成管理における具体的なテンプレート等の事例には事欠かないと思いますので、作業計画を中心に説明します。 要は、TerraformerやTerraformの使い方は様々良い資料があると思うため、今回固有の対応をした点を注力して説明します。 本稿の構成を以下に記載します。まず、対象とするモニターの状態などの前提を説明します。次に、作業の流れを概説し、Terraformの ディレクト リ構成等を少し説明します。そして最後にまとめます。 1. 前提 まず作業の目的と対象とするモニターについて説明します。 目的 GUI で人間が手で作成したモニターをTerraformで構成管理する。 対象とするモニター 構成管理の対象とするモニターの概要を以下に記載します。 あるプロジェクトでのイン フラリ ソースの監視のために手で作成されたモニター309件を対象とします。 複数種類のイン フラリ ソースの監視のためのモニター群ですが、当該プロジェクトであることを示すタグのみが付与された泥団子状態です。対象のモニターを、サーバロールごとのタグを付与した上で、ロールごとにmodule化し取り扱いをしやすくすることを目的とします。 モニター件数 特徴 309件 ・インポート対象を識別できる特定のタグが付与されている(例: project: xxx といったタグのみ) ・モニターは Rails のWebサーバや、バッチサーバ、DBサーバといったイン フラリ ソースの監視目的 2. 作業の概要 作業の概要について説明します。今回の対応は大きく2段階に分かれます。 まず、STEP1としてインポート対象のモニターをTerrformerで一旦1つのtfファイルにインポートし、その後人手でサーバのロールを表すタグをモニターに付与しapplyすることで、泥団子状態を改善させます。 次に、STEP2として、サーバのロールごとに再度Terraformerでインポートし、tfファイルを リファクタリング しapplyを行います。 以降で各STEPの内容とポイントを説明します。 STEP1: 事前準備 STEP1では泥団子状態を脱することを最優先とします。 そのため、一旦Terraformerでインポートした後に、人間が大雑把にモニターにタグを付与しapplyします。その際のポイントとしては、モニターによっては「どのようなタグを付与するべきか」悩むケースもあると思いますが、Terraformで管理可能になることで泥団子状態より悪くはならないため速度優先で「エイヤ」でタグを付与しapplyしてしまいます。 実際にタグの付与は私が作業したのですが、作業当時の記録を見ると10分で25%進んだと記録があり、1時間もかからずに思いの他早く済んでいたようです。 一旦既存のモニターをTerraformerで一括で1つのtfファイルにインポートする tfファイルのモニターの内容を精査し、各モニターの用途(サーバロール)が判別可能なようなタグを人手で付与しapplyする 図1 STEP2: サーバロール単位でのインポートと リファクタリング 以下をサーバロール数分繰り返します。 端的には、Terraformerでインポートしたtfファイルの内容を人間が読みやすいように修正し、かつ意図しないリソース再作成なども発生しないようにtfstateも修正した上でapplyします。 特定のサーバロールのモニターをterrformerでインポート tfファイルを リファクタリング してapplyする 図2 3. インポート後の ディレクト リ構成や修正内容 インポート後の ディレクト リ構成やTerraformerが自動生成した内容からの修正ポイントを説明します。 本記事は、技術的な説明を主とする記事ではないため概要のみを簡単に記載します。 ディレクト リ構成 ディレクト リ構成を以下図に記載します。 複数環境に対応可能なようmodule側に主なリソースを切り出して、そのmoduleを環境ごとに呼び出しモニターを作成する構成としています。 また、 terraform workspace 等は使わずに愚直に環境・サーバロール単位でtfstateを分ける構成としています。 リモートバックエンドも使わずに、原始的にtfstateを リポジトリ にコミットします。 これは、複数人で 人海戦術 的な方針で分担して作業する際にリモートバックエンドを操作するためのキーの設定やミスする可能性のある箇所を極力減らすことを念頭においたというのがもっともらしい言い訳ですが、実際は横着をしたためです。 図3 tf、tfstateの リファクタリング Terraformerでインポートした際に リファクタリング を行った観点を以下で箇条書します。 リソース名を人間が判読可能なものにする resource "datadog_monitor" "tfer--monitor_1234567" といった自動生成されたリソース名を人間が判別しやすいものに修正します。 1のリソース名の修正に伴いtfstateも修正する これは、モニターの場合は状態を持たないリソースのため特に再作成でも大きな問題は無いですが、 リファクタリング 時の修正差分等を terraform plan で確認したいために tfstateも修正し極力再作成を回避します。 ヒアドキュメントを使う Terraformerでインポートしたモニターは message が1行で可読性が良くないためヒアドキュメントを使うよう修正します。 まとめられるリソースはfor_eachでまとめる for_eachでまとめられるリソースは for_eachで複数作成します。その際の2との兼ね合いは状況を見て判断します(場合によっては再作成もやむなしとする)。 4. まとめ(所感) 最後に所感とまとめを記載して終わります。 所感 利用開始直後でも最低限の初期設計は重要 その時はその時のベストを尽くしてるはずなので昔を悪し様に思わない気持ちが重要(今もそのうち昔になるので) ただし過去の経緯や判断に過度に忖度をする必要性はない(でないとずっと辛いままのため) こういった、過去のしがらみ踏まえた改善活動ができるのは事業会社ならでは まとめ ある作業を、「すべて人間 or すべて スクリプト で対応する」といった形に拘ってしまうと辛いことがあるため最も早く目的を達成できそうな方法を選ぶことが重要。それは会社の人的リソースの状況にもよると想像されるため、適度な塩梅は自分たちで考えていくしか無い(身も蓋もないまとめ)。 明日の記事の担当はエンジニアの橋本さんです。お楽しみに。 株式会社 エニグモ すべての求人一覧 hrmos.co
こんにちは、新卒エンジニアの川本です。 BUYMA のサーバーサイドを中心に開発しています。 この記事は Enigmo Advent Calendar 2022 の12日目の記事です。 データ モデリング について学習中で、DB設計の入門書を読んで論理設計について第1~5正規化といった内容を学習しましたが、いざ個人開発でデータ モデリング しようとしたらどうしたらいいのかよくわからず、手を動かすことはできませんでした。 そんな状況の時にイミュータ ブルデー タ モデリング という モデリング 手法について知ったことをきっかけけに、データ モデリング がイメージしやすくなりました。 この記事ではイミュータ ブルデー タ モデリング を実践してみて感じたことを紹介できればと思います。 目次 データと情報の違い イミュータブルデータモデリングとは イミュータブルデータモデリングの手順 普段Railsで開発していて感じたこと イミュータブルデータモデリングとの向き合い方 最後に データと情報の違い データと情報には以下の違いがあります。 データ 客観的な事実を記録したもので、文脈や解釈がない RDB に記録される 情報 文脈や解釈の与えられたデータ SQL で抽出する この違いから正確な情報が必要ならデータが正確に記録されていなければならないことがわかると思います。 イミュータ ブルデー タ モデリング とは イミュータ ブルデー タ モデリング とは、データを失わずに RDB に正確に記録できるように、更新と削除がなるべく起きないようにデータ モデリング することです。 更新と削除が起きるということは、データが変更された、失われたということです。 つまり、正確なデータをすべて RDB に記録できていないことになり、必要な情報を抽出できない可能性が出てくるということです。 イミュータ ブルデー タ モデリング の手順 ここでは、イミュータ ブルデー タ モデリング を実践したことで、正規化について理解が深まった瞬間があったので例を交えて紹介できればと思います。 詳しい手順が知りたい方は以下の記事がすごくわかりやすいです。 scrapbox.io エンティティはイベントとリソースにに分類される 分類の判断には、エンティティが日時属性を持つかどうかで判断できます。 日時属性を持つエンティティはイベントエンティティに分類されます。 エンティティに対して、「~する」という動詞をつけてみると識別しやすいです。 例えば「会員する」という日本語は不自然ですけど、「注文する」という日本語は自然です。 ここから注文はイベントエンティティで会員はリソースエンティティであることがわかります。 今回はこのイベントエンティティについて少し深ぼって説明していきます。 イベントエンティティには日時属性を1つだけ持つようにする イベントは1つの日時属性を持ちます。 例えば、注文エンティティなら注文日時を持ちます。 ここで以下のような設計の注文テーブルがあったとします。 上記の注文テーブルは、注文日時の他に請求日時、入金日時、更新日時を持ってしまっています。 これはこの注文テーブルの中に、以下の異なるイベントが混在していることを示しています。 請求 入金 これらは別のイベントエンティティである請求エンティティ、入金エンティティとして分解することができます。 そうすると以下のように設計することができます。 こうすることで、請求や入金が発生しても注文テーブルをUPDATEする必要なく、それぞれのテーブルへのデータのINSERTのみでデータを記録することができます。 ここまでの流れは実はOne Fact in One Place(1つの事実は1つの場所に)という正規化の行為そのものとなっています。 第1~5正規化を意識しながら初学者が正しく正規化するのは難しいと思いますが、ここまで紹介した手順だとまだイメージがつきやすいのではないでしょうか。 普段 Rails で開発していて感じたこと Rails で普段開発をしていると更新日時をもたないことは違和感があるかもしれません。 Rails でscaffoldすると更新日時カラム(updated_at)が自動で入ると思います。 order(注文)リソースをscaffoldする。 $ rails g scaffold order amount:integer 自動的に作成日時(created_at), 更新日時(updated_at)が入る。 # == Schema Information # # Table name: orders # # id :bigint not null, primary key # amount :integer not null # created_at :datetime not null # updated_at :datetime not null # class Order < ApplicationRecord end これは Rails がWEBシステムで必要なのは、最新のデータだけであるという制約を設けていて、UPDATEが起こること前提の設計になっているからだと思われます。 この強い制約を設けることでUPDATEやDELETEを許容する代わりに、たいていのことはデータベースと1対1となったModelの CRUD で表現できるというシンプルさを手に入れることができます。 Modelに書くことができず、Serviceクラスが肥大化したりするのはデータ モデリング の設計が悪い可能性が高いそうです。 このあたりは以下の Podcast で詳しく説明されているので、聞いてみてください。 anchor.fm イミュータ ブルデー タ モデリング との向き合い方 イミュータ ブルデー タ モデリング で考えると、イベントをエンティティとして捉えることからエンティティの数も増え、INSERTのみで記録することからデータ量も増えます。 そうすると、論理設計の段階では問題なかったかもしれませんが、いざ物理設計する際に実装を考慮するとパフォーマンスの問題に直面することもあるかと思います。 イミュータ ブルデー タ モデリング はデータ モデリング における正解ではなく、一種の論理設計をする際の制約として捉えるといいと思います。 考え方としては以下のような順番かと思います。 イミュータ ブルデー タ モデリング で論理設計する。 物理設計の段階で実装を考慮するとパフォーマンス的に厳しい箇所が見つかる。 問題となる箇所に対してUPDATEを許容する。 最後に イミュータ ブルデー タ モデリング はデータ モデリング における正解ではなく、一種の考え方です。 私のように初学者でデータ モデリング に対して苦手意識がある人は一度イミュータ ブルデー タ モデリング について調べて実践してみるといいかもしれません。 興味がある人は、 WEB+DB PRESS Vol.130のデータ モデリング 特集を面白いので読んでみてください。 gihyo.jp こちらの楽々ERDレッスンという書籍も実践的な内容が多くおすすめです。 www.shoeisha.co.jp 明日の記事の担当は インフラエンジニア の 山口 さんです。お楽しみに! 株式会社 エニグモ すべての求人一覧 hrmos.co
こんにちは、22新卒で入社したエンジニアのhashinoです。 この記事は Enigmo Advent Calendar 2022 の9日目の記事です。 背景 皆さん、普段コンテナを利用していますか? エニグモ では、 Kubernetesを活用 していて、開発環境でもDockerを活用しています。 しかし、最近はWebAssemblyがコンテナを完全に置き換えるかもしれないという噂も耳にします。 既に Kubernetes では、kubelet API 互換の Krustlet が注目を集めています。kubeletは、ご存知の通りOCI準拠のコンテナを実行しますが、Krustletは、 WebAssembly System Interface ランタイムのWebAssemblyを実行するものです。 Kubernetes 上でPodの代わりにWebAssemblyを動作させることが可能になっています。 また、 Fermyon では、WebAssemblyマイクロサービスを構築するためのDeveloper ToolであるSpinを開発しております。 このような背景から、コンテナの代わりにWebAssemblyが利用される未来も想像ができるようになってきたところです。 そして、先々月末に大きなニュースが発表されました。 それが、DockerのWebAssemblyコンテナです。 https://www.docker.com/blog/docker-wasm-technical-preview/ この記事を読んだところ、Docker API を使ってWASM Runtimeでの実行が可能になるということです。 さらには、WASM Imageなる概念もあり、これにより既存のDocker Imageと似たようなエコシステムやパイプラインの活用が期待できるということです。 DockerとWASMはどのように統合されたのか 多くの読者の方が気になっている点ですが、プレビューに書いてあった内容を簡単に私なりにまとめました。 OCIアーティファクト とcontainerd-shimを活用している。 OCI準拠のWebAssemblyランタイムである WasmEdge を採用し、そのために containerd-wasm-shim を作成した。containerd-wasm-shimは、OCI アーティファクト からWASMモジュールを抽出し、WasmEdge Runtimeを使用して実行する。 containerd-shimを使用できるようにするために、 --runtime=io.containerd.wasmedge.v1 フラグによって、WASM Runtimeを宣言できるようにした。 実際に私の MacBook のDocker DesktopでWASMコンテナを作成して実行してみました。 今回は、 公式のサンプル を参考に進めました。 [デモ]実際にDocker DesktopでWASMを実行してみる 今回は、Go言語を使ってサンプルを作ります。 WASMへの コンパイル が容易なtinygoをインストールします。 brew tap tinygo-org/tools brew install tinygo そして、 Hello World するだけのGoのプログラムを用意します。 本記事では、このコードをWASMバイナリに コンパイル して、それをOCIイメージとして利用可能であることを検証します。 package main func main() { println("hello world") } では、まずWASMバイナリに コンパイル しましょう。 tinygo build -o wasm.wasm -target wasm ./main.go このコマンドで、 wasm.wasm というバイナリが出来上がります。 ここから、WASMコンテナ関連の部分に入っていきます。 先ほど作成したWASMバイナリをOCI互換のイメージにしていきます。 以下のように少し無理矢理ですが、Dockerfileを使って実現できます。 FROM scratch COPY ./wasm.wasm /wasm.wasm ENTRYPOINT [ "wasm.wasm" ] ここで利用している scratch というコンテナイメージは、Dockerコンテナを実現する上で最小限のもののみので構成された非常に軽量なコンテナイメージです。 今回は、Dockerイメージのエコシステムを活用するためにscratchの中にWASMモジュールを入れるように構成しています。 docker buildx build --platform wasi/wasm32 -t sample-wasm-container . これでOCI互換のWASMイメージを作成できるはずなのですが、 ------ > exporting to image: ------ ERROR: failed to solve: operating system is not supported このようなエラーに遭遇しました。(かなり詰まりました汗) Docker DesktopのContainerdのイメージストア機能はデフォルトではOFFのようなので、ONにする必要があります。 以下のリンクの手順通りに進めることで解決しました。 https://docs.docker.com/desktop/containerd/ もう一度以下のコマンドを実行すると成功します。 docker buildx build --platform wasi/wasm32 -t sample-wasm-container . では、出来上がったイメージを見てみましょう。 ➜ docker image ls REPOSITORY TAG IMAGE ID CREATED SIZE sample-wasm-container 0.1 1711f20d1ef5 4 minutes ago 128kB ➜ docker image inspect sample-wasm-container | grep -A 3 "Architecture" "Architecture": "wasm32", "Os": "wasi", "Size": 127948, "VirtualSize": 127948, wasm32 というArchitectureのイメージができていることが確認できます。 では、ここからこのイメージをDocker Hubに登録していきます。 docker image tag sample-wasm-container mikiko/sample-wasm-container:1.0 docker image push hashino/sample-wasm-container:1.0 ところが、以下のようなエラーが出ました。 server message: insufficient_scope: authorization failed 以下のコマンドで再度dockerにログインすることで解決できました。 docker login Docker HubのWebUIからもDockerイメージが保存されていることが確認できます。 では、実際にDocker Hubに保存したWASMイメージを実行してみましょう。 docker container run --rm --name= \ --runtime=io.containerd.wasmedge.v1 \ --platform=wasi/wasm32 \ mikiko/hello-wasm-container:0.1 hello world うまく実行することができました! まとめ 本記事では、先々月に発表されたDocker WASMコンテナを実際に実行してみるところまで進めました。 Dockerが公開したWASMコンテナを検証してみました。 これまで通りのコンテナ レジストリ を活用したビルドやデプロイに関するエコシステムを活かして、WASMを導入できることが分かりました。 ぜひ、皆さんもご検証ください!! 参考文献 https://www.publickey1.jp/blog/22/docker_desktopwebassemblywebassembly.html https://github.com/opencontainers/artifacts https://www.docker.com/blog/docker-wasm-technical-preview/ https://docs.docker.com/desktop/wasm/ https://nigelpoulton.com/webassembly-the-future-of-cloud-computing/ https://github.com/tinygo-org/tinygo https://tinygo.org/docs/guides/webassembly/ 株式会社 エニグモ すべての求人一覧 hrmos.co
斧を研ぎましょう こんにちは、エンジニアの後藤です。 BUYMA のWebアプリを作る仕事をしています。 この記事は Enigmo Advent Calendar 2022 の8日目の記事です。 この記事のゴール この記事のきっかけ どのように実現するか 新たに見つけた課題 最後に 本当に最後に この記事のゴール この記事では、 Visual Studio Code でコードを書きつつ、サクサク rspec を実行したり Java プロジェクトをビルドしたり、 lint も実行したり、ということができるようになる、というのをゴールとしています。 エディタのカスタマイズを 愛する人 、エディタとコンソールを行ったり来たりしながら開発を進めている人におすすめの記事となります。 対象のエディタは Visual Studio Code です。 この記事のきっかけ この記事を書くきっかけになったのは私が、エディタを emacs から Visual Studio Code へ乗り換えたことに起因します。 emacs で rails アプリケーションを書いていたときは、 rspec -mode という emacs 拡張をインストールすることで、 rspec ファイルとテスト対象のファイルを行き来する カーソルのある行の rspec を実行 カーソルのあるファイルの rspec を実行 といったことがキーボードから行えました。 Visual Studio Code を使い始めてから、 Rails Go to Spec という プラグイン を見つけて、 rspec とテスト対象のファイルを行き来する はできるようになったのですが、カーソルのある行の rspec を実行するという事ができませんでした。そのため、 プラグイン でも書くか(エディタのカスタマイズは大好物です)、と思っていた所に良い方法をみつけたのでこの記事を書きました。 どのように実現するか 結論から言うと、 Visual Studio Code の標準機能のみで実現できました。その方法は、プロジェクトの tasks.json にタスクを追加することと、キーボードショートカットの割当です。 Visual Studio Code には、プロジェクトの ディレクト リ内にある、 .vscode/tasks.json ファイルにタスクを記述しておくことで、 Tasks: Run Task からプロジェクトに関連したタスクを実行できる、という機能です。 この機能と、 Tasks: Rerun Last Task へキーボードショートカットに割り当てることで、実現します。 では、早速 rails プロジェクトを対象に rspec を実行する場合を見ていきましょう。以下のような .vscode/tasks.json を作成します。 { // See https://go.microsoft.com/fwlink/?LinkId=733558 // for the documentation about the tasks.json format " version ": " 2.0.0 ", " tasks ": [ { " label ": " rspec file ", " type ": " shell ", " command ": " rspec -fd ${relativeFile} ", " group ": " test ", " problemMatcher ": [ { " owner ": " ruby ", " fileLocation ": [ " relative ", " ${workspaceFolder} " ] , " pattern ": { " regexp ": " ^rspec \\ s+(.*):( \\ d+) \\ s+# \\ s+(.*)$ ", " file ": 1 , " line ": 2 , " message ": 3 } } ] } , { " label ": " rspec here ", " type ": " shell ", " command ": " rspec -fd ${relativeFile}:${lineNumber} ", " group ": " test ", " problemMatcher ": [ { " owner ": " ruby ", " fileLocation ": [ " relative ", " ${workspaceFolder} " ] , " pattern ": { " regexp ": " ^rspec \\ s+(.*):( \\ d+) \\ s+# \\ s+(.*)$ ", " file ": 1 , " line ": 2 , " message ": 3 } } ] } ] } 作成したタスクは、以下になります。 タスク名 説明 rspec file カーソルのある rspec ファイルのテストをすべて実行する rspec here カーソルのある行の rspec のテストを実行する これで準備は整いました。 それでは、開発の流れを見ていきます。 rspec ファイルを編集していて、特定のテストを実行します。 Tasks: Run Task -> rspec here を選び、カーソルのある行の rspec のみが実行されます。 また、編集中のファイル全体の rspec を実行する場合は Tasks: Run Task -> rspec file を選択することで実行できます。 加えて、 Tasks: Rerun Last Task にキーボードショートカットを割り当てておくことで(私の場合はCommand + Shift + T)で、 rspec の作成する Tasks: Run Task -> rspec here` を実行 テストが失敗するのを確認 ソースを実装 キーボードショートカットから、 Tasks: Rerun Last Task を実行 Tasks: Run Task -> rspec here が実行される テストが成功するのを確認する Tasks: Run Task -> rspec file を実行 同じファイル内の他のテストが壊れていないことを確認 という開発ができるようになりました。 ちなみに、今回は rspec に特化したタスクを記述しましたが、 JavaScript を書いているときは、lint タスクを書く、 Java プロジェクトの場合は、ビルドタスクや JUnit タスクを書いておくことで、すべての言語で同じように開発が進められます。 めでたしめでたし。 新たに見つけた課題 と思っていたのですが、新しい課題を見つけました。 rspec here で、特定のテストを実行後、ソースファイルを編集した後に、カーソルと rspec ファイルのテスト対象行に持っていかないと、同じテストが実行できません。これは、地味に面倒くさい。できれば、 Tasks: Rerun Last Task Command のようなコマンドで、最後に実行したタスクのコマンドをそのまま実行する事ができれば、解決しそうですが、どうやらそのような機能は無いようです。 これは、機能追加の依頼をするか プラグイン を書かないと解決できなさそうです。 これは、来年の アドベントカレンダー の時にでも解決したいと思います。 最後に みなさん、斧を研いでいるでしょうか? 斧を研ぐと言っても、本当に斧を研ぐということではなく「きこりのジレンマ」のお話です。「きこりのジレンマ」というのは簡単に説明を書くと、 あるところに、忙しく木をきっているきこりがいました。彼の木を切るペースが日に日に落ちていきます。それを眺めていた人が、斧の切れが悪くなっているのでは?と感じ、斧を研いではどうか、と提案するのですが、きこりは忙しくて斧を研いでいる暇はないと答えた というお話です。 では、エンジニアにとっての斧は何でしょうか?いろいろあると思うのですが、エディタは斧の一つではないかと思います。 年1回、 アドベントカレンダー の時だけなどと言っていないで、日々斧を研ぎましょう。 Tasks: Rerun Last Task Command は他のみんなも欲しいのでは、という事を信じて、 Visual Studio Code のプロポーザルを書いてみたいと思います。近い将来。 本当に最後に 明日の記事の担当はエンジニアの橋野さんです。お楽しみに。 BUYMA を作っている、株式会社 エニグモ では、斧を研いで仕事を進めるエンジニアを求めています。ご興味のある方は以下の求人を御覧ください。 株式会社 エニグモ すべての求人一覧 hrmos.co
こんにちは、サービスエンジニアリング本部の寺田です。 軽く自己紹介になりますが、私は SIer で SE を2年間経験したのち、現職の エニグモ には 2020/7 よりジョインしております。 普段は主に Ruby on Rails を用いた BUYMA のサーバーサイド開発をやっています。 最近興味ある事は アルゴリズム で、週末には Atcoder にちょくちょく挑戦したりしています。 ちなみに、この記事は Enigmo Advent Calendar 2022 の7 日目の記事になります! 12 月はこのように弊社のエンジニアが記事を執筆しますので、ぜひお楽しみに! 組み込みメソッドをなぜ覚える必要があるのか? 私が嫌いなものそれは暗記です...なるべく覚えるものは少なく済ませたい、そんな思いが私にとって常にあるのです。そんな私にとってプログラミングを勉強したての頃に思ったことはこれでした。 「ぶっちゃけ for と if が使えれば何でもできるんじゃないのか?」 そうなんです。たいていのプログラムは for と if を使えば表せるのです。ではなぜ膨大な量の組み込みメソッドを覚える必要があるのか?? 一つ理由となるのは、それは読みやすい(=可読性が高い)からですよね。 ただ、組み込みメソッドを使ったプログラムが誰にとっても読みやすいかと言ったら、そうでもないですよね? # 配列の合計を計算する ## inject を利用した場合 puts [ 2 , 3 , 4 , 5 ].inject { |result, item| result + item } ## for ループを利用した場合 sum = 0 [ 2 , 3 , 4 , 5 ].each do |v| sum += v end puts sum 配列の要素の合計を計算するのに、 inject というメソッドを使ってます。これは知ってる人は簡単に読めるかもしれませんが、直感的には for ループを使用したプログラムの方が読みやすい気がします。 では、組み込みメソッドを絶対使った方が良いケースとはどんな場合でしょうか? それは、 「 for や if を使って簡単に書けるプログラムより圧倒的に早い場合」 これは絶対に組み込みメソッドを使ったプログラムの方がいいですよね? ruby には優れた アルゴリズム を使用して書かれたメソッドがあらかじめ存在しています。今回の記事では4つのメソッドと、これらに使用されている アルゴリズム について紹介していこうと思います! array#sort 問題 1~100000 までの数字がランダムに並び替えられた配列を、 昇順に並び替えてください。 for と if を使ったプログラム len = array.length len.times do |i| (len - 1 - i).times do |j| if array[j] > array[j + 1 ] array[j], array[j + 1 ] = array[j + 1 ], array[j] end end end for と if だけを使って簡単にプログラムを作成した場合、こんな感じでしょうか? ここで用いられている アルゴリズム は バブルソート というものです。 バブルソート の アルゴリズム を図解すると... 隣り合った数値同士を比較し、大小関係が合っていない場合は値を交換する。というのが基本の考え方になります。 1番目の要素と2番目の要素の大小関係を比較 2番目の要素と3番目の要素の大小関係を比較 ... 最後から一つ前と最後の要素の大小関係を比較 とすることで、一番最後の要素の値が確定します。 これを要素の数だけ繰り返すというものになります。 なので、ループの回数は要素の個数を n とすると、 n(n-1)/2 回で、計算量は となります。 オーダー記法 ここでオーダー記法(例に出てきた という書き方)を軽く押さえておきましょう。 オーダー記法はコンピューターによる計算がどれくらいの時間がかかるかを示したものです。 各計算量オーダーに対して、実際にどれくらいの計算回数になるのか表で見てみましょう。 10 3 33 100 100 7 664 10000 1000 10 9966 1000000 10000 13 132877 100000000 1000000 20 19931569 1000000000000 計算量はオーダーによってかなり異なることがわかります。 に比べて はかなり高速ですし、 よりは の方がとても高速です。 また、この差は数が大きくなればなるほど広がっていきます。 Ruby の組み込み関数を使用 ここで本題に戻りましょう。 Ruby の組み込みメソッドを使用して、この問題を解くならば、 Array#sort が使えます。 array.sort Array#sort で使われている アルゴリズム は クイックソート というものです。 手順としては以下の流れでソートを行います。 基準となる数値を決める 基準より小さい or 以上のグループに配列の要素を振り分けていく 要素が1つ以上あるグループにおいて、各グループ内で新たに基準を決める グループ内で基準より小さい or 以上のグループに配列の要素を振り分けていく この繰り返しを、それぞれのグループの要素が1つずつになるまで繰り返していき、最後に全てを結合することでソートを実現していきます。 計算量ですが、平均で になります。 最良のケースでは一度のループで各グループの要 素数 が、前回のループの半分の数になっていきます。 証明は省きますが、このように段々と 1/2 になっていき、最終的にn 個 => 1 個に要素が減っていくまでにかかる計算量は となると覚えておくと良いでしょう。 さらに、毎回のループで配列の長さである n 回の比較を行うため、 が計算量となります。 実際にプログラムを実行して計算量を比較してみたのが以下の実行結果です。 n = 100000 の場合、for ループによる バブルソート より、 Array.sort が 1000 倍も早いです。 Enumerable#bsearch 問題 1000000000 個の要素があるソート済みの配列の中から、 特定の数以上の値が初めて現れるインデックス番号を求めてください。 for と if を使ったプログラム array.each_with_index do |v, i| if (v >= 99999999 ) puts i break end end 非常にシンプルに考えるとこうでしょうか? このような アルゴリズム は 全探索 と呼ばれるものです。 配列の全要素に対して一つ一つ比較して答えを求めるものになります。 配列の長さだけループが走るので、長さ n の配列に対して計算量は です。 Ruby の組み込み関数を使用 では Ruby の組み込み変数、 Enumerable#bsearch を使った場合はどのような アルゴリズム になるでしょうか? array.bsearch { |x| x >= 99999999 } こちらで使用されている アルゴリズム は 二分探索 というものです。 配列の中から 2 という数字を探す場合を考えてみましょう。 こちらの アルゴリズム では以下の手順で答えを求めます。 左端と右端の値の中心の値を基準に、探したい数が基準より小さい or 以上かを求める 基準より小さい場合 => 探したい値は左半分にあるとわかる 基準以上の場合 => 探したい値は右半分にあるとわかる 要素が一つになるまで繰り返す こちらもループを繰り返すたびに、要素の数は 1/2 => 1/4 => 1/8 ... とどんどん半分になっていきます。 なので、要素が1つになるまでに必要なループのオーダーは になります。 これは全探索の よりかなり高速です。実際にプログラムを実行した結果を見てみましょう。 ものすごい違いですね。二分探索は全探索より 1000000 倍も早いという結果となりました。 Integer#pow 問題 を計算してください。 とても大きな数で累乗の計算をしてくださいというケースですね。 for と if を使ったプログラム ans = 1 ( 100000000 ).times do ans = ans * 3 end puts ans 累乗は言い換えれば底を指数の回数だけ掛け合わせるということです。 これを素直に実装すれば上記のようになるでしょうか。 指数の回数だけループして掛け算を行いますので、指数を n とすると計算量は になります。 Ruby の組み込み関数を使用 Ruby の組み込み関数には Integer#pow というものがあります。これは Integer#** と アルゴリズム は同じです。 3 .pow( 100000000 ) こちらは アルゴリズム として 繰り返し二乗法 が使われています。 例として を考えてみましょう。 愚直に計算するとループの回数は指数の数である 8 回です。 しかし は変換してみると、以下のようになることがわかります。 このように考えれば、ループの回数は4回で済みます。 この手順を一般化して考えると、以下のようになります。 求めたい値を、指数を 1/2 にした値を掛け合わせた式で表す 指数が 1 になるまで繰り返す 指数はループを経ることに 1/2 => 1/4 => 1/8 ... と半分になっていきます。指数が n ならば、1 になるまでに発生するループの回数は で非常に高速です。 実際にプログラムを動かして比較してみました。 非常に高速です。愚直な計算方法より 1000000 倍も早いことがわかります。 Integer#gcd 問題 463836510 と 692647128 の最大公約数を求めてください for と if を使ったプログラム ans = 0 ( 1 ...[a, b].max).each do |d| ans = d if (a % d == 0 && b % d == 0 ) end puts ans 最大公約数とは、二つの値を割り切れる最大の整数のことです。 この定義をそのまま実装するならば、上記のようなプログラムになるかと思います。 こちらは全探索の アルゴリズム となっていて、与えられた数が n とすると計算量は となります。 Ruby の組み込み関数を使用 最大公約数を求めるメソッドとして、 Integer#gcd が用意されています。 a.gcd(b) こちらは ユークリッドの互除法 と呼ばれる アルゴリズム が使用されています。 ユークリッドの互除法 というのは以下の定義を指します。 2つの 自然数 a, b について、a / b = q、a % b = r とすると、 「a, b の最大公約数」は「b と r の最大公約数」に等しい。 式で表すと以下のようになります。 ここで は a と b の最大公約数を表します。 これを繰り返すことによって、少ないループの回数で最大公約数を求めることができます。 例えば、629 と 481 の最大公約数を求めてみましょう。 最後のステップでは、148 を 37 で割った際のあまりは 0 になっています。 これは 148 と 37 では 37 が最大公約数になっていることを意味します。 ゆえに、 = = 37 と最大公約数が求まります。 そして、この アルゴリズム の計算量はざっくりと になります。 、 として、 より以下のことが言えます。 すなわち、 が成り立ちます。 2回のループを経ることで、確実に値が 1/2 以下になるわけですね。 先ほどから何度も出てきたパターンですが、値が 1/2 に減少していく アルゴリズム は計算量が となります。 実際に実行速度を比べてみましょう。 計算速度は 10000000 倍に速くなっています。 まとめ Ruby には良い アルゴリズム を使った組み込みメソッドが用意されている アルゴリズム の紹介として、 クイックソート 二分探索 繰り返し二乗法 ユークリッドの互除法 を紹介した。 良い アルゴリズム を使うことでプログラムは何万倍にも高速になることがある タイトル詐欺になってしまいますが、大事なことは Ruby のメソッドを覚えることではなく、どういう アルゴリズム があり、自分が直面した問題を解決するためにどの アルゴリズム を使うべきかを判断できるようになることだと思います。 Web エンジニアの裾野が広がってコンピューターサイエンスを学んだことのないエンジニアの方もいらっしゃるかと思います。この機会にぜひ興味を持ってもらえると嬉しいかなと思います! 明日の記事の担当は サービスエンジニアリング本部 の 後藤 さんです。お楽しみに! 株式会社 エニグモ すべての求人一覧 hrmos.co
こんにちは、2022年に新卒入社したエンジニアの川本と橋野です! この記事は Enigmo Advent Calendar 2022 の6日目の記事です。 エニグモ では社内の若手を中心にjunior workshopという名で勉強会を行っております。 経験の浅いメンバーの技術力アップを主目的としておりますが、興味のある方はどなたでも参加できる会となっています。(ベテランの方大歓迎です!) 勉強会の形式と、半年ほど勉強会をやってみて感じた、よかった点、今後やってみたいことなどを紹介できればと思います! よかった点 よかった点1:知見の共有 1つの技術書を読むのにも、1人で読むより、勉強会であれば様々な知見を持ったメンバーが集まっているので吸収できることが多いです。 技術書の内容を業務で実践したことがある人からは、具体的なエピソードを聞くことができますし、抽象的な概念の勉強をしている時は特に理解の助けになります。 よかった点2:社内交流が増える 勉強会は誰でも参加OKとしているので、様々なメンバーと交流できるいい機会となっています。 普段一緒に仕事をしないメンバーのことを知れたり、他部署の業務内容を知れたりと、コミュニケーション活性化や、会社理解の向上に繋がっていると思います。 以下のような形でSlackで勉強会当日にリマインドをしており、誰でも飛び入りでzoomのURLから参加可能となっており、参加の敷居はなるべく下げるようにしています。↓ エンジニア採用をやっている人事の方も参加してくれています! よかった点3:社内文化の形成 エニグモ には技術的関心が高いエンジニアが多いので、社内では下記のような技術力向上に向けての取り組みがされています。 Hacker's Delight エンジニアが個人的に学習していることや、業務で実践したことなどを発表するLT会です。週に1回開催しています! 開発合宿 コロナ以前は開発合宿も行われておりました!そろそろ再開できそう。。。? tech.enigmo.co.jp Ruby Kaigi 2022年は現地の 三重県 でオンフライン参加された方もいます! tech.enigmo.co.jp Kaigi on Rails 2022年は Gold Sponsors として参加しました! tech.enigmo.co.jp 若手勉強会もこうした社内文化の1つとして エニグモ に根付き、エンジニア組織全体のコミュニケーション活性化、技術力の向上に繋がると考えています。 勉強会の形式 輪講 という形式で、週替わりで担当者が勉強した内容をまとめてきて発表し、みんなが質問して議論していくというやり方でやっています。 22新卒が中心に開催している勉強会は、5月から開催しています。 そして、12月現在、2テーマ目に突入しています。 1テーマ目と2テーマ目の間には、振り返りを設けて、進め方を少し改善をしました。 初期から大切にしていることと、改善したことを紹介します。 初期から大切にしていること 勉強会を開催するにあたって、やりたいこと/やりたくないことなど開始前に話し合いました。 参加者が参加負担にならないようにし、勉強会を継続して開催したい。 受け身になりすぎないようにしたい。 この2つの意見を受けて、 みんなが予習必要ではない方式にする。 担当者は必ずしもまとめ記事を作ってくる必要はなく、最低限ファシリテートできるようにしておく。 開催出来なさそうであればその週はスキップをする。 以上のことを決め、無事に1テーマ目を終えることができました。 改善したこと 初期から大切にしていることに加え、改善した点があります。 以前は、勉強会の時間内に決めたところまでを読み終わらなかった場合、時間を延長してまでやっていました。 しかし、今は時間のキリのいいところで終わり、終わらなかった分は翌週にまわしています。 これは、1テーマ目の勉強会での反省にあった、「進度が早く、理解があまり出来ないまま進んでしまった。」という意見があったためです。 このため、現在のやり方では、みんなで疑問点を解消しつつ、意見交換をし、私たちのペースで進め、理解を確実にしていくことを大事にしています。 今後やりたいこと これまでの勉強会は技術書を読んできて、内容を議論するといった形式でしたが、もう少し手を動かしたいという意見もあります。 なので今後はライブコーディングをしながら下記のような方法も取り入れてみたいと考えています! AtCoder やLeetCodeの問題を解く。 SQL の問題を解く。 リファクタリング をみんなで考えて議論しながらやっていく。 まとめ 最近の勉強会は、担当者ごとに発表のやり方の個性がでていて、飽きず面白いです。 また、勉強会は個人にとっても会社にとってもメリットが多いと感じました。 これからも勉強会をさらに盛り上げれるように工夫していきたいと思います。 テックブログでも若手勉強会でやった内容をどんどん発信していければいいなと思っております! 明日の記事の担当は 私(川本)のメンターをしていただいているエンジニアの寺田さんです。 アルゴリズム についての記事です。お楽しみに! 株式会社 エニグモ すべての求人一覧 hrmos.co
この記事は Enigmo Advent Calendar 2022 の 5日目の記事です。 こんにちは。フロントエンドエンジニアのWooです。 エニグモ へ入社して3年目、主に BUYMA の購入者側のページをReactで構築しています。 BUYMA ではReactのグロー バルス テート管理のために主にReduxを使用していますが、今回は新しい取組としてRecoilを導入し、開発を行なってみましたので、その経験を共有しようと思います。 まず、軽くRecoilについて理解し、Recoilを導入するようになった理由、Recoilで開発する時に良かった点や困ったこと、他にリリースする際に失敗した話、最後にはこれからRecoilのより良い開発経験のための整理などをこの記事でお話ししたいと思います。 Recoilを軽く理解する RecoilはReactプロジェクトのための数多いグロー バルス テート管理ライブラリの中の一つで Facebook が2020年5月に出したライブラリです。なので、Reactを作った Facebook が出したRecoilは他のライブラリ(Redux, Mobx)とは違い、React専用ながらReactに最適化されたと言えるライブラリです。 Recoilを導入するようになった理由 2022年2月、 BUYMA では スマホ ウェブの検索絞り込み画面の使用性を高める企画が始まりました。 従来の画面はかなり前に作られたものだったので、画面の機能改善ではなく、新しい検索絞り込み画面を開発する方向に進められました。 私は新しい検索絞り込み画面のステートをどう構成すれば開発しやすくなるのかを悩みながらステート設計方法などを探していました。 そんな中、私はRecoilに接するようになりました。 新しく作る検索絞り込み画面は機能は多いながらも操作性は単純で軽い画面になる必要がありました。 Recoilはユーザーの絞り込み条件により、変更される数多いステートを柔軟に拡張・分解しながら開発ができそうと思いました。 さらにReduxに比べコードの量も多く減らすこともでき、少人数でもスピード感ある開発ができそうと思いましたので、検索絞り込み画面開発に適合だと判断、導入を進めるようになりました。 Recoilで開発した時の良かった点 まず、RecoilはReactのSuspenseと一緒に動作するようにデザインされていました。 コンポーネント をSuspenseで囲むと非同期処理などでまだ保留中の下位 コンポーネント をキャッチし、代替するUIをレンダーしてくれました。 これにより、データを取得している間のローダー表示を全体的に統一することができました。 その他、ステートを使う時にどんなエラーが生じるかの定義が簡単でした。それはErrorBoundaryでキャッチする仕組みでした。 ErrorBoundaryはエラーをキャッチし、エラーを記録し、クラッシュした コンポーネント ツリーの代わりにフォールバック用のUIを表示するReact コンポーネント でした。 グロー バルス テートの設定と定義が非常に簡単で、ステートの使用はRecoilが提供するHooksを利用、ステートをget/setすることだけでした。React Hooksの文法と似ていることで、初めてRecoilを書くエンジニアでもすぐになれる利点がありました。 また、グロー バルス テートを使用するためのボイラープレートの量が非常に少なく、全体的にラーニングカーブが低いというメリットがありました。 そして、ステートの変更や定義が頻繁に行われても既存コードとの影響度が低いため、開発要件によるステートの変更でも柔軟に対応が可能でした。 propsを渡さなくても良い特徴では コンポーネント の リファクタリング や分割などが容易でした。 API の非同期の処理ではユニークなインプットがある時のみ実行されるようにキャッシュされる処理があり、ユーザーの同じアクションを防ぐなどの実装を考慮しなくてもよい便利さがありました。 Recoilで開発をする時に困った経験 ユーザの絞り込み条件変更によるReactの処理では、Suspenseを利用しました。 Suspenseは API の非同期処理を待機中の下位 コンポーネント の代わりにローダーをレンダーしてくれましたが、実際に動作を確認すると絞り込み条件変更の度に表示される真っ白のローダー画面が与える印象は求めている操作性の軽い画面とは違うように感じられました。 非同期データを使う最小限の コンポーネント をSuspenseで囲む コンポーネント 構造が設計可能であれば、全画面ローダーを表示する必要はないかもしれません。 しかし、検索絞り込み画面はヘッダー以外のところが非同期データによってレンダーされる コンポーネント のため、前述の方法の設計ができませんでした。 そうして考えた方法は、単なるスピナー表示ではなく、スピナーを絞り込み項目と一緒に見せる方法でした。 スピナーに絞り込み項目も含めSuspenseに渡すことになったので、このローダー コンポーネント は肥大化されましたが、パフォーマンスに大きな影響はありませんでした。各 コンポーネント ごとにステートを使っているおかげで、 リファクタリング はJSX部分の簡単な修正で済みました。 Recoilで開発したコードをリリースする際に失敗した話 BUYMA のReactコードは、babelを通してES5コードに変形した後、もう一度uglifyを行う過程を経ることになります。 このES5に変形する過程ではnode_modulesを含みません。 問題はnode_modulesにあるRecoilライブラリを OOO _app.jsの結合ファイルに含めてuglifyしようとした時に起きました。 uglifyはES6コードを解析できず、圧縮に失敗していました。 Recoilライブラリは主にES6で書かれていたのが原因でした。 リリースの過程でしか確認できないファイル圧縮の失敗は予想できなかったのです。 結局、他の代案を探さなければなりませんでした。 RecoilライブラリをES5に変形する方法など考えてみましたが、おすすめしない方法だとネット上では勧告していました。 近年 BUYMA は IE をサポートしなくなりましたので、結合のファイルをES5に変更する過程をなくす方法もありました。しかし、その方法は影響範囲が多く、少人数で解決できないと考えられました。 そんな中、 BUYMA はReactライブラリを CDN ロードする方式で運用されていることが思い出しました。 近いところの一番簡単な方法が見つからず、遠いところの難しい方法だけを考えていました。 結局、Recoilの CDN を利用してuglify問題を解決する方法を採用することになりました。 Recoilのより良い開発経験のための整理 最近はまた別の画面でRecoilを使って開発をしました。 2回目の開発経験では、 命名 部分の理解度を高めるとRecoilを知らないエンジニアでもコードが理解しやすいかもという考えを持ちながらコードを作成しました。 selectorは以下のように関数の名前を 命名 しました。 意味的に派生したステートの名前を 命名 しようとしました。 コンポーネント では、上記の派生したステートの名前を簡単に理解できるようにコードを作成しました。 Recoilのフォルダとファイルの構造はステートの単位を意味するatomsと派生のステートを修正して新しい結果を作るselectorsに分離して使用することが便利でした。 /api /atoms /components /containers /hooks /selectors Recoilの開発の際にはReduxの開発の際と同様、意図しないステートの更新などを確認する必要があるので、開発ツールとして DebugObserver は必須だと思います。 ReduxのDevToolほど強力なツールではないと思いますが、シンプルな更新ログの形でいつも Chrome のDevToolsを開いて開発する私には見やすくてわかりやすかったので、不便ではありませんでした。 終わりに Recoilに対する小さな経験を話すことができて嬉しいです。 今度機会があればRecoilの多様なユーティリティ機能の使用経験を整理してみたいと思いました。 開発ツールの不在などReduxよりパワフルではないと思いますが、Reactフレンドリーな書き方や簡単な非同期処理など、開発しやすいRecoilライブラリの長所と魅力をこの記事で少しでも伝えられましたらと思います。 関連記事 Recoil: https://recoiljs.org/docs/introduction/installation グロー バルス テータス管理ライブラリ Recoil : https://abangpa1ace.tistory.com/212 株式会社 エニグモ すべての求人一覧 hrmos.co