LoginSignup
6
7

More than 3 years have passed since last update.

【構築】CentOS7+VPN+docker:Nuxt.js+wordpress環境構築(コンテナセットアップ編)

Last updated at Posted at 2019-06-24

これはなに?

こんにちわ!ナギちゃんだよー☆
きょーは前回セットアップしたVPSをつかってdocker-composeでチンするだけのお手軽タピオカ時短コンテナをたてるよー!

はい。

長いので

に分割されています。


これはなんですか?

Nuxt+Wordpress+開発用コンテナ群をつくります。開発用コンテナはVPN接続時しか見れないすぐれもの!

なにがめんどくさいですか?

docker勉強用にはじめたものの地雷を多数ふんでしまい満身創痍になりました。勉強用ならもっと簡単なところからはじめたほうがよかった・・・

Let's encrypt

みんなやってるから簡単だろー?とか思ったら地味にめんどくさかったです。
たしかにぐぐるといろいろな手段があるようで

◆nginxコンテナ+https-portalコンテナ
・https-portalというLet's Encryptの取得+更新をすべて自動でやってくれるコンテナがある
・https-portalのnginx > webapp用のnginx > webappというめんどい経路になる
・(いじった範囲では)https-portalのnginx用confをこちらで操作するとうまくうごきませんでした
・(いじった範囲では)用意されている変数でリダイレクト先指定するとURLにいらないものがつくのでリネームがめんどそう

◆nginx-proxyコンテナ+docker-letsencrypt-nginx-proxy-companionコンテナ
・nginx-proxyというコンテナ群を監視し自動でproxyしてくれるコンテナがある
・nginx~companionというLet's Encryptの取得+更新をすべて自動でやってくれるコンテナがある
・開発用コンテナもまざるので自動でproxyとかやってほしくない(設定をしらべるのもめんどう)

◆nginxコンテナ+certbotコンテナ
・certbotがうごくだけの公式コンテナがある(自動更新などはない)
・certbot動作時は外部Port80で通信できる必要がある + 同じ外部ポート指定で動作するコンテナは作れない
・(初回)ssl証明書がない > nginxが起動しない > nginxコンテナ終了
・(初回)nginxが起動しない> certbotが認証できない(上とあわせてデッドロック)
・ただしcertbotはhttp://xxxx/.well-known/acme-challenge/にのみアクセスできれば認証できる

◆1つのコンテナにnginx+certbotインストール
・上とほぼ同様で、さらにnginxコンテナに余分なものがついてしまう

というかんじになります。
多くの人が悩んでいるようで、自作イメージを配布されている方もいるのですが、何が入っているかわからないイメージは使用しないは鉄則そうなのと、初回さえどうにかなれば綺麗にすみそうということで、今回は3番目でいくことにします。

ネットワーク設定

portsを指定したコンテナは、グローバルで公開され、firewalldによるブロックは効きません
たとえば「firewalldで80,443のみ許容」していても、「mariadbコンテナのportsを3306」のように指定すると、なにごともなくyourdomain:3306で外部からアクセスできてしまいます。これはdockerがiptablesを直接操作するためのようです。
(とはいえiptablesをみると、firewalldで設定したポートスキャンのあたりは残っているようなので、firewalldで設定する意味がまったくないわけではないようです)

