从 Compute Engine 上的 node.js 应用程序使用 knex 连接到 Cloud SQL

huangapple go评论82阅读模式
英文:

Connect to Cloud SQL from node.js app on Compute Engine using knex

问题

我理解你要求只提供翻译,不包括代码部分。以下是你提供的文本的翻译:

如何从在Compute Engine中运行的node.js应用程序中使用knex连接到Cloud SQL?我尝试使用公共IP,无论是否使用Cloud SQL身份验证代理(尝试在启动脚本中安装和启动代理),但在使用knex连接调用终点时出现错误:KnexTimeoutError:Knex:获取连接超时。池可能已满。您是否缺少.transacting(trx)调用?

这是我的启动脚本:

set -v

# 与元数据服务器通信以获取项目ID
PROJECTID=$(curl -s "http://metadata.google.internal/computeMetadata/v1/project/project-id" -H "Metadata-Flavor: Google")
REPOSITORY="..."
# CMS="..."

# 安装日志监视器。监视器将自动捕获发送到syslog的日志。
curl -s "https://storage.googleapis.com/signals-agents/logging/google-fluentd-install.sh" | bash
service google-fluentd restart &

# 从apt安装依赖项
apt-get update
apt-get install build-essential

apt-get install -yq ca-certificates git supervisor postgresql-client
apt-get install --yes liblcms2-2 liblcms2-dev liblcms2-utils

# 安装nodejs
rm -r /opt/nodejs
mkdir /opt/nodejs
curl https://nodejs.org/dist/v14.0.0/node-v14.0.0-linux-x64.tar.gz | tar xvzf - -C /opt/nodejs --strip-components=1
ln -s -f /opt/nodejs/bin/node /usr/bin/node
ln -s -f /opt/nodejs/bin/npm /usr/bin/npm
# 从Google Cloud存储库获取应用程序源代码。
# 启动脚本期间不设置git所需的$HOME。
export HOME=/root
rm -r /opt/app
# rm -r /cms
git config --global credential.helper gcloud.sh
git clone https://source.developers.google.com/p/${PROJECTID}/r/${REPOSITORY} /opt/app

# 安装应用程序依赖项
cd /opt/app
git checkout development

npm install
npm install bunyan @google-cloud/logging-bunyan 
npm install sharp@0.29.0

# 创建一个nodeapp用户。应用程序将以此用户身份运行。
cd /
cd /home/app
userdel -f app
cd /
rm -r /home/app
cd /opt/app
useradd -m -d /home/app app
chown -R app:app /opt/app
curl -o cloud-sql-proxy https://storage.googleapis.com/cloud-sql-connectors/cloud-sql-proxy/v2.3.0/cloud-sql-proxy.linux.amd64
chmod +x cloud-sql-proxy
export GOOGLE_APPLICATION_CREDENTIALS=/service-account-key/key.json
export INSTANCE_HOST='<public-ip>'
export DB_PORT='5432'
export DB_USER='...'
export DB_PASS='...'
export DB_NAME='...'
./cloud_sql_proxy -instances=dreamlouvre:europe-west1:dream-louvre-sql=tcp:5432 -credential_file=$GOOGLE_APPLICATION_CREDENTIALS &
# 配置supervisor以运行node应用程序。
rm /etc/supervisor/conf.d/node-app.conf
cat >/etc/supervisor/conf.d/node-app.conf <<EOF
[program:app]
directory=/opt/app
command=npm start
autostart=true
autorestart=true
user=app
environment=HOME="/home/app",USER="app",NODE_ENV="development",SQL_CONNECTION_NAME="...",SQL_DB_NAME="...",SQL_USER="...",SQL_PW="...",
stdout_logfile=syslog
stderr_logfile=syslog
EOF
supervisorctl reread
supervisorctl update
# 应用程序现在应在supervisor下运行

这是我的knex实例脚本:

exports.initKnex = () => {

    const configuration = {
        user: config.config.sqlUser, // 例如 'my-user'
        password: config.config.sqlPw, // 例如 'my-user-password'
        database: config.config.sqlDbName, // 例如 'my-database'
    };
    configuration.host = `${config.config.sqlConnectionName}`;
    const knex = Knex({ client: 'pg', connection: configuration });
    knex.client.pool.max = 1;
    knex.client.pool.min = 1;
    knex.client.pool.createTimeoutMillis = 30000; // 30秒
    knex.client.pool.idleTimeoutMillis = 600000; // 10分钟
    knex.client.pool.createRetryIntervalMillis = 200; // 0.2秒
    knex.client.pool.acquireTimeoutMillis = 600000; // 10分钟
    return knex;
} 

这是导致错误的终点:

exports.getCurrency = async (req, res) => {

    let response = {};
    try{
        response = await knex('exchangeRates').where({'countryCode': req.params.cc}).select('currencyCode', 'rate', 'digits');
        //console.log("RESPONSE: ", response);
      }
    catch (err){
        console.log("error: ", err);
        return res.status(500).json(err);
    }
    return res.status(200).json({'data': response});

}
英文:

How do I connect to Cloud SQL from node.js app running in Compute engine using knex? I tried with public ip with and without cloud sql authproxy (tried to install and start proxy in a startup-script), but got error when calling endpoint with knex-connection: KnexTimeoutError: Knex: Timeout acquiring a connection. The pool is probably full. Are you missing a .transacting(trx) call?

Here's my startup script:

set -v

