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

Webアクセシビリティに必要なのは「理想への翻訳」 - FRONTEND CONFERENCE 2016参加報告

$
0
0

こんにちは、kintone開発チームの小林です。3月5日に大阪で開催されたFRONTEND CONFERENCE 2016に登壇しました。発表内容は、私がWebアクセシビリティの重要性をサイボウズ社内で伝えていった取り組みについてです。 カンファレンス登壇中の写真

資料はこちらです:

www.slideshare.net

発表中のツイートをTogetterにまとめました:

「あなたの言葉で伝えるWebアクセシビリティ」への反応 - Togetterまとめ

Webアクセシビリティの重要性が理解されない!

「Webアクセシビリティとは、障がい者・高齢者対応のことだ」と言われることがあります。しかし、これは正しい解釈ではありません。Webアクセシビリティとは、障がい者や高齢者も含めて、「すべての人」がWebにアクセスできることです。

例えば、Webアクセシビリティでは、色のみに依存した表現を避けるべきだ、と言われています。色覚異常の人など、色を区別することが難しい人たちにとって、色のみに依存した表現を使ってしまうと、正しい情報にアクセスできない可能性があるからです。

以下の画像には3つのグラフが描かれています。左側のグラフは一般の色覚の人が見たグラフ、真ん中と右側は、一般の色覚でない人が見た色を再現したグラフです。一般の色覚でない人にとっては、グラフの内容が明瞭に読み取れないことがあることがわかります。 様々な色覚で見え方を比較したグラフ

しかし、色に対する配慮は一般の色覚を持った人にも重要な場合があります。例えば、白黒印刷した場合や、Kindleで表示した場合です。このような状況では、色の情報が失われてしまうため、正しい情報にアクセスできない可能性があります。また、色が表示できる端末であったとしても、屋外など、日光が強くあたっている環境では、色が正確に視認できるとは限りません。色のみに依存した表現は、障がいの有無にかかわらず避けるべきなのです。

Webアクセシビリティとは、人、デバイス、コンテキスト(利用環境)が、それぞれ多様であったとしても、Webの情報にアクセスできるようにすることです。障がい者や高齢者など「人」にフォーカスされることの多いWebアクセシビリティですが、本質的には、人を含めたあらゆる多様性の中で、Webにアクセスできることを指しています。

そして、どんな状況でも普遍的にアクセスできることこそがWebの本質であり、だからこそWebアクセシビリティは重要なのだ、と説明されることがあります。以下の文章は、Webアクセシビリティの理念として、よく参照されるTim Berners-Lee氏の言葉です:

The power of the Web is in its universality. Access by everyone regardless of disability is an essential aspect.

(拙訳: Webの力はその普遍性にある。障がいの有無にかかわらず、誰もがアクセスできることが本質的な側面なのだ。)

しかし、このようなWebアクセシビリティの理念を伝えたとしても、多くの人に共感されるとは限りません。他に優先すべきタスクがある、と言われてしまったり、プロダクトにとって重要なの?と疑問を投げかけられてしまったりします。

チームの理想に翻訳する

Webアクセシビリティの重要性に共感してもらうために、私が最も重要なプロセスだと考えているのは、Webアクセシビリティの理念をそのまま伝えるのではなく、「チームの理想」に翻訳して伝えることです。

「理想」や「チーム」はサイボウズ社内で定義されている用語です。「理想」とは個々人が望んでいる状態を指し、「チーム」とは「共通の理想を実現するために行動する集団」を指します。 「会社」は「チーム」のひとつです。なぜなら会社には「全社目標」や「ミッション」といった、共通の理想があり、社員は理想を実現するために行動しているからです。

「チーム」は共通の理想の実現に寄与しない行動をとりません。「売上◯億円」という理想を掲げたチームにおいて、無駄な出張や、費用対効果の乏しいキャンペーンなどは、理想を阻害する行為として抑制されたり禁止されたりします。

Webアクセシビリティの理念を伝えても、人の行動が変わらないのはなぜか?これには様々な原因が考えられますが、重要な原因のひとつは、みなさんの相手にしているものが「チーム」であり、Webアクセシビリティがチームの理想に寄与しないと判断されたからではないでしょうか。

例えば、社内の1人の開発者にWebアクセシビリティの理念を伝えたとします。その開発者自身はWebアクセシビリティの理念に共感するかもしれません。しかしその開発者は開発チームに属するメンバーのひとりであり、開発チームの理想にしたがって行動しています。チームは、理想の実現に寄与しない行動をとりません。Webアクセシビリティの理念がいかに素晴らしいものであっても、「開発チームの理想に寄与しない」と開発者が判断した場合、開発者の行動を変えることは困難です。

この状況を克服するためには、Webアクセシビリティの理念をそのまま伝えるのではなく、チームの理想に翻訳して伝えること。言い換えれば、チームの理想にとってWebアクセシビリティがどんな意味を持つのか、を伝えることが重要です。

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

ここからは具体的に、私が「サイボウズ」というチームに対して、Webアクセシビリティが持つ意味について考えたことをお伝えします。

「サイボウズ」というチームの理想は、「チームワークあふれる社会を創る」というものです。会社、学校、地域のサークル、家庭、公共団体など、世の中には様々なチームがあります。サイボウズの理想は、それぞれのチームが、チームワークを向上させ、よりよい社会が実現されることです。サイボウズのグループウェアは、この理想を実現するために開発されています。

では、このサイボウズの理想にとって、Webアクセシビリティはどのような意味をもつのでしょうか。これを考えるには、「アクセシビリティ」という言葉の意味に立ち返ることが重要だと私は考えました。

アクセシビリティ(Accessibility)とは、アクセス(Access)できる能力(Ability)のことです。これは何に対するアクセスなのでしょうか?通常のアクセシビリティの意味で解釈すれば、アクセスの対象はサイボウズの製品・サービスということになります。しかし、ユーザは、サイボウズの製品・サービスにアクセスすること自体を目的に行動するわけではありません。より本質的な問いかけは、ユーザは、わたしたちの製品・サービスにアクセスすることで、一体何にアクセスしているのか?ということです。

サイボウズのユーザが本当にアクセスしているもの、それはチームです。ユーザは、チームに参加したい、チームの活動に貢献したいと願って、サイボウズの製品・サービスにアクセスするからです。したがって、サイボウズにとって、アクセシビリティとは、「ユーザがチームにアクセスできる能力」です。そして、サイボウズにとって、Webアクセシビリティを確保することの意味は、「チームに参加(アクセス)したい」というユーザの願いを尊重することです。これが、Webアクセシビリティの理念をサイボウズの理想に翻訳した結果です。

翻訳した内容を広める

Webアクセシビリティの理想をサイボウズの理想に翻訳したのち、私は、翻訳した内容をさまざまな人に広めることにしました。

サイボウズの開発チームでは、たくさんのLT大会が開催されています。私はまずサイボウズにとってのWebアクセシビリティの意味を社員に伝えることにしました。 また、サイボウズには、自分で作ったグループウェアを自分たちの業務で使う「ドッグフーディング」の文化があります。私は、グループウェア上でも、Webアクセシビリティに関する多くの書き込みを行いました。

次第に、Webアクセシビリティに関してより専門的に話し合う場の必要性を感じ、Webアクセシビリティに関する勉強会を企画することにしました。 勉強会は、デザイニングWebアクセシビリティという本を輪講する形式をとりました。私は、勉強会の初回で、サイボウズにとってWebアクセシビリティが持つ意味を参加者に伝えました。

いろいろな施策の結果、多くの社員が、一般的なWebアクセシビリティの理念に終始することなく、サイボウズとしての意味を考えてくれるようになりました。以下の画像は、Webアクセシビリティ勉強会で発表した社員が、最後のまとめとして書いたスライドです。「アクセシブルなナビゲーションを設計することは『チームに参加したい』というユーザの願いを叶えるためにも必要なこと」だと書いています。 Webアクセシビリティ勉強会のスライド。所感: 目的の情報にアクセスしたくても、たどり着けないことには意味がない。アクセシブルなナビゲーションを設計することによって、Webを利用するユーザが、目的の情報にたどり着きやすくなる。つまり、アクセシブルなナビゲーションを設計することは「チームに参加したい」というユーザの願いを叶えるためにも必要なこと。

チャンスを大切にする

現在、サイボウズの社内では、Webアクセシビリティへの認知や啓蒙が徐々に進んでいる状況です。今後、サイボウズの社内でWebアクセシビリティを推進していくには、以下のような多くの課題が残されています:

  • Webアクセシビリティの取り組みが全社で合意できていない。
  • Webアクセシビリティ基準に関する明確な目標が定まっていない。
  • Webアクセシビリティを製品に導入するプロセスが確立されていない。

しかし、課題が解決されていない状態の中でも、次第にWebアクセシビリティに関する実装や配慮が増えてきています。例えば、一部の画面で文字の視認性を向上させるため、文字と背景色のコントラスト比を一定値以上にする取り組みを行なったり、一部のフォームをキーボード操作に対応させたりしています。

コントラスト比の取り組みについては、デザインリニューアルのタイミングでプロダクトマネージャに提案し、採用してもらうことができました。また、キーボード対応については、開発合宿でプロトタイプを実装したことで、実現にこぎつけました。Webアクセシビリティをサービスに導入するプロセスが確立されていない場合、地道に啓蒙活動を続けながら、チャンスを活かすことが重要だと私は考えています。

翻訳者は「あなた」

ここまで、Webアクセシビリティの理念をチームの理想に翻訳し、翻訳を広め、実現するプロセスについて説明してきました。このうち最も重要なプロセスは「翻訳」であると私は考えています。翻訳ができていなければ、翻訳した内容を広めることも、実現することもできないからです。では、Webアクセシビリティの理念をチームの理想に翻訳できる「翻訳者」とは誰なのでしょうか?

私は、翻訳者は読者のみなさんだと思います。なぜなら、みなさんはチームに属しているからです。会社に勤めている方は勿論、フリーランスの方であっても、顧客と一緒に開発に取り組む以上、チームの一員であるはずです。みなさんは、Webアクセシビリティの理念とチームの理想、両方を知っています。きっとみなさんは翻訳する力を持っているはずです。

多くの方が翻訳者となり、一般的なWebの理念ではなく、みなさんの言葉でWebアクセシビリティの重要性を伝えることで、みなさんのサービスがアクセシブルになることを願っています。


オープンソースカンファンレンス 2016 Tokyo/Spring に出展しました

$
0
0

こんにちは、サイボウズ・ラボの星野です。

先日明星大学で開催されたオープンソースカンファレンス 2016 Tokyo/Spring (以下、OSC) にサイボウズは初めてスポンサーとして出展しましたので、簡単に報告させて頂きます。

サイボウズ・ブース!

f:id:cybozuinsideout:20160308163322j:plain

設営直後のブースです。結構目立つので、お客さんが興味を示してくれたと思います。

出展内容

f:id:cybozuinsideout:20160308163318j:plain

我々が展示した内容は以下の 3 つです。

  • サイボウズで作っているオープンソースソフトウェアの紹介
  • サイボウズ・ラボユースの宣伝
  • サイボウズの中の人が書いた本の展示

サイボウズで作っているオープンソースソフトウェアの紹介

cybozu.comによるクラウドサービスを始めてから、弊社でオープンソースソフトウェア(OSS) を扱う機会が増え、また、自社で使いたい、しかし弊社ビジネスには直接関係ないソフトウェアをOSS として開発することが増えました。その取り組みをご紹介し、サイボウズでのソフトウェア開発について知ってもらいたいというわけです。

今回は、インフラ開発をしている Hazama チームの取り組み (memached 互換 key-value ストア yrmcds など、レポジトリ)と、私が主に取り組んでいる Linux ブロックデバイスのバックアップシステム WalBを紹介するチラシを作り、展示、配布しました。

サイボウズ・ラボユースの宣伝

サイボウズ・ラボユースは、 学生さん向けの企画で、自分のやりたい OSS 開発を行うと、お金がもらえてサイボウズ・ラボメンバーの技術面でのサポートも受けられるという制度です。取り組みが始まってから 5 年経ち、ラボユース卒業生達のその後の活躍も聞こえてくるようになりました。

今回の OSC では、ラボユースのことを広く知ってもらう目的で、チラシを作って配りました。過去・現在のラボユース生も何名か手伝ってくれて、彼等の最近の取り組みについてのチラシを用意してもらって一緒に配りました。

サイボウズの中の人が書いた本の展示

ブースでは、弊社エンジニアが執筆した技術書を展示しました。他にもエンジニアが書いた本はあるのですが、スペースの制約で 3 冊だけでした。

予算の都合でプレゼントはできませんでした、すみません。実際に本を見掛けて寄ってらっしゃるお客さんを見て、その効果を実感しました。すごい。私は本を書いたことがないので、書けるものならそのうちお客さんが寄ってきてくれる本を書きたい、と思いました。

その他

それ以外に、tech@サイボウズ式のステッカーを今回作って配りました。サーバに貼って安定運用されることを願う御札?型ステッカーです。ステッカーそのものには会社名や製品名を入れず、ネタとしておもしろいもの、純粋にもらった人が貼りたくなるようなものを今後も作っていきたい、と担当者は語っています。ご期待ください。

あれ、製品の展示はしないの?と思ったあなた、はい、今回製品展示はしていません。というのも、出展の手伝いに行った弊社の人間は、私を含めてどちらかというとインフラ寄りのエンジニアが中心だったため、自分達が説明できるものを展示しました。OSC の参加者は技術者が多く、製品のアピールよりは技術のアピールの方が良いと考えたためです。

反省会

開催後、社内で反省会を開いて次に繋げる議論をしたのですが、その中からいくつか挙げておきます。

  • 製品の展示はしない、という方針で望みましたが、 ブースに立ち寄ってくださった方々の中で、製品についての話題や質問される方もいらっしゃいました。 分かる範囲で説明したのですが、製品のパンフレットなどは準備しておけばよかったです。

  • 写真をたくさん取って後でブログ用に使う予定だったのですが、後になって SD カードの入れ忘れ、という痛恨のミスが発覚。 リスクを減らすためにも写真は分担してそれぞれで取った方が良かったですね。

  • 限られたブースのスペースをうまく使いこなせていませんでした。 特に、常時何かを流そうとディスプレイを 2 つ持っていったのですが、コンテンツを準備する時間があまりなく、 ひとつは何も写していませんでした。。展示物のバランスを考えると共に、次回参加時はもう少しコンテンツを練っていければと思います。

感想

