Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 30 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sjlu/63bce0f7003b217bc800602c81fbce12 to your computer and use it in GitHub Desktop.
Save sjlu/63bce0f7003b217bc800602c81fbce12 to your computer and use it in GitHub Desktop.
Migrating from Amazon Linux to Amazon Linux 2 with Elastic Beanstalk and Node.js

This file is a log of everything I've encountered when trying to migrate a Node.js, Elastic Beanstalk application from the Amazon Linux platform to the Amazon Liunx 2 platform. Here's why you should migrate:

  1. LTS support up to 2023 source
  2. The Amazon Linux AMI's end-of-life is December, 2020 source
  3. Amazon Linux 2 has some big package upgrades (GCC, Glibc, etc.)
  4. Elastic Beanstalk also has some upgrades on top of Amazon Linux 2 (e.g. faster deploys)

Challenges

Disabling NPM install

Previously you were able to use a .ebextensions file and override /opt/elasticbeanstalk/hooks/appdeploy/pre/50npm.sh and /opt/elasticbeanstalk/hooks/configdeploy/pre/50npm.sh. The new EB version doesn't have this and runs its commands internally.

I found that Elastic Beanstalk will not run npm if it cannot find your package.json file and skip those commands. To avoid making modifications to the code base, we temporarily move it during the build phase of Elastic Beanstalk. To do so, we utilized .platform/hooks.

./platform/hooks/prebuild/00_disable_npm.sh

#!/bin/bash
mv package.json package.json.tmp
mv package-lock.json package-lock.json.tmp

./platform/hooks/predeploy/00_enable_npm.sh

#!/bin/bash
mv package.json.tmp package.json
mv package-lock.json.tmp package-lock.json

These two files must have executable permissions, remember to run chmod +x on them!

nginx config

The default nginx config is a bit limiting and you'd probably want to extend it in some way. The old way of using an .ebextensions file gets blown away by Elastic Beanstalk on deploy so you'll now want to create a .platform/nginx/conf.d/proxy.conf file instead, a method that Elastic Beanstalk recognizes.

Here's an example we utilize to increase the upload size and timeout limits.

client_max_body_size 32M;
proxy_connect_timeout 120s;
proxy_send_timeout 120s;
proxy_read_timeout 120s;
send_timeout 120s;

Logs

Logging is now done with syslog and writes to /var/log/web.stdout.log and /var/log/web.stderr.log. If you want to mimick the previous version of /var/log/nodejs/nodejs.log, you'll need to edit Elastic Beanstalk's default rsyslog config /opt/elasticbeanstalk/config/private/rsyslog.conf. We are using an .ebextension to override it.

As of 5.1.0, the template path has changed. It is now generated based on the Procfile environment.

<=5.1.0

files:
  "/opt/elasticbeanstalk/config/private/rsyslog.conf":
    mode: "000644"
    owner: root
    group: root
    content: |
      # This rsyslog file redirects Elastic Beanstalk platform logs.
      # Logs are initially sent to syslog, but we also want to divide
      # stdout and stderr into separate log files.

      template(name="SimpleFormat" type="string" string="%msg%\n")
      $EscapeControlCharactersOnReceive off

      if $programname  == 'web' then {
        *.=warning;*.=err;*.=crit;*.=alert;*.=emerg; /var/log/web.stderr.log; SimpleFormat
        *.=info;*.=notice /var/log/web.stdout.log; SimpleFormat
      }

5.1.0+

files:
  "/opt/elasticbeanstalk/config/private/rsyslog.conf.template":
    mode: "000644"
    owner: root
    group: root
    content: |
      # This rsyslog file redirects Elastic Beanstalk platform logs.
      # Logs are initially sent to syslog, but we also want to divide
      # stdout and stderr into separate log files.

      template(name="SimpleFormat" type="string" string="%msg%\n")
      $EscapeControlCharactersOnReceive off

      {{range .ProcessNames}}if $programname  == '{{.}}' then {
        *.=warning;*.=err;*.=crit;*.=alert;*.=emerg /var/log/{{.}}.stderr.log; SimpleFormat
        *.=info;*.=notice /var/log/{{.}}.stdout.log; SimpleFormat
      }
      {{end}}

npm start

You no longer are able to specify a start command. Instead, they implemented Heroku's version of Procfile. Make sure you have a web: npm start proc.

Additionally, if you use the same code base for different environments, you'll need to use an environment variable and have your own bash script choose what to run.

#!/bin/bash
#!/bin/bash

if [ "$INTERSELLER_ENV" = "" ]
then
  echo -e "INTERSELLER_ENV not set\n"
else
  npm run $INTERSELLER_ENV
fi

Node engine

In AL1, the Node.js version to use was specified inside your environment's configuration. This is no longer the case in AL2, which now follows the engines.node key of your package.json. The value MUST be a precise identifier, eb-engine will error if you pass an approximate specifier like >=12.10.0 . Here is a correct configuration:

  "engines": {
    "node": "12.18.3",
    "npm": ">=6.10.2"
  },

Thanks @ronjouch for this one!

yum Security Updates

By default, Elastic Beanstalk prevents newer packages from being installed on the image. This is because there is a $guid lock in the yum repository. You can use this .ebextensions to unlock and update minimal security patches on the instance.

