- 投稿日:2019-03-05T22:18:06+09:00
Ansible+Jupyter-notebookをDockerで使うためのDockerfileの書き方
色々とハマったのでメモっておきます。
ファイル内のコメント(#)で説明を書いてます。
インストールしているもの:
python3, git, ansible, jupyter notebook, ubuntuの日本語化パッケージ(公式のubuntuイメージはそのままだと日本語ファイルが文字化けしたので)gitは、対象機器からコンフィグを採取してファイルにした時、社内サーバに立てたGitLabに簡単に保管できるようにするため入れてます。
Dockerfile
FROM ubuntu:latest MAINTAINER your_name # イメージ容量の肥大を抑えるため、RUNはなるべく一つにまとめる。 # set -xを最初に書く事でコメントをshellに表示できるのでDockerのビルドプロセスを確認しやすい RUN set -x && \ apt-get update && \ : "python3, pip, ubuntuの日本語化パッケージ, gitをインストールする" && \ apt-get install -y \ language-pack-ja-base language-pack-ja \ git \ python3-pip && \ locale-gen ja_JP.UTF-8 && \ : "ansibleとjupyter notebookをインストールする" && \ python3 -m pip install --upgrade pip && \ python3 -m pip install ansible && \ python3 -m pip install jupyter # 日本語の扱いを可能にするための環境変数 ENV LANG ja_JP.UTF-8これで確か、だいたい1GBくらいのイメージが出来上がった。
Buildコマンドについて
Proxy環境下では、Proxy認証のための値を渡してやらないとRUNで書いたものが実行できません。
そのため、--build-argオプションを使って対処します。また、今回はansible-basicというイメージ名にしました。
docker build --build-arg \ http_proxy=http://yourname@password:port \ --build-arg https_proxy=http://yourname@password:port -t ansible-basic .ちなみに
jupyter notebookをDockerから使いたい場合は、イメージをrunする時にポートの指定が必要です。
以下はその例。
docker run -p 8080:8080 -it --name test01 ansible-basic:latest /bin/bash
上記のコマンドでコンテナを作成した場合、またrootユーザーのままJupyter notebookを起動する場合はコマンドは以下のようになる。
jupyter notebook --port 8080 --ip=0.0.0.0 --allow-root
上記のコマンドでアドレスが出力されるので、ブラウザに打ち込んで以下のように書き変える。ex.)http://abcabcabchash:8080/hogehoge...
→http://ホストのIPaddress:8080/hogehoge...これで見れるはず。
------後記------
データセンターに設置してある機器にAnsibleを使いたい、という時、データセンターの外から
SSHの許可がない場合はデータセンタへの持ち込み用PCにAnsible環境を入れて作業する事になるかと思います。そんな時、各PCごとに環境構築するのは非常に手間がかかるし、また色んな人が毎回微妙に異なる環境を構築すると管理も大変になってしまうので、Dockerを使って用意することにしました。
Vagrant or Docker で迷いましたが、Ansibleでの作業が終われば手軽に削除が可能なのと、素早く環境を立てられる理由からDockerを選定しました。
- 投稿日:2019-03-05T22:18:06+09:00
AnsibleをDockerで使うためのDockerfileの書き方
色々とハマったのでメモっておきます。
ファイル内のコメント(#)で説明を書いてます。
インストールしているもの:
python3, git, ansible, jupyter notebook, ubuntuの日本語化パッケージ(公式のubuntuイメージはそのままだと日本語ファイルが文字化けしたので)gitは、対象機器からコンフィグを採取してファイルにした時、社内サーバに立てたGitLabに簡単に保管できるようにするため入れてます。
Dockerfile
FROM ubuntu:latest MAINTAINER your_name # set -xを最初に書く事でコメントをshellに表示できるのでDockerのビルドプロセスを確認しやすい RUN set -x && \ apt-get update && \ : "python3, pip, ubuntuの日本語化パッケージ, gitをインストールする" && \ apt-get install -y \ language-pack-ja-base language-pack-ja \ git \ python3-pip && \ locale-gen ja_JP.UTF-8 && \ : "ansibleとjupyter notebookをインストールする" && \ python3 -m pip install --upgrade pip && \ python3 -m pip install ansible && \ python3 -m pip install jupyter # 日本語の扱いを可能にするための環境変数 ENV LANG ja_JP.UTF-8------後記------
データセンターに設置してある機器にAnsibleを使いたい、という時、データセンターの外から
SSHの許可がない場合はデータセンタへの持ち込み用PCにAnsible環境を入れて作業する事になるかと思います。そんな時、各PCごとに環境構築するのは非常に手間がかかるし、また色んな人が毎回微妙に異なる環境を構築すると管理も大変になってしまうので、Dockerを使って用意することにしました。
Vagrant or Docker で迷いましたが、Ansibleでの作業が終われば手軽に削除が可能なのと、素早く環境を立てられる理由からDockerを選定しました。
- 投稿日:2019-03-05T22:18:06+09:00
AnsibleをDockerで使うためのDockerfileの書き方とビルドについて
色々とハマったのでメモっておきます。
ファイル内のコメント(#)で説明を書いてます。
インストールしているもの:
python3, git, ansible, jupyter notebook, ubuntuの日本語化パッケージ(公式のubuntuイメージはそのままだと日本語ファイルが文字化けしたので)gitは、対象機器からコンフィグを採取してファイルにした時、社内サーバに立てたGitLabに簡単に保管できるようにするため入れてます。
Dockerfile
FROM ubuntu:latest MAINTAINER your_name # イメージ容量の肥大を抑えるため、RUNはなるべく一つにまとめる。 # set -xを最初に書く事でコメントをshellに表示できるのでDockerのビルドプロセスを確認しやすい RUN set -x && \ apt-get update && \ : "python3, pip, ubuntuの日本語化パッケージ, gitをインストールする" && \ apt-get install -y \ language-pack-ja-base language-pack-ja \ git \ python3-pip && \ locale-gen ja_JP.UTF-8 && \ : "ansibleとjupyter notebookをインストールする" && \ python3 -m pip install --upgrade pip && \ python3 -m pip install ansible && \ python3 -m pip install jupyter # 日本語の扱いを可能にするための環境変数 ENV LANG ja_JP.UTF-8これで確か、だいたい1GBくらいのイメージが出来上がった。
Buildコマンドについて
Proxy環境下では、Proxy認証のための値を渡してやらないとRUNで書いたものが実行できません。
そのため、--build-argオプションを使って対処します。また、今回はansible-basicというイメージ名にしました。
docker build --build-arg \ http_proxy=http://yourname@password:port \ --build-arg https_proxy=http://yourname@password:port -t ansible-basic .ちなみに
jupyter notebookをDockerから使いたい場合は、イメージをrunする時にポートの指定が必要です。
以下はその例。
docker run -p 8080:8080 -it --name test01 ansible-basic:latest /bin/bash
上記のコマンドでコンテナを作成した場合、またrootユーザーのままJupyter notebookを起動する場合はコマンドは以下のようになる。
jupyter notebook --port 8080 --ip=0.0.0.0 --allow-root
上記のコマンドでアドレスが出力されるので、ブラウザに打ち込んで以下のように書き変える。ex.)http://abcabcabchash:8080/hogehoge...
→http://ホストのIPaddress:8080/hogehoge...これで見れるはず。
------後記------
データセンターに設置してある機器にAnsibleを使いたい、という時、データセンターの外から
SSHの許可がない場合はデータセンタへの持ち込み用PCにAnsible環境を入れて作業する事になるかと思います。そんな時、各PCごとに環境構築するのは非常に手間がかかるし、また色んな人が毎回微妙に異なる環境を構築すると管理も大変になってしまうので、Dockerを使って用意することにしました。
Vagrant or Docker で迷いましたが、Ansibleでの作業が終われば手軽に削除が可能なのと、素早く環境を立てられる理由からDockerを選定しました。
- 投稿日:2019-03-05T19:47:00+09:00
Docker イメージから Singularity のイメージを作るときに、気をつけること
概要
(最新のSingularityでは、いけるのかもしれないが)
既存のdockerイメージから、Singularity Documentation - Sylabs.io のイメージを作るときに気をつけること
結論
docker save ではダメで、 docker export
以下のコマンドから作ってはダメなようで、
docker save YOURCONTAINER > with_save.tarきちんと export しないといけないようだ
docker create --name for_export YOURCONTAINER docker export for_export > for_export.tarDocker Private Registry をつかう
その他の方法としては、 Docker の private registry を使う方法があるようだ。
参考情報
本家がそろそろ移動するようです (2019-03-05)
Singularity
は、以下のサイトに移動するとのこと
Singularity Documentation - Sylabs.io本家での議論
why is a valid Docker image not good for Singularity? · Issue #429 · sylabs/singularity
docker save と docker export の違い
- 投稿日:2019-03-05T17:31:56+09:00
Docker for Windows でコンテナからlocalのドライブにアクセスできない時に試すこと
Docker は 全ての環境依存の問題を解決する!
.
.
.そう思っていた時期もほんのちょっぴりありました。
常用していれば、なんだかんだで、for Macならでは、そして多数の for Windowsならではの問題にぶちあたりますよね。
特にファイルシステムまわり。今回踏んだ地雷は、Docker for Windowsで どうしてもdocker-compose.ymlに書いたvolumesの通りにlocalのドライブがマウントされていない ように見えること。
実際、コンテナにdocker exec -it bashして確認しても、マウントされているはずの箇所は空です。
Dockerをリスタートしたり、Windowsをリスタートしたりと散々試したあげく辿り着いた答えが、なんであるのかよくわからない設定項目のアレ
もちろんちゃんと✓されている状態なのですが、
Shared Drivesの✓を一度外して Apply
もう一度✓をつけて Applyでした。
そんなんわかるかー!
というわけで参考まで。
- 投稿日:2019-03-05T05:04:44+09:00
Jupyter notebookをリモートのDocker(Ubuntu)をサーバーにしてローカルのブラウザで見る方法
要約
Jupyter notebookは便利です。Anacondaからインストールすると、ローカルのコンピュータをサーバーとして自作自演クライアントになる設定になっています。リモートをサーバーとしてupyter notebookを走らせることはできますが、セキュリティ面で心配です。そこで、sshで暗号化してhttpsでつながるようにすることにしました。jupyter notebookのコンフィギュレーションにちょっと鍵情報をたして、dockerにjupyter notebookを走らせるよう記述し新たなdockerをビルドします。
注意点
実際にはここに書いてある以外にクライアントのブラウザでhttpsを受けるために証明書が必要です。ここには書いてないです。
このバージョンは完全な記述ではないです。手順
joupyter notebookのconfigurationに鍵情報を書き込む。
jupyter notobookを常駐させたいdockerをリモートで走らせ、たうえで、
In [2]: from IPython.lib import passwd In [3]: passwd() Enter password:となるので、自分で決めたパスワードを入力する。
Verify password:で同じのを入力する。すると、
Out[3]: 'sha1:*****とshaのカギができるので、これを
editor ~/.jupyter/jupyter_notebook_config.pyとかやって、jupyter_notebook_config.pyを開き、編集する。上記ののカギを
c.NotebookApp.password = u’sha1***'というようにeditする。
jupyter_notebook_config.py
ではコメントアウトしてあるはずなので、コメントアウトを解除しておく。jupyterを起動するためのシェルスクリプト
jupyterを起動するためのシェルスクリプトも書いておきます。
jupyter_run.sh#!/bin/bash jupyter notebook --port=9999 --ip=0.0.0.0 --allow-root --no-browser & echo "Access from a local browser using URL: " echo "https://123.45.67.890:9999/"dockerをコミットする。
このように設定したdockerを
docker commit e839bcb92clc6d user/mydocker:latestとして、コミットします。
Dockerfileとしては、
FROM user/mydocker:latest MAINTAINER rk RUN pip install scikit-learn RUN mkdir /workspace/bin ENV PATH $PATH:/worksapce/bin RUN echo $PATH ADD jupyter_run.sh /workspace/bin CMD /bin/bash /workspace/bin/jupyter_run.shというようにしておく。これを使ってdockerをビルドすればとりあえず完成です。
- 投稿日:2019-03-05T00:29:16+09:00
Goを読んでDockerの抽象構文木の構造をサクッと理解する
こんにちはpo3rinです。Dockerfile の抽象構文木(以降 AST と呼ぶ)ってどうなっているんだろうと思い調べてみました。
Dockerfile の AST を所得する
下記の Dockerfile の AST をみてみます。
FROM golang:latest WORKDIR /go ADD . /go CMD ["go", "run", "main.go"]mody の buildkit が Dockerfile の Parser を提供しているのでそれを使います。
package main import ( "fmt" "log" "os" "github.com/moby/buildkit/frontend/dockerfile/parser" ) func main() { f, _ := os.Open("./Dockerfile") r, _ := parser.Parse(f) ast := r.AST.Dump() fmt.Printf("%+v", ast) }これで文字列化された Dockerfile の AST の Dump がみれます。
$ go run main.go (from "golang:latest") (workdir "/go") (add "." "/go") (cmd "go" "run" "main.go")かなり、簡略化して表示されるので、AST の構造までは覗けません。そこはコードを読んでいく必要がありそです。
DockerfileのASTの構造を知る
github.com/moby/buildkit/frontend/dockerfile/parser を読むと下記の構造体があります。
// Node is a structure used to represent a parse tree. type Node struct { Value string // actual content Next *Node // the next item in the current sexp Children []*Node // the children of this sexp Attributes map[string]bool // special attributes for this node Original string // original line used before parsing Flags []string // only top Node should have this set StartLine int // the line in the original dockerfile where the node begins endLine int // the line in the original dockerfile where the node ends }Node は解析木を表すために使用される構文コードです。基本的に Value、 Next、 Children の3つのフィールドを使います。Value は現在のトークンの文字列値です。 Next は次のトークンで、Children はすべての子Nodeのスライスになっています。
他の言語のASTをみたことある人はかなりシンプルな構造だと感じると思いますが、実は公式も「素直に言ってかなりお粗末」だと言っています。しかし、Dockerfile はプログラミング言語よりもシンプルなので、Node もシンプルであることはむしろ効果的だと言っています。
This data structure is frankly pretty lousy for handling complex languages, but lucky for us the Dockerfile isn't very complicated. This structure. works a little more effectively than a "proper" parse tree for our needs.
さてNodeが実際にどのように構成されているのか見てみましょう。ネストした構造体を覗くときは
github.com/kr/pretty
が便利なのでこれを使いましょう。まずは大枠を掴みます。package main import ( "os" "github.com/kr/pretty" "github.com/moby/buildkit/frontend/dockerfile/parser" ) func main() { f, _ := os.Open("./Dockerfile") r, _ := parser.Parse(f) pretty.Print(r.AST) }出力はこうなります。
&parser.Node{ Value: "", Next: (*parser.Node)(nil), Children: { &parser.Node{ Value: "from", Next: &parser.Node{ Value: "golang:latest", Next: (*parser.Node)(nil), Children: nil, Attributes: {}, Original: "", Flags: nil, StartLine: 0, endLine: 0, }, Children: nil, Attributes: {}, Original: "FROM golang:latest", Flags: {}, StartLine: 1, endLine: 1, }, &parser.Node{ Value: "workdir", Next: &parser.Node{ Value: "/go", Next: (*parser.Node)(nil), Children: nil, Attributes: {}, Original: "", Flags: nil, StartLine: 0, endLine: 0, }, Children: nil, Attributes: {}, Original: "WORKDIR /go", Flags: {}, StartLine: 3, endLine: 3, }, &parser.Node{ Value: "add", Next: &parser.Node{ Value: ".", Next: &parser.Node{ Value: "/go", Next: (*parser.Node)(nil), Children: nil, Attributes: {}, Original: "", Flags: nil, StartLine: 0, endLine: 0, }, Children: nil, Attributes: {}, Original: "", Flags: nil, StartLine: 0, endLine: 0, }, Children: nil, Attributes: {}, Original: "ADD . /go", Flags: {}, StartLine: 4, endLine: 4, }, &parser.Node{ Value: "cmd", Next: &parser.Node{ Value: "go", Next: &parser.Node{ Value: "run", Next: &parser.Node{ Value: "main.go", Next: (*parser.Node)(nil), Children: nil, Attributes: {}, Original: "", Flags: nil, StartLine: 0, endLine: 0, }, Children: nil, Attributes: {}, Original: "", Flags: nil, StartLine: 0, endLine: 0, }, Children: nil, Attributes: {}, Original: "", Flags: nil, StartLine: 0, endLine: 0, }, Children: nil, Attributes: {"json":true}, Original: "CMD [\"go\", \"run\", \"main.go\"]", Flags: {}, StartLine: 6, endLine: 6, }, }, Attributes: {}, Original: "", Flags: nil, StartLine: 1, endLine: 6, }全体像は下記のようになります。
1行の中でもtokenごとにNodeに分けられます。一番上のNodeがルートNodeと呼ばれます。ルートNode自身はValueを持たずChildren Nodeの一覧を保持します。こう見るとルートNodeのChildrenの数はイメージのレイヤ数と基本的に一致します。Next Nodeは同じ行の中の次のtokenのNodeです。Flagsは
--from=builder
などのDockerfile上で使われるFlagか格納されます。StartLineとendlineは文字通り、そのノードのDockerfileにおける行数です。Originalは解析前に使用された元の行を格納しています。PrintWarnings を使って Dockerfile に対する Warning を見る
Dockerfile をビルドする際に稀に出る Warning は Dockerfile の parse 時に検知しています。mody は下記のようなparseした後のASTに対してWarningをだすメソッドもあります。
func (r *Result) PrintWarnings(out io.Writer)このような空白行があると記載がDockerfileにあると
RUN echo "Hello" && \ # (空行) echo "Docker AST"このようなDockerfileに対して PrintWarnings メソッドを使うと下記のような Warning を吐きます。
[WARNING]: Empty continuation line found in: RUN echo "Hello" && echo "Docker AST" [WARNING]: Empty continuation lines will become errors in a future release.おまけ : ASTを使ったDockerfileのLint
Dockerfile の AST が手に入ったので簡単なlintツールも作れそうです。Dockerfile におけるベストプラクティスの一つはレイヤの数を最小にすることです。つまり、二回連続でRUNを使っている Dockerfile は一つのコマンドに統合すべきです。下のコードは RUN を二連続で呼び出している部分を検知します。
package main import ( "log" "os" "github.com/kr/pretty" "github.com/moby/buildkit/frontend/dockerfile/parser" ) func main() { f, _ := os.Open("./Dockerfile") r, _ := parser.Parse(f) pretty.Print(r.AST) var valueToken string for _, child := range r.AST.Children { if valueToken == child.Value { log.Fatal("RUN is used in two consecutive layers") } valueToken = child.Value } }こんな Dockerfile は
FROM golang:latest WORKDIR /go ADD . /go RUN echo "Hello" RUN echo "Docker AST" CMD ["go", "run", "main.go"]こういう風に検知できますね。
$ go run main.go 2019/03/04 23:36:10 RUN is used in two consecutive layersDockerfile の AST はシンプルゆえ複雑なことはできませんが、これくらいの検知なら十分です。
終わりに
簡単に Dockerfile の AST を追ってみました。今後このASTを使ってどのようにビルドしているのか追ってみて、暇ならまた記事にします。
- 投稿日:2019-03-05T00:11:52+09:00
WSLでも通常のLinuxでもdockerにマウントできるカレントパスを取得する
概要
WSL上でdockerを使用する場合、docker for windowsに接続して使用する事が一般的かと思います。
ここで、一番厄介なのがボリュームのパスです。
docker for windowsはwindowsの世界で動いているので、マウントするボリュームパスとしてはwindowsのパスを指定する必要が有ります。
WSL上のshellから「カレントディレクトリの内容をdockerに食わせたい」と考えた場合、パスの形式が違うので一度変換する必要が有ります。
スクリプト等でこれをやろうとする、WSLと普通のlinuxで書き方が変わってしまってややこしいので変換関数を作りました。変換補助関数
以下が今回作成した補助関数です。
これを使うと、WSLの場合は、現在のパスがwindowsのパスとして取得されます。function getPWD(){ local t_PWD=`pwd` if [ -f /proc/sys/fs/binfmt_misc/WSLInterop ]; then echo `wslpath -w $t_PWD`; else echo $t_PWD fi }使用イメージ
カレントディレクトリをlocalにマウントする
pwd=`getPWD` docker run --rm -v ${pwd}:/local ${DOCKER_IMAGE}