This tool is used to generate pipeline JSON for Spinnaker from Kubernetes manifest files.
The basic premise is that Kubernetes already has a well defined standard for how to define cluster resources (ReplicaSets, Deployments, etc), but there's no way to glue them into Spinnaker pipeline stages. That's what this tool aims to provide.
Using a pipeline configuration YAML file, you can define stages and reference Kubernetes manifest definitions to help fill in:
- The environment variables
- The container image
- The command and arguments
- Ports and load balancers
This is a lengthy example of how a pipeline YAML looks.
name: Example Deployment
application: example
disableConcurrentExecutions: true
keepQueuedPipelines: true
description: This pipeline deploys some sweet code
notifications:
- address: "#launchpad"
type: "slack"
when:
- pipeline.complete
- pipeline.failed
message:
pipeline.complete: |
The stage has completed!
pipeline.failed: |
The stage has failed!
parameters:
- name: "random"
description: "random description"
required: true
default: "hello-world"
triggers:
- jenkins:
job: "Example/job/master"
master: "jenkins"
propertyFile: "build.properties"
enabled: true
- webhook:
source: "random-string"
enabled: true
imageDescriptions:
- name: main-image
account: "namely-registry"
image_id: "${ trigger.properties['docker_image'] }"
registry: "registry.namely.land"
repository: "namely/example-all-day"
tag: "${ trigger.properties['docker_tag'] }"
organization: "namely"
- name: second-image
account: "namely-registry"
image_id: "${ trigger.properties['docker_image'] }"
registry: "registry.namely.land"
repository: "namely/second-repo-name"
tag: "${ trigger.properties['docker_tag'] }"
organization: "namely"
stages:
- account: int-k8s
name: "Migrate INT"
runJob:
manifestFile: test-deployment.yml
imageDescriptions:
- name: main-image
containerName: example
container: # override default command defined in the manifest
command:
- bundle
- exec
- rake
- db:migrate
notifications:
- address: "#launchpad"
type: "slack"
when:
- stage.complete
- stage.failed
message:
stage.complete: |
The stage has completed!
stage.failed: |
The stage has failed!
- account: int-k8s
name: "Deploy to INT"
deploy:
groups:
- manifestFile: test-deployment.yml
maxRemainingASGS: 2 # total amount of replica sets to keep around afterwards before deleting
scaleDown: true
stack: web # primarily for labeling purposes on the created resources
details: helloworld
strategy: redblack
targetSize: 2 # this is the total amount of replicas for the deployment
containerOverrides: {}
imageDescriptions:
- name: main-image
containerName: example
- name: second-image
containerName: init-example
loadBalancers:
- "test"
- account: int-k8s
name: "Proceed to Staging?"
refId: "3"
reliesOn:
- "2"
manualJudgement:
timeoutHours: 48
failPipeline: true
instructions: |
If this stage has completed QA, press proceed.
If you have a Go environment installed and configured, you can use go get
to install the latest package of this project:
$ go install github.com/namely/k8s-pipeliner/cmd/k8s-pipeliner
To use it:
$ cd your-project
$ k8s-pipeliner create pipeline.yml
If you want a pretty view and have JQ installed, you can do:
$ k8s-pipeliner create pipeline.yml | jq .
To copy the result to your clipboard and you're on a Mac, you can do:
$ k8s-pipeliner create --linear pipeline.yml | pbcopy
Pull the latest from master branch and run
make install
Once you've copied the resulting JSON from the pipeline configuration, you can go modify an already created Pipeline by clicking "Pipeline Actions" -> "Edit as JSON".
Paste the JSON, and then in the bottom right of the screen click "Save".
Here are the independent pieces of schema for pipeline.yml that you can use. You can also take a look at the Config Definitions.
We currently support 2 types of triggers in k8s-pipeliner, webhooks and jenkins.
triggers:
- webhook:
source: "random-string"
enabled: true
The "source" field in webhooks is the endpoint that you need to hit in order to kick off Spinnaker. If you're running the gate API at "gate-api.example.com", your webhook endpoint from this configuration would be https://gate-api.example.com/webhooks/webhook/random-string (random-string is our source value)
Spinnaker also supports triggering off of Jenkins jobs completing, to use this trigger include a jenkins
field:
triggers:
- jenkins:
job: "Example/job/master"
master: "jenkins"
propertyFile: "build.properties" # optional
enabled: true
If you want to have a manual judgement in your pipeline, you can define a manualJudgement
step within the stages
array:
stages:
- name: "Continue Deploy?"
manualJudgement:
failPipeline: true
instructions: |
Once you're confident with this deploy, please approve it to continue.
- If
failPipeline
is set to true, the manual judgement must be approved for the rest of the pipeline to continue. - The
instructions
are displayed within the UI when the pipeline is stalled waiting for a manual judgement. This is useful for whoever is providing the manual judgement to have context.
A Job is a step in a pipeline that runs a one off task. A good example might be running a database migration before rolling out a piece of code.
stages:
- name: "Run Migrations"
runJob:
manifestFile: manifests/deployment.yml
container:
command:
- bundle
- exec
args:
- rake
- db:migrate
manifestFile
is used to generate the majority of the stage JSON for running the container. Things like environment, volumes, commands, etc are all stored within this Kubernetes Manifest file.container
key is used to overwrite some of the values that are provided in themanifestFile
. For example, if you want to run a migration script that is provided in the container instead of the defaultrails server
, this is where you'd define it.command
portion of the container override overwrites thecommand
portion of the container being run in the job.args
portion of the container override overwrites theargs
portion of the container being run in the job.
A Deploy stage is used for running new server groups. You can use this stage to deploy several groups in-tandem. This is useful if you're deploying the same container for different application needs. IE: One is a consumer and another is a publisher.
imageDescriptions:
- name: main-image
account: "namely-registry"
image_id: "${ trigger.properties['docker_image'] }"
registry: "registry.namely.land"
repository: "namely/example-all-day"
tag: "${ trigger.properties['docker_tag'] }"
organization: "namely"
stages:
- name: "Deploy"
deploy:
groups:
- manifestFile: manifests/deployment.yml
maxRemainingASGS: 2
scaleDown: true
stack: web
details: genpop
strategy: redblack
targetSize: 10
loadBalancers:
- namely
imageDescriptions:
- name: main-image
containerName: my-container
manifestFile
is used to generate the majority of the stage JSON for running the container. Things like environment, volumes, commands, etc are all stored within this Kubernetes Manifest file.maxRemainingASGS
determines how many ReplicaSets Spinnaker will keep around after a deploy occurs. If usingredblack
strategy you need at least 2. This is used for rolling back deploys.scaleDown
scales down the previous server group after a deploy. If you want traffic to be routed to both deployments set this tofalse
stack
is concatenated to the application name when deploying. Soapplication-stack
would be a result. CANNOT have dashes.details
is concatenated to the application name and stack when deploying. Soapplication-stack-detail
would be a result. This can have multiple dashes.strategy
is used to determine which strategy Spinnaker should use when deploying this new group.targetSize
is the amount of replicas to be deployed to the Kubernetes cluster. This is not taken from thedeployment
manifest fileloadBalancers
are the Spinnaker load balancers to be attached to this deployment. An array of strings. These will need to be defined inside of Spinnaker before a deploy to work.
imageDescriptions
are a top level key in your YAML that define a Docker image to be deployed. You can use Spinnaker expression syntax in these fields to add dynamic images in your deployment pipeline.
The name
key on the image description is then referenced in your groups
of a deploy
stage. You can see this here:
imageDescriptions:
- name: main-image
containerName: my-container
What k8s-pipeliner does is it looks into the manifest you've supplied, finds the container with the name "my-container", and includes the image description for the Spinnaker JSON that is rendered for it. This allows you to specify multiple containers in your pods and be able to swap out the images based on dynamic values for them.
This tool also supports the ability to include parameters in your pipeline definitions:
parameters:
- name: "random"
description: "random description"
required: true
default: "hello-world"
This configures your pipeline to have parameters in the UI / enable pipeline expressions.
The evaluate variables stage allows you to evaluate complex expressions and use them throughout your pipeline:
- name: "evaluate variables"
variables:
- key: "my-fun-key"
value: ${my-complex-expression}
You can reference the variable by the key name:
- name: "Some other stage"
someField: ${ my-fun-key }
Files under the configuratorFiles
section are expected to be in the k8s-configurator format. These will be run through k8s-configurator to generate the environment-specific manifest. By default, the environment used by k8s-configurator will be determined by the account used in this stage. However, you may set the optional env
property for configuratorFiles to override this.
stages:
- account: ops-k8s
name: "Deploy"
deployEmbeddedManifests:
moniker:
app: app
cluster: cluster
detail: detail
stack: stack
files:
- file: manifests/deployment.yml
- file: manifests/service.yml
- file: manifests/migrate-job.yml
configuratorFiles:
- file: test-configurator.yml
env: superOps
containerOverrides:
name: "container-name"
resources:
requests:
memory: "100"
cpu: "200"
All of these files will be composed into a single stage deployment into the given account. This means you can deploy services and deployments in tandem together.