Deploy x86 Microservices on Power with OpenShift

Hands-on Tutorial

Table of Contents

  1. Introduction
    1. The Sock Shop
    2. The Hands-on Lab
  2. Getting Started
    1. Assumptions
    2. Connect to the Bastion CLI
    3. Login to OpenShift
    4. Copy login command for CLI use
  3. Understand the Go Build Process
    1. Clone from GitHub
    2. Analyzing the Go build process
  4. Build and Push Go Container to the Internal Registry
    1. Building the Go Container
    2. Push the Go image to the internal registry
  5. Building a .NET Service from a UBI image
    1. Clone the .NET application from GitHub
    2. Understanding the NET build process
    3. Creating the Dockerfile to build the microservice container
  6. Build and Push .NET Container to the Registry
    1. Building the .NET Container
    2. Push the .NET images to the internal registry
  7. Deploy the Microservice Application
  8. Access the Application on OpenShift
  9. Lab Wrap up
  10. OpenShift Multi-Arch Compute
    1. YouTube
      1.  Introduction and Early Adoption Program Replay
      2. UKI Brunch & Learn Replay
      3. Deploy the Sock Shop Demonstration
      4. Migrating-Workloads-between-x86-&-IBM-Power-Worker-Nodes
    2. IBM Redbook
    3. Power Modernisation Website
    4. Multi-Arch Compute Assistance Compute

1 Introduction

In this lab, you will take an Open-Source E-Commerce solution written for x86 around eight years ago and make it run on Power with OpenShift, using Java, Node.js, Go and .NET microservices.

1.1 The Sock Shop

The Sock Shop is a sample microservices application created by Weaveworks that Luke Marsden presented at Code Mesh 2016. The complete recording of the presentation is available for replay below.

https://youtu.be/zzSElP8pQUA

Weaveworks created the Sock Shop to address a gap in microservices demonstrations. While there was much discussion about microservices back then, most demos were overly simplistic examples (like a basic counter or guestbook application) that didn’t reflect real-world complexity. Weaveworks wanted to create a more realistic reference application to demonstrate microservices’ best practices.

The Sock Shop is an e-commerce application for selling socks. While visually simple, its backend architecture demonstrates actual microservices patterns:

  • Multiple independent services that can be maintained, upgraded and deployed separately
  • Services written in different programming languages (including Go, Node.js, Java Spring and .NET)
  • Each stateful service has its independent database to avoid schema coupling
  • REST APIs for service communication
  • Components include:
    •   Frontend UI
    •   Catalog service
    •   Cart service
    •   User/account service
    •   Order service
    •   Multiple databases

The application was designed to be cross-platform compatible, with implementations for multiple container orchestration platforms including Docker Compose, Kubernetes, DC/OS, and Amazon ECS. This made it valuable as a testing ground for different orchestration technologies and deployment patterns.

The Sock Shop served as both a learning tool for microservices best practices (and mistakes) and as a realistic test application for container orchestration platforms and related tooling.

1.2 The Hands-on Lab

The Sock Shop application was written around eight years ago, way before the components and OpenShift was available and supported on IBM Power. Today, you will modify two of the x86 microservice components to work on IBM Power ppc64le with Red Hat OpenShift. Of course, all the other components have also been modified to work on IBM Power, but due to the time available to us today, we will concentrate on the user (Go) and orders (.NET) microservices and deploy the complete application with already updated, prebuilt images. You are welcome to work through the other microservices outside of this class.

The first part of the lab will focus on getting familiar with using the lab environment:

The first part of the lab will focus on getting familiar with using the lab environment:

  • Connecting to the SSH command line interface for the bastion
  • Connecting to the OpenShift Console via Web Browser
  • Loging into OpenShift via the command line interface

The second part of the lab will focus on:

  • Cloning and analyzing the user microservice created using Go for x86

The third part of the lab will focus on:

  • Building the Go microservice on Power and pushing to the OpenShift Internal Registry

The fourth part of the lab will focus on:

  • Cloning and analyzing the orders microservice created with .NET for x86
  • Creating a Dockerfile to build the .NET orders microservice on Power

The fifth part of the lab will focus on:

  • Building the .NET microservice on Power and pushing to the OpenShift Internal Registry

The sixth part of the lab will focus on:

  • Deploying the microservice solution on Power with OpenShift

The seventh part of the lab will focus on:

  • Accessing the solution

The eighth part of the lab will focus on:

  • Wrapping up the hands-on lab exercise

The final part of the lab guide provides additional information about the new Red Hat OpenShift Multi-Arch Compute functionality.

Please Note:

Commands that you should execute are displayed in bold blue txt. Left click within this area to copy the command to you clipboard.
Example output from commands that have been executed are displayed with white txt with black background.
  • Bullet items are required actions

Standard paragraphs are for informational purposes.

A walk through of this hands-on lab, alongside instructions can be viewed below. I will add commentary as soon as I have time.

2 Getting Started

This part of the demo will guide you through process of cloning the code hosted on GitHub, inspecting the files to understand how the code is organized to build container images. This part of the demo will be done using the root user. Rootless containers are possible but more effort than expected for this demo would be needed so we will run the demos as root. On a production environment this would not be done using root, but the process is similar.

You can watch this lab on an unlisted YouTube playlist below

2.1 Assumptions

  1. You have access to an OpenShift Container Platform (OCP) environment
    • Make a note of the OCP Console URL
  2. You have access to a RHEL based Bastion LPAR
    • Make a note of the Bastion IP address
  3. The OCP user that you use is “cecuser”, if not, then just substitute “cecuser” for your own OCP user name
    • Make a note the OCP user name, if not using “cecuser”
    • Make a note of the “cecuser” password

