记录一次docker自建ipfs网关

前段时间想自己写合约铸造nft,了解到nft图片通常存储在ipfs,去看了下ipfs网关价格,卧槽,比存储桶都贵啊,因此决定自建。

自建ipfs网关基本0额外成本,吃灰小鸡多干点活就行了,还能规避网关认为内容不合规导致的风险。进行一些设置后,可以只在网关存储并pin自己上传的白名单文件,并提供给其它网关访问,且不会被滥用。

    docker-compose.yml

    services:
      kubo:
        image: ipfs/kubo:v0.36.0
        container_name: kubo
        restart: unless-stopped
        networks:
          - dockernetwork
        ports:
          - "24001:4001/tcp"
          - "24001:4001/udp"
        volumes:
          - ./ipfs_data:/data/ipfs
          - ./scripts:/scripts:ro
        environment:
          - IPFS_PATH=/data/ipfs
        entrypoint: ["/bin/sh","-c","/scripts/boot.sh"]   # 启动脚本
        healthcheck:
          test: ["CMD-SHELL", "ipfs id >/dev/null 2>&1"]
          interval: 30s
          timeout: 10s
          retries: 3
          start_period: 30s
    
      ipfs-webui:
        image: l1angth6/ipfs-webui:latest
        container_name: ipfs-webui
        restart: unless-stopped
        expose:
          - "8080"
        networks:
          - dockernetwork
        environment:
          - IPFS_API_URL=https://ipfs-api.example.com
    
    networks:
      dockernetwork:
        external: true

    解释一下为什么又单独运行了一个ipfs-webui,因为设置了关闭反序列化渲染,因此无法加载自带的webui。我发现没有官方的webui容器,就用官方仓库编译了一个,不放心或嫌麻烦请开启反序列化或使用自己放心的webui(比如官方webui也不是不能用)。

    config/pins.txt 按照本文思路可以不使用

    # IPFS Pin列表
    # 每行一个CID,以#开头的为注释
    # 
    # 示例:
    # QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn

    scripts/boot.sh 需要给可执行权限

    #!/bin/sh
    set -e
    
    # 若未初始化,则初始化
    ipfs config show >/dev/null 2>&1 || ipfs init --profile=server
    
    # 暴露面:监听容器网卡,如果不需要使用api、webui 可以将api监听改为127.0.0.1
    ipfs config Addresses.API /ip4/0.0.0.0/tcp/5001
    ipfs config Addresses.Gateway /ip4/0.0.0.0/tcp/8080
    
    # 防滥用:不从网络抓取新块;关闭反序列化渲染(trustless-only)
    ipfs config --json Gateway.NoFetch true
    ipfs config --json Gateway.DeserializedResponses false
    
    # 轻量 DHT 客户端
    ipfs config Routing.Type dhtclient
    
    # CORS:允许 WebUI 容器和管理域名访问 API
    ipfs config --json API.HTTPHeaders.Access-Control-Allow-Origin '["https://ipfs-webui.example.com", "https://ipfs-api.example.com", "http://localhost:3000", "http://127.0.0.1:5001"]'
    ipfs config --json API.HTTPHeaders.Access-Control-Allow-Methods '["GET","POST","PUT","OPTIONS"]'
    ipfs config --json API.HTTPHeaders.Access-Control-Allow-Headers '["Authorization","Content-Type","X-Requested-With","Range","User-Agent","Origin","Accept","Referer"]'
    ipfs config --json API.HTTPHeaders.Access-Control-Expose-Headers '["Location","Ipfs-Hash"]'
    ipfs config --json API.HTTPHeaders.Access-Control-Allow-Credentials '["true"]'
    
    # 垃圾回收周期(注意这里是字符串,不能用 --json 直接写裸字面量)
    ipfs config Datastore.GCPeriod 12h
    
    # 以守护进程运行(并启用 GC)
    exec ipfs daemon --migrate=true --enable-gc

    scripts/pin-manager.sh 需要手动执行,如果通过webui操作则不需要使用此脚本

    #!/bin/bash
    PIN_LIST="./config/pins.txt"
    CONTAINER_NAME="kubo"
    
    case "$1" in
      "add")
        echo "$2" >> "$PIN_LIST"
        docker exec "$CONTAINER_NAME" ipfs pin add "$2"
        echo "已Pin并添加到列表: $2"
        ;;
      "sync")
        echo "同步Pin列表中的所有文件..."
        while read -r cid; do
          [[ "$cid" =~ ^#.*$ ]] && continue
          [[ -z "$cid" ]] && continue
          echo "正在Pin: $cid"
          docker exec "$CONTAINER_NAME" ipfs pin add "$cid"
        done < "$PIN_LIST"
        ;;
      "list")
        docker exec "$CONTAINER_NAME" ipfs pin ls --type recursive
        ;;
      "gc")
        echo "执行垃圾收集..."
        docker exec "$CONTAINER_NAME" ipfs repo gc
        ;;
      *)
        echo "用法: $0 {add <CID>|sync|list|gc}"
        ;;
    esac

    此用例需要解析网关(到kubo:8080)、api(到kubo:5001)、webui域名(到ipfs-webui:8080)。并且必须对api域名做强鉴权,比如限制白名单ip。如果不想这样,可以只解析网关域名并修改boot.sh中api为外部不可访问,操作麻烦些:

    ipfs config Addresses.API /ip4/127.0.0.1/tcp/5001

    默认软存储上限为10GB,通过以下命令查看

    # 查看当前上限与占用:
    docker exec kubo ipfs config Datastore.StorageMax
    docker exec kubo ipfs config Datastore.StorageGCWatermark
    docker exec kubo ipfs repo stat --human
    
    # 调整软上限(示例:改为 200GB)与水位线:
    docker exec kubo ipfs config Datastore.StorageMax 200GB
    docker exec kubo ipfs config Datastore.StorageGCWatermark 90
    # 重启容器以确保生效
    docker compose restart kubo
    

    说明:StorageMax字符串(如 200GB);水位线 % 一般保持 90。GC 的默认周期是 1 小时,你已在脚本里改成了 12h

    p2p端口用于向其它节点提供文件,默认是4001,本文中设置为24001。如果修改了p2p端口,可能需要添加公告。在boot脚本中添加公告,使其他网关能发现本地文件,(放在已有的 Routing.Type dhtclient 之后、exec ipfs daemon … 之前):

    # ---- 公告外网可达地址(两种写法,二选一) ----
    
    # 重置(避免残留坏条目)
    ipfs config --json Addresses.Announce '[]'
    ipfs config --json Addresses.AppendAnnounce '[]'
    
    # 公告 ipv4
    ipfs config --json Addresses.Announce '[
      "/ip4/152.53.38.001/tcp/14001",
      "/ip4/152.53.38.001/udp/14001/quic-v1"
    ]'
    
    # 避免 UPnP/NAT 自动探测干扰(你已手动公告)
    ipfs config --json Swarm.DisableNatPortMap true
    ipfs config --json Addresses.AppendAnnounce '[]'
    
    # (可选)只再公布已 pin 的内容,便于别的节点/DHT 更快发现你
    ipfs config Reprovider.Strategy pinned
    
    # ================
    
    # 方式B:用域名(IP 变化少、或喜欢用域名时)
    # 确保 A/AAAA 已指向本机
    ipfs config --json Addresses.Announce '[
      "/dns4/ipfs-test.example.com/tcp/24001",
      "/dns4/ipfs-test.example.com/udp/24001/quic-v1"
    ]'
    ipfs config --json Addresses.AppendAnnounce '[]'
    
    # (可选)只再公布已 pin 的内容,便于别的节点/DHT 更快发现你
    ipfs config Reprovider.Strategy pinned

    随后运行 docker compose up -d ,如果没有报错,则继续进行

    访问ipfs-webui.example.com ,api如果没有自动设置,则手动设置为

    https://ipfs-api.example.com/api/v0

    随后可以在左侧的 文件 标签页上传和pin文件

    图片[1]-记录一次docker自建ipfs网关-THsInk

    这里尝试上传了一个静态网站和一些nft需要的文件。

    如果先pin文件夹,之后对文件夹内容进行了修改,会导致此文件夹cid改变,pin的文件夹实际是此文件夹的历史版本。pin文件夹会递归pin此文件夹内当前包含的文件和文件夹。

    至此,实现了:

    • 存储自己需要存储在ipfs中的文件,并保证了其可访问性
    • 节省了大量网关服务产生的费用
    • ipfs节点被设置为无法获取不在本地的文件,意味着如果其他人获取了网关地址,无法用来访问不在此网关本地的文件。类似于设置了文件白名单。也可以直接不解析域名到此网关。
    • 使用此网关上传的文件可以通过其它网关访问。比如nft图片和网站,通常都应该能显示。

    此网关无法查看网页,因为设置了Gateway.DeserializedResponses false 以提高安全性。即使开启,也无法正确查看有资源路径的网页,如果要支持这种类型网站,需要设置将cid解析到单独的子域名,由于我暂时用不到此功能,cloudflare对子域名证书也有限制,因此暂未尝试。

    上传的网页在其它支持上述功能的网关测试可以正常访问。

    然后又要说一下折腾网站的事。。最开始在youtube看到有Ethereum Name Service,注册的.eth域名可以代替标准0x eth地址用来转账交易,还可以搭配ipfs创建一个web3网站,可以通过一些现成的服务访问此.eth网站,挺有意思的。类似的名称服务还有.sol .pol等,和eth按年付费不同,这些通常是一次性付费的。本来想买个sol域名,结果sol域名的1刀活动结束了,索性不做了,以后再说吧。

    v2ex最近也有一股sol风潮,我对sol链倒是用得少,pol反而多点。突然发现一个月前买的v2ex币涨了挺多,卖出大部分后爽吃了一顿😂

    © 版权声明
    THE END
    喜欢就支持一下吧
    点赞0 分享
    评论 抢沙发
    头像
    欢迎您留下宝贵的见解!无需注册,过短或乱码评论会被屏蔽。
    提交
    头像

    昵称

    取消
    昵称表情代码图片快捷回复

      暂无评论内容