Saltstack自动化部署nginx
作者:彬彬
前言
为什么写这个题材? 上周去武汉高铁路上,拜读宇哥(刘宇)的Puppet实战,里面讲解的细节,让我萌生写此题材的想法;目前saltstack社区并没有像puppet那样提供nginx部署模块,但CSSUG(中国saltstack用户组)已经有贡献者分享了openstack、moosefs、zabbix、mysql模块内容分享了。写这篇文章目的,是想分享自己写的生产模块,同时希望更多人分享自己的模块,让saltstack变得容易使用、更好。
日常部署nginx可以概括为两类:
1.配置变更,新增虚拟主机、安全设置、日志切割
2.软件升级,nginx某版本出现漏洞
前期规划
软件安装
推荐大家在自己环境里部署个Yum仓库,方便部署软件包,好处就不用说了,这块内容我就略过了。
Nginx部署
虚拟主机文件和主配置分开配置,利用pillar和state来做新增虚拟主机,在state存储描述信息,而pillar存储真实数据,通过递增pillar内容来实现虚拟机主机添加,日后可以把pillar信息外存到数据库,做个平台来管理pillar信息。
针对机器设备不一致,CPU核数就不一,这时就可以用saltstack grains的{{grains[‘num_cpus’]}}来配置,
日志切割利用file states和cron states实现。
安全设置,以非root用户启动,在SLS定义变量,在文件当中以{{ nginx_user }}引用
部署
1. 创建nginx模块目录
$ mkdir -p /srv/salt/nginx/files
2. 创建nginx安装代码
$ vi /srv/salt/nginx/install.sls {% set nginx_user = 'nobody' + ' ' + 'nobody' %} base: pkgrepo.managed: - humanname: CentOS-$releasever - Base - baseurl: http://agent.domain.com/centos/6.3/x86_64/ - gpgcheck: 0 - gpgkey: http://agent.domain.com/centos/6.3/x86_64/RPM-GPG-KEY-CentOS-6 nginx: pkg.installed: - name: nginx - require: - pkgrepo: base file.managed: - name: /usr/local/nginx/conf/nginx.conf - source: salt://nginx/files/nginx.conf - template: jinja - defaults: nginx_user: {{ nginx_user }} num_cpus: {{grains['num_cpus']}} service.running: - name: nginx - enable: True - watch: - pkg: nginx - file: nginx cron_nginx_logrote: file.managed: - name: /opt/run/logrote.sh - source: salt://nginx/files/logrote.sh cron.present: - name: sh /opt/run/logrote.sh - user: root - minute: 1 - hour: 0 - require: - file: cron_nginx_logrote
以上代码实现yum仓库、nginx部署、安全设置、日志切割:
Set命令: 将nginx启动用户定义为变量,在nginx.conf里引用替换
Pkgrepo sate: 创建一个yum repo,让客户端通过此repo下载nginx软件包
Pkg state:从yum repo下载nginx,但依赖base.repo先执行成功,目前salt不能实现按顺序执行,期望日后能完善这块。不然写require和order有点不够理想
File state:同步nginx主配置文件和日志切割脚本到minion上,其中nginx主配置文件中引用grains实现CPU核数不一问题,利用set变量实现nginx启动用户设置,当用grains、set、pillar来实现内容替换,请用jinja模板,推荐在用jinja时使用defaults定义变量。
Service state: 定义nginx服务状态,开机自动启动,当包和文件发生变化时重启;
Cron state:定义切割脚本计划任务。每天0点采用root进行日志切割,依赖file state。验证使用crontab -u root -l
3.创建虚拟主机配置代码
$ vi /srv/salt/nginx/vhost.sls include: - nginx.install {% if 'vhost' in pillar %} {% for eachvhost in pillar['vhost'] %} {% set vhost_id = 'web-vhost-' + eachvhost.name %} {{ vhost_id }}: file.managed: - name: {{ eachvhost['target'] }} - source: {{ eachvhost['source'] }} - watch_in: - service: nginx {% endfor %} {% endif %}
4.创建pillar文件
$ vi /srv/pillar/nginx/vhost.sls vhost: - name: web source: salt://nginx/files/web.domain.com.conf target: /usr/local/nginx/conf/vhost/web.domain.com.conf - name: call source: salt://nginx/files/call.domain.com.conf target: /usr/local/nginx/conf/vhost/call.domain.com.conf
上述3、4代码,实现pillar管理虚拟主机,简单来说,在pillar定义3个字段,标识、虚拟机文件源路径以及目标路径。通过在state里for循环pillar里的信息,实现虚拟机管理。
5.nginx主配置文件代码如下
$ vi /srv/salt/nginx/files/nginx.conf user {{ nginx_user }}; worker_processes {{grains['num_cpus']}}; //grains引用 error_log /opt/log/nginx/nginx_error.log crit; pid /opt/run/nginx.pid; #Specifies the value for maximum file descriptors that can be opened by this process. worker_rlimit_nofile 65535; events { use epoll; worker_connections 65535; } http { include mime.types; default_type application/octet-stream; charset utf-8; server_names_hash_bucket_size 128; client_header_buffer_size 32k; large_client_header_buffers 4 32k; client_max_body_size 300m; sendfile on; tcp_nopush on; keepalive_timeout 60; tcp_nodelay on; server_tokens off; client_body_buffer_size 512k; gzip on; gzip_min_length 1k; gzip_buffers 4 16k; gzip_http_version 1.1; gzip_comp_level 2; gzip_types text/plain application/x-javascript text/css application/xml; gzip_vary on; log_format wwwlogs '$remote_addr - $remote_user [$time_local] "$request" '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" $http_x_forwarded_for' ; #limit_zone crawler $binary_remote_addr 10m; include vhost/*.conf; }
6.创建虚拟主机配置,虚拟主机目录在软件包中已经被定义了,所以创建就省去了。
$ vi /srv/salt/nginx/files/web.domain.com.conf server { listen 80; server_name web.domain.com; index index.php; root /opt/www/web; location ~ \.php$ { fastcgi_pass unix:/var/run/php-fpm.sock; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi.conf; } access_log /opt/log/nginx/access.log wwwlogs; }
7.创建日志切割脚本,脚本来自puppet实战,生产环境是使用logrotate切割
$ vi /srv/salt/nginx/files/logrote.sh #!/bin/bash logpath=/opt/log/nginx/ pidfile=/opt/run/nginx.pid ACCESS_LOG="${logpath}$(date -d "yesterday" + "%Y")/$(date -d "yesterday" + "%m")/$(date -d "yesterday" + "%Y%m%d").log" mkdir -p ${logpath}$(date -d "yesterday" + "%Y")/$(date -d "yesterday" + "%m")/ mv ${logs_path}access.log $ACCESS_LOG kill -USR1 `cat $pidfile` /bin/gzip -9 $ACCESS_LOG find ${logs_path} -name "*.log.gz" -mtime +7 | xargs rm -f
8.创建salt和pillar的top.sls入口文件和nginx.init
$ vi /srv/salt/nginx/init.sls include: - nginx.install - nginx.vhost
引用路径是以/srv/salt为开始。为了top.sls书写简单,一般每个模块下都写个init.sls, 然后用include包含要执行的sls
$ vi /srv/salt/top.sls base: //指定环境 '*': //minion指定,这里是所有 - nginx //应用nginx.init
在SLS命名里,一个子目录里存在init.sls文件,那么nginx/init.sls可以简写为nginx,但是如果同时存在nginx/init.sls和nginx.sls,那么nginx/init.sls会被忽略,只会把nginx.sls当做nginx。所以这里注意下
$ vi /srv/pillar/top.sls base: ‘*’: - nginx.vhost
9.最后同步SLS代码到minion
$ salt ‘*’ state.highstate