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

 

モバイルチームはじめました

f:id:cybozuinsideout:20170209164231p:plain

はじめまして、モバイルチームの刈川です。みなさんの会社や組織における「モバイル」ってどんな感じでしょうか。昨年からサイボウズにもモバイル専用のチームができたので今回はその紹介をしたいと思います。

モバイルチームとは

モバイルチームの説明をする前にサイボウズが置かれていたモバイル事情についてお話します。

f:id:cybozuinsideout:20170209153425p:plain

サイボウズでは上図のように、製品ごとに各PGがモバイル開発を行っている状況でした。 ここで問題になっていたのが、

  • モバイル開発スキルがチーム間で分散する
  • テストやCI環境の整備などの作業が後手に回りがち
  • そもそもモバイル開発自体の優先度が低い(※Webアプリの会社なので)

このような問題がありました。 そこでモバイル開発に特化したチームを設けることでこれらの問題を解決しようとしました。 f:id:cybozuinsideout:20170209153540p:plain

モバイルチームがやったこと

各製品のモバイル開発にジョイン

前述の通り、モバイル製品を触れるプログラマは各製品チームにいるのですが、リソースや優先度の都合で対応が後手に回りがちなのが現状でした。そこでモバイルチームが各製品チームのモバイルプロジェクトにジョインすることでモバイル対応を支援してきました。

モバイルに関する情報を収集・共有

社内外からモバイルに関する情報を収集し、社内勉強会などを通じて共有を行いました。 情報の収集元は様々ですが、例えばDroidKaigiiOSDCGoogle for Mobileなどのカンファレンスに参加したり、Twitterやconnpass等で告知される社外勉強会への参加、また、Android WeeklyiOS Dev Weeklyの定期購読などを通じて情報を収集していきました。その中で製品コードに使えそうなツールやライブラリは積極的に製品チームに提案し、実際に導入されたものもいくつかあります。

iOS/AndroidのCI環境の準備・運用

モバイルプロジェクトの中にはテストをしっかり書いているものもあれば、テストが存在しないものもあったり、また製品チーム内だけでCI環境を回していたりと、情報や環境が分散しているのが現状でした。そこでモバイルチームでこれらの作業を受け持つことにより、社内で共通で使えるCI環境の整備を目指しています。CIにはAndroidはJenkins, iOSはCircleCIを利用しています。

ワークショップの開催

f:id:cybozuinsideout:20170209164444p:plain

これまでサイボウズのプロダクトはWebアプリケーションが中心でした。そのため、モバイルアプリの開発経験があるエンジニアは多くありません。そこで、まずは導入部分の障壁を取り払うためにAndroidアプリの基礎を学ぶためのワークショップをハンズオン形式行いました。教材に利用したのはUdacityでGoogleが公式に提供しているDeveloping Android Appsコースです。

プロトタイプの作成

f:id:cybozuinsideout:20170209164913p:plain

モバイルの強みのひとつに、機能を切り出して単機能アプリとして提供できることが挙げられます。例えばFacebookのMessengerのようなアプリです。そういった単機能アプリで実現できそうなプロトタイプをモバイルチームで作成し、各製品のPMに提案を行いました。作ったものの例としては、kintoneで使えるカメラアプリやビデオチャットアプリ、通知消化アプリなどの単機能アプリです。これらのプロトタイプアプリを社内で使ってもらってフィードバックを得ることで既存製品に組み込んだり、別プロジェクトとして立ち上げることも視野に入るようになりました。

モバイルチームのこれから

今年も引き続き活動をしてきますが、今年は特にプロトタイプの作成に力を入れていく予定です。既存製品の新機能や全く新しいモバイル製品の提案などをプロトタイプ作成を通じて行い、モバイルの可能性を社内外にアピールし、サイボウズから新しいモバイル体験を世の中に提供できるよう取り組んでいきます。そのためにはまずはモバイルのニーズをヒアリングし、アイデアを募り、多くのチームを巻き込んでサイボウズらしいチーム開発をしていきます。

 

Squid で安全なインターネットアクセス環境を構築する方法

こんにちは。情報システム部の松平です! 今回は社内ネットワーク内から安全にインターネット上の Web サービスを活用するためのプロキシ構築方法を紹介します。

