1 Star 0 Fork 489

黄韬 / gulimall_out

forked from wanzenghui / gulimall 
加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
集群篇 (暂未更新).md 150.67 KB
一键复制 编辑 原始数据 按行查看 历史
wanzenghui 提交于 2022-02-24 22:28 . 新版笔记
typora-copy-images-to typora-root-url
assets
assets

[TOC]

1599806025370

https://www.kubernetes.org.cn/7315.html:根据这片文章的脉络开始结合本md学习

一、K8S

1、简介

Kubernetes.简称k8s。是用于自动部署,扩展和管理容器化应用程序的开源系统。
【可以分布式的部署整个系统,管理整个系统】
中文官网:https://kubernetes.io/zh/【推荐这个,然后点击 (学习 Kubernetes 基础知识)】
中文社区:https://www.kubernetes.org.cn/
官方文档:https://kubernetes.io/zh/docs/home/
社区文档:http://docs.kubernetes.org.cn/

1)与传统部署方式对比

https://kubernetes.io/zh/docs/concepts/overview/what-is-kubernetes/

1、传统方式:硬件+OS+应用程序环境【应用打包】
	弊端:
		1)资源分配问题【多个应用之间资源不隔离,没有明确的界限(一个服务宕机可能导致所有宕机)】
		2)迁移问题,每个应用的环境都需要迁移
		
2、虚拟化部署时代:在OS基础上运行多个虚拟机
	优点:
		1)资源隔离
		2)降低硬件成本
	弊端:
    	1)资源浪费:每一个虚拟机都有一套完整的系统

3、容器部署时代:
	优点:
		1)资源节省
		2)迁移方便:容器打包成镜像运行在任何一个安装了docker的服务器上
		3)资源隔离
		4)降低硬件成本
    	

1599806917415

2)kubernetes作用:管理容器

https://kubernetes.io/zh/docs/concepts/overview/what-is-kubernetes/

上面提到了使用容器部署的技术,docker
那就需要kubernetes来管理容器了【可以使用swarm,docker官方的。找到每个服务器上的docker,给每一个docker里面部署一个容器,但是swarm只能管理docker,还有其他的服务器】
	例如:使用swarm启动3个cart服务,swarm会感知集群内多个docker服务器,找到3台来启动cart服务

1、服务发现、负载均衡
	springcloud只是针对java的,它是针对所有服务的
2、存储编排:
	
3、自动部署和回滚:
	如果某服务宕机还可以将这个应用部署到另外一个容器上
	版本不对回滚,自我打包

4、自我修复
	如果宕机了会自动部署到另外一个容器上
5、自动伸缩
	如果超过负载了CPU 占用80,检查到后自动伸缩

1599807906072

1599807584739

3)kubernetes组件

Master 组件

Node 组件

主从方式:

1599808744617

master暴露API,命令行或可视化界面调用API,管理NODE,每个NODE上面都安装了docker【镜像仓库】

1599808793994

# master组件

master内部还有很多组件
一次流程:
	1)给API Server发送请求,cart部署3台
	2)然后将信息存储到etcd
	3)scheduler从etcd拿到信息开始调度【那个NODE可以完成该任务】
	4)

1、API Server

2、etcd
	存储数据,键值数据库【etcd也可以作为集群】
	
3、kube-scheduler
	
4、controller
	有很多控制器

1599808901921

1599809158441

1599809093625

# Node组件

1599809218020

1599809244615

kublet:生命周期,代理保证 容器都运行在pod中
kube-proxy:路由器,负载均衡【当前node有多份pod】

1599809406617

# 一次完整调用

1、controller监听etcd,会创建pod实例,通过API Server写入etcd
2、scheduler监听到该事件,选定一个落户的Node,将结果通过API Server写入etcd
3、指定的Node的kubelet监听到etcd,创建一个Pod

1599810779911

1599810800496

1599810811990


1599810144507

kubectl:可视化界面,给API Server发送请求
pod:例如docker里面多个容器组成一个pod,两个应用组成一个功能

1、kubectl发送请求,master组件API Server接收请求
2、相关命令操作存储到etcd
3、master组件的scheduler根据etcd中存储的信息创建调度任务
4、判断是有状态无状态应用调用不同的controller,例如musql是有状态应用,数据是存在本地的
5、
6、由Node的kubeket创建Pod,Pod内部有多个Container,并且每个Container可以挂载自己的资源
7、多个pod组成一个service,负载均衡
8、访问路由器->kube-proxy

1599809778192

1599810167791

1599810364342

1599810438680

1599810509510

1599810517648

1599810585695

例:指定版本、名字、镜像、端口

1599810604686

2、使用篇

1)6个步骤

如何使用?分为以下六步【https://kubernetes.io/zh/里面有下面的图】
	这下面6个是可以点击的,例如1,就会跳进相关教程了:https://kubernetes.io/zh/docs/tutorials/kubernetes-basics/create-cluster/cluster-intro/

1599806693681

2)搭建前的准备工作 P341

不使用这个:1、安装minikube
https://github.com/kubernetes/minikube/releases

1、前置要求
一台或多台机器,操作系统CentOS7.x-86_x64
硬件配置:2GB或更多RAM,2个CPU 或更多CPU,硬盘30GB或更多
集群中所有机器之间网络互通
可以访问外网,需要拉取镜像
禁止swap分区I


参照文档:https://v1-17.docs.kubernetes.io/zh/docs/setup/production-environment/
2、kubadmin
kubeadm是官方社区推出的一个用于快速部署kubernetes,集群的工具。这个工具能通过两条指令完成一个kubernetes集群的部署:
	#创建一个Master 节点
	$kubeadm init

	#将一个Node节点加入到当前集群中
	kubeadm join <Master节点的IP和端口>

3、部署步骤
	1.在所有节点上安装Docker和kubeadm
	2.部署 Kubernetes Master
	3.在master上部署容器网络插件
	4.部署 Kubernetes Node,将节点加入Kubernetes集群中【对应小节7】
	5.部署Dashboard Web页面,可视化查看 Kubernetes资源

4、环境准备
	1、准备工作
	我们可以使用vagrant快速创建三个虚拟机。虚拟机启动前先设置virtualbox的主机网络。现全部统一为192.168.56.1,以后所有虚拟机都是56.x的ip地址
	然后全局设置位置,别放C盘

1599811575770

1599811911358

1599811465234

3)开始搭建

1、拷贝k8s文件夹到一个无空格、中文的目录下,修改Vagrantfile的node.vm.box = "centos7"
	因为之前vagrant box add centos7 CentOS-7-x86_64-Vagrant-2004_01.VirtualBox.box给本地设置了一个centos7,否则使用centos/7会去仓库下载会很慢

2、在k8s目录下执行vagrant up,然后就会创建出3个虚拟机

3、修改每个node,使其可以远程连接
	vagrant ssh k8s-node1
	su root   密码:vagrant
	vi /etc/ssh/sshd_config	修改PasswordAuthentication yes
	reboot【service sshd restart】
	xshell连接,192.168.56.100 -- root + vagrant
	
4、修改网络
	网卡1是k8s使用,网卡2仅主机模式是保证主机开发工具可以连接虚拟机【再一个局域网内】
	前言:使用ip route show查看每个虚拟机使用的都是eth0网卡,然后输入ip addr,每个虚拟机的eth0网卡都是10.0.2.215这个ip,原因是都使用的是网络地址转换NAT【三台虚拟机共用一个ip】。
	就是三个虚拟机节点的IP都是一样的,然后通过 + 端口转发【宿主机端口与虚拟机端口的映射(会同时映射3台)】
	1)全部关闭电源
	2)全局设定=》网络=》创建NAT网络
	3)选中node1机器(每个机器都要设置)=》网络=》网卡1=》选择NAT网络(选中刚刚创建的)=》高级=》刷新mac地址【每台虚拟机都会生成一个IP,这个IP要记录下来,ip addr查看网卡1就是eth0的ip】=》ok
	
5、设置linux环境
关闭防火墙:
	systemctl stop firewalld
	systemctl disable firewalld
关闭selinux:【默认安全策略】
	sed -i 's/enforcing/disabled/' /etc/selinux/config
	setenforce 0
关闭swap:
	swapoff -a	临时
	sed -ri 's/.*swap.*/#&/' /etc/fstab
	
修改主机名:hostname 查看主机名如果是localhost,则修改为k8s-node1
hostnamectl set-hostname <newhostname>:指定新的hostname

修改hosts
vi /etc/hosts
10.0.2.15 k8s-node1
10.0.2.4 k8s-node2
10.0.2.5 k8s-node3

将桥接的IPv4流量传递到iptables的链:
cat > /etc/sysctl.d/k8s.conf << EOF
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
EOF

sysctl --system


疑难问题:
遇见提示是只读的文件系统,运行如下命令
mount -o remount rw /

date查看时间(可选)
yum install -y ntpdate
ntpdate time.windows.com同步最新时间


备份:
给三台主机做备份

1599832767824

1599835763117

4)安装Docker、kubeadmin、kubelet、kubectl

# 安装docker

1、卸载旧版本【uninstall old versions】
	sudo yum remove docker \
                  docker-client \
                  docker-client-latest \
                  docker-common \
                  docker-latest \
                  docker-latest-logrotate \
                  docker-logrotate \
                  docker-engine
                  
2、安装相关依赖
sudo yum install -y yum-utils device-mapper-persistent-data lvm2

3、设置镜像地址
	sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
推荐使用阿里镜像:
	sudo yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo【这个镜像和后面提到的镜像加速不同,这里指的是下载docker本身的镜像地址】
	
4、安装docker以及docker-cli
sudo yum install -y docker-ce docker-ce-cli containerd.io

5、启动docker
sudo systemctl start docker
虚拟机开机启动:sudo systemctl enable docker

6、测试
docker images -a

7、docker配置镜像下载加速
	1)默认从hub.docker下载软件镜像【很慢】
	2)修改成aliyun的镜像加速器
	3)执行【获取第一步配置的镜像加速器网址】
sudo tee /etc/docker/daemon.json <<-'EOF'
{
  "registry-mirrors": ["https://7zi5cb9i.mirror.aliyuncs.com"]
}
EOF

sudo systemctl daemon-reload
sudo systemctl restart docker
	
可以备份一下这里
docker pull quay.io/openebs/provisioner-localpv:1.5.0

# 添加阿里云yum源

cat > /etc/yum.repos.d/kubernetes.repo << EOF
[kubernetes]
name=Kubernetes
baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64/
enabled=1
gpgcheck=0
repo_gpgcheck=0
gpgkey=https://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg https://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg
EOF

# 安装kubeadmin、kubelet、kubectl

	查看是否安装相关源:yum list|grep kube
1、安装
yum install -y kubelet-1.17.3 kubeadm-1.17.3 kubectl-1.17.3

2、开机启动kubelet,要把每一个Node注册到集群中
sudo systemctl enable kubelet
sudo systemctl start kubelet【到这一步暂时还启动不起来:systemctl status kubelet,后面还有要配置的】

# kubeadm reset:重置集群

https://blog.csdn.net/woay2008/article/details/93250137

swapoff -a
kubeadm reset
主机批量处理:
systemctl daemon-reload
systemctl restart kubelet
iptables -F && iptables -t nat -F && iptables -t mangle -F && iptables -X  
rm -rf $HOME/.kube
主机+从机:
     rm -rf /var/lib/etcd
	 rm -rf /etc/cni/net.d
	 rm -rf /var/lib/kubelet
	 rm -rf /etc/kubernetes

问题解决过程

期间,我尝试了所有能搜索的相关资料,都没有一个好使的。我还确认了kubeadm reset命令会完全清除已创建的集群配置,那么为什么清配置后重新创建集群却不行呢?实在没办法我把注意力集中到额外执行的这几个命令上:

  mkdir -p $HOME/.kube
  sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
  sudo chown $(id -u):$(id -g) $HOME/.kube/config
这几个命令会创建一个目录,并复制几个配置文件,重新创建集群时,这个目录还是存在的,于是我尝试在执行这几个命令前先执行rm -rf $HOME/.kube命令删除这个目录,最后终于解决了这个问题!!!
总结

这个问题很坑人,删除集群然后重新创建也算是一个常规的操作,如果你在执行 kubeadm reset命令后没有删除创建的 $HOME/.kube目录,重新创建集群就会出现这个问题!

5)部署k8s-master

# master节点初始化

使用kubeadm这个工具

1、挑选一台主机为master【当前选择10.0.2.15作为主机】
暂时先不运行下面语句,将k8s文件夹拷贝到master主机的root路径下
sudo kubeadm init \
--apiserver-advertise-address=10.0.2.15 \
--image-repository registry.cn-hangzhou.aliyuncs.com/google_containers \
--kubernetes-version v1.17.3 \
--service-cidr=10.96.0.0/16 \
--pod-network-cidr=10.244.0.0/16

由于默认拉取镜像地址k8s.gcr.io国内无法访问,这里指定阿里云镜像仓库地址。可以手动按照我们的执行master_images.sh先拉取镜像,地址变为registry.aliyuncs.com/google_containers也可以。

科普:无类别域间路由(Classless Inter-Domain Routing、CIDR)是一个用于给用户分配IP地址以及在互联网上有效地路由IP数据包的对IP地址进行归类的方法。
拉取可能失败,需要下载镜像。

运行完成提前复制:加入集群的令牌

pod-network-cidr:docker最小的单位是容器,pod是k8s中最小的单位(多个容器组成)
service-cidr:service是一组pod组成对外提供服务,负载均衡

2、修改可执行文件权限【拉取镜像】
cd k8s
ll查看文件
master_images.sh:rw- 没有执行权限【root没有执行权限】

chmod 700 master_images.sh  => rwx

3、下载镜像:执行master_images.sh
./master_images.sh
docker images 查看镜像下载情况

4、初始化master
kubeadm init \
--apiserver-advertise-address=10.0.2.15 \
--image-repository registry.cn-hangzhou.aliyuncs.com/google_containers \
--kubernetes-version v1.17.3 \
--service-cidr=10.96.0.0/16 \
--pod-network-cidr=10.244.0.0/16

打印Your Kubernetes control-plane has initialized successfully!代表初始化成功

5、 生成配置文件
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config

6、部署pod网络
参照doc:https://kubernetes.io/docs/concepts/cluster-administration/addons/
使用Flannel: 是一个可以用于 Kubernetes 的 overlay 网络提供者。

7、记录令牌【这是第4步执行后记录下来的】
kubeadm join 10.0.2.15:6443 --token 1jgtez.7x3xdrn9j15jb1pn \
    --discovery-token-ca-cert-hash sha256:0d6f2f6bd1074a00c6d25b67da8fcbd5bab4ecbefc59fe4a354cb8bc0d1db1cf 

如果token过期,重新获取一个没有过期时间的token【默认是2h过期】:
kubeadm token create --print-join-command --ttl=0

6)安装pod网络插件(CNI)

master配置

1、执行
kubectl apply -f \
https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
这个地址被墙,使用我们k8s文件夹中的yml即可,同时flannel.yml中指定的images访问不到可以去docker hub找一个

2、不执行上述网络地址的yml,使用本地的flannel.yml【先拉取镜像】
	1)如果kube-flannel.yml中指定的images访问不到去docker.hub中查找并修改yml中的配置
		# 1、访问hub.docker.com,搜索flannel,点击from quay.io/coreos/flannel
		# 2、拷贝v0.11.0-amd64的来源jmgao1983/flannel:v0.11.0-amd64
		# 3、flannel.yml中image指定的地方,修改
	2)也可以使用quay-mirror.qiniu.com和registry.aliyuncs.com
	例如quay.io=》quay-mirror.qiniu.com  || gcr.io改为registry.aliyuncs.com
下拉镜像:quay.io/coreos/flannel:v0.10.0-s390x
如果拉取较慢,可以改为:quay-mirror.qiniu.com/coreos/flannel:v0.10.0-s390x

下拉镜像:gcr.io/google_containers/kube-proxy
可以改为: registry.aliyuncs.com/google_containers/kube-proxy

3、执行kubectl apply -f kube-flannel.yml
使用本地的yml规则安装pod组件【kubectl delete -f kube-flannel.yml:删除yml中配置的所有组件 】

4、获取名称空间
kubectl get ns

5、获取所有名称空间的pod
kubectl get pods --all-namespaces -o wide
如果全部都是running就成功了,如果有不是running状态的等待一段时间,下载好镜像后就可以了

docker images|grep flannel可以查到以下镜像:
jmgao1983/flannel                                                             v0.11.0-amd64       ff281650a721        19 months ago       52.6MB

访问hub.docker.ccom,搜索flannel:点击from quay.io/coreos/flannel

flannel.yml:修改docker镜像地址,就是下图image指定的那一段。

1600083281929

1600083242226

7)将所有Node节点join都master节点

1、获取kubernetes中所有的节点【在master执行】
kubectl get nodes

2、当master是ready状态时,在k8s-node2、k8s-node3执行以下命令将其添加到master
kubeadm join 10.0.2.15:6443 --token 4k7qur.oe6ab1qsgpl7swcm \
    --discovery-token-ca-cert-hash sha256:06b6d21ef7e786b8d759017c29f3041dd886af32026aa5f5333cb91e79f4e636 
    
3、master监控Node各节点
watch kubectl get pod -n kube-system -o wide
查看到node2和node3镜像下载失败
docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/kube-proxy:v1.17.3
docker pull kubesphere/kube-proxy:v1.17.3
docker pull jmgao1983/flannel:v0.11.0-amd64

1600086458266

8)排除错误方法

多学会看日志

1、master执行watch kubectl get pod -n kube-system -o wide发现问题,例如CrashLoopBackOff
https://www.jianshu.com/p/bcc05427990d
	1)找到出问题的主机执行kubectl get pod获取pod的状态
	2)查看pod的详细信息:kubectl describe pod elkhost-944bcbcd4-8n9nj
	3)查看此pod日志:kubectl logs elkhost-944bcbcd4-8n9nj

2、

9)入门操作kubernetes集群

前面已经搭建好了一个kubernetes集群环境,下面的demo是给k8s集群部署一个Tomcat
以前是docker run 下载一个镜像,来启动一个容器,现在是在master节点创建一个部署,指定一个镜像,然后指挥其他节点来下载镜像

1、部署一个tomcat,pod的形式【此时还不能访问没有service】
kubectl create deployment tomcat6 --image=tomcat:6.0.53-jre8【1个部署对应1个pod对应1个容器】
Kubectl get pods -o wide可以获取到tomcat信息【Kubectl get all】
# 容灾恢复1:在k8s-node3执行docker stop tomcat6停掉该容器,kubernetes会给node3重启一个容器
# 容灾恢复2:如果k8s-node3宕机,master会找到集群中另一个节点启动pod

2、暴露一个部署【创建service】
kubectl expose deployment tomcat6 --port=80 --target-port=8080 --type=NodePort
解释:暴露一个tomcat6的部署,容器的8080端口映射pod的80端口,然后使用NodePort的方式【service为pod的80端口随机分配一个端口提供服务】。也可以不使用--type使用--node-port=31533
192.168.56.101:31533访问tomcat6

3、动态扩容测试
kubectl get deployment
应用升级:kubectl set image
扩容哪个部署:kubectl scale --replicas=3 deployment tomcat6
结果:在k8s-node2部署了2个pod,在k8s-node3部署了1个pod,一共3个。并且service对外暴露的接口都是31533
192.168.56.101:31533
192.168.56.102:31533 都可以访问tomcat6

1600091057549

10)命令集合

1、kubectl create deployment tomcat6 --image=tomcat:6.0.53-jre8
	选择某一节点部署tomcat6,指定镜像,部署后使用Kubectl get pods -o wide可以查到
1、kubectl get pods -o wide
	获取所有pod信息,并且获取到pod部署在哪个节点
   kubectl get pods --namespace kube-system -o wide【获取指定命名空间下的所有pod】
   kubectl --namespace kube-system describe pod tiller-deploy-7b76b656b5-qpmb4【查询pod详细信息】
   kubectl get deployment -n kube-system【查询名称空间下的部署】
   kubectl delete deployment tiller-deploy  --namespace kube-system【删除名称空间下的部署】
	【要删除部署,不要删除pod,否则会自动部署pod】

   kubectl get pod -n kube-system -l app=helm【查看helm】
   kubectl get pods -n kube-system -owide | grep tiller-deploy【查看tiller】
   
3、kubectl get all
	获取所有:pod、service、deployment
4、kubectl get nodes
	获取所有节点
5、kubectl expose deployment tomcat6 --port=80 --target-port=8080 --type=NodePort
	暴露一个部署。service随机分配端口
6、kubectl get svc
   kubectl get svc -o wide
	查看service,能查看到分配的端口
7、kubectl get deployment
	查看部署
8、kubectl get ns
	查看namespace命名空间
8、kubectl set image
	应用升级
9、kubectl scale --replicas=3 deployment tomcat6
	扩容3份tomcat6这个部署,3个pod
10、kubectl delete deployment.apps/tomcat6-5f654a5s
    kubectl delete service/tomcat6
    kubectl delete pod/tomcat6-5f654a5s-mlntp
    删除
11、watch kubectl get pod -n kube-system -o wide
	监控
	
12、查看日志:kubectl logs --namespace kubesphere-devops-system

1600092795688

1600093091103

11)污点

https://docs.lvrui.io/2018/11/14/%E4%B8%BAk8s-master%E8%8A%82%E7%82%B9%E6%B7%BB%E5%8A%A0%E6%B1%A1%E7%82%B9taints/

3、k8s细节

1)kubectl命令

https://kubernetes.io/zh/docs/reference/kubectl/overview/
查看文档

# kubectl文档

# 资源类型

# 格式化输出

# 常用操作

# 命令参考

2)yaml语法

之前是使用命令的方式 创建 部署、暴露部署、创建pod【多个容器】
之前使用kubectl create deployment tomcat6 --image=tomcat:6.0.53-jre8命令行的方式部署tomcat6,现在只需要kubectl apply -f example-controller.yaml就可以实现一次部署了

1、使用yaml的方式完成部署
	1)生成一段yaml模板到tomcat6.yaml
	kubectl create deployment tomcat6 --image=tomcat:6.0.53-jre8 --help可以看到--dry-run -o yaml生成模板的提示
	生成模板到文件tomcat6.yaml:
	kubectl create deployment tomcat6 --image=tomcat:6.0.53-jre8 --dry-run -o yaml > tomcat6.yaml
	2)使用该yaml完成部署
	kubectl apply -f tomcat6.yaml
	然后kubectl get pods,查看到部署的pods
	
2、使用yaml的方式完成暴露部署
	1)生成一段yaml模板到tomcat6_expose.yaml
	kubectl expose deployment tomcat6 --port=80 --target-port=8080 --type=NodePort --dry-run -o yaml
	kubectl expose deployment tomcat6 --port=80 --target-port=8080 --type=NodePort --dry-run -o yaml > tomcat6_expose.yaml
	2)使用该yaml完成暴露
	kubectl apply -f tomcat6_expose.yaml
	
3、使用yaml的方式创建pod
	kubectl get pod tomcat6-5f7ccf4cb9-kb5hv -o yaml > mypod.yaml
如下图,创建两个image。
	kubectl apply -f mypod.yaml
	kubectl get pods : 0/2 获取到tomcat6-new 有2个容器
	

1600095450915

1600095496466

# yml模板

1600093981351

# yaml字段解析

3)入门操作

# pod、controller、service

Pod是Kubernetes应用程序的基本执行单元,即它是Kubernetes对象模型中创建或部署的最小和最简单的单元。Pod表示在集群上运行的进程。

控制器:创建和管理POD以何种方式部署【副本、上线、修复能力】
	ReplicaSet:取代了ReplicationController,用于副本复制
	ReplicationController:过时
	Deployments:
	StatefulSets:有状态的部署【mysql有状态部署】
	DaemonSet:每一个节点都需要启动

service:
	service是对pod的负载均衡,pod可以使用不同的端口,然后整个service提供一个单独的端口为外接提供服务。

controller和service:

1600254533803

二、KubeSphere

1、安装前提环境

KubeSphere是一个可视化工具
前提需要安装:https://www.cnblogs.com/xiao987334176/p/13267339.html
	1、helm+tiller
	2、存储类型StorageClass【这里安装的是OpenEBS,上面网站教程安装的是nfs】
	3、安装部署KubeSphere【需要8核16g,这里安装的是mini版本】

KubeSphere是一款面向云原生设计的开源项目,在目前主流容器调度平台 Kubernetes之上构建的分布式多租户容器管理平台,提供简单易用的操作界面以及向导式操作方式,在降低用户使用容器调度平台学习成本的同时,极大降低开发、测试、运维的日常工作的复杂度。

安装网址doc:https://v2-1.docs.kubesphere.io/docs/zh-CN/installation/prerequisites/

1600255046194

1、安装helm+tiller


KubeSphere的doc:https://v2-1.docs.kubesphere.io/docs/zh-CN/installation/prerequisites/

下载helm参照:https://blog.csdn.net/qianghaohao/article/details/98851147【被墙】
helm安装指南:https://zhuanlan.zhihu.com/p/77496043

1、安装Helm (master节点执行)
这个网站有很多中下载heml的方法:http://www.coderdocument.com/docs/helm/v2/using_helm/installing_helm.html

方法一:
	1)使用sh文件下载【需要翻墙】:curl -L https://git.io/get_helm.sh | bash
	2)chmod 700 get_helm.sh
	3)./get_helm.sh

方法二:
	1)直接去github下载tar.gz:https://github.com/helm/helm/tags
	2)然后解压 tar -zxvf helm-v2.16.3-linux-amd64.tar.gz
	3)mv /root/k8s/linux-amd64/helm /usr/local/bin/helm
	   mv /root/k8s/linux-amd64/tiller /usr/local/bin/tiller
	4)helm help【查看是否安装成功】

2、创建服务账号和角色绑定
vi helm-rbac.yaml,把下面内容加到yaml中

apiVersion: v1
kind: ServiceAccount
metadata:
  name: tiller
  namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: tiller
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
  - kind: ServiceAccount
    name: tiller
    namespace: kube-system

3、执行:kubectl apply -f helm-rbac.yaml

4、、初始化+安装tiller【这里最终解决是修改了dns】
	helm init --service-account tiller --upgrade -i registry.aliyuncs.com/google_containers/tiller:v2.16.3
 	【--tiller-image指定镜像,否则会被墙】
安装tiller的doc:https://zhuanlan.zhihu.com/p/77496043

5、检查tiller安装是否成功【如果安装失败,参考命令集合,查看pod细节 describe】
【这四个都可以查是否安装成功】
tiller
helm version
kubectl get deployment -n kube-system【看pod】
kubectl get pod -n kube-system -l app=helm【看部署】
kubectl get pods --all-namespaces【看所有pods】
修改dns:【解决docker拉取失败】
vim /etc/resolv.conf
nameserver 114.114.114.114将原来的覆盖掉
不需要重启网络,否则会将新的dns覆盖掉

也可以在centos7中永久性的修改dns,但感觉速度不是很好
cd /etc/sysconfig/network-scripts/
ls
打开设置了静态ip的网卡文件,如ifcfg-enp0s8

在PEERDNS="no"下面添加
DNS1=8.8.8.8
DNS2=114.114.114.114

PEERDNS="no"
DNS1=8.8.8.8
DNS2=114.114.114.114


重启网络
sudo service network  restart

查看dns是否修改
cat /etc/resolv.conf

2、安装 存储类型 OpenEBS ( StorageClass )

OpenEBS 只是kubernetes的一种存储类型,还有GlusterFS、NFS(network file system )
下载教程:doc:https://v2-1.docs.kubesphere.io/docs/zh-CN/appendix/install-openebs/
1、查看节点名称:
	kubectl get node -o wide
2、确认 master 节点是否有 Taint,如下看到 master 节点有 Taint。
	kubectl describe node k8s-node1 | grep Taint
3、删除 master 节点的 Taint:【污点,就是不让master可以部署。去掉后就可以部署了】
	
	kubectl taint nodes k8s-node1 node-role.kubernetes.io/master:NoSchedule-
4、安装 OpenEBS【http://docs.openebs.io/】
	1)创建 OpenEBS 的 namespace,OpenEBS 相关资源将创建在这个 namespace 下:
	kubectl create ns openebs
	kubectl get ns
	2)安装
	helm install --namespace openebs --name openebs stable/openebs --version 1.5.0
	
	BUG1:
	helm repo remove stable
	helm repo add stable https://kubernetes-charts.storage.googleapis.com
	helm repo update
	helm search
	然后重新安装:
	helm install --namespace openebs --name openebs stable/openebs --version 1.5.0
	
	BUG2:镜像下载失败【重新设置一下阿里云加速】
	查看部署
	kubectl get deployment -n openebs
	kubectl --namespace openebs describe pod openebs-ndm-g8mfh
	再下载image【k8s-node2、k8s-node3都下载】
	docker pull openebs/admission-server:1.5.0			
	docker pull openebs/m-apiserver:1.5.0      			
	docker pull openebs/provisioner-localpv:1.5.0		
	docker pull openebs/node-disk-manager-amd64:v0.4.5  【这个在k8s-node1(master)中下载】
	docker pull openebs/node-disk-operator-amd64:v0.4.5 
	docker pull openebs/openebs-k8s-provisioner:1.5.0	
	
	docker pull openebs/snapshot-controller:1.5.0		
	docker pull openebs/snapshot-provisioner:1.5.0		
	然后查看部署状态:【全部running,则安装成功】
	kubectl get deployment -n openebs
	watch kubectl get pods --all-namespaces -o wide【监控,如果全部ready则成功】
	
5、安装 OpenEBS 后将自动创建 4 个 StorageClass,查看创建的 StorageClass
	kubectl get sc
6、将 openebs-hostpath设置为 默认的 StorageClass:
kubectl patch storageclass openebs-hostpath -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'

7、OpenEBS 的 LocalPV 已作为默认的存储类型创建成功。可以通过命令 kubectl get pod -n openebs来查看 OpenEBS 相关 Pod 的状态,若 Pod 的状态都是 running,则说明存储安装成功
【污点在kubesphere安装成功后加】
kubectl taint nodes k8s-node1 node-role.kubernetes.io/master=:NoSchedule

1600273465679

2、yaml部署+暴露tomcat6

之前是命令行的方式安装,现在使用yaml
1、创建yaml部署脚本:
kubectl create deployment tomcat6 --image=tomcat:6.0.53-jre8 --dry-run -o yaml > tomcat6-deployment.yaml
2、修改yaml,3个副本
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: tomcat6
  name: tomcat6
spec:
  replicas: 3
  selector:
    matchLabels:
      app: tomcat6
  template:
    metadata:
      labels:
        app: tomcat6
    spec:
      containers:
      - image: tomcat:6.0.53-jre8
        name: tomcat
3、部署
kubectl apply -f tomcat6-deployment.yaml 
kubectl get all:查看部署效果
kubectl get pods --all-namespaces -o wide:查看pods

4、暴露【暴露与部署可以一次执行】
	1)运行以下命令,并复制生成的yaml代码
	kubectl expose deployment tomcat6 --port=80 --target-port=8080 --type=NodePort --dry-run -o yaml
	2)修改部署文件,在文档末位加上---,然后另起一行将以下内容粘贴进去
	vi tomcat6-deployment.yaml 
	
apiVersion: v1
kind: Service
metadata:
  labels:
    app: tomcat6
  name: tomcat6
spec:
  ports:
  - port: 80
    protocol: TCP
    targetPort: 8080
  selector:
    app: tomcat6
  type: NodePort

5、删除之前的部署
kubectl get all
kubectl delete deployment.apps/tomcat6

6、部署+暴露
kubectl apply -f tomcat6-deployment.yaml 
kubectl get all

7、访问
192.168.56.101:32501
192.168.56.102:32501

1600321201191

3、安装ingress

1600321387857

https://mritd.me/2016/12/06/try-traefik-on-kubernetes/
https://mritd.me/2017/03/04/how-to-use-nginx-ingress/
ingress的作用:
	1)为集群中每个节点都开启80、443端口,配置域名环境就可以直接负载均衡请求到service【底层也是nginx】
	2)可以使用域名直接访问,如果直接配置nginx的话,nginx是部署在k8s集群上的,使用NodePort对外暴露一个端口,则必须带上端口访问nginx。
	3)使用域名访问ingress,ingress带上host转发给nginx的service

192.168.56.101:32501
192.168.56.102:32501

查看ingress-controller,使用的controller是DaemonSet,所以每个节点都要部署

1、kubectl apply -f ingress-controller.yaml 【每一个节点都要部署】

2、创建ingress规则
注意:servicePort是暴露的pod端口,而不是service暴露的随机端口
vi ingress-tomcat6.yaml

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: web
spec:
  rules:
  - host: tomcat6.atguigu.com
    http:
      paths:
        - backend:
           serviceName: tomcat6
           servicePort: 80
3、kubectl apply -f ingress-tomcat6.yaml

4、宿主机配置host
192.168.56.102 tomcat6.atguigu.com

5、访问,就可以直接访问tomcat6了,ingress监听80端口转发到102上面的8080pod上
http://tomcat6.atguigu.com/

如果此时192.168.56.102 node3节点宕机,请求会被转发到node2 192.168.56.101上

使用DaemonSet,每一个节点都要部署:

1600322453986

4、安装KubeSphere

最关键的两个文档:
https://kubesphere.com.cn/forum/d/1262-k8s/56【安装OpenEBS和KubeSphere都要删掉master的污点】
https://kubesphere.com.cn/forum/d/1272-kubeadm-k8s-kubesphere-2-1-1【安装nfs版本】

这是guthub的安装指南:https://github.com/kubesphere/ks-installer/blob/master/README_zh.md
中国官方安装文档:https://v2-1.docs.kubesphere.io/docs/zh-CN/installation/install-on-k8s/

1600359650270

【这个是基本的安装流程,具体流程需要看后面一个版块的流程】

1、下载yaml【下载不到】
安装mini版本:
wget https://raw.githubusercontent.com/kubesphere/ks-installer/v3.0.0/deploy/kubesphere-installer.yaml
wget https://raw.githubusercontent.com/kubesphere/ks-installer/v3.0.0/deploy/cluster-configuration.yaml

2、查看日志
kubectl logs -n kubesphere-system $(kubectl get pod -n kubesphere-system -l app=ks-install -o jsonpath='{.items[0].metadata.name}') -f

3、访问:192.168.56.100:30880/login
admin/P@88w0rd
改成了Wan123
这个版本是添加和关闭相关组件的版本:
1、下载yaml
wget https://raw.githubusercontent.com/kubesphere/ks-installer/v3.0.0/deploy/kubesphere-installer.yaml
wget https://raw.githubusercontent.com/kubesphere/ks-installer/v3.0.0/deploy/cluster-configuration.yaml

2、调整虚拟机内存
Node2和Node调到11G,6核

3、安装
kubectl apply -f kubesphere-installer.yaml
kubectl apply -f cluster-configuration.yaml

4、查看日志
kubectl logs -n kubesphere-system $(kubectl get pod -n kubesphere-system -l app=ks-install -o jsonpath='{.items[0].metadata.name}') -f

5、安装可插拔功能组件
kubectl edit cc -n kubesphere-system ks-installer

kubectl edit cm -n kubesphere-system ks-installer【之前试了这个没用】
kubectl edit cm -n kubesphere-system  ks-console-config
6、设置污点
kubectl taint nodes k8s-node1 node-role.kubernetes.io/master=:NoSchedule

1600340780958

这是下载到本地的日志:
1、关闭应用商店
2、开启DevOps系统【Jenkins一站式部署】
3、开启KubeSphere日志系统
4、关闭Service Mesh微服务治理功能【熔断、限流、链路追踪】【前提是要开启日志】
5、开启KubeSphere告警通知系统【】
6、安装Metrics-Server开启HPA【监控CPU使用率 弹性伸缩】
kubesphere-minimal.yaml

---
apiVersion: v1
kind: Namespace
metadata:
  name: kubesphere-system

---
apiVersion: v1
data:
  ks-config.yaml: |
    ---

    persistence:
      storageClass: ""

    etcd:
      monitoring: False
      endpointIps: 192.168.0.7,192.168.0.8,192.168.0.9
      port: 2379
      tlsEnable: True

    common:
      mysqlVolumeSize: 20Gi
      minioVolumeSize: 20Gi
      etcdVolumeSize: 20Gi
      openldapVolumeSize: 2Gi
      redisVolumSize: 2Gi

    metrics_server:
      enabled: False

    console:
      enableMultiLogin: False  # enable/disable multi login
      port: 30880

    monitoring:
      prometheusReplicas: 1
      prometheusMemoryRequest: 400Mi
      prometheusVolumeSize: 20Gi
      grafana:
        enabled: False

    logging:
      enabled: False
      elasticsearchMasterReplicas: 1
      elasticsearchDataReplicas: 1
      logsidecarReplicas: 2
      elasticsearchMasterVolumeSize: 4Gi
      elasticsearchDataVolumeSize: 20Gi
      logMaxAge: 7
      elkPrefix: logstash
      containersLogMountedPath: ""
      kibana:
        enabled: False

    openpitrix:
      enabled: False

    devops:
      enabled: True
      jenkinsMemoryLim: 2Gi
      jenkinsMemoryReq: 1500Mi
      jenkinsVolumeSize: 8Gi
      jenkinsJavaOpts_Xms: 512m
      jenkinsJavaOpts_Xmx: 512m
      jenkinsJavaOpts_MaxRAM: 2g
      sonarqube:
        enabled: True
        postgresqlVolumeSize: 8Gi

    servicemesh:
      enabled: False

    notification:
      enabled: True

    alerting:
      enabled: True

kind: ConfigMap
metadata:
  name: ks-installer
  namespace: kubesphere-system

---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: ks-installer
  namespace: kubesphere-system

---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  creationTimestamp: null
  name: ks-installer
rules:
- apiGroups:
  - ""
  resources:
  - '*'
  verbs:
  - '*'
- apiGroups:
  - apps
  resources:
  - '*'
  verbs:
  - '*'
- apiGroups:
  - extensions
  resources:
  - '*'
  verbs:
  - '*'
- apiGroups:
  - batch
  resources:
  - '*'
  verbs:
  - '*'
- apiGroups:
  - rbac.authorization.k8s.io
  resources:
  - '*'
  verbs:
  - '*'
- apiGroups:
  - apiregistration.k8s.io
  resources:
  - '*'
  verbs:
  - '*'
- apiGroups:
  - apiextensions.k8s.io
  resources:
  - '*'
  verbs:
  - '*'
- apiGroups:
  - tenant.kubesphere.io
  resources:
  - '*'
  verbs:
  - '*'
- apiGroups:
  - certificates.k8s.io
  resources:
  - '*'
  verbs:
  - '*'
- apiGroups:
  - devops.kubesphere.io
  resources:
  - '*'
  verbs:
  - '*'
- apiGroups:
  - monitoring.coreos.com
  resources:
  - '*'
  verbs:
  - '*'
- apiGroups:
  - logging.kubesphere.io
  resources:
  - '*'
  verbs:
  - '*'
- apiGroups:
  - jaegertracing.io
  resources:
  - '*'
  verbs:
  - '*'
