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

コンテナ技術 ハンズオン Vol.2 Dockerイメージ編

本題の前に


位置付け・前提

本記事は、社内ワークショップ のために用意したものです。そのため、次の前提で進めることをご理解ください。

また、本稿はこちらの続きとなります。

コンテナ技術 ハンズオン Vol.1 Docker操作編

目的や前提なども上記の内容に準拠します。


docker ハンズオン環境

今回も Play with Docker を活用してハンズオンを行います。

playwithdocker.png

※要:Docker Hub アカウント

注意点として、この環境は4時間の制限があります。4時間経過すると環境が削除されてしまう点を注意して下さい。

もちろんローカル環境でも構いません。


参加者の前提知識

Dockerfile および Docker レイヤーの考え方を理解していることが前提とします。参考までに下記の資料を一読しておくことをお奨めします。

Dockerfile を書くためのベストプラクティス解説編

HTMLページが良ければ下記でもOKです。

Dockerfile のベストプラクティス — Docker-docs-ja 1.9.0b ドキュメント


ハンズオンの前に


前回の問題

前回のハンズオンで最後に出した問題はこちら。

  1. 起動しているコンテナはホストのどのディレクトリにあるでしょうか。
  2. dockerイメージはどこにあるでしょうか。
  3. dockerのプロセスはどれか。

うまく ググれカス 答えられましたでしょうか。


前回の問題の解答

解答1. コンテナの実態ディレクトリの調べ方はこちら。

$ docker inspect {NAME|ID}

このコマンドを実行して、表示されるJSONデータのうち、下記の該当箇所を確認すればわかります。

"HostnamePath": "/var/lib/docker/containers/8c059e84473033b3a0474b8a9a4beb8f848f003492d87ba6865f93863ce889b2/hostname",
"HostsPath": "/var/lib/docker/containers/8c059e84473033b3a0474b8a9a4beb8f848f003492d87ba6865f93863ce889b2/hosts",
"LogPath": "/var/lib/docker/containers/8c059e84473033b3a0474b8a9a4beb8f848f003492d87ba6865f93863ce889b2/8c059e84473033b3a0474b8a9a4beb8f848f003492d87ba6865f93863ce889b2-json.log",

この場合、/var/lib/docker/containers/8c059e84473033b3a0474b8a9a4beb8f848f003492d87ba6865f93863ce889b2 配下が実態のディレクトリとなります。


前回の問題の解答

解答2. イメージの場所は解答1から予想が付きます。

$ ls /var/lib/docker/image
overlay2

しかし、これは外れです。メタデータなどしかありません。

実態のデータはこちらにあります。

/var/lib/docker/overlay2

配下に複雑な文字列のディレクトリがならんでいて、その中に実態となるデータが保存されます。


前回の問題の解答

解答3. 例として、2つインスタンスが起動しているdockerのプロセスを見てみます。

PID   USER     TIME  COMMAND
    8 root      0:14 dockerd
   35 root      0:02 containerd --config /var/run/docker/containerd/containerd.toml --log-level debug
 2860 root      0:00 containerd-shim -namespace moby -workdir /var/lib/docker/containerd/daemon/io.co
 2877 root      0:00 /bin/sh
 7628 root      0:00 containerd-shim -namespace moby -workdir /var/lib/docker/containerd/daemon/io.co
 7648 root      0:00 /bin/bash

PID が 8 と 35 は Docker エンジン自体のプロセスです。

一方、 PID 2866 と 2877、7628 と 7648 は別のインスタンスです。

変な感じですが、OSから直接見たプロセス(の実行コマンド)には差が無いように見えます。

この辺は、今後 学習を進めていく中で疑問が解消できると良いかと思います。


ハンズオン


今回のゴール

  • docker インスタンスから新たなイメージを作成して、そのインスタンスを作成できるようになる。
  • dockerfile から新たなイメージを作成すること
  • dockerコンテナ同士を連携させること

今回やらないこと

  • コンテナ/イメージを分析したり、たくさん操作したりはしません。
  • kubernetes / docker-compose のようなオーケストレーションツールの使いません。

これから進める中で必ず理解しておいて欲しいこと

Docker イメージはレイヤーになっている

Dockerコンテナ実行とは、イメージ内のプログラムを実行

この概念は、イメージを作成していく上で重要な概念となります。理解できていない人は、上の画像をクリックして該当箇所を学習した方が良いでしょう。


イメージ・レイヤ作成


インスタンスからイメージ・レイヤを作成する

Docker イメージ・レイヤを作成するイメージとコマンドは下記です。

Dockerイメージを作る"docker commit"

利用したコンテナからイメージを生成します。

docker commit {コンテナ名/インスタンス名} {イメージ名}

このとき、実態としてイメージからコンテナ化した後の作業・設定などを反映した状態がイメージとして保存されます。


docker commit ハンズオン

下記の通り、docker コンテナを作成および操作します。※コメントは無視して入力してください。

# 軽量Linux "Alpine Linux"の最新版コンテナを pull して、起動(run)する
docker run -it --name alpine-linux-test alpine:latest

# パッケージ群を最新化する
apk update

# 開発に便利なパッケージを追加する
apk --update add python3 python3-dev zsh vim tmux git tig wget

# Pythonのパッケージ管理ツールを最新化する
pip3 install --upgrade pip

# Pythonでよく使うモジュールをインストールする
pip install flask requests

