internal TheEfsLambdaStack(Construct scope, string id, IStackProps props = null) : base(scope, id, props) { // EFS needs to be setup in a VPC with 2Azs _vpc = new EC2.Vpc(this, "Vpc", new EC2.VpcProps { MaxAzs = 2 }); // Create a file system in EFS to store information _fileSystem = new EFS.FileSystem(this, "Filesystem", new EFS.FileSystemProps { Vpc = _vpc, RemovalPolicy = RemovalPolicy.DESTROY }); // Create a access point to EFS EFS.AccessPoint accessPoint; accessPoint = _fileSystem.AddAccessPoint("AccessPoint", new EFS.AccessPointOptions { CreateAcl = new EFS.Acl { OwnerGid = "1001", OwnerUid = "1001", Permissions = "750" }, Path = "/export/lambda", PosixUser = new EFS.PosixUser { Gid = "1001", Uid = "1001", } }); // Create the lambda function _functionProxyHandler = new Lambda.Function(this, "efsLambdaFunction", new Lambda.FunctionProps { Runtime = Lambda.Runtime.PYTHON_3_8, Code = Lambda.Code.FromAsset("lambda_fns"), Handler = "message_wall.lambda_handler", Vpc = _vpc, Filesystem = Lambda.FileSystem.FromEfsAccessPoint(accessPoint, "/mnt/msg") }); // Api Gateway HTTP integration _apiGateway = new APIGv2.HttpApi(this, "EFS Lambda", new APIGv2.HttpApiProps { DefaultIntegration = new APIGv2Integration.LambdaProxyIntegration(new APIGv2Integration.LambdaProxyIntegrationProps { Handler = _functionProxyHandler }) }); // Output to CFN new CfnOutput(this, "HTTP API Url", new CfnOutputProps { Value = _apiGateway.Url }); }
internal AppStack(Construct scope, string id, IStackProps props = null) : base(scope, id, props) { // 面倒くさいのでスタックは分けない。 var vpc = new Vpc(this, "Vpc"); var efs = new EFS.FileSystem(this, "Efs", new EFS.FileSystemProps() { Vpc = vpc, }); var efsUser = new PosixUser() { Gid = "1001", Uid = "1001", }; var efsCreateAcl = new Acl() { OwnerGid = "1001", OwnerUid = "1001", Permissions = "755", }; var efsAccessPoint = new EFS.AccessPoint(this, "EfsAccessPoint", new EFS.AccessPointProps() { FileSystem = efs, // 他の設定そのままで "/" では書き込み権限が得られていなかった。 // CDK上ではなく、NFSマウント後にルートユーザーで権限を操作すればよい。 // (ルートディレクトリは既定でルートユーザーが所有している状態) // See. https://docs.aws.amazon.com/ja_jp/efs/latest/ug/using-fs.html // https://docs.aws.amazon.com/ja_jp/efs/latest/ug/accessing-fs-nfs-permissions-per-user-subdirs.html Path = "/", // ファイルIOに用いるユーザーとディレクトリ作成時権限の設定は必須である様子。 // CDKが既定のユーザーを構成してくれるようなことはない。 // -> ↑嘘。必要がなければ構成しなくても問題ない。所詮はNFSなので、権限が他のユーザーに解放されているディレクトリは操作できる。はず。 PosixUser = efsUser, CreateAcl = efsCreateAcl, }); // Assets // https://docs.aws.amazon.com/cdk/api/latest/docs/aws-s3-assets-readme.html // vs // https://docs.aws.amazon.com/cdk/api/latest/docs/aws-s3-deployment-readme.html // 静的にS3にファイルを残し、スタックのデプロイ後にDataSyncでEFSに転送するのでDeployment。 var assetBucket = new Bucket(this, "AssetBucket", new BucketProps() { }); new BucketDeployment(this, "AssetBucketDeployment", new BucketDeploymentProps() { Sources = new ISource[] { Source.Asset("assets") }, DestinationBucket = assetBucket, }); // https://github.com/shelfio/chrome-aws-lambda-layer var chromeLayer = new LayerVersion(this, "ChromeLayer", new LayerVersionProps() { Code = AssetCode.FromAsset("chrome_aws_lambda.zip"), CompatibleRuntimes = new Runtime[] { Runtime.NODEJS_12_X } }); var renderImageBucket = new Bucket(this, "RenderImageBucket", new BucketProps() { }); var renderHtmlToS3Function = new Function(this, "RenderHtmlToS3Function", new FunctionProps() { Vpc = vpc, Runtime = Runtime.NODEJS_12_X, MemorySize = 1024, Timeout = Duration.Seconds(10), Code = Code.FromAsset("handlers"), Handler = "render-html-to-s3.handler", Environment = new Dictionary <string, string>() { ["BucketName"] = renderImageBucket.BucketName, ["EfsMountPath"] = "/mnt/efs", }, Layers = new ILayerVersion[] { chromeLayer }, Filesystem = Lambda.FileSystem.FromEfsAccessPoint(efsAccessPoint, "/mnt/efs"), }); // VPCやEFSに関してはCDK上の関連から // セキュリティグループや既定のロールへのインラインポリシーが構成される。 // S3バケットはCDK上の関連はないため明に権限を付与する。 renderImageBucket.GrantReadWrite(renderHtmlToS3Function); // 踏み台 var bastion = new BastionHostLinux(this, "Bastion", new BastionHostLinuxProps() { InstanceType = InstanceType.Of(InstanceClass.BURSTABLE3, InstanceSize.NANO), Vpc = vpc, }); assetBucket.GrantRead(bastion); // https://docs.aws.amazon.com/cdk/api/latest/docs/aws-efs-readme.html efs.Connections.AllowDefaultPortFrom(bastion); bastion.Instance.UserData.AddCommands( "yum check-update -y", // Ubuntu: apt-get -y update "yum upgrade -y", // Ubuntu: apt-get -y upgrade "yum install -y amazon-efs-utils", // Ubuntu: apt-get -y install amazon-efs-utils "yum install -y nfs-utils", // Ubuntu: apt-get -y install nfs-common "file_system_id_1=" + efs.FileSystemId, "efs_mount_point_1=/mnt/efs/fs1", "mkdir -p \"${efs_mount_point_1}\"", "test -f \"/sbin/mount.efs\" && echo \"${file_system_id_1}:/ ${efs_mount_point_1} efs defaults,_netdev\" >> /etc/fstab || " + "echo \"${file_system_id_1}.efs." + Stack.Of(this).Region + ".amazonaws.com:/ ${efs_mount_point_1} nfs4 nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,noresvport,_netdev 0 0\" >> /etc/fstab", "mount -a -t efs,nfs4 defaults", "chmod go+rw /mnt/efs/fs1" ); new CfnOutput(this, "BastionInstanceId", new CfnOutputProps() { ExportName = "BastionInstanceId", Value = bastion.InstanceId, }); new CfnOutput(this, "AssetBucketName", new CfnOutputProps() { ExportName = "AssetBucketName", Value = assetBucket.BucketName, }); }