【问题标题】:Cannot connect to Cloud SQL Proxy via Docker - Error: connect ENOENT无法通过 Docker 连接到 Cloud SQL 代理 - 错误:连接 ENOENT
【发布时间】:2021-08-01 10:24:18
【问题描述】:

我似乎无法使用 Docker 容器连接到 CloudSQL。

首先是我的文件路径:https://imgur.com/a/Nmx41o6

Dockerfile.dev:


FROM node:14-slim

WORKDIR /usr/src/app


COPY package*.json ./


RUN npm install


COPY . ./

Dockerfile.sql

RUN mkdir /cloudsql
WORKDIR /usr/src/app


COPY package*.json ./

RUN npm install


COPY ./cloud_sql_proxy ./
COPY ./service_acct.json ./

version: '3.8'
services: 
    cloud-sql-proxy:
        build: 
            context: .
            dockerfile: DockerFile.sql
        volumes: 
            - /cloudsql:/cloudsql
            - /service_acct.json:/app/service_acct.json
        command: ./cloud_sql_proxy -dir=/cloudsql -instances=test-game-199281:us-east1:testgame -credential_file=/app/service_acct.json
      
    app:
        build: 
            context: .
            dockerfile: DockerFile.dev
        env_file: 
            - ./.env
        volumes: 
            # since we copied root into host in dockerfile, we can map the whole directory with app.
             - "./src:/app/src"
        
        ports: 
            - "5000:5001"
        command: sh -c "npm run dev"

我的节点 index.js 文件。我不认为有什么问题,也许我输入了错误的连接字符串格式?据我所知,密码和用户是正确的。

const express = require('express');
const { Pool, Client } = require('pg')
const app = express();
require('dotenv').config({path:'../.env'})

const pool = new Pool({
    user: 'postgres',
    host: '/cloudsql/test-game-199281:us-east1:testgame',

    database: 'TestDB',
    password: '********',
    port: 5432
    
  })

  
app.get('/', (req, res) => {
    pool.connect(function(err, client, done) {
        if (err) {
            console.log("not able to get connection " + err);
            res.status(400).send(err);
            return
        }
        client.query("SELECT * FROM company", [1], (err, result) =>{
            done();
            if (err) {
                console.log(err);
                res.status(400).send(err);
            }
            res.status(200).send(result.rows);
        });
    });

});

我得到的错误:

Hello world listening on port 5001

app_1 | Error: connect ENOENT /cloudsql/test-game-199281:us-east1:testgame

/.s.PGSQL.5432

app_1 | at PipeConnectWrap.afterConnect [as oncomplete] (net.js:1146:16) {

app_1 | errno: -2,

app_1 | code: 'ENOENT',

app_1 | syscall: 'connect',

app_1 | address: '/cloudsql/test-game-199281:us-east1:testgame

/.s.PGSQL.5432'

app_1 | }

已解决:我切换到 TCP。螺丝 unix 插座。太混乱了。

【问题讨论】:

  • 拆开,检查每一块。两个容器是否都在运行,来自两者的日志,检查端口,你可以连接到不是来自应用程序的实例(消除应用程序的问题),任何有用的堆栈驱动程序日志吗? sql 实例是否允许从您连接的任何地方进行连接?服务帐号是否有足够的权限?
  • 我认为您的问题从 /tmp/keys/keyfile.json 开始。除非你创建目录/tmp/keys/,否则我认为挂载会失败。

标签: node.js docker google-cloud-platform google-cloud-sql


【解决方案1】:

在不同的docker之间连接时,您可以尝试通过服务名称cloud-sql-proxy:5432而不是localhost:5432进行连接。

每个 docker 都是一个独立的网络,因此您不能使用 localhost,因为 localhost 将引用 docker 容器自己的本地网络。