最近このような活気のあるイベントにあまり参加していなかったので、少し気持ちが若返りました(^^ OSC 初回から参加しているメンバーもいたため、なんとなく事前に雰囲気は聞いていましたが、やはり実際に味わうと違いますね。私は OSC 初参加だったため、自社ブースの手伝いは少なめで、他企業の展示やコミュニティブースなどを見学する時間を多めにもらいましたが、おもしろそうなところで足を止めて話を聞いていると、いくら時間があっても足りず、時間があっという間に過ぎた 2 日間でした。

おわりに

今後も、試行錯誤しながら、サイボウズの OSS への取り組みなどをアピールしていきたいと考えておりますので、ご興味のある方は、是非 OSC にご参加ください!

アーキテクチャ刷新プロジェクト「Neco」の紹介

$
0
0

f:id:cybozuinsideout:20160310015353j:plain

@ymmt2005こと山本です。 今回は開発本部と運用本部のメンバーが協力して進めている cybozu.comサイボウズ Liveのアーキテクチャ刷新プロジェクト「Neco」について紹介します。

Neco を 3 行で説明すると、

  • サイボウズのクラウドインフラのいけてないところを洗い出して、
  • 5 年程度を目安に改善するつもりだけど、
  • やりたいことが多すぎるので、We are hiring!

で済んでしまうのですが、それだけでは面白みに欠けますので、いけてない内実を暴露しながら解説いたします。

サイボウズはクラウド 5 年生

正確に言うとサイボウズ Liveなど一部のサービスはもっと以前から取り組んでいたのですが、本格的にクラウドサービスといえるインフラを構築してサービス提供を開始したのは今から 4 年前の 2011 年 11 月となります。そこでオープンしたのが cybozu.comでした。

この 4 年で、以下のようにサービスは成長しました。

  • 有償契約社数 1 万 3 千社超
  • サーバー台数 1,000 台規模
  • 平日アクセス数 1.6 億 (ピーク時秒間 3,000 アクセス)

企業向けサービスの特徴なのか、ユーザーとアクセス数はほぼ一定の率で増加してきています。

ここがいけてないよ、サイボウズ!

100 台未満のサーバー規模の頃から引き摺っている仕組みがあちらこちらにあるため、「そんなこともできていないのか」と思われそうな部分が多々あります。

ログ分析基盤どころか回収さえ辛い

クラウドと言えば大量データとそれを活かしたデータ分析、と洒落こみたいところですが、まだサイボウズにはしっかりした分析基盤と言えるものがありません。

それどころか、膨れあがったアクセスログや各種ログを回収してある程度の分析が可能な形式にするのも満足にできていません。重要なアクセスログはなんとか形をつけているものの、製品が出力する(あるいはしたい)各種ログの回収・分析は後手にまわっています。

賢くない検索

ほとんどのサービスで全文検索機能を提供してはいます。してはいるのですが、スコアリングが賢くないため検索結果は新着順で表示しています。

スコアリングを改善しようにも、インデックスを再作成する作業が大きく人手に依存しているため気軽に試せない状況です。実際、検索結果を改善しようと何日もかけてインデックス再作成をしたものの、おもうような精度が得られずに断念したこともあります。

温かみのある人手デプロイ

cybozu.comの主な製品は kintone, Garoon, Office, メールワイズの4 つですが、kintone 以外はオンプレミス版をもとに、クラウド版を開発しました。

cybozu.com では共通のログイン画面やユーザー管理機能にこれらの製品を連携する仕組みになっていますが、出自がばらばらのため、デプロイ方式もばらばらになっています。

一番大変なのが製品のバージョンアップで、バラバラに機能開発した製品をまとめてバージョンアップさせるのに、製品アーカイブの組み合わせを人手で管理していたりします。

レプリケーションしない MySQL

サイボウズでは MySQL を各所に利用していますが、大半でレプリケーション機能を使っていません。びっくりですよね?

レプリケーションしたくないわけではなく、優先順位の問題でこうなっています。

具体的には、サイボウズ Office やメールワイズといったファイルシステムを直接使う製品のストレージを冗長化する仕組み(square)や VM 自動回復の仕組み(月読)を優先して実装したので、MySQL は square/月読上で動かすことで冗長化しています。

全部オンプレミス

今時珍しい、AWS も GCP も使っていない自前インフラです。cybozu.com の B2B という特性上、データセンターの見学やサーバーの物理機材の管理を求めるお客様の要望に応えるためです。

前述の障害自動回復システムやサーバーの完全自動インストール( BIOS や RAID の設定まで完全に自動化してます)などの取り組みの結果、管理工数は抑えています。突発的にユーザーが増えることもないので、スケーラビリティに悩むこともほとんどありません。

とはいっても、AWS を使いたくなることもあります。海外にサーバーを置きたいとか、cybozu.com とは無関係の(B2Bでない)サービスに利用するといった場合です。

改善してきたこともあるよ!

いけてないことばかりだと意気消沈するので、やってきたことも挙げておきます。 当ブログの記事から抜粋すると:

などなど。

Neco でやりたいこと

サイボウズのクラウドインフラの現状を乱暴にまとめると、経年劣化して非効率になっていると言えます。人手に頼る場面が多すぎますし、製品のリリース作業も時間と人手が大変多くかかっている状況です。

もっと効率良くしたい。効率良くして、もっと良い製品を素早く提供したい。

こうした思いから開発本部と運用本部で立ち上げた共同プロジェクトが「Neco」です。まずはじめに、現状の問題点を 19 の分科会に分けて議論をしました。

以下は分科会の一部です。

  • Operational Simplicity
  • ストレージ・バックアップ
  • 検索エンジン
  • サービスディスカバリとリソースプール
  • ネットワーク

方針が決まったものから、担当者を割り当てて進めています。夢物語で終わらせないよう、プロジェクトの期間は全体で 5 年としています。一つ一つ改善を積み上げて、5 年後には見違えるほど効率化してやるぜ!という心意気です。

活動成果は、順次 Inside Out で紹介していきます!

最後に

なぜ Neco なのかは、入社した人だけが知ることができます。サイボウズでは、改善に燃える技術者をいつでもお待ちしております。

We are hiring!

NAT をやめて、透過 SOCKS プロキシを導入した

$
0
0

以下の記事内容について、奥一穂氏(@kazuho)より、「connectのエラーコードが信頼できなくなるといった欠点もあるのに透過 SOCKS プロキシが汎用的に良いように読めてしまう」というご指摘をいただきました。確かに、下記内容は当社が抱えていた複数の課題を短期間で解消できる「ワークアラウンド」として透過 SOCKS プロキシという技法もあることを紹介したものであり、NAT と比較して常に良いという主張をしたかったわけではありません。また、記事内では解説を省きましたが、従来より HTTP(S) 通信は NAT ではなく HTTP プロキシを利用しています。謹んで補足・訂正とさせていただきます。

猫が好きだけど猫アレルギーで近寄ることができない山本泰宇です。 先日アーキテクチャ刷新プロジェクト「Neco」を紹介しましたが、今回はその活動の一環として実施したネットワークアドレス変換(NAT, Network Address Translation) を廃止して透過 SOCKS プロキシに置き換えた件を紹介します。

多少長い記事ですので、内容を先にまとめておきます:

  • NAT は透過性が利点だが、欠点でもある
  • SOCKS の弱点である透過性の問題を自作透過プロキシ transocksで解決
  • 透過プロキシには LD_PRELOAD より断然 iptables 方式が良い
  • SOCKS サーバーも必要な要件を実装したものを自作
  • Go はいいぞ

NAT の欠点

ご存知の方も多いと思いますが、NAT はインターネットに直接接続していないプライベートネットワーク内のコンピュータのアドレスを、NAT 装置が持つグローバル IP アドレスに書き換えることで、プライベートネットワーク内のコンピュータがインターネット上のサーバーと通信できるようにする技術です。

IP パケットのプライベートアドレスを NAT 装置が書き換えるだけなので、LAN 内のコンピュータは自分がグローバル IP アドレスを持っていないことを意識することなく(= 透過的に)インターネット上のサーバーと通信ができます。

一方で、この透過性には欠点もあります。実際サイボウズで直面した問題を紹介します。

  • ルーティングがネットワーク装置に依存しているため、出口を柔軟に選べない

    ある程度以上の規模のクラウドサービスでは、各国・地方に専用線を通じた高速なアクセスを提供したいといった理由で、インターネットに接続している出口を複数持つことが多いと思います。NAT の場合、どの出口を選ぶかはスイッチのルーティングによるため、ポリシーベースルーティング (PBR)などの設定で対応します。

    しかしながら、ネットワーク層での設定は通常アプリケーション層とは異なる人がメンテナンスしているため、アプリケーション層からすると柔軟性に欠けることになります。

  • 通信先の設定によっては、通信できないことがある

    有名な例に、Linux の net.ipv4.tcp_tw_recycle を有効にすると NAT の内側から接続できなくなるというものがあります。インターネット上のサーバーでは tcp_tw_recycle を有効にするべきではないのですが、有効にしているサーバーは実際に存在します。

    対策としては TCP タイムスタンプを付けないようにすることで、NAT 装置によってはタイムスタンプを取り去る機能を持つものがあります。

  • NAT 装置は通信先サーバーの IP アドレスとポート番号しか分からないため、ドメイン名での規制ができない

    例えば cybozu.com のサブドメインは全て同じ IP アドレスを持つため、demo.cybozu.com だけ通信を許可するといったことができません。各種の CDN も同様の事情を抱えているため、NAT で通信規制すると CDN をまるごと規制するしかなく、各種 Web サービスの利用が困難になる場合があります。

    IP アドレスではなく、ドメイン名(FQDN)で規制したい場合に困るというわけです。

まとめると、以下が NAT の欠点です。

  1. ルーティングの柔軟性に欠ける
  2. 通信先によっては通信できないことがある
  3. ドメイン名で規制ができない

SOCKS とは

プライベートネットワーク内のコンピュータが外部と通信する技術は NAT 以外に、プロキシという方法もあります。「代理」という意味で、文字通りプライベートネットワーク内のコンピュータの代わりにインターネット接続を代行してくれるサービスです。

主にウェブで利用する HTTP プロキシが有名ですが、SOCKSは電子メール通信など TCP/IP 通信全般を対象にできるプロトコルです。SOCKS5 では IPv4/v6 アドレスやドメイン名で接続先を依頼することができます。

NAT と違い、SOCKS は利用するプログラムが明示的に SOCKS プロトコルで SOCKS サーバーに接続する必要があるため、透過的ではありません。そのため、SOCKS 対応していないプログラムはそのままではインターネット上のサーバーに接続できないことになります。

透過性がない代わりに、NAT の欠点は以下のように回避できます。

  1. 接続先 SOCKS サーバーをプログラムが選択できるため、柔軟にルーティングできる
  2. SOCKS サーバーが先方と直接通信するため、tcp_tw_recycle 問題は発生しない
  3. ドメイン名で規制できる

透過 SOCKS プロキシの実装方式

SOCKS 未対応のプログラムをどうにかして SOCKS 対応させることができると、NAT の欠点を回避することができます。そのためにはプログラムに手をいれずに通常の TCP 通信を SOCKS サーバーへの通信に変換する必要があります。

プログラムに手を入れずに TCP 通信を変換する方式としては、動的リンカで connect等のシステムコールを自作のものに置き換える LD_PRELOAD方式やカーネルモジュールでシステムコールテーブルを乗っ取る方式も考えられますが、あまり良いアイデアとは言えません。

LD_PRELOAD 方式はまず libc の動的なシンボル解決に依存していますので、静的リンクされているバイナリや libc に依存していない Go のプログラムには効果がありません(c.f. https://github.com/golang/go/issues/3744)。

またシステムコールを乗っ取るといっても、透過性を実現するためにどこまでのシステムコールを乗っ取るべきかという難しい問題があります。最低限は connect, getpeernameなどですが、非同期 connect に対応するなら select, poll, epollなども必要でしょうし、TCP Fast Openなら sendmsgなども... と考えていくとどこまでやれば確実といえるのか、不安が残ります。

通信を乗っ取るのであれば、それ専用に設計されている仕組みを利用する方式のほうが筋が良いでしょう。というわけで Linux であれば iptables (ip6tables)を使うのが断然良いです。iptables でインターネット向けのパケットを一旦別のプログラムに渡るようにし、そのプログラムが SOCKS サーバーと通信してデータを転送するようにします。

今回はルーティングをプログラムで制御したいので、ローカルで生成されたパケットの宛先を制御できる DNAT / REDIRECTを利用することにします。具体的には nat テーブルの OUTPUT チェーンに以下のようにルールを設定すると、ローカルホストの 1081 版ポートで動作しているプログラムに通信をリダイレクトできます。(ルータにしかけるのであれば、TPROXYを利用する方式も考えられますが、割愛します)

*nat
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
:TRANSOCKS - [0:0]
-A OUTPUT -p tcp -j TRANSOCKS
-A TRANSOCKS -d 0.0.0.0/8 -j RETURN
-A TRANSOCKS -d 10.0.0.0/8 -j RETURN
-A TRANSOCKS -d 127.0.0.0/8 -j RETURN
-A TRANSOCKS -d 169.254.0.0/16 -j RETURN
-A TRANSOCKS -d 172.16.0.0/12 -j RETURN
-A TRANSOCKS -d 192.168.0.0/16 -j RETURN
-A TRANSOCKS -d 224.0.0.0/4 -j RETURN
-A TRANSOCKS -d 240.0.0.0/4 -j RETURN
-A TRANSOCKS -p tcp -j REDIRECT --to-ports 1081
COMMIT

DNAT / REDIRECTは NAT 技術に利用されるものですので、宛先アドレスが書き換えられた状態でプログラムに転送されてきます。透過プロキシをするには元々接続したかったインターネット上のサーバーのアドレスを知る必要がありますが、man には書かれていない getsockoptのオプションで、書き換えられる前の宛先アドレスを知る方法があります。

自作透過 SOCKS プロキシ transocks

iptablesで宛先を書き換えられる前のアドレスを知るには、以下のようにします:

struct sockaddr_storage addr;

// for IPv4
socklen_t len = sizeof(struct sockaddr_in);
int level = IPPROTO_IP;
int opt = SO_ORIGINAL_DST;

// for IPv6 (Linux 3.8 or better)// socklen_t len = sizeof(struct sockaddr_in6);// int level = IPPROTO_IPV6;// int opt = IP6T_SO_ORIGINAL_DST;if( getsockopt(sock, level, opt, &addr, &len) == -1 ) {
    // handle error
}

このテクニックを利用して透過プロキシを実現する既存のソフトウェアは幾つかあるのですが、SOCKS や IPv6 に対応していなかったり、高負荷時にクラッシュするといった現象があったため、改修するより自作したほうが早いと判断しました。

自作にあたっては Goを利用しました。高速なネットワークサーバーを自作するなら、Go は非常に良い選択肢です。実際 SOCKS5 サーバーへの接続は golang.org/x/net/proxyとして既成のものがあるため、必要なのは上記 getsockoptの部分くらいです。

成果物は github.com/cybozu-go/transocksで公開しています。SOCKS5 サーバー以外に、Squid のような HTTP プロキシの CONNECT メソッドを利用することもできるようになっています。全て含めて 2 日足らずで実装できました。

http_tunnel.goは x/net/proxy のプラグインとして利用できますので、部品としてもお使いいただけます。

自作 SOCKS サーバー usocksd

SOCKS サーバーも Go で自作しました。複数のグローバル IP アドレスを動的に使いわけるという要件が必要であったためです。こちらも既成の SOCKS5 サーバーライブラリを利用することで、1 日程度で実装できました。

利用した armon/go-socks5は良い設計ですが、プライベートネットワーク内の同一ホストに対して、同じグローバル IP アドレスを利用するといったことが実現できなかったため、contextに対応して実現できるよう改造しています。これをしないと、FTP サーバーへの接続などで障害が起きる場合があります。

成果物は github.com/cybozu-go/usocksdで公開しています。

稼動状況

usocksd を DMZ に、transocks を従来 NAT を必要としていたホストに設置する構成で既に cybozu.comにて稼動しています。

問題なく動いていますが、usocksd は少し GC の CPU 負荷が高いようだったので、GOGC=300としてチューニングしました。Go1.6 の GC 負荷については、https://github.com/golang/go/issues/14161#issuecomment-178357292を一読しておくと良いです。

まとめ

アーキテクチャ刷新プロジェクト「Neco」の成果の一つである、NAT の欠点を回避するための透過 SOCKS プロキシという技法と、その実装および稼動状況を紹介しました。

既存のソフトウェアはクラッシュしたり望ましい仕様ではなかったりしたため、透過プロキシおよび SOCKS サーバーは自作しました。Go を使えば望ましい仕様の SOCKS プロキシが楽々自作できることも紹介しました。Go はいいぞ。

サイボウズでは Go (や C/C++, Python, Java etc.)を駆使して問題を解決する技術者を募集しています。We are hiring!

「第5期サイボウズ・ラボユース成果報告会」開催

$
0
0

サイボウズ・ラボの光成です。 3月30日にサイボウズ・ラボユースの成果報告会がありましたのでその紹介をします。

サイボウズ・ラボユースとは

サイボウズ・ラボユースとは、日本の若手エンジニアの発掘と育成を目指す機会を提供する場として2011年に設立されました。

各自が選んだテーマで開発し、サイボウズ・ラボのメンバーがそのサポートを行います(メンター)。 開発物はオープンソースで公開することが条件で本人に帰属するのが特徴です。

f:id:cybozuinsideout:20160331165755j:plain

発表内容

ペアリングライブラリの実装とアプリケーション開発

最初の発表は緑川志穂さんによる「Pythonによるペアリングライブラリの実装とアプリケーション開発」でメンターは私です。

ペアリングとは、公開鍵暗号技術の応用の一つであるIDベース暗号を中心に近年広く使われる演算です。 私はペアリングの理解に必要な数学やC++の解説などを行いました。

16歳ながら一人で論文や書籍を読み、がんがん実装していくので非常に驚きました(成果物)。

これは余談ですが、拙著の『クラウドを支えるこれからの暗号技術』のレビューもしていただけました。 もともとそのテキストを書くとき念頭にあったのは「自分が高校生のときに読んだら面白いと思ってもらえそうなもの」だったので実際にそういう人が現れて感激です。まさか中学生(当時)が読むとは思いませんでしたが。

構文を自在に変更できるプログラミング言語

2番目の発表は赤間仁志さんによる「ユーザが構文を自在に変更できるプログラミング言語」でメンターは川合です。

コアはJSONライクな木構造による最小限の文法しか持たないのですが、自身の構文解析器を書き換えていくことで様々な文法に対応可能なプログラミング言語です(成果物 Garbanzo)。

第57回プログラミング・シンポジウムプログラムで発表されたときより完成度があがっていました。

実際に否定構文!を導入したり、その構文を削除するデモはわかりやすくてみなさんも興味深く聞いていたようです。

並列WALによるトランザクションの高速化

3番目の発表は神谷孝明さんによる「並列WALによるPostgreSQLのトランザクション高速化に向けた取り組み」でメンターは星野です。

WALとはWrite Ahead Loggingの略でデータベースを扱うマシンの電源がいつ落ちても書き込まれたデータを正しく復旧できる仕組みです。

従来HDDの特性に合わせて最適化されていたデータベースをSSDやioDriveなどの特性を考慮し並列性を高めたアルゴリズムを導入しようという試みです。

残念ながら開発を始めた時期が遅く、まだデバッグ中で動くところまではいっていません(成果物)。 しかし今後も大学で研究をしながら継続開発される予定とのことで成果の報告が楽しみです。

拡張可能なパケット解析ライブラリ

4番目の発表は城倉弘樹さんによる「拡張可能なパケット解析のライブラリの開発」でメンターは私です。

パケット解析ツールとしてはWiresharkがお手軽で便利なGUIツールとして有名です。 それに対して、新しい構造のパケットにできるだけ簡単に対応できるようなC++用のライブラリを作りたいというのが動機です。

プログラミングを初めてまだ1年だそうですが、資料を読んだりして熱心に取り組んでいます(LibPGEN)。 彼もそうですが、開発中は「昼御飯食べるのを忘れた」というのをちらほら聞きました。若い…。

デモでは少し前にちょっとしたブームになっていたズンドコプロトコル(ズンドコキヨシまとめ)の実装例を紹介されました。

OS, FPGAによるネットワークレイテンシの低減

最後はラボユース1期生だった粟本真一さんによる「OS, FPGAによるネットワークレイテンシの低減」です。

自作OSにBSD用のネットワークドライバの必要な部分を移植して低レイテンシなARPコマンドを作ったという話です。 今後はFPGAでも実装し、速度比較も行いたいとのことです。

ラボユース生だったときは、いきなりネットワークドライバを移植しようとして失敗したのだけど、経験を積んで何が必要か把握していたので今回はうまくいったとのことです。

着実に成長していてすばらしいですね。

まとめ

参加者の方からもいろいろな質問をいただきました。 また、懇親会では第6期に応募してみようかなという方やOBの方も何人か参加され、盛り上がりました。 どうもありがとうございました。

f:id:cybozuinsideout:20160331185639j:plain

正式な第6期サイボウズ・ラボユースの募集は4月に出す予定ですのでよろしくお願いします。

Shibuya.XSS #7 が開催されました

$
0
0

Cy-SIRT の伊藤です。 2016 年 3 月 28 日に Shibuya.XSS tech Talk #7がサイボウズで開催されました。

f:id:cybozuinsideout:20160405100943j:plain

今日は各セッション内容について簡単にご紹介するとともに、講演者の方の資料への URL を残します。

mala さん - 超絶技巧 CSRF

speakerdeck.com

2001 年当時に公開された Cross Protcol Scripting Attack と呼ばれる攻撃手法によるリスクが、現在は XHR Level2 でプレフライトのチェック無しに multipart-formdata を経由でバイナリデータを送れるようになったことで増加しているとことを紹介いただきました。

Masato Kinugawa さん - PATHでXSSする技術

speakerdeck.com

クエリ部と同様にパスでもユーザ入力を受け付けているアプリの場合、アプリケーションの取り扱い次第では XSS が起こりうることを紹介いただきました。ブラウザごとの挙動の差異を分かり易く解説いただいたので、折を見て参照したいです。

hasegawayosuke さん - Electron のはなし

http://utf-8.jp/public/2016/0328/shibuyaxss.pdf

file プロトコルにおける origin はディレクトリ関係なく全ファイルが同一オリジンであることを活用し、Electron アプリに XSS があった場合、webview タグを使わずに、ファイルサーバに置いた罠コンテンツを利用することで任意コード実行が可能であることを紹介いただきました。

やぎはしゅ さん - http://こいつの:話@shibuya.xss.moe/

www.slideshare.net

URI の中に Basic 認証の ID/PASS を記載することで入力ダイアログを表示せずに認証するといった際に使われる RFC 3986 で定義された仕様について、各ブラウザの現在の挙動と攻撃への悪用可能性について紹介いただきました。

llamakko さん - Firefox の話 - 脆弱性発見から報奨金受け取りまで

speakerdeck.com

Firefox の脆弱性を発見するご自身の経験を元に、脆弱性の探し方と心構えを紹介いただきました。診断対象の機能や不具合情報に対する引き出しが少ないと、探すことができる脆弱性が限られてしまうため、セキュリティアドバイザリなど、情報収集をしっかり行うのが重要と言う点に共感しました。

どのセッションも大変勉強になりました。講演者の皆様、貴重なプレゼンをありがとうございました。

今後もサイボウズでは、開発系のイベント開催のお手伝いをしていく予定です。開発系イベント主催者の皆様、ぜひ、お声がけください。

Windows マシンで開発を始めるこれからの君へ

$
0
0

春うらら。
進級進学就職により、あまたの新人さんが世に出る時期ですね。
こんにちは。松山開発部の矢野と申します。

この時期になると、自分が新人だった頃のことを思い出します。
あの頃はマウス派だった私もすっかりキーボード派になりました。
(ほんとはキーボード寄りの折衷派ですが)

さて、唐突ですが、これから Windows で開発を行うことになる新人エンジニアのみなさんに向けて、私が便利だなぁと思っている Tips を紹介したいと思います。

Windows ロゴ キー を使う

ショートカットキーってご存知ですか?
Ctrl+Z で元に戻す、Ctrl+Y でやり直し、とかは聞いた覚えもあるのではないでしょうか。

実は Windows ロゴ キーにもいろいろなショートカットキーが割り当てられています。
f:id:cybozuinsideout:20160407121336j:plain

たくさんあるショートカットキーの中でも特におすすめなものを紹介します。

「Windows ロゴ キー+R」

一押しのショートカットキーは「Windows ロゴ キー+R」です。
押下すると「ファイル名を指定して実行」ダイアログが表示されるので、プログラム名などを覚えてさえいればこれだけでだいたいのことが済ませられます。
f:id:cybozuinsideout:20160407131959p:plain

たとえば「mspaint」「notepad」「snippingtool」「control」「cmd」などを打ってみると楽しいかもしれません。

「Windows ロゴ キー+X」

「Windows ロゴ キー+X」もおすすめです。
クイックリンク(主要の設定系プログラムへのリンク)が表示されます。
管理者権限でコマンドプロンプトを起動したいときは、ここからやるのが早いと思います。
f:id:cybozuinsideout:20160407125358p:plain

「Windows ロゴ キー+T」

普段はタスクバーを隠す設定にしている方におすすめしたいのが「Windows ロゴ キー+T」です。
マウス動かすのがめんどくさいけどタスクバーを表示させたいときにはコレです。

「Windows ロゴ キー+L」

セキュリティーが気になるナウでヤングな方には「Windows ロゴ キー+L」で画面をロックした状態にできることを覚えて帰っていただきたいです。
休憩に入るときなどにはタターンと打ち込んで颯爽と離席しましょう。モテます。

その他

もっと知りたくなったらこちらをご参照ください。
http://windows.microsoft.com/ja-jp/windows/keyboard-shortcuts#keyboard-shortcuts=windows-8

ファイル名を指定して実行する

「ファイル名を指定して実行」を使うと素早くプログラムを起動することができます。
よく仕事で使うのはこのあたりでしょうか。

  • mstsc
  • devmgmt.msc
  • regedit
  • services.msc

私は目で探すのが苦手ですしデスクトップにアイコンをたくさん置くのが好きではないので、パスを通した適当な場所にショートカットファイルを集めて「ファイル名を指定して実行」で開くという操作をよくします。
f:id:cybozuinsideout:20160407131337p:plain

このようにお好みのプログラムのショートカットファイルを作っておくと、「Windows ロゴ キー+R」と組み合わせ、素早くプログラムを起動できるようになります。

よく使うプログラムはクラウドへ

よく使うプログラムはクラウド型ストレージサービスに入れておくと便利です。
同期用クライアントアプリがあるものが良いですね。(DropBox, OneDrive, GoogleDrive など)

設定ファイルもまとめてクラウドへ

会社と家とで作業時の勝手が違うのなんて嫌ですよね。
いつでもどこでも同じ設定で作業できたほうがメリットが多いです。
設定ファイルもクラウド型ストレージサービスに入れておきましょう。

個人的にはここら辺を入れています。

  • vimrc
  • IMEのユーザー辞書
  • git系の設定ファイル(gitconfig, gitignore, gittemplate...)

まとめ

  • ショートカットキーを利用して使い勝手をアップ
  • 「Windows+R」でプログラムを素早く起動する
  • よく使うツールや設定ファイルはクラウドサービスで管理

これらを実践していると嬉しいことが二つあります。

一つ目は Windows のバージョンアップなどの影響を受けにくいということ。
Windows のバージョンアップで設定項目の場所が変わったりすることはままあることですが、プログラムを直接指定して起動するくせがついていると影響を受けにくいです。

二つ目は 会社と家との環境差が少なくなって作業がしやすくなるということ。
弊社にはウルトラワークという単発的在宅勤務が認められる制度があります。例えば台風などで出社が困難なときに利用するのですが、いざ会社と違うマシンで作業を行おうと思っても使い勝手に差があると作業効率が悪くなってしまいます。
よく使うツールや設定ファイルをクラウドサービスで管理することで、使い勝手の差を小さくすることができます。


いかがでしたでしょうか。

すでにご存じのこともあるかとは思いますが、
少しでも新人エンジニアのみなさんのお役に立てれば幸いです。

pmjp.slack.comオフ会#3 を開催しました

$
0
0

新学期が始まりましたね。 こんにちは、子供は小3、ワーママプロダクトマネージャーの河合です。

4/7(木)にサイボウズ東京オフィスでpmjp.slack.comオフ会#3を 開催しました。 朝から大雨、春の嵐になるかも。という悪天候で欠席者多数になるのでは。。 と心配していましが夜には雨もすっかりあがり、春とは思えぬ熱気あふれる会になりました。

pmjp.slack.comオフ会とは

Web業界のプロダクトマネジメント・オーナーシップに興味を持つ人々が集まるコミュニティ、 Product Managers Japan(PMJP)のオフ会です。

普段はSlackのPMJPチームでコミュニケーションをしています。 興味がある方は是非ご参加を!

オフ会は今回が開催3回目、初のプレゼン&LTもありました。 各発表と、交流会の様子をご紹介します。

サイボウズでのプロダクトマネージャーについて

最初はサイボウズでプロダクトマネージャー(開発PM)をしている山田と齋藤から。

f:id:cybozuinsideout:20160408154639j:plain

f:id:cybozuinsideout:20160408155656j:plain

サイボウズではプロダクトマネージャーには開発PMと販売PM(BPM)がいます。 開発PMは製品における、販売PMはビジネスにおける責任者です。

事業成長にともなってどう組織変遷して、なぜプロダクトマネージャーを置いているのか、 なぜ今のような体制になったのか。という紹介をしました。

www.slideshare.net

www.slideshare.net

200人規模のベンチャーを2人のPMで回す方法

freeeの坂本さん。「会社設立フリー」を企画、リリースするまでにPMとしてどんなことをしてきたのか。 そして、社長からプロダクトライン全部見てねと任されたときに、どうスケールさせたか。 「プロジェクトマネジメント」を手から離して、「Why」(なぜそれをやるのか)を明確にし、 伝え共感してもらうことにフォーカスをした。というお話でした。

f:id:cybozuinsideout:20160408152800j:plain

「Why」に専念できるのは、エンジニアが優秀だから「How」(どう実現するか)を 任せられるから。すばらしいですね。

LT1:マーケターPMによるプロダクト開発の加速

LTトップはSmarby矢本さん。

f:id:cybozuinsideout:20160408163703j:plain

ビジネス側と開発側を両方見ているので、 俯瞰的に客観的に、優先度を明確に決められるので開発効率があがりました。 マーケターPMとして「客観性」を強みにして、開発スピードをアップを紹介。 でもPMとして最も重要な要素は「偏執」!

LT2:PMじゃなくてもプロダクトを育てたい!!

GMOペパボ高橋さん。

f:id:cybozuinsideout:20160408163706j:plain

プロダクト開発チームで実際にやってみたツール(手法)の紹介。 カスタマージャーニーマップ、プラグマティックペルソナ、KPIツリーを実際やったそうです。

最後に、カンバン仕事術(チームでの仕事の見える化がわかる)の紹介と 交流会でかんたんなワークショップ「コイン渡しゲーム」もできます。 とのことでした。興味津々です。

LT3:大きい組織におけるPMとその育成についてふわっと相談するLT

オプト平岩さんから(新しいジャンル!)相談LT。

www.slideshare.net

ビジネスPMと開発PMがいるものの、開発SV(でもやってることはPM?)もいて。。 赤裸々な社内での状況を共有してもらいつつ、 どう大きな組織の中でPM的な考え方を広めて、育成すればよいのかという問題提議でした。

LT4:PMの心得3条

当日に滑り込みでLTすることになったIncrements東峰さん。 及川さんから託されたPM心得3条。

f:id:cybozuinsideout:20160408163709j:plain

PM心得3条は「引く、聞く、割る」

  • 「引く」は 一歩引いて考えることが大切ということ。

  • 「聞く」 チームメンバーの話をよく聞き、発言を引き出そうということ。 いい意見だと採用できるし、意見が割れても聞くことからしっかり議論できるので、 チームで納得感が得られます。

  • 「割る」ざっくりした数字力が大事! 100人の話をしているのか、1万人の話をしているのか。

交流会

発表が終わると交流会。みなさん盛り上がってます。 f:id:cybozuinsideout:20160408163712j:plain

GMOペパボの高橋さんの話で気になっていた「コイン渡しゲーム」の様子 f:id:cybozuinsideout:20160408152341j:plain

20枚のコインを、ひっくり返してから隣に渡すというのを 20枚/5枚ずつ/1枚ずつの3パターンで4人1周まわすのを3セット。 それぞれのタイムを比較してみると・・ 結果にびっくり! 気になる方は試してみてください。

このあと私もばっちりゲームに参加させてもらいました。

〆の挨拶では次回の開催予定も発表され、さらに規模が大きなオフ会になりそうで 今からとても楽しみです。


Android N ふうにダイレクトリプライ機能を実装してみた

$
0
0

こんにちは、大阪開発部のブノアです。

この間、Android N Developer Previewの発表がありました。その中にメッセージ系の通知に直接、コンテキストを変えず、返信できる機能が含まれています。私はとても便利な機能だと感じました。しかしながら、Android Nがリリースされるまでに同じように便利な機能は使えないのか、Android Nに更新されていない端末にあのような便利な機能は提供できないのかと考えました。 同じタイミングで、グーグルのHangoutアプリInboxアプリの最新版にそういう機能が追加されていました!

私も気になって、「ダイレクトリプライ」機能を作ってみました!今回の実験台は弊社が提供しているサイボウズLive TIMELINEアプリにしました。

サイボウズLive TIMELINE(タイムライン)とは?

TIMELINEはサイボウズLiveのスマホ専用アプリです。簡単にまとめるとグループの機能で、グループに参加しているメンバーとのチャットができる場です。
今回は、そのチャットができる機能に対してダイレクトリプライを追加してみたという話になります。

まずは結果のビデオをみせます。とあるウェブサイトをみている最中に仲間からメッセージが届きます。

説明

  1. とあるウェブサイトを閲覧している
  2. 閲覧の最中にTIMELINEアプリの誰かからメッセージが来る
  3. 返信ボタンの利用でメッセージを書いて返信する
  4. このまま、ウェブサイト閲覧の継続ができる
  5. また、TIMELINEの人からメッセージが来る
  6. いいねボタンで賛成している事を送信する
  7. このまま、ウェブサイト閲覧の継続ができる

ダイレクトリプライ機能のメリット

普段は返信やいいね送信をするには、アプリの切り替えが必要です。よって、上記の説明が7つステップじゃなくて、11ステップになります。「ウェブサイト→TIMELINEにて返信→ウェブサイト→TIMELINEにていいね→ウェブサイト」のような流れになるからです。
アプリ切り替えずにアクションができる事は自分がやっている事の邪魔にならず、集中ができるし、アプリ切り替えに必要なメモリ、パフォーマンスも不要となり、端末の電池に優しい仕組みです。

作る方法

さて、どうやってダイレクトリプライを作ったか説明します。

要件

  • 通知に「返信」・「いいね」できるアクションボタンの追加
  • 「返信」を押すとコンテキスト切り替えなく、返信できる機能
  • 「いいね」を押すとこのまま、いいねが送信される機能

更に「最近のアプリ」に影響を与えないものであれば嬉しいです。

通知に「返信」・「いいね」できるアクションボタン

まずはアクションボタンの追加です。
アクションボタンは「文字列+アイコン」の組み合わせで作る事ができます。今回は、「返信」アクションと「いいね」アクションの2つを作りました。

f:id:cybozuinsideout:20160406135234p:plain

コードは既存の通知を生成する部分を編集して、既存の通知に2つのアクションを追加するだけになります。それから各アクションのIntentの定義もします。

// 既存の通知。これにアクションを追加していく。final NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(GcmIntentService.this);
// メイン通知の定義、省略/* ... */// 通知の管理のため、ユニークなIDを定義するint notificationId = /*...*/;

// 返信アクションを追加する
Intent replyIntent = new Intent(GcmIntentService.this, DirectReplyActivity.class);
replyIntent.putExtra(/* 必要な情報を */);
replyIntent.putExtra("NOTIFICATION_ID", notificationId);
replyIntent.setAction("TIMELINE_DIRECT_REPLY");
PendingIntent replyPendingIntent =
        PendingIntent.getActivity(GcmIntentService.this, 0, replyIntent, PendingIntent.FLAG_CANCEL_CURRENT);
// 通知ビルダーにアクションを追加する
mBuilder.addAction(R.drawable.icon_reply, "返信する", replyPendingIntent);

Intent likeIntent = new Intent(GcmIntentService.this, DirectReplyActivity.class);
likeIntent.putExtra(/* 必要な情報を */);
// 返信アクションと区別できるため。然るべきやり方は他にあるが。※記事の後に話します
likeIntent.putExtra("LIKE_ACTION", true);
likeIntent.putExtra("NOTIFICATION_ID", notificationId);
likeIntent.setAction("TIMELINE_LIKE");
PendingIntent likePendingIntent =
        PendingIntent.getActivity(GcmIntentService.this, 0, likeIntent, PendingIntent.FLAG_CANCEL_CURRENT);
// 通知ビルダーにアクションを追加する
mBuilder.addAction(R.drawable.icon_like, "いいね", likePendingIntent);

// 今まで通り、通知を発行する
NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
mNotificationManager.notify(notificationId, mBuilder.build());

ご覧の通り、2つとものアクションは DirectReplyActivityアクティビティを起動します。

「いいね」アクションIntentの然るべき形について

今回の「いいね」アクションは何も表示せずバックグラウンドでデータをサーバーに送信するだけなのでアクティビティ経由でデータを送信する事は非効率だと思います。正しいやり方は「いいね」アクションが実行されたら、ブロードキャストして専用のレシーバーにて処理をするのが然るべき形だと思います。

「返信」を押すと返信できる機能

今回は半透明のアクティビティを作りました。半透明だと、半透明なアクティビティを開く前に、開いていたアクティビティはまだ見えているため、アクティビティの onStop()が実行されず、ただの onPause()になります。参考:アクティビティのライフサイクル

半透明バックグラウンドは単色でも大丈夫ですが少しでもきれいにするため、色の線形グラデーションのバックグラウンドを設定しました。

<?xml version="1.0" encoding="utf-8"?><shape xmlns:android="http://schemas.android.com/apk/res/android"android:shape="rectangle"><gradientandroid:angle="90"android:startColor="#0052A2D4"android:endColor="#FF52A2D4"android:type="linear"/></shape>

Androidが提供している半透明スタイルを拡張して、上記に作ったバックグラウンドを使います。

<style name="Theme.Timeline.Transparent"parent="android:Theme.Translucent.NoTitleBar"><item name="android:windowBackground">@drawable/direct_reply_background</item></style>

最後に AndroidManifestにてダイレクトリプライアクティビティに使います。

<activityandroid:name=".ui.view.activity.DirectReplyActivity"android:excludeFromRecents="true"← 「最近のアプリ」に影響を与えないためandroid:launchMode="singleTask"android:screenOrientation="portrait"android:theme="@style/Theme.Timeline.Transparent"← 作ったテーマを定義するandroid:windowSoftInputMode="stateAlwaysVisible"← 返信用の入力項目が表示されたらキーボードが出したいため />

アクティビティのレイアウト

ゴールは下記のスクリーンショットの通りです。

f:id:cybozuinsideout:20160406140026p:plain

説明

  • 上左の矢印:アクティビティを閉じるためのバックボタン
  • 上右のアイコン:やっぱり長いメッセージや添付付けたい場合、TIMELINE自体で返信できるようの移動ボタン
  • 入力項目:返信メッセージかける入力項目
  • 入力項目の右アイコン:送信するボタン

従ってレイアウトは下記の通りに書きました。※ 長くなるので、細かいデザインの記述は省略しています。

<?xml version="1.0" encoding="utf-8"?><LinearLayoutandroid:id="@+id/direct_reply_layout"><LinearLayoutandroid:layout_width="fill_parent"android:layout_height="wrap_content"android:orientation="horizontal"><!-- アクティビティを閉じる用のボタン --><ImageViewandroid:id="@+id/direct_reply_go_back" /><!-- アクティビティのタイトル --><TextViewandroid:id="@+id/direct_reply_title_text"android:text="[Group]に返信しておく" /><!-- やっぱりTIMELINEで返信したい場合、            TIMELINEアプリを開くボタン --><ImageViewandroid:id="@+id/direct_reply_open_up" /></LinearLayout><LinearLayoutandroid:orientation="horizontal"><!-- 返信の入力項目 --><EditTextandroid:id="@+id/direct_reply_message"android:hint="返信を送信" /><!-- 送信ボタン --><ImageViewandroid:id="@+id/direct_reply_send_message" /></LinearLayout></LinearLayout>

アクティビティの中身

やりたい事は

  1. Intentの確認と通知の既読化
  2. 「いいね」アクションの場合、いいねを送信し、アクティビティを停止する
  3. 「返信」アクションの場合、レイアウトの定義、クリックイベント処理設定

1. Intentの確認

Androidでは通知のアクションボタンをクリックしても通知自体は既読になりません。そのため、手動で既読化する事が必要で、既読にするため、通知のアクションのIntent作成時に notificationIdを設定しています。

protectedvoid onCreate(Bundle savedInstanceState) {
    /* ... */

    Intent intent = getIntent();
    // 通知IDを取得し、デフォルト値は-1に設定int notificationId = intent.getIntExtra("NOTIFICATION_ID", -1);
    // 通知を既読する
    NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
    manager.cancel(notificationId);

    /* ... */
}

それから必要な情報を取得する。TIMELINEの場合は、どのグループからメッセージが来たのかを保持しています。

2. 「いいね」アクションの場合

「いいね」アクションの場合は、このまま対象のグループにいいねを送信し、アクティビティを閉じます。

protectedvoid onCreate(Bundle savedInstanceState) {
    /* ... */// いいねアクションかどうかを確認する。デフォルト値でそうでないboolean isLikeAction = intent.getBooleanExtra("LIKE_ACTION", false);
    if (isLikeAction) {
        // サーバーにいいねアクションを送信する
        sendLikeAction();
        // サーバーに最新の既読のメッセージを設定する
        updateReadMessages();
        // アクティビティを閉じるthis.finish();
    }

    /* ... */
}

3. 「返信」アクションの場合

「返信」アクションの場合は、レイアウトを定義し、表示します。グループ名のよって、タイトルも設定します。

publicclass DirectReplyActivity extends Activity implements View.OnClickListener {
    @Overrideprotectedvoid onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        Intent intent = getIntent();
        // グループ情報を取得
        group = (Group) intent.getSerializableExtra(BundleKey.GROUP);
        // レイアウトを定義
        setContentView(R.layout.direct_reply);

        // グループ名をタイトルに設定
        TextView viewTitle = (TextView) findViewById(R.id.direct_reply_title_text);
        viewTitle.setText(getString(R.string.direct_reply_title, group.getName()));

        // すべてのボタンのクリックイベント設定
        ImageView openUpButton = (ImageView) findViewById(R.id.direct_reply_open_up);
        openUpButton.setOnClickListener(this);
        ImageView goBackButton = (ImageView) findViewById(R.id.direct_reply_go_back);
        goBackButton.setOnClickListener(this);
        sendMessageButton = (ImageView) findViewById(R.id.direct_reply_send_message);
        sendMessageButton.setOnClickListener(this);
        // デファクトで入力項目が空なので送信ボタンは無効とする
        sendMessageButton.setClickable(false);
    }

    @Overridepublicvoid onClick(View view) {
        int id = view.getId();
        switch (id) {
            case R.id.direct_reply_open_up:
                // TIMELINE自体で書きたい時ようのアクション// 入力中のものも再利用のため、Intentに設定する
                Intent intent = new Intent(getApplicationContext(), TimelineActivity.class);
                intent.putExtra("GROUP_ID", groupId);
                intent.putExtra("TEXT_CONTENT", messageText.getText().toString());
                // ダイレクトリプライがTIMELINEのヒストリースタックに残らないようにする
                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
                startActivity(intent);
                break;
            case R.id.direct_reply_send_message:
                // メッセージを送信する
                sendMessage();
                // サーバーに最新の既読のメッセージを設定する
                updateReadMessages();
                break;
            case R.id.direct_reply_go_back:
                // 何もせずアプリを閉じるthis.finish();
                break;
        }
    }
}

