ARG to Rescue: Reuse Variables in Multistage Dockerfile

概要

在参照使用 Ubuntu 22.04使用Podman部署OpenGrok的详细教程 进行 OpenGrok 部署的时候,其中的 Dockerfile 配置了两个独立的镜像来源。恰好这两个镜像来源都需要配置 PIP 国内镜像。于是思考如何在同一个 Dockerfile 存在两个镜像的场景下,如果共用一个变量。

研究了一阵,发现还是通过 ARG 变量实现上述想法。

具体说明参考下面的完整文章。

Introduction

The Docker ecosystem is rich with tools and best practices that streamline containerization. One of these practices is using multistage builds to create lean, efficient containers. However, as your Dockerfiles grow more complex, managing variables and maintaining readability can become a challenge. Enter the ARG instruction—your key to sharing variables across stages in a multistage Dockerfile. In this blog post, we’ll explore how ARG can simplify your Dockerfiles, enhance reusability, and maintain cleaner code.

Why Use Multistage Builds?

Multistage builds in Docker allow you to use multiple FROM statements in a single Dockerfile, creating separate stages that can be used to build a final image. This method is particularly useful for creating lightweight production images, as you can copy only the necessary artifacts from earlier stages. Here’s a quick example to illustrate:

In this simple example, the build stage compiles a Go application, and the production stage creates a minimal image containing only the compiled binary.

The Challenge: Sharing Variables Across Stages

While multistage builds are powerful, they can introduce a common challenge: variable reuse. Suppose you need to use a specific version of an application or a common path across multiple stages. Without a way to share variables, you might end up duplicating code, leading to maintenance headaches and potential errors.

Here’s where ARG (argument) comes into play. The ARG instruction allows you to define variables that can be used throughout your Dockerfile, even across different stages.

Introducing ARG: The Basics

The ARG instruction defines a variable that users can pass at build time to customize the build process. Unlike environment variables (ENV), which are persisted in the image, ARG variables are only available during the build process and do not become part of the final image.

Let’s start with a basic example:

In this example, BASE_IMAGE is an argument that can be overridden when building the Dockerfile. The default value is alpine:3.12, but you could specify a different base image at build time:

Sharing ARG Variables Across Stages

The real power of ARG comes into play with multistage builds. To share ARG variables between stages, you need to redefine the ARG in each stage. Let’s look at a more advanced example:

In this Dockerfile, we define GO_VERSION as an argument at the top. By repeating ARG GO_VERSION in each stage, we make the argument available for use. Notice how the build stage uses GO_VERSION to specify the Go image, and the production stage echoes the Go version used.

Advanced Usage: Combining ARG with Environment Variables

You might find it useful to combine ARG with ENV to set environment variables conditionally based on build arguments. This can further enhance your Dockerfile’s flexibility.

Following example demonstrates the use of ARG and ENV to have a generic Dockerfile for a Turborepo application:

Key Points on Environment Variables in Multistage Builds
  • Environment Variables (ENV):

    • Are specific to the stage where they are defined.
    • Do not persist across stages.
    • If you need an environment variable in multiple stages, you have to redefine it or pass it via ARG.
  • Build Arguments (ARG):

    • Are defined once and can be passed to any stage by redeclaring them.
    • Provide a way to share configuration details like versions or paths between stages.

Debugging ARG Variables

When working with ARG variables, you might run into issues where arguments aren’t passed correctly or variables aren’t set as expected. Here are some tips to help you debug:

  1. Check Build Logs: Use docker build with the --progress=plain flag to get more detailed logs that can help identify where arguments are being used or missed.

  2. Echo Variables: Add RUN echo statements to print the values of your ARG variables during the build process.

  3. Use Default Values: Define sensible default values for your ARG variables to ensure that your build doesn’t fail if arguments are not provided.

Best Practices for Using ARG

  • Define ARG Variables Early: Place ARG instructions at the top of your Dockerfile to make them accessible in all stages.
  • Use Descriptive Names: Choose meaningful names for your arguments to make the Dockerfile easier to understand and maintain.
  • Avoid Secrets in ARG: Never use ARG to pass sensitive data like passwords or API keys, as they can be exposed in the Docker image history.

Conclusion

