20190305のdockerに関する記事は8件です。

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を選定しました。

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

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を選定しました。

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

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を選定しました。

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

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

Docker 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 の違い

Dockerでsaveとexportを使用した環境のバックアップと再作成 - YoshinoriN's Memento

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

Docker for Windows でコンテナからlocalのドライブにアクセスできない時に試すこと

Docker は 全ての環境依存の問題を解決する!

.
.
.

そう思っていた時期もほんのちょっぴりありました。
常用していれば、なんだかんだで、for Macならでは、そして多数の for Windowsならではの問題にぶちあたりますよね。
特にファイルシステムまわり。

今回踏んだ地雷は、Docker for Windowsで どうしてもdocker-compose.ymlに書いたvolumesの通りにlocalのドライブがマウントされていない ように見えること。

実際、コンテナにdocker exec -it bashして確認しても、マウントされているはずの箇所は空です。

Dockerをリスタートしたり、Windowsをリスタートしたりと散々試したあげく辿り着いた答えが、なんであるのかよくわからない設定項目のアレ

docker.resized.png

もちろんちゃんとされている状態なのですが、

Shared Drivesのを一度外して Apply
もう一度をつけて Apply

でした。

そんなんわかるかー!

というわけで参考まで。

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

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をビルドすればとりあえず完成です。

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

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,
}

全体像は下記のようになります。

node.png

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 layers

Dockerfile の AST はシンプルゆえ複雑なことはできませんが、これくらいの検知なら十分です。

終わりに

簡単に Dockerfile の AST を追ってみました。今後このASTを使ってどのようにビルドしているのか追ってみて、暇ならまた記事にします。

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

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