Website story

Simple website, “simple setup” — lessons learned (part 1)

Jekyll, Docker, Express.js, Nginx and Jenkins FTW!

Marek Rozmus
Sandstream Development

--

Photo by Anton Darius on Unsplash

It is a story that may also happen to you (if you are a full stack developer or if your boss just wants you to do it).

After all these years, our company’s web page became outdated, so we needed a new shiny one that would get us even more customers, or just to generate some new bug reports from testers saying “it doesn’t work on Safari / Edge!” — pick one. The deadline was classic: “as soon as possible and give it a modern look”.

It all began in a company far, far away…

If you need some specific part, then skip all this intro nonsense and click one of the following links:

  • Jekyll and Docker on dev and production
  • … and express.js — so you want to know more? Great! …but come back later
  • … and nginx — read the line above
  • … and Jenkins — read the line above
  • … and optimization — read the line above

“Stay awhile and listen”

Aah, so you’re still here — good. At least there is someone besides me who is reading it.

As you might already know, the stories of putting all techs together to work are not just straight line, but a lot of turns and backs and changing/fixing stuff (like any other IT project). But to get rid of “goto” statements (bad coding), it was simplified to “how it would look like if programmers knew everything before starting coding”.

Back to the topic — we wanted a new web page. After some research, we bought a template to speed up the work and to focus on content rather than design.

Let’s just say that templates are made to look great, inside they’re sometimes totally mess. In our case template got the following stack: HTML, CSS, some Javascripts, and… PHP.

As we are using only cutting edge technologies we, have decided to set up the backend with node.js and express.js and do not even mention about PHP anymore. Just get rid of all that stuff.

Some files were huge, and the same code was in several places. Code duplication and large files are a nightmare (you know — clean code, uncle bob, and so on… story for another time). First of all, we needed to cut all those big files into smaller ones to get rid of code duplication. We also needed some tool that will pick those pieces and build us working web page. One of us already got experience with Jekyll so why not use it.

To get rid of the problem “works on my machine”, we’ve decided to use Docker.

Although we all work on Apple computers, some of us “just don’t dig it”, and have Windows installed. Well, one of these people is me and to get it even more complicated, I use Ubuntu WSL on Windows on Mac (go figure). I just can’t live without Total Commander, Beyond Compare and I’m just too old for that Mac OS sh…

To set up Docker, only one person needs to know the magic. Others can just run one script and start working on a brand new, shiny web page. The only problem was that this person was me and all my previous experiences with docker I could just summarize: “docker sucks”. So what was the first thought I had when I’ve realized that I’ll be “docker expert”? — “Why me?”

After a few hours of despair (silent, of course) that I won’t see some code for a while, I saw an opportunity to try to find some common ground with Docker. I’ll just try to understand line by line, parameter by parameter, experiment and still get paid (and hopefully not fired when the boss sees results).

Jekyll + Docker on dev and production

Photo by me when trying Docker and Jekyll to play nicely together

The first thing to do was to find a way to run Jekyll inside a Docker container. After a short Google session I’ve found that some of the work was already done.

Thanks to envygeeks, I could start right away with Jekyll and Docker. I used images from https://github.com/envygeeks/jekyll-docker

If you already have some Jekyll project, then just run this command inside the project folder.

docker run — rm \
— volume=”$PWD:/srv/jekyll” \
jekyll/jekyll:3.8 \
jekyll build

If not then clone this repository and run the command above.

This command will go through the project’s sources and build them into the _site folder.

Would You Like To Know MORE?What does this command do exactly? Lets analyze it:--rm - this switch will instruct Docker to remove the container automatically when the work is done--volume="$PWD:/srv/jekyll" - maps your current folder $PWD to folder inside the container /srv/jekyll. Jekyll expects that the sources or at least _config.yml will be there. To make the command a little shorter you can also use -v instead of --volume. Note a single dash in a short parameter name. I will use the shorter version in further examples.jekyll/jekyll:3.8 - image name to be used for this containerjekyll build - command which should be executed inside the container

As you can see no need to install Ruby, all those gems, and leave garbage on your system (well maybe besides the Docker’s images/containers, but it is rather easy to get rid off — reclaiming disk space on Windows… not so much).

OK, this is great for building stuff, but I want to have a dev environment with my beloved HOT/LIVE reloading. For those who don’t know this yet — hot/live reloading feature rebuilds your sources and reloads the page automatically every time something changes. So, in this case, no more F5 pressing and run docker command only once.

To get it up and running and achieve minimal dev setup I’ve added livereload: true line in Jekyll _config.yml and started dev server with following the command:

docker run --rm \
-v="$PWD:/srv/jekyll" \
--publish 4000:4000 \
--publish 35729:35729 \
--interactive --tty \
jekyll/jekyll:3.8 \
jekyll serve --force_polling

