GuidesAPI Reference
Log In

Configuring Services

To define a Service, create a Prodvana config in a directory of your choosing, e.g. if you have an existing directory you use to store other configurations for the Service, use that. Below is an example of a Service defined to read in Kubernetes configurations in the same directory.

service:
  name: nginx
  application: my-application
  kubernetesConfig:
    type: KUBERNETES
    local:
      path: .

Use pvnctl to apply this config file:

pvnctl configs apply path/to/my-service.pvn.yaml

Once applied, the Service will be configured but not deployed. You can see it by going to my-demo-organization.runprodvana.com -> Applications -> your Application -> your Service.

Releasing from UI

To deploy the Service from the Service UI, click "create release" and follow the on-screen instructions.

Parametrizing from UI

You can define parameters in your service config that can then be set at Release time in the UI. This is useful for fast changes of safe fields, such as replica count or container image, without going through a code commit.

service:
  name: nginx
  application: my-application
  kubernetesConfig:
    type: KUBERNETES
    local:
      path: .
  parameters:
  - name: image
    required: true
    dockerImage:
      imageRegistryInfo:
        containerRegistry: dockerhub-public
        imageRepository: library/nginx

You can then use {{.Params.image}} where you'd like the image name to be templated in, including in your Kubernetes configuration.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: "{{.Params.image}}"

Template Variables

Template variables are available for Service configs and any Kubernetes, Kustomize, or Helm configs they reference. These variables are replaced with their actual values for each Release Channel. Template variables are referenced using the {{ .SomeVariable.nestedField }} or {{ .SomeVariable["nestedField"] }} syntax.

VariableValue
.Params.<parameter name>, e.g. .Params.imageThe parameter value for that Release Channel
.Constants.<constant name>, e.g. .Constants.envThe constant value for that Release Channel. Services inherit constants from release channels but may supply their own override. See "Defining Constants" below.
.Builtins.ReleaseChannel.NameThe Release Channel name
.Builtins.ReleaseChannel.Labels.<label name>, e.g. .Builtins.ReleaseChannel.Labels.envThe label value for the Release Channel. See Labels.
.Builtins.Runtime.NameThe Runtime name this Service runs on for this release channel
.Builtins.Runtime.Labels.<label name>, e.g. .Builtins.Runtime.Labels.envThe label value for the Runtime. See Labels.

Template variables can be used anywhere a string is expected, with the following exceptions:

  • local.path and local.excludePatterns under kubernetesConfig

Common Examples

Using Kubernetes Config

Prodvana works natively with your Kubernetes configs. When using Kubernetes configs, Prodvana will automatically replace the namespace with the namespace of the release channel being deployed. Prodvana will also add additional environment variables (PVN_*) as well as any environment variables that are set in the release channel config as defaults, as long as it recognizes the Kubernetes objects (deployment, job supported today).

service:
  name: my-service
  kubernetesConfig:
    type: KUBERNETES
    local:
      path: path/to/config/relative/to/this/file  # can also be a directory, in which case all yaml files are selected

Parameters can be used with Kubernetes configs.

service:
  name: my-service
  kubernetesConfig:
    type: KUBERNETES
    local:
      path: path/to/config/relative/to/this/file  # can also be a directory, in which case all yaml files are selected
  parameters:
    - name: image
      required: true
      dockerImage:
        imageRegistryInfo:
          containerRegistry: my-registry-name
          imageRepository: my-repository

The template variables should be used directly inside Kubernetes configurations, such as

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: my-deployment
  template:
    metadata:
      labels:
        app: my-deployment
    spec:
      containers:
        - name: api
          image: "{{.Params.image}}"

The order of operation is:

  1. pvnctl reads in your local config file and passes it as a string to Prodvana.
  2. Prodvana sets namespace and injects environment variables.
  3. Prodvana evaluates and applies template variables.

Templating Kubernetes Config Path

local.path field under kubernetesConfig does not support the use of template variables, as pvnctl uses this value to read your local config files before evaluating variables.

To use template variables in config paths, use path to specify an overarching directory and use subPath instead to control which file or directory under path to read.

service:
  name: my-service
  application: my-application
  kubernetesConfig:
    type: KUBERNETES
    local:
      path: configs
      subPath: "{{.Params.myParam}}"
  ...

