이번 글은 Feast Server를 생성하는데, CLI 환경위에서 진행합니다.
1. Local에서 Feast Server 실행하기
feast를 설치하고, init 후 새 dir을 만들어서 apply를 수행합니다.
pip install feast
feast init feature_repo
cd feature_repo
feast apply
마찬가지로 materialize-incremental을 수행합니다. 동작방식의 뜻은 마지막으로 materialize를 시작한 시점부터 최근까지 feature를 online store에 적재한다는 뜻이라고 저번 글에 이야길 했습니다.
feast materialize-incremental $(date +%Y-%m-%d)
feast serve
그럼 아마 아래와 같은 에러가 나타날 것 입니다.
feast.errors.ExperimentalFeatureNotEnabled:
You are attempting to use an experimental feature that is not enabled.
Please run `feast alpha enable python_feature_server`
이 부분은 feast가 update 되면 없어질 문제입니다. 일단 시키는 대로 수행합니다.
feast alpha enable python_feature_server
이제 local에서 서버가 띄워졌습니다. 새 터미널을 켜서 curl로 쿼리를 실행해보겠습니다.
curl -X POST \
"http://localhost:6566/get-online-features" \
-d '{
"features": [
"driver_hourly_stats:conv_rate",
"driver_hourly_stats:acc_rate",
"driver_hourly_stats:avg_daily_trips"
],
"entities": {
"driver_id": [1001, 1002, 1003]
}
}'
2. Docker Container로 Feast server 실행하기
우선 도커 이미지를 빌드합니다. 그 전에 새 폴더 하나 만들고 requirements.txt를 만듭니다.
mkdir -p docker
cd docker
sudo vim requirements.
requirements.txt는 아래처럼 구성합니다.
feast
scikit-learn
mlflow
이제 아래와 같은 Dockerfile을 생성합니다. run 부분을 보면 이전 글에서 했던 내용의 중복이라고 생각하면 됩니다.
# syntax=docker/dockerfile:1
FROM jupyter/base-notebook
WORKDIR /home/jovyan
COPY . /home/jovyan
RUN pip3 install -r requirements.txt
USER jovyan
RUN feast init feature_repo && \
cd feature_repo && \
feast apply && \
feast materialize-incremental $(date +%Y-%m-%d) && \
feast alpha enable python_feature_server
COPY feature_server.py /opt/conda/lib/python3.9/site-packages/feast/feature_server.py
CMD [ "/bin/sh", "-c", "cd /home/jovyan/feature_repo && feast serve"]
WORKDIR /home/jovyan
마지막 준비물인 feature_server.py 파일을 만듭니다.
import click
import uvicorn
from fastapi import FastAPI, HTTPException, Request
from fastapi.logger import logger
from google.protobuf.json_format import MessageToDict, Parse
import feast
from feast import proto_json
from feast.protos.feast.serving.ServingService_pb2 import GetOnlineFeaturesRequest
from feast.type_map import feast_value_type_to_python_type
def get_app(store: "feast.FeatureStore"):
proto_json.patch()
app = FastAPI()
@app.post("/get-online-features")
async def get_online_features(request: Request):
try:
# Validate and parse the request data into GetOnlineFeaturesRequest Protobuf object
body = await request.body()
request_proto = GetOnlineFeaturesRequest()
Parse(body, request_proto)
# Initialize parameters for FeatureStore.get_online_features(...) call
if request_proto.HasField("feature_service"):
features = store.get_feature_service(request_proto.feature_service)
else:
features = list(request_proto.features.val)
full_feature_names = request_proto.full_feature_names
batch_sizes = [len(v.val) for v in request_proto.entities.values()]
num_entities = batch_sizes[0]
if any(batch_size != num_entities for batch_size in batch_sizes):
raise HTTPException(status_code=500, detail="Uneven number of columns")
entity_rows = [
{
k: feast_value_type_to_python_type(v.val[idx])
for k, v in request_proto.entities.items()
}
for idx in range(num_entities)
]
response_proto = store.get_online_features(
features, entity_rows, full_feature_names=full_feature_names
).proto
# Convert the Protobuf object to JSON and return it
return MessageToDict( # type: ignore
response_proto, preserving_proto_field_name=True, float_precision=18
)
except Exception as e:
# Print the original exception on the server side
logger.exception(e)
# Raise HTTPException to return the error message to the client
raise HTTPException(status_code=500, detail=str(e))
return app
def start_server(store: "feast.FeatureStore", port: int):
app = get_app(store)
click.echo(
"This is an "
+ click.style("experimental", fg="yellow", bold=True, underline=True)
+ " feature. It's intended for early testing and feedback, and could change without warnings in future releases."
)
uvicorn.run(app, host="0.0.0.0", port=port)
이제 재료들을 완성하였고, 도커 build를 하겠습니다. tag는 feast-docker로 하겠습니다.
docker build --tag feast-docker .
많은 시간이 걸립니다. 빌드가 전부 다 되었다면 이제 feast docker container를 run 하겠습니다.
docker run -d --name feast-jupyter -p 8888:8888 -p 6566:6566 -p 5001:5001 -e JUPYTER_TOKEN='password' \
-v "$PWD":/home/jovyan/jupyter \
--user root \
-it feast-docker:latest
docker ps -a
curl을 이용해 테스트를 해보겠습니다.
curl -X POST \
"http://localhost:6566/get-online-features" \
-d '{
"features": [
"driver_hourly_stats:conv_rate",
"driver_hourly_stats:acc_rate",
"driver_hourly_stats:avg_daily_trips"
],
"entities": {
"driver_id": [1001, 1002, 1003]
}
}'
정상적으로 수행되는 것을 알 수 있다.
만약에 jupyter lab으로 추가 실행하고 싶다면 아래 명령어를 수행하면 됩니다.
docker exec -it feast-jupyter start.sh jupyter lab &
* 본 포스팅은 패스트캠퍼스 환급 챌린지 참여를 위해 작성되었습니다.
'AI > MLOps' 카테고리의 다른 글
[패스트캠퍼스 챌린지 37일차] ML Workflow (0) | 2022.03.01 |
---|---|
[패스트캠퍼스 챌린지 36일차] Feast - ML lifecycle (0) | 2022.02.28 |
[패스트캠퍼스 챌린지 34일차] Feast - Feature 추출 & 추론 (0) | 2022.02.26 |
[패스트캠퍼스 챌린지 33일차] Feast - Store 생성 & 배포 (0) | 2022.02.25 |
[패스트캠퍼스 챌린지 32일차] Feast 소개 (0) | 2022.02.24 |