MongoShake——基于MongoDB的跨数据中心的数据复制平台

摘要: MongoShake是基于MongoDB的通用型平台服务,作为数据连通的桥梁,打通各个闭环节点的通道。通过MongoShake的订阅消费,可以灵活对接以适应不同场景,例如日志订阅、数据中心同步、监控审计等。其中,集群数据同步作为核心应用场景,能够灵活实现灾备和多活的业务场景。

背景

在当前的数据库系统生态中,大部分系统都支持多个节点实例间的数据同步机制,如Mysql Master/Slave主从同步,Redis AOF主从同步等,MongoDB更是支持3节点及以上的副本集同步,上述机制很好的支撑了一个逻辑单元的数据冗余高可用。

跨逻辑单元,甚至跨单元、跨数据中心的数据同步,在业务层有时候就显得很重要,它使得同城多机房的负载均衡,多机房的互备,甚至是异地多数据中心容灾和多活成为可能。由于目前MongoDB副本集内置的主从同步对于这种业务场景有较大的局限性,为此,我们开发了MongoShake系统,可以应用在实例间复制,机房间、跨数据中心复制,满足灾备和多活需求。

另外,数据备份是作为MongoShake核心但不是唯一的功能。MongoShake作为一个平台型服务,用户可以通过对接MongoShake,实现数据的订阅消费来满足不同的业务场景。

简介

MongoShake是一个以golang语言进行编写的通用的平台型服务,通过读取MongoDB集群的Oplog操作日志,对MongoDB的数据进行复制,后续通过操作日志实现特定需求。日志可以提供很多场景化的应用,为此,我们在设计时就考虑了把MongoShake做成通用的平台型服务。通过操作日志,我们提供日志数据订阅消费PUB/SUB功能,可通过SDK、Kafka、MetaQ等方式灵活对接以适应不同场景(如日志订阅、数据中心同步、Cache异步淘汰等)。集群数据同步是其中核心应用场景,通过抓取oplog后进行回放达到同步目的,实现灾备和多活的业务场景。

应用场景举例

  1. MongoDB集群间数据的异步复制,免去业务双写开销。
  2. MongoDB集群间数据的镜像备份(当前1.0开源版本支持受限)
  3. 日志离线分析
  4. 日志订阅
  5. 数据路由。根据业务需求,结合日志订阅和过滤机制,可以获取关注的数据,达到数据路由的功能。
  6. Cache同步。日志分析的结果,知道哪些Cache可以被淘汰,哪些Cache可以进行预加载,反向推动Cache的更新。
  7. 基于日志的集群监控

功能介绍

MongoShake从源库抓取oplog数据,然后发送到各个不同的tunnel通道。现有通道类型有:

  1. Direct:直接写入目的MongoDB

  2. RPC:通过net/rpc方式连接

  3. TCP:通过tcp方式连接

  4. File:通过文件方式对接

  5. Kafka:通过Kafka方式对接

  6. Mock:用于测试,不写入tunnel,抛弃所有数据

消费者可以通过对接tunnel通道获取关注的数据,例如对接Direct通道直接写入目的MongoDB,或者对接RPC进行同步数据传输等。此外,用户还可以自己创建自己的API进行灵活接入。下面2张图给出了基本的架构和数据流。

未分类

未分类

MongoShake对接的源数据库支持单个mongod,replica set和sharding三种模式。目的数据库支持mongod和mongos。如果源端数据库为replica set,我们建议对接备库以减少主库的压力;如果为sharding模式,那么每个shard都将对接到MongoShake并进行并行抓取。对于目的库来说,可以对接多个mongos,不同的数据将会哈希后写入不同的mongos。

并行复制

MongoShake提供了并行复制的能力,复制的粒度选项(shard_key)可以为:id,collection或者auto,不同的文档或表可能进入不同的哈希队列并发执行。id表示按文档进行哈希;collection表示按表哈希;auto表示自动配置,如果有表存在唯一键,则退化为collection,否则则等价于id。

HA方案

MongoShake定期将同步上下文进行存储,存储对象可以为第三方API(注册中心)或者源库。目前的上下文内容为“已经成功同步的oplog时间戳”。在这种情况下,当服务切换或者重启后,通过对接该API或者数据库,新服务能够继续提供服务。

此外,MongoShake还提供了Hypervisor机制用于在服务挂掉的时候,将服务重新拉起。

过滤

提供黑名单和白名单机制选择性同步db和collection。

压缩

支持oplog在发送前进行压缩,目前支持的压缩格式有gzip, zlib, 或deflate。

Gid

一个数据库的数据可能会包含不同来源:自己产生的和从别处复制的数据。如果没有相应的措施,可能会导致数据的环形复制,比如A的数据复制到B,又被从B复制到A,导致服务产生风暴被打挂了。或者从B回写入A时因为唯一键约束写入失败。从而导致服务的不稳定。

在阿里云上的MongoDB版本中,我们提供了防止环形复制的功能。其主要原理是,通过修改MongoDB内核,在oplog中打入gid标识当前数据库信息,并在复制过程中通过op_command命令携带gid信息,那么每条数据都有来源信息。如果只需要当前数据库产生的数据,那么只抓取gid等于该数据库id的oplog即可。所以,在环形复制的场景下,MongoShake从A数据库抓取gid等于id_A(A的gid)的数据,从B数据库抓取gid等于id_B(B的gid)的数据即可解决这个问题。

说明:由于MongoDB内核gid部分的修改尚未开源,所以开源版本下此功能受限,但在阿里云MongoDB版本已支持。这也是为什么我们前面提到的“MongoDB集群间数据的镜像备份”在目前开源版本下功能受限的原因。

Checkpoint

MongShake采用了ACK机制确保oplog成功回放,如果失败将会引发重传,传输重传的过程类似于TCP的滑动窗口机制。这主要是为了保证应用层可靠性而设计的,比如解压缩失败等等。为了更好的进行说明,我们先来定义几个名词:

LSN(Log Sequence Number),表示已经传输的最新的oplog序号。

LSN_ACK(Acked Log Sequence Number),表示已经收到ack确认的最大LSN,即写入tunnel成功的LSN。

LSN_CKPT(Checkpoint Log Sequence Number),表示已经做了checkpoint的LSN,即已经持久化的LSN。

LSN、LSN_ACK和LSN_CKPT的值均来自于Oplog的时间戳ts字段,其中隐含约束是:LSN_CKPT<=LSN_ACK<=LSN

未分类

如上图所示,LSN=16表示已经传输了16条oplog,如果没有重传的话,下次将传输LSN=17;LSN_ACK=13表示前13条都已经收到确认,如果需要重传,最早将从LSN=14开始;LSN_CKPT=8表示已经持久化checkpoint=8。持久化的意义在于,如果此时MongoShake挂掉重启后,源数据库的oplog将从LSN_CKPT位置开始读取而不是从头LSN=1开始读。因为oplog DML的幂等性,同一数据多次传输不会产生问题。但对于DDL,重传可能会导致错误。

排障和限速

MongoShake对外提供Restful API,提供实时查看进程内部各队列数据的同步情况,便于问题排查。另外,还提供限速功能,方便用户进行实时控制,减轻数据库压力。

冲突检测

目前MongoShake支持表级别(collection)和文档级别(id)的并发,id级别的并发需要db没有唯一索引约束,而表级别并发在表数量小或者有些表分布非常不均匀的情况下性能不佳。所以在表级别并发情况下,需要既能均匀分布的并发,又能解决表内唯一键冲突的情况。为此,如果tunnel类型是direct时候,我们提供了写入前的冲突检测功能。

目前索引类型仅支持唯一索引,不支持前缀索引、稀疏索引、TTL索引等其他索引。

冲突检测功能的前提需要满足两个前提约束条件:

  1. MongoShake认为同步的MongoDB Schema是一致的,也不会监听Oplog的System.indexes表的改动

  2. 冲突索引以Oplog中记录的为准,不以当前MongoDB中索引作为参考。

另外,MongoShake在同步过程中对索引的操作可能会引发异常情况:

  1. 正在创建索引。如果是后台建索引,这段时间的写请求是看不到该索引的,但内部对该索引可见,同时可能会导致内存使用率会过高。如果是前台建索引,所有用户请求是阻塞的,如果阻塞时间过久,将会引发重传。

  2. 如果目的库存在的唯一索引在源库没有,造成数据不一致,不进行处理。

  3. oplog产生后,源库才增加或删除了唯一索引,重传可能导致索引的增删存在问题,我们也不进行处理。

为了支持冲突检测功能,我们修改了MongoDB内核,使得oplog中带入uk字段,标识涉及到的唯一索引信息,如:

{
    "ts" : Timestamp(1484805725, 2),
    "t" : NumberLong(3),
    "h" : NumberLong("-6270930433887838315"),
    "v" : 2,
    "op" : "u",
    "ns" : "benchmark.sbtest10",
    "o" : { "_id" : 1, "uid" : 1111, "other.sid":"22222", "mid":8907298448, "bid":123 }
    "o2" : {"_id" : 1}
    "uk" : {
            "uid": "1110"
            "mid^bid": [8907298448, 123]
            "other.sid_1": "22221"
    }
}

uk下面的key表示唯一键的列名,key用“^”连接的表示联合索引,上面记录中存在3个唯一索引:uid、mid和bid的联合索引、other.sid_1。value在增删改下具有不同意义:如果是增加操作,则value为空;如果是删除或者修改操作,则记录删除或修改前的值。

具体处理流程如下:将连续的k个oplog打包成一个batch,流水式分析每个batch之内的依赖,划分成段。如果存在冲突,则根据依赖和时序关系,将batch切分成多个段;如果不存在冲突,则划分成一个段。然后对段内进行并发写入,段与段之间顺序写入。段内并发的意思是多个并发线程同时对段内数据执行写操作,但同一个段内的同一个id必须保证有序;段之间保证顺序执行:只有前面一个段全部执行完毕,才会执行后续段的写入。

如果一个batch中,存在不同的id的oplog同时操作同一个唯一键,则认为这些oplog存在时序关系,也叫依赖关系。我们必须将存在依赖关系的oplog拆分到2个段中。

MongoShake中处理存在依赖关系的方式有2种:

(1) 插入barrier

通过插入barrier将batch进行拆分,每个段内进行并发。举个例子,如下图所示:

未分类

ID表示文档id,op表示操作,i为插入,u为更新,d为删除,uk表示该文档下的所有唯一键, uk={a:3} => uk={a:1}表示将唯一键的值从a=3改为a=1,a为唯一键。

在开始的时候,batch中有9条oplog,通过分析uk关系对其进行拆分,比如第3条和第4条,在id不一致的情况下操作了同一个uk={a:3},那么第3条和第4条之间需要插入barrier(修改前或者修改后无论哪个相同都算冲突),同理第5条和第6条,第6条和第7条。同一个id操作同一个uk是允许的在一个段内是允许的,所以第2条和第3条可以分到同一个段中。拆分后,段内根据id进行并发,同一个id仍然保持有序:比如第一个段中的第1条和第2,3条可以进行并发,但是第2条和第3条需要顺序执行。

(2) 根据关系依赖图进行拆分

每条oplog对应一个时间序号N,那么每个序号N都可能存在一个M使得:

如果M和N操作了同一个唯一索引的相同值,且M序号小于N,则构建M到N的一条有向边。
如果M和N的文档ID相同且M序号小于N,则同样构建M到N的一条有向边。
由于依赖按时间有序,所以一定不存在环。
所以这个图就变成了一个有向无环图,每次根据拓扑排序算法并发写入入度为0(没有入边)的点即可,对于入度非0的点等待入度变为0后再写入,即等待前序结点执行完毕后再执行写入。

下图给出了一个例子:一共有10个oplog结点,一个横线表示文档ID相同,右图箭头方向表示存在唯一键冲突的依赖关系。那么,该图一共分为4次执行:并发处理写入1,2,4,5,然后是3,6,8,其次是7,10,最后是9。

未分类

未分类

说明:由于MongoDB中冲突检测uk部分的修改尚未开源,所以开源版本下此功能受限,但在阿里云MongoDB版本已支持。

架构和数据流

未分类

上图展示了MongoShake内部架构和数据流细节。总体来说,整个MongoShake可以大体分为3大部分:Syncer、Worker和Replayer,其中Replayer只用于tunnel类型为direct的情况。

Syncer负责从源数据库拉取数据,如果源是Mongod或者ReplicaSet,那么Syncer只有1个,如果是Sharding模式,那么需要有多个Syncer与Shard一一对应。在Syncer内部,首先fetcher用mgo.v2库从源库中抓取数据然后batch打包后放入PendingQueue队列,deserializer线程从PendingQueue中抓取数据进行解序列化处理。Batcher将从LogsQueue中抓取的数据进行重新组织,将前往同一个Worker的数据聚集在一起,然后hash发送到对应Worker队列。

Worker主要功能就是从WorkerQueue中抓取数据,然后进行发送,由于采用ack机制,所以会内部维持几个队列,分别为未发送队列和已发送队列,前者存储未发送的数据,后者存储发送但是没有收到ack确认的数据。发送后,未发送队列的数据会转移到已发送队列;收到了对端的ack回复,已发送队列中seq小于ack的数据将会被删除,从而保证了可靠性。

Worker可以对接不同的Tunnel通道,满足用户不同的需求。如果通道类型是direct,那么将会对接Replayer进行直接写入目的MongoDB操作,Worker与Replayer一一对应。首先,Replayer将收到的数据根据冲突检测规则分发到不同的ExecutorQueue,然后executor从队列中抓取进行并发写入。为了保证写入的高效性,MongoShake在写入前还会对相邻的相同Operation和相同Namespace的Oplog进行合并。

用户使用案例

高德地图 App是国内首屈一指的地图及导航应用,阿里云MongoDB数据库服务为该应用提供了部分功能的存储支撑,存储亿级别数据。现在高德地图使用国内双中心的策略,通过地理位置等信息路由最近中心提升服务质量,业务方(高德地图)通过用户路由到三个城市数据中心,如下图所示,机房数据之间无依赖计算。

未分类

这三个城市地理上从北到南横跨了整个中国 ,这对多数据中心如何做好复制、容灾提出了挑战,如果某个地域的机房、网络出现问题,可以平滑的将流量切换到另一个地方,做到用户几乎无感知?

目前我们的策略是,拓扑采用机房两两互联方式,每个机房的数据都将同步到另外两个机房。然后通过高德的路由层,将用户请求路由到不同的数据中心,读写均发送在同一个数据中心,保证一定的事务性。然后再通过MongoShake,双向异步复制两个数据中心的数据,这样保证每个数据中心都有全量的数据(保证最终一致性) 。任意机房出现问题,另两个机房中的一个可以通过切换后提供读写服务。下图展示了城市1和城市2机房的同步情况。

未分类

遇到某个单元不能访问的问题,通过MongoShake对外开放的Restful管理接口,可以获得各个机房的同步偏移量和时间戳,通过判断采集和写入值即可判断异步复制是否在某个时间点已经完成。再配合业务方的DNS切流,切走单元流量并保证原有单元的请求在新单元是可以读写的,如下图所示。

未分类

性能测试数据

具体测试数据请参考性能测试文档。

开源

MongoShake的github地址https://github.com/aliyun/mongo-shake?spm=a2c4e.11153940.blogcont603329.16.12416563VjpGZE , 欢迎加入!

后续

MongoShake将会长期维护,大版本和小版本将会进行持续迭代。欢迎提问留言以及加入一起进行开源开发。

另外,阿里云MongoDB团队招人,包括云平台开发和数据库内核开发,语言不限,欢迎投递简历以及来信咨询:[email protected]

centos7.2 kvm虚拟化管理平台WebVirtMgr部署

在服务器上部署kvm虚拟化,虚出多台VM出来,以应对新的测试需求。
当KVM宿主机越来越多,需要对宿主机的状态进行调控,决定采用WebVirtMgr作为kvm虚拟化的web管理工具,图形化的WEB,让人能更方便的查看kvm 宿主机的情况和操作
WebVirtMgr是近两年来发展较快,比较活跃,非常清新的一个KVM管理平台,提供对宿主机和虚机的统一管理,它有别于kvm自带的图形管理工具(virtual machine manager),让kvm管理变得更为可视化,对中小型kvm应用场景带来了更多方便。
WebVirtMgr采用几乎纯Python开发,其前端是基于Python的Django,后端是基于Libvirt的Python接口,将日常kvm的管理操作变的更加的可视化。

WebVirtMgr特点

  • 操作简单,易于使用
  • 通过libvirt的API接口对kvm进行管理
  • 提供对虚拟机生命周期管理

WebVirtMgr 功能

宿主机管理支持以下功能

  • CPU利用率
  • 内存利用率
  • 网络资源池管理
  • 存储资源池管理
  • 虚拟机镜像
  • 虚拟机克隆
  • 快照管理
  • 日志管理
  • 虚机迁移

虚拟机管理支持以下功能

  • CPU利用率
  • 内存利用率
  • 光盘管理
  • 关/开/暂停虚拟机
  • 安装虚拟机
  • VNC console连接
  • 创建快照

下面对部署过程进行记录,希望能帮助到有用到的朋友们。
这里我将webvirtmgr服务器和kvm服务器放在同一台机器上部署的,即单机部署

  • 系统:Centos 7.2
  • 内存:64G
  • CPU:32核
  • ip:192.168.56.50(内网),192.168.0.29(外网)

一、基础环境

#close firewalld and NetworkManager
[root@linux-node1 ~]# systemctl disable firewalld
[root@linux-node1 ~]# systemctl disable NetworkManager

#关闭SELinux
sed -i 's/SELINUX=enforcing/SELINUX=disabled/g' /etc/selinux/config
setenforce 0

1.1 开启blos 开启vt,检查

1)查看是否支持虚拟机
说明1:半虚拟化是不能运行与安装KVM虚拟机的。
[root@ops ~]#egrep '(vmx|svm)' --color=always /proc/cpuinfo

1.2 系统版本

[root@webvirtmgr-node1 ~]# cat /etc/redhat-release 
CentOS Linux release 7.2.1511 (Core) 

[root@webvirtmgr-node1 ~]# uname -r
3.10.0-327.el7.x86_64

1.3 安装epel源

#备份源
yum install wget -y
mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup
wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo
yum -y install epel-release
yum clean all
yum makecache
#install software
yum install net-tools vim lrzsz -y

1.4 安装kvm软件

yum install qemu-kvm libvirt libvirt-python libguestfs-tools virt-install virt-manager python-virtinst libvirt-client virt-viewer -y

1.5 本机网络

[root@webvirtmg ~]# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN 
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
    link/ether 00:0c:29:68:4b:e3 brd ff:ff:ff:ff:ff:ff
    inet 192.168.0.50/24 brd 192.168.0.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::20c:29ff:fe68:4be3/64 scope link 
       valid_lft forever preferred_lft forever
3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
    link/ether 00:0c:29:68:4b:ed brd ff:ff:ff:ff:ff:ff
    inet 192.168.56.50/24 brd 192.168.56.255 scope global eth1
       valid_lft forever preferred_lft forever
    inet6 fe80::20c:29ff:fe68:4bed/64 scope link 
       valid_lft forever preferred_lft forever

1.6 配置桥接网络,(备注:br0绑定eth1)

[root@webvirtmg ~]# cd /etc/sysconfig/network-scripts/

[root@webvirtmg network-scripts]# cat ifcfg-eth1
TYPE=Ethernet
BOOTPROTO=static
DEFROUTE=yes
IPV4_FAILURE_FATAL=no
NAME=eth1
DEVICE=eth1
ONBOOT=yes
IPADDR=192.168.56.50
PREFIX=24
GATEWAY=192.168.56.2
DNS1=192.168.56.2
BRIDGE=br0 
NM_CONTROLLED=no

[root@webvirtmg network-scripts]# cat ifcfg-br0
TYPE=Bridge
DEVICE=br0
NM_CONTROLLED=no
BOOTPROTO=static
DEFROUTE=yes
IPV4_FAILURE_FATAL=no
NAME=eth1
ONBOOT=yes
IPADDR=192.168.56.50
PREFIX=24
GATEWAY=192.168.56.2
DNS1=192.168.56.2

#重启网络服务

systemctl restart network

1.7 启动libvirt

[root@webvirtmgr-node1 ~]# systemctl restart libvirtd
[root@webvirtmgr-node1 ~]# systemctl status libvirtd

1.8 测试

[root@webvirtmgr-node1 ~]# virsh -c qemu:///system list
 Id    Name                           State
----------------------------------------------------

[root@webvirtmgr-node1 ~]# virsh --version
3.9.0
[root@webvirtmgr-node1 ~]# virt-install --version
1.4.3
[root@webvirtmgr-node1 ~]# ln -s /usr/libexec/qemu-kvm /usr/bin/qemu-kvm
[root@webvirtmgr-node1 ~]# lsmod |grep kvm
kvm_intel 162153 0 
kvm 525259 1 kvm_intel

1.9 查看网桥

[root@webvirtmg ~]# brctl show
bridge name bridge id       STP enabled interfaces
br0     8000.000c29684bed   no      eth1
virbr0      8000.000000000000   yes

二、部署webvirtmgr

参考官网:https://github.com/retspen/webvirtmgr/wiki/Install-WebVirtMgr

1.1 安装依赖包

yum install git python-pip libvirt-python libxml2-python python-websockify supervisor nginx -y

1.2 从git-hub中下载相关的webvirtmgr代码

[root@openstack ops]# cd /usr/local/src/
[root@openstack src]# git clone git://github.com/retspen/webvirtmgr.git    (下载地址:https://pan.baidu.com/s/1pLS3kCj      获取密码:8efm)

1.3 安装webvirtmgr

[root@openstack src]# cd webvirtmgr/
[root@openstack webvirtmgr]# pip install -r requirements.txt

1.4 检查sqlite3 (备注:自带不需要安装,导入模块检查一下。)

