0%

在Cloud Build中加速npm构建

Cloud Build不像Github Action那样支持node缓存。谷歌提供了几种编译加速的方法,这篇使用了Cloud Storage来储存node_modules文件夹来实现编译加速。

Cloud Build在俺写的简单评价下几家云服务平台准备博客搬家有介绍。

实现

Cloud Build不支持一个环境跑多个命令,解决的方法有几种:

  1. 利用Bash进行命令组合,在简单评价下几家云服务平台中有例子。
  2. 利用Dockerfile的多阶段编译,在准备博客搬家中演示过。
  3. 多加几个编译步骤(steps)。代码仓库会被挂载到/workspace文件夹中,而这个文件夹在编译的各个阶段是持久的

之前编译用的是方法2,但在实现缓存node_modules文件夹遇到了麻烦,那就是Dockerfile的COPY是单向的,只能从主机拷贝到容器,而不能从容器拷贝到主机。为了顺利实现缓存,抛弃Docker的多阶段编译而改为增加Cloud Build的编译步骤。

cloudbuild.yaml

下面是一个React项目的Cloudbuild.yaml。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# cloudbuild.yaml
# 替换变量,根据自己的项目改
# version,会打到镜像的tag上
# name,是镜像和Cloud Run的名称
# build_dir,是代码仓库的子路径。如果在代码仓库的根路径编译用'.'
substitutions:
_VERSION: '1.0.0'
_NAME: 'artifact_name'
_BUILD_DIR: 'project_location'
steps:
# 以分号;结束的语句失败也没事,只要不是最后一条命令,会继续运行下一句。如果找不到压缩包返回true,继续构建。
# Cloud Storage的路径根据自己的改
- id: 'get cache'
name: gcr.io/cloud-builders/gsutil
dir: ${_BUILD_DIR}
entrypoint: 'bash'
args:
- -c
- |
gsutil cp gs://your_back_name/some_location/node_modules.tar.gz . \
; tar zxf node_modules.tar.gz || true
# 编译完之后会把node_modules文件夹打包压缩
# 拷贝build, dockerfile和nginx conf到一个新建的文件夹,方便后面生成镜像
- id: "build react"
name: 'node:16'
dir: ${_BUILD_DIR}
entrypoint: 'bash'
args:
- -c
- |
ls && npm install && npm run build \
&& mkdir docker && cp -r build docker && cp Dockerfile default.conf docker \
&& tar zcf node_modules.tar.gz node_modules/
# 把压缩的缓存拷贝到Cloud Storage中
- id: 'put cache'
name: gcr.io/cloud-builders/gsutil
dir: ${_BUILD_DIR}
entrypoint: 'bash'
args:
- -c
- |
gsutil cp node_modules.tar.gz gs://your_bucket_name/some_location/
# 在新建的文件夹中构建Docker镜像
- id: 'build docker image'
name: 'gcr.io/cloud-builders/docker'
dir: ${_BUILD_DIR}/docker
entrypoint: 'bash'
args:
- -c
- |
ls \
&& docker build -t us-docker.pkg.dev/your_project/some_location/${_NAME}:${_VERSION} .
# 推送镜像到Artifact Registry
# 路径根据自己项目改
- id: 'push docker image'
name: 'gcr.io/cloud-builders/docker'
args: ['push', 'us-docker.pkg.dev/your_project/some_location/${_NAME}:${_VERSION}']
# 部署项目到Cloud Run(可选)
- id: 'deploy image to cloud run'
name: 'gcr.io/google.com/cloudsdktool/cloud-sdk'
entrypoint: gcloud
args: ['run', 'deploy', '${_NAME}', '--image',
'us-docker.pkg.dev/your_project/some_location/${_NAME}:${_VERSION}', '--region', 'us-central1']
images:
- us-docker.pkg.dev/your_project/some_location/${_NAME}:${_VERSION}

Dockerfile和Nginx conf

Dockerfile就很简单了,就把build文件夹下的文件拷贝到html文件夹下就好了。

如果用80端口则可以注释掉第二个COPY。我用的8080端口,用其他端口要把下面4个8080都要改掉。

1
2
3
4
5
6
7
# Dockerfile
FROM nginx:stable-alpine
ENV PORT=8080
COPY build /usr/share/nginx/html
COPY default.conf /etc/nginx/conf.d/default.conf
EXPOSE 8080
CMD ["nginx", "-g", "daemon off;"]

Nginx的配置文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# default.conf
# 这里用的8080端口,要用其他改掉下面的两个8080,一个V4一个V6。
server {
listen 8080;
listen [::]:8080;
server_name localhost;

#access_log /var/log/nginx/host.access.log main;

location / {
root /usr/share/nginx/html;
index index.html index.htm;
}

#error_page 404 /404.html;

# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}

# proxy the PHP scripts to Apache listening on 127.0.0.1:80
#
#location ~ \.php$ {
# proxy_pass http://127.0.0.1;
#}

# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
#location ~ \.php$ {
# root html;
# fastcgi_pass 127.0.0.1:9000;
# fastcgi_index index.php;
# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
# include fastcgi_params;
#}

# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
# deny all;
#}
}

额外提醒

Cloud Build, Cloud Run和Cloud Storage最好全部放一个地区,避免产生跨区域的流量费用。

我放的us-central-1,Cloud Storage有5G的免费储存额度。我的两个小项目的node_module.tar.gz的大小在100M左右,不会产生储存费用。

加速效果

测试了两个很小的React项目,只展示npm编译步骤的时间变化。

是否缓存 项目一 项目二
2分16秒 2分34秒
1分11秒 1分29秒

编译步骤大概能节省1分钟,但Cloud Run部署大概需要1-2分钟左右,最后总时间3分多钟。

小结

Cloud Build一天提供120分钟的免费构建,其实不优化也够用了,但优化后还是可以节省一点点时间。