Python + Selenium + ChromeDriver 環境を Docker Compose でまとめてみた
以前、Python + Selenium を使って Chrome ブラウザを操作する、スクレイピングのサンプルプロジェクトを紹介した。
今回はこの環境を Docker Compose で構築して、ホスト OS 環境を汚すことなく動かせるようにしてみる。
コード全量が置いてある GitHub リポジトリは以下。
Docker Compose とは?
Docker Compose とは、複数の Docker コンテナを組み合わせて使用する際に、うまいことまとめて実行してくれるツール。Docker 本体に同梱されているので、Docker をインストールしてあれば docker-compose
コマンドが既に動作するはずだ。
例えば、
- Python を動かすコンテナ
- Chrome ブラウザがインストールしてあるコンテナ
- Python コンテナから Chrome コンテナを操作できるように仲介する Selenium Hub コンテナ
というように、起動する順序やポートの関係性が重要な時に、それをシェルスクリプト等で実現するのではなく、Docker Compose の定義体 (YAML ファイル) に起こしておくことで、あとはよしなにやってもらえる、という便利なコマンドだ。
- 参考 : GitHub - sikkimtemi/selenium
- 上で述べたような構成を取っているサンプルプロジェクト。
今回自分が作った例では、複数のコンテナを扱うことはないのだが、Docker Compose を使うと
$ docker run -v `pwd`:/data -it python:3.7.5-buster /bin/bash
などという長ったらしいコマンドを打つ必要がなくなり、
$ docker-compose up -d
と打つだけで実行可能になって楽なので、使ってみた。
Docker イメージ環境をイメージ (想像) する
さて、今回動かしたいプロジェクトは、
- Python の実行環境
- Chrome ないしは Chromium ブラウザ
- Selenium
が OS 上にインストールされていないと、サンプルコードだけ持ち込んでも動作させられない。
愚直にやるとしたら、Dockerfile
で
- Ubuntu など適当な OS をベースイメージにする
apt-get
などを使って Python をインストール- Chromium ブラウザをインストール
- pip で Selenium をインストール
という感じに実装すると思う。勿論それでも良いのだが、今回は既に Python + Chromium + Selenium が揃っているベースイメージを見つけたので、それを利用しようと思う。
Docker コンテナ上に Chrome・Chrome Driver・Selenium をインストールする
自分が使用したのは joyzoursky/python-chromedriver:3.7-selenium
というイメージだ。
- Docker Hub
- GitHub - joyzoursky/docker-python-chromedriver: Dockerfile for running Python Selenium in headless Chrome (Python 2.7 / Python 3.6 / Python 3.7 / Alpine based Python / Chromedriver / Selenium / Xvfb included in different versions)
何をやっているか調べるため、Dockerfile
を読んでみる。
- 参考 : docker-python-chromedriver/Dockerfile at master · joyzoursky/docker-python-chromedriver · GitHub
中を読むと、python:3.7
という公式イメージをベースに、Chrome と Chrome Driver をインストール、さらに pip で Selenium をインストールしている。なるほど、このぐらいなら自分で実装しても良いレベルだが、今回はコレをそのまま利用することにしよう。
Docker Compose ファイルを書く
さて、自分のプロジェクトディレクトリで、docker-compose.yaml
ファイルを書く。内容は以下のとおり。
my-python:
# ディレクトリを指定するとその配下の Dockerfile を取得して実行する
build: ./
# `$ docker ps` 時に NAME として見える名前
container_name: my-python
# 勝手に終了しないよう設定しておく
command: tail -f /dev/null
# `$ docker exec -it my-python bash` でアタッチした際のカレントディレクトリとなる
working_dir: /root/app/
# ボリュームのマウント
volumes:
- ./:/root/app/
# 環境変数
environment:
- TZ=Asia/Tokyo
build
で指定しているのは、ベースイメージとなるDockerfile
の所在- コレの代わりに
image
と書くと、既存の Docker イメージ名を指定してベースにできる
- コレの代わりに
command
で謎のtail
を行っているのは、起動したコンテナを終了させずに置いておくためのイディオムvolumes
部分で、共有ディレクトリをマウントしている。docker run
時に指定する-v
(--volume
) オプションと同等だ
あとは読めば分かるレベルかと。
さて、build
プロパティではディレクトリを指定しているが、そのディレクトリの配下にある Dockerfile
を探すようになっている。今回は単一の Docker イメージを使うので、プロジェクトルートディレクトリ直下にしたが、必要に応じて複数イメージ用のディレクトリを分けて置いておくと良い。
その Dockerfile
の内容は次のとおり。
# ココで前述のベースイメージを指定している
FROM joyzoursky/python-chromedriver:3.7-selenium
# - Vi・Vim が入っていないのでインストールする
# - pip をアップデートして pipienv をインストールする
RUN set -x && \
apt-get update && \
apt-get install -y vim && \
pip install --upgrade pip && \
pip install pipenv
このプロジェクトがすぐに動かせるよう、pipenv
をインストールしてある。また、コンテナ内でちょっとした編集ができるように vim
も入れているというワケ。
Docker Compose で起動してみる
このような設定ファイルを用意したら、次のコマンドを実行する。
$ docker-compose up -d
Building my-python
# …中略…
Creating my-python ... done
-d
は --detach
(デタッチ) の意味。対象のイメージが存在しなければその場で docker pull
および docker build
を行い、そのまま docker run
してくれるのだが、すぐにアタッチする必要はないので、-d
を付けている。
このコマンドで Dockerfile
をベースにしたビルドまでが完了し、コンテナが起動していることが確認できるだろう。
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
81526a50b822 practice-python-scraping_my-python "tail -f /dev/null" 10 seconds ago Up 9 seconds my-python
それではこのコンテナにアタッチしてみる。
$ docker exec -it my-python bash
docker-dompose.yaml
の working_dir
プロパティで指定したディレクトリにいる状態で、bash
接続できる。
ココからサンプルプロジェクトを動かしていくには、以下のように実行していく。
# 環境構築
$$ pipenv install --dev
# スクリプト実行
$$ pipenv run crawl
$$ pipenv run scrape
共有ディレクトリを設定しているので、結果はホスト OS からも確認できるだろう。
以上
docker
コマンドにオプションをたくさん付けて何個もコンテナを起動したりしていたのが、全てを設定ファイルに書いておいてコマンド一発で起動できるようになった。
複数コンテナを扱わなくても楽になることが多いので、Docker Compose はぜひ覚えておきたい。
Chrome や Chrome Driver をどのようにコンテナ化するかという方法は他にも考えられ、今回の方法だけが唯一の正解というワケではないので、各自「ビルド時間が短く済法」だったり「イメージサイズが小さく済む構成」だったり「スケールしやすい構成」だったりを考えてみて欲しい。