- 投稿日:2020-11-28T23:36:17+09:00
SwaggerでREST APIを生成する
SwaggerでREST APIを生成する
はじめに
macOS環境の記事ですが、Windows環境も同じ手順になります。環境依存の部分は読み替えてお試しください。
目的
この記事を最後まで読むと、次のことができるようになります。
No. 概要 備考 1 Swaggerを理解する 2 Swagger EditorでAPIを設計する 3 Swagger CodegenでAPIを生成する python-flask 4 Swagger UIで仕様/定義を可視化する 5 Generated APIを検証する 実行環境
環境 Ver. macOS Catalina 10.15.6 Swagger 3.0.3 Docker 19.03.13 関連する記事
Swaggerを理解する
特徴
- REST APIを構築するためのオープンソースフレームワーク
- YAML/JSONでAPIを設計
- 設計をもとにAPIドキュメントの生成が可能
- 設計をもとに20以上の言語からスタブの生成が可能
API Tools
No. 概要 備考 Swagger Editor APIを設計するためのツール Swagger Codegen 設計からサーバ向けスタブ/クライアント向けSDKを生成するためのツール Generate Server, Generate Client Swagger UI 設計から仕様/定義を可視化(ドキュメント化)するためのツール Generate Server Swagger EditorでAPIを設計する
Case1: Swagger Editor (Web)
1. Swagger Editor表示
Case2: Swagger Editor (Docker)
1. Swagger Editor起動
command.shdocker stop $(docker ps -q) docker rm $(docker ps -q -a) docker rmi $(docker images -q) -f docker pull swaggerapi/swagger-editor docker run -d -p 80:8080 swaggerapi/swagger-editor*今回は
PORT:80
に割り当てる2. Swagger Editor表示
YAML - Hello World
hello_world.yamlopenapi: 3.0.3 info: title: Hello World description: Sample API for trying out Swagger termsOfService: http://localhost/termsOfService contact: email: na010210dv@gmail.com license: name: nsuhara url: http://localhost/license version: 0.0.1 externalDocs: description: Find out more about Swagger url: http://swagger.io servers: - url: http://localhost:8080 - url: http://localhost:80 tags: - name: hello description: Greeting API externalDocs: description: Find out more about hello url: http://localhost/hello - name: xxx description: xxx externalDocs: description: xxx url: http://localhost/xxx paths: /hello: get: tags: - hello summary: Return 'Hello, {your_name}' description: Return 'Hello, {your_name}' operationId: get_hello parameters: - name: your_name in: query description: The name displayed with Hello required: true schema: type: string responses: 200: description: Successful operation content: application/json: schema: $ref: '#/components/schemas/Hello' application/xml: schema: $ref: '#/components/schemas/Hello' 400: description: Invalid parameters content: {} 404: description: Not found content: {} /xxx: post: tags: - xxx summary: xxx operationId: post_xxx requestBody: description: xxx content: '*/*': schema: type: array items: $ref: '#/components/schemas/Xxx' required: true responses: default: description: Successful operation content: {} x-codegen-request-body-name: body get: tags: - xxx summary: xxx description: xxx operationId: get_xxx parameters: - name: xxx in: query description: xxx required: true schema: type: string responses: 200: description: Successful operation content: {} 400: description: Invalid parameters content: {} 404: description: Not found content: {} components: schemas: Hello: type: object properties: result: type: string example: Hello, {your_name} xml: name: Hello Xxx: type: object properties: id: type: integer format: int64 xxx: type: string xml: name: Xxx securitySchemes: petstore_auth: type: oauth2 flows: implicit: authorizationUrl: http://petstore.swagger.io/oauth/dialog scopes: write:pets: modify pets in your account read:pets: read your pets api_key: type: apiKey name: api_key in: headerSwagger CodegenでAPIを生成する
Swagger Editor > Generate Server > python-flask
*今回は
python-flask
環境を生成するSwagger UIで仕様/定義を可視化する
1. zip解凍
- python-flask-server-generated.zip
2. requirements編集 (connexion問題)
- python-flask-server-generated/requirements.txt
requirements.txt- connexion + connexion[swagger-ui] python_dateutil == 2.6.0 setuptools >= 21.0.03. Swagger UI起動
- python-flask-server-generated/README.md
command.shdocker build -t swagger_server . docker run -p 8080:8080 swagger_server*今回は
PORT:8080
に割り当てる4. Swagger UI表示
Generated APIを検証する
1. ビジネスロジック追加
- python-flask-server-generated/swagger_server/controllers/hello_controller.py
hello_controller.py+ import json import connexion import six from swagger_server.models.hello import Hello # noqa: E501 from swagger_server import util def get_hello(your_name): # noqa: E501 """Return 'Hello, {your_name}' Return 'Hello, {your_name}' # noqa: E501 :param your_name: The name displayed with Hello :type your_name: str :rtype: Hello """ - return 'do some magic!' + return json.dumps({ + 'result': 'Hello, {}'.format(your_name) + })2. ビジネスロジック検証 (Web browser)
command.shopen "http://localhost:8080/hello?your_name=nsuhara"
result.sh"{\"result\": \"Hello, nsuhara\"}"3. ビジネスロジックを検証 (Curl)
command.shcurl -X GET "http://localhost:8080/hello?your_name=nsuhara" -H "accept: application/json"result.sh"{\"result\": \"Hello, nsuhara\"}"
- 投稿日:2020-11-28T20:53:02+09:00
DockerでPHP開発をしている際に、vscodeでの実装変更が反映されない。
環境
macOS
DockerでPHPを使用解決方法
順序1
Docker コンテナの停止・削除 $docker-compose down順序2
Docker コンテナの起動 $docker-compose up -dこれだけでした汗
発生状況&原因
発生状況
vscodeでいつものようにPHPの勉強をしていた際に、vscodeで新たな実装変更を行いました。ターミナルで動作確認すると実装変更が反映されない状況になってしまいました。
このような状況に、、、汗
ss@saaaMPro src % docker-compose exec app php book_log.php Could not open input file: book_log.phpなので原因を探るため
ターミナルでのディレクトリの位置が間違っていないか?
ファイル名が間違っていないか?
エラー内容が、入力ファイルが開けないと言っている、、、、なぜ、、(泣)原因
ここでは、server-sideディレクトリのpart2の中のディレクトリで作業を行っていました。
一旦、ファイルの中を確認!
ここで気になったのは、codeディレクトリの中に違う階層ではあるものの、同じpart2のディレクトリがあり、原因はここではないのか?と何の根拠もないのに自分の中で原因を発見できたと思ってしまいました。汗
記憶をたどると、作業前にFiderを開き、code/part2の中のファイルを直接コピーし、server-side/part2のファイルにペーストし、作業に取り掛かっていました。
その間、Dockerコンテナは最初から作動中だったので、移動前の状況のままになります(恐らく、、、)
なので、コピーしたファイルを移動し変更しても、Dockerコンテナの中は以前のままで反映さていないので、最初の項目(解決方法)を実行し、最新の状態にしてから、作業を行ったところ無事変更を行えました。
前提知識の不足や認識の間違いなど、ご指摘やアドバイスなどいただけましたら凄く有り難いので
お願いします!!!
- 投稿日:2020-11-28T20:48:29+09:00
docker mysql接続時に「Illegal mix of collations」エラーが発生
開発環境にdockerを導入し、
docker-compose up
をした後にログインをしようとしたところエラーが発生。データベースに接続できなくなってしまったので、その時の対処法を記録として残します。エラー内容
ActiveRecord::StatementInvalid (Mysql2::Error: Illegal mix of collations (latin1_swedish_ci,IMPLICIT) and (utf8_general_ci,COERCIBLE) for operation '='):latin1という文字コードとutf8という文字コードが混在していることが原因で起きているエラーのようです。
本当に混在しているか確認するため、こちらの記事を参考にDocker上でMySQLにログインしてみました。
確認してみるとlatin1とutf8が混在しているのが分かります。
そしてlatin1という文字コードは日本語を扱うことができないためこれをutf8に変更する必要があります。解決方法
docker-compose.yml
を以下のように変更しました。(こちらの記事、およびdockerhub内mysqlの 公式リファレンス参照)docker-compose.ymlversion: '3' services: db: image: mysql:5.7 ↓↓↓# 文字コードをutf8に指定 この1行を追加する↓↓↓ command: mysqld --character-set-server=utf8 --collation-server=utf8_unicode_ci environment: MYSQL_USER: root MYSQL_PASSWORD: # 仮想コンテナにアクセスするためのポート番号 ports: - "3306:3306" # データを永続化する(ホスト側のdb/mysql/volumesをコンテナ側の/var/lib/mysqlに同期する) volumes: - mysql-data:/var/lib/mysqldbにcommandを追加することで文字コードの設定を渡せます。
再度dockerを立ち上げたところエラーが起きることなく、無事問題は解決しました!
最後に
dockerを始めたばかりの初心者でわからないところがわからない状態ですが、1歩づつ進んでいきます!!
- 投稿日:2020-11-28T19:18:31+09:00
[AWS][goofys][Docker] バックアップを考える
AWS EC2 上でのバックアップの仕組みを作ったので覚え書き。
バックアップとしては、特定のディレクトリを tar ball にアーカイブできればよいという単純な要件だけど、ちゃんと定時の仕組みにしようとすると面倒くさいものだったりします。
- 固めた tar ball はどこに置くのか?
- バックアップのサイクルは?(日次など)
- 世代管理も必要ですよね?
他に、個人的な要望として、以下のようなものもありました。
- OS へのセットアップはしたくない
- できるだけ docker コンテナで~
ということで、以下の形で構成。
- 格納先は S3 バケットへ
- S3 へのアクセスは goofys マウントで
- goofys マウントを docker コンテナ上で
- バックアップのシェルスクリプトの実行を busybox コンテナ上で
- 世代管理は自前のスクリプトで
すべてコンテナ上で実現したので、いくらかの設定ファイルとスクリプトだけ用意するだけで済みました。
世代管理のスクリプトは自前で面倒ですが、一度作っておくと再利用できて便利です。通常、OS の設定が必要になる goofys マウントや世代管理の logrotate など使わなくてよい構成です。
こうしておけば、Kubernetes な環境になったときにも流用しやすいかなというのもあります。全体のシーケンス
全体の流れは以下のステップとなりました。
- S3 バケットをマウント
- バックアップ実行
- S3 バケットのマウント解除
全体を一発で流せるようにとも考えたのですが、バックアップ完了後にマウント解除を連動するのがうまくいかなかったのと、リストア処理の連動などマウントだけを利用したいケースもあるのでこの形で。
S3 バケットをマウント
S3 バケットをファイルシステムとしてマウントする goofys を利用します。
以下のコンテナが用意されています。https://github.com/cloudposse/goofys
これを使って docker-compose.yml を用意するだけで簡単に S3 バケットのマウントができます。
docker-compose.ymlversion: '3.8' services: goofys: image: cloudposse/goofys privileged: true environment: - REGION=ap-northeast-1 - BUCKET=my-backup-backet - UID=1000 - GID=1000 volumes: - /backup_store:/mnt/s3:shared$ docker-compose up -d # S3 バケットをマウント $ ls /backup_store/ # バケットを参照できるようになる : $ docker-compose down # S3 バケットのマウント解除 $ sudo umount /backup_store # ホスト上でのマウントが解除されないのでアンマウントgoofys コンテナ内で /mnt/s3 へ S3 バケットがマウントされます。
このポイントをホストへの外部ボリュームにしておくことで、ホスト側で S3 バケットへのファイルシステム参照ができるようになります。バックアップスクリプト実行
バックアップ処理自体はシェルスクリプトを作成し、スクリプトの実行を busybox コンテナで動かします。
これも docker-compose.yml を用意しておくと簡単。環境変数設定、コマンド名はスクリプトの内容による
docker-compose.command.ymlversion: '3.8' services: backup-base: &base image: busybox environment: - ENV_BACKUP_TARGET_DIR=/backup_target # バックアップ対象 - ENV_BACKUP_STORE_DIR=/backup_store/ # バックアップ先 (S3 マウント先) - ENV_BACKUP_DAYS=5 # 世代指定 volumes: - /backup_store:/backup_store:slave - /data:/backup_target # バックアップ対象(ホスト上のディレクトリ) - ./backup.sh:/backup.sh # バックアップ用のスクリプト - ./restore.sh:/restore.sh # リストア用のスクリプト backup-store: <<: *base command: - "sh" - "/backup.sh" backup-restore: <<: *base command: - "sh" - "/restore.sh"$ docker-compose -f docker-compose.command.yml run backup-store # バックアップを実行 $ docker-compose -f docker-compose.command.yml run backup-restore # リストアを実行これらの docker-compose のコマンドイメージを Makefile などにまとめておくと便利です。
なお、busybox には bash ではなく ash という組み込み用のコンパクトなシェルが入っているので、シェルスクリプトを書く際には注意が必要です。
おまけ: 世代管理処理のサンプル
対象ファイルと保存する世代数を指定して実行する形です。(日次実行)
function rotate() { _tgd=$1 _tgf=$2 _days=$3 _now=$( date +%Y/%m/%d ) _nod=$( date +%Y%m%d ) _thd=$( date +%Y%m%d -d "1970.01.01-00:00:$(( $( date +%s ) - $(( ${_days} * 24*60*60 )) ))" ) if test -f ${_tgd}/${_tgf} ; then _mod=$( stat -c%y ${_tgd}/${_tgf} | cut -d " " -f1 | sed -e 's/[-:]//g' ) mv ${_tgd}/${_tgf} ${_tgd}/${_tgf}-${_mod} fi _a="" ( cd ${_tgd} ; ls -1 ${_tgf}-* 2>&1 > /dev/null ) if test $? -eq 0 ; then _a=$( cd ${_tgd} ; ls -1 ${_tgf}-* | sort -r ) fi _t=${_tgf}-${_thd} for _i in ${_a} do if test ${_i} \< ${_t} || test ${_i} = ${_t} ; then rm -f ${_tgd}/${_i} fi done }以下のように実行すると、ファイルの作成日別のファイルにリネームし、指定した世代分だけ残して他を削除します。
rotate /backup_store aaa.tar.gz 3 ---- /backup_store/aaa.tar.gz-20201128 /backup_store/aaa.tar.gz-20201127 /backup_store/aaa.tar.gz-20201126なお、ash 用に考慮が必要だったのは以下の点。
# -d に "- 5 day" のような形式が使えない date -d "2020/11/28 - 5 day"# 配列が使えない array=()# if [[ ]] が使えない if [[ $i < $t >]] ; then : fi
- 投稿日:2020-11-28T15:05:06+09:00
(コンテナの操作)プログラマのためのDocker教科書読む.4
稼働しているDockerコンテナの操作
本番環境の運用の時などで稼働しているコンテナの状態を確認したり、任意のプロセスを実行させたりする時がある。
※プロセス
とは実行状態にあるプログラム(実行してるアプリケーション)のことコンテナの中で新しいアプリケーションを実行したいときの操作
稼働コンテナへの接続
稼働しているコンテナ接続する。
接続したコンテナごと終了させる時はCtrl + C
,
コンテナないで動くプロセスのみを終了させるときはCtrl + P
→Ctrl + Q
。nginxのイメージからコンテナを作って起動
$ docker container run --name webserver -d -p 80:80 nginxコンテナに接続する
$ docker container attach webserver・・・反応がないので接続できてるのかできていないのか確認できなかった。
コンテナ内で動いているプロセスがなかったから?稼働コンテナでプロセス実行
Webサーバーのようにバックグラウンドで実行しているコンテナにアクセスしたいとき、
docker container attach
コマンドで接続しても、シェルが動作していない場合はコマンドを受け付けない。
なのでdocker container exec
コマンドを使い任意コマンドを実行する。
このdocker container exec
コマンドは起動中のコンテナのみ実行できる。コンテナを起動
$ docker container start webserverシェルを起動させる
$ docker exec -it webserver /bin/bash root@e243f26ebcce:/#echoコマンドでHelloWorld
$ root@e243f26ebcce:/# echo "HelloWorld" HelloWorld $ root@e243f26ebcce:/#コマンドをDocker内のシェルを使って直接実行する
$ docker exec -it webserver /bin/echo "HelloWorld" HelloWorld $コンテナのプロセスを確認
実行しているプロセスのPIDとUSERと実行しているコマンドが表示される。
PIDは、Linuxプロセス識別子のことで一意の値。$ docker container top webserver PID USER TIME COMMAND 6314 root 0:00 nginx: master process nginx -g daemon off; 6362 101 0:00 nginx: worker processコンテナのポート転送確認
$ docker container port webserver 80/tcp -> 0.0.0.0:80コンテナの80番ポートがホストの80番ポートに転送されている。
コンテナの名前変更
webserverからwebserver2に名前を変更する。
$ docker container rename webserver webserver2 $ docker container ls CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES e243f26ebcce nginx "/docker-entrypoint.…" 52 minutes ago Up 21 minutes 0.0.0.0:80->80/tcp webserver2コンテナ操作の差分を確認
コンテナ内で何らかの操作を行い、コンテナがイメージから生成されたときからの差分が確認できる。
差分が出たときの区分一覧
変更の区分 説明 A ファイル追加 D ファイル削除 C ファイル更新 $ docker container diff webserver2 C /run A /run/nginx.pid C /etc C /etc/nginx C /etc/nginx/conf.d C /etc/nginx/conf.d/default.conf C /var C /var/cache C /var/cache/nginx A /var/cache/nginx/fastcgi_temp A /var/cache/nginx/proxy_temp A /var/cache/nginx/scgi_temp A /var/cache/nginx/uwsgi_temp A /var/cache/nginx/client_tempコンテナ内のファイルをコピー
コンテナ内のファイルをホストにコピーするとき。
docker container cp コピー元 コピー先
以下はコンテナ内からホストへコピー。
$ docker container cp webserver2:/etc/nginx/nginx.conf /tmp/nginx.confホストからコンテナ内へコピー。
$ docker container cp /tmp/nginx.conf webserver2:/etc/nginx/nginx.confまとめ
dockerコンテナの中に入ることができる。
仮想環境でターミナル操作しているのと同じイメージ。
コンテナの中のプロセスを起動してそれを操作できる。
本番で稼働しているコンテナにattachで入って抜けるときにCtrl + C
使うとコンテナも停止してしまうので注意すること。参考文献
- 投稿日:2020-11-28T14:20:10+09:00
FFmpegをAWS Lambdaで動かす
ffmpegをAWS Lambdaで動かしてみました
何を思ったかRubyのラッパー使ってみました
途中結構躓くことが多かったので面倒でしたDockerfile
DockerfileFROM lambci/lambda:build-ruby2.7 RUN gem install bundler -p WORKDIR /var/task RUN export TASK=/var/task RUN mkdir ffmpeg_sources RUN mkdir ffmpeg_dist RUN mkdir bin RUN yum install autoconf automake bzip2 bzip2-devel cmake freetype-devel gcc gcc-c++ git libtool make pkgconfig zlib-devel -y RUN yum install -y libpng-devel WORKDIR /var/task/ffmpeg_sources RUN curl -O -L https://www.nasm.us/pub/nasm/releasebuilds/2.14.02/nasm-2.14.02.tar.bz2 RUN tar xjvf nasm-2.14.02.tar.bz2 WORKDIR nasm-2.14.02 RUN ./autogen.sh RUN ./configure --prefix="$TASK/ffmpeg_build" --bindir="$TASK/bin" RUN make RUN make install RUN WORKDIR /var/task/ffmpeg_sources RUN curl -O -L https://www.tortall.net/projects/yasm/releases/yasm-1.3.0.tar.gz RUN tar xzvf yasm-1.3.0.tar.gz WORKDIR yasm-1.3.0 RUN ./configure --prefix="$TASK/ffmpeg_build" --bindir="$TASK/bin" RUN make RUN make install WORKDIR /var/task/ffmpeg_sources RUN git clone --depth 1 https://code.videolan.org/videolan/x264.git WORKDIR x264 RUN PKG_CONFIG_PATH="$TASK/ffmpeg_build/lib/pkgconfig" ./configure --prefix="$TASK/ffmpeg_build" --bindir="$TASK/bin" --enable-static RUN make RUN make install WORKDIR /var/task/ffmpeg_sources RUN git clone https://bitbucket.org/multicoreware/x265_git.git WORKDIR /var/task/ffmpeg_sources/x265_git/source RUN cmake -G "Unix Makefiles" -DCMAKE_INSTALL_PREFIX="$TASK/ffmpeg_build" -DENABLE_SHARED:bool=off . RUN make RUN make install WORKDIR /var/task/ffmpeg_sources RUN git clone --depth 1 https://github.com/mstorsjo/fdk-aac WORKDIR fdk-aac RUN autoreconf -fiv RUN ./configure --prefix="$TASK/ffmpeg_build" --disable-shared RUN make RUN make install WORKDIR /var/task/ffmpeg_sources RUN curl -O -L https://downloads.sourceforge.net/project/lame/lame/3.100/lame-3.100.tar.gz RUN tar xzvf lame-3.100.tar.gz WORKDIR lame-3.100 RUN ./configure --prefix="$TASK/ffmpeg_build" --bindir="$TASK/bin" --disable-shared --enable-nasm RUN make RUN make install WORKDIR /var/task/ffmpeg_sources RUN curl -O -L https://archive.mozilla.org/pub/opus/opus-1.3.1.tar.gz RUN tar xzvf opus-1.3.1.tar.gz WORKDIR opus-1.3.1 RUN ./configure --prefix="$TASK/ffmpeg_build" --disable-shared RUN make RUN make install WORKDIR /var/task/ffmpeg_sources RUN git clone --depth 1 https://chromium.googlesource.com/webm/libvpx.git WORKDIR libvpx RUN ./configure --prefix="$TASK/ffmpeg_build" --disable-examples --disable-unit-tests --enable-vp9-highbitdepth --as=yasm RUN make RUN make install WORKDIR /var/task/ffmpeg_sources RUN curl -O -L https://ffmpeg.org/releases/ffmpeg-snapshot.tar.bz2 RUN tar xjvf ffmpeg-snapshot.tar.bz2 WORKDIR ffmpeg RUN PATH="$TASK/bin:$PATH" PKG_CONFIG_PATH="$TASK/ffmpeg_build/lib/pkgconfig" ./configure \ --prefix="$TASK/ffmpeg_build" \ --pkg-config-flags="--static" \ --extra-cflags="-I$TASK/ffmpeg_build/include" \ --extra-ldflags="-L$TASK/ffmpeg_build/lib" \ --extra-libs=-lpthread \ --extra-libs=-lm \ --bindir=../../ffmpeg_dist \ --enable-gpl \ --enable-libfdk_aac \ --enable-libfreetype \ --enable-libmp3lame \ --enable-libopus \ --enable-libvpx \ --enable-libx264 \ --enable-libx265 \ --enable-nonfree RUN make RUN make install RUN chmod -R a+x ../../ffmpeg_dist RUN mkdir /var/task/dist RUN cp -r ../../ffmpeg_dist/* /var/task/dist RUN yum install -y yum-utils rpmdevtools WORKDIR /tmp RUN yumdownloader unixODBC.x86_64 libtool-ltdl.x86_64 gnutls.x86_64 \ bzip2-libs.x86_64 freetype.x86_64 libpng.x86_64 RUN rpmdev-extract *rpm RUN cp /tmp/*/usr/lib64/* /var/task/dist WORKDIR /var/task/dist COPY . . RUN bundle config set --local path 'vendor/bundle' RUN bundle install RUN zip -r dist.zip . RUN mkdir /var/task/output CMD cp dist.zip /var/task/output上のDockerfileと一緒のディレクトリにlambda_functionをおいて
docker build . -t lambda_ruby_ffmpeg docker run -v "{PWD}":/var/task/output lambda_ruby_ffmpegでstreamio-ffmpegが使えます
githubの方にも上げておいたのでlambda_function.rbを編集してdocker上でビルドして走らせるだけでデプロイパッケージのzipが出てくると思います。
ただrequireはbundleに依存するのでrequire 'bundler' Bundler.requireは忘れないでください。
それとバイナリにパスは通っていないはずなのでカレントディレクトリ(/var/task)にパスを通してください躓いたところ
以下文体崩壊
--pathが古い
bundle install --path vendor/bundleを実行したら--pathが古いといわれた。AWSの公式サンプルにも乗っていたのにそれでいいのかAmazon
fixRUN bundle config set --local path 'vendor/bundle'ffmprobeの実行権限がないと言われた
windowsでzipファイルの作業してたらはまった。Lambdaのコンテナ上で圧縮しないと
実行権限の付与も忘れずに
RUN chmod -R a+x ../../ffmpeg_distx265のリポジトリがない
MercurialというGitに似たものがあったらしい(無知)。
これを見ればわかる通りffmpegの公式のビルド手順にもx265のビルドにはMercurialを使うように書かれているが、
公式(かどうかはわからないが)のx265のビルド方法を見てみると gitを使っていた。
Mercurialからgitに乗り換えるという歴史的な瞬間に立ち会ってしまった。
考えてみればGitもBitKeeperに触発それて作られているわけでほかにも分散型バージョン管理システムがあっても不思議じゃない。
AWSにせよなんにせよクラウドサービスの多くがgithubフレンドリーな姿勢になるほどGitはデファクトスタンダードになっているが、その一方で他の分散型バージョン管理システムのシェアを奪っているんだなあと寂寥感に苛まれた。
そして苦労して入れたところでおそらく使われない。
ffmpegで必要なライブラリ、例えばLameとかもgitで管理されているわけではなくsourceforgeで進められていて、tarボールをcurlするような形になっていて常に最新のバージョンをとってこられるよう指定できない。そう考えたらgitもMercurialもいい感じなのになあ。* .so. * エラー
「2021年までにモバイルトラフィックの80%は動画で占められます」
https://media.kaizenplatform.com/n/n51e413c8087c
これほどまでに動画は我々の世界に身近になったけれど、動画を再生するまでに何があるのか知っている人はほとんどいない
ffmpegはビルドすると30MBほどになる巨大なプログラムだが、動かすにはまだ足りない。
これらの
START RequestId: effdb447-3a9a-4fa4-832e-e7359370d9d0 Version: $LATEST
ffprobe: error while loading shared libraries: libfreetype.so.6: cannot open shared object file: No such file or directory
END RequestId: effdb447-3a9a-4fa4-832e-e7359370d9d0
REPORT RequestId: effdb447-3a9a-4fa4-832e-e7359370d9d0 Duration: 2249.69 ms Billed Duration: 2300 ms Memory Size: 128 MB Max Memory Used: 113 MB Init Duration: 724.21 ms
RequestId: effdb447-3a9a-4fa4-832e-e7359370d9d0 Error: Runtime exited with error: exit status 127
Runtime.ExitErrorSTART RequestId: 51a2ee5f-3cfc-4647-a1cf-58096076e7b6 Version: $LATEST
ffprobe: error while loading shared libraries: libbz2.so.1: cannot open shared object file: No such file or directory
END RequestId: 51a2ee5f-3cfc-4647-a1cf-58096076e7b6
REPORT RequestId: 51a2ee5f-3cfc-4647-a1cf-58096076e7b6 Duration: 2024.82 ms Billed Duration: 2100 ms Memory Size: 128 MB Max Memory Used: 111 MB Init Duration: 748.18 ms
RequestId: 51a2ee5f-3cfc-4647-a1cf-58096076e7b6 Error: Runtime exited with error: exit status 127
Runtime.ExitErrorSTART RequestId: 47eaba0a-f985-4103-a452-935a78e4e6e1 Version: $LATEST
ffprobe: error while loading shared libraries: libpng15.so.15: cannot open shared object file: No such file or directory
END RequestId: 47eaba0a-f985-4103-a452-935a78e4e6e1
REPORT RequestId: 47eaba0a-f985-4103-a452-935a78e4e6e1 Duration: 2530.37 ms Billed Duration: 2600 ms Memory Size: 128 MB Max Memory Used: 128 MB Init Duration: 887.27 ms
RequestId: 47eaba0a-f985-4103-a452-935a78e4e6e1 Error: Runtime exited with error: exit status 127
Runtime.ExitErrorエラーメッセージは実行しようとしたときに必要になった共通ライブラリ。lambdaで実行するにはこれらを含めなければならなかった。
つくづく動画をデジタル信号に変えるのはあらゆる処理が必要なのだなと感じた。
- 投稿日:2020-11-28T11:21:26+09:00
Dockerfile に bash のプロンプトの設定を書く
- 投稿日:2020-11-28T10:40:54+09:00
nvidia gpuを利用したdockerイメージの作り方、環境構築仕方
2020年の自分のやり方をメモます。dockerコンテナ内にgpuを利用したアプリ実行するため、
- ホスト環境の整備
- ホスト環境に合わせたdockerイメージの作成
- docker起動時に適切な引数、環境変数を与える
が必要です。順に解説していきます
ホスト環境の整備
自分の環境がubuntu20.04で、これでベースにして説明します。
dockerのインストール
説明割愛
nvidiaのドライバーのインストール
20.04または18.04の環境において、以下のスクリプトで一発で入れます。
$ sudo update-pciids $ echo -e "blacklist nouveau\noptions nouveau modeset=0" > /etc/modprobe.d/blacklist-nvidia-nouveau.conf $ sudo update-initramfs -u $ sudo ubuntu-drivers autoinstall $ sudo prime-select nvidia終わったら再起動し、nvidia-smiコマンドでgpuが使えているを確認。
nvidia container-runtime toolをインストール
以下の中身のnvidia-container-runtime-script.shを作成(https://collabnix.com/introducing-new-docker-cli-api-support-for-nvidia-gpus-under-docker-engine-19-03-0-beta-release/ 参照)
curl -s -L https://nvidia.github.io/nvidia-container-runtime/gpgkey | \ sudo apt-key add - distribution=$(. /etc/os-release;echo $ID$VERSION_ID) curl -s -L https://nvidia.github.io/nvidia-container-runtime/$distribution/nvidia-container-runtime.list | \ sudo tee /etc/apt/sources.list.d/nvidia-container-runtime.list sudo apt-get update sudo apt-get install nvidia-container-runtime作成されたnvidia-container-runtime-script.shを実行する。終わったら一旦docker deamonを再起動。
$ sh nvidia-container-runtime-script.sh $ sudo systemctl restart dockerdocker run にgpusオプションがつけるのを確認:
$ docker run --help | grep -i gpus --gpus gpu-request GPU devices to add to the container ('all' to pass all GPUs)nvidia-container-runtime-hook実行ファイルがあるのを確認:
$ which nvidia-container-runtime-hook /usr/bin/nvidia-container-runtime-hook
ホスト環境に合わせたdockerイメージの作成
nvidia公式dockerhubから自分のosとnvidiaドライバーバージョンに一致したイメージをベースにしてDockerfileを作成:
https://ngc.nvidia.com/catalog/containers/nvidia:cudagl/tags例えば自分の環境がubuntu20.04でCUDA Versionが11.0なので、nvidia/cudagl:11.0-base-ubuntu20.04 このイメージをベースにします。
Dockerfileサンプル:FROM nvidia/cudagl:11.0-base-ubuntu20.04 # Run a full upgrade and install utilities for development. ENV DEBIAN_FRONTEND=noninteractive RUN apt-get update && apt-get install -y tzdata # timezone setting ENV TZ=Asia/Tokyo RUN apt-get update && apt-get upgrade -y && apt-get install -y \ mesa-utilsこのdockerfileを
$ docker build --network=host -t foo-gpu-image .でbuildします。
docker起動時に適切な引数、環境変数を与える
先ほど作成したdockerイメージを以下のスクリプトで起動
#!/bin/bash xhost +si:localuser:root docker run -it --name foo_container --rm --gpus all \ --privileged \ --env="DISPLAY" \ -e QT_X11_NO_MITSHM=1 \ foo-gpu-image /bin/bashで、サンプルのopengl歯車起動
# glxgears
ぐるぐる回れば成功。おめでとう。また、QTアプリ起動する場合に環境変数QT_X11_NO_MITSHM=1 を設定する必要があります。
以上です。
次回を待て!
- 投稿日:2020-11-28T09:31:42+09:00
Sphinx + Docker + CircleCIで自動ビルド/デプロイ
導入
Sphinxは拡張性が高いドキュメントツールとしてITエンジニアから愛されている。[要出典]
ただ、「reStructuredTextの書き方がわからない」「ビューワーがなく、いちいちビルドするのがめんどくさい」等否定的な意見が多いのも確かである。
今回はめんどくさがりなエンジニア、あまりコード触れない事務寄りの人達の為にローカルでの自動ビルド環境を整えた。
また、CircleCIを使ってCI/CDする。環境
ディレクトリ構成
ディレクトリ構成は以下。
tree -a
をベースに修正。. ├── .circleci │ └── config.yml ├── .gitignore ├── README.md ├── build │ └── .gitkeep ├── containers │ └── python │ └── Dockerfile ├── docker-compose.yml └── src ├── .circleci │ └── comments.py ├── .venv ├── Makefile ├── Pipfile ├── Pipfile.lock ├── setup.cfg └── source ├── .rstcheck.cfg ├── _static │ └── css │ └── my_theme.css ├── _templates ├── conf.py └── index.rstDocker
あまり導入するライブラリが少ないので、直インストールでも良いとは思ったが、きっちりするためにpipenvを導入した。
pipenvとdockerコマンドの相性がどうも思わしくない、DockerfileはCMDに一つのコマンドしか設定できない、volumeの設定をしたい、との諸々の事情があり、docker-compose
を利用することにした。containers/python/DockerfileFROM python:3.9-buster ENV PIPENV_INSTALL_TIMEOUT=9000 ENV PIPENV_VENV_IN_PROJECT=1 RUN apt-get update && apt-get install -y python-pip RUN /usr/local/bin/python -m pip install --upgrade pip RUN pip install pipenvdocker-compose.ymlversion: '3' services: python: build: context: . dockerfile: ./containers/python/Dockerfile working_dir: '/var/src/' tty: true volumes: - ./src:/var/src - ./build:/var/build ports: - "8000:8000" command: > bash -c "pipenv install --dev && pipenv run sphinx-autobuild --host 0.0.0.0 --port 8000 /var/src/source /var/build/html"Dockerfileではpipとpipenvのインストールにとどめ、パッケージのインストールはdocker-compose.ymlに記述。pipenvはパッケージのインストールに時間がかかる場合が多いので、
PIPENV_INSTALL_TIMEOUT=9000
を設定している。
service名をpython
としているが、この時内部でコマンドを叩きたい時にdocker-compose exec python <command>
となり、pythonを使う際はdocker-compose exec python pipenv run python 〜
となり、混乱の元なので、サービス名は区別がつく名前にすることを推奨したい。Sphinx
ライブラリ設定
src/Pipfile[[source]] name = "pypi" url = "https://pypi.org/simple" verify_ssl = true [dev-packages] flake8 = "*" rstcheck = "*" requests = "*" [packages] sphinx = "*" sphinx-rtd-theme = "*" sphinx-autobuild = "*" [requires] python_version = "3.9"
- dev-packages
- flake8
- 主にsrc/source/conf.pyのチェック
- rstcheck
- .rst形式のファイルのチェック
- requests
- bitbucketのPull RequestへのComment APIを送信するのに使う
- packages
- sphinx
- ドキュメントを作成する根幹ライブラリ
- sphinx-rtd-theme
- Read the Docsテーマ。好み次第。
- sphinx-autobuild
- ファイルをウォッチして自動ビルドを行う
conf.py""" デフォルトコメント部分を省略して表示 """ import sphinx_rtd_theme project = 'project_name' copyright = 'year, author_name' author = 'author_name' release = 'version' extensions = [] templates_path = ['_templates'] language = 'ja' exclude_patterns = [] html_theme = 'sphinx_rtd_theme' html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] html_static_path = ['_static'] html_style = 'css/my_theme.css' # defaultの表示に不満があるので継承したmy_themeを指定カスタムテーマ
Sphinx の sphinx_rtd_theme をカスタマイズするを利用
src/source/_static/css/my_theme.css@import url("theme.css"); .wy-nav-content { max-width: none; } h1,h2,h3,h4,h5,h6 { border-bottom: 1px solid #ccc; } .wy-table-responsive table td, .wy-table-responsive table th { white-space: normal; } colgroup { display: none; }ビルド
docker-compose up -d --buildsphinx-autobuildがドキュメントをビルドしてbuild以下にhtmlを格納する。
docker-compose.ymlにてホスト側・コンテナ側のポートを共に8000番と指定している。
ホスト側のポートを変更する場合はdocker-compose.ymlのportsを<host-port>:8000
の形式で変更する。文法チェック
手元で事前にチェックすることでcircleciで通らないという事がないようにする。
pre-commitにフックするのは一々実行されるのが煩わしいので、手動で各自が実行することにした。
Pipfileの[script]に書いておいてコマンド化した方が良いかも。flake8
Pythonファイルのチェックに用いる
src/setup.cfg[flake8] max-line-length = 120docker-compose exec python pipenv run flake8 source/conf.pyrstcheck
reSTファイルのチェックに用いる
src/source/.rstcheck.cfg[rstcheck] report=warningdocker-compose exec python pipenv run rstcheck -r sourceCircleCI
.circleci/config.ymlversion: 2.1 orbs: aws-cli: circleci/aws-cli@1.3.0 jobs: build: working_directory: ~/python-ci docker: - image: circleci/python:3.9.0-buster environment: PIPENV_VENV_IN_PROJECT: true steps: # コミットのチェックアウト - checkout # パッケージのインストール - run: name: Install python test dependencies command: | pip install pipenv cd src && pipenv sync --dev # flake8 - run: name: run flake8 command: | cd src && pipenv run flake8 source/conf.py # rstcheck - run: name: run reST checking command: | cd src && pipenv run rstcheck -r source # ドキュメントをビルドする - run: name: build doc command: | cd src && pipenv run make clean html # artifactsにビルドしたドキュメントを保存 - store_artifacts: path: /home/circleci/python-ci/build/html # gitのコミットメッセージを環境変数に設定 - run: name: get commit message command: | echo 'export GIT_COMMIT_DESC=`git log --pretty=format:"%s - %an" -n 1 ${CIRCLE_SHA1}`'>> $BASH_ENV source $BASH_ENV # artifactsに保存したドキュメントのurlをプルリクエストにコメントする - run: name: send comment to pull requests command: | cd src && pipenv run python .circleci/comments.py - persist_to_workspace: root: . paths: - build deploy: working_directory: ~/python-ci executor: aws-cli/default steps: # build jobのworkspaceを引き継ぐ - attach_workspace: at: . # orbを利用してaws-cliをセットアップする。 - aws-cli/setup # S3のbucketにビルドしたhtmlをアップロードする - run: name: update html command: aws s3 sync --exact-timestamps --delete build/html/ s3://${AWS_S3_DOCS_BUCKET_NAME}/ # CloudFrontのcacheを強制的にクリアする - run: name: clear cache command: aws cloudfront create-invalidation --distribution-id ${DISTRIBUTION_ID} --paths "/*" workflows: version: 2 build_and_deploy: jobs: # <context_name>にはCircleCIのクレデンシャル情報を入れたcontextの名前を入れる - build: context: <context_name> - deploy: context: <context_name> requires: - build filters: branches: # masterブランチにコミットされた時のみdeployする only: - masterCircleCIの
Organization Settings>Contexts
にて保存できるcontextには
- aws-cliで用いるAWSクレデンシャル・リージョン情報
- AWS_ACCESS_KEY_ID
- AWS_SECRET_ACCESS_KEY
- AWS_DEFAULT_REGION
- deployするS3のバケット名
- AWS_S3_DOCS_BUCKET_NAME
- Cloudfrontのdistribution id
- DISTRIBUTION_ID
- Bitbucketのユーザ/アプリパスワード
- BITBUCKET_USER
- BITBUCKET_PASS
などを登録する。
事前にS3のバケット、CloudFrontの設定をしておき、aws-cli用クレデンシャルのIAMではS3とCloudFrontの変更権限を与える。htmlをhttpでホストするだけならbucket名をドメインで作成、静的ウェブサイトホスティングを有効にしてRoute53で関連づけるだけでよく、IP制限もbucketの設定変更で対応できる。但し、「今どきhttpなんですか?」と社内の人間に煽られた場合は、CloudFrontとS3をつなぎ、バージニア北部リージョンのAWS Certificate Manager取得した証明書を関連付けなければいけない。証明書の取得リージョンについてはCloudFrontの設定ページに記載があるが、最初に証明書を取得してからCloudFrontに進もうと考えていると、引っかかるので注意。
BitbucketのPull Requestにドキュメントのリンクをコメント
PRごとにCIでStorybookをビルドしてデザイナーとインタラクションまで作っていく話で紹介されているように、Pull Requestにコメントするのも実装した。
紹介されているように差分を取って該当箇所を、という感じではなく、ここでは単にindexを投げているが、CircleCIのページに行ってページのリストから探すよりはまだマシかな、と思った次第。src/.circleci/comments.pyimport os import json import requests """ プロジェクト依存変数 リポジトリのUUIDは https://api.bitbucket.org/2.0/repositories/<organization_name>/<project_name> あるいは https://bitbucket.org/<organization_name>/<project_name>/src/master/ でデベロッパーツールを開き、__initial_state__を検索 で取得可能 """ VCS_TYPE = 'bb' REPOSITORY_UUID = 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' PATH = '/home/circleci/python-ci/build/html/index.html' MESSAGE = ''' [CircleCI]{} {} ビルドされたドキュメント {}{} ''' def main(): pull_requests_str = os.environ.get('CIRCLE_PULL_REQUESTS', '') if pull_requests_str: user_name = os.environ.get('BITBUCKET_USER') user_pass = os.environ.get('BITBUCKET_PASS') project_name = os.environ.get('CIRCLE_PROJECT_REPONAME') build_number = os.environ.get('CIRCLE_BUILD_NUM') node_index = os.environ.get('CIRCLE_NODE_INDEX') comment = os.environ.get('GIT_COMMIT_DESC') sha1 = os.environ.get('CIRCLE_SHA1') url_base = f'https://{build_number}-{REPOSITORY_UUID}-{VCS_TYPE}.circle-artifacts.com/{node_index}' pull_requests = pull_requests_str.split(',') for pull_request in pull_requests: pull_request_list = pull_request.split('/') work_space = pull_request_list[-4] pull_request_number = pull_request_list[-1] url = f'https://api.bitbucket.org/2.0/repositories/' \ + f'{work_space}/{project_name}/pullrequests/{pull_request_number}/comments' print(url) headers = { 'content-type': 'application/json', } message = MESSAGE.format( comment, sha1, url_base, PATH ) payload = { 'content': { 'raw': message } } r = requests.post( url, auth=(user_name, user_pass), headers=headers, data=json.dumps(payload) ) print(f'status: {r.status_code}') print(r.text) else: print('there is no pull request') if __name__ == '__main__': main()pipenvの関係でsrc以下に入れている。
REPOSITORY_UUIDはBitbucketのサイト/APIから入手して書き換える。
artifactsのurlについては、CircleCI artifactsのurlをjobのスクリプト内で取得するを参考にされたし。BitbucketのPRにコメントをつけるAPIには困った。markdownやhtmlでコメントを送る機能があるようなのだがドキュメント通りに送ってもうまく行かなかった。rawでmarkdownを送り付けたらその通りになったので、↑のMessageではmarkdownで書いてrawに入れている。
f文字列でも良さそうだが、インデントによる挙動がわからなかったので、上部に書いてformatで代入した。参考文献
Sphinx
Sphinx の sphinx_rtd_theme をカスタマイズする
flake8
rstcheck
PRごとにCIでStorybookをビルドしてデザイナーとインタラクションまで作っていく話
Creates a new pull request comment. - Bitbucket API
How to post html comments on pull request via 2.0 api? - ATLASSIAN Comunity