2.2 Connect to the Bastion CLI

Now we will go over the steps to connect to the CLI for the environment.

I typically use the Putty application, but you are free to use your favourite terminal.

Putty is available for download HERE

  • Install Putty from the above link if desired.
  • Open the Putty application.
  • Fill the hostname with the IP address of your Bastion. This may be found in your assigned Project Kit if using IBM TechZone resources.
  • Press Open
  • Click Accept
  • You will see a “login as:” prompt, type cecuser and press enter:
  • Enter the password for the “cecuser” user. This will be the same for both the CLI and for the GUI.

2.3 Login to OpenShift

To login to the OpenShift environment from the command line, find the oc login command from your OpenShift GUI.

  • Point your browser to your OpenShift web console
  • Accept the certificate warning if certificates have not been configured correctly on demo equipment.
  • Click on the htpasswd option:
  • Add your user and password contained on the step 1 and Click login.
  • Familiarize yourself with the navigation for approximately 10 minutes if it’s your first time. You can easily switch between Developer and Administrator views using the menu option located at the top left corner.

2.4 Copy login command for CLI use

If you need to login again to the CLI, for any reason, you can find the login command on main OpenShift web console page.

  • On the top right side, you will see the cecuser drop down click on it and then on “Copy login command”
  • Once again click on the htpasswd option:
  • Add your user and password contained on the step 1 and Click login.

Click on Display Token on the top left

  • You can use the oc login command whenever your Authorization is expired. You may need to use the API token for login in into the registry.
  • As cecuser, copy and paste the oc login command from the web page into your Putty Session.

3 Understand the Go Build Process

This hands-on lab is intended for a Power Infrastructure person, so you just need to understand the following at a reasonably high level to give Developers guidance on how to develop for multiplatform.

Most of the time all that is needed is to find the right image that will be used to compile the code and to build the container.

You can view a recording of the lab exercise HERE

3.1 Clone from GitHub

  • We will be running this demo using the root user. To get root access use the command as follows:
sudo su -
Last login: Wed Oct 30 09:57:14 EDT 2024 from 129.40.95.89 on pts/2
  • Check you have the correct user. The whoami command should return root.
whoami
root
  • Install Git
dnf install git -y
Updating Subscription Management repositories.
Red Hat Enterprise Linux 8 for Power, little en  19 kB/s | 3.8 kB     00:00
Red Hat CodeReady Linux Builder for RHEL 8 Powe  63 kB/s | 4.5 kB     00:00
Red Hat Enterprise Linux 8 for Power, little en  22 kB/s | 4.1 kB     00:00
Red Hat Enterprise Linux 8 for Power, little en  49 kB/s | 4.5 kB     00:00
Red Hat Enterprise Linux 8 for Power, little en  78 MB/s |  58 MB     00:00
Dependencies resolved.
================================================================================
 Package       Arch    Version          Repository                         Size
================================================================================
Installing:
 git           ppc64le 2.43.5-1.el8_10  rhel-8-for-ppc64le-appstream-rpms  92 k
Installing dependencies:
 emacs-filesystem
               noarch  1:26.1-12.el8_10 rhel-8-for-ppc64le-baseos-rpms     70 k
 git-core      ppc64le 2.43.5-1.el8_10  rhel-8-for-ppc64le-appstream-rpms  13 M
 git-core-doc  noarch  2.43.5-1.el8_10  rhel-8-for-ppc64le-appstream-rpms 3.1 M
 perl-Error    noarch  1:0.17025-2.el8  rhel-8-for-ppc64le-appstream-rpms  46 k
 perl-Git      noarch  2.43.5-1.el8_10  rhel-8-for-ppc64le-appstream-rpms  79 k
 perl-TermReadKey
               ppc64le 2.37-7.el8       rhel-8-for-ppc64le-appstream-rpms  42 k

Transaction Summary
================================================================================
Install  7 Packages

Total download size: 16 M
Installed size: 55 M
Downloading Packages:
(1/7): perl-Error-0.17025-2.el8.noarch.rpm      344 kB/s |  46 kB     00:00
(2/7): perl-TermReadKey-2.37-7.el8.ppc64le.rpm  257 kB/s |  42 kB     00:00
(3/7): emacs-filesystem-26.1-12.el8_10.noarch.r 394 kB/s |  70 kB     00:00
(4/7): git-2.43.5-1.el8_10.ppc64le.rpm          1.5 MB/s |  92 kB     00:00
(5/7): perl-Git-2.43.5-1.el8_10.noarch.rpm      1.0 MB/s |  79 kB     00:00
(6/7): git-core-doc-2.43.5-1.el8_10.noarch.rpm   23 MB/s | 3.1 MB     00:00
(7/7): git-core-2.43.5-1.el8_10.ppc64le.rpm      52 MB/s |  13 MB     00:00
--------------------------------------------------------------------------------
Total                                            39 MB/s |  16 MB     00:00
Running transaction check
Transaction check succeeded.
Running transaction test
Transaction test succeeded.
Running transaction
  Preparing        :                                                        1/1
  Installing       : git-core-2.43.5-1.el8_10.ppc64le                       1/7
  Installing       : git-core-doc-2.43.5-1.el8_10.noarch                    2/7
  Installing       : perl-Error-1:0.17025-2.el8.noarch                      3/7
  Installing       : perl-TermReadKey-2.37-7.el8.ppc64le                    4/7
  Installing       : emacs-filesystem-1:26.1-12.el8_10.noarch               5/7
  Installing       : perl-Git-2.43.5-1.el8_10.noarch                        6/7
  Installing       : git-2.43.5-1.el8_10.ppc64le                            7/7
  Running scriptlet: git-2.43.5-1.el8_10.ppc64le                            7/7
  Verifying        : emacs-filesystem-1:26.1-12.el8_10.noarch               1/7
  Verifying        : perl-TermReadKey-2.37-7.el8.ppc64le                    2/7
  Verifying        : perl-Error-1:0.17025-2.el8.noarch                      3/7
  Verifying        : git-2.43.5-1.el8_10.ppc64le                            4/7
  Verifying        : git-core-2.43.5-1.el8_10.ppc64le                       5/7
  Verifying        : git-core-doc-2.43.5-1.el8_10.noarch                    6/7
  Verifying        : perl-Git-2.43.5-1.el8_10.noarch                        7/7
