WSL2+Dockerにレンタルサーバーに合わせた環境を作る試行錯誤

記事を読むにあたって

今から記載する内容は、DockerもWSLも触ったことのない人間が手探りでなんとかローカル環境を構築するまでの試行錯誤の記録です。
ベースOSから必要な機能を手探りで探してDockerfileを作成しています。いろいろお作法があるんだと思いますがお目溢しいただけると嬉しいです。
最後に成果物にリンクしています。参考になるかわかりませんが同じ環境の方の助けになれば。

記事のいけてないところ

DockerとWSLとVSCodeの組み合わせでデバッグができる環境まで持って行きたかったけどバグ?でWSL上のVSCodeが起動しなかった。

やりたいこと

レンタルサーバーで借りている環境に近いものを構築してローカル開発環境を整備したい。メモリが貧弱なのでできるだけリソースは抑えたい。

レンタルサーバーの環境

借りているサーバーはロリポップ。今はライトプランだけどそのうちスタンダードプランに変更する予定。

  • 1サーバー構成
  • CentOS 7系
  • PHP 7.4(CGI版)
    • PEAR(インストールまで)
    • GD(インストールまで)
    • Apache 2.4.x
  • Smarty3
  • SQLite
  • MySQL 5.6(※直近で必要なかったので見送り)

また、これ以外にもBootstrap3環境が入っていますが、Webアプリ内に配置されるものなのでDockerでは記載しませんでした。

導入環境

Windows 10 Pro 1908
メモリ 8GB(貧弱…)

構築したい内容

Windows Subsystem for Linux 2(WSL2)と
Docker for Windowsの上で
CentOSを動かしたい。
目的の上ではDockerは必須ではありませんが後学のために。

WSL2とDockerの準備

WSL2の導入

一連の作業は以下の公式ドキュメントに記載されています。

WSL のインストール
コマンド wsl --install を使用して Linux 用 Windows サブシステムをインストールします。 Windows コンピューター上で、好みの Linux ディストリビューションによって実行される Bash ターミナルを使用します。Ubuntu、Debian、SUSE、Kali、Fedora、Peng...

WSL 2 は、Windows 10、バージョン 2004、ビルド 19041 以上でのみ使用できます。
前述のとおり導入環境は1908でしたので、まずWindowsのバージョン更新から行いました。更新にあたってドライブの空き領域が必要になるので注意が必要です。

この手順内で「選択した Linux ディストリビューションをインストールする」という章があるのですが、CentOSがなかったので飛ばしました。

また、トラブルシューティング内に記載の「Linux 用 Windows サブシステムのオプション コンポーネントのインストール」手順も実施してます。

PS C:\Users\hoge> wsl --set-default-version 2
WSL 2 を実行するには、カーネル コンポーネントの更新が必要です。詳細については https://aka.ms/wsl2kernel を参照してください

なお、もろもろのコマンド実行はWindows Terminalを管理者権限で起動してPowerShellで行っています。

ここまでは以下の記事を参考にさせていただきました。

WSL2導入|WinアップデートからWSL2を既定にするまでのスクショ
https://qiita.com/tomokei5634/items/27504849bb4353d8fef8

WSL 2 のインストール,Ubuntu 20.04, 18.04 のインストールと利用
https://www.kkaneko.jp/tools/wsl/wsl2.html

Docker Desktop for Windowsのインストール

インストーラーのダウンロードと公式手順はこちら。
安定版Stableを選択しました。

Docker Desktop for Windows – Docker Hub
https://hub.docker.com/editions/community/docker-ce-desktop-windows/

インストール時の設定では、

  • Enable WSL 2 Windows Features

にチェックを入れてインストールします。

インストール後、「Close and logout」ってボタンが表示されたので押したら強制的にWindowsからログアウトされました。
作業途中の物がある場合は気をつけてください…

インストールが完了するとDockerは自動的に開始します。動作中はクジラのアイコンがタスクトレイに表示され、ターミナルへのアクセスができます。

ってあってちょっと可愛いなって思いました。

バージョン確認とHello-worldもうまく動きました。くじらアイコンも表示されています。


この時点でメモリを6.2GB/8GB利用しています。動くかな…

