분산 학습 데이터가 조작되지 않았다는 걸 어떻게 증명할까: 블록체인 앵커링과 MLOps

분산·연합학습 환경에서 데이터·모델 무결성을 온체인 해시로 앵커링하고 서명된 Docker 이미지로 배포하는 MLOps 파이프라인 설계를 정리한다.

2026-08-12 · 최초 발행 2025-10-31

연합학습처럼 여러 조직이 각자의 데이터로 하나의 모델을 학습시키는 환경에서는 "이 데이터셋이 학습 시점 이후 바뀌지 않았다"는 사실을 증명할 방법이 필요하다. 감사가 들어오거나 모델 성능에 이상이 생겼을 때 특정 시점의 데이터·모델·파이프라인 조합을 그대로 재현할 수 있어야 하기 때문이다. 블록체인 앵커링과 MLOps를 결합하면 이 문제를 데이터 무결성 증적, 모델 서명·검증, Dockerized 노드 배포라는 세 가지 조각으로 풀 수 있다.

온체인에는 해시만, 바이너리는 오프체인에

핵심 아이디어는 단순하다. 데이터·모델·파이프라인 산출물의 해시(머클 루트 등)를 온체인에 기록해 변경 불가능한 증적을 남기되, 실제 바이너리는 S3/MinIO, IPFS, OCI 레지스트리 같은 오프체인 저장소에 둔다. 온체인은 메타데이터·해시·서명·버전만 관리한다.

MLOps 쪽에서는 DVC, MLflow, Kubeflow 같은 실험·모델 레지스트리를 이 블록체인 앵커와 연계해 이력의 불변성을 강화한다. CI/CD가 모델을 컨테이너로 패키징하고 서명한 뒤, 배포 시점에 블록체인 레지스트리에 기록된 해시와 교차 검증하는 식이다.

Dockerized 노드 배포 층에서는 학습자(Trainer), 집계자(Aggregator), 서빙(Serving) 노드를 컨테이너로 표준화하고 이미지 서명·정책 검증으로 공급망을 보호한다. 노드가 부팅될 때 온체인 해시로 데이터·모델·이미지를 검증한 뒤에야 가동을 시작한다.

무결성 앵커가 실제로 하는 일

입력 데이터셋·샤드는 파일 단위 해시와 머클 루트를 생성해 온체인에 등록하고 버전 태그를 붙인다. 모델 아티팩트(가중치, ONNX, TorchScript)와 컨테이너 이미지 다이제스트(OCI digest)도 서명해 앵커에 연결한다. 대용량 바이너리는 오프체인에 저장하고 온체인은 해시·위치·서명자만 관리하는데, 체인에 장애가 나도 오프체인 캐시로 버틸 수 있게 재동기화 잡을 따로 둔다.

파이프라인 자체는 트레이너 노드가 데이터 샤드를 다운로드해 해시를 검증하고 로컬 학습 후 업데이트에 서명해 전송하는 흐름, 집계자가 그 서명과 정합성을 검증하고 집계한 뒤 모델 산출물의 해시·서명을 온체인에 커밋하는 흐름으로 나뉜다. 컨테이너 쪽에서는 Sigstore cosign 같은 이미지 서명, SBOM, 취약점 스캔을 통과한 이미지만 배포하고, Canary/Rollback 기준을 온체인 메타데이터 버전과 연동해 승인 워크플로우까지 기록한다. 퍼미션드 체인(Hyperledger Fabric 등)을 쓰는 경우 조직별 MSP/PKI로 접근을 제어하고, 합의 지연과 가용성을 고려해 앵커 기록은 배치로 처리하며 읽기 경로는 오프체인 캐시로 최적화한다.

데이터 업로드부터 롤아웃까지

전체 흐름을 트랜잭션·에러 처리까지 포함해 그리면 다음과 같다.

Registry/DeployerAggregator (Docker)Trainer Node (Docker)Blockchain RegistryOff-chain Store (S3/IPFS/OCI)Hasher/MerkleProducerRegistry/DeployerAggregator (Docker)Trainer Node (Docker)Blockchain RegistryOff-chain Store (S3/IPFS/OCI)Hasher/MerkleProducer실패 시 재시도최대 N회 후 경보 발행데이터셋 샤드 업로드파일 해시/머클루트 계산바이너리 업로드루트 해시+메타데이터 온체인 커밋(tx)최신 데이터셋 루트 해시 조회샤드 다운로드머클 증명 검증(불일치 시 격리)업데이트 전송(서명 포함)업데이트 서명·이상치 검증집계 및 모델 산출물 생성모델 아티팩트/이미지 푸시모델 해시/이미지 다이제스트 커밋(tx)승인된 버전 알림이미지 서명 검증(cosign)롤아웃 트리거(Canary→전면)기동 시 온체인 해시 재검증

에러는 세 갈래로 처리한다. 온체인 커밋이 실패하면 지수형 백오프로 재시도하다가 Dead-letter 큐로 넘기고 운영 알림을 띄운다. 해시가 불일치하면 해당 샤드를 격리하고 재다운로드하며, 반복해서 불일치하면 데이터 원천 자체를 차단한다. 모델 롤아웃이 실패하면 에러율·SLA 기준을 충족하는 즉시 이전 버전으로 자동 롤백한다.

중앙 레지스트리만 쓸 때와 뭐가 다른가

전략 성능 확장성 일관성 안정성 운영 편의
중앙 레지스트리만 사용 매우 우수 우수 변경 추적 취약 단일 장애점 존재 가장 단순
블록체인 앵커링(권장) 우수 우수 강한 감사·불변성 합의지연 영향 경미 중간(자동화 필요)
완전 온체인 저장 낮음 낮음 최고(이론상) 체인 비용·용량 한계 낮음

중앙 레지스트리만 쓰면 성능·단순함은 최고지만 변경 이력을 누가 언제 바꿨는지 추적하기 어렵고 단일 장애점이 생긴다. 반대로 완전 온체인 저장은 이론상 최고 수준의 일관성을 주지만 체인 비용과 용량 한계 때문에 현실적이지 않다. 오프체인 저장 + 온체인 메타데이터 앵커링이 실무에서 균형점으로 꼽히는 이유다.

어디에 쓰이나

의료 연합학습: 병원마다 데이터를 로컬에 보관하고, 샤드의 머클 루트만 온체인에 앵커링해 원본 변경을 탐지한다. 모델 배포는 서명된 Docker 이미지로 각 병원 내 프록시를 거쳐 점진적으로 롤아웃한다.

금융 사기 탐지: 지점·국가별 업데이트에 서명을 강제하고, 집계 전 이상 업데이트를 차단한다. 규제 감사가 들어오면 특정 버전의 데이터·모델·파이프라인 조합을 온체인 증적으로 재현할 수 있다.

엣지/IoT 예지보전: 네트워크가 간헐적으로만 연결되는 환경에서는 로컬에 캐시해뒀다가 나중에 일괄로 앵커 동기화한다. 경량 컨테이너와 이미지 서명 검증으로 무단 펌웨어·모델 교체를 막는다.

코드로 보는 앵커링 흐름

전제: Docker 24+/Compose v2, Python 3.10+, cosign 2.x, OpenSSL, 로컬 이더리움 개발체인(Ganache) 또는 퍼미션드 체인(Fabric 테스트넷) 중 택1.

데이터셋 머클 루트를 계산해 온체인에 커밋하는 예시(pip install web3 py-solc-x eth-account 전제):

# anchor_dataset.py
# Python 3.10, web3>=6, py-solc-x>=1.1
import json, glob, hashlib, os
from web3 import Web3
from solcx import compile_source, install_solc

SOLC = "0.8.20"
install_solc(SOLC)

SRC = """
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract Anchor {
    event Anchored(bytes32 key, uint256 version, bytes32 merkleRoot, string uri);
    struct Entry { uint256 version; bytes32 root; string uri; }
    mapping(bytes32 => Entry) public registry;
    function commit(bytes32 key, bytes32 merkleRoot, string calldata uri) external {
        uint256 v = registry[key].version + 1;
        registry[key] = Entry(v, merkleRoot, uri);
        emit Anchored(key, v, merkleRoot, uri);
    }
    function get(bytes32 key) external view returns (uint256, bytes32, string memory) {
        Entry memory e = registry[key];
        return (e.version, e.root, e.uri);
    }
}
"""

def merkle_root(files):
    # leaves: sha256(file_bytes)
    hashes = []
    for f in sorted(files):
        with open(f, "rb") as fh:
            hashes.append(hashlib.sha256(fh.read()).digest())
    if not hashes:
        return hashlib.sha256(b"").hexdigest()
    while len(hashes) > 1:
        it = []
        for i in range(0, len(hashes), 2):
            a = hashes[i]
            b = hashes[i+1] if i+1 < len(hashes) else a
            it.append(hashlib.sha256(a + b).digest())
        hashes = it
    return "0x" + hashes[0].hex()

def main():
    # 1) 해시 계산
    files = glob.glob("data/**/*.parquet", recursive=True)
    root = merkle_root(files)
    key = Web3.keccak(text="dataset:v1")
    uri = "s3://bucket/dataset/v1/"  # 또는 ipfs://CID

    # 2) 체인 연결 및 배포
    w3 = Web3(Web3.HTTPProvider("http://localhost:8545"))
    acct = w3.eth.account.from_key("0x59c6995e998f97a5a0044976f...REPLACE_DEV_KEY")
    compiled = compile_source(SRC, output_values=["abi", "bin"], solc_version=SOLC)
    _, c = compiled.popitem()
    Anchor = w3.eth.contract(abi=c["abi"], bytecode=c["bin"])
    tx = Anchor.constructor().build_transaction({"from": acct.address, "nonce": w3.eth.get_transaction_count(acct.address)})
    signed = acct.sign_transaction(tx)
    receipt = w3.eth.send_raw_transaction(signed.rawTransaction)
    addr = w3.eth.wait_for_transaction_receipt(receipt).contractAddress
    print("Contract:", addr)

    # 3) 커밋
    anchor = w3.eth.contract(address=addr, abi=c["abi"])
    tx2 = anchor.functions.commit(key, Web3.to_bytes(hexstr=root), uri).build_transaction({
        "from": acct.address, "nonce": w3.eth.get_transaction_count(acct.address)
    })
    signed2 = acct.sign_transaction(tx2)
    r2 = w3.eth.send_raw_transaction(signed2.rawTransaction)
    print("Anchored:", w3.eth.wait_for_transaction_receipt(r2).transactionHash.hex(), root, uri)

if __name__ == "__main__":
    main()

실행 전에는 docker run -p 8545:8545 trufflesuite/ganache로 Ganache를 띄우고, data/** 폴더에 샘플 파일을 두고, 예시의 개발용 개인키는 실제 키로 교체해야 한다. 트레이너는 시작 시 컨트랙트의 get(key)를 호출해 루트 해시와 로컬 머클 증명을 비교하고 불일치하면 종료하는 방식으로 검증한다.

트레이너·집계자·서빙을 Docker Compose로 묶으면 이런 형태다.

version: "3.9"
services:
  chain:
    image: trufflesuite/ganache
    ports: ["8545:8545"]

  trainer1:
    build: ./trainer
    environment:
      - CHAIN_URL=http://chain:8545
      - DATA_URI=s3://bucket/dataset/v1/
      - ANCHOR_KEY=dataset:v1
    depends_on: [chain]
  trainer2:
    build: ./trainer
    environment:
      - CHAIN_URL=http://chain:8545
      - DATA_URI=s3://bucket/dataset/v1/
      - ANCHOR_KEY=dataset:v1
    depends_on: [chain]

  aggregator:
    build: ./aggregator
    environment:
      - CHAIN_URL=http://chain:8545
    depends_on: [trainer1, trainer2, chain]

  serving:
    build: ./serving
    ports: ["8080:8080"]
    environment:
      - CHAIN_URL=http://chain:8545
      - MODEL_KEY=model:fraud-v5
    depends_on: [aggregator, chain]

trainer 컨테이너의 Dockerfile은 단순하다.

FROM python:3.10-slim
RUN pip install web3 boto3
COPY trainer.py /app/trainer.py
WORKDIR /app
CMD ["python", "trainer.py"]

trainer.py의 핵심 로직은 온체인 루트 조회 → 오프체인 데이터 다운로드 → 머클 검증 → 미니배치 학습 → 업데이트 서명 후 집계자 전송 순이다. aggregator는 업데이트 서명을 검증하고 집계한 뒤 모델 파일을 만들어 OCI 레지스트리에 푸시하고, 이미지 다이제스트·모델 해시를 온체인에 커밋한다. serving은 기동 시 온체인 모델 해시와 로컬 모델 파일 해시가 일치하는지 확인한 뒤에야 FastAPI 서비스를 시작한다.

이미지 서명·검증은 cosign으로 한다.

cosign sign --key cosign.key registry.example.com/ml/serving:fraud-v5
cosign verify --key cosign.pub registry.example.com/ml/serving:fraud-v5

Kubernetes에 배포할 때는 Kyverno나 OPA Gatekeeper 같은 정책으로 미서명 이미지를 거부하도록 강제한다.

어디까지 온체인에 둘 것인가

해시 체계는 SHA-256으로 표준화하고, 머클 증명을 저장하며, 컨테이너 이미지 다이제스트는 고정(pin)해두는 게 기본이다. 퍼미션드 체인을 쓴다면 조직별 MSP와 체인코드 접근 제어(Role/Attribute 기반)를 두고, 앵커 기록은 배치 처리·읽기 경로는 캐시 계층화로 합의 지연을 줄인다. 모델 업데이트 서명은 ECDSA/Ed25519로 강제하고 이상치 탐지(Robust aggregation)를 함께 적용한다.

다만 체인 가용성·합의 지연에 따라 배포 대기 시간이 늘어날 수 있고, 온체인에 얼마나 많은 정보를 남길지는 데이터 최소화와 감사성 사이에서 균형을 잡아야 한다. 운영 복잡도가 늘어나는 부분은 IaC와 파이프라인 표준화로 상쇄하는 방향이 현실적이다. 무결성·감사성 측면에서는 데이터·모델 위변조 탐지 확률이 크게 늘고 내부자 위협이 억제되며, 온체인 인덱싱 기준으로 변경 이력 재현 시간이 50% 이상 단축된다는 보고가 있다. 운영 신뢰도 측면에서는 서명·정책 기반 배포로 잘못된 모델 롤아웃 사고율이 줄고, Canary와 자동 롤백 조합으로 MTTD/MTTR이 3060% 개선된다. 다만 해시·서명 오버헤드가 25%, 합의 지연이 수백 ms~수초 추가되는 비용은 감수해야 한다.

블록체인MLOps데이터무결성연합학습모델배포