判明した事

実装を始める前に本当に通常の Activityで行けるのかと少し疑問はあったのですが、大丈夫でした。また、ダイレクトリプライが適応されたアプリケーションのバックスタックにも、最近のアプリにも影響を与えないようにできたのでよかったと思います。
Android N ではない端末でもダイレクトリプライ機能の提供ができるように、Android が最新でない状態でもまだまだ考えられていない機能がいっぱいあるのではないかと思いました!

最後に

説明は以上となります。意外と便利な機能を低コストで作れました!
便利な機能を素早く提供できるよう新しい技術に引き続きチャレンジしていきます!

以上、ブノアでした!

Javaの謎のパフォーマンス劣化現象との戦い

$
0
0

こんにちは。アプリケーション基盤チームの横田です。 Javaの謎のパフォーマンス劣化にまつわる調査をしていたのですが、1ヶ月の苦労の末に原因がわかりましたので、報告させていただきます!

忙しい人のためのまとめ

  • jdk-7u40以降のjdk-7 *1でJavaのパフォーマンスが劣化する謎の現象
  • CodeCacheの容量限界に近づくとJITコンパイラを停止してコンパイルしたコードを捨てる機能が原因だった
  • 起動オプションで回避できるので、長期運用するときは -XX:-UseCodeCacheFlushing, -XX:ReservedCodeCacheSize=128mをつける
  • 上のオプションを設定した時に、CodeCacheの量が適切でない場合はログが出力されるので ReservedCodeCacheSizeを適宜修正する

