|
| 1 | +--- |
| 2 | +title: "Orthanc on OpenShift" |
| 3 | +authors: jennings |
| 4 | +tags: [OpenShift, Orthanc, NERC] |
| 5 | +--- |
| 6 | + |
| 7 | +import ChrisLogo from '/static/img/logo/ChRISlogo-color.svg'; |
| 8 | +import ORTHANC_LOGO from '/static/img/3rdparty/OrthancLogo-2016.png'; |
| 9 | +import OpenshiftLogo from '/src/components/OpenshiftLogo'; |
| 10 | +import styles from './styles.module.css'; |
| 11 | + |
| 12 | +<div className={styles.flex}> |
| 13 | + <ChrisLogo className={styles.chrisLogo} /> |
| 14 | + <div className={styles.plus} /> |
| 15 | + <img className={styles.wideLogo} src={ORTHANC_LOGO} alt="Orthanc Logo" /> |
| 16 | + <div className={styles.plus} /> |
| 17 | + <OpenshiftLogo className={styles.wideLogo} /> |
| 18 | +</div> |
| 19 | +<br /> |
| 20 | + |
| 21 | +Our research lab, the [FNNDSC](https://fnndsc.org), directs international |
| 22 | +collaborations on pediatric neuroimaging research. The data sharing platform |
| 23 | +for this research is [_ChRIS_](/). We have implemented a solution for sharing |
| 24 | +DICOM data between "peer" instances of _ChRIS_ using |
| 25 | +[Orthanc](https://orthanc.uclouvain.be/), an open-source DICOM server. |
| 26 | +Here we describe our deployment of Orthanc on |
| 27 | +[Red Hat OpenShift](https://developers.redhat.com/products/openshift/overview) |
| 28 | +and other open-source contributions we would like to share with the Orthanc |
| 29 | +community. |
| 30 | + |
| 31 | +<!--truncate--> |
| 32 | + |
| 33 | +## Summary |
| 34 | + |
| 35 | +Our team is proud to share three contributions with the Orthanc community: |
| 36 | + |
| 37 | +- A Rust SDK for Orthanc plugins, described in a previous blog: |
| 38 | + [Rust for Orthanc Plugins](../2025-07-21-advantages-of-rust-orthanc-plugins/index.mdx). |
| 39 | +- A patient-oriented [custom UI](#patient-list-ui) for Orthanc. |
| 40 | +- A [Helm chart](#helm-chart) for deploying Orthanc on OpenShift. |
| 41 | + |
| 42 | +The latter two points are discussed below. |
| 43 | + |
| 44 | +## Patient List UI {#patient-list-ui} |
| 45 | + |
| 46 | + |
| 47 | + |
| 48 | + |
| 49 | +Our researchers had a simple ask: |
| 50 | + |
| 51 | +- See studies organized in "folders" by patient |
| 52 | +- Be able to compare images side-by-side in OHIF |
| 53 | + |
| 54 | +We have developed a custom user interface (UI) satisfying these two needs. |
| 55 | +It is GPL-licensed (same as Orthanc) hence free for anyone to use. As an |
| 56 | +Orthanc plugin developed against its C bindings, the process of installation |
| 57 | +is as simple as downloading the pre-compiled `.so` file and telling Orthanc |
| 58 | +to load it via its JSON configuration. For more details, see its README on the |
| 59 | +GitHub repository [FNNDSC/orthanc-patient-list](https://github.com/FNNDSC/orthanc-patient-list). |
| 60 | + |
| 61 | +### Packaging a Static Web App as an Orthanc Plugin |
| 62 | + |
| 63 | +Packaging a custom UI for Orthanc as an Orthanc plugin has numerous advantages: |
| 64 | +it is both easier for end-users to install, and integrated with Orthanc's web |
| 65 | +server features e.g. authentication. |
| 66 | + |
| 67 | +Creating an Orthanc plugin traditionally required you to setup a C++ and CMake |
| 68 | +toolchain. Now, it is much easier with the Rust `orthanc_sdk`. Version 0.2.0 |
| 69 | +introduces a new feature which makes it possible to package a JavaScript web |
| 70 | +app bundle as a native Orthanc plugin with just (50 lines of boilerplate and) |
| 71 | +3 lines of code: |
| 72 | + |
| 73 | +```rust |
| 74 | +/// Import your bundle (e.g. output of `vite build` into the static binary |
| 75 | +const DIST: include_dir::Dir = include_dir!("$CARGO_MANIFEST_DIR/../dist"); |
| 76 | + |
| 77 | +/// In `OrthancPluginInitialize`, register a REST callback |
| 78 | +orthanc_sdk::register_rest_no_lock(context, c"/my_webapp(/.*)?", Some(rest_callback)); |
| 79 | + |
| 80 | +/// In the callback function, call `serve_static_file` |
| 81 | +orthanc_sdk::serve_static_file(context, output, url, request, &DIST, "/my_webapp") |
| 82 | +``` |
| 83 | + |
| 84 | +More details with a complete example can be found on |
| 85 | +[docs.rs/orthanc_sdk](https://docs.rs/orthanc_sdk/0.2.0/orthanc_sdk/fn.serve_static_file.html). |
| 86 | + |
| 87 | +## Helm Chart for Orthanc {#helm-chart} |
| 88 | + |
| 89 | +We also maintain `fnndsc/charts` which provides a Helm chart for Orthanc. |
| 90 | +[Helm](https://helm.sh/) calls itself "the package manager for Kubernetes"—it |
| 91 | +makes it easy to share reusable templates for creating Kubernetes resources |
| 92 | +such as deployments. |
| 93 | + |
| 94 | +The Orthanc chart provided by `fnndsc/charts` will take advantage of |
| 95 | +OpenShift-specific features when available (it also works on plain Kubernetes). |
| 96 | +[Red Hat OpenShift](https://developers.redhat.com/products/openshift/overview) |
| 97 | +is best described as a distribution of Kubernetes bundled with a preset of |
| 98 | +common components. It is the commercial offering of [OKD](https://okd.io/) |
| 99 | +(i.e. OKD is free and open-source OpenShift). The chart for Orthanc provided |
| 100 | +by `fnndsc/charts` leverages the OpenShift container platform (OCP) to: |
| 101 | + |
| 102 | +- Automatically setup and configure an `ObjectBucketClaim` to provide |
| 103 | + S3-compatible storage for Orthanc. |
| 104 | + - Automatic configuration of client-side S3 object storage encryption is also |
| 105 | + made easy. |
| 106 | +- Use the [CrunchyData Postgres Operator](https://github.com/CrunchyData/postgres-operator) |
| 107 | + to provide a PostgreSQL cluster. |
| 108 | + - The configuration is highly flexible. It is possible to specify multiple |
| 109 | + replicas, high availability, automated backups, pooling with pgBouncer, etc. |
| 110 | +- Integration with either [Prometheus](https://github.com/prometheus-operator/prometheus-operator) |
| 111 | + or [OpenTelemetry](https://opentelemetry.io/) operator to ingest Orthanc's |
| 112 | + metrics to a cloud-native observability platform. |
| 113 | +- Optional integration with [oauth2-proxy](https://oauth2-proxy.github.io/oauth2-proxy/) |
| 114 | + to secure Orthanc with modern single-sign-on platforms. |
| 115 | +- Configure Orthanc using YAML instead of JSON. |
| 116 | + - Furthermore, the templates will auto-configure values intelligently. E.g. |
| 117 | + if unspecified, the value for `ConcurrentJobs` will be set depending on the |
| 118 | + value of `resources.requests.cpu` converted to number of cores. |
| 119 | + |
| 120 | +`fnndsc/charts` is not the first open-source Helm chart for Orthanc. The |
| 121 | +[Korthweb](https://digihunch.github.io/korthweb/) project predates |
| 122 | +`fnndsc/charts`. Our chart is simpler than Korthweb in some ways (without |
| 123 | +FluxCD, Istio, cert-manager) while also having the advantages described above. |
| 124 | + |
| 125 | +<details> |
| 126 | +<summary>Side note: about PostgreSQL on Kubernetes</summary> |
| 127 | + |
| 128 | +The most popular solution for deploying Postgres on Kubernetes is Bitnami |
| 129 | +Charts. Bitnami, which was bought by Broadcom, did a rug-pull on the |
| 130 | +open-source community and is converting Bitnami Charts to a proprietary service |
| 131 | +(see [bitnami/charts\#35164](https://github.com/bitnami/charts/issues/35164)). |
| 132 | +Indeed, Korthweb uses Bitnami for Postgres. |
| 133 | + |
| 134 | +`fnndsc/charts` leverages Crunchy PGO instead of Bitnami for its Orthanc chart. |
| 135 | + |
| 136 | +</details> |
| 137 | + |
| 138 | +### Adding Third-Party Plugins to Orthanc on OpenShift |
| 139 | + |
| 140 | +Adding plugins to Orthanc is trivial when running it locally, but simple things |
| 141 | +are trickier to do in Kubernetes and especially OpenShift. Fortunately, |
| 142 | +OpenShift provides a simple and powerful mechanism for making customizations to |
| 143 | +container images. |
| 144 | + |
| 145 | +We can tell OpenShift to build a custom Orthanc image: |
| 146 | + |
| 147 | +```yaml |
| 148 | +kind: BuildConfig |
| 149 | +apiVersion: build.openshift.io/v1 |
| 150 | +metadata: |
| 151 | + name: my-customized-orthanc |
| 152 | + annotations: |
| 153 | + kubernetes.io/description: "Installs custom plugins into Orthanc." |
| 154 | +spec: |
| 155 | + runPolicy: Serial |
| 156 | + triggers: |
| 157 | + - type: ConfigChange |
| 158 | + source: |
| 159 | + dockerfile: | |
| 160 | + # syntax=docker/dockerfile:1.2 |
| 161 | + FROM jodogne/orthanc-plugins |
| 162 | + ADD --chmod=644 https://github.com/FNNDSC/orthanc-patient-list/releases/download/release%2F20250805/libpatient_list_ui.so /usr/local/share/orthanc/custom_plugins/libFnndscPatientList.so |
| 163 | + strategy: |
| 164 | + dockerStrategy: |
| 165 | + from: |
| 166 | + kind: DockerImage |
| 167 | + name: docker.io/jodogne/orthanc-plugins:1.12.8 |
| 168 | + pullSecret: |
| 169 | + name: fnndscnercpull |
| 170 | + output: |
| 171 | + to: |
| 172 | + kind: ImageStreamTag |
| 173 | + name: orthanc:custom |
| 174 | +``` |
| 175 | + |
| 176 | +OpenShift will build the inline `Dockerfile` and store it in a local container |
| 177 | +registry (usually `image-registry.openshift-image-registry.svc:5000`). Next, |
| 178 | +the Orthanc deployment needs to be annotated to tell OpenShift that Orthanc |
| 179 | +should be updated when the plugin-containing image is built: |
| 180 | + |
| 181 | +```shell |
| 182 | +oc set triggers deploy/orthanc --from-image=orthanc:custom -c orthanc |
| 183 | +``` |
| 184 | + |
| 185 | +Not only is this an elegant way to load plugins into Orthanc, it also has the |
| 186 | +added benefit of improving container cold-start times because kubelet will be |
| 187 | +able to pull images from a local registry instead of Docker Hub. |
| 188 | + |
| 189 | +## Conclusion |
| 190 | + |
| 191 | +These work come together to form the basis of the cloud-native data sharing |
| 192 | +platform used by our research center's staff and their international |
| 193 | +collaborators. We hope that the broader Orthanc community will also find use |
| 194 | +in our open-source contributions. |
0 commit comments