Problem Statement
I always wanted to work with Kubernetes but the subject comes with steep learning curve.Especially for someone who does not work directly with software infrastructure on day-to-day basis. The best way to learn is to actually use it in actual setting. This is my first attempt to use Kubernetes to serve a content on the Internet.
What: Non-Fictitious CI/CD with Kubernetes
My plan is to setup a working and simple Continuous Integration and Continuous Deployment (CI/CD) workflow that integrates with Kubernetes cluster.What can be simpler than deploying a static site. I might as well I start a new blog site.This blog site is the result of CI workflow that I’ll be describing below.
How: Services that Build and Deploy This Blog to Kubernetes Cluster
Services involved in this CI:
- GitHub Action. Workflow orchestration tool that executes steps in CI.
- Github Package Registry. Docker image registry provided by Github.
- Kubernetes cluster. At the point of writing this post I’m running my cluster on AWS with spot instances provisioned by kops. Still evaluating if this is the cheapest method for running Kubernetes cluster. (This is for future blog post)
Below are the high level steps:
- Make changes to my blog repository.
- Push to origin with tag (i.e
v1.0.0
). Pushing to master without tag will not trigger the build which allow me to push my changes to master multiple times without triggering the deployment step. - GitHub Action will do its magic to checkout, build and push newly created image to Kubernetes cluster.
Below is the GitHub Action yml file which wires everthing together.
name: Docker
on:
push:
tags:
- v*
env:
IMAGE_NAME: blog
jobs:
push:
runs-on: ubuntu-latest
if: github.event_name == 'push'
steps:
- uses: actions/checkout@v2
- name: Retrieve Gem Bundles Cache
uses: actions/cache@v1
with:
path: _bundle
key: ${{ runner.os }}-gems-${{ hashFiles('**/Gemfile.lock') }}
restore-keys: |
${{ runner.os }}-gems-
- name: Generate site
run: |
mkdir -p _bundle/cache
chmod a+rwx -R .
docker-compose run jekyll bundle install
docker-compose run jekyll bundle exec jekyll build --trace
- name: Build image
run: docker build . --file Dockerfile --tag $IMAGE_NAME
- name: Check bundle
run: |
ls _bundle/gems
- name: Log into registry
run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login docker.pkg.github.com -u ${{ github.actor }} --password-stdin
- name: Push image
run: |
IMAGE_ID=docker.pkg.github.com/${{ github.repository }}/$IMAGE_NAME
# Change all uppercase to lowercase
IMAGE_ID=$(echo $IMAGE_ID | tr '[A-Z]' '[a-z]')
# Strip git ref prefix from version
VERSION=$(echo "${{ github.ref }}" | sed -e 's,.*/\(.*\),\1,')
# Strip "v" prefix from tag name
[["${{ github.ref }}" == "refs/tags/"*]] && VERSION=$(echo $VERSION | sed -e 's/^v//')
# Use Docker `latest` tag convention
["$VERSION" == "master"] && VERSION=latest
echo IMAGE_ID=$IMAGE_ID
echo VERSION=$VERSION
docker tag $IMAGE_NAME $IMAGE_ID:$VERSION
docker push $IMAGE_ID:$VERSION
echo "::set-env name=VERSION::$VERSION"
- name: Check Version
id: check_version
run: |
echo ::set-output name=version::$VERSION
- name: Update k8s deployment
uses: Consensys/kubernetes-action@master
env:
KUBE_CONFIG_DATA: ${{ secrets.KUBE_CONFIG_DATA }}
with:
args: set image deployment/blog-deployment blog=docker.pkg.github.com/taufek/blog/blog:${{ steps.check_version.outputs.version }}
Github Action Yaml Breakdown
Trigger this Github Action workflow if the push to origin includes tag starting with v
. For example v1.0.0
.
on:
push:
tags:
- v*
Standard steps to checkout and retrieve cache (if there is any).
- uses: actions/checkout@v2
- name: Retrieve Gem Bundles Cache
uses: actions/cache@v1
with:
path: _bundle
key: ${{ runner.os }}-gems-${{ hashFiles('**/Gemfile.lock') }}
restore-keys: |
${{ runner.os }}-gems-
Generate the static site via Jekyll command. I use Docker image to skip Jekyll setup steps.
- name: Generate site
run: |
mkdir -p _bundle/cache
chmod a+rwx -R .
docker-compose run jekyll bundle install
docker-compose run jekyll bundle exec jekyll build --trace
Build new Docker image for this blog. $IMAGE_NAME
value is blog
.
- name: Build image
run: docker build . --file Dockerfile --tag $IMAGE_NAME
Login to Github Package Registry. This is required before pushing the image to the registry.
- name: Log into registry
run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login docker.pkg.github.com -u ${{ github.actor }} --password-stdin
Push the Docker image registry as docker.pkg.github.com/taufek/blog/blog:1.0.0
.
- name: Push image
run: |
IMAGE_ID=docker.pkg.github.com/${{ github.repository }}/$IMAGE_NAME
# Change all uppercase to lowercase
IMAGE_ID=$(echo $IMAGE_ID | tr '[A-Z]' '[a-z]')
# Strip git ref prefix from version
VERSION=$(echo "${{ github.ref }}" | sed -e 's,.*/\(.*\),\1,')
# Strip "v" prefix from tag name
[["${{ github.ref }}" == "refs/tags/"*]] && VERSION=$(echo $VERSION | sed -e 's/^v//')
# Use Docker `latest` tag convention
["$VERSION" == "master"] && VERSION=latest
echo IMAGE_ID=$IMAGE_ID
echo VERSION=$VERSION
docker tag $IMAGE_NAME $IMAGE_ID:$VERSION
docker push $IMAGE_ID:$VERSION
echo "::set-env name=VERSION::$VERSION"
- name: Check Version
id: check_version
run: |
echo ::set-output name=version::$VERSION
Update Kubernetes Deployment with new image tag.
- name: Update k8s deployment
uses: Consensys/kubernetes-action@master
env:
KUBE_CONFIG_DATA: ${{ secrets.KUBE_CONFIG_DATA }}
with:
args: set image deployment/blog-deployment blog=docker.pkg.github.com/taufek/blog/blog:${{ steps.check_version.outputs.version }}
You can view the blog repository here taufek/blog.
I’m planning to post more Kubernetes content in the future as I learn more about Kubernetes.
Keep watching this space.
Top comments (0)