Webapp deployment

A Java Web Application (webapp) is a collection of servlets, other Java classes, static resources (such as HTML pages), other resources, and meta information that describes the webapp bundled together. The Java webapp in Magnolia PaaS has a typical structure.

Webapp structure example
custom
├── .gitignore
├── .gitlab-ci.yml (1)
├── .m2
│   └── settings.xml
├── README.md
├── custom-module
│   ├── pom.xml
│   ├── src
│   └── target
├── custom-webapp
│   ├── Dockerfile
│   ├── pom.xml
│   ├── src
│   └── target
├── pom.xml
└── values.yml (2)
1 The .gitlab-ci.yml file ensures your development changes are automatically picked up.
2 The values.yml file ensures your Magnolia PaaS application is deployed to the your cluster.

The .gitlab-ci.yml file

It’s important that you configure the .gitlab-ci.yml file correctly so that your development changes are picked up and deployed. If you are using a different CI/CD pipeline, you can use this file as a blueprint.

Magnolia automatically picks up the changes when using this approach.
.gitlab-ci.yml
# Use the latest Maven version

stages:
  - build
  - push
  - deploy

variables:
  MAVEN_OPTS: "-Dhttps.protocols=TLSv1.2 -Dmaven.repo.local=$CI_PROJECT_DIR/.m2/repository -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=WARN -Dorg.slf4j.simpleLogger.showDateTime=true -Djava.awt.headless=true"
  MAVEN_CLI_OPTS: "-s .m2/settings.xml --batch-mode --errors --fail-at-end --show-version -DinstallAtEnd=true -DdeployAtEnd=true"

