AWS OpenSearch Service Migration cross account using snapshot (include terraform code)


AWS OpenSearch 계정간 마이그레이션 (snapshot 이용)

개요

AWS OpenSearch Service를 다른 AWS Account로 이동할 경우가 생긴다. 가장 편리한 방법은 remote redindex를 하는 것이다. 하지만, Target AWS Account의 OpenSearch에서 Source OpenSearch에서 접근을 할 수 없는 경우 복잡해진다. VPC Endpoint Service 를 생성하여 접근을 허용하는 방법을 이용하면 가능하다고 하는데, 필자의 경우 그 부분이 잘 되지 않았다. 그래서 S3에 snaphost을 생성하고, S3에 Target AWS Account를 허용한 다음 그걸 이용해서 Target OpenSearch에서 restore 하는 방법으로 진행하였다. 이 방법도 공홈에 비교적 자세히 소개되어 있으나 몇몇 지점에서 오류가 발생하였는데, 거기에 대한 안내가 없어서 힘들었다. OpenSearch가 버전업되면서 문법 등 바뀐점이 있는데 그것들이 반영되지 않은 것 같다.

이 post는 2023년 11월 기준으로 정상적으로 수행된 내용으로 작성한다.

먼저 전체 작업의 개요부터 보자.

순서

  1. Source AWS Account에 Snapshot을 저장할 S3 생성
  2. Source AWS Account에 Snapshot 생성에 사용할 IAM Role, Policy 생성
  3. Source OpenSearch에 Snapshot Repository 등록
  4. Snapshot 생성
  5. S3를 Target AWS Account와 공유
  6. Target AWS Account에 Snapshot을 restore할 IAM Role, Policy 생성
  7. Target OpenSearch에 Snapshot Repository 등록
  8. Target OpenSearch에 Snapshot Restore

동일한 OpenSearch에서의 실행을 할 것이라면 5, 6, 7 과정을 생략하면 되고, 동일한 Account에서의 다른 OpenSearch에서 실행할 것이라면 5, 6 과정을 생략하면 된다. 이제 각각의 과정을 하나하나 소개하겠다.

각 resource의 명칭은 아래와 같이 정의하겠다.

  • OpenSearch domain name: OpenSearch
  • Snapshot이 저장될 S3 Bucket : OpenSearchSnapshot (Bucket 이름은 모든 Account를 통틀어서 unique해야한다.)
  • Snapshot을 수행할 IAM Role: TheSnapshotRole
  • Snapshot에 필요한 권한을 정의한 Policy: S3OpenSearchSnapshot
  • Source AWS Account: SOURCE_ACCOUNT (원래는 숫자 12자리)
  • Target AWS Account: TARGET_ACCOUNT (원래는 숫자 12자리)
  • AWS Region: ap-northeast-2 (서울)

1. Source AWS Account에 Snapshot을 저장할 S3 생성

S3 Bucket을 생성한다.

  • Terraform
resource aws_s3_bucket OpenSearchSnapshot {
  bucket = "OpenSearchSnapshot"
}

2. Source AWS Account에 Snapshot 생성에 사용할 IAM Role, Policy 생성

2-1. 먼저 TheSnapshotRole이란 이름의 IAM Role을 생성한다. 콘솔에서 진행할 경우 Trust Relationships를 수동으로 만들어줘야 한다.

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "Statement1",
            "Effect": "Allow",
            "Principal": {
                "Service": "opensearchservice.amazonaws.com"
            },
            "Action": "sts:AssumeRole"
        }
    ]
}
  • Terraform
resource "aws_iam_role" TheSnapshotRole {
  name = "TheSnapshotRole"
  assume_role_policy = jsonencode({
    Version = "2012-10-17",
    Statement = [
      {
        Effect = "Allow",
        Action = "sts:AssumeRole",
        Principal: {
          Service: "opensearchservice.amazonaws.com"
        },
      }
    ]
  })
}

2-2. 이번 작업에 필요한 권한을 가진 Policy를 생성한다.

2개의 policy로 나눠서 생성해도 되고 하나에 모두 정의해도 된다. 필자는 편의상 1개의 policy로 정의하겠다. 이 policy는 다음 3가지 권한을 가져야 한다.

  • S3에 접근
  • OpenSearch에 접근
  • OpenSearch에서 Snapshot 생성시 S3에 IAM Role을 전달