- apiGroups:
  - storage.k8s.io
  resources:
  - '*'
  verbs:
  - '*'
- apiGroups:
  - admissionregistration.k8s.io
  resources:
  - '*'
  verbs:
  - '*'

---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: ks-installer
subjects:
- kind: ServiceAccount
  name: ks-installer
  namespace: kubesphere-system
roleRef:
  kind: ClusterRole
  name: ks-installer
  apiGroup: rbac.authorization.k8s.io

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: ks-installer
  namespace: kubesphere-system
  labels:
    app: ks-install
spec:
  replicas: 1
  selector:
    matchLabels:
      app: ks-install
  template:
    metadata:
      labels:
        app: ks-install
    spec:
      serviceAccountName: ks-installer
      containers:
      - name: installer
        image: kubesphere/ks-installer:v2.1.1
        imagePullPolicy: "Always"

5、 KubeSphere v3.0.0对接SonarQube

https://kubesphere.com.cn/forum/d/2044-kubesphere-v3-0-0-sonarqube

1、不同版本语句不同,具体使用 --help查看
helm upgrade --install sonarqube sonarqube --repo https://charts.kubesphere.io/main --namespace kubesphere-devops-system --set service.type=NodePort

helm upgrade --install sonarqube sonarqube --repo https://charts.kubesphere.io/main -n kubesphere-devops-system  --create-namespace --set service.type=NodePort

2、根据提示获取sonarqube登录信息
  export NODE_PORT=$(kubectl get --namespace kubesphere-devops-system -o jsonpath="{.spec.ports[0].nodePort}" services sonarqube-sonarqube)
  export NODE_IP=$(kubectl get nodes --namespace kubesphere-devops-system -o jsonpath="{.items[0].status.addresses[0].address}")
  echo http://$NODE_IP:$NODE_PORT
 
3、获取sonarqube token
登录sonarqube
user: admin
password: admin

My Account –> Security –> Tokens
填入token名称【gulimall-analyze】,点击Generate创建token
记录token值,备用【后面凭证那块需要】

83826e0a0371fadd0d76e5c8dfbd8a2995d6ef09

4、选择continue按钮,但是没找着
要选择java项目、maven构建

1600583506178

6、多租户管理快速入门

中文官方文档说明:https://v2-1.docs.kubesphere.io/docs/zh-CN/quick-start/admin-quick-start/
平台资源分为三级:集群 (Cluster)、 企业空间 (Workspace)、 项目 (Project) 和 DevOps Project (DevOps 工程)
	集群 (Cluster):Node、service这些都是集群资源【不可以所有人能访问】
	企业空间 (Workspace):分为不同的企业空间
	项目 (Project) 和 DevOps Project (DevOps 工程):部署在企业空间上的项目【所以需要自己的企业空间】

1600493263143

内置角色:
platform-admin				平台管理员,可以管理平台内的所有资源。【admin账户】
只分配以下三个角色:
users-manager				平台用户管理员,管理平台所有用户。【只能创建用户】
workspaces-manager			平台企业空间管理员,管理平台所有企业空间。【只能创建企业空间】
platform-regular			平台普通用户,在被邀请加入企业空间或集群之前没有任何资源操作权限。【有了这个角色就可以使用资源了】

1、创建角色管理用户
	1)创建角色
		3.0有默认的users-manager角色所以这里省略了这一步
	2)创建用户
		用户名:atguigu-hr
		邮箱:atguigu-hr@163.com
		角色:users-manager
		密码:Wan123
2、登录atguigu-hr创建用户
	1)创建工作空间管理员ws-manager,role:workspaces-manager
		作用:创建企业空间+分配企业空间管理员:ws-admin
	2)创建指定企业空间管理员ws-admin,role:platform-regular
		作用:邀请企业空间成员+分配成员权限
	3)创建项目管理员project-admin,role:platform-regular
		作用:创建项目+邀请项目成员+分配成员权限
	4)创建普通项目成员project-regular,role:platform-regular
		作用:创建部署、密钥、存储pvc

3、登录ws-manager创建企业空间
	1)创建企业空间gulimall-workspace
	2)指派管理员ws-admin
	
4、登录ws-admin邀请企业成员
	1)project-admin:可以创建项目	 role:gulimall-workspace-self-provisioner
	2)project-regular:只能查看项目 role:gulimall-workspace-viewer
	
5、登录project-admin创建项目【测试】
	测试1)创建资源型项目gulimall,并邀请project-regular为该项目的开发人员
	测试2)创建DevOps项目

创建企业空间:

1600506501483

登录ws-admin邀请企业成员:

1600507256731

7、创建 Wordpress 应用并发布到k8s【普通的部署:应用】

参照文档:https://v2-1.docs.kubesphere.io/docs/zh-CN/quick-start/wordpress-deployment/
下面就不记笔记了

1、直接使用docker部署。
	1)拉取好mysql镜像,然后结合Wordpress部署连接mysql就可以启动应用了
	
2、使用kubernetes部署
	1)前端先创建Wordpress的deployment,部署就会下载镜像生成pod,然后创建service对外暴露【service的好处就是跟集群kubernetes其他节点网络是互通的(负载)】
	2)后端创建mysql的deployment,生成pod,创建service对外暴露
	3)secret:密钥,不是明文传输,当需要使用的时候直接引入
	4)PV:节点的网络存储,当一个存储请求过来了,会绑定该请求进行存储
	5)pvc(persistent Volume Claim):存储请求【前端Wordpress的静态资源,mysql对应的磁盘文件】

1600508732458

1600508566712

1)创建 企业空间、项目、应用

1、登录ws-manager创建企业空间wordpress,指派ws-admin作为该企业空间的管理员【gulimall-workspace-admin】

2、登录ws-admin邀请人员进入该企业空间,指定project-admin为项目管理者【可以创建项目gulimall-workspace-self-provisioner】,project-regular【不可以创建项目gulimall-workspace-viewer】

3、登录project-admin创建wordpress项目,指定人员project-regular为开发人员

4、登录project-regular进入项目,创建应用

2)创建密钥

1、创建mysql 密钥
也可以创建yaml文件,然后kubectl apply -f xxx.yaml

kind: Secret
apiVersion: v1
metadata:
  name: mysql-secret
  namespace: wordpress-project
  annotations:
    kubesphere.io/alias-name: MySQL 密钥
    kubesphere.io/creator: project-regular
    kubesphere.io/description: MySQL 初始密码 123456
data:
  MYSQL_ROOT_PASSWORD: MTIzNDU2
type: Opaque





2、创建WordPress 密钥



3)创建存储卷

创建两个存储卷,wordpress和mysql各一个
wordpress-pvc
mysql-pvc

1600511962914

4)创建应用

1600512817453

mysql镜像环境变量关联之前配置的root密钥

1600512961518

挂载存储卷:
docker run name 5ome-mysqlv /my/oun/datadir:/var/lib/mysql -e WSQl_ROOT_PASSMORD=mly-secret-pw -d mysql:tag

以前会将/var/lib/mysql容器内部资源挂载到本地linux系统下,同样适用kubernetes部署也要将/var/lib/mysql这个目录挂载到pvc里面

5)查看应用资源

6)访问 Wordpress

三、DevOps

1、设么是DevOps

可持续部署、持续集成
DEV:怎么开发?【高并发:怎么承担高并发】
OPS:怎么部署?【高可用:怎么做到高可用】

DevOps是 开发、测试、运维 三者叠加 都需要了解的内容【了解对方的通电难点,合理设计】
DevOps的作用:软件产品交付过程中IT工具链的打通,使各个团队减少时间损耗、更高效地协同工作

1600521932785

2、CI和CD

1600522213101

1)持续集成(Continuous Integration)

集成:将所有代码打包继承到整个项目中,然后测试
而持续集成需要具备的条件是以下5条
	
	1、持续集成是指软件个人研发的部分向软件整体部分交付,频繁进行集成以便更快地发现其中的错误。"持续集成"源自于极限编程(XP),是XP最初的12种实践之一。
	2、CI需要具备这些:
		1)全面的自动化测试。这是实践持续集成&持续部署的基础,同时,选择合适的自动化测试工具也极其重要;
		2)灵活的基础设施。容器,虚拟机的存在让开发人员和QA人员不必再大费周折;
		3)版本控制工具。如 Git.CVS,SVN等;
		4)自动化的构建和软件发布流程的工具,如Jenkins.flow.ci;
		5)反馈机制。如构建/测试的失败,可以快速地反馈到相关负责人,以尽快解决达到一个更稳定的版本。

2)持续交付(Continuous Delivery)

交付:将测试通过的代码交付到 类生产环境

	持续交付在持续集成的基础上,将集成后的代码部署到更贴近真实运行环境的「类生产环境」(production-like environments)中。持续交付优先于整个产品生命周期的软件部署,建立在高水平自动化持续集成之上。
【灰度发布。】
持续交付和持续集成的优点非常相似:
	1、快速发布。能够应对业务需求,并更快地实现软件价值。
	2、编码->测试->上线->交付的频繁迭代周期缩短,同时获得迅速反馈;
	3、高质量的软件发布标准。整个交付过程标准化、可重复、可靠.
	4、整个交付过程进度可视化,方便团队人员了解项目成熟度;
	5、更先进的团队协作方式。从需求分析、产品的用户体验到交互设计、开发、测试、运维等角色密切协作,相比于传统的瀑布式软件团队,更少浪费。

3)持续部署(Continuous Deployment)

	持续部署是指当交付的代码通过评审之后,白动部署到生产环境中。持续部署是持续交付的最高阶段。这意味着,所有通过了一系列的自动化测试的改动都将自动部署到生产环境。它也可以被称为"Continuous Release"。
	
		“开发人员提交代码,持续集成服务器获取代码,执行单元测试,根据测
		试结果决定是否部署到预演环境,如果成功部署到预演环境,进行整体
		验收测试,如果测试通过,自动部署到产品环境,全程自动化高效运转。”
	
	【持续部署主要好处是,可以相对独立地部署新的功能,并能快速地收集真实用户的反馈。】
		"You build ityou run it,这是Amazon一年可以完成 5000万次部署,
		平均每个工程师每天部署超过50次的核心秘籍。

Jams Bowman绘制的持续交付工具链图 P357

1600523044067

3、流水线流程

1、checkout scm:从github拉取代码
2、unit test:单元测试
3、sonarqube analysis:代码审查
4、build and push snapshot:构建镜像推送到docker hub中【登录docker hub查看tag】
5、push the latest image:推送最新镜像【docker hub最新镜像被覆盖】
6、deploy to dev:手动确认是否部署到开发环境【namespace一样的项目】
7、push the tag:手动确认release【候选】代码是否推送到github的tag上【git tag -a v0.0.2 -m v0.0.2】同时0.0.2也会推送到docker hub上,所以可能会存在两份一样的
8、deploy to production:是否部署到生产环境【namespace一样的项目】

1、开发环境项目,从github拉取版本创建容器部署
2、生产环境项目,从github拉取版本创建容器部署

1600524033548

1600523932369

4、创建流水线【demo】

1)创建凭证+fork项目+修改Jenkinsfile

https://v2-1.docs.kubesphere.io/docs/zh-CN/quick-start/devops-online/
https://www.kubernetes.org.cn/5826.html
需要一个Jenkinsfile ,记录三个凭证
	DockerHub、GitHub 和 kubeconfig (kubeconfig 用于访问接入正在运行的 Kubernetes 集群)
	
1、创建凭证【四个凭证,还有之前记录的SonarQube的token】
	dockerhub-id:账户凭证
	github-id:账户凭证
	demo-kubeconfig:kubeconfig
	【代码审查】sonar-qube:秘密文本,密钥填入token值
	这个是内置的:https://v2-1.docs.kubesphere.io/docs/zh-CN/devops/sonarqube
	访问路径:kubectl get all -n wordpress-project
	我使用的是3.0版本自己下载的pod【查看之前的章节】
	
2、将项目fork到自己的仓库
https://github.com/kubesphere/devops-java-sample

3、修改Jenkinsfile-online
    environment {
        DOCKER_CREDENTIAL_ID = 'dockerhub-id'
        GITHUB_CREDENTIAL_ID = 'github-id'
        KUBECONFIG_CREDENTIAL_ID = 'demo-kubeconfig'
        REGISTRY = 'docker.io'
        DOCKERHUB_NAMESPACE = 'wanzenghui'
        GITHUB_ACCOUNT = 'wanzenghui'
        APP_NAME = 'devops-java-sample'
        SONAR_CREDENTIAL_ID = 'sonar-qube'
    }

4、删除Jenkinsfile-online中maven的-o

1600525306457

1600573773919

2)创建 DevOps工程、流水线

其实 项目=DevOps工程,应用=流水线

1、找到devops-java-sample/deploy/dev/devops-sample-svc.yaml,看任意一个yaml里面部署的名称空间,这个demo有两个项目两个名称空间,kubesphere-sample-dev和 kubesphere-sample-prod

	1)登录project-admin,创建两个资源型项目【kubesphere-sample-dev和 kubesphere-sample-prod】并邀请成员project-regular作为operator
	2)登录project-admin,创建一个DevOps工程,邀请成员project-regular作为operator
	
2、创建流水线
	1)登录project-regular,进入DevOps工程,创建一个流水线demo-cicd
	2)选择一个代码仓库github=》获取token,然后选择自己的项目。下一步
	3)丢弃分支,使用默认值
	4)指定路径,jenkinsfile-online
	5)设置拉取时间5min,和webhook回调【在github设置】
	
3、创建完毕,开始拉取项目、依赖、构建、单元测试
	流水线创建完成后会创建两个活动,因为有两个分支master+dependency
	1)此时会弹出异常,原因:拉取jar包失败
		点开活动 master,查看日志:从repo.spring.io/spring-boot-starter-parent下载失败,应该从maven仓库去下载
	2)解决bug,停止master活动
	修改代码pom.xml中spring-boot-starter-parent的版本【去spring.io查看springboot的版本】,删除自定义的maven仓库<repositories>
	然后运行流水线,tag_name设置v0.0.2

登录project-admin创建两个项目

1600583974027

设置webhook回调:

1600585436057

1600585373321

四、集群【docker与k8s集群部署】

集群的目标

一、数据备份容灾:主机故障后,存储的数据仍然可以在别的地方拉起【主从复制】
二、压力分担:避免单点压力过高,分离操作,各单点完成自己的工作完成整个工作【读写分离】
三、高可用:主机宕机对业务无影响。宕机原因:网卡、路由器、机房、CPU负载过高、内存溢出、自然灾害等不可预期的原因导致,也称单点问题【双主、raft选举】
四、单点性能限制:当单点数据量过大导致性能降低,所以需要多态分担共同存储整个集群数据,并且做好互相备份保证即使单点故障,也能在其他节点找到数据【分片】

集群的基础形式

一、主从式
	1、主从复制:同步方式【从节点复制主节点的数据保证一样,从机读,主机写】
		优点:压力分担
		demo:innodb cluster
	2、主从调度:控制方式
		缺点:主机宕机没有调度了
		demo:k8s,master节点调度选择一个Node服务
	
二、分片式【客户端调度,代码逻辑判断调度的节点而不是服务端调度,所以没有主机宕机危险】
	1、数据分片存储,片区之间备份。
		优点:突破数据量限制
	demo:sharding proxy【还实现了读写分离】

三、选主式:raft算法
	为了容灾选主:
	为了调度选主:选出一个主节点作为调度【k8s】
	
	demo:redis的哨兵

1600587951969

1、mysql集群【主从复制】

原理与实现:https://segmentfault.com/a/1190000008942618
主+从

1)集群方案【原理】

1、双主复制+主从复制+读写分离:高可用、容灾、压力分担

2、分片:突破数据量限制【就是多套 方案1,各自范围内,由一个节点控制最终请求到达哪套 主从节点】

1600590072553

# 候选主节点

1.1、双主复制-MMM
主主复制管理器
1、两个主机master1,master2,其中master1作为write vip,两个主机 数据复制。当第一个主机宕机,第二个主机晋升为主机,切客户端的使用的mysql写ip不用改变,monitor会将写ip的地址转换给master2【ip飘移】
2、master2和slave作为读
3、monitor:监控器,监控IP集。
4、VIP集:虚拟ip集,每个结点都有自己的IP。

优点:高可用问题
缺点:数据丢失【例如master1宕机还没来得及复制的数据】

1600590959582

1.2、从节点替补-MHA
MHA,太慢了,使用从节点作为替补主节点

1600591934243

1.3、InnoDB Cluster

1600592086139

MySQL Router:调度节点,如果Primary宕机,选择一个作为主节点

1600592392335

2)集群实现

# 实现主从复制【InnoDB Cluster】

InnoDB Cluster有单主模式、双主模式

docker 安装模拟Mysql 主从复制 集群
1、下载mysql镜像

2、创建Master实例并启动,指定密码为root
docker run -p 3307:3306 --name mysql-master \
-v /mydata/mysql/master/conf:/etc/mysql \
-v /mydata/mysql/master/log:/var/log/mysql \
-v /mydata/mysql/master/data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=root \
-d mysql:5.7

参数说明
-p 3307:3306:将容器的3306端口映射到主机的3307端口
-v/mydata/mysal/master/conf:/etc/mysql:将配置文件夹挂在到主机
-v/mydata/mysal/master/log:/var/log/mysal:将日志文件夹挂载到主机
-v/mydata/mysal/master/data:/ar/lib/mysal:将配置文件夹挂载到主机
-e MYSQL_ROOT_PASSWORD=root:初始化root用户的密码

3、创建slave实例并启动
docker run -p 3317:3306 --name mysql-slaver-01 \
-v /mydata/mysql/slaver/conf:/etc/mysql \
-v /mydata/mysql/slaver/log:/var/log/mysql \
-v /mydata/mysql/slaver/data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=root \
-d mysql:5.7

4、修改master配置
vi /mydata/mysql/master/conf/my.cnf
注意:skip-name-resolve一定要加,不然连接mysql会很慢

[client]
default-character-set=utf8

[mysql]
default-character-set=utf8

[mysqld]
init_connect='SET collation_connection = utf8_unicode_ci'
init_connect='SET NAMES utf8'
character-set-server=utf8
collation-server=utf8_unicode_ci
skip-character-set-client-handshake
skip-name-resolve

server-id=1
log-bin=mysql-bin
read-only=0
binlog-do-db=gulimall_oms
binlog-do-db=gulimall_pms
binlog-do-db=gulimall_sms
binlog-do-db=gulimall_ums
binlog-do-db=gulimall_wms
binlog-do-db=gulimall_admin

replicate-ignore-db=mysql
replicate-ignore-db=sys
replicate-ignore-db=performance_schema
replicate-ignore-db=information_schema

5、修改salver配置
vi /mydata/mysql/slaver/conf/my.cnf
注意:skip-name-resolve一定要加,不然连接mysql会很慢

[client]
default-character-set=utf8

[mysql]
default-character-set=utf8

[mysqld]
init_connect='SET collation_connection = utf8_unicode_ci'
init_connect='SET NAMES utf8'
character-set-server=utf8
collation-server=utf8_unicode_ci
skip-character-set-client-handshake
skip-name-resolve

server-id=2
log-bin=mysql-bin
read-only=1
binlog-do-db=gulimall_oms
binlog-do-db=gulimall_pms
binlog-do-db=gulimall_sms
binlog-do-db=gulimall_ums
binlog-do-db=gulimall_wms
binlog-do-db=gulimall_admin

replicate-ignore-db=mysql
replicate-ignore-db=sys
replicate-ignore-db=performance_schema
replicate-ignore-db=information_schema