1. Web プロキシの NAT に対する利点

企業では、ルータやファイアウォールなどの NAT 機能を利用して、社内ネットワーク内のコンピュータからインターネットにアクセスすることが多いのではないかと思います。
NAT の構成では、悪質なサイトなど、特定のサイトへのアクセスを IP アドレスでしかブロックできない場合があります。

IP アドレスでブロックした場合、AWSAkamai といった CDN まで対象に含めてしまうと、まったく関係のないサイトで利用されている CSS、JavaScript ファイルまでブロックしてしまうケースが出てきます。

そこで注目したいのが Web プロキシである Squid の活用です。 Web プロキシは、IP アドレスではなく FQDN でブロックが可能となっており、昨今の CDN を多用する Web サービスを安全に利用できます。 また、FQDN で接続ログが残るのも良い点になります。

2. Squid とは

Squid はコンピュータとサーバー間の通信を中継するプロキシサーバーソフトです。 様々なプロトコルに対応しており、 Web サービスへのアクセスに用いられる HTTP、HTTPS の通信を中継することができる Web プロキシサーバーになります。

3. 具体的な設定例

Squid は CentOS などの環境に簡単にインストールできます。インストール後、コンピュータ側のブラウザでプロキシの経由設定を行えば完了です。 ここでは CentOS 7.2 にインストールする手順を参考として記載します。

Squid を構築する

1) Squid をインストールする

yum -y install squid

2) Squid の設定ファイルを理解する

Squid では squid.conf ファイルを編集することでブラックリスト方式とホワイトリスト方式によるアクセス制御ができます。

squid.conf
# ローカルネットワークの定義
acl localnet src 10.0.0.0/8     # RFC1918 possible internal network
acl localnet src 172.16.0.0/12  # RFC1918 possible internal network
acl localnet src 192.168.0.0/16 # RFC1918 possible internal network
acl localnet src fc00::/7 # RFC 4193 local private network range
acl localnet src fe80::/10# RFC 4291 link-local (directly plugged) machines

# SSL接続時に 443 ポート以外の CONNECT を拒否
acl SSL_ports port 443
acl CONNECT method CONNECT
http_access deny CONNECT !SSL_ports

# 接続先として指定されているポート以外を拒否
acl Safe_ports port 80    # http
acl Safe_ports port 21    # ftp
acl Safe_ports port 443   # https
acl Safe_ports port 70    # gopher
acl Safe_ports port 210   # wais
acl Safe_ports port 1025-65535  # unregistered ports
acl Safe_ports port 280   # http-mgmt
acl Safe_ports port 488   # gss-http
acl Safe_ports port 591   # filemaker
acl Safe_ports port 777   # multiling http
http_access deny !Safe_ports

# キャッシュの設定( manager を定義してないので無効な値)
http_access allow localhost manager
http_access deny manager

# ローカルネットワークからのアクセスを許可
http_access allow localnet
   
# 自身からのアクセスを許可
http_access allow localhost

# ここまで一致しなかった場合は拒否
http_access deny all

# Squid が使用するポート
http_port 3128

# core 出力場所の設定
coredump_dir /var/spool/squid

# キャッシュの設定
refresh_pattern ^ftp:     1440    20%     10080
refresh_pattern ^gopher:  1440    0%1440
refresh_pattern -i (/cgi-bin/|\?) 0     0%0
refresh_pattern .   0 20%     4320   

初期設定では、localnet が acl として定義されており、定義したネットワークから定義したポート以外への接続が禁止されている構成になっています。
上記設定ファイル内の http_access から始まる行でアクセスルールを定めています。
これらの定義は、上から順番に評価されますので記述する順番をよく考える必要があります。

初期設定時のアクセスルール
↓ http_access deny CONNECT !SSL_ports # SSL接続時に 443 ポート以外の CONNECT を拒否  
↓ http_access deny !Safe_ports        # 接続先として指定されているポート以外を拒否  
↓ http_access allow localhost manager # manager が localhost から接続することを許可  
↓ http_access deny manager            # manager が接続することを拒否  
↓ http_access allow localnet          # localnet が接続することを許可  
↓ http_access allow localhost         # localhost が接続することを許可  
↓ http_access deny all                # ここまで一致しなかったものは拒否  
3) コンピュータ側のブラウザでプロキシの経由設定を行う