Dockerfileの作成試行錯誤

今回は目的のOS環境が決まっているので、Dockerfileの作成からチャレンジしてみます。以下の記事を参考にさせていただき、サーバ環境などに合わせて改変を加えました。

ベースとなるOSイメージはDockerのHubサイトからCentOSの公式ビルドを利用しました。
centos7タグを採用。

centos - Official Image | Docker Hub
DEPRECATED; The official build of CentOS.

以下コマンドでもDockerイメージを検索できるんですが通覧性悪かったんでWebサイトでさがしました。

 docker search centos

イメージ作成から動作確認まで

参考元に沿って

  • Dockerfile
  • index.html
  • phpinfo.php
  • supervisord.conf
  • httpd.conf

は同フォルダ内に準備しています。
イメージのビルドコマンドを実施。lolipopという名前でtestタグを付与しました。Dockerfileがあるフォルダで実行します。
途中ビルドに失敗したら必要に応じてDockerfileを書き換えながら再実行します。
※ゴミの掃除方法は後述

docker build -t lolipop:test .

イメージができたらコンテナ起動です。初期生成はrun、以降はstart、stopコマンドを利用するらしいです。
ホスト側のブラウザから確認できるように、コンテナの80番ポートと22番ポートにホスト側の80番と2022ポートを割り当てて起動します。
なおこのポートはDockerfile内でEXPOSE句で外部公開設定したものです。
コンテナ名はlolipopを指定。

docker run -d -p 80:80 -p 2022:22 --name lolipop lolipop:test

http://localhost/にアクセスして動作確認。(※Docker Toolboxはインストールしていません)
無事にIt Works!とPHP7.4.8のphpinfoが見れました。

サーバー環境調整あれこれ

レンタルサーバーの設定に合わせてini設定を置き換えるところまでDockerfile内でやってしまいたいので、SSH接続してphp.iniの原本を確保しておきます。
最終的にはローカルに落としてレンタルサーバーと同じ設定にし、Dockerビルド時にADDで置き換えるようにしておきました。
今後PHPなどのバージョンアップ時は最初はADDせずに作って取ってきて編集してADD、って流れになるのかな…お作法がわからない。

使い慣れたTeraTermでSSH接続を試します。
接続先にlocalhost、接続先ポートにrunで指定したホスト側ポート2022を指定
ユーザ名とパスはDockerfileで作成した一般ユーザの情報を利用します。

rootユーザのパスワードは未だにわかっていませんが、sudoersに入っているのでphp.iniの編集とかは可能です。

ビルド後の状態だとApacheの公開ドキュメントルートの権限がrootユーザーになっていて色々と不便なので、Dockerfile内で作成したユーザーを

  • apacheグループに追加
  • 公開ドキュメントルートの所有者をapache:apacheに変更
  • 権限にグループ書き込み権限を追加
  • SGIDにapacheを設定

するコマンドを追記しました。

SmartyをComposerで導入

レンタルサーバーではSmatryの3系を利用しているので導入します。
とりあえずComposer触ってみるかの精神でComposer経由にしました。

composer.jsonのあるパスでinstallを実行するとその直下にvendorディレクトリができ、各ライブラリがインストールされるらしい。
ドキュメントルートディレクトリの1つ上の階層にcomposer.jsonを配置し、ドキュメントルートとvendorが同一階層に並ぶようにしました。
とりあえずcomposer.jsonを作って
https://github.com/smarty-php/smarty
ここで案内のあるstable versionの内容を記載します(全体を囲む中括弧が必要です)。

あとはinstallです。と思ったら

# composer install
The zip extension and unzip command are both missing, skipping.

エラーが出ました。zip、unzipがなかったみたいなのでDockerfileに追加しました。

さてSmartyを入れたわけですが、PHPファイル開発時のincludeの記載方法をできるだけレンタルサーバに合わせたいです。phpinfo.phpにて現在のincludeパスの設定を確認してみると
.:/usr/share/pear:/usr/share/php
となっていました。

レンタルサーバでは”Smarty/Smarty.class.php”と指定させるようになっています。
Composerにてインストールされたパスは
/webapp/vendor/smarty/smarty/libs/Smarty.class.php
なので、
/usr/share/phpの下に”Smarty”として
“/webapp/vendor/smarty/smarty/libs/”までのシンボリックリンクを貼ってやることにしました。

