The Startup
Published in

The Startup

Deploying a Node.js App to AWS Through the Command Line

Combining AWS CLI and AWS CodeDeploy for a free, simple, quick CI/CD.

This article is intended for use as an introduction to deployment using AWS infrastructure. The focus will be on getting a working app deployed to a server, so you should have something ready to go. The example used is an Express app written in Typescript, so there will be a compile phase, run locally. I'm running on macOS so any local commands are run through the terminal.

You should already be familiar with launching and connecting to EC2s, have an Express app ready to go, AWS CLI installed and configured on your development machine, and understand how to use IAM Roles.

The following is an outline of the steps to work through:

  • Launch an EC2 instance, installing AWS CLI, CodeDeploy agent, Node and MySql on it
  • write a script that compiles your app, and uploads it to S3
  • write the shell scripts and an appspec.yml file for CodeDeploy to install the app to the server
  • use the AWS CLI to trigger a deployment

Setting up the EC2 instance

Select launch instance from the AWS Console EC2 dashboard. For this example, I am using an Ubuntu 20.04 LTS. If you use something different, you may need to make changes to scripts/shell commands.

In the configure instance details step, for the IAM Role option, create a new role and attach the S3ReadOnlyAccess policy to it. The instance needs to have S3 read access to be able to retrieve the app’s code.

Under advanced, there is a ‘user data’ field. Here you can enter the shell commands to be run on the instance when launched. Paste the commands below into this field. This will set up the Ubuntu server appropriately.

Instead of adding to the user data, you could wait until the instance is launched, make an SSH connection, and then run these commands.

Select the storage you need for your app. Under tags, you need to set a Key-Value pair that CodeDeploy can identify the instance by. Any instances that match the values you provide to CodeDeploy will be part of the deployment group. So make sure the tag is unique to your account.

Under security groups, set up rules for SSH (22) and MySQL (3306). And one more custom TCP rule for port 8080 (or any port you want), for the Express app. All with your IP as the source. When you want to open up the app to public access you will need to change this or set up a web server such as apache.

Launch the instance, ensuring you have access to the key pair as you will need to be able to connect to it.

You can view the log generated from the user data commands at /var/log/cloud-init-output.log .

If you SSH into the instances as soon as it is created, the commands will likely still be running. Enter tail /var/log/cloud-init-output.log -f to watch the log as it generates.

Compile and upload to S3

Writing a shell script that compiles code and uploads to S3 will save a lot of time and reduce repetition.

First, run aws s3 create-bucket --bucket yourAppsBucketNamein your terminal to have a bucket ready to upload the app to. Alternatively, create the bucket in the AWS console. Turn on versioning for the bucket's objects, so S3 handles the version management for you.

For reference, below are the relevant parts of my project directory. You’ll need to make changes to your tsconfig.json and other scripts if your project structure is different.

ProjectRoot/
|--express/
| |--src/
| |--node_modules/
| |--package.json
| |--tsconfig.json
|--dist/
|--shScripts/
|--build.sh
|--appspec.yml
|--appName.service
|--.gitignore

In the tsconfig.json,set the compilerOptions.SourceMap property to "../dist" .

Below is the build.sh file that will compile, add some required files to the /dist folder, Zip it and upload to AWS S3.

Complete the next section before running this script.

Preparing for CodeDeploy

To run CodeDeploy, you’ll need a file named appspec.yml at the root of your project. The CodeDeploy agent uses this to run the various commands needed to get your app running on the instance.

Below is an example of the appspec.yml file. Pay attention to the indentation.

There are many other hooks available to suit different needs, but these are the two key ones that will serve the most use. When the agent triggers these hooks, the scripts referenced will be run.

Create a folder called shScripts , then add the three script files shown below:

cleanup.sh

#!/bin/bash
sudo rm -rf /var/www/yourAppName

This will remove the current version of the app if it exists.

dependencies.sh

