aws ec2コマンドでPrivateLinkのエンドポイントサービスを設計する方法|NLBを介したVPC間プライベートサービス連携の実践手順

宮崎智広 この記事の監修:宮崎智広(Linux実務・教育歴20年以上・受講者3,100名超)
HOMELinux技術 リナックスマスター.JP(Linuxマスター.JP)AWS(Amazon Linux) > aws ec2コマンドでPrivateLinkのエンドポイントサービスを設計する方法|NLBを介したVPC間プライベートサービス連携の実践手順
「社内の複数VPCで同じバックエンドサービスを共有したいが、VPCピアリングではCIDRが重複して設計に制約が出る」と感じたことはないでしょうか。
AWS PrivateLinkを使えば、VPCのCIDRアドレスが重複していてもAWSのプライベートネットワーク経由でサービスを安全に共有できます。

この記事では、aws ec2コマンドを使ってPrivateLinkのエンドポイントサービス(プロバイダー側)とInterface型エンドポイント(コンシューマー側)を設計・作成する手順を解説します。NLBを介したマルチAZ冗長構成の実現方法から、EC2での接続確認まで一通り説明します。

動作確認環境: Amazon Linux 2023(EC2)、AWS CLI v2.17以降

この記事のポイント

・aws ec2 create-vpc-endpoint-service-configurationでエンドポイントサービスを作成する
・NLBをバックエンドにすることでマルチAZ冗長のプライベートサービスを実現できる
・コンシューマー側はInterface型エンドポイントを作成するだけでVPC間接続が可能
・VPCピアリングと異なりCIDRアドレスが重複していても接続できる


「このままじゃマズい」と感じていませんか?
参考書を開く気力もない、同年代に取り残される不安——
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

AWS PrivateLinkとは何か(VPCピアリングとの違い)

AWS PrivateLinkは、AWSのバックボーンネットワークを経由して、異なるVPC間でサービスをプライベートに提供する仕組みです。インターネットを経由せず、パブリックIPアドレスも不要なので、セキュリティ要件が厳しいシステム間連携に向いています。

VPCピアリングとの主な違いは次のとおりです。

VPCピアリング: 2つのVPC全体を相互接続する。CIDRの重複は不可。推移的ルーティング(A→B→C経由)も不可
AWS PrivateLink: 特定のサービス(エンドポイント)だけを公開する。CIDRの重複があっても接続可能。VPC全体を公開しないためセキュリティ境界が明確

たとえば、社内の認証サービスや共有APIを複数のVPCから利用したい場合、PrivateLinkを使うとプロバイダーVPCの特定サービスだけを公開でき、VPC間でルーティングを全開放せずに済みます。アカウントをまたいだ接続も同じ手順で実現できます。

AWS環境のVPC設計・Linux基盤の構築を体系的に学ぶ方は、Amazon Linux × AWSの実践的な使い方もあわせてご覧ください。

PrivateLinkの構成要素(3つのコンポーネント)

AWS PrivateLinkを構成する主要な要素は3つです。

NLB(Network Load Balancer): プロバイダー側サービスのフロントエンド。マルチAZ配置にすることでバックエンドEC2への通信を複数AZに振り分けられる
エンドポイントサービス: NLBに関連付けてプロバイダーが作成する論理的なサービス定義。コンシューマーが接続を要求する対象になる
Interface型VPCエンドポイント: コンシューマーVPC内に作成するENI(Elastic Network Interface)。エンドポイントサービスへのプライベートDNSエントリが自動生成される

プロバイダー側の設定フローは「NLB作成 → エンドポイントサービス作成 → コンシューマーへのアクセス許可」の3ステップ、コンシューマー側は「Interface型エンドポイント作成」の1ステップで完結します。

プロバイダー側: NLBを作成してマルチAZ配置にする

まずバックエンドサービス(ここではTCP/8080で動くWebAPIを想定)に対してNLBを作成します。NLBを2つ以上のAZのサブネットに配置することで、AZ障害時でも別AZへの自動切り替えが働きます。

1. VPCとサブネットを確認する

# VPC IDとCIDRを確認する aws ec2 describe-vpcs \ --query 'Vpcs[*].[VpcId,CidrBlock,Tags[?Key==`Name`].Value|[0]]' \ --output table # サブネットIDとAZを確認する(プロバイダーVPCのプライベートサブネット) aws ec2 describe-subnets \ --filters Name=vpc-id,Values=vpc-0a1b2c3d4e5f6a7b8 \ --query 'Subnets[*].[SubnetId,AvailabilityZone,CidrBlock,Tags[?Key==`Name`].Value|[0]]' \ --output table # 出力例(2つのAZにプライベートサブネットが存在することを確認) --------------------------------------------------------------- | DescribeSubnets | +-----------------------+-------------------+---------------+--+ | subnet-0a1b2c111111 | ap-northeast-1a | 10.0.10.0/24 | private-1a | | subnet-0a1b2c222222 | ap-northeast-1c | 10.0.20.0/24 | private-1c | +-----------------------+-------------------+---------------+--+

2. NLBを作成する(マルチAZ配置)

# NLBを作成する(ap-northeast-1a と ap-northeast-1c の2つのAZを指定) aws elbv2 create-load-balancer \ --name my-service-nlb \ --type network \ --subnets subnet-0a1b2c111111 subnet-0a1b2c222222 \ --scheme internal # 実行結果(抜粋) { "LoadBalancers": [ { "LoadBalancerArn": "arn:aws:elasticloadbalancing:ap-northeast-1:123456789012:loadbalancer/net/my-service-nlb/1a2b3c4d5e6f0001", "DNSName": "my-service-nlb-1234567890.elb.ap-northeast-1.amazonaws.com", "Type": "network", "Scheme": "internal", "State": { "Code": "provisioning" }, "AvailabilityZones": [ {"ZoneName": "ap-northeast-1a", "SubnetId": "subnet-0a1b2c111111"}, {"ZoneName": "ap-northeast-1c", "SubnetId": "subnet-0a1b2c222222"} ] } ] } # ターゲットグループを作成する(TCP/8080) aws elbv2 create-target-group \ --name my-service-tg \ --protocol TCP \ --port 8080 \ --vpc-id vpc-0a1b2c3d4e5f6a7b8 \ --target-type instance # バックエンドEC2をターゲットグループに登録する aws elbv2 register-targets \ --target-group-arn arn:aws:elasticloadbalancing:ap-northeast-1:123456789012:targetgroup/my-service-tg/abcd1234 \ --targets Id=i-0a1b2c3d4e5f11111 Id=i-0a1b2c3d4e5f22222 # NLBにリスナーを追加する(TCP/443) aws elbv2 create-listener \ --load-balancer-arn arn:aws:elasticloadbalancing:ap-northeast-1:123456789012:loadbalancer/net/my-service-nlb/1a2b3c4d5e6f0001 \ --protocol TCP \ --port 443 \ --default-actions Type=forward,TargetGroupArn=arn:aws:elasticloadbalancing:ap-northeast-1:123456789012:targetgroup/my-service-tg/abcd1234

--scheme internalを指定することでNLBがプライベートIPアドレスのみを持つ内部ロードバランサーになります。PrivateLinkで使うNLBはinternalにするのが原則です。

プロバイダー側: エンドポイントサービスを作成してコンシューマーを許可する

NLBの準備が完了したら、エンドポイントサービスを作成します。このリソースがPrivateLinkの「公開口」になります。

3. エンドポイントサービスを作成する

