アウトライナーの新しい形を考える

アウトライナーというソフトウェアがある。

アウトライナーはその名が表す通り、情報のアウトライン(輪郭)を作るためのソフトウェアである。情報のアウトラインを作るというのは、別の分野の言葉で言い換えれば、知識や考えの「デッサン」をすることに相当するだろうか。考えを文章として精密に表現する前に、その論の構造だけを抜き出して、全体として筋が通るような組み立てを考える。

そもそもどのようなときに考えのデッサンをする必要性に迫られるのかを考えてみると、比較的込み入ったことを考えたり、あるいは、ある程度ボリュームのある論理的な文章を書いたりするときなど、頭の中だけでは容易に解決できないような複雑な問題に取り組むときに限られるだろうし、そう考えると日常的にアウトライナーを利用している人はそれほど多くないのかもしれない。

それでも、日常生活の中でちょっとした考えをまとめたり、あるいは何か記録しておきたい事柄があった場合に、何らかのツール(紙のノートでも、パソコンやスマホでも)を使ってそれらを「箇条書き」にするという経験は誰にでもあるのではないだろうか。

あるいは、単に箇条書きにするだけではなく、字下げなどをして項目に階層を持たせて、もう一段深い掘り下げを行なっている人も少なくないのかもしれない。その階層構造の導入がいわゆるアウトラインを作ることに他ならない。

字下げをして階層を作れるなら、アウトライナーなんて専用のソフトウェアは必要ないのではと思われるかもしれない。アウトライナーの強みは情報の構造を構造として扱えることにある。例えば、同じ階層にある項目を並び替えたり、ある項目を配下の階層ごと別の場所に移動したり、階層の中を上下に移動させたりなど、構造自体の操作を容易にしてくれる。あるいは、その構造を眺めるときに、ある項目にフォーカスしたり、その場では不要な項目を折り畳んで隠したりと、情報の見え方も柔軟にカスマイズできる。

筆者が地道に開発を続けている Cotoami も、ジャンルとしてはアウトライナーに近いソフトウェアである。Cotoami 以外にもそのようなソフトウェアを以前から作っていて、自分でもそれらを日常的に使ってきたので、アウトライナーというソフトウェアの存在は知っていても、実際にそれを利用したことはなかった。

筆者がアウトライナーに抱いていたイメージは、扱う構造がツリー(階層)であるがゆえに、表現できる情報は、ある時点、ある視点からの知識のスナップショットに過ぎないということだった。ツリーというのは一次元の構造である。あるノードは必ず一つのノードに所属し、文脈は一意に固定される。

最終的なアウトプットとしては当然スナップショットにならざるを得ないにしても、そこに至るまでの思考の過程においていくつかの文脈が入り乱れることもあるだろうし、そのようなものを表現できる思考のツールが必要ではないかと考えたのだ。

しかし、ある時『アウトライン・プロセッシング入門』という本にたまたま出会って、それを読んでみたところ、まさに自分が抱いていたような懸念に言及した上で、本来あるべきアウトライナーの利用方法が語られていて、アウトライナーを利用していても結局はそこに辿り着くのだと分かって大きな感銘を受けた。

例えば、アウトライナーでは、最上位の項目(テーマ)からスタートして、下位の項目に向かって徐々に内容を詳細化していく、いわゆるトップダウンのアプローチでアウトラインを作っていくのではないかという先入観があるが(これは同じツリー構造を利用したマインドマップでも同様)、

実践的なアウトライン・プロセッシングは、トップダウンとボトムアップを相互に行き来する形で行われます。トップダウンでの成果とボトムアップでの成果を相互にフィードバックすることで、ランダムに浮かんでくるアイデアや思考の断片を全体の中に位置づけ、統合していきます。 私はこのプロセスを「シェイク」と呼んでいます。行ったり来たりしながら「揺さぶる」からです。 – 『アウトライン・プロセッシング入門

という感じで、実際には「トップダウンとボトムアップを相互に行き来する」ことで、アウトラインを作る過程で新たに発見した知見を構造に反映していくという、より発見的なアプローチが推奨されている。

「トップダウンとボトムアップを相互に行き来する」という考え方については、Cotoami も全くその通りなのだが、アウトライナーとの違いをあえて挙げるとすれば、Cotoami では基本にランダムな入力があって、そこから構造を組み立てていくという、どちらかと言えばボトムアップに焦点を置いたデザインになっているところだろうか。

Cotoami では、全ての情報がフラットに表示されるタイムライン(複数人で利用する場合はチャットして機能する)と、組み立てられた構造を見せるドキュメントビューやグラフビューが分かれていて、片方はフロー、もう片方はストックという感じで、これらを相互に行き来しながら、構造を何重にも組み立てていくという仕組みになっている。

例えば、ある項目(Cotoami では「コト」と呼んでいる)は、複数の文脈に同時に関係しているかもしれないが、ツリー構造ではそれを表現することができない。そこで、関係をもっと自由に作ることのできるネットワーク構造を導入して、Aという文脈におけるX、Bという文脈におけるX、という感じで同じ情報を複数の視点から見れるようになっている。先ほど、アウトライナーはスナップショット的だと書いたが、アウトライナーと異なる構造を採用している Cotoami はデータベース的なソフトウェアだと言えるかもしれない。

興味深いことに『アウトライン・プロセッシング入門』では、ツリー構造とネットワーク構造の対立についても言及されている。

アウトライナーで扱う「生きたアウトライン」は、見た目はツリーでも通常のツリーよりずっと複雑な内容を扱うことができます。なぜならそれは「流動的なツリー」だからです。それは常に変化する可能性をはらんだ「プロセス」であり、完成品ではありません。

確かにアウトライン上では、ある記述は「A」と「B」どちらかにしか分類できません。しかしアウトライナーに入っている限り、それはある記述が「A」と「B」のどちらに含まれるべきかという思考プロセスの、最新のスナップショットにすぎません。今「A」に分類されている内容が、次の瞬間には「B」に再分類される可能性を常にはらんでいます。見た目上「A」に分類されているけれど、同時に「B」でもある可能性が常にある。それはもはや単純なツリーではありません。

そして重要なのは、アウトラインを最終的に完成品(たとえば文章)としてアウトプットするためには、いずれにしても「A」か「B」かを選ばなければならないということです。複雑なものを勇気を持って単純化しなければならない瞬間がくるのです。そのこと自体に思考を強制し、発動する効果があります。

実は発想と呼ばれるものの多くが、この強制的な単純化から生まれてくるのではないでしょうか。「文章にしたり人に説明したりするとその対象がよりよく理解できる」と言いますが、それは複雑で絡み合った情報を、単純なツリーやリニアな語りに強制的に変換しなければならないからです。その過程で情報は咀嚼され、自分の視点が確定します。アウトライナーで編集されるアウトラインは、「流動的なツリー」であることでそのプロセスを促すのです。

一方の網目構造では、複雑なものが複雑なまま存在できてしまいます。それは一見するといいことですが、ともすると素材が素材のままになってしまう可能性があります。

たとえば情報を保管し、必要に応じて引き出すということを第一義に考えれば、複雑な情報を複雑なまま扱える網目構造のほうが優れているかもしれません。しかし「文章を書き、考える」道具、つまり発想ツールとしてみた場合の有効性は、アウトライナー否定派がいうほど単純には決められないのです。

アウトライン・プロセッシング入門

アウトライナーで扱うのは「生きたアウトライン」であって、ある項目がどこかに所属するというのは、その瞬間のスナップショットに過ぎず、常に変わり得るということ、そして発想を具体化するためにはいずれにせよ「A」か「B」かを選ばなければならないということ、その強制的な単純化が発想にとって重要なのではないかということ、いずれも深く納得できる話で、Cotoami が採用するネットワーク構造(網目構造)に対して、説得力のある反論になっている。

実は、この本を通じて主張されている、「考えてから書く」のではなく、書きながら考えて、そこで発見したことを構造にフィードバックさせていくといったような発見的なやり方を踏まえていれば、アウトライナーでも Cotoami でも基本的にはそれほど違いはないと個人的には考えている。これはツールというよりもマインドセットの問題だからだ。

この問題はプログラミング言語における、オブジェクト指向と関数型の対立に似ている。世の中にはオブジェクト指向言語とか、関数型言語というカテゴリーでプログラミング言語が分類されることがあるが、それらのカテゴリーがプログラムデザインのマインドセットを決定するわけではない。オブジェクト指向言語で関数型プログラミングを実践することは可能であるし、同じように関数型言語でオブジェクト指向プログラミングを実践できる。

もちろん個々のツールがどういうアプローチに向いているかという傾向はある。アウトライナーがスナップショット的、Cotoami がデータベース的というのは、その傾向を表している。