インターネットオプション – [接続]タブの、「LAN の設定」をクリックし Squid を経由する設定を行います。
f:id:cybozuinsideout:20170201151058p:plain

ブラックリスト方式

※ ここでは localnet の acl を利用した設定例を記載します。
ブラックリストは特定のサイトのみをブロックする方式です。原則としてアクセスを許可する場合は向いていますが、安全性でいえば後述するホワイトリストのほうが高いです。

1) ブラックリスト用にファイルを新規作成する

ブラックリスト用にファイルを新規作成し、アクセスをブロックしたいサイトを 1 行づつ記述します。 先頭に. (ドット)を入れることで、サブドメインを含めた指定が可能です。
2017/1/30 時点で cybozu.com において、URL のタイプミスを利用した攻撃サイトが確認されています。 タイプミスを利用した攻撃サイトのブロックを想定してみます。

blacklist
.cybouz.com
.cyboz.com
.cybosu.com

2) squid.conf を編集する

ブラックリスト用の acl 、http_access の定義を追加し、ブラックリスト記載のサイトをブロックします。

squid.conf

acl blacklist dstdomain "/etc/squid/blacklist"
http_access deny blacklist
http_access allow localnet
http_access allow localhost
http_access deny all

3) Squid の設定を反映させる

Squid をリロードして設定を読み込みます。

systemctl reload squid

ホワイトリスト方式

ホワイトリストは特定のサイトのみアクセスを許可する方式です。例えば、cybozu.com のみ許可するといった設定が可能です。

1) ホワイトリスト用にファイルを新規作成する

ブラックリスト形式と同様に、ホワイトリスト用のファイルを新規作成します。

2) squid.conf を編集する

ホワイトリスト用の acl、http_access の定義を追加し、ホワイトリスト記載のサイト以外はブロックするようにコメントアウトします。

squid.conf
acl whitelist dstdomain "/etc/squid/whitelist"
http_access allow whitelist
#http_access allow localnet
#http_access allow localhost
http_access deny all

3) Squid の設定を反映させる

Squid をリロードして設定を読み込みます。

cybozu.com を使うためのホワイトリスト設定

自社の cybozu.com 環境のみアクセスを許可したい場合は、上記のホワイトリスト方式を利用し、ホワイトリスト用のファイルに cybozu.com のアクセスに必要なドメインを追記します。

サブドメイン名.cybozu.com
static.cybozu.com
store.cybozu.com
www.cybozu.com
help.cybozu.com

4. プロキシ設定の配布

Squid を経由するためには、前述したとおり、コンピュータ側での設定が必要となります。コンピュータが大量にあると大変手間な作業ですよね。
また、ノート PC など、社内ネットワーク外にコンピュータを持ちだした場合、プロキシの設定を解除する必要が出てきます。

上記の課題について解決する選択肢の 1 つとして、 WPAD を利用したプロキシの自動検出があります。
WPAD の利用については、賛否あると思いますが、今回は効率的にプロキシを展開する方法として紹介します。

WPAD 方式

WPAD は、プロキシの設定を自動配布するための方法として開発された技術です。 WPAD は、Windows や Mac で利用可能な仕組みです。
プロキシの情報を自動配布するには、DHCP サーバーや DNS サーバーに対してプロキシサーバーを検出させる仕組みの設定が必要となります。
ここでは、DNS サーバーに対する設定を簡単にご説明します。

1) Web サーバーを構築する

まず、社内に Apache などの Web サーバーを構築し、社内のコンピュータからアクセスできるようにします。

2) 自動構成スクリプトファイルを配置する

Web サーバーのルートディレクトリに wpad.dat というファイル名の自動構成スクリプトファイルを格納します。
自動構成スクリプトファイルには、社内のプロキシの接続環境を記述しておきます。

wpad.dat
function FindProxyForURL(url, host)
{
        return "PROXY 192.168.0.10:3128;
        # PROXY サーバーIPアドレス:192.168.0.10 ポート番号:3128 の場合
}