Using ARG to share variables across stages in a multistage Dockerfile can significantly improve your Docker builds’ maintainability and flexibility. Whether you’re building lightweight production images or dynamically configuring your builds, ARG provides a powerful tool to streamline and enhance your Dockerfile.

参考链接


搭建Docker Registry私有仓库

一、概述

本文将详细介绍如何在本地搭建一个 Docker 私有 Registry 仓库,并实现镜像的上传和管理。通过私有仓库,可以方便地在本地网络中存储和分发 Docker 镜像,提高开发和部署效率。

二、搭建步骤

(一)下载Docker Registry镜像

1. 命令:

2. 解析:

  • docker pull 是 Docker 的命令,用于从 Docker Hub 下载指定的镜像。
  • registry 是 Docker 官方提供的 Registry 镜像,用于搭建私有仓库。
  • 默认情况下,docker pull 会下载最新版本的镜像(latest 标签)。
(二)启动Docker Registry容器

1. 命令:

2. 解析:

  • -d:表示以守护进程模式运行容器(后台运行)。
  • -p 5000:6000:将容器的 5000 端口映射到宿主机的 6000 端口,用于访问私有仓库。
  • --restart=always:设置容器在退出后自动重启,确保仓库服务始终可用。
  • --name registry:为容器指定名称 registry,便于后续管理。
  • registry:指定使用的镜像名称(即之前下载的 registry 镜像)。

3. 验证容器是否启动成功:

如果看到名为 registry 的容器正在运行,并且端口映射正确,说明启动成功。

(三)配置Docker客户端以信任私有仓库

由于私有仓库默认使用 HTTPS 协议,Docker 客户端需要配置为信任该仓库。

编辑 /etc/docker/daemon.json 文件:

添加以下内容:

insecure-registries:指定不安全的仓库地址,允许 Docker 客户端通过 HTTP 协议访问该地址。

重启 Docker 服务:

重启 Docker 服务后,配置生效。

如果使用的是 Docker Desktop 则参考下图修改:

(四)上传镜像到私有仓库

此部分功能可以使用 Podman 4.9.3 或更高版本替代,较低的版本存在问题

1. 标记镜像:

  • docker image tag:用于为镜像重新标记一个新的名称和标签。
  • nginx:latest:本地已有的镜像名称和标签。
  • 10.10.10.189:6000/docker.io/library/nginx:latest:目标仓库地址和镜像名称。

2. 推送镜像到私有仓库:

  • docker push:将标记后的镜像推送到指定的仓库地址。
  • 如果推送成功,会显示镜像层的上传进度和最终的摘要信息。
(五)验证镜像是否上传成功

通过 API 查看仓库中的镜像:

三、skopeo 简化多架构同步

四、常见问题及解决方法

(一)无法连接到私有仓库

1. 问题描述:

在推送或拉取镜像时,可能会遇到以下错误:

2. 解决方法:

  • 确保 /etc/docker/daemon.json 文件中正确配置了 insecure-registries。
  • 确保 Docker 服务已重启。
  • 确保仓库地址正确,且防火墙允许访问 6000 端口。
(二)网络问题导致无法解析地址

1. 问题描述: 如果尝试访问 http://10.10.10.189:6000 或 https://10.10.10.189:6000/v2/ 时,可能会遇到解析失败的问题。

2. 解决方法:

  • 检查仓库地址是否正确,确保 IP 地址和端口无误。
  • 确保网络连接正常,可以尝试 ping 或 curl 测试连通性。
  • 如果问题仍然存在,可能是网络配置或防火墙限制,建议检查网络设置或联系网络管理员。
(三)是否可以使用 Podman 替代

目前测试 push 镜像功能是有问题的,在推送的时候会报错,其他功能都是正常的。

如下:

使用 docker 执行上述命令不会报错。

参考链接


更新OpenWRT系统中的所有软件包

在OpenWRT设备上,可以通过以下两种命令来更新系统所有软件包:

仅更新LuCI相关的软件包(Web管理界面):

更新所有可升级的软件包(包括系统内核和其他组件):

为什么要更新OpenWRT软件包?

