OTOBANK Engineering Blog

オトバンクはコンテンツが大好きなエンジニアを募集しています!

オトバンク社内勉強会 - プログラミング教育知ってる?

このブログにて既に数回記事にしている開発チームメンバーによる 社内LT、 今回は、タイトル『プログラミング教育知ってる?』で 2020年から始まるとされる小学校プログラミング教育の話について紹介します。

発表は、弊社CTO 佐藤によるものです。2019年6月1日に開催された、オープンソースカンファレンス2019 Hokkaidoにて行われたセッション「『プログラミング教育』とは? 今、わたしたち道民ができること」」では、司会をつとめております。*1

『プログラミング教育知ってる?』

勉強会で紹介された内容は、来年度から始まる教育内容の確認や、プログラミング教育について「私たちにできる支援は何だろう?」と各プログラミング教育活動についてです。

スライドと取り上げた内容は、OSC2019 Osakaで発表された「2020年から始まる小学校プログラミング教育の話」の資料を大いに基にしております。

プログラミングを教育するの?

学習指導要領では、「プログラミング教育」と一言も言っておりません。プログラミング教育はプログラム言語を学ぶのが目的ではなく、学びを深めるのが目的です。勉強会では、小学校プログラミング教育の概要を確認しながら、 PCN プログラミング クラブ ネットワーク や CoderDojo Japan の取り組みについてみていきました。

感想としては、論理的思考に寄せていくための手立てとして、プログラミング教育が寄与できればなと思います。

オトバンクは、U-16プログラミングコンテスト釧路大会のスポンサーです

U-16プログラミングコンテスト釧路大会の第7回が、 10月12日(土)に開催されます。 オトバンクから弊社CTO 佐藤が参加予定です。イベントについて詳しくは、U-16プログラミングコンテスト釧路大会のサイトを確認ください。 - https://u16procon-kushiro.qloba.com/

勉強会紹介: Mastodonについて

こんにちは。スマホ向けアプリを担当している けいま と申します。以後お見知りおきを。

オトバンクでは毎週、弊社エンジニア全員が集まる場で社内勉強会をしております。 engineering.otobank.co.jp

今回は同じくアプリエンジニアの s4kr4 さんが発表した "Mastodonについて" を紹介します。


Mastodonについて

f:id:p-side:20190708180859p:plain

2016年公開のミニブログシステム。TweetDeckに似たUIを持っています。 短文投稿をすることが目的のシステムです。 ユーザーをフォローすることによりタイムラインが形成でき、リプライやDMでコミュニケーションを取ることが出来ます。

ちなみに「マストドン」は絶滅した動物(象の祖先)の名前とのこと。

特徴

Mastodonは特定(単一)の企業が運営しているサービスではありません。 ソースコードやデプロイ方法が公開されており、誰でもサーバーを建てることが出来ます。

国内では mstdn.jp や Pixiv運営の pawoo.net、Qiitaが運営しているqiitadon.com などがあります。 かつてドワンゴが運営していた friends.nico もありましたが閉鎖されました...

アカウントを作成する際はどのインスタンスに所属するかを決める必要があります。 それはSlackのworkspeceに似ているかも知れません。

どこでアカウントを作っても、異なるインスタンス間でのコミュニケーションもできるようになっています。

技術要素

nginxがWebサーバーとして採用されている。 HTTPS転送やメディアファイルへのリクエストにキャッシュ要求ヘッダを追加するなど。

サーバー処理としてRailsが採用されており、Mastodonの主要部です。 ストリーミング以外のAPI提供をしています。

StreamingAPIの提供にNode.jsを採用しています。

データベースにはPostgreSQLを採用しています。

フロントエンドはReactで記述されています。

画像やユーザーアイコン、カスタム絵文字の保持にAWS S3かOpenstack Swiftが使えるようになっています。 (なおOpenstack SwiftはS3よりもちょっと安いらしい)

ジョブハンドラにsidekiqが採用されており、非同期処理のキューイングやリトライ処理で使われます。 小規模サーバーでユーザーが増えたときに重くなる原因になることが多いようで、 登壇者のアカウントを作成したサーバーにおいても、ユーザーが増えて重くなったので鯖主がsidekiqの対応をしていたようです。

ActivityPub