3) DNS サーバーを設定する

DNS サーバーで wpad という名前の DNS レコード( A レコードまたは CNAME レコード)を登録し、社内ネットワーク上で、wpad の名前を解決できるようにします。

4) コンピュータ側の設定を確認する

Windows のブラウザの設定

初期値で [設定を自動的に検出する]のチェックがオンになっているため、特に設定することなくプロキシを自動検出できます。
f:id:cybozuinsideout:20170201151124p:plain

Macのブラウザ設定

初期値で[自動プロキシ検出]のチェックがオフになっているため、チェックをオンにする必要があります。
f:id:cybozuinsideout:20170201151140p:plain
Mac を社内ネットワーク外に持ち出した場合の挙動は利用するブラウザやアプリケーションの挙動に依存するようです。
Safari など社外で接続できない場合は、[自動プロキシ検出]のチェックをオフにする、またはネットワーク環境を切り替える必要があります。
詳しくは Apple 社のネットワーク環境を使うを参照ください。

Active Directory 方式

社内ネットワークのコンピュータが Active Directory により管理されている場合は、Active Directory のグループポリシーの基本設定を利用して、
コンピュータの[設定を自動的に検出する]のチェックを強制することや、自動構成スクリプトファイルの使用を強制することも可能です。
詳細を記載すると長くなるので今回は割愛させていただきます。
詳しくは Microsoft 社のグループポリシー管理コンソール-基本設定項目の構成をご参照ください。
なお、Active Directory 方式は、Mac は対象とできない点が欠点になります。

5. プロキシのログ監査

Squid では、どの IP アドレスからどのサイトへアクセスしたか /var/log/squid/access.log に記録するようになっています。
ログを保管しておくことで、ウイルス感染などの不正通信チェックなど、通信状況を把握する調査に役立ちます。 ログフォーマットの指定によりカスタマイズもできます。

6. まとめ

いかがでしたでしょうか。細かい設定まで説明しきれてなくて大変申し訳ないのですが、Squid を利用することで FQDN でサイトを許可、または拒否できることがイメージいただけたのではないかと思っております。 弊社 cybozu.com をご利用いただく上での安全な Web プロキシの構築方法として参考になれば幸いです。

7. 参考サイト

Japan IE Support Team Blog LAN のプロキシ サーバーの設定について
Japan IE Support Team Blog “WPAD” について

 

kintone と連携する図書管理システムを作ってみた

みなさんこんにちは。SRE チームの内田(@uchan_nos)です。

サイボウズでは去年の 11/30 から 12/02 にかけて、4 回目となる毎年恒例の社内ハッカソンが行われました。 業務時間で好きなものを作ってよいので、図書委員でもある私は Raspberry Pi 3 と NFC リーダー、バーコードリーダーを使った図書管理システムを作ることにしました。 (ちなみに、この図書管理システムはハッカソンの審査で大賞に輝きました。うれしい!)

年明け 1 月下旬、ついに図書管理システムが本格的に稼働しはじめましたのでご紹介します。 使用機材やソースコードも公開しますので、興味があれば試してみてください。

作ったもの

kintone 上の図書管理アプリの貸出・返却ステータスを、遠隔で変更する IoT システムです。 このシステムには社員証リーダーとバーコードリーダーが接続されていて、 誰がどの本を借りようとしているか(または返そうとしているか)を識別し、図書管理アプリのステータスを更新します。

f:id:cybozuinsideout:20170126005548p:plain

kintone とはウェブ上にデータベースのようなものを構築できる弊社サービスで、 弊社内の開発系書籍はその kintone 上に構築したアプリ(データベース)で管理しています。 このアプリには、書籍名、ISBN、貸出ステータス(本棚にある・貸出中・紛失中)、棚番号などが記録されています。

今回作ったシステムでは、今までウェブブラウザ経由で貸出ステータスを変更していたものを、 社員証のタッチとバーコードの読み取りでできるようにします。 いちいちアプリを呼び出してきたり書籍名を入力する手間がなく、また、システムを本棚に常設することで、 「借りたい」「返したい」と思ったときに 直感的 に使うことができるようになりました。 認知心理学の言葉を借りれば「アフォーダンスが劇的に向上した」といえます。

