シンクロ・フード エンジニアブログ

飲食店ドットコムを運営する、株式会社シンクロ・フードの技術ブログです

AWS 認定ソリューションアーキテクト アソシエイト(新版)を取得しました

基盤チームの安藤です。
今回はAWS 認定ソリューションアーキテクト アソシエイトを取得した話を紹介しようと思います。
インフラ歴は3年ほどで最近までは開発と半々でしたが、積極的にインフラに関わっていくことになったためこの資格に挑戦しました。

AWS 認定ソリューションアーキテクト アソシエイト(新版)とは?

今回受験した新版は2018年2月から受験可能になった新しいバージョンで、内容が刷新されただけでなく問題数や試験時間なども大きく変更されています。
取得した当時は試験は英語でのみ受験可能でしたが、現在は日本語での受験も可能になっているようです。
詳しくは公式ページをご参照ください。
https://aws.amazon.com/jp/certification/certified-solutions-architect-associate/

勉強方法について

新版については情報が少なかったり、対応した教材がまだ少なかったため情報収集に苦労しました。
また、今回は英語での受験ということもあり主に英語の教材を選びました。
準備期間は2週間ほどでした。

Udemy - AWS Certified Solutions Architect Associate 2018 コース

https://www.udemy.com/aws-certified-solutions-architect-associate/
確認用のミニクイズや模擬試験が付いたUdemyのビデオ講座です。
内容はソリューションアーキテクト アソシエイトの試験に特化してかなりまとまった内容でした。
全てを通すとかなり長時間になるため、業務で利用したことがあるサービスは倍速で流し見して、触ったことがないサービスはなるべく手を動かすという形で取り組みました。
一部内容が古く現在では正しくない内容もありましたが、Q&Aで講師や他のユーザーからフォローを受けることができました。

Q&Aには実際に試験を受験した人たちのアドバイスやフィードバックも投稿されていて非常に役立ちました。
新版の情報が少ないなか、どういった範囲の問題がよく出題されるかなど傾向がつかめます。

A Cloud GuruのAWS Certified Solutions Architect - Associateというコースと内容は同じようですが、セール価格で購入できればかなりお得だと思います。
アカウントを連携することでA Cloud Guruのコースに付属している模擬試験も受験できました。

Wizlabs Practice Tests

https://www.whizlabs.com/aws-solutions-architect-associate/practice-tests/
UdemyのコースのQ&Aでよくおすすめされていた練習問題集です。
旧版、新版それぞれに対応した問題が用意されていて、問題数も豊富で解説が丁寧でした。

公式の模擬試験

有料ですが、本番の試験と同様の形式で受験できます。
形式に慣れる意味でもおすすめです。
試験結果は全体の正答率と5つのセクションごとの正答率しかわからず、問題ごとの正否まではわかりませんでした。
また、試験問題を振り返って確認することもできないため要注意です。

試験の流れ

申し込み

AWS training and certificationにログイン後、PSIから試験の予約を行います。
予約の際には受験料16,200円も支払います。今回は会社に全額負担してもらいました。
試験日については、都内であれば平日はほぼ毎日受験可能ですが、休日に受験できる会場はごく一部に限られているようです。

試験当日

会場で受付後、身分証明書(免許証とクレジットカードなど2枚以上の組み合わせ)と紙、ペンだけを持ってブースに入ります。
ブースには身分証明書のスキャナー、Webカメラが設置されたPCが用意されていました。
画面上のガイドと日本語のチャットの指示に従って身分証明書をスキャン後、試験が開始します。
試験中の質問などはチャットを通じて行えます。
試験完了後にはアンケートがあり、最後に試験の合否が画面に表示されて終了になります。

試験終了後

試験後数日以内にAWS training and certificationでスコア等が確認できるようになります。
試験のスコアは100から1000で評価され、合格ラインは720です。
また、セクションごとにMeets Competencies/Needs Improvementの判定があります。

試験の結果

899/1000というスコアで無事合格できました。
セクションごとで見ても全セクションでMeets Competenciesとなり偏りなくスコアを取れました。

感想

  • VPC,サブネット周りの基礎を改めて学べた
  • サービスの詳細な仕様を学べた
    • S3のデータ整合性モデルなど
  • 今まで見落としていた機能、設定を知ることができた
  • 様々なパターンのベストプラクティスを学べた
  • AWSの更新情報に敏感になった

など色々と得るものが多く、とても有用なものになりました。

その他にも

  • AWSサミットで認定者ラウンジに入場できる
  • AWS認定ストアで認定グッズが購入できる

といったおまけもあるようなので、機会があれば試してみようと思います。

最後に

以上、認定ソリューションアーキテクト アソシエイト取得までの紹介でした。
新版についてはまだまだ情報も少ないので、これから挑戦する方の参考になればと思います。


シンクロ・フードではエンジニアを募集しています。 資格の取得なども柔軟に補助がでますので、ご興味のある方は以下よりご連絡ください!

依存しあう複数の Git リポジトリを git subtree で monorepo 化する

基盤チームの川井 (@fohte) です。

今回は、monorepo と呼ばれる複数のリポジトリを単一のリポジトリで管理する運用方法の紹介と、実際にその運用に切り替えた話をします。

弊社は GitHub Enterprise を使っており、Git Flow を独自に拡張した master, develop, feature ブランチの 3 種類からなる運用フローを採用しています。 今回はその環境を前提としてお話します。

monorepo とは?

monorepo は、複数リポジトリを単一のリポジトリで管理するという Git リポジトリの運用方法の一つです。