ln -s /webapp/vendor/smarty/smarty/libs/ /usr/share/php/Smarty

公式のマニュアルに記載のPHPで動作確認します。テンプレートも適用します。
https://www.smarty.net/docsv2/ja/installing.smarty.basic.tpl

smartytest.php

<?php
// 注: Smarty の 'S' は大文字です
require_once('Smarty/Smarty.class.php');
$smarty = new Smarty();
$smarty->display('hello.tpl');

templates/hello.tpl

Hello, World! by Smarty v{$smarty.version}

ドキュメントルート直下とその下階層に同ファイルを配置して、どの階層でも同じ記載でSmartyライブラリが参照できることを確認しました。

Bootstrapはpublic内に配置するものなのでDockerでは特にいじらないことに。
直接ルート内に配置し、動きを確認できました。

その後、コンテナ作成時にホスト側のワークスペースをpublicとしてマウントする方法に変更し、無事に動作確認環境が生成できました。
※本当はする予定ではなかった

docker run -v D:\Work\www\:/webapp/public -d -p 80:80 -p 2022:22 --name lolipop lolipop:test

ゴミコンテナ、ゴミイメージの削除

依存性やらなんやらの作業中にビルドの失敗でゴミコンテナとゴミイメージができてしまったので削除手順メモ。

docker images でNONEを確認
docker image rm imageid で削除
コケたときの残骸が残ってて削除できないよエラーが出たら該当コンテナを確認
docker ps -a
該当コンテナを削除して
docker rm containerid
イメージ削除を再試行

最終的な構成

削ったもの

libmcrypt
php-mcrypt
 非推奨とのことなので削除
memcached
 使わなさそうなので削除

最初は削ってみたけど動かなくて記載を復活したもの

supervisor
 Dockerにはinitプロセスがないらしい。永続系サービスが動かないそうで、supervisor機能を入れるのが一般的とのこと。

追加したもの

gd-last
 php-gdの依存
libraqm
 gd-lastの依存
php-fedora-autoloader
 php-pearの依存
php-pecl-xdebug
 とりあえず入れておく
zip,unzip
 ComposerでSmartyをインストールするときに必要

最終的なDockerfile

# LOLIPOP Dockerfile
#
# Apache + PHP
#
# 2020-07-24
#   CentOS 7.8 + epel,remi
#   Apache 2.4.x
#   PHP 7.4.x

FROM centos:centos7

LABEL maintainer "n0tz <n0tz@example.com>"

# update yum
RUN yum update -y && \
	yum clean all

# epel,remi
RUN yum install -y epel-release && \
	yum install -y http://rpms.famillecollet.com/enterprise/remi-release-7.rpm && \
	yum clean all && \
	sed -i -e "s/enabled *= *1/enabled=0/g" /etc/yum.repos.d/epel.repo && \
	sed -i -e "s/enabled *= *1/enabled=0/g" /etc/yum.repos.d/remi.repo

# httpd, sshd, scp, openssl, sudo, which
RUN yum install -y httpd httpd-tools openssh-server openssh-clients openssl sudo which && \
	yum clean all

# supervisor
RUN yum install --enablerepo=epel -y supervisor && \
	yum clean all

# libraqm (for gd-last)
RUN yum install --enablerepo=epel -y libraqm && \
	yum clean all

# gd-last (for php-gd)
RUN yum install --enablerepo=remi -y gd-last && \
	yum clean all

# php-fedora-autoloader (for php-pear)
RUN yum install --enablerepo=epel -y php-fedora-autoloader && \
	yum clean all

# php
#RUN yum install --enablerepo=remi-php70 -y php php-devel php-gd php-mbstring php-mcrypt php-mysqlnd php-pear php-xml php-opcache && \
RUN yum install --enablerepo=remi-php74 -y php php-devel php-gd php-mbstring php-mysqlnd php-pear php-xml php-opcache php-pecl-xdebug && \
	yum clean all && \
	sed -i -e "s/;date.timezone *=.*$/date.timezone = Asia\/Tokyo/" /etc/php.ini

# composer
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer

# phpunit
RUN curl -L https://phar.phpunit.de/phpunit.phar > /usr/local/bin/phpunit && \
	chmod +x /usr/local/bin/phpunit

# initialize for ssh
RUN sed -i '/pam_loginuid\.so/s/required/optional/' /etc/pam.d/sshd && \
	ssh-keygen -A

# create login user
RUN useradd -d /home/loginuser -m -s /bin/bash loginuser && \
	echo loginuser:loginuserxxx | chpasswd && \
	echo 'loginuser ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers

# timezone
RUN cp -p /usr/share/zoneinfo/Japan /etc/localtime

ENV WEBAPP_ROOT /webapp

RUN cp -p /etc/httpd/conf/httpd.conf /etc/httpd/conf/httpd.conf.orig_20200724
RUN cp -p /etc/supervisord.conf /etc/supervisord.conf.orig_20200724
RUN cp -p /etc/php.d/10-opcache.ini /etc/php.d/10-opcache.ini.orig_20200724

ADD httpd.conf /etc/httpd/conf/httpd.conf
ADD supervisord.conf /etc/supervisord.conf
ADD 10-opcache.ini /etc/php.d/10-opcache.ini

# zip, unzip (for Smarty)
RUN yum install -y zip unzip && \
	yum clean all

EXPOSE 22 80

CMD ["/usr/bin/supervisord"]


# for general user
RUN mkdir -p /webapp/public
RUN gpasswd -a loginuser apache
RUN chown apache:apache /webapp/public
RUN chmod 775 /webapp/public
RUN chmod 2775  /webapp/public

USER loginuser
ADD index.html /webapp/public/index.html
ADD phpinfo.php /webapp/public/phpinfo.php

# Smarty install
USER root
ADD composer.json /webapp/composer.json
RUN cd /webapp/ && \
	composer install

# for lolipop
RUN ln -s /webapp/vendor/smarty/smarty/libs/ /usr/share/php/Smarty

余談:VSCodeを入れたかった

WSLとVSCodeを入れるとデバッグがやりやすくなるっていうから以下手順に沿っていれてみようとしたらできなかったメモ。

Developing in the Windows Subsystem for Linux with Visual Studio Code
https://code.visualstudio.com/docs/remote/wsl

1.WSLを入れる
2.Visual Studio CodeをWindows側に入れる
3.Remote Development拡張を入れる。
入れた。

Powershellでwslと実行してwslのターミナルへ。

VScodeを開きたいディレクトリまで移動。ドライブは/mnt/host/cのように表現されていた。wsl実行したパスが初期になるので参考になる。

code . と打てば起動するらしいがコマンドを認識していなかった。VSCOでは以前から入れていたものなのでPATHが通っていないらしい。

codeはWin10の検索でさっくり見つかったため割愛。AppDataの中にあった。
フルパスで実行してみたものの、エラーが表示される。

/root/.vscode-server/bin/17299e413d5590b14ab0340ea477cdd86ff13daf/bin/code: line 12: /root/.vscode-server/bin/17299e413d5590b14ab0340ea477cdd86ff13daf/node: not found

なんやこれ。
nodeがないと怒られている?

ROOT="$(dirname "$0")"
"$ROOT/node" ${INSPECT:-} "$ROOT/out/vs/server/main.js" "$@"
-rwxr-xr-x 1 root root 43680144 Jul 15 18:22 node
drwxr-xr-x 65 root root 4096 Jul 24 05:56 node_modules

これじゃないの?

visual studio code – VSCode remote server.sh not finding node in wsl docker-desktop – Stack Overflow
https://stackoverflow.com/questions/62437983/vscode-remote-server-sh-not-finding-node-in-wsl-docker-desktop

WSLで入るvscode-serverはDockerに対応していない?マジカヨ
えーそれだけのためにNodejs入れろって書いてある?のか?それはちょっとなあ…

前述の通り、コンテナを作るときのマウントでローカルに持っている開発環境のパスをそのままpublicに持っていきました。これで変更は即時反映になりました。
レンタルサーバに上げずにローカルで動作を確認したいとの目標はとりあえず達成したものの…ものの…

コメント

タイトルとURLをコピーしました