こちらは、ぷりぷりあぷりけーしょんずアプリ開発担当による ぷりぷりあぷりけーしょんず Advent Calendar 2019 の2日目の記事です。
背景
近年のアプリケーション開発において、マイクロサービスがとても流行ってますね。
現場によっては、社内サービスまでマイクロサービスにしちゃってる企業もあったりします。(モノリスなアプリでいいと思うのですが)
マイクロサービスが流行るにいたって、RESTful API (REST API) というのもキーワードとして多く出てきます。
その RESTful API はどのようなリソースがあり、どんな値をリクエストに含め、どのような値が返ってくるのか、認証は何を使用している?といったドキュメント(API 定義)が必要になります。(というか必須だと思ってます)
そのようなドキュメントがあることにより、フロントエンド開発チームとの連携が用意になり、実装パフォーマンスが上がります。
そこで現在、「世界標準となるかもしれない」というほどトレンドであるモック API サーバ Swagger に実際に触れて、自作の RESTful API との紐付けを docker-compose でやってみましたって内容を記載してこうと思っています。
Swagger とは
Swagger の説明に関しては、ググればたくさんの情報が出てくるので省略しますが、ざっくり言えば、REST API を定義するためのオープンソースフレームワークです。(←)
yaml や json を用いて定義します。(今回、json ではやっていません)
個人的に curl で実際のエンドポイントを叩けるのは素敵と感じました。
yaml や json の記述方法などについては以下の記事にとてもわかりやすくまとまっています。
今回は docker hub に上がっている Swagger のイメージを使用し、実装していきます。
イメージは swagger-editor と swagger-ui を使用します。
swagger-editor に関しては、ウェブからでも閲覧できますね。
Docker Compose
Swagger や REST API などを立ち上げるため、docker-compose を使用します。
Docker の説明やインストール方法などに関しては今回は記載しません。
では、実際に先ほどの docker hub のイメージを使用し、 docker-compose.yaml
を作成してきます。
yaml の定義
yaml を定義する前に、実際のフォルダ構成を先に記載しておきます。
今回の記事に必要なファイルやフォルダを tree として表示しています。
root
├── api
│ ├── Dockerfile
│ ├── swagger
│ │ └── openapi.yaml
│ └── その他 REST API のアプリケーションソース
└── compose
└── docker-compose.yaml
REST API に関しては Node + TypeScript + Express で簡単な API を作成しました。
上記のフォルダ構成を元に docker-compose.yaml
を以下に記載します。
version: '3'
services:
# Swagger API
swagger-editor:
image: swaggerapi/swagger-editor
container_name: "swagger-editor"
ports:
- "8001:8080"
swagger-ui:
image: swaggerapi/swagger-ui
container_name: "swagger-ui"
ports:
- "8002:8080"
volumes:
- ../api/swagger/openapi.yaml:/openapi.yaml
environment:
SWAGGER_JSON: /openapi.yaml
# REST API
api:
tty: true
build:
context: ../api
dockerfile: Dockerfile
container_name: 'api'
volumes:
- ../api/:/usr/app/
- /usr/app/node_modules
environment:
NODE_ENV: development
ports:
- '3001:3001'
command: 'npm run dev'
こんな感じで定義しましたが、上記以外にも Database などの定義もあります。しかし、今回の記事とは関係ないというのもあり、またその設定も含めちゃうと、逆にわかりにくくなるため、必要な箇所だけ記述しています。
各々の説明
- swagger-editor
docker hub からイメージを引っ張ってきたくらいで、特別な設定は記載していません。
こちらを立ち上げると Swagger Editor がポート 8001 で立ち上がります。
- swagger-ui
こちらも docker hub からイメージを引っ張ってきて、使用しています。
volumes
に今回の REST API の定義ファイルを相対パスで指定します。( ../api/swagger/openapi.yaml
)
こちらを立ち上げると、REST API の定義を閲覧することができます。(Swagger Editor の左半分がなくなっただけ)
ポートは 8002 を使用。
- api
自作した REST API になります。
急成長中の TypeScript や Node の王道フレームワーク Express などを用いて REST API を作成しました。(まだ完成はしていませんが、1リソースだけ作成しました笑)
npm trends を利用してイケイケなパッケージを導入したり、完璧ではありませんがクリーンアーキテクチャに挑戦してみたりしています。
マルチパートな言語でもないので、色々苦戦はしていますが、こちらのアプリケーションの記事を別で投稿したいとも考えています。
こちらでは、ポート 3001 を用いてアプリケーションを立ち上げています。また、こちらのアプリの定義を Swagger で記載していく感じとなります。
立ち上げてみよう
では実際に、docker-compose コマンドで立ち上げてみます。
compose ディレクトリ内で以下コマンドを実行。
$ docker-compose up --build --remove-orphans
docker-compose ps
でプロセスを確認しましょう。
$ docker-compose ps
Name Command State Ports
----------------------------------------------------------------------------------------------------------
api ./scripts/dev.sh Up 0.0.0.0:3001->3001/tcp
swagger-editor sh /usr/share/nginx/docker ... Up 80/tcp, 0.0.0.0:8001->8080/tcp
swagger-ui sh /usr/share/nginx/run.sh Up 80/tcp, 0.0.0.0:8002->8080/tcp
これで3つのプロジェクトが立ち上がったと思われます。
落とし穴
REST API 定義のために必要な API は立ち上がりました。
あとは Swagger Editor から REST API 定義を記述していくのですが、API のホストやポートを指定して Swagger から実際のエンドポイントを叩く( curl
)と以下のエラーとなりました。
要は Swagger のコンテナから REST API のコンテナへアクセスできていないようです。(localhost は自分自身を指し示しているので、当然な気もします)
ここからは戦いの時間です。色々調査しました。。。
networks の設定で頑張ってみたり、ホストを container_name にして API 叩いてみたり。。。(全てエラー)
結果的に、以下の記事にたどり着くことができました。流石に Swagger に関する記事はたくさん存在しますね。助かります。
こちらは、実際にサンプルも git 管理し、公開してくれているのでとてもわかりやすかったです。
解決策
nginx をかませばいいだけでした。
Swagger と REST API の仲介役として nginx を用意するだけで、上記の問題はすぐ解決するようです。(インフラの目線からしたら当然なのかもしれませんが)
上記の記事を参考に docker-compose.yaml
を以下のように書き換えます。
networks
にも修正が入ることに注意が必要です。
今回は、ポート 8003 で nginx 経由して Swagger API から REST API にアクセスできるように設定しました。
version: '3'
services:
# Swagger API
swagger-editor:
image: swaggerapi/swagger-editor
container_name: "swagger-editor"
ports:
- "8001:8080"
swagger-ui:
image: swaggerapi/swagger-ui
container_name: "swagger-ui"
ports:
- "8002:8080"
volumes:
- ../api/swagger/openapi.yaml:/openapi.yaml
environment:
SWAGGER_JSON: /openapi.yaml
# ===== 追加 =====
swagger-nginx:
image: nginx:mainline-alpine
container_name: "swagger-nginx"
depends_on:
- api
ports:
- "8003:8081"
volumes:
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf
networks:
swagger_link:
aliases:
- local.swagger.api
# ===============
# REST API
api:
tty: true
# ===== 追加 =====
networks:
swagger_link:
aliases:
- local.swagger.apisprout
# ===============
build:
context: ../api
dockerfile: Dockerfile
container_name: 'api'
volumes:
- ../api/:/usr/app/
- /usr/app/node_modules
environment:
NODE_ENV: development
ports:
- '3001:3001'
command: 'npm run dev'
# ===== 追加 =====
networks:
swagger_link:
external: true
# ===============
また、compose ディレクトリ以下に nginx/default.conf
を作成します。
server {
listen 8081;
server_name localhost.example.com;
location / {
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods "POST, GET, PATCH, DELETE, PUT, OPTIONS";
add_header Access-Control-Allow-Headers "Origin, Authorization, Accept";
add_header Access-Control-Allow-Credentials true;
proxy_pass "http://local.swagger.apisprout:3001";
}
}
うん、権限フルですね笑
再度、立ち上げてみよう
ここまでの修正が完了したら、docker のイメージを削除し、また立ち上げてみます。
$ docker-compose down && docker system prune -a
$ docker-compose up --build --remove-orphans
ワクワクしながら立ち上がりを待っていると、速攻怒られました。
Creating network "compose_default" with the default driver
ERROR: Network swagger_link declared as external, but could not be found. Please create the network manually using `docker network create swagger_link` and try again.
networks
設定において、external を true
で設定すると宣言されたネットワーク(今回で言うと swagger_link
)を外部から探しに行ってくれるようです。あれば、それをコンテナにマウントするようですね。
なければ、エラー文にも書いてあるように docker network create swagger_link
を実行してくださいとのこと。
$ docker network create swagger_link
完了したら、再度 docker-compose up
してみましょう。
自分はこれで正常に docker が立ち上がりました。
確認
ドキドキしながら、再度 Swagger からエンドポイントを叩いてみました。
正常に行きました。ふぅ。
最後に、最終的なフォルダ構成を以下に記載します。
root
├── api
│ ├── Dockerfile
│ ├── swagger
│ │ └── openapi.yaml
│ └── その他 REST API のアプリケーションソース
└── compose
├── docker-compose.yaml
└── nginx
└── default.conf
まとめ
Swagger と REST API の環境を Docker comopse で作成するところまでできました。
この環境構築がベストプラクティスかは、実際の業務などで触れてはいないのでわかりませんが、個人的には結構嬉しい第一歩となりました。
あとは中身をしっかりしたものにすれば問題ないですかね。
記事に色々な方のリンクを貼らせていただきました。ほんと助かりました。ありがとうございます。
個人開発でマイクロサービスっぽいことをやりたいという方は、是非参考にしてみてください!(参考にできたら)