# Build the Maven project.
build-magnolia: (1)
  image: maven:3.6-jdk-11-slim
  stage: build
  cache:
    key: "$CI_JOB_NAME"
    paths:
      - $CI_PROJECT_DIR/.m2/repository
  before_script:
    - mkdir -p $CI_PROJECT_DIR/.m2
  script:
    - mvn $MAVEN_CLI_OPTS package
    - ls -Fahl base-webapp/target
  artifacts:
    expire_in: 30 days
    paths:
      - base-webapp/target/*.war

# Build docker images based on artifacts from the build stage.
push-docker-image: (2)
  image:
    name: gcr.io/kaniko-project/executor:debug
    entrypoint: [""]
  stage: push
  dependencies:
    - build-magnolia
  before_script:
    - export WEBAPP_IMAGE=${CI_REGISTRY_IMAGE}/magnolia-webapp
    - export GIT_TAG=$CI_COMMIT_SHORT_SHA (3)
    - mkdir -p /kaniko/.docker
    - echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > /kaniko/.docker/config.json (4)
  script:
    - cd base-webapp
    - /kaniko/executor --context . --dockerfile ./Dockerfile --destination "$WEBAPP_IMAGE:$GIT_TAG"

.deploy: (5)
  image: registry.gitlab.com/mironet/helm-kubectl-gomplate:v0.0.3
  stage: deploy
  before_script:
    - export GIT_TAG=$CI_COMMIT_SHORT_SHA
    - helm repo add mironet https://charts.mirohost.ch/
    - export HELM_CHART_VERSION=1.5.4
  when: manual


deploy-dev: (6)
  extends: .deploy
  script:
    - export DEPLOYMENT=dev
    - export LE_ENVIRONMENT=letsencrypt-prod
    - cat values.yml | gomplate > ${DEPLOYMENT}.yml
    - cat ${DEPLOYMENT}.yml
    - helm upgrade -i ${DEPLOYMENT} mironet/magnolia-helm --version ${HELM_CHART_VERSION} -f ${DEPLOYMENT}.yml --create-namespace -n ${DEPLOYMENT}
  environment:
    name: dev (7)
  when: manual (8)

deploy-uat: (6)
  extends: .deploy
  script:
    - export DEPLOYMENT=uat
    - export LE_ENVIRONMENT=letsencrypt-prod
    - cat values.yml | gomplate > ${DEPLOYMENT}.yml
    - cat ${DEPLOYMENT}.yml
    - helm upgrade -i ${DEPLOYMENT} mironet/magnolia-helm --version ${HELM_CHART_VERSION} -f ${DEPLOYMENT}.yml --create-namespace -n ${DEPLOYMENT}
  environment:
    name: dev (7)
  when: manual (8)
1 In the build-magnolia stage, the web app is built using maven, as with any Magnolia project.
2 In the push-docker-image stage, the Docker image is built and pushed to the Docker registry (in this case the GitLab registry), using the Dockerfile located in the webapp folder.
3 The GIT_TAG is used to set the tag for the created Docker image.
4 The environment variables are set automatically by GitLab if the GitLab registry is used for the project.

We recommend that you use GitLab.

env variables
  • $CI_REGISTRY_USER

  • $CI_REGISTRY_PASSWORD

  • $CI_REGISTRY

5 The general deployment stage defines the helm chart repo and the version of the Helm chart to be used in the actual deployments.
6 The actual deployment stages define the namespace and prefix for the deployment. These stages can be duplicated for different namespaces (so that deployments can run in parallel on the cluster) and for different clusters (see 7).
7 The environment name corresponds to the environment scope defined when the Kubernetes clusters were added to GitLab.
8 The deployment must be triggered manually.

The values.yml file

The values.yml file will hold the configuration used by the Magnolia Helm Chart in the process of deploying the application to the cluster. Properties like DEPLOYMENT must be the same as in the .gitlab-ci.yml file.

Here, we provide you with a template values.yml file to get you started.

For more details on values, see our Magnolia PaaS Helm Values reference page.
Prod vs non-prod

Typically, you will have a values.yml file for both prod and non-prod as the values will be different depending on if the deployment is intended for testing or production. We encourage you to have separate files for this purpose.

Example:

  • test = values.yml

  • prod = values-prod.yml

Cert-manager
ingress:
  enabled: true
  annotations:
    kubernetes.io/ingress.class: "nginx"
    nginx.ingress.kubernetes.io/proxy-body-size: 512m
    cert-manager.io/cluster-issuer: "letsencrypt-prod" (1)
1 The cert-manager is automatically created by us. However, you can use your own. If you choose to do this, please contact the Magnolia PaaS Helpdesk.

Sample file

values.yml
ingress:
  enabled: true
  annotations:
    kubernetes.io/ingress.class: "nginx"
    nginx.ingress.kubernetes.io/proxy-body-size: 512m
    cert-manager.io/cluster-issuer: "letsencrypt-prod"
  hosts:
    - host: {{ .Env.DEPLOYMENT }}.<realm>.magnolia-platform.com (1) (2)
      paths:
        - path: /
          instance: public
        - path: /author
          instance: author
  tls:
    - hosts:
        - {{ .Env.DEPLOYMENT }}.<realm>.magnolia-platform.com (1) (2)
      secretName: {{ .Env.DEPLOYMENT }}.<realm>.magnolia-platform.com (1) (2)
image:
  pullSecrets: (3)
    - name: docker-registry
  pullPolicy: Always
magnoliaAuthor:
  replicas: 1
  restartPolicy: Always
  redeploy: true (4)
  contextPath: /author
  webarchive:
    repository: {{ .Env.CI_REGISTRY_IMAGE }}/magnolia-webapp
    tag: "{{ .Env.GIT_TAG | quote }}"
  bootstrap:
    password: "<password>" (5)
  activation:
    useExistingSecret: True (6)
    secret:
      name: activation-key
      key: activation-secret
  env:
    - name: instance
      value: "author"
    - name: deployment
      value: {{ .Env.DEPLOYMENT }}
    - name: magnolia.superuser.enabled
      value: "true"
    - name: magnolia.superuser.password
      value: "<password>" (5)
    - name: magnolia.bootstrap.license.owner
      value: "[replace with email]" (7)
    - name: magnolia.bootstrap.license.key
      value: "[replace with key]" (7)
  setenv:
    memory:
      maxPercentage: 80 (8)
  resources:
    requests:
      memory: 4Gi (9)
    limits:
      memory: 4Gi (9)
  livenessProbe:
    enabled: true
  podAnnotations:
    magnolia.info/is-magnolia: "true"
  db:
    persistence:
      size: "10Gi" (10)
    contentsync:
      enabled: true
    restore:
      enabled: False
    backup:
      enabled: True (11)
      env:
        - name: MGNLBACKUP_PG_DATA
          value: "/db/data"
        - name: MGNLBACKUP_USE_PG_WAL
          value: "true"
        - name: MGNLBACKUP_SYNC_DIR
          value: "/archive"
        - name: MGNLBACKUP_NO_STDOUT
          value: "true"
        - name: MGNLBACKUP_LOGLEVEL
          value: "debug"
        - name: MGNLBACKUP_HERITAGE
          value: "magnolia-backup"
        - name: MGNLBACKUP_BUCKET
          value: "<realm>-backup-bucket" (2)
        - name: MGNLBACKUP_PREFIX
          value: {{ .Env.DEPLOYMENT }}/author
        - name: MGNLBACKUP_CRON
          value: "0 3 * * *" (11)
        - name: MGNLBACKUP_KEEPDAYS
          value: "30"
        - name: MGNLBACKUP_S3_ENDPOINT
          value: "s3.eu-central-1.amazonaws.com"
        - name: MGNLBACKUP_S3_REGION
          value: "eu-central-1"
        - name: MGNLBACKUP_S3_ACCESSKEY
          valueFrom:
            secretKeyRef:
              name: s3-backup-key
              key: accesskey
        - name: MGNLBACKUP_S3_SECRETKEY
          valueFrom:
            secretKeyRef:
              name: s3-backup-key
              key: secretkey
        - name: MGNLBACKUP_TAGS_NODE_NAME
          valueFrom:
            fieldRef:
              fieldPath: spec.nodeName
        - name: MGNLBACKUP_TAGS_NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace
        - name: MGNLBACKUP_TAGS_POD_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.name
        - name: MGNLBACKUP_TAGS_RELEASE
          value: {{ .Env.DEPLOYMENT }}
    podAnnotations:
      magnolia.info/is-magnolia: "true"
magnoliaPublic:
  replicas: 2 (12)
  restartPolicy: Always
  contextPath: /
  webarchive:
    repository: {{ .Env.CI_REGISTRY_IMAGE }}/magnolia-webapp
    tag: "{{ .Env.GIT_TAG | quote }}"
  bootstrap:
    password: "<password>" (5)
  activation:
    useExistingSecret: True (6)
    secret:
      name: activation-key
      key: activation-secret
  env:
    - name: instance
      value: "public"
    - name: deployment
      value: {{ .Env.DEPLOYMENT }}
    - name: magnolia.superuser.enabled
      value: "true"
    - name: magnolia.superuser.password
      value: "<password>" (5)
    - name: magnolia.bootstrap.license.owner
      value: "[replace with email]" (7)
    - name: magnolia.bootstrap.license.key
      value: "[replace with key]" (7)
  setenv:
    memory:
      maxPercentage: 80 (8)
  resources:
    requests:
      memory: 4Gi (9)
    limits:
      memory: 4Gi (9)
  livenessProbe:
    enabled: true
  podAnnotations:
    magnolia.info/is-magnolia: "true"
  db:
    persistence:
      size: "10Gi" (10)
    contentsync:
      enabled: true
    restore:
      enabled: False
    backup:
      enabled: True (11)
      env:
        - name: MGNLBACKUP_PG_DATA
          value: "/db/data"
        - name: MGNLBACKUP_USE_PG_WAL
          value: "true"
        - name: MGNLBACKUP_SYNC_DIR
          value: "/archive"
        - name: MGNLBACKUP_NO_STDOUT
          value: "true"
        - name: MGNLBACKUP_LOGLEVEL
          value: "debug"
        - name: MGNLBACKUP_HERITAGE
          value: "magnolia-backup"
        - name: MGNLBACKUP_BUCKET (11)
          value: "<realm>-backup-bucket" (2)
        - name: MGNLBACKUP_PREFIX
          value: {{ .Env.DEPLOYMENT }}/public
        - name: MGNLBACKUP_CRON
          value: "0 3 * * *" (13)
        - name: MGNLBACKUP_KEEPDAYS
          value: "30"
        - name: MGNLBACKUP_S3_ENDPOINT
          value: "s3.eu-central-1.amazonaws.com"
        - name: MGNLBACKUP_S3_REGION
          value: "eu-central-1"
        - name: MGNLBACKUP_S3_ACCESSKEY
          valueFrom:
            secretKeyRef:
              name: s3-backup-key
              key: accesskey
        - name: MGNLBACKUP_S3_SECRETKEY
          valueFrom:
            secretKeyRef:
              name: s3-backup-key
              key: secretkey
        - name: MGNLBACKUP_TAGS_NODE_NAME
          valueFrom:
            fieldRef:
              fieldPath: spec.nodeName
        - name: MGNLBACKUP_TAGS_NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace
        - name: MGNLBACKUP_TAGS_POD_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.name
        - name: MGNLBACKUP_TAGS_RELEASE
          value: {{ .Env.DEPLOYMENT }}
    podAnnotations:
      magnolia.info/is-magnolia: "true"
1 The {{ .Env.DEPLOYMENT }} value should be set by the build pipeline and should be the same as used in the RELEASE value in the Helm call.
A wildcard DNS entry for <realm>.magnolia-platform.com `would support any valid value for `{{ .Env.DEPLOYMENT }} and create a corresponding SSL certificate.
2 The <realm> name is generated by Magnolia when we create your cluster. You can set this value as shortname while creating the Magnolia project using the Cloud Archetype
This is typically your company name.
3 Specify the pullSecrets.
if the webarchive image is located in a private registry, see [Use Docker secret].
4 Boolean specifying if there is an automatic redeployment triggered by changes.
defaults
  • magnoliaAuthor = false

  • magnoliaPublic = true

If set to true the instance is restarted even if the tag of the webarchive was not changed between deployments.

5 Specify the environment password.
This must be the same in all places for the bootstrapping container to work correctly.
6 The activation key is handled by the bootstrapper container. This keeps magnoliaAuthor and magnoliaPublic in sync.
7 These 2 values must be replaced with the license info for this project, provided by Magnolia.
8 Specify the maximum percentage memory that is reserved for the tomcat container.

Default = 60

9 Sets your memory for requests and limits.
4GB is typically sufficient for Magnolia CMS.
10 Sets the volume size of the database.
10Gi is typically sufficient for a dev environment, 100Gi for production.
11 Defines that backups of the database are taken and stored to an S3 bucket provisioned by Magnolia.
Every transaction is written, which allows point-in-time recovery, see PostGres Continuous Archiving for more details.
12 The replicas parameter used on magnoliaPublic defines how many public instances are created. If not defined, 1 public instance is created.
13 Defines the time when the full backup of the database takes place, in CRON syntax.
The example shows every day at 3am.
Feedback