Flatt Engineering Blog

We are software engineers working in Flatt. We share our product updates and tech tips.

Vueを用いたPinQulのweb版リリースまでの道のり

はじめまして。今年2月から株式会社Flattにインターンとして加わり、JavaScript, Go, Ruby, Pythonを書いている@akouryyと申します。 この記事では、5月23日にリリースされたPinQulのweb版フロントエンド(pinqul.com)で使用した技術等について説明します。

PinQulとは

「PinQul」はInstagramなどで人気の、トレンドに精通したモデルやインフルエンサー

"それぞれ"にとっての"いいモノ"がもっと手軽に、気軽に、スムーズに。

をテーマに自らデザインしたアイテムや、自身がセレクトや買い付けを行ったアイテムを紹介するガールズファッションのECサイトです。 インターネットで様々な個人が活躍できるようになった時代に、人を基軸とした新たなショッピングプラットフォームとして展開しています。

開発の道のり

f:id:akouryy:20180703190333p:plain
Code Frequency

私の入社直後からweb版の開発を開始し、約3ヶ月でリリースに至りました。

デザインは@toyojuniが書き、ロジックの部分は主に私が(Goによるサーバーサイドの開発などと同時並行で)開発しました。また、@k0mach1iOSアプリと同時並行で開発に参加しました。

この3人に加えて、フロントエンドエンジニアの@kitakさんにメンターとしてコードレビューをしていただきました。また、フレームワーク選定やVueのテクニックからGAEへのデプロイ、高速化、キャッシュまで、沢山のアドバイスをいただきました。以下の内容にも、kitakさんにお世話になった部分が多く含まれています。

使用した技術について

フレームワーク

最近GitHubでReactのスター数を上回ったことで話題になったVue.jsをベースに、サーバーサイドレンダリングの支援などを含めたNuxt.jsを使用しています。Nuxt.jsはReactに対するNext.jsのような位置付けで、ユニバーサルな(サーバー、クライアントの区別をほとんどせずに)アプリケーションを素早く開発できる素晴らしいフレームワークです。

VueやNuxtの素晴らしい点は、

  • 何よりもまずドキュメントが充実しています。Vue, Nuxtに加えVuexなどのライブラリも、日本語のチュートリアルやドキュメントが豊富に用意されています。これらに一通り目を通せばすぐに開発に必要な知識を得ることができます。
  • それ以外の素晴らしい点はドキュメントを読めばわかります! Reactなどの他のフレームワークとの比較も載っていて、フレームワーク選択の際に役立ちます。

デザインパターン

f:id:akouryy:20180703190337p:plain (Vuex ドキュメントより)

上の画像の通り、VuexではFluxやReduxのような単方向フローの設計を用います。PinQulのWeb版の設計もこれに従っています。

アカウント認証

PinQulのアカウントはFirebase Authenticationで管理されています。iOSのこの記事をぜひご覧ください。web版でも既存のバックエンドに合わせてFirebase認証を使用しました。

開発の方針と困ったところ

サーバーサイドレンダリングを使う

今回開発したweb版は基本的にvue-routerによるシングルページアプリケーションで、ページ遷移時には基本的にクライアント側でコンテンツを描画する一方、外部から流入したアクセス時にはサーバー側でページを描画しています。

これはSEOSNSシェアを意識してのことです。特にOGPタグを生成する処理をクライアント側で動的に行っても意味がないので、そのためにサーバー側のレンダリングが必要になります。

サーバーサイドレンダリングを使わない

体感の読み込み時間の短縮

リリース当初はトップページのライブ一覧や商品一覧もサーバー側でAPIを叩いてレンダリングしていたために、トップページにアクセスした際のサーバーの応答に時間がかかってしまいました。この状況を改善するために、トップページ(OGPは固定)ではサーバー側でAPIを叩くのをやめ、ブラウザにページの応答が返ってきた時点ではコンテンツが入るべき場所にダミーの長方形が表示されるようにして、クライアント側で叩いたAPIのレスポンスが返ってきてから置き換えるように変更しました。

全コンテンツが読み込まれるまでの時間は短くなっていませんが、「何も応答が返ってこない」という時間は大幅に短縮されました。

Firebase

Firebaseはサーバー側でうまく動作しなかったので、認証が必要な部分は全てクライアントで処理を行っています。そのためNuxt.jsを使っているとはいえ、結局ソースコードの様々な部分でサーバーサイドレンダリングかどうかを判別する必要がありました。

