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.

参考链接


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

安装必要的依赖:

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

修改后的完整内容如下:

构建镜像:

设置容器:

查看启动文件:

内容如下:

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

另外,Android源代码目录下不能存在 .svn .git  隐藏子目,我们需要手工删除,否则会报错。参考 删除目录下所有的 .svn .git 隐藏子目

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

Running containers with resource limits fails with a permissions error

On some systemd-based systems, non-root users do not have resource limit delegation permissions. This causes setting resource limits to fail.

Symptom

Running a container with a resource limit options will fail with an error similar to the following:

--cpus--cpu-period--cpu-quota--cpu-shares:

--cpuset-cpus--cpuset-mems:

This means that resource limit delegation is not enabled for the current user.

Solution

You can verify whether resource limit delegation is enabled by running the following command:

Example output might be:

In the above example, cpu and cpuset are not listed, which means the current user does not have permission to set CPU or CPUSET limits.

If you want to enable CPU or CPUSET limit delegation for all users, you can create the file /etc/systemd/system/user@.service.d/delegate.conf with the contents:

After logging out and logging back in, you should have permission to set CPU and CPUSET limits.

参考链接


在Ubuntu 16.04/18.04/20.04 LTS上安装OpenGrok-1.3.11/1.3.16/1.5.12/1.7.25浏览Android源码

更加推荐通过 Podman/Docker 方式进行部署,参考链接 Ubuntu 22.04使用Podman部署OpenGrok的详细教程

OpenGrok 是一个快速,便于使用的源码搜索引擎与对照引擎,它能够帮助我们快速的搜索、定位、对照代码树。接下来就具体讲解 Ubuntu 16.04/18.04/20.04 LTS 环境下 OpenGrok 的安装及使用。

OpenGrok 1.3.11/1.3.16 依赖 Java 1.8 , Tomcat 8

OpenGrok 1.5.12 依赖 Java 11 , Tomcat 9

OpenGrok 1.6.0开始依赖 Java 11 , Tomcat 10

1.依旧参照 UBUNTU 13.10 APACHE 2.2 通过 AJP 整合 TOMCAT 7 中讲述的方法,进行 Tomcat 8/9Apache 2.4的配置安装,只不过路径中的 Tomcat7 目录替换成 Tomcat8 (ubuntu 20.04 默认 Tomcat9)。

2.安装 Tomcat 8 (ubuntu 18.04)

安装 Tomcat 9 (ubuntu 20.04)

3.安装 universal-ctags 用于对 C\C++ 代码的支持

给代码建立索引时,要使用到universal-ctags工具,但是一般通过apt-get安装的都是exuberant-ctags,所以要先删除原有的ctags版本,然后安装universal-ctags.

4.下载并安装OpenGrok

可以到"https://oracle.github.io/opengrok/"手工下载文件,然后上传到服务器,也可以直接用wget命令来下载,一般选择安装在"/opt"目录下面。

解压缩文件到当前目录"/opt"

创建一个软链接,方便后续的修改

链接"/opt/opengrok/lib/source.war"到 Tomcat8 的工程目录"/var/lib/tomcat8/webapps/",比如我们有多个源代码工程,建议进行链接操作。

如下:

访问"http://localhost:8080/source/"确认OpenGrok是否已经安装成功,如果安装成功,出现下面的界面:OpenGrok

5.出于安全原因,禁止外网访问Tomcat的8080端口

只允许Tomcat在本地的8080端口监听即可,修改

添加 address="127.0.0.1"

重启Tomcat8

6.配置Apache2对Tomcat通过AJP进行反向代理

Apache2 的配置文件“ /etc/apache2/sites-enabled/000-default.conf” (如果开启了HTTPS,则需要同步修改 /etc/apache2/sites-enabled/000-default-le-ssl.conf 或者 /etc/apache2/sites-enabled/default-ssl.conf) 中,增加如下配置:

Tomcat8 的配置文件/var/lib/tomcat8/conf/server.xml中增加如下配置<Context path="/Android_4.2.2" docBase="Android_4.2.2/"/>,解决跳转404问题。ProxyPass后面必须携带“/”,否则就会出现404问题。

7.配置OpenGrok对源代码进行解析

设置要建立索引的源代码目录的位置,以存储在"/data/OpenGrok/Android_4.2.2"上的Android代码为例子:

注意:

由于我们使用了Apache2的反向代理才需要设置 OPENGROK_WEBAPP_CONTEXT,如果没有设置反向代理,请不要设置。

OPENGROK_WEBAPP_CONTEXT 内容就是在 Apache2 中设置的 ProxyPassReverse 指定的参数。

