GitLab CI環境を自己証明書を使って構築する
※自己証明書を使うのは検証目的に留めておいて、本番運用では証明書の購入が必要。
2018/5/12追記
- gitlab omnibusインストールのパラメータ修正(
gitlab_shell_ssh_port
) - ジョブとしてDockerイメージをビルドするため、
gitlab-runner register
でdocker.sockのマウントを指定
検証環境
検証した環境は以下。
パッケージ | バージョン | 備考 |
---|---|---|
Docker for Mac(Edge) | 18.05.0-ce-rc1-mac63 | Macでk8sを触るためEdgeにしている |
GitLab Community Edition | 10.7.0-ce.0 | |
GitLab Runner | v10.7.0 |
GitLabをDocker Composeで立てる
フォルダ構成は以下。
. ├── docker-compose.yml └── volume ├── certs ├── conf ├── data └── logs
volume/certs
配下に自己証明書を作成して、配備する。
ここで作成した証明書はGitLab Runnerを登録する際に利用する。しかしその時にSAN(Subject Altanative Name)を指定して作成しないと以下のようなエラーが出てしまう。
ERROR: Registering runner... failed runner=cTGFEKNu status=couldn't execute POST against https://192.168.1.9/api/v4/runners: Post https://192.168.1.9/api/v4/runners: x509: cannot validate certificate for 192.168.1.9 because it doesn't contain any IP SANs
そのため、GitLab用途で証明書を作成する際には、SANを設定する。Macでは/System/Library/OpenSSL/openssl.cnf
にあるので、以下のように秘密鍵と証明書を生成する(ただし、192.168.1.9はホストサーバ(Mac)側に割り当たったIPアドレスで、固定である方が望ましい...)。
$ cd volume/certs/ $ openssl req -newkey rsa:4096 -days 365 -nodes -x509 -subj "/C=JP/ST=CHIBA/L=CHIBA/O=NAN/OU=NAN/CN=192.168.1.9" -extensions v3_ca -config <( cat /System/Library/OpenSSL/openssl.cnf <(printf "[v3_ca]\nsubjectAltName='IP:192.168.1.9'")) -keyout server.pem -out server.crt Generating a 4096 bit RSA private key .............................................................++ ...............................................................................................++
docker-compose.yml
は以下。
version: "3" services: gitlab: image: gitlab/gitlab-ce:10.7.0-ce.0 container_name: gitlab restart: always environment: GITLAB_OMNIBUS_CONFIG: | external_url "https://192.168.1.8" gitlab_rails['time_zone'] = "Asia/Tokyo" gitlab_rails['initial_root_password'] = "password" gitlab_rails['initial_shared_runners_registration_token'] = "token" gitlab_rails['gitlab_shell_ssh_port'] = 2222 unicorn['worker_timeout'] = 60 unicorn['worker_processes'] = 3 logging['logrotate_frequency'] = "weekly" logging['logrotate_rotate'] = 52 logging['logrotate_compress'] = "compress" logging['logrotate_method'] = "copytruncate" nginx['listen_port'] = 443 nginx['redirect_http_to_https'] = true nginx['ssl_certificate'] = "/etc/ssl/certs/gitlab/server.crt" nginx['ssl_certificate_key'] = "/etc/ssl/certs/gitlab/server.pem" nginx['ssl_protocols'] = "TLSv1.1 TLSv1.2" # Add any other gitlab.rb configuration options if desired ports: - "443:443" - "2222:22" volumes: - ./volume/conf:/etc/gitlab - ./volume/certs:/etc/ssl/certs/gitlab - ./volume/logs:/var/log/gitlab - ./volume/data:/var/opt/gitlab logging: options: max-size: "4M" max-file: "14"
起動する。
$ docker-compose up -d Starting gitlab ... done
しばらくするとWebブラウザからhttps://192.168.1.9
で接続可能になる(起動未完了時は502エラーページが表示される)。
起動完了後は、docker-compose.yml
で指定したように、ユーザIDroot
、パスワードpassword
でログインできる。
GitLab Runnerを登録する
フォルダ構成は下記。
. ├── docker-compose.yml └── volume ├── certs │ └── 192.168.1.9.crt └── config
ここで192.168.1.9.crt
とは、GitLab構築時に作成した証明書を(GitLabのアドレス).crt
として格納する(参考情報)。
docker-compose.yml
は以下。
version: '3' services: runner: image: gitlab/gitlab-runner:v10.7.0 container_name: gitlab-runner restart: always volumes: - ./volume/config:/etc/gitlab-runner - ./volume/certs:/etc/gitlab-runner/certs - /var/run/docker.sock:/var/run/docker.sock logging: options: max-size: "4M" max-file: "14"
起動する。
$ docker-compose up -d Recreating gitlab-runner ... done
ついで、Runnerを登録する。registerで指定可能なオプションはgitlab-runner register --help
で確認できる(分量が非常に多い)。
また、登録されると./volume/config/config.toml
という設定ファイルが出力されるので、これをあらかじめ設定しておき、registerの引数で渡すこともできる。
$ docker exec gitlab-runner gitlab-runner register -n -r token --executor docker --docker-image alpine:latest --url https://192.168.1.8/ --docker-volumes /var/run/docker.sock:/var/run/docker.sock Running in system-mode. Registering runner... succeeded runner=token Runner registered successfully. Feel free to start it, but if it's running already the config should be automatically reloaded!
以上でめでたくGitLab検証環境できた。
SwaggerでWeb APIを作る - 非同期実行 (その1)
これまでは同期処理(リクエストを送ると処理が実行され、レスポンスが返答されるまで待つ処理)であったが、処理が長いものなどは非同期で処理を実行しなくてはならない。PythonではCeleryというライブラリで実現できる。 ここではまず環境構築(Flask, Celeryの連携確認)まで行う。
Celeryとは
- 非同期のタスク/ジョブキュー管理が可能
- 複数のBrokerをサポート
- RabbitMQが推奨
- Result Backendをしているするとタスクのステータスや実行結果を取得可能
- OpenStackでも採用実績あり
インストール
前提の環境としては、CentOS7を用いる。これをVirtualBox+Vagrantで環境から用意する。
$ vagrant init centos/7
- 5672, 15672のポートを開ける
- BrokerとしてRabbitMQを使用する
- 使用するポートはhttps://www.rabbitmq.com/install-rpm.htmlの"Port Access"参照。
$ cp Vagrantfile{,.bak} $ vim Vagrantfile ...(省略)... config.vm.network "forwarded_port", guest: 5672, host: 5672 config.vm.network "forwarded_port", guest: 15672, host: 15672 ...(省略)...
- 仮想マシンを起動する
$ vagrant up $ vagrant ssh
- rootパスワードは
vagrant
が初期値となっている
$ su - Password: # rpm -Uvh http://download.fedoraproject.org/pub/epel/7/x86_64/e/epel-release-7-9.noarch.rpm'
- RabbitMQのインストールおよび自動起動設定を行う
# yum -y install rabbitmq-server # systemctl enable rabbitmq-server.service # systemctl list-unit-files -t service | grep rabbit rabbitmq-server.service enabled
$ sudo yum -y install git zlib-devel bzip2 bzip2-devel readline-devel sqlite sqlite-devel openssl-devel xz xz-devel $ git clone https://github.com/riywo/anyenv ~/.anyenv $ echo 'export PATH="$HOME/.anyenv/bin:$PATH"' >> ~/.bash_profile $ echo 'eval "$(anyenv init -)"' >> ~/.bash_profile $ exec $SHELL -l $ anyenv install pyenv $ exec $SHELL -l $ pyenv install 3.5.4 $ pyenv versions * system (set by /home/vagrant/.anyenv/envs/pyenv/version) 3.5.4 $ pyenv global 3.5.4 $ pyenv versions system * 3.5.4 (set by /home/vagrant/.anyenv/envs/pyenv/version)
- Celery, flaskのインストール
$ pip install celery flask
- RabbitMQの設定
- ユーザ, バーチャルホスト, ユーザタグ, 権限を設定
- 権限は、設定、読み書き全ての権限を付与
# rabbitmqctl add_user vagrant vagrant Creating user "vagrant" ... ...done. # rabbitmqctl add_vhost vagrant Creating vhost "vagrant" ... ...done. # rabbitmqctl set_user_tags vagrant vagrant_tag Setting tags for user "vagrant" to [vagrant_tag] ... ...done. # rabbitmqctl set_permissions -p vagrant vagrant ".*" ".*" ".*" Setting permissions for user "vagrant" in vhost "vagrant" ... ...done.
FlaskからCeleryを使う
以下の順番で公式チュートリアルを読んでから手を動かし始めた
ほとんどチュートリアル通りの内容。
次にこれを実行するが、その前にCeleryワーカーを起動していないくてはならない。celery worker
コマンドを使うとフォアグラウンドで実行するので、celery multi
コマンドを使って、バックグラウンドで実行する
$ sudo mkdir -p /var/run/celery $ sudo mkdir -p /var/log/celery $ sudo chown -R vagrant. /var/run/celery/ $ sudo chown -R vagrant. /var/log/celery/ $ celery multi start w1 -A app -l info --pidfile=/var/run/celery/%n.pid --logfile=/var/log/celery/%n%I.log
最後にターミナルをもう1つ開いてタスクが投げ込まれるかを見てみる。
- ターミナル1
$ python Python 3.5.4 (default, Aug 24 2017, 12:02:53) [GCC 4.8.5 20150623 (Red Hat 4.8.5-11)] on linux Type "help", "copyright", "credits" or "license" for more information. >>> from app import add_together >>> add_together.delay(1,3) <AsyncResult: 0ee3b370-561c-4e9f-a978-58635b00df89>
- ターミナル2
$ tailf /var/log/celery/w1-1.log ...(省略)... [2017-09-16 02:24:23,203: INFO/ForkPoolWorker-1] Task app.add_together[74dabaad-aa7e-4dee-aab1-a505deaeb76f] succeeded in 0.0009727960004966008s: 4
押して開く
半年ぐらいずっと同じような指摘をいただいている。
「会になっていない」
「口割りまで降りていない」
だったり、矢が下に行ったり。
弓道で本当に一連の動作が大事だなと思えるのは、こういった原因が直前の動作の引き分けにあるのではなく、遡ると取り懸けに原因があったり、足踏みにあったり、奥が深い。
まだ何が原因かは突き止めていないが、先日の練習でイメージが湧いたのは、「妻手を肩にのせるように」というアドバイスだった。 意識すると妻手の肩が入っておらず、それが先の指摘に続いていた。そうすると今度は小さく引いているような気がしてくる(右肩がクルッと回っているような感覚)。これでは次はおそらく大きく引くことという指摘をもらうだろう。
そうなると今の時代は便利で、Youtubeで人の行射を眺めたり、解説を読んだり。最後にたどり着いたのは「押して開くこと」。しばらくはこの押して開くを無意識にできるよう、なんどもなんども反復したい。
SwaggerでWeb APIを作る - DBと連携する
前の記事で書いたWeb APIを今度はDBと連携させる。前回までは辞書オブジェクトにデータを保存していたので、アプリケーションを再起動させるとPOSTしたデータが失われることになる。
DBとしてSQLiteを使ってみる。 業務では専らPostgreSQLなんだけど、開発時や組み込みで使われることがある(らしい)。
まずはdb.py
を作り、下記のような接続処理を書く。
create_engine
の引数では、DBの接続パスと、文字列をunicode文字列として扱うようにcovert_unicode=True
を指定する。
次にモデルを定義する。モデルで定義した属性は、そのままDBの属性となる(O/Rマッピング機能)。
Pet
モデルを以下のように定義する。
as_dict()
は、検索系のAPIで辞書データを返す必要があるため、そのようなメソッドを用意している。
モデルまで作成したので、モデルからDBを初期化する。
$ python -c "from db import init_db; init_db()" $ cat petstore.db nn??tablepetspetsCREATE TABLE pets ( id INTEGER NOT NULL, name VARCHAR(100), tag VARCHAR(20), created DATETIME, PRIMARY KEY (id)
コントローラー部分は、DB連携のためPet
モデルの処理とdb_session
の操作を追加している。
petstore/default_controller.py
一覧取得find_pets
は、複数のタグによる検索が可能となっているので、リストでtags
が取得される。それを条件に展開するので、Pet.tag == tag
をリストの要素分作って、sqlalchemy._or()
の要素に展開するように実装している。
またlimit
による件数の制限は、取得したものについて行うようにしている(本当は検索するときに指定すべき)。
or_filters = or_(*[Pet.tag == tag for tag in tags]) pets = Pet.query.filter(or_filters).all()
SwaggerでWeb APIを作る - APIを実装する
この記事はFujitsu Advent Calendarの20日目です。
Swaggerとは?
前のポストではSwaggerでWeb APIを設計し、ドキュメント化、モックサーバの起動について書いた。 改めてSwaggerについて。
- REST API設計とそのツール群
- REST APIの標準化団体Open API Initiativeが仕様として採用した
Flaskサーバを実装
使うAPI仕様書は、出来合いのPetstore(Simple)のものを使う(Swagger Editorで[File] > [Open Example...]でpetstore_simple.yamlを選択)。その仕様に沿ったサーバ機能の雛形は、同様にSwagger Editorの[Generate Server]から好みのフレームワークを選択することで取得できる。
ディレクトリ構造は以下のようなかたち。 swagger.yamlがAPI仕様書。ロジックはdefault_controller.pyにつくる。
. ├── LICENSE ├── README.md ├── __init__.py ├── app.py ├── controllers │ ├── __init__.py │ └── default_controller.py └── swagger └── swagger.yaml
Swaggerに準拠したFlaskのフレームワークとしては、connexionがある。 hjacobs/connexion-exampleに習い、今回はデータを辞書として持ち回るように実装する。
まずはPythonのライブラリを2つほどインストールする。
$ pip install Flask connexion
次にアプリケーションの起点であるapp.pyを見てみる。
API仕様書のswagger.yaml
をapp.add_api
で読み込み、8080ポートでサーバを起動することが書いてあるのみ。
#!/usr/bin/env python3 import connexion if __name__ == '__main__': app = connexion.App(__name__, specification_dir='./swagger/') app.add_api('swagger.yaml') app.run(port=8080)
default_controller.pyが処理を書くメインとなっている。
このパスとメソッド名をドットでつないだものがoperationId
で指定しているものである。
connexionは、辞書で返却するとjsonで返してくれる(jsonify
などでjson化しなくて良い)。
import logging from connexion import NoContent logger = logging.getLogger(__name__) PETS = {} def add_pet(pet): id = pet['id'] # idがすでに使用されているか exists = id in PETS if exists: logger.info('If exists, update %s', id) PETS[id].update(pet) else: logger.info('If not exists, create %s', id) PETS[id] = pet return PETS[id], 200 def delete_pet(id): if id in PETS: logger.info('Deleting pet %s', id) del(PETS[id]) return NoContent, 204 else: return NoContent, 404 def find_pet_by_id(id): pet = PETS.get(id) return pet or (NoContent, 404) def find_pets(tags=None, limit=None): return [pet for pet in PETS.values() if not tags or pet['tag'] in tags][:limit]
実装したREST APIを使ってみる
- まずはデータのPost。
- レスポンスとして投入したデータを返却する。
$ cat pet.json {"id":1,"name":"foo","tag":"dog"} $ cat pet2.json {"id":2,"name":"bar", "tag":"cat"} $ curl -i -X POST --header "Content-Type: application/json" --header "Accept: application/json" http://localhost:8080/api/pets -d @pet.json HTTP/1.0 200 OK Content-Type: application/json Content-Length: 46 Server: Werkzeug/0.11.11 Python/3.5.2 Date: Mon, 19 Dec 2016 14:02:06 GMT { "tag": "dog", "name": "foo", "id": 1 } $ curl -i -X POST --header "Content-Type: application/json" --header "Accept: application/json" ttp://localhost:8080/api/pets -d @pet2.json HTTP/1.0 200 OK Content-Type: application/json Content-Length: 46 Server: Werkzeug/0.11.11 Python/3.5.2 Date: Mon, 19 Dec 2016 14:02:11 GMT { "tag": "cat", "name": "bar", "id": 2 }
- 一覧を取得
$ curl -i http://localhost:8080/api/pets HTTP/1.0 200 OK Content-Type: application/json Content-Length: 118 Server: Werkzeug/0.11.11 Python/3.5.2 Date: Mon, 19 Dec 2016 14:32:26 GMT [ { "name": "foo", "id": 1, "tag": "dog" }, { "name": "bar", "id": 2, "tag": "cat" } ]
- 一覧を取得(取得件数を1件(limit=1)を指定)
$ curl -i http://localhost:8080/api/pets?limit=1 HTTP/1.0 200 OK Content-Type: application/json Content-Length: 60 Server: Werkzeug/0.11.11 Python/3.5.2 Date: Mon, 19 Dec 2016 14:33:26 GMT [ { "name": "foo", "id": 1, "tag": "dog" } ]
- 一覧を取得(
tags
を指定)dog
のみ取得
$ curl -i http://localhost:8080/api/pets?tags="dog" HTTP/1.0 200 OK Content-Type: application/json Content-Length: 60 Server: Werkzeug/0.11.11 Python/3.5.2 Date: Mon, 19 Dec 2016 14:35:20 GMT [ { "name": "foo", "id": 1, "tag": "dog" } ]
cat
またはdog
のtagのものを取得
$ curl -i http://localhost:8080/api/pets?tags="dog,cat" HTTP/1.0 200 OK Content-Type: application/json Content-Length: 118 Server: Werkzeug/0.11.11 Python/3.5.2 Date: Mon, 19 Dec 2016 14:35:36 GMT [ { "name": "foo", "id": 1, "tag": "dog" }, { "name": "bar", "id": 2, "tag": "cat" } ]
- 削除してから再び取得
- 削除して204となる
- id=1を取得して404となる
$ curl -i -X DELETE http://localhost:8080/api/pets/1 HTTP/1.0 204 NO CONTENT Content-Type: text/html; charset=utf-8 Content-Length: 0 Server: Werkzeug/0.11.11 Python/3.5.2 Date: Mon, 19 Dec 2016 14:37:23 GMT $ curl -i http://localhost:8080/api/pets/1 HTTP/1.0 404 NOT FOUND Content-Type: text/html; charset=utf-8 Content-Length: 0 Server: Werkzeug/0.11.11 Python/3.5.2 Date: Mon, 19 Dec 2016 14:37:43 GMT
その他
レスポンスはpretty printされて返却される
add_api
のオプションとして渡せたらいいなという議論がある。今はflask_compressを使う。
x-swagger-router-controller:
をオペレーションの上に書くとエラー
x-swagger-router-controller
としては、controllerのモジュールのパス(上記の例だとcontrollers.default_controller
を記載して、oeprationId
はメソッドのみを記載するといった使い方ができそうだけど、URL直下に書くと以下のようなエラーが起きる。
Traceback (most recent call last): File "app.py", line 8, in <module> app.add_api('swagger.yaml') File "/Users/hermesian/.anyenv/envs/pyenv/versions/dev352/lib/python3.5/site-packages/connexion/app.py", line 146, in add_api debug=self.debug) File "/Users/hermesian/.anyenv/envs/pyenv/versions/dev352/lib/python3.5/site-packages/connexion/api.py", line 102, in __init__ validate_spec(spec) File "/Users/hermesian/.anyenv/envs/pyenv/versions/dev352/lib/python3.5/site-packages/swagger_spec_validator/validator20.py", line 88, in validate_spec validate_apis(apis, bound_deref) File "/Users/hermesian/.anyenv/envs/pyenv/versions/dev352/lib/python3.5/site-packages/swagger_spec_validator/validator20.py", line 151, in validate_apis oper_params = deref(oper_body.get('parameters', [])) AttributeError: 'str' object has no attribute 'get'
ドキュメントではオペレーション直下に書いているけど、それならoperationId
だけで良さそう。
API仕様書のresponseを実装側で上書きしてしまう
API仕様書ではリターンコードが204だが、アプリケーション側で200を返すように書くと、後者で返される。レスポンスデータについても同様。 これ結構悩ましい問題。レスポンスのバリデーションが必要そう。
SwaggerでWeb APIを作る - Web APIの設計
Web APIの設計
Web APIの設計でExcelを使って定義書を作成していたが、仕様の変更等によって気がついたら設計書とシステムに乖離しているなんてことがあった。そのようなことがないようにWeb APIの定義情報を常に正とするようなアプリにしたい。
そこでSwaggerは、いくつかの企業によるコンソーシアムによって、Web APIの標準化を行うための規約とそのツール群を使う。
Swagger
Swaggerのサイトに行くとSwagger-editorやSwagger-UIなどツールがいくつかあるけど、swagger-nodeとbootprint-openapiで一通りできる。
GitHub - swagger-api/swagger-node: Swagger module for node.js
$ npm install -g swagger
GitHub - bootprint/bootprint-openapi: Bootprint-module to render OpenAPI specifications
$ npm install -g bootprint $ npm install -g bootprint-openapi $ npm -g install html-inline
手順としては、以下。
Swagger-editorでWeb API定義ファイルを作成する
はじめにプロジェクトのディレクトリを作る。
その際にNodeJSのwebフレームワークを選択させられる。これは後で作るモックサーバをカスタマイズするときに改造していくものである(Flaskが対応していたら便利だけど)。
express
を選択する。
$ swagger project create example
次に生成されたディレクトリに降りて、エディタを起動する。
$ cd example/ $ swagger project edit
Webブラウザが起動し、エディタが表示される。
GUIに対応できない環境(x windowを起動していないサーバなど)で利用するときは、-s | --silent
を付ける。
今回はSwagger Editorのpetstores_simple.yamlを使う。
ドキュメント化する
bootprint-openapiでSwaggerファイルをHTML化する。
$ pwd example $ bootprint openapi api/swagger/swagger.yaml target
するとtargetディレクトリ配下にhtml, css, javascriptが書き出され、適当なwebサーバに配置することで閲覧できる。 さらにcss, javascriptをHTMLにまとめたいときは下記のようにし、dist.htmlを公開すれば良い。
$ html-inline target/index.html > target/dist.html
モックサーバを立てる
モックサーバはサーバ機能をまだ実装していない段階で、クライアント側の仕様検討、実装を待ちにすることなく行う目的で建てる。
$ pwd example $ swagger project start -m
クライアント側(同一端末だが)でAPIを叩く。
(2016.12.12 修正: 下記で/petsを叩いていたのだけど、そもそもbathPath: /api
なので、
そのようなAPIはないのは当たり前だった。
$ curl -i http://localhost:10010/api/pets HTTP/1.1 404 Not Found X-Powered-By: Express X-Content-Type-Options: nosniff Content-Type: text/html; charset=utf-8 Content-Length: 21 Date: Mon, 12 Dec 2016 04:50:26 GMT Connection: keep-alive Cannot GET /api/pets
おや? 同じ事象がRunning in mock modeのチュートリアル例でも起きる。
調べてみると、下記のissueに遭遇。x-swagger-router-controller
を指定しないとダメとのこと。
これを付けるのは、pathかoperationの階層で定義すれば良い。これとoperationId
でAPIから呼ばれるメソッドを指定する。
issueでも言われているけど、モックなのにこれを指定しないといけないのは使い勝手が悪い気が...。
Flaskでswaggerに沿ったサーバ機能を作ることができるconnexionでは、むしろoperationレベルでのみでxx-swagger-router-controller
を設定できる。これだとoperationId
のみでもう良くないかと思えてきた。なのでモック使うとき以外は特に使用しないことにする。
pathに含まれるoperationで全て同じとする。
..(省略).. /pets: x-swagger-router-controller: pets ..(省略).. /pets/{id}: x-swagger-router-controller: pets ..(省略)..
改めてクライアント側で叩く。 そのまま使うと決められた値(numberなら1, stringなら"Sample text")が返ってくる。 レスポンスをカスタマイズしたい場合は、Implementing mock mode controllersを参照。
$ curl -X GET -i http://localhost:10010/api/pets HTTP/1.1 200 OK X-Powered-By: Express Access-Control-Allow-Origin: * Content-Type: application/json Date: Sat, 24 Sep 2016 13:08:50 GMT Connection: keep-alive Content-Length: 51 [{"id":1,"name":"Sample text","tag":"Sample text"}] $ cat pet.json {"id":1,"name":"foo","tag":"dog"} $ curl -X POST --header "Content-Type: application/json" --header "Accept: application/json" -i http://localhost:10010/api/pets -d @pet.json HTTP/1.1 200 OK X-Powered-By: Express Access-Control-Allow-Origin: * Content-Type: application/json Date: Sat, 24 Sep 2016 13:29:25 GMT Connection: keep-alive Content-Length: 49 {"id":1,"name":"Sample text","tag":"Sample text"} $ curl -X GET -i http://localhost:10010/api/pets/1 HTTP/1.1 200 OK X-Powered-By: Express Access-Control-Allow-Origin: * Content-Type: application/json Date: Sat, 24 Sep 2016 13:36:02 GMT Connection: keep-alive Content-Length: 49 {"id":1,"name":"Sample text","tag":"Sample text"} $ curl -X DELETE -i http://localhost:10010/api/pets/1 HTTP/1.1 500 Internal Server Error X-Powered-By: Express Access-Control-Allow-Origin: * Content-Type: application/json Date: Sat, 24 Sep 2016 13:37:25 GMT Connection: keep-alive Content-Length: 196 {"message":"Response validation failed: void does not allow a value","code":"INVALID_TYPE","failedValidation":true,"path":["paths","/pets/{id}","delete","responses","204"],"originalResponse":"{}"}
最後だけこけた。どうやらレスポンスが空は許可されていないみたい。 対象療法だけど、レスポンスを追加した。
delete: description: deletes a single pet based on the ID supplied operationId: deletePet parameters: - name: id in: path description: ID of pet to delete required: true type: integer format: int64 responses: '200': description: pet deleted schema: $ref: '#/definitions/pet' default: description: unexpected error schema: $ref: '#/definitions/errorModel'
どうにか。
$ curl -X DELETE -i http://localhost:10010/api/pets/1 HTTP/1.1 200 OK X-Powered-By: Express Access-Control-Allow-Origin: * Content-Type: application/json Date: Sat, 24 Sep 2016 13:51:11 GMT Connection: keep-alive Content-Length: 49 {"id":1,"name":"Sample text","tag":"Sample text"}
サーバ機能の雛形を生成する
最後にAPI仕様書からサーバ機能の雛形を作る
swagger-nodeの方ではないSwagger-Editorを用いると、サーバ機能のダウンロードができる(swagger-codegenでも良い)。 インストールしても良いが、Dockerイメージがあるので、コンテナにて行う。
$ docker pull swaggerapi/swagger-editor $ docker run -p 80:8080 swaggerapi/swagger-editor
あとはhttp://(YOUR_CONTAINER_URI)にアクセスし、API仕様書の内容をコピー&ペーストして、[Generate Server]からお好みのフレームワークの雛形をダウンロードする。これからFlaskでサーバ機能を作るので、Flaskを選択する。
NvidiaのDeep Learning Quest(無料分)を受ける
すでにひとしきり広まった感があるDeep Learning。 Nvidiaがかなり本腰入れていて、それ向けの教育プログラムまである。
どういうものかは知っておきたいので、無料枠で受けれる「ディープラーニング入門」を受講してみよう。実際AWS上に構築された環境を触りながらできるハンズオン形式で、有料のプログラムもAWSの利用料金ぐらいの値段みたい(とNvidia Deep Learning Day 2016 Springで紹介されていた)。
私たちは、あなたがあなたのアプリケーションは、研究者や開発者として必要な最高のどのフレームワークスーツを決める手助けを目的とした深い学習> のための最も一般的なソフトウェアフレームワークを見学します。この見学では、あなたのアプリケーションに一番適したディープラーニングフレーム> ワークを決定する事をゴールにします。ディープラーニングの予備知識は必要ありません。
たしかにDLフレームワークは乱立している印象がある。Software links « Deep Learningを見ると38種もあるのか。
起動に4分程度かかるとのこと。Jupyter Notebook形式なのね。 始まるとなんとタイマーが起動して、55分の間だけ利用できるとのこと。
各DLフレームワークの採用基準も記載があった。 NVDLDでも下記のような比較がされてたけど、TheanoとTorch7の比較(性能面、機能面)があまり理解できていない。 これはもう使ってみないとわからない世界なのかも。 Lua言語の習得コスト考えるとTheanoとかPythonサポートしているものになりそうな印象。
GPUリソースをどうやって調達しようかな...。