macOS で x86_64 な Linux を使う


はじめに

 こんにちは。DMS 開発部の吉村です。DMS 開発部ではニコニコ動画やニコニコ生放送の配信基盤を開発しており、動画のアップロードを受け付けたり、視聴に適したフォーマットに変換してユーザーの皆様にお届けしています。私自身はその中でもインフラ周りの構築を担当しています。

 普段は Apple Silicon で macOS を使用していますが、ソフトウェアを開発していると x86_64 (AMD64) のツール群が必要になる時があります。Docker でも x86_64 のバイナリを動かすことができますが、systemd も含めた一式が欲しい場合に高速な x86_64 の環境を用意する方法をご紹介していきます。

仕組み

 今回使用する構成としては次のようになっており、端的にいうと systemd-nspawn と Rosetta 2 というものを使って x86_64 のコンテナを動かします。コンテナという言葉から分かる通り、x86_64 の Linux Kernel は動いておらず、技術的には Docker と似たものになります。

  1. aarch64 (ARM64) macOS
  2. aarch64 VM
  3. aarch64 Linux
  4. systemd-nspawn + Rosetta 2
  5. x86_64 Distribution

 VM は lima を使用する場合と UTM を使用する場合で解説します。事前にインストールしておいてください。
 Linux は aarch64 のホストでは Ubuntu 24.04 を、x86_64 コンテナでは Ubuntu 22.04 を使用します。22.04 を利用する理由は、執筆時点で 24.04 だと journald や logind がクラッシュしてしまうためです。これについては原因が判明して解決すれば Ubuntu 24.04 にできますが、ここでは安全な方を使用します。古い環境の動作確認を行う場合、例えば x86_64 のディストリビューションに CentOS 7 を使用するといったことも可能ですが、CentOS 7 の深い知識が必要になります。
 systemd は Linux の管理を行うソフトウェアであり、systemd-nspawn は systemd が持つコンテナ機能です。
 Rosetta 2 は x86 の命令を ARM の命令に変換する仕組みで、macOS に備わっているものです。通常のソフトエミュレータに比べて非常に高速に動くところが特徴です。

事前準備

Rosetta 2

 今まで Rosetta を使ったことがない場合は次のコマンドでインストールしておきます:

(macOS)

1
softwareupdate --install-rosetta --agree-to-license

VM (lima の場合)

VM の設定を用意します:

ubuntu.yaml

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
vmType: "vz"
rosetta:
  enabled: true
  binfmt: true
arch: "aarch64"
cpus: 2
memory: "2GiB"
disk: "8GiB"
images:
- location:
    "https://cloud-images.ubuntu.com/releases/24.04/release/ubuntu-24.04-server-cloudimg-arm64.img"
  arch: "aarch64"

VM を起動して、シェルに繋ぎます:

(macOS)

1
2
limactl start ubuntu.yaml
limactl shell ubuntu

VM (UTM の場合)

 次の Linux ISO をあらかじめダウンロードしておきます。ただし執筆時点の UTM 4.6.4 では ISO サイズが小さな cloudimg 版が動作しない ため、Server 版の ISO を使用します:
https://cdimage.ubuntu.com/releases/24.04/release/ubuntu-24.04.2-live-server-arm64.iso

 UTM を起動し、メインウィンドウから「新規仮想マシンを作成」を実行します:

メインウィンドウ

 「仮想化」を選択します:

開始

 「Linux」を選択します:

オペレーティングシステム

 次の設定を入力します:

  • Apple 仮想化を使用
  • Rosetta を有効にする
  • 起動ISOイメージ
    • ubuntu-24.04-server-cloudimg-arm64.img
Linux

 あとはそのまま進めて「保存」を実行してください。保存が完了したら VM を起動して Ubuntu をインストールしておきます。注意点としては Apple 純正 JIS キーボードを使用している際にキーボードレイアウトに English 以外を選択すると _/ が入力できなくなるため、English でインストールしてください。
 インストールが完了したら VM 内で Rosetta を有効化します:

(aarch64 Ubuntu)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
## x86 バイナリを認識して Rosetta で動かすように設定する
sudo apt install binfmt-support

sudo mkdir -p /media/rosetta
sudo mount -t virtiofs rosetta /media/rosetta
echo 'rosetta	/media/rosetta	virtiofs	ro,nofail	0	0' | sudo tee -a /etc/fstab
sudo systemctl daemon-reload
sudo mount -a

sudo /usr/sbin/update-binfmts --install rosetta /media/rosetta/rosetta \
     --magic "\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x3e\x00" \
     --mask "\xff\xff\xff\xff\xff\xfe\xfe\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff" \
     --credentials yes --preserve no --fix-binary yes

 もし Ubuntu Server の minimal 版など、エディタが入っていないものをインストールした場合は、必要なエディタもインストールしておいてください。

Ubuntu の構築

 aarch64 版 Ubuntu の上に x86_64 版 Ubuntu を構築します。ついでに Ubuntu の中で Docker も使えるようにしておきます。
 要素としてはコンテナを実行する systemd-nwpawn と、Ubuntu を構築する debootstrap の2つです。systemd-nwpawn は systemd-container というパッケージで提供されています。debootstrap は Debian 系のディストリビューションの ISO やコンテナイメージを作成するために使用します。
 可変な部分としては Ubuntu のバージョンである jammy と、コンテナ名の ubuntu-x86 程度ですが、そのままでよければ基本的にはコピペで大丈夫です:

(aarch64 Ubuntu)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
sudo apt install systemd-container debootstrap docker.io

