원문 캡처 · janiserdmanis.org
원문 캡처 · janiserdmanis.org

클라우드에서 개인화된 엣지로

오랫동안 Dropbox를 사용해 왔고 안정성과 사용 편의성 때문에 추천할 만하다고 생각한다. 하지만 구독하지 않으면 저장 공간이 제한된다. 구독료가 내게는 너무 비싸게 느껴져 올해 4월부터 대안을 찾기 시작했다. 주로 Raspberry Pi와 집의 이더넷 연결로 할 수 있는 일을 알아봤다.

SAMBA나 OwnCloud 같은 데이터 저장 서비스를 직접 운영하기는 어렵다. 우선 항상 접속할 수 있는 고정 IP 주소가 없다. 이더넷 제공업체에 추가 요금을 내면 고정 IP를 쓸 수 있다. 또 내 네트워크 연결은 데이터 업로드 속도가 약 200kb/s로 느려서 대용량 데이터를 저장하고 관리하기가 번거로워 보였다. 비슷한 서비스를 직접 유지하는 데 드는 노력을 생각하면 Dropbox 구독료도 실제로는 합리적인 비용이다.

BitTorrent Sync 기술에 관한 글을 읽고 가능성을 봤다. 이 기술을 사용하면 클라우드 저장소 없이 컴퓨터와 휴대전화 사이에 동기화 폴더를 만들고 공유할 수 있다. 따라서 저장 공간에 제한이 없고 데이터를 빠르게 공유할 수 있다. 정말 훌륭한 소프트웨어다. Syncthing도 유망한 오픈소스 대안이지만, 나는 BitTorrent Sync를 사용해 보기로 했다.

Raspbian 설정

데이터를 클라우드에 저장하지 않으므로 동기화하려면 컴퓨터가 최소 두 대 켜져 있어야 한다. 보통은 불편한 조건이다. 그래서 다른 사람들처럼 Raspberry Pi를 항상 켜 두는 노드로 설정했다. Raspberry Pi를 새로 설정할 때마다 인터넷에서 같은 명령어를 다시 찾곤 했기에, 이번에는 설정 과정을 자세히 정리하기로 했다. Raspberry Pi와 SD 카드부터 시작한다.

컴퓨터에서 Raspbian Lite를 내려받고 다음 명령어로 SD 카드에 기록한다.

dd bs=4M if=2017-07-05-raspbian-jessie.img of=/dev/sdX

X는 ls /dev의 결과가 어떻게 바뀌는지 보고 찾을 수 있다. 다음으로 /boot/ssh 파일을 만들어 이번 부팅에서 SSH 연결을 허용한다. 이 파일은 부팅할 때 삭제된다. 그런 다음 SD 카드를 Pi에 넣고, 네트워크 연결 설정에서 ‘다른 컴퓨터와 공유’ 방식을 선택해 이더넷 케이블로 컴퓨터에 연결한다. IP 주소는 다음 명령어로 찾을 수 있다.

nmap -n -sP 10.42.0.255

마지막 숫자는 브로드캐스트 주소이며, 연결 정보에서 확인할 수 있다. 다음 명령어로 로컬 네트워크 전체를 검색하는 방법도 있다.

nmap -sP 192.168.1.1/24

이제 SSH로 Pi에 연결할 수 있다.

ssh pi@10.42.0.240

IP 주소에는 앞 단계에서 확인한 값을 넣는다. 비밀번호는 Raspberry다. 먼저 passwd 명령어로 비밀번호를 바꾼다. 그런 다음 raspi-config를 실행해 메뉴에서 다음을 설정한다.

  • 파티션 크기를 늘려 SD 카드 전체 공간 사용
  • 부팅할 때 SSH 활성화

이제 Raspberry 설정을 진행할 수 있다.

이메일로 IP 주소 확인하기

인터넷 제공업체는 집 밖에서도 설정에 접속하고 개인 VPN이나 Samba 서버 같은 기능을 사용할 수 있도록 공인 IP 주소를 제공한다. 하지만 IP가 고정되어 있지 않아 계속 확인할 방법이 필요하다. 나는 이메일을 이용하기로 했다.

IP 주소에서 Gmail로 직접 이메일을 보내는 방법을 한참 찾아봤지만, 그런 방식은 가능하지 않았다. 대신 스팸 방지를 위해 인증된 이메일 계정으로 메시지를 보내야 한다. 그러려면 이메일 비밀번호를 기기에 저장해야 한다. SMTP를 이용하며, Python 메시지 스크립트는 다음과 같다.

import subprocess
import smtplib
import socket
from email.mime.text import MIMEText
import datetime

to = 'akels14@gmail.com'
gmail_user = 'akels14@gmail.com'
gmail_password = '*********'
smtpserver = smtplib.SMTP('smtp.gmail.com', 587)
smtpserver.ehlo()
smtpserver.starttls()
smtpserver.ehlo
smtpserver.login(gmail_user, gmail_password)
today = datetime.date.today()

arg='ip route list'
p=subprocess.Popen(arg,shell=True,stdout=subprocess.PIPE)
data = p.communicate()
split_data = data[0].split()
ipaddr = split_data[split_data.index('src')+1]
my_ip = 'Your ip is %s' % ipaddr
msg = MIMEText(my_ip)
msg['Subject'] = 'IP For RaspberryPi on %s' % today.strftime('%b %d %Y')
msg['From'] = gmail_user
msg['To'] = to
smtpserver.sendmail(gmail_user, [to], msg.as_string())
smtpserver.quit()

부팅할 때 이 스크립트를 실행하려면 /home/pi/bin에 넣고 chmod +x를 실행한 뒤 /etc/rc.local에 다음 내용을 추가한다.

IP=$(hostname -I) || true
 if [ "$_IP" ]; then
 printf "My IP address is %s\n" "$_IP"
 sleep 30
 sudo -u pi -i mailip
 fi

이렇게 하면 다른 Linux 스크립트가 모두 끝난 뒤 스크립트가 실행된다. 이메일을 받지 못할 때도 있었다. 부팅이 끝날 때까지 Raspberry가 네트워크를 기다리도록 raspi-config에서 설정해야 한다. 네트워크에 연결될 때 스크립트를 실행하도록 설정하는 방법도 있다. /etc/network/if-up.d/mailip에 스크립트를 넣으면 되지만, 내 환경에서는 작동하지 않았다.

BitTorrent Sync 설정

인터넷에서 BitTorrent Sync 1.4가 구형 Raspberry Pi에서 가장 잘 작동하는 마지막 안정 버전이라는 의견을 읽었다. 나도 구형 Raspberry Pi를 사용했다. Linux GUI 클라이언트에는 최신 Linux 버전에서 표시기 애플릿이 없고, 개발도 중단된 듯했다. 한 시간 정도 찾아본 끝에 BitTorrent Sync PPA에서 찾았다.

링크에서 ARM 아카이브를 내려받아 압축을 풀고 Raspberry Pi로 복사한 뒤 동기화해야 한다. 다음 명령어를 사용할 수 있다.

rsync btsync pi@[ip pi]:~/bin/

부팅할 때 실행되도록 /etc/rc.local의 exit 0 앞에 다음 줄도 추가한다.

sudo -u pi -i nohup btsync --webui.listen 0.0.0.0:8888 &

브라우저에서 BitTorrent Sync에 로그인하려면 다음 주소를 사용한다.

[ip of pi]:8888/gui/

여기서 공유 폴더를 만들거나 기존 공유 폴더에 연결할 수 있다. 이제 기기를 재시작해도 개인 클라우드처럼 계속 작동한다.

클라우드와 동기화하기

이 설정을 한 달 넘게 사용해 보니 매우 안정적이었고 Dropbox보다 동기화 속도도 빨랐다. 다만 Dropbox, OneDrive, Google Drive와 비교하면 사용하는 사람이 거의 없다. 그 때문에 내 PC에 문제가 생길 때 복잡해지고, 사용이 번거롭게 느껴졌다. 여러 클라우드 서비스의 동기화를 Raspberry Pi에서 처리하는 편이 낫겠다고 판단했다.

하지만 설정은 간단하지 않았다. 예를 들어 Dropbox 클라이언트는 ARM 기기에서 작동하지 않고, 이를 지원할 계획도 없다. 데스크톱을 에뮬레이션하는 방법도 생각했지만, 원리상 작동할 뿐 속도가 견디기 어려울 만큼 느렸다.

