Skip to content

Commit

Permalink
charts/service-deployment: Add chart that can handle generic deployme…
Browse files Browse the repository at this point in the history
…nts (closes #37)
jbeemster committed Jul 28, 2022
1 parent eab6925 commit 8f5d4d9
Showing 12 changed files with 420 additions and 0 deletions.
9 changes: 9 additions & 0 deletions charts/service-deployment/Chart.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
dependencies:
- name: dockerconfigjson
repository: https://snowplow-devops.github.io/helm-charts
version: 0.1.0
- name: cloudserviceaccount
repository: https://snowplow-devops.github.io/helm-charts
version: 0.1.0
digest: sha256:92127ad4fb4b1721b3a51927e4e199bb40ae8d26f0c85369b672845bf061ddaf
generated: "2022-07-25T09:44:35.530345+02:00"
22 changes: 22 additions & 0 deletions charts/service-deployment/Chart.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
apiVersion: v2
name: service-deployment
description: A Helm Chart to setup a generic deployment with optional service/hpa bindings
version: 0.1.0
icon: https://raw.githubusercontent.com/snowplow-devops/helm-charts/master/docs/logo/snowplow.png
home: https://github.com/snowplow-devops/helm-charts
sources:
- https://github.com/snowplow-devops/helm-charts
maintainers:
- name: jbeemster
url: https://github.com/jbeemster
email: jbeemster@users.noreply.github.com
keywords:
- service
- deployment
dependencies:
- name: dockerconfigjson
version: 0.1.0
repository: "https://snowplow-devops.github.io/helm-charts"
- name: cloudserviceaccount
version: 0.1.0
repository: "https://snowplow-devops.github.io/helm-charts"
79 changes: 79 additions & 0 deletions charts/service-deployment/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# service-deployment

A helm chart to deploy a generic deployment with optional service bindings.

## TL;DR

```bash
helm repo add snowplow-devops https://snowplow-devops.github.io/helm-charts
helm install service-deployment snowplow-devops/service-deployment
```

## Introduction

This chart attempts to take care of all the most common requirements of launching a long-running service that requires auto-scaling:

- Downloading from private Docker repositories
- Auto-scaling pods based on CPU usage
- Mounting config volumes
- Configuring secrets
- Binding service-accounts with cloud specific IAM policies

_Note_: This should be a long running process - if you are looking for cron-based execution see our `cron-job` chart.

This chart won't solve for every possible option of deploying a service - it is meant to serve as an opinionated starting point to get something working decently well. For more flexibility open a PR or fork this chart to suit your specific needs.

## Installing the Chart

Install or upgrading the chart with default configuration:

```bash
helm upgrade --install service-deployment snowplow-devops/service-deployment
```

## Uninstalling the Chart

To uninstall/delete the `service-deployment` release:

```bash
helm delete service-deployment
```

## Configuration

| Key | Type | Default | Description |
|-----|------|---------|-------------|
| global.cloud | string | `""` | Cloud specific bindings (options: aws, gcp) |
| image.repository | string | `"nginx"` | |
| image.tag | string | `"latest"` | |
| image.isRepositoryPublic | bool | `true` | Whether the repository is public |
| config.command | list | `[]` | |
| config.args | list | `[]` | |
| config.env | string | `nil` | Map of environment variables to use within the job |
| config.secrets | object | `{}` | Map of secrets that will be exposed as environment variables within the job |
| configMaps | list | `[]` | List of config maps to mount to the deployment |
| resources | object | `{}` | Map of resource constraints for the service |
| readinessProbe.httpGet.path | string | `""` | Path for health checks to be performed (note: set to "" to disable) |
| readinessProbe.initialDelaySeconds | int | `5` | |
| readinessProbe.periodSeconds | int | `5` | |
| readinessProbe.timeoutSeconds | int | `5` | |
| readinessProbe.failureThreshold | int | `3` | |
| readinessProbe.successThreshold | int | `2` | |
| terminationGracePeriodSeconds | int | `60` | Grace period for termination of the service |
| hpa.deploy | bool | `true` | Whether to deploy HPA rules |
| hpa.minReplicas | int | `1` | Minimum number of pods to deploy |
| hpa.maxReplicas | int | `20` | Maximum number of pods to deploy |
| hpa.averageCPUUtilization | int | `75` | Average CPU utilization before auto-scaling starts |
| service.deploy | bool | `true` | Whether to setup service bindings (note: only NodePort is supported) |
| service.port | int | `80` | Port to bind and expose the service on |
| service.aws.targetGroupARN | string | `""` | EC2 TargetGroup ARN to bind the service onto |
| service.gcp.networkEndpointGroupName | string | `""` | Name of the Network Endpoint Group to bind onto |
| dockerconfigjson.name | string | `"snowplow-sd-dockerhub"` | Name of the secret to use for the private repository |
| dockerconfigjson.username | string | `""` | Username for the private repository |
| dockerconfigjson.password | string | `""` | Password for the private repository |
| dockerconfigjson.server | string | `"https://index.docker.io/v1/"` | Repository server URL |
| dockerconfigjson.email | string | `""` | Email address for user of the private repository |
| cloudserviceaccount.deploy | bool | `false` | Whether to create a service-account |
| cloudserviceaccount.name | string | `"snowplow-sd-service-account"` | Name of the service-account to create |
| cloudserviceaccount.aws.roleARN | string | `""` | IAM Role ARN to bind to the k8s service account |
| cloudserviceaccount.gcp.serviceAccount | string | `""` | Service Account email to bind to the k8s service account |
16 changes: 16 additions & 0 deletions charts/service-deployment/templates/NOTES.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
Deployment {{ .Release.Name }} has been installed/updated - to get basic information about the pods:

kubectl describe pod {{ .Release.Name }}

{{- if .Values.service.deploy }}

The service can be accessed via port {{ .Values.service.port }} on the following DNS names from within your cluster:

{{ include "app.fullname" . }}.{{ .Release.Namespace }}.svc.cluster.local

To connect to your server from outside the cluster execute the following commands:

kubectl port-forward --namespace {{ .Release.Namespace }} svc/{{ include "app.fullname" . }} 8080:{{ .Values.service.port }}

You can then navigate to your service in your browser at localhost:8080 or issue request with tools like cURL.
{{- end }}
23 changes: 23 additions & 0 deletions charts/service-deployment/templates/_helpers.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{{/* vim: set filetype=mustache: */}}
{{/*
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 -}}
{{- .Release.Name | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{- end -}}
{{- define "app.secret.fullname" -}}
{{ include "app.fullname" . }}-secret
{{- end -}}

{{/*
Define the default NEG name for GCP deployments.
*/}}
{{- define "service.gcp.networkEndpointGroupName" -}}
{{- default .Release.Name .Values.service.gcp.networkEndpointGroupName -}}
{{- end -}}
11 changes: 11 additions & 0 deletions charts/service-deployment/templates/configmaps.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{{- if .Values.configMaps }}
{{- range $v := .Values.configMaps }}
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ $v.name }}
binaryData:
{{ $v.key }}: "{{ $v.contentsB64 }}"
---
{{- end }}
{{- end }}
101 changes: 101 additions & 0 deletions charts/service-deployment/templates/deployment.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "app.fullname" . }}
spec:
selector:
matchLabels:
app: {{ include "app.fullname" . }}
template:
metadata:
labels:
app: {{ include "app.fullname" . }}
annotations:
{{- if .Values.configMaps }}
{{- range $v := .Values.configMaps }}
checksum/{{ $v.name }}-{{ $v.key }}: "{{ $v.contentsB64 | sha256sum }}"
{{- end }}
{{- end }}
spec:
{{- if .Values.cloudserviceaccount.deploy }}
serviceAccountName: {{ .Values.cloudserviceaccount.name }}
{{- end }}
automountServiceAccountToken: true
terminationGracePeriodSeconds: {{ .Values.terminationGracePeriodSeconds }}

