X11を用いたLinux用バイナリファイルをDocker for Macを用いてMac上で楽に動かせるようにする


はじめに

発端は友人から大学院の授業で使う環境の構築で困ったから手伝ってくれって言われた事でした。
見てみると、ubuntu上で教授が用意したバイナリファイルを実行できるようにできれば良さそうでした。
Windowsは環境構築の方法が解説されて、Macは自力で頑張ってと言われたそうです😇
Dockerを使えば実現できそうなのでやってみました。

リポジトリ

今回使用したバイナリファイルは2次配布が禁止であった為リポジトリには含めていません。各自、実行したいバイナリファイルを用意してください。

環境

macOS Catalina: 10.15.4
Docker: 19.03.8
docker-compose 1.25.5
XQuartz 2.7.11 (xorg-server 1.18.4)

X11クライアントを各自変えてあげればmacOS以外でも動作すると思います。

ゴール

下記のコマンドで簡単に実行できる様にする。

$ make up
$ make #{実行したいバイナリファイル名}

手順

0. XQuartzを導入する

macにXQuartzを未導入の場合は導入してください。
XQuartzを用いる事でMacでX11アプリケーションを動作させる事ができる様になります。
brewを導入済みの場合は下記コマンドでXQuartzを導入できると思います。

$ brew cask install xquartz

導入後は再起動かユーザーのログアウトを行わないとXQuartzの設定の反映が行われないので注意してください。

1. XQuartzのネットワーク・クライアントからの接続を許可する

XQuartzを起動できる様にできたら、XQuartzを起動し、環境設定から「ネットワーク・クライアントからの接続を許可」にチェックを入れ、XQuartzを再起動してください。

2. Dockerfileを記述する

バイナリファイルを実行する環境のDockerfileを記述しています。
実行したいバイナリファイルによって各自変更してください。
x11関連のライブラリをapt-get installする際にタイムゾーンの設定等出てきてしまってビルドが進まなくなってしまった為、タイムゾーンを設定し、ENV DEBIAN_FRONTEND noninteractiveにて他の選択項目が出てきてしまっても進む様にしています。
今回はcommandsディレクトリにバイナリファイル(複数可)を配置する事を想定しています。

Dockerfile

FROM ubuntu:20.04

WORKDIR usr/workdir

# TZを設定する
ENV TZ Asia/Tokyo
RUN apt-get update \
    && apt-get install -y tzdata \
    && rm -rf /var/lib/apt/lists/* \
    && echo "${TZ}" > /etc/timezone \
    && rm /etc/localtime \
    && ln -s /usr/share/zoneinfo/Asia/Tokyo /etc/localtime \
    && dpkg-reconfigure -f noninteractive tzdata

# 必要なライブラリのインストール
ENV DEBIAN_FRONTEND noninteractive
RUN apt-get update \
    && apt-get install build-essential x11-apps x11-utils x11-xserver-utils dbus-x11 -y \
    && apt-get install gedit -y \
    && apt-get install libgfortran4 -y

COPY commands/ ./commands
RUN chmod +x ./commands
ENV PATH /usr/workdir/commands:$PATH

この時点で実行できるといえばできるはずです。
参考:Docker for Mac で X11 アプリケーションを動かす

毎回長いコマンド打つのはめんどくさい、、、という気持ちの方は次の手順へ進んで下さい。

3. docker-coomposeを記述する

コンテナ側にホスト側のX11クライアントの情報を渡してあげる必要があり、コマンドを毎回打つのが面倒な為記述しました。
また、ホスト側srcディレクトリをコンテナ側/usr/workdir/srcディレクトリにマウントする様に記述しています。

docker-compose.yml
version: '3.7'
services:
  app:
    build: .
    volumes:
      - ./src:/usr/workdir/src
    env_file:
      - .env
    tty: true
    stdin_open: true

docker-compose.ymlから参照している.envは下記です。
この環境変数DISPLAYをもとにコンテナ内からXQuartzへアクセスしにいきます。

.env
DISPLAY=host.docker.internal:0

こちらの記事を参考に記述致しました。
host.docker.internalの実態としてはHost(Mac)側のhostnameを取得してくるそうです。
ここら辺の名前解決はDockerのバージョンによって推奨が違う為確認してください。

4. Makefileを記述する

毎回docker-composeコマンドを記述するのが面倒な為、Makefileを定義してあげてmakeコマンドで実行できる様にしました。
基本的にはdocker-composeをmakeで実行できるようにしています。
ポイントとしては、upで実行する際に自動的にXquartzを起動し、xhost +$(hostname)にてホスト(Mac)のhostnameでXquartzへアクセスできる様にしてコンテナ側との整合性を合わせています。
また、xhostでのアクセスはセキュリティリスクもある為気になる方は他の方法で認証してください。

Makefile
build:
    docker-compose build --no-cache

up:
    open -a Xquartz
    xhost +$(hostname)
    docker-compose up -d

down:
    docker-compose down

restart:
    make down
    make up

destroy:
    docker-compose down --rmi all --volumes

destroy-volumes:
    docker-compose down --volumes

ps:
    docker-compose ps

bash:
    docker-compose exec app bash

xeyes:
    docker-compose exec app bash -c 'xeyes'

#{実行したいバイナリファイル名}:
    docker-compose exec app bash -c '#{実行したいバイナリファイル名}'

4. 実行する

下記コマンドでdockerイメージをビルドします

$ make build

buildに成功すれば下記コマンドでdockerコンテナを起動できるはずです。

$ make up

起動できたら、コンテナ側からホスト(Mac)のXQuartzへアクセスできるかxeyesを用いてテストしてみます

$ make xeyes

このような目玉が表示されればコンテナ側からホスト(Mac)のXQuartzへアクセスできています。

後は実行したいバイナリファイルを各自配置し

$ make #{実行したいバイナリファイル名}

上記コマンドを下記の様に実行してください

$ make binary

また、下記コマンドを用いてコンテナへ入ることもできます。

$ make bash

まとめ

Dockerを用いればX11を用いたLinux用バイナリファイルもMac上のプログラムを動かすかの様に簡単に動作させる事ができた!