云原生遥感处理

本文讲解云原生遥感处理的工程实践,覆盖 COG 与 Zarr 云原生格式的读取模式、STAC 目录的元数据检索、Google Earth Engine 的编程模型、无服务器批处理架构、并行策略与任务编排、成本模型与控制,以及云上处理与本地 GDAL 工作流的分工边界。

引言

传统遥感工作流把影像下载到本地磁盘,用 GDAL 打开、处理、再写回磁盘。这套模式在数据量小时没问题,但当分析对象从一个县扩大到一个国家、从一景扩大到十年存档时,下载与本地计算就变得不可行:PB 级数据下载耗时数周,本地磁盘与算力也撑不住。云原生处理把「数据放在云上、计算搬到数据旁边」作为核心原则,用支持随机访问的云优化格式与标准化的元数据目录,让分析只读取需要的那部分数据。

工程上的关键转变有三个。第一是读取模式,从「整景下载」变成「按窗口或按波段范围读取」,网络传输量从整景的几百兆降到几十兆。第二是计算位置,从「本地单机」变成「云上弹性批处理」,把任务切成可并行的瓦片分发出去。第三是元数据先行,用 STAC 这类目录先做空间与时间过滤,只对命中的资产发起读取,避免盲目遍历。

难点也很实在。云上处理的成本不是零,读取量、计算时长、出口流量都要计费,方案设计不当会烧掉大量预算;无服务器函数有超时与内存上限,不适合长任务;不同云平台的存储与计算耦合方式不同,迁移有成本;本地已有的 GDAL 脚本不能直接搬到云上,需要重构成分块并行。本文按「格式到目录到计算到成本」展开,格式与本地读写的细节见 GDAL 栅格读写 ,结果发布见 瓦片服务与切片金字塔 。

目录

  1. 云原生处理要解决的问题与边界
  2. 云原生栅格格式:COG 与 Zarr
  3. STAC 目录与元数据检索
  4. Google Earth Engine 的编程模型
  5. 无服务器批处理架构
  6. 并行策略与任务编排
  7. 成本模型与控制
  8. 与本地 GDAL 工作流的分工
  9. 工程实践与质量评估

1. 云原生处理要解决的问题与边界

先明确云原生处理适合什么、不适合什么。

适合的场景:数据量超过本地可承载(TB 到 PB 级)、分析需要跨大区域或长时序、任务可以切成独立瓦片并行、对时效有一定要求(近实时监测)、需要弹性应对突发峰值。

不适合的场景:数据量小且需反复迭代调试(本地更快更省)、算法高度依赖全局统计(如全图协方差,云上做全局聚合代价高)、需要复杂交互式调试、出口流量敏感且必须把原始数据留在本地。

本地 vs 云原生的判据
维度          本地 GDAL            云原生
数据规模      GB 到 TB             TB 到 PB
迭代速度      快(无网络)          慢(每次读取有延迟)
成本          硬件固定              按量计费,可控也可失控
并行         多核                   近乎无限弹性
适用          开发调试、小区域      大范围、长时序、生产化

实际工程里两者不是二选一,而是分工。开发阶段在本地用小样本调通算法,生产阶段把同一套逻辑搬到云上做全量。要做到这点,算法必须写成分块友好的形式:不依赖全局状态,每个瓦片独立处理,输入输出都是自包含的资产。

2. 云原生栅格格式:COG 与 Zarr

COG(Cloud Optimized GeoTIFF)是云原生栅格的事实标准。它在普通 GeoTIFF 基础上要求数据按瓦片(内部 tile)与金字塔(overview)组织,并把索引放在文件头部,使得一次 HTTP 范围请求就能读取任意窗口,而不必下载整个文件。

import rasterio

def read_window(url, bounds, out_shape=(512, 512)):
    # 通过 HTTP 范围请求只读取感兴趣窗口,不下载整景
    with rasterio.open(url) as src:
        window = rasterio.windows.from_bounds(*bounds, transform=src.transform)
        data = src.read(1, window=window, out_shape=out_shape, resampling=rasterio.enums.Resampling.bilinear)
    return data

COG 的关键参数是内部瓦片大小(常取 256 或 512)与 overview 层级。瓦片太小会产生大量小请求、增加延迟;太大则单次读取冗余数据多。overview 让缩略图与低分辨率分析只需读取少量数据。

Zarr 是另一类云原生格式,把数组切成块(chunk)并分块存储,每个块独立压缩。它天然支持多维(时间、波段、空间)与并行读写,特别适合时序立方体。

创建一个分块的时序立方体时,块的大小要同时考虑时间维与空间维,让常见的「按时间段取全区域」或「按区域取全时段」两类查询都能命中较少的块:

import zarr
import numpy as np

store = zarr.open_array(
    "s3://bucket/cube.zarr",
    mode="w",
    shape=(365, 1024, 1024),
    chunks=(30, 256, 256),        # 时间块 30,空间块 256
    dtype="float32",
)
store[0, :, :] = np.random.rand(1024, 1024).astype("float32")
COG 与 Zarr 的选型
维度        COG                  Zarr
维度支持    2D(多波段为 3D)    任意维度
最佳用途    单景影像、瓦片服务    时序立方体、多维数组
生态        GDAL、rasterio、QGIS  xarray、Dask、dask-image
并行读写    按窗口              按块,天然并行
压缩        逐瓦片               逐块,可指定编解码器

选型判据很简单:单景或少数几景的空间分析用 COG,多时相多维数组用 Zarr。二者可以共存,同一项目里影像资产用 COG 发布,分析用的时序立方体用 Zarr 存储。

3. STAC 目录与元数据检索

STAC(SpatioTemporal Asset Catalog)用一套 JSON 规范描述地理空间资产的时空范围与属性,让「先检索元数据、再按需读取数据」成为标准做法。核心对象有三层:Item(单个资产,如一景影像)、Collection(同类 Item 的集合)、Catalog(Collection 与 Item 的组织容器)。

一次典型的检索流程,先用空间、时间、属性三类条件过滤元数据,再取出命中资产的地址:

import os
from pystac_client import Client

catalog = Client.open(os.environ["STAC_API_URL"])   # 目录服务地址由环境变量注入
search = catalog.search(
    collections=["sentinel-2-l2a"],
    bbox=[116.0, 39.5, 117.0, 40.5],           # 北京附近
    datetime="2026-03-01/2026-06-30",
    query={"eo:cloud_cover": {"lt": 20}},       # 云量小于 20%
    limit=100,
)
items = list(search.items())
for it in items[:3]:
    print(it.id, it.datetime, it.properties.get("eo:cloud_cover"))
    print(it.assets["B04"].href)                # 红光波段的资产地址

STAC 的价值在于把空间、时间、属性三类过滤在元数据层完成,不需要碰影像数据本身。一个覆盖全球的存档可能有千万景影像,检索只返回命中的几十景,再把它们的资产地址交给计算任务。

STAC 常用扩展
扩展名                提供字段
eo                    云量、太阳角、平台
proj                  投影与变换
raster                波段与数据类型
view                  观测几何
sar                  极化与轨道方向
item-assets           多资产的分组

工程上要注意两点。第一,不同 STAC API 的查询能力不同,有的只支持基础的空间时间过滤,属性过滤要用 query 扩展,使用前先确认服务端支持哪些扩展。第二,资产地址可能是需要签名的链接,且有有效期,检索结果不能长期缓存地址,要用时再重新获取。

4. Google Earth Engine 的编程模型

Earth Engine(GEE)把全球公开遥感存档放在 Google 的基础设施上,提供一套惰性求值的编程模型。用户写的是「数据流图」,代码不会立即执行,而是在触发导出或显示时才在服务端并行计算。

