Runtime Extensions

Runtime extensions allow you to define custom runtimes based on Kubernetes jobs. For example, you can define a runtime for deploying VMs used across several applications and services.

πŸ“˜

Beta Feature

This feature is in beta. Some parts are still being developed (as denoted in the sections) and some APIs may change. Talk to us before using.

Configuring a Runtime Extension

To define a runtime extension, create a Prodvana config file. You will need a docker image that does the actual apply in the runtime and a Kubernetes runtime already linked where your job will be running. See Configuring a Runtime for how to link a Kubernetes runtime.

runtime:
  name: my-custom-runtime
  extension:
    proxyRuntime:
        runtime: my-kubernetes-runtime  # pick a kubernetes runtime already linked with Prodvana
    apply:
      taskConfig:
        program:
          cmd:
          - apply-changes
          - --my-change-id={{.Params.changeId}}
          imageTag: my-tag
          imageRegistryInfo:
            containerRegistry: my-registry-name  # pick any registry already linked with Prodvana
            imageRepository: my-repository
    parameters:
    - name: changeId  # parameters can be whatever makes sense for your runtime
      description: the ID to deploy
      string:
            defaultValue: default-id

In the example above, the runtime extension is defined with the apply command apply-changes --my-change={{.Params.changeId}}, running in the my-kubernetes-runtime using the image my-repository:my-tag in the registry my-registry-name. Further more, changeId is defined as a parameter. This is useful as it will allow services deployed to the runtime to customize its behavior, e.g. picking a different VM image or specifying the replica count.

Apply the config with pvnctl configs apply.

pvnctl configs apply my-custom-runtime.pvn.yaml

To use the runtime, reference it in your application config in each of your release channel. It is also possible to have different runtime extensions be used for different release channels, e.g. to have production use a different set of commands than staging.

application:
  name: my-app # replace with your desired app name
  releaseChannels:
  - name: staging
    runtimes:
    - runtime: my-custom-runtime
      type: EXTENSION
  - name: production
    runtimes:
    - runtime: my-other-custom-runtime  # can be the same runtime as the previous step
      type: EXTENSION
    preconditions:
    - releaseChannelStable:
        releaseChannel: staging
    - manualApproval: {}

Next, define a service that uses the runtime extension.

service:
  name: my-service
  application: my-application
  runtimeExtension:
    parameterValues:
    - name: changeId
      string: my-change-id-for-this-service

It is also possible to have the service itself define parameters which are then passed through to the runtime, allowing this parameter to be set from the deploy UI.

service:
  name: my-service
  application: my-application
  runtimeExtension:
    parameterValues:
    - name: changeId
      string: "{{.Params.changeId}}"
  parameters:
  - name: changeId
    string:
      defaultValue: my-default-change-id

Common Examples

Using an Existing Namespace

By default, runtime extension commands will run in an automatically generated namespace, pvn-proxy-runtime-{runtime-name}. However, it can be useful to run commands in an existing namespace created outside of Prodvana, e.g. to reference an existing Kubernetes secret.

To run commands in an existing namespace, modify the runtime config:

runtime:
  name: my-custom-runtime
  extension:
    proxyRuntime:
      runtime: my-kubernetes-runtime  # pick a kubernetes runtime already linked with Prodvana
      containerOrchestration:
        k8s:
            namespace: my-existing-namespace
    ... # the rest of your config here

You are responsible for making sure the namespace exists.

Using Multiple Runtime Extensions

It is possible to have multiple types of runtime extensions configured in a release channel. For example, you may have one kind of runtime for deploying VMs and another for pushing static assets. To do so, define a name for the runtime:

application:
  name: my-app # replace with your desired app name
  releaseChannels:
  - name: staging
    runtimes:
    - runtime: my-vm-runtime
      type: EXTENSION
      name: vm
    - runtime: my-static-assets-runtime
      type: EXTENSION
      name: static
  - name: production
    runtimes:
    - runtime: my-prod-vm-runtime  # can be the same or different from staging
      type: EXTENSION
      name: vm  # this name must match the one defined in staging
    - runtime: my-prod-static-assets-runtime  # can be the same or different from staging
      type: EXTENSION
      name: static
    preconditions:
    - releaseChannelStable:
        releaseChannel: staging
    - manualApproval: {}

Then, in your service config, reference the name to decide which runtime to use.

service:
  name: my-service
  application: my-application
  runtimeExtension: ...  # rest of your config here
  runtimeConnection: vm

Using Kubernetes Configurations

To define commands via Kubernetes configurations, use kubernetesConfig instead of taskConfig in your runtime config file.

runtime:
  name: my-custom-runtime
  extension:
    proxyRuntime:
        runtime: my-kubernetes-runtime  # pick a kubernetes runtime already linked with Prodvana
    apply:
      kubernetesConfig:
        type: KUBERNETES
        local:
          path: path/to/kubernetes/job.yaml
    parameters:
    - name: changeId  # parameters can be whatever makes sense for your runtime
      description: the ID to deploy
      string:
            defaultValue: default-id

🚧

Define Exactly One job and Nothing Else

Defining any other Kuberntes resources here (e.g. configmap) will result in undefined behaviors due to conflicts when multiple services use this runtime extension.

Parameters can still be used, directly inside the Kubernetes configs themselves. The order of operation with parametrization is the same as with service configurations, see Configuring Services.

(Coming Soon) Defining a Custom Fetch Function

Defining a fetch function allows Prodvana to continuously converge services in your runtime. This can be useful, for example, for Prodvana to converge the runtime back to desired state if some external system changed the runtime temporarily. Defining a fetch function also results in a richer UX, with Prodvana able to display information like current versions and number of pods more accurately.