Sasaki Peter

pharmaceutical programmer

Dockerの使い方

2019-03-26 sasaki peterdocker

Docker

docker に入門したときのメモ。

$ docker run hello-world

DockerHubからdockerイメージを取得して、ローカルに保存。のちにdockerイメージからコンテナを起動するコマンド。

runコマンドはpullcreatestartをまとめたもの

$ docker run -it ubuntu bash

タグ指定の方法

$ docker run hello-world:latest

Dockerイメージは読み取り専用らしい

イメージは軽量に作成する

CentOSのベースイメージにルビーのレイヤーを加えるとか、そんな感じに継承してレイヤーを追加していく。

$ docker run docker/whalesay cowsay Hello!!

これで、持っているイメージの一覧が取得できる。

$ docker images

イメージに任意の名前をつけることができる。

aliasみたいなもの

$ docker tag docker/whalesay my_whalesay:ver1

詳細情報を表示する

$ docker inspect my_whalesay

イメージを削除する(コンテナは起動したまま?)

$ docker rmi docker/whalesay
$ docker rmi -f docker/whalesay

同じ中身のイメージを複数削除するときは、ID指定するといい。

$ docker rmi -f 9f5550ca0c41

また取得する

$ docker pull docker/whalesay

imageを作成する

$ docker build -t docker-whale .

カレントディレクトリに存在するDockerfileからdocker-whaleという名前でイメージを作成する。

FROM docker/whalesay:latest↲
RUN apt-get -y update; apt-get install -y fortunes↲
CMD /usr/games/fortune | cowsay↲

docker/whalesayubuntu:14.04をベースイメージとして作成されているため、apt-getが使えるというわけ。

$ doker build --no-cache -t docker-whale .

imageをDockerHubにあげる

GitHubみたいにssh通信ではなく、最初にログインする。

$ docker login

DockerHubアカウントのレポジトリ名と同じイメージ名にする。

$ docker tag docker-whale sasakipeter/docker-whale:latest

そうすると、pushコマンドで単純にイメージ名を引数にとってあげれば、DockerHub上にあげることができる。

$ docker push sasakipeter/docker-whale:ver1

いまだによくわかっていないこと

$ docker ps

上のコマンドで、コンテナの一覧が見られるらしい。

そのコンテナは、imagesからcreateコマンドで作成できるはずなのに、なぜかうまくいかない。

runコマンドでしか、コンテナを起動できないのが現状。

Nginxを使ったWebサーバーの構築

$ docker run --name test-nginx -d -p 8080:80 nginx

-dオプションはdetach

screenコマンドで見るやつと同じで、バックグランドで実行するということ。

つまり、screenしてからyarn startするのと同じ感覚

localhost:8080にnginxのサーバーが立ち上がっている。仮想マシンの8080にアクセスすると、nginxコンテナの80に転送されるらしい。

PC
仮想マシン
DockerEngine
Port80->localhost:8080
localhost:8000->Port80
Webブラウザ
nginxコンテナ

だいたいこんな感じ。

$ docker stop test-nginx
$ docker rm test-nginx

コンテナはストップしたら、docker psには表示されないけれども、コンテナは存在している状態になる。removeしないと、同名のコンテナは作成できない。

COPY命令がうんたんのところにも書いたけど、--rmオプションを使うことによって、コンテナ停止と同時に、コンテナを削除することができるらしい。

コンテナ一括削除

$ docker rm `docker ps -aq`

バインドマウントを使用する

バインドマウント is なに?

自分のこのPC(ホスト)上のファイルと、仮想環境のLinuxの上に乗っかってるnginxをバインディングしてマウントしているってことやなきっと。

下のコマンドで/usr/share/nginx/htmlってやって、バインド、すなわちファイルを転送しているけど、これは仮想環境上の場所(コンテナ内部)で、cdでアクセスしても見つからないことに注意。

$ docker run --name first-nginx -v /Users/sasakipeter/Documents/mycode/Study/Docker/docker-tutorial/html:/usr/share/nginx/html:ro -d -p 8080:80 nginx

-vオプションはvolumeのvらしい。 roはread onlyの略

Webサーバーって聞いて、Linuxサーバーとかそういうのを想像ちゃったけど、それは違くて、WebサーバーっていうのはApacheとかNginxとかそういうミドルウェアのことを言うんだなと気づいた。

こいつらはHTTPリクエストを投げるとレスポンスを返すやつ。

それすなわち、HTTPリクエストが来ると、ブラウザに静的なページを表示させたり、POSTを処理したりしているってこと。多分。

両者の大きな違いは、Apacheはマルチプロセスモデルで、メモリがすぐにいっぱいになりやすいけど、Nginxはシングルスレッドモデルで、イベントドリブンだから重い処理くると止まるけど、そうじゃなければほぼ同時に大量に捌ける。

COPY命令ADD命令

$ docker run --name tmp-nginx --rm -d nginx

この--rmオプションを使うことで、コンテナが停止した時に、コンテナの削除をしてくれる。

ただの感想だけど、イメージからコンテナを作成するって聞いてたけど、どこからともなくコンテナ起動したりしてて、関連性がよくわからないな。

って思ったけど、よくよく考えた毎回runコマンド使ってるし、その中でイメージからコンテナ作成しているんだろうなきっと。

今現状分からない、気になること

  • createコマンド、startコマンドを使って、純粋にdocker-whaleを実行したい。

コンテナ内にホスト上のファイルをコピーしたり、その逆をしたりすることができる。使い方は普通のcpコマンドとほぼ同じ。`scp`コマンドみたく、コンテナの名前を頭につけて:でつなぐだけ。

$ docker cp tmp-nginx:/etc/nginx/conf.d/default.conf ./

このコンフィグファイルにはどのポートでnginxをlistenするかとか書いてある。 そのlistenの項目が80になっているのを8080に変更してDockerfileを作成する。

FROM nginx:latest
COPY default.conf /etc/nginx/conf.d/default.conf

こうすれば、nginxの設定を変え、ポートが8080になったイメージを作成することができる。

$ docker build -t nginx:ver1 .

このDocker imageをもとに、コンテナを作成、起動する。

$ docker run --name web -p 8080:8080 --rm nginx:ver1

コンテナのライフサイクル

$ docker create --name status-test -it alpine /bin/sh

itオプションはよく分からんが、ないとすぐにシェルが終了してしまうらしい。

iオプションは標準出力で渡すとかなんとか。

pullしてないのでalpineイメージ持ってないからコンテナ作成できなくねって思うけど、勝手にpullし始めた。

起動していないコンテナを見ることができた!!!

$ docker ps -a

めっちゃコンテナでてきて焦るやつ←

$ docker start status-test

こうしてコンテナを起動すると、ステータスがUp int minutesみたいになる。

コンテナが作成されて使われていないときはCreatedになる。さらに、ポーズさせるとPausedとなる。

$ docker pause status-test

unpauseコマンドで解除できる。

$ docker unpause status-test

stopコマンドでコンテナを停止させると、statusはExitになる。

停止中のコンテナはstartコマンドで再起動できる。

コンテナの削除は、コンテナを停止してからrmすればよく、停止が面倒なら-fで強制的に削除することもできる。

気づいたけど、コンテナ作成するとき、それはcreate以外にもrunのときも、--nameオプションでコンテナの名前を指定しないと、アンダースコアでつながれたよく分からない名前を勝手につけられる。なんなんだ、この名前は?

コンテナのシェルへの接続

$ docker attach container

こうすれば、コンテナ内で作業できるはず。

デタッチはctrl + p + ctrl + qすればいい。

また、コンテナ起動時に、/bin/bashを引数に投げることで、最初からアタッチした状態で、起動することができる。

$ docker exec -it container /bin/bash

execコマンド自体は、コンテナ内でコマンドを実行するコマンドらしい。

$ heroku run --app hoge ls

みたいなノリか?

こっちなら、exitで抜けても、コンテナは停止しないらしい。

exec -itしてexitで抜けるか、attachしてctrl + p qで抜けるかってだけ。

どっちでも、デタッチしたことになって、コンテナは起動したままになる。

テスト

$ docker run --name connect-test -it -d ubuntu /bin/bash
$ docker attach connect-test
$ docker exec -it connect-test /bin/bash

Docker コミット

コンテナの状態からイメージを作成する。

$ docker commit

通常はDockerfileを使ってイメージを作るのがいい。

何操作したかわからんから。

docker historyコマンドっていうのがあるらしい。

$ docker history commit-test:ver2

みたいにすると、履歴が見られる。

テスト

$ docker run --name commit-test -it -d --rm ubuntu /bin/bash
$ docker exec -it commit-test /bin/bash
$ cd tmp
$ dd if=/dev/zero of=tempfile bs=1M count=10
$ exit
$ docker commit commit-test commit-test:ver2
$ docker run -it commit-test:ver2 /bin/bash

コンテナのリンク機能

リンクオプションはレガシーらしい。

起動中のコンテナに対して操作するオプション?

エイリアスでリンク先に通信できるようになる。

テスト

こんな感じのものを作る

PC
仮想マシン
Port80
link
localhost:8080
reverse-proxyコンテナ
Webブラウザ
static-siteコンテナ

reverse-proxyはnginxサーバー

$ pwd
.../reverse-proxy
$ less reverse_proxy.conf
server {
    listen 8080;
    server_name localhost;

    location / {
        proxy_pass http://ss;
    }
}

localhost:8080にアクセスが来ると、http://ssに転送する設定。