Installed products updated.

Installed:
  emacs-filesystem-1:26.1-12.el8_10.noarch  git-2.43.5-1.el8_10.ppc64le
  git-core-2.43.5-1.el8_10.ppc64le          git-core-doc-2.43.5-1.el8_10.noarch
  perl-Error-1:0.17025-2.el8.noarch         perl-Git-2.43.5-1.el8_10.noarch
  perl-TermReadKey-2.37-7.el8.ppc64le

Complete!
[root@p1308-bastion build]# git clone https://github.com/DanielCasali/microservices-demo-user.git
Cloning into 'microservices-demo-user'...
remote: Enumerating objects: 1052, done.
remote: Counting objects: 100% (4/4), done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 1052 (delta 1), reused 3 (delta 1), pack-reused 1048 (from 1)
Receiving objects: 100% (1052/1052), 171.09 KiB | 5.35 MiB/s, done.
Resolving deltas: 100% (595/595), done.
  • Create a build directory.
mkdir build
  • Change to the newly created Directory.
cd build
  • Check you are on the correct Directory.
pwd
/root/build
  • Inside the new directory under the root home you can now clone the user project to your server.
git clone https://github.com/DanielCasali/microservices-demo-user.git
Cloning into 'microservices-demo-user'...
remote: Enumerating objects: 1052, done.
remote: Counting objects: 100% (4/4), done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 1052 (delta 1), reused 3 (delta 1), pack-reused 1048 (from 1)
Receiving objects: 100% (1052/1052), 171.09 KiB | 5.35 MiB/s, done.
Resolving deltas: 100% (595/595), done.
  • You should see a new folder created:
ls -la
drwxr-xr-x.  3 root root   37 Oct 31 13:37 .
dr-xr-x---. 13 root root 4096 Oct 31 13:34 ..
drwxr-xr-x. 10 root root 4096 Oct 31 13:37 microservices-demo-user
  • Change to this directory
cd microservices-demo-user
  • Check the directory
ls
api      docker                     glide.lock  main.go    scripts
apispec  docker-compose.yml         glide.yaml  Makefile   users
db       docker-compose-zipkin.yml  LICENSE     README.md 

You can see some .go files. This normally means you are working with a Golang or simply “Go” application. We will understand how Go applications are built in the next section.

3.2 Analyzing the Go build process

We will analyze the contents of the Directory to make sure we understand how the application is built in container form. The Dockerfile is the main file that holds this information, as it determines the content of the container image and how it is built.

  • Search the directory for the Dockerfile
ls -la
total 56
drwxr-xr-x. 10 root root  4096 Oct  3 16:52 .
drwxr-xr-x.  3 root root    37 Oct  3 16:52 ..
drwxr-xr-x.  2 root root   161 Oct  3 16:52 api
drwxr-xr-x.  2 root root    56 Oct  3 16:52 apispec
drwxr-xr-x.  3 root root    52 Oct  3 16:52 db
drwxr-xr-x.  4 root root    33 Oct  3 16:52 docker
-rw-r--r--.  1 root root   854 Oct  3 16:52 docker-compose.yml
-rw-r--r--.  1 root root  1447 Oct  3 16:52 docker-compose-zipkin.yml
drwxr-xr-x.  8 root root   163 Oct  3 16:52 .git
drwxr-xr-x.  2 root root    86 Oct  3 16:52 .github
-rw-r--r--.  1 root root    27 Oct  3 16:52 .gitignore
-rw-r--r--.  1 root root  4390 Oct  3 16:52 glide.lock
-rw-r--r--.  1 root root   729 Oct  3 16:52 glide.yaml
-rw-r--r--.  1 root root 11357 Oct  3 16:52 LICENSE
-rw-r--r--.  1 root root  3889 Oct  3 16:52 main.go
-rw-r--r--.  1 root root  2437 Oct  3 16:52 Makefile
-rw-r--r--.  1 root root  2383 Oct  3 16:52 README.md
drwxr-xr-x.  2 root root    45 Oct  3 16:52 scripts
-rw-r--r--.  1 root root  1072 Oct  3 16:52 .travis.yml
drwxr-xr-x.  2 root root   141 Oct  3 16:52 users

We don’t see a Dockerfile on the main code directory, but there is a docker directory.

  • Inspect the docker directory
ls -la docker
total 4
drwxr-xr-x.  4 root root   33 Oct  3 16:52 .
drwxr-xr-x. 10 root root 4096 Oct  3 16:52 ..
drwxr-xr-x.  2 root root   32 Oct  3 16:52 user
drwxr-xr-x.  3 root root   39 Oct  3 16:52 user-db 

There are two “User” microserverices, the application is based on Go and the database on MongoDB. We will not work with the DB, but will focus on the user application.

  • Check the user directory inside the docker directory.
ls -la docker/user
total 4
drwxr-xr-x. 2 root root  32 Oct  3 16:52 .
drwxr-xr-x. 4 root root  33 Oct  3 16:52 ..
-rw-r--r--. 1 root root 870 Oct  3 16:52 Dockerfile-release 
  • Check the Dockerfile-release file in the current docker/user directory, do NOT try to understand it now, we will do this step by step during the demo.
cat docker/user/Dockerfile-release
FROM golang:1.7-alpine

COPY . /go/src/github.com/microservices-demo/user/
WORKDIR /go/src/github.com/microservices-demo/user/

RUN apk update
RUN apk add git
RUN go get -v github.com/Masterminds/glide
RUN glide install && CGO_ENABLED=0 go build -a -installsuffix cgo -o /user main.go

FROM alpine:3.4

ENV	SERVICE_USER=myuser \
	SERVICE_UID=10001 \
	SERVICE_GROUP=mygroup \
	SERVICE_GID=10001

RUN	addgroup -g ${SERVICE_GID} ${SERVICE_GROUP} && \
	adduser -g "${SERVICE_NAME} user" -D -H -G ${SERVICE_GROUP} -s /sbin/nologin -u ${SERVICE_UID} ${SERVICE_USER} && \
	apk add --update libcap

ENV HATEAOS user
ENV USER_DATABASE mongodb
ENV MONGO_HOST user-db

WORKDIR /
EXPOSE 80
COPY --from=0 /user /

RUN	chmod +x /user && \
	chown -R ${SERVICE_USER}:${SERVICE_GROUP} /user && \
	setcap 'cap_net_bind_service=+ep' /user

USER ${SERVICE_USER}

CMD ["/user", "-port=80"]

It is a big Dockerfile because the developer that created the microservice is using it to build and package in a single step. (This is not always the case. Some Dockerfiles may be simpler with build and packaging separated into different files and run at different times.)

Review the example below that has only the compiling section of the application.

FROM golang:1.7-alpine

COPY . /go/src/github.com/microservices-demo/user/
WORKDIR /go/src/github.com/microservices-demo/user/

RUN apk update
RUN apk add git
RUN go get -v github.com/Masterminds/glide
RUN glide install && CGO_ENABLED=0 go build -a -installsuffix cgo -o /user main.go
  1. Starting from the build part of the Dockerfile, we see above that the project uses a very old golang image (FROM golang:1.7).
  2. It copies the entire source of the catalog internally to the image and changes the working directory to where the package was copied.
  3. The Alpine Package Keeper (apk) performs an update and adds Git.
  4. It runs a `go get` for glide, that is an old tool for dependency support, and it is not needed in newer versions of go. (It is unlikely for up-to-date applications maintained by customers to still be using this.)
  5. Next `glide install` is used to install any dependencies, and `go build` to compile the main.go file.

Remember that our task at this point is not to understand the code, just build it to work on Power.

Given this build, we need an image for golang (from the first part delimited by “:”) that has a tag close to “1.7” that means a go image that is the closest to the version 1.7. It is better to use the same version even if newer are available because the code might use functions that were deprecated and discontinued on newer versions and the idea at this stage is not to correct issues, just show that whatever is being done on x86 will build just fine on ppc64le. 

Sometimes it is not possible to find the exact version (as in this case with golang, because it is such an old image).

A common place to start looking for open-source images is often Docker Hub. RedHat Quay.io is becoming a better choice to host images because of limitations that Docker has started imposing on downloads from Dockerhub.

We have run this hands-on lab at conferences and workshops Globally, describing how to modify the above Dockerfile to run on IBM Power for the past three years. Initially, we modified the Dockerfile to pull golang v1.11, because v1.7 is very old and did not support IBM Power ppc64le.

However, I discovered a problem with this approach in May 2024, whilst preparing to run the lab at a conference. The container version of GoLang on DockerHub was based on Debian v10, Buster, which went end of life in June 2024. In preparation for EOL, Debian moved the media to an archive folder. Two alternative solutions were considered, and both were tested successfully.

  1. Adjusts the original GoLang Dockerfile to use the Debian archive.
  2. Provides a preferred option, utilising the Red Hat Universal Base Image.
  • Review the changes made for the preferred option #2, in the Dockerfile comments below.
# Changed to use the go-toolset from Red Hat 
FROM registry.access.redhat.com/ubi8/go-toolset:latest as builder

# Sets the working directory
WORKDIR /go/src/github.com/microservices-demo/user/

# Change ownership of copied files
COPY --chown=1001:0 . /go/src/github.com/microservices-demo/user/

# The go mod init command creates a go.mod file to track your code's dependencies
# go mod tidy removes any unused dependencies and adds any missing dependencies to the go. mod file. It also updates the versions of the dependencies to the latest compatible versions, as specified in the go. mod file. 
# go build is then used to compile the project 
RUN  go mod init github.com/microservices-demo/user && go mod tidy && GOOS=linux go build .

# Uses the Red Hat Universal Base Image 
FROM registry.access.redhat.com/ubi8/ubi-minimal:latest

# No change to environments
ENV HATEAOS user
ENV USER_DATABASE mongodb
ENV MONGO_HOST user-db

# Sets the working directory
WORKDIR /

# Exposes the network port 8080
EXPOSE 8080

# “builder” below refers to the Golang stage defined earlier
COPY --from=builder /go/src/github.com/microservices-demo/user/user /

# No change, sets network port to 8080
CMD ["/user", "-port=8080"]
  • Now create the new Dockerfile excluding the comments, based on the preferred option above.
