概要
AWS VPCエンドポイントのインターフェース型(PrivateLink)には、3種類あります。
- AWSサービス(AWSプリンシパルサービス)
- サービスを名前で検索(独自サービス)
- ご使用のAWS Marketplaceサービス(マーケットプレイスサービス)
このうち、座学だけだと、AWSプリンシパルサービスのEC2エンドポイントと、EC2上に独自で作ったAPIに対するエンドポイントでは何が違うのかわからず小一時間悩んだので、違いと構築した結果をメモします。なお、PrivateLinkそのものに対する説明は、以下の記事が非常に参考になりましたので、こちらを見ていただけれなと思います。
AWS PrivateLinkの使い方と注意点 ~VPCピアリングとの使い分け~
結論
- AWSプリンシパルサービスは、VPCエンドポイントを通じて、AWSサービスのAPIが利用できる
- EC2なら、aws cliからEC2のAPIが利用できる
- 独自サービスは、自分で構築したAPIをVPCエンドポイントを通じて別VPCから利用できる
- EC2上に構築したREST API等を利用できる
AWSプリンシパルサービス
例として、EC2にAPIで作成します。実装方法は以下の記事が大変参考になりました。
AWS PrivateLinkが登場 EC2やELBでどのように使うか
構成
Privateなサブネットに配置したEC2から、aws cliを実行してEC2のAPIを実行します。(EC2のAPIを書く位置がここで良いかはわからないですが...)
実行結果
Private DNSをtrueにした場合
aws ec2 describe-availability-zones
{
"AvailabilityZones": [
{
"OptInStatus": "opt-in-not-required",
"Messages": [],
"ZoneId": "use2-az1",
"GroupName": "us-east-2",
"State": "available",
"NetworkBorderGroup": "us-east-2",
"ZoneName": "us-east-2a",
"RegionName": "us-east-2"
},
# 以下、省略
$ aws ec2 describe-availability-zones --endpoint-url https://ec2.us-east-2.amazonaws.com
{
"AvailabilityZones": [
{
"OptInStatus": "opt-in-not-required",
"Messages": [],
# 以下、省略
Private DNSをfalseにした場合
[ec2-user@ip-10-0-20-44 ~]$ aws ec2 describe-availability-zones
# レスポンス無し
aws ec2 describe-availability-zones --endpoint-url https://XXXXXXXXX.ec2.us-east-2.vpce.amazonaws.com
{
"AvailabilityZones": [
{
"OptInStatus": "opt-in-not-required",
"Messages": [],
# 以下、省略
独自サービス
手順の概要は以下の通りです。
- 独自のAPIを構築する側のVPCにNLBをターゲットにしたエンドポイントサービスを作成する。
- アクセス元のVPCにインターフェース型VPCエンドポイントを作成する。作成時に、1のエンドポイントサービスを指定する。
詳細な手順は以下を参考にさせていただきました。
【新機能】PrivateLinkで独自エンドポイントを作ってアプリをプライベート公開する #reinvent
構成図
Privateなサブネットから、別VPCのPrivateサブネット上にあるEC2にアクセスします。
EC2に構築したREST API
PythonのFlaskを使って、/hello
のリソースに5000番でGETしたらmessageを返すだけのAPIです。このAPIに対してNLBを作成します。
import json
def get_hello():
message = "Hello, good evening"
return_json = json.dumps({"message": message})
return return_json
from flask import Flask
from hello import get_hello
app = Flask(__name__)
@app.route('/hello', methods=['GET'])
def local_endpoint():
return get_hello()
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
[ec2-user@ip-10-0-10-166 ~]$ curl http://10.0.30.21:5000/hello
{"message": "Hello, good evening"}
NLBからアクセスできているかは、ヘルスチェックを参照しましょう。unhealthyの場合、EC2に対するセキュリティグループの設定が上手くできていない可能性があります。今回のAPIで言ったら、同じサブネットである10.0.30.0/24
から5000番を許可する必要があります。(下図の3行目)
実行結果
作成したエンドポイントに表示されたDNS名に対してリクエストを投げます。
[ec2-user@ip-10-0-20-44 ~]$ curl http://XXXXXXXXX.XXXXXXXXXXXX.us-east-2.vpce.amazonaws.com/hello
{"message": "Hello, good evening"}
ハマり所として、VPCエンドポイントのセキュリティグループに対して、アクセス元になるEC2からの通信を許可する必要があります。defaultでVPCエンドポイントを作ると設定されないので、明示的に作成しましょう。ポートは、NLBのリスナーと合わせます。
プライベートDNS名を利用した場合
以下記事で紹介されている通り、2020/1のアップデートで、設定したプライベートなDNS名を、リクエスト側のエンドポイントにすることが可能になったようです。VPCエンドポイントで作成されるDNS名はランダムなIDが入ってわかりにくいので、アクセスしやすくなりましたね。
[新機能] PrivateLinkの公開サービスにプライベートDNS名が指定可能になりました
例えば、example.com
というドメイン名を持っている場合、エンドポイントサービスの作成時のPrivate DNS nameに設定することで、以下の通りアクセスすることが可能となります。なお、Route53のTXTレコード登録も必要となります。
- 名前:
[Domain verification name]
.example.com - 値:
Domain verification value
[ec2-user@ip-10-0-20-44 ~]$ curl http://example.com/hello
{"message": "Hello, good evening"}
終わりに
作ってるうちにわかりましたが、全くの別物でしたね...
また、独自サービスを作ると、VPCエンドポイントとエンドポイントサービスの違いも理解できました。やはり手を動かして理解することが重要ですね。