From 560bb93e3d8cd3e9716b7a935ee05087985a658c Mon Sep 17 00:00:00 2001 From: stefanprodan Date: Fri, 8 Nov 2019 12:38:06 +0200 Subject: [PATCH] Add App Mesh gateway Helm chart --- charts/appmesh-gateway/.helmignore | 21 +++ charts/appmesh-gateway/Chart.yaml | 19 +++ charts/appmesh-gateway/README.md | 87 +++++++++++ charts/appmesh-gateway/templates/NOTES.txt | 1 + charts/appmesh-gateway/templates/_helpers.tpl | 56 +++++++ charts/appmesh-gateway/templates/account.yaml | 8 + charts/appmesh-gateway/templates/config.yaml | 41 +++++ .../appmesh-gateway/templates/deployment.yaml | 144 ++++++++++++++++++ charts/appmesh-gateway/templates/hpa.yaml | 28 ++++ charts/appmesh-gateway/templates/mesh.yaml | 12 ++ charts/appmesh-gateway/templates/psp.yaml | 57 +++++++ charts/appmesh-gateway/templates/rbac.yaml | 39 +++++ charts/appmesh-gateway/templates/service.yaml | 24 +++ charts/appmesh-gateway/values.yaml | 69 +++++++++ 14 files changed, 606 insertions(+) create mode 100644 charts/appmesh-gateway/.helmignore create mode 100644 charts/appmesh-gateway/Chart.yaml create mode 100644 charts/appmesh-gateway/README.md create mode 100644 charts/appmesh-gateway/templates/NOTES.txt create mode 100644 charts/appmesh-gateway/templates/_helpers.tpl create mode 100644 charts/appmesh-gateway/templates/account.yaml create mode 100644 charts/appmesh-gateway/templates/config.yaml create mode 100644 charts/appmesh-gateway/templates/deployment.yaml create mode 100644 charts/appmesh-gateway/templates/hpa.yaml create mode 100644 charts/appmesh-gateway/templates/mesh.yaml create mode 100644 charts/appmesh-gateway/templates/psp.yaml create mode 100644 charts/appmesh-gateway/templates/rbac.yaml create mode 100644 charts/appmesh-gateway/templates/service.yaml create mode 100644 charts/appmesh-gateway/values.yaml diff --git a/charts/appmesh-gateway/.helmignore b/charts/appmesh-gateway/.helmignore new file mode 100644 index 00000000..f0c13194 --- /dev/null +++ b/charts/appmesh-gateway/.helmignore @@ -0,0 +1,21 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*~ +# Various IDEs +.project +.idea/ +*.tmproj diff --git a/charts/appmesh-gateway/Chart.yaml b/charts/appmesh-gateway/Chart.yaml new file mode 100644 index 00000000..b31e96c2 --- /dev/null +++ b/charts/appmesh-gateway/Chart.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +name: appmesh-gateway +description: Flagger Gateway for AWS App Mesh is an edge L7 load balancer that exposes applications outside the mesh. +version: 1.0.0 +appVersion: 1.0.0 +home: https://flagger.app +icon: https://raw.githubusercontent.com/weaveworks/flagger/master/docs/logo/weaveworks.png +sources: + - https://github.com/stefanprodan/appmesh-gateway +maintainers: + - name: Stefan Prodan + url: https://github.com/stefanprodan + email: stefanprodan@users.noreply.github.com +keywords: + - flagger + - appmesh + - envoy + - gateway + - ingress diff --git a/charts/appmesh-gateway/README.md b/charts/appmesh-gateway/README.md new file mode 100644 index 00000000..12d1f47b --- /dev/null +++ b/charts/appmesh-gateway/README.md @@ -0,0 +1,87 @@ +# Flagger Gateway for App Mesh + +[Flagger Gateway for App Mesh](https://github.com/stefanprodan/appmesh-gateway) is an +Envoy-powered load balancer that exposes applications outside the mesh. +The gateway facilitates canary deployments and A/B testing for user-facing web applications running on AWS App Mesh. + +## Prerequisites + +* Kubernetes >= 1.13 +* [App Mesh controller](https://github.com/aws/eks-charts/tree/master/stable/appmesh-controller) >= 0.2.0 +* [App Mesh inject](https://github.com/aws/eks-charts/tree/master/stable/appmesh-inject) >= 0.2.0 + +## Installing the Chart + +Add Flagger Helm repository: + +```console +$ helm repo add flagger https://flagger.app +``` + +Create a namespace with App Mesh sidecar injection enabled: + +```sh +kubectl create ns flagger-system +kubectl label namespace test appmesh.k8s.aws/sidecarInjectorWebhook=enabled +``` + +Install App Mesh Gateway for an existing mesh: + +```sh +helm upgrade -i appmesh-gateway flagger/appmesh-gateway \ +--namespace flagger-system \ +--set mesh.name=global +``` + +Optionally you can create a mesh at install time: + +```sh +helm upgrade -i appmesh-gateway flagger/appmesh-gateway \ +--namespace flagger-system \ +--set mesh.name=global \ +--set mesh.create=true +``` + +The [configuration](#configuration) section lists the parameters that can be configured during installation. + +## Uninstalling the Chart + +To uninstall/delete the `appmesh-gateway` deployment: + +```console +helm delete --purge appmesh-gateway +``` + +The command removes all the Kubernetes components associated with the chart and deletes the release. + +## Configuration + +The following tables lists the configurable parameters of the chart and their default values. + +Parameter | Description | Default +--- | --- | --- +`service.type` | When set to LoadBalancer it creates an AWS NLB | `LoadBalancer` +`proxy.access_log_path` | to enable the access logs, set it to `/dev/stdout` | `/dev/null` +`proxy.image.repository` | image repository | `envoyproxy/envoy` +`proxy.image.tag` | image tag | `` +`proxy.image.pullPolicy` | image pull policy | `IfNotPresent` +`controller.image.repository` | image repository | `weaveworks/flagger-appmesh-gateway` +`controller.image.tag` | image tag | `` +`controller.image.pullPolicy` | image pull policy | `IfNotPresent` +`resources.requests/cpu` | pod CPU request | `100m` +`resources.requests/memory` | pod memory request | `128Mi` +`resources.limits/memory` | pod memory limit | `2Gi` +`nodeSelector` | node labels for pod assignment | `{}` +`tolerations` | list of node taints to tolerate | `[]` +`rbac.create` | if `true`, create and use RBAC resources | `true` +`rbac.pspEnabled` | If `true`, create and use a restricted pod security policy | `false` +`serviceAccount.create` | If `true`, create a new service account | `true` +`serviceAccount.name` | Service account to be used | None +`mesh.create` | If `true`, create mesh custom resource | `false` +`mesh.name` | The name of the mesh to use | `global` +`mesh.discovery` | The service discovery type to use, can be dns or cloudmap | `dns` +`hpa.enabled` | `true` if HPA resource should be created, metrics-server is required | `true` +`hpa.maxReplicas` | number of max replicas | `3` +`hpa.cpu` | average total CPU usage per pod (1-100) | `99` +`hpa.memory` | average memory usage per pod (100Mi-1Gi) | None +`discovery.optIn` | `true` if only services with the 'expose' annotation are discoverable | `true` diff --git a/charts/appmesh-gateway/templates/NOTES.txt b/charts/appmesh-gateway/templates/NOTES.txt new file mode 100644 index 00000000..8ebd989e --- /dev/null +++ b/charts/appmesh-gateway/templates/NOTES.txt @@ -0,0 +1 @@ +App Mesh Gateway installed! \ No newline at end of file diff --git a/charts/appmesh-gateway/templates/_helpers.tpl b/charts/appmesh-gateway/templates/_helpers.tpl new file mode 100644 index 00000000..05f80903 --- /dev/null +++ b/charts/appmesh-gateway/templates/_helpers.tpl @@ -0,0 +1,56 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "appmesh-gateway.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +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 "appmesh-gateway.fullname" -}} +{{- if .Values.fullnameOverride -}} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- if contains $name .Release.Name -}} +{{- .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "appmesh-gateway.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Common labels +*/}} +{{- define "appmesh-gateway.labels" -}} +app.kubernetes.io/name: {{ include "appmesh-gateway.name" . }} +helm.sh/chart: {{ include "appmesh-gateway.chart" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end -}} + +{{/* +Create the name of the service account to use +*/}} +{{- define "appmesh-gateway.serviceAccountName" -}} +{{- if .Values.serviceAccount.create -}} + {{ default (include "appmesh-gateway.fullname" .) .Values.serviceAccount.name }} +{{- else -}} + {{ default "default" .Values.serviceAccount.name }} +{{- end -}} +{{- end -}} diff --git a/charts/appmesh-gateway/templates/account.yaml b/charts/appmesh-gateway/templates/account.yaml new file mode 100644 index 00000000..3c0e3606 --- /dev/null +++ b/charts/appmesh-gateway/templates/account.yaml @@ -0,0 +1,8 @@ +{{- if .Values.serviceAccount.create }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ template "appmesh-gateway.serviceAccountName" . }} + labels: +{{ include "appmesh-gateway.labels" . | indent 4 }} +{{- end }} diff --git a/charts/appmesh-gateway/templates/config.yaml b/charts/appmesh-gateway/templates/config.yaml new file mode 100644 index 00000000..f41bb20b --- /dev/null +++ b/charts/appmesh-gateway/templates/config.yaml @@ -0,0 +1,41 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "appmesh-gateway.fullname" . }} + labels: +{{ include "appmesh-gateway.labels" . | indent 4 }} +data: + envoy.yaml: |- + admin: + access_log_path: {{ .Values.proxy.access_log_path }} + address: + socket_address: + address: 0.0.0.0 + port_value: 8081 + + dynamic_resources: + ads_config: + api_type: GRPC + grpc_services: + - envoy_grpc: + cluster_name: xds + cds_config: + ads: {} + lds_config: + ads: {} + + static_resources: + clusters: + - name: xds + connect_timeout: 0.50s + type: static + http2_protocol_options: {} + load_assignment: + cluster_name: xds + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: 127.0.0.1 + port_value: 18000 diff --git a/charts/appmesh-gateway/templates/deployment.yaml b/charts/appmesh-gateway/templates/deployment.yaml new file mode 100644 index 00000000..fca9b9d1 --- /dev/null +++ b/charts/appmesh-gateway/templates/deployment.yaml @@ -0,0 +1,144 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ template "appmesh-gateway.fullname" . }} + labels: +{{ include "appmesh-gateway.labels" . | indent 4 }} +spec: + replicas: {{ .Values.replicaCount }} + strategy: + type: Recreate + selector: + matchLabels: + app.kubernetes.io/name: {{ include "appmesh-gateway.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + template: + metadata: + labels: + app.kubernetes.io/name: {{ include "appmesh-gateway.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/part-of: appmesh + annotations: + prometheus.io/scrape: "true" + prometheus.io/path: "/stats/prometheus" + prometheus.io/port: "8081" + # exclude inbound traffic on port 8080 + appmesh.k8s.aws/ports: "444" + # exclude egress traffic to xDS server and Kubernetes API + appmesh.k8s.aws/egressIgnoredPorts: "18000,22,443" + checksum/config: {{ include (print $.Template.BasePath "/config.yaml") . | sha256sum | quote }} + spec: + serviceAccountName: {{ include "appmesh-gateway.serviceAccountName" . }} + terminationGracePeriodSeconds: 45 + affinity: + podAntiAffinity: + preferredDuringSchedulingIgnoredDuringExecution: + - podAffinityTerm: + labelSelector: + matchLabels: + app.kubernetes.io/name: {{ include "appmesh-gateway.name" . }} + topologyKey: kubernetes.io/hostname + weight: 100 + volumes: + - name: appmesh-gateway-config + configMap: + name: {{ template "appmesh-gateway.fullname" . }} + containers: + - name: controller + image: "{{ .Values.controller.image.repository }}:{{ .Values.controller.image.tag }}" + imagePullPolicy: {{ .Values.controller.image.pullPolicy }} + securityContext: + readOnlyRootFilesystem: true + runAsUser: 10001 + capabilities: + drop: + - ALL + add: + - NET_BIND_SERVICE + command: + - ./appmesh-gateway + - --opt-in={{ .Values.discovery.optIn }} + - --gateway-mesh={{ .Values.mesh.name }} + - --gateway-name=$(POD_SERVICE_ACCOUNT) + - --gateway-namespace=$(POD_NAMESPACE) + env: + - name: POD_SERVICE_ACCOUNT + valueFrom: + fieldRef: + fieldPath: spec.serviceAccountName + - name: POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + ports: + - name: grpc + containerPort: 18000 + protocol: TCP + livenessProbe: + initialDelaySeconds: 5 + tcpSocket: + port: grpc + readinessProbe: + initialDelaySeconds: 5 + tcpSocket: + port: grpc + resources: + limits: + memory: 1Gi + requests: + cpu: 10m + memory: 32Mi + - name: proxy + image: "{{ .Values.proxy.image.repository }}:{{ .Values.proxy.image.tag }}" + imagePullPolicy: {{ .Values.proxy.image.pullPolicy }} + securityContext: + capabilities: + drop: + - ALL + add: + - NET_BIND_SERVICE + args: + - -c + - /config/envoy.yaml + - --service-cluster $(POD_NAMESPACE) + - --service-node $(POD_NAME) + - --log-level info + - --base-id 1234 + ports: + - name: admin + containerPort: 8081 + protocol: TCP + - name: http + containerPort: 8080 + protocol: TCP + livenessProbe: + initialDelaySeconds: 5 + tcpSocket: + port: admin + readinessProbe: + initialDelaySeconds: 5 + httpGet: + path: /ready + port: admin + env: + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + volumeMounts: + - name: appmesh-gateway-config + mountPath: /config + resources: +{{ toYaml .Values.resources | indent 12 }} + {{- with .Values.nodeSelector }} + nodeSelector: +{{ toYaml . | indent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: +{{ toYaml . | indent 8 }} + {{- end }} diff --git a/charts/appmesh-gateway/templates/hpa.yaml b/charts/appmesh-gateway/templates/hpa.yaml new file mode 100644 index 00000000..c24a452e --- /dev/null +++ b/charts/appmesh-gateway/templates/hpa.yaml @@ -0,0 +1,28 @@ +{{- if .Values.hpa.enabled }} +apiVersion: autoscaling/v2beta1 +kind: HorizontalPodAutoscaler +metadata: + name: {{ template "appmesh-gateway.fullname" . }} + labels: +{{ include "appmesh-gateway.labels" . | indent 4 }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ template "appmesh-gateway.fullname" . }} + minReplicas: {{ .Values.replicaCount }} + maxReplicas: {{ .Values.hpa.maxReplicas }} + metrics: + {{- if .Values.hpa.cpu }} + - type: Resource + resource: + name: cpu + targetAverageUtilization: {{ .Values.hpa.cpu }} + {{- end }} + {{- if .Values.hpa.memory }} + - type: Resource + resource: + name: memory + targetAverageValue: {{ .Values.hpa.memory }} + {{- end }} +{{- end }} diff --git a/charts/appmesh-gateway/templates/mesh.yaml b/charts/appmesh-gateway/templates/mesh.yaml new file mode 100644 index 00000000..8542dd39 --- /dev/null +++ b/charts/appmesh-gateway/templates/mesh.yaml @@ -0,0 +1,12 @@ +{{- if .Values.mesh.create }} +apiVersion: appmesh.k8s.aws/v1beta1 +kind: Mesh +metadata: + name: {{ .Values.mesh.name }} + annotations: + helm.sh/resource-policy: keep + labels: +{{ include "appmesh-gateway.labels" . | indent 4 }} +spec: + serviceDiscoveryType: {{ .Values.mesh.discovery }} +{{- end }} diff --git a/charts/appmesh-gateway/templates/psp.yaml b/charts/appmesh-gateway/templates/psp.yaml new file mode 100644 index 00000000..20444979 --- /dev/null +++ b/charts/appmesh-gateway/templates/psp.yaml @@ -0,0 +1,57 @@ +{{- if .Values.rbac.pspEnabled }} +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +metadata: + name: {{ template "appmesh-gateway.fullname" . }} + labels: +{{ include "appmesh-gateway.labels" . | indent 4 }} + annotations: + seccomp.security.alpha.kubernetes.io/allowedProfileNames: '*' +spec: + privileged: false + hostIPC: false + hostNetwork: false + hostPID: false + readOnlyRootFilesystem: false + allowPrivilegeEscalation: false + allowedCapabilities: + - '*' + fsGroup: + rule: RunAsAny + runAsUser: + rule: RunAsAny + seLinux: + rule: RunAsAny + supplementalGroups: + rule: RunAsAny + volumes: + - '*' +--- +kind: ClusterRole +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ template "appmesh-gateway.fullname" . }}-psp + labels: +{{ include "appmesh-gateway.labels" . | indent 4 }} +rules: + - apiGroups: ['policy'] + resources: ['podsecuritypolicies'] + verbs: ['use'] + resourceNames: + - {{ template "appmesh-gateway.fullname" . }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ template "appmesh-gateway.fullname" . }}-psp + labels: +{{ include "appmesh-gateway.labels" . | indent 4 }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ template "appmesh-gateway.fullname" . }}-psp +subjects: + - kind: ServiceAccount + name: {{ template "appmesh-gateway.serviceAccountName" . }} + namespace: {{ .Release.Namespace }} +{{- end }} diff --git a/charts/appmesh-gateway/templates/rbac.yaml b/charts/appmesh-gateway/templates/rbac.yaml new file mode 100644 index 00000000..51e7fa78 --- /dev/null +++ b/charts/appmesh-gateway/templates/rbac.yaml @@ -0,0 +1,39 @@ +{{- if .Values.rbac.create }} +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: ClusterRole +metadata: + name: {{ template "appmesh-gateway.fullname" . }} + labels: +{{ include "appmesh-gateway.labels" . | indent 4 }} +rules: + - apiGroups: + - "" + resources: + - services + verbs: ["*"] + - apiGroups: + - appmesh.k8s.aws + resources: + - meshes + - meshes/status + - virtualnodes + - virtualnodes/status + - virtualservices + - virtualservices/status + verbs: ["*"] +--- +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: ClusterRoleBinding +metadata: + name: {{ template "appmesh-gateway.fullname" . }} + labels: +{{ include "appmesh-gateway.labels" . | indent 4 }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ template "appmesh-gateway.fullname" . }} +subjects: +- name: {{ template "appmesh-gateway.serviceAccountName" . }} + namespace: {{ .Release.Namespace }} + kind: ServiceAccount +{{- end }} diff --git a/charts/appmesh-gateway/templates/service.yaml b/charts/appmesh-gateway/templates/service.yaml new file mode 100644 index 00000000..e11c2dd1 --- /dev/null +++ b/charts/appmesh-gateway/templates/service.yaml @@ -0,0 +1,24 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ template "appmesh-gateway.fullname" . }} + annotations: + gateway.appmesh.k8s.aws/expose: "false" + {{- if eq .Values.service.type "LoadBalancer" }} + service.beta.kubernetes.io/aws-load-balancer-type: "nlb" + {{- end }} + labels: +{{ include "appmesh-gateway.labels" . | indent 4 }} +spec: + type: {{ .Values.service.type }} + {{- if eq .Values.service.type "LoadBalancer" }} + externalTrafficPolicy: Local + {{- end }} + ports: + - port: {{ .Values.service.port }} + targetPort: http + protocol: TCP + name: http + selector: + app.kubernetes.io/name: {{ include "appmesh-gateway.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} diff --git a/charts/appmesh-gateway/values.yaml b/charts/appmesh-gateway/values.yaml new file mode 100644 index 00000000..b5b89be2 --- /dev/null +++ b/charts/appmesh-gateway/values.yaml @@ -0,0 +1,69 @@ +# Default values for appmesh-gateway. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. + +replicaCount: 1 +discovery: + # discovery.optIn `true` if only services with the 'expose' annotation are discoverable + optIn: true + +proxy: + access_log_path: /dev/null + image: + repository: docker.io/envoyproxy/envoy + tag: v1.12.0 + pullPolicy: IfNotPresent + +controller: + image: + repository: weaveworks/flagger-appmesh-gateway + tag: v1.0.0 + pullPolicy: IfNotPresent + +nameOverride: "" +fullnameOverride: "" + +service: + # service.type: When set to LoadBalancer it creates an AWS NLB + type: LoadBalancer + port: 80 + +hpa: + # hpa.enabled `true` if HPA resource should be created, metrics-server is required + enabled: true + maxReplicas: 3 + # hpa.cpu average total CPU usage per pod (1-100) + cpu: 99 + # hpa.memory average memory usage per pod (100Mi-1Gi) + memory: + +resources: + limits: + memory: 2Gi + requests: + cpu: 100m + memory: 128Mi + +nodeSelector: {} + +tolerations: [] + +serviceAccount: + # serviceAccount.create: Whether to create a service account or not + create: true + # serviceAccount.name: The name of the service account to create or use + name: "" + +rbac: + # rbac.create: `true` if rbac resources should be created + create: true + # rbac.pspEnabled: `true` if PodSecurityPolicy resources should be created + pspEnabled: false + +mesh: + # mesh.create: `true` if mesh resource should be created + create: false + # mesh.name: The name of the mesh to use + name: "global" + # mesh.discovery: The service discovery type to use, can be dns or cloudmap + discovery: dns