{"id":4771,"date":"2023-07-01T09:01:27","date_gmt":"2023-07-01T07:01:27","guid":{"rendered":"http:\/\/miro.borodziuk.eu\/?p=4771"},"modified":"2023-09-22T09:19:35","modified_gmt":"2023-09-22T07:19:35","slug":"deploying-containerized-applications-on-openshift","status":"publish","type":"post","link":"http:\/\/miro.borodziuk.eu\/index.php\/2023\/07\/01\/deploying-containerized-applications-on-openshift\/","title":{"rendered":"Deploying Applications on OpenShift"},"content":{"rendered":"<p><span class=\"fontstyle0\">Red Hat OpenShift Container Platform is a set of modular components and services built on top of Red Hat CoreOS and Kubernetes. RHOCP adds PaaS capabilities such as remote management, increased security, monitoring and auditing, application life-cycle management, and self-service interfaces for developers. An OpenShift cluster is a Kubernetes cluster that can be managed the same way, but using the management tools provided by OpenShift, such as the command-line interface or the web console. This allows for more productive workflows and makes common tasks much easier.<\/span><\/p>\n<p><!--more--><\/p>\n<p><span class=\"fontstyle0\">The following schema illustrates the OpenShift Container Platform stack.<\/span><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-4776 aligncenter\" src=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/Openshift-schema.jpg\" alt=\"\" width=\"1272\" height=\"786\" srcset=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/Openshift-schema.jpg 1272w, http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/Openshift-schema-300x185.jpg 300w, http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/Openshift-schema-1024x633.jpg 1024w, http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/Openshift-schema-768x475.jpg 768w\" sizes=\"(max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px\" \/><\/p>\n<p><span class=\"fontstyle0\">The main method of interacting with an RHOCP cluster is using the <\/span><span class=\"fontstyle2\">oc <\/span><span class=\"fontstyle0\">command. The basic usage of the command is through its subcommands in the following syntax:<br \/>\n<\/span><\/p>\n<pre class=\"lang:default decode:true \">$ oc &lt;command&gt;<\/pre>\n<p><span class=\"fontstyle0\">Before interacting with a cluster, most operations require a logged-in user. The syntax to log in is shown below:<br \/>\n<\/span><\/p>\n<pre class=\"lang:default decode:true \">$ oc login &lt;clusterUrl&gt;<\/pre>\n<p>&nbsp;<\/p>\n<p><span style=\"color: #3366ff;\">Understanding Projects<\/span><\/p>\n<ul>\n<li>The Linux kernel provides namespaces to offer strict isolation between processes<\/li>\n<li>Kubernetes implements namespaces in a cluster environment<\/li>\n<li>To limit inter-namespace access<\/li>\n<li>To apply resource limitations on namespaces<\/li>\n<li>To delegate management tasks to users<\/li>\n<li>OpenShift implements Kubernetes namespaces as a vehicle to manage access to resources for users<\/li>\n<li>In OpenShift, namespaces are reserved for cluster-level admin access<\/li>\n<li>Users work with projects to store their resources<\/li>\n<\/ul>\n<p>To list projects:<\/p>\n<pre class=\"lang:default decode:true \">$ oc get projects<\/pre>\n<p>To list name spaces:<\/p>\n<pre class=\"lang:default decode:true \">$ oc get ns<\/pre>\n<p>&nbsp;<\/p>\n<p><span style=\"color: #3366ff;\">Understanding Applications<\/span><\/p>\n<ul>\n<li>After creating a project, <code>oc new-app<\/code> can be used to create an application<\/li>\n<li>While creating an application, different Kubernetes resources are created<\/li>\n<li>Deployment or deploymentconfig: the application itself, including its cluster properties<\/li>\n<li>Replicationcontroller or replicaset: takes care of running pods in a scalable way, using multiple instances<\/li>\n<li>Pod: the actual instance of the application that typically runs one container<\/li>\n<li>Service: a load balancer that exposes access to the application<\/li>\n<li>Route: the resource that allows incoming traffic by exposing an FQDN<\/li>\n<li>When Source 2 Image (S21) is used, additional resources are used as well<\/li>\n<\/ul>\n<p>This command gives everything which is available with new-app:<\/p>\n<pre class=\"lang:default decode:true \">$ oc new-app -h | less<\/pre>\n<p>Let&#8217;s create a nginx app:<\/p>\n<pre class=\"lang:default decode:true \">$ oc new-app bitnami\/nginx\r\n--&gt; Found container image 80e927a (2 days old) from Docker Hub for \"bitnami\/nginx\"\r\n\r\n* An image stream tag will be created as \"nginx:latest\" that will track this image\r\n\r\n--&gt; Creating resources ...\r\ndeployment.apps \"nginx\" created\r\nservice \"nginx\" created\r\n--&gt; Success\r\nApplication is not exposed. You can expose services to the outside world by executing one or more of the commands below:\r\n'oc expose service\/nginx'\r\nRun 'oc status' to view your app.<\/pre>\n<p>To show all resources currently running in this encvironment use:<\/p>\n<pre class=\"lang:default decode:true \">$ oc get all<\/pre>\n<p>To see imagestreams:<\/p>\n<pre class=\"lang:default decode:true \">$ oc get imagestream<\/pre>\n<p>&nbsp;<\/p>\n<p><span style=\"color: #3366ff;\">Understanding Resources<\/span><\/p>\n<ul>\n<li>To run an application in OpenShift, different Kubernetes resources are used<\/li>\n<li>Each resource is defined in the API to offer specific functionality<\/li>\n<li>The API defines how resources connect to each other<\/li>\n<li>Many resources are used to define how a component in the cluster is running:\n<ul>\n<li>Pods define how containers are started using images<\/li>\n<li>Services implement a load balancer to distribute incoming workload<\/li>\n<li>Routes define an FUN for accessing the application<\/li>\n<\/ul>\n<\/li>\n<li>Resources are defined in the API<\/li>\n<\/ul>\n<p>&nbsp;<\/p>\n<p><span style=\"color: #3366ff;\">Monitoring Applications<\/span><\/p>\n<ul>\n<li>The Pod is the representation of the running processes<\/li>\n<li>To see process STDOUT, use <code>oc logs podname<\/code><\/li>\n<li>To see how the Pods (as well as other resources) are created in the cluster, use <code>oc describe <\/code><\/li>\n<li>To see all status information about the Pods, use<code> oc get pod podname -o yaml<\/code><\/li>\n<\/ul>\n<p>Openshift has awesome command line completion:<\/p>\n<pre class=\"lang:default decode:true \">$ oc completion -h\r\n...\r\n  ## Load the oc completion code for bash into the current shell\r\n  <strong>source &lt;(oc completion bash)<\/strong>\r\n  ## Write bash completion code to a file and source it from .bash_profile\r\n  oc completion bash &gt; ~\/.kube\/completion.bash.inc\r\n  printf \"\r\n  # Kubectl shell completion\r\n  source '$HOME\/.kube\/completion.bash.inc'\r\n  \" &gt;&gt; $HOME\/.bash_profile\r\n  source $HOME\/.bash_profile\r\n  \r\n...<\/pre>\n<p>Let&#8217;s enable this feature:<\/p>\n<pre class=\"lang:default decode:true \">$ source &lt;(oc completion bash)<\/pre>\n<p>And we can use it:<\/p>\n<pre class=\"lang:default decode:true \">$ oc &lt;tab&gt;&lt;tab&gt;\r\nadm            (Tools for managing a cluster)                                                                     logout         (End the current server session)\r\nannotate       (Update the annotations on a resource)                                                             logs           (Print the logs for a container in a pod)\r\napi-resources  (Print the supported API resources on the server)                                                  new-app        (Create a new application)\r\napi-versions   (Print the supported API versions on the server, in the form of \"group\/version\")                   new-build      (Create a new build configuration)\r\napply          (Apply a configuration to a resource by file name or stdin)                                        new-project    (Request a new project)\r\nattach         (Attach to a running container)                                                                    observe        (Observe changes to resources and react to them (experimental))\r\nauth           (Inspect authorization)                                                                            options\r\nautoscale      (Autoscale a deployment config, deployment, replica set, stateful set, or replication controller)  patch          (Update fields of a resource)\r\ncancel-build   (Cancel running, pending, or new builds)                                                           plugin         (Provides utilities for interacting with plugins)\r\ncluster-info   (Display cluster information)                                                                      policy         (Manage authorization policy)\r\ncompletion     (Output shell completion code for the specified shell (bash, zsh, fish, or powershell))            port-forward   (Forward one or more local ports to a pod)\r\nconfig         (Modify kubeconfig files)                                                                          process        (Process a template into list of resources)\r\n--More--<\/pre>\n<p>Lets deploy mariadb:<\/p>\n<pre class=\"lang:default decode:true \">$ oc create &lt;tab&gt;&lt;tab&gt;\r\nbuild                 (Create a new build)                                                           namespace             (Create a namespace with the specified name)\r\nclusterresourcequota  (Create a cluster resource quota)                                              poddisruptionbudget   (Create a pod disruption budget with the specified name)\r\nclusterrole           (Create a cluster role)                                                        priorityclass         (Create a priority class with the specified name)\r\nclusterrolebinding    (Create a cluster role binding for a particular cluster role)                  quota                 (Create a quota with the specified name)\r\nconfigmap             (Create a config map from a local file, directory or literal value)            role                  (Create a role with single rule)\r\ncronjob               (Create a cron job with the specified name)                                    rolebinding           (Create a role binding for a particular role or cluster role)\r\ndeployment            (Create a deployment with the specified name)                                  route                 (Expose containers externally via secured routes)\r\ndeploymentconfig      (Create a deployment config with default options that uses a given image)      secret                (Create a secret using specified subcommand)\r\nidentity              (Manually create an identity (only needed if automatic creation is disabled))  service               (Create a service using a specified subcommand)\r\nimagestream           (Create a new empty image stream)                                              serviceaccount        (Create a service account with the specified name)\r\nimagestreamtag        (Create a new image stream tag)                                                token                 (Request a service account token)\r\ningress               (Create an ingress with the specified name)                                    user                  (Manually create a user (only needed if automatic creation is disabled))\r\njob                   (Create a job with the specified name)                                         useridentitymapping   (Manually map an identity to a user)\r\n\r\n$ oc create deployment -h\r\nCreate a deployment with the specified name.\r\n\r\nAliases:\r\ndeployment, deploy\r\n\r\nExamples:\r\n  # Create a deployment named my-dep that runs the busybox image\r\n  oc create deployment my-dep --image=busybox\r\n  \r\n  # Create a deployment with a command\r\n  oc create deployment my-dep --image=busybox -- date\r\n  \r\n  # Create a deployment named my-dep that runs the nginx image with 3 replicas\r\n  oc create deployment my-dep --image=nginx --replicas=3\r\n  \r\n  # Create a deployment named my-dep that runs the busybox image and expose port 5701\r\n  oc create deployment my-dep --image=busybox --port=5701\r\n\r\nOptions:\r\n    --allow-missing-template-keys=true:\r\n        If true, ignore any errors in templates when a field or map key is missing in the template. Only applies to\r\n        golang and jsonpath output formats.\r\n\r\n    --dry-run='none':\r\n        Must be \"none\", \"server\", or \"client\". If client strategy, only print the object that would be sent, without\r\n        sending it. If server strategy, submit server-side request without persisting the resource.\r\n\r\n    --field-manager='kubectl-create':\r\n        Name of the manager used to track field ownership.\r\n\r\n    --image=[]:\r\n        Image names to run.\r\n\r\n    -o, --output='':\r\n        Output format. One of: (json, yaml, name, go-template, go-template-file, template, templatefile, jsonpath,\r\n        jsonpath-as-json, jsonpath-file).\r\n\r\n    --port=-1:\r\n        The port that this container exposes.\r\n\r\n    -r, --replicas=1:\r\n        Number of replicas to create. Default is 1.\r\n\r\n    --save-config=false:\r\n        If true, the configuration of current object will be saved in its annotation. Otherwise, the annotation will\r\n        be unchanged. This flag is useful when you want to perform kubectl apply on this object in the future.\r\n\r\n    --show-managed-fields=false:\r\n        If true, keep the managedFields when printing objects in JSON or YAML format.\r\n\r\n    --template='':\r\n        Template string or path to template file to use when -o=go-template, -o=go-template-file. The template format\r\n        is golang templates [http:\/\/golang.org\/pkg\/text\/template\/#pkg-overview].\r\n\r\n    --validate='ignore':\r\n        Must be one of: strict (or true), warn, ignore (or false).              \"true\" or \"strict\" will use a schema to validate\r\n        the input and fail the request if invalid. It will perform server side validation if ServerSideFieldValidation\r\n        is enabled on the api-server, but will fall back to less reliable client-side validation if not.                \"warn\" will\r\n        warn about unknown or duplicate fields without blocking the request if server-side field validation is enabled\r\n        on the API server, and behave as \"ignore\" otherwise.            \"false\" or \"ignore\" will not perform any schema\r\n        validation, silently dropping any unknown or duplicate fields.\r\n\r\nUsage:\r\n  oc create deployment NAME --image=image -- [COMMAND] [args...] [options]\r\n\r\nUse \"oc options\" for a list of global command-line options (applies to all commands).\r\n\r\n$ oc create deployment mymariadb --image=mariadb\r\ndeployment.apps\/mymariadb created<\/pre>\n<p>Let&#8217;s chceck the deployment:<\/p>\n<pre class=\"lang:default decode:true \">$ oc get pods --show-labels\r\nNAME                                         READY   STATUS             RESTARTS      AGE    LABELS\r\nmymariadb-7b9b9f8bbf-hslm2                   0\/1     CrashLoopBackOff   8 (44s ago)   16m    app=mymariadb,pod-template-hash=7b9b9f8bbf\r\nnginx-7548849bf9-95t6v                       1\/1     Running            0             103m   deployment=nginx,pod-template-hash=7548849bf9<\/pre>\n<p>More information about mymariadb you will have using this command:<\/p>\n<pre class=\"lang:default decode:true \">$ oc get all --selector app=mymariadb\r\nNAME                             READY   STATUS             RESTARTS        AGE\r\npod\/mymariadb-7b9b9f8bbf-hslm2   0\/1     CrashLoopBackOff   8 (2m25s ago)   18m\r\n\r\nNAME                        READY   UP-TO-DATE   AVAILABLE   AGE\r\ndeployment.apps\/mymariadb   0\/1     1            0           18m\r\n\r\nNAME                                   DESIRED   CURRENT   READY   AGE\r\nreplicaset.apps\/mymariadb-7b9b9f8bbf   1         1         0       18m<\/pre>\n<p>And::<\/p>\n<pre class=\"lang:default decode:true\">$ oc describe pod mymariadb-7b9b9f8bbf-hslm2\r\nName:                 mymariadb-7b9b9f8bbf-hslm2\r\n...\r\nContainers:\r\n  mariadb:\r\n    Container ID:   cri-o:\/\/741766fa717fad0ee4d1597d6763119b2503e823ff9342ea2b940914c20acc92\r\n    Image:          mariadb\r\n    Image ID:       docker.io\/library\/mariadb@sha256:c57215c3aabbdb96353ce8b89a7bd6be089f49a4d6bb37f199288a1bf0438a02\r\n    Port:           &lt;none&gt;\r\n    Host Port:      &lt;none&gt;\r\n    State:          Waiting\r\n      Reason:       CrashLoopBackOff\r\n   <strong> Last State:     Terminated<\/strong>\r\n      Reason:       Error\r\n      Exit Code:    1\r\n      Started:      Sun, 23 Jul 2023 08:50:48 +0000\r\n      Finished:     Sun, 23 Jul 2023 08:50:48 +0000\r\n...\r\n                             node.kubernetes.io\/unreachable:NoExecute op=Exists for 300s\r\nEvents:\r\n  Type     Reason          Age                 From               Message\r\n  ----     ------          ----                ----               -------\r\n  Normal   Scheduled       21m                 default-scheduler  Successfully assigned makarewicz-openshift-dev\/mymariadb-7b9b9f8bbf-hslm2 to ip-10-0-196-172.ec2.internal\r\n  Normal   AddedInterface  21m                 multus             Add eth0 [10.128.7.143\/23] from ovn-kubernetes\r\n  Normal   Pulled          21m                 kubelet            Successfully pulled image \"mariadb\" in 228.415356ms (228.424278ms including waiting)\r\n  Normal   Pulled          21m                 kubelet            Successfully pulled image \"mariadb\" in 231.980697ms (231.988386ms including waiting)\r\n  Normal   Pulled          21m                 kubelet            Successfully pulled image \"mariadb\" in 1.580646376s (1.580656116s including waiting)\r\n  Normal   Created         20m (x4 over 21m)   kubelet            Created container mariadb\r\n  Normal   Started         20m (x4 over 21m)   kubelet            Started container mariadb\r\n  Normal   Pulled          20m                 kubelet            Successfully pulled image \"mariadb\" in 253.779517ms (253.790607ms including waiting)\r\n  Normal   Pulling         19m (x5 over 21m)   kubelet            Pulling image \"mariadb\"\r\n  Warning  BackOff         74s (x95 over 21m)  kubelet            Back-off restarting failed container mariadb in pod mymariadb-7b9b9f8bbf-hslm2_makarewicz-openshift-dev(8deffe2e-3341-4b11-9258-28b29cd00004)<\/pre>\n<p>As we see the last state of apllication is Terminated.<\/p>\n<p>Let&#8217;s check the logs:<\/p>\n<pre class=\"lang:default decode:true \">$ oc logs mymariadb-7b9b9f8bbf-hslm2\r\n2023-07-23 08:55:59+00:00 [Note] [Entrypoint]: Entrypoint script for MariaDB Server 1:11.0.2+maria~ubu2204 started.\r\n2023-07-23 08:55:59+00:00 [ERROR] [Entrypoint]: Database is uninitialized and password option is not specified\r\n        You need to specify one of MARIADB_ROOT_PASSWORD, MARIADB_ROOT_PASSWORD_HASH, MARIADB_ALLOW_EMPTY_ROOT_PASSWORD and MARIADB_RANDOM_ROOT_PASSWORD<\/pre>\n<p>The reason why there is an error with mariadb is we didn&#8217;t specify password.<\/p>\n<p>We can delete deployment:<\/p>\n<pre class=\"lang:default decode:true \">$ oc logs mymariadb-7b9b9f8bbf-hslm2\r\n2023-07-23 08:55:59+00:00 [Note] [Entrypoint]: Entrypoint script for MariaDB Server 1:11.0.2+maria~ubu2204 started.\r\n2023-07-23 08:55:59+00:00 [ERROR] [Entrypoint]: Database is uninitialized and password option is not specified\r\n        You need to specify one of MARIADB_ROOT_PASSWORD, MARIADB_ROOT_PASSWORD_HASH, MARIADB_ALLOW_EMPTY_ROOT_PASSWORD and MARIADB_RANDOM_ROOT_PASSWORD<\/pre>\n<p>&nbsp;<\/p>\n<p><span style=\"color: #3366ff;\">Understanding the API<\/span><\/p>\n<ul>\n<li>OpenShift is based on the Kubernetes APIs<\/li>\n<li>On top of the Kubernetes APIs, OpenShift-specific APIs are added<\/li>\n<li>OpenShift uses Kubernetes resources, but in many cases offers its own functionality using different APIs<\/li>\n<li>As a result, OpenShift resources are not always guaranteed to be compatible with Kubernetes resources<\/li>\n<\/ul>\n<p><span style=\"color: #3366ff;\">Exploring the APIs<\/span><\/p>\n<ul>\n<li><code>oc api-resources<\/code> shows resources as defined in the API<\/li>\n<li><code>oc api-versions<\/code> shows versions of the APIs<\/li>\n<li><code>oc explain [--recursive]<\/code> can be used to explore what is in the APIs<\/li>\n<li>Based on this information, OpenShift resources can be defined in a declarative way in YAML files<\/li>\n<\/ul>\n<p>&nbsp;<\/p>\n<p><span style=\"color: #3366ff;\">Lab: Managing Resources<\/span><\/p>\n<ol>\n<li>As user developer, create a project with the name <code>myproject <\/code><\/li>\n<li>In this project, create an application using <code>oc new-app<\/code><\/li>\n<li>Use <code>oc -h<\/code> to find usage information about this command<\/li>\n<li>Ensure that the application was created successfully<\/li>\n<li>Write the resources created with the <code>oc new-app<\/code> command to a YAML file such that the resource can easily be recreated<\/li>\n<\/ol>\n<p>Solution<\/p>\n<pre class=\"lang:default decode:true \">$ oc whoami\r\nmakarewicz-openshift\r\n\r\n$ oc login -u developer -p developer\r\nLogin failed (401 Unauthorized)\r\nVerify you have provided the correct credentials.\r\n\r\n$ oc new-app -h\r\nCreate a new application by specifying source code, templates, and\/or images.\r\n\r\n This command will try to build up the components of an application using images, templates, or code that has a public\r\nrepository. It will look up the images on the local container storage (if available), a container image registry, an\r\nintegrated image stream, or stored templates.\r\n\r\n If you specify a source code URL, it will set up a build that takes your source code and converts it into an image that\r\ncan run inside of a pod. Local source must be in a git repository that has a remote repository that the server can see.\r\nThe images will be deployed via a deployment or deployment configuration, and a service will be connected to the first\r\npublic port of the app. You may either specify components using the various existing flags or let oc new-app autodetect\r\nwhat kind of components you have provided.\r\n\r\n If you provide source code, a new build will be automatically triggered. You can use 'oc status' to check the progress.\r\n\r\nExamples:\r\n  # List all local templates and image streams that can be used to create an app\r\n  oc new-app --list\r\n  \r\n  # Create an application based on the source code in the current git repository (with a public remote) and a container\r\nimage\r\n  oc new-app . --image=registry\/repo\/langimage\r\n  \r\n  # Create an application myapp with Docker based build strategy expecting binary input\r\n  oc new-app  --strategy=docker --binary --name myapp\r\n  \r\n  # Create a Ruby application based on the provided [image]~[source code] combination\r\n  oc new-app centos\/ruby-25-centos7~https:\/\/github.com\/sclorg\/ruby-ex.git\r\n  \r\n  # Use the public container registry MySQL image to create an app. Generated artifacts will be labeled with db=mysql\r\n  oc new-app mysql MYSQL_USER=user MYSQL_PASSWORD=pass MYSQL_DATABASE=testdb -l db=mysql\r\n  \r\n  # Use a MySQL image in a private registry to create an app and override application artifacts' names\r\n  oc new-app --image=myregistry.com\/mycompany\/mysql --name=private\r\n  \r\n  # Create an application from a remote repository using its beta4 branch\r\n  oc new-app https:\/\/github.com\/openshift\/ruby-hello-world#beta4\r\n  \r\n  # Create an application based on a stored template, explicitly setting a parameter value\r\n  oc new-app --template=ruby-helloworld-sample --param=MYSQL_USER=admin\r\n  \r\n  # Create an application from a remote repository and specify a context directory\r\n  oc new-app https:\/\/github.com\/youruser\/yourgitrepo --context-dir=src\/build\r\n  \r\n  # Create an application from a remote private repository and specify which existing secret to use\r\n  oc new-app https:\/\/github.com\/youruser\/yourgitrepo --source-secret=yoursecret\r\n  \r\n  # Create an application based on a template file, explicitly setting a parameter value\r\n  oc new-app --file=.\/example\/myapp\/template.json --param=MYSQL_USER=admin\r\n  \r\n  # Search all templates, image streams, and container images for the ones that match \"ruby\"\r\n  oc new-app --search ruby\r\n  \r\n  # Search for \"ruby\", but only in stored templates (--template, --image-stream and --image\r\n  # can be used to filter search results)\r\n  oc new-app --search --template=ruby\r\n  \r\n  # Search for \"ruby\" in stored templates and print the output as YAML\r\n  oc new-app --search --template=ruby --output=yaml\r\n\r\nOptions:\r\n    --allow-missing-images=false:\r\n        If true, indicates that referenced container images that cannot be found locally or in a registry should still\r\n        be used.\r\n\r\n    --allow-missing-imagestream-tags=false:\r\n        If true, indicates that image stream tags that don't exist should still be used.\r\n\r\n    --allow-missing-template-keys=false:\r\n        If true, ignore any errors in templates when a field or map key is missing in the template. Only applies to\r\n        golang and jsonpath output formats.\r\n\r\n    --as-deployment-config=false:\r\n        If true create this application as a deployment config, which allows for hooks and custom strategies.\r\n\r\n    --as-test=false:\r\n        If true create this application as a test deployment, which validates that the deployment succeeds and then\r\n        scales down.\r\n\r\n    --binary=false:\r\n        Instead of expecting a source URL, set the build to expect binary contents. Will disable triggers.\r\n\r\n    --build-env=[]:\r\n        Specify a key-value pair for an environment variable to set into each build image.\r\n\r\n    --build-env-file=[]:\r\n        File containing key-value pairs of environment variables to set into each build image.\r\n\r\n    --code=[]:\r\n        Source code to use to build this application.\r\n\r\n    --context-dir='':\r\n        Context directory to be used for the build.\r\n\r\n    --dry-run=false:\r\n        If true, show the result of the operation without performing it.\r\n\r\n    -e, --env=[]:\r\n        Specify a key-value pair for an environment variable to set into each container.\r\n\r\n    --env-file=[]:\r\n        File containing key-value pairs of environment variables to set into each container.\r\n\r\n    -f, --file=[]:\r\n        Path to a template file to use for the app.\r\n\r\n    --grant-install-rights=false:\r\n        If true, a component that requires access to your account may use your token to install software into your\r\n        project. Only grant images you trust the right to run with your token.\r\n\r\n    --group=[]:\r\n        Indicate components that should be grouped together as &lt;comp1&gt;+&lt;comp2&gt;.\r\n\r\n    --ignore-unknown-parameters=false:\r\n        If true, will not stop processing if a provided parameter does not exist in the template.\r\n\r\n    --image=[]:\r\n        Name of a container image to include in the app.  Note:  not specifying a registry or repository means\r\n        defaults in place for client image pulls are employed.\r\n\r\n    -i, --image-stream=[]:\r\n        Name of an existing image stream to use to deploy an app.\r\n\r\n    --insecure-registry=false:\r\n        If true, indicates that the referenced container images are on insecure registries and should bypass\r\n        certificate checking\r\n\r\n    -l, --labels='':\r\n        Label to set in all resources for this application.\r\n\r\n    -L, --list=false:\r\n        List all local templates and image streams that can be used to create.\r\n\r\n    --name='':\r\n        Set name to use for generated application artifacts\r\n\r\n    --no-install=false:\r\n        Do not attempt to run images that describe themselves as being installable\r\n\r\n    -o, --output='':\r\n        Output format. One of: (json, yaml, name, go-template, go-template-file, template, templatefile, jsonpath,\r\n        jsonpath-as-json, jsonpath-file).\r\n\r\n    --output-version='':\r\n        The preferred API versions of the output objects\r\n\r\n    -p, --param=[]:\r\n        Specify a key-value pair (e.g., -p FOO=BAR) to set\/override a parameter value in the template.\r\n\r\n    --param-file=[]:\r\n        File containing parameter values to set\/override in the template.\r\n\r\n    -S, --search=false:\r\n        Search all templates, image streams, and container images that match the arguments provided. Note: the\r\n        container images search is run on the OpenShift cluster via the ImageStreamImport API.\r\n\r\n    -a, --show-all=false:\r\n        When printing, show all resources (default hide terminated pods.)\r\n\r\n    --show-labels=false:\r\n        When printing, show all labels as the last column (default hide labels column)\r\n\r\n    --show-managed-fields=false:\r\n        If true, keep the managedFields when printing objects in JSON or YAML format.\r\n\r\n    --sort-by='':\r\n        If non-empty, sort list types using this field specification.  The field specification is expressed as a\r\n        JSONPath expression (e.g. '{.metadata.name}'). The field in the API resource specified by this JSONPath\r\n        expression must be an integer or a string.\r\n\r\n    --source-secret='':\r\n        The name of an existing secret that should be used for cloning a private git repository.\r\n\r\n    --strategy=:\r\n        Specify the build strategy to use if you don't want to detect (docker|pipeline|source). NOTICE: the pipeline\r\n        strategy is deprecated; consider using Jenkinsfiles directly on Jenkins or OpenShift Pipelines.\r\n\r\n    --template=[]:\r\n        Name of a stored template to use in the app.\r\n\r\nUsage:\r\n  oc new-app (IMAGE | IMAGESTREAM | TEMPLATE | PATH | URL ...) [flags] [options]\r\n\r\nUse \"oc options\" for a list of global command-line options (applies to all commands).\r\nbash-4.4 ~ $ \r\nbash-4.4 ~ $ oc new-app nginx bitnami\/nginx\r\n--&gt; Found image 80e927a (2 days old) in image stream \"makarewicz-openshift-dev\/nginx\" under tag \"latest\" for \"nginx\"\r\n\r\n\r\n--&gt; Found container image 80e927a (2 days old) from Docker Hub for \"bitnami\/nginx\"\r\n\r\n    * An image stream tag will be created as \"nginx-1:latest\" that will track this image\r\n\r\n--&gt; Creating resources ...\r\n    error: deployments.apps \"nginx\" already exists\r\n    imagestream.image.openshift.io \"nginx-1\" created\r\n    deployment.apps \"nginx-1\" created\r\n    error: services \"nginx\" already exists\r\n    service \"nginx-1\" created\r\n--&gt; Failed\r\nbash-4.4 ~ $ oc new-app nginx bitnami\/nginx --dry-run=true -o yaml &gt; nynewapp.yaml\r\nbash-4.4 ~ $ oc delete Lesson 3 Lab: Managing Resources \r\nerror: the server doesn't have a resource type \"Lesson\"\r\nbash-4.4 ~ $ oc delete bitnami\/nginx                    \r\nerror: the server doesn't have a resource type \"bitnami\"\r\n\r\n\r\n$ oc new-app nginx bitnami\/nginx --dry-run=true -o yaml &gt; nynewapp.yaml\r\n\r\n$ cat nynewapp.yaml\r\napiVersion: v1\r\nitems:\r\n- apiVersion: apps\/v1\r\nkind: Deployment\r\nmetadata:\r\nannotations:\r\nimage.openshift.io\/triggers: '[{\"from\":{\"kind\":\"ImageStreamTag\",\"name\":\"nginx:latest\",\"namespace\":\"makarewicz-openshift-dev\"},\"fieldPath\":\"spec.template.spec.containers[?(@.name==\\\"nginx\\\")].image\"}]'\r\nopenshift.io\/generated-by: OpenShiftNewApp\r\ncreationTimestamp: null\r\nlabels:\r\napp: nginx\r\napp.kubernetes.io\/component: nginx\r\napp.kubernetes.io\/instance: nginx\r\nname: nginx\r\nspec:\r\nreplicas: 1\r\nselector:\r\nmatchLabels:\r\ndeployment: nginx\r\nstrategy: {}\r\ntemplate:\r\nmetadata:\r\nannotations:\r\nopenshift.io\/generated-by: OpenShiftNewApp\r\ncreationTimestamp: null\r\nlabels:\r\ndeployment: nginx\r\nspec:\r\ncontainers:\r\n- image: ' '\r\nname: nginx\r\nports:\r\n- containerPort: 8080\r\nprotocol: TCP\r\n- containerPort: 8443\r\nprotocol: TCP\r\nresources: {}\r\nstatus: {}\r\n- apiVersion: apps\/v1\r\nkind: Deployment\r\nmetadata:\r\nannotations:\r\nimage.openshift.io\/triggers: '[{\"from\":{\"kind\":\"ImageStreamTag\",\"name\":\"nginx-1:latest\"},\"fieldPath\":\"spec.template.spec.containers[?(@.name==\\\"nginx-1\\\")].image\"}]'\r\nopenshift.io\/generated-by: OpenShiftNewApp\r\ncreationTimestamp: null\r\nlabels:\r\napp: nginx\r\napp.kubernetes.io\/component: nginx\r\napp.kubernetes.io\/instance: nginx\r\nname: nginx-1\r\nspec:\r\nreplicas: 1\r\nselector:\r\nmatchLabels:\r\ndeployment: nginx-1\r\nstrategy: {}\r\ntemplate:\r\nmetadata:\r\nannotations:\r\nopenshift.io\/generated-by: OpenShiftNewApp\r\ncreationTimestamp: null\r\nlabels:\r\ndeployment: nginx-1\r\nspec:\r\ncontainers:\r\n- image: ' '\r\nname: nginx-1\r\nports:\r\n- containerPort: 8080\r\nprotocol: TCP\r\n- containerPort: 8443\r\nprotocol: TCP\r\nresources: {}\r\nstatus: {}\r\n- apiVersion: v1\r\nkind: Service\r\nmetadata:\r\nannotations:\r\nopenshift.io\/generated-by: OpenShiftNewApp\r\ncreationTimestamp: null\r\nlabels:\r\napp: nginx\r\napp.kubernetes.io\/component: nginx\r\napp.kubernetes.io\/instance: nginx\r\nname: nginx\r\nspec:\r\nports:\r\n- name: 8080-tcp\r\nport: 8080\r\nprotocol: TCP\r\ntargetPort: 8080\r\n- name: 8443-tcp\r\nport: 8443\r\nprotocol: TCP\r\ntargetPort: 8443\r\nselector:\r\ndeployment: nginx\r\nstatus:\r\nloadBalancer: {}\r\n- apiVersion: v1\r\nkind: Service\r\nmetadata:\r\nannotations:\r\nopenshift.io\/generated-by: OpenShiftNewApp\r\ncreationTimestamp: null\r\nlabels:\r\napp: nginx\r\napp.kubernetes.io\/component: nginx\r\napp.kubernetes.io\/instance: nginx\r\nname: nginx-1\r\nspec:\r\nports:\r\n- name: 8080-tcp\r\nport: 8080\r\nprotocol: TCP\r\ntargetPort: 8080\r\n- name: 8443-tcp\r\nport: 8443\r\nprotocol: TCP\r\ntargetPort: 8443\r\nselector:\r\ndeployment: nginx-1\r\nstatus:\r\nloadBalancer: {}\r\nkind: List\r\nmetadata: {}\r\n\r\n$ oc create -f nynewapp.yaml\r\ndeployment.apps\/nginx created\r\ndeployment.apps\/nginx-1 created\r\nservice\/nginx created\r\nservice\/nginx-1 created\r\n\r\n$ oc get all\r\nNAME READY STATUS RESTARTS AGE\r\npod\/nginx-1-658588fd7-rw6nn 1\/1 Running 0 72s\r\npod\/nginx-7548849bf9-mddnj 1\/1 Running 0 72s<\/pre>\n<p>&nbsp;<\/p>\n<p><span style=\"color: #3366ff;\">Deploying a Database Server on OpenShift<\/span><\/p>\n<p>Let&#8217;s l<span class=\"fontstyle0\">og in to the OpenShift cluster<\/span> :<\/p>\n<pre class=\"lang:default decode:true \"># oc login -u system:admin\r\nLogged into \"https:\/\/172.30.9.22:8443\" as \"system:admin\" using existing credentials.\r\n\r\nYou have access to the following projects and can switch between them with 'oc project &lt;projectname&gt;':\r\n\r\n  * chapter1\r\n    default\r\n    kube-dns\r\n    kube-proxy\r\n    kube-public\r\n    kube-system\r\n    myproject\r\n    openshift\r\n    openshift-apiserver\r\n    openshift-controller-manager\r\n    openshift-core-operators\r\n    openshift-infra\r\n    openshift-node\r\n    openshift-service-cert-signer\r\n    openshift-web-console\r\n\r\nUsing project \"chapter1\".\r\n<\/pre>\n<p><span class=\"fontstyle0\">Create a new project:<\/span><\/p>\n<pre class=\"lang:default decode:true\"># oc new-project mysql-openshift\r\nNow using project \"mysql-openshift\" on server \"https:\/\/172.30.9.22:8443\".\r\n\r\nYou can add applications to this project with the 'new-app' command. For example, try:\r\n\r\n    oc new-app centos\/ruby-25-centos7~https:\/\/github.com\/sclorg\/ruby-ex.git\r\n\r\nto build a new example application in Ruby.\r\n<\/pre>\n<p><span class=\"fontstyle0\">Create a new application <\/span><span class=\"fontstyle0\">container image using the <\/span><code><span class=\"fontstyle2\">oc new-app <\/span><\/code><span class=\"fontstyle0\">command.<br \/>\nThis image requires that you use the <\/span><code><span class=\"fontstyle2\">-e <\/span><\/code><span class=\"fontstyle0\">option to set the <\/span><code><span class=\"fontstyle2\">MYSQL_USER<\/span><\/code><span class=\"fontstyle0\">, <\/span><code><span class=\"fontstyle2\">MYSQL_PASSWORD<\/span><\/code><span class=\"fontstyle0\">, <\/span><code><span class=\"fontstyle2\">MYSQL_DATABASE<\/span><\/code><span class=\"fontstyle0\">, and <\/span><code><span class=\"fontstyle2\">MYSQL_ROOT_PASSWORD <\/span><\/code><span class=\"fontstyle0\">environment variables. <\/span><\/p>\n<pre class=\"lang:default decode:true\"># oc new-app --name=mysql-openshift  -p MYSQL_USER=user \\\r\n-p MYSQL_PASSWORD=mypass  -p MYSQL_DATABASE=testdb \\\r\n-p MYSQL_ROOT_PASSWORD=rootpass -p VOLUME_CAPACITY=10Gi  mariadb\r\n\r\n--&gt; Found Docker image 011343c (3 days old) from Docker Hub for \"mariadb\"\r\n\r\n    * An image stream tag will be created as \"mysql-openshift:latest\" that will track this image\r\n    * This image will be deployed in deployment config \"mysql-openshift\"\r\n    * Port 3306\/tcp will be load balanced by service \"mysql-openshift\"\r\n      * Other containers can access this service through the hostname \"mysql-openshift\"\r\n    * This image declares volumes and will default to use non-persistent, host-local storage.\r\n      You can add persistent volumes later by running 'volume dc\/mysql-openshift --add ...'\r\n    * WARNING: Image \"mariadb\" runs as the 'root' user which may not be permitted by your cluster administrator\r\n\r\n--&gt; Creating resources ...\r\n    imagestream.image.openshift.io \"mysql-openshift\" created\r\n    deploymentconfig.apps.openshift.io \"mysql-openshift\" created\r\n    service \"mysql-openshift\" created\r\n--&gt; Success\r\n    Application is not exposed. You can expose services to the outside world by executing one or more of the commands below:\r\n     'oc expose svc\/mysql-openshift'\r\n    Run 'oc status' to view your app.\r\n\r\n<\/pre>\n<p><span class=\"fontstyle0\">If you want you can u<\/span><span class=\"fontstyle0\">se the <\/span><code><span class=\"fontstyle2\">--template <\/span><\/code><span class=\"fontstyle0\">option with the <\/span><span class=\"fontstyle2\"><code>oc new-app<\/code> <\/span><span class=\"fontstyle0\">command to specify a template with persistent storage so that OpenShift does not try and pull the image from the internet:<\/span><\/p>\n<pre class=\"lang:default decode:true\">#  <span class=\"fontstyle0\">oc new-app \\\r\n<\/span><span class=\"fontstyle2\">&gt; <\/span><span class=\"fontstyle0\">--template=mysql-persistent \\\r\n<\/span><span class=\"fontstyle2\">&gt; <\/span><span class=\"fontstyle0\">-p MYSQL_USER=user -p MYSQL_PASSWORD=mypass -p MYSQL_DATABASE=testdb \\\r\n<\/span><span class=\"fontstyle2\">&gt; <\/span><span class=\"fontstyle0\">-p MYSQL_ROOT_PASSWORD=rootpass -p VOLUME_CAPACITY=10Gi<\/span> \r\n<\/pre>\n<p><span class=\"fontstyle0\">Verify that the MariaDB pod was created successfully and view the details about the pod and its service. Run the <\/span><span class=\"fontstyle2\"><code>oc status<\/code> <\/span><span class=\"fontstyle0\">command to view the status of the new application:<\/span><\/p>\n<pre class=\"lang:default decode:true\"># oc status\r\nIn project mysql-openshift on server https:\/\/172.30.9.22:8443\r\n\r\nsvc\/mysql-openshift - 172.30.139.165:3306\r\n  dc\/mysql-openshift deploys istag\/mysql-openshift:latest\r\n    deployment #1 deployed 2 minutes ago - 1 pod\r\n\r\n2 infos identified, use 'oc status --suggest' to see details.\r\n<\/pre>\n<p><span class=\"fontstyle0\">List the pods in this project to verify that the MySQL pod is ready and running:<\/span><\/p>\n<pre class=\"lang:default decode:true\"># oc get pods -o=wide\r\nNAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE\r\nmysql-openshift-1-bbrd5 1\/1 Running 0 28m 172.17.0.9 localhost &lt;none&gt;<\/pre>\n<p><span class=\"fontstyle0\">Notice the worker on which the pod is running. You need this information to be able to log in to the MySQL database server later.<\/span><\/p>\n<p><span class=\"fontstyle0\">Use the <\/span><code><span class=\"fontstyle2\">oc describe <\/span><\/code><span class=\"fontstyle0\">command to view more details about the pod:<\/span><\/p>\n<pre class=\"lang:default decode:true \"># oc describe pod mysql-openshift-1-bbrd5\r\nName:               mysql-openshift-1-bbrd5\r\nNamespace:          mysql-openshift\r\nPriority:           0\r\nPriorityClassName:  &lt;none&gt;\r\nNode:               localhost\/172.30.9.22\r\nStart Time:         Sat, 08 Jul 2023 12:33:52 +0200\r\nLabels:             app=mysql-openshift\r\n                    deployment=mysql-openshift-1\r\n                    deploymentconfig=mysql-openshift\r\nAnnotations:        openshift.io\/deployment-config.latest-version=1\r\n                    openshift.io\/deployment-config.name=mysql-openshift\r\n                    openshift.io\/deployment.name=mysql-openshift-1\r\n                    openshift.io\/generated-by=OpenShiftNewApp\r\n                    openshift.io\/scc=restricted\r\nStatus:             Running\r\nIP:                 172.17.0.9\r\nControlled By:      ReplicationController\/mysql-openshift-1\r\nContainers:\r\n  mysql-openshift:\r\n    Container ID:   docker:\/\/3962296a6904ba1342c09231b09702648cc21c8389e929c982f1d6e01c4a29c4\r\n    Image:          mariadb@sha256:c57215c3aabbdb96353ce8b89a7bd6be089f49a4d6bb37f199288a1bf0438a02\r\n    Image ID:       docker-pullable:\/\/mariadb@sha256:c57215c3aabbdb96353ce8b89a7bd6be089f49a4d6bb37f199288a1bf0438a02\r\n    Port:           3306\/TCP\r\n    Host Port:      0\/TCP\r\n    State:          Running\r\n      Started:      Sat, 08 Jul 2023 12:34:08 +0200\r\n    Ready:          True\r\n    Restart Count:  0\r\n    Environment:\r\n      MYSQL_DATABASE:       testdb\r\n      MYSQL_PASSWORD:       mypass\r\n      MYSQL_ROOT_PASSWORD:  rootpass\r\n      MYSQL_USER:           user\r\n    Mounts:\r\n      \/var\/lib\/mysql from mysql-openshift-volume-1 (rw)\r\n      \/var\/run\/secrets\/kubernetes.io\/serviceaccount from default-token-cbrkl (ro)\r\nConditions:\r\n  Type              Status\r\n  Initialized       True\r\n  Ready             True\r\n  ContainersReady   True\r\n  PodScheduled      True\r\nVolumes:\r\n  mysql-openshift-volume-1:\r\n    Type:    EmptyDir (a temporary directory that shares a pod's lifetime)\r\n    Medium:\r\n  default-token-cbrkl:\r\n    Type:        Secret (a volume populated by a Secret)\r\n    SecretName:  default-token-cbrkl\r\n    Optional:    false\r\nQoS Class:       BestEffort\r\nNode-Selectors:  &lt;none&gt;\r\nTolerations:     &lt;none&gt;\r\nEvents:\r\n  Type    Reason     Age   From                Message\r\n  ----    ------     ----  ----                -------\r\n  Normal  Scheduled  43m   default-scheduler   Successfully assigned mysql-openshift\/mysql-openshift-1-bbrd5 to localhost\r\n  Normal  Pulling    43m   kubelet, localhost  pulling image \"mariadb@sha256:c57215c3aabbdb96353ce8b89a7bd6be089f49a4d6bb37f199288a1bf0438a02\"\r\n  Normal  Pulled     43m   kubelet, localhost  Successfully pulled image \"mariadb@sha256:c57215c3aabbdb96353ce8b89a7bd6be089f49a4d6bb37f199288a1bf0438a02\"\r\n  Normal  Created    43m   kubelet, localhost  Created container\r\n  Normal  Started    43m   kubelet, localhost  Started container\r\n<\/pre>\n<p><span class=\"fontstyle0\">List the services in this project and verify that the service to access the MySQL pod was created:<\/span><\/p>\n<pre class=\"lang:default decode:true \"># oc get svc\r\nNAME              TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE\r\nmysql-openshift   ClusterIP   172.30.139.165   &lt;none&gt;        3306\/TCP   46m\r\n<\/pre>\n<p><span class=\"fontstyle0\">Retrieve the details of the <\/span><span class=\"fontstyle2\">mysql-openshift <\/span><span class=\"fontstyle0\">service using the <\/span><span class=\"fontstyle2\"><code>oc describe<\/code><br \/>\n<\/span><span class=\"fontstyle0\">command and note that the Service type is <\/span><span class=\"fontstyle2\">ClusterIP <\/span><span class=\"fontstyle0\">by default:<\/span><\/p>\n<pre class=\"lang:default decode:true\"># oc describe service mysql-openshift\r\nName:              mysql-openshift\r\nNamespace:         mysql-openshift\r\nLabels:            app=mysql-openshift\r\nAnnotations:       openshift.io\/generated-by=OpenShiftNewApp\r\nSelector:          app=mysql-openshift,deploymentconfig=mysql-openshift\r\nType:              <strong>ClusterIP<\/strong>\r\nIP:                172.30.139.165\r\nPort:              3306-tcp  3306\/TCP\r\nTargetPort:        3306\/TCP\r\nEndpoints:         172.17.0.9:3306\r\nSession Affinity:  None\r\nEvents:            &lt;none&gt;\r\n<\/pre>\n<p><span class=\"fontstyle0\">View details about the deployment configuration <code>(<\/code><\/span><code><span class=\"fontstyle2\">dc<\/span><\/code><span class=\"fontstyle0\"><code>)<\/code> for this application:<\/span><\/p>\n<pre class=\"lang:default decode:true \"># oc describe dc mysql-openshift\r\nName:           mysql-openshift\r\nNamespace:      mysql-openshift\r\nCreated:        About an hour ago\r\nLabels:         app=mysql-openshift\r\nAnnotations:    openshift.io\/generated-by=OpenShiftNewApp\r\nLatest Version: 1\r\nSelector:       app=mysql-openshift,deploymentconfig=mysql-openshift\r\nReplicas:       1\r\nTriggers:       Config, Image(mysql-openshift@latest, auto=true)\r\nStrategy:       Rolling\r\nTemplate:\r\nPod Template:\r\n  Labels:       app=mysql-openshift\r\n                deploymentconfig=mysql-openshift\r\n  Annotations:  openshift.io\/generated-by=OpenShiftNewApp\r\n  Containers:\r\n   mysql-openshift:\r\n    Image:      mariadb@sha256:c57215c3aabbdb96353ce8b89a7bd6be089f49a4d6bb37f199288a1bf0438a02\r\n    Port:       3306\/TCP\r\n    Host Port:  0\/TCP\r\n    Environment:\r\n      MYSQL_DATABASE:           testdb\r\n      MYSQL_PASSWORD:           mypass\r\n      MYSQL_ROOT_PASSWORD:      rootpass\r\n      MYSQL_USER:               user\r\n    Mounts:\r\n      \/var\/lib\/mysql from mysql-openshift-volume-1 (rw)\r\n  Volumes:\r\n   mysql-openshift-volume-1:\r\n    Type:       EmptyDir (a temporary directory that shares a pod's lifetime)\r\n    Medium:\r\n\r\nDeployment #1 (latest):\r\n        Name:           mysql-openshift-1\r\n        Created:        about an hour ago\r\n        Status:         Complete\r\n        Replicas:       1 current \/ 1 desired\r\n        Selector:       app=mysql-openshift,deployment=mysql-openshift-1,deploymentconfig=mysql-openshift\r\n        Labels:         app=mysql-openshift,openshift.io\/deployment-config.name=mysql-openshift\r\n        Pods Status:    1 Running \/ 0 Waiting \/ 0 Succeeded \/ 0 Failed\r\n\r\nEvents:\r\n  Type          Reason                  Age     From                            Message\r\n  ----          ------                  ----    ----                            -------\r\n  Normal        DeploymentCreated       51m     deploymentconfig-controller     Created new replication controller \"mysql-openshift-1\" for version 1\r\n<\/pre>\n<p><span class=\"fontstyle0\">Expose the service creating a route with a default name and a fully qualified domain name (FQDN):<\/span><\/p>\n<pre class=\"lang:default decode:true\"># oc expose service mysql-openshift\r\nroute.route.openshift.io\/mysql-openshift exposed\r\n\r\n# oc get routes\r\nNAME              HOST\/PORT                                            PATH      SERVICES          PORT       TERMINATION   WILDCARD\r\nmysql-openshift   mysql-openshift-mysql-openshift.172.30.9.22.nip.io             mysql-openshift   3306-tcp                 None\r\n<\/pre>\n<p><span class=\"fontstyle0\">Connect to the MySQL database server and verify that the database was created successfully. To do that first configure port forwarding between <span class=\"fontstyle2\">workstation <\/span>and the database pod running on OpenShift using port 3306. The terminal will hang after executing the command.\u00a0<\/span><\/p>\n<pre class=\"lang:default decode:true \"># oc port-forward mysql-openshift-1-bbrd5 3306:3306\r\nForwarding from 127.0.0.1:3306 -&gt; 3306\r\nForwarding from [::1]:3306 -&gt; 3306<\/pre>\n<p>O<span class=\"fontstyle0\">pen another terminal and connect to the MySQL server using the MySQL client.<\/span><\/p>\n<pre class=\"lang:default decode:true \"># mysql -u user -pmypass --protocol tcp -h localhost\r\nWelcome to the MariaDB monitor.  Commands end with ; or \\g.\r\nYour MariaDB connection id is 3\r\nServer version: 11.0.2-MariaDB-1:11.0.2+maria~ubu2204 mariadb.org binary distribution\r\n\r\nCopyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.\r\n\r\nType 'help;' or '\\h' for help. Type '\\c' to clear the current input statement.\r\n\r\nMariaDB [(none)]&gt;\r\n<\/pre>\n<p><span class=\"fontstyle0\">Verify the creation of the <\/span><code><span class=\"fontstyle2\">testdb <\/span><\/code><span class=\"fontstyle0\">database.<\/span><\/p>\n<pre class=\"lang:default decode:true\">MariaDB [(none)]&gt; show databases;\r\n+--------------------+\r\n| Database           |\r\n+--------------------+\r\n| information_schema |\r\n| testdb             |\r\n+--------------------+\r\n2 rows in set (0.00 sec)\r\n<\/pre>\n<p><span class=\"fontstyle0\">Exit from the MySQL prompt:<\/span><\/p>\n<pre class=\"lang:default decode:true \">MariaDB [(none)]&gt; exit\r\nBye\r\n\r\n<\/pre>\n<p><span class=\"fontstyle0\">Close the terminal and return to the previous one. Finish the port forwarding process by pressing <\/span><span class=\"fontstyle2\">Ctrl<\/span><span class=\"fontstyle0\">+<\/span><span class=\"fontstyle2\">C<\/span><\/p>\n<pre class=\"lang:default decode:true \"># oc get routes\r\nNAME              HOST\/PORT                                            PATH      SERVICES          PORT       TERMINATION   WILDCARD\r\nmysql-openshift   mysql-openshift-mysql-openshift.172.30.9.22.nip.io             mysql-openshift   3306-tcp                 None\r\n[root@okd ~]# oc port-forward mysql-openshift-1-bbrd5 3306:3306\r\nForwarding from 127.0.0.1:3306 -&gt; 3306\r\nForwarding from [::1]:3306 -&gt; 3306\r\nHandling connection for 3306\r\n^C[root@okd ~]#\r\n<\/pre>\n<p><span class=\"fontstyle0\">Delete the project to remove all the resources within the project:<\/span><\/p>\n<pre class=\"lang:default decode:true\"># oc delete project mysql-openshift\r\nproject.project.openshift.io \"mysql-openshift\" deleted<\/pre>\n<p>&nbsp;<\/p>\n<p><span style=\"color: #3366ff;\">Creating Routes<\/span><\/p>\n<p><span class=\"fontstyle0\">Services allow for network access between pods inside an OpenShift instance, and routes allow for network access to pods from users and applications outside the OpenShift instance.<\/span><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-4795 aligncenter\" src=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/Routes.jpg\" alt=\"\" width=\"1076\" height=\"1064\" srcset=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/Routes.jpg 1076w, http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/Routes-300x297.jpg 300w, http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/Routes-1024x1013.jpg 1024w, http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/Routes-768x759.jpg 768w, http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/Routes-100x100.jpg 100w\" sizes=\"(max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px\" \/><\/p>\n<p><span class=\"fontstyle0\">A route connects a public-facing IP address and DNS host name to an internal-facing service IP. It uses the service resource to find the endpoints; that is, the ports exposed by the service. OpenShift routes are implemented by a cluster-wide router service, which runs as a containerized application in the OpenShift cluster. OpenShift scales and replicates router pods like any other<br \/>\nOpenShift application. <\/span><span class=\"fontstyle0\">In practice, to improve performance and reduce latency, the OpenShift router connects directly to the pods using the internal pod software-defined network (SDN).<br \/>\nThe router service uses <\/span><span class=\"fontstyle6\">HAProxy <\/span><span class=\"fontstyle0\">as the default implementation. An important consideration for OpenShift administrators is that the public DNS host names<br \/>\nconfigured for routes need to point to the public-facing IP addresses of the nodes running the router. Router pods, unlike regular application pods, bind to their nodes&#8217; public IP addresses instead of to the internal pod SDN.<\/span><\/p>\n<p><span class=\"fontstyle0\">The following example shows a minimal route defined using JSON syntax:<br \/>\n<\/span><\/p>\n<pre class=\"lang:default decode:true \">{\r\n\"apiVersion\": \"v1\",\r\n\"kind\": \"Route\",\r\n\"metadata\": {\r\n\"name\": \"quoteapp\"\r\n},\r\n\"spec\": {\r\n\"host\": \"quoteapp.apps.example.com\",\r\n\"to\": {\r\n\"kind\": \"Service\",\r\n\"name\": \"quoteapp\"\r\n}\r\n}\r\n}<\/pre>\n<p><span class=\"fontstyle0\">The <\/span><code><span class=\"fontstyle2\">apiVersion<\/span><span class=\"fontstyle0\">, <\/span><span class=\"fontstyle2\">kind<\/span><\/code><span class=\"fontstyle0\">, and <\/span><code><span class=\"fontstyle2\">metadata <\/span><\/code><span class=\"fontstyle0\">attributes follow standard Kubernetes resource definition rules. The <\/span><code><span class=\"fontstyle2\">Route <\/span><\/code><span class=\"fontstyle0\">value for <\/span><span class=\"fontstyle2\">kind <\/span><span class=\"fontstyle0\">shows that this is a route resource, and the <\/span><span class=\"fontstyle2\"><code>metadata.name<\/code> <\/span><span class=\"fontstyle0\">attribute\u00a0 ives this particular route the identifier <\/span><span class=\"fontstyle2\">quoteapp<\/span><span class=\"fontstyle0\">. As with pods and services, the main part is the <\/span><code><span class=\"fontstyle2\">spec <\/span><\/code><span class=\"fontstyle0\">attribute, which is an object containing the following attributes:<\/span><\/p>\n<ul>\n<li><span class=\"fontstyle2\"><code>host<\/code> <\/span><span class=\"fontstyle0\">is a string containing the FQDN associated with the route. DNS must resolve this FQDN to the IP address of the OpenShift router. The details to modify DNS configuration are outside the scope of this course.<\/span><\/li>\n<li><span class=\"fontstyle2\">to <\/span><span class=\"fontstyle0\">is an object stating the resource this route points to. In this case, the route points to an OpenShift Service with the <\/span><span class=\"fontstyle2\">name <\/span><span class=\"fontstyle0\">set to <\/span><span class=\"fontstyle2\">quoteapp<\/span><\/li>\n<\/ul>\n<p><span class=\"fontstyle0\">Use the <\/span><code><span class=\"fontstyle2\">oc create <\/span><\/code><span class=\"fontstyle0\">command to create route resources, just like any other OpenShift resource. You must provide a JSON or YAML resource definition file, which defines the route, to the <\/span><span class=\"fontstyle2\">oc create <\/span><span class=\"fontstyle0\">command.<br \/>\n<\/span><span class=\"fontstyle0\"><br \/>\nAnother way to create a route is to use the <\/span><span class=\"fontstyle2\"><code>oc expose<\/code> service <\/span><span class=\"fontstyle0\">command, passing a service resource name as the input. The <\/span><code><span class=\"fontstyle2\">--name <\/span><\/code><span class=\"fontstyle0\">option can be used to control the name of the route resource. For example:<br \/>\n<\/span><\/p>\n<pre class=\"lang:default decode:true \">$ oc expose service quotedb --name quote<\/pre>\n<p><span class=\"fontstyle0\">By default, routes created by <\/span><code><span class=\"fontstyle2\">oc expose <\/span><\/code><span class=\"fontstyle0\">generate DNS names of the form: <\/span><span class=\"fontstyle2\"><code>route_name-project_name.default-domain<\/code><br \/>\n<\/span><span class=\"fontstyle0\"><br \/>\nFor example, creating a route named <\/span><code><span class=\"fontstyle2\">quote <\/span><\/code><span class=\"fontstyle0\">in project named <\/span><code><span class=\"fontstyle2\">test <\/span><\/code><span class=\"fontstyle0\">from an OpenShift instance where the wildcard domain is <\/span><code><span class=\"fontstyle2\">cloudapps.example.com <\/span><\/code><span class=\"fontstyle0\">results in the FQDN <\/span><span class=\"fontstyle2\">quote-test.cloudapps.example.com<\/span><span class=\"fontstyle0\">. <\/span><\/p>\n<p><span class=\"fontstyle0\" style=\"color: #3366ff;\"><br \/>\n<\/span><span class=\"fontstyle4\"><span style=\"color: #3366ff;\">Leveraging the Default Routing Service<\/span><br \/>\n<\/span><span class=\"fontstyle0\">The default routing service is implemented as an <\/span><em><span class=\"fontstyle2\">HAProxy <\/span><\/em><span class=\"fontstyle0\">pod. Router pods, containers, and their configuration can be inspected just like any other resource in an OpenShift cluster:<\/span><\/p>\n<pre class=\"lang:default decode:true\">$ oc get pod --all-namespaces | grep router\r\nopenshift-ingress router-default-746b5cfb65-f6sdm 1\/1 Running 1 4d<\/pre>\n<p><span class=\"fontstyle0\">Use <\/span><span class=\"fontstyle2\"><code>oc describe pod<\/code> <\/span><span class=\"fontstyle0\">command to get the routing configuration details:<br \/>\n<\/span><span class=\"fontstyle7\"><br \/>\n<\/span><\/p>\n<pre class=\"lang:default decode:true\">$ oc describe pod router-default-746b5cfb65-f6sdm\r\nName: router-default-746b5cfb65-f6sdm\r\nNamespace: openshift-ingress\r\n...output omitted...\r\nContainers:\r\nrouter:\r\n...output omitted...\r\nEnvironment:\r\nSTATS_PORT: 1936\r\nROUTER_SERVICE_NAMESPACE: openshift-ingress\r\nDEFAULT_CERTIFICATE_DIR: \/etc\/pki\/tls\/private\r\nROUTER_SERVICE_NAME: default\r\nROUTER_CANONICAL_HOSTNAME: apps.cluster.lab.example.com\r\n...output omitted...<\/pre>\n<p><span class=\"fontstyle0\">The subdomain, or default domain to be used in all default routes, takes its value from the <\/span><span class=\"fontstyle2\">ROUTER_CANONICAL_HOSTNAME <\/span><span class=\"fontstyle0\">entry<\/span><\/p>\n<p><span class=\"fontstyle0\" style=\"color: #3366ff;\"> Exposing a Service as a Route<\/span><\/p>\n<p><span class=\"fontstyle0\">Log in to the OpenShift cluster<\/span><\/p>\n<pre class=\"lang:default decode:true\"># oc login -u system:admin\r\nLogged into \"https:\/\/172.30.9.22:8443\" as \"system:admin\" using existing credentials.\r\n\r\nYou have access to the following projects and can switch between them with 'oc project &lt;projectname&gt;':\r\n\r\nchapter1\r\ndefault\r\nkube-dns\r\nkube-proxy\r\nkube-public\r\nkube-system\r\nmyproject\r\n* mysql-openshift\r\nopenshift\r\nopenshift-apiserver\r\nopenshift-controller-manager\r\nopenshift-core-operators\r\nopenshift-infra\r\nopenshift-node\r\nopenshift-service-cert-signer\r\nopenshift-web-console\r\n\r\nUsing project \"mysql-openshift\".\r\n# oc whoami\r\nsystem:admin<\/pre>\n<p>Create a new project for the resources you create during this exercise.<\/p>\n<pre class=\"lang:default decode:true\"># oc new-project example-route\r\nNow using project \"example-route\" on server \"https:\/\/172.30.9.22:8443\".\r\n\r\nYou can add applications to this project with the 'new-app' command. For example, try:\r\noc new-app centos\/ruby-25-centos7~https:\/\/github.com\/sclorg\/ruby-ex.git\r\nto build a new example application in Ruby.<\/pre>\n<p><span class=\"fontstyle0\">Use the <\/span><span class=\"fontstyle2\">oc new-app <\/span><span class=\"fontstyle0\">command to create the application<\/span><\/p>\n<pre class=\"lang:default decode:true\"># oc new-app quay.io\/redhattraining\/hello-world-nginx:latest --name helloworld\r\n--&gt; Found Docker image 44eaa13 (4 years old) from quay.io for \"quay.io\/redhattraining\/hello-world-nginx:latest\"\r\n\r\n    Red Hat Universal Base Image 8\r\n    ------------------------------\r\n    The Universal Base Image is designed and engineered to be the base layer for all of your containerized applications, middleware and utilities. This base image is freely redistributable, but Red Hat only supports Red Hat technologies through subscriptions for Red Hat products. This image is maintained by Red Hat and updated regularly.\r\n\r\n    Tags: base rhel8\r\n\r\n    * An image stream tag will be created as \"helloworld:latest\" that will track this image\r\n    * This image will be deployed in deployment config \"helloworld\"\r\n    * Port 8080\/tcp will be load balanced by service \"helloworld\"\r\n      * Other containers can access this service through the hostname \"helloworld\"\r\n\r\n--&gt; Creating resources ...\r\n    imagestream.image.openshift.io \"helloworld\" created\r\n    deploymentconfig.apps.openshift.io \"helloworld\" created\r\n    service \"helloworld\" created\r\n--&gt; Success\r\n    Application is not exposed. You can expose services to the outside world by executing one or more of the commands below:\r\n     'oc expose svc\/helloworld'\r\n    Run 'oc status' to view your app.\r\n<\/pre>\n<p><span class=\"fontstyle0\">Wait until the application finishes building and deploying by monitoring the progress with the <\/span><code><span class=\"fontstyle2\">oc get pods -w <\/span><\/code><span class=\"fontstyle0\">command:<\/span><\/p>\n<pre class=\"lang:default decode:true \"># oc get pods -w\r\nNAME READY STATUS RESTARTS AGE\r\nhelloworld-1-478t4 1\/1 Running 0 1m<\/pre>\n<p>You can <span class=\"fontstyle0\">monitor the build and deployment logs with the <\/span><span class=\"fontstyle2\"><code>oc logs -f<\/code>.<\/span><span class=\"fontstyle0\"> Press Ctrl + C to exit the command if necessary.<\/span><\/p>\n<pre class=\"lang:default decode:true \"># oc logs -f helloworld-1-478t4<\/pre>\n<p><span class=\"fontstyle0\">Review the service for this application using the <\/span><code><span class=\"fontstyle2\">oc describe <\/span><\/code><span class=\"fontstyle0\">command:<\/span><\/p>\n<pre class=\"lang:default decode:true \"># oc describe svc\/helloworld\r\nName: helloworld\r\nNamespace: example-route\r\nLabels: app=helloworld\r\nAnnotations: openshift.io\/generated-by=OpenShiftNewApp\r\nSelector: app=helloworld,deploymentconfig=helloworld\r\nType: ClusterIP\r\nIP: 172.30.137.26\r\nPort: 8080-tcp 8080\/TCP\r\nTargetPort: 8080\/TCP\r\nEndpoints: 172.17.0.11:8080\r\nSession Affinity: None\r\nEvents: &lt;none&gt;<\/pre>\n<p><span class=\"fontstyle0\">Expose the service, which creates a route. Use the default name and fully qualified domain name (FQDN) for the route:<\/span><\/p>\n<pre class=\"lang:default decode:true\"># oc expose svc\/helloworld\r\nroute.route.openshift.io\/helloworld exposed\r\n\r\n# oc describe route\r\nName:                   helloworld\r\nNamespace:              example-route\r\nCreated:                40 seconds ago\r\nLabels:                 app=helloworld\r\nAnnotations:            openshift.io\/host.generated=true\r\nRequested Host:         <strong>helloworld-example-route.172.30.9.22.nip.io<\/strong>\r\n                          exposed on router router 40 seconds ago\r\nPath:                   &lt;none&gt;\r\nTLS Termination:        &lt;none&gt;\r\nInsecure Policy:        &lt;none&gt;\r\nEndpoint Port:          8080-tcp\r\n\r\nService:        helloworld\r\nWeight:         100 (100%)\r\nEndpoints:      172.17.0.11:8080\r\n<\/pre>\n<p><span class=\"fontstyle0\">Access the service\u00a0 to verify that the service and route are working.<\/span><\/p>\n<pre class=\"lang:default decode:true \"># curl helloworld-example-route.172.30.9.22.nip.io\r\n&lt;html&gt;\r\n&lt;body&gt;\r\n&lt;h1&gt;Hello, world from nginx!&lt;\/h1&gt;\r\n&lt;\/body&gt;\r\n&lt;\/html&gt;<\/pre>\n<p><span class=\"fontstyle0\">Replace this route with a route named <\/span><span class=\"fontstyle2\">xyz<\/span><span class=\"fontstyle0\">.<\/span><\/p>\n<p><span class=\"fontstyle0\">Delete the current route:<\/span><\/p>\n<pre class=\"lang:default decode:true \"># oc delete route\/helloworld\r\nroute.route.openshift.io \"helloworld\" deleted\r\n<\/pre>\n<p><span class=\"fontstyle0\">Create a route for the service with a name of <\/span><code><span class=\"fontstyle2\">xyz:<\/span><\/code><\/p>\n<pre class=\"lang:default decode:true\"># oc expose svc\/helloworld --name=xyz\r\nroute.route.openshift.io\/xyz exposed\r\n\r\n# oc describe route\r\nName:                   xyz\r\nNamespace:              example-route\r\nCreated:                2 minutes ago\r\nLabels:                 app=helloworld\r\nAnnotations:            openshift.io\/host.generated=true\r\nRequested Host:         <strong>xyz-example-route.172.30.9.22.nip.io<\/strong>\r\n                          exposed on router router 2 minutes ago\r\nPath:                   &lt;none&gt;\r\nTLS Termination:        &lt;none&gt;\r\nInsecure Policy:        &lt;none&gt;\r\nEndpoint Port:          8080-tcp\r\n\r\nService:        helloworld\r\nWeight:         100 (100%)\r\nEndpoints:      172.17.0.11:8080\r\n<\/pre>\n<p><span class=\"fontstyle0\">Note the new FQDN that was generated based on the new route name. Both the route name and the project name contain your user name, hence it appears twice in the route FQDN.<br \/>\nMake an HTTP request using the FQDN on port 80:<\/span><\/p>\n<pre class=\"lang:default decode:true \"># curl xyz-example-route.172.30.9.22.nip.io\r\n&lt;html&gt;\r\n  &lt;body&gt;\r\n    &lt;h1&gt;Hello, world from nginx!&lt;\/h1&gt;\r\n  &lt;\/body&gt;\r\n&lt;\/html&gt;\r\n<\/pre>\n<p>Delete project:<\/p>\n<pre class=\"lang:default decode:true \"># oc delete project example-route<\/pre>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Red Hat OpenShift Container Platform is a set of modular components and services built on top of Red Hat CoreOS and Kubernetes. RHOCP adds PaaS capabilities such as remote management, increased security, monitoring and auditing, application life-cycle management, and self-service interfaces for developers. An OpenShift cluster is a Kubernetes cluster that can be managed the &hellip; <\/p>\n<p class=\"link-more\"><a href=\"http:\/\/miro.borodziuk.eu\/index.php\/2023\/07\/01\/deploying-containerized-applications-on-openshift\/\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;Deploying Applications on OpenShift&#8221;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":4777,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[93],"tags":[],"_links":{"self":[{"href":"http:\/\/miro.borodziuk.eu\/index.php\/wp-json\/wp\/v2\/posts\/4771"}],"collection":[{"href":"http:\/\/miro.borodziuk.eu\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/miro.borodziuk.eu\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/miro.borodziuk.eu\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/miro.borodziuk.eu\/index.php\/wp-json\/wp\/v2\/comments?post=4771"}],"version-history":[{"count":44,"href":"http:\/\/miro.borodziuk.eu\/index.php\/wp-json\/wp\/v2\/posts\/4771\/revisions"}],"predecessor-version":[{"id":5131,"href":"http:\/\/miro.borodziuk.eu\/index.php\/wp-json\/wp\/v2\/posts\/4771\/revisions\/5131"}],"wp:featuredmedia":[{"embeddable":true,"href":"http:\/\/miro.borodziuk.eu\/index.php\/wp-json\/wp\/v2\/media\/4777"}],"wp:attachment":[{"href":"http:\/\/miro.borodziuk.eu\/index.php\/wp-json\/wp\/v2\/media?parent=4771"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/miro.borodziuk.eu\/index.php\/wp-json\/wp\/v2\/categories?post=4771"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/miro.borodziuk.eu\/index.php\/wp-json\/wp\/v2\/tags?post=4771"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}