20200208のdockerに関する記事は25件です。

とりあえずLaradockで

対象者

とりあえずインフラとかDockerとか慣れてなくてよく分からないけど、手早くLaravelを動かせればいいやーって人向けです。

Laradockとは

公式サイトには「Laradockは、Dockerに基づく完全なPHP開発環境です。」とあります。

Laradock is a full PHP development environment based on Docker.

公式

https://laradock.io/

前提

  • パソコンがある
  • Wifiがある

※あとDockerのいんすとーるとかはやっといてくださーい、gitも要るよー。

手順

1. ローカルでディレクトリを作成

  • 任意のディレクトリ作成
$ mkdir laradock
$ cd laradock/

2. LaradockのリポジトリをClone

GithubからLaradockのリポジトリをクローン

$ git clone https://github.com/Laradock/laradock.git laradock-practice
$ cd laradock-practice/

laradock-practiceの部分は、好きに変えてください。
デフォルトでは「laradock」というディレクトリになりますが、引数に名前を渡すとオリジナルのものができあがります。名前はつけておいたほうがいいっすね。

3. 各種設定 (Laradock)

  • 設定ファイルのコピー作成
$ cp env-example .env

.env(設定ファイル)を開く

$ vi .env

以下の項目を探し、修正してください

修正前

APP_CODE_PATH_HOST=../

修正後

APP_CODE_PATH_HOST=../laradock-project

laradock-projectの部分はこれから作るLaravelプロジェクトの名前にしてください。
LaravelのWebサーバー上で同期するディレクトリを指定しています。
これをちゃんと指定してあげないとアクセス時404エラーになります。

※viコマンドはコマンドでファイルの編集ができます。編集したい箇所で「i」を入力するとインサートモードになり文字が打てるようになります。インサートモードを終了するには「esc」ボタンを入力します。その後、編集したファイルを保存し、終了するには「:」と「wq」を立て続けに入力してください。

修正前

MYSQL_VERSION=latest
MYSQL_DATABASE=default
MYSQL_USER=default
MYSQL_PASSWORD=secret
MYSQL_PORT=3306
MYSQL_ROOT_PASSWORD=root

修正後

MYSQL_VERSION=5.7(別に5.7じゃなくても)
MYSQL_DATABASE=このへんは
MYSQL_USER=じゆうに
MYSQL_PASSWORD=きめちゃってください
MYSQL_PORT=3306
MYSQL_ROOT_PASSWORD=root

※詳しく調べていませんが、mysqlのverをlatestで最新にするとセキュリティ上の問題でうまく接続できないらしいです。僕はちなみに、5.7にしているのにうまく動かない時があり、なぜだろうと思っていたら「5.7t」と記述しており、5.7トンという重さを指定してしまっていたことがありました。

  • laradock-practice/nginx/sites/default.confの変更

これはnginxに関する設定ファイルです。
nginx起動時に表示するファイルなどを指定できるのですが、パスの指定がうまくいっていない場合があるので、修正する必要があれば書き換えましょう。
着目してもらいたいのは一部分です。
以下のような修正前の状態になっていれば、修正してください。
laradock-projectの部分はそれぞれ作成されるLaravelプロジェクトの名前を指定してください。

修正前

root /var/www/public;

修正後

root /var/www/laradock-project/public;

4. コンテナ立ち上げ〜プロジェクトの作成

  • コンテナの立ち上げ
$ docker-compose up -d mysql nginx

