こんにちは、広野です。
検証で 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 のアップデートを期待しています。
本記事が皆様のお役に立てれば幸いです。