ハードウェア構成

簡単に買えるものだけで構成しています。

  • Raspberry Pi 3 Model B
  • バーコードリーダー BUSICOM BC-BR900L-W
  • NFC リーダー PaSoRi RC-S380
  • WiFi アダプタ WN-G300UA (本運用時のみ)
  • Raspberry Pi 7" Touch Screen LCD (本運用時のみ)
  • スピーカー

f:id:cybozuinsideout:20161201170534j:plain

Raspberry Pi 3 本体は裏にあるので見えませんが、ちゃんと動いています。 Raspberry Pi 3 には USB 端子が 4 つ付いており、バーコードリーダーと NFC リーダーに加え、 開発時にキーボードとマウスも接続できます。すごいですね。

WiFi アダプタは本運用時に追加で買ったものです。 本棚にシステムを設置してみたところ Raspberry Pi 3 本体に内蔵されている WiFi アンテナでは電波の飛びが悪く、ネットワークが不安定になりました。 WN-G300UA のおかげで 電波強度が向上 し、安定して運用できるようになりました。

実はこの WiFi アダプタ、USB コネクタ部が太く、上下の USB 端子で干渉してしまうことが分かりました。 アダプタを片方の USB 端子に挿すと、もう片方には何も挿せなくなるのです。 本運用のために調達しておいた Raspberry Pi 7" Touch Screen LCD は当初、タッチ機能 を使わないつもりでしたが、 アダプタ干渉のためマウスを接続するポートが足りなくなり、この機能が図らずも役に立ちました。 危なかったです(^^;)

ソフトウェア構成

メインの制御プログラムは Python 2.7 で作りました。 この制御プログラムが無限ループにより動作し続け、社員証の読み取り、バーコードの読み取り、kintone アプリのレコードの更新を行います。

本を借りたい・返したい人はこんな感じで操作します。

  1. 社員証をタッチ
  2. 書籍の ISBN バーコードをスキャン

このシステムは音声案内が流れるようになっています。 社員証をタッチすると「97 で始まるバーコードをスキャンしてください」と言われます。 スキャンすると、その書籍をまだ借りてないなら借り、すでに借りている場合は返します。

弊社の社員証は NFC 規格にしたがっていて、nfcpy と PaSoRi リーダーで簡単に読み取ることができました。 社員証の中に社員番号が記録されているので、誰がシステムを操作したかを特定するのに使っています。

バーコードリーダーは USB キーボードとして認識され、スキャンに成功するたびに文字列として読み取った値を送ってきます(例えば 9781530445387\n)。 標準入力から読み込んで処理します。

制御プログラムと kintone とのやり取りには pykintone を使いました。 kintone を操作するライブラリはいくつかあるようですが、今回はレコードのステータスを API からいじる必要があるため、それに対応したものである必要があります。 ステータスをいじる API が搭載されたのは kintone の 2016 年 5 月版からですので、メンテされてないライブラリだと対応していない可能性があります。

音声案内は AquesTalk Pi を利用しています。 このソフトウェアは、コマンドライン引数で文字列(日本語対応)を与えると、標準出力に wav 形式の音声データを吐き出すという、非常に簡単に使える仕組みになっています。 当初は音声案内が無かったのですが、AquesTalk Pi を利用するようにしてから、システムの UX がとても高まりました。

図書管理アプリ

Raspberry Pi から操作する対象の図書管理アプリは kintone にて構成しました。 下図のように蔵書を管理しています。

f:id:cybozuinsideout:20170125105700p:plain

このアプリ自体は以前から使われており、既に 1500 冊を超える本が登録されています。

それぞれの書籍情報は下図ようなレコードになっています。 (同じ本が複数冊ある場合、複数のレコードが登録されています)

f:id:cybozuinsideout:20161202133959p:plain

図書管理システムの第一目標は、このフォームにある「ステータス:レンタル中」を、自席の PC に戻ることなく バーコードをピッとするだけ で操作することです。 kintone ではフィールド以外にも「ステータス」を API から操作できるので、このシステムを実装することができました。

社員番号 - ユーザ名変換アプリ