// GEE 风格的时序合成:按区域与时间过滤,做云掩膜与中值合成
var s2 = ee.ImageCollection('COPERNICUS/S2_SR_HARMONIZED')
  .filterBounds(roi)
  .filterDate('2026-03-01', '2026-06-30')
  .filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', 20));

var masked = s2.map(function (img) {
  var scl = img.select('SCL');
  var mask = scl.neq(9).and(scl.neq(8)).and(scl.neq(3)).and(scl.neq(10));  // 去云与阴影
  return img.updateMask(mask).divide(10000);                                // 转反射率
});

var composite = masked.select(['B4', 'B8']).median();
var ndvi = composite.normalizedDifference(['B8', 'B4']).rename('NDVI');

Export.image.toDrive({ image: ndvi, region: roi, scale: 10, maxPixels: 1e13 });

GEE 的优势是零运维、数据即取即用、全球尺度分析开箱可用。限制也很明确:只能用它提供的算子,自定义算法要用 ee.Image 的组合表达,复杂的机器学习与深度学习支持有限;导出有配额与速率限制;代码在服务端运行,调试不如本地方便。

GEE 与自建云原生流水线的分工:探索性分析、全球尺度统计、快速原型用 GEE;需要自定义模型、精细控制、数据私有、成本可控的生产系统用自建流水线。很多团队的做法是先用 GEE 快速验证思路,再把逻辑移植到基于 STAC 与 COG 的自建系统。

4.1 GEE 的配额与替代方案

GEE 对免费用户与商业用户有不同配额,导出任务有并发与总量限制,超限会排队甚至失败。生产系统若依赖 GEE,必须把这些限制纳入容量规划,并准备降级方案。

GEE 常见限制与应对
限制项              影响              应对
导出并发数          大批量导出排队    分批次提交、错峰执行
单任务像素上限      大区域一次导不出   分块导出再合并
计算时长            复杂任务超时      简化算子、预计算中间结果
资产数量            私有资产有上限    定期清理、只留必要资产

替代方案有三类:自建 STAC 加 COG 流水线(最灵活)、其他云厂商的地理空间平台、开源的 OpenEO 兼容后端。选择取决于是否需要私有数据、是否需要自定义模型、以及团队对运维的接受度。无论哪种,把「先用 GEE 验证、再按需迁移」作为默认策略,能最大化利用 GEE 的低门槛优势而不被其限制锁死。

5. 无服务器批处理架构

无服务器(Serverless)把计算变成按调用计费的函数,适合把大任务切成小块并行。典型架构:

无服务器遥感流水线
1. 调度器按瓦片或按影像生成任务清单
2. 消息队列分发任务
3. 函数并发执行:读取 COG/Zarr 瓦片、计算、写出结果
4. 结果写入对象存储,元数据写入目录或数据库
5. 全部完成后触发后处理与发布

一份函数配置的关键项包括超时、内存、临时存储与环境变量,下面用示意配置说明:

function:
  handler: process_tile.handler
  runtime: python3.11
  timeout: 900
  memory: 3008
  ephemeral_storage: 4096
  environment:
    GDAL_DISABLE_READDIR_ON_OPEN: EMPTY_DIR
    GDAL_HTTP_MULTIPLEX: "YES"
    GDAL_HTTP_MERGE_CONSECUTIVE_RANGES: "YES"
  concurrency_limit: 100

几个环境变量直接影响云上 GDAL 的性能。GDAL_DISABLE_READDIR_ON_OPEN 避免 GDAL 打开文件时列目录(在对象存储上这个操作很慢且可能返回大量无关请求);GDAL_HTTP_MULTIPLEX 与 GDAL_HTTP_MERGE_CONSECUTIVE_RANGES 让多个范围请求复用连接并合并相邻请求,显著减少延迟与请求数。

无服务器的约束要提前考虑。函数有超时上限(常 15 分钟),单瓦片任务必须能在限时内完成;内存上限决定了能一次处理的瓦片大小;冷启动会带来额外延迟,对时效敏感的任务要保持函数预热;并发上限需要向平台申请,否则大批量任务会排队。

容器化批处理(如 AWS Batch、Kubernetes Job)是无服务器的替代方案,适合单任务重、依赖复杂、需要自定义环境(如深度学习框架)的场景。它没有函数的时间限制,但需要管理集群,运维成本更高。

6. 并行策略与任务编排

云原生处理的核心是把问题分解成可并行的独立任务。遥感任务的天然并行维度有两个:空间(按瓦片切)与时间(按时相切)。

import itertools

def gen_tiles(bounds, tile_size_deg=0.5):
    # 按经纬度切网格,生成任务清单
    minx, miny, maxx, maxy = bounds
    xs = [minx + i * tile_size_deg for i in range(int((maxx - minx) / tile_size_deg) + 1)]
    ys = [miny + j * tile_size_deg for j in range(int((maxy - miny) / tile_size_deg) + 1)]
    for x, y in itertools.product(xs, ys):
        yield (x, y, min(x + tile_size_deg, maxx), min(y + tile_size_deg, maxy))

任务切分要避免两类问题。第一是瓦片过大导致单任务超时,或过小导致调度开销与冷启动占比过高;经验是单瓦片处理时间控制在几十秒到几分钟。第二是边界效应,相邻瓦片如果各自独立做空间运算(如滤波、拼接),边界处会失真,需要重叠切分并在后处理时裁剪。

编排工具的选择:简单流水线用云平台自带的工作流服务(如 Step Functions、Cloud Workflows);复杂依赖图用 Airflow 或 Argo Workflows;纯数据并行用 Dask 或 Ray。选择依据是依赖复杂度与团队熟悉度,不必追求最先进的工具。

并行维度与适用场景
维度        切法            适用            注意
空间        按瓦片网格      区域制图        边界重叠
时间        按时相          时序合成        时相间可能需对齐
波段        按波段组        高光谱处理      波段间可能需协方差
场景        按影像          单景处理        负载不均

负载不均是常见问题。不同瓦片的计算量可能差异很大(云多的地方掩膜与填补更费时,城市区域分类更复杂),均匀切分会导致部分任务拖尾。对策是按历史耗时做动态调度,或把瓦片切得更细以平滑差异。

7. 成本模型与控制

云上成本由三部分构成:存储、计算、流量。遥感任务通常是计算与流量占大头,存储相对便宜。

成本构成与优化手段
成本项      计费方式        优化手段
对象存储    按存储量与请求   合理分层,冷数据转低频
计算        按时长与规格     选对规格、避免空转、用竞价实例
出口流量    按传出量         计算与数据同区、避免跨区、减少下载
请求        按请求次数       合并范围请求、加大瓦片、缓存

控制成本的关键做法:

  • 计算与数据同区域部署,跨区流量费用高且延迟大。
  • 用 COG 的窗口读取与 overview,只读需要的数据,避免整景下载。
  • 用竞价实例或抢占式实例跑容错批处理,成本可降一半以上。
  • 设置预算告警与并发上限,防止失控的任务把预算烧光。
  • 缓存中间结果,避免重复计算(同一区域多次分析时尤其重要)。
  • 用 overview 做粗分析,只在需要精细结果时才读全分辨率。

成本估算要在方案设计阶段就做,而不是上线后才发现。粗估公式:总成本约等于「总读取量 乘 单位流量价 加 总计算时长 乘 单位时长价」。把这两个量按瓦片数与单瓦片读取量算出来,就能判断方案是否可行。

一个常被忽视的成本源是元数据检索。频繁查询 STAC API 或遍历大量资产会累积请求费用,且延迟高。做法是把常用检索结果缓存到本地数据库或索引,用 数据库 的空间索引加速后续查询,把「每次分析都重新检索」变成「检索一次、多次复用」。

8. 与本地 GDAL 工作流的分工

云原生不是要取代本地 GDAL,而是要与之分工。GDAL 仍是理解与处理栅格的底层工具,rasterio、xarray 都建立在它之上。

分工建议:

阶段本地 GDAL云原生
算法开发小样本快速迭代不适合
参数调试交互式、即时反馈不适合
全量生产数据量小时可用大范围首选
结果质检抽查、可视化抽样到本地检查
发布服务小规模静态动态瓦片服务

要让同一套算法在两端跑通,关键是抽象出与存储无关的读取接口。本地读本地文件,云上读 HTTP 或对象存储的 COG,上层算法代码不变。

import rasterio
from rasterio.env import Env

def open_any(path_or_url):
    # 同一套读取代码,本地路径与云上 URL 都走 rasterio
    env = Env(GDAL_DISABLE_READDIR_ON_OPEN="EMPTY_DIR", GDAL_HTTP_MULTIPLEX="YES")
    return rasterio.open(path_or_url, mode="r"), env

这种抽象让开发在生产同一份代码上完成,减少「本地能跑云上不能跑」的返工。注意本地与云上的性能特征不同:本地磁盘延迟低但容量有限,云上容量无限但延迟高,因此云上要更积极地用分块、缓存与 overview,而本地可以更随意地整景读取。

数据格式的转换成本也要考虑。把历史存档批量转成 COG 是一次性投入,之后所有云上读取都受益;但如果数据只被读取一两次,转换的存储与计算成本可能超过收益。判断依据是数据的复用次数:高频复用的存档值得转 COG,一次性的中间结果不必。

9. 工程实践与质量评估

云原生流水线的质量评估除了结果精度,还要关注工程指标。

云原生流水线的关键指标
类别        指标                目标
正确性      处理成功率、重试率   成功率大于 99%
性能        单瓦片耗时、端到端时长 按 SLA 定
成本        单瓦片成本、总成本    在预算内
可观测性    日志、指标、追踪      可定位失败任务
数据质量    输出完整率、空值率    无缺失瓦片

可观测性常被忽视。一个跑几千个任务的流水线,没有日志与指标就无法定位是哪个瓦片失败、失败在读取还是计算。做法是每个任务输出结构化日志(任务 ID、输入资产、耗时、错误),把指标汇总到监控面板,把失败任务自动重试并隔离。

幂等性是云原生任务的必备性质。任务因超时或故障重试时,重复执行不能产生重复或错误的结果。做法是输出路径由输入参数唯一决定,写入时用临时路径加原子重命名,或先检查输出是否存在再决定是否计算。

数据一致性也要保证。多个任务并行写出结果后,要有一步汇总校验:检查所有瓦片是否齐全、是否有空瓦片、边界处是否对齐。这一步能抓出静默失败(任务成功但输出为空)这类最难排查的问题。校验通过后再触发发布,避免半成品上线。

与下游的衔接上,云原生处理的输出通常是 COG 或 Zarr 资产加一份 STAC 目录,供瓦片服务或分析任务消费。把结果按 STAC 组织,能让下游用统一的检索接口发现数据,这与 遥感系统概览 里的端到端链路设计一致。

9.1 从原型到生产的迁移路径

把本地方案迁到云上,建议按四步走,每一步都保持可回退。

第一步,格式统一。把输入数据转成 COG 或 Zarr,建立 STAC 目录,这一步不改算法,只改数据的组织方式。第二步,接口抽象。把读取层从「打开本地文件」改为「打开任意路径或 URL」,上层算法不动。第三步,任务化。把单机脚本改写成「输入一个瓦片参数、输出一个瓦片结果」的纯函数,去掉全局状态。第四步,编排与监控。接入队列与函数执行,补上日志、指标、重试与幂等。

迁移检查清单
项目            本地          云上            验证方式
读取            本地文件      窗口读 COG      对比同窗口输出
算法            单机脚本      纯函数           同参数输出一致
输出            写本地        写对象存储       校验完整率
失败处理        人工重跑      自动重试加幂等   注入故障测试
成本            固定           按量             单瓦片成本核算

迁移过程中最容易出错的是「本地能跑云上不能跑」,根源通常是路径假设、时区与临时目录差异、以及并发下的资源竞争。用同一份算法代码、只在配置层区分环境,能把这类问题降到最低。

权衡取舍

  • 本地 vs 云原生:本地快、免费、易调试但容量有限;云上弹性、容量无限但按量计费、延迟高,按数据规模与迭代频率选。
  • COG vs Zarr:单景空间分析用 COG,多维时序立方体用 Zarr,二者可共存。
  • GEE vs 自建:GEE 零运维、数据现成但算子受限、成本不透明;自建灵活可控但需运维,按定制化需求选。
  • 无服务器 vs 容器批处理:无服务器运维轻但有时长与内存限制;容器无限制但需管集群,按任务特征选。
  • 竞价实例 vs 按需实例:竞价便宜但可能被回收,需任务可重试;按需稳定但贵。
  • 粗读 vs 全读:overview 粗读快而省,全分辨率精读准而贵,按精度需求分级读取。
  • 缓存 vs 实时计算:缓存省计算但有失效与一致性问题,高频复用才值得。

常见坑清单

  • 整景下载再处理:现象是流量与时间成本极高,原因是没利用 COG 的窗口读取,规避方法是用窗口与 overview 只读所需数据。
  • 不开 GDAL 的云优化环境变量:现象是打开文件极慢、请求数暴涨,原因是 GDAL 在对象存储上列目录,规避方法是设置 DISABLE_READDIR_ON_OPEN 与 HTTP 多路复用。
  • 计算与数据跨区域:现象是流量费用高、延迟大,原因是读写跨区,规避方法是把计算部署到数据所在区域。
  • 瓦片过大导致函数超时:现象是任务频繁失败重试,原因是单瓦片超出函数时限,规避方法是缩小瓦片或改用容器批处理。
  • 无预算告警:现象是任务失控烧掉预算,原因是缺少成本监控,规避方法是设置预算告警与并发上限。
  • 任务不幂等:现象是重试后结果重复或错误,原因是输出路径不唯一,规避方法是让输出路径由输入唯一决定并用原子写入。
  • 忽略瓦片边界效应:现象是拼接处出现接缝,原因是独立处理未做重叠,规避方法是重叠切分后裁剪。
  • 频繁检索 STAC 不缓存:现象是请求费用高、分析启动慢,原因是每次都重新检索,规避方法是缓存检索结果到本地索引。
  • 只看成功率不看输出质量:现象是任务成功但输出为空或全为 NoData,原因是静默失败未被检测,规避方法是加输出完整率校验。
  • 把 GEE 逻辑直接当生产系统:现象是导出配额受限、自定义模型无法实现,原因是 GEE 算子受限,规避方法是原型用 GEE、生产迁移到自建流水线。

小结

云原生遥感处理的核心是三个转变:数据格式从「文件」变为「可随机访问的云优化资产」,检索从「遍历文件系统」变为「查询元数据目录」,计算从「本地单机」变为「弹性并行」。COG 与 Zarr 解决了随机访问,STAC 解决了元数据检索,无服务器与容器批处理解决了弹性计算,成本控制则决定了方案能否长期运行。

落地建议分三步走。第一步,把核心数据转成 COG 或 Zarr 并建立 STAC 目录,这一步是一次性投入,之后所有分析都受益。第二步,在本地用小样本调通算法,把算法写成分块友好、存储无关的形式。第三步,把同一套逻辑搬到云上,用无服务器或容器并行执行,配好监控、幂等与成本告警。不要一上来就追求全云化,本地开发与云上生产的组合往往是效率与成本的最优解。

下一步可以对照 GDAL 栅格读写 理解 COG 的底层结构与本地读取细节,把云上处理的结果交给 瓦片服务与切片金字塔 发布为可浏览服务,并用 遥感系统概览 里的端到端链路视角检查云原生方案与整体架构的衔接。

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「遥感与空间数据」更多文章

  1. 卫星平台与任务规划
  2. 高光谱遥感处理
  3. 遥感时序分析