阅读视图

发现新文章,点击刷新页面。
🔲 ☆

从Ceph集群中删除OSD节点

从集群中删除OSD节点

使用cephadm部署的Ceph集群,再尝试从集群中删除OSD节点的时候遇到了各种问题……

既然自动化工具目前不太完善(或者说在文档里目前还没看到使用cephadm删除OSD节点的方法),那么就手动删除吧!记录一次使用cephadm搭建的集群如何删除OSD节点。当然使用其他方式部署的OSD要删除的话也是大同小异的。

ceph版本:ceph version 15.2.4 (7447c15c6ff58d7fce91843b705a268a1917325c) octopus (stable)

查看集群OSD状态

先查看集群内OSD的状态,并找到要删除的OSD节点。现在准备删除osd.4osd.5这两个节点,他们都在vm2这台主机上。

ceph osd tree

ceph-rm-osd-tree-2020-07-31

ceph osd dump

ceph-rm-osd-dump-2020-07-31

可以留意一下ID,之后便于确认ID所对应的块设备名(如:/dev/sdx或/dev/nvmex)

删除OSD节点节点

使用cephadm删除OSD节点

目前使用cephadm删除OSD节点后也需要手动的释放磁盘占用

ceph orch osd rm <svc_id>... [--replace] [--force]

要删除osd.4osd.5节点则执行:ceph orch osd rm 4 5

删除的过程需要一段时间,会重新分配集群内的PG,删除CRUSH图节点信息,删除这两个OSD所对应的用户和密钥环。

查看自动删除状态进度ceph orch osd rm status,如果长时间无响应或者依旧没有删除,可以开始尝试手动删除OSD节点

手动删除OSD节点

移除daemon守护进程

接下来的操作请确保你对整个集群拥有admin权限并且对要删除OSD节点的主机有root权限。

登录到拥有Ceph集群admin权限的主机,执行如下命令:

ceph orch daemon rm osd.4 osd.5 --force

返回结果如下,已经移除他们容器的守护进程:

Removed osd.4 from host 'vm2'
Removed osd.5 from host 'vm2'

此时查看集群中这两个OSD的状态:

要删除osd.4osd.5这两个节点的状态变成了DOWN。接下来就可以从crush表中将他们移除。

ceph-rm-osd-tree-2-2020-07-31

如果还没有使用cephadm管理集群,此处你可以登录到vm2这台主机中手动的将他们的systemd守护进程disable

systemctl disable --now  ceph-osd@4
systemctl disable --now ceph-osd@5

删除CRUSH图中所对应的OSD节点

删除CRUSH图中的OSD节点信息

ceph osd crush remove osd.4

ceph osd crush remove osd.5

执行成功返回结果:

removed item id 4 name 'osd.4' from crush map
removed item id 5 name 'osd.5' from crush map

此时再次手动检查集群内的OSD节点中是否还包含osd.4osd.5

已经从CRUSH图标中删除,此处已经看不到这两个OSD节点。

ceph-rm-osd-tree-3-2020-07-31

删除osd节点

ceph osd rm osd.4 osd.5

返回执行成功结果:removed osd.4, osd.5

ceph-osd-rm-2020-07-31

删除osd用户

ceph auth ls | grep ^osd -A4

ceph-auth-ls-2020-07-31

发现Ceph的OSD用户未被删除,残留在此处(会影响下一次添加OSD时,导致密钥环不匹配的错误)。执行如下命令删除:

ceph auth rm osd.4

ceph auth rm osd.5

删除OSD配置(如果有的话)

如果额外对OSD进行了额外配置的话需要手动删除当时的配置信息。登录到这台被删除OSD节点的主机上,修改配置文件:vim /etc/ceph/ceph.conf,将对该OSD节点额外配置的信息删除。

之后把更新过的ceph.conf文件要拷贝到其他集群主机的/etc/ceph/目录下。

解除Ceph对磁盘的占用

之后的操作都需要在移除OSD节点的主机(vm2)上通过root用户特权执行操作。

当删除了OSD之后,由于先前的配置,Ceph仍然对磁盘进行占用。需要移除这块OSD磁盘的DM状态,并之后对其格式化,这样才完整的手动移除一个OSD节点(磁盘)。

查询磁盘DM状态并移除编码

dmsetup status

dmset-status-2020-07-31

注意在一开始要留意的哪个ID,在这里用来识别要删除哪个磁盘的DM标记。

dmsetup remove ceph--14c75e65--a33e--459a--8bc7--c95add15d8a3-osd--block--d2af5a38--54b9--4112--9bbf--8ea313c673f1

dmsetup remove ceph--6ceb7c60--8b90--44fc--b159--34bd3d8c8e0f-osd--block--5f1d52fa--e50c--4915--b059--f672684ce571

执行完上面的命令后,再检查一下DM状态,发现已经成功被删除DM标记:

dmset-status-2-2020-07-31

wipefs格式化文件系统

在使用wipefs工具清除文件系统前,要找到所对应的块设备名称。

执行lsblk -f命令:

下面的图片中可以看到,nvme0n2nvme0n3这两个块设备已经没有了像nvme0n4下方的ID标记,所以可以判定nvme0n2nvme0n3和两块磁盘是我们移除的OSD节点所使用的磁盘。

fdisk-f-2020-07-31

接下来对nvme0n2nvme0n3两个块设备格式他们的文件系统,执行命令:

wipefs -a /dev/nvme0n2 /dev/nvme0n3

wipefs-2020-07-31

到此,完成的将OSD节点从集群中移除。你可以使用lsblk工具或稍后一段时间后在拥有集群admin权限的主机上执行ceph orch device ls命令查看这两个OSD节点所使用的磁盘是否是可以用的状态了。现在你可以将这两个磁盘取出或者在这两块磁盘上重新部署OSD节点…

fdisk-f-clean-2020-07-31

ceph-orch-device-ls-2020-07-31

🔲 ☆

初中古诗文必背篇目

语文古诗默写清单

寒 食
唐代-韩翃(hóng)
春城无处不飞花①,寒食东风御柳斜②。
日暮汉宫传蜡烛③,轻烟散入五侯家④。

注解

①春城:暮春时的长安城。
②寒食:古代在清明节前两天的节日,禁火三天,只吃冷食,所以称寒食。御柳:御苑之柳,皇城中的柳树。
③汉宫:这里指唐朝皇宫。传蜡烛:寒食节普天下禁火,但权贵宠臣可得到皇帝恩赐而得到燃烛。《唐辇下岁时记》“清明日取榆柳之火以赐近臣”。
④五侯:汉成帝时封王皇后的五个兄弟王谭、王商、王立、王根、王逢时皆为候,受到特别的恩宠。这里泛指天子近幸之臣。
⑤飞花:指柳絮

白话文译文

暮春时节,长安城处处柳絮飞舞、落红无数,寒食节的东风吹拂着皇家花园的柳枝。夜色降临,宫里忙着传蜡烛,点蜡烛的轻烟散入王侯贵戚的家里。

创作背景

寒食是中国古代一个传统节日,一般在冬至后一百零五天,清明前两天。古人很重视这个节日,按风俗家家禁火,只吃现成食物,故名寒食。唐代制度,到清明这天,皇帝宣旨取榆柳之火赏赐近臣,以示皇恩。这仪式用意有二:一是标志着寒食节已结束,可以用火了;二是藉此给臣子官吏们提个醒,让大家向有功也不受禄的介子推学习,勤政为民。唐代诗人窦叔向有《寒食日恩赐火》诗纪其实:“恩光及小臣,华烛忽惊春。电影随中使,星辉拂路人。幸因榆柳暖,一照草茅贫。”正可与韩翃这一首诗参照。
中唐以后,几任昏君都宠幸宦官,以致他们的权势很大,败坏朝政,排斥朝官,正直人士对此都极为愤慨。有意见认为此诗正是因此而发。

马诗

马诗·大漠沙如雪(《马诗二十三首》的第五篇)
唐代-李贺
大漠沙如雪⑴,燕山月似钩⑵。
何当金络脑⑶,快走踏清秋⑷。

注释

⑴大漠:广大的沙漠。
⑵燕山:在河北省。一说为燕然山,即今之杭爱山,在蒙古人民共和国西部。钩:古代兵器。
⑶何当:什么时候。金络脑:即金络头,用黄金装饰的马笼头。
⑷踏:走,跑。此处有“奔驰”之意。清秋:清朗的秋天。

白话文译文

平沙覆盖着大漠,犹如无边的积雪,月亮高悬在燕山上,恰似一把弯钩。什么时候我能给它带上金络头,飞快奔驰着,踏遍这清爽秋日时的原野!

创作背景

作者所处的贞元(785~805)、元和(806~820)之际,正是藩镇极为跋扈的时代,诗中“燕山”暗示的幽州蓟门一带又是藩镇肆虐为时最久、为祸最烈的地带,作者希望能扫除战乱,建功立业,但终是不被赏识。对马有所偏爱的作者或许受伯乐识马所启,结合自己怀才不遇的现实,带着愤懑之情创作了此诗。

石灰吟

石灰吟
明代-于谦
千锤万凿出深山,烈火焚烧若等闲。
粉骨碎身浑不怕,要留清白在人间。

注释

1.石灰吟:赞颂石灰。吟:吟颂,指古代诗歌体裁的一种名称(古代诗歌的一种形式)。
2.千锤万凿:也作“千锤万击”或“千鎚万击”;指无数次的锤击开凿,形容开采石灰非常艰难。千、万:虚词,形容很多。锤:锤打。凿:开凿。
3.若等闲:好像很平常的事情。若:好像、好似;等闲:平常,轻松。
4.粉骨碎身:也作“粉身碎骨”;浑:亦作“全”;怕:也作“惜”。
5.清白:指石灰洁白的本色,又比喻高尚的节操。人间:人世间。

白话文译文

石灰石只有经过千万次锤打才能从深山里开采出来,它把熊熊烈火的焚烧当作很平常的一件事。即使粉身碎骨也毫不惧怕,甘愿把一身清白留在人世间。

创作背景

于谦从小学习刻苦,志向远大。相传有一天,他信步走到一座石灰窑前,观看师傅们煅烧石灰。只见一堆堆青黑色的山石,经过熊熊的烈火焚烧之后,都变成了白色的石灰。他深有感触,略加思索之后便写下了此诗。据说此时的于谦才十二岁,他写下这首诗不只是石灰形象的写照,更是他日后的人生追求。

十五夜望月寄杜郎中

十五夜望月寄杜郎中⑴
中唐-王建
中庭地白树栖鸦⑵,冷露无声湿桂花⑶。
今夜月明人尽望⑷,不知秋思落谁家⑸

注解

⑴十五夜:指农历八月十五的晚上,即中秋夜。杜郎中:即杜元颖。
⑵中庭:即庭中,庭院中。地白:指月光照在庭院的样子。
⑶冷露:秋天的冰冷的露水。
⑷尽:都。
⑸秋思(sī):秋天的情思,这里指怀人的思绪。落:在,到

白话译文

庭院地面雪白树上栖息着鹊鸦,秋露点点无声打湿了院中桂花。今夜明月当空世间人人都仰望,不知道这秋日情思可落到谁家?

创作背景

此诗是诗人在中秋佳节与朋友相聚时所作。诗题为“十五夜望月寄杜郎中”,可见是寄友人杜元颖的。原诗诗题下注云:“时会琴客”,说明佳节良友相聚,并非独吟。

迢迢牵牛星

迢迢牵牛星
东汉-???
迢迢牵牛星,皎皎河汉女。
纤纤擢素手,札札弄机杼。
终日不成章,泣涕零如雨。
河汉清且浅,相去复几许?
盈盈一水间,脉脉不得语。

注解

迢(tiáo)迢:遥远的样子。牵牛星:河鼓三星之一,隔银河和织女星相对,俗称“牛郎星”,是天鹰星座的主星,在银河东。
皎皎:明亮的样子。河汉女:指织女星,是天琴星座的主星,在银河西,与牵牛星隔河相对。河汉,即银河。
纤纤:纤细柔长的样子。擢(zhuó):引,抽,接近伸出的意思。素:洁白。
札(zhá)札:象声词,机织声。弄:摆弄。杼(zhù):织布机上的梭子。
章:指布帛上的经纬纹理,这里指整幅的布帛。此句是用《诗经·小雅·大东》语意,说织女终日也织不成布。《诗经》原意是织女徒有虚名,不会织布。而这里则是说织女因相思,而无心织布。
涕:眼泪。零:落下。
清且浅:清又浅。
相去:相离,相隔。去,离。复几许:又能有多远。
盈盈:水清澈、晶莹的样子。一说形容织女,《文选》六臣注:“盈盈,端丽貌。”一水:指银河。间(jiàn):间隔。
脉(mò)脉:含情相视的样子。一作“默默”,默默地用眼神或行动表达情意。

白话译文