files:
  "/home/ec2-user/yum-update-security.sh":
    mode: "00755"
    owner: root
    group: root
    encoding: plain
    content: |
      #!/bin/bash
      ## Enables installing minimal security patches
      sed -i -e s/-\$guid// /etc/yum.repos.d/*.repo
      yum update-minimal --security -y


commands:
  00_run:
    command: "/home/ec2-user/yum-update-security.sh"

Updating Elastic Beanstalk Amazon Linux 2 Kernel

If you have a SLA that does not fit within Elastic Beanstalk's SLA, you may need to update your kernel manually. By Elastic Beanstalk's own recommendation to do so, creating a custom AMI is the way to go as it'll have everything pre-installed (and not take years to attempt installing EB on that instance).

The instructions to create the custom AMI and use the custom AMI are here: https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/using-features.customenv.html

Updating the kernel using live patching leads to a lot of problems and yum running constantly to find those patches, which leads to CPU issues. It's under my personal recommendation not to use this method.

@ronjouch
Copy link

@sjlu thanks for the writeup, it was useful. One more thing deserving a section would be this below. Feel free to copy-paste & integrate to your gist. Thanks again!


Node engine

In AL1, the Node.js version to use was specified inside your environment's configuration. This is no longer the case in AL2, which now follows the engines.node key of your package.json. The value MUST be a precise identifier, eb-engine will error if you pass an approximate specifier like >=12.10.0 . Here is a correct configuration:

  "engines": {
    "node": "12.18.3",
    "npm": ">=6.10.2"
  },

@sjlu
Copy link
Author

sjlu commented Aug 18, 2020

@ronjouch 👍

@yuanzong
Copy link

rsyslog.conf overwrites stops working since Amazon Linux 2/5.1.0

in the log, I saw

2020/09/17 23:17:18.700937 [INFO] Generating rsyslog config from Procfile

@sjlu
Copy link
Author

sjlu commented Sep 18, 2020

@yuanzong it has been updated to /opt/elasticbeanstalk/config/private/rsyslog.conf.template - I've updated the template we use

@yuanzong
Copy link

@yuanzong it has been updated to /opt/elasticbeanstalk/config/private/rsyslog.conf.template - I've updated the template we use

Thanks! Where did you find out all the changes?

@sjlu
Copy link
Author

sjlu commented Sep 18, 2020

@yuanzong trial and error

@WtfJoke
Copy link

WtfJoke commented May 17, 2021

@sjlu I think youre missing ; SimpleFormat in the 5.1.0+ example, arent you?

@sjlu
Copy link
Author

sjlu commented May 17, 2021

@WtfJoke it doesn't look necessary as it could be the default format now

@nutrienrob
Copy link

@sjlu I think youre missing ; SimpleFormat in the 5.1.0+ example, arent you?

I needed to add ; SimpleFormat in order for it to work.

@pgoldweic
Copy link

pgoldweic commented Sep 29, 2021

Thanks so much for your gist! A couple of basic questions: How do run the chmod command to change permissions on the scripts you used for disabling/enabling npm? Does this go somewhere in ebextensions? Also, how do you know the exact node version that the platform branch is using?

@sjlu
Copy link
Author

sjlu commented Sep 30, 2021

Hey @pgoldweic,

AWS has some good documentation around using .ebextensions but to answer your question, yes. It's part of the .ebextension. Here's an example:

files:
  "/tmp/file":
    mode: "000755"
    owner: root
    group: root

Node versions are determined by the Elastic Beanstalk version you are using. The release notes here are a good way to figure out which EB version is running which Node versions: https://docs.aws.amazon.com/elasticbeanstalk/latest/relnotes/release-2021-09-02-linux.html

@pgoldweic
Copy link

pgoldweic commented Sep 30, 2021

thanks @sjlu ! I am still running into trouble when trying to make my hook files executable, apparently because I can't figure out the exact location of these hook files when they get processed by an ebextension. That is, even though I include the hooks in the expected folders at .platform/hooks/... , where are these hook files when the ebextension - that wants to make them executable- runs? Would you mind sharing the chmod line -that presumably is part of one of your ebextensions- that accomplishes this permissions change on one of your hooks?That would be quite helpful.

@sjlu
Copy link
Author

sjlu commented Sep 30, 2021

@nutrienrob I've updated the gist to include ; SimpleFormat as it'll make it easier for others

@onury
Copy link

onury commented Oct 31, 2021

Disabling NPM install is no longer needed if you include node_modules within your bundle.

From AWS docs: When you deploy a node_modules directory to an Amazon Linux 2 Node.js platform version, Elastic Beanstalk assumes that you're providing your own dependency packages, and avoids installing dependencies specified in a package.json file.

@pgoldweic
Copy link

pgoldweic commented Nov 1, 2021

I second @onury comments regarding the NPM disabling, as I've experienced this too. However, my last comment/question above applies to hooks in general, and not to the enable/disable ones (so it would be great to get an answer for it :-)).

@sjlu
Copy link
Author

sjlu commented Nov 1, 2021

@onury and @pgoldweic would you be able to tell me which EB version you're on?

@pgoldweic
Copy link

@sjlu , I am on EB CLI 3.19.4 (Python 3.6.4). I understand it's not the latest though, but haven't upgraded yet.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment