Användarverktyg

Webbverktyg


teknik:guider:backup_med_rsync_i_macintosh_os

Backup med rsync i Macintosh OS

Ska på ett koncist sätt ta upp hur man kan använda rsync för att göra backup som påminner om Apples Time Capsule. Här är vad som behövs för bästa resultat.

  • En dator
  • Rsync
  • OpenSSH
  • Bash
  • Extern disk för bästa datasäkerhet
  • Tålamod

Den här artikeln fokuserar på Mac OS där rsync kommer förinstallerat, den kan dock lika väl användas på andra Linux och Unix system om man anpassar sökvägarna.


För att vara så klar som möjligt så tänker jag kalla datorn med den externa disken ansluten för DatorA och USB-disken för DiskA. Vi kommer även blanda in en DatorB senare som ska göra backup över nätverket till DiskA på DatorA.

Först måste vi hitta disken, jag tänker låtsas att den befinner sig på /Volumes/DiskA, men det beror så klart på dess namn vid formatering.

Vi ska även välja ut en katalog att ta backup på, vi kan senare ha en hel lista på kataloger eller din hemkatalog men vi börjar med en för enkelhet. Jag kallar min viktiga katalog för /Users/Stefan/Pictures.

  • Extern disk /Volumes/DiskA
  • Katalog att spara /Users/Stefan/Pictures

Redan nu kan vi köra ett kryptiskt kommando i terminalen som kommer ta en backup av katalogen.

# rsync -a /Users/Stefan/Pictures "/Volumes/DiskA/$(date '+%F')/"

Nu kommer en katalog skapas i /Volumes/DiskA med namnet efter dagens datum, vilket idag hade varit 2012-08-24. Inuti den katalogen hittar vi en kopia av Pictures.

Kör vi samma kommando igen så kommer /Volumes/DiskA/2012-08-24/Pictures uppdateras med endast innehållet i Pictures-katalogen som ändrats sedan vi körde kommandot sist.

Det kallas inkrementell backup.

Automatisera backup

Vi vill att det här ska hända varje gång en fil ändras, läggs till eller tas bort i originalkopian av Pictures. Det kan vi göra med vad Apple kallar en LaunchAgent. Nu blir det mer komplicerat så läs noga. Här är ett exempel av en LaunchAgent som kör kommandot ovan genom ett skript.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>Label</key>
	<string>org.Stefan.BackupPictures</string>
	<key>ProgramArguments</key>
	<array>
		<string>/Users/Stefan/bin/syncDir</string>
		<string>/Users/Stefan/Pictures</string>
	</array>
	<key>WatchPaths</key>
	<array>
		<string>/Users/Stefan/Pictures</string>
	</array>
</dict>
</plist>

Mycket av det här måste förklaras. Först och främst saknar vi en stor del av pusslet, och det är skriptet som jag kallar /Users/Stefan/bin/syncDir. Det kan befinna sig var som helst men utan det kan inga kataloger synkroniseras. Jag tänker visa skriptet senare.

Precis raden under skriptet ser vi vår Pictures-katalog. Lite längre ner, under WatchPaths, ser vi vår Pictures-katalog igen, varför? Detta är hemligheten i vårt system, det säger till Mac OS att övervaka Pictures-katalogen efter ändringar och utlösa skriptet när en ändring sker.

Den här filen ska ligga under /Users/Stefan/Library/LaunchAgents/org.Stefan.BackupPictures, namnet jag valt åt den är ganska arbiträrt men se till att det är unikt och inte konfliktar med andra namn i framtiden. Rättigheterna på den här filen måste vara rätt så följande kommandon kan hjälpa er.

# chown $(id -u):$(id -g) /Users/Stefan/Library/LaunchAgents/org.Stefan.BackupPictures
# chmod 0644 /Users/Stefan/Library/LaunchAgents/org.Stefan.BackupPictures

För att göra backup av andra kataloger måste vi skapa liknande filer och placera dem på samma sätt men med andra sökvägar. Här har vi ett exempel för katalogen Documents.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>Label</key>
	<string>org.Stefan.BackupDocuments</string>
	<key>ProgramArguments</key>
	<array>
		<string>/Users/Stefan/bin/syncDir</string>
		<string>/Users/Stefan/Documents</string>
	</array>
	<key>WatchPaths</key>
	<array>
		<string>/Users/Stefan/Documents</string>
	</array>