那遥远而亮洁的牵牛星,那皎洁而遥远的织女星。
织女正摆动柔长洁白的双手,织布机札札地响个不停。
因为相思而整天也织不出什么花样,她哭泣的泪水零落如雨。
只隔了道清清浅浅的银河,两界相离也没有多远。
相隔在清清浅浅的银河两边,含情脉脉(mò)相视无言地痴痴凝望。

创作背景

在中国关于牵牛和织女的民间故事起源很早。《古诗十九首》中的这首《迢迢牵牛星》写牵牛织女夫妇的离隔,它的时代在东汉后期,略早于曹丕和曹植。将这首诗和曹氏兄弟的作品加以对照,可以看出,在东汉末年到魏这段时间里牵牛和织女的故事大概已经定型了。

早春呈水部张十八员外

早春呈水部张十八员外(一)
唐代-韩愈
天街小雨润如酥⑵,草色遥看近却无。
最是一年春好处⑶,绝胜烟柳满皇都⑷。

注解

⑴呈:恭敬地送给。水部张十八员外:指张籍(766—830年)唐代诗人。在同族兄弟中排行第十八,曾任水部员外郎。
⑵天街:京城街道。润如酥:细腻如酥。酥,动物的油,这里形容春雨的细腻。
⑵最是:正是。处:时。
⑷绝胜:远远胜过。皇都:帝都,这里指长安。
⑸官忙身老大:韩愈写此诗时任吏部侍郎,公务繁忙,故云“官忙”;韩愈时年56岁,故云“身老大”。身老大,年纪大。
⑹即:已经。
⑺凭:这里作“请”讲。江:曲江,位于唐代京城东南角,为游览胜地,遗址在今陕西西安东南部。

白话译文

京城大道上空丝雨纷纷,它像酥油般细密而滋润,远望草色依稀连成一片,近看时却显得稀疏零星。这是一年中最美的季节,远胜过绿柳满城的春末。

创作背景

此诗作于唐穆宗长庆三年(823年)早春。当时韩愈已经56岁,任吏部侍郎。虽然时间不长,但此时心情很好。此前不久,镇州(今河北正定)藩镇叛乱,韩愈奉命前往宣抚,说服叛军,平息了一场叛乱。穆宗非常高兴,把他从兵部侍郎任上调为吏部侍郎。在文学方面,他早已声名大振。同时在复兴儒学的事业中,他也卓有建树。因此,虽然年近花甲,却不因岁月如流而悲伤,而是兴味盎然地迎接春天。
此诗是写给当时任水部员外郎的诗人张籍的。张籍在兄弟辈中排行十八,故称“张十八”。大约韩愈约张籍游春,张籍因以事忙年老推辞,韩愈于是作这首诗寄赠,极言早春景色之美,希望触发张籍的游兴。

春夜喜雨

春夜喜雨
唐代-杜甫
好雨知时节⑴,当春乃发生⑵。
随风潜入夜⑶,润物细无声⑷。
野径云俱黑⑸,江船火独明⑹。
晓看红湿处⑺,花重锦官城⑻。

注解

⑴知:明白,知道。说雨知时节,是一种拟人化的写法。
⑵乃:就。发生:萌发生长。
⑶潜:暗暗地,悄悄地。这里指春雨在夜里悄悄地随风而至。
⑷润物:使植物受到雨水的滋养。
⑸野径:田野间的小路。
⑹“江船”句:意谓连江上的船只都看不见,只能看见江船上的点点灯火,暗示雨意正浓。
⑺晓:天刚亮的时候。红湿处:雨水湿润的花丛。
⑻花重(zhòng):花因为饱含雨水而显得沉重。锦官城:故址在今成都市南,亦称锦城。三国蜀汉时管理织锦之官驻此,故名。后人有用作成都的别称。此句是说露水盈花的美景。

白话译文

好雨是知道该下雨的时节的,正好下在春天植物萌发生长的时候。
它随着春风在夜里悄悄地落下,悄然无声地滋润着大地万物。
在雨夜,野外的小路和乌云都是黑茫茫的,只有江船上的灯火格外明亮。
天亮后,去看这带着雨的娇美红艳的花朵,整个锦官城变成了沉甸甸的鲜花盛开的世界。

创作背景

这首诗写于唐肃宗上元二年(761)春。杜甫在经过一段时间的流离转徙的生活后,终因陕西旱灾而来到四川成都定居,开始了在蜀中的一段较为安定的生活。作此诗时,他已在成都草堂定居两年。他亲自耕作,种菜养花,与农民交往,对春雨之情很深,因而写下了这首描写春夜降雨、润泽万物的美景诗作。

送元二使安西

送元二使安西
唐代-王维
渭城朝雨浥轻尘⑵,客舍青青柳色新⑶。
劝君更尽一杯酒⑷,西出阳关无故人⑸。

注解

⑴元二:姓元,排行第二,作者的朋友。使:出使。安西:指唐代安西都护府,治所在龟兹城(今新疆库车)。
⑵渭城:即秦代咸阳古城,汉改渭城。朝(zhāo)雨:早晨下的雨。浥(yì):湿。
⑶客舍:驿馆,旅馆。柳色:柳树象征离别。
⑷更尽:再喝干,再喝完。
⑸阳关:在今甘肃省敦煌西南,为古代通西域的要道。故人:老朋友。

白话译文

渭城清晨的细雨打湿了路边尘土,客舍边的杨柳愈发显得翠绿清新。 劝君再饮下这杯离别的美酒,向西出了阳关就再难遇到故人。

创作背景

此诗是王维送朋友去西北边疆时作的诗,后有乐人谱曲,名为“阳关三叠”,又名“渭城曲”,大约作于安史之乱前。其送行之地是渭城。诗人送友人元二远赴安西都护府,从长安一带送到渭城客舍,到了最后分手之地,作这首七绝送别。

江上渔者

江上渔者
北宋-范仲淹
江上往来人,但2爱3鲈鱼4美。
君5看一叶舟6,出没7风波8里。

注解

1.渔者:捕鱼的人。
2.但:只
3.爱:喜欢
4.鲈鱼:一种头大口大、体扁鳞细、背青腹白、 味道鲜美的鱼,生活在近岸浅海夏秋进入淡水河川后,肉更肥美,尤以松江所产最为名贵。
5.君:你。
6.一叶舟:像漂浮在水上的一片树叶似的小船。
7.出没:若隐若现。指一会儿看得见,一会儿看不见。
8.风波:波浪。

白话译文

江上来来往往的人们,只喜爱鲈鱼的美味。你看那渔人驾着像树叶一样的小舟,在大风大浪里时出时没。

小雅·采薇

小雅·采薇
西周-《诗经》
采薇采薇,薇亦作止2。曰归曰归3,岁亦莫止4。 靡室靡家5,猃狁之故6。不遑启居7,猃狁之故。
采薇采薇,薇亦柔止8。曰归曰归,心亦忧止。 忧心烈烈9,载饥载渴10。我戍未定11,靡使归聘12。
采薇采薇,薇亦刚止13。曰归曰归,岁亦阳止14。 王事靡盬15,不遑启处16。忧心孔疚17,我行不来18!
彼尔维何?维常之华19。彼路斯何20?君子之车21。 戎车既驾22,四牡业业23。岂敢定居24?一月三捷25。
驾彼四牡,四牡骙骙26。君子所依,小人所腓27。 四牡翼翼28,象弭鱼服29。岂不日戒30?猃狁孔棘31!

昔我往矣32,杨柳依依33。今我来思34,雨雪霏霏35。 行道迟迟36,载渴载饥。我心伤悲,莫知我哀!

注解

小雅:《诗经》中“雅”部分,分为大雅、小雅,合称“二雅”。雅,雅乐,即正调,指当时西周都城镐京地区的诗歌乐调。小雅部分今存七十四篇。薇:豆科野豌豆属的一种,学名救荒野豌豆,又叫大巢菜,种子、茎、叶均可食用。

作:指薇菜冒出地面。止:句末助词,无实义。

曰:句首、句中助词,无实义。

莫(mù):通“暮”,此指年末。

靡(mǐ)室靡家:没有正常的家庭生活。靡,无。室,与“家”义同。

猃(xiǎn)狁(yǔn):中国古代少数民族名。

不遑(huáng):不暇。遑,闲暇。启居:跪、坐,指休息、休整。启,跪、跪坐。居,安坐、安居。古人席地而坐,两膝着席,危坐时腰部伸直,臀部与足离开;安坐时臀部贴在足跟上。

柔:柔嫩。“柔”比“作”更进一步生长。指刚长出来的薇菜柔嫩的样子。

烈烈:炽烈,形容忧心如焚。

载(zài)饥载渴:则饥则渴,又饥又渴。载,又。

戍:防守,这里指防守的地点。

聘(pìn):问候的音信。

刚:坚硬。

阳:农历十月,小阳春季节。今犹言“十月小阳春”。

靡:无。盬(gǔ):止息,了结。

启处:休整,休息。

孔:甚,很。疚:病,苦痛。

我行不来:我不能回家。一说我从军出发后还没有人来慰问过。

常:常棣(棠棣),既芣苡,植物名。

路:高大的战车:斯何,犹言维何。斯,语气助词,无实义。

君子:指将帅。

戎:车,兵车。

牡:雄马。业业:高大的样子。

定居:犹言安居。

捷:胜利。谓接战、交战。一说邪出,指改道行军。此句意谓,一月多次行军。

骙(kuí):雄强,威武。这里的骙骙是指马强壮的意思。

小人:指士兵。腓(féi):庇护,掩护。

翼翼:整齐的样子。谓马训练有素。

象弭(mǐ):以象牙装饰弓端的弭。弭,弓的一种,其两端饰以骨角。一说弓两头的弯曲处。鱼服:鲨鱼鱼皮制的箭袋。

日戒:日日警惕戒备。

孔棘(jí):很紧急。棘,急。

昔:从前,文中指出征时。往:当初从军。

依依:形容柳丝轻柔、随风摇曳的样子。

思:用在句末,没有实在意义。

雨(yù)雪:下雨。雨,这里作动词。霏(fēi)霏:雪花纷落的样子。

迟迟:迟缓的样子。

白话译文

采薇采薇一把把,薇菜新芽已长大。说回家呀道回家,眼看一年又完啦。有家等于没有家,为跟玁狁去厮杀。没有空闲来坐下,为跟玁狁来厮杀。

采薇采薇一把把,薇菜柔嫩初发芽。说回家呀道回家,心里忧闷多牵挂。满腔愁绪火辣辣,又饥又渴真苦煞。防地调动难定下,书信托谁捎回家!

采薇采薇一把把,薇菜已老发杈枒。说回家呀道回家,转眼十月又到啦。王室差事没个罢,想要休息没闲暇。满怀忧愁太痛苦,生怕从此不回家。

什么花儿开得盛?棠棣花开密层层。什么车儿高又大?高大战车将军乘。驾起兵车要出战,四匹壮马齐奔腾。边地怎敢图安居?一月要争几回胜!

驾起四匹大公马,马儿雄骏高又大。将军威武倚车立,兵士掩护也靠它。四匹马儿多齐整,鱼皮箭袋雕弓挂。哪有一天不戒备,军情紧急不卸甲!

回想当初出征时,杨柳依依随风吹;如今回来路途中,大雪纷纷满天飞。道路泥泞难行走,又渴又饥真劳累。满心伤感满腔悲。我的哀痛谁体会!

创作背景

《采薇》是出自《诗经·小雅·鹿鸣之什》中的一篇。历代注者关于它的写作年代说法不一。但据它的内容和其它历史记载的考订大约是周宣王时代的作品的可能性大些。周代北方的猃狁(即后来的匈奴)已十分强悍,经常入侵中原,给当时北方人民生活带来不少灾难。历史上有不少周天子派兵戍守边外和命将士出兵打败猃狁的记载。从《采薇》的内容看,当是将士戍役劳还时之作。诗中唱出从军将士的艰辛生活和思归的情怀。

有关《采薇》这一首诗的背景,历来众说纷纭。据毛序为:“《采薇》,遣戍役也。文王之时,西有昆夷之患,北有猃狁之难。以天子之命,命将率遣戍役,以守卫中国。故歌《采薇》以遣之。”其实,真正的经典,无一例外都有着穿越时空的魅力。它曾经如此真切细微地属于一个人,但又如此博大深厚地属于每一个人。所以,纵然这首诗背后的那一场战争的烽烟早已在历史里淡去,而这首由戍边战士唱出来的苍凉的歌谣却依然能被每一个人编织进自己的生命里,让人们在这条民歌的河流里看见时间,也看到自己的身影。所以面对《采薇》,与其观世,不如观思;与其感受历史,不如感受生命。

竹石

竹石
清代-郑燮(郑板桥)
咬定青山不放松1,立根原在破岩中2。
千磨万击还坚劲3,任尔东西南北风4。

注解