6、重启:docker restart mysql-master mysql-slaver-01
为master授权用户来他的同步数据
1、进入master容器
docker exec -it mysql-master /bin/bash
2、mysql -uroot -p root

	1)授权root可以远程访问(主从无关,方便我们可以远程链接mysql)
	grant all privileges on *.* to 'root'@'%' IDENTIFIED BY 'root' with grant option;
	flush privileges;

	2)添加同步用户,链接master数据库,在master授权一个 复制权限的 用户
	GRANT REPLICATION SLAVE ON *.* TO 'backup'@'%' IDENTIFIED BY '123456';

3、查看master状态,记录File名字:mysql-bin.000001,后面会用到
show master status;

1600596024636

配置slaver同步master数据
1、进入slaver容器
docker exec -it mysql-slaver-01 /bin/bash
2、mysql -uroot -p root

	1)授权root可以远程访问(主从无关,方便我们可以远程链接mysql)
	grant all privileges on *.* to 'root'@'%' IDENTIFIED BY 'root' with grant option;
	flush privileges;
	2)设置主库连接
	change master to master_host='192.168.56.10',master_user='backup',master_password='123456',master_log_file='mysql-bin.000001',master_log_pos=0,master_port=3307;
	
3、启动从库同步
start slave;

4、查看从库状态
show slave status;

2、mysql 分库分表+读写分离

1)原理

doc:https://shardingsphere.apache.org/document/current/cn/overview/

1、为什么要出现分库分表?
	答:1)缓解单库单表的压力【单表性能问题】
2、设么是分库分表?
	答:当前demo只体现了分表,放在了同一个数据库内【分库、分表逻辑可以同时出现,先根据规则分库,再分表】
	
3、使用InnoDB Cluster分库分表【不采用】
	双主模式下,两个主机都设置步长为2
	主1设置id自增,从1开始
	主2设置id自增,从2开始

4、shardingSphere有三种实现方式
	1)使用sharding-jdbc,引入sharding-jdbc【驱动】
		微服务设置分库分表策略,调用驱动保存数据时计算好 具体保存在哪个库。
	2)使用sharding-proxy,作为代理【看做数据库,不用修改源代码】
		使用中年间sharding-proxy
	3)sharding-sidecar,可以kubernetes中使用,但是还没做好

5、业务原理+demo
	例如订单表
		1、分库:不同位置的订单放在不同数据库
		2、分表:按照时间分表

1600756225245

1600756488828

2)实现分库分表+读写分离

mycat与ShardingSphere比较:
https://my.oschina.net/u/4318872/blog/4281049

# mycat

# ShardingSphere-proxy

以下是2主2从的示意图:

1600756588874

doc:https://shardingsphere.apache.org/document/current/cn/overview/
配置文档:https://blog.csdn.net/qq_44826685/article/details/106190720
下载mysql:https://repo1.maven.org/maven2/mysql/mysql-connector-java/5.1.47/mysql-connector-java-5.1.47.jar

示意图中有多种实现方式:
	1、两个主机,两个从机。主机1数据库demo_db0,主机2demo_db1【4个容器,2个主机容器】
	2、1个主机1个从机,主机两个数据库【现在采用这个】【2个容器 】
	
实现:【参照doc】
1、下载中间件+解压
	下载mysql依赖,放到中间件解压的lib文件夹【在刚刚那个网站里面有下载地址】

=========================================================================================
2、配置认证信息+属性配置 server.yaml
authentication:
  users:
    root:
      password: root
    sharding:
      password: sharding 
      authorizedSchemas: sharding_db
props:
  executor.size: 16  # Infinite by default.
  sql.show: true

=========================================================================================
3、分库分表+读写分离 相关配置
	1)分库分表:数据分片【两个库在同一个主机上】 config-sharding.yaml
		一:分库分表是相对于写的概念,所以是主库。下面两个数据源都是配的主库【可以在不同节点,当前demo放在同一个节点下(同一容器),在同一个容器中创建了两个数据库】
		二:微服务连上sharding_db这个中间件而不是直接连接数据库
		三:主键采用雪花算法,不可以自增
        四:绑定表订单表和订单表项的关系,不需要跨库联查
  		
  		五:根据user_id分库;2、根据order_id分表
schemaName: sharding_db
#
dataSources:
  ds_0:
    url: jdbc:mysql://192.168.56.10:3307/demo_ds_0?serverTimezone=UTC&useSSL=false
    username: root
    password: root
    connectionTimeoutMilliseconds: 30000
    idleTimeoutMilliseconds: 60000
    maxLifetimeMilliseconds: 1800000
    maxPoolSize: 50
  ds_1:
    url: jdbc:mysql://192.168.56.10:3307/demo_ds_1?serverTimezone=UTC&useSSL=false
    username: root
    password: root
    connectionTimeoutMilliseconds: 30000
    idleTimeoutMilliseconds: 60000
    maxLifetimeMilliseconds: 1800000
    maxPoolSize: 50
    
    2)读写分离config-master_slave.yaml
        一:主从复制是InnoDB Cluster中配置【my.cnf】,读写分离是在sharding-proxy配置
		二:有几套分库分表,就配置几套读写分离
创建两个文件
############################config-master_slave.yaml############################
schemaName: sharding_db_0
#
dataSources:
  master_0_ds:
    url: jdbc:mysql://192.168.56.10:3307/demo_ds_0?serverTimezone=UTC&useSSL=false
    username: root
    password: root
    connectionTimeoutMilliseconds: 30000
    idleTimeoutMilliseconds: 60000
    maxLifetimeMilliseconds: 1800000
    maxPoolSize: 50
  slave_ds_0:
    url: jdbc:mysql://192.168.56.10:3317/demo_ds_0?serverTimezone=UTC&useSSL=false
    username: root
    password: root
    connectionTimeoutMilliseconds: 30000
    idleTimeoutMilliseconds: 60000
    maxLifetimeMilliseconds: 1800000
    maxPoolSize: 50
masterSlaveRule:
  name: ms_ds0
  masterDataSourceName: master_0_ds
  slaveDataSourceNames:
    - slave_ds_0
  loadBalanceAlgorithmType: ROUND_ROBIN
  
############################config-master_slave1.yaml############################
schemaName: sharding_db_1
#
dataSources:
  master_1_ds:
    url: jdbc:mysql://192.168.56.10:3307/demo_ds_1?serverTimezone=UTC&useSSL=false
    username: root
    password: root
    connectionTimeoutMilliseconds: 30000
    idleTimeoutMilliseconds: 60000
    maxLifetimeMilliseconds: 1800000
    maxPoolSize: 50
  slave_ds_1:
    url: jdbc:mysql://192.168.56.10:3317/demo_ds_1?serverTimezone=UTC&useSSL=false
    username: root
    password: root
    connectionTimeoutMilliseconds: 30000
    idleTimeoutMilliseconds: 60000
    maxLifetimeMilliseconds: 1800000
    maxPoolSize: 50
masterSlaveRule:
  name: ms_ds1
  masterDataSourceName: master_1_ds
  slaveDataSourceNames:
    - slave_ds_1
  loadBalanceAlgorithmType: ROUND_ROBIN

=========================================================================================
4、创建相关容器+数据库
	一:容器之前创建好了,参照集群实现
	1)docker stop mysql-master mysql-slaver-01
	2)修改配置文件
主库配置,需要同步的两个主库【分库分表的主库】
vi /mydata/mysql/master/conf/my.cnf
添加:
binlog-do-db=demo_ds_0
binlog-do-db=demo_ds_1

从库配置,需要同步的两个主库
vi /mydata/mysql/slaver/conf/my.cnf

添加:
binlog-do-db=demo_ds_0
binlog-do-db=demo_ds_1

=========================================================================================
5、docker start mysql-master mysql-slaver-01
创建数据库demo_ds_0和demo_ds_1

=========================================================================================
6、启动sharding-proxy中间件
	直接在文件夹目录输入cmd就可以在当前目录打开一个cmd窗口
	start.bat 3388

=========================================================================================
7、navicat新建连接,127.0.0.1:3388【这里使用navicat11,否则不会显示代理库】

=========================================================================================
8、创建测试表
在proxy代理库端创建表 t_order和t_order_item表后proxy会创建以下表
	在demo_ds_0创建t_order0、t_order1、t_order_item0、t_order_item1
	在demo_ds_1创建t_order0、t_order1、t_order_item0、t_order_item1

解释:
	主机192.168.56.10:3307,两个从机在同一容器中,但是t_order0和表 t_order1在两个主库都会创建
	主库1:demo_ds_0:表t_order0有数据,t_order1数据为空【写】
	主库2:demo_ds_1:表t_order1有数据,t_order0数据为空【写】

	从机192.168.56.10:3317,两个从机在同一容器中
	从库1:demo_ds_0:表t_order0有数据,t_order1数据为空【读】
	从库2:demo_ds_1:表t_order1有数据,t_order0数据为空【读】

1600764985350

3、redis集群

1)集群方案【原理】

# 高可用+分片

1.1、客户端分区

1600767319377

客户端Jedis直接堆key作hash或求余获得指定节点存储

客户端分区方案的代表为Redis Sharding,Redis Sharding是Redis Cluster出来之前,业界普遍使用的Redis多实例集群方法。Java的Redis客户端驱动库Jedis,支持 RedisSharding功能,即ShardedJedis,以及结合缓存池的ShardedJedisPool。

优点
不使用第三方中间件,分区逻辑可控,配置简单,节点之间无关联,容易线性扩展,灵活性强。

缺点
客户端无法动态增删服务节点,客户端需要自行维护分发逻辑,客户端之间无连接共享,会造成连接浪费。
不是高可用,redis某节点宕机
1.2、代理分区
与sharding proxy类似
代理分区常用方案有Twemproxy和Codis

1600767541430

1.3、哨兵机制【高可用】
redis-cluster未出现之前,使用哨兵机制

监控(Monitoring):哨兵(sentinel)会不断地检查你的Master和Slave是否运作正常。
提醒(Notification):当被监控的某个Redis出现问题时,哨兵(sentinel)可以通过 API向管理员或者其他应用程序发送通知。
自动故障迁移(Automatic failover)):当主数据库出现故障时自动将从数据库转换为主数据库。【投票选举】

哨兵的原理
Redis,哨兵的三个定时任务,Redis,哨兵判定一个Redis.节点故障不可达主要就是通过三个定时监控任务来完成的:
	1、每隔1秒每个哨兵会向主节点、从节点、其他的哨兵节点发送一个“ping”命令来做心跳检测
	2、每隔2秒每个哨兵节点会向Redis节点的_sentinel_hello频道发送自己对主节点是否故障的判断以及自身的节点信息,并且其他的哨兵节点也会订阅这个频道来了解其他哨兵节点的信息以及对主节点的判断
	3、每隔10秒每个哨兵节点会向主节点和从节点发送"info replication”命令来获取最新的拓扑结构
	
    如果在定时Job1检测不到节点的心跳,会判断为"主观下线"。如果该节点还是主节点那么还会通知到其他的哨兵对该主节点进行y心跳检测,这时主观下线的票数超过了<quorum>数时,那么这个主节点确实就可能是故障不可达了,这时就由原来的主观下线变为了“客观下线"。

	故障转移和Leader选举
如果主节点被判定为客观下线之后,就要选取一个哨兵节点来完成后面的故障转移工作,选举出一个leader,这里面采用的选举算法为Raft。选举出来的哨兵leader就要来完成故障转移工作,也就是在从节点中选出一个节点来当新的主节点,这部分的具体流程可参考引用.

1600768592796

每隔十秒:

1600768684518

每隔1s:

1600768761469

1.4、redis-cluster【高可用+分片】
https://redis.io/topics/cluster-tutorial/
	Redis的官方多机部署方案,Redis Cluster。一组Redis Cluster是由多个Redis,实例组成,官方推荐我们使用6实例,其中3个为主节点,3个为从结点。一旦有主节点发生故障的时候,Redis Cluster可以选举出对应的从结点成为新的主节点继续对外服务,从而保证服务的高可用性。那么对于客户端来说,如何知道对应的key是要路由到哪一个节点呢?Redis Cluster把所有的数据划分为16384个不同的槽位,可以根据机器的性能把不同的槽位分配给不同的Redis实例,对于Redis实例来说,他们只会存储部分的Redis数据,当然,槽的数据是可以迁移的,不同的实例之间,可以通过一定的协议,进行数据迁移。
槽+重定向

1600832253052

1600832517935

0~16383:一共16384个槽位,对key使用JAVA CRC16校验算法,然后对16383求余,算出槽位。这是一个重定向过程,如果当前节点与对应槽位没有包含关系,则重定向发送至下一节点重新判断【当前槽位是否指向自身】

redis官方方法,然后哨兵机制就可以不使用了
优点:分片+高可用都实现【相当于mysql的分库分表】

缺点+需要预防的问题:
1、key批量操作支持有限。【不再同一槽位的批量操作不再同一节点,不支持】
	类似mset、mget 操作,目前只支持对具有相同slot值的key执行批量操作。对于映射为不同slot值的key由于执行mget、mget等操作可能存在于多个节点上,因此不被支持。
	
2、key事务操作支持有限。【使用lua脚本不使用redis事务】
	只支持多key在同一节点上的事务操作,当多个key 分布在不同的节点上时无法使用事务功能。|

3、key 作为数据分区的最小粒度
4、不能将一个大的键值对象如 hash、list等映射到不同的节点。
5、不支持多数据库空间
	单机下的 Redis可以支持16个数据库((db0~ db15),集群模式下只能使用一个数据库空间,即db0。
6、复制结构只支持一层【只有一层主从,没有更多】
	从节点只能复制主节点,不支持嵌套树状复制结构。
7、命令大多会重定向,耗时多

一致性hash

1600836994345

一个闭环,计算keyhash与哪一个节点最近。

Hash倾斜
如果节点很少,容易出现倾斜,负载不均衡问题。一致性哈希算法,引入了虚拟节点,在整个环上,均衡增加若干个节点。比如al, a2,b1,b2,c1,c2,a1和a2都是属于A节点的。解决hash倾斜问题

2)集群实现

# redis-cluster

1600915148452

数据分区
3主3从,从为了同步备份,主进行slot数据分片
1、高可用【主机宕机从机替代】
2、数据分片【槽机制】
3、容灾备份【主从备份】
4、单点读取压力分担【这里应该没有读写分离,分开读写操作,让读高效进行】

一、脚本:创建6份配置文件+启动6份redis
for port in $(seq 7001 7006);  \
do \
mkdir -p /mydata/redis/node-${port}/conf
touch /mydata/redis/node-${port}/conf/redis.conf
cat << EOF > /mydata/redis/node-${port}/conf/redis.conf
port ${port}
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
cluster-announce-ip 192.168.56.10
cluster-announce-port ${port}
cluster-announce-bus-port 1${port}
appendonly yes
EOF

docker run -p ${port}:${port} -p 1${port}:1${port} --name redis-${port}  \
-v /mydata/redis/node-${port}/data:/data \
-v /mydata/redis/node-${port}/conf/redis.conf:/etc/redis/redis.conf \
-d redis:5.0.7 redis-server /etc/redis/redis.conf; \
done
【这里去掉了--restart always内存不够 】

二、建立集群,进入一个master节点【每个节点1个副本】
然后从6个节点挑出3个主节点,然后给每个主节点挑一个从节点
docker exec -it redis-7001 /bin/bash 

redis-cli  --cluster create 192.168.56.10:7001 192.168.56.10:7002 192.168.56.10:7003 192.168.56.10:7004 192.168.56.10:7005 192.168.56.10:7006 --cluster-replicas 1

三、测试集群
1、连入集群,要加-c
redis-cli -c -h 192.168.56.10 -p 7001
2、设置一些值查看有什么不同【重定向到其他槽】
set hello 111
set a aaa

四、模拟宕机
1、使用redis客户端连接一个master和slaver,可以查看到数据拷贝现象
2、cluster info:查看当前集群状态
3、cluster nodes:查看集群节点信息
4、模拟宕机docker stop redis-7001
5、cluster info:7001宕机,7006从机晋升主机
6、docker start redis-7001:7001成为7006的从节点

3、Elasticsearch集群【天生集群】

https://www.elastic.co/guide/cn/elasticsearch/guide/current/index.html
https://www.elastic.co/guide/cn/elasticsearch/guide/current/distributed-cluster.html

elasticsearch是天生支持集群的,他不需要依赖其他的服务发现和注册的组件,如zookeeper这些,因为他内置了一个名字叫ZenDiscovery的模块,是elasticsearch,自己实现的一套用于节点发现和选主等功能的组件,所以elasticsearch做起集群来非常简单,不需要太多额外的配置和安装额外的第三方组件。

3.1、集群原理

1、单节点

1、主节点用于调控,数据是存在 数据节点上的
2、任何节点可以做主节点
3、客户端连上任意一个节点都可以完成操作

	一个运行中的Elasticsearch实例称为一个节点,而集群是由一个或者多个拥有相同cluster.name配置的节点组成,它们共同承担数据和负载的压力。当有节点加入集群中或者从集群中移除节点时,集群将会重新平均分布所有的数据。

	当一个节点被选举成为主节点时,它将负责管理集群范围内的所有变更,例如增加、删除索引,或者增加、删除节点等。而主节点并不需要涉及到文档级别的变更和搜索等操作,所以当集群只拥有一个主节点的情况下,即使流量的增加它也不会成为瓶颈。任何节点都可以成为主节点。我们的示例集群就只有一个节点,所以它同时也成为了主节点。

	作为用户,我们可以将请求发送到集群中的任何节点,包括主节点。每个节点都知道任意文档所处的位置,并且能够将我们的请求直接转发到存储我们所需文档的节点。无论我们将请求发送到哪个节点,它都能负责从各个包含我们所需文档的节点收集回数据.并将最终结果返回给客户端。Elasticsearch对这一切的管理都是透明的。

2、集群健康


get /_cluster/health

status字段指示着当前集群在总体上是否工作正常。它的三种颜色含义如下:
green:所有的主分片和副本分片都正常运行。
yellow:所有的主分片都正常运行,但不是所有的副本分片都正常运行。
red:有主分片没能正常运行。

3、分片

	1、一个分片是一个底层的工作单元﹐它仅保存了全部数据中的一部分。我们的文档被存储和索引到分片内,但是应用程序是直接与索引而不是与分片进行交互。分片就认为是一个数据区
	2、一个分片可以是主分片或者副本分片。索引内任意一个文档都归属于一个主分片,所以主分片的数目决定着索引能够保存的最大数据量。
	3、在索引建立的时候就已经确定了主分片数,但是副本分片数可以随时修改。
	4、让我们在包含一个空节点的集群内创建名为 blogs 的索引。索引在默认情况下会被分配5个主分片,但是为了演示目的.我们将分配3个主分片和一份副本(每个主分片拥有一个副本分片):

PUT /blogs{
	"settings" : {
		"number_of_shards" : 3,
		"number_of_replicas" : 1
	}
}
此时集群的健康状况为yellow则表示全部主分片都正常运行(集群可以正常服务所有请求),但是副本分片没有全部处在正常状态。实际上,所有3个副本分片都是unassigned—它们都没有被分配到任何节点。在同一个节点上既保存原始数据又保存副本是没有意义的,因为一旦失去了那个节点,我们也将丢失该节点上的所有副本数据。当前我们的集群是正常运行的,但是在硬件故障时有丢失数据的风险。


【个人理解,此时3个分片是在同一节点上
master节点与主分片不是一个意思,master节点只需要一个。主分片可能存在多个,并有可能存在于不同节点上】

4、新增节点

	当你在同一台机器上启动了第二个节点时,只要它和第一个节点有同样的cluster.name配置,它就会自动发现集群并加入到其中。但是在不同机器上启动节点的时候,为了加入到同一集群,你需要配置一个可连接到的单播主机列表。详细信息请查看最好使用单播代替组播

