如何使Python Docker镜像安全、快速、小巧

news2024/11/24 3:50:28

一、说明

        在微服务领域,拥有安全、高效和紧凑的 Docker 映像对于成功部署至关重要。本博客将探讨有助于构建此类映像的关键因素,包括不以 root 用户身份运行映像的重要性、在构建映像时更新和升级包、在编写 Dockerfile 指令时考虑 Docker 的层架构,以及利用多阶段构建来减小映像大小。本博客的部分内容受到我最近来自Matthijs Brouns的精彩演讲的启发,您可以在此处查看。

        我将使用 CLI 工具潜水来分析图像及其图层。Docker镜像的目的是充当机器学习应用程序的FASTAPI服务器的主机,而Poetry是其依赖项管理器。有关使用诗歌而不是pip/pipenv/pip-tools/conda的动机的更多信息,请阅读此博客和此博客。请注意,对 Docker 和 Docker 文件的基本了解是本文的先决条件。

FROM python:3.11-slim as build

        我正在使用 Python 3.11 的精简版本来最小化容器的大小并使其尽可能轻量级。它的大小为 121 MB。

        完整的Python映像(python:3.11)包括运行应用程序不需要的额外开发工具和文档,使其更大,大小为875 MB,比苗条版本😱大约7倍

        另一种选择是Alpine(python:3.11-alpine),它更小,大小为56.5 MB。但是,它缺少软件包安装程序 pip 和对安装轮子包的支持,这两者都是安装 Pandas 和 Numpy 等应用程序所必需的。若要安装这些应用程序,需要使用 G++ 等编译器包从源文件编译它们,默认情况下也不会在 Alpine 映像上安装这些包。这导致图像尺寸比苗条版本更大(和麻烦得多),所以让我们继续那个。

ENV PIP_DEFAULT_TIMEOUT=100 \
    # Allow statements and log messages to immediately appear
    PYTHONUNBUFFERED=1 \
    # disable a pip version check to reduce run-time & log-spam
    PIP_DISABLE_PIP_VERSION_CHECK=1 \
    # cache is useless in docker image, so disable to reduce image size
    PIP_NO_CACHE_DIR=1

ARG POETRY_VERSION=1.3.2

        Dockerfile 中的 ENV 变量被设置为在 Docker 容器中安装软件包期间优化 pip 和诗歌的行为。此外,我明确定义了要安装的诗歌版本。

二、Docker安全

        让我们继续讨论安全性,因为有几个方面很重要。不建议以 root 用户身份运行 Docker 容器,因为 root 用户可以完全控制主机系统,包括修改或删除文件、启动和停止服务以及访问敏感信息的能力。若要遵循最小特权原则,最好仅使用所需的最低特权运行容器。因此,为了增强安全性,我们为 Docker 容器创建了一个名为 的非 root 用户。appuser

RUN set -ex \
    # Create a non-root user
    && addgroup --system --gid 1001 appgroup \
    && adduser --system --uid 1001 --gid 1001 --no-create-home appuser \

        通过分配特定的用户和组 ID(例如 1001),可以更轻松地管理不同系统之间的用户权限和访问控制,尤其是在 Kubernetes 等平台上运行容器时。

        🤔 那么 的权限与根用户的权限有何不同呢?分配给的权限可能包括对某些文件和目录的读取、写入或执行访问的限制,具体取决于为这些资源设置的默认权限。例如,默认情况下,可能没有对容器中特定子目录的写入访问权限。如果运行 作为的应用程序需要对该目录的写入访问权限,则需要使用以下命令更改该目录的所有权,以授予所需的权限:appuserappuserappuserappuserchownappuser

RUN chown -R appuser:appuser /your-subdirectory

        否则将引发以下错误:

