EnvoyCon 2018 Seattle で登壇しました

f:id:aladhi:20181217091426p:plain

こんにちは、Taiki です。先日、Envoy proxy のためのカンファレンスである EnvoyCon 2018 がシアトルで開催され、参加・登壇してきたので、私の発表内容や他の登壇者の内容をいくつかこの記事では紹介できればと思います。また、EnvoyCon は KubeCon + CloudNativeCon North America 2018 の co-located event として開催されたのもあり、KubeCon + CNCon にも参加してきたので、そちらのほうからも Envoy 関係の発表をいくつか紹介します。

クックパッドの発表

クックパッドではおよそ1年前から Envoy をサービスメッシュの data plane proxy として利用しています。クックパッドは Amazon ECS を使用しているのもあり、サービスメッシュの構築にあたり、自分たちの requirements や環境に合う製品が見つからなかったので、自社で control plane を開発するアプローチを採用しました。事前に生成した Envoy proxy 向けの xDS response を Amazon S3 を用いて配信する、という設計は Envoy ユーザーの中でもユニークなものであり、その設計の詳細や運用の側面について紹介しました。

EnvoyCon での発表

時系列順にいくつかご紹介します。

まずは Google の Harvey から、Envoy 開発の統計をもとに Envoy の開発状況について誰がどのように開発しているのか、という内容のトークがありました。OSS のユーザーとして、利用している OSS の開発状況やコミュニティは、その OSS プロダクトの将来性に関わることもあり関心があるので、興味深いセッションでした。クックパッドからも私がパッチを送っているのもあり、スライドに少し登場しています。

f:id:aladhi:20181217091530p:plain

次に eBay から Bala と Qiu が、 edge proxy としての Envoy 利用事例の発表をしました。eBay の PoP の中で動いていたハードウェアロードバランサーを、visibility の強化やより柔軟なトラフィックルーティングを実現するために、Envoy で置き換えるというチャレンジングな内容でとても楽しめました。

次に Yelp の Ben と John から、Yelp でのサービスメッシュの進化と data plane の Envoy への置き換えについてセッションがありました。Yelp では以前より HAProxy ベースのサービスメッシュを運用していて、そのサービスメッシュの改善・進化についての発表でしたが、”小さく始める” ポリシーやプロダクトの開発に集中するといった考え方がクックパッドでの考え方に近く、親近感を持ちながらセッションを聞きました。

次に、Google の Alyssa から、 Envoy のセキュリティ面での強化(hardening) について発表がありました。Google では全てのトラフィックを受け持つ frontend proxy での Envoy 利用が進んでいるようで、そのために Envoy のセキュリティ面、特に攻撃に対する耐性の強化についての発表でした。こちらは個人的に興味関心がある分野で興味深く聞きました。

他にも非常に興味深い発表が盛りだくさんでした。Twitter の #envoycon ハッシュタグ でも様子を少しつかめるのでぜひご覧ください。

KubeCon + CNCon での発表

Lyft から Daniel が、 database traffic に Envoy を利用している事例について発表しました。主な目的はメトリクスの確保です。Envoy はすでに Redis, DynamoDB, MongoDB への通信に対応したフィルターを実装しており、それを利用してどのような利点があったか、という内容でした。クックパッドでは MySQL, PostgreSQL の利用が主であり、Envoy のフィルターの実装を待つか手伝う必要があるのですぐには実現は難しいのですが、今後導入を図りたいと個人的に考えています。

Keynote, Envoy Update: Lyft から Matt, Constance, Jose の3人が Envoy の近況についてキーノート発表をしました。今回の KubeCon + CNCon での Envoy の盛り上がりを感じられるセッションかなと思います。

KubeCon + CNCon はすでにある程度 YouTube にセッション動画がアップロードされています。EnvoyCon も録画をしている様子があったので、そのうちアップロードされるのではないかと思います。

終わりに

Envoy やサービスメッシュに関連する事例を共有しあったり、現在の課題や実現を目指していることなどをお互いに話し合える場は大変貴重で、とても楽しみ、また数々の学びがありました。

クックパッドという会社はこのような海外登壇であっても強力にバックアップしてくれます。また、先進的な領域で問題に取り組み成果を出しやすい環境があります。興味のある方はぜひ下記のリンクよりコンタクトください。

https://info.cookpad.com/careers

