Cybozu Inside Out | サイボウズエンジニアのブログ

 

RxSwift で多重実行防止と実行中の表現を簡潔に書く

こんにちは。モバイル開発チームに所属している小島です。

Rx を使うとコードをシンプルに書くことができるので好きです。今回はある処理(API呼び出しなどを想定)を同時に複数回実行しないような制限を実現する Extension を考えてみたので紹介したいと思います。

ActivityIndicator

RxSwift のサンプルコードに ActvityIndicator というものがあります。 UIKit に入っている UIActivityIndicator とは全く関係ないので紛らわしいです😢

それはさておき、こいつは何をしてくれるかというと Observable を関連付けると内部のカウンタを +1 して、その Observable が dispose されると -1 してくれます。つまりある Observable が実行状態であれば ON で、終了(Disposeされている)状態であれば OFF というのを表現できます。これを使って実現したいと思います。

Observable を拡張して、flatMap(withLock:) を追加

こんな便利なクラスがあれば、あとはそこまで難しくありません。

extension ObservableType {
    func flatMap<O>(withLock: ActivityIndicator, _ selector: @escaping (Self.E) throws -> O) -> RxSwift.Observable<O.E> where O : ObservableConvertibleType {
        return self
            .withLatestFrom(withLock) { input, loading in
                return (input, loading)
            }
            .filter { (input, loading) in
                return !loading
            }
            .flatMap({ (input, loading) -> RxSwift.Observable<O.E> in
                return (try! selector(input)).trackActivity(withLock)
            })
    }

Observable に対して、flatMap(withLock:) という関数を拡張します。これは何をやっているかというと、先の ActivityIndicator を引数にとって、ActivityIndicator が実行状態であれば処理をスキップし、終了状態であれば引数に渡したクロージャーを実行します。クロージャーは Observable を返すことになっていて、ActivityIndicator と関連付けをします。

こうすることで、返ってきた Observable が実行状態であれば flatMap(withLock:) を呼び出した場合には処理がスキップされます。

サンプルコード

文章で書いても伝わりづらいと思うので、サンプルコードも作ってみました。

このサンプルコードでは、doLongtimeTask という関数が定義されていて、処理が終わったら Complete する Observable を返します。(あと、確認のためにコールされたときにラベルにカウントを表示しています)

    private func doLongtimeTask() -> Observable<String> {
        self.callCount += 1
        self.callCountLabel.text = "`doLongtimeTask` called \(self.callCount) times."

        let asyncSubject = AsyncSubject<String>()

        let time = DispatchTime.now() + 5.0
        DispatchQueue.main.asyncAfter(deadline: time) {
            let result = "Complete! Endtime is \(Date().description)"
            asyncSubject.onNext(result)
            asyncSubject.onCompleted()
        }

        return asyncSubject.asObservable()
    }

また、ボタンタップ時の処理は以下のようにしています。flatMap(withLock:) に ActivityIndicator を渡すことで、doLongtimeTask がすでに実行中の場合は処理を実行しないようにできます。

        self.lockObject = ActivityIndicator()

        self.executeButton.rx.tap
            .flatMap(withLock: self.lockObject, { [weak self] _ in
                return self?.doLongtimeTask() ?? Observable.error(MyError.assertion)
            })
            .subscribe(onNext: { [weak self] result in
                self?.alert(message: result)
            })
            .disposed(by: self.disposeBag)

更に嬉しいのは、ActivityIndicator は現在実行中のものがあるかどうかを asObservable で取り出せますので、以下のように実行中に UIActivityIndicator (スピナーのほう!) のアニメーションを ON にするといったことも簡単にできます。

        self.lockState = self.lockObject.asObservable()

