前置说明 HiClaw是一个定位于治理企业级多Agent协同的平台。 本文想在HiClaw单机embed模式部署时从外部能直接访问某worker的minio文件,方便对其进行二次开发。 以下假设worker的名字叫alice,以及hiclaw部署过程中的内部地址及端口号都保持默认。
1. 在部署机上确认 MinIO 中确实有 alice 的文件 1 2 3 docker exec hiclaw-controller sh -lc \ "mc alias set local http://127.0.0.1:9000 admin your_passwd >/dev/null 2>&1 && \ mc ls local/hiclaw-storage --recursive | grep '^.*agents/alice/'"
2. 在部署机上确认 Higress 到 MinIO 的文件系统路由已生效 1 2 docker exec hiclaw-controller sh -lc \ "curl -i -H 'Host: fs-local.hiclaw.io' http://127.0.0.1:8080/minio/health/live"
3. 查询 Higress 里是否存在文件系统 domain 和 route 1 2 3 4 5 6 7 8 9 10 11 docker exec hiclaw-controller sh -lc ' COOKIE=/tmp/higress.cookie curl -s -c "$COOKIE" -H "Content-Type: application/json" \ -d "{\"username\":\"admin\",\"password\":\"your_passwd\"}" \ http://127.0.0.1:8001/session/login >/dev/null echo "== domains ==" curl -s -b "$COOKIE" http://127.0.0.1:8001/v1/domains echo echo "== routes ==" curl -s -b "$COOKIE" http://127.0.0.1:8001/v1/routes '
4. 如果缺失,手工补 MinIO 的 service source 1 2 3 4 5 6 7 8 9 10 docker exec hiclaw-controller sh -lc ' COOKIE=/tmp/higress.cookie curl -s -c "$COOKIE" -H "Content-Type: application/json" \ -d "{\"username\":\"admin\",\"password\":\"your_passwd\"}" \ http://127.0.0.1:8001/session/login >/dev/null curl -s -b "$COOKIE" -H "Content-Type: application/json" \ -X POST http://127.0.0.1:8001/v1/service-sources \ -d "{\"name\":\"minio\",\"type\":\"static\",\"domain\":\"127.0.0.1:9000\",\"port\":9000,\"properties\":{},\"authN\":{\"enabled\":false}}" '
5. 如果缺失,手工补文件系统 domain 1 2 3 4 5 6 7 8 9 10 docker exec hiclaw-controller sh -lc ' COOKIE=/tmp/higress.cookie curl -s -c "$COOKIE" -H "Content-Type: application/json" \ -d "{\"username\":\"admin\",\"password\":\"your_passwd\"}" \ http://127.0.0.1:8001/session/login >/dev/null curl -s -b "$COOKIE" -H "Content-Type: application/json" \ -X POST http://127.0.0.1:8001/v1/domains \ -d "{\"name\":\"fs-local.hiclaw.io\",\"enableHttps\":\"off\"}" '
6. 如果缺失,手工补文件系统 route 1 2 3 4 5 6 7 8 9 10 docker exec hiclaw-controller sh -lc ' COOKIE=/tmp/higress.cookie curl -s -c "$COOKIE" -H "Content-Type: application/json" \ -d "{\"username\":\"admin\",\"password\":\"your_passwd\"}" \ http://127.0.0.1:8001/session/login >/dev/null curl -s -b "$COOKIE" -H "Content-Type: application/json" \ -X POST http://127.0.0.1:8001/v1/routes \ -d "{\"name\":\"http-filesystem\",\"domains\":[\"fs-local.hiclaw.io\"],\"path\":{\"matchType\":\"PRE\",\"matchValue\":\"/\"},\"services\":[{\"name\":\"minio.static\",\"port\":9000,\"weight\":100}]}" '
7. 在外部服务器上验证文件系统入口是否可达 1 2 curl --resolve fs-local.hiclaw.io:18080:your_server_ip \ -i http://fs-local.hiclaw.io:18080/minio/health/live
如果机器上配置了代理,优先使用不走代理的版本:
1 2 curl --noproxy '*' --resolve fs-local.hiclaw.io:18080:your_server_ip \ -i http://fs-local.hiclaw.io:18080/minio/health/live
8. 在外部服务器上查看是不是被本地代理劫持 1 2 curl -v --resolve fs-local.hiclaw.io:18080:your_server_ip \ http://fs-local.hiclaw.io:18080/minio/health/live
如果输出里出现类似下面的内容,说明请求先被发给了本地代理:
1 2 Uses proxy env variable http_proxy == 'http://127.0.0.1:7897' Trying 127.0.0.1:7897...
9. 在外部服务器上确认 18080 端口本身是通的 1 nc -vz your_server_ip 18080
10. 直接运行 Python 脚本列出 alice 可访问的文件 部署机上可直接运行,脚本会优先自动从 hiclaw-worker-alice 容器环境变量中读取 HICLAW_FS_SECRET_KEY:
1 python3 test_remote_boto.py
这个py文件的内容是:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 import osimport socketimport subprocessimport boto3from botocore.client import ConfigPUBLIC_IP = "your_server_ip" FS_HOST = "fs-local.hiclaw.io" ENDPOINT_URL = f"http://{FS_HOST} :18080" BUCKET = "hiclaw-storage" WORKER_NAME = os.environ.get("WORKER_NAME" , "alice" ) WORKER_SECRET_KEY = os.environ.get("WORKER_SECRET_KEY" ) or os.environ.get("HICLAW_FS_SECRET_KEY" ) PROXY_KEYS = ( "http_proxy" , "https_proxy" , "HTTP_PROXY" , "HTTPS_PROXY" , "all_proxy" , "ALL_PROXY" , ) def disable_proxies (): removed = [] for key in PROXY_KEYS: value = os.environ.pop(key, None ) if value: removed.append(f"{key} ={value} " ) no_proxy_value = f"{PUBLIC_IP} ,{FS_HOST} " os.environ["NO_PROXY" ] = no_proxy_value os.environ["no_proxy" ] = no_proxy_value if removed: print ("Disabled proxies for this script:" ) for item in removed: print (" " , item) print ("NO_PROXY set to:" , no_proxy_value) def load_secret_from_env (): if WORKER_SECRET_KEY: print ("Using worker credentials:" ) print (" access key:" , WORKER_NAME) print (" secret key source: WORKER_SECRET_KEY/HICLAW_FS_SECRET_KEY" ) return WORKER_SECRET_KEY return None def load_secret_from_docker (): container_name = f"hiclaw-worker-{WORKER_NAME} " try : result = subprocess.run( ["docker" , "exec" , container_name, "env" ], capture_output=True , text=True , check=True , ) except FileNotFoundError: return None except subprocess.CalledProcessError: return None for line in result.stdout.splitlines(): if line.startswith("HICLAW_FS_SECRET_KEY=" ): secret = line.split("=" , 1 )[1 ].strip() if secret: print ("Using worker credentials:" ) print (" access key:" , WORKER_NAME) print (f" secret key source: docker exec {container_name} env" ) return secret return None def get_worker_credentials (): secret_key = load_secret_from_env() if not secret_key: secret_key = load_secret_from_docker() if not secret_key: raise RuntimeError( "Missing worker secret key. Set WORKER_SECRET_KEY or HICLAW_FS_SECRET_KEY, " f"or run this script on the HiClaw deployment server so it can read " f"`hiclaw-worker-{WORKER_NAME} ` via docker exec." ) return WORKER_NAME, secret_key def create_client (): original_getaddrinfo = socket.getaddrinfo access_key, secret_key = get_worker_credentials() def patched_getaddrinfo (host, port, family=0 , type =0 , proto=0 , flags=0 ): if host == FS_HOST: host = PUBLIC_IP return original_getaddrinfo(host, port, family, type , proto, flags) socket.getaddrinfo = patched_getaddrinfo client = boto3.client( "s3" , endpoint_url=ENDPOINT_URL, aws_access_key_id=access_key, aws_secret_access_key=secret_key, config=Config( signature_version="s3v4" , s3={"addressing_style" : "path" }, ), region_name="us-east-1" , ) return client def dump_listing (client, label, prefix=None , max_keys=20 ): print (f"\n=== {label} ===" ) kwargs = {"Bucket" : BUCKET, "MaxKeys" : max_keys} if prefix is not None : kwargs["Prefix" ] = prefix response = client.list_objects_v2(**kwargs) print ("Name:" , response.get("Name" )) print ("Prefix:" , repr (response.get("Prefix" ))) print ("KeyCount:" , response.get("KeyCount" )) print ("IsTruncated:" , response.get("IsTruncated" )) contents = response.get("Contents" , []) if not contents: print ("No objects returned" ) return for obj in contents: print (obj["Key" ]) def main (): disable_proxies() client = create_client() dump_listing(client, f"agents/{WORKER_NAME} / 前缀" , prefix=f"agents/{WORKER_NAME} /" ) dump_listing(client, "shared/ 前缀" , prefix="shared/" ) if __name__ == "__main__" : try : main() except Exception as exc: print ("Error:" , type (exc).__name__, exc)
其中一个核心点是得到worker的minio的key,即本质上是如下命令:1 docker exec hiclaw-worker-<worker名称> env | grep HICLAW_FS_SECRET_KEY
如果是在另一台服务器上运行,建议显式传入 worker key:
1 2 3 export WORKER_NAME=aliceexport WORKER_SECRET_KEY='your-alice-secret-key' python3 test_remote_boto.py