Saltstack自动化运维部署

saltstack是基于python开发的一套C/S架构配置管理工具,底层使用ZeroMQ消息队列进行通信,使用SSL证书签发的方式进行认证管理,ZeroMQ使saltstack能快速在成千上万台机器上进行各种操作,它是一款消息队列软件saltstack通过消息队列来管理成天上万台主机客户端,传输指令相关操作,而且采用RSA key方式进行身份确认,传输采用AES方式进行加密,以保证它的安全性。
saltstack主要包含三个部分分别是python软件集,saltstack软件集,ZeroMQ消息队列软件
saltstack软件是一个C/S架构的软件,通过管理端下发指令,客户端接受指令的方式进行操作,管理端成为master,客户端成为minion,saltstack客户端minion在启动时,会自动生成一套密钥包含公钥和私钥。之后将公钥发给服务器端,服务器端验证并接收公钥,以此建立可靠且加密的通信链接,同时通过ZeroMQ消息队列在master和minion之间建立系统通信桥梁,Master上执行某条指令通过消息队列下发到各个Minions去执行,并返回结果其中Daemon运行于每一个成员内的守护进程,承担着发布消息及端口监听的功能对应的端口分别为4505和4506。

命令的发布过程:
1.Saltstack的Master与Minion之间通过ZeroMQ进行消息传递,使用ZeroMQ的发布订阅模式,连接方式包括TCP和IPC。
2.在Master和Minion建立互信之后,salt命令,将cmd.run ls命令从salt.client.LocalClient.cmd_cli发布到Master,获取一个Jobid,很剧Jobid获取命令执行结果。
3.Master接收到命令后,将要执行的命令发送给客户端minion。
4.Minion从消息总线上接收到要处理的命令,交给minion._handle_aes处理。
5.Minion._handle_aes发起一个本地线程调用cmdmod执行ls命令,线程执行完ls后,调用Minion._return_pub方法,将执行结果通过消息总线返回给master。
6.Master接收到客户端返回的结果,调用master._handle_aes方法将结果写入文件。
7.Salt.client.LocalClient.cmd_cli通过轮询获取Job执行结果,将结果输出到终端。

服务的安装及基本配置
server1   Master  IP:172.25.62.1
server2   Minion   IP:172.25.62.2
server3   Minion   IP:172.25.62.3
注意防火墙关闭 selinux状态disabled 三台主机之间相互有解析 因为yum源中没有saltstack相关的rpm包所以我们使用自己的yum源
本地yum源配置

在server1上安装master,server2和server3上安装MInion


server1和server2和server3上都安装tree和lsof    做实验用

将从节点指向主节点  在server2和server3上


启动slave服务  启动服务后才会生成秘钥有兴趣的可以试一下

master端查看并启动服务   因为这里我已经允许过了 所以有所不同
Saltstack自动化运维部署_第1张图片
查看master端的公钥和master发给minion的公钥


查看minion端发送给master端的公钥

由此可知,其验证是双向验证,即master端将其公钥发送到minion端,minion也将自己的公钥发送给master端
Saltstack自动化运维部署_第2张图片
其中405负责发送数据到客户端,4506负责接收客户端的数据到服务器。

基础应用
部署远端httpd服务,在master上定义路径,并重启服务

Saltstack自动化运维部署_第3张图片

了解YAML:默认的sls文件的renderer 是YAML renderer ,YAML是一个有很多强大特性的标记性语言,salt 使用了一个YAML的小型子集,映射非常常规的数据结构,向列表和字典,YAML renderer 的工作是将YAML数据格式的结构编译成python数据结构给salt使用
规则一 :
缩进: YAML 使用一个固定的缩进风格表示数据层结构关系,salt需要每一个缩进级别都有两个空格组成,不要使用tab
规则二:
冒号:python 的字典当然是简单的键值对
规则三:
短横杠:想要表示列表项,使用一个短横杠加一个空格,多项使用同样的缩进级别作为同一列表的一部分。

安装httpd


其中apache-install只是一个名字pkg.installed中pkg是数据包处理的类,是一个大的方式,installed 表示其是对数据包进行安装处理-pkgs 用于指定安装多个数据包
执行,安装httpd
Saltstack自动化运维部署_第4张图片
Saltstack自动化运维部署_第5张图片
server2上查看有没有安装httpd和php