ActivityPubは、W3Cで標準化された非中央集権型のソーシャルネットワークプロトコルです。

異なるインスタンス間でどうやってコミュニケーションを取るのか?を定義するもので、Mastodonはその実装の1つです。

概念としては以下の2つがあります。

outbox: 自分が起こしたアクティビティ(トゥート, ブースト, フォロー)が集積されているコレクション inbox: 自分が受け取ったアクティビティが集積されているコレクション

outboxのアクティビティが一定のルールに応じて他のインスタンスユーザーのinboxに送信されます。 リプライを例に説明すると、 リプライを送ったユーザーのリプライがoutboxに蓄積され、リプライのActivityが相手のinboxに届き、 リプライされた側のユーザーがリプライを受けたことを気づくことができる

質疑応答のコーナー

  • sidekiqってRuby製?
    • Yes
  • 運用難しいって聞いたけど?
    • スケールさせるのが難しいらしい

まとめと雑感

Mastodonは私(keima)もTwitterからの避難地としてアカウントをひとつ持っています。 (現在はからあげクン新商品のレビューアカウントですが・・・)

私はMastodonそのものよりもActivityPub実装に関心があり(そのくせ自分で実装する根性はないのですが・・・)、 より広く使われるようになる未来が来ることを願っています。

個人的には既存サービスのインターフェースとしてActivityPub実装がなされるようになるかと期待したのですが、 そのようなムーブメントは起きず、ActivityPubを実装したシステムが産まれるに留まったのは物足りなく思いました(それだけでも凄いことですが)。

現在においてメールアドレスを人格ごとに1つ以上持つ(例えば個人のGmailアドレスとは別に会社のメールアドレスをもつ)ように、 ActivityPubプロトコルで人格ごとに発信をする未来が理想ですが、流石に夢見すぎかなと我ながら思ったりもしました。

というわけで今回はこの辺で。また来週 ノシ

BEAR.Sunday を GAE flex 環境で使う際の tips

このようなことを偉そうにもつぶやいてしまったので、この記事を書く運びとなりました。

改めましてこんばんわ @kalibora です。

弊社では BEAR.Sunday を Google App Engine の Flexible Environment(以下 GAE)上で動かしているのですが、 実はどーにもこーにもずっと解決出来ない問題がありました。

それはGAEのオートスケールでインスタンスが増える際に、下記のような Ray.DI 起因だと思われるエラーがたまに出る。 というものです。

Dummy\Exception(Argument 1 passed to Dummy\Foo::__construct() must be an instance of Dummy\Bar, integer given, called in /app/var/tmp/prod-hal-api-app/Dummy_Foo-.php on line 5)

一部ファイル名やパスを書き換えていますが、 Ray.DI に生成を任せているクラスの引数に、期待通りのクラスではなく integer が与えられた。という内容のエラーです。

なぜこの様な事が起きるのか詳細は理解できていないのですが、

(本番環境では同時に多数のアクセスが来て、最初のアクセスでオンデマンドにコンパイルしている最中に、また次のリクエストが来て・・・という具合でエラーになるのでしょうか?)

プロダクション | BEAR.Sundayデプロイ の項目には下記のような記述があります。

セットアップを行う際にvendor/bin/bear.compileスクリプトを使ってプロジェクトをウオームアップすることができます。

コンパイルスクリプトはDI/AOP用の動的に作成されるファイルやアノテーションなどの静的なキャッシュファイルを全て事前に作成します。

全てのクラスでインジェクションを行うのでランタイムでDIのエラーが出ることもありません。

また.envには一般にAPIキーやパスワードなどのクレデンシャル情報が含まれますが、内容は全てPHPファイルに取り込まれるのでコンパイル後に消去可能です。コンパイルはdeployをより高速で安全にします。

ということで、本番環境ではコンパイルすべきだということが分かりました。

では GAE 環境へのデプロイではどのタイミングでコンパイルすべきでしょうか?

GAE のデプロイ

そもそも GAE のデプロイの流れを理解していないといけないので少し内部の挙動を追いました。

gcloud app deploy コマンドを打つと、内部では下記のような処理が走るようです。

  1. app.yaml の skip_files なども加味しつつ ソースファイルを src.tgz に固めて Google Cloud Storage にアップロード
  2. Google Cloud Build でビルド(docker イメージを生成)
  3. デプロイ(生成された docker イメージを使って新バージョンを立ち上げ)
  4. プロモート(新バージョンにトラフィックを切り替え)

