-
AWS Secret Manager를 이용한 반복 체크프로그래밍 2024. 11. 21. 09:48
필자는 애플리케이션을 설정할 때 환경 변수 이외에도 각종 설정을 Secret Manager에 기록하여 사용하고 있다.
이번에 살펴볼 내용은 Secret Manager를 사용할 때 설정값의 변경되고 해당 변경값을 계속적으로 읽어 반영해야 하는 경우이다.
먼저 Secret Manager를 이용한 기본적인 코드는 아래와 같다.
var client = new AmazonSecretsManagerClient(keyId, accessKey, RegionEndpoint.GetBySystemName(region)); var request = new GetSecretValueRequest() { SecretId = secretName, VersionStage = versionStage, // VersionStage defaults to AWSCURRENT if unspecified. }; var response = await client.GetSecretValueAsync(request, _cancellationToken);
기본 코드이고 Secret Manager를 등록하여 제공하는 예제 코드이다.
이제 이 코드를 이용하여 5초마다 반복적으로 읽어 들이는 코드를 작성하자.
아래와 같다.
public class SecretManagerV2Loader : IEnvironmentLoader { public ConcurrentDictionary<string, string> Data { get; } = new(); private Task _loopTask; private readonly CancellationToken _cancellationToken = default; private readonly ILogger _logger; public SecretManagerV2Loader(ILogger<SecretManagerV2Loader> logger) { _logger = logger; } public Task Start(Dictionary<string, string> parameters) { if (_loopTask.xIsNotEmpty()) { throw new InvalidOperationException("This loader has already been started."); } return StartLoop(parameters["KEY_ID"], parameters["ACCESS_KEY"], parameters["REGION"], parameters["VERSION_STAGE"]); } private async Task StartLoop(string keyId, string accessKey, string region, string versionStage) { string secretName = "demp/test"; while (!_cancellationToken.IsCancellationRequested) { try { var client = new AmazonSecretsManagerClient(keyId, accessKey, RegionEndpoint.GetBySystemName(region)); var request = new GetSecretValueRequest() { SecretId = secretName, VersionStage = versionStage, // VersionStage defaults to AWSCURRENT if unspecified. }; var response = await client.GetSecretValueAsync(request, _cancellationToken); var map = response.SecretString.ToDeserialize<Dictionary<string, Object>>(); foreach (var keyValuePair in map) { Data.AddOrUpdate( keyValuePair.Key, key => keyValuePair.Value.ToString(), (key, oldValue) => keyValuePair.Value.ToString()); } } catch (OperationCanceledException e) { _logger.LogError(e, e.Message); throw; } catch (Exception e) { _logger.LogError(e, e.Message); throw; } await Task.Delay(5000, _cancellationToken); } } }
위 코드는 비동기적으로 취소 전까지 계속 Loop를 돌면서 client 호출 코드를 실행하고 있다.
중요한 점은 ConcurrentDictionary를 사용하여 처리하고 있다는 점이다.
멀티스레딩에서 사용할 경우 위와 같이 사용한다.
이제 위 코드를 사용하는 부분을 보자.
public interface IEnvironmentLoader { ConcurrentDictionary<string, string> Data { get; } Task Start(Dictionary<string, string> parameters); } public class EnvironmentResolver { private readonly IEnvironmentLoader _loader; private Task _loadTask; public EnvironmentResolver([FromKeyedServices("AWS")]IEnvironmentLoader loader) { _loader = loader; } public void Start(Dictionary<string, string> parameters) { _loadTask = _loader.Start(parameters); } }
위 코드는 Resolver 구현 및 Loader의 인터페이스 코드이다.
위 코드로 무엇을 하려하는지 외부에 노출될 함수나 속성이 어떤 것인지 알 수 있겠다.
builder.Services.AddKeyedSingleton<IEnvironmentLoader, SecretManagerV2Loader>("AWS"); builder.Services.AddKeyedSingleton<IEnvironmentLoader, KeyVaultV2Loader>("AZURE"); builder.Services.AddSingleton<EnvironmentResolver>();
DI Container에는 위와 같이 등록한다.
Singleton인 점에 주의하자.
Env 변수가 전역적이라는 점에서 Singleton으로 설정하는 것이 적절하다.
using (var scope = app.Services.CreateScope()) { var resolver = scope.ServiceProvider.GetRequiredService<EnvironmentResolver>(); resolver.Start(new Dictionary<string, string>() { {"KEY_ID", Environment.GetEnvironmentVariable("AWS_KEYID")}, {"ACCESS_KEY", Environment.GetEnvironmentVariable("AWS_ACCESSKEY")}, {"REGION", Environment.GetEnvironmentVariable("AWS_REGION")}, {"VERSION_STAGE", "AWSCURRENT"}, }); }
Program.cs 에서 위와 app.Run() 전에 위와 같이 호출하여 시작한다.
이제 위 코드는 프로그램 시작시 매 5초간 설정 관련 변수를 읽고 저장하게 된다.
여기서 유의할 점은 Init과 Start를 구분해야 한다는 것이다.
즉, 프로그램이 시작할 때 환경 변수가 명시적으로 필요하다면 추가적으로 Init 함수가 별도로 존재해야 한다.
이는 설정 변수 설정이 프로그램 시작 시에 반드시 필요하다면 Task가 아닌 async-await로 작성된 시작점이 필요하다는 이야기이다.
따라서, 이 부분을 고려해서 수정해 보는 것도 좋겠다.
실제 사용은 아래와 같다.
public DemoController(ILogger<DemoController> logger, SecretManagerV2Loader secretManagerV2Loader) { _logger = logger; _secretManagerV2Loader = secretManagerV2Loader; } public IActionResult GetLoader() { return Ok(_secretManagerV2Loader.Data["키값"]); }
이번에는 환경 변수 또는 설정 변수를 AWS Secret Manager를 이용해 외부화하고 동적으로 읽어 들이는 방법에 대해 알아보았다.
도움이 되기를 바라며...
'프로그래밍' 카테고리의 다른 글
c# 에서 mongodb를 다루는 방법 (2) (0) 2024.11.20 c# 에서 mongodb를 다루는 방법 (0) 2024.11.19 Kafka Consumer를 조금 더 효율적으로 처리하는 방법 (0) 2024.11.13 시계열 데이터를 전송하는 방법 (0) 2024.10.25 사용자 Session을 처리하는 방법 (1) 2024.10.24