Edited at

Dockerで開発用デスクトップ環境(GUI)を構築する


はじめに

今年からDockerを使い始めた未熟者です。


  • 運用をDockerコンテナで行うので、開発も似た環境で行った方が良いかな?

  • 開発時はGUIのツールを使いたい。

そんな単純な動機で、Dockerで開発用デスクトップ環境の構築を試みることにしました。


以下の順に試しました


(1) ホストにX Serverをインストールして、DockerコンテナからホストにXを飛ばす


結果


  • ホストがWindowsのとき(X ServerはMobaXterm):成功したが動きがやや重い。


  • ホストがMacのとき(X ServerはXQuartz):Xを飛ばせたが、PyCharmを操作中にフリーズした。


(2) VNC接続


結果

成功したが動きがやや重い。

ホストとのクリップボード経由のコピペは、


  • 「VNCデスクトップ→ホスト」は成功

  • 「ホスト→VNCデスクトップ」は失敗


(3) XRDP


結果

成功したので採用。 動きも一番軽い。

クリップボード経由のコピペは両方向OK。


XRDPの参考ページ(感謝します)

Dockerでuid/gid指定可能かつsudo使用可能なデスクトップ環境を構築する(XRDP編)

参考ページではコンテナ環境を毎回使い捨てますが、環境をいったん保存して再開したいこともあると思います。

例えば、sudoを使ってホームディレクトリ以外を変更するような作業中に、


  • リモートデスクトップの調子がおかしくなったのでdocker restartしたい。

  • 定時になったので、電源を落として翌日作業の続きを行いたい。

そこで、docker run後にdocker stopで停止しても、docker startで再開できるように実装してみました。


Dockerfile

基本的に参考ページと同じです。変更したのは冒頭の3箇所のみですので、Dockerfile全体は再喝しません。


(1) Ubuntuのバージョンを18.04に上げた

FROM ubuntu:18.04


(2) apt-getをaptに変更した

RUN apt update \

&& DEBIAN_FRONTEND=noninteractive apt install -y \


(3) apt installにsudoのインストールを追加した(Ubuntu 18.04では必要でした)

RUN apt update \

&& DEBIAN_FRONTEND=noninteractive apt install -y \
sudo \
lxde \


endpoint.sh

docker run時にユーザ作成をendpoint.shで行う点は参考ページと同じですが、docker start時は既存ユーザをそのまま使うように実装してみました。


endpoint.sh

#!/bin/bash -eu

USER_ID=$(id -u)
GROUP_ID=$(id -g)
PASSWD=${PASSWD:-${DEFAULT_PASSWD}}
USER=${USER:-${DEFAULT_USER}}

# グループを作成する
echo "GROUP_ID: $GROUP_ID"
if [ x"$GROUP_ID" != x"0" ]; then # rootグループでないとき
# gidが既にあるか調べて、なければグループ作成
grep ":${GROUP_ID}:" /etc/group || groupadd -g $GROUP_ID $USER
fi

# ユーザを作成する
echo "USER_ID: $USER_ID"
if [ x"$USER_ID" != x"0" ]; then # rootでないとき
export HOME=/home/$USER

# uidが既にあるか調べる
set +e
grep ":${USER_ID}:${GROUP_ID}:" /etc/passwd
RET=$?
set -e

# uidがなければ(= docker run)ユーザ作成、あれば(= docker start)何もしない
if [ $RET -ne 0 ]; then
# docker runするときに、ホストのディレクトリ等をコンテナのホームディレクトリに
# マウントさせると、コンテナ内に既に${HOME}ディレクトリが存在して、マウントした
# ボリュームが見えるため、useradd -m すると以下の警告が出る。
# useradd: warning: the home directory already exists.
# Not copying any file from skel directory into it.
# その場合、まだ${HOME}に/etc/skel配下のファイルがなければ、
# ${HOME}にコピーし、あればそのまま利用する。
useradd -d ${HOME} -m -s /bin/bash -u $USER_ID -g $GROUP_ID $USER
[ ! -e ${HOME}/.xsession ] && cp -r /etc/skel/. ${HOME}

# ログインパスワードを設定する
echo "PASSWD: $PASSWD"
echo ${USER}:${PASSWD} | sudo chpasswd
fi
else

# ログインユーザ名をrootにする
USER=$(whoami)
echo "USER: $USER"

# ログインパスワードを上書きする
echo "PASSWD: $PASSWD"
echo ${USER}:${PASSWD} | chpasswd
fi

# パーミッションを元に戻す
sudo chmod u-s /usr/sbin/useradd
sudo chmod u-s /usr/sbin/groupadd

[ ! -e ${HOME}/.xsession ] && cp /etc/skel/.xsession ${HOME}/.xsession

echo "#############################"
echo "USER: $USER"
echo "HOME: $HOME"

# XRDPサーバを起動する
# 下の1行がないと、docker start時にxrdp接続が失敗した
[ -e /var/run/xrdp/xrdp.pid ] && sudo rm /var/run/xrdp/xrdp.pid
# docker start時のxrdp接続で、下の行でstart:失敗、restart:成功
sudo bash -c "/etc/init.d/xrdp restart && tail -F /var/log/xrdp-sesman.log"



docker build

Dockerfileとendpoint.shがあるディレクトリで、以下を実行します。

docker build -t lxde_xrdp:ubuntu18.04_ja .


docker run

通常は、(rootでなく)一般ユーザのユーザIDを渡してコンテナを立ち上げます。docker runのオプションの意味は、参考ページをご参照ください。

# コンテナのホームディレクトリにマウントするホスト側のディレクトリを作成する。

mkdir $HOME/container_lxde_xrdp_$(whoami)_home

# コンテナ立ち上げ例
docker run -it \
--privileged \
--name lxde_xrdp_$(whoami) \
--mount type=bind,src=${HOME}/container_lxde_xrdp_$(whoami)_home,dst=/home/$(whoami) \
-p 3389:3389 \
-u $(id -u):$(id -g) \
-e USER=$(whoami) \
-e PASSWD=p@ss \
lxde_xrdp:ubuntu18.04_ja

Webアプリ開発用に例えば5000番ポートを開けるには、下記のようにdocker runします。

docker run -it \

--privileged \
--name lxde_xrdp_$(whoami) \
--mount type=bind,src=${HOME}/container_lxde_xrdp_$(whoami)_home,dst=/home/$(whoami) \
-p 3389:3389 \
-p 5000:5000 \
-u $(id -u):$(id -g) \
-e USER=$(whoami) \
-e PASSWD=p@ss \
lxde_xrdp:ubuntu18.04_ja

このとき、ブラウザからは以下のアドレスにアクセスします。

http://localhost:5000/


docker stop、docker start

コンテナ停止

docker stop lxde_xrdp_$(whoami)

停止したコンテナを再開

docker start -i lxde_xrdp_$(whoami)