在master上设置httpd的部署和启动

Saltstack自动化运维部署_第6张图片
其中service也是一个大的类,而running是其中的方法其中有reload、restart、enable等
Saltstack自动化运维部署_第7张图片
在master上执行完毕之后 到server2上看httpd服务有没有启动


配置httpd配置文件并推送

将server2上的httpd的配置文件发送给server1  两个文件的md5码相同有兴趣的可以查看一下
Saltstack自动化运维部署_第8张图片

Saltstack自动化运维部署_第9张图片
其中使用到了file.managed模块
--name:表示目标目录,及客户端对应的目录
--source:表示配置文件的来源路径,其是相对于/srv/salt的路径

正常推送一次
Saltstack自动化运维部署_第10张图片
在server1上修改htttpd的默认端口为8080再推送一次




到server2上查看端口是否改变


源码推送nginx
在server1上获取需要的nginx安装包,解决依赖问题
Saltstack自动化运维部署_第11张图片
获取安装包,放到指定的目录下
Saltstack自动化运维部署_第12张图片
Saltstack自动化运维部署_第13张图片
推送nginx服务
Saltstack自动化运维部署_第14张图片
Saltstack自动化运维部署_第15张图片
同样的这里只是进行了服务的安装,nginx服务并没有启动
将server3上nginx的配置文件发给server1
Saltstack自动化运维部署_第16张图片
Saltstack自动化运维部署_第17张图片
对于server3来说没有nginx用户  在采用yum安装的时候会帮你创建nginx用户



Saltstack自动化运维部署_第18张图片

include:
  - nginx.install
  - user.adduser

/usr/local/nginx/conf/nginx.conf:
  file.managed:
    - source: salt://nginx/files/nginx.conf

/usr/local/nginx/html/index.html:
  file.managed:
    - source: salt://nginx/files/index.html

nginx-service:
  file.managed:
    - name: /etc/init.d/nginx
    - source: salt://nginx/files/nginx
    - mode: 755

  service.running:
    - name: nginx
    - enable: true
    - reload: true
    - require:
      - user: nginx
    - watch:
      - file: /usr/local/nginx/conf/nginx.conf

nginx的启动脚本和推送的默认页面,将准备好的nginx启动脚本放到指定的目录中

#!/bin/sh
#
# nginx        Startup script for nginx
#
# chkconfig: - 85 15
# processname: nginx
# config: /usr/local/nginx/conf/nginx/nginx.conf
# pidfile: /usr/local/nginx/logs/nginx.pid
# description: nginx is an HTTP and reverse proxy server
#
### BEGIN INIT INFO
# Provides: nginx
# Required-Start: $local_fs $remote_fs $network
# Required-Stop: $local_fs $remote_fs $network
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: start and stop nginx
### END INIT INFO

# Source function library.
. /etc/rc.d/init.d/functions

if [ -L $0 ]; then
    initscript=`/bin/readlink -f $0`
else
    initscript=$0
fi

#sysconfig=`/bin/basename $initscript`

#if [ -f /etc/sysconfig/$sysconfig ]; then
#    . /etc/sysconfig/$sysconfig
#fi

nginx=${NGINX-/usr/local/nginx/sbin/nginx}
prog=`/bin/basename $nginx`
conffile=${CONFFILE-/usr/local/nginx/conf/nginx.conf}
lockfile=${LOCKFILE-/var/lock/subsys/nginx}
pidfile=${PIDFILE-/usr/local/nginx/logs/nginx.pid}
SLEEPMSEC=${SLEEPMSEC-200000}
UPGRADEWAITLOOPS=${UPGRADEWAITLOOPS-5}
RETVAL=0

start() {
    echo -n $"Starting $prog: "

    daemon --pidfile=${pidfile} ${nginx} -c ${conffile}
    RETVAL=$?
    echo
    [ $RETVAL = 0 ] && touch ${lockfile}
    return $RETVAL
}

stop() {
    echo -n $"Stopping $prog: "
    killproc -p ${pidfile} ${prog}
    RETVAL=$?
    echo
    [ $RETVAL = 0 ] && rm -f ${lockfile} ${pidfile}
}

