Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generic chart to deploy a service #34

Merged
merged 2 commits into from
Jul 28, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
Version 0.1.13 (2022-07-28)
---------------------------
charts/service-deployment: Add chart that can handle generic deployments (#37)

Version 0.1.12 (2022-07-25)
---------------------------
charts/cron-job: Simplify chart by removing input nesting layer (#35)
Expand Down
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: [email protected]
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 }}
Loading