docker-composeであげたコンテナ群はymlに書いておけば自動でbridgeされるので、とりあえずコンテナ間でさえ通信できれば良いコンテナにはportsを一切指定しないようにします。ports:80のように内部だけ設定していても、30000以上の任意のポートに割り振られるので外部に穴があいていることに変わりはないはずです。
また、特定のIPアドレスのみ許容したいコンテナは、左辺にIPアドレスまで指定できます(ex:192.168.1.252:3306:3306
コンテナ間の接続もなにやってるかわからなくてかなり怖いので、実運用するならもっと勉強しなければなりませんね・・・。

VMと同等なCentOSコンテナ

開発用コンテナとして、Nuxtが動作し、sshで直接ログインでき、sudoもsystemctlも使えるなど、ほぼVMのように使えるものを目指しました。
コンテナのアンチパターンであがってそうな感じですが、VSCode Remote Developmentが正式にリリースされたので、今後そこそこ需要あるのでは?とも思います。
とはいえsystemctlやssh用rsaファイルの権限あたりで慣れていないのでかなり疲れました・・・。けっきょく/sbin/initで起動しつつbashコマンドもやる方法が見つけられなかったので nuxtのセットアップはshにまとめておいてあとでdocker execする・・・という風にしています。


じゅんび?

ディレクトリ作成

作業するためのディレクトリと、コンテナ内部のデータを保存するディレクトリを作成します

コンテナ作成作業ディレクトリ

コンテナをつくるためのもろもろ材料を置く作業用ディレクトリを作成します。
前回作成した環境であれば、ユーザにはsudo+docker権限があるので、ログインしたユーザでそのまま作業ができます。好きなところに以下のようにディレクトリを作成し、これから記述していく各Dockerfileやconfを設置してください。

/container
|   #dockerまとめて設定ファイル
├── docker-compose.yml
|
|   #プロキシ用nginxコンテナ用ディレクトリ
├── /nginx
|   └── nginx.conf
|
|   #certbot動かすコンテナ用ディレクトリ
├── /certbot
|
|   #Nuxtで作成されたアプリの公開用コンテナ用ディレクトリ
├── /webapp
|   └── Dockerfile
|
|
|   #wordpressコンテナ用ディレクトリ
├── /wordpress
|
|   #wordpress用mariadbコンテナ用ディレクトリ
├── /wordpress_db
|   └── my.conf
|
|   #Nuxtで開発するためのCentOSコンテナ用ディレクトリ
└── /webapp_dev
    ├── Dockerfile
    ├── sshd.conf
    ├── id_rsa.pub
    └── startup_nuxt.sh

ssh
[user01@hostname ~]# mkdir container
[user01@hostname ~]# mkdir container/nginx
[user01@hostname ~]# mkdir container/certbot
[user01@hostname ~]# mkdir container/webapp
[user01@hostname ~]# mkdir container/webapp_dev
[user01@hostname ~]# mkdir container/wordpress
[user01@hostname ~]# mkdir container/wordpress_db

※なんとなくぜんぶ作っていますが、いらない(なにもファイルいれない)ディレクトリもあるのでお好みで

コンテナ内部からホスト側ディレクトリを参照するもの

コンテナてきには、外部データは最小限にしたいのですが、今回は

  • SSL証明書ファイル一式: 先述の理由もあり、コンテナ起動時に存在していないと初回起動できないため
  • Wordpressのファイル一式: wordpressの設定は永続化してほしいため 
  • データベースのイメージ: wordpress記事を保存するため

としました。ほかは

  • 公開用/開発用ファイルはコンテナ起動時にgithubからcloneし、本体には残さないようにします
  • それぞれのディレクトリは、慣習的に楽かなぁと思い、ホストにインストールしたときと同じような位置にしています
  • が、普段はインストール時につくられるものが存在しないので、ホスト側にディレクトリを作成する必要があります
ssh
[user01@hostname ~]# sudo mkdir /etc/letsencrypt
[user01@hostname ~]# sudo mkdir /var/www
[user01@hostname ~]# sudo mkdir /var/lib/mysql
[user01@hostname ~]# sudo mkdir /var/www
[user01@hostname ~]# sudo mkdir /var/www/html
[user01@hostname ~]# sudo mkdir /var/www/html/wordpress

初回certbot

先述したように、SSL用ファイルがないとnginxを起動できません。この初回起動のために、certbotコンテナを準備して、SSL認証用ファイルを生成します。成功すれば、IMPORTANT NOTES:Congratulations!の表示とともに、etc/letsencryptディレクトリにいろいろなファイルができているはずです。
なお、このcertbotイメージは今後も使っていくので削除する必要はありません。

ssh
[user01@hostname ~]# docker image pull certbot/certbot
[user01@hostname ~]# docker run --rm -p 443:443 -p 80:80 --name certbot-certonly -v "/etc/letsencrypt:/etc/letsencrypt"  certbot/certbot certonly -n -m "${メールアドレス}" -d ${ドメイン} --standalone --agree-tos

githubリポジトリ作成

webappコンテナがファイルをDLするためにつくっておきます。この段階では空でもかまいません。
プライベートリポジトリの場合は、cloneするのに認証が必要となるので、githubのSettings > Developer settings > Personal access tokensからアクセストークンを作成してください。https://${ユーザ名}:1234567890awsedrftgyhujikolp@github.com/${ユーザ名}/${リポジトリ名}みたいなやつが生成できれば、このURLからダウンロードすることができます。


docker-compose

ながくなりましたが、やっとメインとなるdocker-composeをかいてみます。YAMLとかさわったことがなくて病むる。

docker-compose.yml
version: '3'
services:
  nginx:
    image: nginx:1.17.0-alpine
    container_name: nginx
    restart: always
    ports:
      - "${グローバルIP}:80:80"
      - "${グローバルIP}:443:443"
    depends_on:
       - wordpress
    volumes:
    - /etc/letsencrypt:/etc/letsencrypt/
    - ./nginx/nginx.conf:/etc/nginx/nginx.conf


  certbot:
    image: certbot/certbot
    container_name: certbot
    volumes:
      - /etc/letsencrypt:/etc/letsencrypt
      - certs:/var/log/letsencrypt
      - certs:/var/lib/letsencrypt


  webapp:
    container_name: webapp
    restart: always
    image: webapp
    build:
      context: ./webapp
      dockerfile: Dockerfile
    tty: true
    depends_on:
       - wordpress
    command: >
      sh -c 'git clone ${githubのトークン+URL} &&
      cd ${プロジェクト名(cloneしたときに作成されるディレクトリ名)} &&
      yarn install &&
      npm run build && 
      npm start'


  wordpress:
    image: wordpress:5.2.1-php7.2-apache
    container_name: wordpress
    restart: always
    ports:
      - "${VPNローカルIP}:3180:80"
    depends_on:
      - wordpress_db
    environment:
      WORDPRESS_DB_HOST: wordpress_db:3306
      WORDPRESS_DB_USER: wordpress
      WORDPRESS_DB_PASSWORD: ${データベースのパスワード}
      WORDPRESS_DB_NAME: wordpress
    volumes:
      - /var/www/html/wordpress:/var/www/html/


  wordpress_db:
    image: mariadb:10.4.5-bionic
    container_name: wordpress_db
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: root
      MYSQL_DATABASE: wordpress
      MYSQL_USER: wordpress
      MYSQL_PASSWORD: ${データベースのパスワード}
    volumes:
      - /var/lib/mysql:/var/lib/mysql
      - ./mysql/my.conf:/etc/mysql/my.conf


  webapp_dev:
    container_name: webapp_dev
    image: webapp_dev
    build:
      context: ./webapp_dev
      dockerfile: Dockerfile
      args:
        ROOT_PSWD: "${開発用コンテナのROOTパスワード}"
        USER_NAME: "${開発用コンテナのユーザ名}"
        USER_PSWD: "${開発用コンテナのパスワード}"
    privileged: true
    restart: always
    ports:
      - "${VPNローカルIP}:${開発用コンテナSSHポート}:${開発用コンテナSSHポート}"
      - "${VPNローカルIP}:${開発用コンテナWEBポート}:3000"
    depends_on:
       - wordpress
    command: ["/sbin/init"]

volumes:
  certs:

nginxコンテナ

インターネット公開用のneginxコンテナを作成します。

  • ドメイン:Port80 → Port443にリダイレクト(デフォルト)
  • ドメイン:Port443 → 公開用webappコンテナにリダイレクト

シンプルな構成なのでnginxイメージそのまま使えます。つまりDockerfileは不要です。

nginx.conf

httpでhttpsにリダイレクト、httpsでwebappコンテナのExpressにリダイレクトします。
(参考にさせて頂きました: NginxでのSSL設定の細かい意味

/container/nginx/nginx.conf
user nginx nginx;
worker_processes  3;
worker_cpu_affinity 001 010 100;

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

events {
  multi_accept off;
  worker_connections  1024;
  use epoll;
}
http {
  include           mime.types;
  default_type      application/octet-stream;
  error_log /var/log/nginx/error.log;
  access_log  /var/log/nginx/access.log;

  server_tokens     off;
  server_name_in_redirect off;
  port_in_redirect  off;

  sendfile          on;
  tcp_nopush      on;

  keepalive_timeout 5;
  gzip_static       on;
  gzip              on;
  gzip_http_version 1.0;
  gzip_vary         on;
  gzip_comp_level   2;
  gzip_types        text/plain text/xml text/css text/javascript
                    application/xhtml+xml application/xml
                    application/rss+xml application/atom_xml
                    application/javascript application/x-javascript
                    application/x-httpd-php;
  gzip_disable      "MSIE [1-6]\.";

  proxy_cache_path  /var/cache/nginx levels=1:2 keys_zone=one:32m max_size=512m inactive=1d;
  proxy_temp_path   /var/tmp/nginx;
  proxy_cache_key   "$scheme://$host$request_uri";
  proxy_set_header  Host               $host;
  proxy_set_header  X-Real-IP          $remote_addr;
  proxy_set_header  X-Forwarded-Host   $host;
  proxy_set_header  X-Forwarded-Server $host;
  proxy_set_header  X-Forwarded-For    $proxy_add_x_forwarded_for;
  proxy_set_header X-Forwarded-Proto $scheme;
  proxy_set_header  Accept-Encoding    "";
  proxy_connect_timeout 5;
  proxy_send_timeout 10;
  proxy_read_timeout 120;
  proxy_hide_header X-Pingback;
  proxy_hide_header X-Powered-By;
  proxy_hide_header Etag;
  proxy_hide_header Vary;
  proxy_cache_use_stale timeout invalid_header http_500 http_502 http_503 http_504;
  proxy_cache_lock on;
  proxy_cache_lock_timeout 5s;

  server {
    listen 80 default_server;
    server_name ${ドメイン名};
    return 301 https://$host$request_uri;
  }
  server {
    listen 443 ssl http2;
    server_name ${ドメイン名};
    charset UTF-8;

    ssl on;
    ssl_certificate /etc/letsencrypt/live/ge-no.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/ge-no.com/privkey.pem;

    location / {
      proxy_pass https://wordpress:80/;
      proxy_redirect default;
    }
  }
}

certbotコンテナ

さきほどDLしたものそのままでOKです。Dockerfileは不要です。

webappコンテナ

インターネットに公開するNuxt.jsアプリのコンテナです。
ビルド時にnuxt環境をととのえておきます。

Dockerfile
FROM node:12.4.0-alpine

WORKDIR /webapp

ENV LANG C.UTF-8
ENV TZ Asia/Tokyo

RUN apk update && \
    apk add git && \
    npm install -g npm && \
    npm install -g yarn && \
    npm install -g nuxt

wordpressコンテナ

wordpress+wordpressAPIが動作するだけのコンテナです。Dockerfileは不要です。
wordpressイメージは各種バージョンのほかにも、apacheいり、php-fpm版などもあります。今回はバックエンドとして使うためphp-fpmがほしいところではありますが、編集画面にアクセスするのにnginx側で設定するのも面倒なので、素直にapache版を使用しました。CLIを使用してセットアップできればよりかっこよさそうですが、ディレクトリまるまる外部化したおかげもあり、Wordpressのセットアップはコンテナが再起動しても保持されるので、初回のみブラウザからアクセスして設定するところは妥協します。

mariadbコンテナ

Oracleはイヤなので wordpress用mariadbです。Dockerfileは不要です。
my.confはUTF-8の設定と、イメージを適時分割するように指定したのみです。

my.conf

my.conf
[mysqld]
pid-file        = /var/run/mysqld/mysqld.pid
socket          = /var/run/mysqld/mysqld.sock
datadir         = /var/lib/mysql
secure-file-priv= NULL

symbolic-links=0
character-set-server=utf8
default_authentication_plugin = mysql_native_password

innodb_data_file_path=ibdata1:1G
innodb_file_per_table

!includedir /etc/mysql/conf.d/

webapp_devコンテナ

webappコンテナ用のアプリを開発する場所です。

Dockerfile
FROM centos:centos7.6.1810

ARG ROOT_PSWD
ARG USER_NAME
ARG USER_PSWD

RUN yum -y update && yum clean all && \
    yum -y install epel-release openssh-server sudo git

RUN echo "root:$ROOT_PSWD" |chpasswd
RUN useradd $USER_NAME
RUN echo "$USER_NAME:$USER_PSWD" |chpasswd
RUN echo "$USER_NAME  ALL=(ALL)  ALL" >> /etc/sudoers

RUN curl -sL https://rpm.nodesource.com/setup_11.x | bash -
RUN yum -y install nodejs yarn && \
    npm install -g yarn

RUN mkdir /home/$USER_NAME/.ssh && \
    chmod 700 /home/$USER_NAME/.ssh && \
    chown $USER_NAME /home/$USER_NAME/.ssh/ && \
    /usr/bin/ssh-keygen -t dsa -f /etc/ssh/ssh_host_dsa_key -C '' -N ''

COPY sshd.conf /etc/ssh/sshd_config
COPY id_rsa.pub /home/$USER_NAME/.ssh/authorized_keys
COPY startup_nuxt.sh /home/$USER_NAME/startup_nuxt.sh

RUN chown $USER_NAME /home/$USER_NAME/.ssh/authorized_keys && \
    chown $USER_NAME /home/$USER_NAME/startup_nuxt.sh

WORKDIR /home/$USER_NAME
sshd.conf
Protocol 2
Port ${開発用コンテナ用SSHポート}
ListenAddress 0.0.0.0

SyslogFacility AUTHPRIV
PermitRootLogin no
PubKeyAuthentication yes
AuthorizedKeysFile .ssh/authorized_keys
PasswordAuthentication no
ChallengeResponseAuthentication no
GSSAPICleanupCredentials no
UsePAM yes
X11Forwarding yes
UsePrivilegeSeparation sandbox

AcceptEnv LANG LC_CTYPE LC_NUMERIC LC_TIME LC_COLLATE LC_MONETARY LC_MESSAGES
AcceptEnv LC_PAPER LC_NAME LC_ADDRESS LC_TELEPHONE LC_MEASUREMENT
AcceptEnv LC_IDENTIFICATION LC_ALL LANGUAGE
AcceptEnv XMODIFIERS

Subsystem sftp /usr/libexec/openssh/sftp-server
id_rsa.pub
(RSA公開鍵)
startup_nuxt.sh
#!/bin/bash
## startup_nuxt_dev.sh
set -x

readonly REPO_NAME="${プロジェクト名}"
readonly REPO_URL="${Githubのトークン+URL}"
readonly GIT_MAIL="${Gitするときのmailアドレス}"
readonly GIT_USER="${Gitするときのユーザ名}"

git clone $REPO_URL
git config --global user.email "$GIT_MAIL"
git config --global user.name "$GIT_USER"
cd $REPO_NAME
yarn install
npm run dev

ビルドと起動

やっと完成しました!では実行していきましょう。
docker-compose.ymlを設置したディレクトリで

ssh
docker-compose build

なにごともなければ、エラーが出ずに終了するはずです

ssh
docker-compose up

かっこよさげなインジケータとともに起動がはじまります。
初回はかなり時間がかかりますが、2回目からはそこそこ高速です。

Wordpress初期設定

VPN接続し、http://192.168.1.254:3080などでwordpressコンテナに直接続すればWordpress初期設定画面が表示されるはずです。
適当に設定した後、WP APIのプラグインをインストールします。

  • wordpress初期セットアップ
  • [WP REST API Controller]プラグインのインストール
  • [パーマリンク設定] を [数字ベース] に変更
  • http://192.168.1.254:3000/wp-json/wp/v2/postsなどにアクセスしJSONが見れることを確認

その他拡張エディタなどお好みがあれば追加してください。

開発環境初期設定

Nuxtでプロジェクトを作成しましょう。
git push します

コンテナ群再起動

これですべてのファイルがそろったはずです。コンテナを終了し、再度起動します

ssh
[user01@hostname ~]# docker-compose down
[user01@hostname ~]# docker-compose up -d #-dでバックグラウンド実行

動作確認

Webアクセス

しばらくして起動したころになったら、Webアクセスして各種コンテナ内サービスがみれるかを確認します

  • グローバルアドレスでhttpへアクセスし、httpsへ転送されてNuxtの開始ページがみれる
  • グローバルアドレスでhttpsへアクセスし、Nuxtの開始ページがみれる
  • VPNローカルアドレスでhttpへアクセスし、wordpressがみれる
  • VPNローカルアドレスでhttpへアクセスし、開発用コンテナのNuxtの開始ページがみれる

Wordpress接続確認

コンテナ内部で他コンテナの情報がとれるかを確認します

ssh
[user01@hostname ~]# docker exec -it webapp_dev bash
[root@webapp_dev]# curl http://wordpress:80/wp-json/wp/v2/posts

WordpressのHello World記事のJSONとかが取得できていればOKです

VSCodeからssh

  • VSCodeにRemoteSSHエクステンションをインストール
  • settingから接続configを記述
  • 接続

何度かパスフレーズを聞かれるので入力します。SSH Agentの警告がでた場合、いれてもいいですし無しでもできます。

SSL認証鍵更新

期限に余裕があるので更新はスキップされますが、動作していればOKです。

ssh
[user01@hostname ~]# docker run --rm -p 80 -v "/etc/letsencrypt:/etc/letsencrypt" certbot/certbot certonly --webroot -w /etc/letsencrypt -d ${あなたのドメイン} --agree-tos --force-renew

この作業でつかう確認コマンド集

ssh
#コンテナをすべて停止する
docker-compose down

#コンテナイメージ一覧
docker image ls

#稼働中コンテナ一覧
docker ps

#コンテナ内のログ
docker logs ${コンテナ名}

よくあるエラー

コンテナが起動しない(ポートアサインエラー)

コンテナのPortsに外部IPアドレスまで指定した場合、コンテナ起動時にそのIFが存在していないと起動できないようです。portsに仮想IF/IPを指定した場合、仮想IFが存在する状態で(VPN接続するなど)docker-compose upする必要があります

mysqlでmysql_updateしろというログがでている

mariadbでは最新版でも出なかったのですが、mysqlを使ってみた時に遭遇しました。
mysql_upgrade -u ${mariasqlのユーザ} -p${mariadbのパスワード} を実行してください
コンテナ起動時ではなく、イメージがある限り1度実行すれば大丈夫なようです。

nginxの対象ポートじゃないのに勝手にhttpsに転送される

ChromeかFirefoxを使用している場合、ブラウザ側の挙動として、一度httpsアクセスしたサイトは強制的にhttpsにリダイレクトします。リセットしてからアクセスしてください。このへんのテストはEdgeちゃんが唯一輝く場面かと思います・・・。

wordpressがみれない

wordpressは初期設定した際にいろいろ自動でやってくれるので、一度ドメイン側からwordpressにアクセスして初期設定をしてしまうとIPアドレス直打ちではみれなくなったります。wordpresのディレクトリとDBイメージを削除してから再度実行しましょう

npm run devやnpm buildができない

nuxtプロジェクト開始時の設定でnpmにした場合は、npmで、yarnにした場合はyarn startにしてください

Teraterm等ではsshできるのにVSCodeでsshできない

C:\Users\${USERNAME}\.ssh\known_hostsにゴミがのこっている可能性があります。中身をすべて削除してください(ファイルは消さないでください)

さいごに?

おれたちはようやくのぼりはじめたばかりだからな・・・このはてしなく遠いコンテナ坂をよ・・・。

ということで、コンテナ間通信あり、永続化あり、専用コンテナあり、ほぼVMコンテナありと、一通りすべて体験できる地獄コンテナ群でとても勉強になりました。
dockerについてはむかし「ガチなドッカーイメージをプルっちまうとマジ1発でコンテナードな環境がよォ!」てきなかんじのことを聞いたので、じぶんのような雑魚でもセキュアな環境が一発でポワワ・・・な魔法アイテムだと思っていたのですが、使ってみると「結局NWは複雑になるし相変わらずconfをショドーする力いるしDockerfileもほぼshellだしshell力がさらに重要なんじゃ・・・?」とかおもわなくもないです。

ゆくゆくはDockerネットワークの挙動を把握しつつ、環境変数も活かして各種confも定数なしで記述したいところです。

次回はやっとNuxt.jsをさわってみます。ながかった・・・。

6
7
0

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
6
7