【Docker】Dockerfile使用技巧

开启Buildkit
BuildKit是Docker官方社区推出的下一代镜像构建神器,可以更加快速,有效,安全地构建docker镜像。
尽管目前BuildKit不是Docker的默认构建工具,但是完全可以考虑将其作为Docker(v18.09+)的首选构建工具。
官方文档:https://docs.docker.com/build/buildkit/
下面介绍一下怎么开启BuildKit。
在/etc/docker/daemon.json里添加(如果没有这个文件,则新建), 然后重启docker
{ "features": { "buildkit": true } }
或者在执行docker build命令时设置
$ DOCKER_BUILDKIT=1 docker build .
镜像的选择
可以前往官网学习官方的镜像怎么制作的:https://github.com/docker-library/official-images
基础镜像的选择原则:
- 官方镜像优于非官方的镜像,如果没有官方镜像,则尽量选择Dockerfile开源的。
- 固定版本tag而不是每次都使用latest
- 尽量选择体积小的镜像
下面的镜像列表中,都是jre8的镜像,但是由于基础的镜像不同导致最终镜像大小不同。
$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
eclipse-temurin 8-jre-jammy ec4a8981544b 6 days ago 223MB
eclipse-temurin 8-jre-focal e588bf105eb7 3 weeks ago 227MB
eclipse-temurin 8-jre-alpine f7a454a165ae 3 weeks ago 135MB
eclipse-temurin 8-jre-centos7 e5b6f35176e9 23 months ago 350MB
openjdk 8-jre-alpine f7a292bbb70c 4 years ago 84.9MB
Jammy和Focal:这两个都是Ubuntu的版本代号。"Focal"对应的是Ubuntu 20.04 LTS(长期支持版),"Jammy"对应的则是更加新的Ubuntu版本。Ubuntu是一个非常受欢迎的Linux发行版,因为它既有强大的功能,又有着广大的用户和开发者社区。
Alpine:Alpine Linux是一个面向安全、简单和轻量级的Linux发行版,它的镜像大小通常远小于基于 Ubuntu或其他发行版的镜像。这使得它非常适合于Docker镜像,因为小的镜像可以更快地被拉取和部署。
AdoptOpenJDK停止发布OpenJDK二进制,而Eclipse Temurin是它的延伸,提供更好的稳定性。
减少镜像的分层
例如下面的镜像:
from centos:7label author=morirsenv WORK_DIR /usr/localarg version=6.2.5workdir $WORK_DIRADD redis-${version}.tar.gz .run yum -y update
run yum install -y wget gcc gcc-c++ automake autoconf libtool make
run make -C redis-${version}expose 6379env REDIS_HOME $WORK_DIR/redis-6.2.5env PATH $PATH:$REDIS_HOME/srcentrypoint ["redis-server"]
查询镜像的分层:
$ docker image history redis:6.2.5.1
IMAGE CREATED CREATED BY SIZE COMMENT
444f31557106 57 seconds ago ENTRYPOINT ["redis-server"] 0B buildkit.dockerfile.v0
<missing> 57 seconds ago ENV PATH=/usr/local/sbin:/usr/local/bin:/usr… 0B buildkit.dockerfile.v0
<missing> 57 seconds ago ENV REDIS_HOME=/usr/local/redis-6.2.5 0B buildkit.dockerfile.v0
<missing> 57 seconds ago EXPOSE map[6379/tcp:{}] 0B buildkit.dockerfile.v0
<missing> 57 seconds ago RUN |1 version=6.2.5 /bin/sh -c make -C redi… 122MB buildkit.dockerfile.v0
<missing> About an hour ago RUN |1 version=6.2.5 /bin/sh -c yum install … 302MB buildkit.dockerfile.v0
<missing> About an hour ago RUN |1 version=6.2.5 /bin/sh -c yum -y updat… 358MB buildkit.dockerfile.v0
<missing> About an hour ago ADD redis-6.2.5.tar.gz . # buildkit 10.4MB buildkit.dockerfile.v0
<missing> 2 hours ago WORKDIR /usr/local 0B buildkit.dockerfile.v0
<missing> 2 hours ago ARG version=6.2.5 0B buildkit.dockerfile.v0
<missing> 2 hours ago ENV WORK_DIR=/usr/local 0B buildkit.dockerfile.v0
<missing> 2 hours ago LABEL author=morirs 0B buildkit.dockerfile.v0
<missing> 2 years ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0B
<missing> 2 years ago /bin/sh -c #(nop) LABEL org.label-schema.sc… 0B
<missing> 2 years ago /bin/sh -c #(nop) ADD file:b3ebbe8bd304723d4… 204MB
每一行的RUN命令都会产生一层image layer, 导致镜像的臃肿。
修改为如下:
from centos:7label author=morirsenv WORK_DIR /usr/localarg version=6.2.5workdir $WORK_DIRADD redis-${version}.tar.gz .run yum -y update \&& yum install -y wget gcc gcc-c++ automake autoconf libtool make \&& run make -C redis-${version}expose 6379env REDIS_HOME $WORK_DIR/redis-6.2.5env PATH $PATH:$REDIS_HOME/srcentrypoint ["redis-server"]
新的镜像分层如下:
$ docker history redis:6.2.5.2
IMAGE CREATED CREATED BY SIZE COMMENT
fa6f23e424bc 30 seconds ago ENTRYPOINT ["redis-server"] 0B buildkit.dockerfile.v0
<missing> 30 seconds ago ENV PATH=/usr/local/sbin:/usr/local/bin:/usr… 0B buildkit.dockerfile.v0
<missing> 30 seconds ago ENV REDIS_HOME=/usr/local/redis-6.2.5 0B buildkit.dockerfile.v0
<missing> 30 seconds ago EXPOSE map[6379/tcp:{}] 0B buildkit.dockerfile.v0
<missing> 30 seconds ago RUN |1 version=6.2.5 /bin/sh -c yum -y updat… 593MB buildkit.dockerfile.v0
<missing> 2 hours ago ADD redis-6.2.5.tar.gz . # buildkit 10.4MB buildkit.dockerfile.v0
<missing> 2 hours ago WORKDIR /usr/local 0B buildkit.dockerfile.v0
<missing> 2 hours ago ARG version=6.2.5 0B buildkit.dockerfile.v0
<missing> 2 hours ago ENV WORK_DIR=/usr/local 0B buildkit.dockerfile.v0
<missing> 2 hours ago LABEL author=morirs 0B buildkit.dockerfile.v0
<missing> 2 years ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0B
<missing> 2 years ago /bin/sh -c #(nop) LABEL org.label-schema.sc… 0B
<missing> 2 years ago /bin/sh -c #(nop) ADD file:b3ebbe8bd304723d4… 204MB
镜像的大小变化如下:
$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
redis 6.2.5.2 fa6f23e424bc About a minute ago 807MB
redis 6.2.5.1 444f31557106 15 minutes ago 996MB
合理使用缓存
在构建的时候尽量将不变的结构放在Dockerfile的前面,经常变化的结构放在Dockerfile的后面,这样构建的时候不变的部分层构建过了就无需再次构建,节约时间,可以在构建的日志中看到CACHED。
s$ docker image build -f redis.dockerfile2 -t redis:6.2.5.2 .
[+] Building 268.3s (9/9) FINISHED=> [internal] load build definition from redis.dockerfile2 0.0s=> => transferring dockerfile: 412B 0.0s=> [internal] load .dockerignore 0.0s=> => transferring context: 2B 0.0s=> [internal] load metadata for docker.io/library/centos:7 0.0s=> [1/4] FROM docker.io/library/centos:7 0.0s=> [internal] load build context 0.0s=> => transferring context: 42B 0.0s=> CACHED [2/4] WORKDIR /usr/local 0.0s=> CACHED [3/4] ADD redis-6.2.5.tar.gz . 0.0s=> [4/4] RUN yum -y update && yum install -y wget gcc gcc-c++ automake autoconf libtool make && make -C redis-6.2.5 265.0s=> exporting to image 3.2s=> => exporting layers 3.2s=> => writing image sha256:fa6f23e424bc6088f6cc84d5ff67ae1376f18fbc906e14b670bc3e4c316046e6 0.0s=> => naming to docker.io/library/redis:6.2.5.2 0.0s
合理使用.dockerignore
Docker是client-server架构,理论上Client和Server可以不在一台机器上。
在构建docker镜像的时候,需要把所需要的文件由CLI(client)发给Server,这些文件实际上就是build context。
举例:
$ cat redis6.2.6.dockerfile
from centos:7label author=morirsenv WORK_DIR /usr/localarg version=6.2.6workdir $WORK_DIRCOPY . .run tar -zxvf redis-${version}.tar.gz
run yum -y update
run yum install -y wget gcc gcc-c++ automake autoconf libtool make
run make -C redis-${version}expose 6379env REDIS_HOME $WORK_DIR/redis-6.2.5env PATH $PATH:$REDIS_HOME/srcentrypoint ["redis-server"]
构建目录下的文件:
total 4848
drwxrwxr-x 2 morris morris 4096 Sep 19 19:30 ./
drwxrwxr-x 4 morris morris 4096 Sep 18 10:00 ../
-rw-rw-r-- 1 morris morris 2465302 Jul 22 2021 redis-6.2.5.tar.gz
-rw-rw-r-- 1 morris morris 2476542 Oct 4 2021 redis-6.2.6.tar.gz
-rw-rw-r-- 1 morris morris 356 Sep 18 11:28 redis.dockerfile
-rw-rw-r-- 1 morris morris 360 Sep 18 11:39 redis.dockerfile2
-rw-rw-r-- 1 morris morris 373 Sep 19 19:30 redis6.2.6.dockerfile
构建的时候,第一行输出就是发送build context大小为4.947MB,这里包含了不需要的文件redis-6.2.5.tar.gz:
$ docker image build -f redis6.2.6.dockerfile -t redis:6.2.6.1 .
Sending build context to Docker daemon 4.947MB
Step 1/14 : from centos:7---> eeb6ee3f44bd
编写.dockerignore文件,忽略掉不需要的文件,然后放到docker构建上下文的根路径下。
$ docker image build -f redis6.2.6.dockerfile -t redis:6.2.6.1 .
Sending build context to Docker daemon 2.482MB
Step 1/14 : from centos:7---> eeb6ee3f44bd
再次构建build context大小变为2.482MB。
镜像的多阶段构建
假如有一个C的程序,我们想用Docker去做编译,然后执行可执行文件。
#include <stdio.h>void main(int argc, char *argv[])
{printf("hello %s\n", argv[argc - 1]);
}
构建一个Docker镜像,因为要有C的环境,所以我们选择gcc这个image
FROM gcc:9.4COPY hello.c /src/hello.cWORKDIR /srcRUN gcc --static -o hello hello.cENTRYPOINT [ "/src/hello" ]CMD []
build和测试:
$ docker image build -f c.dockerfile -t hello:1.0 .
... ...$ docker container run --rm -it hello:1.0 hello
hello hello$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
hello 1.0 93b8a1824b80 45 seconds ago 1.14GB
可以看到镜像非常的大,1.14GB
实际上当我们把hello.c编译完以后,并不需要这样一个大的GCC环境,一个小的alpine镜像就可以了。
这时候我们就可以使用多阶段构建了。
FROM gcc:9.4 AS builderCOPY hello.c /src/hello.cWORKDIR /srcRUN gcc --static -o hello hello.cFROM alpine:3.13.5COPY --from=builder /src/hello /src/helloENTRYPOINT [ "/src/hello" ]CMD []
构建和测试:
$ docker image build -f c2.dockerfile -t hello:2.0 .
... ...$ docker container run --rm -it hello:2.0 hi
hello hi$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
hello 2.0 8127f3ac5ea6 4 seconds ago 6.55MB
hello 1.0 93b8a1824b80 45 seconds ago 1.14GB
可以看到这个镜像非常小,只有6.55MB。
尽量使用非root用户
Root的危险性
docker的root权限一直是其遭受诟病的地方,docker的root权限有那么危险么?我们举个例子。
假如我们有一个用户,叫morris,它本身不具有sudo的权限,所以就有很多文件无法进行读写操作,比如/root目录它是无法查看的。
$ ls /root
ls: cannot open directory '/root': Permission denied
但是这个用户有执行docker的权限,也就是它在docker这个group里。
$ groups
morris docker
这时,我们就可以通过Docker做很多越权的事情了,比如,我们可以把这个无法查看的/root目录映射到docker container里,你就可以自由进行查看了。
$ docker container run -it --rm -v /root:/root/tmp centos:7 bash
[root@9cc0d1275a15 /]# ls /root/tmp
snap
更甚至我们可以给我们自己加sudo权限:
$ docker container run -it --rm -v /etc/sudoers:/root/sudoers centos:7 bash
[root@744b21d23ba9 /]# echo "demo ALL=(ALL) ALL" >> /root/sudoers
[root@744b21d23ba9 /]# more /root/sudoers | grep demo
demo ALL=(ALL) ALL
然后退出container,morris用户已经有sudo权限了。
$ ls /etc/sudoers
/etc/sudoers
如何使用非root用户
通过groupadd和useradd创建一个nonroot的组和用户,通过USER指定后面的命令要以nonroot这个用户的身份运行
from centos:7label author=morirsenv WORK_DIR /usr/localarg version=6.2.5workdir $WORK_DIRADD redis-${version}.tar.gz .run yum -y update \&& yum install -y wget gcc gcc-c++ automake autoconf libtool make \&& make -C redis-${version} \&& groupadd -r nonroot \&& useradd -r -g nonroot nonroot \&& chown -R nonroot:nonroot $WORK_DIRUSER nonrootexpose 6379env REDIS_HOME $WORK_DIR/redis-6.2.5env PATH $PATH:$REDIS_HOME/srcentrypoint ["redis-server"]
相关文章:
【Docker】Dockerfile使用技巧
开启Buildkit BuildKit是Docker官方社区推出的下一代镜像构建神器,可以更加快速,有效,安全地构建docker镜像。 尽管目前BuildKit不是Docker的默认构建工具,但是完全可以考虑将其作为Docker(v18.09)的首选…...
一招解决“请在微信客户端中打开链接”
一招解决“请在微信客户端中打开链接”-遇见你与你分享 在浏览器访问网站,却提示“请在微信客户端打开链接”。虽然这个情况你可能从未遇到过,但对于爱折腾的小伙伴,确是一道拦路虎 其实解决办法很简单,就是新建一个UA࿱…...
Python循环语句(一)
目录 一.while循环1.while循环的基础语法2.while循环的嵌套应用3.while循环嵌套案例 一.while循环 1.while循环的基础语法 while循环注意点 while的条件需得到布尔类型,True表示继续循环,False表示结束循环需要设置循环终止的条件,如i 1配…...
期中考核复现
web 1z_php ?0o0[]1A&OoO[]2023a include "flag.php":尝试包含名为 "flag.php" 的文件。这意味着它会尝试引入一个名为 "flag.php" 的脚本文件,其中可能包含一些敏感信息或标志。 error_reporting(0):…...
基于XML的Web服务Java接口(JAX-WS)、Jakarta XML Web Services Eclipse 实现
简介 JAX-WS(Java API for XML-Based Web Services),是创建web服务的Java编程接口,特别是SOAP服务。是Java XML编程接口之一,是Java SE 和Java EE 平台的一部分。 JAX-WS 2.0 规范是代替JAX-RPC 1.0的下一代Web服务AP…...
公网无信号区域远程抄表问题解决方案及产品选型
摘要:随着计量自动化系统的逐步完善,电网全用户表码信息采集成为系统数据得以深化应用的重要基础。利用无线公网通信是目前实现远程抄表的主要手段之一,但仍存在公网难以覆盖的偏远山区、公网信号屏蔽地下室或弱信号区域,无法实现…...
lunar-1.5.jar
公历农历转换包 https://mvnrepository.com/artifact/com.github.heqiao2010/lunar <!-- https://mvnrepository.com/artifact/com.github.heqiao2010/lunar --> <dependency> <groupId>com.github.heqiao2010</groupId> <artifactId>l…...
c++编译使用log4cplus
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、log4cplus是什么?二、使用步骤1.下载源代码2.开始配置1.配置介绍2.开始编译 3.cmake引用4.示例 总结 前言 C很强大,但是仍然有很多…...
zookeeper源码(02)源码编译启动及idea导入
本文介绍一下zookeeper-3.9.0源码下载、编译及本地启动。 下载源码 git clone https://gitee.com/apache/zookeeper.gitcd zookeeper git checkout release-3.9.0 git checkout -b release-3.9.0源码编译 README_packaging.md文件 该文件介绍了编译zookeeper需要的环境和命…...
Github 2FA绑定中国+86手机号码实现两步验证
GitHub宣布,到 2023 年底,所有用户都必须要启用双因素身份验证 (2FA),不能只用密码. GitHub开启2FA后,除了输入密码外,还需要通过一次性密码(OTP)等方式做第二级身份验证,才能成功登…...
windows安装mysql-8.0.35
打开cmd(以管理员身份运行),切换到mysql下的bin目录 mysqld --initialize 执行完毕之后,在data目录下会生成很多文件。 打开cmd(以管理员身份运行),切换到mysql下的bin目录,如果刚才的cmd没有关闭,可以继续mysqld -…...
最详细STM32,cubeMX串口发送,接收数据
这篇文章将详细介绍 串口 发送数据,接受数据。 文章目录 前言一、串口的基础知识二、cubeMX 配置三、自动生成代码解析四、串口发送数据函数五、使用串口收发数据点亮 led重定向函数: 总结 前言 实验开发板:STM32F103C8T6。所需软件…...
Kafka入门04——原理分析
目录 01理解Topic和Partition Topic(主题) Partition(分区) 02理解消息分发 消息发送到分区 消费者订阅和消费指定分区 总结 03再均衡(rebalance) 再均衡的触发 分区分配策略 RangeAssignor(范围分区) RoundRobinAssignor(轮询分区) StickyAssignor(粘性分区) Re…...
k8s-----17、集群安全机制
1、集群安全机制概述 1.1 访问k8s的三个步骤 1、认证 2、鉴权(授权) 3、准入控制 进行访问的时候,过程中都需要经过apiserver,apiserver做统一协调,比如门卫。且访问过程中需要证书、token、或者用户名密码。如果需要访问pod,…...
蓝桥算法赛(铺地板)
问题描述 小蓝家要装修了,小蓝爸爸买来了很多块(你可以理解为数量无限) 23 规格的地砖,小蓝家的地板是 nm 规格的,小蓝想问你,能否用这些 23 的地砖铺满地板。 铺满地板:对于地板的每个区域&…...
浅谈AcrelEMS-GYM文体建筑能效管理解决方案-安科瑞 蒋静
1 概述 AcrelEMS-CA 文体建筑能效管理聚焦建筑的能量和信息的流向搭建平台解决方案。该系统解决方案集变电站综合自动化、电力监控、电能质量分析及治理、电气安全、能耗分析、照明控制、设备运维于一体。打破子系统孤立,配置方便,运维便捷;…...
在LayerUI中使用onChange事件监听复选框的值变化
在LayerUI中,你可以使用onChange事件监听复选框的值变化。当复选框的状态发生变化时,onChange事件会被触发。 以下是一个示例代码,演示了如何使用onChange事件监听复选框的值变化: jsx import React from react; import { Chec…...
决策树--ID3算法
决策树–ID3算法 概念 (1)信息熵 E n t r o p y ( x ) − ∑ i N c l a s s P ( x i ) l o g 2 P ( x i ) Entropy(x) -\sum_{i}^{N_{class}}P(x_i)log_2 P(x_i) Entropy(x)−i∑NclassP(xi)log2P(xi) 假设只有2个类别(N2&…...
js延时加载有哪些方式
...
VSCode运行python提示No module name ‘xxx‘
在进行from * import *导入操作时,编辑器能够解析到module, 但是在编辑器中运行时确提示。 No module name xxx 而且单独运行该文件,或在其他编辑器、或terminal中python file运行,都能正常导入module. 解决方案: 在vscode的用…...
【根据当天日期输出明天的日期(需对闰年做判定)。】2022-5-15
缘由根据当天日期输出明天的日期(需对闰年做判定)。日期类型结构体如下: struct data{ int year; int month; int day;};-编程语言-CSDN问答 struct mdata{ int year; int month; int day; }mdata; int 天数(int year, int month) {switch (month){case 1: case 3:…...
基于距离变化能量开销动态调整的WSN低功耗拓扑控制开销算法matlab仿真
目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.算法仿真参数 5.算法理论概述 6.参考文献 7.完整程序 1.程序功能描述 通过动态调整节点通信的能量开销,平衡网络负载,延长WSN生命周期。具体通过建立基于距离的能量消耗模型&am…...
CMake基础:构建流程详解
目录 1.CMake构建过程的基本流程 2.CMake构建的具体步骤 2.1.创建构建目录 2.2.使用 CMake 生成构建文件 2.3.编译和构建 2.4.清理构建文件 2.5.重新配置和构建 3.跨平台构建示例 4.工具链与交叉编译 5.CMake构建后的项目结构解析 5.1.CMake构建后的目录结构 5.2.构…...
汇编常见指令
汇编常见指令 一、数据传送指令 指令功能示例说明MOV数据传送MOV EAX, 10将立即数 10 送入 EAXMOV [EBX], EAX将 EAX 值存入 EBX 指向的内存LEA加载有效地址LEA EAX, [EBX4]将 EBX4 的地址存入 EAX(不访问内存)XCHG交换数据XCHG EAX, EBX交换 EAX 和 EB…...
成都鼎讯硬核科技!雷达目标与干扰模拟器,以卓越性能制胜电磁频谱战
在现代战争中,电磁频谱已成为继陆、海、空、天之后的 “第五维战场”,雷达作为电磁频谱领域的关键装备,其干扰与抗干扰能力的较量,直接影响着战争的胜负走向。由成都鼎讯科技匠心打造的雷达目标与干扰模拟器,凭借数字射…...
深入解析C++中的extern关键字:跨文件共享变量与函数的终极指南
🚀 C extern 关键字深度解析:跨文件编程的终极指南 📅 更新时间:2025年6月5日 🏷️ 标签:C | extern关键字 | 多文件编程 | 链接与声明 | 现代C 文章目录 前言🔥一、extern 是什么?&…...
什么是Ansible Jinja2
理解 Ansible Jinja2 模板 Ansible 是一款功能强大的开源自动化工具,可让您无缝地管理和配置系统。Ansible 的一大亮点是它使用 Jinja2 模板,允许您根据变量数据动态生成文件、配置设置和脚本。本文将向您介绍 Ansible 中的 Jinja2 模板,并通…...
使用 SymPy 进行向量和矩阵的高级操作
在科学计算和工程领域,向量和矩阵操作是解决问题的核心技能之一。Python 的 SymPy 库提供了强大的符号计算功能,能够高效地处理向量和矩阵的各种操作。本文将深入探讨如何使用 SymPy 进行向量和矩阵的创建、合并以及维度拓展等操作,并通过具体…...
高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数
高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数 在软件开发中,单例模式(Singleton Pattern)是一种常见的设计模式,确保一个类仅有一个实例,并提供一个全局访问点。在多线程环境下,实现单例模式时需要注意线程安全问题,以防止多个线程同时创建实例,导致…...
C++.OpenGL (20/64)混合(Blending)
混合(Blending) 透明效果核心原理 #mermaid-svg-SWG0UzVfJms7Sm3e {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-icon{fill:#552222;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-text{fill…...