GAEへのデプロイ

pinqul-webのサーバーの処理はGAE上で行われています。しかし、GAEにデプロイする際にはとても苦労しました。 まず、devDependenciesがGAE上でインストールされず、仕方なく全ての依存パッケージをdependenciesに移してデプロイしました。 また、ヘルスチェックを正しくハンドリングできず、サーバーが502ばかり返すようになったこともありました。

現在ではデプロイは問題なく動いていますが、Flexible Environmentなので1回のデプロイに15分ほどかかってしまうのが少し残念です。 Node.js Standard Environmentの正式版リリースが待ち遠しいです。ベータ版も少し見てみましたが、短時間試しただけではうまくいきませんでした。

今後の展望

今回開発していて改めて、動的型付けのつらさを痛感しました。数え切れないほど undefined.f is not a function といったエラーに遭遇しました。 幸いVue.jsやNuxt.jsも他の多くのライブラリと同様に公式でTypeScriptサポートがなされています。そのため、いつになるかは分かりませんが、今後webの全ソースコードをTypeScriptで書き換えたいと思っています。

テスト

「そのうち書く」と言って3ヶ月以上が過ぎてしまいました……

キャッシュ

勉強不足でキャッシュに関して十分な知識を持っていませんが、適切にキャッシュを入れていきたいです。

エンジニア募集

Flattでは

  • Flattという会社に興味がある方
  • GoやJavaScript, そしてTypeScriptで開発したい敏腕プログラマ
  • 素晴らしいメンターさんのもとで技術力をさらに向上させたいエンジニア

を募集しています!少しでも興味が湧いたという方は、気軽にお茶しながらFlattのメンバーとお話しましょう!お待ちしてます!

Googleが発表したコンテナランタイムサンドボックスgVisorについて発表してきたのでまとめてみた

はじめに

はじめまして、Flatt., Inc. でCEO兼週末エンジニアをやっている @niconegoto です。

今回扱うgVisorはKubeCon2018でローンチが発表された、従来のコンテナよりも安全性を高めたコンテナランタイムであるrunscを含むプロジェクトのことです。 Googleの基盤技術と目されており、Java8/Node用の新たなGAEはgVisor上で動いていることが発表されているなど、普段業務でGAEを使用しているFlattとしても注目の技術です。

以下のツイートのようなgVisorがGoogleの基盤技術であることを匂わせる発言も数々出ており、またGoogle社内で長期間運用された技術が「やっと時代がGoogleに追いついてきたか」という形で世に出てきた事になります。

*テクニカルな内容については追って論文を発表するからそれを待ってね、と書いてあったので論文以外で現状分かっている範囲での情報になります。

github.com

今回はそんなgVisorについてgolnag.tokyo#18にて発表してきたので、補足をしながらまとめていきたいと思います。

資料は以下です。

speakerdeck.com

間違いや表現が悪い部分などもあるかと思いますので見つけたらぜひ @niconegoto までお知らせください。

gVisorとは

READMEには以下のように説明されています。

gVisor is a user-space kernel, written in Go, that implements a substantial portion of the Linux system surface. It includes an Open Container Initiative (OCI) runtime called runsc that provides an isolation boundary between the application and the host kernel. The runsc runtime integrates with Docker and Kubernetes, making it simple to run sandboxed containers.

gVisor takes a distinct approach to container sandboxing and makes a different set of technical trade-offs compared to existing sandbox technologies, thus providing new tools and ideas for the container security landscape.

gVisorは、Goで書かれたユーザスペースカーネルであり、Linuxシステムの大部分を実装しています。 runscというOCI(Open Container Initiative)ランタイムが含まれており、アプリケーションとホストカーネルの間の分離境界を提供します。 runscランタイムはDockerとKubernetesと統合されており、サンドボックス化されたコンテナを簡単に実行できます。

gVisorはコンテナのサンドボックス化とは明確なアプローチをとり、既存のサンドボックステクノロジとは異なる技術的なトレードオフを行い、コンテナセキュリティの新しいツールやアイデアを提供します。

この文章を理解するためには、まず従来のコンテナがどうであったかを理解する必要があります。

f:id:niconegoto:20180602191229p:plain

セキュリティ的に課題があったのが上記の図における赤い部分です。 従来のコンテナ技術では単一のホストカーネルを使用することによってパフォーマンスを向上させていたのですが、それによって単一の脆弱性からホストが攻撃されてしまう危険性があり、多くの問題が報告されていました。