1600918511338

	此时,cluster-health 现在展示的状态为green,这表示所有6个分片(包括3个主分片和3个副本分片)都在正常运行。我们的集群现在不仅仅是正常运行的,并且还处于始终可用的状态。

5、重新分配

1600918664359

1、在第四步的基础上,继续水平扩容增加第三个节点,会重新自动分配

 	Node 1和 Node 2上各有一个分片被迁移到了新的Node 3节点,现在每个节点上都拥有2个分片,而不是之前的3个。这表示每个节点的硬件资源(CPU,RAM.VO)将被更少的分片所共享,每个分片的性能将会得到提升。在运行中的集群上是可以动态调整副本分片数目的,我们可以按需伸缩集群。让我们把副本数从默认的1增加到2

如果调整每个分片拥有两个从分片
PUT /blogs/_settings
{
	"number_of_replicas" : 2
}
会自动调整为以下状态:

1600918850118

1、如果此时Node1宕机,会先选举一个主节点 Node2,此时查看集群状态将是red,主分区P1、P2未正常工作
2、提升副本分区为主分区,集群状态变为yellow
3、为什么是yellow而不是green?因为每个主分区存在两个副本分区才是green

6、问题与解决-脑裂现象

1、主节点:
	创建索引、删除索引、分配分片、追踪集群中节点状态等工作【ZenDiscovery机制选举出来,要成为主节点先成为候选节点】
	
2、候选主节点:
	主节点宕机,选举一个候选主节点作为主节点。指定候选主节点的配置为 node.master:true
	
3、脑裂现象
	网路故障,出现两个集群两个主节点【集群中不同的节点对于master的选择出现了分歧,出现了多个master竞争,导致主分片和副本的识别也发生了分歧,对一些分歧中的分片标识为了坏片。】
	解决方案:
	1)角色分离:master与data节点分离,限制角色
		因为data节点如果既要负责master节点又要负责data节点,压力很大导致脑裂概率增加
	2)增加判定宕机时间【减少误判】:主节点判定宕机时间加长【默认3秒】
		discover.zen.ping_timeout: 5
	3)设置选举触发互连节点最小数目:discover.zen.minimum_master_nodes:1(默认是1),该属性定义的是为了形成一个集群,有主节点资格并互相连接的节点的最小数目。建议设置成 (候选主节点数/2)+1
		例:10个候选主节点互相连接,并且discover.zen.minimum_master_nodes:6,此时网络原因导致6个节点连接,其余4个节点连接,则其余4个节点不会形成一个新的集群。
		
4、数据节点
	node.data:true
	主节点和数据节点分开,主节点不存数据
	
5、客户端节点【分担主节点的请求分发、汇总(其实集群内任意节点都可以完成此任务)】
	功能:为了负载均衡
	node.master:false
	node.data:false

3.2、集群搭建-节点+分片

这张图画错了,一共6台物理机,一台1个节点

1600931042683

	要求:
		1、6台物理机6个节点,3个master节点3个data节点【角色分离,防止脑裂】
		2、判定时间设为10秒
		3、设置discover.zen.minimum_master_nodes=2,只要一个master宕机,另外两个就可以选举

master节点上
node.master = true
node.data = false
discovery.zen.minimum_master_nodes = 2
总结:只要集群名字是一样的,就会自动加入到同一个集群中
1、先修改jvm线程数,防止启动es报错
sysctl -w vm.max_map_count=262144

2、利用docker模拟6台物理机,创建每一个容器使用一个ip
准备docker网络:
	1)docker network ls:查看docker网络
	2)docker network create --driver bridge --subnet=172.18.12.0/16 --gateway=172.18.1.1 mynet
	3)docker network inspect mynet:查看网络消息
	4)创建容器带上 --network=mynet --ip 172.18.12.x指定ip
	
3、创建节点
创建主节点:
for port in $(seq 1 3); \
do \
mkdir -p /mydata/elasticsearch/master-${port}/config
mkdir -p /mydata/elasticsearch/master-${port}/data
chmod -R 777 /mydata/elasticsearch/master-${port}
cat << EOF > /mydata/elasticsearch/master-${port}/config/elasticsearch.yml
cluster.name: my-es  #集群的名称,同一个集群该值必须设置成相同的
node.name: es-master-${port}  #该节点的名字
node.master: true  #该节点有机会成为master节点
node.data: false #该节点可以存储数据
network.host: 0.0.0.0
http.host: 0.0.0.0   #所有http均可访问
http.port: 920${port}
transport.tcp.port: 930${port}
discovery.zen.ping_timeout: 10s #设置集群中自动发现其他节点时ping连接的超时时间
discovery.seed_hosts: ["172.19.1.21:9301","172.19.1.22:9302","172.19.1.23:9303"]
cluster.initial_master_nodes: ["172.19.1.21"] #新集群初始时的候选主节点,es7的新增配置
EOF
docker run --name elasticsearch-node-${port} \
-p 920${port}:920${port} -p 930${port}:930${port} \
--network=mynet --ip 172.19.1.2${port} \
-e ES_JAVA_OPTS="-Xms300m -Xmx300m"  \
-v /mydata/elasticsearch/master-${port}/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml  \
-v /mydata/elasticsearch/master-${port}/data:/usr/share/elasticsearch/data  \
-v /mydata/elasticsearch/master-${port}/plugins:/usr/share/elasticsearch/plugins  \
-d elasticsearch:7.4.2
done

=====================================
创建数据节点:
for port in $(seq 4 6); \
do \
mkdir -p /mydata/elasticsearch/master-${port}/config
mkdir -p /mydata/elasticsearch/master-${port}/data
chmod -R 777 /mydata/elasticsearch/master-${port}
cat << EOF > /mydata/elasticsearch/master-${port}/config/elasticsearch.yml
cluster.name: my-es  #集群的名称,同一个集群该值必须设置成相同的
node.name: es-node-${port}  #该节点的名字
node.master: false  #该节点有机会成为master节点
node.data: true #该节点可以存储数据
network.host: 0.0.0.0
http.host: 0.0.0.0   #所有http均可访问
http.port: 920${port}
transport.tcp.port: 930${port}
discovery.zen.ping_timeout: 10s #设置集群中自动发现其他节点时ping连接的超时时间
discovery.seed_hosts: ["172.19.1.21:9301","172.19.1.22:9302","172.19.1.23:9303"]
cluster.initial_master_nodes: ["172.19.1.21"] #新集群初始时的候选主节点,es7的新增配置
EOF
docker run --name elasticsearch-node-${port} \
-p 920${port}:920${port} -p 930${port}:930${port} \
--network=mynet --ip 172.19.1.2${port} \
-e ES_JAVA_OPTS="-Xms300m -Xmx300m"  \
-v /mydata/elasticsearch/master-${port}/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml  \
-v /mydata/elasticsearch/master-${port}/data:/usr/share/elasticsearch/data  \
-v /mydata/elasticsearch/master-${port}/plugins:/usr/share/elasticsearch/plugins  \
-d elasticsearch:7.4.2
done

4、访问查看集群相关信息指令:
192.168.56.10/_cluster/health:查看健康状况
192.168.56.10/_cluster/status
192.168.56.10/_cat/health
192.168.56.10/_cat/nodes:查看所有节点,带*是主节点

1600932480869

4、RabbitMQ集群

4.1、集群形式

	RabbiMQ是用Erlang开发的,集群非常方便,因为Erlang,天生就是一门分布式语言,但其本身并不支持负载均衡。需要nginx
	RabbitMQ集群中节点包括内存节点(RAM)、磁盘节点(Disk,消息持久化),集群中至少有一个Disk 节点。
	
	1、由磁盘节点和内存节点组成
	2、至少需要一个磁盘节点

1600934253707

1)普通模式

	1、普通模式中集群不会同步消息,只会同步queue、exchange
	
	demo:3个节点A\B\C组成的集群,消费节点C上的队列,如果此时消息在节点A的队列上,集群会将A的信息发送到C的队列上供于消费
	缺点:单点故障无法解决【高可用】
	
	对于普通模式,集群中各节点有相同的队列结构,但消息只会存在于集群中的一个节点。对于消费者来说,若消息进入A节点的Queue中,当从B节点拉取时,RabbitMQ会将消息从A中取出,并经过B发送给消费者。应用场景:该模式各适合于消息无需持久化的场合,如日志队列。当队列非持久化,且创建该队列的节点宕机,客户端才可以重连集群其他节点,并重新创建队列。若为持久化.只能等故障节点恢复。

2)镜像模式

	与普通模式不同之处是消息实体会主动在镜像节点间同步,而不是在取数据时临时拉取,高可用;该模式下,mirror queue有一套选举算法,即1个master、n个slaver,生产者、消费者的请求都会转至master。应用场景:可靠性要求较高场合,如下单、库存队列。缺点:若镜像队列过多,且消息体量大,集群内部网络带宽将会被此种同步通讯所消耗。
	优点:解决了高可用,当消息进入节点A的队列,会同步到B\C。当master A节点宕机,B、C会选择一个主节点
	1、镜像模式由主节点接收 生产者和消费者的请求【代理】
	2、镜像模式 依赖于 先搭建一个普通模式,再设置成镜像模式

4.2、搭建镜像集群

1、创建文件夹
mkdir /mydata/rabbitmq
cd /mydata/rabbitmq
mkdir rabbitmq01 rabbitmq02 rabbitmq03

2、启动3个rabbitmq
--hostname设置容器的主机名
RABBITMQ_ERLANG_COOKIE 节点认证作用,部署集成时需要同步该值

docker run -d --hostname rabbitmq01  --name rabbitmq01 \
-v /mydata/rabbitmq/rabbitmq01:/var/lib/rabbitmq \
-p 15673:15672 -p 5673:5672  \
-e RABBITMQ_ERLANG_COOKIE='dalianpai' rabbitmq:management

docker run -d --hostname rabbitmq02  --name rabbitmq02 \
-v /mydata/rabbitmq/rabbitmq02:/var/lib/rabbitmq \
-p 15674:15672 -p 5674:5672  \
-e RABBITMQ_ERLANG_COOKIE='dalianpai' rabbitmq:management

docker run -d --hostname rabbitmq03  --name rabbitmq03 \
-v /mydata/rabbitmq/rabbitmq03:/var/lib/rabbitmq \
-p 15675:15672 -p 5675:5672  \
-e RABBITMQ_ERLANG_COOKIE='dalianpai' rabbitmq:management


3、节点加入集群
	1)进入个节点完成初始化
	docker exec -it rabbitmq01 /bin/bash
	rabbitmqctl stop_app
	rabbitmqctl reset 【恢复出厂设置】
	rabbitmqctl start_app
	exit
	
	2)将节点2和3加入到集群
	docker exec -it rabbitmq02 /bin/bash
	rabbitmqctl stop_app
	rabbitmqctl reset
	rabbitmqctl join_cluster --ram rabbit@rabbitmq01
	rabbitmqctl start_app
	exit
	
	docker exec -it rabbitmq03 /bin/bash
	rabbitmqctl stop_app
	rabbitmqctl reset
	rabbitmqctl join_cluster --ram rabbit@rabbitmq01
	rabbitmqctl start_app
	exit

	3)访问192.168.56.10:15675查看集群
	随便访问一个就可以15673、15674、15675都可以

4、实现镜像集群
	1)随便进入一个容器
	docker exec -it rabbitmq01 /bin/bash
	设置一个策略,/:当前主机,策略名字是ha,^指的是当前所有主机都是高可用模式[^hello 指hello开头的所有主机],自动同步
	rabbitmqctl set_policy -p / ha "^" '{"ha-mode":"all","ha-sync-mode":"automatic"}'     
	
	exit
	rabbitmqctl  list_policies -p /:查看vhost/下面的所有policy

5、验证集群
	1、创建一个queue
	2、生产一个消息【3个节点都能看到该消息】
	3、消费消息【3个节点的queue上消息都不存在了】

查看集群:

1600934253707

5、k8s有状态服务【预准备】

可以使用kubesphere,快速搭建MySQL环境。
	1、配置:有状态服务抽取配置为ConfigMap【kubesphere可以在配置中心中创建配置】
	2、pvc:有状态服务必须使用pvc持久化数据
	3、集群内互相访问:服务集群内访问使用 DNS 提供的稳定域名

1600948807127

kubesphere创建配置:

1600935250741

如果没有可视化界面:使用yaml就行:

1600935277035

pvc的创建yaml:

1600935387357

5.1、使用kubesphere搭建mysql主从集群

如果没有指定的都选择默认值
一、创建主机
1、创建配置
	1)gulimall-mysql-master-cnf,别名:master配置
	2)配置设置
	key:my.cnf
	value:
[client]
default-character-set=utf8

[mysql]
default-character-set=utf8

[mysqld]
init_connect='SET collation_connection = utf8_unicode_ci'
init_connect='SET NAMES utf8'
character-set-server=utf8
collation-server=utf8_unicode_ci
skip-character-set-client-handshake
skip-name-resolve

server-id=1
log-bin=mysql-bin
read-only=0
binlog-do-db=gulimall_oms
binlog-do-db=gulimall_pms
binlog-do-db=gulimall_sms
binlog-do-db=gulimall_ums
binlog-do-db=gulimall_wms
binlog-do-db=gulimall_admin

replicate-ignore-db=mysql
replicate-ignore-db=sys
replicate-ignore-db=performance_schema
replicate-ignore-db=information_schema

2、创建pvc
名称:gulimall-mysql-master-pvc
单节点读写+10Gi
创建

3、创建有状态服务
	1)名称mysql-master 别名:mysql主节点
	2)选择:容器组分散部署
	3)添加容器镜像
		mysql:5.7
		使用默认端口
		高级设置:CPU 0.5
			    内存2000Mi

	4)环境变量,选择之前设置的密钥root密码【密文】
		MYSQL_ROOT_PASSWORD
	5)挂载配置文件
		读写	/etc/mysql
		选择特定的键和路径
			my.cnf	my.cnf【选择key为my.cnf的挂载到/etc/mysql的my.cnf文件中】

	6)挂载存储,master-pvc【mysql数据放的路径】
		选择已有存储卷
		读写	/var/lib/mysql
	7)开启会话保持

4、创建成功,查看服务mysql-master
	1)查看DNS: mysql-master.gulimall
	2)随便进入一个容器的终端: ping mysql-master.gulimall可以ping通

二、创建从机
1、创建pvc:gulimall-mysql-slaver-pvc
	读写:10Gi
2、创建配置:mysql-slaver-cnf
	key:my.cnf
	value:
[client]
default-character-set=utf8

[mysql]
default-character-set=utf8

[mysqld]
init_connect='SET collation_connection = utf8_unicode_ci'
init_connect='SET NAMES utf8'
character-set-server=utf8
collation-server=utf8_unicode_ci
skip-character-set-client-handshake
skip-name-resolve

server-id=2
log-bin=mysql-bin
read-only=1
binlog-do-db=gulimall_oms
binlog-do-db=gulimall_pms
binlog-do-db=gulimall_sms
binlog-do-db=gulimall_ums
binlog-do-db=gulimall_wms
binlog-do-db=gulimall_admin

replicate-ignore-db=mysql
replicate-ignore-db=sys
replicate-ignore-db=performance_schema
replicate-ignore-db=information_schema
3、创建有状态服务
	1)名称mysql-slaver 别名:mysql从节点
	2)选择:容器组分散部署
	3)添加容器镜像
		mysql:5.7
		使用默认端口
		高级设置:CPU 0.5
			    内存2000Mi

	4)环境变量,选择之前设置的密钥root密码
		MYSQL_ROOT_PASSWORD
	5)挂载配置文件
		读写	/etc/mysql
		选择特定的键和路径
			my.cnf	my.cnf【选择key为my.cnf的挂载到/etc/mysql的my.cnf文件中】

	6)挂载存储,master-pvc【mysql数据放的路径】
		选择已有存储卷
		读写	/var/lib/mysql
	7)开启会话保持

4、创建成功,查看服务mysql-master
	1)查看DNS: mysql-master.gulimall
	2)随便进入一个容器的终端: ping mysql-master.gulimall可以ping通
	
三、为master授权用户来他的同步数据
1、进入master容器,进入容器终端
mysql -uroot -p123456

	可忽略:
	1)授权root可以远程访问(主从无关,方便我们可以远程链接mysql)
	grant all privileges on *.* to 'root'@'%' IDENTIFIED BY 'root' with grant option;
	flush privileges;

2、添加同步用户,链接master数据库,在master授权一个 复制权限的 用户
	GRANT REPLICATION SLAVE ON *.* TO 'backup'@'%' IDENTIFIED BY '123456';

3、查看master状态,复制文件夹名字,例如mysql-bin.000001
show master status;

4、配置slaver同步master数据
进入slaver容器
mysql -uroot -p root

	1)授权root可以远程访问(主从无关,方便我们可以远程链接mysql)
	grant all privileges on *.* to 'root'@'%' IDENTIFIED BY 'root' with grant option;
	flush privileges;
5、设置主库连接,这里的master-host填写之前的DNS
	change master to master_host='mysql-master.gulimall',master_user='backup',master_password='123456',master_log_file='mysql-bin.000001',master_log_pos=0,master_port=3306;
	
6、启动从库同步
start slave;

7、查看从库状态
show slave status;

挂载了配置文件+存储:

1600936986771

服务运行成功:

1600937100569

连入从库:

1600948453227

5.2、使用kubesphere搭建redis主从集群

1、创建redis配置
	gulimall-redis-conf
	key:redis-conf
	value:
	appendonly yes
	
2、创建pvc
	gulimall-redis-pvc
	单个节点读写,5Gi
	
3、创建有状态服务
	1)设置基本信息
	名称:redis
	添加镜像:redis:5.0.7
	使用默认端口
	CPU:0.5
	内存:500Mi
	启动命令:这个不是环境变量【参照docker语句】
		运行命令:redis-server
		参数:/etc/redis/redis.conf
	2)挂载pvc【挂载的目录就是redis数据存储的地方,具体可以查看docker hub】
		读写	/data
	3)挂载配置文件
		读写	/etc/redis
		选择特定的键和路径
			选择键:redis-conf【就是创建配置的时候的key】
			挂载路径:redis.conf【/etc/redis/redis.conf】

5.3、使用kubesphere搭建elasticsearch主从集群

1、创建配置
	名称:gulimall-elasticsearch-conf
	key:http.host
	value:0.0.0.0
	
	其次docker run -e指定的环境变量都可以用key-value指定,而不是之前的整个文件的形式存储值
	key:ES_JAVA_OPTS
	value:-Xms300m -Xmx300m
	
	key:discovery.type=single-node
	value:single-node
	
2、创建pvc
	名称:gulimall-elasticsearch-pvc
	单个节点读写+10Gi

3、创建有状态服务
	1)名称:elasticsearch
	添加镜像:elasticsearch:7.4.2
	使用默认端口
	0.5CPU+1500Mi
	
	2)添加环境变量:选择之前创建的配置,ES_JAVA_OPTS和discovery.type和http.host
	
	3)挂载存储卷
		读写	/usr/share/elasticsearch/data
		
4、验证
	1)使用admin登录kubesphere:192.168.56.100:30880/login
		admin/P@88w0rd
	2)打开控制台:curl http://elasticsearch.gulimall:9200【服务的DNS,查看是否有打印】

1600950734413

5.3.1、安装kibana

docker run -name kibana -e ELASTICSEARCH_HOSTS=http://192.168.56.10:9200 -p 5601:5601 -d kibana:7.4.2

1、创建一个无状态服务
	1)名称:kibana
	镜像:kibana:7.4.2
	环境变量:
		ELASTICSEARCH_HOSTS	http://elasticsearch.gulimall:9200【DNS地址】
	2)选中外网访问
		NodePort
2、访问192.168.56.100:32157

5.4、使用kubesphere搭建rabbitmq主从集群

1、创建pvc
gulimall-rabbitmq-pvc
10Gi

2、创建有状态服务
	1)名称:rabbitmq-management
	镜像:rabbitmq-management
	环境变量:RABBITMQ_ERLANG_COOKIE		atguigu
	2)挂载pvc
	读写	/var/lib/rabbitmq

五、预准备环境

1、k8s搭建Nacos

	nacos的数据可以配置到数据库中,参考/nacos/conf/application.properties.example

	docker启动Nacos容器语句:
docker run --env MODE=standalone --name nacos \
-v /mydata/nacos/conf:/home/nacos/conf -d -p 8848:8848 nacos/nacos-server:1.1.4


1、创建pvc
	gulimall-nacos-pvc
	1Gi
2、创建有状态服务
	1)名称:nacos
	镜像:nacos/nacos-server:1.1.4
	使用默认端口
	2)环境变量:MODE		standalone【单机模式】
	3)挂载pvc
	读写		/home/nacos/data
3、后期可以配合nginx对外暴露


4、这里讲解改成无状态的服务的方法:【对外暴露端口】
	1)删除对外暴露的service,但是不钩中 有状态副本集,此时容器组中还存在 该nacos
	2)创建服务 => 自定义创建 => 指定工作负载
		名称:gulimall-nacos-2
	3)指定工作负载
		选中有状态副本集,刚刚创建的nacos-wrnw3c容器
	4)指定端口
		协议类:HTTP  名称:http-8848  容器端口:8848	服务端口:8848
	5)选中外网访问
		NodePort
5、然后看service暴露的端口:30709
	192.168.56.100:30709/nacos
	username:nacos
	password:nacos
	
重点:这里无状态服务开启对外服务是NodePod 随机端口的方式对外暴露,所以在其他pod节点上使用域名是访问不到的,例如在zipkin容器中ping nacos.gulimall :失败
所以后面部署的应用服务的配置spring.cloud.nacos.discovery.server-addr不能直接使用nacos.gulimall

问题解决:
1、创建服务 => 自定义创建 => 指定工作负载
名称:nacos-service
访问类型:Headless【以域名的方式访问】
端口
HTTP	http-nacos-8848		8848	8848
不选中外网访问

2、测试
进入zipkin终端
	ping nacos-service.gulimall

3、修改各项目配置文件中
spring.cloud.nacos.discovery.server-addr=nacos-service.gulimall:8848


总结:这里一个pod对外暴露了两个service,一个是外网访问,一个是内部集群域名访问

不钩中:

1600953233296

指定工作负载:

1600953633628

指定端口:

1600953690073

2、k8s搭建zipkin【Spring Sleuth】

链路追踪可视化界面

docker run -d -p 9411:9411 openzipkin/zipkin【无状态】
或者
docker run --env STORAGE_TYPE=elasticsearch --env ES_HOSTS=192.168.56.10:9200 openzipkin/zipkin【有状态】

1、创建无状态服务【数据适用es存储】
	1)名称:zipkin
	镜像:openzipkin/zipkin
	2)环境变量:
	STORAGE_TYPE 	elasticsearch
	ES_HOSTS		elasticsearch.gulimall:9200
	3)设置外网访问
	NodePort
2、查看service对外暴露的端口32067
	访问:192.168.56.100:32067


问题解决:
1、创建服务 => 自定义创建 => 指定工作负载
名称:zipkin-service
访问类型:Headless【以域名的方式访问】
端口
HTTP	http-zipkin		9411	9411
不选中外网访问

2、测试
进入Nacos终端
	ping zipkin-service.gulimall

3、修改各项目配置文件中
spring.zipkin.base-url=http://zipkin-service.gulimall:9411/


总结:这里一个pod对外暴露了两个service,一个是外网访问,一个是内部集群域名访问

3、k8s部署dashboard【sentinel】

熔断、限流、降级

sentinel官方没有镜像,可以自己制作一个镜像并启动它,暴露访问【后面每一个业务代码我们都会制作镜像,可以参考】
docker run --name sentinel  -d -p 8858:8858 -d  bladex/sentinel-dashboard:1.7.1

1、创建无状态服务
	1)名称:sentinel
	2)指定端口
		协议类:TCP  名称:tcp-8858  容器端口:8858	服务端口:8333
		【这里应该是HTTP,但是老师是TCP,未验证】
	3)外网访问
		NodePort
2、查看service暴露的端口
访问:192.168.56.100:31987

问题解决:
1、创建服务 => 自定义创建 => 指定工作负载
名称:sentinel-service
访问类型:Headless【以域名的方式访问】
端口
HTTP	http-sentinel		8858	8333
不选中外网访问

2、测试
进入zipkin终端
	ping sentinel-service.gulimall

3、修改各项目配置文件中
spring.cloud.sentinel.transport.dashboard=sentinel-service.gulimall:8333


总结:这里一个pod对外暴露了两个service,一个是外网访问,一个是内部集群域名访问

六、微服务流水线

1、流程疏导

1600957838089

ci/cd:持续继承持续部署,应用的部署应该坚持这个原则【DevOps】


1、为每一个项目准备一个Dockerfile文件,规定了构建镜像规则
作用:
	1).jar文件和临时文件存储位置
	2)规定了 容器运行后暴露的端口
	3)规定了 容器运行后内部执行的指令java -jar /app.jar --spring.profiles.active=prod默认启动项目

	【docker build -f Dockerfile xxxx:这个是构建到本地仓库】
	【docker login -u wanzenghui -p xxx:登录docker hub】
	【docker push docker xxxxx:这个是将本地的镜像推送到远程仓库】

2、为每一个项目生成k8s的部署文件 xx-deploy.yaml
	1)部署pod,根据流程1生成的镜像部署pod【当然镜像是从docker hub拉取下来的】
	2)service对外暴露

3、编写Jenkinsfile
	1)自动拉取gitee
	2)maven打包.jar
	3)根据Dockerfile构建镜像
	4)镜像上传至docker hub【也可以是私有仓库】
	5)

2、开始构建

2.1、抽取生产环境配置

1、抽取生产环境配置application-prod.properties,application-prod.yaml
gulimall-auth-server:
	1)为每一个服务复制一份生产环境的配置文件【启动时带上参数spring.profiles.active=prod】
		application-prod.yaml
		application-prod.properties
		bootstrap-prod.properties
	2)修改域名	
    1.1、修改mysql url
    1.2、修改redis host
    1.3、修改rabbitmq
	1.4、修改elasticsearch
	
	1.5、修改nacos addr
	1.6、修改zipkin
	1.7、修改sentinel
	
spring.datasource.url=jdbc:mysql://mysql-master.gulimall:3306/gulimall_sms?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai
spring.redis.host=redis.gulimall【6379】
spring.rabbitmq.host=rabbitmq-management.gulimall【5672】
elasticsearch.gulimall【9200】

spring.cloud.nacos.discovery.server-addr=nacos-service.gulimall:8848
spring.zipkin.base-url=http://zipkin-service.gulimall:9411/
spring.cloud.sentinel.transport.dashboard=sentinel-service.gulimall:8333


2、修改每个微服务的-prod配置文件,将端口修改成8080
	原因:访问容器的8080 能访问到服务

2.2、流程一:创建Dockerfile文件

作用:构建镜像的时候需要的配置

1、为每个微服务创建一个Dockerfile文件【renren-fast项目中有该文件模板】
	容器对外暴露的端口都是8080,因为容器端口不会冲突,一个pod只会跑一个微服务容器
	将来区分端口的是对外暴露的service端口
2、修改Dockerfiler文件,添加参数--spring.profiles.active=prod
使其加载生产环境的配置文件【默认会覆盖application.properties和application.yaml的配置(互补形式)】

FROM java:8
EXPOSE 8080

VOLUME /tmp
ADD target/*.jar  /app.jar
RUN bash -c 'touch /app.jar'
ENTRYPOINT ["java","-jar","/app.jar","--spring.profiles.active=prod"]
=============解析:
【微服务依赖的基础镜像java8】
【容器暴露的端口】

【挂载的目录,启动日志会挂载到这里,然后k8s可以使用pvc挂载】
【拷贝当前编译目录target/*.jar拷贝到容器根目录并更名为app.jar】
【如果不存在则创建】
【容器启动默认运行的命令】

docker run 启动该镜像后
docker exec -it hello-admin	/bin/bash可以查看到/tmp,可以使用pvc挂载

2.3、手动构建镜像上传本地镜像仓库

需要.jar文件和Dockerfile两个文件构建镜像

1、在gulimall层级下install
clean install -Dmaven.test.skip=true

2、复制renren-fast.jar和Dockerfile上传到192.168.56.10  /opt/admin

3、执行命令打包
-t:标签		
.:表示当前目录下的资源要打包
docker build -f Dockerfile -t docker.io/wanzenghui/admin:v1.0 .

4、查看本地镜像
docker images
wanzenghui/admin		v1.0

5、启动容器
docker run -d --name hello-admin -p 8080:8080 wanzenghui/admin:v1.0

6、进入容器
curl 127.0.0.1:8080
docker exec -it hello-admin	/bin/bash
然后输入 ls:能看到一个app.jar

1601018806244

2.4、流程二:创建部署和暴露配置yaml

作用:部署和暴露镜像的yaml配置
0、注意修改成$PROJECT_NAME和$PROJECT_VERSION

1、在每一个微服务下面创建一个deploy文件夹

2、创建部署文件,参照github项目devops-java-sample
名称:项目名-deploy.yaml
例:gulimall-auth-server-deploy.yaml
	# namespace名称空间是项目
	# app就相当于一个id,与html中id选择器一样作用
	# type: RollingUpdate 滚动更新机制【例如3个pod 1.0版本,此时发布2.0,按照最大不可用的值停止节点】
	# maxUnavailable: 25% 最大不可用【4个pod,每次停1个】
	# maxSurge: 25% 最大存活
	# revisionHistoryLimit:保留多少个版本
	# progressDeadlineSeconds:部署时间deadline
	# targetPort: 8080,容器暴露的端口
	# port: 8080,通过域名  gulimall-auth-server.gulimall:8080 内部访问的,这里是NodePort对外暴露,以节点的方式访问192.168.56.100:20001

kind: Deployment
apiVersion: apps/v1
metadata:
  name: gulimall-auth-server
  namespace: gulimall
  labels:
    app: gulimall-auth-server
spec:
  replicas: 1
  selector:
    matchLabels:
      app: gulimall-auth-server
  template:
    metadata:
      labels:
        app: gulimall-auth-server
    spec:
      containers:
        - name: gulimall-auth-server
          image: $REGISTRY/$DOCKERHUB_NAMESPACE/$PROJECT_NAME:latest
          ports:
            - containerPort: 8080
              protocol: TCP
          resources:
            limits:
              cpu: 1000m
              memory: 500Mi
            requests:
              cpu: 10m
              memory: 10Mi
          terminationMessagePath: /dev/termination-log
          terminationMessagePolicy: File
          imagePullPolicy: IfNotPresent
      restartPolicy: Always
      terminationGracePeriodSeconds: 30
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 25%
      maxSurge: 25%
  revisionHistoryLimit: 10
  progressDeadlineSeconds: 600
---
kind: Service
apiVersion: v1
metadata:
  name: gulimall-auth-server
  namespace: gulimall
  labels:
    app: gulimall-auth-server
spec:
  ports:
    - protocol: TCP
      port: 8080
      targetPort: 8080
      nodePort: 31000
  selector:
    app: gulimall-auth-server
  type: NodePort
  sessionAffinity: None
  
3、为每一个微服务创建一份 xx-deploy.yaml
	修改三个部分
	1、修改成各自项目名name: gulimall-auth-server
	2、app选择器修改成name: gulimall-auth-server
	3、nodePort修改为不同的端口

2.5、端口规划与解析

1601084274598

1、Dockerfile暴露的端口
	解释:是容器对外暴露的端口,一般与容器内部的应用端口是同一个
	
2、deploy.yaml
	targetPort:容器暴露的目标端口,与Dockerfile中EXPOSE是同一个【可重复】
	port:service负载均衡对集群节点暴露的端口,集群间可以域名或Clustr IP(虚拟ip)访问【可重复】
	nodePort:对外负载均衡暴露的端口,每个结点都会开辟端口,使用任何一个节点ip+nodePort都可以访问【不可重复】

3、nodePort规划:
gulimall-auth-server:31000
gulimall-cart:31001
gulimall-coupon:31002
gulimall-gateway:31003
gulimall-member:31004
gulimall-order:31005
gulimall-product:31006
gulimall-search:31007
gulimall-seckill:31008
gulimall-third-party:31009
gulimall-ware:31010
renren-fast:31011

2.6、流程三:创建Jenkinsfile

作用:流水线配置。拉取代码、maven打包、构建镜像、上传仓库、部署+暴露service

可以参考github项目devops-sample.yaml的Jenkinsfile-online文件
但是因为代码是放在gitee上而不是github上,所以这里可以使用kubesphere构建Jenkinsfile文件,选择gitee

2.6.1、stage1 fetch + install

1、从gitee拉取代码
2、install编译+打包
# 手动创建流水线获取Jenkinsfile 模板【非标准流水线创建】

创建DevOps工程

1、创建流水线【这种不是标准创建流水线的方式,点击创建后要选择代码仓库,选择分支】

1601087264076

2、选中类型(node)和 label(maven)【这里跳过了选择代码仓库,选择分支】

1601087353255

3、添加步骤,点加号,再点添加步骤,选择git拉取代码

1601087443474

4、新建凭证,复制项目码云的git地址

分支使用master分支,确定保存

1601087504298

1601087544866

5、查看Jenkinsfile

1601087981802

1601087998578

pipeline {
  agent {
    node {
      label 'maven'
    }
  }
  stages {
    stage ('拉取代码') {
      steps {
        git(url: 'https://gitee.com/lemon_wan/gulimall.git', credentialsId: 'gitee-id', branch: 'master', changelog: true, poll: false)
      }
    }
  }
}
# 参数化构建

1601093515995

作用:
	1、每次发布到仓库的 镜像版本需要手动配置,使用Jenkinsfile可以自动构建参数
	2、动态规定项目名【并不是每次发布都要重新构建所有项目,指定项目】
Jenkinsfile可以使用动态参数,使用传入的方式

pipeline {
  agent {
    node {
      label 'maven'
    }
  }

  parameters {
    string(name:'PROJECT_VERSION',defaultValue: 'v0.0Beta',description:'')
    string(name:'PROJECT_NAME',defaultValue: '',description:'')
  }
  stages {
    stage ('拉取代码') {
      steps {
        git(url: 'https://gitee.com/lemon_wan/gulimall.git', credentialsId: 'gitee-id', branch: 'master', changelog: true, poll: false)
        sh 'echo 正在构建 $PROJECT_NAME  版本号:$PROJECT_VERSION'
      }
    }
  }
}

手动演示生效流程:

1、选中流水线,编辑Jenkinsfile

加入参数化构建

1601094252228

2、点击运行,提示输入参数

1601094338729

3、点击编辑流水线,添加步骤shell

1601094452531

echo $PROJECT_VERSION

保存

4、再次点击编辑Jenkinsfile,shell脚本被整合进来了,点击运行,查看日志是否输出参数

输入参数

1601094514307

点击流水线,点击查看日志

1601094610688

1601094661380

日志:

1601094715695

# 添加参数
在流水线中添加参数,否则无法手动设置参数
PROJECT_VERSION是自动添加好的参数
PROJECT_NAME需要自己添加

1、点击编辑配置

1601100817782

2、添加参数=》选项参数

添加所有被选参数【每个微服务的名字,一行一个】

1601100875182

3、运行

1601101264413

# 环境变量
pipeline {
  agent {
    node {
      label 'maven'
    }
  }

  parameters {
    string(name:'PROJECT_VERSION',defaultValue: 'v0.0Beta',description:'')
    string(name:'PROJECT_NAME',defaultValue: '',description:'')
  }

  environment {
    DOCKER_CREDENTIAL_ID = 'dockerhub-id'
    GITEE_CREDENTIAL_ID = 'gitee-id'
    KUBECONFIG_CREDENTIAL_ID = 'demo-kubeconfig'
    REGISTRY = 'docker.io'
    DOCKERHUB_NAMESPACE = 'wanzenghui'
    GITEE_ACCOUNT = 'lemon_wan'
    SONAR_CREDENTIAL_ID = 'sonar-qube'
  }

  stages {
    stage ('拉取代码') {
      steps {
        git(url: 'https://gitee.com/lemon_wan/gulimall.git', credentialsId: 'gitee-id', branch: 'master', changelog: true, poll: false)
        sh 'echo 正在构建 $PROJECT_NAME  版本号:$PROJECT_VERSION 将会提交给 $REGISTRY 镜像仓库'
      }
    }
  }
}

2.6.2、 stage 2:sonarqube

对从gitee拉取的代码进行审查
# 在gulimall新建mvn-settings.xml

1、创建mvn-settings.xml

否则会去找官方配置文件

https://github.com/wanzenghui/devops-java-sample/blob/master/configuration/settings.xml

<settings>
    <mirrors>
        <mirror>
            <id>aliyun</id>
            <name>aliyun Maven</name>
            <mirrorOf>*</mirrorOf>
            <url>http://maven.aliyun.com/nexus/content/groups/public</url>
        </mirror>
    </mirrors>
</settings>

2、使用从项目中拉取到的 mvn-settings.xml,-gs指定该xml路径【当前目录/mvn-settings.xml】

​ 设置环境变量分支:master

pipeline {
  agent {
    node {
      label 'maven'
    }
  }

  parameters {
    string(name:'PROJECT_VERSION',defaultValue: 'v0.0Beta',description:'')
    string(name:'PROJECT_NAME',defaultValue: '',description:'')
  }

  environment {
    DOCKER_CREDENTIAL_ID = 'dockerhub-id'
    GITEE_CREDENTIAL_ID = 'gitee-id'
    KUBECONFIG_CREDENTIAL_ID = 'demo-kubeconfig'
    REGISTRY = 'docker.io'
    DOCKERHUB_NAMESPACE = 'wanzenghui'
    GITEE_ACCOUNT = 'lemon_wan'
    SONAR_CREDENTIAL_ID = 'sonar-qube'
    BRANCH_NAME = 'master'
  }

  stages {
    stage ('拉取代码') {
      steps {
        git(url: 'https://gitee.com/lemon_wan/gulimall.git', credentialsId: 'gitee-id', branch: 'master', changelog: true, poll: false)
        sh 'echo 正在构建 $PROJECT_NAME  版本号:$PROJECT_VERSION 将会提交给 $REGISTRY 镜像仓库'
        sh 'mvn clean install -Dmaven.test.skip=true'
      }
    }

    stage('sonarqube代码质量分析') {
      steps {
        container ('maven') {
          withCredentials([string(credentialsId: "$SONAR_CREDENTIAL_ID", variable: 'SONAR_TOKEN')]) {
            withSonarQubeEnv('sonar') {
              sh "echo 当前目录 `pwd`"
              sh "mvn sonar:sonar -gs `pwd`/mvn-settings.xml -Dsonar.branch=$BRANCH_NAME -Dsonar.login=$SONAR_TOKEN"
            }
          }
          timeout(time: 1, unit: 'HOURS') {
            waitForQualityGate abortPipeline: true
          }
        }
      }
    }
  }



}
# 异常1:未指定编译后代码位置pom.xml

1601104466432

1、未指定编译后代码位置,在gulimall的 pom.xml 中指定properties和build
参照https://github.com/wanzenghui/devops-java-sample/blob/master/pom.xml

<?xml version="1.0" encoding="UTF-8"?>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>

        <!-- Sonar -->
        <!-- The destination file for the code coverage report has to be set to the same value
             in the parent pom and in each module pom. Then JaCoCo will add up information in
             the same report, so that, it will give the cross-module code coverage. -->
        <sonar.jacoco.reportPaths>${PWD}/./target/jacoco.exec</sonar.jacoco.reportPaths>
        <sonar.groovy.binaries>target/classes</sonar.groovy.binaries>
    </properties>


    <build>
        <plugins>
            <plugin>
                <groupId>org.jacoco</groupId>
                <artifactId>jacoco-maven-plugin</artifactId>
                <version>0.8.2</version>
                <configuration>
                    <append>true</append>
                </configuration>
                <executions>
                    <execution>
                        <id>agent-for-ut</id>
                        <goals>
                            <goal>prepare-agent</goal>
                        </goals>
                    </execution>
                    <execution>
                        <id>agent-for-it</id>
                        <goals>
                            <goal>prepare-agent-integration</goal>
                        </goals>
                    </execution>
                    <execution>
                        <id>jacoco-site</id>
                        <phase>verify</phase>
                        <goals>
                            <goal>report</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <fork>true</fork>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.sonarsource.scanner.maven</groupId>
                <artifactId>sonar-maven-plugin</artifactId>
                <version>3.6.0.1398</version>
            </plugin>
        </plugins>
    </build>
</project>
# 异常2:找不到mvn命令

1601105722875

1、在maven容器中指定 sh命令
2、指定maven配置文件	-gs `pwd`/mvn-settings.xml
pipeline {
  agent {
    node {
      label 'maven'
    }
  }

  parameters {
    string(name:'PROJECT_VERSION',defaultValue: 'v0.0Beta',description:'')
    string(name:'PROJECT_NAME',defaultValue: '',description:'')
  }

  environment {
    DOCKER_CREDENTIAL_ID = 'dockerhub-id'
    GITEE_CREDENTIAL_ID = 'gitee-id'
    KUBECONFIG_CREDENTIAL_ID = 'demo-kubeconfig'
    REGISTRY = 'docker.io'
    DOCKERHUB_NAMESPACE = 'wanzenghui'
    GITEE_ACCOUNT = 'lemon_wan'
    SONAR_CREDENTIAL_ID = 'sonar-qube'
    BRANCH_NAME = 'master'
  }

  stages {
    stage ('拉取代码') {
      steps {
        git(url: 'https://gitee.com/lemon_wan/gulimall.git', credentialsId: 'gitee-id', branch: 'master', changelog: true, poll: false)
        sh 'echo 正在构建 $PROJECT_NAME  版本号:$PROJECT_VERSION 将会提交给 $REGISTRY 镜像仓库'
        container ('maven') {
          sh 'mvn clean install -Dmaven.test.skip=true -gs `pwd`/mvn-settings.xml'
        }
      }
    }

    stage('sonarqube代码质量分析') {
      steps {
        container ('maven') {
          withCredentials([string(credentialsId: "$SONAR_CREDENTIAL_ID", variable: 'SONAR_TOKEN')]) {
            withSonarQubeEnv('sonar') {
              sh "echo 当前目录 `pwd`"
              sh "mvn sonar:sonar -gs `pwd`/mvn-settings.xml -Dsonar.branch=$BRANCH_NAME -Dsonar.login=$SONAR_TOKEN"
            }
          }
          timeout(time: 1, unit: 'HOURS') {
            waitForQualityGate abortPipeline: true
          }
        }
      }
    }
  }



}
# 异常3:编译版本报错

1601106073529

1、在本地maven环境里面,我在setting文件里面指定了所有项目编译都是jdk1.8的版本,所以同样在mvn-setting.xml中设置<profiles>
2、指定maven配置文件 -gs `pwd`/mvn-settings.xml

<settings>
    <mirrors>
        <mirror>
            <id>aliyun</id>
            <name>aliyun Maven</name>
            <mirrorOf>*</mirrorOf>
            <url>http://maven.aliyun.com/nexus/content/groups/public</url>
        </mirror>
    </mirrors>

    <profiles>
        <profile>
            <id>jdk-1.8</id>
            <activation>
                <activeByDefault>true</activeByDefault>
                <jdk>1.8</jdk>
            </activation>
            <properties>
                <maven.compiler.source>1.8</maven.compiler.source>
                <maven.compiler.target>1.8</maven.compiler.target>
                <maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion>
            </properties>
        </profile>
    </profiles>
</settings>
# 运行成功

1601107179734

1601107092009

2.6.3、stage3:构建镜像+推送快照镜像【snapshot】

作用:docker构建镜像,并且推送至docker hub
要求:按照前面参数化构建的项目名,动态构建镜像
1、修改Dockerfile文件目录-f $PROJECT_NAME/Dockerfile
2、将APP_NAME修改成PROJECT_NAME
3、修改setting.xml配置文件,mvn-settings.xml
 
pipeline {
  agent {
    node {
      label 'maven'
    }
  }

  parameters {
    string(name:'PROJECT_VERSION',defaultValue: 'v0.0Beta',description:'')
    string(name:'PROJECT_NAME',defaultValue: '',description:'')
  }

  environment {
    DOCKER_CREDENTIAL_ID = 'dockerhub-id'
    GITEE_CREDENTIAL_ID = 'gitee-id'
    KUBECONFIG_CREDENTIAL_ID = 'demo-kubeconfig'
    REGISTRY = 'docker.io'
    DOCKERHUB_NAMESPACE = 'wanzenghui'
    GITEE_ACCOUNT = 'lemon_wan'
    SONAR_CREDENTIAL_ID = 'sonar-qube'
    BRANCH_NAME = 'master'
  }

  stages {
    stage ('拉取代码') {
      steps {
        git(url: 'https://gitee.com/lemon_wan/gulimall.git', credentialsId: 'gitee-id', branch: 'master', changelog: true, poll: false)
        sh 'echo 正在构建 $PROJECT_NAME  版本号:$PROJECT_VERSION 将会提交给 $REGISTRY 镜像仓库'
        container ('maven') {
          sh 'mvn clean install -Dmaven.test.skip=true -gs `pwd`/mvn-settings.xml'
        }
      }
    }

    stage('sonarqube代码质量分析') {
      steps {
        container ('maven') {
          withCredentials([string(credentialsId: "$SONAR_CREDENTIAL_ID", variable: 'SONAR_TOKEN')]) {
            withSonarQubeEnv('sonar') {
              sh "echo 当前目录 `pwd`"
              sh "mvn sonar:sonar -gs `pwd`/mvn-settings.xml -Dsonar.branch=$BRANCH_NAME -Dsonar.login=$SONAR_TOKEN"
            }
          }
          timeout(time: 1, unit: 'HOURS') {
            waitForQualityGate abortPipeline: true
          }
        }
      }
    }

    stage ('构建镜像 & 推送快照镜像') {
      steps {
        container ('maven') {
          sh 'mvn -Dmaven.test.skip=true -gs `pwd`/mvn-settings.xml clean package'
          sh 'docker build -f $PROJECT_NAME/Dockerfile -t $REGISTRY/$DOCKERHUB_NAMESPACE/$PROJECT_NAME:SNAPSHOT-$BRANCH_NAME-$BUILD_NUMBER .'
          withCredentials([usernamePassword(passwordVariable : 'DOCKER_PASSWORD' ,usernameVariable : 'DOCKER_USERNAME' ,credentialsId : "$DOCKER_CREDENTIAL_ID" ,)]) {
            sh 'echo "$DOCKER_PASSWORD" | docker login $REGISTRY -u "$DOCKER_USERNAME" --password-stdin'
            sh 'docker push  $REGISTRY/$DOCKERHUB_NAMESPACE/$PROJECT_NAME:SNAPSHOT-$BRANCH_NAME-$BUILD_NUMBER'
          }
        }
      }
    }
  }



}
# 异常1:没有jar包

1601108239101

需要cd到该项目内部,否则找不到jar包
因为docker build -f Dockerfile -t docker.io/wanzenghui/admin:v1.0 . 这条命令中
.:代表当前目录下的资源,也就是在当前目录下找不到app.jar,所以要修改sh命令,cd /$PROJECT_NAME进入到项目的根目录后异常解除。
	解释Dockerfile中ADD target/*.jar  /app.jar
docker build 会根据Dockerfile中指定的/target/*.jar找到指定的jar
	/$PROJECT_NAME/target/*.jar 就是代码从gitee拉取下来后,经过install后jar包的目录
【cd $PROJECT_NAME && docker build -f Dockerfile】


pipeline {
  agent {
    node {
      label 'maven'
    }
  }

  parameters {
    string(name:'PROJECT_VERSION',defaultValue: 'v0.0Beta',description:'')
    string(name:'PROJECT_NAME',defaultValue: '',description:'')
  }

  environment {
    DOCKER_CREDENTIAL_ID = 'dockerhub-id'
    GITEE_CREDENTIAL_ID = 'gitee-id'
    KUBECONFIG_CREDENTIAL_ID = 'demo-kubeconfig'
    REGISTRY = 'docker.io'
    DOCKERHUB_NAMESPACE = 'wanzenghui'
    GITEE_ACCOUNT = 'lemon_wan'
    SONAR_CREDENTIAL_ID = 'sonar-qube'
    BRANCH_NAME = 'master'
  }

  stages {
    stage ('拉取代码') {
      steps {
        git(url: 'https://gitee.com/lemon_wan/gulimall.git', credentialsId: 'gitee-id', branch: 'master', changelog: true, poll: false)
        sh 'echo 正在构建 $PROJECT_NAME  版本号:$PROJECT_VERSION 将会提交给 $REGISTRY 镜像仓库'
        container ('maven') {
          sh 'mvn clean install -Dmaven.test.skip=true -gs `pwd`/mvn-settings.xml'
        }
      }
    }

    stage('sonarqube代码质量分析') {
      steps {
        container ('maven') {
          withCredentials([string(credentialsId: "$SONAR_CREDENTIAL_ID", variable: 'SONAR_TOKEN')]) {
            withSonarQubeEnv('sonar') {
              sh "echo 当前目录 `pwd`"
              sh "mvn sonar:sonar -gs `pwd`/mvn-settings.xml -Dsonar.branch=$BRANCH_NAME -Dsonar.login=$SONAR_TOKEN"
            }
          }
          timeout(time: 1, unit: 'HOURS') {
            waitForQualityGate abortPipeline: true
          }
        }
      }
    }

    stage ('构建镜像 & 推送快照镜像') {
      steps {
        container ('maven') {
          sh 'mvn -Dmaven.test.skip=true -gs `pwd`/mvn-settings.xml clean package'
          sh 'cd $PROJECT_NAME && docker build -f Dockerfile -t $REGISTRY/$DOCKERHUB_NAMESPACE/$PROJECT_NAME:SNAPSHOT-$BRANCH_NAME-$BUILD_NUMBER .'
          withCredentials([usernamePassword(passwordVariable : 'DOCKER_PASSWORD' ,usernameVariable : 'DOCKER_USERNAME' ,credentialsId : "$DOCKER_CREDENTIAL_ID" ,)]) {
            sh 'echo "$DOCKER_PASSWORD" | docker login $REGISTRY -u "$DOCKER_USERNAME" --password-stdin'
            sh 'docker push  $REGISTRY/$DOCKERHUB_NAMESPACE/$PROJECT_NAME:SNAPSHOT-$BRANCH_NAME-$BUILD_NUMBER'
          }
        }
      }
    }
  }



}
# 运行成功

登录docker hub查看

1601110356289

2.6.4、stage4:推送最新镜像【latest】

    stage('push latest'){
      when{
        branch 'master'
      }
      steps{
        container ('maven') {
          sh 'docker tag  $REGISTRY/$DOCKERHUB_NAMESPACE/$PROJECT_NAME:SNAPSHOT-$BRANCH_NAME-$BUILD_NUMBER $REGISTRY/$DOCKERHUB_NAMESPACE/$PROJECT_NAME:latest '
          sh 'docker push  $REGISTRY/$DOCKERHUB_NAMESPACE/$PROJECT_NAME:latest '
        }
      }
    }

2.6.5、stage5:部署到开发环境

    stage('部署到开发环境') {
      when{
        branch 'master'
      }
      steps {
        input(id: 'deploy-to-dev-$PROJECT_NAME', message: '是否将$PROJECT_NAME部署到开发环境?')
        kubernetesDeploy(configs: '$PROJECT_NAME/deploy/**', enableConfigSubstitution: true,
        kubeconfigId: "$KUBECONFIG_CREDENTIAL_ID")
      }
    }
# 异常1:部署的镜像不可以是发布版本的镜像
gulimall-auth-server-deploy.xml部署文件里面,镜像应该选择最新镜像而不是发布版本的镜像
异常:image: $REGISTRY/$DOCKERHUB_NAMESPACE/$PROJECT_NAME:$PROJECT_VERSION

正确:image: $REGISTRY/$DOCKERHUB_NAMESPACE/$PROJECT_NAME:latest

原因:因为发布版本是在部署后面做的操作,此时docker hub还未上传发布版本的镜像,应该使用最新版本的镜像

2.6.6、stage5:发布版本【v1.0】

gitee和docker hub都推送一份
版本号是参数化构建时手动输入的参数

1601113192622

    stage('发布版本'){
      when{
        expression{
          return params.PROJECT_VERSION =~ /v.*/
        }
      }
      steps {
        container ('maven') {
          input(id: 'release-image-with-tag', message: '是否发布当前版本?')
          withCredentials([usernamePassword(credentialsId: "$GITEE_CREDENTIAL_ID",
          passwordVariable: 'GIT_PASSWORD', usernameVariable: 'GIT_USERNAME')]) {
            sh 'git config --global user.email "lemon_wan@aliyun.com" '
            sh 'git config --global user.name "lemon_wan" '
            sh 'git tag -a $PROJECT_VERSION -m "$PROJECT_VERSION" '
            sh 'git push http://$GIT_USERNAME:$GIT_PASSWORD@gitee.com/$GITEE_ACCOUNT/gulimall.git --tags --ipv4'
          }
          sh 'docker tag  $REGISTRY/$DOCKERHUB_NAMESPACE/$PROJECT_NAME:SNAPSHOT-$BRANCH_NAME-$BUILD_NUMBER $REGISTRY/$DOCKERHUB_NAMESPACE/$PROJECT_NAME:$PROJECT_VERSION '
          sh 'docker push  $REGISTRY/$DOCKERHUB_NAMESPACE/$PROJECT_NAME:$TAG_NAME '
        }
      }
    }

2.6.7、stage6:部署到生产环境

七、打通应用

1、移植mysql

1、创建一个service暴露mysql-master

2、然后执行 .sql文件恢复数据结构+数据

3、执行完成后可以删除service,也可以不删除

2、部署流程图

1601115915596

3、标准流水线-参数化构建

1、复制Jenkinsfile文件到项目gulimall最外层,默认构建 网关模块

pipeline {
  agent {
    node {
      label 'maven'
    }
  }

  parameters {
    string(name:'PROJECT_VERSION',defaultValue: 'v0.0Beta',description:'项目版本')
    string(name:'PROJECT_NAME',defaultValue: 'gulimall-gateway',description:'构建模块')
  }

  environment {
    DOCKER_CREDENTIAL_ID = 'dockerhub-id'
    GITEE_CREDENTIAL_ID = 'gitee-id'
    KUBECONFIG_CREDENTIAL_ID = 'demo-kubeconfig'
    REGISTRY = 'docker.io'
    DOCKERHUB_NAMESPACE = 'wanzenghui'
    GITEE_ACCOUNT = 'lemon_wan'
    SONAR_CREDENTIAL_ID = 'sonar-qube'
    BRANCH_NAME = 'master'
  }

  stages {
    stage ('拉取代码,编译、打包') {
      steps {
        git(url: 'https://gitee.com/lemon_wan/gulimall.git', credentialsId: 'gitee-id', branch: 'master', changelog: true, poll: false)
        sh 'echo 正在构建 $PROJECT_NAME  版本号:$PROJECT_VERSION 将会提交给 $REGISTRY 镜像仓库'
        container ('maven') {
          sh 'mvn clean install -Dmaven.test.skip=true -gs `pwd`/mvn-settings.xml'
        }
      }
    }

    stage('代码质量分析') {
      steps {
        container ('maven') {
          withCredentials([string(credentialsId: "$SONAR_CREDENTIAL_ID", variable: 'SONAR_TOKEN')]) {
            withSonarQubeEnv('sonar') {
              sh "echo 当前目录 `pwd`"
              sh "mvn sonar:sonar -gs `pwd`/mvn-settings.xml -Dsonar.branch=$BRANCH_NAME -Dsonar.login=$SONAR_TOKEN"
            }
          }
          timeout(time: 1, unit: 'HOURS') {
            waitForQualityGate abortPipeline: true
          }
        }
      }
    }

    stage ('构建镜像 & 推送快照镜像') {
      steps {
        container ('maven') {
          sh 'mvn -Dmaven.test.skip=true -gs `pwd`/mvn-settings.xml clean package'
          sh 'cd $PROJECT_NAME && docker build -f Dockerfile -t $REGISTRY/$DOCKERHUB_NAMESPACE/$PROJECT_NAME:SNAPSHOT-$BRANCH_NAME-$BUILD_NUMBER .'
          withCredentials([usernamePassword(passwordVariable : 'DOCKER_PASSWORD' ,usernameVariable : 'DOCKER_USERNAME' ,credentialsId : "$DOCKER_CREDENTIAL_ID" ,)]) {
            sh 'echo "$DOCKER_PASSWORD" | docker login $REGISTRY -u "$DOCKER_USERNAME" --password-stdin'
            sh 'docker push  $REGISTRY/$DOCKERHUB_NAMESPACE/$PROJECT_NAME:SNAPSHOT-$BRANCH_NAME-$BUILD_NUMBER'
          }
        }
      }
    }

    stage('推送最新镜像'){
      when{
        branch 'master'
      }
      steps{
        container ('maven') {
          sh 'docker tag  $REGISTRY/$DOCKERHUB_NAMESPACE/$PROJECT_NAME:SNAPSHOT-$BRANCH_NAME-$BUILD_NUMBER $REGISTRY/$DOCKERHUB_NAMESPACE/$PROJECT_NAME:latest '
          sh 'docker push  $REGISTRY/$DOCKERHUB_NAMESPACE/$PROJECT_NAME:latest '
        }
      }
    }

    stage('部署到开发环境') {
      when{
        branch 'master'
      }
      steps {
        input(id: 'deploy-to-dev-$PROJECT_NAME', message: '是否将$PROJECT_NAME部署到开发环境?')
        kubernetesDeploy(configs: '$PROJECT_NAME/deploy/**', enableConfigSubstitution: true,
        kubeconfigId: "$KUBECONFIG_CREDENTIAL_ID")
      }
    }

    stage('发布版本'){
      when{
        expression{
          return params.PROJECT_VERSION =~ /v.*/
        }
      }
      steps {
        container ('maven') {
          input(id: 'release-image-with-tag', message: '是否发布当前版本?')
          withCredentials([usernamePassword(credentialsId: "$GITEE_CREDENTIAL_ID",
          passwordVariable: 'GIT_PASSWORD', usernameVariable: 'GIT_USERNAME')]) {
            sh 'git config --global user.email "lemon_wan@aliyun.com" '
            sh 'git config --global user.name "lemon_wan" '
            sh 'git tag -a $PROJECT_VERSION -m "$PROJECT_VERSION" '
            sh 'git push http://$GIT_USERNAME:$GIT_PASSWORD@gitee.com/$GITEE_ACCOUNT/gulimall.git --tags --ipv4'
          }
          sh 'docker tag  $REGISTRY/$DOCKERHUB_NAMESPACE/$PROJECT_NAME:SNAPSHOT-$BRANCH_NAME-$BUILD_NUMBER $REGISTRY/$DOCKERHUB_NAMESPACE/$PROJECT_NAME:$PROJECT_VERSION '
          sh 'docker push  $REGISTRY/$DOCKERHUB_NAMESPACE/$PROJECT_NAME:$TAG_NAME '
        }
      }
    }

  }
}

