使用 docker 容器误区

对于用户来说,可能一开始在不了解的情况下会对容器报以拒绝的态度,但是在尝到容器的甜头、体验到它的强大性能之后,相信大家最终是无法抵挡其魅力的

容器技术能够解决IT业目前面临的很多问题,而且优势也很明显,比如说:

 容器将操作系统、程序库、配置文件、路径和应用程序打包在一起运行,也就是说,我们在做QA测试的时候整个镜像是什么样,投入到产品环境以后就是什么样,其性能不会有任何差距

单个容器的内存占用很小,不像其他进程动辄占用上万MB的内存空间,容器只会给主进程分配内存,可以有效降低系统开销

   虚拟机的启动时间一般都在分钟级,容器的启动速度可以达到秒级,启动容器就跟启动linux进程一样快。     虽然容器的好处这么多,但是有很多用户还不了解,还认为容器跟一般的虚拟机没什么差别。实际上,容器是可销毁的,这是容器跟虚拟机之间最大的差别。容器的存在周期很短,只要用户使用完毕,就可以立即销毁容器,所以用“朝生暮死”来形容也不算过分。                                                                                                                                                 

在对容器进行使用和维护时,我们应该充分利用容器的这个特性,不要再把容器当成一般的虚拟机来看待,不然就真的大材小用了。

在实际使用过程中,为了最大限度地发挥容器的优势,有些错误还是少犯为妙。我总结出了下面几个要点供大家参考,在跑容器的时候大家最好还是尽量遵照这几条原则:

容器随时都可以停止、销毁或迁移

比方说,一个容器里运行的应用版本是1.0.0,我们分分钟就可以把这个应用升级到1.1.0,同时还不会对数据造成任何影响。所以如果用户想要存数据的话,最好是用数据卷来存储

  • 不过在用卷存数据的时候大家还是要注意一点

如果有两个容器共用一个数据卷,都往里面写数据的话,是有可能造成程序崩溃的。我们在设计应用程序的时候应该考虑到这一点,为保万无一失,应用程序应该具备特定的机制,以确保在往共享数据存储区写入数据的时候不会出错

在部分用户看来,容器跟虚拟机没什么两样,所以有些人往往会把应用程序部署到当前运行的若干个容器中

这种做法在开发阶段没有太大的问题,因为做开发的时候我们会很频繁地进行部署和调试

到了持续交付(CD)阶段,下一步就是QA测试和正式投产了,这种做法就不太适合

在这一阶段,我们应该充分考虑到容器的不可变特性,最好是将应用程序打包到一个镜像中交付

  • 镜像越大,就越难发布
  • 镜像中只包含必要的文件和library就可以了,能让应用或者进程运行起来就行
  • 千万不要在镜像中安装些没必要的东西
  • 在构建镜像的时候要避免使用yum这种update命令,免得系统自动下载很多不相干的文件到新镜像层中

大家都知道,docker的文件系统是分层的

在建镜像的时候我们应该这么建,必需区分 从 CI/CD 部署 反向管理层级

  • 将操作系统单独建一层,作为基础镜像
  • 然后用户名定义文件、运行时安装环境、配置文件都要分别建一层镜像
  • 再针对运行的依赖按版本建立镜像
  • 最后才是应用运行镜像层

注意: 上面的层级是 CD 的层级,开发使用 CD 建立后的镜像

  • 测试是在应用运行镜像层 对应版本进行测试的
  • 开发交付必需适配测试使用的应用运行镜像
  • 开发过程不用严格使用容器开发,但必需适配到对应运行版的镜像
  • 开发需要的额外依赖,导致镜像改动,需要升级运行版的镜像,必需严格按照 semver 语义版本规则 进行管理

linux开发者可以使用容器,非 linux 受限于 docker 在OS运行使用的虚拟环境,性能太差

这么做的话,我们以后重建、管理以及发布镜像的时候就要轻省得多

换句话说就是创建镜像的时候不要用“docker commit”命令来创建

  • 用这种办法建镜像是完全不可取的,因为这种办法是不能重复的 破坏容器编排
  • 我们在建镜像的时候应该从Dockerfile创建,或者用其他S2I(从源文件构建镜像)的方式来创建,这样镜像才具有可再生性
  • 而且如果我们把镜像存在git之类提供版本控制能的系统里的话,还可以对 Dockerfile 的改动进行跟踪

latest其实就相当于 maven 里头的 快照

因为容器的文件系统是分层的,我们最好是给镜像多打几个tag

如果只有latest的话,可能过段时间我们再来运行应用程序的时候就发现程序运行不起来了

因为应用的父层(就是Dockerfile里面的跟在FROM命令后面的那一层)被更新的版本覆盖了,而新版本又不能向下兼容,还有可能就是从build cache里面取镜像的时候取到了错的 latest 镜像

在产品环境中部署容器的时候也要避免使用latest,不然容易造成无法跟踪记录镜像版本的问题

容器本来就是用来运行单个应用的(比如http daemon,应用服务器,数据库等等)

如果我们非要在一个容器里跑几个应用,那么在管理每个应用进程、存取日志、升级应用的时候就会很麻烦

如果我们把用户名/密码值对存在镜像里的话,就只有采用硬编码的方式来挨个处理,估计这种麻烦事没人愿意去干

所以我们最好是用环境变量的方从容器外部获取此类信息

docker容器默认是以 root权限 运行的

不过随着技术的成熟,docker也会提供安全性更高的默认操作选项

在现有技术条件下,以root权限运行会对其他应用带来安全隐患,而且在有些运行环境下root权限是取不到的,所以我们在跑容器的时候应该用USER命令来指定非root权限的用户

最好在 Dockerfile 中直接指定运行用户,从根源上解决这个问题

每个容器都有一个内部IP,这个IP不是固定的,我们启动容器或者停止容器的时候IP都会变

如果我们要让应用或者微服务模块在容器之间进行通信的话,正确的做法是

  • 通过设置环境变量来传递主机名和端口号
  • 容器之间,不要使用ip,使用别名