謎のパフォーマンス劣化

弊社でおきていた謎のパフォーマンス劣化の特徴は次のようなものでした

  • Javaの起動直後は高速に動作する
  • 起動から数日経つとパフォーマンス劣化が顕著に発生する
  • IOボトルネックではない
  • パフォーマンス劣化が起き始めるとCPU利用率が2倍程度に増加する
  • CPU利用率は高まるが弊社環境ではCPUリソースには余裕があった
  • 再起動すると現象は回復する

GC、メモリリーク、メモリの断片化によるパフォーマンス劣化などを疑ってみたのですが、どれも決定的な証拠に乏しく「原因はこれだ」と判断できずでした。

次のリンクは、JITコンパイラがCodeCacheの量が足りなくなった時に、プログラムのスループットが悪化する話。

GitHub - martint/jittest: Demonstrate JIT compiler issue in java 7

結果的にはこれが原因でした。

パフォーマンス悪化の原因はjdk7u4以降で導入されたCodeCacheFlusingという機能。以下、引用します

Why do I get message "CodeCache is full. Compiler has been disabled"? (Poonam Bajaj)

UseCodeCacheFlushing is set to false by default in JDK6, and is enabled by default since JDK7u4. This essentially means that in jdk6 when the CodeCache becomes full, it is not swept and flushed and further compilations are disabled, and in jdk7u+, an emergency flushing is invoked when the CodeCache becomes full. Enabling this option by default made some issues related to the CodeCache flushing visible in jdk7u4+ releases. The following are two known problems in jdk7u4+ with respect to the CodeCache flushing:

  1. The compiler may not get restarted even after the CodeCache occupancy drops down to almost half after the emergency flushing.
  2. The emergency flushing may cause high CPU usage by the compiler threads leading to overall performance degradation.

怪しい!怪しすぎるよ!この機能!

次のような挙動をします

  1. ReservedCodeCacheSizeが CodeCacheFlushingMinimumFreeSpace以下になるとJITコンパイラを停止
  2. 不要と判断されたコンパイル済みコードを捨てる。
  3. CodeCacheMinimumFreeSpace以下に CodeCacheが減るとJITコンパイラは活動を再開

弊社で開発しているJavaプログラムは起動直後にCodeCacheが30MB程度に到達します。 そのため一度CodeCacheFlushingが発動してしまうと、JITコンパイラは復活してくれません。

実験

その1 jstatでJITコンパイラの動きを確かめてみる

jstat -compiler JavaはJITコンパイラの動きを観察することができます。

運用環境で稼働中のJavaプロセスに対して実行してみます。JITコンパイラが動いていないことが確認できました。

$ jstat -compiler ${JAVA_PID}
Compiled Failed Invalid   Time   FailedType FailedMethod
   14828      0       0   238.48          0             
   14828      0       0   238.48          0 
   ...           
   14828      0       0   238.48          0   

全部コンパイルされてるんとちゃうんか? Javaパフォーマンスには、JITコンパイラが停止するとログ出力すると書いてあります。 確かにログ出力されていませんでした。

ログ出力されない原因は、次の実験で判明するので、次の実験に進みます。 次は、CodeCacheSizeが適切なのか検証します。

その2 UseCodeCacheFlushingをOffにする

jdk7u40のデフォルトの設定の場合、次のようなことがわかりました。

  • CodeCacheFlushingが動作するとJITコンパイラは停止する。そして、コンパイルしたコードを削除する
  • JITコンパイラが停止しても、ログを出力してくれない。

ReservedCodeCacheSize, CodeCacheFlushingMinimumFreeSpace, CodeCacheMinimumFreeSpaceなどのオプションを変更して前述のJITコンパイラの動作確認用のレポジトリで実験するとよいです

GitHub - martint/jittest: Demonstrate JIT compiler issue in java 7

Java7でCodeCacheが足りない時にログを出すためには、-XX:-UseCodeCacheFlushingを設定するしかありません。 社内でこのオプションを設定して2日ほど経過して、CodeCacheが満杯になってJITコンパイラが停止していることがわかりました。

ログは、次のようになります

OpenJDK 64-Bit Server VM warning: CodeCache is full. Compiler has been disabled.
OpenJDK 64-Bit Server VM warning: Try increasing the code cache size using -XX:ReservedCodeCacheSize=
Code Cache  [0x00007fc715000000, 0x00007fc717fa0000, 0x00007fc718000000)

解決策

長期運用されるJavaプロセスには次のオプションを設定して問題が解決できました。

  • -XX:-UseCodeCacheFlushingでCodeCacheが足りなくなったら、JITコンパイラを止めるようにする。こうするとJITコンパイラ停止時にログが出力されます
  • -XX:ReservedCodeCacheSize=128mでCodeCacheSizeを2倍程度に増やす

リクエスト処理にかかる平均時間が少なくなりました f:id:cybozuinsideout:20160411101010p:plain

CPU利用率も1/2にほどに減少したことが確認できました f:id:cybozuinsideout:20160411101004p:plain

プログラムも高速化されるし、CPUにも優しい素晴らしいオプションですね!

Tips

CodeCacheの現在容量を確認する

現在利用中のCodeCacheSizeは jstatなどで、監視することはできません。 JMXを活用したプロダクトを使うとCodeCacheの現在容量を見ることができます。次のようなプロダクトです。

Java8はどうなるの?

Java8の場合はデフォルトで次のオプション設定になるようです

  • ReservedCodeCacheSize=256m
  • UseCodeCacheFlushingはデフォルトOFF

Java7の時だけの設定で十分のようです!Java8の時には再度確認おねがいします!

おわりに

最後に調査の参考にさせていただいたサイト&書籍です。

色々苦労しましたがJVMの中を読んだり、結構いい経験になりました。 調査は社内の特別環境で、調査・検証を行いました。お客様の環境では、定期的に再起動して現象の影響が出ないようにしています。

*1:Java7のパブリックアップデートを終了していますが、運用環境ではUbuntuがセキュリティパッチを適用したJava7を利用しています

2015 年 脆弱性報奨金制度を振り返って

$
0
0

Cy-SIRT の伊藤です。

2015 年にサイボウズにご報告いただいた脆弱性情報について、全ての評価が完了しました。 お時間をいただき申し訳ありませんでした。今日は、2015 年の最終結果についてご報告いたします。

定量情報

f:id:cybozuinsideout:20160415173959j:plain※ 最終的に認定されなかった件数を含む場合:174 件

f:id:cybozuinsideout:20160415174007j:plain※ 最終的に認定されなかった件数を含む場合:126 件

数値の見方

サイボウズでは脆弱性情報を、以下のフローで取り扱っています。
f:id:cybozuinsideout:20160418172348j:plain

外部からお寄せいただいた脆弱性情報は、受付 >検証 のプロセスを経て、脆弱性の認定手続きに進みます。受付および、検証のプロセスではお寄せいただいた情報が、脆弱性情報であるかどうかを判断しています。 「脆弱性報告件数」は、サイボウズが脆弱性情報と判断したご連絡の件数です。
「脆弱性認定件数」は、お寄せいただいた脆弱性情報の中から、脆弱性として認定した件数です。脆弱性情報がサイボウズに着信した「着信日」を基準とし、各年の報奨金制度の開催期間ごとに集計しています。最終的に脆弱性としては認定されなかった脆弱性情報であっても、報奨金の支払い対象となることがあるため、参考情報として記載しています。

2015 年報奨金獲得額ランキング

f:id:cybozuinsideout:20160415171557j:plain

2015 年に報告いただいた脆弱性情報によって、獲得した報奨金の金額が最も多かったのは、 小勝 純 様でした。おめでとうございます!

2015 年のトレンド

f:id:cybozuinsideout:20160415172459j:plain

2015 年は昨年と比較すると、深刻な脆弱性情報の件数は減少しました。代わりにサイボウズがこれまで脆弱性として認定して来なかった脆弱性について報告いただくことが増え、認定可否を議論することが増えた印象があります。

ここでは例として「CSV Excel Macro Injection (CEMI)」と呼ばれる脆弱性についてご紹介します。

CSVマクロインジェクション

CSV ExcelマクロインジェクションはOWASPのドキュメントには下記のように記載されています。 https://www.owasp.org/index.php/CSV_Excel_Macro_Injection

多くのWebアプリケーションは、ユーザーに対してユーザー設定などのテンプレートをダウンロードすることを許可している。 
また多くのユーザーはExcel(もしくはLibre OfficeやOpen Office)でCSVファイルを開くことを選択する。
WebアプリケーションがCSVファイルの中身を正しく検証していない場合、セルに入力された内容がマクロとして実行される可能性がある。

CSV Excelマクロインジェクション攻撃は、ユーザーの信頼を悪用した攻撃手法である:
・ここでいう「ユーザーの信頼」とは
1.ユーザーはコンテンツの置いてあるサイトを信頼している
2.ユーザーは(ダウンロードしたファイルが)単なるCSVファイルであること、ファイルに関数やマクロが含まれていないと考える
(よって、ファイル内の潜在的な悪意ある機能についてExcelからの任意の警告を気にとめない)

※ 伊藤による意訳となります。

サイボウズでは他社様の事例として、改修されているケースがあることを確認しています。

90131 CSV Excel Macro Injection Vulnerability in export customer tickets

Zendesk rewarded psychomantis with a $100 bounty for CSV Excel Macro Injection Vulnerability in export customer tickets.
Zendesk resolved CSV Excel Macro Injection Vulnerability in export customer tickets that was submitted by psychomantis.
https://hackerone.com/reports/90131

サイボウズではこの脆弱性について認定するかどうかを検討しました。

この脆弱性が顕在化しないようにするには、製品から出力される CSV データを改変する必要があります。具体的にはセル内のデータが =, +, - から始まる場合に、セルの先頭にシングルクォートを入れることになるのですが、そうした場合、CSV の先頭をそれらの文字から始めることができません。この対策方法は、特に "-" (マイナス)については頻出するため、許容することはできないと判断しました。本件についてマイクロソフト様の Excel の仕様についても伺いましたが、以下のような回答をいただいています。

ご報告いただきました動作は、ユーザーがマクロを有効にする必要があることから、本件は脆弱性ではないと判断しております。
弊社が考えております脆弱性の説明およびセキュリティに関する鉄則は、以下も併せてご参照ください。

「セキュリティの脆弱性」の定義
http://technet.microsoft.com/ja-jp/library/gg983510.aspx

セキュリティに関する 10 の鉄則
https://technet.microsoft.com/ja-jp/library/gg983506.aspx#E1

これらを考慮し、最終的には、CSV Excel Macro Injectionの問題を弊社としては脆弱性としては扱わないことといたしました。

脆弱性評価におけるサイボウズの取り組み

サイボウズではサイボウズ製品を通しての統一感を作ること、車輪の再発明を行わないことを目的として、2008 年から「共通仕様」と呼ばれる全製品が守ることを期待される仕様を策定しています。セキュリティに関する仕様も、この枠組みの中で複数の仕様を策定しています。具体的には、各種セキュリティヘッダーの出力方法やファイルダウンロードAPIの仕様などが上げられます。
共通仕様を定めることで、同種の脆弱性の再発を防止でき、脆弱性の認定にかかる時間を短縮する効果もあります。

共通仕様を定めるのは、「Tech Lead Meeting(TLM)」と呼ばれる会議体です。開発本部副本部長、各チームの開発責任者、Cy-SIRT 担当者、その他アドバイザーとして数名が参加しており、オンライン上で随時議論しつつ、月に1度オフラインでミーティングを開催しています。脆弱性認定についても新しい攻撃方法については、この会議体で議論を行い、必要に応じて「共通仕様」として各製品で順守すべき仕様として定義をしています。CEMI についても、Cy-SIRT と TLM が議論をする中で上記の見解を出すに至りました。

終わりに

サイボウズでは 2016 年も継続して脆弱性報奨金制度を運営しています。今年も複数の脆弱性報告をいただいております。

サイボウズ脆弱性報奨金制度
http://cybozu.co.jp/company/security/bug-bounty/

ぜひ、多くの方のご参加をお待ちしております。

株式会社一休と合同勉強会を開催しました

$
0
0

こんにちは。生産性向上チームの宮田(@miyajan)です。

4/20(水)にサイボウズ東京オフィスで株式会社一休のエンジニアたちと合同勉強会&交流会を開催しました。今回はこの会の様子をご紹介します。

経緯

株式会社一休の田中さん(@kentana20)がサイボウズのオフィス見学に来られたときに、『なにか合同で勉強会とかやれるといいですねー』という話をしたのがきっかけでした。その後、外の勉強会で赤坂さん(@chakasaka9)と会ったときも同じような話題が出て、あらためて企画して今回開催という流れになりました。

f:id:cybozuinsideout:20160421180021j:plain
会場の様子

発表一覧

まずは、それぞれの会社から2本ずつ発表が行われました。自動テストやCI、開発/リリースプロセスといったテーマが中心です。その後の懇親会の話のタネとなるので、皆さん全力です。

一休.comのE2Eテスト事情 〜ギリギリ話せるところまで話します〜

一番手は、株式会社一休の赤坂さん(@chakasaka9)です。宿泊サイトのE2Eテストについてお話いただきました。

f:id:cybozuinsideout:20160421182109j:plain
トップバッターに見事に場を暖めていただきました


Selenium WebDriverを使ったE2Eテストの自動化について、工夫している点や実際のディレクトリ構成、改善したいところと実際に運用している人ならではの発表内容でした。発表中に小ネタをはさんで場を盛り上げようとしてくれたのもよかったです。

発表後の質疑応答では、テストケースで使う初期データをどう保存するかという点について議論がされました。扱いやすさや仕様変更時の手間を考えるとどうしても難しくなってしまいがちな点で、サイボウズではdumpしたデータをファイルで管理するのが厳しかったのでAPIを利用している話など、知見の交換が続きました。

一休.comにおけるデプロイフローと自動化

二番手は、株式会社一休の田中さん(@kentana20)。一休.comにおけるデプロイフローや自動化について発表していただきました。

f:id:cybozuinsideout:20160421182338j:plain
昔の話をして『老害かw』とツッコまれる田中さん


一休.comの紹介や採用している技術の紹介、過去のイケてないリリースプロセスからの改善、ChatOpsなどの自動化事例など興味深い内容が多かったです。発表の途中でも質問やツッコミが入り、この日一番盛り上がった発表だったのではないかと思います。

発表後の質疑応答では、過去のイケてない状態から現在のイケてる状態になるまでになにがあったかについて話題が広がりました。田中さんも他のメンバーもタイミングがよかったということを言っていましたが、チャンスを逃さない行動力が重要だったのではないかなと話を聞いてて思いました。

kintone開発プロセス

後半は、サイボウズのターンです。kintoneチームリーダーの長谷川からkintoneチームの開発プロセスについて発表してもらいました。

f:id:cybozuinsideout:20160421182523j:plain
「サラッと話します」と言ってた長谷川さんでしたが、熱弁して時間オーバーします


kintoneの製品紹介から始まり、開発プロセス、役割分担、自動テスト、開発文化と広い内容を語ってくれました。リリース期間の違いや社内ハッカソンのような風土が特に反応があったように思います。

発表後は、サイボウズのPMのバックグラウンドや各職務の人数比の違いなどについて質問がありました。他の会社と話すことによって、自分たちの開発の特徴などをあらためて認識することができてよかったです。

Jenkins 再入門

最後に、私からJenkinsについて発表しました。余談ですが、この発表が始まる前に乾杯して他の人はピザを食べ始めました。

f:id:cybozuinsideout:20160421182717j:plain
はーい、みなさんピザに夢中になってないで発表聞いてくださーい


Jenkinsをカオスにしないための運用の知見と、Jenkins 2.0についての説明をしました。Jenkinsの運用については田中さんの発表でも問題点として挙がっていたので、あるある話なのではないかと思います。

ちなみに、この会の翌日に2.0がリリースされました。公式がDockerイメージを提供しているので、興味がある人はぜひ試してみてください。

$ docker pull jenkinsci/jenkins:2.0
$ docker run -p 8080:8080 -p 50000:50000 jenkinsci/jenkins:2.0

懇親会

発表後は、そのままビール片手にピザを囲んで懇親会となりました。発表の中で気になった部分について、深いところまで踏み込んだ会話が多かったです。

f:id:cybozuinsideout:20160421182915j:plain
激論中


そして、一休のエンジニアの皆さんを連れてサイボウズのオフィスツアーを行いました。想像以上に一休の皆さんに喜んでいただけたので、本当にやってよかったなと思いました。おそらく、会社移転してからこれまでで一番褒められたのではないかと思います。

f:id:cybozuinsideout:20160421183056j:plain
ハンモックにはしゃぐ一休エンジニアたち


最後に、エントランスで集合写真を撮影して解散となりました。皆さんいい笑顔ですね。

f:id:cybozuinsideout:20160421183212j:plain
全員いい笑顔!


おわりに

他社とのクローズドな交流会をやってみて、次のような点がよかったなと感じました。

  • オープンな勉強会ほど会場準備とかをがんばらなくていい
  • カジュアル感があるので発表中でも気軽に質問やツッコミを入れられる
  • 時間に融通が利くので質疑応答でワイワイ盛り上がれる
  • 「自社ではこうしていて〜」みたいな踏み込んだ話がしやすい
  • 人数が多すぎないのでオフィスツアーとかもその場で企画できる
  • 特定の会社と仲良くなれると今後の企画で声をかけやすい

一方で、発表内容はオープンにしても問題ない内容だったのでクローズドでやるにはもったいない部分もありました。そのあたりは、この開催報告などで補っていければと思います。

サイボウズと合同勉強会を開きたい企業の方、いらっしゃいましたらお気軽にお声がけください!

最後になりますが、遊びに来てくださった株式会社一休のみなさまありがとうございました!今後は、宿泊やレストランの予約は一休.comを使います!またやりましょう!

nginx ちょっと不思議だったリクエストリトライのお話

$
0
0

こんにちは、Sustain チームの山口です。

サイボウズリモートサービスというVPN・中継サービスで使用する L7LB を nginx に移行しようといろいろ調査をしています。

nginx をリバースプロキシとして使用する際に少々障害となる動作があり、それに関するアップデートが最近あったので、今回はそのお話です。

3行で分かる本記事の内容

  • nginx をリバースプロキシとして使う場合、リクエストがバックエンドサーバに二重で飛ぶ可能性がある
    • 対策として proxy_request_buffering off が使えそうだが、使えない
  • nginx 1.9.13 で仕様変更が入り、POST, LOCK, PATCH メソッドのリクエストは二重で飛ばないことが保証された

nginx の受動的な health check とリクエストのリトライ動作

nginx はリバースプロキシとして使う場合に、バックエンドサーバの生存確認手段として基本的に受動的な health checkを採用しています。

nginx はバックエンドサーバの生存確認を自分からは行いません。クライアントから来たリクエストを設定されたバックエンドサーバに送ってレスポンスを待ちます。
レスポンスが返ってこなかった場合、送った先のバックエンドサーバが利用できない状態であると見なして、一定期間バランシング対象から外します。
その場合、リクエスト自体は別のバックエンドサーバに送りなおすことでレスポンスが得られるようにしています。この送りなおす動作をリトライと呼んでいます。

例えば upstream に以下のような設定をした場合、

upstream backend_server {
    server backend_a;
    server backend_b;
}
  1. リクエストを nginx が受け付ける
  2. nginx が backend_a(もしくは backend_b)にリクエストを送信する
  3. error/timeout などで backend_a からレスポンスが得られない場合は backend_b へリクエストをリトライする
    • backend_a はバランシング対象から外れている
  4. backend_b から返ってきたレスポンスをクライアントに返す
    • もしすべてのバックエンドサーバがだめだった場合は、502/504 を返す

という動作をします。

リトライの問題点 : リクエストが二重に送られてしまう可能性

ただしこの場合、

  • バックエンドサーバでリクエストの処理に時間がかかった
    • proxy_read_timeout など
  • 処理は終わっていたのに nginx にレスポンスが返せなかった
    • レスポンス受信中のエラーなど

などでも nginx がリトライしてしまい、同じリクエストが複数回バックエンドサーバに届く可能性があります。

proxy_next_upstream

error
an error occurred while establishing a connection with the server, passing a request to it, or reading the response header;

timeout
a timeout has occurred while establishing a connection with the server, passing a request to it, or reading the response header;

*1

nginx では connection 時の error/timeout、リクエスト送信時の error/timeout、レスポンス読み込み時の error/timeout を区別する方法がないため、上記のような問題が起こります。

ずいぶん前から指摘されている問題なのですが、なかなか改善されませんでした。

二重リクエストは proxy_request_buffering off では防げない

1.9.12 以前の場合、これを防ぐために何か良い設定はないかとドキュメントを探すと目につくのは以下の設定ではないかと思います。

proxy_request_buffering

When buffering is disabled, the request body is sent to the proxied server immediately as it is received. In this case, the request cannot be passed to the next server if nginx already started sending the request body.

リクエストボディの送信が始まった後ならリクエストはリトライできない!?
GET はムリでもリクエストボディがあるリクエストはリトライしないように出来ると!?
冪等でない POST リクエストが二重リクエストにならないのを保証できるのなら、とてもありがたい!

…と一見よさそうに見えますが、結論から言うと残念ながらこの設定では二重リクエストを完全に防ぐことはできません。 確かにリクエストがバッファリングされない場合はリトライしないというのはドキュメント通りなのですが、強制的にバッファリングしてしまう場合が存在するためです。

例えばリクエストボディを preread で読み切れるような小さなリクエストボディのリクエストの場合は、request buffering が強制的に on になります。 (=proxy_request_buffering offが無効になる)

該当箇所はここです。

if (rb->rest == 0) {
        /* the whole request body was pre-read */
      ...
        r->request_body_no_buffering = 0;

        post_handler(r);

        return NGX_OK;
    }

他にも proxy_request_buffering が強制的に on になる条件がいくつかあります。

  • chunked-transfer-encoding なリクエストに対して、バックエンドサーバへの通信で HTTP/1.1 ではない場合
  • HTTP/2 が有効な場合
    • nginx 1.9.14 で HTTP/2 でも proxy_request_buffering off が有効化するようになりました
  • Content-Length ヘッダがなく、かつ chunked-transfer-encoding でもない場合
  • proxy_set_body もしくは proxy_pass_request_body が off の場合
    • そもそもこれらを有効にした場合に request_buffering を off にする必要あるのかという話ですが。

などです。(多分他にもまだあります)

proxy_request_buffering が on になった場合は従来通りの動作となり、結果としてリクエスト送信前/後のどちらでもリトライによる二重リクエストが発生してしまいます。

proxy_request_buffering を二重リクエスト阻止のために設定するのには例外が多く、やめておいた方がよさそうです。

他にも upstream_connect_time を Lua で比較する、proxy_connect_timeout を調整するなども検討しましたが、結局のところ nginx 1.9.12 以下でリトライ動作を完全に制限することはできませんという結論に至りました。

1.9.13 でのリトライの仕様変更

