兼任运维后,爬虫这边的任务也要我处理了。发现之前的开发连日志都没有配置,纠错特别麻烦。查看Scrapy logging, 找到解决的办法,原来还是很简单的。

查看logging配置,发现只要配置LOG_FILE和LOG_FORMAT即可。

在settings中添加配置如下

1
2
LOG_FILE='logs/spider.log'
LOG_FORMAT= '%(levelname)s %(asctime)s [%(name)s:%(module)s:%(funcName)s:%(lineno)s] [%(exc_info)s] %(message)s'

其中LOG_FILE指定日志路径,而LOG_FORMAT指定日志格式。因为默认的LOG_FORMAT是没有输出日志的行数,所以这里增加了行数设置。

联系作者

某天,前端同事的Django项目无法启动,报you must set settings.allowed_hosts if debug is false错误,可是查看配置文件,明明设置debug=True, 不知为何。

找了好久,组长过来看了之后,发现是Cannot import name _uuid_generate_random错误,于是想到之前也遇到过这个问题,只是当时没有做笔记,所以印象不深。

按照cannot-import-name-uuid-generate-random-in-heroku-django, 升级Kombu到3.0.30,依然提示这个错误,
之后按照ImportError: cannot import name _uuid_generate_random的提示,升级celery, 执行pip install --upgrade celery,这次可以正常启动。

联系作者

最近测试服务器的数据库反复重启,产看日志, 提示

1
2
3
4
InnoDB: Log scan progressed past the checkpoint lsn 377750615222
InnoDB: The log sequence number in ibdata files does not match
InnoDB: the log sequence number in the ib_logfiles!
InnoDB: Database was not shut down normally!

之后无知的把ib_logfile1和ib_logfile2删掉,数据库依然反复重启。查看MySQL文件目录格式及存放位置才知道这两个文件还是有用的。

之后想到一个办法是重建数据库,也就是将SQL全部导出,之后再重新导入。在使用mysqldump导出数据时,老是提示mysqldump: Error 2013: Lost connection to MySQL server during query when dumping table, 原因还是数据库重启了。

