(来自DockOne社区的分享)
1,我们为什么要用k8s
1.1 拥抱微服务
微服务架构将巨大单体式应用分解为多个服务,每个服务通过 RPC 或者API 进行通信,具备了各个自服务容易开发、维护的优点,另外子服务还具备独立部署、快速扩展等优点。Kubernetes 对微服务本身有很好的支持,应用本身通过Deployment 进行部署,各个服务运行在 Pod 中,Pod 之间服务通过 Service 具备的服务发现实现相互间的通信。同时微服务架构也恰好是云原生应用的一种体现。
1.2 容器编排
Kubernetes 帮助使用者通过简单易用的 API 高效地管理成千上万个运行在
容器中的微服务。Kubernetes 在 1.6 版本中已经支持 5000 个节点,这也说明 Kubernetes具备大规模集群的编排管理能力。同时,Kubernetes还具备包括监控、日志、包管理等各种完善而专业的工具链,这将大大减轻运维和开发人员的负担。
1.3 服务注册发现
微服务的实践过程中存在各种服务依赖关系,因此服务的注册发现十
分重要。Kubernetes 对服务进行抽象,通过抽象的服务层动态地解析到对应的容器服务。Kubernetes 同时提供了 DNS 和环境变量两种方式,帮助实现服务的注册和发现,Kubernetes 早期版本中使用的环境变量方式实现,现在 Kubernetes 则默认使用 DNS,通过使用 DNS 将服务名称解析为服务的 IP 地址,然后 Proxy 转到对应的 Pod
1.4 主机资源利用率
K8s对主机的资源利用率,也是一种提升,基本上物理机,ECS,EC2,主机的利用率通常都在30%左右,极大的浪费了资源。使用k8s后,可以根据主机资源使用情况,自动的调度pod运行到那台机器上。可以极大的提高主机的资源利用率
1.5 弹性扩容
例如我们新上线的应用,因不同的业务场景,初次上线的时候不太确定给多少资源,默认就给应用4G内存,在k8s中我们可以根据内存的使用率,来定义是否启动一个新的pod,以及pod最少多少个,最多多少个
1.6 应用横向扩展
例如我们应用在在访问资源高峰期,应用需要进行添加机器,如果在ECS,EC2等机器,就需要在安装服务呀,配置负载之类的,在容器中就简单多了,直接修改ReplicaSet
修改节点数量,就会根据镜像自动启动新服务
安装步骤大致分为
2.1 安装ansible
2.2 集群配置,修改镜像源
2.3 部署k8s集群
2.4 安装svc ingress
2.5 安装helm
2.6 添加节点
会单独在给一个安装文档,文档格式是markdown,将文档用txt打开,然后全文粘贴,在百度搜索markdown在线编辑,然后粘贴进去阅读即可
推荐网址 https://www.zybuluo.com/mdeditor#1407318
接下来是第二部分,安装k8s
2,使用kubespray安装kubernetes
使用kubespray 安装kubernetes 比常规要方便很多,配置完ssh-key后,直接使用ansible 就可以快速的添加节点,删除节点
| 节点 | 公网IP|内网IP | 角色 |
| ------ | ------ | ------ |------ |
| master1 | 0.0.0.0 |172.16.199.169 | master+etcd|
| master2 | 0.0.0.0 | 172.16.199.168| master+etcd |
| master3 | 0.0.0.0 | 172.16.199.167|master+etcd |
| node1 | 0.0.0.0 |172.16.199.166 |node |
| node2 | 0.0.0.0 | 172.16.199.165 | ansible |
----
##安装k8s
#####基础准备
```
安装ansible
yum -y install ansible git
yum install -y python-pip python-netaddr ansible git
pip install --upgrade Jinja2
配置公钥互信,修改主机名。添加到/etc/hosts
```
######克隆代码
```
git clone https://github.com/kubernetes-incubator/kubespray.git
cd kubespray
git checkout v2.4.0 -b myv2.4.0
cp inventory/inventory.example inventory/inventory.cfg
```
#####修改ansible机器
```
vim inventory/inventory.cfg
[all]
master1 ansible_ssh_host=172.16.199.169 ip=172.16.199.169
master2 ansible_ssh_host=172.16.199.168 ip=172.16.199.168
master3 ansible_ssh_host=172.16.199.167 ip=172.16.199.167
node1 ansible_ssh_host=172.16.199.166 ip=172.16.199.166
[kube-master]
master1
master2
master3
[etcd]
master1
master2
master3
[kube-node]
node1
[k8s-cluster:children]
kube-node
kube-master
```
#####集群配置
```
cp inventory/group_vars/all.yml inventory/group_vars/all.yml.bak
cp inventory/group_vars/k8s-cluster.yml inventory/group_vars/k8s-cluster.yml.bak
vi inventory/group_vars/all.yml
bootstrap_os: centos #指定操作系统为 centos
vi inventory/group_vars/k8s-cluster.yml
flannel # 指定网络方案
kube_network_plugin: flannel # 默认关闭 dashboard dashboard_enabled: false
```
#####修改镜像源
```
vi roles/download/defaults/main.yml
etcd_image_repo: "registry.cn-hangzhou.aliyuncs.com/linkcloud/etcd"
flannel_image_repo: "registry.cn-hangzhou.aliyuncs.com/linkcloud/flannel"
flannel_cni_image_repo: "registry.cn-hangzhou.aliyuncs.com/linkcloud/flannel-cni"
hyperkube_image_repo: "registry.cn-hangzhou.aliyuncs.com/linkcloud/hyper-kube"
pod_infra_image_repo: "registry.cn-hangzhou.aliyuncs.com/linkcloud/pause-amd64"
nginx_image_repo: "registry.cn-hangzhou.aliyuncs.com/linkcloud/nginx"
kubedns_image_repo: "registry.cn-hangzhou.aliyuncs.com/linkcloud/k8s-dns-kube-dns-amd64"
dnsmasq_nanny_image_repo: "registry.cn-hangzhou.aliyuncs.com/linkcloud/k8s-dns-dnsmasq-nanny-amd64"
dnsmasq_sidecar_image_repo: "registry.cn-hangzhou.aliyuncs.com/linkcloud/k8s-dns-sidecar-amd64"
kubednsautoscaler_image_repo: "registry.cn-hangzhou.aliyuncs.com/linkcloud/cluster-proportional-autoscaler-amd64"
```
```
vi roles/kubernetes-apps/ansible/defaults/main.yml
kubedns_image_repo: "registry.cn-hangzhou.aliyuncs.com/linkcloud/k8s-dns-kube-dns-amd64"
dnsmasq_nanny_image_repo: "registry.cn-hangzhou.aliyuncs.com/linkcloud/k8s-dns-dnsmasq-nanny-amd64"
dnsmasq_sidecar_image_repo: "registry.cn-hangzhou.aliyuncs.com/linkcloud/k8s-dns-sidecar-amd64"
kubednsautoscaler_image_repo: "registry.cn-hangzhou.aliyuncs.com/linkcloud/cluster-proportional-autoscaler-amd64"
```
#####ansible 部署 预计1小时
```
部署会有失败的现象,请多等待,因为还会拉去国外地址的源
ansible-playbook -b -i inventory/inventory.cfg cluster.yml --flush-cache
```
#####安装serviceaccount
```
vim sa.yml
apiVersion: v1
kind: ServiceAccount
metadata:
name: tiller
namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
name: tiller
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- kind: ServiceAccount
name: tiller
namespace: kube-system
```
```
kubectl apply -f sa.yml
```
####安装ingress
```
apiVersion: v1
kind: ServiceAccount
metadata:
labels:
app: nginx-ingress
heritage: Tiller
release: nginx-ingress-0
name: nginx-ingress-0-nginx-ingress
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
labels:
app: nginx-ingress
heritage: Tiller
release: nginx-ingress-0
name: nginx-ingress-0-nginx-ingress
rules:
- apiGroups:
- ""
resources:
- configmaps
- endpoints
- nodes
- pods
- secrets
verbs:
- list
- watch
- apiGroups:
- ""
resources:
- nodes
verbs:
- get
- apiGroups:
- ""
resources:
- services
verbs:
- get
- list
- update
- watch
- apiGroups:
- extensions
resources:
- ingresses
verbs:
- get
- list
- watch
- apiGroups:
- ""
resources:
- events
verbs:
- create
- patch
- apiGroups:
- extensions
resources:
- ingresses/status
verbs:
- update
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
labels:
app: nginx-ingress
heritage: Tiller
release: ungaged-bear
name: nginx-ingress-0-nginx-ingress
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: nginx-ingress-0-nginx-ingress
subjects:
- kind: ServiceAccount
name: nginx-ingress-0-nginx-ingress
namespace: default
---
apiVersion: extensions/v1beta1
kind: DaemonSet
metadata:
labels:
app: nginx-ingress
component: controller
heritage: Tiller
release: nginx-ingress-0
name: nginx-ingress-0-nginx-ingress-controller
spec:
selector:
matchLabels:
app: nginx-ingress
component: controller
release: nginx-ingress-0
template:
metadata:
labels:
app: nginx-ingress
component: controller
release: nginx-ingress-0
spec:
containers:
- args:
- /nginx-ingress-controller
- --default-backend-service=default/nginx-ingress-0-nginx-ingress-default-backend
- --election-id=ingress-controller-leader
- --ingress-class=nginx
- --configmap=default/nginx-ingress-0-nginx-ingress-controller
env:
- name: POD_NAME
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.namespace
image: registry.cn-hangzhou.aliyuncs.com/licheng/nginx-ingress-controller:0.21.0
imagePullPolicy: IfNotPresent
livenessProbe:
failureThreshold: 3
httpGet:
path: /healthz
port: 10254
scheme: HTTP
initialDelaySeconds: 10
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 1
name: nginx-ingress-controller
ports:
- containerPort: 80
hostPort: 80
name: http
protocol: TCP
- containerPort: 443
hostPort: 443
name: https
protocol: TCP
readinessProbe:
failureThreshold: 3
httpGet:
path: /healthz
port: 10254
scheme: HTTP
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 1
resources: {}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
dnsPolicy: ClusterFirst
hostNetwork: true
restartPolicy: Always
serviceAccount: nginx-ingress-0-nginx-ingress
serviceAccountName: nginx-ingress-0-nginx-ingress
terminationGracePeriodSeconds: 60
templateGeneration: 1
updateStrategy:
type: OnDelete
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
annotations:
deployment.kubernetes.io/revision: "1"
labels:
app: nginx-ingress
component: default-backend
heritage: Tiller
release: nginx-ingress-0
name: nginx-ingress-0-nginx-ingress-default-backend
spec:
replicas: 1
selector:
matchLabels:
app: nginx-ingress
component: default-backend
release: nginx-ingress-0
strategy:
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
type: RollingUpdate
template:
metadata:
labels:
app: nginx-ingress
component: default-backend
release: nginx-ingress-0
spec:
containers:
- image: docker.io/mirrorgooglecontainers/defaultbackend:1.4
imagePullPolicy: IfNotPresent
livenessProbe:
failureThreshold: 3
httpGet:
path: /healthz
port: 8080
scheme: HTTP
initialDelaySeconds: 30
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 5
name: nginx-ingress-default-backend
ports:
- containerPort: 8080
protocol: TCP
resources: {}
dnsPolicy: ClusterFirst
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
terminationGracePeriodSeconds: 60
---
apiVersion: v1
kind: Service
metadata:
labels:
app: nginx-ingress
component: controller
heritage: Tiller
release: nginx-ingress-0
name: nginx-ingress-0-nginx-ingress-controller
spec:
ports:
- name: http
port: 80
protocol: TCP
targetPort: 80
- name: https
port: 443
protocol: TCP
targetPort: 443
selector:
app: nginx-ingress
component: controller
release: nginx-ingress-0
type: ClusterIP
---
apiVersion: v1
kind: Service
metadata:
labels:
app: nginx-ingress
component: default-backend
heritage: Tiller
release: nginx-ingress-0
name: nginx-ingress-0-nginx-ingress-default-backend
spec:
ports:
- port: 80
protocol: TCP
targetPort: 8080
selector:
app: nginx-ingress
component: default-backend
release: nginx-ingress-0
type: ClusterIP
```
```
然后kubectl apply -f ingress.yml
docker pull registry.cn-hangzhou.aliyuncs.com/licheng/nginx-ingress-controller:0.21.0
```
####安装helm
```
curl https://raw.githubusercontent.com/kubernetes/helm/master/scripts/get > get_helm.sh
```
```
./get_helm.sh
helm init
kubectl get pod -n kube-system
```
####添加Node
```
vi inventory/inventory.cfg
[all]
master1 ansible_ssh_host=172.16.199.169 ip=172.16.199.169
master2 ansible_ssh_host=172.16.199.168 ip=172.16.199.168
master3 ansible_ssh_host=172.16.199.167 ip=172.16.199.167
node1 ansible_ssh_host=172.16.199.166 ip=172.16.199.166
node2 ansible_ssh_host=172.16.199.165 ip=172.16.199.165
[kube-master]
master1
master2
master3
[etcd]
master1
master2
master3
[kube-node]
node1
node2
[k8s-cluster:children]
kube-node
kube-master
```
```ansible-playbook -i inventory/inventory.cfg scale.yml -b -v ```
3.1 如何建设devops 平台
Devops 平台整体架构如下
部署流程的优化
我们最早也是常规的Nginx+tomcat、LAMP、LNMP平台, 后来因服务器数量众多,服务器多数靠各项目的开发进行管理,没有统一的CMDB,同时服务器资源利用率也不高,有很大的资源闲置和资源浪费。我们是从18年8月份开始使用容器的。目前60%的业务都运行在容器平台上。其余的业务正在向容器云平台迁移中。
接下来是第三部分 3, 我们使用容器云平台原因
整体的架构流程如下
右边是自研的监控告警系统和运维对接平台,包括监控平台、日志平台和运维 CMDB 等系统
我们建设基于Kubernetes容器云平台,目标就是希望能够提高研发人员从代码构建到业务上线的整体效率。这个过程是一个完整的 CI/CD 流程,包括从开发人员提交代码,进行代码 Review,到代码编译、单元测试、集成测试,最后构建出业务镜像,在不同的环境中部署业务,上下线服务等各个环节,形成统一的应用生命周期管理
左侧是持续集成框架,其中由应用基线管理、代码编译、单元测试、冒烟测试、发布系统等模块组成。中间是多集群统一管理平台。业务会分别在开发、预发和生成三个不同的环境中部署。业务的最上层是由 SLB 软负载统一管理 DNS、Nginx、LVS 等组件,将业务流量转发给不同Kubernetes 集群中的业务容器。基于 Kubernetes 的 CaaS 由多集群管理、镜像管理、权限管理、服务发现等子模块组成。
3.4 服务发现
我们是在各个项目中,单独引用了一个自己写的jar包,在jar包中定义了注册中心地址。然后各个项目引入jar包,通过Jar中定义的地址去连接
3.2 镜像管理
我们是使用的自建的Harbor作为自己的镜像仓库, 是在阿里云购买的ECS, 我们的服务都是基于jdk, tomcat, nginx, php, nodejs, 等基础镜像起来的, 我们运维只需要维护这几个基础镜像就可以
3.3 权限管理
用户的容器集群由权限模块进行管理, 目前提供给开发的只有日志查询权限, 账号与公司的LDAP 对接。目前暂不支持虚拟账户的认证
3.5 稳定性
目前我们容器云平台,dev环境是自建的,UAT和PRO都是使用的阿里云。关于容器监控我们额外做了prometheus + alerting 并关联钉钉机器人和微信报警。除了应用本身的错误外,整个容器云平台没有出现过因服务类的down机情况
3.6.2 弹性能力。电商行业的特征决定了每年都会出现618,双11,双12等峰值流量。因为,在大促销期间动态的租用公有云服务,在平时只保留满足日常流量的机器即可
3.6 成本
降低机器成本和运维成本是一项长期艰苦的工作,我们在迁入容器云的过程中发现资源利用较好的两种情况。
3.6.1 混合调度。我们在提高容器部署的密度后,对业务带来的好处就是快速扩展和资源混合利用,我们多条线的pod会集中在一台实例中,可以很好的提高单机资源利用率
接下来是第四部分 4,我们现在的微服务架构
基本上也是多数公司的微服务架构,是通过 zuul + eureka + apollo 来完成了,eureka本身有个监测心跳机制,例如我的容器A1, A2 注册到了eureka, 此时要部署容器,例如新部署的容器要B1,B2,在健康检查完全通过后, A1,A2已经被替换掉。在k8s层面做到了无缝切换, 但是 eureka 本身还会将A1,A2保留约2分钟。也就是这个时间内,eureka(注册中心)会同时存在4个pod
可以代替掉传统网关
我们现在正在替换掉eureka,向istio 迁移
Istio 同时也具备
统一的入口
动态服务发现和路由规则
负载均衡
策略和请求tracing
熔断器
健康检查,基于百分比流量拆分和灰度发布
认证和鉴权
在这个时间点通过zuul来访问服务的话,则会有50%的请求发布到老地址上去。老地址容器此时已经被销毁,访问则会报出502,504等错误。荐更换掉eureka。使用istio,istio可以避免这个问题。