Syncthing is a continuous file synchronization program. It synchronizes files between two or more computers in real time, safely protected from prying eyes. Your data is your data alone and you deserve to choose where it is stored, whether it is shared with some third party, and how it’s transmitted over the internet.

가지고 있는 디바이스가 많기 때문에 파일 공유가 매우 중요하다. 자연스럽게 다른 디바이스에서 작업 전환을 할 수 있기 때문이다. 현재 사용하고 있는 파일 공유는 dropbox이다. 써본 파일 공유 솔루션 중에서는 가장 뛰어난 것 같다. 무료로도 적당한 용량을 받을 수 있고, 디바이스간 파일 공유가 매우 빠르고 자연스럽다. 그런데 어느 순간부터 드롭박스에서는 디바이스 숫자에 제한이 생겨버렸다. 모든 디바이스간에 파일 공유가 되지 않는 것이다. 회사야 이런 솔루션을 보통 막아놓기 때문에 상관없다고 해도, 집에서 쓰는 디바이스만 해도 4개가 넘기 때문에 굉장히 불편한 상황이 되어버렸다. 그래서 파일을 수동으로 복사하거나, 기기를 연결했다가 끊는 등의 귀찮은 작업이 자꾸 생겼기 때문에, 비슷한 기능을 제공하면서 많은 디바이스를 추가할 수 있는 그런 대안을 찾기 시작했다.

이런저런 것들을 비교하다 보니 Syncthing이 제일 무난해 보였다. 기기를 추가하고 파일을 공유한다는 것이 굉장히 드롭박스처럼 직관적인 느낌이 있었다. 아래 적겠지만 최초 동기화를 위해 디바이스를 서로 찾아내는 과정이 꽤나 오래 걸리기 때문에 그런 면은 불편하지만, 반대로 어디에 있는 서버라도 어떻게든 인터넷에 자기 자신을 알릴 수만 있으면 결국 동기화를 진행하기 때문에 설정이 좀 간단하다는 장점도 있는 것 같다. 그래서, 개인적인 중요한 파일들이나 빠르게 동기화해야 하는 것들은 기존처럼 드롭박스를 사용하면서, 회사 문서 등의 비교적 작은 파일들은 Syncthing을 통해 적당히 동기화 하려고 한다.

여기서는 상세한 설치법이나 설정보다는 대충 Syncthing이 무엇이고 환경은 어떻게 구성했는지를 간략히 적어보려고 한다.

설치와 실행

설치는 간단하다. Syncthing-Download 페이지에서 필요한 패키지를 그냥 받아 설치하면 된다. 아니면 Github Releases에서 필요한 바이너리를 받으면 된다. 수많은 기기별 바이너리가 있으므로 그냥 받아서 실행하면 된다.

대충 Syncthing이 동작하는 방식

동작 방식은 매우 심플하다. 디바이스는 Syncthing 바이너리를 실행하여 고유한 device_id를 만들고 그것을 자신의 아이덴티티로 삼는다. 여러 디바이스는 자신의 device_id를 통해 서로를 식별 가능하다. 사용자는 신뢰할 수 있는 device_id라면, 연결을 수락할 수 있고, 이를 통해서 허용된 디바이스끼리 파일을 주고받도록 한다.

디바이스가 NAT 뒤에 있더라도 어떻게든 찾아서 서로간 연결이 자동으로 이루어진다. 이는 Syncthing 쪽에서 구성해 놓은 Global Discovery Servers를 통해 이루어진다.(이 또한 바이너리로 제공되어 직접 실행해볼 수 있다.) 그리고 Relay server를 통해서 서로간 파일을 주고받을 수 있게 된다.(이것도 바이너리가 제공된다.) 뭐 자세한 내용은 나중에 따로 올리면 될 것 같고, 그래서 정리해 보면 대략 이런 과정을 거쳐 파일을 주고받는다.

  • 각 디바이스에서 Syncthing 바이너리를 실행한다.
  • 실행된 디바이스는 고유한 device_id를 가진다.
  • device_id를 통해 어떤 기기라도 어디에 있든 인터넷에 연결할 수 있다면, 무조건 서로를 등록할 수 있다.
  • 등록된 기기끼리는 파일을 주고받을 수 있다.

syncthin-architecture

그러면 서버를 어떻게 두는 것이 좋은가 생각해 보자.

구성

첫 번째 구성

처음에는 가지고 있는 기기 모두에 서로를 모두 공유하려고 했었다. 기기는 집 데스크탑 2개, 휴대폰, 태블릿, 회사 데스크탑과 노트북? 이렇게만 해도 대강 6개가 되는데 서로의 device_id를 모두에게 전부 추가한 것이었다. 네트워크 수업때 배우는… 그물형 토폴로지 비슷하게?

그런데 이게 문제가 뭐냐 하면 연결을 끊어도 서로간 연결 경로가 있기 때문에 결국 파일이 똑같이 공유되고, 거기다가 뭔가 하나의 기기가 추가되거나 해서 디바이스 세트가 변경이 되면 나머지 디바이스에 모두 업데이트를 해야 하는것이 너무 괴로운 것이다.

그래서 결국은 스타 토폴로지 형태로 연결했다. 그러니까 중앙 서버가 있고 각 기기는 이 서버에 연결하는 형태. 파일은 각 기기간 바로 공유되지 않고 중앙 서버를 거쳐 연결한다. 어라? 일반적인 클라우드랑 똑같구나.

star-topology

두 번째 구성

아무튼 언제든 파일을 업데이트할 수 있도록 중앙 서버를 만들었는데, 문제는 이 중앙 서버가 항상 실행중인 상태를 유지해야 한다는 것이다. 어떻게 할까 하다가 구축된 쿠버네티스 클러스터 위에 이걸 올리는 것이 어떨까 싶은 생각이 들었다. 공식인지 아닌지는 모르지만(official 인증이 없어 보이는데?) 도커허브에 이미지가 있고, 그러니 이걸 사용해서 팟을 올리고, 거기에 스토리지도 필요하니 우선 Statefulset으로 올려볼까 해서 그냥 한번 간단히 작성해 보았다.

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: syncthing
spec:
  replicas: 1
  selector:
    matchLabels:
      app: syncthing
  serviceName: syncthing
  template:
    metadata:
      labels:
        app: syncthing
    spec:
      containers:
      - image: syncthing/syncthing:1.16
        imagePullPolicy: Always
        name: nginx
        ports:
        - containerPort: 8384
          name: web-ui
          protocol: TCP
        - containerPort: 22000
          name: sync-protocol
          protocol: TCP
        - containerPort: 21027
          name: discovery
          protocol: UDP
        resources: {}
        volumeMounts:
        - mountPath: /mnt/syncthing
          name: syncthing-data
        - mountPath: /var/syncthing
          name: syncthing-config
      volumes:
      - name: syncthing-config
        persistentVolumeClaim:
          claimName: syncthing-config
      - name: syncthing-data
        persistentVolumeClaim:
          claimName: syncthing-data

이와 같이 구성하여 Statefulset을 만들었다. 이외 추가로 외부 노출을 위해 LoadBalancerNodePort 등을 만들지는 않았는데, 어차피 Syncthing 구조상 디스커버리/릴레이 서버를 통해 어딘가의 기기에 연결할 수 있기 때문이다. 대신 시간이 오래 걸리는 것이 문제라면 문제지만, 업무용 데스크탑은 거의 항상 켜져있기 때문에 큰 문제가 없지 않나 싶다.

$ kubectl exec -t -i syncthing-0 -n syncthing  -- /bin/sh
$ ps -ef
PID   USER     TIME  COMMAND
    1 1000      0:01 /bin/syncthing -home /var/syncthing/config
   40 root      0:00 /bin/sh
   46 root      0:00 ps -ef

Statefulset 내 팟 안에서 실행되고 있는 프로세스를 보자. -home 파라미터를 통해 syncthing이 사용할 홈 디렉토리를 지정한다. 이 안에는 설정 파일이 들어있는데, 최초 랜덤하게 지정되는 내 device_id이외 등록된 외부 기기에 대한 정보도 이 설정 파일에 포함되므로 이것을 ConfigMap으로 분리할 수 없다. 왜냐하면 Configmap은 “ReadOnly"이고 따라서 기기가 추가될 때 설정파일을 업데이트할 수 없기 때문이다.

따라서 설정 파일을 분리하지 않고 설정파일이 쓰일 홈 디렉토리 전체를 pvc로 분리했다. 더구나 이렇게 하면 이 볼륨에 인증서와 indexdb도 포함되므로 팟이 죽거나 재시작되더라도 state는 같도록 유지될 것이다.

팟의 숫자는 자동으로 증감이 진행되지 않는다. 굳이 이걸 고가용성을 따져야 하나 싶기도 하다.

단점

단점으로는 디바이스끼리 직접 연결을 하는 것이 아니므로 최초 서로를 찾는 과정이 너무 오래 걸린다. 이는 그러나 처음 구성시 이미 염두에 두었던 내용이라 어느정도 이해는 가지만 그래도 답답할 때가 조금 있는 것 같다.

battery

다른 단점으로는 syncthing 안드로이드 클라이언트의 배터리 광탈 문제인데 아무래도 이건 어떠한 실시간 동기화 솔루션이라도 해결할 수 없는 것이 아닐까 싶다. 우선은 실시간 동기화를 꺼놓은 상태이고, 이러면 한 시간마다 확인한다고 하니 휴대폰으로는 필요할 때 동기화하는 것이 아무래도 스트레스를 덜 받지 않을까 싶다.

마지막으로 단점하나 더 꼽자면, Syncthing Web UI를 열지 않으면 이게 지금 동기화가 된 것인지를 알 수가 없다. 드롭박스처럼 Syncthing의 공유 대상 파일, 디렉토리라면 뭔가 시각적으로 표시해 주는 편이 사용하기 더 좋지 않을까 싶다.