Chef:自前NATインスタンスを作ってみた(Amazon Linux 2014.03版)
こんにちは、三井田です。
VPCでプライベートサブネットからインターネットにアクセスしたい場合、NATインスタンスを利用することがあります。
参考資料:Amazon VPC ユーザガイド
- amzn-ami-vpc-nat-pv-2013.03.1.x86_64-ebs - ami-5f840e5e
- amzn-ami-vpc-nat-pv-2013.09.0.x86_64-ebs - ami-cd43d9cc
このエントリでは、上記のAWSが用意しているNATインスタンスを使うのではなく、自前でNATインスタンスを作ってみました。
自前で作ることで、次のようなメリットがあると思っています。
- 最新のAmazon Linuxベースで、他のサーバなどと環境を合わせることができる
- amzn-ami-minimal-pvイメージを利用すると、EBSタイプのインスタンスでは、EBSは2GBと少量であり、コストが節約できる
- さらに、インスタンスストアタイプのAMIでは、そもそもEBSを利用しないのでさらに節約ができる
- NATインスタンスには重要な永続的なデータは通常必要ないため、インスタンスストアタイプでも十分実用的かと思います
NATインスタンスのシステム要件
NATインスタンスを端的に描写すると、IPマスカレード機能を提供するLinuxルータです。
- Linuxカーネルパラメータ"net.ipv4.ip_forward"の値が「1」である
- iptablesのNATテーブルでPOSTROUTINGチェインにMASQUERADE設定がある
- EC2の送信元/送信先チェック機能がオフになっている
上記の3は冒頭で紹介した「Amazon VPC ユーザガイド」にあるようにマネジメントコンソールなどで変更できます。 1と2についてはインスタンスのOSレベルでの設定となります。
Let's Cooking
近日中にGithubに公開予定です
下で説明する「NATインスタンスを構築するChefクックブック」で作成したクックブックで、EC2インスタンスを構築してみましょう。
次の4つのAMIで試してみました。(AMI IDは東京リージョンのものです)
- amzn-ami-pv-2014.03.0.x86_64-ebs - ami-a1bec3a0
- amzn-ami-minimal-pv-2014.03.0.x86_64-ebs - ami-81bec380
- amzn-ami-pv-2014.03.0.x86_64-s3 - ami-91bec390
- amzn-ami-minimal-pv-2014.03.0.x86_64-s3 - ami-b7bfc2b6
インスタンスを立ち上げたら、マネジメントコンソールから「Change Source/Dest. Check」を選び、Source/Dest. CheckをDisabledにします。
その後、以下のコマンドを実行してNATインスタンスを構築します。
$ knife solo prepare ec2-user@***-***-***-*** $ knife solo cook ec2-user@***-***-***-*** -o "recipe[nat-instance]"
動作確認
プライベートサブネットのルーティングテーブルの0.0.0.0/0のターゲットをNATインスタンスに変更します。
NATインスタンスを踏み台にして、プライベートサブネットのインスタンスにログインして、pingを実行してみましょう。NATインスタンスでは、tcpdumpを実行してパケットの流れを見てみます。
NATインスタンス側で、ソースIPが書き変わったパケットが宛先に出ていれば成功ですね。
※プライベートサブネットのインスタンス [ec2-user@ip-172-31-38-163 ~]$ ping -c 3 www.google.co.jp PING www.google.co.jp (173.194.126.215) 56(84) bytes of data. 64 bytes from nrt04s07-in-f23.1e100.net (173.194.126.215): icmp_seq=1 ttl=57 time=3.10 ms 64 bytes from nrt04s07-in-f23.1e100.net (173.194.126.215): icmp_seq=2 ttl=57 time=3.24 ms 64 bytes from nrt04s07-in-f23.1e100.net (173.194.126.215): icmp_seq=3 ttl=57 time=3.38 ms --- www.google.co.jp ping statistics --- 3 packets transmitted, 3 received, 0% packet loss, time 2006ms rtt min/avg/max/mdev = 3.101/3.244/3.389/0.117 ms
※NATインスタンス [root@ip-172-31-15-6 ec2-user]# tcpdump -i eth0 -nn 'not port 22' tcpdump: verbose output suppressed, use -v or -vv for full protocol decode listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes 09:23:10.521707 IP 172.31.38.163 > 173.194.126.215: ICMP echo request, id 13317, seq 1, length 64 09:23:10.521748 IP 172.31.15.6 > 173.194.126.215: ICMP echo request, id 13317, seq 1, length 64 09:23:10.524414 IP 173.194.126.215 > 172.31.15.6: ICMP echo reply, id 13317, seq 1, length 64 09:23:10.524431 IP 173.194.126.215 > 172.31.38.163: ICMP echo reply, id 13317, seq 1, length 64 09:23:11.523482 IP 172.31.38.163 > 173.194.126.215: ICMP echo request, id 13317, seq 2, length 64 09:23:11.523535 IP 172.31.15.6 > 173.194.126.215: ICMP echo request, id 13317, seq 2, length 64 09:23:11.526193 IP 173.194.126.215 > 172.31.15.6: ICMP echo reply, id 13317, seq 2, length 64 09:23:11.526209 IP 173.194.126.215 > 172.31.38.163: ICMP echo reply, id 13317, seq 2, length 64 09:23:12.525020 IP 172.31.38.163 > 173.194.126.215: ICMP echo request, id 13317, seq 3, length 64 09:23:12.525081 IP 172.31.15.6 > 173.194.126.215: ICMP echo request, id 13317, seq 3, length 64 09:23:12.527900 IP 173.194.126.215 > 172.31.15.6: ICMP echo reply, id 13317, seq 3, length 64 09:23:12.527915 IP 173.194.126.215 > 172.31.38.163: ICMP echo reply, id 13317, seq 3, length 64 ^C 12 packets captured 12 packets received by filter 0 packets dropped by kernel
まとめ
このように、NATインスタンスは手軽に構築することが出来ました。
このレシピで、Source/Dest. Checkの変更まで出来ればさらに自動化を進められそうです。
付録:NATインスタンスを構築するChefクックブック
手許の環境は、Mac OS X Merveriksです。以下の資料を参考にknife soloとberkshelfを実行できる環境を作りました。
- 書籍:入門Chef Solo - Infrastructure as Code
- 書籍:Chef活用ガイド - コードではじめる構成管理
- ブログ:AWS EC2サーバに対するknife solo実行環境構築手順 on Mac OS Xを一から整理してみる
また、なるべく公開されている資産を使う方針で、Opscoe Community Cookbooksのクックブックを活用するようにしました。
Chefリポジトリと空のクックブックの作成
まず、Chefリポジトリを作り、クックブックを作成しました。ディレクトリ構成は次のようになっています。
$ knife solo init chef-repo $ cd chef-repo $ knife cookbook create nat-instance -o site-cookbooks $ tree -F . ├── Berksfile ├── cookbooks/ ├── data_bags/ ├── environments/ ├── nodes/ ├── roles/ └── site-cookbooks/ └── nat-instance/ ├── CHANGELOG.md ├── README.md ├── attributes/ ├── definitions/ ├── files/ │ └── default/ ├── libraries/ ├── metadata.rb ├── providers/ ├── recipes/ │ └── default.rb ├── resources/ └── templates/ └── default/
nat-instanceクックブックの作成
それでは、nat-instanceクックブックを作成していきます。 まず、chef-repo/Berksfileに依存するコミュニティクックブックを記載しておきます。
source "http://api.berkshelf.com" cookbook 'sysctl' cookbook 'iptables'
続いて、chef-repo/site-cookbooks/metadata.rbにも同様に追記しておきます。
name 'nat-instance' <<略>> version '0.1.0' depends 'sysctl' depends 'iptables'
次にレシピ本体を見てみましょう。chef-repo/site-cookbooks/nat-instance/recipes/default.rbです。
include_recipe 'sysctl' sysctl_param 'net.ipv4.ip_forward' do value 1 end include_recipe 'iptables' iptables_rule 'ip_masquerade'
はい!これだけです。2行目のsysctl_paramはsysctlクックブックから、7行目のiptables_ruleはiptablesクックブックからの借り物です。
iptables_rule 'ip_masquerade'の部分で、iptablesのNATテーブルにルールを追加しています。追加するルールは、次のchef-repo/site-cookbooks/nat-instance/template/default/ip_masquerade.erbファイルに記載してあります。
*nat -A POSTROUTING -s <%= node.vpc_cidr %> -j MASQUERADE
上記のテンプレートで<%= node.vpc_cidr %>と変数を埋め込んでいます。このデフォルト値は、chef-repo/site-cookbooks/nat-instance/attributes/default.rbに設定してあります。
EC2のmeta-dataからVPCのCIDRを取得しているのですが、取得する際にohaiが収集したノード情報node[:macaddress]を利用しているのがTipです。
default["vpc_cidr"] = `curl -q http://169.254.169.254/latest/meta-data/network/interfaces/macs/#{node[:macaddress].downcase}/vpc-ipv4-cidr-block 2>/dev/null`
以上です、ではまた!