        self.lockState
            .bind(to: self.progressIndicator.rx.isAnimating)
            .disposed(by: self.disposeBag)

こんな感じでできます

EXECUTE ボタンを何度押しても、ちゃんと doLongtimeTask が1回しか実行されないこと表しているアニメーション

EXECUTE ボタンを何度押しても、ちゃんと doLongtimeTask が1回しか実行されないことがわかると思います。また、実行時に UIActivityIndicator のアニメーションが動き、終わったら止まるといったことも実現できています。

Rx はパズルのように組み合わせて、いろんな機能を実現できるので楽しいですね。それでは。

 

OpenSTFとkintoneでモバイル端末を管理する話

こんにちは。品質保証部の園です。 モバイル端末、何台持っていますか? 管理面倒ですよね!!
積年の問題を解決すべく、OpenSTFkintoneを組み合わせて、検証用のモバイル端末数十機を管理する仕組みを作りました。はまりどころ含めてご紹介します。

OpenSTFの導入

弊社が抱えていた問題点

製品の試験に使う端末を物理的に貸し出していました。その数ざっと100台ほど。iPhone、iPad、各種Android端末、フィーチャーフォンなどなど、OSのバージョンや画面サイズが異なるものが大量にあります。これらはkintoneで作成した「モバイル端末管理アプリ」を用いて、目当ての端末の利用状況を確認し、運よく空いていれば利用登録、管理棚から持ち出して作業する。という流れです。

この管理方法では、以下のような問題が発生していました。

  • アプリ上は利用されていないはずの端末が管理棚に存在しない
  • いつの間にか破損・紛失している
  • 試験をしたい社員が勤務しているオフィスに、目的の端末がない

最初のふたつだけでも面倒なのですが、複数の拠点をまたいでいるプロジェクトでは3番目の問題も厄介でした。エミュレータで済ませられる場合にはそうしていたのですが、どうしても実機での検証が必要な場合、新たに端末を購入したり、海外拠点へ持っていったりしていました。私自身、ベトナムや上海への出張時に試験用の端末を持参したことが度々あります。

OpenSTFとは?

CyberAgent社が作成したモバイル端末管理用ソフトです。現在、OSSとして開発が進められています。

メインの機能は「モバイル端末の管理」と「接続されているモバイル端末の遠隔操作」です。 例えばMicrosoft SurfaceのようにタッチセンサーのついたPCから使用することで、Webブラウザ上に表示されているモバイル端末を指で操作することができるため、実機に近い感覚で操作できます。

OpenSTFの導入効果

OpenSTFを導入することで、前述の問題がほぼ解決できました。 利用者側からは「端末をロッカーの中から探す手間が省けた」という喜びの声があがった他、PCからの操作(「キーボードが使える」「試験仕様書からのコピペが可能」)が好評でした。 定期的に端末の棚卸(資産管理をするため)を行っていた管理責任者も、手間が減って喜んでくれました。端末を専用のラックにいれてOpenSTFに接続したので、行方不明な端末の捜索や破損事案が減ったのです。

一方、海外拠点からの利用についても、速度的な不満は出ませんでした。私も上海オフィスやベトナムオフィスに出張した際に、東京オフィスにある端末を操作してみましたが、特別遅延は感じられませんでした。 ただし、この感想はあくまで手動でテストした際の感想です。 Seleniumなどを使って自動でテストをする場合は、Selenium(Node)をOpenSTFを設置した拠点に配置するなどして、なるべく遅延をなくす工夫をするべきでしょう。

OpenSTFの導入にともなって実施したこと

このようにOpenSTFの導入効果は高かったのですが、運用開始にたどり着くまでには、いくつかの問題に対処する必要がありました。

f:id:cybozuinsideout:20181218152854p:plain
システム構成図

LDAP認証の設定

新しいシステムを作る度に新しいユーザー・パスワードの作成を求めていては、ユーザーの利便性を損ない、システムの利用が促進されません。
…というのは半分建前で、ユーザー管理は手間が増えるので、やりたくありません。そこで、OpenSTFのユーザー認証を社内のActive Directoryに連携させることにしました。

LDAP認証を利用するために、OpenSTFの認証サーバーを以下のように設定します。

stf auth-ldap --app-url https://${PUBLIC_IP}/
--port 3000
-t 60
--ldap-url ldap://${LDAP Server}:${LDAP Port}
--ldap-bind-dn "cn=${CN},ou=${OU},dc=${DC},dc=${DC}"
--ldap-bind-credentials ${AD_PWD}
--ldap-search-dn "dc=${Search-DC}"
--ldap-search-field "cn"
--ldap-username-field

通信のSSL化

作成したOpenSTF環境への接続は、社内ネットワークからに限定しています。 とはいえLDAP認証を使用しており、パスワードが暗号化していない経路を通るのは望ましくありません。 以下の2点を実施して、通信をSSL化しました。

