About

About Me(ir)

Hi there! šŸ‘‹

My name is Meir Gabay (a.k.a. unfor19) and Iā€™m a DevOps Engineer at LSports. I am passionate about studying and teaching.

This website serves as a central point to all of my blog posts and videos; the main topics are:

  • DevOps culture and best-practices
  • Cloud and On-Premise Architecture
  • Programming for both backend and frontend

About This Website

This section is dedicated to the outstanding programmers who dedicated many hours to developing awesome projects in their spare time. Also, itā€™s an excellent opportunity to share the technology stack that Iā€™m using to develop, build and distribute this website.

Plot

Previously, I hosted a WordPress website on AWS Lightstail. The setup was relatively easy, and using WordPress was ok for me, up to a point where I realized that itā€™s pointless to run a server to host my blog.

I didnā€™t want to:

  1. Care for backup.
  2. Care for infrastructure - Add more instances and an Application Load Balancer for high availability. Add a CloudFront distribution (CDN) to cache and serve (sounds like protect and serve) the content fast around the globe.
  3. Care for security - Protect the server from hackers who intend to use known exploits, or perform a DDOS attack, or anything related to keeping the website alive and safe from hackers
  4. Care for anything.

You got the point; I wanted zero concerns, so I shifted from WordPress (web-server) to Jekyll (static website).

Frontend

Jekyll Plugins

Themes And Styles

Iā€™ve created a custom theme to customize this website as much as I please. Having said that, Iā€™m using many frameworks, so here they are:

  • Bootstrap v5 - Iā€™m quoting them since itā€™s true ā€œQuickly design and customize responsive mobile-first sites with Bootstrapā€¦ā€. It was straightforward for me to read the docs and style this website with Bootstrap, especially as a frontend noob; Iā€™m always terrified when it comes to frontend development.
  • purgecss - I was able to minify bootstrap-min.css from 160KB to 16KB. Itā€™s like tree-shaking for unused CSS styles.
  • highlight.js and vs2015.css - The code blocks in each blog post are automatically highlighted.
  • highlightjs-line-numbers.js - Line numbers in code blocks are generated automatically.
  • clipboard.js - Easy copy-paste of code blocks; I had to tweak its configuration a bit to avoid copying line numbers.
  • anchorjs - The cool links next to headers are automatically generated with this great project.
  • darkmode.js - That shiny moon/sun in the bottom right corner of your screen, click it and see what happens

Backend

Before we go any further, it might look like a commercial to Cloudflare; I donā€™t care, Iā€™m in love.

Iā€™m using Cloudflare for:

  • Hosting Solution - Iā€™m hosting a static web page with Cloudflare Pages
  • CI/CD Solution - Using Cloudflare Pages (again) to build and distribute the website, and it only requires git commit and git push, thatā€™s it.
  • CDN Solution - Since Iā€™m using Cloudflare Pages, the website is automatically distributed to Cloudflareā€™s edge locations; How awesome (and easy) is that!
  • Custom Response Headers - To increase the security, Iā€™ve added custom response headers with Cloudflare Workers; Check this siteā€™s score in Mozilla Observatory, previously it was F and D, now itā€™s B. It was very easy to implement and Iā€™m deploying the workers with wrangler during the build stage in Cloudflare Pages. Hereā€™s my Makefile, I simply execute make cloudflare-ci in the build stage, and the website is deployed followed by Cloudflare Workers deployment.

    build-cloudflare:    ## Build application in CloudFlare
        pwd
        ls -lh
        bundle exec jekyll build
        ls -lh
    
    publish-wrangler:    ## Publish worker to CloudFlare
        @if [ "${CF_API_TOKEN}" != "" ]; then\
            npm i @cloudflare/wrangler -g;\
            cd worker-custom-headers && wrangler publish;\
        else\
            echo "CF_API_TOKEN empty, skipped wrangler";\
        fi
    
    cloudflare-ci: build-cloudflare publish-wrangler
    

And, not surprisingly, Iā€™m using GitHub to host my source code.

Local Development

One day, Iā€™ll create a public open-source project that will include all the best practices that Iā€™ve used to build and develop this website. But, until that day comes, Iā€™ll share the technology stack that Iā€™m using to make my life easier during the development process.

Yup, that is it šŸ™Š

Dockerfile

A Dockerfile for local development of a Jekyll based website

ARG JEKYLL_VERSION="4.2.2"

### ----------------------------------------------------------------------
### Base Stage - Jekyll dependencies and app source code in /srv/jekyll
### ----------------------------------------------------------------------
FROM jekyll/jekyll:${JEKYLL_VERSION} as base
USER jekyll
RUN echo "gem: --no-document --no-suggestions --show-install-dir" > ~/.gemrc
ENV BUNDLE_PATH="~/.gem"

# Copy lock-files
WORKDIR /srv/jekyll/jekyll-theme-meirg
COPY jekyll-theme-meirg/Gemfile*  jekyll-theme-meirg/jekyll-theme-meirg.gemspec ./
WORKDIR /srv/jekyll
COPY Gemfile* ./

USER root
RUN chown jekyll:jekyll Gemfile.lock

USER jekyll

# Install dependencies
RUN \
    bundle config set --local without local_theme && \
    bundle install

# Copy theme
WORKDIR /srv/jekyll/jekyll-theme-meirg
COPY jekyll-theme-meirg .

# Install theme
WORKDIR /srv/jekyll
RUN \
    bundle config set --local without "" && \
    bundle install

# Copy source code
COPY . .
ENTRYPOINT [ "bash" ]
### --------------------------------------------------------------------


### ----------------------------------------------------------------------
### Debug Stage - Debug the container
### ----------------------------------------------------------------------
FROM base as debug
WORKDIR /code/
# Skip copy since we're mounting the website to /code/
# docker run --rm -it -v "${PWD}/meirg.co.il":/code/ -p 4000:4000 -p 35729:35729  meirg-website:debug
EXPOSE 4000
EXPOSE 35729
ENTRYPOINT [ "bash" ]
### ----------------------------------------------------------------------


### ----------------------------------------------------------------------
### Serve Stage - Hot reload the application on file change
### ----------------------------------------------------------------------
FROM base as serve
WORKDIR /code/
# Skip copy since we're mounting the website to /code/
# docker run --rm -it -v "${PWD}/meirg.co.il":/code/ -p 4000:4000 -p 35729:35729  meirg-website:serve
EXPOSE 4000
EXPOSE 35729
ENV GIT_DISCOVERY_ACROSS_FILESYSTEM=1
ENTRYPOINT [ "bundle" ]
CMD ["exec", "jekyll", "serve", "--livereload", "--host", "0.0.0.0"]
### ----------------------------------------------------------------------


### ----------------------------------------------------------------------
### Build Stage - Generate a static website in "_site"
### ----------------------------------------------------------------------
FROM base as build
WORKDIR /src/jekyll
ENTRYPOINT [ "bundle" ]
CMD ["exec", "jekyll", "build" ]
### ----------------------------------------------------------------------

Makefiles

This is the Makefile in my root dir

.EXPORT_ALL_VARIABLES:
ARTIFACT_PATH=meirg.co.il/_site

help:                ## Available make commands
	@fgrep -h "##" $(MAKEFILE_LIST) | fgrep -v fgrep | sed -e 's~:~~' | sed -e 's~##~~'

usage: help

build:               ## Build Docker image and compile
	docker build -f Dockerfile --target build -t meirg-website:build meirg.co.il && \
	docker run --rm -it -v "${PWD}/meirg.co.il":/code/ --workdir=/code/ meirg-website:build
	@du -sh "${ARTIFACT_PATH}"

build-serve:         ## Build Docker image for serving a hot-reload app
	docker build -f Dockerfile --target serve -t meirg-website:serve meirg.co.il

build-run: build-serve

run:                 ## Run hot-reload app
	docker run --rm -it -v "${PWD}/meirg.co.il":/code/ -p 4000:4000 -p 35729:35729 meirg-website:serve

serve: run           ## Run hot-reload app

This is the Makefile in meirg.co.il directory

.EXPORT_ALL_VARIABLES:
BOOTSTRAP_MIN_CSS_PATH="_site/assets/styles/bootstrap/bootstrap-5.1.3.min.css"

purge-css:
	du -sh ${BOOTSTRAP_MIN_CSS_PATH}
	purgecss --content _site/index.html \
			_site/about/about.html \
			_site/tags/index.html \
			_site/page/2/index.html \
			_site/tags/aws/index.html \
			_site/2021/12/02/how-to-develop-a-progressive-web-application-on-an-android-device/index.html \
			_site/404.html \
		--css ${BOOTSTRAP_MIN_CSS_PATH} \
		--safelist table table-responsive shadow shadow-sm bg-light bg-dark text-primary text-light mb-2 p-3 m-2 me-1 me-2 mt-1 active page-item page-link pagination card \
		--output ${BOOTSTRAP_MIN_CSS_PATH}
	du -sh ${BOOTSTRAP_MIN_CSS_PATH}

bundle-jekyll:
	pwd
	ls -lh
	bundle exec jekyll build
	ls -lh

# Assuming we're inside meirg.co.il
build: bundle-jekyll purge-css

cloudflare-deps:
	npm i purgecss -g

build-cloudflare: cloudflare-deps bundle-jekyll purge-css  ## Build application in CloudFlare

publish-wrangler:    ## Publish worker to CloudFlare
	@if [ "${CF_API_TOKEN}" != "" ]; then\
		npm i @cloudflare/wrangler -g;\
		cd worker-custom-headers && wrangler publish;\
	else\
		echo "CF_API_TOKEN empty, skipped wrangler";\
    fi

cloudflare-ci: build-cloudflare publish-wrangler

Final Words

Itā€™s funny how I squeezed a whole blog post on ā€œhow to create a Jekyll-based websiteā€ in my ā€œabout pageā€.