建立 Amazon VPC 的 High Availability NAT 架構

Amazon VPC 的架構裡最讓人碎碎唸的一個架構:NAT instance。

Amazon VPC 分成 Public Network 與 Private Network。

前者的 Public Network,裡面的機器除了會有 Private IP 外,需要申請 Public IP (可以是隨機分配,也可以是 Elastic IP) 透過 Intenet Gateway (沒有 NAT 功能) 連外,這邊問題比較小,因為 Routing Table 設一下就好了,High Availability 以及 Scalablility 的問題 AWS 會自己解決掉。

後者 Private Network 因為需要自己架設 NAT instance,所以要自己處理 High Availability 以及 Scalability 問題,由於把機器丟在後面,前面用 ELB 是蠻常見的架構,AWS 一直沒推出 NAT service 讓人感覺很疑惑...

目前一般在處理 Private Network 的 HA NAT 架構是參考「High Availability for Amazon VPC NAT Instances: An Example」這篇文章,但這篇文章的作法有點複雜。

我可以接受有一些 downtime 時間以及一些小狀況,相對的,我想要換取極低的管理成本。

研究了一陣子,最後決定的作法是受到「An Alternative Approach to “HA” NAT on AWS」這篇的啟發,這篇也只講了很簡單的概念,實際上還是要自己研究。

目前是做在 us-west-2 (Oregon) 的 1b 與 1c 兩個 AZ 上。下面討論時就不說明這點了。

規劃的想法是 1b 與 1c 兩個 AZ 各建立一個 auto scaling group,透過 auto scaling 各跑一台 NAT instance 處理自己 AZ 的 NAT traffic (所以不是手動跑)。然後我不想要自己建 image 寫太多 hard code 進去,最好是現成的用一用就好 XD

所以有幾個重點:

  • NAT instance 拿現成的 amzn-ami-vpc-nat 使用,寫這篇文章時是用 2014.09 版。
  • 由於官方的 NAT instance 支援 userdata 在開機時執行指令,所以完全透過 userdata 指定需要的做動就好。
  • 由於 Amazon 官方給的 instance 有 aws 這隻工具 (aws-cli),而這隻工具在有掛上 IAM Role 時會去 http://169.254.169.254/ 上取得對應的 IAM Role 權限,所以都不需要寫太多 hard code 的東西進去。

機器開起來以後希望做幾件事情:

  • 把自己的 Source/Destination IP check 關閉。
  • 把傳進來的 Route Table 的 0.0.0.0/8 設成自己。這邊需要傳進來是由於 NAT instance 是跑在 Public Network 裡,我不會知道要改哪個 Route Table。

所以就有兩個重點,一個是 userdata,其中粗體是要修改的 route table id:

#!/bin/bash
ROUTE_TABLE_ID=rtb-1a2b3c4d
INSTANCE_ID=$(curl http://169.254.169.254/latest/meta-data/instance-id)
aws ec2 modify-instance-attribute \
    --region us-west-2 \
    --instance-id "${INSTANCE_ID}" \
    --no-source-dest-check
aws ec2 replace-route \
    --region us-west-2 \
    --route-table-id "${ROUTE_TABLE_ID}" \
    --destination-cidr-block 0.0.0.0/0 \
    --instance-id "${INSTANCE_ID}" || \
aws ec2 create-route \
    --region us-west-2 \
    --route-table-id "${ROUTE_TABLE_ID}" \
    --destination-cidr-block 0.0.0.0/0 \
    --instance-id "${INSTANCE_ID}"

最前面事先抓 instance_id,然後修改 Source/Destination 檢查,最後面的指令是先試著 ReplaceRoute,如果失敗就 CreateRoute (注意到 shell 的 || 操作)。

另外的重點是 IAM Role,這台機器對應的 IAM Role 權限要開三個:

{
  "Statement": [
    {
      "Action": [
        "ec2:CreateRoute",
        "ec2:ReplaceRoute",
        "ec2:ModifyInstanceAttribute"
      ],
      "Effect": "Allow",
      "Resource": "*"
    }
  ]
}

拿 t2.medium 測試,burst 可以到 200Mbps 左右,應該還算夠用?反正不夠用就自己挑其他機器開吧 XD

最後討論一下 Availability 的情況。這樣的架構可能會有七八分鐘的 downtime,也就是當 instance 掛了,被 auto scaling 重新拉一台新的起來。這邊不做 cross-zone NAT 是因為這樣比較簡單,這邊的 downtime 我還可以接受。

至於某個 zone 掛掉,應該 cross-zone NAT 趕快導到另外一區的問題... 整個 zone 都滅了就沒有這個問題啊 XDDD

Amazon DynamoDB 筆記

Amazon DynamoDB 頁面上的介紹:

Amazon DynamoDB is a fast and flexible NoSQL database service for all applications that need consistent, single-digit millisecond latency at any scale.

資料型態的部份就跳過去了,這篇筆記的重點在於 index 的部份 (了解他如何 scale),尤其是對 RDBMS 有了解的人要如何從他所設計的架構理解 DynamoDB 的 index。

理論基礎是 Amazon 在 2007 年丟出的論文「Dynamo: Amazon’s Highly Available Key-value Store」,這篇論文影響了很多 open source project。

DynamoDB 的 index 有 Primary Key、Local Secondary Index Key (LSI) 以及 Global Index Key (GSI),在「DynamoDB Data Model」這篇有介紹。

這邊會拿 Blogger.com 這種多人的 Blog Hosting 當例子:

  • 一個 user 可以有很多 blog。(table user)
  • 一個 blog 可以有很多 post。(table blog)
  • 一篇 post 可以有很多 comment。(table post)

接下來就從 Primary Key 開始講。

Primary Key

Primary Key 保證唯一,這也是 DynamoDB 裡面可以達到 RDBMS 的 UNIQUE KEY 效果的最佳方式。

有兩種 Primary Key 的型態,一種叫做 Hash,另外一種叫做 Hash-Range。

兩種都需要指定某一個欄位是 Hash-based column,當作切割 (partition) 的依據。

第一種:Hash

以 table user 來說,可以拿 user_id 來當作 Hash-based column,裡面有 blog_id 的 list。

以 table blog 來說,可以拿 blog_id 來當作 Hash-based column,裡面有 post_id 的 list。

要注意的是,如果表格 PK 是 Hash,那麼就不能使用 LSI 與 GSI 了。只有另外一種型態 (Hash-Range) 才可以用 LSI 與 GSI。

相對的,Hash-based 的表格因為功能有限,效率通常很好 XDDD

第二種:Hash-Range

其實 Hash-Range 是一種別的 LSI,兩者最大的差異就是唯一性了。

另外一種 Primary Key 是 Hash-Range,他需要指定兩個欄位,其中其中 Hash 的欄位就如同上面的解釋,當作資料切割的依據。這邊的唯一性是指 (Hash column, Range column) 唯一,而非只有 Hash 唯一或是 Range 唯一。

剛剛說到需要指定的另外一個欄位,被稱為「Range」的原因是因為他可以有效率的以 hash + range query 查詢資料。

以 table post 來說,可以拿 blog_id 當作 Hash-based column,再拿 post_id 當作 Range-based column,等下我們介紹 LSI 時再拿發表時間欄位排序。

同理,table comment 可以拿 (post_id, commend_id) 當 PK。

Query

PK 是 Hash 的當然就是指定 Hash-based column 直接查,條件只能是等號。

PK 是 Hash-Range 的除了可以用 Hash-based column 直接查 (還是只能用等號),另外可以用 Hash-based column + Range-based column 查。

以 SQL 的想法就像是 WHERE hash_col = 123 AND range_col BETWEEN (123, 456) 的感覺。反正 Hash-based column 一定要等號。

講到這邊,其實讀過上面提到的 Amazon 那篇論文應該就大概有感覺架構是怎麼搞的了:(這是推敲出來的,未必是實際架構)

  • 用 Hash-based column 切 consistent hash ring 塞到不同機器上。PK 是 Hash 的到這邊就搞定了。
  • PK 是 Hash-Range 的,還是照上面一條提到的,用 Hash-based column 切開,所以同樣的 Hash-based column 的資料都會塞到同一台機器上,於是就可以用有效率的 ordered tree 來存放 Range-based column 的資料,這樣就可以提供 query 了。

當然,考慮到需要實做 rebalance 機制以逐步擴充,這邊 consistent hash ring 的部份的作法可以更細膩,不過就不是這篇要談的重點了。

接下來要講重頭戲 LSI 與 GSI 了。

Local Secondary Index (LSI) 與 Global Secondary Index (GSI)

前面有提到 LSI 與 GSI 必須 PK 是 Hash-Range 的情況下能用,兩者都不強制唯一性。

LSI 與 GSI 都是 (Hash-column based, Range-column based) 的形式,差別在 LSI 的 Hash-column based column 必須跟 PK 的相同,GSI 的可以不用一樣。

所以對於 table post 可以加一個 LSI (blog_id, post_datetime),就可以用 WHERE blog_id = 123 ORDER BY post_datetime DESC 拉出對應的文章了。

同理,table comment 是 (post_id, comment_datetime)。

Redshift 的 2*dw2.8xlarge 與 32*dw2.large 的比較

這邊的 Amazon Redshift 都是跑在 Oregon (us-west-2),前者的價錢是 USD$9.6/hour,後者是 USD$8/hour,大約貴一些。這是同一個 query 跑過多次後穩定的數字,用 PostgreSQL\timing 出來的數字計算。

先從比較簡單的 query 開始,這個表格大約六億筆資料,如果以記憶體大小算的話,是 memory-fit。

前者是:

dev=# SELECT COUNT(DISTINCT x) FROM t;
 count  
--------
 925612
(1 row)

Time: 3345.946 ms

後者是:

dev=# SELECT COUNT(DISTINCT x) FROM t;
 count  
--------
 925612
(1 row)

Time: 2145.132 ms

再來條件多一點,一樣前者是:

dev=# SELECT x, COUNT(*) AS cnt FROM t GROUP BY x ORDER BY cnt DESC LIMIT 20;
...
(20 rows)

Time: 2121.002 ms

後者是:

dev=# SELECT x, COUNT(*) AS cnt FROM t GROUP BY x ORDER BY cnt DESC LIMIT 20;
...
(20 rows)

Time: 1388.102 ms

發現比較貴的 cluster 的反而比較慢 (大台 dw2.8xlarge 組成的),用多台小台的去堆反而比較快。晚點找時間測沒辦法 memory-fit 的速度。

意外的跟直覺不太一樣,要測過才知道...

Amazon EC2 的 C4 Instance

這時候就要拿出經典台詞:

Anyway,Amazon EC2 推出 C4 Instance 了:「Now Available – New C4 Instances」。

來看看 c4.8xlarge,最近說不定用的上,不過要注意的是,C4 Instance 只支援 EBS only。

c4.8xlarge 的 ECU 是 132 (USD$1.856/hour) 而 c3.8xlarge 是 108 (USD$1.68/hour),跟 C3 相比,看起來更實惠一些。

Amazon EC2 Spot Instance 的關機通知

AWS 改善了 EC2 Spot Instance 的關機通知,在決定 terminate 前兩分鐘 instance 可以得知消息,讓 instance 可以處理「後事」,像是將計算的資料寫回 EBS 或是 S3:「New – EC2 Spot Instance Termination Notices」。

Today we are improving the reclamation process with the addition of a two-minute warning, formally known as a Spot Instance Termination Notice.

不過目前不是 trigger,而是透過 polling 的方式提供:

The Termination Notice is accessible to code running on the instance via the instance’s metadata at http://169.254.169.254/latest/meta-data/spot/termination-time. This field will become available when the instance has been marked for termination (step 3, above), and will contain the time when a shutdown signal will be sent to the instance’s operating system.

建議的 polling 時間是五秒鐘一次:

We recommend that interested applications poll for the termination notice at five-second intervals.

至少有方法得知了...

Amazon CloudFront 允許帶有路徑的 Origin Name 了

剛剛看到的,Amazon CloudFront 允許 Origin Name 帶有 path 了:「Amazon CloudFront Now Allows Directory Path as Origin Name」。

For example, if you're using an Amazon S3 bucket as your origin, you can specify bucket-name.s3.amazonaws.com/production instead of just bucket-name.s3.amazonaws.com. Alternatively, if you have a custom web server with different content in different directories on the server, you can use the same physical server to serve multiple types of content via multiple CloudFront cache behaviors or distributions.

變得更方便,以往是直接開兩個不同的 bucket 放。

AWS OpsWorks 支援非 EC2 instance 的管理

AWS OpsWorks 算是 Chef hosting 加上設計好的管理機制?之前只能用在 AWS 上,現在則可以使用在非 EC2 instance 上了:「AWS OpsWorks Update - Support for Existing EC2 Instances and On-Premises Servers」。

在 EC2 上不用額外收費,在 EC2 外要收 USD$0.02/hour:

OpsWorks costs $0.02 per hour for each on-premises server on which you install the agent, and is available at no additional charge for EC2 instances.

看不太懂為什麼要支援非 EC2 的服務,扶植 Chef 嗎?

新版 EC2 的 Reserved Instance

前幾天 AWS 宣佈 EC2Reserved Instance 改變販賣方式:「Simplifying the EC2 Reserved Instance Model」。

本來的賣法是收兩種錢,一個是一次性的費用,另外一個是每個小時的費用。在舊的架構下,有不同種類的一次性費用,對上不同的小時單價。

新的架構變成收三種錢,一個是一次性的費用,一個是每個月的基本費 (電信資費中,類似於「基本費不可抵通話費」的觀念),最後是每個小時的費用。

看 AWS 文章的說明有點難懂,直接去 EC2 的價目表上面看就知道了。

至於這樣有沒有簡化,我覺得是見仁見智啦... 這次的 RI model 的改變最主要是降低對現金流的要求,付出一點利息就可以把一次性費用打散到十二個月 (或是三十六個月)。

對於 startup 應該還蠻有吸引力的改變。

AWS 對外頻寬與 CloudFront 頻寬降價

AWS 在「AWS Data Transfer Price Reduction」這邊宣佈降價,回朔至 2014 年 12 月 1 日開始計算。

分成一般 Outbound 與 CloudFront Outbound 部份。

一般 Outbound 的部份,US/EU 主要降價的部份是一開始的流量 (10TB 以下的部份),亞洲區的部份主要是新加坡降價特別多,大概是把量養出來了?

CloudFront Outbound 的部份主要也是在 10TB 以下的部份。

另外一個比較特別的地方 (但也許沒有太大差異),是 CloudFront 往 origin 抓資料時,如果 origin 在 AWS network 內就不計算費用 (包括了 S3EC2)。

如果暫時先不考慮 per request 費用,會注意到 US/EU 一般 Outbound 的費用是 USD$0.90/GB,而 CloudFront 是 USD$0.85/GB,量更大的時候會更明顯?

馬上想到 Netflix 這種單一 request 會有巨大流量的用法會隨著這次計費方式改變而有變化?

2014 年 re:Invent 的投影片、影片、錄音

一年一度的 AWS re:Invent 的投影片、影片、錄音檔放出來了:「Now Online - Audio, Video, and Presentations from re:Invent 2014」。

看影片與錄音會比較花時間,從投影片看會是不錯的方法。在 SlideShare 的「reinvent2014」這邊有官方放出的投影片。

量相當多,之後再來挑有興趣的題目來翻。