1.9.13 (もう mainline-1.9.15 まで出てますし、stable-1.10.0 になりましたが) では UDP のサポートが大きな話題として挙げられますが、同時にこのバージョンで上で書いたリクエストリトライの動作に仕様変更が入りました。

nginx change log

*) Change: non-idempotent requests (POST, LOCK, PATCH) are no longer passed to the next server by default if a request has been sent to a backend; the "non_idempotent" parameter of the "proxy_next_upstream" directive explicitly allows retrying such requests.

冪等ではないメソッド(POST, LOCK, PATCH)のリクエストは一度バックエンドサーバに送ったら、request buffering の有無にかかわらずリトライしないようになりました。

http://hg.nginx.org/nginx/rev/91c8d990fb45

+    if (u->request_sent+        && (r->method & (NGX_HTTP_POST|NGX_HTTP_LOCK|NGX_HTTP_PATCH)))

リクエスト送信後のフラグと POST, LOCK, PATCH での条件文が組まれていて、リトライしないようになっています。
ここの時点で request_sent のフラグが立っていれば、POST, LOCK, PATCH のリクエストはリトライしないため、requet 送信後とそれ以外で動作を分けることが可能になります。(正確にはリクエストボディの送信前後)

  1. リクエストを nginx が受け付ける
  2. nginx が backend_a (もしくは backend_b)にリクエストを送信する
  3. error/timeout などで backend_a からレスポンスが完了しない場合
    • request 送信済なら
      • POST, LOCK, PATCH は 502/504 を返す
      • それ以外は backend_b にリトライする
    • request 送信前なら
      • backend_b にリトライし、返ってきたレスポンスをクライアントに返す
    • もしすべてのバックエンドサーバがだめだった場合は 502/504 を返す

デフォルトの動作が変わっているため、逆に1.9.12以前のようなどんなメソッドでもリトライする動作にしたい場合は proxy_next_upstream non_idempotentを設定する必要があります。

おわりに

今回の件は不思議な仕様(というか実装)でかなり悩まされましたが、運よくアップデートが入ったので一件落着しました。細かい上にあまり起きない事例ではありますが、しっかり調査・検証した上で運用していきたいと思います。

今回のこの記事が少しでも皆様の参考になれば幸いです。

参考

注記) nginx で能動的に health check する

バックエンドサーバが生存しているかどうかの能動的なヘルスチェックが出来るなら、接続できるバックエンドサーバのみにリクエストを分配できるので、 proxy_next_upstream errorを切ってしまい、エラー時のリトライを off にしてしまえば、それで大丈夫なはずです。(有料版の NGINX Plusや 3rdPartyModule を使用すればできます)
今回は別の都合上 3rdPartyModule を追加できない状況で、かつバージョンアップで対応できそうなので上記の検証は行っていません。

*1:2016/05/09追記
proxy_next_upstream を off にすることでリトライそのものをしないように設定することもできます。 ただしこの設定では connection error/timeout 時もリトライしなくなってしまいます。
例えばバックエンドサーバの一部がダウンした時に、他に稼働しているバックエンドサーバがあるのに、500 を返す場合があるという別の問題が発生します。 そのため今回は使用を見送りました。

サイボウズ×マネーフォワード×Goodpatch Front-end Meetupを開催しました

$
0
0

f:id:cybozuinsideout:20160513170935p:plain

こんにちは。Sales Systemチームの金子です。5月13日(木)にサイボウズ×マネーフォワード×Goodpatch Front-end Meetupというイベントをサイボウズ東京オフィス・大阪オフィスで同時開催したので、ここで報告させていただきます。

当日の様子はこちらにまとめられています。

togetter.com

サイボウズ×マネーフォワード×Goodpatch Front-end Meetupとは?

マネーフォワードGoodpatchサイボウズの3社合同で企画したフロントエンドエンジニアを対象とした勉強会です。

企業向けサービスを提供する3社でどのようにフロントエンド技術と向き合っているかといったことを中心にそれぞれのフロントエンドエンジニアが発表するという構成でした。

サイボウズのターン

f:id:cybozuinsideout:20160513171057j:plain
presentation by @ama_ch


トップバッターとして、弊社の天野(@ama_ch)からkintoneチームのフロントエンド開発事情を発表させていただきました。

  • kintoneチーム(プロダクトマネージャ+プログラマ+デザイナ+品質保証)の開発体制の紹介
  • Google Closure Toolsを中心にした大規模開発事情
  • ビルドプロセス(Lint、ユニットテスト、CSS/SCSSビルド、型チェック&minify)の紹介
  • 大規模フロントエンド開発を支えるユニットテスト事情
  • モダン化への取り組みと今後の課題

といった内容でした。

kintoneではDrag&Dropで業務アプリを構築します。このような処理ではUIサイドで複雑な処理を要求されます。Google Closure LibraryのComponentをベースにUIを構築しながらも、UIの更新処理やイベント制御が煩雑になりやすいといった問題がありました。そこにVirtual DOMを扱えるReactを導入し交通整理を行うことで煩雑さを緩和しようという取り組みがモダン化の一例として紹介されました。

また導入に際しても、

  • プロダクションとは別のプロジェクトで試験的にReactを試す。
  • 温泉合宿でReact仲間を増やしながら、React ComponentベースのUIパーツを増やしていく。

といった新しい技術をチームにどうやって取り入れていくかといったプロセスの紹介もありました。

マネーフォワードのターン

f:id:cybozuinsideout:20160513171155j:plain
presentation by @kotaro_hirayama


2つ目の発表はマネーフォワード社の平山さん(@kotaro_hirayama)による「健全なフロントエンド開発をしよう~Railsに乗っかるという選択編~」でした。

  • マネーフォワード社にはRuby/Ruby on Railsのコミッターがいる。
  • Rails wayに乗っかることで初期開発コストを抑える。
  • 会計の金額入力などスピーディな入力が求められるUIでは、Railsプロジェクトとは独立したrepositoryでReact等適切な技術を選定して開発し、ライブラリとして本体から使用する。
  • しばらくはRails wayに乗っかるけど、node環境リライトも考えている。

といった内容でした。自社の強みを活かしながら、成長フェーズに合わせたフロントエンド戦略が印象的でした。

Goodpatchのターン

f:id:cybozuinsideout:20160513171304j:plain
presentation by @yoshiko_pg


最後の発表は、Goodpatch社の吉田さん(@yoshiko_pg)による「Prott Front-end in 2016」でした。

  • 以前はCoffeeScript/AngularJSを中心にしたSPAだった。
  • ES2015の仕様化に伴い、ユニットテストから徐々に導入し、現在はプロダクトコードの一部をリプレイスした。
  • これから新機能をES2015/2016/Nextで書いていく。
  • Class Property Declarations (Stage 1)This-Binding Syntax (Stage 0)も積極採用
  • 既存コードのES2015化やリファクタリングもやっていきたい。

といった内容でした。CoffeeScriptを使ったプロダクトにおける、decafを用いた具体的なコード置き換え、ESLintを用いたES2015化のサポートの話など、Railsを採用しているプロダクトで参考になる話だったのではないでしょうか。

座談会

3社の発表後は、各社の発表者とともに弊社佐藤鉄平(@teppeis)がファシリテータを務めて座談会を行いました。

f:id:cybozuinsideout:20160513171359j:plain
@ama_ch @yoshiko_pg @kotaro_hirayama @teppeis による座談会の様子


  • 各社のプロジェクト体制
  • フロントエンドのテスト事情
    • ロジックのテストは当然書く
    • DOMが関わるテストは?
  • ES2015をはじめとした新技術への挑戦
  • 採用しているフレームワーク・ライブラリ
  • JavaScriptにおける型について

といったトピックについて、参加者からの質問を交えながら各社のフロントエンド事情を深掘りしました。企業向けサービスということもあってか、3社ともユニットテストに関しては積極的に取り組んでおり、会場からの質問もそれにまつわるものが多かった印象です。

ES2015への取り組みに関しては3社とも導入予定/やっているという状況で、Web標準仕様への追随はそれぞれの課題となっているようです。

座談会は、地の利を活かした弊社@teppeisと@ama_chによる「やっぱり大規模開発には型が必要だよね。」という締めくくりで幕を閉じました。

懇親会

各セッションが終わってから懇親会も行いました。懇親会でも、まだ熱冷めやらぬといった感じで参加者同士でのフロントエンドトークが盛り上がっていました。一部ではVimmerによるフロントエンドにおけるVim開発即興相談会なるものも行われていたようです。

懇親会では、マネーフォワードさん、Goodpatchさんのご協力でいつものピザとは違ったおしゃれな料理やドリンクが並びました。いつもピザの弊社メンバーからは「こんな旨いおにぎり食べれるなんて羨ましい。」といった感想が聞こえてきました。

f:id:cybozuinsideout:20160513171443j:plain
おいしそうな料理たち

おわりに

当日は、東京会場で40人、大阪会場で13人もの方々が参加してくれました。フロントエンドの技術にはまだまだ成熟しきっていないところもあり、開発で苦労することもあるかと思います。そんななかでエンジニアコミュニティの強みを活かして、今回のような勉強会でその苦労の緩和の手助けや情報共有ができればと思っています。

今回の発表は東京会場からのみでしたが、弊社自慢のテレビ会議システムのおかげで、大阪会場の参加者の方々から

  • 「臨場感があった。」
  • 「音声がとても聞き取りやすかった。」
  • 「また開催して欲しい。」

というありがたい意見をいただきました。

もし、技術系イベントを考えている方がいましたら、東京・大阪同時開催もできるのでお気軽にお問い合わせください。

f:id:cybozuinsideout:20160513171533j:plain
大阪会場の様子


参加してくれた皆さん、マネーフォワードさん、Goodpatchさん、ご協力ありがとうございました!

サイボウズはフロントエンドエンジニアを募集しています。

型 + Reactで大規模開発をやりたい人、Front-end Weekly Lunchに興味のある方はぜひ弊社を検討していただければ幸いです。

WE'RE HIRING!

cybozu.co.jp

余談

弊社では毎週火曜のランチタイムにフロントエンドに興味のある人が集まってJSer.infoを見たり議論したりする "Frontend Weekly Lunch"を開催しています。そこで、偶然の産物として生まれたESLintかるたについて、

  • 「ESLintのRuleを使ってかるたやったらおもしろいんじゃないか?と思ったけど、やったら全然おもしろくなかった。」

というのが、当日の実況タイムラインで一番反応があったのは内緒です。

社内利用のための deb パッケージング入門

$
0
0

こんにちは。@nojimaです。 最近、社内のアーティファクトの deb 化を推進しています。 Building Microservicesでも紹介されているように、deb は apt-get installでインストールできたり、依存関係を自動で管理できたりするため、単純な tar.gz を使うよりも利点が多いです。

Debian 界隈では dpkg-buildpackageなどのツールを使って、ソースコードのビルドから deb の作成までを一貫して管理することが一般的です。 しかし、既にソースコードのビルドを行う仕組みを持っている場合、既存のビルド手順をそのまま使いつつ deb パッケージを作成したい場合もあります。 そこでこの記事では、ビルド済みのバイナリがあるときに、それを deb パッケージ化する方法を紹介します。

※ この記事で紹介する方法は主に個人や社内で利用する deb パッケージを作成することを念頭においています。Debian や Ubuntu の公式レポジトリで配布されるような deb を作成したい場合は dpkg-buildpackageを使うべきです。

TL;DR

  • インストールしたいファイルを一時ディレクトリに置いて
  • DEBIAN/controlファイルを書いて
  • fakeroot dpkg-deb --buildを打つ!!

5分でできる deb 化入門

例題として direnvをさくっと deb 化してみます。

direnv の入手とビルド

まず、direnv をビルドしてバイナリを作ります。

$ git clone https://github.com/direnv/direnv
$ cd direnv
$ git checkout v2.8.0
$ make

ディレクトリ構造の作成

ここから deb パッケージを作るための準備をしていきます。 区別しやすくするために、上で direnv をビルドしたディレクトリを $ORIGと呼び、 これから作成するワーキングディレクトリを $WORKと呼ぶことにします。 $WORKは適当な位置に適当な名前で作成してください。

まず、$WORKの下にインストールすべきファイルを配置します。 ここでは、/usr/bin/direnvに direnv の実行バイナリをインストールしたいので、 $WORK/usr/bin/direnvにビルドしたバイナリを配置します。

$ mkdir -p $WORK/usr/bin
$ cp $ORIG/direnv $WORK/usr/bin/direnv

($WORK/path/to/somethingに置いたファイルは /path/to/somethingにインストールされるというルールになっています。単純ですね)

control ファイルの作成

次に、$WORK/DEBIAN/controlに deb パッケージのメタデータを書きます。 control に書ける項目はいろいろありますが、とりあえずは以下の5つの項目を指定すれば大丈夫です。

Package: direnv
Maintainer: Yusuke Nojima <yusuke-nojima@example.com>
Architecture: amd64
Version: 2.8.0-1
Description: Environment switcher for the shell
 direnv hooks into the shell to load or unload environment variables
 depending on the current directory.

各項目の意味は以下の通りです。

項目 意味
Packageパッケージ名です。
Maintainer deb パッケージのメンテナの名前とメールアドレスを指定します。
Architectureインストール可能なアーキテクチャを指定します。弊社の場合は大体 amd64を指定しています。
Versionバージョンです。基本的に <upstream_version>-<debian_version>という形式で付けます。詳しくは man deb-versionを参照してください。
Descriptionパッケージの説明を書きます。複数行フィールドになっていて、一行目に短い説明を書き、二行目以降に長い説明を書きます。二行目以降は行頭に空白を一つ入れる必要があります。

control ファイルはこれらの項目の他にもいろいろと項目を指定することができます。 詳しくは man deb-controlを参照してください。

また、DEBIANディレクトリには controlファイルの他にもいろいろファイルを置くことができます。 いくつかのファイルについては後で述べます。

deb の作成

準備ができたので deb を作成します。 以下のコマンドを打ってください。 (fakerootがないと言われた場合は sudo apt-get install fakerootをしてください)

$ fakeroot dpkg-deb --build $WORK .

これでカレントディレクトリに direnv_2.8.0-1_amd64.debが作成されるはずです。

dpkg-deb --buildは deb パッケージを作成するためのコマンドで、一番目の引数でパッケージの中身となるディレクトリを指定し、二番目の引数でパッケージを出力するディレクトリを指定します。 パッケージの名前は <package>_<version>_<architecture>.debというルールで自動的に付けてくれます。

また、dpkg-deb --buildをすると $WORKディレクトリ内のファイルの uid, gid がそのまま deb に含まれてしまいます。 普通、deb パッケージによってインストールされるファイルの uid, gid は 0 (root) であって欲しいので、これでは困ります。 これを防ぐために fakerootによって見かけの uid, gid を 0 にしています。 基本的に、dpkg-deb --buildをする場合は fakerootの中で行うと思っておけばよいと思います。

インストールしてみる

早速インストールしてみましょう。

$ sudo dpkg -i direnv_2.8.0-1_amd64.deb
$ direnv --help

ついでにアンインストールもしてみましょう。

$ sudo dpkg -r direnv

以上で deb 化入門は終わりです。 以下は Tips 集です。

インストール/アンインストール時に何か処理を行う

インストール時にユーザを追加したり、update-alternativesしたりしたいことがあります。 このような場合、DEBIANディレクトリの下に以下のようなスクリプトファイルを置くことで、任意の処理を実行できます。

ファイル名 説明
preinstインストールの前やアップグレードの前に実行される
postinstインストールの前やアップグレードの後に実行される
prermアンインストールの前やアップグレードの前に実行される
postrmアンインストールの後やアップグレードの後に実行される

例えば、インストール時に www-dataユーザを作成する場合、以下の様なファイルを DEBIAN/postinstに配置すればよいです。 (chmod +xして実行可能にしておいてください)

#!/bin/sh -e
adduser --system --group --quiet --home /var/lib/www-data www-data

注意点として、スクリプトは何かエラーが起こった時には 0 以外の終了ステータスで終了すべきです。 また、エラーになったときに再実行できるように、スクリプトは冪等に書くべきです。

これらのスクリプトが呼ばれるタイミングや引数については Debian ポリシーマニュアル Chapter 6に詳しく書いてあります。

他のパッケージへの依存を定義する

パッケージによっては他のパッケージがインストールされていないと動作しない場合があります。 そのような場合、controlファイルに Dependsを書いておくことで、apt-get installしたときに自動的に依存を解決してもらうことができます。

例えば、Java に依存したパッケージを作る場合、controlに以下のような行を書くとよいです。

Depends: java7-runtime-headless

複数個の依存がある場合はカンマ区切りで指定します。 また、バージョンの制限がある場合はパッケージ名の後にカッコで指定します。

Depends: libjline-java, liblog4j1.2-java (>= 1.2), libnetty-java

