エンジニアを目指す初学者に向けて、わかりやすく解説したブログです。
サイトをリニューアルしました

【Python】Dockerを使ってPythonの環境を構築する(TA-Libを使う)

簡単にテクニカル分析を行うことができるTA-LibをPythonで使うためには、

  1. TA-Lib本体をインストール(brew install ta-lib
  2. TA-LibのPython用ラッパーをインストール(pip install TA-Lib

という手順を行う必要がある。

元々Pythonは環境構築が面倒な上に、Homebrewによってインストールされたものに依存してしまうのでDockerで全て環境構築をしてしまおうという話。

結論だけを知りたい場合は、結論の項目に必要なファイルとその内容を記載している。

注意点

  • Dockerの環境構築については割愛する
  • pipenvを使った環境構築を行っているが、pipでも十分(むしろ楽)である

Python用のDockerイメージを検索する

Pythonが使えるDockerイメージをdocker searchコマンドを使って検索する。

もちろん、Docker Hubを使って検索しても良い。

NAME                             DESCRIPTION                                     STARS     OFFICIAL   AUTOMATED
python                           Python is an interpreted, interactive, objec…   5754      [OK]
django                           Django is a free web application framework,1038      [OK]
pypy                             PyPy is a fast, compliant alternative implem…   260       [OK]
nikolaik/python-nodejs           Python with Node.js                             57                   [OK]
joyzoursky/python-chromedriver   Python with Chromedriver, for running automa…   56                   [OK]

このようにDocker Hubに公開されているPythonに関係あるイメージを取得することができる。

STARS ユーザーからのお気に入りの件数
OFFICIAL 公式イメージかどうか
AUTOMATED Dockerfileを元に自動生成されたイメージかどうか

項目の見方

Docker Hubで公開されているイメージは全て安全なものとは限らないので、

公式イメージかDockerfileがきちんと公開されているものを利用するのがよい。

今回は、pythonイメージを利用する。

利用するタグは3.8とする。

Dockerfileを作成する

ここからはDockerの使い方を学びつつ進める。

Dockerfileの基本書式

Dockerfileは

命令 引数

というシンプルな書式で記述される。

よく利用する命令は以下の通り。

FROM ベースイメージの指定
RUN コマンド実行(イメージを作成するために実行するコマンド)
CMD コンテナの実行コマンド(イメージを元に生成されたコンテナ内で実行するコマンド)
ENV 環境変数

また、Dockerfileにはシェルスクリプトの知識が多少必要になるので、覚えておくと良い。

ベースイメージのみで作成してみる

Pythonのベースイメージのみを指定し、Dockerイメージを作成し、起動する。

「Dockerfile」という名前のファイルを作成し、下記のように記載する。

Dockerfile

# Python3.8をベースイメージにする
# FROM イメージ名:タグ名
FROM python:3.8

DockerfileからDockerイメージを作成する。

# 「docker build -t 生成するイメージ名:タグ名 Dockerfileの場所」の形式で記載
$ ls
Dockerfile
$ docker build -t python-sample:1.0 .
Sending build context to Docker daemon  184.3kB
Step 1/1 : FROM python:3.8
3.8: Pulling from library/python
6c33745f49b4: Pull complete
ef072fc32a84: Pull complete
c0afb8e68e0b: Pull complete
d599c07d28e6: Pull complete
f2ecc74db11a: Pull complete
26856d31ce86: Pull complete
2cd68d824f12: Pull complete
7ea1535f18c3: Pull complete
2bef93d9a76e: Pull complete
Digest: sha256:9079aa8582543494225d2b3a28fce526d9a6b06eb06ce2bac3eeee592fcfc49e
Status: Downloaded newer image for python:3.8
 ---> f5041c8ae6b1
Successfully built f5041c8ae6b1
Successfully tagged python-sample:1.0

初回はpython:3.8イメージをダウンロードする所から始まることがわかる。

$ docker image ls
REPOSITORY                    TAG       IMAGE ID       CREATED         SIZE
python-sample                 1.0       f5041c8ae6b1   6 days ago      884MB
python                        3.8       f5041c8ae6b1   6 days ago      884MB

docker image lsでイメージの一覧を確認でき、

  • Docker Hubからダウンロードされた「python」イメージ
  • 「python」イメージをベースイメージとして、今作った「python-sample」イメージ

が存在することがわかる。

また、今作った「python-sample」イメージの中身は「python」イメージと全く同一のため、

イメージIDが一致していることが確認できる。

ホストマシンのディレクトリを共有する

Pythonを利用する時、

  • ホストマシンにあるPythonのソースコードを読み込みたい
  • コンテナが消えても、プログラムによって出力されたファイルが消えないようにしたい

という要求が自然と発生する。

これを実現するためにはホストマシンのディレクトリをDockerコンテナと共有(マウント)する必要がある。

まずは下記のようなディレクトリ構成にする。

hello.pyには「hello, world」を出力するPythonのスクリプトが記述されている。

この時のsrc/ディレクトリごとDockerコンテナに共有していく。

$ tree .
.
├── Dockerfile
├── README.md
└── src
    └── hello.py

1 directories, 3 files

次にDockerfileを下記のように書き換え、src/ディレクトリ配下のファイルが読み込まれていることを確認できるようにする。

# Python 3.8をベースイメージとする
FROM python:3.8

WORKDIR /workspace

# ディレクトリを表示し、Pythonファイルを実行する
CMD ls -la \
    && python hello.py

実際にディレクトリをマウントする時、docker container runコマンドの--mountオプションを利用する。

--volumeオプションでも同様のことが可能だが、ここでは違いなどは割愛する)

実行結果

# まずはDockerイメージを「python-sample」という名前でビルドする
$ docker build -t python-sample:1.0 .

# Dockerイメージから、実際にコンテナを起動する
# --name "python-container":起動するコンテナ名を「python-container」とする
# --mountオプションで利用する「source」には、ホストマシンの共有したいディレクトリを指定
# --mountオプションで利用する「target」には、対応するDockerコンテナのディレクトリを指定する
$ docker container run -it --name "python-container" --mount type=bind,source="$(pwd)"/src,target=/workspace python-sample:1.0
total 8
drwxr-xr-x 3 root root   96 Dec 29 05:35 .
drwxr-xr-x 1 root root 4096 Dec 29 14:21 ..
-rw-r--r-- 1 root root   24 Dec 29 05:34 hello.py
hello, world.

コンテナ起動の実行結果を確認すると、ls -laコマンドの実行結果とpython hello.pyの実行結果が表示されていることがわかる。

必要なモジュールをインストールする記述を追加する

Pythonで利用するモジュールをインストールするコマンドを追加する。

また、ここではpipenvを使ってモジュール管理をすることを想定する。

  1. TA-Libのインストールに必要なコマンドをインストールする
  2. TA-Libのインストールを行う
  3. pipenvコマンドをインストールする
# 必要なコマンドのインストールを行う
RUN apt-get -y update && apt-get install -y wget vim git curl make sudo

# TA-Libのインストールを行う
RUN wget http://prdownloads.sourceforge.net/ta-lib/ta-lib-0.4.0-src.tar.gz && \
    tar -xzf ta-lib-0.4.0-src.tar.gz && \
    cd ta-lib/ && \
    ./configure --prefix=/usr && \
    make && \
    sudo make install

# pipenvを使えるようにする
RUN pip install pipenv

TA-Libのインストール方法は下記のLinux用インストール方法を参考にする。

PipfileにはTA-Libを使えるように

TA-Libを利用するように記述されたPipfileを用意する。

pipenv install TA-Libでインストールしながら作成しても良い。

Pipfile

// (省略)

[packages]
ta-lib = "*"

[requires]
python_version = "3.8"

// (省略)

Dockerfileにてモジュールの初期インストールと実行コマンドを記載する

  • pipenvで使うモジュールの初期インストール(pipenv install
  • main.pyの実行用スクリプト(自分で適当に決めてOK)を実行

Dockerfile

# (省略)

# pipenvを使えるようにする
RUN pip install pipenv

# 作業ディレクトリを決める
WORKDIR /workspace

# ディレクトリを表示し、Pythonファイルを実行する
CMD pipenv install && \
    pipenv run dev

main.pyの実行用スクリプトはもちろんPipfileに記載しておく必要がある。

Pipfile

[[source]]
name = "pypi"
url = "https://pypi.org/simple"
verify_ssl = true

[packages]
ta-lib = "*"

[requires]
python_version = "3.8"

[scripts]
dev = "python main.py"

結論

ディレクトリ構成

$ tree .
.
├── Dockerfile
├── README.md
└── src
    ├── Pipfile
    ├── Pipfile.lock
    └── main.py

1 directories, 5 files

./Dockerfile

# Python 3.8をベースイメージとする
FROM python:3.8

# 必要なコマンドのインストールを行う
RUN apt-get -y update && apt-get install -y wget vim git curl make sudo

# TA-Libのインストールを行う
RUN wget http://prdownloads.sourceforge.net/ta-lib/ta-lib-0.4.0-src.tar.gz && \
    tar -xzf ta-lib-0.4.0-src.tar.gz && \
    cd ta-lib/ && \
    ./configure --prefix=/usr && \
    make && \
    sudo make install

# pipenvを使えるようにする
RUN pip install pipenv

WORKDIR /workspace

# ディレクトリを表示し、Pythonファイルを実行する
CMD pipenv install && \
    pipenv run dev

./src/Pipfile

[[source]]
name = "pypi"
url = "https://pypi.org/simple"
verify_ssl = true

[packages]
ta-lib = "*"

[requires]
python_version = "3.8"

[scripts]
dev = "python main.py"

./src/main.py

import talib as ta

def main():
    print(ta.get_function_groups)

if __name__ == '__main__':
    main()

実行結果

$ docker build -t python-sample:1.0 .
# イメージビルド時のログが出力される(省略
Successfully built a55d1417182f
Successfully tagged python-sample:1.0

$ docker container run -it --name "python-container" --mount type=bind,source="$(pwd)"/src,target=/workspace python-sample:1.0
Creating a virtualenv for this project...
Pipfile: /workspace/Pipfile
Using /usr/local/bin/python3.8 (3.8.7) to create virtualenv...
⠏ Creating virtual environment...created virtual environment CPython3.8.7.final.0-64 in 592ms
  creator CPython3Posix(dest=/root/.local/share/virtualenvs/workspace-dqq3IVyd, clear=False, no_vcs_ignore=False, global=False)
  seeder FromAppData(download=False, pip=bundle, setuptools=bundle, wheel=bundle, via=copy, app_data_dir=/root/.local/share/virtualenv)
    added seed packages: pip==20.3.1, setuptools==51.0.0, wheel==0.36.1
  activators BashActivator,CShellActivator,FishActivator,PowerShellActivator,PythonActivator,XonshActivator

✔ Successfully created virtual environment!
Virtualenv location: /root/.local/share/virtualenvs/workspace-dqq3IVyd
Installing dependencies from Pipfile.lock (7fbe10)...
  🐍   ▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉ 18/1800:00:27
To activate this project's virtualenv, run pipenv shell.
Alternatively, run a command inside the virtualenv with pipenv run.
<function get_function_groups at 0x7f715b0b2a60>

まとめ

  1. ベースイメージにはPythonのイメージを利用する
  2. TA-LibはLinuxへのインストール方法を参考にDockerfileにスクリプトを記載する
  3. pipもしくはpipenvの初期設定とPythonスクリプト実行の記載をする
  4. Dockerイメージをビルドする
  5. コンテナ起動時に任意のディレクトリをマウントしながら起動する

補足

  • モジュールの初期インストール(pipenv install)のタイミングを工夫するともう少し効率的になる
  • Dockerイメージの作成とコンテナの起動コマンドは長いので、シェルスクリプトでまとめるともう少しスマートになる
  • 私はpipenvが好きなのでpipenvを使ったが、普通にpip使っても良い(むしろそっちの方が良いかも)
  • 全体を通した実行速度を考えると、普通にTA-Libインストールしてpipenv使った方が速い