Docker:无害建议

在我对Docker文章的评论:不好的建议中 ,有很多要求来解释为什么其中描述的Dockerfile如此可怕。


上一个系列的摘要 :在紧迫的期限内有两名开发人员组成了Dockerfile。 在此过程中,Isper Igor Ivanovich来了。 生成的Dockerfile太糟糕了,以至于AI濒临心脏病发作。



现在,让我们找出这个Dockerfile有什么问题。


因此,一周过去了。


Dev Petya与Ops Igor Ivanovich在饭厅见面,喝杯咖啡。


病人:伊戈尔·伊万诺维奇,你很忙吗? 我想弄清楚我们在哪里搞砸了。


AI:这很好,您不会经常遇到对操作感兴趣的开发人员。
首先,让我们在一些事情上达成共识:


  1. Docker意识形态:一个容器-一个过程。
  2. 容器越小越好。
  3. 使用的缓存越多越好。

警:为什么一个容器中只有一个过程?


AI:Docker在容器启动时会使用pid 1监视进程的状态。如果进程终止,则Docker尝试重新启动容器。 假设您的容器中正在运行多个应用程序,或者主应用程序未使用pid 1启动。如果该进程终止,则Docker将不知道它。


如果没有其他问题,请显示您的Dockerfile。


Petya显示:


FROM ubuntu:latest #    COPY ./ /app WORKDIR /app #    RUN apt-get update #   RUN apt-get upgrade #    RUN apt-get -y install libpq-dev imagemagick gsfonts ruby-full ssh supervisor #  bundler RUN gem install bundler #  nodejs     RUN curl -sL https://deb.nodesource.com/setup_9.x | sudo bash - RUN apt-get install -y nodejs #   RUN bundle install --without development test --path vendor/bundle #     RUN rm -rf /usr/local/bundle/cache/*.gem RUN apt-get clean RUN rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* RUN rake assets:precompile #  ,   ,    . CMD ["/app/init.sh"] 

AI:哦,让我们按顺序整理一下。 让我们从第一行开始:


 FROM ubuntu:latest 

您采用latest标签。 使用latest标签会导致不可预测的后果。 想象一下,映像维护者正在使用其他软件列表构建映像的新版本,该映像将获得最新标签。 最好的情况是,您的容器停止收集,最坏的情况下,您将捕获以前不存在的错误。


您使用具有很多不必要软件的完整操作系统拍摄图像,这会使容器膨胀。 软件越多,漏洞和漏洞就越多。


此外,图像越大,主机和注册表中占用的空间就越大(您是否将图像存储在某处)?


警:是的,当然,我们有一个注册表,您可以设置它。


II:那么,我在说什么?..哦,是的,卷...网络负载也在增长。 对于单个映像,这是不可见的,但是如果进行连续的组装,测试和部署,则可以看到。 而且,如果您在AWS上没有上帝的模式,您还将获得一个太空帐户。


因此,您需要选择最合适的映像,以及准确的版本和最低的软件。 例如,以: FROM ruby:2.5.5-stretch


警:哦,我明白了。 以及如何以及在哪里看到可用的图像? 如何理解我需要哪一个?


AI:通常,图像是从Dockhub拍摄的,不要与pornhub混淆:)。 通常有一个图像的几个程序集:
高山(Alpine) :映像是在仅5 MB的简约Linux映像上编译的。 它的缺点是:它是使用自己的libc实现构建的,其中的标准软件包不起作用。 查找并安装正确的软件包将花费大量时间。
Scratch :基本图像,不用于构建其他图像。 它仅用于运行二进制的准备好的数据。 非常适合运行二进制应用程序,其中包括您需要的一切,例如go-applications。
基于任何操作系统,例如Ubuntu或Debian。 好吧,我认为这里不需要解释。


II:现在我们需要把所有多余的东西都放进去。 打包并清理缓存。 立即您可以进行apt-get upgrade 。 否则,对于每个组件,尽管具有基本图像的固定标签,仍将获得不同的图像。 更新映像中的程序包是维护人员的任务,它伴随着标签的更改。


警:是的,我尝试这样做,结果是这样的:


 WORKDIR /app COPY ./ /app RUN curl -sL https://deb.nodesource.com/setup_9.x | bash - \ && apt-get -y install libpq-dev imagemagick gsfonts ruby-full ssh supervisor nodejs \ && gem install bundler \ && bundle install --without development test --path vendor/bundle RUN rm -rf /usr/local/bundle/cache/*.gem \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* 

II:不错,但是还有一些工作要做。 看,这是以下命令:


 RUN rm -rf /usr/local/bundle/cache/*.gem \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* 

...不会从最终图像中删除数据,而只会创建一个没有此数据的附加图层。 正确的是:


 RUN curl -sL https://deb.nodesource.com/setup_9.x | bash - \ && apt-get -y install libpq-dev imagemagick gsfonts nodejs \ && gem install bundler \ && bundle install --without development test --path vendor/bundle \ && rm -rf /usr/local/bundle/cache/*.gem \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* 

但这还不是全部。 露比,你那里有什么? 这样就不必在开始时复制整个项目。 只需复制Gemfile和Gemfile.lock。


使用这种方法,仅在Gemfile或Gemfile.lock已更改的情况下,才对源的每次更改都不会执行捆绑包安装。


相同的方法适用于依赖管理器的其他语言,例如npm,pip,composer以及其他基于依赖列表文件的语言。


最后,还记得,在开始时我谈到了Docker意识形态“一个容器-一个进程”吗? 这意味着不需要主管。 同样,出于相同的原因,请不要安装systemd。 实际上,Docker本身就是主管。 当您尝试在其中运行多个进程时,就像在一个主管进程中启动多个应用程序一样。
组装时,您将制作一个图像,然后运行所需数量的容器,以便每个进程具有一个进程。


但是稍后会更多。


警:似乎很明白。 看看会发生什么:


 FROM ruby:2.5.5-stretch WORKDIR /app COPY Gemfile* /app RUN curl -sL https://deb.nodesource.com/setup_9.x | bash - \ && apt-get -y install libpq-dev imagemagick gsfonts nodejs \ && gem install bundler \ && bundle install --without development test --path vendor/bundle \ && rm -rf /usr/local/bundle/cache/*.gem \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* COPY . /app RUN rake assets:precompile CMD ["bundle”, “exec”, “passenger”, “start"] 

容器启动时会重新定义守护程序吗?


AI:是的,没错。 顺便说一句,您可以同时使用CMD和ENTRYPOINT。 要了解有什么区别,这是您的作业。 Habré上有一篇关于该主题的好文章


来吧 您下载了该文件以安装节点,但是不能保证它将具有所需的内容。 有必要添加验证。 例如,像这样:


 RUN curl -sL https://deb.nodesource.com/setup_9.x > setup_9.x \ && echo "958c9a95c4974c918dca773edf6d18b1d1a41434 setup_9.x" | sha1sum -c - \ && bash setup_9.x \ && rm -rf setup_9.x \ && apt-get -y install libpq-dev imagemagick gsfonts nodejs \ && gem install bundler \ && bundle install --without development test --path vendor/bundle \ && rm -rf /usr/local/bundle/cache/*.gem \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* 

使用校验和,可以验证是否下载了正确的文件。


P:但是,如果文件更改,则程序集将无法工作。


II:是的,奇怪的是,这也是一个优点。 您会发现该文件已更改,并且您可以在那里看到更改的内容。 他们说,您永远不会知道,脚本会删除到达的所有内容或执行后门操作。


警:谢谢。 事实证明,最终的Dockerfile如下所示:


 FROM ruby:2.5.5-stretch WORKDIR /app COPY Gemfile* /app RUN curl -sL https://deb.nodesource.com/setup_9.x > setup_9.x \ && echo "958c9a95c4974c918dca773edf6d18b1d1a41434 setup_9.x" | sha1sum -c - \ && bash setup_9.x \ && rm -rf setup_9.x \ && apt-get -y install libpq-dev imagemagick gsfonts nodejs \ && gem install bundler \ && bundle install --without development test --path vendor/bundle \ && rm -rf /usr/local/bundle/cache/*.gem \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* COPY . /app RUN rake assets:precompile CMD ["bundle”, “exec”, “passenger”, “start"] 

警:伊戈尔·伊万诺维奇,谢谢你的帮助。 现在是我跑步的时候了,今天我需要再进行10次提交。


伊戈尔·伊万诺维奇(Igor Ivanovich)望着他匆匆忙忙的同事,,了一口浓咖啡。 在思考了99.9%的SLA和无错误的代码几秒钟后,他提出了一个问题。


AI:您将日志保存在哪里?


警:当然是在production.log中。 顺便说一句,是的,我们如何在没有ssh的情况下访问它们?


II:如果将它们保留在文件中,则已经为您发明了一种解决方案。 docker exec命令允许您执行容器中的任何命令。 例如,您可以将cat用作日志。 并使用-it开关并运行bash(如果已将其安装在容器中),您将获得对容器的交互式访问。


但是您不应该将日志存储在文件中。 至少,这导致了容器的不受控制的增长,但是没有人旋转原木。 所有日志都需要扔到stdout中。 在这里您已经可以使用docker logs命令查看它们。


警:伊戈尔·伊万诺维奇(Igor Ivanovich),但是可以将日志作为用户数据放入物理节点上的挂载目录中吗?


AI:最好不要忘记删除下载到节点磁盘的数据。 使用日志,这也是可能的,只记得设置好轮换即可。
所有您可以运行。


警:伊戈尔·伊万诺维奇(Igor Ivanovich),但建议您阅读些什么?


AI:首先,请阅读Docker开发人员建议 ,几乎没有人比他们更了解Docker。


如果您想练习,请集中精力 。 毕竟,没有实践的理论就死了。

Source: https://habr.com/ru/post/zh-CN452108/


All Articles