monorepo のメリットとしては以下の点が挙げられます。

  • 密結合なリポジトリの運用が楽になる
  • 複数リポジトリに横断した修正が簡単にできるようになる

例えば、あるリポジトリ A の変更に従って別のリポジトリ B も変更するとき、リポジトリが複数分かれているとそれぞれのリポジトリに commit が分散することになります。 これは単純に面倒ですし、何より commit 可読性も悪くなります。

また、このようなときはリポジトリごとに Pull Request を立てる必要がありますが、monorepo では 1 つにまとまるため、Pull Request の可読性も向上します。

反対に monorepo のデメリットとしては以下の点が挙げられます。

  • リポジトリが巨大になる

複数に分散しているリポジトリを 1 つにまとめてしまうので、結果として Git リポジトリは統合したリポジトリの分だけ大きくなります。

あまり見慣れない運用方法ではありますが、RailsBabelReact といった巨大な OSS プロジェクトでも採用されています。

monorepo へシームレスに移行する

monorepo に移行するとなると、各リポジトリのブランチも monorepo 側に移さなければいけません。 そこで、開発者の手を止めることなく monorepo に移行する手段として、以下の方法を検討しました。

  • 各リポジトリの feature ブランチを含めたブランチ全てを monorepo に移し、移行直後から monorepo 側の feature ブランチで開発する f:id:synchro-food:20180329222655p:plain
  • master ブランチだけを monorepo に移し、各リポジトリで開発中の feature ブランチは merge 後に移行する f:id:synchro-food:20180329222652p:plain

前者の場合、各開発者の手元の変更は monorepo 側に取り込まれないという欠点があるため、今回は後者の手段を選択しました。 また、弊社では master ブランチとは別に develop ブランチもありますが、develop ブランチも後述の方法で移行すると master と develop が全行差分になってしまう問題があったため、master ブランチ移行後に新規に master から develop ブランチを切り直しました。

移行手順

今回は git subtree コマンドを用いて移行しています。

git subtree 自体は git に内蔵されているコマンドではないため、環境によってはコマンドが無く、その場合は手動でインストールする必要があります。

git clone https://github.com/git/git.git
cd git/contrib/subtree
make
cp git-subtree /usr/local/bin # パスが通っているディレクトリにコピーする

各リポジトリの master ブランチを monorepo に移行する

先に monorepo 化する各リポジトリの remote を登録し、fetch しておきます。

