You just created a Node Express web application with Docker!
50 |
51 |
52 |
53 |
54 |
57 |
58 |
59 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # AWS Fargate: Running a serverless Node.js app on AWS ECS.
2 |
3 | ## Project Summary
4 |
5 | We're going to containerize a node.js project that renders a simple static site and deploy it to an Amazon ECS Fargate cluster. I will supply all the code at https://github.com/austinloveless/Docker-on-AWS.
6 |
7 | The Tutorial assumes you have a basic understanding of Docker. If you are brand new to to Docker you can read my previous [post](https://medium.com/@awsmeetupgroup/docker-on-aws-1855b825de5e) introducing you to Docker.
8 |
9 | ## Installing Prerequisites
10 |
11 | ### Downloading Docker Desktop.
12 |
13 | If you are on a Mac go to https://docs.docker.com/docker-for-mac/install/ or Windows go to https://docs.docker.com/docker-for-windows/install/. Follow the installation instructions and account setup.
14 |
15 | ### Installing node.js
16 |
17 | Download node.js [here](https://nodejs.org/en/).
18 |
19 | ### Installing the AWS CLI
20 |
21 | Follow the instructions [here](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-install.html).
22 |
23 | ## Project setup
24 |
25 | Now that we have our prerequisites installed we can build our application. This project isn't going to focus on the application code, the point is to get more familiar with Docker and AWS. So you can download the [Repo](https://github.com/austinloveless/Docker-on-AWS) and change directories into the `Docker-on-AWS` directory.
26 |
27 | If you wanted to run the app locally and say screw docker you can run `npm install` in side the `Docker-on-AWS` directory. Then run `node app.js`. To see the site running locally visit `http://localhost:80`.
28 |
29 | Now that we have docker installed and the repo downloaded we can look at the `Dockerfile`. You can think of it as a list of instructions for docker to execute when building a container or the blueprints for the application.
30 |
31 | ```
32 | FROM node:12.4-alpine
33 |
34 | RUN mkdir /app
35 | WORKDIR /app
36 |
37 | COPY package.json package.json
38 | RUN npm install && mv node_modules /node_modules
39 |
40 | COPY . .
41 |
42 | LABEL maintainer="Austin Loveless"
43 |
44 | CMD node app.js
45 |
46 | ```
47 |
48 | At the top we are declaring our runtime which is `node:12.4-alpine`. This is basically our starting point for the application. We're grabbing this base image "FROM" the official docker hub [node image](https://hub.docker.com/_/node).
49 |
50 | If you go to the link you can see 12.4-alpine. The "-alpine" is a much smaller base image and is recommended by docker hub "when final image size being as small as possible is desired". Our application is very small so we're going to use an alpine image.
51 |
52 | Next in the Dockerfile we're creating an `/app` directory and setting our working directory within the docker container to run in `/app`.
53 |
54 | After that we're going to "COPY" the `package.json` file to `package.json` on the docker container. We then install our dependencies from our `node_modules`. "COPY" the entire directory and run the command `node app.js` to start the node app within the docker container.
55 |
56 | ## Using Docker
57 |
58 | Now that we've gone over the boring details of a Dockerfile, lets actually build the thing.
59 |
60 | So when you installed Docker Desktop it comes with a few tools. Docker Command Line, Docker Compose and Docker Notary command line.
61 |
62 | We're going to use the Docker CLI to:
63 |
64 | - Build a docker image
65 |
66 | - Run the container locally
67 |
68 | ### Building an image
69 |
70 | The command for building an image is `docker build [OPTIONS] PATH | URL | -`. You can go to the [docs](https://docs.docker.com/engine/reference/commandline/build/) to see all the options.
71 |
72 | In the root directory of the application you can run `docker build -t docker-on-aws .`. This will tag our image as "docker-on-aws".
73 |
74 | To verify you successfully created the image you can run `docker images`. Mine looks like `docker-on-aws latest aa68c5e51a8e About a minute ago 82.8MB`.
75 |
76 | ### Running a container locally
77 |
78 | Now we are going to run our newly created image and see docker in action. Run `docker run -p 80:80 docker-on-aws`. The `-p` is defining what port you want your application running on.
79 |
80 | 
81 |
82 | You can now visit http://localhost:80.
83 |
84 | To see if your container is running via the CLI you can open up another terminal window and run `docker container ls`. To stop the image you can run `docker container stop `. Verify it stopped with `docker container ls` again or `docker ps`.
85 |
86 | ## Docker on Amazon ECS
87 |
88 | We're going to push the image we just created to Amazon ECR, Elastic Container Registry, create an ECS cluster and download the image from ECR onto the ECS cluster.
89 |
90 | Before we can do any of that we need to create an IAM user and setup our AWS CLI.
91 |
92 |
93 | ### Configuring the AWS CLI
94 |
95 | We're going to build everything with the AWS CLI.
96 |
97 | Go to the AWS Console and search for IAM. Then go to "Users" and click the blue button "Add User".
98 |
99 | 
100 |
101 | Create a user name like "ECS-User" and select "Programmatic Access".
102 |
103 | 
104 |
105 | Click "Next: Permissions" and select "Attach exisiting policies directly" at the top right. Then you should see "AdministratorAccess", we're keeping this simple and giving admin access.
106 |
107 | Click "Next: Tags" and then "Next: Review", we're not going to add any tags, and "Create user".
108 |
109 | Now you should see a success page and an "Access key ID" and a "Secret access key".
110 |
111 | 
112 |
113 | Take note of both the Access Key ID and Secret Access key. We're going to need that to configure the AWS CLI.
114 |
115 | Open up a new terminal window and type `aws configure` and input the keys when prompted. Set your region as `us-east-1`.
116 |
117 | 
118 |
119 | ### Creating an ECS Cluster
120 |
121 | To create an ECS Cluster you can run the command `aws ecs create-cluster --cluster-name docker-on-aws`.
122 |
123 | We can validate that our cluster is created by running `aws ecs list-clusters`.
124 |
125 | .
126 |
127 | If you wanted to delete the cluster you can run `aws ecs delete-cluster --cluster docker-on-aws`
128 |
129 |
130 | ### Pushing an Image to Amazon ECR
131 |
132 | Now that the CLI is configured we can tag our docker image and upload it to ECR.
133 |
134 | First, we need to login to ECR.
135 |
136 | Run the command `aws ecr get-login --no-include-email`. The output should be `docker login -u AWS -p` followed by a token that is valid for 12 hours. Copy and run that command as well. This will authenticate you with Amazon ECR. If successful you should see "Login Succeeded".
137 |
138 | Create an ECR Repository by running `aws ecr create-repository --repository-name docker-on-aws/nodejs`. That's the cluster name followed by the image name. Take note of the `repositoryUri` in the output.
139 |
140 | 
141 |
142 | We have to tag our image so we can push it up to ECR.
143 |
144 | Run the command `docker tag docker-on-aws .dkr.ecr.us-east-1.amazonaws.com/docker-on-aws/nodejs`. Verify you tagged it correctly with `docker images`.
145 |
146 | Now push the image to your ECR repo. Run `docker push .dkr.ecr.us-east-1.amazonaws.com/docker-on-aws/nodejs`. Verify you pushed the image with `aws ecr list-images --repository-name docker-on-aws/nodejs`.
147 |
148 | 
149 |
150 |
151 | ## Uploading a node.js app to ECS
152 |
153 | The last few steps involve pushing our node.js app to the ECS cluster. To do that we need to create and run a task definition and a service.
154 | Before we can do that we need to create an IAM role to allow us access to ECS.
155 |
156 | ### Creating an ecsTaskExecutionRole with the AWS CLI
157 |
158 | I have created a file called `task-execution-assume-role.json` that we will use to create the ecsTaskExecutionRole from the CLI.
159 |
160 | ```{
161 | "Version": "2012-10-17",
162 | "Statement": [
163 | {
164 | "Sid": "",
165 | "Effect": "Allow",
166 | "Principal": {
167 | "Service": "ecs-tasks.amazonaws.com"
168 | },
169 | "Action": "sts:AssumeRole"
170 | }
171 | ]
172 | }
173 | ```
174 |
175 | You can run `aws iam create-role --role-name ecsTaskExecutionRole --assume-role-policy-document file://task-execution-assume-role.json ` to create the role. Take note of the `"Arn"` in the output.
176 |
177 | 
178 |
179 | Then run `aws iam attach-role-policy --role-name ecsTaskExecutionRole --policy-arn arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy` to attach the "AmazonECSTaskExecutionRolePolicy".
180 |
181 | Take the `"Arn"` you copied earlier and paste it into the `node-task-definition.json` file for the `executionRoleArn`.
182 |
183 | ```
184 | {
185 | "family": "nodejs-fargate-task",
186 | "networkMode": "awsvpc",
187 | "executionRoleArn": "arn:aws:iam::xxxxx:role/ecsTaskExecutionRole",
188 | "containerDefinitions": [
189 | {
190 | "name": "nodejs-app",
191 | "image": "xxxxx.dkr.ecr.us-east-1.amazonaws.com/docker-on-aws/nodejs:latest",
192 | "portMappings": [
193 | {
194 | "containerPort": 80,
195 | "hostPort": 80,
196 | "protocol": "tcp"
197 | }
198 | ],
199 | "essential": true
200 | }
201 | ],
202 | "requiresCompatibilities": [
203 | "FARGATE"
204 | ],
205 | "cpu": "256",
206 | "memory": "512"
207 | }
208 | ```
209 |
210 | ### Registering an ECS Task Definition
211 |
212 | Once your IAM role is created and you updated the `node-task-definition.json` file with your `repositoryUri` and `executionRoleArn` and you can register your task.
213 |
214 | Run `aws ecs register-task-definition --cli-input-json file://node-task-definition.json `
215 |
216 | ### Creating and ECS Service
217 |
218 | The final step to this process is creating a service that will run our task on the ECS Cluster.
219 |
220 | We need to create a security group with port 80 open and we need a list of public subnets for our network configuration.
221 |
222 | To create the security group run `aws ec2 create-security-group --group-name ecs-security-group --description "Security Group us-east-1 for ECS"`. That will output a security group ID. Take note of this ID. You can see information about the security group by running
223 | `aws ec2 describe-security-groups --group-id `.
224 |
225 | It will show that we don't have any IpPermissions so we need to add one to allow port 80 for our node application. Run `aws ec2 authorize-security-group-ingress --group-id --protocol tcp --port 80 --cidr 0.0.0.0/0` to add port 80.
226 |
227 | Now we need to get a list of our public subnets and then we can create the ECS Service.
228 |
229 | Run `aws ec2 describe-subnets` in the output you should see `"SubnetArn"` for all the subnets. At the end of that line you see "subnet-XXXXXX" take note of those subnets. Note: if you are in `us-east-1` you should have 6 subnets
230 |
231 | Finally we can create our service.
232 |
233 | Replace the subnets and security group Id with yours and run ` aws ecs create-service --cluster docker-on-aws --service-name nodejs-service --task-definition nodejs-fargate-task:1 --desired-count 1 --network-configuration "awsvpcConfiguration={subnets=[ subnet-XXXXXXXXXX,
234 | subnet-XXXXXXXXXX,
235 | subnet-XXXXXXXXXX,
236 | subnet-XXXXXXXXXX,
237 | subnet-XXXXXXXXXX,
238 | subnet-XXXXXXXXXX],securityGroups=[sg-XXXXXXXXXX],assignPublicIp=ENABLED}" --launch-type "FARGATE"`.
239 |
240 | Running this will create the service `nodejs-service` and run the task `nodejs-fargate-task:1`. The `:1` is the revision count. When you update the task definition the revision count will go up.
241 |
242 | ## Viewing your nodejs application.
243 |
244 | Now that you have everything configured and running it's time to view the application in the browser.
245 |
246 | To view the application we need to get the public IP address. Go to the ECS dashboard, in the AWS Console, and click on your cluster.
247 |
248 | 
249 |
250 | Then click the "tasks" tab and click your task ID.
251 |
252 | 
253 |
254 | From there you should see a network section and the "Public IP".
255 |
256 | 
257 |
258 |
259 | Paste the IP address in the browser and you can see the node application.
260 |
261 | 
262 |
263 | Bam! We have a simple node application running in an Amazon ECS cluster powered by Fargate.
264 |
265 | If you don't want to use AWS and just want to learn how to use Docker check out my last [blog](https://medium.com/@awsmeetupgroup/tutorial-docker-and-node-js-2d7fde6eb38b)
266 |
267 | Also, I attached some links here for more examples of task definitions you could use for other applications.
268 |
269 | https://docs.aws.amazon.com/AmazonECS/latest/developerguide/example_task_definitions.html
270 |
271 | https://github.com/aws-samples/aws-containers-task-definitions/blob/master/
272 |
--------------------------------------------------------------------------------