issue.k8s.io

ホストのファイルシステムをマウントされてしまうバグ
>

runc

次に runc という単語が気になるかと思います。 上記の図における runccontainerdはDockerを小さくて再利用可能なコンポーネントに分解するために導入されたもので、 Open Container Initiative (OCI)が策定したコンテナに関する標準規格に基づいています。

f:id:niconegoto:20180527135227p:plain 引用元: Docker 1.11: The first runtime built on containerd and based on OCI technology - Docker Blog

runc というものが今デフォルトで利用されているランタイムなのですが、 runc の部分をよりセキュアに(ホストカーネルと隔離して)実行させようとしているのが runsc であり、それを含むのが今回発表されたgVisorです。

OCIの策定した規格については以下の資料が詳しいのでぜひ。

github.com

ホストカーネルとの分離

よりセキュアに実行するためにはホストカーネルとの分離が必要となったわけなのですが、これまでもいくつかのアプローチが存在しました。 それが

  • マシンレベル仮想化(Machine-level virtualization)
  • ルールに基づく実行(Rule-based execution)

です。

マシンレベル仮想化(Machine-level virtualization)

例) KVMXen

仮想マシンモニタ(VMM)を介して仮想化されたハードウェアをゲストカーネルに公開する方法です。 非常にセキュアですが、パフォーマンス面で大きなオーバーヘッドが存在してしまいます。

f:id:niconegoto:20180527113237p:plain

ルールに基づく実行(Rule-based execution)

例) seccomp、SELinux、AppArmor

アプリケーションまたはコンテナの細かいセキュリティポリシーを指定できます。 しっかりセキュリティーポリシーが設定できれば非常に協力なのですが、初見のアプリケーションなどでそれを完璧に定義することはほぼ不可能です。 上記のような理由から、Rule-based executionは単体で用いられることは少なく、複合的に使用してさらにセキュアにすることに用いられており、実際に今回のgVisorでもこのRule-based executionの機構を併せ持っています。

f:id:niconegoto:20180527113409p:plain

gVisor

ではgVisorはどうなのでしょうか。

gVisorはユーザーアプリケーションのシステムコールを受け取り、仮想化されたハードウェアを通すことなく、ゲストカーネルとして機能します。 このアーキテクチャによって、仮想化によるコストを削減しながら、柔軟なリソースフットプリント(スレッドおよびメモリマッピングに基づく意味での)を提供することができます。 (gVisorのアプローチはUML(User Mode Linux)と似ていますが、UMLはハードウェアを内部的に仮想化しています。)

ただし、注意すべきはgVisorが万能なわけでなく、これまでとは違った形での技術的トレードオフを行っているということです。 gVisorは上記の様なメリットを享受する代わりに、アプリケーションの互換性の低下とシステムコールのオーバーヘッドの増加という犠牲を払っています。

gVisorが苦手とするシステムコールの負荷の大きいワークロードに関してはパフォーマンスが低下する可能性があるため、マシンレベルの仮想化を使用した方が良い可能性があるという事です。

gVisorは何をしているのか

f:id:niconegoto:20180608112319p:plain

簡単にまとめると以下の様になります

① アプリケーションのシステムコールインターセプト(ptrace)

システムコールをフィルターして制限(seccomp)

システムコールを置換(gVisor)

gVisorは、アプリケーションによって行われたすべてのシステムコールインターセプトし、それらを処理するために必要な作業を行います。gVisorは単にアプリケーションシステムコールをホストカーネルにリダイレクトするだけではなく、システムコールを置換します。 VMMと同様に、ユーザーアプリケーションがシステムコールを直接制御することはできません。

コンテナランタイムやオーケストレータを非rootで動かす流れは以前からあり、runcは2016年からrootlessを採用していましたがユーザー空間での実行ができていませんでした。

SentryとGofer

f:id:niconegoto:20180608120345p:plain

gVisorコンテナランタイムは

  • Sentry
  • Gofer

の2つの別々のプロセスに分割されます。

まず、Sentryプロセスはカーネルを含み、ユーザコードの実行とシステムコールの処理を担当します。 Sentryは空のユーザー名前空間で実行され、gVisorによってホストに対して行われたシステムコールは、seccompフィルタを使用して制限されています。

サンドボックスを越えたファイルシステム操作は、9P(Plan 9 Filesystem Protocol)接続を介してGoferと呼ばれるプロキシに送られます。 GopherとGofer発音一緒だしなんでこんな名前にしたんだ…

