コネヒト開発者ブログ

コネヒト開発者ブログ

Kotlin1.3でリリースされたCoroutineを試してみた

2017年11月にAndroidエンジニアとしてjoinした関根です。 最近はiOSアプリの開発も担当しております。 こちらはコネヒト Advent Calendar 2018 - Qiitaの10日目の記事です。

さて、先日Kotlin1.3の正式版がリリースされました。

下記が主要なリリースの一覧となります

  • CoroutineのStable版
  • Kotlin/Native ベータ
  • マルチプラットフォームの拡充
  • Ktor 1.0 ベータ
    • ※現在はStable版となっています

かねてからマルチプラットフォーム化を進めること目標としてきたKotlinですが、今回のリリースで、その未来へ大きく前進したように思えます。 Kotlinからますます目が離せないですね!

さて、それではマルチプラットフォームのご紹介を・・・・といきたいところですが、今回はStableとなったCoroutineの紹介をさせていただこうと思います。 下記が今回のアジェンダです。

  • Coroutineとは何者か?
  • ChannelでのCoroutine間通信
  • Coroutineを利用したPub/Subの仕組み

Coroutineとは何者か?

今回、StableとなったCoroutineはKotlinでのnon-blockingプログラミングを支える機能です。kotlinxパッケージの配下に存在します。 利用目的としては ファイルI/OやネットワークI/Oなどの非同期処理が一番に浮かびます。

// Coroutineの生成
GlobalScope.launch(Dispatchers.Main) {
    progressDialog.isVisible = true
    // asyncでnon-blocking関数化,awaitで待ち受けの開始
    // ※ここで処理は停止されるが、ブロッキングは発生しない
    val bigData = async { loadBigData() }.await()
    progressDialog.isVisible = false
    displayData(bigData)
}

上記のようにblockingが発生しうる処理を直列的な記述を維持しながら、non-blockingな処理を実現できるのがCoroutineの強みだと思っています。また下記のようにすると並行処理もお手軽に実現できます。

// Coroutineの生成
GlobalScope.launch(Dispatchers.Main) {
    progressDialog.isVisible = true

    // asyncでnon-blocking関数化,awaitで待ち受けの開始
    // ※ここで処理は停止されるが、ブロッキングは発生しない
    val bigDataInt = async { loadBigDataInt() }
    val bigDataString = async { loadBigDataString() }
    val bigDataPair = Pair(loadBigDataInt.await(), loadBigDataString.await()) // ここで並行処理が実施される

    progressDialog.isVisible = false
    displayData(bigDataPair)
}

Coroutineが発表されて以降、RxJavaと比較されることが多いですが、筆者としては、どちらを利用することにメリットが大きいかを適材適所で判断し、利用していくのが良いと捉えており、 今回のような単純な非同期処理だけであれば、Coroutineでも十分に有用であると考えております。

ChannelでのCoroutine間通信

アプリ開発においては非同期処理は様々な理由で発生します。 例えば、先ほど例に上げたようなAPI通信に関わるネットワーク I/Oもその一例です。 他にはバックグラウンド処理との同期をしたい場合や、OSからブロードキャストされてきたメッセージングのハンドリングなど。 そのようなユースケースの場合、異なるライフサイクルで存在するコンポーネント間での、メッセージ授受の仕組みが肝要になってきます。この仕組みはCoroutineのChannelという機能を利用することで実現が可能です。

// 送受信用のChannelを生成しておく
val channel = Channel<Int>()

GlobalScope.launch { // 送信側のCoroutine
    for (x in 1..5) channel.send(x * x)
}

GlobalScope.launch { // 受信側のCoroutine
    repeat(5) { println(channel.receive()) } // 5つデータを取得して表示する
}

このようにChannelはCoroutine間で値を安全に授受する仕組みです。 本例以外にもChannelを利用すると、パイプライン処理やFan-in/Fan-outパターンの実装を、楽に記述することができるようになります。こちらはKotlinのサンプルでも紹介されていますので、詳しくはそちらをお読みください。 https://kotlinlang.org/docs/reference/coroutines/channels.html#channels-experimental

なお現時点で、Channelはexperimentalとなっており、今後の開発状況によっては破壊的変更が加わる可能性が残っています。 十分ご検討の上ご利用ください!

Coroutineを利用したPub/Subの仕組み

さて、今回はChannelを利用してPub/Subの仕組みを試してみましたので紹介しようと思います。 ソースコードの全体像は下記です。

class PubSub<T> {
  // 1. Pub/Sub用のChannelを生成
    val bus = BroadcastChannel<T>(1)

    // 2. Channelへの送信
    fun send(item: T) = bus.sendBlocking(item)  

    // 3. CoroutineScopeを受け取りsubscribeを開始する
    fun subscribe(scope: CoroutineScope, onConsume: (T) -> Unit) {  
        scope.async(Dispatchers.IO) { // 4. IOスレッドを利用する指定
            Timber.d("async:"+Thread.currentThread().name)
            bus.openSubscription().consumeEach { // 5. 受信の開始
                onConsume(it)
            }
        }
    }
}
・・・

// 受信
val pubSub = PubSub<Int>()
pubSub.subscribe(this) {
    launch(Dispatchers.Main) { // 6. UIスレッドで実行
      // 何かUIに関わる処理
    }
}

// 送信
pubSub.send(1)

全体的な流れを見てみると以下のようになります。

1. Pub/Sub用のChannelを生成

BroadcastChannelは1対多でのデータ授受を目的としたChannelです。 EventBusのように一つのEventを複数の画面やクラスから購読する際に利用します。