In the example above, say the value of myParam is my-value, the config being used will be under configs/my-value.

When subPath is used:

  • path must be a directory
  • the entirety of path is uploaded to Prodvana as a tarball

If the config type is KUSTOMIZE, pvnctl will find all kustomization files and run kubectl kustomize to get the materialized config. This can result in errors if kustomization components are included under path and cannot be evaluated on their own. To exclude them, use excludePatterns.

service:
  name: my-service
  application: my-application
  kubernetesConfig:
    type: KUSTOMIZE
    local:
      path: configs
      subPath: "{{.Params.myParam}}"
      excludePatterns:  # follows gitignore convention
      - components

Different Kubernetes Config Per Release Channel

If you need to customize Kubernetes configs per Release Channel beyond namespaces and environment variables, the easiest way is to use template variables with subPath. For example:

service:
  name: my-service
  kubernetesConfig:
    type: KUBERNETES
    local:
      path: .
      subPath: "{{.Builtins.ReleaseChannel.Name}}"

If needed, you can also explicitly pass a different config per Release Channel.

service:
  name: my-service
  application: my-application
  perReleaseChannel:
    - releaseChannel: staging
      kubernetesConfig:
        type: KUBERNETES
        local:
          path: staging-configs/
    - releaseChannel: prod
      kubernetesConfig:
        type: KUBERNETES
        local:
          path: prod-configs/

perReleaseChannel acts as an override, so if you have all but one release channel needing a different set of configs, you can also set it as:

service:
  name: my-service
  kubernetesConfig:
    type: KUBERNETES
    local:
      path: global
  perReleaseChannel:
    - releaseChannel: staging
      kubernetesConfig:
        type: KUBERNETES
        local:
          path: staging-configs/

Patching Kubernetes Config

Patching can be useful where templating directly into the Kubernetes config is not possible. For example, you may be using a strict Kubernetes YAML linter that will not let you put the template variable string into places where an int is expected.

For example, here is how you would patch the replica count of a Deployment named my-deployment, with the value of a constant, so that the Release Channel production has a different replica count than everything else.

service:
  name: my-service
  kubernetesConfig:
    type: KUBERNETES
    local:
      path: .
    patches:
    - target:  # omit to patch every Kubernetes object referenced
        kind: Deployment
        name: my-deployment
      replace:
        path: /spec/replicas  # json6902 path
        intAsString: '{{.Constants.replicas}}'
  constants:
  - name: replicas
    string:
      value: '3'
  perReleaseChannel:
  - releaseChannel: 'production'
    constants:
    - name: replicas
      string:
        value: '5'

Patching also works with Kustomize configs, the exact same way.

service:
  name: my-service
  kubernetesConfig:
    type: KUSTOMIZE  # works with Kustomize
    local:
      path: .
    patches:
    - target:
        kind: Deployment
        name: my-deployment
      replace:
        path: /spec/replicas  # json6902 path
        intAsString: '{{.Constants.replicas}}'
  ...  # the rest of configs here

Using Public Helm Charts

To deploy public Helm charts:

service:
  name: kubecost
  application: my-app
  helm:
    remote:
      repo: https://kubecost.github.io/cost-analyzer/
      chart: kubecost/cost-analyzer
      chartVersion: "1.102.2"

Values.yaml can also be specified, like:

service:
  name: my-service
  application: my-app
  helm:
    remote: ...  # helm chart reference here
    valuesOverride:
    - local:
        path: my-local-values.yaml

Values.yaml have access to template variables like parameters, too.

service:
  name: my-service
  application: my-app
  helm:
    remote: ...  # helm chart reference here
    valuesOverride:
    - local:
        path: my-local-values.yaml  # the content of this file can reference template variables, like {{.Params.myImage}}
  parameters:
  - name: myImage
    required: true
    dockerImage:
      imageRegistryInfo:
        containerRegistry: my-registry-name
        imageRepository: my-repository
    

Using Local Helm Charts

To use Helm charts defined locally in your own repository (that is, you have a Chart.yaml file):

service:
  name: my-service
  application: my-app
  helm:
    local:
      path: .  # path to directory containing Chart.yaml
    valuesOverride: ...  # as needed