保持OpenWRT系统及其软件包的最新版本,不仅能让系统享有最新的功能和性能优化,还能显著提高设备的安全性。每一次更新,通常都包含以下内容:

  • 安全补丁:随着时间的推移,可能会发现系统中的安全漏洞。更新能够及时修复这些漏洞,避免设备受到攻击。

  • 功能改进:新的软件包版本往往带来新功能,或改进现有功能,让系统更加易用和强大。

  • 性能优化:定期更新不仅能修复Bug,还能提升系统的性能,使网络设备运行更加流畅。

  • 兼容性和稳定性:更新可能包含对硬件和软件兼容性的提升,减少运行中出现的问题,保持系统的长期稳定性。

    更新命令的使用方法:

    本文将从最基本的更新软件包源开始,接着详细介绍两种可供选择的升级方式——只更新LuCI相关组件或更新所有软件包。用户可以根据需求选择相应的命令。

第一步:更新软件包源

无论你打算只更新LuCI相关的组件,还是更新整个系统,首先都需要刷新软件包源列表。这一步可以确保系统从最新的软件包存储库中获取信息,并知道哪些软件包有更新可用。

详细解释:

opkg update:这个命令不会直接升级软件包,而是更新系统的软件包源列表。它会连接到OpenWRT的软件包存储库,获取最新的包信息,包括版本号、依赖关系等。执行完这个命令后,设备将知道哪些软件包可以升级,从而为后续的升级操作做好准备。

选择1:仅更新LuCI相关的软件包

如果你只想更新LuCI Web管理界面和相关插件,而不想影响其他系统组件,可以使用以下命令:

详细解释:

  • opkg list-upgradable:列出当前系统中所有可以升级的软件包及其版本信息。输出内容通常会包括包名、当前安装版本和最新可用版本。

    例如:

    这表示 luci-app-firewall 和 luci-base 都有新版本可以升级。

  • grep luci-:筛选出所有以 “luci-” 开头的软件包。LuCI相关的所有软件包名称通常都以“luci-”开头,如 luci-base、luci-app-firewall 等。因此,这个命令确保只筛选和更新LuCI相关的软件包。

  • cut -f 1 -d ' ':将筛选结果中的包名提取出来,忽略版本信息。输出的内容会类似于:

  • xargs opkg upgrade:xargs 是用于将前面筛选出的包名传递给 opkg upgrade 命令的工具。它将每个包名作为参数传递给 opkg upgrade,然后逐个升级这些包。

    使用场景:

    如果你只关心Web界面和相关管理功能的升级,而其他系统组件都不需要更改,可以选择这种方法。这可以减少升级的范围,降低更新过程中出现问题的风险。

    选择2:更新所有可升级的软件包

    如果你希望更新系统中所有的软件包,包括内核、应用程序、以及网络相关工具,可以使用以下命令:

    详细解释:

    • opkg list-upgradable:与前面一样,用于列出所有可以升级的软件包。它会显示每个软件包的名称及其版本更新信息。

    • cut -f 1 -d ' ':这条命令将所有软件包名称提取出来,去掉版本信息。和前面的命令类似,它将结果简化为只有包名。

    • xargs opkg upgrade:这一步是将所有提取出的包名传递给 opkg upgrade 命令,然后逐一升级这些包。这是一次性更新系统中所有软件包的命令,确保系统中的每一个组件都保持最新。

    使用场景:

    当你希望确保系统中的所有软件包都保持最新,或者希望全面更新系统时,这条命令非常适合。它不仅会更新LuCI,还会更新内核和所有安装的应用程序、驱动程序等。

    更新所有软件包的风险与注意事项

    在执行更新前,请注意以下几点:

  • 内核更新的风险:更新所有软件包时,可能会涉及内核更新。内核更新可能会提升性能或修复安全问题,但同时也可能导致设备重启,或某些依赖于旧内核的功能失效。如果你使用了第三方驱动或定制的内核模块,更新内核可能会导致系统不稳定。

  • 存储空间不足:OpenWRT设备的存储和内存往往有限,更新大量软件包时,可能会耗尽可用的存储空间。请务必提前检查设备的存储空间,以避免更新过程中出现失败,或导致系统崩溃。可以使用以下命令检查剩余存储空间:

  • 备份系统配置:在执行大规模更新之前,务必备份你的系统配置文件。这可以通过LuCI Web界面或者命令行来完成。如果更新过程中出现问题,备份将是恢复系统的关键。

  • 逐步更新的建议:如果设备正在正常运行,建议分阶段进行更新。例如,可以先更新LuCI Web界面,观察系统的稳定性,再逐步更新其他软件包,避免一次性大规模更新可能导致的系统问题。

备份与恢复

为了确保在更新过程中万一出现问题时能够快速恢复系统,建议在更新前进行配置备份。可以使用以下两种方式备份:

  • 通过LuCI界面:登录LuCI Web管理界面,导航到“系统 > 备份/升级”页面,点击“生成备份”按钮,下载配置文件到本地。

  • 通过命令行备份:

    然后可以通过SCP或FTP下载该备份文件到本地电脑。

总结

通过上述命令,OpenWRT用户可以轻松更新系统中的所有软件包。你可以选择仅更新LuCI Web界面,或者全面升级所有系统组件(包括内核)。在执行更新前,务必确保做好配置备份并确认设备有足够的存储空间,这将帮助你保持系统的安全性、功能完备性以及长期稳定性。

参考链接


更新OpenWRT系统中的所有软件包

米家石墨烯智能电暖器电源开关维修

天气渐凉,翻出以前购买的小米 “米家石墨烯智能电暖器”,如下图:

通电测试功能,结果发现开关只能处于导通状态,无法关闭。关机需要直接拔掉插头,安全性没有保障。以前曾经闻到过烧糊的气味,但是功能正常,于是就没有太在意。

今天拆开发现开关的 1号管脚 烧糊了,如下图:

开关烧糊的另一面,如下图:

出问题的原因是 1号管脚 的接头没有卡紧,导致虚接发热,开关过热损坏,绝缘热缩管也都烤糊了,部分导线需要更换。

这部分的问题还是蛮严重的,可能诱发着火问题。小米的品控持续堪忧。

解决方法比较简单,更换一个新的开关模块,并且需要清理烧糊的插头,重新压线。不要复用原来的插头,原来的插头已经受热氧化,即使勉强能用也会由于电阻升高,依旧诱发接头过热。

开关的原始安装位置,开关的灯是向上的,后面安装的时候参考下图即可:

淘宝搜索 “HS9 智米小米取暖器二挡电源开关艾美特电暖器开关大电流船型开关”/ “智米小米1S取暖器开关”/“HUACONN 3脚2挡”,可以找到替代开关。参考商家链接 东莞蓝浩电子

至于已经烧的发黑的连接线,可以在淘宝搜索  “6.3 双头 高温编织线” ,线号选择 1.5平编织线,购买合适颜色的,我这边是白色的,原来的线接头是旗形的,其实内部安装空间足够,非旗型也是可以的。 参考商家链接 东莞市悦泰电子

其他符合如下尺寸的开关也可以进行替换安装:

注意,拆螺丝的时候,有一颗三角形的特殊螺丝,可以使用 2.3MM 的螺丝刀/批头拆卸。恰好家里以前买过一套 “米家精修螺丝刀套装” ,里面的三角批头刚刚合适。

套装如下图: 

没有这个套装的话,可以淘宝搜索 “2.3mm三角螺丝刀”,购买一把即可。

chrome浏览器如何跟踪新开标签的网络请求?

调试前端页面的时候,有时候会遇到点击弹窗后从另外一个标签页打开页面的情况,这个时候如果手工去打开调试工具,页面加载之前的网络接口调用就追踪不到了,这个问题查询了一下,从 Chrome 50 版本开始支持页面跳转调试器自动跟踪。只是默认情况下,此功能没有开启,需要手工开启此功能。

具体开启此功能的操作如下:

1. 打开浏览器控制台(F12)/开发者工具

2.使用三点菜单(F1,控制台右上角X号旁边的那个按钮打开 setting 栏)

继续阅读chrome浏览器如何跟踪新开标签的网络请求?

安泰信-二合一维修系统-AT8502D-无法长时间工作

安泰信8502D,热风枪焊台一体的,最近出了个问题:

焊台工作大概20分钟后频烦出现自动停止加热,回到待机状态,接着再开机立马就停,有的时候甚至开不了机,按键无反应!

