chef之cookbook入门简明手册

简介: 本手册基于markdown编写,因为论坛不能删除markdown文件,压缩后,由不方便故这里上传生成后的pdf(),令附上,markdown内容,方便大家自行修改编辑===========...
本手册基于markdown编写,因为论坛不能删除markdown文件,压缩后,由不方便故这里上传生成后的pdf(),令附上,markdown内容,方便大家自行修改编辑

===================================================

---
layout: post
title: "chef之cookbook绝对简明手册"
date: 2012-12-28 21:09
comments: true
categories: linux
---


### 1:楔子:

从前有个名叫chef的大厨,功夫甚为了得,通过如下几招:
<pre>
* 1:rpm-Uvh http://rbel.frameos.org/rbel6
* 2:yum -y install rubygem-chef-server
* 3:setup-chef-server.sh
</pre>
便能在一个叫做centos6.*(可能在其它地方也可以)的地方快速搭建一个内部餐厅,
然后在来招
<pre>
knife configure -i
</pre>
便能把餐厅给装修了
餐厅好了,该需要客人了,既然是内部客人,就该有自己的内部vip的凭着,于是chef大厨把“/etc/chef/validation.pem"当做内部餐厅的vip凭证,客人只要练这样一招"curl -L http://opscode.com/chef/install.sh | bash",然后在自己的"/etc/chef/client.rb",写上chef大厨餐厅的地址,自己的名字,自己需要的一些东西,类似这样:
<pre>
cat /etc/chef/client.rb
log_level :info #整个菜谱执行过程的记录级别
log_location STDOUT
chef_server_url 'http://172.58.0.61:4000' #chef大厨餐厅的地址
validation_client_name 'chef-validator'
node_name "172-58-0-64" #自己的名字
</pre>
然后在来招:
<pre>
chef-client
</pre>
就成为chef大厨餐厅的免认证,唯一VIP会员了。

这么复杂的东西,chef大厨也自写了一套秘籍:knife,来进行管理,knife种有不同的招式,来对不同的方面进行不的处理;使用起来也相当方便

比如给客人新加一道菜(客户吃什么也都是chef控制的,东西怎么做也是chef控制的,太霸道了)
<pre>
knife node  run_list add client1 nova
</pre>
在去掉一道菜:
<pre>
knife node  run_list remove client1 nova
</pre>
这套秘籍还有很多的招式,预知详情,输入knife,按回车

不过呢,chef大厨认为自己厨艺比较高,怕餐厅工作都自己搞,人多了忙不过来,就定了个规矩,上菜,只上菜谱,而且不主动给送(就是这么霸道);客户要什么菜,告诉大厨,大厨给你发过去;这样大厨就比较轻松了,客户累了一点(也就是是说真正干事的人是客户自己,客户在自己的家里按照菜谱做自己要的菜);不过这样做的好处就是可以用较低的成本,完成较多的工作;而且呢,客户也可以是一些小客户,在地球上没有独立或者固定地址的客户。

既然这样,也就是说,如果是仅仅是想到不错的菜,对菜的具体加工过程没有洁癖的话,只要去研究这个菜谱怎么搞,就好了;如果有洁癖,想吃很好的菜,那就去研究这个餐厅的工作机制吧,我想要的是吃上不错的菜,那么我就去研究这个菜谱怎么写;而我是个懒人,实现我的效果即可



### 2:菜谱(cookbook)

先看眼菜谱是什么样的

<pre>
tree openssh
openssh
├── attributes
│   └── default.rb
├── files
│   └── default
│       └── mysh
├── metadata.rb
├── recipes
│   ├── default.rb
│   └── pubkeys.rb
└── templates
    └── default
        ├── ssh_config.erb
        └── sshd_config.erb
6 directories, 7 files
</pre>


