蛇ノ目の記

技術のことも。そうでないことも。

Dockerコンテナ上でPythonスクリプトを動かしてみる

仕事でDjango製のWebアプリのテスト環境をDockerで作る、という試みをやったのでその入り口になる話を書いてみる。

Docker Hubを探せばPython3.7のイメージはあるけど、ここではUbuntu16.04のイメージを使う。

Ubuntu 16.04のイメージにPython3.7をインストールする

Dockerfile中にコメントを書いている通り、Ubuntu16.04にPython3.7をインストールする方法は以下の記事を参考にした。

medium.com

# Dockerfile
FROM ubuntu:16.04

# refs. Install Python3.7 in ubuntu 16.04
# https://medium.com/@manivannan_data/install-python3-7-in-ubuntu-16-04-dfd9b4f11e5c
RUN apt update && apt-get install -y \
    build-essential \
    checkinstall \
    libreadline-gplv2-dev \
    libncursesw5-dev \
    libssl-dev \
    libsqlite3-dev \
    tk-dev \
    libgdbm-dev \
    libc6-dev \
    libbz2-dev \
    zlib1g-dev \
    openssl \
    libffi-dev \
    python3-dev \
    python3-setuptools \
    wget \
    && mkdir /tmp/Python37
WORKDIR tmp/Python37
RUN wget https://www.python.org/ftp/python/3.7.0/Python-3.7.0.tar.xz \
    && tar xvf Python-3.7.0.tar.xz
WORKDIR /tmp/Python37/Python-3.7.0
RUN ./configure --enable-optimizations \
    && make altinstall \
    && mkdir /usr/local/Python
WORKDIR /usr/local/Python

Linux力が全然無いので、Python3.7を入れるだけでこんなにパッケージが必要なのか(小並)となった。

docker-compose.ymlを書く

次にdokcer-compose.ymlを用意する。

Pythonスクリプトを置いたディレクトリをReadOnlyでマウントしてから、ファイルを実行する。

# docker-compose.yml
version: "2"
services:
  python:
    build: .
    container_name: run_python
    volumes:
      - ./src:/usr/local/Python/:ro 
    command: "python3.7 sample.py"

以下がそのスクリプト

# sample.py
print('spam ham eggs')

マウントしたスクリプトを実行する

docker-compose build でサービスをビルドしてから、docker-compose upでサービスを実行する。

スクリプトがファイルを出力するとき

docker-compose.ymlの中でマウントすると、コンテナ上で作成されたファイルはローカルにも作られる。

このとき、ReadOnlyを外しておかないと怒られる。

# sample.py
zen_en = 'zen_en.txt'
zen_ja = 'zen_ja.txt'

with open(zen_en, 'w', encoding='utf-8') as f:
    f.write('Beautiful is better than ugly.')

with open(zen_ja, 'w', encoding='utf-8') as f:
    f.write('醜いよりも美しい方が良い。')

これを書いて、サービスを実行するとローカルに2つのファイルが作られる。

ローカルに影響を与えたくないのであれば、DockerfileCOPYを追加する。

このとき、docker-compose.ymlからvolumesは消しておく。

# Dockerfile
.
.
.
WORKDIR /tmp/Python37/Python-3.7.0
RUN ./configure --enable-optimizations \
    && make altinstall \
    && mkdir /usr/local/Python
WORKDIR /usr/local/Python
COPY ./src /usr/local/Python/
# docker-compose.yml
version: "2"
services:
  python:
    build: .
    container_name: run_python
    command: "python3.7 sample.py"

スクリプトに変更があったときはサービスをビルドし直してから、実行する必要があってひと手間増えてしまう(´・ω・`)

sample.pyにコンテナ上にファイルが作られたか確認するためにファイルの中身を確認する処理を追記した。

# sample.py
zen_en = 'zen_en.txt'
zen_ja = 'zen_ja.txt'

with open(zen_en, 'w', encoding='utf-8') as f:
    f.write('Beautiful is better than ugly.')

with open(zen_ja, 'w', encoding='utf-8') as f:
    f.write('醜いよりも美しい方が良い。')

with open(zen_en, encoding='utf-8') as f:
    for line in f:
        print(line)

with open(zen_ja, encoding='utf-8') as f:
    for line in f:
        print(line)
$ docker-compose build
.
.
.
$ docker-compose up
Recreating run_python ... done
Attaching to run_python
run_python | Beautiful is better than ugly.
run_python | 醜いよりも美しい方が良い。
run_python exited with code 0

まとめ

「Dockerコンテナ上にスクリプトを置いて、コマンドを実行する」という流れを応用して、「Webアプリを置いてテストコマンドを実行」とすればDockerを使ったテスト環境を作れる。