deploying_to_aws

Public · content/deploying_to_aws.md · uploaded about 2 months ago

Deploying to AWS

This guide describes how to set up an AWS EC2 instance to support
Kamal-based deployment of Rails applications.

Select a VPC

VMs and other resources in EC2 are created in a "virtual private cloud" (VPC),
providing a controlled network that's isolated from from other AWS users and
the wider internet. AWS creates a default VPC for an account in each region,
which we'll use in this example. To retrieve the ID of your default VPC, run:

aws ec2 describe-vpcs  --query 'Vpcs[?(IsDefault==`true`)].VpcId'

In this example, I've used the placeholder value $vpc_id to represent the ID
taken from output of the command above - the actual ID will be will look
something like vpc-a1b2c3d4.

Set Up Security Groups

Security groups in EC2 provide simple, rule-based control over the network
accessibility of resources in your VPC.

Security group rules come in two types - ingress and egress. Ingress rules
specify the set of IP addresses that are allowed to make connections to the VM,
egress rules specify the addresses to which the VM can make connections[^1].
We'll make two security groups, one for ingress and the other for egress[^2].

Create the security groups

First, edit create_ingress_sg.json, setting the value VpcId to your
$vpc_id. Now, create the security group with the following command:

aws ec2 create-security-group --cli-input-json file://create_ingress_sg.json 

If successful, you'll see a JSON document containing the GroupId the new
security group, which I'll refer to in this example as $ingress_sg_id - the
actual ID will look something like sg-0a1b2c3d4e5f67890

Now edit create_egress_sg.json, update VpcId, and create the egress security
group:

aws ec2 create-security-group --cli-input-json file://create_egress_sg.json 

Like with the ingress group, a successful result will have the GroupId of the
newly created security group, which I'll refer to as $egress_sg_id.

Authorize the security groups

Now, establish the network rules for the security groups. First, edit authorize_ingress_sg.json,
updating the value of the GroupId to $ingress_sg_id. The first two rules
allow traffic from any IPv4 or IPv6 address, to ports 80 and 443,
respectively. The last rule permits ssh from a limited set of IP addresses.
You must update these values ones that are appropriate for your scenario,
e.g., to your organization's public network range or your home office.

To apply the rules to the ingress security group, run:

aws ec2 authorize-security-group-ingress --cli-input-json file://authorize_ingress_sg.json

If successful, the cli will output a JSON response showing the group rules.

Now, update authorize_egress_sg.json, setting the value of GroupId to
$egress_sg_id, then run:

aws ec2 authorize-security-group-egress --cli-input-json file://authorize_egress_sg.json

Upon success, the cli will output a JSON response showing the group rules.

TODO explain the purpose of the egress rules in the sample

Create a EC2 launch template

EC2 templates provide a standard VM configuration that can be used to deploy one
or more instances based on it.

Add an SSH key pair

Secure SHell (ssh) will be used for managing the instance and deploying the
application. Establishing an SSH key in EC2 allows AWS to start VM instances
with the specified key installed on the instance, allowing remote login.

If you don't already have an key pair set up in EC22, you can import it
directly via the CLI. For example to import the public key in
id-example-key.pub with the name my_ssh_key, run:

aws ec2 import-key-pair --key-name my_ssh_key --public-key-material fileb://my-example-key.pub

It's also possible to have AWS generate an ssh key for you[^3]:

aws --output text ec2 create-key-pair --key-name another_example_key  --query 'KeyMaterial' > another_example_key.pem

The resulting file contains a PEM-formatted private key that can be used to
connect to the VM.

Finally, the name given to a key pair must be unique within an AWS account.
Importing/creating a key with an existing name will produce an error. For this
example, I'll refer to the SSH key pair name as $key_pair_name.

Edit the JSON template

Edit create_launch_template.json, setting the values of $key_pair_name,
$ingress_sg_id, and $egress_sg_id to their correct values.

aws ec2 create-launch-template --cli-input-json file://create_launch_template.json

The output from this command will contain a LaunchTemplateId value, to which
I'll refer by the $launch_template_id.

Launch template

Now, you can launch an instance with the new template:

aws ec2 run-instances --launch-template LaunchTemplateId=$launch_template_id,Version=1 --query 'Instances[*].InstanceId'

NB: don't forget to replace $launch_template_id in the command above with the
actual LaunchTemplateId. The output from this command should be the instance ID,
which I'll refer to as $instance_id

Once VM is launched, it takes a bit for it to come online and won't have a
publicly accessible IP address on startup. To get the IP after launching the VM,
run:

aws ec2 describe-instances --instance-ids $instance_id --query 'Reservations[*].Instances[*].PublicIpAddress' --output text

You can connect to the instance via ssh thus:

ssh ubuntu@IP-ADDRESS

If you opted to create a .pem file SSH key, you should change the permissions
on $key_fil.pem so it's only readable by you:

chmod 0400  $key_file.pem 

When connecting, you'll need to specify the path to the .pem file:

ssh -i $key_file.pem ubuntu@IP-ADDRESS

Notes

Adding a server entry to your SSH configuration

If you'd like to not have to remember to specify the key file path and username when
connecting to your VM, you can add an entry in your SSH configuration file like this:

Host myvm.example.edu
  User ubuntu
  IdentityFile /path/to/your/ssh/key_file.pem

Your SSH configuration file can be found under your home directory, in
$HOME/.ssh/config. Note that you might need to create the .ssh directory and
config files if they don't exist; if so, make sure their permissions are set
properly:

chmod 0700 $HOME/.ssh
chmod 0600 $HOME/.ssh/config

[^1]: It's worth pointing out that these rules only need to be given "in one direction," i.e., if you've created an ingress rule from 0.0.0.0/0 to port 443 on the VM, that's all that's needed to allow the VM to respond to HTTPS connections from any IP address; you do not need to add a corresponding egress rule.
[^2]: The division into ingress/egress security groups is purely organizational; for example, one could also make a single group with for rules or separate groups by traffic type (e.g., one for HTTP/S, another for SSH).
[^3]: Avoid this option if possible. For information on how to generate your own SSH keys, please see GitHub's documentation.