VPC LambdaはDHCPオプションセットの影響を受ける件
VPC Lambdaが参照するDNSサーバーはDHCPオプションセットで指定したものなのだろうか
こんにちは、のんピ(@non____97)です。
皆さんはVPC LambdaはDHCPオプションセットの影響を受けるか気になったことはありますか? 私はあります。
DHCPオプションセットを使うことで、DHCPオプションセットを割り当てているVPC上の全てのEC2インスタンスが参照するDNSサーバーを簡単に設定することができます。
特にオンプレミス上のDNSサーバーを参照させる場合に便利です。
この場合、他にもRoute 53 Resolver Outbound Endpointを使う案もあります。私はRoute 53 Resolver Endpoint推しですが、お値段がそれなりにかかります。(0.125 USD/h/ENI)
一方、DHCPオプションセットを使えば無料で参照先のDNSサーバーを指定することが可能です。
そんな便利なDHCPオプションセットですが、EC2インスタンス以外にLamddaにも影響を与えるのでしょうか。
VPCのDHCPオプションセットには「VPC内のEC2インスタンスが」と記載があります。
DHCP オプションセットは、VPC 内の EC2 インスタンスが仮想ネットワーク経由で通信するために使用するネットワーク構成のグループです。
一方で、VPC LambdaのトラブルシューティングにはDHCPオプションセットがLambdaの動作に影響を与えるような記載がされています。
重要:カスタムの動的ホスト構成プロトコル (DHCP) オプションセットを使用している場合は、カスタム DNS サーバーが期待どおりに動作していることを確認します。
VPC にカスタム DHCP オプションセットを使用している場合は、Amazon Route 53 Resolver クエリログを使用して DNS クエリ応答を確認してください。
また、「VPCを作成するとLambdaが自動的にDHCPオプションセットを作成してVPCに関連付ける」といった記載もありました。
When you create a VPC, Lambda automatically creates a set of DHCP options and associates them with the VPC. You can configure your own DHCP options set for your VPC. For more details, refer to Amazon VPC DHCP options.
きっとDHCPオプションセットの影響を受けるんでしょうが、気になったので確認してみます。
いきなりまとめ
- VPC LambdaはDHCPオプションセットのDNSサーバーの設定値の影響を受ける
- ただし、LambdaからDHCPオプションセットで指定したDNSサーバーに対して直接問い合わせは行わない
- リンクローカルアドレスのフォワーダーを介して、指定したDNSサーバーにフォワーディングして名前解決をする
- リンクローカルアドレスのフォワーダーからどこにフォワーディングするかはLambda内の
/etc/resolv.conf
からも確認できる
- Lambda実行時のCloudWatch Logsに出力されるログは、CloudWatch Logsのサービスエンドポイントへの名前解決ができなくとも行える
- おそらくLambdaのService VPC上のHyperplane ENIで名前解決と、実際の通信が行われている
- 一方、名前解決できない場合、Lambdaの処理でログストリームを作成しようとしても失敗する
- Lambdaの処理の中で参照先のDNSサーバーを変更する方法はない認識
- DHCPオプションセットの影響範囲を気につけよう
検証環境
検証環境は以下のとおりです。
VPC Lambda(Node.js 18)を作成しました。VPC LambdaのENIにはElastic IPアドレスを割り当てて、インターネットに抜けられるようにしています。
DHCPオプションセットはVPCのデフォルトのものと、DNSサーバーに存在しないIPアドレスを指定したものの2つを用意しました。
IAMロールはVPC Lambda作成時にデフォルトで作成されるものです。以下IAMポリシーがアタッチされていました。
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "logs:CreateLogGroup", "Resource": "arn:aws:logs:us-east-1:<AWSアカウントID>:*" }, { "Effect": "Allow", "Action": [ "logs:CreateLogStream", "logs:PutLogEvents" ], "Resource": [ "arn:aws:logs:us-east-1:<AWSアカウントID>:log-group:/aws/lambda/vpc-lambda:*" ] } ] }
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "ec2:CreateNetworkInterface", "ec2:DeleteNetworkInterface", "ec2:DescribeNetworkInterfaces" ], "Resource": "*" } ] }
やってみた
VPC Lambdaが参照しているDNSサーバーを確認する
まず、VPC Lambdaが参照しているDNSサーバーを確認してみます。
DHCPオプションセットはデフォルトのもの = DNSサーバー(フォワーダー & フルサービスリゾルバ)はRoute 53 Resolverです。
使用するコードは以下のとおりです。
import * as dns from 'dns'; export const handler = async (event) => { console.log(dns.getServers()) };
実行結果は以下のとおりです。
START RequestId: afaae791-f2ca-4ff7-a0ea-51f254e3d0a7 Version: $LATEST 2023-08-12T03:11:38.081Z afaae791-f2ca-4ff7-a0ea-51f254e3d0a7 INFO [ '169.254.78.1' ] END RequestId: afaae791-f2ca-4ff7-a0ea-51f254e3d0a7 REPORT RequestId: afaae791-f2ca-4ff7-a0ea-51f254e3d0a7 Duration: 58.03 ms Billed Duration: 59 ms Memory Size: 128 MB Max Memory Used: 66 MB Init Duration: 177.40 ms
169.254.78.1
というIPアドレスがDNSサーバーとして返ってきました。何回実行しても169.254.78.1
でした。
Route 53 ResolverのIPアドレスは169.254.169.253
もしくはfd00:ec2::253
、VPCのCIDR+2です。
Route 53 Resolver (「Amazon DNS サーバー」または「AmazonProvidedDNS」とも呼ばれます) は、AWS リージョン内の各アベイラビリティーゾーンに組み込まれている DNS リゾルバーサービスです。Route 53 Resolver は 169.254.169.253 (IPv4)、fd00:ec2::253 (IPv6)、および VPC+2 にプロビジョニングされたプライマリプライベート IPV4 CIDR 範囲に配置されています。例えば、IPv4 CIDR が 10.0.0.0/16 で、IPv6 CIDR が fd00:ec2::253 の VPC がある場合、Route 53 Resolver には 169.254.169.253 (IPv4)、fd00:ec2::253 (IPv6)、または 10.0.0.2 (IPv4) でアクセスできます。
169.254.78.1
というIPアドレスであることから、リンクローカルアドレスであることが分かります。
VPC Lambdaではない、通常のLambdaでも同じコードで実行してみます。
START RequestId: 37a49730-5a48-4e36-8db9-62fd2d46e8c2 Version: $LATEST 2023-08-12T03:13:05.337Z 37a49730-5a48-4e36-8db9-62fd2d46e8c2 INFO [ '169.254.78.1' ] END RequestId: 37a49730-5a48-4e36-8db9-62fd2d46e8c2 REPORT RequestId: 37a49730-5a48-4e36-8db9-62fd2d46e8c2 Duration: 55.48 ms Billed Duration: 56 ms Memory Size: 128 MB Max Memory Used: 66 MB Init Duration: 178.67 ms
同じく169.254.78.1
でした。
VPC上かどうかに関わらず同じIPアドレスが使われるということは、こちらのIPアドレスはLamdaのService VPC上で動作するフォワーダーなのでしょうか。
VPC Lambdaが参照するDNSサーバーを変更してみる
次にVPC Lambdaが参照するDNSサーバーを8.8.8.8
に変更してみましょう。
以下のコードでLambdaを実行します。
import * as dns from 'dns'; export const handler = async (event) => { console.log(dns.getServers()) console.log(await dns.promises.resolve4('dev.classmethod.jp')); dns.setServers(['8.8.8.8']) console.log(dns.getServers()) console.log(await dns.promises.resolve4('dev.classmethod.jp')); };
実行結果は以下のとおりです。
START RequestId: 6dc9c86f-7198-409f-9ac1-107e21042c60 Version: $LATEST 2023-08-12T03:17:28.178Z 6dc9c86f-7198-409f-9ac1-107e21042c60 INFO [ '169.254.78.1' ] 2023-08-12T03:17:28.226Z 6dc9c86f-7198-409f-9ac1-107e21042c60 INFO [ '13.248.175.13', '76.223.57.58' ] 2023-08-12T03:17:28.243Z 6dc9c86f-7198-409f-9ac1-107e21042c60 INFO [ '169.254.78.1' ] 2023-08-12T03:17:28.263Z 6dc9c86f-7198-409f-9ac1-107e21042c60 INFO [ '13.248.175.13', '76.223.57.58' ] END RequestId: 6dc9c86f-7198-409f-9ac1-107e21042c60 REPORT RequestId: 6dc9c86f-7198-409f-9ac1-107e21042c60 Duration: 90.84 ms Billed Duration: 91 ms Memory Size: 128 MB Max Memory Used: 66 MB Init Duration: 184.91 ms
8.8.8.8
に変更後のdns.getServers()
も変わらず169.254.78.1
でした。名前解決の結果も同じなので本当に変更されているのか分かりませんね。
一時的にVPC LambdaのENIへのElastic IPアドレスの関連付けを解除して再実行します。
START RequestId: c98dfe39-f3ed-43a7-a873-ac43a8012a59 Version: $LATEST 2023-08-12T03:21:41.388Z c98dfe39-f3ed-43a7-a873-ac43a8012a59 INFO [ '169.254.78.1' ] 2023-08-12T03:21:41.419Z c98dfe39-f3ed-43a7-a873-ac43a8012a59 INFO [ '13.248.175.13', '76.223.57.58' ] 2023-08-12T03:21:41.428Z c98dfe39-f3ed-43a7-a873-ac43a8012a59 INFO [ '169.254.78.1' ] 2023-08-12T03:21:44.367Z c98dfe39-f3ed-43a7-a873-ac43a8012a59 Task timed out after 3.01 seconds END RequestId: c98dfe39-f3ed-43a7-a873-ac43a8012a59 REPORT RequestId: c98dfe39-f3ed-43a7-a873-ac43a8012a59 Duration: 3007.92 ms Billed Duration: 3000 ms Memory Size: 128 MB Max Memory Used: 66 MB Init Duration: 174.21 ms
8.8.8.8
を指定した後に名前解決をした場合はタイムアウトになりましたね。Elastic IPアドレスが関連付けられていないがためにインターネットに抜けることができず、8.8.8.8
にアクセスできないためです。
つまりは、Lambda関数内で参照するDNSサーバーを変更することはできそうです。
挙動からして、リンクローカルアドレスのフォワーダーを介して、指定したDNSサーバーにフォワーディングして名前解決をしているのだと推測します。
DHCPオプションセットでDNSサーバーを指定してみる
それでは、本題のDHCPオプションセットでDNSサーバーを指定してみます。
指定するDNSサーバーは存在しないIPアドレス10.1.1.10
を指定します。つまりは名前解決をしようとすると必ず失敗する状態です。
処理の中で参照先DNSサーバーを8.8.8.8
に変更して、再度名前解決をしてみます。
実行するコードは以下です。
import * as dns from "dns"; async function resolveName() { try { const resolvePromise = dns.promises.resolve4("dev.classmethod.jp"); const timeoutPromise = new Promise((_, reject) => setTimeout(() => reject("Timeout"), 1000) ); console.log(await Promise.race([resolvePromise, timeoutPromise])); } catch (e) { console.log(e); } } export const handler = async (event) => { console.log(dns.getServers()); await resolveName(); dns.setServers(["8.8.8.8"]); console.log(dns.getServers()); await resolveName(); };
実行結果は以下のとおりです。
START RequestId: 233e77fa-f5d5-443f-96d6-5e5a5d1cb522 Version: $LATEST 2023-08-12T03:40:06.192Z 233e77fa-f5d5-443f-96d6-5e5a5d1cb522 INFO [ '169.254.78.1' ] 2023-08-12T03:40:07.213Z 233e77fa-f5d5-443f-96d6-5e5a5d1cb522 INFO Timeout 2023-08-12T03:40:07.214Z 233e77fa-f5d5-443f-96d6-5e5a5d1cb522 INFO [ '169.254.78.1' ] 2023-08-12T03:40:07.229Z 233e77fa-f5d5-443f-96d6-5e5a5d1cb522 INFO [ '76.223.57.58', '13.248.175.13' ] END RequestId: 233e77fa-f5d5-443f-96d6-5e5a5d1cb522 REPORT RequestId: 233e77fa-f5d5-443f-96d6-5e5a5d1cb522 Duration: 1077.43 ms Billed Duration: 1078 ms Memory Size: 128 MB Max Memory Used: 66 MB Init Duration: 179.23 ms
最初に名前解決をした時 = DHCPオプションセットで指定したDNSサーバーを使った名前解決はタイムアウトしてしまいましたが、8.8.8.8
に変更した場合の名前解決は成功しています。ちなみに、dns.setServers(['169.254.169.253'])
に変更しても結果は同じでした。
つまりはDHCPオプションセットの影響を受けることが判明しました。
Hyperplane ENIからENIにNATされたのち、DHCPオプションセットに従い名前解決をするのですね。
抜粋 : Announcing improved VPC networking for AWS Lambda functions | AWS Compute Blog
Hyperplaneの詳細は以下記事をご覧ください。
もう少し確認してみましょう。
Lambdaの/etc/resolv.conf
を表示します。
使用するコードは以下のとおりです。
import { execSync } from "child_process" export const handler = async (event) => { console.log(`cat /etc/resolv.conf : ${execSync("cat /etc/resolv.conf").toString()}`) };
実行結果は以下のとおりです。
START RequestId: 3e645921-a5ad-4368-9245-716a4d809ae3 Version: $LATEST 2023-08-13T05:09:43.680Z 3e645921-a5ad-4368-9245-716a4d809ae3 INFO cat /etc/resolv.conf : options timeout:2 attempts:5 ; generated by /sbin/dhclient-script ; configured nameserver 10.1.1.10 nameserver 169.254.78.1 END RequestId: 3e645921-a5ad-4368-9245-716a4d809ae3 REPORT RequestId: 3e645921-a5ad-4368-9245-716a4d809ae3 Duration: 120.05 ms Billed Duration: 121 ms Memory Size: 128 MB Max Memory Used: 67 MB Init Duration: 199.74 ms
; configured nameserver 10.1.1.10
とDHCPオプションセットで指定したDNSサーバーのIPアドレスが記載されています。
DHCPオプションセットをデフォルトのものに変更して再実行します。実行結果は以下のとおりです。
START RequestId: 11b65181-2e97-470c-92b1-9730dbcae49c Version: $LATEST 2023-08-13T05:08:24.101Z 11b65181-2e97-470c-92b1-9730dbcae49c INFO cat /etc/resolv.conf : options timeout:2 attempts:5 ; generated by /sbin/dhclient-script search ec2.internal ; configured nameserver 10.0.0.2 nameserver 169.254.78.1 END RequestId: 11b65181-2e97-470c-92b1-9730dbcae49c REPORT RequestId: 11b65181-2e97-470c-92b1-9730dbcae49c Duration: 111.19 ms Billed Duration: 112 ms Memory Size: 128 MB Max Memory Used: 67 MB Init Duration: 196.46 ms
; configured nameserver 10.0.0.2
とIPアドレスがVPC CIDR +2 のアドレスに変わりましたね。
ちなみに/etc/resolv.conf
を無理やり書き換えようとすると怒られます。また、Lambda関数にsudo
はありません。
START RequestId: 4fc7ef64-84c8-42c7-b92c-1fb7550e3847 Version: $LATEST 2023-08-13T05:42:07.288Z 4fc7ef64-84c8-42c7-b92c-1fb7550e3847 INFO cat /etc/resolv.conf : /etc/resolv.conf 2023-08-13T05:42:07.467Z 4fc7ef64-84c8-42c7-b92c-1fb7550e3847 INFO ls -l /etc/resolv.conf : -rw-r--r-- 1 root root 124 Aug 13 05:42 /etc/resolv.conf sed: couldn't open temporary file /etc/sedeFyYCN: Read-only file system 2023-08-13T05:42:07.530Z 4fc7ef64-84c8-42c7-b92c-1fb7550e3847 ERROR Invoke Error {"errorType":"Error","errorMessage":"Command failed: sed -i 's/nameserver 169.254.78.1/nameserver 8.8.8.8/g' /etc/resolv.conf\nsed: couldn't open temporary file /etc/sedeFyYCN: Read-only file system\n","status":4,"signal":null,"output":[null,{"type":"Buffer","data":[]},{"type":"Buffer","data":[115,101,100,58,32,99,111,117,108,100,110,39,116,32,111,112,101,110,32,116,101,109,112,111,114,97,114,121,32,102,105,108,101,32,47,101,116,99,47,115,101,100,101,70,121,89,67,78,58,32,82,101,97,100,45,111,110,108,121,32,102,105,108,101,32,115,121,115,116,101,109,10]}],"pid":25,"stdout":{"type":"Buffer","data":[]},"stderr":{"type":"Buffer","data":[115,101,100,58,32,99,111,117,108,100,110,39,116,32,111,112,101,110,32,116,101,109,112,111,114,97,114,121,32,102,105,108,101,32,47,101,116,99,47,115,101,100,101,70,121,89,67,78,58,32,82,101,97,100,45,111,110,108,121,32,102,105,108,101,32,115,121,115,116,101,109,10]},"stack":["Error: Command failed: sed -i 's/nameserver 169.254.78.1/nameserver 8.8.8.8/g' /etc/resolv.conf","sed: couldn't open temporary file /etc/sedeFyYCN: Read-only file system",""," at checkExecSyncError (node:child_process:885:11)"," at execSync (node:child_process:957:15)"," at Runtime.handler (file:///var/task/index.mjs:6:93)"," at Runtime.handleOnceNonStreaming (file:///var/runtime/index.mjs:1083:29)"]} END RequestId: 4fc7ef64-84c8-42c7-b92c-1fb7550e3847 REPORT RequestId: 4fc7ef64-84c8-42c7-b92c-1fb7550e3847 Duration: 362.74 ms Billed Duration: 363 ms Memory Size: 128 MB Max Memory Used: 69 MB Init Duration: 199.77 ms
名前解決はできなくともLambdaの実行ログはCloudWatch Logsに出力できている謎
ここでふと、「DHCPオプションセットで存在しないDNSサーバーを参照させているのに、どうしてLambdaの実行ログはCloudWatch Logsに出力できているのか」と疑問に思いました。
LambdaからCloudWatch Logsへの通信は特別扱いなのでしょうか。それとも、Lambdaの実行ログのみ名前解決できなくともCloudWatch Logsに出力できるのでしょうか。
動作確認をして謎に迫ってみます。
DCHPオプションセットをデフォルトのものにします。
この状態でCloudWatch Logsのサービスエンドポイントの名前解決と、CloudWatch Logsのログストリームを作成するLambda関数を実行します。
実際のコードは以下のとおりです。
import * as dns from "dns"; import { CloudWatchLogsClient, CreateLogStreamCommand, } from "@aws-sdk/client-cloudwatch-logs"; const logsClient = new CloudWatchLogsClient({region : "us-east-1"}); async function resolveName() { try { const resolvePromise = dns.promises.resolve4("logs.us-east-1.amazonaws.com"); const timeoutPromise = new Promise((_, reject) => setTimeout(() => reject("Timeout"), 500) ); console.log(await Promise.race([resolvePromise, timeoutPromise])); } catch (e) { console.log(e); } } async function createLogStream(logGroupName, logStreamName) { const params = { logGroupName, logStreamName, }; const command = new CreateLogStreamCommand(params); const timeoutPromise = new Promise((_, reject) => setTimeout(() => reject("PutLogEvents Timeout"), 500) ); try { const response = await Promise.race([ logsClient.send(command), timeoutPromise, ]); console.log(response) console.log(`Log stream ${logStreamName} created in group ${logGroupName}`); } catch (e) { console.log(e); } } export const handler = async (event) => { console.log(dns.getServers()); await resolveName(); await createLogStream( "/aws/lambda/vpc-lambda", `test-log-stream-${new Date().getTime().toString()}` ); };
実行結果は以下のとおりです。
START RequestId: 9a182f92-18e5-4eb1-bd73-2cada974654f Version: $LATEST 2023-08-13T02:34:55.398Z 9a182f92-18e5-4eb1-bd73-2cada974654f INFO [ '169.254.78.1' ] 2023-08-13T02:34:55.456Z 9a182f92-18e5-4eb1-bd73-2cada974654f INFO [ '44.202.79.238', '44.202.79.255', '3.236.94.136', '3.236.94.178', '44.202.79.141', '44.202.79.161', '3.236.94.129', '3.236.94.166' ] 2023-08-13T02:34:55.958Z 9a182f92-18e5-4eb1-bd73-2cada974654f INFO { '$metadata': { httpStatusCode: 200, requestId: '77b8ced1-2de3-4ff3-8302-81738eec2c67', extendedRequestId: undefined, cfId: undefined, attempts: 1, totalRetryDelay: 0 } } 2023-08-13T02:34:55.958Z 9a182f92-18e5-4eb1-bd73-2cada974654f INFO Log stream test-log-stream-1691894095457 created in group /aws/lambda/vpc-lambda END RequestId: 9a182f92-18e5-4eb1-bd73-2cada974654f REPORT RequestId: 9a182f92-18e5-4eb1-bd73-2cada974654f Duration: 615.97 ms Billed Duration: 616 ms Memory Size: 128 MB Max Memory Used: 91 MB Init Duration: 570.62 ms
名前解決ができており、ログストリームの作成もできていそうですね。
続いて、存在しないIPアドレス10.1.1.10
をDNSサーバーに指定したDHCPオプションセットに変更して再実行します。
実行結果は以下のとおりです。
START RequestId: 898dd7de-cf17-4126-8238-4421fe5fd492 Version: $LATEST 2023-08-13T02:35:49.934Z 898dd7de-cf17-4126-8238-4421fe5fd492 INFO [ '169.254.78.1' ] 2023-08-13T02:35:50.474Z 898dd7de-cf17-4126-8238-4421fe5fd492 INFO Timeout 2023-08-13T02:35:50.976Z 898dd7de-cf17-4126-8238-4421fe5fd492 INFO PutLogEvents Timeout END RequestId: 898dd7de-cf17-4126-8238-4421fe5fd492 REPORT RequestId: 898dd7de-cf17-4126-8238-4421fe5fd492 Duration: 1054.27 ms Billed Duration: 1055 ms Memory Size: 128 MB Max Memory Used: 90 MB Init Duration: 549.33 ms
サービスエンドポイントの名前解決、ログストリームの作成のどちらも失敗しています。
しかし、ご覧の通りLambda関数の実行ログはCloudWatch Logsに出力されています。
そのため、Lambdaの実行ログの出力の場合のみ、LambdaのService VPC上のHyperplane ENIで名前解決と、実際の通信が行われているのだと考えます。
おまけ : Lambda上でnslookupを実行してみる
Lambda上でdig
やnslookup
を実行することはできません。実行しようとしても「そんなコマンドはない」と怒られます。
どうしても実行してみたかったので、コマンドやライブラリをzipで固めてLambda Layerに登録します。
Node.js 18のマネージドランタイムはAmazon Linux 2なので、Amazon linux 2のコンテナイメージから必要なコマンドやライブラリを引っ張ってきます。
使用するDockerfileは以下のとおりです。
FROM public.ecr.aws/amazonlinux/amazonlinux:2 RUN yum install bind-utils which -y RUN mkdir /layer/ && \ ldd $(which nslookup) > /layer/libraries.txt RUN mkdir -p /layer/lib && \ for lib in $(awk '{ print $3 }' /layer/libraries.txt | sed '/^$/d'); do \ if [ -f "$lib" ]; then \ cp -L "$lib" /layer/lib/; \ fi \ done RUN mkdir -p /layer/bin RUN cp /usr/bin/nslookup /layer/bin/
こちらのDockerfileを使ってビルドを行い、ビルド結果からコマンドやライブラリを抽出してzipで固めます。
> docker build -t lambda-layer . [+] Building 16.0s (10/10) FINISHED => [internal] load build definition from Dockerfile 0.0s => => transferring dockerfile: 535B 0.0s => [internal] load .dockerignore 0.0s => => transferring context: 2B 0.0s => [internal] load metadata for public.ecr.aws/amazonlinux/amazonlinux:2 0.2s => CACHED [1/6] FROM public.ecr.aws/amazonlinux/amazonlinux:2@sha256:fc2f8916205f191471880b45ca3c34fc13fa895ac75abdcb31e3c97 0.0s => [2/6] RUN yum install bind-utils which -y 13.7s => [3/6] RUN mkdir /layer/ && ldd $(which nslookup) > /layer/libraries.txt 0.4s => [4/6] RUN mkdir -p /layer/lib && for lib in $(awk '{ print $3 }' /layer/libraries.txt | sed '/^$/d'); do if [ -f "$li 0.4s => [5/6] RUN mkdir -p /layer/bin 0.3s => [6/6] RUN cp /usr/bin/nslookup /layer/bin/ 0.4s => exporting to image 0.5s => => exporting layers 0.5s => => writing image sha256:0ffcace840ed8413fdbb96a90f5926197dd32cdb297841666430ab7cefeb2459 0.0s => => naming to docker.io/library/lambda-layer 0.0s > docker create --name temp-container lambda-layer 72b207b6974f49ccc142b8603c2b8327206ed266d83c80cdaef578fdb51752d8 > docker cp temp-container:/layer . Preparing to copy... Copying from container - 32.77kB Copying from container - 65.54kB Copying from container - 98.3kB . . (中略) . . Copying from container - 13.47MB Copying from container - 13.5MB Copying from container - 13.51MB Successfully copied 13.51MB to /<ディレクトリパス>/. > zip -r layer.zip ./layer adding: layer/ (stored 0%) adding: layer/.DS_Store (deflated 95%) adding: layer/libraries.txt (deflated 75%) adding: layer/bin/ (stored 0%) . . (中略) . . adding: layer/lib/libcap.so.2 (deflated 74%) adding: layer/lib/libdl.so.2 (deflated 91%) adding: layer/lib/libbind9.so.160 (deflated 58%) adding: layer/lib/libisc.so.169 (deflated 58%)
作成したzipファイルを使ってLambda Layerを作成します。
作成したLambda LayerをLambda関数に割り当てます。
割り当て後、このままだとLambda Layer上のライブラリのパス/opt/layer/lib
を見てくれないので、環境変数LD_LIBRARY_PATH
で/opt/layer/lib:/lib64:/usr/lib64:$LAMBDA_RUNTIME_DIR:$LAMBDA_RUNTIME_DIR/lib:$LAMBDA_TASK_ROOT:$LAMBDA_TASK_ROOT/lib:/opt/lib
を指定します。
LD_LIBRARY_PATH
は以下AWS公式ドキュメントを参考にしました。
この状態で以下コードを実行します。
import { execSync } from "child_process" export const handler = async (event) => { console.log(`echo $LD_LIBRARY_PATH" : ${execSync("echo $LD_LIBRARY_PATH").toString()}`) console.log(`cat /etc/resolv.conf : ${execSync("cat /etc/resolv.conf").toString()}`) console.log(`/opt/layer/bin/nslookup logs.us-east-1.amazonaws.com -debug : ${execSync("/opt/layer/bin/nslookup logs.us-east-1.amazonaws.com -debug").toString()}`) };
DHCPオプションセットはデフォルトのものに設定してLambdaを実行します。実行結果は以下のとおりです。
START RequestId: 570c56f5-58aa-4a38-aa53-b515d3ad9fde Version: $LATEST 2023-08-13T09:40:26.712Z 570c56f5-58aa-4a38-aa53-b515d3ad9fde INFO echo $LD_LIBRARY_PATH" : /opt/layer/lib:/lib64:/usr/lib64:$LAMBDA_RUNTIME_DIR:$LAMBDA_RUNTIME_DIR/lib:$LAMBDA_TASK_ROOT:$LAMBDA_TASK_ROOT/lib:/opt/lib 2023-08-13T09:40:26.790Z 570c56f5-58aa-4a38-aa53-b515d3ad9fde INFO cat /etc/resolv.conf : options timeout:2 attempts:5 ; generated by /sbin/dhclient-script search ec2.internal ; configured nameserver 10.0.0.2 nameserver 169.254.78.1 2023-08-13T09:40:27.990Z 570c56f5-58aa-4a38-aa53-b515d3ad9fde INFO /opt/layer/bin/nslookup logs.us-east-1.amazonaws.com -debug : Server: 169.254.78.1 Address: 169.254.78.1#53 ------------ QUESTIONS: logs.us-east-1.amazonaws.com, type = A, class = IN ANSWERS: -> logs.us-east-1.amazonaws.com internet address = 3.236.94.231 ttl = 18 -> logs.us-east-1.amazonaws.com internet address = 3.236.94.176 ttl = 18 -> logs.us-east-1.amazonaws.com internet address = 3.236.94.198 ttl = 18 -> logs.us-east-1.amazonaws.com internet address = 44.202.79.147 ttl = 18 -> logs.us-east-1.amazonaws.com internet address = 3.236.94.242 ttl = 18 -> logs.us-east-1.amazonaws.com internet address = 3.236.94.226 ttl = 18 -> logs.us-east-1.amazonaws.com internet address = 3.236.94.233 ttl = 18 -> logs.us-east-1.amazonaws.com internet address = 3.236.94.237 ttl = 18 AUTHORITY RECORDS: ADDITIONAL RECORDS: ------------ Non-authoritative answer: Name: logs.us-east-1.amazonaws.com Address: 3.236.94.231 Name: logs.us-east-1.amazonaws.com Address: 3.236.94.176 Name: logs.us-east-1.amazonaws.com Address: 3.236.94.198 Name: logs.us-east-1.amazonaws.com Address: 44.202.79.147 Name: logs.us-east-1.amazonaws.com Address: 3.236.94.242 Name: logs.us-east-1.amazonaws.com Address: 3.236.94.226 Name: logs.us-east-1.amazonaws.com Address: 3.236.94.233 Name: logs.us-east-1.amazonaws.com Address: 3.236.94.237 ------------ QUESTIONS: logs.us-east-1.amazonaws.com, type = AAAA, class = IN ANSWERS: AUTHORITY RECORDS: -> logs.us-east-1.amazonaws.com origin = ns-140.awsdns-17.com mail addr = awsdns-hostmaster.amazon.com serial = 1 refresh = 7200 retry = 900 expire = 1209600 minimum = 60 ttl = 4 ADDITIONAL RECORDS: ------------ END RequestId: 570c56f5-58aa-4a38-aa53-b515d3ad9fde REPORT RequestId: 570c56f5-58aa-4a38-aa53-b515d3ad9fde Duration: 1342.54 ms Billed Duration: 1343 ms Memory Size: 128 MB Max Memory Used: 81 MB Init Duration: 192.16 ms
正常にnslookup
コマンドが叩けていますね。
続いて、存在しないIPアドレス10.1.1.10
をDNSサーバーに指定したDHCPオプションセットに変更して再実行します。
実行結果は以下のとおりです。
START RequestId: 7b9fc2bb-5414-415b-abd9-11bfee6df925 Version: $LATEST 2023-08-13T09:45:20.083Z 7b9fc2bb-5414-415b-abd9-11bfee6df925 INFO echo $LD_LIBRARY_PATH" : /opt/layer/lib:/lib64:/usr/lib64:$LAMBDA_RUNTIME_DIR:$LAMBDA_RUNTIME_DIR/lib:$LAMBDA_TASK_ROOT:$LAMBDA_TASK_ROOT/lib:/opt/lib 2023-08-13T09:45:20.163Z 7b9fc2bb-5414-415b-abd9-11bfee6df925 INFO cat /etc/resolv.conf : options timeout:2 attempts:5 ; generated by /sbin/dhclient-script ; configured nameserver 10.1.1.10 nameserver 169.254.78.1 2023-08-13T09:45:23.020Z 7b9fc2bb-5414-415b-abd9-11bfee6df925 Task timed out after 3.01 seconds END RequestId: 7b9fc2bb-5414-415b-abd9-11bfee6df925 REPORT RequestId: 7b9fc2bb-5414-415b-abd9-11bfee6df925 Duration: 3006.09 ms Billed Duration: 3000 ms Memory Size: 128 MB Max Memory Used: 80 MB Init Duration: 200.86 ms
タイムアウトしました。存在しないDNSサーバーに問い合わせようとしているので当然です。
Lambdaの処理の中で参照先のDNSサーバーを変更する方法はない認識なので、DHCPオプションセットをカスタマイズするときは気をつけよう
VPC LambdaはDHCPオプションセットの影響を受けるかどうか検証してみました。
Lambdaの処理の中で参照先のDNSサーバーを変更する方法はない認識です。DHCPオプションセットをカスタマイズする場合は、影響範囲を気につけましょう。
この記事が誰かの助けになれば幸いです。
以上、AWS事業本部 コンサルティング部の のんピ(@non____97)でした!