Tencent Cloudで実現するIP AnyCastアーキテクチャ（をしたかった）

:::note alert
この記事は構築が**完走していません。**
というのも、AnyCast IPの利用にはSalesを通じた事前機能開放が必要でタイムアップとなり、AnyCastを利用した構築ができませんでした。
ただ、Tencent Cloudを利用したサーバ構築チュートリアルとしてはある程度使える内容と思うため、公開します。
AnyCastの性能をみたかったみなさん、申し訳ありません。
もし、サーバ構築チュートリアルでよいというみなさんはこの先もお読みいただければと思います。
:::


アドベントカレンダーにて投稿した記事内で、Tencent CloudにAnycast IPアドレスを実現するサービスがあることを知りました。あの後再度調べてみて、確かに他のクラウドにもIP Anycastを実現する機能はあるようなのですが、

 * 多人数参加するオンラインゲーム：データをキャッシュする量が少ないため単にCDNでは向かない。（というか後述のビデオチャットと同じくTURNサーバが必要なことも多い）
 * ビデオチャットなど双方向通話：同じくキャッシュ量が少なく、さらにエンタープライズレベルでのモデレーションなどを利用しようとすると、TURNやSFUサーバが必要となる


など、Tencentが有力となるユースケースではIP Anycastを組みたい場合が多いように感じます。
そこで、
②あの有名なWebサービスをTencent Cloud使って作るならどんなアーキテクチャ？
のテーマとして、やや反則気味ですが、IP Anycastの最たる（Web?)サービスである**DNS**をもとにアーキテクチャを考えて、軽く構築して ~~みます。~~ みたかった記事です。



# まずはTencent Cloudアカウントの登録
ここは住所、クレジットカード情報などなどお見せできな部分が非常に多いので細かいスクリーンショットは載せないのですが、初期登録後、一点大事な点があるので、、、

**MFA登録しましょう**

初期登録完了後、右上のユーザアイコンからセキュリティ設定へ移動して
![スクリーンショット 2023-12-23 11.26.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244678/bdb78097-3264-5242-eac4-c31c41325327.png)

SecuritySettingの最上段、MFA Deviceカラム右側の"bind"ボタンをクリックすることでMFAの登録に進むことができます。（私はすでに登録済みのためunbind表示になっていますが、未登録の場合はbindと書かれているはずです）
![スクリーンショット 2023-12-23 11.28.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244678/8980a4c3-be20-38c9-f77e-d02c85da67aa.png)

基本的に画面通りに入力すれば良いのですが、個人的には最後の選択肢で「Login Protection」にもチェックを入れておくのをお勧めします。

![スクリーンショット 2023-12-23 11.27.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244678/a21fc8e1-bb82-1f84-e10a-141857cca945.png)

ここまででMFA Deviceの登録は完了です。



# IP AnyCastなしでのDNSアクセス
Tencent Cloudには簡単にサーバを建てられるTencent Cloud Lighthouseというものがあるのですが、今回はDNS検証のためにbindを設定したり、IP AnyCastの検証のためにネットワーク設定をカスタマイズする必要があるため、VPCとCVM（Cloud Virtual Machine）で構成します。


構成図的にはかなり簡単で以下のような形です。
インターネットを経由してアクセスしているため、東京〜バージニアの接続でレイテンシなどが多く発生します。（IXなどの経由の関係などなどで迂回やホップが通常多く発生します）
![no_anycast.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244678/f9fc8408-b67e-34d3-f7b0-7dc73819f612.png)


## VPCの作成
まずは東京リージョンにVPCを作成します。
VPCコンソールに移動して、左上のドロップダウンからリージョンを選択します。
その後CreateからVPCを作成します。東京リージョンのVPCは10.0.0.0/8系のCIDRで作成します。

![スクリーンショット 2023-12-23 16.59.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244678/48c9e316-7d11-7920-d7b1-2bbb60576cb0.png)
![スクリーンショット 2023-12-23 17.01.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244678/c374355e-3c9d-53e1-90ad-610bace93ba3.png)


