- 投稿日:2020-11-26T22:57:05+09:00
MySQL 文字コードによるINSERT時のエラー(ERROR 1366 (HY000):....)
はじめに
Dockerを使用して立てたMySQLのコンテナ上に自分以外の方がNestJSとtypeORMで書いてくれたmigrationコードを実行してデータベースを構築したところダミーデータ作成時に日本語の部分が以下のエラーで弾かれました。
ERROR 1366 (HY000): Incorrect string value:
このエラーに対してどのように対処したかの一例を述べます。 (ぽんこつすぎて6時間もってかれましたー。。。)ここで紹介するのはデータベースの文字コードを変えて再起動するだけでは解決できなかった事例です。
使用していたdocker-compose.ymlファイル
作成したデータベースの情報がコンテナremove時に失われないようにローカル環境に保持されるようにvolumesを設定しています。version: '3' services: mysql: image: mysql:5.7 volumes: - "./mysql:/var/lib/mysql" restart: always environment: - MYSQL_ROOT_PASSWORD=設定するパスワード - MYSQL_DATABASE=データベース名 ports: - 3306:3306 phpmyadmin: image: phpmyadmin/phpmyadmin environment: - PMA_ARBITRARY=1 - PMA_HOST=mysql - PMA_USER=root - PMA_PASSWORD=設定するパスワード ports: - 8080:80 volumes: - /sessionsとりあえず詳しいこと抜きで解決した方法
- MySQL初期設定用のmy.cnfを文字コードがutf8mb4で動作するように記述し作成し任意の場所に配置
- 作成したmy.cnfがdocker-compose.ymlでコンテナ上の/etc/my.cnfとして配置されるようにvolumesを記述
- エラーが発生しているデータベースを削除
- 同名データベースを作成
- migrateを実行(ここはmigrateコードなどの自動生成コードを作っていない場合はもう一度地道にテーブルを作り直してください)
やったこと
Dockerの公式MySQLの文字コードをutf8mb4に設定する
↑の記事を参考にした
こちらの記事では、commandでmysqldを実行するように書いてあるがここでもエラーが出て実行できなかった。
どうやらmysqldコマンドはrootユーザーでは使用できないようです。なのでちょっとめんどくさかったですが、my.cnfを作業ディレクトリで作成しvolumesで紐付け"my.cnf:/etc/my.cnf" でローカルに作成したmy.cnfをコンテナ上に配置するようにしました。
とりあえず/etcの下に配置すれば動作する感じです。my.cnfの内容で必須なのはcharacter-set-serverとdefault-character-setっぽい
尚、上の記事を参考に作成したmy.cnfではうまく動かなかったです。なので結局参考にしたのはこれ
MySQLで文字コードを直す時のmy.cnfの書き方 - 鶏口牛後な日々
結果的に作成したmy.cnfは以下のとおりです。[mysqld] character-set-server=utf8mb4 explicit-defaults-for-timestamp=1 general-log=1 general-log-file=/var/log/mysql/mysqld.log [mysql] default-character-set=utf8mb4出来上がったdocker-compose.ymlは以下の通り
version: '3' services: mysql: image: mysql:5.7 volumes: - "./mysql:/var/lib/mysql" - "./my.cnf:/etc/my.cnf" ←--- これを追加しました! restart: always environment: - MYSQL_ROOT_PASSWORD=設定するパスワード - MYSQL_DATABASE=データベース名 ports: - 3306:3306 phpmyadmin: image: phpmyadmin/phpmyadmin environment: - PMA_ARBITRARY=1 - PMA_HOST=mysql - PMA_USER=root - PMA_PASSWORD=設定するパスワード ports: - 8080:80 volumes: - /sessionsすでにデータベースを作成しているときはデータベース自体に文字コードを設定してしまっておりMySQLの設定を変更しても適用されないため、データベースの設定を変更する
MySQLの日本語文字化け回避!文字コードを確認&変更する方法 | サービス | プロエンジニア
これをやってもまだ動かなかった
データベースのテーブルを確認するとcollationが例のutf8じゃないやつになっていた。
なので、データベース自体を一度ドロップしてデータベースからテーブルまですべて削除し、migrate(データベースとテーブルの作成)しなおしたところcollationが変更され日本語がINSERTできるようになりました。まとめ
データベースの初期設定さえしっかりやっていればこんなことにはならなかったですね。。。
とりあえず、dockerでも自身のコンピュータのOS上でもMySQLなりpostgressなり動かす場合にはデータベースやらテーブルやらを作成する前にプロジェクトにあったもの(日本のプロジェクトの場合はだいたいutf8でしょうか)の初期設定を行ってはならないということですね。
- 投稿日:2020-11-26T21:10:44+09:00
Julia を始める人へ向けた MyWorkflow.jl の紹介
本日は
- 数ヶ月ぶりにQiitaを更新します.
- Julia アドベントカレンダー2020の記事です.これは jl.devもくもく会 の活動で作られました.
前置き
- 任意のプログラミング言語を触るにあたって最初の壁になるのは環境構築だと思います.多くの人は Python, R との連携するために PyCall.jl, PyPlot.jl, SymPy.jl, Pandas.jl, RCall.jl といった言語間の連携をするパッケージ及び Jupyter Notebook/Lab の上で Julia を動かすためのパッケージとして IJulia.jl を導入することが多いでしょう.Twitterで
#Julia言語
というハッシュタグを見るとつまづいている人が多く見られます.とりあえず,サクッと試したいのにそういうところでつまづいて成功体験を得られずに挫折しまう人いないでしょうか?MyWorkflow.jl の紹介
- MyWorkflow.jl ではそのような作業を済ませた Julia 動作環境を提供しています.厳密に言えば binder という Jupyter Notebook を動かすサービスに環境を作るスクリプトと Julia のサンプルコードを集めたGitHubのリポジトリです.
まずは動かしてみよう.
- MyWorkflow.jlの先にあるREADMEを見ると
launch binder
というアイコンが見えます.
それをクリックするだけで Jupyter Notebook の上で Julia が動く環境ができます.ブラウザを閲覧できる環境であれば,PCに限らずスマホ,タブレット端末でも実行できます.アイコンをクリックするとした図のような画面が出ますのでしばらくお待ちください.
動作環境は binder という Jupyter Notebook を動かすWebサービスを使いますので手元に Julia をインストールしなくても動作します.
しばらく待つと次のような画面がでます.Pythonなどで Jupyter Notebookを使ったことがある人には馴染みのある画面だと思います.
example.jl
というのをクリック・タップしてみましょう.画面が移り変わり次の図のようになると思います.パソコンでみている方は Shift キーと Enter を同時に押すことでセル(
In []:
で始まっているプログラムの塊)を実行することができます.スマホユーザーの場合は画面上にあるRun
というボタンを押すことで実行できます.動かした後は次のような画面になります.やった!
Hello, World
できましたね.ここではexample.jl
というファイルを開きましたが,末尾が.jl
で終わるファイルならば同様に実行できるはずです.たとえば,
pyplot.jl
はPyPlot.jl
という Python のmatplotlib
を Julia から呼び出す例をpyjulia.jl
は Python から Julia を呼び出す例を提供しています.お時間がある時に触ってみてください.しばらく放置するとサーバーとの接続が切れます.また,改変したデータは残らないので,必要に応じてテキストをコピペして各自の手元に保存してください.
手元でも動かしてみたい.
上記の説明では binder という web サービスを使って Julia を走らせましたが,CPUリソースに一定の制限があります.手元のPCが充実しているのであればそちらで動作した方が良いかもしれません.
ひとまず https://github.com/terasakisatoshi/MyWorkflow.jl.git をクローンします. GitHub に慣れていればリポジトリをフォークするというのもありですね.
$ git clone https://github.com/terasakisatoshi/MyWorkflow.jl.gitDocker を使う場合
- いきなり Docker かよ・・・.と思われるかもですが,この業界は怠け者が多いのでちょうど良いのではないでしょうか(この感想を持つ人はわかっている人だと思うので).Docker を使うと環境構築がローカル上に Julia をインストールしてパスを通して・・・というような作業も不要です.
$ make buildすることで Docker を使って環境を構築することができます.これは
Makefile
を見るとわかりますが$ rm -f Manifest.toml $ docker build -t myworkflojl . $ docker-compose build $ docker-compose run --rm julia julia --project=/work -e 'using Pkg; Pkg.instantiate()'とほぼ同じことをしています.このビルド作業はそれなりに時間がかかります.もっとサボりたい人は
$ make pullをすると GitHubActions をトリガーにして作られた Docker のイメージが降ってきます.(ただし,リポジトリの
master
の物が降ってきますので特定のコミットの状態を利用する場合はmake build
を用いてDockerイメージを作ってください.)Tip
binder
の環境はMyWorkflow.jl/binder/Dockerfile
の Dockerfile を用いて構築される- 手元の環境は
MyWorkflow.jl/Dockerfile
を用いて作られるコンテナを利用する
- REPL を使う
$ docker-compose run --rm julia Creating myworkflowjl_julia_run ... done _ _ _ _(_)_ | Documentation: https://docs.julialang.org (_) | (_) (_) | _ _ _| |_ __ _ | Type "?" for help, "]?" for Pkg help. | | | | | | |/ _` | | | | |_| | | | (_| | | Version 1.5.3 (2020-11-09) _/ |\__'_|_|_|\__'_| | Official https://julialang.org/ release |__/ | julia> # ここで色々試す
- Jupyter Notebook を使う場合
$ docker-compose up jupyterこの時
... ... 色々メッセージが出てくる ... mylab | To access the notebook, open this file in a browser: mylab | file:///root/.local/share/jupyter/runtime/nbserver-1-open.html mylab | Or copy and paste one of these URLs: mylab | http://19431004d7e8:8888/?token=xxxxxxxxxxxxxx mylab | or http://127.0.0.1:8888/?token=xxxxxxxxxxxxxというログが流れますので
http://127.0.0.1:8888/?token=xxxxxxxxxxxxx
をウェブブラウザURLの入力欄に貼り付けるとコンテナ内に作成されたカーネルにアクセスできます.
- Jupyter Lab を使う場合
$ docker-compose up labのようにし上記の Jupyter Notebook の使う場合と同様の手順を踏んでください.
Julia ユーザーのためのノートブックである Pluto.jl を使う場合
$ docker-compose up plutoCreating mypluto ... done Attaching to mypluto ... ...色々メッセージが出てくる ... mypluto | Go to http://0.0.0.0:9999/?secret=xxxxxx in your browser to start writing ~ have fun! mypluto | mypluto | Press Ctrl+C in this terminal to stop Pluto mypluto |Jupyter と同様に
http://0.0.0.0:9999/?secret=xxxxxx
をウェブブラウザのURL入力欄に貼り付けてください.他にも Atom/Juno とコンテナを連携する機能も作っていますが,詳細は README.md をご覧ください.
Dockerを使いたくない人向け
- 諸事情で使えない,使いたくない人向けに (Dockerに依存しない)Julia 環境構築を書いておきます.
Julia の環境構築(ダウンロードから)
- https://julialang.org/downloads に行きます.
Current stable release
と書いてあるテーブルから自分の環境に合わせた物をダウンロードします.32bit ?64bit ?どっち? と迷われた場合は場合はとりあえず64bit
の方を選んでください.
- 補足すると現時点の
Long-term support (LTS) release
は1.0.5
ですが近々1.6
が次期のLTS
となることになっています.またJuliaのパッケージ(いわゆるライブラリ)は 1.3 以降で出てきた機能を使うことが少なくないので 2020/12月 現在ではstable release の1.5.x
を使うのが良いでしょう.軽微なバグフィックスされるごとにx
の値が増えていきます(今はx=3
です)- 2020/12月現在は 1.6 は開発のステータス(DEV) になっています.もし 1.6 が安定版としてリリースされると 1.7 が開発のステータスになります.そして 1.5 はメンテナンスされなくなります.ですので,数ヶ月に一度の程度で定期的に Julia のホームページを見てリリース状態を確認するようにしてください.
インストール
- Platform Specific Instructions for Official Binaries を見てください.Windowsユーザーの場合は
rundll32 sysdm.cpl,EditEnvironmentVariables
の存在を知ると幸せになるかもです.環境構築を設定するコントロールパネルのUIが一瞬で出てきます.- そして Path を通してターミナルで
julia
と入力してREPLがひらくことを確認しましょう.MyWorkflow.jl の環境構築
ここでは MyWorkflow.jl を Julia のプロジェクトとしてみなし環境を構築することにします.今回紹介する MyWorkflow.jl だけでなく,自分でパッケージを開発したりするときにも役立つ tips が入っています.
$ git clone https://github.com/terasakisatoshi/MyWorkflow.jl $ cd MyWorkflow.jl $ julia --project=@. -e 'ENV["PYTHON"]=Sys.which("python3"); ENV["JUPYTER"]=Sys.which("jupyter"); using Pkg; Pkg.instantiate(); Pkg.precompile()' ... ... しばらく待つ ...この後は
jupyter notebook
で Jupyter Notebook を立ち上げます.これで MyWorkflow.jl に依存する Julia パッケージを導入することができます.何をやっているか書いておく
Pythonでは, venv, poetry や Anaconda の conda activate など,プロジェクト毎に依存するライブラリを整理する機能がありますが,Julia 同様に特定のプロジェクトに対して必要なパッケージを導入する機能を持っています.Juliaがそのプロジェクトにおいてどのようなパッケージ,どのバージョンをインストールするのかは
Project.toml
という toml フォーマットのファイルの情報に従います.
--project=@.
--project=Project.tomlのパス
を指定することでProject.toml
に対応する環境に入る準備ができます.指定しなければデフォルトの環境(npm などで言えば global なもの)としてみなされます.--project
を指定せずに起動しても後からusing Pkg; Pkg.activate(".")
として切り替えることができます.今回の例では作業ディレクトリにProject.toml
があるので--project=.
でも良いです.--project=@.
にすると作業ディレクトリにProject.toml
がない場合は 親のディレクトリを遡りProject.toml
を探しにいきます.参考: https://stackoverflow.com/questions/53613663/what-is-in-julia-project-command-line-option
ちなみに
export JULIA_PROJECT=@.
のように環境変数を定義しておくと--project=@.
の入力が省けます.
-e
-e '文字列'
で文字列を Julia の式として評価します.-E
もありますが違いは最後に評価した指揮の結果を出力するかしないかの違いです. たとえばjulia -E '1+1'
を試す.-e, --eval <expr> Evaluate <expr> -E, --print <expr> Evaluate <expr> and display the resultWindows 環境だと -e オプションが使えない場合があるようなのでパワーシェルをつかったり REPL を開いて実行するという方法で回避することもできます.
ENV["xxx"]=...
システムの環境変数を定義します.辞書データのように使うことができます.
ENV["PYTHON"]=Sys.which("python3")
をすることでPYTHON
という環境変数をpython3
の実行形式が住んでいるフルパスを示してくれます.シェルでのwhich python3
のようなものです.これは PyCall.jl が Julia がよぶ Python どれなのかを明示的に指示するために使われます.もしローカルの環境に Python がない場合はENV["PYTHON"]
は空の文字列となります.その場合,Julia が~/.julia/conda
以下に Miniconda を導入しこれと連携するようにできています.もしも,連携する Python を変更したい場合はjulia> ENV["PYTHON"]="新しいPythonのフルパス" julia> using Pkg; Pkg.build("PyCall") # buildするのが大事としてください.ここら辺の話は PyCall.jl のREADMEに全て書いてあります.
Windows の場合は
path\\to\\something
のようにパスの区切りを\\
にする必要があります.
Pkg.instantiate()
Project.toml
の情報をパースして,依存パッケージの依存関係などを詳細に記述されたManifest.toml
を作り依存関係の解決を図ります.ローカルの環境にパッケージがなかった場合は(そのパッケージが公式パッケージであれば)ダウンロードが始まります.一度instantiate
をした後はManifest.toml
の情報を読みにいきます.もしもおかしな挙動があった場合は一度Manifest.toml
手動で消して改めてusing Pkg; Pkg.instantiate()
すると良いかもしれません.
Pkg.precompile()
Julia は JIT コンパイル方式で動作します.そのため
using ...
などで Julia パッケージをロードするとその度にコンパイルが走ります.そのオーバーヘッドを抑えるために Julia がコンパイル結果の一部をキャッシュするようになっています.参考 https://docs.julialang.org/en/v1/manual/modules/#Module-initialization-and-precompilation
以上で ローカルでの環境構築の方法について述べました.色々手間があって大変ですね? Docker の方法を使うとコマンド一発でできますよ.(お?Dockerを使う気持ちになれましたか?)
まとめ
MyWorkflow.jl
に入門する機会を用いて とりあえず既存の環境を用いて Julia を試してみたい人から,Docker を使って環境構築する方法,ローカルに環境を構築する方法まで幅広いユーザーに対して Julia の利用方法,環境構築の方法を提示しました.一年前以上からどうしたら Julia の環境をスムーズに構築できるか,気軽に使ってもらえるように考えた結果 Docker を用いた現在のような方法に至りました.自分の作業ワークフローは人それぞれなので各々の事情に合わせて適切はスタイルを身につけられるといいのではと思います.この記事がその助けになれば幸いです.
- 投稿日:2020-11-26T19:20:16+09:00
DockerでCGI #1: EPWING電子辞書サーバー「let me see...」 (2003年)
はじめに
Docker始めました
流行ってから既に数年、今更Docker始めました。
導入手法が明確になり、環境も汚さないのは便利ですね。使える環境(自宅サーバー)を手に入れたのでSSHでつないでやってます。
正直これで良いのでは、というかそもそもDockerである必要性は…。別に環境汚れても良いし。
まぁ本格的な開発をするならローカルPCでやった方が楽なはず。あと配布は気楽。Let me see...
今回Docker対応したのは、電子辞書オープンラボ・かずひこ様による「let me see...」。
2000年代くらいまでCD-ROM電子辞書形式として使われたEPWING形式などに対応しています。
最終更新は2003年で、それをDocker対応するのがちょっと面白いですね。GitHubレポジトリはkurema/forkedLetMeSee、Dockerイメージはghcr.io/kurema/letmeseeです。
導入方法
対応手順を説明する前に導入方法は以下です。
あらかじめEPWing形式の辞書を適当な場所に配置しておいてください。$ git clone https://github.com/kurema/forkedLetMeSee.git $ cd forkedLetMeSee $ nano docker-compose.yml $ nano conf/letmesee.conf $ sudo docker-compose up -d辞書の場所に応じ
docker-compose.yml
を編集し、各辞書や設定をconf/letmesee.conf
に記述します。
conf/letmesee.conf
から見える場所はdocker-compose.yml
でのマウント先以下になるのは注意です。開発過程
Dockerを初めて二つ目なのでやはり手探りです。
いくつかベストプラクティスではかもしれません。Docker対応
Dockerってめんどくさいと思ってましたけど、ベースイメージからのインストール手順を
RUN
に書いていくだけの簡単な作業です。
Dockerfileが導入方法の説明にもなるので便利ですね。タイムゾーン設定
最初にDockerを試した時、以下のメッセージが出ました。
Please select the geographic area in which you live. Subsequent configuration questions will narrow this down by presenting a list of cities, representing the time zones in which they are located.Ubuntu 18.04以降でgitインストール時に発生するらしいです。
httpdでは必要ないかもしれませんが、tzdataは大抵必要だと思うので以下の対処をしています。
ただし、ユーザーが日本在住でない場合は別のタイムゾーンに設定する必要があります。DockerfileRUN apt-get update -y && \ apt-get install -y --no-install-recommends tzdata #Timezone is set to Japan assuming you are in Japan. ENV TZ=Asia/Tokyo参考記事
眠れない夜 (2018)「[Docker] build tzdata タイムゾーン選択回避方法(ubuntu)」エンジニアの眠れない夜 https://sleepless-se.net/2018/07/31/docker-build-tzdata-ubuntu/
@yagince (2018) 「Docker Ubuntu18.04でtzdataをinstallするときにtimezoneの選択をしないでinstallする」Qiita https://qiita.com/yagince/items/deba267f789604643babCGI有効化
ApacheでCGIの有効化はsedを使って設定ファイルを弄るような必要があるようです。スマートではないですね。将来壊れそうです。
できれば専用コマンドが欲しいところ。DockerfileRUN sed -ri 's/#LoadModule cgid_module/LoadModule cgid_module/g; \ s/DirectoryIndex index.html/DirectoryIndex index.rb index.cgi index.html/g; \ s/Options Indexes FollowSymLinks/Options Indexes FollowSymLinks ExecCGI/g; \ s/#Scriptsock cgisock/Scriptsock cgisock/g; \ s/#AddHandler cgi-script .cgi/AddHandler cgi-script .pl .rb .cgi/g' /usr/local/apache2/conf/httpd.confベースコンテナによってhttpd.confの場所が変わります。
扱っているCGIによってAddHandler cgi-script
やDirectoryIndex
の拡張子が変わったりします。今回は.rb
。
Scriptsock cgisock
をコメントインしないとService Unavailable
のエラーが出るようです(参考)。出ました。
以前やった記憶がなかったですが、その時はubuntuでa2enmodが使えたのでそのときに導入されるようです。
mpm_prefork_module
を使う可能性がある場合はs/#LoadModule cgi_module/LoadModule cgi_module/g;
も追加した方が良いでしょう。cgid
がcgi
になります。参考記事
ワタナベ書店 (2015) 「Docker上でApacheコンテナを作成しCGIのコンテンツを走らせるまで」 https://senyoltw.hatenablog.jp/entry/2015/10/21/175847
配置
後はファイルを配置するだけ。
CGIでは実行ファイルにchmodで実行権限を付与すること忘れないようにしましょう。今回は777にしました。個人的には
README.md
ファイルをイメージに配置するのが良いと思います。
何かの理由でDockerイメージだけあるという状況には多少便利です。
レポジトリ全体を圧縮して配置するとかも良いですが、大きめの画像を配置したりすることもあるのでやめました。
容量を気にするなら辞めるなりgz圧縮なり。DockerfileCOPY edict-devel/letmesee/ /usr/local/apache2/htdocs/ RUN chmod 777 /usr/local/apache2/htdocs/*.rb COPY README.md /トラブル
当初、
gem install
をする段でSSLエラーが出るというトラブルがありました。
一時期証明書の追加で対処しましたが、CGI側の修正で不要になりました。
それについてはこちらの記事参照。docker-compose
このCGIは設定ファイルがCGIと同じフォルダに配置されるタイプです。
Dockerイメージ内部のファイル自体を触るのは微妙なのでvolumes
でマウントします。
辞書自体のマウントも必要です。
dockerコマンド一行にしては長いですし、docker-composeファイルにしました。docker-compose.ymlversion: '3' services: letmesee: image: ghcr.io/kurema/letmesee:latest container_name: letmesee restart: always ports: - 50002:80 volumes: - ./conf/letmesee.conf:/usr/local/apache2/htdocs/letmesee.conf # /home/share/DictionaryをEPWing辞書が保存されている場所に変更してください。 - /home/share/Dictionary:/usr/local/dict公開
Docker HubがPull回数制限や利用されていないイメージの削除(後に保留)などを発表したので、イメージはGitHub Container Registryに保存することにしました。
6ヶ月どころか十年単位で放置する見込みなので、継続性が期待できるGitHubのサービスが確実だと判断しました。
マルチCPUアーキテクチャにも対応していて便利です。なにより無料。GitHub Actions
GitHub Actionsで自動化をしたかったのでGitHubコミュニティーの例のマルチCPUアーキテクチャ対応のワークフローをほぼそのまま使いました。
以下の点を変更しています。
- キャッシュはインラインで保存。GitHub Actions側に保存する方法もあるようです。
.github/workflows/docker.ymlcache-from: type=registry,ref=${{ steps.prep.outputs.latest }} cache-to: type=inline
- テスト用手動ディスパッチ。手動時のタグはtestになります。
.github/workflows/docker.ymlon: workflow_dispatch:
- デイリービルドは頻度が高すぎるので月刊に。
- プラットフォームは元イメージと同じ。無料だからってやりすぎですね。
.github/workflows/docker.ymlplatforms: linux/amd64,linux/arm64,linux/ppc64le,linux/s390x,linux/386,linux/arm/v7GitHub Container Registryはベータテスト中なので
GITHUB_TOKEN
が使えない点は注意が必要です。
自分でトークンを発行し、GHCR_PAT
をシークレットに登録してください。時間
ビルド時間はキャッシュなしで11分。キャッシュありで2分といったところです(testタグはキャッシュ元として参照しません)。
タグを付けたら自動でイメージをビルドしプッシュしてくれるのは楽です。
マンスリービルドを参照すれば最新のベースコンテナにも追従できますし。
ただ実を言えば個人開発でCI/CDなんてのは建前で、以下みたいな目的が本音ですね。
- 無料なので使いたい。
- 重い処理をオフロードしたい。
- ミスって個人情報が混入する事態を避けたい。
なので、テスト目的でもガンガンGitHub Actionsで実行して、GitHub Container Registryからイメージをpullして試してます。自前ビルドはしません。
幸いキャッシュが効いてるとCGIだけの修正では処理時間も短くサーバーの容量も新規では大して食いません。
合わせても100kb未満のCGIそれもダウンロード数が1-2桁(現時点では自分だけ)に数百MBを消費するのはどうなのかと最初は思いましたし、z/Architectureなんて環境でこれを使う人なんて絶対居ませんが、Dockerってのはそういう世界みたいです。怖いですね。改修
流石に古いだけあってそのまますんなり動くわけではありませんでしたが、少しの修正で動きます。
コメントがなくrubyは滅多に書かなくても分かりやすかったです。
元作者さんとruby開発者は見事です。
- Ruby側の更新に追従
- 相対パスへの
require
をrequire_relative
に。
- 2003年当時は
require 'letmesee.rb'
で同一パスのrbファイルを参照してくれたようです。- 現時点では
require './letmesee.rb'
にすれば動作します。- 実際には
require_relative './letmesee.rb'
が正しいようです。これを書いている途中に気付いたので修正しました。- CGIならカレントディレクトリの問題とかは起きないはずですが、念のため。
File::open( path )
をFile::open( path , "r:utf-8" )
に修正。
- 記憶にありませんが、文字化けでもしたんだと思います。
head['Content-Length'] = body.size.to_s
をhead['Content-Length'] = body.bytesize.to_s
に修正。
- 2003年当時はstring.sizeでバイト数を取得できたようです。上の修正のせいかもしれません。
- 出力が途中で途切れていたので不思議でした。
Iconv.iconv
の利用をやめてEncoding::Converter#convert
を利用するようにしました。
- iconvの引数は
iconv(to, from, *strs)
なのに対し、Encodingsではnew(source_encoding, destination_encoding)
と文字コードの順番が逆なので注意が必要です。- iconvのUTF-8は
UTF8
でも通るようですが、Encodingsでは通らないので注意が必要です(元々のバグかも)。- Encodingsでは変換元と変換後が同じ文字コードだとエラーが発生するようです。素通りしてくれれば良いのに。
- HTML5対応
- 音声には
<audio>
を使うようにしました。
- 動画はMPEG-1なので
<video>
ではまず動きません。
- JavaScriptで再生するjsmpegやwasm版のffmpegとかもありますが、十年単位で放置する予定なので辞めました。
- ヘッダの変更。簡単なスマートフォン対応(cssで
@media
の追加・viewport
)。old<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> <html lang="ja"> <head> <meta http-equiv="content-type" content="text/html; charset=utf-8"> <%= css_tag %>new<!DOCTYPE html> <html lang="ja"> <head> <meta http-equiv="content-type" content="text/html; charset=utf-8" /> <meta name="viewport" content="width=device-width,initial-scale=1.0" /> <%= css_tag %>
- サーバーの性能向上に伴い、ちょっと富豪的に。
- 辞書を絞って検索するメニューを削除。
- 邪魔なので。
<details>
でもいいと思いますが、当時(2017年)は知りませんでした。- 外字をデフォルトで最大フォントサイズに。
- 高解像度モニターも増えてますし。
- 存在しない場合最小フォントサイズの外字にフォールバックします。
- 高速化。
- トップページの静的表示
letmesee.rb
初期化時全辞書を読み込んでいたので、低性能マシンかつ大量辞書の環境では表示に時間が掛かっていました。- トップページでは辞書を読み込まず静的ファイルを表示するようにしたので表示が早くなりました。
- 外字のキャッシュ
- 外字が大量にある場合に遅かったので、ヘッダを変更し一日はキャッシュするようにしました。
- 本来は一文字ずつ取得しに行く処理は避けるべきです(後述)。
- XML対応
- XMLでの取得に対応しました。ただし、常にwell-formedだとは期待できません。
- Androidアプリでも作ろうかと思いましたが辞めました。
<b>
・<i>
<b>
や<i>
が入れ子になっても正常動作するように修正しました。
- 市販されている辞書では存在しないのか、入れ子に対応していないEPWINGソフトは結構あります。
- 変換された辞書では普通にあって、検索候補の途中から盛大にレイアウトが崩れるので面倒です。
- 対応しないタグを削除するようにしました。
- その他
- CSSや設定などを個人的な好みに合わせて変更。
- 独自テーマを追加し、デフォルトをそちらに変更。
- ロゴ画像を透過。
改修しなかった点は以下です。
- 設定ファイルでの辞書記述
- 大量の辞書を保存している場合、設定ファイルに一つずつ辞書のパスを追加するのは面倒です。
- 自動追加するスクリプトを書くか、最上位パスの指定だけで済むように変更したかったですが面倒なので辞めました。
- 外字表示の高速化
- 現状では外字一文字ずつサーバーに問い合わせるようになっています。
- 外字が多いと毎回CGIが起動して大量のIO処理が発生し時間が掛かります。
- キャッシュはするようにしました。
- 辞書ファイル側では各サイズ2ファイルで小容量なので一括で渡した方が良いかもしれません。
- 普通は数kb程度ですが、大きいと2MB程度にはなります。
- これはかなり大変そうでした。辞めました。
- さらなるデザインの改良。
- 自分で新しいデザインにしましたが、今見るとダサいです。
- これは今後修正するかもしれません。
手間を掛けず、簡単にできる修正だけしたという感じです。
ruby自体まず書かないですが、読みやすく破壊的変更も大してないので楽でした。
またこのCGI自体テーマがあったり拡張しやすい設計です。素晴らしい
CGIは良いものですね。シンプルで分かりやすくて。年表
日時 イベント 1987年 EPWINGの前身となるWINGフォーマット制定。 1988年 EPWING規約制定。 1993年 CGI登場 1995年 Ruby登場 1997年6月8日 EB Library 初版 2000年4月10日 ruby EB v1.0 2002年3月31日 ruby EB v1.7 (最新版) 2003年2月29日 let me see... v1.0 2003年11月9日 let me see... v1.1 (最新版) 2010年3月8日 EB Library v4.4.3 (最新版) 2013年3月13日 Docker 初版 2014年1月30日 ruby EB 久保健洋氏によるruby1.9対応非公式フォーク 初版 2014年1月30日 ruby EB 同フォーク 最終更新 2017年7月11日 let me see... kuremaによるフォーク v1.2 (微修正) 2020年11月10日 let me see... 同フォーク v1.3 (Docker対応) しかしすごい歴史ですね。壮大。
まぁEPWINGの世界ってのはこんな感じです。Unicode対応以外は現在でもそれなりに「使える」フォーマットなのは感心します。
違う世代の辞書ファンたちがちょっとずつ色々何かやってるのがEPWINGの魅力です。
今でもずっと辞書アプリを開発してくださってる方もいらっしゃいます。感謝。ちなみにこの記事では細かく触れませんでしたが、2017年にフォークし小改良、最近Docker対応のついでに中改良といった感じです。
さらに言えば記事の順番が前後しますが、これは私のDockerでCGI二つ目です。
一つ目はニコニコチャンネルキャッシュサーバー。そのうち記事にします。その他、EPWING関係リンク:
- EBStuio (EBWin4のような使いやすいソフト。スマホ版も。)
- 更に極めよ「EPWING/PDIC辞書」 (変換ツールリンク集)。
- MacDicConversion (macOS 辞書.app用変換スクリプト)。
- 投稿日:2020-11-26T19:20:16+09:00
DockerでCGI: EPWING電子辞書サーバー「let me see...」 (2003年)
はじめに
Docker始めました
流行ってから既に数年、今更Docker始めました。
導入手法が明確になり、環境も汚さないのは便利ですね。使える環境(自宅サーバー)を手に入れたのでSSHでつないでやってます。
正直これで良いのでは、というかそもそもDockerである必要性は…。別に環境汚れても良いし。
まぁ本格的な開発をするならローカルPCでやった方が楽なはず。あと配布は気楽。Let me see...
今回Docker対応したのは、電子辞書オープンラボ・かずひこ様による「let me see...」。
2000年代くらいまでCD-ROM電子辞書形式として使われたEPWING形式などに対応しています。
最終更新は2003年で、それをDocker対応するのがちょっと面白いですね。GitHubレポジトリはkurema/forkedLetMeSee、Dockerイメージはghcr.io/kurema/letmeseeです。
導入方法
対応手順を説明する前に導入方法は以下です。
あらかじめEPWing形式の辞書を適当な場所に配置しておいてください。$ git clone https://github.com/kurema/forkedLetMeSee.git $ cd forkedLetMeSee $ nano docker-compose.yml $ nano conf/letmesee.conf $ sudo docker-compose up -d辞書の場所に応じ
docker-compose.yml
を編集し、各辞書や設定をconf/letmesee.conf
に記述します。
conf/letmesee.conf
から見える場所はdocker-compose.yml
でのマウント先以下になるのは注意です。開発過程
Dockerを初めて二つ目なのでやはり手探りです。
いくつかベストプラクティスではかもしれません。Docker対応
Dockerってめんどくさいと思ってましたけど、ベースイメージからのインストール手順を
RUN
に書いていくだけの簡単な作業です。
Dockerfileが導入方法の説明にもなるので便利ですね。タイムゾーン設定
最初にDockerを試した時、以下のメッセージが出ました。
Please select the geographic area in which you live. Subsequent configuration questions will narrow this down by presenting a list of cities, representing the time zones in which they are located.Ubuntu 18.04以降でgitインストール時に発生するらしいです。
httpdでは必要ないかもしれませんが、tzdataは大抵必要だと思うので以下の対処をしています。
ただし、ユーザーが日本在住でない場合は別のタイムゾーンに設定する必要があります。DockerfileRUN apt-get update -y && \ apt-get install -y --no-install-recommends tzdata #Timezone is set to Japan assuming you are in Japan. ENV TZ=Asia/Tokyo参考記事
眠れない夜 (2018)「[Docker] build tzdata タイムゾーン選択回避方法(ubuntu)」エンジニアの眠れない夜 https://sleepless-se.net/2018/07/31/docker-build-tzdata-ubuntu/
@yagince (2018) 「Docker Ubuntu18.04でtzdataをinstallするときにtimezoneの選択をしないでinstallする」Qiita https://qiita.com/yagince/items/deba267f789604643babCGI有効化
ApacheでCGIの有効化はsedを使って設定ファイルを弄るような必要があるようです。スマートではないですね。将来壊れそうです。
できれば専用コマンドが欲しいところ。DockerfileRUN sed -ri 's/#LoadModule cgid_module/LoadModule cgid_module/g; \ s/DirectoryIndex index.html/DirectoryIndex index.rb index.cgi index.html/g; \ s/Options Indexes FollowSymLinks/Options Indexes FollowSymLinks ExecCGI/g; \ s/#Scriptsock cgisock/Scriptsock cgisock/g; \ s/#AddHandler cgi-script .cgi/AddHandler cgi-script .pl .rb .cgi/g' /usr/local/apache2/conf/httpd.confベースコンテナによってhttpd.confの場所が変わります。
扱っているCGIによってAddHandler cgi-script
やDirectoryIndex
の拡張子が変わったりします。今回は.rb
。
Scriptsock cgisock
をコメントインしないとService Unavailable
のエラーが出るようです(参考)。出ました。
以前やった記憶がなかったですが、その時はubuntuでa2enmodが使えたのでそのときに導入されるようです。
mpm_prefork_module
を使う可能性がある場合はs/#LoadModule cgi_module/LoadModule cgi_module/g;
も追加した方が良いでしょう。cgid
がcgi
になります。参考記事
ワタナベ書店 (2015) 「Docker上でApacheコンテナを作成しCGIのコンテンツを走らせるまで」 https://senyoltw.hatenablog.jp/entry/2015/10/21/175847
配置
後はファイルを配置するだけ。
CGIでは実行ファイルにchmodで実行権限を付与すること忘れないようにしましょう。今回は777にしました。個人的には
README.md
ファイルをイメージに配置するのが良いと思います。
何かの理由でDockerイメージだけあるという状況には多少便利です。
レポジトリ全体を圧縮して配置するとかも良いですが、大きめの画像を配置したりすることもあるのでやめました。
容量を気にするなら辞めるなりgz圧縮なり。DockerfileCOPY edict-devel/letmesee/ /usr/local/apache2/htdocs/ RUN chmod 777 /usr/local/apache2/htdocs/*.rb COPY README.md /トラブル
当初、
gem install
をする段でSSLエラーが出るというトラブルがありました。
一時期証明書の追加で対処しましたが、CGI側の修正で不要になりました。
それについてはこちらの記事参照。docker-compose
このCGIは設定ファイルがCGIと同じフォルダに配置されるタイプです。
Dockerイメージ内部のファイル自体を触るのは微妙なのでvolumes
でマウントします。
辞書自体のマウントも必要です。
dockerコマンド一行にしては長いですし、docker-composeファイルにしました。docker-compose.ymlversion: '3' services: letmesee: image: ghcr.io/kurema/letmesee:latest container_name: letmesee restart: always ports: - 50002:80 volumes: - ./conf/letmesee.conf:/usr/local/apache2/htdocs/letmesee.conf # /home/share/DictionaryをEPWing辞書が保存されている場所に変更してください。 - /home/share/Dictionary:/usr/local/dict公開
Docker HubがPull回数制限や利用されていないイメージの削除(後に保留)などを発表したので、イメージはGitHub Container Registryに保存することにしました。
6ヶ月どころか十年単位で放置する見込みなので、継続性が期待できるGitHubのサービスが確実だと判断しました。
マルチCPUアーキテクチャにも対応していて便利です。なにより無料。GitHub Actions
GitHub Actionsで自動化をしたかったのでGitHubコミュニティーの例のマルチCPUアーキテクチャ対応のワークフローをほぼそのまま使いました。
以下の点を変更しています。
- キャッシュはインラインで保存。GitHub Actions側に保存する方法もあるようです。
.github/workflows/docker.ymlcache-from: type=registry,ref=${{ steps.prep.outputs.latest }} cache-to: type=inline
- テスト用手動ディスパッチ。手動時のタグはtestになります。
.github/workflows/docker.ymlon: workflow_dispatch:
- デイリービルドは頻度が高すぎるので月刊に。
- プラットフォームは元イメージと同じ。無料だからってやりすぎですね。
.github/workflows/docker.ymlplatforms: linux/amd64,linux/arm64,linux/ppc64le,linux/s390x,linux/386,linux/arm/v7GitHub Container Registryはベータテスト中なので
GITHUB_TOKEN
が使えない点は注意が必要です。
自分でトークンを発行し、GHCR_PAT
をシークレットに登録してください。時間
ビルド時間はキャッシュなしで11分。キャッシュありで2分といったところです(testタグはキャッシュ元として参照しません)。
タグを付けたら自動でイメージをビルドしプッシュしてくれるのは楽です。
マンスリービルドを参照すれば最新のベースコンテナにも追従できますし。
ただ実を言えば個人開発でCI/CDなんてのは建前で、以下みたいな目的が本音ですね。
- 無料なので使いたい。
- 重い処理をオフロードしたい。
- ミスって個人情報が混入する事態を避けたい。
なので、テスト目的でもガンガンGitHub Actionsで実行して、GitHub Container Registryからイメージをpullして試してます。自前ビルドはしません。
幸いキャッシュが効いてるとCGIだけの修正では処理時間も短くサーバーの容量も新規では大して食いません。
合わせても100kb未満のCGIそれもダウンロード数が1-2桁(現時点では自分だけ)に数百MBを消費するのはどうなのかと最初は思いましたし、z/Architectureなんて環境でこれを使う人なんて絶対居ませんが、Dockerってのはそういう世界みたいです。怖いですね。改修
流石に古いだけあってそのまますんなり動くわけではありませんでしたが、少しの修正で動きます。
コメントがなくrubyは滅多に書かなくても分かりやすかったです。
元作者さんとruby開発者は見事です。
- Ruby側の更新に追従
- 相対パスへの
require
をrequire_relative
に。
- 2003年当時は
require 'letmesee.rb'
で同一パスのrbファイルを参照してくれたようです。- 現時点では
require './letmesee.rb'
にすれば動作します。- 実際には
require_relative './letmesee.rb'
が正しいようです。これを書いている途中に気付いたので修正しました。- CGIならカレントディレクトリの問題とかは起きないはずですが、念のため。
File::open( path )
をFile::open( path , "r:utf-8" )
に修正。
- 記憶にありませんが、文字化けでもしたんだと思います。
head['Content-Length'] = body.size.to_s
をhead['Content-Length'] = body.bytesize.to_s
に修正。
- 2003年当時はstring.sizeでバイト数を取得できたようです。上の修正のせいかもしれません。
- 出力が途中で途切れていたので不思議でした。
Iconv.iconv
の利用をやめてEncoding::Converter#convert
を利用するようにしました。
- iconvの引数は
iconv(to, from, *strs)
なのに対し、Encodingsではnew(source_encoding, destination_encoding)
と文字コードの順番が逆なので注意が必要です。- iconvのUTF-8は
UTF8
でも通るようですが、Encodingsでは通らないので注意が必要です(元々のバグかも)。- Encodingsでは変換元と変換後が同じ文字コードだとエラーが発生するようです。素通りしてくれれば良いのに。
- HTML5対応
- 音声には
<audio>
を使うようにしました。
- 動画はMPEG-1なので
<video>
ではまず動きません。
- JavaScriptで再生するjsmpegやwasm版のffmpegとかもありますが、十年単位で放置する予定なので辞めました。
- ヘッダの変更。簡単なスマートフォン対応(cssで
@media
の追加・viewport
)。old<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> <html lang="ja"> <head> <meta http-equiv="content-type" content="text/html; charset=utf-8"> <%= css_tag %>new<!DOCTYPE html> <html lang="ja"> <head> <meta http-equiv="content-type" content="text/html; charset=utf-8" /> <meta name="viewport" content="width=device-width,initial-scale=1.0" /> <%= css_tag %>
- サーバーの性能向上に伴い、ちょっと富豪的に。
- 辞書を絞って検索するメニューを削除。
- 邪魔なので。
<details>
でもいいと思いますが、当時(2017年)は知りませんでした。- 外字をデフォルトで最大フォントサイズに。
- 高解像度モニターも増えてますし。
- 存在しない場合最小フォントサイズの外字にフォールバックします。
- 高速化。
- トップページの静的表示
letmesee.rb
初期化時全辞書を読み込んでいたので、低性能マシンかつ大量辞書の環境では表示に時間が掛かっていました。- トップページでは辞書を読み込まず静的ファイルを表示するようにしたので表示が早くなりました。
- 外字のキャッシュ
- 外字が大量にある場合に遅かったので、ヘッダを変更し一日はキャッシュするようにしました。
- 本来は一文字ずつ取得しに行く処理は避けるべきです(後述)。
- XML対応
- XMLでの取得に対応しました。ただし、常にwell-formedだとは期待できません。
- Androidアプリでも作ろうかと思いましたが辞めました。
<b>
・<i>
<b>
や<i>
が入れ子になっても正常動作するように修正しました。
- 市販されている辞書では存在しないのか、入れ子に対応していないEPWINGソフトは結構あります。
- 変換された辞書では普通にあって、検索候補の途中から盛大にレイアウトが崩れるので面倒です。
- 対応しないタグを削除するようにしました。
- その他
- CSSや設定などを個人的な好みに合わせて変更。
- 独自テーマを追加し、デフォルトをそちらに変更。
- ロゴ画像を透過。
改修しなかった点は以下です。
- 設定ファイルでの辞書記述
- 大量の辞書を保存している場合、設定ファイルに一つずつ辞書のパスを追加するのは面倒です。
- 自動追加するスクリプトを書くか、最上位パスの指定だけで済むように変更したかったですが面倒なので辞めました。
- 外字表示の高速化
- 現状では外字一文字ずつサーバーに問い合わせるようになっています。
- 外字が多いと毎回CGIが起動して大量のIO処理が発生し時間が掛かります。
- キャッシュはするようにしました。
- 辞書ファイル側では各サイズ2ファイルで小容量なので一括で渡した方が良いかもしれません。
- 普通は数kb程度ですが、大きいと2MB程度にはなります。
- これはかなり大変そうでした。辞めました。
- さらなるデザインの改良。
- 自分で新しいデザインにしましたが、今見るとダサいです。
- これは今後修正するかもしれません。
手間を掛けず、簡単にできる修正だけしたという感じです。
ruby自体まず書かないですが、読みやすく破壊的変更も大してないので楽でした。
またこのCGI自体テーマがあったり拡張しやすい設計です。素晴らしい
CGIは良いものですね。シンプルで分かりやすくて。年表
日時 イベント 1987年 EPWINGの前身となるWINGフォーマット制定。 1988年 EPWING規約制定。 1993年 CGI登場 1995年 Ruby登場 1997年6月8日 EB Library 初版 2000年4月10日 ruby EB v1.0 2002年3月31日 ruby EB v1.7 (最新版) 2003年2月29日 let me see... v1.0 2003年11月9日 let me see... v1.1 (最新版) 2010年3月8日 EB Library v4.4.3 (最新版) 2013年3月13日 Docker 初版 2014年1月30日 ruby EB 久保健洋氏によるruby1.9対応非公式フォーク 初版 2014年1月30日 ruby EB 同フォーク 最終更新 2017年7月11日 let me see... kuremaによるフォーク v1.2 (微修正) 2020年11月10日 let me see... 同フォーク v1.3 (Docker対応) しかしすごい歴史ですね。壮大。
まぁEPWINGの世界ってのはこんな感じです。Unicode対応以外は現在でもそれなりに「使える」フォーマットなのは感心します。
違う世代の辞書ファンたちがちょっとずつ色々何かやってるのがEPWINGの魅力です。
今でもずっと辞書アプリを開発してくださってる方もいらっしゃいます。感謝。ちなみにこの記事では細かく触れませんでしたが、2017年にフォークし小改良、最近Docker対応のついでに中改良といった感じです。
さらに言えば記事の順番が前後しますが、これは私のDockerでCGI二つ目です。
一つ目はニコニコチャンネルキャッシュサーバー。そのうち記事にします。その他、EPWING関係リンク:
- EBStuio (EBWin4のような使いやすいソフト。スマホ版も。)
- 更に極めよ「EPWING/PDIC辞書」 (変換ツールリンク集)。
- MacDicConversion (macOS 辞書.app用変換スクリプト)。
- 投稿日:2020-11-26T17:41:57+09:00
Nodejsのコンテナを作成してみた。
はじめに
Nodejs
のバックエンドサーバーをコンテナで作成する際、ベースイメージをnodejs(node:10)
にしてDockerfileを作成したが、コンテナにアタッチができず、中の構成などを直接見る方法が分からなかった。
困ったときにはnodejs
の設定などを見れるようにしたかったので、試しにUbuntu
ベースにnodejs
をインストールする方法でDockerfileを作成してみた。
(記事の内容は、試しに動かすところまで記載あり。)実行環境(前提)
【Docker導入環境】
・Ubuntu 20.04 LTS(GCP上)
・docker 19.03.13今回やった事のメモ
今回の作業のためにテストフォルダを作成して、そこに移動。
$ sudo mkdir ./test_container $ cd ./test_containerDockerfileの作成
$ sudo nano ./DockerfileDockerfile# ベースイメージ FROM ubuntu:20.04 # 必要パッケージのインストール RUN apt update RUN apt install -y tzdata RUN apt install -y \ nodejs \ npm # Nodejs関連のパッケージインストール RUN mkdir /usr/src/app WORKDIR /usr/src/app COPY ./package.json ./ RUN npm install # index.jsファイルの設置 COPY ./index.js ./ # ポート開放 EXPOSE 8080 CMD ["node", "index.js"]nodejsモジュールをインストールするための
package.json
を作成
※上記のnpm install
のタイミングで、package.json
の情報を元にインストールが行われる。$ sudo nano ./package.jsonpackage.json{ "name": "test", "description": "test", "version": "0.0.1", "main": "index.js", "private": true, "license": "Apache Version 2.0", "author": "Google Inc.", "engines": { "node": "10" }, "dependencies": { "express": "^4.17.1", "moment-timezone": "^0.5.31", "body-parser": "^1.19.0" } }
index.js
を作成$ sudo nano ./index.jsindex.jsconst express = require('express'); const bodyParser = require('body-parser'); const app = express(); const port = 8080; app.use(bodyParser.urlencoded({ extended: true })); app.get('/get_test1', function(req, res) { res.send('GET1パラメータ取得: ' + req.query.get1) }); app.get('/get_test2', function(req, res) { res.send('GET2パラメータ取得: ' + req.query.get2) }); app.post('/post_test1', function(req, res) { res.send('POST-URLへの送信です。') }); app.post('/post_test2', function(req, res) { res.send('POSTパラメータ取得: ' + req.body.data1) }); app.listen(port)ビルド実行
$ docker image build -t test_container:v1 ./コンテナ作成 & 起動
$ docker container run -it -d -p 80:8080 --name con1 test_container:v1動作確認
GETの動きを確認
以下に接続して、上記で設定したレスポンスが返ってくればOK。
外部IP/get_test1?get1=10
外部IP/get_test2?get2=20
POSTの動きを確認
ローカル環境(自分のPC)に適当なhtml
ファイルを作成して、それをブラウザで開きアクセス。post_test1.html(POST送信のテスト用)<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <title>POSTテスト用</title> </head> <body> <form action="http://[外部IP]/post_tset" method="post"> <input type="hidden" name="data1" value="aiueo"> <input type="submit" value="送信"> </form> </body> </html>post_test2.html(POSTパラメータ取得の確認用)<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <title>POSTテスト用</title> </head> <body> <form action="http://[外部IP]/post_tset2" method="post"> <input type="hidden" name="data1" value="aiueo"> <input type="submit" value="送信"> </form> </body> </html>おまけ
はじめの背景の部分で 『コンテナにアタッチができず、中の構成などを直接見る方法が分からなかった』 と書いたが、コンテナ作成時の[CMD] コマンドを
/bin/bash
で上書きできていなかっただけだった・・・
- 投稿日:2020-11-26T16:18:16+09:00
Docker のコンテナを一括で削除したい時
- 投稿日:2020-11-26T16:18:16+09:00
Docker のコンテナを全て掃除したい時
- 投稿日:2020-11-26T15:31:44+09:00
Dockerfileでビルド時に外部から変数を注入して、ファイルに書き込みたい
目的
- Dockerfileでビルド時に外部から変数を注入して、ファイルに書き込みたい
ARGとENVを用いる
DockerfileFROM alpine ARG TEST ENV TEST ${TEST} RUN echo "TEST eq ${TEST}" > /testdocker-compose.ymlversion: "3" services: testweb: build: context: . dockerfile: ./Dockerfile args: - TEST="aaa" tty: true$ docker-compose up --build -d && docker ps Building testweb Step 1/4 : FROM alpine ---> d6e46aa2470d Step 2/4 : ARG TEST ---> Using cache ---> 7a550663ef1a Step 3/4 : ENV TEST ${TEST} ---> Using cache ---> 63c05c3ea630 Step 4/4 : RUN echo "TEST eq ${TEST}" > /test ---> Using cache ---> 8e0906c4cb26 Successfully built 8e0906c4cb26 Successfully tagged test2_testweb:latest Starting test2_testweb_1 ... done CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES e4726f3e8e02 test2_testweb "/bin/sh" 11 seconds ago Up Less than a second test2_testweb_1 $ docker exec -it e4 sh / # cat /test TEST eq aaaRef
- 投稿日:2020-11-26T15:30:51+09:00
nextjs-auth0をdocker上でも使いたい
nextjsでも簡単にauth0を使いたい!
と思ってサンプルに従うと、Dockerビルドってかproduction buildするとエラーで落ちちゃいます。configのサンプル
auth0/nextjs-auth0: Next.js SDK for signing in with Auth0 (Experimental) - https://github.com/auth0/nextjs-auth0#runtime-configurationimport { initAuth0 } from '@auth0/nextjs-auth0'; import config from './config'; export default initAuth0({ domain: process.env.AUTHO_DOMAIN, clientId: process.env.AUTHO_DOMAIN, clientSecret: process.env.APP_SECRRET, scope: 'openid profile',エラー内容こんな感じ。process.env.XXXが見れないから落ちてるのかな。
Automatically optimizing pages... > Build error occurred Error: A valid Auth0 Domain must be provided at Object.createInstance [as default] (/usr/src/app/node_modules/@auth0/nextjs-auth0/dist/instance.node.js:10:15) at initAuth0 (/usr/src/app/node_modules/@auth0/nextjs-auth0/dist/index.js:8:46) at Object.xMDF (/usr/src/app/.next/server/static/GDDYxjIWKG2mlWWc4bTrE/pages/_app.js:781:127) at __webpack_require__ (/usr/src/app/.next/server/static/GDDYxjIWKG2mlWWc4bTrE/pages/_app.js:23:31) at Module.1TCz (/usr/src/app/.next/server/static/GDDYxjIWKG2mlWWc4bTrE/pages/_app.js:147:13) at __webpack_require__ (/usr/src/app/.next/server/static/GDDYxjIWKG2mlWWc4bTrE/pages/_app.js:23:31) at Object.0 (/usr/src/app/.next/server/static/GDDYxjIWKG2mlWWc4bTrE/pages/_app.js:99:18) at __webpack_require__ (/usr/src/app/.next/server/static/GDDYxjIWKG2mlWWc4bTrE/pages/_app.js:23:31) at /usr/src/app/.next/server/static/GDDYxjIWKG2mlWWc4bTrE/pages/_app.js:91:18 at Object.<anonymous> (/usr/src/app/.next/server/static/GDDYxjIWKG2mlWWc4bTrE/pages/_app.js:94:10) error Command failed with exit code 1ボツ案
issue投げて聞いてみたら、
.env
を仮データで追加し実行時に再ビルドすると大丈夫らしい。試したらできた。Can't build on docker. · Issue #86 · auth0/nextjs-auth0 - https://github.com/auth0/nextjs-auth0/issues/86
DockerfileFROM node:14 as builder WORKDIR /app COPY .env.template .env COPY package.json yarn.lock ./ RUN yarn install COPY . . RUN yarn build # ------------------ FROM node:14-alpine as release WORKDIR /app COPY --from=builder /app/package.json /app/yarn.lock ./ RUN yarn install COPY --from=builder /app/.next ./.next COPY . . EXPOSE 3000 CMD ["yarn", "start:build"]package.json"scripts": { "start": "next start", "start:build": "yarn build && yarn start", "build": "next build",確かにできるんだけど、CI時にビルド2回ぐらい回しちゃうことになって、めっちゃ時間かかる・・・
改善案
initAuth0のオブジェクトを直接export defaultすると、環境変数がバインドされちゃうので、関数にしてあとからprocess.envを評価すればいいじゃんって案
Document best practices for using nextjs-auth0 with a nextjs production build. · Issue #154 · auth0/nextjs-auth0 - https://github.com/auth0/nextjs-auth0/issues/154
環境変数はgetConfig()経由で受け取り、バインドすると想定通り動いてくれた。やったね!
src/lib/auth0.jsimport getConfig from 'next/config'; import { initAuth0 } from '@auth0/nextjs-auth0'; const { serverRuntimeConfig, publicRuntimeConfig } = getConfig(); let auth0 = null; const proc = () => { if (!auth0) { auth0 = initAuth0({ clientId: publicRuntimeConfig.auth0.clientId, domain: publicRuntimeConfig.auth0.domain, scope: 'openid email profile', postLogoutRedirectUri: publicRuntimeConfig.baseUrl, redirectUri: `${publicRuntimeConfig.baseUrl}/api/auth/signed-in`, clientSecret: serverRuntimeConfig?.auth0?.secret ?? '', session: { cookieSecret: serverRuntimeConfig?.appSecret ?? '', // Set to 8 hours cookieLifetime: 60 * 60 * 8, storeIdToken: false, storeAccessToken: false, storeRefreshToken: false, }, oidcClient: { // Optionally configure the timeout in milliseconds for HTTP requests to Auth0. httpTimeout: 2500, // Optionally configure the clock tolerance in milliseconds, if the time on your server is running behind. clockTolerance: 10000, }, }); } return auth0; }; export default proc;next.config.jsconst nextConfig = { serverRuntimeConfig: { appSecret: process.env.APP_SECRET, auth0: { secret: process.env.AUTH0_CLIENT_SECRET, }, }, publicRuntimeConfig: { baseUrl: process.env.NEXT_PUBLIC_BASE_URL, auth0: { clientId: process.env.NEXT_PUBLIC_AUTH0_CLIENT_ID, domain: process.env.NEXT_PUBLIC_AUTH0_DOMAIN, }, }, }; module.exports = nextConfig;page/auth/api/sign-in.jsdiff --git a/pages/api/auth/sign-in.js b/pages/api/auth/sign-in.js index 7b109a7..d271932 100644 --- a/pages/api/auth/sign-in.js +++ b/pages/api/auth/sign-in.js @@ -1,6 +1,8 @@ -import auth0 from '../../../src/lib/auth0'; +import Auth0 from '../../../src/lib/auth0'; import { checkHeaders } from '../../../src/lib/middleware/auth'; +const auth0 = Auth0(); + const login = checkHeaders(async (req, res) => { try { await auth0.handleLogin(req, res);
- 投稿日:2020-11-26T14:59:12+09:00
Windows開発環境2020 - VScode/Docker/WSL2
WSL2
要件はバージョン 1903 以降、 ビルド 18362 以上。
手順 1 - Linux 用 Windows サブシステムを有効にする
管理者として PowerShell を開き、以下を実行します。
dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart手順 2: 仮想マシンの機能を有効にする
管理者として PowerShell を開き、以下を実行します。
dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart終わったらPC再起動
手順 3 - Linux カーネル更新プログラム パッケージをダウンロードする
以下をインストール
- x64 マシン用 WSL2 Linux カーネル更新プログラム パッケージ手順 4 - WSL 2 を既定のバージョンとして設定する
管理者として PowerShell を開き、以下を実行します。
wsl --set-default-version 2手順 5 - 選択した Linux ディストリビューションをインストールする
Microsoft Store を開き、希望する Linux ディストリビューションを選択します。基本Ubuntuで良いかと。
手順 6 - 新しいディストリビューションを設定する
新しくインストールした Linux ディストリビューションを初めて起動すると、コンソール ウィンドウが開き、ファイルが圧縮解除されて初期設定が走ります。途中でユーザー名とパスワードを決めてくれと言われますので入力。
Docker Desktop
前提としてWindows 10 ProであればHyper-Vの有効化が必須。
HomeであればUEFIから設定かも。詳しくは管理者に聞いてください。インストール
下記からDocker Desktop Windows版 をダウンロード
ダウンロードが出来たらインストーラーを実行します。
必ず「Enable WSL 2 Windows Features」にチェックが入っている事を確認してインストールしてください。開発環境導入
ここからWSL2上で開発環境を構築していきます。
PowerShellでwsl
コマンドを打つ事でWSL2に切り替えできます。最新の状態に更新
まずは最新にしておきましょう
sudo apt update && sudo apt upgrade -ynvmをインストール
インストール
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.37.0/install.sh | bashインストール出来たか確認するためターミナルを閉じて再度WSLに入ってから下記を叩く
command -v nvm
nvm
と返ってきたらインストール完了Node.jsをインストール
安定版(LTS)をインストール
nvm install node --lts確認します
$node -v v14.15.1yarnをインストール
公式ドキュメント通りにインストールします
curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add - echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list sudo apt update && sudo apt install -y yarn確認します
$ yarn -v 1.22.5同時に入る古いNodejsを削除しておく
sudo apt purge -f libuv1 nodejs
- 投稿日:2020-11-26T14:04:25+09:00
LinuxにDockerをInstallするAnsible-Galaxy
「ubuntu docker install」
「centos docker install」などをググって、Docker公式サイトより手動でインストールしていました。
毎回インストールするのは手間なので、自動化したいと思っていた矢先、
OSSで素晴らしいansible-galaxyを見つけましたので、紹介したいと思います。使用するansible-galaxy
Ansible Galaxy: https://galaxy.ansible.com/geerlingguy/docker
GitHub: https://github.com/geerlingguy/ansible-role-dockerAnsible Playbookを書いてみる
VirtualBox上にUbuntu20.04のVMを作成します。
Vagrantを用いて、Provisioning ToolとしてAnsibleを使用し、そのPlaybookでDockerをインストールします。Vagrantfileを作成する
以下の
Vagrantfile
を作成します。
IPアドレスは192.168.0.3/24
としてあります。# -*- mode: ruby -*- # vi: set ft=ruby : VAGRANTFILE_API_VERSION = "2" Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| config.vm.define "ansible-docker" do |node| node.vm.box = "bento/ubuntu-20.04" node.vm.hostname = "ansible-docker" node.vm.network "private", ip: "192.168.0.3", netmask: "255.255.255.0" node.vm.provider "virtualbox" do |vb| vb.memory = "1024" end end config.vm.provision "ansible" do |ansible| ansible.playbook = "./install_docker.yml" ansible.inventory_path = "./inventory.ini" ansible.limit = 'all' end endroleをインストールする
rolesディレクトリを作成して、その中に今回使用するroleをインストールします。
mkdir roles ansible-galaxy install geerlingguy.docker -p roles/InventoryとPlaybookを作る
inventory.ini
[ansible-docker] 192.168.0.3
install_docker.yml
--- hosts: all roles: role: geerlingguy.docker become: yesPlaybookを実行する
ディレクトリ構造は以下のようになっています。
. ├── Vagrantfile ├── install_docker.yml ├── inventory.ini └── roles └── geerlingguy.docker ├── LICENSE ├── README.md ├── defaults │ └── main.yml ├── handlers │ └── main.yml ├── meta │ └── main.yml ├── molecule │ └── default │ ├── converge.yml │ └── molecule.yml └── tasks ├── docker-compose.yml ├── docker-users.yml ├── main.yml ├── setup-Debian.yml └── setup-RedHat.yml以下のコマンドでVM作成します。(ホストOSでAnsible実行環境が整備されている必要があります。)
vagrant up
docker
,docker-compose
がインストールされていることを確認できます。vagrant@ansible-docker:~$ sudo docker -v Docker version 19.03.12, build 48a66213fe vagrant@ansible-docker:~$ sudo docker-compose -v docker-compose version 1.26.0, build d4451659
- 投稿日:2020-11-26T09:47:26+09:00
dockerのcontainerが起動後すぐに停止してしまう問題を見つける方法
$ docker-compose logsこのコマンドで、docker起動時のログを見る事ができます。
僕の場合は、vueがすぐに停止してしまい、こんなエラーが出てきました。front_1 | npm ERR! errno ENOENT front_1 | npm ERR! app@0.1.0 serve: `vue-cli-service serve` front_1 | npm ERR! spawn ENOENT front_1 | npm ERR! front_1 | npm ERR! Failed at the app@0.1.0 serve script. front_1 | npm ERR! This is probably not a problem with npm. There is likely additional logging output above. front_1 | npm WARN Local package.json exists, but node_modules missing, did you mean to install? front_1 | front_1 | npm ERR! A complete log of this run can be found in: front_1 | npm ERR! /root/.npm/_logs/2020-11-26T00_35_26_684Z-debug.log front_1 | front_1 | > app@0.1.0 serve /app front_1 | > vue-cli-service serve front_1 | front_1 | sh: vue-cli-service: not found front_1 | npm ERR! code ELIFECYCLE front_1 | npm ERR! syscall spawn front_1 | npm ERR! file sh front_1 | npm ERR! errno ENOENT front_1 | npm ERR! app@0.1.0 serve: `vue-cli-service serve` front_1 | npm ERR! spawn ENOENT front_1 | npm ERR! front_1 | npm ERR! Failed at the app@0.1.0 serve script. front_1 | npm ERR! This is probably not a problem with npm. There is likely additional logging output above. front_1 | npm WARN Local package.json exists, but node_modules missing, did you mean to install? front_1 | front_1 | npm ERR! A complete log of this run can be found in: front_1 | npm ERR! /root/.npm/_logs/2020-11-26T00_36_01_318Z-debug.log front_1 | mba:project hoge$原因がわかったので、これをググります。