Blog

Introducing the Thinkwise Container Registry


You might have noticed, there is a new menu item in TCP called Thinkwise Containers. Through this menu item it is possible to request login tokens required for downloading Thinkwise container images from the Thinkwise Container Registry. These container images can be used to deploy the Universal GUI and the Indicium Application Tier of the Thinkwise Platform in a new and easy way.

But what exactly are containers and container images, how do you use them, and what are the benefits of using container images?
 

What Are Container Images

A container image is an application together with all the required dependencies wrapped together into a single image, which basically is a compressed archive. The application that runs from a container image is called a container.

Compared to a virtual machine, a container only contains the required dependencies to run the application or service and is not bloated with other files. This makes containers very small in size and boot up almost instantly. This results in very quick deployments or updates, by simply replacing the old container.

Source: Docker Inc.

For more information, Docker Inc. made a blogpost about what a container is and what the benefits are: https://www.docker.com/resources/what-container/
 

The Thinkwise Container Registry

Container images for the Thinkwise Platform are published in the Thinkwise Container Registry, which is available at registry.thinkwisesoftware.com.

The registry contains images for the Indicium Application Tier and the Universal GUI. Soon we will move the Thinkwise AutoML container image to this registry as well.

Image name Location
Indicium Application Tier registry.thinkwisesoftware.com/public/indicium
Universal GUI registry.thinkwisesoftware.com/public/universal

Different versions of each image are available, which are indicated by so-called tags.

Tag Description
latest The most recent image
2022, 2023 The most recent image of the specified year
2022.2, 2023.1 The most recent image of the specified release
2022.2.17, 2023.1.10 Specific version

The container images are tagged with the same version number as the releases in TCP. Any future releases will be tagged accordingly.

In production environments, you would want to pin a specific version number, to prevent unwanted changes from happening during the day and have more control over when a new version is made available.

 

Features of the Container Images

Indicium Container Image

The container can be configured with both environment variables and secrets to offer easy upgrades between versions without the need to manually edit the appsettings.json file. 

For example, the database host can be configured with the SQL_SERVER variable and the database with the SQL_DATABASE variable.

For sensitive values, you can use secrets. For example, the username and password to connect with the database can be passed to the container with a secret.

By default, the container will look for secrets with the names SQL_USERNAME, SQL_PASSWORD and REDIS_CONNECTION_STRING (both in upper- and lowercase).

 

Universal

The container can be configured with environment variables to offer easy upgrades between versions without the need to manually edit the config.json file.

For example, the Meta server URL for Universal can be set to localhost by running the container with the parameter -e SERVICE_URL=https://localhost/iam/iam.

 

Tokens in TCP

Before you can pull (or download) container images from the Thinkwise Container Registry, you need to authenticate to the registry with valid credentials. These can be obtained in TCP.

 

Requesting a Token

A token can be requested by clicking 'Request token' in the menu 'Thinkwise Containers', make sure to give it a clear description. When you click on EXECUTE, TCP will show a pop-up containing the credentials to log in.

If you plan to follow the examples in this blogpost, request a token now and note down these credentials.

A generated token is visible for the employees of your company, so colleagues can revoke your token if needed. Keep this in mind when you give the token a description. It is always possible to edit the description of a token.

Some examples you could use the credentials for are:

  • Locally on the machine, like Docker Desktop
  • In the cloud on Azure for App Services or Container Instances
  • A Kubernetes cluster on the local machine, hosted on-premises or in the cloud

It is not recommended to use the same credentials at multiple places. A best practice is to generate new credentials with a clear description for each machine or cloud instance you want to use. But to keep things simpler in this blogpost, we will reuse the previously generated credentials for a few examples.

 

Revoking the Token

When you no longer require the token, or it is compromised, it can be revoked by pressing the button 'Revoke token'. This will delete the token, the machine using the credentials of this token will no longer be able to authenticate.

If you plan to follow the examples, do not revoke the token yet.

If a token is deleted on accident, a new token can always be requested. Make sure to update the credentials on the machine the token is used for.

 

Platform Deployment Examples

These examples will guide you through two deployment examples. A local deployment with Docker Desktop and a cloud deployment on Microsoft Azure with Kubernetes.