#### 1:我需要什么
* 1: 变量(attributes)
  存储那些会变,用的比较多,而且不常变的东西,也是就说这里存储的都是默认配置

  在这个文件里定义 penssh/attributes/default.rb

  可以这样定义:
  <pre>
  default['myssh'] = 'False'
  default['openssh']['server']['port'] = "22222"
  default['openssh']['server']['protocol'] = "2"
  default['openssh']['client']['forward_x11'] = "no"
  default['openssh']['client']['gssapi_authentication'] = "no"
  </pre>
  以default(因为这里都是默认配置嘛)开头,然后可以通过已中括号括起的字符串(用引号标起来了嘛)一级级的来组织我们的变量

* 2: 模板(templates)
  整体确定,部分内容会有变化的文本文件;一般这里存储的都是配置文件,所以每个配置文件都是独立的模板,存放openssh/templates文件夹下的

  模板文件名字可以自定义,以.erb结尾

  比如:
  <pre>
  cat openssh/templates/default/sshd_config.erb
\<% @settings.each do |key, value| %>
\<%         if value.kind_of? Array %>
\<%                 value.each do |item| %>
\<%=                 "#{key.split("_").map { |w| w.capitalize}.join} #{item}" %>
\<%                 end %>
\<%         else %>
\<%=         "#{key.split("_").map { |w| w.capitalize}.join} #{value}"%>
\<%         end %>
\<% end %>
  Port <%= @%node['openssh']['server']['port']>
  <% if ['openssh']['server']['port'] == 2222 %>
  Protocol <%= @node['openssh']['server']['protocol'] %>
  <% else %>
  Protocol 1 %>
  <% end %>
  </pre>

  这里实际就是ruby的语法(虽然我看不懂),上面那段比较复杂,看起来是一个循环,先不看;看看后面的简单的,可以对变量进行赋值
  看看后面那2行生成的内容是什么样的(哎,由果推因,无知啊)
  <pre>
  Port 2222
  Protocol 2
  </pre>
  原来<%= @var%>,var那里写上变量名就行了,这个变量名可以直接从第一步定义的变量中导入,只需要将那个"变量"中的default,变成node就可以了. 通过这样,我们可以对配置文件做基本的操作,只需要我们将变量按我们的思路去处理,也就是在这里对变量的处理是最主要的,一会就说怎么让配置文件中的变量搞的飞起来

  还有一点,猜对了,如果"变量"中的port那项不是2222的话,protocol就是1了

* 3: 文件(files)

  100年都不变的各种文件(\*.av,\*.:,\*.bin...)
  存在这里(openssh/files/default)的文件,会原封不懂的给客户,至于放到客户的什么地方,就有后面提到的动作控制了




#### 2:我做什么

有了上面的准备工作,下面就该操刀实干了

* 操作(recipes)

  openssh/recipes 下的以.rb结尾的文件称为一个个recipe,这里记录了每一步该用什么,怎么用,做什么;从前到后依次执行

##### Where there is a shell, there is a way !

shell是如此的强大,但是如果全部用shell来实现我们的需求,那我们刚才花的那几分钟岂不是白花了,不能让他白花;看看shell之外有有那些路可以走。

首先整理下,一般情况下,我们都需要对系统做什么操作(当然,我们需要的所有东西,基本都可以通过执行一个命令来实现,我认为,从某个角度来说,这就是对shell的另一种实现)

操作上有以下几种:

模式:
<pre>
模式 “模式名” do
  。。。 #这里写你
  。。。 #要做的操
  。。。 #做
end
</pre>

模式只能使用chef定义好的,模式名,可以自定义

* 1:对文件夹进行操作

  - 1:创建一个文件夹
  <pre>
   directory "/root/script" do
     owner "root"
     group "root"
     mode  0755
     action :create
   end
  </pre>

  创建一个/root/script的文件夹,用户属于root,用户组也属于root,权限为0755

  - 2:删除一个文件夹
  <pre>
  directory "/root/script" do
     recursive true  #递归删除
     action :delete
  end
  </pre>

  不要怀疑,刚看起来,对一个文件夹操作都要这么多行代码,真麻烦

