HAProxy + Tailscale で公開する自宅 Minecraft サーバ
前フリ
お急ぎの場合: こちらへ
Minecraft のマルチプレイサーバを普通に公開しようとすると グローバルなIPv4アドレス + TCP/25565 ポート(場合によっては TCP/25575 ポートも)の開放 が必要となります。
グローバルIPがない回線での公開手段として思いつくのは Cloudfare Tunnel ですが、無料プランの場合、HTTP/HTTPS 以外の通信を行うにはクライアント側にも cloudflared をインストールしてもらう必要があります。
サーバ側だけで設定が完結する方法はないか考えていたところ、グローバルIPをもつサーバに HAProxy をインストールし、自宅内に立てた Minecraft サーバへ Tailscale 経由でプロキシするという方法を @yude_jp さんに教えていただきました。
ありがとうございます。ということでこの方式でサーバを公開できるようにしてみました。
なお、HA Proxy は任意の TCP パケットを転送できるプロキシサーバのソフトウェアです。
名前通り、本来はバックエンドサーバを複数設置して負荷分散するような用途を想定されているようですが、単純なプロキシサーバとしても使えるよう。
前提環境
- プロキシサーバ: グローバルな IPv4 アドレスをもつ Linux サーバ
- Ubuntu 20.04
- Docker をインストールしておく
- VPS とか AWS EC2 とかなんでもいいと思う
- 筆者は某 VPS の RAM 2GB/CPU 3core くらいのものを使っています
- Ubuntu 20.04
- Minecraft サーバ
- Ubuntu 22.04
- Docker をインストールしておく
- Minecraft Forge 1.21.1 サーバ
- バニラでも Spigot でもなんでもいけるはず
- Ubuntu 22.04
- ご家庭の光回線
実装
Tailscale のインストール
今回の構成では、プロキシサーバと Minecraft サーバは Tailscale が提供する Tailnet を通じて通信するため、プロキシサーバ・Minecraft サーバ両方に Tailscale を先にインストールしておきます。
Tailscale の設定方法は割愛します。公式ドキュメント等をご参照ください。
インストール&起動できたら、コンソールからプロキシサーバに Tailnet 内で割り当てられたIPアドレスを控えておきます。
プロキシサーバの構築
プロキシサーバを構築します。
今回は Docker Compose で HA Proxy を起動します。
HA Proxy は公式ドキュメントによると環境ごとに個別に Docker イメージをビルドする必要があるため、まず 以下のようなDockerfile を用意します。
なお、ファイル内のコメントで記載していますが、カーネルバージョンが古い場合 HAProxy の起動時に cannot bind socket (Permission denied)
というようなパーミッションエラーが出て起動しないことがあります。対処法としてコンテナが実行するバイナリファイルに cap_net_bind_service で well-known ポートをバインドできる権限を割り当てます。
FROM haproxy:lts
USER root
RUN apt-get update
# ここはデバッグ用のツールを入れているだけなので不要かも
RUN apt-get install wget curl iputils-ping net-tools -y
RUN apt-get install libcap2-bin -y
# カーネルバージョンが古い場合、以下の記述が必要な場合がある
RUN setcap 'cap_net_bind_service=+ep' /usr/local/sbin/haproxy
USER haproxy
COPY haproxy.cfg /usr/local/etc/haproxy/haproxy.cfg
次に haproxy.cfg を用意します。
以下の 192.0.2.1 がプロキシサーバ側の グローバルにアクセスできるIPアドレスとなり、198.51.100.1 がMinecraftサーバ側の Tailnet でのIPアドレスとなります。
この設定で、プロキシサーバの25565番ポートへアクセスする通信は backend で指定したサーバへ転送されます。
defaults
mode tcp
timeout connect 10s
timeout client 300s
timeout server 300s
frontend mc-proxy
mode tcp
bind 192.0.2.1:25565
default_backend mc-servers
backend mc-servers
mode tcp
server mc-server 198.51.100.1:25565 check
Docker イメージをビルドします。
$ docker build -t mc-haproxy .
つづいて以下のような Docker Compose ファイルを用意します。
services:
proxy:
image: mc-haproxy
network_mode: host
restart: always
Docker コンテナで稼働する HA Proxy を Tailnet に接続するため、network_mode を host に設定します。この記述を忘れると、コンテナがホスト上の Tailscale 用仮想ネットワークアダプタに接続できず通信ができませんのでご注意ください[1]。
コンテナを起動します。
$ docker compose up -d
Minecraft サーバの構築
普通に Minecraft サーバを起動すればOKです。
今回はこちらも Docker を使用しました。
services:
mc:
image: itzg/minecraft-server
tty: true
stdin_open: true
network_mode: host
env_file:
- ".env"
volumes:
- ./data:/data
user: "1000:1000"
プロキシサーバと同様、network_mode を host にして Tailnet へ接続できるようにしておく必要があります。
コンテナを起動します。
$ docker compose up -d
Minecraft のクライアントからプロキシサーバのグローバルIP(インターネットからアクセスできるもの: 今回は 192.0.2.1
)へアクセスして接続できるかテストします。
感想
いくつかハマりどころはあったものの簡単にインターネットに公開できて便利でした。
なお今回の構成ではセキュリティ面で100%安全になっていないので、適宜ホワイトリストや ACL をかけるなどセキュリティを考慮した設定を入れることをおすすめします。
参考資料
- Tailscale quickstart · Tailscale Docs
https://tailscale.com/kb/1017/install - Minecraft Server on Docker (Java Edition)
https://docker-minecraft-server.readthedocs.io/en/latest/ - HAproxy 2.4 cannot bind socket (Permission denied) [0.0.0.0:80] · Issue #170 · docker-library/haproxy
https://github.com/docker-library/haproxy/issues/170 - 80 番ポートで Permission Denied - Capability 設定
https://zenn.dev/robon/articles/2b3ac3d4a967ce
-
network_mode が host になっていると、コンテナはホストのネットワークアダプタに直接接続します。したがって、コンテナ起動時にリッスンするポート(今回は25565)がすでに使用されている場合うまく起動しないと思われます。 ↩︎
Discussion