diff --git a/cdk/README.md b/cdk/README.md index b68a236..385a3f8 100644 --- a/cdk/README.md +++ b/cdk/README.md @@ -9,6 +9,7 @@ - AWS Account - AWS CLI installed and configured - Domain name with public DNS served from Route 53 +- an EC2 Key pair (optional) **Note:** Installing CDK globally is not required. @@ -97,6 +98,55 @@ Alternatively, you can delete the `minecraft-server-stack` first, then the `minecraft-domain-stack` from the [AWS Console](https://console.aws.amazon.com/cloudformation/). ## Advanced Usage +### EFS Maintenance +1. Log into your AWS console's EC2 section for your chosen region. +2. From the top right corner choose to launch an instance from a launch template ![Launch Instance](../docs/screenshots/efs1_launch_instance.png) + 1. If you have more than one version, pick the highest revision. +3. Select the launch template created by your stack ![Select Template](../docs/screenshots/efs2_select_a_template.png) +4. Select the most recent x86 AMI ![Select AMI](../docs/screenshots/efs3_select_ami.png) +5. Select the cheapest instance type ![Instance Type](../docs/screenshots/efs4_select_instance_type.png) +6. Select your key pair to log in. +7. Select one of the public subnets created by your stack ![Network](../docs/screenshots/efs5_network.png) +8. Change anything else you want (though the defaults should _just work_) and launch the instance ![Launched](../docs/screenshots/efs6_launched.png) +9. Click the instance id to go to the lust of instances again and wait for your instance to show as running. +10. Once running clik the instance id to get the details of its external IP address ![IP](../docs/screenshots/efs7_ip.png) +11. Use ssh with your local key file to log in +``` +ssh -i /path/to/key-file.pem ec2-user@YOUR_INSTANCE_IP +``` +12. The FS should mount under `/mnt/efs/fs1/minecraft` after a minute or two ![Logged In](../docs/screenshots/efs8_logged_in.png) +13. When you are done shut down the instance to terminate it +``` +sudo shutdown now +``` + +**Example Commands** +1. Back-up world to a local machine +``` +# On ec2 instance +cd /mnt/efs/fs1/minecraft/ +tar cjvf world.tar.bz2 world +# on local machine +scp -i /path/to/key-file.pem ec2-user@YOUR_INSTANCE_IP:/mnt/efs/fs1/minecraft/world.tar.bz2 . +``` +2. Upload a world from a local machine +``` +# on local machine +scp -i /path/to/key-file.pem world.tar.bz2 ec2-user@YOUR_INSTANCE_IP:/mnt/efs/fs1/minecraft/ +# On ec2 instance +cd /mnt/efs/fs1/minecraft/ +rm -rf world +tar xjvf world.tar.bz2 +``` +3. Remove everything but the world and backups (useful for modded server version changes). +``` +# On ec2 instance +cd /mnt/efs/fs1/minecraft/ +shopt -s extglob +rm -rfv !("backups"|"world") +rm .ftb-installed +shopt -u extglob +``` ## FAQ diff --git a/cdk/lib/minecraft-stack.ts b/cdk/lib/minecraft-stack.ts index 49a7816..7c2452e 100644 --- a/cdk/lib/minecraft-stack.ts +++ b/cdk/lib/minecraft-stack.ts @@ -17,6 +17,7 @@ import { constants } from './constants'; import { SSMParameterReader } from './ssm-parameter-reader'; import { StackConfig } from './types'; import { getMinecraftServerConfig, isDockerInstalled } from './util'; +import { UserData } from 'aws-cdk-lib/lib/aws-ec2'; interface MinecraftStackProps extends StackProps { config: Readonly; @@ -322,5 +323,56 @@ export class MinecraftStack extends Stack { ], }); iamRoute53Policy.attachToRole(ecsTaskRole); + + const efsMaintenanceInstanceRole = new iam.Role(this, 'EFSMaintenanceRole', { + assumedBy: new iam.ServicePrincipal('ec2.amazonaws.com'), + description: 'Minecraft EC2 instance role', + }); + efsReadWriteDataPolicy.attachToRole(efsMaintenanceInstanceRole); + + const efsMaintenanceSecurityGroup = new ec2.SecurityGroup( + this, + 'EfsMaintenanceSecurityGroup', + { + vpc, + description: 'Security group for Minecraft on-demand EFS Maintenance Instances', + } + ); + + efsMaintenanceSecurityGroup.addIngressRule( + ec2.Peer.anyIpv4(), + ec2.Port.tcp(22) + ); + + /* Allow access to EFS from Fargate service security group */ + fileSystem.connections.allowDefaultPortFrom( + efsMaintenanceSecurityGroup + ); + + const efsMaintenanceLaunchTemplate = new ec2.LaunchTemplate(this, 'EFSMaintenanceLaunchTemplate', { + userData: UserData.custom(`#cloud-config +package_update: true +package_upgrade: true +runcmd: +- yum install -y amazon-efs-utils +- apt-get -y install amazon-efs-utils +- yum install -y nfs-utils +- apt-get -y install nfs-common +- file_system_id_1=${fileSystem.fileSystemId} +- efs_mount_point_1=/mnt/efs/fs1 +- mkdir -p "\${efs_mount_point_1}" +- test -f "/sbin/mount.efs" && printf "\\n\${file_system_id_1}:/ \${efs_mount_point_1} efs iam,tls,_netdev\\n" >> /etc/fstab || printf "\\n\${file_system_id_1}.efs.${config.serverRegion}.amazonaws.com:/ \${efs_mount_point_1} nfs4 nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,noresvport,_netdev 0 0\\n" >> /etc/fstab +- test -f "/sbin/mount.efs" && grep -ozP 'client-info]\\nsource' '/etc/amazon/efs/efs-utils.conf'; if [[ $? == 1 ]]; then printf "\\n[client-info]\\nsource=liw\\n" >> /etc/amazon/efs/efs-utils.conf; fi; +- retryCnt=15; waitTime=30; while true; do mount -a -t efs,nfs4 defaults; if [ $? = 0 ] || [ $retryCnt -lt 1 ]; then echo File system mounted successfully; break; fi; echo File system not available, retrying to mount.; ((retryCnt--)); sleep $waitTime; done; +`), + role: efsMaintenanceInstanceRole, + spotOptions: { + interruptionBehavior: ec2.SpotInstanceInterruption.TERMINATE, + requestType: ec2.SpotRequestType.ONE_TIME + }, + securityGroup: efsMaintenanceSecurityGroup, + instanceInitiatedShutdownBehavior: ec2.InstanceInitiatedShutdownBehavior.TERMINATE + }); + } } diff --git a/docs/screenshots/efs1_launch_instance.png b/docs/screenshots/efs1_launch_instance.png new file mode 100644 index 0000000..9ae070b Binary files /dev/null and b/docs/screenshots/efs1_launch_instance.png differ diff --git a/docs/screenshots/efs2_select_a_template.png b/docs/screenshots/efs2_select_a_template.png new file mode 100644 index 0000000..eaac8b4 Binary files /dev/null and b/docs/screenshots/efs2_select_a_template.png differ diff --git a/docs/screenshots/efs3_select_ami.png b/docs/screenshots/efs3_select_ami.png new file mode 100644 index 0000000..f764576 Binary files /dev/null and b/docs/screenshots/efs3_select_ami.png differ diff --git a/docs/screenshots/efs4_select_instance_type.png b/docs/screenshots/efs4_select_instance_type.png new file mode 100644 index 0000000..8e5b6a5 Binary files /dev/null and b/docs/screenshots/efs4_select_instance_type.png differ diff --git a/docs/screenshots/efs5_network.png b/docs/screenshots/efs5_network.png new file mode 100644 index 0000000..ef2f3ed Binary files /dev/null and b/docs/screenshots/efs5_network.png differ diff --git a/docs/screenshots/efs6_launched.png b/docs/screenshots/efs6_launched.png new file mode 100644 index 0000000..840d9e6 Binary files /dev/null and b/docs/screenshots/efs6_launched.png differ diff --git a/docs/screenshots/efs7_ip.png b/docs/screenshots/efs7_ip.png new file mode 100644 index 0000000..278556f Binary files /dev/null and b/docs/screenshots/efs7_ip.png differ diff --git a/docs/screenshots/efs8_logged_in.png b/docs/screenshots/efs8_logged_in.png new file mode 100644 index 0000000..768210f Binary files /dev/null and b/docs/screenshots/efs8_logged_in.png differ