|
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