podman で nginx + php + mariadb を立ち上げてみる


更新: firewalld の設定に誤りがあったので修正しました。
更新2: php のイメージに gd の jpeg と imagick を追加しました
更新3: firewalld の許可設定は不要でした

CentOS 6 のサポート期限が迫ってきているので CentOS 8 について必死で勉強しています。試しに全部コンテナで環境作ろうとしたのですが、CentOS 8 は docker ではなく podman が標準っぽいので、試しに podman で web サーバーを立ち上げてみました。

主力の PC が Windows なので Hyper-V で CentOS 8 を立ち上げて試しています(なので、 Hyper-V 専用の手順が紛れている可能性がありますがご承知おきください)。podman は docker と互換性があるので、 docker コマンドを podman に置き換えればほぼそのまま動くようです。

今回使用するイメージは以下の3つです。

podman でも Docker Hub からイメージをプルできます。

podman の目玉機能として、 rootless (ルート以外のユーザー)でコンテナ実行できる機能があるのですが、今回は 80 番ポートを使いたかったので rootfull で構築します。

SELinux が有効な場合は盛大にブロックしてくるので、先に setenforce 0 にしておき、後でポリシーを作りましょう(本記事では割愛)。また、 sudo 付けるのが面倒なので、root でログインして作業する想定で書いています。root 権限必須ですので、環境に合わせて sudo 付けてください。

PHP イメージの作成

php に関しては php の extension を入れないといけないので Dockerfile を書いてイメージを作る必要があります。podman でも Dockerfile なんですね。私はイメージ作成用のファイルをユーザーホームに作るタイプなので、/root/php/Dockerfileに以下のファイルを配置しました。必要な拡張は docker-php-ext-install コマンドを使用してインストールします。

また、拡張をビルドするために必要なパッケージを apk add に指定する必要があります。alpine Linux をベースにしているので、パッケージ管理は apk を使用します。dnf, apt-get とはパッケージ名が違う場合があるので注意が必要です。必要な物は php のドキュメントに書いてありますが、私は面倒なのでエラーが出たら追加する方針で書いてます。

shadow は usermod, groupmod コマンドを入れるために指定しているパッケージです。

FROM php:fpm-alpine

RUN apk add --update --no-cache oniguruma-dev curl-dev \
  icu-dev libpng-dev jpeg-dev giflib-dev shadow \
  imagemagick-dev autoconf make g++ libzip-dev freetype-dev

RUN docker-php-ext-configure gd --with-freetype=/usr/include/ --with-jpeg=/usr/include/

RUN docker-php-ext-install mbstring \
  curl intl gd mysqli pdo_mysql opcache \
  zip exif

RUN pecl install imagick

RUN docker-php-ext-enable imagick

ARG UID=1000
ARG GID=1000

RUN usermod -u $UID www-data
RUN groupmod -g $GID www-data

イメージを作成する前に、 www-data ユーザー&グループをホスト側に作っておきます。php fpm イメージでは www-data ユーザーで php を実行するので、ホスト側とコンテナ側で同じユーザー名&UIDに揃えておいた方が便利なためです。

useradd -M www-data

Dockerfile を配置したディレクトリに移動し、 buildah コマンドを打ちイメージを作成します。buildah はイメージ作成用のツールです。最初から CentOS 8 に入っていました。Dockerfile を置いたディレクトリで実行してください。

buildah bud \
  --build-arg UID=$(id -u www-data) \
  --build-arg GID=$(id -g www-data) \
  --tag my-php .

完了後に buildah imagesコマンドをたたくと localhost/my-phpイメージができているのが確認できます。

pod の作成

pod を作成します。pod はコンテナをまとめるグループのようなものです。ネットワーク的に良さそう(localhost でコンテナ間通信できる)だったので、pod でまとめることにしました。

pod はポートなどインフラ部分を複数のコンテナで共有します。ですのでポートフォワーディングの設定は pod を立ち上げる際に実施します。

podman pod create -p 80:80 -p 443:443 -p 3306:3306 -n wwwpod

ポート番号のマッピングを変えたい場合は、左側の数字を変更します。-p host:containerです。

コンテナで使用するディレクトリの作成

場所は任意ですが、今回はコンテナ関係のファイルを /srvに置くことにしました。設定やデータ系を置く場所です。

mkdir --parents \
  /srv/wwwpod/mariadb/data \
  /srv/wwwpod/nginx/config \
  /srv/wwwpod/php/config \
  /srv/wwwpod/public
/srv/wwwpod/
├── mariadb
│   └── data
├── nginx
│   └── config
├── php
│   └── config
└── public

public は web ページのファイルを置く場所なので、 www-data ユーザー&グループを所有者にしておきます。php が www-data ユーザーで実行されるので、読み書きできるようにしておく必要があるためです。

chown www-data:www-data -R /srv/wwwpod/public

mariadb コンテナの立ち上げ

mariadb を立ち上げます。my-secret-pwは root のパスワードなので変更してください。

podman run  --detach \
  --pod wwwpod \
  --name mariadb \
  --restart always \
  --volume /srv/wwwpod/mariadb/data:/var/lib/mysql \
  --env MYSQL_ROOT_PASSWORD=my-secret-pw \
  --log-driver journald \
  mariadb

標準のログドライバーは k8s-file なのでログファイルが出力されるのですが、ログローテーションの説明を見つけられず不安だったので journald を指定しておきます。

php コンテナの立ち上げ

php の設定ファイルを持ってきます。extension の読み込み設定は、 docker-php-ext-install コマンドにより自動で追加されるので不要です。

podman run --name tmp-php -d localhost/my-php
podman cp tmp-php:/usr/local/etc/php/php.ini-production 
/srv/wwwpod/php/config/php.ini
podman rm -f tmp-php