[root@webvirtmg webvirtmgr]# python
Python 2.7.5 (default, Nov 20 2015, 02:00:19) 
[GCC 4.8.5 20150623 (Red Hat 4.8.5-4)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import sqlite3
>>> exit()

1.5、初始化账号

[root@webvirtmg webvirtmgr]# pwd
/usr/local/src/webvirtmgr<br>
[root@webvirtmg webvirtmgr]# ./manage.py syncdb
WARNING:root:No local_settings file found.
Creating tables ...
Creating table auth_permission
Creating table auth_group_permissions
Creating table auth_group
Creating table auth_user_groups
Creating table auth_user_user_permissions
Creating table auth_user
Creating table django_content_type
Creating table django_session
Creating table django_site
Creating table servers_compute
Creating table instance_instance
Creating table create_flavor

You just installed Django's auth system, which means you don't have any superusers defined.
Would you like to create one now? (yes/no): yes
Username (leave blank to use 'root'): admin
Email address: [email protected]
Password:
Password (again):
Superuser created successfully.
Installing custom SQL ...
Installing indexes ...
Installed 6 object(s) from 1 fixture(s)

1.6 拷贝web到 相关目录

[root@openstack ops]# mkdir -pv /var/www
[root@openstack ops]# cp -Rv /usr/local/src/webvirtmgr /var/www/webvirtmgr

1.7 设置ssh

[root@openstack ops]# ssh-keygen -t rsa             //产生公私钥
[root@openstack ops]# ssh-copy-id 192.168.1.17        //由于这里webvirtmgr和kvm服务部署在同一台机器,所以这里本地信任。如果kvm部署在其他机器,那么这个是它的ip
[root@openstack ops]# ssh 192.168.1.17 -L localhost:8000:localhost:8000 -L localhost:6080:localhost:60

1.8 编辑nginx配置文件

#添加这行代码: include /etc/nginx/conf.d/*.conf;

[root@webvirtmg ~]# cd /etc/nginx/
[root@webvirtmg  nginx]# mv nginx.conf /tmp
[root@webvirtmg  nginx]#cp nginx.conf.default nginx.conf

#编辑配置文件
[root@webvirtmg  nginx]#vi nginx.conf

[root@webvirtmg nginx]# cat nginx.conf

#user  nobody;
worker_processes  1;

#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;

#pid        logs/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include       mime.types;
    default_type  application/octet-stream;
    include /etc/nginx/conf.d/*.conf;

    #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
    #                  '$status $body_bytes_sent "$http_referer" '
    #                  '"$http_user_agent" "$http_x_forwarded_for"';

    #access_log  logs/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    #keepalive_timeout  0;
    keepalive_timeout  65;

    #gzip  on;

    server {
        listen       80;
        server_name  localhost;

        #charset koi8-r;

        #access_log  logs/host.access.log  main;

        location / {
            root   html;
            index  index.html index.htm;
        }

        #error_page  404              /404.html;

        # redirect server error pages to the static page /50x.html
        #
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }

        # proxy the PHP scripts to Apache listening on 127.0.0.1:80
        #
        #location ~ .php$ {
        #    proxy_pass   http://127.0.0.1;
        #}

        # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
        #
        #location ~ .php$ {
        #    root           html;
        #    fastcgi_pass   127.0.0.1:9000;
        #    fastcgi_index  index.php;
        #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
        #    include        fastcgi_params;
        #}

        # deny access to .htaccess files, if Apache's document root
        # concurs with nginx's one
        #
        #location ~ /.ht {
        #    deny  all;
        #}
    }


    # another virtual host using mix of IP-, name-, and port-based configuration
    #
    #server {
    #    listen       8000;
    #    listen       somename:8080;
    #    server_name  somename  alias  another.alias;

    #    location / {
    #        root   html;
    #        index  index.html index.htm;
    #    }
    #}


    # HTTPS server
    #
    #server {
    #    listen       443 ssl;
    #    server_name  localhost;

    #    ssl_certificate      cert.pem;
    #    ssl_certificate_key  cert.key;

    #    ssl_session_cache    shared:SSL:1m;
    #    ssl_session_timeout  5m;

    #    ssl_ciphers  HIGH:!aNULL:!MD5;
    #    ssl_prefer_server_ciphers  on;

    #    location / {
    #        root   html;
    #        index  index.html index.htm;
    #    }
    #}

}

#添加 /etc/nginx/conf.d/webvirtmgr.conf 配置文件

[root@webvirtmg nginx]# vim /etc/nginx/conf.d/webvirtmgr.conf  
server {
listen 80 default_server;

server_name $hostname;
#access_log /var/log/nginx/webvirtmgr_access_log;

location /static/ {
root /var/www/webvirtmgr/webvirtmgr; # or /srv instead of /var
expires max;
}

location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-for $proxy_add_x_forwarded_for;
proxy_set_header Host $host:$server_port;
proxy_set_header X-Forwarded-Proto $remote_addr;
proxy_connect_timeout 600;
proxy_read_timeout 600;
proxy_send_timeout 600;
client_max_body_size 1024M; # Set higher depending on your needs
}
}

#重启nginx服务

systemctl restart nginx

1.9 修改防火墙规则

#修改防火墙规则
[root@ops ~]# vim /etc/sysconfig/selinux 
......
SELINUX=disabled

#临时生效
[root@ops ~]# setenforce 0
setenforce: SELinux is disabled

#查看状态
[root@ops ~]# getenforce 
Disabled

#直接执行这行
/usr/sbin/setsebool httpd_can_network_connect true

2.0 授权

chown -R nginx:nginx /var/www/webvirtmgr

2.1 设置 supervisor (如果iptables防火墙开启的话,就必须要开通80、8000、6080端口访问)

[root@test]# vim /etc/supervisord.conf     //在文件末尾添加,注意将默认的python改为python2,因为上面只有用这个版本执行才不报错!
[program:webvirtmgr]
command=/usr/bin/python2 /var/www/webvirtmgr/manage.py run_gunicorn -c /var/www/webvirtmgr/conf/gunicorn.conf.py                     //启动8000端口
directory=/var/www/webvirtmgr
autostart=true
autorestart=true
logfile=/var/log/supervisor/webvirtmgr.log
log_stderr=true
user=nginx

[program:webvirtmgr-console]
command=/usr/bin/python2 /var/www/webvirtmgr/console/webvirtmgr-console                               //启动6080端口(这是控制台vnc端口)
directory=/var/www/webvirtmgr
autostart=true
autorestart=true
stdout_logfile=/var/log/supervisor/webvirtmgr-console.log
redirect_stderr=true
user=nginx

#检查

#检查
[root@test]#vim /var/www/webvirtmgr/conf/gunicorn.conf.py    //确保下面bind绑定的是本机的8000端口,这个在nginx配置中定义了,被代理的端口
bind = '127.0.0.1:8000'

#设置开机启动
[root@webvirtmg nginx]# systemctl enable supervisord.service

#设置开机加载
[root@webvirtmg nginx]#vim /etc/rc.local /usr/sbin/setsebool httpd_can_network_connect true

#重启服务
[root@webvirtmg nginx]# systemctl restart supervisord
[root@webvirtmg nginx]# systemctl status supervisord
● supervisord.service - Process Monitoring and Control Daemon
Loaded: loaded (/usr/lib/systemd/system/supervisord.service; enabled; vendor preset: disabled)
Active: active (running) since Thu 2018-06-28 09:37:15 CST; 6s ago
Process: 19369 ExecStart=/usr/bin/supervisord -c /etc/supervisord.conf (code=exited, status=0/SUCCESS)
Main PID: 19372 (supervisord)
CGroup: /system.slice/supervisord.service
├─19372 /usr/bin/python /usr/bin/supervisord -c /etc/supervisord.conf
├─19373 /usr/bin/python2 /var/www/webvirtmgr/console/webvirtmgr-console
├─19374 /usr/bin/python2 /var/www/webvirtmgr/manage.py run_gunicorn -c /var/www/webvirtmgr/conf/gunicorn.conf.py...
├─19380 /usr/bin/python2 /var/www/webvirtmgr/manage.py run_gunicorn -c /var/www/webvirtmgr/conf/gunicorn.conf.py...
├─19381 /usr/bin/python2 /var/www/webvirtmgr/manage.py run_gunicorn -c /var/www/webvirtmgr/conf/gunicorn.conf.py...
├─19382 /usr/bin/python2 /var/www/webvirtmgr/manage.py run_gunicorn -c /var/www/webvirtmgr/conf/gunicorn.conf.py...
├─19383 /usr/bin/python2 /var/www/webvirtmgr/manage.py run_gunicorn -c /var/www/webvirtmgr/conf/gunicorn.conf.py...
├─19384 /usr/bin/python2 /var/www/webvirtmgr/manage.py run_gunicorn -c /var/www/webvirtmgr/conf/gunicorn.conf.py...
├─19385 /usr/bin/python2 /var/www/webvirtmgr/manage.py run_gunicorn -c /var/www/webvirtmgr/conf/gunicorn.conf.py...
├─19386 /usr/bin/python2 /var/www/webvirtmgr/manage.py run_gunicorn -c /var/www/webvirtmgr/conf/gunicorn.conf.py...
├─19387 /usr/bin/python2 /var/www/webvirtmgr/manage.py run_gunicorn -c /var/www/webvirtmgr/conf/gunicorn.conf.py...
├─19388 /usr/bin/python2 /var/www/webvirtmgr/manage.py run_gunicorn -c /var/www/webvirtmgr/conf/gunicorn.conf.py...
├─19389 /usr/bin/python2 /var/www/webvirtmgr/manage.py run_gunicorn -c /var/www/webvirtmgr/conf/gunicorn.conf.py...
├─19390 /usr/bin/python2 /var/www/webvirtmgr/manage.py run_gunicorn -c /var/www/webvirtmgr/conf/gunicorn.conf.py...
├─19391 /usr/bin/python2 /var/www/webvirtmgr/manage.py run_gunicorn -c /var/www/webvirtmgr/conf/gunicorn.conf.py...
├─19392 /usr/bin/python2 /var/www/webvirtmgr/manage.py run_gunicorn -c /var/www/webvirtmgr/conf/gunicorn.conf.py...
├─19393 /usr/bin/python2 /var/www/webvirtmgr/manage.py run_gunicorn -c /var/www/webvirtmgr/conf/gunicorn.conf.py...
├─19394 /usr/bin/python2 /var/www/webvirtmgr/manage.py run_gunicorn -c /var/www/webvirtmgr/conf/gunicorn.conf.py...
├─19395 /usr/bin/python2 /var/www/webvirtmgr/manage.py run_gunicorn -c /var/www/webvirtmgr/conf/gunicorn.conf.py...
└─19396 /usr/bin/python2 /var/www/webvirtmgr/manage.py run_gunicorn -c /var/www/webvirtmgr/conf/gunicorn.conf.py...

Jun 28 09:37:15 webvirtmg.com systemd[1]: Starting Process Monitoring and Control Daemon...
Jun 28 09:37:15 webvirtmg.com systemd[1]: Started Process Monitoring and Control Daemon.

2.2 查看端口 备注:6080和8000已经启动

#查看端口  备注:6080和8000已经启动
[root@webvirtmg nginx]# netstat -lnpt
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name 
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 19287/nginx: master 
tcp 0 0 192.168.122.1:53 0.0.0.0:* LISTEN 7498/dnsmasq
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 1631/sshd
tcp 0 0 127.0.0.1:25 0.0.0.0:* LISTEN 1828/master
tcp 0 0 127.0.0.1:6010 0.0.0.0:* LISTEN 18977/sshd: root@pt 
tcp 0 0 127.0.0.1:6011 0.0.0.0:* LISTEN 18977/sshd: root@pt 
tcp 0 0 127.0.0.1:8000 0.0.0.0:* LISTEN 19374/python2
tcp 0 0 0.0.0.0:6080 0.0.0.0:* LISTEN 19373/python2
tcp6 0 0 :::22 :::* LISTEN 1631/sshd
tcp6 0 0 ::1:25 :::* LISTEN 1828/master
tcp6 0 0 ::1:6010 :::* LISTEN 18977/sshd: root@pt 
tcp6 0 0 ::1:6011 :::* LISTEN 18977/sshd: root@pt

2.3 访问地址:http://192.168.56.50/login/

账号信息:

username: admin

passwd:************

未分类

未分类

未分类

未分类

未分类

2.4 登录后会报错

解决措施:
1)在webvirtmgr服务器(服务端)上(这里kvm和WebVirtMgr部署在同一台机器上)创建nginx用户家目录(默认nginx服务安装时是没有nginx家目录的),生成nginx的公私钥
[root@test]# cd /home/
[root@test home]# mkdir nginx
[root@test home]# chown nginx.nginx nginx/
[root@test home]# chmod 700 nginx/ -R
[root@test home]# su - nginx -s /bin/bash
-bash-4.1$ ssh-keygen                             #期间输入yes后直接回车,回车
-bash-4.1$ touch ~/.ssh/config && echo -e "StrictHostKeyChecking=nonUserKnownHostsFile=/dev/null" >> ~/.ssh/config
-bash-4.1$ chmod 0600 ~/.ssh/config

#在webvirtmgr服务器(服务端)上(这里kvm和WebVirtMgr部署在同一台机器上),将nginx用户的ssh-key上传到kvm服务器上(这里kvm和WebVirtMgr部署在同一台机器上)

[root@openstack ops]# su - nginx -s /bin/bash
-bash-4.1$ ssh-copy-id [email protected]
Warning: Permanently added '192.168.0.50' (RSA) to the list of known hosts.
[email protected]'s password: #输入192.168.0.50即本机的root账号
Now try logging into the machine, with "ssh '[email protected]'", and check in:
.ssh/authorized_keys

#在kvm(客服端)服务器上(这里kvm和WebVirtMgr部署在同一台机器上)配置 libvirt ssh授权

[root@test]# vim /etc/polkit-1/localauthority/50-local.d/50-libvirt-remote-access.pkla
[Remote libvirt SSH access]
Identity=unix-user:root #注意这里采用的是root用户
Action=org.libvirt.unix.manage
ResultAny=yes
ResultInactive=yes
ResultActive=yes

[root@test]# chown -R root.root /etc/polkit-1/localauthority/50-local.d/50-libvirt-remote-access.pkla

#重启服务

systemctl restart nginx

systemctl restart libvirtd

#其它参考:

http://www.cnblogs.com/kevingrace/p/5737724.html

https://www.jianshu.com/p/160272d81ac3

三、WebVirtMgr的日常配置

参考:https://yq.aliyun.com/articles/46358

3.1 上传Centos6.8的镜像到这个目录中

[root@webvirtmg images]# cd /var/lib/libvirt/images

[root@webvirtmg images]# ll
total 6162064
-rw-------. 1 qemu qemu 21478375424 Jun 28 11:13 Centos6.8.img
-rw-r--r--. 1 qemu qemu  3916431360 Jun 28 11:01 CentOS-6.8-x86_64-bin-DVD1.iso

3.2 配置完成

未分类

kubernetes的dashboard登录方式

无密码登录

dashboard安装报错?下面介绍3种方式,无密码登录,token登录,dashboard客户端查看

未分类

# cat kubernetes-dashboard.yaml
# 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.


# Configuration to deploy release version of the Dashboard UI compatible with
# Kubernetes 1.8.
#
# Example usage: kubectl create -f <this_file>

# ------------------- 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"]
  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/v1beta2
metadata:
  labels:
    k8s-app: kubernetes-dashboard
  name: kubernetes-dashboard
  namespace: kube-system
spec:
  replicas: 1
  revisionHistoryLimit: 1
  selector:
    matchLabels:
      k8s-app: kubernetes-dashboard
  template:
    metadata:
      labels:
        k8s-app: kubernetes-dashboard
    spec:
      containers:
      - name: kubernetes-dashboard
        imagePullPolicy: IfNotPresent
        image: k8s.gcr.io/kubernetes-dashboard-amd64:v1.8.3
        ports:
        - containerPort: 9090
          protocol: TCP
        args:
          # 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:
          # Create on-disk volume to store exec logs
        - mountPath: /tmp
          name: tmp-volume
        livenessProbe:
          httpGet:
            path: /
            port: 9090
          initialDelaySeconds: 30
          timeoutSeconds: 30
      volumes:
      - 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:
  #type: NodePort
  ports:
  - port: 9090
    targetPort: 9090
    #nodePort: 30088
  selector:
    k8s-app: kubernetes-dashboard
# cat dashboard-admin.yaml
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
  name: kubernetes-dashboard
  labels:
    k8s-app: kubernetes-dashboard
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
- kind: ServiceAccount
  name: kubernetes-dashboard
  namespace: kube-system

kubectl创建完成后,加proxy

nohup kubectl proxy --address='0.0.0.0' --port=8888 --accept-hosts='^*$' &

浏览器访问http://master_IP:8888/ui

如果用NodePort模式,把上面type: NodePort 和 nodePort:30088 取消注释,访问30088端口即可

这个无密码登录有个问题:打开容器组的容器面板,右上角有”运行命令”,这里是不能执行的,另外菜单的”设置”不能使用,如果使用下面的token登录可以

token登录

# cat kubernetes-dashboard.yaml 
# 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.

# Configuration to deploy release version of the Dashboard UI compatible with
# Kubernetes 1.8.
#
# Example usage: kubectl create -f <this_file>

# ------------------- 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/v1beta2
metadata:
  labels:
    k8s-app: kubernetes-dashboard
  name: kubernetes-dashboard
  namespace: kube-system
spec:
  replicas: 1
  revisionHistoryLimit: 1
  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.8.3
        image: harbor.iris-technologies.com.cn:50505/k8s/kubernetes-dashboard-amd64:v1.8.3
        imagePullPolicy: IfNotPresent
        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:
#  type: NodePort
  ports:
    - port: 9090
      targetPort: 8443
  selector:
    k8s-app: kubernetes-dashboard

获取dashboard token

kubectl -n kube-system describe secret $(kubectl -n kube-system get secret | grep kubernetes-dashboard-token|awk '{print $1}')|grep token:|awk '{print $2}'

登录:

https://192.168.0.238:6443/api/v1/namespaces/kube-system/services/https:kubernetes-dashboard:/proxy/#!/login

{
  "kind": "Status",
  "apiVersion": "v1",
  "metadata": {

  },
  "status": "Failure",
  "message": "services "https:kubernetes-dashboard:" is forbidden: User "system:anonymous" cannot get services/proxy in the namespace "kube-system"",
  "reason": "Forbidden",
  "details": {
    "name": "https:kubernetes-dashboard:",
    "kind": "services"
  },
  "code": 403
}

证书问题,添加证书
使用client-certificate-data和client-key-data生成一个p12文件

生成client-certificate-data

grep 'client-certificate-data' ~/.kube/config | head -n 1 | awk '{print $2}' | base64 -d >> kubecfg.crt

生成client-key-data

grep 'client-key-data' ~/.kube/config | head -n 1 | awk '{print $2}' | base64 -d >> kubecfg.key

生成p12

openssl pkcs12 -export -clcerts -inkey kubecfg.key -in kubecfg.crt -out kubecfg.p12 -name "kubernetes-client"

kubecfg.p12就是生成的个人证书

下面是Chrome和Firefox浏览器导入证书

未分类

未分类

在Chrome或者Firefox添加 kubecfg.p12 个人证书,再次使用令牌访问,提示。。。等等一堆错误:

configmaps is forbidden: User "system:serviceaccount:kube-system:kubernetes-dashboard" cannot list configmaps in the namespace "default"

根据下面操作:

kubectl create serviceaccount dashboard -n default
kubectl create clusterrolebinding dashboard-admin -n default --clusterrole=cluster-admin  --serviceaccount=default:dashboard
kubectl get secret $(kubectl get serviceaccount dashboard -o jsonpath="{.secrets[0].name}") -o jsonpath="{.data.token}" | base64 --decode

拿这个token登录dashboard界面即可

客户端查看

https://kubernetic.com/ 下载客户端

比如Windows电脑,Win+R打开运行,输入cmd,执行命令mkdir .kube

把master节点 .kube/config 放到 C:Users用户名.kube 目录下,打开kubernetic即可

未分类

kubernetes资源配额详解

1. 资源配额(ResourceQuota)

ResourceQuota对象用来定义某个命名空间下所有资源的使用限额,其实包括:

  • 计算资源的配额
  • 存储资源的配额
  • 对象数量的配额

如果集群的总容量小于命名空间的配额总额,可能会产生资源竞争。这时会按照先到先得来处理。
资源竞争和配额的更新都不会影响已经创建好的资源。

1.1. 启动资源配额

Kubernetes 的众多发行版本默认开启了资源配额的支持。当在apiserver的–admission-control配置中添加ResourceQuota参数后,便启用了。 当一个命名空间中含有ResourceQuota对象时,资源配额将强制执行。

1.2. 计算资源配额

可以在给定的命名空间中限制可以请求的计算资源(compute resources)的总量。

未分类

1.3. 存储资源配额

可以在给定的命名空间中限制可以请求的存储资源(storage resources)的总量。

未分类

1.4. 对象数量的配额

未分类

例如:可以定义pod的限额来避免某用户消耗过多的Pod IPs。

1.5. 限额的作用域

未分类

1.6. request和limit

当分配计算资源时,每个容器可以为cpu或者内存指定一个请求值和一个限度值。可以配置限额值来限制它们中的任何一个值。
如果指定了requests.cpu 或者 requests.memory的限额值,那么就要求传入的每一个容器显式的指定这些资源的请求。如果指定了limits.cpu或者limits.memory,那么就要求传入的每一个容器显式的指定这些资源的限度。

1.7. 查看和设置配额

# 创建namespace
$ kubectl create namespace myspace

# 创建resourcequota
$ cat <<EOF > compute-resources.yaml
apiVersion: v1
kind: ResourceQuota
metadata:
  name: compute-resources
spec:
  hard:
    pods: "4"
    requests.cpu: "1"
    requests.memory: 1Gi
    limits.cpu: "2"
    limits.memory: 2Gi
EOF
$ kubectl create -f ./compute-resources.yaml --namespace=myspace

# 查询resourcequota
$ kubectl get quota --namespace=myspace
NAME                    AGE
compute-resources       30s

# 查询resourcequota的详细信息
$ kubectl describe quota compute-resources --namespace=myspace
Name:                  compute-resources
Namespace:             myspace
Resource               Used Hard
--------               ---- ----
limits.cpu             0    2
limits.memory          0    2Gi
pods                   0    4
requests.cpu           0    1
requests.memory        0    1Gi

1.8. 配额和集群容量

资源配额对象与集群容量无关,它们以绝对单位表示。即增加节点的资源并不会增加已经配置的namespace的资源。

2. Pod限额(LimitRange)

ResourceQuota对象是限制某个namespace下所有Pod(容器)的资源限额
LimitRange对象是限制某个namespace单个Pod(容器)的资源限额
LimitRange对象用来定义某个命名空间下某种资源对象的使用限额,其中资源对象包括:Pod、Container、PersistentVolumeClaim。

2.1. 为namespace配置CPU和内存的默认值

如果在一个拥有默认内存或CPU限额的命名空间中创建一个容器,并且这个容器未指定它自己的内存或CPU的limit, 它会被分配这个默认的内存或CPU的limit。既没有设置pod的limit和request才会分配默认的内存或CPU的request。

2.1.1. namespace的内存默认值

# 创建namespace
$ kubectl create namespace default-mem-example

# 创建LimitRange
$ cat memory-defaults.yaml
apiVersion: v1
kind: LimitRange
metadata:
  name: mem-limit-range
spec:
  limits:
  - default:
      memory: 512Mi
    defaultRequest:
      memory: 256Mi
    type: Container

$ kubectl create -f https://k8s.io/docs/tasks/administer-cluster/memory-defaults.yaml --namespace=default-mem-example

# 创建Pod,未指定内存的limit和request
$ cat memory-defaults-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: default-mem-demo
spec:
  containers:
  - name: default-mem-demo-ctr
    image: nginx

$ kubectl create -f https://k8s.io/docs/tasks/administer-cluster/memory-defaults-pod.yaml --namespace=default-mem-example

# 查看Pod
$ kubectl get pod default-mem-demo --output=yaml --namespace=default-mem-example
containers:
- image: nginx
  imagePullPolicy: Always
  name: default-mem-demo-ctr
  resources:
    limits:
      memory: 512Mi
    requests:
      memory: 256Mi

2.1.2. namespace的CPU默认值

# 创建namespace
$ kubectl create namespace default-cpu-example

# 创建LimitRange
$ cat cpu-defaults.yaml 
apiVersion: v1
kind: LimitRange
metadata:
  name: cpu-limit-range
spec:
  limits:
  - default:
      cpu: 1
    defaultRequest:
      cpu: 0.5
    type: Container

$ kubectl create -f https://k8s.io/docs/tasks/administer-cluster/cpu-defaults.yaml --namespace=default-cpu-example    

# 创建Pod,未指定CPU的limit和request
$ cat cpu-defaults-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: default-cpu-demo
spec:
  containers:
  - name: default-cpu-demo-ctr
    image: nginx

$ kubectl create -f https://k8s.io/docs/tasks/administer-cluster/cpu-defaults-pod.yaml --namespace=default-cpu-example

# 查看Pod
$ kubectl get pod default-cpu-demo --output=yaml --namespace=default-cpu-example
containers:
- image: nginx
  imagePullPolicy: Always
  name: default-cpu-demo-ctr
  resources:
    limits:
      cpu: "1"
    requests:
      cpu: 500m

2.1.3 说明

  1. 如果没有指定pod的request和limit,则创建的pod会使用LimitRange对象定义的默认值(request和limit)
  2. 如果指定pod的limit但未指定request,则创建的pod的request值会取limit的值,而不会取LimitRange对象定义的request默认值。
  3. 如果指定pod的request但未指定limit,则创建的pod的limit值会取LimitRange对象定义的limit默认值。

默认Limit和request的动机

如果命名空间具有资源配额(ResourceQuota), 它为内存限额(CPU限额)设置默认值是有意义的。 以下是资源配额对命名空间施加的两个限制:

  • 在命名空间运行的每一个容器必须有它自己的内存限额(CPU限额)。
  • 在命名空间中所有的容器使用的内存总量(CPU总量)不能超出指定的限额。

如果一个容器没有指定它自己的内存限额(CPU限额),它将被赋予默认的限额值,然后它才可以在被配额限制的命名空间中运行。

2.2. 为namespace配置CPU和内存的最大最小值

2.2.1. 内存的最大最小值

创建LimitRange

# 创建namespace
$ kubectl create namespace constraints-mem-example

# 创建LimitRange
$ cat memory-constraints.yaml
apiVersion: v1
kind: LimitRange
metadata:
  name: mem-min-max-demo-lr
spec:
  limits:
  - max:
      memory: 1Gi
    min:
      memory: 500Mi
    type: Container

$ kubectl create -f https://k8s.io/docs/tasks/administer-cluster/memory-constraints.yaml --namespace=constraints-mem-example

# 查看LimitRange
$ kubectl get limitrange cpu-min-max-demo --namespace=constraints-mem-example --output=yaml
...
  limits:
  - default:
      memory: 1Gi
    defaultRequest:
      memory: 1Gi
    max:
      memory: 1Gi
    min:
      memory: 500Mi
    type: Container
...
# LimitRange设置了最大最小值,但没有设置默认值,也会被自动设置默认值。

创建符合要求的Pod

# 创建符合要求的Pod
$ cat memory-constraints-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: constraints-mem-demo
spec:
  containers:
  - name: constraints-mem-demo-ctr
    image: nginx
    resources:
      limits:
        memory: "800Mi"
      requests:
        memory: "600Mi"

$ kubectl create -f https://k8s.io/docs/tasks/administer-cluster/memory-constraints-pod.yaml --namespace=constraints-mem-example

# 查看Pod
$ kubectl get pod constraints-mem-demo --output=yaml --namespace=constraints-mem-example
...
resources:
  limits:
     memory: 800Mi
  requests:
    memory: 600Mi
...

创建超过最大内存limit的pod

$ cat memory-constraints-pod-2.yaml
apiVersion: v1
kind: Pod
metadata:
  name: constraints-mem-demo-2
spec:
  containers:
  - name: constraints-mem-demo-2-ctr
    image: nginx
    resources:
      limits:
        memory: "1.5Gi"  # 超过最大值 1Gi
      requests:
        memory: "800Mi"

$ kubectl create -f https://k8s.io/docs/tasks/administer-cluster/memory-constraints-pod-2.yaml --namespace=constraints-mem-example

# Pod创建失败,因为容器指定的limit过大
Error from server (Forbidden): error when creating "docs/tasks/administer-cluster/memory-constraints-pod-2.yaml":
pods "constraints-mem-demo-2" is forbidden: maximum memory usage per Container is 1Gi, but limit is 1536Mi.

创建小于最小内存request的Pod

$ cat memory-constraints-pod-3.yaml
apiVersion: v1
kind: Pod
metadata:
  name: constraints-mem-demo-3
spec:
  containers:
  - name: constraints-mem-demo-3-ctr
    image: nginx
    resources:
      limits:
        memory: "800Mi"
      requests:
        memory: "100Mi"   # 小于最小值500Mi

$ kubectl create -f https://k8s.io/docs/tasks/administer-cluster/memory-constraints-pod-3.yaml --namespace=constraints-mem-example         

# Pod创建失败,因为容器指定的内存request过小
Error from server (Forbidden): error when creating "docs/tasks/administer-cluster/memory-constraints-pod-3.yaml":
pods "constraints-mem-demo-3" is forbidden: minimum memory usage per Container is 500Mi, but request is 100Mi.

创建没有指定任何内存limit和request的pod

$ cat memory-constraints-pod-4.yaml
apiVersion: v1
kind: Pod
metadata:
  name: constraints-mem-demo-4
spec:
  containers:
  - name: constraints-mem-demo-4-ctr
    image: nginx

$ kubectl create -f https://k8s.io/docs/tasks/administer-cluster/memory-constraints-pod-4.yaml --namespace=constraints-mem-example

# 查看Pod
$ kubectl get pod constraints-mem-demo-4 --namespace=constraints-mem-example --output=yaml
...
resources:
  limits:
    memory: 1Gi
  requests:
    memory: 1Gi
...

容器没有指定自己的 CPU 请求和限制,所以它将从 LimitRange 获取默认的 CPU 请求和限制值。

2.2.2. CPU的最大最小值

创建LimitRange

# 创建namespace
$ kubectl create namespace constraints-cpu-example

# 创建LimitRange
$ cat cpu-constraints.yaml
apiVersion: v1
kind: LimitRange
metadata:
  name: cpu-min-max-demo-lr
spec:
  limits:
  - max:
      cpu: "800m"
    min:
      cpu: "200m"
    type: Container

$ kubectl create -f https://k8s.io/docs/tasks/administer-cluster/cpu-constraints.yaml --namespace=constraints-cpu-example

# 查看LimitRange
$ kubectl get limitrange cpu-min-max-demo-lr --output=yaml --namespace=constraints-cpu-example
...
limits:
- default:
    cpu: 800m
  defaultRequest:
    cpu: 800m
  max:
    cpu: 800m
  min:
    cpu: 200m
  type: Container
...

创建符合要求的Pod

$ cat cpu-constraints-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: constraints-cpu-demo
spec:
  containers:
  - name: constraints-cpu-demo-ctr
    image: nginx
    resources:
      limits:
        cpu: "800m"
      requests:
        cpu: "500m"

$ kubectl create -f https://k8s.io/docs/tasks/administer-cluster/cpu-constraints-pod.yaml --namespace=constraints-cpu-example

# 查看Pod
$ kubectl get pod constraints-cpu-demo --output=yaml --namespace=constraints-cpu-example
...
resources:
  limits:
    cpu: 800m
  requests:
    cpu: 500m
...

创建超过最大CPU limit的Pod

$ cat cpu-constraints-pod-2.yaml
apiVersion: v1
kind: Pod
metadata:
  name: constraints-cpu-demo-2
spec:
  containers:
  - name: constraints-cpu-demo-2-ctr
    image: nginx
    resources:
      limits:
        cpu: "1.5"
      requests:
        cpu: "500m"

$ kubectl create -f https://k8s.io/docs/tasks/administer-cluster/cpu-constraints-pod-2.yaml --namespace=constraints-cpu-example

# Pod创建失败,因为容器指定的CPU limit过大
Error from server (Forbidden): error when creating "docs/tasks/administer-cluster/cpu-constraints-pod-2.yaml":
pods "constraints-cpu-demo-2" is forbidden: maximum cpu usage per Container is 800m, but limit is 1500m.

创建小于最小CPU request的Pod

$ cat cpu-constraints-pod-3.yaml
apiVersion: v1
kind: Pod
metadata:
  name: constraints-cpu-demo-4
spec:
  containers:
  - name: constraints-cpu-demo-4-ctr
    image: nginx
    resources:
      limits:
        cpu: "800m"
      requests:
        cpu: "100m"

$ kubectl create -f https://k8s.io/docs/tasks/administer-cluster/cpu-constraints-pod-3.yaml --namespace=constraints-cpu-example

# Pod创建失败,因为容器指定的CPU request过小
Error from server (Forbidden): error when creating "docs/tasks/administer-cluster/cpu-constraints-pod-3.yaml":
pods "constraints-cpu-demo-4" is forbidden: minimum cpu usage per Container is 200m, but request is 100m.

创建没有指定任何CPU limit和request的pod

$ cat cpu-constraints-pod-4.yaml
apiVersion: v1
kind: Pod
metadata:
  name: constraints-cpu-demo-4
spec:
  containers:
  - name: constraints-cpu-demo-4-ctr
    image: vish/stress

$ kubectl create -f https://k8s.io/docs/tasks/administer-cluster/cpu-constraints-pod-4.yaml --namespace=constraints-cpu-example    

# 查看Pod
kubectl get pod constraints-cpu-demo-4 --namespace=constraints-cpu-example --output=yaml
...
resources:
  limits:
    cpu: 800m
  requests:
    cpu: 800m
...

容器没有指定自己的 CPU 请求和限制,所以它将从 LimitRange 获取默认的 CPU 请求和限制值。

2.2.3. 说明

LimitRange 在 namespace 中施加的最小和最大内存(CPU)限制只有在创建和更新 Pod 时才会被应用。改变 LimitRange 不会对之前创建的 Pod 造成影响。

Kubernetes 都会执行下列步骤:

  • 如果容器没有指定自己的内存(CPU)请求(request)和限制(limit),系统将会为其分配默认值。
  • 验证容器的内存(CPU)请求大于等于最小值。
  • 验证容器的内存(CPU)限制小于等于最大值。

3. Resource Quality of Service

3.1. 资源QoS简介

request值表示容器保证可被分配到资源。limit表示容器可允许使用的最大资源。Pod级别的request和limit是其所有容器的request和limit之和。

3.2. Requests and Limits

Pod可以指定request和limit资源。其中0 <= request <=Node Allocatable & request <= limit <= Infinity。调度是基于request而不是limit,即如果Pod被成功调度,那么可以保证Pod分配到指定的 request的资源。Pod使用的资源能否超过指定的limit值取决于该资源是否可被压缩。

3.2.1. 可压缩的资源

  • 目前只支持CPU
  • pod可以保证获得它们请求的CPU数量,它们可能会也可能不会获得额外的CPU时间(取决于正在运行的其他作业)。因为目前CPU隔离是在容器级别而不是pod级别。

3.2.2. 不可压缩的资源

  • 目前只支持内存
  • pod将获得它们请求的内存数量,如果超过了它们的内存请求,它们可能会被杀死(如果其他一些pod需要内存),但如果pod消耗的内存小于请求的内存,那么它们将不会被杀死(除非在系统任务或守护进程需要更多内存的情况下)。

3.3. QoS 级别

在机器资源超卖的情况下(limit的总量大于机器的资源容量),即CPU或内存耗尽,将不得不杀死部分不重要的容器。因此对容器分成了3个QoS的级别:Guaranteed,Burstable, Best-Effort,三个级别的优先级依次递减。

当CPU资源无法满足,pod不会被杀死可能被短暂控制。

内存是不可压缩的资源,当内存耗尽的情况下,会依次杀死优先级低的容器。Guaranteed的级别最高,不会被杀死,除非容器使用量超过limit限值或者资源耗尽,已经没有更低级别的容器可驱逐。

3.3.1. Guaranteed

所有的容器的limit值和request值被配置且两者相等(如果只配置limit没有request,则request取值于limit)。

例如:

# 示例1
containers:
  name: foo
    resources:
      limits:
        cpu: 10m
        memory: 1Gi
  name: bar
    resources:
      limits:
        cpu: 100m
        memory: 100Mi
# 示例2
containers:
  name: foo
    resources:
      limits:
        cpu: 10m
        memory: 1Gi
      requests:
        cpu: 10m
        memory: 1Gi

  name: bar
    resources:
      limits:
        cpu: 100m
        memory: 100Mi
      requests:
        cpu: 100m
        memory: 100Mi

3.3.2. Burstable

如果一个或多个容器的limit和request值被配置且两者不相等。

例如:

# 示例1
containers:
  name: foo
    resources:
      limits:
        cpu: 10m
        memory: 1Gi
      requests:
        cpu: 10m
        memory: 1Gi

  name: bar

# 示例2
containers:
  name: foo
    resources:
      limits:
        memory: 1Gi

  name: bar
    resources:
      limits:
        cpu: 100m

# 示例3
containers:
  name: foo
    resources:
      requests:
        cpu: 10m
        memory: 1Gi

  name: bar

3.3.3. Best-Effort

所有的容器的limit和request值都没有配置。

例如:

containers:
  name: foo
    resources:
  name: bar
    resources:

k8s的初步部署zipkin和elk

kubernates的部署

一、界面部署

1、创建

点击右上角创建按钮

未分类

2、设置创建的参数

选择创建的方式可以为输入yaml配置,上传yaml文件和界面配置。

①界面创建

未分类

应用名称

这个是部署pod的名称
- 容器镜像
输入要部署的容器的镜像地址
- 容器组个数
这个参数是k8s要保证运行的pod数量,如果指定3,它会创建3个Pod,并且持续监控它们。如果某个Pod不响应,那么Replication Controller会替换它,保持总数为3。如果之前不响应的Pod恢复了,现在就有4个Pod了,那么Replication Controller会将其中一个终止保持总数为3。
- 服务
选择内部或者外部,内部服务将使得集群外部请求不能访问到pod,外部则会暴露nodePort使得外部机器可访问,服务中的端口类型共有3种:

1)nodePort
 外部机器可访问的端口。
比如一个Web应用需要被其他用户访问,那么需要配置type=NodePort,而且配置nodePort=30001,那么其他机器就可以通过浏览器访问scheme://node:30001访问到该服务,例如http://node:30001。
 例如MySQL数据库可能不需要被外界访问,只需被内部服务访问,那么不必设置NodePort
2)targetPort
 容器的端口(最根本的端口入口),与制作容器时暴露的端口一致(DockerFile中EXPOSE),例如docker.io官方的nginx暴露的是80端口。
3)port
 kubernetes中的服务之间访问的端口,尽管mysql容器暴露了3306端口(参考https://github.com/docker-library/mysql/的DockerFile),但是集群内其他容器需要通过33306端口访问该服务,外部机器不能访问mysql服务,因为他没有配置NodePort类型

- 保密字典
当要拉取私有服务器的镜像的时候需要用户名密码,在k8s中这个是通过配置保密字典secret来实现的。
K8s 中Secret是一个包含少量敏感信息如密码,令牌,或秘钥的对象。这些信息可能被保存在 pod 定义或者 docker 镜像中,把这些信息保存在 Secret对象中,可以在这些信息被使用时加以控制,并可以降低信息泄露的风险。
创建保密字典的方式有很多种,我是通过命令行的方式配置的:

kubectl create secret docker-registry regsecret --docker-server=repo.gildata.com --docker-username=chenyang --docker-password=xxxx [email protected]

这行命令是创建了一个名为regsecret的保密字典,--docker-server表示为仓库地址,--docker-username是用户账户,--docker-password为账户密码,--docker-email用户邮箱。

这个配置完之后,在k8s的界面上就可以看到我们配置的保密字典:

未分类

在界面上选择的时候可以点击下面的高级按钮选择相应的保密字典:

未分类

选择刚刚创建的保密字典:

未分类

这样私有仓库的镜像就可以拉取了。

②yaml配置
1)zipkin的Deployment配置:

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: zipkin
spec:
  replicas: 1
  template:
    metadata:
      labels:
        k8s-app: zipkin
    spec:
      containers:
      - name: zipkin
        image: openzipkin/zipkin
        ports:
          - containerPort: 9411
            hostPort: 9411

我在这里直接用Deployment来管理pod,Deployment为Pod和ReplicaSet提供了一个声明式定义(declarative)方法,用来替代以前的ReplicationController来方便的管理应用。典型的应用场景包括:

  • 定义Deployment来创建Pod和ReplicaSet
  • 滚动升级和回滚应用
  • 扩容和缩容
  • 暂停和继续Deployment

把这段配置文件提交在k8s管理界面的yaml文件框里面,或者直接是提交本地yaml文件,这两种方法大同小异。

附:k8s的pod的配置所有选项介绍:

 # yaml格式的pod定义文件完整内容:
 apiVersion: v1          #必选,版本号,例如v1
 kind: Pod             #必选,Pod
 metadata:             #必选,元数据
   name: string          #必选,Pod名称
   namespace: string       #必选,Pod所属的命名空间
   labels:             #自定义标签
     - name: string       #自定义标签名字
   annotations:          #自定义注释列表
     - name: string
 spec:                #必选,Pod中容器的详细定义
   containers:           #必选,Pod中容器列表
   - name: string        #必选,容器名称
     image: string       #必选,容器的镜像名称
     imagePullPolicy: [Always | Never | IfNotPresent]  #获取镜像的策略 Alawys表示下载镜像 IfnotPresent表示优先使用本地镜像,否则下载镜像,Nerver表示仅使用本地镜像
     command: [string]       #容器的启动命令列表,如不指定,使用打包时使用的启动命令
     args: [string]         #容器的启动命令参数列表
     workingDir: string      #容器的工作目录
     volumeMounts:         #挂载到容器内部的存储卷配置
     - name: string         #引用pod定义的共享存储卷的名称,需用volumes[]部分定义的的卷名
       mountPath: string     #存储卷在容器内mount的绝对路径,应少于512字符
       readOnly: boolean     #是否为只读模式
     ports:              #需要暴露的端口库号列表
     - name: string         #端口号名称
       containerPort: int    #容器需要监听的端口号
       hostPort: int        #容器所在主机需要监听的端口号,默认与Container相同
       protocol: string      #端口协议,支持TCP和UDP,默认TCP
     env:              #容器运行前需设置的环境变量列表
     - name: string        #环境变量名称
       value: string       #环境变量的值
     resources:          #资源限制和请求的设置
       limits:           #资源限制的设置
         cpu: string       #Cpu的限制,单位为core数,将用于docker run --cpu-shares参数
         memory: string      #内存限制,单位可以为Mib/Gib,将用于docker run --memory参数
       requests:         #资源请求的设置
         cpu: string       #Cpu请求,容器启动的初始可用数量
         memory: string      #内存清楚,容器启动的初始可用数量
     livenessProbe:        #对Pod内个容器健康检查的设置,当探测无响应几次后将自动重启该容器,检查方法有exec、httpGet和tcpSocket,对一个容器只需设置其中一种方法即可
       exec:             #对Pod容器内检查方式设置为exec方式
         command: [string]   #exec方式需要制定的命令或脚本
       httpGet:            #对Pod内个容器健康检查方法设置为HttpGet,需要制定Path、port
         path: string
         port: number
         host: string
         scheme: string
         HttpHeaders:
         - name: string
           value: string
       tcpSocket:            #对Pod内个容器健康检查方式设置为tcpSocket方式
          port: number
        initialDelaySeconds: 0   #容器启动完成后首次探测的时间,单位为秒
        timeoutSeconds: 0      #对容器健康检查探测等待响应的超时时间,单位秒,默认1秒
        periodSeconds: 0       #对容器监控检查的定期探测时间设置,单位秒,默认10秒一次
        successThreshold: 0
        failureThreshold: 0
        securityContext:
          privileged: false
     restartPolicy: [Always | Never | OnFailure] #Pod的重启策略,Always表示一旦不管以何种方式终止运行,kubelet都将重启,OnFailure表示只有Pod以非0退出码退出才重启,Nerver表示不再重启该Pod
     nodeSelector: obeject     #设置NodeSelector表示将该Pod调度到包含这个label的node上,以key:value的格式指定
     imagePullSecrets:         #Pull镜像时使用的secret名称,以key:secretkey格式指定
     - name: string
     hostNetwork: false        #是否使用主机网络模式,默认为false,如果设置为true,表示使用宿主机网络
     volumes:              #在该pod上定义共享存储卷列表
     - name: string          #共享存储卷名称 (volumes类型有很多种)
       emptyDir: {}          #类型为emtyDir的存储卷,与Pod同生命周期的一个临时目录。为空值
       hostPath: string        #类型为hostPath的存储卷,表示挂载Pod所在宿主机的目录
         path: string        #Pod所在宿主机的目录,将被用于同期中mount的目录
       secret:             #类型为secret的存储卷,挂载集群与定义的secre对象到容器内部
         scretname: string  
         items:     
         - key: string
           path: string
       configMap:          #类型为configMap的存储卷,挂载预定义的configMap对象到容器内部
         name: string
         items:
         - key: string
           path: string    

2)service配置

在pod成功部署之后,就需要为这个pod配置service。

Kubernete Service 是一个定义了一组Pod的策略的抽象,我们也有时候叫做宏观服务。这些被服务标记的Pod都是(一般)通过label Selector决定的。

举个例子,我们假设后台是一个图形处理的后台,并且由3个副本。这些副本是可以相互替代的,并且前台并不需要关心使用的哪一个后台Pod,当这个承载前台请求的pod发生变化时,前台并不需要直到这些变化,或者追踪后台的这些副本,这些都是通过服务来定位追踪的。

简单地说,服务就是请求到pod的一座桥梁,请求并不需要关心自己会请求到哪个pod,这些对于用户和请求来说都是不可见的,用户也不会感觉到这是一个集群,一个请求过来,service会自动分配到哪个pod上。

service的yaml配置文件:

apiVersion: v1
kind: Service
metadata:
 name: zipkin
 labels:
   k8s-app: zipkin
spec:
 type: LoadBalancer
 ports:
 - port: 9411
   targetPort: 9411
   nodePort: 30002
 selector:
  k8s-app: zipkin

2)ConfigMap配置

ConfigMap用于保存配置数据的键值对,可以用来保存单个属性,也可以用来保存配置文件。ConfigMap跟secret很类似,但它可以更方便地处理不包含敏感信息的字符串。

简单的说,相对于docker-compose的配置文件在文件目录下面,k8s的配置文件放到了自己的配置字典里面。

下面是ConfigMap的配置

apiVersion: v1
kind: ConfigMap
metadata:
  name: logstash-config
  namespace: default
data:
  logstash.yml: |
    http.host: "0.0.0.0"
    path.config: /usr/share/logstash/pipeline
  logstash.conf: |
    input {
        tcp {
            port => 5000
            codec => json_lines {
                charset => "UTF-8"
            }
        }
    }
    output {
        elasticsearch {
            hosts => "elasticsearch:9200"
        }
    }

logstash-config表示配置字典的名称。

data里面每一个key-value键值对都会生成一个文件,key为文件名,value为内容。

同样的,在点击上传之后,会在配置字典菜单中生成相应的配置字典。如图所示:

未分类

这样就可以在deployment中引用这个配置字典:

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: logstash
spec:
  replicas: 1
  template:
    metadata:
      labels:
        k8s-app: logstash
    spec:
      containers:
      - name: logstash
        image: docker.elastic.co/logstash/logstash:6.2.3
        ports:
          - containerPort: 5000
            hostPort: 5000
        env:
          - name: ES_JAVA_OPTS
            value: "-Xmx256m -Xms256m"
        volumeMounts:
          - name: logstash-volume1   #volumes中配的volume名字
            mountPath: /usr/share/elasticsearch/config   #在容器内部的配置文件存放地址
          - name: logstash-volume2   #volumes中配的volume名字
            mountPath: /usr/share/logstash/pipeline     #在容器内部的配置文件存放地址
        imagePullSecrets:
          - name: regsecret   #  保密字典
      volumes:
      - name: logstash-volume1  #这个是现在自定义的volume的名字,方便在volumeMounts中引用
        configMap:
          name: logstash-config   #这个就是之前配的配置字典
          items:
          - key: logstash.yml  #配置字典里面对应的key
            path: config
      - name: logstash-volume2   #这个是现在自定义的volume的名字,方便在volumeMounts中引用
        configMap:
          name: logstash-config   #这个就是之前配的配置字典
          items:
          - key: logstash.conf   #配置字典里面对应的ke
            path: pipeline

3、部署

配置好之后点击部署,然后查看概况,状态图都为绿色,容器组的状态为running,这样就部署成功了,如图:

未分类

可以进行访问Zipkin,输入http://10.106.0.51:32552,这里的32552即为nodePort:

未分类

当在后期需要调整node的数量的时候,可以在deployment中调整数量,如图所示:

未分类

第二步:

未分类

通过 UI 管理 docker

Docker 正在被用在越来越多的场景中,对于不太习惯命令行工具的朋友来说,docker cli 用起来可能会比较吃力。本文笔者将介绍一个功能强大的 docker web 客户端:portainer(岸吊,参考下面插图)。按照 portainer 官方的说法,它可以让我们通过 UI 轻松的管理 docker 主机和 docker swarm 集群。说明:本文的演示环境为 ubuntu 16.04。

未分类

安装 portainer

就像我们通过容器来运行应用一样,运行一个容器就行了,非常简单:

$ docker run -d -p 9000:9000 -v /var/run/docker.sock:/var/run/docker.sock portainer/portainer

然后在浏览器中通过 9000 端口访问 protainer 站点就可以了!

配置管理员信息

第一次访问 portainer 时需要设置管理员的账号信息,我们把管理员账号命名为 admin 并设置密码:

未分类

选择 portainer 管理的对象

简单起见我们先配置 portainer 管理本机的 docker,选择下图中的 “Local” 并点击 “connect” 按钮:

未分类

了解 portainer 的主要功能

简单的安装和配置之后我们就进入了 portainer 的 dashboard 界面:

未分类

Portainer 的界面设计非常简洁,主菜单几乎就是 docker 主要功能的一一对应。接下来,我们将通过 App Templates 菜单来创建容器一个容器,并通过该容器来介绍一些 portainer 中比较常用的功能。

选择左侧的 App Templates 菜单,然后选择创建一个运行在 nginx 容器中的应用:

未分类

在容器属性的配置界面中,指定容器的名称为 webdemo,然后在高级选项中设置把宿主机的 80 端口映射到容器中的 80 端口:

未分类

然后点击 “Deploy the container” 按钮开始创建容器。容器创建完成后会显示在 Containers 界面中:

未分类

在这个管理界面上,我们可以选取一个或多个容器进行操作,比如 start、stop、kill、restart、pause、resume 和 remove,当然还可以通过 “Add container” 按钮添加新的容器。下面我们点击红框中的容器名称 “webdemo” 进入到容器详情界面:

未分类

这个界面中显示了单个容器的详情以及可以对这个容器执行的操作。最上方是针对容器的操作,接下来是容器的状态,然后是 “Access control”、”Create image”、”Container details”、”Volumes” 和 “Connected networks”,为了显示方便,笔者把下面的类别都折叠了。
在容器状态区域,需要关注下红框中的 Status、Logs、Console 和 Inspect,这些都是我们平时比较关心的内容。下面点击它们分别查看对应的视图。
Container statistics 视图实时的显示容器占用的资源信息,需要注意的是相关的数据没有被持久化,显示的内容总是每次打开这个视图后的数据:

未分类

容器日志视图显示容器中的日志输出:

未分类

通过控制台视图我们可以在容器中执行命令:

未分类

Inspect 视图则为我们展示容器的详细信息:

未分类

管理手动创建的容器

Portainer 并不是只能管理自己创建的容器,我们通过命令行创建的容器也会被 portainer 发现并管理。比如我们到宿主机的控制台上创建一个名为 mycon 的容器:

$ docker run --rm -id --name mycon ubuntu

然后在 portainer 的容器列表界面中刷新一下:

未分类

mycon 容器已经出现在容器列表中了。

其它操作

通过菜单中的 Images、Networks 和 Volumes 项可以分别管理容器镜像、network 和数据卷:

未分类

这些差别多就是平时使用比较频繁的功能了!

除了管理单机模式的 docker,portainer 还可以管理 docker swarm 集群。具体的用法并不复杂,这里就不再赘述了,有兴趣的朋友可以参考这里https://media-glass.es/portainer-the-ui-for-docker-d067f6335f23

CDN实现varnish缓存

实验环境:

server5:varnish机(ip:172.25.254.5)
server2:real server机(ip:172.25.254.2)
server3:real server机(ip:172.25.254.3)

server5配置

一、安装varnish相关软件

[root@server5 ~]# yum install -y varnish-libs-3.0.5-1.el6.x86_64.rpm 
[root@server5 ~]# yum install -y varnish-3.0.5-1.el6.x86_64.rpm 

二、修改varnish配置文件

vim /etc/sysconfig/varnish 

修改varnish的端口

未分类

vim /etc/varnish/default.vcl

backend default {
  .host = "172.25.20.2";
  .port = "80";
}

三、修改内核显示,保证流量大时,服务正常运行

[root@server5 ~]# vim /etc/security/limits.conf 

未分类

四、varnish主机配置

修改varnish配置文件

backend web1 {   ##访问web1时,指向172.25.20.2
  .host = "172.25.254.2";
  .port = "80";
}

backend web2 {   ##访问web1时,指向172.25.20.3
  .host = "172.25.254.3";
  .port = "80";
}

sub vcl_recv {   ##www.westos.org和westos.org指向同一地址,节省varnish空间
         ##实质上,域名不同,varnish缓存地址不同
if (req.http.host ~ "^(www.)?westos.org") {
set req.http.host = "www.westos.org";
set req.backend = web1;
} elsif (req.http.host ~ "^bbs.westos.org") {
set req.backend = web2;
} else {error 404 "westos cache";
}
}

sub vcl_deliver {   ##第一次访问时MISS,后续访问HIT,varnish默认清楚时间120s
if (obj.hits > 0) {
set resp.http.X-Cache = "HIT from westos cache";
}
else {
set resp.http.X-Cache = "MISS from westos cache";
}
return (deliver);
}

测试:
在物理机做域名解析:

172.25.254.5    www.westos.org  bbs.westos.org  westos.org

未分类

未分类

五、varnish负载均衡设置

server5

1、修改配置文件

backend web1 {
  .host = "172.25.254.2";
  .port = "80";
}

backend web2 {
  .host = "172.25.254.3";
  .port = "80";
}
director lb round-robin {     ##轮询操作
{ .backend = web1; }
{ .backend = web2; }
}
sub vcl_recv {
if (req.http.host ~ "^(www.)?westos.org") {
set req.http.host = "www.westos.org";
set req.backend = lb;       ##调用轮询
return (pass);
} elsif (req.http.host ~ "^bbs.westos.org") {
set req.backend = web2;      ##直接访问,不参与轮询
} else {error 404 "westos cache";   
}
}

server3开启httpd虚拟主机设置

/etc/httpd/cong/httpd.conf

未分类

重新启动server3的httpd服务

测试:

物理机访问:

未分类

六、varnish推送管理

目的:当web有更新时,varish的cache实现同步更新
安装httpd,php,unzip
修改httpd端口8080
默认发布目录/var/www/html下解压 bansys.zip
修改varnish配置文件 /etc/varnish/default.vcl
acl westos {
"127.0.0.1";
"172.25.254.0"/24;   指定 254 网段的可以推送
}

backend web1 {
.host = "172.25.254.2";   指定服务器
.port = "80";
}

backend web2 {
.host = "172.25.254.3";   指定服务器
.port = "80";
}
director lb round-robin {       轮巡
{       .backend = web1;        }
{       .backend = web2;        }
}
sub vcl_recv {

if (req.request == "BAN") {     如果输入错误  则 报错
if (!client.ip ~ westos) {
error 405 "Not allowed.";
}
ban("req.url ~ " + req.url);
error 200 "ban added";
}

if (req.http.host ~ "^(www.)?westos.org") {
set req.http.host = "www.westos.org";
set req.backend = lb;    引用 lb 函数  参加轮训
} elsif (req.http.host ~ "^bbs.westos.org") {
set req.backend = web2;
} else {error 404 "westos cache";
}
}
sub vcl_deliver {
if (obj.hits > 0) {
set resp.http.X-Cache = "HIT from westos cache";
}                 命中缓存
else {
set resp.http.X-Cache = "MISS from westos cache";
}      未命中缓存
return (deliver);
}

访问 172.25.254.5:8080 即可进行推送
varnishadm ban.url /index.html
清除 index.html 页面缓存
varnishadm ban.url /admin/$
清除 admin 目录缓存

zabbix主机自动发现和监控

在主机较多的时候,配置主机自动发现并加入监控可以代替手动的添加主机,减轻工作量,自动发现由服务端主动发起,Zabbix Server开启发现进程,定时扫描局域网中IP服务器、设备。可以根据需要,在对主机安装系统的时候就安装配置并启动zabbix-agent服务,这样的话只要主机系统安装完成,便会自动加入监控

1、在客户机上安装zabbix-agent并配置

2、zabbix-server自动发现和监控

2.1 创建自动发现规则

依次点击:配置——>自动发现,选择Local network或创建自动发现规则

填写server端所扫描的主机ip地址所在范围,例如数据库服务器所用的范围为192.168.1.51-59

修改适合的时间延迟(延迟太短如果主机太多会导致有的主机扫描不到)

检查项不必修改,默认即可

设备唯一性准则选择ip地址

最后勾选已启用、点击更新

未分类

2.2 创建发现动作

依次点击:配置——>动作,选择事件源为自动发现,点击Auto discovery. Linux servers.——>操作

添加新的动作操作,当主机自动发现并添加监控的时候自动给管理员发送邮件提示,选择要发送邮件的用户,并去掉勾选消息内容,这样收到的邮件才有内容,点击更新后启用该动作

未分类

未分类

2.3 配置邮件通知

配置发件人邮箱,依次点击:管理——>报警媒介类型——>Email,填写邮箱服务器的地址以及邮件用户及客户端授权码,并勾选已启用

未分类

配置收件人邮箱,依次点击:管理——>用户,点击要收到邮件的用户,点击报警媒介——>添加,填写邮件地址,启用时间和报警类型根据需要选择,点击添加——>更新

未分类

3、等待自动发现并添加监控

点击监测中——>自动发现,等待发现主机

未分类

发现主机成功后会自动在主机列表中出现发现的主机并添加了监控

未分类

同时,邮箱也收到了邮件

未分类

zabbix通过企业微信发送监控告警

很好的监控线上业务,需要有及时提醒的告警功能,以便及时处理问题,在运维的监控工作中,告警大概有这么几种,一是邮件,较为传统,往往会被忽略,因为现在垃圾邮件实在太多,二是短信,这个比较有及时性,但有时也会有垃圾短信,一般情况下,人们的习惯是收到短信,都会看一眼!所以相对邮件关注率要高一些,第三种是通过钉钉或者企业微信这种做监控告警,目前来说,还是比较及时和重要的,因为办公信息,大家都很重视,因为大家都是自驱型人才,下面的python脚本,用于调用企业微信自定义应用来发送告警消息(或者这样说不准备,暂且这么说吧):

#!/usr/bin/python
# --*-- coding:utf8 --*--
# Author: Jack.Z


import json
import sys
import simplejson
import requests


def get_token(cid, secret):
    gettoken_url = 'https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=' + cid + '&corpsecret=' + secret
    try:
        req = requests.get(gettoken_url)
    except requests.HTTPError as e:
        print e.errno
        sys.exit()

    return json.loads(req.text)['access_token']


def send_data(token, account, subject_name, context):
    send_url = 'https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=' + token
    send_values = {
        "touser": account,          # 企业号中的用户帐号,在zabbix用户Media中配置,如果配置不正常,将按部门发送。
        "toparty": "2",             # 企业号中的部门id。
        "msgtype": "text",          # 消息类型。
        "agentid": "1000002",       # 企业号中的应用id。
        "text": {
            "content": subject_name + 'n' + context
        },
        "safe": "0"
    }

    data = simplejson.dumps(send_values, ensure_ascii=False).encode('utf-8')
    try:
        req = requests.post(send_url, data)
    except requests.HTTPError as e:
        print e.errno
        sys.exit()

    print req.text


if __name__ == '__main__':
    user = str(sys.argv[1])
    subject = str(sys.argv[2])
    content = str(sys.argv[3])

    corp_id = '*************'
    corp_secret = '******************'

    access_token = get_token(corp_id, corp_secret)
    send_data(token=access_token, account=user, subject_name=subject, context=content)

前提条件是: 你得有企业微信,然后呢,还要自建应用, 然后在zabbix中做相关设置,下面是我们在实际测试中的信息提示载图:

未分类

Mysql中-Xtrabackup备份和恢复应用

关于Xtrabackup(又或innobackupex)的介绍,详细参考官方文档https://www.percona.com/doc/percona-xtrabackup/2.1/index.html

Xtrabackup安装指南

文件准备

[root@wuxiang11 ~]# cd percona-xtrabackup/
[root@wuxiang11 percona-xtrabackup]# ls
libev-4.15-1.el6.rf.x86_64.rpm  percona-release-0.1-4.noarch.rpm  percona-xtrabackup-24-2.4.4-1.el7.x86_64.rpm

开始安装依赖文件

[root@wuxiang11 percona-xtrabackup]# rpm -ivh libev-4.15-1.el6.rf.x86_64.rpm 
warning: libev-4.15-1.el6.rf.x86_64.rpm: Header V3 DSA/SHA1 Signature, key ID 6b8d79e6: NOKEY
Preparing...                ########################################### [100%]
   1:libev                  ########################################### [100%]

安装-01

[root@wuxiang11 percona-xtrabackup]# rpm -ivH percona-release-0.1-4.noarch.rpm
Preparing packages for installation...
percona-release-0.1-4
[root@wuxiang11 percona-xtrabackup]# yum list | grep percona

马上就安装完成

[root@wuxiang11 percona-xtrabackup]# yum install percona-xtrabackup-24

Loaded plugins: fastestmirror, security
Setting up Install Process
Loading mirror speeds from cached hostfile
Resolving Dependencies
--> Running transaction check
---> Package percona-xtrabackup-24.x86_64 0:2.4.11-1.el6 will be installed
--> Processing Dependency: perl(DBD::mysql) for package: percona-xtrabackup-24-2.4.11-1.el6.x86_64
--> Running transaction check
---> Package perl-DBD-MySQL.x86_64 0:4.013-3.el6 will be installed
--> Processing Dependency: perl(DBI::Const::GetInfoType) for package: perl-DBD-MySQL-4.013-3.el6.x86_64
--> Processing Dependency: perl(DBI) for package: perl-DBD-MySQL-4.013-3.el6.x86_64
--> Processing Dependency: libmysqlclient.so.16(libmysqlclient_16)(64bit) for package: perl-DBD-MySQL-4.013-3.el6.x86_64
--> Processing Dependency: libmysqlclient.so.16()(64bit) for package: perl-DBD-MySQL-4.013-3.el6.x86_64
--> Running transaction check
---> Package Percona-Server-shared-51.x86_64 0:5.1.73-rel14.12.625.rhel6 will be installed
---> Package perl-DBI.x86_64 0:1.609-4.el6 will be installed
--> Finished Dependency Resolution

Dependencies Resolved

===============================================================================================================================================================================================
 Package                                           Arch                            Version                                               Repository                                       Size
===============================================================================================================================================================================================
Installing:
 percona-xtrabackup-24                             x86_64                          2.4.11-1.el6                                          percona-release-x86_64                          8.1 M
Installing for dependencies:
 Percona-Server-shared-51                          x86_64                          5.1.73-rel14.12.625.rhel6                             percona-release-x86_64                          2.1 M
 perl-DBD-MySQL                                    x86_64                          4.013-3.el6                                           centos6.7_64                                    134 k
 perl-DBI                                          x86_64                          1.609-4.el6                                           centos6.7_64                                    705 k

Transaction Summary
===============================================================================================================================================================================================
Install       4 Package(s)

Total download size: 11 M
Installed size: 12 M
Is this ok [y/N]: y
Downloading Packages:
(1/4): Percona-Server-shared-51-5.1.73-rel14.12.625.rhel6.x86_64.rpm                                                                                                    | 2.1 MB     00:02     
(2/4): percona-xtrabackup-24-2.4.11-1.el6.x86_64.rpm                                                                                                                    | 8.1 MB     00:01     
(3/4): perl-DBD-MySQL-4.013-3.el6.x86_64.rpm                                                                                                                            | 134 kB     00:00     
(4/4): perl-DBI-1.609-4.el6.x86_64.rpm                                                                                                                                  | 705 kB     00:00     
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Total                                                                                                                                                          2.5 MB/s |  11 MB     00:04     
warning: rpmts_HdrFromFdno: Header V4 DSA/SHA1 Signature, key ID cd2efd2a: NOKEY
Retrieving key from file:///etc/pki/rpm-gpg/RPM-GPG-KEY-Percona
Importing GPG key 0xCD2EFD2A:
 Userid : Percona MySQL Development Team <[email protected]>
 Package: percona-release-0.1-4.noarch (installed)
 From   : /etc/pki/rpm-gpg/RPM-GPG-KEY-Percona
Is this ok [y/N]: y
Running rpm_check_debug
Running Transaction Test
Transaction Test Succeeded
Running Transaction
Warning: RPMDB altered outside of yum.
** Found 3 pre-existing rpmdb problem(s), 'yum check' output follows:
2:postfix-2.6.6-6.el6_5.x86_64 has missing requires of libmysqlclient.so.16()(64bit)
2:postfix-2.6.6-6.el6_5.x86_64 has missing requires of libmysqlclient.so.16(libmysqlclient_16)(64bit)
2:postfix-2.6.6-6.el6_5.x86_64 has missing requires of mysql-libs
  Installing : perl-DBI-1.609-4.el6.x86_64                                                                                                                                                 1/4 
  Installing : Percona-Server-shared-51-5.1.73-rel14.12.625.rhel6.x86_64                                                                                                                   2/4 
  Installing : perl-DBD-MySQL-4.013-3.el6.x86_64                                                                                                                                           3/4 
  Installing : percona-xtrabackup-24-2.4.11-1.el6.x86_64                                                                                                                                   4/4 
  Verifying  : percona-xtrabackup-24-2.4.11-1.el6.x86_64                                                                                                                                   1/4 
  Verifying  : perl-DBD-MySQL-4.013-3.el6.x86_64                                                                                                                                           2/4 
  Verifying  : Percona-Server-shared-51-5.1.73-rel14.12.625.rhel6.x86_64                                                                                                                   3/4 
  Verifying  : perl-DBI-1.609-4.el6.x86_64                                                                                                                                                 4/4 

Installed:
  percona-xtrabackup-24.x86_64 0:2.4.11-1.el6                                                                                                                                                  

Dependency Installed:
  Percona-Server-shared-51.x86_64 0:5.1.73-rel14.12.625.rhel6                      perl-DBD-MySQL.x86_64 0:4.013-3.el6                      perl-DBI.x86_64 0:1.609-4.el6                     

Complete!

检验是否安装成功

[root@wuxiang11 percona-xtrabackup]# rpm -qa|grep xtrabackup
percona-xtrabackup-24-2.4.11-1.el6.x86_64
[root@wuxiang11 percona-xtrabackup]#

备份思路

Xtrabackup提供了全量备份和增量备份两种方式,全量就不解释了,增量是指其可以只备份指定位置后的新增数据。本文介绍的增量备份方法使用到了LSN(Log Sequence Number),可以从备份成功文件夹的xtrabackup_checkpoints文件中获取,其中to_lsn就是下次增量备份的起始位置。
结合两者,我们可以得到下图的备份思路。

未分类

解释如下:

由于全量备份和增量备份的命令参数不同,所以首先要判断是否需要全量备份。
如果是全量备份,那么便执行命令,备份成功后,记录xtrabackup_checkpoints文件中的to_lsn,以便后面进行增量备份
如果是增量备份,那么首先读取上次备份时记录下的to_lsn,如果读取失败,报错并结束。否则执行相应命令,备份成功后记录to_lsn。
编写一个简单的脚本:db-backup.sh,如下所示:

#!/bin/sh

# xtrabackup的相关配置
INNOBACKUPEX="innobackupex "
MY_CNF="/home/config/mysql/3307.backup.cnf"
MY_USER="xtrabackup"
MY_PASSWORD="xtrabackup"
MY_SOCKET="/home/socket/mysql/3307.sock"

# 远程备份机 文件名配置
REMOTE_HOST="dbbackup"
REMOTE_DIR="/home/backup/mysql/test"
LOCAL_LSN_FILE="~/.to_lsn_important"
DATE_NAME=`date +%Y-%m-%d-%H-%M-%S`
REMOTE_FILE=$DATE_NAME.tar.gz
LOCK_FILE="~/.mysql.backup.lock"
LOCAL_BACKUP_DIR="/home/backup/mysql/test/$DATE_NAME"

# 输出帮助信息
function usage()
{
    echo "Usage:"
    echo "-f db will be backuped fully with this parameter. If not , incrementally. "
}

#防止同时执行两个备份命令,发生冲突
if [ -f $LOCK_FILE ] ;then
    echo 'Mysql backup lockfile is locked!'
    exit 0
fi

full=0

while getopts "fh" arg #选项后面的冒号表示该选项需要参数
do
    case $arg in
        f)  
            full=1
            ;;
        h)  # 输出帮助信息
            usage
            exit 0
            ;;
    esac
done

echo "backup dir is $REMOTE_DIR/$REMOTE_FILE"

# backup up db to remote host
echo "start to backup db!"`date +%Y-%m-%d-%H-%M-%S`
if [ "$full"x = "1"x ] ;then
    # 全量备份
    echo '1' > $LOCK_FILE
    $INNOBACKUPEX --defaults-file=$MY_CNF --user=$MY_USER --password=$MY_PASSWORD --socket=$MY_SOCKET ./ --stream=tar | gzip | ssh $REMOTE_HOST "cat - > $REMOTE_DIR/FULL-$REMOTE_FILE"
    ssh $REMOTE_HOST "cd $REMOTE_DIR;rm -f xtrabackup_checkpoints;tar zxfi $REMOTE_DIR/FULL-$REMOTE_FILE xtrabackup_checkpoints "
    toLSN=$( ssh $REMOTE_HOST "cat $REMOTE_DIR/xtrabackup_checkpoints|grep to_lsn|awk -F= '{gsub(/ /,"",$2);print $2}'" )
    if [ $toLSN ] ;then
        echo $toLSN > $LOCAL_LSN_FILE
    else
        echo 'no lsn from remote host!please check!'
    fi
else
    # 增量备份
    if [ -f $LOCAL_LSN_FILE ] ;then
        toLSN=`cat $LOCAL_LSN_FILE`
    fi

    if [ ! $toLSN ] ;then
        echo 'last LSN is not set !please check!'
        exit 0
    fi

    echo '1' > $LOCK_FILE
    mkdir -p $LOCAL_BACKUP_DIR
    echo "last to lsn is "$toLSN
    $INNOBACKUPEX --parallel=6 --defaults-file=$MY_CNF --user=$MY_USER --password=$MY_PASSWORD --socket=$MY_SOCKET --incremental --incremental-lsn=$toLSN $LOCAL_BACKUP_DIR 2>/tmp/innobackexLog
    toLSN=$( cd $LOCAL_BACKUP_DIR/*; cat xtrabackup_checkpoints|grep to_lsn|awk -F= '{gsub(/ /,"",$2);print $2}' )
    echo "new to lsn is "$toLSN;
    if [ $toLSN ] ;then
        echo $toLSN > $LOCAL_LSN_FILE
        cd $LOCAL_BACKUP_DIR/*;tar zc .|ssh $REMOTE_HOST "cat - > $REMOTE_DIR/$REMOTE_FILE"
        echo "save file to $REMOTE_HOST @ $REMOTE_DIR/$REMOTE_FILE"
    else
        echo 'no lsn from local backup file!delete remote backup file!'$LOCAL_BACKUP_DIR/$REMOTE_FILE
    fi
    echo "remove $LOCAL_BACKUP_DIR"
    rm -rf $LOCAL_BACKUP_DIR
fi

rm -f $LOCK_FILE
echo "end to backup db!"`date +%Y-%m-%d-%H-%M-%S`

将上述脚本配置好后,放到mysql主机上,执行 sh db-backup.sh -f进行全量备份,之后将sh backup.sh添加到crontab中每小时增量备份一次就可以了。

注意–parallel=6使用是有条件的,参考文档https://www.percona.com/doc/percona-xtrabackup/2.1/innobackupex/parallel_copy_ibk.html

恢复思路

一旦线上数据库发生宕机之类的灾难性事故,备份数据就要立马发挥作用了。打开备份文件夹一看,如上面的图,每小时一个压缩文件,时间久了,茫茫多,上百个压缩文件,简直是一定的,这必须得来个脚本自动化恢复数据才成。思路很简单,见下图:

未分类

解释如下:

  1. 先解压,没啥说的。
  2. 找到全量备份的数据,因为增量备份也是以此为基准进行的。上面备份的时候,全量备份文件命名以FULL开头便是为了此处便于查找。
  3. 先恢复全量备份数据,然后按照时间顺序恢复增量备份的数据。
  4. 将数据恢复到mysql中
  5. 启动mysql,连上看看有没问题。

简单的实现脚本,我们叫它restore.sh,如下所示

#!/bin/sh
BACK_DIR="/home/backup/mysql/test"
RESTORE_DIR="/home/tmpback/test"

# xtrabackup恢复时使用的配置文件可能与数据库配置文件不同
RESTORE_MY_CNF="/home/config/mysql/3307.backup.cnf"

# 启动mysql需要的配置文件,按需设定
MY_CNF="/home/config/mysql/3307.cnf"
MY_DATA_DIR="/home/data/mysql/3307"
MY_SOCK_DIR="/home/socket/mysql/"
MY_LOG_DIR="/home/logs/mysqld/3307"

#extract all tar file
fullFileName=""
for file in $( ls $BACK_DIR | grep "tar.gz" )
do
    fileName=${file%.tar.gz}
    full=${file%%-*}
    if [ "${full}"x = "FULL"x ] ;then
        fullFileName=$fileName
    fi
    DEST_DIR="$RESTORE_DIR/$fileName"
    if [ -d $DEST_DIR ] ;then
        echo "$DEST_DIR exists!"
    else
        mkdir -p $DEST_DIR
        echo "start to extract file $file to $DEST_DIR"
        tar zxif $BACK_DIR/$file -C $RESTORE_DIR/$fileName
        echo "end to extract file "$file
    fi
done

echo "full backup dir is "$fullFileName

echo "**start to repare full backup dir"

# 恢复全量数据
echo "innobackupex --apply-log --redo-only --use-memory=4G $RESTORE_DIR/$fullFileName"
innobackupex --apply-log --redo-only --use-memory=4G "$RESTORE_DIR/$fullFileName"

echo "**end to repare full backup dir"

# 恢复增量数据
for file in $( ls $RESTORE_DIR|grep -v "FULL" )
do
    echo "**start to repare $file"
    echo "innobackupex --apply-log --redo-only --use-memory=4G $RESTORE_DIR/$fullFileName --incremental-dir=$RESTORE_DIR/$file"
    innobackupex --apply-log --redo-only --use-memory=4G "$RESTORE_DIR/$fullFileName" --incremental-dir="$RESTORE_DIR/$file"

    echo "**end to repare $file"

done

echo "**start to copy back data to mysql"
rm -rf $MY_DATA_DIR
mkdir $MY_DATA_DIR
mkdir $MY_SOCK_DIR
mkdir $MY_LOG_DIR

#将数据恢复到mysql中
echo "innobackupex --defaults-file=$RESTORE_MY_CNF --copy-back $RESTORE_DIR/$fullFileName"
innobackupex --defaults-file=$RESTORE_MY_CNF --copy-back $RESTORE_DIR/$fullFileName
echo "**end to copy back data to mysql"

chown -R mysql:mysql $MY_DATA_DIR
chown -R mysql:mysql $MY_SOCK_DIR
chown -R mysql:mysql $MY_LOG_DIR

echo "All data has been restored! Try to start mysql now"

echo "mysqld_multi --defaults-file=$MY_CNF start 1"

使用方法很简单,配置好后,直接运行sh restore.sh就可以了。