大家都懂,使用 Docker分发应用是非常舒服畅快的事情!通常来说,如果我们在GitHub开源代码,通常也就会选择Docker Hub来构建镜像。当然啦,用GitHub Action,或者自己构建再提交也是一种办法哦。
怎么写Dockerfile,比如不同层级的RUN,正确应用multi stage build,那是另外一个话题啦。今天来说说在为 yyetsweb构建docker image的时候,我踩到的坑……
环境变量
说到环境变量,首先我们要区分一下运行时环境变量和构建时环境变量。
运行时环境变量
我们在Dockerfile中使用 ENV
定义的变量就可以算运行时环境变量啦,比如说这样:
ENV TZ=Asia/Shanghai
这个TZ
变量会被容器中的程序直接读取使用。
构建时环境变量
在构建时会被读取使用的变量,比如说在Docker Hub会有这样的设置:
那么这个变量就是可以在Dockerfile里使用:
RUN echo $REACT_APP_DOMAIN
使用的啦。
我的使用
因为前端的SPA应用要在构建时使用一些环境变量,所以我就在 Docker Hub的设置中加了这几个,但是似乎并没有生效。不知道为什么会这样,考虑到构建时会读取项目根目录下的 .env
文件,那不如应用一下Docker Hub的hooks吧!
Docker Hub的build hooks一共有这么几种:
- hooks/post_checkout
- hooks/pre_build
- hooks/post_build
- hooks/pre_test
- hooks/post_test
- hooks/pre_push (only used when executing a build rule or automated build )
- hooks/post_push (only used when executing a build rule or automated build )
每一个hook都可以包含docker命令和各种bash命令。那对于我的场景来说, 创建一个pre_build
就可以啦
#!/bin/bash cat << EOF >> YYeTsFE/.env SENTRY_ORG=$SENTRY_ORG SENTRY_PROJECT=$SENTRY_PROJECT SENTRY_AUTH_TOKEN=$SENTRY_AUTH_TOKEN REACT_APP_SENTRY_DSN=$REACT_APP_SENTRY_DSN REACT_APP_DOMAIN=$REACT_APP_DOMAIN REACT_APP_GA=$REACT_APP_GA # sourcemap GENERATE_SOURCEMAP=$GENERATE_SOURCEMAP EOF
完美!
submodule
使用git submodule继承其他项目是非常理想的一种开发方式。我们可以控制submodule跟踪到上游的哪一个commit,需要更新时只需要一个 git submodule update --remote
,然后再把hash commit就可以啦。
Docker Hub在build时会自动clone相应的submodule,因此常规情况下来讲,大部分情况下都是可以顺利构建的。只不过有几点需要注意:
- docker hub执行的是shallow copy,因此只有最新的commit和对应的branch会被拉下来,因此如果你的build脚本是依赖了git branch和不同的commit,那会失败的哦。这个时候要用hooks来解决
- private submodule需要一些额外设置,可以参考官网文档来解决
- 在module里执行git相关的命令,很有可能失败的哦(这可能是Docker Hub的bug,也有可能是git版本的问题)。
我要详细的说下第三点🤣
在我的构建脚本中,我用了git rev-parse --short HEAD
来获取submodule的hash,然后把这个hash作为tag推送到sentry上。但是在构建日志中发现了这种错误:
---> Running in 4c09a0ea3725 yarn run v1.22.5 $ (export REACT_APP_SENTRY_RELEASE=$(git rev-parse --short HEAD); react-scripts build && node scripts/sentry.js) [91mfatal: not a git repository: /src/bt57w8kzzcjnpmscaemsqqq/.git/modules/YYeTsFE
然而在本地完全是无法复现的,整个目录树如下,我保证Docker Hub在build时也有同样的结构:
$ tree -L 2 -a |-- .dockerignore |-- .git | |-- HEAD | |-- branches | |-- config | |-- description | |-- hooks | |-- index | |-- info | |-- logs | |-- modules | |-- objects | |-- packed-refs | `-- refs |-- .gitattributes |-- .gitignore |-- .gitmodules |-- .travis.yml |-- Dockerfile |-- Makefile |-- README.md |-- YYeTsFE | |-- .env.example | |-- .git | |-- .gitignore | |-- .sentryclirc | |-- README.md | |-- package.json | |-- public | |-- scripts | |-- src | |-- tsconfig.json | `-- yarn.lock
submodule也有.git,项目根目录也有.git,为什么就会失败呢?进行了无数次尝试,让Docker Hub一晚上疯狂构建😂
后来研究了一下,submodule的.git其实是一个到父目录.git/modules/ModuleName
的引用。
~/PycharmProjects/YYeTsBot/YYeTsFE (60c497b*) [02:06:51] benny$ cat .git gitdir: ../.git/modules/YYeTsFE
然而在 Docker Hub上,这个目录是绝对路径,并且是host的路径🤦♂️:
gitdir: /src/brp47ykf7wlmztspk2mtpl8/.git/modules/YYeTsFE
这不报错就怪了。
解决方法也很简单,在hook中改掉.git
文件,或者在Dockerfile中改掉,考虑到无法定位到问题究竟是出在Docker Hub还是git,我就选择在Dockerfile中搞啦
RUN echo "gitdir: ../.git/modules/YYeTsFE" > .git
就这么一行代码,花了我两三个小时🌚🌚🤬
参考
Docker Hub build hooks: https://docs.docker.com/docker-hub/builds/advanced/
YYeTsBot: https://github.com/tgbot-collection/YYeTsBot
--本评论由Telegram Bot回复~❤️
--本评论由Telegram Bot回复~❤️