  • OpenSTFが使うNginxのSSL化
  • OpenSTFの起動時に付与するオプションを、HTTPS通信、WSS通信に変更

stf app --auth-url http://${PUBLIC_IP}/auth/ldap/
--websocket-url ws://${PUBLIC_IP}/
--port 3000
 ↓ 以下のように変更 ↓
stf app --auth-url https://${PUBLIC_IP}/auth/ldap/
--websocket-url wss://${PUBLIC_IP}/
--port 3000

端末管理用のラック関連

モバイル端末は専用のラックに収納し、施錠して管理することにしました。

f:id:cybozuinsideout:20181218151242j:plain
ラックに格納された端末

ラック内にはハブを配置し、ケーブルに繋がれた端末が数十台鎮座しています。一時期ニュースをにぎわしていたような、過充電による爆発炎上は無いと思いますが、機材の異変は素早く察知し、対処したいものです。 異常発熱への対策として、端末/ラック監視を強化し、充電しない時間帯を設けることとしました。

端末の監視

OpenSTFは、端末のバッテリー温度を常に取得しています。
この情報は、OpenSTFのAPIにより外部プログラムから取得可能なので、端末の温度の急激な変化を察知して通知するようなプログラムを構築することが可能です。

ラック全体の温度監視

運用の都合上、モバイル端末が格納されたラックは、サーバールームではなく、執務スペースに配置されています。ラック自体が閉塞された環境になりやすい構造のため、オフィスの暖房と合わせて温度が上昇してしまうことを懸念しました。対策として、USBファンによる送風や、USB温度計による監視を実施しています。

f:id:cybozuinsideout:20181218151340j:plain
ラック背面側
USBファンはPCケース用の120mmを4つ配置。このあたりは静音PCを組んだ経験が生きています。

充電時間の調整

とはいえ、監視していても24時間体制でトラブルに即対処できるわけではありません。弊社の場合、実機での作業は人間による確認作業の割合が高いため、業務時間外の利用はほとんどありません。そこで、稼働がなく対処できない時間帯については端末の充電を止め、トラブルの要因を減らす方針にしました。

OpenSTFが構築されているサーバーと端末は、電源供給型のUSBハブで接続されています。端末の充電時間の調整は、このUSBハブの電源部分に時刻/曜日で電源供給のON/OFFができるタイマーを挟むことで実現しました。これにより、深夜や休日にトラブルが発生する可能性を低減しています。

モバイル端末の利用予約をしたい!

OpenSTFを導入したことで、モバイル端末の管理・利用効率が格段に向上しました。しかし、運用していくと、「端末の予約をしたい」という要望が出てきました。

従来の運用では kintone上の「モバイル端末管理アプリ」を用いて「この期間はこのモバイル端末を占有します」という『予約』が可能でした。プロジェクトの試験期間にあわせて試験用の端末を確保しておくイメージです。OpenSTFには、そういう期間指定の端末管理機能はありません。実際にOpenSTFへアクセスしたら別の人が目当ての端末を使っていた、という取り合いは避けたいものです。

必要なのは、

  • kintoneアプリから予約情報を取得して
  • (予約時間に)該当の端末が利用中の場合は、端末の利用を終了させ
  • 予約者が利用している状態にする

という動作です。

kintoneとOpenSTFを連携しよう

幸い、OpenSTFとkintoneには、共に様々なAPIが用意されています。
このAPIを使い、kintoneに登録された予約期間に、確実に予約した端末を利用できる状態を作ります。

1) kintoneアプリ上の予約情報を確認する

kintoneのAPI「レコード一括取得」を使用し、kintoneのレコード情報を取得することができます。このAPIを使用して、kintone上に作った「モバイル端末管理アプリ」から予約情報を取得します。

f:id:cybozuinsideout:20181218151844p:plain
予約情報の取得

2) 予約情報がある場合、該当の端末が利用されていないか確認する

OpenSTFのAPI「GET /devices/{serial}」を使用し、端末のSERIAL_IDを指定して情報を取得することができます。このAPIで情報を取得すると、該当の端末の利用状況がわかります。

f:id:cybozuinsideout:20181218151833p:plain
端末の利用状況確認

3) 利用されている場合は、強制的に利用を中断する

OpenSTFのAPI「DELETE /user/devices/{serial}」を使用し、強制切断を行います。
注意点として、このAPIに付与するAPI-TOKENは利用者のAPI-TOKENにしないと切断することができません。そこで、ログイン情報をもとにLDAPサーバーからメールアドレスを取得し、それをキーとしてOpenSTFのrethincDBからAPI-TOKENを取得します。

f:id:cybozuinsideout:20181218151823p:plain
未予約者を強制切断

4) 該当の端末を予約者が利用している状態に変更する

OpenSTFのAPI「POST /user/devices」を使用し、利用者を強制的に設定します。
こちらのAPIも、(3)と同様に予約者のAPI-TOKENを使用して実行する必要があります。

f:id:cybozuinsideout:20181218151805p:plain
予約者の利用状態にする

終わりに

OpenSTFを導入することで、管理しているモバイル端末の半数をラックに格納し、利用者・管理者双方にメリットを提供することができました。ソフトウェアだけではなく、ラックやセンサーなどを含めてうまく運用に乗せられたので、個人的にも満足度が高いです。 今のところOpenSTFはiOS端末を管理できないので、従来の貸し出し方式とハイブリッドな運用になってしまっていますが、そのあたりは今後のアップデートに期待しています。今後は端末の利用状況を分析し、効率の良い端末の破棄/追加も視野に入れていきたいですね。

 

Kubernetesのネットワークプラグインを自作した話

こんにちは、インフラ刷新プロジェクト「Neco」の@ueokandeです。 本日はインフラ刷新プロジェクトで開発したKubernetesネットワークプラグインについてお話します。 Necoでは現在のアーキテクチャを刷新して、機材管理やアプリケーションのデプロイを簡単にするために、オンプレミス環境でKuberneteを運用できる仕組みづくりに取り組んでます。 その成果物の1つである、Kubernetesネットワークプラグイン「Coil」についてご紹介します。

github.com

Kubernetesは管理者がプラグインを選択・作成することで、実行環境や要件に合わせて自由にカスタマイズできます。 ネットワークプラグインもコンテナの実行環境に合わせてカスタマイズ可能です。 サイボウズが自社データセンターでKubernetesを運用するために、Kubernetesネットワークプラグインを作成しました。 この記事では、Coilのアーキテクチャと、なぜ自社開発に至ったかの経緯をお話しします。

Kubernetesのネットワークの仕組み

コンテナを docker run コマンドで起動するとき、DockerはコンテナにDockerのプライベートネットワークからIPアドレスを割り当てます。 このネットワークはノード上に閉じたネットワークで、異なるノード上のコンテナ間は通信できません。 そのためPodが適切なネットワーク設定やIPアドレスを利用できるよう、Kubernetes管理者は環境に応じて適切なネットワークを設定します。

Kubernetesのネットワークを設定する仕組みは、ネットワークプラグインという形で実現されます。 Kubernetes管理者は適切なネットワークプラグインを各ノードにインストールして、kubeletがそれを利用するように設定します。 ネットワークプラグインはContainer Network Interface (CNI)という仕様に基づいており、CNIプラグインとも呼ばれています。

CNIプラグインは実行可能なバイナリです。 環境変数や標準入出力からコンテナの情報を受け取り、結果を標準出力へ返します。 Podがデプロイされてkubeletがコンテナを起動するとき、CNIプラグインを実行します。 CNIプラグインは渡されるコンテナIDやネットワーク名前空間をもとに、適切なネットワーク設定を適用したり、割り当てるIPアドレスを返します。

なぜネットワークプラグインを開発したか

Kubernetesのネットワークは、性能やスケーラビリティを考慮して、各Podが持つIPアドレスはBGPで経路制御することが多いです。 新しいインフラでも、各ノードやPodはすべてBGPによって経路を制御します。 ネットワーク設計やノード側の設定は以下の記事からどうぞ。

BGPが扱えるCNIプラグインはCalicoRomanaなどすでにいくつか存在します。 特にCalicoは多くの企業で採用実績があります。 これらのプラグイン導入も視野に入れてましたが、調査してみて採用に至りませんでした。 これらのプラグインはBGPスピーカーを組み込んでいます。 そしてNecoのネットワークでは、それぞれのノードも自身のIPアドレスを広告してます。 この2つを併用するとき、BGPでは問題が発生します。

それぞれのBGPスピーカーが利用しているBFDという障害検知の仕組みは、隣接するネットワーク機器とピアリングします。 現状それぞれのノードがToRスイッチとピアリングしているため、CNIプラグインがToRスイッチとピアリングできません。 ノード側とCNIプラグインそれぞれの経路を広告するには、片方のBGPスピーカーが両方の経路を広告するような実装が必要です。 これをCalicoまたはRomanaで実装するのは、メンテナンス性などの理由で将来性がないので、最終的にCNIプラグインを開発するという結果に至りました。

