20201128のdockerに関する記事は9件です。

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.sh
docker 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.yaml
openapi: 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: header

Swagger 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.0

3. Swagger UI起動

  • python-flask-server-generated/README.md
command.sh
docker 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.sh
open "http://localhost:8080/hello?your_name=nsuhara"
result.sh
"{\"result\": \"Hello, nsuhara\"}"

3. ビジネスロジックを検証 (Curl)

command.sh
curl -X GET "http://localhost:8080/hello?your_name=nsuhara" -H "accept: application/json"
result.sh
"{\"result\": \"Hello, nsuhara\"}"
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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の中のディレクトリで作業を行っていました。
一旦、ファイルの中を確認!
スクリーンショット 2020-11-28 19.51.17.png

ここで気になったのは、codeディレクトリの中に違う階層ではあるものの、同じpart2のディレクトリがあり、原因はここではないのか?と何の根拠もないのに自分の中で原因を発見できたと思ってしまいました。汗


記憶をたどると、作業前にFiderを開き、code/part2の中のファイルを直接コピーし、server-side/part2のファイルにペーストし、作業に取り掛かっていました。

その間、Dockerコンテナは最初から作動中だったので、移動前の状況のままになります(恐らく、、、)

なので、コピーしたファイルを移動し変更しても、Dockerコンテナの中は以前のままで反映さていないので、最初の項目(解決方法)を実行し、最新の状態にしてから、作業を行ったところ無事変更を行えました。


前提知識の不足や認識の間違いなど、ご指摘やアドバイスなどいただけましたら凄く有り難いので
お願いします!!!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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にログインしてみました。

3fc1ae307c06f5d7926694734fbcbf6b.png

確認してみるとlatin1とutf8が混在しているのが分かります。
そしてlatin1という文字コードは日本語を扱うことができないためこれをutf8に変更する必要があります。

解決方法

docker-compose.ymlを以下のように変更しました。(こちらの記事、およびdockerhub内mysqlの 公式リファレンス参照)

docker-compose.yml
version: '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/mysql

dbにcommandを追加することで文字コードの設定を渡せます。

再度dockerを立ち上げたところエラーが起きることなく、無事問題は解決しました!

最後に

dockerを始めたばかりの初心者でわからないところがわからない状態ですが、1歩づつ進んでいきます!!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[AWS][goofys][Docker] バックアップを考える

AWS EC2 上でのバックアップの仕組みを作ったので覚え書き。

バックアップとしては、特定のディレクトリを tar ball にアーカイブできればよいという単純な要件だけど、ちゃんと定時の仕組みにしようとすると面倒くさいものだったりします。

  • 固めた tar ball はどこに置くのか?
  • バックアップのサイクルは?(日次など)
  • 世代管理も必要ですよね?

他に、個人的な要望として、以下のようなものもありました。

  • OS へのセットアップはしたくない
  • できるだけ docker コンテナで~

ということで、以下の形で構成。

  • 格納先は S3 バケットへ
  • S3 へのアクセスは goofys マウントで
  • goofys マウントを docker コンテナ上で
  • バックアップのシェルスクリプトの実行を busybox コンテナ上で
  • 世代管理は自前のスクリプトで

すべてコンテナ上で実現したので、いくらかの設定ファイルとスクリプトだけ用意するだけで済みました。
世代管理のスクリプトは自前で面倒ですが、一度作っておくと再利用できて便利です。

通常、OS の設定が必要になる goofys マウントや世代管理の logrotate など使わなくてよい構成です。
こうしておけば、Kubernetes な環境になったときにも流用しやすいかなというのもあります。

fig-goofys-backup.png

全体のシーケンス

全体の流れは以下のステップとなりました。

  1. S3 バケットをマウント
  2. バックアップ実行
  3. S3 バケットのマウント解除

全体を一発で流せるようにとも考えたのですが、バックアップ完了後にマウント解除を連動するのがうまくいかなかったのと、リストア処理の連動などマウントだけを利用したいケースもあるのでこの形で。