</dict>
</plist>

För att särskilja sig från andra så sparar jag den som /Users/Stefan/Library/LaunchAgents/org.Stefan.BackupDocuments.

Nu till den sista länken av kedjan, själva skriptet. Detta förväntar jag mig inte att någon ska förstå men en rad i toppen måste ändras till målkatalogen för alla backuper. Ni kommer känna igen den på /Volumes/DiskA.

#!/bin/bash
# Hjälpskript för att ta backup. 
# Endast för Mac OS, kommandon som stat, date och nc har annat syntax 
# på Linux. 
# Av Stefan Midjich

# REDIGERA DEN HÄR SÖKVÄGEN
backupTarget='/Volumes/NAS Storage 0'

# REDIGERA ENDAST HÄR UNDER OM DU VET VAD DU GÖR
destinationFormat="$(date '+%Y-%m')" # backar som standard till YYYY-MM kataloger, en för varje månad
rsyncPort=22 # som standard antar vi att rsync använder ssh på port 22
purge=0 # som standard rensas inget gammalt
purgeDate='-2m' # standard: idag minus 2 månader
hostPattern='^([^:]+):(\/?.*)' # matcha värdnamn och sökväg från backupTarget

# Avsluta direkt om mål-katalogen inte existerar
if [[ ! "$backupTarget" =~ $hostPattern && ! -d "$backupTarget" ]]; then 
	echo "Backup target looks like directory that is not found" 1>&2
	exit 1; 
elif [ -n "${BASH_REMATCH[1]}" ]; then
	# Här har vi hittat vad som ser ut att vara ett värdnamn i backupTarget.
	# Så vi ska kontrollera att värden går att kontakta. 
	remoteHost=${BASH_REMATCH[1]}
	if ! nc -z -w 5 "$remoteHost" $rsyncPort >/dev/null 2>&1; then
		echo "Can't connect to target host on port $rsyncPort" 1>&2
		exit 1
	fi
fi

# Ta backup av samtliga argument som är 
# existerande kataloger.
for syncDir in $@; do
	if [ -d "$syncDir" ]; then
		# Lägg till --delete efter -a om rsync även ska radera i 
		# destinationskatalogen. Annars sparas allt gammalt som raderas i 
		# källkatalogen. 
		rsync -aS "$syncDir" "${backupTarget}/$destinationFormat/"
	fi
done

# Rensa allt som är äldre än purgeDate endast
# om purge är högre än 0. 
if (($purge >= 1)); then
	purgeTime=$(date -v"$purgeDate" '+%s')
	for targetDir in "${backupTarget}/*"; do
		lastModified=$(stat -f '%m' "$targetDir")
		if (($lastModified < $purgeTime)); then rm -rf "$targetDir"; fi
	done
fi

I toppen av skriptet står en rad med targetBackup där jag har angivit sökvägen till den externa USB-disken, den måste så klart ändras. Resten är för avancerade användare.

Nämnvärt är också att formatet som målkatalogerna skapas i är ÅR-Månad istället för År-Månad-Datum.

Som standard raderas inga gamla backuper, ändrar man purge=0 till purge=1 så kommer den ta bort alla kataloger som inte har ändrats på över 2 månader. Var försiktiga med det här eftersom den tar bort alla kataloger som är äldre än två månader i targetBackup sökvägen. Tänk scenariot där man varit på semester i en månad, plötsligt ändrar man lite i Pictures-katalogen, då körs skriptet och gör en ny backup, men sedan tar det bort allt som är äldre än 2 månader. Plötsligt har man mindre backuper än innan semestern.

Det går att ändra vid inställningen purgeDate men skriver man fel där så slutar det att fungera. Saker som *-3m* betyder 3 månader och *-1y* betyder 1 år.

Det här skriptet måste ha exekverbara rättigheter, vilket görs med följande kommando.

# chmod 0755 /Users/Stefan/bin/syncDir