콘솔에서 생성할 경우 아래 JSON 형식의 inline policy를 생성한 뒤, TheSnapshotRole에 attach한다.

{
    "Version": "2012-10-17",
    "Statement": [
      {
        "Action": "s3:ListBucket",
        "Effect"  : "Allow",
        "Resource": "arn:aws:s3:::OpenSearchSnapshot"
      },
      {
        "Action": [
          "s3:GetObject",
          "s3:PutObject",
          "s3:DeleteObject",
          "iam:PassRole"
        ],
        "Effect"  : "Allow",
        "Resource": "arn:aws:s3:::OpenSearchSnapshot/*"
      },
      {
        "Effect":  "Allow",
        "Action": "iam:PassRole",
        "Resource":  "arn:aws:iam::SOURCE_ACCOUNT:role/TheSnapshotRole"
      },
      {
        "Effect": "Allow",
        "Action": "es:*",
        "Resource": "arn:aws:es:ap-northeast-2:SOURCE_ACCOUNT:domain/OpenSearch/*"
      }
    ]
  }
  • Terraform
resource "aws_iam_policy" "S3OpenSearchSnapshot" {
  name        = "S3-OpenSearchSnapshot"
  description = "Policy to access specific S3 bucket"

  policy = jsonencode({
    Version = "2012-10-17",
    Statement = [
      {
        Action = "s3:ListBucket",
        Effect   = "Allow",
        Resource = "arn:aws:s3:::OpenSearchSnapshot"
      },
      {
        Action = [
          "s3:GetObject",
          "s3:PutObject",
          "s3:DeleteObject",
          "iam:PassRole"
        ],
        Effect   = "Allow",
        Resource = "arn:aws:s3:::OpenSearchSnapshot/*"
      },
      {
        Effect =  "Allow",
        Action = "iam:PassRole",
        Resource =  "arn:aws:iam::SOURCE_ACCOUNT:role/TheSnapshotRole"
      },
      {
        Effect = "Allow",
        Action = "es:*",
        Resource = "arn:aws:es:ap-northeast-2:SOURCE_ACCOUNT:domain/OpenSearch/*"
      }
    ]
  })
}

resource "aws_iam_role_policy_attachment" "TheSnapshotRole_S3OpenSearchSnapshot" {
  role       = aws_iam_role.TheSnapshotRole.name
  policy_arn = aws_iam_policy.S3OpenSearchSnapshot.arn
}

3. Source OpenSearch에 Snapshot Repository 등록

OpenSearch에 snapshot repository를 생성해야 한다. 필자의 경우 이 단계에서 시행착오를 많이 거쳤다. 그 이유는 snapshot 관련 권한 설정을 OpenSearch dashboard에서 해줘야 한다.

  • Master User Type을 IAM ARN으로 한 경우: 해당 권한을 가진 user의 ACCESS KEY를 가지고 진행하면 된다.
  • Master User Type을 Internal User Database로 한 경우: dashboard 상 Security -> Roles 로 들어가서 all_access 혹은 미리 정의된 snapshot 관련 role 선택 또는 생성을 하여서 master user를 binding 한 후 Backend roles에 이번 작업에 사용할 ACCESS KEY의 user로 설정한다.