We’re hiring!

本番/ステージング環境GPUぼくめつ大作戦

機械学習チームの林田(@chie8842)です。好きなスポーツはテニスとスノボです。

システムは、その当時の最新の技術で作ったとしても必ずレガシー化します。 機械学習システムも他システムと同様、一度デプロイしたら終わりではなく、継続的なメンテナンスが必要です。昨今機械学習は、特に技術の進歩が目覚ましいため、レガシー化するのも早い分野といえます。本稿ではレガシー化した機械学習アプリケーションのメンテナンスと、それに伴うGPU環境からCPU環境への移行によって、大幅にシステムの運用コストを削減した例をご紹介します。

機械学習アプリケーションにおけるコスト課題

クックパッドにおける最初の大きな機械学習プロジェクトである料理きろくがリリースされたのは、2年前のことです。それ以来、様々な機械学習アプリケーションがデプロイされ、現在では大小含めて30を超える機械学習アプリケーションが運用されています。そのうち、10 個のアプリケーションがGPUサーバ上で運用されており、常時数十台のg2.2xlargeインスタンス1が起動している状況でした。

ここで、これらのGPUインスタンスについて、コスト面における問題が2つありました。 1つは実行環境であるEC2インスタンス料金です。GPUインスタンスは他のGPU非搭載インスタンスと比べて単位時間あたりの使用料金が非常に高価です。たくさんのGPUインスタンスが稼働していることで、クックパッド全体のサーバコストを逼迫させているという問題がありました。 2つめはメンテナンスにかかる人的コストです。GPUサーバをアプリケーション環境として利用することで、通常のモニタリングメトリクスに加えてGPUのリソース監視が必要になります。さらに、コンテナからデバイスドライバを使用するための権限設定など、特別な対応が必要となり、将来的にも人的メンテナンスコストがかかるという問題がありました。

機械学習における処理パフォーマンス

機械学習には、データの特徴をよく表すモデルを作る「学習」と呼ばれる過程と、 作ったモデルを使って分類問題を解くなどの「推論」と呼ばれる過程に分けることができます。 例えば、犬と猫の画像を使って、犬と猫を分類するモデルを作るのが「学習」過程、そのモデルを使って犬と猫の画像を分類するのが「推論」の過程です。

クックパッドでは、GPU環境を利用して定期的にモデルを再学習しているアプリケーションはなく、GPU環境のアプリケーションは、全て「推論」に利用していました。

GPUには、並列処理を高速化できるという特徴があります。機械学習の中でも、特にディープラーニングなどのニューラルネットワークを用いる場合に、GPUが用いられることはよく知られていますが、顕著に処理パフォーマンス上効果的なのは、以下の場合です。

  1. ディープラーニングにおける「学習」処理2
  2. 非常に高いパフォーマンスが要求される「推論」処理

実はディープラーニングを使った機械学習アプリケーションでも、GPUが必要なケースというのは、意外と限られているのです。 例えば、Applied Machine Learning at Facebook: A Datacenter Infrastructure Perspectiveという論文では、以下のように、機械学習システムを大規模に利用しているFacebookのサービスでさえ、推論にはCPU環境を利用しているということが、語られています。

Facebook currently relies heavily on CPUs for inference, 
and both CPUs and GPUs for training, 

クックパッドでは、モデル学習にGPUを使っていますが、先に書いたとおり、アプリケーションとして実行しているものにおいて、1.に当てはまるものはありません。また、ユーザに対してレスポンシブに応答を返したり、大量のデータを処理したりするといったものはないため、2.についても回避できる可能性が高いということが分かっていました。 そこで、必要なものはアプリケーションのチューニングを行い、GPUアプリケーションを全てCPU環境に移行することにしました。

CPU環境への移し替えに伴うサービス影響確認とパフォーマンス・チューニング

GPUで動作しているアプリケーションをCPU環境に載せ替えるにあたり、まずはCPU環境への移行によるパフォーマンス影響の調査を行いました。 Webシステムにおけるパフォーマンスの重要な指標として、「スループット」と「レスポンスタイム」があります。 スループットとは、一定の時間内に処理できるトランザクション数、レスポンスタイムとは、クライアントからのアクセスに対しての応答速度です。 目標とするスループットやレスポンスタイムが決まっていれば、それらにあわせればよいですが、今回は目標が明確に決まっていないアプリケーションが多い状態でした。そこでまずはGPUを利用した場合とCPUを利用した場合のパフォーマンスの差を測定し、それをもってプロダクトオーナと協議するという方式をとりました。