Goferは、アプリケーションに代わってホストファイルを開き、ホストファイルアクセス自体を持たないSentryプロセスに渡すことによって、ファイルシステムのプロキシとして機能します。

ネットワークアクセス

Sentryは、netstackという独自のネットワークスタックを実装しています。

github.com

ネットワークスタックは全てSentry内部で処理され、ホストネットワークスタックから隔離されます。 データリンク層のパケットは、DockerまたはKubernetesによって設定されたネームスペース内の仮想デバイスに直接書き込まれます。

プラットフォーム

Sentryは、基本的なコンテキスト切り替えとメモリマッピング機能を実装するプラットフォームを必要としており、PtraceとKVMに対応しています。 ptraceのパフォーマンスがネックになっているので、現在試験的に導入されているKVMが主流のプラットフォームになっていくのではないかと言われており、このあたりはまだ改善の余地があるところでしょう

そのほかにもLinuxシステムコールで未実装のものがあるなど、発展途上のgVisorですが、この記事を読んだ皆さんが1人1つシステムコールを実装していけばあっとういう間にその問題は解決することでしょう。OSSってすばらしい。

さいごに

もっと知りたいという方はGoogle独自のクラスタマネージャとして既に発表されていたBorgの論文も併せて読んでおくといいかもしれません。

参考資料

Verma, Abhishek, et al. "Large-scale cluster management at Google with Borg." Proceedings of the Tenth European Conference on Computer Systems. ACM, 2015.

[表示が崩れる場合ダウンロードしてご覧ください] 2018年のDocker・Moby

Linux標準の仮想化技術「KVM」の仕組み (1/2):知って見るみるKVM(1) - @IT

お茶がしたい…!!

  • もっとgVisorについて議論したい
  • Flatt何やってるのか気になる…!
  • はあ…Dockerすき…

と思った方は以下のフォームを送っていただければ、ランチなどごちそうさせていただきます。

初期スタートアップにおける社内文化の在るべき形とは ~3ヶ月開発チームをマネジメントしながら本気で考えた道のり~

# はじめに この記事は6人の小さなチームのマネジメントを3ヶ月齧っただけの僕が、それでも外部のメンターさん方に猛烈に相談に乗っていただいたりしながら 社内文化 について試行錯誤した道のりについて綴っています。拙文ですが温かく見守ってくださると幸いです。

背景

簡単に背景を共有させていただきたいと思います。

僕が3ヶ月間エンジニアのマネージャーを経験したこと

僕が所属している 株式会社Flatt のエンジニアチームは

12月まで 1月以降
実働人数 3人 2倍の6人に
社長の関わり方 手も動かしつつマネジメント 現場を離れて社長業に専念
マネジメント層が不在

というように 2017年 => 2018年 という年の変わり目を境に大きく環境が変わりました。

そこで純エンジニアポジションでは1番の古株だった(それが理由かはわかりませんが)僕がエンジニアのマネージャーに抜擢され、この3ヶ月間 6人のエンジニアチームのマネージャーとしてエンジニアの組織全体を見る経験 をしました。

社内文化に対する考え方の変遷

きっかけになったのはマネージャー就任

当初僕は「マネージャーになると言っても人々のタスクを管理してみんなが働きやすい環境を作ればええんかな?」くらいの甘い認識でいました。

ところが始めてみると驚くことに、1日にしなければいけない意思決定の数が大小合わせて今までの10倍くらいになりました

一介の平エンジニアとして働いていた時は、社長から降りてくる指示に従って、目の前の作業を最大限のパフォーマンスで完遂していれば評価されていました。

マネージャーになると『ユーザーのこと(一番大事!)』『リソース』『期日』『メンバーのモチベーション』などの多くのファクターに思いを馳せながら、テンポよくメンバー全員分の意思決定をしていかなければなりません。

しかしいかに小さなチームとはいえ、この意思決定が一過性のものであったり属人的であったりすると組織全体の脆弱性に繋がります。

そこで僕が意思決定の拠り所としたのが 会社の文化やコアバリューでした(当然の流れだと思います)。

(※ 今後便宜のために『正確な』意思決定という言葉を使いますが、これの意図は打ち手として有効かどうかではなく、組織としての軸に沿っている(つまり一過性がなく属人的でない)という意味です)

社内文化は何のために存在するのか?

社内文化やそれに属するマインドに関する記事を見ていると「個人の成長のため」みたいなメンバー側から見た表面的な切り口で自己啓発のような文脈で語られているものが散見されます。