Redan nu kan vi manuellt köra skriptet för att göra en initial backup.

# /Users/Stefan/bin/syncDir /Users/Stefan/Pictures

För att göra vår LaunchAgent aktiv måste man logga ut och logga in igen, då kommer skriptet köras varje gång katalogen Pictures ändras.

Backup över nätverk med rsync

Med befintligt system kan vi även backa filerna över nätverket, från andra datorer. Det kräver att vi använder OpenSSH och skapar lite [privata och publika nycklar](/articles/sshpubkeys.html).

Nu tänker jag introducera en ny maskin vid namn DatorB, det är vår klient som ska skicka sin backup till DatorA och DiskA.

Först ska vi se till att vi kan logga in på DatorA över OpenSSH protokollet. Då måste vi aktivera Delning och Fjärrinloggning på DatorA, här är en bild av hur det ser ut i Systeminställningar under Nätverk.

För säkerhet så tycker jag om att [byta port som SSH använder](/articles/macoschanges.html) men det är överkurs.

Nu kan det loggas in på DatorA men vi är inte färdiga, just nu krävs det lösenord varje gång vi loggar in och det är inte så bra för en backup som ska köras i bakgrunden.

På DatorB måste vi skapa [privata och publika nycklar](/articles/sshpubkeys.html) först.

# ssh-keygen -N '' -f ~/.ssh/id_rsa

Nu ska vi ta innehållet från /Users/Stefan/.ssh/id_rsa.pub och kopiera det till /Users/Stefan/.ssh/authorized_keys på DatorA. Alla nycklar i den filen, en per rad, kommer få logga in i maskinen utan lösenord. Så var noga med vad ni tillåter där.

Nu är vi redo att logga in från DatorB till DatorA över OpenSSH utan att skriva in lösenord.

För att göra saker enklare i skriptet ska vi även skapa en OpenSSH konfiguration som ger oss ett kortnamn för DatorA. DatorA kan mycket väl ha en kryptisk ip-adress för att kontaktas, vi vill hellre ha ett namn som är lätt att skriva och komma ihåg. I filen /Users/Stefan/.ssh/config ska det se ut ungefär så här.

Host DatorA
	HostName 10.10.220.110
	Port 22
	User stefan

IP-adressen är så klart påhittad men namnet vi använder från och med nu är DatorA.

Då kan vi lägga till en LaunchAgent som kör vårt skript. Den ser exakt likadan ut som tidigare.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>Label</key>
	<string>org.Stefan.BackupDocuments</string>
	<key>ProgramArguments</key>
	<array>
		<string>/Users/Stefan/bin/syncDir</string>
		<string>/Users/Stefan/Documents</string>
	</array>
	<key>WatchPaths</key>
	<array>
		<string>/Users/Stefan/Documents</string>
	</array>
</dict>

Sedan kopierar vi skriptet från ovan till DatorB, här ska vi göra en liten ändring i backupTarget eftersom den nu ska skicka backupen över nätverket till DatorA.

backupTarget='DatorA:/Backup'

Notera ändringen i backupTarget, istället för att bara skriva en sökväg har vi även lagt till namnet av DatorA som vi använder för att logga in över SSH. Det är för att rsync använder sig lägligt nog av SSH för att logga in och skicka all data krypterat till DatorA. På DatorA kommer datan hamna i vår kända sökväg /Volumes/DiskA.

Ni ser även att jag lagt till en extra katalog i sökvägen som heter DatorB, det är för att placera filerna från DatorB i en egen katalog. Den katalogen skapas inte av rsync så den måste finnas där.

Tyvärr var jag tvungen att bli lite onödigt komplicerad och ta bort ett par rader i skriptet som kollar huruvida målkatalogen existerar eller inte, det går inte att göra den kontrollen när målkatalogen är på en annan server. Inte på ett lätt och pålitligt sätt iaf.

Det går inte att bygga en sådan här lösning gratis utan att bli lite teknisk, men jag hoppas att jag skrev det tydligt nog för alla att följa med.

teknik/guider/backup_med_rsync_i_macintosh_os.txt · Senast uppdaterad: 2015-05-25 12:40 av stemid