Docker for Macで作ったUbuntuコンテナ内のGUIプログラム(spyder)を使う

docker photo
Photo by maijou2501

Docker for Macを使って開発/分析環境を試験的に作ってみた際の記録です。母艦のMacを汚さずにPythonの開発/分析環境を作れそうだということで、Dockerを使ってみました。Macの場合はMac OSXの上にLinuxコンテナを作るので、軽量とは言えないと思いますが、環境を隔離でき、コンテナごと配布したりできるのが魅力ですね。

構成は次の通り。

  • コンテナはUbuntu16.04で作成します。
  • Pythonはminiconda3でインストールし、最低限必要なパッケージを足しておきます。
  • PythonのIDEにはSpyderを用います。Spyderはコンテナ内で実行し、GUIをMacのディスプレイ上に表示します。ここでは、試験的にX WindowをXQuartzで表示する方法を取り、xhostコマンドで母艦のMac上にSpyderのGUIを表示するようにします1 が、この方法はセキュリティ的には良くない(IPベースで接続許可を出している)ので、試験的でない場合には、Xauthを使う方法などが良いと思われます。

以下、作業内容。MacBook Pro retina(late 2012、OSX Sierra 10.12.2)とMacBook Air(mid 2013、OSX Sierra 10.12.2)で動作確認済みです。

  1. Docker for MacをGet started with Docker for Macからインストールする。
    [Screenshot url=”https://docs.docker.com/docker-for-mac/”]
  2. コンテナ内で実行するプログラムのGUIをMac上に表示するための設定をします。
    • XQuartzをインストールします。私はbrew caskでインストールしました。
      $ brew cask install xquartz
    • インストールしたxquartzを開き、Preferences > Security tabで”Allow connections from network clients” にチェックします。Mac OSXからログオフし、再度ログインします。
    • ターミナルを開き、下記を実行。
      $ open -a xquartz
      $ ip=$(ifconfig en0 | grep inet | awk '$1=="inet" {print $2}')
      $ xhost + $ip
      

      XQuartzを開き、母艦のMacのIPを取得し、X Windowの接続を許可しています。

  3. 下記の内容のDockerfileを適当な場所に作ります。
    FROM ubuntu:16.04
    MAINTAINER Yu Ara
    
    # Change apt repository to Jaist and update package info
    RUN sed -i.bak -e "s%http://archive.ubuntu.com/ubuntu/%http://ftp.jaist.ac.jp/pub/Linux/ubuntu/%g" /etc/apt/sources.list
    RUN apt-get update --fix-missing
    
    # Japanese language support
    RUN localedef -i ja_JP -c -f UTF-8 -A /usr/share/locale/locale.alias ja_JP.UTF-8
    ENV LANG ja_JP.utf8
    RUN apt-get install -y fontconfig
    RUN apt-get install -y fonts-takao
    
    # Install system packages
    RUN apt-get install -y wget bzip2 ca-certificates \
    libglib2.0-0 libxext6 libsm6 libxrender1 \
    git mercurial subversion vim
    
    # Install latest miniconda3 to /opt/conda
    RUN echo 'export PATH=/opt/conda/bin:$PATH' > /etc/profile.d/conda.sh && \
    wget --quiet https://repo.continuum.io/miniconda/Miniconda3-4.1.11-Linux-x86_64.sh -O ~/miniconda.sh && \
    /bin/bash ~/miniconda.sh -b -p /opt/conda && \
    rm ~/miniconda.sh
    
    ENV PATH /opt/conda/bin:$PATH
    
    # Install Tini
    RUN apt-get install -y curl grep sed dpkg && \
    TINI_VERSION=`curl https://github.com/krallin/tini/releases/latest | grep -o "/v.*\"" | sed 's:^..\(.*\).$:\1:'` && \
    curl -L "https://github.com/krallin/tini/releases/download/v${TINI_VERSION}/tini_${TINI_VERSION}.deb" > tini.deb && \
    dpkg -i tini.deb && \
    rm tini.deb && \
    apt-get clean
    
    # Update conda & install conda package
    RUN conda update -y conda
    RUN conda install -y ipython numpy scipy pandas \
    matplotlib seaborn scikit-learn spyder xlrd
    
    # Install graphic driver
    RUN apt-get install -y libgl1-mesa-dri libgl1-mesa-glx --no-install-recommends
    RUN dbus-uuidgen > /etc/machine-id
    
    ENTRYPOINT [ "/usr/bin/tini", "--" ]
    CMD [ "bin/bash" ]
    

    このDockerfileの作成に際しては、下記のサイトを参考にしました。

  4. 2を実行したターミナルで、Dockerfileのあるディレクトリまで移動し、イメージをビルドします。イメージ名(-tオプション)は適当です。
    $ docker build -t ur/python35spyder:latest
  5. 続いて、作成したイメージを実行します。
    $ docker run -it --name spyder -e DISPLAY=$ip:0 -e USER=root \
    -v /tmp/.X11-unix:/tmp/.X11-unix -v /Users:/Users ur/python35spyder:latest

    コンテナ名はspyderとし、一つ目の-eオプションでDISPLAYの環境変数を渡し、二つ目の-eオプションでコンテナ内のUbuntuユーザをrootに指定、一つ目の-vオプションでX Windowを接続、二つ目の-vオプションでMacのUsersフォルダをコンテナにマウント、最後にコンテナで使用するイメージを指定しています。

  6. ターミナル上でUbuntuが起動し、プロンプトがrootの#に変わっています。spyderを実行すると、
    # spyder &

    Macの画面上にUbuntuのSpyderが現れます。

    Macの/Usersディレクトリがマウントされているので、ローカルレポジトリ等からファイルを読み込んで実行できます。なお、Spyder実行の際にfontsconfig関連のエラーを吐きますが、残念ながら解消できず2 、Spyderの実行に支障はありませんので、とりあえずは見なかったことにしています。

  1. 参考サイト:https://fredrikaverpil.github.io/2016/07/31/docker-for-mac-and-gui-applications/ []
  2. straceで確認したところ、fonts.confの参照先がおかしいようですが、修正出来ませんでした

    access("/etc/fonts/@PREFIX@/etc/fonts/fonts.conf", R_OK) = -1 ENOENT (No such file or directory)
    
    access("/opt/conda/etc/fonts/@PREFIX@/etc/fonts/fonts.conf", R_OK) = -1 ENOENT (No such file or directory)

    []