We have got a task to run Omero application in Kubernetes. Basically, we need two things: Docker container of the application and deployment manifest. The application may have some depencencies like database, in this case we need to deploy those as well.
Searching for the term
omero docker, we got the result https://github.com/ome/omero-server-docker which explains how to deploy the Omero server. It has two options:
- Running OMERO with docker-compose
- Running the images (manual)
For Kubernetes, we need to choose the second option because docker-compose cannot be applied directly to Kubernetes.
The second option starts with database and it seems to be the Postgres database. Here, we are lucky because Kubernetes provide a Postgres operator for easy database deployment, see our Operators documentation.
In all the commands below, replace the
[namespace] with your own namespace name.
The Postgres operator creates random passwords by default, but in this example, we will create an explicit password for the Postgres database. We will use
omero as database user and some secret password e.g.,
xxxpassword. First, we need to create a secret with the name and the password. Both of them must be base64 encoded.
You can use the link or use
echo -e 'omero' | base64 in Linux shell. The
-n switch is essential, the string (name, password) must not contain the newline character (
\n). Once we have two base64 encoded values, we create and deploy secret:
apiVersion: v1 data: password: b21lcm9rb2tvcw== username: b21lcm8= kind: Secret metadata: name: omero-db-app type: kubernetes.io/basic-auth
metadata.name can be arbitrary but will be referenced in subsequent manifests. Link.
kubectl create -f db-app-secret.yaml -n [namespace]
Once we have the secret, we deploy the database. Simple configuration is enough.
apiVersion: postgresql.cnpg.io/v1 kind: Cluster metadata: name: omero spec: instances: 1 imageName: 'ghcr.io/cloudnative-pg/postgresql:15.3-2' primaryUpdateStrategy: unsupervised bootstrap: initdb: database: omero owner: omero secret: name: omero-db-app resources: requests: memory: 2Gi cpu: 100m limits: memory: 6Gi cpu: 2 storage: size: 10Gi storageClass: zfs-csi
This manifest references the secret name in the
spec.bootstrap.initdb.secret.name, it must match the name of the secret we created. If the database is slow to handle all the load, the
spec.resources.limits.cpu can be increased. For non-demo purposes, the
spec.resources.requests.cpu should be increased to match actual typical load. Link.
kubectl create -f db-cluster.yaml [namespace]
After a while, a new Pod should be created in the
[namespace] namespace. It will be named
The cluster create has only a single Pod, it is not a real and resilient cluster. For high availability clusters, see documentation.
omero-server was the second step. The page shows how to run it in the Docker:
docker run -d --name omero-server --link postgres:db \ -e CONFIG_omero_db_user=postgres \ -e CONFIG_omero_db_pass=postgres \ -e CONFIG_omero_db_name=postgres \ -e ROOTPASS=omero-root-password \ -p 4063:4063 -p 4064:4064 \ openmicroscopy/omero-server
Application in the managed Kubernetes cannot run as root. It is a good idea to get the expected user id from the container using the docker. So on a machine with docker installed, issue:
docker run -it --rm --entrypoint /bin/bash openmicroscopy/omero-server
This will create and run the Omero server docker container. Next, issue the
id command, it will show the expected user id and group id. It should look like this:
xhejtman@osiris:~$ docker run -it --rm --entrypoint /bin/bash openmicroscopy/omero-server bash-4.2$ id uid=1000(omero-server) gid=997(omero-server) groups=997(omero-server) bash-4.2$
So, for the server deployment, we use user id
1000 and group id
We can see, that we should pass four environment variables:
CONFIG_omero_db_user, ` CONFIG_omero_db_pass
, and ROOTPASS
. We need to pass an additional variable CONFIG_omero_db_host
to specify the database hostname. The created database has the hostname in the form [name]-rw
, so in our case omero-rw`.
We will use the database secret to fill the
CONFIG_omero_db_pass variables. The specific part of the manifest looks like this:
name: CONFIG_omero_db_user valueFrom: secretKeyRef: name: omero-db-app key: username
name: omero-db-app is the name of the secret and the
key: username is the key in the secret.
Scrolling down the github page, we can see that we will also need some persistent storage:
/opt/omero/server/OMERO.server/var: The OMERO.server var directory, including logs
/OMERO: The OMERO data directory
We will use a non-persistent
emptyDir volume for the logs and a persistent volume for the
/OMERO directory. This solution is based on the assumption, that the
/OMERO contains valuable data, while
/opt/omero/server/OMERO.server/var does not.
For the persistent volume, a PVC must be created using the following manifest:
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: pvc-omero-data spec: accessModes: - ReadWriteMany resources: requests: storage: 100Gi storageClassName: nfs-csi
kubectl create -f pvc-data.yaml -n [namespace]
Finally, we are ready to deploy the
omero-server using the complete manifest. It also exposes two ports in the same way as the Docker command. Do not forget to set your own ROOT password in the following fragment. Also, rather a Secret object should be used to pass the password.
name: ROOTPASS value: omero-root-password
kubectl create -f omero-server.yaml -n [namespace]
Now, we should have the
omero-server running. However, to be accessible from the cluster, another manifest needs to be ddeployed—service.
apiVersion: v1 kind: Service metadata: name: omero-server spec: type: ClusterIP ports: - port: 4064 name: ssl targetPort: 4064 - port: 4063 name: plain targetPort: 4063 selector: app: omero-server
kubectl create -f omero-server-svc.yaml -n [namespace]
This manifest ensures, that the
omero-server is accessible using the name
omero-server (it is the value of the
metadata.name). The ports in this service must match the ports from the Docker command
-p 4063:4063 -p 4064:4064. The name can be arbitrary, but will be different for each port.
omero-server is not enough, it does not provide a web interface. So, after some searching, we can follow the omero-web description.
The Docker command is:
docker run -d --name omero-web \ -e OMEROHOST=omero.example.org \ -p 4080:4080 \ openmicroscopy/omero-web-standalone
Obviously, we replace the
omero-server which references our service from the
omero-server. Using the same steps as for the
omero-server, we get the expected user id and user group:
xhejtman@osiris:~$ docker run -it --rm --entrypoint /bin/bash openmicroscopy/omero-web-standalone bash-4.2$ id uid=999(omero-web) gid=998(omero-web) groups=998(omero-web) bash-4.2$
Similar to the
omero-server, the web page states data volume:
/opt/omero/web/OMERO.web/var: The OMERO.web
vardirectory, including logs
For demo purposes, the volume type
emptyDir is sufficient.
Now, we are able to deploy the
kubectl create -f omero-web.yaml -n [namespace]
omero-web should be running now. To access it from the internet, we need a Service and an Ingress – because it is a web application. The Service is simple enabling the port from the Docker command
apiVersion: v1 kind: Service metadata: name: omero-web spec: type: ClusterIP ports: - port: 4080 name: web targetPort: 4080 selector: app: omero-web
kubectl create -f omero-svc.yaml -n [namespace]
And finally the Ingress:
apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: omero annotations: kubernetes.io/ingress.class: nginx kubernetes.io/tls-acme: "true" cert-manager.io/cluster-issuer: "letsencrypt-prod" spec: tls: - hosts: - "[omero-test].dyn.cloud.e-infra.cz" secretName: [omero-test]-dyn-cloud-e-infra-cz rules: - host: [omero-test].dyn.cloud.e-infra.cz http: paths: - pathType: Prefix path: "/" backend: service: name: omero-web port: number: 4080
[omero-test] with your own name. The total hostname must not exceed 63 characters. The port number (
4080) must match the service port number.
kubectl create -f omero-web-ingress.yaml -n [namespace]
You may see the
cm-acme-http-solver- Pod running, in this case, wait until it is finished, then you can open your Omero link in the browser. Log in using the username
root and the password from the
omero-server deployment (the value):
- name: ROOTPASS value: omero-root-password
Now we are done!