谷粒商城(三) 部署
莫等闲,白了少年头,空悲切。
正文
环境搭建和基本介绍
使用vagrant快速创建linux虚拟机
安装virtualbox
https://www.virtualbox.org/wiki/Downloads
下载安装vagrant
本人使用3个Centos7虚拟机,每台内存20G
传统部署时代: 早期,组织在物理服务器上运行应用程序。无法为物理服务器中的应用程序定义资源边界,这会导致资源分配问题。例如,如果在物理服务器上运行多个应用程序,则可能会出现一个应用程序占用大部分资源的情况,结果可能导致其他应用程序的性能下降。一种解决方案是在不同的物理服务器上运行每个应用程序,但是由于资源利用不足而无法扩展,并且组织维护许多物理服务器的成本很高。
虚拟化部署时代: 作为解决方案,引入了虚拟化功能,它允许您在单个物理服务器的 CPU 上运行多个虚拟机(VM)。虚拟化功能允许应用程序在 VM 之间隔离,并提供安全级别,因为一个应用程序的信息不能被另一应用程序自由地访问。
因为虚拟化可以轻松地添加或更新应用程序、降低硬件成本等等,所以虚拟化可以更好地利用物理服务器中的资源,并可以实现更好的可伸缩性。
每个 VM 是一台完整的计算机,在虚拟化硬件之上运行所有组件,包括其自己的操作系统。
容器部署时代: 容器类似于 VM,但是它们具有轻量级的隔离属性,可以在应用程序之间共享操作系统(OS)。因此,容器被认为是轻量级的。容器与 VM 类似,具有自己的文件系统、CPU、内存、进程空间等。由于它们与基础架构分离,因此可以跨云和 OS 分发进行移植。
下面列出了容器的一些好处:
- 敏捷应用程序的创建和部署:与使用 VM 镜像相比,提高了容器镜像创建的简便性和效率。
- 持续开发、集成和部署:通过快速简单的回滚(由于镜像不可变性),提供可靠且频繁的容器镜像构建和部署。
- 关注开发与运维的分离:在构建/发布时而不是在部署时创建应用程序容器镜像,从而将应用程序与基础架构分离。
- 可观察性不仅可以显示操作系统级别的信息和指标,还可以显示应用程序的运行状况和其他指标信号。
- 跨开发、测试和生产的环境一致性:在便携式计算机上与在云中相同地运行。
- 云和操作系统分发的可移植性:可在 Ubuntu、RHEL、CoreOS、本地、Google Kubernetes Engine 和其他任何地方运行。
- 以应用程序为中心的管理:提高抽象级别,从在虚拟硬件上运行 OS 到使用逻辑资源在 OS 上运行应用程序。
- 松散耦合、分布式、弹性、解放的微服务:应用程序被分解成较小的独立部分,并且可以动态部署和管理 - 而不是在一台大型单机上整体运行。
- 资源隔离:可预测的应用程序性能。
- 资源利用:高效率和高密度。
容器时代,容器管理的需求凸显。
Kubernetes 为您提供:
- 服务发现和负载均衡
Kubernetes 可以使用 DNS 名称或自己的 IP 地址公开容器,如果到容器的流量很大,Kubernetes 可以负载均衡并分配网络流量,从而使部署稳定。
- 存储编排
Kubernetes 允许您自动挂载您选择的存储系统,例如本地存储、公共云提供商等。
- 自动部署和回滚
您可以使用 Kubernetes 描述已部署容器的所需状态,它可以以受控的速率将实际状态更改为所需状态。例如,您可以自动化 Kubernetes 来为您的部署创建新容器,删除现有容器并将它们的所有资源用于新容器。
- 自动二进制打包
Kubernetes 允许您指定每个容器所需 CPU 和内存(RAM)。当容器指定了资源请求时,Kubernetes 可以做出更好的决策来管理容器的资源。
- 自我修复
Kubernetes 重新启动失败的容器、替换容器、杀死不响应用户定义的运行状况检查的容器,并且在准备好服务之前不将其通告给客户端。 - 密钥与配置管理 Kubernetes 允许您存储和管理敏感信息,例如密码、OAuth 令牌和 ssh 密钥。您可以在不重建容器镜像的情况下部署和更新密钥和应用程序配置,也无需在堆栈配置中暴露密钥。
K8S架构
一个K8S集群由两部分构成 master节点和node节点。
master节点主要负责集群的控制,对pod进行调度,已经令牌管理等等功能。
node节点主要是负责干活,启动容器、管理容器。
master节点和node节点一般不要部署在一台机器上。
下面一个master,两个node。
将这张图抽象一下,大约是这个样子:

master节点中有很多组件
一个Pod中的应用容器共享同一组资源:
(1)PID命名空间:Pod中的不同应用程序可以看见其他应用程序的进程ID
(2)网络命名空间:Pod中的多个容器能访问同一个IP和端口范围
(3)IPC命名空间:Pod中的多个容器能够使用SystemV IPC或POSIX消息队列进行通信
(4)UTS命名空间:Pod中的多个容器共享一个主机名
(5)Volumes(共享存储卷):Pod中的各个容器可以访问在Pod级别定义的Volumes。容器运行的过程中会产生数据,这些数据就保存在Volume中
每个节点运行着docker,docker中运行着容器,这些容器被kubelet组合成pod,pod负责创建销毁维护声明周期。将pod暴露出去后,提供一个访问地址,访问该地址,kube-proxy会解析地址最终访问到指定的容器,kube-proxy还能进行负载均衡。
- ReplicaSet:确保预期的pod副本数量
- Deployent:无状态应用部署。
Deployment是Kubernetes v1.2引入的概念,引入的目的是为了更好地解决Pod的编排问题。为此,Deployment在内部使用了Replica Set来实现目的,无论从Deployment的作用与目的,它的YAML定义,还是从它的具体命令行操作来看,我们都可以把它看作RC的一次升级,两者相似度超过90%。
Deployment相对于RC的一个最大升级是我们随时知道当前Pod“部署”的进度。实际上由于一个Pod的创建、调度、绑定节点及在目标Node上启动对应的容器这一完整过程需要一定的时间,所以我们期待系统启动N个Pod副本的目标状态,实际上是一个连续变化的“部署过程”导致的最终状态。
Deployment的典型使用场景有以下几个。
- 创建一个Deployment对象来生成对应的Replica Set并完成Pod副本的创建过程。
- 检查Deployment的状态来看部署动作是否完成(Pod副本的数量是否达到预期的值)。
- 更新Deployment以创建新的Pod(比如镜像升级)。
- 如果当前Deployment不稳定,则回滚到一个早先的Deployment版本。
- 暂停Deployment以便于一次性修改多个PodTemplateSpec的配置项,之后再恢复Deployment,进行新的发布。
- 扩展Deployment以应对高负载。
- 查看Deployment的状态,以此作为发布是否成功的指标。
- 清理不再需要的旧版本ReplicaSets。
- StatefulSet:有状态应用部署。
在Kubernetes系统中,Pod的管理对象RC、Deployment、DaemonSet和Job都是面向无状态的服务。但现实中有很多服务是有状态的,特别是一些复杂的中间件集群,例如MySQL集群、MongoDB集群、Kafka集群、Zookeeper集群等,这些应用集群有以下一些共同点。
每个节点都有固定的身份ID,通过这个ID,集群中的成员可以相互发现并且通信。
集群的规模是比较固定的,集群规模不能随意变动。
集群里的每个节点都是有状态的,通常会持久化数据到永久存储中。
如果磁盘损坏,则集群里的某个节点无法正常运行,集群功能受损。
如果用RC/Deployment控制Pod副本数的方式来实现上述有状态的集群,则我们会发现第一点是无法满足的,因为Pod的名字是随机产生的,Pod的IP地址也是在运行期才确定且可能有变动的,我们事先无法为每个Pod确定唯一不变的ID,为了能够在其他节点上恢复某个失败的节点,这种集群中的Pod需要挂接某种共享存储,为了解决这个问题,Kubernetes从v1.4版本开始引入了PetSet这个新的资源对象,并且在v1.5版本时更名为StatefulSet,StatefulSet从本质上来说,可以看作Deployment/RC的一个特殊变种,它有如下一些特性。
StatefulSet里的每个Pod都有稳定、唯一的网络标识,可以用来发现集群内的其他成员。假设StatefulSet的名字叫kafka,那么第一个Pod叫kafak-0,第二个Pod叫kafak-1,以此类推。
StatefulSet控制的Pod副本的启停顺序是受控的,操作第n个Pod时,前n-1个Pod已经时运行且准备好的状态。
StatefulSet里的Pod采用稳定的持久化存储卷,通过PV/PVC来实现,删除Pod时默认不会删除与StatefulSet相关的存储卷(为了保证数据的安全)。
- Job:一次性任务
- Cronjob:定时任务
Service
定义一组pod访问策略
pod的负载均衡,提供一个或多个pod的稳定访问地址
Kubernetes的Service定义了一个服务的访问入口地址,前端的应用通过这个入口地址访问其背后的一组由Pod副本组成的集群实例,Service与其后端Pod副本集群之间则是通过Label Selector来实现“无缝对接”的。而RC的作用实际上是保证Service的服务能力和服务质量始终处于预期的标准。
pod组合一些controller,service又挑出一些pod进行组合,提供负载均衡,比如想访问购物车功能,只访问service即可,service会知道组合的pod在哪些服务器中,访问一个即可。一次部署就是将pod部署到各个节点,Deployment产生pod,service对pod进行组合。
Label
Label是Kubernetes系统中另外一个核心概念。一个Label是一个key=value的键值对,其中key与vaue由用户自己指定。Label可以附加到各种资源对象上,例如Node、Pod、Service、RC等,一个资源对象可以定义任意数量的Label,同一个Label也可以被添加到任意数量的资源对象上去,Label通常在资源对象定义时确定,也可以在对象创建后动态添加或者删除。
我们可以通过指定的资源对象捆绑一个或多个不同的Label来实现多维度的资源分组管理功能,以便于灵活、方便地进行资源分配、调度、配置、部署等管理工作。例如:部署不同版本的应用到不同的环境中;或者监控和分析应用(日志记录、监控、告警)等。一些常用等label示例如下。
- 版本标签:"release" : "stable" , "release" : "canary"...
- 环境标签:"environment" : "dev" , "environment" : "production"
- 架构标签:"tier" : "frontend" , "tier" : "backend" , "tier" : "middleware"
- 分区标签:"partition" : "customerA" , "partition" : "customerB"...
- 质量管控标签:"track" : "daily" , "track" : "weekly"
Label相当于我们熟悉的“标签”,給某个资源对象定义一个Label,就相当于給它打了一个标签,随后可以通过Label Selector(标签选择器)查询和筛选拥有某些Label的资源对象,Kubernetes通过这种方式实现了类似SQL的简单又通用的对象查询机制。
namespace
命名空间,用于隔离
通过kubectl提交一个创建RC的请求,请求通过API Server被写入etcd中
此时,Controller Manager通过API server的监听自由变化的接口监听到此RC事件
发现当前集群中没有它所对应的pod实例
根据RC里的pod模板定义生成一个pod对象,通过API Server写入etcd
此事件被Scheduler发现,它立即执行一个复杂的调度流程,为这个新pod选定一个落户的node,然后通过API Server将这一结果写入到etcd中
目标node上运行的kubelet进程通过API Server监测到这个新生的pod,并按照它的定义,启动该pod并一直对其负责直到生命结束
随后,kubetcl提交一个新的映射到该pod的service的创建请求
Controller Manager通过Label标签查询到关联的pod实例,然后生成service的Endpoints信息,并通过API Service写入到etcd中
接下来,所有node上运行的proxy进程通过API Server查询并监听Service对象与其对应的Endpoints信息,建立一个软件方式的负载均衡器来实现Service访问到后端pod的流量转发功能
k8s中所有资源对象都可以采用yaml或JSON格式的文件定义或描述
集群安装
kubeadm是快速部署k8s集群的工具
创建一个master节点
kubeadm init
一个node节点加入集群
kubeadm join <master节点ip和端口>
一个maser,两个node
步骤:
- 所有节点安装docker和kubeadm
- 部署master
- 部署容器网络插件
- 部署node,将节点加入k8s集群
- 部署web界面,可视化k8s资源
关闭防火墙、selinux
关闭swap
添加主机名和ip对应关系
将桥接的ipv4流量传递到iptables的链
cat > /etc/sysctl.d/k8s.conf << EOF net.bridge.bridge-nf-call-ip6tables=1 net.bridge.bridge-nf-call-iptables=1 EOF
docker依赖
yum install -y yum-utils device-mapper-persistent-data lvm2
设置docker的yum源
yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
安装docker以及docker-cli
yum install -y docker-ce docker-ce-cli containerd.io
配置docker加速
mkdir -p /etc/docker tee /etc/docker/daemon.json <<- 'EOF' { "registry-mirrors":["https://82m9ar63.mirror.aliyuncs.com"] } EOF systemctl daemon-reload systemctl restart docker
开机启动
systemctl enable docker
添加阿里云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
安装kubeadm、kubelet和kubectl
yum list|grep kube yum install -y kubelet-1.17.3 kubectl-1.17.3 kubeadm-1.17.3
开机启动
systemctl enable kubelet
systemctl start kubelet
./master_images.sh 下载节点镜像
#!/bin/bash images=( kube-apiserver:v1.17.3 kube-proxy:v1.17.3 kube-controller-manager:v1.17.3 kube-scheduler:v1.17.3 coredns:1.6.5 etcd:3.4.3-0 pause:3.1 ) for imageName in ${images[@]} ; do docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/$imageName # docker tag registry.cn-hangzhou.aliyuncs.com/google_containers/$imageName k8s.gcr.io/$imageName done
master节点初始化
kubeadm init \
--apiserver-advertise-address=192.168.147.8 \ apiserver是master的组件,因此就是master的地址
--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
下面会出一些命令,根据命令完成后面的步骤
mkdir -p $HOME/.kube sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config sudo chown $(id -u):$(id -g) $HOME/.kube/config
安装pod网络插件
kubectl apply -f kube-flannel.yml
--- apiVersion: policy/v1beta1 kind: PodSecurityPolicy metadata: name: psp.flannel.unprivileged annotations: seccomp.security.alpha.kubernetes.io/allowedProfileNames: docker/default seccomp.security.alpha.kubernetes.io/defaultProfileName: docker/default apparmor.security.beta.kubernetes.io/allowedProfileNames: runtime/default apparmor.security.beta.kubernetes.io/defaultProfileName: runtime/default spec: privileged: false volumes: - configMap - secret - emptyDir - hostPath allowedHostPaths: - pathPrefix: "/etc/cni/net.d" - pathPrefix: "/etc/kube-flannel" - pathPrefix: "/run/flannel" readOnlyRootFilesystem: false # Users and groups runAsUser: rule: RunAsAny supplementalGroups: rule: RunAsAny fsGroup: rule: RunAsAny # Privilege Escalation allowPrivilegeEscalation: false defaultAllowPrivilegeEscalation: false # Capabilities allowedCapabilities: ['NET_ADMIN'] defaultAddCapabilities: [] requiredDropCapabilities: [] # Host namespaces hostPID: false hostIPC: false hostNetwork: true hostPorts: - min: 0 max: 65535 # SELinux seLinux: # SELinux is unused in CaaSP rule: 'RunAsAny' --- kind: ClusterRole apiVersion: rbac.authorization.k8s.io/v1beta1 metadata: name: flannel rules: - apiGroups: ['extensions'] resources: ['podsecuritypolicies'] verbs: ['use'] resourceNames: ['psp.flannel.unprivileged'] - apiGroups: - "" resources: - pods verbs: - get - apiGroups: - "" resources: - nodes verbs: - list - watch - apiGroups: - "" resources: - nodes/status verbs: - patch --- kind: ClusterRoleBinding apiVersion: rbac.authorization.k8s.io/v1beta1 metadata: name: flannel roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: flannel subjects: - kind: ServiceAccount name: flannel namespace: kube-system --- apiVersion: v1 kind: ServiceAccount metadata: name: flannel namespace: kube-system --- kind: ConfigMap apiVersion: v1 metadata: name: kube-flannel-cfg namespace: kube-system labels: tier: node app: flannel data: cni-conf.json: | { "name": "cbr0", "cniVersion": "0.3.1", "plugins": [ { "type": "flannel", "delegate": { "hairpinMode": true, "isDefaultGateway": true } }, { "type": "portmap", "capabilities": { "portMappings": true } } ] } net-conf.json: | { "Network": "10.244.0.0/16", "Backend": { "Type": "vxlan" } } --- apiVersion: apps/v1 kind: DaemonSet metadata: name: kube-flannel-ds-amd64 namespace: kube-system labels: tier: node app: flannel spec: selector: matchLabels: app: flannel template: metadata: labels: tier: node app: flannel spec: affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: beta.kubernetes.io/os operator: In values: - linux - key: beta.kubernetes.io/arch operator: In values: - amd64 hostNetwork: true tolerations: - operator: Exists effect: NoSchedule serviceAccountName: flannel initContainers: - name: install-cni image: quay.io/coreos/flannel:v0.11.0-amd64 command: - cp args: - -f - /etc/kube-flannel/cni-conf.json - /etc/cni/net.d/10-flannel.conflist volumeMounts: - name: cni mountPath: /etc/cni/net.d - name: flannel-cfg mountPath: /etc/kube-flannel/ containers: - name: kube-flannel image: quay.io/coreos/flannel:v0.11.0-amd64 command: - /opt/bin/flanneld args: - --ip-masq - --kube-subnet-mgr resources: requests: cpu: "100m" memory: "50Mi" limits: cpu: "100m" memory: "50Mi" securityContext: privileged: false capabilities: add: ["NET_ADMIN"] env: - name: POD_NAME valueFrom: fieldRef: fieldPath: metadata.name - name: POD_NAMESPACE valueFrom: fieldRef: fieldPath: metadata.namespace volumeMounts: - name: run mountPath: /run/flannel - name: flannel-cfg mountPath: /etc/kube-flannel/ volumes: - name: run hostPath: path: /run/flannel - name: cni hostPath: path: /etc/cni/net.d - name: flannel-cfg configMap: name: kube-flannel-cfg --- apiVersion: apps/v1 kind: DaemonSet metadata: name: kube-flannel-ds-arm64 namespace: kube-system labels: tier: node app: flannel spec: selector: matchLabels: app: flannel template: metadata: labels: tier: node app: flannel spec: affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: beta.kubernetes.io/os operator: In values: - linux - key: beta.kubernetes.io/arch operator: In values: - arm64 hostNetwork: true tolerations: - operator: Exists effect: NoSchedule serviceAccountName: flannel initContainers: - name: install-cni image: quay.io/coreos/flannel:v0.11.0-arm64 command: - cp args: - -f - /etc/kube-flannel/cni-conf.json - /etc/cni/net.d/10-flannel.conflist volumeMounts: - name: cni mountPath: /etc/cni/net.d - name: flannel-cfg mountPath: /etc/kube-flannel/ containers: - name: kube-flannel image: quay.io/coreos/flannel:v0.11.0-arm64 command: - /opt/bin/flanneld args: - --ip-masq - --kube-subnet-mgr resources: requests: cpu: "100m" memory: "50Mi" limits: cpu: "100m" memory: "50Mi" securityContext: privileged: false capabilities: add: ["NET_ADMIN"] env: - name: POD_NAME valueFrom: fieldRef: fieldPath: metadata.name - name: POD_NAMESPACE valueFrom: fieldRef: fieldPath: metadata.namespace volumeMounts: - name: run mountPath: /run/flannel - name: flannel-cfg mountPath: /etc/kube-flannel/ volumes: - name: run hostPath: path: /run/flannel - name: cni hostPath: path: /etc/cni/net.d - name: flannel-cfg configMap: name: kube-flannel-cfg --- apiVersion: apps/v1 kind: DaemonSet metadata: name: kube-flannel-ds-arm namespace: kube-system labels: tier: node app: flannel spec: selector: matchLabels: app: flannel template: metadata: labels: tier: node app: flannel spec: affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: beta.kubernetes.io/os operator: In values: - linux - key: beta.kubernetes.io/arch operator: In values: - arm hostNetwork: true tolerations: - operator: Exists effect: NoSchedule serviceAccountName: flannel initContainers: - name: install-cni image: quay.io/coreos/flannel:v0.11.0-arm command: - cp args: - -f - /etc/kube-flannel/cni-conf.json - /etc/cni/net.d/10-flannel.conflist volumeMounts: - name: cni mountPath: /etc/cni/net.d - name: flannel-cfg mountPath: /etc/kube-flannel/ containers: - name: kube-flannel image: quay.io/coreos/flannel:v0.11.0-arm command: - /opt/bin/flanneld args: - --ip-masq - --kube-subnet-mgr resources: requests: cpu: "100m" memory: "50Mi" limits: cpu: "100m" memory: "50Mi" securityContext: privileged: false capabilities: add: ["NET_ADMIN"] env: - name: POD_NAME valueFrom: fieldRef: fieldPath: metadata.name - name: POD_NAMESPACE valueFrom: fieldRef: fieldPath: metadata.namespace volumeMounts: - name: run mountPath: /run/flannel - name: flannel-cfg mountPath: /etc/kube-flannel/ volumes: - name: run hostPath: path: /run/flannel - name: cni hostPath: path: /etc/cni/net.d - name: flannel-cfg configMap: name: kube-flannel-cfg --- apiVersion: apps/v1 kind: DaemonSet metadata: name: kube-flannel-ds-ppc64le namespace: kube-system labels: tier: node app: flannel spec: selector: matchLabels: app: flannel template: metadata: labels: tier: node app: flannel spec: affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: beta.kubernetes.io/os operator: In values: - linux - key: beta.kubernetes.io/arch operator: In values: - ppc64le hostNetwork: true tolerations: - operator: Exists effect: NoSchedule serviceAccountName: flannel initContainers: - name: install-cni image: quay.io/coreos/flannel:v0.11.0-ppc64le command: - cp args: - -f - /etc/kube-flannel/cni-conf.json - /etc/cni/net.d/10-flannel.conflist volumeMounts: - name: cni mountPath: /etc/cni/net.d - name: flannel-cfg mountPath: /etc/kube-flannel/ containers: - name: kube-flannel image: quay.io/coreos/flannel:v0.11.0-ppc64le command: - /opt/bin/flanneld args: - --ip-masq - --kube-subnet-mgr resources: requests: cpu: "100m" memory: "50Mi" limits: cpu: "100m" memory: "50Mi" securityContext: privileged: false capabilities: add: ["NET_ADMIN"] env: - name: POD_NAME valueFrom: fieldRef: fieldPath: metadata.name - name: POD_NAMESPACE valueFrom: fieldRef: fieldPath: metadata.namespace volumeMounts: - name: run mountPath: /run/flannel - name: flannel-cfg mountPath: /etc/kube-flannel/ volumes: - name: run hostPath: path: /run/flannel - name: cni hostPath: path: /etc/cni/net.d - name: flannel-cfg configMap: name: kube-flannel-cfg --- apiVersion: apps/v1 kind: DaemonSet metadata: name: kube-flannel-ds-s390x namespace: kube-system labels: tier: node app: flannel spec: selector: matchLabels: app: flannel template: metadata: labels: tier: node app: flannel spec: affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: beta.kubernetes.io/os operator: In values: - linux - key: beta.kubernetes.io/arch operator: In values: - s390x hostNetwork: true tolerations: - operator: Exists effect: NoSchedule serviceAccountName: flannel initContainers: - name: install-cni image: quay.io/coreos/flannel:v0.11.0-s390x command: - cp args: - -f - /etc/kube-flannel/cni-conf.json - /etc/cni/net.d/10-flannel.conflist volumeMounts: - name: cni mountPath: /etc/cni/net.d - name: flannel-cfg mountPath: /etc/kube-flannel/ containers: - name: kube-flannel image: quay.io/coreos/flannel:v0.11.0-s390x command: - /opt/bin/flanneld args: - --ip-masq - --kube-subnet-mgr resources: requests: cpu: "100m" memory: "50Mi" limits: cpu: "100m" memory: "50Mi" securityContext: privileged: false capabilities: add: ["NET_ADMIN"] env: - name: POD_NAME valueFrom: fieldRef: fieldPath: metadata.name - name: POD_NAMESPACE valueFrom: fieldRef: fieldPath: metadata.namespace volumeMounts: - name: run mountPath: /run/flannel - name: flannel-cfg mountPath: /etc/kube-flannel/ volumes: - name: run hostPath: path: /run/flannel - name: cni hostPath: path: /etc/cni/net.d - name: flannel-cfg configMap: name: kube-flannel-cfg
查看名称空间
kubectl get ns
获取所有命名空间的pod
kubectl get pods --all-namespaces
kube-flannel-ds-amd64-m4wln 状态为running,再继续下面的步骤
获取节点
kubectl get nodes
有一个节点是master,状态是ready
node2节点
kubeadm join 10.0.2.5:6443 --token qy7chp.d1qvqk6slfpsl284 --discovery-token-ca-cert-hash sha256:9e75c53992ae8803fa727ea5d537c387fa42aec4ddc6ed934c146e665cec5de3
node3节点也一样
[root@k8s-node1 k8s]# kubectl get nodes NAME STATUS ROLES AGE VERSION k8s-node1 Ready master 41d v1.17.3 k8s-node2 Ready <none> 41d v1.17.3 k8s-node3 Ready <none> 41d v1.17.3
入门操作k8s集群
部署一个tomcat
kubectl create deployment tomcat6 --image=tomcat:6.0.53-jre8
查看所有资源
kubectl get all
查看pod详细信息
kubectl get pods -o wide
暴露nginx访问
kubectl expose deployment tomcat6 --port=80 --target-port=8080 --type=NodePort
pod的80映射容器的8080,service会代理pod的80
查看service详细信息
[root@k8s-node1 k8s]# kubectl get svc -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 41d <none>
tomcat6 NodePort 10.96.57.43 <none> 80:32311/TCP 41d app=tomcat6
tomcat暴露了32311端口
可以访问
动态扩容,扩容3份
[root@k8s-node1 k8s]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
tomcat6-5f7ccf4cb9-2bghn 1/1 Running 10 41d 10.244.1.13 k8s-node2 <none> <none>
[root@k8s-node1 k8s]# kubectl scale --replicas=3 deployment tomcat6
deployment.apps/tomcat6 scaled
[root@k8s-node1 k8s]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
tomcat6-5f7ccf4cb9-2bghn 1/1 Running 10 41d 10.244.1.13 k8s-node2 <none> <none>
tomcat6-5f7ccf4cb9-4vmv7 1/1 Running 0 4s 10.244.2.3 k8s-node3 <none> <none>
tomcat6-5f7ccf4cb9-7grgq 1/1 Running 0 4s 10.244.2.4 k8s-node3 <none> <none>
访问任意节点的上述端口都可以访问tomcat
也可以缩容
[root@k8s-node1 k8s]# kubectl scale --replicas=1 deployment tomcat6
deployment.apps/tomcat6 scaled
[root@k8s-node1 k8s]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
tomcat6-5f7ccf4cb9-2bghn 1/1 Running 10 41d 10.244.1.13 k8s-node2 <none> <none>
tomcat6-5f7ccf4cb9-4vmv7 1/1 Terminating 0 66s 10.244.2.3 k8s-node3 <none> <none>
tomcat6-5f7ccf4cb9-7grgq 1/1 Terminating 0 66s 10.244.2.4 k8s-node3 <none> <none>
查看service、deployment及副本等信息,删除部署,也可以删除service
[root@k8s-node1 k8s]# kubectl get all NAME READY STATUS RESTARTS AGE pod/tomcat6-5f7ccf4cb9-2bghn 1/1 Running 10 41d NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 41d service/tomcat6 NodePort 10.96.57.43 <none> 80:32311/TCP 41d NAME READY UP-TO-DATE AVAILABLE AGE deployment.apps/tomcat6 1/1 1 1 41d NAME DESIRED CURRENT READY AGE replicaset.apps/tomcat6-5f7ccf4cb9 1 1 1 41d
[root@k8s-node1 k8s]# kubectl delete deployment.apps/tomcat6
Kubectl
https://kubernetes.io/zh/docs/reference/kubectl/overview/
Kubectl 是一个命令行接口,用于对 Kubernetes 集群运行命令。kubectl
在 $HOME/.kube 目录中寻找一个名为 config 的文件。您可以通过设置环境变量 KUBECONFIG 或设置 --kubeconfig
参数指定其它 kubeconfig 文件。
使用以下语法 kubectl
从终端窗口运行命令:
kubectl [command] [TYPE] [NAME] [flags]
其中 command
、TYPE
、NAME
和 flags
分别是:
-
command
:指定要对一个或多个资源执行的操作,例如create
、get
、describe
、delete
。 -
TYPE
:指定资源类型。资源类型不区分大小写,可以指定单数、复数或缩写形式。例如,以下命令输出相同的结果:```shell kubectl get pod pod1 kubectl get pods pod1 kubectl get po pod1 ```
-
NAME
:指定资源的名称。名称区分大小写。如果省略名称,则显示所有资源的详细信息kubectl get pods
。在对多个资源执行操作时,您可以按类型和名称指定每个资源,或指定一个或多个文件:
-
要按类型和名称指定资源:
-
要对所有类型相同的资源进行分组,请执行以下操作:
TYPE1 name1 name2 name<#>
。
例子:kubectl get pod example-pod1 example-pod2
-
分别指定多个资源类型:
TYPE1/name1 TYPE1/name2 TYPE2/name3 TYPE<#>/name<#>
。
例子:kubectl get pod/example-pod1 replicationcontroller/example-rc1
-
-
用一个或多个文件指定资源:
-f file1 -f file2 -f file<#>
- 使用 YAML 而不是 JSON 因为 YAML 更容易使用,特别是用于配置文件时。
例子:kubectl get pod -f ./pod.yaml
- 使用 YAML 而不是 JSON 因为 YAML 更容易使用,特别是用于配置文件时。
-
flags
: 指定可选的参数。例如,可以使用-s
或-server
参数指定 Kubernetes API 服务器的地址和端口。
警告:从命令行指定的参数会覆盖默认值和任何相应的环境变量。
如果您需要帮助,只需从终端窗口运行 kubectl help
即可
yml
yml模板
使用apply命令可以使用yml文件
kubectl apply -f example.yaml
可以将之前部署tomcat格式化为yml信息
[root@k8s-node1 k8s]# kubectl create deployment tomcat6 --image=tomcat:6.0.53-jre8 --dry-run -o yaml apiVersion: apps/v1 kind: Deployment metadata: creationTimestamp: null labels: app: tomcat6 name: tomcat6 spec: replicas: 1 selector: matchLabels: app: tomcat6 strategy: {} template: metadata: creationTimestamp: null labels: app: tomcat6 spec: containers: - image: tomcat:6.0.53-jre8 name: tomcat resources: {} status: {}
将其保存为tomcat6.yaml
可以使用该文件
kubectl apply -f tomcat6.yaml
入门操作
Pod是个啥
Pod和Controller
控制器可以创建和管理多个Pod,管理副本和上线,并在集群范围内提供自修复能力。如,一个节点识别,控制器可以在不同的节点上调度一样的替身来自动替换Pod。
包含一个或多个Pod的控制器的一些示例:
- Deployment
- StatefulSet
- DaemonSet
控制器通常使用您提供的Pod模板来创建它所负责的Pod
Services
将运行在一组 Pods 上的应用程序公开为网络服务的抽象方法。
使用Kubernetes,您无需修改应用程序即可使用不熟悉的服务发现机制。 Kubernetes为Pods提供自己的IP地址和一组Pod的单个DNS名称,并且可以在它们之间进行负载平衡。
Kubernetes Pods 是有生命周期的。他们可以被创建,而且销毁不会再启动。 如果您使用 Deployment 来运行您的应用程序,则它可以动态创建和销毁 Pod。
每个 Pod 都有自己的 IP 地址,但是在 Deployment 中,在同一时刻运行的 Pod 集合可能与稍后运行该应用程序的 Pod 集合不同。
这导致了一个问题: 如果一组 Pod(称为“后端”)为群集内的其他 Pod(称为“前端”)提供功能,那么前端如何找出并跟踪要连接的 IP 地址,以便前端可以使用工作量的后端部分?
Kubernetes Service
定义了这样一种抽象:逻辑上的一组 Pod
,一种可以访问它们的策略 —— 通常称为微服务。 这一组 Pod
能够被 Service
访问到,通常是通过 selector (查看下面了解,为什么你可能需要没有 selector 的 Service
)实现的。
重新部署下tomcat服务,删除之前的资源
[root@k8s-node1 k8s]# kubectl get all NAME READY STATUS RESTARTS AGE pod/tomcat6-5f7ccf4cb9-2bghn 1/1 Running 10 41d NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 41d service/tomcat6 NodePort 10.96.57.43 <none> 80:32311/TCP 41d NAME READY UP-TO-DATE AVAILABLE AGE deployment.apps/tomcat6 1/1 1 1 41d NAME DESIRED CURRENT READY AGE replicaset.apps/tomcat6-5f7ccf4cb9 1 1 1 41d [root@k8s-node1 k8s]# kubectl delete deployment.apps/tomcat6 deployment.apps "tomcat6" deleted [root@k8s-node1 k8s]# kubectl delete pod/tomcat6-5f7ccf4cb9-2bghn Error from server (NotFound): pods "tomcat6-5f7ccf4cb9-2bghn" not found [root@k8s-node1 k8s]# kubectl delete service/tomcat6 service "tomcat6" deleted
生成yaml文件,去掉不必要的内容
[root@k8s-node1 k8s]# kubectl create deployment tomcat6 --image=tomcat:6.0.53-jre8 --dry-run -o yaml > tomcat6-deployment.yaml [root@k8s-node1 k8s]# vi tomcat6-deployment.yaml
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
进行Deployment,会产生Pod
[root@k8s-node1 k8s]# kubectl apply -f tomcat6-deployment.yaml deployment.apps/tomcat6 created
[root@k8s-node1 k8s]# kubectl get all NAME READY STATUS RESTARTS AGE pod/tomcat6-5f7ccf4cb9-542xn 1/1 Running 0 12s pod/tomcat6-5f7ccf4cb9-c7599 1/1 Running 0 12s pod/tomcat6-5f7ccf4cb9-sffzb 1/1 Running 0 12s NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 41d NAME READY UP-TO-DATE AVAILABLE AGE deployment.apps/tomcat6 3/3 3 3 12s NAME DESIRED CURRENT READY AGE replicaset.apps/tomcat6-5f7ccf4cb9 3 3 3 12s
暴露服务
[root@k8s-node1 k8s]# kubectl expose deployment tomcat6 --port=80 --target-port=8080 --type=NodePort --dry-run -o yaml apiVersion: v1 kind: Service metadata: creationTimestamp: null labels: app: tomcat6 name: tomcat6 spec: ports: - port: 80 protocol: TCP targetPort: 8080 selector: app: tomcat6 type: NodePort status: loadBalancer: {}
我们可以将这个yaml复制到上一个yaml进行合并
[root@k8s-node1 k8s]# vi tomcat6-deployment.yaml 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 --- apiVersion: v1 kind: Service metadata: labels: app: tomcat6 name: tomcat6 spec: ports: - port: 80 protocol: TCP targetPort: 8080 selector: app: tomcat6 type: NodePort
删除之前的部署
[root@k8s-node1 k8s]# kubectl get all NAME READY STATUS RESTARTS AGE pod/tomcat6-5f7ccf4cb9-542xn 1/1 Running 0 5m34s pod/tomcat6-5f7ccf4cb9-c7599 1/1 Running 0 5m34s pod/tomcat6-5f7ccf4cb9-sffzb 1/1 Running 0 5m34s NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 41d NAME READY UP-TO-DATE AVAILABLE AGE deployment.apps/tomcat6 3/3 3 3 5m34s NAME DESIRED CURRENT READY AGE replicaset.apps/tomcat6-5f7ccf4cb9 3 3 3 5m34s
[root@k8s-node1 k8s]# kubectl delete deployment.apps/tomcat6 deployment.apps "tomcat6" deleted [root@k8s-node1 k8s]# kubectl get all NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 41d
现在一次进行部署和暴露
[root@k8s-node1 k8s]# kubectl apply -f tomcat6-deployment.yaml deployment.apps/tomcat6 created service/tomcat6 created
[root@k8s-node1 k8s]# kubectl get all
NAME READY STATUS RESTARTS AGE
pod/tomcat6-5f7ccf4cb9-g74gl 1/1 Running 0 24s
pod/tomcat6-5f7ccf4cb9-hz6md 1/1 Running 0 24s
pod/tomcat6-5f7ccf4cb9-pb2bx 1/1 Running 0 24s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 41d
service/tomcat6 NodePort 10.96.21.159 <none> 80:31871/TCP 24s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/tomcat6 3/3 3 3 24s
NAME DESIRED CURRENT READY AGE
replicaset.apps/tomcat6-5f7ccf4cb9 3 3 3 24s
Ingress
通过Service发现Pod进行关联,基于域名访问
通过Ingress Controller实现Pod负载均衡
支持TCP/UDP 4层负载均衡和HTTP 7层负载均衡
步骤:
- 部署Ingress Controller
[root@k8s-node1 k8s]# kubectl apply -f ingress-controller.yaml namespace/ingress-nginx created configmap/nginx-configuration created configmap/tcp-services created configmap/udp-services created serviceaccount/nginx-ingress-serviceaccount created clusterrole.rbac.authorization.k8s.io/nginx-ingress-clusterrole created role.rbac.authorization.k8s.io/nginx-ingress-role created rolebinding.rbac.authorization.k8s.io/nginx-ingress-role-nisa-binding created clusterrolebinding.rbac.authorization.k8s.io/nginx-ingress-clusterrole-nisa-binding created daemonset.apps/nginx-ingress-controller created service/ingress-nginx created [root@k8s-node1 k8s]# kubectl get pods --all-namespaces NAMESPACE NAME READY STATUS RESTARTS AGE default tomcat6-5f7ccf4cb9-g74gl 1/1 Running 0 7m13s default tomcat6-5f7ccf4cb9-hz6md 1/1 Running 0 7m13s default tomcat6-5f7ccf4cb9-pb2bx 1/1 Running 0 7m13s ingress-nginx nginx-ingress-controller-v28sn 0/1 ContainerCreating 0 80s ingress-nginx nginx-ingress-controller-w8qng 0/1 ContainerCreating 0 80s kube-system coredns-7f9c544f75-lvsrk 1/1 Running 14 41d kube-system coredns-7f9c544f75-xlk4v 1/1 Running 14 41d kube-system etcd-k8s-node1 1/1 Running 15 41d kube-system kube-apiserver-k8s-node1 1/1 Running 15 41d kube-system kube-controller-manager-k8s-node1 1/1 Running 65 41d kube-system kube-flannel-ds-amd64-ktbfz 1/1 Running 12 41d kube-system kube-flannel-ds-amd64-lh9fl 1/1 Running 10 41d kube-system kube-flannel-ds-amd64-m99gh 1/1 Running 16 41d kube-system kube-proxy-dwmnm 1/1 Running 12 41d kube-system kube-proxy-kxcpw 1/1 Running 14 41d kube-system kube-proxy-mnj6q 1/1 Running 10 41d kube-system kube-scheduler-k8s-node1 1/1 Running 59 41d
apiVersion: v1 kind: Namespace metadata: name: ingress-nginx labels: app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx --- kind: ConfigMap apiVersion: v1 metadata: name: nginx-configuration namespace: ingress-nginx labels: app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx --- kind: ConfigMap apiVersion: v1 metadata: name: tcp-services namespace: ingress-nginx labels: app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx --- kind: ConfigMap apiVersion: v1 metadata: name: udp-services namespace: ingress-nginx labels: app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx --- apiVersion: v1 kind: ServiceAccount metadata: name: nginx-ingress-serviceaccount namespace: ingress-nginx labels: app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx --- apiVersion: rbac.authorization.k8s.io/v1beta1 kind: ClusterRole metadata: name: nginx-ingress-clusterrole labels: app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx rules: - apiGroups: - "" resources: - configmaps - endpoints - nodes - pods - secrets verbs: - list - watch - apiGroups: - "" resources: - nodes verbs: - get - apiGroups: - "" resources: - services verbs: - get - list - watch - apiGroups: - "extensions" resources: - ingresses verbs: - get - list - watch - apiGroups: - "" resources: - events verbs: - create - patch - apiGroups: - "extensions" resources: - ingresses/status verbs: - update --- apiVersion: rbac.authorization.k8s.io/v1beta1 kind: Role metadata: name: nginx-ingress-role namespace: ingress-nginx labels: app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx rules: - apiGroups: - "" resources: - configmaps - pods - secrets - namespaces verbs: - get - apiGroups: - "" resources: - configmaps resourceNames: # Defaults to "<election-id>-<ingress-class>" # Here: "<ingress-controller-leader>-<nginx>" # This has to be adapted if you change either parameter # when launching the nginx-ingress-controller. - "ingress-controller-leader-nginx" verbs: - get - update - apiGroups: - "" resources: - configmaps verbs: - create - apiGroups: - "" resources: - endpoints verbs: - get --- apiVersion: rbac.authorization.k8s.io/v1beta1 kind: RoleBinding metadata: name: nginx-ingress-role-nisa-binding namespace: ingress-nginx labels: app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx roleRef: apiGroup: rbac.authorization.k8s.io kind: Role name: nginx-ingress-role subjects: - kind: ServiceAccount name: nginx-ingress-serviceaccount namespace: ingress-nginx --- apiVersion: rbac.authorization.k8s.io/v1beta1 kind: ClusterRoleBinding metadata: name: nginx-ingress-clusterrole-nisa-binding labels: app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: nginx-ingress-clusterrole subjects: - kind: ServiceAccount name: nginx-ingress-serviceaccount namespace: ingress-nginx --- apiVersion: apps/v1 kind: DaemonSet metadata: name: nginx-ingress-controller namespace: ingress-nginx labels: app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx spec: selector: matchLabels: app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx template: metadata: labels: app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx annotations: prometheus.io/port: "10254" prometheus.io/scrape: "true" spec: hostNetwork: true serviceAccountName: nginx-ingress-serviceaccount containers: - name: nginx-ingress-controller image: siriuszg/nginx-ingress-controller:0.20.0 args: - /nginx-ingress-controller - --configmap=$(POD_NAMESPACE)/nginx-configuration - --tcp-services-configmap=$(POD_NAMESPACE)/tcp-services - --udp-services-configmap=$(POD_NAMESPACE)/udp-services - --publish-service=$(POD_NAMESPACE)/ingress-nginx - --annotations-prefix=nginx.ingress.kubernetes.io securityContext: allowPrivilegeEscalation: true capabilities: drop: - ALL add: - NET_BIND_SERVICE # www-data -> 33 runAsUser: 33 env: - name: POD_NAME valueFrom: fieldRef: fieldPath: metadata.name - name: POD_NAMESPACE valueFrom: fieldRef: fieldPath: metadata.namespace ports: - name: http containerPort: 80 - name: https containerPort: 443 livenessProbe: failureThreshold: 3 httpGet: path: /healthz port: 10254 scheme: HTTP initialDelaySeconds: 10 periodSeconds: 10 successThreshold: 1 timeoutSeconds: 10 readinessProbe: failureThreshold: 3 httpGet: path: /healthz port: 10254 scheme: HTTP periodSeconds: 10 successThreshold: 1 timeoutSeconds: 10 --- apiVersion: v1 kind: Service metadata: name: ingress-nginx namespace: ingress-nginx spec: #type: NodePort ports: - name: http port: 80 targetPort: 80 protocol: TCP - name: https port: 443 targetPort: 443 protocol: TCP selector: app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx
- 创建Ingress规则
vi ingress-tomcat6.yaml
kind: Ingress metadata: name: web spec: rules: - host: example.aidata.com http: paths: - backend: serviceName: tomcat6 servicePort: 80
windows的hosts文件里
192.168.56.101 example.aidata.com
浏览器直接访问,不用端口
部署dashboard
[root@k8s-node1 k8s]# kubectl apply -f kubernetes-dashboard.yaml secret/kubernetes-dashboard-certs created serviceaccount/kubernetes-dashboard created role.rbac.authorization.k8s.io/kubernetes-dashboard-minimal created rolebinding.rbac.authorization.k8s.io/kubernetes-dashboard-minimal created deployment.apps/kubernetes-dashboard created service/kubernetes-dashboard created
# Copyright 2017 The Kubernetes Authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # ------------------- Dashboard Secret ------------------- # apiVersion: v1 kind: Secret metadata: labels: k8s-app: kubernetes-dashboard name: kubernetes-dashboard-certs namespace: kube-system type: Opaque --- # ------------------- Dashboard Service Account ------------------- # apiVersion: v1 kind: ServiceAccount metadata: labels: k8s-app: kubernetes-dashboard name: kubernetes-dashboard namespace: kube-system --- # ------------------- Dashboard Role & Role Binding ------------------- # kind: Role apiVersion: rbac.authorization.k8s.io/v1 metadata: name: kubernetes-dashboard-minimal namespace: kube-system rules: # Allow Dashboard to create 'kubernetes-dashboard-key-holder' secret. - apiGroups: [""] resources: ["secrets"] verbs: ["create"] # Allow Dashboard to create 'kubernetes-dashboard-settings' config map. - apiGroups: [""] resources: ["configmaps"] verbs: ["create"] # Allow Dashboard to get, update and delete Dashboard exclusive secrets. - apiGroups: [""] resources: ["secrets"] resourceNames: ["kubernetes-dashboard-key-holder", "kubernetes-dashboard-certs"] verbs: ["get", "update", "delete"] # Allow Dashboard to get and update 'kubernetes-dashboard-settings' config map. - apiGroups: [""] resources: ["configmaps"] resourceNames: ["kubernetes-dashboard-settings"] verbs: ["get", "update"] # Allow Dashboard to get metrics from heapster. - apiGroups: [""] resources: ["services"] resourceNames: ["heapster"] verbs: ["proxy"] - apiGroups: [""] resources: ["services/proxy"] resourceNames: ["heapster", "http:heapster:", "https:heapster:"] verbs: ["get"] --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: kubernetes-dashboard-minimal namespace: kube-system roleRef: apiGroup: rbac.authorization.k8s.io kind: Role name: kubernetes-dashboard-minimal subjects: - kind: ServiceAccount name: kubernetes-dashboard namespace: kube-system --- # ------------------- Dashboard Deployment ------------------- # kind: Deployment apiVersion: apps/v1 metadata: labels: k8s-app: kubernetes-dashboard name: kubernetes-dashboard namespace: kube-system spec: replicas: 1 revisionHistoryLimit: 10 selector: matchLabels: k8s-app: kubernetes-dashboard template: metadata: labels: k8s-app: kubernetes-dashboard spec: containers: - name: kubernetes-dashboard image: k8s.gcr.io/kubernetes-dashboard-amd64:v1.10.1 ports: - containerPort: 8443 protocol: TCP args: - --auto-generate-certificates # Uncomment the following line to manually specify Kubernetes API server Host # If not specified, Dashboard will attempt to auto discover the API server and connect # to it. Uncomment only if the default does not work. # - --apiserver-host=http://my-address:port volumeMounts: - name: kubernetes-dashboard-certs mountPath: /certs # Create on-disk volume to store exec logs - mountPath: /tmp name: tmp-volume livenessProbe: httpGet: scheme: HTTPS path: / port: 8443 initialDelaySeconds: 30 timeoutSeconds: 30 volumes: - name: kubernetes-dashboard-certs secret: secretName: kubernetes-dashboard-certs - name: tmp-volume emptyDir: {} serviceAccountName: kubernetes-dashboard # Comment the following tolerations if Dashboard must not be deployed on master tolerations: - key: node-role.kubernetes.io/master effect: NoSchedule --- # ------------------- Dashboard Service ------------------- # kind: Service apiVersion: v1 metadata: labels: k8s-app: kubernetes-dashboard name: kubernetes-dashboard namespace: kube-system spec: ports: - port: 443 targetPort: 8443 selector: k8s-app: kubernetes-dashboard
KuberSphere
比dashboard更加强大
https://kubesphere.com.cn/docs/zh-CN/installation/prerequisites/
安装helm
[root@k8s-node1 k8s]# curl -L https://git.io/get_helm.sh | bash
get_helm.sh
#!/usr/bin/env bash # Copyright The Helm Authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # The install script is based off of the MIT-licensed script from glide, # the package manager for Go: https://github.com/Masterminds/glide.sh/blob/master/get PROJECT_NAME="helm" TILLER_NAME="tiller" : ${USE_SUDO:="true"} : ${HELM_INSTALL_DIR:="/usr/local/bin"} # initArch discovers the architecture for this system. initArch() { ARCH=$(uname -m) case $ARCH in armv5*) ARCH="armv5";; armv6*) ARCH="armv6";; armv7*) ARCH="arm";; aarch64) ARCH="arm64";; x86) ARCH="386";; x86_64) ARCH="amd64";; i686) ARCH="386";; i386) ARCH="386";; esac } # initOS discovers the operating system for this system. initOS() { OS=$(echo `uname`|tr '[:upper:]' '[:lower:]') case "$OS" in # Minimalist GNU for Windows mingw*) OS='windows';; esac } # runs the given command as root (detects if we are root already) runAsRoot() { local CMD="$*" if [ $EUID -ne 0 -a $USE_SUDO = "true" ]; then CMD="sudo $CMD" fi $CMD } # verifySupported checks that the os/arch combination is supported for # binary builds. verifySupported() { local supported="darwin-386\ndarwin-amd64\nlinux-386\nlinux-amd64\nlinux-arm\nlinux-arm64\nlinux-ppc64le\nwindows-386\nwindows-amd64" if ! echo "${supported}" | grep -q "${OS}-${ARCH}"; then echo "No prebuilt binary for ${OS}-${ARCH}." echo "To build from source, go to https://github.com/helm/helm" exit 1 fi if ! type "curl" > /dev/null && ! type "wget" > /dev/null; then echo "Either curl or wget is required" exit 1 fi } # checkDesiredVersion checks if the desired version is available. checkDesiredVersion() { if [ "x$DESIRED_VERSION" == "x" ]; then # Get tag from release URL local release_url="https://github.com/helm/helm/releases" if type "curl" > /dev/null; then TAG=$(curl -Ls $release_url | grep 'href="/helm/helm/releases/tag/v2.' | grep -v no-underline | head -n 1 | cut -d '"' -f 2 | awk '{n=split($NF,a,"/");print a[n]}' | awk 'a !~ $0{print}; {a=$0}') elif type "wget" > /dev/null; then TAG=$(wget $release_url -O - 2>&1 | grep 'href="/helm/helm/releases/tag/v2.' | grep -v no-underline | head -n 1 | cut -d '"' -f 2 | awk '{n=split($NF,a,"/");print a[n]}' | awk 'a !~ $0{print}; {a=$0}') fi else TAG=$DESIRED_VERSION fi } # checkHelmInstalledVersion checks which version of helm is installed and # if it needs to be changed. checkHelmInstalledVersion() { if [[ -f "${HELM_INSTALL_DIR}/${PROJECT_NAME}" ]]; then local version=$("${HELM_INSTALL_DIR}/${PROJECT_NAME}" version -c | grep '^Client' | cut -d'"' -f2) if [[ "$version" == "$TAG" ]]; then echo "Helm ${version} is already ${DESIRED_VERSION:-latest}" return 0 else echo "Helm ${TAG} is available. Changing from version ${version}." return 1 fi else return 1 fi } # downloadFile downloads the latest binary package and also the checksum # for that binary. downloadFile() { HELM_DIST="helm-$TAG-$OS-$ARCH.tar.gz" DOWNLOAD_URL="https://get.helm.sh/$HELM_DIST" CHECKSUM_URL="$DOWNLOAD_URL.sha256" HELM_TMP_ROOT="$(mktemp -dt helm-installer-XXXXXX)" HELM_TMP_FILE="$HELM_TMP_ROOT/$HELM_DIST" HELM_SUM_FILE="$HELM_TMP_ROOT/$HELM_DIST.sha256" echo "Downloading $DOWNLOAD_URL" if type "curl" > /dev/null; then curl -SsL "$CHECKSUM_URL" -o "$HELM_SUM_FILE" elif type "wget" > /dev/null; then wget -q -O "$HELM_SUM_FILE" "$CHECKSUM_URL" fi if type "curl" > /dev/null; then curl -SsL "$DOWNLOAD_URL" -o "$HELM_TMP_FILE" elif type "wget" > /dev/null; then wget -q -O "$HELM_TMP_FILE" "$DOWNLOAD_URL" fi } # installFile verifies the SHA256 for the file, then unpacks and # installs it. installFile() { HELM_TMP="$HELM_TMP_ROOT/$PROJECT_NAME" local sum=$(openssl sha1 -sha256 ${HELM_TMP_FILE} | awk '{print $2}') local expected_sum=$(cat ${HELM_SUM_FILE}) if [ "$sum" != "$expected_sum" ]; then echo "SHA sum of ${HELM_TMP_FILE} does not match. Aborting." exit 1 fi mkdir -p "$HELM_TMP" tar xf "$HELM_TMP_FILE" -C "$HELM_TMP" HELM_TMP_BIN="$HELM_TMP/$OS-$ARCH/$PROJECT_NAME" TILLER_TMP_BIN="$HELM_TMP/$OS-$ARCH/$TILLER_NAME" echo "Preparing to install $PROJECT_NAME and $TILLER_NAME into ${HELM_INSTALL_DIR}" runAsRoot cp "$HELM_TMP_BIN" "$HELM_INSTALL_DIR" echo "$PROJECT_NAME installed into $HELM_INSTALL_DIR/$PROJECT_NAME" if [ -x "$TILLER_TMP_BIN" ]; then runAsRoot cp "$TILLER_TMP_BIN" "$HELM_INSTALL_DIR" echo "$TILLER_NAME installed into $HELM_INSTALL_DIR/$TILLER_NAME" else echo "info: $TILLER_NAME binary was not found in this release; skipping $TILLER_NAME installation" fi } # fail_trap is executed if an error occurs. fail_trap() { result=$? if [ "$result" != "0" ]; then if [[ -n "$INPUT_ARGUMENTS" ]]; then echo "Failed to install $PROJECT_NAME with the arguments provided: $INPUT_ARGUMENTS" help else echo "Failed to install $PROJECT_NAME" fi echo -e "\tFor support, go to https://github.com/helm/helm." fi cleanup exit $result } # testVersion tests the installed client to make sure it is working. testVersion() { set +e HELM="$(which $PROJECT_NAME)" if [ "$?" = "1" ]; then echo "$PROJECT_NAME not found. Is $HELM_INSTALL_DIR on your "'$PATH?' exit 1 fi set -e echo "Run '$PROJECT_NAME init' to configure $PROJECT_NAME." } # help provides possible cli installation arguments help () { echo "Accepted cli arguments are:" echo -e "\t[--help|-h ] ->> prints this help" echo -e "\t[--version|-v <desired_version>]" echo -e "\te.g. --version v2.4.0 or -v latest" echo -e "\t[--no-sudo] ->> install without sudo" } # cleanup temporary files to avoid https://github.com/helm/helm/issues/2977 cleanup() { if [[ -d "${HELM_TMP_ROOT:-}" ]]; then rm -rf "$HELM_TMP_ROOT" fi } # Execution #Stop execution on any error trap "fail_trap" EXIT set -e # Parsing input arguments (if any) export INPUT_ARGUMENTS="${@}" set -u while [[ $# -gt 0 ]]; do case $1 in '--version'|-v) shift if [[ $# -ne 0 ]]; then export DESIRED_VERSION="${1}" else echo -e "Please provide the desired version. e.g. --version v2.4.0 or -v latest" exit 0 fi ;; '--no-sudo') USE_SUDO="false" ;; '--help'|-h) help exit 0 ;; *) exit 1 ;; esac shift done set +u initArch initOS verifySupported checkDesiredVersion if ! checkHelmInstalledVersion; then downloadFile installFile fi testVersion cleanup
配置权限
创建helm-rbac.yaml文件创建helm-rbac.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
[root@k8s-node1 k8s]# kubectl apply -f helm-rbac.yaml serviceaccount/tiller created clusterrolebinding.rbac.authorization.k8s.io/tiller created
初始化
helm init --service-account=tiller --tiller-image=sapcc/tiller:v2.16.3 --history-max 300
--tiller-image指定镜像,否则会被墙
安装 OpenEBS 创建 LocalPV 存储类型 https://kubesphere.com.cn/docs/zh-CN/appendix/install-openebs/
[root@k8s-node1 k8s]# kubectl get node -o wide NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME k8s-node1 Ready master 41d v1.17.3 10.0.2.5 <none> CentOS Linux 7 (Core) 3.10.0-957.12.2.el7.x86_64 docker://19.3.8 k8s-node2 Ready <none> 41d v1.17.3 10.0.2.4 <none> CentOS Linux 7 (Core) 3.10.0-957.12.2.el7.x86_64 docker://19.3.8 k8s-node3 Ready <none> 41d v1.17.3 10.0.2.15 <none> CentOS Linux 7 (Core) 3.10.0-957.12.2.el7.x86_64 docker://19.3.8
[root@k8s-node1 k8s]# kubectl describe node k8s-node1 | grep Taint Taints: node-role.kubernetes.io/master:NoSchedule
[root@k8s-node1 k8s]# kubectl taint nodes k8s-node1 node-role.kubernetes.io/master:NoSchedule- node/k8s-node1 untainted
[root@k8s-node1 k8s]# kubectl describe node k8s-node1 | grep Taint Taints: <none> [root@k8s-node1 k8s]# kubectl create ns openebs namespace/openebs created
[root@k8s-node1 k8s]# helm install --namespace openebs --name openebs stable/openebs --version 1.5.0 NAME: openebs LAST DEPLOYED: Mon Jun 8 10:10:50 2020 NAMESPACE: openebs STATUS: DEPLOYED RESOURCES: ==> v1/ClusterRole NAME AGE openebs 4s ==> v1/ClusterRoleBinding NAME AGE openebs 4s ==> v1/ConfigMap NAME AGE openebs-ndm-config 4s ==> v1/DaemonSet NAME AGE openebs-ndm 4s ==> v1/Deployment NAME AGE openebs-admission-server 4s openebs-apiserver 4s openebs-localpv-provisioner 4s openebs-ndm-operator 4s openebs-provisioner 4s openebs-snapshot-operator 4s ==> v1/Pod(related) NAME AGE openebs-admission-server-5cf6864fbf-ttntv 4s openebs-apiserver-bc55cd99b-8sbh7 4s openebs-localpv-provisioner-85ff89dd44-h9qzh 4s openebs-ndm-cdqc5 4s openebs-ndm-cvgpf 4s openebs-ndm-operator-87df44d9-849cd 3s openebs-ndm-sc779 4s openebs-provisioner-7f86c6bb64-94pxj 4s openebs-snapshot-operator-54b9c886bf-bsj6f 3s ==> v1/Service NAME AGE openebs-apiservice 4s ==> v1/ServiceAccount NAME AGE openebs 4s NOTES: The OpenEBS has been installed. Check its status by running: $ kubectl get pods -n openebs For dynamically creating OpenEBS Volumes, you can either create a new StorageClass or use one of the default storage classes provided by OpenEBS. Use `kubectl get sc` to see the list of installed OpenEBS StorageClasses. A sample PVC spec using `openebs-jiva-default` StorageClass is given below:" --- kind: PersistentVolumeClaim apiVersion: v1 metadata: name: demo-vol-claim spec: storageClassName: openebs-jiva-default accessModes: - ReadWriteOnce resources: requests: storage: 5G --- For more information, please visit http://docs.openebs.io/. Please note that, OpenEBS uses iSCSI for connecting applications with the OpenEBS Volumes and your nodes should have the iSCSI initiator installed. [root@k8s-node1 k8s]# kubectl get pods --all-namespaces NAMESPACE NAME READY STATUS RESTARTS AGE default tomcat6-5f7ccf4cb9-g74gl 1/1 Running 0 117m default tomcat6-5f7ccf4cb9-hz6md 1/1 Running 0 117m default tomcat6-5f7ccf4cb9-pb2bx 1/1 Running 0 117m ingress-nginx nginx-ingress-controller-q5dzl 1/1 Running 0 3m47s ingress-nginx nginx-ingress-controller-v28sn 1/1 Running 0 111m ingress-nginx nginx-ingress-controller-w8qng 1/1 Running 0 111m kube-system coredns-7f9c544f75-lvsrk 1/1 Running 14 41d kube-system coredns-7f9c544f75-xlk4v 1/1 Running 14 41d kube-system etcd-k8s-node1 1/1 Running 15 41d kube-system kube-apiserver-k8s-node1 1/1 Running 15 41d kube-system kube-controller-manager-k8s-node1 1/1 Running 65 41d kube-system kube-flannel-ds-amd64-ktbfz 1/1 Running 12 41d kube-system kube-flannel-ds-amd64-lh9fl 1/1 Running 10 41d kube-system kube-flannel-ds-amd64-m99gh 1/1 Running 16 41d kube-system kube-proxy-dwmnm 1/1 Running 12 41d kube-system kube-proxy-kxcpw 1/1 Running 14 41d kube-system kube-proxy-mnj6q 1/1 Running 10 41d kube-system kube-scheduler-k8s-node1 1/1 Running 59 41d kube-system kubernetes-dashboard-7c54d59f66-48g9z 0/1 ImagePullBackOff 0 75m kube-system tiller-deploy-5fdc6844fb-kjps5 1/1 Running 0 8m8s openebs openebs-admission-server-5cf6864fbf-ttntv 1/1 Running 0 2m16s openebs openebs-apiserver-bc55cd99b-8sbh7 1/1 Running 3 2m16s openebs openebs-localpv-provisioner-85ff89dd44-h9qzh 1/1 Running 0 2m16s openebs openebs-ndm-cdqc5 1/1 Running 0 2m16s openebs openebs-ndm-cvgpf 1/1 Running 0 2m16s openebs openebs-ndm-operator-87df44d9-849cd 1/1 Running 1 2m15s openebs openebs-ndm-sc779 1/1 Running 0 2m16s openebs openebs-provisioner-7f86c6bb64-94pxj 1/1 Running 0 2m16s openebs openebs-snapshot-operator-54b9c886bf-bsj6f 2/2 Running 0 2m15s
[root@k8s-node1 k8s]# kubectl get sc NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE openebs-device openebs.io/local Delete WaitForFirstConsumer false 21s openebs-hostpath openebs.io/local Delete WaitForFirstConsumer false 21s openebs-jiva-default openebs.io/provisioner-iscsi Delete Immediate false 23s openebs-snapshot-promoter volumesnapshot.external-storage.k8s.io/snapshot-promoter Delete Immediate false 21s
[root@k8s-node1 k8s]# kubectl patch storageclass openebs-hostpath -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}' storageclass.storage.k8s.io/openebs-hostpath patched [root@k8s-node1 k8s]# kubectl taint nodes k8s-node1 node-role.kubernetes.io=master:NoSchedule node/k8s-node1 tainted
安装KubeSphere的前置条件至此完成,安装KubeSphere
[root@k8s-node1 k8s]# vi kubesphere-minimal.yaml [root@k8s-node1 k8s]# kubectl apply -f 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: False jenkinsMemoryLim: 2Gi jenkinsMemoryReq: 1500Mi jenkinsVolumeSize: 8Gi jenkinsJavaOpts_Xms: 512m jenkinsJavaOpts_Xmx: 512m jenkinsJavaOpts_MaxRAM: 2g sonarqube: enabled: False postgresqlVolumeSize: 8Gi servicemesh: enabled: False notification: enabled: False alerting: enabled: False 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"
监控安装过程
kubectl logs -n kubesphere-system $(kubectl get pod -n kubesphere-system -l app=ks-install -o jsonpath='{.items[0].metadata.name}') -f
最后出现下面信息
Console: http://192.168.10.100:30880 Account: admin Password: P@88w0rd
包括了登录账号和密码
kubectl get pods --all-namespaces
所有都running后就可以浏览器访问了
添加功能
- 通过修改 ks-installer 的 configmap 可以选装组件,执行以下命令。
$ kubectl edit cm -n kubesphere-system ks-installer
参考如下修改 ConfigMap
··· metrics-server: enabled: True
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
监控安装进度
kubectl logs -n kubesphere-system $(kubectl get pod -n kubesphere-system -l app=ks-install -o jsonpath='{.items[0].metadata.name}') -f
多租户管理快速入门
平台的资源一共有三个层级,包括 集群 (Cluster)、 企业空间 (Workspace)、 项目 (Project) 和 DevOps Project (DevOps 工程),层级关系如下图所示,即一个集群中可以创建多个企业空间,而每个企业空间,可以创建多个项目和 DevOps工程,而集群、企业空间、项目和 DevOps工程中,默认有多个不同的内置角色。
选 平台角色
自定义角色,点新建
赋予权限
点 账户管理
点创建,填写信息,确定
我们可以使用atguigu-hr登录
创建账户
退出,ws-manager登录
创建企业空间
可以创建企业空间,也可以邀请成员,邀请ws-admin进企业空间,并指定为管理员
ws-admin登录
管理企业空间
邀请project-admin,指定为worksapce-viewer
邀请project-regular,指定为workspace-regular
project-amdin 登录
创建资源项目
邀请project-regular,指定为operator
这是项目的开发人员
创建DevOps工程
邀请project-regular,指定为maintain
登录project-regular
进入项目
配置中心,密钥,创建密钥
默认,添加数据
输入key和value
上,创建一个 WordPress 密钥,Data 键值对填写 WORDPRESS_DB_PASSWORD
和 123456
。此时两个密钥都创建完成。
创建存储卷
- 在当前项目下左侧菜单栏的
存储卷
,点击创建
,基本信息如下。
- 名称:wordpress-pvc
- 别名:Wordpress 持久化存储卷
- 描述信息:Wordpress PVC
同理,创建mysql-pvc
应用
部署新应用
下拉,添加容器镜像
输入mysql:5.6,使用默认端口
内存要大于1000M
下拉
引用配置文件或密钥
点对号
添加存储卷
点对号
点对号
继续添加组件
点击创建
等待
外网访问
访问任意节点30110端口
基于Spring Boot项目构建流水线
DevOps
Dev:怎么开发
Ops:怎么运维
高并发:怎么承担高并发
高可用:怎么做到高可用
CI&CD
持续集成
持续部署
前提条件
- 开启安装了 DevOps 功能组件,参考 安装 DevOps 系统;
- 本示例以 GitHub 和 DockerHub 为例,参考前确保已创建了 GitHub 和 DockerHub 账号;
- 已创建了企业空间和 DevOps 工程并且创建了项目普通用户 project-regular 的账号,若还未创建请参考 多租户管理快速入门;
- 使用项目管理员
project-admin
邀请项目普通用户project-regular
加入 DevOps 工程并授予maintainer
角色,若还未邀请请参考 多租户管理快速入门 - 邀请成员。 - 参考 配置 ci 节点 为流水线选择执行构建的节点。
流程说明:
- 阶段一. Checkout SCM: 拉取 GitHub 仓库代码
- 阶段二. Unit test: 单元测试,如果测试通过了才继续下面的任务
- 阶段三. SonarQube analysis:sonarQube 代码质量检测
- 阶段四. Build & push snapshot image: 根据行为策略中所选择分支来构建镜像,并将 tag 为
SNAPSHOT-$BRANCH_NAME-$BUILD_NUMBER
推送至 Harbor (其中$BUILD_NUMBER
为 pipeline 活动列表的运行序号)。- 阶段五. Push latest image: 将 master 分支打上 tag 为 latest,并推送至 DockerHub。
- 阶段六. Deploy to dev: 将 master 分支部署到 Dev 环境,此阶段需要审核。
- 阶段七. Push with tag: 生成 tag 并 release 到 GitHub,并推送到 DockerHub。
- 阶段八. Deploy to production: 将发布的 tag 部署到 Production 环境。
创建凭证
登录project-regular,创建凭证
创建 DockerHub 凭证
1、点击 创建,创建一个用于 DockerHub 登录的凭证;
- 凭证 ID:必填,此 ID 将用于仓库中的 Jenkinsfile,此示例中可命名为 dockerhub-id
- 类型:选择 账户凭证
- 用户名:填写您个人的 DockerHub 的用户名
- token / 密码:您个人的 DockerHub 的密码
- 描述信息:介绍凭证,比如此处可以备注为 DockerHub 登录凭证
2、完成后点击 确定。
创建 GitHub 凭证
同上,创建一个用于 GitHub 的凭证,凭证 ID 可命名为 github-id,类型选择 账户凭证
,输入您个人的 GitHub 用户名和密码,备注描述信息,完成后点击 确定。
注意:若用户的凭证信息如账号或密码中包含了
@
,$
这类特殊符号,可能在运行时无法识别而报错,这类情况需要用户在创建凭证时对密码进行 urlencode 编码,可通过一些第三方网站进行转换 (比如http://tool.chinaz.com/tools/urlencode.aspx
),然后再将转换后的输出粘贴到对应的凭证信息中。
创建 kubeconfig 凭证
同上,在 凭证 下点击 创建,创建一个类型为 kubeconfig
的凭证,凭证 ID 可命名为 demo-kubeconfig,完成后点击 确定。
说明:kubeconfig 类型的凭证用于访问接入正在运行的 Kubernetes 集群,在流水线部署步骤将用到该凭证。注意,此处的 Content 将自动获取当前 KubeSphere 中的 kubeconfig 文件内容,若部署至当前 KubeSphere 中则无需修改,若部署至其它 Kubernetes 集群,则需要将其 kubeconfig 文件的内容粘贴至 Content 中。
创建 SonarQube Token
访问 SonarQube
master 节点
[root@node01 ~]# kubectl get svc --all-namespaces NAMESPACE NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE default kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 16h gulimall mysql ClusterIP None <none> 3306/TCP 11h gulimall wordpress NodePort 10.96.31.243 <none> 80:30110/TCP 11h ingress-nginx ingress-nginx ClusterIP 10.96.96.80 <none> 80/TCP,443/TCP 16h kube-system kube-controller-manager-headless ClusterIP None <none> 10252/TCP 15h kube-system kube-dns ClusterIP 10.96.0.10 <none> 53/UDP,53/TCP,9153/TCP 16h kube-system kube-scheduler-headless ClusterIP None <none> 10251/TCP 15h kube-system kubelet ClusterIP None <none> 10250/TCP 15h kube-system tiller-deploy ClusterIP 10.96.244.191 <none> 44134/TCP 15h kubesphere-alerting-system alerting-client-server ClusterIP 10.96.66.239 <none> 9200/TCP 15h kubesphere-alerting-system alerting-manager-server ClusterIP 10.96.162.207 <none> 9201/TCP,9200/TCP 15h kubesphere-alerting-system notification ClusterIP 10.96.162.106 <none> 9201/TCP,9200/TCP 15h kubesphere-controls-system default-http-backend ClusterIP 10.96.168.235 <none> 80/TCP 15h kubesphere-devops-system ks-jenkins NodePort 10.96.205.17 <none> 80:30180/TCP 15h kubesphere-devops-system ks-jenkins-agent ClusterIP 10.96.101.100 <none> 50000/TCP 15h kubesphere-devops-system ks-sonarqube-postgresql ClusterIP 10.96.150.29 <none> 5432/TCP 15h kubesphere-devops-system ks-sonarqube-sonarqube NodePort 10.96.3.13 <none> 9000:31426/TCP 15h kubesphere-devops-system s2ioperator ClusterIP 10.96.191.165 <none> 443/TCP 15h kubesphere-devops-system s2ioperator-metrics-service ClusterIP 10.96.217.230 <none> 8080/TCP 15h kubesphere-devops-system uc-jenkins-update-center ClusterIP 10.96.96.70 <none> 80/TCP 15h kubesphere-devops-system webhook-server-service ClusterIP 10.96.91.21 <none> 443/TCP 15h kubesphere-monitoring-system kube-state-metrics ClusterIP None <none> 8443/TCP,9443/TCP 15h kubesphere-monitoring-system node-exporter ClusterIP None <none> 9100/TCP 15h kubesphere-monitoring-system prometheus-k8s ClusterIP None <none> 9090/TCP 15h kubesphere-monitoring-system prometheus-k8s-system ClusterIP None <none> 9090/TCP 15h kubesphere-monitoring-system prometheus-operated ClusterIP None <none> 9090/TCP 15h kubesphere-monitoring-system prometheus-operator ClusterIP None <none> 8080/TCP 15h kubesphere-system etcd ClusterIP 10.96.139.180 <none> 2379/TCP 15h kubesphere-system ks-account ClusterIP 10.96.151.87 <none> 80/TCP 15h kubesphere-system ks-apigateway ClusterIP 10.96.164.101 <none> 80/TCP 15h kubesphere-system ks-apiserver ClusterIP 10.96.76.72 <none> 80/TCP 15h kubesphere-system ks-console NodePort 10.96.213.199 <none> 80:30880/TCP 15h kubesphere-system minio ClusterIP 10.96.42.56 <none> 9000/TCP 15h kubesphere-system mysql ClusterIP 10.96.111.25 <none> 3306/TCP 15h kubesphere-system openldap ClusterIP None <none> 389/TCP 15h kubesphere-system redis ClusterIP 10.96.20.135 <none> 6379/TCP 15h openebs admission-server-svc ClusterIP 10.96.208.248 <none> 443/TCP 15h openebs openebs-apiservice ClusterIP 10.96.233.170 <none> 5656/TCP 15h
访问31426端口
使用默认账号 admin/admin
登入 sonar
输入 name
,然后点击 Generate
。
复制token,保存
点击 Continue
选择 Language Java
,选择 build technology 为 Maven
,复制 token。点击 Finish this tutorial
即可。
Provide a token gulimall-token: 606a3b04c649631669516c21f6235b81b039d519 The token is used to identify you when an analysis is performed. If it has been compromised, you can revoke it at any point of time in your user account.
新建一个凭证
修改 Jenkinsfile
第一步:Fork项目
登录 GitHub,将本示例用到的 GitHub 仓库 devops-java-sample Fork 至您个人的 GitHub。
第二步:修改 Jenkinsfile
1、Fork 至您个人的 GitHub 后,在 根目录 进入 Jenkinsfile-online。
2、在 GitHub UI 点击编辑图标,需要修改如下环境变量 (environment) 的值。
修改项 | 值 | 含义 |
---|---|---|
DOCKER_CREDENTIAL_ID | dockerhub-id | 填写创建凭证步骤中的 DockerHub 凭证 ID,用于登录您的 DockerHub |
GITHUB_CREDENTIAL_ID | github-id | 填写创建凭证步骤中的 GitHub 凭证 ID,用于推送 tag 到 GitHub 仓库 |
KUBECONFIG_CREDENTIAL_ID | demo-kubeconfig | kubeconfig 凭证 ID,用于访问接入正在运行的 Kubernetes 集群 |
REGISTRY | docker.io | 默认为 docker.io 域名,用于镜像的推送 |
DOCKERHUB_NAMESPACE | your-dockerhub-account | 替换为您的 DockerHub 账号名 (它也可以是账户下的 Organization 名称) |
GITHUB_ACCOUNT | your-github-account | 替换为您的 GitHub 账号名,例如 https://github.com/kubesphere/ 则填写 kubesphere (它也可以是账户下的 Organization 名称) |
APP_NAME | devops-java-sample | 应用名称 |
SONAR_CREDENTIAL_ID | sonar-token | 填写创建凭证步骤中的 SonarQube token凭证 ID,用于代码质量检测 |
注:master
分支 Jenkinsfile 中 mvn
命令的参数 -o
,表示开启离线模式。本示例为适应某些环境下网络的干扰,以及避免在下载依赖时耗时太长,已事先完成相关依赖的下载,默认开启离线模式。
去掉文件中的-o参数,修改环境
environment { DOCKER_CREDENTIAL_ID = 'dockerhub-id' GITHUB_CREDENTIAL_ID = 'github-id' KUBECONFIG_CREDENTIAL_ID = 'demo-kubeconfig' REGISTRY = 'docker.io' DOCKERHUB_NAMESPACE = 'zhao750456695' GITHUB_ACCOUNT = 'zhao750456695' APP_NAME = 'devops-java-sample' SONAR_CREDENTIAL_ID = 'sonar-qube' }
3、修改以上的环境变量后,点击 Commit changes,将更新提交到当前的 master 分支。
4、若需要测试缓存,需要切换至 dependency
分支,对 dependency
分支下的 Jenkinsfile-online 做类似的修改,否则该分支的流水线将构建失败。
创建项目
CI/CD 流水线会根据示例项目的 yaml 模板文件,最终将示例分别部署到 kubesphere-sample-dev
和 kubesphere-sample-prod
这两个项目 (Namespace) 环境中,这两个项目需要预先在控制台依次创建,参考如下步骤创建该项目。
第一步:创建第一个项目
提示:项目管理员
project-admin
账号已在 多租户管理快速入门 中创建。
1、使用项目管理员 project-admin
账号登录 KubeSphere,在之前创建的企业空间 (demo-workspace) 下,点击 项目 → 创建,创建一个 资源型项目,作为本示例的开发环境,填写该项目的基本信息,完成后点击 下一步。
- 名称:固定为
kubesphere-sample-dev
,若需要修改项目名称则需在 yaml 模板文件 中修改 namespace - 别名:可自定义,比如 开发环境
- 描述信息:可简单介绍该项目,方便用户进一步了解
2、本示例暂无资源请求和限制,因此高级设置中无需修改默认值,点击 创建,项目可创建成功。
第二步:邀请成员
第一个项目创建完后,还需要项目管理员 project-admin
邀请当前的项目普通用户 project-regular
进入 kubesphere-sample-dev
项目,进入「项目设置」→「项目成员」,点击「邀请成员」选择邀请 project-regular
并授予 operator
角色,若对此有疑问可参考 多租户管理快速入门 - 邀请成员 。
第三步:创建第二个项目
同上,参考以上两步创建一个名称为 kubesphere-sample-prod
的项目作为生产环境,并邀请普通用户 project-regular
进入 kubesphere-sample-prod
项目并授予 operator
角色。
说明:当 CI/CD 流水线后续执行成功后,在
kubesphere-sample-dev
和kubesphere-sample-prod
项目中将看到流水线创建的部署 (Deployment) 和服务 (Service)。
创建流水线
登录project-regular
第一步:填写基本信息
1、进入已创建的 DevOps 工程,选择左侧菜单栏的 流水线,然后点击 创建。
2、在弹出的窗口中,输入流水线的基本信息。
- 名称:为创建的流水线起一个简洁明了的名称,便于理解和搜索
- 描述信息:简单介绍流水线的主要特性,帮助进一步了解流水线的作用
- 代码仓库:点击选择代码仓库,代码仓库需已存在 Jenkinsfile
第二步:添加仓库
1、点击代码仓库,以添加 Github 仓库为例。
2、点击弹窗中的 获取 Token。
3、在 GitHub 的 access token 页面填写 Token description,简单描述该 token,如 DevOps demo,在 Select scopes 中无需任何修改,点击 Generate token
,GitHub 将生成一串字母和数字组成的 token 用于访问当前账户下的 GitHub repo。
4、复制生成的 token,在 KubeSphere Token 框中输入该 token 然后点击保存。
5、验证通过后,右侧会列出此 Token 关联用户的所有代码库,选择其中一个带有 Jenkinsfile 的仓库。比如此处选择准备好的示例仓库 devops-java-sample,点击 选择此仓库,完成后点击 下一步。
第三步:高级设置
完成代码仓库相关设置后,进入高级设置页面,高级设置支持对流水线的构建记录、行为策略、定期扫描等设置的定制化,以下对用到的相关配置作简单释义。
1、分支设置中,勾选 丢弃旧的分支
,此处的 保留分支的天数 和 保留分支的最大个数 默认为 -1。
说明:
分支设置的保留分支的天数和保持分支的最大个数两个选项可以同时对分支进行作用,只要某个分支的保留天数和个数不满足任何一个设置的条件,则将丢弃该分支。假设设置的保留天数和个数为 2 和 3,则分支的保留天数一旦超过 2 或者保留个数超过 3,则将丢弃该分支。默认两个值为 -1,表示将会丢弃已经被删除的分支。
丢弃旧的分支将确定何时应丢弃项目的分支记录。分支记录包括控制台输出,存档工件以及与特定分支相关的其他元数据。保持较少的分支可以节省 Jenkins 所使用的磁盘空间,我们提供了两个选项来确定应何时丢弃旧的分支:
- 保留分支的天数:如果分支达到一定的天数,则丢弃分支。
- 保留分支的个数:如果已经存在一定数量的分支,则丢弃最旧的分支。
2、行为策略中,KubeSphere 默认添加了三种策略。由于本示例还未用到 从 Fork 仓库中发现 PR 这条策略,此处可以删除该策略,点击右侧删除按钮。
说明:
支持添加三种类型的发现策略。需要说明的是,在 Jenkins 流水线被触发时,开发者提交的 PR (Pull Request) 也被视为一个单独的分支。
发现分支:
- 排除也作为 PR 提交的分支:选择此项表示 CI 将不会扫描源分支 (比如 Origin 的 master branch),也就是需要被 merge 的分支
- 只有被提交为 PR 的分支:仅扫描 PR 分支
- 所有分支:拉取的仓库 (origin) 中所有的分支
从原仓库中发现 PR:
- PR 与目标分支合并后的源代码版本:一次发现操作,基于 PR 与目标分支合并后的源代码版本创建并运行流水线
- PR 本身的源代码版本:一次发现操作,基于 PR 本身的源代码版本创建并运行流水线
- 当 PR 被发现时会创建两个流水线,一个流水线使用 PR 本身的源代码版本,一个流水线使用 PR 与目标分支合并后的源代码版本:两次发现操作,将分别创建两条流水线,第一条流水线使用 PR 本身的源代码版本,第二条流水线使用 PR 与目标分支合并后的源代码版本
3、默认的 脚本路径 为 Jenkinsfile,请将其修改为 Jenkinsfile-online。
注:路径是 Jenkinsfile 在代码仓库的路径,表示它在示例仓库的根目录,若文件位置变动则需修改其脚本路径。
4、在 扫描 Repo Trigger 勾选 如果没有扫描触发,则定期扫描
,扫描时间间隔可根据团队习惯设定,本示例设置为 5 minutes
。
说明:定期扫描是设定一个周期让流水线周期性地扫描远程仓库,根据 行为策略 查看仓库有没有代码更新或新的 PR。
Webhook 推送:
Webhook 是一种高效的方式可以让流水线发现远程仓库的变化并自动触发新的运行,GitHub 和 Git (如 Gitlab) 触发 Jenkins 自动扫描应该以 Webhook 为主,以上一步在 KubeSphere 设置定期扫描为辅。在本示例中,可以通过手动运行流水线,如需设置自动扫描远端分支并触发运行,详见 设置自动触发扫描 - GitHub SCM。
完成高级设置后点击 创建。
第四步:运行流水线
流水线创建后,点击浏览器的 刷新 按钮,可见两条自动触发远程分支后的运行记录,分别为 master
和 dependency
分支的构建记录。
1、点击右侧 运行,将根据上一步的 行为策略 自动扫描代码仓库中的分支,在弹窗选择需要构建流水线的 master
分支,系统将根据输入的分支加载 Jenkinsfile-online (默认是根目录下的 Jenkinsfile)。
2、由于仓库的 Jenkinsfile-online 中 TAG_NAME: defaultValue
没有设置默认值,因此在这里的 TAG_NAME
可以输入一个 tag 编号,比如输入 v0.0.1。
3、点击 确定,将新生成一条流水线活动开始运行。
说明: tag 用于在 Github 和DockerHub 中分别生成带有 tag 的 release 和镜像。 注意: 在主动运行流水线以发布 release 时,
TAG_NAME
不应与之前代码仓库中所存在的tag
名称重复,如果重复会导致流水线的运行失败。
至此,流水线 已完成创建并开始运行。
注:点击 分支 切换到分支列表,查看流水线具体是基于哪些分支运行,这里的分支则取决于上一步 行为策略 的发现分支策略。
第五步:审核流水线
为方便演示,此处默认用当前账户来审核,当流水线执行至 input
步骤时状态将暂停,需要手动点击 继续,流水线才能继续运行。注意,在 Jenkinsfile-online 中分别定义了三个阶段 (stage) 用来部署至 Dev 环境和 Production 环境以及推送 tag,因此在流水线中依次需要对 deploy to dev, push with tag, deploy to production
这三个阶段审核 3
次,若不审核或点击 终止 则流水线将不会继续运行。
说明:在实际的开发生产场景下,可能需要更高权限的管理员或运维人员来审核流水线和镜像,并决定是否允许将其推送至代码或镜像仓库,以及部署至开发或生产环境。Jenkinsfile 中的
input
步骤支持指定用户审核流水线,比如要指定用户名为 project-admin 的用户来审核,可以在 Jenkinsfile 的 input 函数中追加一个字段,如果是多个用户则通过逗号分隔,如下所示:
···
input(id: 'release-image-with-tag', message: 'release image with tag?', submitter: 'project-admin,project-admin1')
···
集群部署
集群形式
主从式
主从复制,同步方式
主从调度,控制方式
分片式
数据分片存储,片区之间备份
选主式
为了容灾选主
为了调度选主
集群目标
高可用
突破数据量限制
数据备份容灾
压力分担
MYSQL集群
MMM
1.1 简介
MMM是Master-Master Manager for MySQL(mysql主从复制的简称),是Google开源项目(Perl脚本)。MMM基于MySQL Replication做的扩展架构,主要用来监控mysql主主复制并做失败转移。
其原理是将真实真实数据库节点的IP(RIP)映射为虚拟IP(VIP)集。
有个监管端,会提供多个虚拟IP(VIP),包括一个可写VIP,多个可读VIP,通过监管的管理,这些IP会绑定在可用mysql上,当某一台mysql宕机时,监管会将VIP迁移至其他mysql。
整个监管过程中,需要在mysql中添加相关授权用户,以便让mysql可以支持监管机的维护。授权的用户包括一个mmm_monitor用户和一个mmm_agent用户,如果想使用mmm的备份工具还要添加一个mmm_tools用户。
MMM 是一套支持双主故障切换以及双主日常管理的第三方软件。MMM 由 Perl 开发,用来管理和监控双主复制,虽然是双主架构,但是业务上同一时间只允许一个节点进行写入操作。
MMM 包含两类角色: writer
和 reader
, 分别对应读写节点和只读节点。
使用 MMM 管理双主节点的情况下,当 writer
节点出现宕机(假定是 master1
),程序会自动移除该节点上的读写 VIP,切换到 Master2
,并设置 Master2
为 read_only = 0
, 同时,所有 Slave
节点会指向 Master2
。
除了管理双主节点,MMM 也会管理 Slave
节点,在出现宕机、复制延迟或复制错误,MMM 会移除该节点的 VIP,直到节点恢复正常。
进行IP漂移,将master的IP指向备用的节点,该节点就成了master。但是该节点可能没拿到全部数据,一致性方面存在问题。
1.2 组件
MMM 由两类程序组成
monitor
: 监控集群内数据库的状态,在出现异常时发布切换命令,一般和数据库分开部署agent
: 运行在每个 MySQL 服务器上的代理进程,monitor 命令的执行者,完成监控的探针工作和具体服务设置,例如设置 VIP、指向新同步节点
其架构如下:
1.3 切换流程
以上述架构为例,描述一下故障转移的流程,现在假设 Master1 宕机
- Monitor 检测到 Master1 连接失败
- Monitor 发送 set_offline 指令到 Master1 的 Agent
- Master1 Agent 如果存活,下线写 VIP,尝试把 Master1 设置为
read_only=1
- Moniotr 发送 set_online 指令到 Master2
- Master2 Agent 接收到指令,执行
select master_pos_wait()
等待同步完毕 - Master2 Agent 上线写 VIP,把 Master2 节点设为
read_only=0
- Monitor 发送更改同步对象的指令到各个 Slave 节点的 Agent
- 各个 Slave 节点向新 Master 同步数据
从整个流程可以看到,如果主节点出现故障,MMM 会自动实现切换,不需要人工干预,同时我们也能看出一些问题,就是数据库挂掉后,只是做了切换,不会主动补齐丢失的数据,所以 MMM 会有数据不一致性的风险。
MHA
简介
MHA(Master HA)是一款开源的 MySQL 的高可用程序,它为 MySQL 主从复制架构提供了 automating master failover 功能。MHA 在监控到 master 节点故障时,会提升其中拥有最新数据的 slave 节点成为新的master 节点,在此期间,MHA 会通过于其它从节点获取额外信息来避免一致性方面的问题。MHA 还提供了 master 节点的在线切换功能,即按需切换 master/slave 节点。
MHA 是由日本人 yoshinorim(原就职于DeNA现就职于FaceBook)开发的比较成熟的 MySQL 高可用方案。MHA 能够在30秒内实现故障切换,并能在故障切换中,最大可能的保证数据一致性。目前淘宝也正在开发相似产品 TMHA, 目前已支持一主一从。
MHA 服务
服务角色
MHA 服务有两种角色, MHA Manager(管理节点)和 MHA Node(数据节点):
MHA Manager:
通常单独部署在一台独立机器上管理多个 master/slave 集群(组),每个 master/slave 集群称作一个 application,用来管理统筹整个集群。
MHA node:
运行在每台 MySQL 服务器上(master/slave/manager),它通过监控具备解析和清理 logs 功能的脚本来加快故障转移。
主要是接收管理节点所发出指令的代理,代理需要运行在每一个 mysql 节点上。简单讲 node 就是用来收集从节点服务器上所生成的 bin-log 。对比打算提升为新的主节点之上的从节点的是否拥有并完成操作,如果没有发给新主节点在本地应用后提升为主节点。
由上图我们可以看出,每个复制组内部和 Manager 之间都需要ssh实现无密码互连,只有这样,在 Master 出故障时, Manager 才能顺利的连接进去,实现主从切换功能。
提供的工具
MHA会提供诸多工具程序, 其常见的如下所示:
Manager节点:
masterha_check_ssh
:MHA 依赖的 ssh 环境监测工具;
masterha_check_repl
:MYSQL 复制环境检测工具;
masterga_manager
:MHA 服务主程序;
masterha_check_status
:MHA 运行状态探测工具;
masterha_master_monitor
:MYSQL master 节点可用性监测工具;
masterha_master_swith:master
:节点切换工具;
masterha_conf_host
:添加或删除配置的节点;
masterha_stop
:关闭 MHA 服务的工具。
Node节点:(这些工具通常由MHA Manager的脚本触发,无需人为操作)
save_binary_logs
:保存和复制 master 的二进制日志;
apply_diff_relay_logs
:识别差异的中继日志事件并应用于其他 slave;
purge_relay_logs
:清除中继日志(不会阻塞 SQL 线程);
自定义扩展:
secondary_check_script
:通过多条网络路由检测master的可用性;
master_ip_failover_script
:更新application使用的masterip;
report_script
:发送报告;
init_conf_load_script
:加载初始配置参数;
master_ip_online_change_script
;更新master节点ip地址。
工作原理
MHA工作原理总结为以下几条:
(1) 从宕机崩溃的 master 保存二进制日志事件(binlog events);
(2) 识别含有最新更新的 slave ;
(3) 应用差异的中继日志(relay log) 到其他 slave ;
(4) 应用从 master 保存的二进制日志事件(binlog events);
(5) 提升一个 slave 为新 master ;
(6) 使用其他的 slave 连接新的 master 进行复制。
InnoDB Cluster
InnoDB Cluster主要由MySQL Shell、MySQL Router和MySQL服务器集群组成,三者协同工作,共同为MySQL提供完整的高可用性解决方案。下图所示为InnoDB Cluster的整体架构。
InnoDB Cluster架构
InnoDB Cluster以组复制为基础,集群中的每个MySQL服务器实例都是组复制的成员,提供了在InnoDB Cluster内复制数据的机制,并且具有内置的故障转移功能。MySQL Shell在InnoDB Cluster中充当控制台角色,使用它包含的AdminAPI,可以使安装、配置、管理、维护多个MySQL组复制实例的工作更加轻松。通过AdminAPI的几条交互指令就可自动完成组复制配置。MySQL Router可以根据集群部署信息自动生成配置,将客户端应用程序透明地连接到MySQL服务器实例。如果服务器实例意外故障,群集将自动重新配置。在默认的单主模式下,InnoDB Cluster 具有单个读写主服务器实例。多个辅助服务器实例是主服务器实例的副本。如果主服务器出现故障,则辅助服务器将自动升级为主服务器。MySQL Router可以检测到这种情况并将客户端应用程序自动转发到新的主服务器。
Docker安装模拟MySQL主从复制集群
下载mysql镜像
[root@localhost ~]# docker pull mysql:5.7
创建master实例并启动
docker run -p 3307:3306 --name mysql-master -v /bigdata/mysql/master/log:/var/log/mysql -v /bigdata/mysql/master/data:/var/lib/mysql -v /bigdata/mysql/master/conf:/etc/mysql -e MYSQL_ROOT_PASSWORD=root -d mysql:5.7
-v 挂载文件
创建slave实例并启动
docker run -p 3317:3306 --name mysql-slaver-01 -v /bigdata/mysql/slaver/log:/var/log/mysql -v /bigdata/mysql/slaver/data:/var/lib/mysql -v /bigdata/mysql/slaver/conf:/etc/mysql -e MYSQL_ROOT_PASSWORD=root -d mysql:5.7
修改master基本配置
创建 /bigdata/mysql/master/conf/my.cnf
[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
init_connect通常用于:当一个连接进来时,做一些操作,比如设置autocommit为0,比如记录当前连接的ip来源和用户等信息到一个新表里,当做登陆日志信息。
init_connect 是可以动态在线调整的,这样就有了一些其他的用处。
经过测试init_connect 是用户登录到数据库上之后,在执行第一次查询之前执行 里面的内容的。
如果init_connect 的内容有语法错误,导致执行失败,会导致用户无法执行查询,从mysql 退出。
init_connect 对具有super 权限的用户是无效的。
collation大致的意思就是字符序。首先字符本来是不分大小的,那么对字符的>, = , < 操作就需要有个字符序的规则。collation做的就是这个事情,你可以对表进行字符序的设置,也可以单独对某个字段进行字符序的设置。一个字符类型,它的字符序有多个。以_ci(表示大小写不敏感),以_cs(表示大小写敏感),以_bin(表示用编码值进行比较)。
skip-character-set-client-handshake=1 跳过mysql程序起动时的字符参数设置 ,使用服务器端字符集设置
skip_name_resolve 禁止域名解析
同理,/bigdata/mysql/slaver/conf/my.cnf也复制上面配置
添加master主从复制部分配置
server_id=1 log-bin=mysql-bin read-only=0 binlog-do-db=gulimall_ums binlog-do-db=gulimall_pms binlog-do-db=gulimall_oms binlog-do-db=gulimall_sms binlog-do-db=gulimall_wms binlog-do-db=gulimall_admin replicate-ignore-db=mysql replicate-ignore-db=sys replicate-ignore-db=information_schema replicate-ignore-db=performance_schema
slaver
server_id=2 log-bin=mysql-bin read-only=1 binlog-do-db=gulimall_ums binlog-do-db=gulimall_pms binlog-do-db=gulimall_oms binlog-do-db=gulimall_sms binlog-do-db=gulimall_wms binlog-do-db=gulimall_admin replicate-ignore-db=mysql replicate-ignore-db=sys replicate-ignore-db=information_schema replicate-ignore-db=performance_schema
重启两个mysql
[root@localhost conf]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES f8028c6dbf7d mysql:5.7 "docker-entrypoint.s…" 3 hours ago Up 3 hours 33060/tcp, 0.0.0.0:3317->3306/tcp mysql-slaver-01 00c17fb98652 mysql:5.7 "docker-entrypoint.s…" 3 hours ago Up 3 hours 33060/tcp, 0.0.0.0:3307->3306/tcp mysql-master [root@localhost conf]# docker restart mysql-master mysql-slaver-01
bridge网卡,无法连接docker mysql的问题
为master授权用户来它的同步数据
连接master
添加用来同步的用户
授权一个用户可以访问主节点,进行日志复制
mysql> GRANT REPLICATION SLAVE ON *.* TO 'backup'@'%' identified by 'root'; Query OK, 0 rows affected (0.00 sec)
配置slaver同步master数据
连接slaver
告诉mysql,需要同步哪个主节点
mysql> change master to master_host='192.168.10.100',master_user='backup',master_password='root',master_log_file='mysql-bin.000001',master_log_pos=0,master_port=3307; Query OK, 0 rows affected (0.03 sec) mysql> start slave;
master上新建gulimall_oms数据库,表中运行sql文件
/* Navicat MySQL Data Transfer Source Server : 192.168.56.10_3306 Source Server Version : 50727 Source Host : 192.168.56.10:3306 Source Database : gulimall_oms Target Server Type : MYSQL Target Server Version : 50727 File Encoding : 65001 Date: 2020-03-11 17:36:38 */ SET FOREIGN_KEY_CHECKS=0; -- ---------------------------- -- Table structure for mq_message -- ---------------------------- DROP TABLE IF EXISTS `mq_message`; CREATE TABLE `mq_message` ( `message_id` char(32) NOT NULL, `content` text, `to_exchane` varchar(255) DEFAULT NULL, `routing_key` varchar(255) DEFAULT NULL, `class_type` varchar(255) DEFAULT NULL, `message_status` int(1) DEFAULT '0' COMMENT '0-新建 1-已发送 2-错误抵达 3-已抵达', `create_time` datetime DEFAULT NULL, `update_time` datetime DEFAULT NULL, PRIMARY KEY (`message_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- ---------------------------- -- Records of mq_message -- ---------------------------- -- ---------------------------- -- Table structure for oms_order -- ---------------------------- DROP TABLE IF EXISTS `oms_order`; CREATE TABLE `oms_order` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id', `member_id` bigint(20) DEFAULT NULL COMMENT 'member_id', `order_sn` char(64) DEFAULT NULL COMMENT '订单号', `coupon_id` bigint(20) DEFAULT NULL COMMENT '使用的优惠券', `create_time` datetime DEFAULT NULL COMMENT 'create_time', `member_username` varchar(200) DEFAULT NULL COMMENT '用户名', `total_amount` decimal(18,4) DEFAULT NULL COMMENT '订单总额', `pay_amount` decimal(18,4) DEFAULT NULL COMMENT '应付总额', `freight_amount` decimal(18,4) DEFAULT NULL COMMENT '运费金额', `promotion_amount` decimal(18,4) DEFAULT NULL COMMENT '促销优化金额(促销价、满减、阶梯价)', `integration_amount` decimal(18,4) DEFAULT NULL COMMENT '积分抵扣金额', `coupon_amount` decimal(18,4) DEFAULT NULL COMMENT '优惠券抵扣金额', `discount_amount` decimal(18,4) DEFAULT NULL COMMENT '后台调整订单使用的折扣金额', `pay_type` tinyint(4) DEFAULT NULL COMMENT '支付方式【1->支付宝;2->微信;3->银联; 4->货到付款;】', `source_type` tinyint(4) DEFAULT NULL COMMENT '订单来源[0->PC订单;1->app订单]', `status` tinyint(4) DEFAULT NULL COMMENT '订单状态【0->待付款;1->待发货;2->已发货;3->已完成;4->已关闭;5->无效订单】', `delivery_company` varchar(64) DEFAULT NULL COMMENT '物流公司(配送方式)', `delivery_sn` varchar(64) DEFAULT NULL COMMENT '物流单号', `auto_confirm_day` int(11) DEFAULT NULL COMMENT '自动确认时间(天)', `integration` int(11) DEFAULT NULL COMMENT '可以获得的积分', `growth` int(11) DEFAULT NULL COMMENT '可以获得的成长值', `bill_type` tinyint(4) DEFAULT NULL COMMENT '发票类型[0->不开发票;1->电子发票;2->纸质发票]', `bill_header` varchar(255) DEFAULT NULL COMMENT '发票抬头', `bill_content` varchar(255) DEFAULT NULL COMMENT '发票内容', `bill_receiver_phone` varchar(32) DEFAULT NULL COMMENT '收票人电话', `bill_receiver_email` varchar(64) DEFAULT NULL COMMENT '收票人邮箱', `receiver_name` varchar(100) DEFAULT NULL COMMENT '收货人姓名', `receiver_phone` varchar(32) DEFAULT NULL COMMENT '收货人电话', `receiver_post_code` varchar(32) DEFAULT NULL COMMENT '收货人邮编', `receiver_province` varchar(32) DEFAULT NULL COMMENT '省份/直辖市', `receiver_city` varchar(32) DEFAULT NULL COMMENT '城市', `receiver_region` varchar(32) DEFAULT NULL COMMENT '区', `receiver_detail_address` varchar(200) DEFAULT NULL COMMENT '详细地址', `note` varchar(500) DEFAULT NULL COMMENT '订单备注', `confirm_status` tinyint(4) DEFAULT NULL COMMENT '确认收货状态[0->未确认;1->已确认]', `delete_status` tinyint(4) DEFAULT NULL COMMENT '删除状态【0->未删除;1->已删除】', `use_integration` int(11) DEFAULT NULL COMMENT '下单时使用的积分', `payment_time` datetime DEFAULT NULL COMMENT '支付时间', `delivery_time` datetime DEFAULT NULL COMMENT '发货时间', `receive_time` datetime DEFAULT NULL COMMENT '确认收货时间', `comment_time` datetime DEFAULT NULL COMMENT '评价时间', `modify_time` datetime DEFAULT NULL COMMENT '修改时间', PRIMARY KEY (`id`), UNIQUE KEY `order_sn` (`order_sn`) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单'; -- ---------------------------- -- Records of oms_order -- ---------------------------- -- ---------------------------- -- Table structure for oms_order_item -- ---------------------------- DROP TABLE IF EXISTS `oms_order_item`; CREATE TABLE `oms_order_item` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id', `order_id` bigint(20) DEFAULT NULL COMMENT 'order_id', `order_sn` char(64) DEFAULT NULL COMMENT 'order_sn', `spu_id` bigint(20) DEFAULT NULL COMMENT 'spu_id', `spu_name` varchar(255) DEFAULT NULL COMMENT 'spu_name', `spu_pic` varchar(500) DEFAULT NULL COMMENT 'spu_pic', `spu_brand` varchar(200) DEFAULT NULL COMMENT '品牌', `category_id` bigint(20) DEFAULT NULL COMMENT '商品分类id', `sku_id` bigint(20) DEFAULT NULL COMMENT '商品sku编号', `sku_name` varchar(255) DEFAULT NULL COMMENT '商品sku名字', `sku_pic` varchar(500) DEFAULT NULL COMMENT '商品sku图片', `sku_price` decimal(18,4) DEFAULT NULL COMMENT '商品sku价格', `sku_quantity` int(11) DEFAULT NULL COMMENT '商品购买的数量', `sku_attrs_vals` varchar(500) DEFAULT NULL COMMENT '商品销售属性组合(JSON)', `promotion_amount` decimal(18,4) DEFAULT NULL COMMENT '商品促销分解金额', `coupon_amount` decimal(18,4) DEFAULT NULL COMMENT '优惠券优惠分解金额', `integration_amount` decimal(18,4) DEFAULT NULL COMMENT '积分优惠分解金额', `real_amount` decimal(18,4) DEFAULT NULL COMMENT '该商品经过优惠后的分解金额', `gift_integration` int(11) DEFAULT NULL COMMENT '赠送积分', `gift_growth` int(11) DEFAULT NULL COMMENT '赠送成长值', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单项信息'; -- ---------------------------- -- Records of oms_order_item -- ---------------------------- -- ---------------------------- -- Table structure for oms_order_operate_history -- ---------------------------- DROP TABLE IF EXISTS `oms_order_operate_history`; CREATE TABLE `oms_order_operate_history` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id', `order_id` bigint(20) DEFAULT NULL COMMENT '订单id', `operate_man` varchar(100) DEFAULT NULL COMMENT '操作人[用户;系统;后台管理员]', `create_time` datetime DEFAULT NULL COMMENT '操作时间', `order_status` tinyint(4) DEFAULT NULL COMMENT '订单状态【0->待付款;1->待发货;2->已发货;3->已完成;4->已关闭;5->无效订单】', `note` varchar(500) DEFAULT NULL COMMENT '备注', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单操作历史记录'; -- ---------------------------- -- Records of oms_order_operate_history -- ---------------------------- -- ---------------------------- -- Table structure for oms_order_return_apply -- ---------------------------- DROP TABLE IF EXISTS `oms_order_return_apply`; CREATE TABLE `oms_order_return_apply` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id', `order_id` bigint(20) DEFAULT NULL COMMENT 'order_id', `sku_id` bigint(20) DEFAULT NULL COMMENT '退货商品id', `order_sn` char(32) DEFAULT NULL COMMENT '订单编号', `create_time` datetime DEFAULT NULL COMMENT '申请时间', `member_username` varchar(64) DEFAULT NULL COMMENT '会员用户名', `return_amount` decimal(18,4) DEFAULT NULL COMMENT '退款金额', `return_name` varchar(100) DEFAULT NULL COMMENT '退货人姓名', `return_phone` varchar(20) DEFAULT NULL COMMENT '退货人电话', `status` tinyint(1) DEFAULT NULL COMMENT '申请状态[0->待处理;1->退货中;2->已完成;3->已拒绝]', `handle_time` datetime DEFAULT NULL COMMENT '处理时间', `sku_img` varchar(500) DEFAULT NULL COMMENT '商品图片', `sku_name` varchar(200) DEFAULT NULL COMMENT '商品名称', `sku_brand` varchar(200) DEFAULT NULL COMMENT '商品品牌', `sku_attrs_vals` varchar(500) DEFAULT NULL COMMENT '商品销售属性(JSON)', `sku_count` int(11) DEFAULT NULL COMMENT '退货数量', `sku_price` decimal(18,4) DEFAULT NULL COMMENT '商品单价', `sku_real_price` decimal(18,4) DEFAULT NULL COMMENT '商品实际支付单价', `reason` varchar(200) DEFAULT NULL COMMENT '原因', `description述` varchar(500) DEFAULT NULL COMMENT '描述', `desc_pics` varchar(2000) DEFAULT NULL COMMENT '凭证图片,以逗号隔开', `handle_note` varchar(500) DEFAULT NULL COMMENT '处理备注', `handle_man` varchar(200) DEFAULT NULL COMMENT '处理人员', `receive_man` varchar(100) DEFAULT NULL COMMENT '收货人', `receive_time` datetime DEFAULT NULL COMMENT '收货时间', `receive_note` varchar(500) DEFAULT NULL COMMENT '收货备注', `receive_phone` varchar(20) DEFAULT NULL COMMENT '收货电话', `company_address` varchar(500) DEFAULT NULL COMMENT '公司收货地址', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单退货申请'; -- ---------------------------- -- Records of oms_order_return_apply -- ---------------------------- -- ---------------------------- -- Table structure for oms_order_return_reason -- ---------------------------- DROP TABLE IF EXISTS `oms_order_return_reason`; CREATE TABLE `oms_order_return_reason` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id', `name` varchar(200) DEFAULT NULL COMMENT '退货原因名', `sort` int(11) DEFAULT NULL COMMENT '排序', `status` tinyint(1) DEFAULT NULL COMMENT '启用状态', `create_time` datetime DEFAULT NULL COMMENT 'create_time', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='退货原因'; -- ---------------------------- -- Records of oms_order_return_reason -- ---------------------------- -- ---------------------------- -- Table structure for oms_order_setting -- ---------------------------- DROP TABLE IF EXISTS `oms_order_setting`; CREATE TABLE `oms_order_setting` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id', `flash_order_overtime` int(11) DEFAULT NULL COMMENT '秒杀订单超时关闭时间(分)', `normal_order_overtime` int(11) DEFAULT NULL COMMENT '正常订单超时时间(分)', `confirm_overtime` int(11) DEFAULT NULL COMMENT '发货后自动确认收货时间(天)', `finish_overtime` int(11) DEFAULT NULL COMMENT '自动完成交易时间,不能申请退货(天)', `comment_overtime` int(11) DEFAULT NULL COMMENT '订单完成后自动好评时间(天)', `member_level` tinyint(2) DEFAULT NULL COMMENT '会员等级【0-不限会员等级,全部通用;其他-对应的其他会员等级】', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单配置信息'; -- ---------------------------- -- Records of oms_order_setting -- ---------------------------- -- ---------------------------- -- Table structure for oms_payment_info -- ---------------------------- DROP TABLE IF EXISTS `oms_payment_info`; CREATE TABLE `oms_payment_info` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id', `order_sn` char(64) DEFAULT NULL COMMENT '订单号(对外业务号)', `order_id` bigint(20) DEFAULT NULL COMMENT '订单id', `alipay_trade_no` varchar(50) DEFAULT NULL COMMENT '支付宝交易流水号', `total_amount` decimal(18,4) DEFAULT NULL COMMENT '支付总金额', `subject` varchar(200) DEFAULT NULL COMMENT '交易内容', `payment_status` varchar(20) DEFAULT NULL COMMENT '支付状态', `create_time` datetime DEFAULT NULL COMMENT '创建时间', `confirm_time` datetime DEFAULT NULL COMMENT '确认时间', `callback_content` varchar(4000) DEFAULT NULL COMMENT '回调内容', `callback_time` datetime DEFAULT NULL COMMENT '回调时间', PRIMARY KEY (`id`), UNIQUE KEY `order_sn` (`order_sn`) USING BTREE, UNIQUE KEY `alipay_trade_no` (`alipay_trade_no`) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='支付信息表'; -- ---------------------------- -- Records of oms_payment_info -- ---------------------------- -- ---------------------------- -- Table structure for oms_refund_info -- ---------------------------- DROP TABLE IF EXISTS `oms_refund_info`; CREATE TABLE `oms_refund_info` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id', `order_return_id` bigint(20) DEFAULT NULL COMMENT '退款的订单', `refund` decimal(18,4) DEFAULT NULL COMMENT '退款金额', `refund_sn` varchar(64) DEFAULT NULL COMMENT '退款交易流水号', `refund_status` tinyint(1) DEFAULT NULL COMMENT '退款状态', `refund_channel` tinyint(4) DEFAULT NULL COMMENT '退款渠道[1-支付宝,2-微信,3-银联,4-汇款]', `refund_content` varchar(5000) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='退款信息'; -- ---------------------------- -- Records of oms_refund_info -- ---------------------------- -- ---------------------------- -- Table structure for undo_log -- ---------------------------- DROP TABLE IF EXISTS `undo_log`; CREATE TABLE `undo_log` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `branch_id` bigint(20) NOT NULL, `xid` varchar(100) NOT NULL, `context` varchar(128) NOT NULL, `rollback_info` longblob NOT NULL, `log_status` int(11) NOT NULL, `log_created` datetime NOT NULL, `log_modified` datetime NOT NULL, `ext` varchar(100) DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- ---------------------------- -- Records of undo_log -- ----------------------------
此时,你会发现slaver中也有了该数据库和表
分库分表,读写分离
ShardingSphere
Sharding-Proxy
透明
如果后端连接 MySQL 数据库,需要下载 MySQL Connector/J, 解压缩后,将 mysql-connector-java-5.1.47.jar
拷贝到 %SHARDINGSPHERE_PROXY_HOME%\lib
目录。
配置下载文件conf中的server.yaml
配置认证信息,配置了root和sharding用户,sharing用户只能访问sharding_db
authentication:
users:
root:
password: root
sharding:
password: sharding
authorizedSchemas: sharding_db
#
props:
# max.connections.size.per.query: 1
# acceptor.size: 16 # The default value is available processors count * 2.
executor.size: 16 # Infinite by default.
# proxy.frontend.flush.threshold: 128 # The default value is 128.
# # LOCAL: Proxy will run with LOCAL transaction.
# # XA: Proxy will run with XA transaction.
# # BASE: Proxy will run with B.A.S.E transaction.
# proxy.transaction.type: LOCAL
# proxy.opentracing.enabled: false
# proxy.hint.enabled: false
# query.with.cipher.column: true
sql.show: false
配置分库分表,读写分离 config-sharding.yaml
数据库名字 sharding_db,映射了真实数据库demo_ds_0和demo_ds_1,我们操作sharding_db,数据实际写到两个数据库中
demo_ds_0和demo_ds_1中有两个表t_order_0和t_order_1,t_order_item_0和t_order_item_1,都用order_id分表
绑定表可以两张表联系起来,查询快
根据user_id分库
schemaName: sharding_db # dataSources: ds_0: url: jdbc:mysql://192.168.10.100: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.10.100:3307/demo_ds_1?serverTimezone=UTC&useSSL=false username: root password: root connectionTimeoutMilliseconds: 30000 idleTimeoutMilliseconds: 60000 maxLifetimeMilliseconds: 1800000 maxPoolSize: 50 # shardingRule: tables: t_order: actualDataNodes: ds_${0..1}.t_order_${0..1} tableStrategy: inline: shardingColumn: order_id # 用订单id分表 algorithmExpression: t_order_${order_id % 2} # 余数为0在t_order_0表,否则在1表 keyGenerator: type: SNOWFLAKE # 使用雪花算法 column: order_id t_order_item: actualDataNodes: ds_${0..1}.t_order_item_${0..1} tableStrategy: inline: shardingColumn: order_id algorithmExpression: t_order_item_${order_id % 2} keyGenerator: type: SNOWFLAKE column: order_item_id bindingTables: - t_order,t_order_item # 绑定表 defaultDatabaseStrategy: inline: shardingColumn: user_id # 分库策略 algorithmExpression: ds_${user_id % 2} defaultTableStrategy: none:
读写分离,config-master_slave.xml
主数据源
从数据源
有两个库,配置两个主从
schemaName: sharding_db_1 # dataSources: master_0_ds: url: jdbc:mysql://192.168.10.100: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.10.100:3317/demo_ds_0?serverTimezone=UTC&useSSL=false username: root password: root connectionTimeoutMilliseconds: 30000 idleTimeoutMilliseconds: 60000 maxLifetimeMilliseconds: 1800000 maxPoolSize: 50 # masterSlaveRule: name: ms_ds_0 masterDataSourceName: master_0_ds slaveDataSourceNames: - slave_ds_0
config-master_slave_2.xml
schemaName: sharding_db_2 # dataSources: master_1_ds: url: jdbc:mysql://192.168.10.100: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.10.100:3317/demo_ds_1?serverTimezone=UTC&useSSL=false username: root password: root connectionTimeoutMilliseconds: 30000 idleTimeoutMilliseconds: 60000 maxLifetimeMilliseconds: 1800000 maxPoolSize: 50 # masterSlaveRule: name: ms_ds_1 masterDataSourceName: master_1_ds slaveDataSourceNames: - slave_ds_1
修改docker mysql配置文件
master和slaver都添加
binlog-do-db=demo_ds_0
binlog-do-db=demo_ds_1
创建demo_ds_0和1库
windows的话一定要用cmd命令行连接,navicat等连接可能会出各种错。
Redis集群
客户端分区
代理分区
redis-cluster
redis-cluster
一组Redis Cluster由多个Redis实例组成,官方推荐使用6实例,其中3个为主节点,3个为从节点。
把所有数据划分为16384个不同的槽位,可以根据机器的性能把不同的槽位分配给不同的Redis实例,对于Redis实例来说,它们只会存储部分的Redis数据。槽的数据是可以迁移的,不同实例之间,可以通过一定的协议,进行数据迁移。
槽
创建6个redis节点
3主,3从
使用脚本搭建docker的redis-cluster
for port in $(seq 7001 7006); \ do \ mkdir -p /bigdata/redis/node-${port}/conf touch /bigdata/redis/node-${port}/conf/redis.conf cat << EOF > /bigdata/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.10.101 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 /bigdata/redis/node-${port}/data:/data \ -v /bigdata/redis/node-${port}/conf/redis.conf:/etc/redis/redis.conf \ -d redis:5.0.7 redis-server /etc/redis/redis.conf; \ done
使用redis建立集群
进入一个docker
docker exec -it redis-7001 bash
三台主节点,三个从节点
redis-cli --cluster create 192.168.10.101:7001 192.168.10.101:7002 192.168.10.101:7003 192.168.10.101:7004 192.168.10.101:7005 192.168.10.101:7006 --cluster-replicas 1
--cluster-replicas 1 每个主节点必须有一个副本节点,
[root@node02 redis]# docker exec -it redis-7001 bash root@57e92e2ffa4c:/data# redis-cli --cluster create 192.168.10.101:7001 192.168.10.101:7002 192.168.10.101:7003 192.168.10.101:7004 192.168.10.101:7005 192.168.10.101:7006 --cluster-replicas 1 >>> Performing hash slots allocation on 6 nodes... Master[0] -> Slots 0 - 5460 Master[1] -> Slots 5461 - 10922 Master[2] -> Slots 10923 - 16383 Adding replica 192.168.10.101:7005 to 192.168.10.101:7001 Adding replica 192.168.10.101:7006 to 192.168.10.101:7002 Adding replica 192.168.10.101:7004 to 192.168.10.101:7003 >>> Trying to optimize slaves allocation for anti-affinity [WARNING] Some slaves are in the same host as their master M: aa624181696765f568bee0e650c4af216ba83c23 192.168.10.101:7001 slots:[0-5460] (5461 slots) master M: 825835dcfc9c34bc5d9f6cdc9d6afca45302e4a6 192.168.10.101:7002 slots:[5461-10922] (5462 slots) master M: 08b1626502539c77ec469e2032e693718649f69d 192.168.10.101:7003 slots:[10923-16383] (5461 slots) master S: 153c705b38fd253594a08989d49425d5ca4ee87f 192.168.10.101:7004 replicates 825835dcfc9c34bc5d9f6cdc9d6afca45302e4a6 S: 4c30599c2cabbb20eafc8b4eb9d4d93c597b20cc 192.168.10.101:7005 replicates 08b1626502539c77ec469e2032e693718649f69d S: e62234ef3c5206770ce29580c0309efb09a98429 192.168.10.101:7006 replicates aa624181696765f568bee0e650c4af216ba83c23 Can I set the above configuration? (type 'yes' to accept): yes >>> Nodes configuration updated >>> Assign a different config epoch to each node >>> Sending CLUSTER MEET messages to join the cluster Waiting for the cluster to join ... >>> Performing Cluster Check (using node 192.168.10.101:7001) M: aa624181696765f568bee0e650c4af216ba83c23 192.168.10.101:7001 slots:[0-5460] (5461 slots) master 1 additional replica(s) S: 4c30599c2cabbb20eafc8b4eb9d4d93c597b20cc 192.168.10.101:7005 slots: (0 slots) slave replicates 08b1626502539c77ec469e2032e693718649f69d M: 08b1626502539c77ec469e2032e693718649f69d 192.168.10.101:7003 slots:[10923-16383] (5461 slots) master 1 additional replica(s) S: 153c705b38fd253594a08989d49425d5ca4ee87f 192.168.10.101:7004 slots: (0 slots) slave replicates 825835dcfc9c34bc5d9f6cdc9d6afca45302e4a6 M: 825835dcfc9c34bc5d9f6cdc9d6afca45302e4a6 192.168.10.101:7002 slots:[5461-10922] (5462 slots) master 1 additional replica(s) S: e62234ef3c5206770ce29580c0309efb09a98429 192.168.10.101:7006 slots: (0 slots) slave replicates aa624181696765f568bee0e650c4af216ba83c23 [OK] All nodes agree about slots configuration. >>> Check for open slots... >>> Check slots coverage... [OK] All 16384 slots covered.
M是主,S是从
发现会自动保存数据到不同节点
root@57e92e2ffa4c:/data# redis-cli -c -h 192.168.10.101 -p 7001
192.168.10.101:7001> set 1 2
-> Redirected to slot [9842] located at 192.168.10.101:7002
OK
192.168.10.101:7002> get 1
"2"
192.168.10.101:7002> set 3 6
-> Redirected to slot [1584] located at 192.168.10.101:7001
OK
192.168.10.101:7001> set u 8
-> Redirected to slot [11826] located at 192.168.10.101:7003
OK
192.168.10.101:7003> cluster info
cluster_state:ok
cluster_slots_assigned:16384
cluster_slots_ok:16384
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:6
cluster_size:3
cluster_current_epoch:6
cluster_my_epoch:3
cluster_stats_messages_ping_sent:800
cluster_stats_messages_pong_sent:808
cluster_stats_messages_meet_sent:5
cluster_stats_messages_sent:1613
cluster_stats_messages_ping_received:808
cluster_stats_messages_pong_received:805
cluster_stats_messages_received:1613
192.168.10.101:7003> CLUSTER NODES
e62234ef3c5206770ce29580c0309efb09a98429 192.168.10.101:7006@17006 slave aa624181696765f568bee0e650c4af216ba83c23 0 1592193713000 6 connected
153c705b38fd253594a08989d49425d5ca4ee87f 192.168.10.101:7004@17004 slave 825835dcfc9c34bc5d9f6cdc9d6afca45302e4a6 0 1592193712000 4 connected
aa624181696765f568bee0e650c4af216ba83c23 192.168.10.101:7001@17001 master - 0 1592193712000 1 connected 0-5460
825835dcfc9c34bc5d9f6cdc9d6afca45302e4a6 192.168.10.101:7002@17002 master - 0 1592193714003 2 connected 5461-10922
08b1626502539c77ec469e2032e693718649f69d 192.168.10.101:7003@17003 myself,master - 0 1592193711000 3 connected 10923-16383
4c30599c2cabbb20eafc8b4eb9d4d93c597b20cc 192.168.10.101:7005@17005 slave 08b1626502539c77ec469e2032e693718649f69d 0 1592193712995 5 connected
Elasticsearch集群
一个运行中的ES实例称为一个节点,而集群是由一个或多个拥有相同cluster.name配置的节点组成,它们共同承担数据和负载的压力。当有节点加入集群中或者从集群中移除节点时,集群会重新平均分布所有的数据。
当一个节点被选举为主节点时,它将负责管理集群范围内的所有变更,例如增加、删除索引,增加、删除节点等。主节点不需要涉及到文档级别的变更和搜索等操作。任何节点都可以成为主节点。
作为用户,我们可以将请求发送到集群中的任何节点,包括主节点。每个节点都知道任意文档的位置,并且能将我们的请求直接转发到存储我们所需文档的节点。
集群健康是集群监控信息最重要的一项,status字段包括green、yellow或red。
分片
创建网络
[root@node03 ~]# docker network ls NETWORK ID NAME DRIVER SCOPE 11642c8df0a1 bridge bridge local f3824fdf854e host host local 3f3b1d92bc78 none null local [root@node03 ~]# docker network create --driver bridge --subnet=172.18.12.0/16 --gateway=172.18.1.1 mynet 60cbe8c50aa1cda30cf36571f8609bc7989d0040701932deba51c64a77796b49 [root@node03 ~]# docker network ls NETWORK ID NAME DRIVER SCOPE 11642c8df0a1 bridge bridge local f3824fdf854e host host local 60cbe8c50aa1 mynet bridge local 3f3b1d92bc78 none null local [root@node03 ~]# docker network inspect mynet [ { "Name": "mynet", "Id": "60cbe8c50aa1cda30cf36571f8609bc7989d0040701932deba51c64a77796b49", "Created": "2020-06-15T14:13:23.198137749+08:00", "Scope": "local", "Driver": "bridge", "EnableIPv6": false, "IPAM": { "Driver": "default", "Options": {}, "Config": [ { "Subnet": "172.18.12.0/16", "Gateway": "172.18.1.1" } ] }, "Internal": false, "Attachable": false, "Ingress": false, "ConfigFrom": { "Network": "" }, "ConfigOnly": false, "Containers": {}, "Options": {}, "Labels": {} } ]
修改虚拟内存,否则会报
max virutal memory areas vm.max_map_count [65530] is too low, increase to at least [262144]
1、切换到root用户修改配置sysctl.conf vi /etc/sysctl.conf 添加下面配置: vm.max_map_count=655360 并执行命令: sysctl -p
创建master节点
for port in $(seq 1 3); \ do \ mkdir -p /bigdata/elasticsearch/master-${port}/config mkdir -p /bigdata/elasticsearch/master-${port}/data chmod -R 777 /bigdata/elasticsearch/master-${port} touch /bigdata/elasticsearch/master-${port}/config/elasticsearch.yml cat << EOF > /bigdata/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.18.12.21:9301","172.18.12.22:9301","172.18.12.23:9301"] # 设置集群中的master节点的初始列表,看通过这些节点自动发现其他新加入集群的节点,es7新配置 cluster.initial_master_nodes: ["172.18.12.21"] # 新集群初始时的候选主节点,es7新增 EOF docker run -p 920${port}:920${port} -p 930${port}:930${port} --name elasticsearch-node-${port} \ --network=mynet --ip 172.18.12.2${port} \ -e ES_JAVA_OPTS="-Xms300m -Xmx300m" \ -v /bigdata/elasticsearch/master-${port}/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml \ -v /bigdata/elasticsearch/master-${port}/data:/usr/share/elasticsearch/data \ -v /bigdata/elasticsearch/master-${port}/plugins:/usr/share/elasticsearch/plugins \ -d elasticsearch:7.4.2; \ done
node节点
for port in $(seq 4 6); \ do \ mkdir -p /bigdata/elasticsearch/node-${port}/config mkdir -p /bigdata/elasticsearch/node-${port}/data chmod -R 777 /bigdata/elasticsearch/node-${port} touch /bigdata/elasticsearch/node-${port}/config/elasticsearch.yml cat << EOF > /bigdata/elasticsearch/node-${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.18.12.21:9301","172.18.12.22:9301","172.18.12.23:9301"] # 设置集群中的master节点的初始列表,看通过这些节点自动发现其他新加入集群的节点,es7新配置 cluster.initial_master_nodes: ["172.18.12.21"] # 新集群初始时的候选主节点,es7新增 EOF docker run -p 920${port}:920${port} -p 930${port}:930${port} --name elasticsearch-node-${port} \ --network=mynet --ip 172.18.12.2${port} \ -e ES_JAVA_OPTS="-Xms300m -Xmx300m" \ -v /bigdata/elasticsearch/node-${port}/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml \ -v /bigdata/elasticsearch/node-${port}/data:/usr/share/elasticsearch/data \ -v /bigdata/elasticsearch/node-${port}/plugins:/usr/share/elasticsearch/plugins \ -d elasticsearch:7.4.2; \ done
查看节点 http://192.168.10.102:9206/_cat/nodes
标星号的为master
查看集群健康状况 http://192.168.10.102:9206/_cluster/health?pretty
RebbitMQ集群
RabbitMQ是用Erlang开发的,集群非常方便,Erlang天生就是分布式语言,当其本身不支持负载均衡。
包括内存节点(RAM)、磁盘节点(Disk,消息持久化),至少有一个Disk节点。
普通模式
- 生产环境一般不用
镜像模式
- 各节点同步
集群搭建
mkdir /bigdata/rabbitmq cd /bigdata/rabbitmq mkdir rabbitmq01 rabbitmq02 rabbitmq03 docker run -d --hostname rabbitmq01 --name rabbitmq01 -v /bigdata/rabbitmq/rabbitmq01:/var/lib/rabbitmq -p 15673:15672 -p 5673:5672 -e RABBITMQ_ERLANG_COOKIE='atguigu' rabbitmq:management docker run -d --hostname rabbitmq02 --name rabbitmq02 -v /bigdata/rabbitmq/rabbitmq01:/var/lib/rabbitmq -p 15674:15672 -p 5674:5672 -e RABBITMQ_ERLANG_COOKIE='atguigu' --link rabbitmq01:rabbitmq01 rabbitmq:management docker run -d --hostname rabbitmq03 --name rabbitmq03 -v /bigdata/rabbitmq/rabbitmq01:/var/lib/rabbitmq -p 15675:15672 -p 5675:5672 -e RABBITMQ_ERLANG_COOKIE='atguigu' --link rabbitmq01:rabbitmq01 --link rabbitmq02:rabbitmq02 rabbitmq:management
第一个节点
[root@node03 rabbitmq]# docker exec -it rabbitmq01 /bin/bash root@rabbitmq01:/# rabbitmqctl stop_app Stopping rabbit application on node rabbit@rabbitmq01 ... root@rabbitmq01:/# rabbitmqctl reset Resetting node rabbit@rabbitmq01 ... root@rabbitmq01:/# rabbitmqctl start_app Starting node rabbit@rabbitmq01 ... root@rabbitmq01:/# exit exit
第二个节点
[root@node03 rabbitmq]# docker exec -it rabbitmq02 /bin/bash root@rabbitmq02:/# rabbitmqctl stop_app Stopping rabbit application on node rabbit@rabbitmq02 ... root@rabbitmq02:/# rabbitmqctl reset Resetting node rabbit@rabbitmq02 ... root@rabbitmq02:/# rabbitmqctl join_cluster --ram rabbit@rabbitmq01 Clustering node rabbit@rabbitmq02 with rabbit@rabbitmq01 root@rabbitmq02:/# rabbitmqctl start_app Starting node rabbit@rabbitmq02 ... root@rabbitmq02:/# exit exit
第三节点
[root@node03 rabbitmq]# docker exec -it rabbitmq03 /bin/bash root@rabbitmq03:/# rabbitmqctl stop_app Stopping rabbit application on node rabbit@rabbitmq03 ... ^[[Aroot@rabbitmq03rabbitmqctl reset Resetting node rabbit@rabbitmq03 ... root@rabbitmq03:/# rabbitmqctl join_cluster --ram rabbit@rabbitmq01 Clustering node rabbit@rabbitmq03 with rabbit@rabbitmq01 root@rabbitmq03:/# rabbitmqctl start_app Starting node rabbit@rabbitmq03 ... root@rabbitmq03:/# exit exit
实现镜像集群
随便进入一个节点
[root@node03 rabbitmq]# docker exec -it rabbitmq01 bash
ha策略
root@rabbitmq01:/# rabbitmqctl set_policy -p / ha "^" '{"ha-mode":"all","ha-sync-mode":"automatic"}' Setting policy "ha" for pattern "^" to "{"ha-mode":"all","ha-sync-mode":"automatic"}" with priority "0" for vhost "/" ...
/ 当前主机
查看策略
root@rabbitmq01:/# rabbitmqctl list_policies -p / Listing policies for vhost "/" ... vhost name pattern apply-to definition priority / ha ^ all {"ha-mode":"all","ha-sync-mode":"automatic"} 0
01中添加一个队列
发现03中也有该队列
K8S部署MySQL集群
- 有状态服务抽取配置为ConfigMap
- 有状态服务必须使用pvc持久数据
- 服务集群内访问使用DNS提供的稳定域名
登录project-regular
进入项目
配置,创建配置
下一步,添加数据
[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_ums
binlog-do-db=gulimall_pms
binlog-do-db=gulimall_oms
binlog-do-db=gulimall_sms
binlog-do-db=gulimall_wms
binlog-do-db=gulimall_admin
replicate-ignore-db=mysql
replicate-ignore-db=sys
replicate-ignore-db=information_schema
replicate-ignore-db=performance_schema
创建
存储卷
创建
点 应用负载,点 服务,创建 有状态服务
容器组分散部署,添加容器镜像
使用默认端口
点 高级设置
环境变量,引用配置文件或密钥
挂载配置文件或密钥
添加存储卷
创建
slaver
[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_ums binlog-do-db=gulimall_pms binlog-do-db=gulimall_oms binlog-do-db=gulimall_sms binlog-do-db=gulimall_wms binlog-do-db=gulimall_admin replicate-ignore-db=mysql replicate-ignore-db=sys replicate-ignore-db=information_schema replicate-ignore-db=performance_schema
进入master容器
打开命令行
# mysql -uroot -p123456 mysql: [Warning] Using a password on the command line interface can be insecure. Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 2 Server version: 5.7.30-log MySQL Community Server (GPL) Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. mysql> GRANT REPLICATION SLAVE ON *.* TO 'backup'@'%' identified by '123456'; Query OK, 0 rows affected, 1 warning (0.00 sec) mysql> show master status; +------------------+----------+---------------------------------------------------------------------------------+------------------+-------------------+ | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set | +------------------+----------+---------------------------------------------------------------------------------+------------------+-------------------+ | mysql-bin.000003 | 439 | gulimall_ums,gulimall_pms,gulimall_oms,gulimall_sms,gulimall_wms,gulimall_admin | | | +------------------+----------+---------------------------------------------------------------------------------+------------------+-------------------+ 1 row in set (0.00 sec)
# mysql -uroot -p123456 mysql: [Warning] Using a password on the command line interface can be insecure. Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 3 Server version: 5.7.30-log MySQL Community Server (GPL) Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. mysql> change master to master_host='mysql-master.gulimall',master_user='backup',master_password='123456',master_log_file='mysql-bin.000003',master_log_pos=0,master_port=3306; Query OK, 0 rows affected, 2 warnings (0.02 sec) mysql> start slave; Query OK, 0 rows affected (0.01 sec)
关键:
- 有状态服务
- 挂载配置文件和PVC
- IP都用域名
Redis
创建配置
创建pvc
创建有状态服务
redis:5.0.7镜像
没有环境变量但是要修改启动命令
ElasticSearch
elasticsearch-pvc
创建有状态服务
创建kibana
RabitMQ
存储卷
有状态服务
部署nacos
创建pvc
zipkin
sentinel
K8S部署应用
- 为每个项目准备一个Dockerfile,Docker按照这个Dockerfile将项目制作成镜像
- 为每个项目生成k8s的描述文件
- Jenkins编写好Jenkinsfile
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从二进制到误差:逐行拆解C语言浮点运算中的4008175468544之谜
· .NET制作智能桌面机器人:结合BotSharp智能体框架开发语音交互
· 软件产品开发中常见的10个问题及处理方法
· .NET 原生驾驭 AI 新基建实战系列:向量数据库的应用与畅想
· 从问题排查到源码分析:ActiveMQ消费端频繁日志刷屏的秘密
· C# 13 中的新增功能实操
· Ollama本地部署大模型总结
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(4)
· langchain0.3教程:从0到1打造一个智能聊天机器人
· 2025成都.NET开发者Connect圆满结束