# 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

image-1644549207088.png

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

 

image-1644971700185.png

Get more detailed information about the project

oc get all

image-1644971829839.png

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 ``` [![Build-webhook-secret.png](https://books.clusterapps.com/uploads/images/gallery/2022-02/scaled-1680-/M1C1o7oM4Qy2UFER-build-webhook-secret.png)](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 ``` [![webhook-addresses.png](https://books.clusterapps.com/uploads/images/gallery/2022-02/scaled-1680-/UH80jRrlKLj3y9Br-webhook-addresses.png)](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 ``` [![Screen Shot 2022-02-24 at 19.45.37.png](https://books.clusterapps.com/uploads/images/gallery/2022-02/scaled-1680-/PFypdnBThBD1xTil-screen-shot-2022-02-24-at-19-45-37.png)](https://books.clusterapps.com/uploads/images/gallery/2022-02/PFypdnBThBD1xTil-screen-shot-2022-02-24-at-19-45-37.png) [![Screen Shot 2022-02-24 at 19.47.15.png](https://books.clusterapps.com/uploads/images/gallery/2022-02/scaled-1680-/5EC8swNK8Zco0QHu-screen-shot-2022-02-24-at-19-47-15.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 ```