{{- if not .Values.image.isRepositoryPublic }}
imagePullSecrets:
- name: {{ .Values.dockerconfigjson.name }}
{{- end }}

{{- if .Values.configMaps }}
volumes:
{{- range $v := .Values.configMaps }}
- configMap:
name: {{ $v.name }}
optional: false
name: {{ $v.name }}
{{- end }}
{{- end }}

containers:
- name: "{{ include "app.fullname" . }}"
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: Always

{{- if .Values.config.command }}
command:
{{- range $v := .Values.config.command }}
- "{{ $v }}"
{{- end }}
{{- end }}

{{- if .Values.config.args }}
args:
{{- range $v := .Values.config.args }}
- "{{ $v }}"
{{- end }}
{{- end }}

{{- if .Values.config.env }}
env:
{{- range $k, $v := .Values.config.env }}
- name: "{{ $k }}"
value: "{{ $v }}"
{{- end }}
{{- end }}

{{- if .Values.config.secrets }}
envFrom:
- secretRef:
name: {{ include "app.secret.fullname" . }}
{{- end }}

resources:
{{- toYaml .Values.resources | nindent 10 }}

{{- if ne .Values.readinessProbe.httpGet.path "" }}
readinessProbe:
httpGet:
path: {{ .Values.readinessProbe.httpGet.path }}
port: {{ .Values.service.port }}
scheme: HTTP
initialDelaySeconds: {{ .Values.readinessProbe.initialDelaySeconds }}
periodSeconds: {{ .Values.readinessProbe.periodSeconds }}
timeoutSeconds: {{ .Values.readinessProbe.timeoutSeconds }}
failureThreshold: {{ .Values.readinessProbe.failureThreshold }}
successThreshold: {{ .Values.readinessProbe.successThreshold }}
{{- end }}