Coilの設計方針

CoilがやることはコンテナのIPアドレス管理とネットワークへ経路を適用するのみです。 CNIプラグインは「プラグインチェーン」という仕組みがあり、複数のプラグインを組合せることができます。 ネットワークポリシーの設定などはCoilはやらず、CalicoやCiliumなど他のプラグインを利用する予定です。

Coilの大きな特徴の1つは、BGPスピーカーを組み込まない という点にあります。 Unix哲学には「1つのプログラムは、1つのことをうまくやる」という言葉あります。 Coilはコンテナのアドレスをネットワークに広告せず、別のソフトウェアが経路広告できるように経路情報を外部に出力するだけです。 Necoのネットワークではすでに各ノードにBIRDがインストールされており、ノードのIPアドレスをネットワークに広告しています。 Coilは自身が経路広告せずに、各ノードにインストールされているBIRDがコンテナのアドレスをネットワークに広告できるようにしました。

Coilのもう1つの大きな方針は、アドレス単位の経路広告ではなく、サブネット単位の経路広告という点です。 これはRomanaの設計からインスパイアされました。 CalicoはPodのアドレス広告を/32のアドレスとして広告します。 つまりPodの数だけ経路情報がネットワークに広告されます。 Necoでは将来数十万のPodをデプロイすることを想定してるため、パフォーマンス面で心配がありました。 一方Coilは各Podが持つアドレス単位ではなく、ひとかたまりのサブネット(/27など)として経路広告します。 この単位をCoilではブロックと呼んでいます。 経路広告をブロック単位にすることで、ネットワーク全体の経路数が大きく減ります。 たとえばブロックのサブネットマスクが/27とすると、アドレス単位の経路広告と比べて広告する経路数は1/32に抑えることができます。

ブロックの確保はノードに対して割り当てられ、Coilはノードに割り当てられたブロックからIPアドレスをPodに割り当てます。 Coilはブロックの経路広告と、ノード内のPodの2つの経路を設定します。 前者の経路はネットワークに広告され、どのPodのアドレス(のサブネット)をどのノードが持ってるかを知ることができます。 後者はノード内からPodに到達するための経路です。 この2つの経路を設定するとこで、異なるノードにデプロイされたコンテナの経路を知ることができます。

Coilのアーキテクチャ

BIRDがどの経路を広告すべきかを見れるよう、Coilは割り当てられたブロックを指定されたLinuxのルーティングテーブルに出力します。 Linuxカーネルはシステムが利用するルーティングテーブルのほかに、複数のルーティングテーブルを持っています。 これはLinuxカーネルがネットワーク経路に使用しないので、通常のアプリケーションは影響を受けません。 BIRDはこのルーティングテーブルから経路情報を参照し、ネットワーク全体に広告します。 つまりBIRDに限らずこのルーティングテーブルを参照できるなら、quaggaなどの他のソフトウェアと組合せて利用できるようになっています。

Coilのアーキテクチャの図が以下のようになります。ブロックやIPアドレスの割り当て情報はetcdに保存します。 各ノードはCNIプラグインの coil と、coilのデーモン coild がデプロイされます。 CNIプラグイン coil は、kubeletから呼び出して coild に問い合わせるのみです。 ブロックやアドレスの割り当てをするのは coildです。 coild はetcdを参照して、新しいIPアドレスをコンテナに割り当てたり、ブロックが必要なら新たにブロックをノードに割り当てます。 新しいブロックを割り当てたら、それをBGPによって経路広告するためにルーティングテーブルに出力します。

f:id:cybozuinsideout:20181218164058p:plain
Coilのアーキテクチャ

各Podに経路を割り当てるまでのフローは以下の通りです。

  1. Podがデプロイされると、KubeletがCNIプラグインのcoilを呼び出します。
  2. coilcoildにPodの名前空間を渡して、新しいIPアドレスを要求します。
  3. coildは現在のブロック割り当て情報から、利用可能なIPアドレスを探します。 もし割り当てたブロック内で利用可能なアドレスがなければ、新たなブロックを割り当てます。
    • 新たなブロックを割り当てたら、そのブロックをルーティングテーブルに登録します。
    • BIRDは新しいブロックがルーティングテーブルに追加されたら、ネットワークに経路広告します
  4. 割り当て可能なIPアドレスがあると coildcoil に結果を返します。
  5. coil はコンテナのIPアドレスとノード内の経路を設定します。

