Quantcast
Channel: Cybozu Inside Out | サイボウズエンジニアのブログ
Viewing all 689 articles
Browse latest View live

MySQL のレプリケーションから10年間逃げてきた我々が学んだこと8選

$
0
0

こんにちは。クラウド運用チームで SRE をしている飯塚です。

今回は、MySQL のレプリケーション機能を約10年もの間ずっと使ってこなかった私たちが、レプリケーションを使った高可用性構成に移行するための取り組みの中で学んだことについて紹介します。

背景

当社が提供している cybozu.com では、お客さまのデータ保護の観点から、これまでデータロストのリスク回避の面でメリットが大きい共有ストレージ方式で MySQL の高可用性構成を構築していました。これは、サービス提供開始当時の MySQL は準同期レプリケーションのようなレプリケーションにおけるデータロスト回避のための仕組みが未成熟であると判断していたためです。詳細については以下の資料をご覧ください。

speakerdeck.com

一方で、共有ストレージ方式では VM を収容するホストサーバーの故障時にある程度のダウンタイムが発生することが避けられないといった問題や、ホストサーバーの停止を伴う作業が発生した場合にサービスの停止を伴うメンテナンスが必要になってしまうという問題がありました。このような問題を解決するために、cybozu.com の次世代アーキテクチャではレプリケーションを使った高可用性構成に移行することを計画しています。

この1年間はレプリケーションを使った構成に円滑に移行できるように準備を進めてきましたが、10年もの間レプリケーションを使ってこなかった私たちにとっては、いくつかのインスタンスで実験的にレプリケーションを運用してみるだけでも多数の落とし穴がありました。また、レプリケーション構成での動作に支障が無いように MySQL の使い方を変えていくのも単純な作業ではありませんでした。なぜなら、これまでの設計の中に意図せずレプリケーションに不適切な使い方をしてしまっている箇所がたくさん見つかったためです。

この記事では私たちが対応に苦慮した問題を中心に取り組みから得た学びを紹介します。内訳はスキーマの変更に関するものが1件、バイナリログに関するものが1件、mysqldump の使い方に関するものが5件、レプリケーションの運用中に発生した問題に関するものが1件です。

なお、以降の説明では Oracle MySQL Team での用語変更を踏まえて「マスター」や「プライマリ」と呼ばれていたものを「ソース」、「スレーブ」や「セカンダリ」と呼ばれていたものを「レプリカ」と表記します。

巨大なテーブルへの primary key の付与

行ベースレプリケーションを使用する場合には全てのテーブルに primary key または non-null unique key(ドキュメント上では primary key equivalentと呼称)を付けることが推奨されています。これは パフォーマンス上の問題を回避するためです。この問題を防ぐために MySQL 8.0.13 以降では sql_require_primary_keyにより primary key の付与を必須とするように設定することもできるようになりました。

これまでレプリケーションを使用してこなかったため、当社が提供しているサービス群には primary key が付与されていないテーブルが大量にあり、これを是正することが最初の取り組みでした。大半のテーブルについては大きな手間なく修正することができたのですが、中には数億行のレコードを含むテーブルがあり、単純な方法では primary key を付与できないものがありました。

既存のテーブルに primary key を付与する最も単純な方法として AUTO_INCREMENT付きのカラムをサロゲートキーとして追加することが考えられます。しかし AUTO_INCREMENT 付きのカラムの追加は並行して DML の実行ができない、すなわちサービス停止が必要になるという点が問題になります。事前の検証により、月次のメンテナンスウィンドウを超えた許容できないダウンタイムが発生することが分かっていたため、この方法は採用できませんでした。

技術顧問の @yoku0825さんに相談したところ、UUID()をサロゲートキーとして利用するテクニックを教えていただきました。AUTO_INCREMENTを追加する DDL は DML をブロックしますが、カラムを追加する DDL やカラムを primary key として設定する DDL は DML をブロックしないため、AUTO_INCREMENTに代わるサロゲートキーが用意できれば DML をブロックせずに primary key を追加することができます。MySQL の UUID()で生成される UUID v1 はタイムスタンプの巻き戻りが起きないことなどのいくつかの仮定のもとでは重複が起きないことが保証できるため、私たちの用途ではサロゲートキーとして利用できました。

一般的に、この方法で primary key を追加する手順は以下のようになります。

  1. UUID を追加する nullable なカラムを追加する
  2. テーブルに INSERT するときに UUID のカラムに UUID()を設定するような TRIGGER を作成する(TRIGGER を使わずに、アプリケーションから INSERT するときに UUID()を設定してもよい)
  3. NULL が入っている既存のレコードに小刻みに UUID()を設定していく
  4. NULL が入っているレコードが無くなったら primary key を設定する

3 の既存レコードへの UUID()の設定は少し面倒ですが、私たちが問題としていたテーブルはお客様操作の監査ログを保存するテーブルで、都合のよい性質を持っていました。

ログインなどの履歴などが記録されています。
cybozu.com のお客様操作の監査ログの例

このテーブルは追記専用で、6週間経過すると CSV ファイルに書き出されて MySQL からは削除されるという仕様になっています。すなわち、新しく INSERT されるレコードに UUID()が設定されるようにさえしておけば、6週間後には NULL が入っているレコードは無くなってしまうのです。

この方法により、私たちは巨大なテーブルへの primary key の付与を無停止で完了することができました。巨大なテーブルに変更を加える場合のテクニックについては以下の記事もご覧ください。

blog.cybozu.io

トランザクションサイズが大きい場合には tmpdirに注意

tmpdirは様々な状況で一時ファイルを書き出すのに使われる領域で、MySQL のデータを保存する領域とは別のディスクを指定することで負荷分散することもできるようになっています。私たちの環境ではこれまでデータを保存している共有ストレージではなく、VM を収容するホストサーバーのローカルディスクから切り出された領域を tmpdirに指定していました。各 VM に割り当てられた領域は VM のフェイルオーバーの仕組みの都合上、数十 GiB といった小さい値になっています。

レプリケーションのためのバイナリログの出力を有効にしていなかったこれまでは問題なく動作していたのですが、バイナリログの出力を有効にした途端、いくつかのインスタンスでローカルディスクの容量警告が届くようになってしまいました。

原因はバイナリログの出力を有効にしたことで tmpdirに書き出されるデータの種類が増えてしまったことです。バイナリログを書き出すとき、トランザクションの大きさが binlog_cache_sizeで指定したサイズよりも大きい場合には tmpdirで指定したディレクトリに一時的にトランザクションを書き出すようになっています。当社が提供しているサービスの中にはかなり巨大なトランザクションを生成するものがあり、その影響で tmpdirに指定したローカルディスクの容量が圧迫されるようになってしまいました。

対策としては

  1. tmpdirを容量が大きな共有ストレージ上に移す
  2. ローカルディスクの容量を増やす
  3. 巨大なトランザクションを生成しないようにアプリケーションを改修する

などの方法が考えられます。MySQL の再起動は必要なものの、1 は最も手軽に変更できる方法です。しかし、本来は必要なかった共有ストレージの I/O 負荷が増えてしまうという問題があります。2 や 3 は性能面では望ましい方法と言えますが、それなりの対応コストが必要となります。私たちは苦渋の選択として 1 の方法を選びましたが、将来的に性能面での問題が現れた場合には再考が必要だと考えています。

最近の MySQL にはファイルやディレクトリを指定するオプションが多数存在しています。振り返ってみると、バイナリログの出力を有効にするといった影響の大きい設定変更を加える場合にはファイルの出力先のディスク容量にどのような変化があるかという観点についても事前によく調査するべきであったと考えています。

mysqldump で絵文字が消えていないか要チェック

皆様は utf8mb4 への移行は終わっているでしょうか? MySQL に UTF-8 の文字列を格納するときの文字コードには utf8 と utf8mb4 の2種類があり、utf8 のカラムに絵文字のような4バイト以上の UTF-8 文字列を格納した場合にはその部分が保存できないという問題が起こります。ほとんどの場合は utf8 と utf8mb4 で迷うことなく utf8mb4 を選択することになると思います。

油断しがちなのですが、テーブルの定義だけではなくコマンドラインツールにも utf8mb4 を設定するべき箇所が多数あります。mysqldump には default-character-setというオプションがありますが、デフォルト値は utf8であり、絵文字などを正しくコピーすることができません。以下は「私は🍣と🍺が大好きです」という文字列を含むテーブルを mysqldump した例ですが、デフォルト設定では 🍣 と 🍺 が ? になってしまっているのが分かります。

