Try   HackMD

DevOps Training Session 5: NGINX

tags: devops research reliable

Hello, btb today > i have mission with nginx-webserver if you want to understand nginx > go for my @xeusnguyen - NGINX and Apache on vietnamese - If you want to english version so don't wrr i will back to another version of that. Enjoy the lab >

Setup the docker file and make interaction with

Using the multi-stage for solving that problem > identify what need to target and build it

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

Update: after time for implement > ajust docker file into 2 file individual > easy to using with each others
Docker/Dockerfile.web:

FROM node:14

WORKDIR /app
# RUN apk add --update curl git zip 

COPY ./src/ /app

RUN npm install

CMD ["npm", "start"]

Docker/Dockerfile.nginx:

FROM nginx:1.23.3

COPY ./conf /

ARG domain_key
ENV domain_key=${domain_key}

ARG domain_crt
ENV domain_crt=${domain_crt}

RUN mv *.key  /etc/nginx/

RUN mv *.crt  /etc/nginx/
 
RUN mv nginx.conf default.conf.template

RUN envsubst < default.conf.template > /etc/nginx/conf.d/default.conf

After building complete dockerfile we need to compose it into one for building project with docker-compose
docker-compose.yaml

version: "3"
networks:
  my_network1:
  my_network2:
  my_network3:
  my_network4:

services:
  nginx_alb:
    image: nginx_alb:latest
    container_name: web-server
    ports:
      - 80:80
      - 443:443
    networks:
      - my_network1
      - my_network2
      - my_network3
      - my_network4
    restart: always
  app1:
    image: webpage8001:latest
    container_name: app1
    expose:
      - 8001
    networks:
      - my_network1
    environment:
      - MESSAGE="App 1 !"
      - PORT=8001
  app2:
    image: webpage8002:latest
    container_name: app2
    expose:
      - 8002
    networks:
      - my_network2
    environment:
      - MESSAGE="App 2 !"
      - PORT=8002
  app3:
    image: webpage8003:latest
    container_name: app3
    expose:
      - 8003
    networks:
      - my_network3
    environment:
      - MESSAGE="App 3 !"
      - PORT=8003
  app4:
    image: webpage8004:latest
    container_name: app4
    expose:
      - 8004
    networks:
      - my_network4
    environment:
      - MESSAGE="App 4 !"
      - PORT=8004

nginx.conf

upstream alb
{
        server app1:8001;
        server app2:8002;
}
server {
    listen 80 default_server;
    server_name _;
    return 301 https://$host$request_uri;
}

server{
    listen 443 ssl;
    ssl on;
    server_name _;
    ssl_certificate ${domain_crt};
    ssl_certificate_key ${domain_key};
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers HIGH:!aNULL:!MD5;

    location / {
        return 404;
    }

    location /original {
        proxy_pass http://alb;
    }

    location /v1 {
		return 307 /original;
	}

	location /v2 {
		return 307 /original;
	}

    location /v3 {
        proxy_pass http://app3:8003;
    }

    location /v4 {
        proxy_pass http://app4:8004;
    }
}

And last one, we need onething to run all with bash script
run_setup.sh

#!/bin/bash
export docker_err=100
export syspath_err=101

# Get the absolution of path
abs_path_file_execute=$(realpath "$0")
abs_path_folder_script=$(dirname "$abs_path_file_execute")
abs_path_folder_root=$(dirname "$(dirname "$abs_path_file_execute")")
abs_path_folder_docker="$abs_path_folder_root""/docker/"
abs_path_folder_src="$abs_path_folder_root""/src/"
abs_path_folder_conf="$abs_path_folder_docker/conf"

source "$abs_path_folder_script/try_catch.sh"

# Generate the ssl certificate
# Write a Powershell script and a Bash script that generate a self-signed SSL certificate
DOMAIN="$1"
if [ -z "$DOMAIN" ]; then
  echo "Usage: $(basename $0) <domain>"
  exit 11
fi

fail_if_error() {
  [ $1 != 0 ] && {
    unset PASSPHRASE
    exit 10
  }
}

# Generate a passphrase
export PASSPHRASE=$(head -c 500 /dev/urandom | tr -dc a-z0-9A-Z | head -c 128; echo)

# Certificate details; replace items in angle brackets with your own info
subj="
C=VN
ST=blah
O=Blah
localityName=vietnam
commonName=$DOMAIN
organizationalUnitName=Blah
emailAddress=admin@example.com
"

# Generate the server private key
openssl genrsa -des3 -out "$abs_path_folder_conf/$DOMAIN.key" -passout env:PASSPHRASE 2048
fail_if_error $?

# Generate the CSR
openssl req \
    -new \
    -batch \
    -subj "$(echo -n "$subj" | tr "\n" "/")" \
    -key "$abs_path_folder_conf/$DOMAIN.key" \
    -out "$abs_path_folder_conf/$DOMAIN.csr" \
    -passin env:PASSPHRASE