以下の節では「料理きろく」3という写真に対して料理/非料理判定を行う機械学習アプリケーションの場合を例にとって、パフォーマンス計測とチューニングの進め方を紹介します。 料理きろくの詳細については以下をご覧ください。

techlife.cookpad.com

techlife.cookpad.com

チューニング前のパフォーマンス

料理きろくの機械学習部分は、TensorFlow v1.2.1で実装されていました。 GPUインスタンス上で、アプリケーションの推論部分のみをtensorflow-gpuパッケージとtensorflowパッケージ[^3]で動作させた結果、以下のとおり、tensorflow-gpuを利用したほうが約18倍速いことがわかりました。

GPU CPU
レスポンスタイム 0.0723秒/枚 1.2776秒/枚

なお、上記レスポンスタイムは、下記のように100枚の推論の平均値をとっています。

elapsed_times = []
for i in image_list:
    image = Image.open(i)
    start = time.time()
    model.infer(image) # 推論処理
    elapsed_time = time.time() - start # 実行時間
    elapsed_times.append(elapsed_time)

print(mean(elapsed_times)) # 実行時間の平均値の表示

TensorFlowのチューニング

まずはTensorFlowのレイヤーで、レスポンスタイムを縮めるようチューニングしました。 料理きろくのために行ったチューニングの中では以下の2つの項目が効果的でした。

  1. TensorFlowバージョンアップ
  2. tf.Sessionを使い回すようにする

順に説明します。 まず1.についてです。TensorFlowは、非常に開発が活発なソフトウェアです。バージョンが上がるごとにTensorFlow自体の高速化も行われているため、単純にTensorFlowのバージョンを上げることで、パフォーマンスが向上することが予想できました。このため、今回は思い切ってTensorFlowのバージョンを1.2.1から最新の1.12.0に上げました。バージョンアップに伴いリファクタリングは必要でしたが、モデル自体はv1.2.1で学習したものをそのまま使うことができました。

次に2.についてです。TensorFlowでは、定義した内容を実行するために、tf.Sessionというセッションが必要です。 このtf.Sessionの起動にはオーバヘッドがあるため、複数の処理ごとに張り直すと、それだけで処理が非常に重くなります。そのため、1つのセッションをできるだけ使い回すように修正しました。

元のコードの書き方

    def hoge():
        with tf.Session as sess:
            sess.run()


    def fuga():
        with tf.Session as sess:
           sess.run()

セッションを使い回す場合の書き方

sess = tf.Session()

def hoge(sess):
    sess.run()

def fuga(sess):
    sess.run()

(省略)

sess.close()

その結果、元のCPU版と比べてレスポンスタイムが5分の1程度になりました。

GPU CPU(チューニング前) CPU(チューニング後)
レスポンスタイム 0.0723秒/枚 1.2776秒/枚 0.25秒/枚

Dockerコンテナリソースのチューニング

TensorFlowレイヤーにおけるチューニングはここまでとして、次に実際本番環境と同等のCPU環境で動作させる場合のアプリケーションとDockerコンテナリソースをチューニングしました。 CPUで動作させる場合の本番環境は、c5.xlargeインスタンスからなるクラスタを利用します。4 そのためc5.xlarge環境で、リソースをモニタリングしながら、アプリケーションを実行します。 今回の検証ではリソースモニタリングツールとしてsarを利用しました。sarを採用した理由は、非常に軽量で扱いやすく、時刻付きのテキスト形式でログを保存しやすいためです。

結果として、Dockerコンテナ上で、ホストのリソース利用制限を行わずにアプリケーションを実行した結果、レスポンスタイムとリソース使用率は以下のようになりました。

レスポンスタイム CPU使用率 メモリ使用率
0.474秒/枚 93%程度 20%程度

TensorFlowは、処理が1プロセスであっても、デフォルトで使用可能なCPUすべてを使います。 1プロセスでCPUをほとんど使ってしまうので、1ホスト上で実行するアプリケーションのプロセス数やスレッド数を増やしても、レスポンスタイムが低下してしまい、スループットもそれほどあがらないだろうということがわかります。このため、1ホスト1コンテナ構成で、スループット確保のためには、スケールアウトを行うしかない、という結論となりました。