# Alpineパッケージのキャッシュデータを削除する(軽量化)
rm -rf /var/cache/apk/*

ここまで実行したら、Ctrl-p Ctrl-q でデタッチして、ホストのシェルに戻ってインスタンス状態を確認します。

$ docker ps      
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
d813f16ccdf5        alpine              "/bin/sh"           7 minutes ago       Up 7 minutes                            alpine-linux-test

この状態のインスタンスから、現時点のイメージを生成します。

docker commit alpine-linux-test mydev-tools

そうした上で、Docker イメージの状態を確認します。

$ docker images
REPOSITORY       TAG        IMAGE ID            CREATED             SIZE
mydev-tools      latest     50ad99bbf7b5        34 seconds ago      158MB

このまま、新たにインスタンスを生成して、今度はバックグラウンド起動します。

$ docker run -it -d --name dev-env1 mydev-tools
4d68811389f344265f82c923d00d264524b14fb507d42b199b241be6f1f73ec3

再度インスタンスの一覧を表示します。

$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
589bdf4b45c4        mydev-tools         "/bin/sh"           15 seconds ago      Up 14 seconds                           dev-env1
d813f16ccdf5        alpine              "/bin/sh"           14 minutes ago      Up 14 minutes                           alpine-linux-test

これでインスタンスを複製することに成功しました。
NAMES値だけ変えれば大量のインスタンスを短時間に生成することも可能です。


補足:docker commit コマンド

$ docker commit --help 

Usage:  docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]

Create a new image from a container's changes

Options:
  -a, --author string    Author (e.g., "John Hannibal Smith <hannibal@a-team.com>")
  -c, --change list      Apply Dockerfile instruction to the created image
  -m, --message string   Commit message
  -p, --pause            Pause container during commit (default true)

実際には、-a で作成者を、-m で作成時の記録を残すのが実用的ですね。


dockerイメージを別サーバへ移す

別のサーバへ移行して、コンテナを動作させたい場合があるはずです。

そのような場合、下記の手順で実行します。

  1. dockerインスタンスを export する
  2. exportした1のデータを移行先に移動/複製する
  3. 新たなイメージとして、2で配置したデータを import する
  4. import したイメージからコンテナを生成・起動する

export / import の書式はこちらです。

docker export [Container ID | NAMES] -o [出力ファイル名].tar
docker import [ファイル名|URL] [REPOSITORY[:TAG]]
---

### docker export / import ハンズオン

ここでは、「docker commit ハンズオン」で生成したイメージを export / import してみます。

まず、イメージを export します。

```shell
docker export dev-env1 -o dev-env.tar
---

本来であれば、scp や sftp などでサーバ間でデータを移動させるはずですが、今回は同じ docker 内で実施します。

次に、異なる名前でイメージとしてインポートします。

```shell
docker import dev-env.tar dev-env-copy
---

再度イメージを確認すると、下記のとおりにインポートに成功しています。

```shell
$ docker images
REPOSITORY       TAG        IMAGE ID            CREATED             SIZE
dev-env-copy     latest     98d5d7aeebb1        10 seconds ago      158MB
mydev-tools      latest     50ad99bbf7b5        34 minutes ago      158MB

ここからインスタンスを生成すれば、そのまま異なるサーバの docker で同じ内容のインスタンスを動作させることが可能です。


補足:docker export コマンド

$ docker export --help

Usage:  docker export [OPTIONS] CONTAINER

Export a container's filesystem as a tar archive

Options:
  -o, --output string   Write to a file, instead of STDOUT

実際には、-o 以外は指定するものがほぼありません。


補足:docker import コマンド

$ docker import --help

Usage:  docker import [OPTIONS] file|URL|- [REPOSITORY[:TAG]]

Import the contents from a tarball to create a filesystem image

Options:
  -c, --change list      Apply Dockerfile instruction to the created image
  -m, --message string   Set commit message for imported image

オプション無しでも十分に動作しますが、実用的には -m で記録を残すのが良いでしょう。


補足:docker save コマンド

$ docker save --help  

Usage:  docker save [OPTIONS] IMAGE [IMAGE...]

Save one or more images to a tar archive (streamed to STDOUT by default)

Options:
  -o, --output string   Write to a file, instead of STDOUT

こちらの記事によると、export との違いはこちらだそうです。

・save 上記のレイヤーやタグといったメタ情報含めてコンテナをtarでまとめる。
・export ファイルシステムを愚直にtarでまとめ、メタ情報は無視される。

また、上記の主語として、save はイメージ、export はインスタンス(コンテナ)が対象になります。

通常は export の方が容量が少なくて良いですが、より正確さ(再現性?)を求めるなら save が良さそうです。


補足:docker load コマンド

$ docker load --help

Usage:  docker load [OPTIONS]

Load an image from a tar archive or STDIN

Options:
  -i, --input string   Read from tar archive file, instead of STDIN
  -q, --quiet          Suppress the load output

save したデータは load コマンドでしかインポートできません。


練習問題

どれかインスタンスを save した後、
同じホストの docker で load すると、
どのようなことが起こるか確認してください。


イメージを設計書から作る


dockerfile = イメージの設計書

dockerfile とは、下記のように説明されています。

Docker Image は Dockerfile というファイルを記述し、そのファイルを元にビルドすることでスナップショットの作成ができます。

dockerfile のイメージ

引用元: dockerfile - 入門 Docker

つまり、端的にはイメージを作る元ファイルと考えましょう。


dockerfile の書き方

サンプルとして、すべての命令を書いた dockerfile をご覧ください。

dockerfile
# ベースとなるイメージを指定する
# ローカルになければ Docker Hub から Pull
FROM ubuntu:latest

# 生成するイメージの作者を設定する
MAINTAINER syamane

# apt で最新化後、vim をインストールコマンドを実行する(シェル方式)
RUN apt update && apt install -y vim

# vim の設定ファイルを作成(JSON形式で指定)
RUN ["/bin/bash", "-c", "echo", "set", "nowrap", ">", "/root/.vimrc"]

# リッスンするポートを指定する
EXPOSE 5900

# 環境変数を設定する
ENV PATH=/root:$PATH

# 命令実行時の作業ディレクトリを指定する
WORKDIR /root

# ローカルファイルをコンテナ内にコピーする(今回はコメントアウト)
# ダウンロード元URL もしくは 実行ディレクトリ配下のパスを指定可
# 圧縮ファイルを指定すれば自動解凍する
# ADD *.txt /root/

# ローカルファイルをコンテナ内にコピーする(今回はコメントアウト)
# 実行ディレクトリ配下のみ指定可
# COPY *.log /root/

# vim をヘルプが開いた状態でフォアグラウンド起動する
# シェルを指定すれば、シェルでの操作ができる
CMD ["vim", "-c", "help"]

他にも VOLUME / LABEL / USER / ARG / ONBUILD / STOPSIGNAL / HEALTHCHECK / SHELL がありますが、書かなくてもイメージは作れます。(もちろん書けた方が良いですが)

各命令の詳細な解説は、下記を参考にしましょう。
* Dockerfile リファレンス — Docker-docs-ja 1.9.0b ドキュメント
* Dockerfile のベストプラクティス — Docker-docs-ja 1.9.0b ドキュメント
* Dockerfile を書くためのベストプラクティス解説編


dockerイメージのビルド

dockerfile と イメージに ADD/COPY したいファイルがあるディレクトリで、下記のコマンドを実行します。

docker build -t testimage:1.0 .

この書式は下記の通りです。

docker build [-t {イメージ名}[:{タグ名}]] {Dockerfileのあるディレクトリ}

最後のディレクトリ指定ですが、「.」つまりカレントディレクトリを指定することが多いです。

このコマンドを実行して、エラーが出なければ下記のように成功を示すメッセージを出力して完了です。

$ docker build -t test:1.0 .
Sending build context to Docker daemon  2.048kB
Step 1/2 : FROM alpine:latest
 ---> 961769676411
Step 2/2 : CMD echo hello
 ---> Running in e1c85ea06aba
Removing intermediate container e1c85ea06aba
 ---> 73e67f49d233
Successfully built 73e67f49d233
Successfully tagged test:1.0

$$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
test                1.0                 73e67f49d233        47 seconds ago      5.58MB

dockerfile 書き方あるある


docker build や docker run が上手くいかない場合は?

下記の記事を参考に問題を調査してみましょう。


補足:docker build コマンド

非常に沢山のオプションがあって複雑です。

$ docker build --help

Usage:  docker build [OPTIONS] PATH | URL | -

Build an image from a Dockerfile

Options:
      --add-host list           Add a custom host-to-IP mapping (host:ip)
      --build-arg list          Set build-time variables
      --cache-from strings      Images to consider as cache sources
      --cgroup-parent string    Optional parent cgroup for the container
      --compress                Compress the build context using gzip
      --cpu-period int          Limit the CPU CFS (Completely Fair Scheduler) period
      --cpu-quota int           Limit the CPU CFS (Completely Fair Scheduler) quota
  -c, --cpu-shares int          CPU shares (relative weight)
      --cpuset-cpus string      CPUs in which to allow execution (0-3, 0,1)
      --cpuset-mems string      MEMs in which to allow execution (0-3, 0,1)
      --disable-content-trust   Skip image verification (default true)
  -f, --file string             Name of the Dockerfile (Default is 'PATH/Dockerfile')
      --force-rm                Always remove intermediate containers
      --iidfile string          Write the image ID to the file
      --isolation string        Container isolation technology
      --label list              Set metadata for an image
  -m, --memory bytes            Memory limit
      --memory-swap bytes       Swap limit equal to memory plus swap: '-1' to enable unlimited swap
      --network string          Set the networking mode for the RUN instructions during build (default "default")
      --no-cache                Do not use cache when building the image
      --pull                    Always attempt to pull a newer version of the image
  -q, --quiet                   Suppress the build output and print image ID on success
      --rm                      Remove intermediate containers after a successful build (default true)
      --security-opt strings    Security options
      --shm-size bytes          Size of /dev/shm
  -t, --tag list                Name and optionally a tag in the 'name:tag' format
      --target string           Set the target build stage to build.
      --ulimit ulimit           Ulimit options (default [])

単純にイメージを生成する以外に、リソースを制御することも可能です。


dockerfile からイメージを作るハンズオン

「docker commit ハンズオン」で作成したコンテナと同じイメージを作る dockerfile は下記です。

Dockerfile
FROM alpine:latest
CMD apk update \
 && apk --update add python3 python3-dev zsh vim tmux git tig wget \
 && pip3 install --upgrade pip \
 && pip install flask requests \
CMD rm -rf /var/cache/apk/*
COPY test.txt /test.txt

なお、イメージ内にホスト上のファイルを追加する記述をいれましたので、そのファイルを作成します。

test.txt
DOCKER HANDSON TEST

先程の dockerfile と test.txt を同じディレクトリに配置して、ビルドします。

docker build -t dev-tools-img .

実行するとこんな感じです。

Sending build context to Docker daemon  3.072kB
Step 1/5 : FROM alpine:latest
 ---> 961769676411
Step 2/5 : CMD apk update  && apk --update add python3 python3-dev zsh vim tmux git tig wget  && pip3 install --upgrade pip  && pip install flask requests CMD rm -rf /var/cache/apk/*
 ---> Running in 8e625e558d82
Removing intermediate container 8e625e558d82
 ---> eb9b1153bdd2
Step 3/5 : COPY test.txt /tmp/
 ---> 90ff3bf6c49c
Step 4/5 : CMD cat /test.txt
 ---> Running in 297faf85f943
Removing intermediate container 297faf85f943
 ---> 24bce8916152
Step 5/5 : CMD echo "Complete!"
 ---> Running in 93d43c43f9a9
Removing intermediate container 93d43c43f9a9
 ---> 600d60d001e1
Successfully built 600d60d001e1
Successfully tagged dev-tools-img:latest

実際にインスタンスを作ってみましょう。

$ docker run --name dev-env3 dev-tools-img
$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
7b29bbf49e46        mydev-tools         "/bin/sh"           29 minutes ago      Up 29 minutes                           dev-env1

これでオリジナルのイメージを作成して、そこからインスタンスを生成することができました。

あとは、どんな dockerfile を書くかにかかっているわけです。


dockerfile ハンズオン


dockerfile に慣れるハンズオン

とにかく沢山の Dockerfile からイメージを作って、コンテナを作成してみましょう。


意味のない ubuntu イメージ

Dockerfile
FROM ubuntu
COPY hello.txt /tmp/hello.txt
CMD ["cat", "/tmp/hello.txt"]

もちろんビルドします。

docker build -t hello .

出典:dockerfile - 入門 Docker


サービスをいくつか追加した ubuntu イメージ

Dockerfile
FROM ubuntu
RUN apt-get update && apt-get install -y inotify-tools nginx apache2 openssh-server

今回は dockerfile を指定してみます。

docker build -t build:sample -f dockerfile .

追加しただけで、普通のOSと同じ状態でサービス起動は設定してません。

出典:Dockerfileを使った基本的なDockerの動作 - デベロッパー・コラボ


ApacheでHTTPサーバを実行する。

Dockerfile
FROM centos
MAINTAINER Admin <admin@admin.com>
CMD echo "now running..."
EXPOSE 80
CMD ["/usr/sbin/httpd", "-D", "FOREGROUND"]

※元の内容を一部改変しました。

ビルドします。

docker build -t samplehttp .

実際にインスタンスを作ってみます。

docker run --name ap1 -d -p 80:80 samplehttp

80番ポートにアクセスして、動作できればコンテナを停止しましょう。

出典:Dockerfileを書いてみる - Qiita


ip-api.com へのリバースプロキシ

Dockerfile
FROM centos:6
RUN set -x && \
    yum install -y epel-release && \
    yum install -y nginx && \
    sed -i -e "s/index  index.html index.htm/proxy_pass http:\/\/ip-api.com\/json/" \
        /etc/nginx/conf.d/default.conf && \
    ln -sf /dev/stdout /var/log/nginx/access.log && \
    ln -sf /dev/stderr /var/log/nginx/error.log
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

キチンとビルドします。

docker build -t reverse-proxy .

実際に走らせてみましょう。

docker run --name rp1 -d -p 80:80 reverse-proxy

80番ポートにアクセスして、動作できればコンテナを停止しましょう。(前回のキャッシュが残っていないかは要注意)

出典:効率的に安全な Dockerfile を作るには - Qiita


複雑な nginx サーバーのイメージ

下記の2つのファイルを作成します。

default.conf
server {
        listen 8080 default_server;
        listen [::]:8080 default_server;

        root /app;

        location / {
        }
}
index.html
<html>
<body>
<h1>TEST PAGE</h1>
sample index file
</body>
</html>
Dockerfile
FROM alpine:3.6

# nginxのインストール
RUN apk update && \
    apk add --no-cache nginx

# ドキュメントルート
ADD app /app
ADD default.conf /etc/nginx/conf.d/default.conf

# ポート設定
EXPOSE 8080

RUN mkdir -p /run/nginx

# フォアグラウンドでnginx実行
CMD nginx -g "daemon off;"

ビルドします。

docker build -t alp_nginx .

インスタンスを作って、実行してみます。

docker run -it --name nginx2 -p 80:8080 alp_nginx

80番ポートに接続して、動作できればコンテナを停止しましょう。(前回のキャッシュが残っていないかは要注意)

出典:お気楽にnginxのDockerイメージを作ってみる - Qiita


実用的な開発環境のイメージ

下記の記事の内容を dockerfile で用意してみます。

Dockerでサクッと使い捨ての開発環境を用意する | DevelopersIO

Dockerfile
FROM ubuntu:latest
RUN apt update -y \
 && apt install curl vim git net-tools build-essential -y
RUN curl -sL https://deb.nodesource.com/setup_12.x | bash - \
 && apt install nodejs -y \
 && node -v
RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -
RUN echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list
RUN apt update -y && apt install yarn -y
RUN yarn -v

ビルドします。結構長い時間かかります。

docker build -t dev_container .

インスタンスを作って、実行してみます。

docker run -it --name devcon1 dev_container

無事に開発環境に入れたら、Node.js のバージョンを確認してみます。

node -v

バージョンが表示されたらOKです。


NGINX公式イメージ

GitHubの下記のリポジトリから dockerfile をダウンロードして実行してみます。

nginxinc/docker-nginx: Official NGINX Dockerfiles

git clone https://github.com/nginxinc/docker-nginx.git
cd docker-nginx/stable/alpine/
docker build -t ngx1 .

あとはインスタンスを作って、実行してみます。

docker run -it -d --name n1 -p 80:80 ngx1

無事に80番ポートにアクセスできたらOKです。

このように、GitHub にはたくさんの Dockerfile が公開されています。ぜひ参考にしてみて下さい。

Search · Dockerfile - GitHub


問題(宿題)

CentOS を最新状態にアップデートして、nginx をインストール&起動した状態のDockerイメージ「nginx1」を作成してください。

また、完了した後は下記を調べて、後始末をしましょう。

  1. 起動しているインスタンスを一括停止するコマンドは?
  2. 起動しているインスタンスを一括削除するコマンドは?
  3. 起動しているイメージを一括削除するコマンドは?

さらに Dockerfile 力をレベルアップするためには

下記のようなコンテンツを読むと良いかもしれません。


おわりに

今回は docker イメージを作成する方法について、ハンズオンをしました。

ここまで自由にイメージが作れるようになると、だんだん楽しくなってきたのではないでしょうか。

こうして作成したイメージから生成するコンテナの真骨頂は、大量のコンテナ操作定型的な運用操作の効率化にあります。今後はDocker-compose や Kubernetes といったツールを学んでいきたいところですが、

なお、次はコンテナを管理していく方法について、ハンズオンを行っていきます。


参考

これまでに紹介した記事以外に、本ハンズオン資料を作成する上で下記の記事を参考にさせていただきました。

大変ありがとうございました。

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

GithubにGoで書いたDockerをTravisでCIする

あまり他人のソフト使うのが上手くないので、色々とハマりポイントがあったもので
Git履歴よりハマりポイントのおさらい

目指すもの

GithubにTravisCIを連携させることで、PushやPullRequestした時などに
自動的にTestやLintを行い、コードカバレッジを行う事ができる
大手の開発では一般的なスタイルではなかろうか?

個人的に勉強ついでに作っていた SlackBotのプログラムを使って勉強してみた

https://github.com/YukiMiyatake/GOSICK

SlackBotである。プラグインを入れる事で機能拡張を行う事ができる
とりあえず勉強用テストプロジェクトである
これからリファクタリングしたり interface化して、LintなりTestなり色々な改造を行う予定
(現在 gomodule導入テストのためプラグインもecho以外動かないはず)

dockerファイルを修正

今までDockerfileにて go buildして実行ファイルを起動させていたが
TravisやローカルでTestをまわすために
また、コード修正のたびに docker-compose buildしたくないので
Dockerでは何もせず、シェルスクリプトでビルド実行やテストを行うように変更した

Dockerファイル

dockerfile
WORKDIR /build
COPY src .
CMD ./build.sh
CMD ls

CMD ./GOSICK

この辺りを削除した。ぶっちゃけ Goのイメージそのまま使うのでDockerfileは不要で
docker-composeにimage指定するだけでいいけど
今後なにかインストールする可能性もあるので Dockerファイルはのこした

build.sh

今回はプラグインのビルドなども入るので、ワンライナーで書かず
ビルドをシェルスクリプトでわけたかった
(最終的にはMakefile等にしたいなあ)

build.sh
if [ $TEST = "test" ]; then
    cd plugins/echo
    go test -v ./... --buildmode=plugin
    cd ../..

    go test -v ./...
else
    cd plugins/echo
    go build --buildmode=plugin
    cd ../..

    go build
fi

テストと通常ビルドを分けるため $TEST環境変数で分けることにした

Script

docker_app_build
docker-compose up -d
docker-compose exec gosick_app bash -C '/app/build.sh'
docker_app_test
docker-compose up -d
docker-compose exec -e TEST=test gosick_app bash -C '/app/build.sh'

テストと実行でファイルを分けたが、環境変数以外はかわらないので、まとめてもいいなあ
docker-compose up も別にすべきかなあ

等色々考えているが、しばらく使って考える予定

GithubにTravisを連携させバッヂをつける

https://qiita.com/hoshimado/items/4090d8e64beb8a7f95e1

例えばこの辺を参考に、バッヂまでつける

Travisの設定

とっても苦労したTravisの設定
とりあえずはどのリポジトリをPushしても通知が入るようにしている
プロジェクトのルートに .travis.yml ファイルを作成してPush

最初の設定

.travis.yaml
sudo: required

services:
  - docker

before_install:
#  - docker-compose up -d

install:

before_script:

script:
  - scripts/docker_app_run
 # - docker-compose exec golang go test -v ./...

after_script:

notifications:

いろんなサイトをみて上記の設定ファイルを書いたが、色々なエラーが出てダメ
なので色々と

language設定

TravisのページをみるとRubyと判断されていたのでlanguageを設定

.travis.yaml
language: go

ただ、Goのバージョンが古かったのでちゃんとバージョン指定してみた

.travis.yaml
language: go
go: 
  - 1.12.x

GO111MODULE

ログをみていると GO111MODULE=autoになっていた
今回はgo module使いたいので onにしたいので下記のようにかいてみた

.travis.yaml
script:
  - env GO111MODULE=on scripts/docker_app_test

ところがログをみても GO111MODULE=autoのままだった

どうやらenvセクションに書く必要があった

.travis.yaml
env:
  - GO111MODULE=on

go_import_path

githubのアドレスを加えた

.travis.yaml
go_import_path: github.com/YukiMiyatake/GOSICK

スクリプトパーミッション

Windowsで開発を行っていたため、ファイルのパーミッションがおかしくなっていた
Travis内でパーミッションを設定してもいいが、根本的な解決としてスクリプトファイルにパーミッションをあたえPushした

https://qiita.com/maosanhioro/items/aaade68cdca591232159

この辺をみて、パーミッションを正しく設定したら動いた

DockerComposeのバージョンが古い

docker-composeのバージョンが古かったので念のためバージョンアップを行った

まず、TravisのDockerやDockerComposeバージョンは例えば下記で調べられる

.travis.yml
before_install:
  - docker-compose -v
  - docker -v

これによると

$ docker-compose -v
docker-compose version 1.17.1, build 6d101fb
before_install.3
$ docker -v
Docker version 17.09.0-ce, build afdb6d4

Dockerはとりあえず今回はバージョンはいいとします
DockerComposeだけ 1.24.1にバージョン上げます

.travis.yml
env:
  - GO111MODULE=on
  - DOCKER_COMPOSE_VERSION=1.24.1

before_install:
  - sudo rm /usr/local/bin/docker-compose
  - curl -L https://github.com/docker/compose/releases/download/${DOCKER_COMPOSE_VERSION}/docker-compose-`uname -s`-`uname -m` > docker-compose
  - chmod +x docker-compose
  - sudo mv docker-compose /usr/local/bin

Travis公式のドキュメント通りです。

ところが色々不可解なエラーが出ます

Yamlの罠

yamlじたいほとんど書かないのでよくわかってませんが、気になるポイントをみつけた

.travis.yml
env:
  - GO111MODULE=on
  - DOCKER_COMPOSE_VERSION=1.24.1

結論からいうと

GO111MODULE:on
だとGO111MODULEは定義されずautoになる

GO111MODULE: on
だとGO111MODULEはtrueが定義されてしまう

GO111MODULE= on
だとGO111MODULEは空文字が定義される

よって
GO111MODULE=on

以外はダメ

DOCKER_COMPOSE_VERSION = 1.24.1
DOCKER_COMPOSE_VERSION:1.24.1
DOCKER_COMPOSE_VERSION :1.24.1
は共に定義されない

DOCKER_COMPOSE_VERSION: 1.24.1
DOCKER_COMPOSE_VERSION=1.24.1
はOK

:の場合は前にスペースNG、後ろにスペース必須
=の場合は 前も後ろもスペースNG
GO111MODULEには=しか使えない
DOCKER_COMPOSE_VERSIONは上記ルールを守っているなら:でも=でもOK

細かい事はおいといて

.travis.yml
  - GO111MODULE=on
  - DOCKER_COMPOSE_VERSION=1.24.1

とするとちゃんと定義されるようになった

並列実行問題

.travis.yml
env:
  - GO111MODULE=on
  - DOCKER_COMPOSE_VERSION=1.24.1

で両方の環境変数は定義されたが、なぜか2つ実行されているっぽい
その2つの環境変数は

GO111MODULE=on, DOCKER_COMPOSE_VERSION定義なし
GO111MODULE定義なし, DOCKER_COMPOSE_VERSION=1.24.1

の2つ
どう考えても並列実行されている
ドキュメント見た感じだと globalに設定すればよさそう

.travis.yml
env:
  global:
    - GO111MODULE=on
    - DOCKER_COMPOSE_VERSION=1.24.1

これで、すべての実行環境において上記のフラグが立つようになった
今回は使用していないが metrix: に環境変数配列を列挙すると並列で実行できるので
テストの高速化を行う時は使ってみよう
(ただし無料版だとたぶん2並列までしかできない)

DBのbind問題

下記の謎エラーが出て結構悩まされた

ERROR: for gosick_mysql  Cannot start service gosick_mysql: driver failed programming external connectivity on endpoint gosick_mysql (cb76f17f3761214c04ca3c4ca03fe4b993e65b0ca14b76b711606651d40c8a8c): Error starting userland proxy: listen tcp 0.0.0.0:3306: bind: address already in use
ERROR: for gosick_mysql  Cannot start service gosick_mysql: driver failed programming external connectivity on endpoint gosick_mysql (cb76f17f3761214c04ca3c4ca03fe4b993e65b0ca14b76b711606651d40c8a8c): Error starting userland proxy: listen tcp 0.0.0.0:3306: bind: address already in use

BindAddressが使われている。3306なのでMySQLが既に立ち上がっているようだが、いくら見渡してもそれらしいものがない・・
ので困った
MySQLのDockerは今は使ってないので消せば解決はするが、ちゃんと解決させたいので調べたら、まさかの! TravisでMySQLが立ち上がっていた・・・

ので、実行前にTravisのMySQLを落とす

.travis.yml
before_install:
  - sudo /etc/init.d/mysql stop

やった!できた!

まとめ

色々な罠があったが、とりあえずTravisCIを動かすことができた
一通り自分の陥った罠をリストアップしたので、誰かの役にたてば幸いです

なぜかすべてのテストが通ってないので問題は残ってるけど、TravisCIの問題は解決したということで。

最終的な設定ファイル(不要な設定もあるかもしれないけど)

.travis.yml
language: go
go:
  - 1.12.x

go_import_path: github.com/YukiMiyatake/GOSICK

env:
  global:
    - GO111MODULE=on
    - DOCKER_COMPOSE_VERSION=1.24.1

services:
  - docker

before_install:
  - sudo /etc/init.d/mysql stop
  - docker-compose -v
  - docker -v
  - sudo rm /usr/local/bin/docker-compose
  - curl -L https://github.com/docker/compose/releases/download/${DOCKER_COMPOSE_VERSION}/docker-compose-`uname -s`-`uname -m` > docker-compose
  - chmod +x docker-compose
  - sudo mv docker-compose /usr/local/bin
  - docker-compose build

install:

before_script:

script:
  - scripts/docker_app_test

after_script:

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

HRRを使うHaskellアプリケーションのDockerコンテナを作る

やりたいこと

haskellで実装したdb依存のアプリケーションをdockerコンテナ化したい。

haskellのdbライブラリにhaskell-relational-record (HRR)がある。そこそこ柔軟にクエリを組み立てられる上、コンパイル時にdbからテーブル情報を取得して自動的にデータ型を生成する機能がとても便利である。

アプリをコンテナ化する際、アプリとdbを同じコンテナに共存させることは避けたい。
本記事では、アプリのdockerイメージビルド時に外部のdbコンテナに接続することで、アプリ単体のコンテナを作る方法を示す。

できたもの

dockerコンテナを起動して、
http://localhost:8080
にアクセスするとdbから会社一覧を取得してテキスト表示するだけのhaskellアプリ。

スクショ

メモ

Dockerイメージ作成の流れ

以下のコマンドは、build.shの一部。

I.
コンテナ間通信をするためにdockerのネットワーク(bridge)を作成。

docker network create --driver=bridge $NETWORK

II.
スキーマとテーブルのみを定義したdbコンテナを起動。
本例ではpostgresを利用する。
/docker-entrypoint-initdb.dにsqlファイルをマウントするとコンテナ起動時に実行される。

docker run --net $NETWORK --name $DB_CONTAINER_NAME --expose 5432 -v $(pwd)/schema.sql:/docker-entrypoint-initdb.d/schema.sql -e POSTGRES_USER=$POSTGRES_USER -e POSTGRES_DB=$POSTGRES_DB -e POSTGRES_PASSWORD=$POSTGRES_PASSWORD -d postgres:11.5

III.
アプリケーションをビルド。
Dockerfileでは、ARGでdb接続に必要な情報を要求し、マルチステージビルドで最終的なイメージ容量を削減している。
本例では約5GB(GHCや依存パッケージなど含む)から100MB程(主にバイナリ)になった。

docker build --network $NETWORK -t $APP_CONTAINER_NAME --build-arg DB_HOST=$DB_CONTAINER_NAME --build-arg POSTGRES_USER=$POSTGRES_USER --build-arg POSTGRES_DB=$POSTGRES_DB --build-arg POSTGRES_PASSWORD=$POSTGRES_PASSWORD .

Dockerコンテナの起動

本例ではdocker-composeを用いて起動する。
appコンテナとdbコンテナに与える環境変数(POSTGRES_USER、POSTGRES_DB、POSTGRES_PASSWORD)は一致させる必要がある。
イメージビルド時に与えた値と異なっていても問題ない。

docker-composeではコンテナ間のネットワークが自動的に作られる。
dbコンテナへはホスト名"db"で接続できる。
そのためappコンテナの環境変数DB_HOSTに"db"を与えている。

dbコンテナにはアプリイメージ作成時に用いたものと同じsqlファイルをマウントする。
これに加えて、本例では初期データとしてseed.sqlもマウントしている。

Haskellのコード

環境変数を読んでpostgresへの接続を作成する関数。

connect :: IO Connection
connect = do
    host     <- getEnv "DB_HOST"
    user     <- getEnv "POSTGRES_USER"
    db       <- getEnv "POSTGRES_DB"
    password <- getEnv "POSTGRES_PASSWORD"
    let conninfo = unwords
            [ "host=" <> host
            , "user=" <> user
            , "dbname=" <> db
            , "password=" <> password
            ]
    connectPostgreSQL conninfo

そのほかhrrの記述や表示用データ型の記述などは、直接リポジトリを参照されたい。
コード量は少ない。

まとめ

本記事ではdb接続のあるhaskellアプリケーションのdockerコンテナを単体で作る方法を一つを示した。
hrrはpostgresの他にもmysql,sqlserver,oracleなども対応しているようなので、dbありきのシステム上にhaskellプログラムを実装するということもやり易い。

haskellプログラムを100MB程度のコンテナにできたことで、例えばkubernetesで構築された既存マイクロサービスの一部をhaskellで置き換えたり新しく作ったり割と気軽にできるような気がしてくる。

本例ではwaiを使った最小限のwebアプリを作成したが、servantを使えば例えばspaのバックエンド、spockやlucidを使えば例えば小規模のwebサービスなども作れる。

何となく重たいイメージのあるhaskellプログラムでも、コンテナ化さえしてしまえば、サーバー運用においてはよく使われる他の言語で作ったプログラムと同じような気持ちで扱えるのではないだろうか。

補足

I.
macosやwindowsの場合、dockerネットワークを作成しなくてもhost.docker.internalでホストosにアクセスすることができるので、ホストos経由でdbコンテナに接続することでもアプリのdockerイメージを作成できる。

しかしlinuxの場合、今のところhost.docker.internalが使えないようなので、ci環境でビルドしたりテストしたりすることも考慮して本例ではdockerネットワークを作成する形にした。

II.
開発時はdocker上ではなくホストosでコンパイルするのが吉と思われる。
依存パッケージを追加したりreplで色々試したりするのをdocker上でやろうとすると多分面倒。

III.
なぜそこまでしてhaskellを使いたいか?

  • 純粋な関数と副作用ありの関数を明確に区別してバグ少
  • 型でうまく制約をかければもっとバグ少
  • コンパイル通ったらしょうもないバグは多分ない!

構文の好みや圏論にあやかりたい気持ちもあるが、今のところバグの入り込む余地が少ないと感じるのが説得力ありそうな理由。

IV.
本例はmacosで実行した。
linuxやwindowsで実行する場合はbuild.shの微修正がいるかもしれない。

参考にしたQiita記事

docker multi-stage build

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

Rails & Nuxt.jsのDocker環境をalpineイメージで構築

Ruby on Rails、Nuxt.js、MySQLのDocker環境を作成します。
Rails、Nuxtのalpine環境の構築手順はそれぞれだと多くあるのですが、まとまったものがあまり ない 見つからなかったので、復習を兼ねてポストを作成します。

準備

ディレクトリ作成

作業ディレクトリは任意です。

$ NEW_APP=rails-nuxt-app #任意のアプリ名
$ mkdir ${NEW_APP}
$ cd ${NEW_APP}
$ mkdir ./backend ./frontend

backend はRails用、frontend はNuxt用のディレクトリです。
まずは下記のファイルを修正していきます。

.
├ backend
│   ├ Dockerfile
│   ├ Gemfile
│   └ Gemfile.lock
│
├ frontend
│   └ Dockerfile
│
├ docker-compose.yml
└ .env

docker-compose.yml

.env

docker-compose.ymlで参照する環境変数を記載します。
ここではMySQLのrootパスワード、RailsおよびNuxt環境のホスト、ポート番号のみ定義します。
RailsとNuxtは、共にデフォルトのポートが 3000 番なので、後の利便性のためにいずれかを変えておきます。
(本記事ではNuxt側を 8080 に変更)

MYSQL_ROOT_PASSWORD=password

BACKEND_HOST=0.0.0.0
BACKEND_PORT=3000

FRONTEND_HOST=0.0.0.0
FRONTEND_PORT=8080

./docker-compose.yml

.envで定義した変数を参照しています。
docker-compose.ymlの environment、 Dockerfileの ENV で同じ環境変数が定義されていた場合は、前者が使用されます。
(本記事ではDockerfile単体でもイメージ作成できるように環境変数の記載を残していますが、docker-compose.ymlに定義されていれば問題ありません)

下記はDockerボリュームを作成します。

  • mysqlのdatadir
  • rubyのgem_home
  • nodeのnode_modules
docker-compose.yml
version: '3'
services:
  db:
    image: mysql:5.7.27
    restart: always
    volumes:
      - db-data:/var/lib/mysql
    environment:
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
  backend:
    build: ./backend
    ports:
      - ${BACKEND_PORT}:3000
    command: /bin/sh -c "rm -f /app/tmp/pids/server.pid && bundle exec rails s -b ${BACKEND_HOST}"
    volumes:
      - ./backend:/app
      - backend-bundle:/usr/local/bundle
    environment:
      - HOST=${BACKEND_HOST}
      - PORT=${BACKEND_PORT}
    depends_on:
      - db
    tty: true
    stdin_open: true
  frontend:
    build: ./frontend
    ports:
      - ${FRONTEND_PORT}:8080
    command: /bin/sh -c "yarn dev"
    volumes:
      - ./frontend:/app
      - frontend-node_modules:/app/node_modules
    environment:
      - HOST=${FRONTEND_HOST}
      - PORT=${FRONTEND_PORT}
    tty: true
volumes:
  db-data:
  backend-bundle:
  frontend-node_modules:

backend

Rails環境構築のためのファイルを準備します。

./backend/Gemfile

Railsのバージョンのみ指定しておきます。

Gemfile
source 'https://rubygems.org'

gem 'rails', '5.2.3'

./backend/Gemfile.lock

空ファイルをtouchしておけばOKです。

./backend/Dockerfile

Alpine Linuxのパッケージは最低限のものだけインストールします。
開発を進めるうちにgemのインストールで依存エラーが発生した場合には、不足パッケージを都度追加しましょう。

Dockerfile
FROM ruby:2.6.3-alpine

ENV RUNTIME_PACKAGES "mysql-client mysql-dev tzdata nodejs"
ENV DEV_PACKAGES "build-base curl-dev"
ENV APP_HOME /app
ENV TZ Asia/Tokyo

ENV HOST 0.0.0.0
ENV PORT 3000

WORKDIR ${APP_HOME}
ADD Gemfile ${APP_HOME}/Gemfile
ADD Gemfile.lock ${APP_HOME}/Gemfile.lock

RUN apk update \
    && apk upgrade \
    && apk add --update --no-cache ${RUNTIME_PACKAGES} \
    && apk add --update --no-cache --virtual=.build-dependencies ${DEV_PACKAGES} \
    && bundle install -j4 \
    && rm -rf /usr/local/bundle/cache/*.gem \
        && find /usr/local/bundle/gems/ -name "*.c" -delete \
        && find /usr/local/bundle/gems/ -name "*.o" -delete \
    && apk del --purge .build-dependencies \
    && rm -rf /var/cache/apk/*

COPY . ${APP_HOME}

EXPOSE ${PORT}

CMD ["rails", "server", "-b", ${HOST}]

frontend

./frontend/Dockerfile

Dockerfile
FROM node:12.9.0-alpine

ENV APP_HOME /app
ENV PATH ${APP_HOME}/node_modules/.bin:$PATH
ENV TZ Asia/Tokyo

ENV HOST 0.0.0.0
ENV PORT 8080

WORKDIR ${APP_HOME}
ADD . ${APP_HOME}

RUN apk update \
    && apk upgrade \
    && yarn install \
    && rm -rf /var/cache/apk/*

EXPOSE ${PORT}

CMD ["yarn", "dev"]

アプリケーション作成

Rails、Nuxt環境にプロジェクトを作成します。
docker-compose run を実行したタイミングで、それぞれのDockerイメージがbuildされ、さらにコンテナが立ち上がります。
--no-deps ... docker-compose.ymlで depends_on or links 指定するサービスは起動しない。
--rm ... 処理を終えたコンテナを自動的に削除。

backend

アプリケーション作成

rails new でRailsアプリケーションを作成します。
--api オプションでAPIモードにしていますが、不要な方は外してください。

$ docker-compose run --no-deps --rm backend rails new . --force --api --database=mysql --skip-bundle

DB接続のため、下記のファイルを修正します。

.
└ backend
    ├ config
    │  └ database.yml
    ├ Gemfile
    └ .env

./backend/.env

docker-compose.ymlで参照している MYSQL_ROOT_PASSWORD と同じもの設定します。

MYSQL_ROOT_PASSWORD=password

./backend/Gemfile

.envから環境変数を読み込むdotenv-railsというgemを追加します。

Gemfile
~~省略~~

group :development, :test do
  # Call 'byebug' anywhere in the code to stop execution and get a debugger console
  gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]

  gem "dotenv-rails" #追加
end

~~省略~~

./backend/config/database.yml

DBへのアクセスに使用するパスワードを、環境変数から取得します。

database.yml
~~省略~~

default: &default
  adapter: mysql2
  encoding: utf8
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: root
  password: <%= ENV.fetch("MYSQL_ROOT_PASSWORD") { '' } %> #環境変数を参照するように修正
  host: db #localhostからdocker-compose.ymlのサービス名に修正

~~省略~~

frontend

アプリケーション作成

npx でNuxtアプリケーションを作成します。

後から追加/変更できるので、このタイミングではEnterキー連打でOKです。

$ docker-compose run --rm frontend npx create-nuxt-app .

~~省略~~

create-nuxt-app v2.10.0
✨  Generating Nuxt.js project in .
? Project name #アプリ名 <Enter>
? Project description #任意 <Enter>
? Author name #任意 <Enter>
? Choose the package manager #Yarn <Enter>
? Choose UI framework #None <Enter>
? Choose custom server framework #None <Enter>
? Choose Nuxt.js modules #(Nothing) <Enter>
? Choose linting tools #(Nothing) <Enter>
? Choose test framework #None <Enter>
? Choose rendering mode #Universal (SSR) <Enter>
? Choose development tools #(Nothing) <Enter>

Dockerイメージ作成

アプリケーション作成時にできたDockerボリュームは削除しておきます。

$ docker-compose down --volume
# もしくは docker volume rm ボリューム名

各ファイルを修正した状態で、Dockerイメージをビルドします。

$ docker-compose build

docker-compose.ymlで build を指定しているbackendとfrontendのイメージが作成されたことを確認します。

$ docker images --format "{{.Repository}}\t{{.CreatedSince}}" ${NEW_APP}*
rails-nuxt-app_frontend     About a minutes ago
rails-nuxt-app_backend      About a minutes ago

Hello World

最後にDockerコンテナを起動し、Rails、NuxtアプリケーションのHelloWorldを確認します、

docker-compose.ymlで定義したサービスを -d オプション(デタッチモード)でバックグラウンド起動します。

$ docker-compose up -d

プロセスが立ち上がっていることを確認します。

$ docker-compose ps
        Name                       Command               State           Ports         
---------------------------------------------------------------------------------------
rails-nuxt-app_backend_1    /bin/sh -c rm -f /app/tmp/ ...   Up      0.0.0.0:3000->3000/tcp
rails-nuxt-app_db_1         docker-entrypoint.sh mysqld      Up      3306/tcp, 33060/tcp   
rails-nuxt-app_frontend_1   docker-entrypoint.sh /bin/ ...   Up      0.0.0.0:8080->8080/tcp

失敗している場合は docker-compose logs などで原因を探りましょう。

backend

RailsアプリケーションのDBを作成します。

$ docker-compose exec backend rails db:create
Created database 'app_development'
Created database 'app_test'

ブラウザで http://localhost:3000/ を開きます。
rails-helloworld.png

frontend

ブラウザで http://localhost:8080/ を開きます。
nuxt-helloworld.png


お疲れさまでした。
次回は GraphQL の導入について投稿したいと思います。

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

Docker環境をECSにデプロイ

はじめに

これまでは Vagrant + VirtualBox で作った仮想環境上で、Dockerコンテナを動かしていましたが、これがどのようにAmazon Elastic Container Service (ECS) にデプロイされるかの手順が良く分かっていませんでした。

そこで、簡単なWebアプリケーションコンテナをECSにデプロイして、AWS上で動作確認するところまでをやってみようと思います。

目次

  • Docker イメージを用意
  • ローカルでのDockerイメージの動作確認
  • AWS CLIをインストール
  • ECRにPUSHする為のIAMユーザを作成
  • AWS CLIに認証情報を設定
  • キーペアの作成
  • ECRにリポジトリを作成
  • ECRの該当リポジトリにPUSH
  • ECSのタスク定義を作成
  • ECSのクラスターを作成
  • ECSのタスクを実行
  • 動作確認

Docker イメージを用意

今回はAWS上でのWebアプリケーションの動作確認が目的です。
Dockerイメージを一からビルドしてもいいのですが、それだと時間がかかるので、公開されているDockerイメージ(今回はtraining/webapp)を利用させてもらいます。

例によって、私の環境はWindows7 なので、Vagrant+VirtualBoxで新しい仮想環境を作って、そこにDockerイメージをダウンロードしようと思います。
今回は途中AWS CLIのインストールなどもある為、CentOSで仮想環境を作ります。

> vagrant box add centos/7
> vagrant init centos/7

ホストOS(Windows7)と、ゲストOS(centos/7)間のポートフォワーディング設定部分のコメントアウトを外します。

vagrantfile
26| config.vm.network "forwarded_port", guest: 80, host: 8080

仮想環境を起動します。

> vagrant up
Bringing machine 'default' up with 'virtualbox' provider...
==> default: Importing base box 'centos/7'...
==> default: Matching MAC address for NAT networking...
==> default: Checking if box 'centos/7' is up to date...
==> default: Setting the name of the VM: test_ecs_deploy_v2_default_1566893989169_14897
==> default: Clearing any previously set network interfaces...
==> default: Preparing network interfaces based on configuration...
    default: Adapter 1: nat
==> default: Forwarding ports...
    default: 80 (guest) => 8080 (host) (adapter 1)
    default: 22 (guest) => 2222 (host) (adapter 1)
==> default: Booting VM...
==> default: Waiting for machine to boot. This may take a few minutes...
    default: SSH address: 127.0.0.1:2222
    default: SSH username: vagrant
    default: SSH auth method: private key
    default:
    default: Vagrant insecure key detected. Vagrant will automatically replace
    default: this with a newly generated keypair for better security.
    default:
    default: Inserting generated public key within guest...
    default: Removing insecure key from the guest if it's present...
    default: Key inserted! Disconnecting and reconnecting using new SSH key...
==> default: Machine booted and ready!
==> default: Checking for guest additions in VM...
    default: No guest additions were detected on the base box for this VM! Guest
    default: additions are required for forwarded ports, shared folders, host only
    default: networking, and more. If SSH fails on this machine, please install
    default: the guest additions and repackage the box to continue.
    default:
    default: This is not an error message; everything may continue to work properly,
    default: in which case you may ignore this message.
==> default: Rsyncing folder:

ゲストOS(CentOS)にSSHでログインします。

> vagrant ssh

Dockerをインストールします。

$ sudo yum install -y yum-utils device-mapper-persistent-data lvm2
$ sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
$ sudo yum makecache fast
$ sudo yum install docker-ce

Dockerデーモンを起動します。

$ sudo systemctl start docker

ローカルでDockerイメージの動作確認

今回、ECSにデプロイするWebアプリケーションコンテナ(training/webapp)をpull(ダウンロード)します。

$ sudo docker pull training/webapp
Using default tag: latest
latest: Pulling from training/webapp
[DEPRECATION NOTICE] registry v2 schema1 support will be removed in an upcoming release. Please contact admins of the docker.io registry NOW to avoid future disruption.
e190868d63f8: Pull complete
909cd34c6fd7: Pull complete
0b9bfabab7c1: Pull complete
a3ed95caeb02: Pull complete
10bbbc0fc0ff: Pull complete
fca59b508e9f: Pull complete
e7ae2541b15b: Pull complete
9dd97ef58ce9: Pull complete
a4c1b0cb7af7: Pull complete
Digest: sha256:06e9c1983bd6d5db5fba376ccd63bfa529e8d02f23d5079b8f74a616308fb11d
Status: Downloaded newer image for training/webapp:latest
docker.io/training/webapp:latest

コンテナを起動して、動作確認をしましょう。

$ sudo docker run -d -p 80:5000 training/webapp python app.py
103772d31387e0dc150dcb2ef8916c2c6f554a4f1fbb670a7b96560a7e219b2f
$

ブラウザで http://localhost:8080/ へアクセスして Hello world! の文字が表示されればOKです。

AWS CLI をインストール

AWS CLI は、Pythonのパッケージ管理ツール pip 経由でインストールします。
その為、まずは pip をインストールします。

$ curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py
$ sudo python get-pip.py
Successfully installed pip-19.2.3 setuptools-41.2.0 wheel-0.33.6

AWS CLI をインストールします。

$ sudo pip install awscli
Successfully installed PyYAML-5.1.2 awscli-1.16.226 botocore-1.12.216 colorama-0.3.9 docutils-0.15.2 futures-3.3.0 jmespath-0.9.4 pyasn1-0.4.6 python-dateutil-2.8.0 rsa-3.4.2 s3transfer-0.2.1 six-1.12.0 urllib3-1.25.3

バージョンを確認します。

$ aws --version
aws-cli/1.16.226 Python/2.7.5 Linux/3.10.0-957.12.2.el7.x86_64 botocore/1.12.216

IAMユーザを作成

以下の手順で作成します。

  • AWS のコンソールから IAM サービスにアクセスします。
  • サイドバーにある[ユーザー]をクリックします。
  • ユーザー名を入力し、[プログラムによるアクセス]をチェックします。
  • [次のステップ:アクセス権限]をクリックします。 aws_iam_user_create.png
  • [既存のポリシーをアタッチ]をクリックします。
  • [フィルター: ポリシータイプ]の検索窓に’EC2’と入力
  • 以下のポリシーの項目をチェックします。
    [AmazonEC2ContainerServiceFullAccess],
    [AmazonEC2ContainerRegistryFullAccess]
    ※管理者権限による Amazon ECR へのフルアクセスを許可するものです。 aws_iam_user_create_2.png
  • [次のステップ:タグ]を選択します。
    ※タグの追加は今回は行わないのでスキップします。
  • [次のステップ:確認]を選択します。
  • [ユーザーの作成]をクリックします。
  • [.csvのダウンロード]から認証情報が記載されたファイルをダウンロードします。

AWS CLIに認証情報を設定

先ほどダウンロードした認証情報ファイルを開き、aws cli に設定していきます。

$ aws configure                 #AWS 認証情報の設定
$ sAWS Access Key ID [None]:    #取得したアクセスキーID を入力
$ AWS Secret Access Key [None]: #取得したシークレットアクセスキーIDを入力
$ Default region name [None]:   ap-northeast-1
$ Default output format [None]: json
$ aws configure list            #設定した内容の確認

キーペアの作成

AWSコンソールにて、EC2 > キーペア で「キーペアの作成」をクリックします。
aws_ec2_key_pairs.png
キーペア名は何でもいいですが、とりあえず「ecs-deploy」としましょう。

作成すると、秘密鍵ファイルがダウンロードされます。

ECRにリポジトリを作成

AWSコンソールにて、ECR をクリックし、「リポジトリの作成」をクリックします。
リポジトリ名を入力する画面になるので、「python」とでもしておきましょう。

ECRの「python」リポジトリにPUSH

早速作成したリポジトリにtraining/webappイメージをpythonという名前でAWS CLIにて、PUSH(アップロード)しましょう。

AWS コンソールにて、作成した「python」リポジトリをクリックすると、右上に「プッシュコマンドの表示」というボタンがありますので、これをクリックします。
表示されたコマンドで、DockerイメージのPUSHができます。

まずはaws cli でAWSにログインします。

$ sudo $(aws ecr get-login --no-include-email --region ap-northeast-1)

まだパスワード設定していないので、上記のコマンドだけでいけます。

AWSコンソールの案内では、Dockerイメージの構築を docker buildコマンドで行うように記載されていますが、今回はpullしたtraining/webappイメージをpushしますので、この手順はスキップします。

手元のtraining/webappイメージに、タグ付けしていきます。

$ sudo docker tag training/webapp:latest XXXXXXXXXXXX.dkr.ecr.ap-northeast-1.amazonaws.com/python:latest

XXXXXXXXXXXX.dkr.ecr.ap-northeast-1.amazonaws.com の部分は、ECRのイメージURIです。

では、イメージをpushします。

$ sudo docker push XXXXXXXXXXXX.dkr.ecr.ap-northeast-1.amazonaws.com/python:latest

AWSコンソールからECRサービスに移動し、pythonリポジトリにイメージがあるか確認しましょう。

ECSのタスク定義を作成

以下の手順でタスク定義を作成します。

  • AWSコンソールからECSサービスに移動します。
  • 左メニューのタスク定義をクリックします。
  • 「新しいタスク定義の作成」をクリックします。
  • 「起動タイプの互換性の選択」にて「EC2」を選択し(Fargateはまだ使ったことがないので。。。)、「次のステップ」をクリックします。
  • 「タスク定義名」に「python」と入力します。
  • 「コンテナ定義」の「コンテナの追加」をクリックします。
  • 「コンテナ名」に「python」と入力します。
  • 「イメージ」には先ほどdocker tagコマンドで設定したイメージURIとイメージ名、タグを入力します。
    (例:XXXXXXXXXXXX.dkr.ecr.ap-northeast-1.amazonaws.com/python:latest)
  • 「メモリ制限」には「128」と入力します。
  • 「ポートマッピング」には「80」(ホスト)「5000」(コンテナ)と入力し、「追加」ボタンをクリックします。
  • 最後に、「作成」ボタンをクリックします。

ECSのクラスターを作成

以下の手順でクラスターを作成します。
- 左メニューの「クラスター」をクリックします。
- 「クラスターの作成」ボタンをクリックします。
- 「EC2+ネットワーキング」を選択し、「次のステップ」ボタンをクリックします。
- クラスター名に「ecs-deploy-cluster」を入力します。
- EC2インスタンスタイプで「t2.micro」を選択します。
- インスタンス数で「1」を入力します。
- キーペアで先ほど作成した「ecs-deploy」を選択します。
- 「作成」ボタンをクリックします。
- 起動ステータス画面になるので、「クラスターの表示」ボタンが活性化されるまで待ちます。
- 「クラスターの表示」ボタンが活性化されたら、クリックします。

ECSのタスクを実行

以下の手順でタスクを実行します。

  • [タスク]の項目から[新しいタスクの実行]をクリックします。
  • [起動タイプ]は「EC2」を選択します。
  • [タスク定義]は先ほど作成した、「python:1」を選択します。
  • [タスクの実行]ボタンをクリックします。
  • 状態が[RUNNING]になるまでしばし待機します。
  • [RUNNING]になったら、コンテナインスタンスのリンクをクリックします。
  • [パブリックDNS」に記載されているURIにブラウザからアクセスします。
    「Hello World!」が表示されればOKです。

おわりに

実際にデプロイを一通りやってみると、どのような仕組みで動いているのかが良く分かると思います。

参考

https://techblog.istyle.co.jp/archives/1652
http://yng.hatenablog.jp/entry/2016/08/22/232807

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

Dockerを使ってRails6環境の構築をしてみる

Rails6のリリースがされていたので、自身のPCに環境を作ってみた。

(環境)

Mac(Mojave 10.14.6)

(参考)

https://docs.docker.com/compose/rails/#define-the-project
に沿って実施。
実施の時は5系の記載だったので、6に置き換えて行う。

1.Docker for Macのインストール

(インストール済みならスキップ)
https://docs.docker.com/compose/install/

2.Rails / PostgreSQLアプリケーションを設定

Docker Composeを使用してRails / PostgreSQLアプリケーションを設定する。

2-1.プロジェクト定義

開発していくディレクトリにアプリケーションを構築するために必要な4つのファイルを設定する。
・Dockerfile
・Gemfile
・Gemfile.lock
・entrypoint.sh
・docker-compose.yml

*今回はPC内のUserフォルダ配下に作成
*プロジェクト名をmyappとして設定

$ mkdir myapp
$ cd myapp
Dockerfile
FROM ruby:2.6

RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \
    && echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list

RUN apt-get update -qq && apt-get install -y nodejs postgresql-client yarn
RUN mkdir /myapp
WORKDIR /myapp
COPY Gemfile /myapp/Gemfile
COPY Gemfile.lock /myapp/Gemfile.lock
RUN bundle install
COPY . /myapp

# Add a script to be executed every time the container starts.
COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]
EXPOSE 3000

# Start the main process.
CMD ["rails", "server", "-b", "0.0.0.0"]
Gemfile
source 'https://rubygems.org'
gem 'rails', '6.0.0'
Gemfile.lock(空のファイル)
entrypoint.sh
#!/bin/bash
set -e

# Remove a potentially pre-existing server.pid for Rails.
rm -f /myapp/tmp/pids/server.pid

# Then exec the container's main process (what's set as CMD in the Dockerfile).
exec "$@"
docker-compose.yml
version: '3'
services:
  db:
    image: postgres
    volumes:
      - ./tmp/db:/var/lib/postgresql/data
  web:
    build: .
    command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
    volumes:
      - .:/myapp
    ports:
      - "3000:3000"
    depends_on:
      - db

2-2.プロジェクトの構築

作成した5つのファイルを利用してdocker-compose runを実行し、アプリケーションを生成する。(ターミナルを使って実施)

$ docker-compose run web rails new . --force --no-deps --database=postgresql
:
:
:
最後にWebpackerのインストール成功メッセージが表示される
Webpacker successfully installed ? ?

アプリケーション生成後は、以下を実施する。

$ docker-compose build
:
:
:
最後に下記のようなSuccess情報が表示される
Successfully built aa99bbad99f9
Successfully tagged myapp_web:latest

2-3.データベースの設定と作成

データベースの情報を設定するために、config/database.ymlを変更し、コマンドでDBを作成する。

config/database.yml
# 設定箇所のみ抜粋
default: &default
  adapter: postgresql
  encoding: unicode
  host: db
  username: postgres
  password:
  pool: 5
:
:
development:
  <<: *default
  database: myapp_development
:
:
test:
  <<: *default
  database: myapp_test

データベースを作成する

$ docker-compose run web rake db:create

# 作成が成功すると、以下のコマンドが表示される。
Starting myapp_db_1 ... done
Created database 'myapp_development'
Created database 'myapp_test'

2-4.dockerを起動

dockerを起動し、ローカル環境のページにアクセスする。

docker-compose up

実行すると以下のコマンドが表示される。

myapp_db_1 is up-to-date
Starting myapp_web_1 ... done
Attaching to myapp_db_1, myapp_web_1
:
:
web_1  | => Booting Puma
web_1  | => Rails 6.0.0 application starting in development
web_1  | => Run `rails server --help` for more startup options
web_1  | Puma starting in single mode...
web_1  | * Version 3.12.1 (ruby 2.6.3-p62), codename: Llamas in Pajamas
web_1  | * Min threads: 5, max threads: 5
web_1  | * Environment: development
web_1  | * Listening on tcp://0.0.0.0:3000
web_1  | Use Ctrl-C to stop
 => Booting Puma
 => Rails 6.0.0 application starting in development
 => Run `rails server --help` for more startup options
 Puma starting in single mode...
 * Version 3.12.1 (ruby 2.6.3-p62), codename: Llamas in Pajamas
 * Min threads: 5, max threads: 5
 * Environment: development
 * Listening on tcp://0.0.0.0:3000

http://localhost:3000にアクセスして、ようこそページが表示されれば、構築完了。
スクリーンショット 2019-08-26 8.57.06.png

その他(アプリケーションの停止)

アプリケーションの停止はプロジェクトディレクトリでdocker-compose downを実行する。

・別のターミナルウィンドウを表示し、プロジェクトディレクトリから実施

$ docker-compose down
Stopping myapp_web_1 ... done
Stopping myapp_db_1  ... done
Removing myapp_web_run_b13a7c5899a1 ... done
Removing myapp_web_1                ... done
Removing myapp_web_run_5ad07400cf63 ... done
Removing myapp_db_1                 ... done
Removing network myapp_default

・downするともう一つのターミナル(docker-compose upした側)も停止の実行結果が表示される

web_1  | - Gracefully stopping, waiting for requests to finish
web_1  | === puma shutdown: 2019-08-25 23:58:46 +0000 ===
web_1  | - Goodbye!
web_1  | Exiting
myapp_web_1 exited with code 1
db_1   | 2019-08-25 23:58:47.254 UTC [1] LOG:  received smart shutdown request
db_1   | 2019-08-25 23:58:47.278 UTC [1] LOG:  background worker "logical replication launcher" (PID 28) exited with exit code 1
db_1   | 2019-08-25 23:58:47.279 UTC [23] LOG:  shutting down
db_1   | 2019-08-25 23:58:47.353 UTC [1] LOG:  database system is shut down
myapp_db_1 exited with code 0
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS CodeBuild をローカルでデバッグする CodeBuild Local 入門

簡単に

  1. https://github.com/aws/aws-codebuild-docker-images を clone
  2. aws-codebuild-docker-images/ubuntu/standard/2.0/Dockefile をビルド ( これがビルド用イメージになる )
    • マシンスペックにもよりますが、 Core i7 の Macbook で 20 分程度かかりました
  3. ヘルパースクリプト aws-codebuild-docker-images/local_builds/codebuild_build.sh を利用して CodeBuild Local 起動
Getting-started
$ git clone https://github.com/aws/aws-codebuild-docker-images.git
$ docker build -t aws/codebuild/standard:2.0 ./aws-codebuild-docker-images/ubuntu/standard/2.0/
$ ./aws-codebuild-docker-images/local_builds/codebuild_build.sh -i aws/codebuild/standard:2.0 -s /path/to/src -a ./artifacts

CodeBuild の buildspec.yml のデバッグがつらい!!

CodeBuild の buildspec.yml を書いているとき、こんなことありませんか?

  • buildspec.yml のちょっとした修正の動作確認をしたいけど、この修正でいいかわからない
  • 修正途中のコミットを push したくない、後でコミットログを整理したくない
  • 修正途中のソースでパイプラインが走りきって、ビルドされたコンテナイメージをリポジトリに push してしまうとまずい
  • CodeBuild の待ち時間が長い

それ、 CodeBuild Local なら解決できるかもしれません。

CodeBuild Local とは ?

aws/aws-codebuild-docker-images - GitHub

  • AWS が提供する CodeBuild のビルド用イメージの Docker ファイルと、それを動かすための Agent Docker イメージとヘルパースクリプト
  • ローカルで buildspec.yml を走らせてデバッグできる
  • 本物の CodeBuild 同様、複数ソースをインプットとしたり、アーティファクトの出力もできる

やってみよう !

CodeBuild Local を簡単に体験できる Makefile を書きました。
使い方がわかってきたら、実際にプロダクトに組み込んでみると良いと思います。

ikasam/codebuild-local-getting-started - GitHub

getting-startedの使い方
$ git clone https://github.com/ikasam/codebuild-local-getting-started.git
$ cd codebuild-local-getting-started
$ make

動作確認環境

  • Docker Desktop for Mac v2.1.0.1 (37199)
    • Engine: 19.03.1
  • GNU Make 3.81

ヘルパースクリプト codebuild_build.sh の使い方がわかれば CodeBuild Local が分かる!!

ヘルパースクリプトがやっていること

スクリプトに渡されたオプションから、実際に実行する docker コマンドを組み立てて実行します。最終的にどういうコマンドが実行されるかは、スクリプト実行時の最初に表示されます。

$ ./aws-codebuild-docker-images/local_builds/codebuild_build.sh \
-i aws/codebuild/standard:2.0 -e ./codebuild.local.env -s . -a ./artifacts
Build Command:

docker run -it -v /var/run/docker.sock:/var/run/docker.sock -e \
"IMAGE_NAME=aws/codebuild/standard:2.0" \
-e "ARTIFACTS=/Users/m_kanno/ikasama/codebuild-local-getting-started/artifacts" \
-e "SOURCE=/Users/m_kanno/ikasama/codebuild-local-getting-started/." \
-v "/Users/m_kanno/ikasama/codebuild-local-getting-started:/LocalBuild/envFile/" \
-e "ENV_VAR_FILE=codebuild.local.env" \
-e "INITIATOR=m_kanno" amazon/aws-codebuild-local:latest

※ 見やすさのために出力を改行しています

少しクセのあるオプションたち

ここの README にだいたいのことが書いてありますが、少しクセがあるので補足説明をしておきます。

https://github.com/aws/aws-codebuild-docker-images/tree/master/local_builds

オプション 意味 必須?
-i ビルド用イメージ
-a アーティファクト出力先
-l agent イメージを指定する
-c AWS CLI の設定
-p AWS CLI のプロファイル
-b buildspec.yml の指定
-e 環境変数
-m ソースディレクトリをホストから Volume Mount
-s ソースディレクトリの指定

-i ビルド用イメージ

  • カスタム用ビルドイメージはここで指定します

-c, -p AWS CLI の設定、プロファイル

  • ~/.aws/config, ~/.aws/credential をビルド用イメージ内にコピーします
  • 例えば、 AWS のリソースにアクセスする際に必要です
    • ECR からイメージを pull するとか
  • プロファイルを指定したい場合は -c に加えて -p your_profile も指定します

-e 環境変数

  • ファイルだけ渡せる
  • -e MYENV=hoge というような渡し方はできません
  • 一般的な env ファイルフォーマット
    • 細かい仕様は README に書いてあります
  • ローカルで走らせたくない処理があるときは、環境変数で制御すると良いです
    • CODEBUILD_CI は CodeBuild 上では true となるので、ローカルでは false を渡して if で分岐するとか
環境変数で制御する例
if $CODEBUILD_CI; then echo This line is executed only in the real CodeBuild environment; fi

-m ソースディレクトリをホストから Volume Mount

  • うまく使うとソース転送時間を短縮できます
  • マウント先にビルドの中間生成物を作ったあと削除しないと、ホスト上に中間生成物が残ります

-s ソースディレクトリの指定

  • 省略するとカレントディレクトリをソースとします
  • 複数ソースを指定する場合、 2 つ目以降は -s <sourceIdentifier>:<sourceLocation> の形式で指定します
  • buildspec.yml の中では、 $CODEBUILD_SRC_DIR_<sourceIdentifier> で参照できます 1

2 つの異なる Docker イメージ

ビルド用イメージ

ビルド環境となる Docker イメージです。
CodeBuild が標準で提供しているイメージを利用したり、プロジェクトによってはカスタムイメージを用意します。 getting-started では CodeBuild 標準の aws/codebuild/standard:2.0 を利用しています。
aws/codebuild/standard:2.0 は、CodeBuild のコンソールでは環境設定で設定します。

image.png

CodeBuild Local Agent (agent イメージ)

AWS CodeBuild Local Builds

CodeBuild をエミュレートする Docker イメージです。
こいつが buildspec.yml を解釈し、ビルド用イメージ上でのビルドを指示します。
Dockerfile などは公開されていおらず2、 Docker Hub からイメージを pull して利用します。

CodeBuild Local の制限事項

buildspec.yml 上での Docker の Volume mount がうまく動きません。
これは、以下の環境差異によるものです。

  • CodeBuild: OS 3上の Docker でビルド用イメージを動かす
  • CodeBuild Local: Docker コンテナ (agent) からホスト OS の Docker でビルド用イメージを動かす

Docker デーモンが動いている場所とマウントしようとする場所が違うので、 CodeBuild Local では Volume mount がうまく動きません。4

まとめ

  • CodeBuild Local で buildspec.yml を手軽に動作確認できます
  • CodeBuild Local を簡単に体験できる Makefile を作りました。ぜひ試してみてください!
  • ヘルパースクリプト codebuild_build.sh の使い方をマスターしよう!
  • 一部制限事項があるので、気をつけましょう

参考


  1. Primary Source は $CODEBUILD_SRC_DIR 

  2. CodeBuild のリファレンス実装だからかな? 

  3. たぶん EC2 

  4. 個人的には CI のパイプラインで Docker Volume Mount するのはあまり筋が良くないと思うので避けたほうがいいと思います 

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

コンテナのシェルに接続する2つの方法

コンテナのシェルに接続するには、docker attachdocker execの2つがある

docker attach

$ docker attach <コンテナ名 or コンテナID>
  • コンテナのPID1のプロセスの標準入出力に接続する
  • コンテナの起動時のコマンドでシェルを指定した場合に、docker attachすると起動時に指定したシェルに接続できる
# bashを指定してコンテナを起動
$ docker run -itd --name alpine1 alpine /bin/bash
# bashに接続
$ docker attach alpine1
  • 注意点
    • 起動時にデーモンを起動していた場合は、デーモンの標準入出力に接続される
    • attachで接続したあとに、exitで抜けるとPID1のプロセスが終了してしまいコンテナも停止する
    • 基本的にはCtrl+p qを連続して入力するとプロセスを終了させずに抜けることができるが、起動時に-itをつけていない場合は抜けることができないので注意

docker exec

$ docker exec -it <コンテナ名 or コンテナID> <シェル>
$ docker exec -it alpine /bin/bash
  • docker execは、起動しているコンテナ内で任意のコマンドを実行するコマンド
  • シェルに接続するために、-itオプションをつけてシェルを指定しコマンドを実行する
  • docker attachとは違い、exitで抜けてもコンテナを停止させないので、こちらのほうが安全
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

DockerのKeycloakのログファイル設定変更方法の調査

Amazon Linux2

git clone https://github.com/jboss-dockerfiles/keycloak.git
cd keycloak
cd server
vi ./tools/cli/loglevel.cli
loglevel.cli変更前
/subsystem=logging/logger=org.keycloak:add
/subsystem=logging/logger=org.keycloak:write-attribute(name=level,value=${env.KEYCLOAK_LOGLEVEL:INFO})

/subsystem=logging/root-logger=ROOT:change-root-log-level(level=${env.ROOT_LOGLEVEL:INFO})

/subsystem=logging/root-logger=ROOT:remove-handler(name="FILE")
/subsystem=logging/periodic-rotating-file-handler=FILE:remove

/subsystem=logging/console-handler=CONSOLE:undefine-attribute(name=level)

アクセスログ有効化
管理監査ロギング有効化
イベントログ設定

loglevel.cli変更後
/subsystem=logging/logger=org.keycloak:add
/subsystem=logging/logger=org.keycloak:write-attribute(name=level,value=${env.KEYCLOAK_LOGLEVEL:INFO})

/subsystem=logging/root-logger=ROOT:change-root-log-level(level=${env.ROOT_LOGLEVEL:INFO})

/subsystem=logging/root-logger=ROOT:remove-handler(name="FILE")
/subsystem=logging/periodic-rotating-file-handler=FILE:remove

/subsystem=logging/console-handler=CONSOLE:undefine-attribute(name=level)

/subsystem=undertow/server=default-server/host=default-host/setting=access-log:add

/core-service=management/access=audit/logger=audit-log:write-attribute(name=enabled,value=true)

/subsystem=logging/logger=org.keycloak.events:add
/subsystem=logging/logger=org.keycloak.events:write-attribute(name=level,value=DEBUG)
/subsystem=logging/size-rotating-file-handler=EVENTLOG:add(file={path=events.log,relative-to=jboss.server.log.dir})
/subsystem=logging/size-rotating-file-handler=EVENTLOG:write-attribute(name=level,value=DEBUG)
/subsystem=logging/size-rotating-file-handler=EVENTLOG:write-attribute(name=rotate-size, value=10M)
/subsystem=logging/size-rotating-file-handler=EVENTLOG:write-attribute(name=max-backup-index, value=5)
/subsystem=logging/size-rotating-file-handler=EVENTLOG:write-attribute(name=append,value=true)
/subsystem=logging/size-rotating-file-handler=EVENTLOG:write-attribute(name=formatter,value="%d{yyyy-MM-dd HH\:mm\:ss,SSS} %-5p [%c] (%t) %s%e%n")
/subsystem=logging/size-rotating-file-handler=EVENTLOG:write-attribute(name=autoflush,value=true)
/subsystem=logging/root-logger=ROOT:add-handler(name=EVENTLOG)

/subsystem=logging/size-rotating-file-handler=ACCESSLOG:add(file={path=access_log.log,relative-to=jboss.server.log.dir})
/subsystem=logging/size-rotating-file-handler=ACCESSLOG:write-attribute(name=rotate-size, value=10M)
/subsystem=logging/size-rotating-file-handler=ACCESSLOG:write-attribute(name=max-backup-index, value=5)
/subsystem=logging/size-rotating-file-handler=ACCESSLOG:write-attribute(name=append,value=true)
/subsystem=logging/size-rotating-file-handler=ACCESSLOG:write-attribute(name=formatter,value="%d{yyyy-MM-dd HH\:mm\:ss,SSS} %-5p [%c] (%t) %s%e%n")
/subsystem=logging/size-rotating-file-handler=ACCESSLOG:write-attribute(name=autoflush,value=true)
/subsystem=logging/root-logger=ROOT:add-handler(name=ACCESSLOG)

/subsystem=logging/size-rotating-file-handler=SERVERLOG:add(file={path=server.log,relative-to=jboss.server.log.dir})
/subsystem=logging/size-rotating-file-handler=SERVERLOG:write-attribute(name=rotate-size, value=10M)
/subsystem=logging/size-rotating-file-handler=SERVERLOG:write-attribute(name=max-backup-index, value=5)
/subsystem=logging/size-rotating-file-handler=SERVERLOG:write-attribute(name=append,value=true)
/subsystem=logging/size-rotating-file-handler=SERVERLOG:write-attribute(name=formatter,value="%d{yyyy-MM-dd HH\:mm\:ss,SSS} %-5p [%c] (%t) %s%e%n")
/subsystem=logging/size-rotating-file-handler=SERVERLOG:write-attribute(name=autoflush,value=true)
/subsystem=logging/root-logger=ROOT:add-handler(name=SERVERLOG)
# イメージビルド
docker build -t keycloak-log .

# Dockerコンテナ起動
docker run -d -p 18080:8080 \
             -e KEYCLOAK_USER=admin \
             -e KEYCLOAK_PASSWORD=admin \
             --name keycloak \
             keycloak-log:latest

# コンテナに入る
docker exec -it keycloak bash

# ログ確認
[root@17525ee68503 jboss]# ls ./keycloak/standalone/data | grep audit-log.log
audit-log.log
audit-log.log2019-08-27_133654
audit-log.log2019-08-27_133947

# ブラウザでhttp://localhost:18080/にアクセス
[root@17525ee68503 jboss]# ls ./keycloak/standalone/log
access_log.log  audit.log  events.log  server.log

ロガーを設定してみる
WildFlyでアクセスログを有効化する
3.7. 管理監査ロギング
12. JBoss EAP を用いたロギング

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

DockerでRedisのログファイルをVolumeマウントしてコンテナ起動する方法

./redis-build/Dockerfile
FROM redis:5.0.5

RUN mkdir -p /var/log/redis
RUN touch /var/log/redis/redis.log
docker-compose.yml
version: '3.7'

services:

  redis:
    build: ./redis-build
    ports:
      - "6379:6379"
    command: redis-server --logfile /var/log/redis/redis.log
    volumes:
      - ./log:/var/log/redis
$ mkdir log
$ chmod 777 log
$ docker-compose up -d
$ ls -l ./log

total 8
-rw-r--r--. 1 999 render 1259 Aug 26 18:12 redis.log
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

DockerでMariaDBのログファイルをVolumeマウントしてコンテナ起動する方法

./mycnf/log.cnf
[mysqld]
log_output=FILE
general_log=1
general_log_file=/var/log/mysql/general.log
slow_query_log=1
slow_query_log_file=/var/log/mysql/slow_query.log
long_query_time=10
log_error=/var/log/mysql/error.log
docker-compose.yml
version: '3.7'

services:
  db:
    image: mariadb:10.4.7
    environment:
      MYSQL_ROOT_PASSWORD: password
    volumes:
      - ./mycnf:/etc/mysql/conf.d
      - ./log:/var/log/mysql
$ mkdir log
$ chmod 777 log
$ docker-compose up -d
$ ls -l ./log

total 5056
-rw-rw----. 1 999 render    4600 Aug 26 17:31 error.log
-rw-rw----. 1 999 render 5149478 Aug 26 17:31 general.log
-rw-rw----. 1 999 render     385 Aug 26 17:31 slow_query.log

参考URL

MySQLのログについて確認する
MySQLのスロークエリログの時間制御が効かない時はlog-queries-not-using-indexesを確認
MYSQLでスロークエリのログが出力されない

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