目录
- 项目介绍和源码;
- 拿来即用的bootstrap模板;
- 服务器SSH服务配置与python中paramiko的使用;
- 用户登陆与session;
- 最简单的实践之修改服务器时间;
- 查看和修改服务器配置与数据库的路由;
- 基于websocket的实时日志实现;
- 查看服务器中的日志与前端的datatable的利用;
- 重启服务器进程。
前言
我们的Web小项目需要用到python中的paramiko这个模块去和服务器简单交互,python中的paramiko模块已经帮我们实现了基于SSH的远程服务器连接方法,本篇文章会介绍一下如何在服务器上配置一个ssh服务器,并结合网站的需求介绍下paramiko的功能,给出一些需要使用到的函数。
配置SSH服务器
本地安装paramiko只需要执行pip install paramiko
即可,然后把网站需要用到的服务器功能封装成函数方便调用。除此之外我们还需要在Linux服务器上配置好SSH服务,通过SSH客户端登陆服务器有三种方式,分别是:用户名+密码、用户名+私钥,后者也可以叫做免密登陆。我们先在Linux上安装一个SSH服务:
- 执行
ps -e | grep ssh*
查看服务器是否开启了ssh服务,若没有则在服务器上利用sudo apt-get install openssh-server openssh-client
安装ssh服务。 - 安装好后开启ssh服务:执行
/etc/init.d/ssh start
开启服务,如图1,利用ifconfig
获取到服务器的ip地址。
- 打开Xsehll工具,填好服务器上面查询到的ip地址和端口号(默认为22),输入密码之后,就可以登陆到我们的服务器了,下图图2和图3。
- 若需要免密登陆,就需要配置好公钥和私钥,为了保证安全,在自己的用户名下创建密钥对就好:
lishouxian@lishouxian:~$ ssh-keygen <== 建立密钥对
Generating public/private rsa key pair.
Enter file in which to save the key (/home/lishouxian/.ssh/id_rsa): <== 密钥对生成的地方
Enter passphrase (empty for no passphrase): <== 密钥密码,可留空
Enter same passphrase again:
Your identification has been saved in /home/lishouxian/.ssh/id_rsa. <== 私钥
Your public key has been saved in /home/lishouxian/.ssh/id_rsa.pub. <== 公钥
The key fingerprint is:
SHA256:1Qj14JZLLGhekIjSY7rsCBoDAQ4QeoMvVKN4jxqzzrQ lishouxian@lishouxian
The key's randomart image is:
+---[RSA 2048]----+
|Bo + .....o |
|B.B o .o + * |
|+O+. o o O o |
|++ + o . = . |
|*.o . . S . |
|=B |
|B+ |
|*.. |
| E |
+----[SHA256]-----+
- 用自己的用户账号切换到自己用户名的.ssh目录下安装公钥
cat id_rsa.pub >> authorized_keys
,并执行chmod 600 authorized_keys
获得权限。 - 打开配置文件
vim /etc/ssh/sshd_config
,设置RSAAuthentication
和PubkeyAuthentication
两个字段为yes
。 - 把私钥id_rsa拷贝到本地,在服务器上执行
/etc/init.d/ssh restart
重启SSH服务,这个时候在本地就可以打开Xshell用用户名和密钥登陆服务器了,因为我们没有设置密钥密码,所以登陆时可以不用输入图4红框中的密码。
python中的paramiko使用
服务器的登陆
服务器登录可通过用户名+密码和用户名+密钥免密登陆,下面代码会给出这两种登陆方式对应的代码:
# -*- coding: utf-8 -*-
import paramiko
import os
import time
import re
import sysreload(sys)
sys.setdefaultencoding('utf-8')login_config = {"hostip": "", # 服务器的ip(需要填写)"hostport": 22, # 端口号"username": "lishouxian", # 登陆的用户名"userpwd": "", # 登陆密码(需要填写)"rootusr": "root", # root 用户"rootpwd": "root", # root 密码# 私钥,将配置好的id_rsa文件放在项目目录下"keypath": os.path.join(os.path.dirname(os.path.abspath(__file__)), "id_rsa"),
}# 通过密钥登陆服务器
def login_server_by_rsa():try:server_ssh = paramiko.SSHClient()server_ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())pkey = paramiko.RSAKey.from_private_key_file(login_config["keypath"])server_ssh.connect(hostname=login_config["hostip"],port=login_config["hostport"],username=login_config["username"],pkey=pkey)return server_sshexcept Exception, e:print e# 通过密码登陆服务器
def login_server_by_pwd():try:server_ssh = paramiko.SSHClient()server_ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())server_ssh.connect(hostname=login_config["hostip"],port=login_config["hostport"],username=login_config["username"],password=login_config["userpwd"])return server_sshexcept Exception, e:print e
登陆服务器之后可以操作查看时间、查看文件之类的操作。
获得root权限
因为我们以用户名登陆,修改其他用户名下的文件、修改时间、上传或者删除文件这些需要root权限,获得root权限的代码如下,这种交互方式像在一问一答,代码运行结果如图5,代码送指令并获得反馈,每当执行下一步操作都需要一个状态进行判断while not buff.endswith('Password: ')
语句用来判断是否到了服务器问代码要密码的阶段。
# 获得root权限
def authenticating_channel(login_ssh):channel = login_ssh.invoke_shell()try:print '............Authenticating............'channel.send("su %s\n" % login_config["rootusr"])buff = ''while not buff.endswith('Password: '):resp = channel.recv(10000)buff += respprint buffchannel.send("%s\n" % login_config["rootpwd"])buff = ''while not buff.endswith('# '):resp = channel.recv(10000)buff += respprint buffexcept Exception, e:print echannel.close()login_ssh.close()return channel
网站中用到的一些操作
这些操作基本上都是利用代码向服务器发送Linux命令,然后获得结果。
- 获得服务器时间,用到的Linux命令:
date +%Y-%m-%d\ %H:%M:%S
。
# 获取服务器时间
def get_server_time():ssh = login_server_by_pwd()stdin, stdout, stderr = ssh.exec_command('date +%Y-%m-%d\ %H:%M:%S')servertime = stdout.read()ssh.close()return servertimeprint get_server_time()
- 修改服务器时间,需要权限,用到的Linux命令:
date +%Y-%m-%d\ %H:%M:%S
,结果如图6所示。
# 重置服务器时间
def restore_server_time():local_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())modify_server_time(local_time)# 修改服务器时间
def modify_server_time(newtime):ssh = login_server_by_pwd()channel = authenticating_channel(ssh)print '............Sending ModifyTime Commander............'channel.send("sudo date -s \"%s\" \n" % newtime)buff = ''while not buff.endswith('# '):resp = channel.recv(10000)buff += respprint buff.decode('utf-8')ssh.close()modify_server_time('2018-08-08 08:08:08')
# restore_server_time()
- 获得配置文件,一般情况下服务器的配置文件会写在某个文件夹里面,例子中的配置文件皆为json格式的文件,不同类型的配置文件会用不同的子文件夹装着,考虑到这种情况,可以把 Linux命令写复杂点,但是这样不利于程序的扩展。
ls -lR
遍历文件夹下的所有的文件(包含子文件夹),grep ^-
只显示文件名,不显示文件夹的名字,awk \'{ print $9 }\'
用来获取这些配置文件的名字(\'为转义字符),图7是服务器的所有配置文件,图8是程序返回的配置名列表。
# 获取文件配置
def get_serverconfig_lists():ssh = login_server_by_pwd()# execute commandstdin, stdout, stderr = ssh.exec_command('ls -lR /home/configs/ | grep ^- | awk \'{ print $9 }\'')file_list = stdout.read().split("\n")[:-1]return file_listprint get_serverconfig_lists()
- 查看配置文件,
find \"$PWD\" -name XXX
在本目录和子目录下查找XXX的文件(\"为转义字符),| xargs cat
将上一个命令的结果作为下一个指令的参数,这里的意思是将找到的文件名作为cat
的参数,结果就是打开这个文件,结果如图9。
#读取配置配置
def read_serverconfig(filename):ssh = login_server_by_rsa()# execute commandtry:stdin, stdout, stderr = ssh.exec_command('cd /home/configs/; find \"$PWD\" -name ' + filename + ' | xargs cat')read = stdout.read().decode('utf-8')return readexcept Exception, e:print eprint read_serverconfig('setting_1.json')
- 修改配置文件,我们采用了一个最简单的修改文件方法,直接覆盖原来的文件,所以在本地产生一个文件,输入文件内容(是否符合json文件格式在后面利用javascript在前端页面做了)然后利用paramiko中的sftp模块上传。由于修改文件需要权限,所以有个折衷的办法是:先把文件上传到自己的服务器文件夹下,然后在服务器获得root权限,把自己服务器文件夹下的文件覆盖掉配置文件夹中的同名文件。
# 修改配置文件
def generate_config_upload_file(filename, json_str):local_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), filename).replace('\\', '/')# 登陆服务器ssh = login_server_by_rsa()# 获得配置文件所在的位置stdin, stdout, stderr = ssh.exec_command('cd /home/configs/; find \"$PWD\" -name ' + filename)to_path = stdout.read()# 本地产生一个json文件的配置with open(local_path, 'w') as config_file:config_file.write(json_str)# 上传配置文件my_server_path = "/home/lishouxian/" + filenametry:transport = ssh.get_transport()sftp = paramiko.SFTPClient.from_transport(transport)sftp.put(local_path, my_server_path)os.remove(local_path)except Exception, e:print essh.close()os.remove(local_path)return 'Unsuccessful Upload'sftp.close()# 将文件从自己的项目文件夹转移到配置文件夹下channel = authenticating_channel(ssh)print '............Sending Upload Commander............'channel.send("cp /home/lishouxian/" + filename + " " + to_path + "\n")buff = ''while not buff.endswith('# '):resp = channel.recv(10000)buff += respprint buffssh.close()return 'Successful Upload'json_config = '{"name": "data_3", "id": "2"}'
print generate_config_upload_file('data_3.json', json_config)
- 删除配置文件,Linux命令解释如上面的读取配置。
# 删除配置文件
def delete_config(filename):ssh = login_server_by_rsa()# delete configchannel = authenticating_channel(ssh)try:print '............Sending Delete Commander............'channel.send('cd /home/configs/; find \"$PWD\" -name ' + filename + ' | xargs rm \n')buff = ''while not buff.endswith('# '):resp = channel.recv(10000)buff += respprint buffexcept e:print essh.close()ssh.close()return 'Successful Delete'print delete_config('config_4.json')
- 调用shell脚本,为了演示,重启进程这个服务器的操作我们用自己的写的shell脚本替代,shell脚本会延时一分钟然后输出结果,最后演变成利用paramiko执行用户自定义的Linux脚本过程,我们在服务器的/home/目录下创建一个shell脚本:
#!/bin/sh
echo 'Rebooting...'
sleep 10
echo 'Suceessful rebooted!'
在代码中调用shell代码如下:
# 重启某个进程,调用shell脚本
def rebootserver():ssh = login_server_by_rsa()channel = authenticating_channel(ssh)# reboot game serverprint '............Sending Reboot Commander............'channel.send("bash /home/reboot.sh restart; echo \"quit:$?\" \n")resp = ''while not resp.endswith('# '):resp = channel.recv(10000)print respquit_num = re.findall("quit:(\d+)", resp, re.M)if len(quit_num) > 0:if quit_num[0] == '0':ssh.close()return "Successful Rebooted!"else:ssh.close()return "Unsuccessful Rebooted!"ssh.close()print rebootserver()
结语
本篇文章介绍了如何在服务器上搭建一个支持客户端免密和使用密码登陆的ssh服务,然后利用python中的paramiko模块登陆服务器,后面的网站都是基于paramiko和服务器进行交互。