Tuesday, 7 February 2023

AWS Security Groups

AWS Security Groups control the inbound and outbound traffic for various AWS resources: 
  • EC2 instance
    • running applications e.g. web server
    • running as DNS server
  • RDS - Database server
  • EFS file system
  • Elastic Load Balancer
  • VPC peering rules


Security groups are VPC-specific (and therefore region-specific). They can only be used within the VPC they are created. The exception is where there is a peering connection to another VPC, in which case they can be referred to in the peered VPC. 

For Security Group we can set:

  • Name
  • Description
  • VPC. VPC is region-specific so is security group.
  • Inbound rules
  • Outbound rules

For Security Group Rule (Inbound or Outbound) we can set:
  • Type. The protocol to open to network traffic. You can choose a common protocol, such as SSH (for a Linux instance), RDP (for a Windows instance), and HTTP and HTTPS to allow Internet traffic to reach your instance. You can also manually enter a custom port or port ranges.
  • Protocol. The type of protocol, for example TCP or UDP. Provides an additional selection for ICMP.
  • Port range. For custom rules and protocols, you can manually enter a port number or a port range.
  • Source. Determines the traffic that can reach your instance. Specify a single IP address, or an IP address range in CIDR notation (for example, If connecting from behind a firewall, you'll need the IP address range used by the client computers. You can specify the name or ID of another security group in the same region. To specify a security group in another AWS account (EC2-Classic only), prefix it with the account ID and a forward slash, for example: 111122223333/OtherSecurityGroup.
  • Description. A description for a security group rule.
    A description can be up to 255 characters in length.
    Allowed characters are a-z, A-Z, 0-9, spaces, and ._-:/()#,@[]+=;{}!$*.

Example: we have a Node.js application that is receiving traffic on port 8080, only from a Load Balancer that is on the same VPC. This means we need to create an Inbound rule:

  • Type: Custom TCP
  • Protocol: TCP
  • Port range: 8080
  • Source: Custom; CIDR block: (in our example we're using a default VPC so we'll put here its private IP address block thus allowing only access from the private network)


Terraform Security Group resource

aws_security_group | Resources | hashicorp/aws | Terraform Registry


Terraform Security Group Rule resource

It represents a single ingress or egress group rule, which can be added to external Security Groups: 

aws_security_group_rule | Resources | hashicorp/aws | Terraform Registry

Required arguments: 

  • from_port: start port
  • to_port: end port
  • protocol
    • icmp
    • icmpv6
    • tcp
    • udp
    • all
  • security_group_id: Security group to apply this rule to.
  • type
    • ingress (inbound)
    • egress (outbound) 
Optional arguments:
  • self. Whether the security group itself will be added as a source to this ingress rule.
  • source_security_group_id.  Security group id to allow access to/from, depending on the type.
  • ...

Because security group rule gets attached to the security group, we need to instruct Terraform to provision security group rule after the security group. We do this by using depends_on meta argument:

resource "aws_security_group_rule" "my_ec2_ssh" {
  type            = "ingress"
  from_port       = 22
  to_port         = 22
  protocol        = "tcp"
  cidr_blocks = var.ssh_ip_range
  security_group_id = aws_security_group.my_ec2_sg.id
  depends_on = [aws_security_group.my_ec2_sg]


Security group rules for different use cases - Amazon Elastic Compute Cloud

Monday, 6 February 2023

AWS EC2: Auto Scaling

If our application is getting more traffic, we need to scale it: we need to create more virtual machines to run it so can handle the load, we need a load balancer to distribute the network traffic to these instances. Instead of doing this manually (creating new instances and e.g. using NGINX as load balancer) we can use AWS Auto-scaling Group and AWS Application Load Balancer.
  • Automatically maintains application performance based on the user requirement at the lowest possible price
  • Service which helps user to monitor applications and automatically adjusts capacity to maintain steady, predictable performance at the lowest possible cost
  • Benefits:
    • better fault tolerance
    • better cost management
    • reliability of your service
    • scalability
    • flexibility - changes can be made on the fly
  • Snapshot vs AMI
    • Snapshot
      • used as a backup of a single EBS volume attached to the EC2 instance
      • opt for it when the instance contains multiple static EBS volumes
      • pay only for the storage of the modified data
      • a non-bootable image on EBS volume
    • AMI
      • used as a backup of an EC2 instance
      • widely used to replace a failed EC2 instance
      • pay only for the storage that you use
      • bootable image on EC2 instance
      • creating an AMI image will also create EBS snapshots

How does AWS auto scaling work?

  • Configure single unified scaling policy per application source
  • explore the application
  • choose the service you want to scale 
  • select what to optimize e.g. cost or performance
  • keep track of scaling

Different scaling plans

  • scaling plan helps user to configure a set of instructions for scaling based on software requirement

Launch Template

Let's first explore a tool that can save time when scaling (creating multiple EC2 instances) manually.

How to set up a Launch Template?

EC2 >> Instances >> Launch Template:

We can set:

  • Name
  • Version description
  • AMI. We can create AMI for each version of our application an name them e.g. amy-my-app-v1, ami-my-app-v2 etc...In the same way we can create new version of launch template and bind desired AMI (application) version to it.
  • Instance type e.g. t2.micro 
  • Key pair (for secure connection to the instance)
  • Network settings
    • Launch into:
      • VPC
      • Shared network
    • Security group
  • Storage (volumes e.g. Volume 1(AMI Root, 8GB, EBS, General Purpose SSD))
  • Resource tags
  • Network interfaces
  • User data

User data example for setting an environment variable in our application's env config file:

#!/bin/bash -ex
sudo -u ec2-user bash -c "echo \"
MY_ENV_VAR="My env var value, set from the template" 
\" > /home/ec2-user/my-app.env
systemctl restart --now --no-block version-my-app.service


Shebang arguments explained:

-e Exit immediately if a command exits with a non-zero status.
-x Print commands and their arguments as they are executed.


Launch templates are versioned but we can't manually set the version number. AWS does it automatically by incrementing numbers from version 1.

Launch templates can be used outside Auto-scaling or Load Balancing: whenever we want to launch the instance, we don't need to manually fill details about the new instance, we can just use Launch instance from template.

In our scenario though, we want auto-scaling group to use launch template in order to launch EC2 instances. 


Auto scaling group


Auto scaling group manages how many EC2 instances will be running in parallel.

How to set up an Auto scaling group?

EC2 >> Auto scaling groups >> Create Auto scaling group


  • Choose launch template or configuration
    • Auto scaling group name
    • Launch template. We can select it and also select:
      • Launch template version (we can use this to select the version of our application that we want to be running on these new instances)
  • Choose instance launch options
    • Network
      • VPC
      • Availability Zones and subnets (in the form az_name|subnet_name); We want to list here all of them so we have a large pool of AZs in case something happens to EC2 instances running in some of them. 
    • Instance type requirements - we can override launch template here
  • Configure advanced options
    • Load balancing (optional)
      • No Load Balancer - traffic to auto scaling group will not be fronted by a load balancer
      • Attach to an existing load balancer
        • We'll specify here target groups actually (not load balancer directly)
      • Attach to a new load balancer - quickly create a basic load balancer
    • Health checks (optional)
      • Health Check type
        • EC2 - always enabled
        • ELB - if load balancing is enabled
      • Health check grace period - amount of time until EC2 auto scaling performs the first health check on new instances after they are put into service e.g. 300 seconds
    • Additional settings (optional)
      • Monitoring - enable group metrics collection withing CloudWatch
  • Configure group size and scaling policies
    • Group size
      • Desired capacity. How many EC2 instances do we want running simultaneously? Must be between minimum and maximum. E.g. 2
      • Minimum capacity e.g. 2
      • Maximum capacity e.g. 8
    • Scaling policies - choose whether to use a scaling policy to dynamically resize auto scaling group to meet changes in demand
      • Target tracking scaling policy - choose a desired outcome and leave it to the scaling policy to add and remove capacity as needed to achieve that outcome
        • Name - we can chose an arbitrary name
        • Metric type e.g. Average CPU utilization (average over all EC2 instances running)
        • Target value e.g. 50 (%)
        • Instances need ____ seconds warm up before including in metric
        • Disable scale in to create only a scale-out policy
      • None
    • Instance scale-in protection (optional); if enabled, newly created instances will be protected from scale-in by default
  • Add notifications - send notifications to SNS topics whenever Amazon EC2 auto-scaling launches or terminates the EC2 instances in your auto scaling group
  • Add tags
  • Review

As soon as auto scaling group is created it becomes active. If we set to have 2 as a desired number of instances, auto scaling group will immediately create them. If we try to delete them, auto scaling group will immediately recreate them. If we want permanently to stop these instances we need first to delete auto scaling group and then delete those EC2 instances. 

As seen above, Load balancing is optional so auto scaling (using auto scaling groups) can all happen with NO Load Balancer involved. But typically, we want to have load balancer so the load is equally distributed across all running EC2 instances. We can set up NGINX server as load balancer or use AWS (Application) Load Balancer in which case, in Load balancing options above, we'll choose to attach auto scaling group to an existing or newly created Load Balancer. It's worth mentioning that auto-scaling group is associated to load balancer indirectly, via target groups.


App Scaling - AWS Application Auto Scaling - AWS

Thursday, 2 February 2023

AWS EC2: Elastic Load Balancing

AWS Elastic Load Balancing:
  • has the task of distributing traffic throughout the cluster of servers to ensure higher responsiveness and availability of applications, websites, or databases
  • automatically distributes the incoming traffic across multiple targets, such as:
    • EC2 instances
    • containers
    • IP addresses  
             ...in one or more Availability Zones.
  • monitors the health of its registered targets, and routes traffic only to the healthy targets
  • targets can be added and removed from load balancer as the needs change, without disrupting the overall flow of requests to the application 
  • scales your load balancer as your incoming traffic changes over time
  • supports the following load balancers:  
    • Application Load Balancers
    • Network Load Balancers
    • Gateway Load Balancers
    • Classic Load Balancers 

Load balancer:
  • serves as the single point of contact for clients
  • distributes incoming application traffic across multiple targets which increases the availability of your application
  • has one or more listeners added to it

  • checks for connection requests from clients, using the protocol and port that you configure
  • has rules defined for it
    • a default rule must be defined for each listener, additional rules can be defined optionally 
    • Rules determine how the load balancer routes requests to its registered targets
    • Each rule consists of:
      • priority
      • one or more actions
      • one or more conditions; when conditions for a rule are met, then its actions are performed
      • specified target groups
    • When load balancer receives a request, it evaluates the listener rules in priority order to determine which rule to apply, and then selects a target from the target group for the rule action.

Target groups:

  • Each target group routes requests to one or more registered targets, such as EC2 instances, using the protocol and port number that you specify.  
    • target can be registered with multiple target groups
    • Routing is performed independently for each target group, even when a target is registered with multiple target groups
  • health checks are configured per target group 
    • used to monitor the health of the registered targets
    • they are performed on all targets registered to a target group
    • they can be configured so that the load balancer can send requests only to the healthy targets
  • routing algorithm is configured at the target group level
    • default: round robin
    • least outstanding requests routing algorithm can alternatively be specified


Tuesday, 20 December 2022

Installing Node via Node Version Manager on Ubuntu

Node Version Manager allows you to install different versions of Node.js.
Installation instructions are here:

 We basically need to download and run a bash script:

$ curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.2/install.sh | bash
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 15916  100 15916    0     0  23135      0 --:--:-- --:--:-- --:--:-- 23133
=> Downloading nvm from git to '/home/bojan.komazec/.nvm'
=> Cloning into '/home/bojan.komazec/.nvm'...
remote: Enumerating objects: 356, done.
remote: Counting objects: 100% (356/356), done.
remote: Compressing objects: 100% (303/303), done.
remote: Total 356 (delta 39), reused 164 (delta 27), pack-reused 0
Receiving objects: 100% (356/356), 222.15 KiB | 417.00 KiB/s, done.
Resolving deltas: 100% (39/39), done.
* (HEAD detached at FETCH_HEAD)
=> Compressing and cleaning up git repository

=> Appending nvm source string to /home/bojan.komazec/.bashrc
=> Appending bash_completion source string to /home/bojan.komazec/.bashrc
=> Close and reopen your terminal to start using nvm or run the following to use it now:

export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"  # This loads nvm
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"  # This loads nvm bash_completion


After this, re-open the terminal and type:

$ nvm

Node Version Manager (v0.39.2)

Note: <version> refers to any version-like string nvm understands. This includes:
  - full or partial version numbers, starting with an optional "v" (0.10, v0.1.2, v1)
  - default (built-in) aliases: node, stable, unstable, iojs, system
  - custom aliases you define with `nvm alias foo`

 Any options that produce colorized output should respect the `--no-colors` option.

  nvm --help                                  Show this message
    --no-colors                               Suppress colored output
  nvm --version                               Print out the installed version of nvm
  nvm install [<version>]                     Download and install a <version>. Uses .nvmrc if available and version is omitted.
   The following optional arguments, if provided, must appear directly after `nvm install`:
    -s                                        Skip binary download, install from source only.
    -b                                        Skip source download, install from binary only.
    --reinstall-packages-from=<version>       When installing, reinstall packages installed in <node|iojs|node version number>
    --lts                                     When installing, only select from LTS (long-term support) versions
    --lts=<LTS name>                          When installing, only select from versions for a specific LTS line
    --skip-default-packages                   When installing, skip the default-packages file if it exists
    --latest-npm                              After installing, attempt to upgrade to the latest working npm on the given node version
    --no-progress                             Disable the progress bar on any downloads
    --alias=<name>                            After installing, set the alias specified to the version specified. (same as: nvm alias <name> <version>)
    --default                                 After installing, set default alias to the version specified. (same as: nvm alias default <version>)
  nvm uninstall <version>                     Uninstall a version
  nvm uninstall --lts                         Uninstall using automatic LTS (long-term support) alias `lts/*`, if available.
  nvm uninstall --lts=<LTS name>              Uninstall using automatic alias for provided LTS line, if available.
  nvm use [<version>]                         Modify PATH to use <version>. Uses .nvmrc if available and version is omitted.
   The following optional arguments, if provided, must appear directly after `nvm use`:
    --silent                                  Silences stdout/stderr output
    --lts                                     Uses automatic LTS (long-term support) alias `lts/*`, if available.
    --lts=<LTS name>                          Uses automatic alias for provided LTS line, if available.
  nvm exec [<version>] [<command>]            Run <command> on <version>. Uses .nvmrc if available and version is omitted.
   The following optional arguments, if provided, must appear directly after `nvm exec`:
    --silent                                  Silences stdout/stderr output
    --lts                                     Uses automatic LTS (long-term support) alias `lts/*`, if available.
    --lts=<LTS name>                          Uses automatic alias for provided LTS line, if available.
  nvm run [<version>] [<args>]                Run `node` on <version> with <args> as arguments. Uses .nvmrc if available and version is omitted.
   The following optional arguments, if provided, must appear directly after `nvm run`:
    --silent                                  Silences stdout/stderr output
    --lts                                     Uses automatic LTS (long-term support) alias `lts/*`, if available.
    --lts=<LTS name>                          Uses automatic alias for provided LTS line, if available.
  nvm current                                 Display currently activated version of Node
  nvm ls [<version>]                          List installed versions, matching a given <version> if provided
    --no-colors                               Suppress colored output
    --no-alias                                Suppress `nvm alias` output
  nvm ls-remote [<version>]                   List remote versions available for install, matching a given <version> if provided
    --lts                                     When listing, only show LTS (long-term support) versions
    --lts=<LTS name>                          When listing, only show versions for a specific LTS line
    --no-colors                               Suppress colored output
  nvm version <version>                       Resolve the given description to a single local version
  nvm version-remote <version>                Resolve the given description to a single remote version
    --lts                                     When listing, only select from LTS (long-term support) versions
    --lts=<LTS name>                          When listing, only select from versions for a specific LTS line
  nvm deactivate                              Undo effects of `nvm` on current shell
    --silent                                  Silences stdout/stderr output
  nvm alias [<pattern>]                       Show all aliases beginning with <pattern>
    --no-colors                               Suppress colored output
  nvm alias <name> <version>                  Set an alias named <name> pointing to <version>
  nvm unalias <name>                          Deletes the alias named <name>
  nvm install-latest-npm                      Attempt to upgrade to the latest working `npm` on the current node version
  nvm reinstall-packages <version>            Reinstall global `npm` packages contained in <version> to current version
  nvm unload                                  Unload `nvm` from shell
  nvm which [current | <version>]             Display path to installed node version. Uses .nvmrc if available and version is omitted.
    --silent                                  Silences stdout/stderr output when a version is omitted
  nvm cache dir                               Display path to the cache directory for nvm
  nvm cache clear                             Empty cache directory for nvm
  nvm set-colors [<color codes>]              Set five text colors using format "yMeBg". Available when supported.
                                               Initial colors are:
                                                   b  y  g  r  e
                                               Color codes:
                                                 r/R = red / bold red
                                                 g/G = green / bold green
                                                 b/B = blue / bold blue
                                                 c/C = cyan / bold cyan
                                                 m/M = magenta / bold magenta
                                                 y/Y = yellow / bold yellow
                                                 k/K = black / bold black
                                                 e/W = light grey / white

  nvm install 8.0.0                     Install a specific version number
  nvm use 8.0                           Use the latest available 8.0.x release
  nvm run 6.10.3 app.js                 Run app.js using node 6.10.3
  nvm exec 4.8.3 node app.js            Run `node app.js` with the PATH pointing to node 4.8.3
  nvm alias default 8.1.0               Set default node version on a shell
  nvm alias default node                Always default to the latest available node version on a shell

  nvm install node                      Install the latest available version
  nvm use node                          Use the latest version
  nvm install --lts                     Install the latest LTS version
  nvm use --lts                         Use the latest LTS version

  nvm set-colors cgYmW                  Set text colors to cyan, green, bold yellow, magenta, and white

  to remove, delete, or uninstall nvm - just remove the `$NVM_DIR` folder (usually `~/.nvm`)

Let's now check what's the latest version of Node.js:

$ nvm install 19.3.0
Downloading and installing node v19.3.0...
Downloading https://nodejs.org/dist/v19.3.0/node-v19.3.0-linux-x64.tar.xz...
############################################################################################################################################################## 100.0%
Computing checksum with sha256sum
Checksums matched!
Now using node v19.3.0 (npm v9.2.0)
Creating default alias: default -> 19.3.0 (-> v19.3.0)

Let's verify the installation:

$ node -v