docker-composeにnginxのコンテナ追加したらRedirectでハマったのでメモ
自作のWSGIフレームワークKobinのExampleにおいて、手元でも静的ファイルをNginxで返すように変更を加えた。 ただしRedirectがうまくいかないので調査 & 雑にメモ (ところどころ英語)。
Problem
GitHub - kobinpy/kobin-example: Example application using Kobin python web-framework.
- ローカル(Mac)の http://127.0.0.1:8080 にマッピングしてdocker-compose up。
- Github OAuthでログイン
- GithubからのCallback URLは http://127.0.0.1:8080 に設定してるのでちゃんと帰ってくる
- RedirectResponseの設定するLocationのURLが http://127.0.0.1/ になってしまい困った。
Debugging by PDB in Docker Container
Add following options.
server: : stdin_open: true tty: true command: /bin/bash
And attach container.
$ docker ps $ docker attach <container id>
Insert import pdb; pdb.set_trace
and run a server.
But if you use slim image, there are no editors, curl
and wget
.
So it's very difficult.
# pip install wsgicli # wsgicli run app/__init__.py app --host 0.0.0.0 --port 80
Caution
When you just want to run the server, you can use docker-compose exec server /bin/bash
.
But it's not link from nginx image.
これでデバッグができるようになった。
Nginx
Nginxの設定で解決出来るものではないか調べてみた。
server { listen 80; location /static { alias /usr/src/public; } location / { proxy_pass http://server; proxy_set_header Host $host; proxy_set_header X-Forwarded-Host $host; proxy_set_header X-Forwarded-Port $server_port; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } }
ProxyまわりのHTTPヘッダをちゃんと理解する。
X-Forwarded-For
: クライアントの IP アドレス。X-Forwarded-Host
: オリジナルのホスト名。クライアントが Host リクエストヘッダで渡す。X-Forwarded-Server
: プロキシサーバのホスト名。X-Forwarded-Port
: プロキシサーバのポート番号。
クライアントが要求していたページはHostヘッダで分かる。 httpsとかも使うときは、そのあたりのPortとscheme設定も気をつける必要がありそう。
(Pdb) p request.headers {'HOST': '127.0.0.1', 'X_FORWARDED_HOST': '127.0.0.1', 'X_FORWARDED_PORT': '80', 'X_FORWARDED_FOR': '172.23.0.1', 'X_REAL_IP': '172.23.0.1', 'REFERER': 'http://127.0.0.1:8080/', ...
アプリケーション側でそこも考慮するべきなのかもしれない。 HTTP_REFERERの情報を見れば、ちゃんとリダイレクト出来そう。 Kobinの実装が悪いかもしれない。調べてみる。
HTTP Refererヘッダ
これを元にRedirectさせるべきか調べてみたけどどうやらそうではない。 このヘッダ書き換えられると困る。 DjangoやBottle、Flask(Werkzeug)もHTTP REFERERを見たりしてなかった。
- https://github.com/bottlepy/bottle/blob/master/bottle.py#L2769-L2778
- https://github.com/django/django/blob/master/django/http/response.py#L419-L437
Solution
とりあえずDockerのNginxコンテナを捨てる。理由は
- KobinのRedirectResponseの実装に問題は無さそう
- CloudFrontとかCDNでキャッシュさせれれば、そんなにアクセスこないのでwsgi-static-middlewareでも対応できる。
- 本番でNginx使ったとしても、8080番で公開したりすることはないので、この問題にはならない。
自前のフレームワークだけあって調査がやりやすかった。
- 作者: 久保達彦,道井俊介
- 出版社/メーカー: 技術評論社
- 発売日: 2016/01/16
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (4件) を見る
Core APIの概要とDjango REST Frameworkでの使い方
追記: この記事の内容はかなり古くなっています。翔泳社さんからDjangoの書籍を出版するこちらを読んで頂くのがおすすめです。
Django Advent Calendar 2016 - Qiita 18日目の記事です。
Django REST Frameworkは、DjangoでRESTfulなAPIを提供するときに非常に人気のあるフレームワークです。 既に使ってるよという方も多くいらっしゃるのではないでしょうか。
日経電子版さんも業務でも活用しているようです。
この記事では、REST Framework自体の使い方とかはあまり話しません。 REST Framnworkの作者である @_tomcriestie さんが策定・開発しているCore APIの概要と使い方について、これまでの経緯も含めて簡単に解説します。
Core APIが登場するまで
これまで、Django REST Frameworkと合わせて広く利用されていたライブラリの1つに、Django REST Swaggerというものがあります。 これはDjango REST FrameworkのSerializerやViewSetなどのクラス定義から、Swaggerの定義ファイルをこのライブラリが頑張って生成してくれていて、Swagger UIを提供してくれます。 このライブラリもまた非常によく出来ていて便利です。
Core APIの登場
そんな中、REST Frameworkの開発者であるtomcriestieさんが、Core APIの開発を始めました。 Django REST Frameworkで使われるようになったのもここ半年以内での話なので、初めて聞いたという方も多いのではないでしょうか。
これはWeb APIのスキーマを定義しているもので、このCore APIからOpen API/SwaggerやJSON Hyper-Schemaなどに変換できます。 つまりこれまでは、Django REST FrameworkのViewSetsなどの情報から各ライブラリが頑張ってSwaggerの定義ファイルなどを生成していましたが、Django REST FrameworkがCore APIを生成することによって、SwaggerでもJSON Hyper-Schemaでも簡単に定義ファイルを生成できます。
- 公式サイト: http://www.coreapi.org/
Core APIのSchema generationめちゃくちゃアツい気がする。swaggerとかjson hyper-schemaがこれを通して生成できるようになった / “3.4 Announcement - Django …” https://t.co/kplBPQVi1r
— Masashi Shibata (@c_bata_) 2016年7月14日
当時はDjango REST FrameworkのViewSetsなどの情報からJSON Hyper-Schemaを生成するライブラリを開発していたのですが、Core APIの登場で必要なくなりました。完成する前に出てきてくれてよかったです。
Django REST FrameworkでCore APIを使う
それでは使い方を見ていきましょう。 といってもDjango REST Framework側でCore APIのスキーマをほとんど自動生成してくれるため、覚えないといけないことは少ししかありません。
各APIのdescriptionだけは自分で指定しないと、空文字が挿入されます。 フォーマットも決まっているのでこれに従ってください。
class CategoryViewSet(mixins.ListModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet): """ retrieve: カテゴリの詳細を取得 list: 全カテゴリーの取得 """ queryset = Category.objects.all() serializer_class = CategorySerializer
こうしておくとDjango REST SwaggerのようなCore APIを使用するライブラリを使う時に、うまくDescriptionを入れてくれます。
REST Framework以外で、Core APIを使ってみたい人へ
Document
や Link
など覚えないといけないCore APIの用語がいくつか存在しますが、そちらの解説は公式サイトに少しだけ書かれているので、勉強したい方はそちらを読んで下さい。
開発が活発でAPIもまたすぐに変わる可能性が高いので、特にいま使う予定が無いのであればここは読み飛ばしていただいて大丈夫です。
APIの変更に公式のserver-exampleのアップデートも追いついていない状態なので、Django REST Framework以外でcoreapiを使いたいときは気をつけてください。 coreapi (v2.0.9 現在)のSwaggerの定義ファイルを取得するサンプルは次のようになります。
import coreapi from kobin import Kobin, Response from coreapi import codecs from openapi_codec import OpenAPICodec app = Kobin() def get_document(): return coreapi.Document( url='/', title='Example API', content={ 'integer': 123, 'dict': {'key': 'value'}, 'list': [1, 2, 3], 'link': coreapi.Link( url='/', action='get', transform='inplace', fields=['optional', coreapi.Field('required', required=True, location='path')] ), 'nested': {'child': coreapi.Link(url='/123')} } ) @app.route('/') def swagger(): codec = OpenAPICodec() content = codec.encode(get_document()) return Response(content)
動かすときはこのようになります。 実際のユースケースでは、Acceptヘッダーの中身から適切なCore APIのcodecsを選択して返すというような形になります。
$ python3.6 -m venv venv $ source venv/bin/activate $ pip install coreapi kobin openapi-codec wsgicli $ wsgicli run core_api_sample.py app $ curl http://127.0.0.1:8000/ | jq . { "swagger": "2.0", "info": { "title": "Example API", "version": "" }, "host": "localhost:8080", "schemes": [ "http" ], "paths": { "/": { "get": { "operationId": "link", "responses": { "200": { "description": "" } }, "parameters": [ { "name": "optional", "required": false, "in": "query", "description": "", "type": "string" }, { "name": "required", "required": true, "in": "path", "description": "", "type": "string" } ] } }, "/123": { "get": { "operationId": "child", "responses": { "200": { "description": "" } }, "parameters": [], "tags": [ "nested" ] } } } }
Core APIからSwaggerの定義ファイルが取得出来ています。
Core APIまわりのツール群
Django REST Swagger以外のCore API周りのツール群は、Core APIのGithub Organizationにほぼまとまっているようです。 どんどん新しいライブラリが出てきているので、適宜チェックしておくといいかもしれません。
Codecs周り
Core APIは現在、 Core JSON (CoreAPIのJSON表現のようです)と Open API/Swagger 、 HAL 、 JSON Hyper-Schema への変換を正式にサポートしています。 他にも覗くと、API BluePrintなどの定義ファイルへの変換をサポートする予定があることがわかります。
Documentationまわり
またDjango REST Swaggerもかなり早い段階で、Core APIを使うようになりました (refs: Release v2.0.0)。 11月の初め頃までは使ってみると色々辛い部分がありましたが(詳しくは書きませんが)、個人的にはv3.5.3あたりでCoreAPI周りの実装がだいぶ追いついてきた印象です。
またDocumentationに関しては、Core Docsというのも最近登場しました。 まだほとんど使っている人はいないかと思いますが、Core APIのスキーマを返すAPIのURLを指定すればドキュメンテーションを生成してくれるようです。
各言語のクライアント
2016/12/18現在だと、ちゃんと使えるそうなのはpython-clientのみのようです。 javascript-clientやswift-clientも作るみたいですね。
おわりに
Core API周りのツール群は、まだまだ整っているとは言えない状況ですが、Django REST Frameworkから利用する分には大体便利に使えるかと思います。 また @_tomcriestie さんの開発スピードが信じられないくらい早いので、近いうちに便利なものが出来るんじゃないかなという期待もあります。
リポジトリを見てみると、OpenAPI/SwaggerやJSON Hyper-Schema、HALなどへの変換ライブラリも @_tomcriestie さんがほぼ1人で実装を進めてて彼の実装力には本当に尊敬しかでないですね。 みなさんも是非使ってみてください。
rexecute: ファイルの更新を検知して指定したコマンドを自動実行するCLIツールつくった
そんなことより卒論書きなよって話ですが、texファイルの更新を検知して自動でビルドする仕組みを整備しました。
2年前に論文を書いていたときは、RubyのGuardを使っていたのですが久しぶりに試すと動かなくて、それを直すのも面倒だったので、少し汎用的に使えるツールをGolangでつくりました。
使い方
使い方はシンプルで、lsやfindでファイルの一覧をとってきて、rexecuteコマンドにパイプで渡すだけです。
$ find . -name '*.tex' | rexecute make build
みんなのGo言語で気になったところを読んでいたのですが、CLIツールの作り方の章や、golintやgo vetなどのツールの話がとても参考になりました。 このコマンドもUNIXの哲学にあるような、組み合わせて使える小さなコマンドになっているかなと思っています。
その他の使い方
汎用的なツールなのでわりと何にでも使えます。 例えばSphinxもsphinx-autobuildとか使わなくても次のように使えます。
$ find . -name '*.rst' | rexecute make html
- 作者: 松木雅幸,mattn,藤原俊一郎,中島大一,牧大輔,鈴木健太,稲葉貴洋
- 出版社/メーカー: 技術評論社
- 発売日: 2016/09/09
- メディア: 大型本
- この商品を含むブログ (2件) を見る