Introduction to Beanstalk
Elastic Beanstalk is an Amazon web service that allows you to quickly deploy and manage web applications to the cloud without the hassle of learning and managing the infrastructure surrounding it all. You can simply upload your application and Beanstalk automatically handles the details of capacity provisioning, load balancing, scaling, and application health monitoring.
Beanstalk supports applications developed in Go, Java, .NET, Node.js, PHP, Python, and Ruby. When you deploy your application, Elastic Beanstalk builds the selected supported platform version and provisions one or more AWS resources, usually Amazon EC2 instances, to seamlessly host and run your application. Simply upload your application in the form of an application source bundle (usually a .zip of the source) to Elastic Beanstalk, and then provide some information about the application. Elastic Beanstalk automatically launches an environment and creates and configures the AWS resources needed to run your application.
To deploy a test application to Beanstalk, follow this quick guide provided by Amazon. When on the "Create Application" step, use the following configurations:
- Platform:
NodeJs
- Application Code:
Sample Application
We will obviously modify these settings and add our own source code next. But after following the rest of the guide, you will have an EC2 instance created and deployed as well as a fully functioning web app.
Custom Build & Express Configuration
After the EC2 instance is setup and provisioned, your source code will be unzipped and added to the Docker container. Once this has occurred, the following will happen by default unless otherwise configured:
npm install --production
will be ran.npm start
will be ran whereby your code should be built and the server started and listening on port8081
.
This configuration may work for some but my workflow was much more complicated than this. For myself, I needed the following:
npm install
to be ran in development mode.- Transpile, bundle and generate translations for my application via
Babel
andWebpack
. - Start my Express server on a port other than
8081
.
To fix the issues above, first create an .ebextensions
folder in the root of your project. Next add 00_change_npm_permissions.config
and options.config
files to this folder. These configuration files can be written in either YML or JSON. My examples below use YML.
The configuration file below technically fixes all of the issues outlined above. But depending on your applications dependencies you my need another configuration file as I did.
option_settings:
aws:elasticbeanstalk:application:environment:
PORT: 1771
NPM_USE_PRODUCTION: false
aws:elasticbeanstalk:container:nodejs:
NodeCommand: "npm run deploy"
NodeVersion: 10.15.1
The source above is pretty self explanatory but lets explain further:
- Line 3 instructs the proxy server to forward traffic to port
1771
. This allows my Express server to receive traffic. - Line 4 instructs the build process to run npm in development mode.
- Line 6 instructs the build process to run
npm run deploy
. (I will get into my build and server starting process after later on.) - Line 7 declares the version of Node to use for the application. My application required at least version 10.14 to build and run properly.
Great! That should customize and therefore fix all of the problems for most people. However I had a few dependencies that required access to the tmp
directory to install properly. I found this out when I attempted to deploy my app and ran into the following error:
npm ERR! Error: EACCES: permission denied, open '/tmp/.npm/_locks
npm ERR! Please try running this command again as root/Administrator.
To fix this error, add the the following to your 00_change_npm_permissions.config
file. This will instruct the container to run a chmod
cmd the tmp
folder to give the node process write access to the directory.
files:
"/opt/elasticbeanstalk/hooks/appdeploy/post/00_set_tmp_permissions.sh":
mode: "000755"
owner: root
group: root
content: |
#!/usr/bin/env bash
chown -R nodejs:nodejs /tmp/.npm
Below is a contrived example of my Express server file that is listening on my custom port.
const express = require('express')
const app = express()
app.set('port', (process.env.PORT || 1771))
app.use(express.static('dist'))
app.listen(app.get('port'), () => {
console.log('Express server has started! http://localhost:' + app.get('port') + '/')
})
To complete my build process and then start my Express server, I took advantage of NPMs pre and post script ability to run multiple tasks via one command. Read more about this awesome feature here.
{
"predeploy": "npm install",
"deploy": "cross-env-shell NODE_ENV=production webpack-cli --config ./config/webpack.prod.config.js --display-error-details",
"postdeploy": "node prodServer.js",
}
With this configuration, I can now install dependencies via predeploy
, then transpile and build my code via deploy
and finally start my server via postdeploy
. This will be executed in order by the npm run deploy
command that I configured the build container to initialize with in the options.config
file above.
Uploading The App
Now that the application and server is configured, uploading and deploying it is pretty straightforward. First we will need to compress our source code (not including the root container folder) into into a zip file. For example, you may have all of your files in a folder called "Project". Rather than zipping "Project", you should zip and upload the contents of "Project". Be sure to include hidden files and folders and not the node_modules
folder, as the dependencies will be installed during the build process.
If you fail to zip the source correctly, you will most likely get the following error when attempting to deploy.
npm ERR! enoent ENOENT: no such file or directory, open '/var/app/current/package.json'
npm ERR! enoent This is most likely not a problem with npm itself
npm ERR! enoent and is related to npm not being able to find a file.
Next go to your Elastic Beanstalk applications page inside AWS and click the "Upload and Deploy" button. Add your new zip file, give the version a label and deploy the application. It might take a few minutes to build and deploy the application and the health of the system will fluctuate between various statuses.
Key Takeaways
- When uploading your zipped source code:
- DO NOT include the root package folder
- DO be sure include all the hidden files and folders
- The proxy layer between EC2 and your application will forward requests to port
8081
by default. Express must be listening on port 8081 - Elastic Beanstalk automatically installs your apps dependencies via NPM in production mode via
npm install --production
- When starting your application, Beanstalk will automatically run
npm start
- You can customize all this and more by adding configuration files inside of an
.ebextensions
folder placed in the root of your application.
Next Time
That's really all there is to it if you're okay with a manually intensive build process. But c'mon, let's be real here, we are engineers and we all hate repetitive tasks. As with anything AWS, it is super simple to create an automated CI/CD pipeline that is complete with source code webhooks, build stages, test stages and custom deployment options.
Tune in next time as I will dive into AWS's Codebuild service!