はじめに
この記事は前回の記事の続きです。
前回の記事↓
自宅鯖をDocker化+リバースプロキシを導入+WordPress等を立ち上げる - Qiita
⚠注意⚠
この記事だけでWordPressのサイトを外部に公開しないでください。
本記事はHTTPS接続の設定まで行っていません。HTTP接続しかできませんので危険です。
必ず、SSL証明書を取得しHTTPS接続できる状態にした上で公開をお願いします。
なお、リバースプロキシにSSLを導入する予定で本記事を執筆していますので、WordPress単体でSSLを導入する手順についてはご紹介しませんのでご了承ください。
「docker Let's Encrypt apache Ubuntu」
等で検索すればヒットするかと思います。
記事一覧
- (1) Dockerとは + Dockerインストール
- ▶(2) Dockerで"WordPress+Apache+PHP" + "MySQL"を立ち上げる 🐳<今ココ
- (3) Dockerでリバースプロキシ(nginx)構築+SSL対応
対象とする読者
前回の記事に加えて以下の知識が必要になります。
- 基本的なネットワークの知識
ネットワークについてあまり詳しくないという方にもできるだけわかりやすく説明する予定ですが、「IPアドレス」「ブリッジ」「ルーター」「フォワーディング」などの基本的な用語は抑えておいてください。
環境
- 自宅サーバー
- OS: Ubuntu 20.04.2 LTS
- Docker
- verison: 20.10.5, build 55c4c88
- Docker Compose
- version: 1.28.5, build c4eb3a1
- ファイルフォーマット: 3.9
本題
さて、今回はWordPressとMySQLの環境を立ち上げます。上図の赤枠の部分を構築します。
これだけだとわかりづらいかもしれません、ネットワークのアクセスの線も付け足します。
もっと言えばこうなっています。仮想ネットワークを物理ネットワークのようにわかりやすく図示しています。
IPアドレスは適当です。
完成図が頭に入ったところで、実際に構築してみます。
事前準備
ポート開放する
※ローカル環境でやる場合は必要ありません
- ルーターのポート開放
-
iptables
やufw
でポート開放
以上は済ませておいてください。
今回はポート80を使用しています。
既に他のサービスでポート80が使用されている場合エラーになるので、そこは適宜対応お願いします。
フォワーディングが無効になっていないか
iptables, nftables等確認してください。
有効にしないとコンテナまでアクセスできません。
作業ディレクトリの作成
まずは作業する場所を作成します。
この中にコンテナができるというわけではありませんが、このディレクトリがDocker Composeの単位として働きます。
下図のような構成にします。
永続化データと書いてある「db_data」「wp_data」は自動で生成されますので作成不要です。
※追記: 2021/3/30
⚠WordPressのデータを/home/dockeruser/wordpress/wp_data
に永続化させる紹介をしていますが、MySQLの永続化データdb_data
と同様に名前付きボリュームで行ったほうが良いかもしれません。
詳しくはこちらに記事を書きましたので、検討してみてください。
$ cd /home/dockeruser
$ mkdir wordpress
$ cd wordpress
docker-compose.ymlの作成
次に「docker-compose.yml」ファイルを作ります。
言うならばこれから作るコンテナの設計書のようなものです。
下記サイトにそれぞれサンプルの「docker-compose.yml」があるのでこれを基に作成します。
現時点での最新版は公式のQuickstartみたいです。(バージョン3.9)
(公式) Quickstart: Compose and WordPress | Docker Documentation
(Docker Hub) wordpress | Docker Hub
(有志による日本語版) クィックスタート: Compose と WordPress
上記3つをいい感じに+ちょっと設定を追加させてあげると次のようになります。
version: "3.9"
services:
db:
image: mysql:5.7
container_name: mysql
volumes:
- db_data:/var/lib/mysql
restart: always
environment:
MYSQL_ROOT_PASSWORD: <任意のパスワード>
MYSQL_DATABASE: <任意のデータベース名>
MYSQL_USER: <任意のユーザー名>
MYSQL_PASSWORD: <任意のパスワード>
wordpress:
depends_on:
- db
image: wordpress:latest
container_name: wordpress
volumes:
- ./wp_data:/var/www/html
ports:
- "80:80"
restart: always
environment:
WORDPRESS_DB_HOST: db:3306
WORDPRESS_DB_NAME: <上のMYSQL_DATABASEで設定したデータベース名>
WORDPRESS_DB_USER: <上のMYSQL_USEで設定したユーザー名>
WORDPRESS_DB_PASSWORD: <上のMYSQL_PASSWORDで設定したパスワード>
volumes:
db_data:
ちなみに、公式の
docker-compose.yml
はvolumes: db_data: {}
最後このように書かれていますが、この
{}
は書いても書かなくても同じです。
(参考)Docker compose: meaning of {} in volume definition - Stack Overflow
順に説明しましょう。
コード中の「...」は省略を表しています。実際には書いてない文字なので注意してください。
全部説明すると長いので目次のリンクを置いておきます。
全部飛ばしたい方:Composeの実行
version
version: "3.9"
...
バージョンはDocer Composeファイルのフォーマット形式のバージョンです。
必ず設定しなければなりません。
2021年3月現在、最新は3.9のようです。
他の記事を参考にする場合もバージョンは必ず確認するようにしてください。使えないコマンド等あるかもしれません。
services
...
services:
db:
...
wordpress:
...
サービス名です。後述するコンテナ名とは似てますが、少し違います。
同じCompose内であれば、db
, wordpress
といった名前で名前解決できます。
つまり、wordpress
のコンテナから ping db
とコマンドを打てばIPアドレスに変換され通ります。
サービス名は同じCompose内でかぶならければ、他のComposeのサービス名とかぶっても大丈夫です。
同じCompose内ではかぶってはいけません。
image
...
services:
db:
image: mysql:5.7
...
wordpress:
...
image: wordpress:latest
...
Docker Hubから取得してくるイメージ名をこちらに書きます。
フォーマットは<イメージ名>:<タグ>
です。
今回の場合ですと、MySQLは公式イメージの5.7を、WordPressは公式イメージのlatest(最新版)を使用しています。
事前にDocker Hubからpull
しなくても、Compose初回起動時にイメージがなければ勝手にpullしてくれます。
私はやったことありませんが、自分でDockerfileを作ってイメージにする場合はbuild
を使用します。
Docker Hubでのイメージの探し方
例えばWordPressの場合ですと、
https://hub.docker.com/_/wordpress
上のような感じですね。
latest
のDockerfileにはFROM php:7.4-apache
とあるので、PHPのDocker Hubページのタグ7.4-apache
を見れば何をしてるかがわかります。
wordpress:latest
としたときは
「WordPress5.7」「Apache2」「PHP5.7」をインストールした「Debian」をプルしたことと同じというわけです。
タグのリンクへ飛べばGitHubのDockerfileのページに行きますので、カスタマイズしたい方や何をしてるか知りたい方はそちらを参照してみてください。
**あれ、WordPressってDebianで動いてるの?**
Dockerfileをたどると、Debianなどを基にイメージを作っていることがわかります。
コンテナ技術はゲストOSがないはずなのになぜと思うかもしれません。私は思いました。
こちらの質問が参考になりました。
Docker - Dockerのコンテナ内で使われるOSについて|teratail
"Debianっぽい"ディレクトリ構造にして、実際はホストOS(今回で言うUbuntu)のカーネルで動かしてるみたいです。
container_name
...
services:
db:
...
container_name: mysql
...
wordpress:
...
container_name: wordpress
...
コンテナ名です。docker ps
で出るコンテナ一覧のNAMES
にあたります。
docker run --name
でも指定できます。
コンテナ名は他のコンテナとかぶってはいけません。一意でなければなりません。
逆にサービス名は他のComposeとかぶっても大丈夫です。
Composeでコンテナ名を指定せずに作成するとプロジェクト名_サービス名_連番
となります。
プロジェクト名はdocker-compose.yml
が配置されているディレクトリになります。
docker run
でコンテナ名を指定せずに実行した場合はDockerが勝手に名前をつけます。
(参考)Dockerで --name 指定しないでコンテナ実行するとヘンテコなデフォルト名がつく - Qiita
Composeで作った場合のデフォルト名は環境変数で定義されているので、変更することも可能です。
(参考)docker-composeで作成されるものの名前を明示的に指定する方法 - Qiita
今回の環境でComposeでコンテナ名を指定せずに起動したならば
wordpress_db_1
wordpress_wordpress_1
というコンテナが生成されます。今回はわかりづらいのでコンテナ名を指定しました。
そしてサービス名と同じ用に、同じ仮想ネットワークに所属しているのであれば、コンテナ名で名前解決できます。
しかし違いはあります。
サービス名はComposeの中での名前であり、コンテナ名はDocker全体の名前です。
具体例を出します。
同じサブネット内に所属させた「MySQL+WordPress」を2つ作ります。
これをそれぞれ立ち上げます。
すると下のような構成になります。
この場合、コンテナwordpress2
からping
を打つと、
-
ping db
・・・192.168.10.5のMySQLにつながる -
ping mysql1
・・・192.168.10.3のMySQLにつながる -
ping mysql2
・・・192.168.10.5のMySQLにつながる
となってサービス名が優先されます。
もしも
サービス名をdb1
db2
と一意となっているかつ同じサブネット内なら
ping db1
は ping mysql1
と同じ挙動
ping db2
は ping mysql2
と同じ挙動
になります。
正直サービス名とコンテナ名の違いはよくわかっていません…。
また、コンテナ名は必ず一意となりますが、同じ仮想ネットワークに所属させなければ名前解決できません。
volumes
...
services:
db:
...
volumes:
- db_data:/var/lib/mysql
wordpress:
...
volumes:
- ./wp_data:/var/www/html
...
volumes:
db_data:
volumesです。マウントのようなものです。
ホストのディレクトリとコンテナのディレクトリを同期させることが出来ます。
これを指定しないと、コンテナを再起動するたびにデータが初期化されます。
例えばWordPress+MySQLの場合
「WordPressの記事」
「WordPressのテーマ」
「WordPressの設定」
などが消えます。
volumeには3種類あります。同じvolumes:
という設定項目ですが、3種類の運用方法があります。
① 名前付きボリューム(Named volumes)
フォーマットは
<Dockerのvolume名>:<コンテナ内のディレクトリ>
もしくは
<Dockerのvolume名>:<コンテナ内のディレクトリ>:<オプション名>
です。
Docker Composeでコンテナを立ち上げる前にボリュームの宣言は不要です。
volumes:
db_data:
のように宣言を行っていれば、Dockerはそのボリューム名がなけば作成します。
初期設定でホストの/var/lib/docker/volumes/<Dockerのvolume名>/_data
に配置されます。
デフォルトのボリューム名は<プロジェクト名>_<ボリューム名>
となります。
以下の設定を加えることによって、任意の名前にすることも可能です。
volumes:
db_data:
name: <ボリューム名>
また、既にボリュームを作成してある場合であれば
volumes:
<ボリューム名>:
external: true
とすることで作成済みボリュームを検索します。
<オプション名>
はro
などを設定できるそうです。
ro
= read only 読み取り専用
(公式ドキュメント)Use volumes | Docker Documentation
② 匿名ボリューム(Anonymous volumes)
フォーマットは
<コンテナ内のディレクトリ>
もしくは
<コンテナ内のディレクトリ>:<オプション名>
です。
初期設定で/var/lib/docker/volumes/<Dockerのvolume名>/_data
に配置されます。
名前付きボリュームと違い、Docker側が自動生成した文字列がボリューム名になります。
例:0f7119b5ef2525ee5296d4ffe897f48d154b955faeb1ea8311993455664b167c
他は名前付きボリュームと同じです。
(公式ドキュメント)Use volumes | Docker Documentation
③ バインドマウント(Bind mount)
フォーマットは
<ホストのディレクトリ>:<コンテナ内のディレクトリ>
もしくは
<ホストのディレクトリ>:<コンテナ内のディレクトリ>:<オプション名>
です。
<オプション名>
はro
, z
, Z
などがあります。
分かり次第追記しますが、z・ZはSELinux特有の設定のようです。
名前付きボリューム、匿名ボリュームと違いDocker側のvolumeコマンドで管理されません。
(公式ドキュメント)Use bind mounts | Docker Documentation
イメージは、
こんな感じです。
docker volume ls
でDockerのボリューム一覧を出したときに、./wordpress
などのバインドマウントは表示されません。
docker system prune --volumes
というDockerのネットワーク、コンテナ、ボリューム等々を消すコマンドがありますが、そちらを打っても ./wordpress
は消えません。
volumeもよくわかってない部分が多いので、のちのち記事書けたらと思っています…。
わかっていないこと
- 各種使い分け
- 同期タイミング
- それぞれにファイルが既に有った場合の動作
- Dockerfileの
VOLUME
文との違いとvolumes
と併用したときの挙動 - Dockerfileの
CMD
でシェルスクリプトを実行しファイル生成したあとのボリューム挙動
書いたら記事のリンク貼っておきます。
※追記 2021/3/30 上記疑問を解決した記事を書きました。こちら
ちなみに…
WordPressのDockerfileにはVOLUME /var/www/html
という記述があり、実は匿名ボリュームを勝手に作成してくれています。
なのでdocker-compose.yml内でのボリューム記述はなくてもデータの永続化はされています。
volumes
オプションを指定するとDockerfileのVOLUME
の記述は無視されますので、匿名ボリュームの生成は行われません。
restart
...
services:
db:
...
restart: always
...
wordpress:
...
restart: always
...
簡単です。コンテナが何らかの理由で終了した場合自動で再起動をかけます。
手動で停止した場合は再起動されません。
Docker自体を再起動させたら再起動するみたいです。(試してない)
(参考)Compose file version 3 reference | Docker Documentation
environment
...
services:
...
environment:
MYSQL_ROOT_PASSWORD: <任意のパスワード>
MYSQL_DATABASE: <任意のデータベース名>
MYSQL_USER: <任意のユーザー名>
MYSQL_PASSWORD: <任意のパスワード>
wordpress:
...
environment:
WORDPRESS_DB_HOST: db:3306
WORDPRESS_DB_NAME: <上のMYSQL_DATABASEで設定したデータベース名>
WORDPRESS_DB_USER: <上のMYSQL_USEで設定したユーザー名>
WORDPRESS_DB_PASSWORD: <上のMYSQL_PASSWORDで設定したパスワード>
...
環境変数の設定です。
docker exec -it <コンテナ名> bash
で入ってenv
コマンドを実行してみてください。
コンテナ内でdocker-compose.yml
で設定した環境変数が設定されているのが確認できるはずです。
各Docker Hubページに使える環境変数が書かれているので参考にしてカスタマイズしてください。
コンテナ内でgrep
等すればどこでどう使われているかもわかると思います。
また、パスワード等を直書きするのはおすすめできません。(おすすめできないと言ってベタ書きを紹介してるのもあれですが・・・)
他のファイルに書いた変数を参照することも可能です。
その場合environment:
ではなくenv_file
を使用してください。
env_file:
- ./conf.env
といった風に記載してください。
ファイルの中身は例えばWordPressの場合下のようになります。
WORDPRESS_DB_HOST: db:3306
WORDPRESS_DB_NAME: <上のMYSQL_DATABASEで設定したデータベース名>
WORDPRESS_DB_USER: <上のMYSQL_USEで設定したユーザー名>
WORDPRESS_DB_PASSWORD: <上のMYSQL_PASSWORDで設定したパスワード>
(参考)Compose file version 3 reference | Docker Documentation
また、WordPressの公式イメージに限っては環境変数に_FILE
を付けてファイル名を指定できるみたいです。
Docker Secrets
As an alternative to passing sensitive information via environment variables,_FILE
may be appended to the previously listed environment variables, causing the initialization script to load the values for those variables from files present in the container. In particular, this can be used to load passwords from Docker secrets stored in/run/secrets/<secret_name>
files. For example:$ docker run --name some-wordpress -e WORDPRESS_DB_PASSWORD_FILE=/run/secrets/mysql-root ... -d wordpress:tag
Currently, this is supported for WORDPRESS_DB_HOST, WORDPRESS_DB_USER, WORDPRESS_DB_PASSWORD, WORDPRESS_DB_NAME, WORDPRESS_AUTH_KEY, WORDPRESS_SECURE_AUTH_KEY, WORDPRESS_LOGGED_IN_KEY, WORDPRESS_NONCE_KEY, WORDPRESS_AUTH_SALT, WORDPRESS_SECURE_AUTH_SALT, WORDPRESS_LOGGED_IN_SALT, WORDPRESS_NONCE_SALT, WORDPRESS_TABLE_PREFIX, and WORDPRESS_DEBUG.
ほぼ機械翻訳↓
Dockerシークレット
environment
変数を介して機密情報を渡す代わりに、環境変数に_FILE
を追加して、初期化スクリプトがコンテナー内に存在するファイルからそれらの変数の値をロードするようにすることができます。
特に、これは/run/secrets/<secret_name>
ファイルに保存されているDockerシークレットからパスワードをロードするために使用できます。
例えば、次のように実行します。$ docker run --name some-wordpress -e WORDPRESS_DB_PASSWORD_FILE=/run/secrets/mysql-root ... -d wordpress:tag
現在これは以下の環境変数にサポートされています。
WORDPRESS_DB_HOST
WORDPRESS_DB_USER
WORDPRESS_DB_PASSWORD
WORDPRESS_DB_NAME
WORDPRESS_AUTH_KEY
WORDPRESS_SECURE_AUTH_KEY
WORDPRESS_LOGGED_IN_KEY
WORDPRESS_NONCE_KEY
WORDPRESS_AUTH_SALT
WORDPRESS_SECURE_AUTH_SALT
WORDPRESS_LOGGED_IN_SALT
WORDPRESS_NONCE_SALT
WORDPRESS_TABLE_PREFIX
WORDPRESS_DEBUG
depends_on
...
services:
db:
...
wordpress:
depends_on:
- db
...
依存関係を設定します。簡単に言うと、起動する順番を指定できます。
サービス名を指定してください。コンテナ名は指定できません。
今回は
db
が立ち上がったあとにwordpress
を立ち上げています。
DBサーバーが起動してないと、WordPress側がうまいこといかないのでこうしてます。
ports
...
services:
...
wordpress:
...
ports:
- "80:80"
...
ホストが受け取った特定のポートをコンテナ内のポートに転送します。
<ホストが受け取ったポート>:<コンテナのポート>
"80:80"
だと、
ホストにポート80で入ってきたものは、コンテナのポート80に転送されます。
MySQL側はports指定していませんが、ホストと通信するわけではないので大丈夫です。
ただし、MySQL⇔WordPress間のコンテナ間通信があるのでMySQL側のコンテナでポート開放的な操作が必要です。
そのためにはexpose
というオプションをdocker-compose.ymlに記述する必要がありますが、
MySQLの公式イメージでEXPOSE 3306 33060
と3306と33060を指定して他のコンテナから使えるようにさせてるので大丈夫というわけです。
他のオプション
他にもオプションはたくさんあります。公式のリファレンスに他にもたくさんあるのでカスタマイズしてみてください。
Compose file version 3 reference | Docker Documentation
ちなみに、networks
は次の「nginxでリバースプロキシを構築する」の記事で設定します。
Composeの実行
最終確認
ついに来ました。起動させます。
最終確認です。
docker-compose config
と打てば実際に読み込まれる設定が見えます。
構文チェックまではしてくれないので、自分の目で見て確かめてください。
他にも、
- ポート開放はしていますか?
- フォワーディング無効になっていませんか?
- 他のサービスとポートがかぶっていませんか?
- volumeは適切なディレクトリを指定していますか?
- 環境変数は間違えていませんか?
-
docker-compose.yml
はインデントも大事です。間違えていませんか?
大丈夫であれば起動しましょう。
docker-compose up
する
docker-compose up
docker-compose up -d
と打てばデタッチ(バックグラウンドで起動)できますが、起動確認のため一応ログも見ましょう。
たぶん、イメージをpullして、いろいろ設定されて起動されるはずです・・・。
...
db | [Note] mysqld: ready for connections.
...
wordpress | Complete! WordPress has been successfully copied to /var/www/html
...
こんな感じで出力されてば起動できているはずです。(最初はDBを作るので少し時間かかります。)
ブラウザでアクセスしてみる
ブラウザでアクセスしてみてください。
http://<domainname>
ポートを変更している場合は:ポート番号
も忘れずに
WordPressのインストール画面が出たら起動OKです。
Composeを再起動してみる
WordPressで設定を進めたあと、いちどコンテナを落としてみましょう。
ログが出てる状態でCtrl + C
を押して落とすか、
デタッチモードだったらdocker-compose down
で落としたあと
docker-compose up
でもう1回起動してみてください。
インストール画面からスタートではなく設定が残っていますか?
残っていればデータの永続化もOKです。
トラブルシューティング
構築していてわけわかんなくなったら、
- ホストOSの再起動
sudo service docker restart
それでもだめなら
docker system prune
docker system prune --volumes
とりあえずここらへん試してみてください。
コンテナは落とした後も「死体」みたいなのが残ってるのでそれを掃除するのがdocker system prune
です。
--volumes
をつけると、ボリュームも消えます。
他にも自分が陥ったエラーを書きます。
docker-compose upでiptabelsのエラーが出る
sudo service docker restart
を試してみてください。
docker run実行時のiptablesエラー - Qiita
IPアドレス直打ちならWordPressが表示されるのに、ドメインだと表示されない
過去にhttpsサイトを立ち上げてアクセスしていませんでしたか?
Chromeなどのブラウザでは一度HTTPS接続したサイトはHTTPで接続しようとしても勝手にHTTPS接続を試みます。
プライベートウィンドウなどで接続してみてください。たいていのブラウザであればCtrl + Shift + N
で立ち上がります。
volumeが消せない
docker system prune
したあとに消してください。
もしくはdocker system prune --volumes
で全て消えます。(ネットワーク設定も消えます)
なんかポートがなんちゃらかんちゃらで起動できない
他のサービスとポート番号がかぶっていませんか?
他のポートを検討するか、既存のサービスを落としてください。
volumeが同期されない、ディレクトリが作られない
スペルミスや/
の位置を確認してください。
最後に
長文になりましたが、最後まで読んでいただきありがとうございました。
コメントいただければ分かる範囲でお答えできるのでどうぞ。