Raspberry Pi 엣지 배포
이 튜토리얼에서는 Caffeine Bridge.Host를 Raspberry Pi에 배포하여 현장 장비 데이터를 수집하는 엣지 게이트웨이를 구성합니다.
개요
| 단계 | 주제 | 소요 시간 |
|---|---|---|
| 1 | 개발 환경 준비 | 5분 |
| 2 | ARM 크로스 컴파일 | 10분 |
| 3 | 드라이버 설정 | 10분 |
| 4 | Raspberry Pi 배포 | 5분 |
| 5 | systemd 서비스 등록 | 5분 |
| 6 | Offline Buffer 설정 | 10분 |
사전 요구 사항:
- Tutorial 02: 첫 번째 드라이버 완료
- .NET 10 SDK (개발 PC)
- Raspberry Pi 4 이상 (ARM64, 4GB+ RAM 권장)
- Raspberry Pi OS 64-bit (Bookworm)
- SSH 접근 설정 완료
학습 목표:
- .NET 애플리케이션을 ARM64로 크로스 컴파일할 수 있다
- Caffeine Bridge.Host를 Raspberry Pi에서 서비스로 운영할 수 있다
- 네트워크 단절 시 Offline Buffer로 데이터 무손실을 보장할 수 있다
1단계: 개발 환경 준비
아키텍처 이해
Bridge.Host는 현장 장비와 직접 통신하며, 수집된 데이터를 클라우드의 Caffeine Engine으로 전송합니다. 네트워크가 불안정한 환경에서는 SQLite 기반 Offline Buffer가 데이터를 로컬에 보관합니다.
Raspberry Pi 확인
# SSH로 Raspberry Pi 접속
ssh pi@raspberrypi.local
# OS 아키텍처 확인 (arm64 또는 aarch64)
uname -m
# 사용 가능한 메모리 확인
free -h
# .NET이 이미 설치되어 있는지 확인
dotnet --version 2>/dev/null || echo ".NET 미설치 (self-contained 배포 예정)"
Raspberry Pi에 .NET SDK를 설치할 필요 없습니다. Self-contained 배포를 사용하면 .NET 런타임이 앱과 함께 배포됩니다.
2단계: ARM64 Docker 이미지 배포 (권장)
Caffeine Bridge.Host는 ARM64 Docker 이미지로 제공됩니다:
# Raspberry Pi에서 직접 실행
docker run -d --name bridge-host \
-p 5100:5100 \
--restart unless-stopped \
nexcode/caffeine-bridge-host:latest-arm64
Docker Compose 사용
services:
bridge-host:
image: nexcode/caffeine-bridge-host:latest-arm64
ports:
- "5100:5100"
volumes:
- ./driver_settings.json:/app/driver_settings.json
- ./drivers:/app/drivers
restart: unless-stopped
Self-contained 배포 (Docker 없이)
Docker를 사용할 수 없는 환경에서는 Self-contained 바이너리를 직접 배포할 수 있습니다. ARM64 빌드 패키지는 기술 지원을 통해 제공받을 수 있습니다.
| 옵션 | 설명 | 효과 |
|---|---|---|
| ARM64 바이너리 | Raspberry Pi 호환 | Pi에 .NET 설치 불필요 |
| Self-contained | .NET 런타임 포함 | 단일 실행 파일 (~40MB) |
3단계: 드라이버 설정
driver_settings.json
Bridge.Host가 어떤 드라이버를 로드하고 장비와 어떻게 통신할지 정의합니다:
{
"Driver": {
"Id": "pi-modbus-01",
"Type": "Modbus",
"Port": 502,
"IpAddress": "192.168.1.100",
"Settings": {
"Protocol": "TCP",
"SlaveId": 1,
"PollIntervalMs": 1000,
"Tags": [
{ "Name": "Temperature", "Address": "40001", "Type": "Float" },
{ "Name": "Pressure", "Address": "40003", "Type": "Float" },
{ "Name": "FlowRate", "Address": "40005", "Type": "Float" },
{ "Name": "MotorStatus", "Address": "00001", "Type": "Boolean" }
]
}
}
}
시뮬레이션 모드로 먼저 테스트
실 장비 연결 전에 시뮬레이션 드라이버로 테스트합니다:
{
"Driver": {
"Id": "pi-sim-01",
"Type": "Simulation",
"Settings": {
"DeviceCount": 2,
"TagsPerDevice": 5,
"UpdateIntervalMs": 1000,
"Scenario": "Normal"
}
}
}
환경변수로 오버라이드
배포 환경별로 설정을 변경할 때는 환경변수를 사용합니다:
# 장비 IP를 환경변수로 오버라이드
export Driver__IpAddress=192.168.2.50
export Driver__Port=5020
4단계: Raspberry Pi 배포
파일 전송
# 개발 PC에서 실행
scp -r ./publish/linux-arm64/* pi@raspberrypi.local:/opt/caffeine/bridge/
디렉토리 구조
/opt/caffeine/bridge/
├── Caffeine.Bridge.Host ← 실행 파일
├── appsettings.json ← 앱 설정
├── driver_settings.json ← 드라이버 설정
├── drivers/ ← 동적 로드 드라이버 DLL
│ ├── Caffeine.Drivers.Modbus.dll
│ └── Caffeine.Drivers.Simulation.dll
└── data/ ← Offline Buffer DB
└── offline_buffer.db
실행 테스트
# Raspberry Pi에서 실행
ssh pi@raspberrypi.local
cd /opt/caffeine/bridge
chmod +x Caffeine.Bridge.Host
./Caffeine.Bridge.Host
# 정상 출력 예시:
# [INF] Caffeine Bridge.Host starting...
# [INF] Driver loaded: pi-sim-01 (Simulation)
# [INF] Listening on: http://0.0.0.0:5060
# [INF] Health check: http://0.0.0.0:5060/health
Health Check 확인
curl http://localhost:5060/health
# {"status":"Healthy","driver":"Connected","uptime":"00:01:23"}
5단계: systemd 서비스 등록
서비스 파일 생성
sudo nano /etc/systemd/system/caffeine-bridge.service
[Unit]
Description=Caffeine Bridge.Host Edge Gateway
After=network-online.target
Wants=network-online.target
[Service]
Type=notify
ExecStart=/opt/caffeine/bridge/Caffeine.Bridge.Host
WorkingDirectory=/opt/caffeine/bridge
Restart=on-failure
RestartSec=10
User=pi
Environment=DOTNET_ENVIRONMENT=Production
Environment=ASPNETCORE_URLS=http://0.0.0.0:5060
# 리소스 제한 (Raspberry Pi 보호)
MemoryMax=512M
CPUQuota=80%
# 로그
StandardOutput=journal
StandardError=journal
SyslogIdentifier=caffeine-bridge
[Install]
WantedBy=multi-user.target
서비스 활성화 및 시작
# 서비스 등록
sudo systemctl daemon-reload
sudo systemctl enable caffeine-bridge
# 서비스 시작
sudo systemctl start caffeine-bridge
# 상태 확인
sudo systemctl status caffeine-bridge
# 실시간 로그 확인
journalctl -u caffeine-bridge -f
서비스 관리 명령어
| 명령어 | 설명 |
|---|---|
systemctl start caffeine-bridge | 서비스 시작 |
systemctl stop caffeine-bridge | 서비스 중지 |
systemctl restart caffeine-bridge | 서비스 재시작 |
systemctl status caffeine-bridge | 상태 확인 |
journalctl -u caffeine-bridge -f | 실시간 로그 |
journalctl -u caffeine-bridge --since "1 hour ago" | 최근 1시간 로그 |
6단계: Offline Buffer 설정
왜 Offline Buffer가 필요한가?
제조 현장의 네트워크는 불안정할 수 있습니다:
- 공장 Wi-Fi 간헐적 단절
- VPN 터널 재연결 지연
- 네트워크 장비 유지보수
Offline Buffer는 네트워크 단절 시 데이터를 SQLite DB에 로컬 저장하고, 연결 복구 시 **자동으로 전송(FIFO)**합니다.
appsettings.json 설정
{
"OfflineBuffer": {
"Enabled": true,
"DbPath": "/opt/caffeine/bridge/data/offline_buffer.db",
"MaxSizeMb": 500,
"MessageTtlHours": 72
},
"CloudConnection": {
"Transport": "gRPC",
"EngineUrl": "http://caffeine-engine.local:5050",
"RetryIntervalSeconds": 5
}
}
설정 파라미터
| 파라미터 | 기본값 | 설명 |
|---|---|---|
Enabled | true | Offline Buffer 활성화 |
DbPath | ./offline_buffer.db | SQLite DB 파일 경로 |
MaxSizeMb | 500 | 최대 버퍼 크기 (MB) |
MessageTtlHours | 72 | 메시지 보존 기간 (시간) |
네트워크 단절 시나리오 실습
# 1. 정상 동작 확인
curl http://localhost:5060/health
# {"status":"Healthy","cloudConnection":"Connected","bufferedMessages":0}
# 2. 네트워크 단절 시뮬레이션
sudo ip link set eth0 down
# 3. 잠시 후 상태 확인
curl http://localhost:5060/health
# {"status":"Degraded","cloudConnection":"Disconnected","bufferedMessages":42}
# → 데이터가 Offline Buffer에 저장되고 있음
# 4. 네트워크 복구
sudo ip link set eth0 up
# 5. 자동 전송 확인
# 로그에서 "Flushing X buffered messages" 메시지 확인
journalctl -u caffeine-bridge --since "1 minute ago" | grep -i flush
# [INF] Flushing 42 buffered messages to cloud...
# [INF] Flush completed. 42/42 messages sent successfully.
디스크 용량 관리
Offline Buffer는 두 가지 방식으로 자동 정리됩니다:
- TTL 만료:
MessageTtlHours초과 메시지 자동 삭제 - 크기 제한:
MaxSizeMb초과 시 가장 오래된 메시지부터 삭제
# 버퍼 DB 크기 확인
du -sh /opt/caffeine/bridge/data/offline_buffer.db
# 12M offline_buffer.db
배운 내용 정리
| 항목 | 설명 |
|---|---|
| ARM 크로스 컴파일 | dotnet publish -r linux-arm64 --self-contained |
| Self-contained 배포 | Pi에 .NET 설치 불필요, 단일 바이너리 |
| systemd 서비스 | 자동 시작, 장애 시 자동 재시작, 리소스 제한 |
| Offline Buffer | SQLite 기반 로컬 버퍼, 재연결 시 FIFO 전송 |
| Health Check | /health 엔드포인트로 서비스 상태 모니터링 |
다음 단계
- Tutorial 09: NuGet SCADA 시스템 — 독립 SCADA 시스템 구축
- 배포 가이드: Docker — 컨테이너 기반 배포
- 배포 가이드: Kubernetes — K8s 오케스트레이션
문제 해결
Q: ARM 빌드 시 NETSDK1083 오류가 발생합니다.
linux-arm64RID가 지원되는 .NET 10 SDK인지 확인하세요.dotnet --list-runtimes로 설치된 런타임을 확인할 수 있습니다.
Q: Raspberry Pi에서 실행 시 Permission denied가 발생합니다.
chmod +x Caffeine.Bridge.Host로 실행 권한을 부여하세요.
Q: systemd 서비스가 시작 직후 종료됩니다.
journalctl -u caffeine-bridge -e로 에러 로그를 확인하세요. 포트 충돌이나 설정 파일 경로 오류일 수 있습니다.
Q: Offline Buffer DB가 계속 커집니다.
MessageTtlHours와MaxSizeMb설정을 확인하세요. 클라우드 연결이 장기간 불가능하면 TTL 만료된 메시지가 자동 삭제됩니다.