echo 'FROM registry.access.redhat.com/ubi8/go-toolset:latest as builder
WORKDIR /go/src/github.com/microservices-demo/user/
COPY --chown=1001:0 . /go/src/github.com/microservices-demo/user/
RUN  go mod init github.com/microservices-demo/user && go mod tidy && GOOS=linux go build .
FROM registry.access.redhat.com/ubi8/ubi-minimal:latest
ENV HATEAOS user
ENV USER_DATABASE mongodb
ENV MONGO_HOST user-db
WORKDIR /
EXPOSE 8080
COPY --from=builder /go/src/github.com/microservices-demo/user/user /
CMD ["/user", "-port=8080"]'>Dockerfile

Now we have a single Dockerfile that will build the application on Power and containerize it in a single step, making it easier to integrate the software.

4 Build and Push Go Container to the Internal Registry

You may hear the names Docker registry, JFrog Artifactory, Nexus, Quay or simply local registry, these registries are just different types of repositories for container images. Developers may use them for other parts like maven repositories, but it goes beyond the scope of this lab. 

For this lab, we will use the internal OpenShift Registry.

A centralized Enterprise image registry could be used, but the creation of an Enterprise registry goes beyond the scope of this lab.

You can view a recording of the lab exercise HERE

4.1 Building the Go Container

With the Dockerfile created and the images for Power already known, the container building process is straightforward.

  • Run the command to get the image registry Route into a variable.
REGISTRYHOST=$(su - cecuser -c "oc get route default-route -n openshift-image-registry --template='{{ .spec.host }}'")
  • Verify the command correctly captured the host
echo $REGISTRYHOST
default-route-openshift-image-registry.apps.p1334.cecc.ihost.com

The route above will be different, it can’t be blank, if it is blank make sure you login to the OCP as stated in the Section, Log into OpenShift via the command line interface.

  • Run the podman command to build our new image using the Dockerfile we created previously and tag it to the internal registry for the course.
podman build -f Dockerfile --tag $REGISTRYHOST/sock-shop/user:latest
[1/2] STEP 1/4: FROM registry.access.redhat.com/ubi8/go-toolset:latest AS builder
[1/2] STEP 2/4: WORKDIR /go/src/github.com/microservices-demo/user/
--> Using cache 2963cdf719c22d60e3a2769d903b80a65097b5bd1e0feff1e6d2e3984546b75d
--> 2963cdf719c2
[1/2] STEP 3/4: COPY --chown=1001:0 . /go/src/github.com/microservices-demo/user/
--> 3e88177147fb
[1/2] STEP 4/4: RUN  go mod init github.com/microservices-demo/user && go mod tidy && GOOS=linux go build . 
go: creating new go.mod: module github.com/microservices-demo/user
.
.
.
[2/2] STEP 8/8: CMD ["/user", "-port=8080"]
--> Using cache fbf41164ff850402a9cd942471aa3519efa94b260364dfa4a098ff4664967299
[2/2] COMMIT default-route-openshift-image-registry.apps.p1390.cecc.ihost.com/sock-shop/user:latest
--> fbf41164ff85
Successfully tagged default-route-openshift-image-registry.apps.p1390.cecc.ihost.com/sock-shop/user:latest
fbf41164ff850402a9cd942471aa3519efa94b260364dfa4a098ff4664967299
[root@p1390-bastion microservices-demo-user]#

This created two new images on your podman, one that has no reference (the one used to build) and another one that is the user image.

===================================================

Important Note!

There has been a recent change to an opensource component that is required by the sock-shop application. On Sunday 10th November 2024, I noticed that the openzipkin component had changed, causing the build of this container to fail. The change must have taken place since the end of October, when I last recorded the demonstration. The current output from the podman build command is below.

For this reason, please now skip to chapter 5. Building-a-.NET-Service-from-a-UBI-image

I will modify the application yaml file to pull the prebuilt Go User Application container from Quay.io, so that you may still work through the lab while we resolve this issue. Thanks for your patience.

go: github.com/microservices-demo/user imports
        github.com/openzipkin/zipkin-go-opentracing: github.com/openzipkin/zipkin-go-opentracing@v0.5.0: parsing go.mod:
        module declares its path as: github.com/openzipkin-contrib/zipkin-go-opentracing
                but was required as: github.com/openzipkin/zipkin-go-opentracing
Error: building at STEP "RUN go mod init github.com/microservices-demo/user && go mod tidy && GOOS=linux go build .": while running runtime: exit status 1

===================================================

  • Check the two images created using podman images.
podman images |head -3
REPOSITORY                                                                       TAG         IMAGE ID      CREATED             SIZE
default-route-openshift-image-registry.apps.p1390.cecc.ihost.com/sock-shop/user  latest      5045c5560200  About a minute ago  135 MB
<none>                                                                           <none>      c417b77e4c72  About a minute ago  1.28 GB

You are ready to push the images to be used in production.

4.2 Push the Go image to the internal registry

We will now push the new image to the internal registry. 

  • Login into the registry using podman
podman login $REGISTRYHOST -u cecuser -p $(su - cecuser -c "oc whoami -t") --tls-verify=false
Login Succeeded!
  • Create the sock-shop project.
oc new-project sock-shop
Now using project "sock-shop" on server "https://api.p1323.cecc.ihost.com:6443".

You can add applications to this project with the 'new-app' command. For example, try:

    oc new-app rails-postgresql-example

to build a new example application in Ruby. Or use kubectl to deploy a simple Kubernetes application:

    kubectl create deployment hello-node --image=k8s.gcr.io/e2e-test-images/agnhost:2.33 -- /agnhost serve-hostname
  • Push the created image to the internal registry.
podman push $REGISTRYHOST/sock-shop/user:latest --tls-verify=false
Getting image source signatures
Copying blob 981ce56b3062 done   |
Copying blob 6237f8cea45b skipped: already exists
Copying config 5045c55602 done   |
Writing manifest to image destination

5 Building a .NET Service from a UBI image

