20190126のdockerに関する記事は10件です。

Dockerのあれこれを断捨離する

はじめに

これも『Docker/Kubernetes 実践コンテナ開発入門』読書会で出てきたのですが、「溜まっていく不要なコンテナやイメージを全消しするコマンドってないのかな」という疑問。それに答えられるコマンドがあったのでそれに関してまとめてみます。

pruneコマンド

以下はPrune unused Docker objectsに書かれている内容ほぼそのままですが、多少例を交えて書いてみます。

様々なDockerオブジェクトをprune(枝刈り)するということですが、 そもそも日本人はpruneという言葉にあまり馴染みがない人が多いように思います。私もその一人ですが、唯一知っている例として、

$ find . -name .git -prune -o -print

というようなのがあります。findコマンドでたまに使われる用法ですが、「その前の条件に当てはまるものは検索対象から除外」というオプションですね。上記の例だと「名前が.gitであるものは検索対象から外し、そうでなければ(-o)プリントアウトする(-print)」ということで、つまりは今のディレクトリ以下の.gitディレクトリを除いた全てのファイルを表示するコマンドになります。

やや脱線しましたが、Dockerではいくつかのオブジェクトに対してprune(枝刈り)する機能が提供されています。

イメージの断捨離

まずはイメージから。Dockerで以下のようにタグ付けしてビルドすることはよくあると思います。

$ docker build . -t myimage:latest

ただこれを繰り返すと前のビルドで作られたイメージが名無しで積まれていくことになります。docker-composeを使ってビルドした場合も、明示的にタグ付けしてなくてもイメージに自動でタグが付くので同様です。

$ docker image ls
REPOSITORY          TAG                 IMAGE ID            CREATED              SIZE
myimage             latest              0663708e2fe9        8 seconds ago        101MB
<none>              <none>              0ef42d3b2d90        48 seconds ago       85.6MB
<none>              <none>              6de6344e5c7e        About a minute ago   59.3MB
<none>              <none>              f6852a322079        5 minutes ago        20.7MB

そんな時に、docker image pruneを叩いてみると、「danglingイメージを全部消しちゃうけど良い?」という確認がでて、それにyと答えると、最新のビルドだけ残してキレイに消してくれる!

$ docker image prune
WARNING! This will remove all dangling images.
Are you sure you want to continue? [y/N] y
Deleted Images:
deleted: sha256:6de6344e5c7e0174e61e60850fd2b8141d45c25130a1d4721277bf50d587665b
deleted: sha256:2cd97da5bb5dffe5636663e29d8cc1a0ce466cffc0a7ccc711874e279b81a7b2
deleted: sha256:f6852a32207912f722f8f48f7a8f2e7ff19af7e0ec898b1568b4448eb88083b9
deleted: sha256:5fd80e022f1ae979431fc5ffc5c79fcd4ba17cf3599b438c9945e61aa3262f5f
deleted: sha256:0ef42d3b2d90f849ea528387cc431e102b8f4a359da4d01e1bb2f4fe376e8226
deleted: sha256:fd285edd6b4ad162dce41bd15d0127c0cf52dada43af4631776b7e515cabde96

Total reclaimed space: 152.3MB
$ docker image ls
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
myimage             latest              0663708e2fe9        41 minutes ago      101MB

"dangling"というのは「ぶらぶらしている、宙ぶらりんな」という意味ですが、ここでは

  • タグ付けされていない、かつ
  • 現存しているコンテナから参照されていない(動いているか止まっているかは不問)

という条件を満たすもので、要するに消しちゃっても影響なさそうなイメージを自動的に消してくれるというスグレモノです。ちなみに、どのイメージが消されちゃうかを事前に知りたければ以下のコマンドで確認できます。

$ docker image ls --filter dangling=true

そして、docker image pruneにはオプションがいくつかあります。

-a -all

タグが付いていてもコンテナから使われていないイメージは全て消去します。なので、可動しているコンテナがない状態だと全てのイメージが消えちゃいます。

-f --force

実行時に「消しちゃうけど大丈夫?」という確認をスキップするためのオプションです。

--filter

消去するイメージを選択する追加の条件を指定できます。

指定には2つあります。一つはuntilで、絶対時間で「この時刻よりも前のもの」あるいは相対時間で「何秒・何分・何時間前のもの」という指定ができます。例えば以下のような形になります。

$ docker image prune --filter until=2019-01-25 # 2019年1月25日以前に作成されたイメージを消す
$ docker image prune --filter until=10m # 10分以上前に作成されたイメージを消す

もう一つはラベルを指定するやり方で、各々のイメージを作成時にDockerfileの中でLABELコマンドで付加したラベルを検索条件とします。例えば、

$ docker image prune --filter label=maintainer # "maintainer"ラベルが設定されているイメージを消去
$ docker image prune --filter label=maintainer=maint@example.com # "maintainer"ラベルがmaint@example.comに設定されているイメージを消去

更に、ラベル指定の場合はlabelの後ろに!をつけることにより、「条件に当てはまらないイメージを消去」という指定もできます。

$ docker image prune --filter label!=maintainer # "maintainer"ラベルが設定されていないイメージを消去
$ docker image prune --filter label!=maintainer=maint@example.com # "maintainer"ラベルがmaint@example.comに設定されていないイメージを消去

なおこのfilterオプションでは、どのファイルが消去の対象になるのかの事前確認ができず、ちょっと困っています。labeldocker image ls --filterでチェックできるのですが、label!だとかuntilは確認する方法を見つけられていません。実際に消去の実行をしない--dry-runみたいなオプションがあれば良いのになと思ったりします。

コンテナの断捨離

次にコンテナの整理について。コンテナも気がつくと不要なものが溜まっていたりします。特に、docker container lsとしての何も出てこないので安心していると、実行済みのコンテナがたんまり残っていたりします。確認するには -aオプションを使います。

$ docker container ls
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                      PORTS               NAMES
$ docker container ls -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                      PORTS               NAMES
5802c2477245        alpine              "ls"                11 seconds ago      Exited (0) 10 seconds ago                       awesome_hellman
247e211d6b59        alpine              "ls"                45 seconds ago      Exited (0) 44 seconds ago                       blissful_mclean
daa39f115422        alpine              "ls"                47 seconds ago      Exited (0) 46 seconds ago                       cranky_lamport

実行の終わったコンテナでもdocker restartで再度起動できるためデフォルトでは自動で消えてくれないです。もし停止と同時にコンテナを消去したければ --rmオプションを付けてdocker runすれば良いですが、それでもビルドに失敗したときなどにコンテナが残るため自然と溜まっていってしまいます。

そんな時にdocker container pruneを実行すると、imageの時と同様に本当に消すかどうかを聞かれ、yと答えると綺麗サッパリ消してくれます。対象は「停止中」のコンテナ。

$ docker container prune
WARNING! This will remove all stopped containers.
Are you sure you want to continue? [y/N] y
Deleted Containers:
be1381d7199a6dcf1083c4ef9792ae78e04044743a6c6e1ce3cd517dc22479ab
b9733c0160d16858ba2ef5ee877a23c3249e5313787debdb2c51e5863fdb2d31
f71351f2a878ab343c594675bb4cdff9966220a6d69fbb4d65e22205246525f1
4938b15ff8da0909eef220ce5254647e829fb6257a8ea8cbc9cd8361125f419b
5802c2477245b4f40fa6321a8780fbaac2d194e732b0813f58e305a2a9b444d2
247e211d6b59ce9bc1dbbd0c132d5877e81340c1b9e65d2eff1cf768bd03ebe7
daa39f1154227503b8c0da00153113d25c6305975b8801fbfa5aa317059398e7

Total reclaimed space: 3.831MB

そして、このコマンドにもオプションがいくつかあります。

-f --force

これはdocker image pruneと同様で、実行時に「消しちゃうけど大丈夫?」という確認をスキップするためのオプションです。

--filter

消去するコンテナを選択する追加の条件を指定できます。指定方法もimageと同じでuntillabel。ラベルは基本は元のイメージのものを引き継ぎますが、docker runで実行する時に上書きもできるのでその点は要注意かもしれません。

ボリュームの断捨離

