设为首页 收藏本站
查看: 1301|回复: 0

[经验分享] openstack 扩展开发最佳实践之计算节点高可用

[复制链接]
累计签到:1 天
连续签到:1 天
发表于 2017-3-21 13:30:56 | 显示全部楼层 |阅读模式
前言:注意是扩展开发,这个词是我杜撰的,大概意思是指基于openstack的rest api做的一些开发,用于辅助相关功能,而不是直接改动openstack内的代码,怎么修改添加openstack各个组件的代码不在此文章内容内。

  首先,千万,千万,千万不要用Openstack提供的SDK,原因如下。
  一,SDK的相关文档并不健全。
  二,版本不够统一,即兼容的问题。

   所以不要使用openstack的SDK而是自己查阅openstack的API文档,通过requests库发http请求要比SDK灵活并便捷得多的方式。但是难过的地方在于开头,这也是本章的主要内容。为了使内容更加贴近现实,我们要做的需求是Openstack计算节点的高可用,主题内容如下:
    一:解决思路

    二:查询相关API
    三:组织代码
    四:总结


(一)解决思路
    其实openstack计算节点高可用的方案应该是不同的厂商有不同解决方案,这里不具体分析各个方案,二是针对笔者自己实现的一个方案的实现讲解。

    一,怎样确定计算节点挂了
    二,怎样将计算节点上的虚拟机迁移到现存可用的计算节点
    三,迁移之后是否应该采取相关措施



    笔者的对应的解决方案如下,

    一:通过openstack自身机制监控,即计算节点不可用时,不会上报状态,控制节点会将该计算节点的状态state设置为down,为down的就判定该计算节点不可用。
    二:通过evacuate API将不可用的计算节点上的虚拟机迁移到可用的计算节点,共享存储肯定是必须的。
    三:通过将该不可用计算节点status设置为disable,以达到隔离效果,即该计算节点即使又好了,也不会将新的虚拟机调度到该计算节点,除非人为干预。

    注:这里没有考虑网络环境。


(二)查询相关API
    一:所有计算节点可用状态:/os-services
    二:查询指定计算节点上的虚拟机:/servers/detail
    三:evacuate实例:/servers/<server-id>/action
    四:disable阶段节点:/os-services/disable
    主要就是上面这些API了。


(三)组织代码
    1.首先是认证获取token,获取各个endpoint。

    2.统一封装HTTP请求过程。
    3.然后为每个对应的API封装一个函数。
    4.将整个需求的逻辑独立与封装的函数及类。


    相对应的代码如下

    1.这里新建一个baseInfo的类作为基类,用于获取token及相应的endpoint

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
class baseInfo(object):
    """init the info of all compute, and get the token for access the api"""

    def __init__(self):
        confFile = sys.argv[1]

        headers = {}
        headers["Content-Type"] = "application/json"

        self.cf = ConfigParser()
        self.cf.read(confFile)
        self.conf = self.getConf()
        self.headers = headers

        self.catalog, self.token = self.getToken()
        self.url = [url for url in self.catalog if url["name"] == "nova"]
        self.url = self.url[0]["endpoints"][0]["publicURL"]

    def getConf(self):
        try:
            conf = {
                "url": self.cf.get("ser","OS_AUTH_URL"),
                "uname" : self.cf.get("ser","OS_USERNAME"),
                "passwd" : self.cf.get("ser","OS_PASSWORD"),
                "tname" : self.cf.get("ser","OS_TENANT_NAME"),
                "interval" : self.cf.get("ser","INTERVAL")}

        except Exception as e:
                logging.critical("加载配置文件失败")
                logging.critical(e)
                sys.exit(1)

        return conf

    def getToken(self):
        headers = self.headers
        url = self.conf["url"] + "/tokens"
        data = '{"auth": {"tenantName": "%s", "passwordCredentials": {"username": "%s", "password": "%s"}}}'
        data = data % (self.conf["tname"], self.conf["uname"], self.conf["passwd"])
        try:
            logging.debug("开始获取Token")
            ret = requests.post(url, data=data, headers=headers)
            logging.debug("request url:%s" % ret.url)
            ret = ret.json()
        except Exception as e:
            msg = "获取Token失败 data:%s headers:%s" % (data, headers)
            logging.critical(msg)
            logging.critical(e)

        catalog = ret["access"]["serviceCatalog"]
        token = ret["access"]["token"]["id"]

        return catalog, token




    2.将每个交互API的HTTP请求独立出来,所谓DRY吧,不要重复你自己,这样的好处自然是不用重复写代码,再者就是在一个统一的地方对所有调用了的,统一包装或修改。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
    def getResp(self, suffix, method, data=None, headers=None, params=None, isjson=True):
        """return the result of requests"""
        url = self.url + suffix
        if headers == None:
            headers = self.headers.copy()
        headers["X-Auth-Token"] = self.token

        req = getattr(requests, method)
        try:
            ret = req(url, data=data, headers=headers, params=params, verify=False)
            logging.debug("request url:%s" % ret.url)
        except Exception as e:
            msg = "%s访问%s失败 data:%s headers:%s" % (method, suffix, data, headers)
            logging.critical(msg)
            logging.critical(e)
            sys.exit(1)

        if ret.status_code == 401:
            logging.warning("Token 过期,重新获取Token")
            self.catalog, self.token = self.getToken()
            headers["X-Auth-Token"] = self.token
            logging.debug("request headers:%s" % ret.request.headers)
            ret = req(url, data=data, headers=headers)

        if isjson:
            ret = ret.json()

        return ret




    3.封装每个API作为函数,函数是第一公民嘛。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
