Giter Site home page Giter Site logo

gerhalt / mining-camp Goto Github PK

View Code? Open in Web Editor NEW
45.0 4.0 7.0 90 KB

Easy automated configuration and deployment of Minecraft servers on AWS spot instances, featuring automatic backups and restoration using S3.

Python 87.48% HCL 5.40% Shell 7.12%
minecraft minecraft-server aws terraform ansible python spot-instances server route53 s3

mining-camp's Introduction

Mining Camp

Easy automated configuration and deployment of Minecraft servers on AWS spot instances, with features such as instance shutdown monitoring and automatic backups and restorations using S3.

Mining Camp

Introduction

Amazon sells spare EC2 instances that aren’t currently reserved, called "spot instances", for a fraction of the cost of a reserving them. If you can mitigate their volatility risk, they’re an excellent deal and powerful enough to host demanding game servers.

Advantages:

  • More control over everything. SSH into the box, troubleshoot, adjust anything you like on the fly.

  • Better bang for your buck. More powerful systems than most hosting services offer.

  • Pay for exactly what you use. Spot instances are billed by the second, so if your server isn’t going to be used for a few days, or even overnight, just turn it off.

  • Linux-based. This might be a disadvantage, depending on your comfort level. Windows instances are not only more of a pain, they’re also far more expensive.

  • Works with any AWS region, so you can minimize latency for your player base.

Disadvantages:

  • Spot instances can be terminated at any time. This is mitigated by both the emergency shutdown monitoring script, but in practice reasonable bids for spot instances result in excellent uptime.

  • Requires initial work to setup. This isn’t a push-button solution, but once you’re done with the setup day-to-day interactions are extremely easy.

Sample Cost Breakdowns

I’ve provided a couple samples of pricing based on different instance types below. When deciding what to use, cross reference your requirements against the spot instance pricing and the EC2 instance types. Be aware that spot instance prices can differ not only between AWS regions, but between availability zones as well.

All calculations below are based on an instance in us-east-1, and assume a 31-day month.

Note
AWS charges for absolutely everything. It’s critical that you monitor your bill and be aware of where you’re incurring charges.

i3.large

An i3.large is a quite powerful instance with ~16GB of RAM and a 500GB NVMe drive, meaning an EBS (beyond the 8GB root volume) isn’t required. Price estimates for the various moving pieces:

  • Current spot instance price for an i3.large is $0.0371 per hour at present.

  • An 8GB EBS is used as the root volume for the instance, at $0.10 per GB-month, or roughly $0.00013 an hour. This is deleted on instance shutdown, so no charges are incurred when the server is off.

  • Elastic IPs are free when assigned to a running instance, and $0.005 per hour when unassigned.

  • AWS data transfer is pretty expensive, at $0.09 per GB. I use about 1.4GB per day, but my server has a lot of time when it’s completely idle. For the sake of round numbers, 2GB of bandwidth a day winds up being $0.0075 an hour. This cost is very subjective.

  • S3 is actually quite cheap, especially with lifecycle management rules cleaning up old backups. 20GB of combined server archives and rotating backups is $0.023 per GB, $0.46 a month, or $0.0006 an hour.

From these numbers, when my server is running, I’m paying roughly $0.045 an hour or $33.73 a month to run it full-time. When my server is off, but I want to preserved my backups and elastic IP address, I’m paying $4.17 a month. Just preserving backups is only $0.46, which is very reasonable.

t2.medium

Options with less RAM more be suited to smaller loads. One example is a t2.medium, which has only 4GB of RAM and no SSD, which means it requires an EBS volume. The upside is that it only costs $0.0134 an hour. If we use a 30GB ephemeral EBS volume that’s deleted on instance termination, that adds roughly $0.0004 an hour.

Using this less powerful, less expensive instance brings considerable savings: $0.0255 an hour, or $18.97 when run for an entire month.

Setup

Start by checking out this repository, and navigating to your checkout-out directory. Sample bash commands provided are from the root directory unless otherwise noted.

You’ll need to download and install the following requirements to get started (versions I’ve tested with are in parentheses):

  • pip (20.1)

  • terraform (0.12.24)

You’ll also need:

  • An appropriate version of the JDK/JVM to run the server locally at least once, allowing it to generate the necessary configuration files.

  • A fully configured AWS developer account; you’ll be connecting to it to set up the required infrastructure.

Configuration

Credentials