ボリュームも気をつけていないと溜まっていってしまうものの一つです。docker volume createコマンドで明示的に作ったり、あるいは docker run -v <desitination_path>やDockerfile内のVOLUMEコマンドで無名のボリュームを作ったりしますが、それはコンテナを消去しても自動的には消えません (run --rmを使った場合に一部例外あり)。

docker volume lsをすると今現在システムで抱えているボリュームのリストが出てきます。

$ docker volume ls
DRIVER              VOLUME NAME
local               0f181d792f4313abb670110c0ee079910fa3ca776463b515093a4fea4219dcc9
local               1d383f8ab7ec848c9e8e7969969f88a92d46d20592e7f3aa6ddb590feab2fb55
local               2c3afd63e16ecbb33bc54e8e7545ee1be5d5568236203dd69c5eeb38e98f9c72
...

ここでdocker volume pruneをしてあげると、使われていない(現存するコンテナから参照されていない)ボリュームはサクッといなくなります。

$ docker volume prune
WARNING! This will remove all local volumes not used by at least one container.
Are you sure you want to continue? [y/N] y
Deleted Volumes:
...

Total reclaimed space: 1.528GB

私は1.5GBものスペースを無駄に保持していたようです (^^;

他のpruneコマンド同様に、docker volume prune-f/--force--filterオプションが使えます。使い方は一緒なので説明は割愛させていただきます。

ネットワークの断捨離

上記のイメージ、コンテナ、ボリュームに比べると溜まりにくいしそもそもデータ量もそれほどないので使う機会は少ないかも知れませんが、docker network createで明示的に作成したものは自動では消えないのでルーティングテーブルをシンプルに保ちたい方は定期的にdocker network pruneをやると良いかも知れません。

docker-compose upで複数コンテナを立ち上げたときには自動でデフォルトのbridgeネットワークが作られますが、docker-compose downするとそのネットワークは自動削除されるのでホントに出番が少なさそう(一方、自動で作られたボリュームは残り続けます…)。

基本は現存するコンテナから参照されていないネットワークが削除されますが、デフォルトで作成されている bridgehostnoneは消えずに残るようです。

まとめて断捨離

これまで、個別にpruneするコマンドを紹介してきましたが、実はまとめてキレイにしてくれるコマンドも存在します。それが、docker system prune。これをやると、

  • 停止中のコンテナ
  • どのコンテナからも使われていないネットワーク
  • 宙ぶらりんな(タグがなくどのコンテナからも参照されていない)イメージ
  • ビルドキャッシュ

を全て消してくれます。最後のはビルドの速度を早めるために持っている中間結果を破棄するもので、ビルド時に--no-cacheをつけるとそれを破棄した状態からビルドをはじめてくれますが、当然ビルドが終わった後にはキャッシュが残るわけで、キャッシュだけを削除するというコマンドは今まで無かったように思います。

さらに、--volumeオプションをつけて起動すると

  • どのコンテナからも使われていないボリューム

も合わせて消してくれます。また、これにも-f/--forceオプションがあるので「実行するか一々確認しなくても良いよ」という男気のある方は是非ともご活用ください(笑)。

なお、個別にpruneしていくのも良いのですが、コンテナ以外のイメージ、ボリューム、ネットワークはコンテナから参照されていることを消す条件にしているので、先に不要なコンテナが消えていないと消せません。そういった順序関係を気にしたくなかったらdocker system pruneで一気にやってしまうのも手かなと思います。ただし、必要なモノを消してしまう可能性もあるのであくまでも自己責任でお願いします。

まとめ

pruneコマンドに関してまとめてみました。書き始めたときにはすごく短いエントリになるかと思っていたら意外と長くなりました(^^;
ただ、書きながらボリュームやネットワークに関して自分はあまりわかっていなかったと再認識し、そういう意味でも書いた意味があったかなと思います。

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

コンテナイメージの実体を見てみる

これは何?

コンテナイメージの実体を見てみる話です。
概念と使い方はわかるけど、「コンテナイメージっていったい何者なんだろう」と思っていたので中を見てみました。
認識違いがあったら教えて頂けると非常に嬉しいです。

環境とか

  • Docker version 18.09.1, build 4c52b90

題材として適当なコンテナイメージを用意する

$ docker pull node/8.15.0-alpine
$ docker images
REPOSITORY               TAG                 IMAGE ID            CREATED             SIZE
node                     8.15.0-alpine       5c0c5c94503f        4 weeks ago         66.3MB

コンテナイメージをtarにする

$ docker save node:8.15.0-alpine > node.tar
$ ls -ltr
-rwxrwxrwx 1 xxx xxx 69734400 Jan 26 14:37 node.tar

tarを解凍してみる

$ tar -xvf node.tar
5491dce778832e33c284cd8185100e76d6daa18f8cbc32458c706776894127fc/
5491dce778832e33c284cd8185100e76d6daa18f8cbc32458c706776894127fc/VERSION
5491dce778832e33c284cd8185100e76d6daa18f8cbc32458c706776894127fc/json
5491dce778832e33c284cd8185100e76d6daa18f8cbc32458c706776894127fc/layer.tar
5c0c5c94503f7310f50e73e8bcaa584eab5e5256ac34b9745589b64bb8fa09bb.json
84f3cb797bec406c8818fd5b1deaad92efd6776bb451c974175373102cfe72ac/
84f3cb797bec406c8818fd5b1deaad92efd6776bb451c974175373102cfe72ac/VERSION
84f3cb797bec406c8818fd5b1deaad92efd6776bb451c974175373102cfe72ac/json
84f3cb797bec406c8818fd5b1deaad92efd6776bb451c974175373102cfe72ac/layer.tar
eaabbaf2e7fe2603c758103f3f9397a82ea9c1f39e421735753afef370323a1f/
eaabbaf2e7fe2603c758103f3f9397a82ea9c1f39e421735753afef370323a1f/VERSION
eaabbaf2e7fe2603c758103f3f9397a82ea9c1f39e421735753afef370323a1f/json
eaabbaf2e7fe2603c758103f3f9397a82ea9c1f39e421735753afef370323a1f/layer.tar
manifest.json
repositories

これがコンテナイメージの実体のようです。
上でdocker imagesした時に表示されたイメージIDと同じものがありますね。
種類を分けるとこんな感じでしょうか。

  • buildされたコンテナイメージの情報
    • 5c0c5c94503f7310f50e73e8bcaa584eab5e5256ac34b9745589b64bb8fa09bb.json
  • レイヤーの情報
    • 5491dce778832e33c284cd8185100e76d6daa18f8cbc32458c706776894127fc
    • 84f3cb797bec406c8818fd5b1deaad92efd6776bb451c974175373102cfe72ac
    • eaabbaf2e7fe2603c758103f3f9397a82ea9c1f39e421735753afef370323a1f
  • メタ情報
    • manifest.json
    • repositories

buildされたコンテナイメージの情報

めちゃくちゃ長いです。たくさん情報持ってるんですね。
レイヤー名とかも確認できますね。

5c0c5c94503f7310f50e73e8bcaa584eab5e5256ac34b9745589b64bb8fa09bb.json
{
    "architecture":"amd64",
    "config":{
        "Hostname":"",
        "Domainname":"",
        "User":"",
        "AttachStdin":false,
        "AttachStdout":false,
        "AttachStderr":false,
        "Tty":false,
        "OpenStdin":false,
        "StdinOnce":false,
        "Env":[
            "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
            "NODE_VERSION=8.15.0",
            "YARN_VERSION=1.12.3"
        ],
        "Cmd":["node"],
        "ArgsEscaped":true,
        "Image":"sha256:e615719cc465120b3f6a8a76ad4a39464a3d65be67f99c0bd90b4a847a00d651",
        "Volumes":null,
        "WorkingDir":"",
        "Entrypoint":null,
        "OnBuild":null,
        "Labels":null
    },
    "container":"d29ae8e85f8f1b00dbc79c2160e202969205c1429dcc31553b18585f27248ff7",
    "container_config":{
        "Hostname":"d29ae8e85f8f",
        "Domainname":"",
        "User":"",
        "AttachStdin":false,
        "AttachStdout":false,
        "AttachStderr":false,
        "Tty":false,
        "OpenStdin":false,
        "StdinOnce":false,
        "Env":[
            "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
            "NODE_VERSION=8.15.0",
            "YARN_VERSION=1.12.3"
        ],
        "Cmd":[
            "/bin/sh",
            "-c",
            "#(nop) ",
            "CMD [\"node\"]"
        ],
        "ArgsEscaped":true,
        "Image":"sha256:e615719cc465120b3f6a8a76ad4a39464a3d65be67f99c0bd90b4a847a00d651",
        "Volumes":null,
        "WorkingDir":"",
        "Entrypoint":null,
        "OnBuild":null,
        "Labels":{}
    },
    "created":"2018-12-27T01:40:51.334534029Z",
    "docker_version":"18.06.1-ce",
    "history":[
        {
            "created":"2018-12-21T00:21:29.97055571Z",
            "created_by":"/bin/sh -c #(nop) ADD file:2ff00caea4e83dfade726ca47e3c795a1e9acb8ac24e392785c474ecf9a621f2 in / "
        },
        {
            "created":"2018-12-21T00:21:30.122610396Z",
            "created_by":"/bin/sh -c #(nop)  CMD [\"/bin/sh\"]",
            "empty_layer":true
        },
        {
            "created":"2018-12-27T01:22:27.078806811Z",
            "created_by":"/bin/sh -c #(nop)  ENV NODE_VERSION=8.15.0",
            "empty_layer":true
        },
        {
            "created":"2018-12-27T01:40:46.64036283Z",
            "created_by":"/bin/sh -c addgroup -g 1000 node     \u0026\u0026 adduser -u 1000 -G node -s /bin/sh -D node     \u0026\u0026 apk add --no-cache         libstdc++     \u0026\u0026 apk add --no-cache --virtual .build-deps         binutils-gold         curl         g++
     gcc         gnupg         libgcc         linux-headers         make         python   \u0026\u0026 for key in     94AE36675C464D64BAFA68DD7434390BDBE9B9C5     FD3A5288F042B6850C66B31F09FE44734EB7990E     71DCFD284A79C3B38668286BC97EC7A07EDE3FC1     DD8F2338BAE7501E3DD5AC78C273792F7D83545D     C4F0DFFF4E8C1A8236409D08E73BC641CC11F4C8     B9AE9905FFD7803F25714661B63B535A4C206CA9     77984A986EBC2AA786BC0F66B01FBB92821C587A     8FCCA13FEF1D0C2E91008E09770F7A9A5AE15600     4ED778F539E3634C779C87C6D7062848A1AB005C     A48C2BEE680E841632CD4E44F07496B3EB3C1762     B9E2F5981AA6E0CD28160D9FF13993A75599653C   ; do     gpg --batch --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys \"$key\" ||     gpg --batch --keyserver hkp://ipv4.pool.sks-keyservers.net --recv-keys \"$key\" ||     gpg --batch --keyserver hkp://pgp.mit.edu:80 --recv-keys \"$key\" ;   done     \u0026\u0026 curl -fsSLO --compressed \"https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION.tar.xz\"     \u0026\u0026 curl -fsSLO --compressed \"https://nodejs.org/dist/v$NODE_VERSION/SHASUMS256.txt.asc\"     \u0026\u0026 gpg --batch --decrypt --output SHASUMS256.txt SHASUMS256.txt.asc     \u0026\u0026 grep \" node-v$NODE_VERSION.tar.xz\\$\" SHASUMS256.txt | sha256sum -c -     \u0026\u0026 tar -xf \"node-v$NODE_VERSION.tar.xz\"     \u0026\u0026 cd \"node-v$NODE_VERSION\"     \u0026\u0026 ./configure     \u0026\u0026 make -j$(getconf _NPROCESSORS_ONLN)     \u0026\u0026 make install     \u0026\u0026 apk del .build-deps     \u0026\u0026 cd ..     \u0026\u0026 rm -Rf \"node-v$NODE_VERSION\"     \u0026\u0026 rm \"node-v$NODE_VERSION.tar.xz\" SHASUMS256.txt.asc SHASUMS256.txt"
        },
        {
            "created":"2018-12-27T01:40:46.898832094Z",
            "created_by":"/bin/sh -c #(nop)  ENV YARN_VERSION=1.12.3",
            "empty_layer":true
        },
        {
            "created":"2018-12-27T01:40:51.183517333Z",
            "created_by":"/bin/sh -c apk add --no-cache --virtual .build-deps-yarn curl gnupg tar   \u0026\u0026 for key in     6A010C5166006599AA17F08146C2130DFD2497F5   ; do     gpg --batch --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys \"$key\" ||     gpg --batch --keyserver hkp://ipv4.pool.sks-keyservers.net --recv-keys \"$key\" ||     gpg --batch --keyserver hkp://pgp.mit.edu:80 --recv-keys \"$key\" ;   done   \u0026\u0026 curl -fsSLO --compressed \"https://yarnpkg.com/downloads/$YARN_VERSION/yarn-v$YARN_VERSION.tar.gz\"   \u0026\u0026 curl -fsSLO --compressed \"https://yarnpkg.com/downloads/$YARN_VERSION/yarn-v$YARN_VERSION.tar.gz.asc\"   \u0026\u0026 gpg --batch --verify yarn-v$YARN_VERSION.tar.gz.asc yarn-v$YARN_VERSION.tar.gz   \u0026\u0026 mkdir -p /opt   \u0026\u0026 tar -xzf yarn-v$YARN_VERSION.tar.gz -C /opt/   \u0026\u0026 ln -s /opt/yarn-v$YARN_VERSION/bin/yarn /usr/local/bin/yarn
 \u0026\u0026 ln -s /opt/yarn-v$YARN_VERSION/bin/yarnpkg /usr/local/bin/yarnpkg   \u0026\u0026 rm yarn-v$YARN_VERSION.tar.gz.asc yarn-v$YARN_VERSION.tar.gz   \u0026\u0026 apk del .build-deps-yarn"
        },
        {
            "created":"2018-12-27T01:40:51.334534029Z",
            "created_by":"/bin/sh -c #(nop)  CMD [\"node\"]",  
            "empty_layer":true
        }
    ],
    "os":"linux",
    "rootfs":{
        "type":"layers",
        "diff_ids":[
            "sha256:7bff100f35cb359a368537bb07829b055fe8e0b1cb01085a3a628ae9c187c7b8",
            "sha256:734b6a5256135b58db8ecef74e164d2682722f2b208467bf484392ee87509645",
            "sha256:1f3213370fe13af42ae5e55610b7e64d7c995edf4f95ebdfa4671456812c38dd"
        ]
    }
}

レイヤーの情報

レイヤーと思われるtarの中身を見てみます。

  • 5491dce778832e33c284cd8185100e76d6daa18f8cbc32458c706776894127fc/layer.tar
  • 84f3cb797bec406c8818fd5b1deaad92efd6776bb451c974175373102cfe72ac/layer.tar
  • eaabbaf2e7fe2603c758103f3f9397a82ea9c1f39e421735753afef370323a1f/layer.tar

5491dce778832e33c284cd8185100e76d6daa18f8cbc32458c706776894127fc/layer.tar

$ tar -xvf 5491dce778832e33c284cd8185100e76d6daa18f8cbc32458c706776894127fc/layer.tar
bin/
bin/arch
bin/ash
bin/base64
bin/bbconfig
~~~
etc/alpine-release
etc/apk/
etc/apk/arch
etc/apk/keys/
etc/apk/keys/alpine-devel@lists.alpinelinux.org-4a6a0840.rsa.pub
~~~
var/run
var/spool/
var/spool/cron/
var/spool/cron/crontabs
var/tmp/

果てしないものが展開されましたので中略しています。
途中でalpineという文言もチラホラ見えたので、これはOS?

eaabbaf2e7fe2603c758103f3f9397a82ea9c1f39e421735753afef370323a1f/layer.tar

$ tar -xvf eaabbaf2e7fe2603c758103f3f9397a82ea9c1f39e421735753afef370323a1f/layer.tar
etc/
etc/apk/
etc/apk/protected_paths.d/
etc/apk/world
etc/group
~~~
usr/local/lib/node_modules/npm/node_modules/stringify-package/index.js
usr/local/lib/node_modules/npm/node_modules/stringify-package/package.json
usr/local/lib/node_modules/npm/node_modules/strip-ansi/
usr/local/lib/node_modules/npm/node_modules/strip-ansi/index.js
usr/local/lib/node_modules/npm/node_modules/strip-ansi/license
~~~
usr/sbin/
usr/share/
var/
var/cache/
var/cache/misc/

これも果てしないので中略。
ただ、思いっきりnode_modulesを展開していたのでわかりやすかったです。nodeのレイヤーですね。

メタ情報

manifest.jsonrepositoriesには、メタ情報が入ってるようです。

manifest.json
[
    {
        "Config":"5c0c5c94503f7310f50e73e8bcaa584eab5e5256ac34b9745589b64bb8fa09bb.json",
        "RepoTags":["node:8.15.0-alpine"],
        "Layers":[
            "5491dce778832e33c284cd8185100e76d6daa18f8cbc32458c706776894127fc/layer.tar",
            "eaabbaf2e7fe2603c758103f3f9397a82ea9c1f39e421735753afef370323a1f/layer.tar",
            "84f3cb797bec406c8818fd5b1deaad92efd6776bb451c974175373102cfe72ac/layer.tar"
        ]
    }
]
repositories.
{
    "node":{
        "8.15.0-alpine":"84f3cb797bec406c8818fd5b1deaad92efd6776bb451c974175373102cfe72ac"
    }
}

実際のDockerfileとの対応

開発者の皆様がDockerfileを上げてくれているので、node:8.15.0-alpineのDockerfileを見てみます。

このDockerfileは以下の6つの命令から成ります。

  1. FROM
  2. ENV
  3. RUN
  4. ENV
  5. RUN
  6. CMD

Dockerfileをbuildする際にレイヤーを新たに作り出す命令というのはRUNADDCOPYの3つだけですから、tarを解凍して確認できた3つのレイヤーは

  • FROM(alpineのベースイメージのレイヤー)
  • RUN
  • RUN

と対応していると思われます。(たぶん)

まとめ

  • コンテナイメージをtarにすれば人間が中身を確認できる
  • 内容は大きく3種類
    • コンテナイメージ情報json
    • 各レイヤーのリソースや設定情報もろもろ
    • メタ情報json
  • レイヤーの中には、当たり前だが各プロダクトを構成するファイルがちゃんと詰め込まれている

感想

知らなくてもコンテナ使う上では困らないと思いますが、なんだかよくわからないまま使うのはイヤなので見てみました。
レイヤー情報などわかりやすく格納されていて、かなり納得できました。
同時に、あらためてコンテナおよびDockerの便利さを再確認できました。

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

RSelenium のすヽめ

RSelenium でスクレイピングをしようとした時に日本語の記事がほとんどなく、TOEIC200点の英語力に頼るのがしんどかったので設定からデータ取得の流れを置いときます。

以下のページをスクレイピングしてみます。
https://jglobal.jst.go.jp/search/anythings#%7B%22category%22%3A%220%22%2C%22keyword%22%3A%22%E3%82%AB%E3%83%96%E3%83%88%E3%83%A0%E3%82%B7%22%7D

Dockerの準備

docker のインストールは省略

docker Selenium のイメージを取得します。

docker pull selenium/standalone-chrome

Selenium コンテナを立てます。

docker run -d -p 4444:4444 selenium/standalone-chrome

以上でdocker の設定は終了です。

Rの準備

RSeleniumを準備します。

install.packages("RSelenium")
library(RSelenium)
remDr = remoteDriver(remoteServerAddr = "localhost", port = 4444, browserName = "chrome")
remDr$open()

上記により、seleniumコンテナとRの接続が完了しました。

スクレイピングしてみよう

url <- 'https://jglobal.jst.go.jp/search/anythings#%7B%22category%22%3A%220%22%2C%22keyword%22%3A%22%E3%82%AB%E3%83%96%E3%83%88%E3%83%A0%E3%82%B7%22%7D'
remDr$navigate(url)
Sys.sleep(5)
planeHtmlList <- remDr$getPageSource()
write(unlist(planeHtmlList), "getPage.html")

この時、「ページの読み込み」と「ページの取得」の間隔が狭いとページをうまく取得できないので注意してください。
また、この時、ページの型はlistであるので注意してください。

では、ここからタイトルをスクレイプしてみましょう。

#install.packages("rvest")
#install.packages("XML")
library(rvest)
library(XML)
html <- read_html("getPage.html")
parsed_doc <- htmlParse(html)
title <- xpathSApply(doc = parsed_doc , path = "//a[@href]", xmlValue)

できた。

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

MySQLの文字コードを変更する方法

ProgateでRuby on Railsレッスンで、tweetappというものを作成したので、そのコピーをDockerとGithubを使いながら、開発をしています。

ちなみにRails環境は以下のページを参考に構築しました。
https://qiita.com/reflet/items/f73cac406760ee4ecc13
感謝です(^^)

しかし、新規投稿機能のところで、日本語の文字列をDBに保存できない問題に直面。

エラー文は全文は控えてなかったのですが以下のような感じで、文字コードが原因っぽい。

Mysql2::Error: Incorrect string value ,,,,,,

いろいろネットで調べて以下の方法で解決したので、メモです。

MySQLのクライアントサーバーとMySQLサーバーの文字コードの確認・変更

#確認したいテーブルを選択
mysql> use DB名

#文字コードを確認
mysql> show variables like 'character%';
+--------------------------+----------------------------+
| Variable_name            | Value                      |
+--------------------------+----------------------------+
| character_set_client     | latin1                     |
| character_set_connection | latin1                     |
| character_set_database   | latin1                     |
| character_set_filesystem | binary                     |
| character_set_results    | latin1                     |
| character_set_server     | latin1                     |
| character_set_system     | latin1                     |
| character_sets_dir       | /usr/share/mysql/charsets/ |
+--------------------------+----------------------------+

#以下のコマンドでUTF-8に変更
mysql> set character_set_client = utf8
mysql> set character_set_connection = utf8
mysql> set character_set_database = utf8
mysql> set character_set_results = utf8

「character_set_server」「character_set_system」は「docker-compose.yml」のMySQLにcommandを以下のように追加したらUTF-8に変更できました。

command: mysqld --character-set-server=utf8 --collation-server=utf8_unicode_ci

既に作成したDBの文字コードの確認・変更

#確認
mysql> show create database DB名;
+----------+--------------------------------------------------------------------+
| Database | Create Database                                                    |
+----------+--------------------------------------------------------------------+
| example  | CREATE DATABASE `example` /*!40100 DEFAULT CHARACTER SET latin1 */ |
+----------+--------------------------------------------------------------------+

#変更
mysql> alter database DB名 character set utf8;

既に作成したMySQLのテーブルカラムの確認・変更

#確認
mysql> show create table テーブル名 \G;
*************************** 1. row ***************************
       Table: posts
Create Table: CREATE TABLE `posts` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `content` text CHARACTER SET latin1,
  `created_at` datetime NOT NULL,
  `updated_at` datetime NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8
1 row in set (0.00 sec)

#変更
mysql> alter table テーブル名 modify カラム名 varchar(255) character set utf8;

この時点で日本語に保存できました。

いやーハマっちゃいましたw
これから、カラムを追加したり、新しいテーブルを作成するので、どうなるのでしょうか。

とりあえず進めていきます。

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

PodmanのセキュリティはDockerよりも何が優れているのか?

はじめに

先日、オープンソースのコンテナエンジンPodmanのバージョン1.0.0が公開されました。
Podman | podman.io

PodmanはKubernetesのPodをよりシンプルに柔軟に利用できるように開発されたDocker互換のコンテナエンジンで、Docker Swarm関連を除いたほとんどのDockerコマンドを備えています。

Dockerと比べてアーキテクチャの違いがいくつかありますが、Podmanは特徴の一つとしてセキュリティの強さを謳っています。
この記事ではセキュリティの観点からPodmanの機能に関する調査結果をまとめていきます。

目次

rootlessコンテナ

rootlessとは非root権限で制御できることです。
非root権限で動かせるコンテナのため、問題が起きた際の影響範囲を限定することができます。

2019年1月現在のDocker 18.09ではrootlessに対応していませんが、rootlessモードの開発が進められています。
Allow running dockerd as a non-root user (Rootless mode) #38050

Docker 19.xxで当該機能が追加される......との噂です。

Linux Auditにおける脆弱性の回避

Linuxにはセキュリティイベントを監視する機能として、Linux Auditシステムが搭載されています。
Red Hat Enterprise Linux 7 第5章 システム監査 - Red Hat Customer Portal

その中で利用される一部のフィールド値の扱いが、PodmanとDockerでは異なっており、例えば、Podmanではホスト側から見たloginuidコンテナ側から見たloginuidは同一ですが、Dockerではホスト側から見たloginuidコンテナ側から見たloginuidは異なります。

これはDockerコンテナからのログインユーザーをLinux Auditが識別できない可能性を表しています。

edit_file_and_audit_log
$ sudo docker run --privileged -v /:/host fedora touch /host/etc/shadow
$ sudo ausearch -f /etc/shadow -i
type=PROCTITLE msg=audit(10/10/2018 10:27:20.055:4569) : proctitle=/usr/bin/coreutils
--coreutils-prog-shebang=touch /usr/bin/touch /host/etc/shadow
type=SYSCALL msg=audit(10/10/2018 10:27:20.055:4569) : arch=x86_64 syscall=openat
success=yes exit=3 a0=0xffffff9c a1=0x7ffdb6973f50 a2=O_WRONLY|O_CREAT|O_NOCTTY|
O_NONBLOCK a3=0x1b6 items=2 ppid=11863 pid=11882 auid=unset uid=root gid=root
euid=root suid=root fsuid=root egid=root sgid=root fsgid=root tty=(none) ses=unset
comm=touch exe=/usr/bin/coreutils subj=system_u:system_r:spc_t:s0 key=(null)


引用:
Podman: A more secure way to run containers | Opensource.com

上記コマンドでは、Dockerコンテナを起動してファイル編集を行ない、Linux Auditのログを出力していますが、auid=unsetとなっており、ユーザーの特定が不可能であることが分かります。

この原因はDockerがクライアント・サーバ型のアーキテクチャであることに依るものですが、下記ページが非常に分かりやすかったので詳しく知りたい方はご一読ください。
Intro to Podman (Red Hat Enterprise Linux 7.6 Beta) - RHD Blog

参考記事まとめ

Podman | podman.io
Podman: A more secure way to run containers | Opensource.com
Allow running dockerd as a non-root user (Rootless mode) #38050
Red Hat Enterprise Linux 7 第5章 システム監査 - Red Hat Customer Portal
Intro to Podman (Red Hat Enterprise Linux 7.6 Beta) - RHD Blog
Dockerセキュリティ: 今すぐ役に立つテクニックから,次世代技術まで

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

知識0から始めるRails on Docker

はじめに

Railsの勉強を始めようと思ったが、どうせならDockerを使って仮想環境で開発したい。
しかしどちらも実務で使ったことがないためよくわからない(というかRubyすら触ったことがない)。

ということで、一から調べてみた。RailsとDockerの欲張りセット。
筆者はMacBookProを使っているため、MacOS向けの記事となります。

tl;td

  • Rubyにまつわる各用語の確認、ローカル環境でRailsの導入・起動方法の確認
  • 手動でDocker上でのRailsの導入・起動方法の確認
  • dockerfileにRailsの導入・起動情報を文書化して格納し、docker-composeから呼び出して起動

対象読者

  • わたし
  • これからRailsを勉強しようという方
  • これからDockerを勉強しようという方

ロードマップ(超簡易版)

最終目標:Dockerによる仮想環境上でRails serverを起動し、開発できるようにする
※RailsのデフォルトサーバーはPumaです。この記事ではデフォルトのままなのでPumaを使いますが、便宜上「Rails server」と呼称します。

  • Railsをローカル環境で起動する
    • ローカル環境にRubyを導入する
    • ローカル環境にRailsを導入する
    • Railsプロジェクトを作成し、Rails serverを起動
  • Docker環境を構築して仮想環境でRails serverを起動する
    • ローカル環境にDockerを導入する
    • DockerでRubyを使える仮想環境を構築する
    • 仮想環境でRails serverを起動する
  • dockerfiledocker-composeを使ってコマンド一つでRails serverを起動する
    • dockerfileを作成する
    • docker-compose.ymlを作成し、起動する

Railsをローカル環境で起動する

これについては各所でがっつり解説されているので、ここでは流れ・用語の説明と参考資料の紹介にとどめます。
なお、この章の目的はRubyにまつわる各用語とRails serverの起動までの流れについて確認することが目的なので、既にバッチリの方は飛ばしてください。
参考資料:Ruby初学者のRuby On Rails 環境構築【Mac】

ローカル環境にRubyを導入する

流れ
Homebrewの導入(更新)-> rbenvの導入 -> Rubyの導入

ざっくりHomebrew解説

ざっくりrbenv解説

ローカル環境にRailsを導入する

流れ
Bundlerの導入 -> BundlerでGemfileの作成 -> Gemfileの編集 -> Gemの取得(Railsの導入)

ざっくりGem解説

ざっくりBundler解説

Railsプロジェクトを作成し、Rails serverを起動

流れ
Railsからプロジェクトを作成 -> Rails serverの起動 -> 接続確認

Docker環境を構築して仮想環境でRails serverを起動する

ローカル環境にDockerを導入する

Docker公式から最新版を取得しましょう。
アカウントの登録が必要です。

なお、Dockerの基礎的な仕組みやコマンドなどについては、以下の参考資料がおすすめです。

参考資料:Dockerでプログラマが最低限知るべきことが、最速でわかるチュートリアル
docker

参考資料:【図解】Dockerの全体像を理解する -前編-

ざっくりDocker解説

  • 仮想環境の構築・管理を行うツール
  • 擬似的に様々な環境を構築することができ、ローカル環境と本番環境との差異を減らすことができる
  • 「本番環境で急に動かなくなった問題」や「環境構築面倒すぎる問題」の救世主……らしい

DockerでRubyを使える仮想環境を構築する

参考資料:RailsアプリをDockerで開発するための手順

ここからが本題です。
まずは利用するイメージを決めます。Docker Hubにrubyという便利なイメージが用意されているので、ありがたく使わせていただきましょう。

rubyイメージの取得
$ docker pull ruby

イメージが取得できたら早速run……する前に、このイメージについて調べましょう。

イメージの情報を出力
$ docker inspect ruby

コマンドを実行すると大量の文字が吐き出されます。
目眩がするかもしれませんが、頑張って大事な記述を確認しましょう。注目するのは以下の場所です。

"ContainerConfig": {
……
  "Env": [
                "PATH=/usr/local/bundle/bin:/usr/local/bundle/gems/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
                "RUBY_MAJOR=2.6",
                "RUBY_VERSION=2.6.0",
                "RUBY_DOWNLOAD_SHA256=acb00f04374899ba8ee74bbbcb9b35c5c6b1fd229f1876554ee76f0f1710ff5f",
                "GEM_HOME=/usr/local/bundle",
                "BUNDLE_PATH=/usr/local/bundle",
                "BUNDLE_SILENCE_ROOT_WARNING=1",
                "BUNDLE_APP_CONFIG=/usr/local/bundle"
            ],
……
RUBY_VERSION=2.6.0

Rubyのバージョンを示しています。Docker Hubでも確認できますが、イメージ本体にも情報がしっかり記されていますね。
このバージョンは重要なので覚えておきましょう。あるいはイメージをpullする段階で指定してあげてもいいでしょう。

BUNDLE_PATH=/usr/local/bundle

Bundlerのパスが指定されています。BundlerによるGemfileを使ったGem管理を行えることがわかりますね。

ではこのイメージからコンテナを起動しましょう。
適当な名前のディレクトリを作成し、ターミナルの作業フォルダとします。
以降はプロジェクト名をproject_nameとします。
project_name以外の名前でも構いません。本記事ではこの名前で統一するということです。

rubyイメージからコンテナを起動
$ docker run -i -t --name TEST -p 3000:3000 -v "$PWD":/usr/src/project_name ruby /bin/bash

まずは各オプションについて。

-i       :コンテナのSTDIN(標準入力)にアタッチ。標準入力に入力できる状態にするということ。
-t       :疑似ターミナル (pseudo-TTY) を割り当て。ターミナル画面で操作できるようにするということ。
--name   :コンテナの名前。今回は'TEST'を指定。
-p       :ポートの指定。今回は3000ポートを解放しています。
-v       :ボリュームの指定。[ホストPCのディレクトリ指定]:[コンテナ内のディレクトリ指定]。
/bin/bash:コンテナ起動後に実行するコマンド。今回はシェルを指定。

上記の説明は簡略したもので語弊を含むので、気になった項目は別途調べることをお勧めします。
-i-tの二つはセットで使う(-it)ことが多いので是非覚えておきましょう。
ボリュームについては概念が難しいですが、とりあえず[ホストPCで指定したディレクトリ]を[コンテナ内で指定したディレクトリ]として扱えるようにする、程度の理解でいいと思います。

参考資料:Dockerリファレンス

うまく起動できたら下のような状態になるはずです。

root@03eb04059b1f:/#

コンテナの中のターミナルを操作しているようなイメージですね。
コンテナの中から一旦抜ける時はcontrol + pq、再接続するときは$ docker attach [コンテナの指定]です。
exitでも抜けられますが、コンテナが停止してしまうため注意。停止した場合、ポートを閉じてしまうようです。
$ docker execでもコンテナに接続できます。ただし、プロセス接続するごとにプロセスが増えます。

仮想環境でRails serverを起動する

それでは今起動したコンテナでRails serverの準備をしましょう。
先ほどコンテナを起動した際に、コンテナ内のproject_nameディレクトリをボリュームとして指定しました。
指定したディレクトリが作成されているはずなので、そこまで移動しましょう。

Railsプロジェクトの確認
$ cd /usr/src/project_name

このディレクトリにRailsプロジェクトを作成していきます。
まずは何をするにもGemfileが必要なので、Bundlerを使って作成しましょう。

Gemfileの作成
$ bundle init

次にRailsを導入していきます。
Gemfileを編集する必要がありますが、これはコンテナ内で行う必要はありません。Gemifileを作成したディレクトリはコンテナ起動時にホストPC側のディレクトリとセットでボリューム指定したため、ホストPC側の指定したディレクトリにもGemfileが作成されています。このGemfileを編集するだけでOKです。

Gemfile
# frozen_string_literal: true

source "https://rubygems.org"

git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }

- # gem "rails"
+ gem "rails"
コンテナ内のGemfile確認に反映されているか確認
$ cat Gemfile

これでGemfileの準備ができたので、GemのインストールとRailsプロジェクトの作成を行いましょう。

Gemのインストール
$ bundle install
$ bundle exec rails new .

rails exec .は作業ディレクトリに対してRailsプロジェクトを作成します。
実行するとGemfileの上書き確認をされるので許可しましょう。

ローカル環境ではこの時点でRails serverを起動できましたが、rubyイメージから作成したコンテナではまだRails serverは起動できません。
ですが、なぜ起動できないか確認するためにも一度試してみましょう。

起動確認(失敗する).
$ bundle exec rails server
エラーメッセージ(最後のみ抜粋)
/usr/src/project_name/vendor/bundle/ruby/2.6.0/gems/execjs-2.7.0/lib/execjs/runtimes.rb:58:in `autodetect': Could not find a JavaScript runtime. See https://github.com/rails/execjs for a list of available runtimes. (ExecJS::RuntimeUnavailable)

ExecJS::RuntimeUnavailableというエラーについて調べてみると、どうやらJavaScriptランタイムというものが必要のようです。
今回はnode.jsを導入することにしましょう。
参考資料:rails sコマンド実行時に「Could not find a JavaScript runtime.」とエラーが出る場合の対処法

rubyイメージではyumコマンドは対応していないようなので、apt-getを使ってインストールしましょう。
参考資料:apt-get - パッケージの操作・管理 - Linuxコマンド

node.jsのインストール
$ apt-get update
$ apt-get install nodejs

完了したら今度こそRails serverが起動できるはずです。

Rails_server起動
$ bundle exec rails server

http://localhost:3000/に接続して起動できているか確認しましょう。

dockerfile と docker-compose を使ってコマンド一つでRails serverを起動する

ここからは今まで行ってきたことを設定として文書化していきます。
先ほどまで使用していたコンテナはもう使わないので、$ exitで停止しておきましょう。
ただし、作成したRailsプロジェクトはそのままにしておいてください。

参考資料:Dockerfile リファレンス
参考資料:Compose ファイル・リファレンス
参考資料:RailsアプリをDockerで開発するための手順

dockerfile を作成する

まず、前の章で行ったことを整理しておきましょう。

  • rubyイメージからコンテナを作成(本記事ではバージョン2.6.0)
  • ポート3000:3000を開放し、ボリュームを指定してコンテナを起動
  • コンテナ内でRailsプロジェクトを作成
  • コンテナにnode.jsをインストール
  • Rails serverを実行

dockerfileを書くということは、これらの作業を文書化していくということです。

それではdockerfileを書いていきます。Railsプロジェクトを作成したディレクトリにdockerfileという名前でファイルを作成し、編集していきます。
まずは使用したイメージの情報です。

dockerfile
FROM ruby:2.6.0

:2.6.0はバージョン情報です。指定しなければ最新版が自動で選択されますが、固定させた方がいいでしょう。
これはRubyのバージョンと同じなので、もし違うバージョンからRailsプロジェクトを作成したならそのバージョンに合わせてください。

次にポートの開放とボリューム指定ですが、dockerfileではホストPCのディレクトリを指定することはできません(後でdocker-compose.ymlで指定します)。よって、ここではポートの開放だけを行います。

dockerfile
FROM ruby:2.6.0

EXPOSE 3000

ここでポートの開放を行なっても、runコマンドやdocker-compose.ymlで指定しなければ直接接続できないので注意してください。

次はコンテナ内でRailsプロジェクト作成ですが、これは後でボリューム指定により共有する予定なので必要ありません。
ですが、Gemfileが無くてはGemのインストールができず、railsコマンドが実行できません。
そこで、Gemfileだけをコンテナ内にコピーしてインストールすることにします。

dockerfile
FROM ruby:2.6.0

ENV APP_ROOT /usr/src/project_name 

WORKDIR ${APP_ROOT}

COPY Gemfile ${APP_ROOT}
COPY Gemfile.lock ${APP_ROOT}

RUN bundle install

EXPOSE 3000

各コマンドは以下の通りです。

ENV [key] [value] …… 環境変数の定義。
WORKDIR [ディレクトリ指定] …… 作業ディレクトリの指定
COPY [ソース指定] [保存先指定] …… ファイルのコピー。
RUN [コマンド] …… コマンドの実行。

これでコンテナ内でもrailsコマンドが利用できるようになります。

最後にnode.jsのインストールとRails serverの起動です。

dockerfile
FROM ruby:2.6.0

ENV APP_ROOT /usr/src/project_name 

WORKDIR ${APP_ROOT}

RUN apt-get update && \
    apt-get install -y nodejs

COPY Gemfile ${APP_ROOT}
COPY Gemfile.lock ${APP_ROOT}

RUN bundle install

EXPOSE 3000

CMD bundle exec rails server

Railse sever の起動がRUNではなくCMDなのは、コンテナの立ち上げが完了してから実行してほしいからです。
もしRUNで書いてしまうとそこで動作が止まってしまい、いつまでもコンテナが立ち上がらないということになってしまいます。

では、これでうまく動作するかどうか確認してみましょう。

dockerfileからイメージの作成と起動
$ docker build .

エラーが出ずにRails serverの起動までできればOKです。

docker-compose.yml を作成し、起動する

では、先ほど作成したdockerfileを使ってdocker-compose.ymlを作っていきましょう。
先ほどと同じく、Railsプロジェクトのディレクトリに作成し、以下の通りに編集してください。

docker-compose.yml
app:
  build: .
  ports: 
    - '3000:3000'
  volumes: 
    - .:/usr/src/project_name
app: …… アプリケーションの名前。今回はappとした。
build: …… docker-composeのパス指定。今回は同一ディレクトリにある。
ports: …… 開放するポートの指定。
volumes: …… ボリュームの指定。

これで完了です。では起動してみましょう。

docker-composeからの起動
$ docker-compose up -d

http://localhost:3000/に接続して、いつものアレが表示されたらOKです!
ちなみに、ホストPCのディレクトリを参照しているため、ファイルを編集すると即座に反映されます。Gemfileと同じですね。

おわりに

というわけで、起動できました。とりあえず動いたので満足。

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

Prometheus on Dockerでデータを永続化する方法

背景

PrometheusをDockerで利用しようとしていた
作り直すたびに監視データも初期化されるため、データ永続化をしたい

対象環境

> docker-compose version
docker-compose version 1.23.2, build 1110ad01
docker-py version: 3.6.0
CPython version: 3.6.6
OpenSSL version: OpenSSL 1.1.0h  27 Mar 2018
> docker image ls
REPOSITORY                   TAG                 IMAGE ID            CREATED             SIZE
prom/prometheus              v2.6.1              5517f7057e72        9 days ago          97.8MB

ちなみに、本記事で利用するコードはこちらにあります

方法

/etc/prometheus/data がローカルストレージになっている

> docker-compose exec prometheus ls -la /etc/prometheus
total 12
drwxr-xr-x    1 nobody   nogroup       4096 Jan 25 03:00 .
drwxr-xr-x    1 root     root          4096 Jan 25 06:04 ..
lrwxrwxrwx    1 nobody   nogroup         39 Jan 15 20:13 console_libraries -> /usr/share/prometheus/console_libraries
lrwxrwxrwx    1 nobody   nogroup         31 Jan 15 20:13 consoles -> /usr/share/prometheus/consoles/
lrwxrwxrwx    1 root     root            11 Jan 15 20:13 data -> /prometheus
-rw-r--r--    1 root     root           682 Jan 24 06:48 prometheus.yml
> docker-compose exec prometheus ls -la /etc/prometheus/data/
total 4
drwxr-xr-x    5 nobody   nogroup        170 Jan 25 06:01 .
drwxr-xr-x    1 root     root          4096 Jan 25 06:04 ..
drwxr-xr-x    4 nobody   nogroup        136 Jan 25 03:19 data
-rw-r--r--    1 nobody   nogroup          0 Jan 25 06:01 lock
drwxr-xr-x    3 nobody   nogroup        102 Jan 25 06:01 wal

なので、こいつをローカルの適当なdataディレクトリとマウントしてあげれば、良い

そのときのdocker-compose.ymlとDockerfileとprometheus.ymlは下記となる

docker-compose.yml
version: '2'
services:
  prometheus:
    build: .
    ports:
     - "9090:9090"
    volumes:
     - $PWD/prometheus-data:/etc/prometheus/data
FROM prom/prometheus
ADD prometheus.yml /etc/prometheus/
prometheus.yml
global:
  scrape_interval: 15s
  external_labels:
    monitor: 'codelab-monitor'
scrape_configs:
  - job_name: 'prometheus'
    scrape_interval: 5s
    static_configs:
      - targets: ['localhost:9090']

これで下記のコマンドで起動します

> docker-compose up -d
Creating network "prometheus-test_default" with the default driver
Creating prometheus-test_prometheus_1 ... done

グラフ(httpリクエスト合計)を見ると、正しく起動でき、グラフが上昇しているのがわかります

1回目起動時

ここから、コンテナを落として、再度起動してみます

> docker-compose down
Stopping prometheus-test_prometheus_1 ... done
Removing prometheus-test_prometheus_1 ... done
Removing network prometheus-test_default
> docker-compose up -d
Creating network "prometheus-test_default" with the default driver
Creating prometheus-test_prometheus_1 ... done

すると、下記のように一度コンテナを落とした時のグラフも見えるようになります

2回目起動時

失敗談

下記2点をダブルで行っていて、

  • prometheus.ymlをDockerfileでADDしたものをbuild
  • /etc/prometheus/をカレントディレクトリとマウント

ビルドで追加したymlがマウントしたせいで削除されてしまい、起動時にprometheus.ymlが見当たらずにエラーが出ちゃって小一時間悩んでた

> docker-compose up
Creating network "prometheus-test_default" with the default driver
Creating prometheus-test_prometheus_1 ... done
Attaching to prometheus-test_prometheus_1
prometheus_1  | level=info ts=2019-01-25T03:19:16.5079408Z caller=main.go:243 msg="Starting Prometheus" version="(version=2.6.1, branch=HEAD, revision=b639fe140c1f71b2cbad3fc322b17efe60839e7e)"
prometheus_1  | level=info ts=2019-01-25T03:19:16.5080482Z caller=main.go:244 build_context="(go=go1.11.4, user=root@4c0e286fe2b3, date=20190115-19:12:04)"
prometheus_1  | level=info ts=2019-01-25T03:19:16.5080827Z caller=main.go:245 host_details="(Linux 4.9.125-linuxkit #1 SMP Fri Sep 7 08:20:28 UTC 2018 x86_64 433cf28249ba (none))"
prometheus_1  | level=info ts=2019-01-25T03:19:16.5082197Z caller=main.go:246 fd_limits="(soft=1048576, hard=1048576)"
prometheus_1  | level=info ts=2019-01-25T03:19:16.5088957Z caller=main.go:247 vm_limits="(soft=unlimited, hard=unlimited)"
prometheus_1  | level=info ts=2019-01-25T03:19:16.5120108Z caller=main.go:561 msg="Starting TSDB ..."
prometheus_1  | level=info ts=2019-01-25T03:19:16.5125678Z caller=web.go:429 component=web msg="Start listening for connections" address=0.0.0.0:9090
prometheus_1  | level=info ts=2019-01-25T03:19:16.5382688Z caller=main.go:571 msg="TSDB started"
prometheus_1  | level=info ts=2019-01-25T03:19:16.5388817Z caller=main.go:631 msg="Loading configuration file" filename=prometheus.yml
prometheus_1  | level=info ts=2019-01-25T03:19:16.539723Z caller=main.go:430 msg="Stopping scrape discovery manager..."
prometheus_1  | level=info ts=2019-01-25T03:19:16.5398383Z caller=main.go:444 msg="Stopping notify discovery manager..."
prometheus_1  | level=info ts=2019-01-25T03:19:16.540191Z caller=main.go:466 msg="Stopping scrape manager..."
prometheus_1  | level=info ts=2019-01-25T03:19:16.5409244Z caller=main.go:440 msg="Notify discovery manager stopped"
prometheus_1  | level=info ts=2019-01-25T03:19:16.5410353Z caller=main.go:426 msg="Scrape discovery manager stopped"
prometheus_1  | level=info ts=2019-01-25T03:19:16.5411454Z caller=main.go:460 msg="Scrape manager stopped"
prometheus_1  | level=info ts=2019-01-25T03:19:16.5413125Z caller=manager.go:664 component="rule manager" msg="Stopping rule manager..."
prometheus_1  | level=info ts=2019-01-25T03:19:16.5416848Z caller=manager.go:670 component="rule manager" msg="Rule manager stopped"
prometheus_1  | level=info ts=2019-01-25T03:19:16.5489596Z caller=notifier.go:521 component=notifier msg="Stopping notification manager..."
prometheus_1  | level=info ts=2019-01-25T03:19:16.5493186Z caller=main.go:615 msg="Notifier manager stopped"
prometheus_1  | level=error ts=2019-01-25T03:19:16.5501995Z caller=main.go:624 err="error loading config from \"prometheus.yml\": couldn't load configuration (--config.file=\"prometheus.yml\"): open prometheus.yml: no such file or directory"
prometheus-test_prometheus_1 exited with code 1

参考

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

docker error記録

macOS10.13.6
Safari 12.0.2(13606.3.4.1.4)
Docker Desktop Community 2.0.0.2(30215)
で、アプリでloginした状態で、runしようとした。

$ docker run -it kaizenjapan/100pon /bin/bash
docker: Error response from daemon: Bad response from Docker engine.
See 'docker run --help'.

なぜかエラーになった。コマンドラインでloginしてみた。

$ docker login
Authenticating with existing credentials...
Login Succeeded
KM-S10:~ administrator$ docker run -it kaizenjapan/100pon /bin/bash
Unable to find image 'kaizenjapan/100pon:latest' locally
latest: Pulling from kaizenjapan/100pon
05d1a5232b46: Download complete 
33529d049adc: Downloading   62.4MB/94.96MB
be72550a31b7: Downloading  61.89MB/1.522GB
40cab810eef6: Waiting 
90a7cfada776: Waiting 
63b10c589b27: Waiting 
3b74f2e93e93: Waiting 
4ad328879d5f: Waiting 
53bb48f40962: Waiting 
7430ebce441f: Waiting 
152f700b

なぜかエラーにならない。

何度か、この現象が現れたことがある。記録

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

MetabaseとMySQL環境をDockerで作る

はじめに

株価などネットから取得したデータをMySQLに格納し、
それを可視化したかったのでMetabsaeを試してみました。

環境作成にあたり、Dockerを利用しています。

環境

  • Mac OS X 10.14.2
  • Docker version 18.09.0
  • docker-compose version 1.23.2

参考

今回やりたい構成

Metabase_MySQL.png

ディレクトリ構成

.
├── Data
│   └── mysql_data
└── Docker
    └── docker-compose.yml

永続化のためmysql_dataにMySQLのファイルを置きます。

環境作成

イメージがないこと確認

$ docker-compose images
Container   Repository   Tag   Image Id   Size
----------------------------------------------

docker compose用のファイル準備

docker-compose.ymlでみて欲しいところは以下

  • linksにmysqlと記載することで、Docker内はmysqlで名前解決できる。

  • volumesにローカルのファイルをDockerコンテナにマウントする設定を記載することによってMySQLのデータをローカルに保存している。その結果データを永続化できる。

docker-compose.yml
version: "3"
services:
  metabase:
    container_name: metabase
    image: metabase/metabase
    ports:
      - "3000:3000"
    links:
      - mysql
  mysql:
    container_name: mysql
    image: mysql:5.7.22
    ports:
      - "3306:3306"
    environment:
      MYSQL_DATABASE: stock
      MYSQL_USER: stock
      MYSQL_PASSWORD: stock
      MYSQL_ROOT_PASSWORD: root
    volumes:
      - ../Data/mysql_data/:/var/lib/mysql

イメージを取得

$ docker-compose create
WARNING: The create command is deprecated. Use the up command with the --no-start flag instead.
Pulling mysql (mysql:5.7.22)...
5.7.22: Pulling from library/mysql
be8881be8156: Pull complete
c3995dabd1d7: Pull complete
9931fdda3586: Pull complete
bb1b6b6eff6a: Pull complete
a65f125fa718: Pull complete
2d9f8dd09be2: Pull complete
37b912cb2afe: Pull complete
79592d21cb7f: Pull complete
00bfe968d82d: Pull complete
79cf546d4770: Pull complete
2b3c2e6bacee: Pull complete
Pulling metabase (metabase/metabase:)...
latest: Pulling from metabase/metabase
4fe2ade4980c: Already exists
6fc58a8d4ae4: Pull complete
819f4a45746c: Pull complete
cfead1870569: Pull complete
80eef12953dc: Pull complete
8ea161a68113: Pull complete
db7c1ab963fb: Pull complete
Creating mysql ... done
Creating metabase ... done

$ docker-compose images
Container      Repository        Tag       Image Id      Size
--------------------------------------------------------------
metabase    metabase/metabase   latest   0e41d8de8452   221 MB
mysql       mysql               5.7.22   6bb891430fb6   355 MB

起動

$ docker-compose up -d
Starting mysql ... done
Starting metabase ... done

-dをつけてデタッチドモードにしないと、ターミナルに戻ってこれない。

動作確認(MySQL)

$ mysql --host=127.0.0.1 --user=stock --password
Enter password:
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 3
Server version: 5.7.22 MySQL Community Server (GPL)

Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| stock              |
+--------------------+
2 rows in set (0.02 sec)

Metabaseの設定

初期セットアップ

http://localhost:3000/setup

にアクセス

ss_Metabase_01.png

ss_Metabase_02.png

ss_Metabase_03.png

docker-compose.ymlのlinksに記載したmysqlがホスト名

ss_Metabase_05.png

ss_Metabase_06.png

ss_Metabase_07.png

今回は事前にデータをMySQLに表を作成していたので、MySQLに入っている表の一覧が表示される。

ss_Metabase_08.png

表の名前をクリックすると、勝手にSummaryしている。

ss_Metabase_09.png

もちろん表のデータから色々なグラフ(円グラフや棒グラフ、線グラフ)などをSQLからも作成可能。

ss_Metabase_10.png

おまけ

停止

$ docker-compose stop
Stopping metabase ... done
Stopping mysql    ... done

起動(環境削除していない場合)

$ docker-compose start
Starting mysql    ... done
Starting metabase ... done

環境削除

コンテナを停止し、そのコンテナとネットワークを削除

$ docker-compose down
$ docker-compose down
Stopping metabase ... done
Stopping mysql    ... done
Removing metabase ... done
Removing mysql    ... done
Removing network docker_default

環境削除(imageも削除)

上に加えて、imageも削除

$ docker-compose down --rmi all
Removing metabase ... done
Removing mysql    ... done
Removing network docker_default
Removing image mysql:5.7.22
Removing image metabase/metabase
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

さくっと分析環境構築

欲しい分析環境

AzureVM上にdockerでpostgre日本語環境を立ち上げる。
だいぶ簡単になったが、半年やらなかったら忘れそうなので備忘録に。
GPU使うDeepLearningとか以外はだいたいこの環境でなんとかなる。

Azure data science vm for Linux

  • Microsoft R Server Developer エディションと Microsoft R Open
  • Anaconda Python ディストリビューション (バージョン 2.7 および 3.5)
  • JuliaPro
  • スタンドアロン Spark インスタンスと単一ノードの Hadoop (HDFS、Yarn)
  • JupyterHub - R、Python、PySpark、Julia カーネルをサポート
  • Vowpal Wabbit
  • XGBoost, LightGBM
  • 開発ツールとエディター (RStudio、PyCharm、IntelliJ、Emacs、vim)

postgres日本語環境

分析用にSQL環境が欲しかった。ただし、日本語が混じったデータだったのが少し面倒。

HOW TO

VMの立ち上げ

MacketPlaceのDatascience用Imageからさくっとデプロイ
細かい構成は省略。
image.png

Ubuntuのlocaleを日本語へ

sudo locale-gen ja_JP.UTF-8
sudo /usr/sbin/update-locale LANG=ja_JP.UTF-8

そして再度ログインでlocaleがja_JP.UTF-8になっています。

docker-composeのインストール

curl -L https://github.com/docker/compose/releases/download/1.23.2/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose
# バイナリに対して実行権限を追加
chmod +x /usr/local/bin/docker-compose

Dockerfile作成

適当なディレクトリにDockerfileとcomposeファイルを作成する。

Dockerfile
FROM postgres:latest
RUN localedef -i ja_JP -c -f UTF-8 -A /usr/share/locale/locale.alias ja_JP.UTF-8
ENV LANG ja_JP.UTF-8
docker-compose.yaml
version: '2'
services:
  db:
    image: db
    volumes:
      - ./postgresql:/var/lib/postgresql
    ports:
      - "5432:5432"
    environment:
      POSTGRES_USER: username
      POSTGRES_PASSWORD: password
      POSTGRES_DB: testdb
      LANG: ja_JP.UTF-8

DockerImageを作成、コンテナ起動

sudo docker build -t db
sudo docker-compose up -d

雑記

会社でVisualStudioサブスクリプションが購入していて、Azureが月6000円使えるのでAzureを使っています。
Standard DS3 v2 (4 vcpu 数、14 GB メモリ):1時間25.65円

SQL叩ける環境ほしいな〜。おっ、やっぱクラウドサービスでてるじゃ〜〜ん。
…………………たけぇよ。

というわけで、無料のOSSでデータ分析に向いてるというpostgresqlを利用することに。

またデータが日本語混じりのきったねぇデータなので、ロケール変更しました。

一度頑張って普通にpostgresインストールして同環境を作ったんだけど、環境ぶっ壊して再構築する時にキレそうになり(笑)、ついにdockerを覚える機会になった。遅いよ。

いい勉強になった。dockerさまさまですわ。

参考

Docker for Mac で 日本語locale設定を含む PostgreSQL を構築

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