Linux
docker

dockerでデスクトップ環境(GUI)を構築する

はじめに

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

2018-03-26-003556_1366x768_scrot.png

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

CMDにデスクトップ環境やウィンドウマネージャを指定します。それと同時にfcitxのdaemonを起動したりfcitxの設定を変更するコマンドも指定します。

CMD fcitx-autostart > /dev/null 2>&1 && \
    fcitx-imlist -e fcitx-keyboard-jp > /dev/null 2>&1 && \
    fcitx-imlist -s fcitx-keyboard-jp,mozc,fcitx-keyboard-us > /dev/null 2>&1 && \
    jwm

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

#.jwmrc
<JWM>
  ...
  <StartupCommand>
    fcitx-autostart
    fcitx-imlist -e fcitx-keyboard-jp
    fcitx-imlist -s fcitx-keyboard-jp,mozc,fcitx-keyboard-us
  </StartupCommand>
  ...
</JWM>

原因は不明ですが一度変換キーを押さないと何故かIMEのON,OFFの変更ができないことが偶にあります。

ユーザの作成

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

デスクトップ環境,ウィンドウマネージャ

ホストではタイル型ウィンドウマネージャを使っています。使い慣れているので同じウィンドウマネージャを使おう考えましたが、X serverをネストしているのでキーバインドが重複します。なので、今回はスタック型ウィンドウマネージャのjwmを使用しています。一応、shiftとctrlを同時に押すとスコープを切り替えられます。しかし、一々shiftとctrlを押すのは億劫なので、キーバインドが被らないようにしています。gnomeやlxdeなどを使っていないのは余計なアプリケーションを可能な限り入れたくないからです。jwmはステータスバーが予め入っており、設定ファイルが分かりやすく、非常に軽量です。
画面をクリックするとmenuがでるのですが、そこにターミナルとブラウザを出せるようにする設定だけしています。

フォント

ターミナルで使うフォントをダウンロードしています。

RUN mkdir -p ./.local/share/fonts && \
    curl -L https://github.com/tonsky/FiraCode/raw/master/distr/ttf/{FiraCode-Regular.ttf} -o ./.local/share/fonts/#1 && \
    chown -R ${DOCKER_USER} ./.local && \
    fc-cache

DockerFile

FROM ubuntu:18.04

# 使っていない値を指定する
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 \
                   pulseaudio-utils \
                   fonts-ipafont-gothic \
                   dbus-x11 \
                   fcitx-mozc \
                   fcitx-imlist \
                   vim-gtk3 \
                   curl \
                   feh \
                   vlc \
                   mupdf \
                   ranger

# 音
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}

# google-chrome
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

# ターミナル、bash、ウィンドウマネージャの設定
COPY ./lxterminal.conf  ./.config/lxterminal/lxterminal.conf
COPY ./bashrc  ./.bashrc
COPY ./jwmrc  ./.jwmrc

RUN mkdir -p ./.local/share/fonts && \
    curl -L https://github.com/tonsky/FiraCode/raw/master/distr/ttf/{FiraCode-Regular.ttf} -o ./.local/share/fonts/#1 && \
    chown -R ${DOCKER_USER} ./.local && \
    fc-cache

# 所有者をrootから変更する
RUN chown -R ${DOCKER_USER} ./

USER ${DOCKER_USER}

CMD jwm

普通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
pulseaudio-utils
fonts-ipafont-gothic 日本語フォント
dbus-x11 Xクライアントの通信。日本語入力するのに必要。
fcitx-mozc 日本語入力
fcitx-imlist fcitxの設定をコマンドで行うために必要
vim-gtk3 エディタ
curl ファイルをダウンロードしたい
feh 画像viewer
vlc 動画player
mupdf pdf viewr
ranger cuiファイルマネージャ

作成・起動

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='パスワード' .

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

関数

毎回、長いコマンドを打ってubuntuを起動するのは面倒なので関数を作ります。

function jwm() {
  docker info > /dev/null 2>&1 || { echo 'Is the docker daemon running?' && return }
  [[ -e /tmp/.X11-unix/X1 ]] || Xephyr -wr -resizeable :1 &
  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
  pkill Xephyr
}