nginxの設定ファイルが置いてあるディレクトリにこの設定ファイルを配置したイメージを作成する。

nginxコンテナの中を走査したところ、このnginxコンテナはDebianベースであることがわかった。

root@4d2505f35bbc:/# cat etc/os-release
PRETTY_NAME="Debian GNU/Linux 9 (stretch)"
NAME="Debian GNU/Linux"
VERSION_ID="9"
VERSION="9 (stretch)"
ID=debian
HOME_URL="https://www.debian.org/"
SUPPORT_URL="https://www.debian.org/support"
BUG_REPORT_URL="https://bugs.debian.org/"

ちなみに、デフォルトの設定はこんな風に書いてあった。

root@4d2505f35bbc:/# cat etc/nginx/conf.d/default.conf
server {
    listen       80;
    server_name  localhost;

    #charset koi8-r;
    #access_log  /var/log/nginx/host.access.log  main;

    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
    }

    #error_page  404              /404.html;

    # redirect server error pages to the static page /50x.html
    #
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }

    # proxy the PHP scripts to Apache listening on 127.0.0.1:80
    #
    #location ~ \.php$ {
    #    proxy_pass   http://127.0.0.1;
    #}

    # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
    #
    #location ~ \.php$ {
    #    root           html;
    #    fastcgi_pass   127.0.0.1:9000;
    #    fastcgi_index  index.php;
    #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
    #    include        fastcgi_params;
    #}

    # deny access to .htaccess files, if Apache's document root
    # concurs with nginx's one
    #
    #location ~ /\.ht {
    #    deny  all;
    #}
}

なるほどって思わせられる記述だった。今までのよくわかっていなかったところがよくわかった。

FROM nginx:latest
COPY /reverse_proxy.conf /etc/nginx/conf.d/reverse_proxy.conf
RUN apt-get update && apt-get install -y inetutils-ping

pingコマンドっていうのがあって、これを使うとネットワークで繋がっている他の機器にIP指定でアクセスできるらしい。これができるパッケージをインストールしたイメージを作成(ビルド)する。

$ docker build -t reverse-proxy .

そこから、コンテナを起動する。前に、static-siteコンテナを起動しないといけないらしい。

$ docker run --name static-site -e AUTHOR="Sasaki Peter" -d dockersamples/static-site

-dはデタッチ-eは環境変数の設定オプション。

このコンテナはnginx上に簡易的な静的サイトを設置したもの。

デフォルトの設定ではリッスンしているポートは80になっている。

そこに、先ほど作成したreverse-proxyコンテナから接続してみる。

まず、コンテナを立ち上げる。

$ docker run --name reverse-proxy -p 8080:8080 --link static-site:ss -d reverse-proxy

8080:8080の左側が、ブラウザ上のポートで、左側がnginxの待ち構えているポートを指している。

そこにアクセスがあった時に、—linkオプションで指定したstatic-siteコンテナに接続しているという設定。

これでlocalhost:8080にアクセスすれば、static-siteにリンクが飛ばされる。

接続できないときは、他のコンテナが起動している可能性があるので停止すればいい。

reverse-proxyコンテナに、execattachで入って、情報を見てみる。

$ docker exec -it reverse-proxy /bin/bash
root@d4a6da714cf8:/# cat /etc/hosts
127.0.0.1	localhost
::1	localhost ip6-localhost ip6-loopback
fe00::0	ip6-localnet
ff00::0	ip6-mcastprefix
ff02::1	ip6-allnodes
ff02::2	ip6-allrouters
172.17.0.4	ss 1e068446a36e static-site
172.17.0.5	d4a6da714cf8
root@d4a6da714cf8:/# env | grep SS_
SS_ENV_NGINX_VERSION=1.9.12-1~jessie
SS_PORT_443_TCP_ADDR=172.17.0.4
SS_PORT_80_TCP=tcp://172.17.0.4:80
SS_PORT_443_TCP=tcp://172.17.0.4:443
SS_ENV_AUTHOR=Sasaki Peter
SS_PORT_80_TCP_PROTO=tcp
SS_PORT=tcp://172.17.0.4:80
SS_PORT_443_TCP_PORT=443
SS_PORT_80_TCP_ADDR=172.17.0.4
SS_NAME=/reverse-proxy/ss
SS_PORT_443_TCP_PROTO=tcp
SS_PORT_80_TCP_PORT=80

なるほどね。面白い。

Automated Build

自動でイメージのビルドをしてくれるサービス。

DockerfileをGithubで管理すると、DockerHub上の紐付けされたイメージが自動でビルドされる。

Herokuとか、Netlifyとかで自動デプロイしてくれるのとおんなじような感じ。

Docker Machineについて

Docker for Windows使う場合

C: docker-machine create create-test
C: docker-machine create --driver hyperv create-test
C: docker-machine ls

hypervの場合は内部のネットワークインターフェイスしか持っていない。

C: docker-machine stop

とかなんとか、大変な作業があるらしいが、WSLでDocker Machine動くようなので、それでいい。

参考

と思ったけど、そうでもないのか?

参考

手元にWindows10のマシンがないので検証できない。

ローカルでDockerHostを管理する

$ docker-machine ls

DockerHostを作成

$ docker-machine create --driver virtualbox default

DockerHostの環境変数を見ることができる。

$ docker-machine env default

Windowsでは

$ docker-machine env default | Invoke-Expression

このコマンドで環境変数を一括で設定する。
$ eval $(docker-machine env default)

こうすることで、作成したDockerHostからdockerコマンドを実行できるようになる。

$ docker run hello-world
$ docker images
$ docker ps -a

今まで使っていたホストとは違うので、さっきまでいじっていたイメージや、コンテナは見当たらない。

$ docker-machine ssh default
   ( '>')
  /) TC (\   Core is distributed with ABSOLUTELY NO WARRANTY.
 (/-_--_-\)           www.tinycorelinux.net

docker@default:~$ docker ps -a
docker@default:~$ exit

今作成したDockerHostのIPアドレスを確認する。

$ docker-machine ip default
$ docker run -d -p 8000:80 nginx

確認したIPアドレスの8000番ポートを見るとnginxのスタート画面が見られる。

このようにIP指定してアクセスすることができるようだ。

起動したDockerHostを停止するには次のようにする。

$ docker-machine stop default

再起動

$ docker-machine start default

DockerHostを解除する

$ docker-machine env -u

これで解除コマンドを確認できる。

DockerHostを解除すると、docker-machine ls上には作成したDockerHostは表示されているが、動作はしていないため、docker ps -a等のコマンドを打ち込んでも、DockerHost上で作成したコンテナは確認できず、その前に作成したものが確認できた。

AWSで遊ぶ

AWSにDockerHostを作成する。

アクセスキーを設定する

$ mkcd ~/.aws
$ vi credentials
[default]
aws_access_key_id = xxxxxxx
aws_secret_access_key = xxxxxx

こんな感じにしておくと、AWSにアクセスする際に、自動的に参照してくれるらしい。

AWSのEC2を使う。

$ docker-machine create --driver amazonec2 --amazonec2-open-port 8000 --amazonec2-region ap-northeast-1 aws-sandbox

なぜかEC2のGUIを確認しても、インスタンスが確認できない。

$ eval $(docker-machine env aws-sandbox)
$ docker run -d -p 8000:80 --name webserver kitematic/hello-world-nginx

こうすると、awsのIPの8000ポートにサイトが立ち上がるが、別にどうということはない。

$ docker-machine stop aws-sandbox
$ docker-machine rm aws-sandbox

AWSはインスタンスを立ち上げている時間で課金されるらしい。

GCP

Docker Compose

Docker-compose.yml

version: '3'
services:
    web:
        build: .
        ports:
        - "5000:5000"
        - logvolume01:/var/log
        link:
        - redis
    redis:
        image:redis
volumes:
    logvolume01:{}

2つのサービスがある。webとredis

Django

FROM python:3
ENV PYTHONUNBUFFERED 1
RUN mkdir /code
WORKDIR /code
COPY requirements.txt /code/
RUN pip install -r requirements.txt
COPY . /code/

最後に、ローカルのフォルダ全てをコンテナにあげているのが、ローカルで作業しつつ、コンテナ内に実行環境が作れている要因。

Django==2.0
psycopg2

Docker-compose

version: '3'
services:
    db:
        image: postgres
    web:
        build: .
        command: python3 manage.py runserver 0.0.0.0:8000
        volumes:
            - .:/code
        ports:
            - "8000:8000"
        depends_on:
            - db
$ docker-compose run web django-admin.py startproject examplepj .

これ、ローカルに環境作る方法やん。

Settings.py

DATABASES = {
    'default':{
    	'ENGINE': 'django.db.backends.postgresql',
    	'NAME': 'postgres',
    	'USER': 'postgres',
    	'HOST': 'db', # service name
    	'PORT': 5432, # postgresql default
	}
}
$ docker-compose up -d

コンテナからコマンドを実行する

$ docker-compose run web python3 manage.py startapp polls

viewとか記述したら、勝手に反映されている。

コンテナを停止

$ docker-compose stop

バインドマウント

version: '3'
services:
    db:
        image: postgres
        volumes:
        - pgdatavol:/var/lib/postgresql/data
    web:
        build: .
        command: python3 manage.py runserver 0.0.0.0:8000
        volumes:
            - .:/code
        ports:
            - "8000:8000"
        depends_on:
            - db
volumes:
    pgdatavol:

コンテナ削除

$ docker-compose down -v
$ docker-compose ps
$ docker-compose start