참고로 OpenSearch 상 권한으로는 아래 권한들을 가져야 한다.

  • cluster:admin/opensearch/snapshot_management/*
  • cluster:admin/opensearch/notifications/feature/publish
  • cluster:admin/repository/*

Repository 생성은 dashboard상 Dev Tools에서 수행이 안된다. curl을 사용하던지 python 코드로 작업을 해야한다. 필자는 python 코드로 작업하였다.

import boto3
import requests
from requests_aws4auth import AWS4Auth

host = 'https://OpenSearchDomain.ap-northeast-2.es.amazonaws.com' # 콘솔에서 확인
aws_account = 'SOURCE_ACCOUNT'
region = 'ap-northeast-2'
service = 'es'
credentials = boto3.Session().get_credentials() # ~/.aws/credentials의  default profile

awsauth = AWS4Auth(credentials.access_key, credentials.secret_key, region, service)
headers = {"Content-Type": "application/json"}

# Register repository

path = '/_snapshot/OpenSearchSnapshot'

url = host + path

payload = {
  "type": "s3",
  "settings": {
    "bucket": "OpenSearchSnapshot",
    "region": region,
    "role_arn": f"arn:aws:iam::{aws_account}:role/TheSnapshotRole"
  }
}
r = requests.put(url, auth=awsauth, json=payload, headers=headers)

print(r.status_code)
print(r.text)

정상적으로 수행될 경우 {acknowledge: true} 같은 response가 올 것이고 아닐 경우 아래와 같은 오류가 발생한다.

{"Message":"User: XXXX or anonymous is not authorized to perform: iam:PassRole on resource: arn:aws:iam::SOURCE_ACCOUNT:role/TheSnapshotRole"}

그러면 이걸 수행한 ACCESS KEY의 IAM User가 OpenSearch 내의 적절한 Role에 잘 binding 되었는지 다시 살펴봐야 한다.

4. Snapshot 생성

OpenSearch dashboard로 들어가서 아래 요청을 보내면 된다. curl로도 수행이 가능하다.
snapshot-2023-11-17은 이번에 저장하는 snapshot의 이름이다.

PUT _snapshot/OpenSearchSnapshot/snapshot-2023-11-17

Snapshot 작업이 수행되는 동안은 아래 명령어의 답변에 관련 작업이 보일 것이다.

GET _snapshot/_status

해당 Snapshot의 상세정보에서도 진행 사항을 볼 수 있다.

GET _snapshot/OpenSearchSnapshot/snapshot-2023-11-17

Snapshot 관련 명령어 몇개만 더 소개하겠다.

  • GET _snapshot?pretty : Snapshot Repository들
  • GET _snapshot/OpenSearchSnapshot/_all?pretty : OpenSearchSnapshot Repository 내의 모든 snapshot들
  • GET _cat/snapshots/OpenSearchSnapshot : OpenSearchSnapshot Repository 내의 snapshot 작업들

5. S3를 Target AWS Account와 공유

이제 S3를 다른 AWS Account에서 접근할 수 있도록 Policy를 등록하겠다.

S3 콘솔로 들어가서 Permissions -> Bucket Policy에 아래 내용으로 작성한다.

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::TARGET_ACCOUNT:root"
            },
            "Action": "s3:*",
            "Resource": [
                "arn:aws:s3:::OpenSearchSnapshot/*",
                "arn:aws:s3:::OpenSearchSnapshot"
            ]
        }
    ]
}

제대로 설정되었는지 확인하려면 TARGET_ACCOUNT 로 credential을 설정한 다음 아래 명령어가 정상적으로 수행되면 된다.

aws s3 ls s3://OpenSearchSnapshot

6. Target AWS Account에 Snapshot을 restore할 IAM Role, Policy 생성

2번 단계에서 수행한것과 거의 동일하게 TARGET_ACCOUNT에서 하면 된다.

  • IAM Role은 TARGET_ACCOUNT에서 새로 생성
  • OpenSearch는 TARGET_ACCOUNT에 restore할 OpenSearch로 설정
  • S3는 SOURCE_ACCOUNT에서 생성한 S3

7. Target OpenSearch에 Snapshot Repository 등록

3번 단계에서 수행한것과 거의 동일하게 TARGET_ACCOUNT에서 하면 된다.

8. Target OpenSearch에 Snapshot Restore

OpenSearch dashboard로 들어가서 아래 요청을 보내면 된다. curl로도 수행이 가능하다.
Source OpenSearch에서 생성한 snapshot 이름은 snapshot-2023-11-17이었다.

POST _snapshot/OpenSearchSnapshot/snapshot-2023-11-17/_restore
{
  "indices": ["-.kibana*","-.opendistro_security","-.opendistro-*", "가져올 INDEX 이름*"],
  "include_global_state": false
}

필자는 여기에서도 시행착오를 많이 겪었는데, 이 당시 공식문서에는 indices가 list(string)이 아니라 "-.kibana*,-.opendistro_security,-.opendistro-*" 이런 식으로 하나의 string안에 comma(,)로 구분된 형식이었다. 이럴때의 오류 메세지가 아래와 같이 나와서 더욱 해맸었다.

{
  "type": "security_exception",
  "reason": "no permissions for [] and User [name=XXX, backend_roles=[arn:aws:iam::TARGET_ACCOUNT:user/XXX], requestedTenant=]"
}

References


이 글이 도움이 되셨다면 공감 및 광고 클릭을 부탁드립니다 :)