Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • 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.

@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