Passing files to a Microservice

Housekeeping

See the Getting Started documentation for installing and setting up:

  • microk8s

  • UCF tools

    • Accessing NGC

    • Setting up repositories

Introduction

In the previous two tutorials Partial Redeployment of App and Restarting Pods on Config Changes, we demonstrated how to add configuration files and expose the fields through the microservice parameters. One problem with this is, if the configuration file contains a lot of parameters, exposing them through the microservice parameters might be cumbersome.

Instead, UCF tools allow:

  • microservice to specify that an app should provide entire files/directories and the location to mount them in the microservice containers

  • app to specify the files/directories from the local filesystem against these requirements and automatically mount the files at expected locations in the microservice container

In this tutorial, we will demonstrate how the above can be achieved via the manifest file, the application YAML and UCF Studio.

This tutorial is a continuation of the tutorial Restarting Pods on Config Changes.

Updating the microservices and application

Update the microservice manifest of the HTTP server microservice http-server to the following:

type: msapplication
specVersion: 2.0.0
name: ucf.svc.http-server
chartName: http-server
description: http server
version: 0.0.6
tags: []
keywords: []
publish: false
ingress-endpoints:
  - name: http
    description: REST API endpoint
    protocol: TCP
    scheme: http
    mandatory: False
    data-flow: in-out

params:
  workingDir: "/localvol"
  #> type: string
  #> description: Working directory for listing contents
  #> flags: mandatory
  fileToCreate: somefile.txt
  #> type: string
  #> description: Name of the file to create in the working directory
  #> flags: mandatory

# Files and directories will be mounted with prefix /opt/ext-files/
externalFiles:
- name: app-config.json # File will be available in containers at /opt/ext-files/app-config.json
  description: App Configuration file
  mandatory: True
  isDirectory: False

---
spec:
  - name: http-server-deployment
    type: ucf.k8s.app.deployment
    parameters:
      apptype: stateless

  - name: http-server-container
    type: ucf.k8s.container
    parameters:
      image:
        repository: nvcr.io/nvidia/pytorch
        tag: 22.04-py3
      command: [sh, -c]
      args: [
            "WORKING_DIR=$(cat /opt/ext-files/app-config.json | jq -r '.workingDir') && cd $WORKING_DIR && echo $PWD && touch $(FILE_TO_CREATE) && ls && python -m http.server 8080
            "]
      env:
      - name: POD_NAME
        valueFrom:
          fieldRef:
            fieldPath: metadata.name
      - name: FILE_TO_CREATE
        value: $params.fileToCreate
      ports:
        - containerPort: 8080
          name: http
      volumeMounts:
        - name: localvol
          mountPath: /localvol

  - name: svc
    type: ucf.k8s.service
    parameters:
      ports:
      - port: 8080
        protocol: TCP
        name: http

  - name: localvol
    type: ucf.k8s.volume
    parameters:
      persistentVolumeClaim:
        claimName: local-path-pvc

  - name: cm-dependencies
    type: ucf.appspec.restartPodOnConfigChanges
    parameters:
      # Add dependency on all configmaps detected in the microservice
      addAll: true

      # Add dependency on default configmaps for scripts, configs and workload configs
      # addDefault: true

      # Add dependency on individual configmaps using the configmap names
      # configMaps:
      # - http-server-configs-cm

There are two changes to the manifest file:

  • Version of the microservice has been updated

  • An external file requirement (externalFiles) has been added

  • The container arguments have been updated to read workingDir from the /opt/ext-files/app-config.json file

Updating the parameters in the application

Create a file at path /home/<>/my-config.json next to the app.yaml with following contents:

{
        "workingDir": "/etc"
}

Update the app.yaml file to the following:

specVersion: 2.0.0
version: 0.0.6
doc: README.md
name: server-client-app
description: Server Client Application

dependencies:
- ucf.svc.curl-client:0.0.2
- ucf.svc.http-server:0.0.6

components:
- name: client
  type: ucf.svc.curl-client
- name: http-server
  type: ucf.svc.http-server
  parameters:
    workingDir: /var
    fileToCreate: someotherfile.txt
  files:
    app-config.json: /home/<username>/my-config.json

connections:
    client/http: http-server/http

The file requirement app-config.json of the http-server has been to the newly created file.

Same can be achieved through UCF Studio by selecting the http-server microservice and setting the file path from the property window. UCF Studio provides a file browser to select the file.

UCF Studio - Passing files

Building Microservices and Applications and Deploying them

Follow the steps mentioned below but remember to update the version of the http-server and curl-client under dependencies section in app.yaml.

Inspecting the Microservices and Application

$ microk8s kubectl get all
NAME                                                      READY   STATUS    RESTARTS   AGE
pod/curl-client-curl-client-deployment-569849964-nz598    1/1     Running   0          52m
pod/http-server-http-server-deployment-769c4d5584-xgjg5   1/1     Running   0          41m

NAME                                             TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE
service/kubernetes                               ClusterIP   10.152.183.1     <none>        443/TCP    86d
service/http-server-http-server-deployment-svc   ClusterIP   10.152.183.108   <none>        8080/TCP   52m

NAME                                                 READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/curl-client-curl-client-deployment   1/1     1            1           52m
deployment.apps/http-server-http-server-deployment   1/1     1            1           52m

NAME                                                            DESIRED   CURRENT   READY   AGE
replicaset.apps/curl-client-curl-client-deployment-569849964    1         1         1       52m
replicaset.apps/http-server-http-server-deployment-769c4d5584   1         1         1       52m

Checking the logs of the curl-client pod and the http-server pod confirms that the path /etc mentioned in my-config.json took effect:

$ microk8s kubectl logs --tail -1 -l "app=curl-client-curl-client-deployment"
...
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                             Dload  Upload   Total   Spent    Left  Speed
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Directory listing for /</title>
</head>
<body>
<h1>Directory listing for /</h1>
<hr>
<ul>
<li><a href=".pwd.lock">.pwd.lock</a></li>
<li><a href="adduser.conf">adduser.conf</a></li>
<li><a href="alternatives/">alternatives/</a></li>
<li><a href="apt/">apt/</a></li>
100  4386  100  4386    0     0  1070k      0 --:--:-- --:--:-- --:--:-- 1070k
<li><a href="bash.bashrc">bash.bashrc</a></li>
<li><a href="bash_completion.d/">bash_completion.d/</a></li>
<li><a href="bindresvport.blacklist">bindresvport.blacklist</a></li>
<li><a href="ca-certificates/">ca-certificates/</a></li>
<li><a href="ca-certificates.conf">ca-certificates.conf</a></li>
...
<li><a href="someotherfile.txt">someotherfile.txt</a></li>
...
</ul>
<hr>
</body>
</html>
$ microk8s kubectl logs --tail -1 -l "app=http-server-http-server-deployment,microservice_version=0.0.6"
/etc
X11
adduser.conf
alternatives
apt
bash.bashrc
bash_completion.d
bindresvport.blacklist
ca-certificates
ca-certificates.conf
cron.d
cron.daily
debconf.conf
...

This confirms that the file on host filesystem got mounted inside the microservice containers.

Stopping and Cleaning up Microservices and Applications

Finally, to stop and clean up the application we can run:

$ microk8s helm3 uninstall server-client

Some Notes

  • Informational logs similar to following will be seen when running any helm commands on the output application helm chart. This is because symlinks are used to add the files to the helm chart. These can be safely ignored

    walk.go:74: found symbolic link in path: /home/<username>/ucf_tutorial/apps/server-client-app/server-client-app-0.0.6/charts/http-server/files/app-config.json resolves to /home/<username>/my-config.json
    
  • This method internally adds the files to a configmap which gets mounted in the microservice pods

  • Total accumulated size of files & directories being passed to a microservice must be less than 1MiB per microservice. This restriction comes from Kubernetes.

  • In case you want to pass files / directories larger than 1MiB, you must do so by creating volumes, populating it and setting parameters to mount the volumes on the pods. When doing so, set the the file requirement to special keyword __FROM_VOLUME__ in the app, so that appbuilder will not run any checks.

    - name: http-server
      type: ucf.svc.http-server
      files:
        app-config.json: __FROM_VOLUME__