S3 バケットをマウント

S3 バケットをファイルシステムとしてマウントする goofys を利用します。
以下のコンテナが用意されています。

https://github.com/cloudposse/goofys

これを使って docker-compose.yml を用意するだけで簡単に S3 バケットのマウントができます。

docker-compose.yml
version: '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.yml
version: '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
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

(コンテナの操作)プログラマのためのDocker教科書読む.4

稼働しているDockerコンテナの操作

本番環境の運用の時などで稼働しているコンテナの状態を確認したり、任意のプロセスを実行させたりする時がある。
プロセスとは実行状態にあるプログラム(実行してるアプリケーション)のこと

コンテナの中で新しいアプリケーションを実行したいときの操作

稼働コンテナへの接続

稼働しているコンテナ接続する。
接続したコンテナごと終了させる時はCtrl + C,
コンテナないで動くプロセスのみを終了させるときはCtrl + PCtrl + 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使うとコンテナも停止してしまうので注意すること。

参考文献

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

FFmpegをAWS Lambdaで動かす

ffmpegをAWS Lambdaで動かしてみました
何を思ったかRubyのラッパー使ってみました
途中結構躓くことが多かったので面倒でした

Dockerfile

Dockerfile
FROM 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

fix
RUN bundle config set --local path 'vendor/bundle'

ffmprobeの実行権限がないと言われた

windowsでzipファイルの作業してたらはまった。Lambdaのコンテナ上で圧縮しないと
実行権限の付与も忘れずに
RUN chmod -R a+x ../../ffmpeg_dist

x265のリポジトリがない

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.ExitError

START 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.ExitError

START 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で実行するにはこれらを含めなければならなかった。
つくづく動画をデジタル信号に変えるのはあらゆる処理が必要なのだなと感じた。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Dockerfile に bash のプロンプトの設定を書く

コンテナ上の開発環境で作業する時のプロンプトを色つけたり改行させたりしたかった。
image.png

エスケープの仕方がわからなかっただけなので、こんなやり方じゃなくてできるかもしれないけど、とりあえず動いたのでメモっておきます。

Dockerfile(抜粋)
FROM golang:1.15

ARG BASH_PROMPT="'\[\033[33m\]\n[\! \t \u@\h \w]\n\$ \[\033[37m\]'"

RUN ["bash", "-c", "echo export PS1=$BASH_PROMPT >> /root/.bashrc"]

CMD ["bash"]

こんな感じ
image.png

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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をインストール

  1. 以下の中身の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
    
  2. 作成されたnvidia-container-runtime-script.shを実行する。終わったら一旦docker deamonを再起動。

    $ sh nvidia-container-runtime-script.sh
    $ sudo systemctl restart docker
    
  3. docker run にgpusオプションがつけるのを確認:

    $ docker run --help | grep -i gpus
    --gpus gpu-request               GPU devices to add to the container ('all' to pass all GPUs)
    
  4. 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

スクリーンショット 2020-11-28 10-28-57.png

ぐるぐる回れば成功。おめでとう。また、QTアプリ起動する場合に環境変数QT_X11_NO_MITSHM=1 を設定する必要があります。

以上です。

次回を待て!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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.rst

Docker

あまり導入するライブラリが少ないので、直インストールでも良いとは思ったが、きっちりするためにpipenvを導入した。
pipenvとdockerコマンドの相性がどうも思わしくない、DockerfileはCMDに一つのコマンドしか設定できない、volumeの設定をしたい、との諸々の事情があり、docker-composeを利用することにした。

containers/python/Dockerfile
FROM 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 pipenv
docker-compose.yml
version: '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 --build

sphinx-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 = 120

docker-compose exec python pipenv run flake8 source/conf.py

rstcheck

reSTファイルのチェックに用いる

src/source/.rstcheck.cfg
[rstcheck]
report=warning

docker-compose exec python pipenv run rstcheck -r source

CircleCI

.circleci/config.yml
version: 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:
                - master

CircleCIの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.py
import 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

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む