Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
RESTfulなWebサービスを記述、生成、利用、可視化するためのインターフェースファイルの仕様です。 以前はSwaggerフレームワークの一部でしたが、2016年にOpenAPI Initiativeが統括する独立プロジェクトとなりました。 Swaggerや他のいくつかのツールは、インターフェースファイルを指定してコード、ドキュメント、テストケースを生成することが可能です。
プレセナの一部プロジェクトでは、API定義をOpenAPI仕様に従って記述するだけでなく、定義ファイル(openapi.yaml)からコードを自動生成して利用しています。
当初はAPI定義ファイルであるopenapi.yamlの1ファイルに全ての記述をしていました。 結果として、下記のような問題が発生しました。
ファイルサイズの増加(約2500行)
コンフリクトの頻発
編集すべき記述を素早く見つけられない
これらの問題に対処するためにopenapi.yamlの分割をおこなうことにしました。
openapi.yamlのファイルは大きく分けると下の様なブロックに分かれています。
バージョン情報といったメタ情報:info、serversなど
エンドポイントのURLやリクエスト/レスポンス情報:paths
再利用可能なオブジェクト情報:components
主に記述量が肥大化してしまうのは pathsとcomponentsであったため、これらを別ファイルに分割しました。
ファイルの分割は簡単です。
定義内の他コンポーネントを参照できるようにするフィールドである$ref
を相対パスで記述するだけです。
openapi.yaml
/paths/pet.yaml
/schemas/Pet.yaml
openapi.yamlをルートファイルとして、同階層にpathとschemasのフォルダを作成しました。
paths配下のファイルの命名規則としては、シンプルにエンドポイントURLの「/」を「_」にするファイル名としました。
/paths/pet/find-by-status.yaml
のように階層を深くするアイデアもあったのですが、schemasへの参照を$ref
で記述する際、"../../schemas/Foo"
, "../../../schemas/Bar"
のように 「..」を書く回数の混乱が生じないように現在のような命名規則をとりました。
エンドポイントURLにidが含まれる場合(ex. /pet/{petId}/upload-image)、理想的にはpet_[petId]_upload-image.yaml
のようにしたかったのですが、「[]」を含むと自動生成コードで問題が生じたため、シンプルにpet_petId_upload-image.yaml
としました。
OpenAPIの正確な仕様については、↓から確認できます。
分割によって見通しがよく開発をすすめることができるようになりました。
Visual Studio Codeで開発しているなら、この拡張をいれることで$ref
の記述からファイル参照もできるのでおすすめです。
しばらくこれで開発を進めてみて、課題感がまた出てきたらブラッシュアップしていきたいと思います。
株式会社プレセナ・ストラテジック・パートナーズのエンジニアのための知識ベースとして使用しているオンラインサイトです。
株式会社プレセナ・ストラテジック・パートナーズにおけるシステム開発で発生した各種調査、検討結果や他のメンバーへ共有したいノウハウなどのうち、社外にも公開できるものをコンテンツとして蓄積しています。
以下のような分野に関係するコンテンツを記載しています。
ソフトウェア開発
インフラ開発
セキュリティ
上記以外も、システム開発時に新たに進出した分野があれば追加していく予定です。
株式会社プレセナ・ストラテジック・パートナーズ社内のエンジニア、デザイナー
社外のシステムエンジニア、デザイナー
本サイトのコンテンツは可能な限り正確を期して記載していますが、本サイトのコンテンツに基づく運用結果については、株式会社プレセナ・ストラテジック・パートナーズは責任を負いかねますので、ご了承の上、内容を閲覧してください。
本サイトの更新情報は、Twitterので発信しています。ご確認ください。
この記事は、独特なネーミングがされていて、知っているようで知らないHomebrewの用語の意味を正しく理解するために書きました。
Homebrewは「自家醸造」の意味です。この「醸造」の世界観の元で、Homebrewで扱う一部の概念の名前がつけられていて、直感的に何を指すのかが少しわかりにくいことがあります。
インストール関連のトラブルやPATHを通すパッケージのバージョンを調整したいときに、これらの用語を知っていると、エラー・警告メッセージの内容や、brew info
コマンドなどで説明されている内容を理解しやすくなると思います。
formula (製法)
homebrewでupstreamのソースからビルドするパッケージの定義。formulaeは、その複数形。
cask (大きいたる)
macOSネイティブアプリケーションをインストールするパッケージの定義。
keg (小さいたる)
任意のformulaの任意のバージョンのインストール先ディレクトリ。 例 : /usr/local/Cellar/[formula]/0.1
rack (棚)
1つ以上のバージョンのkegを含むディレクトリ。 例: /usr/local/Cellar/[formula]
prefix
homebrewでパッケージを配置する親ディレクトリ。 例: Intel Macの場合は、/usr/local
keg-only
kegにパッケージを配置するだけで、prefixのbinディレクトリに、シンボリックリンクが作られないこと。
postgresqlもkeg-onlyなので、brew install postgresql
でインストールしても、PATHが通った状態にはならない。
(keg-onlyなパッケージにPATHを通したければ brew link
コマンドを使う。)
cellar (貯蔵室)
1つ以上のrackを含むディレクトリ。 例: /usr/local/Cellar
Caskroom (cask用の貯蔵室)
1つ以上のcaskを含むディレクトリ。 例 : /usr/local/Caskroom
external command
Homebrew/brewのGitHubリポジトリ外で定義されているbrew
の(追加インストール可能な)サブコマンド。
tap (蛇口)
formula、cask、external command用のディレクトリ(かつ、通常はGitリポジトリ)。
bottle (ボトル)
ソースからビルドするのではなく、cellarやrackに配置するために、事前にビルドされたkeg。
以下、用語の定義と意味を説明します。正式な情報はを参照してください。
本サイトの更新情報は、Twitterので発信しています。ご確認ください。
ngrokでホストされるURL(例:http://xxxx.ngrok.io/ )へのアクセスをローカル環境のwebサーバー(http://localhost:3000 など)にトンネリングしてくれます。
例えば、stripeやLINE、その他クラウドサービスのwebhook処理を開発する際に、インターネット上に本番やステージングのwebサーバーを構築する前に、ローカル開発環境でwebhookへの対応コードを実装、テストすることができるようになります。
この記事では、ngrokの開発環境を用意する手順を紹介します。
そうすると、~/.ngrok2/ngrok.yml
にtokenの値が書き込まれてサインアップしたngrokアカウントが認識されるようになります。
トンネリングを開始するためには、以下のようなコマンドを実行します。
これで、以下のような情報がコンソールに表示され、トンネリングに使えるURLが分かります。
この記事では <your-random-subdomain>
と記載しましたが、ここに、トンネリングで使えるインターネットアクセス可能なURLが表示され、また、そのURLがローカルのhttp 3000番ポートにトンネリングされているのが分かります。
この一般公開されているURLをクラウドサービスのwebhookのエンドポイントとして指定しておけば、ローカル環境のwebサーバーを使って動作確認をしながら開発を行うことができます。
無償のアカウントでは、ngrokを起動するたびに、http://<your-random-subdomain>.ngrok.io
の <your-random-subdomain>
の部分がランダムなサブドメインになってしまい、そのたびに、クラウドサービス側の呼び出し先設定を更新しなければなりません。
これは、ngrokの有償登録をすることで固定化できます。有償登録をすると、以下のようにサブドメインを他のユーザーが使っていない任意の文字列に固定できます。
ngrok起動中のコンソールでCtrl+U
でアップデートが開始されます。
ngrokが最近メジャーバージョンアップして、バージョン3がリリースされましたので、アップグレード手順を紹介します。
なお、設定ファイルは、下位互換性がなくなっているので、ngrok本体のアップグレード後には、設定ファイルのアップグレードが必要になります。
当サイトの導入記事で紹介したように、Homebrewでcask(Mac用アプリケーション)としてngrokをインストールしている場合は、以下のコマンドで本体をアップグレードします。
ちなみに、ngrokには、ngrok update
コマンドもありますが、このコマンドではメジャーバージョンアップはされません。
アップグレード後にバージョンを確認するには、以下を実行します。
なお、この直後に、ngrokを起動すると以下のようなエラーが出るため、後述の設定ファイルのアップグレードが必要になります。
これで、~/.ngrok2/ngrok.yml
ファイルが更新されて、通常通りngrokが使用できるようになります。
当社の日常業務の中で、Slackのリマインダーを設定することは、頻繁にあります。
しかし、Slackのコマンドを使ってリマインダーを指定する際、とくに、日時の指定の仕方を中々覚えられず、公式ヘルプを見に行くことが多いです。
公式ヘルプにも詳しく載ってはいるのですが、特に「When」を指定する部分について、この記事では、一覧で分かるように紹介します。チートシート的な使い方を想定しています。
/remind
コマンドのフォーマットは以下です。
この[what]
の部分まではわかりやすいのですが、この記事では最後の[when]
の部分を詳細に紹介します。
[when]
の部分は、以下のように、細かく指定することが出来ます。
x分後、x日後、x週間後など、現在からの相対的な時間で指定する場合、in
を使います。
※秒を指定しても、そこまで正確なタイミングではリマインドされないようです。
なお、時間を指定せずに相対的な日付を指定すると、指定した日の9:00になるようです。
2日後のX時、など細かい時間を指定したい場合は、atも併用します。
次ように、at
とon
を使って、時間と日付を指定します。
次のように曜日を指定すると、指定した曜日が次に来る日に投稿されます。
この使い方が一番多い気がします。末尾にevery
をつけて頻度を指定します。
毎月月初にやらないと行けないタスクをリマインドさせるなどに便利です。
この場合もat
とon
を併用できます
以下のように、特に"
で囲んだりせずに、普通に指定できます。
()は、httpのトンネリングサービスです。
でダウンロードして、インストールすることもできますが、インストールはmacOSの場合であれば、homebrewを使うほうが楽です。以下のコマンドでインストールできます。
ngrokを使うためには、サービスの利用登録が必要なので、でサインアップを行ってください。
サインアップするとにauthtokenが表示されているので、それをコピーして、以下のように、コンソールから設定します。
本サイトの更新情報は、Twitterので発信しています。ご確認ください。
さきほどのエラーメッセージにも、にも説明されている通り、設定ファイルのアップグレードも必要になります。
本サイトの更新情報は、Twitterので発信しています。ご確認ください。
これも公式サイトに載っているままの情報ですが、利用したいIAMユーザーのAccess Key IDとSecret Access Key は、 から、設定したいユーザーを選択して、「認証情報」タブでアクセスキーを生成してください。
はJSONを操作するためのCLIツールで、近年急速に利用が広がっています。Macの場合はbrewでインストールできます。
Session Manager pluginは、AWS CLIのプラグインで、ECSタスク内のコンテナやCLI経由でEC2にログインする際に必要です。に従ってインストールしてください。
本サイトの更新情報は、Twitterので発信しています。ご確認ください。
本サイトの更新情報は、Twitterので発信しています。ご確認ください。
この記事でインストールしているバージョンは古くなっている可能性もあります。最新バージョンに読み替えて利用するようにしてください。
OS:Mac
Scala:2.13.6
Scala を使うには Java と sbt が必要になります。バージョン管理ツールを2つ紹介しますが、どちらを使っても構いません。
SDKMAN
Java と sbt どちらもこれ一つでインストールできるので、 anyenv を使っていないのであればこちらがおすすめです。
anyenv + jenv + sbtenv
anyenv ですべて管理したい人向けです。
また、Java には LTS バージョンが設定されており、このバージョンのみをサポートしているライブラリも多いです。特段の理由がなければ、最新版ではなく 8 や 11 といった LTS バージョンをインストールするのが良いと思います。
下記コマンドを実行してバージョンを確認してみます。 sdk-man-init.sh
の実行は .bash_profile
等の末尾に追加されているので、次からはターミナルを開いた時に自動実行されます。
バージョン管理ツールがインストールできたので、 Java と sbt をインストールします。
Java は 8系と 11系が LTS なので、どちらかをインストールします。選択肢が多いのですが、前項で述べた通り Temurin を選択します。
sbt はとりあえず新しいバージョンを入れておけば良いと思います。
ディレクトリ内で特定のバージョンを有効にするには、 sdk env
コマンドを利用します。
すると .sdkmanrc
ファイルが生成され、デフォルトでカレントバージョンが設定されるので、これを利用したいバージョンに変えればOKです。ただ、コメントにもあるように ~/.sdkman/etc/config
の sdkman_auto_env
を true
にしなければ自動的に適用されないので設定しておきましょう。
ちなみに Metals を使ったプロジェクトの場合、 sbt のバージョンに関しては project/build.properties を参照してくれるため、バージョン指定は不要です。
anyenvを利用する場合、java, sbtのバージョン管理ライブラリはそれぞれjenv, sbtenvを利用することになります。jenvはrbenvやnodenvと違って言語のインストール機能は持っていないため、JDKは手動でインストールし、jenvに読み込ませる必要があります。
環境構築の流れは以下の通りです。
anyenvのインストール
JDKのインストール
jenvのインストール&設定
sbtenvのインストール&設定
インストール可能なJDKを検索するとtemurin11
が表示されるようになりますので、これをインストールします。複数のJavaバージョンを切り替えて利用したい場合、ここで必要な分だけインストールしておきましょう。
これでJDKのインストールは完了です。
anyenvの通常の使い方に沿い、以下コマンドでjenvをインストールします。
jenvのexportプラグインを有効化しておきましょう。
インストールしたJDKのHomeディレクトリをjenvに認識させます。複数のJDKをインストールした場合はそれぞれ実施しましょう。ここで認識させたJDKがjenvのバージョン切り替え対象に加わります。
jenvで利用するバージョンを指定します。
これを行うと実行したディレクトリに.java-version
というファイルが生成されます(中身は指定したバージョン番号だけ記述されたシンプルなファイルです)。このディレクトリに移動した際にはjenvがこれを読み込み、指定されたjavaのバージョンが自動的に参照されるようになります。
shをリセットすると、先ほど有効化したexport
プラグインにより環境変数JAVA_HOMEも設定されます。
以下を実行すると、.java-version
ファイルが存在しないディレクトリでデフォルトで適用するバージョンを設定することができます。必要があれば設定してください。
これでjenvのインストールと設定は完了です。
以下コマンドでsbtenvをインストールします。
sbtのインストールを行います。バージョンはSDKMANの項と同様に1.5.5をインストールする場合、以下のようにします。複数バージョンのsbtを切り替えて利用したい場合、ここで必要な分だけインストールします。
※ gpgが必要
といったエラーが表示された場合、インストールしてsbtの公開鍵を設定してから再実行してください。
インストール完了後、jenvの時と同様に利用するバージョンのsbtを設定します。
以上でanyenv + jenv + sbtenvの環境設定は完了です。
scalameta.metals
という extension をインストールするだけです。
extension は Scala プロジェクト以外は有効化されないはずですが[1]、私の環境では ruby のワークスペースなどにも .metals ディレクトリが出来てしまいました。基本は disable にしておいて、Scala のワークスペースでだけ手動で enable にするのが良いと思います。
VS Code のサポートについてはこちらに詳しく書かれています。
無償JDKとして広く利用されていたAdoptOpenJDKはプロジェクトに移管されることが公表されており、2021/8/13にはそのサブプロジェクトであるEclipse Temurinプロジェクトから新たなJDKがリリースされました。今回はこちらのJDKを利用します。
のインストール以下のコマンドを実施するだけです。
SDKMANではなくこちらの手順を選択している方は既にインストール済みの方が多いと思いますので、詳細な手順は割愛します。新規にインストールする方はのREADMEに沿ってインストールしましょう。
で推奨されている通りHomebrewでインストールを行います。2021/9/14現在temurinからリリースされているJDKは16系が最新となりますが、先述のとおり今回はLTSである11系をインストールします。つまり最新版以外のJDKをインストールする必要があるため、複数バージョンを扱えるようにしてくれるhomebrew-cask-versionsをインストールします。
以前は Scala といえば IntelliJ だったと思うのですが、最近は という Language Server が出てきており、Metals を使えば Visual Studio Code や Emacs などエディタを選ばずに Scala の開発ができるようになっているようです。
[1] に次の記載あり。The extension activates when the main directory contains build.sbt
or build.sc
file, a Scala file is opened, which includes *.sbt
, *.scala
and *.sc
file, or a standard Scala directory structure src/main/scala
is detected.
本サイトの更新情報は、Twitterので発信しています。ご確認ください。
この記事では、M1 Macでの開発環境構築でハマったところを共有するために、記載して行きます。今後、随時新しいハマりどころが発生した場合は、情報を追加していきます。
筆者の環境では、環境構築の検証もかねているので、Rosetta 2をインストールしていません。Rosetta 2をインストール済みの場合は前提が異なってくるため、ご注意ください。
また、以下のものはインストール済みとします。
rbenv
Docker Desktop(4.3.0以上)
postgresql(実行用ではなく、gem pgのビルド時に利用する想定。brewでインストールする)
のように、古めのRubyのバージョンをインストールすると、以下のようなエラーが発生しました。
以下のように、RUBY_CFLAGS
を指定して、rbenvのインストールコマンドを実行すると上手く行きます。
筆者もこの方法を先に試して上手く行ったため、この後の2つめの方法を試せていません。
brew info libffi
コマンドを実行すると、以下のようなCaveatsが表示されます。
これにしたがって、環境変数LDFLAGS
、CPPFLAGS
、PKG_CONFIG_PATH
を指定して、以下のように実行すると良いとのことです(筆者は、試せていませんが掲載しておきます)。
※記事のみやすさのために、環境変数ごとにexportをしていますが、一気に複数の環境変数を指定しても良いと思います。
bundle install時にネイティブ拡張が含まれるいくつかのgemでインストールに失敗したのでgemごとの対処法を記載しておきます。
pgは、M1 Mac特有のエラーというよりかは、Intel Macでもよく発生する、Ruby開発者ならよく見たことあるエラーですが、以下のエラーが発生します。
対処法もいつもどおりで、やり方はいくつかありますが、筆者は以下のようにbundleのbuild時のconfigを指定したのち、bundle install
を実行しました。
なお、このコマンドを実行すると設定が~/.bundle/config
に書き込まれます。
rmagickもM1固有の問題ではなく、毎回遭遇する以下のエラーが発生します。
これは、まず、imagemagickがインストールされていないことが原因で、もしインストール済でこのエラーが発生しているなら、convertコマンドへのPATHが未設定であったり、ビルドに必要なファイルが指定されていなかったりすることが原因です。
以下のように対処します。
まずは、必要なImageMagickをインストールします。Rmagickが対応しているバージョン6を、必ずインストールしてください。
次に、brew info imagemagick@6 コマンドを実行して表示される以下にしたがって、
まず、実行時に必要と思われるconvert
コマンドへのPATHを通しておきます。
念の為、以下のようにconvertコマンドが使えるかを確認しておくとよいでしょう。
次に、rmagickのビルドに必要な環境変数を指定してから、bundle installを実行します。
ここまでで、gemのインストールまで出来ました。
なお、2022年1月17日現在、筆者の環境では、この後、古いnodeをインストールしようとしてエラーが発生して失敗し、対応方法が調べきれず、まだRailsアプリケーションを実行するところまでたどり着いておりません。ただ、今後、nodeをバージョンアップさせる予定なので、新しいバージョンのnodeを利用することで、エラーを回避できる可能性があります。
上記については、後日、この記事をアップデートして共有しようと思います。
※Docker Desktopは、。
によると、以下2つの対処法があるようです。
ように、docker-compose
コマンドはv1のdocker-composeが使われるためRosetta 2のインストールが必要です。、これまでdocker-compose
コマンドを使用していた場合は、以下のように、docker compose
コマンドを使います。
このgemのインストールに失敗する原因は、とと同じです。筆者は、gemのインストール時に発生したエラーに対しては、以下のように、brew info コマンド実行時に表示されるCaveatsの内容にしたがって対処しました。
Slack emoji とGoogle meet を連携して使う
当社のエンジニアは全員フルリモートで働いているため、対面での相談は Google meet
を使うことが多いです。
ただ、対面で相談するまでの準備に手間がかかると、気軽な相談はしづらくなりがちです。
そこで当社では、「Slackのカスタムレスポンスとemoji」にGoogle meetのURLを紐付けています。その結果、Slackでのやりとりが難しいと感じた時に、すぐ対面での相談へと移行できています。
Google meet
Slack
Slackへ相談の前フリと :room:
などのemojiをポストする
Slack botが相談用のGoogle meetのURLを案内する
任意の画像を用意します。
なお、 生成履歴に絵文字を表示する
のチェックボックスについては状況によりON/OFFします。
Slackbotタブに以下を入力し、保存します。
When someone says
に、上記2.のemojiの値 (例: :room:
)
Slackbot responds
に、上記3.のGoogle meetのURL
以上で設定は完了です。
以下、開発プロジェクトへのインストール手順を説明します。
開発プロジェクトにおいて、yarnでnodeパッケージが管理されている前提です。
まずは、開発環境のみの依存パッケージとして、prettier をインストールします。
必要に応じて、prettier-rubyもインストールします。
プロジェクトルートに以下のファイルを作成します。
上記の設定は、当社で使っているデフォルトの設定です。例えば、printWidth
は、デフォルトの80だと少し狭く、フォーマットしたときに、過度に改行が入ってしまうため、120にしています。
Railsのroutesファイルなどのように、なるべく改行をせずに一行で書いてしまいたいような特殊なコードは、pretterでのフォーマットがかからないように、ignoreファイルをプロジェクトルートに追加します。
また、これ以外にも、rubyの後置ifや後置whileなどのフォーマットをprettierにまかせてしまいたいので、以下のように.rubocop.yml
にルールを追加します。
以下、RubyMineやVisual Studio Codeの設定を各自行います。
JavaScriptやTypeScriptだけでなく、Rubyもフォーマットの対象にする場合は、設定画面で、Preferences -> Languages & Frameworks -> JavaScript -> Prettier
から、Prettier Packageを{Project Root}/node_modules/prettier
に設定した上で、Run for files
の部分でrbも含まれるように、以下のように変えておきます。
On save のチェックをいれておくと、⌘S を押したときにフォーマットがかかります。 設定保存後もprettierが動作しない場合は、一度RubyMineを再起動するとうまくいきます。
上記プラグインをインストールした上で、以下のように、.vscode/settings.json
をプロジェクトルートに追加して、言語ごとにフォーマットの設定をします。チームメンバーのvscodeの設定状況によっては、このファイルはリポジトリにpushして他のメンバーと共有してもよいです。
なお、editor.formatOnSave
は任意の設定で、保存時に自動的にフォーマットをかけるかを設定します。
当社の開発で使うRubyのバージョンは、プロジェクトごとに異なることが多く、また、プロジェクトごとにRubyのアップデート状況も異なります。
したがって、プロジェクトごとにRubyのバージョンを切り替える仕組みとしてrbenvを導入して使います。
直接rbenvをインストールするか、anyenv経由でインストールするかで手順が変わります。
anyenvがすでに導入されている場合、以下で簡単にrbenvをインストールできます。
まずは、homebrewでrbenvをインストール。このとき、ruby-buildも同時にインストールされます。
次に、
を実行して、出力された手順にしたがって、シェルの設定を行います。zshを使っている場合、以下のように出力されるので、この内容に従います。
つまり、.zshrcファイルに以下を追加します。
rbenvがインストールされたら、次は、使いたいバージョンのRubyをインストールします。
まずは、インストール可能なRubyの安定バージョンを確認します。
ここに表示されたバージョンを指定して、開発環境にインストールします。
プロジェクトで使うRubyのバージョンは、他の開発メンバーとも共有したいため、.ruby-version
ファイルを作って、他のメンバーにも同じRubyのバージョンの使用を強制できるようにします。
プロジェクトルートで、以下を実行すれば.ruby-version
ファイルがプロジェクトルート直下に作られます。
Rubyのバージョンを上げる手順もanyenvでrbenvをインストールしたかどうかで異なります。
rbenv install -l
コマンドで、インストールしたいRubyのバージョンが表示されていない場合、anyenv内部のruby-buildのアップデートが必要です。このアップデートのコマンドを楽にしてくれるanyenv-updateを使うのをおすすめします。
anyenv-updateが設定してあれば、以下を実行するだけで、インストール可能なRubyのバージョン情報が最新化されます。
このあとは、普通に特定のバージョンのRubyをインストールします。
同様に、インストールしたいRubyのバージョンが表示されていない場合、先に、rbenvとruby-buildをアップデートする必要があります。rbenvとruby-buildのアップデートにはhomebrewを使います。
これで、インストール可能なRubyのバージョンが最新化されるので、特定のバージョンをインストールします。
過去に書いたソースコードを読んでいて、仕様を理解するのに手間取ってハマったので、共有のために記事を書いておきます。
以下のようなコードを見かけたとします。
上のコードから、どのような仕様を想像するでしょうか?
筆者は、ユーザーが見つかった場合、または、初期化した場合、どちらもlast_name
、first_name
がそれぞれ、サンプル
と 太郎
に初期化されると思ってしまいました。
しかし、実際の挙動としては、以下でした。
ユーザーが見つかった場合は、ブロック内の処理は実行されず、
ユーザーをinitializeした場合は、ブロック内の処理が実行される。
コードを見ても、上記の挙動が実装されているのが確認でき、find_by
で該当レコードが見つかった場合には、&block
部分が無視されるのが分かります。
zshを使うときに、補完用の情報を設定をしておくことで、例えば、git、aws-cli、dockerなどのコマンドやサブコマンドをTabキーで補完してくれるツールです。
公式サイトは、以下です。
macOSを使っている前提ですが、homebrewで簡単にインストールできます。
zsh-completionをインストールすると、以下のようなメッセージがでます。
なお、このメッセージは、brew info zsh-completion
コマンドで再表示できます。
このメッセージにしたがって、まずは、次のように .zshrc
ファイル設定します。
次に、zsh関連ファイルの権限設定を行います。これも前述のbrew info
で表示されるメッセージに従います。
前述の brew info
で表示されるメッセージの /opt/homebrew
の部分は、使用しているMacのCPUがIntel製かApple Siliconかによって異なります。
なので、環境に応じて次のようにコマンドを打ちます。
あるいは、以下のコマンドを打てばどちらの環境でもOKです。
なお、この go-w
は、g(roup)とo(thers)から(つまりuser以外から)、書き込み権限を削除(-)しています。
homebrewでgitをインストールすると、自動的に /usr/local/share/zsh/site-functions/ 以下に、git用の補完設定もインストールされるので、それ以上何もする必要はありません。
gitはmacOS標準のものもありますが、上記の補完設定のために、筆者はhomebrewでgitをインストールしなおしてしまいます。
設定がうまくいっている場合、以下のように補完されます。ブランチ名などは、覚えられないので補完を使うと便利になります。
公式サイトに説明があるので、それに従います。手順は公式サイトに記載されていて、また、迷うところもないので、ここに手順を記載するのは省略します。
設定がうまくいっている場合、以下のような補完が使えます。
aws-cliも公式サイトに従うだけですが、少しわかりにくいので、補足説明を追加します。
公式サイトは、以下です。
前述の前提の場合、aws_completer はすでにPATHが通っているので、やることは以下の設定を.zshrc
ファイルに追加するだけです。
設定後は、zshを再起動します。
設定がうまくいっていれば、以下のようにawsコマンドの補完が効くようになります。
もし、文字列画像を用意する場合は、絵文字ジェネレータ()などが便利です。
Slackのemoji設定ページ()を開き、文字列 (例: :room:
)と 上記1.の画像を登録します。
Google meetのページ()にて、URLを取得します。
Slack botの設定ページ( )を開きます。
本サイトの更新情報は、Twitterので発信しています。ご確認ください。
おもにJavaScriptなどのフロントエンド系のソースコードのフォーマッタです。プラグインを追加することでRubyなどの言語にも対応可能です。詳細はを参照してください。
なお、prettier-rubyは、ですが、JS用のprettierと別管理にしたくないので、上記のようにnodeパッケージのプラグインとしてインストールしています。
も併用しているプロジェクトの場合、rubocopに定義されているスタイル関連のルールとprettierのフォーマットが競合して、gitのpre-commitにrubocopのチェックを行っているとリポジトリにコミットできなくなってしまいます。
そういった場合のために、ていて、この設定をプロジェクト用の.rubocop.yml
で継承します(参考:)。
最新のRubyMineでは、Prettierプラグインはデフォルトでインストール済なので、設定だけ行います(参考:)。
プラグインを使います。
本サイトの更新情報は、Twitterので発信しています。ご確認ください。
の手順に従います。
本サイトの更新情報は、Twitterので発信しています。ご確認ください。
実際に、を見てみると、以下のようになっています。
本サイトの更新情報は、Twitterので発信しています。ご確認ください。
なお、この記事では、 の記事に記載さた手順でインストールされている前提で補足説明をします。
本サイトの更新情報は、Twitterので発信しています。ご確認ください。
この記事では、Railsの本番環境におけるアプリケーションサーバー(pumaやunicornなど)のプロセス数とスレッド数のパラメータ設定に関する情報をまとめます。
アプリケーションサーバーのパラメータを設定する際に、OSにおけるプロセスとスレッドは重要な要素になるので、あらかじめ概要を説明しておきます。
プロセスはOSで実行中のプログラムのことで、プロセス内には1つ以上のスレッドが含まれます。CPUのコアに命令をしているのがスレッドです。
複数のプロセス同士は、同じメモリ領域を共有できません。しかし、同じプロセス内のスレッド同士は、同じメモリ領域を共有できるので、プロセス内に複数のスレッドを作成した方がメモリの利用効率が上がります。
複数のスレッドが同じメモリ領域を共有すると、メモリの利用効率は上がりますが、誤って別のスレッドに影響する情報を書き換えてしまう可能性もあります。いわゆるスレッドセーフではない状態が起こるということです。
1つのスレッドしかないプロセスを複数使用すれば、スレッドセーフかどうかを考慮する必要はなくなりますが、複数のスレッドを使う場合と比較して、メモリの利用効率は下がってしまいます。
1つのプロセス内に複数のスレッドを作った場合のメリットとして、一部のスレッドがIO待ち(例えば、データベースへのリクエストとそのレスポンスを待っているような状態)になってしまっても、プロセス内の他のスレッドで別の処理を進めることができるという点があります。この場合、プロセスとしてのスループットが大きくなります。
もし、1つのプロセスで1つのスレッドしか用意しなかったとしたら、そのスレッドでIO待ちが発生すると処理がブロックしてしまいます。
Railsで使うアプリケーションサーバーとして一般的なものにpumaとunicornがあります。
pumaは、マルチプロセス対応で、さらに、各プロセス内に複数のスレッドを作成することができます。
unicornは、マルチプロセス対応ですが、各プロセス内には複数のスレッドを作りません。
したがって、スレッド間でメモリを共有できる分、メモリ効率はpumaの方が良くなりやすいです。ただし、pumaを使う場合は利用しているgemも含めてスレッドセーフな実装でアプリケーションを作っておく必要があります。
一方、unicornは、メモリ効率はpumaと比較して良くありませんが、スレッドセーフであるかどうかを気にする必要がありません。また、詳細な説明は省略しますが、稼働中のサーバーにデプロイを行う際にゼロダウンタイムのデプロイをすることが可能、などの特徴もあります。
この記事では、Rails標準のアプリケーションサーバーであるpumaを使う想定で、以降の説明をします。
Ruby には、Giant VM lock (GVL)とよばれるものが存在します。Giant VM Lockが効いている間は、ネイティブスレッドがロックされ、実質1つのスレッドしか同時に実行できません。
しかし、GVLについては、Ruby 3.0.0のリファレンスマニュアル(上のリンク先)には、以下のように記載されています。
ネイティブスレッドを用いて実装されていますが、現在の実装では Ruby VM は Giant VM lock (GVL) を有しており、同時に実行されるネイティブスレッドは常にひとつです。ただし、IO 関連のブロックする可能性があるシステムコールを行う場合には GVL を解放します。その場合にはスレッドは同時に実行され得ます。また拡張ライブラリから GVL を操作できるので、複数のスレッドを同時に実行するような拡張ライブラリは作成可能です。
詳細な説明は省略しますが、要するに、Rubyのスレッドは、IO関連のシステムコールを行う場合にはGVLを開放するので、その場合は複数のスレッドを実行できます。しかし、逆にIO関連以外のシステムコール(例えば数値計算など)の場合には、GVLが効いてしまい、同時に1つのスレッドしか実行されません。
Webアプリケーションにおいては、IO関連のシステムコール(例えばネットワーク越しのリクエスト・レスポンス処理)が使われることも多くなるため、アプリケーションサーバーが、マルチスレッド対応をすることのメリットはあると考えられます。
前置きが長くなりましたが、ここからが本題です。
一般的なRailsアプリケーションを実行する本番環境のプロセス数とスレッド数を設定するには、以下を考慮に入れます。
CPU数(環境によっては、vCPU数、仮想CPU数)
利用可能なメモリの量
1スレッドで必要なメモリの量
Railsで設定しているコネクションプールの数
webとjobワーカーの数
データベースの同時接続数上限
CPU数は、利用しているサーバーのCPUコアの数です。クラウドのような仮想環境を利用している場合は、物理的なコア数ではなく仮想CPU数(vCPU数)を把握する必要があるかもしれません。
(訳注:IO待ちの場合は、GVLが開放されるとはいえ)GVLを考慮するとCPUの機能をフル活用するなら、Rubyコードのプロセス数は、CPUのコア数に一致させるのがよい、と記載されています。
利用しているサーバーのメモリ量も把握する必要があります。
スレッドごとにRailsアプリケーションの処理が実行されるので、スレッド数が増えればそれだけ必要なメモリ量も大きくなります。また、プロセス数が増えれば必然的に包含するスレッドも最低1つは増えますので、プロセス数を増やす場合も、必要なメモリ量は大きくなります。
利用しているサーバーのメモリ量は、プロセス数やスレッド数の上限を考えるのに必要です。
1スレッドで必要なメモリ量も把握する必要があります。
1スレッドで必要なメモリ量が例えば、400MB前後だとすると、スレッド数を1、2…と増やしていった場合に、必要なメモリは概ね400MB、800MB…と比例して増えていきます。
1スレッドで必要なメモリ量は、アプリケーションの実装によって幅が出てきます。筆者の経験では、Railsアプリケーションの本番環境では、概ね300MB〜1GBくらいの幅で変わるように思います。
もしすでに本番環境やステージング環境を運用しているのであれば、その環境でメトリクス計測ツール(New RelicやHerokuのMetrix機能)で確認するのが確実です。
稼働している本番環境やステージング環境で、実際に合計何スレッドが動作しているのかが分かれば、本番環境で利用しているメモリ量をその合計スレッド数で割れば、1スレッドで必要なメモリの量を概算することができます(厳密には、スレッド間で共有しているメモリがあるはずです。ここで計算しているのは、あくまで概算値です)。
Railsには、コネクションプールの仕組みがあり、以下のように、database.yml
のpool
の値で上限値を指定することができます。
コネクションプールとは、Railsの処理がデータベースにアクセスするたびにコネクション接続と切断を行って負荷が高くなったり、パフォーマンスが低下するのを防ぐために、予め決められた上限数を考慮してデータベースとの間に作っておく接続のグループのことです。
また、このコネクションプールは、各プロセスごとに作られます。プロセス間で共有のプールを持つことはできません。
コネクションは、スレッドの処理でデータベースへの接続が必要になった場合に、コネクションプールからスレッドに1つ割り当てられ、処理が終わるとプールに戻されます。
コネクションプールの値がスレッド数より少ない場合、プール数よりも多いデータベースへのアクセスリクエストが発生した際に、新しいスレッドにデータベースとの接続を割り当てることができなくなり、コネクションの割当待ちのような状態になります。
したがって、この割当待ちによるスループットの低下を防ぎたい場合は、
のような関係を保つように、database.ymlを設定する必要があります。
一般的なRailsアプリケーションにおいて、データベースに接続するのは、pumaやunicornのようなアプリケーションサーバーからだけではなく、sidekiqのようなjobワーカーも存在します。
次節で説明するように、データベース側に接続上限数が存在するため、jobワーカーが存在するか、そして、それが存在する場合どれくらいの数があり、合計何スレッドくらい実行されるか、も考慮にいれる必要があります。
データベース側の同時接続数の上限値も把握しておく必要があります。
データベース側の同時接続数が分かれば、全サーバーの全プロセスで作られるスレッド数の合計値が、その同時接続数を超えないようにパラメータを調整しなければなりません。
したがって、結果的に、データベースの同時接続数は、各サーバーのプロセス数やスレッド数の上限値に影響します。
これまでの説明を考慮すると、メモリに関するパラメータを除外すれば、少なくとも以下のような大小関係がなりたつように、各パラメータを設定する必要があります。
ただし、この大小関係を満たすという条件は変わりませんが、実際は、次の節で考慮に入れる利用可能なメモリ量で頭打ちになることが多いように思います。
上記の大小関係とは別に、メモリによってもパラメータの大小関係が決まってきます。
これは、単純に、以下になります。
最後に、当社で良く利用するHeroku環境の例をもとに、具体的な設定例を考えてみます。
以下のような架空の本番環境を想定することにします。
項目
値
CPU数(web dynoで使用できるvCPUの数)
今回の想定では、web dynoはpremium Mとする。
2
利用可能なメモリの量(premium Mで利用可能なメモリ量)
2.5GB
1スレッドで必要なメモリの量(本記事用に、適当に想定)
600MB
Railsで設定しているコネクションプールの数(スレッド数と同じ値を設定)
あとで計算
アプリケーションサーバーとjobワーカー数 (説明を簡単にするため、アプリケーションサーバー数(web dynoの数)を2、jobワーカー数を0と想定)
2
データベースの接続上限数(Heroku Postgresのmax connectionsの数)
400
CPU数は2なので、プロセス数の適切な値は2です。
ただし、メモリ関連のパラメータの大小関係を考慮すると、1スレッドで必要なメモリ量が600MBなので、合計スレッド数 × 600 <= 2500MB
となるように、合計スレッド数を4にすると良さそうです。
したがって、仮にプロセス数を2とすると、設定可能なスレッド数は2です。もし、プロセス数を1とすると、スレッド数は4です。
おそらく、後者の方がメモリの利用効率は高そうですが、GVLを考慮すると、アプリケーションによっては前者の方がスループットが大きい可能性もあります。これは、実際にステージング環境などで、どちらが良さそうかを確認すると良いかもしれません。
ここでは、仮に、プロセス数を2、スレッド数を2にすると決めたことにします。また、これに合わせて、コネクションプール数は2にします。
web dynoは、2つ使用するので、各web dynoごとに2プロセス、2スレッド作る場合、プロセス数は、合計で2 x 2= 4だけ作成することになります。
さて、この前提で、合計のデータベース接続数を念の為に計算すると、
となるため、データベースの接続上限数の400には、まだまだ余裕があり、問題ありません(この前提の場合、もっとスペックの低いデータベースを使用してもいいのかもしれません)。
pumaを使用する想定の場合、デフォルトでは、/config/puma.rbに以下のような起動設定が用意されています。
なお、実際には、説明用のコメントもつけられているはずですが、コードが長くなるので、以下では割愛しています。
この設定ファイルの
の部分がスレッド数を指定している部分で、
の部分がプロセス数を指定している部分です。
したがって、具体的なパラメータとしては、RAILS_MAX_THREADS
を2に、WEB_CONCURRENCY
を2に設定することになります。設定しない場合、このコードの例では、デフォルト値としてスレッド数5とプロセス数2が使われるので、サーバーのスペックが不十分な場合は、障害が発生するかもしれません。
Railsには、スキーマファイルと呼ばれる schema.rb
があります。
Active Recordはマイグレーションの時系列に沿ってスキーマを更新する方法を知っているので、履歴のどの時点からでも最新バージョンのスキーマに更新できます。Active Recordは
db/schema.rb
ファイルを更新し、データベースの最新の構造と一致するようにします。
とあるように、schema.rb
はデータベース関連の機能で使われます。
その schema.rb
ですが、チームで開発をしている時に
自分の作業を中断して、プルリクエストのレビューをする
自分の作業に戻って、開発を続ける
を繰り返している中で、意図しない差分が schema.rb
に発生していると気づき、戸惑うことがあるかもしれません。
そこで、schema.rb
に差分が発生してしまうことについて
発生する事例
差分が発生していると何が起こるか
どう復旧するか
を記事としてまとめます。
発生する事例として、「どのような流れで発生してしまうのか」を体験できるチュートリアルを用意しました。
チュートリアルは以下の流れで進めます。なお、Railsのバージョンは 7.0.4.2
を想定しています。
mainブランチで、Railsのモデルを作成
mainから派生した第2のブランチ(feature/add_unique_index
)で、モデルの変更を伴うマイグレーションを適用
mainから派生した第3のブランチ(feature/add_column
)で、モデルの変更を伴うマイグレーションを適用
最小構成で rails new
します。後ほどgemを追加するため、現時点では bundle install
を行いません。
今回の動作確認をRSpecで行うため、 Gemfile
の末尾に追加します。
Gemfileの準備ができたため、一連のgemをインストールします。
RSpecのセットアップも行います。
続いてモデルを用意します。今回は isbn
列を持つBookモデルとします。
マイグレーション適用後に、状態を確認しておきます。
ここまでをコミットします。
(もし vendor/bundle
以下をコミットしたくない場合は .gitignore
に追加しておきます)
feature/add_unique_index
)で、モデルの変更を伴うマイグレーションを適用main
ブランチから第2のブランチ( feature/add_unique_index
)を新しく作成し、「Bookの isbn
にUNIQUE制約を追加する」ためのマイグレーションファイルを生成します。
生成したマイグレーションファイルにて、Bookの isbn
にUNIQUE制約を追加します。
マイグレーションを適用後、状況を確認します。
作業が終わったため、コミットします。
feature/add_column
)で、モデルの変更を伴うマイグレーションを適用ここからが、意図しない差分を発生させてしまう手順になります。
本来ならば、第2のブランチ(feature/add_unique_index
)を離れる前にマイグレーションをロールバックします。
ただ、今回は schema.rb
に差分を発生させるため、マイグレーションを適用したまま、
第3のブランチ(feature/add_column
)を新しく作成・切り替え
モデルに列 name
を追加
マイグレーションを適用
を行います。
続いて、マイグレーションの状態を確認します。作業2.のマイグレーションが適用されたまま、今回のマイグレーションも適用されているのが分かります。
schema.rb
を見ると isbn
列にUNIQUE制約が定義されており、意図しない状態となっています。
最後に、「この状態に気づかずに、うっかりコミット」した想定にしておきます。
今回はテストコードを使って、何が起こるかを見ていきます。
「第3のブランチ(feature/add_column
)ではまだUNIQUE制約を付けるマイグレーションがないから、テストはパスするだろう」と考え、以下のテストコードを書いたとします。
しかし、RSpecを実行すると
expected no Exception, got #<ActiveRecord::RecordNotUnique: SQLite3::ConstraintException: UNIQUE constraint failed: books.isbn> with backtrace:
とテストが失敗してしまいます。
今回の場合、UNIQUE制約を追加するマイグレーションファイルが存在しない第3のブランチ(feature/add_column
)でも、UNIQUE制約付きでテスト用のデータベースが作成されてしまった結果、テストが失敗しました。
今回は、以下の流れで復旧していきます。
UNIQUE制約を追加した第2のブランチ(feature/add_unique_index
)へ切り替え、UNIQUE制約のマイグレーション適用をロールバックする
第3のブランチ(feature/add_column
)へ切り替え、 db:migrate
を実行する
順に見ていきます。
feature/add_unique_index
)へ切り替え、UNIQUE制約のマイグレーション適用をロールバックするUNIQUE制約を追加した第2のブランチ( feature/add_unique_index
)へ切り替え、マイグレーションの状況を確認します。
Migration IDに対するNameのうち、NO FILE
と表示されていたものが Add index to book
という表示へと切り替わりました。
続いて、第2のブランチ( feature/add_unique_index
)上で、不要なマイグレーション適用を元に戻します。
なお、 db:migrate:rollback
コマンドでは1ステップずつしか戻せないので、バージョンを指定できるコマンドで戻します。
再度マイグレーションの状況を確認すると、当該Migration IDが down
という未適用状態になりました。
ただ、この時点では schema.rb
に変更が入っていることから、第3のブランチ(feature/add_column
)へ切り替えようとしても
と、エラーになってしまいます。
そこで、 schema.rb
の変更を、当ブランチのコミット時点に戻しておきます。
feature/add_column
)へ切り替え、 db:migrate
を実行するあらためて第3のブランチ(feature/add_column
)へ切り替えます。
ここでマイグレーション適用状況を確認すると、第3のブランチ(feature/add_column
)のマイグレーションのみが適用・表示されています。
ただ、まだ schema.rb
にはUNIQUE制約の定義が残ったままになっています。
そこで、もう一回 db:migrate
を実行し、 schema.rb
を更新します。
ターミナルの実行結果には何も表示されないものの、git status
ではschema.rb
の更新が確認できます。
更新後の schema.rb
を見ると、UNIQUE制約の定義が削除されています。
これでようやく schema.rb
が復旧しました。
あとは忘れずに schema.rb
を第3のブランチ(feature/add_column
)へコミットすれば、復旧は完了です。
今回は schema.rb
に差分が発生する事例とその復旧方法を見てきました。
復旧するのには手間がかかることから、もしプルリクエストにマイグレーションファイルが含まれている場合は、schema.rb
に余分な更新がないかチェックするのが良さそうです。
先日、Rails6.1系で動いていたシステムをRails7.0系にアップグレードしました。
そのシステムは
Rails製APIアプリケーション
コード量・利用者ともに小規模
テストコードが充実している
と比較的アップグレードしやすかったこともあり、無事に完了しました。
この記事ではアップグレードの際に調べたことをまとめておきます。
資料を読み終えたところで、対象のシステムは問題なさそうと判断し、準備を進めることにしました。
Rails7.0がリリースされた直後は、使っているgemがRails7.0対応していないものがありました。
そこで、定期的に各gemのリポジトリを見に行き、Rails7.0対応に関するissueやPull Requestを確認しました。
その後、使用しているすべてのgemがRails7.0対応がなされているのが確認できたため、具体的な作業に入りました。
今回は小規模だったこともあり、
bundle outdated
でバージョンアップできるgemを確認
Rails以外を bundle update --conservative <gem名>
でバージョンアップ
Railsを bundle update --conservative rails
でバージョンアップ
の順で作業をしました。
また、各gemをバージョンアップするごとに、テストを流してパスすることを確認した上でGitブランチにコミットしました。
まず、削除された設定について見てみましたが、主に
Rails7で config/initializers/
以下が整理されたが、対象システムでは使っていない設定だった
ため、システムから削除しても問題なさそうでした。
次に、設定ファイルの変更で気になった点(後述)については調査を行いました。その結果、変更による影響がなさそうだったため、Rails7.0系での変更を各種設定ファイルに取り入れました。
ここでは、今回気になった点とその対応について以下にまとめます。
一方、
config.assets.debug
は使っていないシステムだった
config.file_watcher
はDockerを使っていると影響ありそうな情報が散見されるものの、今回の環境では不要そうだった
ため、これらは設定ファイルから削除することにしました。
他の項目については、対象のシステムで無効にしていた設定だったため、対応は不要でした。
そのため、 config.eager_load
は従来のまま false
と明示的に設定しました。
そこで、Railsのデフォルト値の変更を調べることにしました。
上記の記事にある項目を確認しましたが、今回のシステムに対して影響するものはありませんでした。
次に、さきほどのQiitaの記事で触れられていなかったものについて調査しました。それらを以下にまとめます。
ある種のRubyコアクラスに含まれる#to_sメソッドの上書きを無効にします。この設定は、アプリケーションでRuby 3.1の最適化をいち早く利用したい場合に使えます。
とあります。
今回は小規模なシステムだったこともあり影響がなかったため、デフォルト値の変更を受け入れました。
In Rails 7.1 and beyond, Active Storage has_many_attached relationships will default to replacing the current collection instead of appending to it.
とのことですが、今回のシステムには影響しなかったため、デフォルト値の変更を受け入れました。
Rails7.0系にアップグレード後も開発・運用を継続していますが、今のところ大きな問題は発生していません。
今後もRailsのバージョンアップを継続して行うとともに、作業で気になったこと等はTechBookに記載していこうと思います。
当社のRailsシステム間連携では、各システムで公開しているWeb APIを使っています。
今までは各システムを bin/rails s
で起動し、開発を行ってきました。
ただ、 連携するシステムが増えたり、各システムで使うジョブワーカーが増えたりした結果、現在では各システムを起動する手間が増えてきました。
そこで、今後も効率的に開発できるよう、以下の設定を行いました。
tmux
+ overmind
にて、連携する各システムやワーカーを1つのコマンドで起動できるようにした
RubyMine
にて、 overmind
で起動したプロセスにアタッチし、デバッグできるようにした
この記事では、複数システムを1コマンドで起動できるようにするために、 tmux
+ overmind
+ RubyMine
にてどのような設定をしたか、チュートリアル形式で共有します。
このチュートリアルでは、以下のシステム構成とします。
mac上で、2つのRailsシステム(frontend_app
とbackend_app
)を開発している
frontend_app
について
外部からのHTTPリクエストを受け付ける
Delayed::Job
でジョブを管理している
bin/rails jobs:work
にて Delayed::Job Worker を起動する
各Railsシステムは、ローカルマシン上での bin/rails s
実行により起動する
各Railsシステムは、データベースを適切に設定している
次の図のように、overmind
ディレクトリの中に frontend_app
と backend_app
という2つのRailsシステムのリポジトリがあるものとします。
terminal multiplexer
と呼ばれるソフトウェアのうちの1つです。
1つのターミナルの画面を、複数に分割して利用します。
Herokuで使う Procfile
と同じ書式で定義することで、定義したプロセスを管理できます。
tmux
はデフォルト設定のままでも問題なく使えます。
ただ、慣れないうちはマウス操作はできたほうが便利なため、 tmux
の設定を追加します。
~/.tmux.conf
ファイルを追加し、以下を記載します。
チュートリアルのルートディレクトリである overmind
に、ファイル Procfile
を作成します。
Procfile
には、各Railsシステムやジョブワーカーを起動する時のコマンドを記載します。
なお、ワーキングディレクトリを考慮するため、 &&
を使ってコマンドをチェーンしています。
macのターミナルから tmux
を起動します。
次に、 Procfile
のあるディレクトリに移動し、overmind
にて各プロセスを起動します。
tmux
の画面では、 Procfile
で定義した各プロセスの様子が表示されています。
外部からのHTTPリクエストを受け付ける frontend_app
に対し、 curl
でアクセスします。
すると、 frontend_app
からJSONレスポンスが返ってきます。
tmux
を見ると、各システムやジョブワーカーが連携し、JSONレスポンスを返したことが分かります。
動作確認ができたため、いったん Ctrl + C
にて overmind
での実行を停止します。
現在はtmuxの1つのペインに、 Procfile
で起動したすべてのプロセスのログが表示されています。
ただ、この状態のままでは各プロセスのログを追いづらいです。
そこで、別ペインで表示するよう設定します。
このチュートリアルでは、ウィンドウを上下ペインに分けます。
上ペインはここまで通り overmind
のログを表示します。
一方、下ペインでは backend_app
のログのみを表示するようにします。
以下の準備を行います。
tmuxで Ctrl + b
+ "
を入力し、水平ペインを開く
上ペインにて、以下の操作を行う
overmind s
を実行し、各システム・ワーカーを起動する
下ペインにて、以下の操作を行う
Procfile
のあるディレクトリに移動する
overmind connect backend_app
を実行し、overmindで実行している backend_app
のプロセスに接続する
再びcurlで frontend_app
にアクセスしてみます。
すると、上ペインでは、各システム・ワーカーのログが出力されています。
一方、矢印部分の下ペインでは、 backend_app
のログのみ表示されています。
今までの操作にて、各システム・ワーカーを overmind s
だけで起動できるようになりました。
ただ、何か不具合があった時には、各システムをデバッグしたくなるかもしれません。
もしRubyMineを使っている場合は、 overmind
で起動したプロセスにアタッチ・デバッグできます。
このチュートリアルでは、RubyMineを使って backend_app
のプロセスにアタッチしてみます。
以下の順番で設定を行います。
RubyMineにて、プロセスにアタッチしたいシステムのリポジトリを開く
このチュートリアルでは backend_app
リポジトリを開きます。
RubyMineのメニューにて、 Run > Attach to Process
を選択する
実行しているプロセスが表示されるため、 backend_app
のプロセスを選択する
下部のイメージ参照
RubyMineでブレークポイントを設定する
以上で、デバッグの準備が整いました。
curlで frontend_app
にアクセスしてみます。
すると、RubyMineで設定したブレークポイントで停止します。
実行時の各変数の内容も表示され、デバッグできていることが分かります。
tmux
+ overmind
を利用して、連携する一連のシステムやワーカーを起動できるようにしたことにより、より開発を効率的に行うことができるようになりました。
今後も開発を効率的に行う方法をTechBookにて共有していこうと思います。
CPUの数は、設定可能なプロセス数に影響を及ぼします。どのように影響するかについて、では、以下のように言及しています。
Due to the , the Ruby interpreter (MRI) can only run one thread executing Ruby code at a time. Due to this limitation, to fully make use of multiple cores, your application should have a process count that matches the number of physical cores on the system.)
より一部引用
では、コネクションプール数とスレッド数を同じ値にすることを推奨しているようです。実際に、これらの値が同じであっても上記の条件は満たされますし、スレッド数よりも過剰に大きいコネクションプール数を作成したとしても、その分サーバー上のメモリが無駄になるため、基本的にはHerokuの推奨のやり方に従うのが良いでしょう。
vCPUの値は、を参照。
を参考に確認する。今回の想定はstandard-2とする
本サイトの更新情報は、Twitterので発信しています。ご確認ください。
には
これは、で解説があるように、テストでは schema.rb
を見てテスト用のデータベースを作っているためです。
本サイトの更新情報は、Twitterので発信しています。ご確認ください。
なお、作業はピクシブ株式会社さんのを参考にしながら進めました。ありがとうございました。
Rails6.1から7.0へアップグレードを計画した段階で、Rails7.0系のリリースノートを読みました。
また、Railsガイドのアップグレードガイドの ました。
--conservative
オプションにより、指定したgemと指定したgemが直接依存しているgemのみバージョンアップするようになります ()
次に、を見て各種設定ファイルの差分を確認しました。
ため、Springに関する設定だった
なお、調査をする際はさんの記事が参考になりました。ありがとうございました。
を引用します。
を読むと primary_abstract_class
の方が良さそうでしたので、差し替えました。
のうち、気になった点は以下でした。
を読むと、server timing ミドルウェアは便利そうでしたので追加することにしました。
のうち、気になった点は以下でした。
config.active_support.report_deprecations = false
については、 によると一括でdeprecation warningを消せるオプションでした。 ただ、できる限りdeprecation warningは表示したいため、 true
にしておきました。
のうち、気になった点は以下でした。
この変更を提供したところ、 では CI = true
が設定されていたことから config.eager_load
が true
になってしまい、CI/CDまわりで不具合が出ました。
RailsDiffで差分が出たデフォルト値に、 config/application.rb
の中のconfig.load_defaults
があり、値が 7.0
に変わっていました ()。
まず、7.0
とした場合の影響については を参照しながら調査しました。
調査する中で、 に変更内容がまとまっていたため、参考にいたしました。ありがとうございます。
には
に詳細な記載がありました。
を読み、今回のシステムには影響しなかったため、デフォルト値の変更を受け入れました。
に詳細がありました。
本サイトの更新情報は、Twitterので発信しています。ご確認ください。
Procfile
ベースのプロセスマネージャーです ()。
参考:
に従い、 tmux
と overmind
をインストールします。
参考:
参考:
参考:
本サイトの更新情報は、で発信しています。ご確認ください。
当社では、社内で共通に使いたい機能をgemに切り出し、機能の利用側のGemfileでプライベートリポジトリを参照しています。
ローカル端末でのみ利用する場合はgitのURLはgit@ やssh@ で始まるURLを使えば問題なくbundle installできます。しかしCI/CD環境でもbundle installするため、httpsで始まるURLで登録しています。
このプライベートリポジトリをbundle install時に参照する方法について記載します。
まずCLIをインストールしておきます。Macの場合はbrewコマンドでインストールできます。
次に以下のコマンドを実行するとブラウザが開きますので、Githubの認可を行います。
これで、bundle installが成功します。
そうすると、sshで参照するようになるためbundle installが成功します。
github actionsやAWS codebuildなどのCI/CD環境について記載します。
パーミッションについては、プライベートリポジトリを参照してbundle installするだけであれば、「Contents:Read-only」を選択するだけで良いでしょう。
作成後にPrivate Keyを作れるようになりますので、ひとつ作成して秘密鍵をダウンロードしておきます。
画面上部に表示されているAppIDを控えます
Github App 左メニューのInstall Appを選択し、歯車アイコンをクリックします。
必要なリポジトリを選択し、Saveします。
控えておいたGITHUB_APP_IDおよびGITHUB_APP_PRIVATE_KEYを、下図のようにActionのsecretsに、登録しておきます。
Github App経由でtokenを取得します。その値を、環境変数BUNDLE_GITHUB__COM
に設定します。以下にGithub Actionsの設定例を掲載します。
Github Actionsとやっていることは同じです。Github Appsを用意し、GITHUB_APP_IDとGITHUB_APP_PRIVATE_KEYを使って、Access Tokenを取得します。ただしgithub actionsのように公開された再利用可能ワークフローがないため、自前でスクリプトを実行してtokenを取得します。
以下が、buildspec.ymlから呼ぶスクリプトです。
Rails歴が長い人でも、意外とmigrationの追加用のコマンドを覚えていられず、毎回調べていているので、実装で使ったもの・使いそうなものを少しずつ追加しています。
この記事は定期的に内容が追加される予定です。
以下のように、マイグレーションの名前を指定しながらrails generate
コマンドを入力します。
以下のようにコマンドでカラムと型を指定する(YYY
はテーブル名)か、
some_master_records
というテーブルがある前提で、別のsome_transaction_records
テーブルにdefault_some_master_record_id
というカラムを追加して、そのカラムを使ってsome_master_records
テーブルを参照したい場合に使います。
やり方はいくつかあると思いますが、筆者が使うのは、以下です。
その後、以下のようにadd_reference
メソッドにforeign_key
オプションを指定し、そのオプションの中でto_table
を指定します。
これで、some_transaction_records
テーブルに、default_some_master_record_id
カラムが追加され、some_master_records
テーブルへの外部キー制約も作られます。
なお、この場合、ActiveRecordのモデルクラス(SomeTransactionRecord
クラス)にも参照に使う外部キーと参照先のモデルの設定が必要になります(この記事での説明は省略します)。
ActiveSupportには、to_json
という便利なメソッドがあります。
Railsで開発しているときに使う場面としては、DBから取得したレコードをAPIのレスポンスとして返す場合があります。
出力される属性がこれだけなら、それほど大きな問題がないようにも見えます。しかし、userモデルには、サービスへの機能追加に伴ってプライベートな情報が追加されやすいため、そういった情報がto_json
メソッドで出力されてしまったり、あるいは、本人以外のuser情報も併せて一覧で取得するような場合に、他人の email
やプライベートな情報が含まれてしまったりすると個人情報の漏洩問題になる可能性があります。
したがって、to_json
メソッドで出力する属性を制限するべきかどうかについて、注意し検討する必要が出てきます。
幸い、to_json
メソッドでは、オプションを指定することで、出力を絞り込むことができます。
only
特定の属性のみに絞り込む場合に使います。
except
特定の属性を除外したい場合に使います。
include
特定のassociation(has_manyやbelongs_toで指定しているような別のモデル)を出力に含めたい場合に使います。
methods
特定のメソッドを呼び出した結果を含めたい場合に使います。
methodsオプションは、特定の属性を絞り込むというよりかは、追加で情報を出力する用途に使いますが、関連する機能なので併せて説明します。
以下、各オプションの使用例を示します。
さきほどの例で、only
を指定すると、以下のように指定した属性だけが出力されます。
さきほどの例で、except
を指定すると、以下のように指定した属性以外のものが出力されます(※)。
※読みやすくするために、改行を入れています。
さきほどの例で、include
を指定すると、以下のように指定したassociationの属性も併せて出力されます(※)。
※ some_associationという属性がある前提です。
なお、includeで指定したassociation内でも出力する属性を制限したい場合は、以下のように書けます。
さきほどの例で、methods
を指定すると、以下のように指定したメソッドの呼び出し結果も併せて出力されます(※)。
※some_methodというメソッドがある前提です。
直感とは異なる挙動が観察されたため、記事として残します
RDB
PostgreSQL 13.3
トランザクション分離レベル read committed (デフォルト)
実験環境
IntelliJ IDEA 2024.3 (Ultimate Edition)
PostgreSQLにおいて、トランザクション内で同一キーの行を delete - insert する場合、同時に実行される他のトランザクションからは当該の行が参照できない場合があります。
次のようなSQLを考えます。
実験のためIntellijを使い、2つのセッション分のクエリコンソールを開きます。 これら2つをそれぞれセッション1、セッション2とします。
セッション1で5行目まで実行する。この時 delete によってロックが獲得される
セッション2で4行目まで実行する。するとセッション1で行がロックされているため、待ち状態になる
セッション1で7行目の commit まで実行する。これによりセッション1で獲得されていたロックが解放される
セッション1のロックが解放されたことでセッション2の delete が実行される
この時、4 の結果セッション2では delete が空振りし、insert を実行するもすでに id = 1 の行が存在するため、
[23505] ERROR: duplicate key value violates unique constraint "users_pkey"
となります。
MySQL 8.0.28 で同様の実験を行ったところ、エラーは発生しませんでした。データの更新結果はcommitを後に実行している、セッション2の結果が保存されます。 MySQLのトランザクション分離レベルは repeatable read(デフォルト) です。
ひょっとしたら分離レベルに依存した挙動なのでは?ということでPostgreSQLにて分離レベルをrepeatable readとして実験を行いました。 結果前述4のステップで、以下のようなエラーが返されました。
MySQLではrepeatable readの場合と挙動は変わらず、1行返却されました。
PostgreSQLにおいて、delete 文ではなくselect for updateでロックを獲得した場合の挙動についても確認しました。
この場合、セッション2は select for update の行で待ち状態となり、セッション1のcommit後、セッション2の select for update が実行され 0行 の返却となりました。
今回、別の問題を調査していく中でたまたまこの現象に遭遇しました。 個人的には全く想定していない挙動でした。
改めてこちらの記事はPostgreSQLでの挙動になります。 前述の通りMySQLでは異なる挙動となりましたので、RDBMSの実装に依存するようです。
ご興味があれば、ぜひお手持ちの環境でも試してみてください。
上述のような危険な実装をコードレビューのみに頼らずに、Rubyの静的コード解析ツールであるRuboCopで機械的にチェックする方法を、当ページでは記載します。
rubocop gemはインストール済みの前提で記載します。
以下の4行目をエラーとすることを目標にします。
rubocop gemを入れていれば使えるruby-parse
というコマンドでASTを出力します。
検知したいのは、この
の部分です。また、renderメソッドの第二引数のstatusは省略可能な引数ですので、第二引数の有無にかかわらず検知できるようにしたいです。
renderメソッドを呼び出していて
その引数にはjsonという名前付き引数を指定しており
第二引数以降は問わない
というマッチャを記述しlib/custom_cops/dangerous_render_json.rb
として配置します。
次に、.rubocop.yml
にて、今作ったカスタムルールを読み込む設定を追記します。
rubocopコマンドを実行して動作確認をします。
うまくいけば、以下のように、エラーとして検知できます。
cats に含まれる Validated は、複数の入力値のバリデーションを一つにまとめて返すことができる大変便利な型です。
業務において、入力値のバリデーションを行った値をさらに別の入力値として使いたいというケースがままあり、プロジェクトの新規参入者が悩むことがあるため備忘録として記します。
以下のようなコードがあります。
バリデーション済みの Age
を使って Child
のバリデーションを行いたい。
次に記すように flatMap
を使って書けないものでしょうか。
しかし Validated
には flatMap
がないのでコンパイルエラーになってしまいます。
その理由は Validated
は Monad を実装できず Applicative であるからで、公式ドキュメントに詳しくかかれていました。
andThen
メソッドを使うことで Validated
な値を直列に処理することができます。
当然ですが、直列になるため Age
でバリデーションエラーになった場合には Child
のバリデーションは評価されません。 Age
も Child
も満たさない値 2000
で実行してみましょう。
Child
のバリデーションエラー Error("もう大人です")
は追加されていないことが確認できますね。
一つ前の例は入力が1つでしたが、今度は入力が2つ以上のケースです。
普通に書くとネストしてしまいますが、先ほどと同様に flatMap
がないためこのように書くことはできません。
withEither
メソッドを使うと、一旦 Either
に変換してから Validated
に戻せるため、 flatMap
を使ってネストを解消できます。
または
実行してみましょう。両方のバリデーションエラーが合成できていることを確認できます。
andThen
を2回使うことでも一応可能ですが、本来並列にできるはずの Age
と Job
のバリデーションも直列になってしまい、 Age
でバリデーションエラーになると Job
が評価されなくなってしまいます。
実行すると、次のように Job
のバリデーションエラーは表示されず Age
のバリデーションエラーだけが表示されてしまいます。
に記載があるように、BUNDLE_GITBHUB__COM の環境変数にgithubのPersonal Access Token(PAT)を登録することでbundle install時にプライベートリポジトリを参照する方法があります。しかし、GithubはPATの利用を推奨していません。に非推奨の「GitHub recommends that you use fine-grained personal access tokens instead」といった言及がされています。したがって、当記事ではPAT以外を利用した方法について記載します。
以下のコマンドを実行します。
まず、 から、Github Appsを作ります。組織内でのみ利用したいため、「Where can this GitHub App be installed?」の項目は「Only on this account」にチェックしておきます。
GITHUB_APP_ID, GITHUB_APP_PRIVATE_KEYに加え、GITHUB_APP_INSTALLATION_IDをActionのsecretsに登録しておきます。installation idは各リポジトリのSettingsメニューの下部Github Appsを選択し、Github Appsの一覧のConfigureボタンを押した先のURLに含まれています。<installation_id> の形式です。このinstallation_idを控えてください。
get_github_token.jsの内容は以下のとおりです。クラスメソッドさんのより流用、改変しています。
本サイトの更新情報は、で発信しています。ご確認ください。
あるいは、後、次のように、直接、change
メソッド内にadd_column
メソッドを記載します。
まず、。
本サイトの更新情報は、で発信しています。ご確認ください。
しかし、を認証に使っているサービスなどで、何も考えずに、user.to_json
のようなコードを書いてしまうと、以下のようなJSONがレスポンスに返されてしまいます。
本サイトの更新情報は、で発信しています。ご確認ください。
これはPostgreSQLの仕様によるもので、 リピータブルリードトランザクションでは、トランザクションが開始された後に別のトランザクションによって更新されたデータは変更またはロックすることができないため とあります。
PostgreSQLでは削除データはすぐには物理削除されませんが、この辺りが関係しているような気もします。
userの情報を返すAPIを実装する際、render json: user
とするとuserモデルのすべてのフィールドを含むJSONを返してしまい危険です。パスワードはハッシュ化されているものの、deviseが提供するフィールドlast_sign_in_ip
などクライアントに返してはならない個人情報が含まれており、情報漏えいにつながってしまうためです。も参考にしてください。
基本的にはの手順通りです。
上記パターンにマッチするようにマッチャを記述します。_や...はワイルドカードです。詳細はをご確認ください。
本サイトの更新情報は、で発信しています。ご確認ください。
本サイトの更新情報は、Twitterので発信しています。ご確認ください。
AWS上で稼働するアプリが増えてくると、アプリごとにOrganizationを作りたくなります。この際、スイッチロールという機能を使うとOrganizationごとにIAMユーザーを作る必要がなくなり、ユーザー管理をシンプルにできます。本記事ではスイッチロールの方法について記載します。
以下、「スイッチ元Organization」および「スイッチ先Organization」のことを単に「スイッチ元」および「スイッチ先」と記載します。
管理者アカウントで、スイッチ先にログインします。
ロールを新規作成します。
「別のAWSアカウント」を選択し、アカウントIDにスイッチ元のAWSアカウントIDを入力します。「MFAが必要」にもチェックをつけた方が良いでしょう。
次に進み、スイッチ先で割り当てたい権限をつけます。この例では、Administrator権限を割り当てています。
最後にロール名をつけて、ロールの作成完了します。ロール名は「delegate_root_organization」といった分かりやすい名前が良いでしょう。
作成したロールのARNを控えておきます。
管理者アカウントで、スイッチ元にログインします。
ポリシーを新規作成します。Resourceには、スイッチ先で作成したロールのARNを入力します。
次へ進み、ポリシーの名前をつけて、ポリシーの作成を完了します。ポリシー名には「delegate_from_スイッチ先Organization名」といった分かりやすい名前が良いでしょう。また、複数のスイッチ先にスイッチできる権限を持つポリシーも作成できますが、スイッチ先ごとにポリシーを作成し各人が関わる必要最低限の権限を付与できるようにした方が良いでしょう。
スイッチ元にログインします。
スイッチしたいIAMユーザーに、「スイッチ元での設定」で作成したポリシーを追加します。
ではスイッチをしてみましょう。メニューから「ロールの切り替え」を選択します。
スイッチ先のアカウントID、ロール名、表示名を入力します。
「ロールの切り替え」ボタンをクリックすると、スイッチ先にログインしたのと同じ状態になります。
スイッチしていることは、右上のメニューで分かります。上部のバナー全体に色が変わるなどもう少し目立つと良いのですが。
スイッチ元に戻りたい場合は以下のようにメニューから選択します。
開発中にスイッチロール先でAWS CLIを利用したい場面もあるかと思います。 その場合の設定内容について記載します。
以下のようにアカウント設定されているとします。
スイッチ元アカウント
ID: 123456789012
aws configure
でデフォルトのプロファイルに設定済
スイッチ先アカウント1
ID: 210987654321
スイッチで引き受けるロール: arn:aws:iam::210987654321:role/delegate_root_organization
スイッチするときに使うプロファイル名: sw-staging
スイッチ先アカウント2
ID: 111111111111
スイッチで引き受けるロール: arn:aws:iam::111111111111:role/delegate_root_organization
スイッチするときに使うプロファイル名: sw-production
スイッチ元のアカウントを aws configure
で設定済みであれば、およそ以下のような設定になっているはずです。
~/.aws/config
に設定を追記し、以下のようにします。
このとき、設定内容は以下のようにします。
プロファイル名(sw-staging, sw-production
): 任意の名前で大丈夫です。CLIで利用するときにこの名前を指定します
source_profile
: スイッチ元のプロファイルを指定します
role_arn
: スイッチロールで引き受けるロールのARNを指定します
mfa_serial
: スイッチ元でMFAを設定している場合、MFAデバイスのARNを設定します
インタラクティブに実行できるかどうかで方法が変わってきます。
ユーザー自身が aws s3 ls
を実行する場合など、インタラクティブに実行できる場合は、以下の2通りの方法が利用できます。
--profile
引数にプロファイル名を指定
AWS_PROFILE
環境変数にプロファイル名を指定
例としては以下のとおりです。
個人的には、以下のように使い分けると便利かと思います。
引数: チーム内で同じアカウントは同じ名前に統一しておけば、コマンドを共有するだけでスイッチロールして実行させることができます
環境変数: シェルスクリプトにまとまっているときなど、コマンドを書き換えたくないときに便利です
role_wrapper.sh
という名前で以下スクリプトを作成します(jq
コマンドが必要です)。
これを使って、以下のように実行します。
Herokuで稼働するサーバーのベースとするOSのバージョンのようなものです。
以下で、Stackの確認ができます。2021年10月現在は、heroku-20がデフォルトStackです。
新しいStackに切り替えたい場合や、ステージング環境を作るときに本番に合わせて少し古めのStackを使いたい場合など、StackをデフォルトStackから変更したい場合は、以下のコマンドを使います。
なお、Stackのupgradeは、Herokuの管理者用のWeb画面からも変更できます。downgradeは画面からはできないようです。
弊社ではAWSリソースをTerraformやCloudFormationで管理し、plan/applyの実行にはGithub Actionsを使っています。本稿ではGithub ActionsからAWSリソースを操作するためのIAMロールの設定について説明します。
Github ActionsからAWSリソースを操作するために利用する認証方法には以下のふたつが挙げられます。
アクセスキーおよびシークレットキー
IAMロール
アクセスキーによる方法にはセキュリティリスクが伴います。アクセスキーとシークレットキーがありさえすればどこからでも使え、漏洩すると不正利用される可能性があるためです。さらに、TerraformやCloudFormationによる処理に割り当てられるIAMポリシーは広大になりがちなので、漏洩した時の被害が甚大になる可能性があります。
一方、IAMロールを利用する方法では、一時的な認証情報(STSトークン)を発行することで、安全かつ柔軟にAWSリソースへアクセスできます。特に、GitHub ActionsのようなCI/CD環境では、OIDC(OpenID Connect)を活用することで、アクセスキーを不要にし、より安全な認証フローを実現できます。
IAMのメニューから「IDプロバイダ」を選択し「プロバイダを追加」をクリックします。
プロバイダのタイプで「OpenID Connect」を選択します。
以下を入力します。
URLにtoken.actions.githubusercontent.com
対象者にsts.amazonaws.com
GitHub Actions用のIAMロールを作成します。
作成したロールに、必要十分なIAMポリシー(S3、CloudFormation、Lambda等)を設定します。
作成したロールの信頼関係タブで以下のように信頼ポリシーを設定します。PrincipalのARNやgithubのリポジトリ名は環境に合わせて変更してください。
利用したいGithub Actionsのyamlに以下を記載します。
次に、Github ActionsのSecretsに AWS_ROLE_ARN
を登録します。
これは必ずしも必須ではありませんが、Github Actionsが意図通りに動かない場合は、以下のコードをyamlに追記してIAMロールを適切に引き受けられているのかを確認すると良いです。
以上で設定完了です。アクセスキー方式より煩雑ではありますが、一度行えばセキュリティを保ってGithub Actionsを運用できます。
本サイトの更新情報は、Twitterので発信しています。ご確認ください。
にて、AWSコンソールでのスイッチロールの方法について記載しました。
また、を利用すると aws-vault exec
を使って同様のことが実現できます。をご参照ください。
本サイトの更新情報は、Twitterので発信しています。ご確認ください。
を見ると分かりますが、Ubuntu Linuxのバージョン番号とStack名が揃えられているようです。
本サイトの更新情報は、Twitterので発信しています。ご確認ください。
ビジュアルリグレッションテスト(以下、VRT)は、画像回帰テストとも呼ばれます。
VRTは、改修による予期せぬ UI のデザイン崩れを検出することを目的としています。 UIのスクリーンショットを撮り、それらをコミット間で比較して、変更を特定します。
当社のReactを利用したプロジェクトではデザインシステムの構築にStorybookを利用しています。 細かい運用はこの記事では割愛しますが、見た目上のバリエーションが存在するコンポーネントについては、1つのコンポーネントにつき下記の2画面を用意するようにしています。
Basic: StorybookのControlsアドオンを利用して動作確認できる
All: バリエーションが一覧できる
reg-keygen-git-hash-plugin: 比較すべきコミットを特定します。
reg-notify-github-plugin: GitHubのPRにレポートを通知します。
reg-publish-s3-plugin: 差分レポートをS3にアップロードします
これらのツールを組み合わせることで、Pull Requestにテスト結果の通知が届くようになります。 通知内のリンクから、さらに詳細なレポートを確認することもできます。
yarnやnpmを利用して、パッケージのインストールをおこなってください。 npm-scriptsにも、CIで動かすためのタスクを追加します。
GitHub Actions で正常に動かなったため、タスクを「Storybookのビルド(ci:storybook-generate)」と「スクリーンショットの撮影(ci:storycap)」に分割しています。
CIツールによっては、タスク分割せずに実行が可能かもしれません。
reg-suitの設定ファイルです。
のコマンドでひな形を生成することができます。
ワークフローをトリガーするGitHubイベントは、pull_requestではなく、pushである必要があります。
ubuntu-latestのイメージには、日本語が含まれていないため、日本語フォントインストールのジョブを入れています。Storybook上で日本語を利用していない場合は不要です。
これらのID/KEYは、GitHubのsecretに登録する必要があります。 以上でVRTのために必要な設定は完了です。
上記の設定だけでもVRTとして正しく機能しますが、プレセナでは画面幅に応じた見た目の変化など細かくUIを確認できるようにしています。
上記のような記述を追加することで、画像幅の異なるスクリーンショットを撮ることが可能になります。これによって、網羅性の高いテストを目指すことが可能です。
最近、当社の開発では単一のホストだけでWebアプリケーションをホストせずに、フロントエンドとバックエンドを別のサーバーでホストすることが多くなってきました。
そして、これまでは遭遇してこなかったブラウザやJavaScriptに関するセキュリティの仕様に対処することが頻繁に起こるようになってきました。
この記事では、それらに対処する際に調べて把握した知識の一部であるSame Origin PolicyとCORSについてまとめます。
WhatWGの定義によると、Originには、以下の2種類があります。
An opaque origin
A tuple origin
An opaque Originは、定義によると内部的に使うオリジンで、オリジンをシリアライズして復元することはできず、シリアライズした場合はnullとなるようです。あまり利用イメージが湧きませんが、我々の普段のWeb開発ではあまり使うことはなさそうに思います。念の為に定義文を引用しておくと、以下です。
An opaque origin
A tuple originは、以下の要素で構成されます。
Scheme(https、http、ftp、などURLの先頭部分につけるもの)
Host
Port
Domain
Domainは、Hostの一部であるからか、一般的に、以下の3つをOriginを構成する要素と記載されることも多いようです。この記事でも以下の3つでOriginが構成される想定で、以降の説明を記載します。
Scheme
Host
Port
つまり、https://www.precena.com:8080/index.html のようなURLがある場合に、A tuple originとは、
https(Scheme)
www.precena.com (Host)
8080 (Port)
の組み合わせで構成されます。
URL
同じかどうか (True/False)
理由
http://www.precena.co.jp
https://www.precena.co.jp
False
Schemeが異なる
https://www.precena.co.jp/
https://www.precena.co.jp/company/info
True
Path(/
と/company/info
)が異なるが、Scheme、Host、Portは同じ
https://data.precena.co.jp/
https://www.precena.co.jp/
False
Hostが異なる( data.precena.co.jp
と www.precena.co.jp
)
https://www.precena.co.jp/
https://www.precena.co.jp:3000/
False
Portが異なる(80
と3000
)
ブラウザにSame Origin Policyが実装されているため、リクエスト先のOriginで、後述のCORSの許可がされていなければ、XmlHttpReuqest
やfecth API、<img>
タグを使ったリクエストなどは、異なるOriginに対してはできません。
具体的には、Same Origin PolicyによってアクセスできないようなOriginをまたいだリソースに、あらかじめ、HTTPのヘッダをつけておくことで、アクセス可能にする仕組みです。
例えば、以下のようなHTTPのヘッダを使います。
HTTPヘッダ
ヘッダに記載する内容
Access-Control-Allow-Origin
どのオリジンからのアクセスを許可するか。* も指定可能。
Access-Control-Allow-Methods
どのHTTPメソッドからのアクセスを許可するか。*も指定可能。
ヘッダは、これ以外にもありますが、ここでは記載を省略します。
Railsアプリケーションでは、rack-cors
というgemを使って簡単にCORSの設定を行うことができます。
インストール方法や設定方法は、上記公式サイトに記載されているので、ここでは記載を省略します。
AWS CLIやterraformでスイッチロール時にMFAを利用する際、コマンドを実行する都度MFAコードを入力するのは煩雑です。AWS Vaultを使うとスイッチロール後のプロファイルのシェルに入れるようになります。
defaultプロファイルでIAM ユーザーによるログインをし、some-profile
というプロファイルに設定しているAWSアカウントにスイッチロールする、という構成を前提に、本記事は記載します。具体的には以下の設定を前提とします。
homebrewでコマンドをインストールします。
インストールの確認を兼ねてコマンドを打ってみます。
default profileにcredentialを追加します。以下のコマンドを実行すると、IAMキーとシークレットを求めるプロンプトが出るので、入力します。
最後に、以下のコマンドを実行します。するとMFAのコードを入力を求めるプロンプトが出るので、入力します。
以上で、上記コマンドを実行したターミナルではsome-profileにスイッチロールした状態になります。
AWSリソースに対する異常検知として、CloudWatch Metricsに対して設定したしきい値を超過した場合、CloudWatch Alarmで通知を飛ばすことができます。
日頃Slackをよく見ている身としては、何かあったらSlackチャンネルへ通知されると嬉しいことから、仕組みを考えました。
Slackへ通知する仕組みとして思い浮かぶのはLambdaやChatBotです。
ただ、監視対象が異常となることは稀なものについては、Lambdaよりメンテナンス不要、かつ、ChatBotよりもカスタマイズ可能なものを採用したくなりました。
調べてみたところ、以下のStack OverflowにあるSam氏の回答 Amazon SNS + Slack Workflow 構成で設定・運用できそうでした。
ここでは、実際に構成してみた時の内容を記載します。
今回設定が必要な各リソースは以下となります。
AWS
CloudWatch Alarm
Amazon SNS (以降、SNSと表記)
今回はSlack通知のみ行い、Eメール通知は行わない
Slack
Slack Workflow
では、実際に各サービスを設定していきます。
CloudWatch Alarmを設定する際のウィザードではSNSトピックを作成可能です。 ただ、そのときに作るSNSトピックではEメール通知が必須となってしまいます。
そこで、今回は事前にEメール通知が不要なSNSトピックを作成します。その後、CloudWatch Alarmへ割り当てることにします。
今回のSNSトピックの設定は以下の通りです。 なお、残りの項目はデフォルトのままにします。要件に応じて変更してください。
タイプ
スタンダード
名前
hello_alarm_topic
続いて、CloudWatch Alarmを設定します。
今回必要なCloudWatch Alarmの設定は以下の通りです。
アラーム状態トリガー
アラーム状態
次のSNSトピックに通知を送信
既存のSNSトピックを選択
通知の送信先
hello_alarm_topic (上記で作成したSNSトピック)
アラーム名
hello_world_alarm
なお、設定する途中にあるEメールエンドポイントにて トピックにエンドポイントがありません
と表示されますが、Eメール通知を行わない設定の場合はこのままで問題ありません。
ここまででAWS側の準備ができました。次はSlack Workflowの設定になります。
Slack Workflowでは、SNSトピックのサブスクリプションを受け取れるように設定します。
ただ、AWSのSNSトピックのサブスクリプションを受け取るには、受け取る側で事前に SNSトピックのサブスクリプションの確認
(以降 サブスクリプション確認
と表記)を済ませておく必要があります。
そこで、Slack Workflowでサブスクリプションの確認ができるよう設定します。
まず、Slackチャンネルの詳細にある Integrations
タブから Add Automation
> New Workflow
> Build Workflow
の順にクリックします。
新しいSlackウィンドウが開きます。
右側に表示されているメニューから From a webhook
をクリックします。
Choose how to start the workflow
ダイアログが表示されます。
ここでは何も入力せず、 Continue
をクリックします。
Slackウィンドウの表示が変わります。次は Starts with a webhook
をクリックします。
Change how this workflow starts
ダイアログが表示されます。
ここでは Set Up Variables
をクリックします。
KeyとData Typeの入力が求められるので、それぞれ次の値を設定し、 Done
をクリックします。
Key
SubscribeURL
Data Type
Text
ここで SubscribeURL
というキー名は、サブスクリプション確認をするときに、AWSから渡されてくる項目を指しています。
そのため、 SubscribeURL
というキー名以外で設定した場合はサブスクリプション確認ができなくなるため、注意してください。
次に、Web request URLにある Copy Link
をクリックし、URLをコピーします。
このURLは、AWSの設定でSNSトピックをサブスクライブするときに使います。
これで Change how this workflow starts
ダイアログでの設定は完了するため、 Save
をクリックします。
続いて、 Then, do these things
の設定を行います。
まず、右側のStepsの検索窓に send
と入力します(①)。
次に Send a message to a channel
をクリックします(②)。
Send a message to a channel
ダイアログが表示されるため、設定を行います。
サブスクリプション確認用のメッセージを設定するため、右下にある {} Insert a variable
をクリックし、先ほど作成した SubscribeURL
を選択します。
すると、 Add a message
の中に、 SubscribeURL
が指定されます。
これにより、Slackチャンネルへ投稿する際に、Slack Workflowへ通知された時にキー SubscribeURL
の値が展開されます。
あとはこのまま Save
をクリックします。
設定が終わったので、右上の Finish Up
をクリックします。
Finish Up
ダイアログにて、 Name
に任意の名前(今回は hello_world_slack
)を設定した上で、 Publish
をクリックします。
以上で、Slack Workflowでサブスクリプション確認を可能にする設定は完了です。
再びAWSの設定へと戻り、SNSトピックにSlack Workflow向けのサブスクリプションを作成します。
上記で作成したSNSトピック hello_alarm_topic
を表示し、 サブスクリプションの作成
をクリックします。
続いて、以下の設定を持つサブスクリプションを作成します。
トピックARN
hello_alarm_topic
のARN
プロトコル
HTTPS
エンドポイント
Slackでコピーした Web request URL
の値
rawメッセージ配信の有効化
チェックしない(デフォルトのまま)
なお、 rawメッセージ配信の有効化
をチェックしないことで、CloudWatch Alarmの情報はSNSの Message
キーへと設定されます。一方、rawメッセージ配信を有効化してしまうと Message
キーがなくなってしまうので、注意してください。
ここまででサブスクリプション確認向けの設定が完了したため、実際にサブスクリプション確認を行います。
今回の場合、SNSにサブスクリプションを作成した時点で、Slackチャンネルにサブスクリプション確認用URLが投稿されます。
投稿されたURLをクリックすると、ブラウザが開き、 ConfirmSubscriptionResponse
のXMLが表示されます。
続いて、AWSのサブスクリプションページをリロードすると、ステータスが 確認済
になります。
これでサブスクリプション確認は完了です。
サブスクリプションの確認ができたので、Slackへ投稿するメッセージを正式版へと修正します。
今回は、
SNSから受け取った Message
キーの値
独自メッセージ
を投稿します。
最初に、先ほど作成したWorkflowを開きます。
続いて、 Starts with a webhook
をクリックし、 Data Variables を編集します。
Key SubscribeURL
をKey Message
へと変更します。
続いて、 Then, do these things
の Add a message
を編集します。
独自メッセージを テストアラートが届きました
へと変更
Insert a variable
で Message
を挿入
最終的には、以下のスクリーンショットのような設定になります。
以上で設定は終わりです。 Save
をクリックした後、右上の Publish Changes
をクリックし、変更後のWorkflowを公開します。
以上で全体の仕組みが完成したため、動作確認を行います。
動作確認をするためには CloudWatch Alarmのステータスを アラーム状態
にする必要があります。
メトリクス対象を操作して アラーム状態
とすることもできますが、手間がかかります。そこで今回はCloudWatch AlarmのステータスをAWS CLIで強制的に変更することで、動作確認とします。
まずは適切なAWS環境を使えるよう、AWS Vaultを使って適切なシェルに入ります。
CloudWatch Alarmを見ると、ステータスが アラーム状態
へと変化しています。
Slackのチャンネルを見ると、CloudWatch Alarmからの情報が投稿されていました。 動作は良さそうです。
確認が終わったので、CloudWatch Alarmのステータスを OK
へと戻しておきます。
もし、今回作成したCloudWatch Alarmを無効化しておきたい場合は、以下の手順で行えます。
アクション > アラームアクション > 無効化
以上で、Amazon SNS + Slack Workflowを使って、CloudWatch Alarmの通知をSlackチャンネルへ投稿することができました。
CloudWatch AlarmからSlackチャンネルへの投稿する方法について、Lambdaよりメンテナンス不要、かつ、ChatBotよりもカスタマイズ可能としたい場合の参考となれば幸いです。
集
特定のバックアップを指定してダウンロードしたい場合は、以下のようにバックアップID(bxxx
と記載した部分)を指定します。
※ここでダウンロードしたファイルは、次の節のようにpg_restoreコマンドでPostgreSQLに復元できます。
なお、homebrewでPostgreSQLをインストールした場合、PostgreSQLはkeg onlyなので、pg_restore
コマンドへのPATHが通っていない状態になっています。
その場合は、以下のように、ローカルで利用しているPostgreSQLの特定のバージョンのpg_restore
コマンドに対して、パスを通す必要があります。
PostgreSQLのインスタンス自体は、Dockerの中で動作していてもpg_restore
コマンドを実行できます。その場合、コンテナ内のPostgreSQLが動作しているポートがローカルホストのポートにバインドされていなければなりません。
Docker内のPostgreSQLインスタンスに復元する場合でも、pg_restore
コマンドをホスト環境側(macOSのターミナルなど)で実行する場合は、ホスト環境にPostgreSQLをインストールして、pg_restore
コマンドを使えるようにしておく必要があります。
AWS Lambda関数について、
ソースコードはgitで管理したい
ソースコードのデプロイは容易に行いたい
AWSの各リソースはTerraformで管理しており、別途Lambda向けのものを作る必要はない
lambrollはREADMEが充実していることもあり、悩むところは少なくてすみました。
それでも、組織のAWS環境で lambroll
を使う場合にはいくつか考慮することがあったため、この記事で紹介していきます。
スイッチロール時にMFAを利用するAWS環境の場合、lambrollだけではデプロイすることができません。
実際に見ていきます。
まず、 aws-vault exec
により、スイッチロール後の some-profile
のシェルに入ります。
続いて、lambrollで関数のデプロイを行います。
例えば
staging
production
の2つの環境があり、各環境へ同一ソースコードの関数をデプロイしたくなったとします。
この場合、以下の方法で実現できます。
lambrollでLambdaを定義する時に使うJSON (function.json
) を環境ごとに用意する
例
staging環境向けは function.staging.json
production環境向けは function.production.json
各環境の環境変数は、JSONの Environment
キーの下にそれぞれ定義する
環境変数の値をJSONに含めたくない場合は、AWS SSMから取得するよう定義する
デプロイ時、 --function
で環境にあったJSONファイルを指定する
実際に見ていきます。
まずは function.staging.json
を用意します。
なお、スペースの都合上、例では FunctionName
と Environment
キーだけ記載しています。
続いて、AWS Consoleなどから、AWS SSMにキーを作成します。
今回は /bar/baz
というキーに値を設定します。
最後に、staging環境向けにデプロイします。
一方、production環境向けにデプロイする場合は以下となります。
lambrollではLambda Layerを作成することができません。
実際に見ていきます。
最初に、 aws-vault exec
にて some-profile
のシェルに入ります。
次に、AWS ECRへdocker loginします。なお、 <>
の部分は適宜読み替えてください。
続いて、デプロイするDockerfileを用意します。ここでは省略します。
その後、docker buildにて、タグ付きでDockerイメージをビルドします。
docker pushにて、AWS ECRへDockerイメージをpushします。
さらに、lambrollでAWS ECRにあるコンテナイメージを使うよう、 function.json
へ PackageType
と Code
キーへ設定を追加します。
例えば、以下では、AWS SSMにある「ECRにあるイメージのURL( /path/to/ecr_image_url
)」を指定しています。
最後に、上記のJSONを使ってAWS ECRにあるコンテナイメージをAWS Lambdaへデプロイします。
lambrollとAWS Vaultを使ってデプロイをしていると、以下のようなエラーメッセージが表示されるかもしれません。
これはAWS Vaultのセッションが切れたのが原因です。
解消するには、一度AWS Vaultのセッションを exit
で抜けた後、再度AWS Vaultのセッションに入ります。
AWS SESでメール送信環境を構築したときは、合わせて メールが不達になったこと
を検知する仕組みも構築します。 AWS SESから送信したメールでメール不達があまりにも多いと、AWS SESの利用が停止されるためです。
さらに、AWS SESの本番運用を始める前に、メール不達を検知する仕組みの動作確認も大切です。
シミュレーターには バウンス
や 苦情
などのシナリオが用意されています。
このシナリオを使えば、AWS SES環境のバウンス率・苦情率に影響することなく、メール不達を検知する仕組みの動作確認ができます。
さらに、シナリオとして用意されていないケース、例えば
カスタムメールヘッダ付きのメールを送信し、メールが不達になる
reject
イベントが発生するメールを送信し、メールが不達になる
であっても、シミュレーターを使って動作確認ができます。
この記事では、シナリオとして用意されてないケースに対する、シミュレーターの使い方を記載します。
なお、不達を検知する仕組み 「AWS SESで発生したイベントの通知情報が AWS SES → AWS SNS → AWS SQS
の順で流れていく環境」 は、すでに構築済であるとして、ここでは記載しません。
まずはこのケースを試してみます。
今回は、「カスタムメールヘッダ X-My-Custom-Header
を含むメールを送信したが、バウンスによりメールが不達になる」ケースをシミュレーターで試してみます。
まずはシミュレーターで以下の設定を行います。
Eメール形式欄では、 Raw
を選択する
シナリオ欄では、 バウンス
を選択する
メッセージ欄では、以下のようなMIMEメッセージを入力する
参考までに、設定した後のスクリーンショットは以下となります。
以上で準備ができました。
では、シミュレーターの テストEメールの送信
ボタンをクリックしてメールを送信してみましょう。
すると、このメールはバウンスし、AWS SQSへとイベント情報が連携されます。
AWS SQSのコンソールにて確認すると、以下のスクリーンショットのようなメッセージを受信できました。カスタムメールヘッダ X-My-Custom-Header
が含まれています。
まず、「 reject
(拒否)イベントとは何か」 から記載します。
rejectイベントについて、AWSドキュメントの記載は以下です。
拒否イベントのテスト
Amazon SES を介して送信するすべてのメッセージでウイルスがスキャンされます。ウイルスを含むメッセージを送信すると、Amazon SES はメッセージを受け入れ、ウイルスを検出して、そのメッセージ全体を拒否します。Amazon SES でメッセージが拒否されると、メッセージの処理が停止され、受取人のメールサーバーへのメッセージ配信は試行されません。次に、拒否イベントが生成されます。
ただ、「実際にウィルスを含むファイルを作成し、AWS SESでそのファイルを添付してメールを送信する」を試すのは色々問題があります。
その問題を避けるため、AWSドキュメントにあるように、AWS SESでは
拒否イベントは、欧州コンピューターウイルス対策研究所(EICAR)テストファイルを使用してテストできます
という方法で reject
イベントの発生をテストできそうです。
しかし、シミュレーターではEICARの内容をそのまま添付することはできません。
文字列をbase64エンコードするには、各プログラミング言語のライブラリを使うのが簡単です。
今回はRubyのirbを使って base64
エンコードします。
これでEICARテストファイルは添付できそうです。
では、実際にシミュレーターで送信してみましょう。
今回は、「カスタムメールヘッダ X-My-Custom-Header
があり、かつ、EICARファイルを添付した状態でメールを送信する」ケースをシミュレータで試してみます。
まずはシミュレーターで以下の設定を行います。
Eメール形式欄では、 Raw
を選択する
シナリオ欄では、 カスタム
を選択する
カスタム受信者欄では、任意の受信可能なメールアドレスを指定する
メッセージ欄では、カスタムメールヘッダと添付ファイルを含んだMIMEメッセージを入力する
ここで、添付ファイルを含むMIMEメッセージをゼロから作るのは手間がかかります。
カスタムメールヘッダ X-My-Custom-Header
を追加
元々ある添付ファイルの値を、EICARテストファイルをbase64エンコードした値へと差し替え
という編集を加えて利用します。
具体的には、以下の内容をメッセージ欄へと入力します。
次に、シミュレーターの テストEメールの送信
ボタンをクリックしてメールを送信してみましょう。
すると、このメールの送信時にrejectイベントが発生し、AWS SQSへとイベント情報が連携されます。
AWS SQSのコンソールにて確認すると、以下のスクリーンショットのような
eventTypeが Reject
カスタムメールヘッダ X-My-Custom-Header
が含まれる
というメッセージを受信できました。
以上より、シミュレーターのシナリオとして用意されていないケースであっても、シミュレーターで試せると分かりました。
が存在しますが、プレセナでは下記のツールを利用しています。 以下、VRTを導入していく手順を紹介します。
: Storybookをクロールし、スクリーンショット画像を取得します。
: 画像の差分をレポートとして出力してくれます。
storycapの公式が推奨するコマンドは下記のため、 ( )
REG_NOTICE_CLIENT_IDは、を参考に、 GitHubにreg-suitアプリを追加して取得してください。
AWS_ACCESS_KEY_IDとAWS_SECRET_ACCESS_KEYは、regconfig.jsonに記載したbucketにアクセスできるものをIAMなどで作成してください。 ポリシーなどの詳細は、を参考にしてください。 S3の細かい設定はこの記事では割愛します。
本サイトの更新情報は、Twitterので発信しています。ご確認ください。
に詳細が記載されていますが、Webのセキュリティモデルの基本となるものです。Originを共有するWebのリソースは互いに信用でき、それらの著作者(著作組織)も基本的には同じであると考えます。
An internal value, with no serialization it can be recreated from (it is serialized as "null
" per ), for which the only meaningful operation is testing for equality.
引用:
(日本語訳サイトは)に詳細な説明がありますが、要するに、特定のOriginのページ(html)やスクリプト(JavaScript)が、別のOriginのリソース(APIやページなど)にアクセスするのを防ぐブラウザの仕組みです。
Cross Origin Resource Sharingの略で、 (日本語訳サイトは)や (日本語訳サイトは)に詳しい説明が記載されています。
本サイトの更新情報は、Twitterので発信しています。ご確認ください。
本サイトの更新情報は、Twitterので発信しています。ご確認ください。
なお、今回は通知さえ飛ばせればどんなメトリクスでも構いません。AWSドキュメントで紹介されているチュートリアルを参考にメトリクスを設定しても良いでしょう。
なお、AWS Vaultについての詳細は、を参照してください。
続いてAWS CLIを使ってアラーム状態にします。
すべてに記載されている内容ですが、よく使うコマンドをすぐに参照できるように、一覧としてまとめます。
( 公式サイトの説明ページは、)
本サイトの更新情報は、Twitterので発信しています。ご確認ください。
という場合には、Lambdaのデプロイツールである lambroll
を使うのが便利です。
そこで、別記事でも紹介している AWS Vault
と組み合わせることでデプロイできるようになります。
その代わり、lambrollではContainer Imageでのデプロイにて代替できます。
その動作確認で役立つのが Amazon SES メールボックスシミュレーター
です (以降 シミュレーター
と表記)。
以下のドキュメントにあるように、シミュレーターでファイルを添付したい場合、ファイルの中身の文字列を base64
エンコードして メッセージ
欄へ設定する必要があるためです。
そこで、AWSドキュメントの をベースに
本サイトの更新情報は、Twitterので発信しています。ご確認ください。
今回2社同時に弊社Webシステムの脆弱性診断を実施いただく機会がありました。なかなか同時に2社にみていただくという機会はないと思い、記事にしてみたいと思います。
A社とB社の2社に、同時にある弊社Webシステムの脆弱性診断を依頼させていただきました。 A社はWebアプリケーション診断を得意とし、B社はソースコード診断を得意とする会社様です。 Webアプリケーション診断とソースコード診断は、それぞれ以下のような特徴を持ちます。
Webアプリケーション診断
攻撃者目線でWebアプリケーションに対して擬似的に攻撃を行う診断。 「脆弱性診断」と聞くとこちらをイメージする方が多いかもしれません。
ソースコード診断
ソースコードを直接目視にて確認する、ソースコードレビュー型の診断。 プログラム構造や設計に起因する問題を発見できる可能性が高く、外部からの攻撃による診断では発見が難しい脆弱性を発見できる可能性がある。
Webアプリケーション診断、ソースコード診断、どちらも何点か指摘事項をいただきましたが、注目すべきは同じ指摘が1つもなかったことです。
詳しい指摘事項についてここで書くことはできませんが、外部からの攻撃で発見可能な問題と、内側からのソースコード診断で発見可能な問題は異なる可能性がある、ということが実感できました。
もちろんソースコード診断については外部から攻撃されることのない脆弱性であれば問題がない、という見方もできるかもしれません。しかし今回いただいた指摘に関しては、たまたま外部からの攻撃で発見されなかったと考えられるものもありました。
ソースコード診断もWebアプリケーション診断もそれぞれ特徴があることがわかりました。 以下にまとめてみたいと思います。
診断を行う観点が異なるため、出てくる指摘事項の性質も異なります。
Webアプリケーション診断 外部から実際に擬似攻撃を行うため、危険度の高低はあれど、確実な問題を指摘していただける。
ソースコード診断 外部からの攻撃が成立するか否かに関わらず丁寧に診断いただけるため、ソースコードについても将来問題になる可能性を指摘していただける。
Webアプリケーション診断の方が、脆弱性診断の依頼にかかる手間が大きいです。これは以下のような理由によります。
Webアプリケーション診断
診断対象がAPI単位になる。APIの本数で値段が変化するため、予算によっては対象のAPIを絞るといった作業が必要になる。
APIの仕様など、アプリケーションの仕様について連携する必要がある。
脆弱性診断実施用の環境を用意したり、開発用のサーバーを一時的に脆弱性診断用に利用いただくのであれば診断予定日は開発者が利用しないようにするなど、調整が必要になる。
ソースコード診断
基本的にはソースコードを開示すれば良い。
アプリケーションの実行環境は用意するが、攻撃を行うわけではないため、サーバーのブロックなどが必要ない。
今回診断を2社に依頼したところ、複数のご指摘をいただきましたが、重大な脆弱性につながる指摘はなく、アプリケーション自体はよくできている、との評価をいただくことができました。
いただいたご指摘は軽微なものであるものの、すでに対応を実施し、完了しています。
Webアプリケーション診断とソースコード診断は診断の手法および視点が異なるものであり、出てくる指摘事項も異なるものとなります。 目的に応じてベストな診断は変わってくるかと思います。 適切な診断方法を選択するのが良いでしょう。
Zendeskにおいて、社内の複数サービスの問い合わせフォームを一本化し、そこに送られてきた問い合わせを対象サービスの担当者に自動で振り分けてメールで通知するための設定について記載します。
Zendesk環境
弊社の以下3サービスの問い合わせフォームをZendeskで一本化します。
PLS(eラーニング事業)
プレセナアカデミー(toC向け研修事業)
Nest(研修管理システム)
また、サービスによってフォームの入力項目を自動的に変更し、各サービスごとに適切な情報を収集できるようにします。 具体的には、「対象サービス」「お名前」「ご利用環境」など全サービス共通で入力してほしい項目は常にフォームに表示しますが、 各サービスにおいて独自で入力してほしい項目(例えば「ユーザーID」など)については、 フォーム内でそのサービスが選択された場合のみ表示するようにします。
さらに、フォームから受け付けた問い合わせを自動的に対象サービスの運用担当者に振り分けた後、メールで担当者に通知するようにします。
なお、以下の手順では、PLSを設定する際の流れを記載します。
カスタムチケットフィールドを作成する
フォームの質問項目を検討する
今回の場合、作成したいチケットフィールドの種類は大きく分けて二つあります。
どのサービスにも共通して必要なもの
あるサービスのみに必要なもの
上記を踏まえ、以下のチケットフィールドを作成することとします。
対象サービス
所属企業名
お名前
ご利用環境
PLS用フォーム
なお、チケットの件名や説明のフィールドは、標準フィールドとして既に存在しますので、新たに作成する必要はありません。
チケットフィールドを設置する
まずは管理センターのサイドバーにある[オブジェクトとルール]をクリックし、[チケット]の下の[フィールド]を選択後、右上の[フィールドを追加]をクリックします。
ここでの注意点として、[権限]で選択するオプションは[顧客は編集可能]にします。
今回の場合、フォームの内容はすべて顧客に入力してもらうチケットフィールドしかないからです。チーム内での管理のためにエージェントのみが入力できるチケットフィールドを作成する場合は[エージェントは編集可能]を選択します。
また、[顧客は編集可能]を選択することで、ページの下部に以下が表示され、リクエストの送信時に入力を必須とするかどうか選択することができます。
入力が完了したら忘れずに[保存]します。
問い合わせフォームを作成する
上記で作成したチケットフィールドから必要なものをピックアップしてフォームを作っていきます。
まずは管理センターのサイドバーにある[オブジェクトとルール]をクリックし、[チケット]の下の[フォーム]を選択後、[フォームを追加]をクリックします。
フォームのタイトルを適切なものに設定します。
[利用可能なチケットフィールド]からフォームに追加したいチケットフィールドの[+]を選択します。 チケットフィールド「対象サービス」の選択内容によって他のチケットフィールドを出し分ける設定は後ほど行いますので、ここではフォームの中に含めたいすべての項目を選択しておきます。
なお、選択したチケットフィールドの並べ替えも可能です。 以下赤枠内のアイコンをドラッグすることで並べ替えができます。
フォームに盛り込むチケットフィールドの選択が完了したら忘れずに[保存]します。
作成したフォームに条件を設定する
チケットフィールド「対象サービス」で選択する内容によって、フォーム内に表示されるチケットフィールドが切り替わるように設定します。 具体的には、「対象サービス」で「PLS」を選んだ場合、PLS用のチケットフィールドがフォームに表示されるようにします。
設定方法
まずは管理センターのサイドバーにある[オブジェクトとルール]をクリックし、[チケット]の下の[フォーム]を選択後、条件を追加したいチケットフォームの右側の[︙]から[条件]を選択します。
[条件の適用対象]を[エンドユーザー]にし、[条件を追加]ボタンをクリックします。
表示されるダイアログボックスで以下の画像のように設定します。 これで、チケットフィールド「対象サービス」で「PLS」が選択されたら「所属企業名」「ご利用環境」「PLS用フォーム」「お名前」(+フィールド設定で必須入力としているもの)のフィールドを表示できるようになります。
なお、チェックボックスにて必須とするかどうかも忘れずに設定します。 チケットフィールドで設定した必須要件よりも、こちらの条件設定チケットフィールドの内容が優先されるため、フォーム上で入力必須としたいものはここでも設定しておく必要があるからです。 ここで必須にチェックを入れない場合、フィールドでは必須に設定していても、実際にフォームで入力する際に必須とならないので注意してください。
入力が完了したら忘れずに[保存]します。
振り分け先および通知先となるグループを作成する
管理センターのサイドバーにある[メンバー]をクリックし、[チーム]の下の[グループ]を選択後、画面右上の[グループを追加]をクリックします。
[グループ名][グループメンバー]などを設定します。 今回の場合、[グループ名]にはPLS、[グループメンバー]にはPLSの担当者を追加しておきます。
入力が完了したら忘れずに[保存]します。
振り分けとメール通知を発動する条件を設定する
管理センターのサイドバーにある[オブジェクトとルール]をクリックし、[ビジネスルール]の下の[トリガ]を選択後、[トリガを作成]をクリックします。
[トリガ名][カテゴリ]などを設定します。
どんな時にアクションを実行するかを[条件]で設定します。 条件は複数設定が可能です。 なお、条件に当てはまった際のアクションは後で設定しますので、ここでは設定しません。
今回は以下のように設定しました。
チケットフィールド「対象サービス」で「PLS」が選択されていること
チケットのステータスが新規であること
振り分けとメール通知を実行するよう設定する
上記で設定した条件が満たされたときに実行される[アクション]を設定します。 アクションは複数設定できます。
今回は以下のように設定しました。
問い合わせチケットを「PLS」というグループに割り当てる
「PLS」というグループに所属しているメンバーにメールで通知する
なお、メールで通知する際の件名や本文はカスタマイズすることができます。 本文の{{ticket.url}}の部分をプレースホルダといい、チケットに関する様々な情報を取得することが可能です。 プレースホルダは[メールの本文]の入力欄の下にある[使用可能なプレースホルダ]からコピーすることができます。
入力が完了したら忘れずに[保存]します。
これで、以下が完了しました。
一つの問い合わせフォームでサービスごとに項目を出し分ける動的フォームの作成
問い合わせ対象サービスの担当者に自動でチケットを振り分けてメールで通知する設定
EMconfJP2025の参加レポート
掲題の通りEMConfJP2025に参加してきました。
テーマは「増幅」と「触媒」。
懇親会付きのチケットも当日分のチケットもすぐに売り切れて、当日も大盛況だったと思います。自分が聞いた限りの講演のテーマとしては「事業・経営戦略と技術戦略の接合」に関してが多かった印象です。
聴講した講演について感想などを簡単に書いていきます。
当日は朝から連続して講演を聞いていたため、休憩時間は企業ブースに回るなど、慌ただしく過ごしました。スタンプラリーを埋めて景品のTシャツが欲しかったのですが、アンカンファレンスには参加できなかったことと、会場で知り合った方とプレーリーカードを掲げてチェキを撮ったりができなかったので、途中で断念しました。
広木大地さんによる講演で、内容としては「エンジニアリング組織論への招待」に加えて、EMについてと今の潮流と合わせたものとなっていました。正直、この基調講演だけでもチケット代の価値はあったな〜と思えるくらい良い講演でした。
Engineering Managementの4つのP : People/Product/Project/Platform(以前はテクノロジーだった)にまとめられてとてもわかりやすい整理だと思いました。そして「すべてを一人でできる必要はない、スーパーマンになる必要はない」とのことです。
重要なのは何が必要かを正しく理解し、周囲から「調達」してくる力であり、そのために自分自身がその全てを出来る必要はないが、内容をきちんと「わかっている」必要はある
一番のサビはやはり、『エンジニアリングとは「実現する」ことで、マネジメントとは「なんとかする」ことで、EMの役割は「エンジニアリングマネジメントは価値実現のためになんとかする」ことである』、だったと思います。おそらくみなさんに響いていて、他の登壇者のスライドも当日書き加えられるほどでした。
EMを増やすためにEMを目指すハードルを下げつつ、育成に投資をしながら実際に生み出すことを目指してPotential EM制度という形をとったようです。個人的にとても良い制度だと思ったのですが、組織課題解決のための施策は「状況によって正解が変わる」という点が面白かったです。
ベストプラクティスやn=1の方法論が機能するかはケースバイケースというのは当たり前ではあるのですが、組織の時間軸によっても変化するというのは新鮮な視点でした。
バリューベース戦略というフレームワークを用いて、エンジニアリング組織の戦略を事業・経営戦略に翻訳する、という試みの紹介でした。戦略が会社と組織とでアラインしているというのが重要なのですが、エンジニアリング観点からはそういった定量・定性の観点に起こすことがとても困難であり、どの組織も抱える悩みだと思います。
こういった「翻訳することで橋渡しになる」のもEMの重要な役割であり、そのために多種多様な領域や膨大な役割を持ちあわせる必要があるのだと感じました。
事業価値とエンジニアリングについて「事業軸・技術軸・組織軸」の3軸で切り分け、EMに求められる視点と実践的な内容を踏まえた講演でした。
こちらの講演も、戦略をエンジニア組織に下ろす・組織から上げる、といういかにして「翻訳」をするか、というテーマでした。
財務観点もさることながら、開発生産性のFourKeysとQCDの観測を継続し、改善に繋げている組織はすごいと思いました。また資料の密度が凄まじく、時間がある時にじっくりと見直したいです。
EMを志す人にとって「マクロな課題とミクロな不安」という切り口はわかりやすく整理されていて良いと思いました。EM(マネージャー)とIC(プレイヤー)のどちらの経験もそれぞれ生きる、と頭ではなんとなくわかっていても、それでも尽きない不安とどう向き合うかについての考え方や姿勢を学べました。
組織と事業の規模拡大に伴い、エンジニアリングマネジメントグループを創設し、フィーチャー型組織から職能横断型へと変更して、それぞれのチームにEMを設けるようにした、とのことです。規模も大きく、かなりドラスティックな変化だったようです。
最後の「組織的な課題解決策が次の組織的課題の要因となることを認識すること」というメッセージは、マネジメントや組織経営の難しさを表していると感じました。
個人的に一番刺さった・感動した講演でした。
離職も多くなっていき、収益的にも厳しい事業の中でのVPoEの経験を赤裸々に語っておられました。前職のスタートアップに居た時の似た状況を思い出して、当時はメンバーレベルで力及ばない場面も多く、最終的には辞める決断になりましたが、その時のマネージャーの気持ちを知ったような感覚を得ました。
その時にできる引き出しがいち開発者の領域を出ない・技術者としてもまだまだといった状態で、もっとできることがあったら生き延びれたのだろうか、といような思いを馳せました。
組織のアウトプットの方程式で、メンバーの能力と熱量をいかにして上げつつ、受ける制約や摩擦をいかに減らすか、という観点が面白かったです。「メンバーが向かうべき方向を揃えないと、頑張ったところで役に立たないもの(力を結集して作ったゴミ)ができてしまう」には笑ってしまいましたが、気をつけてないとしばしば起きうる事象だと思います。
またカンファレンスの使い方としては「実は廊下で喋ることが大事」とおっしゃっていて、自分は講演を聞くだけにとどまっていたので、次からは改善したいです。
とても楽しくも学びのあるEMカンファレンスでした。
本当は懇親会にも参加したかったのですが、チケットが一瞬で売り切れてしまったため別の公式懇親会に参加しました。場所は会場近くの「もうやんカレー」で、話に聞いてたものの初めて行ったのですがとても美味しかったです。
EM界隈のコミュニティは活性化している印象ですが、それでもEMの数が各所足りてない様子でした。懇親会にて日々の課題などを話している中で、試してみたい解決策が見つかったり、方向性が決まるといった発見もあったようです。社外のこういった場が大切なことを再確認できました。
当たり前ですが、登壇者の方々はたくさん本を読んでかつ実践もされていて、「単純にすごい」と刺激を受けることができました。また「世界はシステムで動く」というシステム思考に関する書籍への言及・引用が、異なる講演で3回くらいあったので翌日にはポチってしまいました。他にも読んでおくべき本がたくさん見つかり、またしても積読が捗りそうです。引き続きキャッチアップ等を進めていきます。
このたびの素晴らしいイベントを支えてくださった関係者のみなさまに、深く感謝いたします。ありがとうございました!
この記事では、一般的に知られているデータ分析プロセスを簡単に紹介します。
データ分析において、一般的に知られている標準プロセスには以下が存在します。
CRISP-DM(*1)
KDD(*2)
以下、それぞれについて概要を紹介します。
*1 Shearer C., The CRISP-DM model: the new blueprint for data mining, J Data Warehousing (2000); 5:13—22.
*2 Fayyad, Usama; Piatetsky-Shapiro, Gregory und Smyth Padhraic (1996), From Data Mining to Knowledge Discovery in Databases, AI Magazine, American Association for Artificial Intelligence, California, USA, Seite 37–54.
Shearerらが提唱しているCRISP-DM(CRoss Industry Standard Process for Data Mining)では、次の図のようなプロセスにしたがって、データ分析を行います。
1
ビジネス理解
ビジネスにおける課題を明確にし、データ分析プロジェクトの計画を立てます。
2
データ理解
データを取得し、そのデータが分析に使える状態であるか確かめるなどの探索的データ分析を実施して、データの理解を深めます。
3
データ準備
後続のモデリングで要求される形式にデータを整形するなどの、前処理を実施します。
4
モデリング
分析モデル(予測をするためのアルゴリズム)を決め、 前のプロセスで準備したデータをモデルに学習させます。
5
評価
次は、前のプロセスで作成したモデルを使って、実際に分析を行い、このモデルよる予測がビジネスに利用可能であるかを評価します。
6
適用
評価した結果、問題がなければ、そのモデルによる予測をビジネスに適用して、使います。
図にも表現されているように、CRISP-DMでは、必要に応じて前後のプロセスを行き来しながら分析を進めます。
CRISP-DMがビジネスにおけるデータ分析プロジェクト全体を考慮しているのに対し、Fayyadらが提唱しているKDD(Knowledge Discovery in Databases *3)は、よりデータ分析部分にフォーカスしています。
KDDのプロセスは次の図のようになります。
1
データ取得
対象ドメインを理解し、顧客視点から分析の目標を定めた後、必要なデータを取得します。
2
データ選択
取得したデータから、データマイニングに必要なものを選択します。
3
データクレンジング
目的データに対して、外れ値の除去や欠損値への対応などのクレンジングを行います。
4
データ変換
クレンジング済データを、データマイニングに必要な形式に変換します。
5
データマイニング
変換済データに対し、回帰や分類、その他手法などを使ってパターンを抽出する。
6
解釈・評価
データマイニングを行った結果から得られたパターンを解釈し、評価します。
図を見るとわかりますが、KDDにおいても、必要に応じて、前段のプロセスへ戻る可能性があることが明確にプロセスに組み込まれています。
*3 Fayyad, Usama; Piatetsky-Shapiro, Gregory und Smyth Padhraic (1996), From Data Mining to Knowledge Discovery in Databases, AI Magazine, American Association for Artificial Intelligence, California, USA, Seite 37–54.
実際の実務においては、ビジネス理解が必須になるため、どちらかというとCRISP-DMのプロセスが実態に近いですが、データ分析部分のプロセスとして、KDDの考え方も参考にはなるでしょう。
最近、当社の勤怠システムが更改されるとともに、勤怠打刻のWeb APIも公開されました。
すると、エンジニア間で「わたしのかんがえたさいきょうのきんたい」ブームが起き、いろいろな勤怠打刻方法が生み出されました。
今回は、私の作成した Raspberry Pi + PaSoRi + Python の勤怠打刻マシンをご紹介します。
個人的に勤怠打刻に欲しい機能として
タイムカードボックスからタイムカードを取り出して打刻
物理タイムレコーダーと同じような操作感がほしいため
「打刻したら音声で挨拶する」「打刻したらSlackで通知する」
リモートワークしてても物理出退勤してる感を出したいため
があります。
そこで、Raspberry Piと PaSoRi と FeliCa を使って
無印のタイムカードボックスからFeliCaを取り出す
FeliCaをPaSoRiにタッチする
PaSoRiのつながっているRaspberry Piが反応し、Web APIで打刻する
打刻に成功したら、Raspberry Piに接続したスピーカーから音声を出す
Slack API で打刻したことを通知する
出勤
と書かれたタイムカードボックスにFeliCaを入れる
ができるような仕様とします。
ハードウェア
Raspberry Pi 2 Model B (以降、ラズパイと表記)
Raspberry Pi OS, January 28th 2022
PaSoRi RC-S380
100均のスピーカー XYZ-22-A
FeliCa KURURU
ソフトウェア
Python 3.9.2 (Raspberry Pi OS付属)
PasoRiでFeliCaを読むときに使用
勤怠打刻マシンの外観です。
手前の黒いPaSoRiにFeliCaをタッチし、出退勤を打刻します。
今回、Slack botからSlack通知をするために、Slack appを準備します。
Bot tokens
を使うSlack appを作成する
OAuth & PermissionsのScopesは chat:write
勤怠打刻用のラズパイをセットアップします。
SSHを可能にする
IPアドレスを固定化する
mp3ファイルを再生できるよう、 mpg321
をaptでインストールする
/home/pi/projects/dakoku_pi/
ディレクトリを作成する
このディレクトリに打刻用プログラムファイルを入れる
Web APIでの勤怠打刻は、プログラミング言語を問わず利用できるようでした。
そこで、慣れているPythonを使って打刻してみます。
もし今後、勤怠システムの更改があったとしても、今回作成する打刻マシンはなるべく変更箇所を少なくしたいです。
そこで今回は、
共通的な処理を行う親クラス
Web APIで打刻する機能を呼び出す
音声を出す
Slackへ通知する
システムごとの処理を行う子クラス
Web APIで勤怠打刻する
というクラス構成としました。
また、 dakoku_pi
ディレクトリ以下を次のようにしました。
必要なライブラリをインストールします。
なお、Slackのトークンなどの秘匿情報はハードコーディングせず、 .env
ファイルに記載して python-dotenv
で環境変数へロードすることとします。
再掲となりますが、このクラスでは
打刻する
を呼び出す
音声を出す
Slackへ通知する
の機能を持たせます。
そのため、 打刻する
を呼び出すところは
としておき、子クラスに実装を任せます。
その他の共通的な機能は以下とします。
DakokuBase
を継承し、 clock
メソッドを実装します。
社内の勤怠システムに依存するためここでは公開できませんが、 clock
メソッドを実装します。
import階層が深くなるのを避けるため、 __init__.py
にimportを追加します。
今まで作成してきたファイルと nfcpy
を使い、FeliCaを読み込むと打刻できるよう実装します。
なお、開発用にコマンドライン引数も用意しておきます。
打刻した時にスピーカーから音声を出すため、mp3形式のファイルを2つ(出勤・退勤)用意します。
文字から音声を作るサービスで作成したり、自分で録音したりしてください。
作成したら voice
ディレクトリの中に
clock_in.mp3
clock_out.mp3
として保存します。
次に、systemdを使い「ラズパイへPaSoRiを挿入した時に上記スクリプトを実行することで、常時FeliCaのタッチを待ち受けている」状態にします。
udev
を使い、ラズパイのUSBポートへのPaSoRi接続を認識するよう設定します。
まず、 udev
の rules
を作成するため、PaSoRiの idVendor
と idProduct
を確認します。
次に、 /etc/udev/rules.d/90-rc-s380.rules
を以下の内容で作成し、serviceと関連付けます。
なお、serviceに指定した rc-s380.service
は後ほど作成します。
続いて、systemd用のservice として /etc/systemd/system/rc-s380.service
を作成します。
以上で完成です。
新しい勤怠システムがリリースされてからラズパイ勤怠を利用していますが、特に問題は発生していません。
また、打刻し忘れることもなく、安定して運用できています。
弊社からは4名のエンジニアが現地で参加しました。
この記事では、RubyKaigi 2023に参加したエンジニアの中で印象に残ったセッションをピックアップして紹介します。
弊社ではRailsで作っているシステムはRSpecでテストコードを書いています。そのため、「ある箇所で破壊的な修正をしても、他のところでテストが落ちて気づいて救われる」と、テストコードの効果を実感しています。
テストコードのリファクタリングも随時行っているものの、基本的にはテストコードはプロダクトの成長とともに増え続けています。
その結果、最近、CIでのテスト実行時間が増えてきていることに対して課題感が出てきました。
そんな中、このセッションの説明に
I will share some lesser-known parallel testing insights I've gained in reviving the test-queue. And explore possibilities of parallel programming in Ruby.
とあったため、並列テストに関して何か得られそうだと思い参加しました。
セッションで印象に残ったのは、 parallel_tests
と test-queue
の仕組みの違いでした。
parallel_tests
は事前にテストを分割してから各workerで並列実行するため、テスト全体のスループットは一番遅いworkerに引っ張られるとのことでした。
一方、 test-queue
は空いているキューにテストを入れていくため、parallel_tests
の問題点を解決しているとのことでした。
ただ、test-queue
はテストフレームワーク特有の箇所があるため、もしRSpecが実装を変えたら動作しなくなるという説明もありました。
他には、セッションの冒頭にて
10分以内のビルドなど、テストの実行時間を短くするのは大切
RSpecで遅いテストを見つける方法
Database Cleanerのチューニング
についてもお話があり、このあたりの観点でもテストを見直していこうと感じました。
TypeProf の v2 を作っているというお話でした。
v1 はとにかく動作するものを実装する、速度は二の次という前提にしていた
実際に使ってみると本当に欲しかったものは「Rubyの型よりもIDEサポート」ということに気づいた
IDEサポートを強化するには解析速度は重要な要素
解析対象のコードは完全なコードではない(書きかけのコード)
v2 は解析結果を差分更新して速度を出している
対象としているエディタはVS Codeのようですが、Vimでも動きそうな情報を見かけたので試してみようと思います。
個人的 one of the best talks on RubyKaigi 2023 です。
実際に"++"を実装する試みが小さなステップで非常にわかりやすく解説されていました。現状の parse.y が i++
というコードをどのように扱っているのかから始まり、具体的に試してみる→できた→まだ問題が…という流れが parse.y 初心者にとってとっつきやすく、自分も parse.y の中身を見てみたいという気持ちが出てきました。
最後の「"++"があるこの言語は Ruby じゃない Ruby++ だ!」というオチに至るまでのストーリーも面白かったです。
なぜ ReDoS が起きてしまうのか、Ruby の正規表現エンジンである Onigmo の特性や、バックトラックにより分岐が指数関数的に増えて行くことをわかりやすく解説していただき非常に勉強になりました。
また、高速化の手法としてメモ化を導入し計算量が線形時間になることや、 Regexp.linear_time? メソッドで正規表現が線形時間になることのチェックが可能であることも知ることができました。
今後の展望として Regexp.linear_time? が false となるような正規表現の場合に警告を出す RuboCop をスピーカー自身で開発するとのことで、弊社でもリリースされたらすぐに導入したいと考えています。
匿名モジュールを用いて名前空間の衝突を避けようという話でした。
匿名モジュールの名前空間の挙動はこのセッションを聞くまで知らなかったので興味深く聞くことができました。
また、オートロードを匿名の名前空間上で行うために Zeitwerk をフォークして Im という gem を作成した話もありました。リリースインフォに載らないような Ruby の改善をどのように用いて課題を解決したのか、という一連の流れが特に面白かったです。
Public な gem を開発することがなかなかないのでセッションの内容をすぐに活用することは難しいと思いますが、自分も細かいものも含めて Ruby の改善をキャッチアップしてみたいと思わせてくれるようなセッションでした。
弊社のエンジニアたちは東北から関西まで様々な地域で暮らしているため、基本的にフルリモートで勤務しています。
日頃はSlackやGatherなどによるオンラインコミュニケーションを取っている一方、オフラインで直接会話する機会がなかなかありません。
そのため、今回のRubyKaigi 2023は、弊社のエンジニアたちが集まってランチを一緒に食べたり直接会話する良い機会となり、とても楽しく過ごせました。
最後になりましたが、RubyKaigi 2023を運営してくださったみなさま、本当にありがとうございました。
これからは来年沖縄で開催される RubyKaigi 2024を楽しみにして過ごしていきます。
の手順で問い合わせフォームをアクティブ化しておく必要があります
の手順で問い合わせの回答担当者をエージェントとして追加しておく必要があります
次にフィールドタイプを選択します。 各フィールドタイプの詳細についてはを参照してください。 今回の場合、「対象サービス」というフィールドを作成するときにはドロップダウン、それ以外のフィールドを作成するときにはテキストを選択します。
その後の入力方法はの[カスタムチケットフィールドを作成する]>[カスタムチケットフィールドを追加するには]の手順3以降をご覧ください。
本サイトの更新情報は、Twitterので発信しています。ご確認ください。
1.0.4
参考:
複数箇所でSlackへの投稿を行うため、の薄いラッパーを用意しておきます。
を見たところ、そのベンダーIDとデバイスIDの組み合わせが FeliCa S380 [PaSoRi]
で間違いなさそうでした。
5/11(木)~5/13(土)に、長野県松本市のまつもと市民芸術館にてが開催されました。
とはいえ、RSpec3のサポートに加え、以下のプルリクにある通りRSpec4.0(dev)もサポートしていることから、社内のコードを使って test-queue
を試してみるのも良さそうと感じました。
本サイトの更新情報は、Twitterので発信しています。ご確認ください。
ルールを設定する目的は、コンテンツ全体の統一感を出したり、定まったルールの元でコンテンツを書くことで、読者にとって読みやすいコンテンツにすることです。
しかし、コンテンツを書くためのハードルを上げたくもないので、ルールを多く管理したくもありません。ルールの設定は、必要最小限に抑えるようにしましょう。
なお、このページに記載するルールは基本原則ではありますが、記事の特性上ルールから外した書き方をしたい場合は、従う必要はありません。
以下、各ルールごとに説明を記載します。
例えば、次のようなコード例におけるルールです。
この例では、zshのデフォルトのプロンプトを記載していますが、コマンドラインで実行するコードでは、それが分かるように、プロンプトを記載します。
bashを普段使っている人が記事を書くときに、bashのプロンプトを記載するのはOKとします。
また、インフラ系の記事などでは、rootユーザーのプロンプトを明示的に記載したい場合もあると思いますが、その場合は、[root]#
などをプロンプトとして記載します。
上の図のように、gitbook.comでは、見出しのスタイルとして、Heading1、Heading2、Heading3が使えますが、これは必ずしもH1タグ、H2タグ、H3タグになるわけではありません。
目次を適切に管理しやすくするために、記事上の見出しは、Heading1から使い、その内部で区切りをつけたい場合は、Heading2、Heading3と階層を下げてコンテンツを記載してください。
gitbook.comではリンクに下線がつかないことから、リンクに気付きにくいです。
そこで、リンクの文字列の書式は、以下の例のように太字としてください。
例:本サイトの更新情報は、Twitterので発信しています。ご確認ください。
overmind s
した時の様子backend_app
のログのみ表示される