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
Mojo Blog is simple example with DBD::Pg that makes PostgreSQL a lot of fun to use with the Mojolicious real-time web framework.
\nYou can use system Perl version for development. I recommend the use of perlbrew
or plenv
environment.
Cpanminus and Carton are the only prerequisite for running the application. All required modules/dependencies are then installed via Carton from cpanfile.
\nFor Perl-ish configuration Mojolicious plugin is used - Mojolicious::Plugin::Config.
\n$ docker build -t <NAME:TAG> .\n
\n$ docker run --rm -v $PWD/t:/opt/app/t <IMAGE> carton exec prove -lr -j4\n
\ntiller
in the Minikube cluster$ helm init\n
\n$ helm install --dry-run --debug --namespace=default ./helm\n
\n$ helm install --name mojo-blog --namespace=default ./helm\n
\n$ helm ls --all mojo-blog\n
\n$ helm del --purge mojo-blog\n
\n$ helm lint ./helm\n
\n$ helm template --name mojo-blog --namespace=default ./helm > mojo-blog.yaml\n
\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
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" } } } } } }
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.
\nArtefact 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.
\nFor 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.
\nCurrent 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
\nThe next steps can include transporting immutable images across the various stages (INT, PROD) or even across the different clusters.
\nVersioning is essential in application development. It must be possible to relate every deployment to one unique SCM commit to:
\nTo 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.
\nExample: 20170717.111749-master-1.2.3
\nAdditional tagging should also be applied after successful rollouts to INT / PROD in GIT.
\nMojo::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
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
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