Salt小实践

前几天去厦门玩了一圈,回来发现一月都快结束了,Q1看来又要继续忙下去了。
最近看了看SaltStack,今天准备简单的实践一下。其实更早的时候就应该搞一个配置管理工具了,只不过有个自己的小工具能做批量管理,也就一拖再拖,现在看来,如果自动化运维想更进一步,这个东西就不能没有了。
今天想要使用Salt,完成一个简单的负载均衡,目标如下:

目标

  1. 三台服务器,1台做LB,另外两台做NODE,都用Nginx。当然完成配置后,加减或者调整机器角色应该是很容易的。
  2. 要有一些动态的东西,比如这次假装每天流量从0点开始随时间正比上升,nginx worker数量跟着变化。
  3. 全部操作应该都通过Salt进行,并且不要用cmd.run这种模块。

这个目标还是很简单的,刚好也能够把Grains/Pillar/State/Template/Schedule这些最基础的内容都包含进去,动手实践一下还是能增加理解的。主要参考了《Python自动化运维》的第十章。

先贴一下最后的目录结构吧:

/srv/
├── pillar
│   ├── lb_server.sls
│   ├── node_server.sls
│   ├── schedule.sls
│   └── top.sls
└── salt
    ├── data.sls
    ├── _grains
    │   └── nginx_grains.py
    ├── nginx
    │   ├── index.html
    │   ├── lb.conf
    │   └── node.conf
    ├── nginx.sls
    └── top.sls

服务器分组

配置/etc/salt/master,将服务器分为LB和NODE两组,前者使用Nginx进行负载均衡,后者使用Nginx提供WEB服务。修改后最好重启一次salt-master,在我的测试环境里,不重启时Targeting中生效,但Pillar中不可用。

nodegroups:
  lb_group: 'test02.opjasee.com'
  node_group: 'test03.opjasee.com,test04.opjasee.com'

自定义Grains

编写grains_modules(/srv/salt/_grains/nginx_grains.py),为动态更新Nginx配置提供参数。

import time, commands

def NginxGrains():
    grains = {}
    nginx_worker = 5
    max_open_file = 65535
    try:
        nginx_worker = int(time.strftime('%H', time.localtime(time.time())))/2 + 2
    except:
        pass
    try:
        getulimit = commands.getstatusoutput('source /etc/profile; ulimit -n')
        if getulimit[0] == 0:
            max_open_file = int(getulimit[1])
    except:
        pass
    grains['nginx_worker'] = nginx_worker
    grains['max_open_file'] = max_open_file
    return grains

同步并加载该模块,可以使用grains.item命令测试效果。

$ salt '*' saltutil.sync_all
$ salt '*' sys.reload_modules

自定义Pillar

  1. /srv/pillar/top.sls,定义lb_group和node_group节点的配置文件。在lb_server.sls中,只需要定义nginx源配置文件位置,在node_server.sls中,还额外定义了nginx数据目录及index.html模板的位置。

    base:
      lb_group:
        - match: nodegroup
        - lb_server
      node_group:
        - match: nodegroup
        - node_server
    
  2. /srv/pillar/lb_server.sls

    nginx:
      conf: salt://nginx/lb.conf
    
  3. /srv/pillar/node_server.sls

    nginx:
      root: /work/nginx/node
      conf: salt://nginx/node.conf
      data: salt://nginx/index.html
    

配置完成后使用salt '*' saltutil.refresh_pillar命令刷新。

配置State

  1. /srv/salt/top.sls,lb和node都需要安装nginx服务,node另外需要同步index.html模板等文件。

    base:
      '*':
        - nginx
      node_group:
        - match: nodegroup
        - data
    
  2. /srv/salt/nginx.sls,lb和node通过pillar中的定义获取不同的配置文件。

    nginx:
      pkg:
        - installed
      file.managed:
        - source: {{ pillar['nginx']['conf'] }}
        - name: /etc/nginx/nginx.conf
        - user: root
        - group: root
        - mode: 644
        - template: jinja
      service.running:
        - enable: True
        - reload: True
        - watch:
          - file: /etc/nginx/nginx.conf
          - pkg: nginx
    
  3. /srv/salt/data.slsindex.html模板展示一些节点信息,将nginx.conf放过来是为了在修改文件句柄等参数时方便观察。

    index.html:
      file.managed:
        - source: {{ pillar['nginx']['data'] }}
        - name: {{ pillar['nginx']['root'] }}/index.html
        - user: nginx
        - group: nginx
        - mode: 644
        - template: jinja
        - makedirs: True
    nginx.conf:
      file.managed:
        - source: {{ pillar['nginx']['conf'] }}
        - name: {{ pillar['nginx']['root'] }}/nginx.conf.txt
        - user: nginx
        - group: nginx
        - mode: 644
        - template: jinja
        - makedirs: True
    

