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
-
Deploy Rails in Amazon ECS
- Concepts
- Push an image to ECR - we are here
- Create the RDS database, Task Definition, and Load Balancer
- Create the ECS Cluster and connect it all together
- Configure Sidekiq
- Automate Deploys with AWS CodeDeploy
- 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. 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".
(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)
(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.
(2.4) Now, go to the Users tab on the left-side menu. Then, click Create User.
(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".
(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"
(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.
(2.9) Setup your AWS CLI using the official install guide. Click on the OS you are using on the left-hand side.
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".
(3.2) On the next page, use sample-docker-rails-app as the repository name. Then, click "Create Repository".
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.
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. If you decided to skip that blog post, clone it from here.
Add this snippet to your config/database.yml file.
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:
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:
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.
# 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.
mkdir shared
mkdir shared/sockets
mkdir shared/pids
(4.6) After that, commit your progress:
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:
(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
You should be able to see the image you just uploaded, with the tag "v1.0.0"
What's next?
You pushed your first image into ECR. In the next post, 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!
Top comments (12)
Thank you Raphael for such detailed tutorial.
I face one issue at point (5.3). Below command didn't work for me.
*aws ecr get-login-password | docker login --username AWS --password-stdin *
To make it work I had to provide registry url in the end of docker login command
for example, on my terminal I have to enter:
readers you need to replace
with your own repository URI created from section (3.2).
and the terminal will return:
Hi Kavita, thank you for taking the time to read the article. I hope it was helpful. I'm so sorry I wasn't able to get back to you sooner. Can you give me a sample of the docker login command (redact the url if you have to)? This is so I can apply the relevant changes to the article
This worked for me
docs.aws.amazon.com/cli/latest/ref...
There are issues with the base image version and the gem versions. I would confirm
docker-compose build
works first. I update the image to the most recent Ruby 2.5.x and updated Bundler to the last version to support this Ruby version (2.3.26), and updated Rails to 5.2.8.1.in the aws cli setup what would the output be? default is json but there is also yaml text and table does this not matter
it doesn't really matter, whatever your preference is.
What would cause your preference of one over the other?
One thing to note is that with AWS CLI Version 2 the aws ecr get-login command is replaced with
aws ecr get-login-password | docker login --username AWS --password-stdin
Thanks for this Richard, I have edited the article to reflect this. Have a good one! :D
Hi Raph,
Please share about your
shared_dir
Would love to learn how to set it up on fargate.thank you , i will learn how to deplay a ruby on rails application production to ECS, i am in china ,so ,i want to use aliyun ecs