Skip to content

Commit 710e92b

Browse files
committed
Add blog about Orthanc on OpenShift
1 parent bc02a73 commit 710e92b

11 files changed

Lines changed: 248 additions & 2 deletions

File tree

blog/2024-11-15-kubecon.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
title: "Kubecon 2024: Medical Imaging on Hybrid Kubernetes"
33
authors: jennings
4-
tags: [videos]
4+
tags: [videos, Kubernetes, OpenShift]
55
---
66

77
Watch Jennings' talk *"Medical Research Computing Infrastructure on Hybrid Kubernetes"* presented at Kubecon + CloudNativeCon North America 2024.

blog/2025-07-21-advantages-of-rust-orthanc-plugins/index.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ authors: jennings
44
tags: [rust, orthanc, software design]
55
---
66

7-
import ORTHANC_LOGO from './OrthancLogo-2016.png';
7+
import ORTHANC_LOGO from '/static/img/3rdparty/OrthancLogo-2016.png';
88
import RustLogo from './rust-logo-blk.svg';
99
import styles from './styles.module.css';
1010

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
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+
![Screenshot of "Patient List UI"](./screenshot_light.png#gh-light-mode-only)
47+
![Screenshot of "Patient List UI"](./screenshot_dark.png#gh-dark-mode-only)
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.
64.7 KB
Loading
64.3 KB
Loading
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
.flex {
2+
display: flex;
3+
justify-content: space-evenly;
4+
align-items: center;
5+
}
6+
7+
.plus::after {
8+
content: '+';
9+
font-size: min(6cqw, 6cqh);
10+
}
11+
12+
.wideLogo {
13+
width: 30%;
14+
}
15+
16+
.chrisLogo {
17+
width: 15%;
18+
}
105 KB
Loading
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// NOTE: light.jpg is the original file, retrieved from
2+
// https://www.redhat.com/en/blog/introducing-red-hat-openshift-container-platform
3+
// then downsized. dark.png is edited from light.jpg and converted from
4+
// JPG to PNG to support background transparency.
5+
6+
import Dark from "./dark.png";
7+
import Light from "./light.jpg";
8+
import styles from "./styles.module.css";
9+
10+
const OpenshitLogo = ({ className, ...props }) => (
11+
<>
12+
<img
13+
alt="OpenShift Logo"
14+
src={Light}
15+
{...props}
16+
className={className ? `${className} ${styles.light}` : styles.light}
17+
/>
18+
<img
19+
alt="OpenShift Logo"
20+
src={Dark}
21+
{...props}
22+
className={className ? `${className} ${styles.dark}` : styles.dark}
23+
/>
24+
</>
25+
);
26+
27+
export default OpenshitLogo;
40.9 KB
Loading
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
[data-theme="light"] .dark {
2+
display: none;
3+
}
4+
5+
[data-theme="dark"] .light {
6+
display: none;
7+
}

0 commit comments

Comments
 (0)