模板文件

  1. /srv/salt/nginx/index.html

    <html>
    <h2>HOSTNAME: {{ grains['fqdn'] }}</h2>
    <h2>OS: {{ grains['os'] }} {{ grains['osrelease'] }}</h2>
    <h2>WORKER: {{ grains['nginx_worker'] }}</h2>
    <h2>MAX_OPEN_FILE: {{ grains['max_open_file'] }}</h2>
    <h2>ROOT: {{ pillar['nginx']['root']}}</h2>
    <h2>WEIGHT: {{ grains['num_cpus'] }}</h2>
    <h2><a href="nginx.conf.txt">nginx.conf</a></h2>
    </html>
    
  2. /srv/salt/nginx/lb.conf,需要注意使用pillar['master']['nodegroups']['node_group']获取的并不是直接可用的列表。另外权重简单的使用CPU个数确定。

    user    nginx;
    worker_processes  {{ grains['nginx_worker'] }};  
    
    error_log  /var/log/nginx/error.log;  
    
    pid        /var/run/nginx.pid;  
    
    events {  
        worker_connections  {{ grains['max_open_file'] }};  
    }  
    
    http {  
        include       /etc/nginx/mime.types;  
        default_type  application/octet-stream;  
        log_format log_main '$remote_addr - $remote_user [$time_local] "$request" '
                                '$status $body_bytes_sent "$http_referer" '
                                '"$http_user_agent" $http_x_forwarded_for $request_time '; 
        access_log  /var/log/nginx//access.log log_main;  
    
        sendfile        on;  
        tcp_nopush      on;  
        tcp_nodelay     on;  
    
        keepalive_timeout  60;  
    
        upstream test {  
        {% for node in pillar['master']['nodegroups']['node_group'].split(',') %}
          server {{ node }}:80 weight={{ grains['num_cpus'] }} max_fails=3 fail_timeout=30s;
        {% endfor %}
         }  
    
        server {  
                listen       80;  
                server_name  test.opjasee.com;     
    
                location / {  
                  proxy_connect_timeout   3;  
                  proxy_send_timeout      30;  
                  proxy_read_timeout      30;  
                  proxy_pass http://test;  
                }  
       }  
    }
    
  3. /srv/salt/nginx/node.conf

    # {{ grains['fqdn'] }}
    user    nginx;
    worker_processes  {{ grains['nginx_worker'] }};  
    
    error_log  /var/log/nginx/error.log;  
    
    pid        /var/run/nginx.pid;  
    
    events {  
        worker_connections  {{ grains['max_open_file'] }};  
    }  
    
    http {  
        include       /etc/nginx/mime.types;  
        default_type  application/octet-stream;  
        log_format log_main '$remote_addr - $remote_user [$time_local] "$request" '
                                '$status $body_bytes_sent "$http_referer" '
                                '"$http_user_agent" $http_x_forwarded_for $request_time '; 
        access_log  /var/log/nginx/access.log log_main;  
    
        sendfile        on;  
        tcp_nopush      on;  
        tcp_nodelay     on;  
    
        keepalive_timeout  60;  
    
        server {  
                listen       80;  
                server_name  _;     
    
                location / {  
                  root {{ pillar['nginx']['root'] }};
                  index index.html;
                }  
       }  
    }
    

此时执行salt '*' state.highstate即可完成节点上的服务部署。

定时更新

将调度配置在Master Pillar上比较灵活,具体方法为:

  1. /srv/pillar/top.sls增加两行:

    base:
      '*':
        - schedule
    
  2. /srv/pillar/schedule.sls中写入以下内容:

    schedule:
      highstate:
        function: state.highstate
        minutes: 5
    
  3. 刷新pillar配置并观察:

    $ salt '*' saltutil.refresh_pillar
    
Loading Disqus comments...
Table of Contents