同じ手順でリージョンを変えてもう一つのVPCを作成します。
もう一つのVPCは IP AnyCastの効果がわかりやすいように東京から遠いリージョンとして、EastUS（バージニア）リージョンを選択しておきます。こちらは今回の記事上区別がしやすいように172.16.0.0/16系のCIDRで作成しておきます。

## SecurityGroupの作成
クラウドお馴染みのリソースごとのファイアウォールであるSecurityGroupを作っておきます。
まずは、東京リージョンにSecurityGroupを作ります。
東京リージョンはDNSリクエストを出す側として利用しようと思っているので、DNSアウトバウンドとパッケージダウンロード用のHTTP,HTTPSアウトバウンドあとはインスタンス接続用のSSHインバウンドを開けるように設定します。

次にバージニアリージョンにもSGを作成しますが、こちらはDNSのインバウンドとSSHインバウンドに加えて、パッケージのダウンロード用にHTTP,HTTPSを開けておきます。
（インバウンド系は私の今のPublicIPをもとに開けるように設定している関係であまりおみせできる画面がないので、東京リージョンのアウトバウンドの様子だけ掲載します）

![スクリーンショット 2023-12-23 17.54.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244678/59077f77-8db0-c0a5-1b49-b39f5e97b0d8.png)

![スクリーンショット 2023-12-23 21.27.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244678/393f5675-ea41-1419-1467-7dc2b7c2e3b8.png)


# CVMインスタンスの作成
Cloud Virtual Machineをいよいよ作成します。
他クラウドでは"起動"や"作成"をするとインスタンスが作成され、課金も開始されるという作りが多いと思いますが、Tencent Cloudは"購入"でインスタンスを購入し、起動するというような言葉選びになっています。

CVMコンソールページへ移動し、左側の購入を実施します（前述の通り、簡単に環境を立ち上げられるLighthouseは今回はVPC設定などを細かく指定する必要がある可能性があるので、選びません）

![スクリーンショット 2023-12-23 21.01.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244678/fe24b254-4a55-db1f-4bf5-92fc7c2cb14d.png)


今回は実験用の軽いインスタンスなのでコスト削減のためにSpot Instancesを選択して作成します。
※途中スクショの撮り漏れがり、東京リージョンの作成画面とバージニアリージョンの作成画面を合わせて補完して掲載しています。

Spot Instanceのデフォルトででてくる、S5.Medium2がちょうどよいサイズなので、これを選択します。OSはせっかくなのでTencentOS、システムディスクは今回ほぼ利用しないはずなのでTencentOSの最小サイズである20GBに変更しておきます。

![スクリーンショット 2023-12-23 21.04.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244678/ada216f1-bf4c-9841-ef10-5cd1a85d13e6.png)
![スクリーンショット 2023-12-23 21.04.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244678/faca479d-8b89-c764-58dc-e239880472e2.png)
![スクリーンショット 2023-12-23 21.04.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244678/35763a2f-f1d0-0026-dc71-eb36ef31df06.png)

Spot Instanceの選択画面から遷移しようとすると、Spot Instanceについての注意事項が出てくるので同意し、ネットワークなどの設定へ。

![スクリーンショット 2023-12-23 21.12.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244678/1cbfee50-f43f-860b-8010-8f61c61b290a.png)

ネットワーク設定については、これまでに作ったVPC・サブネットやSecurityGroupを選択して進めます。

![スクリーンショット 2023-12-23 21.13.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244678/14fe0110-1a6a-c0b5-5196-4b5046def999.png)
![スクリーンショット 2023-12-23 21.06.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244678/d80bd4ae-c899-521e-812b-1067d3b5913f.png)

ログイン方法にはセキュリティのためパスワードではなくSSH Key Pairを選択しますが、鍵をつくってこなかったのでここで「Create a new one」のリンクからSSH鍵を新規に作成しておきます。
作成したらVMの設定画面に戻り、Key Pair欄すぐ右の再読み込みボタンをクリックすれば作成した鍵が選べるようになっているはずです。

![スクリーンショット 2023-12-23 21.14.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244678/6c344b9e-791b-bf54-2dbb-176185357d44.png)
![スクリーンショット 2023-12-23 21.07.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244678/1d231d0b-b645-429a-8e06-f3a01a13a502.png)
![スクリーンショット 2023-12-23 21.08.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244678/97b25e31-fceb-45e6-edd6-8c1233daec75.png)