Coilはアドレスプールと、Podが属するKubernetes名前空間を紐づけます。 Kubernetesの名前空間の認可を設定することで、特定の権限があるユーザのみに、特定のIPアドレスを割り当てるよう設定できます。 たとえばサイボウズでは利用者全員がグローバルIPを利用できないよう、プライベートIPとグローバルIPを別の名前空間に割り当てます。 そして特定の権限があるユーザーのみ、グローバルIPアドレスを持つPodをデプロイできます。

まとめ

Coilの開発状況は、機能は一通り実装が完了しており、これから本番環境に投入予定です。 ネットワークプラグインの開発は新しいチャレンジでしたが、すでにチーム内ではBGPネットワークの知見が溜まってたのでスムーズに開発が進みました。 またCoilの責務を最小限にして、多くの機能を実装しなかったのがカギです。 おかげでBIRD以外のルーティングソフトウェアからも利用できる設計になりました。

サイボウズでは自社でKubernetesを運用して、そのうえでサイボウズのプロダクトを運用できる環境を開発しています。 他にもまだまだ紹介してないソフトウェアがあるのですが、それはまた別の機会に紹介したいと思います。

 

社内ハッカソンを開催しました

f:id:cybozuinsideout:20181102154637j:plain

こんにちは!開発部の刈川です。 サイボウズでは毎年社内でハッカソンを開催しているのですが、先日開催したので簡単に内容をご紹介したいと思います!

概要

f:id:cybozuinsideout:20181102155726p:plain

今年は東京、松山、大阪、ベトナムの4拠点と各々の自宅(在宅勤務)を含めた55名の方に参加してもらいました。 期間は3日間でテーマは自由となっています。3日目に最終発表会を行い、その中から投票で4つの賞を決めました。 賞は以下のとおりです。

  • 大賞
  • 技術(チャレンジ)賞
  • アイデア賞
  • デザイン賞

成果物をいくつかご紹介します。

Terraform-provider-kintone(大賞)

大賞はTerraform-provider-kintoneでした。これはTerraformというインフラ構成管理ツールのkintone用のプラグインです。 嬉しいポイントをざっくりいうと『いままで画面をポチポチやって作るしかなかったkintoneのアプリケーションをコードベースで管理/デプロイすることができる』ということです。kintoneもterraformも知らない方には「?」という感じですが、これができるようになると様々なことが自動化できて、kintoneが更に便利になるのです。 www.terraform.io

ぷよぷよのプレイ動画を解析して棋譜を生成(技術賞)

f:id:cybozuinsideout:20181130163241p:plain
©SEGA
その名の通り、ぷよぷよのプレイ動画から棋譜を生成して分析するためのツールです。 予め用意したパターン画像からぷよの画像の差分をとって色を判定したり、ツモった時や連鎖が発生したタイミングをグラフで分析したりと技術の無駄遣い感がものすごいのですが、いかにもハッカソンらしいチャレンジが評価されました。
f:id:cybozuinsideout:20181130163731p:plain
ぷよぷよってこんなゲームなの…?

kintone-VR

kintone-VRは、Oculus Goとkintoneを連携してVR空間にkintone上のコメントを描画するシステムです。 ハッカソンの成果発表では実況スレッドを利用しており、デモでは実際にこのシステムを見た反響のコメントがVR空間に描画されていました。

Oculus Goでわいわい

開催にあたって

社内ハッカソンは毎年開催しているのですが、年ごとにテーマを設けています。 たとえば「コネクト」がテーマの回では、普段の業務では関わりのない人同士を運営側でランダムにチーム分けしてハッカソンに臨んでもらいました。こうすることで今まで知らなかったノウハウの共有や、その人がどういう技術が得意だということが分かったりと、ハッカソンの成果物以外の副産物としてのメリットも生まれます。

参加者同士でランチ        在宅でハッカソンしてる人

おわりに

最近サイボウズではモブプログラミングが流行しているので、今度は合宿形式×モブプログラミングで企画をする予定です。 開催したらこちらでご紹介したいと思います。それではまた!

 

KubeCon + CloudNativeCon North America 2018 現地レポート 3日目