[Errno 13] Permission denied: 'your-subdirectory/filename'

        另一个安全最佳实践是更新和升级 Docker 容器中的包。原因是,在 Docker 环境中,用于创建容器的基础映像通常是特定版本的操作系统的快照。随着时间的推移,可能会发现安全漏洞或其他问题,并发布修补程序以使用 和 解决这些问题。该命令更新包索引(有点像可用软件包及其元数据的数据库),并从包存储库中检索最新的包信息。apt-get updateapt-get upgradeapt-get update

RUN set -ex \
    && apt-get update \
    && apt-get upgrade -y

        现在我们有了有关可用软件包的信息,我们可以使用该命令将容器中当前安装的软件包升级到其最新的可用版本。该标志用于在升级过程中自动对任何提示回答“是”。apt-get upgrade-y

        ⬆ 更新过程导致一些自动安装的包、包缓存文件和包索引文件。在运行应用程序时,不再需要这些文件,因为它们达到了帮助升级包的目的。我们可以使用以下命令安全地删除这些文件,这应该会减小 Docker 映像的大小。

# Clean up
RUN set -ex apt-get autoremove -y \\
    && apt-get clean -y \\
    && rm -rf /var/lib/apt/lists/*

        最后的安全最佳做法是确保 docker 映像中不包含任何机密。为了帮助防止这种情况,您可以将常用机密文件和文件夹添加到 .dockerignore 文件中:

**/.env
**/.aws
**/.ssh

        您还可以使用 Trivy,例如,当您使用“root”作为图像用户时,它会警告您。以下是VSCode扩展的屏幕截图:

Trivy 的 VSCode 扩展

三、应用程序文件

        好了,现在我们已经设置了一些环境变量并提高了 docker 容器的安全性,让我们继续复制实际的应用程序文件并安装依赖项。我们希望将工作目录设置为“/app”。此目录在基本映像中尚不存在,但如果不存在,则会为我们创建它。所有后续指令都将在此位置执行,这使我们的 docker 镜像更有条理和可移植性。WORKDIR

WORKDIR /app
COPY pyproject.toml poetry.lock ./

        该命令用于将文件从主机系统复制到容器文件系统。在本例中,我们正在复制 和 ,这是 Poetry 包管理器的配置文件。COPYpyproject.tomlpoetry.lock

四、潜水

        现在我们已经完成了一些步骤,让我们构建 docker 镜像并深入研究它以检查🕵️ ♀️不同的层:

docker build -t app:latest .
dive app:latest

        👇 我们可以在每一层中看到与前一层相比进行了哪些更改。首先,我们使用命令创建了一个应用程序目录,然后复制两个文件。WORKDIR

        当前映像的大小为 139 MB。它看起来相当不错,但可以进行一些改进以减小其大小。首先要记住的是,系统中后续层删除的文件实际上仍然存在于图像中;它们只是在最后一层无法访问。

        👆因此,分别安装安全更新和删除现在不必要的文件和包的步骤不会节省任何空间。起作用的是将这两个步骤合二为一。

