0%

语文古诗默写清单

寒食

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

注解

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

白话文译文

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

创作背景

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

马诗

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

注释

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

白话文译文

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

创作背景

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

石灰吟

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

注释

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

白话文译文

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

创作背景

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

十五夜望月寄杜郎中

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

注解

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

白话译文

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

创作背景

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

迢迢牵牛星

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

注解

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

白话译文

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

创作背景

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

早春呈水部张十八员外

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

注解

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

白话译文

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

创作背景

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

春夜喜雨

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

注解

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

白话译文

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

创作背景

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

送元二使安西

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

注解

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

白话译文

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

创作背景

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

江上渔者

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

注解

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

白话译文

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

小雅·采薇

1
2
3
4
5
6
7
8
9
小雅·采薇
西周-《诗经》
采薇采薇,薇亦作止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,载渴载饥。我心伤悲,莫知我哀!

注解

1
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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
小雅:《诗经》中“雅”部分,分为大雅、小雅,合称“二雅”。雅,雅乐,即正调,指当时西周都城镐京地区的诗歌乐调。小雅部分今存七十四篇。薇:豆科野豌豆属的一种,学名救荒野豌豆,又叫大巢菜,种子、茎、叶均可食用。

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

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

莫(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
5
6
7
8
9
10
11
采薇采薇一把把,薇菜新芽已长大。说回家呀道回家,眼看一年又完啦。有家等于没有家,为跟玁狁去厮杀。没有空闲来坐下,为跟玁狁来厮杀。

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

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

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

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

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

创作背景

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

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

竹石

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

注解

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

白话译文

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

创作背景

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

竹石

泊船瓜洲

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

注解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
泊船:停船。泊,停泊。指停泊靠岸。

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

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

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

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

钟山:今南京市紫金山。

绿:吹绿,拂绿。

还:回。

白话译文

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

夜雨寄北

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

注解

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

白话译文

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

创作背景

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

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

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

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

Ansible 技巧之场

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

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

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

在模版中:

1
2
3
4
5
{% 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中(从网上找到的未经过测试,理论可行):

1
2
3
4
5
6
7
8
9
10
11
12
13
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中处理字符串的语法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
你可以对字符串进行取单个字符等操作: {{ 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主机清单中。可以指定代码块进行任务委派。

1
2
3
4
5
6
7
8
9
- 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关键字也可以用于代码块和任务。

1
2
3
4
5
6
- block:
- systemd:
name: elasticsearch
state: restarted
connection: local

仅运行一遍任务

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

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

1
2
3
4
5
6
7
8
9
10
11
12
- 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语法。在使用循环或者变量时不小心就会顺手把花括号加上。此时会发出警告。

1
2
3
4
5
错误用法:
when: "'{{item.hosts}}' in group_names"

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

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

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

1
2
3
4
5
6
7
错误用法:
when: "'{{item.hosts}}' in group_names"

---

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

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,则将按照导入的顺序一次运行.

1
2
3
4
5
6
7
8
9
10
11
12
13
---
- 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

1
2
3
4
5
6
7
8
9
10
- 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中。

1
2
3
4
5
6
7
- 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中。

1
2
3
4
- name: Play
hosts: localhost
tasks:
include_tasks: db.yml

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

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 主机模式

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

使用通配符匹配

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

1
2
3
- hosts: all
...
- hosts: '*'

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

1
- hosts: '*.example.com'

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

1
- hosts: '192.168*'

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

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

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

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

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

使用&!

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

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

1
- hosts: 'lab,&datacenter'

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

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

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

管理动态清单

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

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

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

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

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

管理多个清单

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

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

调整连接数forks

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

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

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

1
2
3
4
[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台节点两两一组,分批次执行任务。这样不会导致我们的业务因为重启全部的机器而终止。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
---
- 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的主机个数。

Ansible角色

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

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

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

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

1
2
3
4
[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文件包含角色的处理程序
meta main.yml中包含角色相关信息,如作者、许可证、平台、角色依赖项
tasks main.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即可使用。

1
2
3
4
- 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部分,来让任务在普通任务之后和激活的处理程序之后再运行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
---
- 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模块可以静态导入角色。

1
2
3
4
5
6
7
8
9
10
11
12
---
- 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中,其中还包含了如何去使用以及使用的例子。

1
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
├── 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查看示例和需要用到的变量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
---
- 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

1
2
3
4
selinux_booleans:
- name: 'http_enable_homedirs'
state: 'on'
persistent: 'yes'

设置SELinux fcontext上下文

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

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

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

设置SELinux端口

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

1
2
3
4
5
selinux_ports: 
- ports: '82'
setype: 'http_port_t'
proto: 'tcp'
state: 'present'

创建角色框架

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

ansible-galaxy init my_role

1
2
$ 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角色任务:

1
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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
---
# 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变量:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 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

Splunk 数据搜索和报表

Splunk 是?

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

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

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

阅读全文 »

Ansible模块的使用

文件模块

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

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

文件模块使用示例

确保目标主机上存在文件

1
2
3
4
5
6
7
- name: Touch a file and set permissions
file:
path: /home/student/touch.me
owner: student
group: root
mode: 0000
state: touch

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
  File: touch.me
Size: 14 Blocks: 8 IO Block: 4096 regular file
Device: 801h/2049d Inode: 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/2049d Inode: 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类型。

1
2
3
4
- 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

1
2
3
4
$ 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

1
2
3
4
5
- 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

1
2
# 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,则它仅会在目标主机不存在要复制的这个文件时才会进行复制。

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

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

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

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

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

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

1
2
3
4
5
6
7
8
- 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识别和保持幂等性。

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

在目标主机上删除文件

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

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

检索文件的详细信息

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

1
2
3
4
5
6
7
- name: Check all stat of /etc/passwd
stat:
path: /etc/passwd
register: results

- debug:
vars: results

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

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

1
2
3
4
- name: synchronize local file to server file
synchronize:
src: /etc
dest: /home/student/

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

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

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

构建Jinja2模版

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

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

部署Jinja2模板

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

1
2
3
4
5
6
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模版内,使用下面的引用变量语法即可。

1
{{ ansible_managed }}

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

控制结构

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

循环

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

1
2
3
{% for user in users %}
{{ user }}
{% endfor %}

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

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

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

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

1
2
3
{% for host in groups['all'] %}
{{ hostvars[host]['ansible_facts']['default_ipv4']['address'] }} {{ hostvars[host]['ansible_facts']['fqdn'] }} {{ hostvars[host]['ansible_facts']['hostname'] }}
{% endfor %}
1
2
3
4
5
6
7
8
9
---
- name: generate hosts
hosts: all
tasks:
- name: generate template
template:
src: hosts.j2
dest: /home/student/hosts
...

条件语句

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

1
2
3
{% if finished %}
{{ result }}
{% endif %}

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

变量过滤器

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

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

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

1
2
{{ output | to_json }}
{{ output | to_yaml }}

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

1
2
{{ output | to_nice_json }}
{{ output | to_nice_yaml }}

变量测试

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

1
2
3
4
5
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

1
2
3
4
- name: Install the httpd packages
dnf:
name: httpd
state: present

state关键字有如下参数:

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

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

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

安装Development Tools软件组的示例:

1
2
3
4
- name: Install the Development Tools group
dnf:
name: ‘@Development Tools’
state: present

安装postgresql数据库模块:

1
2
3
4
- name: Install the postgresql module
dnf:
name: '@postgresql:9.6/client'
state: present

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

1
2
3
4
- name: Install httpd
package:
name: httpd
state: present

收集已安装的软件包信息

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

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

1
2
3
4
5
6
7
8
9
---
- 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软件包信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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密钥。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
---
- 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后的密码才可以登陆系统。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
---
- 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模块可以管理受控主机中的用户组。

1
2
3
4
5
6
7
8
---
- 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 - 计划要执行的现有脚本文件
state absent/present 添加或删除命令或脚本的状态
unique yes/no 如果任务已在运行,则不会再次执行
units minutes/hours/days/weeks 时间单位

at模块使用示例:

1
2
3
4
5
6
7
8
9
10
---
- 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模块来创建计划任务。

1
2
3
4
5
6
- 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计划任务。

1
2
3
4
5
- 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。如果受控主机恢复运行则继续执行接下来的任务。

1
2
3
- 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}

变量和事实

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

阅读全文 »