こんにちは、Necoプロジェクトのsatです。カンファレンス最終日、3日目のレポートです。

0日目から2日目のレポートはこちらです。

blog.cybozu.io

blog.cybozu.io

blog.cybozu.io

2日目までは執筆者各人がその日に参加したセッションの中からいくつか面白かったものを選んで書くというスタイルでしたが、最終日の今日は少し毛色を変えて1日目と3日目に合計4つのセッションがあったRookというソフトウェアを集中的に紹介したいと思います。Rookとは分散ストレージソフトウェアの一つであるCephをkubernetesによって管理するためのオーケストレーターです。

Cephについてご存じないかたは、本記事を見る前にCephの概要について述べた以下の記事をごらんください。

blog.cybozu.io

Intro: Rook - Jared Watts, Upbound

これはRookの基礎について紹介するセッションでした。

Rookは前述の通りkubernetesを使ってCephを管理するソフトウェアです。RookはまたCNCFのincubating level(孵化段階の)プロジェクトでもあります。

github.com

kubernetesはボリュームプラグインという仕組みによってアプリケーションに提供する永続ストレージのプロバイダを追加できます。Cephもそのバックエンドの一つとして利用できます。ただし、そのためにはユーザが自分自身でCephクラスタを構築、管理する必要があります。kubernetesを拡張してこれらの面倒事を自動管理してくれるソフトウェアがRookです。

f:id:cybozuinsideout:20181214141409j:plain

Rookが提供する機能は単なるクラスタを自動構築、管理だけではありません。Cephは通常クラスタを管理するためにユーザがコマンドを発行する必要があります。これに対してRookではkubernetesが扱う各種リソースと同様にCephクラスタを宣言的に扱えます。例えばCephクラスタを定義するyamlファイルにモニタの数を3つと書いておけば、モニタの数が3つであるクラスタを自動的に構築し、かつ、モニタのうちの一つがダウンしたとしても自動的にモニタの数が3に戻るようにRookがよきにはからってくれます。この宣言的な管理を自前で実装するのではなくkubernetesに任せるというのがRookの巧みなところです。

f:id:cybozuinsideout:20181214141413j:plain

セッションの後半にはRookを使って実際にCephクラスタを構築するデモがありました。Cephのクラスタを幾度となく構築した経験がある筆者から見ると、「なんて楽なんだ」というのが率直な感想です。kubernetesに短いyamlファイルを投入するだけでモニタやOSDなどのCephの各コンポーネントがPodとして次々に立ち上り、次第にCephクラスタが出来上がってゆく様は圧巻でした。

Deep Dive: Rook - Travis Nielsen, Red Hat

このセッションは前述の"Intro: Rook"に比べてRookのさらに詳細について扱うものでした。

前半はRookにおけるCephの各種リソースの定義についての話でした。ここではそのうちの一部について紹介いたします。

  • Cephクラスタを示すリソース: 指定されたバージョンのCephを使ってクラスタを作る。この定義を変更すればCephを自動的にアップグレードしてくれる。Rookのバージョンも同様に管理できる
  • OSDを示すリソース: どのノードのどのストレージデバイスをOSDとして提供するかを定義できる。それに加えてストレージデバイスが並列アクセスが高速なNVMe SSDだった場合に、デバイスごとに複数のOSDを定義してI/Oを高速化させられる

今後もいろいろ定義できる項目が増えていくことでしょう。

後半にはいくつかのデモがありました。たとえばRookによってCephの一つ前の安定版(コードネームはLuminous)から最新の安定版(コードネームはmimic)にアップグレードするといったものがありました。クラスタを定義するyamlファイルの中のCephのバージョンに相当する部分を一か所書き換えるだけでアップグレードができるというのは筆者にとっては驚きでした。

Adding a New Storage Provider to Rook - Jared Watts, Upbound

このセッションはRookに新しいストレージプロバイダを追加する方法についてのものでした。

ここまでCephの話ばかりしていましたが、実はRookはCeph以外のストレージソフトウェアも永続ストレージのプロバイダとして使えるようにするためのフレームワークを持っています。すでに現在NFS, Cassandra, CockroachDBなどをサポートしています。ソースコードを見たところ、現在のRookの主たるプロバイダはあくまでCephであり、残りのストレージソフトウェアは一部の機能しか実装していない評価版のような位置づけのようです。たとえばストレージプロバイダのAPIのバージョンはCephが先月末に唯一最初の安定版といえるv1になった以外はalpha版扱いです。

