错误提示:
mount: /mnt: wrong fs type, bad option, bad superblock on /dev/vdc1, missing codepage or helper program, or other error.
主要场景:
该错误通常在挂载 xfs 类型分区时发生,尤其是在要挂载的磁盘与已挂载磁盘(例如系统盘或数据盘)的磁盘 ID(UUID)冲突时。
解决办法:
1. 检查 UUID 冲突
使用以下命令查询系统日志以检查 UUID 冲突:
1 | dmesg | tail |
如果出现以下提示,则表明存在 UUID 冲突:
1 | XFS (vdc1): Filesystem has duplicate UUID 60d67439-baf0-4c8b-94a3-3f10a362e8fe - can't mount |
2. 使用 nouuid 选项进行临时挂载
如果存在 UUID 冲突,可以使用 nouuid
选项进行临时挂载:
1 | mount -o nouuid /dev/vdc1 /abc |
其中,/dev/vdc1
是要挂载的磁盘分区,/abc
是挂载点。
此操作将成功挂载磁盘分区,但重启后挂载会失效。
3. 永久挂载
要永久挂载,需要使用 xfs_admin
命令为新分区分配一个新的 UUID:
1 | sudo xfs_admin -U generate /dev/vdc1 |
其中,/dev/vdc1
是要更改其 UUID 的磁盘分区。
如果是在admin节点修改的ceph.conf,想推送到所有其他节点,则需要执行下述命令
1 | ceph-deploy --overwrite-conf config push mon01 mon02 mon03 osd01 osd02 osd03 |
修改完毕配置文件后需要重启服务生效,请看下一小节
!!!下述操作均需要在具体运行服务的那个节点上运行,而不是admin节点!!!
在各具体节点执行下行命令,重启节点上的所有ceph守护进程
1 | systemctl restart ceph.target |
在各具体节点执行下行命令,按类型重启相应的守护进程
1 | systemctl restart ceph-mgr.target |
1 | systemctl restart ceph-mds.target |
1 | systemctl restart ceph-radosgw.target |
1 | systemctl restart ceph-mon.target |
登录到osd01节点上,该节点上运行有三个osd daemon进程osd.0、osd.l、osd.2
1 | systemctl restart ceph-osd.target |
1 | systemctl restart ceph-osd@0 |
了解:也可以根据进程类型+主机名.service
1 | systemctl { start | stop | restart} ceph-mon@{mon_instance}.service |
1 | systemctl { start | stop | restart} ceph-mgr@{mgr_instance}.service |
1 | systemctl { start | stop | restart} ceph-osd@{osd_instance}.service |
1 | systemctl { start | stop | restart} ceph-radosgw@{rgw_instance}.service |
1 | systemctl { start | stop | restart} ceph-mds@{mds_instance}.service |
有时候需要更改服务的配置,但不想重启服务,或者是临时修改,此时我们就可以通过admin sockets直接与守护进程交互。如查看和修改守护进程的配置参数。
守护进程的socket文件一般是/var/run/ceph/$cluster-$type.$id.asok
基于admin sockets的操作:
1 | ceph --admin-daemon /var/run/ceph/$cluster-$type.$id.asok command |
常用command如下
1 | help |
命令使用格式如下,在管理节点执行即可
1 | ceph tell {daemon-type}.{daemon id or *} injectargs --{name}={value} [--{name}={value}] |
例如
1 | # 在管理节点运行 |
mon_allow_pool_delete此选项的值默认为false,表示不允许删除pool,只有此选项打开后方可删除,记得改回去!!! 这里使用mon.ceph-monitor-1表示只对ceph-monitor-1设置,可以使用*
命令格式如下,需要登录到守护进程所在的那台主机上执行
1 | ceph daemon {daemon-type}.{id} config set {name}={value} |
例。
1 | ssh root@mon01 |
1 | # 1、查看帮助 |
如果超过半数的monitor节点挂掉,此时通过网络访问ceph的所有操作都会被阻塞,但monitor的本地socket还是可以通信的。
1 | ceph --admin-daemon /var/run/ceph/ceph-mon.mon03.asok quorum_status |
1 | # 检查ceph的状态 |
1 | =======================命令1======================= |
1、查看mds状态
1 | ceph mds stat |
2、删除mds节点
1 | ssh root@mon01 systemctl stop ceph-mds.target |
3、关闭mds集群
1 | ceph mds cluster_down |
4、开启mds集群
1 | ceph mds cluster_up |
5、设置cephfs 文件系统存储方式最大单个文件尺寸
1 | ceph mds set max_file_size 1024000000000 |
6、了解:清除mds文件系统
1 | # 1、强制 mds 状态为 featrue |
1、查看mon状态
1 | ceph mon stat |
2、查看mon映射信息
1 | ceph mon dump |
3、检查Ceph monitor仲裁/选举状态
1 | ceph quorum_status --format json-pretty |
4、查看mon信息包括ip地址
1 | 获得一个正在运行的 mon map,并保存在 1.txt 文件中 |
Ceph使用cephx协议对客户端进行身份验证,集群中每一个Monitor节点都可以对客户端进行身份验证,所以不存在单点故障。cephx仅用于Ceph集群中的各组件,而不能用于非Ceph组件。它并不解决数据传输加密问题,但也可以提高访问控制安全性问题。
1、客户端向Monitor请求创建用户。
2、Monitor返回用户共享密钥给客户端,并将此用户信息共享给MDS和OSD。
3、客户端使用此共享密钥向Monitor进行认证。
4、Monitor返回一个session key给客户端,并且此session key与对应客户端密钥进行加密。此session key过一段时间后就会失效,需要重新请求。
5、客户端对此session key进行解密,如果密钥不匹配无法解密,这时候认证失败。
6、如果认证成功,客户端向服务器申请访问的令牌。
7、服务端返回令牌给客户端。
8、这时候客户端就可以拿着令牌访问到MDS和OSD,并进行数据的交互。因为MDS和Monitor之间有共享此用户的信息,所以当客户端拿到令牌后就可以直接访问。
用户通常指定个人或某个应用
个人就是指定实际的人,比如管理员
而应用就是指客户端或Ceph集群中的某个组件,通过用户可以控制谁可以如何访问Ceph集群中的哪块数据。
Ceph支持多种类型的用户,个人与某应用都属于client类型。还有mds、osd、mgr一些专用类型。
用户标识由“TYPE.ID”组成,通常ID也代表用户名,如client.admin、osd.1等。
关于各权限的意义:
1、查看 ceph 集群中的认证用户及相关的 key
1 | ceph auth list # 简写:ceph auth ls |
2、查看某一用户详细信息
1 | ceph auth get client.admin |
3、只查看用户的key信息
1 | ceph auth print-key client.admin |
4、创建用户,用户标识为client.test。指定该用户对mon有r的权限,对osd有rw的权限,osd没有指定存储池,所以是对所有存储池都有rw的权限。在创建用户的时候还会自动创建用户的密钥。
1 | ceph auth add client.test mon "allow r" osd "allow rw" |
5、修改用户权限
1 | ceph auth caps client.test mon "allow r" osd "allow rw pool=kvm" |
6、删除用户,用户名为osd.0
1 | ceph auth del osd.0 |
7、keyring秘钥环文件
keyring文件是一个包含密码,key,证书等内容的一个集合。一个keyring文件可以包含多个用户的信息,也就是可以将多个用户信息存储到一个keyring文件。
当访问Ceph集群时候默认会从以下四个地方加载keyring文件。
8、创建一个名为client.admin 的用户,设置好用户对mds、osd、mon的权限,然后把密钥导出到文件中
1 | ceph auth get-or-create client.admin mds 'allow *' osd 'allow *' mon 'allow *' > /etc/ceph/ceph.client.admin.keyring1 |
9、创建一个名为osd.0 的用户,设置好用户对mon、osd的权限,然后把密钥导出到文件中
1 | ceph auth get-or-create osd.0 mon 'allow profile osd' osd 'allow *' -o /var/lib/ceph/osd/ceph-0/keyring |
10、创建一个名为mds.nc3 的用户,设置好用户对mon、osd、mds的权限,然后把密钥导出到文件中
1 | ceph auth get-or-create mds.nc3 mon 'allow rwx' osd 'allow *' mds 'allow *' -o /var/lib/ceph/mds/ceph-cs1/keyring |
1、查看osd状态
1 | ceph osd stat |
2、查看osd树
1 | ceph osd tree查看 |
3、查看osd映射信息
1 | ceph osd dump |
4、查看数据延迟
1 | ceph osd perf |
5、查看CRUSH map
1 | ceph osd crush dump |
6、查看与设置最大 osd daemon 的个数
1 | # 查看 |
7、设置 osd 的权重
1 | ceph osd reweight 3 0.5 # 把osd.3的权重改为0.5 |
8、暂停 osd (暂停后整个ceph集群不再接收数据)
1 | ceph osd pause # 暂停的是所有的osd |
9、再次开启 osd (开启后再次接收数据)
1 | ceph osd unpause |
10、设置标志 flags ,不允许关闭 osd、解决网络不稳定,osd 状态不断切换的问题
1 | ceph osd set nodown |
1、创建存储池
1 | # 语法:ceph osd pool create <pool name> <pg num> <pgp num> [type] |
2、修改存储池的pg数
注意:在更改pool的PG数量时,需同时更改PGP的数量。PGP是为了管理placement而存在的专门的PG,它和PG的数量应该保持一致。如果你增加pool的pg_num,就需要同时增加pgp_num,保持它们大小一致,这样集群才能正常rebalancing。
1 | ceph osd pool set egon_test pg_num 60 |
3、查看存储池
1 | # 查看ceph集群中的pool数量 |
4、重命名
1 | ceph osd pool rename <old name> <new name> |
5、在集群中删除一个 pool,注意删除 poolpool 映射的 image 会直接被删除,线上操作要谨慎
存储池的名字需要重复两次
1 | ceph osd pool delete tom_test tom_test --yes-i-really-really-mean-it |
6、为一个 ceph pool
配置配额、达到配额前集群会告警,达到上限后无法再写入数据
当我们有很多存储池的时候,有些作为公共存储池,这时候就有必要为这些存储池做一些配额,限制可存放的文件数,或者空间大小,以免无限的增大影响到集群的正常运行。 设置配额。
1 | # 查看池配额设置 |
7、配置参数
对于存储池的配置参数可以通过下面命令获取。
1 | ceph osd pool get <pool name> [key name] |
如
1 | ceph osd pool get <pool name> size |
如果不跟个key名称,会输出所有参数,但有个报错。
1 | ceph osd pool set <pool name> <key> <value> |
如
1 | # 修改pool的最大副本数与最小副本数 |
常用的可用配置参数有。
8、快照
创建存储池快照需要大量的存储空间,取决于存储池的大小。 创建快照,以下两条命令都可以 。
1 | ceph osd pool mksnap <pool name> <snap name> |
列出快照。
1 | rados -p <pool name> lssnap |
回滚至存储池快照。
1 | rados -p <pool name> rollback <obj-name> <snap name> # 只能回复某个对象 |
删除存储池快照,以下两条命令都可以删除。
1 | ceph osd pool rmsnap <pool name> <snap name> |
提示
Pool池的快照,相对来说是有局限性的,没办法直接恢复快照里边全部object对象文件,只能一个个来恢复,保存点密码文件应该还是可以的。这样的设计效果,猜测有可能是因为如果pool池直接整体恢复,会导致整个ceph集群数据混乱,毕竟集群中数据是分布式存放的!
pool存储池快照功能了解即可,感兴趣详见《附录5:》
9、压缩
如果使用bulestore存储引擎,默认提供数据压缩,以节约磁盘空间。 启用压缩。
1 | ceph osd pool set <pool name> compression_algorithm snappy |
snappy:压缩使用的算法,还有有none、zlib、lz4、zstd和snappy等算法。默认为sanppy。zstd压缩比好,但消耗CPU,lz4和snappy对CPU占用较低,不建议使用zlib。
1 | ceph osd pool set <pool name> compression_mode aggressive |
压缩的模式有none、aggressive、passive和force
参数:
1 | compression_max_blob_size:压缩对象的最大体积,超过此体积不压缩。默认为0。 |
1、查看pg组映射信息
1 | ceph pg dump # 或 ceph pg ls |
2、查看pg信息的脚本,第一个行为pool的id号
1 | ceph pg dump | awk ' |
3、查看pg状态
1 | ceph pg stat |
4、查看一个pg的map
1 | ceph pg map 1.7b |
5、查询一个pg的详细信息
1 | ceph pg 1.7b query |
6、清理一个pg组
1 | ceph pg scrub 1.7b |
7、查看pg中stuck(卡住)的状态
1 | ceph pg dump_stuck unclean |
8、显示一个集群中的所有的 pg 统计
1 | ceph pg dump --format plain # 可用格式有 plain (默认)和 json 。 |
9、查看某个 PG 内分布的数据状态,具体状态可以使用选项过滤输出
1 | ceph pg ls 17 clean # 17为pg的编号 |
10、查询 osd 包含 pg 的信息,过滤输出 pg 的状态信息
1 | ceph pg ls-by-osd osd.5 |
11、查询 pool 包含 pg 的信息,过滤输出 pg 的状态信息
1 | ceph pg ls-by-pool egon_test |
12、查询某个 osd 状态为 primary pg ,可以根据需要过滤状态
1 | ceph pg ls-by-primary osd.3 clean |
13、恢复一个丢失的pg
如果集群丢了一个或多个对象,而且必须放弃搜索这些数据,你就要把未找到的对象标记为丢失( lost )。 如果所有可能的位置都查询过了,而仍找不到这些对象,你也许得放弃它们了。这可能是罕见的失败组合导致的, 集群在写入完成前,未能得知写入是否已执行。
当前只支持 revert 选项,它使得回滚到对象的前一个版本(如果它是新对象)或完全忽略它。要把 unfound 对象 标记为 lost ,执行命令:
1 | ceph pg {pg-id} mark_unfound_lost revert|delete |
rados 是和 Ceph 的对象存储集群(RADOS),Ceph 的分布式文件系统的一部分进行交互是一种实用工具。
1、看 ceph 集群中有多少个 pool (只是查看 pool)
1 | rados lspools # 同 ceph osd pool ls 输出结果一致 |
2、显示整个系统和被池毁掉的使用率统计,包括磁盘使用(字节)和对象计数
1 | rados df |
3、创建一个 pool
1 | rados mkpool test |
4、创建一个对象
1 | rados create test-object -p test # 创建时卡住了,看看新建的存储池的crush_rule是否正确 |
5、上传一个对象
1 | rados -p test put xxx /tmp/egon.txt |
6、查看 ceph pool 中的 ceph object (这里的 object 是以块形式存储的)
1 | rados ls -p test |
7、删除一个对象
1 | rados rm test-object -p test |
8 、删除存储池以及它包含的所有数据
1 | rados rmpool test test --yes-i-really-really-mean-it |
9、为存储池创建快照
1 | rados -p test mksnap testsnap |
10、列出给定池的快照
1 | rados -p test lssnap |
11、删除快照
1 | rados -p test rmsnap testsnap |
12、使用 rados 进行性能测试!!!!!!!!!!!!!!!!!!!
1 | rados bench 600 write rand -t 100 -b 4K -p egon_test |
选项解释:
如果ceph集群有上千个osd daemon,每天坏个2-3块盘太正常了,我们可以模拟down 掉一个 osd 硬盘
1 | # 如果osd daemon正常运行,down的osd会很快自恢复正常,所以需要先关闭守护进程 |
5.2 将坏盘踢出集群
集群中坏掉一块盘后,我们需要将其踢出集群让集群恢复到active+clean状态
1 | ====================方法一===================== |
1 | # 远程连接到osd01节点 |
ps:如果重启失败
1 | 报错: |
在osd01节点上添加新的osd daemon
1 | # 在osd01节点运行下述命令,把固态盘分/dev/sdi成两个分区,分别用作数据盘/dev/sdh的--block-db和--block-wal |
如果是在其他节点,例如mon03节点上添加osd daemon
!!!切记切记切记切记切记切记要为mon03节点添加一个cluster network!!!
1 | # 在mon03节点运行下述命令,把固态盘分/dev/sdc成两个分区,分别用作数据盘/dev/sdb的--block-db和--block-wal |
ps: 如果报错,磁盘发现gp信息
1 | 那么先清理磁盘 |
注意
1 | 在OSD添加或移除时,Ceph会重平衡PG。数据回填和恢复操作可能会产生大量的后端流量,影响集群性能。为避免性能降低,可对回填/恢复操作进行配置: |
你可能需要定期对集群中某个子网进行例行维护,或者要解决某个域内的问题。当你停止OSD时,默认情况下CRUSH机制会对集群自动重平衡,可将集群设为noout状态来关闭自动重平衡:
1 | # 1、关闭自动重平衡 |
1 | 在MON和OSD机器上升级安装指定的ceph版本的软件包 |
如果副本数为2,PB级的集群的容量超过50%,就要考虑扩容了。 假如OSD主机的磁盘容量为48TB(12*4TB),则需要backfill的数据为24TB(48TB 50%) ,假设网卡为10Gb,则新加一个OSD时,集群大约需要19200s(24TB/(10Gb/8)) 约3小时完成backfill,而backfill后台数据填充将会涉及大量的IO读和网络传输,必将影响生产业务运行。 如果集群容量到80%再扩容会导致更长的backfill时间,近8个小时。
OSD对应的磁盘利用率如果超过50%,也需要尽快扩容。
在业务闲时扩容
1 问题
一般来说,在实际运行中,ceph monitor的个数是2n+1(n>=0)个,在线上至少3个,只要正常的节点数>=n+1,ceph的paxos算法能保证系统的正常运行。所以,对于3个节点,同时只能挂掉一个。一般来说,同时挂掉2个节点的概率比较小,但是万一挂掉2个呢?
如果ceph的monitor节点超过半数挂掉,paxos算法就无法正常进行仲裁(quorum),此时,ceph集群会阻塞对集群的操作,直到超过半数的monitor节点恢复。
If there are not enough monitors to form a quorum, the ceph command will block trying to reach the cluster. In this situation, you need to get enough ceph-mon daemons running to form a quorum before doing anything else with the cluster.
所以,
(1)如果挂掉的2个节点至少有一个可以恢复,也就是monitor的元数据还是OK的,那么只需要重启ceph-mon进程即可。所以,对于monitor,最好运行在RAID的机器上。这样,即使机器出现故障,恢复也比较容易。
(2)如果挂掉的2个节点的元数据都损坏了呢?出现这种情况,说明人品不行,2台机器的RAID磁盘同时损坏,这得多背?肯定是管理员嫌工资太低,把机器砸了。如何恢复呢?
详见:https://www.cnblogs.com/linhaifeng/articles/14761126.html
Cephfs的快照功能在官网都很少提及,因为即使开发了很多年,但是由于cephfs的复杂性,功能一直没能达到稳定,这里,只是介绍一下这个功能,怎么使用,并且建议不要在生产中使用,因为搞不好是会丢数据的
1 | 1、使能cephfs可以做快照: |
主机名 | 主机IP | 磁盘 | 角色 |
---|---|---|---|
node3 | public-ip:172.18.112.20 cluster-ip: 172.18.112.20 | vdb | ceph-deploy,monitor,mgr,osd |
node4 | public-ip:172.18.112.19 cluster-ip: 172.18.112.19 | vdb | monitor,mgr,osd |
node5 | public-ip:172.18.112.18 cluster-ip: 172.18.112.18 | vdb | monitor,mgr,osd |
主机名设置,三台主机分别执行属于自己的命令
node3
1 | [root@localhost ~]# hostnamectl set-hostname nod3 |
node4
1 | [root@localhost ~]# hostnamectl set-hostname node4 |
node5
1 | [root@localhost ~]# hostnamectl set-hostname node5 |
执行完毕后要想看到效果,需要关闭当前命令行窗口,重新打开即可看到设置效果
在3台机器上都执行下面命令,添加映射
1 | echo "172.18.112.20 node3 " >> /etc/hosts |
创建用户(三台机器上都运行)
1 | useradd -d /home/admin -m admin |
设置免密登录 (只在node3上执行)
1 | [root@node3 ~]# su - admin |
注意: 没有ssh-copy-id
这个命令可以手动把公钥传到对应的机器上去
1 | cat ~/.ssh/id_*.pub | ssh admin@host3 'cat >> .ssh/authorized_keys' |
三台都执行
1 | [root@node3 ~]$ timedatectl #查看本地时间 |
配置ceph清华源
1 | cat > /etc/yum.repos.d/ceph.repo<<'EOF' |
安装ceph-deploy
1 | [admin@node3 ~]# sudo yum install ceph-deploy |
初始化mon点
ceph需要epel源的包,所以安装的节点都需要yum install epel-release
1 | [admin@node3 ~]$ mkdir my-cluster |
修改ceph.conf,添加如下配置
1 | public network = 172.18.112.0/24 |
安装Ceph软件到指定节点
1 | [admin@node3 my-cluster]$ ceph-deploy install --no-adjust-repos node3 node4 node5 |
–no-adjust-repos是直接使用本地源,不生成官方源.
部署初始的monitors,并获得keys
1 | [admin@nod3 my-cluster]$ ceph-deploy mon create-initial |
做完这一步,在当前目录下就会看到有如下的keyrings:
1 | [admin@node3 my-cluster]$ ls |
将配置文件和密钥复制到集群各节点
配置文件就是生成的ceph.conf,而密钥是ceph.client.admin.keyring,当使用ceph客户端连接至ceph集群时需要使用的密默认密钥,这里我们所有节点都要复制,命令如下。
1 | [admin@node3 my-cluster]$ ceph-deploy admin node3 node4 node5 |
1 | #在L版本的`Ceph`中新增了`manager daemon`,如下命令部署一个`Manager`守护进程 |
1 | #用法:ceph-deploy osd create –data {device} {ceph-node} |
检查osd状态
1 | [admin@node3 my-cluster]$ sudo ceph health |
默认情况下ceph.client.admin.keyring文件的权限为600,属主和属组为root,如果在集群内节点使用cephadmin用户直接直接ceph命令,将会提示无法找到/etc/ceph/ceph.client.admin.keyring文件,因为权限不足。
如果使用sudo ceph不存在此问题,为方便直接使用ceph命令,可将权限设置为644。在集群节点上面node1 admin用户下执行下面命令。
1 | [admin@node3 my-cluster]$ ceph -s |
查看osds
1 | [admin@node3 my-cluster]$ sudo ceph osd tree |
方式一:命令操作
1 | ceph mgr module enable dashboard |
如果以上操作报错如下:
1 | Error ENOENT: all mgr daemons do not support module 'dashboard', pass --force to force enablement |
则因为没有安装ceph-mgr-dashboard
,在mgr的节点上安装。
1 | yum install ceph-mgr-dashboard |
方式二:配置文件
1 | # 编辑ceph.conf文件 |
web登录配置
默认情况下,仪表板的所有HTTP连接均使用SSL/TLS进行保护。
1 | #要快速启动并运行仪表板,可以使用以下内置命令生成并安装自签名证书: |
#查看ceph-mgr服务:
1 | [root@node3 my-cluster]# ceph mgr services |
以上配置完成后,浏览器输入 https://node3:8443 输入用户名admin
,密码admin
登录即可查看
]]>要本地hosts解析
ceph集群请看这里:https://imszz.com/p/877f6188/
1 | ceph osd pool create rbd 128 |
1 | [root@node3 ~]# ceph -s |
1 | [root@node3 ~]# ceph auth get client.admin |
或者自己创建存储池、用户以及用户key
1 | [root@node3 ~]# ceph osd pool create kubernetes |
注意:这里key后面对应的只是一个例子,实际配置中要以运行命令后产生的结果为准
这里的key使用user的key,后面配置中是需要用到的
如果是ceph luminous版本的集群,那么命令应该是ceph auth get-or-create client.kubernetes mon 'allow r' osd 'allow rwx pool=kubernetes' -o ceph.client.kubernetes.keyring
1 | cat > /etc/yum.repos.d/ceph.repo<<'EOF' |
1 | yum -y install ceph |
1 | [root@node3 ~]# ceph mon dump |
1 | cat csi-config-map.yaml |
在kubernetes集群上,将此configmap存储到集群
1 | kubectl apply -f csi-config-map.yaml |
1 | cat <<EOF > csi-rbd-secret.yaml |
将此配置存储到kubernetes中
1 | kubectl apply -f csi-rbd-secret.yaml |
可以通信github直接部署
1 | kubectl apply -f https://raw.githubusercontent.com/ceph/ceph-csi/master/deploy/rbd/kubernetes/csi-provisioner-rbac.yaml |
1 | [root@master-1 ~]# cat csi-provisioner-rbac.yaml |
1 | kubectl apply -f csi-provisioner-rbac.yaml |
可以通信github直接部署
1 | kubectl apply -f https://raw.githubusercontent.com/ceph/ceph-csi/master/deploy/rbd/kubernetes/csi-nodeplugin-rbac.yaml |
1 | [root@master-1 ~]# cat csi-nodeplugin-rbac.yaml |
部署
1 | kubectl apply -f csi-nodeplugin-rbac.yaml |
包含镜像版本,要是用其他版本,请自行修改yaml文件:
1 | k8s.gcr.io/sig-storage/csi-resizer:v1.3.0 |
官方文件
1 | wget https://raw.githubusercontent.com/ceph/ceph-csi/master/deploy/rbd/kubernetes/csi-rbdplugin-provisioner.yaml |
以下yml文件所引用的镜像文件已经本地镜像仓库,请根据自己网络环境调整
1 | [root@master-1 ~]# cat csi-rbdplugin-provisioner.yaml |
1 | [root@master-1 ~]# cat csi-rbdplugin.yaml |
修改csi-rbdplugin-provisioner.yaml和csi-rbdplugin.yaml文件,注释关于ceph-csi-encryption-kms-config与ceph-config配置:
1 | [root@master-1 ~]# grep "#" csi-rbdplugin-provisioner.yaml |
注意:所使用的镜像以及修改为本地仓库镜像,请根据自己网络环境调整
1 | dockerhub.kubekey.local/k8s.gcr.io/sig-storage/csi-resizer:v1.3.0 |
部署
1 | kubectl apply -f csi-rbdplugin-provisioner.yaml |
查看运行状态
1 | [root@master-1 ~]# kubectl get pods |
1 | [root@master-1 ~]# cat csi-rbd-sc.yaml |
部署
1 | kubectl apply -f csi-rbd-sc.yaml |
1 | [root@master-1 ~]# kubectl get storageclass |
1 | [root@master-1 ~]# cat raw-block-pvc.yaml |
理论上volumeMode应该指定为Block的,要求PVC和控制器中都指定为相同的模式才能挂载使用,但是经过验证在应用端也指定Block,还是不能挂载上,因此就都去掉了,变成了默认的Filesystem
部署
1 | kubectl apply -f raw-block-pvc.yaml |
1 | [root@master-1 ~]# kubectl get pvc |
1 | [root@master-1 ~]# cat raw-block-pod.yaml |
部署
1 | kubectl apply -f raw-block-pod.yaml |
查看
1 | [root@master-1 ~]# kubectl get pods |
1 | kubectl edit pvc raw-block-pvc #`raw-block-pvc` 想要扩容的pvc,打开pvc修改容量 |
1 | # Please edit the object below. Lines beginning with a '#' will be ignored, |
1 | [root@master-1 ~]# kubectl get pvc |
扩容完成
1 | vim mysql-statefulset-static.yaml |
对于有状态服务来说,如果还是直接使用volumes,则进行动态扩容的时候会报错,所有的Pod都会使用一个相同的PVC,会产生冲突,因此需要使用VolumeClaimTemplate来创建PV。
1 | kubectl edit pvc data-csi-mysql-0 #`data-csi-mysql-0` 想要扩容的pvc,打开pvc修改容量 |
1 | # Please edit the object below. Lines beginning with a '#' will be ignored, |
1 | [root@master-1 ~]# kubectl describe pvc data-csi-mysql-0 |
需要重新部署pod生效
查看应用
1 | kubectl get StatefulSet #有状态应用 |
副本伸缩
1 | kubectl scale StatefulSet csi-mysql --replicas 0 #副本缩容 |
1 | [root@master-1 ~]# kubectl describe pvc data-csi-mysql-0 |
1 | [root@master-1 ~]# kubectl get pvc |
]]>扩容完成
在虚拟机操作系统内的命令行终端上再次执行“fdisk -l”,发现虚拟磁盘总共有416101个柱面,但只使用了其中的208051个柱面,未被使用的柱面就是扩容之后的磁盘,下面需要为未被使用的柱面创建分区。
1 | [root@yjgltpc-cgzs-2 ~]# fdisk -l |
1 | [root@yjgltpc-cgzs-2 ~]# fdisk /dev/vda |
1 | [root@yjgltpc-cgzs-2 ~]# mkfs.ext4 /dev/vda5 # 格式化为ext4文件系统 |
1 | [root@yjgltpc-cgzs-2 ~]# pvcreate /dev/vda5 |
1 | [root@yjgltpc-cgzs-2 ~]# vgdisplay |
1 |
|
1 | [root@yjgltpc-cgzs-2 ~]# lvextend -l +100%FREE /dev/centos/root # 扩展所有可用空间到根分区 |
1 | #上面只是卷扩容了,下面是文件系统的真正扩容,输入以下命令: |
1 | #发现根分区磁盘容量从原来的“50GB”扩容到“~150GB”。 |
1 | # 从远端共享服务器拷贝一个2GB左右的文件到新建磁盘,验证磁盘的可写性。 |
1 | [root@yjgltpc-cgzs-2 log]# mkfs.ext4 /dev/vda5 |
原来是根分区满了,无法创建归档名称,至少需要1M的剩余空间才能操作。 所以必须先删除一些临时文件. 首先使用如下命令,查找根分区中大于1G的文件。
占位
我的博客即将同步至腾讯云+社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=y982vd2u7c9k
]]>即在这个k8s集群上搭建nacos集群。
1 | git clone https://github.com/nacos-group/nacos-k8s.git |
下载之后,上传代码到可执行服务器上。
在高级使用中,Nacos在K8S拥有自动扩容缩容和数据持久特性,请注意如果需要使用这部分功能请使用PVC持久卷,Nacos的自动扩容缩容需要依赖持久卷,以及数据持久化也是一样,本例中使用的是NFS来使用PVC。也就是说nacos是有状态服务,需要持久化磁盘存储数据。
NFS:Network File System(NFS),网络文件系统,存储数据的硬盘。
可以部署在这样一台机器上,可以和上面的k8s集群通讯,这里选择ip:61作为nfs的部署服务,你也可以选在ip:100等,只要能通就可以。
1 | [root@master-01 nacos-k8s]# rpm -qa nfs-utils rpcbind |
我的是已经安装过的,如果没有安装,请安装
1 | # 服务端 ip:61机器上 |
1 | cd /data |
1 | /data/nfs *(insecure,rw,async,no_root_squash) |
配置完成后需要时期生效:
1 | exportfs -r |
具体含义如下:
1 | service rpcbind start |
1 | rpcinfo -p localhost |
由于已经有其他服务,所有看到的多:
1 | service nfs start |
1 | showmount -e localhost |
至此nfs部署完成
1 | kubectl create -f deploy/nfs/rbac.yaml |
如果的K8S命名空间不是default,请在部署RBAC之前执行以下脚本(就不要执行上面的脚本了或者手动修改yaml文件内所属 namespace
:
1 | # Set the subject of the RBAC objects to the current namespace where the provisioner is being deployed |
1 | kubectl create -f deploy/nfs/deployment.yaml |
内容如下:
1 | apiVersion: v1 |
1 | kubectl create -f deploy/nfs/class.yaml |
1 | kubectl get pod -l app=nfs-client-provisioner |
这个数据库就是记录nacos配置的数据库,做到持久化,就能保证安全了。
1 | kubectl create -f deploy/mysql/mysql-nfs.yaml |
代码,如下,有修改哦:
1 | apiVersion: v1 |
1 | kubectl get pod |
数据库初始化语句位置
1 | https://github.com/alibaba/nacos/blob/develop/distribution/conf/nacos-mysql.sql |
如果库中没有这些表需要自己创建。默认是创建完成,自建数据库可以导入使用
1 |
|
先给出修改后的代码:
1 | --- |
1 | kubectl create -f nacos-k8s/deploy/nacos/nacos-pvc-nfs.yaml |
1 | kubectl get pod -l app=nacos |
查看nacos服务对外暴露的端口
1 | kubectl get svc -o wide |
nacos-ingress.yaml
1 | apiVersion: extensions/v1beta1 |
执行下面命令就可以执行成功了
1 | kubectl apply -f nacos-ingress.yaml |
通过k8s 图形界面存储中删除,删除后红色中横线,并没有删除!
1 | kubectl get pv |
1 | kubectl patch pv pvc-122b45c0-78fb-4185-9a29-4b2f023ba25e -p '{"metadata":{"finalizers":null}}' |
1 | 1.先删除 |
]]>参考:https://blog.csdn.net/fsjwin/article/details/110503029
https://nacos.io/zh-cn/docs/use-nacos-with-kubernetes.html
此例是多副本的 MySQL 数据库。
示例应用的拓扑结构有一个主服务器和多个副本,使用异步的基于行(Row-Based)的数据复制。
说明: 这不是生产环境下配置。 尤其注意,MySQL 设置都使用的是不安全的默认值,这是因为我们想把重点放在 Kubernetes 中运行有状态应用程序的一般模式上。
集群需要用到存储,准备持久卷(PersistentVolume,简称PV),我这里以yaml文件创建3个PV。如后续伸缩需要更新PersistentVolume 配置
1 | kind: PersistentVolume |
注意:如果是使用云服务提供的云盘,注意购买云盘要与node节点使用区一致, 还要注意 node 类型支持那些云盘类型
这里发现pv和pvc还没有绑定状态是Available
1 | kubectl apply -f persistent-volume.yaml |
1 | kubectl get pv |
MySQL 示例部署包含一个 ConfigMap、两个 Service 与一个 StatefulSet。
使用以下的 YAML 配置文件创建 ConfigMap :
1 | apiVersion: v1 |
1 | kubectl apply -f mysql-configmap.yaml |
这个 ConfigMap 提供 my.cnf
覆盖设置,使你可以独立控制 MySQL 主服务器和从服务器的配置。在这里,你希望主服务器能够将复制日志提供给副本服务器,并且希望副本服务器拒绝任何不是通过复制进行的写操作。
ConfigMap 本身没有什么特别之处,因而也不会出现不同部分应用于不同的 Pod 的情况。每个 Pod 都会在初始化时基于 StatefulSet 控制器提供的信息决定要查看的部分。
使用以下 YAML 配置文件创建服务:
1 | # Headless service for stable DNS entries of StatefulSet members. |
1 | kubectl apply -f mysql-services.yaml |
这个无头服务给 StatefulSet 控制器为集合中每个 Pod 创建的 DNS 条目提供了一个宿主。因为服务名为 mysql
,所以可以通过在同一 Kubernetes 集群和名字中的任何其他 Pod 内解析 <Pod 名称>.mysql
来访问 Pod。
客户端服务称为 mysql-read
,是一种常规服务,具有其自己的集群 IP。该集群 IP 在报告就绪的所有MySQL Pod 之间分配连接。可能的端点集合包括 MySQL 主节点和所有副本节点。
请注意,只有读查询才能使用负载平衡的客户端服务。因为只有一个 MySQL 主服务器,所以客户端应直接连接到 MySQL 主服务器 Pod(通过其在无头服务中的 DNS 条目)以执行写入操作。
最后,使用以下 YAML 配置文件创建 StatefulSet:
1 | apiVersion: apps/v1 |
1 | kubectl apply -f mysql-statefulset.yaml |
你可以通过运行以下命令查看启动进度:
1 | kubectl get pods -l app=mysql --watch |
一段时间后,你应该看到所有 3 个 Pod 进入 Running 状态:
1 | NAME READY STATUS RESTARTS AGE |
输入 Ctrl+C 结束 watch 操作。如果你看不到任何进度,确保已启用 动态 PersistentVolume 预配器。
StatefulSet 控制器按序数索引顺序地每次启动一个 Pod。它一直等到每个 Pod 报告就绪才再启动下一个 Pod。
此外,控制器为每个 Pod 分配一个唯一、稳定的名称,形如 <statefulset 名称>-<序数索引>
其结果是 Pods 名为 mysql-0
、mysql-1
和 mysql-2
。
上述 StatefulSet 清单中的 Pod 模板利用这些属性来执行 MySQL 副本的有序启动。
在启动 Pod 规约中的任何容器之前,Pod 首先按顺序运行所有的 Init 容器
第一个名为 init-mysql
的 Init 容器根据序号索引生成特殊的 MySQL 配置文件。
该脚本通过从 Pod 名称的末尾提取索引来确定自己的序号索引,而 Pod 名称由 hostname
命令返回。然后将序数(带有数字偏移量以避免保留值)保存到 MySQL conf.d 目录中的文件 server-id.cnf。这一操作将 StatefulSet 所提供的唯一、稳定的标识转换为 MySQL 服务器的 ID,
而这些 ID 也是需要唯一性、稳定性保证的。
通过将内容复制到 conf.d 中,init-mysql
容器中的脚本也可以应用 ConfigMap 中的 primary.cnf
或 replica.cnf
。由于示例部署结构由单个 MySQL 主节点和任意数量的副本节点组成,因此脚本仅将序数 0
指定为主节点,而将其他所有节点指定为副本节点。
与 StatefulSet 控制器的 部署顺序保证相结合,可以确保 MySQL 主服务器在创建副本服务器之前已准备就绪,以便它们可以开始复制。
通常,当新 Pod 作为副本节点加入集合时,必须假定 MySQL 主节点可能已经有数据。还必须假设复制日志可能不会一直追溯到时间的开始。
这些保守的假设是允许正在运行的 StatefulSet 随时间扩大和缩小而不是固定在其初始大小的关键。
第二个名为 clone-mysql
的 Init 容器,第一次在带有空 PersistentVolume 的副本 Pod上启动时,会在从属 Pod 上执行克隆操作。
这意味着它将从另一个运行中的 Pod 复制所有现有数据,使此其本地状态足够一致,从而可以开始从主服务器复制。
MySQL 本身不提供执行此操作的机制,因此本示例使用了一种流行的开源工具 Percona XtraBackup。在克隆期间,源 MySQL 服务器性能可能会受到影响。为了最大程度地减少对 MySQL 主服务器的影响,该脚本指示每个 Pod 从序号较低的 Pod 中克隆。可以这样做的原因是 StatefulSet 控制器始终确保在启动 Pod N + 1 之前 Pod N 已准备就绪。
Init 容器成功完成后,应用容器将运行。MySQL Pod 由运行实际 mysqld
服务的 mysql
容器和充当的 xtrabackup 容器组成。
xtrabackup
sidecar 容器查看克隆的数据文件,并确定是否有必要在副本服务器上初始化 MySQL 复制。如果是这样,它将等待 mysqld
准备就绪,然后使用从 XtraBackup 克隆文件中提取的复制参数执行 CHANGE MASTER TO
和 START SLAVE
命令。
一旦副本服务器开始复制后,它会记住其 MySQL 主服务器,并且如果服务器重新启动或连接中断也会自动重新连接。另外,因为副本服务器会以其稳定的 DNS 名称查找主服务器(mysql-0.mysql
),即使由于重新调度而获得新的 Pod IP,它们也会自动找到主服务器。
最后,开始复制后,xtrabackup
容器监听来自其他 Pod 的连接,处理其数据克隆请求。如果 StatefulSet 扩大规模,或者下一个 Pod 失去其 PersistentVolumeClaim 并需要重新克隆,则此服务器将无限期保持运行。
你可以通过运行带有 mysql:5.7
镜像的临时容器并运行 mysql
客户端二进制文件,将测试查询发送到 MySQL 主服务器(主机名 mysql-0.mysql
)。
1 | #进入主内部 |
使用主机名 mysql-read
将测试查询发送到任何报告为就绪的服务器:
1 | #进入主内部 |
你应该获得如下输出:
1 | +---------+ |
为了演示 mysql-read
服务在服务器之间分配连接,你可以在循环中运行 SELECT @@server_id
:
1 | #进入主内部 |
你应该看到报告的 @@server_id
发生随机变化,因为每次尝试连接时都可能选择了不同的端点:
1 | #如果进入的主执行则结果显示ID`102`与`101`|另客户端执行 则多显示ID`100`,因为主默认ID`100` |
要停止循环时可以按 Ctrl+C ,但是让它在另一个窗口中运行非常有用,这样你就可以看到以下步骤的效果。
为了证明从副本节点缓存而不是单个服务器读取数据的可用性提高,请在使 Pod 退出 Ready状态时,保持上述 SELECT @@server_id
循环一直运行。
mysql
容器的运行命令 mysql -h 127.0.0.1 -e 'SELECT 1'
,以确保服务器已启动并能够执行查询。
迫使就绪态探测失败的一种方法就是中止该命令:
1 | kubectl exec mysql-2 -c mysql -- mv /usr/bin/mysql /usr/bin/mysql.off |
此命令会进入 Pod mysql-2
的实际容器文件系统,重命名 mysql
命令,导致就绪态探测无法找到它。几秒钟后, Pod 会报告其中一个容器未就绪。你可以通过运行以下命令进行检查:
1 | kubectl get pod mysql-2 |
在 READY
列中查找 1/2
:
1 | NAME READY STATUS RESTARTS AGE |
此时,你应该会看到 SELECT @@server_id
循环继续运行,尽管它不再报告 102
。回想一下,init-mysql
脚本将 server-id
定义为 100 + $ordinal
,因此服务器 ID 102
对应于 Pod mysql-2
。
现在修复 Pod,几秒钟后它应该重新出现在循环输出中:
1 | kubectl exec mysql-2 -c mysql -- mv /usr/bin/mysql.off /usr/bin/mysql |
如果删除了 Pod,则 StatefulSet 还会重新创建 Pod,类似于 ReplicaSet 对无状态 Pod 所做的操作。
1 | kubectl delete pod mysql-2 |
StatefulSet 控制器注意到不再存在 mysql-2
Pod,于是创建一个具有相同名称并链接到相同PersistentVolumeClaim 的新 Pod。你应该看到服务器 ID 102
从循环输出中消失了一段时间,然后又自行出现。
如果你的 Kubernetes 其中一个节点 设置不可调度
,则可以通过发出以下命令来模拟节点停机(就好像节点在被升级)。
首先确定 MySQL Pod 之一在哪个节点上:
1 | kubectl get pod mysql-2 -o wide |
节点名称应显示在最后一列中:
1 | NAME READY STATUS RESTARTS AGE IP NODE |
然后通过运行以下命令腾空节点,该命令将其保护起来,以使新的 Pod 不能调度到该节点,然后逐出所有现有的 Pod。将 <节点名称>
替换为在上一步中找到的节点名称。
这可能会影响节点上的其他应用程序,因此最好 仅在测试集群中执行此操作
1 | kubectl drain <节点名称> --force --delete-local-data --ignore-daemonsets |
现在,你可以看到 Pod 被重新调度到其他节点上:
1 | kubectl get pod mysql-2 -o wide --watch |
它看起来应该像这样:
1 | NAME READY STATUS RESTARTS AGE IP NODE |
再次,你应该看到服务器 ID 102
从 SELECT @@server_id
循环输出中消失一段时间,然后自行出现。
现在去掉节点保护(Uncordon),使其恢复为正常模式:
1 | kubectl uncordon <节点名称> |
使用 MySQL 复制,你可以通过添加副本节点来扩展读取查询的能力。使用 StatefulSet,你可以使用单个命令执行此操作:
注意:要有满足伸缩的 PersistentVolume 配置
1 | kubectl scale statefulset mysql --replicas=5 |
查看新的 Pod 的运行情况:
1 | kubectl get pods -l app=mysql --watch |
一旦 Pod 启动,你应该看到服务器 IDs 103
和 104
开始出现在 SELECT @@server_id
循环输出中。
你还可以验证这些新服务器在存在之前已添加了数据:
1 | #进入主内部 |
1 | +---------+ |
向下缩容操作也是很平滑的:
1 | kubectl scale statefulset mysql --replicas=3 |
但是请注意,按比例扩大会自动创建新的 PersistentVolumeClaims,而按比例缩小不会自动删除这些 PVC。这使你可以选择保留那些初始化的 PVC,以更快地进行缩放,或者在删除它们之前提取数据。
你可以通过运行以下命令查看此信息:
1 | kubectl get pvc -l app=mysql |
这表明,尽管将 StatefulSet 缩小为3,所有5个 PVC 仍然存在:
1 | NAME STATUS VOLUME CAPACITY ACCESSMODES AGE |
如果你不打算重复使用多余的 PVC,则可以删除它们:
1 | kubectl delete pvc data-mysql-3 |
通过在终端上按 Ctrl+C 取消 SELECT @@server_id
循环,或从另一个终端运行以下命令:
1 | kubectl delete pod mysql-client-loop --now |
删除 StatefulSet。这也会开始终止 Pod。
1 | kubectl delete statefulset mysql |
验证 Pod 消失。他们可能需要一些时间才能完成终止。
1 | kubectl get pods -l app=mysql |
当上述命令返回如下内容时,你就知道 Pod 已终止:
1 | No resources found. |
删除 ConfigMap、Services 和 PersistentVolumeClaims。
1 | kubectl delete configmap,service,pvc -l app=mysql |
如果你手动供应 PersistentVolume,则还需要手动删除它们,并释放下层资源。如果你使用了动态预配器,当得知你删除 PersistentVolumeClaims 时,它将自动删除 PersistentVolumes。一些动态预配器(例如用于 EBS 和 PD 的预配器)也会在删除 PersistentVolumes 时释放下层资源。
]]>详细参考:https://kubernetes.io/zh/docs/tasks/run-application/run-replicated-stateful-application/
https://kubernetes.io/zh/docs/concepts/storage/persistent-volumes/
通过给 Ingress 资源指定 Nginx Ingress 所支持的 annotation 可实现金丝雀发布。需给服务创建2个 Ingress,其中1个常规 Ingress,另1个为nginx.ingress.kubernetes.io/canary: "true"
· 固定的 annotation 的 Ingress,称为 Canary Ingress。Canary Ingress 一般代表新版本的服务,结合另外针对流量切分策略的 annotation 一起配置即可实现多种场景的金丝雀发布。以下为相关 annotation 的详细介绍:
nginx.ingress.kubernetes.io/canary-by-header
nginx.ingress.kubernetes.io/canary-by-header-value
nginx.ingress.kubernetes.io/canary-by-header-pattern
nginx.ingress.kubernetes.io/canary-by-cookie
nginx.ingress.kubernetes.io/canary-weight
说明:
以上规则会按优先顺序进行评估,优先顺序为:canary-by-header -> canary-by-cookie -> canary-weight
。
当 Ingress 被标记为 Canary Ingress 时,除了nginx.ingress.kubernetes.io/load-balance
和nginx.ingress.kubernetes.io/upstream-hash-by
外,所有其他非 Canary 注释都将被忽略。
可以把以上的四个 annotation
分为三类:
总体划分为以下两大类:
基于权重的 Canary 规则
基于用户请求的 Canary 规则
注意: Ingress-Nginx 实在0.21.0 版本 中,引入的Canary 功能,因此要确保ingress版本OK
首先创建一个 deployment 代表正式版本的服务,编写 yaml 内容如下:
1 | --- |
为这个服务创建 Ingress 路由规则,yaml 文件内容如下:
1 | apiVersion: extensions/v1beta1 |
应用以上 yaml 文件,创建完成后在 k8s 中查看到如下信息:
1 | [k8s-master ~]# kubectl get ingress -n ns-myapp |
此时在命令行中访问 ingress.test.com
可以看到如下内容:
1 | # curl ingress.test.com |
接下来创建一个 Canary 版本的服务,用于作为灰度测试。
参考将上述 Production 版本的 production.yaml
文件,再创建一个 Canary 版本的应用,包括一个 Canary 版本的 deployment
和 service
(为方便快速演示,仅需将 production.yaml 的 deployment
和 service
中的关键字 production
直接替换为 canary
,实际场景中可能涉及业务代码变更)。
基于权重的流量切分的典型应用场景就是蓝绿部署
,可通过将权重设置为 0 或 100 来实现。例如,可将 Green 版本设置为主要部分,并将 Blue 版本的入口配置为 Canary。最初,将权重设置为 0,因此不会将流量代理到 Blue 版本。一旦新版本测试和验证都成功后,即可将 Blue 版本的权重设置为 100,即所有流量从 Green 版本转向 Blue。
使用以下 canary.ingress
的 yaml 文件再创建一个基于权重的 Canary 版本的应用路由 (Ingress)。
注意:要开启灰度发布机制,首先需设置
nginx.ingress.kubernetes.io/canary: "true"
启用 Canary,以下 Ingress 示例的 Canary 版本使用了基于权重进行流量切分的 annotation 规则,将分配 30% 的流量请求发送至 Canary 版本。
1 | apiVersion: extensions/v1beta1 |
接下来在命令行中使用如下命令访问域名 ingress.test.com 100次,计算每个版本分配流量的占比:
1 | c=0;p=0;for i in $(seq 100); do result=$(curl -s ingress.test.com | grep Hostname | awk -F: '{print $2}'); [[ ${result} =~ ^[[:space:]]canary ]] && let c++ || let p++; done;echo "production:${p}; canary:${c};" |
可以得到如下结果:
1 | production:73; canary:28; |
注意这里权重不是一个精确的百分比,使用过程当中,只是会看到一个近似分布。
基于 Request Header 进行流量切分的典型应用场景即灰度发布或 A/B 测试场景
。
给 Canary 版本的 Ingress 新增一条 annotation :nginx.ingress.kubernetes.io/canary-by-header: canary
(这里的 annotation 的 value 可以是任意值),使当前的 Ingress 实现基于 Request Header 进行流量切分。
将 Canary 版本 Ingress 的 yaml 文件修改为如下内容:
1 | apiVersion: extensions/v1beta1 |
说明:金丝雀规则按优先顺序 canary-by-header - > canary-by-cookie - > canary-weight 进行如下排序,因此上面的 ingress 将忽略原有 canary-weight 的规则。
由于上面的 ingress 规则中没有对 canary-by-header: canary
提供具体的值,也就是 nginx.ingress.kubernetes.io/canary-by-header-value
规则,所以在访问的时候,只可以为 canary
赋值 never
或 always
,当 header 信息为 canary:never
时,请求将不会发送到 canary 版本;当 header 信息为 canary:always
时,请求将会一直发送到 canary 版本。示例如下:
1 | [k8s-master ~ ]# curl -s -H "canary:never" ingress.test.com | grep Hostname |
1 | [k8s-master ~ ]# curl -s -H "canary:always" ingress.test.com | grep Hostname |
也可以在上一个 annotation (即 canary-by-header)的基础上添加一条 nginx.ingress.kubernetes.io/canary-by-header-value: user-value
。用于通知 Ingress 将匹配到的请求路由到 Canary Ingress 中指定的服务。
将 Canary 版本 Ingress 的 yaml 文件修改为如下内容:
1 | apiVersion: extensions/v1beta1 |
上面的 ingress 规则设置了 header 信息为 canary:true
,也就是只有满足这个 header 值时才会路由到 canary 版本。示例如下:
1 | [k8s-master ~ ]# curl -s ingress.test.com | grep Hostname |
1 | [k8s-master ~ ]# curl -s -H "canary:true" ingress.test.com | grep Hostname |
与基于 Request Header 的 annotation 用法规则类似。例如在 A/B 测试场景
下,需要让地域为北京的用户访问 Canary 版本。那么当 cookie 的 annotation 设置为 nginx.ingress.kubernetes.io/canary-by-cookie: "users_from_Beijing"
,此时后台可对登录的用户请求进行检查,如果该用户访问源来自北京则设置 cookieusers_from_Beijing
的值为 always
,这样就可以确保北京的用户仅访问 Canary 版本。
将 Canary 版本 Ingress 的 yaml 文件修改为如下内容:
1 | apiVersion: extensions/v1beta1 |
访问示例如下:
1 | [k8s-master ~ ]# curl -s -b "user_from_beijing=always" ingress.test.com | grep Hostname |
1 | [k8s-master ~ ]# curl -s -b "user_from_beijing=no" ingress.test.com | grep Hostname |
]]>多实例Ingress controllers 参考
https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/annotations/#canary
https://cloud.tencent.com/document/product/457/48907
Zookeeper集群需要用到存储,这里需要准备持久卷(PersistentVolume,简称PV),我这里以yaml文件创建3个PV,供待会儿3个Zookeeper节点创建出来的持久卷声明
1 | kind: PersistentVolume |
这里发现pv和pvc还没有绑定状态是Available
1 | kubectl apply -f persistent-volume.yaml |
1 | kubectl get pv |
建议使用新版创建
Kubernetes 使用注解
volume.beta.kubernetes.io/storage-class
而不是storageClassName
属性。这一注解目前仍然起作用,不过在将来的 Kubernetes 发布版本中该注解会被彻底废弃。
1 | kind: PersistentVolume |
1 | apiVersion: v1 |
1 | ···· |
注意:如果是使用云服务商比如阿里云,要注意购买云盘要与node节点使用区一致
下面的清单包含一个 无头服务, 一个 Service, 一个 PodDisruptionBudget, 和一个 StatefulSet。
1 | apiVersion: v1 |
创建了 zk-hs 无头服务、zk-cs 服务、zk-pdb PodDisruptionBudget 和 zk StatefulSet。
1 | kubectl apply -f zookeeper.yml --namespace=zookeeper |
1 | kubectl get poddisruptionbudgets -n zookeeper |
1 | kubectl logs zk-0 -n zookeeper |
没有权限没有办法创建目录
没有zookeeper用户
创建一下并给个权限
1 | useradd -s /sbin/nologin zookeeper |
【注意】{每个安装zk的机器都要执行创建用户以及授权}
如果你是k8s三节点,请注意:
出于安全考虑Pod不会被调度到Master Node上,也就是说Master Node不参与工作负载
如果希望master进行调度
使用污点(taints)与容忍(tolerations)进行调整
获取 zk StatefulSet 中 Pods 的主机名。
1 | for i in 0 1 2; do kubectl exec --namespace zookeeper zk-$i -- hostname; done |
看一下效果是不是集群模式
1 | for i in 0 1 2; do kubectl exec --namespace zookeeper zk-$i zkServer.sh status; done |
检查每个服务器的 myid 文件的内容
1 | for i in 0 1 2; do echo "myid zk-$i";kubectl exec --namespace zookeeper zk-$i -- cat /var/lib/zookeeper/data/myid; done |
获取 zk StatefulSet 中每个 Pod 的全限定域名
1 | for i in 0 1 2; do kubectl exec --namespace zookeeper zk-$i -- hostname -f; done |
Pod 中查看 zoo.cfg 文件的内容。
1 | kubectl exec --namespace zookeeper zk-0 -- cat /opt/zookeeper/conf/zoo.cfg |
最基本的健康检查是向一个 ZooKeeper 服务器写入一些数据,然后从 另一个服务器读取这些数据
1 | kubectl exec --namespace zookeeper zk-0 zkCli.sh create /hello world |
从 zk-1 Pod 获取数据。
1 | kubectl exec --namespace zookeeper zk-1 zkCli.sh get /hello |
如果出现myid
重复可以进入node内/var/lib/zookeeper/data/
下 修改id
参数,然后重新部署
]]>参考:https://kubernetes.io/zh/docs/tutorials/stateful-application/zookeeper/
(namespace)
下的服务之间调用,之间通过服务名(service name)
调用即可。不过在更多时候,我们可能会将一些服务单独隔离在一个命名空间中(比如我们将中间件服务统一放在 middleware 命名空间中,将业务服务放在 business 命名空间中)。 遇到这种情况,我们就需要跨命名空间访问,K8S 对service 提供了四种不同的类型,针对这个问题我们选用 ExternalName
类型的 service 即可。k8s service 分为四种类型
分别为:
本文使用 ExternalName
实现我们的需求:
通过 {SERVICE_NAME}.{NAMESPACE_NAME}.svc.cluster.local
这样的格式,访问目标 namespace
下的服务。
nodeAffinity
无论是硬策略还是软策略方式,都是调度 pod 到预期节点上,而Taints
恰好与之相反,如果一个节点标记为 Taints ,除非 pod 也被标识为可以容忍污点节点,否则该 Taints 节点不会被调度 pod。比如用户希望把 Master 节点保留给 Kubernetes 系统组件使用,或者把一组具有特殊资源预留给某些 pod,则污点就很有用了,pod 不会再被调度到 taint 标记过的节点。我们搭建的集群默认就给 master 节点添加了一个污点标记,所以我们看到我们平时的 pod 都没有被调度到 master 上去:
1 | $ kubectl describe node master |
我们可以使用上面的命令查看 master 节点的信息,其中有一条关于 Taints 的信息:node-role.kubernetes.io/master:NoSchedule
,就表示给 master 节点打了一个污点的标记,其中影响的参数是NoSchedule
,表示 pod 不会被调度到标记为 taints 的节点,除了 NoSchedule 外,还有另外两个选项:
污点 taint 标记节点的命令如下:
1 | $ kubectl taint nodes node02 test=node02:NoSchedule |
上面的命名将 node02 节点标记为了污点,影响策略是 NoSchedule,只会影响新的 pod 调度,如果仍然希望某个 pod 调度到 taint 节点上,则必须在 Spec 中做出Toleration
定义,才能调度到该节点,
比如现在我们想要将一个 pod 调度到 master 节点:(taint-demo.yaml)
1 | apiVersion: apps/v1 |
由于 master 节点被标记为了污点节点,所以我们这里要想 pod 能够调度到 master 节点去,就需要增加容忍的声明:
1 | tolerations: |
然后创建上面的资源,查看结果:
1 | $ kubectl create -f taint-demo.yaml |
我们可以看到有一个 pod 副本被调度到了 master 节点,这就是容忍的使用方法。
对于 tolerations 属性的写法,其中的 key、value、effect 与 Node 的 Taint 设置需保持一致, 还有以下几点说明:
另外,还有两个特殊值:
最后,如果我们要取消节点的污点标记,可以使用下面的命令:
1 | $ kubectl taint nodes node02 test- |
这就是污点和容忍的使用方法。
]]>在Mac上安装Photoshop CS6的后, 启动台(LaunchPad)莫名其妙的多出了几个”Adobe xxxx…”的图标, 而且无法删除,在访达里面应用程序内也找不到, 非常讨厌。
在网上搜索了试过终端删除,app删除,找到程序文件夹删除等各种方法,但都失败了。。。
最后重点来了,我找到了一个终极解决办法:
重建 启动台(LaunchPad) 内的图标来解决.
打开应用程序- 实用工具 - 终端. 以此出入如下命令:
1 | defaults write com.apple.dock ResetLaunchPad -bool true |
再次打开 LaunchPad 的时候, 所有图标会被重建。
如果发现启动台(LaunchPad)里面出现了一个新的相关文件夹,并且是原来Adobe之类的程序, 那么需要再次打开
访达->应用程序->实用工具
内找到对应相关文件程序删掉即可。
最后,你会发现重置之后之前的所有设置都会丢失. 没有特殊情况不要使用哦. 以免丢失之前的排列方式与文件夹.
有些应用程序(比如说虚拟机),安装之后会在启动台生成文件夹或其它图标,但是卸载了应用之后,这个文件夹依然会保留下来,简直逼死强迫症。
卸载应用程序之后,一般其在启动台生成的文件夹是不会被删除的,不过这个文件夹里面是空的。如果执意要删除的话,可以从Finder
(访达)里面入手。具体操作为,打开访达,按下快捷键「commond」+「shift」+「H」
,之后页面会自动跳转到用户的主页。打开「应用程序文件夹」
,里面的都是launchpad的内容,找到你要删除的目标将其删除即可。
PropellerAds是2018-2019年度最好的cpm广告网络之一,也是支付率最高的cpm广告网络之一。如果您正在寻找移动广告,弹出窗口,对话框和插页式广告,那么PorpellerAds是您最适合的CPM网络。出版商将获得10美元的有效每千次展示费用,这个每千次展示费率取决于访问国家,如果您的网站拥有高流量来自英国,美国,那么您可以预期这个广告网络很多钱。它提供了许多广告格式供用户赚取,这些广告格式是横幅广告,原生直接广告,流行下广告,非页内广告,上推广告,对话广告。螺旋桨广告支付净30基础。最低支付限额为100美元,发布可以通过电汇和PayPal提款。
支持国内IP,PropellerAds本身有banner和弹窗广告 , 但是banner广告收入极低 , 所以不建议去做 反而弹窗收入高(垃圾站点使用高)
链接地址PropellerAds
我们选择账户类型为Publisher,注意这里我们注册为发行商,一定不要选错了
提供广告的请注册Advertiser,
跳转到这个页面
据实填写我们的个人信息即可,填写完成以后点击下一步 ,只填写必要信息即可
点击下一页后在相关的输入框中大家可以根据我填写的内容来进行填写,这里其实只需要简单的说明一下我们目前的流量源
最后点击注册就可以了,基本上注册以后我很快会收到确认邮件,当即注册马上就能进入平台了
在你的邮箱中收到这份确认邮件以后点击验证账户,然后会跳转至设置初始密码的页面,设置完成以后就ok了,恭喜你,
添加网站
验证
验证通过后添加广告类别
选择自己适用的类别
add zone
点击获取代码并选择在自己的官网手动引用就可以
请注意:MultiTag 广告格式包含(In-Page Push (Banner)与Onclick (Popunder)与Interstitial)
不太建议直接使用MultiTag与Onclick (Popunder) 这两种广告格式 因为会跳转到其他网站,可能会包含非法站点
linux系统时间有两个,一个是硬件时间,即BIOS时间,就是我们进行CMOS设置时看到的时间,另一个是系统时间,是linux系统Kernel时间。当Linux启动时,系统Kernel会去读取硬件时钟的设置,然后系统时钟就会独立于硬件运作。有时我们会发现系统时钟和硬件时钟不一致,因此需要执行时间同步。
1 | 1、将日期设置为2017年11月3日 |
1 | 1、查看系统硬件时钟 |
1 | [root@linux-node ~]# hwclock --hctosys 或者 |
时区设置用tzselect
命令来实现。但是通过tzselect
命令设置TZ
这个环境变量来选择的时区,需要将变量添加到.profile
文件中。
1 | 执行tzselect命令 --> 选择Asia --> 选择China --> 选择east China - Beijing, Guangdong, Shanghai, etc-->然后输入1。 |
执行完tzselect
命令选择时区后,时区并没有更改,只是在命令最后提示你可以执行 TZ=’Asia/Shanghai’; export TZ
并将这行命令添加到.profile
中,然后退出并重新登录。
1 | [root@linux-node ~]# echo "ZONE=Asia/Shanghai" >> /etc/sysconfig/clock |
执行完上述过程后,重启机器,即可看到时区已经更改。
1 | 在centos7中设置时区的命令可以通过 timedatectl 命令来实现 |
1 | character-set-client-handshake = FALSE |
用来控制客户端声明使用字符集和服务端声明使用的字符集在不一致的情况下的兼容性.
1 | character-set-client-handshake = false |
1 | # 默认为 true |
声明服务端的字符编码, 推荐使用utf8mb4 , 该字符虽然占用空间会比较大, 但是可以兼容 emoji 😈 表情的存储
1 | character-set-server = utf8mb4 |
声明服务端的字符集, 字符编码和字符集一一对应, 既然使用了utf8mb4的字符集, 就要声明使用对应的字符编码
1 | collation-server = utf8mb4_unicode_ci |
init_connect
是用户登录到数据库上之后, 在执行第一次查询之前执行里面的内容. 如果 init_connect
的内容有语法错误, 导致执行失败, 会导致用户无法执行查询, 从mysql 退出
使用 init_connect
执行 SET NAMES utf8mb4
意为:
声明自己(客户端)使用的是 utf8mb4 的字符编码
希望服务器返回给自己 utf8mb4 的查询结果
1 | init_connect = 'SET NAMES utf8mb4' |
1 | character-set-client-handshake = FALSE |
MySQL5.7 在 5.6 版本的基础之上做了大量的优化, 本篇文章开篇将重点围绕经过优化的基于 GTID 的多线程复制和半同步复制的特性介绍, 后续会持续增加 MySQL5.7 的调优参数
1 | [client] |
##在CentOS7
中编译安装MySQL 5.7.21
. 依赖和源码包 安装相关的依赖:
1 | yum install gcc gcc-c++ ncurses ncurses-devel cmake bison openssl-devel -y |
下载MySQL 5.7.32
源码包和依赖boost
, MySQL 5.7.32
依赖boost 1.59.0
:
1 | curl -o boost_1_59_0.tar.gz https://jaist.dl.sourceforge.net/project/boost/boost/1.59.0/boost_1_59_0.tar.gz |
解压下载的包:
1 | # 进入下载的路径 |
创建MySQL
用户和组, 并且用户不能登陆:
1 | groupadd -r mysql && useradd -r -g mysql -s /sbin/nologin -M mysql |
1 | mkdir -p /home/mysql/data |
1 | chown -Rf mysql:mysql /usr/local/mysql |
使用各种参数, 预编译源代码. 进入解压的MySQL
源码目录, 执行以下命令:
1 | cmake -DCMAKE_INSTALL_PREFIX=/usr/local/mysql -DMYSQL_DATADIR=/home/mysql/data -DSYSCONFDIR=/etc -DMYSQL_UNIX_ADDR=/usr/local/mysql/mysqld.sock -DEXTRA_CHARSETS=all -DDEFAULT_CHARSET=utf8mb4 -DDEFAULT_COLLATION=utf8mb4_unicode_ci -DWITH_MYISAM_STORAGE_ENGINE=1 -DWITH_INNOBASE_STORAGE_ENGINE=1 -DWITH_PARTITION_STORAGE_ENGINE=1 -DWITH_ARCHIVE_STORAGE_ENGINE=1 -DWITH_BLACKHOLE_STORAGE_ENGINE=1 -DENABLED_LOCAL_INFILE=1 -DENABLED_PROFILING=1 -DMYSQL_TCP_PORT=3306 -DWITH_DEBUG=0 -DDOWNLOAD_BOOST=1 -DWITH_BOOST=/usr/local/boost_1_59_0 |
1 | DCMAKE_INSTALL_PREFIX=/usr/local/mysql :安装路径 |
预编译完成后, 执行下面的命令编译, 安装:
1 | # 指定CPU数量编译 |
对目录修改权限, 添加service/systemd
服务:
1 | chown -R mysql:mysql /usr/local/mysql |
将/usr/local/mysql/bin
添加进入环境变量
, 或者直接使用软链接
的方式链到/usr/local/bin
下:
1 | # 添加到环境变量 |
以上都完成后, 还不能启动MySQL, 如果非要启动, 会报错. 需要初始化数据库:
1 | /usr/local/mysql/bin/mysqld --initialize --user=mysql --basedir=/usr/local/mysql --datadir=/home/mysql/data |
1 | --user :指定用户 |
初始化后, 会有一行提示, 冒号后面的是初始密码root@localhost: password
:
1 | A temporary password is generated for root@localhost: xKefZvib13)5 |
以上都配置完成, 就可以启动服务了:
1 | # 使用service |
将初始密码修改成自己的密码, 直接在shell
中输入命令: mysqladmin -uroot -p'old_pass' password 'new_pass'
默认MySQL不
需要配置文件, 编译时已经配置好了, 但是也可以使用配置文件, 指定log
的位置, 编辑vim /etc/my.cnf
, 将以下内容添加到文件中:
1 | [client] |
在初次安装mysql 的时候将数据库目录安装在了系统盘。(第一个磁盘)使用了一段时间之后数据库存储量变大,快将20GB的存放空间占满了。因此必须将存放数据空间换地方了。下面是简单的操作。
1 | mysql -u root -prootadmin |
1 | #进入数据库 |
1 | service mysql stop |
1 | mkdir /data/mysql |
1 | cp -R /usr/local/mysql/data/* /data/mysql/ #或mv /usr/local/mysql/data/* /data/mysql |
1 | chown mysql:mysql -R /data/mysql/ |
1 | service mysqld start |
说明:根据以上的简单6步操作,已经成功的数据库目录更换路径了。
]]>备注:以上系统为CentOS Linux release 7.8.2003 (Core) mysql-5.7.32 编译安装
1、cat /proc/version
1 | [root@localhost ~]# cat /proc/version |
2、uname -a
1 | [root@localhost ~]# uname -a |
1、lsb_release -a
,即可列出所有版本信息:
1 | [root@localhost ~]# lsb_release -a |
这个命令适用于所有的Linux发行版,包括Redhat、SuSE、Debian…等发行版。
2、cat /etc/redhat-release
,这种方法只适合Redhat系的Linux:
1 | [root@localhost ~]# cat /etc/redhat-release |
3、cat /etc/issue
,此命令也适用于所有的Linux发行版。
1 | [root@localhost ~]# cat /etc/issue |
1 | Windows |
在文件末尾添加:
1 | # GitHub Start |