* 2:对普通文件进行操作
  - 1 创建一个文件
  <pre>
  file "/root/123.txt" do
    owner "root"
    group "root"
    mode 00755
    action :create
  end
  </pre>
  - 2 删除一个文件
  <pre>
  file "/root/123.txt" do
    action :delete
  end
  </pre>



* 3:对命令进行操作
  - 2 执行一个命令
   <pre>
   execute "upgrade script" do
     command "shutdown -h 0"
     action :run
   end
   </pre>

  command 决定了执行什么命令(里面写什么就执行什么)
  action 怎么操作做这个命令(这是个通用选项),这里写的是执行,也是每次到这里都会执行这个命令
  这个操作每执行一次,你的系统就关机一次
* 5:对软件包进行操作
  支持redhat,debian系,忽视yum与apt,这个可以写出2种系统都兼容的命令
  - 1 安装一个软件
  <pre>
  package "dstat" do
      action :install
  end
  </pre>

  在模块名出写上,要操作的包的名字

  action 决定怎么操作做个包
    * install:安装
    * upgrade:升级
    * remove:卸载(centos系列种,相当于yum remove,删除了软件包,但没有删除配置文件)
    * purge:卸载包,删除配置文件
* 6:对模板进行操作

  个人认为:linux种对各种服务的配置与管理,基本上就是对配置文件的管理了,这里要多花精力
  <pre>
  address = '192.168.0.'+node['ipaddress'].split('.')[2]
  port = node['mysqld']['port']
  template "/etc/my.cnf" do
    source "my.cnf.erb"
    owner "mysql"
    group "mysql"
    mode 0755
    variables\(
    'bind-ipaddress' => address,
      …… #此处省略
      …… #若干行
    'port' => port
    \)
  end
  </pre>
  在template开始之前,可以对变量做各种处理,以达到我们想要的结果(处理的语法就是ruby的语法),在variables种可以放多个变量;这里我们也可以使用客户的一些信息,比如客户IP(node['ipaddress'])等,通过对定义的变量和客户的一些信息,在加上一些处理,基本就能满足大部分需求了。
  这个template会去"openssh/templates"文件下招my.cnf.erb这个文件,而且这个文件可能是这样的
  <pre>
  \*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
  bind-address = <%= @address %>
  \*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
  \*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
  port = <%= @port %>
  </pre>

  而最终系统的/etc/my.cnf就会是这样:
  <pre>
  \*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
  bind-address = 192.168.0.45
  \*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
  \*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
  port = 3306
  </pre>

* 7:对文件(1小节种提到的文件)进行操作
  这个主要是文件上传的功能,上传制定文件到客户的制定位置,并给与制定的权限
  <pre>
  cookbook_file "/root/script/src/mysql-5.1.56.tar.gz" do
    source "mysql-5.1.56.tar.gz"
    mode '0700'
    owner 'root'
    group 'root'
  end
  </pre>
  这个会去openssh/files/default目录下找名字为mysql-5.1.56.tar.gz的文件,然后上传到客户的/root/script/src/mysql-5.1.56.tar.gz,并切用户和用户组都是root,权限是0700

逻辑上有以下几种:

* 1:执行一个简单的操作

  通过上面的各种模式,就可以定义一个简单的操作了