しかし、個人の成長 / 迅速で正確な意思決定 / コミュニケーションの円滑さ など社内文化の浸透による恩恵は様々ですが、それもひっくるめて社内文化の目的は必ず『長期的な組織の利益の最大化』に帰着します(当たり前のことなんですが)。

『長期的な組織の利益(どう定義するかは組織によります)の最大化』が全ての社内文化の上に存在しており、社内文化はその潤滑油にすぎない という事実を常に念頭に置いておかないと、社内文化はすぐに形骸化してしまいます。

社内文化の形骸化とは

端的に言うと 社内文化の言葉だけが一人歩きして、その本質への理解が疎らになり、結果パフォーマンスの低下に繋がる ことを表現しようとしています。

例えば『自責』という文化があった時に、長期的な組織の利益の最大化を考えると他の人に投げた方がいいトラブルシューティング(その問題について考察し、パフォーマンスを最適化するために他の人にアサインした時点で自責)を、『自責』という言葉の言葉尻だけ捉えて全部自分で抱えた場合、それは本来の目的に適っていないので形骸化していると言えると思います。

これが進行してしまう原因は大きく分けて2つあると思います。

組織の拡大に伴う形骸化

これは規模に応じて避けられなくなってくると思います。

初期のメンバーは特に、その組織に対して並々ならない思いがあります。皆が空気を吸うように「長期的な組織の利益の最大化」を念頭に置いて考え、能動的に行動します。

しかし、規模の拡大に伴って組織全体をマクロに捉える見方は薄れていき、だんだんと部署・チーム・個人のようなミクロな視点が蔓延していきます。

これは当然の動きであり、非難する意図は全くありません。全員が全員、前述のような長期的な組織の利益の最大化を第一に考えられる巨大組織の構成は正直不可能だと思います。

結果として生まれる個人がミクロな見方でバラバラな方向を向いている状態の下で、社内文化の形骸化を防ぐマネジメントはとても難易度が高いと思います。

現に大企業やメガベンチャーに就職した先輩方に企業の文化やコアバリューについてお話を聞くと、「言葉は覚えているが特に深い意味も考えていない人」、「なんとなく小馬鹿にしている人」や「そもそも存在自体を知らない人」がほとんどでした。

認識共有の不足からくる形骸化

前述の通り、組織がかなり大きくなってくると話は変わりますが、今後の組織の在り方を強く定義する存在である経営陣・初期のメンバーについては、この企業文化は高いレベルで正確に共有されていなければなりません

このメンバーは今後、この文化やコアバリューを他のメンバーに伝えていくハブの役割を果たして行くことになるからです。

しかしメンバーが少ない中でも、ただでさえめまぐるしく状況が変化する初期のスタートアップの中では綿密なコミュニケーションの努力を怠っていると文化やコアバリューへの認識は容易に分岐していきます(体験談です)。

それぞれが大きな責任を持ち、かなりの量と質の意思決定を繰り返していると、大きなパラダイムシフトが頻繁に発生します。怖いのはそれが無意識化で進行するということです。

なので、社内の文化やコアバリューを擦り合わせて再定義する作業は、継続的にしていかないといけないのだと思います。

スタートアップ初期の社内文化の在り方

前述の通り、文化やコアバリューの形骸化は時間や規模に伴って大なれ小なれ必ず進行してしまいます。

しかし、堅牢な組織を保ち続けるために、これに抗う最大限の努力は継続的に行うべきだと考えています。

特に初期スタートアップに関しては以下の2つを心がけることが有効であると感じました。

最小人数で定義して共有には全体で取り組む

文化やコアバリューを定義する初期の段階で決めるための話です。

f:id:k0mach1:20180420111836p:plain

Fablic CEOの堀井さん が会社の納会にお話をしに来てくださった時に、

社内文化やコアバリューは社長の独断で定義していい、そしてそれをより多くのメンバーに(初期メンバーは特に)より深く浸透させることに対してのリソースは惜しまず割くべき(コアバリュー合宿など)

とおっしゃっていました(堀井さんはこの記事でも企業文化やコアバリューの大切さについて触れています)。

正直、この社内文化やコアバリューに関しては正解が存在しないと考えています。そんな抽象的な議論に多くの人数が関わると船頭多くして船山に上ってしまい、徒らに時間が過ぎていってしまいます。

それに加えて、お互いの主張が交錯して、最終的な成果物がそれぞれを折衷した何ともいえないものになってしまうというケースも往々にしてあります。

なので、定義は最小人数(できれば社長が1人で)して、他のメンバーはそれを理解するための施作・努力を継続的に行っていくというスタンスの方が効率よく軸を安定させられると思います。

(たまにこれらの定義を社外の人々に任せるケースを見ますが、とてもうーんという気持ちになります。)

共感レベルでの採用

(社内の文化やコアバリューの形骸化に)抗う最大限の努力は継続的に行うべき

と言及しましたが、やらなければいけないことが無限にあるスタートアップの中で、これにコストをかけすぎるのはとても痛手です。

なのでここの共有にコストがかかる人間は(初期は特に)採用の段階で弾くべきだと考えています。

事業の表面的なスピード感を重視するとスキルセットや経歴を最重要視しがちですが、僕は長期的な視点で見た時にこちらの方が圧倒的に重要だと思います。

これは僕が勝手に脳内でイメージしているオレオレ共感度グラフ的なものです。

f:id:k0mach1:20180421151010p:plain

社長の脳みそを完全再現しているレベルでの共感は実質無理なので、初期のメンバーはできる限りピンクレベルであるべきであり、最低でも黄色レベルでないといけないと思います。

また組織が巨大化していった時に、全員の(特に責任を持っている人々の)共感レベルがどれくらいのレベルで保てるかというのが、堅牢な組織作りの大きな指標になると考えています。

社内文化に対する現在の僕なりの結論のまとめ

・文化やコアバリューの形骸化は時間や規模に伴って大なれ小なれ必ず進行する

・しかし、これは組織の脆弱化に繋がるため、抗うための最大限の努力は継続的に行うべき

・社内の文化やコアバリューを擦り合わせて再定義する作業は継続的にしていかないといけない

・初期スタートアップでは『最小人数での定義』と『共感レベルでの採用』が有効である

終わりに

感想

最後まで読んでいただきありがとうございました。正直この3ヶ月間においては日本トップクラスで労力を組織への考察に割いたと思います。

かなりストレスフルでしたが、その過程で働くということへのマインドもかなり研ぎ澄まされたのでかなりいい経験になったと思います。

こんなぺーぺーの悩みを真剣に聞き、相談に乗ってくださった業界の先輩の方々には感謝の気持ちでいっぱいです。

特に納会に来てくださった堀井さんの他に、組織についてたくさんのFBをくださった DeNAの千條さん、eurakaのkaneshinさん、Mercariのosamingoさんには感謝してもしきれません。

今後もエンジニアとしての力量を磨きつつ、考察を続けていこうと思います。

僕とお茶しましょう!

この記事を読んで

  • 内容を読んで中身について議論したい

  • Flattという会社に興味が出た

  • まちおさん大好き

と思った方は以下のフォームを送っていただければ、ご飯なりお茶なりご馳走します!

エンジニア用のフォームのようになっていますが、bizの方もお気軽にどうぞ!

Androidアプリ公開の裏に隠されたバックエンド秘話 〜ffmpegでライブ動画いじり〜

f:id:toyojuni:20180430185809p:plainはじめまして! 株式会社Flattに今年からインターンとして入り、バックエンド側の開発をしている、五十嵐将と申します。 普段はGoを書いていますが、この記事では一切Goはでてきません。

このブログはこれで2記事目なわけでして、記念すべき1記事目は今月初旬のPinQul Androidアプリのリリースについてだったわけですが、 アプリ開発だけでなくてこれにあわせてバックエンド側も頑張ったということで、動画の変換処理について書こうと思います。

PinQulの動画保守業務について

PinQulは、株式会社Flattのライブコマースサービスです。

pinqul.tv

このサービスでは現在、週に1つくらいのペースでライブを放送していますが、ユーザーの声もあり、ライブ放送終了後もそのままの形のライブ放送を一部公開しています。

つまり、生放送中にライブ動画が止まらない・遅れないような対策の他に、過去のアーカイブ動画を保存しておき、いつでも再生できる状態にしておくような保守作業も我々の仕事となっています。

PinQulの動画配信について

動画配信にはHLS形式を採用しています。 これの中身は、細かく分けられたMPEG2-TS形式の動画ファイル群とM3Uプレイリストファイルからなっていて、 アプリのプレーヤーにこのプレイリストファイルのありかを渡すと、プレイリストに書いてある動画が取得され再生される、という流れです。

