# 一些使用docker的tips

作为docker重度使用者，有时也会犯一些低级错误，记录一下长期使用下来的一些经验，长期更新

## ADD or COPY

*不只*一位同事问过我这个问题：在Dockerfile里面看到了下面这一句，但是进入容器之后，在/tmp目录并没有发现`buildkit-v0.12.3.darwin-amd64.tar.gz,只看到了解压后的`\`bin\`文件夹，为何没看到解压呢？

```dockerfile
ADD buildkit-v0.12.3.darwin-amd64.tar.gz /tmp
# ls /tmp
# bin
```

这是因为在Dockerfile里面，`ADD不仅有COPY字面上的意义，还可以`

* 下载网络资源
    
* 解压文件
    

不要需要注意的，下载和解压这两件事是或的关系，也就是说

```dockerfile
ADD https://xxx.tar.gz .
```

与

```dockerfile
RUN curl -O https://xxx.tar.gz
```

的效果是一致的。(ps: 千万不要期望\`ADD https://xxx.tar.gz .\` 既能下载又帮你解压)

## COPY/ADD文件/文件夹

当你想要在Dockerfile上COPY文件夹的时候

```dockerfile
FROM busybox:1.35.0-glibc
COPY bin /tmp
# ls /tmp
# buildctl
```

但其它我想要复制的整个文件夹，而不仅仅是将文件夹里面的内容复制到/tmp文件夹下，

```dockerfile
FROM busybox:1.35.0-glibc
COPY bin /tmp/bin
# ls /tmp
# bin
```

之前踩到这个坑，因为会不自觉地把`COPY`当\`mv\`命令来用

# **heredocs**

我是在推上看到这个的，果然要与时俱进哇

```dockerfile
RUN apt-get update -y \
	&& apt-get install -y --no-install-recommends curl libsasl2-2 gsasl libnss-ldapd libssl-dev netcat-traditional procps krb5-user locales \
	&& rm -rf  /var/lib/apt/lists/*
```

虽然我不是一个强迫症患者，但我也是爱整洁的，所以更喜欢下面这种写法

```dockerfile
RUN <<EOF
apt-get update -y 
apt-get install -y --no-install-recommends curl libsasl2-2 gsasl libnss-ldapd libssl-dev netcat-traditional procps krb5-user locales 
rm -rf  /var/lib/apt/lists/*
EOF
```

值得注意的是，因为你使用\`docker buildx build\`而不是docker build, 则需要在Dockerfile的第一行加上以下语句：

```dockerfile
# syntax=docker/dockerfile:1
```

## ARG

### 将ARG赋值给ENV

就在前两天我被自己坑了

```dockerfile
ARG MY_ARG
FROM busybox:1.35.0-glibc
ENV MY_ENV=$MY_ARG
```

\`docker build --build-arg MY\_ARG=test -t argtest .\`

```bash
docker run --rm -it argtest env |grep MY_ENV                
MY_ENV=
```

我第一反应是：竟然是空的，这不对呀，就很奇怪

```dockerfile
FROM busybox:1.35.0-glibc
ARG MY_ARG
ENV MY_ENV=$MY_ARG
```

原来需要调整下ARG的位置

```bash
docker run --rm -it argtest env |grep MY_ENV                
MY_ENV=test
```

只是，没有找到为何这样做以及出处

### 多阶段构建传递ARG

```dockerfile
ARG MY_ARG
FROM busybox:1.35.0-glibc

FROM alpine:3.18
ENV MY_ENV=$MY_ARG
```

没有意外，MY\_ENV是空的

若要将MY\_ARG传到alpine:3.18那一层，显示声明MY\_ARG即可

```dockerfile
ARG MY_ARG
FROM busybox:1.35.0-glibc

FROM alpine:3.18
ARG MY_ARG
ENV MY_ENV=$MY_ARG
```

## 多架构镜像的构建

其实，若不是要支持所谓的“信创”，平时工作中也没机会构建ARM镜像的，但既然有这个机会，借此契机，自然也折腾了一番，除了docker官网，我主要参考了这篇文章[Multi-Architecture Docker Images](https://getk8e.zhubai.love/posts/2191640428633059328)。

唯一需要补充的是关于配置方面，如果你使用的是一个私有的harbor镜像仓库(eg: 私有镜像仓库是：172.16.2.130)，需要在buildkit的配置文件中添加以下配置

```ini
# /etc/buildkitd.toml
debug = true
[registry."172.16.2.130"]
  http = true
  insecure = true
```

还是来上一个实际的使用案例吧

```dockerfile
FROM busybox:1.35.0-glibc
CMD ["uname", "-i"]
```

构建amd64和arm64镜像，并推送到私有harbor镜像仓库

```bash
docker buildx build --platform linux/amd64,linux/arm64 \
    -t 172.16.2.130/capops/multiarch:v0.0.1 . --push
```

使用这种方法的好处的: 同一份Dockerfile，构建出来的不同架构的镜像，共用同一个镜像tag，例如以上命令，在amd64的机器上执行docker pull 172.16.2.130/capops/multiarch:v0.0.1 获取的是amd64格式的镜像，如下所示

```bash
$ docker inspect --format='{{.Os}}/{{.Architecture}}' 172.16.2.130/capops/multiarch:v0.0.1
linux/amd64
```

而在arm64的机器上执行docker pull 172.16.2.130/capops/multiarch:v0.0.1 获取的是arm64格式的镜像，应用层无需要做任何变动。

参考：

* [https://www.baeldung.com/ops/docker-copy-add](https://www.baeldung.com/ops/docker-copy-add)
    
* [https://getk8e.zhubai.love/posts/2191640428633059328](https://getk8e.zhubai.love/posts/2191640428633059328)
