Docker遇到的一些问题和感想
Docker 是“不可变”架构。
当你希望改变一个服务的时候(比如更新版本、修改配置、开放端口),不允许直接登录到服务器上改变某个文件,而是应该把这个服务整个删掉,然后替换成新的版本。你不能改变它,只能替换它,这就是 Docker 的优点。
在服务规模大的时候,这种维护方式能够保持每个服务版本、配置的一致性。Docker 禁止对容器内部做任何修改,所以只要查看镜像版本和调度参数,就能判断服务的一致性。系统运行在软件定义的基础架构上,这样就可以使用版本管理工具(比如 Git)管理基础架构的变化,像管理软件版本一样管理整个环境。这是他的优势。
Docker “还不够好”。
- 不少同事或者朋友,吐槽了Docker的很多麻烦事儿,简单说就是抛弃了传统的操作系统环境,很多原来的东西都要用新的容器工具链。Docker的隔离性也没有虚拟机级别的好。这些都是客观存在的。Docker是一套新的承载环境,相对于传统的虚拟机需要非常多的新的工具链,但远没有成熟。带来的好处,在传统的模式下也不是没有方案。所以Docker仍然缺少决定性的优势。并不能说服大家大规模的迁移和适应。
- 目前docker 镜像,没有统一标准,体现在一下几个方面。在使用过程中会遇到过各种本班的 OS。包括 alpine, debian, ubuntu, centos, oraclelinux, redhat 等等。即使是镜像采用 CentOS 母版,很多镜像制作者会给操作系统减肥。经过优化后,已经不是官方版本,在使用过程中你会遇到各种麻烦。例如调试的时候需要 curl,wget,telnet,nslookup 等工具在镜像中没有。甚至 ps, top, free, find, netstat, ifconfig 命令都没有。很多容器都不带 iptables 所以,即使带有iptables 在容器中修改规则也很麻烦。
传统OS 以 CentOS为例,有严格的安装规范,例如:
/etc/example 配置文件
/bin/sbin 二进制文件
/var/lib/example 数据文件
/var/log/example 日志文件
/var/run/example PID 文件 /etc/sysconfig/example 启动参数文件
/etc/system.d/example 启动脚本或者被安装在:
/usr/local/etc 配置文件
/usr/local/bin 可执行文件
/usr/local/share 文档最后一种是独立安装在:/usr/local/example 下。容器镜像那可是五花八门,没有统一标准,如果不看 Dockerfile 根本不知道作者将文件安装到了哪里。常常存储目录被放置在根目录。例如 /data
- 在我的执业生涯中是遇到过 Linux 系统有BUG的,如果你采用的镜像有BUG,你想过怎么去debug 吗?
- 在Linux是一般是采用守护进程方式启动。启动后进入后台,启动采用 systemd 。
- 容器中启动通常是直接运行,这样的运行方式,相当于你在linux的Shell 终端直接运行一样,是在前台运行,随时 CTRL + C 或者关闭终端窗口,程序就会退出。容器采用这种方式启动,就是为了让 docker 管理容器,docker 能够感知到容器的当前状态,如果程序退出,docker 将会重新启动这个容器。
- 守护进程方式需要记录 pid 即父进程ID,用于后面管理该进程,例如可以实现 HUP 信号处理。也就是 reload 操作,不用退出当前程序实现配置文件刷新。处理 HUP 信号,无需关闭 Socker 端口,也不会关闭线程或进程,
- 用户体验更好。容器是直接运行(前台运行),所以没有 PID 也不能实现 reload 操作。 配置文件更新需要重新启动容器,容器启动瞬间TCP Socker 端口关闭,此时用户会 timeout。甚至该服务可能会引起集群系统的雪崩效应。
- 很多镜像制作者更趋向使用环境变量传递启动参数。当然你也可以在容器中使用 systemd ,这样做容器不能直接感知到容器的运行状态,systemctl stop example 后,容器仍然正常。需要做存活和健康检查。通过健康状态判断容器的工作情况。如果处于非健康状态,将该节点从负载均衡节点池中将它踢出去。
- Linux 启动一个应用远远比docker 启动一个容器速度要快。因为物理机或者虚拟机的Linux操作系统已经启动,虚拟机也分配了资源,运行可执行文件基本上是瞬间启动。而 docker 启动容器,要分配资源(分配内存和CPU资源,新建文件系统),相当于创建一个虚拟机的过程,最后载入约200MB左右的镜像,并将镜像运行起来,所以启动所需时间较长,有时不可控,尤其是Java应用更为突出。
- 存储面临的问题。传统 Linux 直接操作本地硬盘,IO性能最大化。私有云还好办公有云处处受限。自建的 Docker 或 Kubrnetes 可以使用宿主主机资源,公有云只能使用网络文件系统和分布式系统。这也是我的架构中 KVM,Docker,Kubernetes,物理机混合使用的原因,根据业务场景的需要来选择哪种方案。
- 物理机上部署 docker 可以分配宿主主机的所有资源,适合做有状态的服务的存储持久化的需求。
- 私有云Kubernetes 适合做 CPU密集型运算服务,虽然通过local 卷和 hostPath 可以绑定,但是管理起来不如 Docker 更方便。
- NFS 基本是做实验用的,不能用在生产环境。我职业生涯遇到过很多奇葩,例如 NFS 卡顿,NFS 用一段时间后访问不了,或者可以访问,文件内容是旧的等等。无论是NFS是更先进的分布式文件系统,如果不是 10G以太网,基本都不能用在生产环境。多年前我用4电口1G网卡做端口聚合勉强可以用于生产环境,不过当年的互联网生态跟当今不同,那时还是以图文为主,确切的说是文字为主,配图还很少。
- 内部域名DNS。由于在集群环境中容器名称是随机,IP地址是不固定的,甚至端口也是动态的。为了定位到容器的节点,通常集群中带有DNS功能,为每个节点分配一个域名,在其他容器中使用域名即可访问到需要的容器。
- 看似没有问题,我的职业生涯中就遇到过DNS的问题,bind,dnsmseq 我都用过,都出现过事故。解析卡顿,ping www.domain.com 后迟迟解析不出IP。最长一次用了几分钟才解析到IP地址。
- 所以后面就非常谨慎,配置文件中我们仍然使用域名,因为修改配置文件可能需要 reload 应用,或者重新部署等等。域名写入配置,方便IP地址变更。例如 db.host=www.domain.com 同时我们会在 /etc/hosts 中增加 xxx.xxx.xxx.xxx www.domain.com。这样主要使用 /etc/hosts 做解析,一旦漏掉 /etc/hosts 配置 DNS 还能工作。
- 故障分析。DNS 使用 UDP 协议 53 端口,UDP 在网络中传输不会返回状态,有无数种可能导致 DNS 解析失败。例如内部的交换机繁忙,背板带宽不够(用户存储转发数据包,你可以理解就是交换机的内存),路由的问题等等……
- 容器中的网络环境。
- 相比传统网络,容器中的网络环境是十分复杂的。传统网络中一个数据包仅仅经过路由器,交换机,达到服务器,最多在服务前在增加一些防火墙,负载均衡等设备。
- 容器网络部分实现方式SDN(软件定义网络)相比物理机(路由器、交换机、无服务)实现相对复杂。容器里面使用了IP转发,端口转发,软路由,lvs,7层负载均衡等等技术…… 调试起来非常复杂。docker 的 iptables 规则很头痛。
- 例如一个TCP/IP 请求,需要经过多层虚拟网络设备(docker0,bridge0,tun0……)层层转发,再经过4层和7层的各种应用拆包,封包,最终到达容器内部。有兴趣你可以测试一下对比硬件设备,容器的网络延迟和吞吐量。
- 容器的管理。
- 传统服务可以通过键盘和显示器本地管理,OpenSSH 远程管理,通过配置还能使用串口。容器的管理让你抓狂 docker exec 和 kubectl exec 进入后与传统Linux差异非常大,这是镜像制作者造成的。
- 有些镜像没有初始化 shell 只有一个 $ 符号,没有彩色显示,可能不支持 UTF-8,中文乱码,可能不是标准 ANSI/XTerm 终端,键盘定义五花八门,可能不是美式104键盘,国家和时区并不是东八区,HOME 目录也是不是 /root······
- 想查看端口情况,发现 netstat 和 ss 命令没有。想查看IP地址,发现 ifconfig, ip 命令没有。想测试IP地址是否畅通,发现 ping, traceroute 没有。想测试URL,发现 curl , wget 没有。
- 有些镜像 dnf,yum,apk,apt 可以使用,有些镜像把包管理也给阉割了,你想安装上述工具都安装不了。然后就自己用 Dockerfile 编译,整出200MB的镜像,卧槽这么大。
- 容器的安全。
- 很多容器的镜像中是不包含 iptables 的,所以无法做颗粒度很细的容器内部网络安全设置。即使你制作的镜像带有iptables ,多数容器的策略,IP地址和端口是随机变化的。绑定IP地址又带了容器的复杂性。一旦攻入一个容器,进入容器后,容器与容器间基本是畅通无阻。
- 在容器中藏一个后门比物理机更容易,如上文所说很多容器中没有调试相关命令,限制了你排查后门的难度。所以Dockerfile 制作镜像,最好使用官方镜像衍生出你的镜像。
- 容器与CI/CD
- 在DevOps场景中,使用 docker 或 kubernetes 做 CI/CD 是很扯淡的。当 git 产生提交后,gitlab/jenkins 启动容器,下载代码,编译,打包,测试,产生构建物,编译 Dockerfile ,上传 docker 镜像到 registry,最后部署到容器执行。卧槽!!!速度能急死你。
- 于是乎,我们做了 Cache。 不用每次都 pull 镜像,缓存 Maven 的 .m2 库,不再清理代码(mvn clean)提速不少,测试环境凑合用吧。 注意不mvn clean 有时会编译出错。至于生产环境,我就不说了,有多少人真用CD部署生产环境。
使用物理机,虚拟机,学习成本,试错成本,部署成本远远低于容器技术。Google 官方也曾经说过,未来 kubernetes 重点可能会转向虚拟机。不过Docker的理念和思想还是值得学习的。任何技术都会有平替,只是各种成本的妥协,在你想好怎么处理一些问题的时候,谨慎引入Docker等各类新技术到你的生产环境中。还有一些Docker+微服务遇到的坑和想法,等有空了再更新。
Docker遇到的一些问题和感想的更多相关文章
- 对.Net Core结合Docker和Jexus的实践
本文基于上次尝试之后的进一步尝试,加入Docker容器.编写Dockerfile,并且jexus结合Docker的使用,总结下自己的个人感想. 一.环境介绍 当前的场景有两种方式将Demo实现运行,一 ...
- 工作五年的.neter的一些经历感想和对未来的一些疑惑
本次疫情在家办公快一个月了,节省了上下班的时间,外出活动时间,感觉有好多时间可以利用.人一闲下来就容易想事情,很多事情想不通心里堵的厉害,做事都提不起兴趣.至于想些什么呢,我给大家摆一下. 我的经历 ...
- 支持初始化数据的Zookeeper Docker镜像
最近在做一个演示项目 https://github.com/cnscud/cavedemo, 自然为了方便, 也做了docker打包, 发现zookeeper的镜像没有导入初始化数据的功能, 于是自己 ...
- docker——容器安装tomcat
写在前面: 继续docker的学习,学习了docker的基本常用命令之后,我在docker上安装jdk,tomcat两个基本的java web工具,这里对操作流程记录一下. 软件准备: 1.jdk-7 ...
- Docker笔记一:基于Docker容器构建并运行 nginx + php + mysql ( mariadb ) 服务环境
首先为什么要自己编写Dockerfile来构建 nginx.php.mariadb这三个镜像呢?一是希望更深入了解Dockerfile的使用,也就能初步了解docker镜像是如何被构建的:二是希望将来 ...
- Docker 第一篇--初识docker
已经多年不写博客, 看完<晓松奇谈>最后一期猛然觉醒, 决定仔细梳理下自己这几年的知识脉络. 既然决定写, 那么首先就从最近2年热门的开源项目Docker开始.Docker 这两年在国内很 ...
- 在docker中运行ASP.NET Core Web API应用程序(附AWS Windows Server 2016 widt Container实战案例)
环境准备 1.亚马逊EC2 Windows Server 2016 with Container 2.Visual Studio 2015 Enterprise(Profresianal要装Updat ...
- docker for mac 学习记录
docker基本命令 docker run -d -p 80:80 --name webserver nginx 运行容器并起别名 docker ps 展示目前启动的容器 docker ps -a 展 ...
- scrapy爬虫docker部署
spider_docker 接我上篇博客,为爬虫引用创建container,包括的模块:scrapy, mongo, celery, rabbitmq,连接https://github.com/Liu ...
随机推荐
- 【记录一个问题】android下opencl中的event.getProfilingInfo()测速时间并不准确
使用了类似的代码来做android下opencl的时间测试: cl::CommandQueue queue(context, devices[0], CL_QUEUE_PROFILING_ENABLE ...
- 在linux下编译android下的opencv,使用cmake的方法
#前一篇帖子实验了build_sdk.py来编译opencv,失败了.#本篇尝试使用cmake来编译#感谢这篇帖子提供的指导:https://www.cnblogs.com/jojodru/p/100 ...
- java抽象类概述特点
1 package face_09; 2 /* 3 * 抽象类: 4 * 抽象:笼统,模糊,看不懂!不具体. 5 * 6 * *特点: 7 * 1,方法只有声明没有实现时,该方法就是抽象方法,需要被a ...
- 了解promise、promise对象
Promise 是异步编程的一种解决方案,比传统的解决方案--回调函数和事件--更合理和更强大.它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Promise对象. 所谓Pr ...
- 004 Linux 揭开神器 vim 面纱
01 开篇初识 vim vim 功能吊炸天,但我们掌握一些常用的命令即可应对日常的使用了,不记流水账! Linux 中最常用的编辑器是什么? vim ! vi 跟 vim 啥区别? vim 就是 vi ...
- PowerDotNet平台化软件架构设计与实现系列(12):HCRM人员管理平台
技术服务于业务,良好的技术设计和实现能够大幅提升业务质量和效率. PowerDotNet已经形成了自己的开发风格,很多项目已被应用于生产环境,可行性可用性可靠性都得到了生产环境验证. 编程是非常讲究动 ...
- k8s之Pod基础概念
1. 资源限制 Pod是kubernetes中最小的资源管理组件,Pod也是最小化运行容器化应用的资源对象.一个Pod代表着集群中运行的一个进程.kubernetes中其他大多数组件都是围绕着Pod来 ...
- 4、Linux基础--系统目录
笔记 1.晨考 1.移动文件的命令 mv 2.删除文件的命令及其参数 rm 参数: -r : 递归删除 -f : 不提示删除 -i : 提示删除 3.复制文件的命令及其参数 cp 参数: -r : 递 ...
- ThinkPHP5中使用第三方类库
在TP5中有两种方式使用第三方类库,如果类库支持composer方式安装那就很方便了,使用composer安装的类库存储在Vendor目录下,可以直接使用,以phpmailer为例,使用如下命令安装: ...
- MyBatis缓存Cache包
Mybatis默认使用了一级缓存为数据查询提供效率,其底层数据结构为PerpetualCache.同时MyBatis不止实现了PerpetualCache,还有其他实现提供其他功能,如下: 可以从UM ...