We will now create our own .NET image using the RedHat UBI container.

You can view a recording of the lab exercise HERE

5.1 Clone the .NET application from GitHub

  • Change to the build directory.
cd /root/build
  • Check you are on the correct Directory.
pwd
/root/build
  • Inside the new directory under the root home, you can now clone the orders project to your server.
git clone https://github.com/paulchapmanibm/ordersdotnet.git
Cloning into 'ordersdotnet'...
remote: Enumerating objects: 322, done.
remote: Counting objects: 100% (322/322), done.
remote: Compressing objects: 100% (132/132), done.
remote: Total 322 (delta 166), reused 308 (delta 159), pack-reused 0
Receiving objects: 100% (322/322), 56.50 KiB | 4.71 MiB/s, done.
Resolving deltas: 100% (166/166), done.
  • You should see a new folder created:
ls -la
total 12
drwxr-xr-x.  4 root root   57 Oct  3 17:27 .
dr-xr-x---. 15 root root 4096 Oct  3 16:50 ..
drwxr-xr-x. 10 root root 4096 Oct  3 17:04 microservices-demo-user
drwxr-xr-x. 10 root root 4096 Oct  3 17:27 ordersdotnet
  • Change to this directory
cd ordersdotnet/
  • Check the directory
ls
appsettings.json  docker   orders.NET.csproj          Program.cs  scripts
Config            LICENSE  orders.net_deploy_rsa.enc  Properties  Value
Controllers       Models   orders.NET.sln             README.md 

You can see a .csproj file. This normally means you are working with a C# program and if dotnet/aspnet frameworks are used (like in this project) we can use dotnet provided by Red Hat, which includes C# libraries.

5.2 Understanding the NET build process

The Dockerfile is the main file that holds this information, as it determines the content of the container image and how it is built.

  • From the last directory listing you can see that there is a docker directory, list its contents.
ls -l docker
total 0
drwxr-xr-x. 2 root root 47 Oct  3 17:27 orders.net
  • You can see that there is an orders directory, list its contents.
ls -l docker/orders.net
total 8
-rw-r--r--. 1 root root 242 Oct  3 17:27 Dockerfile
-rw-r--r--. 1 root root 190 Oct  3 17:27 Dockerfile-msft
  • You found the Dockerfile and now you can inspect it.
cat docker/orders.net/Dockerfile
FROM microsoft/dotnet:latest
COPY . /app
WORKDIR /app
RUN ["dotnet", "restore"]
RUN apt-get update && apt-get install -y tcpdump jq
RUN ["dotnet", "build"]
EXPOSE 80/tcp
ENTRYPOINT ["dotnet", "run", "--server.urls", "http://0.0.0.0:80"]

Analyzing the Dockerfile, you can see that:

  1. The “FROM” specifies the microsoft/dotnet image.
  2. The “COPY” will copy all the code that is in the current directory in the server into the container.
  3. The “WORKDIR” sets the directory where commands should take place.
  4. The “RUN” commands:
    1. restores the dependencies and tools of a project.updates the package index files and installs the tcpdump package.
    1. builds the project and its dependencies into a set of binaries.
  5. The “EXPOSE” specifies that port 80 should be used.
  6. The “ENTRYPOINT” statement is the main command to run on the container.  When started it is an array of entries (the spaces are delimited by comas).

Notice that the Dockerfile specifies using dotnet run, which would mean using the SDK to run and not the runtime. We will change this to use the runtime instead, and this will create a smaller image.

We will also use this lab as a way of creating our own SDK and runtime images instead of going to docker hub. We will build the packages from the Red Hat ubi8-minimal image which contains dotnet libraries. 
UBI is the Universal Base Image container framework from Red Hat – UBI images are cut-down versions of RHEL specifically for use as base OS container images.

More info can be found here:

microdnf is a cutdown package manager used within UBI images.  Think of it as a skinny yum.

  • Create a Dockerfile for the dotnet-runtime
echo 'FROM ubi8-minimal
USER root
RUN microdnf install -y dotnet-runtime-7.0 aspnetcore-runtime-7.0 && microdnf clean all' >Dockerfile_dotnet-runtime
  • Create a Dockerfile for the dotnet SDK
echo 'FROM ubi8-minimal
USER root
RUN microdnf install -y dotnet-sdk-7.0 && microdnf clean all' >Dockerfile_dotnet

Now we can build the images that we will use to compile and run the code.

  • Build the image for the .NET
podman build -f Dockerfile_dotnet --tag dotnet:latest
STEP 1/3: FROM ubi8-minimal
Resolved "ubi8-minimal" as an alias (/etc/containers/registries.conf.d/001-rhel-shortnames.conf)
Trying to pull registry.access.redhat.com/ubi8-minimal:latest...
Getting image source signatures
Checking if image destination supports signatures
Copying blob 5b27cce89638 done
Copying config 8f05b252c1 done
Writing manifest to image destination
Storing signatures
STEP 2/3: USER root
--> 12e4860d268
STEP 3/3: RUN microdnf install -y dotnet && microdnf clean all
.
.
.
Installing: aspnetcore-runtime-7.0;7.0.0-1.el8_7;ppc64le;ubi-8-appstream-rpms
Installing: dotnet-sdk-7.0;7.0.100-1.el8_7;ppc64le;ubi-8-appstream-rpms
Installing: dotnet;7.0.100-1.el8_7;ppc64le;rhel-8-for-ppc64le-appstream-rpms
Complete.
Complete.
COMMIT dotnet:latest
--> e151e417ad9
Successfully tagged localhost/dotnet:latest
e151e417ad9087683cdcb866b5059066f7b9cd60dd94390161715c05bd59a513
  • Build the image for the .NET Runtime
