<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Stefano Francavilla</title>
    <description>The latest articles on Forem by Stefano Francavilla (@sfrancavilla).</description>
    <link>https://forem.com/sfrancavilla</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F469967%2F8f696d9e-70ad-4c18-a70e-06a2f800152f.png</url>
      <title>Forem: Stefano Francavilla</title>
      <link>https://forem.com/sfrancavilla</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/sfrancavilla"/>
    <language>en</language>
    <item>
      <title>Deploy web apps (+NGINX) to ECS with Docker</title>
      <dc:creator>Stefano Francavilla</dc:creator>
      <pubDate>Wed, 07 Oct 2020 21:49:05 +0000</pubDate>
      <link>https://forem.com/sfrancavilla/deploy-web-apps-nginx-to-ecs-with-docker-198i</link>
      <guid>https://forem.com/sfrancavilla/deploy-web-apps-nginx-to-ecs-with-docker-198i</guid>
      <description>&lt;span&gt;Background Photo by &lt;a href="https://unsplash.com/@blakeconnally?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Blake Connally&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/tech?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/span&gt;

&lt;h1&gt;
  
  
  &lt;strong&gt;Premise&lt;/strong&gt;
&lt;/h1&gt;

&lt;p&gt;During our first hectic startup days at &lt;a href="https://geowox.com/" rel="noopener noreferrer"&gt;Geowox&lt;/a&gt;, we’ve been used to work &lt;em&gt;"manually"&lt;/em&gt; and directly on AWS resources, such as EC2, leaving out all the services that could have made our life easier.&lt;/p&gt;

&lt;p&gt;Luckily, we’ve now fully embraced the magic world of Docker and we’re trying to manage, as much as possible, our &lt;em&gt;"infrastructure as a code"&lt;/em&gt;.&lt;/p&gt;




&lt;p&gt;This article assume you have some familiarity with the technologies we’re using such as AWS, Docker and Rails.&lt;br&gt;
If you haven’t done it yet, &lt;a href="https://docs.docker.com/get-docker/" rel="noopener noreferrer"&gt;here&lt;/a&gt; you can find a guide to install and set up Docker. And &lt;a href="https://guides.rubyonrails.org/v5.0/getting_started.html" rel="noopener noreferrer"&gt;here&lt;/a&gt;, a guide to getting started with Rails.&lt;br&gt;
&lt;br&gt;&lt;/p&gt;
&lt;h1&gt;
  
  
  &lt;strong&gt;1. What I will cover&lt;/strong&gt;
&lt;/h1&gt;

&lt;p&gt;In this article, I’d like to cover, on a high level:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Containerize Ruby on Rails app and NGINX using Docker and Docker compose&lt;/li&gt;
&lt;li&gt;Use ECR as our Docker registry&lt;/li&gt;
&lt;li&gt;Use ECS to run and manage our containers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, let’s begin.&lt;br&gt;
&lt;br&gt;&lt;br&gt;
&lt;em&gt;Notes: I’m using Ruby on Rails but, this can of course be applied to any other framework or language.&lt;/em&gt;&lt;br&gt;
&lt;br&gt;&lt;/p&gt;
&lt;h1&gt;
  
  
  &lt;strong&gt;2. Containerize app and NGINX&lt;/strong&gt;
&lt;/h1&gt;

&lt;p&gt;Let’s first create a simple Ruby on Rails app on your local machine by opening your terminal and typing the below command:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

rails new ror-ecs


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;&lt;br&gt;&lt;br&gt;
It will create a new rails project in your current directory.&lt;br&gt;
Generate a new controller (&lt;em&gt;welcome&lt;/em&gt;) and view (&lt;em&gt;index&lt;/em&gt;):&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

rails g controller welcome index


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;&lt;br&gt;&lt;br&gt;
Add root instruction in routes.rb:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Next, we need to add &lt;a href="https://docs.docker.com/engine/reference/builder/" rel="noopener noreferrer"&gt;Docker&lt;/a&gt; and &lt;a href="https://docs.docker.com/compose/compose-file/" rel="noopener noreferrer"&gt;Docker Compose&lt;/a&gt; files in the project. Let’s first start with Dockerfile(s). I usually keep them and any related configuration in a separate folder inside the root folder of the app.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fom5ouclyuz508f93mj9x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fom5ouclyuz508f93mj9x.png" alt="Alt Docker folder structure"&gt;&lt;/a&gt;&lt;/p&gt;
Figure 1 — Docker folder structure



&lt;p&gt;&lt;br&gt;&lt;br&gt;
I’ve created 2 separate folders where each Dockerfile resides, one for the Ruby on Rails app and the other for the NGINX web server container (see Figure 1). Let’s have a look at them in detail.&lt;br&gt;
&lt;br&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;2.1 Ruby on Rails (app) Dockerfile&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Below, the content of the Dockerfile for the Rails app container.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;This is all we need to start our Ruby on Rails container. The basic commands are pretty straightforward and I suppose do not require further explanation.&lt;br&gt;
&lt;br&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;2.2 NGINX Dockerfile (web)&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Below, the content of the Dockerfile for the NGINX web server container.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Even in this case, NGINX does execute pretty simple commands including copying our &lt;em&gt;start.sh&lt;/em&gt; script and &lt;em&gt;nginx.conf&lt;/em&gt; template into the right folders inside the container.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;start.sh&lt;/em&gt; does not have much but, in my opinion, can be useful to keep clean our Dockerfile when we add more instructions such as, a simple auth or SSL (we can see that in a future part 2 of this tutorial). At the moment, the script runs 2 simple instructions inside the container:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;envsubst ‘$RAILS_ROOT’ &amp;lt; /tmp/app.conf &amp;gt; /etc/nginx/conf.d/default.conf&lt;/em&gt; replaces any occurrence of RAILS_ROOT environment variable in our temporary conf file and move it inside the NGINX configuration folder as default&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;nginx -g “daemon off;”&lt;/em&gt; tells NGINX to stay in the foreground. As best practice, this is useful for Docker and means that there is one single process inside a single container.&lt;/li&gt;
&lt;/ul&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Now, let’s have a look at our nginx.conf template:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Basically, it defines rules on how files can be accessed by the client and redirects (reverse proxy) requests to the container where the rails app runs on port 3000.&lt;br&gt;
&lt;br&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;2.3 Docker compose&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;It’s now time to create our docker-compose file to manage our containers (Ruby on Rails and NGINX). Basically, Docker compose helps us to define and run multi-container Docker applications.&lt;br&gt;
First, create a docker-compose.yml file in the main folder (see Figure 2).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fd5to20h119tk8jcmzsu4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fd5to20h119tk8jcmzsu4.png" alt="Alt Docker folder structure"&gt;&lt;/a&gt;&lt;/p&gt;
Figure 2 — Docker compose



&lt;p&gt;&lt;br&gt;&lt;br&gt;
Then, copy and paste the following:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
&lt;br&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;2.3 Test locally&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;We can test on our machine that everything works as expected. In the terminal, navigate the root folder (where the &lt;em&gt;docker-compose.yml&lt;/em&gt; resides) and type:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

docker-compose up --build


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;&lt;br&gt;&lt;br&gt;
As from the &lt;a href="https://docs.docker.com/compose/reference/up/" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;, this command simply builds the images and then start the containers in the foreground.&lt;/p&gt;

&lt;p&gt;At the end of the process, the output is something similar to the one in Figure 3:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F3fy7ofkywi33ag71yjmq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F3fy7ofkywi33ag71yjmq.png" alt="Alt Output of docker-compose up&amp;lt;br&amp;gt;
"&gt;&lt;/a&gt;&lt;/p&gt;
Figure 3 — Output of docker-compose up



&lt;p&gt;&lt;br&gt;&lt;br&gt;
The web app should now up and running. Open your browser and navigate to &lt;a href="http://localhost:3000" rel="noopener noreferrer"&gt;http://localhost:3000&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To interrupt the process and stop the containers (with exit code 0), just press &lt;em&gt;“ctrl + C”&lt;/em&gt;.&lt;br&gt;
&lt;br&gt;&lt;/p&gt;
&lt;h1&gt;
  
  
  &lt;strong&gt;3. Deploy to ECS&lt;/strong&gt;
&lt;/h1&gt;

&lt;p&gt;We’re finally ready to deploy our web app to ECS. For the purpose of this tutorial, I prefer to use the command line interface but, you can replicate all the commands in the AWS console. In general, I believe that knowing the command line tools is pretty useful and can speed things up.&lt;/p&gt;

&lt;p&gt;To recap, the AWS services we’re going to use:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Amazon Elastic Container Service (&lt;strong&gt;ECS&lt;/strong&gt;) is a highly scalable, high-performance container orchestration service that supports Docker containers and allows you to easily run and scale containerized applications on AWS.&lt;br&gt;&lt;br&gt;
Amazon Elastic Container Registry (&lt;strong&gt;ECR&lt;/strong&gt;) is a fully-managed Docker container registry that makes it easy for developers to store, manage, and deploy Docker container images.&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h3&gt;
  
  
  &lt;strong&gt;3.1 Install aws-cli and ecs-cli&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Before we start, we need to install aws-cli and ecs-cli.&lt;br&gt;
Without going into further detail, you can just follow &lt;a href="https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-install.html" rel="noopener noreferrer"&gt;this&lt;/a&gt; to install aws-cli and &lt;a href="https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-configure.html" rel="noopener noreferrer"&gt;this&lt;/a&gt; guide to configure it and set up a profile name. You can use the [default] one.&lt;/p&gt;

&lt;p&gt;And, &lt;a href="https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ECS_CLI_installation.html" rel="noopener noreferrer"&gt;this&lt;/a&gt; guide to install ecs-cli.&lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;3.2 Push Docker images to ECR&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Before pushing our Docker images to Amazon ECR, we need to create a repository to store them.&lt;/p&gt;

&lt;p&gt;We’re going to create 2 repositories, one for each image (Ruby on Rails/app and NGINX/web) with the following commands:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

aws ecr create-repository --repository-name ror-ecs-app


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;&lt;br&gt;&lt;br&gt;
and&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

aws ecr create-repository --repository-name ror-ecs-web


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;&lt;br&gt;&lt;br&gt;
The terminal output should print something like the below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fdgt4jw3eefy9696vfarn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fdgt4jw3eefy9696vfarn.png" alt="Alt Create registry output"&gt;&lt;/a&gt;&lt;/p&gt;
Figure 4 — Create registry output



&lt;p&gt;&lt;br&gt;&lt;br&gt;
You can see some useful information related to our images here. Take note of the repository URIs as we need them later.&lt;/p&gt;

&lt;p&gt;You can also review your newly created repositories in the AWS console under ECR service:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F83kjoekqvmpd7q1v0hf1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F83kjoekqvmpd7q1v0hf1.png" alt="Alt Repositories listed on AWS console"&gt;&lt;/a&gt;&lt;/p&gt;
Figure 5 — Repositories listed on AWS console



&lt;p&gt;&lt;br&gt;&lt;br&gt;
Ok, we’ve created repositories for our images, now it’s time to let docker-compose know where to push them. We just need to add the repository URIs (that you saved somewhere before, right?) as below:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Let’s build again the images:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

docker-compose build


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;&lt;br&gt;&lt;br&gt;
You can now check if they’re present in our docker images list:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

docker image ls


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fhna4wuc92g0h1vlqww90.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fhna4wuc92g0h1vlqww90.png" alt="Alt Our newly created images"&gt;&lt;/a&gt;&lt;/p&gt;
Figure 6 — Our newly created images



