# OpenShift
# RHCA - EX180/EX280
Red Hat Certified Specialist in Containers and Kubernetes
Notes, tips and tricks for the EX180 and EX280 exams.
Podman host setup
dnf module install container-tools
dnf install -y buildah
Podman basics
Registry file: /etc/containers/registries.conf
Login to a registry
podman login registry.access.redhat.com
Search for images
podman search mariadb
Inspect images without downloading
skopeo inspect docker://registry.access.redhat.com/rhscl/mariadb-102-rhel7
Download images
podman pull registry.access.redhat.com/rhscl/mariadb-102-rhel7
List images
podman images
Inspect images. *Useful for locating config.user
podman inspect registry.access.redhat.com/rhscl/mariadb-102-rhel7:latest
Configure container volume path
sudo mkdir /srv/mariadb
sudo chown -R 27:27 /srv/mariadb # UID found in podman inspect
sudo semanage fcontext -a -t container_file_t "/srv/mariadb(/.*)"
sudo restorecon -Rv /srv/mariadb
Run image
-d detached
-e per variable
-p local_port:container_port
-v local/path:/path/in/pod
podman run -d -e MYSQL_USER=user \
-e MYSQL_PASSWORD=pass -e MYSQL_DATABASE=db \
-p 33306:3306 \
-v /srv/mariadb:/var/lib/mysql:Z \ # :Z isn't needed if SELinux manually configured
rhscl/mariadb-102-rhel7
Basic pod status and names
podman ps

Enter container in interactive shell
podman exec -it container-name /bin/bash
Commit changes to running image
podman commit container-name image-name
Export image
podman save image-name > /path/to/image.tar
Remove images
podman rmi image-name --force
Restore/Load image
podman load -i /path/to/image.tar
Dockerfile Basics
Container for scanning a network.
# Start with a base image
FROM registry.access.redhat.com/ubi8
# Maintainer information
MAINTAINER ClusterApps <mail@clusterapps.com>
# Run commands to build the container
# Do as much with the fewest RUN lines
RUN yum --assumeyes update && \
yum --assumeyes install \
nmap iproute procps-ng && \
bash && \
yum clean all
# Entrypoint is the command that run when the ccontainer starts
ENTRYPOINT ["/usr/bin/nmap"]
# The arguments for the entrypoint
CMD ["-sn", "192.168.252.0/24"]
Build the image with a tag
podman build -t notes180 .
List images
podman images
Run the image
podman run localhost/notes180
OpenShift Basics
Setup oc completion
source <(oc completion bash)
Create a new project
oc new-project nginx
Create a new application based on an image
oc new-app bitnami/nginx
Basic information about the project
oc status

Get more detailed information about the project
oc get all

Get full details about a pod
oc describe pod/nginx-84654f9574-9gpnt
Get full details about a deployment
oc describe deployment.apps/nginx
Application deployments
Generate a YAML file to use as a base.
oc create deployment nginx --image=bitnami/nginx --dry-run=client -o yaml > newapp.yml
Create a temporary deployment using the YAML file,
oc create -f newapp.yml
Create the service by exposing the deployment
# dry run to add to YAML file
oc expose deployment --port=8080 --dry-run=client nginx -o yaml >> newapp.yml
# run to create the service
oc expose deployment --port=8080 nginx
Expose the service
oc expose svc nginx --dry-run=client -o yaml >> newapp.yml
New app with parameters, as a deployment-config, and with labels (app=database)
```
oc new-app --name mdb -l app=database -e MYSQL_USER=dbuser -e MYSQL_PASSWORD=SuperAwesomePassword -e MYSQL_DATABASE=dbname bitnami/mysql --as-deployment-config
```
Delete the temporary application
oc delete all --all
Edit the YAML file and break up the sections into proper YAML.
Add ---
at the beginning of a section
Add ...
at the end of a section.
The new nginx app YAML file.
---
apiVersion: apps/v1
kind: Deployment
metadata:
creationTimestamp: null
labels:
app: nginx
name: nginx
spec:
replicas: 1
selector:
matchLabels:
app: nginx
strategy: {}
template:
metadata:
creationTimestamp: null
labels:
app: nginx
spec:
containers:
- image: bitnami/nginx
name: nginx
resources: {}
status: {}
...
---
apiVersion: v1
kind: Service
metadata:
creationTimestamp: null
labels:
app: nginx
name: nginx
spec:
ports:
- port: 8080
protocol: TCP
targetPort: 8080
selector:
app: nginx
status:
loadBalancer: {}
...
---
apiVersion: route.openshift.io/v1
kind: Route
metadata:
creationTimestamp: null
labels:
app: nginx
name: nginx
spec:
port:
targetPort: 8080
to:
kind: ""
name: nginx
weight: null
status: {}
...
Delete API resources defined by a YAML file.
oc delete -f newapp.yml
Using Templates
Get a list of templates
oc get templates -n openshift
Get template details
Review the parameters section for a list of environment variables.
oc describe template -n openshift mariadb-persistent
Create a new application from a template
oc new-app --template=mariadb-persistent \
-p MYSQL_USER=jack -p MYSQL_PASSWORD=password \
-p MYSQL_DATABASE=jack
Check the status of the deployment.
oc get all
oc describe pod/mariadb-1-qlvrj
Source 2 Image
Gety a list of templates and streams
oc new-app -L
Deploy a new app from a git repo
oc new-app php~https://github.com/clusterapps/simpleapp.git
Watch the app get built
oc logs -f buildconfig/simpleapp
Review the deployment with oc get all
NAME READY STATUS RESTARTS AGE
pod/mariadb-1-deploy 0/1 Completed 0 16m
pod/mariadb-1-qlvrj 1/1 Running 0 16m
pod/simpleapp-1-build 0/1 Completed 0 2m10s
pod/simpleapp-fb5554fd9-4kgnb 1/1 Running 0 89s
NAME DESIRED CURRENT READY AGE
replicationcontroller/mariadb-1 1 1 1 16m
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/mariadb ClusterIP 10.217.5.246 <none> 3306/TCP 16m
service/simpleapp ClusterIP 10.217.4.16 <none> 8080/TCP,8443/TCP 2m11s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/simpleapp 1/1 1 1 2m11s
NAME DESIRED CURRENT READY AGE
replicaset.apps/simpleapp-75c686cbb8 0 0 0 2m11s
replicaset.apps/simpleapp-fb5554fd9 1 1 1 89s
NAME REVISION DESIRED CURRENT TRIGGERED BY
deploymentconfig.apps.openshift.io/mariadb 1 1 1 config,image(mariadb:10.3-el8)
NAME TYPE FROM LATEST
buildconfig.build.openshift.io/simpleapp Source Git 1
NAME TYPE FROM STATUS STARTED DURATION
build.build.openshift.io/simpleapp-1 Source Git@26e2f16 Complete 2 minutes ago 41s
NAME IMAGE REPOSITORY TAGS UPDATED
imagestream.image.openshift.io/simpleapp default-route-openshift-image-registry.apps-crc.testing/nginx2/simpleapp latest About a minute ago
# Using oc to manage OpenShift
## The Basics
Examples of the basic need to know oc commands to deploy and manage containers on OpenShift.
Set a few variables to prevent sensitive information in the terminal history.
```
export REGISTRY_USERNAME=YourUsername
export REGISTRY_PASSWORD=SomePassword
export REGISTRY_HOST=quay.io
export REGISTRY_EMAIL=mail@example.com
export OSU=developer
export OSP=SuperAw3SomePassrd
```
### Pods
Using `oc explain` it is simple to get the ducumentation for the running version of OpenShift. Here are a few basics.
Get built-in documentation for Pods
```
oc explain pod
```
Get the pod spec
```
oc explain pod.spec
```
Details on the pod's containers
```
oc explain pod.spec.containers
```
Details about the pod's containers images
```
oc explain pod.spec.containers.image
```
Example of a pod file
```
apiVersion: v1
kind: Pod
metadata:
name: hello-world-pod
labels:
app: hello-world-pod
spec:
containers:
- env:
- name: MESSAGE
value: Hi there! You've run a pod.
image: docker.io/mcleary/helloworld-go
imagePullPolicy: Always
name: hello-world-override
resources: {}
```
Create a Pod on OpenShift based on a file
```
oc create -f pod.yaml
```
Use `oc get` to information from OpenShift
Get pods
```
oc get pods
```
Watch pods deploy
```
oc get pods --watch
```
Get all resources
```
oc get all
```
Access the shell of a running container. Use `oc get pods` to get the pod name.
```
oc rsh
```
Use port forwards to interact with the pod on the local machine. Get the pod name from the `oc get pods`.
```
oc port-forward :pod_port>
```
Delete OpenShift resources use the following syntax
```
oc delete
```
Delete a pod
```
oc delete pod
```
### Deployments
Deploy an existing image based on its tag
```
oc new-app mcleary/helloworld-go:latest --as-deployment-config
```
Deploy an application from Git
```
oc new-app https://github.com/clusterapps/helloworld-go.git --as-deployment-config
```
Follow build progress when using
```
oc logs -f bc/helloworld-go
```
Set the name for the DeploymentConfig
```
oc new-app mcleary/helloworld-go --name hello-app --as-deployment-config
```
DeploymentConfig with a parameter
```
oc new-app MESSAGE="This is a parameter" mcleary/helloworld-go --name hello-app --as-deployment-config
```
DeploymentConfig with many patameters
```
oc new-app mysql MYSQL_USER=user MYSQL_PASSWORD=pass MYSQL_DATABASE=testdb -l db=mysql
```
Get information about a DeploymentConfig
Describe the DC to get its labels
```
oc describe dc/hello-app
```
Get the full YAML definition
```
oc get -o yaml dc/hello-app
```
Roll out the latest version of the application
```
oc rollout latest dc/hello-app
```
Roll back to the previous version of the application
```
oc rollback dc/hello-app
```
**Scaling**
General Syntax
`oc scale dc/ --replicas=`
Manual scale to 3 pods
```
oc scale dc/helloworld-go --replicas=3
```
Scale down to one
```
oc scale dc/helloworld-go --replicas=1
```
General Syntax to create a HorizontalPodAutoscaler
```
oc autoscale dc/ \
--min \
--max \
--cpu-percent=
```
Auto scaling between 1 and 10 pods with an 80% CPU target
```
oc autoscale dc/helloworld-go \
--min 1 \
--max 10 \
--cpu-percent=80
```
Show the HPA
```
oc get hpa
```
Information on the HPA
```
oc describe hpa/helloworld-go
```
YAML for the HPA
```
oc get -o yaml hpa/helloworld-go
```
**Deleting resources**
Delete a single resource
```
oc delete
oc delete dc hello-app # Delete deployment config
oc delete svc hello-app # Delete a service
```
Delete all application resources using labels (get labels from oc describe)
```
oc delete all -l app=app=hello-app
```
Delete everything in a project
```
oc delete all --all
```
### Templates
Get a list of templates
Templates in the working namespace
```
oc get template
```
Get built-in templates
```
oc get templates -n openshift
```
Get template details
Full details of the template.
```
oc describe template -n openshift mariadb-persistent
````
Get just the parameters for a template.
```
oc process --parameters mysql-persistent -n openshift
```
Create a new application from a template
```
oc new-app --template=mariadb-persistent \
-p MYSQL_USER=jack -p MYSQL_PASSWORD=password \
-p MYSQL_DATABASE=jack
```
Check the status of the deployment and get the pod name
```
oc get all
```
Full details of a pod from a template.
```
oc describe pod/mariadb-1-qlvrj
```
### Networking
Access oc explain documentation
``
oc explain service
``
Get information about Service spec
```
oc explain service.spec
```
Get YAML definition for a service
```
oc get -o yaml service/hello-world
```
Get YAML definition for a route
```
oc get -o yaml route/hello-world
```
**Creating services**
Create a service for a single pod
```
oc expose --port 8080 pod/hello-world-pod
```
Create a service for a DeploymentConfig
```
oc expose --port 8080 dc/hello-world
```
Check that the service and pod are connected properly
```
oc status
```
Using Pod environment variables to find service Virtual IPs
Log into a pod. Get pod name from `oc get pods`
```
oc rsh pod/helloworld-2
```
Inside the pod, get all environment variables
```
env
```
Testing connectivity using environment variables with wget
```
wget -qO- $HELLOWORLD_GO_PORT_8080_TCP_ADDR:$HELLOWORLD_GO_PORT_8080_TCP_PORT
```
Creating Routes
Create a Route based on a Service. Get the service name from `oc get svc`
```
oc expose svc/helloworld-go
```
Get the Route URL
```
oc status
```
Check the route
```
curl helloworld-go-lab1.apps.okd4.example.com
```
### ConfigMaps
Create a ConfigMap using literal command line arguments
```
oc create configmap helloconfig --from-literal KEY="VALUE"
```
Create from a file
```
oc create configmap helloconfig --from-file=MESSAGE.txt
```
Create from a file with a key override
```
oc create configmap helloconfig --from-file=MESSAGE=MESSAGE.txt
```
Create using `--from-file` with a directory
```
oc create configmap helloconfig --from-file pods
```
Verify
```
oc get -o yaml configmap/helloconfig
```
Consuming ConfigMaps as Environment Variables
Set environment variables
```
oc set env dc/hello-app --from cm/helloconfig
```
### Secrets
Secrets use simalar syntax as ConfigMaps. Secrets are base64 encoded ConfigMaps
There are a few main types.
* Opaque
* Service Account Tokens
* Registry Authentication
* Simple Auth Types
A simple generic (Opaque) Secret. Key-Value pairs
```
oc create secret generic --from-literal KEY="VALUE"
```
Check the Secret
```
oc get -o yaml secret/
```
Consume the Secret as Environment Variables the same as ConfigMaps
```
oc set env dc/ --from secret/
```
Create a default registry secret
```
oc create secret docker-registry secret_name
--docker-server=$REGISTRY_HOST \
--docker-username=$REGISTRY_USERNAME \
--docker-password=$REGISTRY_PASSWORD \
--docker-email=$REGISTRY_EMAIL
```
Link the secret to the service account named "default"
```
oc secrets link default secret_name --for=pull
```
Check that the service account has the secret associated
```
oc describe serviceaccount/default
```
Decoding secrets. First get the hash.
* mysecret = name of secret
* password = is the entry to decode
```
oc get secret mysecret -o yaml |grep password
```
Result simalar to `password: cGFzc3dvcmQ=`
Decode as base64
```
echo "cGFzc3dvcmQ=" |base64 -d
```
### Images
**ImageStreams**
List ImageStreams
```
oc get is
```
List tags
```
oc get istag
```
Create the ImageStream but don't deploy
```
oc import-image --confirm quay.io/clearyme/helloworld-go
```
Importing any new images from the repository using the same command as importing a new.
```
oc import-image --confirm quay.io/clearyme/helloworld-go
```
Creating a new local tag : `oc tag `
```
oc tag $REGISTRY_HOST/$REGISTRY_USERNAME/helloworld-go:latest helloworld-go:local-1
```
Deploy an application based on your new ImageStream (lab1 is the name of the oc project with the newly tagged image)
```
oc new-app lab1/helloworld-go:local-1
```
**Build a custom image**
From within the directory where the `Dockerfile` is, build the image and tag it for the registry.
```
podman build -t $REGISTRY_HOST/$REGISTRY_USERNAME/helloworld-go:latest .
```
Log into a registry
```
podman login $REGISTRY_HOST
```
Push the image ti the registery
```
podman push $REGISTRY_HOST/$REGISTRY_USERNAME/helloworld-go
```
### Builds and BuildConfigs
Create a new BuildConfig from a Git repository URL
`oc new-build `
Example
```
oc new-build https://github.com/clusterapps/helloworld-go.git
```
Example new build from s branch
```
oc new-build https://github.com/clusterapps/helloworld-go.git#update
```
Example using --context-dir to build from a subdirectory
```
oc new-build https://github.com/clusterapps/openshift-playing.git --context-dir hello-world-go
```
**Working with existing BuildConfigs**
Get a list of BuildConfigs
```
oc get bc
```
Start a build
```
oc start-build bc/helloworld
```
Get the list of builds
```
oc get build
```
Get logs for a single build
```
oc logs -f build/helloworld-go-1
```
Get logs for the latest build for a BuildConfig
```
oc logs -f bc/helloworld-go
```
Use `oc cancel-build` to stop a build
``
```
oc cancel-build build/helloworld-go-1
```
**Working with WebHooks**
Get the secret token
```
oc get -o yaml buildconfig/helloworld-go
```
[](https://books.clusterapps.com/uploads/images/gallery/2022-02/M1C1o7oM4Qy2UFER-build-webhook-secret.png)
Export the secret as a variable
```
export GENERIC_SECRET=G_eQdWP67Sa8y38qlo4l
```
Get the webhook URL
```
oc describe buildconfig/helloworld-go
```
[](https://books.clusterapps.com/uploads/images/gallery/2022-02/UH80jRrlKLj3y9Br-webhook-addresses.png)
Copy the webhook URL and replace `` with $GENERIC_SECRET
```
curl -X POST -k
```
**Set build hooks**
Set a post-commit hook
```
oc set build-hook bc/helloworld-go \
--post-commit \
--script="echo Hello from build hook"
```
Check the logs output for "Hello from a build hook"
```
oc logs -f bc/helloworld-go
```
Check the events to see if it ran
```
oc get events
```
Remove a build hook
```
oc set build-hook bc/helloworld-go \
--post-commit \
--remove
```
**Source to Image**
The syntax is the same as normal builds. OpenShift uses S2I to guess the language when there is no Dockerfile. A language can also be specified at build time durring the `oc new-app`.
Works with Java, Ruby, Node, PHP, Python and PERL,
Overriding S2I Scripts
Assemble and Run are the two main scripts
Overrides go in your source at `.s2i/bin/assemble` or `.s2i/bin/run`
They need to call the original scripts, which are at `/usr/libexec/s2i/assemble` or `/usr/libexec/s2i/run`
New app without a `Dockerfile`.
```
oc new-app https://github.com/clusterapps/openshift-playing.git --context-dir s2i/ruby
```
New app specifying the language by adding _language tilda_ to the new-app command.
```
oc new-app ruby~https://github.com/clusterapps/openshift-playing.git --context-dir s2i/ruby
```
New app from a git branch
```
oc new-app https://github.com/clusterapps/helloworld-go.git#updates
```
### Volumes
Check out the built-in documentation
`oc explain persistentvolume.spec` or the The official [Kubernetes Documentation](https://kubernetes.io/docs/concepts/storage/volumes/) for Volumes
Most deployent of k8s and OpenShift may have a dynamic storageclass. You can just the dynamic storage by specifying the mountpoint and size. For manual storage, here are some examples.
Storage basics for containers.
Mount Volumes
**emptyDir**
`oc set volume dc/ --add --type emptyDir --mount-path `
Example: Add an emptyDir volume. An emptyDir is ephemeral. It will survive a pod reboot, but will be deleted when the pod is deleted. Only good for testing.
```
oc set volume dc/helloworld-go --add \
--type emptyDir \
--mount-path /emptydir
```
View the DeploymentConfig to view the volume information. Look for container.volumeMounts and volumes.
```
oc get -o yaml dc/helloworld-go
```
[](https://books.clusterapps.com/uploads/images/gallery/2022-02/PFypdnBThBD1xTil-screen-shot-2022-02-24-at-19-45-37.png)
[](https://books.clusterapps.com/uploads/images/gallery/2022-02/5EC8swNK8Zco0QHu-screen-shot-2022-02-24-at-19-47-15.png)
**ConfigMaps**
Basic example
`oc set volume --add --configmap-name -mount-path `
Create the configmap to use as a Volume
```
oc create configmap volume-file --from-literal file.txt="Contents"
```
Mount the ConfigMap
```
oc set volume dc/helloworld-go --add --configmap-name volume-file --mount-path /configs/
```
**NFS**
NFS is a very common storage method. It's cheap and easy to manage, but does have its own pitfalls.
Create an NFS persistent volume(PV) defination. A regular user cannot create PV.
```bash
cat > nfs.yml < nfspvc.yml <
```
If a build fails, after finding and fixing the issues, run the following command to request a new build:
```
oc start-build
```
Deployment logs
```
oc logs dc/
```
Temporarily access some of these missing commands is mounting the host binaries folders, such as /bin, /sbin, and /lib, as volumes inside the container
```
sudo podman run -it -v /bin:/bin image /bin/bash
```