* 2:执行操作甲后,执行另一个操作乙

  首先相信这样的需求,如果让我设计,我会怎么实现他:

  - 1:首先,操作甲执行完后,要通知乙下,告诉乙做什么,怎么做;这样乙才知道什么执行;
  - 2:而且操作乙平常是不执行的,否则等甲的通知也每什么意义了(当然有一种蛋都疼的情况就是,操作乙平常需要执行,操作甲执行后也需要执行)

  看看chef的实现方式:
  <pre>
  template "/etc/nginx/nginx.conf" do
    notifies :run, "execute[nginx_reload]", :immediately
  end
  execute "nginx_reload" do
    command "nginx -s reload"
    action :nothing
  end
  </pre>
  notifies:标记了,什么时候通知什么操作,做什么动作

  官方文档是这样写的:
  <pre>
  notifies :action, "resource_type[resource_name]", :notification_timing
  </pre>
  那么“notifies :run, "execute[nginx_reload]", :immediately”,这句话翻译成人话,就是这样的,马上运行名字为”nginx_reload”的命令;在结合场景翻译下:在对/etc/nginx/nginx.conf配置完成后,马上运行名字为”nginx_reload”的命令。

  action: 这里的nothing,标记了该操作,平时的不作为,如果吧这里的nothing改为run,那么就满足了上面说到的蛋蛋都疼的需求

* 3:这个操作,只在某种条件下执行

  其实这里应该有2个,该操作在某种条件下执行或者不执行:
  - 1:只有在什么情况下才做这个操作
  - 2:在这种情况下就不做这个操作

  如果将上面2个需求翻译成英语就是
  - 1 nly if
  - 2:not if

  翻译成chef的语法就是:(会英语就是好啊,学技术都这么快,语法都能猜出来了)
  - 1 nly_if
  - 2:not_if

  only_if和not_if的意义虽然不一样,但是用法是一样的

  这种情况下chef的操作代码就像是这样:
  <pre>
  execute 'restart_mysql' do
    command '/etc/init.d/mysqld restart'
    not_if 'netstat -ln |  grep 3306'
  end
  </pre>
  这个翻译成人话就是执行"netstat -ln|grep 3306",如果有输出,就不执行这个命令来,否则执行

* 8:必杀技之执行shell脚本

  既然有shell,就能实现我们的功能,如果上面的那些还满足不了,或者就喜欢写shell脚本,那么这个就是最好也是唯一的选择了

  <pre>
  script "install_nginx" do
    interpreter "bash"
    user "root"
    cwd "/usr/local/src"
    code <<-EOH
    wget 'http://nginx.org/download/nginx-1.2.1.tar.gz'
    tar -xzvf nginx-1.2.1.tar.gz -C /usr/local/src/
    git clone https://github.com/yaoweibin/nginx_tcp_proxy_module.git \
    /usr/local/src/nginx_tcp_proxy_module
    cd /usr/local/src/nginx-1.2.1/
    patch -p1 <  /usr/local/src/nginx_tcp_proxy_module/tcp.patch
    ./configure --add-module=/usr/local/src/nginx_tcp_proxy_module
    make
    make install
    EOH
    not_if 'ls /usr/local/src | grep nginx'
  end
</pre>

interpreter:这里指定了bash,既然需要制定,就说明这里的选择不仅仅一个,还应该有其它,还真猜对了,这里可以写 bash csh perl ruby python(当然应该是bash用的多些吧,反正如果让我执行py的话我是不会这样写的)

user:指定了在什么目录下运行,当然在脚本的第一行,cd 到某个目录也是一样的效果

code:指定了结尾的logo,这里,从当前行开始,一直到,EOH中间的都是要执行的脚本

not_if 在这里加了个判断,只要在/usr/local/src目录下没有名字nginx的文件时,才执行这个脚本

