Skip to main content

Install Patient Journey Intelligence on self-managed Kubernetes

This guide installs Patient Journey Intelligence on a self-managed upstream Kubernetes cluster, the kind you build yourself with kubeadm, the standard Kubernetes setup tool. It works on a single server and on a cluster that spans several servers. A server with 16 vCPU and 64 GiB of memory runs the ingestion services; 32 vCPU and 128 GiB runs all services. Everything runs inside your own infrastructure. Review the common prerequisites and prepare the required release files first.

The guide is for operators and DevOps engineers who already run Kubernetes, or who prefer a standard Kubernetes cluster to K3s.

If you only need Patient Journey Intelligence on one Linux server and have no preference, Install on K3s is shorter, because the release package builds the cluster for you. Choose this guide when your organization standardizes on upstream Kubernetes, or when you plan to run Patient Journey Intelligence across several servers.

Release 2.2.3 or later

Kubernetes installs need release 2.2.3 or later. Earlier releases do not support the --k8s option.

Pass --k8s to the installer and updater. It reads /etc/kubernetes/admin.conf, selects the nginx ingress class, and points the GPU operator at the system containerd socket. Do not combine it with --eks, --gke, --aks, or --openshift.

Steps you run, in order​

Run these steps in this order:

StepTool or scriptRun it
1kubeadm, kubectl, and helmOnce per cluster, to build Kubernetes. Skip it if you already have a cluster.
2pj-app-installer.shOnce, to install Patient Journey Intelligence.
3setup_instance.shAfter installing, to size Patient Journey Intelligence for the server's memory.
4pj-app-updater.shFor every later release, to upgrade Patient Journey Intelligence.

Pass --k8s to the installer and the updater every time you run them. All scripts must run as root or with sudo.

What --k8s changes

It points the scripts at the Kubernetes settings file, /etc/kubernetes/admin.conf, instead of the K3s one, and sends web traffic through the NGINX ingress class instead of Traefik. With --install-gpu-operator, it also points the GPU Operator at the system container runtime. Do not combine it with --eks, --gke, --aks, or --openshift.


Before you begin​

Server requirements​

Choose the size based on the services you plan to run. These sizes are for one server. On a cluster with several servers, plan for at least this much capacity in total.

WorkloadCPUMemory
Ingestion services (minimum)16 vCPU64 GiB
All services32 vCPU128 GiB

Other requirements:

ResourceMinimumNotes
Free disk on /var/lib80 GBContainer images are stored under /var/lib/containerd. No script checks this for you, so confirm it before you start.
Operating systemLinux, such as Debian, Ubuntu, or RHELx86-64. The cluster commands in Step 1 are for Ubuntu.
SwapOffKubernetes does not start while swap is on. Step 1 turns it off.
GPU (optional)NVIDIA GPUOnly needed to run the document processor or language model in GPU mode.

Network requirements​

  • Outbound: every server must reach the internet to download Kubernetes, Helm, and the container images. For servers without internet access, see Air-Gapped Installs.
  • Inbound: open port 80 for HTTP. Open port 443 if you use HTTPS.
  • Between servers: on a cluster with more than one server, open TCP 6443 to the first server, TCP 10250 on every server, and the ports the Calico network plugin uses: TCP 179, UDP 4789, and TCP 5473.
  • DNS: HTTPS needs a DNS name, such as pji.example.com, that points to a server running the ingress controller. On a single server, that is the server itself. Let's Encrypt must reach that server on port 80 to issue the certificate.

Tools on the server​

The installer and updater stop if curl, awk, or jq is missing. They also use openssl and tar. setup_instance.sh uses python3.

Ubuntu or Debian
sudo apt-get update
sudo apt-get install -y curl jq openssl tar python3 unzip
RHEL
sudo dnf install -y curl jq openssl tar python3 unzip

Install the AWS CLI to download the release package:

curl -sSL "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o awscliv2.zip
unzip -q awscliv2.zip
sudo ./aws/install
aws --version

Unlike K3s, a kubeadm cluster does not come with kubectl, crictl, or Helm. Step 1 installs all three.

What John Snow Labs provides​

  • The release version to install, for example 2.3.0.
  • AWS credentials that can read the release package.
  • Your license file, license.json.

Step 1: Build the Kubernetes cluster​

Already have a cluster? Confirm it passes Check The Cluster, then go to Step 2.

The release package does not build Kubernetes for you, so you build the cluster with the standard tools. The commands below are for Ubuntu. For other distributions, and for background on each step, see the Kubernetes documentation.

1a. Prepare every server​

Run these commands on the first server, and later on every server you add. They turn off swap, load the kernel settings Kubernetes needs, and install the container runtime:

sudo swapoff -a && sudo sed -i '/ swap / s/^/#/' /etc/fstab

printf 'overlay\nbr_netfilter\n' | sudo tee /etc/modules-load.d/k8s.conf
sudo modprobe overlay && sudo modprobe br_netfilter
printf 'net.bridge.bridge-nf-call-iptables=1\nnet.bridge.bridge-nf-call-ip6tables=1\nnet.ipv4.ip_forward=1\n' | sudo tee /etc/sysctl.d/99-k8s.conf
sudo sysctl --system

sudo apt-get update && sudo apt-get install -y containerd
sudo mkdir -p /etc/containerd
containerd config default | sudo tee /etc/containerd/config.toml >/dev/null
sudo sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml
sudo systemctl restart containerd && sudo systemctl enable containerd

Then install the Kubernetes tools, Helm, and crictl:

sudo apt-get install -y apt-transport-https ca-certificates curl gpg
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.32/deb/Release.key | sudo gpg --dearmor --yes -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
echo "deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.32/deb/ /" | sudo tee /etc/apt/sources.list.d/kubernetes.list
sudo apt-get update && sudo apt-get install -y kubelet kubeadm kubectl
sudo apt-mark hold kubelet kubeadm kubectl

curl -fsSL https://get.helm.sh/helm-v3.16.4-linux-amd64.tar.gz | sudo tar -C /usr/local/bin -xz linux-amd64/helm --strip-components=1
curl -fsSL https://github.com/kubernetes-sigs/cri-tools/releases/download/v1.32.0/crictl-v1.32.0-linux-amd64.tar.gz | sudo tar -C /usr/local/bin -xz
printf 'runtime-endpoint: unix:///run/containerd/containerd.sock\nimage-endpoint: unix:///run/containerd/containerd.sock\n' | sudo tee /etc/crictl.yaml

If you plan to use more than one server, also install the NFS client. Shared storage, which you turn on with --nfs, needs it on every server:

sudo apt-get install -y nfs-common
Keep every server on the same version

apt-mark hold stops routine package updates from moving one server to a newer Kubernetes version than the others. Upgrade Kubernetes deliberately, on every server, one minor version at a time.

1b. Create the cluster​

Run this on the first server only:

sudo kubeadm init \
--node-name="$(hostname | tr 'A-Z' 'a-z')" \
--apiserver-advertise-address="$(ip -4 route get 1.1.1.1 | grep -oP 'src \K\S+')" \
--pod-network-cidr=192.168.0.0/16

kubeadm init prints a kubeadm join command. Keep it if you plan to add servers. You can also create a new one later.

Give root and your own user access to the cluster. This guide runs kubectl, Helm, and setup_instance.sh with sudo, so root needs its own copy of the settings file:

sudo mkdir -p /root/.kube && sudo cp -f /etc/kubernetes/admin.conf /root/.kube/config
mkdir -p ~/.kube && sudo cp -f /etc/kubernetes/admin.conf ~/.kube/config && sudo chown "$(id -u):$(id -g)" ~/.kube/config
About these settings
  • --node-name sets the server name in lower case. Kubernetes rejects server names with capital letters.
  • --apiserver-advertise-address is the server's address on its default network route. The other servers use it to reach this one.
  • --pod-network-cidr is the address range Kubernetes assigns to pods. It must not overlap the network your servers are on. If it does, choose another range here and use the same range in Step 1c.

1c. Install the network plugin​

Kubernetes has no pod networking until you add a network plugin, and every server stays NotReady until one is running. This guide uses Calico:

sudo kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.28.2/manifests/tigera-operator.yaml
sudo kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.28.2/manifests/custom-resources.yaml
sudo kubectl wait --for=condition=Ready node --all --timeout=10m

Calico expects the 192.168.0.0/16 range set in Step 1b. If you chose a different range, download custom-resources.yaml, change its cidr value to match, and create the resources from your copy instead.

1d. Let the server run applications​

By default, Kubernetes keeps applications off the first server, which manages the cluster. On a cluster with one server, remove that restriction, or nothing can start:

sudo kubectl taint nodes --all node-role.kubernetes.io/control-plane-

On a cluster with several servers, skip this step. Keeping the restriction runs Patient Journey Intelligence on the servers you add and leaves the first server free to manage the cluster.

1e. Add storage and ingress​

A kubeadm cluster has no storage provisioner and no ingress controller. The installer needs both: a default storage class for application data, and the NGINX ingress class for web traffic.

sudo kubectl apply -f https://raw.githubusercontent.com/rancher/local-path-provisioner/v0.0.30/deploy/local-path-storage.yaml
sudo kubectl patch storageclass local-path -p '{"metadata":{"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'
sudo crictl pull docker.io/library/busybox:latest

sudo helm upgrade --install ingress-nginx ingress-nginx \
--repo https://kubernetes.github.io/ingress-nginx \
--namespace ingress-nginx --create-namespace \
--set controller.kind=DaemonSet \
--set controller.hostNetwork=true \
--set controller.hostPort.enabled=true \
--set controller.dnsPolicy=ClusterFirstWithHostNet \
--set controller.ingressClassResource.default=true

The storage provisioner keeps application data under /opt/local-path-provisioner on each server. The ingress controller runs on every server that accepts applications and listens directly on ports 80 and 443.

Do not skip the busybox download

The storage provisioner uses the small busybox image to create each volume. If that image is still downloading while the large Patient Journey Intelligence images download, the first volume request can fail, and the services that need it wait indefinitely. Download it in advance on every server that will run Patient Journey Intelligence.

1f. Check the cluster​

sudo kubectl get nodes
sudo kubectl get storageclass
sudo kubectl get ingressclass

Confirm that:

  • Every server shows Ready.
  • local-path is marked (default).
  • An ingress class named nginx exists.

If you are using a cluster you built another way, also confirm that root can run kubectl and that crictl can reach the container runtime. The installer checks the cluster before it changes anything and stops if one of these is missing.


Ingress controller​

Unlike K3s, kubeadm provides no ingress controller. Install ingress-nginx, which binds ports 80 and 443 on every node. If you followed Step 1e, the controller is already installed with these settings:

helm upgrade --install ingress-nginx ingress-nginx --repo https://kubernetes.github.io/ingress-nginx \
--namespace ingress-nginx --create-namespace \
--set controller.hostNetwork=true --set controller.hostPort.enabled=true \
--set controller.dnsPolicy=ClusterFirstWithHostNet \
--set controller.kind=DaemonSet --set controller.ingressClassResource.default=true

--k8s checks the cluster, StorageClass, and ingress before Helm runs and reports everything missing at once. On a cluster with several nodes it also warns that images are pre-pulled onto the node running the script only.


Step 2: Download the release package​

Set the version you were given, then configure the AWS credentials from John Snow Labs:

export PJI_VERSION="2.3.0"
aws configure

aws configure asks for the access key ID, the secret access key, and a region. Use us-east-1.

Download the package:

aws s3 cp \
"s3://dev.johnsnowlabs.com/jsl-omop/artifacts/pj-app-${PJI_VERSION}.tar.gz" \
~/

Step 3: Unpack the release package​

The package unpacks its files directly into the target folder. Create a folder for this release first, so files from different releases never mix.

mkdir -p ~/pj-app-${PJI_VERSION}
tar -xzf ~/pj-app-${PJI_VERSION}.tar.gz -C ~/pj-app-${PJI_VERSION}
cd ~/pj-app-${PJI_VERSION}

Copy your license file into the same folder:

cp /path/to/license.json ./license.json

Check the contents:

ls -l
Expected files
instructions.md
jsl_create_secret
jsl_docker_pull
k3s-installer.sh
marketplace-installer.sh
packer/
pj-app-2.3.0.tgz
pj-app-installer.sh
pj-app-updater.sh
setup_instance.sh

A Kubernetes install uses pj-app-installer.sh, setup_instance.sh, pj-app-updater.sh, the Helm chart pj-app-<version>.tgz, and the two helper programs jsl_create_secret and jsl_docker_pull. You do not need k3s-installer.sh. The other files are for AWS Marketplace and machine image builds.


Step 4: Install Patient Journey Intelligence​

Choose the command that matches your setup. Run it from the release folder. All options are described in Installer Options.

Before it changes anything, the installer checks that it can reach the cluster, that a default storage class exists, that at least one server accepts applications, and that the NGINX ingress class is installed. It reports every missing item at once, so you can fix them in one pass.

Option A: HTTPS with a DNS name​

Gets a certificate from Let's Encrypt.

sudo ./pj-app-installer.sh \
--k8s \
--host pji.example.com \
--namespace pji \
--license-file license.json \
--tls \
--install-cert-manager \
--acme-email admin@example.com \
--interactive-secrets

Option B: HTTP with a DNS name​

sudo ./pj-app-installer.sh \
--k8s \
--host pji.example.com \
--namespace pji \
--license-file license.json \
--interactive-secrets

Option C: HTTP without a DNS name​

If you leave out --host, the application has no host rule. The installer uses the server's public IP address, or its private IP address if no public one is found.

sudo ./pj-app-installer.sh \
--k8s \
--namespace pji \
--license-file license.json \
--interactive-secrets

Use this option on a single server only. On a cluster with several servers, use a DNS name that points to a server running the ingress controller.

--host takes a name, not an address

Kubernetes does not accept an IP address, such as 203.0.113.10, as a host name, and the install fails if you pass one. Use a DNS name, or leave --host out and use Option C.

Install across several servers​

On a cluster with several servers, add --nfs:

sudo ./pj-app-installer.sh \
--k8s \
--nfs \
--host pji.example.com \
--namespace pji \
--license-file license.json \
--interactive-secrets

--nfs creates shared storage for the model cache, so services on every server can use it. Without it, the cache lives on one server, and work scheduled on the other servers cannot reach it. Every server needs the NFS client from Step 1a.

The installer downloads the container images onto the server it runs on. If that server does not run applications, because you skipped Step 1d, add --skip-image-pull to save time and disk space. The other servers download the images they need when their services first start.

API keys (optional)​

API keys are optional. Currently, they are used only by:

  • the Data Curation Service, when it uses a custom language model
  • the terminology server

If you do not use these features, you can skip them. Press Enter at each prompt, or leave out --interactive-secrets.

note

The features that use these keys may change in later releases. Check the release notes for your version.

--interactive-secrets asks for each key in turn. To provide the keys without prompts, export them and run the installer with sudo -E so it can read them:

export OPENAI_API_KEY="sk-..."
export HUGGINGFACE_API_TOKEN="hf_..."
sudo -E ./pj-app-installer.sh --k8s --host pji.example.com --namespace pji --license-file license.json

See Environment Variables for every supported key.

Administrator password​

At the end, the installer prints Keycloak Admin Password. Store it in your password manager. You can read it again at any time:

sudo kubectl get secret pj-app-keycloak -n pji \
-o jsonpath='{.data.admin-password}' | base64 -d; echo

Step 5: Verify the installation​

Watch the services start. Press Ctrl+C when every pod shows Running or Completed. On a new cluster this takes a while, because the images are large:

sudo kubectl get pods -n pji -w

Check that every storage volume was created. STATUS should show Bound for each one:

sudo kubectl get pvc -n pji

If you used HTTPS, check the certificate. READY should show True:

sudo kubectl get certificate -n pji

Open the application in a browser at https://pji.example.com, http://pji.example.com, or http://<server-ip>, depending on the option you chose.

If you did not pass --license-file, upload the license in the web interface after you sign in.


Step 6: Size Patient Journey Intelligence for the server​

setup_instance.sh tunes a running installation for the server's total memory. It:

  • Sets replica counts and resource limits for the language model, OCR, NLP, de-identification, database, terminology, and Elasticsearch services.
  • Sets the Elasticsearch memory.
  • Updates the pipeline settings in the shared configuration.
  • Saves Spark settings to the application database through the API.

It reads the namespace from your current kubectl context and finds the release, URLs, and credentials in the cluster.

Run it​

Run it from the release folder, so it can find the Helm chart. Point kubectl at the Patient Journey Intelligence namespace first:

sudo kubectl config set-context --current --namespace pji

Preview the settings without changing anything:

sudo bash ./setup_instance.sh 64 --dry-run

Apply them:

sudo bash ./setup_instance.sh 64

Replace 64 with the server's total memory in GB. The script sizes the services to fit on one server, so on a cluster with several servers, use the memory of one server that runs Patient Journey Intelligence, not the total for the cluster.

Arguments​

ArgumentRequiredDescription
<memory-in-GB>YesThe server's total memory in GB, as a whole number. It must be the first argument. Below 16, the script prints a warning and uses its minimum settings.
--dry-runNoPrint the capacity plan and the computed settings. Change nothing.
--skip-infraNoOnly save the Spark settings to the database. Do not change the Kubernetes services.
--skip-sparkNoOnly change the Kubernetes services. Do not save the Spark settings.

You cannot use --skip-infra and --skip-spark together.

Environment variables​

VariableDefaultDescription
KEYCLOAK_ADMIN_USERadminKeycloak administrator user name.
LOCAL_API_PORTa free portLocal port used to reach the API.
LOCAL_KC_PORTa free portLocal port used to reach Keycloak.
LOCAL_ES_PORTa free portLocal port used to reach Elasticsearch.

Keeping the settings after upgrades​

  • Helm chart found: if the release folder has the chart that matches the installed version, the script saves the settings in the installation, so they survive future upgrades. The script prints Chart found.
  • Helm chart not found: the script changes the running services directly and prints Chart file not found. These changes survive restarts, but the next upgrade overwrites them. Run setup_instance.sh again after each upgrade.

If saving the Spark settings fails, run it again with --skip-infra once the API is ready:

sudo bash ./setup_instance.sh 64 --skip-infra

Step 7: Upgrade to a new release​

Use the updater for every new release.

  1. Download and unpack the new release into its own folder, as in Step 2 and Step 3:

    export PJI_VERSION="2.4.0"
    aws s3 cp "s3://dev.johnsnowlabs.com/jsl-omop/artifacts/pj-app-${PJI_VERSION}.tar.gz" ~/
    mkdir -p ~/pj-app-${PJI_VERSION}
    tar -xzf ~/pj-app-${PJI_VERSION}.tar.gz -C ~/pj-app-${PJI_VERSION}
    cd ~/pj-app-${PJI_VERSION}
  2. Run the updater with the same options you used to install, including --k8s:

    sudo ./pj-app-updater.sh \
    --k8s \
    --host pji.example.com \
    --namespace pji \
    --tls \
    --silent

Do not run pj-app-installer.sh again for an upgrade.

The updater does not remember every option

Always pass the same --k8s, --host, --namespace, --release, --tls, --nfs, and --values options you used at install time.

  • If you leave out --k8s, the updater looks for the K3s settings file and cannot reach the cluster.
  • If you leave out --host, the updater removes the host rule, and the application is no longer reachable at that name.
  • If you leave out --tls, the application goes back to HTTP.

Updater Options shows which other options you must repeat.

Updater prompts​

ModeOptionWhat happens
DefaultnoneUses the API keys and administrator password already in the installation.
Interactive--interactive-secretsAsks for API keys and a new Keycloak administrator password. Press Enter to keep the current password.
Silent--silentSkips every prompt.

Update the license​

Copy the new license file into the release folder and pass it to the updater:

sudo ./pj-app-updater.sh \
--k8s \
--host pji.example.com \
--namespace pji \
--tls \
--license-file license.json \
--silent

Common configurations​

These options work the same way with the installer and the updater. Include --k8s every time.

Run the language model on a GPU​

sudo ./pj-app-installer.sh \
--k8s \
--host pji.example.com \
--namespace pji \
--slm-mode gpu \
--slm-gpu-count 1 \
--install-gpu-operator

--install-gpu-operator installs the NVIDIA GPU Operator for GPU workloads. With --k8s, it uses the system container runtime at /run/containerd/containerd.sock.

Larger Elasticsearch​

sudo ./pj-app-installer.sh \
--k8s \
--host pji.example.com \
--namespace pji \
--elasticsearch-profile=medium

Databricks​

To run pipeline jobs on Databricks, export the ECR password before you run the installer or the updater. Use sudo -E so the script can read it:

export ECR_PASSWORD=$(aws ecr get-login-password --region us-east-1)
sudo -E ./pj-app-installer.sh --k8s --host pji.example.com --namespace pji

The script then uses the Databricks de-identification, NLP pipeline, jobs, and custom NLP images from the ECR registry that match the release version.

Export ECR_PASSWORD before every update too:

export ECR_PASSWORD=$(aws ecr get-login-password --region us-east-1)
sudo -E ./pj-app-updater.sh --k8s --host pji.example.com --namespace pji --tls --silent

Optional services​

ServiceOption
Kibana--enable-kibana
NAACCR Edits60 API and NAACCR XML Service--enable-naaccr-services
Data Curation Service--enable-dcs --dcs-tag <tag>
Turn off the CPG Agent--disable-cpg-agent

Air-gapped installs​

Use --airgap when the servers have no internet access. In this mode the scripts do not download images. They check that every image is already on the server and stop if one is missing.

Step 1 downloads Kubernetes, Calico, the storage provisioner, the ingress controller, and Helm from the internet. On servers without internet access, build the cluster and load those images before you start.

  1. Load every image the release needs into the container runtime's k8s.io namespace, on every server that will run Patient Journey Intelligence. For example, import an image archive with sudo ctr -n k8s.io images import <archive>.tar. Check the loaded images with:

    sudo crictl images
  2. If the cluster does not already have the Elasticsearch operator, place eck-operator-3.4.0.tgz in the release folder, or set ECK_OPERATOR_CHART to its path. The image registry.johnsnowlabs.com/pj-app/eck-operator:3.4.0 must also be loaded.

  3. Install. Set CONTAINERD_NAMESPACE=k8s.io so the installer's image check looks where Kubernetes keeps its images:

    sudo CONTAINERD_NAMESPACE=k8s.io ./pj-app-installer.sh \
    --k8s \
    --airgap \
    --host pji.example.com \
    --namespace pji \
    --license-file license.json
  4. Use --airgap and CONTAINERD_NAMESPACE=k8s.io on every update too.


More than one server​

You can start on one server and add more later.

Add a server​

  1. Prepare the new server as in Step 1a, with the same Kubernetes version and the NFS client.

  2. On the first server, create a join command. The one printed by kubeadm init expires after 24 hours:

    sudo kubeadm token create --print-join-command
  3. Run the printed command on the new server. If the new server's name has capital letters, add --node-name with a lower-case name:

    sudo kubeadm join <first-server-address>:6443 --token <token> \
    --discovery-token-ca-cert-hash sha256:<hash> \
    --node-name <lower-case-name>
  4. Download the busybox image on the new server, as in Step 1e.

  5. On the first server, check that the new server joined and shows Ready:

    sudo kubectl get nodes -o wide

Services placed on a new server download their own images the first time they start there, so their first start is slower. This is expected.

Shared storage for the model cache​

Once Patient Journey Intelligence runs on more than one server, the model cache must be on shared storage. The simplest path is to install with --nfs from the start, as in install across several servers.

Switching an existing installation needs planning

If you installed without --nfs, the model cache is on a volume that only one server can use. Kubernetes cannot change the storage type of an existing volume, so the switch means deleting that volume and creating it again on shared storage. The cached models then download again the first time they are needed.

The updater does not delete the old volume for you. If the volume still exists when you run the updater with --nfs, the upgrade stops with a spec is immutable error. Plan the switch for a maintenance window, and contact John Snow Labs before you start.


Troubleshooting​

Message or problemWhat to do
ERROR: --k8s cannot read kubeconfigThe script cannot read /etc/kubernetes/admin.conf. Run it with sudo. If your settings file is somewhere else, pass its path with sudo KUBECONFIG=/path/to/config ./pj-app-installer.sh --k8s ....
ERROR: --k8s cannot reach the clusterThe settings file was found, but the cluster did not answer. Check that sudo kubectl get nodes works.
ERROR: no default StorageClassStorage is not set up. See Step 1e.
ERROR: no schedulable nodeNo server accepts applications. On a single server, see Step 1d. On several servers, add one as in Add A Server.
ERROR: no 'nginx' IngressClassThe NGINX ingress controller is not installed. See Step 1e.
ERROR: crictl cannot reach a container runtimecrictl is missing or not configured. Set it up as in Step 1a, or pass --skip-image-pull.
ERROR: --k8s is for self-managed clusters and cannot be combined with...Remove --eks, --gke, --aks, or --openshift.
ERROR: PVC 'pj-app-pipeline-cache' ... already exists on StorageClassThe model cache volume was created without --nfs. See Shared Storage For The Model Cache.
The connection to the server localhost:8080 was refusedroot has no copy of the cluster settings file. Run the root commands in Step 1b.
Every service stays PendingNo server accepts applications. See Step 1d.
One storage request stays Pending and services waitThe busybox image could not be downloaded. See Step 1e.
Services on one server fail to start with an NFS mount errorThat server is missing the NFS client. Run sudo apt-get install -y nfs-common on it.
Services stop and restart during the installThe server ran out of disk space while downloading images. Free up space and install again.
ERROR: TLS requires an FQDNPass a DNS name, not an IP address, with --host when you use --tls.
ERROR: --tls requires --host <FQDN>Add --host with your DNS name.
ERROR: --enable-dcs requires --dcs-tagAdd --dcs-tag <tag>.
Missing chart: place pj-app-<version>.tgz in this directory.Run the script from the unpacked release folder.
Missing or non-executable binaryRun chmod +x jsl_create_secret jsl_docker_pull.
Error: Could not read APP_URL from ConfigMap from setup_instance.shYour kubectl context points at the wrong namespace. Run sudo kubectl config set-context --current --namespace pji.
Application unreachable after an updateYou left out --host, --tls, or --k8s. Run the updater again with your original options.

Frequently asked questions​

It points the scripts at the Kubernetes settings file, /etc/kubernetes/admin.conf, instead of the K3s one, and sends web traffic through the NGINX ingress class instead of Traefik. With --install-gpu-operator, it also points the GPU Operator at the system container runtime. Do not combine it with --eks, --gke, --aks, or --openshift.

K3s suits evaluations, pilots, and production on a single Linux server, either a VM or bare metal. A self-managed Kubernetes cluster built with kubeadm suits organizations that standardize on upstream Kubernetes and installations that span several servers. Both use the same release package.

A server with 16 vCPU and 64 GiB of memory runs the ingestion services, which is the minimum; 32 vCPU and 128 GiB runs all services. These sizes are for one server, so on a cluster with several servers plan for at least that much capacity in total. You also need 80 GB free on /var/lib, and no script checks this for you.

Yes. Kubernetes does not start while swap is on, and Step 1 turns it off as part of preparing every server.

Install with --nfs from the start. Once the platform runs on more than one server, the model cache must be on shared storage, and installing with --nfs from the beginning is the simplest path.

It needs planning. If you installed without --nfs, the model cache sits on a volume only one server can use, and Kubernetes cannot change the storage type of an existing volume. The switch means deleting that volume and creating it again on shared storage, after which the cached models download again. The updater does not delete the old volume, and the upgrade stops with a spec is immutable error if it still exists. Plan the switch for a maintenance window and contact John Snow Labs first.

Remove Patient Journey Intelligence​

This deletes data

These commands permanently delete the application and its data.

Remove the application only:

sudo kubectl delete namespace pji

Remove Kubernetes from a server. Run this on every server in the cluster:

sudo kubeadm reset -f
sudo rm -rf /etc/cni/net.d /root/.kube ~/.kube

kubeadm reset does not delete data under /opt/local-path-provisioner. Delete that folder too if you want to remove it.


Installer and updater options​