# 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](https://hackmd.io/@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 --> [:coffee:](https://drive.google.com/file/d/1y4iQcC3V6rqYGf2L4pkZL63DwvbvcNm3/view?usp=sharing)
## Setup the docker file and make interaction with
~~Using the multi-stage for solving that problem --> identify what need to target and build it~~
![](https://i.imgur.com/RM2rRkA.png)
**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
![](https://i.imgur.com/59AR1Fz.png)
![](https://i.imgur.com/3TAru7g.png)
![](https://i.imgur.com/ip0DgmA.png)
![](https://i.imgur.com/ZqkkPQf.png)
![](https://i.imgur.com/NKHXe4P.png)
## 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 :smile:
## Reference
[Multi-stage builds](https://docs.docker.com/build/building/multi-stage/)
[Copy but ignore some file](https://unix.stackexchange.com/questions/41693/how-to-copy-some-but-not-all-files)
[Config ssl](http://nginx.org/en/docs/http/configuring_https_servers.html)
[Nginx configure example](https://www.nginx.com/resources/wiki/start/topics/examples/full)
[Nginx --> config with digital ocean](https://www.digitalocean.com/community/tools/nginx)
[Config env for nginx](https://www.baeldung.com/linux/nginx-config-environment-variables)
[:coffee:](https://www.youtube.com/watch?v=ucRVDoFkcxc)
[:thinking_face:](https://stackoverflow.com/questions/22009364/is-there-a-try-catch-command-in-bash)