この個々の動画ファイルが少々よくない性質を持っていたため、次のような問題が起きていました。

Androidアプリのリリースに当たって発覚した問題

前述したとおり、今月4月7日、PinQul Androidアプリがリリースされました🎉

このアプリでも、過去のライブ動画が再生できるような構成になっているのですが、 iOSアプリでは正常に再生できている動画が、Androidでは再生できないという問題が起きていました。 具体的には、初めの数秒はなんとか再生されるのですが、それ以降は完全に動画が止まってしまっていたのでした。

原因

確認のため、有名どころのオープンソース動画プレイヤーVLCでも再生を試しましたが、こちらも一切再生されないような状況でした。 このことから、Androidアプリというより、配信方法に問題がある可能性を考え、実際そのとおりでした。

実はこの細切れ動画のMPEG2-TSファイルは、それぞれ自分が動画全体の中で何秒からの位置にあるかを情報として持っています。

これはffmpegの中に含まれるツール、ffprobeを叩くと確認できます。

$ ffprobe media.mp4.ts
ffprobe version 3.4.2 Copyright (c) 2007-2018 the FFmpeg developers
  built with gcc 7.3.0 (GCC)
=== 省略 ===
Input #0, mpegts, from 'media.mp4.ts':
  Duration: 00:00:03.02, start: 31.158000, bitrate: 2754 kb/s

これは正常な、全体の中で31.158秒付近から始まる(start:のあたりを見てください)動画ファイルなわけですが、 問題があったものでは、すべての動画ファイルでstart時間がそろってしまっていました。

つまり、そもそも動画ファイルがよくないはずなのに、なぜかSafariブラウザを含むiOSアプリでは正常に再生できてしまっている、というわけでした。

これはもとを辿れば配信のために動画を整形しているところが悪いわけで、そちらも今はもう直っているのですが、過去の動画に関してはどうにかしてAndroidで再生させられるようにしなければなりません。

対応

少しソースを見失ってしまったのですが、この問題について検索した結果、

  1. ffmpegで動画をMPEG2-TSに分割する際にinitial_offsetを指定することによってstart timeを指定時間にずらせるため、それで個々の動画のstartを合わせてしまう方法
  2. 細切れの動画を一度まとめて1動画ファイルにしてしまい、また分割する方法

を見つけました。

先に1.の方を見つけたので試してみました。 このとき、どうやってinitial_offsetに指定すべき値を個々の動画に対してとるのかという話があるのですが、 これは下の回答にあるような、ffprobeの魔法の使い方をすることによってとってくるような、実質shellscriptなRubyコードで行いました。

superuser.com

この方法によって、確かに動画はAndroidVLCとも再生できるようにはなりました。しかしながら品質はあまりよくありませんでした。 というのも、やはりdurationのとり方が荒いのでしょうか、再生中に個々の動画の間の切れ目が分かるような断絶が入ってしまって、見ていて心地よいものではありませんでした。

そして調査を続けた結果見つけたのが、2.にあげた、まとめて分割する方法です。 実はMPEG2-TSは、単純にあのcatコマンドでつなげてしまうだけで、再生可能な動画ファイルになってしまうことが判明しました。

これが分かれば簡単です。 動画一覧をとってきて、catで順番につなげ、それをまたffmpegで分割するRubyスクリプトを書き捨て実行すると、すべての動画がAndroidでなめらかに再生可能になりました!

まだストリームの連続性が不完全で、VLCだとコンソールにWarningが出るなどもあり、これでいいのかという話もあるのですが、 まあ元の動画は残っているし、待ちに待ったAndroidリリースですので良しとしました。 また問題があれば対応したいと思います。

働く仲間、募集中!

Flattではサービスを一緒に開発してくれるエンジニア(に限らず)を募集中です!

www.wantedly.com

また、以下のフォームから、もっと気軽にお茶ができるので、ぜひどうぞ!

Android版PinQulアプリをリリースしました

はじめまして、株式会社FlattでAndroidエンジニアをしている前原理来です。 3年ほどAndroidアプリ開発をしています。

2018/04/07, 「PinQul」 Android版アプリをリリースしました。🎉🎉

PinQulとは

株式会社Flattが展開するライブコマースサービスです。 ご存知の方も多いかとは思いますが、ライブ配信と決済を組み合わせて認知行動と購買行動を直結させたライブコマースは、「淘宝(タオバオ)」や「蘑菇街(モグジェ)」に代表されるように中国で大きな興りを見せました。

