使用PostgreSQL,Next.js,Prisma实现全栈应用

422 阅读11分钟

In this article, I'll share my experience building a paginated transaction API that synchronizes blockchain events to a PostgreSQL database using Next.js and Prisma. Here's what we'll cover:

Architecture Overview

Client <-> API Routes <-> TransactionService (将数据存储到db)<-> Blockchain

The system follows a clean architecture pattern:

  • Client Layer:  Frontend interface that displays transaction data
  • API Routes:  Next.js API endpoints handling HTTP requests
  • Service Layer:  TransactionService managing business logic and blockchain interactions
  • Data Layer:  PostgreSQL database with Prisma ORM

Key Components

1. Database Schema

We use Prisma to define our data models:

  • Transaction: Stores transaction details including status and signatures
  • Signature: Records signature information for each transaction
  • SyncStatus: Tracks blockchain synchronization progress,会通过第三方库node-cron设置定时任务

2. Blockchain Event Synchronization

The system maintains synchronization with the blockchain through:

  • Incremental syncing from the last processed block
  • Event filtering and processing
  • Automatic status updates based on signature collection

3. API Implementation

The API supports flexible querying with:

  • Pagination parameters (page, pageSize)
  • Status filtering (CREATED, SIGNED, ACTIVATED)

Deployment

The application is containerized using Docker Compose, which manages:

  • Application container with Next.js
  • PostgreSQL database container
  • Volume persistence for data storage使用volume amount

遇到的bug

数据从区块链上同步之后没有存储到db

从链上同步到数据库是在 TransactionService 的 syncEvents 方法中完成的

每次调用API如果上次已经从区块链拿到 有缓存 所以这次拿不到events

因为 syncEvents 方法会从上次同步的区块高度开始同步:

 				// 1. 获取上次同步的区块高度
        const lastSync = await prisma.syncStatus.findFirst({
          orderBy: { id: 'desc' },
        });
        const fromBlock = lastSync ? lastSync.blockNumber + 1n : 0n;
        
        // 2. 获取当前区块高度
        const currentBlock = await this.provider.getBlockNumber();

解决方法:不用事务,直接存储从合约来的events(work around),同时清除Sync表重新同步

更改schema后的数据同步

# 生成 Prisma 客户端
pnpm prisma generate --schema src/db/schema.prisma

# 运行数据库迁移
pnpm prisma migrate deploy --schema src/db/schema.prisma

# 启动开发服务器
pnpm run dev

需要在 Transaction 模型中添加 blockNumber 字段

首先更新 Prisma schema

然后在 TransactionService 中记录区块高度:

// 处理创建事件
for (const event of createdEvents) {
  const decodedLog = this.contract.interface.parseLog(event);
  const msgHash = this.calculateMsgHash(decodedLog);
  const block = await event.getBlock();

  await prisma.transaction.upsert({
    where: { msgHash },
    create: {
      msgHash,
      amount: ethers.formatUnits(decodedLog.args[2], 8),
      creator: 'xxx',
      target: decodedLog.args[1].toLowerCase(),
      createdAt: new Date(Number(block.timestamp) * 1000),
      status: TxStatus.CREATED,
      nonce: decodedLog.args[3],
      blockNumber: BigInt(block.number)  // 存储区块高度
    },
    update: {}
  });
}

最后更新 TypeScript 接口

并运行数据库迁移:

npx prisma migrate dev --name add_block_number

没有过滤到actived事件

需要在api/route.ts:

从 URL 参数中获取 status

  • 从 URL 参数中获取 status
  • 根据是否有 status 参数构建不同的查询条件
  • 在 prisma 查询中使用这个条件

Awesome🌸

image.png

在更新数据库的时候,Invalid prisma.signature.upsert() invocation:,

Unknown argument msgHash. Available options are marked with ?.:

before

 create: {
          role,
        msgHash,  // 这里直接设置了 msgHash//去掉这一行即可
			  transaction: {
				    connect: { msgHash }  // 同时又在这里通过关系设置了 msgHash
          }
Error processing signature event: PrismaClientValidationError: 
Invalid `prisma.signature.upsert()` invocation:

{
  where: {
    msgHash_role: {
      msgHash: "0x01efc3d02651ab89e47f4aa3029c4e4201dde1abc4c78a4f215f286222f3e1a2",
      role: "xxx"
    }
  },
  create: {
    role: "xxx",
    msgHash: "0x01efc3d02651ab89e47f4aa3029c4e4201dde1abc4c78a4f215f286222f3e1a2",
    ~~~~~~~
    transaction: {
      connect: {
        msgHash: "0x01efc3d02651ab89e47f4aa3029c4e4201dde1abc4c78a4f215f286222f3e1a2"
      }
    },
?   id?: String,
?   signedAt?: DateTime
  },
  update: {}
}

这导致了 Prisma 的验证错误,因为 msgHash 是一个关系字段,不应该直接设置。我们之前的修改移除了直接设置的 msgHash,只保留了通过关系设置的方式,这样就解决了这个问题。

docker-compose.yaml和dockerfile的区别?

Dockerfile

单个容器的构建说明

用于构建单个容器的镜像

  • 定义如何构建应用程序
  • 包含应用程序的环境、依赖和启动命令

docker-compose.yaml

多个容器的配置和关系

  • 配置容器之间的关系
  • 管理网络、卷、环境变量等
  • 简化部署过程

主要区别

  • 范围

    • Dockerfile: 单个容器
    • docker-compose: 多个容器的整体应用
  • 用途

    • Dockerfile: 构建镜像
    • docker-compose: 编排容器
  • Error response from daemon: pull access denied

    koki@kokideMacBook-Air learn_project % docker pull registry.cn-hangzhou.aliyuncs.com/library/postgres:alpine
    
    What's next:
        View a summary of image vulnerabilities and recommendations → docker scout quickview registr
        View a summary of image vulnerabilities and recommendations → docker scout quickview registry.cn-hangzhou.aliyuncs.com/library/postgres:alpine
    Error response from daemon: pull access denied for registry.cn-hangzhou.aliyuncs.com/library/pos
    Error response from daemon: pull access denied for registry.cn-hangzhou.aliyuncs.com/library/postgres, repository does not exist or may require 'docker login': denied: requested access to the 
    Error response from daemon: pull access denied for registry.cn-hangzhou.aliyuncs.com/library/postgres, repository does not exist or may require 'docker login': denied: requested access to the resource is denied
    

    解决方法:手动拉取官方镜像postgres:14-alpine

  • 单位问题导致的函数调用问题

    image.png转存失败,建议直接上传图片文件

    单位的问题:一开始使用了ethers.parseEther

    ethers.parseEther 和 ethers.parseUnits 的主要区别在于:

    // parseEther 固定使用 18 位精度(以太坊主币的精度)
    ethers.parseEther("1.0")    // 1000000000000000000n (18位小数)
    ethers.parseEther("2.5")    // 2500000000000000000n
    
    // parseUnits 可以指定任意精度
    ethers.parseUnits("1.0", 8)     // 100000000n (8位小数)
    ethers.parseUnits("1.0", 6)     // 1000000n (6位小数)
    
  • 前端的时间显示

    Postgres不能存储locale时间类型,只能是unix类型,所以需要在前端修改时间的格式

    const formatLocalTime = (utcTime: string) => {
      const date = new Date(utcTime);
      return date.toLocaleString('zh-CN', { 
        timeZone: 'Asia/Shanghai',
        year: 'numeric',
        month: '2-digit',
        day: '2-digit',
        hour: '2-digit',
        minute: '2-digit',
        second: '2-digit'
      });
    };
    export default formatLocalTime;
    

部署

  • 接口请求经过了7890端口

    koki@debian:~/learn_project$ docker logs -f learn_project
    npm error code ECONNREFUSED
    npm error syscall connect
    npm error errno ECONNREFUSED
    npm error FetchError: request to <https://registry.npmmirror.com/pnpm> failed, reason: connect ECONNREFUSED 127.0.0.1:7890
    npm error     at ClientRequest.<anonymous> (/usr/local/lib/node_modules/npm/node_modules/minipass-fetch/lib/index.js:130:14)
    npm error     at ClientRequest.emit (node:events:518:28)
    npm error     at emitErrorEvent (node:_http_client:101:11)
    npm error     at _destroy (node:_http_client:884:9)
    npm error     at onSocketNT (node:_http_client:904:5)
    npm error     at process.processTicksAndRejections (node:internal/process/task_queues:83:21) {
    npm error   code: 'ECONNREFUSED',
    npm error   errno: 'ECONNREFUSED',
    npm error   syscall: 'connect',
    npm error   address: '127.0.0.1',
    npm error   port: 7890,
    npm error   type: 'system',
    npm error   requiredBy: '.'
    npm error }
    npm error
    npm error If you are behind a proxy, please make sure that the
    npm error 'proxy' config is set properly.  See: 'npm help config'
    npm error A complete log of this run can be found in: /root/.npm/_logs/2024-12-10T08_44_56_797Z-debug-0.log
    

    尝试在docker-compose.yml里面

    version: "3.8"
    services:
      app:
        build:
          context: .
          dockerfile: Dockerfile
        image: learn_project:latest
        container_name: learn_project
        ports:
          - "18887:3000"
        environment://新增环境变量
          - DATABASE_URL=postgresql://gdst:gdst@db:5432/gdst_db
          - HTTP_PROXY=""
          - HTTPS_PROXY=""
          - NO_PROXY="*"
    

    仍然

    尝试直接在命令行 不是在容器内

    unset http_proxy
    unset https_proxy
    unset HTTP_PROXY
    unset HTTPS_PROXY
    

    仍然

    使用命令:来临时去掉代理

    image.png转存失败,建议直接上传图片文件

    env -u http_proxy -u https_proxy docker-compose up
    
    Recreating gdst_db ... done
    Recreating learn_project ... done
    Attaching to gdst_db, learn_project
    gdst_db | 
    gdst_db | PostgreSQL Database directory appears to contain a database; Skipping initialization
    gdst_db | 
    gdst_db | 2024-12-10 09:15:10.486 UTC [1] LOG:  starting PostgreSQL 14.15 on x86_64-pc-linux-musl, compiled by gcc (Alpine 14.2.0) 14.2.0, 64-bit
    gdst_db | 2024-12-10 09:15:10.486 UTC [1] LOG:  listening on IPv4 address "0.0.0.0", port 5432
    gdst_db | 2024-12-10 09:15:10.486 UTC [1] LOG:  listening on IPv6 address "::", port 5432
    gdst_db | 2024-12-10 09:15:10.497 UTC [1] LOG:  listening on Unix socket "/var/run/postgresql/.s.PGSQL.5432"
    gdst_db | 2024-12-10 09:15:10.503 UTC [27] LOG:  database system was shut down at 2024-12-10 09:15:09 UTC
    gdst_db | 2024-12-10 09:15:10.508 UTC [1] LOG:  database system is ready to accept connections
    learn_project | npm error code ECONNREFUSED
    learn_project | npm error syscall connect
    learn_project | npm error errno ECONNREFUSED
    learn_project | npm error FetchError: request to <https://registry.npmmirror.com/pnpm> failed, reason: connect ECONNREFUSED 127.0.0.1:7890
    learn_project | npm error     at ClientRequest.<anonymous> (/usr/local/lib/node_modules/npm/node_modules/minipass-fetch/lib/index.js:130:14)
    learn_project | npm error     at ClientRequest.emit (node:events:518:28)
    learn_project | npm error     at emitErrorEvent (node:_http_client:101:11)
    learn_project | npm error     at _destroy (node:_http_client:884:9)
    learn_project | npm error     at onSocketNT (node:_http_client:904:5)
    learn_project | npm error     at process.processTicksAndRejections (node:internal/process/task_queues:83:21) {
    learn_project | npm error   code: 'ECONNREFUSED',
    learn_project | npm error   errno: 'ECONNREFUSED',
    learn_project | npm error   syscall: 'connect',
    learn_project | npm error   address: '127.0.0.1',
    learn_project | npm error   port: 7890,
    learn_project | npm error   type: 'system',
    learn_project | npm error   requiredBy: '.'
    learn_project | npm error }
    learn_project | npm error
    learn_project | npm error If you are behind a proxy, please make sure that the
    learn_project | npm error 'proxy' config is set properly.  See: 'npm help config'
    learn_project | npm error A complete log of this run can be found in: /root/.npm/_logs/2024-12-10T09_15_11_227Z-debug-0.log
    learn_project exited with code 1
    

    错误发生在这里

    command: sh -c "cd /app &&
          npm config delete proxy &&           # 这些命令正常执行
          npm config delete https-proxy &&     # 这些命令正常执行
          npm config set registry <https://registry.npmmirror.com/> &&  # 这些命令正常执行
          npm install -g pnpm &&              # <-- 错误发生在这里
          pnpm install &&
    

    尝试修改run命令,确保在容器内npm也不使用代理

    //docker-compose.yml
        volumes:
          # todo modify in server
          - /home/koki/learn_project:/app
          # - /Users/koki/projects/learn_project:/app
        depends_on:
          - db
        command: sh -c "cd /app &&
          npm config delete proxy &&//add
          npm config delete https-proxy &&//add
    

    仍然

    尝试在docker image阶段就确保容器内的npm不使用代理

    //Dockerfile
    FROM docker.m.daocloud.io/node:alpine
    
    # 预先配置 npm,删除代理设置
    RUN npm config delete proxy \
        && npm config delete https-proxy \
        && npm config set registry <https://registry.npmmirror.com/> \
        && npm config set strict-ssl false
    
    WORKDIR /app
    EXPOSE 3000
    

    尝试关闭系统级别的clash代理

    koki@debian:~/learn_project$ ps aux | grep clash//查看clash在哪个进程
    root      381557  0.0  0.0 725116  6496 ?        Sl   Nov29   1:52 /home/peter/clash/bin/clash-linux-amd64 -d /home/peter/clash/conf
    koki   583275  0.0  0.0   6332  2096 pts/4    S+   04:31   0:00 grep clash
    koki@debian:~/learn_project$ sudo kill 381557//杀掉这个进程
    

    仍然

    尝试在docker image阶段就npm install -g pnpm,再修改yml文件:

    //..
          
    	     //去掉这里的npm install -g pnpm
        command: sh -c "cd /app &&
          pnpm install &&
          pnpm config set fetch-timeout 300000 &&
          pnpm prisma generate --schema src/db/schema.prisma &&
          pnpm prisma migrate deploy --schema src/db/schema.prisma &&
          pnpm run build &&
          pnpm run start"
    
      db:
        image: docker.m.daocloud.io/postgres:14-alpine
        container_name: gdst_db
        environment:
          - POSTGRES_USER=gdst
          - POSTGRES_PASSWORD=gdst
          - POSTGRES_DB=gdst_db
        ports:
          - "5433:5432"
        volumes:
          - postgres_data:/var/lib/postgresql/data
    
    volumes:
      postgres_data:
    
    koki@debian:~/learn_project$ env -u http_proxy -u https_proxy docker-compose up --build
    Creating network "learn_project_default" with the default driver
    Building app
    Sending build context to Docker daemon  1.575GB
    Step 1/5 : FROM docker.m.daocloud.io/node:alpine
     ---> b68700c9f27a
    Step 2/5 : RUN npm config delete proxy     && npm config delete https-proxy     && npm config delete http-proxy     && npm config delete all-proxy     && npm config set registry <https://registry.npmmirror.com/>     && npm install -g pnpm
     ---> Running in 8ade6fb86642
    (node:7) ExperimentalWarning: CommonJS module /usr/local/lib/node_modules/npm/node_modules/debug/src/node.js is loading ES Module /usr/local/lib/node_modules/npm/node_modules/supports-color/index.js using require().
    Support for loading ES Module in require() is an experimental feature and might change at any time
    (Use `node --trace-warnings ...` to show where the warning was created)
    (node:1) ExperimentalWarning: CommonJS module /usr/local/lib/node_modules/npm/node_modules/debug/src/node.js is loading ES Module /usr/local/lib/node_modules/npm/node_modules/supports-color/index.js using require().
    Support for loading ES Module in require() is an experimental feature and might change at any time
    (Use `node --trace-warnings ...` to show where the warning was created)
    npm error code ECONNREFUSED
    npm error syscall connect
    npm error errno ECONNREFUSED
    npm error FetchError: request to <https://registry.npmmirror.com/pnpm> failed, reason: connect ECONNREFUSED 127.0.0.1:7890
    npm error     at ClientRequest.<anonymous> (/usr/local/lib/node_modules/npm/node_modules/minipass-fetch/lib/index.js:130:14)
    npm error     at ClientRequest.emit (node:events:513:28)
    npm error     at emitErrorEvent (node:_http_client:104:11)
    npm error     at _destroy (node:_http_client:892:9)
    npm error     at onSocketNT (node:_http_client:912:5)
    npm error     at process.processTicksAndRejections (node:internal/process/task_queues:91:21) {
    npm error   code: 'ECONNREFUSED',
    npm error   errno: 'ECONNREFUSED',
    npm error   syscall: 'connect',
    npm error   address: '127.0.0.1',
    npm error   port: 7890,
    npm error   type: 'system',
    npm error   requiredBy: '.'
    npm error }
    npm error
    npm error If you are behind a proxy, please make sure that the
    npm error 'proxy' config is set properly.  See: 'npm help config'
    npm error A complete log of this run can be found in: /root/.npm/_logs/2024-12-11T01_30_54_535Z-debug-0.log
    The command '/bin/sh -c npm config delete proxy     && npm config delete https-proxy     && npm config delete http-proxy     && npm config delete all-proxy     && npm config set registry <https://registry.npmmirror.com/>     && npm install -g pnpm' returned a non-zero code: 1
    

    最终解决方法

    FROM docker.m.daocloud.io/node:alpine
    
    # 清理所有可能的代理设置
    RUN set -x \
        && npm config ls -l | grep proxy \
        && npm config rm proxy \
        && npm config rm https-proxy \
        && npm config rm http-proxy \
        && npm config rm all-proxy \
        && unset HTTP_PROXY HTTPS_PROXY http_proxy https_proxy \
        && echo "registry=https://registry.npmmirror.com/" > /root/.npmrc \
        && cat /root/.npmrc \
        && npm install -g pnpm
    
    WORKDIR /app
    EXPOSE 3000
    
    version: "3.8"
    services:
      app:
        build:
          context: .
          dockerfile: Dockerfile
        image: learn_project:latest
        container_name: learn_project
        ports:
          - "18887:3000"
        environment:
          - DATABASE_URL=postgresql://gdst:gdst@db:5432/gdst_db
          - HTTP_PROXY=""
          - HTTPS_PROXY=""
          - NO_PROXY="*"
        volumes:
          # todo modify in server
          - /home/koki/learn_project:/app
          # - /Users/koki/projects/learn_project:/app
        depends_on:
          - db
        command: sh -c "cd /app &&
          pnpm install &&
          pnpm config set fetch-timeout 300000 &&
          pnpm prisma generate --schema src/db/schema.prisma &&
          pnpm prisma migrate deploy --schema src/db/schema.prisma &&
          pnpm run build &&
          pnpm run start"
    
      db:
        image: docker.m.daocloud.io/postgres:14-alpine
        container_name: gdst_db
        environment:
          - POSTGRES_USER=gdst
          - POSTGRES_PASSWORD=gdst
          - POSTGRES_DB=gdst_db
        ports:
          - "5433:5432"
        volumes:
          - postgres_data:/var/lib/postgresql/data
    
    volumes:
      postgres_data:
    

    在docker compose up的时候:

    learn_project | Error: request to <https://binaries.prisma.sh/all_commits/5dbef10bdbfb579e07d35cc85fb1518d357cb99e/linux-musl/libquery_engine.so.node.gz.sha256> failed, reason: connect ECONNREFUSED 127.0.0.1:7890
    

    以及

    learn_project | An error occurred in getProxyAgent(), no proxy agent will be used. Error: Error while instantiating HttpsProxyAgent with URL: """"
    learn_project | TypeError: Invalid URL
    learn_project | Check the following env vars "https_proxy" or "HTTPS_PROXY". The value should be a valid URL starting with "https://"
    

    解决方法

    //。。
    //增加这些配置
          - http_proxy=
          - https_proxy=
          - HTTP_PROXY=
          - HTTPS_PROXY=
          - PRISMA_CLI_PROXY=
          - PRISMA_ENGINES_MIRROR=
        volumes:
          # todo modify in server
          - /home/koki/learn_project:/app
          # - /Users/koki/projects/learn_project:/app
        depends_on:
          - db
        command: >
          sh -c "cd /app &&
          env | grep -i proxy > /tmp/env_check.log &&//增加打印日志
          npm config list > /tmp/npm_config.log &&
          pnpm install &&
          pnpm config set fetch-timeout 300000 &&
          pnpm prisma generate --schema src/db/schema.prisma &&
          pnpm prisma migrate deploy --schema src/db/schema.prisma &&
          pnpm run build &&
          pnpm run start"
    
      db:
        image: docker.m.daocloud.io/postgres:14-alpine
        container_name: gdst_db
        environment:
          - POSTGRES_USER=gdst
          - POSTGRES_PASSWORD=gdst
          - POSTGRES_DB=gdst_db
        ports:
          - "5433:5432"
        volumes:
          - postgres_data:/var/lib/postgresql/data
    
    volumes:
      postgres_data:
    
  • 查看容器日志的时候

    ✔ Generated Prisma Client (v6.0.1) to ./node_modules/.pnpm/@prisma+client@6.0.1_prisma@6.0.1/node_modules/@prisma/client in 93ms
    
    Start by importing your Prisma Client (See: <https://pris.ly/d/importing-client>)
    
    Tip: Interested in query caching in just a few lines of code? Try Accelerate today! <https://pris.ly/tip-3-accelerate>
    
    prisma:warn Prisma failed to detect the libssl/openssl version to use, and may not work as expected. Defaulting to "openssl-1.1.x".
    Please manually install OpenSSL and try installing Prisma again.
    prisma:warn Prisma failed to detect the libssl/openssl version to use, and may not work as expected. Defaulting to "openssl-1.1.x".
    Please manually install OpenSSL and try installing Prisma again.
    Prisma schema loaded from src/db/schema.prisma
    Datasource "db": PostgreSQL database "gdst_db", schema "public" at "db:5432"
    
    Error: Could not parse schema engine response: SyntaxError: Unexpected token 'E', "Error load"... is not valid JSON
    

    prisma需要openssl

     for docker.m.daocloud.io/node:alpine                                                                                                                                                                        0.0s
     => CACHED [app 1/4] FROM docker.m.daocloud.io/node:alpine                                                                                                                                                                                   0.0s
     => [app 2/4] RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories                                                                                                                                               0.4s
     => ERROR [app 3/4] RUN apk add --no-cache     git     openssl     openssl-dev     postgresql-client                                                                                                                                         0.4s
    ------                                                                                                                                                                                                                                            
     > [app 3/4] RUN apk add --no-cache     git     openssl     openssl-dev     postgresql-client:
    0.309 fetch <https://mirrors.aliyun.com/alpine/v3.21/main/x86_64/APKINDEX.tar.gz>
    0.310 WARNING: fetching <https://mirrors.aliyun.com/alpine/v3.21/main:> could not connect to server (check repositories file)
    0.310 fetch <https://mirrors.aliyun.com/alpine/v3.21/community/x86_64/APKINDEX.tar.gz>
    0.310 WARNING: fetching <https://mirrors.aliyun.com/alpine/v3.21/community:> could not connect to server (check repositories file)
    0.311 ERROR: unable to select packages:
    0.311   git (no such package):
    0.311     required by: world[git]
    0.311   openssl (no such package):
    0.311     required by: world[openssl]
    0.311   openssl-dev (no such package):
    0.311     required by: world[openssl-dev]
    0.311   postgresql-client (no such package):
    0.311     required by: world[postgresql-client]
    ------
    failed to solve: executor failed running [/bin/sh -c apk add --no-cache     git     openssl     openssl-dev     postgresql-client]: exit code: 4
    

    尝试1: 使用Alpine 和 apk add

    尝试在dockerfile中

    RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories
    # 安装所需的依赖
    RUN apk add --no-cache \
        openssl \
        openssl-dev \
        libc6-compat \
        git \
        postgresql-client
    

    报错

    koki@debian:~/learn_project$ env -u http_proxy -u https_proxy docker-compose up --build
    Building app
    Sending build context to Docker daemon  1.575GB
    Step 1/6 : FROM docker.m.daocloud.io/node:alpine
     ---> b68700c9f27a
    Step 2/6 : RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories
     ---> Using cache
     ---> 408859334c4a
    Step 3/6 : RUN apk add --no-cache     openssl     openssl-dev     libc6-compat     git     postgresql-client
     ---> Running in 9f0ef1d47eee
    fetch <https://mirrors.aliyun.com/alpine/v3.21/main/x86_64/APKINDEX.tar.gz>
    WARNING: fetching <https://mirrors.aliyun.com/alpine/v3.21/main:> could not connect to server (check repositories file)
    fetch <https://mirrors.aliyun.com/alpine/v3.21/community/x86_64/APKINDEX.tar.gz>
    WARNING: fetching <https://mirrors.aliyun.com/alpine/v3.21/community:> could not connect to server (check repositories file)
    ERROR: unable to select packages:
      git (no such package):
        required by: world[git]
      libc6-compat (no such package):
        required by: world[libc6-compat]
      openssl (no such package):
        required by: world[openssl]
      openssl-dev (no such package):
        required by: world[openssl-dev]
      postgresql-client (no such package):
        required by: world[postgresql-client]
    The command '/bin/sh -c apk add --no-cache     openssl     openssl-dev     libc6-compat     git     postgresql-client' returned a non-zero code: 5
    ERROR: Service 'app' failed to build : Build failed
    

    尝试检查Alpine 版本,并下载指定版本

    koki@debian:~/learn_project$ env -u http_proxy -u https_proxy docker-compose up --build
    Building app
    Sending build context to Docker daemon  1.575GB
    Step 1/7 : FROM docker.m.daocloud.io/node:alpine
     ---> b68700c9f27a
    Step 2/7 : RUN cat /etc/os-release
     ---> Using cache
     ---> ebb99cabf400
    Step 3/7 : RUN echo "<https://mirrors.aliyun.com/alpine/v3.21/main/>" > /etc/apk/repositories     && echo "<https://mirrors.aliyun.com/alpine/v3.21/community/>" >> /etc/apk/repositories
     ---> Running in 3f09f4777da4
    Removing intermediate container 3f09f4777da4
     ---> dd1686d4bb54
    Step 4/7 : RUN apk add --no-cache     openssl     openssl-dev     libc6-compat     git     postgresql-client
     ---> Running in 52f73d456853
    fetch <https://mirrors.aliyun.com/alpine/v3.21/main/x86_64/APKINDEX.tar.gz>
    WARNING: fetching <https://mirrors.aliyun.com/alpine/v3.21/main/:> could not connect to server (check repositories file)
    fetch <https://mirrors.aliyun.com/alpine/v3.21/community/x86_64/APKINDEX.tar.gz>
    WARNING: fetching <https://mirrors.aliyun.com/alpine/v3.21/community/:> could not connect to server (check repositories file)
    ERROR: unable to select packages:
      git (no such package):
        required by: world[git]
      libc6-compat (no such package):
        required by: world[libc6-compat]
      openssl (no such package):
        required by: world[openssl]
      openssl-dev (no such package):
        required by: world[openssl-dev]
      postgresql-client (no such package):
        required by: world[postgresql-client]
    The command '/bin/sh -c apk add --no-cache     openssl     openssl-dev     libc6-compat     git     postgresql-client' returned a non-zero code: 5
    ERROR: Service 'app' failed to build : Build failed
    

    更换镜像

    FROM docker.m.daocloud.io/node:alpine
    
    # 检查 Alpine 版本
    RUN cat /etc/os-release
    
    # 使用 USTC 的 Alpine 3.21 镜像源
    RUN echo "<https://mirrors.ustc.edu.cn/alpine/v3.21/main/>" > /etc/apk/repositories \
        && echo "<https://mirrors.ustc.edu.cn/alpine/v3.21/community/>" >> /etc/apk/repositories
    
    # 安装所需的依赖
    RUN apk add --no-cache \
        openssl \
        openssl-dev \
        libc6-compat \
        git \
        postgresql-client
    
    # 清理所有可能的代理设置
    RUN set -x \
        && npm config ls -l | grep proxy \
        && npm config rm proxy \
        && npm config rm https-proxy \
        && npm config rm http-proxy \
        && npm config rm all-proxy \
        && unset HTTP_PROXY HTTPS_PROXY http_proxy https_proxy \
        && echo "registry=https://registry.npmmirror.com/" > /root/.npmrc \
        && cat /root/.npmrc \
        && npm install -g pnpm
    
    WORKDIR /app
    EXPOSE 3000
    

    尝试2:使用Debian 基础镜像和apt

    FROM docker.m.daocloud.io/node:20-slim
    
    # 使用清华大学的 Debian 镜像源
    RUN sed -i 's/deb.debian.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apt/sources.list \
        && sed -i 's/security.debian.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apt/sources.list
    
    # 安装依赖
    RUN apt-get update \
        && apt-get install -y \
            openssl \
            libssl-dev \
            git \
            postgresql-client \
        && rm -rf /var/lib/apt/lists/*
    
    # 清理所有可能的代理设置
    RUN set -x \
        && npm config ls -l | grep proxy \
        && npm config rm proxy \
        && npm config rm https-proxy \
        && npm config rm http-proxy \
        && npm config rm all-proxy \
        && unset HTTP_PROXY HTTPS_PROXY http_proxy https_proxy \
        && echo "registry=https://registry.npmmirror.com/" > /root/.npmrc \
        && cat /root/.npmrc \
        && npm install -g pnpm
    
    WORKDIR /app
    EXPOSE 3000
    

    Works!

    怎么区分要用apt add 还是apk get

    这取决于你使用的基础镜像(base image)的操作系统:

    常见的基础镜像对应的包管理器:

    • apt-get

      • Debian
      • Ubuntu
      • 示例镜像:node:20-slim, ubuntu:20.04
    • apk

      • Alpine Linux
      • 示例镜像:node:20-alpine, alpine:3.18

检查代理配置是从哪里来的

koki@debian:~/learn_project$ sudo nano /etc/environment//空文件
koki@debian:~/learn_project$ nano ~/.bashrc//空文件
koki@debian:~/learn_project$ sudo nano /etc/apt/apt.conf.d/proxy.conf
koki@debian:~/learn_project$ env | grep -i proxy
koki@debian:~/learn_project$ ls /etc/apt/apt.conf.d/
00CDMountPoint  00trustcdrom  01autoremove  02autoremove-postgresql  20listchanges  70debconf  99needrestart
koki@debian:~/learn_project$ apt-config dump | grep -i proxy
koki@debian:~/learn_project$ sudo systemctl show --property=Environment docker
Environment=
koki@debian:~/learn_project$ cat ~/.docker/config.json
{
  "proxies": {
    "default": {
      "httpProxy": "<http://127.0.0.1:7890>",
      "httpsProxy": "<http://127.0.0.1:7890>",
      "noProxy": "127.0.0.1"
    }
  }
}

清除代理即可: echo '{}' > ~/.docker/config.json