Skip to content

Commit 252ca47

Browse files
Feature transformation server docker image (#1972)
* Add Dockerfile and app for building FTS docker image Signed-off-by: Felix Wang <wangfelix98@gmail.com> * Add CI for building FTS Signed-off-by: Felix Wang <wangfelix98@gmail.com>
1 parent 95ac807 commit 252ca47

File tree

5 files changed

+108
-1
lines changed

5 files changed

+108
-1
lines changed
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
name: build_feature_transformation_server
2+
3+
on:
4+
push:
5+
branches:
6+
- master
7+
8+
jobs:
9+
build-docker-image:
10+
runs-on: ubuntu-latest
11+
steps:
12+
- uses: actions/checkout@v2
13+
- name: Set up QEMU
14+
uses: docker/setup-qemu-action@v1
15+
- name: Set up Docker Buildx
16+
uses: docker/setup-buildx-action@v1
17+
- name: Login to DockerHub
18+
uses: docker/login-action@v1
19+
with:
20+
username: ${{ secrets.DOCKERHUB_USERNAME }}
21+
password: ${{ secrets.DOCKERHUB_TOKEN }}
22+
- name: Build and push
23+
run: |
24+
docker build \
25+
--file sdk/python/feast/infra/transformation_servers/Dockerfile \
26+
--tag feastdev/feature-transformation-server:`git rev-parse HEAD` \
27+
.
28+
docker push feastdev/feature-transformation-server:`git rev-parse HEAD`

sdk/python/feast/cli.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
from colorama import Fore, Style
2424

2525
from feast import flags, flags_helper, utils
26+
from feast.constants import DEFAULT_FEATURE_TRANSFORMATION_SERVER_PORT
2627
from feast.errors import FeastObjectNotFoundException, FeastProviderLoginError
2728
from feast.feature_store import FeatureStore
2829
from feast.feature_view import FeatureView
@@ -477,7 +478,11 @@ def serve_command(ctx: click.Context, port: int):
477478

478479
@cli.command("serve_transformations")
479480
@click.option(
480-
"--port", "-p", type=click.INT, default=6569, help="Specify a port for the server"
481+
"--port",
482+
"-p",
483+
type=click.INT,
484+
default=DEFAULT_FEATURE_TRANSFORMATION_SERVER_PORT,
485+
help="Specify a port for the server",
481486
)
482487
@click.pass_context
483488
def serve_transformations_command(ctx: click.Context, port: int):

sdk/python/feast/constants.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,17 @@
2222
# feature_store.yaml environment variable name for remote feature server
2323
FEATURE_STORE_YAML_ENV_NAME: str = "FEATURE_STORE_YAML_BASE64"
2424

25+
# Environment variable for registry
26+
REGISTRY_ENV_NAME: str = "REGISTRY_BASE64"
27+
2528
# Environment variable for toggling usage
2629
FEAST_USAGE = "FEAST_USAGE"
2730

2831
# Environment variable for the path for overwriting universal test configs
2932
FULL_REPO_CONFIGS_MODULE_ENV_NAME: str = "FULL_REPO_CONFIGS_MODULE"
33+
34+
# Environment variable for overwriting FTS port
35+
FEATURE_TRANSFORMATION_SERVER_PORT_ENV_NAME: str = "FEATURE_TRANSFORMATION_SERVER_PORT"
36+
37+
# Default FTS port
38+
DEFAULT_FEATURE_TRANSFORMATION_SERVER_PORT = 6569
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
FROM python:3.7-slim
2+
3+
# Copy app handler code
4+
COPY sdk/python/feast/infra/transformation_servers/app.py app.py
5+
6+
# Copy necessary parts of the Feast codebase
7+
COPY sdk/python sdk/python
8+
COPY protos protos
9+
COPY README.md README.md
10+
11+
# Install dependencies
12+
RUN pip3 install -e 'sdk/python[ci]'
13+
14+
# Start feature transformation server
15+
CMD [ "python", "app.py" ]
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import base64
2+
import os
3+
import tempfile
4+
from pathlib import Path
5+
6+
import yaml
7+
8+
from feast import FeatureStore
9+
from feast.constants import (
10+
DEFAULT_FEATURE_TRANSFORMATION_SERVER_PORT,
11+
FEATURE_STORE_YAML_ENV_NAME,
12+
FEATURE_TRANSFORMATION_SERVER_PORT_ENV_NAME,
13+
REGISTRY_ENV_NAME,
14+
)
15+
from feast.infra.local import LocalRegistryStore
16+
from feast.registry import get_registry_store_class_from_scheme
17+
18+
# Load RepoConfig
19+
config_base64 = os.environ[FEATURE_STORE_YAML_ENV_NAME]
20+
config_bytes = base64.b64decode(config_base64)
21+
22+
# Create a new unique directory for writing feature_store.yaml
23+
repo_path = Path(tempfile.mkdtemp())
24+
25+
with open(repo_path / "feature_store.yaml", "wb") as f:
26+
f.write(config_bytes)
27+
28+
# Write registry contents for local registries
29+
config_string = config_bytes.decode("utf-8")
30+
raw_config = yaml.safe_load(config_string)
31+
registry_path = raw_config["registry"]
32+
registry_store_class = get_registry_store_class_from_scheme(registry_path)
33+
if registry_store_class == LocalRegistryStore:
34+
registry_base64 = os.environ[REGISTRY_ENV_NAME]
35+
registry_bytes = base64.b64decode(registry_base64)
36+
registry_dir = os.path.dirname(registry_path)
37+
if not os.path.exists(repo_path / registry_dir):
38+
os.makedirs(repo_path / registry_dir)
39+
with open(repo_path / registry_path, "wb") as f:
40+
f.write(registry_bytes)
41+
42+
# Initialize the feature store
43+
store = FeatureStore(repo_path=str(repo_path.resolve()))
44+
45+
# Start the feature transformation server
46+
port = (
47+
os.environ.get(FEATURE_TRANSFORMATION_SERVER_PORT_ENV_NAME)
48+
or DEFAULT_FEATURE_TRANSFORMATION_SERVER_PORT
49+
)
50+
store.serve_transformations(port)

0 commit comments

Comments
 (0)