Skip to content

Commit 1257f88

Browse files
add tutorial for cross-account model invocation on amazon managed cluster (opensearch-project#3064) (opensearch-project#3070)
Signed-off-by: Yaliang Wu <ylwu@amazon.com> (cherry picked from commit 74c211e) Co-authored-by: Yaliang Wu <ylwu@amazon.com>
1 parent 73c8816 commit 1257f88

File tree

1 file changed

+395
-0
lines changed

1 file changed

+395
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,395 @@
1+
# Topic
2+
3+
> This tutorial explains detail steps if you want to configure connector to use Bedrock model in another AWS account. This cross-account model invocation feature starts from 2.15.
4+
5+
> Bedrock has [quota limit](https://docs.aws.amazon.com/bedrock/latest/userguide/quotas.html). You can purchase [Provisioned Throughput](https://docs.aws.amazon.com/bedrock/latest/userguide/prov-throughput.html) to increase quota limit.
6+
7+
This doc introduces how to build semantic search in Amazon managed OpenSearch with [Bedrock Titan embedding model](https://docs.aws.amazon.com/bedrock/latest/userguide/titan-embedding-models.html) in another AWS account.
8+
If you are not using Amazon OpenSearch, you can refer to [bedrock_connector_titan_embedding_blueprint](https://github.com/opensearch-project/ml-commons/blob/2.x/docs/remote_inference_blueprints/bedrock_connector_titan_embedding_blueprint.md).
9+
10+
Note: You should replace the placeholders with prefix `your_` with your own value
11+
12+
In this tutorial, we will use two AWS accounts. One for running OpenSearch cluster, we call it account A; the other for invoking Bedrock model, we call it account B.
13+
14+
# Steps
15+
16+
The support cross account model invokation, we need to configure two roles in connector credential part:
17+
- `roleArn`: role in account A which will be used to assume external account role in account B
18+
- `externalAccountRoleArn`: role in account B which will be used to invoke Bedrock model.
19+
20+
In this tutorial , we will use such role name
21+
- Account A: `my_cross_account_role`, `roleArn` will be `arn:aws:iam::<your_aws_account_A>:role/my_cross_account_role_accountA`
22+
- Account B: `my_invoke_bedrock_role`, `externalAccountRoleArn` will be `arn:aws:iam::<your_aws_account_B>:role/my_invoke_bedrock_role_accountB`
23+
24+
## 0. Create OpenSearch cluster in Account A
25+
26+
Go to AWS OpenSearch console UI and create OpenSearch domain.
27+
28+
Note the domain ARN which will be used in later steps.
29+
30+
31+
## 1. Create IAM role in Account B
32+
To invoke Bedrock model, we need to create an IAM role with proper permission.
33+
This IAM role will be configured in connector. Connector will use this role to invoke Bedrock model.
34+
35+
Go to IAM console, create IAM role `my_invoke_bedrock_role_accountB` with:
36+
37+
- Custom trust policy:
38+
```
39+
{
40+
"Version": "2012-10-17",
41+
"Statement": [
42+
{
43+
"Sid": "",
44+
"Effect": "Allow",
45+
"Principal": {
46+
"AWS": "arn:aws:iam::<your_aws_account_A>:role/my_cross_account_role_accountA"
47+
},
48+
"Action": "sts:AssumeRole"
49+
}
50+
]
51+
}
52+
```
53+
- Permission
54+
```
55+
{
56+
"Version": "2012-10-17",
57+
"Statement": [
58+
{
59+
"Action": [
60+
"bedrock:InvokeModel"
61+
],
62+
"Effect": "Allow",
63+
"Resource": "arn:aws:bedrock:*::foundation-model/amazon.titan-embed-text-v1"
64+
}
65+
]
66+
}
67+
```
68+
69+
## 2. Create IAM role in account A
70+
71+
### 2.1 Create IAM role for assuming externalAccountRoleArn
72+
Create an IAM role which can assume `externalAccountRoleArn` in account B.
73+
74+
Go to IAM console, create IAM role `my_cross_account_role_accountA` with:
75+
76+
- Custom trust policy:
77+
```
78+
{
79+
"Version": "2012-10-17",
80+
"Statement": [
81+
{
82+
"Effect": "Allow",
83+
"Principal": {
84+
"Service": "es.amazonaws.com"
85+
},
86+
"Action": "sts:AssumeRole"
87+
}
88+
]
89+
}
90+
```
91+
- Permission
92+
```
93+
{
94+
"Version": "2012-10-17",
95+
"Statement": [
96+
{
97+
"Effect": "Allow",
98+
"Action": "sts:AssumeRole",
99+
"Resource": "arn:aws:iam::<your_aws_account_B>:role/my_invoke_bedrock_role_accountB"
100+
}
101+
]
102+
}
103+
```
104+
105+
### 2.2 Create IAM role for Signing create connector request
106+
107+
Generate a new IAM role specifically for signing your create connector request.
108+
109+
110+
Create IAM role `my_create_connector_role_accountA` with
111+
- Custom trust policy. Note: `your_iam_user_arn` is the IAM user which will run `aws sts assume-role` in step 3.1
112+
```
113+
{
114+
"Version": "2012-10-17",
115+
"Statement": [
116+
{
117+
"Effect": "Allow",
118+
"Principal": {
119+
"AWS": "your_iam_user_arn"
120+
},
121+
"Action": "sts:AssumeRole"
122+
}
123+
]
124+
}
125+
```
126+
- permission
127+
```
128+
{
129+
"Version": "2012-10-17",
130+
"Statement": [
131+
{
132+
"Effect": "Allow",
133+
"Action": "iam:PassRole",
134+
"Resource": "arn:aws:iam::<your_aws_account_A>:role/my_cross_account_role_accountA"
135+
},
136+
{
137+
"Effect": "Allow",
138+
"Action": "es:ESHttpPost",
139+
"Resource": "your_opensearch_domain_arn_created_in_step0"
140+
}
141+
]
142+
}
143+
```
144+
145+
Copy this role ARN which will be used in later steps.
146+
147+
### 2.3 Map backend role
148+
149+
1. Log in to your OpenSearch Dashboard and navigate to the "Security" page, which you can find in the left-hand menu.
150+
2. Then click "Roles" on security page (you can find it on left-hand), then find "ml_full_access" role and click it.
151+
3. On "ml_full_access" role detail page, click "Mapped users", then click "Manage mapping". Paste IAM role ARN created in step 2.2 to backend roles part: `arn:aws:iam::<your_aws_account_A>:role/my_create_connector_role_accountA`.
152+
Click "Map", then the IAM role configured successfully in your OpenSearch cluster.
153+
154+
![Alt text](images/semantic_search/mapping_iam_role_arn.png)
155+
156+
## 3. Create Connector
157+
158+
Find more details on [connector](https://opensearch.org/docs/latest/ml-commons-plugin/remote-models/connectors/)
159+
160+
161+
### 3.1 Get temporary credential of the role created in step 2.2:
162+
Use your IAM user credential to assume role created in step 2.2
163+
```
164+
aws sts assume-role --role-arn arn:aws:iam::<your_aws_account_A>:role/my_create_connector_role_accountA --role-session-name your_session_name
165+
```
166+
167+
Configure the temporary credential in `~/.aws/credentials` like this
168+
169+
```
170+
[default]
171+
AWS_ACCESS_KEY_ID=your_access_key_of_role_created_in_step2.2
172+
AWS_SECRET_ACCESS_KEY=your_secret_key_of_role_created_in_step2.2
173+
AWS_SESSION_TOKEN=your_session_token_of_role_created_in_step2.2
174+
```
175+
176+
### 3.2 Create connector
177+
178+
Run this python code with the temporary credential configured in `~/.aws/credentials`
179+
180+
```
181+
import boto3
182+
import requests
183+
from requests_aws4auth import AWS4Auth
184+
185+
host = 'your_amazon_opensearch_domain_endpoint_created_in_step0'
186+
region = 'your_amazon_opensearch_domain_region'
187+
service = 'es'
188+
189+
credentials = boto3.Session().get_credentials()
190+
awsauth = AWS4Auth(credentials.access_key, credentials.secret_key, region, service, session_token=credentials.token)
191+
192+
path = '/_plugins/_ml/connectors/_create'
193+
url = host + path
194+
195+
bedrock_model_region='your_bedrock_model_region'
196+
payload = {
197+
"name": "Amazon Bedrock Connector: titan embedding v1",
198+
"description": "The connector to bedrock Titan embedding model",
199+
"version": 1,
200+
"protocol": "aws_sigv4",
201+
"parameters": {
202+
"region": bedrock_model_region,
203+
"service_name": "bedrock"
204+
},
205+
"credential": {
206+
"roleArn": "arn:aws:iam::<your_aws_account_A>:role/my_cross_account_role_accountA"
207+
"externalAccountRoleArn": "arn:aws:iam::<your_aws_account_B>:role/my_invoke_bedrock_role_accountB"
208+
},
209+
"actions": [
210+
{
211+
"action_type": "predict",
212+
"method": "POST",
213+
"url": f"https://bedrock-runtime.{bedrock_model_region}.amazonaws.com/model/amazon.titan-embed-text-v1/invoke",
214+
"headers": {
215+
"content-type": "application/json",
216+
"x-amz-content-sha256": "required"
217+
},
218+
"request_body": "{ \"inputText\": \"${parameters.inputText}\" }",
219+
"pre_process_function": "connector.pre_process.bedrock.embedding",
220+
"post_process_function": "connector.post_process.bedrock.embedding"
221+
}
222+
]
223+
}
224+
225+
headers = {"Content-Type": "application/json"}
226+
227+
r = requests.post(url, auth=awsauth, json=payload, headers=headers)
228+
print(r.text)
229+
```
230+
The script will output connector id.
231+
232+
sample output
233+
```
234+
{"connector_id":"N0qpQY0BOhavBOmfOCnw"}
235+
```
236+
237+
Copy connector id which will be used in later steps.
238+
239+
## 4. Create Model and test
240+
241+
Login your OpenSearch Dashboard, open DevTools, then run these
242+
243+
1. Create model group
244+
```
245+
POST /_plugins/_ml/model_groups/_register
246+
{
247+
"name": "Bedrock_embedding_model",
248+
"description": "Test model group for bedrock embedding model"
249+
}
250+
```
251+
Sample output
252+
```
253+
{
254+
"model_group_id": "LxWiQY0BTaDH9c7t9xeE",
255+
"status": "CREATED"
256+
}
257+
```
258+
259+
2. Register model
260+
261+
```
262+
POST /_plugins/_ml/models/_register
263+
{
264+
"name": "bedrock titan embedding model v1",
265+
"function_name": "remote",
266+
"description": "test embedding model",
267+
"model_group_id": "LxWiQY0BTaDH9c7t9xeE",
268+
"connector_id": "N0qpQY0BOhavBOmfOCnw"
269+
}
270+
```
271+
Sample output
272+
```
273+
{
274+
"task_id": "O0q3QY0BOhavBOmf1SmL",
275+
"status": "CREATED",
276+
"model_id": "PEq3QY0BOhavBOmf1Sml"
277+
}
278+
```
279+
280+
3. Deploy model
281+
```
282+
POST /_plugins/_ml/models/PEq3QY0BOhavBOmf1Sml/_deploy
283+
```
284+
Sample output
285+
```
286+
{
287+
"task_id": "PUq4QY0BOhavBOmfBCkQ",
288+
"task_type": "DEPLOY_MODEL",
289+
"status": "COMPLETED"
290+
}
291+
```
292+
4. Predict
293+
```
294+
POST /_plugins/_ml/models/PEq3QY0BOhavBOmf1Sml/_predict
295+
{
296+
"parameters": {
297+
"inputText": "hello world"
298+
}
299+
}
300+
```
301+
Sample response
302+
```
303+
{
304+
"inference_results": [
305+
{
306+
"output": [
307+
{
308+
"name": "sentence_embedding",
309+
"data_type": "FLOAT32",
310+
"shape": [
311+
1536
312+
],
313+
"data": [
314+
0.7265625,
315+
-0.0703125,
316+
0.34765625,
317+
...]
318+
}
319+
],
320+
"status_code": 200
321+
}
322+
]
323+
}
324+
```
325+
326+
## 5. Semantic search
327+
328+
### 5.1 create ingest pipeline
329+
Find more details: [ingest pipeline](https://opensearch.org/docs/latest/ingest-pipelines/)
330+
331+
```
332+
PUT /_ingest/pipeline/my_bedrock_embedding_pipeline
333+
{
334+
"description": "text embedding pentest",
335+
"processors": [
336+
{
337+
"text_embedding": {
338+
"model_id": "your_bedrock_embedding_model_id_created_in_step4",
339+
"field_map": {
340+
"text": "text_knn"
341+
}
342+
}
343+
}
344+
]
345+
}
346+
```
347+
### 5.2 create k-NN index
348+
Find more details: [k-NN index](https://opensearch.org/docs/latest/search-plugins/knn/knn-index/)
349+
350+
You should customize your k-NN index for better performance.
351+
```
352+
PUT my_index
353+
{
354+
"settings": {
355+
"index": {
356+
"knn.space_type": "cosinesimil",
357+
"default_pipeline": "my_bedrock_embedding_pipeline",
358+
"knn": "true"
359+
}
360+
},
361+
"mappings": {
362+
"properties": {
363+
"text_knn": {
364+
"type": "knn_vector",
365+
"dimension": 1536
366+
}
367+
}
368+
}
369+
}
370+
```
371+
### 5.3 ingest test data
372+
```
373+
POST /my_index/_doc/1000001
374+
{
375+
"text": "hello world."
376+
}
377+
```
378+
### 5.4 search
379+
Find more details: [neural search](https://opensearch.org/docs/latest/search-plugins/neural-search/).
380+
```
381+
POST /my_index/_search
382+
{
383+
"query": {
384+
"neural": {
385+
"text_knn": {
386+
"query_text": "hello",
387+
"model_id": "your_embedding_model_id_created_in_step4",
388+
"k": 100
389+
}
390+
}
391+
},
392+
"size": "1",
393+
"_source": ["text"]
394+
}
395+
```

0 commit comments

Comments
 (0)