咬定:咬紧
立根:扎根。破岩:裂开的山岩,即岩石的缝隙。
千磨万击:指无数的磨难和打击。坚劲:坚强有力。
任:任凭,无论,不管。尔:你

白话译文

紧紧咬定青山不放松,原本深深扎根石缝中。
千磨万击身骨仍坚劲,任凭你刮东西南北风。

创作背景

这是一首题画诗,原题《竹石》,是郑板桥题自己画的竹石图的。

竹石

泊船瓜洲

泊船瓜洲
宋代-王安石
京口瓜洲一水间,钟山只隔数重山。
春风又绿江南岸,明月何时照我还?

注解

泊船:停船。泊,停泊。指停泊靠岸。

京口:古城名。故址在江苏镇江市。

瓜洲:镇名,在长江北岸,扬州南郊,即今扬州市南部长江边,京杭运河分支入江处。

一水:一条河。古人除将黄河特称为“河”,长江特称为“江”之外,大多数情况下称河流为“水”,如汝水、汉水、浙水、湘水、澧水等。这里的“一水”指长江。一水间指一水相隔之间。

间:根据平仄来认读jiàn四声。

钟山:今南京市紫金山。

绿:吹绿,拂绿。

还:回。

白话译文

京口和瓜洲之间只隔着一条长江,我所居住的钟山隐没在几座山峦的后面。暖和的春风啊,吹绿了江南的田野,明月什么时候才能照着我回到钟山下的家里?

夜雨寄北

夜雨寄北
唐代(晚)-李商隐
君2问归期3未有期,
巴山4夜雨涨秋池5。
何当6共7剪西窗烛8,
却话9巴山夜雨时。

注解

寄北:写诗寄给北方的人。诗人当时在巴蜀(现在四川省),他的亲友在长安,所以说“寄北”。这首诗表达了诗人对亲友的深刻怀念。
君:对对方的尊称,等于现代汉语中的“您”。 [2]
归期:指回家的日期。
巴山:指缙云山 [3] ,在陕西南部和四川东北交界处。这里泛指巴蜀一带。 [2]
秋池:秋天的池塘。
何当:什么时候。
共:副词,用在谓语前,表示动作行为是由两个或几个施事者共同发生的。可译为“一起”。 [2]
剪西窗烛:剪烛,剪去燃焦的烛芯,使灯光明亮。这里形容深夜秉烛长谈。“西窗话雨”“西窗剪烛”用作成语,所指也不限于夫妇,有时也用以写朋友间的思念之情。 [2]
却话:回头说,追述。

白话译文

你问我何时回家,我回家的日期定不下来啊!我此时唯一能告诉你的,就是这正在盛满秋池的绵绵不尽的巴山夜雨了。如果有那么一天,我们一齐坐在家里的西窗下,共剪烛花,相互倾诉今宵巴山夜雨中的思念之情,那该多好!

创作背景

这首诗选自《玉溪生诗》卷三,是李商隐留滞巴蜀(今四川省)时寄怀长安亲友之作。因为长安在巴蜀之北,故题作《夜雨寄北》。

在南宋洪迈编的《万首唐人绝句》里,这首诗的题目为《夜雨寄内》,意思是诗是寄给妻子的。他们认为,李商隐于大中五年(851)七月赴东川节度使柳仲郢梓州幕府,而王氏是在这一年的夏秋之交病故,李商隐过了几个月才得知妻子的死讯。

现传李诗各本题作《夜雨寄北》,“北”就是北方的人,可以指妻子,也可以指朋友。有人经过考证认为它作于作者的妻子王氏去世之后,因而不是“寄内”诗,而是写赠长安友人的。

就诗的内容看,按“寄内”解,便情思委曲,悱恻缠绵;作“寄北”看,便嫌细腻恬淡,未免纤弱。

🔲 ☆

Ansible 技巧之场

Ansible 技巧之场

看到技巧之场,我就想起来被百万氪金王支配的恐惧!

拼接主机与http或其他字符串

有时候我们有一个列表,里面是IP地址。但是有时候我们要将http://拼接在每个IP地址的开头,可以利用Jinja2语法进行操作。

在模版中:

{% set http_hosts=[] %} # 定一个list变量
{% set hosts_list = discovery_seed_hosts | flatten %} # 将列表扁平化,因为这个变量列表里面存在嵌套的情况
{% for host in hosts_list %}
{% set _ = http_hosts.append("http://%s:%s" % (host,9200)) %} #使用Python语法进行操作。
{% endfor %}

在Plybook中(从网上找到的未经过测试,理论可行):

set_fact:
cluster_address: |
[
{% set pxc_hosts = groups.pxc %}
{% for host in pxc_hosts %}
{% set pxc_ip = hostvars[host].inventory_hostname | default(host) %}
{% set pxc_port = group_port %}
"{{ pxc_ip }}:{{ pxc_port }}"
{% endfor -}
]

# 使用的时候
{{ cluster_address | join(',') }}' # 使用join 过滤器可生成',' 分隔的字符串,分隔符自己定义

对字符串的操作

得益于Python特性,在Ansible中可以使用Python中处理字符串的语法。

你可以对字符串进行取单个字符等操作: {{ vars[2] }}

也可以切片: {{ vars[3:6]}}

判断某些字符是否在字符串中: when: "'haha' in vars"

使用加号连接两个字符串: {{ var1+var2 }}

使用乘号*连续输出字符串: {{ var1*3 / 'la'*nums }}

使用find函数查找:

when: {{ vars.find('test') == -1 }} # 值为

find函数的语法是find(string,begin,end)

when: {{ vars.find('test',5) == -1 }} # 从第5位开始查找

任务委派

有时候你想让某一个tasks在指定的某一个受控主机上运行而不是要在Play中指定的组或主机中全部运行这个tasks。

这种情况下可以新写一个Play,然后将hosts和tasks进行指定。但是这样要新建一个Play,有点麻烦。

可以使用delegate_to关键字来让某一个tasks指定运行在某一台主机上,任务委派时不会去看目标主机是否在inventory主机清单中。可以指定代码块进行任务委派。

- debug:
msg: "only host"
delegate_to: 192.168.1.196

---

ok: [192.168.1.39 -> 192.168.1.196] => {
"msg": "only host"
}

仅在主机上执行

如果指向让某一个任务在本主机上运行。你可以使用刚刚的任务委派,来指向Ansible控制节点。也可以尝试connection: local关键字。

connection关键字也可以用于代码块和任务。

- block:
- systemd:
name: elasticsearch
state: restarted
connection: local

仅运行一遍任务

在某些情况下,下载资源到本地时,你只需要下载一次就可以。但是任务却执行和主机清单中相同的次数。得益于Ansible模块幂等性,虽然说任务运行很多次,但只有第一次真正进行了下载,对我们的影响不大。但是能否继续减少性能开支呢。

使用run_once: true关键字让任务或代码块只运行一次。

- hosts: all
  gather_facts: no
  tasks:
  - get_url:
      url: "https://github.com/rofl0r/proxychains-ng/archive/v4.14.tar.gz"
      dest: "/tmp"
    connection: local
    run_once: true
 
  - copy:
      src: "/tmp/v4.14.tar.gz"
      dest: "/tmp"

遇到过的错误…

在when关键字中使用了Jinja2语法

[WARNING]: conditional statements should not include jinja2 templating delimiters such as {{ }} or {% %}. Found:XXX

在when关键字中不应该包含Jinja2语法。在使用循环或者变量时不小心就会顺手把花括号加上。此时会发出警告。

错误用法:
when: "'{{item.hosts}}' in group_names"

正确用法:
when: item.hosts in group_names

没有获取事实就要使用hostvars获取变量

在主机没有gether facts事实之前,是无法通过hostvars来获取它的事实变量的。这个错误在生成hosts文件时出现过一次,折腾了好久。难受的是没有指向正确的报错位置,这导致我在错误的方向研究了好久都没有解决。

错误用法:
when: "'{{item.hosts}}' in group_names"

---

正确用法:
when: item.hosts in group_names
🔲 ☆

Ansible 包含和导入文件

Ansible 包含和导入文件

如果Playbook很长或很复杂,可以尝试将其分成较小的文件以便与管理。适宜食用模块化方式将多个Playbook组合成一个主要的Playbook,或者将文件中的任务列表插入到Play中。和杨可以更轻便、更加便于管理的操作项目中的Play和任务序列。

包含与导入文件概念区别

Ansible支持两种方式将内容带入到Playbook,可以使用包含内容,也可以使用导入内容。

包含内容(include)是一个动态的过程。在playbook运行期间,Ansible会在内容到达时再去处理所包含的内容。

导入内容(import)是一个静态操作。在最开始的时候Ansible运行Playbook时就将它解析好并导入到要执行的这个Playbook中了。

导入Playbook

使用import_playbook指令可以将外部的Playbook导入到当前的Playbook中。

导入的Playbook是一个完成的playbook,因此要使用import_playbook指令需要在Play的位置导入,而不能在tasks处进行导入Playbook。如果你导入了多喝playbook,则将按照导入的顺序一次运行.

---
- name: Play1
import_playbook: web.yml

- name: Play2
hosts: localhost
tasks:
- debug:
msg: This is Play 2

- name: Play3
import_playbook: db.yml
...

在上面的例子中,会一次执行三个Play,并且导入操作可以和展开的Play穿插使用。

导入和包含任务

可以将任务文件中的任务列表导入或包含在Play中。任务文件是包含一个任务平面列表的文件:

vim tasks_firewalld.yml

- name: Install the firewall
yum:
name: firewalld
state: latest

- name: Start the firewall
service:
state: started
name: firewalld
enabled: true

导入任务文件

使用import_tasks功能将任务文件静态导入Playbook的Play中。在一开始解析Playbook时就会将该任务文件插入到Play中。

- name: Play
hosts: localhost
tasks:
- debug:
msg: This is Play
- name: I'm Import Task
import_tasks: db.yml
  • 使用import_tasks功能时,导入时设置的when等条件语句将应用于导入的每个任务。
  • 使用import_tasks功能时,无法将loop循环应用在上面。
  • 如果使用变量来指定要导入的文件的名称,则无法使用主机或组清单变量。

包含任务文件

使用include_tasks可以将任务文件动态包含进Tasks中。

- name: Play
hosts: localhost
tasks:
include_tasks: db.yml

在Play运行到达该Tasks前,Ansible不会处理该任务文件的任何内容。使用include_tasks时,包含时设置的when等条件语句将确定任务是否包含在Play中。

🔲 ☆

Ansible 学习文档

Ansible 学习文档

Ansible自动化运维学习。大部分操作及示例基于RHEL8实现。

文档未进行过任何校对和查错,目前仅用于个人学习使用。文档中可能会包含错误,请您雅正,谢谢。

最近一次的更新日期:2020年6月27日

本次更新内容:继续完成《Ansible 常用模块》 ⭕️

近期持续更新Ansible常用模块。

文档列表

更新日志

2020年6月27日

  • 继续完成《Ansible 常用模块》 ⭕️

2020年6月22日

  • 完成《Ansible 技巧之场》✅

2020年6月18日

  • 修复《Ansible 任务控制》中部分Markdown解析错误

2020年6月16日

  • Ansible角色 ✅
  • 添加部分刚刚没有同步到文档中的内容
  • 添加软件包管理模块使用
🔲 ☆

Ansible 主机模式

Ansible 主机模式

通过主机模式,可以更高效地为play或命令选择受控主机。除了常规的使用组名或清单中的IP地址以及all、ungrouped外,你还可以使用以下方式匹配受控主机。

使用通配符匹配

使用'*'匹配模式会与all主机模式具有相同的效果,它会匹配任意字符串,会使用清单中的全部主机。

- hosts: all
...
- hosts: '*'

也可以使用*与文字进行匹配,使用通配符匹配以.example.com结尾的全部主机例子如下:

- hosts: '*.example.com'

匹配192.168开头的全部主机或主机组的名称:

- hosts: '192.168*'

⚠️注意:使用匹配模式匹配时,Ansible不会区分它是主机还是组的名字。如果主机的域名和组名有被匹配到,那么不管是主机还是主机组中全部的主机都会被匹配。

使用逗号匹配多个主机和组

使用逗号可以来匹配多个主机和组,可以混合使用受控主机、主机组和通配符。

- hosts: 'server1.com,*.example.com,192.168.1.1'

也可以使用:冒号作为分隔符进行分隔,特别是将IPv6地址用作受管主机名称时。不过在较老的例子中依旧可以见到使用冒号进行分隔。

使用&!

如果在使用逗号的列表中某一项与&结合,则主机必须与该项也匹配才会匹配主机模式。

如下例子,主机模式匹配同时出现在lab组和datacenter组中的主机才能作为受控主机。

- hosts: 'lab,&datacenter'

也可以通过在主机模式前面使用感叹号!,从列表中排除匹配某以模式的主机。他的工作方式类似于逻辑NOT。