git remote add <name> <repo_url>
git fetch -a <name>
  • <name>: remote の名前です。今回は monorepo のサブディレクトリ名とします。
  • <repo_url>: 各リポジトリの URL です。 (例: git@github.com:rails/rails.git, https://github.com/rails/rails.git)

また、空 commit を作成しておきます。

git checkout --orphan master
git commit --allow-empty -m 'Root commit for master branch'

次に、git subtree add コマンドで各リポジトリを monorepo のサブディレクトリとして追加します。

git subtree add --prefix=apps/<name> <name>/master

--prefix に指定したディレクトリにリポジトリが取り込まれます。 今回は apps ディレクトリ下にフラットにリポジトリを置いています。

最後に、念のため git diff-tree で差分をチェックし、問題がなければ master ブランチの移行は完了です。

git diff-tree -r --stat <name>/master master:apps/<name>

feature ブランチの変更内容を monorepo に取り込む

今回は、各リポジトリで開発中の feature ブランチはそのリポジトリに merge してしまい、その後 monorepo 側に取り込みます。

その後、git subtree merge コマンドでサブディレクトリに merge し、monorepo 側に各リポジトリでの変更を取り込みます。

git checkout master
git subtree merge --prefix=apps/<name> <name>/<branch>

master ブランチ移行時同様に差分をチェックし、問題がなければ feature ブランチの変更の取り込みは完了です。

git diff-tree -r --stat <name>/master master:apps/<name>

所感

移行前に懸念していたこととして、上述のデメリットとしても挙げた Git リポジトリが重くなるかもしれないという点がありました。 今回はアクティブな Rails アプリケーションのみ (5, 6 リポジトリ) を monorepo 化し、commit の総数は 6,000 ほどとなりましたが、現時点では git コマンドに重さを感じることはありません。 ファイルが増えている分容量自体は大きくなっているため git clone 時などに重さを感じますが、これは複数リポジトリを git clone しているようなものであるため許容しています。

また、移行の難易度やコストが高いのではという懸念もありましたが、想像以上に容易に移行することができ、全く問題はありませんでした。

monorepo 化して 1 ヶ月ほど運用されていますが、特に大きな問題もなく、1 リポジトリで完結することの恩恵を大いに受けて生産性が向上していると感じます。

まとめ

今回は monorepo という Git リポジトリの運用方法について紹介しました。 git subtree コマンドを用いると手軽に monorepo 化できるので、密結合なリポジトリがあるときの解決策の 1 つとしてぜひ検討してみてください!


シンクロ・フードでは開発基盤設計に興味のあるエンジニアも募集しています。ご興味のある方は以下よりご連絡ください!

Flow で静的型付けしながらフロントエンド開発した話

基盤チームの川井 (@fohte) です。

今回は飲食店リサーチというサービスのフロントエンドを Flow で型付けしながら React で開発して得た知見の話をします。

Flow とは?

Flow は Facebook 社が開発した、JavaScript の構文を拡張して静的型解析機能を提供するツールです。
例えば以下のような Flow を使ったコードは、Flow の型チェックによって事前にバグを検出することができます。

/* @flow */
function square(n: number): number {
  return n * n
}

square('2') // エラー

嬉しいですね。雑にコードを書いていても Flow が型の不一致を警告してくれるため、書いたコードに対して安心感が生まれます。さらに型解析により定義元ジャンプや補完も効くため、開発効率は大きく向上することでしょう。

Flow はあくまで静的型解析機能のみを提供するため、TypeScript などとは異なりそれ自身でトランスパイルは行なえません。実用するためには拡張された構文だけを取り除く以下のようなツールを用いて通常の JavaScript に変換する必要があります。

トランスパイルがほぼ必須となることから気軽さは少ないですが、既に JavaScript のビルド環境が整っている場合には導入しやすいです。

なぜ Flow を使うのか

今回開発した飲食店リサーチというサービスではフォームが動的に生成され、また入力内容によってフォームが動的に変化するという要件がありました。これを実現するためにはそれなりのコード量を必要とする複雑な処理を書く必要があり、そこで静的型解析ができればコード量が増えても安心できるプログラムが書きやすいのではと考え、Flow の導入に踏み切りました。

なぜ TypeScript ではないのか

同じく静的型付き言語の AltJS である TypeScript は、たびたび Flow と比較して語られます。静的型解析が欲しいという要求には TypeScript は十分に満たしているため TypeScript でも問題はありませんでしたが、既に webpack + Babel でのビルド環境が整っていたため、それに乗っかる形で今回は Flow を選択しました。

TypeScript と Flow で機能面での差はほとんどありませんが、TypeScript を選択するメリットとして、コミュニティが Flow よりも大きく (Flow も大きいですが) 歴史も長いためドキュメントが豊富です。 Flow は公式のドキュメントにない情報も少なくなく、また問題が発生したときもリポジトリの issues を見に行くしかないことが多い印象があります。

Flow は着実にバージョンアップを重ねており、このような問題も次第に解消されることを期待しています。

Flow を導入した所感

今回の開発は長期的にかつ断続的に続いたため過去のコードを思い出す手間がありましたが、型定義がドキュメント代わりになっていてその手間はほとんど省けました。静的に型チェックができることで、自分がコードを忘れていても型が間違っていれば即座にエラーになりますし、非常に体験が良かったです。

一方でデメリットとして、複雑な型を表現すると Flow 特有の知識が必要になり、ドキュメントが少ないことも相まって学習難度が上がってしまいます。難しい代表例としては、ある型から別の型を生成する Utility Types が挙げられます。例えばある object 型の key を全て read-only にしたいとき、$ReadOnly を用いればそれを表現できます。

type Props = {
  x: string,
  y: number,
  z: boolean,
}

type ReadOnlyProps = $ReadOnly<Props>

function render(props: ReadOnlyProps) {
  console.log(props.x) // OK
  props.x = 'foobar' // Error

  // ...
}

このような Utility Types は柔軟に型を表現できますが、Flow に慣れていないと難読な型表現になってしまいます。 Utility Types を駆使しないと表現できない型もあり、全てを厳密に型付けしようとすると消耗してしまいます。ときには型検査を捨てる any type を使い、型定義を妥協することが必要になることもあります。 *1

まとめ

今回は JavaScript に静的型解析機能を提供する Flow を紹介しました。型定義に苦戦することはあるものの、静的型解析で受けられる恩恵は非常に大きいです。

ぜひ快適な静的型解析生活を!


シンクロ・フードではフロントエンド開発に興味のあるエンジニアも募集しています。ご興味のある方は以下よりご連絡ください!

*1:mixed type という逃げもあります。

Irisという超絶キュートなキーボードを作成した話

こんにちは、シンクロ・フードの大久保です。 今回はあまり社内の業務と関係がないのですが、Irisというキーボードを作成したお話しをしようかと思います。

f:id:synchro-food:20180213200721j:plain
これがIrisです

TL;DR

  • IrisというキーボードはコンパクトなErgoDoxで、作って良かった!
  • 組み立て式のキーボード制作は思っているよりも簡単
  • はんだ付けへの抵抗感を無くすのにも良さそう

はじめに

僕はErgoDoxという、左右分割型で親指キーがあるキーボードを使っていて、概ね満足はしていたのですが、ErgoDoxは大きく、使わないキーが沢山あるので、もっとスリムにしたいなあ…と思っていました。 いくつか乗り換え先キーボードの選択肢もあったのですが、色々調べた結果、Irisというキーボードを自作しようという結論に達しました。

f:id:synchro-food:20180213200853j:plain
これがErgoDox。親指キーが特徴です。ErgoDoxEZを使っている人は結構多いのではないでしょうか。

Irisとは

Irisとは、RealForceHHKBのような完成品のキーボードではなく、基礎となる基盤のキットやキースイッチ、キーキャップを自分で購入し、組み立てるタイプのキーボードです。同様のキーボードとしては、PlanckLetsSplit(通称レツプリ)などが有名です。

Irisの特徴としては、以下の点が挙げられます。

  • キー配列がErgoDoxとほぼ同じだが、無駄なキーが削れられている
  • qmk_firmwareが使える
  • コンパクト

ただし、組み立てにははんだ付けなどが必要で、ここは少し心理的な抵抗感があるかと思います。僕も電子工作の経験は少ないので、かなり迷ったのですが、同僚のエンジニアも一緒に作る、ということで、2人で作成していくことにしました。 結論から言うと、思っていたよりも簡単に作ることができました。ただし、Irisの場合は、レツプリのように作成ログの日本語情報があまりWeb上にないので(と思っていたら、2018年になって記事増えてますね…)、以下に僕が作った流れを記載しておきます。

部品集め

まずは組み立てるために必要な部品を購入します。 必要なものは大体4つ。基盤関連、キースイッチ、キーキャップ、工具類です。 2万円くらいあれば、すべて揃うと思います。

基盤関連

Irisはkeebioというサイトで購入可能です。僕はキーボードをLEDで光らせる必要がなかったので、LED関連の機器は購入していません。 必要なものは、PCBの商品詳細にすべて書いてあるのですが、僕が購入したものを以下に記載していきます。

PCB

Iris Keyboard - PCBs for Split Ergonomic Keyboard – Keebio

元となる基盤とダイオード、抵抗、ジャックなどのセットです。

Case

Iris Keyboard - Case/Plates – Keebio

基盤を覆うケースです。midlayerという中間層があるタイプとないタイプがあります。

Pro Micro × 2

Pro Micro - 5V/16MHz - Arduino-compatible ATmega32U4 – Keebio

左右で2つ必要です。

TRRS Cable

TRRS Cable – Keebio

左右のキーボードをつなぐケーブルです。「4極ケーブル」や「TRRS」、という名前で探せば、他のサイトでも購入できます。

MicroUSB Cable

Amazon CAPTCHA

Pro MicroとPCを接続するためのケーブルです。なんでもよいと思います。

キースイッチ

キースイッチは56個必要です。無難にいけば、CherryMX互換のものを買えば良いです。 Cherryの茶軸とか赤軸とか、そういうやつです。 僕はNovelKeyというサイトで購入しました。

https://www.novelkeys.xyz/product/outemu-ice-switches/

僕は上記のOutemuIceSwitchのLight Purple軸を使いました。

市販のキーボードではあまり使えない軸を選べるのは自作キーボードの醍醐味です。ですが、注文してから届くまで時間がかかるので、悩みすぎないようにしましょう。僕は注文してから3週間くらいかかりました…。

キーキャップ

キーキャップも56個必要です。キースイッチをCherryMX互換のものを購入した場合は、CherryMX互換のキーキャップを買えば良いです。

僕はpimpmykeyboardというサイトで購入しました。

Pimpmykeyboard.com

Irisのキーキャップは、すべてが同じ大きさなので、keycapsの1xkeyを56個揃えればよいです。 keysetsで買うと安いですが、ちょうどよい組み合わせがないので、多少は多めに購入する必要があります。 DSAとかSAとかはProfileという、形状の種類なのですが、よくわからない場合は、真っ平らなDSAを買うのが無難だと思います。キーキャップは後でいくらでも変えることができるので、気に入らなければ変更すれば良いと思います。

工具類

これは人それぞれですが、僕が使ったものを書いていきます。

はんだごて

白光 ダイヤル式温度制御はんだこて FX600
https://www.amazon.co.jp/dp/B006MQD7M4/ref=cm_sw_r_cp_ep_dp_4NpDAb0CFJP13

温度調整できるので、これで良いと思います。 他にもはんだごてを置く台なども必要なので、セットみたいなものを買うのが無難かもしれません。

はんだ

これは覚えていないのですが、適当なものを買えば良いと思います…。

練習キット

http://amzn.asia/aRcSdG0

はんだ付けが中学生以来…という方は買って練習すると精神衛生上、良いと思います。僕はRaspberryPiを触っていたときに買ったものが残っていたので、これで軽く練習しました。 実際は、練習せずに一発勝負でも問題はないと思います。

吸い取り線

http://amzn.asia/4KxVzY5

失敗した時用。あると精神衛生上、良いと思います。

他にも言い出せばキリがないのですが、絶縁マットやら固定台とかテスターなど色々あるのですが、必要に応じて購入してください。

あとはひたすら待つ

ここまで完了したら、もう完成したも同然です。そして、注文から到着まで、長いです…。 情報収集しつつ、ワクワクしながら待ちましょう。

組み立て

さて、すべてが揃ったら、いよいよ組み立てです。 一つずつ、作業をご紹介します。

ダイオードをはんだ付けする

f:id:synchro-food:20180124202946j:plain

ひたすらはんだ付けです。スイッチの数だけあるので、全部で56箇所?
もし、はんだ付けを初めてやる、という方であれば、とても良い練習になると思います。

f:id:synchro-food:20180125131741j:plain
両手が付け終わった…。(余計な線はペンチで切る)

Irisだと、ダイオードの向きは黒いほうが下になります。

TRRSジャックとリセットボタン、ProMicro用ピンヘッドをはんだ付けする

f:id:synchro-food:20180212191328j:plain
赤がTRRS,青がProMicro用ピンヘッド,黄がリセットボタン

ひたすらちゅんちゅんやるだけ。抵抗はオプショナルなので、つける必要はないです(僕はつけてしまいましたが…)。

キースイッチをはんだ付けする

f:id:synchro-food:20180126124146j:plain
思いっきり会社のデスクで作業してます(お昼休みに作業してました…)

f:id:synchro-food:20180126130010j:plain

ここもひたすらはんだ付けするだけ。

ProMicroをはんだ付け

ピンヘッドに入れて、はんだ付けしたあと、不要なピンを切っていきます。できる限り薄くしたほうがケースと干渉しなくなります。

f:id:synchro-food:20180126194126j:plain
はんだ付けして、ピンを切った状態。左右でProMicroの向きが違います

これではんだ付け作業は終了。あとはケースの蓋を閉じるだけ。

f:id:synchro-food:20180126222309j:plain

QMKfirmwareをビルドする

ProMicroにfirmwareを書き込みます。 自作キーボードの場合、qmk_firmwareというオープンソースのfirmwareを使うのが一般的で、Irisもそちらを用います。 具体的な作業は、PCでソースをビルドし、コマンドを使ってProMicroに書き込む、という作業を行います。

qmk_firmwareをforkする

以下リポジトリをforkします。
https://github.com/qmk/qmk_firmware

ビルド環境を整える

下記リンクに従って、各種OS毎にビルドする環境を整えてください。 https://docs.qmk.fm/getting_started_build_tools.html

僕はMacでした。Macユーザーの場合はhomebrewでバンバン入れていくだけです。この準備、25分くらいかかります。 なぜかEl Captanだと失敗するときがあったのですが、何度か実行すると、うまくいきました(適当ですいません)。Siera環境だと一発で成功しました。

ビルド実行する

Iris向けのキーマップはすでに準備されているので、まずはREADMEに従ってデフォルトを書き込むのが良いと思います。 https://github.com/qmk/qmk_firmware/tree/master/keyboards/iris

ProMicroにmicroUSBケーブルを接続して、以下コマンドを実行すると、ビルドと書き込みが始まります。

make iris/rev2:default:avrdude

最初だけは、両方のProMicroに書き込む必要があります。 ですので、以下の手順で実行していけば良いです。

  1. 左手のProMicroに書き込み(左手のみ、反応する状態になる)
  2. 右手のProMicroに書き込み(両手が左手のキーマップになる)
  3. 左手のProMicroに書き込み(左手、右手が正しいキーマップになる)

デフォルトのキーマップは、左手側のProMicroにmicroUSBを接続することを前提としているので、上記流れになります。 なお、上記対応をやったあとは、左手だけに書き込むことで、両手のキーマップが反映することになります。 ※このあたりの手順を知らずに対応していたので、両手が左手になったとき、めちゃくちゃ焦りました

完成

f:id:synchro-food:20180127004006j:plain
会社では終わらず、自宅に持ち帰って作業。動いたときはめちゃくちゃ嬉しかったです。

これで完成!

あとはキーマップを自分向けにカスタマイズしていくだけです! 基本的にはdefaultのキーマップをコピーして別名にし、それを変更して、そちらをビルドして書き込む感じです。 例えば、mykeymap、というファイル名でキーマップを作った場合は、以下のコマンドを実行する感じになります。

make iris/rev2:mykeymap:avrdude

尚、ErgoDoxのキーマッピングに慣れている方から見ると、KC_が省略されているキーマッピングになっていて、少し??となると思いますが、sourceを読めば理解できると思います。 一応、僕のキーマップもおいておきます。 https://github.com/shunohkubo/qmk_firmware/blob/develop/keyboards/iris/keymaps/chibikubo/keymap.c

キーマップをあれこれ考えるのも、とても楽しく、沼が待ってます。

参考にしたサイト

非常に詳細なビルドログ
https://imgur.com/a/iQH2W#k3cwV69

作成作業の動画。めっちゃ参考にしました。ProMicroの向きが、ちょっと不思議です。
Iris Split Ergonomic Keyboard Build Log - YouTube

2017の自作キーボードアドベントカレンダーはモチベーションを高める意味でも助けれられました!
https://adventar.org/calendars/2114

終わりに

非常に簡単に作成できますので、興味があれば是非作ってみることをオススメします。
尚、Irisは2018年3月時点で品切れ中…。4月には再度生産してくれるそうなので、手に入ると思います。
他の自作キーボードも、基本的にはIrisと同じ流れで作れると思うので、そういったものを作るのも良いと思います。

シンクロ・フードでは、自作キーボードに興味のあるエンジニアが数名在籍しているので、興味のある方は是非ご応募ください!キーボード談義に花を咲かせましょう。(業務でキーボードを自作することはありませんのでご注意ください…)

www.synchro-food.co.jp

AWS Lambda+API Gateway + S3で格安リアルタイム画像リサイズAPIを作成する

こんにちは、シンクロ・フードの大久保です。

今回は実際に弊社で運用しているAWS Lambdaを使ったリアルタイム画像変換APIについてご紹介したいと思います。 リアルタイム画像変換APIについては、あまり詳しく説明しませんが、画像のサイズ変換等をURLパラメータで指定してリアルタイムに変換することです。 弊社の場合はリサイズしか行わないため、画像変換APIというよりは画像リサイズAPI、というほうが適切かもしれません。

リアルタイム画像変換の方法について

弊社の方法をご紹介する前に、リアルタイム画像変換の一般的な実現手法を挙げてみます。

  1. Nginx, ApacheなどのWebサーバのプラグインを用いる
  2. CDNとして提供されている機能を用いる

1は、クックパッド社のmod_tofuが有名で、色々企業で実現されている手法です。サーバを用意しなければならない点や、冗長化を考えると弊社ではコスト的に取り組めませんでした。 2は、Akamaiやfastlyなど、高機能なCDNサービスを使う方法です。このあたりのサービスは以前は高額なものが多かったのですが、fastlyなどはとてもお得だと思います。

APIドキュメント
https://docs.fastly.com/api/imageopto/

料金
https://www.fastly.com/pricing/

こんなブログポストを書いておいてなんですが、リアルタイム画像変換は自作するよりも、fastlyの利用を検討することをオススメします。弊社も、このリアルタイム変換のシステムを作る前にfastlyで実現できることを知っていたら、fastlyを利用していたのではないかと思います。

ということで、以下はそれでもLambdaを使って自分たちで構築したい、という方向けの記事です。

設計

ベースとなる設計は、以下のAWSブログの記事をベースとしています。

https://aws.amazon.com/blogs/compute/resize-images-on-the-fly-with-amazon-s3-aws-lambda-and-amazon-api-gateway/

弊社はこの設計に、さらにCloudFrontをかませており、以下のようなリクエストの流れとなります。元記事よりリクエストの流れを丁寧に説明していきます。

f:id:synchro-food:20180114145809p:plain

前提として、S3のバケットは2つ用意する必要があります。1つはリサイズ元画像を格納するOriginalBucket、もう1つはリサイズ後の画像を格納するResizedBucketです。

  1. ユーザーがCloudFrontにリクエストを送ります。初回リクエストはCloudFront上にキャッシュが無いため、CloudFrontはオリジンとして設定しているS3のResizedBucketにリクエストを流します。
  2. CloudFrontから流れてきたリクエストを受け、S3はBucket内を探しますが、Resizeされた画像が存在しません
  3. 画像が存在しない場合、内部用の画像変換APIへ307レスポンスが投げられます
  4. CloudFrontはS3からの307のレスポンスをそのままユーザーへ戻します
  5. ユーザーは307レスポンスを受け、次は内部用画像変換APIのURLを待ち受ける、APIGatewayにリクエストを投げます
  6. API GatewayよりLambdaの画像変換関数がキックされます
  7. LambdaがS3のOriginalBucketより、画像リサイズ元となる画像を取得します
  8. 7で取得した画像をパラメータに従ってリサイズし、ResizedBucketに保存します
  9. 8で保存した画像へのURLへの301レスポンスをLambdaにて返します
  10. 301レスポンスをそのままユーザーに返します
  11. ユーザーは301レスポンスに従い、もう一度リサイズ画像のリクエストを送ります
  12. CloudFrontからResizedBucketにリクエストが流れ、変換後画像をレスポンスとしてキャッシュしつつ、結果をキャッシュする

大きな流れは以上です。最初に画像を閲覧したユーザーは3回リクエストが飛ぶことになりますが、2回目以降はCloudFrontがキャッシュしているため、高速に戻すことができます。 リクエストが3回も飛ぶ動きなどが一見気持ち悪い手法ですが、この方法で半年以上運用し、特に大きな問題は起きていません。

以降、少し細かい設定方法を説明していきます。

Lambda関数を用意する

流れの図にある、7(S3からリサイズ元画像を取得する)と8(画像をリサイズしResizedBucketに入れる)、という2つの処理を行うLambda関数を作ります。 コードは、元記事も紹介している、以下のコードを改造していくのが良いと思います。

https://github.com/awslabs/serverless-image-resizing

弊社はこのコードを元に、以下の修正を加えて運用しています。

  • 不正なリサイズリクエストを除外するためのハッシュチェック
  • 細かいバリデーション、ログ埋め込みなど
  • serverlessフレームワーク化(まったく必須ではありません)

ハッシュチェックについては、誰かれかまわずリサイズできると困るため、確実に自分たちのサービスからのリクエストであることをチェックするために、アプリケーション側で作成したハッシュ値をLambda側でチェックしています。

API Gatewayの設定

こちらは特に特殊なことをやっているわけではないのですが、API Gatewayは分かりにくいため、記載しておきます。

リソースの作成

まず、API Gatewayにある「APIを作成」というボタンから新しいAPIを作成したあと、「アクション」→「リソースの作成」を選択します。

f:id:synchro-food:20180114161809p:plain

その後、上記画面のような入力画面が表示されるのですが、ここで「プロキシリソースとして設定」にチェックを入れて、リソースを作成してください。進むと以下のような画面が出ます。

f:id:synchro-food:20180118155859p:plain

こちらでは統合タイプを「Lambda関数プロキシ」、Lambdaリージョンは、後述するLambda関数のリージョン、Lambda関数は、作成したLambda関数を指定してください。

デプロイ

「アクション」→「APIのデプロイ」を選択し、ステージを選択します。 最初はステージがないため、「新しいステージ」を選択し、ステージ名を入力してください。 とりあえず、prod、という名称にすることが多いように思います。

デプロイが完了すると、APIGatewayのURLが生成されます。

https://xxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/prod

これでAPIGatewayの準備は完了です。

S3の設定

上記設計図におけるResizedBucketは、StaticWebHostingを有効にしておく必要があります。それ自体は簡単なのですが、同時にリダイレクトルールを以下のように設定しておく必要があります。

<RoutingRules>
  <RoutingRule>
    <Condition>
      <HttpErrorCodeReturnedEquals>404</HttpErrorCodeReturnedEquals>
    </Condition>
    <Redirect>
      <Protocol>https</Protocol>
      <HostName>${API Gatewayのホスト名}</HostName>
      <ReplaceKeyPrefixWith>${API Gatewayのステージ名}/resize?key=</ReplaceKeyPrefixWith>
      <HttpRedirectCode>307</HttpRedirectCode>
    </Redirect>
  </RoutingRule>
</RoutingRules>

もう一つ、Lambdaが作成した画像ファイルを都度閲覧可能にする必要があるため、バケットポリシーの設定も必要です。この設定がないと、Lambdaが作成したリサイズ後画像がPrivate権限となっていて、画像参照ができません。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "PublicReadGetObject",
            "Effect": "Allow",
            "Principal": {
                "AWS": "*"
            },
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::${S3のバケット名}/*"
        }
    ]
}

CloudFrontの設定

こちらは通常通り作成し、オリジンにはResizedBucketを指定します。

キャッシュ問題について

ただし、このままですと、CloudFrontは1回目の307リクエストの結果をキャッシュしてしまい、変換後の画像を表示してくれません。

この問題は、CloudFrontがキャッシュするかどうかは、S3のオブジェクトが持つCacheControlヘッダを使うようにすれば、解決します。

具体的な操作は以下の通りです。

  • CloudFrontの「Behaviors」→「Edit」→「Object Caching」を、「Use Origin Cache Headers」から「Customize」にする。
  • Lambda側のコードで、リサイズ後画像をS3にPutする際に、CacheControlヘッダを追加する。
      .then(buffer => S3.putObject({
          Body: buffer,
          Bucket: BUCKET,
          ContentType: 'image/png',
          CacheControl: 'max-age=2592000, s-maxage=259200',
          Key: key,
        }).promise()
      )

これでCloudFrontが適切にCacheしてくれるようになります。

基本的な設定の流れは以上です。 この設定を行うことで、以下のようなURLを渡すと、そのサイズにリサイズされた画像が戻ってくるようになります。 呼び出しイメージは以下のような雰囲気です。

https://xxxxxxxxxxx.cloudfront.net/hash/300x100/xxxx.jpg

実際に運用した感想

実際に上記のような設計でリアルタイム画像変換APIを作成し、半年ほど運用をしましたので、所感を列挙します。

  • 安定しています。CloudFront,S3はもちろんですが、lambdaでのトラブルは一切ありません。
  • 画像以外のリクエストが多い。faviconや画像以外のファイルリクエストが沢山きます。リリース後、余計なリクエストを除外する仕組みを入れました。
  • エラー検知は、とりあえずCloudWatchにて、Lambdaの実行エラーをアラームにしています。変換に失敗したときにメールが飛んできますが、エラー内容がメールに記載されていないので、少し面倒です。
  • lambdaのメモリ設定やtimeout設定などは、最初大きめに取り、ログを見ながら最適なメモリサイズやtimeoutに落としていく、という方法が良さそうです。
  • (これはリクエスト数次第ですが)費用は安いです。一度変換した画像は二度と変換が実行されないため、lambdaの呼び出し回数も少ないです。

まとめ

特に手法自体は新規性がないと思いますが、実際にプロダクション環境で運用している事を公開することで、誰かの参考になるかなと思い、ご紹介しました。 リアルタイム変換を今から導入しようと思うのであれば、まずはFastlyを第一に検討すべきかと思いますが、なんらかの問題があった場合などにご検討ください。

シンクロ・フードでは常にエンジニアを募集しています。 ご興味のある方は以下よりエントリーしてみてください!応募ではなく、話を聞いてみたい的なものでも結構ですので、お気軽にお問い合わせください。

Filelint を作って社内プログラムのコーディングスタイルを矯正した話

はじめまして。今年新卒で入社した基盤チームの川井 (@fohte) です。

最近までは、新卒企画研修として開発した wenu という飲食店向け Web サービスの開発基盤を整えたり、フロントエンド (React) のロジック部分を担当していました。

社内の既存プログラムの問題点

新卒研修終了後に配属され、既存プロジェクトの開発に携わったのですが、古くからあるコードが多く残されており、またコーディングスタイルも統一されていませんでした。
具体的には、以下のような問題がありました。

  • 行末にスペースやタブが残されている
  • ファイルの最後に空行が存在したりしなかったりする
  • インデントがソフトタブだったりハードタブだったりと混在している

これらの問題は構文エラーではなく、コンパイルも正常に通るために放置されていました。
しかし、各開発者が編集した際に無駄にスペースが挿入されたり削除されたりと、意味のない変更が差分として上がってしまう問題がありました。
意味のない差分はレビュワーにとっても負担になりますし、これらを強制することでこのような負担を軽減できないかと考えました。

解決手段

上記の問題は ESLintRuboCop といった言語特化型の lint ツールでは対応していないファイルでも見られるため、単純に様々な lint ツールを導入するといった方法では完全に解決することが困難です。
そのため、今回はこれらの lint ツールを使いつつ、また全てのファイルに対してコーディングスタイルを統一できるような手段について検討しました。

EditorConfig

まず、前述の問題を解決するような、言語を問わずコーディングスタイルを統一するツールとして、EditorConfig というツールが存在します。

EditorConfig は、開発者ごとのエディタや IDE の設定によってコーディングスタイルに差異が生まれてしまう問題を解決しようとしているツールの 1 つです。
.editorconfig という ini 形式のファイルをプロジェクトに配置し、それを各エディタや IDE が参照することで、設定内容に従って自動的にコーディングスタイルが統一されるようになります。

各自が自分の利用しているエディタや IDE に EditorConfig プラグインを入れる必要があるため少し抵抗になりますが、それを上回るメリットが存在すると感じたため、今回は開発チーム全体にプラグインを導入してもらうことにしました。

.editorconfig は以下のように設定し、各プロジェクトのルートディレクトリに配置しました。

root = true

[*]
end_of_line = lf
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true

[*.md]
trim_trailing_whitespace = false

Filelint

EditorConfig を用いることで新規プログラムではコーディングスタイルが統一されますが、既存プログラムに対して適用することはできません。
そこで、EditorConfig のように言語を問わずコーディングスタイルを統一できる Golang 製の lint ツールを作成しました。

github.com

https://user-images.githubusercontent.com/11088009/27952943-16962632-6345-11e7-896f-f6d43aff084b.gif

Filelint では、以下の一般的なコーディングルールを提供しています。

ルール名 内容
indent (実験的なため不安定) ハードタブ (タブ文字) でのインデントあるいはソフトタブ (半角スペース) でのインデントを強制する
linebreak 改行コードを LF または CRLF に強制する
first-newline ファイルの最初に改行を必須にする / しない
final-newline ファイルの末尾に改行を必須にする / しない
no-bom UTF-8 BOM の挿入を禁止する
no-eol-space 各行の末尾が半角スペースあるいはタブ文字で終わることを禁止する

インデントの強制に関しては、Filelint は構文を解析していないため、うまくインデントが解析できないことがあります。 そのため、ESLint や RuboCop といった構文を解析する lint ツールを併用することを推奨しています。

使い方

ESLint 風のコマンドで、特定のディレクトリやファイルに対してコーディングスタイルに関する lint が実行できます。

# 現在のディレクトリ下のテキストファイルに再帰的に lint をかける
filelint
# README.md に lint をかける
filelint README.md
# 現在のディレクトリ下のテキストファイルに再帰的に lint をかけて自動修正する
filelint --fix
# some/dir 下のテキストファイルに再帰的に lint をかけて自動修正する
filelint some/dir --fix

以下のような YAML 形式の設定ファイルを .filelint.yml というファイル名でプロジェクトに配置することで、各 lint ルールの設定の他、特定のファイルを除外したり、特定のファイルには別のルールを適用することができます。

files:
  exclude:
    - '**/*.min.css'
    - '**/*.min.js'
targets:
  - patterns: ['**/*']
    rules:
      indent:
        enforce: false
      linebreak:
        enforce: true
        style: lf
      first-newline:
        enforce: true
        num: 0
      final-newline:
        enforce: true
        num: 1
      no-bom:
        enforce: true
      no-eol-space:
        enforce: true
  - patterns: ['**/*.md']
    rules:
      no-eol-space:
        enforce: false

インストール方法

GitHub Releases で各 OS 向けのバイナリを含んだパッケージを公開しています。 ここからダウンロードして解凍し、 $PATH に通されているディレクトリ下にバイナリファイルを配置してください。

Golang の環境がある場合は以下のコマンドでインストールできます。

go get github.com/synchro-food/filelint

実際の運用

前述の .filelint.yml はあるプロジェクトで利用しているもので、これを基本として各プロジェクトに配置し、ベースブランチにて filelint --fix コマンドで自動修正しました。

また、すでにベースブランチから切られている開発中ブランチに関しても、コンフリクトを避けるために以下の手順で各開発者の手元で修正して頂きました。

# 適用するブランチ (開発中のブランチ) に移動する
git checkout <branch name>

# .filelint.yml が追加されたコミットを現在のブランチに適用する
git cherry-pick $(git log --reverse origin/master --format="%H" -- .filelint.yml | head -1)

# Filelint を実行する
# (一時的に --rule オプションを用いてインデント修正をしています)
filelint --rule 'indent: {enforce: true, size: 2, style: soft}' --fix $(git diff $(git merge-base origin/master HEAD) --diff-filter=d --name-only)

今後継続的にコーディングスタイルを統一させるため、CI を用いた自動コードレビューシステムを現在構築しています。

最後に

Filelint は社内初の OSS プロダクトです。 今後 OSS 活動を推進していきたいと思っていますので、バグ報告や機能追加要望、PR などお待ちしております。

シンクロ・フードでは OSS 活動に興味のあるエンジニアも募集しています。 ご興味のある方は以下よりご連絡ください。

徳丸浩先生をお招きしてセキュリティ研修を行いました!

こんにちは、シンクロ・フードの大久保です。
弊社では今年の4月に入社した新卒エンジニアを対象に、セキュアスカイ・テクノロジー社(以下SST社)の「eラーニング研修」+「1日オンサイト研修」を行いました。

安全なWebサイト構築のための教育[eラーニング] | Webセキュリティ教育サービス | SST 株式会社セキュアスカイ・テクノロジー

「オンサイト研修」では、あの徳丸本でお馴染み、徳丸浩先生が来ていただけるとのこと!eラーニング研修を受けていない既存エンジニアもオンサイト研修には参加OK、ということだったので、エンジニア全員、徳丸浩先生のオンサイト研修に参加しました。

f:id:synchro-food:20170720170537j:plain SST社の乗口社長もお越しいただきました

f:id:synchro-food:20170720170801j:plain 前半はWebサイトをめぐる状況についてのお話し

f:id:synchro-food:20170720182317j:plain 後半はチームに分かれてディスカッション&発表

私物の徳丸本を持参してサインをもらうエンジニアもちらほら…。

参加者は若いメンバーが多いため、セキュリティを学ぶ動機付けを目的としたオンサイト研修でした。
これからエンジニアとしてキャリアをスタートする新卒メンバーに、セキュリティの大切さや興味を持つきっかけになれば良いと思っています。

徳丸浩先生、SST社乗口社長、ありがとうございました!

尚、シンクロ・フードでは、エンジニアを募集しています。 こういった研修も積極的に実施していく予定ですので、興味があれば是非採用ページをご覧ください!

キャリア採用 | シンクロ・フード採用サイト