php を立ち上げます。

podman run --detach \
  --pod wwwpod \
  --name php \
  --volume /srv/wwwpod/php/config/php.ini:/usr/local/etc/php/php.ini:ro \
  --volume /srv/wwwpod/public:/usr/share/nginx/html \
  --log-driver journald \
  localhost/my-php

nginx コンテナの立ち上げ

まずは nginx の設定ファイルを持ってきます。

podman run --name tmp-nginx -d nginx
podman cp tmp-nginx:/etc/nginx/nginx.conf /srv/wwwpod/nginx/config/nginx.conf
podman cp tmp-nginx:/etc/nginx/conf.d/default.conf /srv/wwwpod/nginx/config/conf.d/default.conf
podman rm -f tmp-nginx

/srv/wwwpod/nginx/config/conf.d/default.conf を編集し、 php を実行できるようにします。

server {
    listen       80;
    server_name  localhost;

    #charset koi8-r;
    #access_log  /var/log/nginx/host.access.log  main;

    location / {
        root   /usr/share/nginx/html;
        index  index.php index.html index.htm;
    }

    #error_page  404              /404.html;

    # redirect server error pages to the static page /50x.html
    #
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }

    # proxy the PHP scripts to Apache listening on 127.0.0.1:80
    #
    #location ~ \.php$ {
    #    proxy_pass   http://127.0.0.1;
    #}

    # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
    #
    location ~ \.php$ {
        root   /usr/share/nginx/html;
        try_files $uri =404;
        fastcgi_pass   127.0.0.1:9000;
        fastcgi_index  index.php;
        fastcgi_param  SCRIPT_FILENAME  /usr/share/nginx/html$fastcgi_script_name;
        include        fastcgi_params;
    }

    # deny access to .htaccess files, if Apache's document root
    # concurs with nginx's one
    #
    location ~ /\.ht {
        deny  all;
    }
}

nginx を立ち上げます。

podman run --detach \
  --pod wwwpod \
  --name nginx \
  --volume /srv/wwwpod/nginx/config/nginx.conf:/etc/nginx/nginx.conf:ro \
  --volume /srv/wwwpod/nginx/config/conf.d:/etc/nginx/conf.d:ro \
  --volume /srv/wwwpod/public:/usr/share/nginx/html:ro \
  --log-driver journald \
  nginx

コンテナ立ち上げ後に設定ファイルを修正した場合は、 reload コマンドを実行します。

podman exec nginx nginx -s reload

nginx が2つ並んでるのは間違いではないです。nginx コンテナの nginx コマンドを実行します。

net.ipv4.ip_forward を設定

デフォルトの状態だとコンテナからインターネットが見れなかったので以下のコマンドを打ちました。

sysctl -w net.ipv4.ip_forward=1

cat <<EOF > /etc/sysctl.d/podman.conf
net.ipv4.ip_forward=1
EOF

web ページの配置

/srv/wwwpod/public に html や php ファイルを置けば見えるようになるはずです。試しに WordPress を置いて軽く動かしてみましたが、大丈夫そうです。少なくとも記事の投稿やプラグインのインストールは可能でした。

firewalld の設定

この章は設定不要でした。firewalld の拒否設定より先にポートフォワーディングが適用されるので、ポートマッピングしただけで外から見えます。

外部に公開する場合は firewall-cmd を使って許可するポートを指定します。

firewall-cmd --zone=public --add-port=8080/tcp
firewall-cmd --zone=public --add-port=8080/tcp --permanent

firewall-cmd --zone=public --add-service=https
firewall-cmd --zone=public --add-service=https --permanent

–permanent ありの場合は firewall-cmd –reload も必要ですが、 reload をするとコンテナの再起動が必要になります(コンテナが起動時に自動設定したルールが消える。)。そのため、–permanent オプションあり(恒久的に反映)となし(即時反映)を両方実行します。

その他

診断

何か問題が起きた時に調査する方法です。

実行中のコンテナを見る場合は以下のコマンドを使用します。

podman ps

各コンテナの shell に入る場合は以下のコマンドを打ちます。php だけ alpine なので bash が入っていないです。

podman exec -it nginx bash
podman exec -it php sh
podman exec -it mariadb bash

各コンテナのログを見る場合は以下のコマンドを打ちます。

podman logs -f nginx
podman logs -f php
podman logs -f mariadb

コンテナの立ち上げ、停止

pod ごとまとめて開始・停止したり、コンテナごとに開始・停止したりできます。

podman pod start wwwpod
podman start nginx
podman start php
podman start mariadb
podman pod stop wwwpod
podman stop nginx
podman stop php
podman stop mariadb

課題

お試しなので権限回りの設定や動作確認が甘めです。

  • nginx と mariadb も UID GID そろえた方がよさそう
    • nginx はデフォルトだと UID=101 GID=101 で動く
    • mariadb (mysqld) は UID=999 GID=999 で動く
  • ✔ php と nginx のワーカープロセス、 mariadb 以外は root で動くので、SELinux を適切に設定したほうがよさそう
  • ✔ インターネット側からアクセスできるかの確認 → 確認しました
  • ✔ ホスト起動時にコンテナも立ち上げる方法 → こちらの記事にまとめました
  • mariadb の設定ファイルもホスト側に置く
  • ✔ 存在しない php ファイルへアクセスすると「 File not found. 」画面が出てしまう → 本記事を修正しました
  • ✔ podman のネットワークが初期設定だと IPv6 の通信を受けられない → こちらの記事にまとめました

バージョン情報

今回使用した podman のバージョンです。

podman version 1.6.4
buildah version 1.11.6


コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください