Developing an App for Great Bear
This tutorial is the first part of a four part series. The series is about creating a basic application for Great Bear, extending it with a liveness probe, metrics and finally publishing it.
Other parts of the series:
- Second part: Adding Liveness Probe to an app
- Third part: Adding Prometheus metrics to an app
- Fourth part Publishing an App to Great Bear
This guide gives an example of how to develop an application for Great Bear. The guide assumes that you are already familiar with using docker and creating docker images, creating Helm charts for Kubernetes, and deploying applications to Kubernetes clusters using Helm charts. The guide also assumes that you have docker and helm installed, and the required access credentials for Great Bear. For more information on the development process for Great Bear, see Great Bear Application Lifecycle.
You are going to create a very basic application called ‘Bear Echo’, which is one of the simplest apps you can deploy with Great Bear, and can be useful for testing purposes. Bear Echo will use python Flask to serve a simple webpage, and allow users to define a custom text message when deploying the app that is then used as header in the rendered web page.
Create the application and Dockerfile
-
First step is to create a new directory called
bear-echo
where you will save the code for the application. -
Next we need to write the python code for the Bear Echo server, create a file in the
bear-echo
directory calledserver.py
, copy the following code and save the file:# bear-echo/server.py import os import sys import importlib from flask import Flask _HTTP_PORT: int = int(os.getenv('HTTP_PORT') or '11777') _BASE_URL: str = os.getenv('BASE_URL') or '/' _ECHO_TEXT: str = os.getenv('ECHO_TEXT') or 'Hello World' app = Flask(__name__) @app.route(_BASE_URL, methods=['GET']) def echo(): return(f'<hr><center><h1>{_ECHO_TEXT}</h1><center><hr>') def _main(): print('ECHO_TEXT: ' + _ECHO_TEXT) print(f'Started on 0.0.0.0:{_HTTP_PORT}{_BASE_URL}') app.run(host='0.0.0.0', port=_HTTP_PORT) if __name__ == '__main__': _main()
-
Now we have our python application, we need a Dockerfile to create the image that will be uploaded to the Great Bear Application Store,. Create a file in the
bear-echo
directory calledDockerfile
, copy the following code and save the file:# bear-echo/Dockerfile FROM python:3.8-alpine WORKDIR /echo RUN pip install flask COPY server.py ./ EXPOSE 11777 CMD [ "python", "server.py" ]
Create the Great Bear Application Package (GBAP)
- With the Dockerfile created, you can now move onto creating the GBAP, used to publish our application to the Great Bear Application Store for subsequent deployment to Great Bear managed sites through the Great Bear Dashboard.
- Follow the steps to install and configure the Great Bear Packaging SDK tool.
- Use the SDK tool to create a new GBAP instance within your
bear-echo
directory, entering the desired fields as prompted:$ gbear app create bear-echo/GBAP Creating a GBAP with required fields.... Enter the chart name: bear-echo Enter the chart version [SemVer2 MAJOR.MINOR.PATCH], (leave empty for default: 0.0.1): Enter the application name: bear-echo Enter the application description: A simple HTTP echo service, based on Python Flask. Successfully created a GBAP in path bear-echo/GBAP
- The SDK tool has now created a boiler plate GBAP filesystem tree:
bear-echo/GBAP ├── Chart.yaml ├── gbear │ └── appmetadata.yaml
Author the GBAP Assets
The GBAP contains the following two fundamental elements:
- A Helm Chart to describe a set of Kubernetes resources used to deploy the application at Great Bear managed sites.
- Great Bear specific application metadata (gbear/appmetadata), containing:
- Properties used to render the application in the Great Bear Application Store
- Properties used to define application parameters which can be overridden when deploying the Helm Chart.
For further details on the GBAP structure, see Great Bear Application Package Guide
Create the Helm values file
-
Inside the
GBAP
directory, we next need to create a file calledvalues.yaml
.bear-echo/GBAP ├── Chart.yaml ├── gbear │ └── appmetadata.yaml ├── values.yaml
-
This file will contain the default values for our Bear Echo application, and includes read-only parameters like
GB_SITE_NAME
andGB_NODE_NAMES
which are provided by Great Bear as environment variables when it deploys the application. It also defines the image repository location, and you should make sure to replace<tenant-registry-url>
in therepository
value to the tenant registry URL communicated to you by Cisco.bear-echo/GBAP/values.yaml:
replicaCount: 1 strategyType: Recreate image: repository: <tenant-registry-url>/bear-echo tag: 0.0.1 pullPolicy: IfNotPresent imagePullSecrets: - name: gbear-harbor-pull config: # Config parameters described in application metadata # Their values will be injected here by the Great Bear deployer # GB_SITE_NAME: | Site name with "quotes" and 'quotes' GB_NODE_NAMES: | {"node1":"Display name 1 with \"quotes\" and 'quotes'","node2":"Display name 2 with \"quotes\" and 'quotes'"} ACCESS_PORT: | "31777" service: type: NodePort port: 11777 ports: http: port: 11777 protocol: TCP nodePort: "{{ default 31777 (int .Values.config.ACCESS_PORT)}}" # ---- nameOverride: "" fullnameOverride: "" nodeSelector: {} affinity: {} ingress: enabled: false istioGateway: "" servicePath: "" siteId: ""
Create the Helm template files
-
The next step is to create the template files that will create our Kubernetes deployment and the service endpoint for the deployment, along with some template helpers that are re-used throughout the chart. Therefore, inside the
GBAP
directory, create a new directory calledtemplates
bear-echo/GBAP ├── Chart.yaml ├── gbear │ └── appmetadata.yaml ├── templates
-
The first file we will create in the new
templates
directory is_helpers.tpl
, which will hold some template helpers that we can re-use throughout the chart. Yourbear-echo/GBAP/templates/_helpers.tpl
file should contain the following helpers:{{/* Expand the name of the chart. */}} {{- define "app.name" -}} {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} {{- end -}} {{/* Create a default fully qualified app name. We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). If release name contains chart name it will be used as a full name. */}} {{- define "app.fullname" -}} {{- if .Values.fullnameOverride -}} {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} {{- else -}} {{- $name := default .Chart.Name .Values.nameOverride -}} {{- if contains $name .Release.Name -}} {{- .Release.Name | trunc 63 | trimSuffix "-" -}} {{- else -}} {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} {{- end -}} {{- end -}} {{- end -}} {{/* Create chart name and version as used by the chart label. */}} {{- define "app.chart" -}} {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} {{- end -}} {{/* Common labels */}} {{- define "app.labels" -}} app.kubernetes.io/name: {{ .Chart.Name }} app.kubernetes.io/instance: {{ .Release.Name }} app.kubernetes.io/managed-by: {{ .Release.Service }} helm.sh/chart: {{ include "app.chart" . }} {{- end -}} {{/* Selector labels */}} {{- define "app.selector" -}} app.kubernetes.io/name: {{ .Chart.Name }} app.kubernetes.io/instance: {{ .Release.Name }} {{- end -}}
-
The next file to create in the
templates
directory is thedeployment.yaml
file, which will provide a basic manifest for creating the Kubernetes deployment our Bear Echo application. In our case, thebear-echo/GBAP/templates/deployment.yaml
should contain:apiVersion: apps/v1 kind: Deployment metadata: name: {{ include "app.fullname" . }} labels: {{- include "app.labels" . | nindent 4 }} spec: replicas: {{ .Values.replicaCount }} strategy: type: {{ .Values.strategyType }} selector: matchLabels: {{- include "app.selector" . | nindent 6 }} template: metadata: labels: {{- include "app.selector" . | nindent 8 }} spec: {{- with .Values.imagePullSecrets }} imagePullSecrets: {{ toYaml . | indent 8 | trim }} {{- end }} {{- with .Values.podSecurityContext }} securityContext: {{ toYaml . | indent 8 | trim }} {{- end }} containers: - name: {{ .Chart.Name }} image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" imagePullPolicy: {{ .Values.image.pullPolicy }} env: {{- with .Values.config }} {{- range $key, $value := . }} - name: {{$key}} # Some config parameters (GB_SITE_NAME, GB_NODE_NAMES) # can contain both quotes "" and '', for example: {"node1": "User's home"} # and the value itself should be quoted in order not to be parsed as object # so it's required to put it as a text field to make both quotes processable value: | {{$value -}} {{- end }} {{- end }} {{- with .Values.ports }} ports: {{- range $key, $value := . }} - name: {{ $key }} containerPort: {{ $value.port }} protocol: {{ default "TCP" $value.protocol }} {{- end }} {{- end }} {{- with .Values.securityContext }} securityContext: {{ toYaml . | indent 12 | trim }} {{- end }} {{- with .Values.resources }} resources: {{ toYaml . | indent 12 | trim }} {{- end }} {{- with .Values.nodeSelector }} nodeSelector: {{ toYaml . | indent 8 | trim }} {{- end }} {{- with .Values.affinity }} affinity: {{ toYaml . | indent 8 | trim }} {{- end }} {{- with .Values.tolerations }} tolerations: {{ toYaml . | indent 8 | trim }} {{- end }}
-
The final file that is needed to complete the
templates
directory is theservice.yaml
file, which provides a basic manifest for creating a service endpoint the Bear Echo deployment. Thebear-echo/GBAP/templates/service.yaml
should contain this code:apiVersion: v1 kind: Service metadata: name: {{ include "app.fullname" . }} labels: {{- include "app.labels" . | nindent 4 }} {{- with .Values.service.annotations }} annotations: {{- range $key, $value := . }} {{ $key }}: {{ $value | quote }} {{- end }} {{- end }} spec: type: {{ .Values.service.type }} {{- with .Values.ports }} ports: {{- range $key, $value := . }} - name: {{ $key }} port: {{ $value.port }} nodePort: {{ tpl $value.nodePort $ }} protocol: {{ default "TCP" $value.protocol }} {{- end }} {{- end }} selector: {{- include "app.selector" . | nindent 4 }}
Customise the Great Bear Application Metadata
-
The SDK tool has generated the following boiler plate
gbear/appmetadata.yaml
file, containing our application name and description entered when we created the GBAP.bear-echo/GBAP/gbear/appmetadata.yaml:
displayName: bear-echo description: A simple HTTP echo service, based on Python Flask. # The following optional fields provide a more detailed properties to describe your GBAP. # Uncomment as required and modify field values specifically for your application. # Specify whether the application should be deployed once per great bear site (defaults to false if not explicitly defined) #singleton: true # A dictionary (key-values) to label applications to handle specially like kind: EXAMPLE These should be explicit attributes. #labels: # kind: EXAMPLE # A list of tags used by the Great Bear application store to present applications such as e.g the private attribute. #tags: # - example-apps # - blog # A list of architectures supported like [amd64, arm64v8] #architectures: # - amd64 # A path/URL of icon to use in the app store #icon: http://example.com/appicon # A list of configuration fields that are provided as run-time values of the Helm deployment. #configuration: # - name: outputMessage # The name of the field. # key: application.message # A dot-separated name where the value of this field will be added to the Helm values. Like postgres.image.tag. Defaults to config.{name} (current behavior). # title: Output message # The label of the parameter to be displayed on the Great Bear dashboard when deploying the application. # description: Message sent to the user # The description of the parameter to be displayed on the Great Bear dashboard when deploying the application. # value: Hello World # The default value of the parameter # type: String # A simple string, rendered as a textbox # - name: password # key: application.password # title: Initial password # type: Secret # A string, rendered as a password input. # - nodeport # key: service.nodePorts.https # type: NodePort # A port number in the 30000-32767 range. # title: Node port to serve HTTPS on # value: 37777 # - name: sitename # key: application.mySite # type: Runtime # An injected variable via Go Templating performed on the value of this field. Field is not shown on the UI. # value: This application is running in {{.GB_SITE_ID}} # This value will be substituted by GB when the application is deployed to the edge # - name: forceLoginToggle # key: application.forceLogin # choices: [yes, no] # UI renders the parameter as a toggle switch, the UI will recognise the [yes, no] options as a toggle switch. # value: yes # Dashboard will display a toggle switch in the enabled position # - name: applicationTextColor # key: application.textColor # choices: [black, red, blue] # UI renders the parameter as a dropdown list, containing the specified values. # value: red # Dashboard will display a dropdown list with specified value 'red' as the default
-
The next step is to specialise this boiler plate file with specific application metadata for our
bear-echo
application, editing properties to determine its visibility in the Application Store, and specify which parameters a user can set when the application is deployed. Edit yourbear-echo/GBAP/gbear/appmetadata.yaml
file to include the following properties, which enables ECHO_TEXT and ACCESS_PORT to be defined by a user deploying the application through Great Bear, and other important details:displayName: bear-echo description: A simple HTTP echo service, based on Python Flask. icon: https://raw.githubusercontent.com/FortAwesome/Font-Awesome/5.15.4/svgs/solid/tools.svg tags: - debug - sample # Defines whether the app can be deployed to the same site several times singleton: true configuration: - name: "ECHO_TEXT" title: "Message to say" description: "The application will echo this text" value: "A Hello from Great Bear." # Default used if not overridden by the user type: String - name: "ACCESS_PORT" title: "Access HTTP port (30000-32767)" value: 31777 # Default used if not overridden by the user type: NodePort
Validate the GBAP
- Now that we have finished authoring our GBAP assets, we can use the SDK tool to validate the contents:
$ gbear app validate bear-echo/GBAP
Validating Package: charts/
-- Validating Application Metadata...
-- Validating Chart Dependencies...
-- Validating Kubernetes Resource Labels...
Validation Complete:
Application Metadata: Passed (Errors: 0, Warnings: 0)
Chart Dependencies: Passed (Errors: 0, Warnings: 0)
Kubernetes Resource Labels: Passed (Errors: 0, Warnings: 0)
For further details on GBAP validation, see Validate an Application Package
- As the GBAP contains a Helm Chart, it is good practice to test that the Chart can be successfully installed to a local Kubernetes cluster using
helm install bear-echo charts/ --dry-run --debug
Next steps
Assuming the everything is setup correctly, we can now move on to extend the application, see: