|
1 | 1 | # AsyncAPI |
2 | 2 |
|
3 | | -Create APIs that process your workloads asynchronously. |
| 3 | +### Define an API |
4 | 4 |
|
5 | | -## Implementation |
6 | | - |
7 | | -Create a folder for your API. In this case, we are deploying an iris-classifier AsyncAPI. This folder will have the |
8 | | -following structure: |
| 5 | +```python |
| 6 | +# main.py |
9 | 7 |
|
10 | | -```text |
11 | | -./iris-classifier |
12 | | -├── cortex.yaml |
13 | | -├── handler.py |
14 | | -└── requirements.txt |
15 | | -``` |
| 8 | +from fastapi import FastAPI |
16 | 9 |
|
17 | | -We will now create the necessary files: |
| 10 | +app = FastAPI() |
18 | 11 |
|
19 | | -```bash |
20 | | -mkdir iris-classifier && cd iris-classifier |
21 | | -touch handler.py requirements.txt cortex.yaml |
| 12 | +@app.post("/") |
| 13 | +def hello_world(): |
| 14 | + return {"msg": "hello world"} |
22 | 15 | ``` |
23 | 16 |
|
24 | | -```python |
25 | | -# handler.py |
26 | | - |
27 | | -import os |
28 | | -import pickle |
29 | | -from typing import Dict, Any |
30 | | - |
31 | | -import boto3 |
32 | | -from botocore import UNSIGNED |
33 | | -from botocore.client import Config |
34 | | - |
35 | | -labels = ["setosa", "versicolor", "virginica"] |
| 17 | +### Create a `Dockerfile` |
36 | 18 |
|
| 19 | +```Dockerfile |
| 20 | +FROM python:3.8-slim |
37 | 21 |
|
38 | | -class Handler: |
39 | | - def __init__(self, config): |
40 | | - s3 = boto3.client("s3") |
41 | | - s3.download_file(config["bucket"], config["key"], "/tmp/model.pkl") |
42 | | - self.model = pickle.load(open("/tmp/model.pkl", "rb")) |
| 22 | +RUN pip install --no-cache-dir fastapi uvicorn |
| 23 | +COPY main.py / |
43 | 24 |
|
44 | | - def handle_async(self, payload: Dict[str, Any]) -> Dict[str, str]: |
45 | | - measurements = [ |
46 | | - payload["sepal_length"], |
47 | | - payload["sepal_width"], |
48 | | - payload["petal_length"], |
49 | | - payload["petal_width"], |
50 | | - ] |
| 25 | +CMD uvicorn --host 0.0.0.0 --port 8080 main:app |
| 26 | +``` |
51 | 27 |
|
52 | | - label_id = self.model.predict([measurements])[0] |
| 28 | +### Build an image |
53 | 29 |
|
54 | | - # result must be json serializable |
55 | | - return {"label": labels[label_id]} |
| 30 | +```bash |
| 31 | +docker build . --tag hello-world |
56 | 32 | ``` |
57 | 33 |
|
58 | | -```python |
59 | | -# requirements.txt |
| 34 | +### Run a container locally |
60 | 35 |
|
61 | | -boto3 |
| 36 | +```bash |
| 37 | +docker run --port 8080:8080 hello-world |
62 | 38 | ``` |
63 | 39 |
|
64 | | -```yaml |
65 | | -# text_generator.yaml |
| 40 | +### Make a request |
66 | 41 |
|
67 | | -- name: iris-classifier |
68 | | - kind: AsyncAPI |
69 | | - handler: |
70 | | - type: python |
71 | | - path: handler.py |
| 42 | +```bash |
| 43 | +curl --request POST --header "Content-Type: application/json" localhost:8080 |
72 | 44 | ``` |
73 | 45 |
|
74 | | -## Deploy |
75 | | -
|
76 | | -We can now deploy our API with the `cortex deploy` command. This command can be re-run to update your API configuration |
77 | | -or handler implementation. |
| 46 | +### Login to ECR |
78 | 47 |
|
79 | 48 | ```bash |
80 | | -cortex deploy cortex.yaml |
81 | | -
|
82 | | -# creating iris-classifier (AsyncAPI) |
83 | | -# |
84 | | -# cortex get (show api statuses) |
85 | | -# cortex get iris-classifier (show api info) |
| 49 | +aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin <AWS_ACCOUNT_ID>.dkr.ecr.us-east-1.amazonaws.com |
86 | 50 | ``` |
87 | 51 |
|
88 | | -## Monitor |
89 | | - |
90 | | -To check whether the deployed API is ready, we can run the `cortex get` command with the `--watch` flag. |
| 52 | +### Create a repository |
91 | 53 |
|
92 | 54 | ```bash |
93 | | -cortex get iris-classifier --watch |
94 | | -
|
95 | | -# status up-to-date requested last update |
96 | | -# live 1 1 10s |
97 | | -# |
98 | | -# endpoint: http://<load_balancer_url>/iris-classifier |
99 | | -# |
100 | | -# api id last deployed |
101 | | -# 6992e7e8f84469c5-d5w1gbvrm5-25a7c15c950439c0bb32eebb7dc84125 10s |
| 55 | +aws ecr create-repository --repository-name hello-world |
102 | 56 | ``` |
103 | 57 |
|
104 | | -## Submit a workload |
| 58 | +### Tag the image |
105 | 59 |
|
106 | | -Now we want to submit a workload to our deployed API. We will start by creating a file with a JSON request payload, in |
107 | | -the format expected by our `iris-classifier` handler implementation. |
| 60 | +```bash |
| 61 | +docker tag hello-world <AWS_ACCOUNT_ID>.dkr.ecr.us-east-1.amazonaws.com/hello-world |
| 62 | +``` |
108 | 63 |
|
109 | | -This is the JSON file we will submit to our iris-classifier API. |
| 64 | +### Push the image |
110 | 65 |
|
111 | 66 | ```bash |
112 | | -# sample.json |
113 | | -{ |
114 | | - "sepal_length": 5.2, |
115 | | - "sepal_width": 3.6, |
116 | | - "petal_length": 1.5, |
117 | | - "petal_width": 0.3 |
118 | | -} |
| 67 | +docker push <AWS_ACCOUNT_ID>.dkr.ecr.us-east-1.amazonaws.com/hello-world |
119 | 68 | ``` |
120 | 69 |
|
121 | | -Once we have our sample request payload, we will submit it with a `POST` request to the endpoint URL previously |
122 | | -displayed in the `cortex get` command. We will quickly get a request `id` back. |
| 70 | +### Configure a Cortex deployment |
123 | 71 |
|
124 | | -```bash |
125 | | -curl -X POST http://<load_balancer_url>/iris-classifier -H "Content-Type: application/json" -d '@./sample.json' |
| 72 | +```yaml |
| 73 | +# cortex.yaml |
126 | 74 |
|
127 | | -# {"id": "659938d2-2ef6-41f4-8983-4e0b7562a986"} |
| 75 | +- name: hello-world |
| 76 | + kind: AsyncAPI |
| 77 | + pod: |
| 78 | + containers: |
| 79 | + - name: api |
| 80 | + image: <AWS_ACCOUNT_ID>.dkr.ecr.us-east-1.amazonaws.com/hello-world |
128 | 81 | ``` |
129 | 82 |
|
130 | | -## Retrieve the result |
131 | | - |
132 | | -The obtained request id will allow us to check the status of the running payload and retrieve its result. To do so, we |
133 | | -submit a `GET` request to the same endpoint URL with an appended `/<id>`. |
| 83 | +### Create a Cortex deployment |
134 | 84 |
|
135 | 85 | ```bash |
136 | | -curl http://<load_balancer_url>/iris-classifier/<id> # <id> is the request id that was returned in the previous POST request |
137 | | -
|
138 | | -# { |
139 | | -# "id": "659938d2-2ef6-41f4-8983-4e0b7562a986", |
140 | | -# "status": "completed", |
141 | | -# "result": {"label": "setosa"}, |
142 | | -# "timestamp": "2021-03-16T15:50:50+00:00" |
143 | | -# } |
| 86 | +cortex deploy |
144 | 87 | ``` |
145 | 88 |
|
146 | | -Depending on the status of your workload, you will get different responses back. The possible workload status |
147 | | -are `in_queue | in_progress | failed | completed`. The `result` and `timestamp` keys are returned if the status |
148 | | -is `completed`. The result will remain queryable for 7 days after the request was completed. |
149 | | - |
150 | | -It is also possible to setup a webhook in your handler to get the response sent to a pre-defined web server once the |
151 | | -workload completes or fails. |
| 89 | +### Wait for the API to be ready |
152 | 90 |
|
153 | | -## Debugging logs |
| 91 | +```bash |
| 92 | +cortex get --watch |
| 93 | +``` |
154 | 94 |
|
155 | | -If necessary, you can view logs for your API in CloudWatch using the `cortex logs` command. |
| 95 | +### Get the API endpoint |
156 | 96 |
|
157 | 97 | ```bash |
158 | | -cortex logs iris-classifier |
| 98 | +cortex get hello-world |
159 | 99 | ``` |
160 | 100 |
|
161 | | -## Delete the API |
| 101 | +### Make a request |
| 102 | + |
| 103 | +```bash |
| 104 | +curl --request POST --header "Content-Type: application/json" http://***.amazonaws.com/hello-world |
| 105 | +``` |
162 | 106 |
|
163 | | -Finally, you can delete your API with a simple `cortex delete` command. |
| 107 | +### Get the response |
164 | 108 |
|
165 | 109 | ```bash |
166 | | -cortex delete iris-classifier |
| 110 | +curl http://***.amazonaws.com/hello-world/<REQUEST_ID> |
167 | 111 | ``` |
0 commit comments