You’ll need your AWS credentials available for most of these operations, under the minecraft profile. ~/.aws/credentials will look like:

[minecraft]
aws_access_key_id = <your_access_key_here>
aws_secret_access_key = <your_secret_key_here>

To tell Ansible to use this profile, you can do one of the following:

  • export AWS_PROFILE=minecraft in the shell you’ll be running the Ansible commands in.

  • Prefix each command with AWS_PROFILE=minecraft, which sets the environment for that particular invocation.

  • Change the minecraft section title to default (or duplicate it). This will cause those credentials to be tried by default.

SSH Key Pair

You’ll need a key pair for accessing your instance. Generate a public-private key pair. As an example, you can do this with ssh-keygen:

ssh-keygen -t rsa -b 4096 -C "AWS"

In the EC2 console, select Import Key Pair on the NETWORK & SECURITY → Key Pairs page. Upload your public key, and name it "aws-public". The launch configuration Terraform creates includes this key, allowing SSH access to Ansible (and for troubleshooting!)

Instance Access

You’ll need to choose one of the following methods to access your instance:

  • Use the public IP automatically assigned to your spot instance. This requires no setup on your part, but the address will be different each time the instance is started. The IP of your instance can be found under EC2 → Instances in the "IPv4 Public IP" column.

  • Use an elastic IP. This associates a known IP with your instance each time it’s started. Setup for this must be done by hand. Elastic IPs also incur a cost while they’re not in use, as well as additional charges if you make too many assignments per month. (DEPRECATED)

  • Use Route 53 to host an owned domain or subdomain, pointed at your server. Requires a domain and minor manual setup with your registrar.

via Elastic IP (DEPRECATED)

You’ll need to create an elastic IP for association with your instance, providing a convenient public-facing IP. In the AWS console, do the following:

  1. Enter the EC2 service.

  2. Click on Elastic IPs, under the NETWORK & SECURITY menu on the left-hand side of the screen.

  3. Click Allocate new address.

  4. Leave the scope as "VPC", and click close.

  5. You should see your new elastic IP in the list. Save the Allocation ID for later use during the setup.

Once a server has been spun up, this elastic IP will be attached to it. An allocated elastic IP is included in the price of a running instance, but you will be billed for any unassigned EIPs by the hour. For this reason, if you plan to stop your Minecraft server for long periods of time, be sure to delete your EIPs and create new ones when you’re ready to begin hosting again.

via Domain

Proceed through the rest of the setup, specifying a server hostname. Once you’ve applied your Terraform config, return to do this section and do the following:

  1. Enter the AWS console, and navigate to the Route 53 service.

  2. Select the hosted zone for the hostname you chose.

  3. You should see an NS record with four hosts.

  4. Add a corresponding NS record to your domain for each host. I leave this as an exercise for the reader.

  5. Now, when your server is booted, it will automatically add an A record with a short TTL pointed at your server’s public IP.

Virtual Environment & Python Requirements

Using pip, install the necessary Python 2.7 requirements. I recommend using virtualenv and virtualenvwrapper. Running the following installs Ansible, the AWS command-line interface, and libraries required for interacting with AWS programmatically.

$ mkvirtualenv minecraft
(minecraft) $ pip install -r requirements.txt

Minecraft Server Archive

You’ll need to create a Minecraft server archive to be pulled onto your instance each time the box is spun up. In this example, I’ll be creating an archive for my Feed the Beast server named daftcyborg.

$ # Create a base directory named after your server name
$ mkdir daftcyborg
$ cd daftcyborg

$ # Get your base server pack. In my case, I've already downloaded the FTB server
$ ls
FTBRevelationServer_1.0.0.zip
$ unzip FTBRevelationServer_1.0.0.zip

$ # Install the server requirements, if the pack requires it
$ sh ./FTBInstall.sh

$ # Create and populate the bootstrap script - mining camp will use this to
$ # launch your server. Copy from _utitilies/_, and update with memory limits
$ # and the `.jar` you're using.
$ cp <repo-checkout-directory>/utilities/server-start.sh .

$ # Launch the server. You'll need to do this twice, once to create the
$ # eula.txt and once to generate the base
$ sh ./server-start.sh
Missing eula.txt. Startup will fail and eula.txt will be created
Make sure to read eula.txt before playing!
To continue press <enter>

Open eula.txt, and agree (or don’t) to the terms and conditions.