## x86_64 版 Ubuntu を /var/lib/machines/ubuntu-x86 の下にダウンロードする
sudo mkdir -p /var/lib/machines/ubuntu-x86
sudo debootstrap \
    --foreign \
    --arch=amd64 \
    --components=main,restricted,universe,multiverse \
    --include=systemd,dbus,docker.io \
    jammy \
    /var/lib/machines/ubuntu-x86
## debootstrap の処理中に /proc がアンマウントされてしまい Rosetta が使えなくなるので、スクリプトをパッチする
sudo sed -i -E \
    -e 's!^(\s*)(umount "\$TARGET/proc")!\1true # \2!g' \
    /var/lib/machines/ubuntu-x86/debootstrap/functions
## x86_64 の /proc は aarch64 の /proc をミラーリングする
sudo mount --bind /proc /var/lib/machines/ubuntu-x86/proc
## ダウンロードした Ubuntu をインストールする
sudo chroot /var/lib/machines/ubuntu-x86 /debootstrap/debootstrap --second-stage

 これで /var/lib/machines/ubuntu-x86 にコンテナが出来上がりましたが、パスワードがないとログインできないため root ユーザーのパスワードの設定とコンテナ内でネットワークと Docker が使えるように準備します。

(aarch64 Ubuntu)

1
2
## コンテナに直接入る
sudo systemd-nspawn -D /var/lib/machines/ubuntu-x86

 root ユーザーのパスワードを設定します。ここでは割愛しますが必要に応じて root 以外のユーザーを作成するのもこのタイミングです:

(x86_64 Ubuntu)

1
passwd

 ネットワークと Docker の準備を行ってコンテナから抜けます:

(x86_64 Ubuntu)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
## 標準的なパッケージのリポジトリを追加
cat - << EOF >> /etc/apt/sources.list
deb http://archive.ubuntu.com/ubuntu jammy-updates main restricted universe multiverse
deb http://archive.ubuntu.com/ubuntu jammy-backports main restricted universe multiverse
deb http://archive.ubuntu.com/ubuntu jammy-security main restricted universe multiverse
EOF
## systemd-resolved の無効化
systemctl disable systemd-resolved
rm -f /etc/resolv.conf
## ユーザーを docker グループに追加
usermod -aG docker $USER
exit

 コンテナが完成したため aarch64 版Ubuntu に戻ってきて systemd-nspawn の設定を行います。ここではネットワークは仮想化せずにホストネットワークをそのまま使います:

(aarch64 Ubuntu)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
sudo mkdir -p /etc/systemd/nspawn
sudo tee /etc/systemd/nspawn/ubuntu-x86.nspawn << EOF
[Network]
VirtualEthernet=no

[Files]
Bind=/var/run/docker.sock

[Exec]
PrivateUsers=false
EOF

x86_64 版 Ubuntu へのログインと終了

 前述の x86_64 版 Ubuntu の構築時にも扱いましたが、x86_64 版 Ubuntu を起動せずに次のコマンドで直接コンテナに入ることができます:

(aarch64 Ubuntu)

1
sudo systemd-nspawn -D /var/lib/machines/ubuntu-x86

これは chroot に近い使い方です。

 コンテナに直接入るのではなく systemd とその他サービス類を起動してログインシェルを経由するには、手動で起動する方法と、自動的に起動する方法があります:

(aarch64 Ubuntu)

1
2
3
4
5
## 手動で起動する方法
sudo machinectl start ubuntu-x86

## aarch64 版 Ubuntu の起動時に自動的に起動する方法
sudo machinectl enable ubuntu-x86

 コンテナを終了するには次のように行います:

(aarch64 Ubuntu)

1
sudo machinectl poweroff ubuntu-x86

 ログインシェルでのログインは次のように行います:

(aarch64 Ubuntu)

1
sudo machinectl login ubuntu-x86

ログインシェルを経由した場合は exit してもログインシェルに戻るだけなので、コンテナから抜け出すには control+] を3回押します。同様の説明は machinectl login の時にも書かれています:

Connected to machine ubuntu-x86. Press ^] three times within 1s to exit session.

 ログインシェルが煩わしい場合は直接ログインすることもできます。パスワードも不要です:

(aarch64 Ubuntu)

1
2
## ユーザーを作成した場合は user@ubuntu-x86
sudo machinectl shell ubuntu-x86

こうすると exit してもログインシェルに戻ることはありません。

Docker

x86_64 コンテナ内でも arm64 (aarch64) の Docker イメージが優先されます。x86_64 のイメージを優先するには ~/.bashrc などで DOCKER_DEFAULT_PLATFORM を指定する必要があります:

~/.bashrc (x86_64 Ubuntu)

1
export DOCKER_DEFAULT_PLATFORM=linux/amd64

おわりに

 Raspberry Pi や Apple Silicon の登場以降 ARM に対応したアプリケーションが増えて不便しなくなってきましたが、まだたまに x86 が必要になることがあります。Docker の中でも systemd を動かす方法はあるのですが、Docker はストレージのパフォーマンスの扱いが難しかったりネットワーク周りも考えることが増えるため、別の解として systemd-nspawn をご紹介しました。Rosetta を前提とした ISO やコンテナイメージがないことで構築手順が少々煩雑なので、将来公式のイメージが公開されることを願ってやみません。


株式会社ドワンゴでは、教育事業、ニコニコ事業、モバイル事業など様々なサービス、コンテンツを一緒につくるメンバーを募集しています。 ドワンゴに興味がある・または応募しようか迷っている方がいれば、気軽に応募してみてください。