20190226のdockerに関する記事は11件です。

DockerfileのADDは、remoteのurlから取得したファイルを勝手に展開してくれないからどうにかする

はじめに

この感情は...怒り...!?

ADDでremote urlを指定してtgz落として、そのまま展開してくれるよネーなんて思ってたらしてくれなかった。

ADD | Dockerfile

Dockerfileを学びながら書いていたらADDという物を知った

The ADD instruction copies new files, directories or remote file URLs from and adds them to the filesystem of the image at the path .
ADD | Docker Documentation

簡単にいうとホスト側のファイルをコンテナ内に追加するインストラクション。
同じようなものにCOPYというものがる。

ADDCOPYの違い

ざっくりと。
より深く知りたかったらこちらとか
[docker] COPY とADD の違いを試してみた - Qiita

COPY

  • 指定したファイルをコピーする
  • urlはコピー元に指定できないヨ!
  • 圧縮されていてもそのまま!

ADD

  • 指定したファイルをコピーする
  • remoteのファイル(url)もコピー元に指定できる!
  • そのファイルが圧縮されていたら自動解凍!

ざっくりとした違いはこんなかんじ。
ADDでremoteの圧縮されたファイルを指定したらそのまま解凍してコンテナ内に設置できそう!
それができればwget && tar && rmとかナンセンスなRUNをしなくてすみそう!

-> できませんでした。

エラー発生してTwitterで嘆いてると友人から公式リファレンスを投げつけられる。

Resources from remote URLs are not decompressed.
要約: remoteのurlを指定した場合自動解凍はされません。

Dockerfile reference | Docker Documentation

かなしみ

最終的な形

RUN curl -sL http://example.com/release/1.0/example-bin-1.0.tgz \
    | tar zx -C ./

curlしたものをtarにパイプで渡すことでちょっとはマシなDockerfileになった。
ただこれだとexample-bin-1.0というDirができてダサいので、

RUN mkdir example \
    && curl -sL http://example.com/release/1.0/example-bin-1.0.tgz \
    | tar zx -C example  --strip-components 1

これでexampleというdirに中身がいい感じになるようになった。
幸せ。

余談

remoteの圧縮ファイルをADDしたら展開されてしまうバージョンがあったらしくて、
リファレンスと挙動が違うとissueが立ってた。
Behaviour of ADD for remote tar balls changed with 17.06 · Issue #33849 · moby/moby
Screenshot_2019-02-26 23.30.03_ryrodE.png

うん。

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

DockerfileのADDはurlをコピー元に指定すると自動解凍してくれない

はじめに

この感情は...怒り...!?

ADDでremote urlを指定してtgz落として、そのまま展開してくれるよネーなんて思ってたらしてくれなかったので泣きながらRUNを書いた。

最終的なものはこれ

RUN mkdir example \
    && curl -sL http://example.com/release/1.0/example-bin-1.0.tgz \
    | tar zx -C example  --strip-components 1

rmとかは用いていないけどあんまり美しくない。。。

ADD | Dockerfile

Dockerfileを学びながら書いていたらADDという物を知った

The ADD instruction copies new files, directories or remote file URLs from and adds them to the filesystem of the image at the path .
ADD | Docker Documentation

簡単にいうと指定したファイルやディレクトリをコンテナ内に追加するインストラクション。
同じようなものにCOPYというものがる。

ADDCOPYの違い

ざっくりと。
より深く知りたかったらこちらとか
[docker] COPY とADD の違いを試してみた - Qiita

COPY

  • 指定したファイルやフォルダをコピーする
  • urlはコピー元に指定できないヨ!
  • 圧縮されていてもそのまま!

ADD

  • 指定したファイルやフォルダをコピーする
  • remoteのファイル(url)もコピー元に指定できる!
  • コピー元のファイルが圧縮されていたらコンテナ内に自動解凍して配置!

ざっくりとした違いはこんなかんじ。
ADDでremoteの圧縮されたファイルを指定したらそのまま解凍してコンテナ内に設置できそう!
それができればwget && tar && rmとかナンセンスなRUNをしなくてすみそう!

-> できませんでした。

エラー発生してTwitterで嘆いてると友人から公式リファレンスを投げつけられる。

Resources from remote URLs are not decompressed.
要約: remoteのurlを指定した場合自動解凍はされません。

Dockerfile reference | Docker Documentation

かなしみ

最終的な形

RUN curl -sL http://example.com/release/1.0/example-bin-1.0.tgz \
    | tar zx -C ./
# la
example-bin-1.0/

# tree example-bin-1.0
example-bin-1.0
├── bin
│   ├── example
│   ├── sample
│   └── answer
・
・

curlしたものをtarにパイプで渡すことでちょっとはマシなDockerfileになった。
ただこれだとexample-bin-1.0というフォルダができてダサいので、

RUN mkdir example \
    && curl -sL http://example.com/release/1.0/example-bin-1.0.tgz \
    | tar zx -C example  --strip-components 1

tarのオプションでケアしてあげる。
これでmv example-bin-1.0 exampleしたかのような状態。

# la
example/

# tree example
example
├── bin
│   ├── example
│   ├── sample
│   └── answer
・
・

/tmpとかに落として,解凍して,リネームして,/tmpに保存したヤツ削除……という1連の動作を&&で繋ぐよりはスマートだけど、煮え切らないですね。

余談

remoteの圧縮ファイルをADDしたら展開されてしまうバージョンがあったらしくて、
リファレンスと挙動が違うとissueが立ってた。
Behaviour of ADD for remote tar balls changed with 17.06 · Issue #33849 · moby/moby
Screenshot_2019-02-26 23.30.03_ryrodE.png

うん。

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

機械学習実行環境をDockerイメージにする時OpenCVに困ってませんか?

機械学習モデルの実行環境用にDockerイメージを作ろうとして、OpenCVのインストールにハマってしまい丸1日を費やしたので、同じことで悩む人が少しでも減ることを願ってシェアします。

問題点

PythonオフィシャルのDockerイメージに、機械学習に必要なライブラリをpipでインストールして起動し、必要ライブラリをインポートしてバージョン情報を表示するだけの下記プログラムを実行します。

import keras
import tensorflow
import numpy
import PIL
import cv2

print('keras:', keras.__version__)
print('tensorflow:', tensorflow.__version__)
print('numpy:', numpy.__version__)
print('pillow:', PIL.__version__)
print('cv2:', cv2.__version__)

すると、下記のOpenCVが依存しているネイティブ・ライブラリが見つからないというエラーがimportの時点で発生します。

Traceback (most recent call last):
  File "version.py", line 5, in <module>
    import cv2
  File "/usr/local/lib/python3.6/site-packages/cv2/__init__.py", line 3, in <module>
    from .cv2 import *
ImportError: libgthread-2.0.so.0: cannot open shared object file: No such file or directory

解決策

このエラーは、opencv-python自体はpipでインストールされておりcv2は見つかったものの、依存関係にある外部ライブラリが見つからないというエラーになります。「docker pip opencv」などをキーワードに対処方法をググってみると、大半はOpenCVのソースからコンパイルするスクリプトをDockerFileに書こうという記述ばかりが並びちょっと凹みます。

Macでは、pipでopencv-pythonがインストールできて使えていたので、余計に混乱しましたがちょっと冷静になって考えてみると、元々OpenCVはネイティブライブラリでありopencv-pythonはそれに皮を被せただけのラッパーに過ぎないと気づきました。

だったら、apt-getで元々のネイティブライブラリをインストールすれば、依存関係のエラーは解消できるはずと考えてDockerFileを以下のようにしてイメージを作成してみたところエラーは解消されました。

# Replace this line to use python official image as a base.
FROM python:3.6.8-slim-stretch

LABEL Name=try_ml_docker Version=0.0.1
EXPOSE 50000

# Add the following line to get native library of OpenCV.
RUN apt-get update && apt-get -y libopencv-dev 

WORKDIR /app
# Replace this line to copy requirements.txt inside the docker image.
ADD ./requirements.txt /app

RUN python3 -m pip install -r requirements.txt
CMD ["python3", "-m", "try_ml_docker"]

requirements.txtには以下の内容を記載します。coremltoolsはkerasとtensorflowのバージョンに著しく依存するので、coremltools2.0を利用する場合はkerasとtensorflowは下記バージョンに指定します。その他のpythonライブラリは最新版を利用します。