如下例子:主机模式匹配datacenter中的所有主机,但是server1.example.com主机除外!

- hosts: 'datacenter,!server1.example.com'

管理动态清单

目前为止使用静态清单很容易编写,对于管理小型的主机架构很方便。但是如果要操作一个大型的集群中许许多多的计算机,或者在机器交替非常快的环境工作,在面对维护主机静态清单想必是不太好受。

在大型IT环境下,通常会有系统来跟踪服务器资产,比如有外部目录服务通过Zabbix等监控系统维护,或者位于FreeIPA或Active Directory服务器上。Cobbler等安装服务器或红帽卫星等管理服务可能跟踪部署的逻辑系统。

Ansible支持动态清单脚本,这些清单脚本在Ansible执行时会执行脚本并检索当前信息,时清单能够得到实时的更新。这些脚本时可以执行的程序,能够从一些外部来源收集信息,并以JSON格式输出清单。

动态清单脚本的使用和静态清单一样。清单的位置可以直接在ansible.cfg中配置,也可以通过-i选型指定。如果清单文件可以执行,则会被视作动态清单程序,Ansible会尝试运行脚本来生成清单。如果文件不可执行,则它会被视作静态清单。

在使用动态清单时要确保该脚本具有执行权限并且返回值为JSON数据类型。

管理多个清单

Ansible支持在同一运行中使用多个清单,如果配置清单的地址是一个目录,那么会使用该目录下所有的清单文件,如果可执行文件会作为动态清单文件对待。

🔲 ☆

Ansible 角色

Ansible角色

使用Ansible角色可以有更多的机会去重用以前便携的Playbook中的代码。可以在标准化目录结构中打包所有任务、变量、文件、模版以及调配基础架构或部署应用其他资源。

除了自行编写、使用角色外,也可从其他来源获取角色。常用红帽企业Linux管理角色包含在rhel-system-roles软件包中可以方便的去使用。也可以从Ansible Galaxy网站获取由社区提供的其他角色。

使用ansible-galaxy list可以看到在在当前Ansible配置环境下找到了Role角色列表。

那么Ansible怎么发现这些Role呢?这些Role的路径在ansible.cfg配置文件中已经定义好了,每个目录之间通过冒号分隔:

[defaults]
inventory=./inventory
remote_user=devops
roles_path=./roles:/usr/share/ansible/roles:/etc/ansible/roles

Ansible角色子目录

子目录描述
defaults此目录中的main.yml文件包含角色变量的默认值,使用角色时可以覆盖这些默认值。这些变量的优先级较低,应该在Play中更改和定义。
files此目录包含由角色要处理的全部静态文件
handles此目录中的main.yml文件包含角色的处理程序
metamain.yml中包含角色相关信息,如作者、许可证、平台、角色依赖项
tasksmain.yml中包含角色任务的定义
templates此目录包含角色引用的template模板
tests此目录包含清单和test.yml Playbook,用于进行测试
vars此目录的main.yml文件定义角色的变量值。这些变量通常用于角色内部用途。这些变量的优先级较高,不应在Playbook中覆盖修改

定义变量和默认值

对于角色变量可以通过在vars目录下的main.yml文件来定义。与其他变量一样,使用这些变量需要在角色文件中引入{{ VARS_NAME }}。这些变量具有较高的优先级,无法被Ansible中清单变量覆盖。

默认变量在defaults目录下的main.yml文件中定义。它们的变量优先级是最低的,任何定义变量的形式都会将其覆盖,所以更改默认变量可以使Play操作更精准、更适合受控主机。

可以在vars/main.yml或defaults/main.yml中定义具体的变量,但没必要在两者中都定义变量。

在Playbook中使用Ansible角色

可以在Play中引入roles即可使用。

- hosts: remote server
roles:
- role1
- role2

角色中使用的任何copy、script、template或include_tasks/import_tasks任务都可以引用角色中相关的文件、模版或任务文件,并且无需使用相对路径或绝对路径,因为Ansible会自动在角色的files、templates、tasks子目录下去寻找他们。

控制执行顺序

在Ansible中的每一个Play是会按照Play的顺序依次执行。在每个Play中如果定义了角色,那么会优先运行角色,之后再运行任务。在最后执行被激活的handles处理程序。

在某些情况下,可能需要在执行角色任务之前执行一些任务,你可以为Play配置pre_tasks部分,这样就可以在运行角色之前执行一部分任务。如果配置在pre_tasks中的任务出发了handles处理程序。那么也会在角色或其他普通任务之前执行处理程序。

当然也可以使用为Play配置post_tasks部分,来让任务在普通任务之后和激活的处理程序之后再运行。

---
- name: Play to illustrate order
hosts: example.com
pre_tasks:
- debug:
msg: "I am pre task"
notify: my handler
roles:
- role1
tasks:
- debug:
msg: "I am nomal task"
notify: my handler
post_tasks:
- debug:
msg: "I am posted handler"
...

上面的例子中,每个任务部分都会执行debug任务来通知my handler处理程序。my handler任务执行了三次:

  • 第一次在执行了所有的pre_tasks任务后执行处理程序
  • 第二次在执行角色结束后的普通tasks任务后执行
  • 第三次是在执行完post_tasks任务后执行处理程序

除了将角色包含在Play中的roles部分外,也可以将角色添加到普通的tasks中。使用include_role模块可以动态包含角色,使用import_role模块可以静态导入角色。

---
- name: Execute a role as a task
hosts: localhost
tasks:
- name: a simple task
debug:
msg: "Im first task"

- name: "A task to include role here"
include_role:
name: linux-system-roles.network
...

RHEL 系统角色

RHEL红帽系统中,从Linux 7.4开始,系统内随附了多个Ansible角色。它们位于rhel-system-roles软件包内。RHEL8中,需要启动AppStream仓库来安装此软件包。

角色名描述
rhel-system-roles.kdump配置kdump崩溃恢复服务
rhel-system-roles.network配置网络
rhel-system-roles.postfix配置postfix服务为每个主机配置邮件传输代理
rhel-system-roles.selinux配置和管理SELinux自定义,包括模式、文件、端口上下文、布尔值设置和SELinux用户
rhel-system-roles.timesync使用网络时间协议配置时间同步

访问RHEL系统角色文档

安装后,这些RHEL系统角色文档存放在/usr/share/doc/rhel-system-roles中,其中还包含了如何去使用以及使用的例子。

├── kdump
│   ├── COPYING
│   ├── README.html
│   └── README.md
├── network
│   ├── example-bond-with-vlan-playbook.yml
│   ├── example-bridge-with-vlan-playbook.yml
│   ├── example-down-profile-playbook.yml
│   ├── example-eth-simple-auto-playbook.yml
│   ├── example-eth-with-vlan-playbook.yml
│   ├── example-infiniband-playbook.yml
│   ├── example-inventory
│   ├── example-macvlan-playbook.yml
│   ├── example-remove-profile-playbook.yml
│   ├── LICENSE
│   ├── README.html
│   └── README.md
├── postfix
│   ├── COPYING
│   ├── README.html
│   └── README.md
├── selinux
│   ├── COPYING
│   ├── example-selinux-playbook.yml
│   ├── README.html
│   └── README.md
└── timesync
├── COPYING
├── example-timesync-playbook.yml
├── example-timesync-pool-playbook.yml
├── README.html
└── README.md

时间同步角色例子

如果为受控主机配置NTP时间同步服务,那么可以使用rhel-system-roles.timesync角色自动化配置。

通过/usr/share/doc/rhel-system-roles/timesync/README.md查看示例和需要用到的变量。

---
- name: Config NTP sync service
hosts: all
vars:
timesync_ntp_servers:
- hostname: 0.rhel.pool.ntp.org
iburst: yes
- hostname: 1.rhel.pool.ntp.org
iburst: yes
timezone: Asia/Shanghai
roles:
- rhel-system-roles.timesync

tasks:
- name: Set TimeZone
timezone:
name: "{{ timezone }}"
...

也可以把在Play中定义的变量放到变量文件中。将变量文件可以放到group_varshost_vars子目录中。

配置SELinux角色

使用linux-system-roles.selinux角色控制SELinux行为。t

通过/usr/share/doc/rhel-system-roles/selinux/README.md查看示例和需要用到的变量。

设置SELinux运行模式

变量 selinux_state: enforcing。可以设置为enforing、permissive、disabled。如果不设置则不更改。

设置SELinux布尔值

例如将httpd_enable_homedirs布尔值永久设置为no

selinux_booleans:
- name: 'http_enable_homedirs'
state: 'on'
persistent: 'yes'

设置SELinux fcontext上下文

下面的例子完成了对/srv/www目录下的所有文件的默认SELinux类型设置为httpd_sys_content_t

selinux_fcontexts:
- target: '/srv/www(/.*)?'
setype: 'httpd_sys_content_t'
state: present

使用selinux_restore_dirs变量指定要对其运行restorecon目录的列表。

设置SELinux端口

使用selinux_ports变量可以对端口进行管理。

selinux_ports: 
- ports: '82'
setype: 'http_port_t'
proto: 'tcp'
state: 'present'

创建角色框架

创建角色不需要额外的开发工具,角色是文件目录结构和文件组成的。可以使用创建目录和编辑文件命令配合完成创建一个角色框架。为了节省时间,可以使用ansible-galaxy init来创建角色框架。

ansible-galaxy init my_role

$ ls my_role/
defaults files handlers meta README.md tasks templates tests vars

默认变量的覆盖

在以下情况中,角色中defaults目录中定义的默认变量会被覆盖:

  • 在清单文件中定义,作为主机变量或组变量
  • 在playbook项目的group_vars或hosts_vars目录下的YAML文件中定义
  • 作为变量嵌套Play的vars关键字中定义
  • 在Play的定义roles角色时的所定义的变量

安装V2ray-core服务角色示例

功能如下:

  • 自动安装最新版本
  • 按照受控节点平台自动选择platform平台
  • 可以选择从Github/Jsdelivr/用户自定义V2ray-core地址下载
  • 可以控制卸载

Tasks角色任务:

---
# tasks file for v2ray
- name: Prepare to Install V2ray-core
block:
- name: V2ray download path is exist
file:
path: "{{ v2ray_download_path }}"
state: directory
mode: 0755
- set_fact:
install_error: false

when: v2ray_present

- name: Get the latest V2ray version and servers info
block:

- name: Get Server machine platform
shell: |
case "${1:-"{{ ansible_facts.machine }}"}" in
i686|i386)
echo '32'
;;
x86_64|amd64)
echo '64'
;;
*armv7*|armv6l)
echo 'arm'
;;
*armv8*|aarch64)
echo 'arm64'
;;
*mips64le*)
echo 'mips64le'
;;
*mips64*)
echo 'mips64'
;;
*mipsle*)
echo 'mipsle'
;;
*mips*)
echo 'mips'
;;
*s390x*)
echo 's390x'
;;
ppc64le)
echo 'ppc64le'
;;
ppc64)
echo 'ppc64'
;;
*)
return 1
;;
esac
register: return_machine
- name: Get the latest V2ray version and servers info
shell: >
curl -H "Accept: application/json" -H "User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:74.0) Gecko/20180101 Firefox/74.0" -s "https://api.github.com/repos/v2ray/v2ray-core/releases/latest" --connect-timeout 10| grep 'tag_name' | cut -d\" -f4
register: return_version
args:
warn: false
- set_fact:
latest_v2ray: "{{ return_version.stdout_lines[0] }}"
machine: "{{ return_machine.stdout }}"
rescue:
- name: "ERROR: Get the latest V2ray version and servers info. Please check your Network Connection and V2ray-core version"
set_fact:
install_error: true
when: v2ray_present and not install_error

- name: Download V2ray-core
block:
- name: "Download V2ray-core{{ latest_v2ray }} from jsdelivr"
get_url:
url: "https://cdn.jsdelivr.net/gh/v2ray/dist/v2ray-linux-{{ machine }}.zip"
dest: "{{ v2ray_download_path }}"
when: v2ray_download_from == "jsdelivr"
- name: "Download V2ray-core{{ latest_v2ray }} from Github"
get_url:
url: "https://github.com/v2ray/v2ray-core/releases/download/{{ latest_v2ray }}/v2ray-linux-{{ machine }}.zip"
dest: "{{ v2ray_download_path }}"
when: v2ray_download_from == "Github"
- name: "Download V2ray-core{{ latest_v2ray }} from {{ v2ray_download_from }}"
get_url:
url: "{{ v2ray_download_from }}"
dest: "{{ v2ray_download_path }}"
when: |
v2ray_download_from != "jsdelivr"
and
v2ray_download_from != "Github"
rescue:
- name: "ERROR: Download V2ray-core. Please check your Network Connection and V2ray-core version"
set_fact:
install_error: true
when: v2ray_present and not install_error


