Deploy Rails in Amazon ECS: Part 2 - Push an image to ECR
Tags: aws, docker, ruby, devops
Published: 2019-08-08 13:11:37

---
title: Deploy Rails in Amazon ECS: Part 2 - Push an image to ECR
published: true
description: 
tags: aws, docker, ruby, devops
---

This is the second part of the *Deploy  Rails in Amazon ECS* post. It's part of a broader series called *More than 'hello world' in Docker*. The series will help you ready your app: from setting it up locally to deploying it as a production-grade workload in AWS.

- [Build Rails + Sidekiq web apps in Docker](https://dev.to/jamby1100/more-than-hello-world-in-docker-run-rails-sidekiq-web-apps-in-docker-1b37)
- **Deploy Rails in Amazon ECS**
  - [Concepts](https://dev.to/jamby1100/deploy-rails-in-amazon-ecs-part-1-concepts-26nl)
  - **Push an image to ECR - we are here**
  - [Create the RDS database, Task Definition, and Load Balancer](https://dev.to/jamby1100/deploy-rails-in-amazon-ecs-part-3-create-the-rds-database-task-definition-and-load-balancer-1ffe)
  - [Create the ECS Cluster and connect it all together] (https://dev.to/jamby1100/deploy-rails-in-amazon-ecs-part-4-create-an-ecs-cluster-338f)
  - [Configure Sidekiq](https://dev.to/jamby1100/deploy-rails-in-amazon-ecs-part-5-integrate-sidekiq-and-redis-for-background-processing-150f)
- [Automate Deploys with AWS CodeDeploy](https://dev.to/raphael_jambalos/automate-deployments-with-aws-codepipeline-52f5)
  - [Deep Dive on AWS CodeBuild](https://dev.to/jamby1100/automate-docker-build-with-aws-codebuild-9om)
- Advanced ECS Concepts:
  - Service Discovery and Auto Scaling - coming soon
  - Fargate - coming soon

# 2 | Setting up an IAM User

The first step in deploying your Dockerized application in the cloud is being able to upload the images you’ve built to an image repository. For this tutorial, we will use AWS Elastic Container Registry (ECR).

You will need an AWS account. You can set up one [here](https://aws.amazon.com/premiumsupport/knowledge-center/create-and-activate-aws-account/). In this section, we will create an IAM policy with all the permissions we need to access ECR. Then, we will attach that policy to a new user we will use later on.

**(2.1)** When you're done with your AWS account setup, let's create an IAM policy. On the Services tab, search for IAM and click it. Then, click the "Policies" tab on the left-side menu. After that, click "Create Policy".

![](https://thepracticaldev.s3.amazonaws.com/i/rzsn42rutib2s592li0i.png)

**(2.2)** On the next page, choose *Elastic Container Registry* as the service, All *Elastic Container Registry Actions* as the actions, and *All Resources* as the resources. This permission grants our user ability to do anything inside the ECR service (such as push docker images to ECR, etc)

![](https://thepracticaldev.s3.amazonaws.com/i/7y5yft6754oe4eke3tb4.png)

**(2.3)** On the next page, add *ecr-admin-access* as the name, and *Give admin access to ECR* as the description. Then, click *Create Policy*.

![](https://thepracticaldev.s3.amazonaws.com/i/faaam1s7l3c1haiecwxb.png)

**(2.4)** Now, go to the *Users* tab on the left-side menu. Then, click *Create User*.

![](https://thepracticaldev.s3.amazonaws.com/i/uek0i7gzb7xm7jusgkoe.png)

**(2.5)** In the next page, add *ecr-fetch-user* as the username and enable *Programmatic access*. There are two main ways to access AWS: through the Management Console (which you are now using) or through the CLI. Enabling *Programmatic access* will enable us to access AWS via CLI only. Then, click "Next: Permissions". 

![](https://thepracticaldev.s3.amazonaws.com/i/fy802k5vt9spyzd5sw8a.png)

**(2.6)** On the next page, search for the policy we created in 2.1: *ecr-admin-access*. Then, tick the checkbox on the left and click "Next: Tags"

![](https://thepracticaldev.s3.amazonaws.com/i/8ol8tzgttuai6d8izztq.png)

**(2.7)** Skip the next pages by clicking next. Then, review your IAM user. When you've reviewed it, click "Create user".


**(2.8)** The next page will show the Access Key ID (AKID) and Secret. You will use these credentials to get access to AWS in a later step. Make sure to download the CSV as this will be the last time you will be able to see these credentials.

![](https://thepracticaldev.s3.amazonaws.com/i/bxwd98tdbenztxntp1lr.png)

**(2.9)** Setup your AWS CLI using the [official install guide](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-install.html). Click on the OS you are using on the left-hand side.

![](https://thepracticaldev.s3.amazonaws.com/i/5dyssp47nx98qs7kxmjq.png)

At the step where you type `aws configure`, put in the AKID and secret you downloaded from 2.8. For the region, choose "ap-southeast-1".

# 3 | Create an ECR Repository

With an AWS CLI setup, let's dive in!

**(3.0)** In a production setup, you must choose the AWS region closest to you. For simplicity, choose the Singapore region ("ap-southeast-1") on the upper-right side.

**(3.1)** On the services tab, search for ECR and click it. On the page's right-hand side, click "Create Repository".

![](https://thepracticaldev.s3.amazonaws.com/i/z1ml8e0sfb3yvzpqzmbk.png)

**(3.2)** On the next page, use *sample-docker-rails-app* as the repository name. Then, click "Create Repository". 

![](https://thepracticaldev.s3.amazonaws.com/i/3y9frxs6ub9ztkcb3j5m.png)

You will see the repository URI. Take note of this, we will use it in step 5.2. We have create an image repository. With the URI of the repository provided, we can push as many versions of the same image as we like.

![](https://thepracticaldev.s3.amazonaws.com/i/5ahih8r16o6dlyegg59b.png)

# 4 | Preparing our Rails 5 app for ECS

**(4.1)** First, `cd` into the project's root directory of the project we did on the last [post](https://dev.to/jamby1100/more-than-hello-world-in-docker-run-rails-sidekiq-web-apps-in-docker-1b37). If you decided to skip that blog post, clone it from [here](https://github.com/jamby1100/rails-docker-project/releases/tag/v0.0.1).

Add this snippet to your config/database.yml file.

```yml
staging:
  adapter: postgresql
  encoding: unicode
  host: <%= ENV['POSTGRESQL_HOST'] %>
  username: <%= ENV['POSTGRESQL_USER_NAME'] %>
  password: <%= ENV['POSTGRESQL_PASSWORD'] %>
  pool: 5
  database: <%= ENV['POSTGRESQL_DB'] %>
```
This allows our database credentials to be supplied via an environment variable (instead of hardcoding them here).

**(4.2)** Then, create a copy of your config/environments/development.rb file by using the command:

```sh
cp config/environments/development.rb config/environments/staging.rb
```
In step 4.1 and step 4.2, we created a new Rails environment called `staging`.  This is so we can distinguish between the development environment on our local and our staging environment on our containers.

**(4.3)** After that, commit your progress:

```sh
git add .
git commit -m "Add staging environment"
git push origin master
```

**(4.4)** For us to run our rails server on our containers, create a new file called `config/docker_puma.rb` and paste the snippet from below. We are going to use the default application server for Rails, Puma.

```rb
# Change to match your CPU core count
workers 1

# Min and Max threads per worker
threads 1, 6

app_dir = File.expand_path("../..", __FILE__)
shared_dir = "#{app_dir}/shared"

# Default to production
rails_env = ENV['RAILS_ENV'] || "production"
environment rails_env

# Set up socket location
bind "unix://#{shared_dir}/sockets/puma.sock"

# Logging
# - Commened out this line so logs would pour into awslogs cloudwatch
# stdout_redirect "#{app_dir}/log/puma.stdout.log", "#{app_dir}/log/puma.stderr.log", true

# Set master PID and state locations
pidfile "#{shared_dir}/pids/puma.pid"
state_path "#{shared_dir}/pids/puma.state"
activate_control_app

on_worker_boot do
  require "active_record"
  ActiveRecord::Base.connection.disconnect! rescue ActiveRecord::ConnectionNotEstablished
  ActiveRecord::Base.establish_connection(YAML.load_file("#{app_dir}/config/database.yml")[rails_env])
end
```

**(4.5)** Then, create folders needed by puma.

```sh
mkdir shared
mkdir shared/sockets
mkdir shared/pids
```

**(4.6)** After that, commit your progress:

```sh
git add .
git commit -m "Add special docker puma config. Add folders for puma"
git push origin master
```

# 5 | Pushing an Image to ECR

In this section, we will push an image to ECR. 

**(5.1)** Let's build a Docker image with this command: `docker build . -t my-rails-app`. This may take a while. 

**(5.2)** When the build is done, let's tag the image we just built. Get the URI of the repository you took note of in step 3.2 and run: `docker tag my-rails-app:latest <uri-from-3.2>:v1.0.0`

**(5.3)** Then, let's authenticate by typing `aws ecr get-login --no-include-email --region=ap-southeast-1`. The command will output a string starting in `docker login -u AWS -p`. This string will be used for us to log in with the ECR private image repository we created in section 3. Copy-paste the entire string back into the terminal.

For those using AWS CLI 2.0, you can use the command: `aws ecr get-login-password | docker login --username AWS --password-stdin`.

**(5.4)** Let's now push our image to ECR by: `docker push <uri-from-3.2>:v1.0.0`. Your local machine is now pushing the image to ECR, layer by layer. It should look something like this:

![](https://thepracticaldev.s3.amazonaws.com/i/evazgje5cv3x8ijzs82j.png)

**(5.5)** Go back to the AWS Management Console. On the services tab, search for ECR and click it. Then, click the repository you created in step 3.2

![](https://thepracticaldev.s3.amazonaws.com/i/rxiiehtfuvq8cjpew9s5.png)

You should be able to see the image you just uploaded, with the tag "v1.0.0"

![](https://thepracticaldev.s3.amazonaws.com/i/k02eu2l7w2dpc021zb9s.png)

# What's next?

You pushed your first image into ECR. In the [next post](https://dev.to/jamby1100/deploy-rails-in-amazon-ecs-part-3-create-the-rds-database-task-definition-and-load-balancer-1ffe), you will build the required AWS resources to set up your Rails app in ECS.

*Special thanks to Allen, my editor, for helping this post become more coherent. Another special thanks to Richard Hessler for the tip on AWS CLI 2.0*

**I'm happy to take your comments/feedback on this post. Just comment below, or message me!**