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`](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`](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`](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`](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`](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](https://docs.github.com/en/authentication/connecting-to-github-with-ssh/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent).