🖥️

【OCI + Terraform】RocketChat構築でハマった「ポート3000に繋がらない」問題のトラブルシューティング手順

に公開

はじめに・・・

こんにちは!@maijun2です。
勤務先のブログに以下掲載した記事があります。

OCIクラウドのTerraformでインフラ自動化 | MyLearn無料枠とサブスクの違いも紹介

Terraform でインフラ作成の自動化という記事なのですが、実はちょっとトラブルがあって最初うまく出来ませんでした。

そのトラブルシューティングの手順について記事としてまとめてみます。

🧩 背景:やりたい事

OCI の学習環境は Terraform が使えるので、以下のような構成で使い捨てのチャットツールを構築しようと考え色々と弄くってました。

  • 使い捨てのチャットツールは、RocketChat
  • 利用 OS は OCI で提供されている Ubuntu
  • 上記、Ubuntu サーバー起動時に Cloud-Init にて snap 利用して RocketChatをインストール
  • Ubuntu サーバーはプライベートサブネット上に設置
  • パブリックサブネットにロードバランサーを設置
  • ロードバランサー経由でプライベートサブネットの Ubuntu サーバーへトラフィック流す
  • 上記構成をすべて Terraform で実装
  • 利用し終わったら使い捨てる

🛠 使用した技術・サービス

  • OCI(Oracle Cloud)
  • Terraform(インフラ構成管理)
  • Cloud-Init(初期構成スクリプト)
  • Snapパッケージ(RocketChatの導入)

🧱 ネットワーク構成イメージ

[インターネット]

[パブリックサブネット: 10.0.1.0/24]
     ↓  Port 80 (http)
[ロードバランサー]
     ↓ port 3000
[プライベートサブネット: 10.0.2.0/24]

[Ubuntu サーバー]

🚧 トラブル発生

Terraform にて apply して無事に各種リソースが構築されました。

しかし構築されたロードバランサー側からプライベートサブネットにある Ubuntu サーバーに繋がりません。

ロードバランサーのヘルスチェックでもエラーが出て正しくバックエンドの Ubuntu サーバーが使えない事がわかりました。

なぜなんだ?ということでトラブルシューティングをしていくことにしました。

🔎 調査①:OCI のネットワーク設定を確認

一通り OCI のネットワーク設定をチェックしてみました。

  • インターネットゲートウェイの設置と利用
  • サブネットやルート表の構成確認
  • セキュリティリストやネットワーク・セキュリティグループなどチェック
  • ロードバランサーや Ubuntu サーバーの設置サブネット確認

特に問題ない状況です。

🔎 調査②:パブリックサブネットのインスタンスで動作チェック

ロードバランサーと Ubuntu サーバー間の疎通が出来てないってことなので、ロードバランサーと同じ場所のパブリックサブネットに検証用のインスタンスを設置して動作チェックすることにしました。

curl http://10.0.2.221:3000
# => curl: (7) Failed to connect to 10.0.2.221 port 3000: No route to host

プライベートサブネットにある Ubuntu サーバーにやはりポート3000で繋がりません。

ちなみにポート3000で接続はできなかったので、ssh接続を試してみると・・

ssh opc@10.0.2.221
[opc@10.0.2.221 ~]$ whoami
opc
[opc@10.0.2.221 ~]$

あー!sshはつながるってことは、3000番ポートでの接続が出来ないのか!って判りました。

🔎 調査③:Ubuntu サーバー側の確認

とりあえずUbuntu サーバー側で3000番ポートで接続出来ないのが解ったので、今度はUbuntus サーバー側で各種確認をしていきます。

まずは RocketChat が正しく動いてポートもListenしているのかチェック

[opc@10.0.2.221 ~]$ sudo ss -tulnp
Proto State Recv-Q Send-Q Local Address:Port Peer Address:Port Process (PID)
udp UNCONN 0 0 127.0.0.53%lo:53 0.0.0.0:* systemd-resolve (2589)
udp UNCONN 0 0 10.0.2.40%ens3:68 0.0.0.0:* systemd-network (2584)
udp UNCONN 0 0 0.0.0.0:111 0.0.0.0:* rpcbind (583), systemd (1)
udp UNCONN 0 0 [::]:111 [::]:* rpcbind (583), systemd (1)
tcp LISTEN 0 4096 127.0.0.53%lo:53 0.0.0.0:* systemd-resolve (2589)
tcp LISTEN 0 4096 127.0.0.1:27017 0.0.0.0:* mongod (25460)
tcp LISTEN 0 511 0.0.0.0:3000 0.0.0.0:* node (25616)
tcp LISTEN 0 128 0.0.0.0:22 0.0.0.0:* sshd (1044)
tcp LISTEN 0 4096 0.0.0.0:111 0.0.0.0:* rpcbind (583), systemd (1)
tcp LISTEN 0 511 *:43935 : node (25616)
tcp LISTEN 0 128 [::]:22 [::]:* sshd (1044)
tcp LISTEN 0 4096 [::]:111 [::]:* rpcbind (583), systemd (1)

