IPv6化改造部分应用

Rhilip 2017-10-27 PM 3881℃ 0条

因为前段时间的重大会议,本人主服务器的IPv4被屏蔽了。导致某个国内主机长时间无法与后端主服务器(国外)数据库连接。(真是个杯具的故事)
但是,虽然IPv4数据包在回国路由上被丢包,但是IPv6的数据包并没有被阻断。于是对该国内主机上的部分应用进行IPv6改造使其正常连接后端数据库。

一、后端数据库改造

原先后端使用的数据库MySQL版本为5.5,在未指定bind-address的情况下,默认监听的是0.0.0.0)。在思考后,选择将数据库直接升级到5.7,在5.6.6之后版本中,这个值默认设置成 *,即同时监听IPv4和IPv6连接。
(参见 https://dev.mysql.com/doc/refman/5.6/en/server-options.html#option_mysqld_bind-address )

| --bind-address=addr | Type | Default |

Permitted Values (<= 5.6.5)string0.0.0.0
Permitted Values (>= 5.6.6)string*

具体的升级过程就不在此唠述,因为,本人偷懒使用了lnmp.org自带的升级工具。

二、SSpanel后端改造

SSpanel后端使用cymysql库与主服务器通讯。但是这个库默认不支持以IPv6的形式与数据库进行连接。如果以IPv6的形式连接数据库会报出如下错误

>>> import cymysql
>>> conn = cymysql.connect(host='::1', user='root', passwd='', db='database_name', charset='utf8')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python2.7/dist-packages/cymysql/__init__.py", line 85, in Connect
    return Connection(*args, **kwargs)
  File "/usr/local/lib/python2.7/dist-packages/cymysql/connections.py", line 245, in __init__
    self._connect()
  File "/usr/local/lib/python2.7/dist-packages/cymysql/connections.py", line 410, in _connect
    raise OperationalError(2003, "Can't connect to MySQL server on %r (%s)" % (self.host, e.args[0]))
cymysql.err.OperationalError: (2003, "Can't connect to MySQL server on '::1' (-9)")

但是好在 CyMySQL 其实是 PyMySQL 的一个分支,而PyMySQL在这个issue Support IPv6 #118 中已经修复了这个问题。(所以为什么不直接用pymysql嘛,所以为什么cymysql到现在还有没修这个问题嘛。。。。

补:在提交issues To support IPv6 后,CyMySQL v0.9.2已经可以使用IPv6的形式连接客户端。(真速度.....)


那么就自己动手吧。根据PyMySQL中相关issues和commit,修改上述报错中最后提示的/usr/local/lib/python2.7/dist-packages/cymysql/connections.py文件。
做如下patch

diff -Naur cymysql/connections.py cymysql-ipv6/connections.py
--- cymysql/connections.py    2017-06-10 03:51:56.000000000 -0400
+++ cymysql-ipv6/connections.py    2017-10-27 05:12:51.125249151 -0400
@@ -398,13 +398,14 @@
                 self.host_info = "Localhost via UNIX socket"
                 if DEBUG: print('connected using unix_socket')
             else:
-                sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
-                t = sock.gettimeout()
-                sock.settimeout(self.connect_timeout)
-                sock.connect((self.host, self.port))
+                sock = socket.create_connection((self.host, self.port), self.connect_timeout)
                 self.host_info = "socket %s:%d" % (self.host, self.port)
                 if DEBUG: print('connected using socket')
-            sock.settimeout(t)
         except socket.error as e:
             sock.close()
             raise OperationalError(2003, "Can't connect to MySQL server on %r (%s)" % (self.host, e.args[0]))

三、ServerStatus监控脚本改造

原来使用的服务器监控脚本和cymysql的一样情况。虽然脚本的部分地方考虑到了IPv6的情况。如(在这以将ServerStatus中文化的最初repo tenyue/ServerStatus 为例说明):
在监控脚本的L150中,和cymysql一样使用了socket.socket(socket.AF_INET, socket.SOCK_STREAM)这种只支持IPv4形式与服务器通信。

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

但是很奇怪的是这个监控脚本get_network()中判断IPv4、IPv6连接的逻辑又是这样的:如果脚本以IPv4的形式与服务器通信,那么这直接默认监控的主机的IPv4是正常的,那么get_network()接收的参数ip_version就只有6,即只检查监控主机的IPv6是否正常。
没办法,自己fork -> Rhilip/ServerStatus 修改吧。而且个人觉得tenyue的部分修改不这么好,比如:

  • 移除了IPv6的支持(其实是前端不显示)
  • 在添加load 1,5,15的情况下未考虑向前兼容。如果前端接收的字段仍为原先的load,但是后端更新后变成 load_1,load_5,load_15。丢失了原先代表load 1 的load字段。
  • 原有脚本的是tab缩进的,改成4空格23333

Flask

这个相对简单,只要指定host是 ::就行。

from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello():
    return "Hello world~"


if __name__ == '__main__':
    app.run(host="::")

非特殊说明,本博所有文章均为博主原创。

评论啦~