それを受けて日本でも様々なプレイヤーがこの領域に参入してきたのが昨年2017年でした。今年に入ってもプレイヤーの参入は相次いでおり(一方、すでに3つほど撤退を決めた事業者も存在しますが)次世代ECとしてホットな領域です。

PinQulはBtoCの若い女性向けのライブコマースプラットフォームです。

メルカリチャンネル等が競合として挙げられることもありますが、BtoCとCtoCでは本質的に違うのでそういうわけでもありません。またPinQulと同じBtoCでもだいぶ方針の違うプレイヤーであったりするので一口にライブコマースと言う切り口で見てしまうのは危険です。

プライベートブランド制作や大手アパレルとのアライアンス等、Tech Crunchさんの記事で取材いただいておりますので興味のある方は是非ご一読ください。

「ユーザーと近い世代の方がいいものを作れる」東大発・Flattがライブコマースアプリ「PinQul」公開 https://jp.techcrunch.com/2017/10/05/pinqul/

30分で40着以上売れることも――ライブコマース「PinQul」がプライベートブランドを開始 https://jp.techcrunch.com/2017/12/19/pinqul-pb/

ライブコマースはECの有力なチャネルとなるか、PinQulとTOKYO BASEがPB商品の共同販売へ https://jp.techcrunch.com/2018/02/16/pinqul-tokyobase/

2017/10 にiOS版アプリが先行リリースされてからiOSアプリでしか利用する手段がありませんでした。
そこで僕がAndroid版アプリを作るということになりました。

開発期間

2017/10 中旬頃から開発を開始しました。
リリースは2018/04 なので約6ヶ月でリリースまで漕ぎ着けた形になります。
f:id:Flatt:20180407174822p:plain

開発リソース

Androidエンジニアは僕一人で、リリースまで一人で作りあげました 😤
自分が学部3年生ということもあり、学業と並行した開発でした。
平日は週5で授業があり、授業の合間、空き時間を見つけてコミットして、土日もガッツリやるという生活を送っていました。
そんなまったりペースの開発でしたが会社のメンバーは暖かく応援してくれました。

技術的な特徴

ソースコードは100% Kotlin

近年Android界隈ではKotlinが盛り上がっています。Javaより簡潔に書けて、イマドキな言語仕様も入っていて素敵な言語だと思っています。今から新規Androidアプリ開発するならKotlinや、ということでKotlin採用しました。
自分はKotlinを書き慣れているので、あと当時は僕一人でリリースまで持っていく予定だったので学習コスト等は考えず採用しました。

設計

自分は以前Fluxで動画再生アプリを作った経験があります。最初は、機能自体も大枠似ているし、その通り作ってもよいのではとも考えました。
しかし複雑に組んだ(レイヤー分けされた?)設計だと保守管理は楽にはなりそうですが開発速度が落ちると考えました。
そこで以下の点に絞って設計を考えました。

  • 今はリリース優先であり、開発速度は犠牲にできない
  • 自分のエンジニア思想的にコードは動けばいいということではないのである程度きれいな設計が欲しい
  • 後からJoinしてくる人に設計で負担をかけたくない

結果採用したのは、CleanArchitectureっぽいやつ(Gatewayはなく、UseCaseはある、等)です。

(参考にした記事) MVP(MVVM)とクリーンアーキテクチャに関する理解 - Qiita

https://github.com/android10/Sample-Data/blob/master/Android-CleanArchitecture/clean_architecture_layers_details.png?raw=true

ライブラリ選定

RxJava2を使っています。
通信周りはOkhttp,Retrofitです。
Picassoを使っているのですが、後から複雑なことがやりたくなったりしてきて少し大変だったのでGlideだったら良かったかなーと思っています。
layoutは基本的にConstraint layoutです。
Android Architecture Componentsとか、Daggerとか入れたかったんですが今回は見送りました。
今後余裕が出てきたら使っていきたいです。
あとは薄くUI周りのライブラリを使っていたり、Forkして改造して使ったりしています。

これからの目標

ようやくiOS版とAndroid版が揃いました。
ここからアプリのユーザーを増やして、使いやすくして、サービスを伸ばすためのスタートラインに立てました。
ユーザーファーストで考えながら、前述したライブラリ等の技術的チャレンジも行っていければと思っています。

採用

Flattでは新サービスを一緒に開発してくれるiOSAndroidエンジニアを募集しています。

www.wantedly.com

www.wantedly.com