Remote-Containerを使ってDockerのコンテナ内でJuliaのPlotsを使い，ホスト側で結果を表示する

# はじめに
## この記事の背景
JuliaをDocker Containerの中で動かしていると，そのままではPlotsを表示することが出来ない．これはJulia特有のことではなく，一般にコンテナの内部で動くプログラムからGUI表示をすることはそのままの設定では出来ない．

この記事では，Docker Containerで動くJuliaでPlotをホスト側に表示するために必要な設定をまとめる．コンテナ内でJuliaのPlotを行い，その結果をx forwardingによってホスト側で表示する．そのためのコンテナ側の環境変数の設定とホスト側の用意について述べる．

本記事ではエディタにVS Codeを用い，その拡張機能[Remote-Cointainers](https://code.visualstudio.com/docs/remote/containers)を利用してコンテナ内でVS Codeを開いて開発できるようにしようと思う．Remote-Cointainersを使うと，あたかもローカルで作業しているかのようにコンテナで作業が出来てVS Codeの強力な開発機能を使える．


## この記事はこんな人向け！  

- [Julia公式のDocker image](https://hub.docker.com/_/julia)に基づいたコンテナ環境でJuliaを使いたい！
- コンテナ内で`Plots.jl`を使いたい！
- ホスト側にプロットした結果をGUIで表示したい！
- VS Codeの[Remote-Cointainers](https://code.visualstudio.com/docs/remote/containers)の拡張機能を使いたい！


## 実行環境

- Windows 10 Pro 20H2 / Windows 10 Home 20H2
- Docker Desktop Version 3.2.2
- Visual Studio Code Version 1.54.3
    - Extension: [Remote - Containers](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers9) v0.165.0

# 事前用意

## 前提事項

前提事項としてDocker DesktopとVS Codeはインストールされているものとします．インストール方法は他の資料を参考にしてもらえると幸いです．

## Remote -Containersのインストール

Remote-Cointainersは，Dockerコンテナ内でVS Codeを開いて開発を行うことが出来るようにするVS Codeの拡張機能です．インストールは簡単です．

1. VS CodeのEXTENSIONS: MARKETPLACEで`remote-containers`を検索する．
1. Remote-Containersをインストールする

![vscode.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/598251/e43de174-8c00-465f-7995-1601adc8fb87.jpeg)

## 設定ファイルの用意
以下に示すような構成でファイルを用意してください．

開発を行うフォルダの中に以下の示すような構成でフォルダとファイルを作ります．

```
(directory you specify)
│  plottest.jl
│
├─.devcontainer
│      devcontainer.json
│      Dockerfile
│
└─.tmp
```

- `plottest.jl`: プロットのテストを行うファイルです．
- `.devcontainer`: Remote - Containersの設定ファイルやコンテナの作成に関連したファイルを置くフォルダです．
    - `devcontainer.json`: Remote -Containersの設定ファイルです．
    - `Dockerfile`: Dockerイメージの基となるファイルです． 
- `.tmp`: ソケットファイルが格納されるフォルダです．

これらのファイルについての詳しいことは，後ほど述べます．

## `Dockerfile`の用意

`Dockerfile`は，必要なアプリやパッケージと環境設定を記述したファイルです．このファイルを基にしてコンテナが作られます．今回は`Dockerfile`の中に，`Plots.jl`のインストールとホスト側でプロットの結果を表示するための設定を記述します．以下のような`Dockerfile`を用意します．

```Dockerfile:Dockerfile
# Get official docker image fo julia 
FROM julia

# set username as `root`
USER root

# update package list
RUN apt-get update

# install git and x11-apps for x11 test
RUN apt-get install -y git x11-apps

# Required package for GR backend of Plots
# https://gr-framework.org/julia.html#installation
RUN apt-get install -y libxt6 libxrender1 libxext6 libgl1-mesa-glx libqt5widgets5

# Install `Plots.jl and precompile it`
RUN julia -e 'using Pkg; Pkg.REPLMode.pkgstr("add Plots;precompile");using Plots'

# Configuration for x11 forwarding
ENV DISPLAY host.docker.internal:0.0
ENV XDG_RUNTIME_DIR /workspaces/Julia-programing/.tmp
ENV LIBGL_ALWAYS_INDIRECT 1
```

Dockerfileでポイントとなるのは次の部分です．

- `RUN apt-get install -y libxt6 libxrender1 libxext6 libgl1-mesa-glx libqt5widgets5`  
`Plots.jl`のバックエンド`GR`が依存するパッケージをインストールしておく．詳細は[ここ](https://gr-framework.org/julia.html#installation)を参照してください．
- `RUN julia -e 'using Pkg; Pkg.REPLMode.pkgstr("add Plots;precompile");using Plots'`  
`Plots.jl`をインストールします．パッケージモードを呼び出してインストールを行います．この時プリコンパイルも同時に行います．
- `ENV DISPLAY host.docker.internal:0.0`  
コンテナ側に接続先（ホスト側）のXサーバーを教えてあげる環境変数です．これによりコンテナ側がどのディスプレイを使えばよいのかがわかります．

`x11-apps`はXサーバーの動作確認のために入れます．gitはおまけで入れています．

## `devcontainer.json`の用意
`devcontainer.json`は，VS codeの拡張機能Remote-Containersを使ってコンテナを立ち上げてVS codeで開発する際の設定などを書いておくファイルです．以下のようなファイルを用意します．

```json-doc:devcontainer.json
{
    "name": "Julia dev container",

    // specify your workspace
    "context": "..",

    "dockerFile": "Dockerfile",

    "settings": { 
        // use bash as terminal
        "terminal.integrated.shell.linux": "/bin/bash",

        // set path of git
        "git.path": "/usr/bin/git"
    },

    // user name of container
    "remoteUser": "root",

    // specify vs code extentions you will use on remote
    "extensions": [
        "julialang.language-julia"
    ]
}
```

`extensions`の部分に，リモート環境で使いたいVS Codeの拡張機能を書いておくと，VS Codeでコンテナを開くときにインストールされます．今回はJuliaの拡張機能を追加します．ほかにリモート環境で使いたい拡張機能を書いておけば，インストールされます．このファイルに書く内容は，VS Codeで拡張機能のページを開いたとき，下の図の赤枠の部分に表示されます．

![スクリーンショット 2021-03-27 152052.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/598251/d1bd179d-e4ec-e0c6-69b3-845fab3ea5a3.jpeg)

## ホスト側にXサーバー（VcXsrv）をインストール

デフォルトではコンテナ側でGUIを使えないので，ホスト側にXサーバーを立ち上げて，コンテナ側のGUIを表示できるようにする必要がある．Xサーバーはリモート環境のGUIの表示を行うための機能を提供するソフトという認識で問題なさそうです．

今回は__VcXsev__というXサーバーを用います．[公式ページ](https://sourceforge.net/projects/vcxsrv/)からインストーラをダウンロードします．インストール時に特に弄る必要はなく，デフォルトで選択されているままでインストールすれば問題ありません．



# コンテナとXサーバーの起動

## VS codeでイメージをビルドしてコンテナの中に入る

VS codeで開発フォルダを開き，コマンドパレットを開きます．`Remote-Cointainers: Rebuild and Reopen in Container`を選択します．
![スクリーンショット 2021-03-27 165308.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/598251/1bc97155-f802-d97d-36c2-1a662711f650.jpeg)
VS Codeの左下の緑色のアイコンをクリックして`Reopen in Container`を選択しても良いです．
![スクリーンショット 2021-03-27 165458.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/598251/9505b82b-4af5-7cd8-478d-2a7ca6225ca3.jpeg)

これでイメージをビルドしてコンテナを立ち上げることが出来ます．ビルドにはそこそこ時間がかかります．ビルドが終わってコンテナが立ち上がると，VS Codeでコンテナ内にアクセスできます．開発フォルダがコンテナ環境で開かれて，ターミナルもコンテナ環境のものを使えます．拡張機能も使えるので，非常に開発しやすいです．


## Xサーバーを立ち上げる

インストールしたVcXsrvを起動して，コンテナからのGUI描画を受け付けられるようにします．スタートメニューから`XLaunch`を選択します．
![スクリーンショット 2021-03-27 155032.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/598251/21c7dd60-80b6-ce52-add3-3770ac668381.jpeg)

Display settingsの画面が出てきます．ここではMultiple Windowsを選択します．
![VcXsrv1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/598251/dad517c6-b55a-3b1c-a663-f20c5c3a684b.jpeg)

Client startupの画面が出てきます．ここではStart no clientを選択します．
![VcXsrv2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/598251/46724778-9715-12d7-a036-0099f63d834c.jpeg)

Extra settingsが出てきます．何も弄らなくても問題なく表示できます．色々検索してみると，Disable access controlにチェックを入れる例が多く出てきます．
![VcXsrv3.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/598251/d7c1f645-e461-1b58-c12c-1974c0bd457a.jpeg)
[この記事](https://unix.stackexchange.com/questions/120459/what-is-access-control-with-x-server)によれば，アクセスコントロールを無効にすると誰でもXサーバーにアクセスできてしまうようです．必要が無ければチェックを外しておいた方が良いのかもしれません．


![VcXsrv4.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/598251/593c53ca-0652-4035-696b-aa2d42d51b49.jpeg)
最後に完了を押せば，Xサーバーの準備は完了です．Xサーバーのインジゲーターがタスクバーに現れると思います．

# コンテナ環境からプロットGUIをホストに表示する

## Xサーバーの動作確認
コンテナの環境変数`DISPLAY`が適切に設定されて，ホスト側のXサーバーが正常に動作しているかを確認します．

`Dockerfile`で`x11-apps`をインストールしました．これはXアプリケーションを集めたものです．これらを実行して，コンテナ内からホスト側に表示できるかどうかをテストします．

コンテナ内で，コマンド`xeyes`を実行します．すると以下のような眼玉が現れます．この目玉はカーソルを追いかけるように動きます．これが確認出来たら，コンテナ内からGUIが表示できていることになります！

```terminal:
root@xxxxxxxxxx:# xeyes 
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/598251/e6663290-4292-532c-858f-904acc4fda48.png)

ちなみに`x11-apps`にはほかにも面白いXアプリケーションがあります．詳しくは[ここ](https://packages.debian.org/ja/sid/x11-apps)を参照してください．

## プロットの表示テスト

さて，Xサーバーの動作が確認出来たら，いよいよJuliaでプロットします．コンテナ内でコマンド`julia`を実行して，JuliaのREPLモードに入ります．以下のようにランダムな値を100個生成してプロットします．

```julia:
julia> using Plots

julia> plot(rand(100))
```
上手くいけば，以下のようにウィンドウが表示されてグラフが表示されるはずです．おめでとうございます！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/598251/e8643254-0fa1-46a6-a440-7cf6f603d2bd.png)

テストスクリプト`plottest.jl`を作って実行しても表示されます．ここでスクリプトでプロットを表示するためには関数`gui()`を忘れないようにしてください．プログラムが終了するとプロットが消えてしまうので`sleep()`を使っています．（詳細：[Plotドキュメンテーション](Plotshttps://docs.juliaplots.org/latest/basics/#Basic-Concepts)）

```julia:plottest.jl
using Plots
# plot
plot(rand(100))
# display plot
gui()

sleep(10)
```

# 参考資料

今回の記事の参考にさせていただいた資料です．先人の試みに感謝！

- [Dockerコンテナの中でGUIアプリケーションを起動させる](https://unskilled.site/docker%E3%82%B3%E3%83%B3%E3%83%86%E3%83%8A%E3%81%AE%E4%B8%AD%E3%81%A7gui%E3%82%A2%E3%83%97%E3%83%AA%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%82%92%E8%B5%B7%E5%8B%95%E3%81%95%E3%81%9B%E3%82%8B/)
- [Julia docker image with Plots support](https://discourse.julialang.org/t/julia-docker-image-with-plots-support/33112)
- [Docker for windowsのコンテナ内でGUIアプリを実行](https://fileszero.kimurak.net/2019-04-18-docker-and-gui-on-windows/)
- [WSLでJuliaを使う](https://qiita.com/qope/items/3f6a201765841f5fc9ab)
- [VSCode Remote Containerが良い](https://qiita.com/d0ne1s/items/d2649801c6f804019db7)
- [WindowsのDockerコンテナ上でmatplotlibを動作させる](https://qiita.com/miraijin/items/e29b7a60db6d181ffe35)