/Openshift

Some Getting Started Documentation

Primary LanguageJavaScript

Openshift Developers Guide

Intro

The purpose of this guide is to introduce developers, in a practical way, to the concepts, components and tooling of the OpenShift ecosystem

Getting Started

They are various options to get started with OpenShift:

This portal is a great start to get familiar with Openshift.

Instructions for MacOSX

First we need to install Docker, at the moment of writing this document oc-client work best with this old version.

Add insecure registry: Openshift UI

Install Socat using Homebrew, brew install socat

Now download oc-client, extract and add it into your path.

export PATH=$HOME/folder-with-oc-client/:$PATH

If everything is fine, you should be able to run this:

  oc version

  #oc v3.9.0+191fece
  #kubernetes v1.9.1+a0ce1bc657
  #features: Basic-Auth

Now you can try and create your Openshift cluster:

oc cluster up

We should get the following message:

Starting OpenShift using openshift/origin:v3.7.1 ...
OpenShift server started.

The server is accessible via web console at:
    https://127.0.0.1:8443

You are logged in as:
    User:     developer
    Password: <any value>

To login as administrator:
    oc login -u system:admin

In some cases using oc-cli method can be complicated to setup, Minishift offers a VM solution that encapsulate all this complexity.

Once you have setup your cluster you can start playing with Openshift, first we can start by login in, to do this we need to write the following command:

  oc login -u <username>

or if you want to get a interactive login:

 oc login

Project/Namespaces are the way Openshift divide the cluster between users, basically all your object should be defined under a Openshift namespace, to create a new project you should do.

 oc new-project hello-world

This will create a new namespace called hello-world.

asciicast

The Pod is the minimal building blog in Openshift, represent a single application from the perspective of Openshift. But in the inside it can contain multiple resources containers, storage resources, etc.

To create we just need to create a small template:

apiVersion: v1
kind: Pod
metadata:
  name: myapp-pod
  labels:
    app: myapp
spec:
  containers:
  - name: myapp-container
    image: busybox
    command: ['sh', '-c', 'echo Hello Openshift! && sleep 3600']

Consider this template like a recipe, it basically tells Openshift what to do for you, in this case it specifies a resource of the kind Pod, and put some name to it myapp-pod, then we describe what we want to run inside, we said, we want a busybox container and we want to run to run some shell command in it.

Then we need to pass this to Openshift:

oc create -f pod.yml       #pod "myapp-pod" created

#or you can grab the template from the cloud.

oc create -f https://raw.githubusercontent.com/cesarvr/Openshift/master/templates/pod.yml

Then we can ask Openshift, about the state of the resource by doing:

oc get pods                                                               

We should see our Pod up and running.

  NAME        READY     STATUS    RESTARTS   AGE
  myapp-pod   1/1       Running   0          4d

asciicast

In the last section, we end up running our first pod, but there some catch, we are not able to scale up or down our application, also we are not able to update the state or rolling out a new version. For been able to do that we need another entity called DeploymentConfig.

[DeploymentConfig] is a way to describe a desired state for our application, we can scale, downscale, pause deployments, roll-back and roll-out.

Let's scale our app, to do this we just need to create a Deployment template:

apiVersion: apps/v1beta1
kind: Deployment
metadata:
  name: hello-dev
spec:
  selector:
    matchLabels:
      app: hello-dev
  replicas: 2 # tells deployment to run 2 pods matching the template
  template:
    metadata:
      labels:
        app: hello-dev
    spec:
      containers:
      - name: myapp-container
        image: busybox
        command: ['sh', '-c', 'echo Hello World! && sleep 13600']

The template here is very similar to the one used to create the Pod:

apiVersion: apps/v1beta1
kind: Deployment
metadata:
  name: hello-dev

Here we said we want to target the apps/v1beta endpoint of Openshift, and inside this endpoint we want the Deployment object.

spec:
  selector:
    matchLabels:
      app: hello-dev
  replicas: 2 # tells deployment to run 1 pods matching the template
  template:
    metadata:
      labels:
        app: hello-dev

Here we describe the state we want for our application, we specify we want two replicas, once the Deployment Config is created, Openshift will check the current state of the system (0 replicas) versus the expected state (2 replicas), then it will increase the number of Pods to achieve the desired state.

spec:
  containers:
    - name: myapp-container
    image: busybox
    command: ['sh', '-c', 'echo Hello World! && sleep 13600']

This is the exact same code as our previous example, we just want to say hello and sleep.

To load this Deployment template, is similar to what we did with our Pod last time.

 oc create -f deploy.yml

Deployment

deployment

We are going to deploy simple Node.js application, the best way to do this is to use a BuilderConfig but for now and for the sake of learning we going use a external image and update our Deployment Config.

To achieve this goal we are going to export an image to our cluster, for this we need a Openshift object called ImageStream, which allow us to monitor and perform actions based in images tag changes.

To export the image we run the following code

oc import-image alpine-node:latest --from=docker.io/mhart/alpine-node --confirm

We can check the status of the ImageStream by running:

oc get is

# NAME          DOCKER REPO                               TAGS      
# alpine-node   172.30.1.1:5000/hello-world/alpine-node   latest

Some explanations:

  • Name
    • Name: Name of the ImageStream object.
  • Docker Repo URL
    • 172.30.1.1:5000: This is the URL for the Docker registry in your Openshift installation.
    • hello-world: This is the project/namespace, this means that this image is pullable from this project/namespace only, which is good, because we don't want to clutter the Docker registry for others.
    • alpine-node: It's the actual image our ImageStream is pointing to.
  • Tags
    • tags: This is the Docker tag, that our ImageStream is monitoring for changes.

Now that we have exported our image, we can update our deployment configuration:

Once we got our image imported into our cluster is ready to use, next step is to modify our previously created deployment template.

We start with this template:

apiVersion: apps/v1beta1
kind: Deployment
metadata:
  name: hello-dev
spec:
  selector:
    matchLabels:
      app: hello-dev
  replicas: 2 # tells deployment to run 2 pods matching the template
  template:
    metadata:
      labels:
        app: hello-dev
    spec:
      containers:
      - name: myapp-container
        image: busybox
        command: ['sh', '-c', 'echo Hello World! && sleep 13600']

Here we need to modify the image section, where we going to replace busybox with the URL of our imported image, to get the URL just write oc get is and copy/paste the URL that appear in the section called Docker Repo. Next, we need to change the command and add some instruction to execute our Node.js server.

We should change the container:

apiVersion: apps/v1beta1
kind: Deployment
metadata:
  name: hello-dev
spec:
  selector:
    matchLabels:
      app: nodejs-app
  replicas: 2
  template:
    metadata:
      labels:
        app: nodejs-app
    spec:
      containers:
      - name: myapp-container
        image: 172.30.1.1:5000/hello-world/alpine-node
        command: ['node', '-e']
        args:
          - require('http').createServer((req, res) => { res.end('Hello World') }).listen(8080)
        ports:
        - containerPort: 8080

This will execute node -e with those args, that snippet its just a simple Node.js HTTP server program that would listen in port 8080 and will respond a Hello World. We also going to open a port for our Pod, in this case 8080.

We save this modification as deploy-server.yml.

Then we load the template:

oc create -f deploy-server.yml

deployment server

If you have the old version of the deployment, you need to delete it first:

oc delete deployment hello-dev

Umm, we have finish the deployment of our server app, but we still are not communicating with our server from the outside, to be honest we don't have even access from our guest machine. For this we need a combination of two OpenShift objects Service and Router, in the next section we are going to explore how route request to our Pods.

Before we start exposing our server application to external traffic, we need to talk about some concepts:

  • Labels: labels provide a easy way to organize our objects in the cluster, think of it as a way to group a set of objects, in the example above we choose to setup the label app: nodejs-app.

  • Services: OpenShift object that is in charge to redirect the traffic to our Pods, it work at cluster level, saying this, you should never target the IP of the Pod directly, always use a Service.

    • Why? Because the Pod entities are ephemeral objects designed to be disposable, moved on-demand around the cluster. Services works as an entity that keep tracks of them and offer a single point endpoint to contact your Pod.
  • Routers: This object redirect traffic from the outside to our Service. We need this object when we want to expose our Services to the exterior.

To create a Service we need to create a definition in a template:

kind: Service
apiVersion: v1
metadata:
  name: helloworld
spec:
  selector:
    app: nodejs-app
  ports:
  - protocol: TCP
    port: 80
    targetPort: 8080

In this definition we are telling OpenShift that we want a Service object that send traffic to objects with the tag app:nodejs-app, also we want to take traffic from port 80 and we want to forward the traffic to port 8080.

Pay special attention to the selector app:nodejs-app, this basically tell the Service object to query objects in the cluster that match those labels, once he find it, it will start to direct traffic between them.

To create the service:

oc create -f service.yml
oc get service

#NAME         CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE
#helloworld   172.30.106.249   <none>        80/TCP    4h

The Router, as mentioned before is the object to direct traffic into the cluster, creating it is very simple, we can explicitly expose the service using oc expose.

oc expose svc helloworld
oc get route

#NAME         HOST/PORT                                 PATH      SERVICES     PORT      TERMINATION   WILDCARD
#helloworld   helloworld-hello-world.127.0.0.1.nip.io             helloworld   8080                    None

Now we can talk with our Pods from the outside.

curl helloworld-hello-world.127.0.0.1.nip.io
#Hello World%

Service-Router