- 投稿日:2019-07-28T23:18:01+09:00
Julia+Jupyter Notebook環境をDockerで構築
概要
JuliaをJupyter Notebookで使える環境を手軽に再現できるようにするため、Dockerで環境構築していきます。
Jupyter公式のDocker imageをベースにして、データの永続化やパッケージの自動追加をできるようにします。環境
- OS: Ubuntu 18.04
- Docker 18.09.8
手順
1. jupyter/datascience-notebookを取得
docker pull jupyter/datascience-notebook2. コンテナを起動
docker run \ -p 8888:8888 \ --user root \ --name mynotebook \ jupyter/datascience-notebook-pでポート番号、--userでユーザーの指定が必要です。コンテナ名の指定--nameは任意です。
3. Jupyter Notebookにアクセス
To access the notebook, open this file in a browser: file:///home/jovyan/.local/share/jupyter/runtime/nbserver-17-open.html Or copy and paste one of these URLs: http://df19b359c643:8888/?token=xxxxx or http://127.0.0.1:8888/?token=xxxxx表示されたURLをブラウザで開くとJupyter Notebookが使えます。
ちなみにJuliaだけでなくRとPythonも使用可能です。Jupyter公式のDocker imageについてはこのページが詳しいです。データの永続化
このままではコンテナでの作業が保存されないので、ローカルのディレクトリをマウントします。
docker run -p 8888:8888 \ -v ~/path/to/directory:/home/jovyan/work \ --user root \ --name mynotebook \ jupyter/datascience-notebook-vでマウントするディレクトリを指定します。ちなみにデフォルトユーザー名であるjovyanとはjupyterを使う人というような意味のようです。
パッケージの追加を自動化
1. Dockerfileの作成
よく使うパッケージを自動で追加できるようにちょっとしたDockerfileを作成します。
例としてPlotsとDifferentialEquationsを追加してみます。DockerfileFROM jupyter/datascience-notebook MAINTAINER yoshikiri # Plotsを追加 RUN julia -e 'using Pkg; Pkg.add("Plots"); using Plots' # DifferentialEquationsを追加 RUN julia -e 'using Pkg; Pkg.add("DifferentialEquations"); using DifferentialEquations'REPLを起動せずコマンドでjulia文を実行するには-eオプションを使います。
julia -e 'println("hello, julia")'2. DockerfileからDocker imageを作成
docker build -t notebook .上記コマンド実行後、新たにDocker imageが作成されたことが確認できます。imageのバージョンを指定しないとlatestになります。
$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE notebook latest 4233009a8db5 1 minutes ago 5.77GB3. Docker imageからコンテナを起動
docker run -p 8888:8888 \ -v ~/path/to/directory:/home/jovyan/work \ --user root \ --name mynotebook \ notebook最後のimage名を変更しただけです。
まとめ
- jupyter/datascience-notebookでJulia (とR, Python) + Jupyter Notebook環境を構築
- ボリュームをマウントしてデータを永続化
- パッケージを自動で追加するDocker imageをDockerfileから作成
- 作成したDockerfileをGitHubなどで共有すれば、どこでも同じ環境を構築可能
意見や間違いがあればコメントで教えていただけると大変ありがたいです。
参考文献
- 投稿日:2019-07-28T22:59:41+09:00
docker multistage buildとDockerHub
背景
Dockerのマルチステージビルド機能とDockerHubへのPushまでのメモ
用意するもの
- マルチステージビルドを用いたDockerfile
- => (sample) ryuichi1208/iniToJson
- DockerHubのアカウント
- => (sample) ryuichi1208/multi_stage_build
※ Docker 17.05以上が必要です。
Dockerfile
マルチステージを用いるDockerfileと使わないDockerfileを用意します。
マルチステージビルドについては下記をご参照ください
■ Use multi-stage buildsノーマルFROM golang:1.12.7-alpine LABEL multi_stage="no" WORKDIR /app RUN apk add --no-cache make && \ rm -rf /var/cache/apk/* && \ mkdir src COPY ./src ./src COPY ./Makefile . RUN make ENTRYPOINT ["./main"] CMD [""]マルチステージビルドFROM golang:1.12.7-alpine AS builder LABEL multi_stage="no" WORKDIR /app RUN apk add --no-cache make && \ rm -rf /var/cache/apk/* && \ mkdir src COPY ./src ./src COPY ./Makefile . RUN make FROM busybox LABEL multi_stage="yes" WORKDIR /app COPY --from=builder /app/main . # COPY --from=0でも前ステージを指定してることになるのでそちらでも可能 COPY ./test.ini . ENTRYPOINT ["./main"] CMD [""]上記をそれぞれビルドします。できあがったイメージはこちらです。
タグはわかりやすいようにつけてます。
353MB => 3MBは優秀ですね。$ docker image ls --filter 'label=multi_stage=no' REPOSITORY TAG IMAGE ID CREATED SIZE ryuichi1208/mumulti_stage_build nomal 508808fe62ea 45 minutes ago 353MB $ docker image ls --filter 'label=multi_stage=yes' REPOSITORY TAG IMAGE ID CREATED SIZE ryuichi1208/mumulti_stage_build multi b5c000ea4c0a About an hour ago 3.25MBDockerHubへPush
DockerHubへログインしてpushします。
詳細については下記をご参照ください。
■ Docker Hub QuickstartPushしたものはこちら。
それぞれマルチステージビルドを使ったものと使わなかったイメージです。他ホストからイメージしてRun
$ docker run ryuichi1208/multi_stage_build:multi $ docker images REPOSITORY TAG IMAGE ID CREATED SIZE ryuichi1208/multi_stage_build multi 27cb320e59ac 2 hours ago 3.25MB
- 投稿日:2019-07-28T21:59:32+09:00
ローカルで開発していたアプリをDocker化してみた(nginx+Flask+postgres)
はじめに
ローカルで作った簡易的なブログサービス(こちらの記事を参照)をDocker化してみました。正直この規模だと単一コンテナで十分だと思いますが、折角dockerをお勉強する機会なので、
nginx(Webサーバ)
とgunicorn+flask(APサーバ)
とpostgre(DBサーバ)
でコンテナを分けて、docker-compose
で全コンテナの統合管理をします。docker構成
ディレクトリ構成
ローカルの任意の場所にこの構成で作ります。
TutorialBlog ├docker-compose.yml ├nignx/ │ ├nginx.conf │ └Dockerfile ├app/ │ ├templates/ │ │ └(略) │ ├static/ │ │ └(略) │ ├app.py │ ├models.py │ └Dockerfile ├postgres/ │ ├initdb │ │ └createdb.sql │ └Dockerfile ├Pipfile ├Pipfile.lock └run.pyブラウザ⇆localhost:80⇆nginx:80
ブラウザからのリクエストをlocalhostの80番ポートで受けてnginxコンテナ(nginx-server)の80番ポートに流す設定は
docker-compose.yml
に書きます。docker-compose.ymlservices: nginx-server: ports: - 80:80 #[localhost側のポート]:[nginx-server側のポート]nignx:80⇆gunicorn:4000
nginxコンテナの80番ポートに流れてきたリクエストをgunicornコンテナの4000番ポートに流すためには何段階か設定が必要です。
なお、スペースの都合上、関係のある箇所だけ抜き出して書いています。最終形は一番最後にまとめて記載するので、解説に興味ない方は読み飛ばしてください。docker-compose.yml
gunicornコンテナ(gunicorn-server)を定義して、4000番ポートを解放します。localhostと接続したいわけではないので、
ports
ではなくexpose
で指定しています。
※参照 Docker-docs-ja exposedocker-compose.ymlservices: nginx-server: #(略) gunicorn-server: expose: - "4000"nginx.conf
upstream
ブロック内でnginxからリクエストを受け流す先のサーバーを定義します。gunicorn-serverの4000番ポートを解放するよう設定したので、server gunicorn-server:4000;
と書きます。
server
ブロック内で、nginxの80番ポートで受けたリクエストを、upstreamで定義したサーバーに流すよう設定しています。
(ここのlocation部分を色々いじると、リクエストURLに応じて受け流す先のサーバーを制御できるようになるっぽいです。詳細は「nginx連載5回目: nginxの設定、その3 - locationディレクティブ」を参照。)nginx.confhttp { upstream application { server gunicorn-server:4000; } server { listen 80; location / { proxy_pass http://application/; } } }run.py
flaskのデフォルトポートは5000番になっているので、4000番で受けるよう変更します。(※4000番にした意図は特にありません。適当です。デフォルトの5000番のままでいいと思います。)
あと、gunicornを介してリクエストを受け取るために、host="0.0.0.0"
を指定します。
※参考 docker-composeでgunicorn+nginx+flaskを動かしてみた話 - ハマったポイント①:Flaskのサーバーはデフォルトだと公開されてないrun.pyfrom app.app import app if __name__ == "__main__": app.run(host="0.0.0.0",port=4000)gunicorn⇆postgres:5432
gunicorn(flask)とpostgresの通信にも何段階か設定が必要です。
docker-compose.yml
postgersコンテナ(postgres-server)を定義して、5432番ポートを解放します。localhostと接続したいわけではないので、
ports
ではなくexpose
で指定しています。
また、postgresに接続するためのユーザ名とパスワードをenvironment
で定義します。(認証情報なので、ハードコーディングしないで、別の場所に格納した方がベターかもしれません。)docker-compose.ymlservices: nginx-server: #(略) gunicorn-server: #(略) postgres-server: expose: - "5432" environment: - POSTGRES_USER=[ユーザ名] - POSTGRES_PASSWORD=[パスワード]models.py
先ほど定義した[ユーザ名][パスワード]を使用して、
app.config['SQLALCHEMY_DATABASE_URI']
にpostgresへの接続情報を記載します。
末尾の/tutorial_blog
は、tutorial_blog DBへの接続を定義しています。tutorial_blog DBをPostgres内に作成する部分については後述します。models.pyfrom flask_sqlalchemy import SQLAlchemy from app.app import app app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql://[ユーザ名]:[パスワード]@postgres-server:5432/tutorial_blog' db = SQLAlchemy(app)postgres⇆databaseボリューム
postgresコンテナだけだと、コンテナを削除した際にデータが消滅してしまうので、コンテナ外の「ボリューム」と呼ばれる領域にデータを格納する必要があります。一番下の
volumes:
でdatabaseボリュームを使用することを宣言し、postgres-server:
内のvolumes:
で、databaseボリュームと、postgresのデータ格納領域である/var/lib/postgresql/data
を繋げています。
※参考 Docker、ボリューム(Volume)について真面目に調べたdocker-compose.ymlservices: nginx-server: #(略) gunicorn-server: #(略) postgres-server: #(略) volumes: - database:/var/lib/postgresql/data volumes: database: driver: localpostgresコンテナの設定
Postgres起動時処理
Postgresを起動時に、コンテナ内の
/docker-entrypoint-initdb.d
以下に置かれているファイルが実行されます。そこにローカルの/postgres/initdb
をマウントしておいて、initdb
以下に実行させたい処理を書きます。
※参考 dockerでPostgreSQLのコンテナ作成と初期化docker-compose.ymlservices: nginx-server: #(略) gunicorn-server: #(略) postgres-server: #(略) volumes: - ./postgres/initdb:/docker-entrypoint-initdb.d - #(略)今回はtutorial_blog DBをPostgresに(無ければ)作る、という処理を初期化処理として組み込みたいため、
createdb.sql
に、そのSQL文を記載して、initdb下に格納しておきます。createdb.sqlcreate database tutorial_blogDockerfile
postgres/Dockerfile
に、Dockerイメージ作成のためのコマンドを書いていきます。
Postgresコンテナはデフォルトのまま利用するので、参照元のイメージ指定だけ行います。postgres/DockerfileFROM postgresまた、
docker-compose.yml
側で、どのDockerfileを利用してイメージのビルドを行うか定義します。docker-compose.ymlservices: nginx-server: #(略) gunicorn-server: #(略) postgres-server: build: ./postgres #(略)gunicornコンテナの設定
flaskアプリファイルのマウント
ローカルでの開発物をgunicorn-serverにマウントして、コンテナ内でも使えるようにします。
今回のflaskアプリでは、依存パッケージを記載しているPipfile
、Pipfile.lock
、アプリ本体であるapp/
以下全てのファイル、アプリ起動用のrun.py
をコンテナ側でも使いたいので、この4つをコンテナ側の/var/www/
以下にマウントしていきます。docker-compose.ymlservices: nginx-server: #(略) gunicorn-server: #(略) volumes: - ./Pipfile:/var/www/Pipfile - ./Pipfile.lock:/var/www/Pipfile.lock - ./app:/var/www/app/ - ./run.py:/var/www/run.py postgres-server: #(略)Dockerfile
app/Dockerfile
に、Dockerイメージ作成とコンテナ起動時処理のためのコマンドを書いていきます。app/Dockerfile# 参照元イメージの指定 FROM python:3.7 # ワーキングディレクトリの指定 WORKDIR /var/www # コンテナ起動時の実行コマンド CMD ["bash","-c","pip install pipenv && pipenv install --system && gunicorn run:app -b 0.0.0.0:4000"]
CMD
にコンテナ起動時の実行コマンドを記載しています。
(※本当はCMDとENTRYPOINTの違いを理解しなきゃなんだろうけど、一旦これで動いたので。詳細はこちら「DockerfileのCMDとENTRYPOINTを改めて解説する」を参照。)
コンテナ起動後にやりたいことは、
1. pipenvのインストール
2. 依存パッケージのインストール
3. gunicornを介してのアプリ起動
なので、それらを順次行えるようにコマンドを記載しています。コンテナの中で以下のコマンドを実行するイメージですね。$ pip install pipenv $ pipenv install --system $ gunicorn run:app -b 0.0.0.0:4000
pipenv install --system
では、Pipfile
、Pipfile.lock
から、依存パッケージのインストールを行なっています。コンテナ内でわざわざpython仮想環境を立てる必要はないので、--system
をつけて、コンテナ内に直接パッケージのインストールを行なっています。
gunicorn run:app
で、run.py
のapp
変数を渡してgunicornを介してのflaskアプリ起動を行なっています。その際の-b 0.0.0.0:4000
オプションで、gunicornで受け入れるポートを指定しています。
※参考 Running Gunicorn
※参考 docker-composeでgunicorn+nginx+flaskを動かしてみた話 - ハマったポイント②:gunicorn起動にbindすべしまた、
docker-compose.yml
側で、どのDockerfileを利用してイメージのビルドを行うか定義します。docker-compose.ymlservices: nginx-server: #(略) gunicorn-server: build: ./app #(略) postgres-server: #(略)db.create_all()
models.pyで定義されたテーブル/カラム情報をもとに、SQLAlchemyのcreate_all()を走らせることで、postgresのtutorial_blog DBにテーブル/カラムの初期設定を行います。
gunicorn run:app
で、app.pyが実行されるので、その中にcreate_all()
を仕込んでおきます。models.pyfrom flask_sqlalchemy import SQLAlchemy from app.app import app db = SQLAlchemy(app)app.pyfrom flask import Flask app = Flask(__name__) from app.models import db db.create_all() db.session.commit()nginxコンテナの設定
nginx.confのマウント
ローカルの
nginx/nginx.conf
に格納しているnginx設定ファイルをコンテナ内でも使えるようにするため、マウント設定を行います。docker-compose.ymlservices: nginx-server: #(略) volumes: - ./nginx/nginx.conf:/etc/nginx/nginx.conf gunicorn-server: #(略) postgres-server: #(略)Dockerfile
nginx/Dockerfile
に、Dockerイメージ作成とコンテナ起動時処理のためのコマンドを書いていきます。nginx/Dockerfile# 参照元イメージの指定 FROM nginx # コンテナ起動時の実行コマンド CMD ["nginx", "-g", "daemon off;","-c","/etc/nginx/nginx.conf"]コンテナ起動後に以下のコマンドを実行しています。
$ nignx -g daemon off; -c /etc/nginx/nginx.confnginxをバックグラウンド実行するとコンテナが停止してしまうようなので、
-g daemon off;
で、フォアグラウンド実行を指定しています。
※参考 Docker 事始め - dockerハマりポイント
また、-c /pass/to/configfile
で、nginx設定ファイルの指定を行なっています。
※参考 CommandLine | NGINXさらに、
docker-compose.yml
側で、どのDockerfileを利用してイメージのビルドを行うか定義します。docker-compose.ymlservices: nginx-server: build: ./nginx #(略) gunicorn-server: #(略) postgres-server: #(略)コンテナ起動順序の指定
postgresコンテナ→gunicornコンテナ→nginxコンテナの順に起動したいので(nginxコンテナよりgunicornコンテナが先にたってないとupstream指定ができない、gunicornコンテナよりpostgresコンテナが先に立ってないとpostgres接続ができない)、
docker-compose.yml
に起動順序を記載していきます。
depends_on
を記載すると、指定されたコンテナが起動してから自分のコンテナを起動する、という制御をかけることができます。docker-compose.ymlservices: nginx-server: #(略) depends_on: - gunicorn-server gunicorn-server: #(略) depends_on: - postgres-server postgres-server: #(略)コンテナの起動・動作確認・停止・削除
本当は公式ドキュメント読み込まないといけないところなんですけど、時間がなかったので日本語でまとまっているこちら「docker-compose コマンドまとめ」を参考にさせていただきました。
dockerイメージのビルド
$ docker-compose buildコンテナの起動
$ docker-compose up -dブラウザ表示確認
ブラウザで
localhost
にアクセスすると、メインページが表示されるはずです。
コンテナの停止とdockerイメージ削除
$ docker-compose down --rmi alldockerイメージ一覧の確認
$ docker imagesコンテナ一覧の確認
$ docker ps -aボリューム一覧の確認
$ docker volume ls各コンテナのログ取得
$ docker logs [コンテナ名]最終的なファイル内容
docker-compose.yml
docker-compose.ymlversion: "3" services: nginx-server: build: ./nginx volumes: - ./nginx/nginx.conf:/etc/nginx/nginx.conf ports: - 80:80 depends_on: - gunicorn-server gunicorn-server: build: ./app volumes: - ./Pipfile:/var/www/Pipfile - ./Pipfile.lock:/var/www/Pipfile.lock - ./app:/var/www/app/ - ./run.py:/var/www/run.py expose: - "4000" depends_on: - postgres-server postgres-server: build: ./postgres expose: - "5432" environment: - POSTGRES_USER=user - POSTGRES_PASSWORD=password volumes: - ./postgres/initdb:/docker-entrypoint-initdb.d - database:/var/lib/postgresql/data volumes: database: driver: localnginx/Dockerfile
nginx/DockerfileFROM nginx CMD ["nginx", "-g", "daemon off;","-c","/etc/nginx/nginx.conf"]app/Dockerfile
app/DockerfileFROM python:3.7 WORKDIR /var/www CMD ["bash","-c","pip install pipenv && pipenv install --system && gunicorn run:app -b 0.0.0.0:4000"]postgres/Dockerfile
postgres/DockerfileFROM postgresnginx.conf
解説した部分以外はこちら「Flask+uwsgi+nginxの環境が作りたい?それ、Dockerなら1コマンドで出来るよ。」をかなり参考にしました。
nginx.confuser nginx; worker_processes 1; error_log /var/log/nginx/error.log warn; pid /var/run/nginx.pid; events { worker_connections 1024; } http { include /etc/nginx/mime.types; default_type application/octet-stream; log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; access_log /var/log/nginx/access.log main; upstream application { server gunicorn-server:4000; } server { listen 80; charset utf-8; location / { proxy_pass http://application/; } } }おわりに
元となるdockerイメージがあるとはいえ結構自前で設定しなきゃいけない部分が多かったので、今までherokuさんがよしなにやってくれていたWebサーバ(nginx)やDBサーバ(postgres)、wsgi(gunicorn)周りがどう動いているのかちょっとだけ理解できました。
今後の方向性としては
1. 他のDockerミドルウェア/アプリと繋ぐ(とりあえずElasticSearchを使ってみたい)
2. クラウドサーバにデプロイしてサービス公開(EC2に乗せてみたい)
3. コンテナオーケストレーション(kubernetes使ってみたい)
という感じで進めたいと思います!参考まとめ
Docker-docs-ja expose
nginx連載5回目: nginxの設定、その3 - locationディレクティブ
docker-composeでgunicorn+nginx+flaskを動かしてみた話
Docker、ボリューム(Volume)について真面目に調べた
DockerfileのCMDとENTRYPOINTを改めて解説する
dockerでPostgreSQLのコンテナ作成と初期化
Running Gunicorn
Docker 事始め
CommandLine | NGINX
docker-compose コマンドまとめ
Flask+uwsgi+nginxの環境が作りたい?それ、Dockerなら1コマンドで出来るよ。
- 投稿日:2019-07-28T19:36:03+09:00
docker-composeを使ってVeu.jsのプロジェクトを作成して、Dockerで動かしてみた
docker-compose
を使ってVeu.js
のプロジェクトを作成して、Dockerで動かしてみました。プロジェクトを作成するディレクトリを作成する
directory/hoge_directoryプロジェクトを作成するための
docker-compose.yml
を作成するdirectory/hoge_directory |_ docker-compose.ymldocker-compose.ymlversion: '3' services: node: image: node:12.7.0-alpine volumes: - .:/vuejs今回はコンテナ内で作成した
Vue.js
プロジェクトを、ホストに同期させて作るので、
ホスト側のhoge_directory
とコンテナ内の/vuejs
をマウントするvolumes: - .:/vuejs
docker-compose
でサービスを起動して、コンテナ内に入るsh$ docker-compose run node shマウントされている
/vuejs
に移動するsh/ # cd vuejs/
Vue CLI
をインストールする今回は
Vue CLI
を使用して、プロジェクトを作成するので、Vue CLI
をインストールするsh/vuejs # yarn global add @vue/cli
Vue.js
のプロジェクトを作成するsh/vuejs # vue create .上記のコマンドを実行すると、対話式でプロジェクトの設定をしていく
YESを選択? Your connection to the default yarn registry seems to be slow. Use https://registry.npm.taobao.org for faster installation? (Y/n) YYESを選択? Generate project in current directory? (Y/n) Ydefaultを選択? Please pick a preset: (Use arrow keys) ❯ default (babel, eslint) Manually select featuresYarnを選択? Pick the package manager to use when installing dependencies: (Use arrow keys) ❯ Use Yarn Use NPMコンテナ内と、ホスト側に、プロジェクトが作成される
コンテナ/vuejs |_ node_modules |_ public |_ src |_ .gitignore |_ babel.config.js |_ docker-compose.yml |_ package.json |_ README.md |_ yarn.lockホスト/hoge_directory |_ node_modules |_ public |_ src |_ .gitignore |_ babel.config.js |_ docker-compose.yml |_ package.json |_ README.md |_ yarn.lockプロジェクトの作成完了!!!
docker
でVue.js
を動かすDockerfileを作成する
directory/hoge_directory |_ node_modules |_ public |_ src |_ .gitignore |_ babel.config.js |_ docker-compose.yml |_ package.json |_ README.md |_ yarn.lock |_ Dockerfile <- 作成DockerfileFROM node:12.7.0-alpine WORKDIR /myapp COPY package.json ./ COPY yarn.lock ./ RUN yarn install
docker-compose.yml
を修正するdocker-compose.ymlversion: '3' services: view: build: . command: yarn run serve volumes: - .:/myapp - /myapp/node_modules ports: - "8000:8080"
Vue.js
プロジェクトをマウントした場合に、
コンテナ側のnode_modules
が上書きされて、削除される場合があるので、
マウントされないようにするvolumes: - ./vuejs:/myapp - /myapp/node_modules <- マウントで上書きされなくなる(プロジェクトが作成される時に作成される
.gitignore
では、
node_modules
はデフォルトで無視されているので、Gitで共有していると、
コンテナが立ち上がらなくなる)サービスを起動して、コンテナを立ち上げる
sh$ docker-compose up -dアクセスする
URLhttp://localhost:8000/終わり
マウントした時に、
node_modules
が上書きされて、削除されるのは、
なかなかハマりました...
- 投稿日:2019-07-28T19:36:03+09:00
docker-composeを使ってVue.jsのプロジェクトを作成して、Dockerで動かしてみた
docker-compose
を使ってVue.js
のプロジェクトを作成して、Dockerで動かしてみました。プロジェクトを作成するディレクトリを作成する
directory/hoge_directoryプロジェクトを作成するための
docker-compose.yml
を作成するdirectory/hoge_directory |_ docker-compose.ymldocker-compose.ymlversion: '3' services: node: image: node:12.7.0-alpine volumes: - .:/vuejs今回はコンテナ内で作成した
Vue.js
プロジェクトを、ホストに同期させて作るので、
ホスト側のhoge_directory
とコンテナ内の/vuejs
をマウントするvolumes: - .:/vuejs
docker-compose
でサービスを起動して、コンテナ内に入るsh$ docker-compose run node shマウントされている
/vuejs
に移動するsh/ # cd vuejs/
Vue CLI
をインストールする今回は
Vue CLI
を使用して、プロジェクトを作成するので、Vue CLI
をインストールするsh/vuejs # yarn global add @vue/cli
Vue.js
のプロジェクトを作成するsh/vuejs # vue create .上記のコマンドを実行すると、対話式でプロジェクトの設定をしていく
YESを選択? Your connection to the default yarn registry seems to be slow. Use https://registry.npm.taobao.org for faster installation? (Y/n) YYESを選択? Generate project in current directory? (Y/n) Ydefaultを選択? Please pick a preset: (Use arrow keys) ❯ default (babel, eslint) Manually select featuresYarnを選択? Pick the package manager to use when installing dependencies: (Use arrow keys) ❯ Use Yarn Use NPMコンテナ内と、ホスト側に、プロジェクトが作成される
コンテナ/vuejs |_ node_modules |_ public |_ src |_ .gitignore |_ babel.config.js |_ docker-compose.yml |_ package.json |_ README.md |_ yarn.lockホスト/hoge_directory |_ node_modules |_ public |_ src |_ .gitignore |_ babel.config.js |_ docker-compose.yml |_ package.json |_ README.md |_ yarn.lockプロジェクトの作成完了!!!
docker
でVue.js
を動かすDockerfileを作成する
directory/hoge_directory |_ node_modules |_ public |_ src |_ .gitignore |_ babel.config.js |_ docker-compose.yml |_ package.json |_ README.md |_ yarn.lock |_ Dockerfile <- 作成DockerfileFROM node:12.7.0-alpine WORKDIR /myapp COPY package.json ./ COPY yarn.lock ./ RUN yarn install
docker-compose.yml
を修正するdocker-compose.ymlversion: '3' services: view: build: . command: yarn run serve volumes: - .:/myapp - /myapp/node_modules ports: - "8000:8080"
Vue.js
プロジェクトをマウントした場合に、
コンテナ側のnode_modules
が上書きされて、削除される場合があるので、
マウントされないようにするvolumes: - ./vuejs:/myapp - /myapp/node_modules <- マウントで上書きされなくなる(プロジェクトが作成される時に作成される
.gitignore
では、
node_modules
はデフォルトで無視されているので、Gitで共有していると、
コンテナが立ち上がらなくなる)サービスを起動して、コンテナを立ち上げる
sh$ docker-compose up -dアクセスする
URLhttp://localhost:8000/終わり
マウントした時に、
node_modules
が上書きされて、削除されるのは、
なかなかハマりました...
- 投稿日:2019-07-28T18:46:36+09:00
DockerのみでつくるRailsプロジェクト
概要
Railsプロジェクトを作成する際にDocker環境があるのにわざわざローカル環境にRubyを入れて…は効率が悪すぎるので、Rubyコンテナを使用してRailsプロジェクトを作成しました。
忘備録に手順をまとめたので同じようにRailsプロジェクトを作成したい方の助けになればいいと思います。環境
- macOS Mojave 10.14
- Docker version 18.09.2, build 6247962
rubyコンテナの取得
DockerHubよりRubyイメージを取得
$ docker pull ruby:2.6.3 $ docker images REPOSITORY TAG IMAGE ID CREATED SIZE ruby 2.6.3 f1c13927d193 13 days ago 870MBGemfileの生成
Rubyイメージからコンテナを作成し、Gemfileを生成する。
// コンテナの起動 $ docker run --rm -v `pwd`:/myapp -w /myapp -it ruby:2.6.3 bash // オプション --rm: 実行後のコンテナを削除 -v: 共有ディレクトリの設定 -w: ワーキングディレクトリの設定 -it: コンテナをフォアグラウンドで実行 // Gemfileの生成 root@a1eeb5367697:/myapp# bundle init Writing new Gemfile to /myapp/GemfileGemfileの編集
Gemfileを編集し、Railsをインストールするように変更する。
Gemfile- # gem "rails" + gem "rails"Dockerfile、docker-compose.ymlの作成
コンテナを作成するDockerfileとdocker-compose.ymlを作成する。
各ファイルは下記のようにディレクトリを作成し、配置する。ディレクトリ構成
├ docker-compose.yml └ docker ├ mysql │ ├ volumes ← DB永続化用ディレクトリ │ └ password.env └ rails └ Dockerfileファイル内容
DockerfileFROM ruby:2.6.3 ENV LANG C.UTF-8 RUN set -x && \ : "前提パッケージのインストール" && \ apt-get update -qq && \ apt-get install -y \ build-essential \ mysql-client \ nodejs RUN set -x && \ : "パッケージのインストール" && \ gem install bundler WORKDIR /tmp ADD Gemfile Gemfile ADD Gemfile.lock Gemfile.lock RUN bundle install ENV APP_HOME /myapp RUN mkdir -p $APP_HOME WORKDIR $APP_HOME ADD . $APP_HOMEdocker-compose.ymlversion: '3' services: db: image: mysql:5.7.26 ports: - "3306:3306" volumes: - ./docker/mysql/volumes:/var/lib/mysql env_file: ./docker/mysql/password.env web: build: context: . dockerfile: ./docker/rails/Dockerfile command: bundle exec rails s -p 3000 -b '0.0.0.0' ports: - "3000:3000" volumes: - .:/myapp environment: RAILS_ENV: development env_file: ./docker/mysql/password.env depends_on: - dbDBのパスワードを設定
password.envMYSQL_ROOT_PASSWORD=passwordRailsプロジェクトの生成
Dockerfile、docker-compose.ymlで定義したコンテナのビルドを行い、コンテナにログインする。
// コンテナビルド $ docker-compose build // コンテナにログイン $ docker-compose up -d $ docker-compose exec web bash // オプション -d: バックグラウンド実行 // railsプロジェクトの生成 root@477e08f00455:/var/www/CleanManager# rails new . -d mysql -BT // オプション -d: DBをmysqlに設定 -B: bundle installを省略 -T: テストファイルの作成を省略 // パッケージのインストール root@477e08f00455:/var/www/CleanManager# bundle installrailsアプリの起動
コンテナの再起動を行う。
docker-compose.ymlにビルドインサーバを起動するコマンドを記述しているため、別途起動するコマンドを実行する必要はない。
再起動後、ブラウザで http://localhost:3000 にアクセスし起動していることを確認する。root@477e08f00455:/var/www/CleanManager# exit $ docker-compose down $ docker-compose up -dまとめ
Docker初心者だったので、よくわからないことがありましたが、なんとか作成することができました。
DBをMySQLからPostgreSQLに変更したかったのですが、PostgreSQLコンテナが起動直後に落ちてしまったのでMySQLで妥協しました。
今後も原因を探して判明しだい、再度記事にしたいと思います。
間違い等ありましたら、コメントから修正お願いします。
- 投稿日:2019-07-28T15:18:26+09:00
Python ✖︎ Flask ✖︎ Webアプリ(4)DBに接続して操作してみよう
目的
- Flask で Postgresql に接続
- DB にレコードを登録できること
- DB からレコードを取得できること
本編
環境準備
python のパッケージを追加
requirements.txtflask sqlalchemy psycopg2上記を
pip install -r requirements.txt
でインストールポイント
sqlalchemyは python の ORM ライブラリ(簡単に言うとDB 操作は sqlalchemy が担当するよ)
psycopg2
は flask と postgresql を繋いでくれる役割
インストールしたパッケージを一括でアンインストールする(必要があれば)
- pip でインストールしたパッケージの一覧を取得
$ pip freeze > piplist.txt
- 一括でアンインストール
$ pip uninstall -y -r piplist.txt前回まで
Python ✖︎ Flask ✖︎ Webアプリ(3)Docker登場 DBの準備
Python ✖︎ Flask ✖︎ Webアプリ(2)HTMLの表示とメソッドとパラメータの受け取りかた
Python ✖︎ Flask ✖︎ Webアプリ(1)こんにちは世界フォルダ構成
. ├── README.md ├── docker-compose.yml ├── form.html ├── postgresql │ └── init │ ├── 1_create_db.sql │ └── 2_create_table.sh ├── requirements.txt ├── src │ ├── UserModel.py │ ├── main.py │ ├── setting.py │ └── templates │ └── hello.html └── test.http
DB接続の準備
setting.pyfrom sqlalchemy import * from sqlalchemy.orm import * from sqlalchemy.ext.declarative import declarative_base import psycopg2 # postgresqlのDBの設定 DATABASE = "postgresql://postgres:@192.168.1.19:5432/flask_tutorial" # Engineの作成 ENGINE = create_engine( DATABASE, encoding="utf-8", # TrueにするとSQLが実行される度に出力される echo=True ) # Sessionの作成 session = scoped_session( # ORM実行時の設定。自動コミットするか、自動反映するなど。 sessionmaker( autocommit=False, autoflush=False, bind=ENGINE ) ) # modelで使用する Base = declarative_base() Base.query = session.query_property()ポイント
DB の接続先情報
DATABASE = "postgresql://postgres:@192.168.1.19:5432/flask_tutorial
DATABASE = "postgresql://{DBのユーザ}:{DBのパスワード}@{url}:{ポート番号}/{DB名}
今回のDBは前回用意したDBをそのまま利用
{url}は自分のPCのIPを入力Engine 作成
ENGINE = create_engine( DATABASE, encoding="utf-8", # TrueにするとSQLが実行される度に出力される echo=True )Engine はDBにアクセスするための土台なんだと覚えとけば、とりあえずOK
- Session作成
session = scoped_session( # ORM実行時の設定。自動コミットするか、自動反映するなど。 sessionmaker( autocommit=False, autoflush=False, bind=ENGINE ) )SessionはflaskとDBとのやり取りを全て担当してくれるもの
データを挿入するテーブルを準備
2_create_table.sh#!/bin/bash psql -U postgres -d flask_tutorial << "EOSQL" CREATE TABLE users ( id SERIAL NOT NULL, name VARCHAR(200), created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, updated_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, PRIMARY KEY (id) ); EOSQLUserModel.pyfrom datetime import datetime from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import Column, Integer, String, DateTime from setting import Base from setting import ENGINE class User(Base): """ UserModel """ __tablename__ = 'users' id = Column(Integer, primary_key=True, autoincrement=True) name = Column(String(200)) created_at = Column(DateTime, default=datetime.now, nullable=False) updated_at = Column(DateTime, default=datetime.now, nullable=False) def __init__(self, name): self.name = name if __name__ == "__main__": Base.metadata.create_all(bind=ENGINE)ポイント
- DBのDockerコンテナを初めて起動した際に、
2_create_table.sh
でUserテーブルを作成する
※ DBが一度作成されている場合は、/Users/${USER}/Volumes/flask_tutorial/postgres
を削除する- flask側にDB上に、どんなテーブルがあるのかをsqlarchemyを使用して定義してあげる
DB 確認
- コンテナを起動
$ docker-compose up -d Creating network "flask-tutorial_default" with the default driver Creating flask_tutorial_postgresql ... done Creating flask_tutorial_pgadmin4 ... done
- pgadminでUserテーブルが作成されていることを確認
DB操作準備
main.pyfrom flask import Flask, request, render_template from UserModel import User from setting import session from sqlalchemy import * from sqlalchemy.orm import * # appという名前でFlaskのインスタンスを作成 app = Flask(__name__) # 登録処理 @app.route('/', methods=["POST"]) def register_record(): name = request.form['name'] session.add(User(name)) session.commit() return render_template("hello.html", name=name, message="登録完了しました!") # 取得処理 @app.route('/', methods=["GET"]) def fetch_record(): name = request.args.get('name') db_user = session.query(User.name).\ filter(User.name == name).\ all() if len(db_user) == 0: message = "登録されていません。" else: message = "登録されています。" return render_template("hello.html", name=name, message=message) if __name__ == '__main__': app.run()ポイント
操作に必要なものをインポート
- 操作対象のテーブル
- DBの操作を行うのでsessionが必要
from UserModel import User from setting import session from sqlalchemy import * from sqlalchemy.orm import *
- 登録処理
# 登録処理 @app.route('/', methods=["POST"]) def register_record(): name = request.form['name'] session.add(User(name)) session.commit() return render_template("hello.html", name=name, message="登録完了しました!")実際に登録している箇所は以下
session.add(User(name)) session.commit()
session.add
でINSERT文を実行
session.commit()
コミットしてる
- 取得処理
# 取得処理 @app.route('/', methods=["GET"]) def fetch_record(): name = request.args.get('name') db_user = session.query(User.name).\ filter(User.name == name).\ all() if len(db_user) == 0: message = "登録されていません。" else: message = "登録されています。" return render_template("hello.html", name=name, message=message)取得処理は、以下
db_user = session.query(User.name).\ filter(User.name == name).\ all()
session.query(User.name)
で取得したいテーブル.列名
filter(User.name == name)
はWHERE句
all()
でクエリを実行※ 今回は単純なSELECT文のみ、テーブル結合などもできるので必要に応じて適宜調べてください。
DB操作確認
- コンテナを起動
$ docker-compose up -d Creating network "flask-tutorial_default" with the default driver Creating flask_tutorial_postgresql ... done Creating flask_tutorial_pgadmin4 ... done
- flaskを起動
$ python src/main.py * Serving Flask app "main" (lazy loading) * Environment: production WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. * Debug mode: off * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
- form.htmlで確認
次回
- nginxのdockerコンテナを用意して、静的なページをflaskから切り離す
- 投稿日:2019-07-28T15:18:26+09:00
Python ✖︎ Flask ✖︎ Webアプリ(4)DBに接続
目的
- Flask で Postgresql に接続
- DB にレコードを登録できること
- DB からレコードを取得できること
本編
環境準備
python のパッケージを追加
requirements.txtflask sqlalchemy psycopg2上記を
pip install -r requirements.txt
でインストールポイント
sqlalchemyは python の ORM ライブラリ(簡単に言うとDB 操作は sqlalchemy が担当するよ)
psycopg2
は flask と postgresql を繋いでくれる役割
インストールしたパッケージを一括でアンインストールする(必要があれば)
- pip でインストールしたパッケージの一覧を取得
$ pip freeze > piplist.txt
- 一括でアンインストール
$ pip uninstall -y -r piplist.txt前回まで
Python ✖︎ Flask ✖︎ Webアプリ(3)Docker登場 DBの準備
Python ✖︎ Flask ✖︎ Webアプリ(2)HTMLの表示とメソッドとパラメータの受け取りかた
Python ✖︎ Flask ✖︎ Webアプリ(1)こんにちは世界フォルダ構成
. ├── README.md ├── docker-compose.yml ├── form.html ├── postgresql │ └── init │ ├── 1_create_db.sql │ └── 2_create_table.sh ├── requirements.txt ├── src │ ├── UserModel.py │ ├── main.py │ ├── setting.py │ └── templates │ └── hello.html └── test.http
DB接続の準備
setting.pyfrom sqlalchemy import * from sqlalchemy.orm import * from sqlalchemy.ext.declarative import declarative_base import psycopg2 # postgresqlのDBの設定 DATABASE = "postgresql://postgres:@192.168.1.19:5432/flask_tutorial" # Engineの作成 ENGINE = create_engine( DATABASE, encoding="utf-8", # TrueにするとSQLが実行される度に出力される echo=True ) # Sessionの作成 session = scoped_session( # ORM実行時の設定。自動コミットするか、自動反映するなど。 sessionmaker( autocommit=False, autoflush=False, bind=ENGINE ) ) # modelで使用する Base = declarative_base() Base.query = session.query_property()ポイント
DB の接続先情報
DATABASE = "postgresql://postgres:@192.168.1.19:5432/flask_tutorial
DATABASE = "postgresql://{DBのユーザ}:{DBのパスワード}@{url}:{ポート番号}/{DB名}
今回のDBは前回用意したDBをそのまま利用
{url}は自分のPCのIPを入力Engine 作成
ENGINE = create_engine( DATABASE, encoding="utf-8", # TrueにするとSQLが実行される度に出力される echo=True )Engine はDBにアクセスするための土台なんだと覚えとけば、とりあえずOK
- Session作成
session = scoped_session( # ORM実行時の設定。自動コミットするか、自動反映するなど。 sessionmaker( autocommit=False, autoflush=False, bind=ENGINE ) )SessionはflaskとDBとのやり取りを全て担当してくれるもの
データを挿入するテーブルを準備
2_create_table.sh#!/bin/bash psql -U postgres -d flask_tutorial << "EOSQL" CREATE TABLE users ( id SERIAL NOT NULL, name VARCHAR(200), created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, updated_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, PRIMARY KEY (id) ); EOSQLUserModel.pyfrom datetime import datetime from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import Column, Integer, String, DateTime from setting import Base from setting import ENGINE class User(Base): """ UserModel """ __tablename__ = 'users' id = Column(Integer, primary_key=True, autoincrement=True) name = Column(String(200)) created_at = Column(DateTime, default=datetime.now, nullable=False) updated_at = Column(DateTime, default=datetime.now, nullable=False) def __init__(self, name): self.name = name if __name__ == "__main__": Base.metadata.create_all(bind=ENGINE)ポイント
- DBのDockerコンテナを初めて起動した際に、
2_create_table.sh
でUserテーブルを作成する
※ DBが一度作成されている場合は、/Users/${USER}/Volumes/flask_tutorial/postgres
を削除する- flask側にDB上に、どんなテーブルがあるのかをsqlarchemyを使用して定義してあげる
DB 確認
- コンテナを起動
$ docker-compose up -d Creating network "flask-tutorial_default" with the default driver Creating flask_tutorial_postgresql ... done Creating flask_tutorial_pgadmin4 ... done
- pgadminでUserテーブルが作成されていることを確認
DB操作準備
main.pyfrom flask import Flask, request, render_template from UserModel import User from setting import session from sqlalchemy import * from sqlalchemy.orm import * # appという名前でFlaskのインスタンスを作成 app = Flask(__name__) # 登録処理 @app.route('/', methods=["POST"]) def register_record(): name = request.form['name'] session.add(User(name)) session.commit() return render_template("hello.html", name=name, message="登録完了しました!") # 取得処理 @app.route('/', methods=["GET"]) def fetch_record(): name = request.args.get('name') db_user = session.query(User.name).\ filter(User.name == name).\ all() if len(db_user) == 0: message = "登録されていません。" else: message = "登録されています。" return render_template("hello.html", name=name, message=message) if __name__ == '__main__': app.run()ポイント
操作に必要なものをインポート
- 操作対象のテーブル
- DBの操作を行うのでsessionが必要
from UserModel import User from setting import session from sqlalchemy import * from sqlalchemy.orm import *
- 登録処理
# 登録処理 @app.route('/', methods=["POST"]) def register_record(): name = request.form['name'] session.add(User(name)) session.commit() return render_template("hello.html", name=name, message="登録完了しました!")実際に登録している箇所は以下
session.add(User(name)) session.commit()
session.add
でINSERT文を実行
session.commit()
コミットしてる
- 取得処理
# 取得処理 @app.route('/', methods=["GET"]) def fetch_record(): name = request.args.get('name') db_user = session.query(User.name).\ filter(User.name == name).\ all() if len(db_user) == 0: message = "登録されていません。" else: message = "登録されています。" return render_template("hello.html", name=name, message=message)取得処理は、以下
db_user = session.query(User.name).\ filter(User.name == name).\ all()
session.query(User.name)
で取得したいテーブル.列名
filter(User.name == name)
はWHERE句
all()
でクエリを実行※ 今回は単純なSELECT文のみ、テーブル結合などもできるので必要に応じて適宜調べてください。
DB操作確認
- コンテナを起動
$ docker-compose up -d Creating network "flask-tutorial_default" with the default driver Creating flask_tutorial_postgresql ... done Creating flask_tutorial_pgadmin4 ... done
- flaskを起動
$ python src/main.py * Serving Flask app "main" (lazy loading) * Environment: production WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. * Debug mode: off * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
- form.htmlで確認
次回
- nginxのdockerコンテナを用意して、静的なページをflaskから切り離す
- 投稿日:2019-07-28T14:43:53+09:00
gRPC UIを使ってみんなが触れるgRPCの遊び場を作りました
gRPC
は近年非常によく使われるようになったRPCフレームワーク1ですが、その柔軟なメッセージフォーマットに対応するリッチなGUIクライアントに欠けていました。REST APIでいうPostman的な存在です。このようなツールがなければgRPCを利用した開発が面倒なのですが、その問題は現在においてはほぼ払拭されたといっても過言ではありません。gRPC UIが登場したからです。(本記事は自分のブログからの転載記事です。)
TL;DR
- gRPC UIで作成した遊び場はこちらです
gRPC UI playground
- gRPC UIはgRPCサーバへの要求と応答がWebで簡単にできるので、今後のgRPCを利用した開発に広く使われるツールとなりそうです
- gRPC UI playgroundは以下のサービスを利用して、無料で立ち上げました。当面はみんなが遊べるようにしておくつもりです
はじめに
本記事は
gRPC
のWeb UIであるgRPC UI
の紹介記事です。gRPCに興味がある方、gRPCを利用している方を対象にしています。実際にgRPC UIを触れる遊び場も作ったのでgRPCに興味がある方は遊んでいってください。また本記事ではこの遊び場をどうやって作ったのかも簡単に解説します。gRPCとは
gRPCはGoogleが開発してオープンソースとして公開したRPCフレームワークです。IDL2としてProtocol Buffers3が利用可能です4。gRPCは基本的には以下の図ように単純で、クライアントから要求(
Proto Request
)を送ってサーバが応答(Proto Response
)を返すというものです。クライアントやサーバの記述には、Protocol Buffersが対応している様々な言語(C++, Ruby, Java等)が利用可能です。What is gRPCより引用
gRPCは上記のように単純な要求/応答型以外にも様々なやりとりに対応できます。具体的には以下の4つです。
- Unary RPC(単純な要求/応答型)
- Server streaming RPC(サーバが複数の応答を返すことができる)
- Client streaming RPC(クライアントが複数の応答を返すことができる)
- Bidirectional streaming RPC(クライアントとサーバがそれぞれ複数の要求/応答ができる。一般的な双方向通信)
上記のような柔軟な呼び出しはHTTP/25の基盤に支えられて実現されています。
gRPC UIについて
gRPC UIはgRPCサーバとブラウザでやりとりできるツールです。百聞は一見にしかず。以下の画面を御覧ください。
この画面を見て震えたのはやはり要求の入力画面(
Request Form
)の充実っぷりです。gRPCは自分でデータの型を定義して入れ子のようなメッセージを定義できますが、そのような入力も簡単にできます。上記の図のではtest.TestMessage
というメッセージの中にperson(test.Person
というメッセージの型)が内包されています。また複数のデータが入力可能なフィールドは「Add Item
」で簡単に追加できます。その他の注目すべき点は赤字で説明を入れておいたので参考にしてください。基本的にはgRPC(Protocol Buffers)で定義されている入力形式はほぼ網羅されています。また、驚いたのはgRPCには基本的なスカラ値(
string
やint64
等)以外にも「Well-Known Types」と呼ばれる型が定義されていますが、gRPC UIにはこれらにもリッチなインタフェースが提供されていたことです。以下の画面はtimestamp
型に対するインタフェースです。またリッチなフォームだけではなく、JSON形式でリクエストを投げることもできます。
JSON形式で便利なのはコピペが容易なところです。また省略されているフィールドは出力されないので見やすくなります。フォーム型の編集画面とも連動しているので、非常に使いやすいです。リクエストは「Invoke」ボタンを押すことでサーバに投げることができます。このボタンはフォーム型のリクエストにも一番下にあります。以下が「Invoke」を押して返ってきた応答になります。
この画面も特に説明がいらないくらい分かりやすいものになっています。gRPC UIで対応できないのは双方向の複雑なやり取りです。これは従来どおりCUI等を利用して行うしかありません。
gRPC UIで遊ぼう! 〜 gRPC UI playground 〜
さて、ここまで来たらgRPC UIで遊んでみたくなったと思います。以下にみんなが遊べるように「遊び場」を立ち上げたのでぜひいろいろ遊んでみてください。
gRPC UIはバックグラウンドで起動しているgRPC UIのテストサーバ(testsvr)とやりとりしています。プロトコルは「test.proto」、サーバの実装は「testsvr.go」に記述されています。実際にgRPC UIを動かしながらソースやIDLを読むとgRPCの理解が深まると思います。
メソッドに関しては「
DoManyThings
」が全部詰めになっていて、残りのメソッドはそれを機能単位で分割したものになっています。たぶん・・・grpcui-playgroundの作り方
さて、ここからはおまけです。上記の遊び場(以降、
grpcui-playground
と記載)の構築方法の興味がある方を対象に簡単に説明したいと思います。grpcui-playgroundは3つのサービスを利用して無料で立ち上げました。以下がその構成になります。上の図をざっくり説明すると、基本的にはDockerを使ってサービス提供しています。一つのコンテナの中にgPPC UIとgRPC Server(
testsvr
)という二つのプロセスが動いています。一つのコンテナに二つのプロセスはあまりお行儀がよくありませんが、今回は遊び場ということと無料で立ち上げることを重視したのでこのような構成になっています。
利用したコンテナホスティングサービスはArukas6になります。今回はFreeプランを利用しています。Arukasにコンテナイメージを提供するためにはDocker Hubも利用しました。Docker HubにはGitHubと連携しておくとGitHubの変更を検知してコンテナをビルドする機能があるのでそれも利用しています。Dockerfileを書く
Dockerfileを書くときの注意点は二つです。まず、ビルドイメージを小さくするためにAlpine Linux7をベースイメージにします。次にgRPC UIが提供しているテストサーバ(
testsvr
)はgoで書かれているのでgoのビルド環境が必要です。ただしgoのビルド環境は動作には必要ないのでマルチステージビルドを活用して、なるべく小さなイメージにするようにしましょう8。DockerfileFROM golang:1.12.7-alpine as build-env MAINTAINER hinastory WORKDIR /testsvr COPY testsvr /testsvr COPY start.sh /start.sh RUN apk update && apk add --no-cache git RUN go build RUN go get -x github.com/fullstorydev/grpcui RUN go install -x github.com/fullstorydev/grpcui/cmd/grpcui FROM alpine WORKDIR / COPY --from=build-env /testsvr/testsvr /bin/testsvr COPY --from=build-env /go/bin/grpcui /bin/grpcui COPY start.sh /start.sh EXPOSE 8080 ENTRYPOINT [ "/start.sh" ]GitHubでDockerfileを公開する
ここは特に説明はしませんが、GitHubに公開レジストリを作成し、Dockerfileと
testsvr
のソースと起動スクリプトをpushします。pushした自分のリポジトリは以下になります。
![]()
Docker Hubでイメージをビルド & 公開する
Docker Hubでイメージをビルドするのには、GitHubと連携しておいたほうが楽です。GitHubと連携しておけばGitHubのレポジトリをDocker HubのUIから選択できるようになり、GitHubにpushするだけでDockerfileをビルドしてくれるようになります。
注意すべき点はDockerイメージのタグの付け方です。デフォルトだとブランチを監視してlatestタグを付けるようになっていますが、Arukasはイメージをキャッシュする場合があるようなのでバージョンが入った適切なタグをつけることが望ましいです。そしてDockerのイメージタグはGitHubのタグと連動しているとよいと思われます。
Docker Hubでその設定をするのは簡単でDockerHubのリポジトリの
Builds
の画面からビルドルールを設定でき、Source Type
をTag
にして、Source
とDocker Tag
を正規表現で記述することでお望みのタグが構成できると思います。またここでDockerfileの位置やビルドコンテキストも指定可能なので、少し複雑なリポジトリ構成でも対応出来ます。ここの設定が終わったらGitHubにタグをpushしてビルドが始まるか確認します。基本的には検知はすぐに行われて
In Progress
の状態にすぐに変わりますがビルドが終わるまでには時間がかかるので、コーヒーでも飲んでまったりと待ちましょう(笑)。自分のコンテナイメージは以下に公開してあります。ArukasでDockerコンテナを公開する
最後にArukasuでコンテナを公開します。FreeプランはO.1vCPUと128MB RAMの非力な環境ですが、今回のような遊び場には充分だと思われます。また「転送量課金」がないのでDDoSとかの心配がまったくいらないのも嬉しいところです。もちろん最悪サービスは落とされますが、料金に怯える心配は無いわけです。ちなみにFreeプランでも電話認証とクレジットカードの登録は必須です。このアカウント登録が最大の難関でコンテナの起動は驚くほどあっけなく終わりました。アプリ起動の詳細は「アプリの作成 – Arukas Help Center」を参照してください。以下は起動したコンテナの管理画面です。
Endpoint
はアプリの再起動で変わることが無いURLです。Port
はインスタンスの起動毎に変わります。ちなみにgRPC UIは8080ポートでHTTPでサービスを公開していますが、エンドポイントではHTTPSになっているので自動でArukasがHTTPSに包んでくれるみたいです。まとめ
本記事では、gRPCサーバとブラウザでやり取りできるgRPC UIを紹介しました。gRPC UIは非常に便利なので今後gRPC関連の開発で広く使われていくものと思われます。そして実際にgRPC UIを触れる遊び場を作成して以下に公開しました。
- 遊び場
- gRPC UI playground
- プロトコルは「test.proto」、サーバの実装は「testsvr.go」
- 遊び場は当面は公開予定ですが、ある日突然告知なしで死ぬ可能性があるのでご容赦ください
- Dockerfile
- コンテナイメージ
また、おまけとしてその遊び場の作り方を(無料でコンテナサービスを立ち上げる手順)を簡単に説明しました。
本記事がgRPCを理解し、より便利に使えるようになるための一助になれば幸いです。参考文献
- gRPC Documentation
- Developer Guide | Protocol Buffers
- RFC 7540 - Hypertext Transfer Protocol Version 2 (HTTP/2)(日本語訳)
- Docker Hub Documentation
- about Alpine Linux
- Arukas Help Center
- 遠隔手続き呼出し - Wikipedia
- インタフェース記述言語 - Wikipedia
RPC(Remote Procedure Call)は、簡単に言えばプログラムの中から内部の関数を呼ぶのと似たような感覚で、外部のネットワーク上の関数や手続きを呼べるようにする技術のことです。他のRPCにはSOAPやJSON-RPCなどがあります。 ↩
IDLはインタフェース記述言語(Interface Description Language)のことです。簡単に言えば関数呼び出しの宣言部分を定義する際に使う言語です。 ↩
Protocol BuffersはGoogleが開発したシリアライズフォーマットです。IDLは独自の言語でシンプルで分かりやすいのが特徴です。IDLのファイルは
.proto
の拡張子を持っており、protoc
というコンパイラを用いてIDLファイルからGo言語やRuby、Python、Java等様々な言語のバインディングを生成できます。現在のProtocol Buffersのバージョンは3になります。 ↩JSONも利用可能みたいですが、自分は使ったことはありません。 ↩
HTTP2はHTTP/1.1の後継バージョンでRFC7540で定義されています。HTTP/1.1と比べてヘッダーの圧縮やバイナリメッセージ構造、双方向通信のサポート等様々な改善がなされています。 ↩
Arukasはさくらインターネットが提供しているDockerのホスティングサービスです。Arukasを反対から読むと・・・ ↩
Alpine Linuxは軽量、シンプル、セキュアをコンセプトにしたLinuxディストリビューションです。組み込み系で実績のあるmusl libcとbusyboxをベースにしています。その軽量さからコンテナのベースイメージとしてよく利用されています。 ↩
最初は無邪気にベースイメージを
stretchに
してシングルステージでビルドしたため、イメージのサイズが400MBを超えてしまいました・・・現在のサイズは 18MBです。特にパブリックなレジストリに登録する際はコンテナイメージのサイズに充分気を配り、リソースを無駄にしないように心掛けましょう(自戒)。 ↩
- 投稿日:2019-07-28T11:54:59+09:00
【メモ】Rootless Docker
Rootless Mode
Docker 19.03 ではまだ実験的扱いであるが、良い感じ。
https://github.com/moby/moby/blob/ca5aab19b482f27629374cdde4df26b5676e39cb/docs/rootless.md
Security が高まります。
Katacodaで試すとよろしい。
https://www.katacoda.com/courses/docker/rootless使い方
ざっくりすべて一般ユーザ(rootless)で行う
- 一般ユーザで入れて(
curl -sSL https://get.docker.com/rootless |sh
)- 環境変数セットして
- rootlessのシェル実行(Daemon起動)して
- run する
- 投稿日:2019-07-28T11:45:03+09:00
【メモ】VFS Storage Driver
VFS Storage Driver
https://docs.docker.com/storage/storagedriver/vfs-driver/
- union FSではない
- 容量とパフォーマンスにデメリットがある
- CoWは未対応
- 新しいレイヤーを作るときは「deep copy(オブジェクトとメモリの両方がコピーされる)」である
- /var/lib/docker/vfs/dir/ に配置されるが、イメージレイヤIDsとの関連性はない
- どんな backing filesystemでも使える
- 投稿日:2019-07-28T11:34:24+09:00
Docker ComposeでGitLabを起動
Docker Composeとは
- Dockerコンテナの設定をyamlファイルで定義し、構築・管理が行えるツール
docker container run
コマンドのオプションなどをファイルで定義するイメージ- 複数個のコンテナを扱える
メリット
- 設定をコードで管理できる(IaC)
- 複数個のコンテナを扱う場合は
- runコマンドを何回も打たなくて済む
- コンテナ同士の依存関係をファイルで定義できる
実行環境
- OS
- Amazon Linux 2
- 前提
- Docker 18.06.1-ceがインストール済み
インストール
- Dockerエンジンのパッケージには含まれていないので別途インストールが必要
- Docker for Windows/Macには含まれている
- 公式ドキュメントに沿って実行
手順
- 資源をダウンロード
$ sudo curl -L "https://github.com/docker/compose/releases/download/1.24.1/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
- 実行権限を付与
$ sudo chmod +x /usr/local/bin/docker-compose
- バージョンが表示されれば成功
$ docker-compose version docker-compose version 1.24.1, build 4667896b docker-py version: 3.7.3 CPython version: 3.6.8 OpenSSL version: OpenSSL 1.1.0j 20 Nov 2018実行
yamlファイル
- 今回は例としてGitLabのコンテナを動かす
docker-compose.yamlversion: '3.7' services: gitLab: image: gitlab/gitlab-ce:latest ports: - "80:80" volumes: - '/srv/gitlab/config:/etc/gitlab' - '/srv/gitlab/logs:/var/log/gitlab' - '/srv/gitlab/data:/var/opt/gitlab'
- version
- Docker 18.06では3.7に対応
- Docker Engineのバージョンに依存するので注意
- servicesの配下に適当な名前を付与し、その配下に設定を記述する
- image
- 起動させるDockerイメージ
- ports
- ホスト側とコンテナ側で何番のポートをフォワーディングするかを指定
- pオプションに該当
volumes
- コンテナからマウントするホストのパスを指定
- vオプションに該当
上記の設定は
docker container run
コマンドの場合は次のコマンドに該当する$ docker container run gitlab/gitlab-ce:latest -p 80:80 -v /srv/gitlab/config:/etc/gitlab -v /srv/gitlab/logs:/var/log/gitlab -v /srv/gitlab/data:/var/opt/gitlab実行
docker-compose up
のコマンドで実行可能$ docker-compose -f ./docker-compose.yaml up -d
- f オプションでyamlファイルを指定できるが、カレントパスにdocker-compose.yaml(yml)という名前のファイルがあれば省略可能
- カレントパス以外のパスにあるファイルや、他の名前が付いているファイルを指定する場合は必須
- これ以外のdocker-composeコマンドでもファイルを参照するので、基本的にカレントディレクトリにファイルがある状態で操作するべき
- d オプションでコンテナをデタッチモード(バッククダウンドでの実行)で起動される
- 以下のコマンドで起動が確認できる
$ docker-compose ps Name Command State Ports --------------------------------------------------------------------------------------------------------------------- gitlab_gitLab_1 /assets/wrapper Up (health: starting) 22/tcp, 443/tcp, 0.0.0.0:80->80/tcp名前に関して
- [プロジェクト名] _ [サービス名] _ [採番された番号]で命名される
- プロジェクト名
- 環境変数「COMPOSE_PROJECT_NAME」に指定されている値
- 設定されていない場合はyamlファイルが配置されているフォルダ名が設定される
- サービス名
- docker-compose.yamlのservicesで指定した名前
- 採番された番号
- スケールする場合にコンテナ毎に採番される
- スケールしていない場合は1が付与される
- 参考
停止
- 以下のコマンドで停止できる
$ docker-compose stop Stopping gitlab_gitLab_1 ... done実行(複数コンテナ)
yamlファイル
- GitLabのCIツールであるGitLab Runnerを同時に構築する
docker-compose.yamlversion: '3.7' services: gitLab: image: gitlab/gitlab-ce:latest ports: - "80:80" volumes: - '/srv/gitlab/config:/etc/gitlab' - '/srv/gitlab/logs:/var/log/gitlab' - '/srv/gitlab/data:/var/opt/gitlab' gitLab-runner: image: gitlab/gitlab-runner:latest volumes: - '/srv/gitlab-runner/config:/etc/gitlab-runner' - '/var/run/docker.sock:/var/run/docker.sock' networks: mynet: ipv4_address: 172.20.0.3 depends_on: - gitLab
- 同じようにservicesを定義すればよい
- 今回はgitLab-runnerという名前で追記
- depends_onを記載することでコンテナ間の依存関係を定義できる
- GitLabが起動した後にRunnerを起動させたいので、Runnerのdepends_onにGitLabのサービス名を指定しておく
実行
- 先ほどと同様に起動する
$docker-compose -f ./docker-compose.yaml up -d Starting gitlab_gitLab_1 ... done Starting gitlab_gitLab-runner_1 ... done
- 投稿日:2019-07-28T11:15:41+09:00
DOCKER で ORACLE 12C を起動して PDB に接続するまで
接続には IntelliJ IDEA のプラグイン DBNavigator を使って接続します。
Docker の起動まで
あまりはっきり覚えていませんが・・・ Oracle Database Server Docker Image Documentation からイメージを取得します。
Docker image に Oracle が追加されます。
% docker image ls REPOSITORY TAG IMAGE ID CREATED SIZE store/oracle/database-enterprise 12.2.0.1 12a359cd0528 23 months ago 3.44GBそして、次のコマンドで docker を起動します。
docker run -it --name oracle-db -p 1521:1521 -p 5500:5500 store/oracle/database-enterprise:12.2.0.1Oracle PDB への接続準備
次のコマンドを実行し、 SQLPLUS を起動します。
docker exec -it $CONTAINER_ID bash -c "source /home/oracle/.bashrc; sqlplus /nolog"# ORCLCDB に接続します。 ## sys ユーザ のパスワードは Oradoc_db1 で、 sysdba として接続します。 SQL> conn sys/Oradoc_db1@ORCLCDB as sysdba # PDB のセッションに変更します。 SQL> alter session set container=ORCLPDB1 # ユーザを作成します。 SQL> create user developer identified by developer; # 接続権限をつけます。 SQL> grant create session to developer;接続確認
接続する場合はサービス名を確認します。
次のコマンドで、 Docker 内 の bash を使います。docker exec -it bash -c "source /home/oracle/.bashrc; bash"
lsnrctr status
を実行します。[oracle@3fda365c7b37 /]$ lsnrctl status LSNRCTL for Linux: Version 12.2.0.1.0 - Production on 28-JUL-2019 01:43:37 Copyright (c) 1991, 2016, Oracle. All rights reserved. Connecting to (DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=0.0.0.0)(PORT=1521))) STATUS of the LISTENER ------------------------ Alias LISTENER Version TNSLSNR for Linux: Version 12.2.0.1.0 - Production Start Date 28-JUL-2019 01:43:11 Uptime 0 days 0 hr. 0 min. 26 sec Trace Level off Security ON: Local OS Authentication SNMP OFF Listener Parameter File /u01/app/oracle/product/12.2.0/dbhome_1/admin/ORCLCDB/listener.ora Listener Log File /u01/app/oracle/diag/tnslsnr/3fda365c7b37/listener/alert/log.xml Listening Endpoints Summary... (DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=0.0.0.0)(PORT=1521))) (DESCRIPTION=(ADDRESS=(PROTOCOL=ipc)(KEY=EXTPROC1521))) (DESCRIPTION=(ADDRESS=(PROTOCOL=tcps)(HOST=3fda365c7b37)(PORT=5500))(Security=(my_wallet_directory=/u01/app/oracle/product/12.2.0/dbhome_1/admin/ORCLCDB/xdb_wallet))(Presentation=HTTP)(Session=RAW)) Services Summary... Service "8eb4210da4b50113e053020011ac5c30.localdomain" has 1 instance(s). Instance "ORCLCDB", status READY, has 1 handler(s) for this service... Service "ORCLCDB.localdomain" has 1 instance(s). Instance "ORCLCDB", status READY, has 1 handler(s) for this service... Service "ORCLCDBXDB.localdomain" has 1 instance(s). Instance "ORCLCDB", status READY, has 1 handler(s) for this service... Service "orclpdb1.localdomain" has 1 instance(s). Instance "ORCLCDB", status READY, has 1 handler(s) for this service... The command completed successfully下から3行目に
orclpdb1.localdomai
と書かれているのがわかります。
このサービス名を使ってデータベースに接続します。DB Navigator の画面で、 次の様に記述して "Test Connection" ボタン をクリックします。
Name Value Host localhost Database orclpdb1.localdomain (Service name) User developer Password developer 接続に成功します。
その他権限設定 (必要があれば)
SQL> grant create table to developer;
- 投稿日:2019-07-28T07:41:15+09:00
dockerでERROR: for mysql Cannot start service mysql: driver failed programming external connectivity on endpoint test-mysqlが出た時の対処
docker-compose run web rails new . --force --database=mysql --skip-bundleを実行するとエラーが出る
dockerでrailsの環境構築をしている中でいざ、railsのアプリを作成しようと以下のコマンドを実行
docker-compose run web rails new . --force --database=mysql --skip-bundleそうすると以下のようなエラーが出る。
ERROR: for mysql Cannot start service mysql: driver failed programming external connectivity on endpoint test-mysql (b2c5bfe3339ba18ad0892ba8fc5a1f427a89b3915035bc12b0c4c1207e016f75): Error starting userland proxy: Bind for 0.0.0.0:3306 failed: port is already allocated
ERROR: Encountered errors while bringing up the project.ようはそのポートはもう割り振ってしまっていますよって事ですね。
そのためポートを一度削除する必要があります。まずは占領しているポート番号を特定します。
ポート番号を特定したら以下のコマンドを実行
$ sudo lsof -i:3300 *ここではポート番号3300にしています。Password:
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
mysqld 94 _mysql 13u IPv6 0x6973239584594d63 0t0 TCP *:mysql (LISTEN)上の94という番号を今度はkillします。
sudo kill 9447そうしてもう一度
docker-compose run web rails new . --force --database=mysql --skip-bundle
を実行すれば完了です。
- 投稿日:2019-07-28T06:36:37+09:00
Verdaccio+Docker+EC2 その1
今回仕事で任されたタスクをもう一度最初から作っていきます。
Project
同僚がFrontendチームで使うというVerdaccioを何の前知識もなくDockerizeして使えるようにしていく。元々同僚の頭の中では形があったようだが詳細は全く説明されず、最後あたりで説明された。。。
最終的に設定するもの
- docker-compose (Verdaccio, Verdaccio s3 plugin, https-portal)
- Bitbucket pipeline
- git hooks - post-receiveUsage
Verdaccioアクセスはhttps://<サイト>A. Publish a package
1. チームの誰かがパッケージをパブリッシュする。
2. このパッケージはS3 bucketへ。
3. BitBucket Pipelineを通してプロダクションにデプロイ。B. Add a new user
1. 新規ユーザーを追加>conf/htpasswdに追加される。
2. BitBucket Pipelineを通してプロダクションにデプロイ。
今回はその1の設定
- Verdaccio (Dockerfile, config.yaml)
- EC2 (docker, docker-compose, npm)
Spec and Software
- Amazon Ubuntu Server 18.04
- Verdaccio
- Docker
Path
/home/ubuntu └── verdaccio ├── Dockerfile ├── conf │ ├── config.yaml │ └── htpasswd <- adduserで作られる ├── docker-compose.yaml ├── package.json <- publishで必要 ├── plugins └── storage └── @mypackage <- publishで追加されるverdaccio/conf/config.yaml
storage: /verdaccio/storage auth: htpasswd: file: /verdaccio/conf/htpasswd uplinks: npmjs: url: https://registry.npmjs.org/ packages: '@mypackage/*': <- ポイント access: $all publish: $authenticated '@*/*': access: $authenticated publish: $authenticated proxy: npmjs '**': proxy: npmjs logs: - {type: stdout, format: pretty, level: http}verdaccio/package.json
適当に作る。
npm init
名前は上のポイントに合わせる
"name": "@mypackage/verdaccio",
References
Tips for EC2
1) これはちゃんとReferencesのリンクでドキュメントされてます。
sudo chown -R 100:101 verdaccio/
2) 777でないとconf/htpasswdやstorage/@mypackageに書き込めない。他に方法があれば教えてください。
sudo chmod -R 777 verdaccio/conf/
sudo chmod -R 777 verdaccio/storage/
docker内ではこうなってる必要があります。
/ # ls -ltr /verdaccio/ drwxr-xr-x 2 100 101 4096 Jul 27 16:59 plugins drwxrwxrwx 2 100 101 4096 Jul 27 21:10 conf drwxrwxrwx 3 100 101 4096 Jul 27 21:11 storage / # ls -ltr /verdaccio/conf/ -rwxrwxrwx 1 100 101 372 Jul 27 21:03 config.yaml -rw-r--r-- 1 verdacci nogroup 55 Jul 27 21:10 htpasswd / # ls -ltr /verdaccio/storage/@mypackage/ drwxr-xr-x 2 verdacci nogroup 4096 Jul 27 21:28 verdaccio3) これは見つけるのになかなか時間がかかった。conf/config.yamlのパスをAbsolute Pathにする必要あり。
From
storage: ./storage
TO
storage: /verdaccio/storage
Error Messages
上1&2を設定しないと出るエラー。issues/1136とかで質問されてます。
EACCES: permission denied, open '/verdaccio/conf/htpasswd'
qqq is not allowed to publish package verdaccio : verdaccio
Useful commands
docker-compose up --force-recreate
docker exec -it --user=root verdaccio /bin/sh
Testing
1. docker起動
docker-compose up
expected loggingverdaccio | warn --- config file - /verdaccio/conf/config.yaml verdaccio | warn --- Plugin successfully loaded: verdaccio-htpasswd verdaccio | warn --- http address - http://0.0.0.0:4873/ - verdaccio/4.0.42. URLにアクセス
3. Add a user 'qqq'
npm adduser --registry http://<IP>:4873
expected logging
req: 'PUT /-/user/org.couchdb.user:qqq'
4. Login on URL as 'qqq'
expected logging
req: 'POST /-/verdaccio/login'
req: 'GET /-/verdaccio/packages'
5. Publish
npm publish --registry http://<IP>:4873
expected logging
req: 'PUT /@mypackage%2fverdaccio'
6. Refresh URL
expected logging
req: 'GET /-/verdaccio/packages'
- 投稿日:2019-07-28T02:25:45+09:00
Dockerベースの機械学習環境を構築できるOSSを作った
概要
自分が所属している研究室のメンバーで、機械学習の実験環境をスムーズに作れるOSSを作り、githubで公開した。
kronos
https://github.com/d-hacks/kronos
こちらにドキュメントなどが書いてあるが、ざっくり説明すると、このcliを入れると、機械学習のプロジェクトディレクトリの中にDockerのファイルを良い感じに作ってくれたり、その中で良い感じにjupyter notebookとかjupyter labとかを開いたり、Dockerのイメージの中でスクリプトを実行したりできる。何が嬉しいの?
特殊なパッケージをいれたい場合は少しDockerの知識が必要になるが、基本的にはDockerの知識いらずでローカル環境とGPU環境で同じコードを動かすことができる。(内部的にはnvidia-dockerを使っている)
$ kronos initってやると、既存のディレクトリ内に
docker
ディレクトリができる。$ kronos buildで、
docker
ディレクトリ内のファイルを読み込んでイメージをビルドし、$ kronos run experiment.pyで、イメージ内でスクリプトが実行される。
--gpu
オプションを使うと、gpuモードになって、gpuをdocker経由でアクセスできるようになる。まだ足りない点
今後は、より簡単にDockerイメージの中で実験を行えるようにしたいので、opencvだったり、強化学習系の実験に必要なライブラリだったりをもっとも楽にいれられるようにしたい。それこそ、Dockerの知識がなくてもできたら最高に嬉しい。
プルリク大歓迎です
どんどん新しいプルリクを投げてくれると嬉しいです。もっとシームレスなDocker環境の構築ができるツールを作っていきたい。。。