断电几个小时候后再开机,还能用20分钟或半个小时,然后继续重复以上的故障现象。

继续阅读安泰信-二合一维修系统-AT8502D-无法长时间工作

Armorly A1-70W型车载电动充气洗车一体机更换LET隔膜泵

七年前买车的时候买的电动充气洗车一体机,型号信息(Armorly A1-70W)。没有用几次,时间长了以后,水泵就就坏了,不出水。一直也没丢弃,这些天恰好翻出来,准备修理一下。

拆外壳是非常简单的,只需要拧下背部的八颗螺丝即可。拆水泵只需要拧下额外的四颗螺丝即可。

继续阅读Armorly A1-70W型车载电动充气洗车一体机更换LET隔膜泵

Foxmail 本地邮箱密码破解思路方法分享

本文主要以 POP3 为例讲解, 其他邮件协议可以参考思路, 自行尝试解决。

最近发生了一件比较尴尬的事, 公司邮箱密码忘记了, 又不想麻烦 IT 部门更改, 就想尝试下自己破解下本地的密码。 (密码已经以加密形式保存在本地电脑)

看到网上分享的一些办法, 如下:

  • 破解本地密码文件。(密文通过秘钥(不通版本秘钥有差别), 异或运算计算出的密文密码, 解密就是按照加密规则逆运算回去)
  • 去掉 SSL 访问, 用抓包工具( Wireshark 等)抓取明文数据。

第一种耗时耗力, 版本差异引起方法不通, 还需要破壳工具啥的自己去实际抓抓。

第二种不能用, 公司邮箱服务不允许明文连接, 加密数据不好破解。

所以我用了另一种方式, 下面直接分享步骤和代码, 后面再分享思路

  1. 更改 hosts 文件, 添加如下内容:
  2. 更改 Foxmail 邮箱服务配置, 去掉 SSL

  3. 启动 Python 写的服务程序, 代码如下:

  4. Foxmail 中点击 “收件” , Python 服务打印用户名密码:

    本地的加密用户名就获取到了。

下面说下思路。其实思路也很简单, 就是模拟 POP3 协议, 写个假的 POP3 服务, 然后让 Foxmail 连接这个POP3 服务, 并把用户名和密码发送给我们的 POP3 服务。也是参考抓包提取密码的方法。只是没见过其他人分享过, 自己就分享了下, 其他邮件协议也可以参考下, 不需要把邮件协议完全模拟出来, 只要能够骗过 Foxmail 把用户名密码传过来认证就可以了。

下面大体说下 POP3 协议:

  1. TCP 三次握手, 连接到 POP3 服务
  2. 服务端发送 “+OK...” 信息, 表示服务已经准备好, 等待客户端发送认证信息。( POP3 消息边界符也是 CRLF, 别忘记在消息后面添加)
  3. 客户端 发送 USER <邮箱名> 到 POP3服务
  4. POP3 返回 “+OK” 消息, 等待客户端发送密码认证
  5. 客户端发送 PASS <邮箱密码> 到POP3服务
  6. POP3 返回  “+OK” 消息, 表示认证成功, 就可以等待客户端接下来的操作。
  7. 客户端发送 QUIT 表示断开连接。

基于这个步骤, 我们就可以写个模拟 POP3 协议的服务, “骗取” Foxmail 的本地密码。

当然这种只适合用户忘记本地密码。

参考链接


Foxmail 本地邮箱密码破解思路方法分享

Ubuntu 22.04使用Podman部署Tomcat 10的详细教程

安装必要的依赖:

官方镜像会在报错的时候暴露 Tomcat 10 版本号,错误堆栈,构成安全隐患,我们需要通过构建自定义镜像解决此问题:

内容如下:

构建镜像:

设置容器开机自启:

查看启动文件:

内容如下:

需要额外注意的一个地方是,给出的路径必须是完整路径 “/home/podman/.dockers/tomcat/webapps”,不能是 “~/.dockers/tomcat/webapps”,Systemd不能正确展开 “~” ,导致路径找不到,从而在启动的时候失败,报告错误代码 125

Systemd 配置,开机/系统重启自动启动服务:

后续 WAR 包存储到 ~/.dockers/tomcat/webapps 目录下即可进行正常访问。

参考链接