네. SFTPGo를 MinIO AIStor 앞단의 FTP/FTPS/SFTP Gateway로 두는 방식은 꽤 괜찮은 아키텍처라고 봅니다. 특히 지금 말씀하신 것처럼 기존 클라이언트가 S3 API가 아니라 FTP 계열로 파일을 올려야 하는 요구라면, AIStor 자체에 FTP 기능을 억지로 붙이는 것보다 SFTPGo를 별도 계층으로 두는 게 훨씬 깔끔합니다.
SFTPGo는 S3-compatible backend를 지원하고, 공식 문서에도 AIStor/MinIO를 S3 backend로 직접 연결하는 설정이 명시되어 있습니다. (SFTPGo Docs)
External / Legacy Client
|
FTP / FTPS / SFTP
|
v
+----------------------+
| SFTPGo |
| |
| FTP/FTPS |
| SFTP |
| User/Auth/ACL |
| Quota/Logging |
+----------+-----------+
|
S3 API
|
v
+----------------------+
| AIStor |
| MinIO |
| |
| Bucket |
| └── project-A/ |
| └── project-B/ |
| └── ... |
+----------------------+
SFTPGo가 filesystem을 제공하는 게 아니라 S3 backend를 직접 storage로 사용할 수 있기 때문에, 중간에 별도의 NFS/PV 같은 staging filesystem을 둘 필요가 없습니다. (SFTPGo Docs)
제가 보기엔 이게 가장 큰 장점입니다.
AIStor
└─ S3 API
↑
|
SFTPGo
↑
|
FTP/FTPS/SFTP clients
AIStor는 원래 잘하는 S3/Object Storage 역할에 집중시키고,
SFTPGo는
등을 담당하게 합니다.
SFTPGo 자체가 여러 protocol과 storage backend를 추상화하는 MFT 제품이라 이 용도에 상당히 잘 맞습니다. (SFTPGo Docs)
예를 들어 AIStor가
bucket: customer-data
customer-data/
├── companyA/
├── companyB/
├── companyC/
└── companyD/
라면 SFTPGo에서
FTP user: companyA
↓
S3 bucket: customer-data
S3 key prefix: companyA/
FTP user: companyB
↓
S3 bucket: customer-data
S3 key prefix: companyB/
처럼 구성할 수 있습니다.
SFTPGo의 Key Prefix가 특정 사용자를 bucket 내부의 특정 path로 제한하는 용도로 제공됩니다. (SFTPGo Docs)
이건 앞서 이야기하셨던 bucket 하나 아래 prefix를 project별로 사용하는 구조와도 상당히 잘 맞습니다.
여기서는 조금 중요합니다.
일반 FTP:
FTP
21/tcp
+
PASV ports
는 ID/password와 데이터가 암호화되지 않습니다.
따라서 외부 client가 FTP밖에 지원하지 않는 특별한 이유가 없다면:
FTPS Explicit TLS
또는
SFTP
를 권합니다.
SFTPGo는 FTP/FTPS를 제대로 지원하고 passive port range도 설정할 수 있습니다. (SFTPGo Docs)
예:
Internet / DMZ
|
| TCP 21
| TCP 50000-50100
v
LoadBalancer
|
v
SFTPGo pods
|
| HTTPS/S3
v
AIStor
Kubernetes 환경이라면 SFTPGo를 Deployment로 HA 구성하는 것도 가능합니다.
성능입니다.
S3 client가 직접 AIStor에 업로드하는 경우:
Client
|
| S3 multipart
v
AIStor
인데,
SFTPGo를 넣으면:
Client
|
| FTP
v
SFTPGo
|
| S3
v
AIStor
가 됩니다.
즉 SFTPGo가 protocol translation gateway가 됩니다.
따라서 대용량 파일을 많이 올리는 환경에서는:
Client → SFTPGo → AIStor
의 SFTPGo가 병목이 될 수 있습니다.
특히 현재처럼 AIStor 자체의 network/storage throughput이 상당히 큰 환경에서는 이 부분을 반드시 benchmark해야 합니다.
S3 backend에서 단순히 파일을 받아 local filesystem에 저장했다가 다시 AIStor로 copy하는 구조가 아닙니다.
SFTPGo가 S3 backend를 직접 사용할 수 있고, S3 backend는 multipart transfer도 지원합니다. (SFTPGo Docs)
그래서 개념적으로:
FTP DATA stream
↓
SFTPGo
↓
S3 multipart upload
↓
AIStor
로 처리할 수 있습니다.
중간에 별도의 persistent local storage를 두지 않는 구조를 추천합니다.
현재 환경이라면 저는 이런 식으로 설계하겠습니다.
External
|
LB / Ingress
|
+--------+--------+
| |
SFTPGo-1 SFTPGo-2
| |
+--------+--------+
|
S3 API
|
AIStor Service
|
AIStor cluster
그리고 SFTPGo는:
Deployment
replicas: 2~3
Service
TCP 21
TCP 22
passive ports
Config/DB
PostgreSQL
형태로 운영합니다.
다만 FTP passive mode 때문에 일반 HTTP Ingress만 사용하는 것은 적합하지 않습니다. TCP LoadBalancer/NodePort 계층을 설계해야 합니다.
SFTPGo에는 upload mode가 있습니다.
예를 들어 atomic upload를 사용하면:
client
|
| upload
v
temporary object
|
| upload complete
v
final object
처럼 업로드 중인 파일을 최종 파일로 노출하지 않는 방식을 사용할 수 있습니다. SFTPGo는 일반/atomic/atomic+resume 등의 upload mode를 제공합니다. (SFTPGo Docs)
이건 외부 업체가 FTP로 파일을 올리는 환경에서 상당히 유용합니다.
예를 들어:
/report.csv
를 받는 동안 다른 application이 그것을 읽으면 문제가 될 수 있는데,
report.csv.tmp
↓
upload complete
↓
report.csv
같은 publish 모델을 만들 수 있습니다.
SFTPGo에는 단순 FTP server 이상의 기능이 있습니다.
예를 들어:
Client
|
| FTPS
v
SFTPGo
|
+-- Authentication
+-- IP restriction
+-- User restriction
+-- Prefix restriction
+-- Transfer limits
+-- Audit
+-- Event Manager
|
v
AIStor
Enterprise 기능까지 사용한다면 ICAP 기반 antivirus/DLP 검사도 upload 전에 적용할 수 있습니다. (SFTPGo Docs)
이 부분은 반드시 잘 설계해야 합니다.
예를 들어 SFTPGo가 AIStor에 접근하는 credential을 하나 만들어서
SFTPGo
|
| AccessKey: ftp-gateway
v
AIStor
모든 사용자가 이 credential을 공유하도록 하면 좋지 않습니다.
가능하면:
SFTPGo user A
↓
S3 credential/policy
↓
bucket/prefix A
SFTPGo user B
↓
S3 credential/policy
↓
bucket/prefix B
또는 SFTPGo 내부의 user/prefix ACL과 AIStor bucket policy를 이중으로 방어하는 구조를 권합니다.
즉,
SFTPGo에서 제한했다고 해서 AIStor 자체의 credential 권한을 과도하게 주지 않는 것
이 중요합니다.
현재 말씀하신 "기존 client가 FTP로 파일을 올려야 하고, backend는 AIStor"라는 요구라면 저는 꽤 긍정적으로 봅니다.
| 방식 | 평가 |
|---|---|
| AIStor에 FTP 기능을 직접 붙임 | ❌ 비추천 |
| FTP → 별도 filesystem → AIStor | △ 운영 복잡 |
| FTP → SFTPGo → AIStor S3 | ◎ 추천 |
| SFTP → SFTPGo → AIStor S3 | ◎ 추천 |
| Client가 직접 S3 | ◎ 가장 효율적 |
특히 현재 Kubernetes + AIStor 환경이라면:
Legacy FTP clients
|
FTPS/SFTP
|
┌─────────────┐
│ SFTPGo │
│ 2~3 pods │
└──────┬──────┘
│
S3 API/HTTPS
│
┌──────▼──────┐
│ AIStor │
│ │
│ bucket │
│ ├ projectA │
│ ├ projectB │
│ └ projectC │
└─────────────┘
이 구조가 현재 환경에는 상당히 현실적인 선택이라고 봅니다.
다만 수백 MB~수십 GB 파일을 동시에 수십~수백 개 업로드하는 환경이라면 SFTPGo를 단순히 설치하는 것보다, SFTPGo → AIStor throughput / concurrency / memory / connection 수를 실제로 POC하는 게 중요합니다. SFTPGo에는 host별 concurrent transfer 제한 같은 설정도 제공되므로 이를 AIStor의 API/RDMA 네트워크 부하와 함께 튜닝할 수 있습니다. (SFTPGo Docs)
원하시면 다음 단계로 현재 사용 중인 AIStor/K8s 구조 기준으로 SFTPGo + AIStor를 Kubernetes에 HA로 배치하는 구체적인 구성도(Deployment/Service/Passive FTP ports/DB/Secret/AIStor policy)와 함께, 10/50/100 concurrent FTP upload 성능 POC 항목까지 설계해드릴 수 있습니다.
===
가능합니다. 오히려 현재처럼 Cilium Native Routing + BGP + ECMP + Gateway API를 사용하는 환경이라면 SFTPGo를 별도 LoadBalancer Service로 노출하고, Gateway API는 HTTP 계열 관리 UI/API에만 사용하는 것을 권합니다.
핵심은 FTP/FTPS의 control connection과 passive data connection을 Gateway API의 HTTP routing으로 처리하려고 하지 않는 것입니다. FTP는 여러 TCP 포트를 사용하기 때문에 일반적인 HTTPRoute 구조와 맞지 않습니다. SFTPGo 공식 Kubernetes 가이드도 SFTP/FTP는 Ingress가 아니라 LoadBalancer/NodePort 같은 TCP 노출 방식을 권장합니다. (GitHub)
External Network
|
+------------+------------+
| |
VIP: FTP/SFTP VIP: HTTPS
| |
Cilium BGP/ECMP Cilium Gateway
| |
+---------+---------+ |
| | |
SFTPGo Pod-1 SFTPGo Pod-2 |
| | |
+---------+---------+ |
| |
+----------+------------+
|
S3 HTTPS
|
AIStor
즉 두 개의 진입 경로로 분리합니다.
| 용도 | 노출 방식 | Cilium |
|---|---|---|
| FTP/FTPS | LoadBalancer Service | LB-IPAM + BGP + ECMP |
| SFTP | LoadBalancer Service | LB-IPAM + BGP + ECMP |
| SFTPGo Web Admin/API | Gateway API | Cilium Gateway |
| AIStor S3 | 기존 방식 | Cilium Native/BGP/ECMP |
Cilium의 LB-IPAM은 LoadBalancer Service에 VIP를 할당하고, BGP Control Plane이 그 VIP를 BGP로 advertise하는 구조를 지원합니다. upstream router가 ECMP를 지원하면 같은 VIP를 여러 node에서 광고해서 ECMP load-balancing도 할 수 있습니다. (Cilium Documentation)
예를 들어 SFTPGo에 다음 endpoint를 제공한다고 하겠습니다.
sftp.example.com TCP/22
ftp.example.com TCP/21
ftps.example.com TCP/21
그리고 FTP passive range를:
50000-50100
으로 잡습니다.
그러면 Kubernetes 입장에서는 최소한:
SFTPGo Service
├── 21/TCP
├── 22/TCP
└── 50000-50100/TCP
가 필요합니다.
SFTPGo의 기본 passive range도 50000–50100이며, 이 포트들이 client에서 접근 가능해야 합니다. (GitHub)
예를 들어 Cilium LB-IPAM pool을:
172.20.100.0/24
라고 해보겠습니다.
SFTPGo에:
172.20.100.10
이라는 VIP를 할당합니다.
apiVersion: v1
kind: Service
metadata:
name: sftpgo
namespace: sftpgo
annotations:
lbipam.cilium.io/ips: "172.20.100.10"
spec:
type: LoadBalancer
loadBalancerClass: io.cilium/bgp-control-plane
externalTrafficPolicy: Local
selector:
app: sftpgo
ports:
- name: ftp
port: 21
targetPort: 21
protocol: TCP
- name: sftp
port: 22
targetPort: 22
protocol: TCP
- name: passive-50000
port: 50000
targetPort: 50000
protocol: TCP
# ...
다만 50000~50100을 전부 하나씩 Service port로 만드는 것은 운영상 상당히 귀찮습니다.
따라서 여기서 SFTPGo의 passive FTP 구조와 Kubernetes Service 설계를 조금 더 신중하게 잡아야 합니다.
100개 포트를 무조건 사용할 필요가 없다면 예를 들어:
50000-50031
정도로 시작합니다.
동시 FTP session이 32개 정도라면:
FTP control
21
Passive data
50000-50031
정도로 시작하고 실제 concurrency에 따라 늘립니다.
SFTPGo 자체에서도 passive port range를 지정할 수 있습니다. (GitHub)
예:
ftpd:
bindings:
- port: 21
address: 0.0.0.0
passive_port_range:
start: 50000
end: 50031
여기가 상당히 중요합니다.
현재 환경에서:
Gateway API
|
+-- HTTPRoute
+-- TLSRoute
+-- TCPRoute
가 가능하지만, FTP 전체를 TCPRoute로 구성하는 것은 추천하지 않습니다.
Cilium Gateway API 자체는 TCPRoute를 지원합니다. (Cilium Documentation)
하지만 FTP는:
TCP/21
|
+---- passive TCP/50000
+---- passive TCP/50001
+---- passive TCP/50002
...
처럼 별도 data connection을 생성합니다.
그래서:
Gateway
|
+-- TCPRoute :21
+-- TCPRoute :50000
+-- TCPRoute :50001
...
로 만들 수는 있어도 굉장히 불필요하게 복잡해집니다.
특히 FTP client compatibility까지 생각하면 더욱 그렇습니다.
Internet
|
SFTP/FTP clients
|
172.20.100.10
|
Cilium BGP/ECMP
|
+---------+---------+
| |
Node A Node B
| |
SFTPGo-1 SFTPGo-2
Admin
|
HTTPS
|
Gateway API VIP
|
HTTPRoute
|
SFTPGo Web Admin
즉:
FTP/SFTP
↓
LoadBalancer Service
↓
Cilium BGP/ECMP
Web Admin
↓
Gateway API
↓
Cilium Envoy
↓
SFTPGo
이게 가장 깔끔합니다.
externalTrafficPolicy는 Local을 우선 검토현재처럼 BGP + ECMP를 사용한다면 저는:
externalTrafficPolicy: Local
을 우선 추천합니다.
이유는 client source IP 보존 때문입니다.
예를 들어:
Client
|
| src = 10.10.10.50
v
VIP 172.20.100.10
|
| ECMP
v
Node-A
|
v
SFTPGo
SFTPGo가 실제 client IP를 볼 수 있게 하는 것이 좋습니다.
특히 FTP/SFTP 서버에서는:
등 때문에 source IP가 중요합니다.
Cilium Gateway API에서도 LoadBalancer/NodePort의 externalTrafficPolicy가 client IP visibility에 영향을 준다고 설명하고 있습니다. (Cilium Documentation)
externalTrafficPolicy: Local + ECMP에는 조건이 하나 있습니다이게 현재 환경에서 가장 중요한 Cilium 설정 포인트입니다.
예를 들어:
SFTPGo-1 → Node-A
SFTPGo-2 → Node-B
SFTPGo-3 → Node-C
라고 하고,
BGP가:
VIP 172.20.100.10
|
+--- Node-A
+--- Node-B
+--- Node-C
로 advertise하면 좋습니다.
그러나:
Node-A에는 SFTPGo endpoint 없음
인데 Node-A가 VIP를 광고하면 문제가 생길 수 있습니다.
따라서 ECMP로 VIP를 advertise하는 node와 실제 SFTPGo endpoint가 있는 node의 관계를 확인해야 합니다.
Cilium BGP advertisement에서 Service의 LoadBalancer IP를 광고할 수 있고, ECMP router가 같은 VIP를 여러 node에서 받는 구조가 가능합니다. (Cilium Documentation)
저라면 SFTPGo를 단순히:
replicas: 2
만 하지 않고 최소한:
Node A
└─ SFTPGo-1
Node B
└─ SFTPGo-2
Node C
└─ SFTPGo-3
처럼 분산합니다.
그리고:
topologySpreadConstraints
podAntiAffinity
를 적용합니다.
그래야:
BGP ECMP
↓
Node A/B/C
↓
SFTPGo A/B/C
구조가 자연스럽게 만들어집니다.
이것도 놓치면 안 됩니다.
SFTPGo를:
SFTPGo-1
SFTPGo-2
SFTPGo-3
로 띄우려면 사용자/설정 정보를 외부 DB에 두는 것이 좋습니다.
현재 환경에서 이미 CNPG를 사용하고 있으니:
SFTPGo
|
+--- SFTPGo-1
+--- SFTPGo-2
+--- SFTPGo-3
|
v
CNPG PostgreSQL
구조가 적합합니다.
SFTPGo의 multi-node 구성에서도 external data provider가 필요합니다. (GitHub)
SFTPGo:
SFTPGo
|
| HTTPS
| S3 API
v
AIStor VIP
로 연결합니다.
예를 들어:
storage:
provider: s3
endpoint: https://aistor-s3.example.com
bucket: customer-data
SFTPGo는 S3-compatible backend를 직접 지원하기 때문에 중간 filesystem/PV를 반드시 둘 필요가 없습니다. (SFTPGo Docs)
예를 들어:
AIStor
bucket: data
data/
├── project001/
├── project002/
└── project003/
SFTPGo:
user: project001
storage:
bucket: data
key_prefix: project001/
이런 식으로 구성합니다.
그리고 AIStor policy도 이중으로 제한하는 것을 권합니다.
SFTPGo ACL
+
AIStor IAM/policy
둘 중 하나가 잘못되어도 다른 쪽에서 방어할 수 있게 합니다.
예를 들어 SFTPGo namespace에서:
Internet
|
v
SFTPGo
|
| 443
v
AIStor
만 허용하고,
SFTPGo
|
+-- PostgreSQL/CNPG
도 필요한 포트만 허용합니다.
즉:
Ingress:
TCP 21
TCP 22
TCP 50000-50031
Egress:
AIStor :443
CNPG :5432
DNS :53
정도로 최소화합니다.
Gateway API보다 이것이 더 중요실제로 POC에서 가장 먼저 확인해야 할 것이:
FTP control connection
↓
TCP/21
PASV
↓
SFTPGo가
"172.20.100.10:50007"
같은 주소를 client에게 전달
client
↓
172.20.100.10:50007
↓
Cilium
↓
SFTPGo
입니다.
즉 SFTPGo가 client에게 정확한 VIP와 passive port를 알려줘야 합니다.
SFTPGo는 NAT/proxy 환경에서 force_passive_ip 및 passive_ip_overrides를 제공하므로 이 부분을 명시적으로 설정할 수 있습니다. (GitHub)
예를 들어:
ftpd:
bindings:
- port: 21
force_passive_ip: 172.20.100.10
passive_port_range:
start: 50000
end: 50031
이 설정이 굉장히 중요합니다.
현재 환경이:
Cilium
├─ Native routing
├─ BGP
├─ ECMP
└─ Gateway API
이기 때문에 제가 실제 구축한다면 SFTPGo FTP용 VIP와 기존 Gateway VIP를 분리하겠습니다.
예:
172.20.100.0/24
│
├── 172.20.100.10
│ SFTPGo FTP/SFTP VIP
│
├── 172.20.100.20
│ Gateway API VIP
│
├── 172.20.100.21
│ Gateway API VIP
│
└── ...
그리고 BGP advertisement도:
SFTPGo LB VIP
↓
BGP
↓
ECMP
로 별도 관리합니다.
Cilium LB-IPAM과 BGP Control Plane이 이런 LoadBalancer IP advertisement 구조를 지원합니다. (Cilium Documentation)
External Client
|
+------------+------------+
| |
SFTP / FTPS HTTPS
| |
v v
VIP 172.20.100.10 VIP 172.20.100.20
| |
| BGP + ECMP | Gateway API
| |
+--------+--------+ |
| | | |
Node-A Node-B Node-C |
| | | |
S1 S2 S3 <--------------+
\ | /
\ | /
+------+------+
|
S3 HTTPS
|
v
AIStor Service/VIP
|
v
AIStor
Namespace: sftpgo
Deployment
replicas: 3
Service
type: LoadBalancer
loadBalancerClass: io.cilium/bgp-control-plane
externalTrafficPolicy: Local
Ports
21 FTP/FTPS
22 SFTP
50000-50031 passive FTP
Gateway
HTTPS 443
└── HTTPRoute
└── SFTPGo WebAdmin/API
CNPG
└── PostgreSQL
Secret
├── SFTPGo DB credential
├── AIStor access key
└── AIStor secret key
LB-IPAM
↓
VIP allocation
BGP Control Plane
↓
advertise LoadBalancerIP
Router
↓
ECMP
↓
SFTPGo nodes
SFTP를 우선 추천만약 client가 SFTP를 지원한다면 FTP/FTPS보다 SFTP를 우선하겠습니다.
이유가 단순합니다.
SFTP
↓
TCP/22 하나
↓
Cilium LB
↓
SFTPGo
반면 FTP/FTPS는:
TCP/21
+
Passive TCP/50000-50031
이 필요합니다.
즉 Cilium + BGP + ECMP 환경에서 운영 난이도가 SFTP가 훨씬 낮습니다.
SFTPGo 자체도 SFTP, FTP/S 등 여러 protocol을 제공하며 S3-compatible backend를 사용할 수 있습니다. (SFTPGo Docs)
현재 환경에서는 SFTPGo를 Gateway API 뒤에 넣는 구조보다는 SFTPGo 자체를 Cilium LoadBalancer Service로 노출하고, SFTPGo Web UI/API만 Gateway API로 넣는 구조가 가장 좋습니다.
특히 다음 5가지를 중점적으로 설계하면 됩니다.
LoadBalancer Service + Cilium LB-IPAMexternalTrafficPolicy: Local 검토force_passive_ip그리고 현재 사용 중인 Cilium 1.18.4 기준으로 실제 YAML을 작성한다면 약간 주의해야 합니다. 위 공식 문서는 최신 Cilium 문서이고, Gateway API/TCPRoute/LB-IPAM의 세부 동작은 1.18과 최신 버전 사이에 차이가 있을 수 있습니다. 따라서 실제 구축용으로는 현재 사용 중인 Cilium 1.18.4 + K8s 1.33.x 기준으로 CiliumBGPAdvertisement, CiliumLoadBalancerIPPool, SFTPGo Service, Gateway/HTTPRoute, NetworkPolicy까지 한 세트의 실제 YAML로 맞추는 게 좋습니다.
===
네. 두 질문 모두 "아니요, 꼭 그렇지는 않습니다"입니다. 오히려 지금 환경에서는 제가 앞서 설명한 내용을 조금 정정해서 보는 게 좋습니다.
아닙니다.
SFTPGo의 DB는 필수가 아니라 배포 형태에 따라 선택입니다.
SFTPGo는 기본적으로 SQLite를 사용할 수 있고, PostgreSQL/MySQL/MariaDB 등을 사용할 수 있습니다. 다만 여러 SFTPGo instance가 하나의 설정/사용자 DB를 공유하는 multi-instance 구성에서는 PostgreSQL/MySQL/MariaDB/CockroachDB 같은 shared DB가 필요합니다. (SFTPGo Docs)
| 구성 | DB | CNPG 필요 |
|---|---|---|
| SFTPGo 1 Pod | SQLite | ❌ |
| SFTPGo 1 Pod + 장애 시 재기동 | SQLite + PVC | ❌ |
| SFTPGo 2~3 Pod HA | PostgreSQL | ❌ CNPG 자체는 필수 아님 |
| SFTPGo 2~3 Pod HA + 기존 CNPG 활용 | CNPG PostgreSQL | ⭕ 추천 |
| 아주 단순한 FTP gateway POC | SQLite | 가장 간단 |
따라서 단순히 "FTP → AIStor" gateway 용도로 SFTPGo를 하나 띄우는 것이라면 CNPG까지 넣을 필요가 없습니다.
SFTPGo 공식 문서도 SQLite를 기본 provider로 제공하고, PostgreSQL은 multi-instance/shared deployment에 사용할 수 있다고 명시합니다. (Mintlify)
처음에는:
SFTPGo
|
SQLite + PVC
|
AIStor
로 POC를 합니다.
성능/운영 검증 후 production HA가 필요하면:
SFTPGo-1 SFTPGo-2 SFTPGo-3
\ | /
CNPG
|
AIStor
로 전환하겠습니다.
즉 CNPG를 처음부터 넣을 필요는 없습니다.
이건 Cilium BGP + LoadBalancer + ECMP를 어떻게 구성하느냐에 따라 달라지지만, 지금 말씀하신 구조라면 정상적인 Cilium BGP Service VIP 구조에서는 외부 방화벽은 VIP만 허용하는 방향이 맞습니다.
예를 들어:
External Client
|
| TCP 21/22/50000-50010
v
VIP 10.100.50.10
|
| BGP / ECMP
+----------+
| |
Node-A Node-B
| |
SFTPGo-1 SFTPGo-2
외부 방화벽에서는:
ALLOW
destination = 10.100.50.10
TCP 21
TCP 22
TCP 50000-50010
이면 됩니다.
Node-A의 IP, Node-B의 IP를 외부 FTP client가 직접 접근할 필요는 없습니다.
Cilium BGP Control Plane은 Service의 LoadBalancer VIP를 BGP peer에게 광고할 수 있고, upstream router가 ECMP를 지원하면 동일 VIP를 여러 node에서 광고하여 여러 node로 traffic을 분산할 수 있습니다. (Cilium Documentation)
externalTrafficPolicy: Local이면 더 명확합니다이 구조를:
spec:
type: LoadBalancer
externalTrafficPolicy: Local
로 구성하면 Cilium BGP가 해당 node에 local endpoint가 있을 때만 해당 node가 VIP를 advertise하도록 할 수 있습니다. local endpoint가 없어지면 advertisement도 중단됩니다. (Cilium Documentation)
예를 들어:
SFTPGo-1 → Node-A
SFTPGo-2 → Node-B
SFTPGo-3 → Node-C
이면:
VIP
|
+-----+-----+
| | |
Node-A Node-B Node-C
| | |
S1 S2 S3
BGP:
VIP → Node-A
VIP → Node-B
VIP → Node-C
가 되고,
Node-B에서 SFTPGo가 죽으면:
VIP → Node-A
VIP → Node-C
만 남도록 할 수 있습니다.
이게 BGP + ECMP + externalTrafficPolicy: Local의 장점입니다.
외부 client → Node IP 직접 접근을 막는 방화벽이라면 안 열어도 됩니다.
즉:
Internet
|
v
FW / Router
|
VIP 10.100.50.10
|
Cilium BGP/ECMP
/ \
Node-A Node-B
| |
SFTPGo SFTPGo
방화벽:
VIP:
TCP/21 ALLOW
TCP/22 ALLOW
TCP/50000-50010 ALLOW
Node IP:
TCP/21 DENY
TCP/22 DENY
TCP/50000-50010 DENY
이런 식이 이상적인 형태입니다.
여기서 중요한 차이가 있습니다.
Client
↓
VIP:21
↓
Cilium
↓
SFTPGo Pod
→ VIP만 방화벽 허용
Client
↓
Node-A:30xxx
Node-B:30xxx
Node-C:30xxx
↓
SFTPGo
→ Node IP + NodePort를 방화벽에서 허용해야 함
따라서 지금 환경에서는 NodePort를 외부 방화벽에 노출시키는 구조는 피하고 Cilium LoadBalancer VIP를 사용하는 게 좋습니다.
이 부분이 핵심입니다.
FTP client가:
FTP control
10.100.50.10:21
로 접속한 다음 PASV를 요청하면 SFTPGo가 예를 들어:
10.100.50.10:50003
을 client에게 알려줍니다.
그 다음:
Client
|
+---- TCP/21 ----------> VIP
|
+---- TCP/50003 -------> VIP
가 됩니다.
따라서 방화벽은:
VIP 10.100.50.10
├── 21
├── 22
└── 50000-50010
만 열면 됩니다.
SFTPGo 문서에서도 passive port range는 client에서 접근 가능해야 하고, NAT 환경에서는 외부에서 접근할 IP를 force_passive_ip로 지정할 수 있다고 설명합니다. (GitHub)
즉:
ftpd:
bindings:
- port: 21
force_passive_ip: 10.100.50.10
passive_port_range:
start: 50000
end: 50010
같은 형태입니다.
force_passive_ip에 무엇을 넣느냐가 매우 중요합니다.
외부 client가 접근하는 주소가:
10.100.50.10
이라면:
force_passive_ip = 10.100.50.10
입니다.
만약 실제로는:
Public IP
↓ NAT
10.100.50.10
이라면:
force_passive_ip = Public IP
가 되어야 합니다.
즉 client가 PASV 응답을 받고 실제로 접속 가능한 주소를 반환해야 합니다.
가능하면 저는 SFTP를 우선 사용하도록 하겠습니다.
SFTP:
Client
|
| TCP/22
v
VIP
|
| BGP/ECMP
v
SFTPGo
|
| HTTPS/443
v
AIStor
방화벽:
VIP:22 → ALLOW
끝입니다.
반면 FTP/FTPS:
VIP:21
VIP:50000-50010
이 필요합니다.
SFTPGo 자체도 공식적으로 SFTP를 FTP보다 우선 권장하고 있습니다. (GitHub)
Firewall
|
VIP 10.x.x.10
TCP/22
|
Cilium BGP/ECMP
/ \
Node-A Node-B
| |
SFTPGo-1 SFTPGo-2
\ /
AIStor
|
S3/443
외부 FW에는 VIP:22만 허용.
Firewall
|
VIP 10.x.x.10
|
+------------+------------+
| | |
21 22 50000-50010
| | |
+------------+------------+
|
Cilium BGP/ECMP
|
SFTPGo Pods
|
AIStor
외부 FW에는 VIP의 21/22/50000-50010만 허용하고, Node IP에는 해당 포트를 열지 않는 구조를 권합니다.
현재 목적이 "기존 FTP client가 AIStor에 파일을 넣도록 하는 gateway"라면 처음부터:
SFTPGo + CNPG + Gateway API + 별도 DB HA
까지 만들 필요 없습니다.
저라면 1차 POC를:
SFTPGo 1~2 Pod
+
SQLite/PVC
+
Cilium LoadBalancer VIP
+
BGP/ECMP
+
AIStor S3
로 최대한 단순하게 구성하고,
실제 production에서 SFTPGo HA가 필요하다고 판단되면 그때 CNPG PostgreSQL로 바꾸겠습니다.
그리고 현재 Cilium 1.18.4를 사용하시는 점을 감안하면, SFTPGo의 LoadBalancer Service를 실제로 어떻게 선언해야 Cilium 1.18.4에서 VIP가 BGP advertise되고, externalTrafficPolicy: Local에서 ECMP가 어떻게 동작하는지를 기준으로 YAML을 한번 정확히 잡아보는 게 다음 단계입니다. (Cilium Documentation)