その他設定は特に変更せず、最後の確認事項に出てくる注意事項に再度チェックした上でインスタンスを作成。
支払い設定などが問題なければ、Instance画面に作成したVMが表示されるようになります。

![スクリーンショット 2023-12-23 21.09.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244678/9429ce91-4afa-b151-745d-fdbb4df445db.png)
![スクリーンショット 2023-12-23 21.35.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244678/f57726e6-42f2-d606-0397-b3bf3d56c8ef.png)

## バージニアリージョンのCVMへbindを設定
DNSのテストのために、バージニアリージョンのCVMにbindを設定します。
CVMコンソールから、確認できるPublic IPと、あらかじめ設定したSSH鍵を指定してSSHログインします。

```
ssh -l root -i advent_calender_key.pem <接続先CVM PublicIP>
```

:::note alert
CVMコンソールでインスタンスを選択し、「Log in」ボタンからの接続を試したくなりますが、Chromeなどの多くのブラウザは22番ポートを危険なポートとしてブロックするため、ブラウザ経由でのSSHは繋がりません。
別途CLIからのSSH接続をお勧めします。
:::

### bindのインストール
Tencent OSはCentOS系なのでyumでインストールします。
そしてインストール確認、
```bash
sudo yum install bind
named -v
```
```bash
<インストールログは省略>
BIND 9.11.36-RedHat-9.11.36-11.tl3 (Extended Support Version) <id:68dbd5b>
```

### bindの起動
インストール直後だとサービスが起きていないので起こしておきます。

```bash
systemctl start named
systemctl status named
```

```bash
● named.service - Berkeley Internet Name Domain (DNS)
   Loaded: loaded (/usr/lib/systemd/system/named.service; disabled; vendor preset: disabled)
   Active: active (running) since Sat 2023-12-23 22:07:22 CST; 3s ago
  Process: 53979 ExecStart=/usr/sbin/named -u named -c ${NAMEDCONF} $OPTIONS (code=exited, status=0/SUCCESS)
  Process: 53955 ExecStartPre=/bin/bash -c if [ ! "$DISABLE_ZONE_CHECKING" == "yes" ]; then /usr/sbin/named-checkconf -z "$NAME>
 Main PID: 53981 (named)
    Tasks: 5 (limit: 10824)
   Memory: 15.2M
   CGroup: /system.slice/named.service
           └─53981 /usr/sbin/named -u named -c /etc/named.conf
```

### bindの設定

長いので折りたたみますが、bindに設定を入れます。
日本語コメントを入れている部分と追加ファイルが変更箇所です（おそらくsshでTencentOSに持っていくと日本語は文字化けすると思うので、適宜削除ください。

<details><summary>named.conf</summary>

/etc/named.confを編集します。

```
//
// named.conf
//
// Provided by Red Hat bind package to configure the ISC BIND named(8) DNS
// server as a caching only nameserver (as a localhost DNS resolver only).
//
// See /usr/share/doc/bind*/sample/ for example named configuration files.
//

options {
    // SGで絞るので、bind側での問い合わせ元の絞り込みは実施しない。
	//listen-on port 53 { 127.0.0.1;};
	//listen-on-v6 port 53 { ::1; };
	directory 	"/var/named";
	dump-file 	"/var/named/data/cache_dump.db";
	statistics-file "/var/named/data/named_stats.txt";
	memstatistics-file "/var/named/data/named_mem_stats.txt";
	secroots-file	"/var/named/data/named.secroots";
	recursing-file	"/var/named/data/named.recursing";
    // SGで絞るので、bind側での問い合わせ元の絞り込みは実施しない。
	allow-query     { any;};

	/* 
	 - If you are building an AUTHORITATIVE DNS server, do NOT enable recursion.
	 - If you are building a RECURSIVE (caching) DNS server, you need to enable 
	   recursion. 
	 - If your recursive DNS server has a public IP address, you MUST enable access 
	   control to limit queries to your legitimate users. Failing to do so will
	   cause your server to become part of large scale DNS amplification 
	   attacks. Implementing BCP38 within your network would greatly
	   reduce such attack surface 
	*/
    //再帰問い合わせは切っておく
	recursion no;

	dnssec-enable yes;
	dnssec-validation yes;

	managed-keys-directory "/var/named/dynamic";

	pid-file "/run/named/named.pid";
	session-keyfile "/run/named/session.key";

	/* https://fedoraproject.org/wiki/Changes/CryptoPolicy */
	include "/etc/crypto-policies/back-ends/bind.config";
};

logging {
        channel default_debug {
                file "data/named.run";
                severity dynamic;
        };
};

zone "." IN {
	type hint;
	file "named.ca";
};

// テスト用のローカルゾーンを作る。
zone "example" IN {
       file "example.zone";
       type master;
};


include "/etc/named.rfc1912.zones";
include "/etc/named.root.key";

```
</details>