- name: Unarchive V2ray-core
block:
- name: Create unarchive V2ray-core directory
file:
path: "{{ v2ray_download_path }}/v2ray-linux-{{ machine }}"
state: directory
mode: 0755
- name: Unarchive V2ray-core
unarchive:
src: "{{ v2ray_download_path }}/v2ray-linux-{{ machine }}.zip"
dest: "{{ v2ray_download_path }}/v2ray-linux-{{ machine }}"
remote_src: true
rescue:
- name: "ERROR: Unarchive V2ray-core. Please check your platform"
set_fact:
install_error: true
when: v2ray_present and not install_error

- name: Install V2ray-core and Start V2ray-core
block:
- name: Create V2ray install directory
file:
path: "{{ item }}"
state: directory
mode: 0755
loop: "{{ v2ray_installed_dir }}"
- name: Copy binary file into directory
copy:
src: "{{ v2ray_download_path }}/v2ray-linux-{{ machine }}/{{ item.src }}"
dest: "{{ item.dest }}"
remote_src: true
mode: 0755
loop: "{{ v2ray_binary }}"
- name: Copy V2ray config and Start V2ray-core
copy:
src: "{{ v2ray_config }}"
dest: /etc/v2ray/config.json
mode: 0755
notify: Start V2ray Service
rescue:
- name: "ERROR: Install V2ray-core. Please check your permissions"
set_fact:
install_error: true
when: v2ray_present and not install_error


- name: Remove v2ray-core
block:
- name: Stop V2ray-core service
systemd:
name: v2ray.service
daemon_reload: true
enabled: false
state: stopped
ignore_errors: true
- name: Remove V2ray-core service
file:
path: /etc/systemd/system/v2ray.service
state: absent
- name: Remove v2ray-core file
file:
path: "{{ item }}"
state: absent
loop: "{{ v2ray_installed_dir }}"

when: not v2ray_present or install_error

- name: Check remove result
debug:
msg: Removed v2ray-core
when: not v2ray_present

- name: Check install result
debug:
msg: Install V2ray-core Failure
failed_when: yes
when: v2ray_present and install_error

- name: Check install result
debug:
msg: Install V2ray-core Successful
when: v2ray_present and not install_error

Default变量:

# You can download the latest V2ray-core from Github/jsdelivr/(https://yourself)
v2ray_download_from: "Github"
v2ray_download_path: "/tmp/v2ray"
v2ray_config: "files/v2ray.conf"

# user_control_boolean
v2ray_present: true

# Ansible Role Control vars (Don't change it!!!)
v2ray_installed_dir:
- /etc/v2ray
- /var/log/v2ray
- /usr/bin/v2ray
- "{{ v2ray_download_path }}"
v2ray_binary:
- src: geoip.dat
dest: /usr/bin/v2ray
- src: geosite.dat
dest: /usr/bin/v2ray
- src: v2ctl
dest: /usr/bin/v2ray
- src: v2ray
dest: /usr/bin/v2ray
- src: systemd/v2ray.service
dest: /etc/systemd/system
🔲 ☆

Ansible 调整连接数

Ansible调整连接数量和同时运行的任务数量

调整连接数forks

Ansible在处理Playbook时会按照顺序运行每一个play。确定受管主机列表后,Ansible将按照顺序在受管主机上运行任务。通常只有会在全部受管主机完成当前任务后才会继续往下执行其他任务。

Ansible可以同时连接到play中的所有主机以执行每项任务,这种方式适用于小规模的主机列表。如果对着数百台受控主机同时进行操作的话,控制节点会面临着巨大的性能压力。

所以,为了避免这种情况发生导致控制节点压力过大或出现异常,Ansible在执行任务时默认最大连接数为5。这个默认值是由Ansible配置文件中的forks参数控制。如果你的控制节点性能强劲,你可以在配置文件中手动配置该参数调整为更大的数值,这样在执行任务时可以同时连接到更多的受控节点,进而加快任务的执行速度。

[defaults]
inventory=inventory
remote_user=devops
forks=5

当然ansibleansible-playbook命令都提供了-f--forks选项来覆盖ansible在配置文件中读取到的forks值。

分批次完成任务

通常情况下Ansible会在执行下一个任务前,要求全部的受控主机都完成当前的任务。但是要求全部的主机同时完成某一个任务可能会带来麻烦,特别是在处理Web负载均衡的机器时,如果任务要求集群内的全部主机同时重启Web服务,那么业务就会中断。所以在playbook中使用serial关键字可以解决这个问题,它能够控制play批量的完成任务,而不是让全部主机同时都完成一个任务。

下面的例子是使用serial关键字来控制一批只能2台受控主机完成这个Play,假设我们一共有6台机器,那么剩下的四台机器分别要等前面的两台受控主机执行完成全部的Play后才会完成到它们执行Play,6台节点两两一组,分批次执行任务。这样不会导致我们的业务因为重启全部的机器而终止。

---
- name: Update web server
hosts: webservers
serial: 2
tasks:
- name: Latest version of apache installed
yum:
name: httpd
state: latest
notify:
- Restart apache

handlers:
- name: Restart apache
service:
name: httpd
enabled: yes
state: restarted
...

serial关键字也可以设置为百分比。此百分比应用于该play中主机的总数,已确定每次批量执行play的主机个数。

🔲 ☆

Splunk数据搜索和报表

Splunk 数据搜索和报表

Splunk 是?

Splunk是机器数据分析平台,他可以收集并处理所有有机器产生的日志数据,在Splunk中可以做到”any data from any source”。我们作为管理员可以使用Splunk生成这些数据的报表、仪表盘等,赋予这冰冷枯燥日志数据的新生。

搜索是Splunk核心功能之一,基本上近乎所有的功能都是由搜索展开的。充分挖掘这些数据并从中获得有价值的信息,这让我想起在泥沙中淘金的感觉。从基本的报表和仪表,再到数据模型和功能完备的Splunk应用程序,这些都是由Splunk搜索在后台提供着支撑。

Splunk使用自己的搜索语言(SPL)。SPL有很多搜索指令,其中大部分包含有多种函数,参数和字句。

搜索

Splunk的搜索以基本搜索开头,之后是一系列的搜索指令,命令之间由管道符号(|)分割。Splunk搜索时经常会使用管道符不断精炼数据结果。

当然Splunk也可以直接搜索你想要的元素,比如一个agent、IP、error。但是只有在数据量很少的情况下才可以这么用,否则在海量(GB)级别的文本数据中搜索一个单词或者一个IP地址还不如淘到金子的可能性大。因此使用SPL和多种Splunk命令来提炼搜索。搜索的越精细,搜索的执行时间就越少,也能尽快的找到这些所需要的数据。

尽可能的在第一个管道符之前尽可能多的过滤内容,这会节省CPU和磁盘I/O。并且在编写完查询指令后可以先将搜索时间番位设置较短的区间,如果查找的结果是需要的数据再扩大搜索范围。

常用搜索指令

命令描述
chart/
timechart
将结果以表格的形式输出。或者借助Splunk图表按时间顺序展示输出结果
dedup根据指定的字段删除重复的结果
eval评估新的或现有的字段和值。eval可以实用化许多不同的函数
fields指定在搜索结果中保留或移除字段
head该命令保留前X(指定值)行的结果
lookup从外部源或列表查找字段,返回附加的字段值
rare从字段中找到最少值
rename重新命名字段
replace该命令允许随后对结果进行搜索和过滤
sort该命令按升降序对结果进行排序
stats对结果执行统计运算。stats命令可以使用许多不同的函数
table该命令把结果以列表的格式输出
tail保留最后X(指定)行的结果
top从字段中找到最常见的值
transaction该命令根据共同的事务标识符将多个时间合并为单一事件

高级时间配置

在搜索框的右面可以选择限定时间范围,当你所要查找的时间比较复杂的时候,可以尝试使用高级选项来选取时间范围。

高级时间配置-2020-06-10

时间修饰符可以接受多种时间单位:秒(s),分钟(m),小时(h),天(d),星期(w),月(mon),季度(q),年(y)等。

比如要搜索在一天前的半夜整点到距离当前时间30分钟前出现过的全部事件可以这样表示:最早=-1d@d/最晚=-30m

时间修饰符-2020-06-10

(@)是向下取整标志符。

布尔运算符

Splunk中可以使用三种类型的布尔运算符:AND、OR、NOT。Splunk只会识别这三个大写的运算符,所以要大写!默认情况下AND运算符是隐含的,可以不写。比如 ssh AND failure等同于ssh failure

搜索字段

Splunk中的字段可以被认为是有着一个或多个值的关键词。这些字段都可以被Splunk搜索。进入到Splunk的每一个数据源都会至少包含源、主机、索引、源类型这四个字段,一些数据源会有数百个附加的字段。如果原始日志数据包含有键值对或以JSON或XML结构化格式呈现的数据,Splunk会自动提取字段并使其可供搜索。

搜索特定的字段值也很简单,在当你Splunk自动识别或手表标记好的字段后,直接输入字段名与布尔运算符就可以进行搜索。如sourcetype="access_combined" status!=200,就可以搜索含有来源类型为access_combined,并且status字段值不等于200的事件。

报表

在搜索结束后可以保存搜索供以后继续使用或者用于仪表盘。保存的搜索被称之为报表。

将搜索结果存储为报表-2020-06-10

让搜索的结果更具有可读性

有了上面的基础,就可以来为这些数据填些花样玩法。

table命令生成数据列表

在Splunk首页选择“搜索和报表”应用程序。时间选择器选择“过去24小时”。之后搜索index=main sourcetypee=access,之后会返回原始数据搜索事件。

此时我们在刚才的搜索指令使用管道符相连table命令:index="main" sourcetype="access_combined" | table _time,referer_domain,method,uri_path,JSESSIONID,useragent

这样的列表会只显示你关注的字段,而不现实原始事件,将数据以数据列表形式呈现。

table命令1-2020-06-10

点击右上角的另存为——>报表,就可以将将搜索存储为报表,方便下次的时候使用。

搜索分段描述
index=”main”Splunk中所有的数据都会保存在一个或多个索引中。尽管没有强制要求,不过再好在搜索时指定索引,这样能更快并且更精确的得到搜索结果
sourcetype=”access_combined”只搜索与access_combined数据源类型
|table _time,referer_domain,method,uri_path,JSESSIONID,useragent使用table命令时,将上一步的搜索结果放到管道左面。并告知Splunk以列表格式返回数据。Splunk会在搜索结果中只显示table命令中规定的字段

table命令对大范围数据搜索有很高的性能需求,尽可能的要将table用在搜索末尾,当其他的Splunk命令处理完毕后,再执行table命令。

下载报表

我们已经把数据呈现为可读的格式,因此可以下载这些数据并存储为CSV文件格式方便我们统计数据。在搜索栏右侧下方有一个导出按钮,这里可以选择其他格式:JSON、XML、CSV。

将每个字段制表

如果需要将所有字段都列成表格,没必要指定所有字段名。可以使用通配符(*)。

index="main" sourcetype="access_combined" | table *

但是!假设全部的字段有很多,然而你还不想要全部字段,只需要大部分的字段,那么你可以在使用table命令之前使用fields命令先移除你不需要的字段再使用通配符(*)。

index="main" sourcetype="access_combined" |fields - sourcetype,index,_row,date* linecount| table *

fields过滤前-2020-06-10

fields过滤后-2020-06-10

在fields命令后加上一个减号(-),Splunk会将后面的字段移除。

找出最常访问的网页

在获得了Web服务器日志后,你就能看得到你的客户喜欢哪些网站,如果可以找到最常访问的网页,那么可以在这些页面上继续开发新功能!哪些访问次数较少的可以将他们移除掉或者重新设计一下!

index="main" sourcetype="access_combined" | stats count by uri_path | sort - count

搜索分段描述
stats count by uri_path使用stats命令,将上一步的搜索结果放在管道左侧,并告知Splunk计算每个uri_path字段值的数量。
sort - count将上一步stats命令生成的count字段作为输入,并告知Splunk将统计结果进行降序(-),最常访问的网站就会显示在结果的顶端

搜索访问量排名前10的网页

可以使用top代替stats命令。默认情况下top会显示排名前10的网页

index="main" sourcetype="access_combined" | top uri_path

如果你需要查看更多的排行,用limit可以满足你!

index="main" sourcetype="access_combined" | top limit=20 uri_path

找出最常使用的Web浏览器

Web日志中也包含了useragent相关信息,其中包括浏览器信息和操作系统信息。

index="main" sourcetype="access_combined" | eval browser=useragent | replace *Firefox* with Firefox,*Chrome* with Chrome,*MSIE* with IE,*Safari* with Safari,*Opera* with Opera in browser | top useother=t browser

搜索分段描述
eval browser=useragent使用eval命令,创建一个新的字段browser并将useragent字段中的内通存放到browser字段中
replace *Firefox* with Firefox,*Chrome* with Chrome,*MSIE* with IE,*Safari* with Safari,*Opera* with Opera in browser使用replace命令,利用通配符(*)将browser中匹配到的内容使用with参数后面的文本替换。如果包含空格的值要加上引号,否则会导致语法识别错误
top limit=5 userother=t browsertop命令告知Splunk找到排名前5的浏览器,并将limit所限制导致溢出的值都归到OTHER下

