基本概念
灰度发布(又名金丝雀发布)是指在黑与白之间,能够平滑过渡的一种发布方式。在其上可以进行A/B testing,即让一部分用户继续用产品特性A,一部分用户开始用产品特性B,如果用户对B没有什么反对意见,那么逐步扩大范围,把所有用户都迁移到B上面来。灰度发布可以保证整体系统的稳定,在初始灰度的时候就可以发现、调整问题,以保证其影响度。
蓝绿部署是不停老版本,部署新版本然后进行测试,确认OK,将流量切到新版本,然后老版本同时也升级到新版本。
灰度是不同版本共存,蓝绿是新旧版本切换,2种模式的出发点不一样。
环境准备
1. 下载安装minikube
minikube是一个可以快速在单机上部署kubernetes并运行容器的工具,由于条件有限,我都是用它来学习Kubernetes知识。
Mac OSX
curl -Lo minikube https://kubernetes.oss-cn-hangzhou.aliyuncs.com/minikube/releases/v1.13.0/minikube-darwin-amd64 && chmod +x minikube && sudo mv minikube /usr/local/bin/
Linux
curl -Lo minikube https://kubernetes.oss-cn-hangzhou.aliyuncs.com/minikube/releases/v1.13.0/minikube-linux-amd64 && chmod +x minikube && sudo mv minikube /usr/local/bin/
2. 启动
本机环境需提前安装好docker-engine
minikube start --driver=none
启动成功之后,查看启动的pods
kubectl get pods -A
NAMESPACE NAME READY STATUS RESTARTS AGE
kube-system coredns-6c76c8bb89-nz22j 1/1 Running 2 24h
kube-system etcd-minikube 1/1 Running 2 24h
kube-system kube-apiserver-minikube 1/1 Running 2 24h
kube-system kube-controller-manager-minikube 1/1 Running 2 24h
kube-system kube-proxy-2qwq9 1/1 Running 2 24h
kube-system kube-scheduler-minikube 1/1 Running 2 24h
kube-system storage-provisioner 1/1 Running 3 24h
kubernetes-dashboard dashboard-metrics-scraper-c95fcf479-7qnhf 1/1 Running 0 24h
kubernetes-dashboard kubernetes-dashboard-5c448bc4bf-6gccm 1/1 Running 0 24h
3. 准备镜像
准备Dockerfile文件
Dockerfile
FROM python:alpine
RUN pip install flask -i https://pypi.douban.com/simple
COPY app.py /app.py
EXPOSE 5000
ENTRYPOINT ["python", "/app.py"]
app.py
from flask import Flask
from flask import request
from flask import jsonify
import socket
app = Flask(__name__)
@app.route('/')
def index():
return jsonify({'Version': 'v1',
'Hostname': socket.gethostname(),
'Address': socket.gethostbyname(socket.gethostname()),
'Message': 'Hello, World',
})
if __name__ == '__main__':
app.run('0.0.0.0', 5000)
构建第一个镜像:
docker build . -t flask-app:v1
构建第二个镜像:
先将app.py里面的v1改为v2
docker build . -t flask-app:v2
如此我们便有了两个版本的镜像。
蓝绿部署
准备deployment和service文件,内容如下:
deployment-v1.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: flask-app
spec:
replicas: 1
selector:
matchLabels:
app: flask-app
version: v1
template:
metadata:
labels:
app: flask-app
version: v1
spec:
containers:
- image: flask-app:v1
name: flask-app
ports:
- containerPort: 5000
deployment-v2.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: flask-app-v2
spec:
replicas: 1
selector:
matchLabels:
app: flask-app
version: v2
template:
metadata:
labels:
app: flask-app
version: v2
spec:
containers:
- image: flask-app:v2
name: flask-app
ports:
- containerPort: 5000
service.yaml
apiVersion: v1
kind: Service
metadata:
name: flask-app
spec:
ports:
- name: "http"
port: 5000
protocol: TCP
targetPort: 5000
selector:
app: flask-app
version: v1
type: NodePort
执行: kubectl apply -f deployment-v1.yaml
执行: kubectl apply -f service.yaml
执行: minikube ip
查看节点ip, kubectl get svc
查看nodeport端口
执行: curl $http://${node_ip}:${node_port}
输出:
{"Address":"172.17.0.4","Hostname":"flask-app-69cd76c45c-kpzn5","Message":"Hello, World","Version":"v1"}
执行: kubectl apply -f deployment-v2.yaml
将service.yaml里面的v1全部改为v2
执行: kubectl apply -f service.yaml
继续执行: curl $http://${node_ip}:${node_port}
输出:
{"Address":"172.17.0.8","Hostname":"flask-app-v2-6748f77677-vb4rx","Message":"Hello, World","Version":"v2"}
灰度发布
进行灰度发布之前先清理一下之前的deployment和service
kubectl delete -f deployment-v1.yaml
kubectl delete -f deployment-v2.yaml
kubectl delete -f service.yaml
准备几个文件 deployment-canary-v1.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: flask-app-v1
spec:
replicas: 1
selector:
matchLabels:
app: flask-app
template:
metadata:
labels:
app: flask-app
spec:
containers:
- image: flask-app:v1
name: flask-app
ports:
- containerPort: 5000
deployment-canary-v2.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: flask-app-v2
spec:
replicas: 1
selector:
matchLabels:
app: flask-app
template:
metadata:
labels:
app: flask-app
spec:
containers:
- image: flask-app:v2
name: flask-app
ports:
- containerPort: 5000
service-canary.yaml
apiVersion: v1
kind: Service
metadata:
name: flask-app
spec:
ports:
- name: "http"
port: 5000
protocol: TCP
targetPort: 5000
selector:
app: flask-app
type: NodePort
执行:
kubectl apply -f deployment-canary-v1.yaml
kubectl apply -f deployment-canary-v2.yaml
kubectl apply -f service-canary.yaml
执行: kubectl get svc
查看nodeport
执行:
while true; do curl http://${node_ip}:${node_port}; done
输出:
{"Address":"172.17.0.7","Hostname":"flask-app-69cd76c45c-knn4c","Message":"Hello, World","Version":"v1"}
{"Address":"172.17.0.4","Hostname":"flask-app-v2-7c957654cc-2mtbb","Message":"Hello, World","Version":"v2"}
{"Address":"172.17.0.4","Hostname":"flask-app-v2-7c957654cc-2mtbb","Message":"Hello, World","Version":"v2"}
{"Address":"172.17.0.4","Hostname":"flask-app-v2-7c957654cc-2mtbb","Message":"Hello, World","Version":"v2"}
{"Address":"172.17.0.7","Hostname":"flask-app-69cd76c45c-knn4c","Message":"Hello, World","Version":"v1"}
{"Address":"172.17.0.7","Hostname":"flask-app-69cd76c45c-knn4c","Message":"Hello, World","Version":"v1"}
{"Address":"172.17.0.4","Hostname":"flask-app-v2-7c957654cc-2mtbb","Message":"Hello, World","Version":"v2"}
利用service roundrobin负载均衡,可以看到v1与v2的服务共存。