# エンドポイントサービスを作成する aws ec2 create-vpc-endpoint-service-configuration \ --network-load-balancer-arns arn:aws:elasticloadbalancing:ap-northeast-1:123456789012:loadbalancer/net/my-service-nlb/1a2b3c4d5e6f0001 \ --no-acceptance-required # 実行結果(抜粋) { "ServiceConfiguration": { "ServiceType": [{"ServiceType": "Interface"}], "ServiceId": "vpce-svc-0a1b2c3d4e5f6a7b8", "ServiceName": "com.amazonaws.vpce.ap-northeast-1.vpce-svc-0a1b2c3d4e5f6a7b8", "ServiceState": "Available", "AvailabilityZones": [ "ap-northeast-1a", "ap-northeast-1c" ], "AcceptanceRequired": false, "NetworkLoadBalancerArns": [ "arn:aws:elasticloadbalancing:ap-northeast-1:123456789012:loadbalancer/net/my-service-nlb/1a2b3c4d5e6f0001" ] } }

--no-acceptance-requiredを指定すると、コンシューマーがエンドポイントを作成した際に自動で承認されます。本番環境では省略(デフォルト: 承認必須)にして、許可リストで制御するのが安全です。

4. コンシューマーアカウントへのアクセスを許可する

# コンシューマーのAWSアカウントIDを許可リストに追加する aws ec2 modify-vpc-endpoint-service-permissions \ --service-id vpce-svc-0a1b2c3d4e5f6a7b8 \ --add-allowed-principals arn:aws:iam::999888777666:root # 許可リストを確認する aws ec2 describe-vpc-endpoint-service-permissions \ --service-id vpce-svc-0a1b2c3d4e5f6a7b8 # 実行結果 { "AllowedPrincipals": [ { "PrincipalType": "Account", "Principal": "arn:aws:iam::999888777666:root" } ] }

アカウント単位ではなく特定のIAMロールやユーザーのARNを指定することもできます。社内の共有サービスを複数チームに公開する場合は、チームのロールARN単位で許可リストを管理するとより細かく制御できます。

コンシューマー側: Interface型エンドポイントを作成して接続する

コンシューマーアカウント(または別のVPC)でInterface型エンドポイントを作成します。この操作でコンシューマーVPCにENIが作成され、プライベートDNSエントリが自動生成されます。

5. Interface型エンドポイントを作成する

# コンシューマー側: エンドポイントを作成する # (別アカウントのAWS CLI認証情報を設定して実行する) aws ec2 create-vpc-endpoint \ --vpc-id vpc-0f1e2d3c4b5a6b7c8 \ --vpc-endpoint-type Interface \ --service-name com.amazonaws.vpce.ap-northeast-1.vpce-svc-0a1b2c3d4e5f6a7b8 \ --subnet-ids subnet-0f1e2d3c4b5a11111 subnet-0f1e2d3c4b5a22222 \ --security-group-ids sg-0a1b2c3d4e5f12345 \ --private-dns-enabled # 実行結果(抜粋) { "VpcEndpoint": { "VpcEndpointId": "vpce-01234567890abcdef", "VpcEndpointType": "Interface", "VpcId": "vpc-0f1e2d3c4b5a6b7c8", "ServiceName": "com.amazonaws.vpce.ap-northeast-1.vpce-svc-0a1b2c3d4e5f6a7b8", "State": "pending", "DnsEntries": [ { "DnsName": "vpce-01234567890abcdef-zz1yy2xx.ap-northeast-1.vpce.amazonaws.com", "HostedZoneId": "Z7HUB22UULQXV" } ], "NetworkInterfaceIds": [ "eni-0a1b2c3d4e5f11111", "eni-0a1b2c3d4e5f22222" ] } }

--subnet-idsに複数AZのサブネットを指定することで、各AZにENIが1つずつ作成されます。これがコンシューマー側のマルチAZ冗長の実体です。

6. セキュリティグループを設定する

エンドポイントに関連付けたセキュリティグループ(sg-0a1b2c3d4e5f12345)には、コンシューマーEC2からエンドポイントへの通信を許可するインバウンドルールを設定します。

# エンドポイント用SGのインバウンドルールを設定する(TCP/443をコンシューマーVPCのCIDRから許可) aws ec2 authorize-security-group-ingress \ --group-id sg-0a1b2c3d4e5f12345 \ --protocol tcp \ --port 443 \ --cidr 10.1.0.0/16 # エンドポイントのDNS名を一覧表示する aws ec2 describe-vpc-endpoints \ --vpc-endpoint-ids vpce-01234567890abcdef \ --query 'VpcEndpoints[0].DnsEntries[*].DnsName' # 出力(AZごとのDNS名も自動生成される) [ "vpce-01234567890abcdef-zz1yy2xx.ap-northeast-1.vpce.amazonaws.com", "vpce-01234567890abcdef-zz1yy2xx-ap-northeast-1a.ap-northeast-1.vpce.amazonaws.com", "vpce-01234567890abcdef-zz1yy2xx-ap-northeast-1c.ap-northeast-1.vpce.amazonaws.com" ]

エンドポイントのセキュリティグループはインバウンドルールのみ有効です。アウトバウンドはデフォルトで全許可になっているので、特別な理由がない限りそのままで問題ありません。

接続動作を確認する(EC2からのテスト)

エンドポイントの状態がavailableになったら、コンシューマーVPCのEC2からDNS名を使って接続できます。

7. エンドポイントのステータスを確認する

# エンドポイントのステータスを確認する(available になるまで1~2分かかる) aws ec2 describe-vpc-endpoints \ --vpc-endpoint-ids vpce-01234567890abcdef \ --query 'VpcEndpoints[0].State' # 出力 "available" # コンシューマーEC2からDNS名解決を確認する [ec2-user@ip-10-1-0-50 ~]$ nslookup vpce-01234567890abcdef-zz1yy2xx.ap-northeast-1.vpce.amazonaws.com Server: 10.1.0.2 Address: 10.1.0.2#53 Non-authoritative answer: Name: vpce-01234567890abcdef-zz1yy2xx.ap-northeast-1.vpce.amazonaws.com Address: 10.1.10.45 Name: vpce-01234567890abcdef-zz1yy2xx.ap-northeast-1.vpce.amazonaws.com Address: 10.1.20.67 # DNS解決結果がプライベートIPアドレス(10.x.x.x)であることを確認する # VPC外のパブリックIPには解決されていない = PrivateLink経由の通信になっている

8. EC2からHTTP接続を確認する

# curlでサービスのヘルスチェックエンドポイントに接続する [ec2-user@ip-10-1-0-50 ~]$ curl -v \ https://vpce-01234567890abcdef-zz1yy2xx.ap-northeast-1.vpce.amazonaws.com/api/health \ --resolve 'vpce-01234567890abcdef-zz1yy2xx.ap-northeast-1.vpce.amazonaws.com:443:10.1.10.45' # 接続が成功した場合の出力(抜粋) * Trying 10.1.10.45:443... * Connected to ... (10.1.10.45) port 443 * SSL handshake completed < HTTP/1.1 200 OK {"status":"healthy"}

プライベートIPアドレス(10.x.x.x)への接続が確認できればPrivateLink経由の通信が成立しています。インターネットを経由していないため、EC2にパブリックIPがなくてもアクセスできる点がNATゲートウェイ経由との大きな違いです。

マルチAZ冗長設計の考え方

PrivateLinkのマルチAZ冗長は、NLBとInterface型エンドポイントの両側に複数のAZを設定することで実現します。

NLB側: --subnetsに複数AZのサブネットを指定することでNLBがマルチAZ配置になる。AZ障害が発生すると正常なAZへのルーティングに自動で切り替わる
エンドポイント側: コンシューマーが--subnet-idsに複数AZのサブネットを指定することで各AZにENIが作成され、AZ単位のフェイルオーバーが可能になる
AZアフィニティ: デフォルトでコンシューマーのトラフィックは同一AZ内のENIを優先してルーティングされる(クロスAZデータ転送コストの削減効果がある)

注意点として、エンドポイントサービスの提供AZ(AvailabilityZones)とコンシューマーのエンドポイントサブネットが一致していないAZでは接続できません。事前にdescribe-vpc-endpoint-servicesで提供AZを確認しておきましょう。

# エンドポイントサービスが提供するAZを確認する(コンシューマー側から実行) aws ec2 describe-vpc-endpoint-services \ --service-names com.amazonaws.vpce.ap-northeast-1.vpce-svc-0a1b2c3d4e5f6a7b8 \ --query 'ServiceDetails[0].AvailabilityZones' # 出力(プロバイダーがap-northeast-1a・1cで提供していることを確認) [ "ap-northeast-1a", "ap-northeast-1c" ]

冗長設計の考え方をより実践的なアーキテクチャの観点から学ぶ方は、AWS上級セミナー(冗長設計編)もご参照ください。

設計上の注意点とコスト

ENI時間課金: Interface型エンドポイントはENIが存在する時間に対して課金される(東京リージョンでENI1個あたり約$0.01/時間)。不要なエンドポイントは削除して無駄な課金を避ける
データ処理料金: エンドポイントを経由したデータ量に対しても課金される。大量データを転送する場合はコストを試算してからPrivateLinkを採用するか判断すること
DNS設定の前提条件: --private-dns-enabledを使うにはVPCで「DNSホスト名の有効化」と「DNS解決の有効化」の両方がオンになっている必要がある
NAT不要: PrivateLink経由の通信にNATゲートウェイは不要。コンシューマーEC2にパブリックIPがなくても接続できる
承認設定: 本番環境では--no-acceptance-requiredを省略し(デフォルトで承認必須)、コンシューマーからの接続要求をLambdaとEventBridgeを組み合わせて自動承認する運用が安全
VPC DNS属性の確認: コンシューマーVPCでenableDnsHostnamesenableDnsSupportが無効の場合、プライベートDNSが機能しない

# VPCのDNS属性を確認する aws ec2 describe-vpc-attribute \ --vpc-id vpc-0f1e2d3c4b5a6b7c8 \ --attribute enableDnsHostnames aws ec2 describe-vpc-attribute \ --vpc-id vpc-0f1e2d3c4b5a6b7c8 \ --attribute enableDnsSupport # enableDnsHostnames の出力(trueであることが必要) { "VpcId": "vpc-0f1e2d3c4b5a6b7c8", "EnableDnsHostnames": { "Value": true } }

エンドポイントが接続できない時のトラブルシュート

PrivateLinkの設定後に接続できない場合、原因は大きく3つのパターンに分かれます。

「vpce-svc-xxx does not exist」エラーが出る場合

コンシューマー側でcreate-vpc-endpointを実行した際にサービスが見つからないエラーが出るケースです。

・プロバイダー側でコンシューマーアカウントのARNが許可リストに登録されているか確認する
describe-vpc-endpoint-service-permissionsでAllowedPrincipalsを確認する
・サービス名(com.amazonaws.vpce.リージョン.vpce-svc-xxx)に誤りがないか確認する

# プロバイダー側: 許可リストを確認する aws ec2 describe-vpc-endpoint-service-permissions \ --service-id vpce-svc-0a1b2c3d4e5f6a7b8 \ --query 'AllowedPrincipals[*].Principal' # 出力(コンシューマーアカウントのARNが含まれているか確認) [ "arn:aws:iam::999888777666:root" ]

エンドポイントのStateがpendingのまま変わらない場合

プロバイダー側でAcceptanceRequiredが有効になっているにもかかわらず、承認操作が行われていない場合に発生します。

・プロバイダー側でエンドポイント接続要求を確認して承認する

# プロバイダー側: 承認待ちの接続要求を確認する aws ec2 describe-vpc-endpoint-connections \ --filters Name=vpc-endpoint-service-id,Values=vpce-svc-0a1b2c3d4e5f6a7b8 \ --query 'VpcEndpointConnections[?VpcEndpointState==`pendingAcceptance`].[VpcEndpointId,VpcEndpointState]' # 承認操作を実行する aws ec2 accept-vpc-endpoint-connections \ --service-id vpce-svc-0a1b2c3d4e5f6a7b8 \ --vpc-endpoint-ids vpce-01234567890abcdef

