dockerでデスクトップ環境(GUI)を構築してみる。

#はじめに
私はArch Linuxを使っているのですが、Ubuntuを触りたいときが稀にあります。Virtual BoxではOSのインストール作業、起動停止の遅さやメモリの圧迫などが気になります。なので、dockerでデスクトップ環境を構築する方法を模索しました。

![2018-12-16-161114_1366x768_scrot.png](https://qiita-image-store.s3.amazonaws.com/0/163210/380a2b94-09ae-c538-a26b-674979b2e1d3.png)


記事の中程に[Dockerfile](https://qiita.com/aaaat/items/fe3e6490a0aeceea5ddb#dockerfile)を載せています。

#Xephyr
dockerでGUIを使う方法はいくつかありますが、ホストがLinuxなので手軽にホストのX serverで描画します。Xephyr は X アプリケーションとして実行されるネストされた X serverです。
ubuntuなら`apt install xserver-xephyr`、arch linuxなら`pacman -S xorg-server-xephyr`でインストールできます。
`Xephyr -resizeable :1`で可変ウィンドウでディスプレイ番号1のX serverを起動します。
イメージ作成時あるいはコンテナ起動時に環境変数でディスプレイ番号を指定します。
`-e DISPLAY=:1` `ENV DISPLAY=:1`

#サウンド
音の出力はホストのpulse audioのサーバーを使います。コンテナ起動時にホストのソケットファイルを共有します。cookieによる認証を行っているのでそれも共有しておきます。
`-v /run/user/$UID/pulse/native:/tmp/pulse/native`
`-v $HOME/.config/pulse/cookie:/tmp/pulse/cookie`

環境変数でpathを指定します。

```
ENV PULSE_SERVER=unix:/tmp/pulse/native \
    PULSE_COOKIE=/tmp/pulse/cookie
```

#タイムゾーン
tzdataをインストール時にタイムゾーンを聞かれてビルドが止まるので、apt installする前に`export DEBIAN_FRONTEND=noninteractive`を実行しておきます。
`ENV TZ=Asia/Tokyo`で指定します。

#日本語入力
```
ENV GTK_IM_MODULE=fcitx \
    QT_IM_MODULE=fcitx \
    XMODIFIERS=@im=fcitx \
    DefalutIMModule=fcitx
```

デスクトップ環境やウィンドウマネージャの設定ファイルで、起動時に実行するコマンドを指定します。jwmの場合は下記です。

```xml:~/.jwmrc
<JWM>
  ...
  <StartupCommand>
    fcitx-autostart
    fcitx-imlist -e fcitx-keyboard-jp
    fcitx-imlist -s fcitx-keyboard-jp,mozc,fcitx-keyboard-us
  </StartupCommand>
  ...
</JWM>
```

#ユーザの作成
UIDはホストのUIDに合わせます。サウンド出力のときに使用するcookieを参照できるようにするためです。
`--build-arg DOCKER_UID=$(id -u)`

```Dockerfile
ARG DOCKER_USER=docker
ARG DOCKER_UID=1000
ARG DOCKER_PASSWORD=docker
RUN useradd -m --uid ${DOCKER_UID} --groups sudo --shell /bin/bash ${DOCKER_USER} \
      && echo ${DOCKER_USER}:${DOCKER_PASSWORD} | chpasswd
```

# デスクトップ環境
- デスクトップ環境と銘打っていますが、デスクトップ環境はインストールしません。jwmというスタック型ウィンドウマネージャをインストールします。jwmはタスクバーが予めあり、設定ファイルが分かりやすく、非常に軽量です。画面をクリックするとmenuが出ます。
- 注意点としてキーバインドが重複するとホストが優先されます。ctrlとshiftを同時に押すことで、キーバインドとマウスカーソルのスコープをjwm(ネストしているX server)だけに限定できます。


#Dockerfile
```Dockerfile
FROM ubuntu:latest

# 使っていない値を指定する
ENV DISPLAY=:1

# ミラーの変更
RUN sed -i 's@archive.ubuntu.com@ftp.jaist.ac.jp/pub/Linux@' \
      /etc/apt/sources.list

RUN export DEBIAN_FRONTEND=noninteractive \
    && apt update \
    && apt install -y x11-xserver-utils \
                      xinit \
                      tzdata \
                      language-pack-ja-base \
                      language-pack-ja \
                      sudo \
                      jwm \
                      lxterminal \
                      alsa-utils \
                      pulseaudio \
                      fonts-ipafont-gothic \
                      dbus-x11 \
                      fcitx-mozc \
                      fcitx-imlist \
                      vim-gtk3 \
                      libcurl4 \
                      epiphany-browser \
                      curl \
                      feh

RUN curl -O https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb \
      && apt install -y ./google-chrome-stable_current_amd64.deb \
      && rm google-chrome-stable_current_amd64.deb
# 音
ENV PULSE_SERVER=unix:/tmp/pulse/native \
    PULSE_COOKIE=/tmp/pulse/cookie

# 日本語
RUN locale-gen ja_JP.UTF-8
ENV LANG=ja_JP.UTF-8

# タイムゾーン
ENV TZ=Asia/Tokyo

# 日本語入力
ENV GTK_IM_MODULE=fcitx \
    QT_IM_MODULE=fcitx \
    XMODIFIERS=@im=fcitx \
    DefalutIMModule=fcitx

# docker内で使うユーザを作成する。
# ホストと同じUIDにする。
# ホストのpulseaudioのcookieを触るときに、permision deniedにならない。
ARG DOCKER_UID=1000
ARG DOCKER_USER=docker
ARG DOCKER_PASSWORD=docker
RUN useradd -m \
  --uid ${DOCKER_UID} --groups sudo --shell /bin/bash ${DOCKER_USER} \
  && echo ${DOCKER_USER}:${DOCKER_PASSWORD} | chpasswd

WORKDIR /home/${DOCKER_USER}

# jwm(window manager), lxterminal(terminal), bashの設定
RUN curl -L \
  https://raw.githubusercontent.com/atsuya0/dotfiles/master/etc/jwmrc \
  -o ./.jwmrc \
  && mkdir -p ./.config/lxterminal \
  && curl -L \
  https://raw.githubusercontent.com/atsuya0/dotfiles/master/terminal/lxterminal/lxterminal.conf \
  -o ./.config/lxterminal/lxterminal.conf \
  && curl -L \
  https://raw.githubusercontent.com/atsuya0/dotfiles/master/etc/bashrc \
  -o ./.bashrc

RUN chown -R ${DOCKER_USER} ./

USER ${DOCKER_USER}

CMD jwm
```

###インストールするパッケージ一覧
|パッケージ名 | 概要 |
|------------------|----------------------|
|x11-xserver-utils | X window system (GUI)|
|xinit | X window system (GUI)|
|tzdata | TimeZone|
|language-pack-ja-base | 日本語|
|language-pack-ja | 日本語|
|sudo |
|jwm | 軽量なスタック型 window manger|
|lxterminal | ターミナル|
|alsa-utils | 音|
|pulseaudio | 音|
|fonts-ipafont-gothic | 日本語フォント|
|dbus-x11 | Xクライアントの通信。日本語入力するのに必要。|
|fcitx-mozc | 日本語入力|
|fcitx-imlist | fcitxの設定をコマンドで行うために必要|
|vim-gtk3 | エディタ|
|libcurl4 | ブラウザの依存パッケージ|
|epiphany-browser | ブラウザ|
|curl | ファイルダウンロード用|

google-chromeをdebファイルからインストールしていますが、まともに動きません。

#作成・起動
argを指定しないと　uid=1000,user=docker,password=docker　になります。

```bash

docker build \
    -t ubuntu-jwm \
    --build-arg DOCKER_UID=$(id -u) \
    --build-arg DOCKER_USER='ユーザ名' \
    --build-arg DOCKER_PASSWORD='パスワード' .
```
環境変数DISPLAYを`:1`にした場合は、`Xephyr -resizeable :1`を実行後にdocker runを実行します。

```bash

docker run \
    -v /tmp/.X11-unix/:/tmp/.X11-unix/ \
    -v /run/user/$UID/pulse/native:/tmp/pulse/native \
    -v $HOME/.config/pulse/cookie:/tmp/pulse/cookie \
    -it --rm ubuntu-jwm
```

#dockerの起動を楽にする
毎回、コマンドを打ってdockerを起動するのは辛いので関数を書きます。

```bash

function jwm() {
  docker info &> /dev/null \
    || { echo 'Is the docker daemon running?'; return; }
  local -r display=1
  [[ -e "/tmp/.X11-unix/X${display}" ]] \
    || Xephyr -wr -resizeable ":${display}" &> /dev/null &
  docker run \
    -v /tmp/.X11-unix/:/tmp/.X11-unix/ \
    -v /run/user/$UID/pulse/native:/tmp/pulse/native \
    -v $HOME/.config/pulse/cookie:/tmp/pulse/cookie \
    -it --rm ubuntu-jwm &> /dev/null
  pkill Xephyr
}
```