Skip to main content

Docker Tips

无法访问 Docker Hub(被墙了)

很遗憾,被墙了,2023年6月开始无法访问。

最简单的解决方法是科学上网。

删除所有无用镜像

docker image prune --all --force

添加 --all 命令会提示删除所有没有用到(小于1个容器)的镜像,可以放心删,因为我们启动容器前有个好习惯 docker pull

WARNING! This will remove all images without at least one container associated to them.
Are you sure you want to continue? [y/N]

能大幅减少存储空间的占用,特别是服务器资源吃紧时能缓解燃眉之急。

...
deleted: sha256:da68989491bef192fa61a6c4a1864254171f48d96d53ac51782563b49d50057b

Total reclaimed space: 2.844GB

容器内通过 ip 访问宿主机

首先是通过 docker0 这个网络,找出可访问宿主机的 ip。 为了尽量清晰,命令比较长,有优化的空间:

ifconfig docker0 | grep --only-matching --extended-regexp "\<inet[[:space:]].*[[:space:]][[:space:]]netmask\>" | grep --only-matching --extended-regexp "[0-9]{1,}.[0-9]{1,}.[0-9]{1,}.[0-9]{1,}"

执行结果如下:

root@SERVER:/tmp# ifconfig docker0 | grep --only-matching --extended-regexp "\<inet[[:space:]].*[[:space:]][[:space:]]netmask\>" | grep --only-matching --extended-regexp "[0-9]{1,}.[0-9]{1,}.[0-9]{1,}.[0-9]{1,}"
172.17.0.1

启动容器时用 --env-file .env 从文件读取并传递环境变量

持续集成流水线中可以设置一个名叫 PROD_ENV_FILE 的 File 类型的 CI 变量。

注意如果 $PROD_ENV_FILE 里面有引用其它受保护的 CI 变量时,无法直接通过 --env-file $PROD_ENV_FILE 传给容器。

例如,$PROD_ENV_FILE 里的内容是 PASSWORD=$CI_SECRECT 最终传给容器的是 PASSWORD='[MASKED]',显然不对。还要注意字符串的受保护变量,在输出时会自动在加两边加引号,所以若有 PASSWORD='$CI_SECRECT' 最终会变成 PASSWORD=''变量值'',显然是不符合预期的。

必须先打印成文件 cp $PROD_ENV_FILE .env 然后再 --env-file .env

记得最后要 rm .env,因为这个文件是保存在容器外 docker 所在的宿主机的。

注意 --network host 模式下不支持 --network-alias 所以容器命名 --name 很重要

-p 主机端口:容器内端口

注意第一个是指主机端口。

若有多个端口需要映射,可以重复多次使用该选项。例如 -p 8080:80 -p 8090:8001

--volume 主机绝对路径:容器内绝对路径

注意要用绝对路径。

不打包重要文件至镜像中

以下命令,相当于只在执行 npm ci 时将重要文件临时映射进去使用。

# Download dependencies as a separate step to take advantage of Docker's caching.
# Leverage a cache mount to /root/.npm to speed up subsequent builds.
# Leverage a bind mounts to package.json and package-lock.json to avoid having to copy them into
# into this layer.
RUN --mount=type=bind,source=package.json,target=package.json \
--mount=type=bind,source=package-lock.json,target=package-lock.json \
--mount=type=cache,target=/root/.npm \
npm ci --omit=dev

其实 WORKDIR 可以多次使用

相当于 cd 命令,但是由于是 Dockerfile 支持的原生语法,不需通过 RUN cd 切换目录,因此不会出现在 layer 的命令中。总结,在 Dockerfile 中切换目录要用 WORKDIR。

CMD 的参数要逐个分开写

简单来说,以空格分开的字符串,都要写进 CMD 数组中。

例如,命令 http-server dist/build/h5 --port 8090 要写成 CMD ["http-server", "dist/build/h5", "--port", "8090"]

建议使用 ENTRYPOINT 以支持同时执行多个命令

CMD 只能写一个命令,建议使用 ENTRYPOINT [ "/srv/entrypoint.sh" ] 更灵活。

同时,ENTRYPOINT 能保证命令永远被执行,而 CMD 的命令可能由于 Docker Compose 配置了 CMD 被覆盖。

注意,如果 ENTRYPOINT 为一个字符串值,则 CMD 将被忽略。

CMD 和 ENTRYPOINT 的结合妙用,可参考 Nginx 官方镜像的 Dockerfile。收获良多。

AWS 的官方博客和文档也很有用,Docker 的 ENTRYPOINT 和 CMD 参数探秘

快速进入最近创建的容器

docker exec -it $(docker container ls -q) bash

-q, --quiet Only display container IDs

容器启动时自启动多个服务

使用 Supervisor。

Tips:

  • 子进程的“去守护进程”化:
  • 平时的 service xxx start 启动的都是后台的守护进程,而非前台运行。我们可以去 /etc/init.d/ 里面找到各服务的启动命令作为参考。
  • 能通过 Supervisor 启动的服务的可执行文件一般都在 /usr/sbin/ 目录里面。

RUN 里面的 cd 要用绝对路径

RUN 默认是在 WORKDIR 目录中执行的,如果有需要在 WORKDIR 的子目录做操作,要用绝对路径。

$(pwd) 无效;./ 任何相对路径都无效。

COPY 和 RUN 都严格按顺序执行

若果 RUN 里面要用到非源镜像内的文件,必须要在 COPY 以后再执行。

# [×] No such directory
RUN chmod 777 -R /srv/dist/storage
COPY dist /srv/

# [√] Work
COPY dist /srv/
RUN chmod 777 -R /srv/dist/storage

复制完整目录不能省略目录名

# 错
COPY src/dir /srv/

# 对
COPY src/dir /srv/dir

RUN 语句内的括号要转义

() 需要转义 \(\),防止 yaml 解析报错。