#!/bin/bash
cd /var/www/yourAppName
sudo npm install --only=prod

Install the node dependencies fro your app.

runApp.sh

This will tell your server to run node as a service. Your express sever’s file will need to have #! /bin/bash as the first line to allow it to be executed.

Next, create a .service file which systemctl will use to run the app as a service.

yourAppName.service

This will instruct systemctl on how to start the app and to restart if it crashes.

Run it

With all of this in place, you are ready to run the script build.sh, created earlier.

In the terminal, navigate to the root of your project. Give execute permissions to the build script by running chmod u+x build.sh and then enter ./build.sh .

You should see the output in your terminal working through the commands, with the final output giving a successful upload to S3.

Check that the file has uploaded correctly, and unpack the .zip to check all the files required are in the right place.

AWS CodeDeploy

Now that everything is ready to go, the last things to do are create a service role for CodeDeploy, prepare an application and deployment group in CodeDeploy and finally create a deployment.

I’ve outlined below how to do this through the AWS CLI, but you can easily follow this in the AWS Console.

You first need to create an IAM Role so CodeDeploy has the appropriate permissions. Create a file in your project root called CodeDeployRole.json , add the following to it:

{"Version": "2012-10-17",     
"Statement": [
{
"Sid": "",
"Effect": "Allow",
"Principal": {
"Service": [
"codedeploy.amazonaws.com"
]
},
"Action": "sts:AssumeRole"
}
]
}

Create the role, from the terminal at your project root by running:

aws iam create-role \
--role-name CodeDeployServiceRole \
--assume-role-policy-document file://CodeDeployRole.json

You should see the created role output. From this output copy the Role.Arn value to your clipboard, as it will be needed to create the deployment group.

Next, you can attach the managed policy for CodeDeploy to the created role:

aws iam attach-role-policy \
--role-name CodeDeployServiceRole \
--policy-arn arn:aws:iam::aws:policy/service-role/AWSCodeDeployRole

Now you are ready to create the application, deployment group, and deployment.

At your terminal run aws deploy create-application --application-name yourAppName and you should get an application id as the response.

Next, run the following in your terminal to create a deployment group. Remember to replace the --ec2-tag-filters Key-Value to match the EC2 instance created earlier, and change the --service-role-arn to the arn copied from the previous step. The deployment group name can be whatever you want.

aws deploy create-deployment-group \
--application-name yourAppName \
--deployment-group-name yourAppName-DGroup1 \
--ec2-tag-filters Key=PROJECT,Value=yourAppName,Type=KEY_AND_VALUE \
--service-role-arn arn:aws:iam::221433653687:role/CodeDeployRole

Finally, you can create the deployment. When you run the following command, CodeDeploy will attempt to install and run your application. Be sure to put your application and deployment group names in, and set the correct bucket and key values for --s3-location .

Put the following in a file called deploy.sh :

aws deploy create-deployment \
--application-name yourAppName\
--deployment-config-name CodeDeployDefault.OneAtATime \
--deployment-group-name yourAppName-DGroup1 \
--description "$1" \
--s3-location bucket=yourAppBucketName,bundleType=zip,key=yourAppName-dist.zip \
--ignore-application-stop-failures

Give the file executable permissions with chmod u+x deploy.sh , then run it passing in the description as the first parameter:

./deploy.sh "Deployment Description"

You should receive a deployment id as the output from this command. Copy this id, and run the following command, replacing the id with yours to see the status of the deployment:

aws deploy get-deployment --deployment-id d-A1B2C3123

The AWS Console is an easier place to view the events of your deployment. On the AWS site go to Services>CodeDeploy>Deploy>Deployments and click on your deployment id from the list. Here you'll be able to see the events and view any logs from them.

Put your instances IP:AppPort (eg. 18.133.224.119:8080) into your web browser, and you should get a response from your Express app.

You’ve now successfully created a CI/CD pipeline suitable for small applications that can be compiled and deployed locally from your command line.

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store