今まで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