&lt;p&gt;&lt;br&gt;&lt;br&gt;
As you can see, both images have been created and tagged automatically as &lt;em&gt;latest&lt;/em&gt;. You can specify which image you’d like to push on the registry by its tag.&lt;/p&gt;

&lt;p&gt;Push them to the respective repositories on ECR:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

docker-compose push


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;&lt;br&gt;&lt;br&gt;
If you’re not logged in to AWS yet, you should see something like the following error:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

ERROR: denied: Your Authorization Token has expired. Please run ‘aws ecr get-login --no-include-email’ to fetch a new one.


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;&lt;br&gt;&lt;br&gt;
As explicitly required, you need to run&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

aws ecr get-login --no-include-email


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;&lt;br&gt;&lt;br&gt;
to retrieve the command (which includes a secret token) needed to login, which is something like the below:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

docker login -u AWS -p &amp;lt;very_long_token&amp;gt; https://XXXXXXXX.dkr.ecr.eu-xxxx-1.amazonaws.com


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;&lt;br&gt;&lt;br&gt;
&lt;em&gt;Note: the  is automatically generated and is shown in the output.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You can now login by copying the command and typing it in the terminal.&lt;br&gt;
You should then see “Login succeeded” in the terminal.&lt;br&gt;
Push again your images:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

docker-compose push


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;&lt;br&gt;&lt;br&gt;
It can take few minutes to complete.&lt;br&gt;
Once completed, you can verify that the images have been uploaded by navigating to ECR Service in the AWS console.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fpa5g44n1sn6gzxvqhpwo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fpa5g44n1sn6gzxvqhpwo.png" alt="Alt Detail page of our app image"&gt;&lt;/a&gt;&lt;/p&gt;
Figure 7 — Detail page of our app image



&lt;p&gt;&lt;br&gt;&lt;br&gt;
Well done so far, we’re close to have our web app up and running in the cloud.&lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;3.2 Publish your Ruby on Rails app with ECS&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;We’ve already described what Amazon ECS is but, let’s further explore some basic ECS components such as &lt;em&gt;cluster&lt;/em&gt; and &lt;em&gt;task definition&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;A &lt;em&gt;cluster&lt;/em&gt; is a logical grouping of running resources (e.g. tasks). In our case, considering that we’re running tasks that use the EC2 launch type, a cluster is also a grouping of container instances. You can create multiple clusters in an account to keep your resources separate.&lt;/p&gt;

&lt;p&gt;Before running Docker containers on Amazon ECS, we must create a task definition.&lt;br&gt;
A &lt;em&gt;Task definition&lt;/em&gt; is a JSON file that describes one or more containers in your application. There are different parameters you can specify inside a task definition file, such as Docker images, CPU usage, IAM role, etc.&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;ecs-cli compose&lt;/em&gt; command that we’re going to use, allows us to create task definitions and manage our Amazon ECS tasks using our Docker compose file. Basically, what happen is that ECS will “convert” &lt;em&gt;docker-compose.yml&lt;/em&gt; into a single task definition where the 2 containers, defined as &lt;em&gt;services&lt;/em&gt;, will run.&lt;/p&gt;

&lt;p&gt;Without any further specification, the &lt;em&gt;ecs-cli&lt;/em&gt; commands look for a Docker compose file in the current directory, named &lt;em&gt;docker-compose.yml&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The first thing that we should do is to locally define a cluster configuration before creating it on ECS. This configuration can then be passed as a parameter to the ecs-cli commands. So, let’s define a cluster configuration by running the following command on terminal:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