Prerequisites

  • A database server containing an Intelligent Application Manager. This can be in the cloud or local, as long as it can be accessed from the cloud and/or local and have credentials to log in to the databases. For more information on how to install this, follow the guided platform deployment instructions for Install an Intelligent Application Manager. A complete guide on how to install the Intelligent Application Manager and the Software Factory is out of scope for this blogpost.

If the database server is hosted in the cloud, make sure to create exceptions for your IP address and/or cloud related services.

Recommendations


Locally with Docker

Docker provides a way to run containers locally on a machine, whether you are on Windows, macOS, or a Linux distribution.

These instructions will set up a local platform deployment using the Indicium Application Tier and the Universal GUI. To route the inbound traffic to the correct applications, a service with the container image traefik is used. Traefik uses the labels applied to the services to route traffic to the correct service, in this case it is path based. Traefik will also redirect any traffic from http to https with a self-signed certificate.


Prerequisites


Logging in to the registry

Before Docker can pull the images, it needs to log in to the Thinkwise Container Registry. This can be done with the following command:

docker login registry.thinkwisesoftware.com

It will ask for a username and password, paste the username and secret in here like the example below:

$ docker login registry.thinkwisesoftware.com
Username: robot-04fd64a4-f3d4-4a2e-ae9a-372a4976c7d3
Password:
Login Succeeded

Make sure to enter the Username and Password from your own requested token.


Steps

  1. Create a directory with the name Thinkwise-Platform-Containers
  2. Open this directory in the preferred IDE
  3. Create a file with the name docker-compose.yaml and add the following:
    ---
    version: "3.8"
    services:
    proxy:
    image: traefik
    ports:
    - 80:80
    - 443:443
    command:
    - "--providers.docker=true"
    - "--providers.docker.exposedbydefault=false"
    - "--entrypoints.web.address=:80"
    - "--entrypoints.websecure.address=:443"
    - "--entrypoints.web.http.redirections.entrypoint.to=websecure"
    - "--entrypoints.web.http.redirections.entrypoint.scheme=https"
    volumes:
    - "/var/run/docker.sock:/var/run/docker.sock"

    universal:
    image: registry.thinkwisesoftware.com/public/universal:${TAG}
    environment:
    - SERVICE_URL=https://localhost/indicium/iam/iam
    labels:
    - "traefik.enable=true"
    - "traefik.http.routers.frontend.rule=Host(`localhost`)"
    - "traefik.http.routers.frontend.tls=true"

    indicium:
    image: registry.thinkwisesoftware.com/public/indicium:${TAG}
    environment:
    - SQL_SERVER=${SQL_SERVER}
    - SQL_DATABASE=${SQL_DATABASE}
    - SQL_USERNAME=${SQL_USERNAME}
    - SQL_PASSWORD=${SQL_PASSWORD}
    - ENABLE_REVERSE_PROXY=true
    - ALLOWED_HEADERS=All
    - TRUSTED_NETWORKS=0.0.0.0/0
    - EXTERNAL_PATH_BASE=/indicium
    labels:
    - "traefik.enable=true"
    - "traefik.http.routers.backend.rule=(Host(`localhost`) && PathPrefix(`/indicium`))"
    - "traefik.http.middlewares.backend.stripprefix.prefixes=/indicium"
    - "traefik.http.routers.backend.middlewares=backend"
    - "traefik.http.routers.backend.tls=true"
  1. Create a file with the name .env (the dot in front of the file is important), with the following, and enter the correct values of your database server:
    TAG=2022.2.17

    # Hostname of the database server
    SQL_SERVER=

    # Database to use
    SQL_DATABASE=IAM

    # Username to access the database,
    SQL_USERNAME=

    # Password of username to access the database
    SQL_PASSWORD=
  1. Save the file
  2. Type the command docker compose up -d (remove -d to put the command in the foreground) and wait for the container images to be pulled and all the defined services to be started. Docker will load the variables from the .env file. In this case, the tag 2022.2.17 is specified