reload() {
    echo -n $"Reloading $prog: "
    killproc -p ${pidfile} ${prog} -HUP
    RETVAL=$?
    echo
}

upgrade() {
    oldbinpidfile=${pidfile}.oldbin

    configtest -q || return
    echo -n $"Starting new master $prog: "
    killproc -p ${pidfile} ${prog} -USR2
    echo

    for i in `/usr/bin/seq $UPGRADEWAITLOOPS`; do
        /bin/usleep $SLEEPMSEC
        if [ -f ${oldbinpidfile} -a -f ${pidfile} ]; then
            echo -n $"Graceful shutdown of old $prog: "
            killproc -p ${oldbinpidfile} ${prog} -QUIT
            RETVAL=$?
            echo
            return
        fi
    done

    echo $"Upgrade failed!"
    RETVAL=1
}

configtest() {
    if [ "$#" -ne 0 ] ; then
        case "$1" in
            -q)
                FLAG=$1
                ;;
            *)
                ;;
        esac
        shift
    fi
    ${nginx} -t -c ${conffile} $FLAG
    RETVAL=$?
    return $RETVAL
}

rh_status() {
    status -p ${pidfile} ${nginx}
}

# See how we were called.
case "$1" in
    start)
        rh_status >/dev/null 2>&1 && exit 0
        start
        ;;
    stop)
        stop
        ;;
    status)
        rh_status
        RETVAL=$?
        ;;
    restart)
        configtest -q || exit $RETVAL
        stop
        start
        ;;
    upgrade)
        rh_status >/dev/null 2>&1 || exit 0
        upgrade
        ;;
    condrestart|try-restart)
        if rh_status >/dev/null 2>&1; then
            stop
            start
        fi
        ;;
    force-reload|reload)
        reload
        ;;
    configtest)
        configtest
        ;;
    *)
        echo $"Usage: $prog {start|stop|restart|condrestart|try-restart|force-reload|upgrade|reload|status|help|configtest}"
        RETVAL=2
esac

exit $RETVAL


在发布页面中写nginx.page

[root@server1 files]# echo nginx.page > index.html

向server3推送nginx
Saltstack自动化运维部署_第19张图片
Saltstack自动化运维部署_第20张图片
在server3上查看nginx服务相关
Saltstack自动化运维部署_第21张图片


推送多个主机不同的服务


推送   高级推
Saltstack自动化运维部署_第22张图片
Saltstack自动化运维部署_第23张图片


推送集群Nginx+httpd+haproxy
用server1部署haproxy,安装salt-minion

将server1上的minion指向master


启动服务并允许注册
Saltstack自动化运维部署_第24张图片
安装haproxy编写配置文件


编写配置文件,包括服务的重启动,自启动等
Saltstack自动化运维部署_第25张图片
创建准用的文件夹存放haproxy的配置文件 并进行修改

Saltstack自动化运维部署_第26张图片
推送多个服务

Saltstack自动化运维部署_第27张图片
Saltstack自动化运维部署_第28张图片


写一个nginx和httpd默认发布页面


测试一下  因为之前改过apache的端口 访问的时候加上更改的端口号



salt的相关命令    1.查找server2的ip  2.查找server2的uuid  3.查看server2的系统类型
Saltstack自动化运维部署_第29张图片

Grians匹配

salt -G 'os:RedHat' cmd.run 'touch /mnt/han'    ##在操作系统。。Mnt下建文件
[root@server1 salt]# salt -G 'os:RedHat' test.ping  ##尝试链接
[root@server1 salt]# salt -G 'os:RedHat' cmd.run hostname  
##run 一个长命令要用单引号引起来
[root@server1 salt]# salt -G 'os:RedHat' cmd.run 'ip addr show eth0'  
##在操作系统为redhat的主机上显示eth0信息


Saltstack自动化运维部署_第30张图片
Saltstack自动化运维部署_第31张图片

设置标签对于server2上的httpd服务


重启服务

对于server3上的nginx也需要进行更改




效果
Saltstack自动化运维部署_第32张图片
也可以添加用户 方便管理,查看




每台机器有了不同的标示之后,便于找出他们的不同点进行不同的部署
Saltstack自动化运维部署_第33张图片
高级推送走一波
Saltstack自动化运维部署_第34张图片
Saltstack自动化运维部署_第35张图片