$ mysqldump --compact --default-character-set=utf8mb4 d1
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `t1` (
  `c1` varchar(256) COLLATE utf8mb4_unicode_ci DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
INSERT INTO `t1` VALUES ('私は🍣と🍺が大好きです');
$
$ mysqldump --compact d1
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `t1` (
  `c1` varchar(256) COLLATE utf8mb4_unicode_ci DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
INSERT INTO `t1` VALUES ('私は?と?が大好きです');
$

この問題はテストデータに意図して絵文字を入れるなどの準備なしでは気づきにくく、知らない間にデータが欠損していたという問題が起こりかねません。レプリケーションの運用経験がなかった我々は、当初この問題に気づいていませんでした。ある程度の量のデータを含むテスト環境のレプリカを作成しようとしたときに、絵文字を含むカラムが unique key に含まれていたことで Duplicate entry が発生して発覚しました。

mysqldump に付けるべきオプションは以下の資料を参照するのが確実です。

MySQL :: MySQL 8.0入門セミナー講演資料 (レプリケーション編)

mysqldump が Error 1412: Table definition has changed... で失敗する

これは mysqldump に --single-transactionを付けた一貫性のある読み取り中に特定の DDL を実行したときに発生する問題です。詳細は以下のドキュメントを参照してください。

MySQL :: MySQL 5.7 Reference Manual :: 14.7.2.3 Consistent Nonlocking Reads

当社のように B2B SaaS の形態でサービスを提供している環境では、テナントごとに MySQL の DATABASE を区切るように設計していることがあります。この設計には様々なメリットがある一方で、契約や解約によって利用環境が作成、削除されるたびに MySQL の DATABASE の作成、削除が行われるなど、日常的に DDL が実行されやすい状態にあるという点はレプリケーションの運用と相性が良くありません。

この問題についてはレプリケーション構成に本格的に移行する前に

  • アプリケーションではユーザーの操作起因での DDL が実行されないようにする
  • テナントに契約、解約に伴う DDL の実行タイミングは mysqldump の状態と歩調を合わせる
  • パーティショニングに関係する操作の実行タイミングを調整する

といった改善を続けていく必要があると考えています。

mysqldump したデータのリストアが Duplicate entry 'xxx-yyy-PRIMARY-n_diff_pfx01' for key 'PRIMARY'で失敗することがある

mysqldump したデータをレプリカ側でリストアしたとき、標記のエラーによりリストアに失敗することがありました。

これは Bug #71814で報告されていた不具合でした。mysqldump のオプションに --all-databasesを付けて全てのテーブルをレプリカにコピーしようとすると、オプティマイザで使用される統計情報が記録されたテーブルである mysql.innodb_index_statsをリストアするときに以下のようなレースコンディションが発生することがあります。

  1. mysql.innodb_index_statsのリストア前にテーブルを初期化する
  2. mysql.innodb_index_statsへのデータの INSERT を開始する
  3. innodb_stats_auto_recalcによって統計情報が再計算される
  4. mysql.innodb_index_statsへのデータの INSERT を完了する

ここで innodb_stats_auto_recalcによって統計情報の再計算が完了したあとに mysql.innodb_index_statsにデータを INSERT しようとすると Duplicate entry により INSERT に失敗します。

対処方法としては mysqldump のオプションに --ignore-table=mysql.innodb_index_statsを付与することで mysql.innodb_index_statsをレプリカにコピーしないようにしました。

この問題は MySQL 8.0.1 以降の mysqldump では修正済みです。

mysqldump したデータのリストア時のディスク枯渇には要注意

ソースの故障時にレプリカが昇格することを見据えて、レプリカの設定でもバイナリログやスロークエリログは有効にしておくことが多いかと思います。しかしながら、mysqldump したデータをレプリカでリストアするときだけは注意が必要です。リストア中に巨大なバイナリログやスロークエリログが生成されてディスク容量を圧迫することがあるためです。mysqldump の前後で設定を変更して、リストア時にはバイナリログやスロークエリログを生成しないように設定することをお勧めします。

バイナリログの設定は MySQL の再起動なしでは適用できないため、別のパラメータも一緒に変更するよい機会です。たとえば innodb_flush_log_at_trx_commit = 0skip-innodb_doublewriteは InnoDB のデータ保護機構を無効化する設定であり、通常の運用での利用は推奨されませんが、リストアの間だけ高速化のため利用することは許容できるかもしれません。リストア中の障害でレプリカのデータが破損してしまった場合には、レプリカを最初から作り直せばよいからです。レプリカに MySQL 8.0.21 以降を使用する場合には ALTER INSTANCE DISABLE INNODB REDO_LOGを使うことも検討できます。

mysqldump でユーザー情報もコピーした場合は FLUSH PRIVILEGESが必要

手動で実行することはほぼ無いと思いますが、CREATE USERなどを使わずに mysql.userなどのテーブルを書き換えることによってユーザー情報を書き換えた場合には FLUSH PRIVILEGESを実行して変更を MySQL に伝える必要があります

気を付ける必要があるのは mysqldump のオプションに --all-databasesを付けて mysql.userなどのテーブルをコピーした場合です。この場合は mysql.userなどのテーブルの書き換えが発生するため FLUSH PRIVILEGESが必要な条件に合致します。これを忘れるとレプリカ側で不可解なエラーが多数発生します。

前述のようにリストアの前後で MySQL の設定を変更するために再起動を行っている場合には、再起動後に mysql.userなどのテーブルからユーザー情報が読み込まれるため FLUSH PRIVILEGESを実行する必要はありません。

unknown error reading log event on the master でレプリケーションが停止したとき

実験的に本番環境のいくつかの MySQL インスタンスでレプリケーションを開始して数か月経ったある日、あるレプリカでレプリケーションが突然停止しました。

IO スレッドのエラーには以下のようなメッセージが記録されていました。

Got fatal error 1236 from master when reading data from binary log: 'unknown error reading log event on the master; the first event 'mysql-bin.001471' at 216402234, the last event read from './mysql-bin.002208' at 2049, the last byte read from './mysql-bin.002208' at 2049.'

このエラーに関しては下記の資料が参考になりました。

speakerdeck.com

資料に記載の再現条件を踏まえて同様の事象であると判断しました。判断に使った基準は以下になります。

  • ソース側の MySQL が 5.7.25 より古いバージョンであり、不具合を含むバージョンであること
  • 提供しているサービスの特性から、問題の発生条件となる DDL などの操作が頻繁に行われていること

結果として、バイナリログを手動でローテーションさせてから START SLAVEすることでレプリケーションを再開することができました。

おわりに

この記事ではレプリケーションを使った高可用性構成に MySQL を移行するための取り組みの中で学んだことについて紹介しました。

マネージドサービスが広く使われている現代において MySQL をレプリケーションなしで運用しているサービスが当社以外でどれほどあるか不明なため、役に立つ内容があるかどうか分からないですが、読者の皆様のお役に立てるものがあれば幸いです。

サイボウズのクラウドサービスでは、機材故障に伴うダウンタイムやサービス停止を伴うメンテナンスによってご迷惑をおかけしているお客様もいます。クラウド運用チームではより高い可用性をもつクラウドサービスを提供するため、今後も改善に取り組んでいきます。


Kubernetes 用 CNI プラグイン Coil v2 の紹介

$
0
0

Neco プロジェクトの ymmtです。

サイボウズでは 2018 年から Kubernetes 用のネットワークプラグイン Coil を開発しています。 当時は Kubernetes の知識がチームに蓄積されておらず、いささか使い勝手が悪い仕様でした。

そこで 3 か月ほど前から設計を全面的に見直した Coil v2 の開発を開始し、先日リリースしました。 Coil v2 は多くの方にご利用いただけると思いますので、本記事にて機能と使い方を紹介します。

特徴

Coil v2 は以下のような特徴を備えた CNI プラグインです。 CNI とは、Kubernetes で使われているネットワークプラグインの規格名です。

  • 高速な Pod 間通信
  • Namespace 毎に指定できる複数の IP アドレスプール
  • IPv4/v6 シングルスタックおよびデュアルスタック
  • 任意のルーティングソフトウェアと連携可能
  • オプトイン方式で外部ネットワークへの NAT 接続を提供
  • 設定操作が kubectlで可能

以下、順を追って解説していきます。

高速な Pod 間通信

Coil は後述する 外部ネットワークへの NAT 接続機能を除き、オーバーレイネットワークを使いません。 Pod 間のパケットは加工されずに物理ネットワークを流れるため余分なオーバーヘッドはなく高速です。

Namespace 毎に指定できる複数の IP アドレスプール

大規模なデータセンターは、複数のインターネットを含む外部ネットワークへの接続を持っていることがあります。 IPv4 でネットワークを構成する場合に顕著なのですが、外部ネットワークに接続可能(ルータブル)な IP アドレスは少数しかなく、通常の Pod が持つ IP アドレスでは直接外部ネットワークにアクセスできないことがあります。

このような場合、ルータブルなアドレスを特定のノードや Pod に割り当て、ゲートウェイサーバーとして使うことで Pod に外部ネットワークへのアクセスを提供することが可能です。

Coil ではこのようなゲートウェイとなる Pod を特定の namespace で作れるようにする機能があります。 以下はインターネットに接続可能な HTTP proxy の Pod を作成する例です。

1: 外部ネットワークに接続可能なアドレスを、AddressPoolというカスタムリソースで定義

apiVersion: coil.cybozu.com/v2
kind: AddressPool
metadata:name: internet
spec:blockSizeBits:0subnets:- ipv4: 103.79.??.???/28

2: Namespace にアノテーションでどの AdressPool を使うか指定

$ kubectl create ns internet-egress
$ kubectl annotate ns internet-egress coil.cybozu.com/pool=internet

3: 当該 Namespace に proxy 用の Deployment と Service を作成

apiVersion: apps/v1
kind: Deployment
metadata:namespace: internet-egress
  name: squid
spec:
  ...
---apiVersion: v1
kind: Service
metadata:namespace: internet-egress
  name: proxy
spec:
  ...

これで proxy.internet-egress.svcという DNS 名でインターネットに接続できるプロキシが Kubernetes クラスタ内で利用できるようになります。

IPv4/v6 シングルスタックおよびデュアルスタック

IPv4, IPv6 のシングルスタック構成に加え、デュアルスタック構成もサポートしています。 設定は、AddressPoolのアドレスに IPv4, IPv6 を片方ないし両方指定するだけです。

IPv4 と IPv6 で使える機能に差は基本的にありません。注意点として、Kubernetes のデュアルスタック対応はまだアルファなのでそれに起因する機能制限はあります。

任意のルーティングソフトウェアと連携可能

Coil は他のネットワークソフトウェアと連携することを前提に設計されました。 例えば MetalLBで LoadBalancer を実装し、CalicoCiliumで NetworkPolicy を実装するといった具合です。

これらのソフトウェアを組み合わせる際に障害になりがちなのが、BGP 機能です。BGP については以前記事を書いたので詳しくない方はそちらをご覧ください。

blog.cybozu.io

MetalLB や Calico は自力で BGP を話すのですが、同一のノードから複数のソフトウェアが BGP でルーターとピアすることはできないので複雑な回避策が必要になってきます。Neco ではノード上ですでに BIRD が動作して BGP でルーターと接続しているため、さらに構成に無理が生じます。

そこで、Coil は自力ではどのルーティングプロトコルも話さない設計としました。 代わりに経路広告するべき経路を Linux カーネルの使っていないルーティングテーブル(デフォルトでは 119 番)に登録します。

BIRD などは Linux カーネルのルーティングテーブルを読み取ることが可能なので、このように間接的に連携することで BGP の問題を回避することができます。また BIRD 以外のルーティングソフトウェアとも連携できますし、BGP 以外のプロトコルで経路広告することも可能になります。

この設計の代償は、手軽に動かすことができなくなる点です。Coil v1 では必ず他のルーティングソフトウェアと連携しなければ、異なるノード間での Pod 通信が動きませんでした。Coil v2 では、すべてのノードが同一の L2 ネットワーク(サブネット)にある場合に限りますが、付属の coil-routerというソフトウェアを併用すれば自力でノード間のルーティングが可能です。

オプトイン方式で外部ネットワークへの NAT 接続を提供

Coil v2 の目玉となる機能です。先ほど HTTP proxy をゲートウェイ Pod として動作させる例を出しましたが、これでは HTTP/HTTPS 以外で外部と通信ができません。例えば外部にある MySQL と接続するとか、DNS サーバーに問い合わせるといった操作ができないわけです。

より汎用的なゲートウェイとしてはやはり NAT サーバーを配置したくなります。Coil では、以下のように Egressというカスタムリソースを定義することで、当該 Namespace に自動的に NAT サーバーを構築してくれます。

apiVersion: coil.cybozu.com/v2
kind: Egress
metadata:namespace: internet-egress
  name: nat
spec:destinations:- 0.0.0.0/0
  replicas:2

この定義から、NAT 用の Pod を 2 つ作る Deploymentと、リクエストを負荷分散する Serviceが自動的に作られます。

この NAT サーバーを利用する Pod は、以下のようにアノテーションをつける必要があります。 Egressを定義したら自動的にパケットがインターネットに届くようになるわけではないので、必要な Pod にのみ選択的に NAT サーバーを利用させることが可能です。

apiVersion: v1
kind: Pod
metadata:namespace: default
  name: nat-client
  annotations:egress.coil.cybozu.com/internet-egress: nat
spec:
  ...

Egressはデフォルトゲートウェイとしてではなく、特定の外部ネットワークに対して作成することもできます。 どの Egressを利用するか Pod が使い分けることで、自在に通信を制御できます。

設定操作が kubectlで可能

AddressPoolEgressはカスタムリソースなので kubectlで作成・編集できます。 また、AddressPoolからどのようにアドレスブロックがノードに割り当てられているかも簡単に確認できます。

$ kubectl get addressblocks.coil.cybozu.com  | head
NAME                NODE          POOL              IPV4               IPV6
default-0           10.69.0.4     default           10.64.0.0/27
default-1           10.69.0.203   default           10.64.0.32/27
default-10          10.69.0.5     default           10.64.1.64/27
default-11          10.69.1.141   default           10.64.1.96/27
...

使い方

Coil は GitHub で OSS として公開されています。

github.com

kind で試す

kindとは、Docker 上で Kubernetes を動かすことができるツールです。 Coil は kind で動作させることができるので、簡単に試せます。

しっかりインストールして使う

以下のドキュメントを用意してあります。BIRD との連携方法も記載しています。

v1 から v2 へのオンラインマイグレーション

サイボウズの Kubernetes クラスタは Coil v1 で 2 年ほど動作していましたが、すでに v2 への移行が完了しています。 すでにプロダクションワークロードが稼働しているため、移行作業はオンラインで行う必要がありました。

ここではどのようにオンラインマイグレーションを実現したか、解説します。

基本的なアイデア

Coil v1 はデータを etcd で管理し、v2 は kube-apiserver のカスタムリソースで管理しています。 そのためデータのコンバートが必要なのですが、Pod の IP アドレス管理を v2 では各ノードのカーネルに与えた情報で管理するように方式を変更したため、すべてのデータをコンバートすることはできません。

そこで、Coil v1 が利用しているアドレスブロックは、Coil v2 では利用できない予約済みアドレスブロックとしてコンバートすることにしました。こうすることで、v1 の IP アドレス管理情報は無視して v2 で新規にアドレスブロックを払い出し、Pod を動作させられます。

Coil v1 で動いていた Pod がすべてなくなったら、予約済みアドレスブロックを削除すれば v1 の痕跡を残さず v2 に移行できたということになります。

移行ステップとツール

  1. Coil v1 のプログラムを停止

    DaemonSet と Deployment を削除することで、新規に Pod が作られなくなります。 また、etcd のデータが変更されなくなりコンバートを開始できるようになります。 既存の Pod の通信が止まったりはしません。

  2. etcd のデータをカスタムリソースにコンバート

    この際、v1 が使っているアドレスブロックは予約済みアドレスブロックにします。

  3. Coil v2 のカスタムリソースを定義

  4. 2 のデータを読み込み
  5. Coil v2 のプログラムを動作開始

    この時点から、Pod の作成が再開されます。新しく作られた Pod は v2 のアドレスブロックで管理されます。

  6. すべての hostNetwork=falseな Pod を削除

    この操作で Coil v1 の Pod はクラスタから消えます。 Deployment や DaemonSet コントローラーが作り直した Pod は v2 で動作しています。

  7. 予約済みアドレスブロックを削除

  8. coildを再起動

    予約済みアドレスブロックは特殊な存在で、それが削除されたことに気づかせるため coildを再起動します。

この移行ステップの大半は、coil-migratorというツールで自動化しました。 具体的に 1, 2, 6, 7, 8 が自動化されています。

トラブル

実は、当初 8 のステップの必要性に気づかずにいました。結果、v1 時代のアドレスブロックが間違ったノードから経路広報され続けるという障害を起こしてしまいました。

コンバートツールは後付けで作ったので、既存のプログラムの想定外の状況を作り出してしまったわけです。最後に再起動するのが安全でした。

Kubernetes Meetup Tokyo #35 の資料

2020年10月28日に開催された Kubernetes Meetup Tokyo #35で Coil v2 を紹介しました。その際に使った資料が以下です。Kubernetes のネットワーク機能をどのように実現するかを丁寧に解説していますので、よろしければこちらもご覧ください。

余談: v1 と v2 は何が違うのか

Coil v2 は v1 のコードを再利用せず、全部新規に書き起こしたのですが、そうなった背景を説明します。 Coil v1 は以下の仕様で 2018 年に作成されました。

  • データは etcd に保存

    インストール時に etcd にユーザーを作ったり認証用の証明書を発行する必要がありました。 カスタムリソースなら kube-apiserver にデータを保存できるのですが、当時はまだ作り方を知らなかったため。

  • 管理操作は専用コマンドラインツール

    etcd にデータを保存していたため。kubectlで管理ができないのは単純に不便でした。

  • 効率の悪い IP 管理機能

    これは実装が悪いのですが、Pod のアドレスをすべて etcd で集中管理していて etcd への負荷が高くなりがちでした。 各ノード内でローカルに管理することもできたわけで、Coil v2 ではそうしています。

  • 完全に機能させるために外部ルーターが必須

    Coil だけでは自力でルーティングできず、BIRD等を別途用意しないと試験環境が作れませんでした。 お手軽とは言えませんし、CI の実行に時間もかかるしで大変でした。

  • Prometheus でメトリクスがとれない

    Coil v1 を作り始めたころは Kubernetes だけでなく、Prometheus 用メトリクスの出し方も詳しくなかったため。

  • IPv6 未対応

    そもそも IPv6 に対応可能なデータモデルになっていませんでした。

それからあれこれ開発するうちに、Kubernetes のカスタムリソースやコントローラーの作り方、Prometheus メトリクスの出し方などを学習し、「今作ればこうするのに」というアイデアが溜まっていきました。 そこで Coil に外部ネットワークに NAT 接続できる機能を追加することになった際、これらの不満点もすべて解消することにしました。

データモデルから全部見直したので、結果 Coil v2 は v1 のコードを一行も引き継いでいない完全に新規の実装となった訳です。 作り直す過程でモジュラーかつテストしやすい構造にできたので、テストのカバレッジを大幅に高められたことは良い副産物でした。

まとめ

サイボウズでは Coil v2 を使うことで Kubernetes 向けの高度なネットワーク機能を実現しています。 便利に生まれ変わった Coil v2 をご利用いただければ幸いです。

2022年新卒採用 エキスパートエンジニア職のご紹介

$
0
0

サービス運用部の吉川です。今回はサービス運用部が2022年新卒採用で始めたエキスパートエンジニア職採用について、学生の視点に立ってみて説明が必要だと感じた点をQ&A形式でまとめてみました。クラウド基盤エンジニアとして働くことを検討中の方はエントリー前のチェック項目としてご活用ください。*1

Q1: 制度の概要を教えてください

A1: サイボウズのクラウド基盤エンジニアとして一年目から即戦力となって働く自信のある学生を対象にした新卒採用制度です。配属先が限定される点がキャリア採用に近い形となっています。

Q2: 対象となる職種と配属先は?

A2: 運用本部のサービス運用部に配属され、SRE、QA、Maneki Project、Neco Projectに関する業務を担当します。詳しくは募集要項の歓迎スキル欄から辿れるキャリア採用ページの説明をご覧ください。

Q3: 新しい採用制度ができた背景が知りたいです

A3: OSSの普及やインフラのソフトウェア化が進むにつれて、企業で働くエンジニアに引けを取らない経験やスキルを持つ学生も増えています。また、そういった自信を持つエンジニアから入社時の待遇について差別化を求める声もあがっています。このような事情を考慮し、具体的に応募が期待できる一部職種についてキャリア採用に近い形での採用制度を導入することになりました。

Q4: エントリーするにあたって何か条件はありますか?

A4: 学歴など形式的な条件は設けておりませんが、書類選考と採用面接でクラウド基盤エンジニアとして即戦力で働けることを示す必要があります。これまでの研究業績やクラウド基盤に関連するOSSの開発履歴など、エントリー時に何かしら具体的に提示してください。

Q5: エキスパートエンジニア職と従来のエンジニア職のどちらで応募したら良いですか?

A5: 入社後にクラウド基盤エンジニアとして働きたいという考えが明確であり、キャリア採用の場合と同様に即戦力として働くための経験・スキルを持っている方はエキスパートエンジニア職での応募をご検討ください。入社後に自分の適性について相談しながら配属先を検討したいという方は従来のエンジニア職で応募してください。後者のパスでもクラウド基盤エンジニアを志望することは可能です。

Q6: 入社後のキャリアや将来の待遇に関して特別な扱いはありますか?

A6: ありません。初年度の年収を決める際に入社時の即戦力としての期待値を考慮しますが、それ以降はどの制度で入社した人も対等に扱います。

*1:回答は記事執筆時の内容に基づいたものであり、採用状況に伴って制度詳細は変わる可能性があります。

サイボウズサマーインターン2020 Webコース開催報告 ~完全オンラインでkintoneカスタマイズ開発~

$
0
0

こんにちは、kintone開発チームの内山です。今年もインターンシップを開催しました!
今年のインターンシップは、コロナウィルスの状況を鑑みて、初の完全オンライン開催となりました。
この開催報告では、インターンシップの概要と完全オンライン開催で気をつけたこと、完全オンライン開催ならではだった問題を紹介します。

集合写真
集合写真

概要

日程としては9月7日〜9月11日と9月14日〜9月18日の2回開催し、期間中はWeb会議サービスを使って、完全オンラインで実施しました。
今年のインターンシップでは「身近なチームをサポートするkintoneカスタマイズ」という課題に取り組んでもらいました。
課題を進める際は、kintone開発チームが実際に行っているリモート・モブプログラミングという開発スタイルを体験してもらいました。

kintone開発チームのリモート・モブプログラミング については以下の記事をご覧ください。 blog.cybozu.io

kintone・kintoneカスタマイズ

インターンの課題として扱ったkintoneとkintoneカスタマイズの概要について説明します。

kintone

kintoneとはサイボウズが提供しているクラウドサービスです。
データベースアプリとコミュニケーションアプリが融合したようなサービスです。
詳しくは以下のサイトをご覧ください。

kintone.cybozu.co.jp

kintoneカスタマイズ

kintoneカスタマイズとは、JavaScriptやCSSを用いてkintoneの標準機能を拡張できる仕組みです。
例えば、カスタマイズを行うことで以下のようなことが出来ます。

  • ボタンを画面に追加し、ある特定のデータに対して独自の計算処理を実行する
  • 条件によって文字や背景の色やサイズを変更する

詳しくは以下のサイトをご覧ください。

developer.cybozu.io

大まかなスケジュール

1日目: オリエンテーション、kintoneカスタマイズのハンズオン、アイディア出し
2日目: アイディア出し、モブプログラミング見学、実装
3日目: 実装、スプリントレビュー見学
4日目: 実装
5日目: 発表準備、成果発表会、懇親会

オリエンテーションでは、インターンシップの概要やセキュリティ教育を行い、ハンズオンではkintoneの基本的な使い方やkintoneカスタマイズの方法をレクチャーしました。
その後はインターン生同士でモブのチームを作成し、アイディア出しや実装はインターン生主体で進めてもらいました。
成果発表会ではメンターや運営以外の社員も参加し、作ったカスタマイズに対して多くのレビューが集まりました。

インターン生の成果物

slacktone

Slackの投稿をkintoneアプリに転記出来るカスタマイズです。

slacktoneの投稿画面
slacktoneの投稿画面

コミュニケーションツールとしてSlackを利用しているユーザーが、複数のワークスペース間に散らばった重要な投稿を一元管理したいときに活用出来るカスタマイズです。
kintoneのアプリでSlackの投稿を管理することで、kintoneのソートや絞り込み機能を活用でき、目的の投稿を簡単に見つけられるようになります。 中継サーバーとしてAWS Lambdaを用いて、Slack上の投稿をkintoneのアプリに転記するカスタマイズを作成してもらいました。

kintone上で使えるタイマー

kintone上で使えるタイマー
kintone上で使えるタイマー(右下)

会議の際にkintone上で議事録を取りつつ、タイムキープもしたいときに活用出来るカスタマイズです。
上記のケースでは、kintoneとは別にタイマーサービスを同時に使う必要があり、複数のタブを管理しなければいけないという問題がありました。
そこで、lit-htmlとTypeScriptを使ってkintoneで使えるタイマーのカスタマイズを作成してもらいました。 kintone全体のカスタマイズとして実装したため、アプリやスペースの画面で使用することが出来ます。

f:id:cybozuinsideout:20201106164505p:plain
slacktone

完全オンライン開催で気をつけたこと

完全オンライン開催ではkintoneチームのメンバーや実際の開発時の雰囲気を感じ取ってもらえるかが懸念事項としてありました。
そこでイベントをいくつか用意しました。

  • 社員との面談
  • モブプログラミング見学
  • スプリントレビュー見学

社員との面談

話してみたい分野や技術をインターン生に質問し、その分野に詳しいサイボウズの社員と面談する時間を設けました。
興味を持っている分野やサイボウズについて雑談をしてもらい、サイボウズの社員を知ってもらえる良い機会になりました。

モブプログラミング見学

kintone開発チームがどのようにタスクを進めているかを知ってもらうため、実際のkintone開発チームのモブプログラミングを見学してもらいました。
機能を開発している様子を見てもらい、適宜メンターが背景情報などの補足説明を行いました。
時間は30分程度だったため、取り組んでいるタスクの詳細までは知ってもらうことは難しかったですが、モブプログラミングの進め方の参考になったと思います。

スプリントレビュー見学

スクラムイベントであるスプリントレビューの見学を実施しました。
kintone開発チームのスプリントレビューは毎週行われ、1週間で取り組んだ活動や成果を報告しています。
当日用意した実況スレッドには、社員やプロダクトについて理解を深めることが出来たという感想がありました。

完全オンライン開催ならではだったこと

今回の課題では、ライセンス上問題がなければ使用するライブラリや言語は自由としました。
そのため該当する場面は少なかったのですが、詰まっている状態や困っている状態を察しづらいという問題がありました。
オフィスで開催しているときであれば様子を見て声をかけることが出来ましたが、完全オンライン開催ではウェブカメラからの視覚情報のみに限られているので難しかったです。
対策として、kintone開発チームでも普段から実践しているのですが、モブのドライバーに常に画面共有してもらうようにしました。

これによって、ドライバーがどのように問題解決を行っているかを知ることができ、適宜アドバイスを挟むことが出来ました。
一方で、ナビゲーターがどのような問題解決を行っているかは分からなかったので、複数人が画面を共有できるビデオ会議ツールを使ってインターン生全員に共有してもらっても良かったかもしれません。
また、メンター側は少なくとも2人はビデオ会議に参加し、質問をいつでも受け付けられる状態にしました。
これにより、完全オンライン開催でもインターン生のサポートを行うことが出来ました。

参加者の感想

開催後アンケートで頂いた感想をいくつか転記致します。

リモートでできる最大限の範囲でサイボウズのやり方や文化を学ぶことができて嬉しかったです。学生のチーム開発や一般的な企業とも違う、サイボウズのチーム開発を学ぶことができたと思ってます。


社員さんや青野さんとの面談時間を用意していただいたり、サイボウズを知ってもらいたいという思いが伝わりました。 また、サイボウズさんには暖かい人がとても多くいて、社内でチームワークを重要視していることもひしひしと伝わりました! サイボウズさんの魅力にさらに気づけたインターンとなりました。


めちゃくちゃ楽しい一週間でした! ただコードを書くだけではなく, 一週間の間に青野さんランチや, 社員さんとの面談, 人事さんとの面談, スプリントレビュー会, Frontend Barといった社内の勉強会にも参加させていただいて, サイボウズを知ることができた1週間だったと思います!

サイボウズが遂げたい理想、「チームワーク溢れる社会を創る」が、チームワーク溢れるサイボウズに身を置くことでどういうことか肌で感じることができましたし、それは確かに実現したい理想であると強く思いました。

モブプロも確かにいいなと感じ, 今後もできればやっていきたいと感じました

今回は、完全オンライン開催したインターンシップの開催報告でした。
開催する前までは満足してもらえるか心配でしたが、コメントを見る限り完全オンライン開催でも会社や社員の雰囲気を掴んでもらえたようです。
来年度の実施方法についてはまだ検討していませんが、今回の経験をもとにしてより良いインターンシップを追求して行きます。

サイボウズサマーインターン2020 セキュリティコース 開催報告

$
0
0

こんにちは!Cy-PSIRTの純平です。

本記事は7月に行われたサマーインターンシップの開催報告です。

例年、「品質保証セキュリティコース」として開催していたセキュリティコースのインターンですが、 より対象者を明確にし、充実したコンテンツにするため今年より「品質保証コース」と「セキュリティコース」を別開催といたしました。 さらに、今年のインターンは新型コロナウイルス(COVID-19)の影響によりフルリモートで開催いたしました。 blog.cybozu.io

概要

セキュリティコースは 7/1 ~ 3の3日間、2名の学生に参加いただき、PSIRTが普段行っている業務のうち、脆弱性診断、脆弱性評価、外部からの脆弱性報告(外部通報)対応や振り返りなど体験していただきました。

PSIRTの紹介はこちらの記事をご覧ください。 blog.cybozu.io

脆弱性診断

今回はサイボウズ製品であるGaroonに対しての脆弱性診断を体験していただきました。 脆弱性診断をするには製品に対する知識を得たうえで脆弱性診断仕様書を作成する必要があるため、実際に診断をする前に、Garoonに触れていただきました。

製品への理解ができてきたところで早速脆弱性診断仕様書を作成いただきました。数年前まではExcelで作っていましたが、現在は弊社が提供するクラウドサービスkintoneで作成しています。

実際にインターン生に作ってもらった脆弱性診断仕様書
実際にインターン生に作ってもらった脆弱性診断仕様書

kintoneを用いての脆弱性診断仕様書の作成は慣れていない中、短い時間で完成度の高い仕様書が出来上がり、メンターもビックリしました!

脆弱性評価

続いては脆弱性の評価です。PSIRTでは、検出された脆弱性がどれぐらいの影響があるのかを評価するための方法の1つとしてCVSS v3を用いてスコアリングしています。インターン生の方々は実際にスコアを算出した経験はあまりないにも関わらず、普段からCVSSスコアを見る機会もあるからか、大きく困ることなく適切に評価できていました。

kintone上で評価と議論をする様子
kintone上で評価と議論をする様子

「なぜその評価になったのか」を説明できることも大切だと考えているため、評価によってはレビュー担当のメンターとコメントで議論する場面もありました。

ランチ🍖

業務体験とは離れますが、PSIRTのメンバーとインターン生でオンラインランチを実施しました。 インターン自体オンラインの開催ですが”チームを知ってもらいたい!”、”インターン生のことをもっと知りたい!”という気持ちは変わりません! チームの雰囲気を知ってもらうためにオンラインでつないでざつだんしながらランチタイムです。

ランチの様子
ランチの様子
※実際のランチ中のスクリーンショットを撮り忘れてしまったので、ランチ直後の様子です。

外部通報対応

次に外部通報の対応です。サイボウズでは報奨金制度を自社で運用しており、サイボウズ製品の脆弱性情報が寄せられています。製品理解、検証の流れ、評価方法を知っていただいたところで、それらすべての理解が必要な外部通報の対応も体験していただきました。外部から通報された報告のトリアージ、再現確認、評価の一連の流れを体験していただき、サイボウズ脆弱性報奨金制度の運営側を知っていただくことができました。

振り返り

3日間毎日振り返りを実施しました。KPT方式を採用し、その日のできごとでよかったこと(Keep)、問題点(Problem)を挙げ、次の日にどのように改善するか(Try)を共有する時間です 。

実際に登録された振り返り
実際に登録された振り返り
最終日の振り返りの様子
最終日の振り返りの様子
振り返り自体も普段PSIRTが行っている改善活動の一つです。

懇親会🍻

もちろんオンラインでの開催です!PSIRTメンバーのLT、ざつだんや感想などで盛り上がりました。 最後には恒例の完走賞もお渡しいたしました。

懇親会の様子
懇親会の様子

いただいた感想

いただいた感想をご紹介いたします。

普段個人でセキュリティをやっているとなかなか学ぶことができない、会社としてのセキュリティ向上のプロセスを知ることのできる貴重な機会でした。


リモート開催で不安でしたが、特に困ることなく作業を進めることができました。3日間でしたが、もっと体験したいと思えるインターンシップでした。

まとめ

今年はセキュリティコースの初めての単独開催でした。実際にPSIRTが普段行っている業務を体験していただきました。参加していただいたインターン生も普段はセキュリティを勉強している方々でしたが、ユーザー企業としてのセキュリティ品質向上の活動を体験していただいて得た学びを活かして今後も活躍していただけたら嬉しいです!

SLO策定とアラート設定までの長い道のり

$
0
0

こんにちは、@ueokandeです。早速ですが、皆さんが運用しているサービスには、SLO (Service-level objective: サービスレベル目標) がありますか?アラートの監視項目はどのように設定して、基準値をどのように決めていますか?

社外とのコミュニケーションだけでなく、社内向けのSLOを決めておくことで、サービスの健康状態を知るための手がかりや、普段の開発・運用タスクの優先度を決める上での指標にもなります。 またSLOがあると、サービスを監視するアラートに、理にかなった閾値を設定できます。 この記事ではAWS版kintoneの、SLOとアラートを設定するまでの記録について紹介します。

cybozu.com版kintoneのSLOとアラート

国内のcybozu.comで運用しているkintoneにも、もちろんSLOやアラートはあります。 しかし現状のSLOはkintoneが利用できるかどうかであって、性能やスループットなどの、機能面やお客様視点の指標はありません。 そして、それらに基づくアラートも設定できていませんでした。 そのため「kintoneが遅い」というお問い合わせがあっても、障害なのか否かの判断ができなかったり、適切な監視項目や閾値を設定できませんでした。

もちろん社内でも問題意識はありました。 しかし開発チームと運用チームが組織的に分かれており、SLOや性能指標に関する議論をなかなか進められなかったのが現実です。 そんな中で、新たにSLOを見直す機会ができました。 それがUS向けkintoneのAWS移行プロジェクトです。

このプロジェクトのゴールは単なるAWSへの移行だけでなく、クラウドネイティブな開発・運用体勢のための仕組みづくりも含まれます。 プロジェクトのメンバーはkintone開発チームやcybozu.com運用チームから構成し、SLO、アーキテクチャ、CI/CDパイプラインなどを見直すチャンスとなりました。

AWS版kintoneのSLOとアラート (v1)

AWS版kintoneのリリースまでにチームでSLOを設定し、SLOに基づくアラートを仕掛けました。 国内のcybozu.comでは稼働率に基づくSLOを設定しており、AWS版kintoneのSLOでも稼働率の概念を導入しました。 結論から言うと、ここで紹介するSLOやアラートはうまくいきませんでした。

SLOの設定

kintoneの主要な機能(レコード操作やアプリ管理)に対して、それぞれの機能ごとにSLOを設定しました。 SLOで稼働率を計算するために、それぞれの機能でダウンタイムを定義しました。 同期処理 (APIリクエスト) のダウンタイムは、エラーレートと応答時間をもとに定義されます。

あるリクエストRに対するダウンタイムの定義は以下のとおりです。

リクエストRが、5分ウィンドウで以下のどちらかの基準を超えれば、そのウィンドウはダウンタイムとする。

  • エラー件数がN件以上かつエラーレートがM%以上
  • 応答時間のXパーセンタイルがY秒以上

たとえばレコード操作の応答時間が著しく低下して基準値を上回ると、その5分間でレコード操作機能がダウンしたとカウントされます。 ダウンタイムの基準が決まれば、1ヶ月の稼働率がわかります。 あるリクエストRの月間稼働率は「(1ヶ月 − リクエストRの月間ダウンタイム) ÷ 1ヶ月」となります。

ここから、それぞれの機能に対して以下のSLOを設定しました。

リクエストRの月間稼働率が99.9%

アラートの設定

SLOが決まると、それに基づくアラートを設定できます。 SLOに基づくアラートは、障害によってSLOを違反する前に気づくことができます。 それぞれの機能に対してSLOに基づいたアラートを設定しました。

アラートの閾値はダウンタイムの定義を元にして設定しました。 リクエストRに対して、以下の2つのアラートを設定しました。

  • 5分ウィンドウで、エラー件数がN件以上かつエラーレートがM%以上
  • 5分ウィンドウで、応答時間のXパーセンタイルがY秒以上

5分ウィンドウでどちらかの閾値を超えると、PagerDutyに通知されてチームメンバーが緊急対応します。

結果

このSLOとアラートを、およそ半年間運用してみました。 その結果は、設定したSLOやアラートはうまく機能しませんでした。 そこで気付いたことがいくつかあります。

アラートが過敏すぎる。アラートの基準値が非常に過敏だとわかりました。 リクエスト数の少ない機能(アプリ管理機能など)では特に顕著となります。 1リクエストが遅くなっただけなのに、アラートが鳴るということがありました。 アラートは昼夜問わず鳴り、過敏過ぎるアラートはチームメンバーの健康状態にも影響します。

アラート基準の定義が曖昧になる。 SLOに対してアラートが過敏だとわかると、次にとるアクションは閾値の緩和やウィンドウ幅の拡大です。 リリース後しばらくは、アラートの調整を続けました。 それを繰り返していると、だんだんと「今の閾値はどうやって決めたんだっけ?」と、閾値の基準がわからなくなってきます。

シンプルじゃない。稼働率に基づくSLOを設定するために、ダウンタイムという概念を導入しました。 ダウンタイムや稼働率は、実際にどれくらいサービスを利用できなくなったかのコミュニケーションには便利です。 しかしその一方で、SLOの集計が複雑になったり、アラートやエラーバジェットの計算が難しくなります。

AWS版kintoneのSLOとアラート (v2)

およそ半年間運用してみて、SLOがややこしかったりアラートが過敏過ぎるというのがわかりました。 ちょうどその頃、GoogleのThe Site Reliability Workbookの邦訳版が出版され、社内でも勉強会が開催されました。 この本はSite Reliability Engineeringの続編ですが、より実践的なSLOの運用方法や、具体的なアラートの設定が紹介されています。 そこで改めてSLOを見直し、それに基づく新たなアラート基準を設けました。

SLOの設定

以前のSLOはシンプルではなく、計算が複雑になったりアラートを仕掛けにくかったという課題がありました。 新しいSLOは、kintoneの主要な機能(レコード操作やアプリ管理)に対して、エラーレートと応答時間を別々に定義しました。 あるリクエストRに対するSLOは以下のとおりです。

  • リクエストRが、リクエスト成功率が1ヶ月で99.9%
  • リクエストRが、応答時間X秒未満のリクエストが1ヶ月間でY%未満

以前のSLOのダウンタイムの概念はありませんが、定義がシンプルになりました。 そのためSLOの実績の計算や、エラーバジェットの算出が容易になっています。

SLOダッシュボードによる可視化

以前設定したSLOでは、ダッシュボードによる可視化をせずに、月末にSLOの実績を計算するのみでした。 そのため月の途中で残りのエラーバジェットを確認できず、チームメンバーもそれを気にすることがありませんでした。 The Site Reliability WorkbookのImplementing SLOsでは、SLOが決まると実績を可視化するとよいとあります。

新しいSLOに基づいて、現在どのくらい達成できているかをダッシュボード上に可視化しました。 ダッシュボード上では過去1ヶ月間のリクエスト成功率と、応答時間が基準値を上回った割合を表示します。 このダッシュボードは開発チームだけでなくPMも見れるようにして、SLOの実績を確認したりタスクの優先度付けに利用しています。

SLOの実績をダッシュボードで可視化
SLOの実績をダッシュボードで可視化

アラートの設定

新しいSLOが可視化できれば、いよいよアラートの設定です。 以前のアラートは過敏で、たとえば月間SLOを達成できそうな一時的な遅延でもアラートが鳴りました。 新しいアラートでは、The Site Reliability WorkbookのAlerting on SLOsで紹介されている、バーンレートに基づくアラートを採用しました。 バーンレートとはエラーバジェットの消費速度を表す指標です。 バーンレートに基づくアラートは、エラーバジェットが枯渇する勢いのエラーが発生したときにアラートを鳴らすことができます。

以下の図はバーンレートとエラーバジェットの消費量の関係です。 SLOの期間内(この場合は1ヶ月)でちょうどエラーバジェットを使い切るバーンレートを1とし、1ヶ月後にエラーバジェットが0になります。 バーンレート2はその2倍の速度でエラーバジェットを消費し、15日後にエラーバジェットが0になります。

バーンレートとエラーバジェットの消費量の関係
バーンレートとエラーバジェットの消費量の関係(The Site Reliability Workbook Figure 5-4より)

具体的なアラートの評価期間と閾値も紹介されています。 まずはじめは、1時間でエラーバジェットの2%を消費、または6時間でエラーバジェットの5%を消費したときに、アラートを鳴らすとよいとありました。

エラーバジェットの消費量 ウィンドウ バーンレート
2% 1時間 14.4
5% 6時間 6

たとえばリクエストRの、1時間ウィンドウのエラーレートに関するアラートの基準は以下のとおりです。

  • リクエストRの1時間のエラーレート > (14.4 * 0.001)

1時間、6時間というウィンドウ幅はかなり長いように思えますが、仮にサービスが全断 (エラーレート100%) した場合に、検知にかかる時間は1分未満です。

結果

このSLOやアラートを運用してまだ十分な期間は経過してないので、結果はまだわかりません。 これから数ヶ月は、設定したSLOが十分か、あるいはアラートの閾値が妥当か明らかになるフェーズです。 バーンレートに基づくアラートの運用実績を積むまでは、以前の“過敏な”アラートもしばらく残す予定です。

新しいSLOにより、SLOの達成率やエラーバジェットの計算がしやすくなりました。 それだけでなく、ダッシュボードによる可視化をしたことで、チームメンバー全員がSLOを意識するようになりました。

おわりに

何度かSLOやアラートを見直して、現在の形になりました。 ここに至るまでに得られた教訓はいくつかあります。

  • 誰のためのSLOか。 SLOを決める上で、誰にとってのSLOなのか議論することが多かったです。 「ユーザービリティに直結するからSLOはこのくらい」「少し遅延しても問題ないのでこのくらい」のように、 運用面だけではなく利用者視点でのSLOを考慮しました。 サービスを運用する上でSLOは必要ですが、その背景にはサービスの利用者がいるはずです。

  • シンプルであれ。 SLOの見直しによって、複雑なSLOの定義を見直すこととなりました。 複雑なSLOやアラートは計算が難しくなるだけでなく、実際のコミュニケーションでの障害にもなりました。 口頭で人に説明できるくらいのシンプルさが大事です。

  • みんなが見える場所に可視化。 SLOに限らず可視化は大事だと言うのは昔から言われていますが、今回改めて認識しました。 容易に計算できる数値であっても、それを見える場所や導線を用意しないと、誰も意識しないのがわかりました。

  • データに基づくコミュニケーション。 SLOの可視化により、メンバー全員がSLOを意識することが増えて、定期的にサービスのパフォーマンスを気にするようにもなりました。 サービスのチューニングをするときも、勘に頼るのではなくSLOを見てユーザーへの影響をデータで判断できるようになりました。

この記事では、AWS版kintoneのSLOとアラートの取り組みについて紹介しました。 皆さんの運用しているサービスにSLOがなくコミュニケーションで悶々としていたり、職人芸でアラートの閾値を設定している方の参考になればと思います。

富岳版XbyakがIntelの深層学習ライブラリoneDNNにmergeされる

$
0
0

初めに

サイボウズ・ラボの光成です。

このたび、Intelの公式深層学習ライブラリoneDNNに、富士通が開発しているスーパーコンピュータ富岳向けのPull Requestがmergeされました。 その開発に関わることになった経緯を紹介します。

目次

  • 概要
  • Xbyakとは
  • 動機
  • Intelとの関わり
  • 富士通との関わり

概要

富士通研究所はスーパーコンピュータ富岳で深層学習(ディープラーニング)を高速に処理するためのソフトウェアを開発してます。 そのためにIntelが開発している深層学習ライブラリoneDNNを富岳に移植して改良しています。 このたび、その成果の一部が本家のoneDNNに取り込まれました。

富岳はA64FXというArm v8-Aにベクトル演算機能SVEが追加されたCPUを持ちます。 oneDNNを富岳に移植するには、私が開発しているXbyakのA64FX用Xbyak_aarch64が必要でした。詳しい話は富士通研究所の方々がblogで紹介されるのでここではXbyakがどう関わっているかの紹介をします。

Xbyakとは

Xbyakとは私が14年近く前から開発しているx86/x64向けのJITアセンブラです。 C++のヘッダファイルで提供され、使い勝手としてはC++の文法が使える高機能なアセンブラです。

JIT(Just In Time)とはプログラム実行時にコード生成するという意味です。 通常のC++プログラミングはコンパイル時にコードが静的に決まり、実行時にはその通りに動作します。 JITによって実行時に決まるパラメータに応じた柔軟な最適化が可能になります。 Javaのランタイム実行エンジンやモダンブラウザのJavaScript実行エンジン、最近ではPHP 8などが与えられたプログラムに応じたJITを行っています。 Xbyakは、そのような機能の開発を自分でやりたい場合に支援します。

動機

私はコーデックや暗号系のライブラリの開発をすることが多かったのですが、最適化のためにアセンブリ言語(以下asmと表記)を利用するとき不便に感じることがよくありました。

一つはC/C++の構造体やクラスのメンバー変数にアクセスするためにそれらのメンバー変数が構造体の先頭から何バイト目にあるか、一つ一つasm側でオフセットを設定しないといけない点です。 コード情報が重複するので、メンバー変数を入れ換えたり追加したときにasm側も修正が必要で更新し忘れや間違いが発生しやすいです。大規模な開発ではスクリプトやツールを使ってC/C++のコードからオフセットを自動設定することもしていましたが汎用性は高くありません(templateの構造体のパースなどコストが高すぎて無理)。

struct X {
  int x;
  int y;
  int a[4];
};
// nasmの場合
struc X
 .x resd 1
 .y resd 1
 .a resd 4
endstruc

templateでoffsetが変わる構造体の扱いは難しい。

template<size_t N>
struct X {
  uint64_t x[N];
  uint64_t y[N];
};

もう一つは、IntelのCPUはしばしば新しい命令が追加されるのですが古いCPUでも動作可能にしつつ、新しい命令にも対応しようとするとasm側のコードが煩雑になる点です。 またアセンブラの擬似命令(ifやloop、マクロといった機能)は文法が独特で分かりにくいことが多いというのもあります。

Xbyakの記述は通常のC++の文法に従うのでこの2点を解決します。 個人的には「C++の文法でasmを記述できる」というのが最も便利に感じます。

AVX-512のbfloat16が使えるときとそうでないときでコード生成を変える例(jit_avx512_core_amx_conv_kernel.cppより引用 )

if (is_bf16) {
    vmovdqu16(zmm_src_tmp, ptr[reg_src + offset]);
    vpermw(zmm_dst, zmm_idx, zmm_src);
    vmovdqu16(ptr[reg_dst + offset], zmm_dst);
} else {
    vmovdqu8(zmm_src_tmp, ptr[reg_src + offset]);
    vpermb(zmm_dst, zmm_idx, zmm_src);
    vmovdqu8(ptr[reg_dst + offset], zmm_dst);
}

より詳しい説明は blog.cybozu.ioをごらんください。

現在はXbyakを使ってペアリング演算の実装mclや準同型暗号の論文を書いたり、Ethereum 2などブロックチェーン系プロジェクトで利用されている署名ライブラリblsを開発したりしています。

Intelとの関わり

前置きが長くなりました。 主に自分のためのツールとして開発をしていたのですが2016年、GitHubのissueでAVX-512に対応してという依頼が来ます。 当時AVX-512が使えるKnights Landingはまだ発売されてなくて、そんなに急いで対応しなくていいんじゃない?と聞いたのですがぜひとも欲しいというので対応することにしました。

とは言えAVX-512用のマニュアルは千ページ以上あり、エンコーディングも複雑で新設される命令も非常に多く、かなり苦労しました。マニュアルの表記が間違っていたので指摘したこともあります。 どうにか対応すると、BVLCが開発していた深層学習フレームワークCaffeのIntel向け最適化バージョンに組み込まれました。

それがうまくいったようで、その後Intelが新しく開発したMKL DNN(現oneDNN)のCPU向け最適化コードで幅広く利用されるようになります。 深層学習では計算パラメータが非常に多く、ユーザが与えたパラメータや、実行時のCPUのキャッシュサイズなどに応じて行列のサイズやデータの形式が変わることがあり、それらに対応するのがJITだと都合がよいのです。 また前節で述べたように、より新しい命令に対応したコード生成をしやすいという利点もあります。

oneDNNは機械学習フレームワークとして有名なTensorFlowやPyTorchなどで利用されています。

新しい命令が発表され、IntelのCPUの公式マニュアルが更新されると、Xbyakをその命令に対応させます。 発表された直後は、まだgccやclangが対応していないこともあり、動作確認が難しいです。GitHubで「実装したけどちゃんと動いているか分からない。」と書くと、Intelの人に「大丈夫。未公開ツールで動作確認した。」という返事が来たことがあります。

2020年の6月にIntelがIntel AMXという新しい命令体系を発表したときには、ほぼ同時にXbyakに対応pull requestがやってくるという、個人ではなかなか得難い体験もしました(cf. Intel Begins Volleying Open-Source Patches Around Intel AMX)。

Xbyakは先月10月に登場したAVX向けの機械学習命令VNNIにも対応し、oneDNNでは既にAMXやこれらの命令を利用した開発が進められています。

富士通との関わり

最初に述べたように、富岳のCPU A64FXはAArch64というスマートフォンに載っているCPUと同じ系統の汎用CPUで、SVEというAVX-512に似たSIMD命令を利用できます。

SVEについてはたとえば8月に開催されたARM入門勉強会で私が発表したIntro to SVE 富岳のA64FXを触ってみた(動画 : https://youtu.be/3pfjrIyjhBc?t=12462)を紹介しておきます。

www.slideshare.net

youtu.be

富岳で機械学習まわりの開発を容易にするためにoneDNNの移植計画が立ち上がり、そこでoneDNNのエンジンで利用されているXbyakの富岳版を開発するための共同研究が始まったのが2019年の4月でした。 移植に関わる詳細な情報は富士通研究所のblogや、技術評論社による対談記事がgihyo.jpで公開されています。合わせてご参照ください。

Xbyak_aarch64は私もコントリビュータとしてバグ取りや改良をしています。 そしてXbyak_aarch64を使った富岳用のoneDNNの開発が進み、その成果の一部を富士通が本家Intelに統合してもらうよう依頼し、了承されました。個人的には富士通がIntelに出したPull Request

github.com

の中で、富士通の人が書いてくれた謝辞に私の名前があるのをIntelの人がみつけて、

Acknowledgment We thank S. Mitsunari (Cybozu Labs, Inc.), the developer of the original Xbyak.

Yeah, we greatly thank Mitsunari-san for the Xbyak on x86 too! 🎉 His work helped us a lot to make oneDNN development faster and simpler.

コメントを下さったのがとてもうれしかったです。 今後も開発に関わり、よりよいものにしたいです。

まとめ

XbyakというJITアセンブラの紹介とそれがIntelの深層学習ライブラリに利用され、富岳でも使われるようになった経緯を紹介しました。

サイボウズサマーインターン2020 モバイルアプリ開発コース開催報告

$
0
0

モバイルコース集合写真

こんにちは!モバイルチームの刈川です。 今年もサイボウズではサマーインターンシップを開催しました。 すでにセキュリティコースWeb開発コースの開催報告が上がっていますが、今回はモバイルアプリ開発コースについて紹介したいと思います。

概要

今年は初のオンラインでの開催となりました。 ※去年の様子はこちら

インターンでは主に「開発を通じてサイボウズの文化や雰囲気を知ってもらう」ことを目的として、様々なプログラムを用意しました。特に今年はCOVID-19の影響で物理的に出社してもらうことが難しい状況だったため、オンラインでも会社やメンバーの雰囲気が伝わるような工夫をしています。期間中はほぼ全員が自宅からビデオ会議に繋ぎ、面談をしたりランチをしたり、また、画面共有を活用したモブプログラミングも行いました。

やったこと

参加者2〜3名 + メンター2名で一つの開発チームと見立て、kintone を利用したクライアントアプリの開発を行いました。モバイルコースは9月の上旬と中旬の2つの日程で開催したのですが、日程ごとにiOSとAndroidにメンバーを分けました。 作ったアプリは以下のとおりです。

  • [Android] バーコードスキャナを利用した在庫管理アプリ
  • [iOS] タイムカードアプリ

ざっくりと以下のような工程で進めていきました。 時間割

インターンでの開発の流れ

基本的にインターンはサイボウズが普段から実践している開発プロセスに則った形で進めていきます。サイボウズではスクラム開発の手法を取り入れており、以下のような流れで開発を進めていきます。

  • 要件(バックログ)の紹介
  • バックログのリファインメント
  • スプリントプランニング
  • 実装(モブプログラミング)
  • スプリントレビュー

※各用語は一般的なスクラム開発と同じですので初耳だよ〜という方は調べてみてください:)

画面共有を利用した設計の様子
画面共有のお絵かき機能を活用し、設計をしていく

まずは実装するアプリの要件(バックログ)を紹介したあと、そのバックログがどのくらいの実装コストがありそうか、技術的に懸念点はないか、などをチーム全員で話し合います(リファインメント)

リファインメントが終わったら、実装計画(プランニング)をやっていきます。 ここでは機能を実装するにあたって行うべきタスクの洗い出しや、タスクを遂行する上で障害となる懸念点の共有や解決を全員で話し合います。これらの項目を実装に入ってから相談していると作業が途中で手が止まってしまうため、実装を始める前に全員が問題意識を共有できている状態になるようにします。プランニングが終わったらいよいよ実装の開始です。ここまでで大体1日目が終了します。

開発はモブプログラミング形式で進めていきます。モブプログラミングでは実際に手を動かす「ドライバー」1人と、そのドライバーに指示を出す「ナビゲーター」複数人に分かれます。手を動かす人が固定されないように、時間で区切って適宜メンバーを入れ替えていきます。

5日目の最終日では成果発表会を行います。発表では他のコースの参加者とメンターを交え、コースでやったことや実装した機能のデモ、学んだことなどを発表してもらいました。

開発以外にも…

開発以外にも、社員との面談や、実際に製品開発チームが行っている会議を見学したり、ふりかえりや雑談などを交えてサイボウズの雰囲気を感じ取ってもらいました。

参加者の感想

インターンに参加してくれた方の感想を一部ご紹介します。

この五日間で技術や開発までのプランニングなど多くの学びを得ることができました。また画面越しではあるものの、サイボウズの会社の雰囲気や働きやすさも感じ取ることができました。今回インターンを通してインプットしたことをこれからはしっかりを自身のなかで整理し、アウトプットしてきたいと思います。五日間ありがとうございました。


社員の方も非常に優しく接していただき,社内の様子がよく分かるという意味で非常に素晴らしいインターンシップだったと思います. 特にアジャイルやスクラムといった組織論についても教えていただきプログラミングの技術的な部分以外も成長できたと言うところが個人的には一番大きな収穫だったと思っています. ただし,もちろん技術的にも非常に成長することができたと思っています.普段はAndroidをやっている中,iOSコースで参加したのですが,サイボウズのモバイルチームの方々は両方やられている方の割合が多く,iOSコースなのにAndroidで例えても的確に教えていただけたりなど,特徴的な部分も多かったと思います. このように,勉強できることももちろん多かったですが,何より楽しく充実した5日間を過ごすことができました. ありがとうございました.


わかったつもりになっていても、モブプロでチームメンバーに実装の妥当性や内部実装などについて指摘されることで初めて気づくわからなかったところも多く、理解度を高めていくという意味でとてもよかったと思っています。なおかつそれが、責められるわけではなく、同じくわからないなりに提案として提示されるチーム内の風土が自然と出来上がっており、心の安全が保たれていた感覚があったのがよかったです。

まとめ

モバイルコース集合写真2

今回はオンライン開催という特殊な回だったのでメンター・参加者も含め、初めての体験が多い年となりました。本来ならば物理的に出社してもらってオフィス見学なども体験させてあげたかったのですが、それを抜きにしてもオンラインでのコミュニケーションを通してサイボウズの雰囲気が十分に伝わったようでなによりでした。

インターンは来年も開催する予定なので、この記事が参加しようかな〜と思っている人の参考になれば幸いです。


インターン生Meetup 2020 を開催しました!

$
0
0

インターン生Meetup 2020 のロゴ

こんにちは、エンジニアインターン運営チームの村田です。

今回は 2020/10/16 に開催した「インターン生Meetup」の内容をご紹介します!

インターン生Meetupとは

インターン生Meetupとは、サイボウズのサマーインターンシップ2020に参加していただいた学生の皆様を招待して、インターン期間中になかなか関われなかった入社1~2年目のエンジニアとの交流、インターンでは知り尽くせなかったサイボウズの気になる点を質問できるイベントです。

前回の開催報告はこちらblog.cybozu.io

今年は若手エンジニアによるLT会、Q&Aセッション、メンターの方々との懇親会を実施しました!

本記事では当日行われた「Q&Aセッション」の一部をご紹介します!

Q&Aセッション

事前にインターン生の皆様から質問を募集し、当日はその質問をもとに各コースのエンジニアの方々にインタビューをさせていただきました。

Q&amp;Aセッションの様子
Q&Aセッションの様子

登壇者の方々

  • 小西 達也: セキュリティエンジニア (PSIRT)
  • 川畑 ひとみ: QAエンジニア (kintone)
  • 阪上 大地: クラウド基盤エンジニア (Neco Project)
  • 白鳥 亜美: Webエンジニア (kintone)
  • 中村 拓人: モバイルエンジニア (kintone)

質問と回答をいくつかご紹介します。

Q1, 入社する時、サイボウズに決定した理由を教えて下さい。現在所属している部署を選んだ理由や経緯について、お聞きしたいです。

小西:インターンに参加したときに情報共有のための基盤が整っていることを実感したからですね。PSIRT を選んだ理由は、社内のセキュリティ診断の仕事に携わりたいと思っていたからです。学生の頃からセキュリティ関連の勉強が好きで、その分野に就職したいと考えていました。サイボウズのセキュリティインターンに参加したときに製品の品質を検証するための奥深さを実感し、自分が診断することがダイレクトに製品の品質に直結するところにやりがいを感じたため、今社内のセキュリティ診断の仕事に携わっています。

川畑:前職は食品会社で食品開発の仕事をしていました。就職前にサイボウズの社員の方々と何度かお話しする機会があって、おだやかな感じの雰囲気が自分に合っていると感じたため入社を決めました。私は就職する時点でkintone開発チームのQAエンジニアと決まっていたのですが、就職前に kintone のプログラマーの方ともお話しする機会を用意していただけて、そこで人柄が分かっていたので安心して配属することができましたね。

Q2. 入社した時に想像した働き方とギャップはありませんでしたか?

阪上:特にギャップはありませんでしたね(笑)。ブログなどで発信されている内容と同じだなと思います。良い意味でのギャップだと、理性的な人が多いところだと思います。 チームをより良くしていくという意識が強いメンバーで構成されているため、チームのためになることなら多少凹むかもぐらいのことでも共有しています。多少厳しいコメントでも言いやすいし、皆さんも自分に対してチームをより良くするためのコメントを言ってくれるので良い環境だなと思っています。悪い意味でのギャップだと、自分が入社したタイミングからフルリモートだったので、オフィスに出社したかったなあと思うところです。

Q3. サイボウズで働いていて、改めて感じるサイボウズの良いところはどこですか?

小西:質問責任という文化があるので分からないところや疑問点を質問しやすい文化があるところです。kintone での情報共有が活発なので、自分が気になった情報はいつでも調べることができます。心理的安全性が高くて、みなさん公明正大にやったこと、失敗したことについて報告していますね。失敗したとしても責めることはなくて、次どういうアクションをするのか考える環境が出来上がっているところが良いところだなと思います。

Q4. Cybozuで社会人, 技術者として成長できたと思うことはなんですか?

川畑:QAエンジニアとしては成長できたと思います!自分は未経験で入社したので「サーバーって何?APIって何?」というところから始めました。入社当初は自分の仕事が割り振られていなくて、半年間お給料をもらいながらQA業務の研修と勉強をさせてもらえてました。最高の環境だったと思います。今はプログラミングの勉強を始めたりして、より技術者として成長するための努力をしています。

阪上:いつ何の話をするのか/しないのかといったような議論の仕方を学べているところが社会人としての成長かなと思います。サイボウズはトラブル解決のプロトコルが明確で、問題解決メソッドの共有だったり、いつどういうことを話すのか認識が揃っている人が多い。会社内での議論を通じておだやかに過ごすための勉強ができていますね。

中村:学生の頃と比べて責任を持ってチーム開発をするようになったと思います。学生のときはアプリが動作すればok!みたいな感じで開発していたものもありましたが、入社してからは、開発者体験や品質を継続的に維持していくための仕組みについて考えながら開発するようになりました。

Q5. どのような人と一緒に仕事がしたいですか?

小西:正直な人と仕事がしたいと思っています。脆弱性の評価に迷うときはメンバー全員で議論するのですが、そのときに客観的に評価をする必要があるので、他の人の意見に流されずに自分の意見を正直に言える人と仕事がしたいです。

川畑:自分の意見をちゃんと話してくれる人ですね。意見を言ってくれるとより良い方向に物事を進めることができるので。

阪上:良い製品を作りたいと思っている人と一緒に仕事したいですね。

白鳥:同じ理想を持っていることは前提で、自分にない視点を持っている人と一緒に仕事をしたいと思っています!

中村:ポジティブで冗談の通じる人と仕事がしたいと思っています。モブプログラミングで開発することが多いので、真面目な話ばかりしていると結構疲れてきちゃうんですよね(笑)。だからたまに雑談挟んでくれると嬉しいです。

Q6. 大学の研究よりも、趣味でやっている開発の方が就活の時に有効に機能する感じがして憤りを感じています...。しかし本格的に研究が始まるため、趣味の開発は少しずつ割合を減らしていかなければなりません。もし就活で研究をアピールする際に、どのようなポイントやコツがあると思われますか?

中村:各企業によって学生に求める能力が異なるので、企業が求めている人物像に合わせて説明の仕方を変えることが大事だと思っています。例えばサイボウズの場合は"思考力"を大事にしているように感じるので、今取り組んでいる研究に対してどれぐらい真剣に考えて行動してきたのかを説明できるようにしておくと良いと思います。

白鳥:サイボウズの面接だと研究がそのまま業務に使える人はそんなに多くないと思っていて、事実ベースで話されても「研究頑張っているんだな〜!」ということしか伝わらないと感じています。なぜその研究に取り組もうと思ったのか、研究によって自分が何を得たのかといった思考の部分を説明できるようにしておくことが大事だと思います。

おわりに

今年はインターンシップもインターン生Meetupもフルリモート開催になりましたが、無事参加していただいた学生の皆様に楽しんでいただけたようでした! イベントのほんの一部分の紹介となりましたが、サイボウズのインターン生Meetupについて、少しでも雰囲気を感じ取っていただけたら嬉しいです。また来年も開催できたらいいなと思います!

各コースのインターンシップの報告記事がありますので、気になる方は是非ご覧ください!

blog.cybozu.io

blog.cybozu.io

blog.cybozu.io

blog.cybozu.io

サイボウズ社内ハッカソン2020開催〜オンラインで全社を巻き込んで盛り上げるために工夫したことや振り返り〜

$
0
0

社内ハッカソン2020のロゴ

こんにちは!開発本部の社内コネクトチーム「WASABI」の @hokatomoです。 今年はオンラインで社内ハッカソンをやりました!

  • 開催概要と開催結果(参加数大幅増加!)
  • 全社を巻き込むために、オンライン開催で工夫したこと
  • 得られたもの

以上のまとめです。

目次

  1. 開催概要・結果
  2. オンラインハッカソンを盛り上げるために!工夫したポイント
  3. 準備の大変さは技術力と本業の知識で解決💪
  4. 大賞受賞と成果物紹介
  5. 「サイボウズの誰かを知る」にちょっとでも近づけた!
  6. 終わりに。「エンジニアによるエンジニアのための社内イベントの企画」について語りました

1.開催概要・結果

社内ハッカソンは今年で8回目!

社内ハッカソンの歴史は2013年からはじまり、今年で8回目です。

昨年はリアルで開催していました。

blog.cybozu.io

今年のテーマは「サイボウズの誰かを知る」そのテーマに込めた意味

社内では在宅勤務のメンバーが増えてきたので、

  • 業務以外のコミュニケーションがもしかして減ってきているかも?
  • 自分のチームや所属部署以外のメンバーと、ちょっとしたコミュニケーションがなくなってるかも…?

なんとなくですが感じていたんですね。

なので、ハッカソンを通じて「業務じゃないコミュニケーション」からお互いを知ることにより

  • 業務上のコミュニケーションがしやすくなる(かも)
  • オンライン上のコミュニケーションがもっと良くなるのでは?!

と考え「サイボウズの誰かを知る」をテーマにしました。

開催概要

  • 開催方式:基本オンライン
    • 対面での開発は社内で周知されている行動指針を守ればOK
  • ハッカソン主催・運営:開発本部で社内イベントをやっているWASABIが担当
    • メンバーはモバイルエンジニア、フロントエンドエンジニア、QA、技術広報です

WASABIチームについて

blog.cybozu.io

ハッカソン全体の流れ(全てオンライン)

  • 参加表明をする
  • チーム決めを運営におまかせ!の人たちはチーム顔合わせ会
  • 開会式
  • 中間発表会
  • 成果発表会
  • 表彰式

以上全てをZoomで行いました。

開催結果、参加者が増えました!

  • 参加者数:67名(前年比6名増加↑
  • 参加チーム数:39チーム(前年比9チーム増加↑
  • 参加国:2カ国(日本、ベトナム )
  • ベトナムには連結子会社があります
  • 賞の種類:大賞、技術賞、アイデア賞
  • 成果発表会参加者:約200名(日本では個別にZoom接続、ベトナムでは広めのスペースに何人か集まり設備からZoom接続)

2.オンラインハッカソンを盛り上げるために!工夫したポイント

初めて社内ハッカソンをオンラインで開催したので、盛り上げる施策をいくつか準備しました。

バーチャル背景

開催前は「ハッカソン参加するよ!」とアピールができる背景を用意

ハッカソン参加するよアピールができるバーチャル背景
実際のバーチャル背景

「一人でも使ってくれたらいいなぁ」と思っていたところ、思ったよりも広く使ってもらえて良かったです。

さまざまなオンラインミーティングで「その背景何?」と聞かれることが多く、バーチャル背景を告知の場にするの良いかも!と気づけました。

ハッカソン中は「チーム別のバーチャル背景ジェネレーター」を用意

バーチャル背景ジェネレーターのスクリーンショット
バーチャル背景ジェネレーターのスクリーンショット
@ueokandeにバーチャル背景ジェネレーターを作成してもらい、 チーム名が入った背景を手軽に作れるようにしました。 成果発表会時に、より一体感が出ました!

Like👍で届く「ハッカソン頑張った&応援グルメ」企画

グルメ企画で私が取り寄せた鹿児島銘菓
グルメ企画で私が取り寄せた鹿児島銘菓

  • 運営がkintoneで専用のアプリを作成
  • 自分が食べたいものをレコードに登録(上限金額あり)
  • 「食べたい!」と思うグルメのレコードに「Like」をする
    • Likeが「私にそれ届けて!」という意思表示になる
    • kintoneのレコードに「Like」が出来るプラグインがあるのでそれを使用
  • 協力メンバーが取りまとめて発注〜経費精算を行う
  • ハッカソン成果発表会前に、自分がLikeしたグルメが手元に届く

という流れの企画です。 上の写真は私がお取り寄せした鹿児島銘菓詰め合わせセットです。

補足

  • この企画に参加する時点で「運営や協力チームに発注に必要な個人情報の開示を許可する」としています。
  • Likeをしたユーザーの住所情報を人事から提供してもらい、運営・協力チームが発注をします。

この企画の背景

それぞれが個別に飲食を購入すると、経費精算の確認や、領収書の回収、軽減税率混在による間違いの対応がとても大変でした。

もちろん一括発注の大変さはあるのですが、

  • 成果発表会への参加のハードルをさげる
  • 運営や協力チームで一括発注・一括精算を行うことにより経費精算の手間を削減する

以上の効果を狙い、この形でやりました。 協力チームのおかげで円滑に発注〜精算までできましたし、多くの本部からの参加のきっかけともなりました!

大きなトラブルはなかったのですが、「ほんとにLike!だけで届くんですか?」とよく聞かれたり心配されました😂

「Like」で届くグルメ企画、予期せぬ嬉しい効果があった!

  • おすすめのグルメを登録した同僚の好みを知れたり、
  • 思い出のグルメを登録してくれる同僚がいて、そのエピソードを知れたり
  • 共通のグルメを頼むことによって話題が生まれたり

オンラインで離れていても、食によってこれだけ盛り上がるんだと実感が出来ました😃

お祭り感を出す!

最終日の成果発表会は、2トラックで開催しました。

多数のチームの発表時間を分かりやすく伝えるため、 そしてフェスのような雰囲気を出して、もっと盛り上げたい!と考え WASABIチームの刈川さんがタイムテーブルを作成👏👏

タイムテーブルからわかり易さはもちろん、お祭り感が演出でき、盛り上がりの一因となりました。

2トラック開催は課題もあったが、今回は「参加のしやすさ」を優先

2トラック開催は「聴きたいトラックがかぶって聴けない」というフィードバックがあり、課題として感じています。

ただ、多くの人に参加して欲しいという思いがあったので今回は「気軽に参加できる」を優先し、 後日録画を全チームチャプター付きで展開することを想定し、2トラックの判断をしました。

この課題は来年への学びなので、必ず何かしらで活かしたいです!

常時接続可能なさぎょイプZoomを用意

ゆるく集まれるZoomを準備、開放しました。

「進捗どうですか?」と雑談したり、突然ねるねるねーるを作り始める同僚がいたり、 有効的に活用してもらえたようで良かったです。

ただ、「今誰が接続しているか、それとも誰もいないのか分からない」というのが課題と分かったので、そのあたりは来年への学びとなりました。

3.準備の大変さはそれぞれの技術力と経験で解決💪

私はハッカソンの経験に乏しい…😥→ベテランメンバーが大活躍!

私は社内ハッカソンをメインで担当するのが初めてで「これどうやってやったらいいんだ…」と行き詰まることが多々ありました。 そこでベテランのハッカソン運営メンバー @kariheiが颯爽と現れ随所への配慮のおかげで、円滑に進みました!

手作業辛い😫→スクリプトで解決!

ハッカソンの準備では、手作業でデータを並べ替えたり……という時間のかかる作業を想定していました。 「あーこの作業時間かかるなぁ」と思っていたら、 フロントエンドエキスパートチームにも所属している @toshi_tomaがスクリプトを書いてくれたおかげで、数時間かかる作業が数秒で解決!

海外との調整難しい→慣れているメンバーが連携や翻訳の調整を担当💪

参加してくれたベトナムのメンバーとの各種調整や翻訳など、自分は経験がなかったのでどうしよう……となっていました。 困っていたところ、運営メンバーの @ayumiが普段の業務で知見があるので思い切ってお任せ🙏 安心してベトナムとも連携することができました。

4.大賞受賞と成果物紹介

チームベテラン(@ymmt,@mitomasan,@nakajmg)がそのまま自社プロダクトに取り込めるのでは?!という機能をハッカソン期間中(←?!)に開発。そのスピード感と技術力で見事大賞を受賞!

普段業務を一緒にしないメンバー同士で開発していく様子をオンラインで見て、 距離関係なくどんどんモノが出来上がっていくさまはカッコよさにしびれました……!!

1on1や雑談を支援するような仕組みを作ったのは、参加チーム中約1割

どうやったら「オンラインで雑談しにくいのを乗り越えるか」「雑談しやすい仕組みを作るか」といったところに注力したものを作っていたチームは1割ほどいました。

注目度が高いテーマだったことが伺えます。

5.「サイボウズの誰かを知る」にちょっとでも近づけた!

(長くなりましたが)終わりに、今年のテーマ「サイボウズの誰かを知る」を振り返ると

  • オンライン開催でも、例年通り普段の業務では関係のない人と話すきっかけが作れた
  • 参加しなくても、ハッカソンを見ていると「誰がどんなことができるのか」がkintoneのスレッド等で垣間見えた
  • 全員がほぼオンライン、同じ状況でイベントに参加するのは体験が同じになって良い(オンラインが置いてけぼりみたいなのがない)
  • オンラインだからこそ挑戦できた取り組みがあった(グルメ企画等)

という成果や学びが得られました。

これも、運営メンバーや協力してくれたさまざまなチームの皆さん、楽しんでくれた参加者の皆さんのおかげです!

「はじめて参加したけど、楽しかったです」

初めてのオンラインハッカソン、果たしてうまく行くのだろうかと不安がとても大きかったのが正直なところです。

でも、それぞれの強みを活かした運営チームメンバーや協力チーム、 オンラインでさらに楽しんでくれている参加メンバーのおかげで円滑に行えたと感じています。

そして、表彰式の際に参加したメンバーから「はじめて参加したけど、楽しかったです」の一言で全部報われました……!!! 来年もがんばるぞ!

6.終わりに。「エンジニアによるエンジニアのための社内イベントの企画」について語りました

speakerdeck.com

オープンソースカンファレンス2020 OnlineFall というイベントで、ハッカソン運営チームWASABIメンバーが「エンジニアによるエンジニアのための社内イベントの企画」について語りました。

ハッカソン以外にも各種開発系の社内イベントをやっているWASABIチームですが

  • 自分たちで主催する開発系の社内イベントは何を大切にしているのか
  • マンネリ化しない工夫
  • どんなミッションを持ってやっているのか

などを @toshi_tomaが話しています。

上記はスライドです。

www.youtube.com動画版もあります。

ぜひご覧ください!

2020年、サイボウズのアクセシビリティを振り返る

$
0
0

カバー:サイボウズのアクセシビリティを振り返る

こんにちは。開発本部 デザイン&リサーチ アクセシビリティチームの SUGI(@blindsoup2p1)です。

私は今年の4月に新卒で入社した全盲のスクリーンリーダーユーザーで、アクセシビリティ・エンジニアとして活動しています。

サイボウズでは今年、正式にアクセシビリティチームが発足し、グループウェアのアクセシビリティ向上や社内への啓発を進めてきました!

この記事では、今年1年アクセシビリティチームが取り組んできた過程と成果をまとめ、来年の展望を示します。 と共に、エクストリームユーザーである私がアクセシビリティ向上にどうかかわっていったのかを合わせて紹介します。

※アクセシビリティとは、能力・環境・状況にかかわらず、誰もがサービスやコンテンツを利用できること、またはその度合いのことです。
よくアクセシビリティは障害者・高齢者対応という誤解を受けますが、能力・環境・状況(の制約)は障害者・高齢者に限らず等しく誰もが一時的または半永久的に起こりうるものです。
そのためアクセシビリティは、すべての人、一人ひとりが利用できること、そのアクセス性を確保することといえます。

※エクストリームユーザーとは、サービスのターゲット層の中でも極端な性質を持つユーザーのことです。
エクストリームユーザーにヒアリングすることで、多くの人が気づいていない潜在的な問題や、常識・固定概念にとらわれない発想を得ることができます。
私の場合はスクリーンリーダー(音声読み上げソフト)を日常的に使用して画面を全く見ずに操作するという点で、エクストリームな状況にあります。

サイボウズにとってのアクセシビリティ

チームに入りたいと願うすべての人が、チームにアクセスできる能力。

サイボウズは「チームワークあふれる社会を作る」を理念に掲げ、グループウェアの開発を進めています。

この思想を踏まえてアクセシビリティについて考えた時、ユーザーが単にグループウェアにアクセスできる(利用できる)では不十分です。その先の目的が大事です。

つまり、ユーザーはグループウェアにアクセスすることで、何を求めているのか、何を達成したいのか。

その答えが、チームにアクセスすることです。

グループウェアを使うことで、チームに参加したい、貢献したい、活躍したい、協力したい…。

このようなユーザーの願いを尊重し、そのアクセス性を確保すること。

チームに入りたいと願うすべての人がチームにアクセスできる。その先に「チームワークあふれる社会」があります。

新設したアクセシビリティチームの紹介

2020年1月、正式にアクセシビリティチームが発足しました!

それまでは開発本部の小林大輔さんが協力者を募りつつ1人で活動していましたが、今年になってメンバーが4倍になりました!

チームの活動内容は、以下の3つです。

  • 社内啓発:勉強会を開催して社内のアクセシビリティリーダーを育成
  • 研究開発:サイボウズ OfficeGaroonkintoneのアクセシビリティ向上
  • 社外発信:記事執筆やイベント開催などでサイボウズのアクセシビリティの認知拡大

メンバーは以下の4人です。仕事の場所や時間は多種多様です。

  • 小林(@sukoyakarizumu)
    • kintone PG、現在アクセシビリティチーム専任
    • コロナ以降、基本在宅、時々必要に応じて出社
    • 2014年から協力者を募りつつ、アクセシビリティの啓発・製品のアクセシビリティ向上を行ってきた
  • 西藤(@ka3zu1ma10)
    • 週3日サイボウズ Officeのデザイナー、週2日アクセシビリティの兼務
    • コロナ以降フル在宅勤務
    • デザインもリサーチもアクセシビリティも幅広くこなすマルチプレイヤー
  • kotanori(@uexkull_)
    • 週3日システムエンジニア、週2日アクセシビリティチームの兼務
    • 入社当時から6年間フル在宅勤務
    • ロービジョン、拡大率200%
  • SUGI(@blindsoup2p1)
    • 今年(2020年)新卒入社、アクセシビリティチーム専任
    • 入社当時から在宅勤務、時々必要に応じて出社
    • 全盲、スクリーンリーダーNVDAを使用

今年のチームの活動振り返り

サイボウズアクセシビリティサイトの公開

サイボウズのアクセシビリティの情報を発信していくサイトを公開しました。

現時点のコンテンツとしては、製品情報と啓発プログラムがあります。

製品情報では、定期的にアクセシビリティにかかわるアップデート情報を掲載しています。

啓発プログラムでは、アクセシビリティを広めていきたいと考えている方に向けて、サイボウズでの取り組みやノウハウを発信しています。

このサイトは、サイボウズがアクセシビリティに取り組んでいることを知っていただくために、専用ページとして開設しました。

ひとまずは情報を必要としている人に届けたいという思いです。

製品の改善リクエストも受け付けているので、ぜひフィードバックをお寄せください。

社内勉強会

当事者目線で伝えるということ

定期的にアクセシビリティ勉強会を開催しています。アクセシビリティを向上させるHTMLの話、見やすい・読みやすい資料の作り方など内容はさまざまです。

勉強会を定期的に行う理由は、参加者に、アクセシビリティを身近に感じてもらい、重要と思ってもらうためです。

そのために、私がスクリーンリーダーのデモをしたり、サイボウズ製品のバッドプラクティスを紹介したりしています。

私が勉強会で発表することの意味は、当事者目線で話せること、日々の体験から必要性を語れることです。

とはいってもスクリーンリーダー特有の話に傾きすぎることは避けたいと常々思っています。

スクリーンリーダーだけではなく、キーボードユーザー、肢体不自由、タブレット・モバイルユーザー、母国語が日本語ではない人など、さまざまなユースケースに話を広げることを意識しています。

伝えたいポイントは、アクセシビリティは特別対応ではない、アクセシビリティはちょっとしたこと(タグや属性を書き換える、上流のうちから意識してデザインするなど)で向上できるということです。

いくつかの勉強会は資料・動画付きで公開しています。

参加者の意識の向上

勉強会を受けて、参加者の意識が向上した事例も少しづつ増えてきています。

たとえば、特に全社的な会議の資料などは、見やすさ・読みやすさを意識して作られることが増えました。

周辺の文字や代替テキストで図表の説明をすること、スライドに識別可能なタイトルをつけることなどです。

また、話し方についても、画面共有ありきではなく、口頭だけでも伝わる話し方を意識することが増えたように感じます。

開発方面では、見出しレベルが適切か、ボタンのマークアップは適切かなどのちょっとしたことを相談されることが以前より増えました。

写真:サイボウズ社内での勉強会の開催風景
サイボウズ社内での勉強会の開催風景

サイボウズ Office

明らかに問題が山積みの状態からのスタート

入社当初サイボウズ Officeは、スクリーンリーダーではほぼ使えない、あるいは著しく困難を要する状態でした。

  • 見出しが全くなく、すべての画面でページの先頭から情報を読んでいかなければならない
  • レイアウトテーブルがふんだんに使われていて、いたるところで「テーブル」と読まれて煩わしい
  • 入力欄にフォーカスした時、対応するラベルを読み上げない

この現状を打破すべく、サイボウズ Officeのデザイナーと一緒に週1回のペースで定期的にバックログの検討、実装を行っています。

サイボウズ Officeの場合は、開発PGの人でが少ないことから、PMと相談した結果、バックログの検討から実装までをアクセシビリティチームが巻き取って進めています。

デザインを変えるとお問い合わせが増えてしまう可能性があるので、ひとまずは、UIの変更が生じない基本的なタスクからです。

実際の進め方としては、通知の処理、スケジュール閲覧などのシナリオを決めてストーリーマッピングを行い、優先度の高いものから順次実装という流れです。

私のかかわり方としては、バックログの作成、実装(モブプログラミングもしくは個人タスク)、動作確認・検証です。

バックログの作成では、問題点を洗い出し、改善点と解決策をまとめます。これをチームでわいわいしながらストーリーマッピングに当てはめていきます。

実装はモブで行いますが、最近はメンバーもサイボウズ Officeのコードに慣れてきたので、チームを細分化したり、個人でやるケースが増えてきています。

動作確認・検証では、現時点ではNVDAとPC-Talkerという2つのスクリーンリーダーで品質をチェックしています。

スクリーンリーダーによって挙動が違うことがあるので、チェックは大事ですし、なにより自分自身が勉強になっています。

図表:サイボウズOfficeのユーザストーリーマッピング
サイボウズOfficeのユーザストーリーマッピング

今年取り組んできて私が感じたこと

2つあって、後付けでアクセシビリティを担保するのはコストが高いということ、そしてバックログのコストとユーザーへのメリットを考えることが大事ということです。

「見出しを付けるなどの”当たり前品質”程度のアクセシビリティ改善であれば、簡単にできるでしょ」、と最初は思っていました。

しかし、サイボウズ Officeは23年という歳月の中で、コードは複雑に絡み合い、共通化している部分、そうではない部分、他のページとは仕様が異なっている部分などさまざまな事情が積み重なっています。

そのため、ちょっとした手を加えるだけでも、思わぬところに影響範囲が及んだり、それによってレビュアーの負担を増加させてしまったり、品質を確保するために実装に思わぬ時間を取られたり、受け入れ試験の想定外の修正が入ったりしました。

新規開発のフローと共に改善するのであれば、このような手間は最小限に抑えられたし、それが理想の姿でしょう。

後付けのコストの高さを実感したことに付随して、リリースするバックログのコストパフォーマンスの良さとユーザーへのメリットの最大化を考えるきっかけになりました。

リリースできるバックログには限りがあるため、トップページやよく使う機能など、ユーザーにとっての大きなメリットが得られるようなものから厳選して取り組む必要があります。

地ならしした結果主要な画面の閲覧が容易に

5月から定期的にアップデートを行い、入社当初と比べると格段に使える状態になりました。

  • トップページの通知一覧から飛ぶことができるすべての画面に見出しをつけ、通知内容を確認しやすくしました。
  • 主要なアプリケーションの画面に見出しをつけ、必要な情報にたどり着きやすくしました。
  • スケジュール一覧のテーブル見出しをつけ、目的の予定にたどり着きやすくしました。

詳しいアップデート内容はアクセシビリティサイトの製品情報に記載しています:

Garoon

アクセシビリティチームが開発のフローに参加

PM・デザイナー・PGと一緒に、問題点を洗い出し、優先度の並び替え、コストの見積もりなどを定期的に行っています。

その上でPMと合意がとれたタスクの実装のプロトタイプを、アクセシビリティチームが作ります。

レビュー・マージ・試験などはGaroonチームのスクラムに任せています。

バックログの紹介は、全社員が参加可能なリファインメント・スプリントレビューなどの会議体で行っています。

そのため、Garoonでどのようなアクセシビリティ上の問題があって、どのように解決し、どのような効果が得られたのか、PM・PG・QA・その他会議参加者が知ることができます。

多くの人の目に触れることはとても重要なことです。アクセシビリティチームで完結することなく、アクセシビリティの重要性やノウハウを共有できるからです。

私のかかわり方は、サイボウズ Officeと同じで、バックログの作成、実装、動作確認・検証です。

Garoonの場合はリリースが3か月おきなので、より効果の高いバックログを厳選する必要があります。

共通コンポーネントの改善

アクセシビリティのバックログを作るとは別に、UIのリファクタのタイミングでアクセシビリティ向上を一緒に行っています

GaroonデザイナーがアクセシビリティチームとPM・PGとの橋渡しを担ってくれていて、UI関連の変更がある時は、アクセシビリティチームに相談してくれます。

相談の中でマークアップやキーボード操作などの対応方針を決めて、リファクタを行っているベトナム拠点の開発PGにフィードバックしています。

共通コンポーネントなので、一度しっかり調査して改善を加えれば、一律にそのパーツが使われている画面の品質が保たれるます。

徐々に回り始めたアクセシビリティ改善フロー

11月のアップデートで、最初のアクセシビリティ改善として、トップページの”当たり前品質”の向上を行いました。

トップページには見出しがなく、レイアウトテーブルも多く使われており、最も利用するページにも関わらず使い勝手が悪い状況でした。

アップデート情報は、アクセシビリティサイトに記載している他公式のリリースノートにも記載されました

また、共通コンポーネントとしてはツリービューとモーダルダイアログの改善を行いました。

さらに、リリースは先ですが、画面全体のランドマークの改善を行っています。

kintone

新規開発チームとの協力

kintoneについては、他の製品と比較すると、新規開発と共にアクセシビリティを考慮していく流れがあります

アクセシビリティチームが実装を巻き取るのではなくて、デザイナーがUIを考える時、PGが実装する時に、必要に応じてモブで相談に入っています。

その結果、デザイナーやPGの知見がたまっていきますし、デザインする時、実装する時に、多角的な視点で考えてみる習慣をつけるきっかけにもなります。

この流れを作るために、アクセシビリティチームに相談しやすい環境づくりをしてきました。

まず、質問・相談スレッドを立て、気軽に質問してもらうよう呼びかけました。

次に、kintoneの問題点をフィードバックする会を行いました。

伝えたいポイントは、kintoneを使うためにかなり試行錯誤しなければならないユーザー、もしくは使うことができないユーザーがいること。その困りごとは、ちょっとしたことで解決することができることです。

エンジニアのやるべきこと・やりたいに訴えかけるつもりでしたが、結果的には実装の段階で相談してもらえることが増えました。

また、アクセシビリティチーム側からも、リリース予定のバックログを眺めて、UIに変更が生じる場合にはモブで相談できないか持ち掛けるようにもしています。

現時点では開発フローに正式な手順としてアクセシビリティの相談・モブ実装があるわけではないのですが、意識の高い人たちによって、もしくはアクセシビリティチームの判断によって、スモールステップで取り組んでいる段階です。

モバイルチームとの協力

モバイルチームが、kintoneモバイルアプリの読み上げ改善をしてくれました。順次リリースしていっています。

定期的にアクセシビリティチームとモバイルチームで、現状のアプリの問題点を探求する勉強会を開催しています。

洗い出した問題は、モバイルチームが主導して、実装してくれています。

製品ロゴを読み上げない問題、エラーを読み上げない問題など、主要な問題が今年改善されました。

React化チームとの協力

kintoneは現在、新規開発とは別フローで、脱レガシーの一環としてReact + TypeScript化に取り組んでいて、キーボードユーザー、スクリーンリーダーユーザーなどの体験が向上するよう意識しながら実装を進めています

毎回開発PGとモブでマークアップを相談したり、複雑なコンポーネントの場合は、アクセシビリティチームで持ち帰って調査・検討したりしています。

理想的な状態を探るのに時間を書けてしまうこともあるのですが、PGとフラットにアクセシビリティの細かい話ができるのは素晴らしいことだと思っています。

ツリーやドロップダウン、モーダルダイアログ、それらの複合ウィジェットなどの複雑なコンポーネントについては、WAI-ARIA(アクセシビリティを向上させるために、追加の意味を定義するHTMLの属性)の込み入った話しをすることになります。

この属性を付け外しすると読み上げがどう変わるとか、仕様的にはこの属性は付けてはいけないとか、読み上げの体験を向上するためにラベルを付けたほうが良いなどです。

また、キーボードユーザーの体験を向上するために、タブキーでフォーカスできる部分の制御や矢印キーのキー操作のサポートについてもどのようにデザインすれば良いか話し合います。

このような深い話が他チームとできるのは非常にうれしいことです。アクセシビリティチームとしても知見がかなりたまりました。

私としても、アクセシビリティのガイドラインやHTMLの仕様書を頻繁に参照したり、コンポーネントのサンプルを実装してみてスクリーンリーダーごとの動作確認をしたりと、かなり勉強になりました。

今後の課題

現在は、アクセシビリティチームが主体となり、ともすればチーム内で完結する形で進めていますが、今後の課題は、自チーム以外の人たちに裾野を広げていくことです。

社内啓発の勉強会は、毎回40から50人ほどが参加してくれていますが、アクセシビリティに興味関心をもってくれている一部の人に限られています

無理にアクセシビリティに明るい人を増やす必要性はありませんが、アクセシビリティのアの字も知らない状態というのは避けたいです。

特に今サイボウズは人が増え続けていますから、このままいくとアクセシビリティはごくごく一部の人が知っているだけの化石になってしまいます。

だからこそ来年の展望としては、より広い人に、そしてそれぞれの興味関心に合ったコンテンツを提供していきたいと考えています。

  • 新卒・中途入社向けの人へのスターターキット(定期開催)の整備
  • 特定の部署やチームに向けての、よりニーズを絞った勉強会の開催

製品のアクセシビリティ向上については、デザイナー・PM・PGとの接点を増やしていく必要があります。

デザイナーやPMがバックログを考える時、PGが実装する時などに一緒にモブに入るなど、より密接に関係性を持たなければなりません。

さもなければ、アクセシビリティは別動隊で誰か(アクセシビリティチーム)が行う特殊な要件という位置づけを超えられません。

また、現在はその都度個別にアクセシビリティチームが相談を受けてフィードバックをしていますが、主体的に判断できるようにガイドラインの整備が必要不可欠だとも感じています。

まとめ

今年、まだまだ模索中ではありますが、それぞれの製品でアクセシビリティ向上を行っていく基盤を整え、実際にいくつかリリースすることができてきました。

私が入社するずっと前から、啓発や改善は行われてきており、その積み重ねが形となって実現してきている段階です。

単発ではなく、継続的な仕組みを作り始められていることが、大きな1歩でしょう。

グループウェアを使えるかどうかは、すなわち仕事を進められるか否かに直結します。

グループウェアを使うことができて初めて、スタートラインに立てます。

だからこそ、私たちはサイボウズのアクセシビリティを重要視していますし、志を高くもって継続的に取り組んでいくものと思っています。

ツール選定者や、実際の利用者の方々にとって、サイボウズのグループウェアが有力な選択肢となるために、来年も今年以上に精進していきます。

協力者を募集しています

アクセシビリティチームは、工数が足りていません。一緒に働く仲間を募集しています

チームワークあふれる社会を作るチームに入りたいと願うすべての人が、チームにアクセスできる

この考えに共感してくれる方、一緒にサイボウズのグループウェアを必要としている人に届けませんか?

採用情報(新卒・キャリア)をご覧ください。

また、改善・要望などを受け付けています

ぜひ、製品改善のフィードバックをお寄せください

ユーザーの方々からの直接のフィードバックが、何よりのモチベーションと組織を動かす原動力となります。よろしくお願いします。

サイボウズのアクセシビリティの情報については、ウェブサイトとTwitterで発信しています。よろしければチェックしていただけると幸いです。

サイボウズサマーインターン2020 品質保証コース & 1Dayイベント開催報告

$
0
0

こんにちは、品質保証(QA)エンジニアの俊成です。今年のインターンは、新型コロナウイルスの感染拡大を考慮し、Zoomを用いてオンラインで開催しました。今年は、よりたくさんの学生さんにQAエンジニアについて知っていただくため、品質保証コースとは別に「1Dayイベント」と題した1日限りのオンラインイベントも開催しました。この記事では品質保証コースと1Dayイベントについて紹介します。

1Dayイベント

1Dayイベントは、6月28日(日)に開催しました。このイベントではサイボウズが考える品質保証と、QAエンジニアの働き方についての紹介、そして品質保証を体験できるワークショップを行いました。 インターンとは異なり、選考なしで学生であればどなたでも参加することができるイベントにしました。そうすることで、QAエンジニアに興味がある方だけでなく、エンジニア職全般に興味がある方にもQAエンジニアのことを知っていただける機会になったのではないかと思います。

cybozu.connpass.com

※当日の資料もアップロードしていますので、是非ご覧ください。


時間内容
12:50~開場
13:00~イベント開始
- 会社説明
- 品質保証って何だろう
- 品質保証ワークショップ
- QAエンジニアの働き方を紹介
17:00~終了
1Dayイベントのスケジュール


『品質保証って何だろう』では、ソフトウェアにどんな品質があるか、品質保証とは何か、品質保証をするためにサイボウズのQAエンジニアがどんな仕事をしているかお話ししました。

『品質保証ワークショップ』では、Webフォームを対象に試験設計(必要な確認を列挙し、その手順や正しい結果を書き起こす作業)を体験してもらいました。

『QAエンジニアの働き方』では、サイボウズのQAエンジニアの日常や、やりがい・大変なところ、適性などについてお話ししました。また入社後の研修についても紹介をしました。

イベントの最後には、少しでも会社の雰囲気を感じていただきたく、中継にてオフィスツアーを行いました。

参加していただいた方々のご協力もあり、たくさんの質問をいただくことができ、盛況なイベントとなりました。

インターン

次にインターンの紹介です。

品質保証コースは、9月7日(月)~9月9日(水)と9月14日(月)~9月16日(水)の2回開催しました。2回合わせて6名の学生さんに参加していただきました。 本コースは、サイボウズ製品の品質保証を担当しているQAエンジニアの業務を体験することを目的としたプログラムです。kintone開発の現場で実施されている仕様書の作成や試験、不具合の登録といった業務を体験して、サイボウズの品質保証活動について理解を深めることができます。

インターンのスケジュール
インターンのスケジュール

一日目

仕様書の作成と試験設計を行いました。 kintone開発の現場では、仕様書はプログラマとQAエンジニアがモブ(みんなで一つの画面を見てディスカッションしながら進めていく形式)で作成しています。インターンではインターン生とメンターがモブで仕様書を作成しました。仕様の検討や記述に漏れがないことを意識して上手く作成することができました。

次に、仕様書に記載した動作や、ユーザーがしそうな操作から試験設計をしました。(試験設計したドキュメントのことを試験仕様書と言います)

試験仕様書
試験仕様書

二日目

試験仕様書をインターン生同士でクロスレビューしました。インターン生が作成した試験仕様書の完成度の高さにレビューをしたメンターも驚きました。レビューが終わると、試験仕様書をもとに試験を実施しました。

試験の後には、不具合の登録を行いました。不具合の登録には「不具合情報の共有」「不具合の状態管理」「不具合の分析」といった目的があります。品質保証の大切な仕事の一つです。インターンでは、予めメンターが用意していた怪しい挙動に対して、不具合なのか製品の仕様なのかを調査し、不具合であれば登録をしてもらいました。インターン生とメンターでわいわい行いました。

不具合登録のワーク
不具合登録のワーク

三日目

試験の自動化にチャレンジしてもらいました。試験を自動化することで、素早く正確に、繰り返して試験を実行することができ、作業効率を高めることができます。このインターンでは、試験自動化の技術だけではなく、なぜ試験を自動化するのか、どのような試験を自動化するべきなのかといった試験自動化の基本的な考え方も学んでいただきました。また、Selenium IDEを用いて実際に手を動かし、簡単な自動化も体験してもらいました。

試験自動化にチャレンジ
試験自動化にチャレンジ


インターン期間中は、品質保証について学びながら業務を体験するだけではなく、オンライン開催でも社内の雰囲気を感じていただけるようにメンター以外のQAエンジニアとのコミュニケーションをとる時間を設けました。 三日目の昼には、社長である青野とのランチミーティングを行いました。この時間は、青野からのお話だけではなく、気になっていることを直接自由に質問できるため毎年好評のイベントです。

ランチミーティングの様子
ランチミーティングの様子

インターン参加者の感想

インターンシップ中、気さくにあだ名で呼んでくださったり、終始和やかな雰囲気で進めてくださることで、質問もしやすい雰囲気でとてもありがたかったです。 今回お世話になった品質保証コースのメンターさん、インターンシップに関わってくださった人事の皆さん、もちろん社長の青野さん、その他いろいろ楽しく、ためになるお話をしてくださったすべての皆さん。 本当にありがとうございます。とても楽しい3日間でした。


オンラインインターンでどこまでQAのことを理解できるか、サイボウズのことを理解できるか心配でしたが、終わってみればその心配も無用だったと感じます。 QAの業務については、知識がほぼ0に近い状態から始まりましたが、一連の業務を手を動かしながら体験することができ、大変な部分も楽しい部分も見つけることができました。 サイボウズ全体についても、10名近くの社員の方と雑談したり、社長とのランチタイムを通して、どのような会社・組織なのか感じることができました。


インターン前は、QAエンジニアが何をするのか正直よくわかっていなかったのですが、今回インターンをして、業務内容が明確になりました。それぞれの業務について、具体的な内容、目的、難しい部分、メリット/デメリット、やりがいなど、たくさんのことを教えていただき感謝しています。 また、社員さんと交流することができる時間もたくさん用意していただけた(青野さんとのランチ会はびっくりしました笑)ので、オンラインではありますがチームワークを大切にする雰囲気等を肌で感じることができたなと思っています。 3日間という短い間でしたが、お世話になりました。ありがとうございました。


メンターの方、人事の方、メンターの方とのお話で質問責めにしてしまったのですが、それに対し、丁寧に知りたいことを教えてくださる社員の方々に感動しましたし、学んで得たことはすぐに自分の言葉でアウトプットして確認をさせていただけたり、学びを常に意識して、得たこと、感じたことを最大限に享受できた気がします。 製品を大事にしている会社だからこそ、1つのシステムの品質保証と向上をとことん極める姿勢というのは、次々とお客様に合わせてシステムをつくる会社では経験できない境地なのかと感じましたし、QAの奥深さや極めていく面白さを改めて感じました。


他のインターンシップでは体験できないことも体験した貴重な時間だったと感じます。 3日間という短い期間でしたが、みなさんと仲良くなれ、品質保証について詳しく学ぶことができました。業務だけではなく、社の文化も合わせて体験できたことがとても経験として大きかったです。 今回の経験を踏まえて、これから自分がどうしていきたいのかを考えることができ、実りあり・密度あり・笑顔の絶えない3日間でした。 ありがとうございました!!


本音で話せてよかったです!!

最終日の懇親会の様子1最終日の懇親会の様子2
最終日の懇親会の様子

まとめ

今年は従来のインターンをオンラインにしたり、オンラインでの1Dayイベントを開催したりという初めての取り組みに挑戦しました。 オンラインでの開催となることで、会社の雰囲気や緊張感といった知識以外の体験を伝えることが難しくなるのではないかと危惧していました。しかし、参加していただいた皆さんが自発的に質問をしてくださり、社員とのコミュニケーションにも積極的に取り組んでいただけたおかげで、その心配も杞憂に終わったと思っています。 参加者の皆さんには、得た学びをこれからの活動に役立ててもらえると嬉しい限りです。皆さんの今後の活躍を期待しています!

Flaky Testとの戦い

$
0
0

こんにちは。Necoチームの池添です。 最近にわかにFlaky Test界隈が盛り上がりを見せているようです1,2,3。 この流れに乗じてNecoプロジェクトにおけるFlaky Testとの戦いについて紹介したいと思います。

Necoプロジェクトにおけるテスト

Necoプロジェクトでは、自社データセンター上のインフラ構築と運用を自動化する仕組みを開発しており、サーバのプロビジョニングから、Kubernetesクラスタの構築、Kubernetesクラスタ上で動くさまざまなアプリケーションのデプロイ、各種ソフトウェアやOSのアップグレードなどを自動化しています。

本プロジェクトでは「Test Everything」を設計原則のひとつとしており、VMを利用した仮想データセンターの仕組みを用いて、開発した自動化の仕組みを毎日テストしています。

詳しく知りたい方は下記の記事もご覧ください。

blog.cybozu.ioblog.cybozu.io

Flaky Testとは

Flaky Testとは、実行結果が不安定なテストのことです。 コードを変更していないにもかかわらず、実行するたびにテストが成功したり失敗したり結果が変化するため、原因が追及しにくく非常にやっかいな問題です。

Necoプロジェクトのテストでは10台以上のVMを利用しており、Kubernetesクラスタ上には数十のアプリケーションがデプロイされていて、サーバの再起動やアプリケーションのアップグレードを含む様々なテストを実施しています。 このようなテストでは、結果が不安定になってしまうことも想像に難くないでしょう。

Flaky Testがあると何が起こるのか

Flaky Testが常態化すると、テストが失敗したときに失敗原因を精査せず「また不安定系か。とりあえずRerunしよ。ポチッ」という対応になりがちです。

しかしこのような対応をしてしまうと、本来テストで発見すべき不具合を見逃すことにもつながります。

また、我々のプロジェクトでのテストは1回に1時間半程度要するため、再実行のための待ち時間が発生し、開発効率の低下につながります。

さらに、テストの実行にはクラウドのインスタンスを利用しているので、再実行のたびに費用がかかってしまいます。

Flaky Testを完全になくすことは難しいのですが、このような問題を回避するためにもFlaky Testを減らす努力を怠ってはなりません。

過去3ヶ月間の分析

それでは、我々のプロジェクトにおける、過去3ヶ月のmasterブランチでのテスト成功率を見てみましょう。

masterブランチでのテストは、いったんフィーチャーブランチでテストが通った後に実行されるものであるため、ここでの失敗はFlaky Testの可能性が高いと言えるでしょう。

success rate
テスト成功率

このグラフをみると、10月頃のテスト成功率が大きく下がっていることが分かると思います。 この頃我々は、テストの不安定さに非常に悩まされていました。

10/19週に関してはテスト成功率こそ100%ですが、試行回数そのものが非常に少なくなっています。 これは、テストが不安定なためmasterブランチにマージすることすら困難な状況になっていたからです。 テストが不安定になるとmasterへのマージ回数も減り、開発効率が低下してしまうことが窺えます。

そこで10月から11月にかけてFlaky Testの改善に力を入れました。 その結果、12月にはテスト成功率90%程度まで改善することができました。

分類

過去に我々が直面したFlaky Testの要因を分類して紹介したいと思います。

外部要因

まず1つめは外部要因によるものです。

我々のテストは、パブリッククラウドのインスタンスを利用して実行しています。 そのためたくさんのテストを同時に実行すると、インスタンスのクォータ制限に引っかかったり、利用しているリージョンでのインスタンス不足により、テストが実行できなくなるケースがあります。

また、我々が開発しているプラットフォームは日々成長しています。 デプロイするアプリケーションの追加に合わせて、インスタンスを増強してやらないとリソースが逼迫してテストが不安定になります。

さらに仮想データセンターを実現するために、1つのインスタンス上に複数のVMを立ち上げる方式(Nested VM)をとってます。 このNested VMにもテストを不安定にさせる問題があります。 この問題に関しては社内のLinuxカーネル有識者と協力して調査をおこなったのですが解決には至っていません。 今後はクラウド環境上でのNested VMを利用したテストを止め、自社データセンターのベアメタルマシン上でテストすることを計画しています。

我々のインフラはGitHubやコンテナレジストリ、パブリッククラウドのDNSサービスや、証明書発行サービスなどに強く依存しています。 これらのサービスに障害が発生した場合にも、我々のテストは失敗することになります。 複数のクラウドサービスを利用してフェイルオーバーすれば、障害に備えることもできますが、このあたりは必要とする可用性とコストの兼ね合いになってくるでしょう。

利用しているソフトウェアの不具合

我々のプラットフォームでは、Kubernetesをはじめ、Rook, ArgoCD, Contourなど、非常にたくさんのOSSを利用しています。 Flaky Testの要因として、これらのソフトウェアの不具合が見つかることも多々あります。

例えば、最近では下記のような問題が見つかりました。

  • Kubernetesのクライアントにおいて、巨大なリソースを更新する際に必要以上に時間がかかってしまいタイムアウトする問題
  • kube-proxyにおいて、IPVSモードでロードバランシングアルゴリズムにsource hashingを選択した際、古いIPVSの設定が残ってしまい、まれにルーティングに失敗する問題
  • ArgoCDにおいてNetworkPolicyを適用する際に、ArgoCDの同期処理が停止してしまう問題

このような問題が見つかった場合は、ソフトウェアの提供元にIssueやPRを発行することになります。 PRが取り込まれるまでの間は、ワークアラウンドを見つけたり、パッチをあてビルドし直したソフトウェアを利用したりして、問題を回避します。

自分たちのアプリケーションの不具合

自分たちが開発しているアプリケーションの不具合ももちろん見つかります。 各ソフトウェアは、単独でのテストは実施していますが、仮想データセンター上にデプロイして他のソフトウェアと組み合わせることによって初めて発覚する不具合もあります。

最近では、下記のような問題が見つかりました。

  • Kubernetesクラスタを管理するソフトウェア(CKE)のリーダーが切り替わった際に、CKEが管理するetcdの設定が変わってしまい、負荷状況によってetcdがタイムアウトしてしまう問題
  • Kubernetes上のPodのlivenessProbeの設定がシビアすぎるために、負荷状況によってPodが起動する前にkillされてしまう問題
  • ArgoCDによるアプリケーションのデプロイ時に、アプリケーション間の依存関係を考慮していなかったために、タイミングによってデプロイに失敗してしまう問題

仮想データセンター上で複雑な構成のテストを実施することで、アプリケーション単独のテストでは見つけられない不具合も発見できており、有意義なテストが実施できていると言えるでしょう。

Flaky Testとどう立ち向かうか

次に、我々がどのようにFlaky Testの対策をおこなっているのかをご紹介したいと思います。

Be Declarative

Necoプロジェクトにおけるもうひとつの重要な設計原則として「Be Declarative」があります。 これはKubernetesの特徴でもあり、あるべき理想の状態を宣言しておくと、その状態に収束するようにソフトウェアが自動的に調整するというアーキテクチャスタイルです。 Necoプロジェクトでは、サーバのプロビジョニング、Kubernetesクラスタの構築、サーバの再起動オペレーションなど、様々な運用をDeclarativeな仕組みで自動化しています。

Declarativeなアーキテクチャスタイルで実装されたシステムは、不安定な要素が含まれていたとしても自動的に復旧をおこないます。 テストで不安定な問題が見つかった場合は、Delcarativeな仕組みで解決ができないか考えてみるとよいでしょう。

ただし、最終的に理想の状態に収束したとしても、途中で重要な問題が発生している可能性もあります。 このような問題を見逃さないためにも、メトリクスの収集・分析・アラートは正しく設定しておく必要があります。

継続的な改善

Flaky Testは完全になくすことは難しいため、継続的な対応が必要となります。

Necoプロジェクトでは3人程度のチームが4つありメンバーをローテーションしているのですが、そのうちの1つのチームがFlaky Testの修正を担当するようにしています。 CIテストの失敗が発生するとSlackにアラートが飛んでくるので、テストを実行しているインスタンスに乗り込んで原因を調査するようにしています。 不具合はなかなか再現しなかったり、再現してもすぐに消えてしまったりするものもあるので、素早い対応が重要です。 オンコールの障害対応と似ていますね。

また原因の調査にはログが重要です。CIでは必要そうなログはとりあえず全部保存しておくのがよいでしょう。

お金で解決

仮想データセンターをテストするためには非常にたくさんのリソースを必要とします。 このリソースをケチるとテストが不安定になりがちです。

お金で解決できる問題であれば、お金を使うのがいいでしょう。

まとめ

本記事ではNecoプロジェクトにおけるFlaky Testとの戦いの記録を紹介しました。

我々は2年以上このFlaky Testと戦い続けてきましたが、銀の弾丸はなく、愚直に向き合うしかありません。

本記事で紹介した内容が、皆様のFlaky Testとの戦いに少しでも役に立てば幸いです。

ファンを増やす・交流に取り組むチームで同士でコラボしました〜さくらインターネット「コミケユニット」さん編〜

$
0
0

コネクト支援チームの西原です.
 
11月13日の金曜日に「さくらの夕べオンライン〜サイボウズさんといっしょ!〜」を開催しました.
 
開催当日の様子は以下の動画からご覧いただけます.今回,ご協力いただいたのはさくらインターネット「コミケユニット」の皆さんです.コミケユニットとは?????という方は動画の 17:40 からの前佛さんの発表にてバッチリ説明されていますので,そちらをご覧ください.

www.youtube.com

開催報告はさくらのナレッジに掲載されていますので,ここでは開催に至るまでの昔話をお話します.

knowledge.sakura.ad.jp

開催に至った思い出話

コロナ禍の渦中 7 月か 8 月頃に社内(のZoom)でコネクト支援チームの風穴さんと西原の二人でザツダン時間を満喫中に出た話が発端でした.オフラインでのイベントができていない中,大変前向きでいい話だなと思った記憶があります.
 
偶然 LOCAL*1の定例会後の時間で,お世話になっているさくらインターネットの三谷さんがいらしたので雑におはなししてみたところ「ああいいね」くらいでふわーっと過ぎ去り,言い出しっぺの自分も忘れかけていた 9 月の末にさくら側も乗り気だからやるべという連絡がやってきて,実際に動くことになったのです.

社内のザツダンで出た動機

  • 社外の人たちと一緒にイベントやりたい
  • 別の会社の同じようなことやってる人たちと話す時間をとりたい

という 2 点が主の動機でした.これを達成するためには...... と動き始めます.

社外の人たちと一緒にイベントやりたい

 
オンライン化の流れの中でオフラインのときほど複数社での共同イベントが多くないのではないか?という肌感覚があったことにはじまります.コネクト支援チームの久宗さんと西原の 2 人で,毎週木曜日に今のイベントを知るための会を開いているのですが,その中でも
 

  • イベント数自体が前年度に比べて減少傾向であること
  • 規模が大きく YouTube での配信があるイベントが参加者数も伸びていること
    • 多くの場合ある一団体が開催しているイベントだったように思います
  • 小さなイベントや Zoom で展開しているイベントが苦戦しているように見えること
    • これは交流を主目的にしたイベントが苦戦しているとも見えた

 
という状況がなんとなく掴めてきました.自分の家でテレビ的につまみ食いできるコンテンツであることが求められ,家という生活優勢の空間で拘束されることが好ましくないという思いの現れのようにも感じました.
 
結果として,コンテンツを持っているところやキレイに配信する技を持ったところに参加者が集中し,しかし懇親会は閑散としオフラインイベントのような熱の高まりや密な交流を再現するに至らず,という状況が出来上がったのかなと推察します*2
 
という個人的な思いの高まりから,オンラインイベントでの人の動きや交流が少ない状況を打ち壊すために,自分たちだけで完結しないイベントを開き,運営側だけでもイベント企画から実施までの交流時間を確保しつつやるのはどうかと考えました.

別の会社の同じようなことやってる人たちと話す時間をとりたい

ということでイベント内のコンテンツをどうするか考えました.技術ネタを揃えてもいいけれど自分たちのこれからも踏まえて「コネクト支援チームと同じような活動をしているところと知見をぶつけ合いたい」という話に落ち着きました.我々コネクト支援チームの活動はざっくりまとめると以下のとおりです.
 

  • サイボウズのことを IT エンジニアの活動を通じて知ってもらう
  • 社内の IT エンジニアが外で発表したり活動したりできる場を拓く
  • 社内外問わず IT エンジニアやそれを目指す人たちが自由に学び,活動する場を支援する

 
実は IT コミュニティ等に深く入って活動している年数で考えると,こちらはそう長いメンバーが揃っているわけでもないので胸を借りるつもりでいくという方針が立てられました.経験量の多い人たちの話を聞きに行って修行するイベントと位置付けることで,我々の学びになるのは明白です.
 
さっと頭に思い浮かんだのがさくらインターネットさんでした.IT コミュニティ歴が長い人たちが活動していて,長らく Open Source Conference にも出展している姿を横で見ている限り IT コミュニティにも理解*3があり......我々が勉強したい全てを持っており,しかも,偶然にも西原が長らくお世話になっている人の中にさくらインターネットの方がいるという完璧な状況をみすみす見逃すわけもなく,今回のイベントに至ったのです.

会を終えて

開催報告にもあるとおり,メインコンテンツでは素晴らしい知見をたくさんいただき,これは大変よい会だったと自画自賛したい気持ちでいっぱいです(もちろん素晴らしいのは他の皆さんのご協力あってのことです).後の懇親会では参加者の皆さんも交えて意見交換できて大変有意義な時間となりました.話の種は,地方での活動展開をどうするか,やるだけやるって形じゃなくて続くようにする苦労,社内の IT エンジニアがこういった活動に入ってきてくれるにはどうすればいいだろうか,などなど答えの出ないものもたくさんありましたが,これからの活動の糧になったことは間違いありません.
 
実はこの会の一番の収穫は懇親会ではないかと勝手に思っています.林さんが取り仕切られたこの懇親会の運びは大変優れたもので,オフラインの頃の懇親会と同じように「強い意志でついていく」が実現されていたのです.オフラインの懇親会だと二次会に「ついていく」つまり,流されるとすれば一次会で終わりだったのですが,従来のオンライン懇親会だと二次会に「移行する」ので,流されるとすれば一次会では終わらないのです.
 
自分から言い出せない人が苦しまないという観点においては,オフライン懇親会のように「自ら選択して二次会に入る」形は非常によいものだと感じました.いや,オンラインでも同じだろう,と考えるかもしれませんが「自ら選択して二次会に入る」のと「自ら選択して一次会で抜ける」のはエライ違いでないかなと思っています.
 
この絶妙な懇親会の試行錯誤はまだ続いており 12/25(金) 19:00 開催の「さくらの聖夜2020」でも堪能できるようです.これは参加待ったなしですね.

sakura-tokyo.connpass.com

また試合したい

今回は法林さんがいらっしゃるので,試合形式以外には考えられなかったのですが,大変楽しいものでしたので,これはまたやりたいと思っているところです.ある技術分野に絞っての 5 vs 5 もいいですね.今回の内容と近いところで,別の企業さんとやるのもいいですね.社内外でこの分野でどうだ!とかコネクト支援チームと似たようなことやってるから一緒にイベントどうですか!とかそういうお声掛けをいただけると,大変ありがたい限りです.ぜひぜひお声掛けください!
 
ということで,いい試合だったという記録でした.

サイボウズのコネクト支援チームとさくらインターネットのコミケユニットの集合写真
今回はチームの集合写真を載せました.サイボウズ側は題して「ろくろボルテージ」です.

*1:北海道の IT コミュニティのために活動する一般社団法人です.

*2:地方等でオフラインだろうと新規開拓に苦戦しているところは,オンラインになったところで状況変わらずというところかもしれません.ちなみに,自分の古巣である旭川や富良野の勉強会では,数年ぶりに参加できましたという懐かしい顔ぶれがちょこちょこ現れることがあり,オンラインの利点を感じているところです.しかし,これも元々つながりがある人たちに限るので,ある種貯金を切り崩しているような感じです.

*3:西原の守備範囲かつ独断と偏見で補足すると,実に企業が IT コミュニティに入っていくというのは非常にデリケートなもので,元々ある文化圏をいたずらに破壊しないようにやらねばならんのです(そう思っているのは自分だけかもしれませんが......).自分自身が 2010 年頃から地方で草の根的に IT コミュニティに参加したり,運営したりする中で感じたことを今も忘れずに活動するよう心がけています.この純粋に技術と向き合う会である IT コミュニティをビジネスや採用の場として使おうという流れが加速した頃,これはイカンということで「採用活動はご遠慮ください」などと言った注意書きを書かざるを得なくなったコミュニティもたくさんあった記憶があります.
 
その後「コミュニティ」という言葉がふわっとした状態のまま,多様な解釈を抱えて広まっていきました.そうしてコミュニティという「手法」が広まり 90 年代あたりから続く空気感での IT コミュニティとはちょっと違ったコミュニティが増えたようにも感じます.この絶妙な空気感の違いを理解しながら活動するにあたり,さくらインターネットさんのコミケユニットの皆さんの知見は大変貴重なもので,お話を聞かないわけにはいかないと感じたのです.

Neco の宣言的なサーバー全台再起動の仕組み

$
0
0

こんにちは、Neco チームの 阪上です。皆さんは Kubernetes クラスタのマシン全台の再起動オペレーションに丸一日かかったことはありますか? 手順を確認して雑談するだけで時間が過ぎて行く…穏やかですが、物足りなさも感じます。

そこで今回は、Kubernetes クラスタのサーバー全台を自動で宣言的に再起動する仕組みについて解説したいと思います。

背景

サイボウズでは自社データセンターでアプリケーションを運用するために Necoというインフラ基盤を開発しています。 ベアメタルで k8s クラスタを運用していると、ファームウェアの更新やセキュリティ対応のため、クラスタの全てのマシンを再起動したいことがあります。 しかしサイボウズのクラウド基盤 cybozu.comはメンテナンスを除いて 24 時間 365 日無停止での稼働が前提のため、再起動のためにサービスを停止することはできません。

これまではマシンを複数のグループに分割して手作業で少しずつ再起動していたのですが、社内で Neco の活用が広がるにつれて色々なサービスが動作するようになり、手動での再起動オペレーションが難しくなってきました。そこで我々は Neco の k8s 管理ツール CKEにマシン全台の再起動機能を実装することにしました。

Neco はなるべく人手を介さない自動運用を目標としているため、宣言的な設計になっていることが重要です。このことから、仕様は以下のように決めました。

  • クラスタの管理者は全台再起動を一言宣言するだけ。全ては自動的に実行される。
  • クラスタ上のサービスに影響が出ないようにする。
  • 管理者は再起動の進捗を監視しない。トラブル発生時はアラートの形で自動的に通知される。
  • 多少のサーバーが再起動に失敗しても処理を進める。故障などの理由で起動に失敗したサーバーがある場合は管理者にアラートで通知し、都度対応してもらう。

実装

処理の大枠の流れは次の通りです。

  1. 管理者が neco reboot-workerと入力する。
  2. neco reboot-workerコマンドが CKE の再起動キューにサーバーを二台ずつ登録する。
  3. CKE が再起動キューを定期的に確認し、再起動待ちのサーバーがあれば順番に再起動する。

Neco の k8s クラスタは用途の異なる二種類のサーバー (compute, storage) で構成されています。 これらは再起動時に相互に干渉しないので、各一台ずつを同時に再起動します。

重要なのは 3 番目のステップで、サーバーを再起動する前にサーバー上の Pod を全て止めてもサービスに影響しないか確認する必要があります。 Kubernetes にはノードをメンテナンスするための標準機能があるので、CKE ではこれを使って全台再起動を実装しました。

Kubernetes のノードメンテナンスの仕組み

Kubernetes には、ノードを再起動するなどのシナリオでノード上の Pod を安全に削除するための標準機能があります。 ここではその使い方を解説します。

PodDisruptionBudget

k8s クラスタに参加しているノードを再起動するには、ノード上のアプリケーションを全て止める必要があります。 そのためアプリケーション開発者に Pod を冗長化してもらい、メンテナンスのために削除してもよい Pod の数を宣言してもらいます。 この宣言は PodDisruptionBudget (PDB) というリソースで管理します。

たとえば Serviceのバックエンドとして 3 個の Pod を作成し、1 つをクラスタ管理者が削除しても良いことにすると、メンテナンス中でも 2 個の Pod が動作することになります。メンテナンス中の耐障害性を確保したい場合は、その分の余裕も考える必要があります。

Pod の Delete と Evict

Pod の削除には Delete と Evict という二つの操作が用意されています。Delete (kubectl delete <pod>) は単に Pod を削除しますが、Evict を使うと削除後に PDB を満たせる場合のみ Pod を削除します。 Eviction は通常ノードのメンテナンスのために使われるので、ノード上の Pod を全て Evict する kubectl drain <node>というコマンドが用意されています。

削除した Pod が同じノードに再スケジュールされるのを防ぐため、kubectl drainを使うとノードには Unschedulable フラグがセットされます。 ノードのメンテナンスが終わったら、kubectl uncordonコマンドを使ってフラグを外します。

クラスタに Node 1 から 4 まで 4 台のノードが含まれており、サービスのバックエンドとなる Pod が Node 1 から 3 の 3 台に乗っている。クラスタ管理者が Node 1 の Pod を Evict しようとしている。

上の図では、クラスタの管理者が Node 1 の Pod を削除しています。Pod を 1 つだけ消してもよい設定の場合、このサービスの Pod はこれ以上削除できません。 したがって Node 2 の Pod も削除したい場合は Node 1 か Node 4 で Pod を起動する必要があります。 PDB の計算では Ready になっている Pod しかカウントされないため、Node 2 の Pod を Eviction で削除するには新しい Pod の起動完了を待つ必要があります。

サービスのバックエンドとなる Pod が Node 2 から Node 4 の 3 台に乗っている。Node 4 の Pod は Not Ready である。クラスタ管理者が Node 2 の Pod を Evict しようとしているが、失敗している。

PDB の書きかた

PDB の適切な設定はアプリケーションごとに異なるので、リソースとして宣言します。

apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:name: sample-pdb
spec:maxUnavailable:1selector:matchLabels:app: sample-app

停止してもよい Pod の数は色々な形で宣言できます。

  • maxUnavailable: 1 1 つだけ Pod を止めてもよい
  • minAvailable: 4 4 個以上の Pod が Ready であればよい
  • minAvailable: 80% Pod が全体の 80% 以上 Ready であればよい

PDB の対象となる Pod の数は Pod の ownerReferencesに指定された DeploymentStatefulSetのレプリカ数で決まります1

適切な PDB の内容は主にアプリケーションがステートフルかステートレスかによって異なります。 ステートフルアプリケーションの場合は通常 maxUnavailableを 1 にするか、minAvailableに分散合意に必要な quorum の数を指定します。 ステートレスアプリケーションの場合は負荷に応じて適切なパーセンテージを指定することができます。

PDB の設定の詳細は Kubernetes の公式資料に書かれていますのでご参照ください。

Eviction API

ノードからの Pod の退避は kubectl drainで実行できますが、より fine-grained な操作が必要な場合は Eviction APIを使うことができます。 Eviction API では、Pod 名を指定して個別に Evict することができます。

{"apiVersion": "policy/v1beta1",
  "kind": "Eviction",
  "metadata": {"name": "sample-pod",
    "namespace": "sample-ns"
  }}

CKE では、開発環境のアプリケーション Pod を強制的に削除するために Eviction API を活用して Delete と Evict を使い分けています。 Go からは Evict関数として呼び出せます。

import (
    policyv1beta1 "k8s.io/api/policy/v1beta1"
    metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

clientset.CoreV1().Pods(namespace).Evict(context, &policyv1beta1.Eviction{
    ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: namespace},
})

モニタリングとアラート

再起動の進捗を知るため、Prometheusのメトリクスとアラートを設定しました。

  • 再起動キューのエントリ数をメトリクスにして Grafanaで確認できるようにしました。
  • キューのエントリ数が 1 時間以上減らない場合はアラートを上げるようにしました。 これは通常 PDB 違反を意味しています。たとえば、StatefulSet を構成する Pod が乗ったノードが故障してコンテナを再作成できなくなり、そのまま他の Pod が乗ったノードを再起動しようとしている場合などです。
  • ノードが長時間 NotReady の場合はアラートを上げるようにしました。

再起動実施中の Grafana ダッシュボードの様子。キューのエントリ数が 59 から 0 まで 12 時間かけて減少している。

こちらが実際の再起動の様子です。順調に処理できているのが分かります。

便利な機能

全自動とはいえ、夜間にサーバーを順次再起動していくのは不安なこともあります。 またトラブルが発生した時には処理を中断したいこともあるかもしれません。 そのため、管理者がコマンドを入力するとキューの処理を一時停止することもできるようにしました。

結果

これまで丸一日かかっていた全台再起動の手順が neco reboot-workerと入力するだけ、5 秒で終わるようになりました。 進捗を監視する必要もなく、操作後は他のタスクに集中できるようになりました。 このシステムを使って何度か全台再起動を実施しましたが、特に問題なく無事に再起動できています。 今後、サーバー台数が増えてきたらラック単位での再起動を検討する予定です。


大規模 Closure Tools プロジェクトに Prettier を導入するまでの道のり

$
0
0

こんにちは、フロントエンドエキスパートチームの鈴木(@__sosukesuzuki)です。

弊社のサービスである kintone では、コードのフォーマットを ESLint のみで行っているためプロジェクト内でコーディングスタイルを統一しきれていないという問題を抱えていました。

そこで opinionated なコードフォーマッター Prettierを導入し、コードベース全体でコーディングスタイルを統一するための支援をフロントエンドエキスパートチームで行いました。

2011 年にローンチされた kintone では、フロントエンドの大部分が Closure Toolsを使って開発されています。Closure Tools は型の指定方法やクラスの定義方法などに独自システムを採用しており、現在の JavaScript のエコシステムや仕様と一部乖離しています。そのため、Prettier 本体が Closure Tools に対応していない部分があり、Prettier を kintone の開発にそのまま導入することができませんでした。

こういった背景から、今回チームの活動として Prettier に Closure Tools 対応を入れる活動を行いました。 Prettier は OSS として公開されていますので、 Closure Tools 特有の問題を解決するための PR を出して取り込んでもらいました。その結果、kintone に Prettier を導入することができるようになりました。

今回は、Closure Tools プロジェクトにコードフォーマッター Prettier を導入するにあたって直面した課題と、それに対してどのように対処していったのか、それら対処を含む Prettier 2.2のリリースについて紹介します。

Closure Tools 環境での開発体験の向上の手段として、そして OSS への向き合い方として参考になれば嬉しいです。

導入するにあたって直面したバグ

まず試しに Prettier を kintone のソースコードに対して実行してみましたが、いくつかの問題が発生しました。問題のすべてが Closure Tools 特有のコードスタイルによるフォーマットのバグでした。

1. 型が壊れる

Closure Tools では JavaScript のコードに型を付けることができます。TypeScript や Flow のように型を明示するための構文があるわけではなく、JSDoc のコメントで記述します。

/**
 * @type {string}
 */
const foo = "FOO";

また次のようにしてキャストをすることができます。このとき、式を囲む括弧は必須です。

const foo = /** @type {string} */ (bar);

しかし当時の Prettier ではコメントとキャスト式の間に改行があると、型定義が壊れてしまうバグがありました。

// Input
const foo = /** @type {string} */
  (bar);

// Output
const foo /** @type {string} */ = bar;

このフォーマットには次の問題があります。

  • コメントが =の左側に移動している。
  • barを囲む括弧が消えている。

kintone のソースコード中にも、このバグによって型が壊れてしまう部分が存在しました。

なのでこのフォーマットのバグを修正しました。

https://github.com/prettier/prettier/pull/7709

この修正により、現在は次のようにフォーマットされるようになっています。

// Input
const foo = /** @type {string} */
  (bar);

// Output
const foo = /** @type {string} */ (bar);

このバグは Closure Tools 特有というわけではなく、JSDoc スタイルで型を記述できる処理系であれば起こってしまう問題です。なのでもしかしたら TypeScript の JSDoc コメントをよく使っている人はこのバグに出会ったことがあるかもしれません。

2. 長いスーパークラスが折りたたまれる

Closure Tools には通常の JavaScript とは異なるネームスペースの概念があります(https://developers.google.com/closure/library/docs/introduction#names)。

ドットで区切られたパスでネームスペースを定義します(JavaScript の構文上はただのメンバー式+代入式です)。たとえば、/abc/def/ghi.jsというパスのファイルのネームスペースはabc.def.ghiと定義します。

goog.math.clamp = function(value, min, max) {
  return Math.min(Math.max(value, min), max);
};

このように関数を定義した場合、次のようにして使うことができます。

var clampedValue = goog.math.clamp(2, 3, 4);

このネームスペースの機能とクラスを組み合わせて使うと、例えば次のようなコードを書くことがあります。

aaaaaaaa.bbbbbbbb.cccccccc.dddddddd.eeeeeeee.ffffffff.gggggggg2 = class extends aaaaaaaa.bbbbbbbb.cccccccc.dddddddd.eeeeeeee.ffffffff.gggggggg1 {
  method() {
    console.log("foo");
  }
};

これは極端な例ですが、つまり「代入式の左辺がメンバー式で、右辺がメンバー式をスーパークラスとして持つクラス式」になっている状態です。

このとき以前の Prettier は次のようにかなり中途半端な位置に改行を入れるようにフォーマットをしていました。

aaaaaaaa.bbbbbbbb.cccccccc.dddddddd.eeeeeeee.ffffffff.gggggggg2 = class extends aaaaaaaa
  .bbbbbbbb.cccccccc.dddddddd.eeeeeeee.ffffffff.gggggggg1 {
  method() {
    console.log("foo");
  }
};

aaaaaaaの後ろに改行が入っています。

これでも意味的には何も変わらないのですが、単純に見た目が良くないのに加えてネームスペースの名前で grep することができなくなってしまうという問題がありました。たとえば、aaaaaaaa.bbbbbbbb...gggggggg1が参照されているところを探すために grep をしたときに、Prettier により改行されると grep 結果に含まれなくなってしまいます。

そこで、スーパークラスの始まりで改行して括弧で囲むように修正をしました。

https://github.com/prettier/prettier/pull/9341

Prettier 2.2 以降では上記のコードは次のようにフォーマットされます。

aaaaaaaa.bbbbbbbb.cccccccc.dddddddd.eeeeeeee.ffffffff.gggggggg2 = class extends (
  aaaaaaaa.bbbbbbbb.cccccccc.dddddddd.eeeeeeee.ffffffff.gggggggg1
) {
  method() {
    console.log("foo");
  }
};

見た目も整っていて、grep を阻害することもありません。

リリースをしないと使えない

問題を解決する Pull Request がマージされていても、それを含むバージョンがリリースされていなければプロダクトでは使うことができません。

なのでリリースを行う必要があります。

偶然にも私は Prettier のメンテナーでリリースを担当しているので、リリースとそのための作業を業務時間に行い迅速にリリースをすることができました。

ここから先は前述した Closure Tools 特有の問題たちとは別で、Prettier 2.2 をリリースするときの話になります。

作業を整理する

Prettier では GitHub Milestones を使ってタスクを管理しています。バージョンの名前がついたマイルストーンがあって、メンテナーたちが各々やりたい作業を各マイルストーンに入れていきます。

普段のリリースでは、「そろそろリリースしたいよね〜」という話になったときに該当バージョンのマイルストーンに入っているタスクを整理します。

今回は、リリースしたくなったタイミングですでにいくつか重要な機能がマージされていたので、マイルストーンに入っているタスクをすべて 2.3 まで延期することになりました。

大体の場合、このようなタスクはよっぽどクリティカルでない限り言い出しっぺがやらないと永遠にやられないので、今後も延期されるんじゃないかな...と思っています。

ちなみに、マイルストーンは GitHub で誰でも見ることができます。

https://github.com/prettier/prettier/milestones

破壊的変更をリバートする

なぜか master に破壊的変更がコミットされていたのでそれをリバートします。

コミットされてからだいぶ時間がたっていたのでキレイにリバートするのがとても大変でした(しかも全然楽しくない)。

https://github.com/prettier/prettier/pull/9500

チェックリストを作る

GitHub の wiki にチェックリストの雛形があるので、それをコピーして issue を作成します。

https://github.com/prettier/prettier/wiki/Release-Checklist

https://github.com/prettier/prettier/issues/9549

後は、基本的にこのチェックリストに従ってリリース作業を行います。

リリースブログを用意します。

https://prettier.io/blogに投稿するための記事を用意します。

https://github.com/prettier/prettier/pull/9589

Prettier のリリースブログはかなりの分量がありますが、そのほとんどをスクリプトで自動生成しています。

というのも、各 PR に詳細なチェンジログを含めるようにしているのでそれらをスクリプトで結合するだけでブログ記事ができます。というかむしろ、リリースブログの Markdown ファイルを直接いじってはいけません。

他のプロジェクトで新しい Prettier を実行してみる

Prettier は大量のスナップショットテストを持っていますが、もちろんそれらは完全ではありません。

なので、すでに Prettier を使っている OSS プロジェクトに対して新しい Prettier を実行し問題が発生しないかをチェックする必要があります。

2.1 のリリースまでは次の工程をすべて手動で行っていました。

  • 対象のリポジトリをフォークする
  • Prettier のバージョンを最新にする
  • Prettier 実行スクリプトを調べて実行する
  • コミットする
  • PR を出して差分を共有する

この一連の作業を各リポジトリごとに毎回行う必要がありました。

これは大変面倒くさいので、GitHub Actions を使って楽に確認できるツールを作成し、今回のリリースからはそれを使うようになりました。 Actions を使って楽に確認できるツールを作成したので、今回のリリースからはそれを使うようになりました。

https://github.com/sosukesuzuki/prettier-regression-testing

GitHub の Issue 上で runから始まるコマンドをコメントとして投稿すると、事前に登録してあるリポジトリに対して新しい Prettier を実行し、その差分をコメントで見せてくれるというものです。

実際に 2.2 のリグレッションチェックのために使われた Issue は https://github.com/sosukesuzuki/prettier-regression-testing/issues/8です。

このように、Issue 上で各プロジェクトに新しい Prettier を実行した差分をコメントで教えてくれます。

f:id:cybozuinsideout:20210118091711p:plain

このツールにより、かなり簡単に問題を事前に防げるようになりました。

(実はこのツールは本運用するかどうかもわからない状態で適当に JavaScript で書いてしまって、かなり雑なコードになってしまっているので、TypeScript で書き直そうと思っています。)

TypeScript 4.1 対応について考える

今回リリースしようとしていた時期は、偶然にも TypeScript 4.1 のリリースと重なっていました。

どうせマイナーリリースするならもうすぐリリースされる TypeScript 4.1 対応したかったし、おそらく多くのユーザーもそれを望んでいたことと思います。

ただ、TypeScript の新しいバージョンのサポートは、いくつかのプロジェクトをまたぐ作業になるので、問題が発生しやすく時間がかかってしまうことが多いです。今回も例にもれず、いくつか問題が発生してしまいました。

Prettier は(デフォルトでは) typescript-eslint プロジェクトが提供しているパーサー typesript-estreeを使って TypeScript のコードをパースします。しかし、リリース準備が整ったタイミングでは typescript-estree は TypeScript 4.1 に対応したバージョンをリリースしていませんでした。

理想としては、デフォルトのパーサーである typescript-estree の TypeScript 4.1 対応を待ってから Prettier をリリースしたいところでしたが、その時点ではどのくらい時間がかかるかもわかりませんでした。

一方 Babel はそのときすでに TypeScript 4.1 のサポートをリリースしていたので、「Babel を介した TypeScript 4.1 対応のみ 2.2 に入れて、typescript-estree での対応は次のバージョンに延期しよう」という妥協の案を提案しました。

あまり採用したくない妥協の案ではあったものの、特に反対意見もなかったのでそのままリリースしようとしていました。

しかし TypeScript 4.1 の RC がリリースされたタイミングで typescript-estree が TypeScript 4.1 対応版をリリースしました。これにより、ギリギリのタイミングでデフォルトパーサーでの TypeScript 4.1 対応をリリースできる準備が整ったかのように思われました。

しかし、ここでもう一つ問題が発生します。単純に typescript-estree をアップデートするだけでは、typescript-estree 4.x 系の破壊的変更の影響で Prettier の挙動が壊れてしまうことがわかったのです(実はこれは前からわかっていたことで、完全に忘れていました)。

typescript-estree 4.x 系には、不正な位置に存在するデコレータのノードを AST から消すようになる破壊的変更がありました(Pull Request へのリンク)。

@decorator
interface Foo {}
class Bar {}

このコードは TypeScript としてインバリッドです(TypeScript Playground へのリンク)。しかし AST からデコレータの情報が完全に消えてしまうとコードフォーマッター的には困るわけです。

このとき Prettier がパーサーに期待する挙動としては、「ノードの情報を AST に含める」 or 「エラーをスローする」のどちらかです。

ここで、Prettier 側でエラーをスローするために 2 つの案が提案されました。

1 つめは「入力の文字列と出力の文字列のそれぞれに含まれる@の数を数えて、異なっていたらエラーをスローする」というものです。かなり愚直な力技ではありますが、これで凌ぐことはできます。

2 つめは「typescript-estree の AST の他のメタ情報を返す API を使って頑張ってデコレータの有無を判定してエラーをスローする」というものです。typescript-estree には parseAndGenerateServicesというメソッドがあります。このメソッドは入力の文字列をパースし、AST を生成し、それとともにいくつかのメタ情報を返します。

このメタ情報の一部を使えば、デコレータの有無を判定しエラーをスローすることができますが、この方法にも問題がありました。parseAndGenerateServicesメソッドは Prettier にとって必要な情報以外にも多くの情報を取得してしまい、その処理が重いのでパフォーマンスの悪化が懸念されたのです。

そこで typescript-eslint のメンテナーに相談し、「AST + Prettier にとって必要なメタ情報」のみを返す新しい API parseWithNodeMapsを実装してもらいました。

ちなみに Prettier にとって必要なメタ情報というのは TypeScript Compiler API の AST から ESTree compatible な AST への WeakMap(tsNodeToESTreeNodeMap)と、その逆の WeakMap(esTreeNodeToTSNodeMap)です。それらを使う処理はこのあたりに記述されているので、興味があったら読んでみてください。

これらの作業をやっていたら TypeScript 4.1 のリリースが翌日に迫っていたので 4.1 の正式リリースを待ってから Prettier をリリースすることにしました。

リリースする

TypeScript 4.1 の正式リリースが来たので、後はリリーススクリプトを叩くだけです。

Prettier のリリーススクリプトは過去のメンテナーたちが書いたもので、表示される指示に従うと npm への publish や GitHub でのタグの登録など全部できるようになっています。

リリース後に軽く動作確認をして、リリースブログを公開したら、リリース作業完了です!

まとめ

Closure Tools で書かれた大規模なソースコードに Prettier を導入するまでの道のりについて紹介しました。

ここで紹介したすべての作業は Prettier 本体にマージされリリースされているので、過去に同じような問題に遭遇したことがある方は今もう一度試してみたら上手くいくかもしれません。

今回紹介した作業のような、プロダクトの改善に必要な作業を OSS 側で行うのもフロントエンドエキスパートチームの仕事の一貫です。興味のある方は以下の採用ページからご応募ください。

https://cybozu.co.jp/company/job/recruitment/list/front_end_expert.html

コンテナレジストリの可用性を高める取り組み

$
0
0

こんにちは、Necoチームの池添です。

みなさんKubernetes向けのコンテナレジストリにはどこのサービスを利用していますか?そのサービスの調子が悪くて困ったりしたことはありませんか? 今回はコンテナレジストリをKubernetesクラスタ上にセルフホストし、システムの可用性を高める取り組みについて紹介したいと思います。

セルフホストコンテナレジストリがなぜ必要か

コンテナレジストリには、Docker Hub, Red Hat Quay, GitHub Container Registry (GHCR), さらには各種パブリッククラウドベンダーによるものなど、数多くのサービスが存在します。

これらのコンテナレジストリのいずれかひとつに頼っていると、そこが単一障害点になってしまいます。 コンテナレジストリがダウンすると新しいコンテナを立ち上げる事ができなくなり、障害につながる場合もあります。

また昨年、Docker Hubの無料プランのPull Rate Limitが発表され、話題にもなりました。 Rate Limitに引っかかると、コンテナレジストリに障害が起きていなくてもコンテナイメージが取得できなくなってしまいます。

さらには、コンテナイメージのダウンロード時間や、一度に大量のノードにコンテナイメージをダウンロードする際の通信量を削減したいという要望もあるでしょう。

これらの問題を解決するため、コンテナイメージレジストリをセルフホストしたいという需要があります。

セルフホストコンテナレジストリ

コンテナレジストリをセルフホストしたい場合、HarborやDocker Registryを利用するのが一般的でしょう。

goharbor.io

docs.docker.com

Harborはマルチテナントによる利用、Web UI、セキュリティチェックなどをサポートした多機能なコンテナレジストリです。

一方のDocker Registryは、必要最低限の機能を提供するシンプルなコンテナレジストリ実装です。

また少し毛色は違いますが、P2Pでコンテナイメージを配布するDragonflyや、Upstreamのコンテナイメージの変更を検知して自動でキャッシュするtaggerのようなOSSもあります。

コンテナイメージ名の解決方法

Kubernetes上にPodをデプロイする場合、マニフェストには以下のようにコンテナイメージの名前を指定します。

spec:
  containers:
  - name: ubuntu
    image: quay.io/cybozu/ubuntu:20.04

通常、複数のコンテナレジストリが存在する場合、利用するレジストリに応じてイメージ名を変更しなければなりません。

例えば、Quay上のコンテナイメージを利用する場合はquay.io/cybozu/ubuntu:20.04、セルフホストしたレジストリのコンテナイメージを利用する場合はself-hosted-registry:5000/ubuntu:20.04を指定するといった具合です。

このイメージ名を手動で書き換えることは非常に不便です。

この問題を解決するためには、以下のような方法が考えられます。

  • ミラーリング & Pull Through Cache方式
  • Man in the Middle方式
  • Mutating Admission Webhook方式

この3つの方式を解説していきます。

ミラーリング & Pull Through Cache方式

Kubernetesで利用可能なコンテナランタイムの多くは、コンテナレジストリのミラーリングを設定することができます。

例えばcontainerdでは以下のような設定をおこないます。

[plugins."io.containerd.grpc.v1.cri".registry.mirrors]
  [plugins."io.containerd.grpc.v1.cri".registry.mirrors."quay.io"]
    endpoint = ["http://self-hosted-registry:5000", "https://quay.io"]

この設定により、quay.ioから始まるコンテナイメージを利用する際に、まずセルフホストしたコンテナレジストリ(http://self-hosted-registry:5000)に対してPullを試みます。

複数のコンテナレジストリを指定することができる

そこでもしコンテナイメージが見つからなかったり、コンテナレジストリに接続できなければ、アップストリームのコンテナレジストリ(https://quay.io)からPullします。

片方のコンテナレジストリが故障した場合、フォールバックされる

さて、ミラーリング設定により利用するコンテナレジストリを複数設定することが可能になりました。 このミラーリングを有効に利用するためには、セルフホストしたコンテナレジストリに、アップストリームに存在するコンテナイメージをPushしておく必要があります。 しかし、事前に必要なコンテナイメージをすべて取得しておくのは少々面倒です。

そこで、Pull Through Cacheという仕組みを利用します。(Docker Registry, Harborのどちらでも利用可能です)

この機能を利用すると、セルフホストしたコンテナレジストリからPullする際に、レジストリがすでに対象のコンテナイメージを保持していた場合はそれを返し、保持していない場合はアップストリームからイメージを取得し、それをキャッシュに保存することができます。

キャッシュにイメージが存在しない場合はUpstreamからPullする

なお、Googleが提供するGCRではDocker HubをPull Through Cacheしてくれる機能があります。 AWSのECRも将来的にサポートされるようです。

Man in the Middle方式

HTTPS_PROXY設定を書き換えて、アップストリームコンテナレジストリへのアクセスをセルフホストコンテナレジストリに変更してしまう方式です。 以下のようなOSSを利用して実現することが可能です。

rpardini/docker-registry-proxy

後述するように、コンテナランタイムにDockerを利用している場合は、Docker Hub以外のミラーリング設定をおこなうことができません。 コンテナランタイムにDockerを利用していて、Docker Hub以外のレジストリをミラーリングしたい場合は、Man in the Middle方式を検討してみてもよいかもしれません。

Mutating Admission Webhook方式

KubernetesのMutating Admission Webhook機能を利用し、Podを作成・更新するタイミングでイメージ名を書き換えてしまう方式です。

下記の実装などが参考になるでしょう。

docker-proxy-webhook

ただしこの方式の場合、セルフホストしたコンテナレジストリが障害を起こした場合にアップストリームにフォールバックするのが難しいため、可用性の観点では課題がありそうです。

注意点

コンテナレジストリをセルフホストし、ミラーリング設定をおこなう際の注意点を紹介します。

Dockerコンテナランタイムのミラーリングはdocker.ioのみ

Dockerコンテナランタイムではdocker.ioしかミラーリングをおこなうことができません。 何年も前から議論されてはいるのですが、実装は進んでいないようです。

Kubernetes 1.20でDockershimがdeprecationになったこともありますし、KubernetesのバックエンドにはDocker以外のコンテナランタイムを利用するのがよいでしょう。

HarborのPull Through CacheはDocker HubとHarbor Registryのみ

Harborのドキュメントには、以下のように記載されています。

Harbor only supports proxy caching for Docker Hub and Harbor registries. https://goharbor.io/docs/2.1.0/administration/configure-proxy-cache/

なお、Docker Registryがサポートするレジストリは明記されていませんが、我々が検証をおこなったところDocker Hub, Quay, GHCRが問題なく動作しました。

Pull Through Cacheのアップストリームは1つだけ

Docker Registry, Harborともに、Pull Through Cacheの接続先となるアップストリームは1つだけしか設定できません。

複数のレジストリに対してPull Through Cacheを利用したい場合は、それぞれのレジストリ用のミラーをセルフホストする必要があります。例えば、Docker Hub, Quay, GHCRに対してPull Through Cacheを利用したい場合は、3つのDocker Registryを立てなければなりません。

Pull Through CacheでimagePullSecretsは利用できない

Kubernetesでは、認証が必要なプライベートコンテナレジストリにアクセスするためにimagePullSecretsという仕組みがあります。

kubernetes.io

しかし、Pull Through Cacheでイメージを取得する際には、imagePullSecretsに指定した認証情報はアップストリームのコンテナレジストリに渡りません。 これはコンテナレジストリの実装に関わらず、Docker, containerd, podman, cri-oのいずれを利用しても同じです。

Pull Through Cacheでプライベートなコンテナレジストリにアクセスするためには、コンテナレジストリにユーザー名とパスワードを指定する必要があります。

ただし、セルフホストしたコンテナレジストリにアクセスできる人は誰でもプライベートコンテナを利用することが可能になります。 必要に応じて、セルフホストしたコンテナレジストリに認証をかけるなどの対策を検討してください。

また、上記でコンテナレジストリに設定できる認証情報は、Docker Registry,Harborともに1つだけのようです。 複数の認証情報を使い分けることができないため、運用でのカバーなどが必要になってくるでしょう。

我々が採用した方式

前提条件として、我々の運用しているKubernetesクラスタでは以下の技術を利用しています。

  • アップストリームコンテナレジストリとしてQuayとGHCRを利用している
  • コンテナランタイムとしてcontainerdを採用している

このことからQuayやGHCRのPull Through Cacheが必要であり、Harborの提供する多くの機能は必要ないため、セルフホストコンテナレジストリとしてDocker Registryを採用しました。

そして、各アップストリームレジストリごとにコンテナレジストリをセルフホストし、ミラーリング & Pull Through Cacheの設定をおこなっています。

興味のある方は、下記のマニフェストや設定もご覧ください。

まとめ

本記事ではコンテナレジストリの可用性を高めるための取り組みとその注意点を紹介しました。 コンテナレジストリの冗長化にはハマりどころが多いので、この記事が少しでもみなさんのお役に立てば幸いです。

インフラのリリース自動化戦略とその行き着く先

$
0
0

こんにちは、@ueokandeです。 本番リリースってドキドキしますよね。 本日はkintone.comのリリース自動化と、その戦略についてお話します。

kintone.comのCI/CDパイプライン

kintone.comのインフラ構成はモノレポで管理しており、AWSの構成や、Kubernetes上にデプロイするサービスなどが1つのレポジトリに存在します。 現在のkintone.comは、開発環境、ステージング環境、本番環境の3つがあります。 適用タイミングをずらすことによる環境間の乖離を防ぐため、各リリースはすべての環境に適用することとしました。 開発環境でしばらく寝かせたい変更は、機能フラグやカナリアによって切り替えます。

CI/CDパイプラインは以下のようになっています。 それぞれの環境に順に適用し、本番環境適用後にテストが通れば無事リリース完了です。

kintone.comのCI/CDパイプライン
kintone.comのCI/CDパイプライン

kintone.comのローンチ前に本番環境を新しく作るとき、安全に本番リリースする仕組みが必要だという議論がありました。 その議論では以下の要件が挙がりました。

  • インフラの構成やサービスの設定値はコードで表現されている
  • オペミスや属人化を防ぐため適用作業は自動化されている
  • デプロイに失敗したり問題が発生したら即座に切り戻すことができる

リリースの自動化だけではこれらの要件を満たせません。 ある人のリリース作業がほかの人に影響する可能性を考慮する必要があります。 これらの要件を満たすリリース戦略は、開発が進むに連れて改善と進化を遂げてきました。

戦略1: リリースロック

まず最初の戦略では、すべてのリリース作業をロックして排他制御しました。 誰かのリリースが適用されているときは、他の人はリリースできません。 単純な実装ではありますが、いくつかのメリットがあります。

  • リリース中に問題が発生しても修正するまで他のリリースをブロックできる
  • 切り戻し作業がgit revertで確実にできる

リリースの状態管理はGitHub Issuesにラベルをつけて管理します。 GitHub Issuesを見ると、現在適用中の変更や、誰が適用してるかをひと目で確認できます。

GitHub Issues上でリリースをロックする
GitHub Issues上でリリースをロックする

マージ前にロックを確保する必要があるため、Pull Requestのマージボタンは使いません。 代わりに、以下の処理を行うCLIツールを作りました。

  1. GitHub Issues上でロックを取る
  2. ロック取得に成功したらPull Requestをマージする
  3. 本番適用後にテストが通れば、Issueを閉じてロックを開放する

戦略1.1: リリースロック + GitHub Actions

CLIツールを使うと、GitHubの画面とターミナルを往復する必要があり、それが面倒だったのでリリースロックの仕組みをGitHub Actionsに移行しました。 Pull Request上でマージ命令をコメントすると、GitHub ActionsのBotが自動でマージします。

GitHub Actionsによるリリースロックの拡張
GitHub Actionsによるリリースロックの拡張

GitHub ActionsのYAMLファイルの一部を紹介します。 Issueコメント (IssueコメントとPull Requestコメントは同じイベントが発火する)がつけられたときにGitHub Actionsが実行され、CLIツールを呼び出します。

name: Merge and Release
on:issue_comment:types: created
jobs:merge-and-release:name: Merge and release a pull request
    runs-on: ubuntu-latest
    if: github.event.issue.state == 'open'&& startsWith(github.event.comment.body, '/')
    steps: # on_going_releaseラベルのあるIssueがあるかチェックして、 # なければロックを取得してリリース開始

チーム内でリリース頻度が上がってくると、リリースのロック確保に失敗したり、他の人のリリースが終わるのをじっと待つことが多くなりました。 この戦略のままでは、チーム全体のリリース速度が上がらないということに気付きました。 そこでリリース速度を上げるために、ロックする戦略から、 キューイングする戦略へと移行しました。

戦略2: リリースキュー

リリースロックは、他のリリース状況の確認や、ロック中の待ち時間が課題でした。 そこでリリースするPull Requestをキューに積むことで、ロックの確認によるストレスや待ち時間を減らします。 キューに積まれたリリースは順次適用され、あるリリースが本番環境まで適用されたら、次のリリースが開始します。

これも同じく、GitHub Issue上でリリースのキューを作りました。 あるリリースの本番適用が完了してIssueが閉じられると、GitHub Actionsが発火して次のリリースを開始します。

GitHub Actions上でリリースをキューイング
GitHub Actions上でリリースをキューイング

GitHub ActionsのYAMLファイルの一部を紹介します。 on_going_releaseのIssueが閉じられたタイミングで、ready_to_releaseのあるIssueの先頭1つを on_going_releaseに切り替えます。

name: Release queued PRs
on:issues:types:[closed]jobs:do-merge-commands:name: Release queued Pull Request
    runs-on: ubuntu-latest
    if: contains(github.event.issue.labels.*.name, 'on_going_release')
    steps: # `ready_to_release` のあるIssueの先頭1つを `on_going_release` に # 切り替えてリリースを開始する。

リリースキューもロックの戦略と同じで、本番環境へのリリースは排他的に行われます。 そのため問題が生じたとしても、他のリリースによって問題が悪化することを防ぎます。

GitHub社の例

ここまでkintone.comのリリース戦略を見てきました。 kintone.comはリリース戦略が、ロックからキューへと進化してきました。 実はGitHub社のリリース戦略も、同じ進化をたどってきたようです。

GitHub社は、リリースキューよりも更に速度を出すために、リリーストレインという戦略でリリースしています。 この戦略は、いくつかのリリース候補を「トレイン」に乗せて、時刻になればトレインに積まれた変更が本番にリリースされます。 キューの待ちを減らして、リリースする本人が適用タイミングを選ぶことができます。

GitHub社のCI/CDに関する情報は、以下のスライドで説明されています。

おわりに

この記事ではkintone.comのリリース自動化戦略と、GitHub社のリリース戦略を紹介しました。 我々はまだリリースキューでとどまっていますが、リリース回数が多い日はキューでも待たされることがあります。 一方で本番適用の影響を最小限にするため、複数の変更を同時に適用するのを避けて、リリーストレインに踏み切れないという気持ちもあります。

将来kintone.comの規模がさらに大きくなるとリリーストレイン、または別の戦略が必要になるかもしれません。 その時は一体どのような戦略でリリースするのでしょうか。 時が来たら改めて記事を書きたいと思います。

分散ストレージCephのオーケストレータRookのデータ破壊バグを修正しました

$
0
0

はじめに

こんにちは、Necoプロジェクトsatです。本記事では分散ストレージCephのオーケストレータであり、Kubernetes上で動作するRookに関するものです。このRookに存在していたデータ破壊バグを我々が検出、修正した体験談、およびそこから得られたことを読者のみなさんに共有します。本記事は以前Kubernetes Meetup Tokyo #36におけるLTで述べた問題のフォローアップという位置づけです。

speakerdeck.com

"解決までの流れ(詳細)"の節以外はRookやCephについて知らなくても適宜用語を説明するなどして読めるように書きました。

Rook/Ceph固有の話にも興味があるかたは以下の記事/スライドも併せてごらんください。

blog.cybozu.io

speakerdeck.com

用語

Rook/Cephについて知らないかた向けに、まずは本節において出てくる用語を解説しておきます。

  • OSD: ディスク1つに対して1個ないし数個存在するデータ構造。Cephは1つないし複数のノード上にあるOSDを束ねてストレージプールを作る。
  • OSDデーモン: OSDを操作する常駐プロセス
  • OSD Pod: 内部でOSDデーモンを動かすためのKubernetesのPod
  • OSDのデータ構造
    • データ領域: ユーザデータを保存する
    • bluefs: メタデータを保存するためのファイルシステム
      • メタデータ領域: データ領域の中の個々のデータの位置やサイズなどの管理
      • ジャーナルログ: bluefsの操作の整合性を保つためのログ

これらの関係を図示すると以下のようになります。

f:id:cybozuinsideout:20210128055844j:plain
CephクラスタのストレージプールとOSDの関係

f:id:cybozuinsideout:20210128061905j:plain
OSDto

f:id:cybozuinsideout:20210128055949j:plain
OSDのデータ構造

問題要旨

  • 発生した問題: Cephのデータ保存領域であるOSDが壊れてアクセス不能になった
  • 発生条件: 同じOSDに対応するOSD Podが2つ以上同時起動した場合
  • 原因: OSDを扱うデーモンを動かすOSD Podの同時起動に対するRookの考慮漏れ
  • 対処: Rookの修正によってOSDデーモンの同時起動を防ぐように排他制御した

解決までの流れ(概要)

調査および修正はおおよそ次のように進みました。

  1. 問題の検出: Kubernetesクラスタ、およびその上にRook/Cephクラスタを構築した開発環境の中のいくつかのノードにおいてメモリ不足になりOOM killerが繰り返し発生した。事象がおさまった後に当該ノード上のOSDがいくつか壊れていた
  2. 初期調査(1,2日程度): 問題発生時のログを調査した上でCephコミュニティ、およびRookコミュニティにissueを発行
  3. 本格調査(一か月程度): Cephコミュニティ上で開発者と共に調査。最終的に本来1つのOSDに対して1つしか起動してはいけないOSDデーモンが同時に2つ以上起動していた可能性を指摘される
  4. 根本原因調査(1,2日程度): OSDデーモンが同時起動したら同じ問題が起きること、OOM killerを契機にこの問題が起こりうることを確認
  5. 修正方針検討(3週間程度): RookとCephのどちらで直すべきかを2つのコミュニティをまたいで議論。最終的にRookにおいて修正することにした
  6. 修正(一か月程度): 修正作成、レビュー

合計すると3カ月弱という長期間を要しました。所要時間のうちの多くはupstream OSSコミュニティ上での議論に費やしましたが、このようなときに先走って手を動かしすぎると思わぬ手戻りが発生することがあるので、しっかりと合意をとるのが大事です。

もう一点。この問題は開発環境で起きたこと、発生頻度が低いこと、発生しても致命的な問題が起きないこと*1より、調査速度よりもRook/Cephの調査ノウハウ獲得を優先しました。これによってこの問題の対処以外の他の作業を止めずに余裕をもって調査を進められました。重大な問題だからといってなんでもかんでも最優先でやるべしというわけではなく、緊急度が低ければこのような対応をするほうがいいということもあります。

仮にこの問題が本番環境で起きていた場合はもっと急ぐ必要があるため、対処方法が変わってくるでしょう。たとえば原因究明後はupstreamコミュニティでの議論を待たずOSD Podを即座に独自修正版に更新し、upstreamにおいて修正が適用され次第そちらに差し替えるなどの対処が必要です。原因究明までの調査も短縮化の必要がありますが、今後似たような問題が起きた場合に、今回よりもはるかに高速に進められる知見を得られました。

解決までの流れ(詳細)

本節ではこの手の問題の検出から解決までの思考プロセスに感心があるかた、およびRookやCephのトラブル解析に興味があるかたに向けて、調査の詳細について記載します。興味がないかたは"おわりに"の節まで飛ばしてください。

問題の検出

最初に問題を検出したのは我々の開発環境でした。開発環境にはKubernetesクラスタ、およびその上にRook/Cephクラスタが構築されています。

あるとき、この環境上のいくつかのノードがメモリ不足に陥り、カーネルのOut-Of-Memory Killer(OOM killer)が繰り返し発生しました。この事象がおさまった後に当該ノード上に存在していたOSD Podのうちのいくつかが壊れていました。壊れたOSDはすぐに作り直したのですが、この事象が本番系で発生するとお客様のデータ損実につながります。データ損失は数ある問題の中でも最も重大なもののひとつなので、詳細調査をすることにしました。

初期調査

問題が発生したOSDに対応するOSD Podは、OOM killerのために再起動された後、initContainerの動作中にデータの不整合を検出して失敗し続けていました。

OSD Podのログを調査したところ、bluefs内のジャーナルログの再生に失敗していることがわかりました。

2020-10-28T02:07:55.499+0000 7f0aa7f1e0c0 -1 bluefs _check_new_allocations invalid extent 1: 0x7ae18a0000~10000: duplicate reference, ino 26
2020-10-28T02:07:55.500+0000 7f0aa7f1e0c0 -1 bluefs mount failed to replay log: (14) Bad address
2020-10-28T02:07:55.500+0000 7f0aa7f1e0c0 -1 bluestore(/var/lib/ceph/osd/ceph-9) _open_bluefs failed bluefs mount: (14) Bad address

続いて詳細な原因を確かめるため、bluefsのジャーナルログの中身を見ました。

2020-10-28T08:39:58.788+0000 7f79675c70c0 20 bluefs _replay 0x263000: op_file_remove 26                                         ... ★1
<中略>
2020-10-28T08:39:58.788+0000 7f79675c70c0 20 bluefs _replay 0x264000: op_file_update file(ino 32 size 0x1356 mtime 2020-10-27T23:10:42.988971+0000 allocated 10000 extents [1:0x7ae18a0000~10000])   ... ★2
<中略>
2020-10-28T08:39:58.788+0000 7f79675c70c0 -1 bluefs _check_new_allocations invalid extent 1: 0x7ae18a0000~10000: duplicate reference, ino 26 ... ★3

この結果、bluefsは次のような流れで壊れたことがわかりました。

  1. bluefs上のファイル(inode 26)を削除
  2. bluefs内の領域(extent) "0x7ae18a0000~10000"を別のファイル(inode 32)に割り当てた
  3. 上記extentは既に削除したはずのファイル(inode 26)に割り当て済みだったので不整合発生

もう一点、ファイル(inode 26)の最後の更新時刻(2020-10-23T08:29:44.981029+0000)は、それより一つ前のログが出力されたときの時刻(2020-10-28T08:39:58.788+0000)よりもはるかに古いこと、つまり時系列順に並ぶはずのログの時刻が逆転していることがわかりました。

本格調査

前節における調査の結果、なんらかの理由によってbluefsが壊れていることが明らかになったため、upstream OSSにissueを発行しました。このときはRookかCephか、いずれの問題かが明らかになっていなかったので、両方に対してissueを発行して、片方で進捗した場合はもう片方にも情報共有する、というやりかたにしました。

tracker.ceph.com

github.com

幸いにもCephのissueにおいてIgor FedotovさんというOSDについての豊富な知見をお持ちのかたがこのissueを見てくれることになりました。その後しばらく共同調査を進め、最終的にデバッグ情報を有効にした状態で採取したジャーナルログの調査によって、OSDデーモンの中に存在するジャーナルログを操作するための_kv_sync_threadというスレッドが複数存在していることが明らかになりました。以下ログの抜粋です。

2020-11-24T15:49:35.726+0000 7faf0eb7a700 10 bluestore(/var/lib/ceph/osd/ceph-0) _kv_sync_thread start   ... ★1
<中略>
2020-11-24T15:50:01.624+0000 7f60bf7e4f40 0 set uid:gid to 167:167 (ceph:ceph)                                             ... ★2
2020-11-24T15:50:01.624+0000 7f60bf7e4f40 0 ceph version 15.2.4 (7447c15c6ff58d7fce91843b705a268a1917325c) octopus (stable), process ceph-osd, pid 1
<中略>
2020-11-24T15:50:04.404+0000 7f60a9ad2700 10 bluestore(/var/lib/ceph/osd/ceph-0) _kv_sync_thread start   ... ★3
<中略>
2020-11-24T15:50:05.828+0000 7faf0eb7a700 10 bluefs _flush 0x5562cd35f420 ignoring, length 25780 < min_flush_size 524288 ... ★ 4
<中略>
2020-11-24T15:50:05.828+0000 7f60a9ad2700 10 bluefs _flush 0x55c8422695e0 ignoring, length 14199 < min_flush_size 524288  ... ★5
2020-11-24T15:50:05.828+0000 7f60a9ad2700 10 bluefs _flush 0x55c8422695e0 ignoring, length 17245 < min_flush_size 524288
2020-11-24T15:50:05.828+0000 7faf0eb7a700 10 bluefs _fsync 0x5562cd35f420 file(ino 415 size 0x1e827 mtime 2020-11-24T15:50:05.829650+0000 allocated 20000 extents [1:0xa04a0000~20000]) ... ★6

ログから次のような事象が発生していたことが明らかになりました。

  1. OSDデーモンの中の_kv_sync_thread A(7faf0eb7a700)が起動
  2. 新しい別のOSDデーモン(7f60bf7e4f40)が起動。ここからがおかしい
  3. step1のものとは別の、恐らくはOSDデーモン(7f60bf7e4f40)の中のkv_sync_thread B(7f60a9ad2700)が起動
  4. _kv_sync_thread Aが動作
  5. _kv_sync_thread Bが再び動作
  6. また_kv_sync_thread Aが動作

こんなことが起きてしまったらbluefsが壊れるのは当然です。

f:id:cybozuinsideout:20210128061955j:plain
bluefsの破壊

根本原因調査

この問題が実際に発生していたことを確認するために、検証環境において次の2つのことを確認しました。

  • OSD Podのレプリカ数が本来1であるところを2にすると、期待通りOSDは破壊され、かつ、OSDデーモンのログは問題を検出したときと同様なものになること
  • 問題検出時と同様にOOM killerが発生したときに、Kubernetesの仕様上、同じ問題が起こりうること。具体的には以下の処理が繰り返し起きるときに同じOSDを操作するOSD Podが同時に2つ動作しうること
    • ノード上で発生したOOM killerによるOSDデーモンの強制終了
    • kubeletによるOSDデーモンの再生成
    • ノードのメモリ不足に起因するOSD Podのeviction

OSD PodがStatefulSetによって管理されていれば上記のようなOSD Podの同時起動は起こりえないのですが、実際には様々な事情によりDeploymentによって管理されているためOSD Pod自体の同時起動は起きえます*2。しかしOSD Podの中にあるOSDデーモンの同時起動はCephがファイルロックによって防いでいるために、本来はこの問題は発生しないはずです。なぜこのようなことが発生してしまったのでしょうか。

f:id:cybozuinsideout:20210128062311j:plain
Cephによる排他制御

答えはファイルロックのありかが不適切だったことによります。Cephはロック用のファイルを"/var/lib/ceph/osd/ceph-"(以下「OSDディレクトリ」と記載)に配置するのですが、RookはこのディレクトリをOSD Pod間で共有せずに、次のように個々のOSDデーモン用のメモリ上に置いてしまっていました。

containers:command:- ceph-osd
  volumeMounts:- mountPath: /var/lib/ceph/osd/ceph-0
    name: set1-data-0-xfvdg-bridge
    subPath: ceph-0
…
volumes:- emptyDir:medium: Memory                # "/var/lib/ceph/osd/ceph-0"はメモリ上に存在name: set1-data-0-xfvdg-bridge
…

このため、複数のOSD Podが同時に動作した場合、それぞれ自身が持つメモリ上に存在する別々のロックファイルを獲得しようとして、常に獲得に成功してしまうという状況になっていました。

f:id:cybozuinsideout:20210128062430j:plain
不適切なロック

修正方針の検討

この問題を解決するもっとも単純な方法はRookに手を加えて以下のようにhostPathを使ってロックファイルを共有させることです。

volumes:- hostPath:Path: /var/lib/rook/<OSDごとに固有のパス>
  name: set1-data-0-xfvdg-bridge
…

ただしこの問題はRookのメンテナから反対を受けました。その理由は、安易にコンテナのデータを他のコンテナと共有すべきではない、可能な限りそのようなデータは少なくすべきだ、というものでした。OSDディレクトリ以下にはロック用のファイル以外にも多くのファイルが存在するために、それらのファイルも一緒に共有するのを嫌うのは無理からぬことです。彼はそのかわりにCephに手を入れてロックファイルだけをOSDディレクトリ以外の場所に置けるようにしてはどうかという提案をしました。

この提案をIgorさんにしたところ、技術的には可能であるが、そうするとアップグレード処理が複雑になること、他のツールにも修正が必要になりうることなどより難色を示されました。この答えをRookコミュニティに持ち帰った結果、最終的には不格好ではあるがデータが壊れるよりマシ、ということで上記のemptyDirをhostPathに変更する方法によって問題を修正することにしました。

修正

修正そのものは比較的シンプルだったために初期実装は簡単に終わりました。しかしながら影響範囲が大きかったこともあり、3週間にわたる綿密なレビューの末、ようやく修正がmasterブランチにマージされました。この修正はRook v1.5 branchにもバックポートされていて、近日リリースされるv1.5.6に取り込まれるはずです。

github.com

時間がかかって大変でしたが、見落としていた箇所が修正できたり、のちのちこのコードを見る人のためにコメントを追加したりと、最初に出したバージョンよりもはるかによいものになりました。

おわりに

本記事ではupstream OSSにおける複雑な問題に対処する際の以下のようなポイントを共有しました。

  • 焦らずに関係者の合意をとった上で進めることが大事
  • 所要時間のほとんどは実装ではなく議論に費やされる
  • upstreamの修正を待たずに手元の環境に一時的に緊急修正をすべき場合もある

これらの情報が今後みなさまに何らかの形で役立つことを願います。

最後になりますが、この問題の解決に協力してくださったプロジェクトメンバ、Rookコミュニティのみなさま、およびCephコミュニティのみなさまに感謝いたします。

*1:サイボウズのK8sクラスタにおいては1つのノード上の全OSDが壊れてもユーザデータを失わないようになっています

*2:ここではなぜDeploymentを使っているかについては触れません

サイボウズの新しいインフラ基盤を支えるストレージの本番適用に向けた取り組み

$
0
0

はじめに

こんにちは、Necoプロジェクトのsatです。Necoプロジェクトではサイボウズのクラウドサービスcybozu.comの次期インフラ基盤を開発しています。その根幹となるコンポーネントの一つがお客様のデータをあずかるストレージです。本記事ではNecoのストレージの本番適用に向けた取り組みの一つを紹介いたします。

たくさんの取り組みをしているのですが、ここではストレージシステムの運用にあたって、データを格納するRook/Cephクラスタの運用に必要なオペレーションを作る部分について扱います。

用語解説

ここでは本記事を読むにあたって必要になるCephの用語について、いくつか説明しておきます。

OSD

OSDとはCephクラスタに組み込まれる個々のディスク上に存在している、ユーザの実データを保存する領域です。Cephは自身を構成するノード上にあるOSDを束ねてストレージプールを作ります。

f:id:cybozuinsideout:20210128055844j:plain
CephクラスタとOSDの関係

MON

Cephではクラスタの状態をMONというデーモンを使って管理しています。MONは複数個作って冗長化するのが一般的です。MONが複数個ある場合は、全てのMONがクラスタの最新の状態を保持するようになっています。

MONは奇数個作るのが一般的で、かつ、それらのうち過半数が正常動作していればクラスタにアクセスできます*1。そう出ない場合はクラスタにアクセスできなくなるので、後述の手順で正常動作するMONの数を回復する必要があります。

具体的な取り組み

ここからは以下の2つのオペレーションについて、それぞれどういう取り組みをしているかについて書きます。

  • ディスクの削除、交換処理
  • クラスタにアクセスできなくなった場合の対処

OSDの削除、交換処理

OSDの削除

Rook/Cephクラスタを構成するディスクが壊れた場合、その上に存在しているOSDをクラスタから削除する必要があります。この手順はCeph、およびRookの両方においてドキュメント化されています。

docs.ceph.com

github.com

我々はまず、実際にこの手順に従ってOSDの削除処理を試してみました。すると、我々が使っているPVC-based clusterというタイプのクラスタでは、物理サーバ上にクラスタを構築するオンプレミス環境において、OSDをうまく削除できないという問題をあることを発見しました。なぜこういうことが起きたかというと、当時このPVC-based clusterはクラウド環境上で動作することだけを想定していたからです。

この問題についてはupstream Rookに対してissueを発行したところ、すぐにメンテナのかたがPRを投稿してくれて、私を含めた他のメンテナたちとも議論した末、無事にマージされました。

github.com

github.com

このように自分達が検出した問題を他の誰かが修正してくれるのは、OSS開発の醍醐味のひとつで、とても嬉しいものです。今後もこのようにupstreamコミュニティ全体で協力して、リソースを出し合って開発を進めていきたいと思います。

もう一点。OSDの削除についてはオペレーションコストを下げるために自動化ジョブが用意されています。しかし、このジョブについてのドキュメントが無かったため、公式ドキュメントに必要事項を追記しました。これに加えて、OSDの削除手順が全体的にわかりにくかったので、このPRにおいて整理して、ほとんどを書き直しました。

github.com

ここで手元の手順を作るだけではなくupstream Rookのドキュメントも修正する理由は2つあります。ひとつは、この手順に従って問題が発生したときにupstream Rookコミュニティの支援が得られるためです。もうひとつは、Rookの他のユーザが正しい手順を使えるようになり、Rookユーザ全員のためになるからです。

OSDの交換

続いてOSDの交換手順です。通常は運用中にOSDを削除するときは、削除したOSDのかわりに別の正常なOSDを追加します。ところがRookにおけるOSDの交換には専用のオペレーションが無く、一旦OSD削除処理をしてから別のOSDを追加する、という素朴かつ面倒なものしかありませんでした。

我々はこれを見直して、OSDを削除すると自動的に他の正常なOSDに入れ替わる手順を新たに作りました。この手順については、もう一度整理してからupstream RookにPRを送り、公式化を目指す予定です。

クラスタにアクセスできない場合の対処

上述のように、複数個あるMONのうちの正常動作しているものが半分以下になっていると、クラスタにアクセスできません。この場合はクラスタの外から働きかけて正常なMONが過半数になるように回復しなければなりません。

正常なMONが1つ以上ある場合

正常なMONが一つ以上ある場合は、正常なMONのデータを他のMONにコピーすれば問題を解決できます。この手順はCephにもRookにもドキュメント化されています。

docs.ceph.com

github.com

まず我々はRookの公式手順に従えば実際にクラスタにアクセスできるようになるかを確認しました。その結果、一部について問題があることがわかったため、問題を修正するPRをupstream Rookに送付しました。

github.com

このPRは幸いにもすぐにマージされました。ただし、このオペレーションは手作業でやるには複雑なので、今後はなるべく自動化していこうということを他のメンテナと合意しています。

正常なMONがひとつも残っていない場合

正常なMONが一つも残っていない場合、MONのデータはすでに信用できないものになっているため、オペレーションはより面倒になります。具体的にはOSDに保存されているMONのデータのコピーを利用して、最新に近いクラスタの状態を復元することになります。これについてCephでは以下のようにドキュメント化されています。

docs.ceph.com

このオペレーションについては残念ながらRookには対応するドキュメントが無かったため、我々はCephのものを参考に、一から手順を作り上げました。こちらについてもupstreamにPRを投げて、現在レビュー結果を反映中です。

github.com

こちらもMONが1つ以上残っている場合と同様、今後自動化していく予定です。

なお、本件についてはCephのマニュアルにも誤りを見つけたため、こちらについてもPR投稿中です。

tracker.ceph.com

github.com

今後の取り組み

オペレーションの手順ができたからといっても、まだまだ終わりではありません。まず既に述べたように、手作業によるミスを無くすためにさらなる自動化が必要です。それに加えて、次のようなことも必要です。

  • 上記オペレーションを、今後プロジェクトに入ってくる技術者たちに共有する
  • オペレーションが意味するところを理解するために、RookやCephについての知見を共有する。これができていないとオペレーション実行時にトラブルが発生しても対応できない
  • 上記オペレーションが必要になった際にすみやかに実行できるように、定期的に訓練をする
  • 社内用途で実際に使ってみる。ソフトウェアは実用してみてはじめて運用の勘所がわかると共に安定化してくる

今後もこれらの取り組みをどんどん進めていきます。

おわりに

本記事で話題にしたRook/Cephクラスタの安定稼働に加えて、ログ基盤の作成など、他にもやることはたくさんあります。ストレージ以外でもそれは同様です。参考までに、これまでに取り組んできたことについては以下リンク先の記事一覧をごらんください。

blog.cybozu.io

Necoプロジェクトはこれらの作業に一緒に取り組んでくれるかたを絶賛募集中です。我こそはというかたは、ぜひ以下URLからご応募ください。

cybozu.co.jp

*1:分散システムについてご存知の方に向けて書くと、これを実現するために分散合意アルゴリズムPAXOSを使っています

Viewing all 689 articles
Browse latest View live