coremltools==2.0
keras==2.1.6
tensorflow==1.5.0
numpy
pillow
opencv-python

Tips

ソースコードやデータセットはVS Code上のものをホストとコンテナの間で共有したいのでカレント・ディレクトリを共有するようにコンテナを実行します。-vパラメーターで渡すのは絶対パスなので、`pwd`コマンドでカレントディレクトリの絶対パスを取得して渡しています。pwdコマンドをくくっているのはバッククォートですので気をつけてください。

$ docker run -v `pwd`:/app -it -d try_ml_docker:latest /bin/bash

おわりに

Apple Musicから流れてくる甲斐バンドの「安奈」を聴きながら、やっぱMac最強、Debian/Ubuntuまじめんどくせぇ…

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

【2019年2月版】Docker for Windows Inside 〜 現代の都市伝説に迫る 〜

Docker for Windows 突然の不調

Dockerが動いてるはずなのにアクセスできない、リクエストは飛んでいるのか?dockerコマンドが信用できない、マウントしたはずのボリュームがない、けどコンテナを作り直しても消えたはずのフォルダが残ってしまっている。。。
怪談のような実話、docker for windows の怖い世界へようこそ。

こちらの Docker for WindowsのMobyLinuxVMに接続する方法 ページにを参考に、Docker for Windowsの本体である Moby Linuxへダイブしてみましょう。

対象バージョン

OS: Windows10 Pro 1803
Docker: Docker Desktop CE Ver.2.0.0.3(31259), Build:8858db3

Dockerファイルを作成

Hyper-V ManagerをもってしてもMobyVMには接続できません。
細かい仕組みはよくわかりませんが、上記ページにある通りにDockerfileを作成し…

