TOC created using the yzhang.markdown-all-in-one VS Code extension
crc setup
crc start
crc delete
oc login -u developer -p developer https://api.ocp4.example.com:6443
oc whoami --show-console
https://role.rhu.redhat.com/rol-rhu/app/courses/do288-4.14/
etcd key value store
quay.io container registry
kubelet: on each cluster machine - ensure containers up & running
CRI-O: run, stop, restart containers implementing Kubernetes Container Runtime Interface (CRI)
OC 4.12
https://crc.dev/docs/introducing/
crc setup
crc start
crc delete
oc login -u developer -p developer https://api.ocp4.example.com:6443
oc whoami --show-console
GitOps Operator
oc api-resources | less
oc explain pvc
oc get projects
oc get all -n openshift-authentication
oc new-app -i mysql \
-e MYSQL_USER=user -e MYSQL_PASSWORD=pass \
-e MYSQL_DATABASE=testdb -l db=mysql
oc new-app \
--name hello -i php \ #use an image stream
--code http://gitserver.example.com/mygitrepo.git
oc delete all --selector app=test
oc new-app \
-o yaml registry.example.com/mycontainerimage
oc new-app -e DB_PASSWORD=test --name=todo-list \
registry.ocp4.example.com:8443/redhattraining/openshift-dev-deploy-review-todo-list
oc delete all -l app=todo-list

odo create project ...
odo deploy
odo preference set ImageRegistry REGISTRY_URL/NAMESPACE
oc expose service SERVICE_NAME
schemaVersion: 2.2.0
metadata:
name: nodejs
version: 2.1.1
displayName: Node.js Runtime
description: Stack with Node.js 16
tags: ["Node.js", "Express", "ubi8"]
projectType: "Node.js"
language: "JavaScript"
provider: Red Hat
supportUrl: https://github.com/devfile-samples/devfile-support#support-information
parent:
id: nodejs
registryUrl: "https://registry.devfile.io"
components:
- name: image-build
image:
imageName: nodejs-image:latest
dockerfile:
uri: Dockerfile
buildContext: .
rootRequired: false
- name: kubernetes-deploy
kubernetes:
uri: deploy.yaml
commands:
- id: build-image
apply:
component: image-build
- id: deployk8s
apply:
component: kubernetes-deploy
- id: deploy
composite:
commands:
- build-image
- deployk8s
group:
kind: deploy
isDefault: true
Use Red Hat Universal Base Images / UBI
find list here: https://catalog.redhat.com/search?searchType=containers
Support for non-root-users:
USER root
RUN chgrp -R 0 /var/cache && \
chmod -R g=u /var/cache
USER 1001
Graceful shutdown
#!/bin/env bash
function graceful_shutdown() {
kill -SIGTERM "$java_pid"
wait "$java_pid"
exit 0
}
# Trap the SIGTERM signal
trap graceful_shutdown SIGTERM
_...script omitted..._
# Start the application
java -jar example.jar &
java_pid=$!
_...script omitted..._
# Wait for the process to finish
wait "$java_pid"
pre-stop hook
apiVersion: v1
kind: Pod
metadata:
name: example-pod
spec:
containers:
- name: my-container
image: example.com/myimage
lifecycle:
preStop:
httpGet:
path: /shutdown
port: 8080
maybe add labels with the io.openshift or io.k8s prefixes
LABEL io.openshift.min-cpu 2
use / define
3 Red Hat registries:
Get pull secret: https://console.redhat.com/openshift/install/pull-secret
Create image pull secret …
oc create secret docker-registry SECRET_NAME \
--docker-server REGISTRY_URL \
--docker-username USER \
--docker-password PASSWORD \
--docker-email=EMAIL
# or
oc create secret generic SECRET_NAME \
--from-file .dockerconfigjson=${XDG_RUNTIME_DIR}/containers/auth.json \
--type kubernetes.io/dockerconfigjson
… and then use it in manifest …
spec:
containers:
- name: example-container
image: REGISTRY_URL
imagePullSecrets:
- name: SECRET_NAME
… or link it to a service account
oc secrets link --for=pull default SECRET_NAME
# unlink:
oc secrets unlink default wrong-registry-credentials
side note on how to access event log:
oc get event --field-selector type=Warning --sort-by='.lastTimestamp'
oc describe is php -n openshift
# periodic updates, :tag is optional
oc import-image myimagestream --confirm --scheduled=true \
--from example.com/example-repo/my-app-image:latest
# all tags
oc import-image myimagestream --confirm --all \
--from registry/myorg/myimage
oc tag myimagestream:tag myimagestream:latest
oc create secret generic regtoken \
--from-file .dockerconfigjson=${XDG_RUNTIME_DIR}/containers/auth.json \
--type kubernetes.io/dockerconfigjson
oc import-image myimagestream --confirm \
--from registry.example.com/myorg/myimage
oc get istag # oc get ImageStreamTag
use with kubernetes ressources
oc set image-lookup myimagestream
skopeo: Various operations with container images and container image registries
skopeo login registry.ocp4.example.com:8443 \
-u developer -p developer
skopeo inspect \
docker://registry.ocp4.example.com:8443/redhattraining/hello-world-nginx
build strategy
sourceStrategy)dockerStrategy)customStrategy)jenkinsPipelineStrategy)builder image is automatically detected for S2I builds and contains amongst others
The build input sources, in order of precedence are: Dockerfile, Git, Image, Binary, Input Secrets, and External artifacts.
build config (bc) - example for S2I builds
kind: BuildConfig
apiVersion: build.openshift.io/v1
metadata:
name: "php-sample-build"
spec:
runPolicy: "Serial"
triggers:
- type: "ImageChange"
source:
git:
uri: "http://services.lab.example.com/php-helloworld"
strategy:
sourceStrategy:
from:
kind: "ImageStreamTag"
name: "ruby-20-centos7:latest"
output:
to:
kind: "ImageStreamTag"
name: "origin-ruby-sample:latest"

