nova vnc 流程

9 篇文章 0 订阅
8 篇文章 0 订阅

前段时间部署的openstack环境,今天准备用vnc查看下虚拟机的console,结果尽然,提示连接不了服务器。 

原来是在打开的页面是在nova.conf 里 novncproxy_base_url 配置的

[vnc]
enabled=True
keymap=en-us
vncserver_listen=0.0.0.0
vncserver_proxyclient_address=$my_ip
novncproxy_base_url=http://cloud.xx.com:6080/vnc_auto.html
novncproxy_host=0.0.0.0
novncproxy_port=6080


但是还是不行,虽然打开了页面,但是好像还不能连接到vnc,于是多刷新了几次,发现是刷新三次会有一次能连接到console,这就奇怪了, 于是查看代码,终于发现问题了,默认的token对于的connection_info是存在本地的,没有存放到db或者memcached里,所以在某个nova-consoleauth里成功的,在其它nova-consoleauth里是验证不成功的


为了试验下是否推理成功,于是停掉其它两个nova-consoleauth,发现这时候果然可以成功访问了。


于是开始想nova novnc的流程,vnc是怎么实现通信,各个组件间怎么交互的,还有以前公司 vnc 是怎么实现的。 

什么东西还是要多想多记录,才会更好积累。  其中感受还是颇多的。


知道具体出问题的原因后,又细看了下代码,有相应的配置,可以配置成用memcached来存储token对应的connection_info的。想想以前,根本是没有可配置项的,都是我们自己的写的代码,去完善,这其中的过程也是挺美女的,至少能理解整个流程了。


于是在nova.conf里加入配置:

[cache]
enabled=True
backend=oslo_cache.memcache_pool
memcache_servers = memcached_1.tencent.com:11211

然后重启所有的 nova-consoleauth 服务,测试成功。


nova-consoleauth流程

二、过程分析
     VNC Proxy的功能:
  • 将公网(public network)和私网(private network)隔离
  • VNC client运行在公网上,VNCServer运行在私网上,VNC Proxy作为中间的桥梁将二者连接起来
  • VNC Proxy通过token对VNC Client进行验证
  • VNC Proxy不仅仅使得私网的访问更加安全,而且将具体的VNC Server的实现分离,可以支持不同Hypervisor的VNC Server但不影响用户体验
     VNC Proxy的部署
  • 在Controller节点上部署nova-consoleauth 进程,用于Token验证
  • 在Controller节点上部署nova-novncproxy 服务,用户的VNC Client会直接连接这个服务
  • Controller节点一般有两张网卡,连接到两个网络,一张用于外部访问,我们称为public network,或者API network,这张网卡的IP地址是外网IP,如图中172.24.1.1,另外一张网卡用于openstack各个模块之间的通信,称为management network,一般是内网IP,如图中10.10.10.2
  • 在Compute节点上部署nova-compute,在nova.conf文件中有下面的配置
    • vnc_enabled=True
    • vncserver_listen=0.0.0.0 //VNC Server的监听地址
    • vncserver_proxyclient_address=10.10.10.2 //nova vnc proxy是通过内网IP来访问vnc server的,所以nova-compute会告知vnc proxy用这个IP来连接我。
    • novncproxy_base_url=http://172.24.1.1:6080/vnc_auto.html //这个url是返回给客户的url,因而里面的IP是外网IP
  • 注:
    也可以将控制节点放在内网,专门设置一个api节点,这时,nova-consoleauth就需要安装在控制节点上,novnc
    python-novnc包安装在api节点上。 
VNC Proxy的运行过程:
 1、一个用户试图从浏览器里面打开连接到虚拟机的VNC Client
 2浏览器向nova-api发送请求,要求返回访问vnc的url
 3nova-api调用nova-compute的get vnc console方法,要求返回连接VNC的信息
 4nova-compute调用libvirt的get vnc console函数
 5libvirt会通过解析虚拟机运行的/etc/libvirt/qemu/instance-0000000c.xml文件来获得VNC Server的信息
 6libvirt将host, port等信息以json格式返回给nova-compute
 7nova-compute会随机生成一个UUID作为Token
 8nova-compute将libvirt返回的信息以及配置文件中的信息综合成connect_info返回给nova-api
 9nova-api会调用nova-consoleauth的authorize_console函数
 10、nova-consoleauth会将instance –> token, token –> connect_info的信息cache起来
 11、nova-api将connect_info中的access url信息返回给浏览器: http://172.24.1.1:6080/vnc_auto.html ?token=7efaee3f-eada-4731-a87c-e173cbd25e98&title=helloworld%289169fdb2-5b74-46b1-9803-60d2926bd97c%29
 12、浏览器会试图打开这个链接
 13、这个链接会将请求发送给nova-novncproxy
 14、nova-novncproxy调用nova-consoleauth的check_token函数
 15、nova-consoleauth验证了这个token,将这个instance对应的connect_info返回给nova-novncproxy
 16、nova-novncproxy通过connect_info中的host, port等信息,连接compute节点上的VNC Server,从而开始了proxy的工作


class NovaProxyRequestHandlerBase(object):
  
    def new_websocket_client(self):
        """Called after a new WebSocket connection has been established."""
        # Reopen the eventlet hub to make sure we don't share an epoll
        # fd with parent and/or siblings, which would be bad
        from eventlet import hubs
        hubs.use_hub()

        
        query = parse.query
        token = urlparse.parse_qs(query).get("token", [""]).pop()  
        if not token:           #如果在url里没有找到token,就去cookie里找,好熟悉的代码啊,以前我们也是这样做的。
            # NoVNC uses it's own convention that forward token
            # from the request to a cookie header, we should check
            # also for this behavior
            hcookie = self.headers.getheader('cookie')
            if hcookie:
                cookie = Cookie.SimpleCookie()
                for hcookie_part in hcookie.split(';'):
                    hcookie_part = hcookie_part.lstrip()
                    try:
                        cookie.load(hcookie_part)
                    except Cookie.CookieError:
                        # NOTE(stgleb): Do not print out cookie content
                        # for security reasons.
                        LOG.warning(_LW('Found malformed cookie'))
                    else:
                        if 'token' in cookie:
                            token = cookie['token'].value

        ctxt = context.get_admin_context()
        rpcapi = consoleauth_rpcapi.ConsoleAuthAPI()
        connect_info = rpcapi.check_token(ctxt, token=token)

        host = connect_info['host']
        port = int(connect_info['port'])

        # Connect to the target
        self.msg(_("connecting to: %(host)s:%(port)s") % {'host': host,
                                                          'port': port})
        tsock = self.socket(host, port, connect=True)     #这个就是调用的 NovaProxyRequestHandler 里的socket方法

        # Start proxying
        try:
            self.do_proxy(tsock)               #从这里可以看出实现这个代理很简单,直接把
        except Exception:
            if tsock:
                tsock.shutdown(socket.SHUT_RDWR)
                tsock.close()
                self.vmsg(_("%(host)s:%(port)s: "
                          "Websocket client or target closed") %
                          {'host': host, 'port': port})
            raise


class NovaProxyRequestHandler(NovaProxyRequestHandlerBase,
                              websockify.ProxyRequestHandler):
    def __init__(self, *args, **kwargs):
        websockify.ProxyRequestHandler.__init__(self, *args, **kwargs)

    def socket(self, *args, **kwargs):
        return websockify.WebSocketServer.socket(*args, **kwargs)  #这个websockify里的socket,其实就是个tcp的socket


从以上代码可以看出 这个websocket 就是调用的 websockify库,然后调用 do_proxy  实现 tcp socket 转 websocket 实现和 浏览器的通信。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值