今までPython3.x & Apache2.4.xを使って、CGIをひと通り試してきました。
次は、CGIを別のものに置き換えてWebアプリケーションを作りたくなりました。
PythonのWebアプリケーションというとWSGIが思い浮かびます。ただ、CGI以降WSGI以前に登場したものを使ってみたくなりました。
調べてみると、
- PyApache
- mod_snake
- mod_python
がありました。
PyApacheは、
Runs with Python1.X/2.X under Apache 1.3.X/2.0 (configurable).
[Sunday 18th. January 2004]
との記載があり、現在では使えそうにありませんでした。
mod_snakeも
I no longer have the time or motivation to work on mod_snake, so instead of letting it sit here, I have decided to kill this project.
RIP - Mod Snake
Jon Travis Last modified: Thu May 9 19:00:01 PDT 2002
とあり、プロジェクトが終了していました。
- Graham Dumpleton: The mod_python project soon to be officially dead.
- Graham Dumpleton: The mod_python project is now officially dead.
などの記事から終了したのかなと思いました。
ただ、その後、
にある強い想いとともに復活したようです。
今年に入ってからもGitHub上でcommitがあり、最新版であるmod_python 3.5の日本語ドキュメントも存在することからも、プロジェクトとして動いているようでした。
- mod_python - Apache / Python Integration
- grisha/mod_python: mod_python
- Mod_python ドキュメント — Mod_python 3.5.0-1330047 ドキュメント
また、Python3対応も
The latest release is 3.5.0. Key feature of mod_python 3.5.0 is Python 3 support.
とあり、一応対応できているようでした。
そこで今回、以下を参考に、Python2系でmod_pythonをインストールし、Hello worldしてみます。
インストール — Mod_python 3.5.0-1330047 ドキュメント
目次
環境
- Mac OS X 10.11.6
- Docker for Mac 17.03.1-ce-mac12
- Alpine3.5 + Apache2.4.25 + Python 2.7.13
- mod_python
- GitHub上の最新版
- 今回利用したコミットは
8acf1b7
調査
Alpine Linuxのパッケージにはmod_pythonが無い
Docker + Alpine Linuxを使うため、Alpineのパッケージでmod_pythonを調べました。しかし、mod_pythonは無いようでした。
*python*での検索結果 | Alpine packages
Alpineでmod_pythonを使うには、自分でソースコードからインストールする必要がありそうでした。
mod_pythonをコンパイル・インストールするために必要なパッケージについて
ドキュメントのインストールに記載がありました。
インストール — Mod_python 3.5.0-1330047 ドキュメント
Alpine Linuxの場合、以下のパッケージを用意すれば良さそうでした。
- python
- 今回はPython2系のため
- python-dev
- apache2-dev
- flex
- git
- GitHubからmod_pythonのソースコードを持ってくるため
- build-base
./configure
で必要なもの一式alpine-sdk
だと、これに加えて他のパッケージが多く含まれているため、必要最低限そうなbuild-base
を選択- 場合によっては、build-baseからさらに削れるかも
- 参考:What is the alpine equivalent to build-essential? · Issue #24 · gliderlabs/docker-alpine
- sudo
sudo make install
で必要
Dockerで配布しているApacheの状態について
今回はDockerで配布しているApache(2.4/alpine/Dockerfile)を使うことにします。
httpd - Docker Store
どんな状態のApacheなのかを調べてみます。
# Dockerコンテナを起動 $ docker container run -p 8081:80 --name httpd24 httpd:2.4.25-alpine # 別のコンソールから、Apacheの状態を確認 $ docker container exec -it httpd24 httpd -V Server version: Apache/2.4.25 (Unix) Server built: Mar 3 2017 21:57:00 Server's Module Magic Number: 20120211:67 Server loaded: APR 1.5.2, APR-UTIL 1.5.4 Compiled using: APR 1.5.2, APR-UTIL 1.5.4 Architecture: 64-bit Server MPM: event threaded: yes (fixed thread count) forked: yes (variable process count) Server compiled with.... -D APR_HAS_SENDFILE -D APR_HAS_MMAP -D APR_HAVE_IPV6 (IPv4-mapped addresses enabled) -D APR_USE_SYSVSEM_SERIALIZE -D APR_USE_PTHREAD_SERIALIZE -D SINGLE_LISTEN_UNSERIALIZED_ACCEPT -D APR_HAS_OTHER_CHILD -D AP_HAVE_RELIABLE_PIPED_LOGS -D DYNAMIC_MODULE_LIMIT=256 -D HTTPD_ROOT="/usr/local/apache2" -D SUEXEC_BIN="/usr/local/apache2/bin/suexec" -D DEFAULT_PIDLOG="logs/httpd.pid" -D DEFAULT_SCOREBOARD="logs/apache_runtime_status" -D DEFAULT_ERRORLOG="logs/error_log" -D AP_TYPES_CONFIG_FILE="conf/mime.types" -D SERVER_CONFIG_FILE="conf/httpd.conf"
Server MPM: event
のため、event MPMで動いているようです。
ただ、mod_pythonに関する記事ではpreforkで動いているのを目にするのが多かったため、今回はeventからpreforkへMPMを切り替えることにします。
Apache2.2まではコンパイル時にMPMを指定する必要がありましたが、Apache2.4からはLoadModuleだけでMPMが切り替えられそうでした。
MPMが普通のモジュールになった | Apache2.2の設定ファイルをApache2.4に移植するためにやったことまとめ - Qiita
そのため、デフォルトでインストールされるモジュールを見てみたところ、
# modulesディレクトリには、mpmモジュールが存在しない $ docker container exec -it httpd24 pwd /usr/local/apache2 $ docker container exec -it httpd24 ls -al modules/ ... -rwxr-xr-x 1 root root 177416 Mar 3 21:57 mod_lua.so -rwxr-xr-x 1 root root 23488 Mar 3 21:57 mod_macro.so -rwxr-xr-x 1 root root 27920 Mar 3 21:57 mod_mime.so -rwxr-xr-x 1 root root 37208 Mar 3 21:57 mod_mime_magic.so -rwxr-xr-x 1 root root 46872 Mar 3 21:57 mod_negotiation.so ... # コンパイル時のモジュールを確認 $ docker container exec -it httpd24 httpd -l Compiled in modules: core.c mod_so.c http_core.c event.c # ロードされているモジュールを確認 $ docker container exec -it httpd24 httpd -M AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using 172.17.0.2. Set the 'ServerName' directive globally to suppress this message Loaded Modules: core_module (static) so_module (static) http_module (static) mpm_event_module (static) authn_file_module (shared) authn_core_module (shared) authz_host_module (shared) authz_groupfile_module (shared) authz_user_module (shared) authz_core_module (shared) access_compat_module (shared) auth_basic_module (shared) reqtimeout_module (shared) filter_module (shared) mime_module (shared) log_config_module (shared) env_module (shared) headers_module (shared) setenvif_module (shared) version_module (shared) unixd_module (shared) status_module (shared) autoindex_module (shared) dir_module (shared) alias_module (shared)
と、デフォルトではMPM系のモジュールが無いようです。
これより、公式のDockerイメージではprefork MPMで動かすのが難しいと考えました。
そのため、公式のDockerfileを一部修正し、Apacheをソースコードからインストールする方針とします。
修正部分は、./configure
する時のオプションにおける
--with-mpm=prefork
で、デフォルトのMPMをpreforkにする--enable-mpms-shared=all
で、MPM系モジュールを生成する
の2点です。
- 参考
準備
Dockerfile
今回用意するDockerfileは
- ベースはAlpine 3.5
- Apache2.4は、ソースコードからインストール
- Python2.7やmod_pythonに必要なものは、Alpineのパッケージからインストール
- mod_pythonのソースコードは、GitHubのmasterを使用
httpd.conf
やDockerで使うhttpd-foreground
は、ローカルに用意してコピーhttpd-foreground
は、パーミッションの都合上、ローカルでchmod 755
しておく
cd
は&&
でつなぐ
です。
#-------------------------------------------------- # Apache2.4のインストール:公式のDockerfileを流用 #-------------------------------------------------- FROM alpine:3.5 # ensure www-data user exists RUN set -x \ && addgroup -g 82 -S www-data \ && adduser -u 82 -D -S -G www-data www-data # 82 is the standard uid/gid for "www-data" in Alpine # http://git.alpinelinux.org/cgit/aports/tree/main/apache2/apache2.pre-install?h=v3.3.2 # http://git.alpinelinux.org/cgit/aports/tree/main/lighttpd/lighttpd.pre-install?h=v3.3.2 # http://git.alpinelinux.org/cgit/aports/tree/main/nginx-initscripts/nginx-initscripts.pre-install?h=v3.3.2 ENV HTTPD_PREFIX /usr/local/apache2 ENV PATH $HTTPD_PREFIX/bin:$PATH RUN mkdir -p "$HTTPD_PREFIX" \ && chown www-data:www-data "$HTTPD_PREFIX" WORKDIR $HTTPD_PREFIX ENV HTTPD_VERSION 2.4.25 ENV HTTPD_SHA1 bd6d138c31c109297da2346c6e7b93b9283993d2 # https://issues.apache.org/jira/browse/INFRA-8753?focusedCommentId=14735394#comment-14735394 ENV HTTPD_BZ2_URL https://www.apache.org/dyn/closer.cgi?action=download&filename=httpd/httpd-$HTTPD_VERSION.tar.bz2 # not all the mirrors actually carry the .asc files :'( ENV HTTPD_ASC_URL https://www.apache.org/dist/httpd/httpd-$HTTPD_VERSION.tar.bz2.asc # see https://httpd.apache.org/docs/2.4/install.html#requirements RUN set -x \ && runDeps=' \ apr-dev \ apr-util-dev \ perl \ ' \ && apk add --no-cache --virtual .build-deps \ $runDeps \ ca-certificates \ coreutils \ dpkg-dev dpkg \ gcc \ gnupg \ libc-dev \ # mod_session_crypto libressl \ libressl-dev \ # mod_proxy_html mod_xml2enc libxml2-dev \ # mod_lua lua-dev \ make \ # mod_http2 nghttp2-dev \ pcre-dev \ tar \ # mod_deflate zlib-dev \ \ && wget -O httpd.tar.bz2 "$HTTPD_BZ2_URL" \ && echo "$HTTPD_SHA1 *httpd.tar.bz2" | sha1sum -c - \ # see https://httpd.apache.org/download.cgi#verify && wget -O httpd.tar.bz2.asc "$HTTPD_ASC_URL" \ && export GNUPGHOME="$(mktemp -d)" \ && gpg --keyserver ha.pool.sks-keyservers.net --recv-keys A93D62ECC3C8EA12DB220EC934EA76E6791485A8 \ && gpg --batch --verify httpd.tar.bz2.asc httpd.tar.bz2 \ && rm -r "$GNUPGHOME" httpd.tar.bz2.asc \ \ && mkdir -p src \ && tar -xf httpd.tar.bz2 -C src --strip-components=1 \ && rm httpd.tar.bz2 \ && cd src \ \ && gnuArch="$(dpkg-architecture --query DEB_BUILD_GNU_TYPE)" \ && ./configure \ --build="$gnuArch" \ --prefix="$HTTPD_PREFIX" \ --enable-mods-shared=reallyall \ # デフォルトはpreforkにする --with-mpm=prefork \ # ただし、他のMPMもビルドする:この指定がない場合、MPMのmoduleがないので切り替えられない # http://tt4cs.blogspot.jp/2013/01/how-to-enable-dso-for-apache-2.4.html --enable-mpms-shared=all \ && make -j "$(nproc)" \ && make install \ \ && cd .. \ && rm -r src man manual \ \ && sed -ri \ -e 's!^(\s*CustomLog)\s+\S+!\1 /proc/self/fd/1!g' \ -e 's!^(\s*ErrorLog)\s+\S+!\1 /proc/self/fd/2!g' \ "$HTTPD_PREFIX/conf/httpd.conf" \ \ && runDeps="$runDeps $( \ scanelf --needed --nobanner --recursive /usr/local \ | awk '{ gsub(/,/, "\nso:", $2); print "so:" $2 }' \ | sort -u \ | xargs -r apk info --installed \ | sort -u \ )" \ && apk add --virtual .httpd-rundeps $runDeps \ && apk del .build-deps # ローカルでchmod 755しておく COPY httpd-foreground /usr/local/bin/ EXPOSE 80 CMD ["httpd-foreground"] #-------------------------------------------------- # mod_pythonのインストール #-------------------------------------------------- RUN apk --update --no-cache add python && \ # mod_pythonで必要なパッケージを追加 apk add --no-cache --virtual .mod_python_build_libs git && \ apk add --no-cache --virtual .mod_python_build_libs python-dev && \ apk add --no-cache --virtual .mod_python_build_libs apache2-dev && \ apk add --no-cache --virtual .mod_python_build_libs flex && \ # ./configureで必要 apk add --no-cache --virtual .mod_python_build_libs build-base && \ # sudo make installで必要 apk add --no-cache --virtual .mod_python_build_libs sudo && \ # GitHubからソースコードを持ってきてインストール cd /tmp && \ mkdir mod_python && \ cd mod_python && \ git clone https://github.com/grisha/mod_python.git . && \ ./configure --with-apxs=/usr/local/apache2/bin/apxs --with-python=/usr/bin/python --with-flex=/usr/bin/flex && \ make && \ sudo make install && \ # 不要なパッケージやソースコードを一括削除 apk del .mod_python_build_libs && \ rm -r /tmp/mod_python #-------------------------------------------------- # Apacheの設定 #-------------------------------------------------- # ローカルのhttpd.confをコピー COPY httpd.conf /usr/local/apache2/conf/
httpd.conf
CGIで使ったものを流用します。
ただし、
- CGI関係の記述を削除
- MPMモジュールの指定を追加
LoadModule mpm_prefork_module modules/mod_mpm_prefork.so
- mod_python向けの記述を追加
LoadModule python_module modules/mod_python.so
- mod_pythonのWebアプリを
/usr/local/apache2/htdocs/test
に配置するための記述を追加<Directory "/usr/local/apache2/htdocs/test">
ディレクティブ以下
を変更します。
# ServerRoot: The top of the directory tree ServerRoot "/usr/local/apache2" ServerName localhost # Listen: Allows you to bind Apache to specific IP addresses and/or ports Listen 80 # LoadModule LoadModule authz_core_module modules/mod_authz_core.so LoadModule authz_host_module modules/mod_authz_host.so LoadModule mime_module modules/mod_mime.so LoadModule log_config_module modules/mod_log_config.so LoadModule env_module modules/mod_env.so LoadModule setenvif_module modules/mod_setenvif.so LoadModule unixd_module modules/mod_unixd.so LoadModule status_module modules/mod_status.so # for mod_python LoadModule python_module modules/mod_python.so # for running prefork MPM LoadModule mpm_prefork_module modules/mod_mpm_prefork.so # unixd_module settings User daemon Group daemon # 'Main' server configuration # ServerAdmin: Your address, where problems with the server should be e-mailed. ServerAdmin you@example.com # Deny access to the entirety of your server's filesystem. <Directory /> AllowOverride none Require all denied </Directory> # DocumentRoot DocumentRoot "/usr/local/apache2/htdocs" <Directory "/usr/local/apache2/htdocs"> Options Indexes FollowSymLinks AllowOverride None Require all granted </Directory> <Directory "/usr/local/apache2/htdocs/test"> AddHandler mod_python .py PythonHandler mptest PythonDebug On </Directory> # The following lines prevent .htaccess and .htpasswd files <Files ".ht*"> Require all denied </Files> # Log settings ErrorLog /proc/self/fd/2 LogLevel warn # log_config_module settings LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined LogFormat "%h %l %u %t \"%r\" %>s %b" common CustomLog /proc/self/fd/1 common # mime_module settings TypesConfig conf/mime.types AddType application/x-compress .Z AddType application/x-gzip .gz .tgz
mod_pythonを使ったWebアプリ
チュートリアルにあったテストファイルを、ホストのpath/to/htdocs/test
の中にmptest.py
として用意します。
#!/usr/bin/python # coding: utf-8 from mod_python import apache def handler(req): req.content_type = 'text/plain' req.write("Hello World!") return apache.OK
Dockerコンテナ起動時に、このファイルをホストとDockerコンテナとで共有します。
なお、shebangの値は、以下で調べたものを使いました。
$ docker container exec -it mod_python which python /usr/bin/python
起動と動作確認
Dockerコンテナの起動
Dockerコンテナのイメージを作成し、起動します。
# Dockerコンテナイメージを作成 $ docker image build -t alpine:python27_httpd24_mod_python . # Dockerコンテナを起動し、ホストのhtdocs以下をコンテナと共有 $ docker container run -p 8081:80 --name mod_python -v `pwd`/htdocs/:/usr/local/apache2/htdocs alpine:python27_httpd24_mod_python
Apacheの状況確認
# modulesディレクトリには、mpmモジュールが存在する $ docker container exec -it mod_python pwd /usr/local/apache2 $ docker container exec -it mod_python ls -al modules/ ... -rwxr-xr-x 1 root root 37208 Jun 1 09:49 mod_mime_magic.so -rwxr-xr-x 1 root root 91720 Jun 1 09:49 mod_mpm_event.so -rwxr-xr-x 1 root root 48408 Jun 1 09:49 mod_mpm_prefork.so -rwxr-xr-x 1 root root 67720 Jun 1 09:49 mod_mpm_worker.so ... # コンパイル時のモジュールを確認 $ docker container exec -it mod_python httpd -l Compiled in modules: core.c mod_so.c http_core.c # ロードされているモジュールを確認 $ docker container exec -it mod_python httpd -M Loaded Modules: core_module (static) so_module (static) http_module (static) authz_core_module (shared) authz_host_module (shared) mime_module (shared) log_config_module (shared) env_module (shared) setenvif_module (shared) unixd_module (shared) status_module (shared) python_module (shared) mpm_prefork_module (shared) # Vオプションで状況確認 $ docker container exec -it mod_python httpd -V Server version: Apache/2.4.25 (Unix) Server built: Jun 1 2017 09:48:37 Server's Module Magic Number: 20120211:67 Server loaded: APR 1.5.2, APR-UTIL 1.5.4 Compiled using: APR 1.5.2, APR-UTIL 1.5.4 Architecture: 64-bit Server MPM: prefork threaded: no forked: yes (variable process count) Server compiled with.... -D APR_HAS_SENDFILE -D APR_HAS_MMAP -D APR_HAVE_IPV6 (IPv4-mapped addresses enabled) -D APR_USE_SYSVSEM_SERIALIZE -D APR_USE_PTHREAD_SERIALIZE -D SINGLE_LISTEN_UNSERIALIZED_ACCEPT -D APR_HAS_OTHER_CHILD -D AP_HAVE_RELIABLE_PIPED_LOGS -D DYNAMIC_MODULE_LIMIT=256 -D HTTPD_ROOT="/usr/local/apache2" -D SUEXEC_BIN="/usr/local/apache2/bin/suexec" -D DEFAULT_PIDLOG="logs/httpd.pid" -D DEFAULT_SCOREBOARD="logs/apache_runtime_status" -D DEFAULT_ERRORLOG="logs/error_log" -D AP_TYPES_CONFIG_FILE="conf/mime.types" -D SERVER_CONFIG_FILE="conf/httpd.conf"
mod_pythonがあり、prefork MPMで動作しているようです。
Webアプリの動作確認
curl
を使って動作確認をします。
$ curl localhost:8081/test/mptest.py -D - HTTP/1.1 200 OK Date: Thu, 01 Jun 2017 10:38:23 GMT Server: Apache/2.4.25 (Unix) mod_python/3.5.0-8acf1b7 Python/2.7.13 Transfer-Encoding: chunked Content-Type: text/plain Hello World!
動作しているようです。
ただ、コンテナを起動しているコンソールでは
[Thu Jun 01 10:39:29.700391 2017] [:notice] [pid 9] mod_python: (Re)importing module 'mptest' 172.17.0.1 - - [01/Jun/2017:10:39:29 +0000] "GET /test/mptest.py HTTP/1.1" 200 12 [Thu Jun 01 10:39:29.712520 2017] [:error] [pid 9] make_obcallback: could not import mod_python.apache.\n ImportError: No module named mod_python.apache [Thu Jun 01 10:39:29.712558 2017] [:error] [pid 9] make_obcallback: Python path being used "['/usr/lib/python27.zip', '/usr/lib/python2.7', '/usr/lib/python2.7/plat-linux2', '/usr/lib/python2.7/lib-tk', '/usr/lib/python2.7/lib-old', '/usr/lib/python2.7/lib-dynload']". [Thu Jun 01 10:39:29.712566 2017] [:error] [pid 9] get_interpreter: no interpreter callback found.
と表示されていますが、現段階では気にしないことにします。
なお、DockerでApacheの再起動を普通にすると、
... [Thu Jun 01 11:13:46.312764 2017] [core:notice] [pid 1] AH00052: child pid 129 exit signal Segmentation fault (11) [Thu Jun 01 11:13:46.312770 2017] [core:error] [pid 1] AH00546: no record of generation 0 of exiting child 129 〜以降繰り返し〜 ...
のようにSegmentation faultします。
- php - Restart apache on Docker - Stack Overflow
- php - How to restart apache2 without crashing docker container? - Stack Overflow
ソースコード
GitHubに上げました。alpine_apache_python27_mod_python
ディレクトリの中が今回のものです。
thinkAmi-sandbox/Docker_Apache-sample