2. Channelへの送信

今回の例ではsendBlockingで値を送信していますが、 呼び出し元がCoroutineの場合はsendメソッドを利用しましょう。

SendChannel.send - kotlinx-coroutines-core

3. CoroutineScopeを受け取りsubscribeを開始する

CoroutineScopeはCoroutine自身とその子Coroutineのスコープを決めるものです。 スコープを決めることにより、呼び出し元のLyfecycleに応じて中断処理を行うことが可能となります。 今回のサンプルではActivity単位のスコープとすることを想定しています。

4. IOスレッドを利用する指定

受信待ち受け処理は、I/Oスレッドで走らせています。

5. 受信の開始

openSubscriptionを呼び出すことにより、受信用のChannelが新たに生成されます。 これにより1対多のメッセージ授受を実現できます。 1対1で事足りる場合には通常のChannelを利用すると良いと思われます。

6. UIスレッドで実行

メッセージ受信後の処理をUIスレッドで行うように指定しています。 ここをDispatchers.IOのように指定するとワーカースレッドで処理が行われます。

実装は以上となります。 kotlinとkotlinxのパッケージのみで、Pub/Subが実現できると夢が広がりますね。

終わりに

今回はCoroutineとChannelについての紹介とそれらを利用したPub/Sub実装の紹介をさせて頂きました。 弊社ではRxJavaを利用した実装が多く存在するのですが、Coroutineで代用できる部分がないか、探って行きたいと思います! CoroutineとChannelの良き実装アプローチがあれば、教えてください!!

それでは!!

Travis上でDockerを利用した継続的インテグレーションを実現する with レイヤーキャッシュ

こんにちは!2018年も残すところあと3週間、コネヒト Advent Calendar 2018 - QiitaのDay-9でございます。

ブラック・ラグーンの新刊発売の衝撃がまだ鳴り止まない!の金城(@o0h_)です。 今回はもう5回は読みました、皆さんはどうですか?

ブラック・ラグーン(11) (サンデーGXコミックス)

コネヒトでは継続的インテグレーションにTravisCIを利用しています。
ローカルの開発環境、プロダクトの本番環境にはDocker(ECS)を利用しています。
本番イメージのビルドやpushもTravisCI上で行っています。

かねてより、「折角DockerベースなのにCIは"別環境"なの悲しい」「良い感じにビルドしたイメージをキャッシュして、デプロイを早くできないか」といった声が上がっていました。
そして、サーバーサイドエンジニアが集い「今四半期中に、CIの活用についての見直しを行おう」という誓いを立てたのです。

よりDockerフレンドリーなCIが他にある中で、Travis CI上でのDocker活用には少し工夫がいるかもな・・という風にも感じております*1

具体的なtipsが世の中に出回るといいな、と思い今回は現時点で我々が得ている成果・知見についてご報告いたします。

TL;DR

  • Docker自体はTravis CIでもサポートされているので難なく使える
  • Dockerのビルドをキャッシュするためには、明示的なキャッシュの作成とアップロードが必要
  • マルチステージビルドの結果をキャッシュするには、target buildの結果を保存し --cache-from オプションを利用したビルドを行う

https://cdn.mamari.jp/authorized/5c0cc234-0d90-4bff-b968-001bac120002.jpg

今回の対象となるDockerfile(前提の共有)

JSとPHPが相乗りするイメージを利用しています。
そのため、ローカル環境でのビルド効率化のために、multi stage buildとBuildKitを利用しています。

f:id:o0h:20181209183955p:plain

これを、「Travis CI上でもばっちり使えるように、頑張ってみようぜ!」というのがこの記事の趣旨です。

【ここで参考になりそうな記事】

やりたいこと・目的

Travis CIは、以下の用途で利用しています

  • push/prごとのテストやスタイルチェックと言ったビルド
  • Amazon ECRへのデプロイ
    • docker build コマンドの実行(都度ゼロベースでのビルド)
    • レジストリへのpush、サービスイン

これを、以下のように変更するのが目的です

  • push/prごとのテストやスタイルチェックと言ったビルド
    • PHP系のテスト等は、Dockerコンテナ上で実行する
      • そのために、ビルドに係る処理コストを最小化する
  • Amazon ECRへのデプロイ
    • レイヤーキャッシュを利用して docker build を実行する
    • レジストリへのpush、サービスイン

Dockerのイメージビルド時のキャッシュ利用

Travis CIでのビルド時に、Dockerの「前に作ったイメージ」をキャッシュしておくことは可能でしょうか?
基本的には、こちらの記事で触れている通りの方法でDockerコンテナを利用することができます。

【ここで参考になりそうな記事】

multi stage build時のキャッシュについて

ここで注意として、「マルチステージビルドを使っていると、--cache-fromに最終ステージを指定してもレイヤーキャッシュがされない」という問題があります。

具体的には、

  1. 中間ステージのビルドにキャッシュが利用されない
  2. そのため、最終ステージの「手前」の部分に変更が生じる
  3. 手前が変更されているため、以後のレイヤーでもキャッシュを利用しない

かのように見える現象があります。
例えば、以下のようなDockerfileがあったとします

FROM aaa:latest as A
# hogehoge

FROM bbb:latest as B
# fugafuga

FROM xxx
# piyopiyo

これをビルドします

docker build -t xxx .

さて、ここで使った労力をレジストリにpushしておいて、別ホストでのビルド時にもcacheを使いたい!ですね。

docker pull mine/xxx:latest
docker build -t xxx --cache-from mine/xxx:latest

結果は、残念ながら「A」のステージでキャッシュが効きませんでした。。。
という具合です。

これを回避するため、中間ビルドのイメージも個別に生成することにしました

【ここで参考にした記事】

docker save/loadを利用して、multi stage buildでもレイヤーキャッシュの恩恵を受ける

方向を転換し、「中間ステージごとにイメージを保持・読み込みを行い」「ステージごとに、必要なイメージを明示的に --cache-from 指定をして読み込ませる」というものにします。

なお、この段階で、「最終ステージ以外のものをレジストリに上げるのはどうなのかな・・・」という気持ちになったので、ローカルにイメージを置くことにしています。

docker build -t A --target A --cache-from A .
docker build -t B --target B --cache-from A --cache-from B .
docker build -t xxx --cache-from A --cache-from B --cache-from xxx .

--target を指定しつつタグ付けを行うことで、imageのsaveができるようになります。

docker save A -o A.tar
docker save B -o B.tar
docker save xxx -o xxx.tar

saveしたイメージをファイルとして共有することで、他ホストでも読み込み可能になります。

docker load A.tar
docker load B.tar
docker load xxx.tar

ということで、大まかな方針として「中間ステージ・最終ステージをそれぞれビルドして、セーブして、次のビルドの前にロードする」というやり方に決めました。

【ここで参考にした記事】

ビルド結果のキャッシュを作る

Travis CIは、 .travis.yml 内で「このディレクトリをキャッシュする」というパスを任意に指定することができます。
このディレクトリ下に書き出されたファイルは、次のビルドの際に冒頭で読み込まれ利用可能になるという仕組みです。

それを踏まえて、docker save の出力先を「キャッシュ対象ディレクトリ」にしてしまえば良さそう。

$HOME/docker というディレクトリを設け、そこに放り込むことにします。

# .travis.yml
cache:
  directories:
    - $HOME/docker

Travsi CIは、ビルドのメインとなる script ステージの後〜 deploy ステージの間に、キャッシュを作成・クラウド(S3)アップロードを行います。

  1. OPTIONAL Install apt addons
  2. OPTIONAL Install cache components
  3. before_install
  4. install
  5. before_script
  6. script
  7. OPTIONAL before_cache (for cleaning up cache)
  8. after_success or after_failure
  9. OPTIONAL before_deploy
  10. OPTIONAL deploy
  11. OPTIONAL after_deploy
  12. after_script

from: Job Lifecycle - Travis CI

なので、 before_cache の段階で docker save を実行してしまえばよいです。*2

# .travis.yml
before_cache:
  >
    mkdir -p $HOME/docker && rm $HOME/docker/* && docker images -a --filter='dangling=false' --format '{{.Repository}}:{{.Tag}} {{.ID}}' | xargs -n 2 -t sh -c 'docker save $0 -o $HOME/docker/$1.tar'
  1. cache保持用のディレクトリを(存在しなければ)作り
  2. 利用されていないファイルが生き残り続けないよう、ディレクトリ配下のファイルを一旦全て削除し
  3. docker imagsで取得した「存在するイメージ(タグ)」を、ファイル名にIDを使って保存する
    1. タグを利用すると/が入ってきてややこしくなるため、安全かつ一意な文字列としてIDを利用する

これで、「必要そうなイメージをキャッシュに回す」ことができるようになります。

【ここで参考にした記事】

image保存のチューニング

しかしながら、これは結構なオーバーヘッドになります。
1つは、docker save自体が結構な時間がかかること。saveするイメージは少ないほうが良いです。
2つ目に、キャッシュを使える場面で活かしきれていないこと。multi stage buildを利用するにあたり「キャッシュを最大限生かせるように」考えていますから、「めったに変更がない中間ステージイメージ」が存在するという状態も作られています。これを「毎回、絶対に作り直す」というのは効率が悪く思います。

そのため、「保存されている内容と今使っている内容がに差異がなければ、そのimageのsaveは省略する」ことで省力化できると効率が良いです。

また、Travis CIのキャッシュの保存・取得は、外部ストレージへのネットワーク経由のアップロード・ダウンロードによって行われます。それを考えると、ファイルサイズが小さい方が有利です。
このタイミングで、出力イメージのgunzip圧縮も行うことにしました。

# .travis.yml
before_cache:
  - cache_threshold=$(date “+%Y%m%d %H:%M”)
  - docker images -a --filter=‘dangling=false’ --format ‘{{.Repository}}:{{.Tag}} {{.ID}}’ | xargs -n 2 -t sh -c ‘if [ -e $HOME/docker/$1.tar.gz ]; then touch $HOME/docker/$1.tar.gz; else docker save $0 | gzip -2 >$HOME/docker/$1.tar.gz && echo “$0($1) saved”; fi’
  - find $HOME/docker/. \! -newermt “$cache_threshold” | xargs rm -rf
  1. before_cacheに入った時点での日時をメモしておき
  2. cacheディレクトリ以下のimageそれぞれについて、
    1. いま利用されているものとIDが同一であれば、touch してタイムスタンプだけ更新
    2. IDが違ったら = 内容に変更が生じていたら、docker saveを行って上書き
  3. 1のステップでメモした日時とディレクトリ下のファイルのタイムスタンプを比較、「before_cacheに入る段階より古い」ものを削除
    1. これで「もう利用してないイメージ」が破棄される

ようにしました。

Travis CI上でキャッシュ済みイメージを利用したビルドを行う

ここまでで、 $HOME/docker ディレクトリに「使えそうなイメージ」が配置されています。
あとは、これを実際に利用したビルドを行うようにしましょう。

# .travis.yml
before_install:
  - if [[ -d $HOME/docker ]]; then ls $HOME/docker/*.tar.gz | xargs -I {file} sh -c "zcat {file} | docker load"; fi

instal:
  - docker build -t A --target A --cache-from A .
  - docker build -t B --target B --cache-from A --cache-from B .
  - docker build -t xxx --cache-from A --cache-from B --cache-from xxx .

こうすることで、以前にビルドしたイメージからうまくキャッシュを利用してくれるようになりました。

script/deployでDockerイメージを利用する

あとは、dockerコマンド経由でのテスト実行や静的解析など、自由に「いつもの環境」での処理実行を行えるようになります。
script ステージでのコンテナの利用は、先に紹介したTravisのドキュメントをご参照ください。

また、デプロイ用に改めてイメージをビルドする必要がある!という場合も、同様に(必要に応じた)--cache-fromオプションの指定などで対応できると思います。

感想

本記事の内容は、まだまだ改善の余地があるような気もしています・・・それでも、チーム内で求めていた「大体こんな感じ」という水準までは、一旦持ってくることができました。そうして、公開に至っております。

また、Travisの利用するキャッシュサイズが大きくなることや、docker loadに関するオーバーヘッドが大きくなり、プロジェクトによっては今までよりビルドごとに要する時間が大きくなるんだろうな、という印象も受けています。
それでも、チーム内では「テストの確実性が上がる、本場と同等のイメージで動かせる恩恵がある」という事で合意を得ています。
ここでもまた、「やはりDocker使うならイメージが小さいのが正義・・・!」という気持ちを新たにしました。
ということで、次にやりたいのは「イメージをめっちゃダイエットする」です🐶

「ここ、もっとこうした方が良いのでは?」「ここの理解が間違っている!」といった箇所がありましたら、お気軽にフィードバックをいただけると嬉しいです!

話が固くなりましたので、最後にいらすとやさんのホトトギスを見てお別れしたいと思います。

明日は @katsutomu さんが元気な記事を書いてくれます!

f:id:o0h:20181210005627p:plain

*1:CircleCI等の他サービスを利用すると、もっと容易にDcokerベースの継続的インテグレーションができるかもね!という声は社内でも上がりました。検討の結果、今のスコープでは「CIの乗り換えはしない」という結論に至っております

*2:最初、after_deployでキャッシュを作成していたら上手く動かず、ハマりました・・・

ランチタイムを使ってブレストしているお話

f:id:kichikuchi:20181207122553p:plain

こんにちは。コネヒトでiOSエンジニアをしている @kichikuchi です。 この記事は コネヒト Advent Calendar 2018 8日目のエントリーです。

弊社では隔週金曜日に社内のフリースペースでみんなでご飯を食べながらブレストする通称アモーレ会という取り組みをしています。
約1年程前にスタートして、これまでに20回開催しました。

本エントリーではこの取り組みについて

  1. 何をやってるのか
  2. やってみてどうだったか

の順に紹介させていただきます。

何をやってるのか

アモーレ会とは弊社が運営するサービスのママリをよりよくするためのアイデアを出すことを目的にブレストをする会です。

隔週金曜日の午前中に Slack で声をかけ、4人以上集まりそうだったらお昼に社内のフリースペースで開催しています。

f:id:kichikuchi:20181207122641p:plain
アモラーが集まっている様子

業務ではなくお昼の時間を使っているため参加は自由で、これまでに開発部・企画部・人事部・コンテンツ部と様々な部の人が参加してくれています。

あまり大人数でブレストをすると収拾がつかなくなってしまうので3 ~ 4人になるようにチームを分け、テーマに沿ってチーム毎に30分程度ブレストした後それぞれどんなアイデアが出たかを発表するというのが会の大まかな流れです。

以下がこれまでにアモーレ会で扱ったテーマの一部抜粋です。

- ユーザの抱えている課題は何か
- なんでママリで質問するのか
- なんでママリで質問に答えてくれるのか
- 質問しやすくする
- 7日目のRRを向上させる
- 一年後にもママリを使い続けてもらう
- 適切な人に適切な質問を届ける

初期は課題を発見するためのブレストと深掘るためのブレストを回をまたいで行っていたんですが、徐々に単発でアイデアを出しやすいテーマにシフトしました。

どういうテーマを設定するかはアモーレ会を続けて行く中での悩みの種だったので、次節で改めて触れたいと思います。

やってみてどうだったか

約1年間続けてみて良かったこと、うまくいかなかったことがそれぞれあるので順々に書いていきたいと思います。

職種関係なくプロダクトと向き合う時間が持てるようになった

プロダクトに向き合い、より良くするためのアイデア出しは本来誰がやってもいいことのはずです。いいはずなんですが、そうはいってもそれぞれが主業務を抱えているのでその役割は自然とディレクターポジションの人に偏ってしまいます。

誰でも参加できるブレストを行う場としてアモーレ会を定期開催するようになったことで、プロダクトに向き合い、改善するためのアイデアを考える時間を職種関係なく定期的に持てるようになりました。

新鮮な視点が得られるようになった

アモーレ会には主業務としてプロダクト開発を行ってはいない、様々な職種の方が参加してくれています。 開発メンバー以外では、例えばこれまでに人事部・企画部・コンテンツ部の方が参加してくれました。

弊社の開発チームでは普段の業務中にブレストを行う機会がもちろんあります。しかし、アモーレ会には上述の通り異なるバックグラウンドを持つ人が多数参加してくれているため、開発チームだけで行うブレストでは出ないような新鮮な意見が出やすい場になりました。

テーマ設定が難しい

アモーレ会はお昼の時間を使ったカジュアルなブレスト会で、誰でも自由に参加可能なので回毎にメンバーが変わります。

そのため複数回にわたって一つのテーマを深ぼっていったり、前回の内容を踏まえた上でのテーマに取り組むということが少しやりづらくなってしまいました。

上記の理由から一回約40分で収まりそうなテーマを毎回設定する必要があるのですが、会が続くごとに取り組みやすいテーマを探すことが徐々に負担になってしまいました。

アイデアの実現が難しい

当たり前のことですが、アイデアを出すだけではプロダクトは一向に良くなりません。プロダクトをよくするためには出たアイデアを実行する必要があります。

しかし、現状のアモーレ会ではこのアイデアを出した後のフロー設計ができていません。

アイデアを実現するためにはそのアイデアの定性・定量面の裏付けを取ったり、細かい仕様を詰めたり、さらには工数に対して期待できる効果はどの程度なのかの見積もりを行う必要があります。

そこまでやった上で、開発チームがもっているKPIに則った施策と比較して優先度を高く実行すべきと判断できた時に晴れて開発フローにのせられるようになるので、アイデアを出してから実行するまでには意外と距離があります。

出したアイデアを具体化させるフローの設計が難しく、アモーレ会は現状アイデアを発散させる場所止まりになってしまっています。

おわりに

本エントリーでは誰もが気軽に参加できるプロダクトに関するブレストの場を作っている弊社の取り組みを紹介させていただきました。 ただ最後に述べたように、アイデアを具体化させるフローが整備できていないという大きな課題が残っています。

同じような取り組みをしていて、かつ実行までのフローをこんな風に設計しているよ!という知見を持っている方がいたら是非コメントいただけると嬉しいです!

明日のアドベントカレンダーは二度目の登場 @o0h_ さんです! お楽しみに〜。

JapanContainerDays v18.12にスピーカーとして参加してきました

こんにちは。インフラエンジニアの永井(shnagai)です。

f:id:nagais:20181207104504j:plain

今回は、12/4,5で行われたJapanContainerDays v18.12にスピーカーとして参加させていただいたので、 登壇内容の振り返りと2日間通じて参加した感想を簡単に書こうと思います。

この記事はコネヒト Advent Calendar 2018の7日目の記事です。

qiita.com

発表内容

コンテナを本番導入することでチーム開発がどう変わっていくかというテーマの元お話してきました。

このイベント自体は、今最も勢いのあるKubernetes関連のセッションが8割くらいを占めていた印象ですが、 オーケストレーションツールやその他エコシステム導入の一歩前でこれから本番でコンテナを使っていこうという段階の方々をターゲットに、 本番コンテナ化を後押しできればいいなというような気持ちで資料を作りました。

実際に会場でアンケートを取ったところ、本番導入していた方は2割くらいだったと思うので、一定の役割は果たせたかなと思います。 キーノート後の午後一発目の良い枠で他のセッションも面白そうなものが揃っていたので、会場に人がそこそこ入ってくれていて安心したのを覚えています。セッションを聞いてくださった方々ありがとうございました。

資料は下記で公開していますので、よろしければご覧ください。

その他の感想

今回2日間色々なセッションに参加させていただいたりイベントで出会った方との会話の中で、色々な刺激を受けたので雑に自分が感じた感想をまとめておきます。

  • Kubernetesがこんなに愛されているのは、周辺のエコシステムを含めた作り込みが出来る部分が大きいのかなと感じた。
  • ただ、やはりエコシステムを含めて全部理解して使いこなすのは難しいからKnative等の複雑さを抽象化してくれるツールが出てきている
  • Kubernetesはやはりコミュニティとして勢いがありみんな知りたい。でも実際に活用しているのは、まだ一部の優秀なエンジニアが揃った企業が多く、その使い方も1チームでやるというよりは基盤を作る側とサービス開発が別れていて、その中で自律的な開発を促してスピードを緩めないためのツールとしてk8sをうまく使っている様子。 キーノートのLINEさんの2000人の開発者が自由に開発するようなシステムを開発していて、そのバックエンドにk8sを使っているという話の規模に度肝を抜かれた。
  • コネヒトはコンテナオーケストレーションツールとしてAmazon ECSを使っていて、Kubernetesに切り替えたら圧倒的なメリット得られるかなみたいなことが想像出来てなくて、何人かコミュニティ界隈の方に聞いてみたがその答えは今の所はなさそう。
  • マルチクラウドやハイブリットクラウドだったり、開発環境から本番までだったりを全て同じ環境で動かすような話であればやはりKubernetes

知らなかった技術のことをキャッチアップ出来たり、他の会社の事例を知れたりでカンファレンス参加はとても刺激的でした。 色々と考えさせられる部分もあり楽しい2日間でした。運営の皆様ありがとうございました。

来年からは Cloud Native Days という名前に変わり、コンテナだけでなくCloudNativeなアプリケーションやその基盤を含めたもっと大きなイベントになるそうです。 今から開催が楽しみです。

また、コネヒトではコンテナ周りの基盤を整備したりサービス改善を行うエンジニアを募集しています。 少しでも興味もたれた方はDMでもWantedly上からでも良いのでご連絡いただけるとうれしいです。

AWSとDockerで600万人の生活を支えるインフラエンジニア募集! - コネヒト株式会社のインフラエンジニア中途の求人 - Wantedly

明日は、 @kichikuchiによる記事です!お楽しみに!

社内LTを発起してから、開催30回を迎えました。

こんにちは!師走の2日目ということで、コネヒト Advent Calendar 2018 - QiitaのDay-2です。
何が嬉しいってドリフターズの新刊が出たことです・・・

ドリフターズ 6 (ヤングキングコミックス)

サーバーサイドやっています、金城(@o0h_)です。今回の話は、not技術です。

コネヒトでは、昼休みを使って「社内LT会」を定期開催しています。先月で30回を迎え、個人的には「よく続いているのかな〜」と感じているところです。
そこで、Advent Calendarに乗じてちょっと何かを書いてみよう!という目論見です。

続きを読む

1人でも多くCfP応募してもらうために取り組んだこと

f:id:tommy_kw:20181127210323p:plain

初めに

本記事はコネヒト Advent Calendar 2018の1日目のエントリーです!

qiita.com

こんにちは!Androidエンジニアの富田です。先日DroidKaigi 2019のCfP応募をして、残念ながら不採択だったのですが、実は裏目標を設定していました。それは社内メンバーの1人でも多くCfP応募してもらうことです。今回はその取り組み内容を共有したいと思います。

なぜCfP応募して欲しいのか?

コネヒトではデザイナー、エンジニア含めて13名いるのですが、デザイナー、エンジニア向けカンファレンスのCfP応募経験者は3割くらいしかいません。僕自身も初めてCfP応募したのが去年のDroidKaigiです。それまでは、「本当に出していいんだろうか?」と興味はあったのですが、なかなかCfP応募まで至りませんでした。この悔しい経験からCfP応募ってもっと身近なもので、チームとして協力し合うことで少しでもCfP応募経験者を増やしたい。延いてはCfPが採択されることによって、有名なカンファレンスに登壇し、プレゼンス向上に繋げたいという狙いがありました。

取り組んだこと

それでは取り組んだことについて紹介しますが、「よし、支援したい!」と思い立った時、実際に何を支援すればいいんだろうと悩みました。僕はAndroidエンジニアでちょうどDroidKaigi 2019のCfP応募がスタートしていたので、これをきっかけに巻き込んで行く形式に決めました。具体的には以下の項目について取り組みました。

  • 1人登壇ではなく複数登壇を選択
  • 周りを巻き込みやすいテーマ選定
  • 目的やスケジュールの共有とドキュメント化
  • 登壇メンバーだけでなく、社内メンバーからもフィードバックをもらう
  • 関連テーマの勉強会を週1回開催

1人登壇ではなく複数登壇を選択

僕の場合は「僕なんかが...」、「登壇緊張する...」と言った心理コストが高く一歩踏み出すのが難しかったです。そのため心理コストを下げるにはどうすればいいんだろうと考えた時に、全て1人で準備するのではなく複数で登壇すればいいのでは?と考えました。DroidKaigi 2019でも少数ではありますが、複数登壇する方がいらっしゃり、複数人でやることによって相乗効果やコスト配分を調整できるメリットがあると思います。

周りを巻き込みやすいテーマ選定

DroidKaigi 2019の募集要項には多くのカテゴリがあり、下記からカテゴリを選択します。

  • xR
  • セキュリティ
  • UI・デザイン
  • アプリアーキテクチャ
  • ハードウェア
  • Androidプラットフォーム
  • 保守・運用・テスト
  • 開発体制
  • Android FrameworkとJetpack(Support Library)
  • 開発ツール
  • クロスプラットフォーム
  • その他

得意なカテゴリを選べば良いと思うのですが、支援してもらうにはわかりやすいテーマで自分ゴト化できるテーマが良いです。今回はエンジニアだけでなく、デザイナーも巻き込みたく、「UI・デザイン」カテゴリを選択しました。カテゴリは決まりましたが、さらに具体的なテーマが必要で、最近興味を持っていたMaterial Themingを選択することによって、デザイナー2名と共にプロポーザル内容を考えていく運びとなりました。

tech.connehito.com

みなさんが使っているアプリケーションや導入した過程で使いづらいところに対してフィードバックを送ってほしい。Google Material ThemingのようにGoodpatch Material Themingがあってもよくて、自社で使うときにカスタマイズしたMaterial Themingを使うのもあり。さらにJapan Material Themingといったような、みんなでデザインシステムを作っていくようなやり方を一緒にできると良いです。

上記の記事はMaterial Themingに関する勉強会のブログで興味を持った一部を抜粋しました。 自社サービスにカスタマイズしたMaterial Themingを利用することでサービスのUI/UX向上に繋がるためこの分野を深掘っていこうと決めました。

目的やスケジュールの共有とドキュメント化

コネヒトではドキュメント管理ツールにDocbaseを使用しています。1人でも多くの人たちを巻き込むには取り組みの目的、プロポーザル草案、今後のスケジュールの見える化が大切だと思い、以下のようなドキュメントを作成しました。

f:id:tommy_kw:20181129184617p:plain

この巨大なイベントで「ママリを紹介したい!」、「コネヒトを紹介したい!」と2016年10月から胸に秘めていました...。そろそろ本気で巨大イベント登壇目指しませんか?やりましょう!数百人規模のイベントで登壇する機会って少ないと思いますし、こんな面白そうなことを一人でやるのは勿体無い!ということで今回は関根さんと一緒に複数人登壇します!

このドキュメントに関するポジティブなフィードバックも多く、誰でも状況が把握できる環境を作るのは大切だなと改めて感じました。このドキュメントを事前に共有しておいた結果、後述する全体からフィードバックをいただくフェーズでも比較的スムーズにフィードバックを頂くことができました。

登壇メンバーだけでなく、社内メンバーからもフィードバックをもらう

Androidエンジニア2名で考えたプロポーザル

f:id:tommy_kw:20181125212118p:plain

Androidエンジニア2名で考えたプロポーザルは上記になりますが、その後にデザイナー2名、さらに社内メンバーからフィードバックを頂きましたので一部紹介します。

  • ママリについて知っている人が少ないため、タイトルを「UX、サービスデザイン」に変更した方が良さそう

  • システマチックになっているので、サービスとマテリアルデザインをコラボさせたい

  • 「こうやって行きたい!」だと実現可能性が低いため、「ちゃんとやった!」やりきった内容にしたい

  • 長文だとパッと読んだ時に頭に入りづらい気がしたので、スライドの大項目になりそうな部分とかandroid開発者に響きそうな部分を抜き出して箇条書きで下に追加してあげるとわかりやすいかもなと思いました。

  • Custom Material Themingを作るためのサービスデザイン という文章の流れからデザインフローの話をするのかな?と僕は認識していたので、そしたらここはサービスデザインの一環としてCustom Material Themingを取り入れる方法みたいに主従を逆転させた方がいいかな〜と思いました!

  • 運営しているママ向けNo1アプリのママリを用いてMaterial Themingのカスタマイズ → 運営しているママ向けNo1アプリのママリを用いてMaterial Themingのカスタマイズ方法

  • お二人とも頑張って!応援くらいしかできない

プロポーザル改善後

f:id:tommy_kw:20181125212308p:plain

フィードバックを受けた結果、上記のように改善しました。単純に改善コメントをもらうだけでなく、応援コメントもいただきました。嬉しかったですし、こういう支援の仕方もあるんだなと学びでした。

関連テーマの勉強会を週1回開催

CfPを一緒に考えてくださったデザイナーメンバーと共にMaterial Themingの勉強会を週1回開催しました。この活動の目的は一緒にMaterial Themingについて学習してもらうことでより自分ゴト化するのが目的でした。以下の動画では、簡単にMaterial Designの歴史やMaterial Themingの紹介がされています。こちらの動画を見て、なぜMaterial Themingができたのか理解を深めました。

www.youtube.com

さらに、Material Themingを知るためには、ベースとなるMaterial Designについてもっと理解を深める必要があります。そのため、Material Designの基礎となる「Material System」、「Material Foundation」、「Material Guidelines」の3つの読書会を週1回開催して情報共有を行いました。

material.io

狙いの結果は?

残念ながらDroidKaigi 2019は不採択で、そもそもの目的だった「CfP応募するメンバーを増やす」に対しては、まだ新しくCfP応募しているメンバーはいません。個人でハンドリングすることが難しいのでもうちょっと様子を見たいと思います。ただ社内のメンバーからは、

  • うわあああ残念ですがナイストライでした!!みんなで勉強してたのはめっちゃいい取り組みだったと思うのでブログとかに書きましょ
  • ナイストライでした!!焦らず大胆に実績づくりですね!!!!
  • ナイストライ&分析ありがとうございます!来年はコネヒトのセッション聞きたくてたまらないようにしていきましょ!
  • 残念!これだけ要因がわかってたらそれを埋めてけばいけそう感。次回まで引き続きトライしていこうな〜

など「ナイストライ!」とポジティブな意見をもらいました。頂いたコメントの通りなのですが、この活動を継続する必要があります。

最後に

カンファレンス開催に依存してしまう問題があるので、結果についてはもうちょっと様子見をしますが、最初の一歩を踏み出せました。まだ振り返りができていないので課題の特定、整理を行い、改善してうまく登壇まで繋がればと思います。引き続き頑張っていきます!

qiita.com

明日のコネヒト Advent Calendar 2018は金城さんのターンです!お楽しみに!

AWS Fargateを本番運用した所感

こんにちは。インフラエンジニアの永井(shnagai)です。

今回は、Fargateを本番投入し1ヶ月強が過ぎたので、運用する中で気付いた点について書こうと思います。

以前書いた、Fargateに関する調査のまとめ記事はこちら。

tech.connehito.com

内容はざっくり下記3項目です。

  • いきなりFargateはハードルが高め
  • 良かった点
    • コンテナのリソースキャパシティを簡単に変更出来る
    • オートスケーリングもシンプルに組める
    • 安定運用
  • つらい点
    • タスクの起動速度がEC2バックエンドと比べるとやはり遅い
    • 料金面

いきなりFargateはハードルが高め

Fargate導入を通して一番感じたのは、新規にコンテナ化するアプリケーションをECSで動かす場合、EC2バックエンドで試験をパス出来る状態まで持っていった後に、最後にFargateで動かすパターンがよさそうということです。

今回のFargateの本番投入では、 EC2で動いているサービスをDockerコンテナ化して新たにFargateでリリースするというものでした。 他のサービスでも同じような作業をしていたので、 Dockerコンテナ化やアプリケーション改修については比較的スムーズに終わりました。

サービス公開前に試験用ECSをFargateで作り、コンテナをデプロイして試験を始めたのですが色々と困りごとが起こりました。 アプリケーションがうまく動かず下記のような会話が繰り広げられました。

  • 環境変数がうまく読み込めていないのでは?
  • NW通信がうまく出来ていないのでは?
  • ちょっと1コマンド叩いて検証したい。

docker exec でコンテナに入りデバッグ出来たらすぐに解決出来るような問題ばかりなのですが、Fargateはコンテナに入りデバッグが出来ないのがネックになりました。 初期の環境構築では、どうしても意図せぬ設定漏れ等が出てくることはあり、それを潰すための試験だったのですが、デバッグに時間がかかり非効率になってしまいました。

結局EC2バックエンドで試験環境を作り、試験をパスした状態でFargate用にタスク定義を作り直すというステップを踏むことにしました。

もちろん標準出力は、CloudWatchLogsにはかれるのである程度はデバッグ出来るのですが、NWの問題やOSレイヤのデバッグは少し厳しいなというのが正直な感想です。 本番運用後は、コンテナにログインするような事はないので特に問題ないのですが、開発フェーズではEC2バックエンドでdocker execでデバッグ出来る状態にしたほうが、効率よく作業出来ると思いました。

良かった点

ここからは、Fargateを導入してよかった点について書いていきます。

コンテナのリソースキャパシティを簡単に変更出来る

Fargateの最大のメリットだなと個人的に感じた点で、 これまで意識しなければならなかったクラスタを構成するEC2側のリソースキャパシティを考慮せずに、 タスク単位で自由に割当リソースの変更が可能となります。

※現状だと1コンテナあたり Memory:0.5〜30GB vCPU 0.25〜4の制限あり

下記図をみていただくとわかりやすいのですが、EC2バックエンドとFargateではコンテナに対するリソース割り当ての考え方が根本的に違います。

f:id:nagais:20181121150313j:plain

何が嬉しいかというと、負荷試験で決めたキャパシティが実際にサービスインしてみると不足していたり逆に少し過剰気味というようなケースやサービスが爆発的に成長してリソース増強が必要になるようなケースで、コンテナに割り当てるリソースの見直しを行うステップが劇的に簡単になります。

【EC2バックエンド時代】

  1. EC2バックエンドの実態である、AutoScallingGroup(ASG)でインスタンスタイプや台数を変更すると同時にECSサービス側で新しいASGでもコンテナが動くように起動数を調整
  2. ASGの変更内容を元に計算し、タスク定義にて1コンテナあたりに割り当てるリソース量を変更
  3. 新たなタスク定義でECSサービスを更新して反映

1のステップが特に面倒で、リソースを縮退する時には、コンテナがリソース不足で落ちてしまわないように注意を払う必要がありました。

これがFargateになると下記のように簡単なステップでリソースの変更が可能になります

【Fargate】

  1. タスク定義で、コンテナに割り当てるリソース量を変更
  2. 新たなタスク定義でECSサービス更新

クラスタ側のリソースは考えずに、コンテナに割り当てたいリソース量を変更するだけでOKとなります。

今後、Fargateに関してはリソース容量の増加や様々なアップデートがあるのではと予想していますが、タスク定義を変更するだけで柔軟に設定が変えられるのは大きなメリットだなと思いました。

オートスケーリングもシンプルに組める

オートスケーリングについても前述のEC2を意識しないキャパシティプランニングが可能なことと同じ理由で、 シンプルにコンテナあたりのCPU、MemoryもしくはELBでカウントするリクエスト数のしきい値ベースで設定が可能なので設定が簡単になります。

安定運用

1ヶ月強本番運用している中で、Fargate起因でサービスが落ちたことはないので基盤として安定しているのはとても安心出来ます。 ECSのオートヒーリングがあるので、仮に1コンテナ落ちたとしてもサービス影響は限定的ではあるのですが。

つらい点

今回一部のサービスをFargate化しましたが、全部を入れ替えるのはまだ先だなと感じたつらいポイントについても書こうと思います。

タスクの起動速度がEC2バックエンドと比べるとやはり遅い

Fargateにするとホストが固定されないため、Docker Pull時のレイヤキャッシュが効かなくなります。 それが理由でEC2バックエンド時代と比べるとECSサービス更新からコンテナが立ち上がるまでの時間が約1.5〜2分程遅くなりました。 その分デプロイ速度が遅くなってしまいます。

これはDockerイメージのサイズも大きく影響するので一概には言えないですが、アーキテクチャ上仕方ないとは思いつつ、AWSの今後の進化に期待したい部分だと思っています。 Cron的な起動時間が厳格に求められるスケジュールバッチのバックエンドには、FargateでなくEC2バックエンドを使うのが今のところは正解かなと個人的には思っています。

料金面

スポットインスタンスやリザーブドインスタンス(RI)を使って、EC2バックエンドでは大幅なコスト削減が可能です。 同じリソースを使った際のオンデマンド料金でも、Fargateの価格設定は少し割高かつRIのような料金プランもないので現状コスト比較をすると分が悪いです。

これは、来週開催されるre:invent かその後かはわからないですが、そのうちRDSのRIのように柔軟なリソース変更に自動適用する形でRIが発表されるのではと期待しています。

まとめ

先日、パラメータストアに置いた秘匿変数をタスク定義で環境変数に展開する機能がリリースされたり、 ECSでのDockerコンテナ運用は、Dockerイメージ+タスク定義で簡単に出来る世界が徐々に整備されつつあるなと感じる今日このごろでした。

dev.classmethod.jp

コネヒトでは、一緒に新たな技術を導入しながらサービスを育てていけるエンジニアを募集しています。 少しでも興味もたれた方はご連絡ください。

AWSとDockerで600万人の生活を支えるインフラエンジニア募集! - コネヒト株式会社のインフラエンジニア中途の求人 - Wantedly