Launch the server again, and wait for it to complete. This will generate the world base, and any settings and properties files necessary. Quit the server, and do the following as desired:

  • Remove the world directory, which is the world directory name used by default and which will (assuming you update the server.properties file) be named differently when your server is run.

  • Edit server.properties as desired. It is important that the server-port be left as 25565, otherwise you’ll need to adjust the Terraform configuration. Fields I recommend changing are level-name, level-seed, and motd.

  • Add yourself and any other players desired to ops.json.

  • Update server-icon.png to a custom icon.

Copy server.properties to ansible/files/server.properties, which Ansible will install every time over the top of the properties file in the archive, allowing easy configuration changes.

Now, clean up your leftover base archive, since you don’t need it anymore:

$ rm FTBRevelationServer_1.0.0.zip

Navigate up a level, and create a gzipped tarball:

$ cd ..
$ tar -cvzf daftcyborg-server-12-20-2017.tgz daftcyborg/

Lastly, push the archive to S3:

$ # The parameterized command is 'aws s3 cp <server_file> s3://<bucket_name>/<server_name>/'
$ # My version looks like:
$ aws s3 cp daftcyborg-server-12-20-2017.tgz s3://josh-minecraft/daftcyborg/

Lastly, save the full name, including file extension, of the archive you generated; it will be required when you run the setup wizard.

Settings

The recommended way to configure the system is to run the setup wizard from the root repo directory, like so:

$ ./utilities/setup.py

This guides you through each required setting, offering default values if available. It then takes your input and renders out terraform/variables.tf and ansible/group_vars/all from corresponding *.j2 templates. If you like, you can populate those templates by hand as well.

  • It’s important you choose the right aws_availability_zone, since spot prices can vary substantially from zone to zone.

  • Maximum spot price determines the maximum price you’re willing to pay per hour. Setting this wisely will prevent you from being surprised by a large bill at the end of the month.

Terraform

Terraform allows you to easily setup EC2 and S3 to match your needs. To apply the terraform configuration, run:

terraform apply terraform/

Once this has successfully completed, your AWS configuration is done. Unless you change your configuration, you won’t need to run this again.

Ansible

The first time you do the configurations, you’ll need to bundle your local Ansible configuration and push it to your S3 bucket, under the same directory as your server is stored. A provided playbook takes care of this for you:

cd ansible/
ansible bundle.yml
Note
You’ll need to repeat this step each time you adjust any server settings.

Instance Interactions

Launch

Jump to the ansible directory, and run the start.yml playbook to configure the instance and launch the minecraft server:

cd ansible
ansible-playbook start.yml

This merely increases the size of the auto-scaling group from 0 → 1; the server won’t be available for a few more minutes as it provisions.

Shutdown

Shutting down your server is very similar, with a few extra command options that allow Ansible to SSH into the host and save the server state before killing the box itself.

cd ansible
ansible-playbook -i ec2.py --private-key=~/.ssh/aws -u ubuntu -c ssh stop.yml

When this playbook finishes, your instance will be gone, but the state of the server will have been preserved and pushed to S3, ready for the next time you launch it.

Note
If the auto-inventory script is taking too long, you can update ansible/ec2.ini's regions entry with the particular AWS region you’re using.
Note
If using an older version of Ansible, the Paramiko library used by default may run into errors when gathering facts from the remote host. If this happens, add -c ssh to the ansible-playbook command above.

Troubleshooting

Logs from the initial provision are available via the console as well as in /var/log/user-data.

Tests

Tests are currently available for the Prospector tool. You’ll need to install the requirements in the test directory in order to run them. From the root, with your virtual environment active:

(minecraft) $ pip install -r utilities/tests/requirements.txt

Now you can launch the test suite:

(minecraft) $ python -m unittest -v utilities.tests.test_prospector

mining-camp's People

Contributors

dependabot[bot] avatar gerhalt avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

mining-camp's Issues

Upgradability

Mostly minor, but the git repo doesn't have a .gitignore file, so files we modify or get modified are being tracked by git. This makes it difficult to git pull or upgrade this repo as and when you make changes.

I believe the following files should be ignored and replaced by .example files, steps added to the README (or an initialization script) for copying the example files to the real files. This will allow you to upgrade the examples as you will without mucking with users' existing configs.

ansible/files/prospector.cfg
ansible/group_vars/all
terraform/variables.tf

These files shouldn't be tracked at all

terraform/.terraform/
terraform/terraform.tfstate
terraform/terraform.tfstate.backup

There are probably more but this is as far as I got before thinking I should open this issue.

Graceful shutdown on ASG scale-in

Spot instance termination is very frequently checked via a cron job running on the host, and if a termination notice is seen, the server is gracefully stopped and a backup produced to S3 (see utilities/shutdown.sh).

This doesn't happen if the ASG is scaled in. Instead, we could register a lifecycle hook with a consumer listening to an SQS topic. Because our ASGs only operate with a singleton, having a topic per ASG and a consumer on the box, ready to call the shutdown script, seems reasonable.

This feature sets the stage for setting up things like user-count-based health checks, where the ASG could autoscale down to 0 if no players have been present for a certain amount of time.

Not an Issue - Just props!

Hey there,
This really isn't an issue so feel free to delete this if you want. Just wanted to say that I just used your system to setup my own server and it works very well! With minimal difficulties (at least as far as server setups go) everything is working!

Genius idea to use the spot instances with S3 backups!

Switch to using R53 instead of IP addresses

Route53 seems like a much better solution for allowing routing to the server, instead of having to have an elastic IP sitting around.

Using only a hosted subdomain, rather than a full hosted zone: https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/CreatingNewSubdomain.html#AddNewSubdomainRecords
Routing traffic to an EC2 instance: https://github.com/awsdocs/amazon-route53-docs/blob/master/doc_source/routing-to-ec2-instance.md

  1. Create a R53 hosted zone for the domain (or subdomain).
  2. Add NS records for each of the four nameservers.
  3. Each time an instance spins up, it should add an A record to the hosted zone with the instance's public IP.

Terraform version and language update - .12.x / HCL2

Hi!

One of my coworkers linked me this repo, and I noticed it was last tested on an older version of Terraform that was running before HCL2 released. Would you be open to moving to the current version if I can get the PR done and tested?

Bundle scripts into Ansible archive instead of cloning repository

Before adding local provisioning, a user would run start.yml, which would reach out and provision the remote host. As part of that, it would clone this repo from github and use the scripts there. Because we already have an archive with files, settings and tasks available (the Ansible tarball pushed via the bundle.yml playbook), we should include the local versions of the scripts in that tarball so we have a snapshot of the scripts as they existed locally, instead of something from github that may or may not be compatible.

Server invocation is wrong

When I originally wrote this, I was using FTB, which uses a ServerStart.sh script that in turns makes the java ... call to actually run the server. My bootstrap script expects this script to exist in the base of the server archive. If you use something like Vanilla MC, however, this script doesn't exist. This should be changed to something more generic, or instruct users how to create / populate it.

The instance we're trying to create can't use the ami store root device it seems

Autoscaling group error:

Placing Spot instance request. Status Reason: AMI 'ami-0d7fbe2d96677967f' with an instance-store root device is not supported for the instance type 'i3.large'. Placing Spot instance request failed.

Also, this gave errors so I removed it.

--- a/terraform/s3.tf
+++ b/terraform/s3.tf
@@ -16,10 +16,6 @@ resource "aws_s3_bucket" "minecraft" {
     id      = "backup pruning"
     enabled = true

-    tags {
-      "backup"    = "old"
-    }
-
     expiration {
       days = 3
     }

Automatic Auto-Provisioning

Right now, instances need to be provisioned remotely after they're launched. It'd be much more robust to run Ansible provisioning locally on the box as part of the bootstrap process. I think the easiest way to set this up is:

  1. Create a new set of tagged Ansible tasks that archive the local /ansible contents and push them to the same S3 minecraft bucket already used for server archives and backups.
  2. Add user data to the launch configuration, which should:
    a. Install any prerequisites needed to run Ansible locally.
    b. Pull and extract the archive created above to a temporary directory.
    c. Run the startup playbook.

Revisit Backups

I just installed a vanilla 1.15.2 server, and automatic backups are not working. When I originally set this up, I wrote it to work specifically with the backup mods that Feed the Beast was using at the time. Rather than trying to deal with different world backup mods, it makes much more sense to:

  1. Pause automatic writes to disk - /save-off.
  2. Force a full write of all chunks to disk - /save-all flush
  3. Archive the full world directory.
  4. Resume automatic disk writes /save-on.
  5. Push the archive to S3.

These commands were first added in Alpha v1.0.16_02, which means it should work across all versions we'd be interested in running.

NOTE: The Bedrock server uses a slightly different command format, but I don't think that's a case I am interested in supporting right now.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.