ecs-cli configure --cluster ror-ecs-cluster --region eu-west-1 --config-name ror-ecs-conf --cfn-stack-name ror-ecs-stack --default-launch-type ec2


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;cluster&lt;/em&gt; Specifies the name of our cluster. In this case I’ve chosen ror-ecs-cluster but, feel free to use a different name. &lt;em&gt;Required&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;region&lt;/em&gt; Specifies the region where the cluster is created. In my case is eu-west-1 (Ireland) but, feel free to change it to the one you are using. &lt;em&gt;Required&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;config-name&lt;/em&gt; Specifies the configuration name that we’ll refer to when we’ll create the cluster. If it’s not present, the name is set to default&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;cfn-stack-name&lt;/em&gt; Specifies the stack name in CloudFormation. If omitted, the name is defined as amazon-ecs-cli-setup-&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;default-launch-type&lt;/em&gt; Specifies the default launch type to use between FARGATE and EC2. In our case we will be using EC2. If not defined, no launch type is used.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If everything works as expected, the output should be:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

INFO[0000] Saved ECS CLI cluster configuration ror-ecs-conf.


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;&lt;br&gt;&lt;br&gt;
Your configuration is saved locally in the &lt;em&gt;~/.ecs/config&lt;/em&gt; file:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

version: 1
default: default
clusters:
  ror-ecs-conf:
    cluster: ror-ecs-cluster
    region: eu-west-1
    cfn-stack-name: ror-ecs-stack
    default_launch_type: EC2


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;&lt;br&gt;&lt;br&gt;
The next step is to create our cluster using the configuration we’ve previously defined:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

ecs-cli up --keypair id_rsa --capability-iam --size 1 --instance-type t2.medium --cluster-config ror-ecs-conf


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;keypair&lt;/em&gt; Specifies the name of an existing Amazon EC2 key pair to enable SSH access to the EC2 instances in your cluster. It’s not required but useful if you want to have access to the EC2 instance(s) that will be created&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;capability-iam&lt;/em&gt; Specifies the creation of IAM resources. In this case it is required&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;size&lt;/em&gt; Specifies the number of instances to launch and register to the cluster. The default value is 1 so, in this case, it would not be required.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;instance-type&lt;/em&gt; Specifies the instance type. In our case, we’re creating a t2.medium instance&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;cluster-config&lt;/em&gt; Specifies the name of our cluster. For our example, I’ve chosen ror-ecs-cluster but, feel free to use a different name&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The output will be something like the following:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fljxau6u22z04q9qa2mcu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fljxau6u22z04q9qa2mcu.png" alt="Alt ecs-cli up command output"&gt;&lt;/a&gt;&lt;/p&gt;
Figure 8 — ecs-cli up command output



&lt;p&gt;&lt;br&gt;&lt;br&gt;
As you can see, CloudFormation took care of creating some resources for us such as VPC, Security Groups, Subnets, etc.&lt;/p&gt;

&lt;p&gt;Log into the AWS console and you can have a look at the resources created by the CloudFormation stack:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F0z2f0b9rhocfd64qwzzo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F0z2f0b9rhocfd64qwzzo.png" alt="Alt ror-ecs-stack on AWS console"&gt;&lt;/a&gt;&lt;/p&gt;
Figure 9 — ror-ecs-stack on AWS console



&lt;p&gt;&lt;br&gt;&lt;br&gt;
Also, you can see the newly created cluster:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F9536yt41oywdietnaak4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F9536yt41oywdietnaak4.png" alt="Alt Cluster detail page on AWS console"&gt;&lt;/a&gt;&lt;/p&gt;
Figure 10 — Cluster detail page on AWS console



&lt;p&gt;&lt;br&gt;&lt;br&gt;
If you scroll down, under the “ECS Instances” tab, you can also review the EC2 instance details that has been created and launched:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fby8w33zjjsfubhbjo2o9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fby8w33zjjsfubhbjo2o9.png" alt="Alt EC2 instance details on ECS cluster"&gt;&lt;/a&gt;&lt;/p&gt;
Figure 11 — EC2 instance details on ECS cluster




&lt;p&gt;&lt;br&gt;&lt;br&gt;
Ok, everything is set up for our final step: deploy our docker-compose file to our cluster; this will also create our task definition file for our containers.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