1、创建流水线

自动化微服务部署,参数化构建

1601128432451

3、选择代码仓库

1601128490424

4、高级设置

保留分支的天数 7、保留分支的最大个数 7

取消定时触发【如果没有扫描触发,则定期扫描】

开启浅克隆【不需要提交代码,只是构建】

1601128662809

3.1、异常1:$PROJECT_NAME没有动态取出

原因:要使用双引号
    stage('部署到开发环境') {
      when{
        branch 'master'
      }
      steps {
        input(id: "deploy-to-dev-$PROJECT_NAME", message: "是否将$PROJECT_NAME部署到开发环境?")
        kubernetesDeploy(configs: "$PROJECT_NAME/deploy/**", enableConfigSubstitution: true,
        kubeconfigId: "$KUBECONFIG_CREDENTIAL_ID")
      }
    }

1601129183709

3.2、异常2:端口非法

1601129610707

部署对外暴露的端口非法 30000-32767
修改部署文件的nodePort

kind: Deployment
apiVersion: apps/v1
metadata:
  name: gulimall-auth-server
  namespace: gulimall
  labels:
    app: gulimall-auth-server
spec:
  replicas: 1
  selector:
    matchLabels:
      app: gulimall-auth-server
  template:
    metadata:
      labels:
        app: gulimall-auth-server
    spec:
      containers:
        - name: gulimall-auth-server
          image: $REGISTRY/$DOCKERHUB_NAMESPACE/$PROJECT_NAME:latest
          ports:
            - containerPort: 8080
              protocol: TCP
          resources:
            limits:
              cpu: 1000m
              memory: 500Mi
            requests:
              cpu: 10m
              memory: 10Mi
          terminationMessagePath: /dev/termination-log
          terminationMessagePolicy: File
          imagePullPolicy: IfNotPresent
      restartPolicy: Always
      terminationGracePeriodSeconds: 30
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 25%
      maxSurge: 25%
  revisionHistoryLimit: 10
  progressDeadlineSeconds: 600
---
kind: Service
apiVersion: v1
metadata:
  name: gulimall-auth-server
  namespace: gulimall
  labels:
    app: gulimall-auth-server
spec:
  ports:
    - protocol: TCP
      port: 8080
      targetPort: 8080
      nodePort: 31000
  selector:
    app: gulimall-auth-server
  type: NodePort
  sessionAffinity: None

3.3、异常3:OOMKilled

1601130040860

1601130465648

容器部署异常,内存溢出
原因:
	jvm启动的时候,分配内存过大,而k8s里面部署的限制是500Mi【xx-deploy.yaml】,导致内存溢出直接将pod给杀了

解决:
	构建docker镜像的时候,约束微服务jvm最大占用内存

FROM java:8
EXPOSE 8080

VOLUME /tmp
ADD target/*.jar  /app.jar
RUN bash -c 'touch /app.jar'
ENTRYPOINT ["java","-jar","-Xms128m","-Xmx300m","/app.jar","--spring.profiles.active=prod"]

3.4、异常4:发布版本失败,已存在

1601201619774

1601201664840

原因:
	发布的时候没有加上项目名,不同项目发布同样版本发布失败,已存在。
	例如gulimall-cart发布v1.0成功,但是因为标签没有加项目名字,导致发布gulimall-product的v1.0版本没哟项目名作为区分,发布版本失败。
	修改:
    stage('发布版本'){
      when{
        expression{
          return params.PROJECT_VERSION =~ /v.*/
        }
      }
      steps {
        container ('maven') {
          input(id: 'release-image-with-tag', message: '是否发布当前版本?')
          sh 'docker tag  $REGISTRY/$ALIYUNHUB_NAMESPACE/$PROJECT_NAME:SNAPSHOT-$BRANCH_NAME-$BUILD_NUMBER $REGISTRY/$ALIYUNHUB_NAMESPACE/$PROJECT_NAME:$PROJECT_VERSION '
          sh 'docker push  $REGISTRY/$ALIYUNHUB_NAMESPACE/$PROJECT_NAME:$TAG_NAME '
          withCredentials([usernamePassword(credentialsId: "$GITEE_CREDENTIAL_ID",
          passwordVariable: 'GIT_PASSWORD', usernameVariable: 'GIT_USERNAME')]) {
            sh 'git config --global user.email "lemon_wan@aliyun.com" '
            sh 'git config --global user.name "lemon_wan" '
            sh 'git tag -a $PROJECT_NAME-$PROJECT_VERSION -m "$PROJECT_VERSION" '
            sh 'git push http://$GIT_USERNAME:$GIT_PASSWORD@gitee.com/$GITEE_ACCOUNT/gulimall.git --tags --ipv4'
          }
        }
      }
    }

3.5、异常5:redisson分布式锁配置类

1601260641967

分布式锁的配置要改连接的是192.168.56.10
修改gulimall-product和gulimall-seckill的MyRedissonConfig.java配置类
	自动获取配置文件中redis的host
@Configuration
public class MyRedissonConfig {

    /**
     * 所有对Redisson的使用都是通过RedissonClient对象
     * @return
     * @throws IOException
     */
    @Bean(destroyMethod="shutdown")
    RedissonClient redisson(@Value("spring.redis.host")String host) throws IOException {
        // 1、创建配置
        Config config = new Config();
        // 集群模式
//        config.useClusterServers()
//                .addNodeAddress("127.0.0.1:7004", "127.0.0.1:7001");
        config.useSingleServer().setAddress("redis://" + host + ":6379");
        // 2、根据config创建出RedissonClient实例
        return Redisson.create(config);
    }

}

3.6、异常6:阿里云镜像仓库里镜像私有

1601261298229

私有的镜像,拉取会失败
改为公开就可以了

1601261353443

4、部署+暴露 所有微服务

1、运行

1601130879661

2、输入参数,开始自动构建、部署、发布

1601130951313

5、构建nginx镜像并推送远程

192.168.56.10之前部署了nginx容器,其中 转发配置+静态资源都已经挂在好了,现在将这个容器直接打包成镜像,然后上传至远程仓库【docker exec -it nginx /bin/bash】

1、把一个正在运行的容器 在本地打包一个镜像
-a:作者
-m:提交的一些信息
容器id
docker commit -a "wanzenghui" -m "nginx" [CONTAINER NAME] wanzenghui/gulimall-nginx:v1.0

2、登录docker hub
docker login -u wanzenghui

3、推送
docker push wanzenghui/gulimall-nginx:v1.0

4、但是此时想起来有些需要修改的配置,与网关的转发规则
cd /mydata/nginx/conf
cp nginx.conf nginx.conf.bak
vi nginx.conf
修改上游服务器
upstream gulimall{
	server 192.168.56.100:31003;
	server 192.168.56.101:31003;
	server 192.168.56.102:31003;
}
之前是192.168.56.1:88

5、重新构建镜像
docker commit -a "wanzenghui" -m "nginx-update-upstream" 97b83151128b gulimall-nginx:v1.2
这里演示,名字打错了,要改成wanzenghui/gulimall-nginx:v1.2
	查看镜像docker [imagesID]
	docker tag gulimall-nginx:v1.2 wanzenghui/gulimall-nginx:v1.0
	
6、3、推送
docker push wanzenghui/gulimall-nginx:v1.2

本地镜像:wanzenghui/gulimall-nginx

1601132245085

6、使用阿里云容器镜像服务

docker hub太慢了,使用阿里云镜像仓库
1、登录阿里云,开通 【容器镜像服务】
https://cr.console.aliyun.com/cn-hangzhou/instances/repositories

2、创建镜像仓库
   创建命名空间 wanzenghui
   选择本地仓库

3、使用阿里云镜像仓库 上传nginx镜像
	1)登录
	docker login --username=mrwanzh registry.cn-shanghai.aliyuncs.com
	然后输入密码【之前在阿里云创建 之前在阿里云创建 容器镜像服务设置的密码】
	
	2)拉取镜像
	docker pull registry.cn-shanghai.aliyuncs.com/wanzenghui/gulimall-nginx:v1.0
	3)推送
	docker commit -a "wanzenghui" -m "nginx" 97b83151128b wanzenghui/gulimall-nginx:v1.0
	docker images查看上面的image ID
	docker tag [ImageId] registry.cn-shanghai.aliyuncs.com/wanzenghui/gulimall-nginx:v1.0
	docker push registry.cn-shanghai.aliyuncs.com/wanzenghui/gulimall-nginx:v1.0
	4)上传成功
	
4、修改Jenkinsfile文件
	1)镜像仓库地址 + 名称空间
	之前是REGISTRY='docker.io'修改为REGISTRY='registry.cn-shanghai.aliyuncs.com'
	DOCKERHUB_NAMESPACE = 'wanzenghui'
	
	2)创建凭证aliyun的凭证
		点击DevOps工程,选择工程,点击凭证,点击创建
		名称:aliyun-hub-id
		类型:账户凭证
		用户名:mrwanzh
		密码:创建阿里云镜像仓库时设置的密码
		
	3)使用aliyun的凭证
	DOCKER_CREDENTIAL_ID = 'aliyun-hub-id'

5、然后重新创建一个流水线,

1601133714085

1601133783689

1601134514004

k8s中为阿里云创建凭证:

1601135111159

7、部署nginx

第5、6步将nginx上传到了阿里云的镜像仓库,这里要从阿里云拉取镜像并部署+暴露服务

1、配置中心=》密钥=》创建
	名称:aliyun-docker
	类型:镜像仓库密钥
	仓库地址:registry.cn-shanghai.aliyuncs.com
			这里使用的是公网地址,如果是阿里云集群内可以使用专有地址
	用户名:mrwanzh
	密码:创建镜像仓库时设置的密码

2、创建无状态服务
	名称:gulimall-nginx
	容器镜像:找到刚刚配置的镜像仓库
	输入镜像名字:wanzenghui/gulimall-nginx:v1.0
	暴露外网访问NodePort
	创建成功,查看port 30498
	
3、测试访问
192.168.56.101:30498

8、部署ingress

直接使用kubesphere创建应用路由而不用编写文件了

1、使用project-admin管理员账户登录kubesphere
	1)进入项目gulimall=》项目设置=》高级设置=》设置网关=》访问方式:loadBalancer
	2)删除默认的两个注解qingcloud

2、创建应用路由
	1)进入项目gulimall=》应用负载=》应用路由=》创建路由
	2)名称:gulimall-com
	3)添加路由规则
		指定域名:
			域名:gulimall.com
			协议:http
			路径:/		gulimall-nginx		80
	4)下一步=》创建
    
3、配置本地host环境,不可以是master的ip,master不要安装ingress,使用slaverIP
192.168.56.101 gulimall.com
	

8.1、异常1:访问静态资源失败

1601277556427

gulimall.com在本地配置成了192.168.56.101
然后访问gulimall.com/static/search/image/bg-attr.png出现异常
原因查询:
	进入gulimall-nginx,查看容器日志

	原因:挂载路径不对,例如以前192.168.56.10  docker exec -it nginx /bin/bash,进入容器根目录
	静态资源的路径是:/usr/share/nginx/html/static,所以需要挂载/usr/share/nginx/html
	
	然后使用kubesphere进入容器终端内部,cd到/usr/share/nginx/html文件夹内部,发现并没有我们原本部署的静态资源static、es文件夹等等。docker commit这个构建镜像的命令,并不能把资源也构建到镜像中。
	问题解决:
	1、连接192.168.56.10,将/mydata/nginx下的两个文件夹压缩:
		1)conf.tar.gz  和  html.tar.gz
		2)创建dockerfile,写入以下
【解析:从官方拉取nginx镜像,然后使用conf.tar.gz解压后的数据添加到/usr/share/nginx/html
然后使用conf.tar.gz解压后的数据添加到/etc/nginx】

FROM nginx
MAINTAINER leifengyang
ADD html.tar.gz /usr/share/nginx/html
ADD conf.tar.gz /etc/nginx
EXPOSE 80
ENTRYPOINT nginx -g "daemon off;"

	2、将刚刚3个文件都上传到192.168.56.10 	/opt/nginx,开始打包镜像
	docker build -t gulimall-nginx:v1.0 -f Dockerfile .
	
	3、将该镜像再构建一份阿里云版本
	docker tag [ImageId] registry.cn-shanghai.aliyuncs.com/wanzenghui/gulimall-nginx:v1.0
	
	4、登录阿里云
	docker login --username=mrwanzh registry.cn-shanghai.aliyuncs.com
	然后输入密码
	
	5、推送
	docker push registry.cn-shanghai.aliyuncs.com/wanzenghui/gulimall-nginx:v1.0
	
	6、删除gulimall-nginx服务+部署【也可以直接在原服务上修改】
		1)重新部署无状态服务,容器镜像选择阿里云,镜像选择wanzenghui/gulimall-nginx:v1.0
		使用默认端口
		勾选外网访问+NodePort
		2)打开容器终端,cd /usr/share/nginx/html 查看是否有静态资源
	
	7、删除路由【ingress服务】
	 重新创建 应用路由【服务=》应用负载=》应用路由=》创建】
	 名称:gulimall.com
	 路由规则:
	 	指定域名:
	 		域名:gulimall.com
	 		协议:http
	 		路径:/	gulimall-nginx	80
	 		
	 8、测试
	 本机访问 gulimall.com
	 	如果能访问首页,并且加载静态资源就成功了
	

1601278746457

8.2、异常2:点击首页搜索商品无法访问

1、本地host设置域名,将所有请求都转发到ingress
192.168.56.101 gulimall.com
192.168.g6.101 search.gulimall.com
192.168.56.101 item.gulimall.com
192.168.56.101 auth.gulimall.com
192.168.56.101 cart.gulimall.com
192.168.56.101 order.gulimall.com
192.168.56.101 member.gulimall.com
192.168.56.101 seckill.gulimall.com

2、为每一个服务都建立路由规则
	将所有到达ingress的请求都转发给nginx

1601279552888

9、部署vue项目【可以使用Jenkins】

可以使用Jenkins,但以下是手动的

1、修改static/config/index-prod.js中api接口请求地址
  // api接口请求地址
  window.SITE_CONFIG['baseUrl'] = '192.168.56.100:31003/api';
  
2、构建
npm run build

2、复制/renren-fast-vue/dist 文件夹的所有内容
	1)将dist文件夹内所有内容 压缩成dist.tar.gz【高可用集群篇\docs\code\admin-vue-app】
	2)在admin-vue-app文件夹下创建Dockerfile
FROM nginx
MAINTAINER leifengyang
ADD dist.tar.gz /usr/share/nginx/html
EXPOSE 80
ENTRYPOINT nginx -g "daemon off;"

3、构建镜像
	将admin-vue-app文件夹上传到192.168.56.10的/opt目录下
	docker build -t gulimall-admin-vue-app:v1.0 -f Dockerfile .

4、登录aliyun
   构建aliyun tag
   push推送
   修改仓库镜像为共有【否则无法被拉取】
   
5、k8s部署,无状态服务
	1)创建一个无状态服务
	名称:gulimall-admin-vue-app
	添加容器镜像:wanzenghui/gulimall-admin-vue-app:v1.0
	使用默认端口
	选择外网访问,访问方式NodePort
	
	2)测试访问
	192.168.56.100:port

	3)使用ingress转发
	添加路由规则,admin.gulimall.com转发给gulimall-admin-vue-app服务,80端口
	本地使用admin.gulimall.com访问
	

1601281595946

1601281974504

10、滚动更新部署

1、打开服务,选择gulimall-admin-vue-app

1601282466432

2、点击更多操作 =》 编辑配置模板

1601282512533

3、容器组模板,选中镜像,编辑

1601282575221

4、改成新版本就行了v1.3

1601282605062

5、拉起一个新的容器

如果构建成功会删除旧版本

1601282655591

1601282682825

八、kubesphere监控告警功能

需求:当线上服务出现问题邮件通知

1、开启POP3/SMTP服务【接受邮件功能】
保存 授权码

3、打开kubesphere
	1)登录admin账户kubesphere
	2)邮件服务器,服务器配置
	3)发送测试邮件

4、登录project-regular
	1)监控告警=》告警策略=》添加策略
	名称:mem
	监控目标:选中一个工作负载,例如gulimall-order应用
	告警规则:
		添加规则:
			规则:内存用量		1分钟/周期		连续1次	>	10Mib	危险告警
			

1601284906024

总结

1、为什么要有pvc

对于有状态服务,在服务器宕机后从新服务器重启能恢复数据,数据从容器内部挂载到外部

2、域名生效情况

1、有状态服务

2、无状态服务【参考nacos的service】
	1)未暴露NodePod
	2)访问类型:Headless【以域名的方式访问】

3、k8s的密钥与凭证

1601263448946

1601263684896

密钥:
	1、多用于kubesphere 部署和暴露service时用到,例如 登录阿里云容器镜像仓库的账号密码,mysql的密码
	2、还可以创建 镜像仓库密钥,参照七.7
	
凭证:参照 七.6
	为Jenkinsfile提供,该文件中的 环境变量与例如DOCKER_CREDENTIAL_ID = 'aliyun-hub-id'
	辅助构建流水线。

具体不同可以查看yaml的创建方式

4、ConfigMap的作用

例如在部署mysql容器时,多个主节点会使用相同的配置文件my.cnf,所以可以现在kubernetes集群中创建ConfigMap,然后部署pod的时候挂载相关配置,就不用手动为每一个容器创建配置文件了

demo:

	key:master-mysql.cnf
	value:
[client]
default-character-set=utf8

[mysql]
default-character-set=utf8

[mysqld]
init_connect='SET collation_connection = utf8_unicode_ci'
init_connect='SET NAMES utf8'
character-set-server=utf8
collation-server=utf8_unicode_ci
skip-character-set-client-handshake
skip-name-resolve

server-id=1
log-bin=mysql-bin
read-only=0
binlog-do-db=gulimall_oms
binlog-do-db=gulimall_pms
binlog-do-db=gulimall_sms
binlog-do-db=gulimall_ums
binlog-do-db=gulimall_wms
binlog-do-db=gulimall_admin

replicate-ignore-db=mysql
replicate-ignore-db=sys
replicate-ignore-db=performance_schema
replicate-ignore-db=information_schema

5、Jenkinsfile使用流程

1、提交代码到gitee

2、在 流水线 上点击运行 就可以开始构建项目【选择 分支、版本、项目】
    1、checkout scm:从github拉取代码
    2、unit test:单元测试
    3、sonarqube analysis:代码审查
    4、build and push snapshot:构建镜像推送到阿里云镜像仓库中【登录docker hub查看tag】
    5、push the latest image:推送最新镜像【最新镜像被覆盖,阿里云好像没有覆盖功能】
    6、deploy to dev:手动确认是否部署到开发环境【namespace一样的项目】
    7、push the tag:手动确认release【候选】代码是否推送到gitee的tag上(发行版本)【git tag -a v0.0.2 -m v0.0.2】同时0.0.2也会推送到docker hub上,所以可能会存在两份一样的
    8、deploy to production:是否部署到生产环境【namespace一样的项目】

6、集群总结

1、主从
2、分片
3、选主

因为内存限制,k8s没有搭建redis、es、rabbitmq的集群,参照docker安装集群的方式即可

1601286792885

Java
1
https://gitee.com/StickNine_admin/gulimall_out.git
git@gitee.com:StickNine_admin/gulimall_out.git
StickNine_admin
gulimall_out
gulimall_out
dev

搜索帮助