移行

上記の実験から、GPUからCPUへの移行のパフォーマンス影響としては、以下のとおりとなりました。

  • レスポンスタイムがGPUを利用した場合と比べて0.47秒程度増える
  • スループットはサーバのスケールアウト(オートスケール)により担保する

この内容でプロダクトオーナーに了解をとり、移行を行いました。 クックパッドでは、Hakoという、Kubernetesライクなコンテナオーケストレーション環境が導入されており、移行自体は、Dockerイメージの更新及びHakoの定義ファイルの更新のみを行えば、スケールアウトも自動で行われ、サーバ作業を行うことなく実行できます。 Hakoについては、詳しくは以下の記事を参照してください。

techlife.cookpad.com

最初はGPU環境と並行運用し、最終的にCPU環境のみを残すようにして、移行します。

コスト削減の結果

現在移行による並行運用中のアプリケーションもありますが、料理きろくも含めてすべての移行対象アプリケーションをCPU環境で動作しています。

これによるサーバコスト削減効果を算出したところ、EC2利用料金は元の6分の1程度となり、年間1,500万円以上の節約となることがわかりました。

その他の有効なチューニング

今回はチューニングの一例として、料理きろくをCPU環境に移行する内容を紹介しました。 最後に、料理きろくのチューニングではやらなかったけどその他の一般的に有効なチューニング手法を3つだけ紹介します。

モデルアルゴリズムの変更

料理きろくのモデルは、InceptionV3ベースです。モデル更新を行う必要があるため、今回はしませんでしたが、最近では、MobileNetなどの軽量モデルが発展しており、こうしたモデルに置き換えることで、パフォーマンスが向上する可能性が大きいです。

プロセス数/スレッド数の調整

料理きろくでは、TensorFlowの演算によるCPUがボトルネックとなったため実施しませんでしたが、1プロセスでCPU、メモリ、IOといったリソースを十分に使い切れていない場合、WSGI等を利用してプロセス数、スレッド数を増やすことで、1ホストあたりのスループットをあげることができます。

共有メモリの利用

上記において、例えばプロセス数を増やした場合、各プロセスがメモリ上にモデルをロードすることになります。現状クックパッドで運用しているモデルには、それほど巨大なモデルがありませんが、例えば今後以下ブログにおいて紹介したBERTなどの巨大なモデルを利用したいとなったときは、プロセス間で同じ共有メモリ上のモデルを利用するようにアプリケーションを記述することが有効になるかもしれません。

techlife.cookpad.com

さいごに

機械学習というと、新しい手法を試し、ハイパーパラメータチューニングを行ってモデルの精度を高めることに興味がある人が多いでしょう。しかしながら、機械学習をサービスに活かすためには、こうしたアーキテクチャ面における「チューニング」も重要な仕事であることを分かっていただけたなら嬉しいです!


  1. AWS上のGPUサーバ

  2. 最近はGPUが必要ない軽量なモデルもある

  3. tensorflow-gpuはGPU版、tensorflowはCPU版のPythonのTensorFlowパッケージ

  4. 厳密にはc5.xlarge以上のリソースを持つインスタンスタイプが混ざったスポットインスタンスからなるクラスタ

毎週リリースを実現するテスト活動

こんにちは。 品質向上グループの茂呂一子(@ichiko_revjune)です。

クックパッドアプリは、サブミット・リリース作業を自動化して、アプリを毎週サブミットするようになりました。これを実現するリリースフローについては、 クックパッドアプリはみんなが寝ている間にサブミットされる で紹介しました。

このリリースフローを実現していく過程では、「機械に人間があわせる」という方針で、サブミット・リリース作業が自動化されていきました。つまり、毎週サブミット・リリースをするためには、何をどのように自動化するべきかという視点で自動化する対象が決まっていきました。

アプリは開発が終わればすぐにリリースできるというものではありません。この記事では、リリース前のテスト作業をどのように調整して、毎週リリースを実現しているのかを説明していきます。自動サブミットの導入はiOSアプリが先行したため、ここではiOS版クックパッドアプリについて説明します。

自動サブミット導入以前のリリース前テスト