会社によってポリシーは違うと思いますが、弊社の図書管理では 誰が どの本を借りたかを管理しています。 図書管理では様々なトラブルが発生するので(例えば、借りたい人がいるのに本棚に無いとか、「本棚にある」というステータスなのに見つからないとか) 最後に誰が借りたかが分かるとトラブルが解決しやすくなります。

弊社の社員証には 6 桁の社員番号が記録されています。 社員番号さえあれば個人の識別ができるはずですから、これでシステムが作れます。やったー!

というのはちょっと早とちり。 kintone 上でユーザを指定するには社員番号ではなく、kintone のユーザ名 を得る必要があるのです。 そこで今回は、社員番号と kintone 上のユーザ名を記録した kintone アプリを作って対処しました。

このアプリは本質的なところではないので、詳しくは紹介しませんが、 社員番号とユーザ名の 2 つのフィールドがあるレコードが、社員の数だけ並んだ単純なアプリです。

リソース

今回作成したソースコードは https://github.com/uchan-nos/hondana で公開しています。 オープンソースライセンス(MIT ライセンス)で公開しておりますので、ご自由にお使いください。

さいごに

今後は運用しながら出たバグを修正したり、使い方を分かりやすく掲示するなど、図書管理システムを便利にする活動をしていきます。

今回のシステムでは社員証と ISBN という、2 つの情報を組み合わせて「誰が何を」借りたかを管理しました。 もうちょっと頑張って、同じ ISBN を持つ複数の書籍を区別できるよう、1 冊 1 冊固有の管理番号を振り、バーコードを印刷して貼り付けておけば、社員証のタッチをなくすことができます。 これは実際の図書館が使っている方法ですね。

また、このアイデア自体は書籍に限らず「物を管理する」こと全般に適用できます。 既に総務部からひきあいが来ていて、電源タップなどの備品の貸し出し管理に使えたら便利だよね、という話をしています。 仕出し弁当の管理に使うこともでき、社内に存在するいくつかの問題を解決できそうな気がしています。

社員証を読み取る技術を応用すれば、社内でイベントを開催したときの参加者の記録を取れます。 そのほかにも無限に可能性が広がる技術なので、応用先を見つけていきたいと思っています。

 

エンジニアの複業採用始めました

こんにちは、エンジニア採用担当の岡田(@y_okady)です。サイボウズではこの度、サイボウズでの仕事を複(副)業とする方を募集する「複業採用」を開始しました。おかげさまで早速たくさんのご応募・ご意見をいただいているのですが、その中の一つに「副業で具体的にどんな仕事ができるのかわからない」といった声がありました。

具体的なことは何も書いてないしそりゃそうだよなーと思ったので、開発/運用のエンジニア職について現時点で考えている業務内容を共有させていただきます。

業務内容

Webアプリケーションエンジニア

  • 既存機能の改善、不具合改修、パフォーマンスチューニング、受け入れ試験自動化
  • 新機能のプロトタイピング
  • アーキテクチャ刷新に向けた研究開発、技術調査
  • モニタリングシステムの改善

担当していただく製品/プロジェクトは、kintoneNecoなどをイメージしています。

モバイルエンジニア (iOS/Android)

  • モバイルアプリケーションの開発
  • モバイル関連の技術調査
  • 新製品のプロトタイピング

生産性向上エンジニア

  • 社内業務改善ツールの開発
  • 開発支援ツールの開発
  • 継続的インテグレーションにおける性能検証の導入

※現時点でモバイルエンジニア、生産性向上エンジニアの募集要項はキャリア採用のページに掲載されていませんが、後日掲載を予定しています。

業務内容を書いてみたものの

実際に副業として働いているエンジニアはまだ一人もおらず、どんな業務をお願いできそうかはまだまだ手探り状態です。ここに挙げた業務以外にも、スクラムマスターやスクラムトレーナー、Jenkinsおじさんといった役割をお願いすることも可能なんじゃないかと考えています。この業務は大丈夫でこの業務はダメ、といったものはまだ一つもありませんので、まずはご相談いただいて、一緒に実現可能性を考えていきましょう。

勤務地について