<details><summary>example.zone</summary>

/var/named/example.zoneに以下ファイルを作成します。


```
$ORIGIN example.
$TTL      86400
@         IN       SOA     dns.example. test.example.com (
                                        2023122500 ; Serial
                                        28800      ; Refresh
                                        14400      ; Retry
                                        3600000    ; Expire
                                        86400 )    ; Minimum
          IN       NS      dns.example.
dns       IN       A       170.106.35.94
server    IN       A       192.168.0.2
```
</details>

設定ファイルを配置した上で、bindを再起動します。（設定ファイルに不備がある場合はrestartのタイミングでこけます）
その後、localhostを指定して名前解決を実施し、ちゃんと意図した解決がされるかを確認します。

```
sudo systemctl restart named
nslookup server.example 127.0.0.1
```

```bash
Server:		127.0.0.1
Address:	127.0.0.1#53

Name:	server.example
Address: 192.168.0.2
```

## 東京リージョンのCVMからの名前解決レイテンシの測定
ということでまずは、IP AnyCastによる高速化なしで東京リージョンのCVMから名前解決を行なってみます。

:::note warn
東京のCVM->バージニアのCVMでのSGの穴あけを忘れずに。
:::

10回DNSの名前解決をさせるスクリプトを書いて実行時間を計測します。
※(nslookup server.example <CVMのPublicIP>を10回書いているだけ。
計測自体も2回やってみましたが、3.4秒ほどでした。

```
time ./query-10.sh > /dev/null

real	0m3.400s
user	0m0.051s
sys	0m0.035s
```

```
time ./query-10.sh > /dev/null

real	0m3.401s
user	0m0.054s
sys	0m0.032s

```

# IP AnyCastありでのアクセス
次にIP AnyCastありでのアクセスに切り替えていきます。
構成図的には以下の通りのイメージです。

このちょっとした図だけを見るとあまり差がないように見えますが、AnyCast IPによって、早い段階でTencent CloudのNetwork内に入り、以降はTencent Cloudの最適化されたネットワークによって接続されるため通常のインターネット経由でのアクセスに比べ無駄な経路などが削減され、高速化することが期待されます。

![with_anycast.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244678/c3f3668e-1c05-c382-aee8-4375108b0b2f.png)


## バージニアリージョンのインスタンスにAnyCastIPを割り当て

:::note alert
ここから構築できていません。
IP AnyCastの機能は課金形態が特殊になっているため、Salesへの問い合わせと解放が必要です！
今回のアドベントカレンダーに間に合わなかった私のようにならないよう、事前に余裕をもって申請しましょう。
:::

ということでAnyCastの設定をしていきます。
まずは、バージニアリージョンでAnyCast対応のEIPを取得します。

**事前にSalesへコンタクトして解放されていれば**
CVMのPublicIPを購入する際にAnyCast IPを選択することができます。
そこで作成したAnyCast IPを、今CVMに割り当てられている通常のIPと入れ替えて割り当てることで、AnyCast対応ができ、レイテンシなどが改善することが期待されます。

# 終わりに
IP AnyCastでの通信改善自体がみれなかったことは非常に心残りですが、VM構築などの部分については結構詳しく書けたと思いますので、誰かの役に立てば嬉しいなと思います。