パッケージをインストールする時点で既に他のパッケージが必要という場合もあります。 そのような場合、Dependsではなくて Pre-Dependsに書いてください。

Pre-Depends: adduser

バーチャルパッケージを Provide する

同じ機能を提供するパッケージが複数個存在することがあります。 例えば、Java の場合、OpenJDK と OracleJDK が大体同じ機能を提供しています。 このような場合、「OpenJDK または OracleJDK に依存」という宣言が多発することになるのですが、これをいちいち OR で宣言するのは面倒です。 そこで、java-runtimeという仮想的なパッケージを作り、Java ランタイムに依存したいパッケージはこれに Dependsすることにし、 OpenJDK や OracleJDK は java-runtimeという機能を Providesすることで、簡単に依存を記述できるようにしています。

ということで、OracleJDK とかを独自にパッケージングする場合、これらのバーチャルパッケージを Providesに宣言しておく必要があります。

Provides: java-compiler, java-sdk, java-runtime, java-runtime-headless

※ 実際に JDK をパッケージングする場合は java6-sdk, java7-runtime-headlessなどバージョンを含んだバーチャルパッケージも Provide する必要があります。詳しくは公式の JDK のパッケージを見てみてください。

公式パッケージの中身を見てみる

apt-get downloadして dpkg-deb --raw-extractすると公式パッケージの中身が見れます。 controlpostinstとかを参考にしたい場合に便利です。

例えば、mysql-server-5.5の中身を見たい場合は以下のようにします。

$ apt-get download mysql-server-5.5
$ mkdir foo
$ dpkg-deb --raw-extract mysql-server-5.5_5.5.47-0ubuntu0.14.04.1_amd64.deb foo

apt サーバを立てる

HTTP サーバをインストール

@apt-server
$ sudo apt-get install nginx
server {
    listen 80;

    location / {
        root /srv/apt-repo;
        autoindex on;
    }
}

deb を配置

@apt-server
$ sudo mkdir -p /srv/apt-repo/pool
$ sudo cp direnv_2.8.0-1_amd64.deb /srv/apt-repo/pool

Packages.gz を作成

@apt-server
$ cd /srv/apt-repo
$ apt-ftparchive packages pool | gzip | sudo dd of=Packages.gz bs=1M

インストールしてみる

@local
$ cat <<EOF | sudo dd of=/etc/apt/sources.list.d/my-apt-repo.list
deb [trusted=yes] http://サーバ名/ ./
EOF
$ sudo apt-get update
$ sudo apt-get install direnv

設定ファイルを指定する

DEBIAN/conffilesに設定ファイルのリストを指定することができます。

deb パッケージをインストールするときに既にファイルが存在する場合、普通は問答無用で deb パッケージの中のファイルで上書きされます。 しかし、設定ファイルはインストール後にユーザが変更することがあるため、上書きしてよいかどうかがわかりません。

そこで、設定ファイルを DEBIAN/conffilesに指定しておくと、dpkgがある程度よしなに扱ってくれます。

conffilesの例:

/etc/logrotate.d/mysql-server
/etc/mysql/conf.d/mysqld_safe_syslog.cnf
/etc/mysql/debian-start
/etc/init.d/mysql
/etc/logcheck/ignore.d.paranoid/mysql-server-5_5
/etc/logcheck/ignore.d.workstation/mysql-server-5_5
/etc/logcheck/ignore.d.server/mysql-server-5_5
/etc/apparmor.d/usr.sbin.mysqld
/etc/init/mysql.conf

詳しくは Debian ポリシーマニュアル Appendix Eを参照してください。

インストール時/アンインストール時の挙動

deb パッケージをインストールするときに、インストール対象のファイルが既にファイルシステムに存在していることがあります。 また、アンインストール時にアンインストール対象のファイルが既に存在しなかったり他のパッケージからまだ参照されていたりすることがあります。 このような場合、以下のようなルールで処理が行われます。

何かを配置しようとしたときのルール:

Install file/node Install symlink Install directory
Not exists Extract Extract Extract
File/node/symlink Atomic overwrite Atomic overwrite Non-atomic overwrite
Directory Non-atomic overwrite Do nothing Do nothing

何かを削除しようとしたときのルール:

  • 削除ファイルが conffileに含まれる場合、purgeされるまでは削除せず残しておく。
  • 削除対象が存在しない場合、無視する。
  • 削除対象がディレクトリであるか、ディレクトリへのシンボリックリンクである場合、削除対象が他のパッケージから参照されていない場合のみ削除する。
  • 削除対象がディレクトリでないか、ディレクトリへのシンボリックリンクでない場合、削除する。

圧縮方法を指定する

dpkg-deb --buildはデフォルトでデータを xz で圧縮します。 しかし、jar ファイルのように元々圧縮されているファイルを xz で再び圧縮するのは、時間のわりにサイズが減らないのでうれしくないです。 このような場合、オプションに -Z noneを指定すると無圧縮にできます。

おわりに

このように deb パッケージを作成するのはとても簡単です。 tar.gz を心を込めて scp するような運用から卒業して、スマートに apt-get installする世界を目指していきましょう!!


バグゼロを実現した話とその後の顛末

$
0
0

こんにちは、アプリケーション基盤チームの青木(@a_o_k_i_n_g)です。好きなメソッドは emptyIfNullです。

僕は、自社クラウドである cybozu.comのミドルウェアを開発するチームで働いています。具体的には、検索サービスやファイルサーバー、非同期処理用ワーカー、セッションマネージャーなどなどを提供しています。

僕がこのチームに来たのは数年前ですが、当時はバグの多いプロダクトでした。今はすべての既知のバグを直し、残存不具合件数が 0 件、つまりバグゼロな状態になりました。また、バグゼロを実現してから 2 年ほど経過していますが今もその品質を保っています。今回はこのバグゼロを実現した方法と、その後の顛末について記そうと思います。

以前のコード

数年前に提供されていたこのミドルウェア群は、はっきり言って、バグの塊のようなプロダクトでした。

当時のコードは保守性とは程遠いものでした。カプセル化はされておらず、if 文や for 文のネストは幾重にも重なり、サーバーの処理はサーブレット内にベタ書きされ、使われていない変数やメソッドが多数存在していました。不適切な名付け、直感に反する継承をふんだんに用いてマルチスレッドなプログラムが書かれ、デバッグには大変な時間を要しました。とあるセッションマネージャークラスはあまりに複雑化し「改行すら入れたくない」という声まで上がるほどでした。

仕様の面でも優れてない面がありました。あるサムネイル作成機能は縦横無尽に仕様が拡張され、全体像を誰も把握できなくなっていました。僕が担当になって数年後、仕様に存在しない API が発掘されたこともありました。

これらのミドルウェア群が引き起こす不具合でサービスが停止、稼働率に影響を与えることもあるという状況でした。緊急改修してリリースすることがしばしばあり、プログラマのみならず品質保証部隊や運用部隊も疲弊感が高まっていたように思います。

バグゼロに向けて

バグを減らす事は簡単なように見えて色々と障壁があります。すべての既知の不具合を直すにはそれらの壁を乗り越えなければなりません。何より一番大きい壁が工数の問題です。特に試験的な工数の壁が大きく立ちはだかり、単純な不具合の改修が後回しになるのはもはや日常茶飯事と言えるでしょう。実際、僕がバグゼロに向けて直そうとしている時には

「これはとりあえず放置しておこう」

「一旦後に回そう」

「現状はとりあえずこのままで」

という声を何度も聞きました。もちろんこれも一理あります。影響範囲が広ければその分試験もしなければならないからです。だから無闇矢鱈にバグ改修をするわけにはいきません。それでも僕は、バグ含みのコードで運用されるような状態に我慢できませんでした。

そこで僕は下記のいくつかの手段を取って、バグを減らしていきました。

  1. テストコードを書く
  2. ログを監視
  3. リリースサイクルを短くする
  4. 古いコードを破棄する
  5. バグ改修の際に周辺のコードを綺麗にする

テストコードを書く

僕がこのチームの担当になってからまずやったことは、テストコードをひたすらに書くことでした。毎日 8 時間以上、ただひたすらに、です。このテストを書き続ける作業を 2〜3 ヶ月は続けました。あくまで目安ではありますが、カバレッジは 90% 弱程度まで達しており、はた目にも「ほとんどのコードを通っている」と言えるような状況になりました。

これだけテストが揃ってくると挙動の変更に気づきやすくなります。空文字を返していたはずが null を返すようになった、というような些細な変化にも気づけるようになり、品質の向上に貢献しました。同時に「テストコードを書く」という今となっては当たり前の文化が形成されました。

ログを監視

次に僕が行ったのはログの解析です。日々膨大な量が書きだされるログはトラブルが無い限りスルーしがちですが、これを毎日監視し、未知の例外や想定していないログメッセージが出たらすぐ気づけるような仕組みを構築しました。このログ解析スクリプトは、ログを ERROR や WARN, Exception 等で grep し、既知のログを除外していくことで最後には未知のログが残るという至極単純なものです。とは言えこのログ解析スクリプトは極めて有用に働き、累計では相当数の不具合を検出できました。

ログを監視すると、どういったログが必要でどういったログが必要無いのかが見えてきます。その知見をコードに取り込み、不具合を発見しやすく解析しやすいログを吐き出すよう日々改善していきました。

リリースサイクルを短くする

それからリリースタイミングを増やしました。それまでは 1 年に 4 回程度のリリースでしたが、毎月リリースするようにし、バグを直した版のアーカイブをできるだけ早く適用出来るようにしました。これは直接的にはバグゼロには関係無いですが、バグを発見・改修・リリースのサイクルが早まったことでモチベーションが高まり、結果的にはバグゼロへの道に進めたように思います。

古いコードを破棄する

ソースコードには古い実装が何かと残りがちです。そういったゴミコードは一つ一つは些細なものかもしれませんが、だんだん蓄積してくると品質に影響が出てきます。例えば障害対応時にコードを眺めていたらそれは今は使われていないコードだった、というような事もありました。また、プログラマは「いつか使うだろう」という機能をつい入れ込みがちですが、いつか使うと思って実装したコードも結局使われなかった事も何度かありました。そういった使われていないゴミコードは早急に削除すべきでしょう。

個人的には、行数を削減したプルリクエストはもっと評価されて良いように思います。不要なコードを削除したり既存の処理をより簡潔に記述できたなら、それは今後のメンテナンス工数の削減という意味では大きな意味を持っているのでは無いでしょうか。

バグ改修の際に周辺のコードを綺麗にする

バグ改修の仕方には 2 つの方法があります。ひとつは、修正するコード量を最小にしてバグを直す方法。プルリクの diff が小さくなり、レビュー時も低コストで確認出来ます。もうひとつは、原因となった箇所を含めたある程度の範囲を修正し、バグを直すと共に保守性を向上させるような修正の仕方です。

僕の経験上、前者の方法では結局バグゼロになることは無いと判断し、できるだけ後者の方法で修正するよう心がけました。もちろん試験工数に影響の出ない範囲で、です。バグの直し方はケースバイケースではありますが、少なくとも「日々コードの品質を向上させていく」という心がけが無いとバグゼロ実現は困難でしょう。

これらを継続して続けた結果、残存不具合件数がついに 0 になりました。

バグゼロ実現後

そうして、僕のチームのミドルウェア群はバグゼロを達成しました。それからおよそ 2 年ほど経過しましたが、今でも残存不具合件数は 0 件を保っています。

バグゼロを実現して良かったことがたくさんあります。

まず第一に、当然ではありますが、ユーザーに提供するサービスの品質が向上しました。

次に、何か新規に不具合を見つけても、ほとんどの場合は現在開発中のバージョンに取り込めるようになりました。以前は工数の関係で次のバージョンで修正、リリースするのは半年先、というようなこともしばしばありましたが、そういうケースはかなり減りました。

ある程度工数に余裕が出来てきたので、機能改善タスクを当たり前のように取り込めるようになりました。以前は、不具合を直さなければならないのでその分工数が取られてしまい、一度のリリースで行える機能改善には限界がありました。

それから、これは品質保証部やプロダクトマネージャーからの意見ですが、管理コストが減って計画が立てやすくなったという意見も得られました。例えば残存不具合があると不具合再現確認をする必要があるのですが、それに割くコストをまるっと削減できます。

デメリットはちょっと思いつきません。バグゼロを実現する過程では品質保証部の負荷が高まる事もありましたが、それを乗り越えた今では特にデメリットらしいものは無いと言って良いでしょう。

まとめ

プログラマの皆様なら共感してくれるかと思いますが、コードをリファクタしたり、不要になったコードを削除して保守性を高めることは大きな価値を持っています。新機能を提供する価値にも匹敵するかも知れません。でもプログラマ以外に響くのはたいてい新機能であり、その結果、いつも保守性はないがしろにされがちです。

今回のケースでは、保守性を高めることは結果的にユーザーの幸福につながる、という点を示せたように思います。

バグゼロであることは、何より気持ちいいです。自分の担当する製品が日々安定して稼働し、不具合を見つけても後回しにならずにすぐ直せる環境にいることはプログラマのひとつの喜びと言えるのではないでしょうか。

プライバシーポリシー

$
0
0

本ブログは、サイボウズ株式会社が運営しております。本ブログをご覧になる際には、サイボウズ株式会社の個人情報保護方針(Privacy Policy)をご確認ください。

  http://cybozu.co.jp/company/copyright/privacy_policy_detail.html

当社は、お客様が本サイトをご利用された場合、本サイトを改善するために、第三者が提供する以下の各サービス(以下、「第三者サービス」といいます。)を利用して、お客様の本サイトへのアクセスに関する解析を行っています。第三者サービスは本サイトが発行するクッキーをもとにして、お客様による本サイトの訪問履歴を収集、記録、分析します。当社は、その分析結果を受け取り、お客様による本サイトの訪問状況を把握します。本サイトが発行するクッキーおよび第三者サービスにより収集、記録、分析されたお客様の情報には、特定の個人を識別する情報は一切含まれません。また、それらの情報は、各第三者サービスに関するの利用規約またはプライバシーポリシーに基づいて、各社により管理されます。

お客様は、本サイトを使用することにより、当社が各第三者サービスを利用して上記の目的と方法でお客様の情報の収集と保存を行うことにつき、明示的に同意いただいたものとします。

第三者サービス

Google アナリティクス 

当社が本サイトで使用しているGoogle アナリティクスの広告向け機能の内容は次のとおりです。

  • Google アナリティクス リマーケティング
  • Google ディスプレイネットワークの表示回数レポート
  • Google アナリティクスのユーザーの分布とインタレストカテゴリに関するレポート
  • Google アナリティクスを使用して広告Cookieと匿名IDを使ったデータを収集する統合的なサービス

Google アナリティクスの利用規約に関する説明についてはGoogle アナリティクスのサイトを、Google社のプライバシーポリシーに関する説明については同社のサイトをご覧下さい。

  Google アナリティクスの利用規約:
    http://www.google.com/analytics/terms/jp.html 
  Googleのプライバシーポリシー: 
    http://www.google.com/intl/ja/policies/privacy/ 

お客様は、ご自分のデータが Google アナリティクスで使用されることを望まない場合は、Google社の提供するGoogle アナリティクス オプトアウト アドオンをご利用ください。

  Google アナリティクス オプトアウト アドオン: 
    https://tools.google.com/dlpage/gaoptout?hl=ja 

Mixpanel

お客様は、ご自分のデータがMixpanelで使用されることを望まない場合は、以下のオプトアウトのリンクをご利用ください。

  https://mixpanel.com/optout/

Mixpanelの利用規約に関する説明についてはMixpanel社のサイトをご覧下さい。

  Mixpanelの利用規約
    https://mixpanel.com/terms/

サイボウズエンジニアの職場環境 @ 2016

$
0
0

山本泰宇(@ymmt2005)です。今回はサイボウズエンジニアの働く環境を詳細にご紹介します。

働く場所

サイボウズの開発拠点は現在国内外に 5 箇所あります。

オフィスで働くのが基本ですが、サイボウズには自分の好きな時間・場所で働けるウルトラワークという制度があるので、自宅や実家で働くことも可能です。どの程度可能かというと、

  • Amazon から宅配便が届くから、
  • 雨・雪が降っているから、
  • 電車が混んでいて乗りづらいから、
  • 急に子供が熱を出したから、

といった様々な理由で、連絡一ついれれば在宅勤務できます。後述する社内情報システムを活用することで、在宅でも開発業務や社内会議への参加が可能です。

働く時間

開発・運用のほとんどのメンバーは裁量労働制で働いていますので、働く時間は深夜帯を除けば原則自由に変更できます(深夜帯は深夜勤務手当てがでる関係で、事前申請と承認が必要)。とはいえ、通常は何時から何時まで(例: 8 時 〜 17 時)働くつもりかチームに共有して、通常と異なる場合は連絡するようにしてもらっています。