この記事 にあるように、以前のリリースフローでは、開発期間、コードフリーズ、テスト期間を経てリリースをしていました。このテスト期間に行うテストをリリース前テストと呼びます。テスト期間は、3〜4営業日でした。

リリース前テストの内容は大きく3つです。

ひとつめは、UI操作を伴うシナリオテストを複数のテスト環境(iOSデバイスとiOSバージョンの組み合わせ)で実行することです。このシナリオテストは、Appiumを使って機械的に実行しています。

ふたつめは、自動化がむつかしいが重要な機能を手動でテストすることです。例えば、In App Purchase に関する機能や、写真撮影が必要な機能などです。

みっつめは、各リリースに含まれる変更に関連した機能を手動でテストすることです。変更内容から起こりそうな不具合を想像し、それを実際に確認するというような作業です。探索的テストと呼ばれるものに近いでしょう。

これらを実施して不具合が見つかれば修正し、リリース前にその修正がうまくいっているかを確認していました。

自動サブミット導入のために

自動サブミット導入後もリリース前テストはなくなりません。スケジュールされた自動サブミットが完了し、テスト対象が確定してからリリース前テストが始まります。

先に説明した、以前のリリースフローにあわせて設計したテストをそのまま実施すると3〜4営業日かかることになり、リリース前テスト以外の活動にあてられる時間がなくなってしまいます。わたしたちは自動化したシナリオテストを持っているため、テスト実施以外にも、これらをメンテナンスする時間は必要です。また、テストツールを改善したり、リリースフローを改善するなどの活動もリリースを続ける上では欠かせません。テストのやり方か内容を変更しなければ、毎週のサブミットに耐えられなくなってしまいます。

自動化できるところは自動化し、人間にしかできないことを人間が行うという方針で、テスト作業を効率化してきました。自動サブミット導入当初はサブミットからリリースまで、2〜3営業日かかっていました。しかし、最近は2営業日でリリースできるようになってきました。

幸いにして、大きな不具合を出すことなく、毎週のサブミットとリリースが実現できています。テスト作業をどのように変えていったのかを課題ごとに説明していきます。

一番時間がかかるのはシナリオテスト実行

既に自動化してあった200件弱のシナリオテストは、ひとつのテスト環境(iOS デバイスとiOSバージョンの組み合わせ)での実行に10時間以上かかっていました。加えて、以前はシナリオテストをCIマシン上で実行させる為の環境整備が追いついておらず、手元のPC上で実行していました。そのため、毎晩の就業時間外にひとつのテスト環境を選び、実行していました。

リリース前テストでは、iPhone/iPadいずれの環境でも動かすことと、サポートiOSバージョンを網羅することを満たすよう、複数のテスト環境で実行します。したがって、1リリースあたりのシナリオテストにかかる時間は、10時間かけるテストする組み合わせの数でした。

これらの問題はCIマシンでの実行と、シナリオテストの並列実行という方法で実行時間を短縮しました。

今年に入ってから、CIマシン上で実行することは試みていました。そして、自動サブミット導入の頃に準備が整い、本格的に実行をCIマシンに任せるようになりました。シナリオテストを実行しているCI環境はビルド用のCI環境とは別のものを使用しています。理由はふたつあり、ひとつめは、実行にかかる時間が長いため短時間で終了するビルドタスクと混ぜて実行するべきでないためです。ふたつめは、最新のOSでは動作が不安定になりやすく、ビルド環境と同じタイミングでOSを更新できないことが多いためです。

シナリオテストの並列実行は、今年チームに加わった加藤が、実行の高速化を目的に実現してくれました。並列実行と呼んでいるのは、同じデバイスとiOS を使用するiOS Simulatorを複数起動し、別のシナリオを同時に実行することです。UIテストのボトルネックのひとつにSimulatorを操作する時間があるため、Simulatorを増やすことに効果がありました。これによって、1つのテスト環境でのテスト実行にかかる時間が約3分の1になり、実行時間の総量も同じように少なくなりました。

実行時間の短縮という他にも、金曜日から日曜日にかけてシナリオテストを実行するようスケジュールして、実行開始が遅延してもシナリオテストが終わるのを待つ時間が発生しにくいように工夫しています。自動サブミットは金曜日の早朝に動きだすので、順調にいけば金曜日の午後にはシナリオテスト結果がそろい、レポートを見れるようになっています。

