如何部署具有本地 npm 依赖项的 GAE 服务?
How can I deploy a GAE service with a local npm dependency?
我是 Google Cloud 的新手,似乎无法弄清楚如何部署具有同级本地依赖项的 Google App Engine 服务。我有一个这样的项目结构(我的堆栈是 TypeScript、nestJS、React):
-frontend
app.yaml
package.json
-backend
app.yaml
package.json
-common
package.json
dispatch.yaml
frontend
包将静态代码部署到default
服务,backend
包部署到api
服务。 common
包包含一些我想在前端和后端之间共享的代码。他们将其包含在 package.json
文件中,如下所示:
dependencies: {
common: "file:../common"
}
此结构在本地运行良好。问题是当我部署后端时,npm install
失败,因为它找不到 common
包。这是有道理的,因为我知道它只会将 backend
的内容上传到该服务。但是实现这一目标的正确方法是什么?
我想我可以只部署一个包含所有代码的服务,并让我的顶级 package.json
委托 npm start
到后端的 package.json
。但这只有效,因为我的前端是完全静态的,所以我只有一个包需要 npm start
才能被调用。似乎应该有更好的方法来处理这个问题,因为如果我有两个单独的后端,每个后端都需要自己的 npm start
.
,那么这种方法就会失效
我猜我正在以一种根本错误的方式思考这件事的某些方面,但我需要帮助来弄清楚那是什么。
我不熟悉 GAE,但我共享代码的解决方案是将共享代码打包为 NPM 包并将其导入服务器和客户端应用程序
如果您选择不制作共享包 public,您仍然可以直接从 git 存储库安装。请注意,这可能会使您的部署复杂化,因为您必须配置 SSH 密钥以允许克隆您的私有存储库。
这里的事情是你定义了 2 个 App Engine 服务,frontend
和 backend
,它们将被独立打包并安装在不同的实例上,它们永远不会共存于一个单一的实例。所以两者都需要包含通用包。
当您 运行 gcloud app deploy
时,包含该服务的 app.yaml
文件的文件夹被视为根文件夹,树中的文件和文件夹不会被部署,正如你提到的。
我理解,从开发的角度来看,两个服务共享一个公共包是有意义的,因为它避免了重复代码。管理此问题的一种方法是使用 Cloud Build 创建一个构建管道,该管道将处理将此公共代码合并到两个服务中并分别部署它们。例如,像这样:
steps:
- name: ubuntu
id: 'copy-file'
args:
- '-c'
- |
cp ./common/package.json frontend/ && cp ./common/package.json backend/
- name: 'gcr.io/cloud-builders/gcloud'
args: ['app', 'deploy']
dir: 'frontend/'
timeout: '1600s'
waitFor: ['copy-file']
- name: 'gcr.io/cloud-builders/gcloud'
args: ['app', 'deploy']
dir: 'backend/'
timeout: '1600s'
waitFor: ['copy-file']
第一步会将公共包复制到两个目录(您需要更新 package.json
中的公共依赖路径,因为它现在位于同一目录中)。接下来的 2 个步骤将 运行 并行(两个 wait for the first step to finish) and deploy each service separately (note the dir 参数)。
然后您可以通过 运行 在根目录中执行以下命令来部署您的服务:
gcloud builds submit
请注意,这将始终部署这两个服务。
如果您希望能够部署一项服务而不是另一项服务,您可以像这样定义 2 个 cloudbuilds 文件:
cloudbuild-frontend.yaml:
steps:
- name: ubuntu
args:
- '-c'
- |
cp ./common/package.json frontend/
- name: 'gcr.io/cloud-builders/gcloud'
args: ['app', 'deploy']
dir: 'frontend/'
timeout: '1600s'
cloudbuild-backend.yaml:
steps:
- name: ubuntu
args:
- '-c'
- |
cp ./common/package.json backend/
- name: 'gcr.io/cloud-builders/gcloud'
args: ['app', 'deploy']
dir: 'backend/'
timeout: '1600s'
你最终会得到这样一棵树:
-frontend
app.yaml
package.json
-backend
app.yaml
package.json
-common
package.json
cloudbuild-frontend.yaml
cloudbuild-backend.yaml
dispatch.yaml
然后您可以通过 运行ning gcloud builds submit --config=cloudbuild-frontend.yaml
或 gcloud builds submit --config=cloudbuild-backend.yaml
部署一项或多项服务
我在使用 Firebase 时遇到过类似的情况,我有代码想在前端和后端之间共享,而不必在两个地方维护更改。
由于您使用的是 Typescript,因此您可以简单地使用 tsc
,Typescript 编译器,通过在 tsconfig.json
文件。 https://www.typescriptlang.org/docs/handbook/tsconfig-json.html
对于前端,您可以将其输出为 .js
文件,然后将其加载为 <script src='myCommonFile.js>
。在后面,你会 require
或 import
它。
那么诀窍就是编写代码,使其可以在浏览器和节点环境中使用。引用 this SO post 中的代码:
(function(exports){
// Your code goes here. For example:
exports.test = function(){
return 'hello world'
};
})(typeof exports === 'undefined'? this.mymodule = {} : exports);
因此,如果 exports
未定义,您必须在浏览器中,在这种情况下 mymodule
声明在 window
上(即 this
)。或者,如果定义了 exports
,则它处于 node
上下文中,在这种情况下,您可以只使用 var mymodule = require('mymodule')
。在任一环境中,您都可以将其用作 mymodule.test()
。漂亮!
我是 Google Cloud 的新手,似乎无法弄清楚如何部署具有同级本地依赖项的 Google App Engine 服务。我有一个这样的项目结构(我的堆栈是 TypeScript、nestJS、React):
-frontend
app.yaml
package.json
-backend
app.yaml
package.json
-common
package.json
dispatch.yaml
frontend
包将静态代码部署到default
服务,backend
包部署到api
服务。 common
包包含一些我想在前端和后端之间共享的代码。他们将其包含在 package.json
文件中,如下所示:
dependencies: {
common: "file:../common"
}
此结构在本地运行良好。问题是当我部署后端时,npm install
失败,因为它找不到 common
包。这是有道理的,因为我知道它只会将 backend
的内容上传到该服务。但是实现这一目标的正确方法是什么?
我想我可以只部署一个包含所有代码的服务,并让我的顶级 package.json
委托 npm start
到后端的 package.json
。但这只有效,因为我的前端是完全静态的,所以我只有一个包需要 npm start
才能被调用。似乎应该有更好的方法来处理这个问题,因为如果我有两个单独的后端,每个后端都需要自己的 npm start
.
我猜我正在以一种根本错误的方式思考这件事的某些方面,但我需要帮助来弄清楚那是什么。
我不熟悉 GAE,但我共享代码的解决方案是将共享代码打包为 NPM 包并将其导入服务器和客户端应用程序
如果您选择不制作共享包 public,您仍然可以直接从 git 存储库安装。请注意,这可能会使您的部署复杂化,因为您必须配置 SSH 密钥以允许克隆您的私有存储库。
这里的事情是你定义了 2 个 App Engine 服务,frontend
和 backend
,它们将被独立打包并安装在不同的实例上,它们永远不会共存于一个单一的实例。所以两者都需要包含通用包。
当您 运行 gcloud app deploy
时,包含该服务的 app.yaml
文件的文件夹被视为根文件夹,树中的文件和文件夹不会被部署,正如你提到的。
我理解,从开发的角度来看,两个服务共享一个公共包是有意义的,因为它避免了重复代码。管理此问题的一种方法是使用 Cloud Build 创建一个构建管道,该管道将处理将此公共代码合并到两个服务中并分别部署它们。例如,像这样:
steps:
- name: ubuntu
id: 'copy-file'
args:
- '-c'
- |
cp ./common/package.json frontend/ && cp ./common/package.json backend/
- name: 'gcr.io/cloud-builders/gcloud'
args: ['app', 'deploy']
dir: 'frontend/'
timeout: '1600s'
waitFor: ['copy-file']
- name: 'gcr.io/cloud-builders/gcloud'
args: ['app', 'deploy']
dir: 'backend/'
timeout: '1600s'
waitFor: ['copy-file']
第一步会将公共包复制到两个目录(您需要更新 package.json
中的公共依赖路径,因为它现在位于同一目录中)。接下来的 2 个步骤将 运行 并行(两个 wait for the first step to finish) and deploy each service separately (note the dir 参数)。
然后您可以通过 运行 在根目录中执行以下命令来部署您的服务:
gcloud builds submit
请注意,这将始终部署这两个服务。
如果您希望能够部署一项服务而不是另一项服务,您可以像这样定义 2 个 cloudbuilds 文件:
cloudbuild-frontend.yaml:
steps:
- name: ubuntu
args:
- '-c'
- |
cp ./common/package.json frontend/
- name: 'gcr.io/cloud-builders/gcloud'
args: ['app', 'deploy']
dir: 'frontend/'
timeout: '1600s'
cloudbuild-backend.yaml:
steps:
- name: ubuntu
args:
- '-c'
- |
cp ./common/package.json backend/
- name: 'gcr.io/cloud-builders/gcloud'
args: ['app', 'deploy']
dir: 'backend/'
timeout: '1600s'
你最终会得到这样一棵树:
-frontend
app.yaml
package.json
-backend
app.yaml
package.json
-common
package.json
cloudbuild-frontend.yaml
cloudbuild-backend.yaml
dispatch.yaml
然后您可以通过 运行ning gcloud builds submit --config=cloudbuild-frontend.yaml
或 gcloud builds submit --config=cloudbuild-backend.yaml
我在使用 Firebase 时遇到过类似的情况,我有代码想在前端和后端之间共享,而不必在两个地方维护更改。
由于您使用的是 Typescript,因此您可以简单地使用 tsc
,Typescript 编译器,通过在 tsconfig.json
文件。 https://www.typescriptlang.org/docs/handbook/tsconfig-json.html
对于前端,您可以将其输出为 .js
文件,然后将其加载为 <script src='myCommonFile.js>
。在后面,你会 require
或 import
它。
那么诀窍就是编写代码,使其可以在浏览器和节点环境中使用。引用 this SO post 中的代码:
(function(exports){
// Your code goes here. For example:
exports.test = function(){
return 'hello world'
};
})(typeof exports === 'undefined'? this.mymodule = {} : exports);
因此,如果 exports
未定义,您必须在浏览器中,在这种情况下 mymodule
声明在 window
上(即 this
)。或者,如果定义了 exports
,则它处于 node
上下文中,在这种情况下,您可以只使用 var mymodule = require('mymodule')
。在任一环境中,您都可以将其用作 mymodule.test()
。漂亮!