docker-compose upコマンドはdocker-compose.ymlに書かれた情報をもとに処理を実行します(コンテナイメージの作成からコンテナの立ち上げなど。引数なしだと記述されている内容すべてが実行されるので、ひとまずDBサーバのmysqlとwebサーバのnginxを指定します。
※-dはデタッチモード(バックグラウンドで起動します)

  • コンテナの起動確認

以下のように立ち上がっていれば大丈夫です。

$ docker ps
CONTAINER ID        IMAGE                COMMAND                  CREATED             STATUS              PORTS                                            NAMES
82e444c0ae67        laradock_nginx       "/bin/bash /opt/star…"   6 seconds ago       Up 4 seconds        0.0.0.0:80-81->80-81/tcp, 0.0.0.0:443->443/tcp   laradock_nginx_1
bd531621d6a3        laradock_php-fpm     "docker-php-entrypoi…"   7 seconds ago       Up 6 seconds        9000/tcp                                         laradock_php-fpm_1
1665d8a83f3b        laradock_workspace   "/sbin/my_init"          8 seconds ago       Up 7 seconds        0.0.0.0:2222->22/tcp                             laradock_workspace_1
d8b22e74c28f        docker:dind          "dockerd-entrypoint.…"   10 seconds ago      Up 8 seconds        2375-2376/tcp                                    laradock_docker-in-docker_1
7af2df7ebe17        laradock_mysql       "docker-entrypoint.s…"   10 seconds ago      Up 8 seconds        0.0.0.0:3306->3306/tcp, 33060/tcp                laradock_mysql_1
  • ワークスペースコンテナに入る

ワークスペースコンテナに入ります。exec、bashでそのコンテナに対してコマンドが叩けるようになります。

ワークスペースとはLaravelの開発を進めるにあたって必要なcomposerなどのツール類が用意された作業スペースみたいなところです。

デフォルトだとrootでワークスペースに入ることになるのですが、後ほどcomposerコマンドを実行した際に、怒られてしまうので、怒られるのが苦手な人は--user=laradockとかでlaradockユーザーとしてワークスペースに入ってください。

$ docker-compose exec --user=laradock workspace bash
.../var/www$
  • Laravelプロジェクトの作成

以下のコマンドで、Laravelのプロジェクトがlaradockディレクトリと同じ階層の中に作成されます。
この実行内容だと最新verのLaravelでプロジェクトが作られます。ver指定したい場合は調べてください。

.../var/www$ composer create-project laravel/laravel laradock-project
...以下略
Application key set successfully.
  • プロジェクト内へ移動

プロジェクト内に移動します。

.../var/www$ cd laradock-project/

5. 各種設定(Laravelプロジェクト)

  • 設定ファイルの修正
.../var/www$ vi .env

以下のように修正

修正前

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel
DB_USERNAME=root
DB_PASSWORD=

修正後

DB_CONNECTION=mysql
DB_HOST=mysql
DB_PORT=3306
DB_DATABASE=このへんはさっきつくった
DB_USERNAME=Laradockの.envの内容と
DB_PASSWORD=合わせてください

.envの内容は先ほど作ったLaradockの方の内容と整合性が取れている必要がありますので合わせておいてください。

7. MySQLの確認

MySQLの設定がうまくいっているかを確認します。

  • MySQLコンテナへ入る

Ctl + dでワークスペースコンテナを出て、MySQLコンテナに入ります。

$ docker-compose exec mysql bash
  • MySQLへ接続

MySQLへ、接続します。defaultの部分は先ほど.envファイルで設定したユーザーネームを指定し、その後パスワードを聞かれるので、同じように.envで設定したパスワードを入力してください。うまくログインできればsqlコマンドが打てる画面に移り変わります。

# mysql -u default -p
Enter password: 
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 4
Server version: 5.7.27 MySQL Community Server (GPL)

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

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

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

mysql> 
  • データベースの確認

うまく設定できていれば、.envで設定したデータベースが作られているはずです

mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| default            |
+--------------------+
2 rows in set (0.02 sec)
  • ユーザーの確認(rootで入った場合のみ行えます)

こちらも設定できていれば、.envで設定したユーザーが登録されていることが確認できますが、mysqlにroot権限で接続している必要があります。

mysql> select User from mysql.user;
+---------------+
| User          |
+---------------+
| default       |
| root          |
| mysql.session |
| mysql.sys     |
| root          |
+---------------+
5 rows in set (0.02 sec)
  • マイグレーションチェック

マイグレーションを実行し、データベースの接続が上手くいっているかを確認します。
Laravelにおけるマイグレーションとはデータベースのバージョン管理機能のことです。

MySQL確認の延長ですが、一旦MySQLコンテナから抜け、ワークスペースコンテナに入り、プロジェクト内に移動して以下のコマンドを実行します。

データベース接続がうまくいっていればマイグレーションが実行されます。

$ php artisan migrate
Migration table created successfully.
Migrating: 2014_10_12_000000_create_users_table
Migrated:  2014_10_12_000000_create_users_table (0.07 seconds)
Migrating: 2014_10_12_100000_create_password_resets_table
Migrated:  2014_10_12_100000_create_password_resets_table (0.06 seconds)
Migrating: 2019_08_19_000000_create_failed_jobs_table
Migrated:  2019_08_19_000000_create_failed_jobs_table (0.03 seconds)

ここまでの流れで上手くいかない場合

  • Laradock、Laravelプロジェクトの各.envファイルの整合性が取れていない可能性

→.envファイルの設定がそれぞれ同じになっているかを確認し、合っていなければ合わせてください。

  • ストレージデータの影響

→laradockでDBを使用する際のストレージがデフォルトで、~/.laradock/dataに設定されています。~/.laradock/dataにmysqlというディレクトリが存在しているので一旦削除してやり直してみてください。

補足
デフォルトで~/.laradock/dataが設定されていると説明しましたが、これでは他にLaradockを使ってプロジェクトを作成した際に同じ場所を参照してしまうので変えた方がいいっぽいです。ここでは詳しく解説はしませんので詳細は調べてください。

  • .envの書き換え等が反映されていない可能性

コンテナを立ち上げたまま、.env等の設定を書き換えただけでは内容が反映されません。
一度、コンテナを起動させ直しましょう。

再起動

$ docker-compose restart

ダメなら停止と削除〜起動

$ docker-compose down
$ docker-compose up -d mysql nginx

8. プロジェクトの起動確認

  • localhostへアクセス

nginxのコンテナを立ち上がっている状態でlocalhostへアクセスします。うまくいっていれば、Laravelのトップページが表示されます。

スクリーンショット 2020-02-08 19.51.35.png

上手くいかない場合

  • Laradockの.envファイルのAPP_CODE_PATH_HOSTの設定が正しくできていない

→プロジェクトが存在するパスをちゃんと記述してあげてください。

  • nginxのdefault.confのパス指定が上手くできていない

 →上記に手順を記載していますので、やり直してください。

  • nginxコンテナが立ち上がっていない

 →docker psコマンドで確認し、立ち上がっていなければ、立ち上げてください。

ここまでくればとりあえずの環境構築は完了です!

あとは煮るなり焼くなり好きにしてください!

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

とりあえずLaradock

対象者

とりあえずインフラとかDockerとか慣れてなくてよく分からないけど、手早くLaravelを動かせればいいやーって人向けです。

Laradockとは

公式サイトには「Laradockは、Dockerに基づく完全なPHP開発環境です。」とあります。

Laradock is a full PHP development environment based on Docker.

公式

https://laradock.io/

前提

  • パソコンがある
  • Wifiがある

※あとDockerのいんすとーるとかはやっといてくださーい、gitも要るよー。

手順

1. ローカルでディレクトリを作成

  • 任意のディレクトリ作成
$ mkdir laradock
$ cd laradock/

2. LaradockのリポジトリをClone

GithubからLaradockのリポジトリをクローン

$ git clone https://github.com/Laradock/laradock.git laradock-practice
$ cd laradock-practice/

laradock-practiceの部分は、好きに変えてください。
デフォルトでは「laradock」というディレクトリになりますが、引数に名前を渡すとオリジナルのものができあがります。名前はつけておいたほうがいいっすね。

3. 各種設定 (Laradock)

  • 設定ファイルのコピー作成
$ cp env-example .env

.env(設定ファイル)を開く

$ vi .env

以下の項目を探し、修正してください

修正前

APP_CODE_PATH_HOST=../

修正後

APP_CODE_PATH_HOST=../laradock-project

laradock-projectの部分はこれから作るLaravelプロジェクトの名前にしてください。
LaravelのWebサーバー上で同期するディレクトリを指定しています。
これをちゃんと指定してあげないとアクセス時404エラーになります。

※viコマンドはコマンドでファイルの編集ができます。編集したい箇所で「i」を入力するとインサートモードになり文字が打てるようになります。インサートモードを終了するには「esc」ボタンを入力します。その後、編集したファイルを保存し、終了するには「:」と「wq」を立て続けに入力してください。

修正前

MYSQL_VERSION=latest
MYSQL_DATABASE=default
MYSQL_USER=default
MYSQL_PASSWORD=secret
MYSQL_PORT=3306
MYSQL_ROOT_PASSWORD=root

修正後

MYSQL_VERSION=5.7(別に5.7じゃなくても)
MYSQL_DATABASE=このへんは
MYSQL_USER=じゆうに
MYSQL_PASSWORD=きめちゃってください
MYSQL_PORT=3306
MYSQL_ROOT_PASSWORD=root

※詳しく調べていませんが、mysqlのverをlatestで最新にするとセキュリティ上の問題でうまく接続できないらしいです。僕はちなみに、5.7にしているのにうまく動かない時があり、なぜだろうと思っていたら「5.7t」と記述しており、5.7トンという重さを指定してしまっていたことがありました。

  • laradock-practice/nginx/sites/default.confの変更

これはnginxに関する設定ファイルです。
nginx起動時に表示するファイルなどを指定できるのですが、パスの指定がうまくいっていない場合があるので、修正する必要があれば書き換えましょう。
着目してもらいたいのは一部分です。
以下のような修正前の状態になっていれば、修正してください。
laradock-projectの部分はそれぞれ作成されるLaravelプロジェクトの名前を指定してください。

修正前

root /var/www/public;

修正後

root /var/www/laradock-project/public;

4. コンテナ立ち上げ〜プロジェクトの作成

  • コンテナの立ち上げ
$ docker-compose up -d mysql nginx

docker-compose upコマンドはdocker-compose.ymlに書かれた情報をもとに処理を実行します(コンテナイメージの作成からコンテナの立ち上げなど。引数なしだと記述されている内容すべてが実行されるので、ひとまずDBサーバのmysqlとwebサーバのnginxを指定します。
※-dはデタッチモード(バックグラウンドで起動します)

  • コンテナの起動確認

以下のように立ち上がっていれば大丈夫です。

$ docker ps
CONTAINER ID        IMAGE                COMMAND                  CREATED             STATUS              PORTS                                            NAMES
82e444c0ae67        laradock_nginx       "/bin/bash /opt/star…"   6 seconds ago       Up 4 seconds        0.0.0.0:80-81->80-81/tcp, 0.0.0.0:443->443/tcp   laradock_nginx_1
bd531621d6a3        laradock_php-fpm     "docker-php-entrypoi…"   7 seconds ago       Up 6 seconds        9000/tcp                                         laradock_php-fpm_1
1665d8a83f3b        laradock_workspace   "/sbin/my_init"          8 seconds ago       Up 7 seconds        0.0.0.0:2222->22/tcp                             laradock_workspace_1
d8b22e74c28f        docker:dind          "dockerd-entrypoint.…"   10 seconds ago      Up 8 seconds        2375-2376/tcp                                    laradock_docker-in-docker_1
7af2df7ebe17        laradock_mysql       "docker-entrypoint.s…"   10 seconds ago      Up 8 seconds        0.0.0.0:3306->3306/tcp, 33060/tcp                laradock_mysql_1
  • ワークスペースコンテナに入る

ワークスペースコンテナに入ります。exec、bashでそのコンテナに対してコマンドが叩けるようになります。

ワークスペースとはLaravelの開発を進めるにあたって必要なcomposerなどのツール類が用意された作業スペースみたいなところです。

デフォルトだとrootでワークスペースに入ることになるのですが、後ほどcomposerコマンドを実行した際に、怒られてしまうので、怒られるのが苦手な人は--user=laradockとかでlaradockユーザーとしてワークスペースに入ってください。

$ docker-compose exec --user=laradock workspace bash
.../var/www$
  • Laravelプロジェクトの作成

以下のコマンドで、Laravelのプロジェクトがlaradockディレクトリと同じ階層の中に作成されます。
この実行内容だと最新verのLaravelでプロジェクトが作られます。ver指定したい場合は調べてください。

.../var/www$ composer create-project laravel/laravel laradock-project
...以下略
Application key set successfully.
  • プロジェクト内へ移動

プロジェクト内に移動します。

.../var/www$ cd laradock-project/

5. 各種設定(Laravelプロジェクト)

  • 設定ファイルの修正 .../var/www$ vi .env

以下のように修正

修正前

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel
DB_USERNAME=root
DB_PASSWORD=

修正後

DB_CONNECTION=mysql
DB_HOST=mysql
DB_PORT=3306
DB_DATABASE=このへんはさっきつくった
DB_USERNAME=Laradockの.envの内容と
DB_PASSWORD=合わせてください

.envの内容は先ほど作ったLaradockの方の内容と整合性が取れている必要がありますので合わせておいてください。

7. MySQLの確認

MySQLの設定がうまくいっているかを確認します。

  • MySQLコンテナへ入る

Ctl + dでワークスペースコンテナを出て、MySQLコンテナに入ります。

$ docker-compose exec mysql bash
  • MySQLへ接続

MySQLへ、接続します。defaultの部分は先ほど.envファイルで設定したユーザーネームを指定し、その後パスワードを聞かれるので、同じように.envで設定したパスワードを入力してください。うまくログインできればsqlコマンドが打てる画面に移り変わります。

# mysql -u default -p
Enter password: 
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 4
Server version: 5.7.27 MySQL Community Server (GPL)

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

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

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

mysql> 
  • データベースの確認

うまく設定できていれば、.envで設定したデータベースが作られているはずです

mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| default            |
+--------------------+
2 rows in set (0.02 sec)
  • ユーザーの確認(rootで入った場合のみ行えます)

こちらも設定できていれば、.envで設定したユーザーが登録されていることが確認できますが、mysqlにroot権限で接続している必要があります。

mysql> select User from mysql.user;
+---------------+
| User          |
+---------------+
| default       |
| root          |
| mysql.session |
| mysql.sys     |
| root          |
+---------------+
5 rows in set (0.02 sec)
  • マイグレーションチェック

マイグレーションを実行し、データベースの接続が上手くいっているかを確認します。
Laravelにおけるマイグレーションとはデータベースのバージョン管理機能のことです。

MySQL確認の延長ですが、一旦MySQLコンテナから抜け、ワークスペースコンテナに入り、プロジェクト内に移動して以下のコマンドを実行します。

データベース接続がうまくいっていればマイグレーションが実行されます。

$ php artisan migrate
Migration table created successfully.
Migrating: 2014_10_12_000000_create_users_table
Migrated:  2014_10_12_000000_create_users_table (0.07 seconds)
Migrating: 2014_10_12_100000_create_password_resets_table
Migrated:  2014_10_12_100000_create_password_resets_table (0.06 seconds)
Migrating: 2019_08_19_000000_create_failed_jobs_table
Migrated:  2019_08_19_000000_create_failed_jobs_table (0.03 seconds)

ここまでの流れで上手くいかない場合

  • Laradock、Laravelプロジェクトの各.envファイルの整合性が取れていない可能性

→.envファイルの設定がそれぞれ同じになっているかを確認し、合っていなければ合わせてください。

  • ストレージデータの影響

→laradockでDBを使用する際のストレージがデフォルトで、~/.laradock/dataに設定されています。~/.laradock/dataにmysqlというディレクトリが存在しているので一旦削除してやり直してみてください。

補足
デフォルトで~/.laradock/dataが設定されていると説明しましたが、これでは他にLaradockを使ってプロジェクトを作成した際に同じ場所を参照してしまうので変えた方がいいっぽいです。ここでは詳しく解説はしませんので詳細は調べてください。

  • .envの書き換え等が反映されていない可能性

コンテナを立ち上げたまま、.env等の設定を書き換えただけでは内容が反映されません。
一度、コンテナを起動させ直しましょう。

再起動

$ docker-compose restart

ダメなら停止と削除〜起動

$ docker-compose down
$ docker-compose up -d mysql nginx

8. プロジェクトの起動確認

  • localhostへアクセス

nginxのコンテナを立ち上がっている状態でlocalhostへアクセスします。うまくいっていれば、Laravelのトップページが表示されます。

スクリーンショット 2020-02-08 19.51.35.png

上手くいかない場合

  • Laradockの.envファイルのAPP_CODE_PATH_HOSTの設定が正しくできていない

→プロジェクトが存在するパスをちゃんと記述してあげてください。

  • nginxのdefault.confのパス指定が上手くできていない

 →上記に手順を記載していますので、やり直してください。

  • nginxコンテナが立ち上がっていない

 →docker psコマンドで確認し、立ち上がっていなければ、立ち上げてください。

ここまでくればとりあえずの環境構築は完了です!

あとは煮るなり焼くなり好きにしてください!

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

【docker】docker試煉日(1)

目的

熟悉與掌握docker用法,實作出內部測試環境與降低多環境需求之成本

Docker 架構

Docker 包括三個基本元素:

  • 鏡像(Image):Docker 鏡像(Image),就相當於是一個 root 文件系統。比如官方鏡像 ubuntu:16.04 就包含了完整的一套 Ubuntu16.04 最小系統的 root 文件系統。
  • 容器(Container):鏡像(Image)和容器(Container)的關係,就像是VM安裝概念一樣,鏡像是靜態的定義,容器是鏡像運行時的實體。容器可以被創建、啟動、停止、刪除、暫停等,隨時可依據需求啟動一個你所需要的容器(Ubuntu CentOS等)。
  • 倉庫(Repository):倉庫可視為一個程式碼控制中心,用來保存鏡像。 Docker 使用客戶端-服務器 (C/S) 架構模式,使用遠端API來管理和創建Docker容器。

Docker 容器通過 Docker 鏡像來創建。

576507-docker1.png

安裝方法

https://www.runoob.com/docker/centos-docker-install.html

起手式

當安裝完成docker 都要先來跑跑看是否已經能正常運行
接下來我們就開始運行我們的第一個Docker吧

P-MP15:~ puwu$ docker run ubuntu:15.10 /bin/echo "Hello world"
Unable to find image 'ubuntu:15.10' locally
15.10: Pulling from library/ubuntu
7dcf5a444392: Pull complete
759aa75f3cee: Pull complete
3fa871dc8a2b: Pull complete
224c42ae46e7: Pull complete
Digest: sha256:02521a2d079595241c6793b2044f02eecf294034f31d6e235ac4b2b54ffc41f3
Status: Downloaded newer image for ubuntu:15.10
Hello world

好的,我們今天終於完成第一項任務,可以讓我的使用docker 幫我們印出Hello world
這是多麼不容易的事情(?)

接下來讓我們了解一下啟動時參數使用的用途

使用docker鏡像nginx:latest以後台模式啟動一个容器,并將容器命名為mynginx。

docker run --name mynginx -d nginx:latest  

使用鏡像nginx:latest以後臺模式啟動壹個容器,將容器的80端口映射到主機的80端口,主機的目錄/data映射到容器的/data

docker run -p 80:80 -v /data:/data -d nginx:latest  

使用鏡像nginx:latest以互動模式啟動一個容器,在容器內執行/bin/bash命令。

P-MP15:~ puwu$ docker run -it nginx:latest /bin/bash
root@fecf799928e4:/# 

更多詳細的參數可以參考HERE

背景服務與操作可參考這篇(後續再補上)HERE

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

コード書いたことないPdMやPOに捧ぐ、Rails on Dockerハンズオン vol.4 - Static pages -

この記事はなにか?
この記事はが社内のプログラミング未経験者、ビギナー向けに開催しているRuby on Rails on Dockerハンズオンの内容をまとめたものです。ていうかこの記事を基にそのままハンズオンします。ハンズオンは
1回の内容は喋りながらやると大体40~50分くらいになっています。お昼休みに有志でやっているからです。
現在進行形なので週1ペースで記事投稿していけるように頑張ります。
ビギナーの方のお役にたったり、同じように有志のハンズオンをしようとしている人の参考になれば幸いです。

他のハンズオンへのリンク
Vol.1 - Introduction -
Vol.2 - Hello, Rails on Docker -
Vol.3 - Scaffold, RESTful, MVC -
・ Vol.4 - Static pages -

$, #, >について
$: ローカルでコマンドを実行するときは、頭に$をつけています。
#: コンテナの中でコマンドを実行するときは、頭に#をつけています。
>: Rails console内でコマンド(Rubyプログラム)を実行するときは、頭に>をつけています。

はじめに

第4回目は、Static pages(静的なページ)を作ることにチャレンジしてみましょう!
そもそも静的なページとは、アクセスするユーザーや時間帯に関係なく同じコンテンツが表示されるページのことです。対義語は動的なページは例えばマイページのようなユーザーごとにコンテンツが変わってくるようなページのことですね。
静的なページのみで構築されているサイトを『Webサイト』、動的なページも存在するサイトを『Webアプリケーション』と呼び分けたりします。

今回は、静的なトップページを作っていきますよ!

Railsの初期設定

とその前に。
今回は日本で使われ日本で運用するアプリを想定するのですが、Railsはデフォルトでは英語が使われていたりUTC(協定世界時)が使われていたりと、そのまま日本でサービス展開しようとすると面倒な部分があります。
最初にこの設定をローカライズ(日本化)しておきましょう!

Timezoneの設定

Dockerfiledocker-compose.ymlでコンテナのタイムゾーンは日本に設定していましたね(TZ=Asia/Tokyo)。Railsアプリも同じように設定してあげます。

config/application.rb
...
class Application < Rails::Application
  ...
  config.time_zone = 'Tokyo'
  config.active_record.default_timezone = :local
  ...
end

time_zoneはRailsアプリが時間を扱うときにどのtimezoneで動くかを指定する設定でデフォルトだとUTCになってます。例えばTime.currentで現在の時間を表示できたりするのですが、この結果をどのtimezoneで表示するかがこの設定で決まります。この後、Modelを保存したりするときなどもその作成日時などがこのtimezoneで設定されます。

active_record.default_timezoneはDBに書き込まれている時間をどのtimezoneとして扱うかの設定です。

言語の設定

Railsではエラーメッセージなどが準備されているのですが、デフォルトでは英語で定義されています。日本でサービス展開する場合「?」となってしまうので、日本語化します。

config/application.rb
...
class Application < Rails::Application
  ...
  config.i18n.default_locale = :ja
  ...
end

この設定で、config/locales/にあるyamlファイルの中からja:で定義されている文字列が表示されるようになるんです。日本語版のファイルを作ってくださっている方がいらっしゃいますのでベースとして利用させていただきましょう。

rails-i18n/ja.yml at master · svenfuchs/rails-i18n

こちらのファイルをconfig/locales/ja.ymlとして保存することで日本語化完了です!

ちなみに『i18n』は『internationalization』のことで『国際化』と訳されます。アクセスする国によって時間や言語を変えたりすることです(今回は日本オンリーに対応ですが...笑い)。頭の『i』とお尻の『n』の間に18文字あるので『i18n』と表現します。技術系だとこういうの最近多いっすよね?

Bootstrap

まだまだ静的なページの作成には入りませんよ!次は、Bootstrapを使えるようにしていきましょう。

BootstrapはTwitter社が開発したCSSフレームワークです。
レスポンシブデザインに標準で対応されており、CSSだけでなくJavascript(jQuery)も含まれています。
多くの人に使われているためインターネット上に情報があふれていますので初心者にも安心です。一方で、多くの人に使われているため似通ったデザインになってしまうので少し慣れてくると敬遠されがちな印象です。

Boostrapをyarnでインストール

Rails5まではgemでインストールするのが主流だったと思うのですが、Rails6ではWebpackerが必須になったこともありパッケージマネージャー経由でインストールするのが主流になっていくと思われますのでその方法で。

まだコンテナを立ち上げていなかったですね。それではコンテナを立ち上げてコンテナの中でコマンドを実行していきましょう!

$ docker-compose up -d
$ docker-compose exec web ash
# yarn add bootstrap jquery popper.js

これでBootstrap関連のライブラリをインストールできたのでRailsアプリで使えるように設定していきます。

まずapplication.jsでBootstrapを読み込みます。

app/javascript/packs/application.js
...
require("bootstrap")
...

次にBootstrapをSCSSで読み込むためにapplication.cssapplication.scssにリネームします。

# mv app/assets/stylesheets/application.css app/assets/stylesheets/application.scss

ファイルの中身はCSSの記法で書かれているものなので一度全て削除して以下のように書き換えましょう。

app/assets/stylesheets/application.scss
@import 'bootstrap/scss/bootstrap';

最後にBootstrapと依存関係にあるjQuerypopper.jsの設定を追加してあげます。

config/webpack/environment.js
const { environment } = require('@rails/webpack')

const webpack = require('webpack')

environment.plugins.append('Provide', new webpack.ProvidePlugin({
    $: 'jquery',
    jQuery: 'jquery',
    Popper: ['popper.js', 'default']
}))

module.exports = environment

やばいですね。たったこれだけでBootstrapの準備が整いました。Bootstrapで用意されているあらゆるスタイルシートやjavascriptが利用可能になっちゃいました。
BootstrapがどんなことができるかはBootstrap公式のDocumentationを見るのが一番いいと思っているのですが、これだけの表現が上の設定でできるようになってしまったのです。

Topページを作成する

いよいよ静的なページを作っていきます。
静的なページではModelのような動的なものはいらないのでMVCのVCがあればOKですね。
VC(ルーティングも!)はrails generate controllerコマンドで作成することができます。今回はstatic_pages controllerでhome actionを作ってみましょう。

# rails generate controller static_pages home

rails generate controller NAME [action action]の形式でコマンドを実行できます。actionは複数指定可能です。
慣習的にNAMEは複数形を用います。

このコマンドだけですでにhttp://localhost:3000/static_pages/homeへのRouting, Controller#Action, Viewが出来上がっているのでアクセスできるようになっていますね。
image.png

このコマンドで作成・更新されたファイルの中身をちょっとみていきましょう!

Routing

config/routes.rb
Rails.application.routes.draw do
  get 'static_pages/home'
end

get 'static_pages/home'の行が追加されてます。前回はresoucesメソッドを使ったルーティングの指定の仕方でしたが、このように一つ一つのルートを定義することもできるのです。
これだけで「static_pages/homeのパスにgetメソッドできたリクエストをstatic_pages controllerのhome actionにルーティングする」ことを定義しています。

Controller

app/controllers/static_pages_controller.rb
class StaticPagesController < ApplicationController
  def home
  end
end

homeアクションが定義されていますね。特段処理はないので、app/views/static_pages/home.html.erbをレンダリングするのみです。

View

app/views/static_pages/home.html.erb
<h1>StaticPages#home</h1>
<p>Find me in app/views/static_pages/home.html.erb</p>

app/views/static_pages/home.html.erbファイルが生成されています。
中身も先ほどhttp://localhost:3000/static_pages/homeと同じものが表示されていることがわかりますね。

ルートパスをTopページにする

現状ルートパス(http://localhost:3000)にアクセスするとHello worldのページが表示されています。本当はこのURLにアクセスしたときにTopページを表示させたいのでルーティングの設定を更新していきます。

config/routes.rb
Rails.application.routes.draw do
  root 'static_pages#home'
end

ルートパスの設定の仕方は`root '[controller_name]#[action_name]'とするだけです!

試しにhttp://localhost:3000/にアクセスしてみてください。Topページが表示されるようになりましたね。
またhttp://localhost:3000/static_pages/homeにもう一度アクセスしてみましょう。get 'static_pages/home'を削除してるので以下のようにそんなルートないよとエラーになります。
image.png

Bootstrapでページを装飾する

にしても味気ないページですよね。ということでBootstrapのCSSを使いながらいい感じのページに装飾していきましょう!

CSSはHTMLのスタイル(色とか大きさとか)をまとめた変数みたいなものです。HTMLタグ(<h1>とか<p>とか)にclass属性をつけることでそのスタイルが適用されます。SCSSはCSSを書きやすくしたものです(なので本質はCSS)。

<h1 class="hoge">Hello.</h1>
.hoge {
  font-size: 32px;
  color: red;
}

これで32pxの赤文字で"Hello."が表示されるようになるってイメージですね。

Topページのコンテンツを装飾する

ではまず、app/views/static_pages/home.html.erbを更新していい感じのTopページを作っていきましょう。

app/views/static_pages/home.html.erb
<div class="jumbotron mb-0">
  <div class="container text-center">
    <h1>Welcome to Sample App.</h1>
    <h2>Twitterみたいなアプリです。</h2>
    <%= link_to "Sign up now!", "#", class: "btn btn-lg btn-primary mt-5" %>
  </div>
</div>

ここに出てくるjumbotron, container, text-center, btn, btn-lg, btn-primary, mt-5はすべてBootstrapのclassです。Bootstrapのclassの使い方はしっかりと公式のDocumantationがあるのでそちらをみてみましょう。実際に自分で何かを作るとなるとこういう公式ドキュメントと向き合うことが一番の近道だったりするのでここでは説明しないっす。

1つだけ、ERBコードがありますね。

app/views/static_pages/home.html.erb
<%= link_to "Sing up now!", "#", class: "btn btn-lg btn-primary mt-5" %>

link_toはリンクを作るためのメソッドです。HTML的にいえば<a>タグを作ってくれるということです。
link_to [表示文字], [リンク先], [options]の構文になってまして、今回の例だと以下のような<a>タグが出来上がります。

<a href="#" class="btn btn-lg btn-primary mt-5">Sign up now!</a>

リンク文字列やリンク先などに変数を指定でき(例えばルートパスはroot_pathが変数として割り当てられている)るのでERBファイルでリンクを作成する場合多用するコードなので覚えておきましょう。
リンク先として#を指定していますが、まだ遷移先のページを作成していないので仮置きしているだけです(#を指定しておくとリンクを押しても今いるページから遷移しないです。)

編集ができたらトップページをリロードしてみましょう。以下のページになっていれば成功です!
image.png

ふへー。ぽくなってきましたね。

ただHeaderとかFooterとかも欲しくなってきました。

ヘッダーを装飾する

突然ですが、ブラウザでトップページのソースコードをみてみましょう。(右クリックで「ページのソースを表示する」を選択するとみれます。)
app/views/static_pages/home.html.erbに全く書いた記憶がないもの、例えば<head>タグとかがありますよね。

Topページ
<!DOCTYPE html>
<html>
  <head>
    <title>App</title>
    <meta name="csrf-param" content="authenticity_token" />
<meta name="csrf-token" content="3702TRDf2wiVe1ErXV1NWoksR1sWw1+LO31miZuonos9K8zOmMwJ+VotF3ZXCPRP+g9IiKoZovoA8HpIjLjqmA==" />


    <link rel="stylesheet" media="all" href="/assets/application.debug-9519f0a34796e622c941135a612890361ca8a44f05d715927aaa1575ce29c235.css" data-turbolinks-track="reload" />
    <script src="/packs/js/application-923746675d9ea6d857bd.js" data-turbolinks-track="reload"></script>
  </head>

  <body>
    <div class="jumbotron">
      <div class="container text-center">
        <h1>Welcome to Sample App.</h1>
        <h2>Twitterみたいなアプリです。</h2>
        <a class="btn btn-lg btn-primary mt-5" href="#">Sign up now!</a>
      </div>
    </div>
  </body>
</html>

これはなんでしょう?:thinking:

実はRailsアプリケーションではViewファイルの中でもLayoutと呼ばれるやつがいます。こいつはなにかというと、ページで共通な部分を表現してくれるものです。デフォルトでapp/views/layouts/application.html.erbのレイアウトファイルが利用されています。ちょっと中を覗いてみましょう。

app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
  <head>
    <title>App</title>
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>

    <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
    <%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
  </head>

  <body>
    <%= yield %>
  </body>
</html>

ERBコードが入っているので少し表現は違いますが、Topページのソースコードと似た構文をしていることがわかります。実は最終的に表示されているHTMLはapp/views/static_pages/home.html.erbがこのapp/views/layouts/application.html.erb<%= yield %>に代入されたものです。

少しだけapp/views/layouts/application.html.erbの中身をご紹介。
まず全体の構文ですが、HTMLの基本の構文が記述されていますね。

<!DOCTYPE html>
<html>
  <head>
    ...
  </head>

  <body>
    ...
  </body>
</html>

<body>の中のyeildについては上で話した通りで、actionのviewファイルの中身が代入されるようになっています。

<head>の中はどうでしょうか?

title

titleはブラウザのタブのところに表示される文字列です。今だとAppと指定されているのでそれが表示されていますね。

scrf_meta_tags

CSRF(Cross-Site Request Forgery)対策のための記述です。今回のTopページではあまり活躍しませんが、POSTリクエストを飛ばすページで活躍します。簡単にいえば、正しいユーザーからのリクエストなのかというリクエストの真正性(Authenticity)を検証するために必要なことを開発者が意識せずともRailsがカバーしてくれるようになるとのこと。

csp_meta_tag

CSP(Content Security Policy)に必要なタグを生成してくれるコードです。CSPはXSS(Cross Site Scripting)を防ぐためのもので、コンテンツの提供元や取得方法を制限する手法です。別途、制限を設定することで動作するようになります。ここでは紹介程度。

stylesheet_link_tag

stylesheetの読み込み。app/assets/stylesheets/application.css[scss]を読み込んでくれている。

javascript_pack_tag

javascriptの読み込み。app/javascript/packs/application.jsを読み込んでくれている。

ってな具合です。

さて、長々と話してしまいましたが、本当に言いたかったことは『ヘッダーやフッターは各ページ個別のものではなく、サイトで共通のものであるはずなので、レイアウトファイルに記述するんだよ』ということです。

では、ヘッダーをapp/views/layouts/application.html.erbに記述します!

app/views/layouts/application.html.erb
...
<body>
  <header class="navbar navbar-dark navbar-expand bg-dark">
    <div class="container">
      <%= link_to "sample app", root_path, class: "navbar-brand" %>
      <ul class="navbar-nav">
        <li class="nav-item"><%= link_to "Home", root_path, class: "nav-link" %></li>
        <li class="nav-item"><%= link_to "Sign in", "#", class: "nav-link" %></li>
      </ul>
    </div>
  </header>

  <%= yield %>
</body>
...

このあたりもBootstrapの公式ドキュメントを参考に記述してみました => Navbar · Bootstrap

Topページにアクセスしてみましょう。以下のようになっていれば成功です!
image.png
おー、どんどんぽくなってきましたね!

フッターを装飾する

最後にフッターも付けちゃいましょう!今回はコピーライトを書いているくらいのフッターをば。

フッターは<footer>タグを使います。

app/views/layouts/application.html.erb
...
<body>
  ...
  <%= yield %>

  <footer class="bg-dark">
    <p class="text-center text-white py-2 mb-0">(c) Hoge Inc. All Rights Reserved.</p>
  </footer>
</body>
...

特別難しいところはありませんね。再度Topページにアクセスします。
image.png
おー、ぽい。ぽいのですが、ブラウザの下の方に余白ができているのが気になりますね...
表示するコンテンツがブラウザの縦サイズに合わない場合は、フッターは画面の最下部に表示されるようにしたい...

ということで少しCSSをいじってみましょう。

app/assets/stylesheets/application.scss
@import 'bootstrap/scss/bootstrap';

body {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}

footer {
  margin-top: auto;
}

本当はfooter用のファイルを作ってappliction.scss@importする方が整理されるのでいいのですが、今回は分量も少ないのでひとまずapplication.scssに記述します。

これで何をしているかというと、まずbodyタグに対してdisplay: flex;をあててます。これはFlexboxという要素をきれいに横並びや縦並びでレイアウトしてくれるレイアウトモジュールです。
flex-direction: column;と定義しているので縦方向に要素を並べてくれます。
そして、min-height: 100vh;を定義しているのでbodyタグで囲まれた要素は最低ブラウザいっぱいの高さを持つ要素に指定されたことになります。

今、bodyタグの中にはheader, <%= yeild %>の中のdiv, footerの3つの要素が同じレベルに存在しています。これを縦方向に並べていることになるんですね。
この時、Flexboxの親要素(今回だとbody)をコンテナ、子要素(今回だとheader, div, footer)をアイテムと呼びます。

footerにはmargin-top: auto;が定義されています。これは親要素に対して、この要素の上部に最大限のmargin(余白)を付与することを表していて、簡単に言うと親要素の一番下に配置するってことになります。
今、親要素のbodyは縦に最小で100vhの高さを持ちます。コンテンツが足りない場合はbodyは100vhの高さになるのでfooterは100vhの一番下に位置します。コンテンツが100vh以上の場合はbodyheader, div, footerの3要素の高さの合計が高さになるので、footerは自然な形でdivのすぐ下に配置されることになります。

さてさて、ではTopページをリロードしてみてください!
image.png
とてもいい感じになりましたね!

スマートフォンに対応する

PCだといい感じに表示されるのですが、スマートフォンだと実はとても見にくい状態になっています。ChromeのDeveloper toolsなどで確かめてみてください(Chrome DevTools での Device Mode によるモバイル端末のシミュレート

BootstrapはもともとResponsive Design(ブラウザの横幅に合わせてCSSが変わる)に対応しているので、Viewport設定の1行をheadタグ内に挿入するだけでいい感じのデザインになります。

app/views/layouts/application.html.erb
...
<head>
  ...
  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
  ...
</head>
...

呪文みたいな感じで書いちゃうことが多いのですが、意味は「もう逃げない。HTMLのviewportをちゃんと理解する - Qiita」の記事が参考になりました。

スマホモードでリロードしてみましょう。
image.png
文字の大きさもいい感じになりましたね。

Google Fontsでフォントを変える

んー。デザインはよくなってきたけどフォントがデフォルトっぽくて気になるなぁ...
フォントはこのアプリを使うユーザーの端末にフォントがインストールされていないと使えなかったりするのであんまり冒険できないところだったりします。アプリケーションの中でフォントを配布してもいいのですが、まぁ若干面倒ですね。
そこで便利なのがGoogle Fontsです。『Webフォント』と呼ばれますが、インターネット上ですでに公開されているため端末のインストールなしで利用でき、どの端末からでも同じフォントで表現ができるようになります。

app/assets/stylesheets/application.scssでGoogle Fontsの中からNoto Sans JPをインポートしてbodyfont-familyに指定するだけなんですね簡単です。

app/assets/stylesheets/application.scss
@import 'bootstrap/scss/bootstrap';
// Google Fontsから"Noto Sans JP"をインポート
@import url('https://fonts.googleapis.com/css?family=Noto+Sans+JP&display=swap');

body {
  // "Noto Sans JP"を全体のデフォルトフォントに指定
  font-family: 'Noto Sans JP', sans-serif!important;
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}

footer {
  margin-top: auto;
}

少し特別な書き方として、font-familyのところで!importantと末尾に記述してます。
!importantはCSSの適用優先度を決めるための記述で、最優先で反映させるものに使います。つまり、他にfont-familyを記述したとしてもこいつが適用されるってことです。
Bootstrapでもfont-familyを定義してくれていたりするので、こっちを最優先にするために記述してます。

Google Fontsのサイトでお好きなフォントを探してみて適用してみてください!日本語フォントの場合は、『Language』で『Japanese』を選択すれば日本語フォントありに絞られます。『+』で使用フォントを追加するとインポートの仕方とかも教えてくれるので気軽に使えます。もっと詳しくはこちらの記事から↓
【2019年版】Google Fontsの使い方:初心者向けに解説!

まとめ

今回は、Railsの初期設定とBootstrapのインストール、Topページのデザインをやってみました。
静的なページではありますが、コーディングした内容が反映されてどんどん変わっていくのを実感する楽しみを感じてもらえたんじゃないかと思います。

次回は今回触れていなかったModelが主役です。Modelの作成からModelに用意されているメソッドを使ってデータの作成や更新を遊んでみようと思います。

では、次回も乞うご期待!ここまでお読みいただきありがとうございました!

Reference

P.S. 間違っているところ、抜けているところ、説明の仕方を変えるとよりわかりやすくなるところなどありましたら、優しくアドバイスいただけると助かります。

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

CentOS8にDockerをインストール。名前解決できなかったのが解消した。

はじめに

さっき書いた ESXi6.7にCentOS8を最小構成で構築 の作業後に、dockerを入れたけど、dnfでこけてしまった。
pingでIP直打ちは外に通るのに、名前解決ができない。が解決したので、記録として残します。
コンテナ起動時に --net=host をやりたくなかったので情報をさがしてみました。

解決方法だけ記載

ホスト側で、NAPTの設定したら動いた。

# firewall-cmd --add-masquerade --permanent
# firewall-cmd --reload

前提条件

最小構成でインストールしたからでしょうか?nftablesは起動しておらず、firewalldが動作し、裏でiptablesが動いている状態でした。

nftablesは停止している。

# systemctl status nftables
● nftables.service - Netfilter Tables
   Loaded: loaded (/usr/lib/systemd/system/nftables.service; disabled; vendor preset: disabled)
   Active: inactive (dead)
     Docs: man:nft(8)

firewalldが動いている。

# systemctl status firewalld
● firewalld.service - firewalld - dynamic firewall daemon
   Loaded: loaded (/usr/lib/systemd/system/firewalld.service; enabled; vendor preset: enabled)
   Active: active (running) since Sat 2020-02-08 16:28:47 JST; 27min ago
     Docs: man:firewalld(1)
 Main PID: 1182 (firewalld)
    Tasks: 2 (limit: 23585)
   Memory: 38.9M
   CGroup: /system.slice/firewalld.service
           mq1182 /usr/libexec/platform-python -s /usr/sbin/firewalld --nofork --nopid

iptablesが動いている。

# iptables -L
Chain INPUT (policy ACCEPT)
target     prot opt source               destination

Chain FORWARD (policy DROP)
target     prot opt source               destination
DOCKER-USER  all  --  anywhere             anywhere
DOCKER-ISOLATION-STAGE-1  all  --  anywhere             anywhere
ACCEPT     all  --  anywhere             anywhere             ctstate RELATED,ESTABLISHED
DOCKER     all  --  anywhere             anywhere
ACCEPT     all  --  anywhere             anywhere
ACCEPT     all  --  anywhere             anywhere

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination

Chain DOCKER (1 references)
target     prot opt source               destination

Chain DOCKER-ISOLATION-STAGE-1 (1 references)
target     prot opt source               destination
DOCKER-ISOLATION-STAGE-2  all  --  anywhere             anywhere
RETURN     all  --  anywhere             anywhere

Chain DOCKER-ISOLATION-STAGE-2 (1 references)
target     prot opt source               destination
DROP       all  --  anywhere             anywhere
RETURN     all  --  anywhere             anywhere

Chain DOCKER-USER (1 references)
target     prot opt source               destination
RETURN     all  --  anywhere             anywhere

dockerを使えるようにする

パッケージのインストール

インストール時のパッケージのバージョンではねられるので、--nobestをつけてインストールする。

# dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
# dnf install --nobest docker-ce docker-ce-cli containerd.io

サービス設定しておく

# systemctl enable docker
# systemctl start docker

テストで hello-world を立ち上げてみる。
うまくいけば、下のように、 Hello from Docker! が表示される

# docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
1b930d010525: Pull complete
Digest: sha256:9572f7cdcee8591948c2963463447a53466950b3fc15a247fcad1917ca215a2f
Status: Downloaded newer image for hello-world:latest

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.
    (amd64)
 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/

CentOS8のイメージで起動してみる

systemctl を動作させるために、 /sbin/init で走らせないとダメぽい。

# docker pull centos:centos8
# docker run --privileged -it -d --name centos8_check centos:centos8 /sbin/init
# docker exec -it centos8_check /bin/bash
[root@fced0781866f /]#

ログインできた。

トラブル発生

名前解決できない!

コンテナ内でepelパッケージのインストールしてみようとしたら、できない。

[root@fced0781866f /]# dnf install epel-release
Failed to set locale, defaulting to C.UTF-8
CentOS-8 - AppStream                                           0.0  B/s |   0  B     00:05
Failed to download metadata for repo 'AppStream'
Error: Failed to download metadata for repo 'AppStream'

コンテナ→GooglePublicDNSはIP直打ちで到達できる。

[root@fced0781866f /]# ping 8.8.8.8
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=52 time=5.60 ms

コンテナのDNSはLAN内のサーバを参照している

[root@fced0781866f /]# cat /etc/resolv.conf
# Generated by NetworkManager
search prosper2.net
nameserver 10.254.10.241

コンテナ→DNSへのpingは通る

[root@fced0781866f /]# ping 10.254.10.241
PING 10.254.10.241 (10.254.10.241) 56(84) bytes of data.
64 bytes from 10.254.10.241: icmp_seq=1 ttl=127 time=0.467 ms

のに、名前解決できない

[root@fced0781866f /]# ping dns.google
ping: dns.google: Name or service not known

なぜだ。。。

解消できた

ホスト側で、NAPTの設定したら動いた。

# firewall-cmd --add-masquerade --permanent
# firewall-cmd --reload

ちゃんと dnf install epel-release できた。

# dnf install epel-release
Failed to set locale, defaulting to C.UTF-8
CentOS-8 - AppStream                                           4.7 MB/s | 6.4 MB     00:01
CentOS-8 - Base                                                4.8 MB/s | 5.0 MB     00:01
CentOS-8 - Extras                                              6.2 kB/s | 2.1 kB     00:00
Dependencies resolved.
===============================================================================================
 Package                   Architecture        Version               Repository           Size
===============================================================================================
Installing:
 epel-release              noarch              8-5.el8               extras               22 k

Transaction Summary
===============================================================================================
Install  1 Package

Total download size: 22 k
Installed size: 30 k
Is this ok [y/N]: y
Downloading Packages:
epel-release-8-5.el8.noarch.rpm                                915 kB/s |  22 kB     00:00
-----------------------------------------------------------------------------------------------
Total                                                           36 kB/s |  22 kB     00:00
warning: /var/cache/dnf/extras-cbfb2f07b0021b7e/packages/epel-release-8-5.el8.noarch.rpm: Header V3 RSA/SHA256 Signature, key ID 8483c65d: NOKEY
CentOS-8 - Extras                                              1.6 MB/s | 1.6 kB     00:00
Importing GPG key 0x8483C65D:
 Userid     : "CentOS (CentOS Official Signing Key) <security@centos.org>"
 Fingerprint: 99DB 70FA E1D7 CE22 7FB6 4882 05B5 55B3 8483 C65D
 From       : /etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
Is this ok [y/N]: y
Key imported successfully
Running transaction check
Transaction check succeeded.
Running transaction test
Transaction test succeeded.
Running transaction
  Preparing        :                                                                       1/1
  Installing       : epel-release-8-5.el8.noarch                                           1/1
  Running scriptlet: epel-release-8-5.el8.noarch                                           1/1
  Verifying        : epel-release-8-5.el8.noarch                                           1/1

Installed:
  epel-release-8-5.el8.noarch

Complete!

追記

インストール時に --nobest でとりあえずは大丈夫だったけど、依存関係のエラーが出続けてしまっていた。

# dnf update
メタデータの期限切れの最終確認: 0:53:51 時間前の 2020年02月08日 16時38分36秒 に実施しました。
エラー:
 問題: package docker-ce-3:19.03.5-3.el7.x86_64 requires containerd.io >= 1.2.2-3, but none of the providers can be installed
  - cannot install the best update candidate for package docker-ce-3:18.09.1-3.el7.x86_64
  - package containerd.io-1.2.10-3.2.el7.x86_64 is excluded
  - package containerd.io-1.2.2-3.3.el7.x86_64 is excluded
  - package containerd.io-1.2.2-3.el7.x86_64 is excluded
  - package containerd.io-1.2.4-3.1.el7.x86_64 is excluded
  - package containerd.io-1.2.5-3.1.el7.x86_64 is excluded
  - package containerd.io-1.2.6-3.3.el7.x86_64 is excluded
(インストール不可のパッケージをスキップするには、'--skip-broken' を追加してみてください または、'--nobest' を追加して、最適候補のパッケージのみを使用しないでください)

これは嫌なので、無理やりRPMを入れてしまおう。まず、どこにあるか調べる。

# grep stable /etc/yum.repos.d/docker-ce.repo
[docker-ce-stable]
baseurl=https://download.docker.com/linux/centos/7/$basearch/stable

そうか、そもそもCentOS7がターゲットなのか。。。
ここから拾ってこよう。

# dnf update https://download.docker.com/linux/centos/7/x86_64/stable/Packages/containerd.io-1.2.10-3.2.el7.x86_64.rpm
メタデータの期限切れの最終確認: 1:00:47 時間前の 2020年02月08日 16時38分36秒 に実施しました。
containerd.io-1.2.10-3.2.el7.x86_64.rpm                                                                                                 7.5 MB/s |  23 MB     00:03
依存関係が解決しました。
========================================================================================================================================================================
 パッケージ                                アーキテクチャー                   バージョン                                 リポジトリー                             サイズ
========================================================================================================================================================================
アップグレード:
 containerd.io                             x86_64                             1.2.10-3.2.el7                             @commandline                              23 M

トランザクションの概要
========================================================================================================================================================================
アップグレード  1 パッケージ

合計サイズ: 23 M
これでよろしいですか? [y/N]: y
パッケージのダウンロード:
トランザクションの確認を実行中
トランザクションの確認に成功しました。
トランザクションのテストを実行中
トランザクションのテストに成功しました。
トランザクションを実行中
  準備             :                                                                                                                                                1/1
  scriptletの実行中: containerd.io-1.2.10-3.2.el7.x86_64                                                                                                            1/1
  アップグレード中 : containerd.io-1.2.10-3.2.el7.x86_64                                                                                                            1/2
  scriptletの実行中: containerd.io-1.2.10-3.2.el7.x86_64                                                                                                            1/2
  scriptletの実行中: containerd.io-1.2.0-3.el7.x86_64                                                                                                               2/2
  整理             : containerd.io-1.2.0-3.el7.x86_64                                                                                                               2/2
  scriptletの実行中: containerd.io-1.2.0-3.el7.x86_64                                                                                                               2/2
  検証             : containerd.io-1.2.10-3.2.el7.x86_64                                                                                                            1/2
  検証             : containerd.io-1.2.0-3.el7.x86_64                                                                                                               2/2

アップグレード済み:
  containerd.io-1.2.10-3.2.el7.x86_64

完了しました!

よしよし、もっかいupdateしよ。

# dnf update
メタデータの期限切れの最終確認: 1:01:08 時間前の 2020年02月08日 16時38分36秒 に実施しました。
依存関係が解決しました。
========================================================================================================================================================================
 パッケージ                           アーキテクチャー                  バージョン                                    リポジトリー                                サイズ
========================================================================================================================================================================
アップグレード:
 docker-ce                            x86_64                            3:19.03.5-3.el7                               docker-ce-stable                             24 M

トランザクションの概要
========================================================================================================================================================================
アップグレード  1 パッケージ

ダウンロードサイズの合計: 24 M
これでよろしいですか? [y/N]: y
パッケージのダウンロード:
docker-ce-19.03.5-3.el7.x86_64.rpm                                                                                                      7.2 MB/s |  24 MB     00:03
------------------------------------------------------------------------------------------------------------------------------------------------------------------------
合計                                                                                                                                    7.2 MB/s |  24 MB     00:03
トランザクションの確認を実行中
トランザクションの確認に成功しました。
トランザクションのテストを実行中
トランザクションのテストに成功しました。
トランザクションを実行中
  準備             :                                                                                                                                                1/1
  scriptletの実行中: docker-ce-3:19.03.5-3.el7.x86_64                                                                                                               1/1
  アップグレード中 : docker-ce-3:19.03.5-3.el7.x86_64                                                                                                               1/2
  scriptletの実行中: docker-ce-3:19.03.5-3.el7.x86_64                                                                                                               1/2
  scriptletの実行中: docker-ce-3:18.09.1-3.el7.x86_64                                                                                                               2/2
/usr/bin/dockerd は dockerd の為の互換用として設定されていません。

  整理             : docker-ce-3:18.09.1-3.el7.x86_64                                                                                                               2/2
  scriptletの実行中: docker-ce-3:18.09.1-3.el7.x86_64                                                                                                               2/2
  検証             : docker-ce-3:19.03.5-3.el7.x86_64                                                                                                               1/2
  検証             : docker-ce-3:18.09.1-3.el7.x86_64                                                                                                               2/2

アップグレード済み:
  docker-ce-3:19.03.5-3.el7.x86_64

完了しました!

うん、大丈夫そう。

# dnf update
メタデータの期限切れの最終確認: 1:01:38 時間前の 2020年02月08日 16時38分36秒 に実施しました。
依存関係が解決しました。
行うべきことはありません。
完了しました!

よかった。

出典

https://serverfault.com/questions/987686/no-network-connectivity-to-from-docker-ce-container-on-centos-8

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

雑に知ってしまったDockerを雑に知り直す

image.png
かわいい。Moby Dockという名前だそうです。
(くん付けかちゃん付けかで悩んで性別調べたけど出てこなかったので、ご存知の方いたらこっそり教えて下さい)

TL;DR

  • Dockerの存在は知っていて、コンテナ起動の方法なんかも知っている
  • 自分でイメージ作成したことが無く、ちゃんとしたデバッグの手法とか知らないので改めて一から学ぶ
  • k8sとかもそのうちやる

Dockerとは

星の数ほど書かれた見出しではあるものの、単語が多すぎて???ってよくなります。
解説記事眺めていて登場頻度が高めだなと感じた単語だけピックアップ。(適宜更新予定)

  • 仮想化
    ホスト型/ハイパーバイザ型/コンテナ型などの方式があり、Dockerはコンテナ型。他の方式との違いについては実際に触っていないのでハッキリとは言えず。解説記事を鵜呑みにするなら
    • 構成のコード化。コードなので共有するためのファイルが軽い
    • 処理/起動が早い
    • プロセス:コンテナの比率は基本的に1:1
      DBサーバとAPIサーバが必要なアプリケーションを構成する場合、他の方式だと一つの仮想環境にまとめることが多いが、コンテナ型の場合は1つのコンテナに対して1つのプロセス。よってコンテナを2つ立ち上げる。サービスの規模次第とも思うので例外はありそうだけど、これがベストプラクティスらしい。
  • ホストマシン
    Dockerをインストールしたマシン。今この記事を書いているMacbookのこと。
  • Dockerエンジン
    Dockerのコア。Dockerそのものっていう認識。
  • コンテナ
    超頻出。センター英語で言うとas long asぐらい頻出。うまく言語化するのが難しいけどプロセスの実行環境みたいな。ホストマシンからは分離された独立した環境。
  • Dockerイメージ
    これも超頻出。コンテナの設計図、なんて解説されたりもしているけど、調べてくうちに設計図兼ファイル群みたいなイメージなのかなと感じた。レイヤ構造になっていて理解するにはLinuxの知識がもっと必要だと感じたので要勉強。
  • Docker Hub
    大量のDockerイメージが保存されたクラウド。
    プログラミング言語の実行環境や、DBサーバWebサーバのイメージなどが置いてあり、ここからイメージを引っ張ってくればあっというまに環境ができあがる。

インストール

※今回もmacでやってます

https://hub.docker.com/ からdmgファイル落としてきていつも通りのムーブでインストールしましょう。

前は特にアカウントとか要らなかった気がするけど、今は会員登録してダウンロードするらしい。

ちゃんと探せば会員登録しなくても落とせるリンクありそう。

brewをインストールしてあるなら以下でいけるはず

$ brew install docker
$ brew cask install docker

使ってみる

今回はnginxサーバ立ち上げてみました。

$ docker run [Dockerイメージ名]
-d バックグラウンド起動 
-p ポートフォワード設定 
--name コンテナに名前をつける 

$ docker run -d -p 80:80 --name webserver nginx
Unable to find image 'nginx:latest' locally # nginxっていう名前のイメージをまずローカルで探してから、Docker Hubを見に行く
latest: Pulling from library/nginx
bc51dd8edc1b: Pull complete 
66ba67045f57: Pull complete
bf317aa10aa5: Pull complete
Digest: sha256:ad5552c786f128e389a0263104ae39f3d3c7895579d45ae716f528185b36bc6f
Status: Downloaded newer image for nginx:latest
0d5655a34ba94e4cecad342f78e943830ea452f196ce5017464547266d6658b8

http://localhost:80 へアクセス

image.png

お手軽すぎてYABAI

  • 立ち上げたコンテナ情報やイメージ情報確認
$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
nginx               latest              2073e0bcb60e        5 days ago          127MB

$ docker ps -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                NAMES
0d5655a34ba9        nginx               "nginx -g 'daemon of…"   8 minutes ago       Up 8 minutes        0.0.0.0:80->80/tcp   webserver
  • コンテナへ接続
$ docker exec -it webserver(コンテナ名) /bin/bash

root@0d5655a34ba9:/# hostname
0d5655a34ba9

# ちゃんとnginxの設定ファイルもある
root@0d5655a34ba9:/# cat /etc/nginx/nginx.conf 
user  nginx;
worker_processes  1;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    keepalive_timeout  65;

    #gzip  on;

    include /etc/nginx/conf.d/*.conf;
}
  • コンテナ停止
$ docker stop webserver
webserver

$ docker container ls
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES

# コンテナ停止しただけなので、イメージは残ってる
$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
nginx               latest              2073e0bcb60e        5 days ago          127MB

うんうん。

おわり

nginxはユーザ設定やパフォーマンスを少しいじったことがあるだけでインストールすらしたことありません。よくわからんけどなんか動いてる、っていうのはまさにこのこと。楽しいけど良し悪しあるなぁというのが本音です。(せっかくなのでnginxの勉強もします)

nginxコンテナを起動したわけですが、ここからの道のりはまだまだ長く。。
どうやってこの環境にアプリケーション持ち込むの?とか、
自分の好きな設定をいれたDockerイメージを作成したりとか、
コンテナはそれぞれが隔離された環境なのでコンテナ同士が通信できるようにしたりとか。
知らないといけないことは山積みなので定期的にインプット/アウトプットしていきます。

今回の学び

image.png

テザリングでのpullのし過ぎには気をつけましょう。

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

雑に知ってしまったDockerを知り直す

image.png
かわいい。Moby Dockという名前だそうです。
(くん付けかちゃん付けかで悩んで性別調べたけど出てこなかったので、ご存知の方いたらこっそり教えて下さい)

TL;DR

  • Dockerの存在は知っていて、コンテナ起動の方法なんかも知っている
  • 自分でイメージ作成したことが無く、ちゃんとしたデバッグの手法とか知らないので改めて一から学ぶ
  • k8sとかもそのうちやる

Dockerとは

星の数ほど書かれた見出しではあるものの、単語が多すぎて???ってよくなります。
解説記事眺めていて登場頻度が高めだなと感じた単語だけピックアップ。(適宜更新予定)

  • 仮想化
    ホスト型/ハイパーバイザ型/コンテナ型などの方式があり、Dockerはコンテナ型。他の方式との違いについては実際に触っていないのでハッキリとは言えず。解説記事を鵜呑みにするなら
    • 構成のコード化。コードなので共有するためのファイルが軽い
    • 処理/起動が早い
    • プロセス:コンテナの比率は基本的に1:1
      DBサーバとAPIサーバが必要なアプリケーションを構成する場合、他の方式だと一つの仮想環境にまとめることが多いが、コンテナ型の場合は1つのコンテナに対して1つのプロセス。よってコンテナを2つ立ち上げる。サービスの規模次第とも思うので例外はありそうだけど、これがベストプラクティスらしい。
  • ホストマシン
    Dockerをインストールしたマシン。今この記事を書いているMacbookのこと。
  • Dockerエンジン
    Dockerのコア。Dockerそのものっていう認識。
  • コンテナ
    超頻出。センター英語で言うとas long asぐらい頻出。うまく言語化するのが難しいけどプロセスの実行環境みたいな。ホストマシンからは分離された独立した環境。
  • Dockerイメージ
    これも超頻出。コンテナの設計図、なんて解説されたりもしているけど、調べてくうちに設計図兼ファイル群みたいなイメージなのかなと感じた。レイヤ構造になっていて理解するにはLinuxの知識がもっと必要だと感じたので要勉強。
  • Docker Hub
    大量のDockerイメージが保存されたクラウド。
    プログラミング言語の実行環境や、DBサーバWebサーバのイメージなどが置いてあり、ここからイメージを引っ張ってくればあっというまに環境ができあがる。

インストール

※今回もmacでやってます

https://hub.docker.com/ からdmgファイル落としてきていつも通りのムーブでインストールしましょう。

前は特にアカウントとか要らなかった気がするけど、今は会員登録してダウンロードするらしい。

ちゃんと探せば会員登録しなくても落とせるリンクありそう。

brewをインストールしてあるなら以下でいけるはず

$ brew install docker
$ brew cask install docker

使ってみる

今回はnginxサーバ立ち上げてみました。

$ docker run [Dockerイメージ名]
-d バックグラウンド起動 
-p ポートフォワード設定 
--name コンテナに名前をつける 

$ docker run -d -p 80:80 --name webserver nginx
Unable to find image 'nginx:latest' locally # nginxっていう名前のイメージをまずローカルで探してから、Docker Hubを見に行く
latest: Pulling from library/nginx
bc51dd8edc1b: Pull complete 
66ba67045f57: Pull complete
bf317aa10aa5: Pull complete
Digest: sha256:ad5552c786f128e389a0263104ae39f3d3c7895579d45ae716f528185b36bc6f
Status: Downloaded newer image for nginx:latest
0d5655a34ba94e4cecad342f78e943830ea452f196ce5017464547266d6658b8

http://localhost:80 へアクセス

image.png

お手軽すぎてYABAI

  • 立ち上げたコンテナ情報やイメージ情報確認
$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
nginx               latest              2073e0bcb60e        5 days ago          127MB

$ docker ps -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                NAMES
0d5655a34ba9        nginx               "nginx -g 'daemon of…"   8 minutes ago       Up 8 minutes        0.0.0.0:80->80/tcp   webserver
  • コンテナへ接続
$ docker exec -it webserver(コンテナ名) /bin/bash

root@0d5655a34ba9:/# hostname
0d5655a34ba9

# ちゃんとnginxの設定ファイルもある
root@0d5655a34ba9:/# cat /etc/nginx/nginx.conf 
user  nginx;
worker_processes  1;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    keepalive_timeout  65;

    #gzip  on;

    include /etc/nginx/conf.d/*.conf;
}
  • コンテナ停止
$ docker stop webserver
webserver

$ docker container ls
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES

# コンテナ停止しただけなので、イメージは残ってる
$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
nginx               latest              2073e0bcb60e        5 days ago          127MB

うんうん。

おわり

nginxはユーザ設定やパフォーマンスを少しいじったことがあるだけでインストールすらしたことありません。よくわからんけどなんか動いてる、っていうのはまさにこのこと。楽しいけど良し悪しあるなぁというのが本音です。(せっかくなのでnginxの勉強もします)

nginxコンテナを起動したわけですが、ここからの道のりはまだまだ長く。。
どうやってこの環境にアプリケーション持ち込むの?とか、
自分の好きな設定をいれたDockerイメージを作成したりとか、
コンテナはそれぞれが隔離された環境なのでコンテナ同士が通信できるようにしたりとか。
知らないといけないことは山積みなので定期的にインプット/アウトプットしていきます。

今回の学び

image.png

テザリングでのpullのし過ぎには気をつけましょう。

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

SpringBoot 2.3.0 M1から入ったBuild Docker images with Cloud Native Buildpacksを試す

きっかけ

SpringBoot 2.3.0 M1がリリースされました。

https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.3.0-M1-Release-Notes

いろいろな改善や、不要機能の削除などがされていますが、リリースノートを見ていて気になった、Build Docker images with Cloud Native Buildpacksを試してみたいと思います。

リリースノートには、以下記載がありました。

Support for building Docker images using Cloud Native Buildpacks has been added to the Maven and Gradle plugins via the spring-boot:build-image goal and the bootBuildImage task.

MavenかGradleプラグインでDockerイメージが作れるようになるようです。
早速試したいと思い、その活動のまとめとなります。

How to Get Started

前回と同じ設定で
Spring Initializr でアプリ雛形を作ります。
作成後に、環境情報を取得したかったので、SpringBoot Actuatorも有効にしておきます。

早速DockerImageを作ります。イメージを作成する環境ではDockerDeamonを起動しておく必要があるので、その点は注意してください。

これまでSpringBootアプリケーションをDockerImageビルドする場合は以下のステップを踏んでました。
1. mvn package でアプリのJarファイルを作成
2. どのようなDockerImageを作るのかをDockerfileに記載
3. docker buildコマンドでイメージを作成

  • Dockerfileの設定例
FROM openjdk:8-jdk-alpine
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","-jar","/app.jar"]

Build Docker images with Cloud Native Buildpacksでは、上記手順を踏まずに1ステップでDockerImageまで作ることができます。
Mavenの場合は、mvn spring-boot:build-image、Gradleの場合はgradle bootBuildImageを叩くだけです。Cloud Native Buildpacksの機能で、ソースの中身を読み取ってDockerfileに設定が必要な内容を補完してくれます。

$ mvn spring-boot:build-image
[INFO] Scanning for projects...
[INFO]
[INFO] --------------------------< com.example:demo >--------------------------
[INFO] Building demo 0.0.1-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] >>> spring-boot-maven-plugin:2.3.0.M1:build-image (default-cli) > package @ demo >>>
[INFO]
[INFO] --- maven-resources-plugin:3.1.0:resources (default-resources) @ demo ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 1 resource
[INFO] Copying 0 resource
[INFO]
[INFO] --- maven-compiler-plugin:3.8.1:compile (default-compile) @ demo ---
[INFO] Nothing to compile - all classes are up to date

中略

[INFO]  > Running builder
[INFO]     [builder]
[INFO]     [builder]     Cloud Foundry OpenJDK Buildpack v1.0.80
[INFO]     [builder]       OpenJDK JRE 11.0.5: Reusing cached layer
[INFO]     [builder]
[INFO]     [builder]     Cloud Foundry JVM Application Buildpack v1.0.113
[INFO]     [builder]       Executable JAR: Reusing cached layer
[INFO]     [builder]       Process types:
[INFO]     [builder]         executable-jar: java -cp $CLASSPATH $JAVA_OPTS org.springframework.boot.loader.JarLauncher
[INFO]     [builder]         task:           java -cp $CLASSPATH $JAVA_OPTS org.springframework.boot.loader.JarLauncher
[INFO]     [builder]         web:            java -cp $CLASSPATH $JAVA_OPTS org.springframework.boot.loader.JarLauncher
[INFO]     [builder]
[INFO]     [builder]     Cloud Foundry Spring Boot Buildpack v1.0.157
[INFO]     [builder]       Spring Boot 2.3.0.M1: Reusing cached layer
[INFO]     [builder]       Process types:
[INFO]     [builder]         spring-boot: java -cp $CLASSPATH $JAVA_OPTS com.example.demo.DemoApplication
[INFO]     [builder]         task:        java -cp $CLASSPATH $JAVA_OPTS com.example.demo.DemoApplication
[INFO]     [builder]         web:         java -cp $CLASSPATH $JAVA_OPTS com.example.demo.DemoApplication
[INFO]     [builder]
[INFO]     [builder]     Cloud Foundry Spring Auto-reconfiguration Buildpack v1.0.159
[INFO]     [builder]       Spring Auto-reconfiguration 2.11.0: Reusing cached layer
[INFO]
[INFO]  > Running exporter
[INFO]     [exporter]    Reusing layer 'app'
[INFO]     [exporter]    Reusing layer 'config'
[INFO]     [exporter]    Reusing layer 'launcher'
[INFO]     [exporter]    Reusing layer 'org.cloudfoundry.openjdk:openjdk-jre'
[INFO]     [exporter]    Reusing layer 'org.cloudfoundry.jvmapplication:executable-jar'
[INFO]     [exporter]    Reusing layer 'org.cloudfoundry.springboot:spring-boot'
[INFO]     [exporter]    Reusing layer 'org.cloudfoundry.springautoreconfiguration:auto-reconfiguration'
[INFO]     [exporter]    *** Images (89a7e99f9c15):
[INFO]     [exporter]          docker.io/library/demo:0.0.1-SNAPSHOT
[INFO]
[INFO]  > Running cacher
[INFO]     [cacher]      Reusing layer 'org.cloudfoundry.openjdk:2f08c469c9a8adea1b6ee3444ba2a8242a7e99d87976a077faf037a9eb7f884b'
[INFO]     [cacher]      Reusing layer 'org.cloudfoundry.jvmapplication:executable-jar'
[INFO]     [cacher]      Reusing layer 'org.cloudfoundry.springboot:spring-boot'
[INFO]     [cacher]      Reusing layer 'org.cloudfoundry.springautoreconfiguration:46ab131165317d91fd4ad3186abf755222744e2d277dc413def06f3ad45ab150'
[INFO]
[INFO] Successfully built image 'docker.io/library/demo:0.0.1-SNAPSHOT'
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  01:01 min
[INFO] Finished at: 2020-02-08T15:30:43+09:00
[INFO] ------------------------------------------------------------------------

初回はベースイメージのダウンロードとかで時間がかかりますが、2回目以上は上記の時間ぐらいでできます。

さて、ほんとにDockerImageができているのか、確認してみましょう。

docker images
REPOSITORY                           TAG                 IMAGE ID            CREATED              SIZE
demo                                 0.0.1-SNAPSHOT      89a7e99f9c15        About a minute ago   226MB

ちゃんといますね。起動は通常のDockerImageと同じです。ここではdocker run -it -p8080:8080 demo:0.0.1-SNAPSHOT で起動させます。

docker run -it -p8080:8080 demo:0.0.1-SNAPSHOT

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::             (v2.3.0.M1)

2020-02-08 06:33:43.540  WARN 1 --- [           main] pertySourceApplicationContextInitializer : Skipping 'cloud' property source addition because not in a cloud
2020-02-08 06:33:43.570  WARN 1 --- [           main] nfigurationApplicationContextInitializer : Skipping reconfiguration because not in a cloud
2020-02-08 06:33:43.635  INFO 1 --- [           main] com.example.demo.DemoApplication         : Starting DemoApplication on 40fbed8bc770 with PID 1 (/workspace/BOOT-INF/classes started by cnb in /workspace)
2020-02-08 06:33:43.636  INFO 1 --- [           main] com.example.demo.DemoApplication         : No active profile set, falling back to default profiles: default
2020-02-08 06:33:48.387  INFO 1 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2020-02-08 06:33:48.456  INFO 1 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2020-02-08 06:33:48.466  INFO 1 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.30]
2020-02-08 06:33:48.738  INFO 1 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2020-02-08 06:33:48.740  INFO 1 --- [           main] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 4841 ms
2020-02-08 06:33:50.208  INFO 1 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
2020-02-08 06:33:50.900  INFO 1 --- [           main] o.s.b.a.e.web.EndpointLinksResolver      : Exposing 14 endpoint(s) beneath base path '/actuator'
2020-02-08 06:33:51.122  INFO 1 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2020-02-08 06:33:51.142  INFO 1 --- [           main] com.example.demo.DemoApplication         : Started DemoApplication in 9.779 seconds (JVM running for 12.428)

無事、起動できています。前回アプリを使っているので、Actuatorも有効のままです。envの情報を取得してみましょう。

curl localhost:8080/actuator/env | jq
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  8753    0  8753    0     0  10130      0 --:--:-- --:--:-- --:--:-- 10130
{
  "activeProfiles": [],
  "propertySources": [
    {
      "name": "server.ports",
      "properties": {
        "local.server.port": {
          "value": 8080
        }
      }
    },
    {
      "name": "servletContextInitParams",
      "properties": {}
    },
    {
      "name": "systemProperties",
      "properties": {
        "awt.toolkit": {
          "value": "sun.awt.X11.XToolkit"
        },
        "java.specification.version": {
          "value": "11"
        },
        "sun.cpu.isalist": {
          "value": ""
        },
        "sun.jnu.encoding": {
          "value": "ANSI_X3.4-1968"
        },
        "java.class.path": {
          "value": "/layers/org.cloudfoundry.springautoreconfiguration/auto-reconfiguration/java-buildpack-auto-reconfiguration-2.11.0.RELEASE.jar:/workspace/BOOT-INF/classes:/workspace/BOOT-INF/lib/HdrHistogram-2.1.11.jar:/workspace/BOOT-INF/lib/LatencyUtils-2.0.3.jar:/workspace/BOOT-INF/lib/jackson-annotations-2.10.1.jar:/workspace/BOOT-INF/lib/jackson-core-2.10.1.jar:/workspace/BOOT-INF/lib/jackson-databind-2.10.1.jar:/workspace/BOOT-INF/lib/jackson-datatype-jdk8-2.10.1.jar:/workspace/BOOT-INF/lib/jackson-datatype-jsr310-2.10.1.jar:/workspace/BOOT-INF/lib/jackson-module-parameter-names-2.10.1.jar:/workspace/BOOT-INF/lib/jakarta.annotation-api-1.3.5.jar:/workspace/BOOT-INF/lib/jakarta.el-3.0.3.jar:/workspace/BOOT-INF/lib/jul-to-slf4j-1.7.29.jar:/workspace/BOOT-INF/lib/log4j-api-2.12.1.jar:/workspace/BOOT-INF/lib/log4j-to-slf4j-2.12.1.jar:/workspace/BOOT-INF/lib/logback-classic-1.2.3.jar:/workspace/BOOT-INF/lib/logback-core-1.2.3.jar:/workspace/BOOT-INF/lib/micrometer-core-1.3.3.jar:/workspace/BOOT-INF/lib/slf4j-api-1.7.29.jar:/workspace/BOOT-INF/lib/snakeyaml-1.25.jar:/workspace/BOOT-INF/lib/spring-aop-5.2.3.RELEASE.jar:/workspace/BOOT-INF/lib/spring-beans-5.2.3.RELEASE.jar:/workspace/BOOT-INF/lib/spring-boot-2.3.0.M1.jar:/workspace/BOOT-INF/lib/spring-boot-actuator-2.3.0.M1.jar:/workspace/BOOT-INF/lib/spring-boot-actuator-autoconfigure-2.3.0.M1.jar:/workspace/BOOT-INF/lib/spring-boot-autoconfigure-2.3.0.M1.jar:/workspace/BOOT-INF/lib/spring-boot-starter-2.3.0.M1.jar:/workspace/BOOT-INF/lib/spring-boot-starter-actuator-2.3.0.M1.jar:/workspace/BOOT-INF/lib/spring-boot-starter-json-2.3.0.M1.jar:/workspace/BOOT-INF/lib/spring-boot-starter-logging-2.3.0.M1.jar:/workspace/BOOT-INF/lib/spring-boot-starter-tomcat-2.3.0.M1.jar:/workspace/BOOT-INF/lib/spring-boot-starter-web-2.3.0.M1.jar:/workspace/BOOT-INF/lib/spring-context-5.2.3.RELEASE.jar:/workspace/BOOT-INF/lib/spring-core-5.2.3.RELEASE.jar:/workspace/BOOT-INF/lib/spring-expression-5.2.3.RELEASE.jar:/workspace/BOOT-INF/lib/spring-jcl-5.2.3.RELEASE.jar:/workspace/BOOT-INF/lib/spring-web-5.2.3.RELEASE.jar:/workspace/BOOT-INF/lib/spring-webmvc-5.2.3.RELEASE.jar:/workspace/BOOT-INF/lib/tomcat-embed-core-9.0.30.jar:/workspace/BOOT-INF/lib/tomcat-embed-websocket-9.0.30.jar:/workspace"
        },
        "java.vm.vendor": {
          "value": "AdoptOpenJDK"
        },
        "sun.arch.data.model": {
          "value": "64"
        },
        "java.vendor.url": {
          "value": "https://adoptopenjdk.net/"
        },
        "catalina.useNaming": {
          "value": "false"
        },
        "user.timezone": {
          "value": "GMT"
        },
        "os.name": {
          "value": "Linux"
        },
        "java.vm.specification.version": {
          "value": "11"
        },
        "sun.java.launcher": {
          "value": "SUN_STANDARD"
        },
        "user.country": {
          "value": "US"
        },
        "sun.boot.library.path": {
          "value": "/layers/org.cloudfoundry.openjdk/openjdk-jre/lib"
        },
        "sun.java.command": {
          "value": "******"
        },
        "jdk.debug": {
          "value": "release"
        },
        "sun.cpu.endian": {
          "value": "little"
        },
        "user.home": {
          "value": "/home/cnb"
        },
        "user.language": {
          "value": "en"
        },
        "java.specification.vendor": {
          "value": "Oracle Corporation"
        },
        "java.version.date": {
          "value": "2019-10-15"
        },
        "java.home": {
          "value": "/layers/org.cloudfoundry.openjdk/openjdk-jre"
        },
        "file.separator": {
          "value": "/"
        },
        "java.vm.compressedOopsMode": {
          "value": "32-bit"
        },
        "line.separator": {
          "value": "\n"
        },
        "java.specification.name": {
          "value": "Java Platform API Specification"
        },
        "java.vm.specification.vendor": {
          "value": "Oracle Corporation"
        },
        "java.awt.graphicsenv": {
          "value": "sun.awt.X11GraphicsEnvironment"
        },
        "java.awt.headless": {
          "value": "true"
        },
        "sun.management.compiler": {
          "value": "HotSpot 64-Bit Tiered Compilers"
        },
        "java.runtime.version": {
          "value": "11.0.5+10"
        },
        "user.name": {
          "value": "cnb"
        },
        "path.separator": {
          "value": ":"
        },
        "os.version": {
          "value": "4.9.184-linuxkit"
        },
        "java.runtime.name": {
          "value": "OpenJDK Runtime Environment"
        },
        "file.encoding": {
          "value": "ANSI_X3.4-1968"
        },
        "spring.beaninfo.ignore": {
          "value": "true"
        },
        "java.vm.name": {
          "value": "OpenJDK 64-Bit Server VM"
        },
        "java.vendor.version": {
          "value": "AdoptOpenJDK"
        },
        "java.vendor.url.bug": {
          "value": "https://github.com/AdoptOpenJDK/openjdk-build/issues"
        },
        "java.io.tmpdir": {
          "value": "/tmp"
        },
        "catalina.home": {
          "value": "/tmp/tomcat.9057283283640867778.8080"
        },
        "java.version": {
          "value": "11.0.5"
        },
        "user.dir": {
          "value": "/workspace"
        },
        "os.arch": {
          "value": "amd64"
        },
        "java.vm.specification.name": {
          "value": "Java Virtual Machine Specification"
        },
        "PID": {
          "value": "1"
        },
        "java.awt.printerjob": {
          "value": "sun.print.PSPrinterJob"
        },
        "sun.os.patch.level": {
          "value": "unknown"
        },
        "catalina.base": {
          "value": "/tmp/tomcat.9057283283640867778.8080"
        },
        "java.library.path": {
          "value": "/layers/org.cloudfoundry.openjdk/openjdk-jre/lib:/usr/java/packages/lib:/usr/lib64:/lib64:/lib:/usr/lib"
        },
        "java.vendor": {
          "value": "AdoptOpenJDK"
        },
        "java.vm.info": {
          "value": "mixed mode"
        },
        "java.vm.version": {
          "value": "11.0.5+10"
        },
        "sun.io.unicode.encoding": {
          "value": "UnicodeLittle"
        },
        "java.class.version": {
          "value": "55.0"
        }
      }

後略

Javaの実装はAdoptOpenJDKの11が使われていますね。OSはLinuxのようです。

まとめ

Dockerfileを書かなくてもよいので、Dockerの知識がなくても簡単にDockerImageを作成できました。仕様や細かい動きを確認しないと本番へすぐ投入とはいかなそうですが、気軽にDockerImage作成できることは、メリットとなるのではないでしょうか?

使ったソースコードは以下に配置しておきました。
ご参考です。

https://github.com/omix222/springboot23-1

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

DockerでPythonのWebアプリケーションをnginx + gunicorn で起動する

構成

  • Webサーバー
    • nginx
  • WSGIサーバー(APサーバー)
    • Gunicorn
  • Webアプリケーションフレームワーク
    • Django
      • ここではWSGIを使用するので、WSGIに準拠していれば他のフレームワークでも良い
      • この記事ではDjango固有の設定は発生しない
  • WebサーバーとWSGIサーバーはDockerコンテナで稼働させる
  • WebサーバーとWSGIサーバーの通信にはUNIXドメインソケットを使用する

用語の整理

WSGIサーバーとは

  • WSGIとはWeb Server Gateway Interfaceのことで、WebサーバーとPythonのAPサーバー間通信のプロトコルのこと
    • DjangoやFlask、Bottleなどのフレームワークもこのプロトコルに準拠している
  • 上記のWSGIに則ったAPサーバーをWSGIサーバーと呼び、Gunicornはその一種
    • gunicorn以外のWSGIサーバーにはuWSGIがある

UNIXドメインソケットとは

  • ファイルシステムのパスを通して通信相手を探す通信方法
  • そのパスにファイルが作成され、それぞれのプロセスがそのファイルにアクセスする
  • 単にファイルを共有しているだけに思えるが、作成されるファイルはソケットファイルと呼ばれる特殊なファイルであり実態はない
    • あくまで通信のインターフェース

構築

Gunicorn

  • まずはWSGIサーバーを単体で稼働させてみる

プロジェクト作成

  • Djangoをインストールする
$ pip install Django==3.0.2
  • 作業環境でプロジェクトを作成する
$ django-admin.py startproject django_project .
  • ここではdjango_projectという名前でプロジェクトを作成
  • treeコマンドで確認すると以下のようになる
$ tree
.
├── django_project
│   ├── __init__.py
│   ├── asgi.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
├── manage.py

Gunicornコンテナ起動

  • Dockerfileを作成
FROM python:3.8.0-alpine

WORKDIR /usr/src/app

ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1

RUN pip install --upgrade pip
COPY ./requirements.txt /usr/src/app/requirements.txt
RUN pip install -r requirements.txt
  • requirements.txt は以下の通り
requirements.txt
Django==3.0.2
gunicorn==20.0.4
  • ビルドする
$ docker build -t gunicorn:tmp .
  • docker-compose.yamlの記載は以下の通り
    • 後でnginxの情報も追記する
version: '3.7'

services:
  gunicorn:
    image: gunicorn:tmp 
    container_name: gunicorn
    command: python manage.py runserver 0.0.0.0:8000
    volumes:
      - .:/usr/src/app/
    ports:
      - 8000:8000
  • python manage.py runserver 0.0.0.0:8000コマンドでテスト用簡易サーバーが起動される
  • ここでは稼働確認のため8000番のポートをフォワーディングしている
  • $ docker-compose up -dで起動してlocalhost:8000にアクセスしてDjangoの画面が表示されればOK

スクリーンショット 2020-01-12 20.57.11.png

NginxでUNIXドメインソケット使用する設定

  • 公式のDockerイメージを使用するが、UNIXドメインソケットの設定を記載したconfファイルを作成し起動時にマウントさせる
  • gunicorn.confという名前で以下の内容を記載
gunicorn.conf
upstream gunicorn-django {
    server unix:///var/run/gunicorn/gunicorn.sock;
}

server {
    listen 80;
    server_name localhost;

    location / {
        try_files $uri @gunicorn;
    }

    location @gunicorn {
        proxy_pass http://gunicorn-django;
    }
}
  • upstream gunicorn-django{…} の内容がUNIXドメインソケットの設定
  • 記載したパスのソケットファイルを介することでgunicornと通信を行う
    • つまりgunicorn側からもこのソケットファイルを呼び出す設定が必要になる
  • あとはlocationのproxy_passでupstreamで指定したgunicorn-djangoを指定すればよい
  • Nginxのdocker-compose.yamlの記載は以下の通り
version: '3.7'

services:
  nginx:
    image: nginx:1.17.7
    container_name: nginx
    ports:
      - "80:80"
    volumes:
      - ./gunicorn.conf:/etc/nginx/conf.d/default.conf
  • Nginx内に/etc/nginx/conf.d/default.confという名前でマウントする
  • Nginxは/etc/nginx/nginx.confを起動時に読み取り、そのファイル内でinclude /etc/nginx/conf.d/*.conf;という記載がされているため、/etc/nginx/conf.d配下に自分で作成したconfファイルをマウントしておけば起動時に一緒に読み込んでくれる

GunicornでUNIXドメインソケット使用する設定

  • 先ほどローカルで起動させた状態から多少変更を加える

コンテナの起動コマンド

WSGIの設定

  • GunicornがNginxと通信を行えるよう、以下のようなgunicornコマンドを叩く必要がある
gunicorn django_project.wsgi
  • これは今回django_projectというプロジェクトを作成したときに構築されるdjango_project配下にあるwsgi.pyを読み込む
  • ローカルで起動させるときはmanage.pyを起動させたが、WSGIで通信させるときはこのコマンドが必要になる

UNIXドメインソケットの設定

  • UNIXドメインソケットで通信させるため、--bindで起動時ソケットファイルのパスを指定する
gunicorn django_project.wsgi --bind=unix:/var/run/gunicorn/gunicorn.sock
  • これによりgunicornへのUNIXドメインソケット通信が可能になる

Doickerfile修正

  • 上記の内容を基に、DockerfileのCMDを変更
Dockerfile
FROM python:3.8.0-alpine

WORKDIR /usr/src/app

ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1

RUN pip install --upgrade pip
COPY ./requirements.txt /usr/src/app/requirements.txt
RUN pip install -r requirements.txt

RUN mkdir -p /var/run/gunicorn

CMD ["gunicorn", "django_project.wsgi", "--bind=unix:/var/run/gunicorn/gunicorn.sock"]

  • 再度ビルドしておく
$ docker build -t gunicorn:latest .

Djangoプロジェクトへアクセスできるホストを指定

  • django_project配下に作成されるsetting.py内のALLOWED_HOSTS=[]に指定する
  • 本来なら制限するべきだが、ここでは全てのホストからのアクセスを許可するためALLOWED_HOSTS=[*]にしておく
  • このファイルはwsgi.pyから呼び出されている

UNIXドメインソケット通信でマウントするボリュームを作成

  • Nginx、Gunicornでソケットファイルのパスを指定したので、ソケットファイルのボリュームを作成する必要がある
  • 今回はDockerで起動させるので、DockerのVolumeを起動時に作成し、2つのコンテナからそのVolumeにマウントを行う
  • 最終的なdocker-compose.yamlは以下の通り
docker-compose.yaml
version: '3.7'

services:
  gunicorn:
    image: gunicorn:latest
    container_name: gunicorn
    volumes:
      - .:/usr/src/app/
      - gunicorn:/var/run/gunicorn
  nginx:
    image: nginx:1.17.7
    container_name: nginx
    depends_on:
      - gunicorn
    ports:
      - "80:80"
    volumes:
      - ./gunicorn.conf:/etc/nginx/conf.d/default.conf
      - gunicorn:/var/run/gunicorn
volumes:
  gunicorn:
    driver: local
  • gunicornというVolumeを作成し、それぞれのコンテナにマウントする(今回はどちらのコンテナでも/var/run/gunicornを指定している)

稼働

  • 以下のコマンドで起動
$ docker-compose up -d
Starting gunicorn ... done
Starting nginx    ... done
  • 起動後にlocalhostにアクセスしてみる

スクリーンショット 2020-02-08 14.44.28.png

  • 画面が表示されればOK
  • 最初にGunicornを単体で稼働させたときと表示されている画面は同じだが、今回はnginxを経由して表示されている

ソースコード

  • 今回作成したファイル一式はこちら

参考

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

DockerでPythonのWebアプリケーションをNginx + Gunicorn で起動する

構成

  • Webサーバー
    • nginx
  • WSGIサーバー(APサーバー)
    • Gunicorn
  • Webアプリケーションフレームワーク
    • Django
      • ここではWSGIを使用するので、WSGIに準拠していれば他のフレームワークでも良い
      • この記事ではDjango固有の設定は発生しない
  • WebサーバーとWSGIサーバーはDockerコンテナで稼働させる
  • WebサーバーとWSGIサーバーの通信にはUNIXドメインソケットを使用する

用語の整理

WSGIサーバーとは

  • WSGIとはWeb Server Gateway Interfaceのことで、WebサーバーとPythonのAPサーバー間通信のプロトコルのこと
    • DjangoやFlask、Bottleなどのフレームワークもこのプロトコルに準拠している
  • 上記のWSGIに則ったAPサーバーをWSGIサーバーと呼び、Gunicornはその一種
    • gunicorn以外のWSGIサーバーにはuWSGIがある

UNIXドメインソケットとは

  • ファイルシステムのパスを通して通信相手を探す通信方法
  • そのパスにファイルが作成され、それぞれのプロセスがそのファイルにアクセスする
  • 単にファイルを共有しているだけに思えるが、作成されるファイルはソケットファイルと呼ばれる特殊なファイルであり実態はない
    • あくまで通信のインターフェース

構築

Gunicorn

  • まずはWSGIサーバーを単体で稼働させてみる

プロジェクト作成

  • Djangoをインストールする
$ pip install Django==3.0.2
  • 作業環境でプロジェクトを作成する
$ django-admin.py startproject django_project .
  • ここではdjango_projectという名前でプロジェクトを作成
  • treeコマンドで確認すると以下のようになる
$ tree
.
├── django_project
│   ├── __init__.py
│   ├── asgi.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
├── manage.py

Gunicornコンテナ起動

  • Dockerfileを作成
FROM python:3.8.0-alpine

WORKDIR /usr/src/app

ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1

RUN pip install --upgrade pip
COPY ./requirements.txt /usr/src/app/requirements.txt
RUN pip install -r requirements.txt
  • requirements.txt は以下の通り
requirements.txt
Django==3.0.2
gunicorn==20.0.4
  • ビルドする
$ docker build -t gunicorn:tmp .
  • docker-compose.yamlの記載は以下の通り
    • 後でnginxの情報も追記する
version: '3.7'

services:
  gunicorn:
    image: gunicorn:tmp 
    container_name: gunicorn
    command: python manage.py runserver 0.0.0.0:8000
    volumes:
      - .:/usr/src/app/
    ports:
      - 8000:8000
  • python manage.py runserver 0.0.0.0:8000コマンドでテスト用簡易サーバーが起動される
  • ここでは稼働確認のため8000番のポートをフォワーディングしている
  • $ docker-compose up -dで起動してlocalhost:8000にアクセスしてDjangoの画面が表示されればOK

スクリーンショット 2020-01-12 20.57.11.png

NginxでUNIXドメインソケット使用する設定

  • 公式のDockerイメージを使用するが、UNIXドメインソケットの設定を記載したconfファイルを作成し起動時にマウントさせる
  • gunicorn.confという名前で以下の内容を記載
gunicorn.conf
upstream gunicorn-django {
    server unix:///var/run/gunicorn/gunicorn.sock;
}

server {
    listen 80;
    server_name localhost;

    location / {
        try_files $uri @gunicorn;
    }

    location @gunicorn {
        proxy_pass http://gunicorn-django;
    }
}
  • upstream gunicorn-django{…} の内容がUNIXドメインソケットの設定
  • 記載したパスのソケットファイルを介することでgunicornと通信を行う
    • つまりgunicorn側からもこのソケットファイルを呼び出す設定が必要になる
  • あとはlocationのproxy_passでupstreamで指定したgunicorn-djangoを指定すればよい
  • Nginxのdocker-compose.yamlの記載は以下の通り
version: '3.7'

services:
  nginx:
    image: nginx:1.17.7
    container_name: nginx
    ports:
      - "80:80"
    volumes:
      - ./gunicorn.conf:/etc/nginx/conf.d/default.conf
  • Nginx内に/etc/nginx/conf.d/default.confという名前でマウントする
  • Nginxは/etc/nginx/nginx.confを起動時に読み取り、そのファイル内でinclude /etc/nginx/conf.d/*.conf;という記載がされているため、/etc/nginx/conf.d配下に自分で作成したconfファイルをマウントしておけば起動時に一緒に読み込んでくれる

GunicornでUNIXドメインソケット使用する設定

  • 先ほどローカルで起動させた状態から多少変更を加える

コンテナの起動コマンド

WSGIの設定

  • GunicornがNginxと通信を行えるよう、以下のようなgunicornコマンドを叩く必要がある
gunicorn django_project.wsgi
  • これは今回django_projectというプロジェクトを作成したときに構築されるdjango_project配下にあるwsgi.pyを読み込む
  • ローカルで起動させるときはmanage.pyを起動させたが、WSGIで通信させるときはこのコマンドが必要になる

UNIXドメインソケットの設定

  • UNIXドメインソケットで通信させるため、--bindで起動時ソケットファイルのパスを指定する
gunicorn django_project.wsgi --bind=unix:/var/run/gunicorn/gunicorn.sock
  • これによりgunicornへのUNIXドメインソケット通信が可能になる

Doickerfile修正

  • 上記の内容を基に、DockerfileのCMDを変更
Dockerfile
FROM python:3.8.0-alpine

WORKDIR /usr/src/app

ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1

RUN pip install --upgrade pip
COPY ./requirements.txt /usr/src/app/requirements.txt
RUN pip install -r requirements.txt

RUN mkdir -p /var/run/gunicorn

CMD ["gunicorn", "django_project.wsgi", "--bind=unix:/var/run/gunicorn/gunicorn.sock"]

  • 再度ビルドしておく
$ docker build -t gunicorn:latest .

Djangoプロジェクトへアクセスできるホストを指定

  • django_project配下に作成されるsetting.py内のALLOWED_HOSTS=[]に指定する
  • 本来なら制限するべきだが、ここでは全てのホストからのアクセスを許可するためALLOWED_HOSTS=[*]にしておく
  • このファイルはwsgi.pyから呼び出されている

UNIXドメインソケット通信でマウントするボリュームを作成

  • Nginx、Gunicornでソケットファイルのパスを指定したので、ソケットファイルのボリュームを作成する必要がある
  • 今回はDockerで起動させるので、DockerのVolumeを起動時に作成し、2つのコンテナからそのVolumeにマウントを行う
  • 最終的なdocker-compose.yamlは以下の通り
docker-compose.yaml
version: '3.7'

services:
  gunicorn:
    image: gunicorn:latest
    container_name: gunicorn
    volumes:
      - .:/usr/src/app/
      - gunicorn:/var/run/gunicorn
  nginx:
    image: nginx:1.17.7
    container_name: nginx
    depends_on:
      - gunicorn
    ports:
      - "80:80"
    volumes:
      - ./gunicorn.conf:/etc/nginx/conf.d/default.conf
      - gunicorn:/var/run/gunicorn
volumes:
  gunicorn:
    driver: local
  • gunicornというVolumeを作成し、それぞれのコンテナにマウントする(今回はどちらのコンテナでも/var/run/gunicornを指定している)

稼働

  • 以下のコマンドで起動
$ docker-compose up -d
Starting gunicorn ... done
Starting nginx    ... done
  • 起動後にlocalhostにアクセスしてみる

スクリーンショット 2020-02-08 14.44.28.png

  • 画面が表示されればOK
  • 最初にGunicornを単体で稼働させたときと表示されている画面は同じだが、今回はnginxを経由して表示されている

ソースコード

  • 今回作成したファイル一式はこちら

参考

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

Rubyで学ぶ「ゼロから作るDeep Learning」Docker環境構築編

Dockerの環境構築

やること

  • ローカルのワーキングディレクトリを作成
  • DockerでRubyの環境を作成
  • Dockerで作成したワーキングディレクトリをマウントして環境に入る
  • Dockerに必要なパッケージ等をインストール
    • gnuplot & numo-gnuplot
      • プロット用
    • numo-narray
      • 行列計算 & 基礎的な数学の関数(sin cos exp等)

Docker環境を作って、ワーキングディレクトリをマウントする所まで

実行コマンド

# ローカルにワーキングディレクトリを作成
local$ mkdir working_dir
# RubyのDockerイメージを取得
local$ docker pull ruby:latest
# ワーキングディレクトリをマウントしてDocker環境に入る
local$ docker run -v /(workingディレクトリをおいているパス)/working_dir/:/working_dir/ -it ruby /bin/bash

なぜワーキングディレクトリをマウントしておくか

gnuplotで出力したファイルを、Dockerファイルシステムの外で簡単に確認できる。ローカル環境にコピーするコマンドを叩かず、手元の環境(macOS)のプレビューアプリ等で出力したpngイメージを確認できるので便利。他にも、特に設定せずに、自分のお気に入りのエディタを使えるのもよい。

Dockerに必要なパッケージ等をインストール

# gunuplotのインストール
docker$ apt update -y && apt upgrade -y && apt install -y gnuplot
# 各種gemのインストール
docker$ gem install numo-narray numo-gnuplot

とりあえず動かしてみる

ローカルのワーキングディレクトリに先人の記事のファイルをそのまま作成

Python vs Ruby 『ゼロから作るDeep Learning』 1章 sin関数とcos関数のグラフ
https://qiita.com/niwasawa/items/6d9aba43f3cdba5ca725

# ワーキングディレクトリへ移動
docker$ cd working_dir
# 作ったファイルがあるかの確認
docker:working_dir$ ls
sin_cons.rb # ←ちゃんとローカルで作ったファイルがある
# 実行
docker:working_dir$ ruby sin_cos.rb
docker:working_dir$ ls
ruby_graph.png  sin_cons.rb # ← 出力されている

結果

中身の確認。問題なくできた。
ruby_graph.png

備考:なぜRuby?

鳥取県米子市出身で、松江にサテライトオフィスを持つ企業で働いている為。
松江といえばMatzさん、Rubyの聖地。仕事でもRubyをつかうしね。

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

敢えてRubyで学ぶ「ゼロから作るDeep Learning」Docker環境構築編

Dockerの環境構築

やること

  • ローカルのワーキングディレクトリを作成
  • DockerでRubyの環境を作成
  • Dockerで作成したワーキングディレクトリをマウントして環境に入る
  • Dockerに必要なパッケージ等をインストール
    • gnuplot & numo-gnuplot
      • プロット用
    • numo-narray
      • 行列計算 & 基礎的な数学の関数(sin cos exp等)

Docker環境を作って、ワーキングディレクトリをマウントする所まで

実行コマンド

# ローカルにワーキングディレクトリを作成
local$ mkdir working_dir
# RubyのDockerイメージを取得
local$ docker pull ruby:latest
# ワーキングディレクトリをマウントしてDocker環境に入る
local$ docker run -v /(workingディレクトリをおいているパス)/working_dir/:/working_dir/ -it ruby /bin/bash

なぜワーキングディレクトリをマウントしておくか

gnuplotで出力したファイルを、Dockerファイルシステムの外で簡単に確認できる。ローカル環境にコピーするコマンドを叩かず、手元の環境(macOS)のプレビューアプリ等で出力したpngイメージを確認できるので便利。他にも、特に設定せずに、自分のお気に入りのエディタを使えるのもよい。

Dockerに必要なパッケージ等をインストール

# gunuplotのインストール
docker$ apt update -y && apt upgrade -y && apt install -y gnuplot
# 各種gemのインストール
docker$ gem install numo-narray numo-gnuplot

とりあえず動かしてみる

ローカルのワーキングディレクトリに先人の記事のファイルをそのまま作成

Python vs Ruby 『ゼロから作るDeep Learning』 1章 sin関数とcos関数のグラフ
https://qiita.com/niwasawa/items/6d9aba43f3cdba5ca725

# ワーキングディレクトリへ移動
docker$ cd working_dir
# 作ったファイルがあるかの確認
docker:working_dir$ ls
sin_cons.rb # ←ちゃんとローカルで作ったファイルがある
# 実行
docker:working_dir$ ruby sin_cos.rb
docker:working_dir$ ls
ruby_graph.png  sin_cons.rb # ← 出力されている

結果

中身の確認。問題なくできた。
ruby_graph.png

備考:なぜRuby?

鳥取県米子市出身で、松江にサテライトオフィスを持つ企業で働いている為。
松江といえばMatzさん、Rubyの聖地。仕事でもRubyをつかうしね。

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

RSpecにおいてActionController:RoutingError No route matches [GET] "/assets/〜"

前提
 Dockerで開発
 rails 5.2.1

エラー内容
 ActionController:RoutingError No route matches [GET] "/assets/〜"

エラーが起こった状況
 実装したcontrollerおよびテンプレートに対するテストを追加しようとした。
 requests specについては、上記エラーは出なかった。
 system specを作成しbundle exec rspecを実行すると、上記エラーが出てしまう。

解決方法
config/enviroments/test.rbに以下を追加した。

test.rb
  config.assets.paths << Rails.root.join('app', 'assets', 'images', 'plugins', 'rs-plugin', 'assets')

エラーで出ていたassetsファイルがassets/images/plugins/rs-plugin/assetsにあるため上記コードを追加。

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

Docker

前回につづき、またもメモ気味な内容

前回は時間もなかったのでさくさくと書いてみた。
Dockerを始めてみよう

今回はもう少しそこから進んでみる。

DockerHub

Dockerを使って環境を作成するというなら切っても切れない。
Docker Hub
指示に従い、普通に登録。

Dockerイメージ

Dockerコンテナのベースになるもの。
DockerHubのExploreみるといろいろある。(ubuntu,Centos,nginx…などなど)
このDockerイメージを実行してコンテナが作成されるっぽい。

docker run <イメージ名>

※ちなみにこの「run」は
docker create <イメージ名>
docker start <イメージ名>の合わせ技みたいね。

Dockerコンテナ

Dockerといえばこのコンテナ
イメージから実行された仮想環境。
起動されたコンテナは任意で停止させないと起動しっぱなしなので、用が済んだら
docker stop <イメージ名>docker rm <イメージ名>を入力しておくか、最初からstopかけたら削除のコマンドを入れておくなりしておこう。

Dockerファイル

Dockerイメージを作成するファイル
かるく作成してみたが、なかなか慣れが必要そう。
でも、これを使えるようになるとDockerイメージを自由に構成できるし、開発環境も統一出来そうっすね。いいね。
でも難しそう…

おい、楽しいぞDocker

触ってみると難しいことは今のところはまだなさそう。
コンテナとかイメージをちゃんと説明できそうもないが実際にアプリ作れるようになるともう少し理解が進むかも。

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

Dockerを始めてみよう その2

前回につづき、またもメモ気味な内容

前回は時間もなかったのでさくさくと書いてみた。
Dockerを始めてみよう

今回はもう少しそこから進んでみる。

DockerHub

Dockerを使って環境を作成するというなら切っても切れない。
Docker Hub
指示に従い、普通に登録。

Dockerイメージ

Dockerコンテナのベースになるもの。
DockerHubのExploreみるといろいろある。(ubuntu,Centos,nginx…などなど)
このDockerイメージを実行してコンテナが作成されるっぽい。

docker run <イメージ名>

※ちなみにこの「run」は
docker create <イメージ名>
docker start <イメージ名>の合わせ技みたいね。

Dockerコンテナ

Dockerといえばこのコンテナ
イメージから実行された仮想環境。
起動されたコンテナは任意で停止させないと起動しっぱなしなので、用が済んだら
docker stop <イメージ名>docker rm <イメージ名>を入力しておくか、最初からstopかけたら削除のコマンドを入れておくなりしておこう。

Dockerファイル

Dockerイメージを作成するファイル
かるく作成してみたが、なかなか慣れが必要そう。
でも、これを使えるようになるとDockerイメージを自由に構成できるし、開発環境も統一出来そうっすね。いいね。
でも難しそう…

おい、楽しいぞDocker

触ってみると難しいことは今のところはまだなさそう。
コンテナとかイメージをちゃんと説明できそうもないが実際にアプリ作れるようになるともう少し理解が進むかも。

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

Dockerを使ったLaravelの環境構築

はじめに

最近は現場でも、Docker環境下でLaravleを触ることが増えて少しは知見も溜まってきたかと思いますので、忘れないように忘備録として残しておきます

ここでは、dockerdocker-composeのインストールに関しては触れませんので、まだの方はこちらの記事がわかりやすかったので参考にしてみてください

記事内の不備や、もっとこうした方がいいいよというアドバイスがございましたらコメントお願いします

構成

任意のディレクトリにSampleProjectを作成して下記のディテクトり構成で環境を構築します

SampleProject
  ├── docker-compose.yml
  ├── docker
  │   ├── php
  │   │    ├── Dockfile
  │   │    └── php.ini
  │   └── nginx
  │        └── default.conf
  └── src
      └──  Laravelのプロジェクトファイル

docker-compose.ymlの中身

docker-compose.ymlは複数のコンテナを同時に動かすためのツールである、Docker Composeを利用するために使用するYAMLファイルです

docker-compose.yml
version: '3'

services:
  php:
    container_name: php-1
    build: ./docker/php
    volumes:
      - ./src:/var/www

  nginx:
    image: nginx
    container_name: nginx-1
    ports:
      - 80:80
    volumes:
      - ./src:/var/www
      - ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf
    depends_on:
      - php

  db:
    image: mysql:5.7
    container_name: db-host-1
    environment:
      MYSQL_ROOT_PASSWORD: root
      MYSQL_DATABASE: sample_project
      MYSQL_USER: docker
      MYSQL_PASSWORD: docker
      TZ: 'Asia/Tokyo'
    command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
    volumes:
      - ./docker/db/data:/var/lib/mysql
      - ./docker/db/my.cnf:/etc/mysql/conf.d/my.cnf
      - ./docker/db/sql:/docker-entrypoint-initdb.d
    ports:
      - 3306:3306

  node:
    image: node:12.13-alpine
    tty: true
    volumes:
      - ./src:/var/www
    working_dir: /var/www

Dockerfileの中身

DockerfileとはDocker上で動作させるコンテナの構成情報を記述するためのファイルです

FROM php:7.2-fpm
COPY php.ini /usr/local/etc/php/

RUN apt-get update \
  && apt-get install -y zlib1g-dev mariadb-client \
  && docker-php-ext-install zip pdo_mysql

#Composer install
RUN php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
RUN php composer-setup.php
RUN php -r "unlink('composer-setup.php');"
RUN mv composer.phar /usr/local/bin/composer

ENV COMPOSER_ALLOW_SUPERUSER 1

ENV COMPOSER_HOME /composer

ENV PATH $PATH:/composer/vendor/bin


WORKDIR /var/www

RUN composer global require "laravel/installer"

php.iniの中身

php.ini
[Date]
date.timezone = "Asia/Tokyo"
[mbstring]
mbstring.internal_encoding = "UTF-8"
mbstring.language = "Japanese"

default.confの中身

default.confはngninxの設定情報を記述するためのファイルです

default.conf
server {
  listen 80;
    index index.php index.html;
    root /var/www/public;

  location / {
    try_files $uri $uri/ /index.php?$query_string;
    }

  location ~ \.php$ {

    fastcgi_split_path_info ^(.+\.php)(/.+)$;
    fastcgi_pass php:9000;
    fastcgi_index index.php;
    include fastcgi_params;
      fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
      fastcgi_param PATH_INFO $fastcgi_path_info;
  }
 }

環境構築の手順

はじめに任意の場所にSampleProjectを作成する

~/ $ mkdir SampleProject

docker-compose.ymlファイルを作成して、中身は冒頭で紹介したdocker-compose.ymlの内容をコピペする

~/SampleProject $ touch docker-compose.yml

Dockerfilephp.iniを作成して、中身は冒頭で紹介したそれぞれのファイルの内容をコピペする

~/SampleProject/docker/php $ touch Dockerfile
~/SampleProject/docker/php $ touch php.ini

defalt.confファイルを作成して、中身は冒頭で紹介したdefault.confの内容をコピペする

~/SampleProject/nginx $ touch default.conf

docker-compose.ymlファイルがあるディレクトリ内で下記コマンドを実行してdockerを起動させる

~/SampleProject $ docker-compose up -d

Creating php                ... done
Creating db-host            ... done
Creating sampleproject_node ... done
Creating nginx              ... done

phpのコンテナ内に入る

~/SampleProject $ docker-compose exec php bash

phpコンテナ内でLaravelプロジェクトを作成する

root@7eb4359bf51c:/var/www# laravel new

phpコンテナを抜ける

root@7eb4359bf51c:/var/www# exit

これでSampleProject/src内にLaravelのプロジェクトファイルが作成され、
http://localhost/ にアクセスするとおなじみのWelcomeページが表示されます

スクリーンショット 2020-02-01 23.33.35.png

MySQLコンテナに入る

~/SampleProject $ docker-compose exec db bash

MySQLコンテナ内でMySQLにログイン

root@630fbaf32806:/# mysql -u root -proot

今回のプロジェクトで使用するデータベースを作成する
データベース名はdocker-compose.ymlで指定した、MYSQL_DATABASEと合わせる必要があります

mysql> mysql> CREATE DATABASE sample_project;

Query OK, 1 row affected (0.01 sec) 

Laravelのプロジェクトファイル内の.envファイルを下記に修正する

.env
DB_CONNECTION=mysql
DB_HOST=db # docker-compose.ymlに記載したDBのサービス名
DB_PORT=3306
DB_DATABASE=sample_project # 使用するDB名
DB_USERNAME=root
DB_PASSWORD=root

phpコンテナに入る

~/SampleProject $ docker-compose exec php bash

phpコンテナ内でマイグレーションを実行する

root@7eb4359bf51c:/var/www# php artisan migrate

Migration table created successfully.
Migrating: 2014_10_12_000000_create_users_table
Migrated:  2014_10_12_000000_create_users_table (0.06 seconds)
Migrating: 2014_10_12_100000_create_password_resets_table
Migrated:  2014_10_12_100000_create_password_resets_table (0.04 seconds)
Migrating: 2019_08_19_000000_create_failed_jobs_table
Migrated:  2019_08_19_000000_create_failed_jobs_table (0.02 seconds)

マイグレーションが無事に実行されれば成功です!!

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

#Docker hub から pull するときのメジャー・マイナー・パッチバージョン指定

https://hub.docker.com/_/alpine

ゆるい指定をしたときは、最新版を pull するというような運用をしているイメージが多いだろう。

同じイメージなのかどうかを確認するには pull したときの Digest が同じかどうかを見ることで確認できそうだ。

image

$ docker pull alpine:latest
Digest: sha256:ab00606a42621fb68f2ed6ad3c88be54397f981a7b70a79db3d1172b11c4367d
$ docker pull alpine:3     
Digest: sha256:ab00606a42621fb68f2ed6ad3c88be54397f981a7b70a79db3d1172b11c4367d
$ docker pull alpine:3.11  
Digest: sha256:ab00606a42621fb68f2ed6ad3c88be54397f981a7b70a79db3d1172b11c4367d
$ docker pull alpine:3.10
Digest: sha256:7c3773f7bcc969f03f8f653910001d99a9d324b4b9caa008846ad2c3089f5a5f
$ docker pull alpine:3.10.4
Digest: sha256:7c3773f7bcc969f03f8f653910001d99a9d324b4b9caa008846ad2c3089f5a5f

ゆるい指定の場合は、pullする時期によって、別のものが取れてしまう

Dockerfileなどに書くときは、なるべくマイナーバージョン、パッチバージョンまで指定しておきたい

Original by Github issue

https://github.com/YumaInaura/YumaInaura/issues/2978

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

docker-compose buildでNOTICE: chromedriver-helper is deprecated after 2019-03-31.と出た時の対処法

docker-compose build

とgemfileをアップデートをしたときに、エラーにはならなかったが

NOTICE: chromedriver-helper is deprecated after 2019-03-31.       

Please update to use the 'webdrivers' gem instead.               
See https://github.com/flavorjones/chromedriver-helper/issues/83

という注意喚起のメッセージが出た時の対処法について、僕が解決した方法を書いていこうと思います。

メッセージの意味

NOTICE: chromedriver-helper is deprecated after 2019-03-31.       

Please update to use the 'webdrivers' gem instead.               
See https://github.com/flavorjones/chromedriver-helper/issues/83

まずこの注意喚起の文章の意味を知らないことには話が進まないので、これを日本語訳していこうと思います。

結論、この文章は

chromedriver-helperはサービス終了したのでwebdrivers gemに変更してください。

という意味の文になります。

対処法

意味が分かれば対処は簡単です。まずはgemfileを開いてください。

gemfileの

group :test doの中のchromedriver-helperを消してwebdriversに書き直します。

そして再び

docker-compose build

を実行するとこの注意喚起の文は表示されなくなっていると思います。参考になれば幸いです。

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

Dockerで脆弱性WebアプリケーションDVWA構築

DockerでDVWA構築

今回は、Dockerで脆弱性WebアプリケーションであるDVWAの構築手順をまとめていきたいと思います。

DVWAを構築する目的は、攻撃手法を学び、より安全なWebアプリケーションを作成するためです。

そのため次回以降の記事では、SQLインジェクション、CSRF、XSSといったことにも触れていきたいと思います。

DVWAはPHP/MySQL製の脆弱Webアプリケーション

DVWA(Damn Vulnerable Web Application)は、PHP/MySQL製のセキュリティ脆弱性のあるWebアプリケーションです。

このDVWAに様々な攻撃を仕掛けることによってユーザーの情報を取得できるので、脆弱性を産む可能性のあるコードについて学ぶことができます。

またセキュリティレベルをLow、Medium、Highに設定できるので、基礎的な攻撃手法から応用的なものまで学ぶことができます

Dockerで簡潔にDVWA構築

今回はより短時間で容量をくわず構築するため、DockerでDVWAのイメージをプルし構築しました

前提条件

  • yum updateしたCentOS7サーバー(グローバル接続済み)

DVWAのイメージをプルして起動

まず、初期状態なので、dockerをインストール。

# yum install docker

続いてdockerコマンドで、DVWAのイメージをプル(インストール)します。

# docker pull infoslack/dvwa

続いて、HTTPの外部からアクセスされるポート番号を80、コンテナ側のポート番号を80、MySQL側も外部からアクセスされるポート番号を3306、コンテナ側のポート番号を3306に指定し、バックグラウンドでdockerを起動します。

# docker run -d -p 80:80 -p 3306:3306 -e MYSQL_PASS="mypass" infoslack/dvwa

ここまでできたら、グローバルIPアドレス/setup.php(グローバルに繋がっているサーバーの場合)にアクセスすると、DVWAのセットアップ画面がでます。

create/Reset Databaseを選択し、パスワード入力画面でUsernameをadmin、Passwordをpasswordと入力。

無事ログインできたら、DVWAの構築は完了です。

参考URL

https://www.pupha.net/archives/1297/
https://securitybytes.io/docking-dvwa-be9abf862210

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

仕事中にこっそりヒーリングっどする

できること

cureutils で無限の癒やしを得る。

※注意点

  • プリキュア初心者(ヒーリングっどプリキュアが初プリキュア)です。
  • ヒーリングっどプリキュアのヒロインたちは実装されていません(タイトル詐欺)。
  • この記事は湧いた頭を冷ますために書いたものです。過度な期待はしないでください。
  • 技術的なものは何も得られません。

何があったのか

顧客をコントロールできないPM、納期直前まで続く仕様変更、メンバーはわがままばっかり言う、連日の午前様。
もぅマヂ無理。
癒やし、癒やしが欲しい……。

合間合間の気分転換。
Tera Term の小さな画面、fortune が吐き出した言葉たちを眺める。

こんな感じ。

$ while :; do fortune; sleep 300; echo -e "\n"; done
Electrocution, n.:
        Burning at the stake with all the modern improvements.


Last week a cop stopped me in my car.  He asked me if I had a police
record.  I said, no, but I have the new DEVO album.  Cops have no sense
of humor.


We are all agreed that your theory is crazy.  The question which
divides us is whether it is crazy enough to have a chance of being
correct.  My own feeling is that it is not crazy enough.
                -- Niels Bohr

でも、アメリカンなジョークはよくわからない……。
もっと癒やされたい……。
なにかないか……。

あった……!

ありがとうございます、ありがとうございます。

面白そうなものを見つけてしまったからには、使ってみなくてはならない。

コンテナ化

開発環境を汚したくなかったので、Docker で実行環境を用意した。
ついでにコマンド一発でプリティでキュアキュアな口上を述べて頂けるようにした。

使い方

docker-compose.yml はこう。

version: '3'
services:
  app:
    image: ancolin/cureutils:latest
    environment:
      - USE_IN_BATTLE=TRUE          # TRUE: 戦闘中のセリフを使う
      - ONLY_USE_IN_BATTLE=FALSE    # TRUE: 戦闘中のセリフだけ使う
      - PRINT_QUICK=FALSE           # TRUE: セリフをまとめて表示する
      - HUMAN_NAME=                 # 名前: 特定のヒロインのセリフだけ使う

こんな shell スクリプトを書くと実行が楽ちんで良い。

$ cat cure.sh
#!/bin/sh
set -eu
cd /home/ancolin/docker/docker-cureutils
while :; do
    docker-compose run --rm app
    sleep 60
    echo -e "\n"
done
$ ./cure.sh
スカイローズ・トランスレイト!
青いバラは秘密のしるし! ミルキィローズ!

by 美々野くるみ / ミルキィローズ (milky_rose)


チェインジ!プリキュア・ビートアップ!
ブルーのハートは希望のしるし!
つみたてフレッシュ、キュアベリー!
レッツプリキュア!

by 蒼乃美希 / キュアベリー (cure_berry)

もともとの cureutils の機能も使える。

$ docker-compose run --rm app cure version
Cureutils 1.2.0
$

改善効果

世の中の仕組みを理解できた。

  • PMは顧客をコントロールできない。
  • 仕様変更は納品したあとも続く。
  • メンバーは簡単に辞める(病める)。
  • マイカーorタクシーなら24/7通勤可能。

それでもくじけないポジティブな心を得ることができる(できたとは言ってない)。

宣伝

プリキュアって戦士だったんだね、魔法少女じゃないんだね、知らなかったよ。
みんなもプリキュアみよね。
プリキュア

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

MacでDocker+Rails+MySQL環境構築手順メモ

勉強するときに環境構築にかなりつまづいたので、その手順をメモしときたいと思います。

環境構築手順

前提として、rails開発用のディレクトリが用意されているものとします。

以下の手順に従ってファイル作成やコマンドを実行していけば問題なく手順は整うと思います。

Dockerfileの作成(以下をコピペ)

# rubyはお好みのバージョンで(ローカルのバージョンがいいと思います)

FROM ruby:2.6.3

# 必要なパッケージのインストール(基本的に必要になってくるものだと思うので削らないこと)

RUN apt-get update -qq && \

    apt-get install -y build-essential \

                       libpq-dev \        

                       nodejs           


# 作業ディレクトリの作成、設定

RUN mkdir /app_name

##作業ディレクトリ名をAPP_ROOTに割り当てて、以下$APP_ROOTで参照

ENV APP_ROOT /app_name

WORKDIR $APP_ROOT


# ホスト側(ローカル)のGemfileを追加する

ADD ./Gemfile $APP_ROOT/Gemfile

ADD ./Gemfile.lock $APP_ROOT/Gemfile.lock


# Gemfileのbundle install

RUN bundle install

ADD . $APP_ROOT

Gemfileの作成(以下をコピペ)

source 'https://rubygems.org'

#好きなバージョンを指定

gem 'rails', '5.2.2'

空のGemfile.lockの作成

何も書かなくていいです。

docker-compose.ymlを作成(以下をコピペ)

docker-compose.yml
version: '3'

services:
  db:
    image: mysql:8.0.17
    command: mysqld --default-authentication-plugin=mysql_native_password
    volumes:
      - ./db/mysql_data:/var/lib/mysql
    environment:
      MYSQL_ROOT_PASSWORD: root
      MYSQL_DATABASE: root
    ports:
      - "4306:3306"

  web:
    build: .
    command: bundle exec rails s -p 3000 -b '0.0.0.0'
    volumes:
      - .:/app_name
    ports:
      - "3000:3000"
    links:
      - db

ターミナルでrails newを実行

$ docker-compose run web rails new . --force --database=mysql --skip-bundle

上のコマンドを打ってしばらく待つ

database.ymlを修正(一度全て消してから以下をコピペ)

database.yml
default: &default
  adapter: mysql2
  encoding: utf8
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: <%= ENV.fetch("MYSQL_USERNAME", "root") %>
  password: <%= ENV.fetch("MYSQL_PASSWORD", "root") %>
  host: <%= ENV.fetch("MYSQL_HOST", "db") %>

development:
  <<: *default
  database: app_name_development

test:
  <<: *default
  database: app_name_test

production:
  <<: *default
  database: app_name_production
  username: app_name
  password: <%= ENV['APP_NAME_DATABASE_PASSWORD'] %>

ターミナルでDockerを起動

docker-compose build

しばらく待つ。できたら以下を実行。

docker-compose up

DB作成

別のターミナルを開いて以下を実行

docker-compose run web rails db:create

ブラウザでlocalhost:3000にアクセス

サーバーが起動してたらおk。

サーバーを止める

docker-compose down

サーバーを再起動

docker-compose up

まとめ

これでrailsアプリ作成の準備が整うはずです。
環境構築は完全に初見ごろしだと思うので誰かの助けに慣れたら嬉しいです。
また、何かおかしい点とかあれば言って頂けるとありがたいです。

お付き合い頂きありがとうございました。

参考サイト

https://qiita.com/azul915/items/5b7063cbc80192343fc0

https://qiita.com/take18k_tech/items/ada21511c551bcfe0b8c

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

Dockerを使用したRails環境の構築について

はじめに

Railsのチュートリアルを始める際に、DokerによるRails環境構築方法を備忘録的にまとめました。
間違い等ございましたら、ご指摘いただけると嬉しいです。
(ちょっと…自分が書いたのを見返してみたら、自分用とはいえ文章のクオリティがすこぶる悪い気がするので、いずれ修正します…。)

環境と自己紹介

  • PCはWindows
  • Docker for Windows
  • Ruby 2.5.3
  • Mysql 5.7
  • Rails 5.1.6(最終的に5.1.7がインストールされていました。。)
  • Heroku

(上記はいずれも、現時点でのRailsチュートリアルに沿ったツール、バージョンにしたつもりです。)

  • GitHub
    (チュートリアルではprivateにするため、Bitbucketを使用していますが、GitHubでもprivateにできそうなのでGitHubにしてみました。)

  • プログラマでも何でもありません。IT系の企業でもない会社でIT担当してます。

  • 普段はコードは書きません。年齢もそこそこなので、半分趣味の世界ですが、何か自分や会社の役に立てたいと思います。

  • プログラムにはPython(Django)から入り、チュートリアルをポチポチ進めました。

  • いずれ、頑張ってCircleCIやAWSの環境を組み込んでいければいいなと思います。

■ チュートリアル前の準備

コードを書き始める前に、必要な各種ファイルの準備を進めていきます。
自分の中で、どこの記述がどこに関連しているのかがわかりにくかったので、コメント多くして整理してみました。

1. Gemfile、Gemfile.lockを準備

まずは、GemfileとGemfile.lockを作成しました。

Gemfile.
# 最初はこの2行だけ。

# gemのダウンロード元のURL
source 'https://rubygems.org'

# gemとバージョンを指定
gem 'rails', '5.1.6'
Gemfile.lock
# 空の状態で作成しました。

2. Docker関係の準備

続いて、Dockerfileとdocker-compose.ymlを準備しました。

Dockerfile.
# このイメージをもとにして、以降、Railsに必要なパッケージなどを追加していく。
FROM ruby:2.5.3

# コンテナ内で実行するコマンドを定義
# Railsに必要なパッケージのインストール(build-essentialとnodejs)
RUN apt-get update -qq && apt-get install -y build-essential nodejs

# rootディレクトリにappディレクトリを作成し、appへ移動
# 今後Railsのプロジェクトファイルはappディレクトリ内に作成される。
RUN mkdir /app
WORKDIR /app

# GemfileとGemfile.lockをPC上からコピー
COPY Gemfile /app/Gemfile
COPY Gemfile.lock /app/Gemfile.lock

# Gemfileに記述されたgemをインストールする
# Railsもgemで公開されているため、GemfileにRailsとそのバージョンを書いておく
# これにより、Rubyのコンテナ内に、GemfileをもとにRailsがインストールされる。
RUN bundle install

# Dockerfileが置いてあるフォルダの内容をコピーする
# Railsのアプリケーション実行に必要なファイルをすべてコンテナの中に含める
COPY . /app


docker-compose.yml
# docker-compose.ymlのバージョン
version: '3'

# サービスを定義する。ここでは、webとdbの2つ
services:

# web定義ここから
  web:
    build: . # docker-compose.ymlと同じフォルダにあるDockerfileからイメージをビルドする。
    command: bundle exec rails s -p 3000 -b '0.0.0.0' # デフォルトで実行するコマンドを記載。ここではrailsのサーバー起動。
    volumes: # PC上のディレクトリをコンテナ上のappディレクトリにマウント。PC上の修正がコンテナにも反映される。
      - .:/app
    ports: # コンテナ外部に3000ポートを開放。<コンテナ外に公開するポート>:<コンテナ内で転送されるポート>
      - "3000:3000"
    depends_on: # webコンテナが起動する前にdbが起動するようにする。
      - db
    tty: true # コマンドラインでの操作やコマンドの出力結果を対話的にうための疑似ttyを割り当てる。pryを使用するために必要。
    stdin_open: true # 標準入力を開き続ける。これがないと、docker exec -itでコンテナを操作できないんだと思う。
# web定義ここまで

# db定義ここから
  db:
    image: mysql:5.7 # mysql5.7のイメージからビルドする。
    environment: # コンテナ内の環境変数に設定する。
      MYSQL_ROOT_PASSWORD: password # mysqlのrootパスワード
    ports:
      - '3306:3306' # コンテナ外部に3306ポートを開放し、コンテナ内の3306に転送する。
    volumes: # これがないと、データはコンテナ内に直に保存されるが、コンテナ削除時にデータも消えてしまう。
      - mysql_data:/var/lib/mysql
# db定義ここまで

# volume定義ここから
volumes: # PC上にデータを保持するための領域を作成する。
  mysql_data:
# volume定義ここまで

3.環境構築

実際に、コマンドを打って環境を構築してみました。手順はこんな感じで行いました。
(解釈に間違いありましたらご教示ください。。)

1.$ docker-compose run web rails new . --force --database=mysql
  →これにより、新しいRailsのプロジェクトが作成される。
   この時は、Gemfileにはrailsのみが記載されているので、
   rubyのイメージをベースとして、railsがインストールされたイメージがビルドされる。
2.--forceオプションをつけているので、Gemfile、Gemfile.lockにgemのインストールが追記(上書き)される。
  →この時点では、追記されたgemはまだイメージにインストールされていない。
   「new .」の「.」はPC上のカレントディレクトリを意味している。
    新しいRailsプロジェクトの各ファイルはカレントディレクトリ上に作成される。
3.--database=mysqlを指定しているので、config/database.ymlの設定がMySQLになる。
   (標準はSQLite3)
3.$ docker-compose build
  →このコマンドにより、Gemfileに追加されたgemがインストールされたイメージがビルドされる。
   Dockerfileの最後の行にある COPYにより、作成された各ファイルが、
   カレントディレクトリから、イメージ上のappディレクトリにコピーされる。

4.データベースの接続設定

/config/database.yml
default: &default
  adapter: mysql2
  encoding: utf8
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: root
  password: password # docker-composer.ymlに設定したパスワード
  host: db # docker-compose.ymlに作成したdbのサービス名

5.サーバーの起動

サーバーを起動します。
$ docker-compose up -dと入力してサーバーを起動します。

test_db_1 is up-to-date
Creating test_web_1 ... done

となりました。

$ docker-compose psでコンテナが起動しているか確認します。

   Name                 Command               State                 Ports
---------------------------------------------------------------------------------------
test_db_1    docker-entrypoint.sh mysqld      Up      0.0.0.0:3306->3306/tcp, 33060/tcp
test_web_1   bundle exec rails s -p 300 ...   Up      0.0.0.0:3000->3000/tcp

ただ、今の状態では、webとdbのコンテナが立ち上がったのみで、
dbのコンテナにはデータベースが作成されていない状態なので、
次のステップでdbコンテナのmysqlにデータベースを作成する。

6.開発環境用のデータベースを作成する

$ docker-compose run web bundle exec rake db:create

Starting test_db_1 ... done
Created database 'app_development'
Created database 'app_test'

7.Webページにアクセスして確認

localhost:3000にアクセスしてみました。。。railsのバージョンを5.1.6と指定したつもりが、5.1.7になっているのは…如何に。

image.png

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

[Docker]クソ重いHello, World!イメージを劇的に軽くする

Docker v17くらいからMultiStageBuildという機能が実装されています。
今まで、「Hello,Worldを出力するだけのImageなのに、ベースImageサイズが大きくて、結局出来上がるImageも数百MBになるんだけど、、、」と、なっていたものが、BuildはGoのImage,実行はalpineのImageとできるようになりました。(v17以前でもできなくはなかったけど、めんどくさかった)
これが一体どういうことなのかをみていきたいと思います。

今回は、Hello, World!を出力するコンテナを作成していきます。

main.go
package main

import "fmt"

func main() {
        fmt.Println("Hello, World!")
}

MultiStageBuildを使わない場合

Dockerfileは以下のようになります。

FROM golang:1.13.7-alpine3.11

COPY ./main.go ./

RUN go build -o ./hello ./main.go

ENTRYPOINT ["./hello"]

それぞれ同じディレクトリに存在する状態で、以下のコマンドを打ってみましょう。

$ docker build -t hello:0.1 .

ビルド出来たらRunしてみます。

$ docker run hello:0.1
Hello World!

ちゃんとmain.goがbuildされて、実行されました。では、この時のImageサイズをみてみます。

$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
hello               0.1                 ea6e8238c063        49 seconds ago      361MB
golang              1.13.7-alpine3.11   87eefb76f0a8        9 days ago          359MB

このように、golangのcontainer Imageがベースになっているため、サイズが大きくなっています。
Hello, World!を出力するだけのコンテナなのに、これじゃあ重すぎですね。。。

MultiStageBuildを使う場合

Dockerfileは以下のようになります。

#Stage 1
FROM golang:1.13.7-alpine3.11 as builder
COPY ./main.go ./
RUN go build -o /hello ./main.go

#Stage 2
FROM alpine:3.11
COPY --from=builder /hello .
ENTRYPOINT ["./hello"]

Stage1はgolangのImageを使っていて、ここでmain.goをbuildしています。そしてその実行ファイルをStage2に持ってきて、こっちで実行すると言う形です。

$ docker run hello:0.1
Hello World!

こちらもちゃんと実行されました。同様に、Imageのサイズをみてみます。

$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
hello               0.1                 15f814bf3a87        12 seconds ago      7.6MB
<none>              <none>              6f908ead16f8        13 seconds ago      361MB
golang              1.13.7-alpine3.11   87eefb76f0a8        9 days ago          359MB
alpine              3.11                e7d92cdc71fe        2 weeks ago         5.59MB

ここで<none>はStage1のImageで、<hello>がStage2のImageになっています。Hello, Worldを出力するImageは<hello>なので、そのサイズなんと7.6MBです!MultiStageBuildを使わない場合と比べて約350MBもImageサイズを削減することを実現できました!!

まとめ

ということで、今回はDockerのMultiStageBuildの機能について、触れてみました。サイズの重たいBuild環境を分離することで、Imageサイズの削減を可能にすることを挙げましたが、他にもStageで分けることで、Dockerfileの保守のしやすさが上がる、セキュリティに強くなるなど、良い事が沢山あります!みなさまも使った事がない方は一度チャレンジしてみてください!

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

ボリューム(volumes)で何ができるか気づけたから解説!(docker-compose)

はじめに

Dockerって機能多すぎて、とりあえず実装して何も理解してないケースありませんか?
僕はボリュームがそのうちの1つでした...。

『これからDockerdocker-composeを使い始める!』『volumesが何をしているのか分からない...』という方には是非見て頂きたい記事となっております!

これを読むことで何が分かるのか

  • Dockerのボリュームとは何か分かる
  • docker-composeのvolumesに何を設定するべきか分かる
  • docker volume lsをした時に何故か増えてるボリューム達が何なのか分かる
  • DockerにてNginxを使いたい時に、コンテナ間のリソース共有実装の足がかりになる

データを永続化するとは

突然ですが、貴重なデータが含まれているデータってシンプルに消えてほしくないですよね(。・_・。)
そんな貴重なデータをDockerにてDB(データベース)コンテナとして起動させていると、docker psコマンドにて以下の画像のようにDBコンテナ(postgreSQL)が立ち上がっていることが確認できますよね。
docker-ps加工済.jpg

ここでもし誤ってDBコンテナを消してしまう事があったら、どうなってしまうでしょうか...

DBに蓄積してきたデータが全て消えます。

ですがコンテナ管理において、デファクトスタンダードとされているDocker様がそんな状況を放置するでしょうか?いや否です。

コンテナのデータを別の場所で保管できるボリューム機能があります!

ボリュームを使えば、例えDBのコンテナを消してしまっても、データを別場所にて保管しているので、復元できるという仕組みですね(/・ω・)/

ボリュームとは

こちらは僕が説明するよりコチラの記事(Docker、ボリューム(Volume)について真面目に調べた - Qiita *1)の説明が優れていると思ったので、引用させていただきます。

  • ボリュームとは、データを永続化できる場所のことである。
    • 外部HDDのようなイメージ。コンテナ本体にマウントして使う。
  • コンテナボリューム の違いについて
    • コンテナ内部にデータ(ファイル)を保存しても、コンテナ破棄すると消えてしまう。
    • なので、データを永続化したいときは、コンテナの外にデータを置く必要がある。
    • その場所のことを、ボリュームと呼ぶ。
  • ボリューム(=データを永続化できる場所) は2種類ある。
    • ホストのディレクトリ(ファイル)
      • ホストで ls で見えるモノ。
    • Docker の リソースとしての Volume
      • docker volume ls で見えるモノ。
    • (本当は、これ以外にもNFSなども指定できるようだ)
  • ボリュームをコンテナにマウントすることで、コンテナからアクセスできるようになる。
  • あるいは--volumes-from <コンテナ名>とすると、指定したコンテナのマウントと同じようにマウントできる。

今までの僕のdocker-composeは...?

まずは『こんな記事にわざわざ見に来ていただいて感謝』ということで、恥ずかしながらも前まで設定していた駆け出しdocker-composeを公開します。笑

docker-compose.yml
version: "3"
services: 
    db:                     # データベース(つまり消えて欲しくない貴重なデータ群)が含まれているプログラム達
        image: postgres
        environment:
            - POSTGRES_USER
            - POSTGRES_PASSWORD
        ports:
            - "5432:5432"
    back:                   # バックエンド(静的ファイル配信やmodelの定義、routing設定など)のプログラム達
        build: .
        volumes:            # 注目
            - .:/code       # 注目
        ports: 
            - "8000:8000"
        command: gunicorn config.wsgi -b 0.0.0.0:8000 # サーバー起動
        depends_on: 
            - db

上記の『ボリュームとは』から言うと、

volumes: 
- .:/code

という記述は、ホスト側のディレクトリ(カレントディレクトリ化)コンテナ内(code下)マウントして、バックエンドのプログラムを永続化しているということになります(/・ω・)/

dbにはvolumesを記述していないので、もちろん消えて欲しくない貴重なデータが含まれたデータベースは永続化していません!?!?

もう序盤で既にオチを悟った方もいるかもしれませんが(笑)、詳しくおかしな点について解説していきますね!

余談(ここはスルーしてOKです)

恐らくこれを見た経験者は『いやそこボリュームとしてデータ永続化してどないすんねん!』と言うでしょう。笑
しかも事前にvolumeの名前も定義もしていないので、docker volume lsでボリューム一覧を出力した時にも、どれがどのボリュームなのかわからない。
やはり独学で実装していると、docker volume lsした時に知らないボリュームが増えていくことに『違和感』を覚えることはありましたが、『おかしい』ことを確信せずに実装していたのです。

なぜ勉強することになったか?

現在(2020/2/7)開発中である投票サービス《Shappar》で静的ファイルをDocker×Nginxで配信しようと思ったところ、以下の記事を見つけました。

DjangoをDocker Composeでupしよう! *2

↑上記事は要約すると『DjangoをGunicorn×Nginx×docker-composeを使ってサーバーを立ち上げよう!』というものです。そこでdocker-composeの使用例として以下のような記述を見つけました。

docker-compose.yml
version: '3.7'
services:
  django:
    restart: always
    build: ./django
    expose:
      - "3031"
    depends_on:
      - postgres
    command: bash -c "python manage.py migrate && gunicorn my_docker_project.wsgi -b 0.0.0.0:3031"
    volumes:
      - "staticdata:/opt/static/"        # 3. ナンダコレ
  nginx:
    restart: always
    image: nginx
    depends_on:
      - django
    ports:
      - "80:80"
    volumes:
      - "./nginx/:/etc/nginx/"            # 1. ナンダコレ
      - "staticdata:/opt/apps/static/"    # 1. ナンダコレ  3. ナンダコレ
  postgres:
    image: postgres
    ports:
      - "5432:5432"
    volumes:
      - "dbdata:/var/lib/postgresql/data"
    environment:
      POSTGRES_PASSWORD: hogemojahogemoja

volumes:  # 2.ナンダコレ
  dbdata:
  staticdata:

この記述を見て思った異変(自分の無知)の数々

  1. ./nginx/:って...下のdbdata:みたいにカンマスラッシュ付けずにnginx:←こうやって書き方統一しろよな!(※ヤバい間違いをしています。)
  2. なんか一番下にvolumesおる...
  3. なぜ2回もstaticdataを指定しているのだ...

先ほどの疑問点について勉強を開始

1. ディレクトリの記述方法について

とりまdockerの公式docs *3に飛びます。すると以下の記述がありました。

docker-compose.yml
volumes:
  # 1.パスを指定したら、Engine はボリュームを作成
  - /var/lib/mysql

  # 2.絶対パスを指定しての割り当て
  - /opt/data:/var/lib/mysql

  # 3.ホスト上のパスを指定する時、Compose ファイルからのパスを指定
  - ./cache:/tmp/cache

  # 4.ユーザの相対パスを使用
  - ~/configs:/etc/configs/:ro

  # 5.名前付きボリューム(Named volume)
  - datavolume:/var/lib/mysq

記述によって動作が全く違う件

引用してきた上記のdocker-composeから、僕が疑問に思った記述方法は③と⑤に当てはまります。

結論から言うと、./nginx/:/etc/nginx/nginx:/etc/nginx/とでは、プログラムの実行内容が全然違います。

./nginx/: docker-compose.ymlが存在しているディレクトリから、相対パスによってマウントする側の指定先としてホスト側のディレクトリの指定を行います。
                            
nginx: この書き方をすると、マウントする側の指定先としてnginxという名前付きボリュームを探します。

マウントする側の指定方法として、./nginx/:はカレントディレクトリから〜、対してnginx:名前付きボリュームありきで〜、というように方法が全く異なっていますね。

はて?名前付きボリュームですか。

2. 名前付きボリュームとは?(一番下にvolumesがいる訳)

ここで先ほどの2つ目の疑問点『なんか一番下にvolumesおる...』が解決できます。
なんとdocker-composeのvolumesにて名前を定義することによって、ボリュームに名前を付けています。

実際に先ほどのdocker-composeをもう一度見てみましょう。

docker-compose.yml
version: '3.7'
services:
  django:
    restart: always
    build: ./django
    expose:
      - "3031"
    depends_on:
      - postgres
    command: bash -c "python manage.py migrate && gunicorn my_docker_project.wsgi -b 0.0.0.0:3031"
    volumes:
      - "staticdata:/opt/static/"         # staticdataは一番下のvolumeで定義してるやつ!
  nginx:
    restart: always
    image: nginx
    depends_on:
      - django
    ports:
      - "80:80"
    volumes:
      - "./nginx/:/etc/nginx/"            # docker-composeからnginxというディレクトリを相対パスにより指定
      - "staticdata:/opt/apps/static/"    # staticdataは一番下のvolumesで定義してるやつ!
  postgres:
    image: postgres
    ports:
      - "5432:5432"
    volumes:
      - "dbdata:/var/lib/postgresql/data" # dbdataはvolumesで定義してるやつ!
    environment:
      POSTGRES_PASSWORD: hogemojahogemoja

volumes:       # 名前付きボリュームを定義している
  dbdata:      # ボリューム
  staticdata:  # ボリューム

一番下のvolumesで名前を付けていますね〜。これによりボリュームが作成されます!
もし既に名前を付けているボリュームvolumesにて指定すると、その名前の付いたボリュームからデータを参照してきます。

つまり『データを永続化している』訳ですね(/・ω・)/

そして以下のことが言えます!

  • 先頭に./が付いていないdbdatastaticdataは一番下のvolumes:で名前を付けてから、名前付きボリュームとしてコンテナの外部に保管し、データを永続化している。
  • 逆に先頭に./が付いているnginxは相対パスでホスト側ののマウントするディレクトリを指定して、データを永続化している。ただし名前が付いていないので、VOLUME NAMEには20文字くらいの文字列が自動で名前として付きます。なので『もう一度使える』という意味での《永続化》はしていないですね。

果たして名前が付いているかdocker volume lsコマンドにて確認します!
volumename.jpg

《サービス名_ボリューム名》となっていることが確認できました〜。(画像ではアンダーバーが途切れてます。笑)

3. 何故2回もstaticdataを指定しているのだ...

1と2からなんとなく推測してみましょう。

今までの文章から考察するとこうなるかな...

  • 1的観点から記述法を見ると、名前付きボリューム(staticdata)でバックエンドとNginxにマウントをとっているのがわかる。
  • 2的観点から名前付きボリュームということはデータの永続化(データを別場所にて保管)をしている。
  • staticdataとdbdataを比較すると、dbdataではpostgresのvolumesの1つしか設定していなかった。

《データを別の場所にて管理》 & 《バックエンドとNginxの2つマウント取ってる。》

これらの要素からボリュームでデータを別場所で管理できることを利用して、コンテナ間のリソース共有をしている!

もしやバックエンドコンテナとNginxコンテナで静的ファイルを共有して、静的ファイルに関してだけNginxで配信するという形が取れるのでは...?

当初の僕の目的であったNginxで静的ファイルを配信することに繋がってきました:relaxed:

まとめ

最後まで読んでいただきありがとうございました。
Docker系は使えるツールがあまりにも多いので、本当に把握しきれないです。特にボリュームの概念をつかむのはある程度触ってからでないと難しいのかも知れません(コンテナの概念やマウントという言葉の意味など...)。

ですがどこかのDocker初心者が僕のようにボリュームの概念について躓かないように、初心者である僕の目線でわかりやすく記事にさせていただきました!

間違っている点やシンプルな感想などでもございましたら、お気軽にコメントください!!

バックエンド(Django,RESTAPI)やAWS,Dockerなどの勉強していたり、機械学習にも少し手を出しているわたくしの姿を発信しているTwitterがあるので、もし良ければ...@heacet43

参考文献

  1. Docker、ボリューム(Volume)について真面目に調べた - Qiita [https://qiita.com/gounx2/items/23b0dc8b8b95cc629f32]
  2. DjangoをDocker Composeでupしよう! - Qiita [https://qiita.com/kyhei_0727/items/e0eb4cfa46d71258f1be]
  3. Compose ファイル・リファレンス — Docker-docs-ja 17.06.Beta ドキュメント [http://docs.docker.jp/compose/compose-file.html#volumes-volume-driver]
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ボリュームで何ができるか気づけたから解説!(docker-compose)

はじめに

Dockerって機能多すぎて、とりあえず実装して何も理解してないケースありませんか?
僕はボリュームがそのうちの1つでした...。

『これからDockerdocker-composeを使い始める!』『volumesが何をしているのか分からない...』という方には是非見て頂きたい記事となっております!

これを読むことで何が分かるのか

  • Dockerのボリュームとは何か分かる
  • docker-composeのvolumesに何を設定するべきか分かる
  • docker volume lsをした時に何故か増えてるボリューム達が何なのか分かる
  • DockerにてNginxを使いたい時に、コンテナ間のリソース共有実装の足がかりになる

データを永続化するとは

突然ですが、貴重なデータが含まれているデータってシンプルに消えてほしくないですよね(。・_・。)
そんな貴重なデータをDockerにてDB(データベース)コンテナとして起動させていると、docker psコマンドにて以下の画像のようにDBコンテナ(postgreSQL)が立ち上がっていることが確認できますよね。
docker-ps加工済.jpg

ここでもし誤ってDBコンテナを消してしまう事があったら、どうなってしまうでしょうか...

DBに蓄積してきたデータが全て消えます。

ですがコンテナ管理において、デファクトスタンダードとされているDocker様がそんな状況を放置するでしょうか?いや否です。

コンテナのデータを別の場所で保管できるボリューム機能があります!

ボリュームを使えば、例えDBのコンテナを消してしまっても、データを別場所にて保管しているので、復元できるという仕組みですね(/・ω・)/

ボリューム(Data Volume)とは

こちらは僕が説明するよりコチラの記事(Docker、ボリューム(Volume)について真面目に調べた - Qiita *1)の説明が優れていると思ったので、引用させていただきます。

  • ボリュームとは、データを永続化できる場所のことである。
    • 外部HDDのようなイメージ。コンテナ本体にマウントして使う。
  • コンテナボリューム の違いについて
    • コンテナ内部にデータ(ファイル)を保存しても、コンテナ破棄すると消えてしまう。
    • なので、データを永続化したいときは、コンテナの外にデータを置く必要がある。
    • その場所のことを、ボリュームと呼ぶ。
  • ボリューム(=データを永続化できる場所) は2種類ある。
    • ホストのディレクトリ(ファイル)
      • ホストで ls で見えるモノ。
    • Docker の リソースとしての Volume
      • docker volume ls で見えるモノ。
    • (本当は、これ以外にもNFSなども指定できるようだ)
  • ボリュームをコンテナにマウントすることで、コンテナからアクセスできるようになる。
  • あるいは--volumes-from <コンテナ名>とすると、指定したコンテナのマウントと同じようにマウントできる。

今までの僕のdocker-composeは...?

まずは『こんな記事にわざわざ見に来ていただいて感謝』ということで、恥ずかしながらも前まで設定していた駆け出しdocker-composeを公開します。笑

docker-compose.yml
version: "3"
services: 
    db:                     # データベース(つまり消えて欲しくない貴重なデータ群)が含まれているプログラム達
        image: postgres
        environment:
            - POSTGRES_USER
            - POSTGRES_PASSWORD
        ports:
            - "5432:5432"
    back:                   # バックエンド(静的ファイル配信やmodelの定義、routing設定など)のプログラム達
        build: .
        volumes:            # 注目
            - .:/code       # 注目
        ports: 
            - "8000:8000"
        command: gunicorn config.wsgi -b 0.0.0.0:8000 # サーバー起動
        depends_on: 
            - db

上記の『ボリューム(Data Volume)とは』から言うと、

volumes: 
- .:/code

という記述は、ホスト側のディレクトリ(カレントディレクトリ化)コンテナ内(code下)マウントして、バックエンドのプログラムを永続化しているということになります(/・ω・)/

dbにはvolumesを記述していないので、もちろん消えて欲しくない貴重なデータが含まれたデータベースは永続化していません!?!?

もう序盤で既にオチを悟った方もいるかもしれませんが(笑)、詳しくおかしな点について解説していきますね!

余談(ここはスルーしてOKです)

恐らくこれを見た経験者は『いやそこボリュームとしてデータ永続化してどないすんねん!』と言うでしょう。笑
しかも事前にvolumeの名前も定義もしていないので、docker volume lsでボリューム一覧を出力した時にも、どれがどのボリュームなのかわからない。
やはり独学で実装していると、docker volume lsした時に知らないボリュームが増えていくことに『違和感』を覚えることはありましたが、『おかしい』ことを確信せずに実装していたのです。

なぜ勉強することになったか?

現在(2020/2/7)開発中である投票サービス《Shappar》で静的ファイルをDocker×Nginxで配信しようと思ったところ、以下の記事を見つけました。

DjangoをDocker Composeでupしよう! *2

↑上記事は要約すると『DjangoをGunicorn×Nginx×docker-composeを使ってサーバーを立ち上げよう!』というものです。そこでdocker-composeの使用例として以下のような記述を見つけました。

docker-compose.yml
version: '3.7'
services:
  django:
    restart: always
    build: ./django
    expose:
      - "3031"
    depends_on:
      - postgres
    command: bash -c "python manage.py migrate && gunicorn my_docker_project.wsgi -b 0.0.0.0:3031"
    volumes:
      - "staticdata:/opt/static/"        # 3. ナンダコレ
  nginx:
    restart: always
    image: nginx
    depends_on:
      - django
    ports:
      - "80:80"
    volumes:
      - "./nginx/:/etc/nginx/"            # 1. ナンダコレ
      - "staticdata:/opt/apps/static/"    # 1. ナンダコレ  3. ナンダコレ
  postgres:
    image: postgres
    ports:
      - "5432:5432"
    volumes:
      - "dbdata:/var/lib/postgresql/data"
    environment:
      POSTGRES_PASSWORD: hogemojahogemoja

volumes:  # 2.ナンダコレ
  dbdata:
  staticdata:

この記述を見て思った異変(自分の無知)の数々

  1. ./nginx/:って...下のdbdata:みたいにカンマスラッシュ付けずにnginx:←こうやって書き方統一しろよな!(※ヤバい間違いをしています。)
  2. なんか一番下にvolumesおる...
  3. なぜ2回もstaticdataを指定しているのだ...

先ほどの疑問点について勉強を開始

1. ディレクトリの記述方法について

とりまdockerの公式docs *3に飛びます。すると以下の記述がありました。

docker-compose.yml
volumes:
  # 1.パスを指定したら、Engine はボリュームを作成
  - /var/lib/mysql

  # 2.絶対パスを指定しての割り当て
  - /opt/data:/var/lib/mysql

  # 3.ホスト上のパスを指定する時、Compose ファイルからのパスを指定
  - ./cache:/tmp/cache

  # 4.ユーザの相対パスを使用
  - ~/configs:/etc/configs/:ro

  # 5.名前付きボリューム(Named volume)
  - datavolume:/var/lib/mysq

記述によって動作が全く違う件

引用してきた上記のdocker-composeから、僕が疑問に思った記述方法は③と⑤に当てはまります。

結論から言うと、./nginx/:/etc/nginx/nginx:/etc/nginx/とでは、プログラムの実行内容が全然違います。

./nginx/: docker-compose.ymlが存在しているディレクトリから、相対パスによってマウントする側の指定先としてホスト側のディレクトリの指定を行います。
                            
nginx: この書き方をすると、マウントする側の指定先としてnginxという名前付きボリュームを探します。

マウントする側の指定方法として、./nginx/:はカレントディレクトリから〜、対してnginx:名前付きボリュームありきで〜、というように方法が全く異なっていますね。

はて?名前付きボリュームですか。

2. 名前付きボリュームとは?(一番下にvolumesがいる訳)

ここで先ほどの2つ目の疑問点『なんか一番下にvolumesおる...』が解決できます。
なんとdocker-composeのvolumesにて名前を定義することによって、ボリュームに名前を付けています。

実際に先ほどのdocker-composeをもう一度見てみましょう。

docker-compose.yml
version: '3.7'
services:
  django:
    restart: always
    build: ./django
    expose:
      - "3031"
    depends_on:
      - postgres
    command: bash -c "python manage.py migrate && gunicorn my_docker_project.wsgi -b 0.0.0.0:3031"
    volumes:
      - "staticdata:/opt/static/"         # staticdataは一番下のvolumeで定義してるやつ!
  nginx:
    restart: always
    image: nginx
    depends_on:
      - django
    ports:
      - "80:80"
    volumes:
      - "./nginx/:/etc/nginx/"            # docker-composeからnginxというディレクトリを相対パスにより指定
      - "staticdata:/opt/apps/static/"    # staticdataは一番下のvolumesで定義してるやつ!
  postgres:
    image: postgres
    ports:
      - "5432:5432"
    volumes:
      - "dbdata:/var/lib/postgresql/data" # dbdataはvolumesで定義してるやつ!
    environment:
      POSTGRES_PASSWORD: hogemojahogemoja

volumes:       # 名前付きボリュームを定義している
  dbdata:      # ボリューム
  staticdata:  # ボリューム

一番下のvolumesで名前を付けていますね〜。これによりボリュームが作成されます!
もし既に名前を付けているボリュームvolumesにて指定すると、その名前の付いたボリュームからデータを参照してきます。

つまり『データを永続化している』訳ですね(/・ω・)/

そして以下のことが言えます!

  • 先頭に./が付いていないdbdatastaticdataは一番下のvolumes:で名前を付けてから、名前付きボリュームとしてコンテナの外部に保管し、データを永続化している。
  • 逆に先頭に./が付いているnginxは相対パスでホスト側ののマウントするディレクトリを指定しているだけ。つまりデータを永続化してない。

果たして名前が付いているかdocker volume lsコマンドにて確認します!
volumename.jpg

《サービス名_ボリューム名》となっていることが確認できました〜。(画像ではアンダーバーが途切れてます。笑)

3. 何故2回もstaticdataを指定しているのだ...

1と2からなんとなく推測してみましょう。

今までの文章から考察するとこうなるかな...

  • 1的観点から記述法を見ると、名前付きボリューム(staticdata)でバックエンドとNginxにマウントをとっているのがわかる。
  • 2的観点から名前付きボリュームということはデータの永続化(データを別場所にて保管)をしている。
  • staticdataとdbdataを比較すると、dbdataではpostgresのvolumesの1つしか設定していなかった。

《データを別の場所にて管理》 & 《バックエンドとNginxの2つマウント取ってる。》

これらの要素からボリュームでデータを別場所で管理できることを利用して、コンテナ間のリソース共有をしている!

もしやバックエンドコンテナとNginxコンテナで静的ファイルを共有して、静的ファイルに関してだけNginxで配信するという形が取れるのでは...?

当初の僕の目的であったNginxで静的ファイルを配信することに繋がってきました:relaxed:

まとめ

最後まで読んでいただきありがとうございました。
Docker系は使えるツールがあまりにも多いので、本当に把握しきれないです。特にボリュームの概念をつかむのはある程度触ってからでないと難しいのかも知れません(コンテナの概念やマウントという言葉の意味など...)。

ですがどこかのDocker初心者が僕のようにボリュームの概念について躓かないように、初心者である僕の目線でわかりやすく記事にさせていただきました!

間違っている点やシンプルな感想などでもございましたら、お気軽にコメントください!!

バックエンド(Django,RESTAPI)やAWS,Dockerなどの勉強していたり、機械学習にも少し手を出しているわたくしの姿を発信しているTwitterがあるので、もし良ければ...@heacet43

参考文献

  1. Docker、ボリューム(Volume)について真面目に調べた - Qiita [https://qiita.com/gounx2/items/23b0dc8b8b95cc629f32]
  2. DjangoをDocker Composeでupしよう! - Qiita [https://qiita.com/kyhei_0727/items/e0eb4cfa46d71258f1be]
  3. Compose ファイル・リファレンス — Docker-docs-ja 17.06.Beta ドキュメント [http://docs.docker.jp/compose/compose-file.html#volumes-volume-driver]
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む