8
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

自宅鯖のDocker内でWordPressを構築する

Last updated at Posted at 2021-03-27

はじめに

この記事は前回の記事の続きです。

前回の記事↓
自宅鯖をDocker化+リバースプロキシを導入+WordPress等を立ち上げる - Qiita

⚠注意⚠

この記事だけでWordPressのサイトを外部に公開しないでください。
本記事はHTTPS接続の設定まで行っていません。HTTP接続しかできませんので危険です。
必ず、SSL証明書を取得しHTTPS接続できる状態にした上で公開をお願いします。

なお、リバースプロキシにSSLを導入する予定で本記事を執筆していますので、WordPress単体でSSLを導入する手順についてはご紹介しませんのでご了承ください。
「docker Let's Encrypt apache Ubuntu」
等で検索すればヒットするかと思います。

記事一覧

対象とする読者

前回の記事に加えて以下の知識が必要になります。

  • 基本的なネットワークの知識

ネットワークについてあまり詳しくないという方にもできるだけわかりやすく説明する予定ですが、「IPアドレス」「ブリッジ」「ルーター」「フォワーディング」などの基本的な用語は抑えておいてください。

環境

  • 自宅サーバー
    • OS: Ubuntu 20.04.2 LTS
  • Docker
    • verison: 20.10.5, build 55c4c88
  • Docker Compose
    • version: 1.28.5, build c4eb3a1
    • ファイルフォーマット: 3.9

本題

img10.png
さて、今回はWordPressとMySQLの環境を立ち上げます。上図の赤枠の部分を構築します。
これだけだとわかりづらいかもしれません、ネットワークのアクセスの線も付け足します。
img20.png
もっと言えばこうなっています。仮想ネットワークを物理ネットワークのようにわかりやすく図示しています。
IPアドレスは適当です。
概念図2-Network.png

完成図が頭に入ったところで、実際に構築してみます。

事前準備

ポート開放する

※ローカル環境でやる場合は必要ありません

  • ルーターのポート開放
  • iptablesufwでポート開放

以上は済ませておいてください。
今回はポート80を使用しています。
既に他のサービスでポート80が使用されている場合エラーになるので、そこは適宜対応お願いします。

フォワーディングが無効になっていないか

iptables, nftables等確認してください。
有効にしないとコンテナまでアクセスできません。

作業ディレクトリの作成

まずは作業する場所を作成します。
この中にコンテナができるというわけではありませんが、このディレクトリがDocker Composeの単位として働きます。
下図のような構成にします。
img41.png
永続化データと書いてある「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つをいい感じに+ちょっと設定を追加させてあげると次のようになります。

docker-compose.yml
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

docker-compose.yml(抜粋)
version: "3.9"
...

バージョンはDocer Composeファイルのフォーマット形式のバージョンです。
必ず設定しなければなりません。
2021年3月現在、最新は3.9のようです。
他の記事を参考にする場合もバージョンは必ず確認するようにしてください。使えないコマンド等あるかもしれません。

services

docker-compose.yml(抜粋)
...
services:
  db:
...
  wordpress:
...

サービス名です。後述するコンテナ名とは似てますが、少し違います。
同じCompose内であれば、db, wordpressといった名前で名前解決できます。
つまり、wordpressのコンテナから ping dbとコマンドを打てばIPアドレスに変換され通ります。
サービス名は同じCompose内でかぶならければ、他のComposeのサービス名とかぶっても大丈夫です。
同じCompose内ではかぶってはいけません。

image

docker-compose.yml(抜粋)
...
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

img51.png

上のような感じですね。
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

docker-compose.yml(抜粋)
...
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つ作ります。
img60.png
これをそれぞれ立ち上げます。
すると下のような構成になります。
img61.png
この場合、コンテナ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 db1ping mysql1 と同じ挙動
ping db2ping mysql2 と同じ挙動
になります。

正直サービス名とコンテナ名の違いはよくわかっていません…。

また、コンテナ名は必ず一意となりますが、同じ仮想ネットワークに所属させなければ名前解決できません。

volumes

docker-compose.yml(抜粋)
...
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



イメージは、

img70.png
こんな感じです。
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

docker-compose.yml(抜粋)
...
services:
  db:
...
    restart: always
...
  wordpress:
...
    restart: always
...

簡単です。コンテナが何らかの理由で終了した場合自動で再起動をかけます。
手動で停止した場合は再起動されません。
Docker自体を再起動させたら再起動するみたいです。(試してない)

(参考)Compose file version 3 reference | Docker Documentation

environment

docker-compose.yml(抜粋)
...
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の場合下のようになります。

./conf.env
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

docker-compose.yml(抜粋)
...
services:
  db:
...
  wordpress:
    depends_on:
      - db
...

依存関係を設定します。簡単に言うと、起動する順番を指定できます。
サービス名を指定してください。コンテナ名は指定できません。

今回は
dbが立ち上がったあとにwordpressを立ち上げています。

DBサーバーが起動してないと、WordPress側がうまいこといかないのでこうしてます。

ports

docker-compose.yml(抜粋)
...
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.ymlがあるディレクトリで実行してください
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が同期されない、ディレクトリが作られない

スペルミスや/の位置を確認してください。

最後に

長文になりましたが、最後まで読んでいただきありがとうございました。

コメントいただければ分かる範囲でお答えできるのでどうぞ。

8
6
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?