ただ、Cotoami としては、現状のアウトライナーではなかなか難しいと思われるアプローチを可能にしていきたいところである。そうすることによって、知的生産の可能性を少しでも広げていきたい。そのためには『アウトライン・プロセッシング入門』で紹介されているアプローチの先にはどのような展開があり得るかということについて考えなければならない。

その一つの可能性が「コトノマ」というコンセプトである。

Cotoami のデータベースでは、アウトライナーの一つの項目に相当する「コト」という単位の情報が相互につながりを持って、単純なツリーではなく、自由なネットワークを構成できるようになっている。ネットワークが大きくなるにつれて、他より多くのつながりを持つコトが現れるだろう。そのようなコトは、参加者にとって重要なコンセプトを表している可能性が高い。そのコトを「コトノマ」に格上げすることによって、そのコンセプトについてさらに深く掘り下げることができるようになる。

そして、そのコンセプトについて掘り下げる過程でさらなるコンセプトの発見があり…という連鎖を Cotoami では期待している。

この機能は、アウトライナーの「フォーカス」という機能の発展形だと言えるかもしれない。

アウトライナーの持つ「文章の集合体」としての性質は、アウトライナーを開発する側でも必ずしも意識していない場合があるように感じます。 この点に自覚的なアウトライナーは、それをサポートする「フォーカス」という機能を持っています(アウトライナーによって「ズーム」「ホイスト」「巻き上げ」などとも呼ばれます)。ひと言でいうと、アウトラインの中の任意の項目を一時的に最上位階層として表示する機能です。他の項目は見えなくなるので、その項目に集中することができます。 – 『アウトライン・プロセッシング入門

コトノマは、そのテーマについての情報を集めるための専用のタイムラインを持つ他は、他のプレインなコトと同様に扱うことができる。ネットワークの中で重要なポイントを表すための特殊なコトだと言えるだろう。

ネットワークを育てる内に、その過程で発見した重要な事柄がコトノマとしてネットワーク状に結節点を作る。そのコトノマのリストは、ネットワークを育てた参加者の足跡であると同時に、外からデータベースを見るときの入り口としても機能する。

Cotoami の基本的な考え方で、アウトライナーと一線を画すのは、そもそもデータベース的なシステムだということもあるが、一人の人間がその全貌を掴めないぐらいの膨大なネットワークを作るというユースケースまで想定しているところだ。一人で利用するケースから、チーム、不特定多数のコラボレーションまで、あらゆる情報を蓄積して巨大なネットワークを作る。

そのときそのときに必要なアウトプットのために、スナップショット的なツリーを作ることはあっても、全体として綺麗に整理された状態を保つということにはこだわらない。かつて、野口悠紀雄氏が「「超」整理法」で書いていたように、探すための分類・整理は結果的に徒労に終わることになるし、整理は情報を既知のカテゴリーに収めることになって、むしろ新しい発見を遠ざける原因になってしまう。

「発想と呼ばれるものの多くが、この強制的な単純化から生まれてくる」という仮説が、確かにそうかもと思わせる一方、拙速な整理が、結局のところ既知のカテゴリーに立ち返ってしまう原因になるのではないかという予感もある。何か新しいことを発見するために、カテゴリーは結果として横断されなければならない。

そのために「コトノマの連鎖」というものが重要になってくるのではないかと考えている。投稿した情報を眺めていく過程で生まれたコンセプトをコトノマに変換し、そこからまた発見される隣接概念をまたコトノマ化する。そのような隣接概念の渡り歩きによって生まれるネットワークを可視化することによって、その足跡が結果として未知のカテゴリーを生むのではないかと期待している。

何気ない会話からどうやってストックに耐え得る知識を引き出すか?

情報のフローとストックを同じシステムの中で循環させるという Cotoami の枠組みが、とりあえず一通り出来上がってきたので、実験しながら改善策を探るというフェーズに入っている。

公式の Cotoami サーバー https://cotoa.me では、公開でその実験を行なっているのだが、参加者がまだ少ないものの、会話の中からストックとなる情報を組み立てる過程が少しずつではあるが現れてきている。

その循環のプロセスは以下の通りである。

例えば、普通のチャットのように、タイムラインにただ投稿したものは構造のないフラットな、時系列の情報になる。これがいわゆるフローの状態だ。情報はどんどん流れていって、常に最新の情報だけを目にすることになる。

この常に流れていく一次元のタイムラインに、自然な形で構造を導入するとしたら、どのような方法があるだろうか? 最も簡単な方法は、既存の情報に対して reply(返信)をすることである。reply によって、時系列の上に、文脈(話題)の構造が生まれる。

この仕組みは全く新しいものではなく、2chの安価やツリー型の掲示板など、Webの黎明期からあちこちで利用されている。ただし、その構造を見せる方法には色んなバリエーションがある。例えば Cotoami では、タイムラインに複数の話題が混在していても個別に遡っていくことができる。

以下のように、PC上のブラウザであれば、reply の連鎖によって生まれたスレッドを同時に複数開いて比較することも出来る。

reply の次の段階では、タイムライン上で重要だと思うコト(Cotoamiでは個々の発言を「コト」と呼ぶ)を pin して、流れていかないように確保する。

普段通りにチャットする流れで、reply によって会話の繋がりを作るのは自然に行えるが、その発言の中で何を pin しておくかという判断は思ったよりも単純ではない。特に、気ままにチャットを楽しんでいるようなときは、そのようなアクションがあることさえ忘れてしまう。自発的な選択という行為は案外ハードルが高いし、会話に参加する人数が多くなるほど、何を選択するか、という問題の重みは大きくなる。

あるいは、UIのデザインでその障壁を下げることは可能かもしれない。例えば、”pin” ではなくて、”いいね” のような、会話の中で自然に行えるアクションにするとしたら、どのようなものがあるだろうか?(例えば「メモメモφ(・ω・`)」ボタン?)

pin で起点となる情報が出来たら、そこに has-a 関係となるようなコトを追加して構造を作っていく。

現状の Cotoami では、has-a 関係と reply の区別はなく、双方ともポストとポストの間にコネクションを作って辿れるようにする、という同じアクションである。将来的にはコネクションのタイプを指定できるようにしても面白いかもしれない。

そして、最後はコトのコトノマ(カテゴリー)化。Cotoami で最も重要なステップである。

「コトノマ」とはチャットの文脈で言えば、いわゆるチャットルームに相当するものである。コトをコトノマに変換することによって、その話題についての専用のチャット・タイムラインを用意でき、議論をさらに掘り下げることが出来る。と同時に、コトノマはコトと同じように扱えるので、相互に繋げたり、タイムラインにポストしたりすることも出来る。

Cotoami の最終的な目的は、このコトノマのネットワークを作ることである。そしてコトノマは、タイムラインでやり取りされている内容から自然に生まれてくるのが望ましい。そうなれば、出来上がったコトノマのリストは、参加者にとって(先入観からではない)自然に重要なコンセプトになるはずだというのが Cotoami の仮説である。

ここまでの流れを追うと、コトノマが出来上がっていく過程というのは、基本的には「連想」によって駆動されることになる。なので、あるテーマについて話しているときは、その隣接概念がコトノマの候補として上がってくるケースが多い。ただ、個人的にはこの連想だけだとつまらないと思っている。それとは少し違う方法でコンセプトの発見に誘導できないか、というのが今後の大きなテーマである。

チャットでウィキペディアをつくる

例えば、何か新しいことを始めようと思うと、どうしても「新しいことを始める」ためのフォーマットに乗っかってしまう。

これは、「やるぞ」っていう意気込みが強ければ強いほど、むしろそうなってしまったりするし、その何かをやろうという構えが、本当はもっと柔軟に考えられたかもしれない可能性を狭めることになったりしないだろうか?

例えば、新しいことを始めるぞということで、いついつどこに集まってブレストでもやろうということになって、立派な会議室なんかを予約して、場合によっては上司や先輩、あるいはよく知らない人などが集まった空間で、本当に価値のあるアイデアが出たりするものなのだろうか?

それよりも、日頃たわいもなくしている雑談で、ふざけたりしながらいろんな話題を縦横無尽に移動する中で、「これってそもそもこういうことじゃない?」とか「これとこれを組み合わせたら面白そう」みたいな気づきから、自然にコンセプトが浮き上がってきて、それが結果として「ワタシたちの百科事典」になるような仕組みあったとしたら、それが本当に自分たちがやりたかったこと(必要だと思ったこと)の種になるんじゃないだろうか?

例えば、

みたいな会話が、

という感じで「煽り運転」というテーマに辿り着く。

さらに、そのテーマにつなげる形で、あちこちで見つけてきた情報を投稿していく。

このテーマについて、もっと深く掘り下げる場所を作っておきたいということであれば、

みたいな感じで、発言内容から新しいチャットルーム(ここではコトノマと呼んでいる)を作る。このコトノマが、会話に参加している人たちにとっての百科事典の項目になる。

さらに何日か経って、

ここで キラーン✧ とひらめくTakeo氏、

その勢いで新しいコトノマを作る。

これはあくまでも例なので内容については深く考えないで頂きたい。

「チャットでウィキペディアをつくる」ということの雰囲気は感じて頂けただろうか? こんな感じで、発想にリミットがかからない普段の会話からコンセプトをすくい上げて辞書化するというのが、Cotoami の一つの活用例である。

予めこういうことをやろうとか、こういう情報が必要だろうという予断を持つよりも、興味のまま、話を面白そうな方向へ転がす時に出てくるキーワードを拾い上げたり、それらの間にネットワークを作ったりすることによって、本当に自分たちが必要とするオリジナルなアイデアを生み出す。それが「チャットでウィキペディアをつくる」という実践である。

Cotoami コンセプト考: 発想を支援するツール

仕事の合間に Cotoami というシステムを開発している。

Cotoami は、それ以前に開発していた Oinker というシステムの後継に当たる。

これはこれで便利に使っていたのだけど(例えば、この「ゆびてく」の記事の多くは、一度 Oinker で素材を作ってから書いている)、どうしても身内に閉じてしまいがちなので、さらに色々な展開を望むならオープンに開発した方が良いのでは? という考えに至った。

そのような経緯で、Oinker をオープンソースとして一から作り直そう、ということで始めたのが Cotoami プロジェクトである。

2017年5月現在、完成にはまだ程遠い状態であるが、中途半端な状態でもとりあえずサービスとして公開して、興味を持って頂いた方々からのフィードバックを募っている。

断続的に開発して5ヶ月程経ったが、すでに Oinker とは大分異なるシステムになってきている。最終的にどのようなシステムにするかもぼんやりとしか決めておらず、思いついたアイデアを Twitter に投げて、それに対していろんな意見や提案を頂いて考え直したりと、そのようなプロセスを経て、最終的には当初の想像とは全く違うシステムが出来上がれば良いなと期待している。

現段階でぼんやりとしながらも頭の中にあるアイデアを、全て俎上に載せて、色々な方々からフィードバックを頂く機会を作れないかということでこの記事を書いている。とりあえず今回は第一回ということで、中心となりそうなコンセプトについて紹介してみたい。


Cotoami で何を作ろうとしているのか? 大きな括りで言えば「発想を支援するツール」ということになると思う。

ここで注意しないといけないのは、そもそもパーソナルコンピュータやインターネット、そしてWeb自体が「発想を支援するツール」になっているという事実である。そういう基盤がすでにある上に「発想を支援するツール」をわざわざ作る意味については常に考えておかないといけない。

パーソナルコンピューティングの主題は、コンピュータを使っていかに人間の能力を高めるか(「Amplify human reach」)ということであったが、これは言い換えれば「クリエイティビティ(創造性)」の追求である。 – 「オブジェクト指向とは何だったのか?」 

もっと具体的に言えば、パソコンやスマートフォンを買えば普通に付いてくるテキストエディタやその他の基本的なツール、こういったものを押しのけてまで必要なものになり得るのかという問いである。結局テキストエディタでいいんじゃない? とならないかどうか。テキストエディタは思っている以上に柔軟で強力である。

というわけで、まずは、最も単純なツールであるテキストエディタを出発点に考えてみたい。

テキストエディタで簡単に実現できないことは何か? それはコンテンツに構造を持たせることである。インデントなどを駆使して擬似的な構造を表現することはできる。しかしそれはあくまでも見た目上の構造である。構造を構造として扱うことはできない。

ここで一つ目の要件が出てくる。

要件 1) コンテンツに構造を持たせる

複雑な発想を表現するためには、コンテンツに構造を導入する必要がある。そのような構造をサポートするツールで普及しているものと言えば、最も基本的なところでファイルシステム(ファイルシステムを駆使すればテキストエディタでもある程度構造を扱えるようになる)、そしてアウトライナーやマインドマップ、さらには Web 上での共同作業を支援するあらゆるツールの基礎となっている WikiWikiWeb がある。

これらのツールが採用する構造のタイプに目を向けると、大体以下の二種類に分かれる。

  1. ツリー型: ファイルシステム、アウトライナー、マインドマップ
  2. ネットワーク型: WikiWikiWeb

ツリーとネットワークを比較した時に最も分かりやすい違いは、ある一つのコンテンツがたった一つの場所に所属するか、あるいは複数の場所に所属するかという違いである。ツリー型だと一つのコンテンツは一つの場所にしか所属させることができない。つまり、コンテンツの文脈は常に一つになるが、ネットワーク型だと一つのコンテンツを複数の文脈の中に配置することができる。

アウトライナーやマインドマップのようなツールは、一つのテーマ(文脈)を出発点に、そこから連想したり知識を細分化したりするのが基本になる。一つのテーマを俯瞰したり、まとめたりするのには便利なのだが、発想を広げようとするとツリー構造が制限になる。

ツリー型だと知識の俯瞰はしやすいが、本来の知識が持つ多様な文脈を表現できない。ネットワーク型だと知識の全体像を掴むのは難しくなるが、複数のテーマを横断するような知識の表現が可能になる。

Cotoami としては、「発想を広げる」という観点から、一つのコンテンツをいろんな文脈に置くことのできるネットワーク型を選択したい。

要件 2) コンテンツはネットワークを構成する

そして、ここまでに挙げたツールの多くが個人向けのツールである。より発想を広げたいと考えた時、やはり他の人と何らかの形で連携できた方が良さそうである。それも少人数で流動性の低いチームだけでなく、不特定多数の人とコラボレーションできればもっと発想は広がるだろう。

要件 3) 個人から、複数人のチーム、さらには不特定多数の人とのコラボレーションをサポートする

ここまでに挙げたツールの中で、これら三つの要件を満たすのは WikiWikiWeb だけである。この WikiWikiWeb が、Cotoami から見たときの重要な参照点になる。

WikiWikiWeb は、プロダクトそのものよりも、コンセプト自体が永遠に生き残るような「発想を支援するツール」の傑作だと思う。Cotoami としても、結果としてそのようなコンセプトを残せればと思うのだけど、それはちょっと高望みし過ぎかもしれない。

さて、「発想を支援する」という観点から考えた場合、この WikiWikiWeb に足りないものは何だろうか?

それは、どんな小さなアイデアでも気軽に投稿できるような仕組みがない、ということではないだろうか。

思いついたことを気軽に書き込める Inbox 的な Wiki ページを用意すれば良いのではないかと思われるかもしれない。しかし、Wiki の場合、一つのページ内に書き込まれた内容はテキストエディタと同様に見た目上の構造しか持たせることが出来ない。ページとして登録するにしても、思いつきの断片でわざわざページを作りたくないというケースも多いだろう。ユーザーインタフェース的な問題もあって、投稿障壁が最小限になっているとは言い難い部分もある。

発想の可能性を広げるためには、投稿障壁を最小限にして材料となる情報を出来るだけたくさん集めること、そして、どんな小さなアイデアでも構造の単位としてネットワークを構成できるように、そしてそれを柔軟に操作できるようにしたい。というわけで、以下の二つの要件が導き出される。

要件 4) どんな小さなアイデアでも気軽に投稿できる(投稿障壁を最小限にする)

要件 5) どんな小さなアイデアでも構造の単位となれる

Cotoami では、投稿障壁を最小限にするために、コンテンツの入力部分はチャットアプリと同じ形になっている。

Twitter が情報発信の障壁を劇的に下げて、それ以前までは考えられなかったような発言を利用者から引き出したように、情報の入力を Twitter や、さらに障壁が低いと考えられるチャットの形式にすることによって、どんな些細な情報でも発想の材料として利用できるようにしたい。

投稿障壁をチャットのレベルまで下げると、発想の材料としては使えないようなノイズも多く含まれるようになる。そこで、以下のような要件が出てくるだろう。

要件 6) ネットワークに参加させるコンテンツを取捨選択できる

さて、この辺からいよいよ Cotoami の核心に近づく。

チャット形式で入力されたアイデアは、思いつきで投稿されるため、その内容は比較的ランダムになりやすい。特に複数人で会話をしているようなケースだとそのような傾向が強くなるだろう。ある程度テーマを決めて話していても、その周辺となるような話題に足を踏み入れたりすることも少なくないはずである。あるいは個人で利用しているケースでも、思いついたことは何でも記録するという形で利用していれば、そこに様々なテーマが現れてくるはずだ。それらのテーマを事後的に発見して、構造を立ち上げていけるような仕組みが欲しい。

要件 7) コンテンツの中から事後的にテーマを発見して構造を立ち上げていける

ここが既存の発想支援ツールと一線を画すところだと言えるかもしれない。アウトライナー、マインドマップ、そして WikiWikiWeb も同様、基本的には「連想」をベースにした発想を支援するツールである。ツリーの場合、その連想はどちらかと言えば、上から下というように細分化する方向に向かう。ネットワークの場合は、より自由な連想を可能にするが、隣接する領域にしか発想が伸ばせないという意味では同じである。

ここで Cotoami として考えていることは、一見相互に関係ないと思われるコンテンツの中につながりを発見して、それを新しい発想の拠点とすることである。

具体的にどのように実現するか? 以下の図を見て欲しい。

まず、チャット形式で投稿される小さなコンテンツ、これを Cotoami では Coto(コト) と呼んでいる。その Coto は、Cotonoma(コトノマ) という、チャットルーム的な場所で投稿される。「Stage 1 – Posting to timeline」の部分は、まずは Cotonoma を作って、そこに Coto を投稿して行く(チャットする)という段階を表している。

そうして集めたランダムな Coto の中につながりを探して、そのつながりの中心となるような Coto を選び(あるいはつながりを表す名前を新たに Coto として投稿して)、そこから関係する Coto に向かってリンクを作成する。これが二段階目の「Stage 2 – Connecting」である。

そのようにつながりを作って行くと、Cotonoma の中にいくつものグループが出来上がってくる。そして、その中からこれは重要だと思えるものが出てくるはずである。その重要だと思われるグループの中心となっている Coto を Cotonoma に変換することによって、その新しく生まれたテーマを、専用の場所で掘り下げて行くことができる。これが「Stage 3 – Convert a category coto into a cotonoma」である。

その後はまた最初のステージに戻って同じ流れを繰り返して行く。このようにして生まれた Cotonoma(テーマ)のリストは、発想のプロセスを開始する前に想定していたものとは異なるものになっている可能性が高い。これを「発想」の肝として考えるのが Cotoami のコンセプトである。


というわけで、今回は Cotoami の中心となるコンセプトについて書いてみた。第一回ということで、比較的長い間維持されそうな核となるコンセプトに絞ったのだが、ツッコミどころは色々とありそうな予感もある。あるいは、これらのコンセプトからどのような展開があり得るのかということも開発を進める上で明らかになってくると思う。このような話題、あるいはツールに興味のある方は、是非是非 Cotoami プロジェクトをウォッチして頂いて、思いついたことがあったら何でも、Twitter やコメント欄などで気軽に話しかけて頂けたら幸いである。

Cotoami成長記録 (5) – リアルタイムチャット

ようやくチャットが出来るようになりましたー(ウハウハ)。

他のメンバーが同じコトノマに接続していれば、投稿の内容をリアルタイムに共有出来ます。

上の動画にあるように、チャット中に新しい話題に移りたくなった場合は、新しいコトノマをタイムラインに投稿してメンバーをそちらに誘導する、なんてことも出来るようになりました。

メンバーがコトノマに接続しているかどうかは、以下のように、表示の濃淡で分かるようになっています。

チャットが出来て、入れ子にすることが可能なコトノマ(部屋)による情報の整理だけでも、結構色々なことが可能になるような感じもありますが、コトやコトノマの編集がまだ出来ないし、さらには Oinker 最大の機能である「つながりを作る機能」もないので Cotoami の全貌はまだ見えていません。


Cotoami の最新バージョンは https://cotoa.me/ で試験運用中。

Cotoami のロードマップ: https://github.com/cotoami/cotoami/issues/2

細かな更新情報は Twitter でつぶやいています: https://twitter.com/cotoami

ソースコードはこちら: https://github.com/cotoami/cotoami

@__tai2__さんによるCotoami開発日誌: https://cotoami-dev.tumblr.com/

Cotoami成長記録 (4) – コトノマの共有

次の大きなヤマはチャット機能。今月中に実現できるよう頑張りたいと思います。

出来ませんでした _| ̄|○

チャットに必要になるコトノマの共有機能と、自分が参加しているコトノマが分かるように、コトノマ一覧機能を作ったところで力尽きました…

というわけで、現状の Cotoami は、コトノマの中身はシェアできるけど、内容はリアルタイムに更新されない、言わば掲示板レベルのハリボテです。

コトノマには以下のような感じでメンバーを設定できるようになりました(サインアップしてない人への招待機能は未実装)。

cotonoma-members

コトノマ一覧は、以下のような感じで端末によってレスポンシブに表示されます。

PC(左側に表示):

cotonomas-pc

スマホ(ヘッダーからプルダウン):

cotonomas-mobile

一覧の内容は現在地のコトノマ内で作られたコトノマのみに絞り込まれます(ホームではアクセス可能な全てのコトノマを表示)。

来月の今頃にはチャットでウハウハになっていることを願って…

Cotoami成長記録 (5) – リアルタイムチャット


Cotoami の最新バージョンは https://cotoa.me/ で試験運用中。

Cotoami のロードマップ: https://github.com/cotoami/cotoami/issues/2

細かな更新情報は Twitter でつぶやいています: https://twitter.com/cotoami

ソースコードはこちら: https://github.com/cotoami/cotoami

@__tai2__さんによるCotoami開発日誌: https://cotoami-dev.tumblr.com/

Cotoami成長記録 (3) – コトとコトノマ

今回は重要な機能を追加しました。その名も「コトノマ」機能です。

Cotoami では、投稿される情報の単位を「コト」と呼んでいます。その「コト」をグルーピングするものが「コトノマ」です。ファイルシステムのフォルダやディレクトリ、チャットのルーム(チャット機能はまだありませんが)などに相当します。

cotonoma

画面左上の(+)ボタンをクリック(タップ)して、コトノマを作ると、コトと同じようにタイムライン上に投稿されます。投稿されたリンクをクリックすると、そのコトノマに移動します。コトノマの中にまた別のコトノマを作ることも出来ます。

コトと同じように、コトノマも扱うことが出来る。ここが重要な部分です。現状の機能だとまだ有り難みが少ないですが、将来的にはアイデアを生み出す際の力となるはずです。

コトノマからホーム画面に戻ってくると、他のコトノマに投稿したコトやコトノマも含めて、自分が投稿したもの全てが表示されるようになっています。

さて、次の大きなヤマはチャット機能。今月中に実現できるよう頑張りたいと思います。お楽しみに〜。

Cotoami成長記録 (4) – コトノマの共有


Cotoami は誰でもダウンロードして試せるオープンソースプロジェクトですが、 https://cotoa.me/ で最新バージョンの運用もしています。興味のある方は是非是非お試し下さい。

細かな更新情報は Twitter でつぶやいています: https://twitter.com/cotoami

ソースコードはこちら: https://github.com/cotoami/cotoami

Elm沼とKubernetes沼で交互に溺れているうちに1月が終わっていた

ElmCotoami のユーザーインタフェースを作るために選択したプログラミング言語で、そのデザインは JavaScript 界で一世を風靡している Redux の原型になったと言われている。その原型を体験してみたいという単純な好奇心だけで Elm を選択し、分かりやすいチュートリアルに感心したのも束の間、実際にアプリケーションを作り始めると、入り口ではあんなに優しかった Elm の顔がみるみる般若のようになり、気がついたら底なしの泥沼に足を取られていた。

Elm のような、いわゆる純粋な関数型と呼ばれる環境では、言語からコンピューターを直接操作することが出来ない。出来るのはデータの変換だけだ。この変換がいわゆる「関数」で、関数型でプログラマーに許されているのは関数を書くことだけである。しかし、コンピューターを操作出来ないと何も出来ないのでどうするのかと言うと、コンピューターへの命令をデータとして表現して、それを返す関数を作って環境に渡すみたいなことをする。

関数型にはこのような制限があるので、従来の命令型言語でプログラムを書く場合に比べて、かなり回りくどい書き方をしなければならない。しかしその代償として、プログラムは必ず「データの変換」という形に落とし込まれるので、実行時に意図しない動作をすることが格段に少なくなり、デバッグやテストも容易になるというわけだ。

関数型の回りくどさに加えて、Elm には厳密な型システムがある。実は今、開発スピードを減退させている最大の原因はこの型システムである。代数的データ型とか、今まで遭遇したことのないコンセプトに純粋な感動を覚える一方、曖昧さを許さない型システムのおかげで、型を合わせるためにどうすれば良いかというのを考えてるだけで膨大な時間が過ぎて行く。

動的型が、書く時にいかに楽をするか(少ない記述でいかに多くを実現するか)を主眼に置いているとすれば、静的型は、書いた後にいかに楽をするか(起こりうることを明示的に示しておく)ことに主眼を置く。しかし、動的型でも、曖昧に書いて楽をしつつも、危ないと思うところは慎重に書いたり、テストでフォローしたりするわけなので、単純に上のような図式が当てはまるとも思えず、どちらが良いと判断するのはなかなか難しい。動的型の言語であれば、場所によって手綱を締めたり緩めたり出来るのが、静的型だと一律で同じような書き方をしなければならない、というのがなかなか辛いところである。

まだ言語自体に慣れてないということもあるし、これから開発が進んで、規模が大きくなり、複数人で連携するようになったら関数型や型システムの恩恵を実感出来るのかもしれないなと思いつつ、最初の敷居はやっぱり高かったということだけここに記録しておく。

一方の、Kubernetes 沼の方はと言えば、こちらは Cotoami で試行錯誤した成果を仕事の方にフィードバックしたいということで、プロダクションへの導入を目指して色々と準備しているため、Elm よりもより深い深い泥沼となっている。Elm の場合はとりあえずユーザーインタフェースが動けばなんとかなるのに対して、インフラの場合はその辺のごまかしが効かないので、あらゆる側面から検証を行わなければならず、人材の不足も手伝ってかなり余裕のない状況に陥ってる今日この頃(もし、同じように Kubernetes で試行錯誤している方がいたら、情報交換したいなあと思うのですが… @marubinotto までご連絡お待ちしております(切実))。

Kubernetes は未来のインフラだとの意を強くする一方(やっと10年ぐらい前のGoogleに追いついただけとも言える)、アジャイルでない組織に導入してもメリットはあまりないだろうなとも思う。Kubernetes は単なるインフラというよりも、サービスがどのように開発・運用されるべきかという思想が強烈に埋め込まれた環境だと言った方が良いかもしれない。

Kubernetes によって、いわゆるデリバリーサイクルは極限まで短縮され、システムの柔軟性もかつてないレベルで実現出来るようになる。しかしその一方、運用やモニタリングにかかるコストは従来より高いように感じられるし、Kubernetes 自体も高速で進化していくので、そこにキャッチアップ出来る人材も確保する必要がある。例えば、運用において、MTTR (Mean-Time-To-Recovery) よりも、MTBF (Mean-Time-Between-Failures) の方を重視するような組織だと、なかなかこの機動性をメリットだと感じるのは難しいだろう。

既に開発が落ち着いていて安定運用されているサービスを Kubernetes に移行するメリットは、おそらくほとんどない。基本的には開発の初期から導入するのが望ましい。Cotoami はリスクのないオープンソース開発なので、その辺の事情で迷う必要がなく、一番始めのハリボテアプリの段階から Kubernetes で本番 ( https://cotoa.me/ ) の運用を始めて、自動デプロイなどの仕組みも整備出来た。

去年まで、ウチのチームでは、AMI (Amazon Machine Image) のように VM 上で動くホストがパッケージングの単位になっていた。パッケージを作るのも、パッケージをデプロイするのもうんざりするほど時間がかかる。それが Kubernetes/Docker によって、ホストの中で動く一つ一つのプロセスがパッケージングの単位となり、それらの小さな部品を組み合わせてホスト(Kubernetes では Pod と呼ばれている)を作り、それらをまた組み合わせて大きなWebサービスを作るという形に変わり、一度の更新も最小限で高速、しかも無停止ということで、新しい時代の到来を感じずにはいられない。

Cotoami成長記録 (2) – サインイン

ひたすら匿名で投稿するだけだったハリボテメモ帳に、サインイン機能を追加しました。

認証はメールアドレスのみで行うシンプルなもので、ユーザー登録を行う必要もありません。

signin

メールアドレスを送信すると、以下のようなサインイン用のメールが送られて来ます。

signin-mail

メールに書かれたURLを開くとサインイン完了です。

signed-in

アカウントのアイコンやユーザー名は Gravatar のものを利用しています。

サインインせずに匿名(Anonymous)のままでも書き込みを行うことが出来ますが、サインインの際に「Save the anonymous cotos (posts) into your account」のチェックボックスを ON にしておくと、匿名で投稿した内容を自分のアカウントに移すことが出来ます。

まだまだハリボテの段階を脱していませんが、これで「PCで書いたメモをスマホで見る」といったような当たり前の使い方が出来るようになりました。

Cotoami https://cotoa.me/

ソースコードはこちら: https://github.com/cotoami/cotoami/tree/signin

Cotoami成長記録 (3) – コトとコトノマ


(舞台裏)ローカル Cotoami を一瞬で作る魔法のコマンド

現段階でのシステム構成は以下のような感じになっています。

cotoami-2

アカウントごとの投稿内容(Cotoamiでは「コト(Coto)」と呼んでます)を保存するためのデータベース(PostgreSQL)や、サインインメールを送信するためのメールサーバーなどが新しい登場人物です。

既にそこそこ複雑なシステムになってきていますが、minikube というサーバーの箱庭環境があれば、驚くほど簡単にローカル Cotoami システムを構築することができます。

minikube のインストールについては以下の記事を参考にしてみて下さい。

kubectl cluster-info で準備が出来ていることを完了したら、以下のコマンドを実行するだけでシステムが出来上がります。

# 魔法のコマンド
$ kubectl create -f https://raw.githubusercontent.com/cotoami/cotoami-infra/signin/kubernetes/all-in-one.yaml

kubectl get pods コマンドで全てのサーバーが「Running」になったら準備完了。

$ kubectl get pods
NAME                           READY     STATUS    RESTARTS   AGE
cotoami-3593364574-ji2wy       1/1       Running   1          15m
maildev-2318592657-ag3ib       1/1       Running   0          15m
postgres-4226949952-8dk6q      1/1       Running   0          15m
redis-master-517881005-pqvud   1/1       Running   0          15m

WebアプリのURLを知るためには、以下のコマンドを実行します。

$ minikube service cotoami --url
http://192.168.99.101:31923

出力されたURLをブラウザで開くと、Cotoami の画面が表示されるはずです。

サインインメールは maildev というダミーのメールサーバーが受け取るようになっています。以下のコマンドで maildev の URL を取得して、

$ minikube service maildev --url
http://192.168.99.101:32261
http://192.168.99.101:30914

出力された二つの URL の内、上の URL をブラウザで開くと、

maildev

こんな感じで送信されたサインインメールを見ることができます。サインイン URL の先頭が http://cotoami となっているのを、minikube service cotoami --url で出力された URL に置き換えてブラウザで開くと、サインイン完了です。

Cotoami成長記録 (1) – ハリボテメモ帳的な何か

今月から始まった Cotoami プロジェクト。開発途上のバージョンからどんどん公開して、開発が進むに連れて変わって行く様を、逐一こちらに記録して行きたいと思います。

まずは第一歩ということで、ハリボテメモ帳的な何かから出発。

cotoami-1

Cotoami http://cotoa.me/

上のサイトにアクセスするとすぐに試せるので適当に何か書き込んでみて下さい(書き込んだ内容は本人にしか見えません)。同じブラウザでアクセスする限り、書き込んだ情報は維持されます。ハリボテなので、データが突然消えることがあるかもしれませんが…

このハリボテアプリのシステム構成は以下の通り、

cotoami-1

ハリボテとは言え、データは Redis に保存したりしてます。

この段階のコードは以下から見ることができます。

Cotoami成長記録 (2) – サインイン

Kubernetes で実現する Phoenix/Elm アプリのホットデプロイ自動化完全詳解(2016年12月版)

今年の初頭に「Phoenixアプリのホットデプロイ完全自動化」の記事を書いてから一年が過ぎようとしている。この自動化は Elixir/Erlang の Hot swapping 機能を利用していて、git push から10分以内でデプロイが完了するという、当時としてはそこそこ満足のいく達成だったのだが、こんな不具合や、exrm (Elixir Release Manager) 作者の「hot upgrades はあんまりオススメ出来ない発言」などを見るにつけ、これを本番で使うのはちょっと辛いかもしれないと思うようになった。

今回、Cotoami プロジェクト を始めるに当たって、前々から気になっていた Google の Kubernetes(クバネテス)を試してみようと思い立った。そして実際に自動化の仕組みを構築してみて、その簡単さと仕組みの先進さに驚いた。言語に依存しないマイクロサービスのパッケージングと、それらを組み合わせて簡単にスケーラブルなWebサービスを構築できる仮想環境。これで本格的にコンテナの時代が来るんだなという新しい時代の訪れを感じずにはいられない。

というわけで、以下では Kubernetes を使った自動化の詳細について紹介したいと思う。この仕組みの全貌は Cotoami プロジェクトの一部として公開しているので、興味のある方は以下の GitHub プロジェクトを覗いて頂ければと思う。



 

Kubernetes とは何か?

Kubernetes が提供する仕組みは Container Orchestration と呼ばれている。Container Orchestration とは、Docker のようなコンテナ(アプリケーションを実行環境ごとパッケージングする仕組み)で実現されている小さなサービス(マイクロサービス)を組み合わせて、より大きなサービスを作るための仕組みである。

今では、Webサービスを複数のサービス(プロセス)の連携として実現することが当たり前になって来ている。次第に細かくなりつつあるこれらのサービスを扱う時の最大の障害が従来型の重い仮想化だ。例えば、Amazon Machine Images (AMI) のような従来型の仮想化技術を使ってサービスを更新する場合、イメージをビルドするのに20分から30分程度、更にそれを環境にデプロイするのに10分以上かかってしまう。自動化も容易ではない。サービスの数が多くなるほどに時間的なペナルティが積み重なってしまい、マイクロサービスのメリットを享受するのは難しくなる。なので、実際はマシンイメージをデプロイの単位にすることはせずに、言語やフレームワーク固有のパッケージに頼ったデプロイを行っている現場が多いのではないだろうか。

これらの問題を一挙に解決しようとするのが、Docker のような軽い仮想化と、それらをまるでソフトウェアモジュールのように組み合わせることを可能にする Container Orchestration 技術である。

 

Kubernetes を最短で試す

複数サービスの連携を、ローカルマシンで簡単に試せるというのも Kubernetes のようなツールの魅力だ。Kubernetes には Minikube というスグレモノのツールが用意されていて、ローカルマシン上に、お手軽に Kubernetes 環境を立ち上げることが出来る。

以下では、Mac OS X での手順を紹介する。

1. VirtualBox をインストールする

筆者の環境:

$ vboxmanage --version
5.1.8r111374

2. Minikube をインストールする

$ curl -Lo minikube https://storage.googleapis.com/minikube/releases/v0.12.2/minikube-darwin-amd64 && chmod +x minikube && sudo mv minikube /usr/local/bin/

$ minikube version
minikube version: v0.12.2

3. Kubernetes を操作するためのコマンドツール kubectl をインストールする

$ curl -Lo kubectl https://storage.googleapis.com/kubernetes-release/release/v1.3.0/bin/darwin/amd64/kubectl && chmod +x kubectl && sudo mv kubectl /usr/local/bin/

4. Minikube を起動する

$ minikube start
Starting local Kubernetes cluster...
Kubectl is now configured to use the cluster.

以下のような情報を見れれば、準備は完了。

$ kubectl cluster-info
Kubernetes master is running at https://192.168.99.101:8443
KubeDNS is running at https://192.168.99.101:8443/api/v1/proxy/namespaces/kube-system/services/kube-dns
kubernetes-dashboard is running at https://192.168.99.101:8443/api/v1/proxy/namespaces/kube-system/services/kubernetes-dashboard

$ kubectl get nodes
NAME       STATUS    AGE
minikube   Ready     5d

5. サンプルプロジェクトをデプロイしてみる

Kubernetes には色んなサンプルプロジェクトが用意されているが、ここでは Guestbook という簡単なアプリを試してみる。

以下のファイル(guestbook-all-in-one.yaml)を適当な場所に保存して、

https://github.com/kubernetes/kubernetes/blob/master/examples/guestbook/all-in-one/guestbook-all-in-one.yaml

以下のコマンドを実行してデプロイする。

$ kubectl create -f guestbook-all-in-one.yaml 
service "redis-master" created
deployment "redis-master" created
service "redis-slave" created
deployment "redis-slave" created
service "frontend" created
deployment "frontend" created

これによって、以下の3つの Deployments と、

$ kubectl get deployments
NAME           DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
frontend       3         3         3            3           5m
redis-master   1         1         1            1           5m
redis-slave    2         2         2            2           5m

それぞれの Deployments に対応する3つの Serviceskubernetesはシステムのサービスなので除く)が出来上がっていることが分かる。

$ kubectl get services
NAME           CLUSTER-IP   EXTERNAL-IP   PORT(S)    AGE
frontend       10.0.0.118   <none>        80/TCP     7m
kubernetes     10.0.0.1     <none>        443/TCP    6d
redis-master   10.0.0.215   <none>        6379/TCP   7m
redis-slave    10.0.0.202   <none>        6379/TCP   7m

簡単に説明すると、Deployment は一つのマイクロサービスのクラスタに対応し、Service はそのクラスタへのアクセス手段を提供する。

たったこれだけの手順で、冗長化された Redis をバックエンドにした、アプリケーションの環境が出来上がってしまった。構成の全ては guestbook-all-in-one.yaml というテキストファイルに定義されている。

早速ブラウザでアクセスして試してみたいところだが、デフォルトの設定だとサービスが Kubernetes の外部には公開されていないので、frontendサービスの設定をちょっと書き換えて(guestbook-all-in-one.yaml に以下のような感じで type: NodePort の行を追加する)、

apiVersion: v1
kind: Service
metadata:
  name: frontend
  labels:
    app: guestbook
    tier: frontend
spec:
  ports:
  - port: 80
  type: NodePort
  selector:
    app: guestbook
    tier: frontend

以下のコマンドを実行して設定ファイルの更新を環境に適用する。

$ kubectl apply -f guestbook-all-in-one.yaml 

更新が完了したら、以下のコマンドでアプリケーションのURLを知ることが出来る。

$ minikube service frontend --url
http://192.168.99.101:31749

以下のようなページが表示されただろうか?

guestbook

6. お片づけ

先ほどのサンプルプロジェクトで作ったリソースは、以下のコマンドで全部削除出来る。

$ kubectl delete -f guestbook-all-in-one.yaml

Minikube の停止は以下。

$ minikube stop

 

Phoenix/Elm アプリの Docker イメージを作る

さて、Cotoami の話に戻ろう。Cotoami では、以下のような構成で自動化を実現しようとしている。

cotoami-auto-deploy

CircleCI 上のビルドで Docker イメージをビルドして Docker Hub にリリース。その後、AWS上に構築した Kubernetes に更新の命令を出して、新しいイメージでアプリケーションの Rolling Update(無停止デプロイ)を行う。

この仕組みを構築するためには、まず Phoenix/Elm アプリケーションを Docker でパッケージングするための Dockerfile を用意する必要がある。しかし、ここで気をつけなければならないのは、パッケージングそのものよりも、CircleCI 上でどうやって Phoenix/Elm アプリケーションをビルドするかという問題である。

Elixirアプリケーションは、クロスコンパイル・ビルドが出来るという説明もあるが、実行環境とビルド環境は合わせておいた方が良いというアドバイスもよく見かけるので、Cotoami ではよりトラブルが少なそうな、環境を合わせるアプローチを取ることにした。

今回の例では、実行環境も Docker 上になるので、まずビルド用の Docker イメージを用意しておき、それを使ってアプリケーションのコンパイルとテストを行い、その後、そのイメージをベースにしてアプリケーションをパッケージングするという、docker build の二段構え方式でビルドを実施する。

まずは、以下の Dockerfile で Phoenix/Elm アプリのビルド環境を作る。

一度作ったイメージは、CircleCI のキャッシュディレクトリに入れておき、後々のビルドで使い回せるようにしておく。この辺の設定は全て circle.yml に書く。

アプリケーションのコンパイルとテストが終わったら、ビルド用のイメージをベースにして、アプリケーションのパッケージングを行う。そのための Dockerfile が以下である。

これらの組み合わせで、git push する度に、Docker Hub にアプリケーションのイメージがリリースされるようになる(Docker Hub に docker push するために、CircleCI に 認証用の環境変数を設定しておくこと: DOCKER_EMAIL, DOCKER_USER, DOCKER_PASS)。

参考: Continuous Integration and Delivery with Docker – CircleCI

 

AWS上に Kubernetes 環境を作る

アプリケーションの Docker イメージが用意出来たら、それを動かすための Kubernetes 環境を作る。今回は AWS 上に Kubernetes 環境を構築することにした。

Kubernetes から kops という、これまた便利なツールが提供されていて、これを使うと簡単に環境を構築出来る。

1. kops のインストール

Mac OS の場合:

$ wget https://github.com/kubernetes/kops/releases/download/v1.4.1/kops-darwin-amd64
$ chmod +x kops-darwin-amd64
$ mv kops-darwin-amd64 /usr/local/bin/kops

2. Kubernetes 用のドメイン名を用意する

ここが比較的厄介なステップなのだが、kops による Kubernetes 環境はドメイン名を名前空間として利用する仕組みになっている。具体的には、Route 53 内に Kubernetes 環境用の Hosted zone を作る必要がある。

例えば、立ち上げようとしているWebサービスのドメインが example.com だとすれば、k8s.example.com のような専用の Hosted zone を用意する(k8s は Kubernetes の略称)。

Cotoami の場合、AWS のリソースは出来るだけ Terraform を利用して管理することにしているので、Terraform で Hosted zone を設定する際の例を以下に置いておく。

resource "aws_route53_zone" "main" {
  name = "example.com"
}

resource "aws_route53_zone" "k8s" {
  name = "k8s.example.com"
}

resource "aws_route53_record" "main_k8s_ns" {
  zone_id = "${aws_route53_zone.main.zone_id}"
  name = "k8s.example.com"
  type = "NS"
  ttl = "30"
  records = [
    "${aws_route53_zone.k8s.name_servers.0}",
    "${aws_route53_zone.k8s.name_servers.1}",
    "${aws_route53_zone.k8s.name_servers.2}",
    "${aws_route53_zone.k8s.name_servers.3}"
  ]
}

主ドメインとなる example.com の Hosted zone について、サブドメイン k8s の問い合わせを委譲するような NS レコードを登録しておくのが味噌。

以下のコマンドを叩いて、DNSの設定がうまく行っているかを確認する。

$ dig NS k8s.example.com

上で設定した4つの NS レコードが見えれば OK。

3. kops の設定を保存するための S3 bucket を作る

kops は、Amazon S3 上に保存された構成情報に基づいて環境の構築・更新などを行う。というわけで、予めそのための S3 bucket を作っておき、その場所を環境変数 KOPS_STATE_STORE に設定する。

$ aws s3 mb s3://kops-state.example.com
$ export KOPS_STATE_STORE=s3://kops-state.example.com

これで、準備は完了。いよいよ Kubernetes の環境を立ち上げる。

4. Kubernetes の設定を生成する

新しい環境の名前を staging.k8s.example.com として、以下のコマンドで新規環境の設定を生成する。生成された設定は先ほどの S3 bucket に保存される。

$ kops create cluster --ssh-public-key=/path/to/your-ssh-key.pub --zones=ap-northeast-1a,ap-northeast-1c staging.k8s.example.com

Kubernetes ノードにログインするための ssh キーや、ノードを展開する Availability Zone などを指定する。細かいオプションについては、以下を参照のこと。

デフォルトでは、以下のような構成の環境が立ち上がるようになっている。

  • master (m3.medium)
  • node (t2.medium * 2)

5. Kubernetes 環境を立ち上げる

Kubernetes 環境を AWS 上に立ち上げる。単純に以下のコマンドを実行すれば良いのだが、

$ kops update cluster staging.k8s.example.com --yes

Terraform の設定ファイルを生成するオプションもあるので、Cotoami ではその方法を取ることにした。

$ kops update cluster staging.k8s.example.com --target=terraform

$ cd out/terraform
$ terraform plan
$ terraform apply

生成されたデフォルトの構成から、セキュリティグループなどをより安全な設定にカスタマイズすることもあると思われるが、これらのファイルは自動生成によって更新される可能性があることに注意する必要がある。ファイルを直接編集すると、新しく生成したファイルに同じ変更を施すのを忘れてしまう可能性が高い。なので、AWS のコンソール上で直接カスタマイズした方が良いかもしれない(新しい設定ファイルとの齟齬は terraform plan の時に気づける)。

どのようなファイルが生成されるか興味のある方は、Cotoami のリポジトリを覗いてみて欲しい。

環境を立ち上げる過程で、kop によって kubectl の設定も自動的に追加されている。以下のコマンドを実行すれば、AWS上の環境に接続していることが確認できるはずだ。

$ kubectl cluster-info
Kubernetes master is running at https://api.staging.k8s.example.com
KubeDNS is running at https://api.staging.k8s.example.com/api/v1/proxy/namespaces/kube-system/services/kube-dns

 

Kubernetes 上にアプリケーションをデプロイする

Kubernetes の準備は整ったので、後はアプリケーションをデプロイするだけである。Minikube のところでサンプルアプリをデプロイしたのと同じように、サービスの構成情報を YAML ファイルに定義しておき、kubectl create コマンドでデプロイを行う。

Cotoami の構成ファイルは以下に置いてある。

$ kubectl create -f deployment.yaml
$ kubectl create -f service.yaml

設定ファイルの仕様については Kubernetes のサイトを参照して頂くとして、内容自体は単純だということはお分かり頂けると思う。deployment.yaml では、アプリケーションの Docker イメージ名やクラスタを構成するレプリカの数、ポート番号などが指定されている。service.yaml では、そのサービスを外部にどのように公開するかという設定がされており、面白いのは type: LoadBalancer と書いておくと、AWS の ELB が自動的に作成されてアプリケーションのエンドポイントになるところだろうか。

 

デプロイ自動化をビルド設定に組み込む

最初のデプロイが無事に成功すれば、無停止更新の仕組みは Kubernetes 上に用意されている。後はそれを利用するだけである。

CircleCI から Kubernetes にアクセスするためには、以下のような準備が必要になる。

  1. kubectl のインストール
  2. kubectl の設定
    • ensure-kubectl.sh では、環境変数 S3_KUBE_CONF に設定された Amazon S3 のパスから kubectl の設定ファイルをビルド環境にコピーする。
    • Kubernetes on AWS を構築する過程でローカルに出来上がった設定ファイル ~/.kube/config を S3 にコピーして、その場所を CircleCI の環境変数 S3_KUBE_CONF に設定する。
      • この設定ファイルには、Kubernetes にアクセスするための credential など、重要な情報が含まれているので、取り扱いには注意すること!
    • CircleCI 側から S3 にアクセスするためのユーザーを IAM で作成して最低限の権限を与え、その credential を CircleCI の AWS Permissions に設定する。

これらの設定が完了すれば、ビルド中に kubectl コマンドを呼び出せるようになる。Cotoami の場合は、circle.ymldeployment セクションに、以下の二行を追加するだけで自動デプロイが行われるようになった。

https://github.com/cotoami/cotoami/blob/auto-deployment/circle.yml

- ~/.kube/kubectl config use-context tokyo.k8s.cotoa.me
- ~/.kube/kubectl set image deployment/cotoami cotoami=cotoami/cotoami:$CIRCLE_SHA1

長くなってしまったが、以上が自動化の全貌である。

Elixir/Phoenix と Elm による関数型 Web 開発環境の構築

前回は、Cotoami のアーキテクチャについて、コレオグラフィ型を採用するという話を書いた。しかし、開発の最初からコレオグラフィを前提にした構成にするのはスモールスタートとは言い難いので、まずは核となるWebアプリケーションを作るところから初めて、徐々にイベント駆動の箇所を増やしてく感じで進めたい。

このWebアプリケーションを実装する環境として選んだのが、Phoenix FrameworkElm である。両方とも関数型の言語なので、Webアプリケーション全体を関数型の枠組みで実装することになる。

Elixirの強みについてはゆびてくで何度か触れているのでここでは割愛するが、Elm を選択したのは何故だろうか?

大きな要因としては、Elmアプリのアーキテクチャを参考にデザインされたという JavaScript のライブラリ Redux での開発経験が挙げられる。その過程で、複雑化するフロントエンドを実装する技術として、全てのビジネスロジックを「変換の連鎖」へと落とし込む関数型の有効性を実感した(参考: 関数型つまみ食い: 関数型とはプログラミング言語ではなく、プログラムデザインの問題であることに気づく | ゆびてく)。Elm の場合は、Redux では冗長になりがちだったこの仕組みを簡潔に表現出来る上に、Static Typing があるというのも大きなアドバンテージだと考えた。

エディタ上で即座にフィードバックを受けることが出来る
プログラムの誤りについて、エディタ上で即座にフィードバックを受けることが出来る

Phoenix と Elm の相性については、最近 Elm 側で Phoenix のサポートが入ったというのが明るい材料ではあるが… こればかりは試してみないと分からない。


[2016/12/09追記]

素晴らしいツッコミを頂く。


 
以下に Phoenix/Elmアプリケーションのひな形を作るまでの手順をまとめてみた。

 

関連ツールのインストール

Node.js

以下を参考に nvm をインストールする。

creationix/nvm: Node Version Manager – Simple bash script to manage multiple active node.js versions

  • Phoenixのサイトに「Phoenix requires version 5.0.0 or greater.」とある。

筆者の環境:

$ node -v
v5.4.1

 

Elixir

Installing Elixir - Elixir

Mac OS X で Homebrew を利用している場合。

$ brew update
$ brew install elixir

筆者の環境:

$ elixir -v
Erlang/OTP 19 [erts-8.0.2]  [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]

Elixir 1.3.4

 

PostgreSQL

標準構成の Phoenix が利用するデータベース。環境によってパッケージも様々なのでインストール方法については割愛。データベースを使わないのであれば省略可。

以下のコマンドでデータベース一覧が取得出来ればデータベースのスタンバイは出来ている。

$ psql -l

筆者の環境:

# SELECT version();
                                                              version                                                              
-----------------------------------------------------------------------------------------------------------------------------------
 PostgreSQL 9.4.0 on x86_64-apple-darwin13.4.0, compiled by Apple LLVM version 6.0 (clang-600.0.56) (based on LLVM 3.5svn), 64-bit
(1 row)

 

Phoenix

Installation · Phoenix

$ mix archive.install https://github.com/phoenixframework/archives/raw/master/phoenix_new.ez

筆者の環境:

$ mix phoenix.new -v
Phoenix v1.2.1

 

Elm

インストーラが用意されているので簡単。

Install · An Introduction to Elm

筆者の環境:

$ elm -v
0.18.0

 

Phoenix/Elm アプリケーションを作る

Phoenixアプリのひな形を作る

$ mix phoenix.new cotoami

依存関係の取得とデータベースの作成。

$ cd cotoami
$ mix deps.get
$ mix ecto.create   # PostgreSQLを使わなければ省略可
$ npm install

アプリを起動してブラウザでチェックしてみる。

$ mix phoenix.server

http://localhost:4000 にアクセスすると「Welcome to Phoenix!」のページが表示される。

 

elm-brunch をセットアップする

Phoenix に標準で付いてくる Brunch というJavaScriptのビルドツールがあるのだが、elm-brunch という Elm をビルドするための拡張があるのでそれをインストールする。

$ npm install --save-dev elm-brunch

brunch-config.js に elm-brunch の設定を追加。以下の二カ所を修正。

  1)
    ...
    watched: [
      "web/static",
      "test/static",
      "web/elm"
    ],
    ...

  2)
  ...
  plugins: {
    elmBrunch: {
      elmFolder: "web/elm",
      mainModules: ["App.elm"],
      outputFolder: "../static/vendor"
    },
    babel: {
      // Do not use ES6 compiler in vendor code
      ignore: [/web\/static\/vendor/]
    }
  },
  ...

 

Elmアプリのひな形を作る

$ mkdir web/elm && touch web/elm/App.elm
$ cd web/elm
$ elm package install elm-lang/html

App.elm の内容を以下のように編集。

module App exposing (..)

import Html exposing (Html, text)

main : Html msg
main =
  text "Hello Cotoami!"

 

ElmアプリをPhoenixアプリに配置する

Phoenixアプリのファイルをそれぞれ以下のように編集。

web/templates/layout/app.html.eex


<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="">
<meta name="author" content="">
<title>Hello Cotoami!</title>
<link rel="stylesheet" href="<%= static_path(@conn, "/css/app.css") %>">
</head>
<body>
<div class="container">
<main role="main">
<%= render @view_module, @view_template, assigns %>
</main>
</div>
<script src="<%= static_path(@conn, "/js/app.js") %>"></script>
</body>
</html>

view raw

app.html.eex

hosted with ❤ by GitHub

web/templates/page/index.html.eex

<div id="elm-container"></div>

web/static/js/app.js に以下の二行を追記:

const elmDiv = document.querySelector("#elm-container")
const elmApp = Elm.App.embed(elmDiv)

これで準備は完了。ブラウザをリロードすると「Hello Cotoami!」と表示される。さらには、App.elm の内容を編集して保存すると、ブラウザが自動的にリロードされて即座に変更を確認出来るようになっているはずだ。

参考: Setting up Elm with Phoenix – Medium

コレオグラフィ: 開発者が参加・離脱しやすいアーキテクチャを考える

Cotoami のアーキテクチャを、どのような体制、あるいは場を作って開発したいのかという観点から考えてみたい。いわゆるコンウェイの法則に沿ったシステムデザインである。

現状のシステム、つまり Oinker.me のアーキテクチャは以下のようになっている。

oinker

Grailsフレームワークを利用して実装されたアプリケーションサーバーが中心となり各種サービスの連携を計る、いわゆるオーケストレーション (orchestration) モデルのアーキテクチャである。

マイクロサービス本でも指摘されているように、オーケストレーションは素直なアーキテクチャではあるが、システムを拡張する度に指揮者となるサービスに手を入れる必要がある。例えば、現状 Oinker の検索エンジンは壊れているのだが、この修正を誰かに頼みたいと思ったら、まず指揮者となるサービス(Grailsアプリケーション)について理解してもらう必要がある(「え、今更Groovyを勉強しないといけないの?」)。これは指揮者が肥大化すればするほど骨の折れる作業になる。

多くの組織で開発言語を統一しようとするのは、おそらくこのオーケストレーションの仕組みに起因している。メンバーが同じ言語や環境に習熟していないと共同開発が難しくなるのである。

しかし、新しいものを生み出そうとする場合、様々なレベルにおいて多様性を損なう事は長期視点で考えるとマイナスになると筆者は考える。システムデザインにおいて「一貫性」は抗い難い誘惑だ。しかし、システムや組織が進化するためには、多少のいびつさを許容しなければならない。そのいびつさを許容する仕組みが、最近耳にする事が多くなって来た「コレオグラフィ (choreography)」モデルのアーキテクチャにあるのではないかと考えている。

ThoughtWorksのサイトに、オーケストレーションとコレオグラフィの違いを表す図が紹介されている。

オーケストレーション:

Dependency graph in a real world orchestrated microservice project
Dependency graph in a real world orchestrated microservice project

コレオグラフィ:

Dependency graph illustrating the concept of a fully choreographed set of microservices
Dependency graph illustrating the concept of a fully choreographed set of microservices

コレオグラフィアーキテクチャによって、システムを構成する各サービスの連携が疎結合に保たれ、かつ、非同期の連携なので全てのサービスが同じ負荷に耐えなくても良いというおまけも付いてくる。システムに新たな機能を追加する場合に参照するのは、指揮者となるサービスではなく、システムの拡張点となるイベントである。追加機能の起点となるイベントだけに着目すれば良いので、イベントを受け取れる限り、どのような言語・環境でサービスを開発しても良い。既存システムを変更せずに機能を追加出来るのはもちろん、いらなくなった機能を捨てたり、新しい言語で実装し直したり、ということも容易になる。

と、ここまではあくまでWebや書籍でかじった知識で書いている机上の空論に過ぎないので、この考えがどれぐらい有効なのかはまだ分からない。Cotoamiプロジェクト上で検証して行きたいと思う。

今のところの感覚では、イベント駆動という連携が、RESTのような従来型の連携に比べて複雑なのは明らかで、だからこそ、コレオグラフィは同じ言語で実装された統一的なプラットフォームの上で実現するという流れが強いように感じる。だけども、それではサービスの多様性を実現するための疎結合という理想からは離れてしまう。おそらくこの辺にコレオグラフィの辛みがあるのかもしれない。

あまり意識されないかもしれないが、言語やプラットフォームを統一するのは、それはそれである種の密結合である。今は目眩を覚えるほどの多様性が言語やフレームワークにある時代である。その多様性の中で独自性を得ようとするエンジニアの力を借りるために、この密結合はデメリットとして働くのではないかと今のところは考えている。

Cotoami: Oinker.me オープンソース化計画

この「ゆびてく」を運営する動機の大部分は、Oinker.me というサービスを出来るだけ多くの人に知ってもらいたい、というところから来ている。夏に簡単な紹介記事を一つ書いて、これから Oinker を使った知的生産について色んな側面から紹介してみようと意気込んでいたのだが、一方で現状のやり方ではイマイチ広がりがないなという感覚もあった。

個人的には Oinker.me というサービス自体をどうこうしたいと言うよりも、それ以前から育てて来たコンセプトを展開させるための場を如何に作るか、フィードバックを貰ったり、実験したりする場を如何に継続するかというのが大きな問題だった。広がりがないとプロジェクトも短命で終わってしまう。

そこでまず、Oinker.me をオープンソース化しようと思い立った。でも、単にそのままオープンソース化するだけでは面白くないので、全く新しい形にゼロから作り直そうと考えた。そして―――これが最大の事件なのだが―――全てを一人で回して独善ループに陥っているプロジェクトの救世主として、フリーランスプログラマの武藤大樹氏(@__tai2__)をゲスト開発者としてお迎えして新たなスタートを切る事になった。

tai2

Oinker.me では、知的生産の「予測の出来なさ」をそのまま力にするようなツールを作れないかという事で取り組んで来たのだが、それを更に進めるために、チームに色んな個性を呼び込んでプロジェクト自体の「予測の出来なさ」を増大させて行きたい。

新たなスタートを切るに当たってプロジェクトの名前も変える事にした。新しいプロジェクトの名前は「Cotoami (言編み・言網)」である。

「言を編んで、網(ネットワーク)を作る」ということで、名前がそのままコンセプトを表している。

元々は実験的な社内プロジェクトに、社外の個性を呼び込んで組織の枠を越えた新しい恊働のあり方を探る、Cotoamiプロジェクトにはそのような狙いも込められている。

現段階ではまだ「Hello world」的なプログラムに過ぎないが、これを徐々に発展させて行きたい。その過程はこの「ゆびてく」で詳細に報告して行く予定なので、是非是非ご注目頂ければと思う。