blog.lazkani.io/content/posts/time-to-deploy-our-static-blog.md

218 lines
7.2 KiB
Markdown
Raw Normal View History

+++
title = "Time to deploy our static blog"
author = ["Elia el Lazkani"]
date = 2021-07-10
lastmod = 2021-07-10
tags = ["docker", "dockerfile", "linux", "traefik", "nginx", "ssl", "letsencrypt"]
categories = ["container"]
draft = false
+++
In the previous post, entitled "[Let's play with Traefik]({{< relref "let-s-play-with-traefik" >}})", we deployed
_Traefik_ and configured it. We left it in a running state but we haven't
_really_ used it properly yet.
Let's put it to some good use this time around.
<!--more-->
## Pre-requisites {#pre-requisites}
This blog post assumes that you already have a generated static _website_ or
_blog_. There are multiple tools in the sphere which allows you to statically
generate your blog.
You can find a list of them on the
[Awesome Static Web Site
Generators](https://github.com/myles/awesome-static-generators).
Once we have the directory on disk, we can move forward.
## Components {#components}
Let's talk components a tiny bit and see what we have and what we need. We
already a _static site_. We can expose our _site_ using _Traefik_. We can also
generate an _SSL certificate_ for the exposed _site_.
What we don't have, is a way to _serve_ our _static site_. _Traefik_ is only a
_reverse proxy_ server. A _reverse proxy_, sort of, routes into and out of
sockets. These sockets could be open local ports, or they could, also, be other
containers.
## Nginx {#nginx}
That's where [_nginx_](https://nginx.org/) comes into the picture.
> nginx [engine x] is an HTTP and reverse proxy server, a mail proxy server, and a
> generic TCP/UDP proxy server, originally written by Igor Sysoev.
We can find an _nginx_ docker image on
[dockerhub](https://hub.docker.com/_/nginx). But, if we look around carefully
we can see a section that mentions "_running nginx as a non-root user_". This
led me to a small discovery which made me look for an alternative of that image.
Luckily for us, _nginxinc_ also releases an _unprivileged_ version of that image
under the name of [nginx-unprivileged](https://hub.docker.com/r/nginxinc/nginx-unprivileged).
### Configuration {#configuration}
The _nginx_ docker image can be configured using a _template_ configuration file
which can be mounted into the container.
The configuration can include _variables_ which will be replaced by _environment
variables_ we inject into the container.
Let's look at an example configuration `default.conf.template`.
```cfg
server {
listen ${NGINX_BLOG_PORT};
server_name localhost;
root /usr/share/nginx/html/${NGINX_BLOG_HOST};
location / {
index index.html;
try_files $uri $uri/ =404;
}
}
```
In the example above, we use `NGINX_BLOG_HOST` and `NGINX_BLOG_PORT` as
_environment variables_ to be replaced in the _nginx_ configuration.
## Container {#container}
After creating our _nginx_ configuration, we need to run an _nginx_ container
and serve our blog to the users.
In the [previous post]({{< relref "let-s-play-with-traefik" >}}), we used _docker-compose_ to
deploy _Traefik_. We will continue with that and deploy our _nginx_ container
alongside.
### docker-compose {#docker-compose}
Before we go ahead and create another service in the _docker-compose_ file,
let's talk a bit about what we need.
We need to deploy an _unprivileged nginx_ container, first and foremost. We need
to inject a few _environment variables_ into the container to be included in the
_nginx_ templated configuration. We, also, need not forget to include the
_labels_ required for _Traefik_ to route our container properly, and generate an
_SSL certificate_. Finally, we need to mount both the _nginx configuration
template_ and, of course, our _static blog_.
Now let's head to work.
```yaml
nginx:
container_name: nginx
image: nginxinc/nginx-unprivileged:alpine
restart: unless-stopped
mem_limit: 8m
command: ["nginx", "daemon off;"]
volumes:
- "./blog/static/:/usr/share/nginx/html/blog:ro"
- "./blog/nginx/default.conf.template:/etc/nginx/templates/default.conf.template:ro"
environment:
- NGINX_BLOG_PORT=80
- NGINX_BLOG_HOST=blog.example.com
labels:
- "traefik.http.routers.blog-http.rule=Host(`blog.example.com`)"
- "traefik.http.routers.blog-http.service=blog-http"
- "traefik.http.services.blog-http.loadbalancer.server.port=80"
- "traefik.http.routers.blog-http.middlewares=blog-main"
- "traefik.http.middlewares.blog-main.chain.middlewares=frame-deny,browser-xss-filter,ssl-redirect"
- "traefik.http.middlewares.frame-deny.headers.framedeny=true"
- "traefik.http.middlewares.browser-xss-filter.headers.browserxssfilter=true"
- "traefik.http.middlewares.ssl-redirect.headers.sslredirect=true"
- "traefik.http.routers.blog-http.tls.certresolver=cloudflareresolver"
```
If we look at the _Traefik_ configuration we can see the following important configurations.
`traefik.http.routers.blog-http.rule`
: This configures the `hostname`
_Traefik_ should be listening on for our _nginx_ container.
`traefik.http.routers.blog-http.service`
: This configures the _router_ to
use our _service_.
`traefik.http.services.blog-http.loadbalancer.server.port`
: We configure the
_service_ `port`.
`traefik.http.routers.blog-http.middlewares`
: We configure the _router_ to
use our `middleware`.
`traefik.http.middlewares.blog-main.chain.middlewares`
: We configure all the
`middleware` chain.
`traefik.http.middlewares.ssl-redirect.headers.sslredirect`
: We always
redirect `http` to `https`.
`traefik.http.routers.blog-http.tls.certresolver`
: We configure the
_resolver_ to use to generate our _SSL certificate_.
We can also see our _static blog_ and the _nginx template_ being mounted as
_read-only_ inside the container to their right paths. Finally, we verify that
our `NGINX_BLOG_HOST` and `NGINX_BLOG_PORT` are configured correctly.
## Final steps {#final-steps}
After putting everything in place, we do a quick last check that everything is
correctly in place. Once we are satisfied with the results, we run !
```shell
docker-compose up -d
```
And we're good to go.
If we point our `/etc/hosts` to our site, we can test that everything works.
```cfg
192.168.0.1 blog.example.com
```
<div class="admonition note">
<p class="admonition-title"><b>Note</b></p>
Replace `192.168.0.1` with your public server's IP address. This is an example
of an IP unroutable on the internet.
</div>
If everything is configured properly, we should see our _site_ pop up
momentarily. The _SSL certificate_ will fail for a few minutes until _Traefik_
is able to generate a new one and serve it. Give it some time.
Once everything up and running, you can enjoy your _blog_ being served by
_Traefik_ through an _nginx_ container.
## Conclusion {#conclusion}
You can serve your _static blog_ with _Traefik_ and _nginx_ easily. Make sure to
take the necessary measures to run container _safely_ and it should be easy as pie.
_Traefik_ makes it possible to route to multiple containers this way, allowing
us to add more services to the _docker-compose_ file. At the same time, _nginx_,
with the _templating feature_, offers us another flexible way to serve a big
variety of _static sites_. Using them in combination open a wide range of
possibilities.