DockerでApicurio Studioをホスティングした

Apicurio Studio使ってますか。
私は先日使い始めました。
まだ良くわかっていません。

Dockerでホスティングしたのですが、
その道が想像以上に険しかったので、あとに続く人のために遺しておきます。

# Apicurio Studioとは?
Apicurio Studio(アピクリオ・スタジオ)は[Apicurioプロジェクト](https://www.apicur.io/)のひとつで、
OSSのOpenAPI設計ツールです。
OpenAPIのツールといえば[Swagger](https://swagger.io/)が有名ですが、
複数人で作業するとなると[有料プラン](https://swagger.io/tools/swaggerhub/pricing/)を利用しないといけません。
Swagger以外にもツールはあるそうですが、結構有料のものばかり(くわしくは[こちら](https://qiita.com/fukubaka0825/items/1fd54cea4153abd0987f))。
業務ならまだしも、趣味開発では支出を避けたいところです。

そこで、OSSのApicurio Studioを知りました。
無料というだけでなく、WebのGUIベースの操作で設計できるのも魅力じゃないでしょうか。
モックやテストといった機能はまだなくて後日追加予定らしいですが、タダなら試してみる価値はあると思います。
[活発さ](https://github.com/Apicurio/apicurio-studio/graphs/commit-activity)はぼちぼちです。

# 運用方法
Apicurio Studioを使うには次の3つの方法があります。

1. 公式がお試し用として配信している[Liveバージョン](https://studio.apicur.io/)を使う
1. ローカルマシンでホスティングする
1. Dockerでホスティングする

2はダウンロードしてすぐ使えるスクリプト込の「[クイックスタート](https://www.apicur.io/studio/download/)」が楽だと思います。
3もdocker-composeを簡単に実行できるスクリプトが[GitHub](https://github.com/Apicurio/apicurio-studio/tree/master/distro/docker-compose)にあります。

本格運用するなら2か3でしょうが、私は[サーバー群をdocker-composeで運用している](https://qiita.com/F_clef/items/0bd119f6f954e056c05b)ので、Apicurio StudioもDockerで立てようと思います。
さらに、上記で述べたGitHubで公開されているスクリプトではDBやらURLの融通が聞かないので、自前でやろうと思います。

ちなみに、公式ドキュメントはあまり親切ではありません。
Dockerイメージのページにある説明も簡素なもので、結構試行錯誤しました。
現状動いているので大丈夫だとは思いますが、なにか間違っていればコメントでご指摘いただけると幸いです。

# アーキテクチャ
Apicurio StudioをDockerで運用するには、アーキテクチャの理解が不可欠です。
1サービスとは思えない構造で、[4つのイメージ](https://hub.docker.com/search?q=apicurio%2Fapicurio-studio&type=image)から成っています。

[![](https://www.apicur.io/images/guides/arch-diagram.png)](https://www.apicur.io/studio/docs/running-apicurio-on-openshift-or-docker)

図はOpenShiftと書いてありますが、Dockerでも同じです。

| 図内の表記 | イメージ名 | 役割 |
|:-:|:-:|:-:|
| Angular based UI application, Apicurio Studio UI Component | apicurio-studio-ui | フロントエンド |
| Apicurio Studio API Component | apicurio-studio-api | バックエンド |
| Apicurio Studio WebSocket Component | apicurio-studio-ws | 編集画面の同期用WebSocket |
| KEYCLOAK | apicurio-studio-auth | ユーザー認証 |

この構成にならってもいいんですが、
この中のapicurio-studio-authはDBがH2限定なんで、KEYCLOAKは別途イメージから作ろうと思います。
よって、今回作るのは以下のような構成になります。

| イメージ | コンテナ名 |
|:--|:-:|
| postgres | pg |
| nginx | deploy |
| jboss/keycloak | keycloak |
| apicurio/apicurio-studio-ui | apicurio-ui |
| apicurio/apicurio-studio-api | apicurio-api |
| apicurio/apicurio-studio-ws | apicurio-ws |

ドメインはこの記事ではexample.comとします。

# docker-composeの設定
以下のdocker-compose.ymlファイルでコンテナを起動します。
起動時にDBの初期化があるので、pgを先に起動し、設定したあとで他のコンテナを起動します。

```yml:docker-compose.yml
version: '3'                                              
                                                          
services:
    pg:
        image: postgres
        restart: always
        environment:
            POSTGRES_USER: xxxx
            POSTGRES_PASSWORD: xxxxxx
        volumes:
            略

    deploy:                                               
        image: nginx                                      
        restart: always                                   
        ports:
            - 80:80
            - 443:443
        volumes:
            略

    keycloak:
        image: jboss/keycloak
        restart: always
        environment:
            DB_VENDOR: postgres
            DB_ADDR: pg
            DB_PORT: 5432
            DB_DATABASE: keycloak
            DB_USER: xxxx
            DB_PASSWORD: xxxxxx
            KEYCLOAK_USER: admin
            KEYCLOAK_PASSWORD: XXXXXX
        volumes:
            略

    apicurio-api:
        image: apicurio/apicurio-studio-api
        restart: always
        environment:
            APICURIO_KC_AUTH_URL: https://keycloak.example.com/auth/
            APICURIO_HUB_STORAGE_JDBC_TYPE: postgresql9
            APICURIO_DB_DRIVER_NAME: postgresql
            APICURIO_DB_CONNECTION_URL: jdbc:postgresql://pg:5432/apicurio
            APICURIO_DB_USER_NAME: xxxx
            APICURIO_DB_PASSWORD: xxxxxx
            APICURIO_DB_INITIALIZE: "true"
            APICURIO_HUB_STORAGE_JDBC_INIT: "true"
        depends_on:
            - pg
            - apicurio-ws

    apicurio-ws:
        image: apicurio/apicurio-studio-ws
        restart: always
        environment:
            APICURIO_KC_AUTH_URL: https://keycloak.example.com/auth/
            APICURIO_HUB_STORAGE_JDBC_TYPE: postgresql9
            APICURIO_DB_DRIVER_NAME: postgresql
            APICURIO_DB_CONNECTION_URL: jdbc:postgresql://pg:5432/apicurio
            APICURIO_DB_USER_NAME: xxxx
            APICURIO_DB_PASSWORD: xxxxxx
            APICURIO_DB_INITIALIZE: "true"
            APICURIO_HUB_STORAGE_JDBC_INIT: "true"
        depends_on:
            - pg

    apicurio-ui:
        image: apicurio/apicurio-studio-ui
        restart: always
        environment:
            APICURIO_KC_AUTH_URL: https://keycloak.example.com/auth/
            APICURIO_UI_HUB_API_URL: https://apicurio-api.example.com
            APICURIO_UI_EDITING_URL: wss://apicurio-ws.example.com
        depends_on:
            - pg
            - apicurio-api
            - apicurio-ws
```

# postgreSQLの設定
keycloakとApicurio Studioで使うデータベースを作成しておきます。

```sql:データベースの作成
create database keycloak;
create database apicurio;
```

# nginxの設定
Apicurio StudioはJavaScriptでAPIやWebSocketにアクセスするので、
複数のコンテナすべてのポートをホストのポートとバインドする必要があります。
でもそんなにいくつもポートを使っていると、何番が何だったとか、ポートフォワーディングの設定が大変なので、
リバースプロキシを挟んでおきます。
nginxコンテナの80と443だけをホストとバインドし、すべてのWeb通信はnginxを通します。

リバースプロキシを設定して、docker-compose.yml内に記述したURLでそれぞれアクセスできるようにします。
※常時SSL設定などは省略しています。

```conf:nginx_apicurio.conf
# keycloak
server {
    listen       443 ssl;
    listen [::]:443 ssl;
    server_name  keycloak.example.com;

    location / {
        include /etc/nginx/params/proxy_params;
        proxy_pass https://keycloak:8443;
    }
}

# ui
server {
    listen       443 ssl;
    listen [::]:443 ssl;
    server_name  apicurio.example.com;

    location / {
        include /etc/nginx/params/proxy_params;
        proxy_pass http://apicurio-ui:8080;
    }

    location /studio {
        return 301 https://$host;
    }
}

# api
server {
    listen       443 ssl;
    listen [::]:443 ssl;
    server_name  apicurio-api.example.com;

    location / {
        include /etc/nginx/params/proxy_params;
        proxy_pass http://apicurio-api:8080;
    }
}

# ws
server {
    listen       443 ssl;
    listen [::]:443 ssl;
    server_name  apicurio-ws.example.com;

    location / {
        include /etc/nginx/params/proxy_params;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_pass http://apicurio-ws:8080;
    }
}
```

# Keycloakの設定
Keycloakはシングルサインオンを提供するOSSです。
予めアプリケーションを登録しておき、
アプリケーションからKeycloakにリダイレクト、認証したらKeycloakからアプリケーションにリダイレクトすることで、ログイン情報を認証します。

Keycloakにアプリケーションを登録するには、realmと呼ばれるプロジェクトのようなもの、clientと呼ばれるアプリケーションを作成する必要があります。
設定項目が多くて大変なので、設定済みのものを拝借しましょう。

## apicurio-studio-authからの設定エクスポート
上記apicurio-studio-authイメージには諸々設定済みのKeycloakが用意されています。
なので、apicurio-studio-authイメージでコンテナを起動し、Apicurioのrealm情報をエクスポートし、今回用意したKeycloakにインポートすればいいです。

まずはコンテナを起動します。
8080ポートに接続できるようにし、管理画面へのログイン情報を環境変数で渡しておきます。

```bash:コンテナの起動
docker run -it -p 8080:8080 \
-e "APICURIO_KEYCLOAK_USER=admin" \
-e "APICURIO_KEYCLOAK_PASSWORD=admin" \
-e "APICURIO_UI_URL=https://apicurio.example.com/" \
apicurio/apicurio-studio-auth
```

起動したら、ブラウザで`http://localhost:8080/auth/`にアクセスしてログインします。
上記コマンドで起動した場合、ID:admin、password:adminでログインします。
左上でApicurioが選択されていることを確認し、最下部のExportをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/227222/22e31aef-395c-74ab-0477-3538e5cd3980.png)

(一応)2つの項目をONにし、Exportを押します。
しばらくするとjsonファイルがダウンロードできます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/227222/9ff9df40-5d6f-6b31-4f43-733769279a95.png)

## realmのインポート
先程ダウンロードしたjsonを今回用意するKeycloakにインポートします。
コンテナが起動したら、ブラウザで`https://keycloak.example.com/auth/`にアクセスし、ログインします。

「Master」というデフォルトのrealmが選択されているはずなので、「Add realm」から先程のjsonをインポートします。

## ログインできるよう設定
ログインページの形式を決めます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/227222/49b88a0d-c1db-02d5-ef5b-608d3b8caa49.png)

# 設定完了(のはず)
`https://apicurio.example.com`にアクセスすると、ログイン画面が表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/227222/fca2411e-f073-8201-4d78-7b7f426cc9ca.png)

ユーザー登録をするとログインできます。
APIの追加、編集をしてみて、動けば成功です。
失敗しててもめげずに、あれこれいじってみてください。
ブラウザのコンソール、各コンテナのログを見れば解決できると思います。

間違えても簡単にやり直せます。Dockerならね。