**devops当中,运维工作层面上的,deploy相关层级的工作,大体上所需要用到的工具分为三个层级
Bootstraping (安装基本系统,ip地址主机名等),可以利用PXE,cobbler这样的解决方案来完成系统自动安装或者在云计算申请创建虚拟机实例
Configuration, 安装好系统之后需要配置一些服务启动起来(ansible,puppet,saltstack)
Command and Control;编排功能(puppet,ansible,)
puppet是用ruby语言研发的Configuration工具
**
puppet:Configuration
架构模式
apply:standalone 单机运行
master/agent:
manifest:(配置好后放在manifest 清单的文件当中)
resource 资源是抽像关管理以后,利用配置编程语言,来能够让用户定义脱离底层实现方式的,只需要描述资源目标结果状态的,这么一种配置语言
node
资源定义起来十分容易,type指明类型
资源:resource
type{‘title’:
atrribute => value,(属性信息,进行设定对应的值,从而定义目标资源)
…
}
在定义资源的众多属性中,有几个特殊属性
关系元参数:before/require, notify/subscribe
特殊参数:NameVar(对应的title就可以当做name或者path来使用)
目标状态:ensure
资源类型:group, user, package, service, file; 核心类型
exec, cron, notify, …
puppet describe
不同类型所用的属性并不一样 、
资源类型中还有三种
exec, 运行命令
cron,
notify
资源类型:
exec:
Executes external commands. Any command in an exec
resource must be able to run multiple times without causing harm — that is, it must be idempotent.
exec中的命令必须能重复N遍而不产生有危害的错误,拥有幂等性
exec资源,大体有如下属性:
command (namevar):要运行的命令;
cwd:The directory from which to run the command.(运行任何目录时都有工作目录,cwd,表示要先cd到哪个目录下,来运行这个命令,切换工作目录至目标目录中,指运行命令时所处的目录)
creates:文件路径,仅此路径表示的文件不存在时,command方才执行;(等于创建目录,只要目录不存在就执行,目录存在就不执行)**
user/group:运行命令的用户身份;
path:The search path used for command execution. Commands must be fully qualified if no path is (command如果使用相对路径,到哪去找这个command文件所在的位置,类似环境变量,所以叫search path)
specified.
onlyif:此属性指定一个命令,此命令正常(退出码为0)运行时,当前command才会运行;(跟creates类似,creates只能表示文件的存在性,如果要随意定义条件就需要onlyif)
unless:此属性指定一个命令,此命令非正常(退出码为非0)运行时,当前command才会运行;(这个命令失败,command才会执行)
refresh:重新执行当前command的替代命令;
refreshonly:仅接收到订阅的资源的通知时方才运行;(这个命令不会直接自动执行。只有接收到被通知事件才执行)
**
用创建目录的方式来演示
没有使用绝对路径,就必须指明path
creates 什么时候执行命令,只有知道目录路径不存在时才会执行
这里的资源没有ensure属性
尝试先不定义creates
已经有了再跑一遍试试、
命令本身是有错误的
对于目录,或者文件路径执行的操作,可以加上creates,这个目录不存在才创建这个目录
再跑一遍,就不会报错了
如何确定命令成功执行,很多时候需要加一个前提才能实现,现在
用创建用户来说明
onlyif表示前面命令成功了才执行,unless前面的命令执行失败了才执行,判断可以用id 判断用户是否存在,如果失败就说明不存在用户,就执行创建
先执行 id mogilefs 是否有这个用户,失败就执行useradd
也可以订阅一个资源,只有这个资源执行时,本地才执行
比如安装mogilefs的时候,这个rpm包不会自动创建用户,refreshonly 只有被触发时才能被执行,subscribe订阅一个资源
表示mogilefs这个包在安装时才执行
会告诉你这个资源不存在
需要在清单里定义package
因为yum没有安装所以执行失败,用户也就没创建
定义计划任务的
cron:
Installs and manages cron jobs. Every cron resource created by Puppet requires a command and at least one periodic attribute (hour, minute, month, monthday, weekday,or special).
至少有一个命令定义一个周期性属性
command:要执行的任务;
ensure:present/absent;(present定义,absent表示删除)
hour:
minute:
monthday:
month:
weekday:
user:以哪个用户的身份运行命令(创建给哪个用户的计划任务,不给一般都是管理员或当前用户)
target:添加为哪个用户的任务
name:cron job的名称; 靠name来引用
示例:
cron{‘timesync’:
command => ‘/usr/sbin/ntpdate 10.1.0.1 &> /dev/null’,
ensure => present,
minute => ‘*/3’,每隔三分钟,向指定服务器同步时间
user => ‘root’,
}
不给的时间都表示,每隔三分钟*
不想要可以删除,定义成ensure
再次查看就没有了
最简单的资源
notify:
Sends an arbitrary message to the agent run-time log.相当于echo一段信息给你
属性:
message:信息内容
name:信息名称;
必要的时候可以定义一个执行资源的触发了后,调用这个资源,告诉用户发生了什么事了,真正执行模块操作的时候,这段信息会记录到日志中去的,所以自定义消息可以用这个来实现
就是8种核心类型的资源
核心类型:
group: 组
user:用户
packge:程序包
service:服务
file:文件
exec:执行自定义命令,要求幂等
cron:周期性任务计划
notify:通知
多次调用可以在puppet使用变量
在 写shell脚本中,变量也有作用域的概念,在函数中使用local定义的变量生效周期只是当前函数上下文,函数调用是开始,返回是消失
在一个脚本定义的变量是全局的,对整个脚本都有效,如果在脚本定义变量使用环境变量,对整个shell进程都有效,环境变量的作用域是当前shell进程,本地变量作用域是当前shell脚本,所以一个shell定义的变量是当前脚本的全局,而脚本中定义了函数,在函数内部可以调用变量 ,因为作用域是当前脚本,包含函数,但是在外部就不能引用
puppet也有变量
puppet的变量值有很多类型,变量必须以$开头,赋值=
任何正常数据类(只要不是正则表达式的)都可以赋予puppet中的变量,如字符串
变量的值是有类型的,数值型,字符型,
puppet的变量类型有字符串,数值,布尔值,数组(数字索引的数组,在redis中叫列表),hash映射(关联数组,自定义索引的),
undef (变量未被赋值,未定义)
因此能作为puppet变量值的,有
数据类型:
字符型:引号可有可无;但单引号为强引用,双引号为弱引用;
数值型:默认均识别为字符串,仅在数值上下文才以数值对待;
数组:[]中以逗号分隔元素列表;
布尔型值:true, false;
hash:{}中以逗号分隔k/v数据列表; 键为字符型,值为任意puppet支持的类型;{ ‘mon’ => ‘Monday’, ‘tue’ => ‘Tuesday’, };
undef:未定义 ;(相当于在shell脚本中撤销一个变量unset)
每个变量都有两个名字,简单名称和完全限定名称FQN,变量是有作用域的
要引用一个变量,本来不在一个域的里面的,是引用不到的,但是实际上,可以用绝对路径来引用,相当于相对路径和绝对路径的概念一样,相对路径,引用的起始点只能是当前目录,如果使用当前目录下找不到的就需要使用绝对路径
对于puppet来讲,即便是不在一个作用域的变量引用也可以,但是需要使用绝对路径
数据类型:
字符型:引号可有可无;但单引号为强引用,双引号为弱引用;
数值型:默认均识别为字符串,仅在数值上下文才以数值对待;
数组:[]中以逗号分隔元素列表;
布尔型值:true, false;
hash:{}中以逗号分隔k/v数据列表; 键为字符型,值为任意puppet支持的类型;{ ‘mon’ => ‘Monday’, ‘tue’ => ‘Tuesday’, };
undef:未定义 ;(相当于在shell脚本中撤销一个变量unset)
这个特殊数据类型不能赋值变量的
=~匹配为真,不匹配为假
!~不匹配为真,匹配为假
?i忽略大小写,不把点号当换行符并且不忽略模式中的空白字符和注释
-表示引导选项,表示禁用的,没有-号表示启用,冒号隔开开始
判断$operationsystem的值是不是ubunto或者debian,就把packages这个变量赋值apache2
scope限定生效范围的,但引用的话可以用绝对路径
最外层是顶级作用域,puppet是一个master/agent模型,定义范围就是整个master和agent的puppet的范围,如果只针对单个agent节点生效的,叫做节点作用域
**
在一个node中执行的代码,在puppet中称为类。可重用代码是通过类来实现的,类还可以有子类,基类,父类,子类,外面的类 称为基类,在基类内部有子类
在class内部有一个example::child表示一个变量名,child变量就可以在当前使用,但是父类现在引用不了(类似函数中定义的变量,只能在函数中使用,在全局中无法引用,要想引用就需要在外面使用绝对路径引用)
对于父类,parent变量是可以在child中使用的,但是parent生成的代码不能咋other和four中使用,相当于平级,彼此之间不互相干涉,在node级别定义的,这三个class都可以用,甚至class的子class也可以使用,只要是子类都可以使用,top域的下面的子域都可以使用**
top只能用自己的变量,node不能用child,parent,other,four,因为看不见,因为作用域不在自己同一个作用域
每一个级别用两个冒号隔开
puppet有很多内建的变量
在ansible中有facter,使用facter来收集当前变量
facter -p可以打印出当前系统所有的变量
这些变量会自动传给puppet主机,所以每一个agent端周期请求代码时,会先发master,自己的主机名和所有的facter变量,服务端可以对这些变量做处理,引用某些变量,有条件的,给每一个客户端,生成独有的配置,用变量值替换来实现
有很多内建的变量
environment环境类型。
clientcert客户端证书,
clientversion 客户端版本
modulename模块名称
对变量实现所谓的比较操作
comparison opertators 比较操作符
boolean operators 布尔型操作符,与或非
arithmetic operators 算数操作符
<<左移一位
=~模式匹配
!~模式不匹配
in 表示 在什么中
在puppet内部就可以用if语句了
可以用if来写比较复杂的资源清单了,条件满足时该怎么干,不满足该怎么干
常用的流程控制语句,if,case,selector,unless=ifnot
现在创建一个用户,基于变量来使用
不同类型的资源,名字一样是可以的
告诉你组fdfs当前不存在,可以创建
创建4个用户,可以定义数组
跑一遍试试
不能在此处用数组来表示,对用户名来讲是不可以的,会当做一个字符串来使用,如果写成其他对应的数据格式,列表元素本来就写成这样,如果可以要用到数组的索引元素,表示第0个元素
就成功了
试试把引号去了,不加0
对name而言不支持创建四个用户,直接引用这个值是不行的
但是程序包可以这么干,跟命令本身有关系,如果一个命令可以接受多个参数,那就可以使用,但是这个由于调用的是useradd所以不支持
下面看使用流程控制语句完成更强大的功能
支持单分支,双分支,多分支,任何puppet清单中的任何内容都可以在if语句里编写
puppet流程控制语句:
if语句:
if CONDITION {
…
} else {
…
}
CONDITION的给定方式:
(1) 变量
(2) 比较表达式
(3) 有返回值的函数
if $osfamily =~ /(?i-mx:debian)/ {
$webserver = ‘apache2’
} else {
$webserver = ‘httpd’
}
如果是centos使用yum isntall安装nginx
facter -p可以写当前系统的版本
当前值是absent,要变成present,下一次实际运行就安装上了,不运行了,换一种
我们的系统不是fedora,所以我们的代码应该是不被执行的
否则就打印一句话
输出了一句话
条件写法有很多种
variables 只是一个变量(字符串,非空为真,空为加,数值,非0位真,0为假)
表达式
也可以写函数
CONDITION的给定方式:
(1) 变量
(2) 比较表达式
(3) 有返回值的函数
if $osfamily =~ /(?i-mx:debian)/ {判断osfamily 做模式匹配=~ /(?i-mx:debian)/ 如果你的操作系统类型不区分大小写的时候能匹配debian
$webserver = 'apache2'
} else {
$webserver = 'httpd'
}
装程序包
package{"$webserver":
ensure => installed,
before => [ File[‘httpd.conf’], Service[‘httpd’] ],
}
提供配置文件
file{‘httpd.conf’:
path => ‘/etc/httpd/conf/httpd.conf’,
source => ‘/root/manifests/httpd.conf’,
ensure => file,
}
启动服务
service{‘httpd’:
ensure => running,
enable => true,
restart => ‘systemctl restart httpd.service’,
subscribe => File[‘httpd.conf’],
}
属于redhat系,redhat系的包名都是httpd
装程序包
package{"$webserver":
ensure => installed,
before => [ File[‘httpd.conf’], Service[‘httpd’] ],(后面两个资源依赖于前面的包安装,用数组来表示,这个两个资源都要依赖于package)
}
提供配置文件
file{‘httpd.conf’:
path => ‘/etc/httpd/conf/httpd.conf’,
source => ‘/root/manifests/httpd.conf’,
ensure => file,
}
启动服务
service{‘httpd’:
ensure => running, 确保主机运行
enable => true,开机自动启动
restart => ‘systemctl restart httpd.service’,自定义restart命令
subscribe => File[‘httpd.conf’],订阅file类的资源,一旦file发生改变,这个服务就会refresh
}
定义条件时,如果你想使用模式匹配,=,匹配为真,不匹配为假~ ,正则表达式要写在双斜线中// (!~不匹配为真,匹配为假)
^开头
notic是puppet内建函数,也是打印信息的
多分支的if语句就适用case了
case关键词+控制表达式(对应返回的值或者是变量的值,能符合第一个分支就执行第一个语句)
case CONTROL_EXPRESSION {
case1: { … }
case2: { … }满足第二个就执行第二个语句
case3: { … }
…
default: { … }都满足不了,就执行default
}
表达式的结果与各个case比较
3.用有返回的函数,可以是字符串,也可以是变量,把控制变量的值和case的变量值做比较
判断这个变量的值,如果是solaris 就打印出solaris
如果是redhat,表示或者是centos,逗号表示满足其中一个才可以
变量值是否能给这个正则表达式模式所匹配,如果能就执行第三个语句,可以调用$1,$1表示第一组括号中间匹配到的内容
否则alien外星人
注意:在case语句中,必须明确给明要执行的语句,每个分段都必须是一段正常执行的代码
如果是ubunto或者是debian就是apache2,如果是redhat,centos就是httpd,其他的就是返回错误
是第一个分支
selector是更简单的case语句
**所不同的是,case语句每一个分支都是代码段,对于selector而言,都是返回一个值value,意思你不能执行任何代码,是直接把一个字符串扔回来,相当于函数一样,满足条件返回值
**
如果满足了ubunto或者debian返回的是apache2
满足centos和fedore 返回的是httpd
selector语句:
CONTROL_VARIABLE ? {控制变量来判断
case1 => value1,符合第一个分支就返回一个值
case2 => value2,
…
default => valueN,否则返回一个默认值
}
CONTROL_VARIABLE的给定方法:
(1) 变量
(2) 有返回值的函数
各case的给定方式:
(1) 直接字串;
(2) 变量
(3) 有返回值的函数
(4) 正则表达式模式;
(5) default
注意:不能使用列表格式;但可以是其它的selecor;
如果要用正则表达式就用斜线隔开
需要判定一个值返回赋值变量的时候,selector比case更加简洁
puppet3.0版本还支持unless语句
selector语句:
CONTROL_VARIABLE ? {
case1 => value1,
case2 => value2,
…
default => valueN,
}
CONTROL_VARIABLE的给定方法:
(1) 变量
(2) 有返回值的函数
各case的给定方式:
(1) 直接字串;
(2) 变量
(3) 有返回值的函数
(4) 正则表达式模式;
(5) default
注意:不能使用列表格式;但可以是其它的selecor;
$pkgname = $operatingsystem ? {
/(?i-mx:(ubuntu|debian))/ => 'apache2',
/(?i-mx:(redhat|fedora|centos))/ => 'httpd',
default => 'httpd',
}
package{"$pkgname":
ensure => installed,
}
示例2:
$webserver = $osfamily ? {
"Redhat" => 'httpd',
/(?i-mx:debian)/ => 'apache2',
default => 'httpd',
}
package{"$webserver":
ensure => installed,
before => [ File['httpd.conf'], Service['httpd'] ],
}
file{'httpd.conf':
path => '/etc/httpd/conf/httpd.conf',
source => '/root/manifests/httpd.conf',
ensure => file,
}
service{'httpd':
ensure => running,
enable => true,
restart => 'systemctl restart httpd.service',
subscribe => File['httpd.conf'],
}
对于manifest的定义,无非就是资源定义,资源清单