# Talk to the metadata server to get the project id
PROJECTID=$(curl -s &quot;http://metadata.google.internal/computeMetadata/v1/project/project-id&quot; -H &quot;Metadata-Flavor: Google&quot;)
REPOSITORY=&quot;...&quot;
# CMS=&quot;...&quot;

# Install logging monitor. The monitor will automatically pick up logs sent to
# syslog.
curl -s &quot;https://storage.googleapis.com/signals-agents/logging/google-fluentd-install.sh&quot; | bash
service google-fluentd restart &amp;

# Install dependencies from apt
apt-get update
apt-get install build-essential

apt-get install -yq ca-certificates git supervisor postgresql-client
apt-get install --yes liblcms2-2 liblcms2-dev liblcms2-utils

# Install nodejs
rm -r /opt/nodejs
mkdir /opt/nodejs
curl https://nodejs.org/dist/v14.0.0/node-v14.0.0-linux-x64.tar.gz | tar xvzf - -C /opt/nodejs --strip-components=1
ln -s -f /opt/nodejs/bin/node /usr/bin/node
ln -s -f /opt/nodejs/bin/npm /usr/bin/npm
# Get the application source code from the Google Cloud Repository.
# git requires $HOME and it&#39;s not set during the startup script.
export HOME=/root
rm -r /opt/app
# rm -r /cms
git config --global credential.helper gcloud.sh
git clone https://source.developers.google.com/p/${PROJECTID}/r/${REPOSITORY} /opt/app


# Install app dependencies
cd /opt/app
git checkout development

npm install
npm install bunyan @google-cloud/logging-bunyan 
npm install sharp@0.29.0




# Create a nodeapp user. The application will run as this user.
cd /
cd /home/app
userdel -f app
cd /
rm -r /home/app
cd /opt/app
useradd -m -d /home/app app
chown -R app:app /opt/app
curl -o cloud-sql-proxy https://storage.googleapis.com/cloud-sql-connectors/cloud-sql-proxy/v2.3.0/cloud-sql-proxy.linux.amd64
chmod +x cloud-sql-proxy
export GOOGLE_APPLICATION_CREDENTIALS=/service-account-key/key.json
export INSTANCE_HOST=&#39;&lt;public-ip&gt;&#39;
export DB_PORT=&#39;5432&#39;
export DB_USER=’...’
export DB_PASS=’...’
export DB_NAME=’...’
./cloud_sql_proxy -instances=dreamlouvre:europe-west1:dream-louvre-sql=tcp:5432 -credential_file=$GOOGLE_APPLICATION_CREDENTIALS &amp;
# Configure supervisor to run the node app.
rm /etc/supervisor/conf.d/node-app.conf
cat &gt;/etc/supervisor/conf.d/node-app.conf &lt;&lt; EOF
[program:app]
directory=/opt/app
command=npm start
autostart=true
autorestart=true
user=app
environment=HOME=&quot;/home/app&quot;,USER=&quot;app&quot;,NODE_ENV=&quot;development&quot;,SQL_CONNECTION_NAME=&quot;...&quot;,SQL_DB_NAME=&quot;...&quot;,SQL_USER=&quot;...&quot;,SQL_PW=&quot;...&quot;,
stdout_logfile=syslog
stderr_logfile=syslog
EOF
supervisorctl reread
supervisorctl update
# Application should now be running under supervisor

and here's my knex-instance script:

exports.initKnex = () =&gt; {

    const configuration = {
        user: config.config.sqlUser, // e.g. &#39;my-user&#39;
        password: config.config.sqlPw, // e.g. &#39;my-user-password&#39;
        database: config.config.sqlDbName, // e.g. &#39;my-database&#39;
    };
    configuration.host = `${config.config.sqlConnectionName}`;
    const knex = Knex({client: &#39;pg&#39;, connection: configuration});
    knex.client.pool.max = 1;
    knex.client.pool.min = 1;
    knex.client.pool.createTimeoutMillis = 30000; // 30 seconds
    knex.client.pool.idleTimeoutMillis = 600000; // 10 minutes
    knex.client.pool.createRetryIntervalMillis = 200; // 0.2 seconds
    knex.client.pool.acquireTimeoutMillis = 600000; // 10 minutes
    return knex;
} 

And here's the endpoint that causes the error:

exports.getCurrency = async (req, res) =&gt; {

    let response = {};
    try{
        response = await knex(&#39;exchangeRates&#39;).where({&#39;countryCode&#39;: req.params.cc}).select(&#39;currencyCode&#39;, &#39;rate&#39;, &#39;digits&#39;);
        //console.log(&quot;RESPONSE: &quot;, response);
      }
    catch (err){
        console.log(&quot;error: &quot;, err);
        return res.status(500).json(err);
    }
    return res.status(200).json({&#39;data&#39;:response});

}

答案1

得分: 3

我注意到你的配置有几个问题。

首先,你正在下载Cloud SQL代理的v2版本(v2.3.0),但你的CLI调用命令是使用v1语法./cloud_sql_proxy。这是旧的v1方法,而v2现在使用./cloud-sql-proxy。你可以查看迁移指南以了解两个CLI命令和标志之间的区别。

简而言之,你的v2形式的./cloud_sql_proxy命令应该是:

./cloud-sql-proxy --port=5432 --credentials-file=$GOOGLE_APPLICATION_CREDENTIALS dreamlouvre:europe-west1:dream-louvre-sql &

其次,由于你正在使用Cloud SQL代理,你的INSTANCE_HOST变量不应该设置为Cloud SQL实例的IP地址,而应该设置为127.0.0.1以与代理交互。

第三,看起来你的Knex配置缺少指定port字段。它应该类似于以下内容(在此处查看完整代码示例):

const configuration = {
  host: process.env.INSTANCE_HOST, // 例如 '127.0.0.1'
  port: process.env.DB_PORT, // 例如 '5432'
  user: process.env.DB_USER, // 例如 'my-user'
  password: process.env.DB_PASS, // 例如 'my-user-password'
  database: process.env.DB_NAME, // 例如 'my-database'
}

最后,我建议你使用Cloud SQL Node.js连接器而不是Cloud SQL代理。它是代理的直接进程内替代品,提供了所有相同的优势,但以本机npm包的方式提供。

你可以按照以下方式安装它:

npm install @google-cloud/cloud-sql-connector

它还支持Knex,如下所示:

const Knex = require('knex');
const { Connector } = require('@google-cloud/cloud-sql-connector');

// connectWithConnector初始化了一个使用Cloud SQL Node.js连接器的Cloud SQL实例的连接池。
const connectWithConnector = async (config) => {
  const connector = new Connector();
  const clientOpts = await connector.getOptions({
    instanceConnectionName: 'project-id:region:instance-name', // Cloud SQL实例连接名称
    ipType: 'PUBLIC',
  });
  const dbConfig = {
    client: 'pg',
    connection: {
      ...clientOpts,
      user: process.env.DB_USER, // 例如 'my-user'
      password: process.env.DB_PASS, // 例如 'my-user-password'
      database: process.env.DB_NAME, // 例如 'my-database'
    },
    // ... 在此处指定其他属性。
    ...config,
  };
  // 建立与数据库的连接。
  return Knex(dbConfig);
}
英文:

I noticed a couple things with your configuration.

Firstly, you are downloading v2 of the Cloud SQL Proxy (v2.3.0), yet your CLI invocation command is using v1 syntax ./cloud_sql_proxy. This is the old v1 method, while ./cloud-sql-proxy is used now in v2. You can view the migration guide for guidance on the differences between the two CLI commands and flags.

In short your ./cloud_sql_proxy command in v2 form would be:

./cloud-sql-proxy --port=5432 --credentials-file=$GOOGLE_APPLICATION_CREDENTIALS dreamlouvre:europe-west1:dream-louvre-sql &amp;

Secondly, since you are using the Cloud SQL Proxy, your INSTANCE_HOST variable should not be set to your Cloud SQL instance IP address, it should be set to 127.0.0.1 to interact with the Proxy.

Thirdly, it looks as though your Knex configuration is missing a port field being specified. It should look something like this (see full code sample here)

const configuration = {
  host: process.env.INSTANCE_HOST, // e.g. &#39;127.0.0.1&#39;
  port: process.env.DB_PORT, // e.g. &#39;5432&#39;
  user: process.env.DB_USER, // e.g. &#39;my-user&#39;
  password: process.env.DB_PASS, // e.g. &#39;my-user-password&#39;
  database: process.env.DB_NAME, // e.g. &#39;my-database&#39;
}

Finally, my recommendation would be to use the Cloud SQL Node.js Connector instead of the Cloud SQL Proxy. It is a direct in-process replacement for the Proxy, that provides all the same benefits but in a native npm package.

You can install it as follows:

npm install @google-cloud/cloud-sql-connector

It also supports Knex like so:

const Knex = require(&#39;knex&#39;);
const {Connector} = require(&#39;@google-cloud/cloud-sql-connector&#39;);

// connectWithConnector initializes connection pool for a Cloud SQL instance
// of Postgres using the Cloud SQL Node.js Connector.
const connectWithConnector = async config =&gt; {
  const connector = new Connector();
  const clientOpts = await connector.getOptions({
    instanceConnectionName: &#39;project-id:region:instance-name&#39;, // Cloud SQL Instance Connection Name
    ipType: &#39;PUBLIC&#39;,
  });
  const dbConfig = {
    client: &#39;pg&#39;,
    connection: {
      ...clientOpts,
      user: process.env.DB_USER, // e.g. &#39;my-user&#39;
      password: process.env.DB_PASS, // e.g. &#39;my-user-password&#39;
      database: process.env.DB_NAME, // e.g. &#39;my-database&#39;
    },
    // ... Specify additional properties here.
    ...config,
  };
  // Establish a connection to the database.
  return Knex(dbConfig);

huangapple
  • 本文由 发表于 2023年6月16日 05:20:45
  • 转载请务必保留本文链接:https://go.coder-hub.com/76485591.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定