本セッションでは既に取り込まれているMinioのプロバイダを例に、プロバイダを追加するにはkubernetesにどのようなリソースを定義してどのようなコードを書けばいいかということを紹介していました。個人的にはCeph以外のプロバイダを使うことは無いと思いますが、開発全体が活発で、かつ、個々のプロバイダがRook本体と独立して開発できるのはいいことだと思います。

Meet the Maintainer: Rook - Jared Watts, Upbound

これはRookに興味のある参加者がRookのメンテナに直接質問できるセッションです。

筆者は本イベントに参加する前にRookについて興味を持っていたため、その動向を追ってきました。それに加えて各種Rookのセッションを見たことによって、いくつかの疑問が湧きました。せっかくなので本セッションにおいてそれらについてメンテナに質問してみました。

  • Q: 現在RookはRADOSオブジェクトのレプリカをそれぞれ同じノードに置かないように設定できるが、それ以外の設定はできない。私が作ろうとしているCephクラスタにおいては各レプリカを別ラックに置きたいと思っている。直近でこのような設定をできるようにする予定はあるか
  • A: 今後しばらくはRookによってクラスタを組んだ後にRookを介さずに直接Cephを操作することによってそのようなことを実現するしかない。将来的にはサポートするかもしれない。

  • Q: 最新版のv0.9までは毎回のように非互換が発生していた。これではプロダクション環境でRookを使いにくい。今後しばらくはこのような状況が続くと考えてよいか

  • A: 今後は非互換を発生させないようにする見込み。もちろんRookに閉じた話ではなくCephに非互換が発生した場合はこの限りではない

  • Q: Cephには大量のリバランス処理をきっかけとしてネットワークの帯域を使い果たし、一切クラスタにアクセスできなくなるという問題が起こりうる。実際そのようなケースをこれまでにいくつか見てきた。この問題に対処するための帯域制御機能をRookに実装する予定はあるか

  • A: 今のところはそのような帯域制御はユーザ自身がするしかない。Rookがそのような仕組みを用意することはやろうと思えばできるだろうが、そのような機能を追加する予定は今のところ無い

知りたいことにすべて丁寧に回答してもらえたので非常に満足できたセッションでした。

ここで余談をひとつ。本イベントは多くのセッションについてスライドが公開されていたり、ものによっては発表時の動画が公開されることすらあります。それでも現地に行く価値はどこにあるのでしょうか。それは上記のような質疑を含む参加者同士の対話です。みなさまもこの手のイベントに参加する際はご自身の持つ課題についてメンテナを含む他の参加者と議論してみてください。イベントがもっと楽しくなりますよ。

Rookについての現在の印象

Rookは宣言的にCephクラスタを扱えること、および、そのために既にある程度成熟しているkubernetesを利用しているという2つの点において、なかなか筋がいいソフトウェアに見えます。現在はまだ粗削りですが、Red Hat社やQuantum社などの企業のエンジニアやホビープログラマなどの多様な人々によって活発に開発が進んでいることより、将来的には「kubernetesでCephを使うのならばRookを使うのが常識」という状況になっても不思議ではありません。今後も引き続き開発動向を追っていく予定です。

KubeCon全体の総評

とにかく人数が多くて熱のこもったイベントだったというのが全体の印象です。筆者はこれまでOpen Source SummitなどのOSSに関する国際イベントにいくつも出てきましたが、八千人もの大人数が参加する規模ものはかつて無かったです。世間のコンテナ熱、kubernetes熱を見るに、次回以降もますます盛り上がっていくことでしょう。

もちろん我々は単に驚いて圧倒されていたわけではなく、得るものがたくさんありました。ここではそのうちの1つを紹介いたします。我々はkubernetesの最新、かつ深い知識を得るために本イベントにやってきました。ところが、幸か不幸かレベルが高いと記載されているセッションに参加しても既に知っている内容が多かったです。このため、次回以降は参加するだけではなく、自分たちのセッションを持つことによって我々の知見をkubernetesのコミュニティに共有することが一つの目標になりました。

これまで4日にわたってKubeCon + CloudNativeCon North America 2018の様子をお届けしてまいりましたが、これにてすべて終了です。ここまで読んでくださった方に感謝いたします。我々のレポートがみなさんにとって何かしら役に立つものであれば幸いです。