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.
custom
├── .gitignore
├── .gitlab-ci.yml (1)
├── .m2
│ └── settings.xml
├── README.md
├── 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 pipeline
When Magnolia PaaS is set up with GitLab, changes in your project automatically trigger your pipeline via what is configured in your .gitlab-ci.yml
file.
This way, changes are picked up automatically and you don’t have to worry about it. However, the final deployment step is sometimes manual and you’ll need to make a deploy action to finish the process. This is typically done by clicking a button manually in GitLab.
For a full view into deploying your webapp with Magnolia PaaS, check out the Webapp deployment page under the "Hello PaaS" getting started section. |
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.5
stage: deploy
before_script:
- export GIT_TAG=$CI_COMMIT_SHORT_SHA
- helm repo add mironet https://charts.mirohost.ch/
- export HELM_CHART_VERSION=1.6.2
- export KUBECONFIG=$KUBE_CONFIG (6)
deploy-dev: (7)
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}
- kubectl annotate --overwrite namespace ${DEPLOYMENT} field.cattle.io/projectId=`kubectl get namespace default --output="jsonpath={.metadata.annotations.field\.cattle\.io/projectId}"` (8)
- kubectl -n default get secret activation-key -o json | jq 'del(.metadata.annotations,.metadata.labels,.metadata.namespace,.metadata.resourceVersion,.metadata.uid,.metadata.namespace,.metadata.creationTimestamp)' | kubectl apply -n ${DEPLOYMENT} -f - (9)
- kubectl -n default get secret gitlab -o json | jq 'del(.metadata.annotations,.metadata.labels,.metadata.namespace,.metadata.resourceVersion,.metadata.uid,.metadata.namespace,.metadata.creationTimestamp)' | kubectl apply -n ${DEPLOYMENT} -f - (9)
- kubectl -n default get secret s3-backup-key -o json | jq 'del(.metadata.annotations,.metadata.labels,.metadata.namespace,.metadata.resourceVersion,.metadata.uid,.metadata.namespace,.metadata.creationTimestamp)' | kubectl apply -n ${DEPLOYMENT} -f - (9)
environment:
name: dev (10)
when: manual (11)
deploy-uat: (7)
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}
- kubectl annotate --overwrite namespace ${DEPLOYMENT} field.cattle.io/projectId=`kubectl get namespace default --output="jsonpath={.metadata.annotations.field\.cattle\.io/projectId}"` (8)
- kubectl -n default get secret activation-key -o json | jq 'del(.metadata.annotations,.metadata.labels,.metadata.namespace,.metadata.resourceVersion,.metadata.uid,.metadata.namespace,.metadata.creationTimestamp)' | kubectl apply -n ${DEPLOYMENT} -f - (9)
- kubectl -n default get secret gitlab -o json | jq 'del(.metadata.annotations,.metadata.labels,.metadata.namespace,.metadata.resourceVersion,.metadata.uid,.metadata.namespace,.metadata.creationTimestamp)' | kubectl apply -n ${DEPLOYMENT} -f - (9)
- kubectl -n default get secret s3-backup-key -o json | jq 'del(.metadata.annotations,.metadata.labels,.metadata.namespace,.metadata.resourceVersion,.metadata.uid,.metadata.namespace,.metadata.creationTimestamp)' | kubectl apply -n ${DEPLOYMENT} -f - (9)
environment:
name: dev (10)
when: manual (11)
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.
|
||
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 KUBE_CONFIG CI/CD variable should be defined as type File and hold KubeConfig of the cluster the deployment should go to. The same variable can be defined in different environment scopes (see 10 ) |
||
7 | 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 10 ). |
||
8 | The newly created namespace is added to the Rancher default project. |
||
9 | The needed secrets are copied over from the default namespace to the newly created namespace. |
||
10 | The environment name corresponds to the environment scope (dev or prod ) defined in the Deployments section. In different environments the same variable names can be used. |
||
11 | 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. |
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
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
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.
|
||
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
|
||
3 | Specify the pullSecrets .
|
||
4 | Boolean specifying if there is an automatic redeployment triggered by changes.
|
||
5 | Specify the environment password .
|
||
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 = |
||
9 | Sets your memory for requests and limits.
|
||
10 | Sets the volume size of the database.
|
||
11 | Defines that backups of the database are taken and stored to an S3 bucket provisioned by Magnolia.
|
||
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.
|
Unresolved include directive in modules/ROOT/pages/deployment/webapp_deployment.adoc - include::partial$rancher/r_clone-secrets.adoc[]