Grains适用于静态  pillar适用于动态
Saltstack自动化运维部署_第36张图片
刷新动态参数
Saltstack自动化运维部署_第37张图片
在server2上


在server3上


测试在server1上
Saltstack自动化运维部署_第38张图片

自定义模块 之后进行同步

Saltstack自动化运维部署_第39张图片

到server3上查看
Saltstack自动化运维部署_第40张图片
执行同步后的模块
在server1上执行
Saltstack自动化运维部署_第41张图片

jinjia模板

Saltstack自动化运维部署_第42张图片


测试  先remove##server2上已经安装好的apache

在server1上推送
Saltstack自动化运维部署_第43张图片
Saltstack自动化运维部署_第44张图片
在server2上进行端口查看
Saltstack自动化运维部署_第45张图片

全局引用
在server1上   源文件端口号是8080不动它
Saltstack自动化运维部署_第46张图片
测试  直接推送出去
Saltstack自动化运维部署_第47张图片
Saltstack自动化运维部署_第48张图片
可以看到全局变量生效拉 在server2上查看端口号 变成了80
Saltstack自动化运维部署_第49张图片

变量的定义静态grain
在server1上



将之前的80改成8080

推送一波
Saltstack自动化运维部署_第50张图片
Saltstack自动化运维部署_第51张图片
端口编程拉8080 再到server2查看
Saltstack自动化运维部署_第52张图片

源码包推送keepalived
在server1上建立对应的文件夹存放安装包
Saltstack自动化运维部署_第53张图片
编写配置文件
Saltstack自动化运维部署_第54张图片
推送keepalived
Saltstack自动化运维部署_第55张图片
Saltstack自动化运维部署_第56张图片
成功后到server2上获取配置文件和脚本
Saltstack自动化运维部署_第57张图片
到server1上继续配置install.sls

Saltstack自动化运维部署_第58张图片
更改keepalived的配置文件

Saltstack自动化运维部署_第59张图片
静态参数设定

Saltstack自动化运维部署_第60张图片
启动文件的设定

Saltstack自动化运维部署_第61张图片
批量推送文件


批量推送
Saltstack自动化运维部署_第62张图片
Saltstack自动化运维部署_第63张图片
检测  失败的原因为master vip在server2上  keepalived都已经安装
Saltstack自动化运维部署_第64张图片
Saltstack自动化运维部署_第65张图片


salt syndic:salt proxy 安装与配置
sakt-master: master  salt-minion: server2 server3
在server1上   关掉salt-minion 关闭自启动 删除注册
Saltstack自动化运维部署_第66张图片
在master上安装salt-syndic


配置master指向topmaster


新开server4作为topmaster  注意server4的yum  同时路径存在






启动服务 允许server1连接
Saltstack自动化运维部署_第67张图片
测试一下   光标卡一会属于正常 稍等一下就好了
Saltstack自动化运维部署_第68张图片

salt-ssh串行
server1安装salt-ssh

Saltstack自动化运维部署_第69张图片
测试连接


api服务

Saltstack自动化运维部署_第70张图片
修改master的配置文件使其支持api


创建用户并修改密码

编写认证文件
Saltstack自动化运维部署_第71张图片
填写资料 生成秘钥
Saltstack自动化运维部署_第72张图片
启动服务 查看8000端口
Saltstack自动化运维部署_第73张图片
Saltstack自动化运维部署_第74张图片
测试minion端的连通性
Saltstack自动化运维部署_第75张图片
API连接测试,并获取token
Saltstack自动化运维部署_第76张图片
进行获取客户端列表
Saltstack自动化运维部署_第77张图片
配置saltapi.py用于生成相关服务的脚本

脚本需要修改的地方


Saltstack自动化运维部署_第78张图片
这里ip为server1的IP 密码是刚设置的redhat

完整的脚本

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import urllib2,urllib
import time

try:
    import json
except ImportError:
    import simplejson as json