2.3. のタイミングでコンパイルできれば良さそうですが、 3. の際にカスタム処理を入れる方法は私の方では見つけられませんでした。

2. のタイミングでは composer install 処理が走るので、 composer の post-install-cmd でコンパイル処理を追加すれば、 生成される docker イメージに vendor ファイルと共にコンパイルされたファイル(tmp/{context}/...)も追加されるので都合が良さそうです。

ですが、ここでいくつか自分たちが遭遇した罠(というか勝手に自分でハマっただけですが)がありました。

罠1. Cloud Build 時に includes している環境変数は使われない

GAE の app.yaml では includes を使って env_variables を別のファイルに書くことも可能で、共通の環境変数はそこで定義していたのですが、 これは Cloud Build 時に使われませんでした。

これに関しては下記のソースコードを見ると分かります。

https://github.com/GoogleCloudPlatform/php-docker/blob/master/builder/gen-dockerfile/src/Builder/GenFilesCommand.php

ということで、 includes は使わないことにしました。

罠2. Doctrine2 が勝手にコンパイル時にDB接続しに行く罠

さて、これで解決かと思いきや Doctrine2 ユーザーへの罠もありました。

DI のコンパイル時に本番環境のデータベース(Cloud SQL)に接続しに行こうとして、 Cloud Build 環境では接続出来ずにエラーになる。

というものです。

実際にデプロイされたGAE環境ではもちろん本番データベースに接続できるものの、Cloud Build環境では接続できないようなので(普通接続する必要はないので問題ない)どうしたものかと悩みましたが、

結局この原因は Automatic platform version detection という接続先のデータベースを自動で判定する機能が原因だったので、 この機能をやめ、明示的に接続先のプラットフォームを指定するようにしました。

罠3. src の タイムスタンプがコンパイル時と docker イメージ作成時で異なる?

ここまでで、Cloud Build 時に生成される docker イメージにコンパイルされたファイルを含めることができました。

これで最初のデプロイ時でもオートスケールでインスタンスが増えた際でも、コンパイル済みのファイルが最初から用意されていることになります。 (最初のリクエスト時にコンパイルで待たされることはありません!エラーも出ない!)

と思いきや、もう1つ遭遇した罠がありました。

プロダクション | BEAR.Sundayコンテキスト の項目には下記のような注意書きがあります。

重要: プロダクションではディプロイ毎に$appを再生成する必要があります。

$app を再生成するには src/ ディレクトリのタイムスタンプを変更します。 BEAR.Sundayはそれを検知して $apptmp/{context}/di のDI/AOPファイルの再生成を行います。

これは逆に言えば

src/ ディレクトリのタイムスタンプが変わると $apptmp/{context}/di のDI/AOPファイルが再生成される。

ということになります。

せっかく作った tmp/{context}/di のファイルを消して再生成したくはないので、 コンパイル時の src/ のタイムスタンプと docker イメージ内に含まれる src/ のタイムスタンプが同じでないといけません。

ですが私が試したところ、どうも1秒くらいズレるようです。 この原因や根本的に合わせる方法は分かりませんでした。

ですので、 Bootstrap クラスを独自のものに差し替え、そのクラスでは src/ のタイムスタンプを直接見るのではなく、 タイムスタンプを保存したファイルがあれば、それを見るようにしました。 (コンパイル時に src/ のタイムスタンプをファイルに出力しておく)

これで GAE デプロイ後も /tmp/{context} ディレクトリが削除されることなく、使い続けることができるようになりました。

まとめ

  1. composer の post-install-cmdvendor/bin/bear.compile を呼ぶようにする
  2. Cloud Build 環境と実際にデプロイして動作する環境の差異に注意する
    • Cloud Build 時は includes した環境変数が使えない
    • データベースに接続できる/できない
  3. デプロイ後にDI関連のファイルが再生成されないようにする
    • 自分は src/ のタイムスタンプを固定したが、単純に tmp/{context}/.do_not_clear ファイルを配置するだけでよかったかもしれない(未検証)

はい。そんな感じで現場からは以上です。