基於ambassador實現K8S灰度釋出
為什麼需要灰度釋出
灰度釋出(又名金絲雀釋出)是指在黑與白之間,能夠平滑過渡的一種釋出方式。在其上可以進行A/B testing,即讓一部分使用者繼續用產品特性A,一部分使用者開始用產品特性B,如果使用者對B沒有什麼反對意見,那麼逐步擴大範圍,把所有使用者都遷移到B上面來。
總結下一些應用場景:
- 微服務依賴很多元件,需要在實際環境驗證
- 部署新功能有風險,然後可以通過導流一小部分使用者實際使用,來減小風險
- 讓特定的使用者訪問新版本,比如部署一個版本,只讓測試使用
- A/B Testing,部署兩個版本,進行版本對比,比如驗證兩個推薦服務的推薦效果
灰度釋出可以保證整體系統的穩定,在初始灰度的時候就可以發現、調整問題,以保證其影響度。
ambassador介紹
ambassador[æmˈbæsədər],是Kubernetes微服務 API gateway,基於Envoy Proxy。
Open Source Kubernetes-Native API Gateway built on the Envoy Proxy
官方地址:
https://www.getambassador.io/
部署ambassador
按官網提示部署ambassador
cat <<EOF | kubectl apply -f - --- apiVersion: v1 kind: Service metadata: labels: service: ambassador-admin name: ambassador-admin spec: type: NodePort ports: - name: ambassador-admin port: 8877 targetPort: 8877 selector: service: ambassador --- apiVersion: rbac.authorization.k8s.io/v1beta1 kind: ClusterRole metadata: name: ambassador rules: - apiGroups: [""] resources: [ "endpoints", "namespaces", "secrets", "services" ] verbs: ["get", "list", "watch"] - apiGroups: [ "getambassador.io" ] resources: [ "*" ] verbs: ["get", "list", "watch"] - apiGroups: [ "apiextensions.k8s.io" ] resources: [ "customresourcedefinitions" ] verbs: ["get", "list", "watch"] - apiGroups: [ "networking.internal.knative.dev" ] resources: [ "clusteringresses", "ingresses" ] verbs: ["get", "list", "watch"] - apiGroups: [ "networking.internal.knative.dev" ] resources: [ "ingresses/status", "clusteringresses/status" ] verbs: ["update"] - apiGroups: [ "extensions" ] resources: [ "ingresses" ] verbs: ["get", "list", "watch"] - apiGroups: [ "extensions" ] resources: [ "ingresses/status" ] verbs: ["update"] --- apiVersion: v1 kind: ServiceAccount metadata: name: ambassador --- apiVersion: rbac.authorization.k8s.io/v1beta1 kind: ClusterRoleBinding metadata: name: ambassador roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: ambassador subjects: - kind: ServiceAccount name: ambassador namespace: kube-system --- apiVersion: apiextensions.k8s.io/v1beta1 kind: CustomResourceDefinition metadata: name: authservices.getambassador.io spec: group: getambassador.io version: v1 versions: - name: v1 served: true storage: true scope: Namespaced names: plural: authservices singular: authservice kind: AuthService categories: - ambassador-crds --- apiVersion: apiextensions.k8s.io/v1beta1 kind: CustomResourceDefinition metadata: name: consulresolvers.getambassador.io spec: group: getambassador.io version: v1 versions: - name: v1 served: true storage: true scope: Namespaced names: plural: consulresolvers singular: consulresolver kind: ConsulResolver --- apiVersion: apiextensions.k8s.io/v1beta1 kind: CustomResourceDefinition metadata: name: kubernetesendpointresolvers.getambassador.io spec: group: getambassador.io version: v1 versions: - name: v1 served: true storage: true scope: Namespaced names: plural: kubernetesendpointresolvers singular: kubernetesendpointresolver kind: KubernetesEndpointResolver --- apiVersion: apiextensions.k8s.io/v1beta1 kind: CustomResourceDefinition metadata: name: kubernetesserviceresolvers.getambassador.io spec: group: getambassador.io version: v1 versions: - name: v1 served: true storage: true scope: Namespaced names: plural: kubernetesserviceresolvers singular: kubernetesserviceresolver kind: KubernetesServiceResolver --- apiVersion: apiextensions.k8s.io/v1beta1 kind: CustomResourceDefinition metadata: name: mappings.getambassador.io spec: group: getambassador.io version: v1 versions: - name: v1 served: true storage: true scope: Namespaced names: plural: mappings singular: mapping kind: Mapping categories: - ambassador-crds --- apiVersion: apiextensions.k8s.io/v1beta1 kind: CustomResourceDefinition metadata: name: modules.getambassador.io spec: group: getambassador.io version: v1 versions: - name: v1 served: true storage: true scope: Namespaced names: plural: modules singular: module kind: Module categories: - ambassador-crds --- apiVersion: apiextensions.k8s.io/v1beta1 kind: CustomResourceDefinition metadata: name: ratelimitservices.getambassador.io spec: group: getambassador.io version: v1 versions: - name: v1 served: true storage: true scope: Namespaced names: plural: ratelimitservices singular: ratelimitservice kind: RateLimitService categories: - ambassador-crds --- apiVersion: apiextensions.k8s.io/v1beta1 kind: CustomResourceDefinition metadata: name: tcpmappings.getambassador.io spec: group: getambassador.io version: v1 versions: - name: v1 served: true storage: true scope: Namespaced names: plural: tcpmappings singular: tcpmapping kind: TCPMapping categories: - ambassador-crds --- apiVersion: apiextensions.k8s.io/v1beta1 kind: CustomResourceDefinition metadata: name: tlscontexts.getambassador.io spec: group: getambassador.io version: v1 versions: - name: v1 served: true storage: true scope: Namespaced names: plural: tlscontexts singular: tlscontext kind: TLSContext categories: - ambassador-crds --- apiVersion: apiextensions.k8s.io/v1beta1 kind: CustomResourceDefinition metadata: name: tracingservices.getambassador.io spec: group: getambassador.io version: v1 versions: - name: v1 served: true storage: true scope: Namespaced names: plural: tracingservices singular: tracingservice kind: TracingService categories: - ambassador-crds --- apiVersion: apiextensions.k8s.io/v1beta1 kind: CustomResourceDefinition metadata: name: logservices.getambassador.io spec: group: getambassador.io version: v1 versions: - name: v1 served: true storage: true scope: Namespaced names: plural: logservices singular: logservice kind: LogService categories: - ambassador-crds --- apiVersion: apps/v1 kind: Deployment metadata: name: ambassador spec: replicas: 3 selector: matchLabels: service: ambassador template: metadata: annotations: sidecar.istio.io/inject: "false" "consul.hashicorp.com/connect-inject": "false" labels: service: ambassador spec: affinity: podAntiAffinity: preferredDuringSchedulingIgnoredDuringExecution: - weight: 100 podAffinityTerm: labelSelector: matchLabels: service: ambassador topologyKey: kubernetes.io/hostname serviceAccountName: ambassador containers: - name: ambassador image: quay.azk8s.cn/datawire/ambassador:0.86.1 resources: limits: cpu: 1 memory: 400Mi requests: cpu: 200m memory: 100Mi env: - name: AMBASSADOR_NAMESPACE valueFrom: fieldRef: fieldPath: metadata.namespace ports: - name: http containerPort: 8080 - name: https containerPort: 8443 - name: admin containerPort: 8877 livenessProbe: httpGet: path: /ambassador/v0/check_alive port: 8877 initialDelaySeconds: 30 periodSeconds: 3 readinessProbe: httpGet: path: /ambassador/v0/check_ready port: 8877 initialDelaySeconds: 30 periodSeconds: 3 volumeMounts: - name: ambassador-pod-info mountPath: /tmp/ambassador-pod-info volumes: - name: ambassador-pod-info downwardAPI: items: - path: "labels" fieldRef: fieldPath: metadata.labels restartPolicy: Always securityContext: runAsUser: 8888 --- apiVersion: v1 kind: Service metadata: name: ambassador spec: type: NodePort externalTrafficPolicy: Local ports: - port: 80 targetPort: 8080 selector: service: ambassador EOF
為了方便訪問閘道器,生成一個ingress:
apiVersion: extensions/v1beta1 kind: Ingress metadata: annotations: nginx.ingress.kubernetes.io/proxy-body-size: "0" nginx.ingress.kubernetes.io/proxy-read-timeout: "600" nginx.ingress.kubernetes.io/proxy-send-timeout: "600" kubernetes.io/tls-acme: 'true' name: ambassador spec: rules: - host: ambassador.iflyresearch.com http: paths: - backend: serviceName: ambassador servicePort: 80 path: /
ambassador 配置
ambassador 使用envoy來實現相關的負載,而envoy類似nginx。ambassador的原理大概是讀取service裡的配置,然後自動生成envoy的配置,當service變更時,動態更新envoy的配置並重啟,所以ambassador需要可以訪問服務API。
ambassador 的配置是放到metadata的annotations,以getambassador.io/config
開頭:
annotations:
getambassador.io/config: |
---
apiVersion: ambassador/v0
kind: Mapping
name: {{ .Values.service.name }}_mapping
prefix: /{{ .Values.service.prefix }}
service: {{ .Values.service.name }}.{{ .Release.Namespace }}
profix指定如何訪問服務,service指定指向那個服務。注意,需要加上namespace名稱,否則容易報找不到後端。
ambassador 灰度
ambassador實現灰度可以根據weight權重,或者指定匹配特定的header來實現。
根據weight進行灰度
用法:
部署一個新版本的service,prefix和之前老服務保持一致,但是配置weight,比如20,這樣20%的流量會流轉到新服務,這樣實現A/B Test
---
apiVersion: v1
kind: Service
metadata:
name: svc-gray
namespace: default
annotations:
getambassador.io/config: |
---
apiVersion: ambassador/v0
kind: Mapping
name: svc1_mapping
prefix: /svc/
service: service-gray
weight: 20
spec:
selector:
app: testservice
ports:
- port: 8080
name: service-gray
targetPort: http-api
根據請求頭 header 進行灰度 (regex_headers 正則匹配)
部署一個新版本,只需要特定的使用者才能訪問,可以通過該方案來實現。
例如:
---
apiVersion: v1
kind: Service
metadata:
name: svc-gray
namespace: default
annotations:
getambassador.io/config: |
---
apiVersion: ambassador/v0
kind: Mapping
name: svc1_mapping
prefix: /svc/
service: service-gray
headers:
gray: true
spec:
selector:
app: testservice
ports:
- port: 8080
name: service-gray
targetPort: http-api
訪問時,當指定gray: true時,訪問灰度版本,可以用postman來測試:
作者:Jadepeng
出處:jqpeng的技術記事本--http://www.cnblogs.com/xiaoqi
您的支援是對博主最大的鼓勵,感謝您的認真閱讀。
本文版權歸作者所有,歡迎轉載,但未經作者同意必須保留此段宣告,且在文章頁面明顯位置給出原文連線,否則保留追究法律責任的權利。