podman build -f Dockerfile_dotnet-runtime --tag dotnet-runtime:latest
STEP 1/3: FROM ubi8-minimal
STEP 2/3: USER root
--> Using cache 12e4860d268be5c86ac55401465e1326a1b287f9e812d125d7011b279914f313
--> 12e4860d268
STEP 3/3: RUN microdnf install -y dotnet-runtime-7.0 aspnetcore-runtime-7.0 && microdnf clean all
Downloading metadata...
.
.
.
Installing: dotnet-runtime-7.0;7.0.0-1.el8_7;ppc64le;ubi-8-appstream-rpms
Installing: aspnetcore-runtime-7.0;7.0.0-1.el8_7;ppc64le;ubi-8-appstream-rpms
Complete.
Complete.
COMMIT dotnet-runtime:latest
--> 3d90ba7a1d3
Successfully tagged localhost/dotnet-runtime:latest
3d90ba7a1d37760de8b567c12996b7d6335cf3a401c92c32da58f5c142e2cc2c

Now that you built both images needed to build the microservice container, we can create a single Dockerfile that will take care of the build and create the final container that will run the orders service.

5.3 Creating the Dockerfile to build the microservice container

To then build the container we will create a Dockerfile file with the contents on the note below:

#BUILD PART
#Start from (FROM) the image for maven used on the build script
#Call the container build “builder” using the “as” statement
#For that we will use the dotnet sdk we just created   
FROM localhost/dotnet:latest as builder
#We copy all the code from the cloned project to the /app
COPY . /app
# We set the working directory for the /app
WORKDIR /app
# We run the two commands for the Build
RUN ["dotnet", "restore"]
RUN ["dotnet", "build"]
#This will build the application  on the /app/bin/Debug/net7.0
#CONTAINER CREATION PART
#Now to create the final container we will start from the dotnet-runtime we built
FROM localhost/dotnet-runtime:latest

#Copy the application built from the builder image
COPY --from=builder /app/bin/Debug/net7.0/ /app/
#Setting the working directory to /app just like the old Dockerfile
WORKDIR /app
# We will run the application orders.NET we created
ENTRYPOINT ["dotnet", "orders.NET.dll"]

Read carefully the comments of the file since it explains each of the steps of the Dockerfile and you will see how an image is created. It is very logical.

  • Create the Dockerfile mentioned on the previous note without the comments.
echo 'FROM localhost/dotnet:latest as builder

COPY . /app

WORKDIR /app

RUN ["dotnet", "restore"]
RUN ["dotnet", "build"]

FROM localhost/dotnet-runtime:latest

COPY --from=builder /app/bin/Debug/net7.0/ /app/
WORKDIR /app
ENTRYPOINT ["dotnet", "orders.NET.dll"]'>Dockerfile

Now we have a single Dockerfile that will build the application and containerize it on a single step, making it easier to integrate the software.

6 Build and Push .NET Container to the Registry

You may hear the names Docker registry, JFrog Artifactory, Nexus, Quay or simply local registry, these registries are just different types of repositories for container images. Developers may use them for other parts like maven repositories, but it goes beyond the scope of this lab. 

For this lab, we will use the internal OpenShift Registry.

A centralized Enterprise image registry could be used, but the creation of an Enterprise registry goes beyond the scope of this demo.

You can view a recording of the lab exercise HERE

6.1 Building the .NET Container

With the Dockerfile created and the images for Power already known, the container building process is straightforward.

  • Run the command to get the image registry Route into a variable
REGISTRYHOST=$(su - cecuser -c "oc get route default-route -n openshift-image-registry --template='{{ .spec.host }}'")
  • Verify the command correctly captured the host.
echo $REGISTRYHOST
default-route-openshift-image-registry.apps.p1309.cecc.ihost.com

The route above will be different, It can’t be blank, if it is blank make sure you login to the OCP like stated in Section Log into OpenShift via the command line interface.

  • Run the podman command to build using the Dockerfile we created on the previous step and tag it to the internal registry for the course.
podman build -f Dockerfile --tag $REGISTRYHOST/sock-shop/orders:latest
[1/2] STEP 1/5: FROM dotnet:latest AS builder
[1/2] STEP 2/5: COPY . /app
--> 9e75fe1e9b7
[1/2] STEP 3/5: WORKDIR /app
--> ae710361dcb
[1/2] STEP 4/5: RUN ["dotnet", "restore"]

Welcome to .NET 7.0!
---------------------
SDK Version: 7.0.100

---------------------
.
.
.
--> adda01ef96a
[2/2] STEP 1/4: FROM dotnet-runtime:latest
[2/2] STEP 2/4: COPY --from=builder /app/bin/Debug/net7.0/ /app/
--> b40fefe8200
[2/2] STEP 3/4: WORKDIR /app
--> ea05fe42b9e
[2/2] STEP 4/4: ENTRYPOINT ["dotnet", "orders.NET.dll"]
[2/2] COMMIT default-route-openshift-image-registry.apps.p1389.cecc.ihost.com/sock-shop/orders:latest
--> bd1b4e1d045
Successfully tagged default-route-openshift-image-registry.apps.p1389.cecc.ihost.com/sock-shop/orders:latest
bd1b4e1d045afd9573329abdeaad2b1c369747bd76431e2b99fc07f7eb7bdb8e

This created two new images. Podman shows one that has no reference (the one used to build) and another one that is the orders microservice image.

  • Check the two images created using podman images
podman images |head -3
REPOSITORY                                                                         TAG         IMAGE ID      CREATED         SIZE
default-route-openshift-image-registry.apps.p1390.cecc.ihost.com/sock-shop/orders  latest      ddcdc4ba3dbc  43 seconds ago  215 MB
<none>                                                                             <none>      de1c27e23baa  46 seconds ago  1.05 GB

You are ready to push the images to be used in production.

6.2 Push the .NET images to the internal registry

We will push the new image to the internal registry.

  • Login into the registry using podman
podman login $REGISTRYHOST -u cecuser -p $(su - cecuser -c "oc whoami -t") --tls-verify=false
Login Succeeded!
  • Push the created image to the Internal registry
podman push $REGISTRYHOST/sock-shop/orders:latest --tls-verify=false
Getting image source signatures
Copying blob 4a275ce82a89 done   |
Copying blob 38bf0ae868e4 done   |
Copying blob 6237f8cea45b skipped: already exists
Copying config ddcdc4ba3d done   |
Writing manifest to image destination

7 Deploy the Microservice Application

Now you have the modified the Microservices, you can deploy the microservices application using a yaml file. You can view a recording of the lab exercise HERE

  • Download the application yaml file from GitHub
wget https://raw.githubusercontent.com/paulchapmanibm/assets/refs/heads/main/usvc/apptx.yaml
--2024-10-16 13:44:05--  https://raw.githubusercontent.com/paulchapmanibm/assets/refs/heads/main/usvc/apptx.yaml
Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 185.199.110.133, 185.199.111.133, 185.199.108.133, ...
Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|185.199.110.133|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 19840 (19K) [text/plain]
Saving to: ‘apptx.yaml.1’

apptx.yaml.1                                100%[========================================================================================>]  19.38K  --.-KB/s    in 0.001s

2024-10-16 13:44:06 (22.0 MB/s) - ‘apptx.yaml.1’ saved [19840/19840]
  • Open the Administrator console for your OpenShift cluster and navigate to the Developer view -> Topology and select sock-shop as the project.  When you run the oc apply command below, you will see the microservices appear, they will start to build and run.
  • Run the command below to create the application now.
oc apply -f ./apptx.yaml
Warning: resource namespaces/sock-shop is missing the kubectl.kubernetes.io/last-applied-configuration annotation which is required by oc apply. oc apply should only be used on resources created declaratively by either oc create --save-config or oc apply. The missing annotation will be patched automatically.
namespace/sock-shop configured 
deployment.apps/carts created
service/carts created
deployment.apps/carts-db created
service/carts-db created
deployment.apps/catalogue created
service/catalogue created
deployment.apps/catalogue-db created
service/catalogue-db created
deployment.apps/front-end created
service/front-end-external created
service/front-end created
deployment.apps/orders created
service/orders created
deployment.apps/orders-db created
service/orders-db created
deployment.apps/payment created
service/payment created
deployment.apps/queue-master created
service/queue-master created
deployment.apps/rabbitmq created
service/rabbitmq created
deployment.apps/session-db created
service/session-db created
deployment.apps/shipping created
service/shipping created
deployment.apps/user created
service/user created
deployment.apps/user-db created
service/user-db created
route.route.openshift.io/front-end-external created

  • When you run the oc apply command below above, you will see the microservices appear one by one in the OpenShift Console, as they start to build and run.

8 Access the Application on OpenShift

This is simply your opportunity to see the microservice solution that you deployed in action. Go ahead, have a play and do whatever you like, just remember that it is a developer’s environment and not a production system. Some functionality will not work as expected, and that is fine. The purpose of the lab is to demonstrate how easy it is to operate applications that were designed for x86, on IBM Power ppc64le with Red Hat OpenShift. You can view an example recording of this lab exercise HERE at 5:30

  • Go to the Developer view on topology and select the sock-shop.
  • See that the application is up and show the results. The application is ready when ALL microservices light blue circle turns dark blue, which may take 3-5 minutes.

  • Open the Sock Shop front end URL, (on the upper right side of the front-end pod), which takes you to the Sock Shop ecommerce website.
  • If necessary, accept the certificate warning
  • Again, accept the certificate warning if necessary
  • Register a new account
  • Search the catalogue
  • View various socks
  • Add socks to your shopping cart
  • Add shipping address
  • Add card payment information
  • Add Credit Card

9 Lab Wrap up

Congratulations, you have completed this lab. You went through the steps to build an application that was written for x86 eight years ago, in a modern language on the Power ppc64le OpenShift Container Platform.

You should now be confident that you understand how Go and .NET frameworks are already ported to Power. There are more than 15K images on Docker Hub that can be used, and nearly 2,000 more certified containers in the Red Hat Container Catalogue.

You now understand how to clone a GitHub repository, you have seen a Dockerfile and how it is used to build images. You have built Power ppc64le images and worked with the OpenShift Internal Registry to publish the solution you created!

10 OpenShift Multi-Arch Compute

Multi-Architecture Compute recordings are available on my YouTube channel

10.1 YouTube

10.1.1 Introduction and Early Adoption Program Replay

10.1.2 UKI Brunch & Learn Replay

10.1.3 Deploy the Sock Shop Demonstration

10.1.4 Migrating Workloads between x86 & IBM Power Worker Nodes

10.2 IBM Redbook

The Creating OpenShift Multiple Architecture Clusters with IBM Power Redbook has now been published. Future versions may be downloaded HERE

10.3 Power Modernisation Website

Find more about information relating to Power Modernisation, Hybrid Cloud and AI at my personal website.

10.4 Multi-Arch Compute Assistance

Let me know if you are you considering an OpenShift Multi-Arch Compute project. Development and I will be happy to discuss use cases and assist with your project.

Credit

Daniel Casali

Daniel Casali | Modernization – WW Subject Matter Expert


by

Tags:

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *