為什么需要灰度發布
灰度發布(又名金絲雀發布)是指在黑與白之間,能夠平滑過渡的一種發布方式。在其上可以進行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
官方地址:
部署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
您的支持是對博主最大的鼓勵,感謝您的認真閱讀。
本文版權歸作者所有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權利。