浏览器信息统计-2020-06-10

同理也可以统计出用户所使用的操作系统。

index="main" sourcetype="access_combined" | eval os=useragent | replace *Windows* with Windows系统 ,*Mac* with Mac系统,*Linux* with Linux系统 in os | top limit=10 useother=t os

操作系统信息统计-2020-06-10

🔲 ☆

Ansible 常用模块

Ansible模块的使用

文件模块

Files文件模块库包含的模块可以对Linux文件进行管理,如创建、删除、编辑和修改文件的权限与属性等。

模块说明
blockinfile插入、更新或删除由可定义标记线包围的多行文本块
lineinfile确保特定行位于某个文件中,或使用反向引用正则表达式来替换现有行。此模块可以在想要更改某一个行的文本时使用
copy将文件从本地或远程计算机复制到目标主机的某个位置。类似于file模块,copy模块还可以设置文件属性,包括SELinux上下文
fetch该模块和copy类似,但以相反的方式工作。fetch用来从目标主机获取文件到本机控制节点上
file设置权限、所有权、SELinux上下文以及常规文件、符号链接、硬连接、目录时间戳等。此模块还可以创建或删除常规文件、符号链接、硬连接和目录。其他过个与文件相关的模块支持与file模块相同的属性设置选项,包括copy模块
stat检索文件的状态信息,与Linux stat命令相似
synchronize对rsync命令的打包

文件模块使用示例

确保目标主机上存在文件

- name: Touch a file and set permissions
file:
path: /home/student/touch.me
owner: student
group: root
mode: 0000
state: touch

如果目标主机已存在该文件,则会进行touch操作。上面的task除了确保文件存在以外,还会保证文件的权限为设定值。

  File: touch.me
Size: 14 Blocks: 8 IO Block: 4096 regular file
Device: 801h/2049dInode: 163550 Links: 1
Access: (0000/----------) Uid: ( 1000/ student) Gid: ( 0/ root)
Context: unconfined_u:object_r:user_home_t:s0
Access: 2020-06-02 17:11:40.642364330 -0400
Modify: 2020-06-02 17:11:40.642364330 -0400
Change: 2020-06-02 17:11:40.644530998 -0400
Birth: -
------------------------------
File: touch.me
Size: 14 Blocks: 8 IO Block: 4096 regular file
Device: 801h/2049dInode: 163550 Links: 1
Access: (0000/----------) Uid: ( 1000/ student) Gid: ( 0/ root)
Context: unconfined_u:object_r:user_home_t:s0
Access: 2020-06-02 17:18:59.746867300 -0400
Modify: 2020-06-02 17:18:59.746867300 -0400
Change: 2020-06-02 17:18:59.746867300 -0400
Birth: -

修改文件属性

使用file模块,确保新的或现有文件具有正确的文件属性和SELinux类型。

- name: SELinux type is set to samba_share_t
file:
path: /home/student/touch.me
setype: samba_share_t

修改前touch.me文件的setype属性是user_home_t,使用file模块处理后setype属性已经改变为samba_share_t

$ ls -lZ
----------. 1 student root unconfined_u:object_r:user_home_t:s0 14 Jun 2 17:18 touch.me
$ ls -lZ
----------. 1 student root unconfined_u:object_r:samba_share_t:s0 14 Jun 2 17:18 touch.me

永久更改SELinux文件上下文属性

设置上下文属性时,file模块的行为和chcon类似。通过运行restorecon可能会意外的撤销使用该模块对文件上下文所做的更改。当使用file设置上下文后,可以使用System模块集合中的sefcontext来更新SELinux策略,如semanage fcontext

- name: SELinux type is persistently set to samba_share_t
sefcontext:
target: /home/student/touch.me
setype: samba_share_t
state: present

可以看得到在SELinux上下文策略中目标的默认上下文已经更改为samba_share_t

# semanage fcontext -l | grep touch.me
/home/student/touch.me all files system_u:object_r:samba_share_t:s0

注意:sefcontext模块只更新SELinux策略中目标的默认上下文,并不更改当前现有文件的上下文。

在目标主机上复制和编辑文件

使用copy模块时,模块假定设置了force: yes。这会强制copy模块覆盖远程文件(如果存在并且包含于与当前要发送的文件内容不同)。如果手动设置force: no,则它仅会在目标主机不存在要复制的这个文件时才会进行复制。

- name: Copy a file to managed hosts
copy:
src: file
dest: /home/student/touch.me
force: yes

如果要从目标主机上拉取文件到本机,则使用fetch模块。

- name: Retrieve SSH key from reference host
fetch:
src: "/home/{{ user }}/.ssh/id_rsa.pub"
dest: "files/keys/{{ user }}.pub"

要确保现有文件中存在某行文本,可以使用lineinfile模块。

- name: Add a line of text to file
lineinfile:
path: /home/student/touch.me
line: "Can you touch me?"
state: present

如果要将文本块插入到文档中,应使用blockinfile模块。

- name: Add additional lines to a file
blockinfile:
path: /home/student/touch.me
block: |
This is the block of first line.
And
This is the block of third line.
state: present

使用blockinfile模块时,注释块标记插入到块的开头和结尾,用来让Ansible识别和保持幂等性。

# BEGIN ANSIBLE MANAGED BLOCK
This is the first line.
# END ANSIBLE MANAGED BLOCK

在目标主机上删除文件

在大多数情况下,如果控制目标主机文件的删除使用file模块的state: absent参数来控制。

- name: Removed file in the server
file:
dest: /home/student/touch.me
state: absent

检索文件的详细信息

使用stat模块可以查看文件的详细信息,并返回文件的事实。你可以利用这些Facts对文件进行检索和校验。stat模块类似于Linux系统中的stat命令。

- name: Check all stat of /etc/passwd
stat:
path: /etc/passwd
register: results

- debug:
vars: results

同步控制节点和受控节点之间的文件

使用synchronize模块来操作同步主机间的文件。synchronize对rsync工具进行打包,它简化了playbook中常见文件管理任务。使用该模块要求双方主机安装rsync工具。

- name: synchronize local file to server file
synchronize:
src: /etc
dest: /home/student/

使用Jinja2模版部署自定义文件

可以利用Jinja2模版语法通过和变量与事实相配合,对固定地方的值进行覆盖和编辑。来实现定制化修改配置文件。

使用`{% EXPR %}`用于表达式或逻辑(循环、判断)
使用`{{ EXPR }}`用于输出最终表达式或变量的结果
使用`{# COMMENT #}`注释,注释的内容不会出现在最终的结果里

构建Jinja2模版

Jinja2模版由多个元素组成:数据、变量和表达式。在呈现Jinja2模版时,这些变量和表达式被替换为对应的值。模版中使用的变量可以在playbook的vars部分中指定。可以将目标主机的事实作为模版中使用的变量。

可以使用ansible all -m setup来查看目标主机中全部的Fact事实。模版文件没有固定的文件拓展名,只要是文本文件即可,但是为了方便记忆理解,通常使用.j2来代表文本文件是Jinja2的模版文件。

部署Jinja2模板

我们刚刚创建好了Jinja2模版,现在要利用这些模版。我们需要使用template模块。src参数指的是模版文件在控制节点中的路径,dest的值是在目标主机的指定目录生成文件。

tasks:
- name: template render
template:
src: motd.j2
dest: /etc/motd
backup: true

template和file模块一样支持对文件权限进行设置。

标示配置文件由Ansible管理

我们使用模版生成文件后,为了避免管理员用户手动的修改这些配置文件,我们最好在模版的开头写上声明。虽然template不会自动地帮我们完成,但是我们可以在模版文件的开头手动引入设定好的提醒文本,使用Jinja2语法将变量的内容填写到配置文件中。

可以使用ansible_managed 指令中默认设置的 Ansible managed字符串来执行此操作。这不是一个正常的变量,但是可以在模版中用作一个变量。ansible_managed指令在ansible.cfg文件中的设置:

ansible_managed = Ansible managed: modified on %Y-%m-%d %H:%M:%S

要将在ansible.cfg文件中配置的ansible_managed字符串包含在Jinja2模版内,使用下面的引用变量语法即可。

{{ ansible_managed }}

通过模版生成的配置文件开头存在了Ansible managed: modified on 2020-06-10 15:55:29。这样就能对修改此文件的人有一个提示的作用。

控制结构

可以在Jinja2模版中使用控制结构,以减少重复的输入。为Play中每个主机能够动态的生成条目,或者有条件的将文本插入到文件中。

循环

Jinja2使用for语句来提供循环功能。

{% for user in users %}
{{ user }}
{% endfor %}

下面的示例模版使用for逐一遍历users变量中的所有值,将myuser替换为各个值,但值为root时除外。