参考官网[forcing-innodb-recovery]
(https://dev.mysql.com/doc/refman/5.5/en/forcing-innodb-recovery.html), 在/etc/my.cnf里配置innodb_force_recovery=3, 将数据库的SQL成功导出。

修改/etc/my.cnf里的datadir,在新的目录里重启MySQL, 之后使用mysql导入数据,数据库终于正常。

使用mysqldump导出test这个数据库的命令

1
mysqldump -h 127.0.0.1 -u root -p123456 test > test.sql

而使用mysql命令行工具导入

1
mysql -h 127.0.0.1 -u root -p123456 test < test.sql

联系作者

服务器上老是有人尝试破解root用户的SSH登录密码,要想办法解决。一个办法是屏蔽ip.

在/etc目录下存在hosts.allow和hosts.deny, 看名字就知道它们的意义。

  • hosts.allow指的是允许登录的ip
  • hosts.deny指的是不允许登录的ip
  • 两个文件配置后自动生效,不要重启什么服务,因为监控这两个文件的服务会自动加载两个文件里的内容
  • 添加了屏蔽ip后,在/var/log/secure里就可以看到效果
    如在/etc/hosts.allow里添加
    1
    sshd:101.71.255.*

则是允许101.71.255.*等ip地址SSH登录

在/etc/hosts.deny里添加

1
sshd:116.31.116.*

则是禁止116.31.116.*等ip地址SSH登录。

hosts.allow和hosts.deny里配置的地址如果出现重复,优先使用hosts.allow, 也就是允许该地址SSH登录。

在/etc/hosts.deny里还可以添加

1
sshd:all

此时,只有hosts.allow里配置的地址才允许SSH登录。在我看来, hosts.allow配置只有当hosts.deny配置了sshd:all才有意义。

其实解决暴力破解root用户密码的最好办法是禁止root用户SSH登录,而用普通用户登录,登录之后切换到root用户。

兼任运维以来,数据库出问题,文件系统出问题,也是运气差。目前来看,还有很多地方需要完善的,慢慢来吧。

联系作者

今天服务器上出现了无法写入viminfo文件 /root/.viminfo错误,网上有人说删除~/.viminf*.tmp即可,查看目录没有这些文件。

后来发现是文件都变成了只读,想到上午的时候ext3文件系统出了问题。在网上找到[http://www.ha97.com/5428.html], 执行df -lhT,知道是ext4文件系统
想执行fsck.ext4 -y /dev/vda1,但不允许。因为这是线上系统。

于是继续找,发现mount -o remount rw /, 当时提示mount: you must specify the filesystem type, 于是加上文件系统类型,执行mount -t ext4 -o remount rw /,之后提示
mount: cannot remount block device rw read-write, is write-protected
查看鸟哥的私房菜,使用鸟哥的命令
mount -o remount,rw,auto/, 没有反应。

于是求助UCloud的工作人员,他们建议重启服务器。等到晚上,重启服务器,系统就起不来了。于是只好找UCloud的工作人员。后来他们进入单用户模式,修复文件系统,重新mount之后,终于弄好了。

可是线上服务停机了一个多小时,因为是单点服务。看来得再加一台机器了。

参考资料

联系作者

当需要添加新的监控程序时,添加了配置后,需要重新加载,这样才能监控起来。此时supervisorctl update命令就派上用场了。

执行supervisorctl update命令后,添加的配置会加载到Supervisor里,这样就可以用Supervisor监控程序了。

发现program配置里有directory这个选项,目的是在启动程序时,让程序进入的目录。例如在使用gunicorn启动Django项目时,如果wsgi指定的路径使用/隔开,则会报importerror import by filename is not supported, 配置directory为wsgi所在的目录,问题就得到解决。

联系作者

在使用Django REST framework时,对接口加上权限限制是必不可少的,例如对于一篇文章,只有管理员和作者才有删除的权限,其它人只能有读取权限。此时Permissions就派上用场了。

REST framework提供了很多种权限,如IsAuthenticated,IsAdminUser等等,要定制permissions, 也是很容易的一件事。要定制permissions, 只需继承BasePermission,然后实现其中一个或者两个方法

  • has_permission(self, request, view)
  • has_object_permission(self, request, view, obj)

其中,has_permission是相对接口而言的,也就是在访问这个接口时,会进行权限检测。而has_object_permission是相对于对象而言的,只有访问对象时才会进行权限检测。

还有一个问题是,当实现自己的get_object并且需要进行权限检查时,不要忘记调用self.check_object_permissions(self.request, obj)

1
2
3
4
def get_object(self):
obj = get_object_or_404(self.get_queryset())
self.check_object_permissions(self.request, obj)
return obj

而对于访问对象进行权限检测时,一个好的方法是如果是安全方法如GET, HEAD等,则运行访问。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class IsOwnerOrReadOnly(permissions.BasePermission):
"""
Object-level permission to only allow owners of an object to edit it.
Assumes the model instance has an `owner` attribute.
"""


def has_object_permission(self, request, view, obj):
# Read permissions are allowed to any request,
# so we'll always allow GET, HEAD or OPTIONS requests.
if request.method in permissions.SAFE_METHODS:
return True

# Instance must have an attribute named `owner`.
return obj.owner == request.user

联系作者

kill命令主要用来杀进程,以前不懂,一只都用kill -9, 现在才发现打错特错。

最近在学习使用Supervisor管理进程,测试Supervisor被杀死之后的情况。发现用kill -9杀死Supervisor后,管理的进程会变成孤儿进程。于是请教the5fire, 他提供了一篇no use kill 9, 顿时解决了疑惑。

在Mac上 man kill看到如下说明

1
2
3
4
5
6
7
1       HUP (hang up)
2 INT (interrupt)
3 QUIT (quit)
6 ABRT (abort)
9 KILL (non-catchable, non-ignorable kill)
14 ALRM (alarm clock)
15 TERM (software termination signal)

kill -9的主要弊端是被杀的进程来不及善后处理就已经死了,这回留下很多问题。所以强烈建议不要使用kill -9来杀死进程, 而是使用kill -15

联系作者

用Supervisor管理进程里说过,当supervisor挂了之后,它管理的进程就给了init进程,之后supervisor再次启动,端口已经被绑定了,怎么破?目前还没有找到解决的办法。现在继续说说这个问题。

这里说当supervisor挂了之后,它管理的进程就给了init进程,这并不完全对。这里其实是我主动用kill命令把supervisor杀掉,而且是用kill -9, 此时supervisor来不及将被杀的信息告诉管理的进程就死了,于是管理的进程变成了孤儿进程。当使用kill -15杀死supervisor时,它管理的进程也会一起挂掉,这样进程所占的资源也来得及释放。supervisor下次就可以正常启动。

一篇错误的博客

linux 后台进程管理利器supervisor里看到

不带参数运行supervisord是以daemon方式运行
当supervisord以非daemon方式运行时,杀掉supervisord后,被监控的进程也退出了。
而以daemon方式运行,杀掉supervisord对被监控进程无影响

然后我在博客里找什么是带参数和不带参数

supervisord (以daemon方式启动)
或 supervisord -c /etc/supervisord.conf (非daemon)

后来看了supervisor的官方文档,知道supervisor是否以daemon方式启动,是在supervisord.conf的supervisord项里配置的。当配置了nodaemon=true时,就会以非daemon方式启动,而不是根据带参数和不带参数决定的。

而被监控的进程是否一起死掉,也是更加supervisor被杀的方式决定的。从这方面看,这篇博客真是错误连篇。

一些需要注意的地方

  • autorestart=true
    在program配置项里,最好加上这个配置项,让监控的程序在关闭后自动重启。我遇到过一个问题是,用kill -15把监控的进程杀掉,之后程序没有自动重启。原因是它的退出码是0, 而exitcodes的默认配置是0,2 此时程序不会自动重启。因为exitcodes里配置的是The list of “expected” exit codes for this program used with autorestart, 而autorestart默认配置是unexpected。

联系作者

一直卡在gopkg.in/inconshreveable/go-update.v0,
想在CentOS6.5上安装Ngrok, 按照搭建 ngrok 服务实现内网穿透上的步骤安装。遇到问题,记录一下。

  • no package golang available.

参考CentOS-6.x下搭建golang环境的三种方式

  • no package build-essential available

参考CentOS Install Build Essentials

  • 卡在gopkg.in/inconshreveable/go-update.v0 (download)

参考ngrok服务安装笔记, 知道是git版本太旧。更新之后就可以编译成功

在CentOS6.5上如何更新git, 可参考CentOS6.5升级git

联系作者