SERVER=raw_input("请输入要安装软件所在的主机名:")
PATH=raw_input("请输入其调用脚本位置(/srv/salt)为相对位置:")
class SaltAPI(object):
    __token_id = ''
    def __init__(self,url,username,password):
        self.__url = url.rstrip('/')
        self.__user = username
        self.__password = password

    def token_id(self):
        ''' user login and get token id '''
        params = {'eauth': 'pam', 'username': self.__user, 'password': self.__password}
        encode = urllib.urlencode(params)
        obj = urllib.unquote(encode)
        content = self.postRequest(obj,prefix='/login')
	try:
            self.__token_id = content['return'][0]['token']
        except KeyError:
            raise KeyError

    def postRequest(self,obj,prefix='/'):
        url = self.__url + prefix
        headers = {'X-Auth-Token'   : self.__token_id}
        req = urllib2.Request(url, obj, headers)
        opener = urllib2.urlopen(req)
        content = json.loads(opener.read())
        return content

    def list_all_key(self):
        params = {'client': 'wheel', 'fun': 'key.list_all'}
        obj = urllib.urlencode(params)
        self.token_id()
        content = self.postRequest(obj)
        minions = content['return'][0]['data']['return']['minions']
        minions_pre = content['return'][0]['data']['return']['minions_pre']
        return minions,minions_pre

    def delete_key(self,node_name):
        params = {'client': 'wheel', 'fun': 'key.delete', 'match': node_name}
        obj = urllib.urlencode(params)
        self.token_id()
        content = self.postRequest(obj)
        ret = content['return'][0]['data']['success']
        return ret

    def accept_key(self,node_name):
        params = {'client': 'wheel', 'fun': 'key.accept', 'match': node_name}
        obj = urllib.urlencode(params)
        self.token_id()
        content = self.postRequest(obj)
        ret = content['return'][0]['data']['success']
        return ret

    def remote_noarg_execution(self,tgt,fun):
        ''' Execute commands without parameters '''
        params = {'client': 'local', 'tgt': tgt, 'fun': fun}
        obj = urllib.urlencode(params)
        self.token_id()
        content = self.postRequest(obj)
        ret = content['return'][0][tgt]
        return ret

    def remote_execution(self,tgt,fun,arg):
        ''' Command execution with parameters '''        
        params = {'client': 'local', 'tgt': tgt, 'fun': fun, 'arg': arg}
        obj = urllib.urlencode(params)
        self.token_id()
        content = self.postRequest(obj)
        ret = content['return'][0][tgt]
        return ret

    def target_remote_execution(self,tgt,fun,arg):
        ''' Use targeting for remote execution '''
        params = {'client': 'local', 'tgt': tgt, 'fun': fun, 'arg': arg, 'expr_form': 'nodegroup'}
        obj = urllib.urlencode(params)
        self.token_id()
        content = self.postRequest(obj)
        jid = content['return'][0]['jid']
        return jid

    def deploy(self,tgt,arg):
        ''' Module deployment '''
        params = {'client': 'local', 'tgt': tgt, 'fun': 'state.sls', 'arg': arg}
        obj = urllib.urlencode(params)
        self.token_id()
        content = self.postRequest(obj)
        return content

    def async_deploy(self,tgt,arg):
        ''' Asynchronously send a command to connected minions '''
        params = {'client': 'local_async', 'tgt': tgt, 'fun': 'state.sls', 'arg': arg}
        obj = urllib.urlencode(params)
        self.token_id()
        content = self.postRequest(obj)
        jid = content['return'][0]['jid']
        return jid

    def target_deploy(self,tgt,arg):
        ''' Based on the node group forms deployment '''
        params = {'client': 'local_async', 'tgt': tgt, 'fun': 'state.sls', 'arg': arg, 'expr_form': 'nodegroup'}
        obj = urllib.urlencode(params)
        self.token_id()
        content = self.postRequest(obj)
        jid = content['return'][0]['jid']
        return jid

def main():
    sapi = SaltAPI(url='https://172.25.62.1:8000',username='saltapi',password='redhat')
    sapi.token_id()
    print sapi.list_all_key()
    #sapi.delete_key('test-01')
    #sapi.accept_key('test-01')
    sapi.deploy(SERVER,PATH)
    #print sapi.remote_noarg_execution('test-01','grains.items')

if __name__ == '__main__':
    main()


在客户端删除相应的服务,并通过salt-api创建

Saltstack自动化运维部署_第79张图片
查看是否安装成功

你可能感兴趣的:(saltstack,自动化运维)