document.write("
FROM debian:stretch-slim
LABEL version="base" maintainer="ales.loncar@celavi.org"
RUN apt-get update \\
&& apt-get install -y --no-install-recommends \\
ca-certificates \\
libpq-dev \\
dumb-init \\
perl \\
cpanminus \\
build-essential \\
procps \\
&& cpanm Carton \\
&& apt-get clean \\
&& rm -rf /var/lib/apt/lists/*
COPY . /opt/app
WORKDIR /opt/app
RUN carton install --deployment
RUN rm -rf /root/.cpanm \\
&& apt-get remove --purge -y build-essential \\
&& apt-get autoremove -y \\
&& rm -rf /tmp/* \\
&& rm -rf /var/tmp/*
RUN groupadd -r app \\
&& useradd -r -g app app \\
&& chown app:app -R /opt/app
USER app
# Expose the default web server port
EXPOSE 8080
# Runs "/usr/bin/dumb-init -- /my/script --with --args"
ENTRYPOINT ["/usr/bin/dumb-init", "--"]
# Run this command when container is started
CMD carton exec hypnotoad -f script/blog
Dockerfile - Snippet hosted by \"Cacher\"

Mojo Blog

\n

Mojo Blog is simple example with DBD::Pg that makes PostgreSQL a lot of fun to use with the Mojolicious real-time web framework.

\n

Local installation

\n

Perl

\n

You can use system Perl version for development. I recommend the use of perlbrew or plenv environment.

\n\n

Prerequisites

\n

Cpanminus and Carton are the only prerequisite for running the application. All required modules/dependencies are then installed via Carton from cpanfile.

\n\n

Configuration

\n

For Perl-ish configuration Mojolicious plugin is used - Mojolicious::Plugin::Config.

\n

Building the Docker container image

\n

Building the latest image

\n
$ docker build -t <NAME:TAG> .\n
\n

Running tests inside container image

\n
$ docker run --rm -v $PWD/t:/opt/app/t <IMAGE> carton exec prove -lr -j4\n
\n

Working with Helm

\n

Install tiller in the Minikube cluster

\n
$ helm init\n
\n

We can do a dry-run of a helm install and enable debug to inspect the generated definitions:

\n
$ helm install --dry-run --debug --namespace=default ./helm\n
\n

Install

\n
$ helm install --name mojo-blog --namespace=default ./helm\n
\n

Check the status of the release

\n
$ helm ls --all mojo-blog\n
\n

Delete the release

\n
$ helm del --purge mojo-blog\n
\n

Do the linting

\n
$ helm lint ./helm\n
\n

Render chart templates locally and store them as Kubernetes YAML

\n
$ helm template --name mojo-blog --namespace=default ./helm  > mojo-blog.yaml\n
\n

Use Port Forwarding to Access Applications in a Cluster

\n

Forward a local port to a port on the pod / deployment / service

\n
$ kubectl port-forward <POD> 8080:8080\n# or\n$ kubectl port-forward dc/<DEPLOYMENT> 8080:8080\n# or\n$  kubectl port-forward svc/<SERVICE> 8080:8080\n
\n
README.md - Snippet hosted by \"Cacher\"
pipeline {
agent any
options { disableConcurrentBuilds() }
environment {
registry = "celavi/mojo-blog"
registryCredential = 'dockerhub'
}
stages {
stage("SCM Checkout") {
steps{
git poll: false, url: 'https://github.com/loncarales/mojo-pg.git'
}
}
stage('Config') {
steps {
script {
dir('examples/blog') {
version = readFile('VERSION').trim()
}
}
}
}
stage("Build") {
steps {
script {
dir('examples/blog') {
dockerImage = docker.build registry + ":build"
docker.withRegistry('', registryCredential ) {
dockerImage.push()
}
}
}
}
}
stage("Test") {
steps {
script {
dir('examples/blog') {
dockerImage.pull()
sh 'docker run --rm -v $PWD/t:/opt/app/t $registry:build carton exec prove -lr -j4'
}
}
}
}
stage("Release") {
steps {
script {
dir('examples/blog') {
dockerImage.pull()
dockerImage.tag('v' + version)
dockerImage.tag('latest')
docker.withRegistry('', registryCredential ) {
dockerImage.push('v' + version)
dockerImage.push('latest')
}
}
}
}
}
stage("Deploy") {
steps {
script {
dir('examples/blog') {
echo "TBA"
}
}
}
}
}
}
Jenkinsfile - Snippet hosted by \"Cacher\"

Analysis

\n

Handling sensitive data

\n

Currently sensitive information, such as passwords, server IP addresses, ... are hard-coded and stored in plain text. If sensitive data must be saved, it must be encrypted first. The best option would be to use a tool for securely managing secrets and encrypting, for example, HashiCorp Vault.

\n

Automation

\n

Artefact building and method of deployment are done manually as described in README.md file. The whole process (configuration, software provisioning and application deployment) can be fully automated. To solve the problem of environment drifting in release pipeline Infrastructure as Code (IaC) approach should be used. Continuous configuration automation (CCA) tools can be thought of as an extension of traditional IaC frameworks. Notable CCA tools: Ansible, SaltStack, Terraform.

\n

Database

\n

For simplicity, PostgreSQL database service is currently deployed without persistent storage (Ephemeral) and, therefore, is not production ready. Any data stored will be lost upon pod destruction. Data persistence on Kubernetes is achieved with Persistent Volumes which provide a plugin model for storage in Kubernetes where how storage is provided is completed abstracted from how it is consumed. For production, PostgreSQL replicated database service with persistent storage (highly available with automated failover and backup) should be used.

\n

Pipeline

\n

Current Gitlab CI/CD and Jenkins pipeline streamline the very simplified process of build, tag and release for only one environment - TEST. The production TEST stages should include

\n\n

The next steps can include transporting immutable images across the various stages (INT, PROD) or even across the different clusters.

\n

Software Versioning

\n

Versioning is essential in application development. It must be possible to relate every deployment to one unique SCM commit to:

\n\n

To keep it clear and straightforward semantic versioning is used which is also used for versioning the Libraries in software development. For Continuous microservices release cycles, on the other hand, rely heavily on these time-based parameters due to their much shorter lifetime and could almost renounce semantic versioning entirely. Though it is recommended to keep a semantic version for microservices to indicate major and minor releases and track microservice development from a top view.

\n

Example: 20170717.111749-master-1.2.3

\n

Additional tagging should also be applied after successful rollouts to INT / PROD in GIT.

\n

Hypnotoad Prefork Web Server

\n

Mojo::Server::Hypnotoad is a built-in prefork web server in production environment. hypnotoad is command line interface to run Mojolicious application. Server start on port 8080 by default which is also port exposed in Docker image. In production generally reverse proxy server (Nginx, Apache) is used to access hypnotoad server.

\n
ANALYSIS.md - Snippet hosted by \"Cacher\"
image: docker:stable
services:
- docker:dind
variables:
CONTAINER_REGISTRY: "registry.gitlab.com"
DOCKER_IMAGE: "loncarales/mojo-blog"
before_script:
- 'docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CONTAINER_REGISTRY'
stages:
- build
- test
- release
- deploy
build:
stage: build
tags:
- docker
script:
- 'docker build -t $CONTAINER_REGISTRY/$DOCKER_IMAGE:build .'
- 'docker push $CONTAINER_REGISTRY/$DOCKER_IMAGE:build'
only:
- master
test:
stage: test
tags:
- docker
script:
- 'docker pull $CONTAINER_REGISTRY/$DOCKER_IMAGE:build'
- 'docker run --rm -v $PWD/t:/opt/app/t $CONTAINER_REGISTRY/$DOCKER_IMAGE:build carton exec prove -lr -j4'
only:
- master
release:
stage: release
tags:
- docker
script:
- 'docker pull $CONTAINER_REGISTRY/$DOCKER_IMAGE:build'
- 'VERSION=$(cat VERSION)'
- 'docker tag $CONTAINER_REGISTRY/$DOCKER_IMAGE:build $CONTAINER_REGISTRY/$DOCKER_IMAGE:v$VERSION'
- 'docker push $CONTAINER_REGISTRY/$DOCKER_IMAGE:v$VERSION'
- 'docker tag $CONTAINER_REGISTRY/$DOCKER_IMAGE:v$VERSION $CONTAINER_REGISTRY/$DOCKER_IMAGE:latest'
- 'docker push $CONTAINER_REGISTRY/$DOCKER_IMAGE:latest'
only:
- master
deploy:
stage: deploy
tags:
- docker
script:
- 'echo TBA'
only:
- master
gitlab-ci.yml - Snippet hosted by \"Cacher\"
version: '2'
services:
blog:
image: celavi/mojo-blog:latest
restart: always
ports:
- '8080'
volumes:
- ./etc/blog.conf:/opt/app/etc/blog.conf
depends_on:
- db
db:
image: postgres:9.6.11
restart: always
environment:
POSTGRES_DB: postgres
POSTGRES_USER: postgres
POSTGRES_PASSWORD: s3cret
docker-compose.yml - Snippet hosted by \"Cacher\"
");