「接続できないって言われたんだけど？」「😱😱😱」

この記事は「本番環境でやらかしちゃった人 Advent Calendar 2022」3日目の記事になります。

# はじめに(スライディング土下座🙇‍♂️)
期待させて申し訳ありませんが、本記事は利用者最大7名程度の本番環境の記事になります。非常に小規模なゆえ、あまり期待せずにご覧ください。（カレンダーが寂しかったのでつい参加したくなりました。過去の枠なので許してください🙇‍♂️）
とはいえ、技術的には変なことをしているので（？）最後まで楽しんでいただけたら幸いです。

# とある夏の夜
「停電対策にUPS買っちゃった！」
「よし、諸々のサーバの電源移動しちゃえ！」
「Proxmoxで仮想基盤組んであるからVMも無停止で移動できて便利！」

「おや、誰かマイクラで遊んでいるな（←知り合いと遊ぶためのサーバをVMに立てています）」
「まあ、無停止で作業できるから大丈夫でしょう！」

この時はここで盛大なフラグを作ったことを知る由もありませんでした。

# やらかし
「さて、とりあえずサーバの電源は移動できたし、ルータの電源も移すか。」
「ここを抜くとインターネットの通信が切れるけど、マイクラのVMは別経由で通信しているから大丈夫でしょう。」

ブチっ（※ラズパイなのでちゃんとシャットダウンしています。）

〜　1時間後　〜

「そういえばさっき知り合いが遊んでいたけどまだ遊んでいるかな？」
知り合い「接続できないって言われたんだけど？」
「😱😱😱」

慌ててルータを立ち上げ直し、とりあえず復旧しました。

# 何が起きてしまったのか
ルータを停止してから通信ができなくなってしまいました。
でも後から見返してみても通信ができそうな構成でした。
ではなぜこのようなことが起きてしまったのでしょうか。

詳しい説明をする前にまずはこのVMが接続されていたネットワークについての説明が必要になります。

## ネットワーク図
かなり大雑把ですがこんな感じです。

![Network.drawio-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155364/2fbe70a5-ba08-4e0a-68a4-fce95054f9f5.png)

一番左のサーバが今回通信ができなくなったマイクラのVMです。
2つのネットワークにつながっているのは管理用と外部公開用でネットワークが分かれているためです。
一般の御家庭ですのでインターネット回線を契約した時に自動的にルータがついてきますが、上のルータがそれになります。
では下のルータは何かというと、ラズパイで構築したルータになります。これは単純に面白いからと他にも複数のネットワークがあるから使用されています。

このサーバにログインするときは次の図のような経路で入っていきます。

![Network.drawio-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155364/f430c04b-03d5-36bd-0d6a-0abcaea9fb56.png)

上のルータでは外部からの通信をDestination NATをかけることで直接サーバに流しています。
**デフォルトゲートウェイは下のルータになっています**が、ユーザは上のルータ経由で接続してくるため下のルータがなくなっても問題にはなりません。
（厳密にはサーバにログインする際はユーザの身元確認のため、下のルータ経由で通信をしますが既にログインしている場合は問題にならないはずです。）

## ルーティング
上記の説明では一見問題ないように見えますね。しかし、よくよくルーティングを考えると重大な見落としがあることに気づきます。

先ほど「デフォルトゲートウェイは下のルータ」と述べました。
デフォルトゲートウェイというのは宛先のIPアドレスがルーティングテーブルにない場合、つまりどこに流せばいいかわからない場合に送りつける端末（ルータ）のことになります。
通常、ルーティングテーブルには同じネットワークのルートしかありません。例えば「192.168.1.0/24」の宛先はeth0経由で、「192.168.2.0/24」の宛先はeth1経由で直接アクセスしに行くと言ったものです。
もし他のネットワークの人と通信がしたい場合は外に出て行ける人に任せる必要があります。その他の人というのがルータであり、とりあえずこの人に任せればOKという人をデフォルトゲートウェイに指定します。

話が脱線しましたが、要はインターネットに出ていく通信は全てデフォルトゲートウェイに行きます。
上のルータから入ってこようが、下のルータから入ってこようが、インターネットに向かって返事を返す時は下のルータを通っていくことになります。

## 原因解明
上記を踏まえて、正しい通信経路を描くと次の図のようになります。

![Network.drawio-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155364/0fb38d1f-dd3d-25be-14b5-3493f50babc4.png)

行きと帰りで通信経路が違いますね。これは非対称ルーティングと呼ばれ、Firewallなどでは弾かれることがあります。
今回は奇跡的にこの経路でも問題なく通信ができていたため、非対称ルーティングになっていることに気づきませんでした。
非対称ルーティングになっていることに気づかないまま下のルータを止めてしまったため、帰りの通信ができなくなり通信が切れてしまった、というのか今回の障害の原因です。

<details><summary>どうしてうまく通信できていたのか</summary>