{# for statement #}
{% for myuser in users if not myuser == "root" %}
User number is {{ loop.index }} - {{ myuser }}
{% endfor %}

loop.index变量是循环到当前处的索引号。他在循环第一次执行时的值为1,每一次迭代递增1。

下面的例子是生成hosts文件。

{% for host in groups['all'] %}
{{ hostvars[host]['ansible_facts']['default_ipv4']['address'] }} {{ hostvars[host]['ansible_facts']['fqdn'] }} {{ hostvars[host]['ansible_facts']['hostname'] }}
{% endfor %}
---
- name: generate hosts
hosts: all
tasks:
- name: generate template
template:
src: hosts.j2
dest: /home/student/hosts
...

条件语句

Jinja2使用if语句来提供条件控制。如果满足某些条件,则会按照语句块内的规则继续生成。

{% if finished %}
{{ result }}
{% endif %}

Jinja2仅能用于模版,不能用于Playbook。条件语句和循环语句可以相互嵌套。

变量过滤器

可以使用Jinja2提供的过滤器改变原有变量输出的格式,例如将字符串转换为JSON或YAML。

如果要转换为json格式时,使用to_json过滤器进行输出。

如果要转换为yaml格式时,使用to_yaml过滤器进行输出。

{{ output | to_json }}
{{ output | to_yaml }}

如果要使结构更适于人类阅读,可以使用下面的过滤器来输出人类可读格式。

{{ output | to_nice_json }}
{{ output | to_nice_yaml }}

变量测试

在Ansible Playbook中与when子句一同使用的表达式是Jinja2表达式。用于测试返回值的内置Ansible测试failed、changed、succeeded、skipped四种。

tasks:
.......
- debug:
msg: "The task was aborted"
when: returnvalue is faild

软件包管理

使用dnf的Ansible模块可以在受控主机上控制dnf软件包管理器。dnf是RHEL8使用的默认包管理器用于替代yum。不过也可以使用yum模块来对RHEL8进行操作。

下面示例中使用task任务来代替原有的dnf包管理器指令:

dnf install httpd -y

- name: Install the httpd packages
dnf:
name: httpd
state: present

state关键字有如下参数:

  • present 如果软件包不存在,则安装软件包
  • absent 如果已安装,则删除软件包
  • latest 如果软件包不是最新版本,则会对软件包进行更新。要是没有安装则会安装最新版本的软件包

name关键字有以下使用方式:

  • 直接填写某一个或以列表的形式填写多个软件包名称
  • 使用'*'配合latest可以进行更新系统全部软件包
  • 若要管理模块或组需要使用@符号

安装Development Tools软件组的示例:

- name: Install the Development Tools group
dnf:
name: ‘@Development Tools’
state: present

安装postgresql数据库模块:

- name: Install the postgresql module
dnf:
name: '@postgresql:9.6/client'
state: present

如果包管理器不是yum或者dnf。可以使用package模块替代yum/dnf模块。package模块可以自动检测并使用受控主机的包管理器去安装配置的软件包。

- name: Install httpd
package:
name: httpd
state: present

收集已安装的软件包信息

使用package_facts模块就能获取到受控主机中已经安装的软件包。它会将获取到的全部软件包信息存入ansible_facts.packages变量中。

使用package_facts模块并进行输出的示例:

---
- name: Output
hosts: prod
tasks:
- name: Check packages
package_facts:
manager: auto
- debug:
var: ansible_facts.packages.httpd

package_facts模块有两个选项:

  • manager 选择软件包管理器。默认auto自动识别
  • strategy 策略

返回prod主机组中已安装的httpd软件包信息。

ok: [serverc] => {
"ansible_facts.packages.httpd": [
{
"arch": "x86_64",
"epoch": null,
"name": "httpd",
"release": "10.module+el8+2764+7127e69e",
"source": "rpm",
"version": "2.4.37"
}
]
}
ok: [serverd] => {
"ansible_facts.packages.httpd": [
{
"arch": "x86_64",
"epoch": null,
"name": "httpd",
"release": "10.module+el8+2764+7127e69e",
"source": "rpm",
"version": "2.4.37"
}
]
}

配置yum仓库

添加yum仓库

使用yum_repository模块来控制第三方yum仓库。在添加仓库时即可配置GPG密钥。

---
- name: Create yum repo
hosts: prod
tasks:
- name: Create yum repo
yum_repository:
name: rpmforge
description: RPMforge YUM repo
file: test
gpgkey: http://materials.example.com/yum/repository/RPM-GPG-KEY-example
baseurl: http://materials.example.com/yum/repository/
enabled: yes
present: yes
gpgcheck: yes

可以直接使用gpgkey选项而不需要使用其他模块进行配置。

用户管理和身份认证

如何管理用户和用户组,以及配置ssh-key。

用户模块

使用user模块可以管理主机上的用户以及它们的许多参数。也可以删除用户、设置主目录、设置UID、关联的用户组等很多参数。

如果需要创建可以登录的计算机用户,需要使用password参数和password_hash('sha512')搭配生成Hash后的密码才可以登陆系统。

---
- name: Add user
hosts: dev
tasks:
- name: User modul
user:
name: natsumi
shell: /bin/bash
groups: wheel
append: yes
state: present
password: "{{ pwd | password_hash('sha512') }}"
vars:
pwd: test123

组模块

group模块可以管理受控主机中的用户组。

---
- name: Add group
hosts: dev
tasks:
- name: change group
group:
name: devuser
state: present

group模块参数: gid,local,name,state,system(如果设置为yes,则表示创建的组是系统组)

系统调度

at一次性任务

使用at模块来创建一个一次性任务。可以安排任务在未来的某一个时间点执行一次。

at模块参数:

参数选项说明
command-计划要运行的命令
count-单位数字。必须和units一同使用
script_file-计划要执行的现有脚本文件
stateabsent/present添加或删除命令或脚本的状态
uniqueyes/no如果任务已在运行,则不会再次执行
unitsminutes/hours/days/weeks时间单位

at模块使用示例:

---
- name: Using at make task
hosts: dev
tasks:
- name: create task
at:
command: "echo 'rick' > lala.txt"
count: 1
units: minutes
unique: yes

目前使用at模块总会在文件名末尾拼接上marcinDELIMITERxxxx字符串,经过Google发现这一串文本是用来在at pool中用来标示任务的。可是在这里为什么将他们输出了出来目前还不得而知。

cron计划任务

要完成重复性的任务,也可以使用cron模块来创建计划任务。

- name: Ensure a job that runs at 2 and 5 exists. Creates an entry like "0 5,2 * * ls -alh > /dev/null"
cron:
name: "check dirs"
minute: "0"
hour: "5,2"
job: "ls -alh > /dev/null"

想要在特殊时间点,比如系统重启后运行一个任务,可以使用special_time参数来配置cron计划任务。

- name: Ensure a job that runs at system reboot. Creates an entry like "@reboot ls -alh > /dev/null"
cron:
name: "check dirs"
special_time: reboot
job: "ls -alh > /dev/null"

reboot模块

使用reboot模块重新启动比直接用shell模块发起关机更安全,使用shell模块关闭受控主机后,它会等待再次开机恢复运行,才会继续向下执行其他任务和Play。

对受控主机重启后并持续等待180s。如果受控主机恢复运行则继续执行接下来的任务。

- name: Reboot a slow machine that might have lots of updates to apply
reboot:
reboot_timeout: 180

如果超出运行时间,则执行出错。接下来的task和play都不会继续执行。

fatal: [servera]: FAILED! => {"changed": false, "elapsed": 19, "msg": "Timed out waiting for last boot time check (timeout=18)", "rebooted": true}

🔲 ☆

Ansible 变量和事实

变量和事实

如何定义变量、在Playbook中使用使用变量、加密敏感变量、Fact事实、魔法变量。

变量的定义

变量可以在很多地方定义,他们的作用域也是不同的。

1、playbook中定义:vars
2、playbook中使用变量文件
3、在当前目录的host_vars/group_vars目录下定义变量文件(针对灵活、大型的playbook适用)
4、运行playbook时,在命令行中使用-e选项来使用变量,这种方式的优先级最高,会覆盖以上所有相同名字的变量属性。

变量的使用

在playbook中,使用"{{ var_name }}"来引用变量。

register寄存器

使用寄存器可以将上一次的执行结果存储到新的变量中,用于下一次使用。

使用vault加密变量

当有一些敏感变量,比如密码等信息你应该是不想使用明文存储到变量文件。

ansible-vault [create|decrypt|edit|encrypt|encrypt_string|rekey|view] [options] [vaultfile.yml]

使用ansible-vault可以对变量文件进行加密,在执行playbook的时候通过输入密码或者使用密钥文件来对加密好的变量进行解密。

如何运行引用了加密变量的playbook

ansible-playbook --ask-vault-pass/--valut-password-file=vault-pass playbook.yml

使用Fact事实

Fact事实是在我们运行ansible-playbook时自动收集对应主机的基本信息的变量。

常用的fact事实

ansible_facts.hostname
ansible_facts.fqdn
ansible_facts.default_ipv4.address
ansible_facts.interfaces

禁用收集Fact事实

在Play中添加gather_facts:no

收集目标主机中自定义的Fact

在目标主机的/etc/ansible/facts.d/目录下创建xxxxx.fact文件,在文件中使用json格式自定义目标主机的Fact。

使用魔法变量

一些变量并非事实或通过setup模块配置,但也能由Ansible自动设置。这些魔法变量也可以用于获取与特定受管主机相关的信息。

groups

列出清单中的所有组和主机。

group_names

列出当前受控主机所属的所有组。

inventory_hostname

包含清单中配置的当前受管主机的主机名称。

hostvars

引用其他主机的变量,前提是要引用的主机已经执行过获取Fact操作。

$ ansible all -m debug -a 'var=inventory_hostname'

servera.lab.example.com | SUCCESS => {
"inventory_hostname": "servera.lab.example.com"
}
serverb.lab.example.com | SUCCESS => {
"inventory_hostname": "serverb.lab.example.com"
}

🔲 ☆

Ansible 任务控制

Ansible的循环控制及条件任务

Ansible Playbook的循环控制、条件控制、处理程序以及错误处理机制。

loop循环控制

Ansible支持使用loop关键字来执行循环任务。

简单迭代任务循环

将loop关键字添加到任务中,并用列表表示要迭代的项目的值。并使用item临时变量来保存每次循环迭代过程中使用的值。

如下是一个启动两个服务的例子,在没有了解循环控制时应这样编写playbook,分别写两个独立的任务来执行:

- name: Postfix is running
service:
name: postfix
state: started

- name: httpd is running
service:
name: httpd
state: started

使用loop循环控制,则可以简写如下:

- name : Start Postfix and httpd services
service:
name: "{{ item }}"
state: started
loop:
- postfix
- httpd

也可以把loop所使用的列表存到play的变量中,他们三个执行的效果是相同的:

vars:
start_services:
- postfix
- httpd
tasks:
- name : Start Postfix and httpd services
service:
name: "{{ item }}"
state: started
loop: "{{ start_services }}"

循环散列或字典列表

loop也可以循环散列或者是字典,下面示例中每个字典或散列有两个键,分别是name和groups。当前循环中的每个键的值可以分别通过item.name和item.group来检索。

- name: User exist and are in the corrent Groups
user:
name: "{{ item.name }}"
group: "{{ item.group }}"
loop:
- name: user1
group: wheel
- name: user2
group: root

早期的循环语法

在Ansible 2.5之前,大多数的playbook使用不同的循环愈发。提供了多个循环关键字,都以前缀with_开头,后跟Ansible查找插件的名称连用。这种循环语法在目前依旧很常见,但在未来某个时刻将会被弃用。

循环关键字描述
with_items和loop类似,但当为with_items提供了列表的列表,他们会被扁平化处理为单级列表。item作为循环变量保存每次迭代过程中的值
with_file此关键字需要控制节点文件名列表。循环变量item保存每次迭代过程中保存文件列表中相应文件的内容
wirh_sequence此关键字不需要列表,而是需要参数生成数字序列列表。循环变量item在每次迭代过程中保存生成的序列中的一个生成项的值

将Register变量与Loop一起使用

---
- name: Loop Register Test
gather_facts: no
hosts: localhost
tasks:
- name: Loop Echo Task
shell: "echo This is my item: {{ item }}"
loop:
- one
- two
register: echo_results

- name: Show echo_results variable
debug:
var: echo_results

条件任务语句

使用条件语句when可以控制该任务是否执行,根据条件配置满足一定情况下执行任务。比如在执行任务前首先要判断一下磁盘剩余空间、内存大小是否满足需求。如果不满足则直接跳过该任务。

变量也可以作为when条件语句的判断条件,如下:

---
- name: Test Boolean Task Demo
hosts: all
vars:
run_task: true
tasks:
- name: Install the web services
yum:
name: httpd
state: present
when: run_task

...

在当变量run_task为真的时候,则执行Install the web services的任务,否则跳过该任务。

下面是常用的判断条件列表。

判断条件示例
等于(字符串)ansible_machine == “x86_64”
等于(数字)max_memory == 512
常见判断<=、<、>、>=、!=
变量存在max_memory is defined
变量不存在max_memory is not defined
第一个变量在第二个变量的列表里var1 in var_all

多个条件组合

在使用when条件语句时,如果要判断组合条件,可以使用andor关键字来进行组合,并与括号分组条件。

when: ansible_facts.distribution == "RedHat" or ansible_facts.distribution == "Debian"

如果多个条件之间是and关系,也可以使用列表的形式来表示:

when:
- ansible_facts.distribution == "RedHat"
- ansible_facts.distribution == "CentOS"

使用括号能编写更复杂的条件:

when: >
(ansible_facts.distribution == "RedHat" and ansible_facts.distribution_major_version == "7")
or
(ansible_facts.distribution == "Fedora" and ansible_facts.distribution_major_version == "28")

loop循环和条件判断组合使用

例子中yum模块将要安装mariadb-server软件包,但是要求根目录满足剩余空间300MB以上才会安装,所以可以遍历目标主机上所有挂在的磁盘然后找到根目录再判断剩余空间,如果满足则安装,不满足则跳过该任务。

当对某个任务结合使用when和loop时,将对每一项都使用when语句进行判断。

tasks:
- name: Make Sure root have enough space to install mariadb
yum:
name: mariadb-server
state: present
loop: "{{ ansible_facts.mounts }}"
when: item.mount == "/" and item.size_available > 300000000

处理程序

有时候我们需要更改完配置文件后重启服务,但只想对文件有更改的情况下才重启服务。得益于Ansible的模块设计的幂等性,我们可以通过判断是否进行了更改而选择执行任务。这种方式叫处理程序。

处理程序可看作非活动任务,只有在使用notify语句激活后才会被触发,只有配置文件更新了并激活了该处理任务时,在到处理程序定义的位置时才会执行该任务。

处理任务只有在被激活的情况下才会执行,要注意的是处理程序不会按照你的激活顺序执行,而是按照激活程序的编写顺序以此判断是否被激活和执行。

- name: Enable internet services
hosts: all
become: yes
tasks:
- name: Add Web content
get_url:
url: http://materials.example.com/labs/playbook-review/index.php
dest: /var/www/html/index.php
mode: 0644
notify: restart apache
handlers:
- name: restart apache
systemd:
name: httpd
state: restarted
...

在第一次添加新页面后,激活restart apache处理程序,当开始执行handlers处理程序时,发现restart apache处理程序被激活,则执行该任务。

如果第二次执行该playbook你会发现处理程序并没有执行,因为任务Add Web content返回的状态是OK,由于没有进行任何更改所以不会执行重启httpd服务的操作。

如何处理任务失败

Ansible在执行任务的过程中,有任何一个任务执行失败,则不论接下来是否还有任务都不会执行。

但是如果你有特定的要求,即便是某一个任务执行失败,也继续往下执行其他任务的话,你需要在对应的任务上添加ignore_errors关键字。

- name: Latest version of notapkg is installed
yum:
name: notapkg
state: latest
ignore_errors: yes

任务失败后强制执行处理程序

哪怕是Play在执行任务的过程中失败了,也会强制执行已经被激活的处理程序。

---
- name: Enable internet services
force_handlers: yes
hosts: all
become: yes
tasks:
- name: Latest version of notapkg is installed
yum:
name: httpd
state: latest
notify:
- restart apache

- name: Always is False
command: /bin/false

handlers:
- name: restart apache
systemd:
name: httpd
state: restarted
...

指定任务失败的条件

使用failed_when关键字可以指定任务已失败的条件,条件语句和when的条件语句相同。

下面的例子是当有任何一个磁盘挂载点剩余的空间小于300MB则将任务结果返回为失败,即便是已经成功安装。

tasks:
- name: Make Sure root have enough space to install mariadb
yum:
name: mariadb-server
state: present
loop: "{{ ansible_facts.mounts }}"
failed_when: item.size_available < 300000000

如果failed_when关键字为true那么不管最后的执行结果如何,都会返回任务处理结果为失败。

指定是否报告任务的Changed结果

当任务对托管主机进行了更改后,如果有处理任务,则会激活处理任务。但在有些特殊模块如command模块,有时并不能按照预期来返回ok结果,所以只能返回Changed作为任务执行结果,这将会激活处理任务。

使用changed_when关键字可以控制何时返回Changed执行结果。他和failed_when关键字一样,和when的条件语句相同。你可以对某一变量是否在另一个变量中出现过进行判断。

tasks:
- shell:
cmd: /usr/local/bin/upgrade-database
register: command_result
changed_when: "'Success' in command_result.stdout"
handelers:
- name: restart database
service:
- name: mariadb
state: restarted

Ansible 块和错误自动处理

在Playbook中,块用来对任务进行逻辑分组,可用于控制任务的执行方式,例如任务块可以和when关键字连用,可以将某一条件用于多个任务。

---
- name: Enable internet services
hosts: all
become: yes
tasks:
- name: Installed Service
block:
- name: Latest version of notapkg is installed
yum:
name: httpd
state: latest

- name: Always is True
command: /bin/true
changed_when: false
when: ansible_facts.distribution == "RedHat"

- name: Always is True
command: /bin/true
changed_when: false
...

通过块,也可以与rescue和always语句连用来处理错误。如果块中包裹的任务有任何一个执行失败,则执行其rescue块中的任务来进行恢复。在block子句中的任务以及rescue运行结束后,最后运行always语句中包含的任务。

  • block:定义要运行的主要任务
  • rescue:定义在block块中有任务执行失败后要运行的任务
  • always:始终都要执行的任务,不论block和rescue子句中定义的任务执行成功还是失败都会运行always中定义的任务。
---
- name: Test Block
hosts: all
become: yes
tasks:
- name: Installed Service
block:
- name: Latest version of notapkg is installed
yum:
name: httpd
state: latest

- name: Always is True
command: /bin/true
changed_when: false
when: ansible_facts.distribution == "RedHat"
rescue:
- name: rescue block tasks
debug:
msg: "rescued block tasks"
always:
- name: Always has been exec
debug:
msg: "Always has been exec"
...

任务控制总结


vars变量


services:
- "{{ web_service }}"
- "{{ fw_service }}"

packages:
- "{{ web_package }}"
- "{{ ssl_package }}"
- "{{ fw_package }}"

ssl_cert_dir: /etc/httpd/conf.d/ssl

web_config_files:
- src: server.key
dest: "{{ ssl_cert_dir }}"
- src: server.crt
dest: "{{ ssl_cert_dir }}"
- src: ssl.conf
dest: /etc/httpd/conf.d
- src: index.html
dest: /var/www/html

PlayBook

- name: Playbook Control Lab
hosts: webservers
vars_files: vars.yml
tasks:
#Fail Fast Message
- name: check ram size
fail:
msg: "Cant install under free ram 256M"
when: ansible_facts.memtotal_mb < min_ram_mb and ansible_facts.distribution != "RedHat"

#Install all Packages

- name: Installed "{{ packages }}" Packages
yum:
name: "{{ packages }}"
state: present

#Enable and start services

- name: Enable and start "{{ services }}" services
service:
name: "{{ item }}"
state: started
enabled: true
loop: "{{ services }}"

#Block of config tasks

- name: Config Document Tasks
block:
- name: existed ssl_cret_dir
file:
path: "{{ ssl_cert_dir }}"
state: directory

- name: Config File existed
copy:
src: "{{ item.src }}"
dest: "{{ item.dest }}"
loop: "{{ web_config_files }}"
notify:
- restart web service
rescue:
- debug:
msg: >
One or more of the configuration changes faild, but the web service is still active.
#Configure the firewall
- name: Configed Firewalld Allowed
firewalld:
service: "{{ item }}"
state: enabled
permanent: true
immediate: true
loop:
- http
- https

#Add handlers
handlers:
- name: restart web service
service:
name: "{{ web_service }}"
state: restarted

🔲 ☆

Ansible Playbook

Ansible PlayBook

如何使用Ansible Playbook来执行复杂任务。其中包括要如何检查Playbook的语法。

PlayBook和临时命令

临时命令可以作为一次性命令临时的在目标主机上执行一项简单的任务。但是要发挥ansible全部实力的话就要用playbook来完成对选定主机的一个或一组指定的有序任务。playbook是一个文本文件,是以YAML格式编写的文本文件,拓展名通常使用yml保存。

如果当vim识别到该文本文件是yaml格式的话,自动将tab键的制表符改为2个空格,以便格式正确。
修改$HOME/.vimrc文件,在其中添加规则:autocmd FileType yaml setlocal ai ts=2 sw=2 et

Playbook格式

使用---三个破折号作为playbook的开头,...三个英文句号作为playbook结尾(结尾非必须)。

在这两个标记之间,会以一个play列表的形式来定义playbook。YAML列表中的项目以一个破折号加空格开头。

- apple
- orange
- grape

play本身是一个简直对集合。确保同一个play中的键应当具有相同的缩进量。下面的例子显示了具有三个键的YAML代码片段,其中前两个键具有简单的值。第三个将含有三个项目的列表作为值。

- name: example
hosts: webservers
tasks:
- first
- second
- third

上面实例play有三个键name、hosts、tasks,他们有着相同的缩紧。第一行的name前面加了一个破折号和一个空格,表示他是第一个键,下面的两个键hosts与tasks和name这个键是同级的。

name通常作为一个play的开头,用来描述这个play是干嘛的,可以省略但是不推荐这么做。

hosts指定了要在哪些主机目标上运行这个playbook,前面了解过为如何为主机分组,所以此处可以填写在inventory中编排好的主机或组。

hosts: 可以使用通配符(*)、&与符号、!非符号进行匹配。也可以使用逗号来分割使用的不同的主机或组。⚠️注意:如果要使用这种模式来匹配主机,为了不与shell字符冲突,应使用''一对单引号来包裹你的匹配模式。

tasks是该play要运行的任务列表。下面的例子中所表达的是:在一个叫example的Play中执行两个简单的任务task1和task2的PlayBook写法:

---
- name: example
hosts: webservers
tasks:
- name: task1
service:
name: httpd
enabled: true
- name: task2
user:
name: testuser
uid: 2000
state: present
...

一个更长点的例子:

---
- name: Enable internet services
hosts: serverb.lab.example.com
become: yes
tasks:
- name: Install internet services
yum:
name:
- firewalld
- httpd
- mariadb-server
- php
- php-mysqlnd
state: latest

- name: Enable Firewalld service
systemd:
name: firewalld
state: started
enabled: yes

- name: Allow 80 tcp port in firewalld
firewalld:
service: http
state: enabled
permanent: yes
immediate: yes

- name: Enable httpd services
systemd:
name: httpd
state: started
enabled: yes

- name: Enable mariadb services
systemd:
name: mariadb
state: started
enabled: yes

- name: Add Web content
get_url:
url: http://materials.example.com/labs/playbook-review/index.php
dest: /var/www/html/index.php
mode: 0644


- name: Check services working well
become: false
hosts: localhost
tasks:
- name: check services correct
uri:
url: http://serverb.lab.example.com
status_code: 200
...

执行playbook

ansible-playbook playbook.yml

检查编写的playbook中的语法错误

ansible-playbook –syntax-check playbook.yml

使用自动检查有时并不能精确的定位到问题位置所在,但是几乎都在问题的附近,也能提供一些指导。

以检查模式运行playbook

ansible-playbook -C playbook.yml

使用检查模式运行playbook并不会对目标主机进行任何更改。可以用来在真正运行前进行一次测试。

🔲 ☆

Ansible 基础概念

Ansible 基础概念

Ansible的基本环境、受控主机清单、Ansible的配置文件。

查看Ansible环境信息

absible --version

查看ansible控制端的环境信息,其中包括默认模块和自定义模块的文件目录以及ansible和python的版本。

[student@workstation ~]$ ansible --version
ansible 2.8.0
config file = /etc/ansible/ansible.cfg
configured module search path = ['/home/student/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
ansible python module location = /usr/lib/python3.6/site-packages/ansible
executable location = /usr/bin/ansible
python version = 3.6.8 (default, Apr 3 2019, 17:26:03) [GCC 8.2.1 20180905 (Red Hat 8.2.1-3)]

构建ansible清单

如果要用Ansible管理其他主机,需要将受控主机添加到清单中。

sudo vim /etc/ansible/hosts

使用静态清单配置

静态清单可以使用多种文件格式,包括INI和YAML。以下使用INI样式格式做例子。

在INI文件开头直接添加主机IP/域名

servera.yeefire.com
serverb.yeefire.com
192.168.1.211
192.168.1.232

将主机进行分组

[china-server]
blog.yeefire.com
server2.yeefire.com

[us-server]
us1.yeefire.com
us2.yeefire.com

分组嵌套

分组嵌套需要在组名处使用:children后缀。分组嵌套可以写在分组之前,他们之间没有绝对顺序。也可以编写与分组嵌套相同的组名,在使用这个名字的时候会将分组嵌套中所有的主机和该名字所对应的组名的主机包括在内。

[china-server]
blog.yeefire.com
server2.yeefire.com

[us-server]
us1.yeefire.com
us2.yeefire.com

[webservers:children]
china-server
us-server

[webservers]
test.app.yeefire.com

简化主机配置

如果要配置范围主机,一个个添加会很麻烦,通常他们都会有固定的格式。
使用[START:END]就可以范围。

[us-server]
us[001:009].yeefire.com

[test]
test[a:d].yeefire.com

localhost是一种特殊的存在,ansible知道他是本机,如果对localhost操作,ansible不使用ssh进行连接,这样可能导致执行结果和其他主机不一致。解决方案也很简单就是将localhost添加到inventory

ansible模块

模块是ansible的核心功能,近乎一些的操作都和模块相关。

使用ansible-doc可以阅读模块帮助手册

常用模块列表

模块类别模块描述
文件模块copy将本地文件复制到受管主机
-file设置文件的权限和其他属性
-lineinfile确保特定行是否在文件中
-synchronize使用rsync同步内容
软件包模块package自动检测操作系统的软件包管理器管理软件包
-yumyum管理软件包
-aptapt管理软件包
-dnfdnf管理软件包
-pip从PyPI管理Python软件包
系统模块firewalld使用firewalld管理任意端口和服务
-reboot重新启动计算机
-service管理服务
-user添加、删除、管理用户账户
NetTools模块get_url通过HTTP、HTTPS、FTP下载文件
-nmcli管理网络
-uri与Web服务交互

ansible -m setup

setup 模块用来收集主机详尽信息。

ansible -m setup localhost

在本地运行ansible的setup模块,收集本地的详尽信息。

ansible配置文件

有四种方式来让ansible读取配置文件,优先级依次由高到低:

使用环境变量

使用ANSIBLE_CONFIG环境变量,使用环境变量的优先级是最高的,也就是说如果Ansible运行时发现该变量存在,那么会直接使用它而不会理会其他三个位置的配置文件。

使用./ansible.cfg

如果在当前执行ansible的目录下存在ansible.cfg配置文件,那么在当没有配置环境变量时,就会使用此配置文件。(可以配置目录结构来方便管理不同的群组)

使用~/.ansible.cfg

使用当前用户Home目录下的ansible.cfg隐藏文件,在当前两个都没有配置的情况下,如果在用户家目录下存在配置文件则会使用此处的配置文件运行ansible。

使用/etc/ansible/ansible.cfg

使用/etc/ansible/ansible.cfg全局配置文件是优先级最低的选择,在上面三个地点都没有找到ansible配置文件时则会使用此处的配置文件。如果这个文件也不存在的话……那就完全按照ansible的默认来操作了。

配置文件参数

inventory主机清单

在配置文件中配置好了主机清单后,在运行临时命令或者Playbook时就可以不用手动指定主机清单。

参数值这里inventory可以是文件,也可以是文件夹。当参数是文件夹时,会使用文件夹下全部的inventory主机清单。

[defaults]
inventory = inventory
🔲 ☆

42Team-Flask框架

42Team Flask框架

前排提示

《42Team-Flask框架》系列教程仅限大连东软信息学院网络中心所属的42Team社团内部使用,该系列文档属于内部资料,仅用于所有42Team社团成员学习使用。

最近一次的更新日期:2020年4月24日

本次更新内容:请求与响应

目录

介绍和安装

基础部分

第一节课内容

第二节课内容

更新日志

2020年4月24日

  • 路由和视图(完)

2020年4月20日

  • HTTP协议快速了解(完)

2020年4月17日

  • 初始化文稿,新建README.md页面
  • Flask介绍(完)
  • 使用Pycharm来构建一个全新的Flask项目
❌