下载安卓APP箭头
箭头给我发消息

客服QQ:3315713922

新的进入容器的方式大放送

作者:课课家教育     来源: http://www.kokojia.com点击数:617发布时间: 2017-11-15 10:28:53

标签: 云计算虚拟机技术

  欢迎各位阅读本篇文章,容器是应用服务器中位于组件和平台之间的接口集合。本次课课家教育平台为大家带来了新的进入容器的方式大放送。

  众所周知,容器是基于操作系统内核的一种轻量级的虚拟化技术。其可以类比于虚拟机,但其本身并不是虚拟机。在传统的虚拟机使用场景中,每个用户都会通过堡垒机,根据自己被分配的权限,登录某些机器的某些账号。当应用部署逐渐转移到基于容器技术的PaaS平台上后,让用户进入容器进行观察、调试应用已经成为了PaaS平台的一个重要且必备的功能。

  远程进入容器功能的传统实现方式是基于虚拟机的思想,在每个容器中启动一个sshd进程。由于容器PID为1的进程的特殊性,为了保证容器不停,容器的ENTRYPOINT需要设置为类似于Supervisord这样的进程管理程序。在这种多进程容器的使用场景中,用户通过ssh-client指定容器的IP远程连接到容器,让用户感觉到自己好像就在使用虚拟机。但是,这种方案会带来以下问题:

  权限管理。如何控制哪些用户能够登录哪些容器?如何和平台已有的权限管理系统集成?这种情况往往都需要通过堡垒机系统控制。而在PaaS中,引入单独的堡垒机系统会增加PaaS的复杂度以及维护成本。

  登录方式选择。无论使用密码还是私钥验证登录,容器内的密码或者authorized_keys的管理都需要通过加入额外的程序解决,无疑会增加容器的复杂度。同时还要面对同权限容器的密码或authorized_keys的一致性问题。

  基于以上问题,在我们的LAIN平台中,设计出了基于WebSocket协议与Docker Remote API的远程登录方案是一个基于Docker的PaaS。其面向技术栈多样寻求高效运维方案的高速发展中的组织,DevOps人力缺乏的startup以及个人开发者。LAIN通过统一高效的开发工作流,降低应用运维复杂度;在IaaS / 私有IDC裸机的基础上直接提供应用开发,集成,部署,运维的一揽子解决方案。

  该方案的整体架构图如下:

新的进入容器的方式大放送_云计算_虚拟机_技术_课课家教育

  从图中可以看出,在LAIN中实现容器远程登录支持需要以下两个组件:

  1. Entry应用。负责如下工作:

  调用Docker Remote API

  通过WebSocket 传递stdin,stdout和stderr。

  根据protobuf3协议对各类消息进行序列化与反序列化。

  对用户登录的鉴权。

  Entry是基于Go语言开发的,并依赖如下代码库:

  github.com/gorilla/websocket:WebSocket的服务端实现。

  github.com/fsouza/go-dockerclient:Go语言的Docker客户端。

  github.com/golang/protobuf/proto:protobuf协议的支持库。

  2. 基于命令行的客户端。负责如下工作:

  WebSocket连接请求的发送。

  监听键盘输入、窗口变化事件以及WebSocket返回的stream。

  将远端的stdout,stderr输出到本地终端的标准输出和标准错误。

  Entry的工作流程

  通过命令行客户端远程登录容器的过程及其实现如下:

  用户通过客户端命令向Entry应用发送WebSocket连接请求。

  Entry应用接收到用户请求,得到请求Header中的Access_token以及要进入的容器信息,通过调用LAIN的console接口判断该用户是否有权限进入容器。如果没有权限,则直接通知客户端鉴权失败,本次连接结束。

  如果通过了权限验证,则WebSocket连接会被建立。紧接着Entry会去调用 execCreate 这个Docker Remote API。在调用时,需要指定Tty,AttachStdin、AttachStdout和AttachStderr参数均为true,Cmd参数为bash,这样才能获得bash进程的标准输入输出和错误。

  如果调用execCreate成功,调用请求会返回该Exec的ID,Entry会继续根据这个ID调用execStart接口。在调用时,需要指定Detach和Tty为false,这样才能连接到bash进程的标准输 入输出和错误。调用execCreate成功后,会返回一个HTTP的stream。在Entry中则通过3个goroutine分别处理stdin,stdout和stderr。

客户端会同时监听WebSocket连接与键盘输入

  客户端会同时监听WebSocket连接与键盘输入,对于WebSocket返回的Message,客户端会通过Entry制定的protobuf3消息格式反序列化出消息结构,并根据消息的类型,将数据发送到本地终端的stdout或stderr。对于键盘输入,客户端会将输入内容封装,经过protobuf3序列化后,通过WebSocket发送给Entry应用,Entry应用经过反序列化后,将输入发送给bash的stdin。

  以上就是Entry的工作原理。从中我们可以看出,Entry已经很好地解决了传统ssh-client登录所遇到的问题:

  Entry通过调用console的接口完成了身份验证工作,由于所有的权限都被console统一管理,因此Entry不需要自己维护权限信息,即Entry本身是无状态应用。这种应用的优势在于可以低成本扩容,用以应对多并发的场景。

  Entry通过Docker Remote API连接容器,这样只要被连接的容器内可以启动bash进程,用户就可以通过客户端连接到该容器。容器无需启动sshd进程,也就无需再以supervisord等进程作为entrypoint。更多的容器就可以以单进程的形式运行,降低了容器本身的维护成本。

  Entry的设计细节

  俗话说,细节决定成败。为了提高使用体验,Entry应用在设计与实现时考虑到了很多细节,在这里拿出来与大家分享。

  1. 连接保持:当WebSocket连接在一段时间内没有数据传输后,会自动断开。这给用户的使用带来了极大的不便。Entry在设计时,对每一个建立的WebSocket连接,会有一个单独的goroutine每隔10秒发送一个PING类型的Message(不是WebSocket协议中的PingMessage),这样保证了在不主动断开的情况下,用户和容器可以一直保持连接。

  2. 使用protobuf3制定消息格式并实现序列化与反序列化:使用protobuf3可以方便地定义与扩展自己的消息格式,同时在传输时能减小一定的带宽占用。

  Entry的消息格式有两类,RequestMessage和ResponseMessage。客户端发送的请求都属于RequestMessage,服务端返回的数据都封装在ResponseMessage中。其中:

  RequestMessage类型包括:PLAIN和WINCH。PLAIN就是用户通过键盘的输入。WINCH则是终端窗口大小改变的消息,内容中会携带新窗口的rows和cols。

  ResponseMessage类型包括:STDOUT, STDERR,PING和CLOSE。STDOUT和STDERR代表了该消息内容是来自于标准输出还是标准错误。PING则代表是连接保持专用的信息。CLOSE则是连接将要断开前Entry返回的信息,会包含错误原因或者正常退出的信息。

  3. 监听终端窗口大小改变:默认的终端窗口大小都是80 * 24,但该标准在当前的日常使用中早已过时。如果在一个全屏的terminal中仍然使用该大小显然是不合理的。因此客户端在成功连接到容器后,客户端会首先根据当前的terminal大小发送一个WINCH类型的RequestMessage,Entry收到后会调用ExecResize接口,这样之后所有的stdout和stderr都会按照新的终端大小显示。客户端还会监听窗口大小改变的事件,如果发生改变,同样还会发送WINCH到Entry。

  4. UTF-8编码检查:客户端和服务端在发送消息内容时,都会对缓冲区内要发送的数据做UTF-8编码检查。如果发送数据不符合编码规则,则会先发送最长符合的缓冲区前缀,后面剩余的数据则被移到缓冲区的开始,待下次发送。这种设计是为了处理中文等非latin1字符的显示问题。避免因为非法的UTF-8编码造成终端显示乱码。

  Entry存在的问题

  非正常退出时,bash进程不会结束,而是会以sleep的状态残留于容器中。如果一个容器有过多的bash进程,很可能因为cgroup的内存限制导致容器退出。目前官方并没有给出类似execKill的API,只能期待在以后版本的docker中能解决这个问题。

  Entry应用依赖特定的LAIN客户端。之前用户只能通过lain enter命令进入容器。但是12月份后我们升级了console的前端,增加了web terminal功能。用户只需要通过点击容器的ID就可以打开一个含有terminal的web页面,然后通过该web页面与容器进行交互,不需要再安装任何客户端。该项目基于Javascript与CSS实现了一个近乎完美模拟xterm终端的插件。目前,console的web terminal可以支持Firefox和Chrome,但是无法支持IE和Safari。

  总结

  Entry是LAIN中一款设计较为精巧、技术含量较高的应用。其利用了WebSocket全双工传输的特点,在单进程容器的场景下实现了对容器的远程登录,同时保证了登录权限的控制。本文希望通过分享LAIN中Entry的设计与实现,为需要开发远程登录容器功能的PaaS同行提供技术方案参考。

  虚拟机:

  虚拟机资源涉及多个方面:CPU、内存、网络以及磁盘。在规划虚拟机时应该考虑这些资源之间的关系,否则,分配的资源不合理将导致虚拟机内的应用程序性能表现不佳。

  CPU:

  虚拟机每个vCPU只运行在一个物理核心之上,因此CPU频率越高虚拟机的运行速度也就越高,vCPU数量越多有助于提升应用的性能表现。一个比较复杂的因素就是在ESXi服务器内,所有的虚拟机共享使用物理CPU。ESXi服务器的核心数越多,每个vCPU获得的核心份额也就越大,因此多核心的性能表现要强于核心频率高但数量少的情况。

  如果虚拟机需要占用大量的CPU时间,那么可以考虑为虚拟机分配第二个vCPU,但是,为虚拟机分配两个以上vCPU并不一定让应用运行的更快,因为只有多线程应用才能有效地使用多个vCPU。

  RAM:

  ESXi服务器内RAM资源通常有限,因此在给虚拟机分配RAM时需要格外小心。VMkernel在处理RAM时非常巧妙;允许虚拟机使用ESXi服务器所有的物理内存而且会尽量避免占用物理内存却没有真正使用的情况。

  物理内存被完全用完后,VMkernel必须确定哪些虚拟机能够保留物理内存,哪些虚拟机要释放物理内存。这称之为“内存回收”。当虚拟机占用的物理内存被回收后,存在的一个风险就是会对虚拟机的性能造成影响。虚拟机被回收的内存越多,相应的风险也就越大。

  最明智的是只为虚拟机分配完成工作所需要的内存。分配额外的内存将会增加回收风险。另一方面,当虚拟机操作系统将未被使用的内存用作磁盘缓存时,将会显著降低对磁盘系统的性能要求,所以这里有一个折衷问题。

  对于数据库服务器以及VDI桌面来说,为虚拟机分配更多的内存往往更划算—在一台ESXi服务器上运行更少的虚拟机—而不是购买高性能的磁盘阵列。关键在于针对虚拟机的负载分配足够多内存而且没有浪费。

因此,我们需要根据不同的应用场景和需求采用不同的方式使用Docker技术或使用服务器虚拟化技术。

  小结:您可以不采纳我的答案,但请您一定要考虑下我的建议哦! 文章中不足及错误之处在所难免,敬请专家和读者给予批评指正。

赞(0)
踩(0)
分享到:
华为认证网络工程师 HCIE直播课视频教程