{{- if .Values.configMaps }}
volumeMounts:
{{- range $v := .Values.configMaps }}
- mountPath: "{{ $v.mountPath }}"
{{- if $v.mountPropagation }}
mountPropagation: {{ $v.mountPropagation }}
{{- else }}
mountPropagation: None
{{- end }}
name: {{ $v.name }}
{{- end }}
{{- end }}
14 changes: 14 additions & 0 deletions charts/service-deployment/templates/hpa.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{{- if .Values.hpa.deploy }}
apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
name: {{ include "app.fullname" . }}
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: {{ include "app.fullname" . }}
minReplicas: {{ .Values.hpa.minReplicas }}
maxReplicas: {{ .Values.hpa.maxReplicas }}
targetCPUUtilizationPercentage: {{ .Values.hpa.averageCPUUtilization }}
{{- end }}
12 changes: 12 additions & 0 deletions charts/service-deployment/templates/secrets.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{{- if .Values.config.secrets }}
apiVersion: v1
kind: Secret
metadata:
namespace: {{ .Release.Namespace }}
name: {{ include "app.secret.fullname" . }}
type: Opaque
data:
{{- range $k, $v := .Values.config.secrets }}
{{ $k }}: "{{ $v | b64enc }}"
{{- end }}
{{- end }}
20 changes: 20 additions & 0 deletions charts/service-deployment/templates/service.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{{- if .Values.service.deploy }}
apiVersion: v1
kind: Service
metadata:
name: {{ include "app.fullname" . }}
{{- if eq .Values.global.cloud "gcp" }}
annotations:
cloud.google.com/app-protocols: '{"http-port": "HTTP"}'
cloud.google.com/neg: '{"exposed_ports": {"{{ .Values.service.port }}":{"name": "{{ include "service.gcp.networkEndpointGroupName" . }}"}}}'
{{- end }}
spec:
type: NodePort
selector:
app: {{ include "app.fullname" . }}
ports:
- name: http-port
port: {{ .Values.service.port }}
protocol: TCP
targetPort: {{ .Values.service.port }}
{{- end }}
13 changes: 13 additions & 0 deletions charts/service-deployment/templates/targetgroupbinding.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{{- if .Values.service.deploy }}
{{- if eq .Values.global.cloud "aws" }}
apiVersion: elbv2.k8s.aws/v1beta1
kind: TargetGroupBinding
metadata:
name: {{ include "app.fullname" . }}
spec:
serviceRef:
name: {{ include "app.fullname" . }}
port: {{ .Values.service.port }}
targetGroupARN: {{ .Values.service.aws.targetGroupARN }}
{{- end }}
{{- end }}
100 changes: 100 additions & 0 deletions charts/service-deployment/values.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
global:
# -- Cloud specific bindings (options: aws, gcp)
cloud: ""

image:
repository: "nginx"
tag: "latest"
# -- Whether the repository is public
isRepositoryPublic: true

config:
command: []
# - "/bin/sh"
args: []
# - "-c"
# - "echo 'Environment $(hello_env)! Secret $(username).'"

# -- Map of environment variables to use within the job
env:
# hello_env: "world"

# -- Map of secrets that will be exposed as environment variables within the job
secrets: {}
# username: "password"

# -- List of config maps to mount to the deployment
configMaps: []
# - name: "volume-1"
# key: "file.cfg"
# contentsB64: "" # The file contents which have already been base-64 encoded
# mountPath: "/etc/config" # Must be unique
# mountPropagation: None # If unset will default to 'None'

# -- Map of resource constraints for the service
resources: {}
# limits:
# cpu: 746m
# memory: 900Mi
# requests:
# cpu: 400m
# memory: 512Mi

readinessProbe:
httpGet:
# -- Path for health checks to be performed (note: set to "" to disable)
path: ""
initialDelaySeconds: 5
periodSeconds: 5
timeoutSeconds: 5
failureThreshold: 3
successThreshold: 2

# -- Grace period for termination of the service
terminationGracePeriodSeconds: 60

hpa:
# -- Whether to deploy HPA rules
deploy: true
# -- Minimum number of pods to deploy
minReplicas: 1
# -- Maximum number of pods to deploy
maxReplicas: 20
# -- Average CPU utilization before auto-scaling starts
averageCPUUtilization: 75

service:
# -- Whether to setup service bindings (note: only NodePort is supported)
deploy: true
# -- Port to bind and expose the service on
port: 80
aws:
# -- EC2 TargetGroup ARN to bind the service onto
targetGroupARN: ""
gcp:
# -- Name of the Network Endpoint Group to bind onto
networkEndpointGroupName: ""

dockerconfigjson:
# -- Name of the secret to use for the private repository
name: "snowplow-sd-dockerhub"
# -- Username for the private repository
username: ""
# -- Password for the private repository
password: ""
# -- Repository server URL
server: "https://index.docker.io/v1/"
# -- Email address for user of the private repository
email: ""

cloudserviceaccount:
# -- Whether to create a service-account
deploy: false
# -- Name of the service-account to create
name: "snowplow-sd-service-account"
aws:
# -- IAM Role ARN to bind to the k8s service account
roleARN: ""
gcp:
# -- Service Account email to bind to the k8s service account
serviceAccount: ""

0 comments on commit 8f5d4d9

Please sign in to comment.