Zenn
🪨

HAProxy + Tailscale で公開する自宅 Minecraft サーバ

2025/02/10に公開
1

前フリ

お急ぎの場合: こちら
Minecraft のマルチプレイサーバを普通に公開しようとすると グローバルなIPv4アドレス + TCP/25565 ポート(場合によっては TCP/25575 ポートも)の開放 が必要となります。
グローバルIPがない回線での公開手段として思いつくのは Cloudfare Tunnel ですが、無料プランの場合、HTTP/HTTPS 以外の通信を行うにはクライアント側にも cloudflared をインストールしてもらう必要があります。
サーバ側だけで設定が完結する方法はないか考えていたところ、グローバルIPをもつサーバに HAProxy をインストールし、自宅内に立てた Minecraft サーバへ Tailscale 経由でプロキシするという方法を @yude_jp さんに教えていただきました。

https://x.com/yude_jp/status/1885749975847665713

ありがとうございます。ということでこの方式でサーバを公開できるようにしてみました。

なお、HA Proxy は任意の TCP パケットを転送できるプロキシサーバのソフトウェアです。
名前通り、本来はバックエンドサーバを複数設置して負荷分散するような用途を想定されているようですが、単純なプロキシサーバとしても使えるよう。

前提環境

  • プロキシサーバ: グローバルな IPv4 アドレスをもつ Linux サーバ
    • Ubuntu 20.04
      • Docker をインストールしておく
    • VPS とか AWS EC2 とかなんでもいいと思う
    • 筆者は某 VPS の RAM 2GB/CPU 3core くらいのものを使っています
  • Minecraft サーバ
    • Ubuntu 22.04
      • Docker をインストールしておく
    • Minecraft Forge 1.21.1 サーバ
      • バニラでも Spigot でもなんでもいけるはず
  • ご家庭の光回線

実装

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 ファイルを用意します。

compose.yaml
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 を使用しました。

compose.yaml
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 をかけるなどセキュリティを考慮した設定を入れることをおすすめします。

参考資料

脚注
  1. network_mode が host になっていると、コンテナはホストのネットワークアダプタに直接接続します。したがって、コンテナ起動時にリッスンするポート(今回は25565)がすでに使用されている場合うまく起動しないと思われます。 ↩︎

1

Discussion

ログインするとコメントできます