勤務地は本業でも副業でも変わらず、東京、大阪、松山に加えてリモート(在宅など)も応相談となっております。今のところ完全リモートワークのエンジニアはまだ出てきていませんが、週2〜3回在宅勤務したり、1ヶ月まるまる在宅勤務するエンジニアも出てきています。完全リモートワークにもこれからチャレンジしていきますので、地方で働きたい方、在宅勤務したい方、お気軽にご相談ください。

おわりに

サイボウズは、多様な人々が多様なスタイルでつながるチームを目指しています。ワークスタイルや契約形態に関係なく、理想でつながり、一緒にチームワークあふれる社会を目指す仲間を増やすため、本業を持ちながらもサイボウズでの仕事に興味を持っていただける方を募集しています。

サイボウズの理念や考え方に共感いただける方の応募をお待ちしております。

複業採用 | サイボウズ 採用情報(新卒・キャリア)

(2017/1/23 追記)
ソーシャルメディア上でいただいた複業採用へのご意見に対して社長の青野が回答する記事が公開されました。サイボウズの複(副)業に対する考え方がまとまっておりますので、こちらも是非ご覧ください。 cybozushiki.cybozu.co.jp

 

我々はいかにして技術選択を間違えたのか? 2016

どうも!アプリケーション基盤チームの横田(@yokotaso)です!

kintoneなどで利用していたJavaフレームワークのSeasarのEOLに伴い、S2Daoからの脱却を試みたのですが、パフォーマンス問題や障害を発生させてしまうなど問題を多々発生させてしまいました。

同じ過ちを繰り返さないという強い決意のもと、今回の失敗をブログで公開いたします。 失敗をあえて公開する点で斬新かつ濃いブログ記事となっております!

失敗体験の公開は恥だが役に立つ!

移行先の選定の失敗

移行先として選定したプロダクトは Hibernate*1です。

Hibernateを選んだ理由としては

  1. Spring Framework を選定した

  2. Spring Frameworkで Interface + アノテーションでプログラミングするならSpring Data JPA が有力

  3. JPAに準拠したのORMの中でも、Hibernateがメジャーである。

この3つを主な理由として、S2Daoから、Spring Data JPAへ移行を試みました。

どう失敗だったのか?

以下の3つの点で失敗だったと考えています。

  • JPAに準拠することを目指しながらもEntityのプロパティ変更を自動的にDatabaseに反映する機能は我々は必要としていなかった

  • パフォーマンスの劣化、運用障害の原因になってしまった

  • Hibernateに問題があるケースの調査が難しく、コストが辛い

技術選定になぜ失敗したのか

設計思想の違いを意識できていなかった

失敗の原因の一つ目はS2DaoとHibernateの設計思想の違いです。

Hibernateはキャッシュ機構が充実したORMです。一方S2Daoはキャッシュ機能はあまり持たない、軽量なORMになります。

S2DaoとHibernateで同じように動作することに注力したため、S2Daoと比較した時に Hibernateが「できてしまう」ことの調査に注力ができませんでした。

結果的にこのキャッシュ機構に苦しめられることになります。

HibernateとS2Daoのキャッシュの仕組みの比較

S2Dao Hibernate
クエリ・キャッシュ
エンティティ・キャッシュ
メタデータ・キャッシュ

単純な比較表ですが、キャッシュの機構としてはHibernateのほうが充実しています。

クエリ キャッシュ
  • Hibernate
    アノテーションで宣言されたクエリをキャッシュする仕組み。それとは別にQueryPlanCacheオブジェクトでキャッシュする

  • S2Dao
    SQLコメント機能を利用するためにパースしたクエリをキャッシュする仕組みを持つ

エンティティ キャッシュ
  • Hibernate
    SELECTなどで生成したEntityは、EntityManagerにキャッシュされる。EntityManagerはEntityの変更を検知する。Databaseと自動的にシンクされる。

  • S2Dao
    エンティティ キャッシュの仕組みはない

メタデータ キャッシュ
  • Hibernate,S2Daoの両方でクラスのメタデータをキャッシュする仕組みがある

  • Hibernateのほうがメタデータに関しても多機能である

得られた教訓

