-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcontent.json
1 lines (1 loc) · 589 KB
/
content.json
1
{"meta":{"title":"BOHC!","subtitle":"blog of hechao!","description":"BOHC is just a blog of hechao.","author":"Hechao","url":"https://wudihechao.github.io"},"pages":[{"title":"categories","date":"2019-10-04T08:22:01.000Z","updated":"2019-11-02T12:20:45.155Z","comments":false,"path":"categories/index.html","permalink":"https://wudihechao.github.io/categories/index.html","excerpt":"","text":""},{"title":"about","date":"2019-10-02T01:55:37.000Z","updated":"2019-11-08T09:19:50.106Z","comments":true,"path":"about/index.html","permalink":"https://wudihechao.github.io/about/index.html","excerpt":"","text":"title: BOHC!subtitle: blog of hechao!description: BOHC is just a blog of hechao.keywords: Linux DEVOPS"},{"title":"友链","date":"2019-11-08T09:17:51.000Z","updated":"2019-11-13T11:32:14.557Z","comments":true,"path":"friends/index.html","permalink":"https://wudihechao.github.io/friends/index.html","excerpt":"","text":"欢迎留言互换友链 友链格式:网站名称:BOHC!网站地址:https://hewanyue.com网站描述:BOHC is just a blog of hechao.网站Logo/头像:https://gravatar.loli.net/avatar/37019f5eb1f0b9951c06bfdc8342afd1"},{"title":"tags","date":"2019-10-02T02:01:55.000Z","updated":"2019-10-05T13:56:56.518Z","comments":false,"path":"tags/index.html","permalink":"https://wudihechao.github.io/tags/index.html","excerpt":"","text":""}],"posts":[{"title":"关于游戏服务端的搭建","slug":"关于游戏服务端的搭建","date":"2020-02-18T13:15:21.000Z","updated":"2020-02-19T16:22:31.685Z","comments":true,"path":"/blog/ea0d2f46.html","link":"","permalink":"https://wudihechao.github.io/blog/ea0d2f46.html","excerpt":"  前一阵,一场突如其来的疫情,将我们都困在了家中,于是有了大把的时间,来做一些自己感兴趣的事情。当然,是打游戏咯。  不过只是打游戏,也太low了,完全不符合我们程序员的气质,我们作为网络世界中的众多缔造者之一,仅仅扮演一个渺小的角色,未免太过无趣。于是,我想到自己搭建服务器给自己玩,做自己的神~哈,开玩笑的,其实是为了更好的了解那些手游端游的运作机制啦,就搭建了几款不同的游戏,也算研究研究 常用架构吧。","text":"  前一阵,一场突如其来的疫情,将我们都困在了家中,于是有了大把的时间,来做一些自己感兴趣的事情。当然,是打游戏咯。  不过只是打游戏,也太low了,完全不符合我们程序员的气质,我们作为网络世界中的众多缔造者之一,仅仅扮演一个渺小的角色,未免太过无趣。于是,我想到自己搭建服务器给自己玩,做自己的神~哈,开玩笑的,其实是为了更好的了解那些手游端游的运作机制啦,就搭建了几款不同的游戏,也算研究研究 常用架构吧。 准备工作准备游戏服务架构  这些天,我下载了很多游戏的服务架构,页游、手游、端游都有。看到里面的配置和架构也都五花八门。总的来说,一般页游大多是AMP+JAVA,手游遇到很多, JAVA+MongoDB,页游AMP+Erlang+RabbitMQ的组合。端游就更加五花八门了,很多引擎或者自制脚本,使用的数据库种类也很多,MongoDB、mysql、SQL Server等等,不同游戏的架构选择都不同。  因为我们自己肯定没法写出来一个完整的游戏服务包,所以最理想的也是最简便的方法就是使用别人已经写好的现成的服务包,安装配置即可(里面坑巨多,免费分享出来的,完整性和质量就没法苛求了),大部分修复下或者调整下也是能够凑活跑起来的。  不过倒也遇到很多问题,游戏中有很多bug,例如这个任务流程过不去,那个点了没反应,只能边玩边吐槽;还有个手游架设好后,一直没有报错,就一直没管他,运行3天后,有小伙伴说登录不上,才发现服务器数据库崩掉连接不上了,由于搭建好后就没有关注监控这个服务器,甚至不知道是啥时候崩掉的,也就不知道为什么会崩,重新搭建连接完数据库,之前玩的游戏数据也就清空没有(没有设置实时备份数据库),小伙伴们很是扫兴,还好不是实际生产中,这我也很无奈啊。  但总体下来,大家反应都是很不错的,毕竟道具无限,人人都是大佬,也算无聊的假期大家一起有共同消遣了。 网络环境  因为要自己开服务器,如果只是在本地电脑上配置,那其他小伙伴们就没法连接进来,自己游戏里再NP也没人分享可太悲哀了。所以,我们一定要连上外网,需要公网IP,才可以供他人访问。  博主有一个aws的海外云主机,还有一个腾讯云的国内云主机,虽然直接在云主机上搭建可以直接解决公网IP的问题,不过这俩云主机配置都不高,都是1C1G,担心无法完美支撑服务跑起来(花钱升级配置是不可能的~)。于是初步决定在本地先将服务跑起来,用 你v懂p得n 打通本地电脑和云主机的网络,配置云主机的反向代理,实现游戏服务器公网搭建。做出规划步骤如下 本地搭建游戏服务端 用 你v懂p得n 打通本地主机和云主机的网络 配置云主机的代理转发 修改本地主机的游戏服务监听端口 配置客户端 正式搭建本地搭建游戏服务端  因为我搭建了很多不同类型的游戏,且每一种其实方法步骤都不一样,需要的配置和环境也都不同,遇到的问题也不尽相同,在这里就没法一一细说了,会在文末将我搭建成功的那些服务包都贴出来,有需要的小伙伴可以自行研究。 打通网络  可以参考我之前博客https://hewanyue.com/blog/2c6b894f.html架设 你v懂p得n 。协议可以使用UDP,之前我用TCP总是被封掉。搭建好,建立连接后,其实就已经算是打通网络了。  在window cmd命令行或者linux的终端里ping 10.8.0.1 就可以ping通云主机了的。然后也可以尝试用云主机验证 访问本地10.8.0.6上面的http服务之类的。 配置云主机反向代理  虽然这两台机子间是联通了,但是别人访问你的云主机的IP,并不能连接到你本地的服务器主机,所以我们需要在防火墙上配置转发。  例如本地服务器开启的端口为 12345,而我们想让别人访问云主机的 54321端口就可以登录我们的游戏服务,需要填写dnat 还有地址伪装。代码如下:1iptables -t nat -D PREROUTING -d 0.0.0.0 -p tcp --dport 54321 -j DNAT --to-destination 10.8.0.6:12345   如果客户端设置的链接协议是UDP,那就将tcp改为UDP,若是不知道客户端程序写的链接方式到底是什么,可以写两条将这俩都转发了。不过如果不写协议会报错的。123[root@ip-172-31-39-115 ~]# iptables -t nat -D PREROUTING -d 0.0.0.0 --dport 54321 -j DNAT --to-destination 10.8.0.6:12345iptables v1.8.2 (nf_tables): unknown option \"--dport\"Try `iptables -h' or 'iptables --help' for more information.   如果不想改不同的端口,倒也可以简单粗暴的,将访问云主机的所有数据统统转发到本地,但这样的话,云主机的其他功能就都会受到影响,不建议这样(云主机上没有其他服务倒也可以这样)。  还有记得防火墙开启转发功能,否则可能服务器的内网网卡收不到数据报文。12echo \"net.ipv4.ip_forward = 1\" >> /etc/sysctl.confsysctl -p   有的游戏,本地服务器还配有网站等等例如gm简易工具,需要我们在外网也能访问。可以设置nginx或者apache的反向代理转发,比较简单,这里就不细说了。 修改本地主机的游戏服务监听IP端口  出于安全考虑,很多游戏服务配置的时候,相互之间都是,配置了访问权限的,我下载的大部分游戏都是监听在本地127.0.0.1回环网卡的,如果我们不修改监听地址,从我们构建来的专线的访问数据是没有权限访问我们的服务的,所以我们需要将监听地址修改为10.8.0.6专线网卡的ip上。或者改为0.0.0.0(不建议,有可能端口冲突,造成服务起不来或者报错)。  一般php或者数据库那些地址就不用改了,因为还是从本地读取的。到时候捋一捋就可以确保正确连接了。 配置客户端  因为一般需要连接的服务器IP端口,都是写死在客户端中的,我们修改了服务器的地址,所以还要在客户端中修改为正确的ip地址端口,才可以链接(页游不用,页游没有客户端)。  不同游戏修改方式和路径都不相同,安卓端可以用APKIDE,苹果ipa的可以直接将后缀改为rar,解压后修改对应ip,这里就不细说了。欢迎就具体游戏来讨论。 附本人搭建测试成功的游戏及网盘分享 手游剑侠情缘游戏预览:(随便拍的,手边没有安卓机,用的电脑模拟器) [video(video-mslzJS9V-1582030113247)(type-youku)(url-https://player.youku.com/embed/XNDU1MTQyNjcwMA==)(image-https://vthumb.ykimg.com/054106015E4BB2DE000001693906AF84)(title-剑侠情缘手游)] 安装vmware的centos虚拟机,里面是java程序。  下载地址  剑侠情缘VM一键端链接:https://pan.baidu.com/s/1D0qOO7XcK2K93BLXCsw-fA提取码:4371 手游蓝月传奇游戏预览:  这个是在windows环境下运行的,用到PHP、nodejs、mysql、Erlang+rabbitMQ  下载地址  【蓝月传奇】一键端+修改教程+全功能GM网页后台+外网教程链接:https://pan.baidu.com/s/1wjfmMWaTf2YxZ0rxjCtknw提取码:6c8m 手游七雄争霸  JAVA游戏,windows环境搭建,用到了memcached、nodejs、PHP  下载地址链接:https://pan.baidu.com/s/1o-pZb4TWKZWf3vmGk70bfQ提取码:78ws 手游 幽冥传奇  Windows环境运行,用的战神引擎。  下载地址链接:https://pan.baidu.com/s/1mLgic84xPkBhkrOiJqx4ZQ提取码:ja6l 页游 传奇世界  H5游戏,也还不错。  下载地址链接:https://pan.baidu.com/s/1Zv1vMoD9l1cegh3FRghaXA提取码:7057 页游 赤月传说  下载地址链接:https://pan.baidu.com/s/1S1RW6JAmR1Xx063XzoJTOA提取码:i168 端游 无极联盟传奇  Windows环境运行,用的GOM引擎。  这个虽然特效还有模式都还不错,不过是个残端,里面没有提供pak密码,不建议安装。  下载地址链接:https://pan.baidu.com/s/13OOZxWbESveJLXmeS-w1Bw提取码:7nom 端游 三国战纪OL(第二季)  Windows环境运行,用的GOM引擎。这个可以完美开服,还算不错。  下载地址链接:https://pan.baidu.com/s/12x4Kvb-_Pipn6Qs73JkBPw提取码:xb6r   很多游戏搭好之后,玩了也还不错,不过忘记保留截图了。还有一些还没上传,等日后再分享。欢迎交流讨论。","categories":[{"name":"生活","slug":"生活","permalink":"https://wudihechao.github.io/categories/生活/"}],"tags":[{"name":"服务端","slug":"服务端","permalink":"https://wudihechao.github.io/tags/服务端/"},{"name":"游戏搭建","slug":"游戏搭建","permalink":"https://wudihechao.github.io/tags/游戏搭建/"},{"name":"资源分享","slug":"资源分享","permalink":"https://wudihechao.github.io/tags/资源分享/"}],"keywords":[{"name":"生活","slug":"生活","permalink":"https://wudihechao.github.io/categories/生活/"}]},{"title":"linux使用vmware虚拟机玩LOL","slug":"linux使用vmware虚拟机玩LOL","date":"2020-01-26T15:16:49.000Z","updated":"2020-01-26T15:43:19.806Z","comments":true,"path":"/blog/c1add9f7.html","link":"","permalink":"https://wudihechao.github.io/blog/c1add9f7.html","excerpt":"  自从入坑linux系统,便越陷越深,操作起来确实方便很多。不过linux系统有一个致命的问题,那就是没法玩游戏!","text":"  自从入坑linux系统,便越陷越深,操作起来确实方便很多。不过linux系统有一个致命的问题,那就是没法玩游戏!  平常也有罢了,不玩也就算了。不过这一阵子,恰逢春节,却又赶上疫情不能出门,几乎排行榜所有的电影都扫遍了,无可奈何又想起了早就删除了的英雄联盟。  但是现在系统已经是Deepin系统了。用起来很简洁,也比较方便,桌面也很漂亮,给大家看看桌面哈~ vmware  言归正传,我们需要安装一个window的虚拟机。因为本地已有vmware,所以直接用vmware创建。  没有安装vmware的去官网下载一个linux版本的,网上找一个个激化码就可以正常使用了。链接为https://download3.vmware.com/software/wkst/file/VMware-Workstation-Full-15.5.1-15018445.x86_64.bundle。可以用迅雷或者谷歌自带下载工具下载,也可以用命令行wget命令下载。  这是一个可执行程序文件,在命令行中对此文件,加执行权限,执行1./VMware-Workstation-Full-15.5.1-15018445.x86_64.bundle   安装完毕VMware后,找到window的iso镜像包(要是没有,提前去下载一个)正常安装window系统即可。至少分配4个G内存,开启3D选项(一般是默认开启的)。 可能遇到的问题3D支持  开机之后很有可能会vmware的又下角会报错,提示3D不可用  需要我们进入虚拟机的安装目录,找到vmx文件,vim打开编辑,在最后加一行mks.gl.allowBlacklistedDrivers = "TRUE"来强制开启3D加速。再开启虚拟机,就不会报No 3D support is available from host了。如果报mks.gl.allowBlacklistedDrivers之类的错误,可能是引号格式不正确,重新检查修改一下引号就可以了。 game_error_directx  当我下载好wegame,安装好英雄联盟客户端,也能正常组队及开始游戏时,载入进度条到100%后,意外给我弹一个这个报错。  然后游戏也无法重连了,一直报错game_error_directx。  这上来就坑队友了啊,我也很无奈。赶紧用directx修复工具修复一下directx,果然可以进入游戏了。(首先确保第一步的3D支持已开启,否则单独用修复工具修复是无效的) 卡顿  进入游戏后,我发现虽然可以流畅游戏,也没有延迟,问题是每隔几分钟就会有个10秒钟左右的画面卡顿,甚至掉线,然后屏幕就黑了。。。这谁顶得住啊。只能停下,赶紧排查原因。  开始以为是网络卡顿,不过检查了下,发现虚拟机的网络一直没有掉,于是猜测是虚拟机的CPU不响应,于是检测了下CPU的使用率,发现果然在卡顿的几秒钟内,宿主机的CPU使用率很高,难道是有进程占用了CPU,导致虚拟机的程序无法得到响应?  于是查找CPU优化,初步拟定方向是,将CPU核心绑定至虚拟机进程,修改虚拟机优先级等等。不过linux版的VMware没法设置优先级,并不像window版本的可以设置优先级。  于是,可优化选项,只有设置预留内存,以及不使用交换分区了。这时我注意到,默认是使用交换分区的,可能导致我不时卡顿的元凶就是这里了。不过无法直接修改内存选项,提示You must be running Workstation as root to change these preferences.。  于是,只能从命令行切换root用户权限执行vmware,修改后(将默认的Allow some virtual machine memory to be swapped改为第一个),在用普通用户启动vmware,发现果然是继续生效的。  再次启动虚拟机,进入游戏之后,果然再也没有出现卡顿了。 关闭工具条  在虚拟机中全屏之后,上面还隐藏一个白色的工具条,打游戏偶尔会点到就会切出去,很影响游戏体验。于是我们要想办法将他去掉。  在Edit,Preferences选项下的Display标签中,将全屏显示选项栏这个选项勾选取消。  现在就可以愉快的进行游戏了,你们能看出来我是在虚拟机中游戏吗","categories":[{"name":"故障记录","slug":"故障记录","permalink":"https://wudihechao.github.io/categories/故障记录/"}],"tags":[{"name":"linux","slug":"linux","permalink":"https://wudihechao.github.io/tags/linux/"},{"name":"vmware","slug":"vmware","permalink":"https://wudihechao.github.io/tags/vmware/"},{"name":"LOL","slug":"LOL","permalink":"https://wudihechao.github.io/tags/LOL/"}],"keywords":[{"name":"故障记录","slug":"故障记录","permalink":"https://wudihechao.github.io/categories/故障记录/"}]},{"title":"K8s进阶——java集群服务搭建","slug":"K8s进阶——java集群服务搭建","date":"2020-01-14T13:36:56.000Z","updated":"2020-01-19T16:28:02.041Z","comments":true,"path":"/blog/a9b6fadd.html","link":"","permalink":"https://wudihechao.github.io/blog/a9b6fadd.html","excerpt":"  K8s集群搭建完成后,真正完成我们业务的是那些跑在k8s上的pod们。将业务跑在k8s集群只上,我们可以实现根据负载或者资源利用率动态扩容或者缩容我们的后端服务器,更加灵活高效的利用我们的物理设备,且能够实现服务的高可用及故障自治愈,本文将详细介绍以上的具体实现。","text":"  K8s集群搭建完成后,真正完成我们业务的是那些跑在k8s上的pod们。将业务跑在k8s集群只上,我们可以实现根据负载或者资源利用率动态扩容或者缩容我们的后端服务器,更加灵活高效的利用我们的物理设备,且能够实现服务的高可用及故障自治愈,本文将详细介绍以上的具体实现。 实验环境  本次演示使用主机系统均为ubuntu1804。 节点 IP master节点 192.168.32.18、192.168.32.19 node节点 192.168.32.21、192.168.32.22 etcd 192.168.32.23、192.168.32.24、192.168.32.25 harbor 192.168.32.20 NFS服务器 192.168.32.20(复用)   如果机器不够,可以选择复用,例如将etcd的三台主机与master主机和harbor主机复用,可以省下来3台主机。推荐node节点的性能高一些,因为一般之后的pod都将运行在node节点上,如果node节点的资源不足,则很有可能无法创建pod,导致服务启动或者扩容失败。  具体K8s集群的搭建,可以参考我之前文章使用kubeasz自动化部署K8s。harbor服务器的搭建和部署配置,也可以参考我之前文章Docker(五)——Docker镜像仓库。  因为是均为内网环境,建议将网络组件calico的IPIP模式(ip-in-ip叠加模式)关掉,使用calico的BGP模式,以节约大量主机内部访问时封装的性能损耗。具体操作可以参考之前博客使用kubeasz自动化部署K8s。 设计架构  我们设计一个简单架构为例,如下图所示。  一般负载层是使用物理机上的haproxy服务来实现4层负载,而不是在K8s集群内部pod 实现。我们这里也就将他省略不再细说。所以我们需要创建一个nginx集群,以及一个tomcat集群。将动态资源,转发给后端的tomcat服务,前端的静态页面由nginx来处理。为了方便我们对后端服务的修改以及部署,我们还需要一个NFS服务器,来共享静态资源以及动态资源,以实现后端服务数据的实时同步。 搭建业务镜像架构  镜像一般是按层次一级一级实现的,可实现多个业务复用。所以,例如我们将要实现的业务为app1,则我们需要的的镜像,就有对应的app1-nginx及app1-tomcat的业务镜像,以及nginx及tomcat的基础镜像,以及基础系统镜像,我们要一层层的来制作镜像。详细可以参考之前文章Docker(三)——镜像制作。1234cd /optmkdir -p k8s/{app1,dockerfile,yaml}cd k8s/dockerfilemkdir {app1,pub-image,system}   最终结构如下图所示。123456789101112root@DockerUbuntu18:/opt/k8s/dockerfile# tree -d.├── app1│ ├── app1-nginx│ └── app1-tomcat├── pub-images│ ├── jdk│ │ └── 8│ ├── nginx-base│ └── tomcat-base└── system └── centos   打好的镜像如下所示。123456789root@DockerUbuntu18:/opt/k8s/dockerfile# docker imagesREPOSITORY TAG IMAGE ID CREATED SIZEharbor.micepro.net/base/app1-tomcat v1 c7d87d05f8a4 3 days ago 1.19GBharbor.micepro.net/base/tomcat-base v8.5.47 12cf3e12f2d0 3 days ago 1.19GBharbor.micepro.net/base/jdk-base v8.212 1582b1e9cfe4 3 days ago 1.16GBharbor.micepro.net/base/app1-nginx v1 4836f9aed79b 3 days ago 940MBharbor.micepro.net/base/nginx-base v1.16.1 bfc05a5c23ed 5 days ago 940MBharbor.micepro.net/base/centos-base v7.6 bc7e922a8352 5 days ago 753MBharbor.micepro.net/base/centos base f1cb7c7d58b7 10 months ago 202MB 配置NFS服务器  我们计划将动态资源和静态资源都放置至我们的NFS共享存储上,也就是我们的harbor上(任意主机,端口不冲突就可以~)  可以通过包管理工具快速安装一个NFS服务,毕竟这只是一个辅助工具,无需过多关注。1234apt install -y nfs-servermkdir /data/app1/{images,static,ROOT} -pecho \"/data/app1 *(rw,no_root_squash)\" >> /etc/exportssystemctl enable --now nfs-server   将/data/app1目录整个共享出去即可,之后我们可以直接将NFS挂载至pod中,也可以通过PV/PVC的方式挂载到pod中。  我们需要修改权限,通过指定uid的方式,使某个UID在多个主机之间都具有权限。这就要求我们之前在创建基础镜像时,也使用的是相同的UID启动程序。例如我们统一都使用www用户,指定UID为2020。(之前创建的容器以及基础镜像中都要修改服务以UID为2020的www用户启动) 编写yaml文件  此时我们就可以开始着手准备yaml文件,来规划我们的服务pod了。 namespace  首先我们需要新创建一个namespace,来实现与其他服务隔离。12cd /opt/k8s/mkdir -p yaml/namespace 12345vim yaml/namespace/app1.yamlapiVersion: v1 #API版本kind: Namespace #类型为namespacmetadata: #定义元数据 name: app1 #namespace名称   然后将这个namespace创建出来,使用命令1kubectl apply -f /opt/k8s/yaml/namespace/app1.yaml   此时使用命令kubectl get ns可以看到我们新创建的namespace1234567root@DockerUbuntu18:/opt/k8s# kubectl get nsNAME STATUS AGEapp1 Active 20sdefault Active 3dkube-node-lease Active 3dkube-public Active 3dkube-system Active 3d pod  先创建好对应的目录,方便后期管理维护。12cd /opt/k8smkdir -p app1/{nginx,tomcat}   然后我们就可以编写pod的yaml文件了。1cd /opt/k8s/app1/nginx   可以将Deployment和server写在一起,也可以分开写。我们这直接都写在一起。1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586vim nginx.yamlkind: DeploymentapiVersion: extensions/v1beta1metadata: labels: app: app1-nginx-deployment-label name: app1-nginx-deployment namespace: app1spec: replicas: 2 #设置nginx集群数量 selector: matchLabels: app: app1-nginx-selector template: metadata: labels: app: app1-nginx-selector spec: containers: - name: app1-nginx-container image: harbor.micepro.net/base/app1-nginx:v1 #command: [\"/apps/tomcat/bin/run_tomcat.sh\"] imagePullPolicy: IfNotPresent #imagePullPolicy: Always ports: - containerPort: 80 protocol: TCP name: http - containerPort: 443 protocol: TCP name: https env: - name: \"password\" value: \"123456\" - name: \"age\" value: \"18\" resources: limits: cpu: 2 memory: 2Gi requests: cpu: 500m# memory: 1Gi memory: 200m volumeMounts: - name: app1-images mountPath: /usr/local/nginx/html/webapp/images readOnly: false - name: app1-static mountPath: /usr/local/nginx/html/webapp/static readOnly: false volumes: - name: app1-images nfs: server: 192.168.32.20 path: /data/app1/images - name: app1-static nfs: server: 192.168.32.20 path: /data/app1/static---kind: ServiceapiVersion: v1metadata: labels: app: app1-nginx-service-label name: app1-nginx-service namespace: app1spec: type: NodePort ports: - name: http port: 80 protocol: TCP targetPort: 80 nodePort: 30080 - name: https port: 443 protocol: TCP targetPort: 443 nodePort: 30443 selector: app: app1-nginx-selector 1cd /opt/k8s/app1/tomcat   编写tomcat的pod的yaml文件123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172kind: DeploymentapiVersion: extensions/v1beta1metadata: labels: app: app1-tomcat-app1-deployment-label name: app1-tomcat-app1-deployment namespace: app1spec: replicas: 1 selector: matchLabels: app: app1-tomcat-app1-selector template: metadata: labels: app: app1-tomcat-app1-selector spec: containers: - name: app1-tomcat-app1-container image: harbor.micepro.net/base/tomcat-app1:v1 command: [\"/usr/local/tomcat/bin/run_tomcat.sh\"] #imagePullPolicy: IfNotPresent imagePullPolicy: Always ports: - containerPort: 8080 protocol: TCP name: http env: - name: \"password\" value: \"123456\" - name: \"age\" value: \"18\" resources: limits: cpu: 2 memory: \"1000Mi\" requests: cpu: 500m memory: \"200Mi\" volumeMounts: - name: app1-myapp mountPath: /data/tomcat/webapps/ROOT readOnly: false volumes: - name: app1-myapp nfs: server: 192.168.32.19 path: /data/app1/ROOT - name: app1-static nfs: server: 192.168.32.19 path: /data/app1/static---kind: ServiceapiVersion: v1metadata: labels: app: app1-tomcat-app1-service-label name: app1-tomcat-app1-service namespace: app1spec: type: NodePort ports: - name: http port: 80 protocol: TCP targetPort: 8080 nodePort: 38080 selector: app: app1-tomcat-app1-selector 修改配置文件  因为要实现一个通用的nginx+tomcat动静分离web架构,即用户访问的静态页面和图片在由nginx直接响应,而动态请求则基于tomcat的service name转发用户请求到tomcat业务app。  所以我们需要修改当时创建镜像的nginx的配置文件,配置upstream服务器为tomcat的pod,配置如下1234567891011upstream tomcat_webserver { server app1-tomcat-app1-service.app1.svc.app1.local:8080;}server { location /myapp { proxy_pass http://tomcat_webserver; proxy_set_header Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Real-IP $remote_addr; }}   其中pod间通信是基于K8s内部DNS实现的,一般是coreDNS或者kubeDNS,如果没有安装是无法解析的,可以使用kubeasz的第七个playbook安装,可参考之前文章使用kubeasz自动化部署K8s。  主机名默认组成结构为release-name-rancher.default.svc.cluster.local,也就是ServerName.NameSpace.svc.CLUSTER_DNS_DOMAIN。ServerName是我们在yaml文件中定义的,NameSpace是我们创建pod时选择的namespace,CLUSTER_DNS_DOMAIN是我们在创建K8s集群时设置的集群名,如果是用kubeasz工具创建的K8s集群,可以去/etc/ansible/hosts文件中查看。  我们可以在pod间测试通信,确保相互之间可以通过主机名通信。12345[root@blog-tomcat-app1-deployment-5b4845ddd8-rr5dq /]# ping app1-nginx-service.app1.svc.cluster.localPING app1-nginx-service.app1.svc.cluster.local (10.68.101.60) 56(84) bytes of data.64 bytes from app1-nginx-service.app1.svc.cluster.local (10.68.101.60): icmp_seq=1 ttl=64 time=0.103 ms64 bytes from app1-nginx-service.app1.svc.cluster.local (10.68.101.60): icmp_seq=2 ttl=64 time=0.314 ms64 bytes from app1-nginx-service.app1.svc.cluster.local (10.68.101.60): icmp_seq=3 ttl=64 time=0.077 ms   而且nginx及tomcat中最好将日志格式改为json格式,方便我们收集日志。  tomcat的配置文件/usr/local/tomcat/conf/server.xml,设置appBase="/data/tomcat/webapps/,日志格式修改如下123<Valve className=\"org.apache.catalina.valves.AccessLogValve\" directory=\"logs\" prefix=\"localhost_access_log\" suffix=\".txt\" pattern=\"{&quot;clientip&quot;:&quot;%h&quot;,&quot;ClientUser&quot;:&quot;%l&quot;,&quot;authenticated&quot;:&quot;%u&quot;,&quot;AccessTime&quot;:&quot;%t&quot;,&quot;method&quot;:&quot;%r&quot;,&quot;status&quot;:&quot;%s&quot;,&quot;SendBytes&quot;:&quot;%b&quot;,&quot;Query?string&quot;:&quot;%q&quot;,&quot;partner&quot;:&quot;%{Referer}i&quot;,&quot;AgentVersion&quot;:&quot;%{User-Agent}i&quot;}\" /> PV/PVC  我们还可以通过创建一个创建PV/PVC的方式的方式来代替直接NFS挂载Volume。  PersistentVolume(PV)是集群中由管理员配置的一段网络存储。 它是集群中的资源,就像节点是集群资源一样。 PV是容量插件,如Volumes,但其生命周期独立于使用PV的任何单个pod。 此API对象捕获存储实现的详细信息,包括NFS,iSCSI或特定于云提供程序的存储系统。  PersistentVolumeClaim(PVC)是由用户进行存储的请求。 它类似于pod。 Pod消耗节点资源,PVC消耗PV资源。Pod可以请求特定级别的资源(CPU和内存)。声明可以请求特定的大小和访问模式(例如,可以一次读/写或多次只读)。  PVC和PV是一一对应的。Persistent Volume相对独立于Pods,单独创建。  Persistent Volume对具体的存储进行配置和分配,而Pods等则可以使用Persistent Volume抽象出来的存储资源,不需要知道集群的存储细节。  Persistent Volume和Persistent Volume Claim类似Pods和Nodes的关系,创建Pods需要消耗一定的Nodes的资源。而Persistent Volume则是提供了各种存储资源,而Persistent Volume Claim提出需要的存储标准,然后从现有存储资源中匹配或者动态建立新的资源,最后将两者进行绑定。  以我们这次的演示为例,PV和PVC如下所示。  PV的yaml文件示例12345678910111213apiVersion: v1kind: PersistentVolumemetadata: name: nfsspec: storageClassName: manual capacity: storage: 10Gi accessModes: - ReadWriteMany nfs: server: 192.168.32.19 path: \"/data/app1/static\"   PVC的yaml文件示例1234567891011apiVersion: v1kind: PersistentVolumeClaimmetadata: name: nfsspec: accessModes: - ReadWriteMany storageClassName: manual resources: requests: storage: 10Gi   pod中引用PVC1234567891011121314151617181920apiVersion: v1kind: Podmetadata: name: testpv labels: role: web-frontendspec: containers: - name: web image: nginx ports: - name: web containerPort: 80 volumeMounts: - name: nfs mountPath: \"/usr/local/nginx/html/webapp/static\" volumes: - name: nfs persistentVolumeClaim: claimName: nfs HPA  虽然我们可以使用命令kubectl scale对运行在k8s 环境中的pod 数量进行扩容(增加)或缩容(减小)。123456789101112root@DockerUbuntu18:~# kubectl --help | grep -w scale scale Set a new size for a Deployment, ReplicaSet, Replication Controller, or Jobroot@DockerUbuntu18:~# kubectl get deployments -n app1NAME READY UP-TO-DATE AVAILABLE AGEapp1-nginx-deployment 1/1 1 1 9dapp1-tomcat-deployment 1/1 1 1 3d4hroot@DockerUbuntu18:~# kubectl scale deployment/app1-tomcat-deployment --replicas=2 -n app1deployment.extensions/app1-tomcat-deployment scaledroot@DockerUbuntu18:~# kubectl get deployments -n app1NAME READY UP-TO-DATE AVAILABLE AGEapp1-nginx-deployment 1/1 1 1 9dapp1-tomcat-deployment 2/2 2 2 74s   不过在实际生产中,我们肯定没法随时根据负载或者服务需要,手动动态伸缩我们的后端服务器数量,但是我们可以通过命令kubectl autoscale自动控制在k8s集群中运行的pod数量(水平自动伸缩),想要实现自动上伸缩,需要我们提前设置pod范围及触发条件。  k8s从1.1版本开始增加了名称为HPA(Horizontal Pod Autoscaler)的控制器,用于实现基于pod中资源(CPU/Memory)利用率进行对pod的自动扩缩容功能的实现,早期的版本只能基于Heapster组件实现对CPU利用率做为触发条件,但是在k8s 1.11版本开始使用Metrices Server完成数据采集,然后将采集到的数据通过API(Aggregated API,汇总API),例如metrics.k8s.io、custom.metrics.k8s.io、external.metrics.k8s.io,然后再把数据提供给HPA控制器进行查询,以实现基于某个资源利用率对pod进行扩缩容的目的。  控制管理器默认每隔15s(可以通过–horizontal-pod-autoscaler-sync-period修改)查询metrics的资源使用情况支持以下三种metrics指标类型:  ->预定义metrics(比如Pod的CPU)以利用率的方式计算  ->自定义的Pod metrics,以原始值(raw value)的方式计算  ->自定义的object metrics支持两种metrics查询方式:  ->Heapster  ->自定义的REST API支持多metrics (未完待续)","categories":[{"name":"Cloud","slug":"Cloud","permalink":"https://wudihechao.github.io/categories/Cloud/"}],"tags":[{"name":"Dockerfile","slug":"Dockerfile","permalink":"https://wudihechao.github.io/tags/Dockerfile/"},{"name":"kubernetes","slug":"kubernetes","permalink":"https://wudihechao.github.io/tags/kubernetes/"},{"name":"yaml","slug":"yaml","permalink":"https://wudihechao.github.io/tags/yaml/"},{"name":"集群","slug":"集群","permalink":"https://wudihechao.github.io/tags/集群/"},{"name":"java","slug":"java","permalink":"https://wudihechao.github.io/tags/java/"}],"keywords":[{"name":"Cloud","slug":"Cloud","permalink":"https://wudihechao.github.io/categories/Cloud/"}]},{"title":"企业级应用——ELK(三):filebeat","slug":"企业级应用——ELK(三):filebeat","date":"2020-01-06T09:47:24.000Z","updated":"2020-01-06T12:10:25.888Z","comments":true,"path":"/blog/bd240ad7.html","link":"","permalink":"https://wudihechao.github.io/blog/bd240ad7.html","excerpt":"  提到ELK,就不得不提到EFK,通常意义上说,EFK是指用filebeat代替logstash形成的新组合。(哈,也有是指Fluentd的,这个我们之后再说)  Filebeat 是基于原先 logstash-forwarder 的源码改造出来的,无需依赖 Java 环境就能运行,安装包10M不到。  而且如果日志的量很大,Logstash 会遇到资源占用高的问题,为解决这个问题,我们引入了Filebeat。Filebeat 是基于 logstash-forwarder 的源码改造而成,用 Golang 编写,无需依赖 Java 环境,效率高,占用内存和 CPU 比较少,非常适合作为 Agent 跑在服务器上,来实现日志转发的功能。","text":"  提到ELK,就不得不提到EFK,通常意义上说,EFK是指用filebeat代替logstash形成的新组合。(哈,也有是指Fluentd的,这个我们之后再说)  Filebeat 是基于原先 logstash-forwarder 的源码改造出来的,无需依赖 Java 环境就能运行,安装包10M不到。  而且如果日志的量很大,Logstash 会遇到资源占用高的问题,为解决这个问题,我们引入了Filebeat。Filebeat 是基于 logstash-forwarder 的源码改造而成,用 Golang 编写,无需依赖 Java 环境,效率高,占用内存和 CPU 比较少,非常适合作为 Agent 跑在服务器上,来实现日志转发的功能。  还是去官网下载https://www.elastic.co/cn/downloads/beats/filebeat。本次演示还是以最新版的filebeat7.5.1为例(以前版本的filebeat配置文件格式参数上可能有一些改变,不过大同小异)。123cd /usr/local/src/wget https://artifacts.elastic.co/downloads/beats/filebeat/filebeat-7.5.1-amd64.debdpkg -i filebeat-7.5.1-amd64.deb 基础配置  配置文件也很简单,如果想要写入文件,则配置如下 1234567891011grep -v \"#\" /etc/filebeat/filebeat.yml | grep -v \"^$\"filebeat.inputs:- type: log paths: - /var/log/syslog exclude_lines: [\"^DBG\"] exclude_files: [\".gz$\"] tags: \"syslog-filebeat\"output.file: path: \"/tmp\" filename: \"filebeat.txt\"   paths路径支持正则通配符写法,exclude是设置不匹配的文件格式。而且filebeat也支持同时从多个路径收集写成如下配置 12345678910111213141516filebeat.inputs:- type: log paths: - /var/log/syslog exclude_lines: [\"^DBG\"] exclude_files: [\".gz$\"] tags: \"syslog-filebeat\"- type: log paths: - /var/log/nginx/access.log exclude_lines: [\"^DBG\"] exclude_files: [\".gz$\"] document_type: \"nginx-accesslog-filebeatoutput.file: path: \"/tmp\" filename: \"filebeat.txt\"   同样,filebeat支持写入redis和kafka 12345678910111213filebeat.inputs:- type: log paths: - /var/log/syslog exclude_lines: [\"^DBG\"] exclude_files: [\".gz$\"] tags: \"filebeat-redis-syslog\"output.redis: hosts: [\"192.168.32.31:6379\"] key: \"filebeat-system-log\" #为了后期日志处理,建议自定义 key 名称 db: 1 #使用第几个库 timeout: 5 #超时时间 password: 123456 #redis 密码   想要写入kafka则添加output插件,配置如下123456789101112131415filebeat.inputs:- type: log paths: - /var/log/syslog exclude_lines: [\"^DBG\"] exclude_files: [\".gz$\"] tags: \"filebeat-kafka-syslog\"output.kafka: #写入 kafka hosts: [\"192.168.15.11:9092\",\"192.168.15.12:9092\",\"192.168.15.13:9092\"] topic: \"systemlog-1512-filebeat\" partition.round_robin: reachable_only: true required_acks: 1 #本地写入完成 compression: gzip #开启压缩 max_message_bytes: 1000000 #消息最大值 配置详解input  也就是设置日志收集的来源,需要的属性有type,path,根据官方文档,现在版本常用写法为,1234567891011filebeat.inputs:- type: log paths: - /var/log/system.log - /var/log/wifi.log- type: log paths: - \"/var/log/apache2/*\" fields: apache: true fields_under_root: true   其中type的类型很多 Log 日志文件,必须有PATH,官方示例如下: 123456789101112filebeat.inputs:- type: log paths: - /var/log/system.log - /var/log/wifi.log - /var/log/*.log- type: log paths: - \"/var/log/apache2/*\" fields: apache: true fields_under_root: true Stdin 标准输入,没有PATH,官方示例如下: 12filebeat.inputs:- type: stdin Container 容器中日志,必须有PATH,官方示例如下: 1234filebeat.inputs:- type: container paths: - '/var/lib/docker/containers/*/*.log' Kafka 从kafka中读取数据,官方示例如下: 123456789filebeat.inputs:- type: kafka hosts: [\"<your event hub namespace>.servicebus.windows.net:9093\"] topics: [\"<your event hub instance>\"] group_id: \"<your consumer group>\" username: \"$ConnectionString\" password: \"<your connection string>\" ssl.enabled: true Redis 从redis中读取数据,官方示例如下: 1234filebeat.inputs:- type: redis hosts: [\"localhost:6379\"] password: \"${redis_pwd}\" UDP 开放UDP端口来接受数据,可设置单条最大数据上限,不定义默认为20MiB。 1234filebeat.inputs:- type: udp max_message_size: 10KiB host: \"localhost:8080\" Docker 也支持直接从容器中读取数据, containers.ids是必须定义说明,可以用*代表所有容器。 1234filebeat.inputs:- type: docker containers.ids: - '8b6fe7dc9e067b58476dc57d6986dd96d7100430c5de3b109a99cd56ac655347' TCP 用法与UDP相同,设置监听的主机和端口。 1234filebeat.inputs:- type: tcp max_message_size: 10MiB host: \"localhost:9000\" Syslog 监听系统日志,指定传输协议即可。类似TCP和UDP。 1234filebeat.inputs:- type: syslog protocol.udp: host: \"localhost:9000\" 1234filebeat.inputs:- type: syslog protocol.tcp: host: \"localhost:9000\" s3 AWS的对象存储日志,不过多介绍 NetFlow Cisco设备网络设备的日志,不过多介绍 Google Pub/Sub google云的订阅发布模式协议数据,不过多介绍。   一般来说,我们都写log,就可以满足我们绝大多数场景的使用了。除了type、path这俩常用的input属性外,还有两个设置属性,我们也经常会用到,就是include_lines、exclude_lines,顾名思义,就是包括和排除,配合path中的通配符,可以帮助我们更灵活的指定要收集的日志文件。  还有一个很常用的属性就是tags,可以写多个,用[]括起来就可以。因为在filebeat中因为有自带type关键字,所以我们在之后筛选日志的时候,无法通过type字段来区分不同的日志源了,所以我们可以通过自定义tags字段,来实现之前在logstash上type的功能,这样在我们收集到的日志中,会自动加入tags 标签属性,然后通过logstash的筛选时,就可以对tags关键字做判断了。 output  输出选项有Elasticsearch、Logstash、Kafka、Redis、File、Console、Elastic Cloud。 File输出到文件中是最简单的设置了,一般用于测试。 123456output.file: path: \"/tmp/filebeat\" #输出文件路径 filename: filebeat #输出日志名称,超过大小限制后会自动添加数字后缀 #rotate_every_kb: 10000 #每个日志文件大小限制 #number_of_files: 7 #路径下最大的储存日志文件数量,超过此值后自动删除最早的日志文件,默认为7。 #permissions: 0600 #创建的日志文件的权限 Logstashfilebeat支持直接将数据输出值logstash主机。 12output.logstash: hosts: [\"127.0.0.1:5044\"]   而logstash主机需要设置输入为beats,才可以顺利接收filebeat的数据。123456789101112input { beats { port => 5044 }}output { elasticsearch { hosts => [\"http://localhost:9200\"] index => \"%{[@metadata][beat]}-%{[@metadata][version]}\" }} redis输出值redis,上面有说明,这里就不详细介绍了。 123456output.redis: hosts: [\"localhost\"] password: \"my_password\" key: \"filebeat\" db: 0 timeout: 5 kafka输出至kafka。 123456789101112output.kafka: # initial brokers for reading cluster metadata hosts: [\"kafka1:9092\", \"kafka2:9092\", \"kafka3:9092\"] # message topic selection + partitioning topic: '%{[fields.log_topic]}' partition.round_robin: reachable_only: false required_acks: 1 compression: gzip max_message_bytes: 1000000   也可以输出至kafka的不同的topic中 Rule settings:topic The topic format string to use. If this string contains field references, such as %{[fields.name]}, the fields must exist, or the rule fails.mappings A dictionary that takes the value returned by topic and maps it to a new >name.default The default string value to use if mappings does not find a match.when A condition that must succeed in order to execute the current rule. All the conditions supported by processors are also supported here.   官方示例如下:12345678910output.kafka: hosts: [\"localhost:9092\"] topic: \"logs-%{[beat.version]}\" topics: - topic: \"critical-%{[beat.version]}\" when.contains: message: \"CRITICAL\" - topic: \"error-%{[beat.version]}\" when.contains: message: \"ERR\" Elaticsearch可以直接将数据输出给elaticsearch服务器,不过一般来说我们不会这样做,一般是会经过logstash来筛选之后再传入elaticsearch。官方示例如下: 1234output.elasticsearch: hosts: [\"https://localhost:9200\"] username: \"filebeat_internal\" password: \"YOUR_PASSWORD\" Console输出至屏幕终端显示。pretty官方的介绍为If pretty is set to true, events written to stdout will be nicely formatted. The default is false,示例如下: 12output.console: pretty: true","categories":[{"name":"linux进阶","slug":"linux进阶","permalink":"https://wudihechao.github.io/categories/linux进阶/"}],"tags":[{"name":"企业级应用","slug":"企业级应用","permalink":"https://wudihechao.github.io/tags/企业级应用/"},{"name":"ELK","slug":"ELK","permalink":"https://wudihechao.github.io/tags/ELK/"},{"name":"Elasticsearch","slug":"Elasticsearch","permalink":"https://wudihechao.github.io/tags/Elasticsearch/"},{"name":"Logstash","slug":"Logstash","permalink":"https://wudihechao.github.io/tags/Logstash/"},{"name":"Filebeat","slug":"Filebeat","permalink":"https://wudihechao.github.io/tags/Filebeat/"}],"keywords":[{"name":"linux进阶","slug":"linux进阶","permalink":"https://wudihechao.github.io/categories/linux进阶/"}]},{"title":"企业级应用——ELK(二):ELK进阶","slug":"企业级应用——ELK(二):ELK进阶","date":"2020-01-03T14:30:18.000Z","updated":"2020-01-06T12:10:25.886Z","comments":true,"path":"/blog/ee80297c.html","link":"","permalink":"https://wudihechao.github.io/blog/ee80297c.html","excerpt":"  之前我们部署好了ELK的基本架构,也实现了从系统日志以及nginx中收集日志,不过等待我们的问题依然很多:怎么讲收集好的日志放至临时缓存?或者怎么从缓存中提取日志?对于java应用等日志非单行的服务日志该如何收集等等。本文将继续讲解ELK的各种进阶用法。","text":"  之前我们部署好了ELK的基本架构,也实现了从系统日志以及nginx中收集日志,不过等待我们的问题依然很多:怎么讲收集好的日志放至临时缓存?或者怎么从缓存中提取日志?对于java应用等日志非单行的服务日志该如何收集等等。本文将继续讲解ELK的各种进阶用法。 收集tomcat日志  收集tomcat中的日志比较简单,跟nginx一样,将日志序列化为json格式即可。  修改tomcat配置文件,将日志格式修改为如下格式。1vim /usr/local/tomcat/conf/server.xml 123<Valve className=\"org.apache.catalina.valves.AccessLogValve\" directory=\"logs\" prefix=\"localhost_access_log\" suffix=\".txt\" pattern=\"{&quot;clientip&quot;:&quot;%h&quot;,&quot;ClientUser&quot;:&quot;%l&quot;,&quot;authenticated&quot;:&quot;%u&quot;,&quot;AccessTime&quot;:&quot;%t&quot;,&quot;method&quot;:&quot;%r&quot;,&quot;status&quot;:&quot;%s&quot;,&quot;SendBytes&quot;:&quot;%b&quot;,&quot;Query?string&quot;:&quot;%q&quot;,&quot;partner&quot;:&quot;%{Referer}i&quot;,&quot;AgentVersion&quot;:&quot;%{User-Agent}i&quot;}\" />   此时查看新生成的访问日志,即可看到新生成的日志已经成json格式了。1tail -f /usr/local/tomcat/logs/localhost_access_log.2020-01-03.txt 1{\"clientip\":\"192.168.32.1\",\"ClientUser\":\"-\",\"authenticated\":\"-\",\"AccessTime\":\"[03/Jan/2020:19:34:29 +0800]\",\"method\":\"GET /bg-button.png HTTP/1.1\",\"status\":\"304\",\"SendBytes\":\"-\",\"Query?string\":\"\",\"partner\":\"http://192.168.32.51:8080/tomcat.css\",\"AgentVersion\":\"Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:69.0) Gecko/20100101 Firefox/69.0\"}   不过为了以防万一,可以去网上使用在线json格式校验工具,检查一下格式是否正确。  此时就可以使用logstash工具来收集我们的tomcat日志了。1vim /etc/logstash/conf.d/tomcat-el.conf   不过,想必你们一定也发现了,tomcat访问日志中是带有日期格式的,每天的访问日志文件名是不同的,这要怎么写到path中呢?  哈,别慌,logstash的input-file插件也支持通配符的写法,我们可以写path => "/usr/local/tomcat/logs/localhost_access_log.*.txt",如下所示。1234567891011121314151617181920input { file { path => \"/usr/local/tomcat/logs/localhost_access_log.*.txt\" stat_interval => 3 start_position => \"beginning\" codec => \"json\" type => \"tomcat_accesslog\" }}output {# stdout {# codec => rubydebug# } if [type] == \"tomcat_accesslog\" { elasticsearch { hosts => [\"192.168.32.41:9200\"] index => \"tomcat_accesslog-%{+YYYY.MM.dd}\" }}}   PS:可以分开至不同的配置文件,也可以合并至一个文件中,不过我们习惯不同服务用单独的配置文件来抓取,方便修改。  重启logstash服务,然后去kibana中创建tomcat_accesslog-*的索引,就可以看到tomcat的访问日志了。 收集JAVA服务的日志  JAVA服务中的日志如果不报错还好,还可以是一行一条,但是一旦出现报错信息,一般都是小半屏幕N多行的报错信息,而我们收集日志的很重要的目的就是为了查看这些报错信息,而如果不对这些日志进行处理,还按照一行一条来收集的话,当我们查看这个日志的时候就会很崩溃了——这乱的几乎完全没法看。所以我们要想办法将JAVA服务的日志处理成一行。  刚好强大的logstash也支持换行匹配,我们刚好就以logstash服务本身日志为例1vim /etc/logstash/conf.d/java-el.conf 123456789101112131415161718192021input { file { path => \"/logstash/logs/logstash-plain.log\" stat_interval => 3 start_position => \"beginning\" type => \"java_accesslog\" codec => multiline { pattern => \"^\\[\" #当遇到[开头的行时候将多行进行合并 negate => true #true 为匹配成功进行操作, false 为不成功进行操作 what => \"previous\" #与以前的行合并,如果是下面的行合并就是 next }}}filter { #日志过滤,如果所有的日志都过滤就写这里,如果只针对某一个过滤就写在 input 里面的日志输入里面}output { if [type] == \"java_accesslog\" { elasticsearch { hosts => [\"192.168.32.41:9200\"] index => \"java_accesslog-%{+YYYY.MM.dd}\" }}}   重启logstash服务,然后去kibana中创建java_accesslog-*的索引,就可以看到java的访问日志了。 redis  从redis读取日志和将收集到的日志储存至redis缓存中,其实使用了input的redis插件和output的redis插件来实现。官方文档地址为: https://www.elastic.co/guide/en/logstash/current/plugins-outputsredis.html 写入redis  我们还以tomcat日志及java日志logstash日志本身为例,写入redis缓存中。  先配置好redis服务器如下:123IP:192.168.32.31PORT:6379auth:123456 12345678910111213141516171819202122232425262728293031323334353637383940input { file { path => \"/usr/local/tomcat/logs/localhost_access_log.*.txt\" stat_interval => 3 start_position => \"beginning\" codec => \"json\" type => \"tomcat_redis_accesslog\" } file { path => \"/logstash/logs/logstash-plain.log\" stat_interval => 3 start_position => \"beginning\" type => \"java_redis_accesslog\" codec => multiline { pattern => \"^\\[\" #当遇到[开头的行时候将多行进行合并 negate => true #true 为匹配成功进行操作, false 为不成功进行操作 what => \"previous\" #与以前的行合并,如果是下面的行合并就是 next }}}output { if [type] == \"tomcat_redis_accesslog\" { redis { data_type => \"list\" key => \"tomcat_redis_accesslog\" host => \"192.168.32.31\" port => \"6379\" db => \"0\" password => \"123456\" }} if [type] == \"java_redis_accesslog\" { redis { data_type => \"list\" key => \"java_redis_accesslog\" host => \"192.168.32.31\" port => \"6379\" db => \"1\" password => \"123456\" }}}   将日志数据以列表的形式储存在redis中,多个不同的日志,储存在不同数据库中。 读取redis  与写入用法大致相同,可以说是怎么写进去的就怎么读出来。形式如下1234567891011121314151617181920212223242526272829303132input { redis { data_type => \"list\" key => \"tomcat_redis_accesslog\" host => \"192.168.32.31\" port => \"6379\" db => \"0\" password => \"123456\" codec => \"json\" }} redis { data_type => \"list\" key => \"java_redis_accesslog\" host => \"192.168.32.31\" port => \"6379\" db => \"1\" password => \"123456\" codec => \"json\" }}}output { if [type] == \"tomat_redis_accesslog\" { elasticsearch { hosts => [\"192.168.32.41:9200\"] index => \"tomcat_redis_accesslog-%{+YYYY.MM.dd}\" }} if [type] == \"java_redis_accesslog\" { elasticsearch { hosts => [\"192.168.32.41:9200\"] index => \"java_redis_accesslog-%{+YYYY.MM.dd}\" }}}   不过需要注意的是input中记得加codec => "json",否则无法解析正确各项属性,不方便我们查看和统计日志。而且不知道大家有没有发现,当我们从redis中取出数据的时候,我们的input选项中没有像往常那样写上type => "tomcat_redis_accesslog",而是直接在output中做了type的判断。这是因为,redis中的数据是在写入的时候,已经附加了type属性,它在redis中储存时还是会保留type属性的,所以取出来的时候,还是按照之前写入时的type类型取出即可。 kafka  在很多大型互联网公司,都喜欢使用kafka来作为缓存层,因为redis虽然效率很高,但数据不如kafka可靠,kafka更适合大数据量场景使用。这就要视业务实际情况而定了。不过使用kafa来作为中间缓冲区的企业还是大有人在的。  我们可以新开启一个kafka主机,IP为192.168.32.36,使用默认端口9092。kafak的使用方法与redis大致相同。配置文件示例如下1234567891011121314151617input { file { path => \"/apps/nginx/logs/access_json.log\" stat_interval => 3 start_position => \"beginning\" codec => \"json\" type => \"nginx_kafka_accesslog\" }}output { if [type] == \"nginx_kafka_accesslog\" { kafka { bootstrap_servers => \"192.168.32.36:9092\" #kafka 服务器地址 topic_id => \"nginx_kafka_accesslog\" codec => \"json\" } }}   与写入redis有些区别的就是,kafka中不是key了,而是topic_id。所以读取时也有一些区别,填写的关键字为topics,而且同样做判断时,type是当初写进去的那个属性。12345678910111213141516input { kafka { bootstrap_servers => \"192.168.32.36:9092\" topics => \"nginx_kafka_accesslog\" codec => \"json\" consumer_threads => 1 #线程数,默认值为1,一般设置与分区数量相同 #decorate_events => true #不写默认是关闭的,开启此属性可以将当前topic、offset、group、partition等信息也带到message中 }}output { if [type] == \"nginx_kafka_accesslog\" { elasticsearch { hosts => [\"192.168.32.41:9200\"] index => \"nginx_kafka_accesslog-%{+YYYY.MM.dd}\" }}}   kafka也支持多文件同时写入,设置不同的topic_id就可以了。","categories":[{"name":"linux进阶","slug":"linux进阶","permalink":"https://wudihechao.github.io/categories/linux进阶/"}],"tags":[{"name":"企业级应用","slug":"企业级应用","permalink":"https://wudihechao.github.io/tags/企业级应用/"},{"name":"ELK","slug":"ELK","permalink":"https://wudihechao.github.io/tags/ELK/"},{"name":"Elasticsearch","slug":"Elasticsearch","permalink":"https://wudihechao.github.io/tags/Elasticsearch/"},{"name":"Logstash","slug":"Logstash","permalink":"https://wudihechao.github.io/tags/Logstash/"},{"name":"Kibana","slug":"Kibana","permalink":"https://wudihechao.github.io/tags/Kibana/"}],"keywords":[{"name":"linux进阶","slug":"linux进阶","permalink":"https://wudihechao.github.io/categories/linux进阶/"}]},{"title":"企业级应用——ELK(一):ELK的部署","slug":"企业级应用——ELK(一):ELK的部署","date":"2020-01-03T10:00:54.000Z","updated":"2020-01-06T12:10:25.882Z","comments":true,"path":"/blog/a1b53f63.html","link":"","permalink":"https://wudihechao.github.io/blog/a1b53f63.html","excerpt":"  ELK是Elasticsearch、Logstash、Kibana的简称,这三者是核心套件,但并非全部。  Elasticsearch是实时全文搜索和分析引擎,提供搜集、分析、存储数据三大功能;是一套开放REST和JAVA API等结构提供高效搜索功能,可扩展的分布式系统。它构建于Apache Lucene搜索引擎库之上。  Logstash是一个用来搜集、分析、过滤日志的工具。它支持几乎任何类型的日志,包括系统日志、错误日志和自定义应用程序日志。它可以从许多来源接收日志,这些来源包括 syslog、消息传递(例如 RabbitMQ)和JMX,它能够以多种方式输出数据,包括电子邮件、websockets和Elasticsearch。  Kibana是一个基于Web的图形界面,用于搜索、分析和可视化存储在 Elasticsearch指标中的日志数据。它利用Elasticsearch的REST接口来检索数据,不仅允许用户创建他们自己的数据的定制仪表板视图,还允许他们以特殊的方式查询和过滤数据。","text":"  ELK是Elasticsearch、Logstash、Kibana的简称,这三者是核心套件,但并非全部。  Elasticsearch是实时全文搜索和分析引擎,提供搜集、分析、存储数据三大功能;是一套开放REST和JAVA API等结构提供高效搜索功能,可扩展的分布式系统。它构建于Apache Lucene搜索引擎库之上。  Logstash是一个用来搜集、分析、过滤日志的工具。它支持几乎任何类型的日志,包括系统日志、错误日志和自定义应用程序日志。它可以从许多来源接收日志,这些来源包括 syslog、消息传递(例如 RabbitMQ)和JMX,它能够以多种方式输出数据,包括电子邮件、websockets和Elasticsearch。  Kibana是一个基于Web的图形界面,用于搜索、分析和可视化存储在 Elasticsearch指标中的日志数据。它利用Elasticsearch的REST接口来检索数据,不仅允许用户创建他们自己的数据的定制仪表板视图,还允许他们以特殊的方式查询和过滤数据。 ELK架构  通常来说,只使用这三个组件就可以进行日志收集了,不过在企业实际生产中,需要用到ELK做集中日志收集的话,日志的产生量都是惊人的,所以通常情况下会需要缓存层来防止elasticsearch被压垮。架构如下图所示。(也可以通过filebeat来收集日志。) ELK的部署  需要注意的是,ELK的这三个组件版本要一致,否则可能会出现一些不必要的问题。我们这里选用最新版本7.5.1为例,演示主机均为ubuntu1804。 Elasticsearch  我们这里用两台主机来搭建一个elasticsearch集群,一般来说因为他的选举机制,elasticsearch集群都是3、5、7奇数个,不过2台主机也可以使用,我们这里节约主机使用两台主机做演示,IP分别为192.168.32.41、192.168.32.42。   官网下载链接为https://www.elastic.co/cn/downloads/elasticsearch。  我们这里选择deb安装包安装,也可以选用源码tar包自己解压安装。1wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-7.5.1-amd64.deb   这个版本的deb包是自带java环境(openjdk11)的,如果主机已经预制java环境,可以去官网下载no-java的版本,使用jdk8的时候有warning,说未来版本将不支持jdk8,建议使用jdk11及以上。1dpkg -i elasticsearch-7.5.1-amd64.deb   elasticsearch的配置文件路径为/etc/elasticsearch/elasticsearch.yml,需要修改的不多,将集群主机IP设置好就可以了,如下所示12345678910root@elasticsearch1:~# grep \"^[a-Z]\" /etc/elasticsearch/elasticsearch.ymlcluster.name: ELK-CLuster #集群名称,名称相同即属于是同一个集群node.name: node-1 #本机在集群内的节点名称path.data: /elasticsearch/datapath.logs: /elasticsearch/logsbootstrap.memory_lock: true #服务启动的时候锁定足够的内存, 防止数据写入swapnetwork.host: 0.0.0.0http.port: 9200discovery.seed_hosts: [\"192.168.32.41\",\"192.168.32.42\"]cluster.initial_master_nodes: [\"node-1\",\"node-2\"]   我这里是单独创建了一个日志路径和数据路径,方便管理,并修改属主赋予权限。12mkdir -p /elasticsearch/{data,logs}chown -R elasticsearch:elasticsearch /elasticsearch   除了在配置文件中设置bootstrap.memory_lock: true以外,还需要在启动配置文件中设置允许无限制使用内存,否则启动检查就会报错,导致服务起不来。12vim /usr/lib/systemd/system/elasticsearch.serviceLimitMEMLOCK=infinity   而且根据官方文档https://www.elastic.co/guide/en/elasticsearch/reference/current/heap-size.html,最大30G 以内的内存。  在一般使用中,使用最大内存和最小内存都设置为2G就可以了。123vim /etc/elasticsearch/jvm.options-Xms2g-Xmx2g   另一台主机也同样配置,记得修改node.name,之后就可以启动elasticsearch了。1systemctl enable --now elasticsearch   在任意主机使用curl命令可以检查集群的健康状态。1curl -sXGET http://192.168.32.41:9200/_cluster/health?pretty=true   获取到的是一个 json 格式的返回值,那就可以通过 python 对其中的信息进行分析,例如对 status 进行分析,如果等于 green(绿色)就是运行在正常,等于yellow(黄色)表示副本分片丢失, red(红色)表示主分片丢失。  至此,elasticsearch服务的部署就算是完成了。 Logstash  logstash也是一个基于java的插件式服务,很多功能都是依靠于插件来实现的,我们安装官方的安装包,大部分常用插件都是已经预置了,如果还有其他的功能需求,就需要去官网或者github下载插件了。这些之后再说,我们先去官网上将Logstash7.5.1安装包下载下来并部署上。1wget https://artifacts.elastic.co/downloads/logstash/logstash-7.5.1.deb   logstash也同样需要java环境12apt updateapt install openjdk-8-jdk   或者安装oracle的jdk,生产环境还是推荐使用oracle公司的jdk,更加稳定。然后安装logstash1dpkg -i logstash-7.5.1.deb   对于logstash的配置也很少,不做修改也可以使用,不过我们这里同样还是修改一下数据目录和日志目录123root@logstash1:~# grep \"^[a-Z]\" /etc/logstash/logstash.ymlpath.data: /logstash/datapath.logs: /logstash/logs   修改属主12mkdir -p /logstash/{data,logs}chown -R /logstash   logstash的默认执行程序路径为/usr/share/logstash/bin/logstash,这其实也是一个shell脚本文件,脚本中调用java的类库。12345678910111213141516171819202122232425262728293031root@logstash1:~# /usr/share/logstash/bin/logstash --helpThread.exclusive is deprecated, use Thread::MutexWARNING: Could not find logstash.yml which is typically located in $LS_HOME/config or /etc/logstash. You can specify the path using --path.settings. Continuing using the defaultsUsage: bin/logstash [OPTIONS]Options: -n, --node.name NAME Specify the name of this logstash instance, if no value is given it will default to the current hostname. (default: \"logstash1\") -f, --path.config CONFIG_PATH Load the logstash config from a specific file or directory. If a directory is given, all files in that directory will be concatenated in lexicographical order and then parsed as a single config file. You can also specify wildcards (globs) and any matched files will be loaded in the order described above. -e, --config.string CONFIG_STRING Use the given string as the configuration data. Same syntax as the config file. If no input is specified, then the following is used as the default input: \"input { stdin { type => stdin } }\" and if no output is specified, then the following is used as the default output: \"output { stdout { codec => rubydebug } }\" If you wish to use both defaults, please use the empty string for the '-e' flag. (default: nil)   不过常用的选项也就-e和-f,分别是通过命令行指定参数或者通过文件来指定配置参数。我们可以使用命令来测试1/usr/share/logstash/bin/logstash -e 'input { stdin{} } output { stdout{ codec => rubydebug }}'   通过标准输入输入信息,并通过标准输出返回日志信息。同样,我们也可以调用input的file插件和output的file插件实现从文件中读取数据,或者写入文件。这样就可以实现对日志文件的抓取了。我们可以先尝试抓取系统日志如syslog。1/usr/share/logstash/bin/logstash -e 'input { file { path => \"/var/log/syslog\"} } output { stdout{ codec => rubydebug }}'   哈,系统日志如果太多,估计会刷屏的。  不过刚才那些都只是一些基本用法而已,而实际生产中,我们肯定不能使用命令行来手动获取数据,我们需要的是一个可靠的服务,来帮我们自动抓取日志并筛选过滤,这就需要我们使用配置文件来设置了。  例如我们要做的抓取本机的nginx的访问日志、错误日志还有系统日志,并传递至之前配好的elasticsearch中。  那就在路径/etc/logstash/conf.d/目录下,创建一个新的配置文件,使用systemd启动时会自动读取conf.d下的配置文件。12345678910111213141516171819202122232425262728293031323334353637383940vim /etc/logstash/conf.d/nginx.confinput { file { path => \"/var/log/syslog\" stat_interval => 3 start_position => \"beginning\" type => \"syslog\" } file { path => \"/apps/nginx/logs/access_json.log\" stat_interval => 3 start_position => \"beginning\" codec => \"json\" type => \"nginx_accesslog\" } file { path => \"/apps/nginx/logs/error.log\" stat_interval => 3 start_position => \"beginning\" type => \"nginx_errorlog\" }}output { if [type] == \"syslog\" { elasticsearch { hosts => [\"192.168.32.41:9200\"] index => \"syslog-%{+YYYY.MM.dd}\" }} if [type] == \"nginx_accesslog\" { elasticsearch { hosts => [\"192.168.32.41:9200\"] index => \"nginx_accesslog-%{+YYYY.MM.dd} }} if [type] == \"nginx_errorlog\" { elasticsearch { hosts => [\"192.168.32.41:9200\"] index => \"nginx_accesslog-%{+YYYY.MM.dd} }}}   logstash支持条件判断,多输入以及多输出,设定type规则,来将每一类日志分类在不同的索引,且支持java的时间变量,可以实现根据日期归档每一天的日志,方便查看和统计。  我们可以使用命令来测试脚本的语法是否正确,如果不加-t可以直接以前台进程的方式启动logstash,不过会占据终端,但测试的时候还是蛮方便的。1/usr/share/logstash/bin/logstash -f /etc/log/logstash/conf,d/nginx.conf -t   不过仅仅是这样,是无法统计具体访问时间、访问ip及访问路径的详细信息的,我们需要将nginx的日志序列化,或者说是储存为json格式。  所以修改nginx的配置文件,将日志格式修改一下。1234567891011121314151617181920212223242526http { include mime.types; default_type application/octet-stream; #log_format main '$remote_addr - $remote_user [$time_local] \"$request\" ' # '$status $body_bytes_sent \"$http_referer\" ' # '\"$http_user_agent\" \"$http_x_forwarded_for\"'; log_format access_json '{\"@timestamp\":\"$time_iso8601\",' '\"host\":\"$server_addr\",' '\"clientip\":\"$remote_addr\",' '\"size\":$body_bytes_sent,' '\"responsetime\":$request_time,' '\"upstreamtime\":\"$upstream_response_time\",' '\"upstreamhost\":\"$upstream_addr\",' '\"http_host\":\"$host\",' '\"uri\":\"$uri\",' '\"domain\":\"$host\",' '\"xff\":\"$http_x_forwarded_for\",' '\"referer\":\"$http_referer\",' '\"tcp_xff\":\"$proxy_protocol_addr\",' '\"http_user_agent\":\"$http_user_agent\",' '\"status\":\"$status\"}'; access_log /apps/nginx/logs/access_json.log access_json;   PS:加入的属性名称不要有type,否则会影响到logstash做type判断。然后记得在配置文件中注明输入信息为json格式。看到如下信息,则说明日志被成功拆解。12345678910111213141516171819{ \"http_user_agent\" => \"Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:69.0) Gecko/20100101 Firefox/69.0\", \"path\" => \"/apps/nginx/logs/access_json.log\", \"@timestamp\" => 2020-01-03T08:17:47.000Z, \"upstreamhost\" => \"-\", \"xff\" => \"-\", \"responsetime\" => 0.0, \"size\" => 0, \"status\" => \"304\", \"http_host\" => \"192.168.32.51\", \"clientip\" => \"192.168.32.1\", \"domain\" => \"192.168.32.51\", \"tcp_xff\" => \"\", \"host\" => \"192.168.32.51\", \"@version\" => \"1\", \"uri\" => \"/index.html\", \"referer\" => \"-\", \"upstreamtime\" => \"-\"}   使用systemctl enable --now logstash启动logstash服务,过一会,日志就写入elasticsearch服务器中了,最好将/etc/systemd/system/logstash.service文件的中启动用户组都改为root,避免因为权限问题,导致无法读取数据。  我记得之前遇到过一次,命令行可以正常使用logstash,不过使用systemd启动就一直报错,logstash: could not find java; set JAVA_HOME or ensure java is in PATH,明明环境变量都是正常的,后来在/usr/share/logstash/bin/logstash脚本文件中加了一个JAVA_HOME=/usr/local/jdk环境变量就好了,而之后配的时候就没有遇到这个问题了,这就很奇怪了。 Kibana  日志信息都已经写到elasticsearch服务器中了,不过我们怎么才可以看到具体的日志信息呢?这就需要借助日志展示工具Kibana了。  虽然elasticsearch可视化工具也有不少,例如head、kopf、cerebro等等,不过他们都是监控elasticsearch集群状态的,对日志做展示分析的还是首推开源的官方组件Kibana。  同样下载kibana7.5.1版本1https://artifacts.elastic.co/downloads/kibana/kibana-7.5.1-amd64.deb   配置上也很简单,设置监听IP及端口,设置elasticsearch主机地址,最后再将语言环境改为中文就可以启动kibana服务了。12345root@kibana1:~# grep \"^[a-Z]\" /etc/kibana/kibana.ymlserver.port: 5601server.host: \"0.0.0.0\"elasticsearch.hosts: [\"http://192.168.32.41:9200\"]i18n.locale: \"zh-CN\"   浏览器访问kibana主机的5601端口。  创建索引模式,依次添加索引。  然后点左边第一个(最上面)的那个discover图标,就可以看到数据了。点击更改,选择对应的索引,还可以创建各种视图等一系列功能这里就不再详细讲解了。都比较简单。  至此ELK的初步部署就算完成了。","categories":[{"name":"linux进阶","slug":"linux进阶","permalink":"https://wudihechao.github.io/categories/linux进阶/"}],"tags":[{"name":"企业级应用","slug":"企业级应用","permalink":"https://wudihechao.github.io/tags/企业级应用/"},{"name":"ELK","slug":"ELK","permalink":"https://wudihechao.github.io/tags/ELK/"},{"name":"Elasticsearch","slug":"Elasticsearch","permalink":"https://wudihechao.github.io/tags/Elasticsearch/"},{"name":"Logstash","slug":"Logstash","permalink":"https://wudihechao.github.io/tags/Logstash/"},{"name":"Kibana","slug":"Kibana","permalink":"https://wudihechao.github.io/tags/Kibana/"}],"keywords":[{"name":"linux进阶","slug":"linux进阶","permalink":"https://wudihechao.github.io/categories/linux进阶/"}]},{"title":"企业级应用——DevOps(一)gitlab的部署与配置","slug":"企业级应用——DevOps(一)gitlab的部署与配置","date":"2019-12-26T14:28:34.000Z","updated":"2019-12-29T04:19:21.178Z","comments":true,"path":"/blog/73a033d9.html","link":"","permalink":"https://wudihechao.github.io/blog/73a033d9.html","excerpt":"  在企业生产中,DEVOPS这个概念越来越火了,不同公司对此都有不同的理解,但有一点毋庸置疑,提到DEVOPS都绕不开CI/CD。CI是continuous integration的简称,意为持续集成,CD是continuous deployment或者Continuous Delivery的缩写,意为持续部署或持续交付。","text":"  在企业生产中,DEVOPS这个概念越来越火了,不同公司对此都有不同的理解,但有一点毋庸置疑,提到DEVOPS都绕不开CI/CD。CI是continuous integration的简称,意为持续集成,CD是continuous deployment或者Continuous Delivery的缩写,意为持续部署或持续交付。  持续集成是指多名开发者在开发不同功能代码的过程当中,可以频繁的将代码行合并到一起并切相互不影响工作。  持续部署是指是基于某种工具或平台实现代码自动化的构建、测试和部署到线上环境以实现交付高质量的产品。持续部署在某种程度上代表了一个开发团队的更新迭代速率。  持续交付是在持续部署的基础之上, 将产品交付到线上环境, 因此持续交付是产品价值的一种交付, 是产品价值的一种盈利的实现。  可以用一个类似戴明环的图来比较形象展示这种生产模式结构。  比较常用到的开源软件有gitlab、Maven、jenkins、saltstack、slastic以及zabbix。其中gitlab和jenkins是最常用的一个组合,也是目前最为火热的结局方案。本文将详细介绍gitlab的部署及使用。 Gitlib—分布式版本控制系统持续集成开源工具  我们需要在公司的服务器安装某种程序,该程序用于按照特定格式和方式记录和保存公司多名开发人员不定期提交的源代码,且后期可以按照某种标记及方式对用户提交的数据进行还原,这是我们就需要用到版本控制系统,也就是所谓的持续集成工具。  早期的集中式版本控制系统如CVS(Concurrent Version System),现已基本淘汰,可能有些公司还在使用SVN(Subversion)作为版本控制系统,不过他的缺点相当明显,集中式管理,太依赖于网络带宽,当大家一起从管理服务器拉代码或提交代码时,遇到网速慢的话,可能提交一个10M的文件就需要5分钟,效率很差。  而gitlab是分布式的版本控制系统,不存在“中央服务器”,每个人的电脑上都是一个完整的版本库,这样,你工作需要提交或者回滚时,就不需要联网了,因为版本库就在你自己的电脑上。需要汇总时,我们将代码提交至gitlab服务器,将每个人的劳动成果也就是分支仓库的代码合并即可。 部署gitlab  具体安装要求及配置可以参考官方文档  https://about.gitlab.com/install/ # Gitlab 服务的安装文档  https://docs.gitlab.com/ce/install/requirements.html #安装环境要求   最好先配置好依赖仓库源,可以配置阿里的镜像仓库源。  官方下载deb安装包比较慢,可以使用清华大学的镜像源下载。   我们这里使用gitlab-ce_11.11.8来演示。也可以去下载其他版本,ubuntu 国内下载地址: https://mirrors.tuna.tsinghua.edu.cn/gitlab-ce/ubuntu/pool/。12wget https://mirrors.tuna.tsinghua.edu.cn/gitlab-ce/ubuntu/pool/bionic/main/g/gitlab-ce/gitlab-ce_11.11.8-ce.0_amd64.debdpkg -i gitlab-ce_11.11.8-ce.0_amd64.deb   安装可能需要一段时间,耐心等待。  然后修改gitlab的配置文件1234567891011121314root@gitlabUbuntu24:~# grep \"^[a-Z ]\" /etc/gitlab/gitlab.rbexternal_url 'http://192.168.32.24' gitlab_rails['gitlab_email_from'] = '[email protected]' gitlab_rails['smtp_enable'] = true gitlab_rails['smtp_address'] = \"smtp.qq.com\" gitlab_rails['smtp_port'] = 465 gitlab_rails['smtp_user_name'] = \"[email protected]\" gitlab_rails['smtp_password'] = \"keyword\" gitlab_rails['smtp_domain'] = \"qq.com\" gitlab_rails['smtp_authentication'] = \"login\" gitlab_rails['smtp_enable_starttls_auto'] = true gitlab_rails['smtp_tls'] = true user['git_user_email'] = \"example@#qq.com\" alertmanager['admin_email'] = '[email protected]'   将上面这些选项都设置好。例如使用的是QQ邮箱,则需要去邮箱设置里获取授权码,填在password处。因为gitlab是一般是通过邮件来注册和获取密码的,所以这些配置都要有,邮箱最好填企业邮箱或者公司领导邮箱,以避免员工离职等其他风险。然后使用命令让配置文件生效。1gitlab-ctl reconfigure   这时就可以通过命令gitlab-ctl start启动gitlab了。  可以通过gitlab-ctl status命令查看gitlab运行状态,或者后跟组件名称查看具体组件的运行状态。 gitlab 相关的目录:1234/etc/gitlab #配置文件目录/run/gitlab #运行 pid 目录/opt/gitlab #安装目录/var/opt/gitlab #数据目录 gitlab常用命令  gitlab用法与github几乎相同,这里就不再详细介绍。12345678910111213141516git config --global user.name “name“ #设置全局用户名git config --global user.email [email protected] #设置全局邮箱git config --global --list #列出用户全局设置git add index.html / . #添加指定文件、 目录或当前目录下所有数据到暂存区git commit -m “11“ #提交文件到工作区git status #查看工作区的状态git push #提交代码到服务器git pull #获取代码到本地git log #查看操作日志vim .gitignore #定义忽略文件git reset --hard HEAD^^ #git 版本回滚, HEAD 为当前版本,加一个^为上一个, ^^为上上一个版本git reflog # #获取每次提交的 ID,可以使用--hard 根据提交的 ID 进行版本回退git reset --hard 5ae4b06 #回退到指定 id 的版本git branch #查看当前所处的分支git checkout -b develop #创建并切换到一个新分支git checkout develop #切换分支 gitlab数据备份  gitlab可以通过命令gitlab-rake来备份数据。数据备份前,需要停止unicorn、sidekiq组件服务。1234gitlab-ctl stop unicorngitlab-ctl stop sidekiqgitlab-rake gitlab:backup:create #在任意目录即可备份当前 gitlab 数据gitlab-ctl start #备份完成后启动 gitlab   备份完成的数据储存在/var/opt/gitlab/backups/路径下,生成与时间相关的tar包。12345root@gitlabUbuntu24:~# ll /var/opt/gitlab/backups/total 4600drwx------ 2 git root 4096 Dec 25 14:41 ./drwxr-xr-x 20 root root 4096 Dec 25 11:02 ../-rw------- 1 git git 4700160 Dec 25 14:41 1577256108_2019_12_25_11.11.8_gitlab_backup.tar   需要注意的是,命令备份仅仅是备份了gitlab中的数据,通常我们我们的备份脚本中还需要备份以下文件。123/var/opt/gitlab/nginx/conf #nginx 配置文件/etc/gitlab/gitlab.rb #gitlab 配置文件/etc/gitlab/gitlab-secrets.json #key 文件   使用命令gitlab-rake gitlab:backup:restore BACKUP=备份文件名可以恢复之前的备份数据(文件名到时间戳即可)。同样,执行恢复命令之前也要先关闭unicorn、sidekiq组件服务。恢复时,需要输入yes确认。123gitlab-ctl stop unicorngitlab-ctl stop sidekiqgitlab-rake gitlab:backup:restore BACKUP=1577256108_2019_12_25_11.11.8 gitlab的关闭注册  服务正常启动之后,就可以访问主机的IP或者域名,登陆gitlab系统了。gitlab和github极为相似,也支持邮箱注册。,第一次访问,会放我们设置管理员账户密码,我们使用管理员账户登录,用户名为root。  gitlab默认是开放注册的,而我们作为企业内部使用的版本管理系统,肯定不希望大家随便创建用户来访问,这样很不便于管理。  我们可以在点击中间的小扳手图标,选择setting设置,将sign-up功能取消,这样登录界面就无法注册了。  注意,小心别关错了,要是把sign-in功能取消就没法登陆了,哪怕使用root权限账号退出去也就没法登陆了。如果真的不幸将gitlab的sign-in功能取消掉,那只能使用命令行的方式修改数据库中设置了。  因为gitlab目前版本默认使用的psql,所以修改数据库的方式,跟mysql不同,具体如下先切换至gitlab-psql用户。1su - gitlab-psql   使用 -h指定数据库路径 -d指定数据库,连接psql数据库。123psql -h /var/opt/gitlab/postgresql -d gitlabhq_productionUPDATE application_settings SET password_authentication_enabled_for_web=true;\\q   修改完数据后,重启gitlab服务。12exitgitlab-ctl restart   此时,web界面就又可以登陆了。 gitlab的中文汉化  gitlab默认语音为英语,有时有些专业数据或者不太常用的选项配置我们可能无法准确理解它的含义,所以将gitlab汉化成中文还是有一定必要的。  gitlab的每个用户,可以点击用户头像,设置,设置偏好,选择语音。  选择中文之后刷新,就可看到一部分菜单变为了中文。但是也仅仅是菜单变成了中文而已,很多选项和图标都还是英文显示,如果想彻底汉化为中文,则需要我们去下载中文汉化补丁了。  汉化补丁是第三方爱好者提供,github地址是https://gitlab.com/xhang/gitlab,找到对应版本的gitlab汉化包。  gitlabv11.11.8汉化包的下载地址如下1wget https://gitlab.com/xhang/gitlab/-/archive/v11.11.8-zh/gitlab-v11.11.8-zh.tar.gz   然后先停止gitlab1gitlab-ctl stop   备份并替换文件123tar xvf gitlab-v11.11.8-zh.tar.gzcp -rp /opt/gitlab/embedded/service/gitlab-rails /opt/gitlab-rails.bakcp -rf gitlab-v11.11.8-zh/* /opt/gitlab/embedded/service/gitlab-rails/   此时,汉化就完成了,可以直接启动gitlab了。就可以看到页面都变成了中文了,效果图如下。","categories":[{"name":"linux进阶","slug":"linux进阶","permalink":"https://wudihechao.github.io/categories/linux进阶/"}],"tags":[{"name":"企业级应用","slug":"企业级应用","permalink":"https://wudihechao.github.io/tags/企业级应用/"},{"name":"DevOps","slug":"DevOps","permalink":"https://wudihechao.github.io/tags/DevOps/"},{"name":"CI/CD","slug":"CI-CD","permalink":"https://wudihechao.github.io/tags/CI-CD/"},{"name":"gitlab","slug":"gitlab","permalink":"https://wudihechao.github.io/tags/gitlab/"}],"keywords":[{"name":"linux进阶","slug":"linux进阶","permalink":"https://wudihechao.github.io/categories/linux进阶/"}]},{"title":"企业级应用——监控(一)zabbix的部署","slug":"企业级应用——监控(一)zabbix的部署","date":"2019-12-20T09:34:00.000Z","updated":"2019-12-29T02:43:20.922Z","comments":true,"path":"/blog/366b680c.html","link":"","permalink":"https://wudihechao.github.io/blog/366b680c.html","excerpt":"  常见的开源监控软件有:cacti、nagios、zabbix、smokeping、open-falcon等,本文主要介绍目前使用较多的开源监控软件zabbix,针对容器环境的开源监控软件Prometheus下次再讲解。","text":"  常见的开源监控软件有:cacti、nagios、zabbix、smokeping、open-falcon等,本文主要介绍目前使用较多的开源监控软件zabbix,针对容器环境的开源监控软件Prometheus下次再讲解。  zabbix功能强大,可横向扩展、自定义监控项、支持多种监控方式、可监控网络与服务等。 zabbix功能简述 数据采集zabbix是周期性采集时序数据。  采集对象可以有:服务器、路由器、交换机、存储、防火墙、IP、PORT、URL、自定义监控对象…  采集目标:监控项,指标数据(metrics data)  数据采集方式:zabbix-server,zabbix-proxy,zabbix-agent  按照有无代理分类:    Agentless:SNMP,Telnet,ssh, IPMI, JMX,    Agent:zabbix agent 数据存储可以储存历史数据和局势数据,还有阈值。  历史数据: 每个监控项采集到的每个监控值  趋势数据: 趋势表里主要保留某个监控项一个小时内历史数据的最大值、最小值和平均值以及该监控项一个小时内所采集到的数据个数。  阈值:可按照预定义的阈值等级实现分层报警支持的数据库类型有:  SQL: MySQL/MariaDB(Zabbix)  NoSQL:Redis(Open-falcon)  rrd: Round Robin Database(Cacti) 数据展示可以使用原生的zabbix web界面可以展示graph -> screen -> slideshow(将多个screen以幻灯片的方式进行轮流展示)还支持以zabbix为数据源,在grafana展示更绚丽的界面。 报警通知支持email,短信,微信,语音等多种方式报警通知,也可以实现故障自治愈。host (host groups) items -> triggers -> action (条件-conditions, 操作-operations) #自定义告警配置zabbix架构  在zabbix服务中,一般都包含有zabbix-server、zabbix-agent、zabbix-proxy,及数据库,结构如下图所示:  一般都需要在被监控端安装zabbix-agent服务来抓取数据,然后汇总到zabbix-server 端来展示分析监控报警,如果agent过多或者可能不同机房的数据,可以通过zabbix-proxy来暂存收集数据,之后在汇总至zabbix-server端,所以zabbix-proxy端及zabbix-server 端都需要一个mysql数据库来储存即时及历史监控数据的(zabbix-proxy处临时储存)。而且整个体系中最重要的就是数据库了,数据都在数据库中,只要数据库中的数据不丢失,重建一个zabbix监控架构还是比较容易的,所以可以对数据库做主从复制高可用,可参见之前文章。zabbix部署zabbix server包管理工具安装  我们先来搭建zabbix-server端。  对于Ubuntu系统:先下载配置镜像仓库,并apt安装zabbix-server-mysql 、zabbix-frontend-php 及zabbix-agent,其中zabbix-server的二进制程序及启动配置文件都在在zabbix-server-mysql安装包里。1234wget https://repo.zabbix.com/zabbix/4.0/ubuntu/pool/main/z/zabbix-release/zabbix-release_4.0-3%2Bbionic_all.debdpkg -i zabbix-release_4.0-3+bionic_all.debapt updateapt -y install zabbix-server-mysql zabbix-frontend-php zabbix-agent   然后开始安装zabbix-server的数据库。可以与zabbix-server复用一台主机,也可以单独一台主机。我们这里使用一台新的主机,apt安装好mariadb(与mysql操作一样)。  先以root身份登陆mysql主机,然后为zabbix创建一个数据库,例如zabbix_server,再创建一个zabbix用户,并授权123MariaDB [mysql]> create database zabbix_server character set utf8 collate utf8_bin;MariaDB [mysql]>grant all privileges on zabbix_server.* to zabbix@\"192.168.32.%\" identified by \"zabbix\";MariaDB [mysql]>flush privileges;   然后回到zabbix-server主机,安装一个mysql客户端,并测试可否用之前创建的zabbix账号登录数据库。12apt mysql-client -ymysql -uzabbix -pzabbix -h192.168.32.20   确保可以登陆之后,导入数据库表结构1zcat /usr/share/doc/zabbix-server-mysql/create.sql.gz | mysql -uzabbix -pzabbix -h192.168.32.20 zabbix_server   此时在登入数据库服务器,使用命令查询,可以看到已经生成了很多表1mysql> show tables from zabbix_server;   然后编辑zabbix配置文件1vim /etc/zabbix/zabbix_server.conf   修改数据库相关信息,其他的可以不做修改。12345678910111213141516root@DockerUbuntu19:~# grep \"^[a-Z]\" /etc/zabbix/zabbix_server.confLogFile=/var/log/zabbix/zabbix_server.logLogFileSize=1PidFile=/var/run/zabbix/zabbix_server.pidSocketDir=/var/run/zabbixDBHost=192.168.32.20DBName=zabbix_serverDBUser=zabbixDBPassword=zabbixStartTrappers=10Timeout=30AlertScriptsPath=/usr/lib/zabbix/alertscriptsExternalScripts=/usr/lib/zabbix/externalscriptsFpingLocation=/usr/bin/fpingFping6Location=/usr/bin/fping6LogSlowQueries=3000   重启zabbix服务。1systemctl restart zabbix-server zabbix-agent apache2   此时访问zabbix-server主机对应的IP或者域名,加上路径zabbix/setup.php就可以访问zabbix的web界面进行检查配置了。在环境检查时,可能会有一项报错,提示PHP option date.timezone检查unkown。虽然不影响服务启动,不过我们最好还是将他解决掉。1vim /etc/zabbix/apache.conf   搜索timezone,将值改为php_value date.timezone Asia/Shanghai。注意,有两个timezone设置,分别是针对PHP5.版本和PHP7.版本,我们修改7版本的就可以了,也可以都修改。然后重启服务。1systemctl restart zabbix-server zabbix-agent apache2   此时,再刷新一下网页,就可以看到所有检查都是OK状态了。正确填写数据库信息和server主机信息,点击Finish,配置就完成了,会自动跳转至登录界面,默认用户名为Admin,默认密码为zabbix。 编译安装zabbix  源码编译安装zabbix的话,先下载源码包。  下载路径为https://jaist.dl.sourceforge.net/project/zabbix/ZABBIX%20Latest%20Stable/。  我们这里以4.015版本为例1234cd /usr/local/srcwget https://jaist.dl.sourceforge.net/project/zabbix/ZABBIX%20Latest%20Stable/4.0.15/zabbix-4.0.15.tar.gztar xf zabbix-4.0.15.tar.gzcd zabbix-4.0.15/   编译安装需要我们自己来创建zabbix用户组。12groupadd -g 1111 zabbix #创建zabbix用户和组useradd -u 1111 -g 1111 zabbix   安装相关依赖的安装包  CentOS:1yum install gcc libxml2-devel net-snmp net-snmp-devel curl curl-devel php phpbcmath php-mbstring mariadb mariadb-devel -y   Ubuntu:12apt updateapt-get install apache2 apache2-bin apache2-data apache2-utils fontconfig-config fonts-dejavu-core fping libapache2-mod-php libapache2-mod-php7.2 libapr1 libaprutil1 libaprutil1-dbd-sqlite3 libaprutil1-ldap libfontconfig1 libgd3 libiksemel3 libjbig0 libjpeg-turbo8 libjpeg8 liblua5.2-0 libodbc1 libopenipmi0 libsensors4 libsnmp-base libsnmp30 libsodium23 libssh2-1 libtiff5 libwebp6 libxpm4 php-bcmath php-common php-gd php-ldap php-mbstring php-mysql php-xml php7.2-bcmath php7.2-cli php7.2-common php7.2-gd php7.2-json php7.2-ldap php7.2-mbstring php7.2-mysql php7.2-opcache php7.2-readline php7.2-xml snmpd ssl-cert ttf-dejavu-core libmysqlclient-dev libxml2-dev libxml2 snmp libsnmp-dev libevent-dev openjdk-8-jdk curl libcurl4-openssl-dev   然后就可以编译并安装了。1234./configure --prefix=/apps/zabbix_server \\--enable-server --enable-agent --with-mysql \\--with-net-snmp --with-libcurl --with-libxml2 --enable-javamake install   之后流程就与之前一样。 设置中文web页面及乱码问题如果Ubuntu系统安装时为选择中文语言,则web界面大概率是英文界面,可能会对我们的使用有一定影响。点击右上角的用户头像标志,可以选择语音。不过如果系统没有安装中文时,是无法选择中文的。  所以需要我们在ubuntu系统中安装并设置中文简体语言环境.。  1、安装简体中文语言环境1apt-get install language-pack-zh*   2、增加中文语言环境变量123vim /etc/environmentPATH=\"/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games\"LANG=\"zh_CN.UTF-8\"   3、重新设置本地配置1dpkg-reconfigure locales   此时刷新网页,就可以选择中文Chinese(zh_CN)了,点Update修改保存。此时就可以看到界面变成中文了。不过这时,在图形等界面,大概率会出现字符无法显示的乱码情况,这是由于当前系统有些监控项部分显示有乱码,使由于web界面显示为中文但是系统没有相关字体支持,因此需要相关字体的支持才能正常显示。这时需要我们自己配置一个字体并修改zabbix的font设置。  可以去网上下载字体,也可以从windows中获取已有字体。  在windows中路径为控制面板\\外观和个性化\\字体(复制至地址栏就可以找到了)  不过需要注意的是,里面有的字体是otf格式,zabbix无法识别,要选择ttf格式的字体才可以。  将选好的或者下载好的ttf格式字体,拷贝至zabbix-server主机的zabbix安装目录下的fonts目录里。  如果是ubuntu通过apt安装,则为/usr/share/zabbix/assets/fonts,如果是编译安装则可通过find命令搜索一下find /PATH(安装路径) -name fonts。  可以看到里面已经有一个字体文件了的(虽然是个软链接)。123456root@DockerUbuntu19:~# ll /usr/share/zabbix/assets/fonts总用量 11520drwxr-xr-x 2 root root 4096 12月 18 18:59 ./drwxr-xr-x 5 root root 4096 12月 18 17:43 ../lrwxrwxrwx 1 root root 38 12月 18 17:44 graphfont.ttf -> /etc/alternatives/zabbix-frontend-font-rw-r--r-- 1 root root 11785184 12月 18 18:59 simkai.ttf   将我们准备好的ttf文件拷贝至此目录,修改zabbix环境变量配置文件(apt安装路径为/usr/share/zabbix/include/defines.inc.php)12cd /usr/share/zabbix/vim include/defines.inc.php   搜索替换graphfont为我们期望的字体名称。一共有两处。都替换了即可。12345#define('ZBX_GRAPH_FONT_NAME', 'graphfont'); // font file namedefine('ZBX_GRAPH_FONT_NAME', 'simkai'); // font file name- - -#define('ZBX_FONT_NAME', 'graphfont');define('ZBX_FONT_NAME', 'simkai');   不需要重启zabbix及apache,修改后的字体文件即可直接生效。  至此zabbix-server端的配置就完成了。 zabbix proxyzabbix proxy对比zbbbix server 功能 zabbxy proxy zabbix server 轻量级 是 相对重量级 图形 无 带图形控制界面 可以独立工作 是,可以独立采集数据并存储 是,即数据采集、存储、分析、展示于一体 易维护 是,配置完成后基本无需管理 维护也不难 独立数据库 保留少量最近数据 保留指定时间内的所有数据 报警通知 否,代理服务器不发送邮件通知 支持邮件、短信等告警机制 zabbix proxy版本选择  zabbix proxy的大版本必须要和zabbix server版本一致,否则会导致出现zabbix server与zabbix proxy不兼容问题,会提示报错:1proxy \"zabbix-proxy-active\" protocol version 3.2 differs from server version 4.0   确认下zabbix-server的版本1234567891011121314root@DockerUbuntu19:/usr/share/zabbix/assets/fonts# zabbix_server -Vzabbix_server (Zabbix) 4.0.15Revision 992445e02c 25 November 2019, compilation time: Nov 25 2019 09:01:31Copyright (C) 2019 Zabbix SIALicense GPLv2+: GNU GPL version 2 or later <http://gnu.org/licenses/gpl.html>.This is free software: you are free to change and redistribute it according tothe license. There is NO WARRANTY, to the extent permitted by law.This product includes software developed by the OpenSSL Projectfor use in the OpenSSL Toolkit (http://www.openssl.org/).Compiled with OpenSSL 1.1.0g 2 Nov 2017Running with OpenSSL 1.1.1 11 Sep 2018   所以我们最好也安装相同版本的zabbix-proxy。 zabbix proxy部署  因为proxy也需要一个数据库,可以选择复用server端的数据库,或者再另外创建一个。我们这里复用之前server端的数据库。  所以在之前的MariaDB数据库中,新建一个库,并创建对应权限用户。12345MariaDB [(none)]> create database zabbix_proxy;Query OK, 1 row affected (0.01 sec)MariaDB [(none)]> grant all privileges on zabbix_proxy.* to proxy@\"172.18.32.%\" identified by \"proxy\";Query OK, 0 rows affected (0.10 sec)   回到proxy主机,然后去官方镜像仓库https://repo.zabbix.com/zabbix/4.0/找到对应系统、对应版本的安装包路径  CentOS:1yum install https://repo.zabbix.com/zabbix/4.0/rhel/7/x86_64/zabbix-proxy-mysql-4.0.15-1.el7.x86_64.rpm   Ubuntu:1234wget https://repo.zabbix.com/zabbix/4.0/ubuntu/pool/main/z/zabbix-release/zabbix-release_4.0-3%2Bbionic_all.debdpkg -i zabbix-release_4.0-3+bionic_all.debapt updateapt install zabbix-proxy-mysql   导入proxy的数据库表(CentOS和Ubuntu路径可能不同,不过名字都叫schema.sql.gz)1zcat /usr/share/doc/zabbix-proxy-mysql-4.0.15/schema.sql.gz | mysql -uproxy -pproxy -h192.168.32.20 zabbix_proxy   修改配置文件1234567891011121314151617181920212223242526vim /etc/zabbix/zabbix_proxy.confProxyMode=1 #0为主动,1为被动Server=192.168.32.19 #zabbix server服务器的地址或主机名Hostname=Zabbix proxy #代理服务器名称,需要与zabbix server添加代理时候的proxyname是一致的!ListenPort=10051 #zabbix proxy监听端口LogFile=/var/log/zabbix/zabbix_proxy.logEnableRemoteCommands=1 #允许zabbix server执行远程命令PidFile=/var/run/zabbix/zabbix_proxy.pidSocketDir=/var/run/zabbixDBHost=192.168.32.20 #数据库服务器地址DBName=zabbix_proxy #使用的数据库名称DBUser=proxy #连接数据库的用户名称DBPassword=proxy #数据库用户密码DBPort=3306 #数据库端口ProxyLocalBuffer=720 #已经提交到zabbix server的数据保留时间ProxyOfflineBuffer=720 #未提交到zabbix server的时间保留时间HeartbeatFrequency=60 #心跳间隔检测时间,默认60秒,范围0-3600秒,被动模式不使用ConfigFrequency=5 #间隔多少秒从zabbix server获取监控项信息DataSenderFrequency=5 #数据发送时间间隔,默认为1秒,范围为1-3600秒,被动模式不使用StartPollers=20 #启动的数据采集器数量CacheSize=2G #保存监控项而占用的最大内存HistoryCacheSize=2G #保存监控历史数据占用的最大内存HistoryIndexCacheSize=128M #历史索引缓存的大小Timeout=30 #监控项超时时间,单位为秒LogSlowQueries=3000 #毫秒,多久的数据库查询会被记录到日志   然后在web界面添加proxy,主动模式是proxy端主动向server端推送数据,所以不需要填写IP,被动模式是server端向proxy拉取数据,需要填写proxy端IP或者域名DNS。选在主动代理或者被动代理,视情况而定,要与配置文件中相同。  至此,zabbix proxy端也就配置好了。 zabbix agentagent工作模式  一般来说,zabbix agent与proxy有两种工作模式,一个是主动模式,即被监控端主动向server端或者proxy端发起请求,请求监控项列表,并按照列表的内容主动定时推送监控信息,此时agent端使用随机端口访问server端或者proxy端的固定端口(10051端口);另一个是被动模式,是server端或者proxy依照设定好的监控项条目,直接去agent段获取相应的数据,此时是agent端打开固定端口(10050)等待请求。  主动模式与被动模式中的主动与被动,是相对于agent端来说的,agent主动的就叫做主动模式,agent端被动的就叫做被动模式。 zabbix agent部署  在被监控主机上安装zabbix-agent安装包。流程与之前一样。1apt install zabbix-agent   修改配置文件,主要需要设置Server和ServerActive,其他保持默认就可以了。Server是控制允许哪个主机可以从本机上拉取数据,一般把server端和paroxy端都写上,方便修改(,其实只写proxy端ip就可以了),ServerActive是设置向哪个主机请求主动监控配置的,如果打算使用被动模式,则可不进行设置此项,而且设置了也不会生效。1234567891011root@DockerUbuntu21:~# grep \"^[a-Z]\" /etc/zabbix/zabbix_agentd.confPidFile=/var/run/zabbix/zabbix_agentd.pidLogFile=/var/log/zabbix-agent/zabbix_agentd.logLogFileSize=1Server=192.168.32.19,192.168.32.20ListenPort=10050StartAgents=4ServerActive=127.0.0.1Hostname=192.168.32.21Timeout=20Include=/etc/zabbix/zabbix_agentd.conf.d/*.conf   重启agent服务,是配置生效1systemctl restart zabbix-agent 配置监控  之前的一系列操作,只是完成了一个服务基础,之后的操作才是重点,也就是配置监控。  进入zabbix的web管理界面,可以看到有很多项目,我们选择配置——主机——创建主机,来添加我们的监控对象。  在创建主机界面,需要选择一个模版,也就是监控规则,因为我们还没有创建模版,所以可以使用系统自带的模版,可以搜索linux使用自带的一些监控项。  需要注意的是,agent使用工作模式到底是主动模式还是被动模式,就在于监控项的类型是主动还是被动模式。系统默认模版都是被动式的,如果想使用主动式,可以批量修改模版监控项(模版的具体介绍会在之后文章介绍)。  而且,agent端使用的主动式或者被动式方式,与proxy设置的主动模式还是被动模式没有关系。proxy创建时选择的模式要与proxy配置文件一致,而这个设定只是控制proxy与server之间的关系,而真正控制agent工作模式的就在于这个监控项目的设置了。也就是说如果监控项目都是被动式,ServerActive设不设置都不会生效,如果有一部分项目是主动式,若没有设置正确的ServerActive,则这些项目将会获取不到数据了。  至此,对于zabbix的初步配置就生效了,等一下就可以看到添加的主机都显示绿色的可用状态。  之后就可以在图形界面看到图形数据了。","categories":[{"name":"linux进阶","slug":"linux进阶","permalink":"https://wudihechao.github.io/categories/linux进阶/"}],"tags":[{"name":"企业级应用","slug":"企业级应用","permalink":"https://wudihechao.github.io/tags/企业级应用/"},{"name":"监控","slug":"监控","permalink":"https://wudihechao.github.io/tags/监控/"},{"name":"zabbix","slug":"zabbix","permalink":"https://wudihechao.github.io/tags/zabbix/"}],"keywords":[{"name":"linux进阶","slug":"linux进阶","permalink":"https://wudihechao.github.io/categories/linux进阶/"}]},{"title":"使用kubeasz自动化部署K8s","slug":"使用kubeasz自动化部署K8s","date":"2019-12-18T07:21:12.000Z","updated":"2020-01-08T12:08:34.251Z","comments":true,"path":"/blog/8f374cb8.html","link":"","permalink":"https://wudihechao.github.io/blog/8f374cb8.html","excerpt":"  本文使用kubeasz项目基于二进制方式部署和利用ansible-playbook实现自动化部署K8s。","text":"  本文使用kubeasz项目基于二进制方式部署和利用ansible-playbook实现自动化部署K8s。  架构图如下所示  根据kubeasz官方文档中高可用集群所需节点配置如下 角色 数量 描述 管理节点 1 运行ansible/easzctl脚本,可以复用master,建议使用独立节点(1c1g) etcd节点 3 注意etcd集群需要1,3,5,7…奇数个节点,一般复用master节点 master节点 2 高可用集群至少2个master节点 node节点 3 运行应用负载的节点,可根据需要提升机器配置/增加节点数 配置集群环境  此次部署节点设置如下:ansible安装节点&master1:172.18.32.18master2:172.18.32.19harbor:172.19.32.20node1:172.18.32.21node2:172.18.32.22etcd1:172.18.32.23etcd2:172.18.32.24etcd3:172.18.32.25haproxy1+keepalived:172.18.32.183haproxy2+keepalived:172.18.32.184 VIP:172.18.32.250 配置免密登录  先将ansible安装节点也就是master1主机(可以不复用主机)的秘钥分发至各个主机。1234567891011121314151617181920212223242526272829303132333435363738394041vim fenfamiyao.sh#!/bin/bash#目标主机列表PASSWORD=\"password\"PORT=\"22\"IP=\"172.18.32.18172.18.32.19172.18.32.20172.18.32.21172.18.32.22172.18.32.23172.18.32.24172.18.32.25\"[ -a /root/.ssh/id_rsa ] || ssh-keygen -t rsa which sshpass &> /dev/null || yum install sshpass -y #适用于CentOSfor node in ${IP};do sshpass -p $PASSWORD ssh-copy-id -p$PORT -o StrictHostKeyChecking=no ${node}# if [ $? -eq 0 ];then# echo \"${node} 秘钥 copy 完成,准备环境初始化.....\"# ssh -p$PORT ${node} \"mkdir /etc/docker/certs.d/harbor.hechao.com -p\"# echo \"Harbor 证书目录创建成功!\"# scp -P$PORT /usr/local/src/harbor/certs/harbor-ca.crt ${node}:/etc/docker/certs.d/harbor.hechao.com/harbor-ca.crt# echo \"Harbor 证书拷贝成功!\"# scp -P$PORT /etc/hosts ${node}:/etc/hosts# echo \"host 文件拷贝完成\"# scp -r -P$PORT /root/.docker ${node}:/root/# echo \"Harbor 认证文件拷贝完成!\"# scp -r -P$PORT /etc/resolv.conf ${node}:/etc/ echo \"镜像加速链接同步!\" ssh ${node} \"mkdir -p /etc/docker\" scp -r -p$PORT /etc/docker/daemon.json ${node}:/etc/docker/daemon.json# else# echo \"${node} 秘钥 copy 失败\"# fidone 安装ansible环境  因为要使用ansible批量自动化部署K8s集群,每个主机都要先安装ansible环境。  ubuntu默认的python版本是3.6,而ansible需要python2.7。123apt updateapt install python2.7ln -s /usr/bin/python2.7 /usr/bin/python   master1上安装ansible1apt install ansible   centos一般是自带python2.X环境,所以一般不需要在进行额外操作。  在master1上检查各节点连通性1ansible all -m ping 配置kubeasz下载kubeasz  官方文档地址为https://github.com/easzlab/kubeasz,根据适配的K8s版本选择合适的kubeasz版本。根据官方文档,目前支持以下版本 集群版本 kubernetes v1.13, v1.14, v1.15, v1.16操作系统 CentOS/RedHat 7, Debian 9/10, Ubuntu 1604/1804运行时 docker 18.06.x-ce, 18.09.x, containerd 1.2.6网络 calico, cilium, flannel, kube-ovn, kube-router   我们下载2.0.3版本的kubeasz,并给予执行权限12curl -C- -fLO --retry 3 https://github.com/easzlab/kubeasz/releases/download/2.0.3/easzupchmod +x easzup   可以用file easzup命令看到这是一个ASCII文本文件,其实就是一个shell脚本。12root@DockerUbuntu18:~# file easzupeaszup: Bourne-Again shell script, ASCII text executable   用vim编辑修改此文件。  大部分地方无需修改,不过可以设置docker版本为18.09.9和K8s的版本为v1.15.5,其他地方不用动。12export DOCKER_VER=18.09.9export K8S_BIN_VER=v1.15.5   执行-- help查看脚本使用方法。123456789101112131415root@DockerUbuntu18:~# ./easzup --help./easzup: illegal option -- -Usage: easzup [options] [args] option: -{DdekSz} -C stop&clean all local containers -D download all into /etc/ansible -S start kubeasz in a container -d <ver> set docker-ce version, default \"18.09.9\" -e <ver> set kubeasz-ext-bin version, default \"0.3.0\" -k <ver> set kubeasz-k8s-bin version, default \"v1.15.5\" -m <str> set docker registry mirrors, default \"CN\"(used in Mainland,China) -p <ver> set kubeasz-sys-pkg version, default \"0.3.2\" -z <ver> set kubeasz version, default \"2.0.3\" see more at https://github.com/kubeasz/dockerfiles   先将/etc/ansible目录下所有自带的配置文件以及hosts文件删掉1\\rm -rf /etc/ansible/*   执行脚本下载所有需要的镜像和二进制文件1./easzup -D   稍等片刻之后,下载好需要的镜像和二进制文件,此时就可以开始ansible自动部署安装K8s集群了。 ansible部署K8s  kubeasz工具都已经写好了playbook,只需要设定好hosts文件即可一键安装了。先进入/etc/ansible目录,然后复制提供好的host模版文件并编辑。123cd /etc/ansible/cp example/hosts.multi-node hostsvim hosts 123456789101112131415161718192021222324root@DockerUbuntu18:/etc/ansible# grep -v ^# hosts|grep -v ^$[etcd]172.18.32.23 NODE_NAME=etcd1172.18.32.24 NODE_NAME=etcd2172.18.32.25 NODE_NAME=etcd3[kube-master]172.18.32.18172.18.32.19[kube-node]172.18.32.21172.18.32.22[harbor][ex-lb][chrony][all:vars]CONTAINER_RUNTIME=\"docker\"CLUSTER_NETWORK=\"calico\"SERVICE_CIDR=\"10.68.0.0/16\"CLUSTER_CIDR=\"172.20.0.0/16\"NODE_PORT_RANGE=\"30000-65000\"CLUSTER_DNS_DOMAIN=\"cluster.local.\"bin_dir=\"/usr/kube/bin\"ca_dir=\"/etc/kubernetes/ssl\"base_dir=\"/etc/ansible\"   以为我本地已经搭好harbor服务器了,所以就不要设置harbor服务让他来安装了。除了各节点IP以外,需要修改的就是bin_dir目录,这样就省去了创建软链接的步骤,也省去配置PATH变量了。我选择的网卡是calico,也可以使用默认的flannel,之后具体区别会再详细介绍。  直接执行 playbook剧本90.setup.yml可以直接一键安装完成,不过如果出现问题我们不好排错,推荐一个剧本一个剧本的来跑。 01.prepare.yml 1ansible-playbook 01.prepare.yml   这个剧本总共三个环节,一般来说都不会报错(如果提示软链接创建失败,则可忽略)。  1.如果设置了chrony服务器,则master、node、etcd主机都会向chrony服务器同步时间。  2.控制节点上创建CA、创建集群参数及客户端认证参数  3分发证书工具CFSSL及kubeconfig配置文件 02.etcd.yml 1ansible-playbook 02.etcd.yml   这个剧本是在配置etcd服务器。在3个etcd节点上,创建etcd目录并分发证书,导入之前下载在控制端的二进制程序etcd及etcdctl及导入etcd的systemctl unit文件,设置开机自动启动etcd服务。这步也很简单,一般也不会出现什么问题。 03.docker.yml   因为我们在hosts文件中设置了运行时为docker,所以我们第三个剧本选择03.docker.yml,选择执行03.containerd,yml剧本也不会执行,里面做了条件判断1234567root@DockerUbuntu18:/etc/ansible# cat 03.containerd.yml # to install containerd service- hosts: - kube-master - kube-node roles: - { role: containerd, when: \"CONTAINER_RUNTIME == 'containerd'\" }   这个阶段的剧本比较复杂,要在每一个节点上安装docker,在执行这一步之前,先检查一下模版二进制文件的docker版本是否和我们之前预设的一样。12/etc/ansible/bin/docker -vDocker version 18.09.6, build 481bc77   如果不一样,可以去/opt/kube/bin/目录下找一下之前下好的二进制文件,确认下版本。1/opt/kube/bin/docker -v   这个目录下的版本一般是不会错的,将此目录下的二进制文件复制至模版目录/etc/ansible/bin/。1cp /opt/kube/bin/* /etc/ansible/bin/   执行ansible剧本1ansible-playbook 03.docker.yml 04.kube-master.yml   这个剧本是对两个master节点操作,执行了以下操作  1.创建kubernetes签名请求以及证书和私钥  2.导入配置文件,启动kube-apiserver、kube-controller-manager及kube-scheduler服务  3.设置主节点的kube-apiserver.service文件,并设置apiserver的IP地址并创建配置用户rbac权限。  此时使用命令kubectl get node可以看到两个主节点都是出于ready状态了。1234root@DockerUbuntu18:~# kubectl get nodeNAME STATUS ROLES AGE VERSION172.18.32.18 Ready,SchedulingDisabled master 1m v1.15.5172.18.32.19 Ready,SchedulingDisabled master 1m v1.15.5 05..kube-node.yml   这个剧本主要是将几个node节点加到集群中来。流程比较复杂,大致有以下步骤:  1,创建kube-node目录:/var/lib/kubelet、/var/lib/kube-proxy和/etc/cni/net.d目录  2.导入之前准备好的二进制可执行文件kubectl、kubelet、kube-proxy、bridge、host-local和loopback  3.配置haproxy,监听本地的127.0.0.1:6443端口,代理至两个主节点的6443端口  4.生成node节点的kubelet的配置文件,并分发至各个node节点。kubelet连接主节点的apiserver。  5.node节点连接后,对node节点标记为kubernetes.io/role=node   这步很容易出现kubelet服务无法启动,或者一直处于loaded状态,返回值为255。这是因为node节点的kubelet的认证失败。可以去node节点主机上查看服务kubelet服务状态。12systemctl status kubectljournalctl -u kubelet -e   我遇到几次node节点kubelet无法正常启动的情况,但是换了个全新的node节点就可以加进去了,说明问题不是在主节点上,一般将node节点还原至干净系统都可以解决问题。  还有一次返回值255但报错提示是,找不到/run/systemd/resolve/resolv.conf文件,于是创建一个1mkdir /run/systemd/resolve/;echo \"nameserver 233.5.5.5\" > /run/systemd/resolve/resolv.conf   自己创建一个解析之后问题解决。  顺利的话,使用kubectl get node可以看到主节点和node节点都处于ready状态。123456root@DockerUbuntu18:/etc/ansible# kubectl get nodeNAME STATUS ROLES AGE VERSION172.18.32.18 Ready,SchedulingDisabled master 2h v1.15.5172.18.32.19 Ready,SchedulingDisabled master 2h v1.15.5172.18.32.21 Ready node 2h v1.15.5172.18.32.22 Ready node 2h v1.15.5 06.network.yml   在这个剧本中,主要是配置各个pod之间的网络访问。需要在每个物理节点上都生成一个calico-node服务的pod,再启动一个calico-kube-controllers服务的pod来管理他们,(因为默认主节点设置了不可调度,所以这个calico-kube-contronllers一般是在node节点上生成)。因为使用的是pod方式启动,所以这个剧本中,除了配置calicao证书及私钥外,主要是生成了一个calico DaemonSet yaml文件,路径为/opt/kube/kube-system/calico.yaml,所以之后如果修改网络配置,可以直接在此路径下修改这个yaml文件,例如如果想要修改镜像地址为私有harbor地址,则123kubectl delete -f /opt/kube/kube-system/calico.yamlvim /opt/kube/kube-system/calico.yamlkubectl apply -f /opt/kube/kube-system/calico.yaml   需要注意的是,如果将yaml文件中拉取镜像的地址改为了私有harbor,则需要在yaml文件中加入Secret资源,使用imagePullSecrets拉取。  当我们的node节点不需要跨网段时,通常会选择将IPIP模式(ip-in-ip叠加模式)关掉,使用calico的BGP模式,以节约大量主机内部访问时封装的性能损耗。  查看路由,可以看到目前荣期间通信的网络接口为tunl0。1234567891011root@DockerUbuntu18:~# route -nKernel IP routing tableDestination Gateway Genmask Flags Metric Ref Use Iface0.0.0.0 172.18.0.1 0.0.0.0 UG 0 0 0 eth0172.17.0.0 0.0.0.0 255.255.0.0 U 0 0 0 docker0172.18.0.0 0.0.0.0 255.255.0.0 U 0 0 0 eth0172.20.185.64 0.0.0.0 255.255.255.192 U 0 0 0 *172.20.233.128 172.18.32.19 255.255.255.192 UG 0 0 0 tunl0172.20.250.128 172.18.32.22 255.255.255.192 UG 0 0 0 tunl0172.20.250.192 172.18.32.21 255.255.255.192 UG 0 0 0 tunl0192.168.0.0 0.0.0.0 255.255.0.0 U 0 0 0 eth1   于是可以编辑/etc/ansible/roles/calico/defaults/main.yml或者直接修改/opt/kube/kube-system/calico.yaml文件,将其中的 CALICO_IPV4POOL_IPIP修改为off,将name: FELIX_IPINIPMTU属性注释掉,改为FELIX_IPINIPENABLED值为false。12345- name: CALICO_IPV4POOL_IPIP value: \"off\" - name: FELIX_IPINIPENABLED value: \"false\"   然后执行kubectl delete -f /opt/kube/kube-system/calico.yaml将网络组件calico的pod都先停掉,reboot重启后,使用命令ifconfig就会发现,之前使用的tunl0网卡就不见了。再使用命令kubectl apply -f /opt/kube/kube-system/calico.yaml,将k8s网络连接起来,使用命令route -n查看路由信息,就会发现,跨主机通信直接使用eth0网卡了。此时模式从IPIP修改为BGP模式。1234567891011root@DockerUbuntu18:~# route -nKernel IP routing tableDestination Gateway Genmask Flags Metric Ref Use Iface0.0.0.0 172.18.0.1 0.0.0.0 UG 0 0 0 eth0172.17.0.0 0.0.0.0 255.255.0.0 U 0 0 0 docker0172.18.0.0 0.0.0.0 255.255.0.0 U 0 0 0 eth0172.20.185.64 0.0.0.0 255.255.255.192 U 0 0 0 *172.20.233.128 172.18.32.19 255.255.255.192 UG 0 0 0 eth0172.20.250.128 172.18.32.22 255.255.255.192 UG 0 0 0 eth0172.20.250.192 172.18.32.21 255.255.255.192 UG 0 0 0 eth0192.168.0.0 0.0.0.0 255.255.0.0 U 0 0 0 eth1 07.cluster-addon.yml   第七步是安装一些功能插件,如在node节点生成dns解析(默认使用的是coredns,可以在roles/cluster-addon/defaults/main.yml文件中设置),安装dashboard(可视化web界面)。我们也可以选择自己安装这些功能插件。  可参考官方文档https://kubernetes.io/docs/tasks/access-application-cluster/web-ui-dashboard/#deploying-the-dashboard-ui  dashboard默认是1.6.3版本的,比较老。我们这里手动安装dashboard1.10.1版本,过程如下1234cd /etc/ansible/manifests/dashboardmkdir 1.10.1cp 1.6.3/ui* 1.10.1/cd 1.10.1   先下载yaml文档1wget https://raw.githubusercontent.com/kubernetes/dashboard/v1.10.1/src/deploy/recommended/kubernetes-dashboard.yaml   再创建admin tokenvim admin-user-sa-rbac.yaml12345678910111213141516171819apiVersion: v1kind: ServiceAccountmetadata: name: admin-user namespace: kube-system---apiVersion: rbac.authorization.k8s.io/v1kind: ClusterRoleBindingmetadata: name: admin-userroleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: cluster-adminsubjects:- kind: ServiceAccount name: admin-user namespace: kube-system   创建集群角色vim read-user-sa-rbac.yaml123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129---apiVersion: rbac.authorization.k8s.io/v1kind: ClusterRolemetadata: name: dashboard-read-clusterrolerules:- apiGroups: - \"\" resources: - configmaps - endpoints - persistentvolumeclaims - pods - replicationcontrollers - replicationcontrollers/scale - serviceaccounts - services - nodes - persistentvolumeclaims - persistentvolumes verbs: - get - list - watch- apiGroups: - \"\" resources: - bindings - events - limitranges - namespaces/status - pods/log - pods/status - replicationcontrollers/status - resourcequotas - resourcequotas/status verbs: - get - list - watch- apiGroups: - \"\" resources: - namespaces verbs: - get - list - watch- apiGroups: - apps resources: - daemonsets - deployments - deployments/scale - replicasets - replicasets/scale - statefulsets verbs: - get - list - watch- apiGroups: - autoscaling resources: - horizontalpodautoscalers verbs: - get - list - watch- apiGroups: - batch resources: - cronjobs - jobs verbs: - get - list - watch- apiGroups: - extensions resources: - daemonsets - deployments - deployments/scale - ingresses - networkpolicies - replicasets - replicasets/scale - replicationcontrollers/scale verbs: - get - list - watch- apiGroups: - policy resources: - poddisruptionbudgets verbs: - get - list - watch- apiGroups: - networking.k8s.io resources: - networkpolicies verbs: - get - list - watch- apiGroups: - storage.k8s.io resources: - storageclasses - volumeattachments verbs: - get - list - watch- apiGroups: - rbac.authorization.k8s.io resources: - clusterrolebindings - clusterroles - roles - rolebindings verbs: - get - list - watch   此时目录结构如下123456789root@DockerUbuntu18:/etc/ansible/manifests/dashboard/1.10.1# tree.├── admin-user-sa-rbac.yaml├── kubernetes-dashboard.yaml├── read-user-sa-rbac.yaml├── ui-admin-rbac.yaml└── ui-read-rbac.yaml0 directories, 5 files   然后通过yaml文件启动dashboard的pod1kubectl apply -f .   可以通过命令查看pod 是否启动成功。1kubectl get pods --all-namespaces | grep dashboard   看到状态running之后,输入命令开启认证生成登陆用户名密码1easzctl basic-auth -s 1234[INFO]basic-auth for apiserver is enabled!BASIC_AUTH_USER: 'admin'BASIC_AUTH_PASS: '4fe554e56c32f27b'[INFO] Action successed : basic-auth basic-auth -s`   通过命令kubectl cluster-info查看集群信息来查看登陆url1kubectl cluster-info   使用命令来获取token1kubectl -n kube-system describe secret $(kubectl -n kube-system get secret | grep admin-user | awk '{print $1}')   这时就可以登陆web界面查看K8s集群信息了。","categories":[{"name":"Cloud","slug":"Cloud","permalink":"https://wudihechao.github.io/categories/Cloud/"}],"tags":[{"name":"kubernetes","slug":"kubernetes","permalink":"https://wudihechao.github.io/tags/kubernetes/"},{"name":"kubeasz","slug":"kubeasz","permalink":"https://wudihechao.github.io/tags/kubeasz/"},{"name":"dashboard","slug":"dashboard","permalink":"https://wudihechao.github.io/tags/dashboard/"}],"keywords":[{"name":"Cloud","slug":"Cloud","permalink":"https://wudihechao.github.io/categories/Cloud/"}]},{"title":"使用kubeadm部署安装K8s","slug":"使用kubeadm部署安装K8s","date":"2019-12-13T03:08:47.000Z","updated":"2019-12-18T07:32:52.479Z","comments":true,"path":"/blog/107b7ce7.html","link":"","permalink":"https://wudihechao.github.io/blog/107b7ce7.html","excerpt":"  本文将介绍通过kubeadm部署K8s集群的详细过程,且通过两个mater节点实现K8s集群的高可用。  本次演示使用 k8s 官方提供的部署工具 kubeadm 自动安装, 需要在 master 和 node 节点上安装 docker 等组件, 然后初始化, 把管理端的控制服务和 node 上的服务都以pod 的方式运行。","text":"  本文将介绍通过kubeadm部署K8s集群的详细过程,且通过两个mater节点实现K8s集群的高可用。  本次演示使用 k8s 官方提供的部署工具 kubeadm 自动安装, 需要在 master 和 node 节点上安装 docker 等组件, 然后初始化, 把管理端的控制服务和 node 上的服务都以pod 的方式运行。  架构结构示意图如下路所示  环境搭建(master及node节点均为ubuntu1804):  master1:192.168.32.18  master2:192.168.32.19  harbor:192.168.32.20  node1:192.168.32.21  node2:192.168.32.22  需要禁用 swap, selinux, iptables。1swapoff -a 搭建master节点安装keepalived  可以通过apt快速安装或者源码编译,下面以apt包管理工具安装为例123apt updateapt install keepalived -ycp /usr/share/doc/keepalived/samples/keepalived.conf.vrrp /etc/keepalived/keepalived.conf   然后修改配置文件,实例如下123456789101112131415161718192021222324252627282930! Configuration File for keepalivedglobal_defs { notification_email { acassen } notification_email_from [email protected] smtp_server 192.168.200.1 smtp_connect_timeout 30 router_id LVS_DEVEL}vrrp_instance VI_1 { state MASTER interface eth0 garp_master_delay 10 smtp_alert virtual_router_id 32 priority 100 advert_int 1 authentication { auth_type PASS auth_pass 1111 } virtual_ipaddress { # optional label. should be of the form \"realdev:sometext\" for # compatibility with ifconfig. 172.18.32.250 label eth0:1 }}   另一个节点也安装keepalived,然后测试VIP是否可以漂移成功。 安装 docker  在主节点上先安装docker,详细可参考之前文章。  可以通过阿里云镜像,使用脚本来安装vim docker1806.sh12345678910111213141516171819#!/bin/bash# step 1: 安装必要的一些系统工具apt-get updateapt-get -y install apt-transport-https ca-certificates curl software-properties-common# step 2: 安装GPG证书curl -fsSL https://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | sudo apt-key add -# Step 3: 写入软件源信息sudo add-apt-repository \"deb [arch=amd64] https://mirrors.aliyun.com/docker-ce/linux/ubuntu $(lsb_release -cs) stable\"# Step 4: 更新并安装Docker-CEapt-get -y updateapt-get -y install docker-ce=18.06.0~ce~3-0~ubuntu# 安装指定版本的Docker-CE:# Step 1: 查找Docker-CE的版本:# apt-cache madison docker-ce# docker-ce | 17.03.1~ce-0~ubuntu-xenial | https://mirrors.aliyun.com/docker-ce/linux/ubuntu xenial/stable amd64 Packages# docker-ce | 17.03.0~ce-0~ubuntu-xenial | https://mirrors.aliyun.com/docker-ce/linux/ubuntu xenial/stable amd64 Packages# Step 2: 安装指定版本的Docker-CE: (VERSION例如上面的17.03.1~ce-0~ubuntu-xenial)# sudo apt-get -y install docker-ce=[VERSION] 1bash docker1806.sh 配置阿里加速器123456vim /etc/docker/daemon.json{ \"registry-mirrors\": [\"https://360k4x9i.mirror.aliyuncs.com\",\"https://registry.docker-cn.com\"], \"insecure-registries\": [\"https://harbor.local.com\"], \"bip\": \"10.20.0.1/24\"} 安装kubeadm  先配置k8s的镜像源,并安装kubeadm123456789curl https://mirrors.aliyun.com/kubernetes/apt/doc/apt-key.gpg | apt-key add - cat >/etc/apt/sources.list.d/kubernetes.list <<EOFdeb https://mirrors.aliyun.com/kubernetes/apt/ kubernetes-xenial mainEOFapt-get updateapt install kubeadm=1.16.1-00 kubectl=1.16.1-00 kubelet=1.16.1-00systemctl start kubelet && systemctl enable kubelet 安装K8s  因为默认使用的是google的镜像仓库,国内是连接不上的,所以我们最好提前下载好镜像。本次演示安装版本为kubernetes v1.16.1  先查看需要下载的镜像及版本kubeadm config images list --kubernetes-version v1.16.11234567k8s.gcr.io/kube-apiserver:v1.16.1k8s.gcr.io/kube-controller-manager:v1.16.1k8s.gcr.io/kube-scheduler:v1.16.1k8s.gcr.io/kube-proxy:v1.16.1k8s.gcr.io/pause:3.1k8s.gcr.io/etcd:3.3.15-0k8s.gcr.io/coredns:1.6.2   我们先去阿里云镜像仓库提前下载好镜像,可以通过快速实现.。如果有harbor服务器,可以先上传到本地harbor。12345678#!/bin/bashdocker pull registry.cn-hangzhou.aliyuncs.com/google_containers/kube-apiserver:v1.16.1docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/kube-controller-manager:v1.16.1docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/kube-scheduler:v1.16.1docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/kube-proxy:v1.16.1docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/pause:3.1docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/etcd:3.3.15-0docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/coredns:1.6.2 master 初始化  因为我们打算做master的高可用。所以我们在master初始化时,要加选项--control-plane-endpoint=172.18.32.250指定VIP`。只需在一个master节点上做初始化即可。12345678910kubeadm init \\--apiserver-advertise-address=172.18.32.18 \\--control-plane-endpoint=172.18.32.250 \\--apiserver-bind-port=6443 \\--kubernetes-version=v1.16.1 \\--pod-network-cidr=10.10.0.0/16 \\--service-cidr=10.20.0.0/16 \\--service-dns-domain=k8s.local \\--image-repository=registry.cn-hangzhou.aliyuncs.com/google_containers \\--ignore-preflight-errors=swap   也可以基于yaml文件而不是用命令行命令来进行初始化。可以使用命令kubeadm init --config kubeadm-init.yaml ,基于文件初始化。  kubeadm config print init-defaults 输出默认初始化配置  kubeadm config print init-defaults > kubeadm-init.yaml 将默认配置输出至文件123456789101112131415161718192021222324252627282930313233343536373839404142root@k8s-master1:~# cat kubeadm-init.yaml #修改后的初始化文件内容apiVersion: kubeadm.k8s.io/v1beta2bootstrapTokens:- groups:- system:bootstrappers:kubeadm:default-node-tokentoken: abcdef.0123456789abcdefttl: 24h0m0susages:- signing- authenticationkind: InitConfigurationlocalAPIEndpoint:advertiseAddress: 172.18.32.18bindPort: 6443nodeRegistration:criSocket: /var/run/dockershim.sockname: k8s-master1.k8s.localtaints:- effect: NoSchedulekey: node-role.kubernetes.io/master---apiServer:timeoutForControlPlane: 4m0sapiVersion: kubeadm.k8s.io/v1beta2certificatesDir: /etc/kubernetes/pkiclusterName: kubernetescontrolPlaneEndpoint: 172.18.32.250:6443 #添加基于 VIP 的 EndpointcontrollerManager: {}dns:type: CoreDNSetcd:local:dataDir: /var/lib/etcdimageRepository: registry.cn-hangzhou.aliyuncs.com/google_containerskind: ClusterConfigurationkubernetesVersion: v1.16.1networking:dnsDomain: k8s.localpodSubnet: 10.10.0.0/16serviceSubnet: 10.20.0.0/16scheduler: {}   初始化成功,记录下来--token和--discovery-token-ca-cert-hash,,之后加入其他节点时需要用到。  如果初始化失败了需要 使用命令kubeadm reset可以清除已有容器数据以便重新安装,PS:此命令如果在安装完成后使用会清除已创建的k8s集群。 配置kube证书123mkdir -p $HOME/.kubesudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/configsudo chown $(id -u):$(id -g) $HOME/.kube/config 配置网卡12wget https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.ymlkubectl apply -f kube-flannel.yml 加入其他节点  其他三台k8s节点也要安装docker及k8s,可以通过脚本快速实现12345678910111213141516171819202122232425262728293031323334353637383940414243vim node.sh#!/bin/bash# step 1: 安装必要的一些系统工具apt-get updateapt-get -y install apt-transport-https ca-certificates curl software-properties-common# step 2: 安装GPG证书curl -fsSL https://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | sudo apt-key add -# Step 3: 写入软件源信息sudo add-apt-repository \"deb [arch=amd64] https://mirrors.aliyun.com/docker-ce/linux/ubuntu $(lsb_release -cs) stable\"# Step 4: 更新并安装Docker-CEapt-get -y updateapt-get -y install docker-ce=18.06.0~ce~3-0~ubuntucat > /etc/docker/daemon.json << EOF{ \"registry-mirrors\": [\"https://360k4x9i.mirror.aliyuncs.com\",\"https://registry.docker-cn.com\"], \"insecure-registries\": [\"https://harbor.local.com\"], \"bip\": \"10.20.0.1/24\"}EOFcurl https://mirrors.aliyun.com/kubernetes/apt/doc/apt-key.gpg | apt-key add - cat >/etc/apt/sources.list.d/kubernetes.list <<EOFdeb https://mirrors.aliyun.com/kubernetes/apt/ kubernetes-xenial mainEOFapt-get updateapt install kubeadm=1.16.1-00 kubectl=1.16.1-00 kubelet=1.16.1-00 -ysystemctl start kubelet && systemctl enable kubeletdocker pull registry.cn-hangzhou.aliyuncs.com/google_containers/kube-apiserver:v1.16.1docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/kube-controller-manager:v1.16.1docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/kube-scheduler:v1.16.1docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/kube-proxy:v1.16.1docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/pause:3.1docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/etcd:3.3.15-0docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/coredns:1.6.2systemctl enable --now docker kubelet   当前 maste 生成证书用于添加新控制节点:1kubeadm init phase upload-certs --upload-certs   得到--certificate-key的值,也要记录下来。  之后想加入哪个节点,就在哪个节点上操作。先加入另一个master节点。1234kubeadm join 172.18.32.250:6443 --token 89beqy.13jxavbu7yz3187d \\--discovery-token-ca-cert-hash sha256:7388af4f1662805a844cce7c1371facb83f32dddb998370d11bfb41957fe75bf \\--certificate-key 3630d5719795c77e7071d77a206cc17078c912f9c3915e76e70bb26e75e26178 \\--control-plane   再加入各个node节点,命令区别是少了--control-plane选项以及控制秘钥。12kubeadm join 172.18.32.250:6443 --token 89beqy.13jxavbu7yz3187d \\--discovery-token-ca-cert-hash sha256:7388af4f1662805a844cce7c1371facb83f32dddb998370d11bfb41957fe75bf   之后通过命令,就可以看到4个主机都处于ready状态了。至此k8s集群的搭建就完成了。1kubectl get node","categories":[{"name":"Cloud","slug":"Cloud","permalink":"https://wudihechao.github.io/categories/Cloud/"}],"tags":[{"name":"kubeadm","slug":"kubeadm","permalink":"https://wudihechao.github.io/tags/kubeadm/"},{"name":"docker","slug":"docker","permalink":"https://wudihechao.github.io/tags/docker/"},{"name":"kubernetes","slug":"kubernetes","permalink":"https://wudihechao.github.io/tags/kubernetes/"}],"keywords":[{"name":"Cloud","slug":"Cloud","permalink":"https://wudihechao.github.io/categories/Cloud/"}]},{"title":"Docker(五)——Docker镜像仓库","slug":"Docker(五)——Docker镜像仓库","date":"2019-12-07T14:30:04.000Z","updated":"2019-12-09T02:47:35.873Z","comments":true,"path":"/blog/6187894.html","link":"","permalink":"https://wudihechao.github.io/blog/6187894.html","excerpt":"  比较常见的docker镜像仓库,有docker官方仓库https://hub.docker.com/,和阿里云镜像仓库https://cr.console.aliyun.com/cn-hangzhou/instances/images,可以比较方便的拉取镜像或储存容器镜像。而在企业生产中,绝对部分情况我们都是使用企业内部的镜像仓库,来分发部署我们的代码。本文将详细介绍阿里云仓库还有私有云仓库Registry、Harbor的搭建和使用的详细步骤方法。","text":"  比较常见的docker镜像仓库,有docker官方仓库https://hub.docker.com/,和阿里云镜像仓库https://cr.console.aliyun.com/cn-hangzhou/instances/images,可以比较方便的拉取镜像或储存容器镜像。而在企业生产中,绝对部分情况我们都是使用企业内部的镜像仓库,来分发部署我们的代码。本文将详细介绍阿里云仓库还有私有云仓库Registry、Harbor的搭建和使用的详细步骤方法。 阿里云仓库  docker官方仓库配置比较简单,而且大部分是默认配置,且速度不如阿里云镜像仓库速度快,所以我这里就不介绍了,使用方式和阿里云容器镜像仓库差不多类似。 注册账号  使用阿里云仓库服务首先要注册阿里云账号,支付宝也可以登陆,比较快捷。点击上面的网址登陆即可。 创建仓库  先创建一个命名空间,这相当于每个人独立的url,可以以代码类别或者性质命名创建(也可以凭个人喜好),每个账号只能创建5个命名空间,不过也够用了。  然后创建镜像仓库。  地域选择离自己比较近的地域,这样延迟会稍微低一些,选择已创建的命名空间,仓库名的命名一般是服务名或者软件名。公开或者私有看个人请款选择。 上传镜像  有了仓库之后,我们就可以上传镜像了。 打标签  要上传镜像,第一步,要先重新打标签,将阿里云的仓库源的地址,仓库名,以及版本号重新打标签,生成新镜像。例如对已有的haproxy镜像重新打标签,因为我选择的是北京节点,所以打标命令如下:1docker tag haproxy-base:v1 registry.cn-beijing.aliyuncs.com/【命名空间名称】/【仓库名】:【版本号】 登陆  想上传或者下载镜像一般都需要授权才可以,这就要求我们要用有权限的帐号登陆,才可以上传镜像或者下载镜像。12345678root@DockerUbuntu:/opt/dockerfile/web/haproxy/2.0.5# docker login --username=【账号名】 registry.cn-beijing.aliyuncs.comPassword: WARNING! Your password will be stored unencrypted in /root/.docker/config.json.Configure a credential helper to remove this warning. Seehttps://docs.docker.com/engine/reference/commandline/login/#credentials-storeLogin Succeededroot@DockerUbuntu:/opt/dockerfile/web/haproxy/2.0.5# 上传  用docker images可以查看所有的镜像,选择已经打了阿里云网址的标签的镜像上传就可以了12345678910111213141516root@DockerUbuntu:/opt/dockerfile/web/haproxy/2.0.5# docker push registry.cn-beijing.aliyuncs.com/【命名空间名称】/【仓库名】:【版本号】The push refers to repository [registry.cn-beijing.aliyuncs.com/命名空间名/仓库名]0ec88a00d427: Pushed 6a6e6f03a1a5: Pushed 965bdb9c5299: Pushed a1d450e33837: Pushed 837dac687863: Pushed b39d6a9ec3e2: Pushed 1e7fbf47b8df: Pushed dc298319f184: Pushed e4809dffd3aa: Pushed b3cdf76b6336: Pushed 2fc5c4732662: Pushed 2d03b9db6c3f: Pushed 89169d87dbe2: Pushed haproxy: digest: sha256:f0b4157cd18498e4bc373e333e4fb0b85a65d00e03de2d65015b0d0da9099af6 size: 3049   这时就上传成功了 拉取镜像  其他主机登陆成功之后,通过拉取命令就可以从阿里云端下载镜像了。123456789101112131415161718[root@DockerCentOS ~]# docker pull registry.cn-beijing.aliyuncs.com/【命名空间名称】/【仓库名】:【版本号】haproxy: Pulling from xxxxxxxxxx/webac9208207ada: Already exists 75c124fe932b: Pull complete 9ef7eb04bb69: Pull complete c5f97c472240: Pull complete 9dc49af65399: Pull complete a745615abdba: Pull complete ddcf37c0f462: Pull complete 0c406d186167: Pull complete 246fafa1cb32: Pull complete 057e62247ad8: Pull complete 770d7edff222: Pull complete b4064e1ed3ec: Pull complete d0a103ae1f19: Pull complete Digest: sha256:f0b4157cd18498e4bc373e333e4fb0b85a65d00e03de2d65015b0d0da9099af6Status: Downloaded newer image for registry.cn-beijing.aliyuncs.com/xxxxxxxxxx/web:haproxyregistry.cn-beijing.aliyuncs.com/xxxxxxxxxx/web:haproxy   这时就可以在docker images的镜像列表中看到刚刚拉取的镜像了。 搭建私有仓库  阿里云镜像仓库虽然很方便,但是在生产环境中,每次都从云端拉取或者上传至云端仓库,太消耗企业带宽,有时候数据繁忙的时候,很有可能会堵塞业务,而且速度也较慢。所以企业中都会基于内部局域网搭建企业内部使用的私有仓库。一般搭建私有仓库有两种解决方案,一个是docker自带的Docker Registry,还有就是由vmware公司开源的harbor。 Docker Registry  Docker Registry 作为 Docker 的核心组件之一负责镜像内容的存储与分发, 客户端的 docker pull 以及 push 命令都将直接与 registry 进行交互,最初版本的 registry由Python实现,由于设计初期在安全性, 性能以及API的设计上有着诸多的缺陷,该版本在 0.9 之后停止了开发,由新的项目 distribution(新的 docker register 被称为 Distribution)来重新设计并开发下一代 registry,新的项目由 go 语言开发,所有的 API, 底层存储方式, 系统架构都进行了全面的重新设计已解决上一代registry 中存在的问题, 2016 年 4 月份 rgistry 2.0 正式发布, docker 1.6 版本开始支持 registry 2.0,而八月份随着 docker 1.8 发布, docker hub 正式启用 2.1 版本registry 全面替代之前版本 registry,新版 registry 对镜像存储格式进行了重新设计并和旧版不兼容, docker 1.5 和之前的版本无法读取 2.0 的镜像, 另外, Registry2.4 版本之后支持了回收站机制,也就是可以删除镜像了,在 2.4 版本之前是无法支持删除镜像的,所以如果你要使用最好是大于 Registry 2.4 版本的。  Docker Registry的优势就是比较小(25M),但是功能表比较简单。 下载 docker registry 镜像1234567891011[root@DockerCentOS ~]# docker pull registryUsing default tag: latestlatest: Pulling from library/registryc87736221ed0: Pull complete 1cc8e0bb44df: Pull complete 54d33bcb37f5: Pull complete e8afc091c171: Pull complete b4541f6d3db6: Pull complete Digest: sha256:8004747f1e8cd820a148fb7499d71a76d45ff66bac6a29129bfdbfdc0154d146Status: Downloaded newer image for registry:latestdocker.io/library/registry:latest 搭建单机仓库  先创建授权使用目录1mkdir -p /docker/auth   创建一个用户并创建密码文件12cd /dockerdocker run --entrypoint htpasswd registry -Bbn Mice 123456 > auth/htpasswd #创建一个用户并生成密码   验证用户名密码12[root@DockerCentOS docker]# cat auth/htpasswdMice:$2y$05$XqNS4BH3gkxodR9MQyhnIuL19uT4wfa6MjUgXvJUYuo0T0o0J8Tzy   从registry镜像中启动 docker registry,指定容器名称为registry1,挂载本地/docker/auth目录至容器的/auth目录,传递账号密码变量至容器。1234567docker run -d -p 5000:5000 --restart=always \\--name registry1 \\-v /docker/auth:/auth \\-e \"REGISTRY_AUTH=htpasswd\" \\-e \"REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm\" \\-e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd\\ registry   此时如果用我们创建的用户名密码尝试登陆了,记得IP或者域名后面要加5000端口,否则会报502错误。1234root@DockerUbuntu19:~# docker login 192.168.32.20Username: MicePassword: Error response from daemon: login attempt to http://192.168.32.20/v2/ failed with status: 502 Bad Gateway   不过很有可能当你加上5000端口,可能还会有报错。ヾ(≧O≦)〃嗷~1234root@DockerUbuntu19:~# docker login 192.168.32.20:5000Username: MicePassword: Error response from daemon: Get https://192.168.32.20:5000/v2/: http: server gave HTTP response to HTTPS client   这是因为我们每一个docker主机要设置允许insecure-registries,加上我们registry仓库的IP或者域名。  同样,可以修改/lib/systemd/system/docker.service启动脚本文件,或者/etc/docker/daemon.json文件,推荐修改/etc/docker/daemon.json文件。12345{ \"registry-mirrors\": [\"https://360k4x9i.mirror.aliyuncs.com\"], \"insecure-registries\": [\"192.168.32.19\",\"DockerCentOS20:5000\"], \"bip\": \"10.20.0.1/24\"}   然后重启docker服务,此时再尝试登陆,就回提示登陆成功。123456root@DockerUbuntu19:~# docker login DockerCentOS20:5000Username: MicePassword: WARNING! Your password will be stored unencrypted in /root/.docker/config.json.Configure a credential helper to remove this warning. Seehttps://docs.docker.com/engine/reference/commandline/login/#credentials-store   之后就与阿里云的镜像仓库使用方法相同了,不过速度上会快很多(毕竟内网),可以如果要上传镜像至regist仓库,先打好标签就可以了,下载写明下载仓库源,也就可以正常下载了。ヾ(=゚・゚=)ノ喵♪ Harbor  Harbor是一个用于存储和分发Docker镜像的企业级Registry服务器, 由vmware开源,其通过添加一些企业必需的功能特性,例如安全、标识和管理等, 扩展了开源 Docker Distribution。作为一个企业级私有Registry服务器,Harbor 提供了更好的性能和安全。提升用户使用Registry构建和运行环境传输镜像的效率。Harbor支持安装在多个Registry节点的镜像资源复制, 镜像全部保存在私有Registry中,确保数据和知识产权在公司内部网络中管控, 另外,Harbor也提供了高级的安全特性,诸如用户管理,访问控制和活动审计等 ,所以企业生产中,我们更多会选择Harbor。 下载harbor安装包  下载地址: https://github.com/vmware/harbor/releases  安装文档:https://github.com/vmware/harbor/blob/master/docs/installation_guide.md  本次以harbor的1.75版本为例,演示harbor的安装过程。  下载离线包,并解压。123cd /usr/local/srcwget https://storage.googleapis.com/harbor-releases/release-1.7.0/harbor-offline-installer-v1.7.5.tgztar xvf harbor-offline-installer-v1.7.5.tgz   也可以下载在线安装包,但里面没有镜像,之后还要去拉取镜像,不适合生产环境。 安装docker-compose  Harbor是需要使用docker编排工具docker-compose安装,docker-compose我们之后会专门介绍。而且对docker-compose版本是有要求的,可以查看上面的官方文档链接查看确切版本。  而如果使用包管理工具yum或者apt直接安装的docker-compose可能版本会比较低,我们这里采用python包管理工具python-pip来安装docker-compose。12apt install python-pip -ypip install docker-compose   然后可以通过命令docker compose -v看到docker-compose版本已经是最新稳定版1.25版本了。12root@DockerUbuntu19:/usr/local/src# docker-compose -vdocker-compose version 1.25.0, build b42d419 安装Harbor  我们习惯于将源码包放在/usr/local/src下,而将主程序放在/usr/local/目录中,所以我们可以将harbor目录的的路径改为/usr/local/harbor,可以通过mv命令,也可以通过软链接方式实现1ln -sv /usr/local/src/harbor /usr/local/   然后修改配置文件harbor.cfg12cd /usr/local/harborvim harbor.cfg   修改其中主机名(改为IP或者域名),管理员登录密码,及邮箱即可。12345678hostname = 192.168.32.19email_identity = harboremail_server = smtp.163.comemail_server_port = 25email_username = [email protected]_password = XXXXXXXXXXemail_from = admin <[email protected]>harbor_admin_password = XXXXXXXXXXXXX   然后更新配置文件中的环境变量到安装文件中,忘记更新环境变量会提示找不到环境变量文件ERROR: Couldn't find env file: /usr/local/src/harbor/common/config/core/env。1./prepare   此时就可以执行命令,来创建并安装Harbor了。1docker-compose up -d   或者执行官方脚本(两个都可以)1./install.sh   这时候就可以通过浏览器访问我们刚刚搭建的harbor仓库了,至此企业私有仓库就算是搭建好了。  管理员用户名为admin,密码为我们之前在配置文件中修改的harbor_admin_password。  后期如果需要修改配置文件信息,需要先停止harbor,然后修改信息后,更新配置文件信息至harbor服务,之后再试用docker-compose up -d启动harbor服务即可,流程如下。12345cd /usr/local/harbordocker-compose stopvim harbor.cfg./preparedocker-compose up -d   推送流程与使用其他云镜像仓库相同,先打标签,开头加上ip/仓库名,然后直接推送即可。","categories":[{"name":"Cloud","slug":"Cloud","permalink":"https://wudihechao.github.io/categories/Cloud/"}],"tags":[{"name":"Docker","slug":"Docker","permalink":"https://wudihechao.github.io/tags/Docker/"},{"name":"企业级应用","slug":"企业级应用","permalink":"https://wudihechao.github.io/tags/企业级应用/"},{"name":"容器","slug":"容器","permalink":"https://wudihechao.github.io/tags/容器/"},{"name":"镜像仓库","slug":"镜像仓库","permalink":"https://wudihechao.github.io/tags/镜像仓库/"}],"keywords":[{"name":"Cloud","slug":"Cloud","permalink":"https://wudihechao.github.io/categories/Cloud/"}]},{"title":"Docker(四)——容器跨主机网络配置","slug":"Docker(四)——容器跨主机网络配置","date":"2019-12-07T11:32:04.000Z","updated":"2019-12-09T02:47:35.879Z","comments":true,"path":"/blog/7e3785f.html","link":"","permalink":"https://wudihechao.github.io/blog/7e3785f.html","excerpt":"  跨主机互联是说 A 宿主机的容器可以访问 B 主机上的容器,但是前提是保证各宿主机之间的网络是可以相互通信的, 然后各容器才可以通过宿主机访问到对方的容器, 实现原理是在宿主机做一个网络路由就可以实现 A 宿主机的容器访问 B主机的容器的目的, 复杂的网络或者大型的网络可以使用 google 开源的 k8s 进行互联。本文之后将详细介绍docker网络配置,并演示容器跨主机通信的实现。","text":"  跨主机互联是说 A 宿主机的容器可以访问 B 主机上的容器,但是前提是保证各宿主机之间的网络是可以相互通信的, 然后各容器才可以通过宿主机访问到对方的容器, 实现原理是在宿主机做一个网络路由就可以实现 A 宿主机的容器访问 B主机的容器的目的, 复杂的网络或者大型的网络可以使用 google 开源的 k8s 进行互联。本文之后将详细介绍docker网络配置,并演示容器跨主机通信的实现。 docker网络基础  之前我们说过,当我们安装完docker应用后,就会自动添加一块虚拟的docker0网卡,并基于docker0网卡,提供了3种可选网络类型供创建的容器使用,分别是bridge(桥接),host(主机),none(无外部网络)。其中默认是采用桥接模式,容器中的网卡桥接在docker的网桥上,且通过DHCP自动分配IP,与docker0在同一网段。  当我们每创建一个容器,宿主机上就会新建一个网卡与容器中的网卡相对应,如下图所示。  容器桥接模式跨网络访问的结构示意图如下图所示  想实现不同宿主机上的容器跨主机肯定要经过宿主机来做网络转发,通过设置宿主机静态路由或者修改iptables规则来实现,可这时就面临一个问题:所有的容器服务默认的DHCP网段都是172.17.0.0/16网段,如果node1上的容器想直接访问node2宿主机上的容器,就会被直接当做docker0网桥的内部网段,数据报文根本都不会从node1主机的eth0网卡发出去,也根本到不了node2主机上。这种情况下,无论我们怎么修改iptables规则或者路由规则都无济于事的。所以我们想实现容器跨主机访问,首先要将不同宿主机上的容器分到不同的网段,然后才可以通过路由规则或者iptables进行跳转或转发。 修改docker网络的网段  我们可以对每一个宿主机上的docker配置文件进行修改,实现每个宿主机的docker容器都在不同网段的目的,可以通过以下方式修改(未避免影响, 先在各服务器删除之前创建的所有容器,docker rm -f `docker ps -a -q`)。 修改启动system脚本文件docker.service1vim /lib/systemd/system/docker.service   在ExecStart=选项结尾加上--bip=10.1.0.1/24,就指定了10.1.0.0/24网段,然后执行命令重新加载配置文件和重启服务。12systemctl daemon-reloadsystemctl restart docker   注意:不能写10.1.0.0/24,会报错,虽然写网段结尾是0,如10.1.0.0/24更符合我们的习惯,不过确实会报错,报错信息如下:123456Dec 07 16:27:58 DockerUbuntu dockerd[14794]: failed to start daemon: Error initializing network controller: Error creating default \"bridge\" network: failed to allocate gateway (10.10.0.0): Address already in useDec 07 16:27:58 DockerUbuntu systemd[1]: docker.service: Main process exited, code=exited, status=1/FAILUREDec 07 16:27:58 DockerUbuntu systemd[1]: docker.service: Failed with result 'exit-code'.Dec 07 16:27:58 DockerUbuntu systemd[1]: Failed to start Docker Application Container Engine.-- Subject: Unit docker.service has failed-- Defined-By: systemd 也可以修改daemon.json文件,在里面添加"bip": "10.2.0.1/24",如下所示(上面那个是我的阿里云加速器链接,注册阿里账号免费获取,之前文章有详细介绍,需改成自己的或者删掉):123456vim /etc/docker/daemon.json{ \"registry-mirrors\": [\"https://xxxxxxxx.mirror.aliyuncs.com\",\"https://registry.docker-cn.com\"], \"bip\": \"10.1.0.1/24\"}   daemon.json文件是json数据格式,需要遵守json语法,换行记得要加,逗号。  直接重启docker服务后生效1systemctl restart docker   此时看网卡的ip就已经变为了我们设置的网段,之后创建的容器服务器就会自动获取我们设置好的网段中的IP了。123456789root@DockerUbuntu:~# ifconfigdocker0: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500 inet 10.10.0.1 netmask 255.255.255.0 broadcast 10.10.0.255 inet6 fe80::42:25ff:fe2b:ecbc prefixlen 64 scopeid 0x20<link> ether 02:42:25:2b:ec:bc txqueuelen 0 (Ethernet) RX packets 0 bytes 0 (0.0 B) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 41 bytes 3526 (3.5 KB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0   同样的操作,将node2宿主机中的docker0网段设置为10.2.0.0/24 修改静态路由  我这里node1的eth0网卡ip为192.168.32.19,node2的eth0网卡ip为192.168.32.20,且两个宿主机之前网络是可以通过eth0网卡相互连接的。添加路由规则如下:  在node1上添加静态路由1ip r add 10.2.0.0/24 via 192.168.32.20 dev eth0   在node2上添加静态路由1ip r add 10.1.0.0/24 via 192.168.32.19 dev eth0 修改iptables规则  宿主机如果为centos7,则不需要修改iptables规则,而宿主机如果为ubuntu系统则需要添加forward规则来放行。我仔细看了下这两个系统的iptables规则,发现在centos系统docker创建的iptables规则中对Chain FORWARD是默认ACCEPT,而ubuntu系统中docker创建的iptables对Chain FORWARD是默认DROP  之后两边各启动一个容器就可以实现相互通信或访问了。 docker网络进阶  之前我们演示了通过docker0网卡的桥接方式实现了容器跨主机访问。我们通过修改docker0网卡的网段设置来实现,每个主机上容器的网段不同。  这种实现方式有个问题就是,但当每次我们修改了docker0网段之后,如果之后打算变更网段,之前的容器都将无法与docker0网桥桥接,导致网络不通,不能使用。  对此我们有一个更灵活的方案来实现容器的跨主机通信。那就是我们还可以通过创建一个或多个自定义网络,将新创建的每个容器指定连接到我们创建的这个网络中,这样他们的网段就是我们设置的这个网络的网段,实现每个主机上容器网段都不相同。 创建自定义网络  可以将我们之前对docker0网卡的修改还原了的(当然,也可以不修改,出于控制变量方便观察考虑,建议修改回去)。  我们可以通过docker network create命令来创建一个自定义网络123456789101112131415161718192021222324root@DockerUbuntu:~# docker network create --helpUsage: docker network create [OPTIONS] NETWORKCreate a networkOptions: --attachable Enable manual container attachment --aux-address map Auxiliary IPv4 or IPv6 addresses used by Network driver (default map[]) --config-from string The network from which copying the configuration --config-only Create a configuration only network -d, --driver string Driver to manage the Network (default \"bridge\") --gateway strings IPv4 or IPv6 Gateway for the master subnet --ingress Create swarm routing-mesh network --internal Restrict external access to the network --ip-range strings Allocate container ip from a sub-range --ipam-driver string IP Address Management Driver (default \"default\") --ipam-opt map Set IPAM driver specific options (default map[]) --ipv6 Enable IPv6 networking --label list Set metadata on a network -o, --opt map Set driver specific options (default map[]) --scope string Control the network's scope --subnet strings Subnet in CIDR format that represents a network segment   例如我们在node1创建一个名为web1的桥接网络,网段为10.10.0.0/24,设置网关为10.10.0.1(可设置为此网段内任意ip)。123456789root@DockerUbuntu:~# docker network create -d bridge --subnet 10.10.0.0/24 --gateway 10.10.0.1 web1a817cf36502eea3469e1cb4b9b7577044f8dce96f015ba57a47f6809c00d72c7root@DockerUbuntu:~# docker network lsNETWORK ID NAME DRIVER SCOPEafd91b2e1731 bridge bridge local241d3e94a6b3 host host local7cc9cf9eb69e none null locala817cf36502e web1 bridge localroot@DockerUbuntu:~#   可以用docker network ls(或docker network list)看到网络类型多了一种,也就是我们刚刚创建的web1类型。而用ifconfig或者ip a命令也可以看到我们的网卡设备里多了一个br-a817cf36502e,ip也恰好是我们指定的10.10.0.0/24网段。  此时我们就可以通过--net选项指定我们刚刚创建的web1网络,来创建并启动容器了。123root@DockerUbuntu:~# docker run -it -d -p 8080:8080 -p 8009:8009 --net=web1 tomcat-app1:v1afc1e3db8a6a670d30cdd0756af65da74895976ce5ebbf876329b04b452a3710root@DockerUbuntu:~#   同样,在宿主机node2上也创建一个自定义网络web2,然后新创建的容器,也指定网络为web2,再设置静态路由和修改iptables规则放行,也可以实现容器间跨主机访问。","categories":[{"name":"Cloud","slug":"Cloud","permalink":"https://wudihechao.github.io/categories/Cloud/"}],"tags":[{"name":"Docker","slug":"Docker","permalink":"https://wudihechao.github.io/tags/Docker/"},{"name":"企业级应用","slug":"企业级应用","permalink":"https://wudihechao.github.io/tags/企业级应用/"},{"name":"容器","slug":"容器","permalink":"https://wudihechao.github.io/tags/容器/"},{"name":"跨主机通信","slug":"跨主机通信","permalink":"https://wudihechao.github.io/tags/跨主机通信/"},{"name":"容器网络","slug":"容器网络","permalink":"https://wudihechao.github.io/tags/容器网络/"}],"keywords":[{"name":"Cloud","slug":"Cloud","permalink":"https://wudihechao.github.io/categories/Cloud/"}]},{"title":"Docker(二)——基础命令详解","slug":"Docker(二)——基础命令详解","date":"2019-12-06T07:02:09.000Z","updated":"2019-12-09T02:47:35.870Z","comments":true,"path":"/blog/5c39f4ef.html","link":"","permalink":"https://wudihechao.github.io/blog/5c39f4ef.html","excerpt":"  安装完Docker的服务,我们就可以开始使用Docker了。","text":"  安装完Docker的服务,我们就可以开始使用Docker了。 Docker镜像  之前我们提到,docker是一个运行容器的工具,可以单独隔离每个服务的运行环境,达到互不干扰和节约资源的目的。而docker运行的容器,是基于一层一层的镜像联合挂载构建而成。所以我们需要先有镜像。  所谓镜像,其实可以理解为,一个个的最简化的安装包,里面只集成了一些必备的程序和文件,且每一层和每一层镜像是可以相互一样的,大大的节约了空间,提高了资源利用率。举个最简单的例子,我们从官方下载一个centos系统的镜像包centos:apline,大小才5.55兆,而centos差不多至少200兆了,而centos安装的ISO镜像文件也都差不多1G左右。这是因为容器使用的镜像系统,需要依赖宿主机内核来运行,容器本身是没有内核的,只包含一些基础功能命令而已。可用docker images查看本地镜像,从大小来看,就知道容器确实很精简。12345root@DockerUbuntu:~# docker imagesREPOSITORY TAG IMAGE ID CREATED SIZEalpine latest 965ea09ff2eb 6 weeks ago 5.55MBcentos latest 0f3e07c0138f 2 months ago 220MBcentos 7.6.1810 f1cb7c7d58b7 8 months ago 202MB   我们可以在docker官方镜像仓库https://hub.docker.com查看官方镜像,也可以通过命令直接搜索centos的可用容器1docker search centos   注意:官方仓库也有很多是个人的镜像,为避免未知风险,一定要采用官方镜像,千万别使用安全性未知或来源不明的的镜像。一般排在第一个是官方镜像。  使用命令docker pull centos就可以自动从docker官方镜像仓库docker.io,下载centos镜像了,因为我们没有指定版本标签,所以这条命令会默认下载centos:latest版本,也就是最新版。生产中我们出于稳定性和便于管理,都会推荐使用指定的稳定版本,而不会采用latest版本(,随着版本发型,之前的latest版本和几个月之后的latest版很有可能就不是一个版本,不利于规范化统一)。我们本次以CentOS7.6中的最新版centos:7.6.1810最为本次演示的版本。下载镜像命令如下:1docker pull centos:7.6.1810   使用docker image rm [ OPTION ] 容器ID可以删除不要的镜像,如果已有容器基于此镜像启动,则会提示错误,无法删除,需先停止容器,或直接加-f选项强制删除镜像,此时容器也会被停止。注意,当容器被停止时,上面的数据也都会丢失,所以需要谨慎关闭容器和删除镜像。  此外,docker官方镜像站,因为在国外(美国,不过应该是有CDN或者国内镜像加速站点的,不过还是有点点慢),肯定不如阿里的容器镜像仓库速度快。所以我们可以配置阿里的镜像加速器,需要注册阿里账号。  进入阿里网站http://cr.console.aliyun.com,登陆之后,可以看到阿里的镜像仓库(之后再细说)还有镜像加速器,点击镜像加速器,可看到如图所示界面:  执行将下面代码,便可实现镜像加速了。12345678sudo mkdir -p /etc/dockersudo tee /etc/docker/daemon.json <<-'EOF'{ \"registry-mirrors\": [\"https://xxxxxxxx.mirror.aliyuncs.com\"]}EOFsudo systemctl daemon-reloadsudo systemctl restart docker   之后使用docker info命令就可以看到已经添加镜像仓库成功 Docker容器  有了镜像之后我们就可以从中运行容器了。基础的命令有docker pull、docker push、docker create、docker run、docker ps、docker rm、docker start、docker stop、docker images、docker exec、docker inspect等等 pull push12345678910root@DockerUbuntu:~# docker pull --helpUsage: docker pull [OPTIONS] NAME[:TAG|@DIGEST]Pull an image or a repository from a registryOptions: -a, --all-tags Download all tagged images in the repository --disable-content-trust Skip image verification (default true) -q, --quiet Suppress verbose output   docker pull命令我们之前也使用过,可以用来拉镜像,相当于命令docker image pull。根据官方帮助信息,我们可以知道,docker pull 如果要拉去指定版本,需要加:tag版本标签,加-a选项可以拉取所有镜像,不过生产中一般都是用的时候公司内部的本地镜像仓库Harbor,所以拉取镜像时,格式要跟公司Harbor服务器的IP或者域名,指定拉取镜像的源仓库,如果不加,则默认从docker官网拉取。镜像名称格式为:Harbor IP/项目名/image 名字:版本号1docker pull 172.18.32.101/centos/centos-base:v1   docker push是用来推送本地镜像至仓库的,相当于命令docker image push,用法格式与pull相似。使用pull推送至公司Harbor时,需要先在docker启动文件/lib/systemd/system/docker.service的ExecStart选项结尾加入参数--insecure-registry,表示加入不安全的镜像仓库,可加多个,示例如下。1ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock --insecure-registry 192.168.32.19 --insecure-registry 192.168.32.20 tag123456root@DockerUbuntu:~# docker tag --helpUsage: docker tag SOURCE_IMAGE[:TAG] TARGET_IMAGE[:TAG]Create a tag TARGET_IMAGE that refers to SOURCE_IMAGEroot@DockerUbuntu:~#   docker tag命令可以用来打标记,格式如上。一般为了区分不同版本,我们会在每次制作完镜像后打上不同的标记,而且要分发到不同的镜像仓库时也要重新打一个对应仓库IP/项目名的镜像,才可以push或pull。 run  docker run可以算是最常用的docker基础命令了,意思是创建并运行容器,相当于docker create和docker start的组合。用法格式如下。1docker run [ OPTION ] 镜像ID [ CMD ] OPTION  常用选项有-i、-t、-d、-p、-v、-e、--name、--net。 -i,–interactive, Keep STDIN open even if not attached  以交互式方式运行。不过只加-t是没法实现交互式的,通常需要-t参数配合,来给容器加一个伪终端实现交互式。 -t,–tty, Allocate a pseudo-TTY  给容器分配一个伪终端,与-i配合使用,通常写作-it。 -d,–detach, Run container in background and print container ID  使容器启动时后台运行,如果不加这个选项,则会占据宿主机当前终端,服务类容器等交互式容器一般都会加上这个选项。 -p,–publish, list Publish a container’s port(s) to the host  映射本地端口到容器的指定端口,同时映射多个端口,则写多个-p 宿主机端口:容器端口选项。 -v,–volume, list Bind mount a volume  使用-v 参数, 将宿主机目录映射到容器内部, 默认是可读写的。-v SOUCE:DEST:ro后面加ro则可实现只读。 -e,–env, list Set environment variables  可用-e选项为容器添加启动时的环境变量。虽然大多环境变量都是在制作容器时就写进容器里,不过此选项应用在环境变量经常变化的场景,方面修改。 --name,Assign a name to the container  给创建的容器命名。同一个宿主机上的容器之间可以通过自定义的容器名称相互访问,由于容器在启动的时候其内部 IP 地址是 DHCP 随机分配的,所以如果通过内部访问的话,自定义名称是相对比较固定的,因此设置容器别名比较适用于此场景。 –link,Add link to another container  为容器设置一个别名,相当于一个路径名。自定义的容器名称可能后期会发生变化, 那么一旦名称发生变化,程序之间也要随之发生变化,比如程序通过容器名称进行服务调用, 但是容器名称发生变化之后再使用之前的名称肯定是无法成功调用, 每次都进行更改的话又比较麻烦, 因此可以使用自定义别名的方式解决,即容器名称可以随意更,只要不更改别名即可。 --net  为容器指定网络。容器网络类型,常见的为bridge、host、null,也可以自己创建自定义网络。可用docker network list查看当前已有的网络。12345root@DockerUbuntu:~# docker network listNETWORK ID NAME DRIVER SCOPE1eb441f702d2 bridge bridge local241d3e94a6b3 host host local7cc9cf9eb69e none null local   docker服务安装完成后,默认在每个宿主机会生成一个名称为 docker0 的网卡其 IP 地址都是 172.17.0.1/16,并且会生成三种不同类型的网络,也就是bridge、host和null,分别代表网桥(桥接)、宿主机网络和无网络访问。  如果不加--net选项则默认使用bridge,相当于桥接在宿主机的docker0网卡上。  host网络就是指容器使用宿主机网络,所以端口就不能重复使用,多个使用host网络的容器间端口也不能重复。  null网络指容器只有回环网卡,没有外部网卡,无法与外部交流交流,不常使用,适用于某些不需要与外界通信的数据计算或图形、数据处理服务。  还有一种比较特的类型就是container模式,就是指定与一个已有容器共享网络,格式为--net=container:指定名称或 ID 。使用此模式创建的容器需指定和一个已经存在的容器共享一个网络,而不是和宿主机共享网,新创建的容器不会创建自己的网卡也不会配置自己的 IP,而是和一个已经存在的被指定的容器东西 IP 和端口范围,因此这个容器的端口不能和被指定的端口冲突, 除了网络之外的文件系统、进程信息等仍然保持相互隔离,两个容器的进程可以通过 lo 网卡及容器 IP 进行通信。 CMD  docker run后面跟的CMD表示指定启动容器的命令,如果不指定,则为容器默认命令。可用 命令docker inspect 容器ID查看容器详细参数查看容器创建时的的CMD。  因为容器中没有守护进程systemd,所以进程编号PID=1的根进程,就是我们启动容器时指定的CMD命令或创建容器设定的默认CMD。如果这个PID=1的守护进程结束,则整个容器就将被关闭。所以我们一般会指定一个可以占据前台终端的服务来作为守护进程,例如bash或者tail -f /etc/hosts命令,对于后台nginx等服务来说,想让他们作为守护进程启动容器,需要将在后台执行的这个选项关闭,如nginx可以通过docker run -it -d nginx:alpine nginx -g "daemon off;"命令,加上-g "daemon off"选项传递参数或者在配置文件中设置daemon off来关闭后台执行选项,否则容器启动就会因为没有前台进程而终止。为了方便我们修改配置或者重启服务,企业生产中我们都采用tail -f /etc/hosts来作为前台进程,这样我们重启服务容器就不受影响了。 ps123456789101112131415root@DockerUbuntu:~# docker ps --helpUsage: docker ps [OPTIONS]List containersOptions: -a, --all Show all containers (default shows just running) -f, --filter filter Filter output based on conditions provided --format string Pretty-print containers using a Go template -n, --last int Show n last created containers (includes all states) (default -1) -l, --latest Show the latest created container (includes all states) --no-trunc Don't truncate output -q, --quiet Only display numeric IDs -s, --size Display total file sizes   docker ps命令比较简单,较长使用的参数是-a、-q,分别是显示所有容器(包括为未运行容器,ps默认只显示正在运行的容器)和只显示容器ID。也可以使用docker ps -aq来显示所有容器的ID,可配合其他命令如docker rm命令使用。docker rm -f `docker ps -aq`删除本地所有容器(很危险,小心操作!)。 rm  docker rm命令是用来删除容器的,如果容器正在运行,使用docker rm命令删除时会报错提示Error response from daemon: You cannot remove a running container XXX. Stop the container before attempting removal or force remove,告诉我们无法删除运行中的容器,需要先将容器停止或者强制删除,使用docker rm -f命令强制删除,或者使用下面的停止命令 start stop  docker start 容器ID启动容器,可以加选项-i,表示启动容器并输出容器内的标准输出至宿主机终端。需要注意的是,如果当时用docker run或者docker create命令创建容器时,没有加-it或-d等一些其它选项或者参数的话,在start容器这一步也没法作出更改了的。容器怎么创建的,启动时就会按照创建时设定好的模式运行,这些是没办法再次修改了的,包括启动CMD。  docker stop 容器ID终止容器。需要注意:终止容器时,容器内的数据都将丢失,在备份重要数据之前不要终止容器。 images  docker images命令其实相当于docker image ls,显示本地所有镜像。docker image CMD还有很多,之后介绍镜像管理的时候 在详细介绍。 exec  docker exec命令可以向一个已运行容器发送命令。用法如下所示。123456789101112131415[root@DockerCentOS ~]# docker exec --helpUsage: docker exec [OPTIONS] CONTAINER COMMAND [ARG...]Run a command in a running containerOptions: -d, --detach Detached mode: run command in the background --detach-keys string Override the key sequence for detaching a container -e, --env list Set environment variables -i, --interactive Keep STDIN open even if not attached --privileged Give extended privileges to the command -t, --tty Allocate a pseudo-TTY -u, --user string Username or UID (format: <name|uid>[:<group|gid>]) -w, --workdir string Working directory inside the container   最常使用的格式是docker exec -it 容器ID bash/sh,可以进入正在运行的容器。","categories":[{"name":"Cloud","slug":"Cloud","permalink":"https://wudihechao.github.io/categories/Cloud/"}],"tags":[{"name":"Docker","slug":"Docker","permalink":"https://wudihechao.github.io/tags/Docker/"},{"name":"企业级应用","slug":"企业级应用","permalink":"https://wudihechao.github.io/tags/企业级应用/"},{"name":"容器","slug":"容器","permalink":"https://wudihechao.github.io/tags/容器/"}],"keywords":[{"name":"Cloud","slug":"Cloud","permalink":"https://wudihechao.github.io/categories/Cloud/"}]},{"title":"Docker(三)——镜像制作","slug":"Docker(三)——镜像制作","date":"2019-12-06T07:02:09.000Z","updated":"2019-12-09T02:47:35.882Z","comments":true,"path":"/blog/ddd838e7.html","link":"","permalink":"https://wudihechao.github.io/blog/ddd838e7.html","excerpt":"  在docker使用过程中,其实大部分时间都是花在了打镜像上,因为容器本身底层不可写,顶层可读写缺无法持久化性质,我们如果对容器进行了修改,想要进行横向扩容,快速部署时,一般需要重新制作镜像,在分发到其他主机或终端。(虽然也可以将数据储存在NFS和宿主机本地,而不是容器内部来方便的修改配置文件及保存数据等。)  docker中镜像的制作方式一般手工修改后导出和通过Dockerfile生成两种方式。","text":"  在docker使用过程中,其实大部分时间都是花在了打镜像上,因为容器本身底层不可写,顶层可读写缺无法持久化性质,我们如果对容器进行了修改,想要进行横向扩容,快速部署时,一般需要重新制作镜像,在分发到其他主机或终端。(虽然也可以将数据储存在NFS和宿主机本地,而不是容器内部来方便的修改配置文件及保存数据等。)  docker中镜像的制作方式一般手工修改后导出和通过Dockerfile生成两种方式。 手动制作镜像  因为镜像本身的不可修改性,有时候官方镜像中使用的工具的版本可能不是那么符合我们的生产环境,我们就需要自己制作镜像了。一般来说,我们都是基于官方镜像,作出修改来符合自身实际场景中使用,然后在导出保存为我们自己的镜像。  以一个tomcat容器为例,我们如果需要tomcat8的容器,可以直接从官网拉取tomcat8的镜像docker pull tomcat:8.5.49-jdk8-openjdk修改完成后,还可以使用命令docker commit将已有容器制作为镜像.1234567891011root@DockerUbuntu:/opt/dockerfile/web/tomcat/tomcat-apps/app1# docker commit --helpUsage: docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]Create a new image from a container's changesOptions: -a, --author string Author (e.g., \"John Hannibal Smith <[email protected]>\") -c, --change list Apply Dockerfile instruction to the created image -m, --message string Commit message -p, --pause Pause container during commit (default true)   -a添加镜像制作人信息,-m添加备注信息,-p选项是默认选项,在制作为镜像时暂停容器,-c使用Dockerfile指令来创建镜像,Dockerfile之后我们会详细讲解。例如1docker commit -a \"[email protected]\" -m \"tomcat app1 v1\" --change=\"EXPOSE 8080 8009\" f5f8c13d0f9f centos-tomcat-app1:v1 Dockerfile  不过如果业务场景要求的配置场景要修改nginx的编译参数或者要求底层是centos7系统这就没法更改了(官方镜像一般都是debian系统),我们只能修改最上层的镜像。此时我们可以通过分层构建的方式来制作镜像(,毕竟docker镜像本来就是分层构建的)。  DockerfileDockerFile 可以说是一种可以被 Docker 程序解释的脚本, DockerFile 是由一条条的命令组成的,每条命令对应 linux 下面的一条命令, Docker 程序将这些 DockerFile 指令再翻译成真正的 linux 命令,其有自己的书写方式和支持的命令, Docker 程序读取 DockerFile 并根据指令生成 Docker 镜像,相比手动制作镜像的方式, DockerFile 更能直观的展示镜像是怎么产生的,有了写好的各种各样 DockerFile 文件,当后期某个镜像有额外的需求时,只要在之前的DockerFile 添加或者修改相应的操作即可重新生成新的 Docke 镜像,避免了重复手动制作镜像的麻烦。  Docker中常用到的命令令有FROM(指定基础镜像名称),MAINTAINER(镜像作者署名及联系方式),USER(切换用户身份,初始一般为root),WORKDIR(指定或切换工作目录),ADD(将当前宿主机目录的文件拷贝至容器指定位置,tar包可以自动解压),RUN(运行命令,其实就是shell命令,可执行多条,用&&符号连接),ENV(设置环境变量),CMD(设置默认镜像启动命令,要可以占据前台,否则基于此镜像启动的容器会直接停止),之后我们结合实际例子一一说明。   例如我们使用Dockerfile来分层构建定制的tomcat镜像来运行app1服务(当然,也可以一步到位),步骤如下: 构建目录架构我们通常将Dockerfile文件都放置在/opt/目录下 1mkdir /opt/dockerfile/{web/{nginx,tomcat,jdk},system/{centos,ubuntu,redhat}} -pv 构建系统镜像 1234cd /opt/dockerfile/system/centosmkdir 7.6cd 7.6docker pull centos:7.6.1810   创建Dockerfile文件,注意D要大写1234567vim Dockerfile#CentOS 7.6 镜像FROM centos:7.6.1810MAINTAINER Mice [email protected] rpm -ivh http://mirrors.aliyun.com/epel/epel-release-latest-7.noarch.rpmRUN yum install -y vim wget tree lrzsz gcc gcc-c++ automake pcre pcre-devel zlib zlib-devel openssl openssl-devel iproute net-tools iotopCMD [\"bash\"]   创建制作镜像脚本,来生成镜像。当然,也可以直接使用docker build命令配合-t参数(指定标签名)直接将当前目录的Dockerfile制作为镜像,使用脚本是为了日后修改不至于每次名称都不一样,可以保证每次打的镜像的名称和版本号统一,否则以后镜像多了会乱(脚本中也可以在标签中加上时间)。123vim build_centos.sh#!/bin/bashdocker build -t centos-base:v7.6.1810 .   当前目录结构为123456root@DockerUbuntu:/opt/dockerfile/system/centos/7.6# tree.├── build_centos.sh└── Dockerfile0 directories, 2 files   执行命令bash build_centos.sh来创建第一层镜像centos-base:v7.6.181012345678910111213141516171819root@DockerUbuntu:/opt/dockerfile/system/centos/7.6# bash build_centos.sh Sending build context to Docker daemon 3.072kBStep 1/5 : FROM centos:7.6.1810 ---> f1cb7c7d58b7Step 2/5 : MAINTAINER Mice [email protected] ---> Using cache ---> 3899d2446806Step 3/5 : RUN rpm -ivh http://mirrors.aliyun.com/epel/epel-release-latest-7.noarch.rpm ---> Using cache ---> 5a72857ed63dStep 4/5 : RUN yum install -y vim wget tree lrzsz gcc gcc-c++ automake pcre pcre-devel zlib zlib-devel openssl openssl-devel iproute net-tools iotop ---> Using cache ---> 705fed38cb94Step 5/5 : CMD [\"bash\"] ---> Running in aea451be0461Removing intermediate container aea451be0461 ---> 160b9544f121Successfully built 160b9544f121Successfully tagged centos-base:v7.6.1810   有时到yum那步会提示报错,无法解析IP。多执行几次脚本,多试几次就可以了。他会有缓存自动保存镜像,已经写好的层数会自动缓存的,如上面的---> Using cache。 构建适合版本的jdk镜像123cd /opt/dockerfile/web/jdk/mkdir 8u212cd 8u212   将准备好的jdk压缩包jdk-8u212-linux-x64.tar.gz放入此目录,然后还是编写Dockerfile以及build_jdk.sh脚本1234567891011121314vim Dockerfile#JDK 8u212FROM centos-base:v7.6.1810MAINTAINER Mice [email protected] jdk-8u212-linux-x64.tar.gz /usr/local/src/ADD env.sh /etc/profile.d/RUN ln -sv /usr/local/src/jdk1.8.0_212 /usr/local/jdk && groupadd www -g 2019 && useradd www -u 2019 -g wwwENV JAVA_HOME /usr/java/defaultENV PATH $JAVA_HOME/bin:$PATHENV JRE_HOME $JAVA_HOME/jreENV CLASSPATH $JAVA_HOME/lib/:$JRE_HOME/lib/RUN rm -rf /etc/localtime && ln -snf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo \"Asia/Shanghai\" > /etc/timezone 123vim build_jdk.sh#!/bin/bashdocker build -t jdk-base:v1.8.0_212 .   需要注意的是,因为ENV环境变量是当前用户(当前终端)有效,也就是说对于容器本身来说,他运行的环境变量是已经通过ENV可以设置好,可是如果发生故障,我们需要连接进入容器时,这个ENV就对我们当前终端无效了,我们可能就无法使用那些ENV设置了的PATH变量了,所以我们需要添加一个环境变量配置文件至/etc/profile.d目录(也可直接替换profile文件),以便这些环境变量在我们连接至容器后也可以生效。12345vim env.shJAVA_HOME=/usr/local/jdkJRE_HOME=$JAVA_HOME/jreCLASSPATH=$JAVA_HOME/lib/:$JRE_HOME/lib/PATH=$PATH:$JAVA_HOME/bin   目录结构如下所示12345678root@DockerUbuntu:/opt/dockerfile/web/jdk/8u212# tree.├── build_jdk.sh├── Dockerfile├── env.sh└── jdk-8u212-linux-x64.tar.gz0 directories, 4 files   还是通过build脚本,构建jdk容器。 构建适合版本的tomcat镜像先创建版本目录(,为日后可能需要不同版本的tomcat弄好框架)。123cd /opt/dockerfile/web/tomcat/mkdir 8.5.47cd 8   将准备好的tomcat源码包拷到这个目录,或者wget下载1wget http://mirrors.tuna.tsinghua.edu.cn/apache/tomcat/tomcat-8/v8.5.47/bin/apache-tomcat-8.5.47.tar.gz   制作Dockerfile12345678910111213141516vim Dockerfile#tomcat 8-jdk 1.8.0_212-centos 7.6FROM jdk-base:v1.8.0_212MAINTAINER Mice [email protected] TZ \"Asia/Shanghai\"ENV LANG en_US.UTF-8ENV TERM xtermENV TOMCAT_MAJOR_VERSION 8ENV TOMCAT_MINOR_VERSION 8.5.47ENV CATALINA_HOME /apps/tomcatENV APP_DIR ${CATALINA_HOME}/webappsRUN mkdir /appsADD apache-tomcat-8.5.47.tar.gz /apps/RUN ln -sv /apps/apache-tomcat-8.5.47 /apps/tomcat   老规矩,创建build脚本123vim build_tomcat.sh#!/bin/bashdocker build -t tomcat-base:v8.5.47 .   然后执行脚本打镜像~ 放置项目app1至指定目录构建镜像  到现在,tomcat容器的环境都配置好了,不过里面还没有跑服务,所以可以针对不同的业务,创建不同的镜像,而他们底层都是共用的一个基础镜像tomcat-base:v8.5.47,所有底层镜像因为都是只读的,所以可以复用而互不干扰,也不会重复占用多余的空间。还是先创建业务APP目录。12mkdir -pv /opt/dockerfile/web/tomcat/myapps/app1/cd /opt/dockerfile/web/tomcat/myapps/app1/   因为当app1有变化时,tomcat服务可能会需要重启才会生效变化,而如果tomcat服务是容器的启动进程(即PID=1的进程)时,重启tomcat会导致容器终止,容器里面在运行的数据及session都会丢失。所以我们使用tail -f命令来作为这个容器的守护进程来启动容器。我们可以通过构建一个脚本run_tomcat.sh来实现,启动tomcat并让tail -f 最为前台进程。1234vim run_tomcat.sh#!/bin/bashsu - www -c \"/apps/tomcat/bin/catalina.sh start\"su - www -c \"tail -f /etc/hosts\"   且为了安全考虑,我们打算让tomcat服务以www用户身份启动,所以在构建容器时,需要注意权限问题,Dockerfile如下:12345678vim Dockerfile# tomcat-appsFROM tomcat-base:v8.5.47ADD run_tomcat.sh /apps/tomcat/bin/run_tomcat.shADD app1/* /apps/tomcat/webapps/myapp/RUN chown www.www /apps/ -REXPOSE 8080 8009CMD [\"/apps/tomcat/bin/run_tomcat.sh\"]   而且我们把run_tomcat.sh传进去后,要以脚本启动的话,要对脚本加执行权限,这样传进去的时候也是有执行权限的。1chmod +x run_tomcat.sh   然后构建build脚本123vim build_app1.sh#!/bin/bashdocker build -t tomcat-app1:v1 .   然后将APP1的程序代码拷贝至当前目录,结构示意图如下:123456789root@DockerUbuntu:/opt/dockerfile/web/tomcat/myapps/app1# tree.├── build_app1.sh├── Dockerfile├── app1│ └── index.html└── run_tomcat.sh1 directory, 4 files   就可以执行build脚本打镜像啦。1bash build_app1.sh   需要注意的是,启动镜像时记得加端口映射,命令如下1docker run -it -d -p 8080:8080 -p 8009:8009 tomcat-app1:v1   此时通过ss -tanl命令就可以看到8080、80009端口已经被docker proxy监听了。说明服务启动成功。如果宿主机为centos,或者redhat(ubuntu默认是开启的),可能会需要先打开内核参数ip_forward选项,否则会报错网络不可用:1WARNING: IPv4 forwarding is disabled. Networking will not work.   那就开启IP转发。12vim /etc/sysctl.confnet.ipv4.ip_forward=1   然后sysctl -p生效就可以正常使用容器网络了。 注意事项  需要注意的是,RUN命令是类似启动一个新的进程或者是shell,来执行每一次命令,执行完毕后此次RUN进程结束,下一次是一个全新的RUN进程了,相互之间不会联系。这么说可能大家无法理解,举个最简单的例子吧,就是当我们想要编译安装haproxy的时候,需要进入编译目录然后执行make命令,编译完后还要在编译目录执行make install命令。所以可以写成1RUN cd /usr/localk/haproxy-2.0.5 && make --xxx参数选项省略xxxx && make install   但是如果我们像写脚本那样,写成123RUN cd /usr/localk/haproxy-2.0.5RUN make --xxx参数选项省略xxxxRUN make install   或者12RUN cd /usr/localk/haproxy-2.0.5 && make --xxx参数选项省略xxxxRUN make install   就会报错找不到路径,因为没一条命令都是一层独立的镜像,每层镜像开始都会在默认的初始目录,所以如果打算将make和make install分开写,则两次都加cd /usr/local/haproxy-2.0.5,或者直接切换工作目录,如下所示。1234567891011WORKDIR /usr/local/src/haproxy-2.0.5RUN make ARCH=x86_64 \\TARGET=linux-glibc USE_PCRE=1 \\USE_OPENSSL=1 \\USE_ZLIB=1 \\USE_CPU_AFFINITY=1 \\USE_LUA=1 \\LUA_INC=../lua-5.3.5/src/ \\LUA_LIB=../lua-5.3.5/src/ \\PREFIX=/apps/haproxyRUN make install PREFIX=/apps/haproxy   WORKDIR和USER很类似,都是修改后,对后续Dockerfile指令有效。且WORKDIR还支持相对路径,例如123WORKDIR /aWORKDIR bWORKDIR c   则最终的工作目录为/a/b/c。","categories":[{"name":"Cloud","slug":"Cloud","permalink":"https://wudihechao.github.io/categories/Cloud/"}],"tags":[{"name":"Docker","slug":"Docker","permalink":"https://wudihechao.github.io/tags/Docker/"},{"name":"Dockerfile","slug":"Dockerfile","permalink":"https://wudihechao.github.io/tags/Dockerfile/"},{"name":"企业级应用","slug":"企业级应用","permalink":"https://wudihechao.github.io/tags/企业级应用/"},{"name":"容器","slug":"容器","permalink":"https://wudihechao.github.io/tags/容器/"}],"keywords":[{"name":"Cloud","slug":"Cloud","permalink":"https://wudihechao.github.io/categories/Cloud/"}]},{"title":"Docker(一)——基础概念及部署","slug":"Docker(一)——基础概念及部署","date":"2019-12-04T01:58:21.000Z","updated":"2019-12-04T02:06:06.590Z","comments":true,"path":"/blog/eaa1ac6e.html","link":"","permalink":"https://wudihechao.github.io/blog/eaa1ac6e.html","excerpt":"  在企业生产应用中,docker容器技术及k8s的编排管理工具的使用率越来越高,这项技术甚至已经改变了很多企业的架构与框架流程,因为容器技术的出现,可以将应用以集装箱的方式打包交付,使应用在不同的团队中共享,通过镜像的方式应用可以部署于任何环境中。这样避免了各团队之间的协作问题的出现,成为企业实现DevOps目标的重要工具,而且以容器方式交付的Docker技术支持不断地开发迭代,提升了产品开发和交付速度,极大的方便了业务的横向扩容。  且与KVM虚拟化技术不同的是,Docker直接移植于Linux内核之上,通过运行Linux进程将底层设备虚拟隔离,这样系统性能的损耗也要比虚拟机低的多,几乎可以忽略。同时,Docker应用容器的启停非常高效,可以支持大规模的分布系统的水平扩展,真正给企业开发带来福音。","text":"  在企业生产应用中,docker容器技术及k8s的编排管理工具的使用率越来越高,这项技术甚至已经改变了很多企业的架构与框架流程,因为容器技术的出现,可以将应用以集装箱的方式打包交付,使应用在不同的团队中共享,通过镜像的方式应用可以部署于任何环境中。这样避免了各团队之间的协作问题的出现,成为企业实现DevOps目标的重要工具,而且以容器方式交付的Docker技术支持不断地开发迭代,提升了产品开发和交付速度,极大的方便了业务的横向扩容。  且与KVM虚拟化技术不同的是,Docker直接移植于Linux内核之上,通过运行Linux进程将底层设备虚拟隔离,这样系统性能的损耗也要比虚拟机低的多,几乎可以忽略。同时,Docker应用容器的启停非常高效,可以支持大规模的分布系统的水平扩展,真正给企业开发带来福音。 Docker简介Docker 是什么  首先 Docker 是一个在 2013 年开源的应用程序并且是一个基于 go 语言编写是一个开源的 PAAS 服务(Platform as a Service, 平台即服务的缩写), go 语言是由google 开发, docker 公司最早叫 dotCloud 后由于 Docker 开源后大受欢迎就将公司改名为 Docker Inc, 总部位于美国加州的旧金山, Docker 是基于 linux 内核实现, Docker 最早采用 LXC 技术(LinuX Container 的简写, LXC 是 Linux 原生支持的容器技术, 可以提供轻量级的虚拟化, 可以说 docker 就是基于 LXC 发展起来的,提供 LXC 的高级封装,发展标准的配置方法), 而虚拟化技术 KVM(Kernelbased Virtual Machine) 基于模块实现, Docker 后改为自己研发并开源的 runc 技术运行容器。 Dokcer与虚拟机技术对比  Docker 相比虚拟机的交付速度更快, 资源消耗更低, Docker 采用客户端/服务端架构,使用远程 API 来管理和创建 Docker 容器,其可以轻松的创建一个轻量级的、 可移植的、自给自足的容器, docker 的三大理念是 build(构建)、ship(运输)、 run(运行), Docker 遵从 apache 2.0 协议,并通过(namespace 及cgroup 等)来提供容器的资源隔离与安全保障等,所以 Docke 容器在运行时不需要类似虚拟机(空运行的虚拟机占用物理机 6-8%性能)的额外资源开销,因此可以大幅提高资源利用率,总而言之 Docker 是一种用了新颖方式实现的轻量级虚拟机.类似于 VM 但是在原理和应用上和 VM 的差别还是很大的,并且 docker的专业叫法是应用容器(Application Container)。| 优势 | Docker | 虚拟机 ||–|–|–|| 资源利用率更高 | 一台物理机可以运行数百个容器 | 但是一般只能运行数十个虚拟机 || 开销更小 | 不需要启动单独的虚拟机占用硬件资源 | 需要启动单独的虚拟机占用硬件资源 || 启动速度更快 | 可以在数秒内完成启动 | 需要几十秒甚至数分钟 |   使用虚拟机是为了更好的实现服务运行环境隔离, 每个虚拟机都有独立的内核,虚拟化可以实现不同操作系统的虚拟机,但是通常一个虚拟机只运行一个服务, 很明显资源利用率比较低且造成不必要的性能损耗, 我们创建虚拟机的目的是为了运行应用程序,比如 Nginx、 PHP、 Tomcat 等 web 程序, 使用虚拟机无疑带来了一些不必要的资源开销,但是容器技术则基于减少中间运行环节带来较大的性能提升。 Docker 的组成 Docker 主机(Host): 一个物理机或虚拟机,用于运行 Docker 服务进程和容器。 Docker 服务端(Server): Docker 守护进程, 运行 docker 容器。 Docker 客户端(Client): 客户端使用 docker 命令或其他工具调用 docker API。 Docker 仓库(Registry): 保存镜像的仓库,类似于 git 或 svn 这样的版本控制系统,官方仓库: https://hub.docker.com/ Docker 镜像(Images): 镜像可以理解为创建实例使用的模板。 Docker 容器(Container): 容器是从镜像生成对外提供服务的一个或一组服务。 详细介绍参见官方文档https://docs.docker.com/engine/docker-overview/ Docker实现需要解决的问题  容器技术确实有很多优点,不过当使用多个容器时带来的以下问题怎么解决:  1.怎么样保证每个容器都有不同的文件系统并且能互不影响?  2.一个 docker 主进程内的各个容器都是其子进程,那么实现同一个主进程下不同类型的子进程? 各个进程间通信能相互访问(内存数据)吗?  3.每个容器怎么解决 IP 及端口分配的问题?  4.多个容器的主机名能一样吗?  5.每个容器都要不要有 root 用户?怎么解决账户重名问题?  这就不得不引入一个Namespace的概念了。 Namespace  namespace 是 Linux 系统的底层概念, 在内核层实现,即有一些不同类型的命名空间被部署在核内, 各个 docker 容器运行在同一个 docker 主进程并且共用同一个宿主机系统内核,各 docker 容器运行在宿主机的用户空间, 每个容器都要有类似于虚拟机一样的相互隔离的运行空间, 但是容器技术是在一个进程内实现运行指定服务的运行环境, 并且还可以保护宿主机内核不受其他进程的干扰和影响, 如文件系统空间、网络空间、进程空间等。| 隔离类型 | 实现功能 | 系统调用参数 | 内核版本 ||–|–|–|–|| MNT Namespace(mount) | 提供磁盘挂载点和文件系统的隔离能力 | CLONE_NEWNS | Linux 2.4.19 || IPC Namespace(Inter-Process Communication) | 提供进程间通信的隔离能力 | CLONE_NEWIPC | Linux 2.6.19 || UTS Namespace(UNIX Timesharing System) | 提供主机名隔离能力 | CLONE_NEWUTS | Linux 2.6.19 || PID Namespace(Process Identification) | 提供进程隔离能力 | CLONE_NEWPID | Linux 2.6.24 || Net Namespace(network) | 提供网络隔离能力 | CLONE_NEWNET | Linux 2.6.29 || User Namespace(user) | 提供用户隔离能力 | CLONE_NEWUSER | Linux 3.8 | MNT Namespace:每个容器都要有独立的根文件系统有独立的用户空间, 以实现在容器里面启动服务并且使用容器的运行环境,容器里面是不能访问宿主机的资源, 宿主机是使用了 chroot 技术把容器锁定到一个指定的运行目录里面。 IPC Namespace:一个容器内的进程间通信, 允许一个容器内的不同进程的(内存、 缓存等)数据访问,但是不能跨容器访问其他容器的数据。 UTS Namespace:UTS namespace(UNIX Timesharing System 包含了运行内核的名称、版本、底层体系结构类型等信息)用于系统标识, 其中包含了 hostname 和域名domainname , 它使得一个容器拥有属于自己 hostname 标识,这个主机名标识独立于宿主机系统和其上的其他容器。 PID Namespace:Linux 系统中,有一个 PID 为 1 的进程(init/systemd)是其他所有进程的父进程, 那么在每个容器内也要有一个父进程来管理其下属的子进程,那么多个容器的进程通 PID namespace 进程隔离(比如 PID 编号重复、 器内的主进程生成与回收子进程等)。 Net Namespace:每一个容器都类似于虚拟机一样有自己的网卡、 监听端口、 TCP/IP 协议栈等,Docker 使用 network namespace 启动一个 vethX 接口,这样你的容器将拥有它自己的桥接 ip 地址,通常是 docker0,而 docker0 实质就是 Linux 的虚拟网桥,网桥是在 OSI 七层模型的数据链路层的网络设备,通过 mac 地址对网络进行划分,并且在不同网络直接传递数据。 User Namespace:User Namespace 允许在各个宿主机的各个容器空间内创建相同的用户名以及相同的用户 UID 和 GID, 只是会把用户的作用范围限制在每个容器内,即 A 容器和 B 容器可以有相同的用户名称和 ID 的账户,但是此用户的有效范围仅是当前容器内, 不能访问另外一个容器内的文件系统,即相互隔离、互补影响、 永不相见。   通过这些内核级功能的实现,docker才可以正常工作,实现不同容器间的各种资源的隔离,形成共享同一组硬件及内核上却互不影响的独立应用级虚拟化系统。不过此时,我们还面临一个问题,就是资源使用率的控制。如果一个容器因为BUG或代码本身等原因导致无限制的使用宿主机上的资源,将会导致宿主机CPU或者内存不足进而极有可能影响到其他容器的正常运行。所以我们需要对每个容器的资源利用做一个限制,我们通过内核中的Linux Cgroups功能来限制每个容器能使用的资源的上限。 Linux Cgroups  Linux Cgroups 的全称是 Linux Control Groups, 它最主要的作用,就是限制一个进程组能够使用的资源上限,包括 CPU、内存、磁盘、网络带宽等等。此外,还能够对进程进行优先级设置,以及将进程挂起和恢复等操作。  Cgroups 在内核层默认已经开启,可通过下列命令验证查看Cgroups设置  CentOS7.6:12345678910111213141516[root@localhost ~]# cat /boot/config-3.10.0-957.el7.x86_64 |grep CGROUPCONFIG_CGROUPS=y# CONFIG_CGROUP_DEBUG is not setCONFIG_CGROUP_FREEZER=yCONFIG_CGROUP_PIDS=yCONFIG_CGROUP_DEVICE=yCONFIG_CGROUP_CPUACCT=yCONFIG_CGROUP_HUGETLB=yCONFIG_CGROUP_PERF=yCONFIG_CGROUP_SCHED=yCONFIG_BLK_CGROUP=y# CONFIG_DEBUG_BLK_CGROUP is not setCONFIG_NETFILTER_XT_MATCH_CGROUP=mCONFIG_NET_CLS_CGROUP=yCONFIG_NETPRIO_CGROUP=y[root@localhost ~]#   ubuntu1804中:123456789101112131415161718192021root@DockerUbuntu:~# cat /boot/config-4.15.0-70-generic |grep CGROUPCONFIG_CGROUPS=yCONFIG_BLK_CGROUP=y# CONFIG_DEBUG_BLK_CGROUP is not setCONFIG_CGROUP_WRITEBACK=yCONFIG_CGROUP_SCHED=yCONFIG_CGROUP_PIDS=yCONFIG_CGROUP_RDMA=yCONFIG_CGROUP_FREEZER=yCONFIG_CGROUP_HUGETLB=yCONFIG_CGROUP_DEVICE=yCONFIG_CGROUP_CPUACCT=yCONFIG_CGROUP_PERF=yCONFIG_CGROUP_BPF=y# CONFIG_CGROUP_DEBUG is not setCONFIG_SOCK_CGROUP_DATA=yCONFIG_NETFILTER_XT_MATCH_CGROUP=mCONFIG_NET_CLS_CGROUP=mCONFIG_CGROUP_NET_PRIO=yCONFIG_CGROUP_NET_CLASSID=yroot@DockerUbuntu:~#   可以看到内核较新的 ubuntu 支持的功能更多。 cgroups 参数具体解释:123456789101112blkio:块设备 IO 限制。cpu:使用调度程序为 cgroup 任务提供 cpu 的访问。cpuacct:产生 cgroup 任务的 cpu 资源报告。cpuset:如果是多核心的 cpu,这个子系统会为 cgroup 任务分配单独的 cpu和内存。devices:允许或拒绝 cgroup 任务对设备的访问。freezer:暂停和恢复 cgroup 任务。memory:设置每个 cgroup 的内存限制以及产生内存资源报告。net_cls:标记每个网络包以供 cgroup 方便使用。ns:命名空间子系统。perf_event:增加了对每 group 的监测跟踪的能力,可以监测属于某个特定的group 的所有线程以及运行在特定 CPU 上的线程。   可以通过命令ll /sys/fs/cgroup/查看系统Cgroups Docker依赖的技术  Docker容器技术如果真正想在企业中应用,还必须依赖下面的一些技术,会在之后的文章中详细介绍。 容器网络:  docker 自带的网络 docker network 仅支持管理单机上的容器网络, 当多主机运行的时候需要使用第三方开源网络,例如 calico、 flannel 等。 服务发现:  容器的动态扩容特性决定了容器 IP 也会随之变化, 因此需要有一种机制可以自动识别并将用户请求动态转发到新创建的容器上, kubernetes 自带服务发现功能,需要结合 kube-dns 服务解析内部域名。 容器监控:  可以通过原生命令 docker ps/top/stats 查看容器运行状态,另外也可以使heapster/ Prometheus 等第三方监控工具监控容器的运行状态。 数据管理:  容器的动态迁移会导致其在不同的 Host 之间迁移,因此如何保证与容器相关的数据也能随之迁移或随时访问,可以使用逻辑卷/存储挂载等方式解决。 日志收集:  docker 原生的日志查看工具 docker logs, 但是容器内部的日志需要通过 ELK 等专门的日志收集分析和展示工具进行处理。 Docker部署  我们初步知道了Docker的概念和实现原理,也知道了Docker在使用中可能会遇到的问题,说了这么多,现在我们先将Docker部署一下。  Docker常见的安装方式有三种,可以通过rpm包下载,或者二进制安装,也可以通过epel源安装。  官方 rpm 包下载地址:  https://download.docker.com/linux/centos/7/x86_64/stable/Packages/  二进制下载地址:  https://download.docker.com/https://mirrors.aliyun.com/docker-ce/linux/static/stable/x86_64/  阿里镜像下载地址:  https://mirrors.aliyun.com/docker-ce/linux/centos/7/x86_64/stable/Packages/  Ubuntu的安装docker-ce阿里云镜像仓库方式如下(使用 apt-get 进行安装):123456789101112131415161718# step 1: 安装必要的一些系统工具sudo apt-get updatesudo apt-get -y install apt-transport-https ca-certificates curl software-properties-common# step 2: 安装GPG证书curl -fsSL https://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | sudo apt-key add -# Step 3: 写入软件源信息sudo add-apt-repository \"deb [arch=amd64] https://mirrors.aliyun.com/docker-ce/linux/ubuntu $(lsb_release -cs) stable\"# Step 4: 更新并安装Docker-CEsudo apt-get -y updatesudo apt-get -y install docker-ce# 安装指定版本的Docker-CE:# Step 1: 查找Docker-CE的版本:# apt-cache madison docker-ce# docker-ce | 17.03.1~ce-0~ubuntu-xenial | https://mirrors.aliyun.com/docker-ce/linux/ubuntu xenial/stable amd64 Packages# docker-ce | 17.03.0~ce-0~ubuntu-xenial | https://mirrors.aliyun.com/docker-ce/linux/ubuntu xenial/stable amd64 Packages# Step 2: 安装指定版本的Docker-CE: (VERSION例如上面的17.03.1~ce-0~ubuntu-xenial)# sudo apt-get -y install docker-ce=[VERSION]   注意:在与 kubernetes 结合使用的时候,要安装经过 kubernetes 官方测试通过的 docker版本, 避免出现不兼容等未知的及不可预估的问题发生, kubernetes 测试过的docker 版本可以在 github 查询, 具体如下:https://github.com/kubernetes/kubernetes/blob/master/CHANGELOG-1.14.md#external-dependencies  安装完成后就可以用systemctl start docker命令启动Docker了。  然后用docker info命令来验证当前容器信息1docker info 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647Containers: 0 #当前主机运行的容器总数Running: 0 #有几个容器是正在运行的Paused: 0 #有几个容器是暂停的Stopped: 0 #有几个容器是停止的Images: 0 #当前服务器的镜像数Server Version: 18.09.9 #服务端版本Storage Driver: overlay2 #正在使用的存储引擎Backing Filesystem: extfs #后端文件系统,即服务器的磁盘文件系统Supports d_type: true #是否支持 d_typeNative Overlay Diff: true #是否支持差异数据存储Logging Driver: json-file #日志类型Cgroup Driver: cgroupfs #Cgroups 类型Plugins: #插件Volume: local #卷Network: bridge host macvlan null overlay # overlay 夸主机通信Log: awslogs fluentd gcplogs gelf journald json-file local logentries splunk syslog #日志类型Swarm: inactive #是否支持 swarmRuntimes: runc #已安装的容器运行时Default Runtime: runc #默认使用的容器运行时Init Binary: docker-init #初始化容器的守护进程,即 pid 为 1 的进程containerd version: 894b81a4b802e4eb2a91d1ce216b8817763c29fb #版本runc version: 425e105d5a03fabd737a126ad93d62a9eeede87f # runc 版本init version: fec3683 #init 版本Security Options: #安全选项Apparmor #安全模块, https://docs.docker.com/engine/security/apparmor/seccomp #审计(操作), https://docs.docker.com/engine/security/seccomp/Profile: default #默认的配置文件Kernel Version: 4.15.0-55-generic #宿主机内核版本Operating System: Ubuntu 18.04.3 LTS #宿主机操作系统OSType: linux #宿主机操作系统类型Architecture: x86_64 #宿主机架构CPUs: 2 #宿主机 CPU 数量Total Memory: 3.83GiB #宿主机总内存Name: DockerUbuntu #宿主机 hostnameID: ZFPD:UIA5:SR6E:Y6SS:52QL:5MPT:VDY3:ATVI:QMVG:HAFF:MN74:2HPD #宿主机IDDocker Root Dir: /var/lib/docker #宿主机数据保存目录Debug Mode (client): false #client 端是否开启 debugDebug Mode (server): false #server 端是否开启 debugRegistry: https://index.docker.io/v1/ #镜像仓库Labels: #其他标签Experimental: false #是否测试版Insecure Registries: #非安全的镜像仓库127.0.0.0/8Live Restore Enabled: false #是否开启活动重启(重启 docker-daemon 不关闭容器)Product License: Community Engine #产品许可信息 在结尾可能会有类似warning警报,1WARNING: No swap limit support 这是提示说我们没有开启 swap 资源限制,这就需要我们通过修改内核参数,来限制swap资源的使用。1vim /etc/default/grub 1GRUB_CMDLINE_LINUX=\"net.ifnames=0 biosdevname=0 cgroup_enable=memory swapaccount=1\" 12update-grubreboot   重启生效。至此,docker工具部署就完成了~","categories":[{"name":"Cloud","slug":"Cloud","permalink":"https://wudihechao.github.io/categories/Cloud/"}],"tags":[{"name":"Docker","slug":"Docker","permalink":"https://wudihechao.github.io/tags/Docker/"},{"name":"企业级应用","slug":"企业级应用","permalink":"https://wudihechao.github.io/tags/企业级应用/"},{"name":"容器","slug":"容器","permalink":"https://wudihechao.github.io/tags/容器/"},{"name":"虚拟化","slug":"虚拟化","permalink":"https://wudihechao.github.io/tags/虚拟化/"}],"keywords":[{"name":"Cloud","slug":"Cloud","permalink":"https://wudihechao.github.io/categories/Cloud/"}]},{"title":"KVM虚拟化","slug":"KVM虚拟化","date":"2019-11-29T14:28:35.000Z","updated":"2019-12-04T02:03:42.614Z","comments":true,"path":"/blog/f989902e.html","link":"","permalink":"https://wudihechao.github.io/blog/f989902e.html","excerpt":"  KVM 是Kernel-based Virtual Machine的简称,是一个开源的系统虚拟化模块,自Linux 2.6.20之后集成在Linux的各个主要发行版本中,KVM目前已成为学术界的主流 VMM (virtual machine monitor,虚拟机监视器,也称为hypervisor)之一。","text":"  KVM 是Kernel-based Virtual Machine的简称,是一个开源的系统虚拟化模块,自Linux 2.6.20之后集成在Linux的各个主要发行版本中,KVM目前已成为学术界的主流 VMM (virtual machine monitor,虚拟机监视器,也称为hypervisor)之一。  可参考红帽官方对kvm的定义:https://www.redhat.com/zh/topics/virtualization/what-is-KVM  KVM的虚拟化需要硬件支持(如Intel VT技术或者AMD V技术),是基于硬件的完全虚拟化,而Xen早期则是基于软件模拟的半虚拟化,新版本则是支持基于硬件支持的完全虚拟化,但Xen本身有自己的进程调度器,存储管理模块等,所以代码较为庞大,广为流传的商业系统虚拟化软件VMware ESXI系列是Full-Virtualization,(IBM文档:http://www.ibm.com/developerworks/cn/linux/l-using-kvm/ )  简单地说,KVM就是基于硬件支持实现,将单台主机,分隔成多个互不干扰的虚拟主机的技术,不受困于底层操作系统,可以为每个主机提供单独的、所需的的运行环境,如下图所示:  如果每个节点都是用共享存储如NAS,则可实现跨主机的虚拟机快速迁移,这也是我们在生产环境中经常采用的架构。本文将以下面架构,来具体展示KVM在实际生产中的应用。  三个主机,其中两个主机node1和node2上运行KVM,另一台是NAS共享存储服务器,通过10.0.0.0/16网段内网连接。在node1上的用KVM构建虚拟机H1和W1分别运行haproxy+keepalived服务和nginx服务,其中H1桥接方式到node1的两块网卡上,W1桥接到内网网卡eth1上。node2节点中也是如此。于是,将Client端请求通过haproxy反向代理只内网的web服务集群中,保证负载均衡和高可用,哪怕一个物理节点服务器down掉也不影响客户服务访问。  NAS服务器的搭建很容易,这里就先不详细介绍了,本文之后主要介绍KVM服务的配置及虚拟机集群的搭建。 宿主机环境准备:  KVM需要宿主机CPU必须支持虚拟化功能,因此如果是在vmware workstation上使用虚拟机做宿主机,那么必须要在虚拟机配置界面的处理器选项中开启虚拟机化功能(VMware支持嵌套虚拟化,而KVM不支持,大部分云服务器如阿里云就是基于KVM技术二次研发制作的,所以云服务器都不支持从中再建虚拟机)。  KVM模块因为已经被集成到linux内核之中,所以我们只需要安装KVM管理工具就可以直接使用KVM创建虚拟机了。  可用命令验证是否开启虚拟化(也可通过lscpu命令,grep -o选项只看匹配词本身,也可以用egrep代替grep -E)123[root@localhost ~]# grep -Eo \"vmx|svm\" /proc/cpuinfo | wc -lvmxvmx KVM软件包安装  在Ubuntu 18.04中:  可参考官网https://ubuntu.com/server/docs/virtualization-libvirt123apt install qemu-kvm virt-manager libvirt-daemon-systemkvm-ok #验证是否支持kvm 12INFO: /dev/kvm existsKVM acceleration can be used   CentOS 7.X:1yum install qemu-kvm qemu-kvm-tools libvirt libvirt-client virt-manager virt-install 12systemctl start libvirtdsystemctl enable libvirtd 网络环境配置  为了让三个节点间虚拟机可以直接相互之间通信,需要将KVM上的虚拟机以桥接模式与宿主机的网卡连接,这就需要我们提前在宿主机上配置好网桥。  若宿主机为Ubuntu18.04:1sudo vim /etc/netplan/01-netcfg.yaml 12345678910111213141516171819202122232425262728# This file describes the network interfaces available on your system# For more information, see netplan(5).network: version: 2 renderer: networkd ethernets: eth0: dhcp4: no dhcp6: no eth1: dhcp4: no dhcp6: no bridges: br0: dhcp4: no dhcp6: no addresses: [172.18.0.75/16] gateway4: 172.18.0.1 nameservers: addresses: [180.76.76.76] interfaces: - eth0 br1: dhcp4: no dhcp6: no addresses: [10.0.0.75/16] interfaces: - eth1   修改网卡配置后,使用netplan apply命令使配置文件的修改生效1netplan apply   若宿主机为CentOS:1cd /etc/sysconfig/network-scripts/ 1vim ifcfg-eh0 123456TYPE=EthernetBOOTPROTO=staticNAME=eth0DEVICE=eth0ONBOOT=yesBRIDGE=br0 1vim ifcfg-eh1 123456TYPE=EthernetBOOTPROTO=staticNAME=eth1DEVICE=eth1ONBOOT=yesBRIDGE=br1 1vim ifcfg-br0 123456789TYPE=BridgeBOOTPROTO=staticNAME=br0DEVICE=br0ONBOOT=yesIPADDR=172.18.32.75NETMASK=255.255.0.0GATEWAY=172.18.0.1DNS1=180.76.76.76 1vim ifcfg-br1 1234567TYPE=BridgeBOOTPROTO=staticNAME=br1DEVICE=br1ONBOOT=yesIPADDR=10.0.0.75NETMASK=255.255.0.0   重启网络服务,生效配置1systemctl restart network   ip a命令或ifconfig查看桥接网卡是否生效 KVM虚拟机创建创建虚拟磁盘123[root@localhost ~]# ll /var/lib/libvirt/images/ #默认保存虚拟机磁盘的路径total 0[root@localhost ~]#   使用qemu-img create命令可以创建磁盘,如果创建raw格式磁盘文件,则理解占据实际大小,若创建qcow2稀疏格式磁盘,则磁盘文件会随着使用的增大而增大1qemu-img create -f raw /var/lib/libvirt/images/CentOS7.raw 10G建磁盘 12ll -h /var/lib/libvirt/images/CentOS7.raw-rw-r--r-- 1 root root 10G Nov 29 16:34 /var/lib/libvirt/images/centos.raw 1qemu-img create -f qcow2 /var/lib/libvirt/images/CentOS7.qcow2 10G 12ll -h /var/lib/libvirt/images/CentOS7.qcow2-rw-r--r-- 1 root root 193K Nov 29 16:36 /var/lib/libvirt/images/centos.qcow2   我们选用qcow2格式的磁盘,先创建H1虚拟机,1qemu-img create -f qcow2 /var/lib/libvirt/images/H1.qcow2 10G 创建虚拟机  先导入ISO光盘镜像文件,可以使用共享存储上的ISO文件,也可本机导入,本机上传的话一般习惯性放到/usr/local/src/目录下  使用virt-install命令创建虚拟机,参数可virt-install --help查看帮助信息1virt-install --help 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132usage: virt-install --name NAME --ram RAM STORAGE INSTALL [options]使用指定安装介质新建虚拟机。optional arguments:-h, --help show this help message and exit--version show program's version number and exit--connect URI 使用 libvirt URI 连接到 hypervisor通用选项:-n NAME, --name NAME 客户端事件名称--memory MEMORY 配置虚拟机内存分配。例如:--memory 1024 (in MiB)--memory 512,maxmemory=1024--vcpus VCPUS 为虚拟机配置的 vcpus 数。例如:--vcpus 5--vcpus 5,maxcpus=10,cpuset=1-4,6,8--vcpus sockets=2,cores=4,threads=2,--cpu CPU CPU 型号及功能。例如:--cpu coreduo,+x2apic--cpu host--metadata METADATA 配置虚拟机元数据。例如:--metadata name=foo,title=\"My pretty title\",uuid=...--metadata description=\"My nice long description\"安装方法选项:--cdrom CDROM 光驱安装介质-l LOCATION, --location LOCATION安装源(例如:nfs:host:/path、http://host/pathftp://host/path)--pxe 使用 PXE 协议从网络引导--import 在磁盘映像中构建虚拟机--livecd 将光驱介质视为 Live CD-x EXTRA_ARGS, --extra-args EXTRA_ARGS附加到使用 --location 引导的内核的参数--initrd-inject INITRD_INJECT使用 --location 为 initrd 的 root添加给定文件--os-variant DISTRO_VARIANT在其中安装 OS 变体的虚拟机,比如'fedora18'、'rhel6'、'winxp' 等等。--boot BOOT 配置虚拟机引导设置。例如:--boot hd,cdrom,menu=on--boot init=/sbin/init (for containers)--idmap IDMAP 为 LXC 容器启用用户名称空间。例如:--idmap uid_start=0,uid_target=1000,uid_count=10设备选项:--disk DISK 使用不同选项指定存储。例如:--disk size=10 (new 10GiB image in default location)--disk /my/existing/disk,cache=none--disk device=cdrom,bus=scsi--disk=?-w NETWORK, --network NETWORK配置虚拟机网络接口。例如:--network bridge=mybr0--network network=my_libvirt_virtual_net--network network=mynet,model=virtio,mac=00:11...--network none--network help--graphics GRAPHICS 配置虚拟机显示设置。例如:--graphics vnc--graphics spice,port=5901,tlsport=5902--graphics none--graphics vnc,password=foobar,port=5910,keymap=ja--controller CONTROLLER配置虚拟机控制程序设备。例如:--controller type=usb,model=ich9-ehci1--input INPUT 配置虚拟机输入设备。例如:--input tablet--input keyboard,bus=usb--serial SERIAL 配置虚拟机串口设备--parallel PARALLEL 配置虚拟机并口设备--channel CHANNEL 配置虚拟机沟通频道--console CONSOLE 配置虚拟机与主机之间的文本控制台连接--hostdev HOSTDEV 将物理 USB/PCI/etc主机设备配置为与虚拟机共享--filesystem FILESYSTEM将主机目录传递给虚拟机。例如:--filesystem /my/source/dir,/dir/in/guest--filesystem template_name,/,type=template--sound [SOUND] 配置虚拟机声音设备模拟--watchdog WATCHDOG 配置虚拟机 watchdog 设备--video VIDEO 配置虚拟机视频硬件。--smartcard SMARTCARD配置虚拟机智能卡设备。例如:--smartcard mode=passthrough--redirdev REDIRDEV 配置虚拟机重定向设备。例如:--redirdev usb,type=tcp,server=192.168.1.1:4000--memballoon MEMBALLOON配置虚拟机 memballoon 设备。例如:--memballoon model=virtio--tpm TPM 配置虚拟机 TPM 设备。例如:--tpm /dev/tpm--rng RNG 配置虚拟机 RNG 设备。例如:--rng /dev/random--panic PANIC 配置虚拟机 panic 设备。例如:--panic default虚拟机配置选项:--security SECURITY 设定域安全驱动器配置。--numatune NUMATUNE 为域进程调整 NUMA 策略。--memtune MEMTUNE 为域进程调整内粗策略。--blkiotune BLKIOTUNE为域进程调整 blkio 策略。--memorybacking MEMORYBACKING为域进程设置内存后备策略。例如:--memorybacking hugepages=on--features FEATURES 设置域 <features> XML。例如:--features acpi=off--features apic=on,eoi=on--clock CLOCK 设置域 <clock> XML。例如:--clock offset=localtime,rtc_tickpolicy=catchup--pm PM 配置 VM 电源管理功能--events EVENTS 配置 VM 生命周期管理策略--resource RESOURCE 配置 VM 资源分区(cgroups)虚拟化平台选项:-v, --hvm 客户端应该是一个全虚拟客户端-p, --paravirt 这个客户端一个是一个半虚拟客户端--container 这台虚拟机需要一个容器客户端--virt-type HV_TYPE 要使用的管理程序名称(kvm、qemu、xen等等)--arch ARCH 模拟的 CPU 构架--machine MACHINE 要模拟的机器类型其它选项:--autostart 引导主机时自动启动域。--wait WAIT 等待安装完成的分钟数。--noautoconsole 不要自动尝试连接到客户端控制台--noreboot 完成安装后不要引导虚拟机。--print-xml [XMLONLY]输出所生成域 XML,而不是创建虚拟机。--dry-run 完成安装步骤,但不要创建设备或者定义虚拟机。--check CHECK 启用或禁用验证检查。例如:--check path_in_use=off--check all=off-q, --quiet 禁止无错误输出-d, --debug 输入故障排除信息   我们使用命令创建虚拟机(网卡配置选择default的话默认是nat模式)1234567virt-install --virt-type kvm \\--name H1 --ram 1024 --vcpus 2 \\--cdrom=/usr/local/src/CentOS-7-x86_64-Minimal-1908.iso \\--disk=/var/lib/libvirt/images/H1.qcow2 \\--network network=default \\--graphics vnc,listen=0.0.0.0 \\--noautoconsole   输入virt-manager可开启xshellmanager的终端窗口如下图  双击图标打开,可以看到已经开始自动从光盘启动安装了,配置完毕,手动安装CentOS7系统。如果输入virt-manage不能启动xmanager的窗口,检查一下项目 确认已经安装xmanager xshell文件—属性—连接—隧道—设置转发x11到xmanager ssh服务配置文件是否开启X11Forwarding选项 设置为yes   以上都没问题,建议重装xmanager,我就是配置都对,但是输入virt-manage命令没反应,重装了一下xmanager和xshell之后就可以正常使用了。   安装好CentOS7系统重启后,在vrit-manager的窗口中点击虚拟机info,给H1虚拟机添加一块网卡,并将两块网卡分别选择为主机的桥接网卡br0和br1,如下图所示  在虚拟机中配置两个网卡的IP,配置分别如下12345678910vi /etc/sysconfig/network-scripts/ifcfg-eth0TYPE=\"Ethernet\"BOOTPROTO=\"static\"IPADDR=\"172.18.32.85\"GATEWAY=\"172.18.0.1\"DNS1=\"180.76.76.76\"DEFROUTE=\"yes\"NAME=\"eth0\"DEVICE=\"eth0\"ONBOOT=\"yes\" 12345678vi /etc/sysconfig/network-scripts/ifcfg-eth1TYPE=\"Ethernet\"BOOTPROTO=\"static\"IPADDR=\"10.0.0.85\"DEFROUTE=\"no\"NAME=\"eth1\"DEVICE=\"eth1\"ONBOOT=\"yes\"   此时H1的基本配置就算完成了。  查看/var/lib/libvirt/images/目录下,看到已经有一个镜像了了123[root@localhost ~]# ll /var/lib/libvirt/images/total 1594308-rw-r--r-- 1 root root 1632632832 Nov 29 18:54 H1.qcow2   可以直接将此文件cp一份来当web1的磁盘文件12cd /var/lib/libvirt/images/cp H1.qcow2 W1.qcow2   也可以在node2节点上的H2和web2虚拟机的磁盘文件。我们直接拷贝改名后,执行virt-install命令,创建另外三个虚拟机。1234567virt-install --virt-type kvm \\--name W1 --ram 1024 --vcpus 2 \\--cdrom=/usr/local/src/CentOS-7-x86_64-Minimal-1908.iso \\--disk=/var/lib/libvirt/images/W1.qcow2 \\--network network=default \\--graphics vnc,listen=0.0.0.0 \\--noautoconsole   然后将W1强制终止,因为第一次是默认从光驱启动的,我们已经有系统了,不需要重装,重启后就发现已经是装好的系统,与H1一模一样的。  然后修改主机名为web1,将网卡桥接在eth1上,修改网卡配置。12345678TYPE=\"Ethernet\"BOOTPROTO=\"static\"IPADDR=\"10.0.0.175\"PREFIX=\"16\"DEFROUTE=\"yes\"NAME=\"eth0\"DEVICE=\"eth0\"ONBOOT=\"yes\"   对node2上的两个虚拟机采用同样方式创建。  之后分别通过脚本装HAProxy+keepadlived服务和nginx+PHP服务。  修改完几个服务的配置文件(可参考之前文章企业级应用:负载均衡层——haproxy(一))之后,就可以将通过客户机就可以访问,后端web服务器了。 附一键安装脚本HAProxy一键安装脚本1234567891011[root@HAProxy1 haproxy]# tree.├── haproxy-2.0.8.tar.gz├── haproxy84.cfg├── haproxy.cfg├── haproxy.service├── haproxy.sh└── lua-5.3.5.tar.gz0 directories, 6 files[root@HAProxy1 haproxy]# bash haproxy.sh 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162#!/bin/bashDST=\"/apps\"[ -a haproxy.cfg ] || { echo ' the absence of haproxy.conf' ;exit 1;}[ -a haproxy-2.0.8.tar.gz ] || { echo ' the absence of haproxy-2.0.8.tar.gz' ;exit 1;}[ -a haproxy.service ] || { echo ' the absence of haproxy.service' ;exit 2;}[ -a /usr/lib/systemd/system/haproxy.service ] && { echo ' haproxy is aready installed' ;exit 3;}id haproxy &>/dev/null && { echo 'user haproxy is exist' ; exit 4;} || useradd -r -s /sbin/nologin -u 79 haproxyyum install -y libtermcap-devel ncurses-devel libevent-devel readline-devel gcc make[ -a lua-5.3.5.tar.gz ] || wget http://www.lua.org/ftp/lua-5.3.5.tar.gztar xvf lua-5.3.5.tar.gzcd lua-5.3.5make linux testyum install gcc gcc-c++ glibc glibc-devel pcre pcre-devel openssl openssl-devel systemd-devel -ymkdir -p $DST/haproxy#[ -a haproxy-2.0.8.tar.gz ] || wget http://www.haproxy.org/download/2.0/src/haproxy-2.0.8.tar.gzcd ..tar xvf haproxy-2.0.8.tar.gzsleep 1cd haproxy-2.0.8make ARCH=x86_64 \\TARGET=linux-glibc USE_PCRE=1 \\USE_OPENSSL=1 \\USE_ZLIB=1 \\USE_SYSTEMD=1 \\USE_CPU_AFFINITY=1 \\USE_LUA=1 \\LUA_INC=../lua-5.3.5/src/ \\LUA_LIB=../lua-5.3.5/src/ \\PREFIX=$DST/haproxymake install PREFIX=$DST/haproxycat > /usr/lib/systemd/system/haproxy.service << \"END\"[Unit]Description=HAProxy Load BalancerAfter=syslog.target network.target[Service]ExecStartPre=/usr/sbin/haproxy -f /etc/haproxy/haproxy.cfg -c -qExecStart=/usr/sbin/haproxy -Ws -f /etc/haproxy/haproxy.cfg -p /var/lib/haproxy/haproxy.pidExecReload=/bin/kill -USR2 $MAINPID[Install]WantedBy=multi-user.target\\ENDsed -i \"s@/usr@$DST/haproxy@\" /usr/lib/systemd/system/haproxy.servicemkdir -p /etc/haproxycd ..cp haproxy.cfg /etc/haproxy/sed -i \"s@chroot /apps/haproxy@chroot $DST/haproxy@\" /etc/haproxy/haproxy.cfgmkdir -p /var/lib/haproxychown 99.99 /var/lib/haproxy/ -Rcat >> /etc/sysctl.conf << ENDnet.ipv4.ip_forward = 1net.ipv4.ip_nonlocal_bind = 1ENDsysctl -p #修改内核参数之后生效,systemctl daemon-reload 是重读启动脚本systemctl enable --now haproxysystemctl status haproxy Nginx+PHP一键安装脚本1234567891011121314[root@Web1 web]# tree.├── nginx│ ├── nginx-1.16.1.tar.gz│ ├── nginx.conf│ ├── nginx.service│ └── nginx.sh├── php-fpm│ ├── php-7.3.10.tar.xz│ └── php-fpm.sh└── web.sh2 directories, 7 files[root@Web1 web]# bash web.sh 123456vim web.sh#!/bin/bashcd nginxbash nginx.shcd ../php-fpmbash php-fpm.sh 1234567891011121314151617181920vim nginx/nginx.sh#!/bin/bashDST=\"/apps\"[ -a nginx.conf ] || { echo ' the absence of nginx.conf' ;exit 1;}[ -a nginx.service ] || { echo ' the absence of nginx.service' ;exit 2;}[ -a /usr/lib/systemd/system/nginx.service ] && { echo ' nginx is aready installed' ;exit 3;}id nginx &>/dev/null && { echo 'user nginx is exist' ; exit 4;} || useradd -r -s /sbin/nologin -u 80 nginxyum install -y gcc gcc-c++ glibc glibc-devel pcre pcre-devel openssl openssl-devel systemd-devel net-tools iotop bc zip unzip zlib-devel bash-completion nfs-utils automake libxml2 libxml2-devel libxslt libxslt-devel perl perl-ExtUtils-Embed vim lrzsz tree psmisc wget || { echo 'Dependencies is not installed';exit 5;}#mkdir -p $DST/nginxmkdir -p /data/apps/nginxln -s /data/apps/ /apps[ -a nginx-1.16.1.tar.gz ] || wget https://nginx.org/download/nginx-1.16.1.tar.gztar xvf nginx-1.16.1.tar.gzcd nginx-1.16.1./configure --prefix=$DST/nginx --user=nginx --group=nginx --with-http_ssl_module --with-http_v2_module --with-http_realip_module --with-http_stub_status_module --with-http_gzip_static_module --with-pcre --with-stream --with-stream_ssl_module --with-stream_realip_module --with-select_module --with-file-aiomake -j 4 && make installcp ../nginx.conf $DST/nginx/conf/cp ../nginx.service /usr/lib/systemd/system/systemctl enable --now nginxsystemctl status nginx 1234567891011121314151617181920vim nginx/nginx.service[Unit]Description=The nginx HTTP and reverse proxy serverAfter=network.target remote-fs.target nss-lookup.target[Service]Type=forkingPIDFile=/apps/nginx/logs/nginx.pid# Nginx will fail to start if /run/nginx.pid already exists but has the wrong# SELinux context. This might happen when running `nginx -t` from the cmdline.# https://bugzilla.redhat.com/show_bug.cgi?id=1268621ExecStartPre=/usr/bin/rm -f /apps/nginx/logs/nginx.pidExecStartPre=/apps/nginx/sbin/nginx -tExecStart=/apps/nginx/sbin/nginxExecReload=/bin/kill -s HUP $MAINPIDKillSignal=SIGQUITTimeoutStopSec=5KillMode=processPrivateTmp=true[Install]WantedBy=multi-user.target 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263vim nginx/nginx.confuser nginx;worker_processes 2;worker_cpu_affinity 0001 0010;worker_priority -10;error_log logs/error.log info;error_log /apps/nginx/logs/error.log error;events { worker_connections 65536; use epoll; accept_mutex on; multi_accept on;}http { include mime.types; default_type application/octet-stream; log_format access_json '{\"@timestamp\":\"$time_iso8601\",' '\"host\":\"$server_addr\",' '\"clientip\":\"$remote_addr\",' '\"size\":$body_bytes_sent,' '\"responsetime\":$request_time,' '\"upstreamtime\":\"$upstream_response_time\",' '\"upstreamhost\":\"$upstream_addr\",' '\"http_host\":\"$host\",' '\"uri\":\"$uri\",' '\"domain\":\"$host\",' '\"xff\":\"$http_x_forwarded_for\",' '\"referer\":\"$http_referer\",' '\"tcp_xff\":\"$proxy_protocol_addr\",' '\"http_user_agent\":\"$http_user_agent\",' '\"status\":\"$status\"}'; access_log /apps/nginx/logs/access_json.log access_json; sendfile on; keepalive_timeout 65 65; server_tokens off; charset utf-8; gzip on; server { listen 80; server_name www.example.net; location = / { root html; index index.html index.htm; } error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } location ~ ^/(status|ping)$ { include fastcgi_params; fastcgi_pass 127.0.0.1:9000; fastcgi_param PATH_TRANSLATED $document_root$fastcgi_script_name; fastcgi_hide_header X-Powered-By; } location ~ \\.php$ { root html; fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; } }} 12345678910111213141516171819202122232425262728293031323334353637383940414243444546vim php-fpm/php-fpm.sh#!/bin/bashDST=\"/apps\"[ -a php-*.tar.* ] || { echo ' the absence of php-7.3.10.tar.xz' ;exit 1;}[ -a /usr/lib/systemd/system/php-fpm.service ] && { echo ' php-fpm is aready installed' ;exit 2;}[ -a $DST/php* ] && { echo ' php is aready installed' ;exit 2;}mkdir -p $DST/phpyum install libxml2-devel bzip2-devel libmcrypt-devel -y || { echo 'Dependencies is not installed';exit 5;}#wget https://www.php.net/distributions/php-7.3.10.tar.xztar xvf php-*.tar.*cd php-7.3.10./configure \\--prefix=$DST/php \\--enable-mysqlnd \\--with-mysqli=mysqlnd \\--with-pdo-mysql=mysqlnd \\--with-openssl \\--with-freetype-dir \\--with-jpeg-dir \\--with-png-dir \\--with-zlib \\--with-libxml-dir=/usr \\--with-config-file-path=/etc \\--with-config-file-scan-dir=/etc/php.d \\--enable-mbstring \\--enable-xml \\--enable-sockets \\--enable-fpm \\--enable-maintainer-zts \\--disable-fileinfo make -j 4 && make installcp php.ini-production /etc/php.inicp sapi/fpm/php-fpm.service /usr/lib/systemd/system/cp $DST/php/etc/php-fpm.conf.default $DST/php/etc/php-fpm.confcp $DST/php/etc/php-fpm.d/www.conf.default $DST/php/etc/php-fpm.d/www.confsed -i 's@nobody@nginx@g' $DST/php/etc/php-fpm.d/www.confsed -i '/#/!s@index index.html index.htm@index index.php index.html index.htm@' $DST/nginx/conf/nginx.confsed -i 's@/scripts$fastcgi_script_name@$document_root$fastcgi_script_name@g' $DST/nginx/conf/nginx.conf$DST/nginx/sbin/nginx -s reloadsystemctl enable --now php-fpm","categories":[{"name":"Cloud","slug":"Cloud","permalink":"https://wudihechao.github.io/categories/Cloud/"}],"tags":[{"name":"一键安装","slug":"一键安装","permalink":"https://wudihechao.github.io/tags/一键安装/"},{"name":"负载均衡","slug":"负载均衡","permalink":"https://wudihechao.github.io/tags/负载均衡/"},{"name":"虚拟化","slug":"虚拟化","permalink":"https://wudihechao.github.io/tags/虚拟化/"},{"name":"KVM","slug":"KVM","permalink":"https://wudihechao.github.io/tags/KVM/"},{"name":"ubuntu","slug":"ubuntu","permalink":"https://wudihechao.github.io/tags/ubuntu/"}],"keywords":[{"name":"Cloud","slug":"Cloud","permalink":"https://wudihechao.github.io/categories/Cloud/"}]},{"title":"OpenVPN的搭建","slug":"OpenVPN的搭建","date":"2019-11-25T13:05:48.000Z","updated":"2019-12-21T12:05:28.050Z","comments":true,"path":"/blog/2c6b894f.html","link":"","permalink":"https://wudihechao.github.io/blog/2c6b894f.html","excerpt":"  企业中,必不可少的应用就是VPN了,它可以帮助员工在外网中访问公司内网,常见开源实现方案有OpenVPN和jumpserver。  OpenVPN是采用了端口转发的原理实现,是基于IP+端口的4层代理机制,一般是用于出差员工访问公司内部ERP系统等使用,而jumpserver是7层代理,所以功能更加强大却也更加复杂,一般适合运维人员管理维护企业内部服务器。对于中小公司日常使用,OpenVPN就已经完全足够了,当然,也可用于科学,你懂得。本文将对linux环境下(CentOS),OpenVPN的安装配置做一个详细的介绍。","text":"  企业中,必不可少的应用就是VPN了,它可以帮助员工在外网中访问公司内网,常见开源实现方案有OpenVPN和jumpserver。  OpenVPN是采用了端口转发的原理实现,是基于IP+端口的4层代理机制,一般是用于出差员工访问公司内部ERP系统等使用,而jumpserver是7层代理,所以功能更加强大却也更加复杂,一般适合运维人员管理维护企业内部服务器。对于中小公司日常使用,OpenVPN就已经完全足够了,当然,也可用于科学,你懂得。本文将对linux环境下(CentOS),OpenVPN的安装配置做一个详细的介绍。 部署OpenVPN部署OpenVPN服务器下载安装OpenVPN  OpenVPN的rpm包可以在epel源中直接下载安装,所以我们需要先配置epel源,推荐选用阿里的epel源,CentOS67命令如下:12345678cat > /etc/yum.repos.d/aliyunepel.repo << \"END\"[aliyun-epel]name=aliyun-epelbaseurl=https://mirrors.aliyun.com/epel/$releasever/$basearch/gpgcheck=1gpgkey=https://mirrors.aliyun.com/epel/RPM-GPG-KEY-EPEL-$releaseverenabled=1END   CentOS8路径有所变化,命令如下:12345678cat > /etc/yum.repos.d/aliyunepel.repo << \"END\"[aliyun-epel]name=aliyun-epelbaseurl=https://mirrors.aliyun.com/epel/$releasever/Everything/$basearch/gpgcheck=1gpgkey=https://mirrors.aliyun.com/epel/RPM-GPG-KEY-EPEL-$releaseverenabled=1END   或者直接安装官方的epel源(6、7、8通用)。1yum install epel-release -y   配置好epel源之后,直接yum安装服务器端软件和证书管理工具就可以了。12yum install openvpn -y #openvpn服务端yum install easy-rsa -y #证书管理工具 OpenVPN配置  将模版配置文件及证书管理工具复制到指定目录123cp /usr/share/doc/openvpn-2.4.8/sample/sample-config-files/server.conf /etc/openvpn/ #openvpn server 配置文件cp -r /usr/share/easy-rsa/ /etc/openvpn/easyrsa-server #证书管理工具cp /usr/share/doc/easy-rsa-3.0.6/vars.example /etc/openvpn/easyrsa-server/3/vars   配置文件模版如果没有,那可能是在路径为/usr/share/doc/openvpn/sample/sample-config-files/server.conf,easy-rsa如果CentOS8yum安装不上,只能去github上https://github.com/OpenVPN/easy-rsa下载,将里面的easyrsa3目录复制出来就是证书管理工具,并且不需要复制vars.example文件,里面已经有了,改个名字就可以。1cp -r easyrsa3 /etc/openvpn/easyrsa-server   先进入证书管理工具目录(若github上下载的则是cd /etc/openvpn/easyrsa-server/,之后则都没有3的子目录,或者也创建一个3的子目录保持一致性)1cd /etc/openvpn/easyrsa-server/3   若easyrsa为github下载的话,目录结果如下12345678910111213141516[root@aws-host easyrsa-server]#tree.├── easyrsa├── openssl-easyrsa.cnf├── vars.example└── x509-types ├── ca ├── client ├── code-signing ├── COMMON ├── email ├── server └── serverClient1 directory, 10 files[root@aws-host easyrsa-server]#   OpenVPN的server端配置文件如下:1234567891011121314151617181920212223242526vim /etc/openvpn/server.conflocal 172.18.200.101 #本机监听IP,写公网IPport 1194 #端口# TCP or UDP server?proto tcp #协议,指定OpenVPN创建的通信隧道类型#proto udp#dev tap:创建一个以太网隧道,以太网使用tapdev tun:创建一个路由IP隧道,互联网使用tun一个TUN设备大多时候,被用于基于IP协议的通讯。一个TAP设备允许完整的以太网帧通过Openvpn隧道,因此提供非ip协议的支持,比如IPX协议和AppleTalk协议#dev-node MyTap #TAP-Win32适配器。非windows不需要#topology subnet #网络拓扑,不需要配置server 10.8.0.0 255.255.255.0 #客户端连接后分配IP的地址池,服务器默认会占用第一个IP 10.8.0.1#ifconfig-pool-persist ipp.txt #为客户端分配固定IP,不需要配置#server-bridge 10.8.0.4 255.255.255.0 10.8.0.50 10.8.0.100 #配置网桥模式,不需要push \"route 10.20.0.0 255.255.0.0\" #给客户端生成的静态路由表,下一跳为openvpn服务器的10.8.0.1,地址段为openvpn服务器后的公司内部网络,可以是多个网段push \"route 172.31.0.0 255.255.248.0\";client-config-dir ccd #为指定的客户端添加路由,改路由通常是客户端后面的内网网段而不是服务端的,也不需要设置;route 192.168.40.128 255.255.255.248;client-config-dir ccd;route 10.9.0.0 255.255.255.252;learn-address ./script #运行外部脚本,创建不同组的iptables 规则,不配置;push \"redirect-gateway def1 bypass-dhcp\" #启用后,客户端所有流量都将通过VPN服务器,因此不需要配置#;push \"dhcp-option DNS 208.67.222.222\" #推送DNS服务器,不需要配置#;push \"dhcp-option DNS 208.67.220.220\"#client-to-client #允许不同的client通过openvpn server直接通信,不开启#;duplicate-cn #多个用户共用一个账户,一般用于测试环境,生产环境都是一个用户一个证书keepalive 10 120 #设置服务端检测的间隔和超时时间,默认为每 10 秒 ping一次,如果 120 秒没有回应则认为对方已经 down   所以最终配置如下123456789101112131415161718192021local 172.18.32.71 #写公网IP,测试环境无所谓port 1194proto tcpdev tunca /etc/openvpn/certs/ca.crtcert /etc/openvpn/certs/server.crtkey /etc/openvpn/certs/server.key # This file should be kept secretdh /etc/openvpn/certs/dh.pemserver 10.8.0.0 255.255.255.0 #默认设置,不需要修改push \"route 10.0.0.0 255.255.255.0\" #内网网段,不需要修改,科学上网则为 push \"redirect-gateway def1 bypass-dhcp\"client-to-clientkeepalive 10 120cipher AES-256-CBCmax-clients 100user nobodygroup nobodypersist-tunstatus openvpn-status.loglog-append /var/log/openvpn/openvpn.logverb 9mute 20 搭建CA并签发证书  初始化pki环境1./easyrsa init-pki #生成pki目录用于保存证书   创建CA签发机构,然后直接回车1./easyrsa build-ca nopass #创建ca并不使用密码   创建服务端证书(私钥),直接回车1./easyrsa gen-req server nopass #生成server证书且不使用密码   然后使用自建ca签发服务器证书,即生成服务端crt公钥。crt公钥后期将用户发送给客户端,从而实现与openvpnserver端加密传输数据。1./easyrsa sign server server #签发服务端证书,备注信息为server   然后输入yes输入。 创建非对称秘钥对  还在easyrsa-server目录下,生成秘钥,这可能会花费一段时间。1./easyrsa gen-dh   到此服务端证书环境配置完成,下面是配置客户端证书配置 创建客户端证书及配置文件  证书还是使用easyrsa工具,(github下载的也还是那个目录easyrsa3,不需要复制vars.example文件,改个名字就可以)12cp -r /usr/share/easy-rsa/ /etc/openvpn/easyrsa-client/cp /usr/share/doc/easy-rsa-3.0.6/vars.example /etc/openvpn/easyrsa-client/3/vars 12cd /etc/openvpn/easyrsa-client/3./easyrsa init-pki   例如员工为Mice,则创建名为Mice的证书1./easyrsa gen-req Mice nopass #客户证书为Mice,没有设置密码   然后直接回车,便生成了Mice的证书申请和私钥。进入easyrsa的server目录中,(注意,一定要进入easyrsa-server目录,否则会报错),导入证书申请并给客户端的证书请求签发证书,记得输入yes确认。12./easyrsa import-req /etc/openvpn/easyrsa-client/3/pki/reqs/Mice.req Mice./easyrsa sign client Mice   复制证书到server目录:123456mkdir /etc/openvpn/certscd /etc/openvpn/certs/cp /etc/openvpn/easyrsa-server/3/pki/dh.pem .cp /etc/openvpn/easyrsa-server/3/pki/ca.crt .cp /etc/openvpn/easyrsa-server/3/pki/issued/server.crt .cp /etc/openvpn/easyrsa-server/3/pki/private/server.key .   创建客户端配置文件12mkdir /etc/openvpn/client/Mice/vim /etc/openvpn/client/Mice/client.ovpn 123456789101112131415client #声明自己是个客户端dev tun #接口类型,必须和服务端保持一致proto tcp #使用的协议,必须和服务端保持一致remote 172.18.32.71 1194 #server端的ip和端口,可以写域名但是需要可以解析成IPresolv-retry infinite #如果是写的server端的域名,那么就始终解析,如果域名发生变化,会重新连接到新>的域名对应的IPnobind #本机不绑定监听端口,客户端是随机打开端口连接到服务端的1194persist-key #persist-tunca ca.crtcert Mice.crtkey Mice.keyremote-cert-tls server #指定采用服务器校验方式#tls-auth ta.key 1cipher AES-256-CBCverb 3   对签发的客户端证书及配置文件进行归档,并打包发送给客户端主机。12345cd /etc/openvpn/client/Mice/cp /etc/openvpn/easyrsa-server/3/pki/ca.crt .cp /etc/openvpn/easyrsa-server/3/pki/issued/Mice.crt .cp /etc/openvpn/easyrsa-client/3/pki/private/Mice.key .tar czvf Mice.tar.gz * 配置防火墙转发规则  开启路由转发功能:123# vim /etc/sysctl.conf# sysctl -pnet.ipv4.ip_forward = 1   先清除现有防火墙策略以免发生干扰,如果CentOS7/8或Redhat7/8没有iptables命令就yum install -y iptables-services安装一个,之后设置IP伪装需要用到。  清空已有规则123456iptables -Fiptables -Xiptables -Ziptables -t nat -Fiptables -t nat -Xiptables -t nat -Z   创建iptables 规则:123iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -j MASQUERADE #此IP是server端默认ip,不要改iptables -A INPUT -p TCP --dport 1194 -j ACCEPTiptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT   CentOS6/7的话可以用保存iptables规则(适用未安装iptables-services,俩方式选一个就好):1service iptables save   显示OK说明保存成功1iptables: Saving firewall rules to /etc/sysconfig/iptables:[ OK ]   如果用的services,则systemctl enable --now iptables(会自动从/etc/sysconfig/iptables文件中读取防火墙策略)   创建日志目录并授权:12mkdir /var/log/openvpnchown nobody.nobody /var/log/openvpn 启动OpenVPN服务  启动openvpn服务1systemctl enable --now openvpn@server   如果没有[email protected]文件,那就自己编写一个吧1vim /usr/lib/systemd/system/[email protected] 1234567891011[Unit]Description=OpenVPN Robust And Highly Flexible Tunneling Application On %IAfter=network.target[Service]Type=notifyPrivateTmp=trueExecStart=/usr/sbin/openvpn --cd /etc/openvpn/ --config %i.conf[Install]WantedBy=multi-user.target   验证日志:12345678910111213tail /var/log/openvpn/openvpn.logTue Nov 19 13:27:27 2019 Socket Buffers: R=[87380->87380] S=[16384->16384]Tue Nov 19 13:27:27 2019 Listening for incoming TCP connection on[AF_INET]172.18.32.71:1194Tue Nov 19 13:27:27 2019 TCPv4_SERVER link local (bound):[AF_INET]172.18.32.71:1194Tue Nov 19 13:27:27 2019 TCPv4_SERVER link remote: [AF_UNSPEC]Tue Nov 19 13:27:27 2019 GID set to rootTue Nov 19 13:27:27 2019 UID set to rootTue Nov 19 13:27:27 2019 MULTI: multi_init called, r=256 v=256Tue Nov 19 13:27:27 2019 IFCONFIG POOL: base=10.8.0.4 size=62, ipv6=0Tue Nov 19 13:27:27 2019 MULTI: TCP INIT maxclients=4096 maxevents=4100Tue Nov 19 13:27:27 2019 Initialization Sequence Completed OpenVPN客户端  官方客户端下载地址: https://openvpn.net/community-downloads/  非官方地址:https://sourceforge.net/projects/securepoint/files/  如果是默认安装路径的话,保存证书到openvpn 客户端安装目录:C:\\Program Files\\OpenVPN\\config  之后就可以直接启动OpenVPN。此时就可以用浏览器直接访问内网IP或者直接ssh登陆内部服务器了。 关于科学上网  经过本人尝试多次,使用OpenVPN做代理转发来实现科学上网,还是有点难度。理论上简单可行的东西,搞了好几次都没成功。  本人有亚马逊的海外云主机,在上面搭建了OpenVPN之后怎么也没法成功,甚至云主机都登陆不上了。开始以为是GW将数据包拦截了,导致云主机被封掉。后来使用其他海外主机直接连接均无法连接失联的云主机,使用端口检查工具发现,那个云主机的所有端口都被关闭了。猜测应该是被亚马逊给封掉了,无奈之下只能释放实例,重新搭建服务器。弄了数次发现,结局均以实例被封告终。猜测亚马逊应该是检查到异常流量,直接把实例的数据连接都断掉了(据说OpenVPN的收费版本不会被封掉),绝望之下,尝试了下UDP协议来代替TCP协议,将客户端设置和服务端设置都改为UPD,竟然发现可以访问外网了,而且服务器至今也平安无事~有需求的小伙伴可以尝试UDP协议。另外附上我云主机的server端配置,希望对大家有所帮助。123456789101112131415161718192021222324grep ^[a-Z] /etc/openvpn/server.conflocal 0.0.0.0port 11094proto udpdev tunca /etc/openvpn/certs/ca.crtcert /etc/openvpn/certs/server.crtkey /etc/openvpn/certs/server.keydh /etc/openvpn/certs/dh.pemserver 10.8.0.0 255.255.255.0push \"redirect-gateway def1 bypass-dhcp\"push \"dhcp-option DNS 8.8.8.8\"keepalive 10 120cipher AES-256-CBCmax-clients 10user openvpngroup openvpnpersist-keypersist-tunstatus openvpn-status.loglog-append /var/log/openvpn/openvpn.logverb 3mute 20explicit-exit-notify 1   本文写得很简略,发现有小伙伴对相关问题很有兴趣,欢迎留言讨论。","categories":[{"name":"linux进阶","slug":"linux进阶","permalink":"https://wudihechao.github.io/categories/linux进阶/"}],"tags":[{"name":"企业级应用","slug":"企业级应用","permalink":"https://wudihechao.github.io/tags/企业级应用/"},{"name":"经验分享","slug":"经验分享","permalink":"https://wudihechao.github.io/tags/经验分享/"},{"name":"OpenVPN","slug":"OpenVPN","permalink":"https://wudihechao.github.io/tags/OpenVPN/"},{"name":"SSH","slug":"SSH","permalink":"https://wudihechao.github.io/tags/SSH/"},{"name":"科学上网","slug":"科学上网","permalink":"https://wudihechao.github.io/tags/科学上网/"}],"keywords":[{"name":"linux进阶","slug":"linux进阶","permalink":"https://wudihechao.github.io/categories/linux进阶/"}]},{"title":"tomcat集群的session共享","slug":"tomcat集群的session共享","date":"2019-11-22T13:50:11.000Z","updated":"2019-12-25T12:25:45.326Z","comments":true,"path":"/blog/6254cc16.html","link":"","permalink":"https://wudihechao.github.io/blog/6254cc16.html","excerpt":"  tomcat作为一个应用服务器,单机性能上都是无法满足生产中需要的,而想要解决高并发场景,光靠提升单机性能,成本与效果肯定都是无法让人接受的,而此时我们一般都采用tomcat集群的方式,用多台tomcat服务器来共同支撑我们的业务。  但这时就出现了一个新的问题,那就是会话保持。因为每台tomcat服务器的session是独立的,当客户端被调度到一个新的tomcat服务器时,他无法识别之前一台的tomcat服务器分配的sessionID,于是对于此次访问,之前的会话信息就都没有了,这表现在用户的客户端就相当于,点开一个新的链接,就发现需要重新登陆,或者之前的购物车里的商品都不见了等等。这样的客户访问体验绝对不是我们想要的,所以我们需要实现会话保持功能!","text":"  tomcat作为一个应用服务器,单机性能上都是无法满足生产中需要的,而想要解决高并发场景,光靠提升单机性能,成本与效果肯定都是无法让人接受的,而此时我们一般都采用tomcat集群的方式,用多台tomcat服务器来共同支撑我们的业务。  但这时就出现了一个新的问题,那就是会话保持。因为每台tomcat服务器的session是独立的,当客户端被调度到一个新的tomcat服务器时,他无法识别之前一台的tomcat服务器分配的sessionID,于是对于此次访问,之前的会话信息就都没有了,这表现在用户的客户端就相当于,点开一个新的链接,就发现需要重新登陆,或者之前的购物车里的商品都不见了等等。这样的客户访问体验绝对不是我们想要的,所以我们需要实现会话保持功能! 会话保持实现  一般tomcat的会话保持有三种方案实现: nginx、httpd或者haproxy的调度实现session绑定,一般是源地址哈希方式实现优点:简单易配置缺点:①如果目标服务器故障后,没有做持久化的话就会丢失session;②即便做了持久化,当服务器故障后,nginx或者haproxy会不得不重新分配一个tomcat服务器,而这时因为新的tomcat服务器上没有原来的sessionID,所以无法找到相应会话信息,会重新分配一个sessionID给客户端,就算原来的tomcat服务器重新上线,又被分配到原来的tomcat服务器中,可此时客户端已经有了新的sessionID,也不会去读取最开始的session信息,那些会话信息就相当于永远丢失了。 session复制集群,官方给出的tomcat会话共享解决方案  tomcat自己提供的多播集群,通过多播将任何一台的session同步到其他节点。缺点:①tomcat的同步节点不宜过多,互相即时通信session需要太多带宽;②每一台tomcat服务器都拥有全部session信息,内存占用太多。 session server  session 共享服务器,一般使用memcached、redis做共享的session服务器。目前最理想的解决方案,不过会需要额外的机器来配置共享服务器。反向代理的session绑定  这种实现session保持的方案,一般是使用的不多,一般用于公司内部中的会话保持场景。只需在haproxy或者nginx中的调度算法中,加入基于源地址hash即可(调度算法参考之前博客)。于是,用户每次访问都会被调度到同一台tomcat服务器上,上面已有他的session信息,便实现了会话保持。配置实现  我们用一台nginx服务器做反向代理,两台tomcat服务器来演示实现过程。 &gt nginx主机ip:192.168.32.207&gt tomcat主机1ip:192.168.32.231&gt tomcat主机2ip:192.168.32.232   将3台机子中的nginx或tomcat服务启动之后,分别检查80端口和8080端口是否都已监听,确保服务启动。12iptables -Fgetenforce 0   确保防火墙和SELinux设置不会干扰我们几台主机间的相互通信。  nginx主机反向代理的配置123456789101112131415upstream tomcat { #ip_hash; #先关闭原地址iphash,观察效果 server 192.168.32.231:8080 weight=1 fail_timeout=5s max_fails=3; server 192.168.32.232:8080 weight=1 fail_timeout=5s max_fails=3;}server { listen 80; index index.jsp# server_name www.example.net;# location ~* \\.(jsp|do)$ { location / { proxy_pass http://tomcat; }}   可启用主机名,也可不启用直接用端口访问。用域名访问还需要改DNS或者hosts设置,比较麻烦,我们就直接通过IP+端口访问就可以了。  tomcat服务器中可以新建一个host,也可使用原先的localhost默认主机。我们这次不用之前的localhost,而是自己新创建一个host标签,指定appBase在/data/myapp目录下。两个tomcat服务器都要执行如下操作:1vim /apps/tomcat/conf/server.xml   找到Engine标签,将默认主机修改为myapp1&ltEngine name=\"Catalina\" defaultHost=\"myapp\"&gt   找到localhost的&lt/Host&gt标签,在下面创建新的主机myapp,指定appBase为/data/myapp123&ltHost name=\"myapp\" appBase=\"/data/myapp\" unpackWARs=\"true\" autoDeploy=\"true\"&gt&lt/Host&gt   PS:Host name一般为主机域名,当一个tomcat中有多个host服务时,就是通过http报文请求头部的host信息来判断去访问哪个host服务的,当找不到对应的主机之后才访问defaultHost,我们这里因为只有启用了一个host,且为defaultHost,所以就无所谓,可以任意命名了,只需对应上就好。  之后我们要在/data/myapp目录下创建一个ROOT目录来作为tomcat访问的默认目录,注意ROOT是大写的。1mkdir -p /data/myapp/ROOT   为了方便我们看到效果,我们编写index.jsp时,调用一些函数,方便我们看到我们访问的tomcat主机的IP和端口、sessionID、访问时间。1vim /data/myapp/ROOT/index.jsp 1234567891011121314&lt%@ page import=\"java.util.*\" %&gt&lt!DOCTYPE html&gt&lthtml lang=\"en\"&gt&lthead&gt&ltmeta charset=\"UTF-8\"&gt&lttitle&gtlbjsptest&lt/title&gt&lt/head&gt&ltbody&gt&ltdiv&gtOn &lt%=request.getServerName() %&gt&lt/div&gt&ltdiv&gt&lt%=request.getLocalAddr() + \":\" + request.getLocalPort() %&gt&lt/div&gt&ltdiv&gtSessionID = &ltspan style=\"color:blue\"&gt&lt%=session.getId() %&gt&lt/span&gt&lt/div&gt&lt%=new Date()%&gt&lt/body&gt&lt/html&gt   此时,我们访问nginx的80端口,就可以被反向代理到后端的两个tomcat服务器上去。而这时,是轮询的调度方式,也就是一边一次,可以看到访问的主机和sessionID都是一直在变化的。效果如下图所示:   每一次的sessionID都没有重复过,这肯定不满足我们的需要。所以我们将nginx配置中#ip_hash;的#注释去掉,重启nginx服务,再看效果,如下图所示:  可以看到,每次刷新,主机IP和sessionID都不再变化,说明绑定session成功。 session复制集群  这是tomcat官方提供的解决方案,所有tomcat上都有全量的session,不过同步session信息会消耗带宽,而且所有服务器保存所有session信息也比较占用资源,对于tomcat这种本身就处于效率瓶颈的服务来说,高并发场景下超过若五个tomcat服务器,就不再建议使用。  配置详细可以参见官网配置说明。123456789101112131415161718192021222324252627282930313233343536373839&ltCluster className=\"org.apache.catalina.ha.tcp.SimpleTcpCluster\" channelSendOptions=\"8\"&gt &ltManager className=\"org.apache.catalina.ha.session.DeltaManager\" expireSessionsOnShutdown=\"false\" notifyListenersOnReplication=\"true\"/&gt &ltChannel className=\"org.apache.catalina.tribes.group.GroupChannel\"&gt &ltMembership className=\"org.apache.catalina.tribes.membership.McastService\" address=\"228.0.0.4\" port=\"45564\" frequency=\"500\" dropTime=\"3000\"/&gt &ltReceiver className=\"org.apache.catalina.tribes.transport.nio.NioReceiver\" address=\"auto\" port=\"4000\" autoBind=\"100\" selectorTimeout=\"5000\" maxThreads=\"6\"/&gt &ltSender className=\"org.apache.catalina.tribes.transport.ReplicationTransmitter\"&gt &ltTransport className=\"org.apache.catalina.tribes.transport.nio.PooledParallelSender\"/&gt &lt/Sender&gt &ltInterceptor className=\"org.apache.catalina.tribes.group.interceptors.TcpFailureDetector\"/&gt &ltInterceptor className=\"org.apache.catalina.tribes.group.interceptors.MessageDispatchInterceptor\"/&gt &lt/Channel&gt &ltValve className=\"org.apache.catalina.ha.tcp.ReplicationValve\" filter=\"\"/&gt &ltValve className=\"org.apache.catalina.ha.session.JvmRouteBinderValve\"/&gt &ltDeployer className=\"org.apache.catalina.ha.deploy.FarmWarDeployer\" tempDir=\"/tmp/war-temp/\" deployDir=\"/tmp/war-deploy/\" watchDir=\"/tmp/war-listen/\" watchEnabled=\"false\"/&gt &ltClusterListener className=\"org.apache.catalina.ha.session.ClusterSessionListener\"/&gt&lt/Cluster&gt   将官网上的这段集群配置,插入到两个tomcat服务器中我们创建的host标签中(也可插入引擎标签中,就相当于所有主机都生效),即&ltHost name标签与&lt/Host&gt标签的中间。  配置说明:12345678910Cluster 集群配置Manager 会话管理器配置Channel 信道配置 Membership 成员判定。使用什么多播地址、端口多少、间隔时长ms、超时时长ms。同一个多播地址和端口认为同属一个组。使用时修改这个多播地址,以防冲突 Receiver 接收器,多线程接收多个其他节点的心跳、会话信息。默认会从4000到4100依次尝试可用端口。 address=\"auto\",auto可能绑定到127.0.0.1上,所以一定要改为可以用的IP上去 Sender 多线程发送器,内部使用了tcp连接池。 Interceptor 拦截器ReplicationValve 检测哪些请求需要检测Session,Session数据是否有了变化,需要启动复制过程ClusterSessionListener 集群session侦听器   复制完官方文档里的配置,我们还需要修改接收器的ip为本机ip,不能使用auto,否则会无法同步session信息。  此外,还需要在应用的web.xml文件中最后一行&lt/web-app&gt标签的上面一行插入子标签&ltdistributable/&gt表示可分配。  操作如下:123mkdir /data/myapp/ROOT/WEB-INFcp /apps/tomcat/conf/web.xml /data/myapp/ROOT/WEB-INF/sed -i '/&lt\\/web-app&gt/i&ltdistributable/&gt' /data/myapp/ROOT/WEB-INF/web.xml   此时,如果我们将前端Nginx或者其他反向代理服务器中的源地址hash去掉,我们就可以看到,虽然访问的tomcat服务器在变化(服务器ip变化),而sessionID却是不变的,因为每个tomcat服务器上都有全量的相同的session信息。 session 共享服务器  这种实现方式其实才是本文标题中真正的session共享,毕竟共享经济炒的很热,我们都知道,所谓共享,共用才叫共享,前面提到的那种tomcat集群里会话信息人手一份可不能称作是共享。于是乎,我们想到用将session放到外部存储,所有的tomcat服务器都去外部存储中去查找sessionID。  储存session信息肯定不能存储在磁盘文件中,这样的读取写入性能都会很慢,放在mysql数据库中或者以硬盘文件的方式保存,高并发场景下的读取写入速度都将会大打折扣。所以我们要使用类似memcached或者redis这种Key/Value的非关系型数据库里,也被称作NoSQL。  memcached和redis这种键值对型数据库的数据信息都是存储在内存中的,读写效率都很高,而且由于没有复杂的表关系,采用的是哈希算法,他们对于信息的查找都是O(1),而不是类似mysql等数据库的O(n),意思就是说耗时/耗空间与总数据量大小无关,不会随着数据量的增大导致查找时间几何倍数的增长。  但也因为memcached和redis的数据都是储存在内存中的,而且memcached还不支持持久化,所以我们一定要做好高可用,一旦发生故障,会话信息将立即丢失,几乎没有恢复的可能。  要实现tomcat共享session服务器,首先,我们要让tomcat将session储存到memcached或者redis等外部存储上,这就需要我们对tomcat进行配置,其次我们要将session信息序列化为变为字节流以便能储存在session服务器中,还要能将session服务器中的数据反序列化为可以识别的session信息,最后,当然我们还需要一个客户端来跟后端的session服务器通信,才能将数据写入和读取。  这想实现确实也比较复杂,不过在github已有开源解决方案(网址是https://github.com/magro/memcached-session-manager),memcached-session-manager,简称msm,后端采用memcached或者redis都可以(之前只支持memcached,因而得名msm,后来支持redis后,人们还是习惯叫它msm),且已经完成了对tomcat的session共享的配置支持(支持tomcat6.X、7.X、8.X、9.X),我们直接去下载对应版本的去使用就可以了。  根据项目的介绍文档,我们知道,想实现tomcat的session共享,我们至少需要配套的工具有: tomcat的session管理工具 memcached-session-manager 与session服务器通信的客户端如果是memcached,则建议使用spymemcached.jar如果是redis,则建议使用jedis.jar 将session信息序列化的工具,作者推荐使用kryo。   这些工具官网上也都提供了下载链接,我们直接下载下来即可。  kryo如下图所示  其他工具包如下图所示  将这些jar包统统拷贝到tomcat服务器的lib目录下(改变lib目录下的jar包需重启tomcat服务才能生效)  使用msm搭建session共享服务器,如果后端为memcached,则有两种模式可以选,分别是sticky模式和non-sticky模式,后端为redis,则使用类似non-sticky模式。 sticky模式  以两台服务器为例,将tomcat1和memcached1部署在一台服务器上(简称为t1、m1),tomcat2和memcached2部署在另一台服务器上(简称为t2、m2)为例,结构图如下图所示。12345&ltt1&gt &ltt2&gt . \\ / . . X . . / \\ .&ltm1&gt &ltm2&gt   实现原理:当请求结束时Tomcat的session会送给memcached备份。即Tomcat session为主session,memcached session为备session,使用memcached相当于备份了一份Session。查询Session时Tomcat会优先使用自己内存的Session,Tomcat通过jvmRoute发现不是自己的Session,便从memcached中找到该Session,更新本机Session,请求完成后更新memcached。  可能有的朋友看的一头雾水,这到底是个什么结构。其实很简单,sticky模式就是t1的session信息还是储存在t1上,不过以m2为备用数据库,t2的session信息也是放在t2中储存,以m1服务器为备用服务器。这就意味着,用户在访问t1时,是从t1获取session信息,当t1挂掉或者整个节点1服务器挂掉之后,用户会被调度到t2上,而t2本地中没有session信息时,就会去m2中上找相关sessionID,而m2因为是t1的备用存储,所以有跟t1完全相同的session信息,于是用户的sessionID就可以被t2识别;而当m2备用存储服务挂掉之后,t1服务会通过检测发现自己没有备用存储,就会自动将m1也指定为自己的备用存储,将备份信息也同步至m1中,于是用户若再从t2访问时,虽然因为m2挂掉,其中的数据都无法访问,但t2就可以从m1上读取到对应的sessionID并同步到t2本身的存储中,也可以保持之前的会话信息。  修改的配置也很简单,依照官网说明,将下面的代码标签插入/conf/context.xml文件中的context标签结尾就可以了123456&ltManager className=\"de.javakaffee.web.msm.MemcachedBackupSessionManager\"memcachedNodes=\"n1:192.168.32.231:11211,n2:192.168.32.232:11211\"failoverNodes=\"n1\"requestUriIgnorePattern=\".*\\.(ico|png|gif|jpg|css|js)$\"transcoderFactoryClass=\"de.javakaffee.web.msm.serializer.kryo.KryoTranscoderFactory\"/&gt   n1、n2只是memcached的节点别名,可以重新命名。failoverNodes是指故障转移节点,也就是发生故障之后的备用节点,所以在n1节点上,n1是备用节点,n2是主存储节点。另一台Tomcat中配置将failoverNodes改为n2,意思是其主节点是n1,备用节点是n2。若配置成功,在/apps/tomcat/log/catalina.out文件中看到如下信息。1tail -n 20 /apps/tomcat/logs/catalina.out 12345678923-Nov-2019 13:35:54.187 INFO [myapp-startStop-1] de.javakaffee.web.msm.MemcachedSessionService.startInternal --------- finished initialization:- sticky: true- operation timeout: 1000- node ids: [n1]- failover node ids: [n2]- storage key prefix: null- locking mode: null (expiration: 5s)-------- 此时在访问我们的前端代理(取消ipHASH绑定)就会看到下面界面,将节点2 关机,可以看到访问ip固定为192.168.32.231,而使用的memcached变成了n1节点。 non-sticky模式  从msm 1.4.0之后开始支持non-sticky模式。Tomcat session为中转Session,如果n1为主session,n2为备session,则产生的新的Session会发送给主、备memcached,并清除本地Session,也就是说tomcat本身不储存session信息,只负责产生session。  需要注意的是,如果n1下线,n2转换为主节点。n1再次上线,n2依然是主Session存储节点。  配置方法与sticky大致相同,不过在/conf/context.xml文件中的context标签结尾插入代码略有不同,具体代码如下12345678&ltManager className=\"de.javakaffee.web.msm.MemcachedBackupSessionManager\"memcachedNodes=\"n1:192.168.32.231:11211,n2:192.168.32.232:11211\"sticky=\"false\"sessionBackupAsync=\"false\"lockingMode=\"uriPattern:/path1|/path2\"requestUriIgnorePattern=\".*\\.(ico|png|gif|jpg|css|js)$\"transcoderFactoryClass=\"de.javakaffee.web.msm.serializer.kryo.KryoTranscoderFactory\"/&gt   重启tomcat服务后生效。此时在/apps/tomcat/log/catalina.out文件中看到如下信息。1tail -n 20 /apps/tomcat/logs/catalina.out 12345678923-Nov-2019 13:43:05.863 INFO [myapp-startStop-1] de.javakaffee.web.msm.MemcachedSessionService.startInternal --------- finished initialization:- sticky: false- operation timeout: 1000- node ids: [n1, n2]- failover node ids: []- storage key prefix: null- locking mode: uriPattern:/path1|/path2 (expiration: 5s)--------   再次尝试访问负载代理服务器,发现同样实现了访问tomcatIP变化,sessionID不变,说明配置成功。  而后端使用redis作为session共享服务器时,仅支持non-stricky模式。建议用另外的服务器安装redis服务,并修改监听IP后启动,tomcat服务器中将jedis.jarjar包拷贝至tomcat安装路径下lib目录,同样在/conf/context.xml文件中的context标签结尾插入下面的代码即可(例如redis服务器IP端口为192.168.32.233:6379,可配置redis集群,可参考我之前博客redis高可用配置)。12345678&ltManager className=\"de.javakaffee.web.msm.MemcachedBackupSessionManager\"memcachedNodes=\"redis://192.168.32.233:6379\"sticky=\"false\"sessionBackupAsync=\"false\"lockingMode=\"uriPattern:/path1|/path2\"requestUriIgnorePattern=\".*\\.(ico|png|gif|jpg|css|js)$\"transcoderFactoryClass=\"de.javakaffee.web.msm.serializer.kryo.KryoTranscoderFactory\"/&gt","categories":[{"name":"tomcat","slug":"tomcat","permalink":"https://wudihechao.github.io/categories/tomcat/"}],"tags":[{"name":"tomcat","slug":"tomcat","permalink":"https://wudihechao.github.io/tags/tomcat/"},{"name":"redis","slug":"redis","permalink":"https://wudihechao.github.io/tags/redis/"},{"name":"集群","slug":"集群","permalink":"https://wudihechao.github.io/tags/集群/"},{"name":"MSM","slug":"MSM","permalink":"https://wudihechao.github.io/tags/MSM/"},{"name":"session","slug":"session","permalink":"https://wudihechao.github.io/tags/session/"}],"keywords":[{"name":"tomcat","slug":"tomcat","permalink":"https://wudihechao.github.io/categories/tomcat/"}]},{"title":"linux上部署与配置tomcat","slug":"linux上部署与配置tomcat","date":"2019-11-21T10:58:03.000Z","updated":"2019-11-22T01:54:03.124Z","comments":true,"path":"/blog/b365780e.html","link":"","permalink":"https://wudihechao.github.io/blog/b365780e.html","excerpt":"  Tomcat 服务器是一个免费的开放源代码的Web 应用服务器,属于轻量级应用服务器。因为Tomcat 技术先进、性能稳定,而且免费,因而深受Java 爱好者的喜爱并得到了部分软件开发商的认可,成为目前比较流行的Web 应用服务器。本文将详细讲解tomcat在linux环境(以CentOS为例)中的安装与配置。","text":"  Tomcat 服务器是一个免费的开放源代码的Web 应用服务器,属于轻量级应用服务器。因为Tomcat 技术先进、性能稳定,而且免费,因而深受Java 爱好者的喜爱并得到了部分软件开发商的认可,成为目前比较流行的Web 应用服务器。本文将详细讲解tomcat在linux环境(以CentOS为例)中的安装与配置。 安装jdk  JDK是java Development Kit的缩写,即java语言的软件开发包,是javac、javap、jdb等开发、调试、编译工具。  OpenJDK是Sun公司采用GPL v2协议发布的JDK开源版本,可以用于商业用途,于2009年正式发布。它是基于JDK7的beta版开发,但为了也将java SE 6开源,从OpenJDK7的b20构建反向分之开发,从中剥离了不符合java SE 6规范的代码,发布了OpenJDK 6。所以OpenJDK6和JDK6没有关系。  在CentOS中,可以选择yum安装openjdk1yum install java-1.8.0-openjdk   通过java -version可以看到版本信息为123openjdk version \"1.8.0_212\"OpenJDK Runtime Environment (build 1.8,0_212-b04)OpenJDK 64-Bit Server VM (build 25.212-b04,mixed mode)   也可以去Oracle官网下载JDK 8的rpm包安装(需要注册)1yum install jdk-8u191-linux-x64,rpm   而通过java -version看到版本信息为1234[root@localhost ~]# java -versionjava version \"1.8.0_231\"Java(TM) SE Runtime Environment (build 1.8.0_231-b11)Java HotSpot(TM) 64-Bit Server VM (build 25.231-b11, mixed mode) 部署tomcat  确定JDK版本之后,我们就可以准备开始部署tomcat了。  首先要去官网下载源码包,国内用的是清华大学的镜像下载地址,下载速度还是可以接受的。  生产中我们一般不会选用最新版的tomcat9,而是选用tomcat8更为稳妥。tomcat8的下载链接是http://mirrors.tuna.tsinghua.edu.cn/apache/tomcat/tomcat-8/v8.5.47/bin/apache-tomcat-8.5.47.tar.gz1wget http://mirrors.tuna.tsinghua.edu.cn/apache/tomcat/tomcat-8/v8.5.47/bin/apache-tomcat-8.5.47.tar.gz   解压到想要安装的任意指定目录,例如/apps/tomcat/目录1tar xvf apache-tomcat-8.5.47.tar.gz -C /apps/   更改目录名称(也可以用软连接方式)。1mv /apps/apache-tomcat-8.5.47 /apps/tomcat 配置环境变量  jdk需要配置环境变量才可以找到,否则会无法访问jsp文件。  需要在环境变量配置文件中加入12export JAVA_HOME=/usr/java/defaultexport PATH=$JAVA_HOME/bin:$PATH   于是,运行下面命令即可1234cat > /etc/profile.d/jdk.sh <<\"END\"export JAVA_HOME=/usr/java/defaultexport PATH=$JAVA_HOME/bin:$PATHEND   运行命令使环境变量生效1. /etc/profile.d/jdk.sh 修改进程属主  一般来说,tomcat服务如果以root身份启动,风险还是很高的,因为tomcat本身bug还是有不少的,如果被别有用心之人加以利用,入侵进来,就可以获得root权限,这造成的后果绝对是灾难级的,这甚至会威胁到其他服务器的安全。为了规避这一风险,我们一般都会将tomcat服务以一个系统用户的身份启动,例如java用户。  创建java用户useradd -r java  更改服务目录的属主属组chown -R tomcat/*  启动服务时,我们使用命令su - java -c '/apps/tomcat/bin/startup.sh'来启动,就可以以java用户的身份来启动进程了。 配置文件  tomcat中配置文件目录在tomcat/conf/server.xml,其中需要注意的是:1<Server port=\"8005\" shutdown=\"SHUTDOWN\">   这代表管理端监听在8005端口,向管理端发送SHUTDOWN指令就会是tomcat直接终止,而且这是不需要密码权限控制的。如果在生产环境中,如果有任何人有意或者无意向tomcat服务器的8005端口发送了SHUTDOWN指令,整个服务都会被终止掉,,而且还不需要密码验证,这对我们的影响无疑是巨大的,所以我们通常都会将此处的管理监听端口改掉,或将shutdown指令设为别的复杂口令,让别人无法轻易或不小心将我们的服务器shutdown~  可采取MD5值的方式,选任一文件做MD5运算后得到的随机数值即为口令。1sed -i \"s@SHUTDOWN@`md5sum /root/.ssh/authorized_keys |cut -d' ' -f1`@\" tomcat/conf/server.xml   此时,就可以通过安装目录下的tomcat/bin/startup.sh来启动tomcat服务了。1su - java -c ' /apps/tomcat/bin/startup.sh'   然后查看端口1ss -tanl   即可看到8080端口已经处于监听状态了12345678910[root@localhost ~]# ss -tanlState Recv-Q Send-Q Local Address:Port Peer Address:Port LISTEN 0 128 *:22 *:* LISTEN 0 100 127.0.0.1:25 *:* LISTEN 0 100 :::8009 :::* LISTEN 0 100 :::8080 :::* LISTEN 0 128 :::22 :::* LISTEN 0 100 ::1:25 :::* LISTEN 0 1 ::ffff:127.0.0.1:8005 :::* [root@localhost ~]#   访问tomcat服务器的ip的8080端口,可看到如下图所示界面,即表示tomcat安装成功。  不过,想使用tomcat自带的Manager App及Host Manager工具,还需要做一些额外的配置,此时如果直接访问会显示403权限拒绝,如下图所示。 修改应用访问控制  错误提示说明已经说得很明白了,首先确保已经修改了应用的反问控制权限,,如果还没有,you’ll need to edit the Manager’s context.xml file。  那这个context.xml在哪里呢?它是指应用的上下文设置文件,路径就是tomcat目录下的webapps/manager/META-INF/context.xml文件,另一个应用Host Manager的路径就是tomcat目录下的webapps/host-manager/META-INF/context.xml  所以我们要修改这两个配置文件中的访问控制,将我们的访问IP加到里面,这样我们的访问IP才有权限去访问这两个管理应用。123<Context antiResourceLocking=\"false\" privileged=\"true\" > <Valve className=\"org.apache.catalina.valves.RemoteAddrValve\" allow=\"127\\.\\d+\\.\\d+\\.\\d+|::1|0:0:0:0:0:0:0:1|172.18.*|192.168.32.*\" />   在allow=标签后面,加上我们的ip,支持通配符写法和PCRE格式的正则表达式写法,用|隔开。两个应用的配置文件都要修改。 创建权限角色光将我们的来访IP加入许可白名单还是不够的,我们还需要一个有权限的账户才可以访问这些管理工具路径是tomcat目录下的conf/tomcat-users.xml文件。1234567<tomcat-users xmlns=\"http://tomcat.apache.org/xml\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://tomcat.apache.org/xml tomcat-users.xsd\" version=\"1.0\"> <role rolename=\"manager-gui\"/> <role rolename=\"admin-gui\"/> <user username=\"admin\" password=\"123456\" roles=\"manager-gui,admin-gui\"/>   在下面加入后三行,先创建角色"manager-gui"和"admin-gui",分别是Manager App的管理员和Host Manager的管理员。然后设置用户名密码及其所处的角色,也可以设置两个不同的用户来分别充当这两个角色。用户名密码可自行设置。123<role rolename=\"manager-gui\"/><role rolename=\"admin-gui\"/><user username=\"admin\" password=\"123456\" roles=\"manager-gui,admin-gui\"/>   有了管理员权限的账户,在被允许的客户端上就可以访问Manager App和Host Manager这两个工具了。点击图标访问,出现如下图所示的提示输入用户名密码弹窗,说明配置正确。 附:一键部署tomcat脚本  附上我自己用的tomcat脚本供大家参考(未配置管理工具)1234567891011121314151617181920212223DST=\"/apps\"[ -a jdk-8u231-linux-x64.rpm ] || { echo ' the absence of jdk' ;exit 1;}[ -d $DST/*tomcat* ] && { echo \"tomcat is aready installed into $DST/tomcat\" ;exit 2;}id java &>/dev/null && { echo 'user java is exist,redis is aready installed' ; exit 3;}iptables -Fsystemctl disable firewalldsed -i '@SELINUX=enforcing@SELINUX=disabled@' /etc/selinux/configsetenforce 0mkdir -p /data$DST#mkdir -p $DSTln -s /data$DST $DSTyum install jdk-8u231-linux-x64.rpmtar xf *tomcat-*.tar.* -C $DST/mv $DST/*tomcat-* $DST/tomcatsed -i \"s@SHUTDOWN@`md5sum /root/.ssh/authorized_keys |cut -d' ' -f1`@\" $DST/tomcat/conf/server.xmlcat > /etc/profile.d/jdk.sh <<\"END\"export JAVA_HOME=/usr/java/defaultexport PATH=$JAVA_HOME/bin:$PATHEND. /etc/profile.d/jdk.shuseradd -r javachown -R java.java $DST/tomcat/*su - java -c '$DST/tomcat/bin/startup.sh'","categories":[{"name":"tomcat","slug":"tomcat","permalink":"https://wudihechao.github.io/categories/tomcat/"}],"tags":[{"name":"linux","slug":"linux","permalink":"https://wudihechao.github.io/tags/linux/"},{"name":"企业级应用","slug":"企业级应用","permalink":"https://wudihechao.github.io/tags/企业级应用/"},{"name":"tomcat","slug":"tomcat","permalink":"https://wudihechao.github.io/tags/tomcat/"},{"name":"一键安装","slug":"一键安装","permalink":"https://wudihechao.github.io/tags/一键安装/"},{"name":"JAVA","slug":"JAVA","permalink":"https://wudihechao.github.io/tags/JAVA/"}],"keywords":[{"name":"tomcat","slug":"tomcat","permalink":"https://wudihechao.github.io/categories/tomcat/"}]},{"title":"redis高可用配置","slug":"redis高可用配置","date":"2019-11-15T08:19:19.000Z","updated":"2019-12-04T02:03:05.239Z","comments":true,"path":"/blog/14b8983d.html","link":"","permalink":"https://wudihechao.github.io/blog/14b8983d.html","excerpt":"  redis高可用一般有两种方式实现:哨兵和集群。在哨兵 sentinel 机制中,可以解决 redis 高可用的问题, 即当 master 故障后可以自动将 slave 提升为 master 从而可以保证 redis 服务的正常使用,但是无法解决 redis 单机写入的瓶颈问题, 即单机的 redis 写入性能受限于单机的内存大小、 并发数量、 网卡速率等因素。  redis 官方在 redis 3.0 版本之后推出了无中心架构的 redis cluster机制, 在无中心的 redis 集群当中,其每个节点保存当前节点数据和整个集群状态,每个节点都和其他所有节点连接, 特点如下:","text":"  redis高可用一般有两种方式实现:哨兵和集群。在哨兵 sentinel 机制中,可以解决 redis 高可用的问题, 即当 master 故障后可以自动将 slave 提升为 master 从而可以保证 redis 服务的正常使用,但是无法解决 redis 单机写入的瓶颈问题, 即单机的 redis 写入性能受限于单机的内存大小、 并发数量、 网卡速率等因素。  redis 官方在 redis 3.0 版本之后推出了无中心架构的 redis cluster机制, 在无中心的 redis 集群当中,其每个节点保存当前节点数据和整个集群状态,每个节点都和其他所有节点连接, 特点如下:  1:所有 Redis 节点使用(PING 机制)互联。  2:集群中某个节点的失效, 是整个集群中超过半数的节点监测都失效才算真正的失效(类似Sdown和Odown) 。  3:客户端不需要 proxy 即可直接连接 redis, 应用程序需要写全部的 redis 服务器 IP。  4:redis cluster 把所有的 redis node 映射到 0-16383 个槽位(slot)上, 读写需要到指定的 redis node 上进行操作,因此有多少个 reids node 相当于 redis 并发扩展了多少倍。  5:Redis cluster 预先分配 16384 个(slot)槽位,当需要在 redis 集群中写入一个 key -value 的时候,会使用 CRC16(key) mod 16384 之后的值,决定将 key 写入值哪一个槽位从而决定写入哪一个 Redis 节点上, 从而有效解决单机瓶颈。 Sentinel(哨兵)手动配置主从  需要手动先指定某一台 Redis 服务器为 master,然后将其他 slave 服务器修改配置文件或使用命令配置为 master 服务器的 slave。哨兵的前提是已经手动实现了一个 redis master-slave 的运行环境。  例如我们将192.168.32.81上的redis服务设置为master,192.168.32.82、192.168.32.83设置为slave,192.168.32.81中配置文件masterauth 设置为123456  在192.168.32.82上设置主节点信息SLAVEOF 192.168.32.81 6379(redis5版本命令为REPLICAOF 192.168.7.101 6379)1234192.168.32.82:6379> SLAVEOF 192.168.32.81 6379OK192.168.32.82:6379> CONFIG SET masterauth \"123456\"OK   同样,192.168.32.83也要设置主节点信息及密码1234192.168.7.103:6379> SLAVEOF 192.168.7.101 6379OK192.168.7.103:6379> CONFIG SET masterauth \"123456\"OK   此时通过info Replication命令,在两个从节点上可以看到角色已变更为slave,且master_link_status状态要为up,说明主从结构配置完成。123456127.0.0.1:6379> info Replication# Replicationrole:slavemaster_host:192.168.32.81master_port:6379master_link_status:up   需要注意的是:不同的 redis 版本之间存在兼容性问题, 因此各 master 和 slave 之间必须保持版本一致,且redis slave 也要开启持久化并设置和 master 同样的连接密码,此外,如果开启了安全模式,未设置密码的话也会导致主从无法连接。 配置Sentinel  哨兵可以不和 Redis 服务器部署在一起。可以使用另外不同的机子,一般采用单数个哨兵(至少3个)。不过为了节约服务器,我们还是和redis服务器部署到一起。  3个机子都手动添加Sentinel配置文件  vim /apps/redis/etc/sentinel.conf123456789101112bind 0.0.0.0port 26379daemonize yespidfile \"redis-sentinel.pid\"logfile \"sentinel_26379.log\"dir \"/usr/local/redis\"sentinel monitor mymaster 192.168.32.81 6379 2 #法定人数限制(quorum),即有几个slave 认为 master down 了就进行故障转移sentinel auth-pass mymaster 123456sentinel down-after-milliseconds mymaster 30000 #(SDOWN)主观下线的时间sentinel parallel-syncs mymaster 1 #发生故障转移时候同时向新 master 同步数据的slave 数量, 数字越小总同步时间越长sentinel failover-timeout mymaster 180000 #所有 slaves 指向新的 master 所需的超时时间sentinel deny-scripts-reconfig yes #禁止修改脚本   通过/apps/redis/bin/redis-sentinel /apps/redis/etc/sentinel.conf命令指定配置文件,启动哨兵,此时通过ss -tanl命令可以看到已监听26379端口,说明哨兵服务已启动。123456789[root@CentOS8 ~]# redis-cli -h 192.168.32.81 -p 26379192.168.32.81:26379> info Sentinel# Sentinelsentinel_masters:1sentinel_tilt:0sentinel_running_scripts:0sentinel_scripts_queue_length:0sentinel_simulate_failure_flags:0master0:name=mymaster,status=ok,address=192.168.32.81:6379,slaves=2,sentinels=3   可以通过redis-cli指定26379端口连接哨兵服务器,info Sentinel查看哨兵状态,显示status=ok,slaves=2,sentinels=3,与我们之前设计的相同,说明哨兵服务正常。  此时,当master故障后,哨兵会通过投票机制,选举一个slave作为新的master,继续实现主从结构,而程序应用也会在主从切换的时候收到通知,Jedis 会进行连接的切换(在 JedisPool 中添加了 Sentinel 和MasterName 参数, JRedis Sentinel 底层基于 Redis 订阅实现 Redis 主从服务的切换通知, 当 Reids 发生主从切换时, Sentinel 会发送通知主动通知 Jedis 进行连接的切换, JedisSentinelPool 在每次从连接池中获取链接对象的时候,都要对连接对象进行检测,如果此链接和 Sentinel 的 Master 服务连接参数不一致,则会关闭此连接,重新获取新的 Jedis 连接对象。),实现redis的高可用。 Redis Cluster  在哨兵 sentinel 机制中,可以解决 redis 高可用的问题, 即当 master 故障后可以自动将 slave 提升为 master 从而可以保证 redis 服务的正常使用,但是无法解决 redis 单机写入的瓶颈问题, 即单机的 redis 写入性能受限于单机的内存大小、 并发数量、 网卡速率等因素,所以Redis自带的Cluster就是很好的一个解决方案。 集群前提  1.每个 redis node 节点采用相同的硬件配置、相同的密码、 相同的 redis 版本。  2.每个节点必须开启的参数cluster-enabled yes #必须开启集群状态, 开启后 redis 进程会有 cluster 显示cluster-config-file nodes-6380.conf #此文件有 redis cluster 集群自动创建和维护,不需要任何手动操作  3.所有 redis 服务器必须没有任何数据  4.先启动为单机 redis 且没有任何 key value 集群部署Redis 3、4  Redis 3 和 4 版本需要使用到集群管理工具 redis-trib.rb,这个工具是 redis 官方推出的管理 redis 集群的工具,集成在 redis 的源码 src 目录下,是基于 redis 提供的集群命令封装成简单、便捷、实用的操作工具, redis-trib.rb 是 redis 作者用 ruby 开发完成的。所以需要有ruby环境,如果系统自带的ruby版本较低(需要Ruby version >= 2.3.0),那就需要重新编译安装更高版本。 编译安装ruby12 yum remove ruby rubygems -ywget https://cache.ruby-lang.org/pub/ruby/2.5/ruby-2.5.5.tar.gz 1234tar xf ruby-2.5.5.tar.gzcd ruby-2.5.5./configure make -j 4 && make install   安装redis的gem包(Gem是一个管理Ruby库和程序的标准包,它通过Ruby Gem(如 http://rubygems.org/ )源来查找、安装、升级和卸载软件包,非常的便捷)。 1gem install redis   之后就可以使用redis-trib.rb命令来创建管理集群了。 集群创建  通过命令redis-trib.rb create来创建集群,选项–replicas 指定 master 的slave数量.  需要注意的是,集群至少也需要3台master,(–replicas 指定的slave数量可以为0,不过一般不建议),如果指定slave数量为2的话就至少需要9个redis服务器了(3主6从),比较浪费资源,所以我们一般设置replicas数值为1。  如果设置slave数量为0,则所有的6个服务器均为master,无法实现高可,集群信息如下图所示:12345678127.0.0.1:6379> cluster nodesc65ab2a5e9d8a9435d54f0ef7db302c62b6f9308 192.168.32.82:6379@16379 master - 0 1573794294000 3 connected 5461-8191a13d711b6b54909e5aed44cc8f75195915bbfb95 192.168.32.85:6379@16379 master - 0 1573794293235 6 connected 13653-163835d614e18da36f9d7b3536566205281743adc95c5 192.168.32.8:6379@16379 myself,master - 0 1573794294000 1 connected 0-27306fe42df7ea9c4dbb00d9bae6f7e4238367b1089b 192.168.32.81:6379@16379 master - 0 1573794292212 2 connected 2731-5460e3beb9f45b73b265050499ee093ed1a473a3d566 192.168.32.83:6379@16379 master - 0 1573794295286 4 connected 8192-109228ae323030e3ae28519a27058c6592b94d1aae734 192.168.32.84:6379@16379 master - 0 1573794295000 5 connected 10923-13652127.0.0.1:6379>   如果设置slave数量为2,若创建集群是加入的服务器数量不够9台,则会报错:123456789101112[root@CentOS8 ~]#redis-trib.rb create --replicas 2 \\192.168.32.8:6379 \\192.168.32.81:6379 \\192.168.32.82:6379 \\192.168.32.83:6379 \\192.168.32.84:6379 \\192.168.32.85:6379>>> Creating cluster*** ERROR: Invalid configuration for cluster creation.*** Redis Cluster requires at least 3 master nodes.*** This is not possible with 6 nodes and 2 replicas per node.*** At least 9 nodes are required.   若之前有创建集群失败的一些操作,或集群中有的redis服务器中有数据信息的话,也是无法创建成功的。会报类似如下如下信息:1/usr/local/share/gems/gems/redis-4.1.3/lib/redis/client.rb:126:in `call': ERR Slot 2823 is already busy (Redis::CommandError)   所以我们一般在创建集群之前,先连上redis服务器清空数据,并重置集群关系,确保所有的redis服务器都没有数据且都为单机master。12FLUSHALLcluster reset   然后执行创建集群命令1234567redis-trib.rb create --replicas 1 \\192.168.32.8:6379 \\192.168.32.81:6379 \\192.168.32.82:6379 \\192.168.32.83:6379 \\192.168.32.84:6379 \\192.168.32.85:6379   即可成功创建集群。连接redis,登陆,即可看到集群主从关系。12345678910111213[root@CentOS8 ~]#redis-cli127.0.0.1:6379> cluster nodesNOAUTH Authentication required.127.0.0.1:6379> auth 123456OK127.0.0.1:6379> cluster nodesc65ab2a5e9d8a9435d54f0ef7db302c62b6f9308 192.168.32.82:6379@16379 master - 0 1573798716854 3 connected 10923-16383a13d711b6b54909e5aed44cc8f75195915bbfb95 192.168.32.85:6379@16379 slave 6fe42df7ea9c4dbb00d9bae6f7e4238367b1089b 0 1573798715000 6 connected5d614e18da36f9d7b3536566205281743adc95c5 192.168.32.8:6379@16379 myself,master - 0 1573798716000 1 connected 0-54606fe42df7ea9c4dbb00d9bae6f7e4238367b1089b 192.168.32.81:6379@16379 master - 0 1573798717877 2 connected 5461-109228ae323030e3ae28519a27058c6592b94d1aae734 192.168.32.84:6379@16379 slave 5d614e18da36f9d7b3536566205281743adc95c5 0 1573798715834 5 connectede3beb9f45b73b265050499ee093ed1a473a3d566 192.168.32.83:6379@16379 slave c65ab2a5e9d8a9435d54f0ef7db302c62b6f9308 0 1573798716000 4 connected127.0.0.1:6379>   监控集群状态1redis-trib.rb check 192.168.32.8:6379 Redis 5  Redis 5 版本可以直接用redis-cli客户端命令加各种命令参数来管理创建集群(配置文件中如果已配置正确的密码,则不需要-a 选项,-a选项会有warning警告)。  创建集群1234567redis-cli -a 123456 --cluster create --cluster-replicas 1 \\192.168.32.8:6379 \\192.168.32.81:6379 \\192.168.32.82:6379 \\192.168.32.83:6379 \\192.168.32.84:6379 \\192.168.32.85:6379   监控集群状态1redis-cli -a 123456 --cluster check 192.168.32.8:6379 集群管理维护  集群运行时间长久之后,难免由于硬件故障、网络规划、 业务增长等原因对已有集群进行相应的调整, 比如增加 Redis node 节点、 减少节点、 节点迁移、更换服务器等。 动态添加节点  同步之前 Redis node 的配置文件到 192.168.32.86 Redis 编译安装目录, 注意修改配置文件的监听 IP。12scp redis.conf 192.168.32.86:/apps/redis/etc/scp redis_6380.conf 192.168.32.86:/apps/redis/etc/   对预备加入的redis服务器清除数据并执行集群重置12FLUSHALLcluster reset   redis3、4 中添加节点(Adding node 192.168.32.87:6379 to cluster 192.168.32.8:6379)1redis-trib.rb add-node 192.168.32.87:6379 192.168.32.8:6379   redis5 中添加节点(Adding node 192.168.32.87:6379 to cluster 192.168.32.8:6379)1redis-cli --cluster add-node 192.168.32.87:6379 192.168.32.8:6379 重新分配槽位  集群槽位迁移时,迁出的服务器中不能有数据,否则会迁移失败并终止,且需要修复集群,才可以进行第二次迁移。  redis3、4 中对新加的主机重新分配槽位(后可跟集群中任意主机ip:port):1redis-trib.rb reshard 192.168.32.8:6379   redis5 对新加的主机重新分配槽位(后可跟集群中任意主机ip:port):1redis-cli --cluster reshard 192.168.32.8:6379   输入命令后根据提示输入123456789How many slots do you want to move (from 1 to 16384)? 4096 #需要迁移多少槽位What is the receiving node ID? 5d614e18da36f9d7b3536566205281743adc95c5 #选择迁移到哪个redis主机What is the receiving node ID? 6bffc0037eea959f0dcc11aca657f928d86cfc75Please enter all the source node IDs. Type 'all' to use all the nodes as source nodes for the hash slots. Type 'done' once you entered all the source nodes IDs.Source node #1:6bffc0037eea959f0dcc11aca657f928d86cfc75 #选择从哪个主机迁出Source node #2:done #输入done结束选择Do you want to proceed with the proposed reshard plan (yes/no)? yes #是否执行,yes确认,之后就会迁移完毕 修复集群  如果迁移失败,则需要修复集群。   redis3、4 中对对报错redis服务器进行单独修复(后跟需要修复的主机ip:port):1redis-trib.rb fix 192.168.32.82:6379   redis5 中对对报错redis服务器进行单独修复(后跟需要修复的主机ip:port):1redis-cli --cluster fix 192.168.32.82:6379 配置主从关系  在整个 Redis cluster 集群中,每个 master 至少有一个 slave。也可以有多个,但是至少要有一个提供数据备份和服务高可用。  redis-trib.rb info 192.168.32.8:637912345678[root@CentOS8 ~]#redis-trib.rb info 192.168.32.8:6379192.168.32.8:6379 (5d614e18...) -> 0 keys | 4096 slots | 1 slaves.192.168.32.82:6379 (c65ab2a5...) -> 0 keys | 4096 slots | 0 slaves.192.168.32.81:6379 (6fe42df7...) -> 0 keys | 4096 slots | 1 slaves.192.168.32.87:6379 (6bffc003...) -> 0 keys | 4096 slots | 1 slaves.192.168.32.86:6379 (e06732b2...) -> 0 keys | 0 slots | 0 slaves.[OK] 0 keys in 5 masters.0.00 keys per slot on average.   我们看到之前的3主3从结构因为我们加了两个服务器变成了5主3从,新加的两个服务器,我们需要将它们设置为一主一从来实现高可用。于是我们要将192.168.32.86变为192.168.32.82的slave。  连接登陆192.168.32.86上的redis服务器,先查好192.168。32.82的ID为c65ab2a5e9d8a9435d54f0ef7db302c62b6f9308,执行1cluster replicate c65ab2a5e9d8a9435d54f0ef7db302c62b6f9308 删除节点  删除节点,需要先将redis主机上的槽位都迁移到其他主机上才可以操作。  迁移完成后,redis3、4 删除节点(要删除的redis主机要写节点号,集群可以写集群中任意主机ip+port) 1234[root@CentOS8 ~]#redis-trib.rb del-node 192.168.32.8:6379 6bffc0037eea959f0dcc11aca657f928d86cfc75>>> Removing node 6bffc0037eea959f0dcc11aca657f928d86cfc75 from cluster 192.168.32.8:6379>>> Sending CLUSTER FORGET messages to the cluster...>>> SHUTDOWN the node.   redis5 删除节点1redis-cli --cluster del-node 192.168.32.8:6379 6bffc0037eea959f0dcc11aca657f928d86cfc75   主节点被删除之后,其之前它的 slave 自动称为了 Redis 集群中其他 master的 slave,此节点如果不需要也可以一并删除。","categories":[{"name":"NotOnlySQL","slug":"NotOnlySQL","permalink":"https://wudihechao.github.io/categories/NotOnlySQL/"}],"tags":[{"name":"企业级应用","slug":"企业级应用","permalink":"https://wudihechao.github.io/tags/企业级应用/"},{"name":"高可用","slug":"高可用","permalink":"https://wudihechao.github.io/tags/高可用/"},{"name":"redis","slug":"redis","permalink":"https://wudihechao.github.io/tags/redis/"},{"name":"集群","slug":"集群","permalink":"https://wudihechao.github.io/tags/集群/"},{"name":"Sentinel","slug":"Sentinel","permalink":"https://wudihechao.github.io/tags/Sentinel/"}],"keywords":[{"name":"NotOnlySQL","slug":"NotOnlySQL","permalink":"https://wudihechao.github.io/categories/NotOnlySQL/"}]},{"title":"一键源码编译安装redis","slug":"一键源码编译安装redis","date":"2019-11-13T03:06:46.000Z","updated":"2019-12-04T02:02:46.558Z","comments":true,"path":"/blog/f2c1a89e.html","link":"","permalink":"https://wudihechao.github.io/blog/f2c1a89e.html","excerpt":"  Redis 和 Memcached 是非关系型数据库也称为 NoSQL 数据库, MySQL、 Mariadb、 SQLServer、 PostgreSQL、 Oracle 数据库属于关系型数据(RDBMS, Relational Database Management System)。  Redis(Remote Dictionary Server)在 2009 年发布, 开发者 Salvatore Sanfilippo 是意大利开发者, 他本想为自己的公司开发一个用于替换 MySQL 的产品 Redis, 但是没有想到他把 Redis 开源后大受欢迎,短短几年, Redis 就有了很大的用户群体,目前国内外使用的公司有知乎>网、新浪微博、 GitHub 等。  redis 是一个开源的、 遵循 BSD 协议的、 基于内存的而且目前比较流行的键值数据库(key-value database),是一个非关系型数据库,redis 提供将内存通过网络远程共享的一种服务,提供类似功能的还有 memcache,但相比 memcache, redis 还提供了易扩展、高性能、 具备数据持久性等功能。  Redis 在高并发、低延迟环境要求比较高的环境使用量非常广泛, 目前 redis 在 DBEngine 月排行榜 https://db-engines.com/en/ranking 中一直比较靠前,而且一直是键值型存储类的首位。","text":"  Redis 和 Memcached 是非关系型数据库也称为 NoSQL 数据库, MySQL、 Mariadb、 SQLServer、 PostgreSQL、 Oracle 数据库属于关系型数据(RDBMS, Relational Database Management System)。  Redis(Remote Dictionary Server)在 2009 年发布, 开发者 Salvatore Sanfilippo 是意大利开发者, 他本想为自己的公司开发一个用于替换 MySQL 的产品 Redis, 但是没有想到他把 Redis 开源后大受欢迎,短短几年, Redis 就有了很大的用户群体,目前国内外使用的公司有知乎>网、新浪微博、 GitHub 等。  redis 是一个开源的、 遵循 BSD 协议的、 基于内存的而且目前比较流行的键值数据库(key-value database),是一个非关系型数据库,redis 提供将内存通过网络远程共享的一种服务,提供类似功能的还有 memcache,但相比 memcache, redis 还提供了易扩展、高性能、 具备数据持久性等功能。  Redis 在高并发、低延迟环境要求比较高的环境使用量非常广泛, 目前 redis 在 DBEngine 月排行榜 https://db-engines.com/en/ranking 中一直比较靠前,而且一直是键值型存储类的首位。 编译安装  redis在centos版本自带的yum源中版本比较低,一般都需要我们编译安装更高版本的redis。 准备源码包  可以先用下载工具去官网下载好源码包,下载链接为 https://db-engines.com/en/ranking 。  出于稳定性要求,一般来说实际生产中不会选用最新版的redis,避免因为漏洞造成服务器的安全隐患,所以本次演示,我们选用目前redis4的最新版本,redis-4.0.14来安装。 编译安装  tar xf redis-4.0.14..tar.gz  cd redis-4.0.14  编译安装redis的依赖包不多,有make和gcc就够了。1yum install make gcc -y   如果是最小化安装的新系统,忘记安装gcc的话直接去尝试编译安装redis,就会遇到报错:12345678910111213make[3]: Entering directory '/root/redis-4.0.14/deps/hiredis'gcc -std=c99 -pedantic -c -O3 -fPIC -Wall -W -Wstrict-prototypes -Wwrite-strings -g -ggdb net.cmake[3]: gcc: Command not foundmake[3]: *** [Makefile:156: net.o] Error 127make[3]: Leaving directory '/root/redis-4.0.14/deps/hiredis'make[2]: *** [Makefile:46: hiredis] Error 2make[2]: Leaving directory '/root/redis-4.0.14/deps'make[1]: [Makefile:180: persist-settings] Error 2 (ignored) CC adlist.o/bin/sh: cc: command not foundmake[1]: *** [Makefile:228: adlist.o] Error 127make[1]: Leaving directory '/root/redis-4.0.14/src'make: *** [Makefile:9: install] Error 2   提示没装gcc,再回头补上,yum install gcc -y,也还是会报错的。123456789101112[root@CentOS8 redis-4.0.14]#make PREFIX=/apps/redis installcd src && make installmake[1]: Entering directory '/root/redis-4.0.14/src' CC adlist.oIn file included from adlist.c:34:zmalloc.h:50:10: fatal error: jemalloc/jemalloc.h: No such file or directory #include <jemalloc/jemalloc.h> ^~~~~~~~~~~~~~~~~~~~~compilation terminated.make[1]: *** [Makefile:228: adlist.o] Error 1make[1]: Leaving directory '/root/redis-4.0.14/src'make: *** [Makefile:9: install] Error 2   这是因为上次的编译失败,有残留的文件,我们需要清理下,make distclean,然后重新编译就可以了,make PREFIX=/apps/redis install。  网上也有说可以通过加选项make MALLOC=libc来解决,不过其实是有一些隐患的。首先我们要知道redis 需要使用内存分配器的, 默认是指定内存分配器为 jemalloc ,make MALLOC=libc 就是指定内存分配器为 libc ,而jemalloc 内存分配器在实践中处理内存碎片是要比libc 好的,而且在README.md 文档也说明到了,jemalloc内存分配器也是包含在源码包里面的,可以在deps 目录下看到 jemalloc 目录。  编译完成后,接下来我们还需要几步操作。  创建目录结构mkdir /apps/redis/{etc,logs,data,run}  从源码包中复制配置文件cp redis.conf /apps/redis/etc/  创建systemctl服务启动脚本,为了避免安全隐患,我们还要将redis服务设为以redis身份启动,所以还要修改目录权限12345678910111213141516171819cat > /usr/lib/systemd/system/redis.service <<\"END\"[Unit]Description=Redis persistent key-value databaseAfter=network.targetAfter=network-online.targetWants=network-online.target[Service]#ExecStart=/usr/bin/redis-server /etc/redis.conf --supervised systemdExecStart=/apps/redis/bin/redis-server /apps/redis/etc/redis.conf --supervised systemdExecReload=/bin/kill -s HUP $MAINPIDExecStop=/bin/kill -s QUIT $MAINPIDType=notifyUser=redisGroup=redisRuntimeDirectory=redisRuntimeDirectoryMode=0755[Install]WantedBy=multi-user.targetEND   创建redis用户,并修改redis目录属主属组。12groupadd -g 379 redis && useradd -u 379 -g 379 redis -s /sbin/nologinchown redis.redis -R /apps/redis   修改PATH变量或者直接用软链接的方式(用一个即可,效果一样),方便执行redis命令。  ln -sv /apps/redis/bin/* /usr/local/bin/(软链接)  echo "PATH=/apps/redis/bin:$PATH" > /etc/profile.d/redis.sh;. /etc/profile.d/redis.sh(改PATH变量) 修改内核参数  这时redis服务就其实已经搭建完成了,已经可以通过服务脚本进行启动了。可是如果直接启动会有一些[warning]信息,而且很有可能在高并发的时候导致redis崩溃。123413636:M 13 Nov 10:37:06.195 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.13636:M 13 Nov 10:37:06.195 # Server initialized13636:M 13 Nov 10:37:06.195 # WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect.13636:M 13 Nov 10:37:06.195 # WARNING you have Transparent Huge Pages (THP) support enabled in your kernel. This will create latency and memory usage issues with Redis. To fix this issue run the command 'echo never > /sys/kernel/mm/transparent_hugepage/enabled' as root, and add it to your /etc/rc.local in order to retain the setting after a reboot. Redis must be restarted after THP is disabled.   我们可以通过修改一些内核参数来避免这一风险。 TCP backlog 参数控制的是三次握手的时候 server 端收到 client ack 确认号之后的队列值,redis默认为511。我们需要将内核的net.core.somaxconn值(系统默认128)更改为大于511,或将redis的队列之修改小于128. 将vm.overcommit_memory值改为10、表示内核将检查是否有足够的可用内存供应用进程使用;如果有足够的可用内存,内存申请允许;否则,内存申请失败,并把错误返回给应用进程。1、表示内核允许分配所有的物理内存,而不管当前的内存状态如何。2、表示内核允许分配超过所有物理内存和交换空间总和的内存 transparent hugepage:开启大页内存动态分配,需要关闭让 redis 负责内存管理。   所以,我们执行下面操作1234cat >> /etc/sysctl.conf << ENDnet.core.somaxconn = 512vm.overcommit_memory = 1END 1echo never > /sys/kernel/mm/transparent_hugepage/enabled   至此,redis就正式调试完成,可以正常使用了。 附:一键编译安装redis脚本123456789101112131415161718192021222324252627282930313233343536373839404142434445464748#!/bin/bashDST=\"/apps\"#[ -d $DST ] || { echo \"install into $DST fail,No such file or directory\" ;exit 10;}[ -a redis-*.tar.* ] || { echo ' the absence of redis.tar.gz' ;exit 1;}[ -a /usr/lib/systemd/system/redis.service ] && { echo 'redis.service is exist,redis is aready installed' ;exit 2;}[ -d $DST/redis* ] && { echo \"redis is aready installed into $DST/redis\" ;exit 3;}id redis &>/dev/null && { echo 'user redis is exist,redis is aready installed' ; exit 4;} || { groupadd -g 379 redis && useradd -u 379 -g 379 redis -s /sbin/nologin || { echo user 379 is exist ;exit 5; } }mkdir -p $DST/redisyum install -y make gcc#yum install -y make gcc wget#wget http://download.redis.io/releases/redis-4.0.14.tar.gztar xf redis-*.tar.*PTH=`find . -name \"redis*\" -type d |head -n1`cd $PTHmake PREFIX=$DST/redis installmkdir $DST/redis/{etc,logs,data,run}cp redis.conf /apps/redis/etc/chown redis.redis -R /apps/rediscat > /usr/lib/systemd/system/redis.service << END[Unit]Description=Redis persistent key-value databaseAfter=network.targetAfter=network-online.targetWants=network-online.target[Service]#ExecStart=/usr/bin/redis-server /etc/redis.conf --supervised systemdExecStart=$DST/redis/bin/redis-server $DST/redis/etc/redis.conf --supervised systemdExecReload=/bin/kill -s HUP \\$MAINPIDExecStop=/bin/kill -s QUIT \\$MAINPIDType=notifyUser=redisGroup=redisRuntimeDirectory=redisRuntimeDirectoryMode=0755[Install]WantedBy=multi-user.targetENDln -sv $DST/redis/bin/* /usr/local/bin/cat >> /etc/sysctl.conf << ENDnet.core.somaxconn = 512vm.overcommit_memory = 1ENDecho never > /sys/kernel/mm/transparent_hugepage/enabledsystemctl enable --now redis","categories":[{"name":"NotOnlySQL","slug":"NotOnlySQL","permalink":"https://wudihechao.github.io/categories/NotOnlySQL/"}],"tags":[{"name":"企业级应用","slug":"企业级应用","permalink":"https://wudihechao.github.io/tags/企业级应用/"},{"name":"一键安装","slug":"一键安装","permalink":"https://wudihechao.github.io/tags/一键安装/"},{"name":"redis","slug":"redis","permalink":"https://wudihechao.github.io/tags/redis/"},{"name":"源码编译","slug":"源码编译","permalink":"https://wudihechao.github.io/tags/源码编译/"},{"name":"内核参数","slug":"内核参数","permalink":"https://wudihechao.github.io/tags/内核参数/"}],"keywords":[{"name":"NotOnlySQL","slug":"NotOnlySQL","permalink":"https://wudihechao.github.io/categories/NotOnlySQL/"}]},{"title":"故障:/etc/fstab中NFS自动挂载失败","slug":"故障-etc-fstab中NFS自动挂载失败","date":"2019-11-11T11:17:33.000Z","updated":"2019-12-09T02:46:45.608Z","comments":true,"path":"/blog/219b1607.html","link":"","permalink":"https://wudihechao.github.io/blog/219b1607.html","excerpt":"  之前配置了一个web集群(其实就3个服务器),想实现数据共享和动静分离,感觉配置分布式存储比较复杂,也没必要,就打算在搭建一个NFS服务器,共享几个目录,挂载在几个web服务器上。  搭建NFS服务过程很顺利,手动挂载也没问题,然后打算将挂载信息写进/etc/fstab配置文件,实现开机自动挂载。","text":"  之前配置了一个web集群(其实就3个服务器),想实现数据共享和动静分离,感觉配置分布式存储比较复杂,也没必要,就打算在搭建一个NFS服务器,共享几个目录,挂载在几个web服务器上。  搭建NFS服务过程很顺利,手动挂载也没问题,然后打算将挂载信息写进/etc/fstab配置文件,实现开机自动挂载。12345678910111213141516## /etc/fstab# Created by anaconda on Wed Oct 9 08:35:16 2019## Accessible filesystems, by reference, are maintained under '/dev/disk/'.# See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info.## After editing this file, run 'systemctl daemon-reload' to update systemd# units generated from this file.#/dev/mapper/cl-root / xfs defaults 0 0UUID=100a90e3-01ac-43aa-b6da-a10a6c105282 /boot ext4 defaults 1 2/dev/mapper/cl-data /data xfs defaults 0 0/dev/mapper/swap-swap swap swap defaults 0 0/dev/sr0 /cdrom iso9660 defaults 0 0192.168.32.85:/data/nfsshare /apps/nginx/html/wordpress/wp-content/uploads nfs defaults,_netdev 0 0   以为一切万事大吉之后,重启了机子测试下,df命令一看,发现竟然没有显示,用mount -l命令看了下,也没有挂载信息——竟然没有自动挂载。  开始以为配置文件格式写错了,结果尝试一下mount -a发现一下就挂载成功了。  这就很奇怪了,说明配置文件格式也没问题。  NFS服务器也没任何报错,应该跟NFS服务器没有关系,只是出于某种原因不能自动挂载而已。  可是本地光盘确实可以开机正常挂载的,于是我怀疑是参数写的有问题,不过仔细确认了下,之前参数就是这样的也是可以成功挂载的,抑或是CentOS8有些新特性和改动。上面确实提示After editing this file, run 'systemctl daemon-reload' to update systemd units generated from this file,我也尝试了,没有效果,不过既然其他挂载项都没问题,说明问题就在NFS这一行。  于是去查看下系统日志,发现了问题的端倪1234567891011[root@CentOS8 ~]#tail /var/log/boot.logSee 'systemctl status \"apps-nginx-html-wordpress-wp\\\\x2dcontent-uploads.mount\"' for details.[DEPEND] Dependency failed for Remote File Systems. Starting Permit User Sessions... Starting The nginx HTTP and reverse proxy server... Starting Crash recovery kernel arming...[ OK ] Started OpenSSH server daemon.[ OK ] Started Permit User Sessions. Starting Terminate Plymouth Boot Screen... Starting Hold until boot process finishes up...[ OK ] Started Command Scheduler.   显示远程服务系统也就是NFS服务挂载失败,查看详细信息。12345678910111213[root@CentOS8 ~]#systemctl status \"apps-nginx-html-wordpress-wp\\\\x2dcontent-uploads.mount\"● apps-nginx-html-wordpress-wp\\x2dcontent-uploads.mount - /apps/nginx/html/wordpress/wp-content/uploads Loaded: loaded (/etc/fstab; generated) Active: failed (Result: resources) Where: /apps/nginx/html/wordpress/wp-content/uploads What: 192.168.32.85:/data/nfsshare Docs: man:fstab(5) man:systemd-fstab-generator(8)Nov 11 16:02:39 CentOS8 systemd[1]: apps-nginx-html-wordpress-wp\\x2dcontent-uploads.mount: Mount path /apps/nginx/html/wordpress/wp-content/uploads is not canonical (contains a symlink).Nov 11 16:02:39 CentOS8 systemd[1]: apps-nginx-html-wordpress-wp\\x2dcontent-uploads.mount: Failed to run 'mount' task: Too many levels of symbolic linksNov 11 16:02:39 CentOS8 systemd[1]: apps-nginx-html-wordpress-wp\\x2dcontent-uploads.mount: Failed with result 'resources'.Nov 11 16:02:39 CentOS8 systemd[1]: Failed to mount /apps/nginx/html/wordpress/wp-content/uploads.   说挂载失败,挂载路径不符合规范is not canonical (contains a symlink).。路径写法应该没问题的,难道是因为Too many levels of symbolic links,层级太多了吗?我记得之前挂载点目录层级结构,比这还长都可以挂载成功的,难道又要归结为CentOS8的新特性了么。  一通百度、google之后,竟然没有人和我遇到的问题一样,挂载点的最大层级数是多大也没人提到过,官方文档也没有查到。  正当我打算将层数改小一些再尝试下的时候,突然看到一篇博客提到Too many levels of symbolic links,不过是在软链接中的报错。我突然想起来,当时我安装完系统后,规划的是讲文档和应用都装在/data目录下,可是我实际操作中比较习惯使用/apps/目录来找各种应用,于是之前编译安装nginx及php之前,我创建了一个软链接ln -s /data/apps /apps,以便/apps下的数据跟系统分区隔离开,在一个单独分区。或许这个is not canonical (contains a symlink).中的symlink说的就是指软链接。  将配置文件修改为192.168.32.85:/data/nfsshare /data/apps/nginx/html/wordpress/wp-content/uploads nfs defaults,_netdev 0 0后,再次重启。df命令查看12345678910111213[root@CentOS8 ~]#dfFilesystem 1K-blocks Used Available Use% Mounted ondevtmpfs 393080 0 393080 0% /devtmpfs 408620 0 408620 0% /dev/shmtmpfs 408620 5816 402804 2% /runtmpfs 408620 0 408620 0% /sys/fs/cgroup/dev/mapper/cl-root 52399108 2700076 49699032 6% //dev/sr0 6967726 6967726 0 100% /cdrom/dev/nvme0n1p1 8191416 163348 7592256 3% /boot/dev/mapper/cl-data 41926656 585412 41341244 2% /data192.168.32.85:/data/nfsshare 41926656 535040 41391616 2% /data/apps/nginx/html/wordpress/wp-content/uploadstmpfs 81724 0 81724 0% /run/user/0[root@CentOS8 ~]#   挂载成功~问题解决!原来纯属自己挖的坑!  记录一下,也算,增长点见识,吸取些教训。","categories":[{"name":"故障记录","slug":"故障记录","permalink":"https://wudihechao.github.io/categories/故障记录/"}],"tags":[{"name":"故障","slug":"故障","permalink":"https://wudihechao.github.io/tags/故障/"},{"name":"排错","slug":"排错","permalink":"https://wudihechao.github.io/tags/排错/"},{"name":"记录","slug":"记录","permalink":"https://wudihechao.github.io/tags/记录/"},{"name":"NFS","slug":"NFS","permalink":"https://wudihechao.github.io/tags/NFS/"},{"name":"挂载","slug":"挂载","permalink":"https://wudihechao.github.io/tags/挂载/"},{"name":"软链接","slug":"软链接","permalink":"https://wudihechao.github.io/tags/软链接/"}],"keywords":[{"name":"故障记录","slug":"故障记录","permalink":"https://wudihechao.github.io/categories/故障记录/"}]},{"title":"企业级应用——负载均衡层haproxy(二)","slug":"企业级应用——负载均衡层haproxy(二)","date":"2019-11-08T07:09:10.000Z","updated":"2019-12-25T13:50:54.767Z","comments":true,"path":"/blog/f5daec50.html","link":"","permalink":"https://wudihechao.github.io/blog/f5daec50.html","excerpt":"  继续讲解HAProxy的一些进阶配置及用法,包括报文修改,日志配置,压缩功能,后端服务器状态监测及ACL等功能应用。配置环境及配置文件均延续上一篇《企业级应用:负载均衡层——haproxy(一)》,有任何疑问可以先看上一篇博客。","text":"  继续讲解HAProxy的一些进阶配置及用法,包括报文修改,日志配置,压缩功能,后端服务器状态监测及ACL等功能应用。配置环境及配置文件均延续上一篇《企业级应用:负载均衡层——haproxy(一)》,有任何疑问可以先看上一篇博客。 haproxy的进阶配置haproxy报文修改  在http模式下,基于实际需求修改客户端的请求报文与响应报文,通过reqadd和reqdel在请求报文添加删除字段,通过rspadd与rspidel在响应报文中添加与删除字段。123456789101112131415在请求报文尾部添加指定首部 reqadd &ltstring&gt [{if | unless} &ltcond&gt]从请求报文中删除匹配正则表达式的首部 reqdel &ltsearch&gt [{if | unless} &ltcond&gt] reqidel &ltsearch&gt [{if | unless} &ltcond&gt]在响应报文尾部添加指定首部 rspadd &ltstring&gt [{if | unless} &ltcond&gt]示例: rspadd X-Via:\\ HAPorxy从响应报文中删除匹配正则表达式的首部 rspdel &ltsearch&gt [{if | unless} &ltcond&gt] rspidel &ltsearch&gt [{if | unless} &ltcond&gt]示例: rspidel server.* #从相应报文删除server信息 rspidel X-Powered-By:.* #从响应报文删除X-Powered-By信息 HAProxy日志HAProxy日志配置  需在HAProxy和Rsyslog中分别配置。 HAProxy配置1234567在global配置项定义:log 127.0.0.1 local{1-7} info #基于syslog记录日志到指定设备,级别有(err、warning、info、debug)listen web_portbind 127.0.0.1:80mode httplog globalserver web1 127.0.0.1:8080 check inter 3000 fall 2 rise 5   然后重启HAProxysystemctl restart haproxy Rsyslog配置   编辑配置文件vim /etc/rsyslog.conf123$ModLoad imudp$UDPServerRun 514local3.* /var/log/haproxy.log   然后重启Rsyslogsystemctl restart rsyslog HAProxy日志格式  将特定信息记录在日志中  配置选项:  capture cookie &ltname&gt len &ltlength&gt #捕获请求和响应报文中的 cookie并记录日志  capture request header &ltname&gt len &ltlength&gt #捕获请求报文中指定的首部内容和长度并记录日志  capture response header &ltname&gt len &ltlength&gt #捕获响应报文中指定的内容和长度首部并记录日志  示例:123capture request header Host len 256capture request header User-Agent len 512capture request header Referer len 15 配置示例1234567891011listen web_host bind 172.18.32.249:80 mode http balance roundrobin log global option httplog #日志格式选项 capture request header X-Forwarded-For len 15 capture request header User-Agent len 512 cookie SERVER-COOKIE insert indirect nocache server web1 192.168.32.81:80 cookie web1 check inter 3000 fall 3 rise 5 server web2 192.168.32.82:80 cookie web2 check inter 3000 fall 3 rise 5 压缩功能  启用功能可以对响应给客户端的报文进行压缩,以节省网络带宽,但是会占用部分CPU性能。 配置选项123456compression algo #启用http协议中的压缩机制,常用算法有gzip deflate identity #调试使用的压缩方式 gzip #常用的压缩方式,与各浏览器兼容较好 deflate #有些浏览器不支持 raw-deflate #新出的压缩方式compression type #要压缩的文件类型 配置示例12345678910111213listen web_host bind 172.18.32.249:80 mode http balance roundrobin log global option httplog# capture request header X-Forwarded-For len 15# capture request header User-Agent len 512 compression algo gzip compression type compression type text/plain text/html text/css text/xml text/javascript application/javascriptcookie SERVER-COOKIE insert indirect nocacheserver web1 192.168.32.81:80 cookie web1 check inter 3000 fall 3 rise 5server web2 192.168.32.82:80 cookie web2 check inter 3000 fall 3 rise 5 后端服务器状态监测  haproxy能对后端服务器状态进行检测,如果发现后端服务器异常,可以自动将该服务器下线,实现高可用。  haproxy对后端服务器有三种检测方式: 基于四层的传输端口做状态监测 基于指定URI 做状态监测 基于指定URI的request请求头部内容做状态监测1234option httpchkoption httpchk &lturi&gtoption httpchk &ltmethod&gt &lturi&gtoption httpchk &ltmethod&gt &lturi&gt &ltversion&gt   之前我们的配置都是基于传输IP加端口对检测,所以status状态页的后端检测状态里显示的是L4,基于指定URI做状态监测,需要持续从服务器get指定页面,会占用消耗一些带宽资源,所以基于指定URI的request请求头部内容做状态监测最为合理,配置如下:1234567891011listen web_host bind 172.18.32.249:80 mode http balance roundrobin log global option httplog# option httpchk GET /app/monitor/check.html HTTP/1.0 option httpchk HEAD /app/monitor/check.html HTTP/1.0\\r\\nHost:\\ 192.168.7.102 cookie SERVER-COOKIE insert indirect nocache server web1 192.168.32.81:80 cookie web1 check inter 3000 fall 3 rise 5 server web2 192.168.32.82:80 cookie web2 check inter 3000 fall 3 rise 5   这时再去看9999端口的status页,就会看到后端服务器的检测状态为L7OK了 HAProxy的ACL功能  访问控制列表(ACL,Access Control Lists)是一种基于包过滤的访问控制技术,它可以根据设定的条件对经过服务器传输的数据包进行过滤(条件匹配),即对接收到的报文进行匹配和过滤,基于请求报文头部中的源地址、源端口、目标地址、目标端口、请求方法、URL、文件后缀等信息内容进行匹配并执行进一步操作,允许其通过或丢弃。 ACL配置选项:12acl &ltaclname&gt &ltcriterion&gt [flags] [operator] [&ltvalue&gt]acl 名称 匹配规范 匹配模式 具体操作符 操作对象类型 ACL-Name  实例:acl image_service hdr_dom(host) -i img.example.com  ACL名称,可以使用大字母A-Z、小写字母a-z、数字0-9、冒号:、点.、中横线和下划线,并且严格区分大小写,比如Image_site和image_site完全是两个acl。 ACL-criterion  定义ACL匹配规范123456789101112hdr([&ltname&gt [,&ltocc&gt]]):完全匹配字符串hdr_beg([&ltname&gt [,&ltocc&gt]]):前缀匹配hdr_dir([&ltname&gt [,&ltocc&gt]]):路径匹配hdr_dom([&ltname&gt [,&ltocc&gt]]):域匹配hdr_end([&ltname&gt [,&ltocc&gt]]):后缀匹配hdr_len([&ltname&gt [,&ltocc&gt]]):长度匹配hdr_reg([&ltname&gt [,&ltocc&gt]]):正则表达式匹配hdr_sub([&ltname&gt [,&ltocc&gt]]):子串匹配dst 目标IPdst_port 目标PORTsrc 源IPsrc_port 源PORT   示例:123456hdr &ltstring&gt用于测试请求头部首部指定内容hdr_dom(host) 请求的host名称,如 www.example.comhdr_beg(host) 请求的host开头,如 www. img. video. download. ftp.hdr_end(host) 请求的host结尾,如 .com .net .cnpath_beg 请求的URL开头,如/static、/images、/img、/csspath_end 请求的URL中资源的结尾,如 .gif .png .css .js .jpg .jpeg ACL-flags  ACL匹配模式    -i 不区分大小写    -m 使用指定的pattern匹配方法    -n 不做DNS解析    -u 禁止acl重名,否则多个同名ACL匹配或关系 ACL-operator  ACL 操作符12345678整数比较:eq、ge、gt、le、lt字符比较: exact match (-m str) :字符串必须完全匹配模式 substring match (-m sub) :在提取的字符串中查找模式,如果其中任何一个被发现,ACL将匹配 prefix match (-m beg) :在提取的字符串首部中查找模式,如果其中任何一个被发现,ACL将匹配 suffix match (-m end) :将模式与提取字符串的尾部进行比较,如果其中任何一个匹配,则ACL进行匹配 subdir match (-m dir) :查看提取出来的用斜线分隔(“/”)的字符串,如果其中任何一个匹配,则ACL进行匹配 domain match (-m dom) :查找提取的用点(“.”)分隔字符串,如果其中任何一个匹配,则ACL进行匹配 ACL-value  value的类型12345678910111213The ACL engine can match these types against patterns of the following types : Boolean #布尔值 integer or integer range #整数或整数范围,比如用于匹配端口范围 IP address / network #IP地址或IP范围, 192.168.0.1 ,192.168.0.1/24 string exact –精确比较 substring—子串 www.example.com suffix-后缀比较 prefix-前缀比较 subdir-路径, /wp-includes/js/jquery/jquery.js domain-域名,www.example.com regular expression #正则表达式 hex block #16进制 ACL调用方式  ACL调用方式: 与:隐式(默认)使用 或:使用“or” 或 “||”表示 否定:使用“!“ 表示   示例:123if valid_src valid_port #与关系if invalid_src || invalid_port #或if ! invalid_src #非 ACL具体示例域名匹配:123456789101112131415listen web_host bind 172.18.32.249:80 mode http balance roundrobin log global option httplog acl web_host hdr_dom(host) www.example.net use_backend example_host if web_host default_backend default_webbackend example_host mode http server web1 192.168.32.81:80 check inter 2000 fall 3 rise 5 backend default_web mode http server web2 192.168.32.82:80 check inter 2000 fall 3 rise 5 匹配浏览器类型:  匹配客户端浏览器,将不同类型的浏览器调动至不同的服务器组1234567891011121314151617listen web_host bind 172.18.32.249:80 mode http balance roundrobin log global option httplog acl web_host hdr_dom(host) www.example.net use_backend example_host if web_host acl redirect_test hdr(User-Agent) -m sub -i \"Mozilla/5.0 (Windows NT 6.1; WOW64;Trident/7.0; rv:11.0) like Gecko\" redirect prefix http://192.168.7.103 if redirect_test default_backend default_webbackend example_host mode http server web1 192.168.32.81:80 check inter 2000 fall 3 rise 5backend default_web mode http server web2 192.168.32.82:80 check inter 2000 fall 3 rise 5 基于文件后缀名实现动静分离:1234567891011121314151617181920listen web_host bind 172.18.32.249:80 mode http balance roundrobin log global option httplog acl php_server path_end -i .php use_backend php_server_host if php_server acl image_server path_end -i .jpg .png .jpeg .gif use_backend image_server_host if image_server default_backend default_webbackend php_server_host mode http server web1 192.168.32.81 check inter 2000 fall 3 rise 5backend image_server_host mode http server web1 192.168.32.82 check inter 2000 fall 3 rise 5backend default_web mode http server web1 192.168.32.8:80 check inter 2000 fall 3 rise 5 匹配访问路径实现动静分离:123456789101112131415listen web_host bind 172.18.32.249:80 mode http balance roundrobin log global option httplog acl static_path path_beg -i /static /images /javascript use_backend static_path_host if static_path default_backend default_webbackend static_path_host mode http server web1 192.168.32.81 check inter 2000 fall 3 rise 5backend default_web mode http server web1 192.168.32.8:80 check inter 2000 fall 3 rise 5 预定义ACL预定义ACL: ACLname Equivalent to Usage FALSE always_false never match HTTP req_proto_http match if protocol is valid HTTP HTTP_1.0 req_ver 1.0 match HTTP version 1.0 HTTP_1.1 req_ver 1.1 match HTTP version 1.1 HTTP_CONTENT hdr_val(content-length) gt 0 match an existing content-length HTTP_URL_ABS url_reg ^[^/:]*: // match absolute URL with scheme HTTP_URL_SLASH url_beg / match URL beginning with “/“ HTTP_URL_STAR url * match URL equal to “*” LOCALHOST src 127.0.0.1/8 match connection from local host METH_CONNECT method CONNECT match HTTP CONNECT method METH_DELETE method DELETE match HTTP DELETE method METH_GET method GET HEAD match HTTP GET or HEAD method METH_HEAD method HEAD match HTTP HEAD method METH_OPTIONS method OPTIONS match HTTP OPTIONS method METH_POST method POST match HTTP POST method METH_PUT method PUT match HTTP PUT method METH_TRACE method TRACE match HTTP TRACE method RDP_COOKIE req_rdp_cookie_cnt gt 0 match presence of an RDP cookie REQ_CONTENT req_len gt 0 match data in the request buffer TRUE always_true always match WAIT_END wait_end wait for end of content analysis   预定义ACL使用示例123456789101112131415161718listen web_host bind 172.18.32.249:80 mode http balance roundrobin log global option httplog acl static_path path_beg -i /static /images /javascript use_backend static_path_host if HTTP_1.1 TRUE static_path default_backend default_webbackend php_server_host mode http server web1 192.168.32.81 check inter 2000 fall 3 rise 5backend static_path_host mode http server web1 192.168.32.82 check inter 2000 fall 3 rise 5backend default_web mode http server web1 192.168.32.8:80 check inter 2000 fall 3 rise 5   详细信息查看官网http://cbonte.github.io/haproxy-dconv/2.0/configuration.html#7.4","categories":[{"name":"linux进阶","slug":"linux进阶","permalink":"https://wudihechao.github.io/categories/linux进阶/"}],"tags":[{"name":"企业级应用","slug":"企业级应用","permalink":"https://wudihechao.github.io/tags/企业级应用/"},{"name":"高可用","slug":"高可用","permalink":"https://wudihechao.github.io/tags/高可用/"},{"name":"HAProxy","slug":"HAProxy","permalink":"https://wudihechao.github.io/tags/HAProxy/"},{"name":"负载均衡","slug":"负载均衡","permalink":"https://wudihechao.github.io/tags/负载均衡/"},{"name":"调度算法","slug":"调度算法","permalink":"https://wudihechao.github.io/tags/调度算法/"}],"keywords":[{"name":"linux进阶","slug":"linux进阶","permalink":"https://wudihechao.github.io/categories/linux进阶/"}]},{"title":"企业级应用——负载均衡层haproxy(一)","slug":"企业级应用——负载均衡层haproxy(一)","date":"2019-11-04T09:22:26.000Z","updated":"2019-12-25T13:50:17.154Z","comments":true,"path":"/blog/5aeb7732.html","link":"","permalink":"https://wudihechao.github.io/blog/5aeb7732.html","excerpt":"  HAProxy是法国开发者 威利塔罗(Willy Tarreau) 在2000年使用C语言开发的一个开源软件,是一款具备高并发(一万以上)、高性能的TCP>和HTTP负载均衡器,支持基于cookie的持久性,自动故障切换,支持正则表达式及web状态统计。  HAProxy特别适用于那些负载特大的web站点,这些站点通常又需要会话保持或七层处理。HAProxy运行在当前的硬件上,完全可以支持数以万计的并发连接。并且它的运行模式使得它可以很简单安全的整合进您当前的架构中, 同时可以保护你的web服务器不被暴露到网络上。  包括 GitHub、Bitbucket、Stack Overflow、Reddit、Tumblr、Twitter和 Tuenti[7]在内的知名网站,及亚马逊网络服务系统都使用了HAProxy。","text":"  HAProxy是法国开发者 威利塔罗(Willy Tarreau) 在2000年使用C语言开发的一个开源软件,是一款具备高并发(一万以上)、高性能的TCP>和HTTP负载均衡器,支持基于cookie的持久性,自动故障切换,支持正则表达式及web状态统计。  HAProxy特别适用于那些负载特大的web站点,这些站点通常又需要会话保持或七层处理。HAProxy运行在当前的硬件上,完全可以支持数以万计的并发连接。并且它的运行模式使得它可以很简单安全的整合进您当前的架构中, 同时可以保护你的web服务器不被暴露到网络上。  包括 GitHub、Bitbucket、Stack Overflow、Reddit、Tumblr、Twitter和 Tuenti[7]在内的知名网站,及亚马逊网络服务系统都使用了HAProxy。 HAProxy功能HAProxy功能: TCP和HTTP反向代理 SSL/TSL服务器 可以针对HTTP请求添加cookie,进行路由后端服务器 可平衡负载至后端服务器,并支持持久连接 支持所有主服务器故障切换至备用服务器 支持专用端口实现监控服务 支持不影响现有连接情况下停止接受新连接请求 可以在双向添加,修改或删除HTTP报文首部 响应报文压缩 支持基于pattern实现连接请求的访问控制 通过特定的URI为授权用户提供详细的状态信息 不具备的功能: 正向代理–squid,nginx 缓存代理–varnish web服务–nginx、tengine、apache、php、tomcat UDP–目前不支持UDP协议,2.1版本会支持UDP协议代理 单机性能–LVS HAProxy安装  HAProxy 支持基于lua实现功能扩展,lua是一种小巧的脚本语言,于1993年由巴西里约热内卢天主教大学(Pontifical Catholic University of Rio de Janeiro)里的一个研究小组开发,其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。  由于centos自带的lua版本比较低并不符合HAProxy要求的lua最低版本(5.3)的要求,因此需要编译安装较新版本的lua环境,然后才能编译安装HAProxy。 安装lua环境  配置lua环境1yum install libtermcap-devel ncurses-devel libevent-devel readline-devel gcc make   下载lua源码tar包1wget http://www.lua.org/ftp/lua-5.3.5.tar.gz   编译安装lua123tar xvf lua-5.3.5.tar.gzcd lua-5.3.5make linux test 安装HAProxy  下载haproxy源码包1wget http://www.haproxy.org/download/2.0/src/haproxy-2.0.8.tar.gz   安装依赖包1yum install gcc gcc-c++ glibc glibc-devel pcre pcre-devel openssl openssl-devel systemd-devel   (附加工具包net-tools vim iotop bc zip unzip zlib-devel lrzsz tree screen lsof tcpdump wget ntpdate)  编译安装haproxy  cd haproxy-2.0.8  HAProxy 1.8及1.9版本编译参数:12345678make ARCH=x86_64 \\TARGET=linux2628 \\USE_PCRE=1 \\USE_OPENSSL=1 \\USE_ZLIB=1 \\USE_SYSTEMD=1 \\USE_CPU_AFFINITY=1 \\PREFIX=/apps/haproxy   HAProxy 2.0编译参数:12345678910make ARCH=x86_64 \\TARGET=linux-glibc USE_PCRE=1 \\USE_OPENSSL=1 \\USE_ZLIB=1 \\USE_SYSTEMD=1 \\USE_CPU_AFFINITY=1 \\USE_LUA=1 \\LUA_INC=/data/tar/lua-5.3.5/src/ \\LUA_LIB=/data/tar/lua-5.3.5/src/ \\PREFIX=/apps/haproxy 12make install PREFIX=/apps/haproxycp haproxy /usr/sbin/   haproxy启动脚本  vim /usr/lib/systemd/system/haproxy.service123456789[Unit]Description=HAProxy Load BalancerAfter=syslog.target network.target[Service]ExecStartPre=/usr/sbin/haproxy -f /etc/haproxy/haproxy.cfg -c -qExecStart=/usr/sbin/haproxy -Ws -f /etc/haproxy/haproxy.cfg -p /var/lib/haproxy/haproxy.pidExecReload=/bin/kill -USR2 $MAINPID[Install]WantedBy=multi-user.target\\   haproxy配置文件(基本配置文件)  mkdir /etc/haproxy  vim /etc/haproxy/haproxy.cfg12345678910111213141516171819202122232425262728293031323334353637383940global chroot /apps/haproxy #锁定运行目录 stats socket /var/lib/haproxy/haproxy.sock mode 600 level admin #socket文件 uid 99 #运行haproxy的用户身份,也可设user,group gid 99 daemon #以守护进程运行# nbproc 4 #指定每个haproxy进程开启的线程数,默认为每个进程一个线程# cpu-map 1 0 #绑定haproxy 进程至指定CPU# cpu-map 2 1# cpu-map 3 2# cpu-map 4 3maxconn 100000 #每个haproxy进程的最大并发连接数#maxsslconn #每个haproxy进程ssl最大连接数,用于haproxy配置了证书的场景下#spread-checks #后端server状态check随机提前或延迟百分比时间,建议2-5(20%-50%)之间pidfile /var/lib/haproxy/haproxy.pidlog 127.0.0.1 local3 info #定义全局的syslog服务器;最多可以定义两个defaults option http-keep-alive option forwardfor maxconn 100000 mode http timeout connect 300000ms timeout client 300000ms timeout server 300000mslisten stats mode http bind 0.0.0.0:9999 stats enable log global stats uri /haproxy-status stats auth haadmin:q1w2e3r4yslisten web_port bind 192.168.32.84:80 mode http log global server web1 127.0.0.1:8080 check inter 3000 fall 2 rise 5   设置haproxypid及socket创建权限  useradd -s /sbin/nologin -r -u 99 haproxy  mkdir /var/lib/haproxy  chown 99.99 /var/lib/haproxy/ -R   至此,才完成haproxy的安装与配置,启动并查看haproxy的状态是否正常吧。  systemctl enable --now haproxy  systemctl status haproxy 配置web均衡  haproxy最主要的功能就是为后端服务器做反向代理,例如我们要为后面的四个web服务器做反向代理,配置文件如下:123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960global maxconn 100000 chroot /apps/haproxy stats socket /var/lib/haproxy/haproxy.sock1 mode 600 level admin process 1 stats socket /var/lib/haproxy/haproxy.sock2 mode 600 level admin process 2 uid 99 gid 99 daemon nbproc 2 #指定每个haproxy进程开启的线程数,默认为每个进程一个线程 cpu-map 1 0 #cpu工作线程绑定 cpu-map 2 1# cpu-map 3 2# cpu-map 4 3 pidfile /var/lib/haproxy/haproxy.pid log 127.0.0.1 local3 infodefaults option redispatch #当server Id对应的服务器挂掉后,强制定向到其他健康的服务器 option abortonclose #当服务器负载很高的时候,自动结束掉当前队列处理比较久的链接 option http-keep-alive #开启与客户端的会话保持 option forwardfor #透传客户端真实IP至后端web服务器 mode http #默认工作类型 timeout connect 600s #客户端请求到后端server的最长连接等待时间(TCP之前) timeout server 600s #客户端请求到后端服务端的超时超时时长(TCP之后) timeout client 600s #与客户端的最长非活动时间 timeout http-keep-alive 120s #session会话保持超时时间,范围内会转发到相同的后端服务器 timeout check 50s #对后端服务器的检测超时时间option http-keep-alive#frontend WEB_PORT_80# bind 192.168.32.84:80# mode http# use_backend web_prot_http_nodes#backend web_prot_http_nodes# mode http# option forwardfor# balance static-rr# server web0 192.168.32.8:80 check inter 3000 fall 3 rise 5# server web3 192.168.32.83:80 check inter 3000 fall 3 rise 5# server web2 192.168.32.82:80 check inter 3000 fall 3 rise 5# server web1 192.168.32.81:80 check inter 3000 fall 3 rise 5listen stats bind 0.0.0.0:9999 mode http stats enable log global stats uri /proxy_status #进入后台状态页url路径 stats auth haadmin:hapasswd #进入后台的账号密码listen web_host #使用listen替换frontend+backend的配置方式 bind 192.168.32.84:80 mode http log global balance roundrobin #定义调度算法为roundrobin server web0 192.168.32.8:80 weight 1 check addr 192.168.32.8 port 9000 inter 3000 fall 2 rise 5 server web1 192.168.32.81:80 weight 1 check addr 192.168.32.81 port 9000 inter 3000 fall 2 rise 5 server web2 192.168.32.82:80 weight 1 check addr 192.168.32.82 port 9000 inter 3000 fall 2 rise 5 server web3 192.168.32.83:80 weight 1 check addr 192.168.32.83 port 9000 inter 3000 fall 2 rise 5   重启服务systemctl restart haproxy,便可登陆本机的状态页(haadmin:hapasswd)查看后端服务器状态。   刷新几次可以看到,不同pid也就是不同线程提供的status页面。 配置详解globe 全局配置段123进程及安全配置相关的参数性能调整相关参数Debug参数   全局配置一般大多类似不用多说,须注意的是nbproc若开启多线程,socket设置最好也分开设置,最好每个线程用process #指定固定的socket,方便后期用命令行socat工具管理(echo "disable server web_host/web1" | socat stdio /var/lib/haproxy/haproxy.sock2),例如:12345678910111213globalmaxconn 100000chroot /apps/haproxystats socket /var/lib/haproxy/haproxy.sock1 mode 600 level admin process 1stats socket /var/lib/haproxy/haproxy.sock2 mode 600 level admin process 2uid 99gid 99daemonnbproc 2 #指定每个haproxy进程开启的线程数,默认为每个进程一个线程cpu-map 1 0 #cpu工作线程绑定cpu-map 2 1pidfile /var/lib/haproxy/haproxy.pidlog 127.0.0.1 local3 info   其他详细信息参见官方文档:https://cbonte.github.io/haproxy-dconv/2.0/intro.html proxies:代理配置段1234defaults [&ltname&gt] #默认配置项,针对以下的frontend、backend和lsiten生效,可以多个namefrontend &ltname&gt #前端servername,类似于Nginx的一个虚拟主机 server。backend &ltname&gt #后端服务器组,等于nginx的upstreamlisten &ltname&gt #将frontend和backend合并在一起配置   注:name字段只能使用”-”、”_”、”.”、和”:”,并且严格区分大小写,例如:Web和web是完全不同的两组服务器。 defaultsoption redispatch #当server Id对应的服务器挂掉后,强制定向到其他健康的服务器option abortonclose #当服务器负载很高的时候,自动结束掉当前队列处理比较久的链接option http-keep-alive #开启与客户端的会话保持option forwardfor #透传客户端真实IP至后端web服务器mode http #默认工作类型timeout connect 120s #客户端请求到后端server的最长连接等待时间(TCP之前)timeout server 600s #客户端请求到后端服务端的超时超时时长(TCP之后)timeout client 600s #与客户端的最长非活动时间timeout http-keep-alive 120s #session 会话保持超时时间,范围内会转发到相同的后端服务器timeout check 5s #对后端服务器的检测超时时间 frontendbind:指定HAProxy的监听地址,可以是IPV4或IPV6,可以同时监听多个IP或端口,可同时用于listen字段中 1bind [&ltaddress&gt]:&ltport_range&gt [, ...] [param*] 1234listen http_proxy #监听http的多个IP的多个端口和sock文件 bind :80,:443,:8801-8810 bind 10.0.0.1:10080,10.0.0.1:10443 bind /var/run/ssl-frontend.sock user root mode 600 accept-proxy 123listen http_https_proxy #https监听 bind :80 bind :443 ssl crt /etc/haproxy/site.pem 1234listen http_https_proxy_explicit #监听ipv6、ipv4和unix sock文件 bind ipv6@:80 bind ipv4@public_ssl:443 ssl crt /etc/haproxy/site.pem bind [email protected] user root mode 600 accept-proxy 12listen external_bind_app1 #监听file descriptor bind \"fd@${FD_APP1}\"   企业生产示例:12345frontend WEB_PORT bind :80,:8080 bind 192.168.7.102:10080,:8801-8810,192.168.7.101:9001-9010 mode http/tcp #指定负载协议类型 use_backend backend_name #调用的后端服务器组名称 backend定义一组后端服务器,backend服务器将被frontend进行调用。 123mode http/tcp #指定负载协议类型option #配置选项server #定义后端real server   注意:mode要与frontend一致。option后面加httpchk,smtpchk,mysql-check,pgsql-check,ssl-hello-chk方法,可用于实现更多应用层检测功能。123456789101112check #对指定real进行健康状态检查,默认不开启 addr IP #可指定的健康状态监测IP port num #指定的健康状态监测端口 inter num #健康状态检查间隔时间,默认2000 ms fall num #后端服务器失效检查次数,默认为3 rise num #后端服务器从下线恢复检查次数,默认为2weight #默认为1,最大值为256,0表示不参与负载均衡backup #将后端服务器标记为备份状态disabled #将后端服务器标记为不可用状态redirect prefix http://www.example.net/ #将请求临时重定向至其它URL,只适用于http模式maxconn &ltmaxconn&gt:当前后端server的最大并发连接数backlog &ltbacklog&gt:当server的连接数达到上限后的后援队列长度   frontend+backend配置实例:12345678910#官网业务访问入口======================================frontend WEB_PORT_80 bind 192.168.7.248:80 mode http use_backend web_prot_http_nodesbackend web_prot_http_nodes mode http option forwardfor server 192.168.7.101 192.168.7.101:8080 check inter 3000 fall 3 rise 5 server 192.168.7.102 192.168.7.102:8080 check inter 3000 fall 3 rise 5 listenlisten相当于frontend+backend的结合,即定义前端监听代理,又定义了后端服务器,例如上面的frontend+backend组合可用下面这种listen方式代替: 1234567#官网业务访问入口=====================================listen WEB_PORT_80 bind 192.168.7.102:80 mode http option forwardfor server web1 192.168.7.101:80 check inter 3000 fall 3 rise 5 server web2 192.168.7.101:80 check inter 3000 fall 3 rise 5 haproxy调度算法  HAProxy通过固定参数balance指明对后端服务器的调度算法,该参数可以配置在listen或backend选项中。  HAProxy的调度算法分为静态和动态调度算法,但是有些算法可以根据参数在静态和动态算法中相互转换。 静态算法 static-rr基于权重的轮询调度,不支持权重的运行时调整及后端服务器慢启动,其后端主机数量没有限制 first根据服务器在列表中的位置,自上而下进行调度,但是其只会当第一台服务器的连接数达到上限,新请求才会分配给下一台服务,因此会忽略服务器的权重设置。(生产不常用) 动态算法 roundrobin基于权重的轮询动态调度算法,支持权重的运行时调整,不完全等于lvs中的rr轮训模式,HAProxy中的roundrobin支持慢启动(新加的服务器会逐渐增加转发数),其每个后端backend中最多支持4095个real server,roundrobin为默认调度算法,且支持对real server权重动态调整。 leastconn加权的最少连接的动态,支持权重的运行时调整和慢启动,即当前后端服务器连接最少的优先调度(新客户端连接),比较适合长连接的场景使用,比如MySQL等场景。 其他算法source  源地址hash,基于用户源地址hash并将请求转发到后端服务器,默认为静态即取模方式,但是可以通过hash-type支持的选项更改,后续同一个源地址请求将被转发至同一个后端web服务器,比较适用于session保持/缓存业务等场景。  源地址有两种转发客户端请求到后端服务器的服务器选取计算方式,分别是取模法和一致性hash map-base取模法  map-based:取模法,基于服务器总权重的hash数组取模,该hash是静态的即不支持在线调整权重,不支持慢启动,其对后端服务器调度均衡,缺点是当服务器的总权重发生变化时,即有服务器上线或下线,都会因权重发生变化而导致调度结果整体改变。  所谓取模运算,就是计算两个数相除之后的余数,10%7=3, 7%4=3,(2^32-1)%(1+1+2)  取模法示意图:取模法配置示例: 1234567listen web_host bind 192.168.7.101:80,:8801-8810,192.168.7.101:9001-9010 mode tcp log global balance source server web1 192.168.7.103:80 weight 1 check inter 3000 fall 2 rise 5 server web2 192.168.7.104:80 weight 1 check inter 3000 fall 2 rise 5 一致性hash  一致性哈希,该hash是动态的,支持在线调整权重,支持慢启动,优点在于当服务器的总权重发生变化时,对调度结果影响是局部的,不会引起大的变动,hash(o)mod n 。  Hash对象到后端服务器的映射关系:  一致性hash后端服务器在线与离线的调度方式示意图:  一致性hash配置示例: 12345678listen web_host bind 192.168.7.101:80,:8801-8810,192.168.7.101:9001-9010 mode tcp log global balance source hash-type consistent server web1 192.168.7.103:80 weight 1 check inter 3000 fall 2 rise 5 server web2 192.168.7.104:80 weight 1 check inter 3000 fall 2 rise 5 uri  基于对用户请求的uri做hash并将请求转发到后端指定服务器,也可以通过map-based和consistent定义使用取模法还是一致性hash。  uri 取模法配置示例:1234567listen web_host bind 192.168.7.101:80,:8801-8810,192.168.7.101:9001-9010 mode http log global balance uri server web1 192.168.7.103:80 weight 1 check inter 3000 fall 2 rise 5 server web2 192.168.7.104:80 weight 1 check inter 3000 fall 2 rise 5   uri 一致性hash配置示例:12345678listen web_host bind 192.168.7.101:80,:8801-8810,192.168.7.101:9001-9010 mode http log global balance uri hash-type consistent server web1 192.168.7.103:80 weight 1 check inter 3000 fall 2 rise 5 server web2 192.168.7.104:80 weight 1 check inter 3000 fall 2 rise 5 url_param  url_param对用户请求的url中的 params 部分中的参数name作hash计算,并由服务器总权重相除以后派发至某挑出的服务器;通常用于追踪用户,以确保来自同一个用户的请求始终发往同一个real server1234假设url = http://www.example.com/foo/bar/index.php?k1=v1&k2=v2则:host = \"www.example.com\"url_param = \"k1=v1&k2=v2\"   url_param取模法配置示例:1234567listen web_host bind 192.168.7.101:80,:8801-8810,192.168.7.101:9001-9010 mode http log global balance url_param name,age #支持对单个及多个url_param 值hash server web1 192.168.7.103:80 weight 1 check inter 3000 fall 2 rise 5 server web2 192.168.7.104:80 weight 1 check inter 3000 fall 2 rise 5   url_param一致性hash配置示例:12345678listen web_host bind 192.168.7.101:80,:8801-8810,192.168.7.101:9001-9010 mode http log global balance url_param name,age #支持对单个及多个url_param 值hash hash-type consistent server web1 192.168.7.103:80 weight 1 check inter 3000 fall 2 rise 5 server web2 192.168.7.104:80 weight 1 check inter 3000 fall 2 rise 5 hdr  针对用户每个http头部(header)请求中的指定信息做hash,此处由 name 指定的http首部将会被取出并做hash计算,然后由服务器总权重相除以后派发至某挑出的服务器,假如无有效的值,则会使用默认的轮询调度。  hdr取模法配置示例:1234567listen web_host bind 192.168.7.101:80,:8801-8810,192.168.7.101:9001-9010 mode http log global balance hdr(User-Agent) server web1 192.168.7.103:80 weight 1 check inter 3000 fall 2 rise 5 server web2 192.168.7.104:80 weight 1 check inter 3000 fall 2 rise 5 一致性hash配置示例:12345678listen web_host bind 192.168.7.101:80,:8801-8810,192.168.7.101:9001-9010 mode http log global balance hdr(User-Agent) hash-type consistent server web1 192.168.7.103:80 weight 1 check inter 3000 fall 2 rise 5 server web2 192.168.7.104:80 weight 1 check inter 3000 fall 2 rise 5 rdp-cookie  rdp-cookie对远程桌面的负载,使用cookie保持会话  rdp-cookie取模法配置示例:12345listen RDP bind 192.168.7.101:3389 balance rdp-cookie mode tcp server rdp0 172.18.132.20:3389 check fall 3 rise 5 inter 2000 weight 1   rdp-cookie一致性hash配置示例:123456listen RDP bind 192.168.7.101:3389 balance rdp-cookie hash-type consistent mode tcp server rdp0 172.18.132.20:3389 check fall 3 rise 5 inter 2000 weight 1   基于iptables实现:123net.ipv4.ip_forward = 1# iptables -t nat -A PREROUTING -d 192.168.7.101 -p tcp --dport 3389 -j DNAT --todestination 172.18.139.20:3389# iptables -t nat -A POSTROUTING -s 192.168.0.0/21 -j SNAT --to-source 192.168.7.101 random  在1.9版本开始增加一个叫做random的负载平衡算法,其基于一个随机数作为一致性hash的key,随机负载平衡对于大型服务器场或经常添加或删除服务器非常有用,因为它可以避免在这种情况下由roundrobin或leastconn导致的锤击效应。  random配置实例:1234567listen web_host bind 192.168.7.101:80,:8801-8810,192.168.7.101:9001-9010 mode http log global balance random server web1 192.168.7.103:80 weight 1 check inter 3000 fall 2 rise 5 server web2 192.168.7.104:80 weight 1 check inter 3000 fall 2 rise 5 算法总结12345678910static-rr--------->tcp/http 静态first------------->tcp/http 静态roundrobin-------->tcp/http 动态leastconn--------->tcp/http 动态random------------>tcp/http 动态source------------>tcp/httpUri--------------->httpurl_param--------->http 取决于hash_type是否consistenthdr--------------->httprdp-cookie-------->tcp 12345678910first #使用较少static-rr #做了session共享的web集群roundrobinrandomleastconn #数据库source #基于客户端公网IP的会话保持Uri--------------->http #缓存服务器,CDN服务商,蓝汛、百度、阿里云、腾讯url_param--------->httphdr #基于客户端请求报文头部做下一步处理rdp-cookie #很少使用   详细可参见官方文档:https://cbonte.github.io/haproxy-dconv/2.0/configuration.html#4 haproxy工作模式tcp:四层负载  在四层负载设备中,把client发送的报文目标地址(原来是负载均衡设备的IP地址),根据均衡设备设置的选择web服务器的规则选择对应的web服务器IP地址,这样client就可以直接跟此服务器建立TCP连接并发送数据。 四层工作模式的IP透传:  haproxy配置中在后端服务器定义中加入关键字send-proxy(注意不要加在check关键字属性的中间了),并重启服务。  在后端nginx服务器配置中监听端口处也加上协议名proxy_protocol,并修改日志格式,在开头加入变量$proxy_protocol_addr,重启服务后即可在日志中看到访问的源地址。  send-proxy是haproxy后端设置的关键字,写错会报错,可以用来启用代理协议Proxy protocol。Proxy protocol是HAProxy的作者Willy Tarreau于2010年开发和设计的一个Internet协议,通过为tcp添加一个很小的头信息,来方便的传递客户端信息(协议栈、源IP、目的IP、源端口、目的端口等),在网络情况复杂又需要获取用户真实IP时非常有用。12345678910111213141516haproxy 配置:listen web_prot_http_nodes bind 172.18.32.249:80 mode tcp balance roundrobin server web1 192.168.32.81:80 send-proxy check inter 3000 fall 3 rise 5nginx配置:http { log_format main '$proxy_protocol_addr $remote_addr - $remote_user [$time_local] \"$request\" ' '$status $body_bytes_sent \"$http_referer\" ' '\"$http_user_agent\" ';server { listen 80 proxy_protocol; #listen 80; 内核参数优化  haproxy在做四层负载时,如果要监听bind由keepalived生成的虚拟IP(VIP)时,需要修改内核参数,支持监听非本机IP,否则监听VIP的80端口时会导致haproxy服务无法启动。。  vim /etc/sysctl.conf1net.ipv4.ip_nonlocal_bind = 1   如果多网卡,且VIP与后端VIP不在一个网段,还需要加上地址转发参数。1net.ipv4.ip_forward = 1   然后sysctl -p使配置文件生效。1234[root@CentOS8 ~]#sysctl -pnet.ipv4.ip_nonlocal_bind = 1net.ipv4.ip_forward = 1[root@CentOS8 ~]#   当然,也可以将haproxy改为监听0.0.0.0:80,表示监听本机所有网卡的IP的80端口,当keepalived的VIP漂到本机是,自然也可以被haproxy监听,没有时也不影响启动。不过这样的话,haproxy只能对一个项目集群做负载均衡了,而我们实际生产中,都是同时代理多个服务项目集群的转发,需通过bind不同IP的80/443端口来实现,如果直接一个服务bind0.0.0.0:80,占用了所有的80/443端口,显然就没法和其他项目共存了。  所以我们建议在四层负载工作模式下,不要监听0.0.0.0:80,而是监听指定的VIP。 http:七层代理  七层负载均衡服务器起了一个反向代理服务器的作用,服务器建立一次TCP连接要三次握手,而client要访问webserver要先与七层负载设备进行三次握手后建立TCP连接,把要访问的报文信息发送给七层负载均衡;然后七层负载均衡再根据设置的均衡规则选择特定的webserver,然后通过三次握手与此台webserver建立TCP连接,然后webserver把需要的数据发送给七层负载均衡设备,负载均衡设备再把数据发送给client;所以,七层负载均衡设备起到了代理服务器的作用。 七层工作模式的IP透传:1234567891011121314haproxy 配置:defaults option forwardfor或者: option forwardfor header X-Forwarded-xxx #自定义传递IP参数,后端web服务器写X-Forwardedxxx #如果写option forwardfor则后端服务器web格式为X-Forwarded-Forlisten配置:listen web_host bind 192.168.7.101:80 mode http log global balance random server web1 192.168.32.81:80 weight 1 check inter 3000 fall 2 rise 5 server web2 192.168.32.82:80 weight 1 check inter 3000 fall 2 rise 5   配置web服务器,记录负载均衡透传的客户端IP地址123456789#apache 配置: LogFormat \"%{X-Forwarded-For}i %a %l %u %t \\\"%r\\\" %>s %b \\\"%{Referer}i\\\" \\\"%{UserAgent}i\\\"\" combined#tomcat 配置: pattern='%{X-Forwarded-For}i %l %T %t &quot;%r&quot; %s %b &quot;%{UserAgent}i&quot;'/>#nginx 日志格式: http { log_format main '\"$http_x_forwarded_For\" - $remote_addr - $remote_user [$time_local] \"$request\" ' '$status $body_bytes_sent \"$http_referer\" ' '\"$http_user_agent\" '; haproxy功能实现  初步调整好haproxy的配置文件之后,启动haproxy服务,就已经可以对后端服务器进行代理来实现负载均衡了,不过很多情况下我们需要对后端服务器进行动态操作,例如修改某些主机的负载权重,对某些主机上线或下线等等,而这时,再不影响业务正常访问的情况下,对haproxy动态操作方式一般有两种:在图形界面status状态页下操作,以及使用socat命令行工具通过socket通信。 图形界面  想实现在status界面拥有修改权限,需在配置文件中加入选项stats admin if TRUE。注意,TRUE要大写,否则服务起不来会报错parsing [/etc/haproxy/haproxy.cfg:52] : error detected while parsing a 'stats admin' rule : no such ACL : 'true'.  最终如下面所示12345678listen stats mode http bind 0.0.0.0:9999 stats enable log global stats uri /proxy_status stats auth haadmin:hapasswd stats admin if TRUE   这是再刷新status界面,就可以看到界面已经发生了变化  就可以对选择的主机进行操作了。 命令行方式  这是通过直接与haproxy的socekt通信,socket路径就是配置文件中指定的socket路径了,只支持本地通信。而且这需要借主socat的工具,需要先进行安装socat工具。1yum install socat   用echo信息的方式通过管道传递给socat工具指定haproxy的socket,就可以发送与接收haproxy的信息了。  查看haproxy工作的详细信息1echo \"show info\" | socat stdio /var/lib/haproxy/haproxy.sock1   查看haproxy控制命令1echo \"help\" | socat stdio /var/lib/haproxy/haproxy.sock1 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465help : this messageprompt : toggle interactive mode with promptquit : disconnectshow tls-keys [id|*]: show tls keys references or dump tls ticket keys when id specifiedset ssl tls-key [id|keyfile] &lttlskey&gt: set the next TLS key for the &ltid&gt or &ltkeyfile&gt listener to &lttlskey&gtshow sess [id] : report the list of current sessions or dump this sessionshutdown session : kill a specific sessionshutdown sessions server : kill sessions on a serverclear counters : clear max statistics counters (add 'all' for all counters)show info : report information about the running process [json|typed]show stat : report counters for each proxy and server [json|typed]show schema json : report schema used for statsdisable agent : disable agent checks (use 'set server' instead)disable health : disable health checks (use 'set server' instead)disable server : disable a server for maintenance (use 'set server' instead)enable agent : enable agent checks (use 'set server' instead)enable health : enable health checks (use 'set server' instead)enable server : enable a disabled server (use 'set server' instead)set maxconn server : change a server's maxconn settingset server : change a server's state, weight or addressget weight : report a server's current weightset weight : change a server's weight (deprecated)show resolvers [id]: dumps counters from all resolvers section and associated name serversclear table : remove an entry from a tableset table [id] : update or create a table entry's datashow table [id]: report table usage stats or dump this table's contentsshow peers [peers section]: dump some information about all the peers or this peers sectiondisable frontend : temporarily disable specific frontendenable frontend : re-enable specific frontendset maxconn frontend : change a frontend's maxconn settingshow servers state [id]: dump volatile server information (for backend &ltid&gt)show backend : list backends in the current running configshutdown frontend : stop a specific frontendset dynamic-cookie-key backend : change a backend secret key for dynamic cookiesenable dynamic-cookie backend : enable dynamic cookies on a specific backenddisable dynamic-cookie backend : disable dynamic cookies on a specific backendshow errors : report last request and response errors for each proxyset maxconn global : change the per-process maxconn settingset rate-limit : change a rate limiting valueset severity-output [none|number|string] : set presence of severity level in feedback informationset timeout : change a timeout settingshow env [var] : dump environment variables known to the processshow cli sockets : dump list of cli socketsshow cli level : display the level of the current CLI sessionshow fd [num] : dump list of file descriptors in useshow activity : show per-thread activity stats (for support/developers)operator : lower the level of the current CLI session to operatoruser : lower the level of the current CLI session to usershow startup-logs : report logs emitted during HAProxy startupshow cache : show cache statusadd acl : add acl entryclear acl &ltid&gt : clear the content of this acldel acl : delete acl entryget acl : report the patterns matching a sample for an ACLshow acl [id] : report available acls or dump an acl's contentsadd map : add map entryclear map &ltid&gt : clear the content of this mapdel map : delete map entryget map : report the keys and values matching a sample for a mapset map : modify map entryshow map [id] : report available maps or dump a map's contentsshow pools : report information about the memory pools usageshow profiling : show CPU profiling optionsset profiling : enable/disable CPU profilingshow threads : show some threads debugging information   查看线程1工作下的web_host集群中web1主机的权重1234[root@CentOS8 ~]#echo \"get weight web_host/web1\" | socat stdio /var/lib/haproxy/haproxy.sock11 (initial 1)[root@CentOS8 ~]#   设置线程2工作下的web_host集群中web1主机的权重为2。设置时,不回应信息,说明设置成功。123[root@CentOS8 ~]#echo \"set weight web_host/web1 2\" | socat stdio /var/lib/haproxy/haproxy.sock2[root@CentOS8 ~]#   需要注意的一点就是,多线程工作模式下,每个线程是独立的,设置1线程的权重,在其他线程上并不生效。这就意味着,如果想将某个服务器下线的话,需要在每个线程上都分别下线,上线是,也需要在每个线程中enable server。可以用脚本写一个循环来实现。","categories":[{"name":"linux进阶","slug":"linux进阶","permalink":"https://wudihechao.github.io/categories/linux进阶/"}],"tags":[{"name":"企业级应用","slug":"企业级应用","permalink":"https://wudihechao.github.io/tags/企业级应用/"},{"name":"高可用","slug":"高可用","permalink":"https://wudihechao.github.io/tags/高可用/"},{"name":"HAProxy","slug":"HAProxy","permalink":"https://wudihechao.github.io/tags/HAProxy/"},{"name":"负载均衡","slug":"负载均衡","permalink":"https://wudihechao.github.io/tags/负载均衡/"},{"name":"调度算法","slug":"调度算法","permalink":"https://wudihechao.github.io/tags/调度算法/"}],"keywords":[{"name":"linux进阶","slug":"linux进阶","permalink":"https://wudihechao.github.io/categories/linux进阶/"}]},{"title":"报错:Header V3 RSA_SHA256 Signature, key ID 2f86d6a1_ NOKEY","slug":"报错:Header-V3-RSA-SHA256-Signature-key-ID-2f86d6a1-NOKEY","date":"2019-11-02T12:04:29.000Z","updated":"2019-12-09T02:46:56.358Z","comments":true,"path":"/blog/5da16ceb.html","link":"","permalink":"https://wudihechao.github.io/blog/5da16ceb.html","excerpt":"  博主在CentOS8上dnf安装PHP的依赖包libmcrypt-devel的时候,死活装不上,一直报错。看了下说是依赖项libmcrypt装不上,报错原因说的是是秘钥校验时缺少主机名。这很奇怪,因为用的是阿里的epel源,之前安装各种软件包都没问题,这次突然就秘钥验证不过了。","text":"  博主在CentOS8上dnf安装PHP的依赖包libmcrypt-devel的时候,死活装不上,一直报错。看了下说是依赖项libmcrypt装不上,报错原因说的是是秘钥校验时缺少主机名。这很奇怪,因为用的是阿里的epel源,之前安装各种软件包都没问题,这次突然就秘钥验证不过了。 12345678910111213141516171819202122232425[root@CentOS8 php-7.3.10]#yum install libmcrypt-devel -yLast metadata expiration check: 0:01:31 ago on Sat 02 Nov 2019 07:47:12 PM CST.Dependencies resolved.===================================================================================================================================== Package Arch Version Repository Size=====================================================================================================================================Installing: libmcrypt-devel x86_64 2.5.8-26.el8 aliyun 18 kInstalling dependencies: libmcrypt x86_64 2.5.8-26.el8 aliyun 109 kTransaction Summary=====================================================================================================================================Install 2 PackagesTotal size: 127 kInstalled size: 320 kDownloading Packages:[SKIPPED] libmcrypt-2.5.8-26.el8.x86_64.rpm: Already downloaded [SKIPPED] libmcrypt-devel-2.5.8-26.el8.x86_64.rpm: Already downloaded warning: /var/cache/dnf/aliyun-a19d7e5a690d289a/packages/libmcrypt-2.5.8-26.el8.x86_64.rpm: Header V3 RSA/SHA256 Signature, key ID 2f86d6a1: NOKEYaliyun 0.0 B/s | 0 B 00:00 Curl error (3): URL using bad/illegal format or missing URL for file://https://mirrors.aliyun.com/epel/RPM-GPG-KEY-EPEL-8 [Invalid file://hostname/, expected localhost or 127.0.0.1 or none]The downloaded packages were saved in cache until the next successful transaction.You can remove cached packages by executing 'dnf clean packages'.   查了下网上很多说法,不过都没有解决疑问。解决方案也五花八门,类似rpm --import /etc/pki/rpm-gpg/RPM*导入秘钥的,也有建议安装时加选项--force --nodeps忽略依赖关系的。秘钥导入没用,我本身yum源仓库配置文件也填写了阿里云镜像源的秘钥路径的。忽略依赖关系强制安装的话,担心会不按依赖项,导致其他别的问题,而且这个选项本身也报错了。  后来只能干脆把秘钥检查关了,确实能装上了。  vim /etc/yum.repos.d/aliyun.repo12345name=aliyunbaseurl=https://mirrors.aliyun.com/epel/$releasever/Everything/$basearch/gpgcheck=0enabled=1gpgkey=file://https://mirrors.aliyun.com/epel/RPM-GPG-KEY-EPEL-$releasever   希望不是因为阿里的安装包被人动过而导致秘钥检查不通过吧,姑且相信吧。记录一下,等后期如果有问题或者有空再来排查原因。    ——————————————后记 ———————————–  原来当时配置yum源文件的时候不小心画蛇添足了,导致gpgkey文件路径不对,应为gpgkey=https://mirrors.aliyun.com/epel/RPM-GPG-KEY-EPEL-$releaseve,用file://的话,就不需要加协议了,直接写主机名或IP加路径。  要时刻提醒自己还是要仔细,一直不出错不代表没错。                          2019.11.10","categories":[{"name":"故障记录","slug":"故障记录","permalink":"https://wudihechao.github.io/categories/故障记录/"}],"tags":[{"name":"故障","slug":"故障","permalink":"https://wudihechao.github.io/tags/故障/"},{"name":"排错","slug":"排错","permalink":"https://wudihechao.github.io/tags/排错/"},{"name":"记录","slug":"记录","permalink":"https://wudihechao.github.io/tags/记录/"},{"name":"epel","slug":"epel","permalink":"https://wudihechao.github.io/tags/epel/"},{"name":"阿里云","slug":"阿里云","permalink":"https://wudihechao.github.io/tags/阿里云/"}],"keywords":[{"name":"故障记录","slug":"故障记录","permalink":"https://wudihechao.github.io/categories/故障记录/"}]},{"title":"nginx防盗链设置的一些细节","slug":"nginx防盗链设置的一些细节","date":"2019-10-30T06:08:19.000Z","updated":"2019-11-10T04:33:36.888Z","comments":true,"path":"/blog/e4415633.html","link":"","permalink":"https://wudihechao.github.io/blog/e4415633.html","excerpt":"  防盗链的必要性,我这里就不再赘述了,这是网站设计的最基本要求。而在nginx中,一般比较容易实现的防盗链手段就是通过ungx_http_referer_module模块(查看官方文档) 检查访问请求的referer信息是否有效来实现防盗链功能。  所谓referer检查,举个例子来说,在正常情况下当用户在浏览http://example.com/abc.html时点击一个链接去到http://example.com/123.mp3文件时,浏览器在发出请求123.mp3 资源时还会附带当刻浏览器所处的页面地址(即http://example.com/abc.html),所以当你的网站程序接收到下载 jacky.mp3 资源请求的时候,先判断http的referer字段的值,如果是从 自己的域名(example.com)过来的,则可以认为是合法的连接请求,否则就返回一个错误的提示信息。","text":"  防盗链的必要性,我这里就不再赘述了,这是网站设计的最基本要求。而在nginx中,一般比较容易实现的防盗链手段就是通过ungx_http_referer_module模块(查看官方文档) 检查访问请求的referer信息是否有效来实现防盗链功能。  所谓referer检查,举个例子来说,在正常情况下当用户在浏览http://example.com/abc.html时点击一个链接去到http://example.com/123.mp3文件时,浏览器在发出请求123.mp3 资源时还会附带当刻浏览器所处的页面地址(即http://example.com/abc.html),所以当你的网站程序接收到下载 jacky.mp3 资源请求的时候,先判断http的referer字段的值,如果是从 自己的域名(example.com)过来的,则可以认为是合法的连接请求,否则就返回一个错误的提示信息。  这种方法通常用于图片、mp3这种容易被人用html“嵌入”到其他网站的资源,使用这种方法可以防止你的图片直接出现在别人的网页里(或者防止mp3直接被其他网站嵌入到flash播放器里),不过访客使用下载工具还是可以轻松下载,因为现在的下载工具一般会自动用你的域名构造一个引用地址,所以如果想再进一步防范的话,可以使用一个对应表限制每个资源的引用地址,例如将 123.mp3 的引用地址限制为http://example.com/abc.htmlid=123456,这样下载工具就不太可能构造一个“正确”的引用地址了。 referer  要过滤掉盗链访问的referer信息,首先要明确知道,正常访问的referer有哪些。一般来说,正常的referer信息有以下四种: none:请求报文首部没有referer首部,比如用户直接在浏览器输入域名访问web网站,就没有referer信息。 blocked:请求报文有referer首部,但无有效值,比如为空。 本站链接:referer首部中包含本站域名。 搜索引擎跳转:referer中为 .baidu. 、 .google. 、及其他搜索引擎(如360、必应)(具体图片或mp3媒体文件,不希望被搜索引擎引用,可单独设置,主页等html页面建议允许搜索引擎跳转)  所以根据官方文档,我们只需制定合适的匹配规则,将正常的访问放过,对那些“非正常的”盗链访问,返回403错误代码,即可实现防盗链。undefined 过滤规则设置  打开nginx配置文件,找到想要定义的location下,加入下面设置123456789101112131415location /blog/ { root /apps/nginx/html/; #定义路径 valid_referers none blocked server_names *.example.com ~\\.google\\. ~\\.baidu\\.; #设置允许访问的匹配规则,匹配规则可以写在一行,也可以分行写。 if ($invalid_referer) { #设置条件判断,不符合上述规则的,返回403状态码 return 403; }}location ^~ /mp3/ { alias /apps/nginx/html/blog/mp3/; #定义路径,也可用root valid_referers none blocked server_names *.example.com ; if ($invalid_referer) { #设置条件判断,不符合上述规则的,返回403状态码 return 403; }}   也可在全局配置server中做设置,不过还是建议每个location单独设置,因为对于图片和或者音频视频文件本身,还是不希望直接被搜索引擎所引用,造成网站资源的无意义的消耗。 跳转设置  对于盗链者,也可以予以反击,允许他们请求我们的资源,不过,只给他们我们指定的资源,例如百度使用的防盗链图:     配置上只需将return 403;改为rewrite ^/ http://www.example/images/return.jpg; 例如:1234567891011location ~ return\\.jpg$ { root /apps/nginx/html/blog/images/}location ~ .*\\.(gif|jpg|jpeg|png|bmp|swf)$ { access_log off; root /apps/nginx/html/blog/images/; valid_referers none blocked server_names *.example.com ~\\.google\\. ~\\.baidu\\.; if ($invalid_referer) { rewrite ^/ http://www.example/images/return.jpg; }}   PS:return.jpg要设置规则优先匹配到,这个图片不能被防盗链,不然会无限重定向,导致显示不正常。 规则细节  设置匹配规则时,根据官方文档,只有none、blocked、server_names、arbitrary string和regular expression五种规则。 Parameters can be as follows: none  the “Referer” field is missing in the request header;blocked  the “Referer” field is present in the request header, but its value has been deleted by a firewall or proxy server; such values are strings that do not start with “http://” or “https://”;server_names  the “Referer” request header field contains one of the server names;arbitrary string  defines a server name and an optional URI prefix. A server name can have an “* ” at the beginning or end. During the checking, the server’s port in the “Referer” field is ignored;regular expression  the first symbol should be a “~”. It should be noted that an expression will be matched against the text starting after the “http://” or “https://”.   这就要求我们在设置匹配规则的时候,要按照这个五种方式来,none、blocked直接写上就可以了,没有什么可说的,我们重点理解下剩下三种。 server_names  server names字面上理解很容易,就是匹配的域名。注意:这里的域名,指本服务器上所有监听的域名。而且这是一个包含的关系,只要referer头部信息中包含有本服务器的监听的任意域名,即可通过匹配。 arbitrary string  翻译过来是任意字符串,其实就是任意可以匹配到到字符串,这里支持通配符。大致有2种写法: 直接写域名例如可以写*.example.com,也可写为www.example.*,可问题是为什么就偏偏不支持 .example. 呢。这我也很费解,不过确实不支持,有兴趣的朋友可以去试一下,也希望能有大佬告知这其中的原理是什么。 123[root@CentOS8 ~]#/apps/nginx/sbin/nginx -tnginx: [emerg] invalid hostname or wildcard \"*.example.*\" in /apps/nginx/conf/nginx.conf:95nginx: configuration file /apps/nginx/conf/nginx.conf test failed 定义匹配域名加路径  例如:www.example.com/blog;  而博主试验过很很多次,如果写成例如www.example.com/*,在www.example.com/blog/页面下去引用页面下的/apps/nginx/html/mp3/123.mp3文件时就会报403错误,而写域名加确切地址如www.example.com/blog时才可以访问。仔细查阅了官方文档,才知道,有个很关键的细节就是,这个通配符的位置,只能在域名里。可以再看一下官方文档,   defines a server name and an optional URI prefix. A server name can have an “*” at the beginning or end. During the checking, the server’s port in the “Referer” field is ignored;   我们可以得知,只可以在域名的开头和结尾用 的通配符,而不是URI中,这也就是为什么我发现www.examlpe.com/无法匹配通过的原因。 &emsp;&emsp;跟server_names一样,只要**包含**自定义字符串就可以,例如匹配规则写成www.example.com/mp3/``,在``www.example.com/mp3/``页面下就可以引用的``/apps/nginx/html/mp3/123.mp3``文件了,在``www.example.com/mp3/abc/efg/``页面下是同样可以跳转访问``/apps/nginx/html/mp3/123.mp3``文件的。 regular expression  被指定的正则表达式模式匹配到的字符串,要使用 ~ 开头,例如:~.*.google.com。这要严格按照正则表达式匹配到的referer写,否则就会无法访问。 总结  设置匹配规则时,必须符合其中的某一种,而不能想当然的把几种规则混合起来使用,想要放行的链接,一定要考虑好,到底确切适用于哪一种规则,才不会出现“误伤“、“漏网”的情况。","categories":[{"name":"linux进阶","slug":"linux进阶","permalink":"https://wudihechao.github.io/categories/linux进阶/"}],"tags":[{"name":"nginx","slug":"nginx","permalink":"https://wudihechao.github.io/tags/nginx/"},{"name":"防盗链","slug":"防盗链","permalink":"https://wudihechao.github.io/tags/防盗链/"},{"name":"referer","slug":"referer","permalink":"https://wudihechao.github.io/tags/referer/"},{"name":"配置文件","slug":"配置文件","permalink":"https://wudihechao.github.io/tags/配置文件/"},{"name":"细节","slug":"细节","permalink":"https://wudihechao.github.io/tags/细节/"}],"keywords":[{"name":"linux进阶","slug":"linux进阶","permalink":"https://wudihechao.github.io/categories/linux进阶/"}]},{"title":"企业级应用——负载均衡层LVS调度器详解","slug":"企业级应用——负载均衡层LVS调度器详解","date":"2019-10-24T14:26:16.000Z","updated":"2019-12-25T13:52:17.736Z","comments":true,"path":"/blog/159ef64c.html","link":"","permalink":"https://wudihechao.github.io/blog/159ef64c.html","excerpt":"  所谓LVS,是Linux Virtual Server的缩写,直译就是linux虚拟服务器。LVS说是虚拟服务器,并不是说这个服务器本身不存在,而是指一般用户访问企业web网站时,访问的都是LVS,而LVS本身上面没有任何web界面资源,真实的界面以及服务都在后端web服务器上,LVS服务器起到的是一个指引分流的作用,所以相对来说,后端的web服务器是real server,而LVS就被称为是virtual server(虚拟服务器)了。  既然这个服务器上没有页面资源,又无法提供服务,那为什么还有必要部署它来多此一举呢?因为通常来说,我们访问的web服务,都不是由单一服务器主机来支撑的,背后都有好几台、甚至成百上千台web服务器集群共同提供,一台单一主机是无法支撑大的访问并发的,需要很多台服务器来共同分担压力,这时就需要一个专门的服务器来进行调度,将大量访问请求分配到不同的web服务器上,减小每台服务器的压力,实现负载均衡。  本文将详细介绍LVS调度器的工作模式及配置实例。","text":"  所谓LVS,是Linux Virtual Server的缩写,直译就是linux虚拟服务器。LVS说是虚拟服务器,并不是说这个服务器本身不存在,而是指一般用户访问企业web网站时,访问的都是LVS,而LVS本身上面没有任何web界面资源,真实的界面以及服务都在后端web服务器上,LVS服务器起到的是一个指引分流的作用,所以相对来说,后端的web服务器是real server,而LVS就被称为是virtual server(虚拟服务器)了。  既然这个服务器上没有页面资源,又无法提供服务,那为什么还有必要部署它来多此一举呢?因为通常来说,我们访问的web服务,都不是由单一服务器主机来支撑的,背后都有好几台、甚至成百上千台web服务器集群共同提供,一台单一主机是无法支撑大的访问并发的,需要很多台服务器来共同分担压力,这时就需要一个专门的服务器来进行调度,将大量访问请求分配到不同的web服务器上,减小每台服务器的压力,实现负载均衡。  本文将详细介绍LVS调度器的工作模式及配置实例。 集群和分布式  集群:同一个业务系统,部署在多台服务器上。集群中,每一台服务器实现的功能没有差别,数据和代码都是一样的。  分布式:一个业务被拆成多个子业务,或者本身就是不同的业务,部署在多台服务器上。分布式中,每一台服务器实现的功能是有差别的,数据和代码也是不一样的,分布式每台服务器功能加起来,才是完整的业务。  分布式是以缩短单个任务的执行时间来提升效率的,而集群则是通过提高单位时间内执行的任务数来提升效率。  对于大型网站,访问用户很多,实现一个群集,在前面部署一个负载均衡服务器,后面几台服务器完成同一业务。如果有用户进行相应业务访问时,负载均衡器根据后端哪台服务器的负载情况,决定由给哪一台去完成响应,并且一台服务器垮了,其它的服务器可以顶上来。分布式的每一个节点,都完成不同的业务,如果一个节点垮了,那这个业务可能就会失败。 Cluster概念  Cluster:集群,为解决某个特定问题将多台计算机组合起来形成的单个系统。;Linux Cluster类型: LB:Load Balancing,负载均衡 HA:High Availiablity,高可用,SPOF(single Point Of failure)MTBF:Mean Time Between Failure 平均无故障时间MTTR:Mean Time To Restoration( repair)平均恢复前时间A=MTBF/(MTBF+MTTR(0,1):99%,99.5%,99.9%,99.99%,99.999% HPC:High-performance computing,高性能 LB Cluster的实现 硬件F5 Big-IPCitrix NetscalerA10 A10 软件lvs:Linux Virtual Server,阿里四层SLB (Server Load Balance)使用网络传输的下四层,不支持应用层等数据的调度内核级:功能虽然弱,性能好,一般最为最前端的调度器.nginx:支持七层调度,阿里七层SLB使用Tenginehaproxy:支持七层调度ats:Apache Traffic Server,yahoo捐助给apacheperlbal:Perl 编写pound LB Cluster基于工作的协议层次划分: 传输层(通用):DPORTLVS:nginx:streamhaproxy:mode tcp 应用层(专用):针对特定协议,自定义的请求模型分类proxy server:http:nginx, httpd, haproxy(mode http), …fastcgi:nginx, httpd, …mysql:mysql-proxy, … LB Cluster中用户的会话保持  负载均衡之后,用户会话保持的解决方案:  (1) session sticky:同一用户调度固定服务器   Source IP:LVS sh算法(对某一特定服务而言)(固定IP)   Cookie (LVS四层调度,无法识别cookie)  (2) session replication:每台服务器拥有全部session   session multicast cluster  (3) session server:专门的session服务器,如Memcached,Redis 分布式系统:常见的分布式存储: Ceph,GlusterFS,FastDFS,MogileFS常见的分布式计算:hadoop,Spark这里不多介绍。 LVS介绍  LVS:Linux Virtual Server,负载调度器,在linux内核集成。LVS项目在1998年5月由章文嵩博士成立,是中国国内最早出现的自由软件项目之一。  LVS实现负载均衡,自身的单点失败问题由keepalived解决(vrrp协议).  keepalived还可以实现检查后端服务器状态. lvs集群类型中的术语: VS:Virtual Server,Director Server(DS),Dispatcher(调度器),Load Balancer RS:Real Server(lvs),,upstream server(nginx),backend server(haproxy) CIP:Client IP VIP: Virtual serve IP VS外网的IP (一般也是内网IP,由防火墙DNAT指向) DIP: Director IP VS内网的IP RIP: Real server IP  访问流程:CIP VIP == DIP RIP lvs: ipvsadm/ipvs  ipvsadm:用户空间的命令行工具,规则管理器,用于管理集群服务及RealServer  ipvs:工作于内核空间netfilter的INPUT钩子上的框架 lvs的模式:  lvs-nat:修改请求报文的目标IP,多目标IP的DNAT  lvs-dr:操纵封装新的MAC地址  lvs-tun:在原请求IP报文之外新加一个IP首部  lvs-fullnat:修改请求报文的源和目标IP lvs-nat:  本质是多目标IP的DNAT,通过将请求报文中的目标地址和目标端口修改为某挑出的RS的RIP和PORT实现转发 (1)RIP和DIP应在同一个IP网络,且应使用私网地址;RS的网关要指向DIP (2)请求报文和响应报文都必须经由Director转发,Director易于成为系统瓶颈 (3)支持端口映射,可修改请求报文的目标PORT (4)VS必须是Linux系统,RS可以是任意OS系统 LVS-DR:  Direct Routing,直接路由,LVS默认模式,应用最广泛,通过为请求报文重新封装一个MAC首部进行转发,源MAC是DIP所在的接口的MAC,目标MAC是某挑选出的RS的RIP所在接口的MAC地址;源IP/PORT,以及目标IP/PORT均保持不变 (1) Director和各RS都配置有VIP (2) 确保前端路由器将目标IP为VIP的请求报文发往Director   在前端网关做静态绑定VIP和Director的MAC地址   在RS上使用arptables工具 arptables -A IN -d $VIP -j DROP arptables -A OUT -s $VIP -j mangle --mangle-ip-s $RIP   在RS上修改内核参数以限制arp通告及应答级别 /proc/sys/net/ipv4/conf/all/arp_ignore /proc/sys/net/ipv4/conf/all/arp_announce (3)RS的RIP可以使用私网地址,也可以是公网地址;RIP与DIP在同一IP网络;RIP的网关不能指向DIP,以确保响应报文不会经由Director (4)RS和Director要在同一个物理网络 (5)请求报文要经由Director,但响应报文不经由Director,而由RS直接发往Client (6)不支持端口映射(端口不能修败) (7)RS可使用大多数OS系统 lvs-tun:  转发方式:不修改请求报文的IP首部(源IP为CIP,目标IP为VIP),而在原IP报文之外再封装一个IP首部(源IP是DIP,目标IP是RIP),将报文发往挑选出的目标RS;RS直接响应给客户端(源IP是VIP,目标IP是CIP)   (1) DIP, VIP, RIP都应该是公网地址   (2) RS的网关一般不能指向DIP   (3) 请求报文要经由Director,但响应不经由Director   (4) 不支持端口映射   (5) RS的OS须支持隧道功能 lvs-fullnat:通过同时修改请求报文的源IP地址和目标IP地址进行转发  CIP –> DIP  VIP –> RIP (1) VIP是公网地址,RIP和DIP是私网地址,且通常不在同一IP网络;因此,RIP的网关一般不会指向DIP (2) RS收到的请求报文源地址是DIP,因此,只需响应给DIP;但Director还要将其发往Client (3) 请求和响应报文都经由Director (4) 支持端口映射注意:此类型kernel默认不支持 LVS调度算法ipvs scheduler:  根据其调度时是否考虑各RS当前的负载状态分为两种:静态方法和动态方法 静态方法:仅根据算法本身进行调度 1、RR:roundrobin,轮询 2、WRR:Weighted RR,加权轮询 3、SH:Source Hashing,实现session sticky,源IP地址hash;将来自于同一个IP地址的请求始终发往第一次挑中的RS,从而实现会话绑定 4、DH:Destination Hashing;目标地址哈希,第一次轮询调度至RS,后续将发往同一个目标地址的请求始终转发至第一次挑中的RS,典型使用场景是正向代理缓存场景中的负载均衡,如:宽带运营商 动态方法:主要根据每RS当前的负载状态及调度算法进行调度Overhead=value较小的RS将被调度 1、LC:least connections 适用于长连接应用 Overhead=activeconns*256+inactiveconns 2、WLC:Weighted LC,默认调度方法 Overhead=(activeconns*256+inactiveconns)/weight 3、SED:Shortest Expection Delay,初始连接高权重优先 Overhead=(activeconns+1)*256/weight 4、NQ:Never Queue,第一轮均匀分配,后续SED 5、LBLC:Locality-Based LC,动态的DH算法,使用场景:根据负载状态实现正向代理 6、LBLCR:LBLC with Replication,带复制功能的LBLC,解决LBLC负载不均衡问题,从负载重的复制到负载轻的RS ipvsadm/ipvs:ipvs:  grep -i -A 10 "ipvs" /boot/config-VERSION-RELEASE.x86_64  支持的协议:TCP, UDP, AH, ESP, AH_ESP, SCTPipvs集群:  管理集群服务  管理服务上的RS ipvsadm:  程序包:ipvsadm  Unit File: ipvsadm.service  主程序:/usr/sbin/ipvsadm  规则保存工具:/usr/sbin/ipvsadm-save  规则重载工具:/usr/sbin/ipvsadm-restore  配置文件:/etc/sysconfig/ipvsadm-configipvsadm命令:核心功能: 集群服务管理:增、删、改 集群服务的RS管理:增、删、改 查看   例:命令总结:123456789ipvsadm -A|E -t|u|f service-address [-s scheduler] [-p [timeout]] [-M netmask] [--pepersistence_engine] [-b sched-flags]ipvsadm -D -t|u|f service-address 删除ipvsadm –C 清空ipvsadm –R 重载ipvsadm -S [-n] 保存ipvsadm -a|e -t|u|f service-address -r server-address [options]ipvsadm -d -t|u|f service-address -r server-addressipvsadm -L|l [options]ipvsadm -Z [-t|u|f service-address] 管理集群服务:增、改、删  增、改:ipvsadm -A|E -t|u|f service-address [-s scheduler] [-p [timeout]]  删除:ipvsadm -D -t|u|f service-addressservice-address: VIP   -t|u|f:   -t: TCP协议的端口,VIP:TCP_PORT   -u: UDP协议的端口,VIP:UDP_PORT   -f:firewall MARK,标记,一个数字  [-s scheduler]:指定集群的调度算法,默认为wlc 管理集群上的RS:增、改、删  增、改:ipvsadm -a|e -t|u|f service-address -r server-address [-g|i|m] [-w weight]  删:ipvsadm -d -t|u|f service-address -r server-address server-address  rip[:port] 如省略port,不作端口映射选项:  lvs类型:  -g: gateway, dr类型,默认  -i: ipip, tun类型  -m: masquerade, nat类型  -w weight:权重   清空定义的所有内容:ipvsadm –C清空计数器:ipvsadm -Z [-t|u|f service-address] 查看:ipvsadm -L|l [options]  –numeric, -n:以数字形式输出地址和端口号  –exact:扩展信息,精确值  –connection,-c:当前IPVS连接输出  –stats:统计信息  –rate :输出速率信息 ipvs规则:/proc/net/ip_vsipvs连接:/proc/net/ip_vs_conn 保存及重载规则  保存:建议保存至/etc/sysconfig/ipvsadmipvsadm-save > /PATH/TO/IPVSADM_FILEipvsadm -S > /PATH/TO/IPVSADM_FILEsystemctl stop ipvsadm.service  重载:ipvsadm-restore < /PATH/FROM/IPVSADM_FILEsystemctl restart ipvsadm.service 注意事项负载均衡集群设计时要注意的问题  (1) 是否需要会话保持  (2) 是否需要共享存储 共享存储:NAS, SAN, DS(分布式存储) 数据同步: lvs-nat:设计要点:  (1) RIP与DIP在同一IP网络, RIP的网关要指向DIP  (2) 支持端口映射  (3) Director要打开核心转发功能 LVS-DRDR模型中各主机上均需要配置VIP,解决地址冲突的方式有三种: (1) 在前端网关做静态绑定 (2) 在各RS使用arptables (3) 在各RS修改内核参数,来限制arp响应和通告的级别限制响应级别:arp_ignore 0:默认值,表示可使用本地任意接口上配置的任意地址进行响应 1: 仅在请求的目标IP配置在本地主机的接收到请求报文的接口上时,才给予响应限制通告级别:arp_announce 0:默认值,把本机所有接口的所有信息向每个接口的网络进行通告 1:尽量避免将接口信息向非直接连接网络进行通告 2:必须避免将接口信息向非本网络进行通告 附:  RS的配置脚本12345678910111213141516171819202122232425#!/bin/bashvip=10.0.0.100mask='255.255.255.255'dev=lo:1case $1 instart)echo 1 > /proc/sys/net/ipv4/conf/all/arp_ignoreecho 1 > /proc/sys/net/ipv4/conf/lo/arp_ignoreecho 2 > /proc/sys/net/ipv4/conf/all/arp_announceecho 2 > /proc/sys/net/ipv4/conf/lo/arp_announceifconfig $dev $vip netmask $mask #broadcast $vip up#route add -host $vip dev $dev;;stop)ifconfig $dev downecho 0 > /proc/sys/net/ipv4/conf/all/arp_ignoreecho 0 > /proc/sys/net/ipv4/conf/lo/arp_ignoreecho 0 > /proc/sys/net/ipv4/conf/all/arp_announceecho 0 > /proc/sys/net/ipv4/conf/lo/arp_announce;;*)echo \"Usage: $(basename $0) start|stop\"exit 1;;esac   VS的配置脚本12345678910111213141516171819202122232425#!/bin/bashvip='10.0.0.100'iface='lo:1'mask='255.255.255.255'port='80'rs1='192.168.0.101'rs2='192.168.0.102'scheduler='wrr'type='-g'case $1 instart)ifconfig $iface $vip netmask $mask #broadcast $vip upiptables -Fipvsadm -A -t ${vip}:${port} -s $scheduleripvsadm -a -t ${vip}:${port} -r ${rs1} $type -w 1ipvsadm -a -t ${vip}:${port} -r ${rs2} $type -w 1;;stop)ipvsadm -Cifconfig $iface down;;*)echo \"Usage $(basename $0) start|stop“exit 1esac FireWall Mark  FWM:FireWall Mark  MARK target 可用于给特定的报文打标记  –set-mark value其中:value 可为0xffff格式,表示十六进制数字  借助于防火墙标记来分类报文,而后基于标记定义集群服务;可将多个不同的应用使用同一个集群服务进行调度  实现方法:  在Director主机打标记:iptables -t mangle -A PREROUTING -d $vip -p $proto –m multiport - -dports $port1,$port2,… -j MARK --set-mark NUMBER  在Director主机基于标记定义集群服务:ipvsadm -A -f NUMBER [options] 持久连接  session 绑定:对共享同一组RS的多个集群服务,需要统一进行绑定,lvs sh算法无法实现  持久连接( lvs persistence )模板:实现无论使用任何调度算法,在一段时间内(默认360s ),能够实现将来自同一个地址的请求始终发往同一个RSipvsadm -A|E -t|u|f service-address [-s scheduler] [-p [timeout]]  持久连接实现方式:  每端口持久(PPC):每个端口定义为一个集群服务,每集群服务单独调度  每防火墙标记持久(PFWMC):基于防火墙标记定义集群服务;可实现将多个端口上的应用统一调度,即所谓的port Affinity  每客户端持久(PCC):基于0端口(表示所有服务)定义集群服务,即将客户端对所有应用的请求都调度至后端主机,必须定义为持久模式 LVS高可用性 Director不可用,整个系统将不可用;SPoF Single Point of Failure  解决方案:高可用  keepalived heartbeat/corosync 某RS不可用时,Director依然会调度请求至此RS  解决方案: 由Director对各RS健康状态进行检查,失败时禁用,成功时启用keepalived heartbeat/corosync ldirectord  检测方式:  (a) 网络层检测,icmp  (b) 传输层检测,端口探测  (c) 应用层检测,请求某关键资源  RS全不用时:backup server, sorry server ldirectord  ldirectord:监控和控制LVS守护进程,可管理LVS规则  包名:ldirectord-3.9.6-0rc1.1.1.x86_64.rpm  下载:http://download.opensuse.org/repositories/network:/haclustering:/Stable/CentOS_CentOS-7/x86_64/  文件:  /etc/ha.d/ldirectord.cf 主配置文件  /usr/share/doc/ldirectord-3.9.6/ldirectord.cf 配置模版  /usr/lib/systemd/system/ldirectord.service 服务  /usr/sbin/ldirectord 主程序,Perl实现  /var/log/ldirectord.log 日志  /var/run/ldirectord.ldirectord.pid pid文件   Ldirectord配置文件示例123456789101112131415checktimeout=3checkinterval=1autoreload=yeslogfile=“/var/log/ldirectord.log“ #日志文件quiescent=no #down时yes权重为0,no为删除virtual=5 #指定VS的FWM 或 IP:PORTreal=172.16.0.7:80 gate 2 #DR模型,权重为 2real=172.16.0.8:80 gate 1fallback=127.0.0.1:80 gate #sorry serverservice=httpscheduler=wrrchecktype=negotiatecheckport=80request=\"index.html\"receive=“Test Ldirectord\"","categories":[{"name":"linux进阶","slug":"linux进阶","permalink":"https://wudihechao.github.io/categories/linux进阶/"}],"tags":[{"name":"企业级应用","slug":"企业级应用","permalink":"https://wudihechao.github.io/tags/企业级应用/"},{"name":"负载均衡","slug":"负载均衡","permalink":"https://wudihechao.github.io/tags/负载均衡/"},{"name":"调度算法","slug":"调度算法","permalink":"https://wudihechao.github.io/tags/调度算法/"},{"name":"集群","slug":"集群","permalink":"https://wudihechao.github.io/tags/集群/"},{"name":"LVS","slug":"LVS","permalink":"https://wudihechao.github.io/tags/LVS/"}],"keywords":[{"name":"linux进阶","slug":"linux进阶","permalink":"https://wudihechao.github.io/categories/linux进阶/"}]},{"title":"源码编译Apache和PHP实现lamp架构","slug":"源码编译Apache和PHP实现lamp架构","date":"2019-10-18T13:12:31.000Z","updated":"2019-12-04T06:13:26.626Z","comments":true,"path":"/blog/dcaccc5a.html","link":"","permalink":"https://wudihechao.github.io/blog/dcaccc5a.html","excerpt":"  企业中web业务最常见的架构就是lamp架构、lnmp架构或者lnmt架构。本文将详细讲解lamp架构的源码编译方式的部署实现。","text":"  企业中web业务最常见的架构就是lamp架构、lnmp架构或者lnmt架构。本文将详细讲解lamp架构的源码编译方式的部署实现。  所谓LAMP架构,是指:    L:linux    A:apache (httpd)    M:mysql, mariadb    (或M:memcached)    P:php, perl, python  WEB资源类型:    静态资源:原始形式与响应内容一致,在客户端浏览器执行    动态资源:原始形式通常为程序文件,需要在服务器端执行之后,将执行结果返回给客户端 LAMP工作原理  httpd:接收用户的web请求;静态资源则直接响应;动态资源为php脚本,对此类资源的请求将交由php来运行  php:运行php程序  MariaDB:数据管理系统 httpd与php结合的方式1、modules (将php编译成为httpd的模块,默认方式) MPM:prefork: libphp5.soevent, worker: libphp5-zts.so2、FastCGI   本文将分别展示以模块方式以及FCGI的方式,源码编译apache、php、mariadb来实现LAMP架构。 编译部署编译安装mariadb  之前曾详细介绍,这里就不再赘述。 编译安装apache  先安装依赖包yum install gcc pcre-devel openssl-devel expat-devel autoconf libtool gcc-c++  下载apache源码包以及apr包123wget https://archive.apache.org/dist/httpd/httpd-2.4.39.tar.gzwget https://www-us.apache.org/dist//apr/apr-1.7.0.tar.gzwget https://www-us.apache.org/dist//apr/apr-util-1.6.1.tar.gz   替换apr、apr-util文件12345tar xf apr-1.7.0.tar.gz -C httpd-2.4.39/srclib/ tar xf apr-util-1.6.1.tar.gz -C httpd-2.4.39/srclib/ cd httpd-2.4.39/srclib/mv apr-1.7.0 aprmv apr-util-1.6.1 apr-util   编译安装1234567891011./configure --prefix=/data/httpd24 \\--enable-so \\--enable-ssl \\--enable-cgi \\--enable-rewrite \\--with-zlib \\--with-pcre \\--enable-modules=most \\--enable-mpms-shared=all \\--with-mpm=prefork \\--with-included-apr 1make -j 4 && make install   写入PATH变量,并生效1vim /etc/profile.d/httpd.sh 12#!/bin/bashexport PATH=/data/httpd24/bin:$PATH 1source /etc/profile.d/httpd.sh 编译安装php模块方式  先安装依赖包1yum install -y libxml2-devel   在官网下载php最新版php-7.3.10.tar.xz包并解压123wget https://www.php.net/distributions/php-7.3.10.tar.xztar xvf php-7.3.10.tar.xzcd php-7.3.10.tar.xz 1234567891011121314151617./configure --prefix=/data/php/ \\--enable-mysqlnd \\--with-mysqli=mysqlnd \\--with-openssl \\--with-pdo-mysql=mysqlnd \\--enable-mbstring \\--with-freetype-dir \\--with-jpeg-dir \\--with-png-dir \\--with-zlib \\--enable-xml \\--enable-sockets \\--with-apxs2=/data/httpd24/bin/apxs \\--with-config-file-path=/data/php/etc \\--with-config-file-scan-dir=/data/php/etc/php.d \\--enable-maintainer-zts \\--disable-fileinfo   PS:如果apache是之前yum装的,很有可能没有apxs文件,只需要dnf install httpd-devel -y命令安装httpd-devel包,即可生成/usr/bin/apxs工具,如果是以前编译安装的,也要改为正确apxs2对应路径。  编译安装1make -j 4 && make install   复制配置文件模版至配置文件目录1cp php.ini-production /data/php/etc/php.ini   修改apache配置文件,设置默认php页面,增加PHP模块1vim /etc/httpd/conf/httpd.conf 123456789101112131415&ltIfModule dir_module&gt DirectoryIndex index.php index.html #增加php页面&lt/IfModule&gt----&ltIfModule mime_module&gt AddType application/x-compress .Z AddType application/x-gzip .gz .tgz AddType application/x-httpd-php .php #增加模块 AddType application/x-httpd-php-source .phps #增加模块&lt/IfModule&gt   添加php测试页1vim /data/httpd24/htdocs/index.php 123&lt?phpphpinfo() ?&gt   重启apache服务1apachectl restart   访问php测试页进行测试,便可看到php设置已经成功1curl HOSTIP FCGI方式  也要先安装依赖包1yum install libxml2-devel bzip2-devel libmcrypt-devel   同样下载tar包,解压并进入编译目录  开始编译安装123456789101112131415161718./configure --prefix=/data/php \\--enable-mysqlnd \\--with-mysqli=mysqlnd \\--with-pdo-mysql=mysqlnd \\--with-openssl \\--with-freetype-dir \\--with-jpeg-dir \\--with-png-dir \\--with-zlib \\--with-libxml-dir=/usr \\--with-config-file-path=/etc \\--with-config-file-scan-dir=/etc/php.d \\--enable-mbstring \\--enable-xml \\--enable-sockets \\--enable-fpm \\--enable-maintainer-zts \\--disable-fileinfo 1make -j 4 && make install   复制模版文件当配置文件1cp /data/php-7.3.10/php.ini-production /etc/php.ini   因为php-fpm模式相当于单独的一个服务,将服务配置文件放至/usr/lib/systemd/system/目录1cp /data/php-7.3.10/sapi/fpm/php-fpm.service /usr/lib/systemd/system/   生成fpm配置文件,并修改进程属主属组为apache1234cd /data/php/etccp php-fpm.conf.default php-fpm.confcd php-fpm.d/cp www.conf.default www.conf 1234vim www.confuser apachegroup apache   加载配置文件并启动进程12systemctl daemon-reloadsystemctl enable --now php-fpm.service   修改配置httpd.conf 支持php-fpm  取消下面两行的注释12LoadModule proxy_module modules/mod_proxy.soLoadModule proxy_fcgi_module modules/mod_proxy_fcgi.so   修改下面行12345678&ltIfModule dir_module&gtDirectoryIndex index.php index.html&lt/IfModule&gt加下面四行AddType application/x-httpd-php .phpAddType application/x-httpd-php-source .phpsProxyRequests OffProxyPassMatch ^/(.*\\.php)$ fcgi://127.0.0.1:9000/data/httpd24/htdocs/$1   也可修改php监听端口9000 为socket路径,下面代理转发命令为为12ProxyRequests OffProxyPassMatch ^/(.*\\.php)$ unix:/var/run/php-fpm/php-fpm.sock|fcgi://localhost/data/httpd24/htdocs/$1   重启apache服务1apachectl restart 至此,php页面就可以正常访问了","categories":[{"name":"linux进阶","slug":"linux进阶","permalink":"https://wudihechao.github.io/categories/linux进阶/"}],"tags":[{"name":"编译安装","slug":"编译安装","permalink":"https://wudihechao.github.io/tags/编译安装/"},{"name":"LAMP","slug":"LAMP","permalink":"https://wudihechao.github.io/tags/LAMP/"},{"name":"总结","slug":"总结","permalink":"https://wudihechao.github.io/tags/总结/"}],"keywords":[{"name":"linux进阶","slug":"linux进阶","permalink":"https://wudihechao.github.io/categories/linux进阶/"}]},{"title":"LAMP架构实现PowerDNS","slug":"LAMP架构实现PowerDNS","date":"2019-10-14T06:05:20.000Z","updated":"2019-12-04T05:53:24.329Z","comments":true,"path":"/blog/57d66c4.html","link":"","permalink":"https://wudihechao.github.io/blog/57d66c4.html","excerpt":"  PowerDNS 是一个跨平台的开源DNS服务组件,它是高性能的域名服务器,除了支持普通的BIND配置文件,PowerDNS还可以从MySQL,Oracle,PostgreSQL等的数据库读取数据。PowerDNS安装了Poweradmin(基于php实现),能实现Web管理DNS记录,非常方便。","text":"  PowerDNS 是一个跨平台的开源DNS服务组件,它是高性能的域名服务器,除了支持普通的BIND配置文件,PowerDNS还可以从MySQL,Oracle,PostgreSQL等的数据库读取数据。PowerDNS安装了Poweradmin(基于php实现),能实现Web管理DNS记录,非常方便。  ps:本次过程是在centos7系统上完成。 配置安装pdns安装pdns包1yum install -y pdns pdns-backend-mysql 创建pdns数据库  搭建好mariadb数据库了,启动数据库服务,参考官方文档创建powerdns数据库及其中的表,参看下面文档 https://doc.powerdns.com/md/authoritative/backend-generic-mysql/vim powerdns.sql123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293CREATE DATABASE powerdns;GRANT ALL ON powerdns.* TO 'powerdns'@'127.0.0.1' IDENTIFIED BY 'powerdns';use powerdns;CREATE TABLE domains ( id INT AUTO_INCREMENT, name VARCHAR(255) NOT NULL, master VARCHAR(128) DEFAULT NULL, last_check INT DEFAULT NULL, type VARCHAR(6) NOT NULL, notified_serial INT DEFAULT NULL, account VARCHAR(40) DEFAULT NULL, PRIMARY KEY (id)) Engine=InnoDB;CREATE UNIQUE INDEX name_index ON domains(name);CREATE TABLE records ( id BIGINT AUTO_INCREMENT, domain_id INT DEFAULT NULL, name VARCHAR(255) DEFAULT NULL, type VARCHAR(10) DEFAULT NULL, content VARCHAR(64000) DEFAULT NULL, ttl INT DEFAULT NULL, prio INT DEFAULT NULL, change_date INT DEFAULT NULL, disabled TINYINT(1) DEFAULT 0, ordername VARCHAR(255) BINARY DEFAULT NULL, auth TINYINT(1) DEFAULT 1, PRIMARY KEY (id)) Engine=InnoDB;CREATE INDEX nametype_index ON records(name,type);CREATE INDEX domain_id ON records(domain_id);CREATE INDEX recordorder ON records (domain_id, ordername);CREATE TABLE supermasters ( ip VARCHAR(64) NOT NULL, nameserver VARCHAR(255) NOT NULL, account VARCHAR(40) NOT NULL, PRIMARY KEY (ip, nameserver)) Engine=InnoDB;CREATE TABLE comments ( id INT AUTO_INCREMENT, domain_id INT NOT NULL, name VARCHAR(255) NOT NULL, type VARCHAR(10) NOT NULL, modified_at INT NOT NULL, account VARCHAR(40) NOT NULL, comment VARCHAR(64000) NOT NULL, PRIMARY KEY (id)) Engine=InnoDB;CREATE INDEX comments_domain_id_idx ON comments (domain_id);CREATE INDEX comments_name_type_idx ON comments (name, type);CREATE INDEX comments_order_idx ON comments (domain_id, modified_at);CREATE TABLE domainmetadata ( id INT AUTO_INCREMENT, domain_id INT NOT NULL, kind VARCHAR(32), content TEXT, PRIMARY KEY (id)) Engine=InnoDB;CREATE INDEX domainmetadata_idx ON domainmetadata (domain_id, kind);CREATE TABLE cryptokeys ( id INT AUTO_INCREMENT, domain_id INT NOT NULL, flags INT NOT NULL, active BOOL, content TEXT, PRIMARY KEY(id)) Engine=InnoDB;CREATE INDEX domainidindex ON cryptokeys(domain_id);CREATE TABLE tsigkeys ( id INT AUTO_INCREMENT, name VARCHAR(255), algorithm VARCHAR(50), secret VARCHAR(255), PRIMARY KEY (id)) Engine=InnoDB;CREATE UNIQUE INDEX namealgoindex ON tsigkeys(name, algorithm);   以上参数设置为官方文档默认值,如果参数数值大小不对,可自行修改调整。数据库名、主机名、用户名及密码自行修改。  导入SQL语句mysql -uroot -ppassword < power.sql,此时powerdns数据库就创建好了。 配置PowerDNS使用mariadb作为后台数据存储可参见官方文档的设置参数: gmysql-hostHost (ip address) to connect to. Mutually exclusive with gmysql-socket.WARNING: When specified as a hostname a chicken/egg situation might arise where the database is needed to resolve the IP address of the database. It is best to supply an IP address of the database here.gmysql-portThe port to connect to on gmysql-host. Default: 3306gmysql-socketConnect to the UNIX socket at this path. Mutually exclusive with gmysql-host.gmysql-dbnameName of the database to connect to. Default: “pdns”.gmysql-userUser to connect as. Default: “powerdns”.gmysql-groupGroup to connect as. Default: “client”.gmysql-passwordThe password to for gmysql-user.gmysql-dnssecEnable DNSSEC processing for this backend. Default=no.gmysql-innodb-read-committedUse the InnoDB READ-COMMITTED transaction isolation level.Default=yes.gmysql-timeoutThe timeout in seconds for each attempt to read from, or write to the server. A value of 0 will disable the timeout. Default: 10   官方文档第一条写的很清楚,gmysql-localhost那一项最好填ip,或者你的hostname为localhost也可以解析成127.0.0.1,所以虽然我是本机数据库,也选择直接使用127.0.0.1(我试了localhost,提示无法连接数据库,应该是因为我改了hostname导致没法解析成127.0.0.1了,干脆直接用ip就成功连接了) vim /etc/pdns/pdns.conf  用/搜索到”launch=”将值修改为gmysql,并在下面添加mysql的相关信息设置:123456launch=gmysqlgmysql-host=127.0.0.1gmysql-port=3306gmysql-dbname=powerdnsgmysql-user=powerdnsgmysql-password=powerdns   好了pdns服务就配置好了,启动服务.12systemctl start pdnssystemctl enable pdns 搭建LAP架构用web界面实现pdns服务安装httpd和php相关包并启动httpd服务123yum -y install httpd php php-devel php-gd php-mcrypt php-imap php-ldap php-mysql php-odbc php-pear php-xml php-xmlrpc php-mbstring php-mcrypt php-mhash gettextsystemctl start httpdsystemctl enable httpd 下载PowerAdmin程序,并解压缩到相应目录1234wget http://downloads.sourceforge.net/project/poweradmin/poweradmin-2.1.7.tgztar xvf poweradmin-2.1.7.tgz -C /var/www/htmlcd /var/www/htmlmv poweradmin-2.1.7 poweradmin 登陆web界面安装PowerAdmin  浏览器输入http://powerdns服务器IP/poweradmin/install/ 进入安装向导界面。  1.选择语言  2. 确认powerdns服务已连接好数据库,如果之前已经有相关记录,本次安装将会清除之前的数据,时候确定安装。  3.填写数据库相关信息  4.为Poweradmin创建一个受限用户  说明:     Username:PowerAdmin用户名     Password:上述用户的密码     Hostmaster:当创建SOA记录指定默认主机管理员     Primary nameserver:主域名服务器     Secondary namesever:辅域名服务器  5.按照下面页面说明,在数据库中创建用户并授权1234GRANT SELECT, INSERT, UPDATE, DELETEON powerdns.*TO 'powerdns'@'127.0.0.1'IDENTIFIED BY 'powerdns';   6.按下面页面说明,创建config.in.php文件内容  vim /var/www/html/poweradmin/inc/config.inc.php12345678910111213141516&lt?php$db_host = '127.0.0.1';$db_user = 'powerdns';$db_pass = 'powerdns';$db_name = 'powerdns';$db_type = 'mysql';$db_layer = 'PDO';$session_key = 'Kae}LCI!&^Ew6(5eyd6B~!SVHRHhu+t(svkGqb1S{C0}TP';$iface_lang = 'en_EN';$dns_hostmaster = 'powerdns';$dns_ns1 = '192.168.32.7';$dns_ns2 = '192.168.32.7';`   7.设置完成,删除install目录  \\rm -rf install  如果不进行第6、7步就会报错,如上图所示。  8.输入用户名密码登陆  9.进入主页面。至此PowerDNS在web上展示就实现了~","categories":[{"name":"linux进阶","slug":"linux进阶","permalink":"https://wudihechao.github.io/categories/linux进阶/"}],"tags":[{"name":"linux","slug":"linux","permalink":"https://wudihechao.github.io/tags/linux/"},{"name":"LAMP","slug":"LAMP","permalink":"https://wudihechao.github.io/tags/LAMP/"},{"name":"经验分享","slug":"经验分享","permalink":"https://wudihechao.github.io/tags/经验分享/"},{"name":"PowerDNS","slug":"PowerDNS","permalink":"https://wudihechao.github.io/tags/PowerDNS/"}],"keywords":[{"name":"linux进阶","slug":"linux进阶","permalink":"https://wudihechao.github.io/categories/linux进阶/"}]},{"title":"yum/dnf安装mariadb10.4.8(最新版)","slug":"yum-dnf安装mariadb10-4-8(最新版)","date":"2019-10-12T07:44:38.000Z","updated":"2019-12-28T03:54:24.286Z","comments":true,"path":"/blog/72c5296a.html","link":"","permalink":"https://wudihechao.github.io/blog/72c5296a.html","excerpt":"  近期想在新出的CentOS8上安装一下最新版的mariadb10.4.8,不过又嫌源码编译太麻烦费时间。就想去找找有没有yum源可用。","text":"  近期想在新出的CentOS8上安装一下最新版的mariadb10.4.8,不过又嫌源码编译太麻烦费时间。就想去找找有没有yum源可用。  果然官网上已经放出了yum安装的repo源了,也支持到CentOS8了——http://mirror.aarnet.edu.au/pub/MariaDB//mariadb-10.4.8/yum/centos/8/x86_64/  那就手动加一个yum源吧。  vim /etc/yum.repos.d/mariadb1048.repo12345[mariadb10.4.8]name=Mariadb10.4.8 RPM repository for Linux $releasever - $basearchbaseurl=http://mirror.aarnet.edu.au/pub/MariaDB//mariadb-10.4.8/yum/centos/$releasever/$basearch/enabled=1gpgcheck=0   加好yum源后,执行安装dnf install mariadb-server -y123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116aliyun 4.7 kB/s | 5.3 kB 00:01 cdrom-AppStream 4.1 MB/s | 4.3 kB 00:00 cdrom-base 3.8 MB/s | 3.9 kB 00:00 Mariadb10.4.8 RPM repository for Linux 8 - x86_64 1.8 kB/s | 2.9 kB 00:01 Dependencies resolved.===================================================================================================================================== Package Arch Version Repository Size=====================================================================================================================================Installing: mariadb-server x86_64 3:10.3.11-2.module_el8.0.0+35+6f2527ed cdrom-AppStream 16 MInstalling dependencies: mariadb x86_64 3:10.3.11-2.module_el8.0.0+35+6f2527ed cdrom-AppStream 6.2 M mariadb-common x86_64 3:10.3.11-2.module_el8.0.0+35+6f2527ed cdrom-AppStream 62 k mariadb-connector-c x86_64 3.0.7-1.el8 cdrom-AppStream 148 k mariadb-errmsg x86_64 3:10.3.11-2.module_el8.0.0+35+6f2527ed cdrom-AppStream 232 k perl-DBD-MySQL x86_64 4.046-2.module_el8.0.0+72+668237d8 cdrom-AppStream 156 k perl-DBI x86_64 1.641-2.module_el8.0.0+66+fe1eca09 cdrom-AppStream 740 k perl-Digest noarch 1.17-395.el8 cdrom-AppStream 27 k perl-Digest-MD5 x86_64 2.55-396.el8 cdrom-AppStream 37 k perl-Net-SSLeay x86_64 1.85-6.el8 cdrom-AppStream 358 k perl-URI noarch 1.73-3.el8 cdrom-AppStream 116 k perl-libnet noarch 3.11-3.el8 cdrom-AppStream 121 k perl-Carp noarch 1.42-396.el8 cdrom-base 30 k perl-Data-Dumper x86_64 2.167-399.el8 cdrom-base 58 k perl-Encode x86_64 4:2.97-3.el8 cdrom-base 1.5 M perl-Errno x86_64 1.28-416.el8 cdrom-base 76 k perl-Exporter noarch 5.72-396.el8 cdrom-base 34 k perl-File-Path noarch 2.15-2.el8 cdrom-base 38 k perl-File-Temp noarch 0.230.600-1.el8 cdrom-base 63 k perl-Getopt-Long noarch 1:2.50-4.el8 cdrom-base 63 k perl-HTTP-Tiny noarch 0.074-1.el8 cdrom-base 58 k perl-IO x86_64 1.38-416.el8 cdrom-base 141 k perl-MIME-Base64 x86_64 3.15-396.el8 cdrom-base 31 k perl-Math-BigInt noarch 1:1.9998.11-5.el8 cdrom-base 195 k perl-Math-Complex noarch 1.59-416.el8 cdrom-base 108 k perl-PathTools x86_64 3.74-1.el8 cdrom-base 90 k perl-Pod-Escapes noarch 1:1.07-395.el8 cdrom-base 20 k perl-Pod-Perldoc noarch 3.28-396.el8 cdrom-base 86 k perl-Pod-Simple noarch 1:3.35-395.el8 cdrom-base 213 k perl-Pod-Usage noarch 4:1.69-395.el8 cdrom-base 34 k perl-Scalar-List-Utils x86_64 3:1.49-2.el8 cdrom-base 68 k perl-Socket x86_64 4:2.027-2.el8 cdrom-base 59 k perl-Storable x86_64 1:3.11-3.el8 cdrom-base 98 k perl-Term-ANSIColor noarch 4.06-396.el8 cdrom-base 46 k perl-Term-Cap noarch 1.17-395.el8 cdrom-base 23 k perl-Text-ParseWords noarch 3.30-395.el8 cdrom-base 18 k perl-Text-Tabs+Wrap noarch 2013.0523-395.el8 cdrom-base 24 k perl-Time-Local noarch 1:1.280-1.el8 cdrom-base 34 k perl-Unicode-Normalize x86_64 1.25-396.el8 cdrom-base 82 k perl-constant noarch 1.33-396.el8 cdrom-base 25 k perl-interpreter x86_64 4:5.26.3-416.el8 cdrom-base 6.3 M perl-libs x86_64 4:5.26.3-416.el8 cdrom-base 1.6 M perl-macros x86_64 4:5.26.3-416.el8 cdrom-base 72 k perl-parent noarch 1:0.237-1.el8 cdrom-base 20 k perl-podlators noarch 4.11-1.el8 cdrom-base 118 k perl-threads x86_64 1:2.21-2.el8 cdrom-base 61 k perl-threads-shared x86_64 1.58-2.el8 cdrom-base 48 k psmisc x86_64 23.1-3.el8 cdrom-base 151 k MariaDB-client x86_64 10.4.8-1.el8 mariadb10.4.8 12 M MariaDB-common x86_64 10.4.8-1.el8 mariadb10.4.8 87 kInstalling weak dependencies: mariadb-backup x86_64 3:10.3.11-2.module_el8.0.0+35+6f2527ed cdrom-AppStream 6.2 M mariadb-gssapi-server x86_64 3:10.3.11-2.module_el8.0.0+35+6f2527ed cdrom-AppStream 49 k mariadb-server-utils x86_64 3:10.3.11-2.module_el8.0.0+35+6f2527ed cdrom-AppStream 1.6 M perl-IO-Socket-IP noarch 0.39-5.el8 cdrom-AppStream 47 k perl-IO-Socket-SSL noarch 2.060-2.el8 cdrom-AppStream 289 k perl-Mozilla-CA noarch 20160104-7.el8 cdrom-AppStream 15 kTransaction Summary=====================================================================================================================================Install 56 PackagesTotal size: 56 MTotal download size: 12 MInstalled size: 260 MIs this ok [y/N]: yDownloading Packages:(1/2): MariaDB-common-10.4.8-1.el8.x86_64.rpm 35 kB/s | 87 kB 00:02 (2/2): MariaDB-client-10.4.8-1.el8.x86_64.rpm 195 kB/s | 12 MB 01:02 -------------------------------------------------------------------------------------------------------------------------------------Total 197 kB/s | 12 MB 01:02 Running transaction checkTransaction check succeeded.Running transaction testThe downloaded packages were saved in cache until the next successful transaction.You can remove cached packages by executing 'dnf clean packages'.Error: Transaction check error: file /usr/bin/msql2mysql conflicts between attempted installs of mariadb-3:10.3.11-2.module_el8.0.0+35+6f2527ed.x86_64 and MariaDB-client-10.4.8-1.el8.x86_64 file /usr/bin/mysql conflicts between attempted installs of mariadb-3:10.3.11-2.module_el8.0.0+35+6f2527ed.x86_64 and MariaDB-client-10.4.8-1.el8.x86_64 file /usr/bin/mysql_find_rows conflicts between attempted installs of mariadb-3:10.3.11-2.module_el8.0.0+35+6f2527ed.x86_64 and MariaDB-client-10.4.8-1.el8.x86_64 file /usr/bin/mysql_plugin conflicts between attempted installs of mariadb-3:10.3.11-2.module_el8.0.0+35+6f2527ed.x86_64 and MariaDB-client-10.4.8-1.el8.x86_64 file /usr/bin/mysql_waitpid conflicts between attempted installs of mariadb-3:10.3.11-2.module_el8.0.0+35+6f2527ed.x86_64 and MariaDB-client-10.4.8-1.el8.x86_64 file /usr/bin/mysqlaccess conflicts between attempted installs of mariadb-3:10.3.11-2.module_el8.0.0+35+6f2527ed.x86_64 and MariaDB-client-10.4.8-1.el8.x86_64 file /usr/bin/mysqladmin conflicts between attempted installs of mariadb-3:10.3.11-2.module_el8.0.0+35+6f2527ed.x86_64 and MariaDB-client-10.4.8-1.el8.x86_64 file /usr/bin/mysqlbinlog conflicts between attempted installs of mariadb-3:10.3.11-2.module_el8.0.0+35+6f2527ed.x86_64 and MariaDB-client-10.4.8-1.el8.x86_64 file /usr/bin/mysqlcheck conflicts between attempted installs of mariadb-3:10.3.11-2.module_el8.0.0+35+6f2527ed.x86_64 and MariaDB-client-10.4.8-1.el8.x86_64 file /usr/bin/mysqldump conflicts between attempted installs of mariadb-3:10.3.11-2.module_el8.0.0+35+6f2527ed.x86_64 and MariaDB-client-10.4.8-1.el8.x86_64 file /usr/bin/mysqlimport conflicts between attempted installs of mariadb-3:10.3.11-2.module_el8.0.0+35+6f2527ed.x86_64 and MariaDB-client-10.4.8-1.el8.x86_64 file /usr/bin/mysqlshow conflicts between attempted installs of mariadb-3:10.3.11-2.module_el8.0.0+35+6f2527ed.x86_64 and MariaDB-client-10.4.8-1.el8.x86_64 file /usr/bin/mysqlslap conflicts between attempted installs of mariadb-3:10.3.11-2.module_el8.0.0+35+6f2527ed.x86_64 and MariaDB-client-10.4.8-1.el8.x86_64 file /usr/share/man/man1/msql2mysql.1.gz conflicts between attempted installs of mariadb-3:10.3.11-2.module_el8.0.0+35+6f2527ed.x86_64 and MariaDB-client-10.4.8-1.el8.x86_64 file /usr/share/man/man1/mysql.1.gz conflicts between attempted installs of mariadb-3:10.3.11-2.module_el8.0.0+35+6f2527ed.x86_64 and MariaDB-client-10.4.8-1.el8.x86_64 file /usr/share/man/man1/mysql_find_rows.1.gz conflicts between attempted installs of mariadb-3:10.3.11-2.module_el8.0.0+35+6f2527ed.x86_64 and MariaDB-client-10.4.8-1.el8.x86_64 file /usr/share/man/man1/mysql_plugin.1.gz conflicts between attempted installs of mariadb-3:10.3.11-2.module_el8.0.0+35+6f2527ed.x86_64 and MariaDB-client-10.4.8-1.el8.x86_64 file /usr/share/man/man1/mysql_waitpid.1.gz conflicts between attempted installs of mariadb-3:10.3.11-2.module_el8.0.0+35+6f2527ed.x86_64 and MariaDB-client-10.4.8-1.el8.x86_64 file /usr/share/man/man1/mysqlaccess.1.gz conflicts between attempted installs of mariadb-3:10.3.11-2.module_el8.0.0+35+6f2527ed.x86_64 and MariaDB-client-10.4.8-1.el8.x86_64 file /usr/share/man/man1/mysqladmin.1.gz conflicts between attempted installs of mariadb-3:10.3.11-2.module_el8.0.0+35+6f2527ed.x86_64 and MariaDB-client-10.4.8-1.el8.x86_64 file /usr/share/man/man1/mysqlbinlog.1.gz conflicts between attempted installs of mariadb-3:10.3.11-2.module_el8.0.0+35+6f2527ed.x86_64 and MariaDB-client-10.4.8-1.el8.x86_64 file /usr/share/man/man1/mysqlcheck.1.gz conflicts between attempted installs of mariadb-3:10.3.11-2.module_el8.0.0+35+6f2527ed.x86_64 and MariaDB-client-10.4.8-1.el8.x86_64 file /usr/share/man/man1/mysqldump.1.gz conflicts between attempted installs of mariadb-3:10.3.11-2.module_el8.0.0+35+6f2527ed.x86_64 and MariaDB-client-10.4.8-1.el8.x86_64 file /usr/share/man/man1/mysqlimport.1.gz conflicts between attempted installs of mariadb-3:10.3.11-2.module_el8.0.0+35+6f2527ed.x86_64 and MariaDB-client-10.4.8-1.el8.x86_64 file /usr/share/man/man1/mysqlshow.1.gz conflicts between attempted installs of mariadb-3:10.3.11-2.module_el8.0.0+35+6f2527ed.x86_64 and MariaDB-client-10.4.8-1.el8.x86_64 file /usr/share/man/man1/mysqlslap.1.gz conflicts between attempted installs of mariadb-3:10.3.11-2.module_el8.0.0+35+6f2527ed.x86_64 and MariaDB-client-10.4.8-1.el8.x86_64Error Summary-------------   竟然报错了,说是版本冲突。仔细一看,装的是本地光盘的mariadb-sever 3:10.3.11-2.module_el8.0.0+35+6f2527ed.用的是本地yum光盘yum源,看了下mariadb官网上的软件名称叫MariaDB-server-10.4.8-1.el8.x86_64.rpm,那就重新换成大写,dnf install MariaDB-server.还是报错:12345[root@CentOS8 ~]#yum install MariaDB-serverLast metadata expiration check: 0:05:33 ago on Sat 12 Oct 2019 10:19:15 AM CST.No match for argument: MariaDB-server * Maybe you meant: mariadb-serverError: Unable to find a match   那没办法,把本地光盘yum源禁用掉再试试吧12345678910111213141516171819202122232425[root@CentOS8 ~]#vim /etc/yum.repos.d/cdrom.repo [root@CentOS8 ~]#yum install mariadb-serverLast metadata expiration check: 0:13:24 ago on Sat 12 Oct 2019 10:34:12 AM CST.No match for argument: mariadb-server * Maybe you meant: MariaDB-serverError: Unable to find a match[root@CentOS8 ~]#yum install MariaDB-serverLast metadata expiration check: 0:13:36 ago on Sat 12 Oct 2019 10:34:12 AM CST.Error: Problem: cannot install the best candidate for the job - nothing provides rsync needed by MariaDB-server-10.4.8-1.el8.x86_64 - nothing provides perl(strict) needed by MariaDB-server-10.4.8-1.el8.x86_64 - nothing provides /usr/bin/perl needed by MariaDB-server-10.4.8-1.el8.x86_64 - nothing provides perl(vars) needed by MariaDB-server-10.4.8-1.el8.x86_64 - nothing provides perl(Getopt::Long) needed by MariaDB-server-10.4.8-1.el8.x86_64 - nothing provides perl(POSIX) needed by MariaDB-server-10.4.8-1.el8.x86_64 - nothing provides perl(File::Basename) needed by MariaDB-server-10.4.8-1.el8.x86_64 - nothing provides perl(File::Path) needed by MariaDB-server-10.4.8-1.el8.x86_64 - nothing provides perl(Data::Dumper) needed by MariaDB-server-10.4.8-1.el8.x86_64 - nothing provides perl(File::Temp) needed by MariaDB-server-10.4.8-1.el8.x86_64 - nothing provides perl(File::Copy) needed by MariaDB-server-10.4.8-1.el8.x86_64 - nothing provides perl(DBI) needed by MariaDB-server-10.4.8-1.el8.x86_64 - nothing provides perl(Sys::Hostname) needed by MariaDB-server-10.4.8-1.el8.x86_64 - nothing provides lsof needed by MariaDB-server-10.4.8-1.el8.x86_64(try to add '--skip-broken' to skip uninstallable packages or '--nobest' to use not only best candidate packages)   这次倒是不能识别小写mariadb,只能识别大写了MariaDB。不过没有了本地光盘yum源,我这是最小安装的系统,也没法安装依赖了啊。这就陷入两难问题了。如果开启本地光盘yum源,无论后跟版本号或是大写都会提示找不到匹配项,小写的话就会去装mariadb-server3XXX版本,不开启本地光盘yum源的话,就没法装所需依赖了。  突然想起可以直接用包管理工具安装rpm包来解决依赖性的问题,还可以指定要按的主程序包。那就试试直接看:123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245[root@CentOS8 ~]#dnf install http://mirror.aarnet.edu.au/pub/MariaDB//mariadb-10.4.8/yum/centos/8/x86_64/rpms/MariaDB-server-10.4.8-1.el8.x86_64.rpmLast metadata expiration check: 0:00:25 ago on Sat 12 Oct 2019 10:51:11 AM CST.MariaDB-server-10.4.8-1.el8.x86_64.rpm 197 kB/s | 26 MB 02:14 Dependencies resolved.===================================================================================================================================== Package Arch Version Repository Size=====================================================================================================================================Installing: MariaDB-server x86_64 10.4.8-1.el8 @commandline 26 MInstalling dependencies: boost-program-options x86_64 1.66.0-6.el8 cdrom-AppStream 143 k perl-DBI x86_64 1.641-2.module_el8.0.0+66+fe1eca09 cdrom-AppStream 740 k perl-Digest noarch 1.17-395.el8 cdrom-AppStream 27 k perl-Digest-MD5 x86_64 2.55-396.el8 cdrom-AppStream 37 k perl-Net-SSLeay x86_64 1.85-6.el8 cdrom-AppStream 358 k perl-URI noarch 1.73-3.el8 cdrom-AppStream 116 k perl-libnet noarch 3.11-3.el8 cdrom-AppStream 121 k lsof x86_64 4.91-2.el8 cdrom-base 253 k perl-Carp noarch 1.42-396.el8 cdrom-base 30 k perl-Data-Dumper x86_64 2.167-399.el8 cdrom-base 58 k perl-Encode x86_64 4:2.97-3.el8 cdrom-base 1.5 M perl-Errno x86_64 1.28-416.el8 cdrom-base 76 k perl-Exporter noarch 5.72-396.el8 cdrom-base 34 k perl-File-Path noarch 2.15-2.el8 cdrom-base 38 k perl-File-Temp noarch 0.230.600-1.el8 cdrom-base 63 k perl-Getopt-Long noarch 1:2.50-4.el8 cdrom-base 63 k perl-HTTP-Tiny noarch 0.074-1.el8 cdrom-base 58 k perl-IO x86_64 1.38-416.el8 cdrom-base 141 k perl-MIME-Base64 x86_64 3.15-396.el8 cdrom-base 31 k perl-Math-BigInt noarch 1:1.9998.11-5.el8 cdrom-base 195 k perl-Math-Complex noarch 1.59-416.el8 cdrom-base 108 k perl-PathTools x86_64 3.74-1.el8 cdrom-base 90 k perl-Pod-Escapes noarch 1:1.07-395.el8 cdrom-base 20 k perl-Pod-Perldoc noarch 3.28-396.el8 cdrom-base 86 k perl-Pod-Simple noarch 1:3.35-395.el8 cdrom-base 213 k perl-Pod-Usage noarch 4:1.69-395.el8 cdrom-base 34 k perl-Scalar-List-Utils x86_64 3:1.49-2.el8 cdrom-base 68 k perl-Socket x86_64 4:2.027-2.el8 cdrom-base 59 k perl-Storable x86_64 1:3.11-3.el8 cdrom-base 98 k perl-Term-ANSIColor noarch 4.06-396.el8 cdrom-base 46 k perl-Term-Cap noarch 1.17-395.el8 cdrom-base 23 k perl-Text-ParseWords noarch 3.30-395.el8 cdrom-base 18 k perl-Text-Tabs+Wrap noarch 2013.0523-395.el8 cdrom-base 24 k perl-Time-Local noarch 1:1.280-1.el8 cdrom-base 34 k perl-Unicode-Normalize x86_64 1.25-396.el8 cdrom-base 82 k perl-constant noarch 1.33-396.el8 cdrom-base 25 k perl-interpreter x86_64 4:5.26.3-416.el8 cdrom-base 6.3 M perl-libs x86_64 4:5.26.3-416.el8 cdrom-base 1.6 M perl-macros x86_64 4:5.26.3-416.el8 cdrom-base 72 k perl-parent noarch 1:0.237-1.el8 cdrom-base 20 k perl-podlators noarch 4.11-1.el8 cdrom-base 118 k perl-threads x86_64 1:2.21-2.el8 cdrom-base 61 k perl-threads-shared x86_64 1.58-2.el8 cdrom-base 48 k rsync x86_64 3.1.3-4.el8 cdrom-base 404 k MariaDB-client x86_64 10.4.8-1.el8 mariadb10.4.8 12 M MariaDB-common x86_64 10.4.8-1.el8 mariadb10.4.8 87 k galera-4 x86_64 26.4.2-1.rhel8.0.el8 mariadb10.4.8 13 MInstalling weak dependencies: perl-IO-Socket-IP noarch 0.39-5.el8 cdrom-AppStream 47 k perl-IO-Socket-SSL noarch 2.060-2.el8 cdrom-AppStream 289 k perl-Mozilla-CA noarch 20160104-7.el8 cdrom-AppStream 15 kTransaction Summary=====================================================================================================================================Install 51 PackagesTotal size: 65 MTotal download size: 25 MInstalled size: 236 MIs this ok [y/N]: yDownloading Packages:(1/3): MariaDB-common-10.4.8-1.el8.x86_64.rpm 35 kB/s | 87 kB 00:02 (2/3): MariaDB-client-10.4.8-1.el8.x86_64.rpm 203 kB/s | 12 MB 00:59 (3/3): galera-4-26.4.2-1.rhel8.0.el8.x86_64.rpm 198 kB/s | 13 MB 01:08 -------------------------------------------------------------------------------------------------------------------------------------Total 376 kB/s | 25 MB 01:08 Running transaction checkTransaction check succeeded.Running transaction testTransaction test succeeded.Running transaction Preparing : 1/1 Installing : perl-Exporter-5.72-396.el8.noarch 1/51 Installing : perl-libs-4:5.26.3-416.el8.x86_64 2/51 Installing : perl-Carp-1.42-396.el8.noarch 3/51 Installing : perl-Scalar-List-Utils-3:1.49-2.el8.x86_64 4/51 Installing : perl-parent-1:0.237-1.el8.noarch 5/51 Installing : perl-Text-ParseWords-3.30-395.el8.noarch 6/51 Running scriptlet: MariaDB-common-10.4.8-1.el8.x86_64 7/51 Installing : MariaDB-common-10.4.8-1.el8.x86_64 7/51 Running scriptlet: MariaDB-common-10.4.8-1.el8.x86_64 7/51 Installing : perl-Term-ANSIColor-4.06-396.el8.noarch 8/51 Installing : perl-macros-4:5.26.3-416.el8.x86_64 9/51 Installing : perl-Errno-1.28-416.el8.x86_64 10/51 Installing : perl-Socket-4:2.027-2.el8.x86_64 11/51 Installing : perl-Text-Tabs+Wrap-2013.0523-395.el8.noarch 12/51 Installing : perl-Unicode-Normalize-1.25-396.el8.x86_64 13/51 Installing : perl-File-Path-2.15-2.el8.noarch 14/51 Installing : perl-IO-1.38-416.el8.x86_64 15/51 Installing : perl-PathTools-3.74-1.el8.x86_64 16/51 Installing : perl-constant-1.33-396.el8.noarch 17/51 Installing : perl-threads-1:2.21-2.el8.x86_64 18/51 Installing : perl-threads-shared-1.58-2.el8.x86_64 19/51 Installing : perl-interpreter-4:5.26.3-416.el8.x86_64 20/51 Installing : perl-MIME-Base64-3.15-396.el8.x86_64 21/51 Installing : perl-IO-Socket-IP-0.39-5.el8.noarch 22/51 Installing : perl-Data-Dumper-2.167-399.el8.x86_64 23/51 Installing : perl-File-Temp-0.230.600-1.el8.noarch 24/51 Installing : perl-Storable-1:3.11-3.el8.x86_64 25/51 Installing : perl-Time-Local-1:1.280-1.el8.noarch 26/51 Installing : perl-Digest-1.17-395.el8.noarch 27/51 Installing : perl-Digest-MD5-2.55-396.el8.x86_64 28/51 Installing : perl-Net-SSLeay-1.85-6.el8.x86_64 29/51 Installing : perl-Math-Complex-1.59-416.el8.noarch 30/51 Installing : perl-Math-BigInt-1:1.9998.11-5.el8.noarch 31/51 Installing : perl-Pod-Escapes-1:1.07-395.el8.noarch 32/51 Installing : perl-Term-Cap-1.17-395.el8.noarch 33/51 Installing : perl-Mozilla-CA-20160104-7.el8.noarch 34/51 Installing : perl-Encode-4:2.97-3.el8.x86_64 35/51 Installing : perl-Pod-Simple-1:3.35-395.el8.noarch 36/51 Installing : perl-Getopt-Long-1:2.50-4.el8.noarch 37/51 Installing : perl-podlators-4.11-1.el8.noarch 38/51 Installing : perl-Pod-Usage-4:1.69-395.el8.noarch 39/51 Installing : perl-Pod-Perldoc-3.28-396.el8.noarch 40/51 Installing : perl-HTTP-Tiny-0.074-1.el8.noarch 41/51 Installing : perl-IO-Socket-SSL-2.060-2.el8.noarch 42/51 Installing : perl-libnet-3.11-3.el8.noarch 43/51 Installing : perl-URI-1.73-3.el8.noarch 44/51 Installing : perl-DBI-1.641-2.module_el8.0.0+66+fe1eca09.x86_64 45/51 Running scriptlet: MariaDB-client-10.4.8-1.el8.x86_64 46/51 Installing : MariaDB-client-10.4.8-1.el8.x86_64 46/51 Running scriptlet: MariaDB-client-10.4.8-1.el8.x86_64 46/51 Installing : rsync-3.1.3-4.el8.x86_64 47/51 Installing : lsof-4.91-2.el8.x86_64 48/51 Installing : boost-program-options-1.66.0-6.el8.x86_64 49/51 Running scriptlet: boost-program-options-1.66.0-6.el8.x86_64 49/51 Running scriptlet: galera-4-26.4.2-1.rhel8.0.el8.x86_64 50/51 Installing : galera-4-26.4.2-1.rhel8.0.el8.x86_64 50/51 Running scriptlet: galera-4-26.4.2-1.rhel8.0.el8.x86_64 50/51 Running scriptlet: MariaDB-server-10.4.8-1.el8.x86_64 51/51 Installing : MariaDB-server-10.4.8-1.el8.x86_64 51/51 Running scriptlet: MariaDB-server-10.4.8-1.el8.x86_64 51/51 Two all-privilege accounts were created.One is root@localhost, it has no password, but you need tobe system 'root' user to connect. Use, for example, sudo mysqlThe second is mysql@localhost, it has no password either, butyou need to be the system 'mysql' user to connect.After connecting you can set the password, if you would need to beable to connect as any of these users with a password and without sudoSee the MariaDB Knowledgebase at http://mariadb.com/kb or theMySQL manual for more instructions.Please report any problems at http://mariadb.org/jiraThe latest information about MariaDB is available at http://mariadb.org/.You can find additional information about the MySQL part at:http://dev.mysql.comConsider joining MariaDB's strong and vibrant community:https://mariadb.org/get-involved/ Verifying : boost-program-options-1.66.0-6.el8.x86_64 1/51 Verifying : perl-DBI-1.641-2.module_el8.0.0+66+fe1eca09.x86_64 2/51 Verifying : perl-Digest-1.17-395.el8.noarch 3/51 Verifying : perl-Digest-MD5-2.55-396.el8.x86_64 4/51 Verifying : perl-IO-Socket-IP-0.39-5.el8.noarch 5/51 Verifying : perl-IO-Socket-SSL-2.060-2.el8.noarch 6/51 Verifying : perl-Mozilla-CA-20160104-7.el8.noarch 7/51 Verifying : perl-Net-SSLeay-1.85-6.el8.x86_64 8/51 Verifying : perl-URI-1.73-3.el8.noarch 9/51 Verifying : perl-libnet-3.11-3.el8.noarch 10/51 Verifying : lsof-4.91-2.el8.x86_64 11/51 Verifying : perl-Carp-1.42-396.el8.noarch 12/51 Verifying : perl-Data-Dumper-2.167-399.el8.x86_64 13/51 Verifying : perl-Encode-4:2.97-3.el8.x86_64 14/51 Verifying : perl-Errno-1.28-416.el8.x86_64 15/51 Verifying : perl-Exporter-5.72-396.el8.noarch 16/51 Verifying : perl-File-Path-2.15-2.el8.noarch 17/51 Verifying : perl-File-Temp-0.230.600-1.el8.noarch 18/51 Verifying : perl-Getopt-Long-1:2.50-4.el8.noarch 19/51 Verifying : perl-HTTP-Tiny-0.074-1.el8.noarch 20/51 Verifying : perl-IO-1.38-416.el8.x86_64 21/51 Verifying : perl-MIME-Base64-3.15-396.el8.x86_64 22/51 Verifying : perl-Math-BigInt-1:1.9998.11-5.el8.noarch 23/51 Verifying : perl-Math-Complex-1.59-416.el8.noarch 24/51 Verifying : perl-PathTools-3.74-1.el8.x86_64 25/51 Verifying : perl-Pod-Escapes-1:1.07-395.el8.noarch 26/51 Verifying : perl-Pod-Perldoc-3.28-396.el8.noarch 27/51 Verifying : perl-Pod-Simple-1:3.35-395.el8.noarch 28/51 Verifying : perl-Pod-Usage-4:1.69-395.el8.noarch 29/51 Verifying : perl-Scalar-List-Utils-3:1.49-2.el8.x86_64 30/51 Verifying : perl-Socket-4:2.027-2.el8.x86_64 31/51 Verifying : perl-Storable-1:3.11-3.el8.x86_64 32/51 Verifying : perl-Term-ANSIColor-4.06-396.el8.noarch 33/51 Verifying : perl-Term-Cap-1.17-395.el8.noarch 34/51 Verifying : perl-Text-ParseWords-3.30-395.el8.noarch 35/51 Verifying : perl-Text-Tabs+Wrap-2013.0523-395.el8.noarch 36/51 Verifying : perl-Time-Local-1:1.280-1.el8.noarch 37/51 Verifying : perl-Unicode-Normalize-1.25-396.el8.x86_64 38/51 Verifying : perl-constant-1.33-396.el8.noarch 39/51 Verifying : perl-interpreter-4:5.26.3-416.el8.x86_64 40/51 Verifying : perl-libs-4:5.26.3-416.el8.x86_64 41/51 Verifying : perl-macros-4:5.26.3-416.el8.x86_64 42/51 Verifying : perl-parent-1:0.237-1.el8.noarch 43/51 Verifying : perl-podlators-4.11-1.el8.noarch 44/51 Verifying : perl-threads-1:2.21-2.el8.x86_64 45/51 Verifying : perl-threads-shared-1.58-2.el8.x86_64 46/51 Verifying : rsync-3.1.3-4.el8.x86_64 47/51 Verifying : MariaDB-client-10.4.8-1.el8.x86_64 48/51 Verifying : MariaDB-common-10.4.8-1.el8.x86_64 49/51 Verifying : galera-4-26.4.2-1.rhel8.0.el8.x86_64 50/51 Verifying : MariaDB-server-10.4.8-1.el8.x86_64 51/51 Installed: MariaDB-server-10.4.8-1.el8.x86_64 perl-IO-Socket-IP-0.39-5.el8.noarch perl-IO-Socket-SSL-2.060-2.el8.noarch perl-Mozilla-CA-20160104-7.el8.noarch boost-program-options-1.66.0-6.el8.x86_64 perl-DBI-1.641-2.module_el8.0.0+66+fe1eca09.x86_64 perl-Digest-1.17-395.el8.noarch perl-Digest-MD5-2.55-396.el8.x86_64 perl-Net-SSLeay-1.85-6.el8.x86_64 perl-URI-1.73-3.el8.noarch perl-libnet-3.11-3.el8.noarch lsof-4.91-2.el8.x86_64 perl-Carp-1.42-396.el8.noarch perl-Data-Dumper-2.167-399.el8.x86_64 perl-Encode-4:2.97-3.el8.x86_64 perl-Errno-1.28-416.el8.x86_64 perl-Exporter-5.72-396.el8.noarch perl-File-Path-2.15-2.el8.noarch perl-File-Temp-0.230.600-1.el8.noarch perl-Getopt-Long-1:2.50-4.el8.noarch perl-HTTP-Tiny-0.074-1.el8.noarch perl-IO-1.38-416.el8.x86_64 perl-MIME-Base64-3.15-396.el8.x86_64 perl-Math-BigInt-1:1.9998.11-5.el8.noarch perl-Math-Complex-1.59-416.el8.noarch perl-PathTools-3.74-1.el8.x86_64 perl-Pod-Escapes-1:1.07-395.el8.noarch perl-Pod-Perldoc-3.28-396.el8.noarch perl-Pod-Simple-1:3.35-395.el8.noarch perl-Pod-Usage-4:1.69-395.el8.noarch perl-Scalar-List-Utils-3:1.49-2.el8.x86_64 perl-Socket-4:2.027-2.el8.x86_64 perl-Storable-1:3.11-3.el8.x86_64 perl-Term-ANSIColor-4.06-396.el8.noarch perl-Term-Cap-1.17-395.el8.noarch perl-Text-ParseWords-3.30-395.el8.noarch perl-Text-Tabs+Wrap-2013.0523-395.el8.noarch perl-Time-Local-1:1.280-1.el8.noarch perl-Unicode-Normalize-1.25-396.el8.x86_64 perl-constant-1.33-396.el8.noarch perl-interpreter-4:5.26.3-416.el8.x86_64 perl-libs-4:5.26.3-416.el8.x86_64 perl-macros-4:5.26.3-416.el8.x86_64 perl-parent-1:0.237-1.el8.noarch perl-podlators-4.11-1.el8.noarch perl-threads-1:2.21-2.el8.x86_64 perl-threads-shared-1.58-2.el8.x86_64 rsync-3.1.3-4.el8.x86_64 MariaDB-client-10.4.8-1.el8.x86_64 MariaDB-common-10.4.8-1.el8.x86_64 galera-4-26.4.2-1.rhel8.0.el8.x86_64 Complete!   果然成功了。问题解决。","categories":[{"name":"NotOnlySQL","slug":"NotOnlySQL","permalink":"https://wudihechao.github.io/categories/NotOnlySQL/"}],"tags":[{"name":"经验分享","slug":"经验分享","permalink":"https://wudihechao.github.io/tags/经验分享/"},{"name":"mariadb","slug":"mariadb","permalink":"https://wudihechao.github.io/tags/mariadb/"},{"name":"yum源","slug":"yum源","permalink":"https://wudihechao.github.io/tags/yum源/"}],"keywords":[{"name":"NotOnlySQL","slug":"NotOnlySQL","permalink":"https://wudihechao.github.io/categories/NotOnlySQL/"}]},{"title":"自动化运维之Ansible","slug":"自动化运维之Ansible","date":"2019-10-07T13:53:40.000Z","updated":"2019-12-18T07:29:18.691Z","comments":true,"path":"/blog/8a189dcf.html","link":"","permalink":"https://wudihechao.github.io/blog/8a189dcf.html","excerpt":"  ansible是新出现的自动化运维工具,基于Python开发,集合了众多运维工具(puppet、cfengine、chef、func、fabric)的优点,实现了批量系统配置、批量程序部署、批量运行命令等功能。","text":"  ansible是新出现的自动化运维工具,基于Python开发,集合了众多运维工具(puppet、cfengine、chef、func、fabric)的优点,实现了批量系统配置、批量程序部署、批量运行命令等功能。 企业实际应用场景分析 Dev开发环境  使用者:程序员  功能:程序员开发软件,测试BUG的环境  管理者:程序员 测试环境  使用者:QA测试工程师  功能:测试经过Dev环境测试通过的软件的功能  管理者:运维  说明:测试环境往往有多套,测试环境满足测试功能即可,不宜过多  1、测试人员希望测试环境有多套,公司的产品多产品线并发,即多个版本,意味着多个版本同步测试  2、通常测试环境有多少套和产品线数量保持一样 发布环境:代码发布机,有些公司为堡垒机(安全屏障)  使用者:运维  功能:发布代码至生产环境  管理者:运维(有经验)  发布机:往往需要有2台(主备) 生产环境  使用者:运维,少数情况开放权限给核心开发人员,极少数公司将权限完全开放给开发人员并其维护  功能:对用户提供公司产品的服务  管理者:只能是运维  生产环境服务器数量:一般比较多,且应用非常重要。往往需要自动工具协助部署配置应用 灰度环境(生产环境的一部分)  使用者:运维  功能:在全量发布代码前将代码的功能面向少量精准用户发布的环境,可基于主机或用户执行灰度发布  案例:共100台生产服务器,先发布其中的10台服务器,这10台服务器就是灰度服务器  管理者:运维  灰度环境:往往该版本功能变更较大,为保险起见特意先让一部分用户优化体验该功能,待这部分用户使用没有重大问题的时候,再全量发布至所有服务器程序发布 程序发布要求:  不能导致系统故障或造成系统完全不可用  不能影响用户体验 预发布验证:  新版本的代码先发布到服务器(跟线上环境配置完全相同,只是未接入到调度器) 灰度发布:  基于主机,用户,业务 发布路径:  /webapp/tuangou  /webapp/tuangou-1.1  /webapp/tuangou-1.2 发布过程:在调度器上下线一批主机(标记为maintenance 状态) –> 关闭服务 –> 部署新版本的应用程序 –> 启动服务 –> 在调度器上启用这一批服务器 自动化灰度发布:脚本、发布平台自动化运维应用场景 文件传输 应用部署 配置管理 任务流编排常用自动化运维工具 Ansible:python,Agentless,中小型应用环境 Saltstack:python,一般需部署agent,执行效率更高 Puppet:ruby, 功能强大,配置复杂,重型,适合大型环境 Fabric:python,agentless Chef:ruby,国内应用少 Cfengine funcAnsible简介 var ap = new APlayer({ element: document.getElementById(\"aplayer-yhSmusci\"), narrow: false, autoplay: true, showlrc: false, music: { title: \"Coming Home\", author: \"Peter Jeremias\", url: \"https://hewanyue.com/mp3/ComingHome.mp3\", pic: \"https://hewanyue.com/mp3/cover/ComingHome.jpg\", lrc: \"\" } }); window.aplayers || (window.aplayers = []); window.aplayers.push(ap); Ansible特性 模块化:调用特定的模块,完成特定任务 Paramiko(python对ssh的实现),PyYAML,Jinja2(模板语言)三个关键模块 支持自定义模块 基于Python语言实现 部署简单,基于python和SSH(默认已安装),agentless 安全,基于OpenSSH 支持playbook编排任务 幂等性:一个任务执行1遍和执行n遍效果一样,不因重复执行带来意外情况 无需代理不依赖PKI(无需ssl) 可使用任何编程语言写模块 YAML格式,编排任务,支持丰富的数据结构 较强大的多层解决方案Ansible架构Ansible工作原理Ansible主要组成部分 ANSIBLE PLAYBOOKS:任务剧本(任务集),编排定义Ansible任务集的配置文件,由Ansible顺序依次执行,通常是JSON格式的YML文件 INVENTORY:Ansible管理主机的清单/etc/anaible/hosts MODULES:Ansible执行命令的功能模块,多数为内置核心模块,也可自定义 PLUGINS:模块功能的补充,如连接类型插件、循环插件、变量插件、过滤插件等,该功能不常用 API:供第三方程序调用的应用程序编程接口 ANSIBLE:组合INVENTORY、API、MODULES、PLUGINS的绿框,可以理解为是ansible命令工具,其为核心执行工具Ansible命令执行来源: USER,普通用户,即SYSTEM ADMINISTRATOR CMDB(配置管理数据库) API 调用 PUBLIC/PRIVATE CLOUD API调用 USER-> Ansible Playbook -> Ansibile利用ansible实现管理的方式: Ad-Hoc 即ansible命令,主要用于临时命令使用场景 Ansible-playbook 主要用于长期规划好的,大型项目的场景,需要有前期的规划过程 Ansible-playbook(剧本)执行过程 将已有编排好的任务集写入Ansible-Playbook 通过ansible-playbook命令分拆任务集至逐条ansible命令,按预定规则逐条执行Ansible主要操作对象 HOSTS主机 NETWORKING网络设备注意事项  执行ansible的主机一般称为主控端,中控,master或堡垒机  主控端Python版本需要2.6或以上  被控端Python版本小于2.4需要安装python-simplejson  被控端如开启SELinux需要安装libselinux-python  windows不能做为主控端 安装Ansiblerpm包安装: EPEL源yum install ansible 编译安装:12345678yum -y install python-jinja2 PyYAML python-paramiko python-babel python-cryptowget https://releases.ansible.com/ansible/ansible-1.5.4.tar.gztar xf ansible-1.5.4.tar.gzcd ansible-1.5.4python setup.py buildpython setup.py installmkdir /etc/ansiblecp -r examples/* /etc/ansible Git方式: 123git clone git://github.com/ansible/ansible.git --recursivecd ./ansiblesource ./hacking/env-setup pip安装: pip是安装Python包的管理器,类似yum 1234yum install python-pip python-develyum install gcc glibc-devel zibl-devel rpm-bulid openssl-develpip install --upgrade pippip install ansible --upgrade 确认安装: ansible --version 配置Ansible配置文件  /etc/ansible/ansible.cfg 主配置文件,配置ansible工作特性(一般保持默认)12345678910111213[defaults]#inventory = /etc/ansible/hosts # 主机列表配置文件#library = /usr/share/my_modules/ # 库文件存放目录#remote_tmp = $HOME/.ansible/tmp #临时py命令文件存放在远程主机目录#local_tmp = $HOME/.ansible/tmp # 本机的临时命令执行目录#forks = 5 # 默认并发数#sudo_user = root # 默认sudo 用户#ask_sudo_pass = True #每次执行ansible命令是否询问ssh密码#ask_pass = True#remote_port = 22#host_key_checking = False # 检查对应服务器的host_key,建议取消注释#log_path=/var/log/ansible.log #日志文件#module_name = command #默认模块   /etc/ansible/hosts Inventory 主机清单  ansible的主要功用在于批量主机操作,为了便捷地使用其中的部分主机,可以在inventory file中将其分组命名 默认的inventory file为/etc/ansible/hosts inventory file可以有多个,且也可以通过Dynamic Inventory来动态生成文件格式 inventory文件遵循INI文件风格,中括号中的字符为组名。可以将同一个主机同时归并到多个不同的组中;此外,当如若目标主机使用了非默认的SSH端口,还可以在主机名称之后使用冒号加端口号来标明ntp.servers.com[webservers]www1.webservers.com:2222www2.webservers.com[dbservers]db1.dbservers.comdb2.dbservers.comdb3.dbservers.com主机清单inventory 如果主机名称遵循相似的命名模式,还可以使用列表的方式标识各主机 示例:[websrvs]www[1:100].example.com[dbsrvs]db-[a:f].example.com   /etc/ansible/roles/ 存放角色的目录 程序  /usr/bin/ansible 主程序,临时命令执行工具  /usr/bin/ansible-doc 查看配置文档,模块功能查看工具  /usr/bin/ansible-galaxy 下载/上传优秀代码或Roles模块的官网平台  /usr/bin/ansible-playbook 定制自动化任务,编排剧本工具  /usr/bin/ansible-pull 远程执行命令的工具  /usr/bin/ansible-vault 文件加密工具  /usr/bin/ansible-console 基于Console界面与用户交互的执行工具 Ansible命令 ansible ansible-doc ansible-playbook ansible-vault ansible-console ansible-galaxy ansible-pullansible-doc: 显示模块帮助ansible-doc [options] [module...]  -l, –list 列出可用模块  -s, –snippet显示指定模块的playbook片段示例:  ansible-doc -l 列出所有模块  ansible-doc ping 查看指定模块帮助用法  ansible-doc -s ping 查看指定模块帮助用法ansible  ansible通过ssh实现配置管理、应用部署、任务执行等功能,建议配置ansible端能基于密钥认证的方式联系各被管理节点ansible &lthost-pattern&gt [-m module_name] [-a args]ansible选项  –version 显示版本  -m module 指定模块,默认为command  -v 详细过程 –vv -vvv更详细  –list-hosts 显示主机列表,可简写 –list  -k, –ask-pass 提示输入ssh连接密码,默认Key验证  -C, –check 检查,并不执行  -T, –timeout=TIMEOUT 执行命令的超时时间,默认10s  -u, –user=REMOTE_USER 执行远程执行的用户  -b, –become 代替旧版的sudo 切换  –become-user=USERNAME 指定sudo的runas用户,默认为root  -K, –ask-become-pass 提示输入sudo时的口令 ansible的Host-pattern匹配主机的列表 All :表示所有Inventory中的所有主机ansible all –m ping * :通配符ansible "*" -m pingansible 192.168.1.* -m pingansible "*srvs" -m ping 或关系ansible "websrvs:appsrvs" -m pingansible "192.168.1.10:192.168.1.20" -m ping 逻辑与ansible "websrvs:&dbsrvs" –m ping在websrvs组并且在dbsrvs组中的主机 逻辑非ansible 'websrvs:!dbsrvs' –m ping在websrvs组,但不在dbsrvs组中的主机注意:此处为单引号 综合逻辑ansible 'websrvs:dbsrvs:&appsrvs:!ftpsrvs' –m ping 正则表达式ansible "websrvs:&dbsrvs" –m pingansible "~(web|db).*.servers.com" –m pingansible常用模块模块文档:https://docs.ansible.com/ansible/latest/modules/modules_by_category.html Command:在远程主机执行命令,默认模块,可忽略-m选项ansible srvs -m command -a 'service vsftpd start'ansible srvs -m command -a 'echo passwd |passwd --stdin user'此命令不支持 $VARNAME &lt &gt | ; & 等,须用shell模块实现 Shell:和command相似,用shell执行命令ansible srv -m shell -a ‘echo passwd |passwd –stdin user’  调用bash执行命令 类似cat /tmp/stanley.md | awk -F'|' '{print $1,$2}'&> /tmp/example.txt这些复杂命令,即使使用shell也可能会失败,解决办法:写到脚本时,copy到远程,执行,再把需要的结果拉回执行命令的机器 Script:在远程主机上运行ansible服务器上的脚本ansible &lthost-pattern&gt -m script -a "/PATH/TO/SCRIPT_FILE"ansible websrvs -m script -a /data/test.sh Copy:从主控端复制文件到远程主机ansible srv -m copy -a "src=/root/test1.sh dest=/tmp/test2.sh owner=wang mode=600 backup=yes"如目标存在,默认覆盖,此处指定先备份ansible srv -m copy -a "content='test content\\n' dest=/tmp/test.txt"指定内容,直接生成目标文件 Fetch:从远程主机提取文件至主控端,copy相反,目前不支持目录ansible srv -m fetch -a 'src=/root/test.sh dest=/data/scripts' File:设置文件属性ansible srv -m file -a "path=/root/test.sh owner=wang mode=755"ansible srv -m file -a "path=/data/testdir state=directory"ansible srv -m file -a 'src=/data/testfile dest=/data/testfile-link state=link' unarchive:解包解压缩,有两种用法:1、将ansible主机上的压缩包传到远程主机后解压缩至特定目录,设置copy=yes.2、将远程主机上的某个压缩包解压缩到指定路径下,设置copy=no常见参数:copy:默认为yes,当copy=yes,拷贝的文件是从ansible主机复制到远程主机上,如果设置为copy=no,会在远程主机上寻找src源文件src:源路径,可以是ansible主机上的路径,也可以是远程主机上的路径,如果是远程主机上的路径,则需要设置copy=nodest:远程主机上的目标路径mode:设置解压缩后的文件权限示例:ansible srv -m unarchive -a 'src=/data/foo.tgz dest=/var/lib/foo'ansible srv -m unarchive -a 'src=/tmp/foo.zip dest=/data copy=no mode=0777'ansible srv -m unarchive -a 'src=https://example.com/example.zip dest=/data copy=no' Archive:打包压缩ansible all -m archive -a 'path=/etc/sysconfig dest=/data/sysconfig.tar.bz2 format=bz2 owner=wang mode=0777'-Hostname:管理主机名ansible node1 -m hostname -a "name=websrv" Cron:计划任务支持时间:minute,hour,day,month,weekdayansible srv -m cron -a "minute=*/5 job='/usr/sbin/ntpdate 172.16.0.1 &>/dev/null' name=Synctime"创建任务ansible srv -m cron -a 'state=absent name=Synctime'删除任务 Yum:管理包ansible srv -m yum -a 'name=httpd state=present'安装ansible srv -m yum -a 'name=httpd state=absent'删除ansible常用模块 Service:管理服务ansible srv -m service -a 'name=httpd state=stopped'ansible srv -m service -a 'name=httpd state=started enabled=yes'ansible srv -m service -a 'name=httpd state=reloaded'ansible srv -m service -a 'name=httpd state=restarted' User:管理用户ansible srv -m user -a 'name=user1 comment="test user" uid=2048 home=/app/user1 group=root'ansible srv -m user -a 'name=sysuser1 system=yes home=/app/sysuser1'ansible srv -m user -a 'name=user1 state=absent remove=yes'删除用户及家目录等数据 Group:管理组ansible srv -m group -a "name=testgroup system=yes"ansible srv -m group -a "name=testgroup state=absent"ansible命令执行过程  1. 加载自己的配置文件 默认/etc/ansible/ansible.cfg  2. 加载自己对应的模块文件,如command  3. 通过ansible将模块或命令生成对应的临时py文件,并将该文件传输至远程服务器的对应执行用户$HOME/.ansible/tmp/ansible-tmp-数字/XXX.PY文件  4. 给文件+x执行  5. 执行并返回结果  6. 删除临时py文件,退出  执行状态:    绿色:执行成功并且不需要做改变的操作    黄色:执行成功并且对目标主机做变更    红色:执行失败ansible-galaxy  连接 https://galaxy.ansible.com 下载相应的rolesansible-galaxy  列出所有已安装的galaxyansible-galaxy list  安装galaxyansible-galaxy install geerlingguy.redis  删除galaxyansible-galaxy remove geerlingguy.redisansible-pull  推送命令至远程,效率无限提升,对运维要求较高ansible-playbook  执行playbook   示例:ansible-playbook hello.yml123456#hello world yml file- hosts: websrvs remote_user: root tasks: - name: hello world command: /usr/bin/wall hello world ansible-vault  功能:管理加密解密yml文件ansible-vault [create|decrypt|edit|encrypt|rekey|view]ansible-vault encrypt hello.yml 加密ansible-vault decrypt hello.yml 解密ansible-vault view hello.yml 查看ansible-vault edit hello.yml 编辑加密文件ansible-vault rekey hello.yml 修改口令ansible-vault create new.yml 创建新文件 Ansible-console  ansible终端。2.0+新增,可交互执行命令,支持tabroot@test (2)[f:10] $ 执行用户@当前操作的主机组 (当前组的主机数量)[f:并发数]$ 设置并发数: forks n 例如: forks 10 切换组: cd 主机组 例如: cd web 列出当前组主机列表: list 列出所有的内置命令: ?或help 示例:root@all (2)[f:5]$ listroot@all (2)[f:5]$ cd appsrvsroot@appsrvs (2)[f:5]$ listroot@appsrvs (2)[f:5]$ yum name=httpd state=presentroot@appsrvs (2)[f:5]$ service name=httpd state=startedplaybook playbook是由一个或多个“play”组成的列表 play的主要功能在于将预定义的一组主机,装扮成事先通过ansible中的task定义好的角色。Task实际是调用ansible的一个module,将多个play组织在一个playbook中,即可以让它们联合起来,按事先编排的机制执行预定义的动作 Playbook采用YAML语言编写 playbook核心元素 Hosts 执行的远程主机列表 Tasks 任务集 Variables 内置变量或自定义变量在playbook中调用 Templates 模板,可替换模板文件中的变量并实现一些简单逻辑的文件 Handlers 和 notify 结合使用,由特定条件触发的操作,满足条件方才执行,否则不执行 tags 标签 指定某条任务执行,用于选择运行playbook中的部分代码。ansible具有幂等性,因此会自动跳过没有变化的部分,即便如此,有些代码为测试其确实没有发生变化的时间依然会非常地长。此时,如果确信其没有变化,就可以通过tags跳过此些代码片断ansible-playbook -t tagsname useradd.ymlplaybook基础组件 Hosts:  playbook中的每一个play的目的都是为了让特定主机以某个指定的用户身份执行任务。hosts用于指定要执行指定任务的主机,须事先定义在主机清单中。可以是如下形式:one.example.comone.example.com:two.example.com192.168.1.50192.168.1.*Websrvs:dbsrvs 或者,两个组的并集Websrvs:&dbsrvs 与,两个组的交集webservers:!phoenix 在websrvs组,但不在dbsrvs组示例: - hosts: websrvs:dbsrvs remote_user  remote_user: 可用于Host和task中。也可以通过指定其通过sudo的方式在远程主机上执行任务,其可用于play全局或某任务;此外,甚至可以在sudo时使用sudo_user指定sudo时切换的用户 12345678- hosts: websrvs remote_user: root tasks: - name: test connection ping: remote_user: user sudo: yes 默认sudo为root sudo_user: wang sudo为wang task列表和action  play的主体部分是task list,task list中的各任务按次序逐个在hosts中指定的所有主机上执行,即在所有主机上完成第一个任务后,再开始第二个任务  task的目的是使用指定的参数执行模块,而在模块参数中可以使用变量。模块执行是幂等的,这意味着多次执行是安全的,因为其结果均一致  每个task都应该有其name,用于playbook的执行结果输出,建议其内容能清晰地描述任务执行步骤。如果未提供name,则action的结果将用于输出playbook基础组件  tasks:任务列表  两种格式:(1) action: module arguments(2) module: arguments 建议使用  注意:shell和command模块后面跟命令,而非key=value  某任务的状态在运行后为changed时,可通过“notify”通知给相应的handlers  任务可以通过”tags“打标签,可在ansible-playbook命令上使用-t指定进行调用示例: 123tasks: - name: disable selinux command: /sbin/setenforce 0   如果命令或脚本的退出码不为零,可以使用如下方式替代 123tasks: - name: run this command and ignore the result shell: /usr/bin/somecommand || /bin/true   或者使用ignore_errors来忽略错误信息 1234tasks: - name: run this command and ignore the result shell: /usr/bin/somecommand ignore_errors: True 运行playbook 运行playbook的方式ansible-playbook &ltfilename.yml&gt ... [options] 常见选项–check -C 只检测可能会发生的改变,但不真正执行操作–list-hosts 列出运行任务的主机–list-tags 列出tag–list-tasks 列出task–limit 主机列表 只针对主机列表中的主机执行-v -vv -vvv 显示过程 示例ansible-playbook file.yml --check 只检测ansible-playbook file.ymlansible-playbook file.yml --limit websrvs  可以对比下SHELL脚本VSPlaybook123456789#!/bin/bash# 安装Apacheyum install --quiet -y httpd# 复制配置文件cp /tmp/httpd.conf /etc/httpd/conf/httpd.confcp /tmp/vhosts.conf /etc/httpd/conf.d/# 启动Apache,并设置开机启动service httpd startchkconfig httpd on 1234567891011- hosts: all remote_user: root tasks: - name: \"安装Apache\" yum: name=httpd - name: \"复制配置文件\" copy: src=/tmp/httpd.conf dest=/etc/httpd/conf/ - name: \"复制配置文件\" copy: src=/tmp/vhosts.conf dest=/etc/httpd/conf.d/ - name: \"启动Apache,并设置开机启动\" service: name=httpd state=started enabled=yes 示例:sysuser.yml1234567- hosts: all remote_user: root tasks: - name: create mysql user user: name=mysql system=yes uid=36 - name: create a group group: name=httpd system=yes 示例:httpd.yml123456789- hosts: websrvs remote_user: root tasks: - name: Install httpd yum: name=httpd state=present - name: Install configure file copy: src=files/httpd.conf dest=/etc/httpd/conf/ - name: start service service: name=httpd state=started enabled=yes handlers和notify结合使用触发条件 Handlers是task列表,这些task与前述的task并没有本质上的不同,用于当关注的资源发生变化时,才会采取一定的操作 Notify此action可用于在每个play的最后被触发,这样可避免多次有改变发生时每次都执行指定的操作,仅在所有的变化发生完成后一次性地执行指定操作。在notify中列出的操作称为handler,也即notify中调用handler中定义的操作Playbook中handlers使用12345678910111213- hosts: websrvs remote_user: root tasks: - name: Install httpd yum: name=httpd state=present - name: Install configure file copy: src=files/httpd.conf dest=/etc/httpd/conf/ notify: restart httpd - name: ensure apache is running service: name=httpd state=started enabled=yes handlers: - name: restart httpd service: name=httpd state=restarted 示例1234567891011121314151617181920- hosts: websrvs remote_user: root tasks: - name: add group nginx tags: user user: name=nginx state=present - name: add user nginx user: name=nginx state=present group=nginx - name: Install Nginx yum: name=nginx state=present - name: config copy: src=/root/config.txt dest=/etc/nginx/nginx.conf notify: - Restart Nginx - Check Nginx Process handlers: - name: Restart Nginx service: name=nginx state=restarted enabled=yes - name: Check Nginx process shell: killall -0 nginx &> /tmp/nginx.log Playbook中tags使用示例:httpd.yml1234567891011- hosts: websrvs remote_user: root tasks: - name: Install httpd yum: name=httpd state=present - name: Install configure file copy: src=files/httpd.conf dest=/etc/httpd/conf/ tags: conf - name: start httpd service tags: service service: name=httpd state=started enabled=yes ansible-playbook –t conf httpd.yml Playbook中变量使用变量名:  仅能由字母、数字和下划线组成,且只能以字母开头 变量来源: 1 ansible setup facts 远程主机的所有变量都可直接调用 2 在/etc/ansible/hosts中定义普通变量:主机组中主机单独定义,优先级高于公共变量公共(组)变量:针对主机组中所有主机定义统一变量 3 通过命令行指定变量,优先级最高ansible-playbook –e varname=value 4 在playbook中定义 123vars: - var1: value1 - var2: value2 5 在独立的变量YAML文件中定义 6 在role中定义 变量命名:   变量名仅能由字母、数字和下划线组成,且只能以字母开头 变量定义:  key=value示例:http_port=80 变量调用方式: 通过 调用变量,且变量名前后必须有空格,有时用“”才生效 ansible-playbook –e 选项指定ansible-playbook test.yml -e "hosts=www user=servers"示例:使用setup变量示例:var.yml12345- hosts: websrvs remote_user: root tasks: - name: create log file file: name=/var/log/ {{ ansible_fqdn }} state=touch ansible-playbook var.yml示例:var.yml12345- hosts: websrvs remote_user: root tasks: - name: install package yum: name={{ pkname }} state=present ansible-playbook –e pkname=httpd var.yml 示例:var.yml12345678910- hosts: websrvs remote_user: root vars: - username: user1 - groupname: group1 tasks: - name: create group group: name={{ groupname }} state=present - name: create user user: name={{ username }} state=present ansible-playbook var.ymlansible-playbook -e "username=user2 groupname=group2” var2.yml 变量来源: 主机变量  可以在inventory中定义主机时为其添加主机变量以便于在playbook中使用示例:[websrvs]www1.servers.com http_port=80 maxRequestsPerChild=808www2.servers.com http_port=8080 maxRequestsPerChild=909 组变量  组变量是指赋予给指定组内所有主机上的在playbook中可用的变量示例:[websrvs]www1.servers.comwww2.servers.com[websrvs:vars]ntp_server=ntp.servers.comnfs_server=nfs.servers.com示例:变量 普通变量[websrvs]192.168.99.101 http_port=8080 hname=www1192.168.99.102 http_port=80 hname=www2 公共(组)变量[websvrs:vars]http_port=808mark=“-”[websrvs]192.168.99.101 http_port=8080 hname=www1192.168.99.102 http_port=80 hname=www2ansible websvrs –m hostname –a ‘name=’ 命令行指定变量:ansible websvrs –e http_port=8000 –m hostname –a'name=' 使用变量文件cat vars.yml12var1: httpdvar2: nginx cat var.yml123456789- hosts: web remote_user: root vars_files: - vars.yml tasks: - name: create httpd log file: name=/app/{{ var1 }}.log state=touch - name: create nginx log file: name=/app/{{ var2 }}.log state=touch 模板template 文本文件,嵌套有脚本(使用模板编程语言编写) Jinja2语言,使用字面量,有下面形式字符串:使用单引号或双引号数字:整数,浮点数列表:[item1, item2, …]元组:(item1, item2, …)字典:{key1:value1, key2:value2, …}布尔型:true/false 算术运算:+, -, *, /, //, %, ** 比较操作:==, !=, >, >=, <, <= 逻辑运算:and,or,not 流表达式:For,If,When template功能:根据模块文件动态生成对应的配置文件 template文件必须存放于templates目录下,且命名为 .j2 结尾 yaml/yml 文件需和templates目录平级,目录结构如下:./├── temnginx.yml└── templates└── nginx.conf.j2 template示例示例:利用template 同步nginx配置文件 准备templates/nginx.conf.j2文件vim temnginx.yml12345- hosts: websrvs remote_user: root tasks: - name: template config to remote hosts template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf ansible-playbook temnginx.yml Playbook中template变更替换修改文件nginx.conf.j2 下面行为worker_processes ;cat temnginx2.yml12345- hosts: websrvs remote_user: root tasks: - name: template config to remote hosts template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf ansible-playbook temnginx2.yml Playbook中template算术运算示例:vim nginx.conf.j212worker_processes {{ ansible_processor_vcpus**2 }};worker_processes {{ ansible_processor_vcpus+2 }}; template条件判断when  条件测试:如果需要根据变量、facts或此前任务的执行结果来做为某task执行与否的前提时要用到条件测试,通过when语句实现,在task中使用,jinja2的语法格式when语句  在task后添加when子句即可使用条件测试;when语句支持Jinja2表达式语法示例:1234tasks: - name: \"shutdown RedHat flavored systems\" command: /sbin/shutdown -h now when: ansible_os_family == \"RedHat\" 示例:when条件判断12345678910111213- hosts: websrvs remote_user: root tasks: - name: add group nginx tags: user user: name=nginx state=present - name: add user nginx user: name=nginx state=present group=nginx - name: Install Nginx yum: name=nginx state=present - name: restart Nginx service: name=nginx state=restarted when: ansible_distribution_major_version == “6” 示例:when条件判断1234567tasks: - name: install conf file to centos7 template: src=nginx.conf.c7.j2 dest=/etc/nginx/nginx.conf when: ansible_distribution_major_version == \"7\" - name: install conf file to centos6 template: src=nginx.conf.c6.j2 dest=/etc/nginx/nginx.conf when: ansible_distribution_major_version == \"6\" 迭代:with_items迭代:当有需要重复性执行的任务时,可以使用迭代机制 对迭代项的引用,固定变量名为”item“ 要在task中使用with_items给定要迭代的元素列表 列表格式:  字符串  字典示例:12345- name: add several users user: name={{ item }} state=present groups=wheel with_items: - testuser1 - testuser2   上面语句的功能等同于下面的语句:1234- name: add user testuser1 user: name=testuser1 state=present groups=wheel- name: add user testuser2 user: name=testuser2 state=present groups=wheel 示例:将多个文件进行copy到被控端12345678- hosts: testsrv remote_user: root tasks: - name: Create rsyncd config copy: src={{ item }} dest=/etc/{{ item }} with_items: - rsyncd.secrets - rsyncd.conf 123456789101112131415- hosts: websrvs remote_user: root tasks: - name: copy file copy: src={{ item }} dest=/tmp/{{ item }} with_items: - file1 - file2 - file3 - name: yum install httpd yum: name={{ item }} state=present with_items: - apr - apr-util - httpd 123456789- hosts: websrvs remote_user: root tasks: - name: install some packages yum: name={{ item }} state=present with_items: - nginx - memcached - php-fpm 示例:迭代嵌套子变量123456789101112131415- hosts:websrvs remote_user: root tasks: - name: add some groups group: name={{ item }} state=present with_items: - group1 - group2 - group3 - name: add some users user: name={{ item.name }} group={{ item.group }} state=present with_items: - { name: 'user1', group: 'group1' } - { name: 'user2', group: 'group2' } - { name: 'user3', group: 'group3' } Playbook中template for if12345678910{% for vhost in nginx_vhosts %}server {listen {{ vhost.listen | default('80 default_server') }};{% if vhost.server_name is defined %}server_name {{ vhost.server_name }};{% endif %}{% if vhost.root is defined %}root {{ vhost.root }};{% endif %}{% endfor %} roles  ansible自1.2版本引入的新特性,用于层次性、结构化地组织playbook。roles能够根据层次型结构自动装载变量文件、tasks以及handlers等。要使用roles只需要在playbook中使用include指令即可。简单来讲,roles就是通过分别将变量、文件、任务、模板及处理器放置于单独的目录中,并可以便捷地include它们的一种机制。角色一般用于基于主机构建服务的场景中,但也可以是用于构建守护进程等场景中。  复杂场景:建议使用roles,代码复用度高 变更指定主机或主机组 如命名不规范维护和传承成本大 某些功能需多个Playbook,通过includes即可实现roles角色(roles):角色集合roles/ mysql/ httpd/ nginx/ memcached/Ansible Roles目录编排roles目录结构  每个角色,以特定的层级目录结构进行组织roles目录结构:playbook.yml roles/ project/ tasks/ files/ vars/ templates/ handlers/ default/ 不常用 meta/ 不常用Roles各目录作用 /roles/project/ :项目名称,有以下子目录 files/ :存放由copy或script模块等调用的文件 templates/:template模块查找所需要模板文件的目录 tasks/:定义task,role的基本元素,至少应该包含一个名为main.yml的文件;其它的文件需要在此文件中通过include进行包含 handlers/:至少应该包含一个名为main.yml的文件;其它的文件需要在此文件中通过include进行包含 vars/:定义变量,至少应该包含一个名为main.yml的文件;其它的文件需要在此文件中通过include进行包含 meta/:定义当前角色的特殊设定及其依赖关系,至少应该包含一个名为main.yml的文件,其它文件需在此文件中通过include进行包含 default/:设定默认变量时使用此目录中的main.yml文件创建role创建role的步骤(1) 创建以roles命名的目录(2) 在roles目录中分别创建以各角色名称命名的目录,如webservers等(3) 在每个角色命名的目录中分别创建files、handlers、meta、tasks、templates和vars目录;用不到的目录可以创建为空目录,也可以不创建(4) 在playbook文件中,调用各角色针对大型项目使用Roles进行编排roles目录结构:playbook.ymlroles/ project/  tasks/  files/  vars/  templates/  handlers/  default/ # 不经常用  meta/ # 不经常用示例:nginx-role.ymlroles/└── nginx├── files│ └── main.yml├── tasks│ ├── groupadd.yml│ ├── install.yml│ ├── main.yml│ ├── restart.yml│ └── useradd.yml└── vars└── main.ymlplaybook调用角色调用角色方法1:123456- hosts: websrvs remote_user: root roles: - mysql - memcached - nginx 调用角色方法2:  传递变量给角色12345- hosts: remote_user: roles: - mysql - { role: nginx, username: nginx }    键role用于指定角色名称   后续的k/v用于传递变量给角色 调用角色方法3:  还可基于条件测试实现角色调用12roles: - { role: nginx, username: nginx, when: ansible_distribution_major_version == ‘7’ } roles playbook tags使用 roles playbook tags使用ansible-playbook --tags="nginx,httpd,mysql" nginx-role.yml123456789// nginx-role.yml- hosts: testweb remote_user: root roles: - { role: nginx ,tags: [ 'nginx', 'web' ] ,when: ansible_distribution_major_version == \"6“ } - { role: httpd ,tags: [ 'httpd', 'web' ] } - { role: mysql ,tags: [ 'mysql', 'db' ] } - { role: marridb ,tags: [ 'mysql', 'db' ] } - { role: php }","categories":[{"name":"linux进阶","slug":"linux进阶","permalink":"https://wudihechao.github.io/categories/linux进阶/"}],"tags":[{"name":"linux","slug":"linux","permalink":"https://wudihechao.github.io/tags/linux/"},{"name":"总结","slug":"总结","permalink":"https://wudihechao.github.io/tags/总结/"},{"name":"Ansible","slug":"Ansible","permalink":"https://wudihechao.github.io/tags/Ansible/"},{"name":"YAML","slug":"YAML","permalink":"https://wudihechao.github.io/tags/YAML/"}],"keywords":[{"name":"linux进阶","slug":"linux进阶","permalink":"https://wudihechao.github.io/categories/linux进阶/"}]},{"title":"mysql数据库MHA实现高可用(多实例间实现)","slug":"mysql数据库MHA实现高可用(多实例间实现)","date":"2019-10-05T01:15:31.000Z","updated":"2019-12-25T13:16:25.884Z","comments":true,"path":"/blog/108203a7.html","link":"","permalink":"https://wudihechao.github.io/blog/108203a7.html","excerpt":"   MHA(master high availability)目前是MySQL高可用方面是一个相对成熟的解决方案。在切换过程中,mha能做到0-30s内自动完成数据库的切换,并且在切换过程中最大的保持数据的一致性,以达到真正意义上的高可用    MHA的组成:    MHA Manager(管理节点)和MHA Node(数据节点)。MHA Manager可以独立部署在一台独立的机器上,管理多个集群,也可以部署在从从库上。    当Master出现故障的时候,它可以自动将最新的数据的Slave提升为新的Master,然后将所有的Slave重新指向新的Master,整个故障转移过程是完全透明的。","text":"   MHA(master high availability)目前是MySQL高可用方面是一个相对成熟的解决方案。在切换过程中,mha能做到0-30s内自动完成数据库的切换,并且在切换过程中最大的保持数据的一致性,以达到真正意义上的高可用    MHA的组成:    MHA Manager(管理节点)和MHA Node(数据节点)。MHA Manager可以独立部署在一台独立的机器上,管理多个集群,也可以部署在从从库上。    当Master出现故障的时候,它可以自动将最新的数据的Slave提升为新的Master,然后将所有的Slave重新指向新的Master,整个故障转移过程是完全透明的。   实验演示图如下:   操作如下:   1、在192.168.32.71主机和192.168.32.72主机上搭建好1主3从的主从配置服务器;   2、在192.168.32.7主机作为监控主机,安装mhamanager包和node包,192.168.32.71主机、192.168.32.72主机只安装node包;   3、将三台主机的ssh公钥信息互相储存(或公用一个公钥私钥对),设置好相互之间的基于key验证免密登录。(因为用的是多实例数据库,SSH连通性验证时还会检查各个数据库之间的连通性,这就包括3307、3308、3309端口数据库之间的SSH连接,所以一定要在192.168.32.72主机家目录的.ssh/authorized_keys文件中加入192.168.32.72本机的公钥,否则会报错,切记,已踩坑)   4、编写mha配置文件,路径随意。vim /etc/mha.cnf1234567891011121314151617181920212223[server default]user=mhauserpassword=mhusermanager_workdir=/data/mastermha/group1/manager_log=/data/mastermha/group1/manager.logremote_workdir=/data/mastermha/group1/ssh_user=rootrepl_user=repluserrepl_password=repluserping_interval=1master_binlog_dir=/data/mysql[server1]hostname=192.168.32.71[server2]hostname=192.168.32.72port=3307candidate_master=1[server3]hostname=192.168.32.72port=3308[server4]hostname=192.168.32.72port=3309    5、在主数据库服务器也就是192.168.32.71主机上创建监控账号(user=mhauser password=mhauser)和复制帐号(repl_user=repluser repl_password=repluser)(要和配置文件相同,已有则无需创建,可以用现成的,权限符合即可)12GRANT ALL ON *.* TO mhauser@'192.168.32.7%' IDENTIFIED BY 'mhauser';GRANT REPLICATION SLAVE ON *.* TO 'repluser'@'192.168.32.7%' IDENTIFIED BY 'repluser'    6、用MHA包自带工具进行测试usr/bin/masterha_check_ssh --conf=/etc/mha.cnf12345678910111213141516171819[root@CentOS7 ~]#/usr/bin/masterha_check_ssh --conf=/etc/mha.cnfFri Sep 27 11:17:25 2019 - [warning] Global configuration file /etc/masterha_default.cnf not found. Skipping.Fri Sep 27 11:17:25 2019 - [info] Reading application default configuration from /etc/mha.cnf..Fri Sep 27 11:17:25 2019 - [info] Reading server configuration from /etc/mha.cnf..Fri Sep 27 11:17:25 2019 - [info] Starting SSH connection tests..Fri Sep 27 11:17:28 2019 - [debug] Fri Sep 27 11:17:25 2019 - [debug] Connecting via SSH from [email protected](192.168.32.71:22) to [email protected](192.168.32.72:22)..Fri Sep 27 11:17:25 2019 - [debug] ok.Fri Sep 27 11:17:25 2019 - [debug] Connecting via SSH from [email protected](192.168.32.71:22) to [email protected](192.168.32.72:22)..Fri Sep 27 11:17:26 2019 - [debug] ok.Fri Sep 27 11:17:26 2019 - [debug] Connecting via SSH from [email protected](192.168.32.71:22) to [email protected](192.168.32.72:22)..Fri Sep 27 11:17:27 2019 - [debug] ok.Fri Sep 27 11:17:28 2019 - [debug] Fri Sep 27 11:17:26 2019 - [debug] Connecting via SSH from [email protected](192.168.32.72:22) to [email protected](192.168.32.71:22)..Fri Sep 27 11:17:27 2019 - [debug] ok.Fri Sep 27 11:17:27 2019 - [debug] Connecting via SSH from [email protected](192.168.32.72:22) to [email protected](192.168.32.72:22)..Fri Sep 27 11:17:28 2019 - [debug] ok.Fri Sep 27 11:17:28 2019 - [debug] Connecting via SSH from [email protected](192.168.32.72:22) to [email protected](192.168.32.72:22)..Fri Sep 27 11:17:29 2019 - [info] All SSH connection tests passed successfully. usr/bin/masterha_check_repl --conf=/etc/mha.cnf123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687[root@CentOS7 ~]#/usr/bin/masterha_check_repl --conf=/etc/mha.cnfFri Sep 27 11:18:12 2019 - [warning] Global configuration file /etc/masterha_default.cnf not found. Skipping.Fri Sep 27 11:18:12 2019 - [info] Reading application default configuration from /etc/mha.cnf..Fri Sep 27 11:18:12 2019 - [info] Reading server configuration from /etc/mha.cnf..Fri Sep 27 11:18:12 2019 - [info] MHA::MasterMonitor version 0.56.Fri Sep 27 11:18:13 2019 - [info] GTID failover mode = 0Fri Sep 27 11:18:13 2019 - [info] Dead Servers:Fri Sep 27 11:18:13 2019 - [info] Alive Servers:Fri Sep 27 11:18:13 2019 - [info] 192.168.32.71(192.168.32.71:3306)Fri Sep 27 11:18:13 2019 - [info] 192.168.32.72(192.168.32.72:3307)Fri Sep 27 11:18:13 2019 - [info] 192.168.32.72(192.168.32.72:3308)Fri Sep 27 11:18:13 2019 - [info] 192.168.32.72(192.168.32.72:3309)Fri Sep 27 11:18:13 2019 - [info] Alive Slaves:Fri Sep 27 11:18:13 2019 - [info] 192.168.32.72(192.168.32.72:3307) Version=10.4.8-MariaDB-log (oldest major version between slaves) log-bin:enabledFri Sep 27 11:18:13 2019 - [info] Replicating from 192.168.32.71(192.168.32.71:3306)Fri Sep 27 11:18:13 2019 - [info] Primary candidate for the new Master (candidate_master is set)Fri Sep 27 11:18:13 2019 - [info] 192.168.32.72(192.168.32.72:3308) Version=10.4.8-MariaDB-log (oldest major version between slaves) log-bin:enabledFri Sep 27 11:18:13 2019 - [info] Replicating from 192.168.32.71(192.168.32.71:3306)Fri Sep 27 11:18:13 2019 - [info] 192.168.32.72(192.168.32.72:3309) Version=10.4.8-MariaDB-log (oldest major version between slaves) log-bin:enabledFri Sep 27 11:18:13 2019 - [info] Replicating from 192.168.32.71(192.168.32.71:3306)Fri Sep 27 11:18:13 2019 - [info] Current Alive Master: 192.168.32.71(192.168.32.71:3306)Fri Sep 27 11:18:13 2019 - [info] Checking slave configurations..Fri Sep 27 11:18:13 2019 - [info] Checking replication filtering settings..Fri Sep 27 11:18:13 2019 - [info] binlog_do_db= , binlog_ignore_db= Fri Sep 27 11:18:13 2019 - [info] Replication filtering check ok.Fri Sep 27 11:18:13 2019 - [info] GTID (with auto-pos) is not supportedFri Sep 27 11:18:13 2019 - [info] Starting SSH connection tests..Fri Sep 27 11:18:17 2019 - [info] All SSH connection tests passed successfully.Fri Sep 27 11:18:17 2019 - [info] Checking MHA Node version..Fri Sep 27 11:18:18 2019 - [info] Version check ok.Fri Sep 27 11:18:18 2019 - [info] Checking SSH publickey authentication settings on the current master..Fri Sep 27 11:18:18 2019 - [info] HealthCheck: SSH to 192.168.32.71 is reachable.Fri Sep 27 11:18:18 2019 - [info] Master MHA Node version is 0.56.Fri Sep 27 11:18:18 2019 - [info] Checking recovery script configurations on 192.168.32.71(192.168.32.71:3306)..Fri Sep 27 11:18:18 2019 - [info] Executing command: save_binary_logs --command=test --start_pos=4 --binlog_dir=/data/mysql --output_file=/data/mastermha/group1//save_binary_logs_test --manager_version=0.56 --start_file=master-bin.000012 Fri Sep 27 11:18:18 2019 - [info] Connecting to [email protected](192.168.32.71:22).. Creating /data/mastermha/group1 if not exists.. ok. Checking output directory is accessible or not.. ok. Binlog found at /data/mysql, up to master-bin.000012Fri Sep 27 11:18:19 2019 - [info] Binlog setting check done.Fri Sep 27 11:18:19 2019 - [info] Checking SSH publickey authentication and checking recovery script configurations on all alive slave servers..Fri Sep 27 11:18:19 2019 - [info] Executing command : apply_diff_relay_logs --command=test --slave_user='mhauser' --slave_host=192.168.32.72 --slave_ip=192.168.32.72 --slave_port=3307 --workdir=/data/mastermha/group1/ --target_version=10.4.8-MariaDB-log --manager_version=0.56 --relay_log_info=/data/mysql3307/data/relay-log.info --relay_dir=/data/mysql3307/data/ --slave_pass=xxxFri Sep 27 11:18:19 2019 - [info] Connecting to [email protected](192.168.32.72:22).. Checking slave recovery environment settings.. Opening /data/mysql3307/data/relay-log.info ... ok. Relay log found at /data/mysql3307/data, up to relay-log.000034 Temporary relay log file is /data/mysql3307/data/relay-log.000034 Testing mysql connection and privileges.. done. Testing mysqlbinlog output.. done. Cleaning up test file(s).. done.Fri Sep 27 11:18:19 2019 - [info] Executing command : apply_diff_relay_logs --command=test --slave_user='mhauser' --slave_host=192.168.32.72 --slave_ip=192.168.32.72 --slave_port=3308 --workdir=/data/mastermha/group1/ --target_version=10.4.8-MariaDB-log --manager_version=0.56 --relay_log_info=/data/mysql3308/data/relay-log.info --relay_dir=/data/mysql3308/data/ --slave_pass=xxxFri Sep 27 11:18:19 2019 - [info] Connecting to [email protected](192.168.32.72:22).. Checking slave recovery environment settings.. Opening /data/mysql3308/data/relay-log.info ... ok. Relay log found at /data/mysql3308/data, up to relay-log.000009 Temporary relay log file is /data/mysql3308/data/relay-log.000009 Testing mysql connection and privileges.. done. Testing mysqlbinlog output.. done. Cleaning up test file(s).. done.Fri Sep 27 11:18:20 2019 - [info] Executing command : apply_diff_relay_logs --command=test --slave_user='mhauser' --slave_host=192.168.32.72 --slave_ip=192.168.32.72 --slave_port=3309 --workdir=/data/mastermha/group1/ --target_version=10.4.8-MariaDB-log --manager_version=0.56 --relay_log_info=/data/mysql3309/data/relay-log.info --relay_dir=/data/mysql3309/data/ --slave_pass=xxxFri Sep 27 11:18:20 2019 - [info] Connecting to [email protected](192.168.32.72:22).. Checking slave recovery environment settings.. Opening /data/mysql3309/data/relay-log.info ... ok. Relay log found at /data/mysql3309/data, up to relay-log.000009 Temporary relay log file is /data/mysql3309/data/relay-log.000009 Testing mysql connection and privileges.. done. Testing mysqlbinlog output.. done. Cleaning up test file(s).. done.Fri Sep 27 11:18:20 2019 - [info] Slaves settings check done.Fri Sep 27 11:18:20 2019 - [info] 192.168.32.71(192.168.32.71:3306) (current master) +--192.168.32.72(192.168.32.72:3307) +--192.168.32.72(192.168.32.72:3308) +--192.168.32.72(192.168.32.72:3309)Fri Sep 27 11:18:20 2019 - [info] Checking replication health on 192.168.32.72..Fri Sep 27 11:18:20 2019 - [info] ok.Fri Sep 27 11:18:20 2019 - [info] Checking replication health on 192.168.32.72..Fri Sep 27 11:18:20 2019 - [info] ok.Fri Sep 27 11:18:20 2019 - [info] Checking replication health on 192.168.32.72..Fri Sep 27 11:18:20 2019 - [info] ok.Fri Sep 27 11:18:20 2019 - [warning] master_ip_failover_script is not defined.Fri Sep 27 11:18:20 2019 - [warning] shutdown_script is not defined.Fri Sep 27 11:18:20 2019 - [info] Got exit code 0 (Not master dead).MySQL Replication Health is OK.    均显示测试成功~   可以启动MHAmanager了,注意这是一个前端程序,如果检测到主数据库宕机,则会自动切换从数据为新的主服务器并使其他的从服务器从新的主服务器数据库上面复制数据,此后程序自动终止。1234[root@CentOS7 ~]#/usr/bin/masterha_manager --conf=/etc/mha.cnfFri Sep 27 11:35:37 2019 - [warning] Global configuration file /etc/masterha_default.cnf not found. Skipping.Fri Sep 27 11:35:37 2019 - [info] Reading application default configuration from /etc/mha.cnf..Fri Sep 27 11:35:37 2019 - [info] Reading server configuration from /etc/mha.cnf..    在主数据库上将mysql进程杀掉后,MHA进程自动结束。   在3307端口数据库上查询,已经没有从节点信息了。   在3308端口数据库以及3309端口数据上显示,主数据库已变为更为3307端口数据库,说明转换主从结构成功!   PS:因为几个数据库都是源码编译安装,数据库路径和二进制文件储存位置都做出了调整,与默认位置不同,而yum安装的MHAmaster中如果配置文件中不写明master_binlog_dir的位置,则会默认去/usr/bin/msyql/目录下找主机二进制文件(当然,mysql数据库也是yum安装的可忽略这些配置,一切默认就好,而配置文件中添加server主机时,如果端口号不是默认3306,一定要记得写明port端口号,如本文中多实例用的不同端口,标记清楚即可。)","categories":[{"name":"NotOnlySQL","slug":"NotOnlySQL","permalink":"https://wudihechao.github.io/categories/NotOnlySQL/"}],"tags":[{"name":"多实例","slug":"多实例","permalink":"https://wudihechao.github.io/tags/多实例/"},{"name":"mysql","slug":"mysql","permalink":"https://wudihechao.github.io/tags/mysql/"},{"name":"MHA","slug":"MHA","permalink":"https://wudihechao.github.io/tags/MHA/"},{"name":"高可用","slug":"高可用","permalink":"https://wudihechao.github.io/tags/高可用/"},{"name":"mariadb","slug":"mariadb","permalink":"https://wudihechao.github.io/tags/mariadb/"}],"keywords":[{"name":"NotOnlySQL","slug":"NotOnlySQL","permalink":"https://wudihechao.github.io/categories/NotOnlySQL/"}]},{"title":"mysql多实例实现主从级联复制及读写分离","slug":"mysql多实例实现主从级联复制及读写分离","date":"2019-09-25T03:21:43.000Z","updated":"2019-12-04T05:57:51.951Z","comments":true,"path":"/blog/d006fe9f.html","link":"","permalink":"https://wudihechao.github.io/blog/d006fe9f.html","excerpt":"   MySQL多实例就是在一台机器上开启多个不同的服务端口(如:3306,3307),运行多个MySQL服务进程,通过不同的socket监听不同的服>务端口来提供各自的服务,本文基于此,来展示如何实现级联复制和读写分离。","text":"   MySQL多实例就是在一台机器上开启多个不同的服务端口(如:3306,3307),运行多个MySQL服务进程,通过不同的socket监听不同的服>务端口来提供各自的服务,本文基于此,来展示如何实现级联复制和读写分离。一、多实例 1、概述    MySQL多实例就是在一台机器上开启多个不同的服务端口(如:3306,3307),运行多个MySQL服务进程,通过不同的socket监听不同的服务端口来提供各自的服务. 2.1、优点 1)有效利用服务器资源    当单个服务器资源过剩时,可以充分利用剩余的资源来提供更多的服务;2)节约服务器资源    当公司资金紧张,但数据库又需要数据库之间各自提供服务时,并且还想使用主从同步等技术,此时多实例就再好不过了;3)方便后期架构扩展    当公司的某个项目才启动时,启动初期并不一定有很大的用户量,因此可以先用一组物理数据库服务器,在上面部署多个实例,方便后续架构扩展、迁移; 2.2、缺点 资源互相抢占问题    当某个服务实例并发很高或者有慢查询时,整个实例会消耗更多的内存、CPU和IO资源,这将导致服务器上的其它实例提供服务的质量下降。这就比如说合租房的各个租客,每当早晨上班时,都会洗漱,此时卫生间的占用率就大,各个租客总会发生等待。 3、部署mysql多实例的两种方式 ① 基于多配置文件    通过使用多个配置文件来启动不同的进程,以此来实现多实例。    优点:逻辑简单,配置简单    缺点:管理起来不方便 ② 基于mysqld_multi    通过官方自带的 mysqld_multi 工具,使用单独配置文件来实现多实例    优点: 便于集中管理管理    缺点: 不方便针对每个实例配置进行定制 4、同一开发环境下安装两个数据库,必须处理以下问题    (1) 配置文件安装路径不能相同    (2)数据库目录不能相同    (3)启动脚本不能同名    (4)端口不能相同    (5)socket文件的生成路径不能相同5、配置搭建   实现目标:   1、源码编译安装或者二进制安装或者yum安装mysql或mariadb客户端以及server端,若没有创建mysql用户、组则创建。   2、在指定路径下创建3个数据库目录  mkdir -p /data/mysql330{789}  挂载在不同的三块硬盘上123mount /dev/sdb /data/mysql3307mount /dev/sdc /data/mysql3308mount /dev/sdd /data/mysql3309    3、用server端带的初始化脚本生成三个数据库123scripts/mariadb-install-db --datadir=/data/mysql3307/ --user=mysqlscripts/mariadb-install-db --datadir=/data/mysql3308/ --user=mysqlscripts/mariadb-install-db --datadir=/data/mysql3309/ --user=mysql    4、准备三个配置文件 123cp /etc/my.cnf /data/mysql3307/my.cnfcp /etc/my.cnf /data/mysql3308/my.cnfcp /etc/my.cnf /data/mysql3309/my.cnf    修改分别其中配置(以下以实例3307为例,3308、3309将下面所有数字对应改成自己的端口号)12345678910111213141516171819[client]#password = your_passwordport = 3307socket = /data/mysql3307/mysql.sock[mysqld]datadir=/data/mysql3307/innodb_file_per-table=onskip_name_resolve=onport = 3307socket = /data/mysql3307/mysql.socklog-bin=mysql-binlog-slave-updatesserver-id = 3307 #每个数据库server-id全局唯一default-character-set=utf8read_only=ON #3307特有选项-开启中继日志relay_log=relay-log #3307特有选项relay_log_index=relay-log.index #3307特有选项    5、编写服务启动脚本(并加执行权限和修改PATH路径)   vim mysqld 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657#!/bin/bashport=$1mysql_user=\"root\"mysql_pwd=\"password\"cmd_path=\"/usr/local/mysql/bin\"mysql_basedir=\"/data/mysql\"mysql_sock=\"${mysql_basedir}${port}/mysql.sock\"function_start_mysql(){ if [ ! -e \"$mysql_sock\" ];then printf \"Starting MySQL...\\n\" ${cmd_path}/mysqld_safe --defaults-file=${mysql_basedir}${port}/my.cnf &> /dev/null & else printf \"MySQL is running...\\n\" exit fi}function_stop_mysql(){ if [ ! -e \"$mysql_sock\" ];then printf \"MySQL is stopped...\\n\" exit else printf \"Stoping MySQL...\\n\" ${cmd_path}/mysqladmin -u ${mysql_user} -p${mysql_pwd} -S ${mysql_sock} shutdown fi}function_restart_mysql(){ printf \"Restarting MySQL...\\n\" function_stop_mysql sleep 1 function_start_mysql}if [ ! -n \"$2\" ];then printf \"Usage: ${mysql_basedir}${port}/bin/mysqld [PORT] {start|stop|restart}\\n\"else case $2 in start) function_start_mysql ;; stop) function_stop_mysql ;; restart) function_restart_mysql ;; *) printf \"Usage: ${mysql_basedir}${port}/bin/mysqld {start|stop|restart}\\n\" esacfi    6、现在就可以实现执行mysqld 3307 start启动数据库服务了。启动三台数据库后(可运行安全加固脚本去掉test数据库以及多余账户),在主机数据库及这3个数据库中分别增加主从配置。   192.168.32.71主机数据库:12MariaDB [(none)]> GRANT REPLICATION SLAVE ON *.* TO 'repluser'@'192.168.32.72' IDENTIFIED BY 'replpasswd';MariaDB [(none)]> SHOW MASTER STATUS; MariaDB [(none)]> show master status;+———————-+—————–+——————-+————————-+|    File     |  Position  | Binlog_Do_DB | Binlog_Ignore_DB |+———————-+—————–+——————-+————————-+| mysql-bin.000026 |   1136   |        |          |+———————-+—————–+——————-+————————-+    查看并记录主机当前二进制日志文件信息及位置。   用mysqldump (或者物理备份),并还原至3个从节点。   192.168.32.71主机: 12mysqldump -uroot -ppassword -A --default-character-set=utf8 --master-data=1 --hex-blob >/data/fullbak_`date +%F`.sqlscp /data/fullbak_`date +%F`.sql 192.168.32.72:/data/    192.168.32.72主机:    启动3个实例的服务   mysqld 3307 start;mysqld 3308 start;mysqld 3309 start    连接3307实例   mysql -S /data/mysql3307/mysql.sock 12MariaDB [(none)]> source /data/fullbak_XXX.sqlMariaDB [(none)]> SHOW MASTER STATUS; MariaDB [(none)]> show master status;+———————–+—————-+——————-+————————-+|    File     |  Position  | Binlog_Do_DB | Binlog_Ignore_DB |+———————–+—————-+——————-+————————-+| mysql-bin.000006 |   1167   |        |          |+———————–+—————-+——————-+————————-+    记录3307实例的当前二进制日志文件信息及位置。    在从节点3307添加主节点配置信息。123456MariaDB [(none)]> change master to master_host='192.168.32.71',master_user='repluser',master_password='replpasswd',master_port=3306,master_log_file='mysql-bin.000026',master_log_pos=1136;MariaDB [(none)]>start slave;    连接3308实例mysql -S /data/mysql3308/mysql.sock   在从节点3308添加主节点配置信息。1234567MariaDB [(none)]> source /data/fullbak_XXX.sqlMariaDB [(none)]> change master to master_host='192.168.32.72,master_user='repluser',master_password='replpasswd',master_port=3307,master_log_file='mysql-bin.000006',master_log_pos=1167;MariaDB [(none)]>start slave;    连接3309实例mysql -S /data/mysql3309/mysql.sock   在从节点3309添加主节点配置信息。1234567MariaDB [(none)]> source /data/fullbak_XXX.sqlMariaDB [(none)]> change master to master_host='192.168.32.72,master_user='repluser',master_password='replpasswd',master_port=3307,master_log_file='mysql-bin.000006',master_log_pos=1167;MariaDB [(none)]>start slave;    三个从节点数据库依次  show slave status\\G查看主从状态信息,确认主从配置无误且均已启动,至此,多实例主从级联配置就完成了。  二、读写分离1、什么是读写分离?    读写分离,基本的原理是让主数据库处理事务性增、改、删操作(INSERT、UPDATE、DELETE),而从数据库处理SELECT查询操作。数据库复制被用来把事务性操作导致的变更同步到集群中的从数据库。 2、为什么要实现读写分离?    因为数据库的“写”(写10000条数据到oracle可能要3分钟)操作是比较耗时的。   但是数据库的“读”(从oracle读10000条数据可能只要5秒钟)。所以读写分离,解决的是,数据库的写入,影响了查询的效率。 3、什么时候要读写分离?    数据库不一定要读写分离,如果程序使用数据库较多时,而更新少,查询多的情况下会考虑使用,利用数据库 主从同步 。可以减少数据库压力,提高性能。当然,数据库也有其它优化方案。memcache 或是 表折分,或是搜索引擎。都是解决方法。 4、部署读写分离   本次使用proxy实现读写分离,最终实现下图所示   1、部署代理机ip:192.168.32.70   安装proxy及mysql客户端123456789cat /etc/yum.repos.d/proxysql.repo <<EOF [proxysql_repo]name= ProxySQL YUM repositorybaseurl=http://repo.proxysql.com/ProxySQL/proxysql-1.4.x/centos/\\$releasevergpgcheck=1gpgkey=http://repo.proxysql.com/ProxySQL/repo_pub_keyEOFyum install proxysql -yyum install mysql -y    修改代理机配置文件端口号 6033 改为 3306(方便客户机连接,其他保持默认)   sed -ri ‘s@(interfaces=”0.0.0.0:)6033@\\13306@’ /etc/proxysql.cnf    登陆proxysql数据库   mysql -uadmin -padmin -P6032 -h127.0.0.1    在代理服务器上增加主机信息    MySQL [(none)]>1234insert into mysql_servers(hostgroup_id,hostname,port) values(10,'192.168.32.71',3306),(10,'192.168.32.72',3307),(10,'192.168.32.72',3308),(10,'192.168.32.72',3309);    加载:MySQL [(none)]> load mysql servers to runtime   保存:MySQL [(none)]> save mysql servers to disk    #设置监控帐号(使用默认监控账号(账号:密码=monitor:monitor)的话,此步可跳过)   MySQL [(none)]>1234set mysql-monitor_username='username'set mysql-monitor_password='password'MySQL [(none)]> load mysql variables to runtime;MySQL [(none)]> save mysql variables to disk;    2、在主服务器上增加创建proxy的监控帐号并授权(从节点会自动复制创建,无需再创)    MySQL [(none)]> grant replication client on . to monitor@’192.168.32.71’identified by ‘monitor’;    3、在代理机上查看监控连接是否正常的 (对connect指标的监控):   如果connect_error的结果为NULL则表示正常   MySQL>select from mysql_server_connect_log;   查看监控心跳信息 (对ping指标的监控):   MySQL> select from mysql_server_ping_log;   (如果显示连接失败,可尝试在从节点刷新权限flush privileges;)   4、设置分组信息   需要修改的是main库中的mysql_replication_hostgroups表,该表有3个字段:writer_hostgroup,reader_hostgroup,comment, 指定写组的id为10,读组的id为20   MySQL>insert into mysql_replication_hostgroups values(10,20,”CentOS7”);   将mysql_replication_hostgroups表的修改加载到RUNTIME生效   MySQL> load mysql servers to runtime;   MySQL> save mysql servers to disk;   Monitor模块监控后端的read_only值,按照read_only的值将节点自动移动到读/写组  查看添加的主机分组表,应该已经自动分组了:   MySQL>select hostgroup_id,hostname,port,status,weight from mysql_servers;   5、在proxysql上配置路由规则,实现读写分离   与规则有关的表:mysql_query_rules和mysql_query_rules_fast_routing,后者是前者的扩展表,1.4.7之后支持   插入路由规则:将select语句分离到20的读组,select语句中有一个特殊语句   SELECT…FOR UPDATE它会申请写锁,应路由到10的写组   MySQL>insert into mysql_query_rules(rule_id,active,match_digest,destination_hostgroup,apply) VALUES(1,1,’^SELECT.*FOR UPDATE$’,10,1),(2,1,’^SELECT’,20,1);   MySQL> load mysql query rules to runtime;   MySQL> save mysql query rules to disk;注意:因ProxySQL根据rule_id顺序进行规则匹配,select … for update规则的rule_id必须要小于普通的select规则的rule_id   在代理机上用命令测试相应读写的是哪个数据库:  mysql -uroot -pPASSWORD -P3306 -h127.0.0.1 -e ‘start transaction;select @@server_id;commit;select @@server_id’+———————+| @@server_id  |+———————+|     3306    |+———————++———————+| @@server_id  |+———————+|     3307     |+———————+  说明读写分离,在两个终端上成功实现~","categories":[{"name":"NotOnlySQL","slug":"NotOnlySQL","permalink":"https://wudihechao.github.io/categories/NotOnlySQL/"}],"tags":[{"name":"多实例","slug":"多实例","permalink":"https://wudihechao.github.io/tags/多实例/"},{"name":"mysql","slug":"mysql","permalink":"https://wudihechao.github.io/tags/mysql/"},{"name":"级联复制","slug":"级联复制","permalink":"https://wudihechao.github.io/tags/级联复制/"},{"name":"读写分离","slug":"读写分离","permalink":"https://wudihechao.github.io/tags/读写分离/"}],"keywords":[{"name":"NotOnlySQL","slug":"NotOnlySQL","permalink":"https://wudihechao.github.io/categories/NotOnlySQL/"}]},{"title":"关于mariadb10.4.8二进制安装及源码编译后设置密码无效的一些发现","slug":"关于mariadb10-4-8二进制安装及源码编译后设置密码无效的一些发现","date":"2019-09-18T11:35:12.000Z","updated":"2019-12-09T02:46:20.632Z","comments":true,"path":"/blog/4f0efae7.html","link":"","permalink":"https://wudihechao.github.io/blog/4f0efae7.html","excerpt":"  之前安装了最新版的mariadb10.4.8后,无论是二进制编译安装还是源码编译安装,设定完密码之后启动mysqld服务,结果都不需要密码就可以登陆进去,无论怎么执行mysql_secure_installation数据库初始化脚本或者mysql_secure_installation安全加固脚本,进入mysql都无需密码,用命令直接设置密码也无效,都是直接一敲mysql就可以进入数据库了。my.cnf配置文件查看了无数遍,也没发现任何蛛丝马迹。今天终于在无意中查看mysql数据库权限时意外有所收获,写出来与大家分享,让大家少走弯路。","text":"  之前安装了最新版的mariadb10.4.8后,无论是二进制编译安装还是源码编译安装,设定完密码之后启动mysqld服务,结果都不需要密码就可以登陆进去,无论怎么执行mysql_secure_installation数据库初始化脚本或者mysql_secure_installation安全加固脚本,进入mysql都无需密码,用命令直接设置密码也无效,都是直接一敲mysql就可以进入数据库了。my.cnf配置文件查看了无数遍,也没发现任何蛛丝马迹。今天终于在无意中查看mysql数据库权限时意外有所收获,写出来与大家分享,让大家少走弯路。  当我进入mysql数据库,打开user表时,查看了下用户没有任何问题。MariaDB [mysql]> select user,host,password from user;+———-+————+———————————————————–+| user   | host    |   password                    |+———-+————+———————————————————–+|  root  | localhost | 54D9A58CB44735F80AC5AD29961814D6D12B8746 ||  root  | 127.0.0.1 | 54D9A58CB44735F80AC5AD29961814D6D12B8746 ||  root  | ::1     | 54D9A58CB44735F80AC5AD29961814D6D12B8746 |+———-+————+———————————————————–+3 rows in set (0.001 sec)|user | host |password ||–|–|–||root | localhost | 54D9A58CB44735F80AC5AD29961814D6D12B8746 ||root | 127.0.0.1 | 54D9A58CB44735F80AC5AD29961814D6D12B8746 ||root |::1| 54D9A58CB44735F80AC5AD29961814D6D12B8746 |  可当我突发奇想打算看看他们这几个用户有什么权限上的区别时,就发现问题了:MariaDB [mysql]> show grants for ‘root‘@’localhost’;+—————————————————————————————————————+|  Grants for root@localhost                                   |+—————————————————————————————————————+| GRANT ALL PRIVILEGES ON . TO ‘root‘@’localhost’ IDENTIFIED VIA mysql_native_password USING ‘invalid’ OR unix_socket WITH GRANT OPTION                          || GRANT PROXY ON ‘‘@’%’ TO ‘root‘@’localhost’ WITH GRANT OPTION         |+—————————————————————————————————————+2 rows in set (0.000 sec)MariaDB [mysql]> show grants for ‘root‘@’127.0.0.1’;ERROR 1141 (42000): There is no such grant defined for user ‘root’ on host ‘127.0.0.1’MariaDB [mysql]> show grants for ‘root‘@’::1’;ERROR 1141 (42000): There is no such grant defined for user ‘root’ on host ‘::1’MariaDB [mysql]>  root用户竟然查不到权限,这就很奇怪了。在之前的mariadb10.2.27上尝试了下,都是正常的,如下图所示:  于是我就尝试对那两个异常的用户账号授权。  竟然也无法授权。这种情况跟我之前尝试过的直接用insert命令向user表中加的user条目情况有点相似。当时我用1insert user set Host='192.168.32.7',User='root',Password='54D9A58CB44735F80AC5AD29961814D6D12B8746',ssl_cipher='',x509_subject='',x509_issuer='',authentication_string=''; 命令在user表中创建了一个用户条目‘root‘@’192.168.32.7’。  这个条目看起来和真正的用户一样,可等到授权时就发现没法授权了,且这个用户也没法远程在192.168.32.7主机登录,尝试insert了一个新的非root用户,也是同样的情况。说明create user 命令不单单只是在这个表上创建了新的用户条目,在其他关联的表上也有条目的增加。OK ,话题扯远了,继续说之前的无法加密的问题。  既然是同样的情况,我想到会不会是说明我这个新装好的mariadb上的这两个用户没有创建成功,是不是像上面提到的两个异常用户只是在这表中“徒有其表”呢?  那就尝试创建用户,并查看了下权限。  竟然创建用户成功了,而且权限也显示出来了(我记得我还没有授权呢啊!!!看来是root用户不用授权)。好像一切都恢复正常了。  赶紧退出看下是不是真的恢复正常了,结果失望的发现还是一敲mysql就可以登陆进去了!!!  不过又尝试了下 用127.0.0.1登陆,发现竟然需要密码验证了,且用之前设置的密码才可以登录。我好像发现了什么~  删除用户‘root‘@’localhost’,不让删,需要至少一个有CREATE USER权限用户,看来之前的‘root‘@’127.0.0.1’用户没有权限。  那就先给‘root‘@’127.0.0.1’用户授权12grant all on *.* to 'root'@'127.0.0.1';   再删除用户‘root‘@’localhost’,成功了,再重新创建‘root‘@’localhost’用户并指定密码,再授权。OK,退出重新登陆一下。  果然数据库加密成功了~!问题解决!——————————————-(以上封存,警醒自己的无知~)——————————————–        后记:  原来是mariadb10.4.8版本默认是可以本地使用使用unix_socket登陆无需密码,才导致密码无效,在上面的图片里显示得清清楚楚。MariaDB [mysql]> show grants for ‘root‘@’localhost’;1234567891011+---------------------------------------------------------------------------------------------------------------+| &emsp;Grants for root@localhost&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp; |+---------------------------------------------------------------------------------------------------------------+| GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' IDENTIFIED VIA mysql_native_password USING 'invalid' **OR unix_socket** WITH GRANT OPTION &emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;|| GRANT PROXY ON ''@'%' TO 'root'@'localhost' WITH GRANT OPTION &emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;|+---------------------------------------------------------------------------------------------------------------+2 rows in set (0.000 sec)**MariaDB [mysql]> show grants for 'root'@'127.0.0.1';**ERROR 1141 (42000): There is no such grant defined for user 'root' on host '127.0.0.1'**MariaDB [mysql]> show grants for 'root'@'::1';**ERROR 1141 (42000): There is no such grant defined for user 'root' on host '::1' MariaDB [mysql]>  当然,也存在用户不存在,而user表中有条目的情况。所以想最终解决这个问题,实际上最直接的命令是:1GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' IDENTIFIED VIA mysql_native_password USING '*54D9A58CB44735F80AC5AD29961814D6D12B8746' WITH GRANT OPTION;   重新对root@localhost用户授权取消unix_sock登陆,并设置密码。  至此,问题才真正解决。                                      后记写于 2019.9.23。","categories":[{"name":"故障记录","slug":"故障记录","permalink":"https://wudihechao.github.io/categories/故障记录/"}],"tags":[{"name":"踩坑","slug":"踩坑","permalink":"https://wudihechao.github.io/tags/踩坑/"},{"name":"mysql","slug":"mysql","permalink":"https://wudihechao.github.io/tags/mysql/"},{"name":"mariadb","slug":"mariadb","permalink":"https://wudihechao.github.io/tags/mariadb/"}],"keywords":[{"name":"故障记录","slug":"故障记录","permalink":"https://wudihechao.github.io/categories/故障记录/"}]},{"title":"二进制安装及源码编译安装mariadb数据库","slug":"二进制安装及源码编译安装mariadb数据库","date":"2019-09-15T02:29:05.000Z","updated":"2019-12-04T02:04:29.548Z","comments":true,"path":"/blog/b124800c.html","link":"","permalink":"https://wudihechao.github.io/blog/b124800c.html","excerpt":"  MariaDB数据库管理系统是MySQL的一个分支,主要由开源社区在维护,采用GPL授权许可 MariaDB的目的是完全兼容MySQL,包括API和命令行,使之能轻松成为MySQL的代替品。在存储引擎方面,使用XtraDB(英语:XtraDB)来代替MySQL的InnoDB。 MariaDB由MySQL的创始人Michael Widenius(英语:Michael Widenius)主导开发,他早前曾以10亿美元的价格,将自己创建的公司MySQL AB卖给了SUN,此后,随着SUN被甲骨文收购,MySQL的所有权也落入Oracle的手中。MariaDB名称来自Michael Widenius的女儿Maria的名字。","text":"  MariaDB数据库管理系统是MySQL的一个分支,主要由开源社区在维护,采用GPL授权许可 MariaDB的目的是完全兼容MySQL,包括API和命令行,使之能轻松成为MySQL的代替品。在存储引擎方面,使用XtraDB(英语:XtraDB)来代替MySQL的InnoDB。 MariaDB由MySQL的创始人Michael Widenius(英语:Michael Widenius)主导开发,他早前曾以10亿美元的价格,将自己创建的公司MySQL AB卖给了SUN,此后,随着SUN被甲骨文收购,MySQL的所有权也落入Oracle的手中。MariaDB名称来自Michael Widenius的女儿Maria的名字。  MariaDB基于事务的Maria存储引擎,替换了MySQL的MyISAM存储引擎,它使用了Percona的 XtraDB,InnoDB的变体,分支的开发者希望提供访问即将到来的MySQL 5.4 InnoDB性能。这个版本还包括了 PrimeBase XT (PBXT) 和 FederatedX存储引擎。  开发这个分支的原因之一是:甲骨文公司收购了MySQL后,有将MySQL闭源的潜在风险,因此社区采用分支的方式来避开这个风险。 过去一年中,大型互联网用户以及Linux发行商纷纷抛弃MySQL,转投MariaDB阵营。MariaDB是目前最受关注的MySQL数据库衍生版,也被视为开源数据库MySQL的替代品。  MariaDB虽然被视为MySQL数据库的替代品,但它在扩展功能、存储引擎以及一些新的功能改进方面都强过MySQL。而且从MySQL迁移到MariaDB也是非常简单的:  1、数据和表定义文件(.frm)是二进制兼容的  2、所有客户端API、协议和结构都是完全一致的  3、所有文件名、二进制、路径、端口等都是一致的  4、所有的MySQL连接器,比如PHP、Perl、Python、Java、.NET、MyODBC、Ruby以及MySQL C connector等在MariaDB中都保持不变  5、mysql-client包在MariaDB服务器中也能够正常运行  6、共享的客户端库与MySQL也是二进制兼容的也就是说,在大多数情况下,你完全可以卸载MySQL然后安装MariaDB,然后就可以像之前一样正常的运行。(以上摘自百度百科)  作为目前比较热门的开源数据库软件,一般常见有三种安装方式:yum或rpm包安装、二进制安装以及源码包安装,包安装过于简单,也很难符合一般企业定制需要,故不做叙述,本文详细讲述后两种安装方式。 二进制安装1.先创建mysql用户及mysql组,并制定家目录为/data/mysql12groupadd -r -g 306 mysql #指定属组gid为306useradd -r -g 306 -u 306 -d /data/mysql mysql #指定属主uid为306,家目录为/data/mysql 2.准备数据目录(mysq用户家目录),并修正权限1mkdir /data/mysql;chown mysql:mysql /data/mysql 3.去官网下载mariadb 二进制tar包(链接是CentOS7X86_64的10.4.8稳定版)  http://ftp.igh.cnrs.fr/pub/mariadb//mariadb-10.4.8/bintar-linux-systemd-x86_64/mariadb-10.4.8-linux-systemd-x86_64.tar.gz4.解压tar包指/usr/local目录下,递归改属主为root、属组为mysql,并在/usr/local目录下创建一个名为mysql的软链接指向解压好的mariadb目录1234tar xzf mariadb-10.4.8-linux-systemd-x86_64.tar.gz -c /usr/localcd /usr/localln -sv mariadb-10.4.8-linux-systemd-x86_64 mysql`chown -R root:mysql /usr/local/mysql/ 5.创建配置文件,并修改1234mkdir /etc/mysql/cp /etc/my.cnf /etc/mysql/my.cnfsed -ri '/datadir=\\//s@(.*=).*@\\1\\/data\\/mysql@' /etc/mysql/my.cnf #修改配置文件,指定数据库储存路径sed -ri '/datadir/a\\innodb_file_per-table=on\\nskip_name_resolve=on' /etc/mysql/my.cnf #设置每个表独立文件 和 禁用主机名解析 6.创建数据库文件1/usr/local/scripts/mysql_install_db --datadir=/data/mysql --user=mysql 7.创建服务脚本并启动服务123cp /usr/local/support-files/mysql.server /etc/rc.d/init.d/mysqldchkconfig --add mysqldservice mysqld start 8.增加PATH环境变量路径,并生效。12echo 'PATH=/usr/local/mysql/bin:$PATH' >/etc/profile.d/mysql.sh. /etc/profile.d/mysql.sh 9.运行安全初始化脚本,设置root口令、禁用匿名登陆、禁用远程主机登陆、删除test数据库,并立即生效(根据提示操作)。12ln -s /var/lib/mysql/mysql.sock /tmp/usr/local/mysql/bin/mysql_secure_installation   至此,二进制安装mariadb数据库就完成了~ 源码编译安装1.先创建mysql用户及mysql组,并制定家目录为/data/mysql12groupadd -r -g 306 mysql #指定属组gid为306useradd -r -g 306 -u 306 -d /data/mysql mysql #指定属主uid为306,家目录为/data/mysql 2.准备数据目录(mysq用户家目录),并修正权限1mkdir /data/mysql;chown mysql:mysql /data/mysql 3.去官网下载mariadb 源码tar包(链接是CentOS7X86_64的10.4.8稳定版)  http://ftp.igh.cnrs.fr/pub/mariadb//mariadb-10.4.8/source/mariadb-10.4.8.tar.gz4.解压源码包,并进入源码包目录12tar xvf mariadb-10.4.8.tar.gzcd mariadb-10.4.8/l 5.CMAKE编译源码包  需要的依赖包有cmake openssldevel ncurses-devel bison bison-devel zlib-devel libcurl-devel libarchive-devel boostdevel gcc gcc-c++ gnutls-devel libxml2-devel libevent-devel libaio-devel ,一口气yum装上。1yum install cmake openssldevel ncurses-devel bison bison-devel zlib-devel libcurl-devel libarchive-devel boostdevel gcc gcc-c++ gnutls-devel libxml2-devel libevent-devel libaio-devel 然后编译12345678910111213141516171819cmake . \\-DCMAKE_INSTALL_PREFIX=/data/apps/mysql \\-DMYSQL_DATADIR=/data/mysql/ \\-DSYSCONFDIR=/etc/ \\-DMYSQL_USER=mysql \\-DWITH_INNOBASE_STORAGE_ENGINE=1 \\-DWITH_ARCHIVE_STORAGE_ENGINE=1 \\-DWITH_BLACKHOLE_STORAGE_ENGINE=1 \\-DWITH_PARTITION_STORAGE_ENGINE=1 \\-DWITHOUT_MROONGA_STORAGE_ENGINE=1 \\-DWITH_DEBUG=0 \\-DWITH_READLINE=1 \\-DWITH_SSL=system \\-DWITH_ZLIB=system \\-DWITH_LIBWRAP=0 \\-DENABLED_LOCAL_INFILE=1 \\-DMYSQL_UNIX_ADDR=/data/mysql/mysql.sock \\-DDEFAULT_CHARSET=utf8 \\-DDEFAULT_COLLATION=utf8_general_ci 6.安装1make -j 4 && make install 7.创建配置文件,并修改1234mkdir /etc/mysql/cp /etc/my.cnf /etc/mysql/my.cnfsed -ri '/datadir=\\//s@(.*=).*@\\1\\/data\\/mysql@' /etc/mysql/my.cnf #修改配置文件,指定数据库储存路径sed -ri '/datadir/a\\innodb_file_per-table=on\\nskip_name_resolve=on' /etc/mysql/my.cnf #设置每个表独立文件 和 禁用主机名解析 8.创建数据库文件12chown -R root:mysql /data/apps/mysql//usr/local/scripts/mysql_install_db --datadir=/data/mysql --user=mysql 9.创建服务脚本并启动服务123cp /data/apps/mysql/support-files/mysql.server /etc/rc.d/init.d/mysqldchkconfig --add mysqldservice mysqld start 10.增加PATH环境变量路径,并生效。12echo 'PATH=/data/apps/mysql/bin:$PATH' >/etc/profile.d/mysql.sh. /etc/profile.d/mysql.sh 11.运行安全初始化脚本,设置root口令、禁用匿名登陆、禁用远程主机登陆、删除test数据库,并立即生效(根据提示操作)。1/data/apps/mysql/bin/mysql_secure_installation   报错:Can’t connect to local MySQL server through socket ‘/data/mysql/mysql.sock’ (2)  用ll看了下/data/mysql/mysql.sock确实不存在,而我记得我配置文件/etc/mysql/my.cnf中定义的sosck是在 下  那就把/var/lib/mysql/mysql.sock文件创建一个软链接至/data/mysql/目录1/ln -s /var/lib/mysql/mysql.sock /data/mysql/   再次尝试安全初始化脚本。1/data/apps/mysql/bin/mysql_secure_installation   果然成功了。  至此,源码编译安装mariadb数据库就完成了~","categories":[{"name":"NotOnlySQL","slug":"NotOnlySQL","permalink":"https://wudihechao.github.io/categories/NotOnlySQL/"}],"tags":[{"name":"linux","slug":"linux","permalink":"https://wudihechao.github.io/tags/linux/"},{"name":"编译安装","slug":"编译安装","permalink":"https://wudihechao.github.io/tags/编译安装/"},{"name":"mysql","slug":"mysql","permalink":"https://wudihechao.github.io/tags/mysql/"},{"name":"mariadb","slug":"mariadb","permalink":"https://wudihechao.github.io/tags/mariadb/"}],"keywords":[{"name":"NotOnlySQL","slug":"NotOnlySQL","permalink":"https://wudihechao.github.io/categories/NotOnlySQL/"}]},{"title":"部署自动化安装及cobber(Http+TFTP+PXE+kickstart无人职守批量安装精简版Linux系统)","slug":"部署自动化安装及cobber-Http-TFTP-PXE-kickstart无人职守批量安装精简版Linux系统","date":"2019-09-06T11:54:42.000Z","updated":"2019-12-04T03:49:47.488Z","comments":true,"path":"/blog/8ddda8e.html","link":"","permalink":"https://wudihechao.github.io/blog/8ddda8e.html","excerpt":"  为了简化每台服务器的系统的装机过程和统一服务器配置,可以采用一键自动化安装系统的方式,实现新机器接上网线开机便可批量安装相同配置的系统,便于以后统一管理。  想要实现自动化安装系统,  1.搭建dhcp服务器,给新机器网卡分配IP地址,并引导机器去搭建好的tftp站点下载引导程序及内核文件;  2.搭建tftp服务器,提供引导程序及内核文件;复制内核文件至服务器目录;  3.搭建http服务器,提供应答kickstart文件、系统安装包程序的下载;挂载光盘或拷贝安装包至目录文件;  4生成ks应答kickstart文件(放置于ftp服务器中);  5.复制光盘中的/cdrom/isolinux/isolinux.cfg文件至目录pxelinux.cfg/,改名为pxelinux.cfg/fault文件,即为安装菜单(之后目录pxelinux.cfg/整体放入tftp服务器中)。","text":"  为了简化每台服务器的系统的装机过程和统一服务器配置,可以采用一键自动化安装系统的方式,实现新机器接上网线开机便可批量安装相同配置的系统,便于以后统一管理。  想要实现自动化安装系统,  1.搭建dhcp服务器,给新机器网卡分配IP地址,并引导机器去搭建好的tftp站点下载引导程序及内核文件;  2.搭建tftp服务器,提供引导程序及内核文件;复制内核文件至服务器目录;  3.搭建http服务器,提供应答kickstart文件、系统安装包程序的下载;挂载光盘或拷贝安装包至目录文件;  4生成ks应答kickstart文件(放置于ftp服务器中);  5.复制光盘中的/cdrom/isolinux/isolinux.cfg文件至目录pxelinux.cfg/,改名为pxelinux.cfg/fault文件,即为安装菜单(之后目录pxelinux.cfg/整体放入tftp服务器中)。  演示环境:CentOS7 Kernel Version: 3.10.0-957.el7.x86_64 本机IP:192.168.32.7 1. 配置DHCP服务器  1.首先安装dhcp服务。1yum install dhcp   2.启动dhcp服务。但直接启动,会报错,如下图所示,须先修改dhcpd的配置文件。  打开/etc/dhcp/dhcpd.conf配置文件后发现,配置文件是空的,难怪报错,有一行“see /usr/share/doc/dhcpd.conf.example”,原来要我们参考这个模版来自己写配置文件。  于是先复制模版至原配置文件目录。  选择覆盖,再打开配置文件。  修改⑴网段⑵租期时长⑶网关⑷DNS⑸DNS域后缀⑹提供资源下载站点IP⑺需要下载的引导文件  此时重启DHCP服务成功,查看DHCP服务状态,已经启动(状态信息的提示,是指另一块网卡没有被DHCP服务器网段分配地址,分配一个就足够了,可以无视)。 2.搭建tftp服务器  1.首先安装tftp服务端1yum install tftp-server -y   2.生成pxelinux.0引导程序,并放入tftp服务器   ⑴先确定引导程序所在的安装包;1yum search pxelinux    ⑵安装syslinux.x86_64;1yum install syslinux.x86_64    ⑶找到引导程序pxelinux.0的所在路径;1rpm -ql syslinux.x86_64|grep pxelinux.0    ⑷将引导程序pxelinux.0及菜单背景模版文件menu.c32拷贝到服务器1cp /usr/share/syslinux/pxelinux.0 /usr/share/syslinux/menu.c32 /var/lib/tftpboot/   3.将准备好的内核文件和initrd.img文件拷贝到服务器12cp /cdrom6/isolinux/vmlinuz /cdrom6/isolinux/initrd.img /var/lib/tftpboot/6/cp /cdrom7/isolinux/vmlinuz /cdrom7/isolinux/initrd.img /var/lib/tftpboot/7/   最终效果如下: 3.搭建http服务器  1.首先安装httpd服务端1yum install httpd -y   2.启动httpd服务1systemctl start httpd   3.查看httpd服务状态是否启动1systemctl status httpd   4.创建安装包文件目录1mkdir -pv /var/www/html/CentOS/{6,7}/os/x86_64/   5.挂载光盘安装包至目录或拷贝安装文件至目录12mount /dev/sr0 /var/www/html/CentOS/7/os/x86_64/mount /dev/sr1 /var/www/html/CentOS/6/os/x86_64/   效果图如下:  6.挂载光盘安装包至目录或拷贝安装文件至目录 4.生成kickstart应答文件  ks文件的生成一般有两种方法:  ①:之前手动安装后系统自动生成的的记录文件/root/anaconda-ks.cfg文件是可以直接复制过来修改使用的。  ②:用kickstart工具生成。我们这里采用第二种。执行命令。(这是一个在图形化界面操作的选项命令,如果用的是Xshell工具连接虚拟机,需要开启Xmanager - Passive,来转发图形化界面)1system-config-kickstart   第一步,选择语言,键盘布局,时区,设置root口令(是否加密存储),安装完成后重启,命令行模式安装;  第二步,选择选择系统安装方式,通过HTTP服务站点安装,输入已经搭好的服务器IP和安装包路径;  第三步,选择安装grub引导安装程序,(先不对grub进行加密了,据说会有加密方式不符的话会有报错),加内核参数net.innames=0实现不更改网卡名称;  第四步,清除原有分区,清除分区标签,设置新分区表;  第五步,设置网卡名eth0,及获取IP方式为DHCP;  第六步,默认关闭SELINUX策略和防火墙;  第七步,选择需要的系统服务;  第八步,附加装机前后脚本;  第九步,将ks应答文件复制到http服务器  1mkdir -p /var/www/html/ksdir/;cp ~/centos7-ks.cfg /var/www/html/ksdir/   (CentOS6的KS同样方式生成)1cp ~/centos6-ks.cfg /var/www/html/ksdir/ 5.创建安装菜单  菜单文件的一般有两种方法:  ①:复制修改光盘中的菜单文件/cdrom/isolinux/lisolinux.cfg文件是可以直接复制过来修改使用的。  ①:VIM手工输入。123456789101112131415default menu.c32timeout 600menu title Auto Install CentOSlabel linux 7 mennu label Install CentOS^7 menu default kernel 7/vmlinuz append initrd=7/initrd.img ks=http://192.168.32.7/ksdir/centos7-ks.cfglabel linux 6 mennu label Install CentOS^6 kernel 6/vmlinuz append initrd=6/initrd.img ks=http://192.168.32.7/ksdir/centos6-ks.cfglabel linux local mennu label Boot from ^local drive localboot Oxffff   再将菜单文件存入tftp服务器中 /linux.cfg目录 下 重命名为fault文件。tftp服务器最终目录机构如下图所示:  至此,基于PXE网卡,无人职守批量安装CentOS系统的配置就完成了。","categories":[{"name":"linux基础","slug":"linux基础","permalink":"https://wudihechao.github.io/categories/linux基础/"}],"tags":[{"name":"linux","slug":"linux","permalink":"https://wudihechao.github.io/tags/linux/"},{"name":"cobber","slug":"cobber","permalink":"https://wudihechao.github.io/tags/cobber/"},{"name":"kickstart","slug":"kickstart","permalink":"https://wudihechao.github.io/tags/kickstart/"},{"name":"pxe","slug":"pxe","permalink":"https://wudihechao.github.io/tags/pxe/"}],"keywords":[{"name":"linux基础","slug":"linux基础","permalink":"https://wudihechao.github.io/categories/linux基础/"}]},{"title":"CentOS6源码编译安装linunx内核最新版5.2.9","slug":"CentOS6源码编译安装linunx内核最新版5-2-9","date":"2019-08-29T08:37:03.000Z","updated":"2019-12-25T12:13:44.437Z","comments":true,"path":"/blog/133d68.html","link":"","permalink":"https://wudihechao.github.io/blog/133d68.html","excerpt":"  之前在CentOS7 上源码编译安装过新内核,不过有次在6上编译就遇到问题了.","text":"  之前在CentOS7 上源码编译安装过新内核,不过有次在6上编译就遇到问题了.提示错误如下1make[2]: *** [scripts/sign-file] Error 1 make[1]: *** [scripts] Error 2 make 搞了好久,好像是参数设置不正确。1error: #error Sorry, your compiler is too old - please upgrade it   好像是说gcc版本过低。  去epel源找了一下,已经是最新版了啊~  查看了下之前CentOS7的gcc版本,是gcc-4.8.5,再去一查这个CentOS6的gcc版本,是gcc-4.4.7,可能就差在这几个小版本上了。  于是升级gcc版本就成了当务之急。  那就源码编译安装个新版的gcc吧。(后来朋友说可以直接把CentOS7上gcc的rpm包直接拿来用的,未尝试) 编译安装gcc  gcc有三个依赖包gmp、mpfr、mpc,要首先编译安装(虽然原本就有,不过如果编译高版本gcc,这三个依赖包不装新版本的话也会报错)  先去下载好三个依赖包源码包及gcc源码包123456mkdir /data/gcc #创建目录/data/gcccd /data/gccwget https://gmplib.org/download/gmp/gmp-6.1.2.tar.xz #下载gmp6.1.2wget http://www.mpfr.org/mpfr-current/mpfr-3.1.5.tar.gz #下载mpfr3.1.5wget ftp://ftp.gnu.org/gnu/mpc/mpc-1.0.3.tar.gz #下载mpc1.0.3wget ftp://ftp.gnu.org/gnu/gcc/gcc-6.3.0/gcc-6.3.0.tar.gz #下载gcc6.3.0   再挨个编译安装三个依赖包(注意这三个依赖包也有依赖关系,需先安装gmp,再安装mpfr,之后再装mpc) gmp  先来第一个,编译安装装gmp1234tar -xJf gmp-6.1.2.tar.xz gmp-6.1.2cd gmp-6.1.2./configure --prefix=/usr/local/gcc/gmp --build=x86_64-linuxmake && make install   这当时有报错  error: could not find a working compiler  当时没有加参数 –build=x86_64-linux,加上之后成功解决,参考: 原文地址. http://www.voidcn.com/article/p-zsgyngma-ug.html mpfr  之后编译安装mpfr1234tar -xJf mpfr-3.1.5.tar.xz mpfr-3.1.5cd mpfr-3.1.5./configure --prefix=/usr/local/gcc/mpfr --with-gmp=/usr/local/gcc/gmpmake && make install   然后编译安装mpc1234tar -xzf mpc-1.0.3.tar.gz mpc-1.0.3cd mpc-1.0.3./configure --prefix=/usr/local/gcc/mpc --with-gmp=/usr/local/gcc/gmp -with-mpfr=/usr/local/gcc/mpfrmake && make install gcc  之后也没有报错,那就开始编译安装gcc。1234tar xvf gcc-6.3.0.tar.gzcd gcc-6.3.0./configure --prefix=/usr/local/gcc --enable-threads=posix --disable-checking --disable-multilib --enable-languages=c,c++ --with-gmp=/usr/local/gcc/gmp --with-mpfr=/usr/local/gcc/mpfr --with-mpc=/usr/local/gcc/mpcmake && make install   gcc中途有几次报错,不过也都解决了。错误提示[1]:1make: *** [sub-make] Error 2   显示这个问题编译不过去。这是因为找不到gmp依赖包导致的,可是我明明装了依赖包的,看来是路径设置有问题,因为当时我是把这三个依赖包都装在gcc下,不过在编译gcc的时候忘记改依赖包的地址了。错误提示[2]:1configure: error: cannot compute suffix of object files: cannot compile   解决办法是:  在/etc/profile里面加上以下内容:1export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/gcc/mpc-0.9/lib:/usr/local/gcc/gmp-5.0.1/lib:/usr/local/gcc/mpfr-3.1.0/lib   然后重新加载配置文件/etc/profile1source /etc/profile 添加变量  /usr/local/lib              #这个是默认系统的变量  /usr/local/gmp/lib  /usr/local/mpfr/lib  /usr/local/mpc/lib  /usr/local/mysql/lib  /usr/local/openssl/lib          #openssl变量   添加保存后记得更新动态库的缓存:  ldconfig -v 备份系统默认的gcc版本123mv /usr/bin/gcc /usr/bin/gcc-bakmv /usr/bin/g++ /usr/bin/g++-bakmv /usr/bin/c++ /usr/bin/c++-bak 建新的gcc软连接1234ln -s /usr/local/gcc/bin/gcc /usr/bin/gccln -s /usr/local/gcc/bin/c++ /usr/bin/c++ln -s /usr/local/gcc/bin/g++ /usr/bin/g++ln -s /usr/local/gcc/lib64/libstdc++.so.6.0.22 /usr/lib64/libstdc++.so.6 编译安装linux kernel 5.2.9  高高兴兴cd linux-5.2.9,进入解压好的内核目录,做好.config配置文件,准备用make menuconfig对内核进行自定义配置的时候,报错了:1error while loading shared libraries: libmpc.so.3: cannot open shared object file: No such file or directory   这又是怎么回事。好像是gcc出问题了。返回去检查。  原来添加完库忘了更新动态库,输入命令:1ldconfig -v ~结果还报错:123456scripts/kconfig/mconf KconfigYour display is too small to run Menuconfig!It must be at least 19 lines by 80 columns.make[2]: *** [menuconfig] Error 1make[1]: *** [menuconfig] Error 2make: *** [sub-make] Error 2 这是因为我的XSELL窗口太小了,把XSHELL放大最大化,果然就打开了熟悉的蓝色界面~修改一些个性化的设置(例如改名、加NFS文件系统),就可以保存了。 编译内核  因为编译内核时间比较长,为防止发生断网或者断电等意外,我们创建一个SCREEN来编译。  12screen -S linuxkernelyes | make -j 16   用一个yes命令省去一直手动y了,等待他编译完成就好了。    在这又遇到一个报错,贴出来和大家分享下:1You are building kernel with non-retpoline compiler.   去查了下,网上都说是因为gcc版本过低,或内核版本过高。可我这明显不是gcc版本低的问题(都那么高了),问题是内核版本应该也不是问题,毕竟之前CentOS7编译就没这么多事。正犯愁不知道怎么解决的时候,发现了一篇文章查看原文,发现这个报错可以通过修改参数直接不启用RETPOLINE从而跳过(具体后果未知),于是修改配置文件重新来过:123make cleansed -ri 's@(CONFIG_RETPOLINE=).*@\\1n@' .configmake -j 16   果然就顺利编译成功了。 编译模块  然后安装模块:  1make modules_ install   安装模块的时候,有报错:  1ERROR: modinfo: could not find module *   不过查了相关文章查看原文,好像这些模块也都可以正常使用,于是乎无视之~  最后,安装内核相关文件。  1make install   至此,新内核就装完啦~重启切换去去体验一下!","categories":[{"name":"linux基础","slug":"linux基础","permalink":"https://wudihechao.github.io/categories/linux基础/"}],"tags":[{"name":"linux","slug":"linux","permalink":"https://wudihechao.github.io/tags/linux/"},{"name":"编译安装","slug":"编译安装","permalink":"https://wudihechao.github.io/tags/编译安装/"},{"name":"kernel","slug":"kernel","permalink":"https://wudihechao.github.io/tags/kernel/"},{"name":"踩坑","slug":"踩坑","permalink":"https://wudihechao.github.io/tags/踩坑/"}],"keywords":[{"name":"linux基础","slug":"linux基础","permalink":"https://wudihechao.github.io/categories/linux基础/"}]},{"title":"for循环实现的小脚本","slug":"for循环实现的小脚本","date":"2019-08-22T13:45:34.000Z","updated":"2019-12-04T05:52:04.653Z","comments":true,"path":"/blog/a96e917a.html","link":"","permalink":"https://wudihechao.github.io/blog/a96e917a.html","excerpt":"  用for循环写了个小脚本,可以输出不同大小的图案,能力有限,输出了一个不太规则的字符图案,希望可以早日写出一个脚本可以自动填充放大任何ASCII图案。","text":"  用for循环写了个小脚本,可以输出不同大小的图案,能力有限,输出了一个不太规则的字符图案,希望可以早日写出一个脚本可以自动填充放大任何ASCII图案。  效果图如下:  脚本如下:123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216#!/bin/bashread -p \"please input the size :\" n[ \"$n\" -ge 0 ] 2>/dev/null || { echo \"please input a right num!\";exit 10;}mkdir -p ~/best/bestN=`echo $n-1|bc`J=`echo $N/2+1|bc`K=`echo $N/4+1|bc`L=`echo $N/5|bc`#第一行echo -e '88888\\c' >~/best/best.f1for i in $(seq $N);do echo -e '888\\c' >>~/best/best.f1donefor i in $(seq $L);do echo -e '88\\c' >>~/best/best.f1doneecho '888ba' >>~/best/best.f1#第二行echo -e '8\\c' >~/best/best.f2for i in $(seq $L);do echo -e '8\\c' >>~/best/best.f2doneecho -e '8 \\c' >>~/best/best.f2for i in $(seq $N);do echo -e ' \\c' >>~/best/best.f2doneecho -e ' \"8\\c' >>~/best/best.f2for i in $(seq $L);do echo -e '8\\c' >>~/best/best.f2doneecho -e 'b \\c' >>~/best/best.f2for i in $(seq $N);do echo -e ' \\c' >>~/best/best.f2doneecho ',d' >>~/best/best.f2#第三行echo -e '8\\c' >~/best/best.f3for i in $(seq $L);do echo -e '8\\c' >>~/best/best.f3doneecho -e '8 \\c' >>~/best/best.f3for i in $(seq $N);do echo -e ' \\c' >>~/best/best.f3doneecho -e ' \"8\\c' >>~/best/best.f3for i in $(seq $L);do echo -e '8\\c' >>~/best/best.f3doneecho -e 'P \\c' >>~/best/best.f3for i in $(seq $N);do echo -e ' \\c' >>~/best/best.f3doneecho '88' >>~/best/best.f3#第四行echo -e '8\\c' >~/best/best.f4for i in $(seq $L);do echo -e '8\\c' >>~/best/best.f4doneecho -e '8aaa\\c' >>~/best/best.f4for i in $(seq $N);do echo -e 'aaa\\c' >>~/best/best.f4doneecho -e 'aaa8\\c' >>~/best/best.f4for i in $(seq $L);do echo -e '8\\c' >>~/best/best.f4doneecho -e 'P'\"'\"' ,adP\\c' >>~/best/best.f4for i in $(seq $N);do echo -e 'PP\\c' >>~/best/best.f4doneecho -e 'PYba, ,adP\\c' >>~/best/best.f4for i in $(seq $N);do echo -e 'PP\\c' >>~/best/best.f4doneecho -e 'PYba, MM\\c' >>~/best/best.f4for i in $(seq $N);do echo -e 'M\\c' >>~/best/best.f4doneecho -e '88MM\\c' >>~/best/best.f4for i in $(seq $N);do echo -e 'M\\c' >>~/best/best.f4doneecho >>~/best/best.f4#第五行echo -e '8\\c' >~/best/best.f5for i in $(seq $L);do echo -e '8\\c' >>~/best/best.f5doneecho -e '8 \\c' >>~/best/best.f5for i in $(seq $N);do echo -e ' \\c' >>~/best/best.f5doneecho -e ' 8\\c' >>~/best/best.f5for i in $(seq $L);do echo -e '8\\c' >>~/best/best.f5doneecho -e 'b, a8P \\c' >>~/best/best.f5for i in $(seq $N);do echo -e ' \\c' >>~/best/best.f5doneecho -e ' `8 I8[ \\c' >>~/best/best.f5for i in $(seq $N);do echo -e ' \\c' >>~/best/best.f5doneecho -e ' \"'\"'\"' \\c' >>~/best/best.f5for i in $(seq $N);do echo -e ' \\c' >>~/best/best.f5doneecho -e ' 88' >>~/best/best.f5#第六行echo -e '8\\c' >~/best/best.f6for i in $(seq $L);do echo -e '8\\c' >>~/best/best.f6doneecho -e '8 \\c' >>~/best/best.f6for i in $(seq $N);do echo -e ' \\c' >>~/best/best.f6doneecho -e ' `8\\c' >>~/best/best.f6for i in $(seq $L);do echo -e '8\\c' >>~/best/best.f6doneecho -e 'b 8PP\"\"\\c' >>~/best/best.f6for i in $(seq $N);do echo -e '\"\"\\c' >>~/best/best.f6doneecho -e '\"\"\"`'\"'\"' `\"Y8\\c' >>~/best/best.f6for i in $(seq $N);do echo -e '88\\c' >>~/best/best.f6doneecho -e 'ba, \\c' >>~/best/best.f6for i in $(seq $N);do echo -e ' \\c' >>~/best/best.f6doneecho -e ' 88\\c' >>~/best/best.f6echo >>~/best/best.f6#第七行echo -e '8\\c' >~/best/best.f7for i in $(seq $L);do echo -e '8\\c' >>~/best/best.f7doneecho -e '8 \\c' >>~/best/best.f7for i in $(seq $N);do echo -e ' \\c' >>~/best/best.f7doneecho -e ' a8\\c' >>~/best/best.f7for i in $(seq $L);do echo -e '8\\c' >>~/best/best.f7doneecho -e 'P \"8b, \\c' >>~/best/best.f7for i in $(seq $N);do echo -e ' \\c' >>~/best/best.f7doneecho -e ' ,aa aa \\c' >>~/best/best.f7for i in $(seq $N);do echo -e ' \\c' >>~/best/best.f7doneecho -e ' ]8I \\c' >>~/best/best.f7for i in $(seq $N);do echo -e ' \\c' >>~/best/best.f7doneecho -e ' 88,' >>~/best/best.f7#第八行echo -e '88888\\c' >~/best/best.f8for i in $(seq $N);do echo -e '888\\c' >>~/best/best.f8donefor i in $(seq $L);do echo -e '88\\c' >>~/best/best.f8doneecho -e '888P\" `\"Ybb\\c' >>~/best/best.f8for i in $(seq $N);do echo -e 'oo\\c' >>~/best/best.f8doneecho -e 'd8\"'\"'\"' `\"Ybb\\c' >>~/best/best.f8for i in $(seq $N);do echo -e 'oo\\c' >>~/best/best.f8doneecho -e 'dP\"'\"'\"' \\c' >>~/best/best.f8for i in $(seq $N);do echo -e ' \\c' >>~/best/best.f8doneecho -e ' \"Y888\\c' >>~/best/best.f8for i in $(seq $N);do echo -e '8\\c' >>~/best/best.f8doneecho >>~/best/best.f8for j in $(seq $K);do cat ~/best/best.f1donefor j in $(seq $J);do cat ~/best/best.f2donefor j in $(seq $J);do cat ~/best/best.f3donefor j in $(seq $K);do cat ~/best/best.f4donefor j in $(seq $J);do cat ~/best/best.f5donefor j in $(seq $K);do cat ~/best/best.f6donefor j in $(seq $J);do cat ~/best/best.f7donefor j in $(seq $K);do cat ~/best/best.f8done\\rm -rf ~/best/best   脚本写的太复杂,可读性太差,等日后水平提升再来改进,留存!","categories":[{"name":"linux基础","slug":"linux基础","permalink":"https://wudihechao.github.io/categories/linux基础/"}],"tags":[{"name":"linux","slug":"linux","permalink":"https://wudihechao.github.io/tags/linux/"},{"name":"script","slug":"script","permalink":"https://wudihechao.github.io/tags/script/"}],"keywords":[{"name":"linux基础","slug":"linux基础","permalink":"https://wudihechao.github.io/categories/linux基础/"}]},{"title":"CentOS7磁盘分区及文件系统","slug":"CentOS7磁盘分区及文件系统","date":"2019-08-14T14:27:33.000Z","updated":"2019-12-04T06:00:24.309Z","comments":true,"path":"/blog/2bae1e5c.html","link":"","permalink":"https://wudihechao.github.io/blog/2bae1e5c.html","excerpt":"  在CentOS系统的主机中加入一块全新的硬盘,是无法直接被我们使用的,因为这块硬盘上没有分区及文件系统,此时是无法被系统识别使用的。","text":"  在CentOS系统的主机中加入一块全新的硬盘,是无法直接被我们使用的,因为这块硬盘上没有分区及文件系统,此时是无法被系统识别使用的。  一般来说,硬盘上的空间,想要被我们利用,都要经过几个步骤: 1设备识别 2磁盘分区 3创建文件系统(并标记文件系统) 4挂载新的文件系统 5在/etc/fstab文件中创建新条目    只有当设备被挂载到文件系统中,才可以被我们访问且使用。  1 设备识别 新插入硬盘设备,没法被系统立马识别到,需要手动扫描,才可以发现并识别新设备echo "- - -" > /sys/class/scsi_host/host0/scan/sys/class/scsi_host/ 目录下面有几个host 就扫描几次。   2 磁盘分区2.1为什么要分区 优化I/O性能 实现磁盘空间配额限制 提高修复速度 隔离系统和程序 安装多个OS 采用不同文件系统 2.2分区方式  一般来说有两种分区方式 :MBR和GPT。 MBR:全程Master Boot Record,1982年,使用32位表示扇区,单个分区不超过2T。 如何分区:按柱面 0磁道0扇面:512bytes      446byets:boot loader      64bytes : 分区表,其中每16bytes标识一个分区      2byetes :55AA MBR分区中一块硬盘最多有4个主分区,也可以三主分区+1拓展(N个逻辑分区)  MBR分区结构 MBR分区表  硬盘主导记录MBR由4个部分组成 主引导程序(偏移地址0000H–0088H),它负责从活动分区中装载,并运行系统引导程序 出错信息数据区,偏移地址0089H-00E1H为出错信息,00E2H-01BDH全为0字节 分区表(DPT,Disk Partition Table)含4个分区项,偏移地址01BEH–01FDH,每个分区表长16个字节,共64字节为分区项1、分区项2、分区项3、分区项4 结束标志字,偏移地址01FE–01FF的2个字节值为结束标志55AA  GPT:GUID(Globals Unique Identifiers) partition table 支持128个分区,使用64位,支持8Z( 512Byte/block )64Z ( 4096Byte/block) 使用128位UUID(Universally Unique Identifier) 表示磁盘和分区 GPT分区表自动备份在头和尾两份,并有CRC校验位 UEFI (Unified Extensible Firmware Interface 统一可扩展固件接口)硬件支持GPT,使操作系统启动 GPT分区结构2.3管理分区2.31列出块设备2.32创建新分区  parted命令:parted [选项]… [设备 [命令 [参数]…]…]  parted /dev/sdb mklabel gpt|msdos  parted /dev/sdb print  parted /dev/sdb mkpart primary 1 200 (默认M)  parted /dev/sdb rm 1  parted –l 列出分区信息parted的操作都是实时生效的,小心使用    fdisk -l [-u] [device…] 查看分区   fdisk /dev/sdb 管理分区子命令:     p 查看分区列表     t 更改分区类型     n 创建新分区     d 删除分区     v 校验分区     u 转换单位     w 保存并退出     q 不保存并退出 gdisk命令与fdisk用法相同,用于创建GPT分区。2.33同步内核与硬盘的分区表查看内核是否已经识别新的分区cat /proc/partationscentos6通知内核重新读取硬盘分区表新增分区用partx -a /dev/DEVICEkpartx -a /dev/DEVICE -f: force删除分区用partx -d –nr M-N /dev/DEVICECentOS 5,7: 使用partprobepartprobe [/dev/DEVICE]3 创建文件系统3.1文件系统  文件系统是操作系统用于明确存储设备或分区上的文件的方法和数据结构;即在存储设备上组织文件的方法。操作系统中负责管理和存储文件信息的软件结构称为文件管理系统,简称文件系统从系统角度来看,文件系统是对文件存储设备的空间进行组织和分配,负责文件存储并对存入的文件进行保护和检索的系统。具体地说,它负责为用户建立文件,存入、读出、修改、转储文件,控制文件的存取,安全控制,日志,压缩,加密等   支持的文件系统:/lib/modules/`uname –r`/kernel/fs   各种文件系统:https://en.wikipedia.org/wiki/Comparison_of_file_systems3.11文件系统类型Linux文件系统:  ext2(Extended file system):适用于那些分区容量不是太大,更新也不频繁的情况,例如 /boot 分区  ext3:是 ext2 的改进版本,其支持日志功能,能够帮助系统从非正常关机导致的异常中恢复。它通常被用作通用的文件系统  ext4:是 ext 文件系统的最新版。提供了很多新的特性,包括纳秒级时间戳、创建和使用巨型文件(16TB)、最大1EB的文件系统,以及速度的提升  xfs:SGI,支持最大8EB的文件系统  btrfs(Oracle), reiserfs, jfs(AIX), swap光盘:iso9660Windows:FAT32, NTFS,exFATUnix:FFS(fast), UFS(unix), JFS2网络文件系统:NFS, CIFS集群文件系统:GFS2, OCFS2(oracle)分布式文件系统:fastdfs,ceph, moosefs, mogilefs, glusterfs, LustreRAW:未经处理或者未经格式化产生的文件系统 3.12文件系统分类根据其是否支持”journal”功能:  日志型文件系统: ext3, ext4, xfs, …  非日志型文件系统: ext2, vfat文件系统的组成部分:  内核中的模块:ext4, xfs, vfat  用户空间的管理工具:mkfs.ext4, mkfs.xfs,mkfs.vfatLinux的虚拟文件系统:VFS查前支持的文件系统:cat /proc/filesystems查前目前的文件系统:lsblk -f 3.2创建文件系统mkfs命令:(1) mkfs.FS_TYPE /dev/DEVICE    ext4    xfs    btrfs    vfat(2) mkfs -t FS_TYPE /dev/DEVICE    -L ‘LABEL’ 设定卷标 mke2fs命令:ext系列文件系统专用管理工具   -t {ext2|ext3|ext4} 指定文件系统类型   -b {1024|2048|4096} 指定块大小   -L ‘LABEL’ 设置卷标   -j 相当于 -t ext3 (mkfs.ext3 = mkfs -t ext3 = mke2fs -j = mke2fs -t ext3)`   -i # 为数据空间中每多少个字节创建一个inode;不应该小于block大小   -N # 指定分区中创建多少个inode   -I 一个inode记录占用的磁盘空间大小,128—4096   -m # 默认5%,为管理人员预留空间占总空间的百分比   -O FEATURE[,…] 启用指定特性   -O ^FEATURE 关闭指定特性 3.3文件系统标签文件系统标签是指向设备的另一种方法。与设备无关blkid:块设备属性信息查看blkid [OPTION]… [DEVICE]   -U UUID 根据指定的UUID来查找对应的设备   -L LABEL 根据指定的LABEL来查找对应的设备e2label:管理ext系列文件系统的LABELe2label DEVICE [LABEL]findfs :查找分区findfs [options] LABEL= < label >findfs [options] UUID= < uuid > 3.4文件系统检测和修复文件系统夹故障常发生于死机或者非正常关机之后,挂载为文件系统标记为“no clean” 注意:一定不要在挂载状态下执行下面命令修复 fsck: File System Check  fsck.FS_TYPE  fsck -t FS_TYPE注意:FS_TYPE 一定要与分区上已经文件类型相同-a 自动修复-r 交互式修复错误 e2fsck:ext系列文件专用的检测修复工具-y 自动回答为yes-f 强制修复-p 自动进行安全的修复文件系统问题xfs_repair:xfs文件系统专用检测修复工具-f 修复文件,而设备-n 只检查-d 允许修复只读的挂载设备,在单用户下修复 / 时使用,然后立即reboot 4 挂载新的文件系统挂载: 将额外文件系统与根文件系统某现存的目录建立起关联关系,进而使得此目录做为其它文件访问入口的行为卸载: 为解除此关联关系的过程把设备关联挂载点:mount Point  mount 设备名 挂载点卸载时:可使用设备,也可以使用挂载点  umount 设备名|挂载点PS:挂载点下原有文件在挂载完成后会被临时隐藏   挂载点目录一般为空 4.1用mount命令挂载文件系统挂载方法:mount DEVICE MOUNT_POINT  mount:通过查看/etc/mtab文件显示当前已挂载的所有设备  mount [-fnrsvw] [-t vfstype] [-o options] device dirdevice:指明要挂载的设备;  (1) 设备文件:例如/dev/sda5  (2) 卷标:-L ‘LABEL’, 例如 -L ‘MYDATA’  (3) UUID, -U ‘UUID’:例如 -U ‘0c50523c-43f1-45e7-85c0-a126711d406e’  (4) 伪文件系统名称:proc, sysfs, devtmpfs, configfsdir:挂载点需事先存在,建议使用空目录;进程正在使用中的设备无法被卸载。mount常用命令选项  -t vsftype 指定要挂载的设备上的文件系统类型  -r readonly,只读挂载  -w read and write, 读写挂载  -n 不更新/etc/mtab,mount不可见  -a 自动挂载所有支持自动挂载的设备(定义在了/etc/fstab文件中,且挂载选项中有auto功能)  -L ‘LABEL’ 以卷标指定挂载设备  -U ‘UUID’ 以UUID指定要挂载的设备  -B, –bind 绑定目录到另一个目录上  查看内核追踪到的已挂载的所有设备:cat /proc/mounts   -o options:(挂载文件系统的选项),多个选项使用逗号分隔      async 异步模式 sync 同步模式,内存更改时,同时写磁盘      包含目录和文件      diratime/nodiratime 目录的访问时间戳      auto/noauto 是否支持自动挂载,是否支持-a选项      exec/noexec 是否支持将文件系统上运行应用程序      dev/nodev 是否支持在此文件系统上使用设备文件      suid/nosuid 是否支持suid和sgid权限      remount 重新挂载      ro 只读 rw 读写      user/nouser 是否允许普通用户挂载此设备,/etc/fstab使用      acl 启用此文件系统上的acl功能      loop 使用loop设备      _netdev 当网络可用时才对网络资源进行挂载,如:NFS文件系统      defaults 相当于rw, suid, dev, exec, auto, nouser, async 4.2卸载命令查看挂载情况  findmnt MOUNT_POINT|device查看正在访问指定文件系统的进程  lsof MOUNT_POINT  fuser -v MOUNT_POINT终止所有在正访问指定的文件系统的进程  fuser -km MOUNT_POINT卸载  umount DEVICE  umount MOUNT_POINT 5 修改/etc/fstab配置文件  使用mount命令挂载设备都是临时挂载,每次开机后需要手动重新挂载,比较费时费力,如果需要实现自动挂载,就要修改文件系统挂载配置文件/etc/fstab文件。/etc/fstab文件 下面的每行定义一个要挂载的文件系统  总共六列,分别对应设备、挂载点、文件系统类型、挂载选项、转储频率及是否自检。 1、要挂载的设备或伪文件系统设备文件LABEL:LABEL=””UUID:UUID=””伪文件系统名称:proc, sysfs 2、挂载点 一般为某文件或目录 3、文件系统类型:ext4,xfs,iso9660,nfs,none 4、挂载选项:defaults(包括rw suidi dev exac auto nouser async) ,acl,bind 5、转储频率:0:不做备份 1:每天转储 2:每隔一天转储 6、fsck检查的文件系统的顺序:允许的数字是0 1 2  0:不自检  1:首先自检;一般只有rootfs才用  2:非rootfs使用  可以使用cat 、echo 等命令 将这6个信息 追加至/etc/fstab文件中,也可以用sed 命令 。   使用mount -a 命令可以立即挂载/etc/fstab中的所有文件系统","categories":[{"name":"linux基础","slug":"linux基础","permalink":"https://wudihechao.github.io/categories/linux基础/"}],"tags":[{"name":"linux","slug":"linux","permalink":"https://wudihechao.github.io/tags/linux/"},{"name":"filesystem","slug":"filesystem","permalink":"https://wudihechao.github.io/tags/filesystem/"}],"keywords":[{"name":"linux基础","slug":"linux基础","permalink":"https://wudihechao.github.io/categories/linux基础/"}]},{"title":"我的一键个性化系统脚本","slug":"我的一键个性化系统脚本","date":"2019-08-11T06:36:15.000Z","updated":"2019-12-04T03:46:12.125Z","comments":true,"path":"/blog/92724e23.html","link":"","permalink":"https://wudihechao.github.io/blog/92724e23.html","excerpt":"  经常要把虚拟机上的系统搞崩,总是各种报错,有次yum安装gcc程序都报错,一旦折腾半天解决不了,只好选择最笨却最有效的方法——还原vrm虚拟机的快照!可是还原快照到干净系统,就导致之前做的喜欢的配置又都没了 还要去慢慢设置开机图案、别名以及各种环境变量比较麻烦,于是就费了点时间,写了下面这个脚本,每次都可以一键实现让新系统恢复自己当初个性化的各种设置。","text":"  经常要把虚拟机上的系统搞崩,总是各种报错,有次yum安装gcc程序都报错,一旦折腾半天解决不了,只好选择最笨却最有效的方法——还原vrm虚拟机的快照!可是还原快照到干净系统,就导致之前做的喜欢的配置又都没了 还要去慢慢设置开机图案、别名以及各种环境变量比较麻烦,于是就费了点时间,写了下面这个脚本,每次都可以一键实现让新系统恢复自己当初个性化的各种设置。  先传效果图:123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228#!/bin/bashread -p "please input a name for the address:" USERDIR#输入用户名,方便在/etc目录下存放生成的配置文件[ -a /etc/$USERDIR ] && [ -d /etc/$USERDIR ] ||{ \\rm -rf /etc/$USERDIR; mkdir /etc/$USERDIR; }#mkdir /etc/$USERDIR#生成配置文件目录,若已存在则直接使用此目录cat > /etc/$USERDIR/motd << ENDoooooooooooo oooooooooo. ooooooooooooo oooo oooooooooo. . 8' 888 '8 '888' 'Y8b 8' 888 '8 '888 '888' 'Y8b .o8 888 'ooooo. 888 888 .ooooo. 888 888 .oo. .ooooo. 888 888 .ooooo. .oooo.o .o888oo 888 d88' '88b 888oooo888' d88' '88b 888 888P"Y88b d88' '88b 888oooo888' d88' '88b d88( "8 888 888 888 888 888 '88b 888ooo888 888 888 888 888ooo888 888 '88b 888ooo888 '"Y88b. 888 888 888 888 888 .88P 888 .o 888 888 888 888 .o 888 .88P 888 .o o. )88b 888 . o888o 'Y8bod8P' o888bood8P' 'Y8bod8P' o888o o888o o888o 'Y8bod8P' o888bood8P' 'Y8bod8P' 8"'888P' "888"END#生成开机欢迎动画,可以随意修改#创建/etc/$USERDIR/login.sh配置文件[ -a /etc/$USERDIR ] && [ -d /etc/$USERDIR ]&& [ -w /etc/$USERDIR ] ||{ \\rm -rf /etc/$USERDIR; mkdir /etc/$USERDIR; }#确认文件目录存在且可用,开始写配置文件。#因为水平有限,不知道怎么把命令以及变量通过脚本生成到新脚本里#只能采用最笨的方法——一行一行的写入,希望各位大佬可以指点一二。echo '#!/bin/bash '>/etc/$USERDIR/login.shecho 'cat /etc/$USERDIR/motd'>>/etc/$USERDIR/login.shecho 'date=`date "+%F %T"`'>>/etc/$USERDIR/login.shecho 'head="System information as of: $date"'>>/etc/$USERDIR/login.shecho ''>>/etc/$USERDIR/login.shecho 'kernel=`uname -r`'>>/etc/$USERDIR/login.shecho 'hostname=`echo $HOSTNAME`'>>/etc/$USERDIR/login.shecho ''>>/etc/$USERDIR/login.shecho '#Cpu load'>>/etc/$USERDIR/login.shecho 'load1=`cat /proc/loadavg | awk '"'"'{print $1}'"'"'`'>>/etc/$USERDIR/login.shecho 'load5=`cat /proc/loadavg | awk '"'"'{print $2}'"'"'`'>>/etc/$USERDIR/login.shecho 'load15=`cat /proc/loadavg | awk '"'"'{print $3}'"'"'`'>>/etc/$USERDIR/login.shecho ''>>/etc/$USERDIR/login.shecho '#System uptime'>>/etc/$USERDIR/login.shecho 'uptime=`cat /proc/uptime | cut -f1 -d.`'>>/etc/$USERDIR/login.shecho 'upDays=$((uptime/60/60/24))'>>/etc/$USERDIR/login.shecho 'upHours=$((uptime/60/60%24))'>>/etc/$USERDIR/login.shecho 'upMins=$((uptime/60%60))'>>/etc/$USERDIR/login.shecho 'upSecs=$((uptime%60))'>>/etc/$USERDIR/login.shecho 'up_lastime=`date -d "$(awk -F. '"'"'{print $1}'"'"' /proc/uptime) second ago" +"%Y-%m-%d %H:%M:%S"`'>>/etc/$USERDIR/login.shecho ''>>/etc/$USERDIR/login.shecho '#Memory Usage'>>/etc/$USERDIR/login.shecho 'mem_usage=`free -m | awk '"'"'/Mem:/{total=$2} /buffers\\/cache/ {used=$3} END {printf("%3.2f%%",used/total*100)}'"'"'`'>>/etc/$USERDIR/login.shecho 'swap_usage=`free -m | awk '"'"'/Swap/{printf "%.2f%",$3/$2*100}'"'"'`'>>/etc/$USERDIR/login.shecho ''>>/etc/$USERDIR/login.shecho '#Processes'>>/etc/$USERDIR/login.shecho 'processes=`ps aux | wc -l`'>>/etc/$USERDIR/login.shecho ''>>/etc/$USERDIR/login.shecho '#User'>>/etc/$USERDIR/login.shecho 'users=`users | wc -w`'>>/etc/$USERDIR/login.shecho 'USER=`whoami`'>>/etc/$USERDIR/login.shecho ''>>/etc/$USERDIR/login.shecho '#System fs usage'>>/etc/$USERDIR/login.shecho 'Filesystem=$(df -h | awk '"'"'/^\\/dev/{print $6}'"'"')'>>/etc/$USERDIR/login.shecho ''>>/etc/$USERDIR/login.shecho '#Interfaces'>>/etc/$USERDIR/login.shecho 'INTERFACES=$(ip -4 ad | grep '"'"'state '"'"' | awk -F":" '"'"'!/^[0-9]*: ?lo/ {print $2}'"'"'|grep e)'>>/etc/$USERDIR/login.shecho 'echo "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"'>>/etc/$USERDIR/login.shecho 'echo "$head"'>>/etc/$USERDIR/login.shecho 'echo "----------------------------------------------"'>>/etc/$USERDIR/login.shecho 'printf "Kernel Version:\\t%s\\n" $kernel'>>/etc/$USERDIR/login.shecho 'printf "HostName:\\t%s\\n" $hostname'>>/etc/$USERDIR/login.shecho 'printf "System Load:\\t%s %s %s\\n" $load1, $load5, $load15'>>/etc/$USERDIR/login.shecho 'printf "System Uptime:\\t%s "days" %s "hours" %s "min" %s "sec"\\n" $upDays $upHours $upMins $upSecs'>>/etc/$USERDIR/login.shecho 'printf "Memory Usage:\\t%s\\t\\t\\tSwap Usage:\\t%s\\n" $mem_usage $swap_usage'>>/etc/$USERDIR/login.shecho 'printf "Login Users:\\t%s\\t\\t\\tWhoami:\\t\\t%s\\n" $users $USER'>>/etc/$USERDIR/login.shecho 'printf "Processes:\\t%s\\n" $processes'>>/etc/$USERDIR/login.shecho 'printf "\\n"'>>/etc/$USERDIR/login.shecho 'printf "Filesystem\\tUsage\\n"'>>/etc/$USERDIR/login.shecho 'for f in $Filesystem'>>/etc/$USERDIR/login.shecho 'do'>>/etc/$USERDIR/login.shecho ' Usage=$(df -h | awk '"'"'{if($NF=="'"'"''"'"''"'"'$f'"'"''"'"''"'"'") print $5}'"'"')'>>/etc/$USERDIR/login.shecho ' echo -e "$f\\t\\t$Usage"'>>/etc/$USERDIR/login.shecho 'done'>>/etc/$USERDIR/login.shecho 'printf "\\n"'>>/etc/$USERDIR/login.shecho 'printf "Interface\\tMAC Address\\t\\tIP Address\\n"'>>/etc/$USERDIR/login.shecho 'for i in $INTERFACES'>>/etc/$USERDIR/login.shecho 'do'>>/etc/$USERDIR/login.shecho ' MAC=$(ip ad show dev $i | grep "link/ether" | awk '"'"'{print $2}'"'"')'>>/etc/$USERDIR/login.shecho ' IP=$(ip ad show dev $i | awk '"'"'/inet / {print $2}'"'"')'>>/etc/$USERDIR/login.shecho ' printf $i"\\t\\t"$MAC"\\t$IP\\n"'>>/etc/$USERDIR/login.shecho 'done'>>/etc/$USERDIR/login.shecho 'echo "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"'>>/etc/$USERDIR/login.shecho 'echo'>>/etc/$USERDIR/login.sh#生成脚本设置常用命令符以及别名及命令历史cat > /etc/profile.d/env.sh << ENDPS1="\\e[1;36m[\\u@\\h \\W]\\\\\\\\$\\e[0m"HISTTIMEFORMAT="%F %T "HISTCONTROL=ignorebothalias rm='mkdir -p /date/delete/`date +%F`;\\mv -t /date/delete/`date +%F` -f'bash /etc/$USERDIR/login.shENDsed -i 's/^alias rm/#&/' ~/.bashrc#取消用户定义的alias rm。#生成VIM的格式脚本cat > ~/.vimrc << END#!/bin/bash""新建.c,.h,.sh,.java文件,自动插入文件头 autocmd BufNewFile *.cpp,*.[ch],*.sh,*.java exec ":call SetTitle()" ""定义函数SetTitle,自动插入文件头 func SetTitle()""如果文件类型为.sh文件 if &filetype == 'sh' call setline(1,"\\##########################################################################") call append(line("."), "\\# File Name: ".expand("%")) call append(line(".")+1,"\\# Author: Name ") call append(line(".")+2,"\\# QQ: xxxxxxxxx") call append(line(".")+3,"\\# mail: [email protected]") call append(line(".")+4,"\\# Description: The test script") call append(line(".")+5,"\\# Created Time: ".strftime("%F %H:%M:%S")) call append(line(".")+6,"\\# Copyright(C): ".strftime("%Y")." All rights reserved") call append(line(".")+7,"\\##########################################################################") call append(line(".")+8,"\\#!/bin/bash") call append(line(".")+9,"") else call setline(1, "/*************************************************************************") call append(line("."), " > File Name: ".expand("%")) call append(line(".")+1, " > Author: Name") call append(line(".")+2, " > Mail: [email protected]") call append(line(".")+3, " > Created Time: ".strftime("%c")) call append(line(".")+4, "************************************************************************/") call append(line(".")+5, "")endif if &filetype == 'cpp' call append(line(".")+6, "#include<iostream>") call append(line(".")+7, "using namespace std;") call append(line(".")+8, "")endif if &filetype == 'c' call append(line(".")+6, "#include<stdio.h>") call append(line(".")+7, "")endif if &filetype == 'java' call append(line(".")+6,"public class ".expand("%")) call append(line(".")+7,"")endif""新建文件后,自动定位到文件末尾endfuncautocmd BufNewFile * normal GENDmkdir -p /etc/yum.repos.d/backup\\mv -f /etc/yum.repos.d/*.repo /etc/yum.repos.d/backup/#禁用已有其他epel源,并做备份。umount /dev/cdrom#[ `cat /etc/fstab|grep -o iso` ]||sed -i '/iso/c\\' /etc/fstab #解挂其他光盘CDUUID=`blkid|sed -nr '/sr0/s/.*\\bUUID="([^"]+).*"/\\1/p'`CDTYPE=`blkid|sed -nr '/sr0/s/.*\\bTYPE="([^"]+).*"/\\1/p'`[ `cat /etc/fstab|grep -o iso` ]||mkdir -p /cdrom #创建光盘目录[ `cat /etc/fstab|grep -o iso` ]||echo -e "UUID=$CDUUID /cdrom $CDTYPE defaults 0 0" >>/etc/fstab #修改fstab文件,方便以后自动挂载光盘#[ `cat /etc/fstab|grep -o iso` ]|| mount -o ro /dev/sr0 /cdrom #挂载光盘#mount -a #挂载光盘mount -o ro /dev/sr0 /cdrom #挂载光盘#生成常用yum源和epel源,这里选用阿里云的cat > /etc/yum.repos.d/cdrom.repo << END[cdrom]name=cdrom-repobaseurl=file:///cdromgpgcheck=0enabled=1ENDcat > /etc/yum.repos.d/aliyun.repo << "END"[aliyun]name=aliyun-epelbaseurl=https://mirrors.aliyun.com/epel/$releasever/$basearch/gpgcheck=1gpgkey=https://mirrors.aliyun.com/epel/RPM-GPG-KEY-EPEL-7enabled=1END#新建本地及阿里云epel源.#修改网卡配置,根据个人情况修改cat > /etc/sysconfig/network-scripts/ifcfg-ens33 << ENDTYPE=EthernetPROXY_METHOD=noneBROWSER_ONLY=noBOOTPROTO=noneDEFROUTE=yesIPV4_FAILURE_FATAL=noIPV6INIT=yesIPV6_AUTOCONF=yesIPV6_DEFROUTE=yesIPV6_FAILURE_FATAL=noIPV6_ADDR_GEN_MODE=stable-privacyNAME=ens33UUID=29126291-418f-4a08-b33e-c5cfa659d9b8DEVICE=ens33ONBOOT=yesIPADDR=172.18.32.7PREFIX=16GATEWAY=172.18.0.1DNS1=114.114.114.114ENDsystemctl restart network.service#重启网络服务,让刚才修改的配置生效mkdir -p /date/apps[ -z `cat ~/.bash_profile|grep apps` ] && echo 'PATH=/date/apps:$PATH' >>~/.bash_profile#修改PAT变量,方便以后装软件。``   水平有限,都是简单代码,留存记录,方便日后查看整理改进。","categories":[{"name":"linux基础","slug":"linux基础","permalink":"https://wudihechao.github.io/categories/linux基础/"}],"tags":[{"name":"script","slug":"script","permalink":"https://wudihechao.github.io/tags/script/"},{"name":"init","slug":"init","permalink":"https://wudihechao.github.io/tags/init/"}],"keywords":[{"name":"linux基础","slug":"linux基础","permalink":"https://wudihechao.github.io/categories/linux基础/"}]},{"title":"由闷骚书生与假正经小姐的古典爱情故事说起","slug":"由闷骚书生与假正经小姐的古典爱情故事说起","date":"2019-08-01T06:37:13.000Z","updated":"2020-02-18T13:21:52.357Z","comments":true,"path":"/blog/1b0b2a49.html","link":"","permalink":"https://wudihechao.github.io/blog/1b0b2a49.html","excerpt":"  城南外湖畔边,天气下着小雨,刮起微风,吹起茶铺门前的风铃叮铃作响让人昏昏欲睡,但铺子里舒缓轻柔的抚琴声却给人自然清醒的感觉,立锥之地,瞥一眼便一览无遗,简单的陈设透露出古朴淡雅,一长发清秀女子品一壶热茶望着这淅淅沥沥的小雨。","text":"  城南外湖畔边,天气下着小雨,刮起微风,吹起茶铺门前的风铃叮铃作响让人昏昏欲睡,但铺子里舒缓轻柔的抚琴声却给人自然清醒的感觉,立锥之地,瞥一眼便一览无遗,简单的陈设透露出古朴淡雅,一长发清秀女子品一壶热茶望着这淅淅沥沥的小雨。  “姑娘有心事?”一身穿白袍英俊书生抿嘴品茶问到。少女:“公子与城北唐公子可是熟识?”  “书生:“我与他乃世交,有何事?”少女:“那下次你与他一同前来我这品茶可好?”书生:“他不喜品茶。”少女:“无妨,那公子可知他喜欢什么,我可以慢慢学。”书生:“我其实也不喜品茶。”少女:“公子莫说谎,不喜为何天天早晨都跑我这茶铺。” var ap = new APlayer({ element: document.getElementById(\"aplayer-addbHWIS\"), narrow: false, autoplay: true, showlrc: false, music: { title: \"我又想你了\", author: \"陈信喆\", url: \"https://hewanyue.com/mp3/woyouxiangnile.mp3\", pic: \"https://hewanyue.com/mp3/cover/woyouxiangnile.jpg\", lrc: \"\" } }); window.aplayers || (window.aplayers = []); window.aplayers.push(ap);   “书生撒下铜钱置于木桌上起身离开,未曾回答。少女见状只心生奇怪,也未曾再问,便转身,从容煎茶。  “书生一路默不作声回到家中书房,文房四宝安静置于桌上,拿起笔挥出:小生不才,未得姑娘青睐,扰姑娘良久,姑娘勿怪,自此所有爱慕之意止于唇齿,匿与年华。饮完这杯酒,还有一杯,就此别过,愿你此生无波澜,敬我余生无悲欢。   “谁又懂书生的心意?但凡经历过那种暗恋相思之苦,才会理解。   “他生莫作有情痴,人间无地著相思。下辈子不要做个多情的人,在人世间的相思之苦是难以承受的。  我们都曾经为一个得不到的人付出过,心酸过,伤神过。那种滋味充满着酸甜苦辣,叫做喜欢过你的感觉。   喜欢一个的人时候,我们每时每刻都会翻看ta的每一条朋友圈动态,也总会把自己给带进去,想看看是不是在说自己,就好像你在做阅读理解一样,做完这些题目后,才发现不是为你准备的。   Ta为你点个赞,你可以开心到不行,小鹿乱撞。什么啊,为什么就给我点赞,ta也喜欢我吗?Ta为什么不跟我表白?Ta这是暗示我要去表白吗?戏精这个词在我们身上表现的淋漓尽致,永远都不会想到是不是别人手滑点赞,因为我们接受不了这个事实。  天天傻傻地期盼他心血来潮的问候一句,然后装作平淡和缓不慌不忙的应答,其实心里能开心一整天。要是没有秒回我的信息,我会觉得自卑,会觉得ta不喜欢跟我这样的人聊天。我曾经发了句晚安给ta,一晚上醒来七次看手机信息。就是那种可怕的朦朦胧胧的意识,梦里都梦到ta好像回了我信息,然后意识带我从梦境里挣扎出来立马去翻看手机。你看,这大概就是喜欢深入骨髓,竟连梦境都不愿放过了吧。   看到ta跟其他人走得近玩的好开心时心里酸的要命,却发现自己根本没有吃醋的立场。Ta对你说过的每一句都有认认真真记在心里,反复琢磨,却不敢表现出来。偶遇的时候心里高兴得飞了出来,却只能冷静的压抑,给ta一个体面的招呼,我们太过卑微的去追求我认为的爱情,没有去想结果怎样。  而事实是这样的结果就像一场游戏,你拼命练到满级,可ta却不知何时会删了游戏。   但暗恋这种感情模式和对方其实关系并不大,更多是自我感动。和真爱没有什么关系,不管付出再多,忍受的再多,也不会有人看见,一个人的独角戏确实很累,你也会变得越来越不像自己,真爱是建立在双方对彼此深入了解的基础上的。   《大话西游》里有一幕:孙悟空与紫霞仙子在围墙上无言告别,围墙下围着一大帮吃瓜群众看戏,曾经我们的喜欢,会让自己误以为是孙悟空或者是至尊宝,其实都不是,我们终究成了围墙下的那帮人,看着别人的爱情,咀嚼着自己的青春。  不知不觉,时间过了很久,我们已经渐渐明白在机场里等船是不会有结果的,有些人我们也不必再等。换个角度看,彼此幸福或许才是最好的结局,未曾拥有的不是遗憾,相反却是青春的美好。   哪一天你回过头看,微笑着对自己说道:那个人,我曾经喜欢过,很喜欢的那种。                                      转载自豆瓣情感私塾      ","categories":[{"name":"生活","slug":"生活","permalink":"https://wudihechao.github.io/categories/生活/"}],"tags":[{"name":"story","slug":"story","permalink":"https://wudihechao.github.io/tags/story/"}],"keywords":[{"name":"生活","slug":"生活","permalink":"https://wudihechao.github.io/categories/生活/"}]},{"title":"自动备份配置文件脚本(screen后台执行)","slug":"自动备份配置文件脚本(screen后台执行)","date":"2019-07-30T03:08:16.000Z","updated":"2019-12-04T03:49:26.893Z","comments":true,"path":"/blog/b580c8f5.html","link":"","permalink":"https://wudihechao.github.io/blog/b580c8f5.html","excerpt":"  Linux中配置文件很多,各种需要设置的各种参数很多,有些自定义选项为了方便使用,都会自己修改使用,不过有时候配置文件或者用户参数属性不小心误删掉或者日后想找回当时的参数设置,没有备份肯定是不行的,所以闲暇之余自己写了一个专门备份/etc目录的脚本,以备不时之需。","text":"  Linux中配置文件很多,各种需要设置的各种参数很多,有些自定义选项为了方便使用,都会自己修改使用,不过有时候配置文件或者用户参数属性不小心误删掉或者日后想找回当时的参数设置,没有备份肯定是不行的,所以闲暇之余自己写了一个专门备份/etc目录的脚本,以备不时之需。  下面贴代码~12345678910111213141516#!/bin/bashecho \"Backup is start!\" #显示备份开始ScreenName=$\"backup\" #设置变量定义窗口名screen -dmS $ScreenName #创建一个出于断开模式下的窗口并指定名字screen -S $ScreenName -p 0 -X stuff $\"\\cp -ap /etc /date/back`date +%F`\" #向窗口传递命令备份/etc目录且命名为当前日期screen -S $ScreenName -p 0 -X stuff $'\\n' #执行命令,相当于回车echo \"backing up,please wait~\" #等待备份完成,并提示等待screen -S $ScreenName -p 0 -X stuff $'exit' #备份完后传递退出窗口命令screen -S $ScreenName -p 0 -X stuff $'\\n' #执行命令echo \"Backup is finshed!\" #提示备份完成,脚本结束。   之前写的脚本总是在创建完screen就停住了,之后的命令没法自动执行,必须Ctrl+A+D暂离screen之后才可以继续执行,不过这样就没法实现自动的初衷了。后来转换思路打算用screen -X的选项从原shell向screen中传递命令却总是失败,后来是参考了大神关于screen传递命令的方法(原文链接),才创建成功的。  此脚本原理大体如下:  脚本中执行screen命令,相当于打开了新的shell,而脚本上的命令都在老shell上,要让备份cp命令在screen上的新shell上跑起来,必须采用向新screen传递命令的方法,才可以实现后台备份,无需担心断网断电的问题。1screen -S $ScreenName -p 0 -X stuff $'cp -ap /etc /date/back`date +%F`'   这条命令单独执行,只会传递在screen中输出cp命令却并不执行,需要screen -S $ScreenName -p 0 -X stuff $'\\n'  命令执行时cp命令才会生效。exit命令亦是如此。  各位大佬如果还有别的更好的方法可以实现功能,希望可以多多交流,不吝赐教。","categories":[{"name":"linux基础","slug":"linux基础","permalink":"https://wudihechao.github.io/categories/linux基础/"}],"tags":[{"name":"script","slug":"script","permalink":"https://wudihechao.github.io/tags/script/"},{"name":"backup","slug":"backup","permalink":"https://wudihechao.github.io/tags/backup/"}],"keywords":[{"name":"linux基础","slug":"linux基础","permalink":"https://wudihechao.github.io/categories/linux基础/"}]},{"title":"VIM编辑器的整理总结","slug":"VIM编辑器的整理总结","date":"2019-07-25T07:38:14.000Z","updated":"2019-12-04T03:49:36.114Z","comments":true,"path":"/blog/8dcd5ad7.html","link":"","permalink":"https://wudihechao.github.io/blog/8dcd5ad7.html","excerpt":"  VIM是linux中功能强大的文本编辑工具,因为功能强大,所以各种参数快捷键也很复杂,为方便记忆,特整理如下:","text":"  VIM是linux中功能强大的文本编辑工具,因为功能强大,所以各种参数快捷键也很复杂,为方便记忆,特整理如下: vim命令基本格式与参数 vim [OPTION] FILE 常见参数: +# :打开文件后,让光标处于第#行,(+ 默认为行尾)。 +/PATTERN :打开文件后,让光标处于第一个被PATTERN匹配到的行的行首。 -b file :以二进制方式打开文件。 -d file1/file2 :比较多个文件的不同。 -m file :以只读方式打开文件。 -e file 或 ex file :直接进入ex模式(扩展命令模式或叫做末行模式)。 vim的几种工作模式 command模式下的光标跳转:字符间跳转:h 左;j下;k 上;l 右。      #command 可以执行#次命令。单词间跳转:w:下一个单词的词首;      e:当前或下一个单词的词尾。      b:当前或前一个单词的词首。      #command 可以执行#次命令。当前页跳转:H:跳转至页首。      M:跳转至页中间行。      L:跳转至页底。      zt:将光别所在行移到屏幕顶端;      zz:将光标所在行移到屏幕中间;      zb:将光标所在行移到屏幕低端。行首行尾跳转:^:跳转至行首的第一个非空的字符;       0:跳转至行首;       $:跳转至行尾。行间移动:#G :移动至第#行行首。(相当于扩展命令模式下 #)      G :移至最后一行行首。   1G 或gg:移至第一行行首。句间移动 : ) : 下一句;     ( : 上一句。段间移动 : } : 下一段;     { : 上一段。翻屏操作:Ctrl+f 向文件尾部翻一屏(向前翻屏);     Ctrl+b 向文件首部翻一屏(向后翻屏);     Ctrl+d 向文件尾部翻半屏(向下翻屏);     Ctrl+u 向文件尾部翻半屏(向上翻屏); command模式下的字符编辑:x : 删除(可认为是剪切,并非真的删除)光标处的字符;#x:删除光标处起始的#个字符;p :在光标所在处的后面插入储存的字符;xp:交换光标所在处的字符及后面字符的位置;~ : 转换大小写;J : 删除当前行后的换行符; 替换命令12r : 替换光标所在处的字符(只能替换一个字符);R: 切换为REPLACE模式,可持续替换多个字符。 删除命令12345678910d : 删除命令(可结合光标跳转字符,实现范围删除);d$: 从光标所在处,删除到行尾;d^: 从光标所在处,删除到非空行首;d0: 从光标所在处,删除到行首;dw: 从光标所在处,删除到下一个单词的词首;de: 从光标所在处,删除到下一个单词的词尾;db: 从光标所在处,删除到前一个单词的词首;dd: 删除光标所在的行;#dd:多行删除;D : 从当前光标位置一直删除到行尾,等同于d$。 改变命令12345678910c : 改变命令(与d命令类似,执行删除后进入插入模式);c$: 从光标所在处,删除到行尾,并进入插入模式;c^: 从光标所在处,删除到非空行首,并进入插入模式;c0: 从光标所在处,删除到行首,并进入插入模式;cw: 从光标所在处,删除到下一个单词的词首,并进入插入模式;ce: 从光标所在处,删除到下一个单词的词尾,并进入插入模式;cb: 从光标所在处,删除到前一个单词的词首,并进入插入模式;cc: 删除光标所在的行,并进入插入模式;#cc:多行删除,并进入插入模式;C : 从当前光标位置一直删除到行尾,并进入插入模式,等同于c$。 复制命令12345678910y : 复制(yank)命令(可结合光标跳转字符,实现范围复制);y$: 从光标所在处,复制到行尾;y^: 从光标所在处,复制到非空行首;y0: 从光标所在处,复制到行首;yw: 从光标所在处,复制到下一个单词的词首;ye: 从光标所在处,复制到下一个单词的词尾;yb: 从光标所在处,复制到前一个单词的词首;yy: 复制光标所在的行;#yy:多行复制; Y : 从当前光标位置一直复制到行尾,等同于y$。 命令模式下常用用法汇总 效果 #ihello[ESC] 插入“hello”#次 0y$ 复制本行 gU 变为大写 gu 变为小写 di” 当光标在” “之间时,则删除” “的内容 yi( 当光标在( )之间时,则复制( )的内容 vi[ 当光标在[ ]之间时,则选中[ ]的内容 dtx 删除字符直到遇到光标之后的第一个x字符 ytx 复制字符直到遇到光标之后的第一个x字符 撤销更改命令123456u : 撤销命令(可撤销最近的更改);#u: 撤销多次之前的更改; U : 撤销光标落在这行后的所有此行的修改。Ctrl+r:重做最后的撤销,取消撤销。. : 重复前一个操作。#.: 重复前一个操作#次。 调整文本颜色1Ctrl+v[[031mCOLORCtrl+v[[0m 输出红色COLOR 取消高亮显示1set nohlsearch 取消搜索后的高亮显示","categories":[{"name":"linux基础","slug":"linux基础","permalink":"https://wudihechao.github.io/categories/linux基础/"}],"tags":[{"name":"linux","slug":"linux","permalink":"https://wudihechao.github.io/tags/linux/"},{"name":"vim编辑器","slug":"vim编辑器","permalink":"https://wudihechao.github.io/tags/vim编辑器/"}],"keywords":[{"name":"linux基础","slug":"linux基础","permalink":"https://wudihechao.github.io/categories/linux基础/"}]},{"title":"Linux下find命令的一般用法。","slug":"Linux下find命令的一般用法。","date":"2019-05-05T09:27:40.000Z","updated":"2019-12-29T02:42:05.807Z","comments":true,"path":"/blog/bc40fcc0.html","link":"","permalink":"https://wudihechao.github.io/blog/bc40fcc0.html","excerpt":"find和locate介绍在linux中,查找文件一般使用locate和find,locate是根据事先构建好的缩影库或者数据库中的数据查找文件名(非实时,速度快,模糊查找),而find作为一个实时查找命令,通过遍历指定起始路径下文件系统层级结构完成文件查找,功能更加强大(实时查找,速度略慢,精确查找)。","text":"find和locate介绍在linux中,查找文件一般使用locate和find,locate是根据事先构建好的缩影库或者数据库中的数据查找文件名(非实时,速度快,模糊查找),而find作为一个实时查找命令,通过遍历指定起始路径下文件系统层级结构完成文件查找,功能更加强大(实时查找,速度略慢,精确查找)。 find 命令一般用法find [OPTIONS] [查找起始路径] [查找条件] [处理动作][查找起始路径] :制定具体搜索目标起始路径;默认为当前目录。[查找条件]:指定的查找标准,可以根据文件名,大小,类型,从属关系,时间戳,权限等标准进行;默认为找出指定目录下的所有文件。[处理动作]:对符合查找条件的文件做出的操作,例如删除等操作;默认为输出至标准输出。 查找条件根据文件名查找:-name,-iname(不区分文件名中的大小写)ps:不支持正则表达式,支持globe风格的通配符如*、?、[]、[^] 根据文件大小查找:-size (+/-) #UNIT 常用单位:c、k、M、G#UNIT :(#-1,#] 为精确查找#大小的文件(大于#-1大小的文件数值显示为#也符合)-#UNIT:[0,#-1] 为查找小于等于#-1大小的文件+#UNTI:(#,∞)为查找大于#大小的文件 根据文件类型查找:-type [文件类型]f:普通文件d:目录文件l:符号链接文件b:块设备文件c:字符设备文件p:管道文件s:套接字文件 根据从属关系查找: -user USERNAME 或 -uid UID -group GROUPNAME 或 -gid GID 查找无属主属组文件:-nouser , -nogroup 根据时间戳查找: 以“天”为单位:-atime(访问时间),-mtime(修改时间)(指文件内容修改),-ctime(改变时间)(指权限及从属关系等修改) 以“分钟”为单位:-amin(访问时间),-mmin(修改时间)(指文件内容修改),-cmin(改变时间)(指权限及从属关系等修改) -atime (+/-) # ,其中#为以现在开始向过去计时的某时间数值。 #:[#,#-1) -#:(#,0] +#:(-∞,#-1) 根据权限查找-perm mode 指明确定权限;-perm /mode 任何一类用户(u,g,o)的权限中的任何一项(r,w,x)符合条件即满足;-perm -mode 每一个用户(u,g,o)的权限中的每一项(r,w,x)都同时符合条件即满足。 |权限|二进制|八进制||:–:|:–:|:–:||- - -|0 0 0|0||- - x|0 0 1|1||- w -|0 1 0|2||- w x|0 1 1|3||r - -|1 0 0|4||r - x|1 0 1|5||r w -|1 1 0|6||r w x|1 1 1|7| mode形式  可以为 -perm /220      也可以为-perm /u+w,g+w 或 -perm /u=w,g=w 组合条件查找:同时满足:与:-a,-and;默认满足任一:或:-o,-or不满足:非:-not,! #] find /… ! A -a ! B →→ #]find /… ! ( A -o B ) 或 #]find /… -not ( A -o B ) #] find /… ! A -o ! B →→ #]find /… ! ( A -a B ) 或 #]find /… -not ( A -a B ) 处理动作-print:输出至标准输出;默认动作-ls:类似对查找到的文件执行“ls-l”-delete:删除查找到的文件-fls /PATH/TO/SOMEFILE 将查找到的文件信息长格式保存至指定路径。-ok COMMAND {} \\; 对找到的每个文件执行COMMAND命令(可能需要确认)-exec COMMAND {} \\; 对找到的每个文件执行COMMAND命定(无需确认)ps:find传递查找到的文件路径之后面的命令时。实现查找出所有符合的文件路径,并一次性传递给后面的命令。但是有些命令不能接受过长的参数,此时命令执行会失效,可用管道find /… | xargs COMMAND执行。","categories":[{"name":"linux基础","slug":"linux基础","permalink":"https://wudihechao.github.io/categories/linux基础/"}],"tags":[{"name":"linux","slug":"linux","permalink":"https://wudihechao.github.io/tags/linux/"},{"name":"find命令","slug":"find命令","permalink":"https://wudihechao.github.io/tags/find命令/"}],"keywords":[{"name":"linux基础","slug":"linux基础","permalink":"https://wudihechao.github.io/categories/linux基础/"}]},{"title":"Hello World","slug":"hello-world","date":"2019-04-08T23:30:00.000Z","updated":"2019-12-04T03:44:40.073Z","comments":true,"path":"/blog/4a17b156.html","link":"","permalink":"https://wudihechao.github.io/blog/4a17b156.html","excerpt":"Welcome to Hexo! This is your very first post. Check documentation for more info. If you get any problems when using Hexo, you can find the answer in troubleshooting or you can ask me on GitHub.","text":"Welcome to Hexo! This is your very first post. Check documentation for more info. If you get any problems when using Hexo, you can find the answer in troubleshooting or you can ask me on GitHub. Quick StartCreate a new post1$ hexo new \"My New Post\" More info: Writing Run server1$ hexo server More info: Server Generate static files1$ hexo generate More info: Generating Deploy to remote sites1$ hexo deploy More info: Deployment","categories":[],"tags":[],"keywords":[]}]}