有了上面这些东西,就能够编写基本的cookbook的编写(我就仅仅知道这么多),但有时候,我们需要,对某一个更深入些,那么就去:[ http://wiki.opscode.com/display/chef/Resources#Resources-Script],就会得到你需要的了


### 3:杂项

* 1:变量

  - 1:针对一个用户的变量

    在attributes种可以定义默认的变量,但是有些时候,在莫个变量上,有些用户希望是自己独有的,这种情况,我们可以针对改用户做修改
   每个用户的情况,在chef餐厅里都有存储,可以通过"knife node"对用户进行操作:

    <pre>
   \# knife node
   FATAL: Cannot find sub command for: 'node'
   Available node subcommands: (for details, knife SUB-COMMAND --help)
   \*\* NODE COMMANDS \*\*
   knife node create NODE (options)
   knife node run_list add [NODE] [ENTRY[,ENTRY]] (options)
   knife node list (options)
   knife node edit NODE (options)
   knife node bulk delete REGEX (options)
   knife node show NODE (options)
   knife node from file FILE (options)
   knife node delete NODE (options)
   knife node run_list remove [NODE] [ENTRIES] (options)
   </pre>
  比如要对莫个客户做调整:
  <pre>
  \#knife node edit client_name
  </pre>
  会打开一个编辑器,显示当前用户的配置文件(json格式,要注意里面的东西一定要是json的否则,如果你些了半天,有个标点符号错,然后保存了,恭喜,你刚才些的白写了),
  <pre>
  {
    "name": "client_name",
    "run_list": [
      "recipe[os_init]",
      "recipe[openssh]"
    ],
    "normal": {
      "openssh": {
        "server": {
        },
        "client": {
        }
      },
      "manage": {
        "extra": [
          "user1",
          "user2"
        ]
      },
      "tags": [
      ]
    },
    "chef_environment": "_default"
  }
  </pre>
  在normal的下面有这样一段:
  <pre>
  "manage": {
    "extra": [
      "user1",
      "user2"
    ]
  },
  </pre>
  在recipe或者template中,如何使用这些信息呢?
  通过 node['manage']['default'] 这个变量就可以获取到一个内容为有2个元素('user1','user2')


  - 2:让一批用户都可以用的变量

    在attribute中的变量,所有用户都可以使用,在针对客户定义的变量,只有这个用才可以使用,但是如果有一批用户都需要这个变量呢?给这批用户一个个加多累啊,在说要改这个变量的时候,在一个个改更累,我们需要一个环境级别的变量,用户需要的时候,导入这个环境,即可。

        在chef中和cookbook并级的,还以一个environments的文件夹,这里就时存储各种变量的配置文件的
    <pre>
    \# knife environment
    FATAL: Cannot find sub command for: 'environment'
    Available environment subcommands: (for details, knife SUB-COMMAND --help)
    \*\* ENVIRONMENT COMMANDS \*\*
    knife environment list (options)
    knife environment create ENVIRONMENT (options)
    knife environment edit ENVIRONMENT (options)
    knife environment show ENVIRONMENT (options)
    knife environment delete ENVIRONMENT (options)
    knife environment from file FILE (options)
    </pre>

    随便写个存储环境变量的文件:
    <pre>
    vim environments/new.json
    </pre>
    这个也会打开一个编辑器,然活可以在里面写json格式的数据
    <pre>
    {
      "name": "openstack",
      "description": "",
      "cookbook_versions": {
      },
      "json_class": "Chef::Environment",
      "chef_type": "environment",
      "default_attributes": {
        "mysql": {
          "allow_remote_root": true,
          "root_network_acl": "%"
        }
      },
      "override_attributes": {
        "developer_mode": false,
        "enable_monit": true,
        "manage": {
          "extra": [
            "user3",
            "user4"
         ]
      }
      }
    }
    </pre>
    这个文件里面需要注意的有3块:name default_attirbute override_attribute

    name:当前环境的名字
    default_attribute:该环境中的默认属性
    override_attribute:该环境中的会覆盖到默认属性的属性

    环境变量定义好后,需要导入
    <pre>
    \# knife environment from file environments/new.json
    Updated Environment openstack
    </pre>
    环境变量导入后,在chef中就存在了,客户在要用的时候,还需要做一个操作:修改客户的配置:
      <pre>
      #knife node edit client_name
      {
        "name": "client_name",
         \*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
        "chef_environment": "openstack"
      }
      </pre>
    将chef_environment对应的值改成刚刚定义的变量名,在然后,在recipe或者template中,通过 node['manage']['default'] 这个变量就可以获取到一个内容为有2个元素('user3','user3')

* 2:针对特定类型的客户做不同处理

    比如我们需要对cenotos和ubuntu的系统分别上传不同的文件,或者不同的模板。当然我们可以在recipes种通过系统的类型来判断,做不同的操作。chef还给我们提供了一种方法。

    我们注意到在files和template些都有个default的文件夹,也是时默认从这里找我们需要的模板和文件;是不是centos的客户会首先重centos的文件夹下找呢?对就是这样的

    我们需要往不同的系统上传不同的文件或者模板时,只需要创建相应的文件夹,然后在文件夹种方式和default种同名的文件,不同的系统的客户,就会先来该系统的目录下查找,找不到,在去default目录下查找的
相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。 &nbsp; 相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情:&nbsp;https://www.aliyun.com/product/rds/mysql&nbsp;
目录
相关文章
|
2月前
|
API 数据安全/隐私保护 Windows
图文手把手教你 OpenClaw 对接阿里云百炼大模型
本教程为OpenClaw 2.7.1 Windows版接入阿里云百炼的超详细图文指南,涵盖API Key创建、密钥配置、模型测试及常见问题排查,步骤清晰、避坑实用,助新手10分钟完成大模型能力接入。(239字)
|
2月前
|
Rust JavaScript Java
PHP已死?谎言与真相:2025年PHP生态现状、薪资趋势与未来前景
每隔几年,技术圈就会响起“PHP已死”的论调。然而现实是,直至2025年,PHP仍然驱动着超过77%的网站,WordPress市场占有率持续攀升,Laravel和Symfony社区依然活跃。
431 0
|
XML 测试技术 数据库
深入理解自动化测试中的数据驱动方法
【4月更文挑战第23天】 在软件测试领域,为了提高测试效率和质量,自动化测试已成为不可或缺的实践。数据驱动测试(DDT)作为一种高效的自动化测试策略,它通过将测试逻辑与测试数据分离,允许测试人员以更灵活、可维护的方式设计用例。本文将探讨数据驱动方法的基本原理,实现方式以及在实际项目中的应用案例,旨在帮助读者深入理解并有效运用数据驱动方法来提升自动化测试的效率和质量。
|
编解码 运维 算法
非科班计算机专业的转码秋招记录
【2月更文挑战第26天】本文介绍地理信息科学(GIS)专业的2024届应届生,在研三上学期期间,寻找后端研发、软件开发等IT方向工作的非科班转码秋招情况~
556 3
非科班计算机专业的转码秋招记录
|
JSON 算法 算法框架/工具
【python】python指南(十二):Json与dict、list互相转换
【python】python指南(十二):Json与dict、list互相转换
509 0
【node】 cnpm|npm查看、修改镜像地址操作 换源操作
【node】 cnpm|npm查看、修改镜像地址操作 换源操作
5496 1
|
网络协议 数据库 C++
vs2017_enterprise正式版离线安装包bt下载
vs2017_enterprise正式版离线安装包bt下载 点击这里下载种子 磁力链接  安装前请先打开certificates目录,安装里面的三个证书 离线下载教程 : https://docs.
5040 0
|
机器学习/深度学习 编解码 固态存储
【机器视觉】Python+OpenCV+MediaPipe实时人流检测
MediaPipe 人脸检测是一种超快的人脸检测解决方案,带有 6 个地标和多人脸支持。它基于 BlazeFace,这是一种轻量级且性能良好的人脸检测器,专为移动 GPU 推理量身定制。检测器的超实时性能使其能够应用于任何需要准确的面部感兴趣区域作为其他任务特定模型输入的实时取景器体验。
1365 0
【机器视觉】Python+OpenCV+MediaPipe实时人流检测