ちゃんと3000番ポートが待ち受けてました。
次にファイヤーウォールが有効になっているかチェックします。

Ubuntu では UFW(Uncomplicated Firewall)機能があるので、状況を確認します

[opc@10.0.2.221 ~]$ sudo ufw status
Status: inactive

無効になってました!
ファイアーウォール機能は無効になっているのに、ポート3000番が繋がらない状況ですね。

🔎 調査④:Ubuntu サーバー側で tcpdump してみる

繋がらない状況を深堀り調査するために、今度は Ubuntu サーバー側でパケット内容の精査をすることにしました。 tcpdump 実行して様子を確認していきます。

[opc@10.0.2.221 ~]$ sudo tcpdump -i any port 3000

あとは検証用のインスタンスから curl コマンドでポート番号3000を指定して接続テストをしてみると以下図の結果が出てきました。

tpcdump の内容を確認すると、読み取り結果として、各行に Flags [S](SYN フラグ) → 検証用のインスタンス(10.0.1.x)が TCP 接続を開始しようとしている

送信元:10.0.1.x
宛先:Ubuntu サーバー 10.0.2.221 のポート 3000

しかし送信元から宛先へすべて一方向(SYN のみ)、宛先から送信元への戻りの SYN-ACK の応答がないことが判りました。

受け付けてるのに応答が無いってことは、応答が許可されてないってパターンですね。

🔎 調査⑤:Ubuntu サーバー側の iptables 設定を確認する

UFW 以外に Ubuntu では iptables でパケットを DROP している場合もあります。
それで iptables の設定を確認してみました。

[opc@10.0.2.221 ~]$ sudo iptables -L -n -v

あーなるほど。
tcp dpt:22(SSH)は ACCEPT してるが、他は REJECT してるのが判りました。

2364 143K REJECT all -- * * 0.0.0.0/0 0.0.0.0/0 reject-with icmp-host-prohibited

✅ 解決方法:iptablesで3000番ポートを明示的に許可

ssh だけ明示的に許可されているのに、他のポートはすべて拒否られているというオチでしたので、なら3000番ポートも許可してあげれば OK ですね。

[opc@10.0.2.221 ~]$ sudo iptables -I INPUT -p tcp --dport 3000 -j ACCEPT
[opc@10.0.2.221 ~]$ sudo apt install -y iptables-persistent
[opc@10.0.2.221 ~]$ sudo netfilter-persistent save

パブリックサブネットに設置した検証用のインスタンスから curl 実施して無事に RocketChat も表示されるようになりました。

🧪 Cloud-Init に iptables 設定を組み込む

今回は Terraform でインフラ構成を自動作成しています。
上記の iptables の設定もcloud_init.tpl へ以下のように設定しました。

#cloud-config
package_update: true
package_upgrade: true
packages:
  - snapd

runcmd:
  - sleep 30
  - snap wait system seed.loaded
  - snap install core
  - snap refresh core
  - snap install rocketchat-server
  - iptables -I INPUT -p tcp --dport 3000 -j ACCEPT
  - DEBIAN_FRONTEND=noninteractive apt-get install -y iptables-persistent
  - netfilter-persistent save
  - systemctl enable snap.rocketchat-server.rocketchat-server.service
  - systemctl start snap.rocketchat-server.rocketchat-server.service

一応、 Terraform テンプレも一式以下の GitHub で公開しております。
https://github.com/maijun2/oci-with-rocketchat

🎯 学びとまとめ

Terraform や CloudFormation など IaC ツールでクラウド側にインフラ作成するのはサクッと出来ますが、その後の疎通がうまくいかないって結構あるかもしれません。

その時には・・

  • クラウド側のセキュリティ設定や経路情報だけではなく、OS内部の設定も忘れず確認する!
  • tcpdump でパケットの到達は見えるが、応答がない場合はアプリ/OS側のブロックを疑う!
  • Ubuntu では ufw を使っていなくても iptables に強力な REJECT all ルールがあることがある
  • トラブルシューティングする際には、ひとつずつ要素を潰しながら原因を特定していく

このあたり意識すると良いかもしれません。

🙌 最後に

OCI × Terraform × Cloud-Init × Snap を使って RocketChat を構築する流れ自体は非常にスムーズでした。

でも「ポート開いてるはずなのに繋がらない...」とハマったのは久々で、結構懐かしい感じがしました。ちょうどいい機会なのでトラブルシューティングしながらブログ記事にまとめてましたw

この記事が、今後どなたかがトラブルシューティングする際に役立てれば幸いです!

Discussion