こんにちは、広野です。
検証で Amazon Linux 2023 のデスクトップ環境 (GUI) が必要になり、最新ソリューションを調べてたところ Amazon DCV を使ってみたくなったので環境をつくってみました。
Amazon DCV について
私が説明するまでもなく AWS 公式サイトに説明があるので、そちらを紹介します。もともと NICE-DCV という名前で開発されていたツールを AWS が買収したものです。
そして、構築の仕方も re:Post にありますので参考にしました。
作成した環境
- パブリックサブネットの Amazon Linux 2023 インスタンスに Amazon DCV をインストールしています。
- セキュリティグループで、指定したソース IP アドレスからのみアクセスできるようにします。
- ログインユーザー名は ec2-user、初期パスワードは AWS Secrets Manager で生成します。
- Amazon EC2 インスタンスには Elastic IP アドレスを割り当て、アクセスする URL が固定になるようにします。
- AWS CloudFormation で一発デプロイします。Amazon DCV のインストールは userdata スクリプト内で自動実行します。
- 既存 VPC を使用する前提です。
- ポートを 443 にしたかったんですが、QUIC というプロトコル (UDPを使用、1000番以上のポート使用を推奨) が使えるとレスポンスが向上するようだったので、今回はデフォルトのままにしました。
デプロイすると、AWS CloudFormation スタックの出力欄に URL が表示されます。単純に、Elastic IP に HTTPS 8443 番ポートでアクセスするだけのものです。
ブラウザでアクセスすると、ログイン画面が表示されます。
初期パスワードは AWS Secrets Manager で生成されるので、マネジメントコンソールから参照します。ログイン後、パスワード変更してもらえるとより安心です。
無事ログインできると、Amazon Linux 2023 のデスクトップ画面が表示されます。これで検証できそうです!
AWS CloudFormation テンプレート
- パラメータとして、既存の VPC、サブネットを選択できるようにしています。
- 自分の IAM ユーザー名をパラメータとして指定します。AWS Secrets Manager の閲覧権限に使用されます。
- インスタンスタイプは Amazon DSV の要件上最低でも medium 以上にする必要があるので t3.medium 一択にしています。
- EBS のボリュームサイズ、OS のタイムゾーンをパラメータで指定できるようにしています。
AWSTemplateFormatVersion: "2010-09-09" Description: The CloudFormation template that creates a remote-accessible EC2 instance with Amazon DCV. # ------------------------------------------------------------# # Input Parameters # ------------------------------------------------------------# Parameters: SystemName: Type: String Description: System name. use lower case only. (e.g. example) Default: example MaxLength: 10 MinLength: 1 SubName: Type: String Description: System sub name. use lower case only. (e.g. prod or dev) Default: dev MaxLength: 10 MinLength: 1 VpcId: Type: AWS::EC2::VPC::Id Description: Choose a existing VPC ID you deploy the EC2 instance in. InstanceSubnet: Type: AWS::EC2::Subnet::Id Description: Choose an existing Public Subnet ID you deploy the EC2 instance in. InstanceType: Type: String Description: EC2 instance type for dcv-server. Default: t3.medium AllowedValues: - t3.medium InstanceVolumeSize: Type: Number Description: The volume size in GB Default: 10 AllowedSubnet: Type: String Description: Allowed source IPv4 subnet and subnet mask. (e.g. xxx.xxx.xxx.xxx/32) Default: xxx.xxx.xxx.xxx/32 MaxLength: 18 MinLength: 9 YourIamUserName: Type: String Description: Your IAM user name. This is used for the permission of your dcv-server credential. Default: youriamusername MaxLength: 30 MinLength: 1 TimeZone: Type: String Description: The specified time zone. Default: Asia/Tokyo MaxLength: 30 MinLength: 1 Resources: # ------------------------------------------------------------# # EC2 Launch template # ------------------------------------------------------------# EC2LaunchTemplate: Type: AWS::EC2::LaunchTemplate Properties: LaunchTemplateName: !Sub dcv-server-${SystemName}-${SubName} LaunchTemplateData: InstanceType: !Ref InstanceType ImageId: >- {{resolve:ssm:/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64}} BlockDeviceMappings: - Ebs: VolumeSize: !Ref InstanceVolumeSize VolumeType: gp3 DeleteOnTermination: true Encrypted: true DeviceName: /dev/xvda NetworkInterfaces: - SubnetId: !Ref InstanceSubnet Groups: - !Ref Ec2SecurityGroup DeviceIndex: 0 AssociatePublicIpAddress: false MetadataOptions: HttpTokens: required Monitoring: Enabled: true TagSpecifications: - ResourceType: volume Tags: - Key: Cost Value: !Sub ${SystemName}-${SubName} DependsOn: - Ec2SecurityGroup # ------------------------------------------------------------# # EC2 Security Group # ------------------------------------------------------------# Ec2SecurityGroup: Type: AWS::EC2::SecurityGroup Properties: VpcId: !Ref VpcId GroupDescription: Allow dcv-server access via ALB only SecurityGroupIngress: - Description: Allow HTTPS from clients FromPort: 8443 IpProtocol: tcp ToPort: 8443 CidrIp: !Ref AllowedSubnet - Description: Allow QUIC from clients FromPort: 8443 IpProtocol: udp ToPort: 8443 CidrIp: !Ref AllowedSubnet SecurityGroupEgress: - Description: Allow all outbound traffic IpProtocol: -1 CidrIp: 0.0.0.0/0 Tags: - Key: Cost Value: !Sub ${SystemName}-${SubName} - Key: Name Value: !Sub SG-dcv-server-${SystemName}-${SubName} # ------------------------------------------------------------# # EC2 Role / Instance Profile (IAM) # ------------------------------------------------------------# Ec2Role: Type: AWS::IAM::Role Metadata: cfn_nag: rules_to_suppress: - id: W11 reason: CodeWhisperer requires '*' as a resource, reference https://docs.aws.amazon.com/codewhisperer/latest/userguide/cloud9-setup.html#codewhisperer-IAM-policies Properties: RoleName: !Sub Ec2Role-dcv-server-${SystemName}-${SubName} Description: This role allows EC2 instance to invoke S3 and SSM. AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: Service: - ec2.amazonaws.com Action: - sts:AssumeRole Path: / ManagedPolicyArns: - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore - arn:aws:iam::aws:policy/AWSCodeCommitPowerUser - arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy Policies: - PolicyName: !Sub QDeveloperPolicy-${SystemName}-${SubName} PolicyDocument: Version: "2012-10-17" Statement: - Action: - "codewhisperer:GenerateRecommendations" Resource: "*" Effect: Allow - PolicyName: !Sub Ec2SecretsPolicy-${SystemName}-${SubName} PolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Action: - secretsmanager:GetResourcePolicy - secretsmanager:GetSecretValue - secretsmanager:DescribeSecret - secretsmanager:ListSecretVersionIds Resource: - !GetAtt SecretDcvServer.Id - PolicyName: !Sub Ec2S3Policy-${SystemName}-${SubName} PolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Action: - "s3:GetObject" Resource: - !Sub "arn:aws:s3:::dcv-license.${AWS::Region}/*" DependsOn: - SecretDcvServer Ec2InstanceProfile: Type: AWS::IAM::InstanceProfile Properties: InstanceProfileName: !Ref Ec2Role Path: / Roles: - !Ref Ec2Role DependsOn: - Ec2Role # ------------------------------------------------------------# # Secrets Manager # ------------------------------------------------------------# SecretDcvServer: Type: AWS::SecretsManager::Secret Properties: Name: !Sub dcv-server-${SystemName}-${SubName} Description: dcv-server credential GenerateSecretString: PasswordLength: 8 ExcludePunctuation: true IncludeSpace: false RequireEachIncludedType: true Tags: - Key: Cost Value: !Sub ${SystemName}-${SubName} SecretDcvServerResourcePolicy: Type: AWS::SecretsManager::ResourcePolicy Properties: BlockPublicPolicy: true SecretId: !Ref SecretDcvServer ResourcePolicy: Version: "2012-10-17" Statement: - Effect: Deny Principal: "*" Action: secretsmanager:GetSecretValue Resource: "*" Condition: StringNotEquals: aws:PrincipalArn: - !Sub "arn:aws:iam::${AWS::AccountId}:user/${YourIamUserName}" - !GetAtt Ec2Role.Arn # ------------------------------------------------------------# # Elastic IP address for EC2 instance # ------------------------------------------------------------# EipEc2: Type: AWS::EC2::EIP Properties: Domain: vpc Tags: - Key: Cost Value: !Sub ${SystemName}-${SubName} - Key: Name Value: !Sub eip-dcv-server-${SystemName}-${SubName} # ------------------------------------------------------------# # EIP Association # ------------------------------------------------------------# EIPAssociation: Type: AWS::EC2::EIPAssociation Properties: AllocationId: !GetAtt EipEc2.AllocationId InstanceId: !Ref Ec2Instance DependsOn: - Ec2Instance - EipEc2 # ------------------------------------------------------------# # EC2 # ------------------------------------------------------------# Ec2Instance: Type: AWS::EC2::Instance Properties: IamInstanceProfile: !Ref Ec2InstanceProfile LaunchTemplate: LaunchTemplateId: !Ref EC2LaunchTemplate Version: !GetAtt EC2LaunchTemplate.LatestVersionNumber UserData: Fn::Base64: !Sub | #!/bin/bash set -euxo pipefail timedatectl set-timezone ${TimeZone} # Install packages dnf update -y dnf groupinstall "Desktop" -y # Setup GDM sed -i '/^\[daemon\]/a WaylandEnable=false' /etc/gdm/custom.conf systemctl set-default graphical.target # Get credential from Secrets Manager and set ec2-user password PASSWORD=$(aws secretsmanager get-secret-value --secret-id "${SecretDcvServer.Id}" --region "${AWS::Region}" --query SecretString --output text) echo "ec2-user:${!PASSWORD}" | chpasswd sed -i 's/PasswordAuthentication no/PasswordAuthentication yes/g' /etc/ssh/sshd_config systemctl restart sshd # Install dcv-server cd /tmp rpm --import https://d1uj6qtbmh3dt5.cloudfront.net/NICE-GPG-KEY curl -L -O https://d1uj6qtbmh3dt5.cloudfront.net/nice-dcv-amzn2023-$(arch).tgz tar -xvzf nice-dcv-amzn2023-$(arch).tgz && cd nice-dcv-*-amzn2023-$(arch) dnf install -y ./nice-dcv-server-*.rpm dnf install -y ./nice-dcv-web-viewer-*.rpm dnf install -y ./nice-xdcv-*.rpm systemctl enable dcvserver sed -i "/^\[session-management\/automatic-console-session/a owner=\"ec2-user\"\nstorage-root=\"%home%\"" /etc/dcv/dcv.conf sed -i "s/^#create-session/create-session/g" /etc/dcv/dcv.conf dnf install -y cups usermod -a -G sys dcv systemctl enable --now cups dnf install -y xorg-x11-drv-dummy tee /etc/X11/xorg.conf > /dev/null << EOF Section "Device" Identifier "DummyDevice" Driver "dummy" Option "UseEDID" "false" VideoRam 512000 EndSection Section "Monitor" Identifier "DummyMonitor" HorizSync 5.0 - 1000.0 VertRefresh 5.0 - 200.0 Option "ReducedBlanking" EndSection Section "Screen" Identifier "DummyScreen" Device "DummyDevice" Monitor "DummyMonitor" DefaultDepth 24 SubSection "Display" Viewport 0 0 Depth 24 Virtual 4096 2160 EndSubSection EndSection EOF if [ -f /usr/bin/nvidia-xconfig ]; then /usr/bin/nvidia-xconfig --preserve-busid --enable-all-gpus dnf install -y vulkan-tools glx-utils fi reboot Tags: - Key: Cost Value: !Sub ${SystemName}-${SubName} - Key: Name Value: !Sub dcv-server-${SystemName}-${SubName} DependsOn: - Ec2InstanceProfile # ------------------------------------------------------------# # Output Parameters # ------------------------------------------------------------# Outputs: # EIP DcvServerUrl: Value: !Sub "https://${EipEc2.PublicIp}:8443"
まとめ
いかがでしたでしょうか?
ベタに Amazon EC2 インスタンスをインターネットからアクセスさせる環境になっているのはイケてないので、Amazon DCV がインストールされたプライベートサブネットの EC2 インスタンスに対して Fleet Manager 経由のリモートアクセスをサポートしてくれるような、AWS のアップデートを期待しています。
本記事が皆様のお役に立てれば幸いです。