Dockerfile
FROM alpine
RUN apk update && \
    apk add util-linux && \
    rm -rf /var/cache/apk/*
ENTRYPOINT ["nsenter", "--target", "1", "--mount", "--uts", "--ipc", "--net", "--pid"]

Dockerfileをビルド!

> docker build -t docker-admin .

コンテナを起動!

> docker run -it --privileged --pid=host docker-admin /bin/sh

入れた〜!

/ #

Dockerの使用メモリを調べる

さっそくfreeコマンドで実際にDockerが使っているメモリの量を調べてみます。
WindowsのリソースモニタからはDockerで設定したメモリの全量が起動時に割り当てられているように見えるため、現在起動中の全コンテナ含めてdockerが使っているメモリの量と、残りの空き容量をサクッと調べるにはこれが手っ取り早いのです。

/ # free
             total       used       free     shared    buffers     cached
Mem:      10197936    3979880    6218056       1076      90512    3252380
-/+ buffers/cache:     636988    9560948
Swap:      2097148          0    2097148

うん、あと、コンテナ3つくらいいけるな、と。

Docker for WindowsとMoby上のDocker Serviceとの通信ログ

続いてdockerデーモン?のログです。ここではdocker for windowsのdockerコマンドが実際に投げているdockerコマンドがログに出力されています。(ややこしくてすいません。)

/ # tail -f /var/log/docker.log
2019-02-20T09:38:15Z docker time="2019-02-20T09:38:15.036785600Z" level=debug msg="Calling GET /v1.24/networks"
2019-02-20T09:38:15Z docker time="2019-02-20T09:38:15.037680700Z" level=debug msg="Calling GET /v1.24/version"
2019-02-20T09:43:15Z docker time="2019-02-20T09:43:15.003472500Z" level=debug msg="Calling GET /_ping"
2019-02-20T09:43:15Z docker time="2019-02-20T09:43:15.003966800Z" level=debug msg="Calling GET /v1.24/info"

Windows側とどのようにやり取りして、結果どうなったかがわかります。コンテナの中身がどうなったかはここでは出てきませんが。。。

あるはずのないVolume

そして、一番問題だった例のアレ。docker run-v に架空のパスを指定するとエラーにはならずに作られてしまう問題。
通常のWindowsのターミナルやPower Shellでは適当に /mnt/c/work/github/... などと指定してもパスの解決でエラーになるため弾かれますが、WSLMsys2Git for Windowsなどのシェルからdockerコマンド入れる際にパスをミスってLinuxスタイルのパスを入力してもなんとなく解決できてしまう?ため、このままMobyにパスが送られてしまうのです!
当然、MobyもLinuxなので/始まりのパスは問題なく受け付けられます。
晴れて『エクスプローラーでは存在しないなのに実在するパス』がマウントされてしまうのです。
そんな存在しないはずなのに実在するパスの入口が以下!
rootfsって明らかに怪しいよね!

/ # ls containers/services/docker/ -l
total 18
-rw-r--r--    1 root     root         12420 Jan  1  1970 config.json
drwxr-xr-x   19 root     root          4096 Jan  1  1970 lower
drwxr-xr-x    1 root     root           200 Feb 20 06:57 rootfs
-rw-r--r--    1 root     root          1272 Jan  1  1970 runtime.json
drwxrwxrwt    4 root     root            80 Feb 20 06:57 tmp

そしてさらにrootfsの中は以下のようにいかにもなディレクトリ構成になっております。
ちらっと見えますかmntが。私の/mnt/c/work/githubなどとやってしまった結果があそこに作成されてしまっているのです!

drwxr-xr-x    2 root     root         14336 Feb 13 10:29 bin
drwxr-xr-x    4 root     root          2048 Feb 13 10:29 dev
drwxr-xr-x    1 root     root           140 Feb 20 06:57 etc
drwxr-xr-x    2 root     root          2048 Feb 13 10:28 home
drwxr-xr-x    1 root     root            60 Feb 20 06:57 lib
drwxr-xr-x    5 root     root          2048 Feb 13 10:28 media
drwxr-xr-x    2 root     root          2048 Feb 13 10:28 mnt      # <- これな。。。
drwxr-xr-x    1 root     root            80 Feb 20 06:57 opt
drwxr-xr-x    2 root     root            40 Feb 20 06:57 port
dr-xr-xr-x    2 root     root          2048 Feb 13 10:28 proc
drwx------    2 root     root          2048 Feb 13 10:29 root
drwxr-xr-x    1 root     root           100 Feb 20 06:57 run
drwxr-xr-x    2 root     root         22528 Feb 13 10:29 sbin
-rwxr-xr-x    1 root     root             0 Feb 20 06:57 sendtohost
drwxr-xr-x    2 root     root          2048 Feb 13 10:28 srv
drwxr-xr-x    2 root     root          2048 Feb 13 10:28 sys
drwxrwxrwt    1 root     root            40 Feb 20 08:09 tmp
drwxr-xr-x    1 root     root            80 Feb 13 10:29 usr
drwxr-xr-x   12 root     root          2048 Feb 13 10:29 var

ただし、出来てしまったディレクトリをむやみにrm -rfなどで消してはいけません。
私のようにドライブ共有ができなくなってしまうかもしれませんよ・・・?

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

Docker Registryを構築する

はじめに

本記事は、Docker Registryを使用して、プライベートレジストリ環境を構築する手順について記載しています。

本記事の環境は以下になります。
手元のMacBook Airがクライアントで、iMacがプライベートレジストリ環境になります。

Docker.png

Docker Registry

Docker Registryは、Dockerイメージを一元管理するためのリポジトリサービスです。
公式のDocker Hubでは、CentOSなど各種Linuxディストリビューションの公式イメージや、有志で作成されたイメージなどが配布されています。

公式イメージの「registry」を使用することで、プライベートレジストリ環境を構築することができます。

現在、registryはVersion2.0系が主流で、Go言語で実装されています。

Docker Registryの構築

イメージのダウンロード(registry)

はじめに、registryイメージをダウンロードします。

  • ダウンロードするイメージの確認
    docker search registry
NAME                                    DESCRIPTION                                     STARS               OFFICIAL            AUTOMATED
registry                                The Docker Registry 2.0 implementation for s…   2454                [OK]                
konradkleine/docker-registry-frontend   Browse and modify your Docker registry in a …   213                                     [OK]
hyper/docker-registry-web               Web UI, authentication service and event rec…   161                                     [OK]
atcol/docker-registry-ui                A web UI for easy private/local Docker Regis…   113                                     [OK]
distribution/registry                   WARNING: NOT the registry official image!!! …   57                                      [OK]
marvambass/nginx-registry-proxy         Docker Registry Reverse Proxy with Basic Aut…   44                                      [OK]
jhipster/jhipster-registry              JHipster Registry, based on Netflix Eureka a…   41                                      [OK]
google/docker-registry                  Docker Registry w/ Google Cloud Storage driv…   37                                      
confluentinc/cp-schema-registry         Official Confluent Docker Images for Schema …   29                                      
joxit/docker-registry-ui                Docker registry v2 web User Interface           23                                      [OK]
klausmeyer/docker-registry-browser      Web Interface for the Docker Registry HTTP A…   17                                      [OK]
openshift/origin-docker-registry        The integrated OpenShift V3 registry            13                                      
deis/registry                           Docker image registry for the Deis open sour…   12                                      
landoop/schema-registry-ui              UI for Confluent's Schema Registry              7                                       [OK]
parabuzzle/docker-registry-ui           Docker registry frontend for registry v2        6                                       
quiq/docker-registry-ui                 Docker Registry UI                              5                                       
anoxis/registry-cli                     You can list and delete tags from your priva…   5                                       [OK]
elasticio/docker-registry-ecs           Docker image to run Docker private registry …   4                                       [OK]
allingeek/registry                      A specialization of registry:2 configured fo…   4                                       [OK]
webhippie/registry                      Docker images for registry                      1                                       [OK]
aibaars/docker-registry2-gcs            Docker Registry2 w/ Google Cloud Storage dri…   1                                       
yammer/docker-registry-cache            Simple docker registry cache using squid-pro…   1                                       [OK]
convox/registry                                                                         0                                       
lorieri/registry-ceph                   Ceph Rados Gateway (and any other S3 compati…   0                                       
torchbox/kube-registry-proxy            kube-registry-proxy fork with correct timeou…   0                                       [OK]
  • イメージのダウンロード
    docker pull registry
Using default tag: latest
latest: Pulling from library/registry
169185f82c45: Pull complete 
046e2d030894: Pull complete 
188836fddeeb: Pull complete 
832744537747: Pull complete 
7ceea07e80be: Pull complete 
Digest: sha256:870474507964d8e7d8c3b53bcfa738e3356d2747a42adad26d0d81ef4479eb1b
Status: Downloaded newer image for registry:latest
  • イメージの確認
    docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
registry            latest              d0eed8dad114        3 weeks ago         25.8MB
centos              latest              1e1148e4cc2c        2 months ago        202MB

コンテナの起動

次に、ダウンロードしたregistryイメージをもとにレジストリ用のコンテナを起動します。
レジストリは5000番ポートを使用します。

  • コンテナの起動
    docker run -d -p 5000:5000 registry
e4a6f960fdb3c79250a4baf90e84c96bcebedd86c023900bb3786f32828ae0b8
  • コンテナの起動確認
    docker ps --format "{{.ID}}\t{{.Image}}\t{{.Ports}}"
e4a6f960fdb3    registry    0.0.0.0:5000->5000/tcp

これでプライベートレジストリ環境が構築できました。

イメージのアップロード

構築したレジストリ環境にアップロードするためのイメージを作成します。
(※)本記事の環境はMac

本記事では例として、CentOSののベースイメージにnginxをインストールします。
なお、nginxをインストールするためには、nginx用レポジトリの追加が必要になるため、公式を参考にして、レポジトリファイルの内容をコピーして作成します。

  • Dockerfileの作成
    vi Dockerfile
# イメージの取得
FROM centos:latest

# 作成者情報
MAINTAINER 0.1 https://twitter.com/Brutus08159681

# Nginx用レポジトリファイルのコピー
ADD nginx.repo /etc/yum.repos.d/

# Nginxのインストール
RUN ["yum", "-y", "install", "nginx"]

# Nginxの自動起動設定
RUN ["systemctl", "enable", "nginx.service"]
  • nginx.repoの作成
    vi nginx.repo
[nginx-stable]
name=nginx stable repo
baseurl=http://nginx.org/packages/centos/$releasever/$basearch/
gpgcheck=1
enabled=1
gpgkey=https://nginx.org/keys/nginx_signing.key
  • Dockerファイルのビルド
    docker build -t webserver .
Sending build context to Docker daemon  10.75kB
Step 1/5 : FROM centos:latest
 ---> 1e1148e4cc2c
Step 2/5 : MAINTAINER 0.1 https://twitter.com/Brutus08159681
 ---> Running in 4d7ff271e184
Removing intermediate container 4d7ff271e184
 ---> 360f621826fd
Step 3/5 : ADD nginx.repo /etc/yum.repos.d/
 ---> 065d346e93ad
Step 4/5 : RUN ["yum", "-y", "install", "nginx"]
 ---> Running in db772f205c5a
Loaded plugins: fastestmirror, ovl

//途中省略

Installed:
  nginx.x86_64 1:1.14.2-1.el7_4.ngx                                             

Dependency Installed:
  make.x86_64 1:3.82-23.el7            openssl.x86_64 1:1.0.2k-16.el7           

Complete!
Removing intermediate container db772f205c5a
 ---> a5dc224331c9
Step 5/5 : RUN ["systemctl", "enable", "nginx.service"]
 ---> Running in cb98e1b1ec8a
Created symlink /etc/systemd/system/multi-user.target.wants/nginx.service, pointing to /usr/lib/systemd/system/nginx.service.
Removing intermediate container cb98e1b1ec8a
 ---> afd46670157f
Successfully built afd46670157f
Successfully tagged webserver:latest

プライベートレジストリにアップロードするために、先ほど作成したイメージにタグを付けます。

  • タグ付け
    docker tag webserver localhost:5000/nginx
    docker images
REPOSITORY             TAG                 IMAGE ID            CREATED             SIZE
webserver              latest              bc4ced76a58a        2 minutes ago       284MB
localhost:5000/nginx   latest              bc4ced76a58a        2 minutes ago       284MB
registry               latest              d0eed8dad114        3 weeks ago         25.8MB
centos                 latest              1e1148e4cc2c        2 months ago        202MB

タグ付けしたイメージをプライベートレジストリ環境にアップロードします。

  • イメージのアップロード(コンテナのプライベートレジストリ)
    docker push localhost:5000/nginx
The push refers to repository [localhost:5000/nginx]
eecb714a1eec: Pushed 
f12d083c2a5d: Pushed 
fbf94d353010: Pushed 
071d8bd76517: Layer already exists 
latest: digest: sha256:ec84796f9312457e9afd0a0b3fff1ee40e52512f4f3b83dbaee8a7e728971ee4 size: 1155

イメージのアップロードが完了したので、確認のため、ローカルに保存したイメージを削除します。

docker rmi webserver

Untagged: webserver:latest

docker rmi localhost:5000/nginx

Untagged: localhost:5000/nginx:latest
Untagged: localhost:5000/nginx@sha256:ec84796f9312457e9afd0a0b3fff1ee40e52512f4f3b83dbaee8a7e728971ee4
Deleted: sha256:afd46670157fa5608642dae40aaad502eb69787bb0494536d9774089f7e44d8d
Deleted: sha256:edfe82b3e83ea6e3fbcb29ba5d801f7a00d5cc6cd1c650fe519d00fbea6a34f7
Deleted: sha256:a5dc224331c9be59615fdf8101d188ed56dd6052176c744f07d1bbdb23109464
Deleted: sha256:313c7c3089ee43faf482383dcb1e04e35cef648c566332239a86920fb4921d1e
Deleted: sha256:065d346e93ad13542b6790324f0ca7766335730334d0d39200948239737164a6
Deleted: sha256:d24e00ece851f250e549c84f3e2165f952cee2bf43c913dd1328023e8888bb81
Deleted: sha256:360f621826fd1ad8823e54e70a586a37cbdea1c9844b6b9f7b00853537931548

docker images

REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
registry            latest              d0eed8dad114        3 weeks ago         25.8MB
centos              latest              1e1148e4cc2c        2 months ago        202MB

イメージのダウンロード(プライベートレジストリ環境→nginxのイメージ)

プライベートレジストリ環境にあるレジストリ用のコンテナから、先ほどアップロードしたnginxのイメージをダウンロードします。

  • コンテナのプライベートレジストリ環境からダウンロード
    docker pull localhost:5000/nginx
latest: Pulling from nginx
a02a4930cb5d: Already exists 
53cacbc24ea9: Pull complete 
6743d7a92897: Pull complete 
0b2d1befb17c: Pull complete 
Digest: sha256:ec84796f9312457e9afd0a0b3fff1ee40e52512f4f3b83dbaee8a7e728971ee4
Status: Downloaded newer image for localhost:5000/nginx:latest
  • イメージの確認
    docker images
REPOSITORY             TAG                 IMAGE ID            CREATED             SIZE
localhost:5000/nginx   latest              afd46670157f        3 minutes ago       284MB
registry               latest              d0eed8dad114        3 weeks ago         25.8MB
centos                 latest              1e1148e4cc2c        2 months ago        202MB
  • コンテナの起動
    docker run --privileged --name "test1" -d -p 8080:80 localhost:5000/nginx /sbin/init
  • nginxの起動確認
    ブラウザからローカルホストの8080にアクセスします。 スクリーンショット 2019-02-23 20.40.35.png

「Welcome to nginx!」の画面が表示されればOKです。

他のDockerクライアントからのダウンロード

他のDockerがインストールされているクライアントから、プライベートレジストリ環境にあるイメージをダウンロードする手順について解説します。

プライベートレジストリ環境のイメージをダウンロードする場合は、docker pullコマンドの引数にプライベートレジストリ環境のIP:5000/取得イメージ名を指定するだけですが、ホスト側で証明書の設定をしていないと以下のようにエラーが出力されます。

docker pull プライベートレジストリ環境のIP:5000/nginx

Using default tag: latest
Error response from daemon: Get https://プライベートレジストリ環境のIPアドレス:5000/v2/: http: server gave HTTP response to HTTPS client

ホスト側で証明書を使用しないでHTTP接続する場合は、以下の設定を行います。
(※)本記事の環境はMac

クライアン側のDockerアイコンをクリックして、perferencesのDeamonタブを開きます。
スクリーンショット 2019-02-23 21.08.24.png

insecure-registries:の「+」をクリックし、プライベートレジストリ環境のIP:5000を追加します。最後に、「Apply & Restart」をクリックします。

スクリーンショット 2019-02-23 21.15.36.png

Docker再起動後、再度、docker pullコマンドを実行すると、イメージがダウンロードできます。

Using default tag: latest
latest: Pulling from nginx
a02a4930cb5d: Pull complete 
53cacbc24ea9: Pull complete 
6743d7a92897: Pull complete 
0b2d1befb17c: Pull complete 
Digest: sha256:ec84796f9312457e9afd0a0b3fff1ee40e52512f4f3b83dbaee8a7e728971ee4
Status: Downloaded newer image for プライベートレジストリ環境のIPアドレス:5000/nginx:latest

おわりに

プライベートレジストリ環境を構築することで、統一したイメージを使用した開発ができるので、Dockerの持ち味をより引き出せます。

参考

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

Raspberry Piにdockerをインストールする

概要

Raspberry Piを簡易サーバにするためにdockerを入れる。
せっかく入れるので記録しておく。

docker install

参考

手順

以下のコマンドでdockerインストールできる。

curl -sSL https://get.docker.com | sh

実行結果

pi@raspberrypi:~ $ curl -sSL https://get.docker.com | sh
# Executing docker install script, commit: 40b1b76
+ sudo -E sh -c apt-get update -qq >/dev/null
+ sudo -E sh -c apt-get install -y -qq apt-transport-https ca-certificates curl >/dev/null
+ sudo -E sh -c curl -fsSL "https://download.docker.com/linux/raspbian/gpg" | apt-key add -qq - >/dev/null
Warning: apt-key output should not be parsed (stdout is not a terminal)
+ sudo -E sh -c echo "deb [arch=armhf] https://download.docker.com/linux/raspbian stretch edge" > /etc/apt/sources.list.d/docker.list
+ sudo -E sh -c apt-get update -qq >/dev/null
+ sudo -E sh -c apt-get install -y -qq --no-install-recommends docker-ce >/dev/null
+ sudo -E sh -c docker version
Client:
 Version:           18.09.0
 API version:       1.39
 Go version:        go1.10.4
 Git commit:        4d60db4
 Built:             Wed Nov  7 00:57:21 2018
 OS/Arch:           linux/arm
 Experimental:      false

Server: Docker Engine - Community
 Engine:
  Version:          18.09.0
  API version:      1.39 (minimum version 1.12)
  Go version:       go1.10.4
  Git commit:       4d60db4
  Built:            Wed Nov  7 00:17:57 2018
  OS/Arch:          linux/arm
  Experimental:     false
If you would like to use Docker as a non-root user, you should now consider
adding your user to the "docker" group with something like:

  sudo usermod -aG docker pi

Remember that you will have to log out and back in for this to take effect!

WARNING: Adding a user to the "docker" group will grant the ability to run
         containers which can be used to obtain root privileges on the
         docker host.
         Refer to https://docs.docker.com/engine/security/security/#docker-daemon-attack-surface
         for more information.

** DOCKER ENGINE - ENTERPRISE **

If you’re ready for production workloads, Docker Engine - Enterprise also includes:

  * SLA-backed technical support
  * Extended lifecycle maintenance policy for patches and hotfixes
  * Access to certified ecosystem content

** Learn more at https://dockr.ly/engine2 **

ACTIVATE your own engine to Docker Engine - Enterprise using:

  sudo docker engine activate

動作確認

バージョン確認

pi@raspberrypi:~ $ docker -v
Docker version 18.09.0, build 4d60db4

hello-worldやってみる

pi@raspberrypi:~ $ docker pull hello-world
Using default tag: latest
Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Post http://%2Fvar%2Frun%2Fdocker.sock/v1.39/images/create?fromImage=hello-world&tag=latest: dial unix /var/run/docker.sock: connect: permission denied

エラーが出たので、以下のサイトを参考に権限付与。
https://techoverflow.net/2017/03/01/solving-docker-permission-denied-while-trying-to-connect-to-the-docker-daemon-socket/

pi@raspberrypi:~ $ sudo usermod -a -G docker pi
pi@raspberrypi:~ $ docker pull hello-world
Using default tag: latest
latest: Pulling from library/hello-world
c1eda109e4da: Pull complete
Digest: sha256:2557e3c07ed1e38f26e389462d03ed943586f744621577a99efb77324b0fe535
Status: Downloaded newer image for hello-world:latest

hello-worldイメージ取得成功。

取得できたイメージを確認

pi@raspberrypi:~ $ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
hello-world         latest              618e43431df9        8 weeks ago         1.64kB

docker hello world実行

pi@raspberrypi:~ $ docker run hello-world

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
    (arm32v7)
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
 https://hub.docker.com/

For more examples and ideas, visit:
 https://docs.docker.com/get-started/

できた!!!

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

Dockerでコンテナのbashに入ってからコマンド打てなかった話

なんの話するの

MySQLでエラー頻発するようになって、
Dockerに移行しようと思い付き、
いまいじっているが。。

コンテナに対してbashを起動したあと、
何したらいいのかわからなかったので
備忘録としてまとめておく。

コンテナに対するbashとは?

ターミナルのこと。
そのコンテナ上で(=作業したいapp(コンテナ)に対して)ターミナルを起動させる。

イメージをビルドしたあと、

docker-composer run potepanec bash

を実行、これによりサービス名「potepanec」のコンテナに対してbash(ターミナル)が起動される。

その次僕は

root@a2953f92ffc3:/tmp`# bundle exec rails s

と打ったが

Usage:
  rails new APP_PATH [options]

Options:
      [--skip-namespace], [--no-skip-namespace]            # Skip namespace (affects only isolated applications)
  -r, [--ruby=PATH]                                        # Path to the Ruby binary of your choice
                                                           # Default: /usr/local/bin/ruby
  -m, [--template=TEMPLATE]                                # Path to some application template (can be a filesystem path or URL)
  -d, [--database=DATABASE]                                # Preconfigure for selected database (options: mysql/postgresql/sqlite3/oracle/frontbase/ibm_db/sqlserver/jdbcmysql/jdbcsqlite3/jdbcpostgresql/jdbc)
                                                           # Default: sqlite3
      [--skip-yarn], [--no-skip-yarn]                      # Don't use Yarn for managing JavaScript dependencies
      [--skip-gemfile], [--no-skip-gemfile]                # Don't create a Gemfile
  -G, [--skip-git], [--no-skip-git]                        # Skip .gitignore file
      [--skip-keeps], [--no-skip-keeps]                    # Skip source control .keep files
  -M, [--skip-action-mailer], [--no-skip-action-mailer]    # Skip Action Mailer files
  -O, [--skip-active-record], [--no-skip-active-record]    # Skip Active Record files
      [--skip-active-storage], [--no-skip-active-storage]  # Skip Active Storage files
  -P, [--skip-puma], [--no-skip-puma]                      # Skip Puma related files
  -C, [--skip-action-cable], [--no-skip-action-cable]      # Skip Action Cable files
  -S, [--skip-sprockets], [--no-skip-sprockets]            # Skip Sprockets files
      [--skip-spring], [--no-skip-spring]                  # Don't install Spring application preloader
      [--skip-listen], [--no-skip-listen]                  # Don't generate configuration that depends on the listen gem
      [--skip-coffee], [--no-skip-coffee]                  # Don't use CoffeeScript
  -J, [--skip-javascript], [--no-skip-javascript]          # Skip JavaScript files
      [--skip-turbolinks], [--no-skip-turbolinks]          # Skip turbolinks gem
  -T, [--skip-test], [--no-skip-test]                      # Skip test files
      [--skip-system-test], [--no-skip-system-test]        # Skip system test files
      [--skip-bootsnap], [--no-skip-bootsnap]              # Skip bootsnap gem
      [--dev], [--no-dev]                                  # Setup the application with Gemfile pointing to your Rails checkout
      [--edge], [--no-edge]                                # Setup the application with Gemfile pointing to Rails repository
      [--rc=RC]                                            # Path to file containing extra configuration options for rails command
      [--no-rc], [--no-no-rc]                              # Skip loading of extra configuration options from .railsrc file
      [--api], [--no-api]                                  # Preconfigure smaller stack for API only apps
  -B, [--skip-bundle], [--no-skip-bundle]                  # Don't run bundle install
      [--webpack=WEBPACK]                                  # Preconfigure for app-like JavaScript with Webpack (options: react/vue/angular/elm/stimulus)

Runtime options:
  -f, [--force]                    # Overwrite files that already exist
  -p, [--pretend], [--no-pretend]  # Run but do not make any changes
  -q, [--quiet], [--no-quiet]      # Suppress status output
  -s, [--skip], [--no-skip]        # Skip files that already exist

Rails options:
  -h, [--help], [--no-help]        # Show this help message and quit
  -v, [--version], [--no-version]  # Show Rails version number and quit

Description:
    The 'rails new' command creates a new Rails application with a default
    directory structure and configuration at the path you specify.

    You can specify extra command-line arguments to be used every time
    'rails new' runs in the .railsrc configuration file in your home directory.

    Note that the arguments specified in the .railsrc file don't affect the
    defaults values shown above in this help message.

Example:
    rails new ~/Code/Ruby/weblog

    This generates a skeletal Rails installation in ~/Code/Ruby/weblog.

と出てきて、進まなくなった。

ふーん、けど見た感じrails newはいけなくもないのか。
→てことは(これから編集したい)アプリが認識されていない?
そもそもパスが違う?

たったのこれだけだった

root@a2953f92ffc3:/tmp$ bundle exec rails s

tmpってなんだよtmpって

root@a2953f92ffc3:/tmp$ cd ..

の結果、

root@a2953f92ffc3:/$

あ〜作業ディレクトリに入ってなかったのね。
bash起動したらtmpっていう作業フォルダじゃないとこに入っちゃったのか。

てなわけで

root@a2953f92ffc3:/$ ls
bin  boot  bundle  dev  etc  home  lib  lib64  media  mnt  opt  potepanec  proc  rails  root  run  sbin  srv  sys  tmp  usr  var

potepanecに入ろう

root@a2953f92ffc3:/$ cd potepanec
root@a2953f92ffc3:/potepanec$ ls
Dockerfile.dev  Gemfile  Gemfile.bak  Gemfile.lock  Gemfile.lock.bak  README.md  Rakefile  WITHOUTDOCKER.md  app  bin  config  config.ru  db  docker-compose.yml  docs  lib  log  node_modules  package.json  public  tmp  vendor

おーいつもの顔ぶれ!



てことで、ただ単に作業ディレクトリに移動してなかったってだけでした。

誰かの役に立てたら、と思って記事書いたけど、こんなん間違うの俺だけかも?


おわり

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

GCP Container Registry 上にあるコンテナイメージの一番新しい tag を取ってくる

$ gcloud container images list-tags gcr.io/${PROJECT_NAME}/${IMAGE_NAME} --sort-by=~timestamp |\
 awk 'NR == 2 {print $2}'
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Telepresence で Microservices on Kubernetes の開発環境構築

この記事は以下の 3 点について書いています。

  • Microservices on Kubernetes の開発環境の課題と Telepresence の概要
  • Telepresence を使い、ローカルのコンテナから Kubernetes 上のサービスにアクセスするサンプル
  • Telepresence を利用した、Spring Bootの開発環境のサンプルと解説

背景

Microservices on Kubernetes の開発環境の課題

Kubernetes 上で動作するマイクロサービスの開発では、あるサービスを開発する際に依存関係をどのように用意するかが問題になります。

ローカルにすべての依存関係を用意しようとすると、クラウドが提供するリソースのエミュレートや PC のリソース不足などの問題が発生します。

一方、Kubernetes 上ですべて開発しようとすると、開発中のデバッグが困難になります。
適切にデバッグするためには、開発者全員が Kubernetes をよく理解している必要があります。

その他のメリット・デメリットを含め、この問題について Telepresence の開発元 Datawire のマイクロサービスアーキテクチャガイド Development environments for Kubernetes にまとめられています。

Telepresence とは

上記の問題の解決策として、 PC と Kubernetes を接続し、手元で開発しつつ依存関係は Kubernetes を見にいくという手段があります。
PC と Kubernetes を VPN で接続するイメージです。

それを実現してくれるのが Telepresence です。
Telepresenceは OSS であり、CNCF Sandbox プロジェクトの 1 つです。

検証環境

今回は以下の環境で検証しました。

$ telepresence --version
0.97
$ docker --version
Docker version 18.09.2, build 6247962
$ kubectl version
Client Version: version.Info{Major:"1", Minor:"13", GitVersion:"v1.13.1", GitCommit:"eec55b9ba98609a46fee712359c7b5b365bdd920", GitTreeState:"clean", BuildDate:"2018-12-13T19:44:10Z", GoVersion:"go1.11.2", Compiler:"gc", Platform:"darwin/amd64"}
Server Version: version.Info{Major:"1", Minor:"11+", GitVersion:"v1.11.5-eks-6bad6d", GitCommit:"6bad6d9c768dc0864dab48a11653aa53b5a47043", GitTreeState:"clean", BuildDate:"2018-12-06T23:13:14Z", GoVersion:"go1.10.3", Compiler:"gc", Platform:"linux/amd64"}

Kubernetes は Amazon EKS を利用しています。

サンプル実行

Telepresence のドキュメント のサンプルを実行していきます。

サンプルアプリケーションデプロイ

まずは Telepresence のドキュメント に記載の通り、サンプルアプリケーションをデプロイしておきます。

$ kubectl run qotm --image=datawire/qotm:1.3 --port=5000 --expose

Telepresence 実行サンプル

Telepresence では、--docker-run というオプションを利用して、ローカルで Kubernetes 上と通信可能なコンテナを起動することができます。

例えば、以下のように起動します。

$ telepresence --docker-run --rm -it alpine sh

curl を使い、先ほどデプロイしたサンプルアプリケーションへのアクセスを確認します。

# apk add --no-cache curl
# curl http://qotm:5000/
{"hostname":"qotm-5969b5f959-vv82j","ok":true,"quote":"The light at the end of the tunnel is interdependent on the relatedness of motivation, subcultures, and management.","time":"2019-02-26T07:13:41.112912","version":"1.3"}

このように、非常に簡単に Kubernetes 上のアプリケーションにアクセスできました。

telepresence --docker-run--docker-run 以降の部分は、通常の docker run コマンドのオプションを与えるようになっています。

実際に Telepresence のヘルプを確認すると...

$ telepresence -h
usage: telepresence [-h] [--version] [--verbose] [--logfile LOGFILE]
                    [--method {inject-tcp,vpn-tcp,container}]
                    [--new-deployment DEPLOYMENT_NAME | --swap-deployment DEPLOYMENT_NAME[:CONTAINER]
                    | --deployment EXISTING_DEPLOYMENT_NAME]
                    [--context CONTEXT] [--namespace NAMESPACE]
                    [--expose PORT[:REMOTE_PORT]]
                    [--also-proxy CLOUD_HOSTNAME] [--mount PATH_OR_BOOLEAN]
                    [--env-json FILENAME] [--env-file FILENAME]
                    [--run-shell | --run ... | --docker-run ...]
    :
    :
    :
  --docker-run ...      Run a Docker container, by passing the arguments to
                        'docker run', e.g. '--docker-run -i -t ubuntu:16.04
                        /bin/bash'. Requires --method container.

このように、docker run に渡す引数を与えるようにと書かれています。

telepresence --docker-run は内部で docker run コマンドを実行しており、その内容は ./telepresence.log で確認できます。

$ cat ./telepresence.log
    :
    :
    :
 256.9 TEL | Main process (docker run --name=telepresence-1551165091-314365-90696 --network=container:telepresence-1551165070-841074-90696 -e=TELEPRESENCE_POD -e=TELEPRESENCE_CONTAINER -e=KUBERNETES_SERVICE_PORT -e=KUBERNETES_PORT_443_TCP_PROTO -e=QOTM_SERVICE_HOST -e=QOTM_PORT_5000_TCP -e=QOTM_PORT_5000_TCP_PORT -e=QOTM_PORT_5000_TCP_ADDR -e=KUBERNETES_SERVICE_HOST -e=KUBERNETES_PORT_443_TCP -e=KUBERNETES_PORT_443_TCP_PORT -e=KUBERNETES_PORT_443_TCP_ADDR -e=QOTM_PORT -e=QOTM_PORT_5000_TCP_PROTO -e=KUBERNETES_SERVICE_PORT_HTTPS -e=KUBERNETES_PORT -e=QOTM_SERVICE_PORT -e=TELEPRESENCE_ROOT -e=TELEPRESENCE_METHOD --volume=/tmp/tel-5i3c8wyy/fs:/tmp/tel-5i3c8wyy/fs --init --rm -it alpine sh)
    :
    :
    :

Telepresence が環境変数などのオプションを加えた上で docker run を実行していることが分かります。

Spring Boot の開発環境構築

ここまでで Telepresence の概要がつかめたので、実際に Spring Boot の開発環境を構築します。

シェルスクリプト作成

開発環境構築といっても、以下のシェルスクリプトを用意するだけです。

#!/bin/bash
#
# 開発環境のコンテナを起動するスクリプト
#
# 引数1. 開発対象のプロジェクトホームディレクトリ
#
# <実行例>
# ./develop.sh .
#

set -o errexit
set -o nounset
set -o pipefail
set -o xtrace

readonly DEVELOP_PROJECT_DIR="$1"
readonly DEVELOP_PROJECT_ABSOLUTE_DIR=$(cd ${DEVELOP_PROJECT_DIR}; pwd)

readonly IMAGE='openjdk:8u191-jdk-alpine3.8'
readonly APP_PORT='8080'
readonly DEBUG_PORT='5005'

readonly CONTAINER_WORKING_DIR='/opt/work'

telepresence \
  --docker-run \
  --rm \
  -p "${APP_PORT}":"${APP_PORT}" \
  -p "${DEBUG_PORT}":"${DEBUG_PORT}" \
  -v "${DEVELOP_PROJECT_ABSOLUTE_DIR}":"${CONTAINER_WORKING_DIR}" \
  -v ~/.m2:/root/.m2 \
  -w "${CONTAINER_WORKING_DIR}" \
  "${IMAGE}" \
  ./mvnw spring-boot:run \
  -Drun.jvmArguments="-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=${DEBUG_PORT}"

解説

上記のシェルスクリプトのポイントを順に解説していきます。

spring-boot-devtools によるホットデプロイ

コードを書き、コンテナにビルドしてから動作確認するのではあまりに時間がかかります。
そこで、spring-boot-devtools を使っています。

pom.xml に spring-boot-devtools を追加し、./mvnw spirng-boot:run というコマンドで起動することで、ソースコードの変更が即座に反映されるようになります。

このとき、 -v "${DEVELOP_PROJECT_ABSOLUTE_DIR}":"${CONTAINER_WORKING_DIR}" の部分で、プロジェクトホームディレクトリをコンテナのワーキングディレクトリにマウントしていることがポイントです。

これにより、コンテナ内で開発せずとも、ローカルで IDE を使っての開発が可能になります。

※ IntelliJ で spring-boot-devtools を利用する際は IntelliJ 側に追加の設定が必要なのでご注意ください

--rm オプション

私はローカルで実行するコンテナには基本的に --rm オプションをつけるようにしています。
--rm オプションをつけることで、コンテナは停止時に削除されるようになります。
これにより、PC に不要なコンテナがたまっていかないようにします。

.m2 ディレクトリのマウントによる Maven キャッシュの利用

--rm で毎回コンテナを作り直すことにすると、そのままでは .m2 ディレクトリの Maven キャッシュが効かず、毎回起動に長時間かかるようになってしまいます。

-v ~/.m2:/root/.m2 で .m2 ディレクトリをマウントすることで、この問題を解決しています。

--rm オプションを使わずコンテナを使いまわすという解決策は、コンテナを削除しないモチベーションを発生させてしまい、再現性の低い開発環境を生み出してしまう可能性があるため推奨しません。

デバッガの実行

コンテナ上でアプリケーションが動いていても、デバッガは問題なく利用できます。
起動コマンドに -Drun.jvmArguments="-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=${DEBUG_PORT}" というオプションを付与し、 -p "${DEBUG_PORT}":"${DEBUG_PORT}" のようにしてデバッガ用のポートをホストにフォワードするだけです。
これで IDE のデバッガが通常通り動作します。

まとめ

Telepresence は --docker-run を使うだけであれば非常に低い学習コストで利用可能でした。
Kubernetes 上の Deployment の入れ替えなど、応用的な利用方法もあるようですが、まずは --docker-run だけでも使ってみるとよさそうです。

今回は Spirng Boot の開発環境を例にしましたが、コンテナ上で開発環境を構築するには結構なコツを知っている必要があります。
例えば、webpack-dev-server をコンテナ上で使用する場合、設定を変更しないとライブリロードしてくれません。

Kubernetes やその周辺ツールを使いこなすためには、実際に色々構築して知見をためていく必要がありそうです。

余談 (Telepresence をコンテナ化するべきか)

Amazon EKS + Telepresence on Mac を利用する場合、以下の通り依存関係となるツールが非常に多いです。

  • Docker
  • AWS CLI
  • kubectl
  • aws-iam-authenticator
  • osxfuse
  • Telepresence

Docker はいずれにせよインストールが必要ですが、その他 5 つのツールを全ての開発者にインストールしてもらうのは少し手間になります。
そこで、Docker 以外の 5 つのツールをコンテナ化して配布することも考えられます。

コンテナ化するメリットは

  • 開発環境セットアップの手間が減る
  • 開発者から Kubernetes を隠蔽できる

といった点だと思われます。

一方、コンテナ化しないメリットは

  • 興味を持った開発者にとって開発環境を理解するコストが下がる
  • 開発者が少しずつ Kubernetes にふれ、学んでいく可能性がある

といった点だと思います。

Telepresence までコンテナ化することにより、ポータビリティは向上しますが、Dev - Ops の分離のような現象が発生する可能性があります。
何でもコンテナにすれば良い訳ではないという一例でした。

参考

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

Dockerコンテナで起動したAngularをVSCodeでデバッグ

Dockerコンテナで起動したAngularをVSCodeでデバッグ

VSCodeのlaunch.jsonで手間取ってしまったのでメモ。

前提

  • アプリはdockerコンテナ内の /app/front ディレクトリで公開
  • 公開URLはlocalhostではなく http://myapp.develop/ で公開。npm hotelを使ってlocalhost:port に独自のローカルドメインを付与しているだけなので通常のlocalhost:4200でも特に問題ない。ちなみに npm hotel は複数の案件を手がけなければならないときなど非常に便利です。各サービスごとに自由にドメインを設定できるし、今時ならローカル開発時からSSLでやるのも珍しくないでしょうから重宝します。

VSCode launch.jsonの設定

VSCodeが生成するChrome用のデバッグ設定は以下のようになる。

{
  // Use IntelliSense to learn about possible attributes.
  // Hover to view descriptions of existing attributes.
  // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
  "version": "0.2.0",
  "configurations": [
    {
      "type": "chrome",
      "request": "launch",
      "name": "Launch Chrome against localhost",
      "url": "http://localhost:8080",
      "webRoot": "${workspaceFolder}"
    }
  ]
}

私の環境の場合まず、独自ローカルドメインでホストしているのでurlを次のように変更する必要がある。

"url": "http://myapp.develop",

Angularは内部でwebpackを使ってソースをバンドルしてるため以下の追加設定をlaunch.jsonに追加する必要がある。

vscode/launch.json
    "sourceMapPathOverrides": {
    "webpack:////app/front/*": "${webRoot}/*"
    }

VSCodeが自動で展開してくれる sourceMapPathOverrides のデフォルトは以下のようになる。
通常の開発では作業ディレクトリがそのまま公開URLのルートになるのでこのデフォルトのままでもいけるようだがdockerコンテナで公開している場合などは適宜ソースマップのパスを変換してやる必要がある。

vscode/launch.json
      "sourceMapPathOverrides": {
        "webpack:///./*": "${webRoot}/*",
        "webpack:///src/*": "${webRoot}/*",
        "webpack:///*": "*",
        "webpack:///./~/*": "${webRoot}/node_modules/*",
        "meteor://?app/*": "${webRoot}/*"
      }

最終的に出来があるlaunch.jsonは

vscode/launch.json
{
  // Use IntelliSense to learn about possible attributes.
  // Hover to view descriptions of existing attributes.
  // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
  "version": "0.2.0",
  "configurations": [
    {
      "type": "chrome",
      "request": "launch",
      "name": "Launch Chrome against localhost",
      "url": "http://myapp.develop",
      "webRoot": "${workspaceFolder}",
      "sourceMaps": true,
      "sourceMapPathOverrides": {
        "webpack:////app/front/*": "${webRoot}/*"
      }
    }
  ]
}

これでVSCodeでF5を押せばブラウザが起動してVSCodeでブレークポイントをおいたりデバッグが可能になる。

ソースマップのオーバーライドパスがよくわからない場合は、Chromeの開発ツールでsourceの箇所を開いて適当なソースファイルがどのようなパスになっているか確認してみればすぐ分かる。

image01.png

image02.png

この場合だと、webpack:////app/front/ をワークスペースのパスで置き換えるように指定してあげればよい。

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

KubernetesからみたDocker

Kubernetesとは?

Containerの利点を引き上げるもの。

間違ってはいないが中身の無い回答である。まずはKubernetes云々という前にContainer(Docker)について考える。

Containerとは?

Containerとは小さなVMであるという比喩から始めよう。

VMと言えば、VMWareやVirtualBox等のハイパーバイザーを使って、サーバ上で別のOSを動かす技術のことであるが、これの良さは、それぞれのVM毎に独立して、かつカスタマイズしたサーバ環境を容易に実現できるところにある。例えば以下のような使い方ができる。

  • あらかじめ開発に必要なjavaやDBをインストールしておいたVMを、OVAなどのVMイメージとしてチームメンバーに配布する。チームメンバーは手元のPCでそのVMイメージをもとにVMを立ち上げる。このようにすれば、チームメンバー間の開発環境の差異(OSによる挙動の差異やらインストールしたミドルウェアのバージョンの違いなど)を意識せず、開発に取り組める。気に入らなければ、壊して作り直せばよい。
  • ソフトウェアを予めセッティングしして本番稼働可能な状態でVMイメージとして保存しておく。顧客のリクエストがあれば、そのVMを起動するだけでデプロイ完了。
  • NginxやMariaDBをチューニングしまくって、超高速にWordPressを動かせるサーバ環境を作った。これをVMイメージとして公開して、多くの人が手元のVMで使用できる状態にする -> 例えば KUSANAGI for AWS など。

しかしながら、VM/VMイメージの実際の運用はなかなか骨が折れる。まずVMを起動するまでに時間がかかる。パソコンを普通に立ち上げることと変わらない。またカスタマイズしたVMイメージは往々にしてサイズが大きくなる。VMイメージのサイズが1GBを超えることは珍しくない。従って、例えば「VMイメージに梱包したアプリケーションを1行変更したものを、再度VMイメージにしてファイルサーバにあげて、それを本番環境でダウンロードしなおして動かす」なんて運用は、かったるすぎてまずやろうと思わない。

VMの「独立したサーバ環境を提供する」という点は非常に魅力的だ。サーバにインストールされたライブラリのバージョンと、そのサーバで動いている複数のアプリケーションの依存関係を気にする、という考慮にかかる膨大な時間を取り除いてくれる。ただ、もう少し起動時間が短ければ、もう少しサイズが小さければ、もっとその良さを活かせるのに。

この願望をかなえるのがContainerである。このような理由でContainerを"小さなVM"と例えた。

Containerの利点:

  • 起動と停止が速い
  • Containerイメージが軽量
  • Container毎に独立した環境
  • どこでも動く
  • アプリケーションとミドルウェアのデプロイが同じ操作で行える

以下では、上にあげた利点を説明する。尚、Containerと呼ばれるものは沢山あるが、kenji-kondoはDockerしか知らないので、以下Doockerに限った話しかしない。

Dockerの利点

起動停止の速さ

実際にdockerを動かしてみる。

[vagrant@base ~]$ docker run centos echo "Hello World"
Unable to find image 'centos:latest' locally
latest: Pulling from library/centos
a02a4930cb5d: Pull complete
Digest: sha256:184e5f35598e333bfa7de10d8fb1cebb5ee4df5bc0f970bf2b1e7c7345136426
Status: Downloaded newer image for centos:latest
Hello World

docker run centos echo "Hello World"というコマンドだけで、以下のようなことが行われた。

  • CentOS7ベースのContainerイメージをインターネット上のrepositoryからダウンロード
  • Containerを起動
  • Hello Worldをecho
  • Container停止

Containerイメージを取得する部分でkenji-kondoの自宅のネットワーク環境では30秒ほどかかった。この時点でmvnでjavaのアプリケーションをビルドするよりも速い。

Containerイメージはローカルに存在しなければインターネット上から探して取得するようになっている。なので、すでにダウンロードしておいた状態でもう一度同じコマンドを実行すると、Container起動 -> Hello Worldをecho -> Container停止 の動作しか行わない。

[vagrant@base ~]$ time docker run centos echo "Hello World"
Hello World

real    0m2.304s
user    0m0.094s
sys     0m0.116s

Container起動 -> Hello Worldをecho -> Container停止 が2.3秒で完了した。

軽量

先ほどダウンロードしたContainerイメージのサイズを確認する。

[vagrant@base ~]$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
centos              latest              1e1148e4cc2c        2 months ago        202MB

SIZEのところに注目。202MBしかない。Centos7のminimal.isoでも900MBほどある。(https://www.centos.org/download/)

しかも、この202MBというのは、Containerとしては大きい部類である。さらに軽量なContainerイメージだと、以下のように1.2MBのものもある。

[vagrant@base ~]$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
busybox             latest              d8233ab899d4        10 days ago         1.2MB
centos              latest              1e1148e4cc2c        2 months ago        202MB

これほど軽量にできる理由の一端を示す。動かしたContainerにログインして、/etc/以下のファイルを除いてみる。

[vagrant@base ~]$ docker run -it redis /bin/sh
/ #
/ # ls -l /etc/
total 28
-rw-rw-r--    1 root     root           307 Jan 30 15:40 group
-rw-r--r--    1 root     root            13 Feb 25 16:57 hostname
-rw-r--r--    1 root     root           174 Feb 25 16:57 hosts
-rw-r--r--    1 root     root           127 Dec 31 09:43 localtime
lrwxrwxrwx    1 root     root            12 Feb 25 16:57 mtab -> /proc/mounts
drwxr-xr-x    6 root     root            79 Feb 14 18:58 network
-rw-r--r--    1 root     root           340 Jan 30 15:40 passwd
-rw-r--r--    1 root     root            78 Feb 25 16:57 resolv.conf
-rw-------    1 root     root           243 Jan 30 15:40 shadow

10個しかない。普通のサーバだとデフォルトで150~200個ほどある。また別のContainerにログインしてpsコマンドをたたいてみる。

[vagrant@base ~]$ docker run -it redis  /bin/sh
# ps
/bin/sh: 1: ps: not found
# vi
/bin/sh: 3: vi: not found

psもviもできない。

このように、普通のサーバでは当然あるだろうものがない。本当にアプリケーションを動かすためだけに必要なものだけをContainerに詰め込んでる、というかんじ。だからサイズが小さくなる。

独立した環境

これはVMの利点と同じ。例えばContainer_Aにpython27のアプリを詰め込み、かつContainer_Bにpython36で動くアプリを詰め込んで動かす。サーバから見ればいずれもただのDockerのプロセスにしか見えていない。サーバにpythonがインストールされていなくても動く。「python27がインストールされたサーバでpython36を動かしたいから、インフラエンジニアと調整してpyenvやvirtualenvで仮想python実行環境を作ったぜ」という配慮が必要なくなる。

どこでもなんでも動く

"Write once, run anywhere."というjavaの標語のように、Dockerにも"Build once, run anywhere."という標語がある。これが示すように、Dockerもdockerがインストールされた環境ならばどこでも動くことを目指して設計・実装されている。

javaと違うのは、どこでも動く主体がjavaだけではないことだろうか。pythonでもrubyでも、はたまたnginxやredisでも、Containerイメージとなったものならば、確かに動く。

またアプリケーションやミドルウェアによらず、オペレーションが"Containerへの操作"で統一できるところも、俗人化の排除を手助けしてくれる。

nginxのContainerを動かす:

[vagrant@base ~]$ sudo docker run -d -p 80:80 nginx
854ce9264ed5f834309fefc4912735c479264f57c64bf5bf868a21ed8d83e7a7

[vagrant@base ~]$ curl -s localhost | head -n4
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>

curl localhost:8080Hello Dockerと返すだけのSpring Bootアプリケーションが梱包されたContainerを動かす:

[vagrant@base ~]$ docker run -d -p 8080:8080 kenchaaan/myjava
ad4ac112c83b82cd38145563243a00b6e5823496a6057a9f4e9244b34224f54c

[vagrant@base ~]$ curl localhost:8080
Hello Docker

Dockerを使った開発と設計

ここからはDocker周辺の話題を。

開発

DockerでContainerイメージを作るときは、手動/自動にかかわらず、以下の操作がどこかで行われるはずだ。

  • ContainerをBuildする。
  • ContainerをregistryにPushする。

ContainerのBuildにはDockerfileを用いる。詳解はしないが、例えばjavaのアプリケーションを梱包したDockerのContainerイメージを作成するには、まず以下のようなDockerfileを作成する。

FROM fabric8/java-centos-openjdk8-jdk:1.5
ENV JAVA_APP_DIR=/deployments
EXPOSE 8080 8778 9779
COPY maven /deployments/

これを以下のbuildコマンドでDockerのContainerイメージを作成する。

[vagrant@base build]$ docker build -t kenchaaan/dockerapp .
Sending build context to Docker daemon  16.68MB
Step 1/4 : FROM fabric8/java-centos-openjdk8-jdk:1.5
 ---> 8a328b3d3aa8
Step 2/4 : ENV JAVA_APP_DIR=/deployments
 ---> Using cache
 ---> 50f4cae3cd9c
Step 3/4 : EXPOSE 8080 8778 9779
 ---> Using cache
 ---> 814a42ccd82b
Step 4/4 : COPY maven /deployments/
 ---> 0bc9ced02477
Successfully built 0bc9ced02477
Successfully tagged kenchaaan/dockerapp:latest

イメージができあがる。

[vagrant@base build]$ docker images
REPOSITORY                         TAG                 IMAGE ID            CREATED             SIZE
kenchaaan/dockerapp                latest              0bc9ced02477        9 seconds ago       434MB

これをregistryにpushする。registryというのはGitHubのContainerイメージ版といったもので、作ったContainerイメージはここに保存する。有名なregistryはDocker Hubなど。例えばNginxやRedisのContainerイメージも置いてある。Nginxがrpmを提供するようなかんじで、ほとんどのソフトウェアがすでにContainerイメージを準備してくれている。

[vagrant@base build]$ docker push kenchaaan/dockerapp
The push refers to repository [docker.io/kenchaaan/dockerapp]
2c2915e2962a: Pushed
7aa6c4d600b7: Mounted from kenchaaan/myjava
f5c2b7b376f1: Mounted from kenchaaan/myjava
4e3206c5f5d6: Mounted from kenchaaan/myjava
3e51ff6f922f: Mounted from kenchaaan/myjava
b19c5338c05c: Mounted from kenchaaan/myjava
d930d319e704: Mounted from kenchaaan/myjava
9f59ad7680b3: Mounted from kenchaaan/myjava
f1bcfa48d09e: Mounted from kenchaaan/myjava
f4796ab1c9c9: Mounted from kenchaaan/myjava
latest: digest: sha256:c7bd45346655c3a297d2a907f153c6a979f408e35511fc84f86346c32a2eb380 size: 2409

するとこのようにregistry上にpushされたことが確認できる。

https://cloud.docker.com/repository/docker/kenchaaan/dockerapp

以上がContainerのbuildとpushだ。これをやれば、例えば別の環境で作成したContainerを動かしたければ、以下のようにrunコマンドを実行すればよい。

docker run -d -p 8080:8080 kenchaaan/dockerapp

上の例は最も原始的な操作かもしれない。実際は自動ビルドツールを使って開発を行うことが主となるだろう。例えばDockerfileを記述する手間を省くためのmaven-pluginも存在するし(fabric8)、JenkinsのようなContainerのCI/CDツールは沢山ある(例えばJenkins Xなど)。開発者はGitHubにコードをpushするだけで、勝手にContainerが作成され、リリースまでやってくれる。

設計

Containerの良さは起動の速さや軽量さにあることを説明した。従って、Containerを自分で作るときは、軽量さを意識しなければ、Containerの良さを引き出せない。

Containerを軽量にしたいときにもっともシンプルな考え付く方法は、アプリケーションをいれないことだ。全くアプリケーションをいれなければ意味がないので、Container作成で言われているよい設計は「一つのContainerにつき、小さなアプリケーション一つ」という考え方だ。

また、Containerは動かしているときにログインして設定を変えることを想定していない。先のデモ、psやviなどの基本的なコマンドがないContainerもあることがそれを示唆している。Containerをアップデートするときは、Containerを一度停止、removeして、再度デプロイしなおす、という運用が想定されている。

Containerの出現により、設計のあり方もこれまでと変わる。これはmicroserviceアーキテクチャとして、別途記載する。

Dockerを取り巻く状況

色々な企業がDockerをいれている。例えばPaizaという、オンラインプログラミングサイトは、問題に対してユーザが回答したコードを評価するための実装として、Dockerを活用している -> https://www.slideshare.net/paiza_official/paizadocker

以前は金融でDockerを使うなんて狂気の沙汰だといわれていたこともあった。けれど最近ではvisaもDockerを使い始めた。https://www.publickey1.jp/blog/17/visadockerdockercon_2017.html

Dockerを使うことによって新たにでてくる問題というのも多く語られている。「docker デメリット」とかでググるとよい。

Dockerだけではできないこと

Dockerだけでは以下のことをすることができない、あるいはこれをするために自分で仕組みを作らなければならない:

  • Containerのローリングアップデート
  • Containerを動かすホストのリソース管理、スケジューリング
  • Containerの死活監視と、セルフヒーリング(自動復旧)
  • ...

ここでKubernetesの登場である。

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