我们的服务器由 Puppet 配置管理工具来管理,服务器上线后由 puppet 完成初始化和配置等一系列工作(比如,静态 IP 配置,DNS 设置,NFS/SAN 挂载,LDAP/Kerberos 登录,安全加固配置,内核参数优化,防火墙规则配置等等),等初始化完成后开始运行,运行一段时间后有一些需要自动和手动操作的任务(比如升级、重启、备份等),这时候我们使用 Fabric 来批量执行这些临时任务。
所以从这里可以看到 Puppet 和 Fabric 其实是两个不同性质的工具,看下面的归类可能会更清楚一些。Puppet 和 Fabric 两个的工作其实可以由一个工具 SaltStack(或 AnsibleWorks)完成,减少一个工具的使用会减轻一点负担(学习工具的人力成本、安装和配置工具的时间成本等等)。
操作系统和软件的安装、配置、初始化等;
(Puppet, Chef, CFEngine, AnsibleWorks, SaltStack, …)
自动执行任务,比如定期备份、清除日志等;
(Fabric, AnsibleWorks, SaltStack, …)
手动执行任务,比如部署应用、升级、重启、检查和校验文件系统、增加用户等。
(Fabric, Rake, Func, Rundeck, AnsibleWorks, SaltStack, …)
SaltStack 采用 zeromq 消息队列进行通信,和 Puppet/Chef 比起来,SaltStack 速度快得多。还有一点我们喜欢 SaltStack 的地方是它是 Python 写的,比 Puppet/Chef 这些 Ruby 工具更接近我们的能力圈。
安装主控服务器(salt master)
和大多数类似工具一样,SaltStack 需要在一台机器(主控)上安装服务器端软件(SaltStack 称之为 salt master),在多台机器(受控)上安装客户端软件(SaltStack 称之为 salt minion)。在主控机器上给下属(受控)发命令,在受控机器上接受和执行上级(主控)的命令。
在 Ubuntu 上安装 salt master:
在 CentOS 6.x 上安装 salt master:
安装受控客户端(salt minion)
在 Ubuntu 上安装 salt minion:
在 CentOS 6.x 上安装 salt minion:
安装完 salt minion 后记得修改配置文件,让 salt minion 指向 salt master 服务器地址:
在 master 上执行 salt-key list 就会看到有个 minion1.vpsee.com 请求加入受控,执行 -a 接受请求后,主控和受控之间的信任关系就建立起来了,主控就可以任意 “摆布” 受控了:
执行命令的例子
在主控机器上执行一个命令,让所有受控机器执行 hostname 命令:
在主控机器上执行一个命令,让所有受控机器上执行内建 test.ping 命令:
还有一些内建命令可以尝试:
执行状态的例子
开头的时候我们说了 SaltStack = Fabric + Puppet,上面 “执行命令的例子” 演示了 Fabric 类似的功能,这里要演示的是 Puppet 类似的功能,在主控上定义好系统配置应该有的状态,然后受控自动完成相应的操作和配置。
首先确定状态定义的文件应该放在什么地方,以下操作都在主控(salt master)上执行。检查 /etc/salt/master 文件的 file_roots 条目,默认是在 /srv/salt 下,如果没有这个目录还需要手动创建一个:
比如我们想在所有受控机器上安装 vim 软件包,并使用自己定义的 vimrc 文件:
强制执行这个状态:
再来一个例子,参考 “安装和使用系统监控工具 Glances” 一文,我们想在所有受控机器上安装 Glances,如何实现呢?
强制执行这个状态:
所以从这里可以看到 Puppet 和 Fabric 其实是两个不同性质的工具,看下面的归类可能会更清楚一些。Puppet 和 Fabric 两个的工作其实可以由一个工具 SaltStack(或 AnsibleWorks)完成,减少一个工具的使用会减轻一点负担(学习工具的人力成本、安装和配置工具的时间成本等等)。
操作系统和软件的安装、配置、初始化等;
(Puppet, Chef, CFEngine, AnsibleWorks, SaltStack, …)
自动执行任务,比如定期备份、清除日志等;
(Fabric, AnsibleWorks, SaltStack, …)
手动执行任务,比如部署应用、升级、重启、检查和校验文件系统、增加用户等。
(Fabric, Rake, Func, Rundeck, AnsibleWorks, SaltStack, …)
SaltStack 采用 zeromq 消息队列进行通信,和 Puppet/Chef 比起来,SaltStack 速度快得多。还有一点我们喜欢 SaltStack 的地方是它是 Python 写的,比 Puppet/Chef 这些 Ruby 工具更接近我们的能力圈。
安装主控服务器(salt master)
和大多数类似工具一样,SaltStack 需要在一台机器(主控)上安装服务器端软件(SaltStack 称之为 salt master),在多台机器(受控)上安装客户端软件(SaltStack 称之为 salt minion)。在主控机器上给下属(受控)发命令,在受控机器上接受和执行上级(主控)的命令。
在 Ubuntu 上安装 salt master:
$ sudo add-apt-repository ppa:saltstack/salt
$ sudo apt-get update
$ sudo apt-get install salt-master
$ sudo apt-get update
$ sudo apt-get install salt-master
在 CentOS 6.x 上安装 salt master:
# rpm -Uvh http://ftp.linux.ncsu.edu/pub/epel/6/i386/epel-release-6-8.noarch.rpm
# yum update
# yum install salt-master
# yum update
# yum install salt-master
安装受控客户端(salt minion)
在 Ubuntu 上安装 salt minion:
$ sudo add-apt-repository ppa:saltstack/salt
$ sudo apt-get update
$ sudo apt-get install salt-minion
$ sudo apt-get update
$ sudo apt-get install salt-minion
在 CentOS 6.x 上安装 salt minion:
# rpm -Uvh http://ftp.linux.ncsu.edu/pub/epel/6/i386/epel-release-6-8.noarch.rpm
# yum update
# yum install salt-minion
# yum update
# yum install salt-minion
安装完 salt minion 后记得修改配置文件,让 salt minion 指向 salt master 服务器地址:
$ sudo vi /etc/salt/minion
...
# Set the location of the salt master server, if the master server cannot be
# resolved, then the minion will fail to start.
master: saltmaster.vpsee.com
...
$ sudo restart salt-minion
...
# Set the location of the salt master server, if the master server cannot be
# resolved, then the minion will fail to start.
master: saltmaster.vpsee.com
...
$ sudo restart salt-minion
在 master 上执行 salt-key list 就会看到有个 minion1.vpsee.com 请求加入受控,执行 -a 接受请求后,主控和受控之间的信任关系就建立起来了,主控就可以任意 “摆布” 受控了:
# salt-key list
Accepted Keys:
Unaccepted Keys:
minion1.vpsee.com
Rejected Keys:
# salt-key -a minion1.vpsee.com
The following keys are going to be accepted:
Unaccepted Keys:
minion1.vpsee.com
Proceed? [n/Y]
Accepted Keys:
Unaccepted Keys:
minion1.vpsee.com
Rejected Keys:
# salt-key -a minion1.vpsee.com
The following keys are going to be accepted:
Unaccepted Keys:
minion1.vpsee.com
Proceed? [n/Y]
执行命令的例子
在主控机器上执行一个命令,让所有受控机器执行 hostname 命令:
# salt '*' cmd.run "hostname"
minion1.vpsee.com:
minion1.vpsee.com
minion1.vpsee.com:
minion1.vpsee.com
在主控机器上执行一个命令,让所有受控机器上执行内建 test.ping 命令:
# salt '*' test.ping
minion1.vpsee.com:
True
minion1.vpsee.com:
True
还有一些内建命令可以尝试:
# salt '*' disk.usage
# salt '*' network.interfaces
# salt '*' network.interfaces
执行状态的例子
开头的时候我们说了 SaltStack = Fabric + Puppet,上面 “执行命令的例子” 演示了 Fabric 类似的功能,这里要演示的是 Puppet 类似的功能,在主控上定义好系统配置应该有的状态,然后受控自动完成相应的操作和配置。
首先确定状态定义的文件应该放在什么地方,以下操作都在主控(salt master)上执行。检查 /etc/salt/master 文件的 file_roots 条目,默认是在 /srv/salt 下,如果没有这个目录还需要手动创建一个:
# vi /etc/salt/master
...
#file_roots:
# base:
# - /srv/salt
...
# mkdir /srv/salt
...
#file_roots:
# base:
# - /srv/salt
...
# mkdir /srv/salt
比如我们想在所有受控机器上安装 vim 软件包,并使用自己定义的 vimrc 文件:
# vi /srv/salt/vim.sls
vim:
pkg.installed
/etc/vimrc:
file.managed:
- source: salt://vimrc
- mode: 644
- user: root
- group: root
# vi /srv/salt/vimrc
syntax enable
set textwidth=79
set shiftwidth=4
set tabstop=4
set expandtab
set softtabstop=4
set shiftround
set fileencodings=utf-8
set encoding=utf8
set tenc=utf8
vim:
pkg.installed
/etc/vimrc:
file.managed:
- source: salt://vimrc
- mode: 644
- user: root
- group: root
# vi /srv/salt/vimrc
syntax enable
set textwidth=79
set shiftwidth=4
set tabstop=4
set expandtab
set softtabstop=4
set shiftround
set fileencodings=utf-8
set encoding=utf8
set tenc=utf8
强制执行这个状态:
# salt '*' state.sls vim
再来一个例子,参考 “安装和使用系统监控工具 Glances” 一文,我们想在所有受控机器上安装 Glances,如何实现呢?
# vi /srv/salt/glances.sls
python-pip:
pkg.installed
build-essential:
pkg.installed
python-dev:
pkg.installed
glances:
pip.installed:
- require:
- pkg: python-pip
python-pip:
pkg.installed
build-essential:
pkg.installed
python-dev:
pkg.installed
glances:
pip.installed:
- require:
- pkg: python-pip
强制执行这个状态:
# salt '*' state.sls glances
...
minion1.vpsee.com:
----------
State: - pip
Name: glances
Function: installed
Result: True
Comment: Package was successfully installed
Changes: Glances==1.7.1: Installed
...
...
minion1.vpsee.com:
----------
State: - pip
Name: glances
Function: installed
Result: True
Comment: Package was successfully installed
Changes: Glances==1.7.1: Installed
...
#!/usr/bin/python2.6
import os
import time
import paramiko
import ConfigParser
import threading
import Queue
import optparse
class thread_ssh(threading.Thread):
def __init__(self,work_queue):
super(thread_ssh,self).__init__()
self.work_queue = work_queue
def run(self):
while True:
try:
machine = self.work_queue.get()
self.process(machine)
finally:
self.work_queue.task_done()
def process(self,machine):
hostname=str(machine[1]).strip()
port=int(machine[2])
username=str(machine[3]).strip()
password=str(machine[4]).strip()
cmd=';'.join( machine[5:])
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.load_system_host_keys()
#ssh.load_host_keys('/root/.ssh/known_hosts')
ssh.connect(hostname,port,username,password)
(stdin,stdout,stderr)=ssh.exec_command(cmd)
output=stdout.read()
pid=os.getpid()
print "The %s run in %s" %(self.name,hostname)
print output
info.write('n=======================n====>>'+hostname+'n=======================nn')
info.write(output)
info.flush()
ssh.close()
def read_cfg_file(file):
machines = []
config = ConfigParser.ConfigParser()
config.read(file)
host_items = config.items("HOST")
cmd_items = config.items("CMD")
for item in host_items:
host=item[1].split()
for cmd_item in cmd_items:
host.append(cmd_item[1])
machines.append(host)
return machines
def parse_options():
description = "A ssh client with thread."
usage="usage: %prog [options]n Please use --helpnnAuthor: mrmuxl@sina.com"
version = "Version: %prog 0.1 nAuthor: mrmuxl@sina.com"
parser = optparse.OptionParser(description = (description),usage = (usage),version=(version))
parser.add_option("-c","--config",action="store",type="string",dest="config",default="pssh.cfg",help="program's config file")
parser.add_option("-o","--outpufile",action="store",type="string",dest="outputfile",default="outputfile.log",help="program's output file")
parser.add_option("-l","--logfile",action="store",type="string",dest="logfile",default="ssh.log",help="ssh client running log")
opts,args=parser.parse_args()
return opts,args
if __name__ == '__main__':
start_cpu = time.clock()
start_real = time.time()
opts,args = parse_options()
paramiko.util.log_to_file(opts.logfile)
info=open(opts.outputfile,'a')
hosts=read_cfg_file(opts.config)
work_queue = Queue.Queue()
for i in range(9):
pssh =thread_ssh(work_queue)
pssh.daemon=True
pssh.start()
for machine in hosts:
if machine[0] == 'On' or machine[0] == 'on' or machine[0] == 'ON':
work_queue.put(machine)
work_queue.join()
info.close()
end_cpu = time.clock()
end_real = time.time()
print("%f Real Seconds" % (end_real - start_real))
print("%f CPU Seconds" % (end_cpu - start_cpu))
import os
import time
import paramiko
import ConfigParser
import threading
import Queue
import optparse
class thread_ssh(threading.Thread):
def __init__(self,work_queue):
super(thread_ssh,self).__init__()
self.work_queue = work_queue
def run(self):
while True:
try:
machine = self.work_queue.get()
self.process(machine)
finally:
self.work_queue.task_done()
def process(self,machine):
hostname=str(machine[1]).strip()
port=int(machine[2])
username=str(machine[3]).strip()
password=str(machine[4]).strip()
cmd=';'.join( machine[5:])
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.load_system_host_keys()
#ssh.load_host_keys('/root/.ssh/known_hosts')
ssh.connect(hostname,port,username,password)
(stdin,stdout,stderr)=ssh.exec_command(cmd)
output=stdout.read()
pid=os.getpid()
print "The %s run in %s" %(self.name,hostname)
print output
info.write('n=======================n====>>'+hostname+'n=======================nn')
info.write(output)
info.flush()
ssh.close()
def read_cfg_file(file):
machines = []
config = ConfigParser.ConfigParser()
config.read(file)
host_items = config.items("HOST")
cmd_items = config.items("CMD")
for item in host_items:
host=item[1].split()
for cmd_item in cmd_items:
host.append(cmd_item[1])
machines.append(host)
return machines
def parse_options():
description = "A ssh client with thread."
usage="usage: %prog [options]n Please use --helpnnAuthor: mrmuxl@sina.com"
version = "Version: %prog 0.1 nAuthor: mrmuxl@sina.com"
parser = optparse.OptionParser(description = (description),usage = (usage),version=(version))
parser.add_option("-c","--config",action="store",type="string",dest="config",default="pssh.cfg",help="program's config file")
parser.add_option("-o","--outpufile",action="store",type="string",dest="outputfile",default="outputfile.log",help="program's output file")
parser.add_option("-l","--logfile",action="store",type="string",dest="logfile",default="ssh.log",help="ssh client running log")
opts,args=parser.parse_args()
return opts,args
if __name__ == '__main__':
start_cpu = time.clock()
start_real = time.time()
opts,args = parse_options()
paramiko.util.log_to_file(opts.logfile)
info=open(opts.outputfile,'a')
hosts=read_cfg_file(opts.config)
work_queue = Queue.Queue()
for i in range(9):
pssh =thread_ssh(work_queue)
pssh.daemon=True
pssh.start()
for machine in hosts:
if machine[0] == 'On' or machine[0] == 'on' or machine[0] == 'ON':
work_queue.put(machine)
work_queue.join()
info.close()
end_cpu = time.clock()
end_real = time.time()
print("%f Real Seconds" % (end_real - start_real))
print("%f CPU Seconds" % (end_cpu - start_cpu))
Linux操作系统的一大优点是对于同样一件事情,你可以使用高达数百种方法来实现它。例如,你可以通过数十种方法来生成随机密码。本文将介绍生成随机密码的十种方法。
生成一个随机密码
对于下面的任何一种方法,你可以通过简单的修改来生成特定长度的密码,或者只使用其输出结果的前N位。希望你正在使用一些类似于LastPass的密码管理器,这样你就不用自己记住这些随机生成的密码了。
1. 这种方法使用SHA算法来加密日期,并输出结果的前32个字符:
2. 这种方法使用内嵌的/dev/urandom,并过滤掉那些日常不怎么使用的字符。这里也只输出结果的前32个字符:
3. 这种方法使用openssl的随机函数。如果你的系统也许没有安装openssl,你可以尝试其它九种方法或自己安装openssl。
4. 这种方法类似于之前的urandom,但它是反向工作的。Bash的功能是非常强大的!
5. 这种方法使用string命令,它从一个文件中输出可打印的字符串:
6. 这是使用urandom的一个更简单的版本:
7. 这种方法使用非常有用的dd命令:
8. 你甚至可以生成一个只用左手便可以输入的密码:
9. 如果每次都使用上述某种方法,那更好的办法是将它保存为函数。如果这样做了,那么在首次运行命令之后,你便可以在任何时间只使用randpw就可以生成随机密码。或许你可以把它保存到你的~/.bashrc文件里面。
10. 最后这种生成随机密码的方法是最简单的。它同样也可以在安装了Cygwin的Windows下面运行。在Mac OS X下或许也可以运行。我敢肯定会有人抱怨这种方法生成的密码没有其它方法来的随机。但实际上如果你使用它生成的全部字符串作为密码,那这个密码就足够随机了。
是的,这种方法也极其好记。
生成一个随机密码
对于下面的任何一种方法,你可以通过简单的修改来生成特定长度的密码,或者只使用其输出结果的前N位。希望你正在使用一些类似于LastPass的密码管理器,这样你就不用自己记住这些随机生成的密码了。
1. 这种方法使用SHA算法来加密日期,并输出结果的前32个字符:
date +%s | sha256sum | base64 | head -c 32 ; echo
2. 这种方法使用内嵌的/dev/urandom,并过滤掉那些日常不怎么使用的字符。这里也只输出结果的前32个字符:
< /dev/urandom tr -dc _A-Z-a-z-0-9 | head -c${1:-32};echo;
3. 这种方法使用openssl的随机函数。如果你的系统也许没有安装openssl,你可以尝试其它九种方法或自己安装openssl。
openssl rand -base64 32
4. 这种方法类似于之前的urandom,但它是反向工作的。Bash的功能是非常强大的!
tr -cd '[:alnum:]' < /dev/urandom | fold -w30 | head -n1
5. 这种方法使用string命令,它从一个文件中输出可打印的字符串:
strings /dev/urandom | grep -o '[[:alnum:]]' | head -n 30 | tr -d '\n'; echo
6. 这是使用urandom的一个更简单的版本:
< /dev/urandom tr -dc _A-Z-a-z-0-9 | head -c6
7. 这种方法使用非常有用的dd命令:
dd if=/dev/urandom bs=1 count=32 2>/dev/null | base64 -w 0 | rev | cut -b 2- | rev
8. 你甚至可以生成一个只用左手便可以输入的密码:
</dev/urandom tr -dc '12345!@#$%qwertQWERTasdfgASDFGzxcvbZXCVB' | head -c8; echo ""
9. 如果每次都使用上述某种方法,那更好的办法是将它保存为函数。如果这样做了,那么在首次运行命令之后,你便可以在任何时间只使用randpw就可以生成随机密码。或许你可以把它保存到你的~/.bashrc文件里面。
randpw(){ < /dev/urandom tr -dc _A-Z-a-z-0-9 | head -c${1:-16};echo;}
10. 最后这种生成随机密码的方法是最简单的。它同样也可以在安装了Cygwin的Windows下面运行。在Mac OS X下或许也可以运行。我敢肯定会有人抱怨这种方法生成的密码没有其它方法来的随机。但实际上如果你使用它生成的全部字符串作为密码,那这个密码就足够随机了。
date | md5sum
是的,这种方法也极其好记。