In Visual Studio Code, the integrated terminal can be toggled by pressing Ctrl + ` (backtick on the same key as ~).

Within a minute, the Universal GUI and Indicium Application Tier should be available on https://localhost and https://localhost/indicium with a self-signed certificate. This self-signed certificate can be trusted. The same credentials used for the database server in the .env file can be used to log in to the Universal GUI.


Applying an update

These steps will update the Indicium Application Tier and Universal GUI from version 2022.2.17 to 2023.1.10:

  1. Open the .env file again in the preferred editor
  2. Modify value of TAG from 2022.2.17 to 2023.1.10
  3. Save the file
  4. Execute the command docker compose up -d

A new version of the Universal GUI and Indicium Application Tier are now accessible on localhost.


Clean up

To clean up the Docker environment:

  1. Stop and remove the created containers and Docker network:
    docker compose down
  1. Remove the example credentials from Docker:
    docker logout registry.thinkwisesoftware.com

Do not forget to revoke the created token in TCP if you have not done so yet and are not planning to follow another example from this blogpost.


In an Azure Kubernetes Service Cluster

For production or in larger scale environments it is also possible to run containers through a container orchestrator tool like Kubernetes.

These instructions will set up a platform deployment in a Kubernetes Cluster on Azure using the Universal GUI and Indicium Application Tier. To route the inbound traffic to the correct services, an Ingress Controller from NGINX is used. NGINX will redirect all traffic from http to https and uses a self-signed certificate. For Indicium to authenticate to the IAM database, a secret will be created which is exposed inside the container of Indicium. Both deployments contain two health checks for starting and availability.

Note that a service in Kubernetes is not the same as a service in Docker.


Prerequisites

  • An Azure Subscription
  • A Storage Account for the Azure Cloud Shell

The used subscription will be charged by Microsoft for using an Azure Kubernetes Service as long as the cluster runs. For more information about costs, see Azure Calculator.


Steps

  1. Download Thinkwise-Platform-K8s.zip
  2. Open the Azure Cloud Shell in a new tab, make sure you select the Bash shell in the prompt or top left of the page.
  3. Create a new Resource Group with an Azure Kubernetes Service cluster and retrieve the credentials to use the Kubernetes CLI in the Azure Cloud Shell:
    RG_NAME=thinkwise-k8s-demo
    AKS_NAME=$(whoami)-aks
    LOCATION=westeurope

    az group create \
    --location $LOCATION \
    --resource-group $RG_NAME

    az aks create \
    --resource-group $RG_NAME \
    --name $AKS_NAME \
    --generate-ssh-keys

    az aks get-credentials \
    --resource-group $RG_NAME \
    --name $AKS_NAME
  4. Install a basic NGINX Ingress Controller to the Kubernetes cluster with Helm:
    INGRESS_NAMESPACE=ingress-basic

    helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
    helm repo update

    helm install ingress-nginx ingress-nginx/ingress-nginx \
    --create-namespace \
    --namespace $INGRESS_NAMESPACE \
    --set controller.service.annotations."service\.beta\.kubernetes\.io/azure-load-balancer-health-probe-request-path"=/healthz
  1. Create a new namespace to use for the Thinkwise Platform and set it as the active namespace:
    TWP_NAMESPACE=twp-example
    kubectl create namespace $TWP_NAMESPACE
    kubectl config set-context --current --namespace $TWP_NAMESPACE

    A namespace in Kubernetes is similar to a resource group in Azure.

  1. Upload the zip to the Cloud Shell with the Upload-button:
  1. Extract the zip and open the folder in a web editor on the Cloud Shell:
    unzip Thinkwise-Platform-K8s.zip
    cd Thinkwise-Platform-K8s
    code .
  1. Filter out the public IP address of the NGINX Ingress Controller and add it to the deployment and ingress manifests:
    PUBLIC_IP=$(kubectl get service --namespace $INGRESS_NAMESPACE ingress-nginx-controller -o jsonpath={.status.loadBalancer.ingress[].ip})

    sed -i "s/\[public-ip\]/$PUBLIC_IP/g" deployments/* ingress/*
  1. Open the file indicium-configuration.yaml in the web editor, modify the value of SQL_SERVER to match the hostname or IP address of the prepared database server that contains the IAM database, then save the file with Ctrl+S

  2. Create a secret for Indicium to connect with the database server:

    read -rp "Enter the database username: " DB_USERNAME
    read -srp "Enter the database password: " DB_PASSWORD && echo -e "\n"

    kubectl create secret generic database-credentials \
    --from-literal=SQL_USERNAME="$DB_USERNAME" \
    --from-literal=SQL_PASSWORD="$DB_PASSWORD"

    Secrets are a way to securely store values in Kubernetes

  1. Create a secret in the current namespace to pull the container images with the name thinkwise-registry:
    read -rp "Enter the username: " REGISTRY_USERNAME
    read -srp "Enter the secret: " REGISTRY_SECRET && echo -e "\n"

    kubectl create secret docker-registry thinkwise-registry \
    --docker-server registry.thinkwisesoftware.com \
    --docker-username "$REGISTRY_USERNAME" \
    --docker-password "$REGISTRY_SECRET"
  1. Apply the configuration in the current and subdirectories to the Kubernetes cluster, then print the address to the Universal GUI:
    kubectl apply -R -f .
    kubectl wait deployment/indicium --for=condition=Available
    printf "\nThe Universal GUI is now available on:\\nhttps://$PUBLIC_IP.nip.io/\n\n"

The browser will warn you about a self-signed certificate, this can be ignored. Use the same credentials used for the database to log in to the Universal GUI.


Applying an update

These steps will update the Universal GUI and Indicium Application Tier from version 2022.2.17 to 2023.1.10 on the Azure Kubernetes Service cluster

  1. In the deployments directory, modify the tag of the container image from 2022.2.17 to 2023.1.10  of both files and save the changes with Ctrl+S
  2. Apply the new configuration to the cluster by executing the following command in the root of the Thinkwise-K8s-Platform directory:
    kubectl apply -R -f .
    kubectl rollout status deployments

In around 30 seconds, a new version of the Universal GUI and Indicium Application Tier are now accessible on the same address as before (you might need to refresh the page first).


Clean up

To clean up the Azure Kubernetes environment:

  1. Delete the namespace containing the secrets, deployments, and configuration from Kubernetes:
    TWP_NAMESPACE=twp-example
    kubectl delete namespace $TWP_NAMESPACE
  1. Delete the resource group in Azure containing the cluster
    RG_NAME=thinkwise-k8s-demo
    AKS_NAME=$(whoami)-aks
    LOCATION=westeurope
    az group delete --resource-group $RG_NAME

Do not forget to revoke the created token in TCP if you have not done so yet and are not planning on following another example from this blogpost.

 

Wrapping up

Container images are a great solution for developers and system administrators. The full configuration on how to run each image can be defined in code, which provides a simple and easy way to deploy and upgrade components of the Thinkwise Platform. In addition, this process can be adopted into Continuous Delivery to automate the upgrades of the Indicium Application Tier and the Universal GUI.

Disable checkingPremium suggestionsDisable checkingPremium suggestionsDisable checkingPremium suggestions

11 replies

Userlevel 5
Badge +5

Very nice development or should I say deployment 🙂? Thinkwise running in Docker in a few clicks.

Userlevel 5

I really love this! I am running some home automation dockers at home and I really love how easy the update process is with this, great addition!

Userlevel 6
Badge +10

@Leon Kroon Great write-up! We got it working very easily on one of our Developers’ laptop.

What I am curious about though is the exact use cases you intend to solve with this Container Registry. Could you clarify this a bit more?

We currently do quite some manipulations to the Indicium and Universal GUI files before we can deploy those to our environments, and as far as I understand the below changes cannot be done when using this Container Registry. 

Indicium

  • web.config
  • appsettings.json

Universal GUI

  • config.json
  • manifest.json
  • index.html
  • add multiple logo files
  • add custom.css
  • delete custom.sample.css

Instead of this Thinkwise Container Registry endpoint we would benefit much more if an endpoint is provided from which we can download the Indicium and Universal GUI ZIP package, which we can then manipulate with our current automation solution to make them customer-specific. When can we expect such an endpoint? See below Idea:

 

Hi @Arie V, the configuration for both Indicium and the Universal GUI can be fully customized by providing environment variables to the container, with support to pass custom JSON to the container as configuration. This gives the ability to modify the configuration of the container depending on the environment it runs in, without customizing it before deploying it.

In addition to this, it is also possible to simply mount the configuration file (appsettings.json and config.json) inside the container, but I think this approach makes the containers less flexible.

As for the custom CSS and files for the Universal GUI, these can be mounted inside the container as well.

It is not possible to modify the web.config of Indicium inside the container yet. Are these settings still mandatory inside a container, and if so, could these settings be provided with environment variables too?

Full documentation for the container images will be published soon.

 

One of the things I intend to solve with this registry is cloud independent deployments of the Thinkwise Platform with easy upgrades.

Userlevel 6
Badge +10

@Leon Kroon Thanks for the clarifications! Good to know that more configuration is possible.

Userlevel 5
Badge +16

@Leon Kroon  this also works with AWS Amazon Elastic Container Registry? 

 

Hi @Freddy, if AWS ECR offers image replication, it should be able to work. However, I have not tested this.

 

Userlevel 5
Badge +16

Hi @Leon Kroon ,

We've decided to host our own cloud environment and I'm trying to use the container registry. Any idea what can be the issue here?  Successfully logged in, pulled the image.. it says it has read the appsettings.json file..  but it cannot connect.. however if I run dotnet indicium.dll in the shell it connects. 

 

 

Userlevel 5
Badge +16

Hi @Leon Kroon ,

We've decided to host our own cloud environment and I'm trying to use the container registry. Any idea what can be the issue here?  Successfully logged in, pulled the image.. it says it has read the appsettings.json file..  but it cannot connect.. however if I run dotnet indicium.dll in the shell it connects. 

 

 

I found it, for some reason, when you run Indicium in a container it cannot access the SQL server via Localhost.. the external IP I also use to connect via SMSS does work. 

 

​ 

Hi @Leon Kroon ,

We've decided to host our own cloud environment and I'm trying to use the container registry. Any idea what can be the issue here?  Successfully logged in, pulled the image.. it says it has read the appsettings.json file..  but it cannot connect.. however if I run dotnet indicium.dll in the shell it connects.

 

I found it, for some reason, when you run Indicium in a container it cannot access the SQL server via Localhost.. the external IP I also use to connect via SMSS does work. 

 


Hi @Freddy that is correct, when referring to Localhost, the Indicium container will try to connect with itself on port 1433. Each container you run has its own internal IP address. To connect to a database, you will need to specify the correct hostname or IP address. If these containers are run on Docker, the internal DNS of Docker could be used for this to avoid hardcoded IP addresses (in case of connecting from one container to another).

For example, a service defined with the name “container1” in a Docker Compose file can be resolved (and pinged) by all other services with containers defined in this same Docker Compose file, because Docker Compose automatically puts all defined services in the file into the same custom network.
The same can be achieved by manually putting all created containers into the same custom Docker network.

Userlevel 5
Badge +16

​ 

Hi @Leon Kroon ,

We've decided to host our own cloud environment and I'm trying to use the container registry. Any idea what can be the issue here?  Successfully logged in, pulled the image.. it says it has read the appsettings.json file..  but it cannot connect.. however if I run dotnet indicium.dll in the shell it connects.

 

I found it, for some reason, when you run Indicium in a container it cannot access the SQL server via Localhost.. the external IP I also use to connect via SMSS does work. 

 


Hi @Freddy that is correct, when referring to Localhost, the Indicium container will try to connect with itself on port 1433. Each container you run has its own internal IP address. To connect to a database, you will need to specify the correct hostname or IP address. If these containers are run on Docker, the internal DNS of Docker could be used for this to avoid hardcoded IP addresses (in case of connecting from one container to another).

For example, a service defined with the name “container1” in a Docker Compose file can be resolved (and pinged) by all other services with containers defined in this same Docker Compose file, because Docker Compose automatically puts all defined services in the file into the same custom network.
The same can be achieved by manually putting all created containers into the same custom Docker network.

It's a brave new world for me, but all seems very powerful. As we use RedHat I've setup containerized environments with Podman and Pods. The pods really did the trick, as everything within the pod can communicate directly with each-other. 

 

Reply