loading...
Cover image for Platformless Devops with Docker and Nginx in "Just a VM" - Part 2 (web API with Nginx as proxy)

Platformless Devops with Docker and Nginx in "Just a VM" - Part 2 (web API with Nginx as proxy)

lucassen profile image Asbjørn Lucassen ・4 min read

Platformless Devops with Docker and Nginx in "Just a VM" (4 Part Series)

1) Platformless Devops with Docker and Nginx in "Just a VM" - Part 1 (Intro, VM, Nginx) 2) Platformless Devops with Docker and Nginx in "Just a VM" - Part 2 (web API with Nginx as proxy) 3) Platformless Devops with Docker and Nginx in "Just a VM" - Part 3 (DNS,HTTPS) 4) Platformless Devops with Docker and Nginx in "Just a VM" - Part 4 (Gitlab CI/CD)

This is part 2 in my multi-part series on setting up a super-lean stack in "Just a VM".

We continue where we left stuff in part 1. There we set up the VM and nginx basics. We continue by pulling, running and setting up the backend app to be proxied through nginx.

The Api

We obviously need a backend api so we have something to proxy. I will not do a step by step on how to achieve this. You can either bring your own backend of choice, or use any node starter of choice, like the companion starter to this article or an official nestjs starter.

At this point we also want to set up how the code will be pulled down to the server. Because yeah, we are going for a very similar approach as what heroku is doing. The server basically gets a "go on then" from an outside source(in our case the CI-environment), it does git pull, builds and restarts with new docker images.

Be wary that this approach leads to a slight downtime. Zero-downtime deployment is beyond the scope of this guide, but can be achieved with modifications.

We are running our program in docker with docker-compose to easily get a few qualities we desire for production environments; predictable, reliable, isolated, idempotent deploys and daemonization/running as a service with just a flag. This may not seem like a big deal but it is.

Anyway, moving forward from this point you need, well, basically your program.

Pull deploy

If you want to follow along with the companion repository I suggest you fork it. To mimic a proper deploy situation we want to pull from a private repository. With gitlab that is done by git+https with basic auth. To create the basic auth, create a deploy token and note your secret key.

Gitlab deploy token

Go onto the server and clone down the repository using the secret key. We also want a neat name like "approot" to make it sound like we are doing something technically challenging.

cd $HOME
git clone https://devops-token:<secret_deploytoken>@gitlab.com/<gitlabuser>/<your fork>.git approot

It's time to fire up our docker containers for the first time. But first, we need to install Node. It is not strictly needed for the backend which is built and runs in containers, but with the starter, we use npm as a scripting helper and if you extend with your own frontend and host statics in nginx, that part needs npm anyway, so might as well install it now. We install using the official node LTS version delivered via PPA.

sudo apt update
sudo apt upgrade
sudo apt-get install curl software-properties-common
curl -sL https://deb.nodesource.com/setup_12.x | sudo bash -
sudo apt-get install -y nodejs
node -v

If you did this, the last line should output the latest node lts(12.16.3 as of May 21 2020).

First run

Before running, we need to set up our environment variables.
We have the choice of putting them in OS-level in ~/.bashrc or just making them available in the repository for docker.
I prefer local configuration, so head into the repo and create the environment file(If not, modify docker-compose.prod.yml to not look for it).

Generate a decent random using the built in uuidgen:

uuidgen -r

Copy it into the following

POSTGRES_PASSWORD=<proper random>
POSTGRES_HOST=127.0.0.1

DATABASE_URL=postgres://postgres:<proper random>@pghost:5432/postgres
REDIS_URL=redis://redishost:6379

And put those env vars into the .env file:

cd $HOME
cd approot
nano .env

Since the subfolder web/ has it's own Dockerfile we need to provide that with the environment as well. This is best done with just symbolic linking the just created env file:

ln -s ./.env ./web/.env

Now, run docker-compose through the npm script provided:

npm run docker-prod

We can now do a quick check of the api on the server:

curl http://localhost:5000/api/v1/user/1

Which outputs he who is real and stands up.

Proxy

We are now going to configure Nginx to proxy our backend.
Nginx routing is usually done by having site configs in /etc/nginx/sites-available and then symlink them in /sites-enabled.

We can start by removing the default nginx placeholder:

cd /etc/nginx/sites-enabled
sudo rm default

We can then create a new very basic proxy config here:

cd /etc/nginx/sites-available
sudo nano approot

With contents:

upstream  approot {
    server 127.0.0.1:5000;
    keepalive 64;
}

server {
    listen 80;

    server_name _;    

    root /var/www/html;

    location / {
     try_files $uri $uri/ =404;
    }

    location /api {
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_set_header X-NginX-Proxy true;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_max_temp_file_size 0;
        proxy_pass http://approot/api;
        proxy_redirect off;
        proxy_read_timeout 240s;
    }
}

Take notice that we only proxy and expose /api, the normal / will just give a 403 Forbidden for now.

Afterwards symlink it into sites-enabled/

sudo ln -s /etc/nginx/sites-available/approot /etc/nginx/sites-enabled/

After every nginx config change it is recommended to do a config check with the -t flag. And then if OK just reload the nginx config:

sudo nginx -t
sudo nginx -s reload

We can now test the proxy by going to our local machines browser at http://<ip>/api/v1/user/1

End of part 2

Now we have a proxied backend node app running in docker with it's own database. In the next part we will go briefly over DNS and SSL.

Platformless Devops with Docker and Nginx in "Just a VM" (4 Part Series)

1) Platformless Devops with Docker and Nginx in "Just a VM" - Part 1 (Intro, VM, Nginx) 2) Platformless Devops with Docker and Nginx in "Just a VM" - Part 2 (web API with Nginx as proxy) 3) Platformless Devops with Docker and Nginx in "Just a VM" - Part 3 (DNS,HTTPS) 4) Platformless Devops with Docker and Nginx in "Just a VM" - Part 4 (Gitlab CI/CD)

Posted on May 25 by:

lucassen profile

Asbjørn Lucassen

@lucassen

Full-stack power-cycler, button-smasher, experienced stack-trace googler, with specialization in stackoverflow copypasta

Discussion

markdown guide