fail_if_error $?
cp "$abs_path_folder_conf/$DOMAIN.key" "$abs_path_folder_conf/$DOMAIN.key.org"
fail_if_error $?

# Strip the password so we don't have to type it every time we restart Apache
openssl rsa -in "$abs_path_folder_conf/$DOMAIN.key.org" -out "$abs_path_folder_conf/$DOMAIN.key" -passin env:PASSPHRASE
fail_if_error $?

# Generate the cert (good for 10 years)
openssl x509 -req -days 3650 -in "$abs_path_folder_conf/$DOMAIN.csr" -signkey "$abs_path_folder_conf/$DOMAIN.key" -out "$abs_path_folder_conf/$DOMAIN.crt"
fail_if_error $?

# Remove docker ps & image
docker stop "$(docker ps -a)" || true
docker rm -f "$(docker ps -aq)" || true
docker rmi nginx_alb:latest || true
docker rmi "$(docker image list | grep webpage)" || true

# Remove the network
docker network rm "$(docker network list | grep my_network)" || true

# Move on the src folder and after that execute the docker script
# Solution 1: Using the mv but not working if don't move absolute path
# mv $(ls $abs_path_folder_script --ignore=run_setup.sh --ignore=try_catch.sh) "$abs_path_folder_docker/conf/" || throw $syspath_err

# Solution 2: Move with name of Domain --> it work good 
# mv "$abs_path_folder_script/$DOMAIN.crt" "$abs_path_folder_docker/conf" || throw $syspath_err
# mv "$abs_path_folder_script/$DOMAIN.key" "$abs_path_folder_docker/conf" || throw $syspath_err
# mv "$abs_path_folder_script/$DOMAIN.csr" "$abs_path_folder_docker/conf" || throw $syspath_err
# mv "$abs_path_folder_script/$DOMAIN.key.org" "$abs_path_folder_docker/conf" || throw $syspath_err

# Solution 3: Create into conf and do not erase smt

# cp -r "$PWD"/../src/ "$PWD"/../docker/
cp -r "$abs_path_folder_src" "$abs_path_folder_docker" || throw $syspath_err
# cd "$PWD"/../docker/ || exit
cd "$abs_path_folder_docker" || throw $syspath_err

# Pull and create each website with specified name
docker build -t webpage8001:latest -f Dockerfile.web . || throw $docker_err
docker build -t webpage8002:latest -f Dockerfile.web . || throw $docker_err
docker build -t webpage8003:latest -f Dockerfile.web . || throw $docker_err
docker build -t webpage8004:latest -f Dockerfile.web . || throw $docker_err

rm -rf src/ || throw $syspath_err

docker build -t nginx_alb:latest --build-arg domain_key=$DOMAIN.key --build-arg domain_crt=$DOMAIN.crt -f Dockerfile.nginx . || throw $docker_err

cd "$abs_path_folder_docker/conf/" || throw $syspath_err
rm $(ls --ignore=nginx.conf) || throwErrors $syspath_err
cd "$abs_path_folder_root" || throw $syspath_err

docker-compose up -d || true

try_catch.sh

#!/bin/bash

function try()
{
    [[ $- = *e* ]]; SAVED_OPT_E=$?
    set +e
}

function throw()
{
    exit $1
}

function catch()
{
    export ex_code=$?
    (( $SAVED_OPT_E )) && set +e
    return $ex_code
}

function throwErrors()
{
    set -e
}

function ignoreErrors()
{
    set +e
}

Oh i missing the web part just use the source code from previous session > but need to config env to see what dif between the webpage
index.js

const http = require('http');
const port = process.env.PORT || 3000;
const message = process.env.MESSAGE;

const server = http.createServer((req, res) => {
  res.statusCode = 200;
  const msg = message
  res.end(msg);
});

server.listen(port, () => {
  console.log(`Server running on http://localhost:${port}/`);
})

package.json

{
    "name": "node-hello",
    "version": "1.0.0",
    "description": "",
    "main": "index.js",
    "scripts": {
      "start": "node index.js"
    },
    "repository": {
      "type": "git",
      "url": "git+https://github.com/johnpapa/node-hello.git"
    },
    "keywords": [],
    "author": "",
    "license": "ISC",
    "bugs": {
      "url": "https://github.com/johnpapa/node-hello/issues"
    },
    "homepage": "https://github.com/johnpapa/node-hello#readme"
  }

Result





Conclusion

  • This is quite easy implement diagram but > you will diggest into the nginx > docker > compose > network > nodejs
  • Note: don't touch everything about in conf if u don't know. I cost one morning to know wrong include > ignore default configuration
  • So happy implement > Session 5: Nginx end here > Next session i will move on the cloud computing > Azure i think so
    Image Not Showing Possible Reasons
    • The image file may be corrupted
    • The server hosting the image is unavailable
    • The image path is incorrect
    • The image format is not supported
    Learn More →

Reference

Multi-stage builds
Copy but ignore some file
Config ssl
Nginx configure example
Nginx > config with digital ocean
Config env for nginx