DNS名がプライベートIPに解決されない場合

コンシューマーVPCで「DNSホスト名の有効化」または「DNS解決の有効化」が無効になっているケースです。

describe-vpc-attributeenableDnsHostnamesenableDnsSupportが両方trueであることを確認する
・どちらかがfalseの場合はmodify-vpc-attributeで有効化する

# DNS属性を有効化する(コンシューマーVPCで実行) aws ec2 modify-vpc-attribute \ --vpc-id vpc-0f1e2d3c4b5a6b7c8 \ --enable-dns-hostnames aws ec2 modify-vpc-attribute \ --vpc-id vpc-0f1e2d3c4b5a6b7c8 \ --enable-dns-support

本記事のまとめ

やりたいこと コマンド
NLBをマルチAZ配置で作成する aws elbv2 create-load-balancer --type network --subnets サブネット1 サブネット2 --scheme internal
エンドポイントサービスを作成する aws ec2 create-vpc-endpoint-service-configuration --network-load-balancer-arns NLBのARN
コンシューマーアカウントを許可する aws ec2 modify-vpc-endpoint-service-permissions --service-id vpce-svc-xxx --add-allowed-principals arn:aws:iam::ACCOUNT_ID:root
Interface型エンドポイントを作成する aws ec2 create-vpc-endpoint --vpc-endpoint-type Interface --service-name サービス名 --subnet-ids サブネット1 サブネット2
エンドポイントのDNS名を確認する aws ec2 describe-vpc-endpoints --vpc-endpoint-ids vpce-xxx --query 'VpcEndpoints[0].DnsEntries[*].DnsName'
提供AZを確認する aws ec2 describe-vpc-endpoint-services --service-names サービス名 --query 'ServiceDetails[0].AvailabilityZones'
AWS PrivateLinkは「CIDRが重複した複数VPC間での安全なサービス共有」「アカウントをまたいだ共有サービス基盤の構築」を実現するための重要な設計パターンです。NLBをマルチAZ配置にして、コンシューマー側のエンドポイントも複数AZのサブネットに展開することで、AZ障害に耐えられる高可用性構成が完成します。

VPC設計全般やAmazon Linux環境でのサーバー運用を体系的に習得したい方は、Amazon Linux × AWSの実践的な使い方からはじめるのがおすすめです。

AWS PrivateLinkを実務で使いこなすには、VPC設計の「型」が必要です

PrivateLinkでVPC間をセキュアに接続するには、VPCの設計原則・セキュリティグループ・ルーティングを体系的に理解することが重要です。断片的な知識の寄せ集めではなく、現場で実際に使われる設計パターンを一度体系的に身につけることで、安全なクラウド基盤が構築できるようになります。
現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、『Linuxサーバー構築入門マニュアル(図解60P)』を完全無料でプレゼントしています。

「独学の時間がもったいない」「プロから直接、現場の技術を最短で学びたい」という本気の方には、2日で実務レベルのスキルが身につく【初心者向けハンズオンセミナー】も開催しています。

無料メルマガで学習を続ける

Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。

登録無料・いつでも解除できます

暗記不要・1時間後にはサーバーが動く

3,100名以上が実践した「型」を無料で公開中

プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。

姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

Linux無料マニュアル(図解60P) 名前とメールで30秒登録
宮崎 智広

この記事を書いた人

宮崎 智広(みやざき ともひろ)

株式会社イーネットマーキュリー代表。現役のLinuxサーバー管理者として20年以上の実務経験を持ち、これまでに累計3,100名以上のエンジニアを指導してきたLinux教育のプロフェッショナル。「現場で本当に使える技術」を体系的に伝えることをモットーに、実践型のLinuxセミナーの開催や無料マニュアルの配布を通じてLinux人材の育成に取り組んでいる。

趣味は、キャンプにカメラ、トラウト釣り。好きな食べ物は、ラーメンにお酒。休肝日が作れない、酒量を減らせないのが悩み。最近、ドラマ「フライトエンジェル」を観て涙腺が崩壊しました。