Getting Started on APPUiO Cloud with the Terminal using Python

Programming language logo This tutorial explains how to run applications written in the Python programming language on APPUiO Cloud using the command line.

If you aren’t familiar with issuing commands on a terminal session, you might want to try the Getting Started with Python on the OpenShift Web Console guide instead.

Requirements

To follow this guide, please make sure that you have the following tools installed:

oc

You can download the OpenShift command directly from APPUiO Cloud, selecting the help menu (marked as a question mark) and selecting the "Command line tools" entry.

docker or podman

You can download them from www.docker.com and podman.io.

curl

Available from curl.se.

About the Application

To demo how to run applications written in the Python programming language on APPUiO Cloud, we will use a small demo application using the Flask Framework, bundled as a container thanks to its corresponding Dockerfile, and ready to be used on APPUiO Cloud.

You can browse the full source code of this application on GitLab:

The "Fortune in Python" application does a few simple things:

  • When invoked on the browser, it returns a random number and a funny quote.

  • When invoked with an HTTP request including the Accept: application/json header, it returns the same information in JSON format.

  • Finally, when invoked with an HTTP request including the Accept: text/plain header, it returns the same information in plain text format.

Learn more about the fortune-python application, including how to edit and build it, on the project README.

The Application Router

The code below shows the main router of the application in the Python programming language, located at app.py, courtesy of the Flask Framework.

@app.route("/")
def fortune():
    """
    Print a random, hopefully interesting, adage
    """
    number, fortune = get_fortune()
    accepts = request.headers['accept']
    if accepts == 'application/json':
        resp = jsonify({ 'number': number, 'message': fortune, 'version': version, 'hostname': hostname })
        return resp

    if accepts == 'text/plain':
        result = 'Fortune %s cookie of the day #%d:\n\n%s' % (version, number, fortune)
        resp = Response(result, mimetype='text/plain')
        return resp

    # In all other cases, respond with HTML
    html = render_template('fortune.html', number=number, message=fortune, version=version, hostname=hostname)
    resp = Response(html, mimetype='text/html')
    return resp

if __name__ == "__main__":
    app.run(host='0.0.0.0', port=os.environ.get('listenport', port))

Run the Container

This step is optional; you can easily test the application locally with Docker:

docker run --rm --publish 8080:8080 registry.gitlab.com/vshn/applications/fortune-python:latest

Or with Podman:

podman run --rm --publish 8080:8080 registry.gitlab.com/vshn/applications/fortune-python:latest

Now you can test the application with curl, for example to get some JSON:

curl http://localhost:8080 --header "Accept: application/json"

Or to see a plain text version:

curl http://localhost:8080 --header "Accept: text/plain"

You can also open a browser and navigate to localhost:8080 to get your fortune cookie of the day.

Dockerfile

The application includes a Dockerfile ready to build a container fully compatible with OpenShift. The snippet below shows the instructions used to build the actual running image:

# Inherit from this "empty base image", see https://hub.docker.com/_/python/
FROM python:3.10-alpine

# Take some responsibility for this container
MAINTAINER Jane Smith <jane.smith@example.com>

# Install some required software on Alpine Linux
RUN apk add fortune

# Directory to install the app inside the container
WORKDIR /usr/src/app

# Install python dependencies (cached if requirements.txt does not change)
COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt

# Copy application source code into container
COPY app.py /usr/src/app
COPY templates /usr/src/app/templates/

# Expose this TCP-port, same as app.py
EXPOSE 8080

# Drop root privileges when running the application
(1)
USER 1001

# Run this command at run-time
CMD [ "python", "app.py" ]
1 This explicitly prevents the container from running as root; this is a requirement of OpenShift, and a good practice for images in general.

You can use the Dockerfile above to build your own copy of the container, which you can then push to the registry of your choice. Clone the repo:

git clone https://gitlab.com/vshn/applications/fortune-python.git

cd into it:

cd fortune-python

And build your image with Docker:

docker build -t fortune-python .

Or with Podman instead:

podman build -t fortune-python .

Step 1: Create a Project

Follow these steps to login to APPUiO Cloud on your terminal:

  1. Login to the APPUiO Cloud console:

    oc login --server=https://api.${zone}.appuio.cloud:6443

    You can find the exact URL of your chosen zone in the APPUiO Cloud Portal.

    This command displays a URL on your terminal:

    You must obtain an API token by visiting
    https://oauth-openshift.apps.${zone}.appuio.cloud/oauth/token/request
  2. Click on the link above and open it in your browser.

  3. Click "Display token" and copy the login command shown as "Log in with this token"

  4. Paste the oc login command on the terminal:

    oc login --token=sha256~_xxxxxx_xxxxxxxxxxxxxxxxxxxxxx-xxxxxxxxxx-X \
        --server=https://api.${zone}.appuio.cloud:6443
  5. Create a new project called "[YOUR_USERNAME]-fortune-python"

    oc new-project [YOUR_USERNAME]-fortune-python
  6. To deploy the application we will use a standard Kubernetes Deployment object. Save the following YAML in a file called deployment.yaml:

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: fortune-python
    spec:
      template:
        spec:
          containers:
          - image: registry.gitlab.com/vshn/applications/fortune-python:latest
            imagePullPolicy: Always
            name: fortune-container
            ports:
            - containerPort: 8080
        metadata:
          labels:
            app: fortune-python
      selector:
        matchLabels:
          app: fortune-python
      strategy:
        type: Recreate
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: fortune-python
    spec:
      ports:
        - port: 8080
          targetPort: 8080
      selector:
        app: fortune-python
      type: ClusterIP
  7. Then apply the deployment to your APPUiO Cloud project:

    oc apply -f deployment.yaml

    And wait until your pod appears with the status "Running":

    oc get pods --watch

Step 2: Publish your Application

At the moment your container is running but it’s not available from the Internet. To be able to access our application, we must create an Ingress object.

  1. Create another file called ingress.yaml with the following contents, customizing the parts marked as [YOUR_USERNAME] and [YOUR_CHOSEN_ZONE] to your liking (and according to the Zones documentation page):

    apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:
      annotations:
        cert-manager.io/cluster-issuer: letsencrypt-production
      name: fortune-python-ingress
    spec:
      rules:
      - host: [YOUR_USERNAME]-fortune-python.apps.[YOUR_CHOSEN_ZONE].appuio.cloud (1)
        http:
          paths:
          - pathType: Prefix
            path: /
            backend:
              service:
                name: fortune-python
                port:
                  number: 8080
      tls:
      - hosts:
        - [YOUR_USERNAME]-fortune-python.apps.[YOUR_CHOSEN_ZONE].appuio.cloud
        secretName: fortune-python-cert
    1 Replace the placeholders YOUR_USERNAME and YOUR_CHOSEN_ZONE with valid values.
About URL lengths

Make sure that the total length of the prefix string [YOUR_USERNAME]-fortune-[LANGUAGE] used in the Ingress YAML above isn’t longer than 63 characters. This is due for two reasons:

  1. As per the DNS RFC, each label (for example the prefix in this case) must be equal or less than 63 characters long (octets to be exact), and the whole domain name must be equal or shorter than 255 octets.

  2. These limits also apply to most Kubernetes resource names.

If your Ingress doesn’t generate a route after deploying your application, shorten the URL in your YAML and redeploy.

  1. Apply the ingress object to your APPUiO Cloud project and wait until you route shows as available.

    oc apply -f ingress.yaml

    And wait for your route to be ready:

    oc get routes --watch
  2. After a few seconds, you should be able to get your daily fortune message using curl in plain text!

    curl https://[YOUR_USERNAME]-fortune-python.apps.[YOUR_CHOSEN_ZONE].appuio.cloud --header "Accept: text/plain"

    Or in JSON instead:

    curl https://[YOUR_USERNAME]-fortune-python.apps.[YOUR_CHOSEN_ZONE].appuio.cloud --header "Accept: application/json"

Step 3: There’s no Step 3!

The "Fortune in Python" application is now running on APPUiO Cloud. Congratulations! Hit the R key in your keyboard to see a new fortune message, or just wait 10 seconds to get a new one automatically.

What’s next? To run your own application written in Python or using the Flask Framework on APPUiO Cloud, follow these steps:

  • Containerize the application making sure it’s compatible with APPUiO Cloud. The Dockerfile above can serve as a starting point.

  • Enhance the deployment for your application with liveness and health probes, or better yet, create a Helm chart.

  • Configure your CI/CD system to automatically deploy your application to your preferred APPUiO Cloud zone.

Finally, when you’re done testing the fortune application, delete the fortune-python project with the following command:

oc delete project [YOUR_USERNAME]-fortune-python