フレームワーク移行は、互換性を確保すると同時に、新しく「できるようになること」の影響も調べよう。 できるようになることも、できなくなることも、移行元のフレームワークから差が大きくないのが望ましい。

長いものに巻かれることにこだわりすぎた

もうひとつの失敗の原因がズバリ長いものに巻かれることにこだわりすぎたことです。

SeasarがEOLになったこともあり、EOLに対する恐怖心がありました。

そのため、移行先のプロダクトは「コミュニティが活発である」、「JPAに準拠している」ことを重視しました。上のような理由のため他ORMの選択肢が少なくなりました。

途中からHibernate一択という状態になってしまい、他のORMとの比較もおろそかになってしまった点も反省しています。JPAに準拠することも諦めるべきでした。JPAにできるだけ準拠することも他のプロダクトの選択肢を減らす原因にもつながりました。

JPAを諦めていればspring-jdbcの中に入っているJdbcTemplateを利用することも検討できました。S2Daoに比べると機能落ちになる面もあるのですが、キャッシュ機構がないので、提供される機能はS2Dao時代と近いです。

後で障害の例を紹介しますが、JdbcTemplateを利用すれば運用障害になるケースは避けられた可能性があります。

得られた教訓

長いものに巻かれるのは大事だが、選択を誤る(謝る)バイアスになる可能性があることに注意すべきである。

移行後に発生してしまった障害

Hibernateがらみの移行で、パフォーマンス問題や障害につながってしまうケースが発生してしまったのですが、味わい深い2つの障害を紹介します。

バッチ処理でOutOfMemoryError

Hibernateはトランザクションの内部で実行されたDELETE/UPDATE文はすべて保持するようになっています。

これはHibernateがSQLとは別にEntityをキャッシュする仕組みを持っているためです。トランザクションの最後にDELETE/UPDATEをSQLとして実行します。

100回程度のDELETE/UPDATEを実行する分には問題になりませんが、10万回レベルのDELETE/UPDATEを実行するようなケースは、OutOfMemoryErrorを発生させる原因になります。

移行後、長時間にわたるバッチ処理が、処理途中からFullGCが大量発生し、OutOfMemoryErrorに至る現象が発生するようになり、原因調査とHibernate内部の調査に大きな時間を割くことになりました。

参考: DELETE/UPDATEでOutOfMemoryError

クエリ・キャッシュが原因でOutOfMemoryError

Hibernateはクエリを内部にキャッシュする仕組みがあります。

Hibernateは次のようなクエリは、すべて別のクエリとしてキャッシュされてしまいます。IN句の数が違うだけです。S2Daoではこの機能はありませんでした。

SELECT * FROM book WHERE id IN (p1, p2, p3);
SELECT * FROM book WHERE id IN (p1, p2, p3, p4);
SELECT * FROM book WHERE id IN (p1, p2, p3, p4, p5);

Hibernateのクエリキャッシュは、LIRSキャッシュアルゴリズムが利用されており、キャッシュが増殖しつづけるのを抑制する仕組みは存在しています。しかし、次の2つの現象が同時に発生したときに障害になる可能性があります。

  • IN句の中身が大量

  • IN句の個数が1つでも違うとすべてキャッシュされる

これが原因で、FullGCに伴うJVMのハングとOutOfMemoryErrorを発生し、障害が発生しました。

参考: QueryPlanCacheでOutOfMemoryError

まとめ

  • フレームワークの移行は後方互換性とともに移行後に「できるようになってしまう」ことも調査しよう

  • 長いものに巻かれるのも大事な要素だが、それにこだわりすぎる余り、技術選択の視野が狭くならないように注意しよう

  • 移行先のフレームワークが一択の状態は危険なサイン。移行先フレームワークは複数用意するようにしよう

最後に

楽しんでいただけたでしょうか?皆様のお役に立てれば幸いです。

今回の移行に際して運用環境のパフォーマンス低下や運用障害を発生させたことは我々の不徳のいたすところであり、猛省を重ねております。

サイボウズは、今後も同じような失敗を繰り返さぬように技術の研鑽を続けていきます。

*1:我々の技術選択の失敗に至るプロセスを公開・共有が目的です。Hibernateを貶める意図は一切ないことを強調させていただきます。