F#로 Azure Blob Storage 시작하기
F#로 Azure Blob Storage 시작하기
Azure Blob Storage는 비정형 데이터(객체/블롭)를 클라우드에 저장하는 서비스예요. 문서, 미디어 파일, 애플리케이션 설치 프로그램처럼 어떤 종류의 텍스트나 바이너리 데이터든 담을 수 있고, 그래서 객체 스토리지라고도 불러요.
이 글에서는 blob storage를 사용해 자주 하는 작업들을 F#으로 직접 해볼 거예요. 샘플은 Azure.Storage.Blobs 패키지를 써서 작성했고, 다루는 작업은 업로드 · 목록 조회 · 다운로드 · 삭제까지예요.
blob storage에 대한 개념적인 개요가 궁금하다면 .NET용 blob storage 가이드를 참고하면 돼요. 이 튜토리얼은 편의를 위해 연결 문자열(connection string)로 Azure를 인증할게요. 실제 운영 환경이라면 보안을 높이기 위해 관리 ID와 함께 Microsoft Entra ID를 쓰거나 Azure.Identity 라이브러리를 쓰는 걸 권장해요.
중요해요. 이 글은 최신
Azure.Storage.Blobs패키지를 사용해요. 예전 코드에서 쓰던WindowsAzure.Storage나Microsoft.Azure.Storage.Blob(더 이상 지원되지 않는 패키지)를 쓰고 있다면, 패키지 참조와 namespace 선언을 새 것으로 바꿔야 해요. 마이그레이션 방법은 Migrate to Azure.Storage.Blobs에서 확인할 수 있어요.
참고. F# 개발자라면 커뮤니티 라이브러리인 FSharp.Azure.Blob도 살펴볼 만해요. Azure Blob Storage를 쓸 때 더 F#답게(idiomatic) 만든 API를 제공해서, blob 작업을 자연스럽게 표현할 수 있답니다.
출처: Get started with Azure Blob Storage using F# (Microsoft Learn)
본문
사전 준비 (Prerequisites)
이 가이드를 따라가려면 먼저 Azure 스토리지 계정을 만들어야 해요. 그리고 그 계정의 **스토리지 액세스 키(access key)**도 필요해요.
F# 스크립트를 만들고 F# Interactive 시작하기
이 글의 샘플은 F# 애플리케이션에서도, F# 스크립트에서도 그대로 쓸 수 있어요. F# 스크립트를 만들려면 개발 환경에 .fsx 확장자 파일(예: blobs.fsx)을 만들면 돼요.
스크립트 실행 방법
F# Interactive(dotnet fsi)는 대화형으로 실행할 수도 있고, 커맨드라인에서 스크립트를 돌리려고 실행할 수도 있어요. 커맨드라인 문법은 다음과 같아요.
> dotnet fsi [options] [ script-file [arguments] ]
스크립트에 패키지 추가하기
Azure.Storage.Blobs 패키지를 설치하려면 #r "nuget: 패키지 이름"을 쓰고, 필요한 namespace를 open 해주면 돼요.
> #r "nuget: Azure.Storage.Blobs"
open Azure.Storage.Blobs
open Azure.Storage.Blobs.Models
open Azure.Storage.Blobs.Specialized
namespace 선언 추가하기
blobs.fsx 파일 맨 위에 다음 open 문들을 추가해 주세요.
open System
open System.IO
open Azure.Storage.Blobs // Namespace for Blob storage types
open Azure.Storage.Blobs.Models
open Azure.Storage.Blobs.Specialized
open System.Text
연결 문자열 가져오기
이 튜토리얼에는 Azure Storage 연결 문자열이 필요해요. 연결 문자열에 대한 자세한 내용은 Configure Storage Connection Strings를 참고해 주세요.
튜토리얼에서는 연결 문자열을 스크립트에 직접 입력할 거예요. 이렇게요.
let storageConnString = "..." // fill this in from your storage account
로컬 더미 데이터 만들기
블롭을 업로드할 대상이 필요하니까, 먼저 스크립트가 있는 디렉터리에 간단한 로컬 더미 데이터를 만들어 둘게요.
// Create a dummy file to upload
let localFile = "./myfile.txt"
File.WriteAllText(localFile, "some data")
blob 서비스 클라이언트 만들기
BlobContainerClient 타입 덕분에 컨테이너도 만들 수 있고, Blob storage에 저장된 블롭도 가져올 수 있어요. 컨테이너 클라이언트를 만드는 한 가지 방법은 다음과 같아요.
let container = BlobContainerClient(storageConnString, "mycontainer")
이제 Blob storage에서 데이터를 읽고 쓰는 코드를 짤 준비가 됐어요.
컨테이너 만들기
이 예제는 컨테이너가 아직 없을 때 만들어 주는 코드예요.
container.CreateIfNotExists()
기본적으로 새 컨테이너는 private(비공개) 상태라서, 이 컨테이너의 블롭을 다운로드하려면 스토리지 액세스 키를 지정해야 해요. 컨테이너 안의 파일을 모두에게 공개하고 싶다면, 아래 코드처럼 컨테이너를 public으로 바꾸면 돼요.
let permissions = PublicAccessType.Blob
container.SetAccessPolicy(permissions)
public 컨테이너의 블롭은 인터넷의 누구든 볼 수 있지만, 수정하거나 삭제하는 건 적절한 계정 액세스 키나 공유 액세스 서명(shared access signature)이 있어야만 가능해요.
컨테이너에 블롭 업로드하기
Azure Blob Storage는 블록(block) 블롭과 페이지(page) 블롭을 지원해요. 대부분의 경우 블록 블롭이 권장되는 타입이에요.
파일을 블록 블롭에 업로드하려면 컨테이너 클라이언트를 가져와서 그걸로 블록 블롭 참조를 얻으면 돼요. 블롭 참조를 얻고 나면 Upload 메서드를 호출해서 어떤 스트림 데이터든 업로드할 수 있어요. 이 작업은 블롭의 내용을 덮어쓰고, 블롭이 없다면 새 블록 블롭을 만들어요.
// Retrieve reference to a blob named "myblob.txt".
let blockBlob = container.GetBlobClient("myblob.txt")
// Create or overwrite the "myblob.txt" blob with contents from the local file.
use fileStream = new FileStream(localFile, FileMode.Open, FileAccess.Read, FileShare.Read)
do blockBlob.Upload(fileStream)
컨테이너의 블롭 나열하기
컨테이너의 블롭을 나열하려면 먼저 컨테이너 참조를 얻어야 해요. 그다음 컨테이너의 GetBlobsByHierarchy 메서드로 그 안의 블롭이나 디렉터리를 가져올 수 있어요. 이 메서드는 블롭의 속성과 메타데이터에 접근할 수 있게 해주는 BlobItem 객체를 반환해요.
for item in container.GetBlobsByHierarchy() do
printfn $"Blob name: {item.Blob.Name}"
예를 들어 photos라는 컨테이너에 다음과 같은 블록 블롭들이 있다고 생각해 볼게요.
photo1.jpg
2015/architecture/description.txt
2015/architecture/photo3.jpg
2015/architecture/photo4.jpg
2016/architecture/photo5.jpg
2016/architecture/photo6.jpg
2016/architecture/description.txt
2016/photo7.jpg \
이 컨테이너에서 GetBlobsByHierarchy를 호출하면(위 샘플처럼) 계층 구조(hierarchical) 목록이 반환돼요.
Directory: https://<accountname>.blob.core.windows.net/photos/2015/
Directory: https://<accountname>.blob.core.windows.net/photos/2016/
Block blob of length 505623: https://<accountname>.blob.core.windows.net/photos/photo1.jpg
블롭 다운로드하기
블롭을 다운로드하려면 먼저 블롭 참조를 가져온 뒤 DownloadTo 메서드를 호출하면 돼요. 아래 예제는 DownloadTo 메서드로 블롭 내용을 스트림 객체에 옮긴 다음, 그 스트림을 로컬 파일로 저장하는 방식이에요.
// Retrieve reference to a blob named "myblob.txt".
let blobToDownload = container.GetBlobClient("myblob.txt")
// Save blob contents to a file.
do
use fileStream = File.OpenWrite("path/download.txt")
blobToDownload.DownloadTo(fileStream)
DownloadContent 메서드를 쓰면 블롭 내용을 텍스트 문자열로 바로 받아올 수도 있어요.
let text = blobToDownload.DownloadContent().Value.Content.ToString()
블롭 삭제하기
블롭을 삭제하려면 먼저 블롭 참조를 얻고, 그 참조의 Delete 메서드를 호출하면 돼요.
// Retrieve reference to a blob named "myblob.txt".
let blobToDelete = container.GetBlobClient("myblob.txt")
// Delete the blob.
blobToDelete.Delete()
블롭을 페이지 단위로 비동기 나열하기
블롭이 아주 많아서 한 번의 나열 작업으로 반환되는 결과 수를 제어하고 싶다면, 결과를 페이지 단위로 나눠서 블롭을 나열할 수 있어요. 이 예제는 결과를 페이지로 반환하는 방법을 보여줘요.
아래 예제는 BlobContainerClient의 GetBlobsByHierarchy 메서드로 계층 구조 목록을 만드는 방식이에요.
let ListBlobsSegmentedInHierarchicalListing(container:BlobContainerClient) =
// List blobs to the console window, with paging.
printfn "List blobs in pages:"
// Call GetBlobsByHierarchy to return an async collection
// of blobs in this container. AsPages() method enumerate the values
//a Page<T> at a time. This may make multiple service requests.
for page in container.GetBlobsByHierarchy().AsPages() do
for blobHierarchyItem in page.Values do
printf $"The BlobItem is : {blobHierarchyItem.Blob.Name} "
printfn ""
이제 이 계층 구조 나열 루틴을 이렇게 사용할 수 있어요. 먼저 더미 데이터를 업로드해 봅시다(앞서 이 튜토리얼에서 만든 로컬 파일을 사용해요).
for i in 1 .. 100 do
let blob = container.GetBlobClient($"myblob{i}.txt")
blob.Upload(localFile)
이제 루틴을 호출해 보세요.
추가(append) 블롭에 쓰기
추가(append) 블롭은 로깅처럼 데이터를 계속 덧붙이는 작업에 최적화된 타입이에요. 블록 블롭처럼 여러 블록으로 구성되지만, 추가 블롭에 새 블록을 더하면 항상 블롭의 맨 끝에 붙어요. 기존 블록은 추가 블롭에서 수정하거나 삭제할 수 없답니다. 또 추가 블롭에서는 블록 ID가 노출되지 않아요(블록 블롭과 달리).
추가 블롭의 각 블록은 크기가 달라도 되고 최대 4MB, 블록 개수는 최대 50,000개예요. 따라서 추가 블롭의 최대 크기는 대략 195GB보다 조금 큰(4MB × 50,000 블록) 정도가 돼요.
다음 예제는 새 추가 블롭을 만들고 간단한 로깅처럼 데이터를 덧붙이는 상황을 흉내 내요.
let appendContainer = BlobContainerClient(storageConnString, "my-append-blobs")
// Create the container if it does not already exist.
appendContainer.CreateIfNotExists() |> ignore
// Get a reference to an append blob.
let appendBlob = appendContainer.GetAppendBlobClient("append-blob.log")
// Create the append blob. Note that if the blob already exists, the
// CreateIfNotExists() method will overwrite it. You can check whether the
// blob exists to avoid overwriting it by using appendBlob.Exists().
appendBlob.CreateIfNotExists()
let numBlocks = 10
// Generate an array of random bytes.
let rnd = Random()
let bytesArray = Array.zeroCreate<byte>(numBlocks)
rnd.NextBytes(bytesArray)
// Simulate a logging operation by writing text data and byte data to the
// end of the append blob.
for i in 0 .. numBlocks - 1 do
let msg = sprintf $"Timestamp: {DateTime.UtcNow} \tLog Entry: {bytesArray.[i]}\n"
let array = Encoding.ASCII.GetBytes(msg);
use stream = new MemoryStream(array)
appendBlob.AppendBlock(stream)
// Read the append blob to the console window.
let downloadedText = appendBlob.DownloadContent().Value.Content.ToString()
printfn $"{downloadedText}"
세 가지 블롭 타입의 차이를 더 알고 싶다면 Understanding Block Blobs, Page Blobs, and Append Blobs를 참고해 주세요.
동시 접근(Concurrent access)
여러 클라이언트나 여러 프로세스 인스턴스가 한 블롭에 동시에 접근해야 한다면, ETag나 **임대(lease)**를 사용할 수 있어요.
- ETag — 다른 프로세스가 블롭이나 컨테이너를 수정했는지 감지할 수 있게 해줘요.
- Lease — 일정 시간 동안 블롭에 대해 독점적이고 갱신 가능한 쓰기·삭제 접근 권한을 얻을 수 있게 해줘요.
자세한 내용은 Managing Concurrency in Microsoft Azure Storage 블로그를 참고하세요.
컨테이너 이름 짓기
Azure storage의 모든 블롭은 반드시 컨테이너 안에 있어야 해요. 컨테이너는 블롭 이름의 일부를 이루죠. 예를 들어 다음 샘플 블롭 URI에서 mydata가 컨테이너 이름이에요.
https://storagesample.blob.core.windows.net/mydata/blob1.txthttps://storagesample.blob.core.windows.net/mydata/photos/myphoto.jpg
컨테이너 이름은 유효한 DNS 이름이어야 하고, 다음 규칙을 따라야 해요.
- 컨테이너 이름은 문자나 숫자로 시작해야 하고, 문자·숫자·하이픈(
-)만 포함할 수 있어요. - 모든 하이픈(
-)은 바로 앞뒤에 문자나 숫자가 있어야 해요. 즉 연속된 하이픈은 허용되지 않아요. - 컨테이너 이름의 모든 문자는 소문자여야 해요.
- 컨테이너 이름 길이는 3자에서 63자 사이여야 해요.
컨테이너 이름은 항상 소문자여야 해요. 이름에 대문자가 들어가거나 다른 규칙을 어기면 **400 오류(Bad Request)**를 받을 수 있어요.
블롭 보안 관리하기
기본적으로 Azure Storage는 계정 액세스 키를 가진 계정 소유자만 접근할 수 있게 제한해서 데이터를 보호해요. 스토리지 계정의 블롭 데이터를 공유해야 할 때는, 계정 액세스 키의 보안을 훼손하지 않는 방식으로 공유하는 게 중요해요. 또 블롭 데이터를 암호화하면 전송 중(over the wire)에도, Azure Storage 안에 저장된 상태에서도 안전하게 지킬 수 있어요.
블롭 데이터 접근 제어하기
기본적으로 스토리지 계정의 블롭 데이터는 스토리지 계정 소유자만 접근할 수 있어요. Blob storage에 요청을 인증하려면 기본적으로 계정 액세스 키가 필요하죠. 다만 일부 블롭 데이터는 다른 사용자에게도 공개하고 싶을 수 있어요.
블롭 데이터 암호화하기
Azure Storage는 클라이언트 쪽과 서버 쪽 양쪽에서 블롭 데이터 암호화를 지원해요.