...
    def chkNode(self):
        """get the compute list service down"""
        suffix = "/os-services"
        ret  = self.getResp(suffix, "get")
        ret = ret["services"]

        cmpAll = [host["host"] for host in ret if host["binary"] == "nova-compute"]
        cmpDown = [host["host"] for host in ret if host["state"] != "up" and host["binary"] == "nova-compute"]

        return cmpDown, cmpAll

    def chkSerFromNode(self):
        """get the server list from failed node"""
        suffix = "/servers/detail"
        params = {"all_tenants":1}

        ret = self.getResp(suffix, "get", params=params)

        ret = ret["servers"]
        serDown = [ser["id"] for ser in ret if ser["OS-EXT-SRV-ATTR:host"] in self.cmpDown]
        self.serDown.extend(serDown)
         
    def evacuate(self, serID):
        """evacuate the server"""
        suffix = "/servers/%s/action" % serID
        data = '{"evacuate": {"onSharedStorage": "True"}}'
        ret = self.getResp(suffix, "post", data=data, isjson=False)

        return ret.ok
...




    4.逻辑独立出来,不要做太多事情。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
def main():
    if len(sys.argv) > 1 and os.path.isfile(sys.argv[1]):
        ch = check()
        fen = fence()
        recov = recover()
        while True:
            interval = ch.conf["interval"]

            try:
                interval = int(interval)
            except Exception as e:
                msg = "时间间隔设置有误 interval:%s" % interval
                logging.critical(msg)
                logging.critical(e)
                sys.exit(1)

            cmd = "pcs status|grep 'Current DC'|grep `hostname`"
            p = sp.Popen(cmd, shell=True, stdout=DEVNULL, stderr=sp.STDOUT)
            vip = p.wait()
            if not vip:
                ch.check()
                logging.info("失败的计算节点%s" %ch.cmpDown)
                fen.fence(ch.cmpDown)
                logging.info("失败的计算节点下面的server %s" %ch.serDown)
                recov.recover(ch.serDown)

            time.sleep(interval)
    else:
        print "配置文件不存在"




(四)总结
    不要用SDK,rest API的文档足够健全。


    至于完整项目可参考我的Github。

    https://github.com/youerning/UserPyScript


相关链接:
Openstack API:https://developer.openstack.org/api-guide/quick-start/index.html


运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-353119-1-1.html 上篇帖子: openstack 扩展开发最佳实践之云主机监控查询 下篇帖子: CentOS7下利用cobbler搭建openstack本地源 开发
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表