当前位置:首页 > 问答 > 正文

容器要快又稳,别忘了这些关键点,不然容易出问题

容器技术要想运行得又快又稳,避免各种烦人的问题,有几个关键的地方必须得留心,这些点就像是盖房子的地基,地基打不牢,房子盖得再漂亮也容易出事儿,咱们就一个一个把这些关键点说清楚。

最基础也最要紧的就是镜像,镜像就是容器的模板,模板要是不好,做出来的东西肯定问题多,一个常见的坏习惯就是把所有东西都塞进一个镜像里,弄得镜像特别臃肿(来源:Docker官方最佳实践),镜像越大,下载速度就越慢,启动容器也可能更慢,而且里面用不到的软件还可能带来安全风险,正确的做法是尽量让镜像变小,选择一个小巧的基础镜像,像Alpine Linux就比完整的Ubuntu小很多;还有,在Dockerfile里,把多个命令合并成一行,并且及时清理掉安装过程中产生的临时文件,这样能有效减少镜像的层数和大小(来源:Docker官方最佳实践),千万别把密码、API密钥这些敏感信息直接写在镜像里,不然谁拿到镜像谁就能看到你的秘密,这些信息应该在运行容器的时候,通过环境变量或者文件挂载的方式传进去(来源:十二要素应用准则)。

容器的运行方式也很关键,很多人觉得容器能随便启停,就忽略了它的状态管理,一个核心原则是容器应该是“无状态”的(来源:十二要素应用准则),什么意思呢?就是容器本身不应该保存任何需要持久化的数据,比如用户上传的文件、应用程序的会话(Session)信息或者数据库文件,你把数据存在容器里,一旦容器崩溃或者被重启,这些数据就全没了,正确的做法是,把这些需要持久化的数据放在容器之外,比如使用 Docker 卷(Volumes)或者把数据挂载到宿主机的一个目录上,这样,容器本身可以随意销毁和重建,但你的数据却安然无恙,同样,会话状态最好也放到外部的缓存服务里,比如Redis,而不是放在容器的内存里。

再说说资源限制,这个问题不注意,很容易导致“一颗老鼠屎坏了一锅粥”,默认情况下,容器可以无限使用宿主机的CPU和内存(来源:Docker文档),如果你不加以限制,万一有一个容器里的程序出了bug,疯狂占用内存或CPU,就会把整个宿主机的资源都吃光,导致其他所有容器都跟着遭殃,服务瘫痪,在启动容器的时候,一定要根据实际情况给它设定CPU和内存的使用上限,你可以规定这个容器最多只能用1个CPU核心、512MB的内存,这样,即使它自己出了问题,也不会影响到宿主机上其他的兄弟容器,保证了整个系统的稳定性。

接下来是网络和端口的管理,容器通常有自己的内部网络,你需要把容器里应用监听的端口映射到宿主机上,外面才能访问(来源:Docker网络概念),这个地方配置错了,服务就访问不到,比如你的应用在容器里运行在8080端口,那你启动容器时就得指定把容器的8080端口映射到宿主机的某个端口(比如80端口)上,如果多个容器之间需要互相通信(比如一个Web容器需要连数据库容器),直接靠IP地址是很不可靠的,因为容器的IP可能会变,这时候最好使用Docker的自定义网络,让容器之间可以通过容器名称来互相访问,这样就稳定多了。

日志处理也是个大事儿,容器里应用打印到控制台(stdout和stderr)的信息,默认会被Docker引擎收集(来源:Docker日志驱动),但你得想办法把这些日志妥善地保存和分析,不能任由日志在容器里堆积,否则会把磁盘撑爆,通常的做法是配置一个日志驱动,把日志集中发送到一个地方,比如Elasticsearch、Fluentd或者云服务商提供的日志服务,这样,无论容器跑到哪台机器上,你都能在一个地方查看到所有日志,出了问题也方便排查。

安全问题是绝对不能放松的,虽然容器有隔离性,但并不是铜墙铁壁,一个重要的原则是:不要用root用户来运行容器里的应用(来源:Docker安全最佳实践),默认情况下,容器里的进程是以root身份运行的,这有潜在风险,你应该在Dockerfile里创建一个专门的、没有特权的普通用户,然后用这个用户来启动你的应用,这样可以最大限度地减少万一应用被攻击后带来的破坏,要定期扫描你的镜像,看看里面用的软件有没有已知的安全漏洞,及时更新打补丁。

想让容器又快又稳,你得在镜像构建上追求小而精,在运行时坚持无状态,严格限制资源使用,正确配置网络,妥善处理日志,并且时刻绷紧安全这根弦,把这些点都做到位了,容器才能真正发挥出它轻量、快速、高效的优势,而不是变成一个麻烦不断的问题来源。

容器要快又稳,别忘了这些关键点,不然容易出问题