PythonでGUIアプリを作成して、それを他の人に動作確認または修正をしてもらいたいときがあります。このとき、GUIアプリを動かすのにPythonパッケージが多数必要であったり、OSによってGUIアプリの動作が違っていたりして動作確認が難しいことがありました。
Dockerを使えば、その問題を解決できるということで、Dockerを導入したのですが、最初の一歩がわからず、環境を共有してもらうのに2、3日かかってしまいました。それなので、Dockerを使って環境を共有するまでのやり方をまとめました。Dockerのインストール方法やDockerについての説明は省いています。初学者の備忘録ですが、参考にしてもらえれば幸いです。
主に参考にした資料は以下です。
- 環境
- 構築する環境
- コンテナの作成
- コンテナの中の環境構築
- コンテナにソースをコピー
- GUIアプリの起動
- コンテナのイメージ化
- 作成したイメージのタグ付け
- DockerHubへのPush
- 他人が自分のイメージを使う場合
- 他人が書き換えたソースの動作確認する場合
- コンテナやイメージの削除
- おわりに
- 参考文献
環境
構築する環境
コンテナの作成
まず、コンテナの作成をします。コンテナの作成は「docker container run <オプション><イメージ名><コンテナで実行するコマンド>」でできます。 Ubuntu20.04を動作させるコンテナの作成は以下のコマンドでできます。
$ docker container run ubuntu:20.04
イメージが手元にない場合はイメージをDockerHubからダウンロードしてきます。<オプション>と<コンテナで実行するコマンド>は上のように省略可能です。 <コンテナで実行するコマンド>を省略した場合、イメージに設定されたデフォルトのコマンドが実行されます。
ただ、上のコマンドではコンテナに入ることもできませんし、すぐにコンテナが停止してしまうのでオプションをつける必要があります。 コンテナのbash操作は以下のコマンドでできます。
$ docker container run -it ubuntu:20.04 bash
「-it」というオプションをつけることでコンテナの直接操作ができるようになります。 ちなみに「-i, --interactive」と「-t, --tty」をくっつけたオプションとなっています。
コンテナの中の環境構築
コンテナのbash操作が可能になればコンテナの中のrootディレクトリにいると思います。 コンテナの中の環境構築ではDocker特有のことはあまりないです。コンテナに入った直後にroot権限をもつので sudoコマンドなしでパッケージをインストールできます。
自分の場合ですと、インストールを早くするためにapt-getの利用リポジトリを日本サーバーに変更します(wxPythonを使うためのパッケージのダウンロードが結構かかる)。それから、Python 3.8とpipをインストールします。
# apt-getの利用リポジトリを日本サーバーに変更 $ sed -i.bak -e "s%http://archive.ubuntu.com/ubuntu/%http://ftp.jaist.ac.jp/pub/Linux/ubuntu/%g" /etc/apt/sources.list # python3.8 と pipのインストール $ apt update $ apt install -y python3.8 python3-pip
wxPythonに必要なライブラリをインストールする。
# wxPythonに必要なライブラリを入れる $ apt install -y libgtk-3-dev \ libnotify-dev \ libsdl2-2.0-0 \ libsdl2-dev
必要なPythonのパッケージをインストールする。
# Pythonのパッケージを入れる $ pip3 install numpy $ pip3 install scipy $ pip3 install matplotlib $ pip3 install -U -f https://extras.wxpython.org/wxPython4/extras/linux/gtk3/ubuntu-20.04 wxPython
コンテナにソースをコピー
環境構築ができたら、ホストからコンテナにソースコードをコピーします。 コンテナへのファイルのコピーは「docker container cp <コピー元><コピー先>」でできます。 今利用しているコンソールはコンテナのbashを表示していますので、別のコンソールを起動してコマンドを入力してください。 ただ、コンテナの名前がわからないと思いますので、「docker container ls 」 で現在稼働しているコンテナを表示させて名前を調べてください。コンテナのパスは「コンテナ名:コンテナ内のパス」 という形式で指定してください。
# コンテナの名前を調べる $ docker container ls CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES c68db22ff87d ubuntu:20.04 "bash" 2 hours ago Up 2 hours pedantic_williams # ソースをディレクトリごとコピー $ docker container cp src pedantic_williams:/
今回は例として、srcに以下のwx_example.pyだけを入れています。
import wx class MyApp(wx.Frame): def __init__(self, parent, id = -1, title = None): wx.Frame.__init__(self, parent, id, title) self.SetSize((300, 300)) #パネルの作成 panel_G = wx.Panel(self, id, pos=(10, 10), size=(200, 280)) panel_G.SetBackgroundColour((0, 255, 0)) #描画 self.Show(True) if __name__ == "__main__": app = wx.App() MyApp(None, wx.ID_ANY, "title") app.MainLoop()
GUIアプリの起動
コンテナの中でコードを実行する前に、ウィンドウをホスト側にとばすためにコンテナのDISPLAY環境変数を設定する必要があります。別のコンソールで「echo $DISPLAY」をしたりしてホスト側のDISPLAY環境変数を手に入れて、コンテナのDISPLAY環境変数に代入します。実行することでウィンドウがとんできます。
$ export DISPLAY=(ホスト側のDISPLAY) $ python3 /src/wx_example.py
とんできたウィンドウはこんな感じです。
コンテナのイメージ化
動作確認ができたら、exitコマンドでコンテナを停止してください。 コンテナを停止したら、「docker container commit <コンテナ名><イメージ名> 」 で、コンテナのイメージ化ができます。
$ docker container commit pedantic_williams wx_ubuntu2004
ちなみにイメージ名は大文字にするとエラーが出ますので、全て小文字にしてください。 作成されたイメージは「docker image ls」で確認ができます。
作成したイメージのタグ付け
DockerHubにイメージを登録するには名前を「<ユーザー名>/<イメージ名>:<タグ名>」 という形式でないといけません。イメージ名とタグの変更は「docker image tag <元のイメージ名><新しいイメージ名> 」 でできます。
$ docker image tag wx_ubuntu2004 setoti/wx_ubuntu2004
タグ名は省略すれば「latest」というタグ名になります。元のイメージ名は削除されずに残っていますが、「docker image ls 」でimageを確認するとIMAGE IDは同じなので実体も同じとなっています。
DockerHubへのPush
さきほど作成したイメージをDockerHubに登録します。DockerHubへイメージをpushするにはDockerHubアカウントが必要なので登録しておいてください。 アカウントができたら「docker login 」でDockerHubへログインします。ログイン状態で「docker image push <イメージ名>」をすれば、リポジトリが作成されてアップロードされます。
$ docker login $ docker image push setoti/wx_ubuntu2004
他人が自分のイメージを使う場合
イメージをアップロードすることができましたので、Dockerをインストールしていれば、他の人は簡単に環境構築ができます。 例えば、このブログで登録したimageは以下のコマンドで使うことができます(dbind-WARNINGが出るかもしれませんが、GTKライブラリによるものなので気にしないでください)。
$ docker container run --rm -e DISPLAY=$DISPLAY setoti/wx_ubuntu2004 python3 /src/wx_example.py
「--rm」はコンテナが停止したらコンテナを即破棄するオプションです。コンテナは停止しても残り続けるため、 例のように一つのコマンドを実行するときとかに使います。また、「-e」で環境変数付きで起動することができます。
他人が書き換えたソースの動作確認する場合
他人が書き換えたソースコードの動作確認をする場合、例えば、以下のようにすればいいです。
$ docker container run --rm -e DISPLAY=$DISPLAY --mount type=bind,source=<srcまでの絶対パス>,target=/src setoti/wx_ubuntu2004 python3 /src/wx_example.py
「--mount type=bind,source=<ホストのディレクトリ>,target=<コンテナのディレクトリ> 」をつけることで、 ホストのディレクトリをコンテナのディレクトリにマウントします。<ホストのディレクトリ>のパスは絶対パスでないと駄目です。 指定ディレクトリに書き換えたwx_example.pyを置けば、コンテナの環境で動作確認ができます。
コンテナやイメージの削除
最後に後始末として停止しているコンテナを削除します。停止しているコンテナの確認は 「docker container ls -a」でできます。「-a」のオプションを付けることで停止しているコンテナも表示されます。 確認できたら、「docker container rm <削除するコンテナ名>」で削除できます。また、「docker image rm <削除するイメージ名>」でイメージの削除ができます。
$ docker container ls -a (停止中のコンテナも含めて表示される) $ docker container rm pedantic_williams $ docker image ls (イメージ名が表示される) $ docker image rm wx_ubuntu2004
例では、変更前のイメージ名を消しています。名前変更後のイメージsetoti/wx_ubuntu2004があるので実体は消されません。
おわりに
ここに書いた方法によるイメージの作成はあまり推奨される方法ではないみたいです。アプリ開発や運用では DockefileやDocker Composeを使うことが推奨されるようです。ただ、私的に使うにはこれでいいかなと思います。
参考文献
[1] 伊藤祐一、「たった1日で基本が身に付く! Docker/Kubernetes超入門」、技術評論社、2020年