【讨论】:

  • 我已经切换到使用 const pool = new Pool({ user: 'postgres', host: '/cloudsql/test-game-199281:us-east1:testgame', database: 'TestDB' ,密码:'FishTofu123',端口:5432 }) 仍然没有运气
  • @darrenz 你能成功吗?
  • @AlanDeep 是的,我切换到 TCP 连接而不是套接字
【解决方案2】:

ENOENT 错误意味着连接器实用程序找不到要连接到数据库的主机。这是一个good answer 进一步解释它。

在您的 docker-compose 文件中,Cloud SQL 代理正在通过 TCP 进行侦听,但您的代码正在尝试通过 Unix 套接字进行连接。您的代码无法连接到主机,因为套接字不存在。

解决方案是将您的代理配置为创建和listen to a Unix Socket。将命令更改为:

/cloud_sql_proxy -instances=INSTANCE_CONNECTION_NAME -dir=/cloudsql -credential_file=/tmp/keys/keyfile.json

无需公开任何端口即可通过 Unix 套接字进行连接。我还建议使用上面链接中的配置对象或pg-pool 指定的配置对象而不是DB URL 来构建池连接,以避免可能出现issue 无法使用connectionString URL 连接到Unix 套接字的情况。

【讨论】:

  • 嗨。我试过 const pool = new Pool({ user: 'postgres', host: '/cloudsql/test-game-199281:us-east1:testgame', database: 'TestDB', password: 'FishTofu123', port: 5432 }) - 仍然是同样的错误。另一条评论说我没有/tmp/keys/keyfile.json,我尝试从根目录(与compose.yml相同的文件夹)将其更改为./google_service_acct.json,因为我将整个内容复制到dockerfile中。还是什么都没有..
  • @darrenZ1002 很高兴看到您解决了问题。太糟糕了,我的解决方案对你不起作用。只是一个友好的建议,请避免在您的问题和 cmets 中包含您的密码。我建议您删除暴露它的 cmets,之后不要忘记更改密码。
【解决方案3】:

您已指示 Cloud SQL Auth 代理使用此标志 -instances=test-game-199281:us-east1:testgame=tcp:0.0.0.0:5432 侦听 0.0.0.0:5432

但是你已经指示你的应用连接到 /cloudsql/<INSTANCE_CONNCECTION_NAME>,这是一个 unix 套接字。

您需要选择一个,并确保您的应用程序和代理之间保持一致。

如果您使用 TCP,则必须将容器中的端口映射到计算机上的端口(或 docker-compose 网络中您的应用可以访问的某个端口)。您必须更新您的应用连接127.0.0.1(或网络中的任何它的docker IP)。您可以在 docker-compose 网络here 上查看更多信息。

如果您使用 Unix 域套接字,则需要批量共享包含该套接字的文件夹,以便两个应用程序都可以访问它。因此,如果它在 /cloudsql 中,您需要在代理容器和应用程序容器之间共享 /cloudsql。您可以在 docker-compose volumes here 上查看更多信息。

Cloud SQL 的管理数据库连接页面has examples of connecting with both TCP and Unix domain sockets

【讨论】:

  • 仍然没有运气,但是在将大部分内容更改为 TCP 连接并将 pg-pool 中的主机指向 docker ip 之后,我确实收到了一个新错误“connect ECONNREFUSED”。这个 reddit 帖子@ 987654324@似乎可以使用tcp和unix成功?idk..
  • 我通过 docker inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' ID-TO-INSPECT 获得了 docker ip
  • 您不能将两者混为一谈:您必须选择其中一个。您的应用程序可以根据它所处的环境进行切换,但它需要能够连接到代理,但设置了代理。此时,最好使用您现在使用的配置更新您的原始问题。
  • 我用最新的尝试再次编辑了它。我可以运行 sudo ./cloud_sql_proxy -dir=/cloudsql -instances=pixel-villains-312919:us-east1:pixelvill -credential_file=./service_acct.json。在我的本地终端上,它可以工作并告诉我“等待新连接”