ユーザのIPアドレスをaa.bb.cc.ddとし、サーバのIPアドレスを192.168.1.2とすると
```
受信：aa.bb.cc.dd -> 192.168.1.2
送信：192.168.1.2 -> aa.bb.cc.dd
```
となります。
下のルータでは送信元が192.168.2.0/24に対してはNATをかけますが、それ以外のパケットは特に何もしません。
（本来はFWで弾くのが正解な気がしますが、上位のルータでやってくれるのでとりあえず…）
そして（下のルータは上のルータにもつながっており）192.168.1.0/24の経路は知っているため、そのままパケットを転送します。

上記の理由により、偶然うまく通信ができていたものと考えられます。
</details>

# 解決策
さて、この状態をどう解決すればいいのか。

eth0で来た通信はeth0に、eth1で来た通信はeth1に返したい…
Firewallで対応すればいいのか、サーバアプリケーションで何か指定ができるのか…

と悩んでいましたが、Linuxには既にその答えが用意されていました。
それがPolicy Based Routingです。

Policy Based Routingとはパケットの条件によってルーティング先を変えるというものです。
私もあまり詳しくはないので他の方の記事に任せます。
https://qiita.com/fururun02/items/69d1ddac298b4ce48269
https://synaptica.info/en/2021/06/16/ubuntu-20-04-gestire-instradamento-di-piu-gateway-e-interfacce-da-una-singola-vm/

今回は、帰りの通信で送信元のIPアドレスが上のルータのネットワーク(192.168.1.0/24)だった場合、上のルータにルーティングするように設定しました。
これにより、非対称ルーティングの状態を解消することができました。

# 惨劇はなぜおこってしまったのか
説明が随分と長くなってしまいましたが、今回のやらかしは次の3点が要因だと個人的に考えます。

## 無駄に複雑なネットワーク構成
初めは自分がコントロールできて、かつシンプルなネットワークを構成していました。
しかし、あれもしたい、これもしたいと拡張したり、また、回線の環境が変わったりした結果、さまざまなネットワークが入り混じる構成になってしまいました。
また、VMを2つのネットワークに接続させていたことも無駄に複雑になった要因になりました。

## 慣れからくる自信や注意不足
慣れほど怖いものはありません。
VMを無停止でサーバやネットワークのメンテナンスができるようになり、メンテナンスがやりやすくなった結果、そう言ったところに注意が行かなくなったのかもしれません。
あとは、最悪止まっても数人にしか影響がないというのも注意不足の大きな要因です。

## ネットワークに関する知識、感覚の欠如
今回、非対称ルーティングになっていることに気づけませんでしたが、ネットワークの構造についての知識や感覚があればすぐに気づけたことです。とはいえ、こればかりは経験を積まないことにはどうしようもないというのが本音です。
（そもそも今回のようなケースって聞いたことがなかったけど、よくあることなのかな…？）
今回の一件で新たな知見や感覚の習得になりました。

# 惨劇を起こさないためにできること
要因の洗い出しが若干雑ですが、これを元に考えられる対策は次の3点かと思います。

## できる限りシンプルにする
KISS(Keep it simple stupid)の原則というものがありますが、この場合も同じことが言えると思います。
VMのネットワークを1つだけにする、SNATも併用することで考えないといけないことをシンプルにするといったことが考えられます。

## 慎重な計画や作業を行う
当たり前ですが、重要なことです。今回は通信が切れる可能性を想定し、事前にアナウンスをしておく、もしくはユーザがいる場合は作業を行わないと言った慎重な対応をすることができたと考えられます。
あとは何らかの監視体制を敷いていれば異変にもすぐに気づけたと思います。今回は1時間近くも通信ができない状態だったことに気づくことができませんでした。
いつも同じことをしているとはいえ、不測の事態が発生することを考慮して計画、作業を行うということは大事なことだと思います。

## 経験を積む
そのために自宅サーバをやっています（笑）
色々試すことで得た知見は最も身につき、役に立つものになると思います。
あとは他の人の経験談を聞くというのもわかりやすいかもしれません。
（そういえばちょうどおすすめなものがあります。[本番環境でやらかしちゃった人 Advent Calendar](https://qiita.com/advent-calendar/2022/yarakashi-production)です！！！）

# あとがき
今回は思いつきでAdvent Calendarの記事を書いてみました。
本番環境と言えないような、小規模なものでしたが、なかなか原因や解決策が面白かったので思い切って書いてみました。
（勢いに任せて眠い中書いていたので、お見苦しいところも多々あるかもしれませんが…）
こんな拙作ですが、楽しんでいただけたら幸いです。

# 余談
ちなみにこの障害を起こしたのは夏でしたが、実際に原因が判明したのはつい最近です。
というのも、最近新しく2本目の回線を引き（！？）、サーバ周りの設定を行っていたのですが、その際にうまく接続ができないという現象が発生し、色々調査した結果、
受信は「新回線→サーバ」で、送信は「サーバ→旧回線」とまさに非対称ルーティングが行われていました。
今回は受信と送信とでIPアドレスが変わってしまうため通信がうまく行かずに気づくことができました。
やっぱり色々試すといい勉強になりますねぇ。