RUN set -ex \
    # Create a non-root user
    && addgroup --system --gid 1001 appgroup \
    && adduser --system --uid 1001 --gid 1001 --no-create-home appuser \
    # Upgrade the package index and install security upgrades
    && apt-get update \
    && apt-get upgrade -y \
    && apt-get autoremove -y \
    && apt-get clean -y \
    && rm -rf /var/lib/apt/lists/*

        这将图像的大小从 18 mb 减小到 总大小 121 mb 🎉 ,这与我们之前的图层完全相同。apt-get update && update-get upgrade -y

五、缓存

好的,让我们继续!安装依赖项后,我们会将我的应用程序需要的一些子目录复制到容器中。接下来是使用 Poetry 安装 python 包:

COPY ./artifacts artifacts
COPY ./api api

RUN pip install "poetry==$POETRY_VERSION" \
    && poetry install --no-root --no-ansi --no-interaction

        🤔 但是这些命令的顺序有意义吗?请记住,每个层都是与其下方层独立的增量。每次更改图层时,它都会更改其后的每个图层。如果上一层与之前完全相同,我们可以只使用缓存的值,而不是重建该步骤。

Step 12/18 : COPY ./api api
 ---> Using cache
 ---> ea3ba41d1a13

        因此,您希望将图层从最不可能更改到最有可能更改进行排序,以缩短构建时间💨。在复制程序的其余部分之前先安装依赖项是有意义的。与应用程序代码相比,更新和更改依赖项的可能性通常要小得多。👇 因此,这是一个更好的指令顺序:

RUN pip install "poetry==$POETRY_VERSION" \
    && poetry install --no-root --no-ansi --no-interaction

COPY ./artifacts artifacts
COPY ./api api

        下一步是公开一个端口供我们的应用程序侦听。 在 Dockerfile 中更多地用于文档📄目的,指示应用程序在容器中侦听哪些端口。EXPOSE

为了实际公开我们的 FASTAPI 服务器,我们运行以下命令:

CMD ["uvicorn", "api.main:app", "--host", "0.0.0.0", "--port", "8000"]

        最后,我们使用命令设置应用程序将运行的用户。这设置为我们之前创建的用户 .USERappuser

让我们再次构建 docker 镜像并检查大小:

        映像现在的总大小为 731 MB。531 mb 来自 Poetry 安装的依赖项,但 Poetry 本身也占用了一些空间。

        💡 尽管 Poetry 在开发阶段对于创建虚拟环境和管理依赖项很有用,但在运行 Docker 映像时不需要这些功能,因为映像提供了自己的隔离环境,并且我们的依赖项已经安装。因此,减小 Docker 映像的大小也可以通过确保 Poetry 包不包含在最终映像中来实现。

六、多阶段构建

        多阶段构建可用于从最终的 Docker 映像中排除 Poetry,因为它可以从单个 Dockerfile 创建多个映像。与直接使用 Poetry 安装依赖项不同,Poetry 还可以在构建阶段将必要的依赖项导出到 needs.txt 文件中。此文件可以复制到最后阶段,并由 pip 用于安装依赖项。

FROM python:3.11-slim as build

. . . 

RUN pip install "poetry==$POETRY_VERSION" \
    && poetry install --no-root --no-ansi --no-interaction \
    && poetry export -f requirements.txt -o requirements.txt


### Final stage
FROM python:3.11-slim as final

WORKDIR /app

COPY --from=build /app/requirements.txt .

RUN pip install -r requirements.txt

        通过在最后阶段排除诗歌,Docker图像的大小减小,如从732 MB减少到538 MB 🎉🍾所示。

七、👇 我们最终得到以下文件:

FROM python:3.11-slim as build

ENV PIP_DEFAULT_TIMEOUT=100 \
    # Allow statements and log messages to immediately appear
    PYTHONUNBUFFERED=1 \
    # disable a pip version check to reduce run-time & log-spam
    PIP_DISABLE_PIP_VERSION_CHECK=1 \
    # cache is useless in docker image, so disable to reduce image size
    PIP_NO_CACHE_DIR=1 \
    POETRY_VERSION=1.3.2

WORKDIR /app
COPY pyproject.toml poetry.lock ./

RUN pip install "poetry==$POETRY_VERSION" \
    && poetry install --no-root --no-ansi --no-interaction \
    && poetry export -f requirements.txt -o requirements.txt


### Final stage
FROM python:3.11-slim as final

WORKDIR /app

COPY --from=build /app/requirements.txt .

RUN set -ex \
    # Create a non-root user
    && addgroup --system --gid 1001 appgroup \
    && adduser --system --uid 1001 --gid 1001 --no-create-home appuser \
    # Upgrade the package index and install security upgrades
    && apt-get update \
    && apt-get upgrade -y \
    # Install dependencies
    && pip install -r requirements.txt \
    # Clean up
    && apt-get autoremove -y \
    && apt-get clean -y \
    && rm -rf /var/lib/apt/lists/*

COPY ./artifacts artifacts
COPY ./api api

EXPOSE 8000

CMD ["uvicorn", "api.main:app", "--host", "0.0.0.0", "--port", "8000"]

# Set the user to run the application
USER appuser

八、总结

        总之,通过在创建 Dockerfile 时遵循这些提示和最佳实践,您可以确保最终映像针对安全性、大小和性能进行了优化。

        确保选择正确的基础映像,了解 Docker 层的不变性,按正确的顺序放置说明,并遵循安全最佳实践。

     

如何使您的 Python Docker 镜像安全、快速和小巧 |作者:比约恩·范·迪克曼 |华帝人工智能 |中等 (medium.com)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/846879.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

嵌入式虚拟仿真实验教学平台之登录注册功能使用

登录注册功能的使用 本文将介绍嵌入式虚拟仿真实验教学平台的账号如何注册以及登录账号。 注册账号 1、首先谷歌或Edge等主浏览器中输入https://app.puliedu.com/网址,然后会跳转到登录页,如下所示: 2、点击上图中框中的新注册账号,跳转…

Java【抽象类和接口】(2)【浅拷贝、深拷贝、object类】

一、Clonable 接口和深拷贝 1.clone接口的使用 注意以下几个点: import javax.jws.soap.SOAPBinding;class Person implements Cloneable{public String name;public int age;public Person(String name, int age) {this.name name;this.age age;}Overridepublic…

无需跟踪管理,数据中心也能实时监测蓄电池状态!

当今数字化时代,数据的存储和管理已成为各行各业的核心要务。数据存储设备,如服务器、网络设备以及计算机系统,不仅仅是信息的仓库,更是组织和企业运营的支柱。 蓄电池作为关键的备份电源,在电力故障发生时起到了至关重…

Hum Brain Mapp:用于功能连接体指纹识别和认知状态解码的高精度机器学习技术

摘要 人脑是一个复杂的网络,由功能和解剖上相互连接的脑区组成。越来越多的研究表明,对脑网络的实证估计可能有助于发现疾病和认知状态的生物标志物。然而,实现这一目标的先决条件是脑网络还必须是个体的可靠标记。在这里,本研究…

CI/CD—K8S 基本理解与部署

1 K8S 是什么 Kubernetes 是一款容器的编排调度工具,来源于 Google 开源的 Brog 系统。Kubernetes简称K8S,是用8代替8个字符 “ubernete” 而成的缩写,用于管理云平台中多个主机上的容器化的应用,Kubernetes 的目标是让部署容器化…

冠达管理:什么叫死亡换手率?

逝世换手率是一个用于衡量公司股份买卖的目标,其核算方式为每年公司股份由于股东逝世而换手的比率。该目标能够用于评估公司股东结构安稳性,以及更广泛的商场安稳性。在这篇文章中,我们将从多个角度来分析逝世换手率。 首先,关于一…

Day 75:通用BP神经网络 (2. 单层实现)

代码: package dl;import java.util.Arrays; import java.util.Random;/*** Ann layer.*/ public class AnnLayer {/*** The number of input.*/int numInput;/*** The number of output.*/int numOutput;/*** The learning rate.*/double learningRate;/*** The m…

web前端之JS

文章目录 介绍一、JS引入到文件1.1 嵌入到HTML文件中1.2 引入本地独立JS文件1.3 引入网络来源文件 二、JS的注释三、JS输出方式四、JS数据类型4.1 判断数据类型 typeof4.2 charAt返回指定位置的字符4.3 concat连接两个字符串4.4 substring从原字符串提取字符串并返回4.4 substr…

java动态生成excel并且需要合并单元格

java动态生成excel并且需要合并单元格 先上图看一下预期效果 集成poi <dependency><groupId>cn.afterturn</groupId><artifactId>easypoi-base</artifactId><version>4.0.0</version> </dependency> <dependency><…

实践分享:小程序事件系统设计

微信小程序官方文档中解释说&#xff1a;事件是用于子组件向父组件传递数据&#xff0c;可以传递任意数据。 小程序开发中的事件是指视图层到逻辑层的通讯方式&#xff0c;主要是可以将用户的行为反馈到逻辑层进行处理。事件可以绑定在组件上&#xff0c;当达到触发事件&#…

java面向对象查缺

匿名对象 匿名对象只能使用一次 public class Test1 {public static void main(String[] args) {PhoneFactory p1 new PhoneFactory();p1.show(new Phone(1000,"black"));} } class PhoneFactory{public void show(Phone p){p.send();} } class Phone{private int…

Nginx代理接口访问返回404

Nginx代理接口访问返回404 一、背景 因为不同业务系统间有接口调用&#xff0c;存在跨域问题&#xff0c;为了解决同源策略&#xff0c;需要将接口通过nginx去转发&#xff0c;但是配置完后通过postman请求一直存在访问404的问题。 访问地址&#xff1a;https://a.test.com/n…

无代码集成明道云与更多应用连接

明道云是一个APaaS平台&#xff0c;可以帮助用户快速搭建个性化企业应用&#xff0c;用户不需要代码开发就能够搭建出用户体验上佳的销售、运营、人事、采购等核心业务应用&#xff0c;打通企业内部数据&#xff0c;也能够通过API和Webhook和其他系统对接。 场景描述&#xff…

Vue实战技巧:从零开始封装全局防抖和节流函数

前言 你是否曾经遇到过用户频繁点击按钮或滚动页面导致反应迟钝的问题&#xff1f;这是因为事件被连续触发&#xff0c;导致性能下降。在本文中&#xff0c;我将为大家介绍 vue 中的防抖和节流策略&#xff0c;并展示如何封装全局的防抖节流函数&#xff0c;以避免频繁触发事件…

酷开系统丨加入酷开会员,体验有趣的云逛荷兰海牙市立博物馆

夏日炎炎&#xff0c;出门是不可能的&#xff0c;还是宅在家里享受空调的吹拂吧&#xff01;如果想足不出户看展览&#xff0c;感受文化的熏陶&#xff0c;那就打开酷开系统&#xff0c;加入酷开会员&#xff0c;在云端逛逛荷兰海牙市立博物馆吧&#xff01; 荷兰海牙市立博物…

格式化后数据恢复,教你3个实用方法!

“格式化后数据还能恢复吗&#xff1f;前几天因为我的电脑中了病毒&#xff0c;我不得不将它进行格式化操作。但是我电脑里有很多比较重要的文件&#xff0c;有什么方法可以帮我恢复电脑中的文件吗&#xff1f;求解答&#xff01;” 格式化是一种比较常见的数据清除方法&#x…

低代码平台:初创公司的理想选择

对于初创公司而言&#xff0c;时间和资源是宝贵的。他们需要快速构建和部署应用程序&#xff0c;以满足业务需求&#xff0c;提高效率&#xff0c;并保持竞争优势。在这个背景下&#xff0c;低代码平台成为了初创公司的一个理想选择。而Zoho Creator作为一款出色的低代码平台&a…

node 报错:tagOffsetsMap[tag] ??= [];...SyntaxError: Unexpected token ,‘??=‘,亲测解决

安装的 node 版本不支持空值赋值运算符(??) 更换合适的 node 版本就行&#xff0c;如图 看看版本对应支持的 更多支持请在 node.green 上查看各种语法支持的版本

计算机视觉与图形学-神经渲染专题-pi-GAN and CIPS-3D

《pi-GAN: Periodic Implicit Generative Adversarial Networks for 3D-Aware Image Synthesis》 摘要 我们见证了3D感知图像合成的快速进展&#xff0c;利用了生成视觉模型和神经渲染的最新进展。然而&#xff0c;现有的方法在两方面存在不足&#xff1a;首先&#xff0c;它们…

ELK 将数据流转换回常规索引

ELK 将数据流转换回常规索引 现象&#xff1a;创建索引模板是打开了数据流&#xff0c;导致不能创建常规索引&#xff0c;并且手动修改、删除索引模板失败 "reason" : "composable template [logs_template] with index patterns [new-pattern*], priority [2…