Azure Storage 계정의 클라이언트 애플리케이션 오류 문제 해결
이 문서는 Azure Monitor에서 메트릭, 클라이언트 쪽 로그 및 리소스 로그를 사용하여 클라이언트 애플리케이션 오류를 조사하는 데 도움이 됩니다.
오류 진단
애플리케이션 사용자가 클라이언트 애플리케이션에서 보고한 오류에 대해 알려오는 경우가 있습니다. 또한 Azure Monitor는 NetworkError, ClientTimeoutError 또는 AuthorizationError와 같은 스토리지 서비스에서 다양한 응답 유형(ResponseType 차원)의 수를 기록합니다. Azure Monitor는 서로 다른 오류 유형의 수만 기록하지만 서버 쪽, 클라이언트 쪽 및 네트워크 로그를 검사하여 개별 요청에 대한 추가 정보를 얻을 수도 있습니다. 일반적으로는 스토리지 서비스에서 반환하는 HTTP 상태 코드를 통해 요청이 실패한 이유를 파악할 수 있습니다.
참고 항목
몇 가지 일시적인 오류가 표시되어야 합니다. 예를 들어 일시적인 네트워크 조건 또는 애플리케이션 오류로 인한 오류입니다.
다음 리소스에서 스토리지 관련 상태 및 오류 코드를 파악할 수 있습니다.
클라이언트에 HTTP 403(사용할 수 없음) 메시지가 표시됨
클라이언트 애플리케이션에서 HTTP 403(사용할 수 없음) 오류가 throw되는 경우 클라이언트가 스토리지 요청을 보낼 때 만료된 SAS(공유 액세스 서명)를 사용 중이어서일 가능성이 높습니다. 그러나 클럭 오차, 잘못된 키, 빈 헤더 등의 다른 원인일 수도 있습니다.
.NET용 Storage 클라이언트 라이브러리에서는 애플리케이션이 수행하는 스토리지 작업과 관련된 클라이언트 쪽 로그 데이터를 수집할 수 있습니다. 자세한 내용은 .NET Storage 클라이언트 라이브러리를 사용한 클라이언트 쪽 로깅을 참조하세요.
아래 표에는 이 문제가 발생함을 나타내는 Storage 클라이언트 라이브러리에서 생성된 클라이언트 쪽 로그의 샘플이 나와 있습니다.
원본 | Verbosity | Verbosity | 클라이언트 요청 ID | 작업 텍스트 |
---|---|---|---|---|
Microsoft.Azure.Storage | 정보 | 3 | 85d077ab- | Starting operation with location Primary per location mode PrimaryOnly. |
Microsoft.Azure.Storage | 정보 | 3 | 85d077ab - | Starting synchronous request to <https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Synchronous_and_Asynchronous_Requests#Synchronous_request> |
Microsoft.Azure.Storage | 정보 | 3 | 85d077ab - | Waiting for response. |
Microsoft.Azure.Storage | Warning | 2 | 85d077ab - | Exception thrown while waiting for response: The remote server returned an error: (403) Forbidden. |
Microsoft.Azure.Storage | 정보 | 3 | 85d077ab - | Response received. Status code = 403, Request ID = <Request ID>, Content-MD5 = , ETag = . |
Microsoft.Azure.Storage | Warning | 2 | 85d077ab - | Exception thrown during the operation: The remote server returned an error: (403) Forbidden.. |
Microsoft.Azure.Storage | 정보 | 3 | 85d077ab - | Checking if the operation should be retried. Retry count = 0, HTTP status code = 403, Exception = The remote server returned an error: (403) Forbidden.. |
Microsoft.Azure.Storage | 정보 | 3 | 85d077ab - | The next location has been set to Primary, based on the location mode. |
Microsoft.Azure.Storage | 오류 | 1 | 85d077ab - | Retry policy did not allow for a retry. Failing with The remote server returned an error: (403) Forbidden. |
이 시나리오에서는 클라이언트가 서버에 토큰을 보내기 전에 SAS 토큰이 만료되는 이유를 조사해야 합니다.
일반적으로 클라이언트에서 즉시 사용할 SAS를 만들 때 시작 시간을 설정해서는 안 됩니다. 현재 시간을 사용하여 SAS를 생성하는 호스트와 스토리지 서비스 간에 약간의 클럭 차이가 있는 경우에는 스토리지 서비스가 아직 유효하지 않은 SAS를 받을 가능성이 있습니다.
SAS에서 매우 짧은 만료 시간을 설정하지 마세요. 다시 말하지만, SAS를 생성하는 호스트와 스토리지 서비스 간의 작은 클록 차이로 인해 SAS가 예상보다 일찍 만료될 수 있습니다.
SAS 키의 버전 매개 변수(예:
sv
=2015-04-05)가 사용 중인 Storage 클라이언트 라이브러리 버전과 일치하나요? 항상 최신 버전의 스토리지 클라이언트 라이브러리를 사용하는 것이 좋습니다.스토리지 액세스 키를 다시 생성하면 기존 SAS 토큰이 무효화될 수 있습니다. 이 문제는 클라이언트 애플리케이션의 캐시에 대한 만료 시간이 긴 SAS 토큰을 생성하는 경우 발생할 수 있습니다.
스토리지 클라이언트 라이브러리를 사용하여 SAS 토큰을 생성하는 경우에는 유효한 토큰을 쉽게 작성할 수 있습니다. 그러나 스토리지 REST API를 사용 중이며 수동으로 SAS 토큰을 생성하는 경우에는 공유 액세스 서명을 사용하여 액세스 위임을 참조하세요.
클라이언트에 HTTP 404(찾을 수 없음) 메시지가 표시됨
클라이언트 애플리케이션이 서버에서 HTTP 404(찾을 수 없음) 메시지를 수신하는 경우 클라이언트가 사용하려는 엔터티, 테이블, Blob, 컨테이너, 큐 등의 개체가 스토리지 서비스에 없는 것입니다. 이러한 현상은 다음과 같은 여러 가지 이유로 인해 발생합니다.
클라이언트 또는 다른 프로세스가 이전에 개체를 삭제함
SAS(공유 액세스 서명) 권한 부여 문제입니다.
클라이언트 쪽 JavaScript 코드에 개체 액세스 권한이 없습니다.
네트워크 오류입니다.
클라이언트 또는 다른 프로세스가 이전에 개체를 삭제함
클라이언트가 스토리지 서비스에서 데이터를 읽거나 업데이트하거나 삭제하려고 하는 시나리오에서는 스토리지 서비스에서 해당 개체를 삭제한 이전 작업을 스토리지 리소스 로그에서 쉽게 식별할 수 있습니다. 로그 데이터에는 다른 사용자나 프로세스가 개체를 삭제한 것이 표시되는 경우가 많습니다. Azure Monitor 로그(서버 쪽)는 클라이언트가 개체를 삭제한 시기를 표시합니다.
클라이언트가 개체를 삽입하려고 하는 시나리오에서는 클라이언트가 새 개체를 만드는 경우 HTTP 404(찾을 수 없음) 응답이 발생하는 이유를 즉시 명확하지 않을 수 있습니다. 그러나 클라이언트가 Blob을 만드는 경우 Blob 컨테이너를 찾을 수 있어야 합니다. 클라이언트가 메시지를 만드는 경우 큐를 찾을 수 있어야 합니다. 클라이언트가 행을 추가하는 경우 테이블을 찾을 수 있어야 합니다.
스토리지 클라이언트 라이브러리의 클라이언트 쪽 로그를 사용하여 클라이언트가 스토리지 서비스에 특정 요청을 보내는 시기를 더 잘 이해할 수 있습니다.
Storage 클라이언트 라이브러리에서 생성된 다음 클라이언트 쪽 로그는 클라이언트가 만드는 Blob에 대한 컨테이너를 찾을 수 없는 경우의 문제를 보여 줍니다. 이 로그에는 다음 스토리지 작업에 대한 세부 정보가 포함됩니다.
요청 ID | 연산 |
---|---|
07b26a5d-... | DeleteIfExists 메서드를 사용하여 Blob 컨테이너를 삭제합니다. 이 작업에는 컨테이너의 존재 여부를 확인하는 HEAD 요청이 포함됩니다. |
e2d06d78 | CreateIfNotExists 메서드를 사용하여 Blob 컨테이너를 만듭니다. 이 작업에는 HEAD 컨테이너의 존재를 확인하는 요청이 포함됩니다. HEAD 404 메시지를 반환하지만 계속합니다. |
de8b1c3c-... | UploadFromStream 메서드를 사용하여 Blob을 만듭니다. PUT 404 메시지와 함께 요청이 실패합니다. |
로그 항목:
요청 ID | 작업 텍스트 |
---|---|
07b26a5d-... | Starting synchronous request to https://domemaildist.blob.core.windows.net/azuremmblobcontainer. |
07b26a5d-... | StringToSign = HEAD............x-ms-client-request-id:07b26a5d-....x-ms-date:Tue, 03 Jun 2014 10:33:11 GMT.x-ms-version:2014-02-14./domemaildist/azuremmblobcontainer.restype:container. |
07b26a5d-... | Waiting for response. |
07b26a5d-... | Response received. Status code = 200, Request ID = eeead849-...Content-MD5 = , ETag = "0x8D14D2DC63D059B". |
07b26a5d-... | Response headers were processed successfully, proceeding with the rest of the operation. |
07b26a5d-... | Downloading response body. |
07b26a5d-... | Operation completed successfully. |
07b26a5d-... | Starting synchronous request to https://domemaildist.blob.core.windows.net/azuremmblobcontainer. |
07b26a5d-... | StringToSign = DELETE............x-ms-client-request-id:07b26a5d-....x-ms-date:Tue, 03 Jun 2014 10:33:12 GMT.x-ms-version:2014-02-14./domemaildist/azuremmblobcontainer.restype:container. |
07b26a5d-... | Waiting for response. |
07b26a5d-... | Response received. Status code = 202, Request ID = 6ab2a4cf-..., Content-MD5 = , ETag = . |
07b26a5d-... | Response headers were processed successfully, proceeding with the rest of the operation. |
07b26a5d-... | Downloading response body. |
07b26a5d-... | Operation completed successfully. |
e2d06d78-... | Starting asynchronous request to https://domemaildist.blob.core.windows.net/azuremmblobcontainer . |
e2d06d78-... | StringToSign = HEAD............x-ms-client-request-id:e2d06d78-....x-ms-date:Tue, 03 Jun 2014 10:33:12 GMT.x-ms-version:2014-02-14./domemaildist/azuremmblobcontainer.restype:container. |
e2d06d78-... | Waiting for response. |
de8b1c3c-... | Starting synchronous request to https://domemaildist.blob.core.windows.net/azuremmblobcontainer/blobCreated.txt. |
de8b1c3c-... | StringToSign = PUT...64.qCmF+TQLPhq/YYK50mP9ZQ==........x-ms-blob-type:BlockBlob.x-ms-client-request-id:de8b1c3c-....x-ms-date:Tue, 03 Jun 2014 10:33:12 GMT.x-ms-version:2014-02-14./domemaildist/azuremmblobcontainer/blobCreated.txt. |
de8b1c3c-... | Preparing to write request data. |
e2d06d78-... | Exception thrown while waiting for response: The remote server returned an error: (404) Not Found.. |
e2d06d78-... | Response received. Status code = 404, Request ID = 353ae3bc-..., Content-MD5 = , ETag = . |
e2d06d78-... | Response headers were processed successfully, proceeding with the rest of the operation. |
e2d06d78-... | Downloading response body. |
e2d06d78-... | Operation completed successfully. |
e2d06d78-... | Starting asynchronous request to https://domemaildist.blob.core.windows.net/azuremmblobcontainer. |
e2d06d78-... | StringToSign = PUT...0.........x-ms-client-request-id:e2d06d78-....x-ms-date:Tue, 03 Jun 2014 10:33:12 GMT.x-ms-version:2014-02-14./domemaildist/azuremmblobcontainer.restype:container. |
e2d06d78-... | Waiting for response. |
de8b1c3c-... | Writing request data. |
de8b1c3c-... | Waiting for response. |
e2d06d78-... | Exception thrown while waiting for response: The remote server returned an error: (409) Conflict.. |
e2d06d78-... | Response received. Status code = 409, Request ID = c27da20e-..., Content-MD5 = , ETag = . |
e2d06d78-... | Downloading error response body. |
de8b1c3c-... | Exception thrown while waiting for response: The remote server returned an error: (404) Not Found.. |
de8b1c3c-... | Response received. Status code = 404, Request ID = 0eaeab3e-..., Content-MD5 = , ETag = . |
de8b1c3c-... | Exception thrown during the operation: The remote server returned an error: (404) Not Found.. |
de8b1c3c-... | Retry policy did not allow for a retry. Failing with The remote server returned an error: (404) Not Found.. |
e2d06d78-... | Retry policy did not allow for a retry. Failing with The remote server returned an error: (409) Conflict.. |
이 예제에서 로그는 클라이언트가 메서드의 요청 CreateIfNotExists
(요청 ID e2d06d78...)과 메서드의 요청 UploadFromStream
(de8b1c3c-...)을 인터리빙하고 있음을 보여 줍니다. 이 인터리빙은 클라이언트 애플리케이션이 이러한 메서드를 비동기적으로 호출하기 때문에 발생합니다. 클라이언트가 컨테이너의 Blob에 데이터 업로드를 시도하기 전에 해당 컨테이너를 만들도록 클라이언트에서 비동기 코드를 수정합니다. 모든 컨테이너를 미리 만드는 것이 가장 좋습니다.
SAS(공유 액세스 서명) 권한 부여 문제
클라이언트 애플리케이션이 작업에 필요한 권한을 포함하지 않는 SAS 키를 사용하려고 하면 스토리지 서비스는 HTTP 404(찾을 수 없음) 메시지를 클라이언트에 반환합니다. 동시에 Azure Monitor 메트릭에는 ResponseType 차원에 대한 AuthorizationError도 표시됩니다.
클라이언트 애플리케이션이 권한이 부여되지 않은 작업을 수행하려는 이유를 조사합니다.
클라이언트 쪽 JavaScript 코드에 개체 액세스 권한이 없습니다.
JavaScript 클라이언트를 사용하고 스토리지 서비스가 HTTP 404 메시지를 반환하는 경우 브라우저에서 다음 JavaScript 오류를 확인합니다.
SEC7120: http://localhost:56309 Access-Control-Allow-Origin 헤더에서 원본을 찾을 수 없습니다.
SCRIPT7002: XMLHttpRequest: 네트워크 오류 0x80070005 액세스가 거부되었습니다.
참고 항목
클라이언트 쪽 JavaScript 문제를 해결할 때는 Internet Explorer에서 F12 개발자 도구를 사용하여 브라우저와 스토리지 서비스 간에 교환되는 메시지를 추적할 수 있습니다.
이러한 오류가 발생하는 이유는 웹 페이지가 생성된 도메인과 다른 도메인에서 API를 호출할 수 없도록 하는 동일 원본 정책 보안 제한을 웹 브라우저가 구현하기 때문입니다.
JavaScript 문제를 해결하려면 클라이언트가 액세스하는 스토리지 서비스에 대해 CORS(원본 간 리소스 공유)를 구성할 수 있습니다. 자세한 내용은 Azure Storage 서비스에 대한 CORS(크로스-원본 자원 공유) 지원을 참조하세요.
다음 코드 샘플에서는 Contoso 도메인에서 실행되는 JavaScript가 Blob Storage 서비스의 Blob에 액세스할 수 있도록 Blob 서비스를 구성하는 방법을 보여 줍니다.
var connectionString = Constants.connectionString;
BlobServiceClient blobServiceClient = new BlobServiceClient(connectionString);
BlobServiceProperties sp = blobServiceClient.GetProperties();
// Set the service properties.
sp.DefaultServiceVersion = "2013-08-15";
BlobCorsRule bcr = new BlobCorsRule();
bcr.AllowedHeaders = "*";
bcr.AllowedMethods = "GET,POST";
bcr.AllowedOrigins = "http://www.contoso.com";
bcr.ExposedHeaders = "x-ms-*";
bcr.MaxAgeInSeconds = 5;
sp.Cors.Clear();
sp.Cors.Add(bcr);
blobServiceClient.SetProperties(sp);
네트워크 오류
네트워크 패킷 손실로 인해 스토리지 서비스가 HTTP 404 메시지를 클라이언트에 반환하는 경우가 있습니다. 예를 들어 클라이언트 애플리케이션이 테이블 서비스에서 엔터티를 삭제하는 경우 클라이언트가 테이블 서비스에서 "HTTP 404(찾을 수 없음)" 상태 메시지를 보고하는 스토리지 예외를 throw하는 것을 볼 수 있습니다. 그런데 Table Storage 서비스에서 테이블을 조사하면 서비스가 요청대로 엔터티를 삭제했음이 확인됩니다.
클라이언트의 예외 세부 정보에는 요청에 대한 테이블 서비스에서 할당된 요청 ID(7e84f12d…)가 포함됩니다. 이 정보를 사용하여 로그 항목의 작업이 인증된 방식을 설명하는 필드에서 검색하여 Azure Monitor의 스토리지 리소스 로그에서 요청 세부 정보를 찾을 수 있습니다. 메트릭을 사용하여 이러한 오류가 발생하는 시기를 파악한 다음 메트릭에서 이 오류를 기록한 시간을 기준으로 로그 파일을 검색할 수도 있습니다. 이 로그 항목에는 "HTTP(404) 클라이언트 기타 오류" 상태 메시지를 반환하며 실패한 삭제 작업이 표시됩니다. 동일한 로그 항목에는 열(813ea74f...)에서 클라이언트가 client-request-id
생성한 요청 ID도 포함됩니다.
또한 서버 쪽 로그에는 동일한 엔터티 및 동일한 클라이언트의 삭제 작업에 대해 동일한 client-request-id
값(813ea74f...)이 있는 다른 항목도 포함됩니다. 이 성공적인 삭제 작업은 실패한 삭제 요청 직전에 수행되었습니다.
이 시나리오의 가장 가능성이 큰 원인은 클라이언트가 엔터티에 대한 삭제 요청을 테이블 서비스로 보냈기 때문입니다. 이 요청은 성공했지만 서버에서 승인을 받지 못했습니다(아마도 임시 네트워크 문제로 인해). 그런 다음 클라이언트는 작업을 자동으로 다시 시도(동일한 client-request-id
사용)하고 엔터티가 이미 삭제되었으므로 이 재시도에 실패했습니다.
이 문제가 자주 발생하는 경우 클라이언트가 테이블 서비스에서 승인을 받지 못하는 이유를 조사해야 합니다. 문제가 일시적으로 발생하는 경우 "HTTP(404) 찾을 수 없음" 오류를 트래핑하고 클라이언트에서 기록하되 클라이언트가 작업을 계속 진행하도록 허용해야 합니다.
클라이언트에 HTTP 409(충돌) 메시지가 표시됨
클라이언트가 Blob 컨테이너, 테이블 또는 큐를 삭제하는 경우 이름을 다시 사용할 수 있게 되기까지 짧은 기간이 있습니다. 클라이언트 애플리케이션의 코드가 삭제된 후 동일한 이름을 CreateIfNotExists
사용하여 Blob 컨테이너를 즉시 다시 만드는 경우 메서드는 결국 HTTP 409(충돌) 오류로 실패합니다.
삭제/다시 만들기 패턴이 공통적으로 사용되는 경우 클라이언트 애플리케이션은 새 컨테이너를 만들 때마다 고유한 컨테이너 이름을 사용해야 합니다.
메트릭에 PercentSuccess가 낮게 표시되거나 분석 로그 항목에 트랜잭션 상태가 ClientOtherErrors 상태인 작업이 있음
Success 값과 동일한 ResponseType 차원은 HTTP 상태 코드를 기준으로 성공한 작업의 백분율을 캡처합니다. 상태 코드가 2XX인 작업은 성공으로 계산되지만 상태 코드가 3XX, 4XX 및 5XX 범위인 작업은 실패한 것으로 계산되고 성공 메트릭 값은 낮습니다. 스토리지 리소스 로그에서 이러한 작업은 트랜잭션 상태 ClientOtherError로 기록됩니다.
이러한 작업이 성공적으로 완료되었으므로 가용성과 같은 다른 메트릭에는 영향을 주지 않습니다. 정상적으로 실행되지만 작업 실패에 해당하는 HTTP 상태 코드를 표시하는 작업의 몇 가지 예는 다음과 같습니다.
- 예를 들어 GET 요청에서 존재하지 않는 Blob에 이르기까지 ResourceNotFound (찾을 수 없음 404)입니다.
- 리소스가 이미 있는 작업과
CreateIfNotExist
같은 ResourceAlreadyExists(충돌 409)입니다. - ConditionNotMet(수정되지 않음 304)는 예를 들어 클라이언트가 마지막 작업 이후 업데이트된 경우에만 이미지를 요청하기 위해 값 및 HTTP
If-None-Match
헤더를 보내는ETag
경우와 같은 조건부 작업에서 발생합니다.
스토리지 서비스에서 반환하는 공통 REST API 오류 코드의 목록은 공통 REST API 오류 코드 페이지에서 확인할 수 있습니다.
참고 항목
- Azure Blob Storage 모니터링
- Azure Files 모니터링
- Azure Queue Storage 모니터링
- Azure Table Storage 모니터링
- 성능 문제 해결
- 가용성 문제 해결
- Azure Storage 모니터링, 진단 및 문제 해결
도움을 요청하십시오.
질문이 있거나 도움이 필요한 경우 지원 요청을 생성하거나Azure 커뮤니티 지원에 문의하세요. Azure 피드백 커뮤니티에 제품 피드백을 제출할 수도 있습니다.