コアタイムは特にありません。チームのミーティング時間で実質束縛されるというケースについては、チームごとに相談してもらっています。全社で毎日朝会、といったことはありません。

実際に働く時間を観察すると、ほとんどの人が 8 時 〜 10 時に出社して、17 時 〜 20 時に退社しているようです。急用で午後から出社とか、早めに帰るといったことも当然可能です。

机・椅子・PC

東京オフィスの設備を例にとると、開発・運用ゾーンの机にはブーメラン型のデスク、椅子にはコクヨのベゼルを導入しています。

f:id:cybozuinsideout:20160520124723p:plain

PC は標準機を情報システム部にて設定し、原則としては標準機を配布しています。標準機では満たせない要求(例えば高性能 GPU 搭載機)がある場合は、標準機以外のものも調達可能です。現在の標準デスクトップ PC のスペックは以下の通りです。

部品 スペック
CPU Core i7 6700 (4 コア)
メモリ 16 GiB
ストレージ 256 GB SSD + 1 TB HDD

通常の開発であれば必要十分なスペックにしています。開発以外の事務職であっても全く同じスペックの PC が支給されています。

標準モニターは Dell U2415で、2 枚までなら無条件で支給されます。モニター、キーボード、マウス(トラックボール)は申請すれば希望のものを購入してもらえます。ちなみに私は Realforce + Kensington SlimBladeを使っています。

また、社員はひとり一台 MacBook か Windows のノート PC も与えられます。全社員がノート PC を持っているので、ハッカソンや開発合宿などがやりやすい環境です。

PC 類は購入から 3 年経過すれば無条件で新しいものに交換してもらえます。全社で共通の標準機や交換ルールを設定することで、不公平感がでないように努めています。

まとめると:

  • 広いデスクと良い椅子
  • デスクトップ PC は 4 コア CPU + 16 GiB メモリ + SSD + HDD
  • モニターは 24 インチ 2 枚
  • 全社員ノート PC 所持
  • 購入から 3 年経過で無条件交換
  • キーボードやマウスは申請すれば希望のものを支給
  • 上記以外のケースでも業務上必要であれば柔軟に対応

社内情報システム

サイボウズは国内国外合わせて 600 名ほどの規模ですが、内外に多数のオフィスを構えています。また、ウルトラワークのように時間・場所を問わず働ける制度を推進しているため、それを支える情報システムの提供は頭が痛いやりがいのある仕事です。

拠点間のコミュニケーションや在宅勤務を支える情報システム群を紹介します。

自社グループウェア

cybozu.comをもちろん大活用しています。社外からでも TLS クライアント認証でセキュアに利用できます。特に kintoneは開発業務に不可欠で、要件・仕様・タスクの管理やオンラインの議論などに活用しています。

Cisco TelePresence ビデオ会議システム

各拠点の会議室に Cisco のビデオ会議システムが導入されています。大きな会議室には天井に高感度マイクが設置されていて、部屋のどこで話しても相手にクリアに音声が伝わるようになっています。また、Jabber というクライアントを利用して社外からでもビデオ会議に参加することができます。

大阪と東京の会議室をつないで多拠点で勉強会を開催したり、有給休暇中だけど面白そうな勉強会に自宅から参加、なんて使いかたもされているようです。

VPN & VDI

多重の認証で保護された VPN アクセスが提供されます。在宅でも普段使っている開発環境にアクセスして開発業務をすることが可能です。また、仮想デスクトップインフラ(VDI)も提供しており、社内に PC を持たない人でも在宅で各種業務ができるようになっています。

開発用ソフトウェア

GitHub便利ですよね。サイボウズでは GitHub のオンプレミス版である GitHub Enterpriseを導入しています。この記事も、社内 GitHub のプルリクエストでレビューしてもらっています。

BTS/ITS は GitHub Issue では力不足なので kintoneを使っています。GitHub のプルリクエストと kintone のレコードを自動でリンクするブラウザ拡張を作ったので、スムースに連携できます。

使用言語はチームによってまちまちですが、Java, JavaScript, Swift, C/C++, Go, PHP, Python が主に使われています。インフラまわりは最近 Go と Java での開発比率が高くなっています。

エディタは IntelliJ IDEAを利用する人が最近は増えています。他は VisualStudio, Emacs, Vim といったところです。

継続的インテグレーションには Jenkins と Drone を使っています。特に Jenkins は重宝しており、SlideShare でノウハウを公開しています。

あと最近、各種アーティファクトの中央管理サーバーとして Artifactory Proを導入しました。既に自作の Debian/Ubuntu パッケージ管理サーバーとして活用していますが、今後さらに活用範囲を広げていくつもりです。

開発用共有サーバー

開発用の共有サーバーは用途に応じて複数の種類があります。

VM サーバーファーム

VMware で統合管理されるサーバー群です。開発チームが自由に VM を作成して利用できます。リソースが足りなければ情報システム部が適宜サーバーを追加してくれます。

何にでも使えるのですが、Windows Server やクライアントを動作させることが多いでしょうか。

開発用データセンター

cybozu.com の本番環境と同様のネットワークやサーバー構成を備えたクラウドインフラです。IaaS の一種と考えていただいて構いません。AWS や GCP のような汎用性はなく、自社製品を動かすことに特化しています。

こちらでも自由に VM や高可用ストレージを作成することができますが、作れる VM の種類は限定されています。主に、自社製品の品質保証に利用しています。脆弱性報奨金制度の試験環境もこちらで構築されています。

性能が必要なケースなどでは、VM ではなく実機を占有して利用することも可能となっています。

AWS / GCP

Selenium テストで大量のクライアントが必要な場合は GCP のようなパブリッククラウドを利用しています。

勉強や研修の機会

業務時間中に多数の勉強会が開かれています。下の画像は kintone のスペースを「勉強会」で検索したものですが、出ているのはほんの一部です。毎日何かしら開催されていますが多過ぎて実態は良くわかりません。

f:id:cybozuinsideout:20160520124739p:plain

社外の勉強会にも言えますが、勉強会に良くでる人も、そうでもない人もいます。出る出ないは各人の判断として尊重されますので、出ないから(出るから)と非難されるようなことはありません。

サイボウズ社内を会場として、社外の方に公開された勉強会も開催しています。最近では、東京、大阪、松山の各拠点で以下の勉強会を開催しました。

技術書は非常に簡単な手続きで購入してもらえます。運用本部の場合は、kintoneのスレッドに希望の図書名を書くだけです。購入手続きはアシスタントの方が代行してくれるので、待っていれば自席に届きます。

業務上の必要性があればですが、海外のカンファレンスにも毎年誰かしら参加しています。最近だと Percona LiveKafka SummitDreamforceなどです。

制度と風土

先述したウルトラワーク以外にも以下のような制度があります。

  • 自由に副業が可能

    会社の名前を利用する場合は申請が必要ですが、無関係の場合申請不要です。

  • 最長 6 年の育児休業

    男性でも数ヶ月の育児休業をとるケースは頻繁に見られます。

  • 働きかたをいつでも変更可能

    子供が産まれたら短時間勤務にし、手がかからなくなったらフルタイムに戻るといったことが自分の意思で選択可能です。

その他色々ありますので、詳しくはこちらのページをご覧ください。

社長の青野いわく「企業のワークスタイル変革は、ツール、制度、風土の3要件に取り組む必要がある」とのことで、これらの制度を活かせる風土作りも重視しています。

オープンソース活動

当社製品・サービスの開発には多数のオープンソースソフトウェアを活用しています。当社も多くはありませんが、以下のようなソフトウェアをオープンソースで公開しています。

今後もオープンソースソフトウェアコミュニティの良き一員として、積極的に活動していくつもりです。

一緒に働きませんか?

以上、2016年現在のサイボウズエンジニアが働く環境について紹介しました。

サイボウズでは成長中のクラウドサービスを開発するエンジニアを積極的に採用しています。業務の内容については、以下の記事もあわせてお読みください。

We are hiring!

するするさせたよ:サイボウズ採用情報のアニメーション

$
0
0

こんにちは、大阪開発部のブノアです。

この間、サイボウズの採用情報ページのアニメーションについてグーグルデベロッパーエキスパートの矢倉さんにツッコミを頂きました!今回はそのアニメーションをするするさせたよという話をしたいと思います。

経緯

4月頃、レンダリングパフォーマンス勉強会を社内で開催しました。東京開発部の鉄平がその話をツイッターで呟いたら矢倉さんにツッコミを頂きました。

矢倉さんが仰っている動くアイコンはこれです。 f:id:cybozuinsideout:20160519150803g:plain

ちょうどレンダリングパフォーマンスの勉強していたので、勉強した事を活かして遊びで調査し、解決まで試していました。
しかし、本番環境を修正するところまでは行きませんでした。

そして、一ヶ月後ぐらいに同じ矢倉さんが調査も解決も含めて記事まで書いてくださいました。調査方法や調査結果の情報分析、解決方法も細かくわかりやすく書いてあるので興味のある方は是非ご覧ください。

するするさせたい:サイボウズ採用情報のアニメーション(その1) - fragmentary
するするさせたい:サイボウズ採用情報のアニメーション(その2) - fragmentary
するするさせたい:サイボウズ採用情報のアニメーション(その3) - fragmentary

私は記事をみてめっちゃありがたいと思いました。そこまでやって頂いたらやる気が伝染します!
その時に社内の仲間と話し合って採用情報ページの本番環境を直す決断をしました。

今は既にするするしてるんじゃないの?

f:id:cybozuinsideout:20160519145020p:plain
という声が社内から聞こえました。人によってするするしている、するするしていないってなんでやろと考えてみました。

私のパソコンで確認した時は、完璧でスムーズなアニメーションではなかったのですが、気になる程でもありませんでした。
それは何故だろうと考えると、私のマシンは2015年のMacBook Proでスペックがとても高い事に気づきました。矢倉さんの記事によると、2011年のMacBook Airを使ってサイボウズの採用情報ページをご覧になったそうです。
矢倉さんと同じくスマホでご覧になる方もパソコンよりはスペックが低いので採用情報ページのアニメーションがするするしていないように思う人はいっぱいいるでしょう。

気にしている、気にしていない、慣れている、慣れていないにかからわず、 あらゆるユーザに「するするアニメーション」を提供し、アクセシビリティに配慮するのは私達デベロッパーの仕事であると思いました。

修正前後の比較

本番環境が修正されたのでもう確認できないのですが、修正する前に修正前版と修正後版が見比べられるビデオを撮りました。わかりやすくするためスローモーションにしています。

www.youtube.com

上の方は修正前で下の方は修正後になっています。上の方はやっぱりよくみるとガタツキがわかりますね。

修正の説明

矢倉さんは細かく説明してくださったのでまとめだけを書きます。
またレンダリングパフォーマンスについて詳しく知りたい方はグーグルのレンダリングパフォーマンスページをご覧ください。

修正前のコード

(function loopIcon(){
  $('.kintone .icon')
    .css({backgroundPosition:'0'});
    .animate({backgroundPosition:'-1680px'}, 1680*36, 'linear', loopIcon);
})()

考えられる問題:

  • JavaScriptの負荷:jQuery.animateだと定期的にJavaScriptを実行され、ターゲット要素の backgroundPositionを少しずつ変えていきます。JavaScriptはいつ実行されるか保証できないためタイミングによってvsyncを挟む事もあって、その時はフレームが表示されずフレームレートが落ちる事もあります。
  • backgroundPositionはペイント処理が実行されるプロパティ(参考:csstriggers.com/background-position)なのでフレームのコストが上がります。フレームができるまでの時間が長くなりvsyncに間に合わない可能性が高まります。

修正後のコード

上記のJavaScriptを下ので上書きして、

// IE9でないときだけ実行したいのでJavaScriptで実行していますが、// CSSのみでも実装可能です。
$('.kintone').css({overflow: 'hidden'});
$('.kintone .icon')
  .css({
    width: '5040px',
    animation: 'loop-icon-transform 60480ms linear infinite'});

スタイルシートにキーフレームを定義する

@keyframes loop-icon-transform {
  from { transform: translateX(0px)     }
  to   { transform: translateX(-1680px) }}

IE9はCSSアニメーションをサポートしていないため、IE9の場合は修正前のコードが実行されるようにしています。

改善された点:

  • JavaScriptは定期的に実行される事がなくなったので、JavaScriptの処理コストとUIスレッドをブロックする事がなくなりました。
  • アニメーションに利用しているプロパティの transformはコンポジットプロパティ(参考:csstriggers.com/transform)なので、ペイント処理は実行されません。ペイント処理は高コストであるため、それでフレームのコストが大分減りました。

フレームのコストを減らした事でロースペックのマシンでもアニメーションがするする動くようになりました!

最後に

  • あらゆるユーザ環境に「するするアニメーション」を提供するのも私達デベロッパーの仕事である。
  • ガタツキのあるアニメーション、スクロール等は不具合だと判断し、KAIZENして行きたい!
  • 記事まで書いてくださった矢倉さんに感謝!
  • レンダリングパフォーマンスはやっぱり面白い!

以上、ブノアでした!

KPIツリー勉強会してみました!

$
0
0

サイボウズ メールワイズの PM やってます、山田(@shoji_yamada)です。
5月20日、講師に POStudy主宰者の関さん(@fullvirtue)と、GMOペパボ高橋さん(@kenchan)をお迎えし、サイボウズの開発PM 対象に「KPIツリー勉強会」をやっていただきました。

とある事業会社・・・サイボウズのことです。気を遣わせてごめんなさい。
事業会社からサイボウズに書き換えてくれた関さん。ありがとうございます。

事の発端

4月にサイボウズで開催されました pmjpオフ会#3にさかのぼります。
そこでの GMOペパボ高橋さんの発表


KPIツリー・・・。KPIは他社のPMさんがよく口にされています。

前々から気になっていたのですが、サイボウズの開発PMは「指標」や「数値」を元に仕事してます!とはいえないのが現状です。
漠然と数値を見るのではなく意識するきっかけを作りたいよね、「指標」を使って成長を判断をすることを意識できるようにしたいよね、という話が部内で出て・・・

ということで、関さんと高橋さんにお願いし、プライベートセミナーを実施していただきました。

KPIツリー勉強会やってみた

用意いただいた資料

参加者は都合良く各プロダクトの開発PMが集まりました。
そして、PM部長。担当プロダクトがないから、人材募集がお題です。
そうです。サイボウズはプロダクトマネージャを募集しています。

キャリア採用プロダクトマネージャー

話がちょっとそれました。

まず、各自でKPIツリーを作ってみました。
(隠すほどのものじゃないのですが・・・緑は他のメンバーからのフィードバック付箋)

f:id:cybozuinsideout:20160524184553j:plain

私が考えてみたのは、枝としては大きく2つなのかな、と。

  • 売上げ(額)
  • 継続率(製品を使い続ける率)

ここを元に枝を増やしてみました。(他のメンバーはそうでもなかったような)。

作り終わった後で、他のメンバーの KPIツリーを見てフィードバックします。
関さんに、「出せない人はプロダクト企画を評価できないひとですよー」といわれます。

うん、でないものだ・・・と。この時点で一回振り返り議論。そして休憩。

再開後、KPIの元となるポジショニングマップを各自作成してみました。

f:id:cybozuinsideout:20160524190054j:plain

超苦戦。どんな顧客に・・・がかけなくて、時間切れ。 そして、他のメンバーの内容を見てフィードバック・・・。 KPI以前なのかもしれない。いや、やることを理解できなかったんだ。何回も繰り返せば、出来てくるはず・・・きっと。

さらに、ポジショニングマップから、何に注力するかを明らかにし、それを KPIツリーに反映させる・・・どうもかみ合わない。 これは、機能のレイヤーで考えてしまっているからで、ビジネスのレイヤーで考えられていないからではと思いました。

最後に、各自振り返りをして、共有して終了。

今回のまとめ

なんか散々・・・ではあるのですが、プロダクトをKPI観点から見るという思考はできました。気づきもたくさんありました。

  • 既存の継続、新規の獲得、それぞれのKPIをどう置くかが迷った
  • 他部署のKPIへの意識が低いことが分かった
  • 流れ(ストーリー)で考えると書きやすそうな気がする
  • 計算式になるかを意識するとKPIが出てきにくかった
  • KPIツリーは上にゴールが来る(!)

関さん、高橋さん、ありがとうございました。

2時間ほぼワークに当てられたのは、同じ会社のメンバーだから、自己紹介いらず、隠す必要なし、他のメンバーに気を遣う必要なし、ということで、効率よく使えました・・・が時間が足らない、とw
とはいえ、これを持ち帰り、BPM(ビジネスの責任者)と協力しつつKPIツリーを作ってみて開発・販売で共通のKPIを認識しながら活動するようにしたいと思います。

Viewing all 690 articles
Browse latest View live