In this mode, pvnctl configs apply must be run on a machine with helm installed. pvnctl will run helm package to package your chart into a tarball and upload it to Prodvana.

With Metadata

Services can have metadata like descriptions and links. This should be included in the same config file.

service:
  ... # service config here
serviceMetadata:
  description: "description here"
  links:
  - url: "http link for your service"
    displayName: "display name"

Skipping Cleanup on Deletion

By default, Prodvana will attempt to delete any objects it created (such as, Kubernetes deployments) when a Service is deleted. This can be undesirable during the migration period to Prodvana and can be skipped by a flag in the config file:

service:
  ... # service config here
  noCleanupOnDelete: true

Referencing Multiple Kubernetes/Kustomize Configs, Controlling Order

It is possible to reference multiple Kubernetes configs (either in Kubernetes yaml format or Kustomize).

service:
	name: my-service
  kubernetesConfig:
    type: KUBERNETES # or, KUSTOMIZE
    local:
      paths:
      - path/to/config/relative/to/this/file
      - path/to/other/config/relative/to/this/file

At pvnctl configs apply time, the configs are read in order. For directories, all yaml files (except .pvn.yaml files) are read in. The configs are then joined into a single YAML file delimited by ---.

Currently, mixing and matching Kubernetes configs with Kustomize configs are not supported.

Defining Constants

Constants can be defined and used in Service configs to customize the fully materialized configs in each release channel. Constants defined by Services override any defined by Release Channels. See .Configuring Applications.

service:
  name: my-service
  ...  # refer to `{{.Constants.myConstant}}` to access the value of your constants anywhere in your configuration, including Kubernetes configurations.
  perReleaseChannel:
  - releaseChannel: production
    constants:
    - name: myConstant
      string:
      	value: myProductionValueForThisService
  - releaseChannel: staging
    constants:
    - name: myConstant
      string:
      	value: myStagingValueForThisService

Constants can also be used in Kubernetes configs directly, the same as parameters.

Adding Environment Variables

Services can have environment variables defined at the Service and Service Instance level.

Specifying environment variables at the Service scope can be helpful for a few use cases:

  • overriding Release Channel default environment variables (see Configuring Applications)
  • passing different environment variables per Release Channel while using a single Kubernetes config file
  • injecting environment variables for a Kubernetes config file you do not own or do not want to modify

When environment variables are specified at multiple locations, they are given the following precedence (from highest to lowest):

  • per-Release-Channel environment variables at the service config layer
  • top-level environment variables at the Service config layer
  • environment variables at the Application's Release Channel config layer
service:
  name: my-service
  env:
    MY_ENV:
      value: value
  perReleaseChannel:
  - releaseChannel: staging
    env:
      MY_OTHER_ENV:
        value: staging-value
  # the rest of config here, including Kubernetes configs

Disable Automatic Rollbacks

By default, releases initiated from the Prodvana UI will automatically roll back to the last known converged version when the Release fails. To disable this behavior, add the following to your service config:

service:
  name: my-service
  autoRollback:
    disabled: true
  ...

Customizing Bundle Naming Schemes

As part of creating a release, Prodvana will create a new bundle of configuration files for your Service. By default, it will name this bundle using contextual information from Service parameters, falling back to $serviceName-{NN} when it can't. You can customize this naming convention.

service:
  name: my-service
  bundleName: some-templated-bundle-name
  ... # rest of config here

For example, if you have a semantic version string (e.g. v1.0.2) as parameter and always wants to use that, you can do:

service:
  name: my-service
  bundleName: '{{.Params.semanticVersion}}'
  parameters:
  - name: semanticVersion
    required: true
    string: {}

You can make the templating arbitrarily complex. For example, if you have two parameters, one semantic version string and one docker image, and you want the bundle name to be the version string and the first 6 letters of the image tag, you can do:

service:
  name: my-service
  bundleName: '{{.Params.semanticVersion}}-{{ slice .Params.image.Tag 0 6 }}'
  ... # rest of config here
  parameters:
  - name: semanticVersion
    required: true
    string: {}
  - name: image
    required: true
    dockerImage:
      imageRegistryInfo:
        containerRegistry: my-registry
        imageRepository: my-repo