oc new-app -i php:7.3 --name=php-helloworld \
--context-dir=php-helloworld https://github.com/RedHatTraining/DO288-apps
S2I build
...
source:
git:
uri: "http://services.lab.example.com/java-helloworld"
configMaps:
- configMap:
name: settings-mvn
destinationDir: ".m2"
secrets:
- secret:
name: secret-mvn
destinationDir: ".ssh"
Docker strategy
...
source:
git:
uri: "http://services.lab.example.com/java-helloworld"
configMaps:
- configMap: 1
name: settings-mvn
destinationDir: ".m2"
secrets:
- secret: 2
name: secret-mvn
destinationDir: ".ssh"
you can include custom assemble & run scripts for s2i
oc new-app --name java-application \
--build-env BUILD_ENV=BUILD_VALUE \
--env RUNTIME_ENV=RUNTIME_VALUE \
-i redhat-openjdk18-openshift:1.8 \
--context-dir java-application \
https://git.example.com/example/java-application-repository
oc new-app --name java-application \
--strategy Docker \
--context-dir java-application \
https://git.example.com/example/java-application-repository
oc get builds
oc start-build buildconfig/app
oc start-build --follow vertx-site
oc cancel-build buildconfig/app
oc cancel-build app-build-3
oc wait --for=condition=complete \
--timeout=600s builds/vertx-site-2
oc set env buildconfig/app BUILD_LOGLEVEL="3"
oc logs -f build/app-1
oc logs -f buildconfig/app
oc debug deploy/app
oc set triggers bc/name --from-image=project/image:tag
oc set triggers bc/name --from-image=project/image:tag --remove
oc set trigger deployment/name
oc set triggers bc/name --from-gitlab
oc create secret generic gitlab \
--from-literal=username=developer --from-literal=password=d3v3lop3r
oc new-app --name builds-triggers \
--source-secret gitlab $IMAGE~$GIT_REPO
oc rsh svc/builds-triggers \
cat /etc/redhat-release
/usr/libexec/s2i/assemble/usr/libexec/s2i/run/usr/libexec/s2i/usagec new-app --name bonjour \
--context-dir labs/builds-s2i/s2i-scripts \
httpd:2.4-ubi9~https://git.ocp4.example.com/developer/DO288-apps
what exactly does the resulting app image look like? -> unfortunately, it also contains the assemble script:
sh-5.1$ ls -la ./usr/libexec/s2i/
total 12
drwxr-xr-x. 2 root root 46 Feb 29 2024 .
drwxr-xr-x. 1 root root 151 Feb 29 2024 ..
-rwxrwxr-x. 1 root root 449 Feb 29 2024 assemble
-rwxrwxr-x. 1 root root 108 Feb 29 2024 run
-rwxrwxr-x. 1 root root 533 Feb 29 2024 usage
does it also contain the build artifacts? -> Perplexity Ai says usually not for compiled languages, but for interpreted languages it can be the case as the build container might be reused as app image. See https://www.perplexity.ai/search/does-an-application-image-buil-9BUxs_GdStyccN_Cy7jESg
Deployment YAML
apiVersion: apps/v1
kind: Deployment
metadata:
name: hello-openshift
spec:
selector:
matchLabels:
app: hello-openshift
replicas: 3
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
template:
...output omitted...
DeploymentConfig is deprecated: https://access.redhat.com/articles/7041372
Deployment Strategies with Deployment Resources:
Deployment Strategies with the Red Hat OpenShift Router:
oc new-app --as-deployment-config \
--name users-db \
-e MYSQL_USER=developer -e MYSQL_PASSWORD=redhat -e MYSQL_DATABASE=users \
https://git.ocp4.example.com/developer/DO288-apps \
--context-dir=apps/deployments-strategy/users-db \
-o yaml > application.yaml
oc apply -f application.yaml
oc rollout latest dc/users-db
oc rollout status deployment example-deployment
oc rollout undo deployment example-deployment
oc rollout pause deployment example-deployment
oc rollout resume deployment example-deployment
oc scale deployment example-deployment --replicas=3
Secret Types:
service-account-tokenbasic-authssh-authdockercfgtlsopaqueoc create configmap example-cm \
--from-literal key1=value1 \
--from-literal key2=value2
oc create configmap example-cm \
--from-file=redis.conf
oc create configmap example-cm \
--from-file=primary=/etc/redis/redis.conf \
--from-file=replica=replica-redis.conf
oc get secret mysecret -o yaml
oc edit configmap my-cm
oc patch configmap/my-cm \
--patch '{"data":{"key1":"newvalue1"}}'
oc extract secret/my-secret --to=/tmp/secret
oc extract secret/postgresql --to=.
tail -n +1 database*; echo
oc set data secret/my-secret --from-file=/tmp/secret
oc set env deployment my-deployment \
--from configmap/my-cm
oc set volume deployment my-deployment --add \
-t secret -m /path/to/mount/volume \
--name myvol --secret-name my-secret
Service accounts provide identity for applications.
oc create serviceaccount my-sa
oc set serviceaccount \
deployment nginx-deployment my-sa
# mounted in pods:
oc exec somepod -- \
ls /var/run/secrets/kubernetes.io/serviceaccount
configure security context
apiVersion: apps/v1
kind: Deployment
metadata:
name: example-deployment
spec:
replicas: 1
selector:
matchLabels:
app: example
strategy: {}
template:
metadata:
labels:
app: example
spec:
containers:
- command:
- sleep
- infinity
image: registry.access.redhat.com/ubi8/ubi:8.0
name: ubi
securityContext:
runAsNonRoot: true
allowPrivilegeEscalation: false
seccompProfile:
type: RuntimeDefault
capabilities:
drop:
- ALL
Storage class
Persistent volume
apiVersion: v1
kind: PersistentVolume
metadata:
name: data-volume
spec:
capacity:
storage: 5Gi
volumeMode: Filesystem
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Recycle
storageClassName: fast
nfs:
path: /exports-ocp4/example
server: 192.168.50.10
Persistent Volumen Claim (PVC)
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: postgres-pvc
spec:
storageClassName: nfs-storage
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
volumeMode: Filesystem
Mount PVC in pod
apiVersion: v1
kind: Pod
metadata:
name: app-pod
spec:
containers:
- name: app-ui
image: quay.io/example/nginx
volumeMounts:
- mountPath: "/var/www/html"
name: ui-volume
volumes:
- name: ui-volume
persistentVolumeClaim:
claimName: data-volume-claim
Adding Storage to Deployments
oc set volumes dc/postgresql \
--add \
--name nfs-volume-storage \
--type pvc \
--claim-mode rwo \
--claim-size 1Gi \
--mount-path /var/lib/pgsql/data \
--claim-name postgres-pvc \
--overwrite
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-deployment
...output omitted...
spec:
...output omitted...
template:
...output omitted...
spec:
containers:
- image: registry.ocp4.example.com:8443/rhel8/mysql-80
ports:
- containerPort: 3306
protocol: TCP
...output omitted...
volumeMounts:
- mountPath: /tmp/data
name: nfs-volume-storage
...output omitted...
volumes:
- name: nfs-volume-storage
persistentVolumeClaim:
claimName: my-data-claim
...output omitted...
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: my-stateful-app
spec:
...output omitted...
replicas: 3
serviceName: my-stateful-app
template:
...output omitted...
spec:
containers:
- image: my-app-image:latest
name: mysql-80
ports:
- containerPort: 80
protocol: TCP
volumeMounts:
- name: app-pvc
mountPath: /var/lib/mysql
subPath: mysql-db
volumes:
- name: init-db-volume
configMap:
name: init-db-cm
volumeClaimTemplates:
- metadata:
name: app-pvc
spec:
accessModes: [ "ReadWriteOnce" ]
storageClassName: nfs-storage
resources:
requests:
storage: 1Gi
apiVersion: apps/v1
kind: Deployment
metadata:
name: example-deployment
spec:
replicas: 1
selector:
matchLabels:
deployment: example-deployment
template:
metadata:
labels:
deployment: example-deployment
spec:
containers:
- image: quay.io/example/deployment:1.0
name: example-deployment
resources:
requests:
cpu: 100m
memory: 200Mi
limits:
memory: 200Mi
Eviction strategy
| Category | Description | OpenShift Eviction Strategy |
|---|---|---|
| Best-Effort | Pods without requests and limits, which have an unpredictable resource consumption. | First pods to evict. |
| Burstable | Pods with requests, and no limits or limits that exceed the requests. | Pods to evict if there are no Best-effort pods left. |
| Guaranteed | Pods with equal requests and limits. | Never evicted. |
oc describe limitranges
oc get events \
--sort-by=metadata.creationTimestamp --field-selector type=Warning
Probes
| Name | Mandatory | Description | Default Value |
|---|---|---|---|
initialDelaySeconds |
Yes | Determines how long to wait after the container starts before beginning the probe. | 0 |
timeoutSeconds |
Yes | Determines how long to wait for the probe to finish. If this time is exceeded, then OpenShift assumes that the probe failed. | 1 |
periodSeconds |
No | Specifies the frequency of the checks. | 1 |
successThreshold |
No | Specifies the minimum consecutive successes for the probe to be considered successful after it has failed. | 1 |
failureThreshold |
No | Specifies the minimum consecutive failures for the probe to be considered failed after it has succeeded. | 3 |
readinessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 15
timeoutSeconds: 1
livenessProbe:
exec:
command:
- cat
- /tmp/health
initialDelaySeconds: 15
timeoutSeconds: 1
livenessProbe:
tcpSocket:
port: 8080
initialDelaySeconds: 15
timeoutSeconds: 1
oc set probe deployment/myapp --readiness \
--get-url=http://:8080/readyz --period-seconds=20
oc set probe deployment/myapp --liveness \
--open-tcp=3306 --period-seconds=20 \
--timeout-seconds=1
Access with ${MYPARAMETER}
- description: Myapp configuration data
name: MYPARAMETER
required: true
default
parameters:
- description: Myapp configuration data
name: MYPARAMETER
value: /etc/myapp/config.ini
generate default
parameters:
- description: ACME cloud provider API key
name: APIKEY
generate: expression
from:"[a-zA-Z0-9]{12}"
oc new-app --file mytemplate.yaml -p PARAM1=value1 \
-p PARAM2=value2
oc process -f mytemplate.yaml -p PARAM1=value1 \
-p PARAM2=value2 > myresourcelist.json
oc create -f myresourcelist.json
oc process -f mytemplate.yaml -p PARAM1=value1 \
-p PARAM2=value2 | oc create -f -
oc process -f mytemplate.yaml --parameters
can not be used as key for a label
helm repo add openshift-helm-charts \
https://charts.openshift.io/
helm search repo openshift-helm-charts
helm pull openshift-helm-charts/redhat-quarkus
helm pull openshift-helm-charts/redhat-quarkus \
--untar --destination redhat-quarkus
helm package my-chart-directory
helm push example-chart-0.1.0.tgz example.repository.org
helm install my-quarkus-application \
openshift-helm-charts/redhat-quarkus \
--set replicaCount=3,image.tag=latest
helm install --wait expense-service .
helm upgrade my-quarkus-application \
openshift-helm-charts/redhat-quarkus
helm ls
helm history my-quarkus-application
helm rollback my-quarkus-application 1
helm uninstall my-quarkus-application
helm create my-helm-chart
templating language
apiVersion: apps/v1
kind: Deployment
metadata:
name: example-deployment
spec:
replicas:
...deployment omitted...
variable scope
...
- image:
imagePullPolicy:
name: example-deployment
control flow
containers:
- image:
-
env:
- name: DATABASE_NAME
valueFrom:
secretKeyRef:
key: database-name
name: postgresql
...
example
...file omitted...
database-user:
database-password:
database-password:
Template rendering - works as well for remote charts
# render all templates
helm template .
# render specific template
helm template -s templates/serviceaccount.yaml .
helm template \
--skip-tests ./ > ../kustomized-quotes/base/app.yaml
Use templates/NOTES.txtfor message displayed after using the chart
| [Helm | Getting Started](https://helm.sh/docs/chart_template_guide/getting_started/) |
example layout
myapp
├── base
└── overlays
├── production
└── staging
kustomization.yaml file for each configuration set
resources:
- deployment.yaml
- secrets.yaml
- service.yaml
overlay example
resources:
- ../../base
patches:
- path: replica_limits.yaml
oc kustomize ./base
oc apply -k overlays/stage
The foundation of OpenShift Pipelines is Tekton To enable the CI/CD capabilities in OpenShift, install the Red Hat OpenShift Pipelines operator from the Operator Hub.
Reusable tasks available at https://hub.tekton.dev/
tkn CLI - tekton
Custom tasks (namespace/project scope)
---
apiVersion: tekton.dev/v1
kind: Task
metadata:
name: fetch-url
spec:
results:
- name: output
params:
- name: URL
description: The URL to fetch.
type: string
steps:
- name: curl-run
image: example.com/example/image:latest
script: |
curl $(params.URL) | tee $(results.output.path)
Cluster tasks
---
apiVersion: tekton.dev/v1
kind: TaskRun
metadata:
name: fetch-app
spec:
taskRef:
kind: ClusterTask
name: git-clone
params:
- name: url
value: https://git.example.com/app
- name: revision
value: main
- name: subdirectory
value: ""
Pipeline definition
---
apiVersion: tekton.dev/v1
kind: Pipeline
metadata:
name: my-pipeline
spec:
params:
- name: GIT_REPO
type: string
default: "example.com/app/repo"
workspaces:
- name: app-build
tasks:
- name: fetch-repository
taskRef:
name: git-clone
kind: ClusterTask
params:
- name: url
value: $(params.GIT_REPO)
workspaces:
- name: output
workspace: app-build
- name: run-lint
taskRef:
name: linter
kind: Task
params:
- name: DIRECTORY
value: "path/to/code"
workspaces:
- name: source
workspace: app-build
runAfter:
- fetch-repository
PipelineRun
---
apiVersion: tekton.dev/v1
kind: PipelineRun
metadata:
name: fetch-pipeline
spec:
pipelineRef:
name: my-pipeline
params:
- name: GIT_REPO
value: https://example.com/app/repo
Tekton
# Task
tkn t list
tkn t describe fetch-url
tkn t start fetch-url
# ClusterTask (deprecated)
tkn ct list
tkn ct describe openshift-client
# Pipeline
tkn p describe my-pipeline
tkn p start my-pipeline
tkn p start my-pipeline \
-w name=app-build,volumeClaimTemplateFile=pvc-template.yaml
# Pipeline Run
tkn pr list
tkn pr logs my-pipeline-1