Glacier ist ein Speicherdienst der zu den Amazon Web Services gehört. Der Service wurde im August 2012 lanciert und funktioniert auf den ersten Blick ähnlich wie S3. Glacier erlaubt es Daten in die Amazon Cloud hoch- und wieder herunterzuladen. Glacier bietet das gleiche Service Level Agreement wie S3, ist designed für eine Durability von 99.999999999% (elf neun), eine Verfügbarkeit von 99.99% und soll einen Ausfall von zwei Amazon Rechenzentren verkraften (alles gleich wie bei S3 Standard).
Dieses Video von Amazon zeigt einen Überblick über Glacier: http://www.youtube.com/watch?v=OUTOEX4zbD4
Bei genauerem Hinschauen wird man aber einige Unterschiede zu S3 feststellen. Als erstes fällt auf dass die Kosten viel tiefer liegen als bei S3.
In der US Region kostet 1 GB pro Monat bei S3 Standard 0.095 US-Cent. Mit S3 Reduced Redundancy Storage sind es noch 0.076 Cent. Bei Glacier kostet 1 GB pro Monat nur 1 US-Cent. Für S3 sind das die Kosten für das erste TB. Bei grösseren Datenmengen fallen die Preis pro GB. Bei Glacier zahlt man immer 1 Cent pro GB egal wieviele Daten man speichert. Wenn auf Glacier weniger als 1 GB gespeichert ist, wird das Minimum von 1 Cent fällig.
Ein interessanter Dienst um die Kosten des Cloudcomputing zu berechnen und zu planen ist PlanForCloud.com. In deren Blog findet man einen Artikel der die Kosten von Glacier und S3 vergleicht: http://blog.planforcloud.com/2012/08/cost-comparison-amazon-glacier-vs-s3.html
Bei Glacier muss man beachten dass beim Löschen zusätzliche Kosten von 3 Cent pro GB entstehen, wenn die Dateien jünger als 90 Tage sind (Differenz zwischen Upload- und Löschdatum). Die 3 Cent werden anteilsmässig verrechnet. Wenn Daten im ersten Monat nach dem Upload gelöscht werden, werden die ganzen 3 Cent fällig. Löschen im zweiten Monat kostet noch 2 Cent, im dritten Monat sind es 1 Cent und nach 90 Tagen ist das Löschen kostenlos.
Der reine Upload Datentransfer ist bei beiden Diensten kostenlos. Allerdings kommen Kosten für die Requests dazu. Bei S3 (put,copy,post,list,get) sind das 1 Cent pro Tausend und bei Glacier (upload,retrieval) sind es 5 Cent pro Tausend Requests. Bei Glacier werden die Kosten für die Requests anteilsmässig abgerechnet. Wenn nur 30 Requests (zum Beispiel ein Upload pro Tag) in einem Monat durchgeführt wurden zahlt man 1 Cent.
Die Kosten für den Download ist bei beiden Diensten gleich. Das erste GB pro Monat ist gratis und danach zahlt man 12 Cent pro GB bis 10 TB/Monat. Bei Glacier wird es nun aber kompliziert, da je nachdem wieviel und wie schnell man Daten wieder herunterlädt, kommen zusätzliche, zum Teil erhebliche, Kosten dazu.
Glacier erlaubt es 5% der gespeicherten Daten pro Monat ohne weitere Kosten downzuloaden. Zum Beispiel wenn 250 GB Daten auf Glacier gespeichert sind kann man 0.17% oder 0.417GB pro Tag ohne weitere Kosten herunterladen (hier wird ein Monat mit 30 Tagen angenommen). Über diese Limite hinaus wird es kompliziert und ich verweise hier auf die offizielle FAQ sowie auf mehrere Blogposts.
Kurz gesagt, je mehr Daten und je kürzer der Zeitraum ist, über den die Daten heruntergeladen werden, desto teurer wird es.
Glacier ist wegen dieser Retrievalkosten vorallem für Backups und die Archivierung von Daten gedacht. In diesen Anwendungen werden Daten vorallem hochgeladen und gelagert und selten wieder heruntergeladen. Bei der Archivierung ist es zudem oft möglich Downloads über mehrere Tage zu verteilen was die Downloadkosten zum Teil erheblich senkt. Backups müssen dagegen komplett und in kurzer Zeit wieder heruntergeladen werden können, was zu hohen Retrieval Kosten führen kann. Allerdings sollte dieser Fall selten auftreten und man ist froh überhaupt ein Backup zu haben. Glacier ist hier eine Art Versicherung bei der die Prämie erst im Schadenfall gezahlt werden muss.
Ein weiterer wichtiger Unterschied zu S3 ist der dass bei Glacier die Daten nicht sofort heruntergeladen werden können. Zuerst muss ein Retrievaljob gestartet werden. Dieser läuft zwischen 3 und 5 Stunden und erst wenn dieser Job erfolgreich beendet wurde können die verlangten Dateien heruntergeladen werden. Diese 3-5 Stunden Verzögerung gilt es zu beachten wenn eine Backuplösung mit Glacier aufgebaut wird.
Es sei hier noch darauf hingewiesen dass seit November 2012 Glacier mit S3 verknüpft werden kann. Diese Verknüpfung ermöglicht es Dateien zwischen S3 und Glacier hin- und herzuverschieben. Damit lassen sich Anwendungsfälle umsetzen, wie der dass die Daten zuerst auf S3 hochgeladen und erst wenn man sie eine längere Zeit nicht mehr benötigt auf Glacier verschoben werden. Zum Beispiel könnte man Backups zuerst auf S3 speichern um im Falle eines Disasterrecovery die Daten sofort wieder herunterladen zu können und so die 3-5 Stunden Verzögerung von Glacier umgeht, bei allerdings höheren Storagekosten. Backups die älter als eine Woche oder ein Monat sind würden laufend auf Glacier verschoben um Storagekosten zu sparen. Dies wird mit einer Lifecycle Konfiguration bewerkstelligt, die einmal installiert, automatisch die Dateien verschiebt. Beachten muss man das Files die von S3 auf Glacier verschoben werden, nur mit S3 zurückgespielt und gelöscht werden können.
Die folgenden Beispiele zeigen wie man mit Java und dem AWS SDK Daten direkt in Glacier hoch- und wieder herunterlädt. Die Integration mit S3 lassen wir hier mal aussen vor und sparen uns dies für einen späteren Blogpost auf.
Bevor wir beginnen wird ein Account bei Amazon benötigt. Da bei den Amazon Web Services keine Setupgebühren fällig werden, können wir gratis einen Account erstellen und uns bei den entsprechenden Diensten anmelden. Auch wenn keine Setupgebühren verrechnet werden muss man beachten das die folgenden Beispiele Kosten generieren, wenn man sie ausführt.
Glacier kennt die Begriffe Archiv und Vault. Ein Archiv ist entweder eine einzelne Datei oder mehrere Dateien komprimiert in einem TAR/ZIP oder einem anderen Archivformat. Archive werden in Vaults abgespeichert.
Glacier erlaubt es Archive bis zu einer Grösse von 40TB abzuspeichern. Beachten muss man das pro Uploadrequest nur 4GB gesendet werden können. Grössere Archive müssen aufgeteilt und in mehreren Requests hochgeladen werden. Amazon empfiehlt bereits Dateien grösser als 100MB in mehrere Teile aufzusplitten.
Vault Management
Um überhaupt Daten in Glacier hochzuladen muss ein Vault erstellt werden. Es können bis zu 1000 Vaults pro Amazon Account erstellt werden. Der Name des Vault kann zwischen 1 und 255 Zeichen lang sein, darf die Zeichen a-z, A-Z, 0-9 und die Sonderzeichen _ (Unterstrich), – (Bindestrich) und . (Punkt) enthalten.
|
|
String accessKeyId = "...."; String secretAccessKey = "...."; AWSCredentials credentials = new BasicAWSCredentials(accessKeyId, secretAccessKey); AmazonGlacierClient client = new AmazonGlacierClient(credentials); client.setEndpoint("https://glacier.us-east-1.amazonaws.com/"); CreateVaultResult result = client.createVault(new CreateVaultRequest().withVaultName("myVault")); System.out.println("Location: " + result.getLocation()); |
Um Requests abzusetzen müssen wir uns bei AWS einloggen. Dazu benötigen wir eine Access Key Id und einen Secret Access Key. Diese findet man, wenn man sich auf der AWS Webseite eingeloggt hat, im Menu MyAccount / Security Credentials. In diesem Beispiel benutzen wir die Klasse BasicAWSCredentials deren Constructor die beiden Keys erwartet. Eine weitere Möglichkeit ist es die Keys mit PropertiesCredentials aus einem Propertyfile einzulesen.
Als nächstes wird eine Instanz des AmazonGlacierClient erstellt über welche alle Requests zu Glacier laufen. Hier müssen wir auch den Endpoint bestimmen mit welchem wir arbeiten möchten. Die Endpoints sind die Rechenzentren von Amazon, die über die Welt verteilt sind. Beachten sollte man das nicht jeder Dienst in jeder Region verfügbar ist. Ausserdem fallen unterschiedliche Kosten in den Regionen an.
Nachdem wir nun eine Instanz des AmazonGlacierClient erstellt haben können wir einen CreateVaultRequest senden. In diesem Beispiel erstellen wir einen Vault mit Namen “myVault”.
Um einen Vault zu löschen muss ein DeleteVaultRequest mit dem Namen des Vaults gesendet werden.
|
|
client.deleteVault(new DeleteVaultRequest().withVaultName("myVault")); |
Der Löschrequest ist nur erfolgreich wenn der Vault keine Archive gemäss dem letzten Inventar enthält und wenn keine Schreiboperationen seit dem letzten Inventar durchgeführt wurden. Wenn diese Bedingungen nicht erfüllt sind wirft deleteVault eine Exception. Vault Inventare werden einmal pro Tag erstellt.
Mit einem ListVaultsRequest können wir alle Vaults eines Accounts auflisten. Das Resultat enthält Informationen wie Name des Vault, wann er erstellt wurde, wann das letzte Inventar durchgeführt wurde und die Anzahl Archive die im Vault gespeichert sind. Beachten muss man dass die Anzahl Archive kein Realtime Wert ist sondern nur einmal täglich, wenn der Inventarjob läuft, aktualisiert wird. Wenn also seit dem letzten Inventar Archive hochgeladen wurden sind diese nicht in der Anzahl enthalten.
|
|
ListVaultsResult lvResult = client.listVaults(new ListVaultsRequest()); for (DescribeVaultOutput v : lvResult.getVaultList()) { System.out.println(v.getVaultName()); System.out.println(v.getCreationDate()); System.out.println(v.getLastInventoryDate()); System.out.println(v.getNumberOfArchives()); } |
Archiv Upload
Für den Upload bietet das AWS SDK eine High- und eine Low-Level API an. In diesem Blog beschränken wir uns auf die High-Level API. Weitere Informationen zu der Low-Level API findet man hier:
http://docs.aws.amazon.com/amazonglacier/latest/dev/uploading-an-archive-single-op-using-java.html
Die High-Level API besteht vereinfacht gesagt aus der Klasse ArchiveTransferManager. Diese Klasse sendet kleine Dateien in einem Stück und splittet grosse Dateien auf und sendet sie in mehreren Requests. Mit der Low-Level API müssen wir diese Dinge selber managen, haben aber dafür auch mehr Kontrolle über den Prozess. Zusätzlich sollte man darauf achten das die Daten zwar mit einer SSL Verbindung verschlüsselt über das Internet übertragen, aber dann unverschlüsselt bei Amazon abgespeichert werden. Es empfiehlt sich daher die Daten vor dem Upload zu verschlüsseln. Aktuell (Stand SDK 1.4.1) muss man die Verschlüsselung selber implementieren da keine Klasse für Glacier ähnlich wie AmazonS3EncryptionClient existiert, welche die Verschlüsselung vor dem Upload transparent durchführt.
|
|
String accessKeyId = "...."; String secretAccessKey = "...."; AWSCredentials credentials = new BasicAWSCredentials(accessKeyId, secretAccessKey); AmazonGlacierClient client = new AmazonGlacierClient(credentials); client.setEndpoint("https://glacier.us-east-1.amazonaws.com/"); ArchiveTransferManager atm = new ArchiveTransferManager(client, credentials); UploadResult result = atm.upload("testvault", "a test file", Paths.get("e:/test.txt").toFile()); System.out.println("Archive ID: " + result.getArchiveId()); |
Die upload Methode des ArchiveTransferManager erwartet als Parameter den Namen des Vaults, eine Beschreibung des Archivs und die Datei selber. In diesem Beispiel laden wir eine Textdatei mit Namen test.txt hoch. Mit Glacier werden Dateien nicht mit ihrem Namen angesprochen sondern mit einer Archiv Id, welche wir nach erfolgreichem Upload erhalten. Dank diesem Mapping von Datei zu Id können Archive nicht irrtümlicherweise überschrieben werden. Wenn wir das Programm mehrmals ausführen dann werden auf Glacier mehrere Archive erstellt, auch wenn der Inhalt des Archivs immer der gleiche ist. Dieses Verfahren hat aber auch den Nachteil das ein Archiv nicht aktualisiert werden kann. Man muss das aktualisierte Archiv uploaden und das zu ersetzende Archiv löschen.
Es wird empfohlen die Verknüpfung zwischen Dateien und Archive Id in einem eigenen System zu verwalten. Wir können diese Informationen zum Beispiel in einer simplen Textdatei oder einer Datenbank speichern. Denkbar wäre es auch diese ID bei Amazon selber in SimpleDB, RDS oder DynamoDB zu speichern.
Die Archive Ids lassen sich auch von Glacier selber mit einem Vault Inventory Retrieval Job auflisten. Hier muss man beachten dass auch dieser Job, wie der Archive Retrieval Job, das Ergebnis erst nach 3 bis 5 Stunden liefert. Dies ist auch einer der Gründe wieso man die Archive Id selber managen sollte. Wenn man die Id nicht kennt, kann es bis zu 10 Stunden dauern bis man eine Datei herunterladen kann. 3-5 Stunden für den Vault Inventory Retrieval Job und dann nochmals 3-5 Stunden für den Archive Retrieval Job.
Das folgende Beispiel zeigt wie ein Vault Inventory heruntergeladen werden kann.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
|
String accessKeyId = "...."; String secretAccessKey = "...."; AWSCredentials credentials = new BasicAWSCredentials(accessKeyId, secretAccessKey); AmazonGlacierClient client = new AmazonGlacierClient(credentials); client.setEndpoint("https://glacier.us-east-1.amazonaws.com/"); InitiateJobRequest initJobRequest = new InitiateJobRequest().withVaultName("testvault").withJobParameters( new JobParameters().withFormat("CSV").withType("inventory-retrieval")); InitiateJobResult initJobResult = client.initiateJob(initJobRequest); String jobId = initJobResult.getJobId(); System.out.println("Job ID: " + jobId); System.out.println("Waiting 3 hours...."); TimeUnit.HOURS.sleep(3); DescribeJobRequest describeJobRequest = new DescribeJobRequest("testvault", jobId); DescribeJobResult result = client.describeJob(describeJobRequest); while (!result.isCompleted()) { System.out.println("Waiting 30 minutes...."); TimeUnit.MINUTES.sleep(30); result = client.describeJob(describeJobRequest); } System.out.println("Job complete"); GetJobOutputResult jobOutputResult = client.getJobOutput(new GetJobOutputRequest().withVaultName("testvault").withJobId( jobId)); Files.copy(jobOutputResult.getBody(), Paths.get("joboutput.csv")); |
Als erstes muss ein “inventory-retrieval” Job mit der Klasse InitiateJobRequest erstellt werden. Diese Klasse erwartet den Vault Name als Argument. Zusätzlich kann angegeben werden in welchem Format man den Output erwartet. Möglich sind JSON und CSV. Nachdem man den Job mit initiateJob gestartet hat erhält man eine Job Id. Mit Hilfe dieser Job Id kann der aktuelle Status des Jobs mit einem DescribeJobRequest abgefragt werden. Wenn der Job erfolgreich beendet wurde, wird mit einem GetJobOutputRequest der Output heruntergeladen. In diesem Beispiel erhalten wir den Output im CSV Format, der wie folgt aussieht:
|
|
ArchiveId,ArchiveDescription,CreationDate,Size,SHA256TreeHash f7rdsN....,"E:\test.txt",2013-02-17T06:56:23Z,663,f6f... |
Dieses Beispiel benutzt Polling um den Jobstatus abzufragen. Dies ist zwar sehr einfach umzusetzen aber im ungünstigsten Fall warten wir bis zu 30 Minuten länger als wir müssten. Amazon empfiehlt in der Dokumentation einen anderen Ablauf mit SNS und SQS. Ein Code Beispiel findet man in der Dokumentation: http://docs.aws.amazon.com/amazonglacier/latest/dev/retrieving-vault-inventory-java.html
Archiv Retrieval
Wie beim Archiv Upload gibt es auch beim Download eine High- und eine Low-Level API. In der High-Level API wird mit Hilfe des ArchiveTransferManager ein Archiv heruntergeladen. Diese Klasse benötigt neben dem AmazonGlacierClient auch eine Instanz des AmazonSQSClient und des AmazonSNSClient.
|
|
String accessKeyId = "...."; String secretAccessKey = "...."; AWSCredentials credentials = new BasicAWSCredentials(accessKeyId, secretAccessKey); AmazonGlacierClient glacierClient = new AmazonGlacierClient(credentials); glacierClient.setEndpoint("https://glacier.us-east-1.amazonaws.com/"); AmazonSQSClient sqsClient = new AmazonSQSClient(credentials); AmazonSNSClient snsClient = new AmazonSNSClient(credentials); sqsClient.setEndpoint("https://glacier.us-east-1.amazonaws.com/"); snsClient.setEndpoint("https://glacier.us-east-1.amazonaws.com/"); ArchiveTransferManager atm = new ArchiveTransferManager(glacierClient, sqsClient, snsClient); String archiveId = "K94..."; atm.download("testvault", archiveId, Paths.get("e:/test.txt").toFile()); |
Der ArchiveTransferManager startet den “archive-retrieval” Job, konfiguriert den Job so dass der Output via SNS/SQS verschickt wird. Danach wartet das Programm bis der Output auf SQS eintrifft. Die Archivdaten werden dann mit GetJobOutputRequest heruntergeladen.
Mehr Kontrolle über den Retrieval Prozess bietet die Low-Level API an. Zum Beispiel lassen sich bestimmte Teile eines Archives herunterzuladen. Dazu kann ein Byte Bereich angegeben werden (zum Beispiel: bytes=0-1048575 um das erste MB herunterzuladen). Dies wird dann wichtig wenn die Retrievalkosten möglichst tief gehalten werden sollen indem man den Download über mehrere Stunden/Tage verteilt.
Beispiele mit der Low-Level API findet man hier:
http://docs.aws.amazon.com/amazonglacier/latest/dev/downloading-an-archive-using-java.html
Archive löschen
Ein Archiv wird mit einem DeleteArchiveRequest gelöscht. Der Request erwartet als Argument den Namen des Vaults und die Archiv Id.
|
|
String archiveID = "K94B...."; client.deleteArchive(new DeleteArchiveRequest().withVaultName("testvault").withArchiveId(archiveID)); |