Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: Example to Deploy Feast Remote Server Components Using Podman Locally #4516

Merged
merged 2 commits into from
Sep 21, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions examples/podman_local/my_project/README.md
redhatHameed marked this conversation as resolved.
Show resolved Hide resolved
redhatHameed marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@

# Feast Installation with Podman
redhatHameed marked this conversation as resolved.
Show resolved Hide resolved
This example explains how to deploy Feast remote server components using Podman locally and run an example using the client.

## Prerequisites

1. Podman: [Podman installation guide](https://podman.io/)
2. Python 3.9+ environment
3. Feast CLI (latest version)

## Setup

1. **Step 1: Feast Project**
redhatHameed marked this conversation as resolved.
Show resolved Hide resolved
- Feast [Project]() Created using the [quick start guide](https://docs.feast.dev/getting-started/quickstart).
redhatHameed marked this conversation as resolved.
Show resolved Hide resolved

2. **Run the installation script**
- Created this [feast_podman.sh](feast_podman.sh) script, which provides a simple way to install and run Feast feature servers (online, offline, and registry) using Podman. It handles container creation, startup, and execution of Feast commands inside the containers.
redhatHameed marked this conversation as resolved.
Show resolved Hide resolved
- Run the script to start the feature servers:
```bash
./feast_podman.sh
```
The script will ask for confirmation with `Do you want to start feature-servers? (y/n):`. Enter `y`, and you will see the success message:
```yaml
All feature-servers started.
```

3. **Verify the installation**
- Use the `podman ps` command to verify:
```bash
podman ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
61442d6d6ef3 docker.io/feastdev/feature-server:latest feast -c /feature... 2 minutes ago Up 2 minutes 0.0.0.0:6566->6566/tcp online-feature-server
1274c21716a6 docker.io/feastdev/feature-server:latest feast -c /feature... 2 minutes ago Up 2 minutes 0.0.0.0:8815->8815/tcp offline-feature-server
4e38ca8c39db docker.io/feastdev/feature-server:latest feast -c /feature... 2 minutes ago Up 2 minutes 0.0.0.0:6570->6570/tcp registry-feature-server
```
- Or you can check using Podman Desktop:
![podman.png](podman.png)

4. **Run the Feast apply**
- You need to perform Feast apply from the container. The same script will prompt `Apply 'feast apply' in the remote registry? (y/n)`. After you confirm, you will see the success message `'feast apply' command executed successfully in the remote registry.`

5. **Run the client examples**
- The [client](client) folder contains the client-side [feature_store.yaml](client/feature_repo/feature_store.yaml) and [test.py](client/feature_repo/test.py).

6. **Cleanup**
- You can clean up the created servers. The same script will prompt `Do you want to delete all the created feature-servers? (y/n):`. Enter `y` to clean up.
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
project: my_project
registry:
registry_type: remote
path: localhost:6570
offline_store:
type: remote
host: localhost
port: 8815
online_store:
type: remote
path: http://localhost:6566

123 changes: 123 additions & 0 deletions examples/podman_local/my_project/client/feature_repo/test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import subprocess
from datetime import datetime
import pandas as pd
from feast import FeatureStore
from feast.data_source import PushMode

def run_demo():
try:
store = FeatureStore(repo_path=".")

print("\n--- Historical features for training ---")
fetch_historical_features_entity_df(store, for_batch_scoring=False)

print("\n--- Historical features for batch scoring ---")
fetch_historical_features_entity_df(store, for_batch_scoring=True)

print("\n--- Load features into online store ---")
store.materialize_incremental(end_date=datetime.now())

print("\n--- Online features ---")
fetch_online_features(store)

print("\n--- Online features retrieved (instead) through a feature service---")
fetch_online_features(store, source="feature_service")

print(
"\n--- Online features retrieved (using feature service v3, which uses a feature view with a push source---"
)
fetch_online_features(store, source="push")

print("\n--- Simulate a stream event ingestion of the hourly stats df ---")
event_df = pd.DataFrame.from_dict(
{
"driver_id": [1001],
"event_timestamp": [
datetime.now(),
],
"created": [
datetime.now(),
],
"conv_rate": [1.0],
"acc_rate": [1.0],
"avg_daily_trips": [1000],
}
)
print(event_df)
store.push("driver_stats_push_source", event_df, to=PushMode.ONLINE_AND_OFFLINE)

print("\n--- Online features again with updated values from a stream push---")
fetch_online_features(store, source="push")
except Exception as e:
print(f"An error occurred in run_demo: {e}")

def fetch_historical_features_entity_df(store: FeatureStore, for_batch_scoring: bool):
try:
entity_df = pd.DataFrame.from_dict(
{
"driver_id": [1001, 1002, 1003],
"event_timestamp": [
datetime(2021, 4, 12, 10, 59, 42),
datetime(2021, 4, 12, 8, 12, 10),
datetime(2021, 4, 12, 16, 40, 26),
],
"label_driver_reported_satisfaction": [1, 5, 3],
"val_to_add": [1, 2, 3],
"val_to_add_2": [10, 20, 30],
}
)
if for_batch_scoring:
entity_df["event_timestamp"] = pd.to_datetime("now", utc=True)

training_df = store.get_historical_features(
entity_df=entity_df,
features=[
"driver_hourly_stats:conv_rate",
"driver_hourly_stats:acc_rate",
"driver_hourly_stats:avg_daily_trips",
"transformed_conv_rate:conv_rate_plus_val1",
"transformed_conv_rate:conv_rate_plus_val2",
],
).to_df()
print(training_df.head())
except Exception as e:
print(f"An error occurred in fetch_historical_features_entity_df: {e}")

def fetch_online_features(store, source: str = ""):
try:
entity_rows = [
{
"driver_id": 1001,
"val_to_add": 1000,
"val_to_add_2": 2000,
},
{
"driver_id": 1002,
"val_to_add": 1001,
"val_to_add_2": 2002,
},
]
if source == "feature_service":
features_to_fetch = store.get_feature_service("driver_activity_v1")
elif source == "push":
features_to_fetch = store.get_feature_service("driver_activity_v3")
else:
features_to_fetch = [
"driver_hourly_stats:acc_rate",
"transformed_conv_rate:conv_rate_plus_val1",
"transformed_conv_rate:conv_rate_plus_val2",
]
returned_features = store.get_online_features(
features=features_to_fetch,
entity_rows=entity_rows,
).to_dict()
for key, value in sorted(returned_features.items()):
print(key, " : ", value)
except Exception as e:
print(f"An error occurred in fetch_online_features: {e}")

if __name__ == "__main__":
try:
run_demo()
except Exception as e:
print(f"An error occurred in the main block: {e}")
83 changes: 83 additions & 0 deletions examples/podman_local/my_project/feast_podman.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
#!/bin/bash

# Set default values (replace these placeholders with real values)
IMAGE_REPOSITORY="feastdev/feature-server"
IMAGE_TAG="latest"
LOCAL_DIR="./feature_repo"
REMOTE_DIR="/feature_repo"
CONTAINER_NAME="registry-feature-server"


# Modes, container names, and their respective ports
MODES=("online" "offline" "registry")
COMMANDS=("serve -h 0.0.0.0" "serve_offline -h 0.0.0.0" "serve_registry")
CONTAINER_NAMES=("online-feature-server" "offline-feature-server" "registry-feature-server")
PORTS=("6566:6566" "8815:8815" "6570:6570")


# Prompt for confirmation to start feature servers
read -p "Do you want to start feature-servers? (y/n): " confirm_all

if [[ $confirm_all == [yY] ]]; then
# Create and run the containers
for i in ${!MODES[@]}; do
CONTAINER_NAME=${CONTAINER_NAMES[$i]}
COMMAND=${COMMANDS[$i]}
PORT=${PORTS[$i]}

echo "Starting $CONTAINER_NAME with port mapping $PORT"

# Run the Podman container using port mappings and volume mounting
podman run -d \
--name $CONTAINER_NAME \
-p $PORT \
-v $(pwd)/$LOCAL_DIR:$REMOTE_DIR \
$IMAGE_REPOSITORY:$IMAGE_TAG \
feast -c $REMOTE_DIR $COMMAND

echo "$CONTAINER_NAME is running on port $PORT."
done
echo "All feature-servers started."
else
echo "Feature-server startup was canceled."
fi

# Confirmation to apply 'feast apply' in the remote registry container
read -p "Apply 'feast apply' in the remote registry? (y/n): " confirm_apply

if [[ $confirm_apply == [yY] ]]; then
# Check if the registry-feature-server container is running
if podman ps -a --format "{{.Names}}" | grep -q "^registry-feature-server$"; then
echo "Running 'feast apply' in the registry-feature-server container"

# Execute the feast apply command in the registry container
podman exec $CONTAINER_NAME feast -c $REMOTE_DIR apply

echo "'feast apply' command executed successfully in the remote registry."
else
echo "Registry feature server container is not running."
fi
else
echo "'feast apply' not performed."
fi

# Prompt to delete containers after the work is done
read -p "Do you want to delete all the created feature-servers? (y/n): " confirm_delete

if [[ $confirm_delete == [yY] ]]; then
for i in ${!CONTAINER_NAMES[@]}; do
CONTAINER_NAME=${CONTAINER_NAMES[$i]}

# Check if the container is running before attempting to stop and delete it
if podman ps -a --format "{{.Names}}" | grep -q "^$CONTAINER_NAME$"; then
echo "Stopping and deleting $CONTAINER_NAME..."
podman stop $CONTAINER_NAME
podman rm $CONTAINER_NAME
else
echo "$CONTAINER_NAME is not running."
fi
done
echo "All feature-servers have been deleted."
else
echo "Feature-servers were not deleted."
fi
Empty file.
Binary file not shown.
redhatHameed marked this conversation as resolved.
Show resolved Hide resolved
Binary file not shown.
Loading
Loading