ecs-cli compose up --cluster-config ror-ecs-conf


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;&lt;br&gt;&lt;br&gt;
The output is the following:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fqecqpcpz9z7tg53o0ws4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fqecqpcpz9z7tg53o0ws4.png" alt="Alt ecs-cli compose command output"&gt;&lt;/a&gt;&lt;/p&gt;
Figure 12 — ecs-cli compose command output



&lt;p&gt;&lt;br&gt;&lt;br&gt;
In the AWS console, you will see the task definition and all the default parameters:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F27jpbfu0wcop6acnb5jx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F27jpbfu0wcop6acnb5jx.png" alt="Alt Task definition JSON v1 on AWS console"&gt;&lt;/a&gt;&lt;/p&gt;
Figure 13 — Task definition JSON v1 on AWS console



&lt;p&gt;&lt;br&gt;&lt;br&gt;
And now? How can we access our website?&lt;br&gt;
We know that public access is managed by our NGINX conf file which, at the moment, has localhost as &lt;em&gt;server_name&lt;/em&gt;. So, we need to change it and we can proceed in 2 ways:&lt;/p&gt;

&lt;p&gt;If you want to use your own a domain (e.g. example.com), you can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;create an A record in your DNS settings that points to the EC2 machine IP address&lt;/li&gt;
&lt;li&gt;use the EC2 machine IP address as server name&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s choose the second option and add the EC2 IP address to our NGINX config file. You can retrieve the public EC2 IP address in AWS console under the EC2 instance detail page:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fiz78qoudg8hwt1x02epc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fiz78qoudg8hwt1x02epc.png" alt="Alt EC2 instance detail page on AWS console&amp;lt;br&amp;gt;
"&gt;&lt;/a&gt;&lt;/p&gt;
Figure 14 — EC2 instance detail page on AWS console




&lt;p&gt;&lt;br&gt;&lt;br&gt;
Update the &lt;em&gt;server_name&lt;/em&gt; inside our &lt;em&gt;nginx.conf&lt;/em&gt; file:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;Let’s build again the updated image with:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

docker-compose build


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;&lt;br&gt;
Push it to the ECR repository:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

docker-compose push


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;&lt;br&gt;
And update our cluster:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

ecs-cli compose up --cluster-config ror-ecs-conf --force-update


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;&lt;br&gt;
Aaaand that’s it! You should see your web app finally online 🎉🎉🎉!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fu8ljhxw9g1xmg1ibze9n.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fu8ljhxw9g1xmg1ibze9n.jpg" alt="Alt The web app is publicly available on http://18.210.50.195"&gt;&lt;/a&gt;&lt;/p&gt;
Figure 15 — The web app is publicly available on http://18.210.50.195





&lt;h1&gt;
  
  
  &lt;strong&gt;4. Conclusion&lt;/strong&gt;
&lt;/h1&gt;

&lt;p&gt;Congratulations forgetting this far! Of course this project is just the beginning and there are a lot of improvements and customisation to be made with ECS (e.g. use an &lt;a href="https://docs.aws.amazon.com/AmazonECS/latest/userguide/create-application-load-balancer.html" rel="noopener noreferrer"&gt;ALB&lt;/a&gt;). At the same time, I’ve tried to cover quite a good amount of concepts for you to start working with ECS.&lt;br&gt;
In this tutorial I mainly wanted to be able to show different aspects I’ve been stuck while learning ECS and I really hope that you will find this useful. Here you can find the source code.&lt;br&gt;
I greatly appreciate any feedback. Let me know if you enjoyed it 🙏 and &lt;a href="https://www.linkedin.com/in/stefanofrancavilla/" rel="noopener noreferrer"&gt;let’s connect&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;Special thanks to &lt;a href="https://www.linkedin.com/in/donato-gabriele-f-45700811/" rel="noopener noreferrer"&gt;Donato Francavilla&lt;/a&gt; for reviewing the article!&lt;/p&gt;

</description>
      <category>aws</category>
      <category>docker</category>
      <category>rails</category>
      <category>startup</category>
    </item>
  </channel>
</rss>