在使用反向代理的时候如果不设置OPENGROK_WEBAPP_CONTEXT会导致在点击具体的变量定义的时候,出现404。

不使用反向代理的时候请只设置OPENGROK_INSTANCE_BASE

这个变量的本质功能就是在建立文件索引的时候,在链接头部增加 OPENGROK_WEBAPP_CONTEXT 指定的路径,比如原来的路径是 https://www.mobibrw.com/a.html ,那么指定路径后,就变成了 https://www.mobibrw.com/ Android_4.2.2/a.html

创建源代码索引

执行时间在40分钟左右,执行完成 。(如果通过SSH远程登录,可能会出现中途连接断开的情况,原因为某项操作比较耗时,导致长时间没有数据通信,网络超时断开。 参考 Linux SSH保持连接(解决Broken pipe))生成的索引文件在源代码的"data"目录下面,重建索引的时候需要执行如下操作,才能再次建立索引

注意,请务必删除源代码中的"prebuilts"目录,这个目录下面存储的是一系列的编译工具,在浏览代码的时候,完全用不上,但是占据的磁盘空间确是巨大的。

注意,如果服务器上面的内存比较有限,请使用如下命令进行内存限制,否则建立索引的时候,会触发内存不足的情况:

修改OpenGrok配置文件

修改其中的

为具体的工程目录"/data/OpenGrok/Android_4.2.2",修改后的配置如下:

刷新浏览器,可以看到Android_4.2.2的源码可以搜索出来了。

参考链接


在Ubuntu 12.04 LTS上安装OpenGrok浏览Android源码

OpenGrok是一个快速,便于使用的源码搜索引擎与对照引擎,它能够帮助我们快速的搜索、定位、对照代码树。接下来就具体讲解Ubuntu 12.04 LTS环境下OpenGrok的安装及使用。

1.参照 UBUNTU 13.10 APACHE 2.2 通过 AJP 整合 TOMCAT 7 中讲述的方法,进行Tomcat,Apache2的配置安装。

2.安装ctags用于对C\C++代码的支持

3.下载并安装OpenGrok

可以到"http://opengrok.github.io/OpenGrok/"手工下载文件,然后上传到服务器,也可以直接用wget命令来下载,一般选择安装在"/opt"目录下面。

解压缩文件到当前目录"/opt"

创建一个软链接,方便后续的修改

拷贝"/opt/opengrok/lib/source.war"到Tomcat7的工程目录"/var/lib/tomcat7/webapps/"

访问"http://localhost:8080/source/"确认OpenGrok是否已经安装成功,如果安装成功,出现下面的界面:OpenGrok

4.出于安全原因,禁止外网访问Tomcat的8080端口

只允许Tomcat在本地的8080端口监听即可,修改

添加 address="127.0.0.1"

重启Tomcat7

5.配置Apache2对Tomcat通过AJP进行反向代理

Apache2的配置文件“ /etc/apache2/sites-available/default”中,增加如下配置:

Tomcat7的配置文件/var/lib/tomcat7/conf/server.xml中增加如下配置<Context path="/AndroidXRef" docBase="source/"/>,解决跳转404问题。ProxyPass后面必须携带“/”,否则就会出现404问题。

6.配置OpenGrok对源代码进行解析

设置要建立索引的源代码目录的位置,以存储在"/data/OpenGrok/Android_4.2"上的Android代码为例子:

注意:

由于我们使用了Apache2的反向代理才需要设置 OPENGROK_WEBAPP_CONTEXT,如果没有设置反向代理,请不要设置OPENGROK_WEBAPP_CONTEXT

在使用反向代理的时候如果不设置OPENGROK_WEBAPP_CONTEXT会导致在点击具体的变量定义的时候,出现404.

不使用反向代理的时候请只设置OPENGROK_INSTANCE_BASE

创建源代码索引

执行时间在40分钟左右,执行完成。生成的索引文件在源代码的"data"目录下面,重建索引的时候需要执行如下操作,才能再次建立索引

注意,请务必删除源代码中的"prebuilts"目录,这个目录下面存储的是一系列的编译工具,在浏览代码的时候,完全用不上,但是占据的磁盘空间确是巨大的。

注意,如果服务器上面的内存比较有限,请使用如下命令进行内存限制,否则建立索引的时候,会触发内存不足的情况:

修改OpenGrok配置文件

修改其中的

为具体的工程目录"/data/OpenGrok/Android_4.2",修改后的配置如下:

刷新浏览器,可以看到Android_4.2的源码可以搜索出来了。

参考链接