シナリオテスト実行の指示を毎回人間がしていた

シナリオテストは、1回のリリースのために複数のテスト環境で実行しています。テスト環境は固定しておらず、リリースごとに変え、ある程度の期間でみたときに、iOSデバイスサイズとiOSバージョンを網羅するように設計してきました。

自動サブミット導入以前は、このテスト環境の選定を人間が行い、実行のたびに指定していました。

自動サブミット導入後は、このテスト環境の選定をスクリプト化し、実行のたびに指定しなくて済むようにし、機械による無作為抽出を導入しました。

機械に任せる領域を増やして、リリース前テストの準備をひとつ減らしました。

お手本画像を人間が更新していた

シナリオテストツールには、表示くずれを見つけるために、シナリオテスト中で取得したスクリーンショット画像をお手本画像と比較して、差分を表示する機能があります。

このお手本画像の管理には2つの課題がありました。ひとつめは、テストツールの実行環境ごとにファイルを管理しており、すべての環境で同じファイルを使っている保証がなかったことです。ふたつめは、人間が差分画像を見ながら更新すべきファイルを探し、コピー&ペーストでお手本画像を更新していたことです。実行環境が複数存在したため、特定の環境への反映漏れということも起きていました。

この作業を改善するにあたって、まず、お手本画像はAWS S3に一元管理するようにしました。実行のたびに、S3から最新のお手本画像を取得し、どこでも同じお手本を使用するようにしました。

次に、更新作業の一部をスクリプト化し、更新するべきかの判断だけを人間が行うようにしました。人間は差分画像の一覧をみながら、更新する画像にマークをつけます。その選択結果をファイルに出力し、それをスクリプトに与えて実行すればS3上にファイルが送信されます。

単純作業を機械に任せることで、人間は不具合の探索に集中しやすくなりました。

シナリオテスト結果から不具合をみつける手助け

シナリオテストは、UI操作を伴うため、不具合がなくてもシナリオが失敗することがあります。本当に不具合があるのかは最終的に、人間が実機を操作して判断することになります。その失敗が不具合の可能性が高いのか、低いのかを知ることで、この再操作を減らすことができます。

失敗しやすいシナリオや不安定なシナリオを見つける目的で、シナリオごとの実行結果を蓄積、可視化するアプリケーションを構築しました。このアプリでは、同じバージョンのアプリに対する複数のテスト環境での実施結果の成否を一覧できます。「どのテスト環境でも失敗しているので不具合の可能性が高い」「ひとつの環境だけ失敗しているので不具合の可能性が低い」といった具合に参考情報を簡単に見つけることができるようになりました。

f:id:ichiko_revjune:20181212111114p:plain
シナリオごとの実行結果を蓄積、可視化

このアプリがない頃は、複数のレポートを開いて見比べていました。成功失敗というデータに絞って可視化することで、ずいぶんと容易に理解できるようになりました。

自動化できない手動テストの効率化

自動化することはできないけれど重要な機能、例えば In App Purchase に関する機能は毎回手動でテストをしています。リリースごとの追加機能を対象とするテストではないので、ほぼ内容は決まっています。テスト作業の見直しを始めた時点では、削ってしまえる機能もありませんでした。

これらの手動で実施するテストの項目は、厳密なテスト手順を定めておらず、この機能でなにができること、という満たすべき項目をリストにしています。探索的テストのチャーターのようなものでしょうか。

自動サブミット導入以前も、関連機能に変更がなければ省略してもよい項目はあり、省略することもありました。自動サブミット導入後は、これを厳格に適用して、関連機能に変更がなく不具合が発生しないであろう機能へのテストを省略するようにしました。合理的にテストしすぎない工夫としては、うまくいっている方ではないでしょうか。

これからの課題

小さな改善を重ねてリリース前テストに時間をかけ過ぎることなく、毎週のリリースを実現している方法を紹介しました。

サブミット自動化後のリリースフローは、リリース前テストで致命的な不具合が見つかった場合、リリースをあきらめるルールです。このフローがうまくまわっていくためには、不具合が入りにくいしくみや、機能変更をマージする前に不具合に気づきやすくするしくみが必要です。そのためには、開発チーム内のテスト技術を向上させることも必要でしょう。これらの点ではまだ効果的なアクションを取れていないので、急がず確実に小さな変化を積み重ねていきたいです。