Clojure
kubernetes
GKE
k8s
ClojureDay 18

GKEへレシピごとアプリケーションをデプロイするSlackボットをClojureで作ったはなし

デプロイの様子

はじめに

この記事はClojure Advent Calendar 2017 18日目の記事です。
前回は 223kazukiさんのClojureでGraphQLサーバを立てるでした。

今回は、Clojureから様々なJava向けライブラリを使って実装したデプロイ用Slackbot「puppeteer」について使ってもらうために宣伝実装時に注意した事などを書こうと思います。

また、Clojure Advent Calendar 2017へはこれが初投稿なのですが、
eureka Engineering Advent Calendar 2017にて2つClojure関係の記事を投稿しておりますので、興味があれば是非読んでみてください!

それでは本題へ入ります。

作成したSlackbot「puppeteer」について

Slackbot「puppeteer」は、Slackで受け取ったレポジトリのユーザー名・レポジトリ名・ブランチ名のメンションをもとにその動作環境をGKEクラスタ上へ構築し、KubernetesのIngressやGoogle Cloud DNSを使用して構築した環境へアクセス出来るURLを自動で発行してくれるBotです。簡略化していますが、大体以下の図のような動作をします。

puppeteeer.png

また、簡略化のために省略していますが、ビルド&デプロイのJob管理のためにAWSのDynamDB、ビルドの完了通知を受け取る為にCloud Pub/Subも使用しています。
詳しい使用方法についてはREADMEをご参照ください。

puppeteerの技術スタック

puppeteerでは以下のライブラリが使われています。

  • environ: 環境変数をClojureで便利に使用するためのライブラリ
  • com.stuartsierra/component: アプリケーション全体の状態管理と、DIコンテナ相当の役割をするClojure向けライブラリ
  • org.julienxx/clj-slack: Slack APIをClojureで便利に使用するためのライブラリ
  • slack-rtm: SlackからのリアルタイムメッセージをClojureで便利に使用するためのライブラリ
  • cheshire: ClojureからJSONへのエンコード/デコードを行うライブラリ
  • circleci/clj-yaml: Clojureからyamlへのエンコード/デコードを行うライブラリ
  • tentacles: GitHub APIをClojureで便利に使用するためのライブラリ
  • com.taoensso/faraday: DynamoDBをClojureから便利に使用するためのライブラリ
  • io.fabric8/kubernetes-client: Kubernetes APIの公式Javaライブラリ
  • com.google.cloud/google-cloud-pubsub: Google Cloud Pub/Subの公式Javaライブラリ
  • com.google.apis/google-api-services-cloudbuild: Google Cloud Container Builderの公式Javaライブラリ
  • com.google.apis/google-api-services-dns: Google Cloud DNSの公式Javaライブラリ

簡単のために出来るだけClojure向けのライブラリを使用したかったのですが、
AWSと比べるとGCPのClojure向けライブラリは殆ど存在せず、半分しぶしぶと言った形でJava向けライブラリを使用しました。

puppeteerのアーキテクチャ

puppeteerの開発者は現状私ひとりですが、
もしかしたらClojureに馴染みのない社内の方にも触って貰えるかも知れないと言った期待から、社内でよく用いられているアーキテクチャに近しいアーキテクチャを決めて開発を始めました。

以下のフォルダ構成のように、大まかに appdomaininfra の三層構造になっていて、 infra 層がKubernetesやGoogle Cloud Container Builder等の外部サービスとの連携を、 domain 層がビジネスロジックを、 app 層がユーザーとの連携を執り行っています。

├── app
│   └── slackbot
├── domain
│   ├── entity
│   └── usecase
└── infra
    ├── client
    └── repository

com.stuartsierra/componentの活用

puppeteerでは、stuartsierra氏のcomponentをアプリケーション全体で使って開発しています。
appdomaininfra 層にあるモジュールはそれぞれ一つずつ必ずComponentを持っていて、アプリケーションの立ち上げ時に他のComponentを注入する事によって利用されます。

例えば、GitHubレポジトリ上にある設定ファイルに関する操作を実装する ConfigurationRepositoryComponent はGitHubのAPIのクライアントである GithubComponent が注入される事で実装されています。

puppeteer/src/puppeteer/infra/repository/conf.clj
...

(defn load-conf
  [{:keys [github-client]}
   {:keys [user-name repo-name branch-name]}]
  (some->>
    (github/get-file github-client ;; 注入されたgithub-clientで設定を取得
                     {:user user-name
                      :repo repo-name
                      :path conf-path
                      :ref branch-name})
    Content->Configuration))

(defrecord ConfigurationRepositoryComponent [github-client] ;; github-clientが注入されてくる
  component/Lifecycle
  (start [this]
    (println ";; Starting ConfigurationRepositoryComponent")
    this)
  (stop [this]
    (println ";; Stopping ConfigurationRepositoryComponent")
    this))

...

このパターンを全ての層で徹底させているため、アプリケーションの起動・終了が簡単になっています。

おわりに

かなり早足ですが、puppeteerの実装について振り返りました。
まだまだ開発中のプロジェクトなので、もし興味があればPRやIssueを立てて頂けると泣いて喜びますので何卒よろしくお願い致します :pray:

それではまたお会いいたしましょう!
次回はyuwki0131さんの記事です、お楽しみに!

2018/07/06 追記: puppeteerはsorcererへ改名されました