この記事は NTTコミュニケーションズ Advent Calendar 2017 の11日目です。
3行で要約
- パワーポイント曼荼羅<<<<越えられない壁<<<<開発フローに組み込めるパイプライン
- リソースのフロー、コンポーネントの整合性・網羅性の設計は、プロジェクトを通じてチームで継続的な改善が必要
- ネタ記事
ポンチ絵描いてますか?
伝説には斯くある──その者ポンチ絵1を纏い、मण्डल《マンダーラ》2をかざし道を闢く。
よーし、いい子だ。今すぐマウスを置け。逆線表とかいう根拠不明なパズルを解く作業をやめて、俺の話を聞け。いいな? おい、待て。その後ろに隠した謎の概念はなんだ? Googleの画像検索3で拾ってきたOSSのロゴを矢印でつなぎ合わせて一体何を召喚するつもりだ? ……OSSは無料? おい、やめろ。とにかくやめろ。期末の終わりに、部長に見せてしまった謎のお絵かきを笑顔で持ってくるな? いいな、すでに後戻りはできないんだ。そんな目をしてもダメだ。経済新聞で聞きかじったバズワードをそのまま書くなぁ──ッ!!!
なぜかって──? その概念 は俺に効く。
( ͡° ͜ʖ ͡°) < マジデキク
パワーポイントお絵かきの問題点
なるほど完璧な作戦っスね──っ 不可能だという点に目をつぶればよぉ〰〰!
ジョジョの奇妙な冒険 第4部 「ダイヤモンドは砕けない」
屏風の虎を出せないことは、一休さんの時代から知られている。しかし、歴史は繰り返されるのだ。
ボスに施策を説明するためにポンチ絵が必要? コンポーネントに分けないと線表が不安? いいや、俺が問題だと言っているのはそこじゃない。
オーケー、そろそろ気づいてくれ。**お前が上司に見せたポンチ絵は、サービス開発に関わる一番重要なコンポーネント4**だってことに。ろくすっぽ検証もしてないくせに、俺たちの開発を振り回しているってことに。
チェックリスト?──いいや、違う。ダブルチェック?──いいや、違う。俺が言いたいことはそういうことじゃない。もう2017年も終わるんだ。ポンチ絵の質を人間の努力でカバーする時代は終わらせてくれ。それはもう技術で解決できるエンジニアリングの領域なんだ。
2018年のスマートなポンチ絵ってやーつ、そろそろ見せてくれないか???
Concourseでポンチ絵を書く
以上、フィクションです。
こんにちは、@nitkyです。初めましての人は初めまして。普段は脆弱性をペシペシしたり、マルウェアを愛でたり、ポンチ絵に巣食う魔を祓うお仕事をしています。あまりに強い除霊を行うと、施策そのものが成仏してしまうケースが稀によくあるとのこと。破ぁッ!!
ポンチ絵という用語に馴染みのない方もいると思うので補足しますが、プロジェクトに忖度が必要な謎パワポお絵描きが存在しているのであれば、間違いなくそれがポンチ絵です。
この記事の背景
ポンチ絵・曼荼羅と呼ばれる超概念図ですが、「◯◯パイプライン」に限りなく近い何か、またはソフトウェア等のコンポーネントを謎のドメインで分割・結合した名状しがたい何か、が含まれていることが有識者より報告されています。[要出典]
「パワポで矢印引きまくるくらいなら、最初からCI/CDツールでパイプライン書けばよくね???」
という若手社員(僕)の純粋な疑問をもとにこの記事は構成されています。邪念はありません。
……曼荼羅人材は徳が高いのです。(ここで筆が途切れている)
この記事の目的
- 神々の世界と人の世界の接続
- 検証可能なパイプラインとしての曼荼羅翻訳
- パワポで1ドットの微調整に30分かけるくらいならパイプライン図かけ
Concourseについて
ConcourseはGo言語で書かれたパイプラインベースのCIシステムです。今回はこれを使ってポンチ絵を検証可能にすることを考えます。
Concourseのインストール
公式ドキュメントのインストール手順を参考に環境を用意してください。お手軽に試すのであれば、Single VMまたはDockerがオススメです。
Concourseのコンセプト
Concourseで扱う概念は大きく分けて三つあります。
- リソース: 実体(エンティティ)を指す。gitリポジトリなど。
- タスク: リソースを利用可能。隔離された環境でリソースを利用してスクリプトを実行。
- ジョブ: タスクを実行可能。依存したリソースをトリガとして実行。
なるほど分からん、という印象を受けたかもしれませんが安心してください。
要はリソースという入力をジョブという関数で変化させていく一連のプロセスをパイプラインと呼んでいます。(図1上にあるパイプラインでは、ジョブによってタスクの存在がラッピングされ見えなくなっています)
これさえ理解できれば、あとはYAMLによるパイプラインの記述に慣れるだけです。例を通してYAMLによるパイプラインの記述を実践していきましょう。
Hello, World!
それでは、曼荼羅人材の気分になって作図を開始していきましょう! (スゥ…
まずは、ここを参考にConcourseのWebUIを表示して、flyをダウンロード&インストールします。
(Docker版であればデフォルトURLは http://localhost:8080/ )
$ chmod +x ~/Downloads/fly
$ mv ~/Downloads/fly /usr/local/bin/fly
$ fly --version
3.6.0
バージョンが表示さればインストールは成功です。動作確認はMac OS Sierraで行いましたが、それ以外の方は各OSや環境に合わせて、パス等の修正をお願いします。
次に、ターゲット名(エイリアス)の指定と、そのログインを行います。今回はhttp://localhost:8080
のターゲット名としてlite
を指定します。
$ fly -t lite login -c http://localhost:8080
ユーザ名とパスワードを聞かれた場合は、インストール時に指定したものを入力してください。(デフォルト設定は concourse: changeme
)
fly targets
で、現在使用可能なターゲットの一覧を確認できます。
$ fly targets
lite http://localhost:8080 main Thu, 07 Dec 2017 00:58:17 UTC
それでは、以下のようなパイプラインのコンフィグを用意してlite
にセットしてみましょう。
jobs:
- name: hello-world
plan:
- task: say-hello
config:
platform: linux
image_resource:
type: docker-image
source: {repository: ubuntu}
run:
path: echo
args: ["Hello, world!"]
$ fly -t lite set-pipeline -p hello-world -c hello.yml
hello.yml
をhello-world
というパイプライン名でセットしたところ、図2のような画面が表示されたと思います。
おっと、僕としたことが失礼しました。曼荼羅人材は単金が高いので、タスクなんて小さな粒度で物事を考えるのは効率5が悪いことを失念していました。細かいタスクを並べ挙げるのは、僕のようなエクソシストエンジニアのお仕事です。
そう考えると、このパイプラインのコンフィグはさらにシンプルになります。
jobs:
- name: hello-world
$ fly -t lite set-pipeline -p hello-world -c hello.yml
さっぱりしましたね。出てくる絵を見比べても違いはありません。
ジョブが用意出たので、次にリソースを用意します。「リソースという入力をジョブという関数で変化させていく一連のプロセスをパイプラインという」の原則に沿って、リソースとジョブをそれぞれ定義しましょう。
resources:
- name: exorcist
type: time
source: {interval: 1m}
jobs:
- name: hello-world
Concourseはリソースを定義する際、そのtype
を宣言する必要があります。一般的な場合、リソースのエンティティとしてgitリポジトリなどが入るのですが、そういう細かい修正はエクソシストエンジニアの仕事です。今、僕たちの気持ちは曼荼羅人材。迷える子羊を、ただひたすらに導いていく高単金マンです。ここは涙を飲んで、一番単純なリソースタイプであるtime
6を仮入れしておきました。**完璧なポンチ絵を描けば、あとはエクソシストエンジニアがなんとかしてくれます。**だんだん僕も曼荼羅人材のお気持ちが高まってきたぞ〜!
$ fly -t lite set-pipeline -p hello-world -c hello.yml
……なんと、エラーが出ました。リソースを宣言したにも関わらず使っていないので、どうやらそれを怒られているようです。
$ fly -t lite set-pipeline -p hello-world -c hello.yml
invalid resources:
resource 'exorcist' is not used
限られたリソースを無駄にしない。曼荼羅人材であれば当然のことです。さっさと追加したリソースをジョブに放り込みましょう。
resources:
- name: exorcist
type: time
source: {interval: 1m}
jobs:
- name: hello-world
plan:
- get: exorcist
無事、コマンドを実行して生贄リソースを投入できました。(リソースとジョブの間が点線で接続されているのはtrigger
がtrue
になっていないからですが、Concourseを用いた作図の趣旨から外れるので今回は割愛します。詳しくはドキュメントを参照してください。)
リソースをジョブに放り込んだら、次は成果の提供ですね。リリースを定義して配置してみましょう。
resources:
- name: exorcist
type: time
source: {interval: 1m}
- name: release
type: time
source: {interval: 1m}
jobs:
- name: hello-world
plan:
- get: exorcist
- put: release
$ fly -t lite set-pipeline -p hello-world -c hello.yml
exorcistをhello-worldに放り込んで、サービスをリリース……。若干helloというよりhellな感じがしなくもないですが、それは気のせいです。基本的なConcourseの使い方は以上となります。あとは人月の神話7がプロジェクトの成功を約束します。
Concourseによるお絵かき例
ここでは、特にConcourseを用いたパイプラインの作図に特化して例を紹介します。ConcourseのWeb画面にある左上のハンバーガーアイコンをクリックすることで、セットされたパイプラインの一覧を表示することができます。
1つのリソースが1つのジョブを通過してreleaseに配置
リソース
- resA
- release
ジョブ
- job01
resources:
- name: resA
type: time
source: {interval: 1m}
- name: release
type: time
source: {interval: 1m}
jobs:
- name: job01
plan:
- get: resA
- put: release
$ fly -t lite set-pipeline -p example1 -c example1.yml
[図3-1: 1つのリソースが1つのジョブを通過してreleaseに配置]
2つのリソースが1つのジョブを通過してreleaseに配置
リソース
- resA
- resB
- release
ジョブ
- job01
resources:
- name: resA
type: time
source: {interval: 1m}
- name: resB
type: time
source: {interval: 1m}
- name: release
type: time
source: {interval: 1m}
jobs:
- name: job01
plan:
- get: resA
- get: resB
- put: release
$ fly -t lite set-pipeline -p example2 -c example2.yml
[図3-2: 2つのリソースが1つのジョブを通過してreleaseに配置]
1つのリソースが2つのジョブを漏れなく通過してreleaseに配置
リソース
- resA
- release
ジョブ
- job01
- job02
resources:
- name: resA
type: time
source: {interval: 1m}
- name: release
type: time
source: {interval: 1m}
jobs:
- name: job01
plan:
- get: resA
- put: release
- name: job02
plan:
- get: resA
- put: release
$ fly -t lite set-pipeline -p example3 -c example3.yml
[図3-3: 1つのリソースが2つのジョブを漏れなく通過してreleaseに配置]
1つのリソースが2つのジョブを順に通過してreleaseに配置
リソース
- resA
- release
ジョブ
- job01
- job02
resources:
- name: resA
type: time
source: {interval: 1m}
- name: release
type: time
source: {interval: 1m}
jobs:
- name: job01
plan:
- get: resA
- name: job02
plan:
- get: resA
passed: [job01]
- put: release
$ fly -t lite set-pipeline -p example4 -c example4.yml
[図3-4: 1つのリソースが2つのジョブを順に通過してreleaseに配置]
2つのリソースが2つのジョブを漏れなく通過してreleaseに配置
resources:
- name: resA
type: time
source: {interval: 1m}
- name: resB
type: time
source: {interval: 1m}
- name: release
type: time
source: {interval: 1m}
jobs:
- name: job01
plan:
- get: resA
- get: resB
- put: release
- name: job02
plan:
- get: resA
- get: resB
- put: release
$ fly -t lite set-pipeline -p example5 -c example5.yml
[図3-5: 2つのリソースが2つのジョブを漏れなく通過してreleaseに配置]
2つのリソースが2つのジョブを順に通過してreleaseに配置
resources:
- name: resA
type: time
source: {interval: 1m}
- name: resB
type: time
source: {interval: 1m}
- name: release
type: time
source: {interval: 1m}
jobs:
- name: job01
plan:
- get: resA
- get: resB
- name: job02
plan:
- get: resA
passed: [job01]
- get: resB
passed: [job01]
- put: release
$ fly -t lite set-pipeline -p example6 -c example6.yml
[図3-6: 2つのリソースが2つのジョブを順に通過してreleaseに配置]
2つのリソースが2つのジョブを漏れなく通過して、1つのジョブに集約されてからreleaseに配置
resources:
- name: resA
type: time
source: {interval: 1m}
- name: resB
type: time
source: {interval: 1m}
- name: release
type: time
source: {interval: 1m}
jobs:
- name: job01
plan:
- get: resA
- get: resB
- name: job02
plan:
- get: resA
- get: resB
- name: job03
plan:
- get: resA
passed: [job01, job02]
- get: resB
passed: [job01, job02]
- put: release
$ fly -t lite set-pipeline -p example7 -c example7.yml
[図3-7: 2つのリソースが2つの並列なジョブを漏れなく通過して、1つのジョブに集約されてからreleaseに配置]
さらに高度なパイプライン
3つのリソースが、4つのジョブを以下のように通過することを考える。
- resA, resB, resCが存在する
- resAはjob01とjob03とjob04をそれぞれ順に通過する
- resBはjob02とjob03を順に通過する
- resCはjob01とjob02を並列に通過し、さらにjob03とjob04を順に通過する
resources:
- name: resA
type: time
source: {interval: 1m}
- name: resB
type: time
source: {interval: 1m}
- name: resC
type: time
source: {interval: 1m}
- name: release
type: time
source: {interval: 1m}
jobs:
- name: job01
plan:
- get: resA
- get: resB
- get: resC
- name: job02
plan:
- get: resB
- get: resC
- name: job03
plan:
- get: resA
passed: [job01]
- get: resB
passed: [job02]
- get: resC
passed: [job01, job02]
- name: job04
plan:
- get: resA
passed: [job03]
- get: resC
passed: [job03]
- put: release
$ fly -t lite set-pipeline -p advanced -c advanced.yml
パワーポイント図との機能比較
###利点
開発フロー(CI/CD)に組み込むことで、図の正しさを継続的に検証可能
- パイプラインのフローでやりとりされるもの(リソース)の一貫した明記
- 各ステージでジョブが行うことの一貫した明記
- 適切なコンポーネント選択なのか検証が可能
- 適切なドメイン分割なのか検証が可能
- (開発時に判明した)事実に沿った修正・変更が容易
- 開発初期段階から、E2Eテストを意識して開発を回せる
- パイプラインの修正頻度が少ない=設計時の見積もりが上手い
- パイプラインの修正頻度が多い=設計時の見積もりが甘い
- パイプラインを動かすことができない=論外
###欠点
動くポンチ絵をちゃんと描ける人材なら、そいつの曼荼羅に困ってない
まとめ
「私……全てのポンチ絵を生まれる前に消し去りたい。全ての宇宙、過去と未来の全てのポンチ絵を、この手で」
参考URL
-
美術作品。実用性は乏しい。魔除けに使う細かい呪文がびっしりと書き込まれている。 ↩
-
上司の精神状態や残業によってポンチ絵の神聖が高まった状態。耐性のない人間が見ると発狂する。 ↩
-
gooにも画像検索があること、忘れないでください。 ↩
-
テクニカルアーキテクトから見れば、アーキテクチャ設計やグランドデザインをコンポーネントといってよいか疑問だが、サービスリリースを成功に導く構成要素として、あえてコンポーネントという用語を使用した。 ↩
-
エンジニアの間では、曼荼羅人材の単金で優秀なエンジニアを雇用することを指す。 ↩
-
インターバル実行などに用いるエンティティ。 ↩
-
銀の弾丸などない。しかし、金の弾丸は社内政治力と深い関わりがある。 ↩