From 4603ee648c99989d397c86cc86788839d750dfd3 Mon Sep 17 00:00:00 2001 From: Philipp Moritz Date: Mon, 30 Jan 2023 00:48:52 -0800 Subject: [PATCH 01/10] Fixes from Prefect feedback (#18) --- ci/prefect-agent-service-ci.yaml | 2 +- prefect_anyscale/infrastructure.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ci/prefect-agent-service-ci.yaml b/ci/prefect-agent-service-ci.yaml index 97e2390..6ef9063 100644 --- a/ci/prefect-agent-service-ci.yaml +++ b/ci/prefect-agent-service-ci.yaml @@ -1,4 +1,4 @@ -name: prefect-agent-18 +name: prefect-agent-19 entrypoint: pip install prefect-anyscale && PREFECT_API_URL=$PREFECT_API_URL PREFECT_API_KEY=$PREFECT_API_KEY python start_anyscale_service.py --queue test runtime_env: working_dir: . diff --git a/prefect_anyscale/infrastructure.py b/prefect_anyscale/infrastructure.py index d8a197e..2e22bfa 100644 --- a/prefect_anyscale/infrastructure.py +++ b/prefect_anyscale/infrastructure.py @@ -52,7 +52,7 @@ async def run( if flow_run_id: cmd += " PREFECT__FLOW_RUN_ID={}".format(flow_run_id) - cmd += " /home/ray/anaconda3/bin/python -m prefect.engine" + cmd += " python -m prefect.engine" # Link the Job on the Anyscale UI with the prefect flow run job_name = "prefect-job-" + flow_run_id @@ -68,15 +68,15 @@ async def run( if self.cluster_env: content += 'cluster_env: "{}"\n'.format(self.cluster_env) + if task_status: + task_status.started(job_name) + with tempfile.NamedTemporaryFile(mode="w") as f: f.write(content) f.flush() logging.info(f"Submitting Anyscale Job with configuration '{content}'") returncode = subprocess.check_call(["anyscale", "job", "submit", f.name]) - if task_status: - task_status.started(job_name) - return AnyscaleJobResult( status_code=returncode, identifier="" ) From 3afd8c9858066138256eb64ccf72b58fa06b3ad0 Mon Sep 17 00:00:00 2001 From: Philipp Moritz Date: Tue, 14 Feb 2023 19:58:07 -0800 Subject: [PATCH 02/10] Update CI to run on new services API (#20) --- ci/prefect-agent-service-ci.yaml | 18 ++++++++++++------ start_anyscale_service.py | 17 +++++++---------- 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/ci/prefect-agent-service-ci.yaml b/ci/prefect-agent-service-ci.yaml index 6ef9063..758d935 100644 --- a/ci/prefect-agent-service-ci.yaml +++ b/ci/prefect-agent-service-ci.yaml @@ -1,6 +1,12 @@ -name: prefect-agent-19 -entrypoint: pip install prefect-anyscale && PREFECT_API_URL=$PREFECT_API_URL PREFECT_API_KEY=$PREFECT_API_KEY python start_anyscale_service.py --queue test -runtime_env: - working_dir: . - upload_path: "s3://anyscale-prefect-integration-test/github-working-dir/" -healthcheck_url: "/healthcheck" \ No newline at end of file +name: prefect-agent-24 +cloud: "anyscale_v2_default_cloud_vpn" +ray_serve_config: + import_path: start_anyscale_service:entrypoint + runtime_env: + env_vars: + PREFECT_API_URL: $PREFECT_API_URL + PREFECT_API_KEY: $PREFECT_API_KEY + ANYSCALE_PREFECT_QUEUE: test + pip: ["prefect-anyscale"] + working_dir: . + upload_path: "s3://anyscale-prefect-integration-test/github-working-dir/" diff --git a/start_anyscale_service.py b/start_anyscale_service.py index 7c22d86..e3dfbbf 100644 --- a/start_anyscale_service.py +++ b/start_anyscale_service.py @@ -5,33 +5,30 @@ from fastapi import FastAPI from ray import serve -parser = argparse.ArgumentParser() -parser.add_argument("--queue", type=str) -args = parser.parse_args() - serve.start(detached=True) app = FastAPI() -@serve.deployment(route_prefix="/", num_replicas=1) +@serve.deployment(route_prefix="/", num_replicas=1, health_check_period_s=10, health_check_timeout_s=30) @serve.ingress(app) class PrefectAgentDeployment: def __init__(self, prefect_env): self.agent = subprocess.Popen( - ["prefect", "agent", "start", "-q", args.queue], + ["prefect", "agent", "start", "-q", prefect_env["ANYSCALE_PREFECT_QUEUE"]], env=dict(os.environ, **prefect_env), ) - @app.get("/healthcheck") - def healthcheck(self): + def check_health(self): poll = self.agent.poll() if poll is None: return else: raise RuntimeError("Prefect agent died") -serve.run(PrefectAgentDeployment.bind({ + +entrypoint = PrefectAgentDeployment.bind({ "PREFECT_API_URL": os.environ["PREFECT_API_URL"], "PREFECT_API_KEY": os.environ["PREFECT_API_KEY"], "PREFECT_EXTRA_ENTRYPOINTS": "prefect_anyscale", -})) + "ANYSCALE_PREFECT_QUEUE": os.environ["ANYSCALE_PREFECT_QUEUE"], +}) From 6035f4709a2cbf29c7b9dd425b15207b4e8fd1b6 Mon Sep 17 00:00:00 2001 From: Philipp Moritz Date: Tue, 14 Feb 2023 20:29:24 -0800 Subject: [PATCH 03/10] Update README for 0.2.0 release (#21) --- README.md | 13 +++++++++---- prefect-agent-service.yaml | 15 ++++++++++----- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index c7bda1a..26ac568 100644 --- a/README.md +++ b/README.md @@ -66,10 +66,15 @@ prefect config view --hide-sources and create a `prefect-agent-service.yaml` file where you fill in the information just displayed in place of the `...`: ```yaml name: prefect-agent -entrypoint: pip install prefect-anyscale && PREFECT_API_URL="https://api.prefect.cloud/api/accounts/..." PREFECT_API_KEY="..." python start_anyscale_service.py --queue test -runtime_env: - working_dir: https://github.com/anyscale/prefect-anyscale/archive/refs/tags/v0.1.0.zip -healthcheck_url: "/healthcheck" +ray_serve_config: + import_path: start_anyscale_service:entrypoint + runtime_env: + env_vars: + PREFECT_API_URL: "https://api.prefect.cloud/api/accounts/..." + PREFECT_API_KEY: "..." + ANYSCALE_PREFECT_QUEUE: test + pip: ["prefect-anyscale"] + working_dir: https://github.com/anyscale/prefect-anyscale/archive/refs/tags/v0.2.0.zip ``` **NOTE**: This will store your Prefect API token in the service diff --git a/prefect-agent-service.yaml b/prefect-agent-service.yaml index 76b0b62..7506670 100644 --- a/prefect-agent-service.yaml +++ b/prefect-agent-service.yaml @@ -1,5 +1,10 @@ -name: prefect-agent-10 -entrypoint: pip install prefect-anyscale && PREFECT_API_URL=$PREFECT_API_URL PREFECT_API_KEY=$PREFECT_API_KEY python start_anyscale_service.py --queue test -runtime_env: - working_dir: https://github.com/anyscale/prefect-anyscale/archive/refs/tags/v0.1.0.zip -healthcheck_url: "/healthcheck" +name: prefect-agent +ray_serve_config: + import_path: start_anyscale_service:entrypoint + runtime_env: + env_vars: + PREFECT_API_URL: $PREFECT_API_URL + PREFECT_API_KEY: $PREFECT_API_KEY + ANYSCALE_PREFECT_QUEUE: test + pip: ["prefect-anyscale"] + working_dir: https://github.com/anyscale/prefect-anyscale/archive/refs/tags/v0.2.0.zip From f2ece0ce2145a7cc8e108f23047d1f283e4adc4d Mon Sep 17 00:00:00 2001 From: Philipp Moritz Date: Wed, 1 Mar 2023 21:10:25 -0800 Subject: [PATCH 04/10] Document infra override and fix compute config (#22) This documents how to use the infra override in a prefect deployment to set the compute config and fixes the compute config and cluster environment pass through, as well as adding a regression test. --- README.md | 24 +++++++++++++++++++++++- ci/prefect-agent-service-ci.yaml | 6 +++--- ci/submit_prefect_run_and_check.py | 3 ++- prefect_anyscale/infrastructure.py | 6 +++--- start_anyscale_service.py | 2 ++ 5 files changed, 33 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 26ac568..8ffcf58 100644 --- a/README.md +++ b/README.md @@ -121,4 +121,26 @@ You can now schedule new runs with this deployment from the Prefect UI ![submit prefect run](./doc/prefect_submit_run.png) -and it will be executed as an Anyscale Job on an autoscaling Ray Cluster which has the same setup as the development setup described above. \ No newline at end of file +and it will be executed as an Anyscale Job on an autoscaling Ray Cluster which has the same setup as the development setup described above. + +#### Overriding properties of the infra block + +You can override properties of the Anyscale infra block in a deployment like this + +```python +import prefect +from prefect.filesystems import S3 +from prefect_anyscale import AnyscaleJob + +from prefect_test import count_to + +deployment = prefect.deployments.Deployment.build_from_flow( + flow=count_to, + name="prefect_test_custom", + work_queue_name="test", + storage=S3.load("test-storage"), + infrastructure=AnyscaleJob.load("test-infra"), + infra_overrides={"compute_config": "test-compute-config"} +) +deployment.apply() +``` diff --git a/ci/prefect-agent-service-ci.yaml b/ci/prefect-agent-service-ci.yaml index 758d935..56c06f3 100644 --- a/ci/prefect-agent-service-ci.yaml +++ b/ci/prefect-agent-service-ci.yaml @@ -1,5 +1,5 @@ -name: prefect-agent-24 -cloud: "anyscale_v2_default_cloud_vpn" +name: prefect-agent-26 +cloud: "anyscale_v2_default_cloud" ray_serve_config: import_path: start_anyscale_service:entrypoint runtime_env: @@ -7,6 +7,6 @@ ray_serve_config: PREFECT_API_URL: $PREFECT_API_URL PREFECT_API_KEY: $PREFECT_API_KEY ANYSCALE_PREFECT_QUEUE: test - pip: ["prefect-anyscale"] + ANYSCALE_PREFECT_DEVELOPMENT: "1" working_dir: . upload_path: "s3://anyscale-prefect-integration-test/github-working-dir/" diff --git a/ci/submit_prefect_run_and_check.py b/ci/submit_prefect_run_and_check.py index 619aad1..a36a0d2 100644 --- a/ci/submit_prefect_run_and_check.py +++ b/ci/submit_prefect_run_and_check.py @@ -13,7 +13,8 @@ name="prefect_test", work_queue_name="test", storage=S3.load("test-storage-github"), - infrastructure=AnyscaleJob.load("anyscale-job-infra") + infrastructure=AnyscaleJob.load("anyscale-job-infra"), + infra_overrides={"compute_config": "test-compute-config"}, ) deployment.apply() diff --git a/prefect_anyscale/infrastructure.py b/prefect_anyscale/infrastructure.py index 2e22bfa..655abe1 100644 --- a/prefect_anyscale/infrastructure.py +++ b/prefect_anyscale/infrastructure.py @@ -58,9 +58,9 @@ async def run( job_name = "prefect-job-" + flow_run_id content = """ - name: "{}" - entrypoint: "{}" - """.format(job_name, cmd) +name: "{}" +entrypoint: "{}" +""".format(job_name, cmd) if self.compute_config: content += 'compute_config: "{}"\n'.format(self.compute_config) diff --git a/start_anyscale_service.py b/start_anyscale_service.py index e3dfbbf..16be52b 100644 --- a/start_anyscale_service.py +++ b/start_anyscale_service.py @@ -25,6 +25,8 @@ def check_health(self): else: raise RuntimeError("Prefect agent died") +if os.environ.get("ANYSCALE_PREFECT_DEVELOPMENT", "0") == "1": + subprocess.check_call(["pip", "install", "-e", "."]) entrypoint = PrefectAgentDeployment.bind({ "PREFECT_API_URL": os.environ["PREFECT_API_URL"], From 22a6314f46c855d816d5908db93acd2de1b94566 Mon Sep 17 00:00:00 2001 From: Philipp Moritz Date: Sun, 19 Mar 2023 19:58:26 -0700 Subject: [PATCH 05/10] Support fetching PREFECT_API_KEY from secret store (#23) Support fetching the PREFECT_API_KEY environment variable from the AWS Secret Store (see instructions in README). --- .github/workflows/workflow.yaml | 15 +++++++++-- README.md | 25 +++++++++++++++++++ ci/prefect-agent-service-awssecrets-ci.yaml | 13 ++++++++++ ci/prefect-agent-service-ci.yaml | 2 +- ci/submit_prefect_run_and_check.py | 7 +++++- doc/prefect_api_key_secret.png | Bin 0 -> 125529 bytes prefect-agent-service.yaml | 2 ++ prefect_anyscale/infrastructure.py | 11 +++++++-- start_anyscale_service.py | 26 ++++++++++++++++++-- 9 files changed, 93 insertions(+), 8 deletions(-) create mode 100644 ci/prefect-agent-service-awssecrets-ci.yaml create mode 100644 doc/prefect_api_key_secret.png diff --git a/.github/workflows/workflow.yaml b/.github/workflows/workflow.yaml index 20a2d8c..05b8b4b 100644 --- a/.github/workflows/workflow.yaml +++ b/.github/workflows/workflow.yaml @@ -29,7 +29,18 @@ jobs: role-to-assume: arn:aws:iam::959243851260:role/github-action-prefect-integration role-session-name: s3access aws-region: us-west-2 - - name: Test Prefect <> Anyscale Integration + - name: Test Prefect <> Anyscale Integration (AWS secret manager) + env: + ANYSCALE_HOST: ${{ secrets.ANYSCALE_HOST }} + ANYSCALE_CLI_TOKEN: ${{ secrets.ANYSCALE_CLI_TOKEN }} + PREFECT_API_URL: ${{ secrets.PREFECT_API_URL }} + PREFECT_API_KEY: ${{ secrets.PREFECT_API_KEY }} + run: | + pip install -e . + envsubst < ci/prefect-agent-service-awssecrets-ci.yaml > /tmp/prefect-agent-service-awssecrets.out + anyscale service deploy /tmp/prefect-agent-service-awssecrets.out + PYTHONPATH=$PYTHONPATH:. python ci/submit_prefect_run_and_check.py --queue test-awssecrets + - name: Test Prefect <> Anyscale Integration (PREFECT_API_KEY) env: ANYSCALE_HOST: ${{ secrets.ANYSCALE_HOST }} ANYSCALE_CLI_TOKEN: ${{ secrets.ANYSCALE_CLI_TOKEN }} @@ -39,4 +50,4 @@ jobs: pip install -e . envsubst < ci/prefect-agent-service-ci.yaml > /tmp/prefect-agent-service.out anyscale service deploy /tmp/prefect-agent-service.out - PYTHONPATH=$PYTHONPATH:. python ci/submit_prefect_run_and_check.py + PYTHONPATH=$PYTHONPATH:. python ci/submit_prefect_run_and_check.py --queue test diff --git a/README.md b/README.md index 8ffcf58..5d58b3b 100644 --- a/README.md +++ b/README.md @@ -144,3 +144,28 @@ deployment = prefect.deployments.Deployment.build_from_flow( ) deployment.apply() ``` +#### Using the AWS Secrets Manager for storing the PREFECT_API_KEY + +We recommend using the AWS Secrets Manager for storing your PREFECT_API_KEY token. Store your +`PREFECT_API_KEY` secret as a Plaintext secret (not Key/value) like the following + +![create prefect secret](./doc/prefect_api_key_secret.png) + +and add the following policy to your `-cluster_node_role` role: + +``` +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": "secretsmanager:GetSecretValue", + "Resource": "" + } + ] +} +``` + +You can then run the agent by specifying a `ANYSCALE_PREFECT_AWS_SECRET_ID` and +`ANYSCALE_PREFECT_AWS_REGION` in your configuration yaml instead of the `PREFECT_API_KEY`, +see the `ci/prefect-agent-service-awssecrets-ci.yaml` file in this repository for an example. diff --git a/ci/prefect-agent-service-awssecrets-ci.yaml b/ci/prefect-agent-service-awssecrets-ci.yaml new file mode 100644 index 0000000..0663aa2 --- /dev/null +++ b/ci/prefect-agent-service-awssecrets-ci.yaml @@ -0,0 +1,13 @@ +name: prefect-agent-awssecrets-27 +cloud: "anyscale_v2_default_cloud" +ray_serve_config: + import_path: start_anyscale_service:entrypoint + runtime_env: + env_vars: + PREFECT_API_URL: $PREFECT_API_URL + ANYSCALE_PREFECT_AWS_SECRET_ID: prefect-api-key + ANYSCALE_PREFECT_AWS_REGION: us-west-2 + ANYSCALE_PREFECT_QUEUE: test-awssecrets + ANYSCALE_PREFECT_DEVELOPMENT: "1" + working_dir: . + upload_path: "s3://anyscale-prefect-integration-test/github-working-dir/" \ No newline at end of file diff --git a/ci/prefect-agent-service-ci.yaml b/ci/prefect-agent-service-ci.yaml index 56c06f3..8fb83b2 100644 --- a/ci/prefect-agent-service-ci.yaml +++ b/ci/prefect-agent-service-ci.yaml @@ -1,4 +1,4 @@ -name: prefect-agent-26 +name: prefect-agent-27 cloud: "anyscale_v2_default_cloud" ray_serve_config: import_path: start_anyscale_service:entrypoint diff --git a/ci/submit_prefect_run_and_check.py b/ci/submit_prefect_run_and_check.py index a36a0d2..9f8f173 100644 --- a/ci/submit_prefect_run_and_check.py +++ b/ci/submit_prefect_run_and_check.py @@ -1,3 +1,4 @@ +import argparse import asyncio import prefect.deployments @@ -8,10 +9,14 @@ from prefect_test import count_to +parser = argparse.ArgumentParser() +parser.add_argument("--queue", help="prefect queue to submit to") +args = parser.parse_args() + deployment = prefect.deployments.Deployment.build_from_flow( flow=count_to, name="prefect_test", - work_queue_name="test", + work_queue_name=args.queue, storage=S3.load("test-storage-github"), infrastructure=AnyscaleJob.load("anyscale-job-infra"), infra_overrides={"compute_config": "test-compute-config"}, diff --git a/doc/prefect_api_key_secret.png b/doc/prefect_api_key_secret.png new file mode 100644 index 0000000000000000000000000000000000000000..317fd78c6b631b9bb8ec22a97fc57f32291c1143 GIT binary patch literal 125529 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 H=PUmMcPsEZ literal 0 HcmV?d00001 diff --git a/prefect-agent-service.yaml b/prefect-agent-service.yaml index 7506670..e6f5bd8 100644 --- a/prefect-agent-service.yaml +++ b/prefect-agent-service.yaml @@ -4,6 +4,8 @@ ray_serve_config: runtime_env: env_vars: PREFECT_API_URL: $PREFECT_API_URL + # Consider using a secret manager to store the PREFECT_API_KEY token + # (see instructions in README.md) PREFECT_API_KEY: $PREFECT_API_KEY ANYSCALE_PREFECT_QUEUE: test pip: ["prefect-anyscale"] diff --git a/prefect_anyscale/infrastructure.py b/prefect_anyscale/infrastructure.py index 655abe1..e0f72ed 100644 --- a/prefect_anyscale/infrastructure.py +++ b/prefect_anyscale/infrastructure.py @@ -2,7 +2,6 @@ import os from typing import Dict, Union import subprocess -import sys import tempfile from prefect.infrastructure.base import Infrastructure, InfrastructureResult @@ -44,10 +43,18 @@ async def run( api_key = env.get("PREFECT_API_KEY") flow_run_id = env.get("PREFECT__FLOW_RUN_ID") + aws_secret_id = env.get("ANYSCALE_PREFECT_AWS_SECRET_ID") + cmd = "" if api_url: cmd += "PREFECT_API_URL={}".format(api_url) - if api_key: + if aws_secret_id: + # If we use the AWS secret manager to pass the PREFECT_API_KEY + # through, we will retrieve the API key when the job gets executed. + aws_region = env.get("ANYSCALE_PREFECT_AWS_REGION") + cmd += " PREFECT_API_KEY=`aws secretsmanager get-secret-value --secret-id {} --region {} --output=text --query=SecretString`".format(aws_secret_id, aws_region) + elif api_key: + logging.warn("Your PREFECT_API_KEY is currently stored in plain text. Consider using a secret manager to store your secrets.") cmd += " PREFECT_API_KEY={}".format(api_key) if flow_run_id: cmd += " PREFECT__FLOW_RUN_ID={}".format(flow_run_id) diff --git a/start_anyscale_service.py b/start_anyscale_service.py index 16be52b..6a25e94 100644 --- a/start_anyscale_service.py +++ b/start_anyscale_service.py @@ -1,10 +1,32 @@ -import argparse +import logging import os import subprocess from fastapi import FastAPI from ray import serve +def get_prefect_secret_environment(): + # We retrieve the PREFECT_API_KEY from the secret store so + # we can pass it to the prefect agent since the agent will + # need it to connect to the prefect control plane. + aws_secret_id = os.environ.get("ANYSCALE_PREFECT_AWS_SECRET_ID") + if aws_secret_id: + import boto3 + client = boto3.client( + "secretsmanager", region_name=os.environ["ANYSCALE_PREFECT_AWS_REGION"] + ) + response = client.get_secret_value(SecretId=aws_secret_id) + return { + "PREFECT_API_KEY": response["SecretString"], + "ANYSCALE_PREFECT_AWS_SECRET_ID": aws_secret_id, + "ANYSCALE_PREFECT_AWS_REGION": os.environ["ANYSCALE_PREFECT_AWS_REGION"], + } + else: + logging.warn("Your PREFECT_API_KEY is currently stored in plain text. Consider using a secret manager to store your secrets.") + return { + "PREFECT_API_KEY": os.environ["PREFECT_API_KEY"] + } + serve.start(detached=True) app = FastAPI() @@ -30,7 +52,7 @@ def check_health(self): entrypoint = PrefectAgentDeployment.bind({ "PREFECT_API_URL": os.environ["PREFECT_API_URL"], - "PREFECT_API_KEY": os.environ["PREFECT_API_KEY"], "PREFECT_EXTRA_ENTRYPOINTS": "prefect_anyscale", "ANYSCALE_PREFECT_QUEUE": os.environ["ANYSCALE_PREFECT_QUEUE"], + **get_prefect_secret_environment() }) From 2ee917ab04fa56c22559a686f5e72871843f2538 Mon Sep 17 00:00:00 2001 From: Philipp Moritz Date: Tue, 28 Mar 2023 21:02:13 -0700 Subject: [PATCH 06/10] Update notes to reflect latest release (#24) Update README to reflect latest release Should do some manual testing before merging this to make sure everything is working (we have tests in the CI, but to make sure nothing slipped) --- README.md | 4 ++-- setup.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 5d58b3b..3808712 100644 --- a/README.md +++ b/README.md @@ -63,7 +63,7 @@ We now need to create an Anyscale Service file for deploying the Anyscale Prefec ```bash prefect config view --hide-sources ``` -and create a `prefect-agent-service.yaml` file where you fill in the information just displayed in place of the `...`: +and create a `prefect-agent-service.yaml` file where you **fill in the information** displayed above in place of the `...`: ```yaml name: prefect-agent ray_serve_config: @@ -74,7 +74,7 @@ ray_serve_config: PREFECT_API_KEY: "..." ANYSCALE_PREFECT_QUEUE: test pip: ["prefect-anyscale"] - working_dir: https://github.com/anyscale/prefect-anyscale/archive/refs/tags/v0.2.0.zip + working_dir: https://github.com/anyscale/prefect-anyscale/archive/refs/tags/v0.2.1.zip ``` **NOTE**: This will store your Prefect API token in the service diff --git a/setup.py b/setup.py index 1d5f359..90c0e57 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ setup( name="prefect-anyscale", - version="0.1.0", + version="0.2.1", description="Prefect integrations with Anyscale.", license="Apache License 2.0", author="Anyscale, Inc.", From 052b1ae9a53790292933f0c4019b4899c488a6ac Mon Sep 17 00:00:00 2001 From: Philipp Moritz Date: Mon, 10 Apr 2023 22:04:06 -0700 Subject: [PATCH 07/10] Add prefect runtime environment hook (#25) This PR makes it so the working_dir is set to the source code directory from prefect so we have access to the source code everywhere in the cluster. --- ci/complex_flow.py | 24 +++++++++++++++++++++ ci/prefect-agent-service-awssecrets-ci.yaml | 2 +- ci/prefect-agent-service-ci.yaml | 2 +- ci/submit_prefect_run_and_check.py | 14 ++++++++++++ ci/test_python_file.py | 2 ++ prefect_anyscale/__init__.py | 19 +++++++++++++++- prefect_anyscale/infrastructure.py | 3 +++ 7 files changed, 63 insertions(+), 3 deletions(-) create mode 100644 ci/complex_flow.py create mode 100644 ci/test_python_file.py diff --git a/ci/complex_flow.py b/ci/complex_flow.py new file mode 100644 index 0000000..aaa1e77 --- /dev/null +++ b/ci/complex_flow.py @@ -0,0 +1,24 @@ +import ray + +from prefect import flow, task +from prefect_ray import RayTaskRunner + +# This custom resource will cause another node to be added +# to the cluster so we can test that the working directory +# is indeed synced to all nodes in the cluster. +@ray.remote(resources={"custom_resource": 1}) +def remote_task(): + from ci.test_python_file import test + return test() + +@task +def test_task(): + return ray.get(remote_task.remote()) + +@flow(task_runner=RayTaskRunner) +def complex_flow(): + result = test_task.submit() + assert result.result() == 42 + +if __name__ == "__main__": + complex_flow() diff --git a/ci/prefect-agent-service-awssecrets-ci.yaml b/ci/prefect-agent-service-awssecrets-ci.yaml index 0663aa2..ff4476e 100644 --- a/ci/prefect-agent-service-awssecrets-ci.yaml +++ b/ci/prefect-agent-service-awssecrets-ci.yaml @@ -1,4 +1,4 @@ -name: prefect-agent-awssecrets-27 +name: prefect-agent-awssecrets-28 cloud: "anyscale_v2_default_cloud" ray_serve_config: import_path: start_anyscale_service:entrypoint diff --git a/ci/prefect-agent-service-ci.yaml b/ci/prefect-agent-service-ci.yaml index 8fb83b2..5797c14 100644 --- a/ci/prefect-agent-service-ci.yaml +++ b/ci/prefect-agent-service-ci.yaml @@ -1,4 +1,4 @@ -name: prefect-agent-27 +name: prefect-agent-28 cloud: "anyscale_v2_default_cloud" ray_serve_config: import_path: start_anyscale_service:entrypoint diff --git a/ci/submit_prefect_run_and_check.py b/ci/submit_prefect_run_and_check.py index 9f8f173..8fb8a7b 100644 --- a/ci/submit_prefect_run_and_check.py +++ b/ci/submit_prefect_run_and_check.py @@ -8,6 +8,7 @@ from prefect_anyscale import AnyscaleJob from prefect_test import count_to +from ci.complex_flow import complex_flow parser = argparse.ArgumentParser() parser.add_argument("--queue", help="prefect queue to submit to") @@ -25,6 +26,18 @@ flow_run = prefect.deployments.run_deployment("count-to/prefect_test", parameters={"highest_number": 5}) +complex_deployment = prefect.deployments.Deployment.build_from_flow( + flow=complex_flow, + name="prefect_complex_test", + work_queue_name=args.queue, + storage=S3.load("test-storage-github"), + infrastructure=AnyscaleJob.load("anyscale-job-infra"), + infra_overrides={"compute_config": "complex-test-compute-config"}, +) +complex_deployment.apply() + +complex_flow_run = prefect.deployments.run_deployment("complex-flow/prefect_complex_test", parameters={}) + async def wait_for_run_complete(flow_id): async with get_client() as client: while True: @@ -37,3 +50,4 @@ async def wait_for_run_complete(flow_id): await asyncio.sleep(5.0) asyncio.run(wait_for_run_complete(flow_run.id)) +asyncio.run(wait_for_run_complete(complex_flow_run.id)) diff --git a/ci/test_python_file.py b/ci/test_python_file.py new file mode 100644 index 0000000..4009b47 --- /dev/null +++ b/ci/test_python_file.py @@ -0,0 +1,2 @@ +def test(): + return 42 diff --git a/prefect_anyscale/__init__.py b/prefect_anyscale/__init__.py index 3753a08..2c67380 100644 --- a/prefect_anyscale/__init__.py +++ b/prefect_anyscale/__init__.py @@ -1,5 +1,22 @@ from prefect_anyscale.infrastructure import AnyscaleJob + +def prefect_runtime_environment_hook(runtime_env): + + if not runtime_env: + runtime_env = {} + + # If no working_dir is specified, we use the current + # directory as the working directory -- this will be + # the directory containing the source code which is + # downloaded by the Prefect engine. + if not runtime_env.get("working_dir"): + runtime_env["working_dir"] = "." + + return runtime_env + + __all__ = [ - "AnyscaleJob" + "AnyscaleJob", + "prefect_runtime_environment_hook", ] diff --git a/prefect_anyscale/infrastructure.py b/prefect_anyscale/infrastructure.py index e0f72ed..7ede61e 100644 --- a/prefect_anyscale/infrastructure.py +++ b/prefect_anyscale/infrastructure.py @@ -59,6 +59,9 @@ async def run( if flow_run_id: cmd += " PREFECT__FLOW_RUN_ID={}".format(flow_run_id) + # Install runtime environment + cmd += " RAY_RUNTIME_ENV_HOOK=prefect_anyscale.prefect_runtime_environment_hook" + cmd += " python -m prefect.engine" # Link the Job on the Anyscale UI with the prefect flow run From 32d42cec3fe9041ece92f59f6e4999121a37c961 Mon Sep 17 00:00:00 2001 From: Philipp Moritz Date: Mon, 17 Apr 2023 11:33:49 -0700 Subject: [PATCH 08/10] Add instructions to run Anyscale Jobs with an existing agent --- README.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/README.md b/README.md index 3808712..762056e 100644 --- a/README.md +++ b/README.md @@ -169,3 +169,18 @@ and add the following policy to your `-cluster_node_role` role: You can then run the agent by specifying a `ANYSCALE_PREFECT_AWS_SECRET_ID` and `ANYSCALE_PREFECT_AWS_REGION` in your configuration yaml instead of the `PREFECT_API_KEY`, see the `ci/prefect-agent-service-awssecrets-ci.yaml` file in this repository for an example. + +### Using your own Prefect Agent + +If you already have a setup with an existing Prefect agent working, you can use that agent +to run the Prefect Anyscale integration. + +Make sure you have the `prefect_anyscale` package installed in the Prefect Agent's environment, +you are logged into Prefect (or have set the `PREFECT_API_URL` and `PREFECT_API_KEY` environment +variables set) and start the agent with +``` +PREFECT_EXTRA_ENTRYPOINTS=prefect_anyscale prefect agent start -q +``` + +The agent will listen to new work on the specified queue and will execute flows that run with +the `AnyscaleJob` infra as Anyscale Jobs. From 9c74b9034b135cf9ea875271732b3fc34507a211 Mon Sep 17 00:00:00 2001 From: Philipp Moritz Date: Mon, 17 Apr 2023 11:39:28 -0700 Subject: [PATCH 09/10] update --- README.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 762056e..b600ed1 100644 --- a/README.md +++ b/README.md @@ -175,9 +175,14 @@ see the `ci/prefect-agent-service-awssecrets-ci.yaml` file in this repository fo If you already have a setup with an existing Prefect agent working, you can use that agent to run the Prefect Anyscale integration. -Make sure you have the `prefect_anyscale` package installed in the Prefect Agent's environment, -you are logged into Prefect (or have set the `PREFECT_API_URL` and `PREFECT_API_KEY` environment -variables set) and start the agent with +First make sure you +- Have the `prefect_anyscale` package installed in the Prefect Agent's environment and +- Are logged into Prefect or have set the `PREFECT_API_URL` and `PREFECT_API_KEY` environment +variables and +- Are logged into Anyscale or have set the `ANYSCALE_HOST` and `ANYSCALE_CLI_TOKEN` environment +variables + +Then start the agent with ``` PREFECT_EXTRA_ENTRYPOINTS=prefect_anyscale prefect agent start -q ``` From a1debaef292f5e4b8d480b5afe0309c09f5cb88a Mon Sep 17 00:00:00 2001 From: Philipp Moritz Date: Wed, 19 Apr 2023 03:47:50 -0700 Subject: [PATCH 10/10] Add example to submit Anyscale Job from Prefect workflow (#27) Add an example on how to run an Anyscale Job as part of an existing Prefect setup --- README.md | 44 +++++++++++++++++++++++++++++- ci/anyscale_job.py | 15 ++++++++++ ci/complex_flow.py | 33 +++++++++++++++++++++- ci/submit_prefect_run_and_check.py | 2 +- 4 files changed, 91 insertions(+), 3 deletions(-) create mode 100644 ci/anyscale_job.py diff --git a/README.md b/README.md index 3808712..303f01c 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,49 @@ do not use the `RayTaskRunner(address="ray://...")` or `RayTaskRunner(address="a cause various issues (version mismatches between client and cluster, loosing connection, slower data transfer and API calls between client and server etc). -## Production Setup +## Running Anyscale Jobs as part of a larger Prefect flow + +You can run Anyscale Jobs as part of a Prefect flow like this: +```python +import os +import subprocess +import tempfile +import yaml + +from prefect import flow, task, get_run_logger + +@task +def execute_anyscale_job(args): + job_config = { + "name": "my-anyscale-job", + "description": "An Anyscale Job submitted from Prefect.", + "cluster_env": "default_cluster_env_2.3.1_py39", + "runtime_env": { + "working_dir": ".", + "upload_path": "", + }, + "entrypoint": "python my_job_script.py " + " ".join([f"--{key} {val}" for key, val in args.items()]), + } + + with tempfile.NamedTemporaryFile(mode="w") as f: + yaml.dump(job_config, f) + f.flush() + # Submit an Anyscale Job from Prefect and record the logs + output = subprocess.check_output( + ["anyscale", "job", "submit", f.name, "--follow"] + ) + logger = get_run_logger() + logger.info("Anyscale Job output: " + output.decode()) + +@flow +def flow_with_anyscale_job(): + execute_anyscale_job.submit({"arg": "value"}) + +if __name__ == "__main__": + flow_with_anyscale_job() +``` + +## Using Anyscale as the compute infrastructure for Prefect workloads This repository is providing an integration between Anyscale and Prefect for production scenarios, where you want to submit your experiments from the Prefect UI and have them run in Anyscale. It uses diff --git a/ci/anyscale_job.py b/ci/anyscale_job.py new file mode 100644 index 0000000..ced9555 --- /dev/null +++ b/ci/anyscale_job.py @@ -0,0 +1,15 @@ +import argparse +import ray + +parser = argparse.ArgumentParser() +parser.add_argument('--arg', type=str) +args = parser.parse_args() + +@ray.remote +def f(arg): + return "Argument is '" + arg + "'" + +args = ["This", "is", "a", "test"] + [args.arg] +results = ray.get([f.remote(arg) for arg in args]) + +print("\n".join(results)) \ No newline at end of file diff --git a/ci/complex_flow.py b/ci/complex_flow.py index aaa1e77..2b660a5 100644 --- a/ci/complex_flow.py +++ b/ci/complex_flow.py @@ -1,6 +1,11 @@ +import os +import subprocess +import tempfile +import yaml + import ray -from prefect import flow, task +from prefect import flow, task, get_run_logger from prefect_ray import RayTaskRunner # This custom resource will cause another node to be added @@ -15,10 +20,36 @@ def remote_task(): def test_task(): return ray.get(remote_task.remote()) +@task +def anyscale_job(args): + job_config = { + "name": "my-anyscale-job", + "cloud": "anyscale_v2_default_cloud", + "description": "An Anyscale Job submitted from Prefect.", + "cluster_env": "default_cluster_env_2.3.1_py39", + "runtime_env": { + "working_dir": "ci/", + "upload_path": "s3://anyscale-prefect-integration-test/working-dir/", + }, + "entrypoint": "python anyscale_job.py " + " ".join([f"--{key} {val}" for key, val in args.items()]), + } + + with tempfile.NamedTemporaryFile(mode="w") as f: + yaml.dump(job_config, f) + f.flush() + # Submit an Anyscale Job from Prefect and record the logs + output = subprocess.check_output( + ["anyscale", "job", "submit", f.name, "--follow"] + ) + logger = get_run_logger() + logger.info("Anyscale Job output: " + output.decode()) + @flow(task_runner=RayTaskRunner) def complex_flow(): result = test_task.submit() assert result.result() == 42 + result = anyscale_job.submit({"arg": "value"}) + assert result.result() == None if __name__ == "__main__": complex_flow() diff --git a/ci/submit_prefect_run_and_check.py b/ci/submit_prefect_run_and_check.py index 8fb8a7b..0422785 100644 --- a/ci/submit_prefect_run_and_check.py +++ b/ci/submit_prefect_run_and_check.py @@ -20,7 +20,7 @@ work_queue_name=args.queue, storage=S3.load("test-storage-github"), infrastructure=AnyscaleJob.load("anyscale-job-infra"), - infra_overrides={"compute_config": "test-compute-config"}, + infra_overrides={"compute_config": "complex-test-compute-config"}, ) deployment.apply()