다행히 rsync에서 영감을 받은 명령줄 클라우드 동기화 프로그램 rclone이 있었다. rsync와 비슷한 데이터 전송은 잘 처리했지만, 이 방식으로 동기화를 구성하는 방법은 바로 알기 어려웠다. union-fuse를 이용하는 방법을 찾기까지 며칠, 어쩌면 몇 주가 걸렸다.

먼저 다음과 같이 폴더 세 개를 만든다. 여기서는 Dropbox를 예로 든다.

mkdir -p ~/Cloud/Dropbox.diff
mkdir -p ~/Cloud/Dropbox.ro
mkdir -p ~/BtSync/Cloud/Dropbox

그런 다음 unionfs로 폴더를 하나로 묶는다. /etc/rc.local의 exit 0 앞에 다음 내용을 넣는다.

sudo -u pi -i nohup unionfs-fuse -o cow Cloud/Dropbox.diff=RW:Cloud/Dropbox.ro=RO BtSync/Cloud/Dropbox &

union-fuse는 sudo apt-get install union-fuse로 설치해야 한다. 이 명령어는 쓰기 대상이 아닌 Dropbox.ro 폴더와 변경 사항을 저장하는 Dropbox.diff를 합쳐 일반 Dropbox 폴더를 만든다. 이 폴더는 btsync를 통해 네트워크로 동기화된다. 이제 Dropbox.diff의 변경 사항을 클라우드에 보내고 Dropbox.ro를 클라우드와 동기화하면 된다. 이것이 내가 사용하는 방식이다.

Pi에서 rclone을 사용하려면 RClone 다운로드 페이지에서 ARM 기기용 바이너리를 내려받아 Pi의 ~/bin에 넣는다.

rsync rclone pi@[ip of pi]:~/bin/

그런 다음 SSH로 Pi에 접속해 rclone config를 실행하고 안내에 따라 Dropbox 같은 원격 저장소를 추가한다. 원격 저장소를 시험하려면 다음 명령어로 클라우드의 내용을 로컬에 복사한다.

rclone sync Dropbox: ~/Cloud/Dropbox.ro

마지막으로 rclone과 union-fuse를 함께 사용해 동기화를 구성한다. 가장 어려운 점은 원격이나 로컬에서 파일을 삭제했을 때 삭제가 양쪽에 반영되도록 하는 것이었다. 동기화 스크립트는 다음과 같다.

#!/bin/bash

Remote=$1 
BaseDir=$HOME/Cloud/$Remote
mkdir -p $BaseDir.ro && mkdir -p $BaseDir.diff

if test "$(ls -A $BaseDir.diff)"; then 
 mkdir -p $BaseDir.diff/.unionfs && cd $BaseDir.diff/.unionfs || exit 1
 find . -name "*_HIDDEN~" -type f | while read file; do rm "$file" && rm "$BaseDir.ro/${file:0:-8}" && rclone delete $Remote:"${file:0:-8}"; done
 find . -name "*_HIDDEN~" -type d | while read dir; do rmdir "$dir" && rmdir "$BaseDir.ro/${dir:0:-8}" && rclone rmdir $Remote:"${dir:0:-8}"; done 

 cd $BaseDir.diff || exit 1

 rsync -ua --exclude "*.!sync" --exclude ".unionfs" . "$BaseDir.ro"
 rclone move -u --delete-after --exclude "*.!sync" --exclude ".unionfs" . $Remote:

 find . -type d ! -name "*_HIDDEN~" -empty -delete
fi 

rclone sync $Remote: $BaseDir.ro
exit 0

이 스크립트를 ~/bin/rclone-bisync에 저장하고 평소처럼 chmod +x를 실행한다.

마지막으로 이 스크립트를 정기적으로 실행해야 한다. 나는 crontab을 사용한다. Raspberry에 SSH로 접속해 crontab -e,를 실행하면 사용자의 crontab 파일을 편집할 수 있다. 여기에 다음 줄을 넣는다.

* * * * * sudo -u pi -i flock -n Dropbox.lock rclone-bisync Dropbox

이 설정은 동기화가 이미 실행 중이 아닐 때 매분 시작한다. 제대로 작동하려면 시스템이 해당 사용자로 로그인해야 하므로 raspi-config에서 자동 로그인을 설정했다. 자동 로그인이 필요 없는 방법으로 sudo crontab -e,를 실행해 시스템 crontab을 편집할 수도 있다.