はじめに
私はArch Linuxを使っているのですが、Ubuntuを触りたいときが稀にあります。Virtual BoxではOSのインストール作業、起動停止の遅さやメモリの圧迫などが気になります。なので、dockerでデスクトップ環境を構築する方法を模索しました。
記事の中程に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の場合は下記です。
<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)
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
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 になります。
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を実行します。
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を起動するのは辛いので関数を書きます。
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
}