What is happening here? Jekyll server starts and watches our sources. The build is triggered every time something is changed, and the page is reloaded.

Would You Like To Know MORE?What does this command do exactly? Lets analyze it:-v - short for --volume - already explained somewhere above--publish - publishes a container's port(s) to the host. You can also use shorter version -p.--publish 4000:4000 - thanks to this we can see hosted page on localhost:4000--publish 35729:35729 - publishing this port makes hot reloading magic to work--interactive --tty - forward our keyboard input to the container. In this case it allows the use of Ctrl+C to stop the server. You can also use a shorter version -it. For more information, go and read some smarter articles about it like this StackOverflow answer. Such explanations are above my paygrade.jekyll serve --force_polling - start the server and watch for changes

TIP

The -it switch is very useful, especially if you want to check what is happening inside the image/container. You can just run `bash` inside the container and look around on file structure. If you want to check container use this command:

docker exec -it CONTAINER_ID /bin/sh

or

docker exec -it CONTAINER_ID /bin/bash

that depends on what kind of shell is available in your container.

For checking the content of the image use following:

docker run -it IMAGE_NAME /bin/sh

I’ve used it a lot until I understood how all that docker magic works and where the hell are my files.

I did it — now everybody can develop a new site without installing Jekyll. They just clone the repository and run one simple docker command.

Unfortunately for some, running this command might be an issue (and you think that every developer should be familiar with copying and pasting code).

As I had enough of people coming and bothering me with the Docker questions, I created a small script that they could just run and be happy. And yes, in that script was just the docker command that you can see somewhere above.

Then I was migrating all docker scripts to docker-compose, which made the life of developers even better.

I created a file called docker-compose.yml in a project folder and placed there following code:

Now everybody could simply run the dev server with docker-compose up.

As you can see the docker command simply maps to docker-compose.yml config with few small differences:

- instead of publish we have ports- there is no config for --rm. You’ll need to manually remove the container with docker container rm CONTAINER_NAME or with docker-compose down- we don’t need to use option -it — pressing Ctrl+C will inform docker-compose to stop all containers it has started

Now I could just tell everyone: “Just run docker-compose up” and have more time for other stuff like preparing production configuration.

Production

With all that Docker things I got all developers off my back. I was just looking with envy as they were bombarding git repository with new commits and pull requests. The only thing that comforted me was that it was mainly “HTML programming” and not some React / Javascript I used to and missed.

OK, back to the story. In Sandstream, we got Scrum software development process. Nothing to brag about when everybody nowadays got Scrum but the problem is that you need to show the current state of project to the customer — fast and frequently.

From a customer point of view, it is awesome, but somebody needs to prepare that configuration. Being a full stack developer will sometimes keep you away from coding for a long time.

Between doing code reviews (the closest to code I could be / yay I’m senior, so babysitting is one of my ‘perks’), I was trying to fix up production.

So what do we need for production?

  • content built for production
  • server that serve that content

We haven’t needed some live production yet, so I used the Jekyll server with some additional config. Yes, I know it is rather lame, but it is just part about Jekyll, so just let me stick to it. In the next parts of this story, I’ll describe how express.js server was added to this setup.

I’ve disabled live reload as it is not needed on production. I have created new _config.prod.yml without the livereload: true and removed port 35729 forwarding. I’ve also changed the port on host to 80 so that we don’t need to type in port to see the page in a browser.

One additional change was to set the environment variable JEKYLL_ENV to production.

We are already awesome and use docker-compose but let’s see how it could look like with docker command:

docker run --rm \
-v="$PWD:/srv/jekyll" \
-p 80:4000 \
-it \
--env JEKYLL_ENV=production \
jekyll/jekyll:3.8 \
jekyll serve --config _config.prod.yml

Off-topic warning: Seriously — I can’t make two “code” blocks in Medium without making huge gap?

Would You Like To Know MORE?What does this command do exactly? Let's analyze it (at least new parts):-p 80:4000 - forward port 4000 from container to port 80 on a host. Now just typing `localhost` in a browser should work.--env JEKYLL_ENV=production - set environment variable inside the container. Also -e can be used.jekyll serve --config _config.prod.yml - start server but use the production config file

Now same but with new docker compose config — docker-compose.prod.yml:

And to run in just type in:

docker-compose -f docker-compose.prod.yml up

The -f switch tells docker-compose to use specific configuration and not the default docker-compose.yml file.

Placing build results on a server will be covered in further parts of this story when/if I’ll write about Jenkins.

To be continued…

If you want to take a look at the working page, go here: sanddev.com, and while browsing remember the pain of developers who made it up and running.

Please also take a look at our GitHub repositories. But who got time to make these anyway if another project needs Docker setup?

If you are reading this, then it means that it is the end of this part. If you dare… or you don’t want me to write anymore:

Or just drop a comment — it will be much cheaper.

--

--