Python脚本如何生成Prefect配置

wen 实用脚本 24

本文目录导读:

Python脚本如何生成Prefect配置

  1. 基础配置生成
  2. 动态生成部署配置
  3. 使用配置文件生成器
  4. 使用模板引擎生成配置
  5. 完整的配置生成脚本
  6. 使用示例

我来详细介绍如何使用Python脚本生成Prefect配置。

基础配置生成

使用Python字典创建配置

from prefect import flow, task
from prefect.client.schemas.schedules import CronSchedule
from prefect.deployments import Deployment
import json
import yaml
from pathlib import Path
# 创建基础配置
def create_prefect_config():
    """生成Prefect基础配置"""
    config = {
        "profiles": {
            "default": {
                "settings": {
                    "PREFECT_API_URL": "http://localhost:4200/api",
                    "PREFECT_LOGGING_LEVEL": "INFO",
                    "PREFECT_AGENT_QUERY_INTERVAL": 5.0
                }
            },
            "production": {
                "settings": {
                    "PREFECT_API_URL": "https://api.prefect.cloud/api",
                    "PREFECT_LOGGING_LEVEL": "WARNING",
                    "PREFECT_AGENT_QUERY_INTERVAL": 10.0,
                    "PREFECT_API_KEY": "${PREFECT_API_KEY}"
                }
            }
        },
        "logging": {
            "level": "INFO",
            "format": "%(asctime)s - %(name)s - %(levelname)s - %(message)s",
            "handlers": {
                "console": {
                    "class": "rich.logging.RichHandler"
                },
                "file": {
                    "class": "logging.handlers.RotatingFileHandler",
                    "filename": "prefect.log",
                    "maxBytes": 10485760,
                    "backupCount": 5
                }
            }
        }
    }
    return config
# 保存为JSON配置文件
def save_config_as_json(config, filepath="prefect.json"):
    with open(filepath, 'w') as f:
        json.dump(config, f, indent=2)
    print(f"Configuration saved to {filepath}")
# 保存为YAML配置文件
def save_config_as_yaml(config, filepath="prefect.yaml"):
    with open(filepath, 'w') as f:
        yaml.dump(config, f, default_flow_style=False)
    print(f"Configuration saved to {filepath}")

动态生成部署配置

from prefect.deployments import Deployment
from prefect.filesystems import GitHub, S3, LocalFileSystem
from prefect.infrastructure import DockerContainer, Process, KubernetesJob
def create_deployment_config(flow_name, flow_path):
    """生成部署配置"""
    # 配置存储
    storage_config = {
        "github": {
            "repository": "https://github.com/your/repo",
            "reference": "main",
            "access_token": "${GITHUB_TOKEN}"
        },
        "s3": {
            "bucket": "my-prefect-bucket",
            "aws_access_key_id": "${AWS_ACCESS_KEY_ID}",
            "aws_secret_access_key": "${AWS_SECRET_ACCESS_KEY}"
        },
        "local": {
            "basepath": "/path/to/flows"
        }
    }
    # 配置基础设施
    infrastructure_config = {
        "docker": {
            "image": "python:3.9",
            "env": {
                "PREFECT_API_URL": "http://prefect-server:4200/api"
            },
            "volumes": ["/local/path:/container/path"],
            "labels": {"app": "prefect-worker"}
        },
        "kubernetes": {
            "customizations": {
                "resources": {
                    "requests": {"cpu": "500m", "memory": "512Mi"},
                    "limits": {"cpu": "1000m", "memory": "1Gi"}
                },
                "env_from": [{"config_map_ref": {"name": "prefect-config"}}]
            }
        },
        "process": {
            "working_dir": "/opt/prefect/flows",
            "stream_output": True
        }
    }
    return {
        "flow_name": flow_name,
        "flow_path": flow_path,
        "parameters": {},
        "storage": storage_config,
        "infrastructure": infrastructure_config,
        "schedule": create_schedule_config()
    }
def create_schedule_config():
    """生成调度配置"""
    from datetime import datetime, timedelta
    from prefect.client.schemas.schedules import (
        CronSchedule,
        IntervalSchedule,
        RRuleSchedule
    )
    schedules = {
        "daily_midnight": {
            "type": "cron",
            "cron": "0 0 * * *",
            "timezone": "Asia/Shanghai"
        },
        "every_hour": {
            "type": "interval",
            "interval": timedelta(hours=1),
            "anchor_date": datetime.now()
        },
        "weekdays_9am": {
            "type": "rrule",
            "rrule": "FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR;BYHOUR=9;BYMINUTE=0"
        }
    }
    return schedules

使用配置文件生成器

class PrefectConfigGenerator:
    """Prefect配置生成器"""
    def __init__(self):
        self.config = {
            "profiles": {},
            "logging": {},
            "workers": [],
            "deployments": []
        }
    def add_profile(self, name, settings):
        """添加配置文件"""
        if "profiles" not in self.config:
            self.config["profiles"] = {}
        self.config["profiles"][name] = {"settings": settings}
        return self
    def add_worker(self, name, worker_type="process", pools=None, **kwargs):
        """添加工作者配置"""
        worker_config = {
            "name": name,
            "type": worker_type,
            "pools": pools or ["default"],
            "settings": kwargs
        }
        self.config.setdefault("workers", []).append(worker_config)
        return self
    def add_deployment(self, flow_path, **kwargs):
        """添加部署配置"""
        deployment = {
            "flow_path": flow_path,
            **kwargs
        }
        self.config.setdefault("deployments", []).append(deployment)
        return self
    def set_logging(self, **kwargs):
        """设置日志配置"""
        self.config["logging"] = kwargs
        return self
    def add_environment_variables(self, env_vars):
        """添加环境变量"""
        self.config.setdefault("environment", {}).update(env_vars)
        return self
    def generate_yaml(self, filepath="generated_prefect.yaml"):
        """生成YAML配置文件"""
        import yaml
        # 自定义YAML输出格式
        class CustomDumper(yaml.SafeDumper):
            pass
        def str_representer(dumper, data):
            if '\n' in data:
                return dumper.represent_scalar('tag:yaml.org,2002:str', data, style='|')
            return dumper.represent_scalar('tag:yaml.org,2002:str', data)
        CustomDumper.add_representer(str, str_representer)
        with open(filepath, 'w') as f:
            yaml.dump(self.config, f, Dumper=CustomDumper, default_flow_style=False, sort_keys=False)
        return filepath
    def generate_json(self, filepath="generated_prefect.json"):
        """生成JSON配置文件"""
        import json
        with open(filepath, 'w') as f:
            json.dump(self.config, f, indent=2)
        return filepath
# 使用示例
def example_usage():
    generator = PrefectConfigGenerator()
    # 添加配置
    config = (generator
        .add_profile("production", {
            "PREFECT_API_URL": "http://prefect-server:4200/api",
            "PREFECT_LOGGING_LEVEL": "INFO"
        })
        .add_profile("development", {
            "PREFECT_API_URL": "http://localhost:4200/api",
            "PREFECT_LOGGING_LEVEL": "DEBUG"
        })
        .add_worker("worker-1", worker_type="process", pools=["pool-1"], 
                    work_queues=["queue-1", "queue-2"],
                    prefetch_seconds=10)
        .add_worker("worker-2", worker_type="docker", 
                    image="prefecthq/prefect:2-python3.9",
                    networks=["prefect-network"])
        .add_deployment(
            flow_path="./flows/my_flow.py",
            name="my-flow-deployment",
            tags=["production", "critical"],
            description="Main data processing flow",
            schedule={
                "cron": "0 */6 * * *",
                "timezone": "UTC"
            },
            parameters={
                "batch_size": 1000,
                "max_retries": 3
            }
        )
        .set_logging(level="INFO", format="%(asctime)s - %(name)s - %(levelname)s")
        .add_environment_variables({
            "DATABASE_URL": "${DATABASE_URL}",
            "REDIS_URL": "${REDIS_URL}"
        })
    )
    # 生成配置文件
    yaml_file = config.generate_yaml("production_prefect.yaml")
    json_file = config.generate_json("production_prefect.json")
    return yaml_file, json_file

使用模板引擎生成配置

from jinja2 import Environment, FileSystemLoader
from typing import Dict, Any
import os
class TemplateConfigGenerator:
    """使用Jinja2模板生成配置"""
    def __init__(self, template_dir="templates"):
        self.env = Environment(
            loader=FileSystemLoader(template_dir),
            trim_blocks=True,
            lstrip_blocks=True
        )
    def generate_from_template(self, template_name: str, context: Dict[str, Any]) -> str:
        """从模板生成配置"""
        template = self.env.get_template(template_name)
        return template.render(**context)
    def generate_prefect_config(self, environment: str = "development") -> str:
        """生成Prefect配置"""
        context = {
            "environment": environment,
            "api_url": self._get_api_url(environment),
            "logging_level": "DEBUG" if environment == "development" else "WARNING",
            "workers": self._get_workers_config(environment),
            "deployments": self._get_deployments_config(environment),
            "created_at": os.environ.get("BUILD_TIMESTAMP", "unknown")
        }
        return self.generate_from_template("prefect_config.yaml.j2", context)
    def _get_api_url(self, environment: str) -> str:
        urls = {
            "development": "http://localhost:4200/api",
            "staging": "https://staging-api.prefect.local/api",
            "production": "https://api.prefect.cloud/api"
        }
        return urls.get(environment, urls["development"])
    def _get_workers_config(self, environment: str) -> list:
        base_config = {
            "development": [
                {"name": "dev-worker", "type": "process", "pools": ["dev-pool"]}
            ],
            "staging": [
                {"name": "staging-worker-1", "type": "process", "pools": ["staging-pool"]},
                {"name": "staging-worker-2", "type": "docker", "pools": ["staging-pool"]}
            ],
            "production": [
                {"name": "prod-worker-1", "type": "kubernetes", "pools": ["prod-pool"]},
                {"name": "prod-worker-2", "type": "kubernetes", "pools": ["prod-pool"]},
                {"name": "prod-worker-3", "type": "kubernetes", "pools": ["prod-pool"]}
            ]
        }
        return base_config.get(environment, [])
    def _get_deployments_config(self, environment: str) -> list:
        """获取部署配置"""
        return [
            {
                "name": f"data-pipeline-{environment}",
                "flow_path": "./flows/data_pipeline.py",
                "schedule": "0 */6 * * *" if environment == "production" else None,
                "tags": [environment, "data-pipeline"],
                "parameters": {
                    "environment": environment,
                    "batch_size": 10000 if environment == "production" else 100
                }
            }
        ]
# 创建Jinja2模板
def create_prefect_template():
    """创建Prefect配置模板"""
    template_content = """
# Prefect Configuration
# Environment: {{ environment }}
# Generated at: {{ created_at }}
profiles:
  {{ environment }}:
    settings:
      PREFECT_API_URL: {{ api_url }}
      PREFECT_LOGGING_LEVEL: {{ logging_level }}
      PREFECT_AGENT_QUERY_INTERVAL: 10.0
logging:
  level: {{ logging_level }}
  format: "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
  handlers:
    console:
      class: "rich.logging.RichHandler"
    file:
      class: "logging.handlers.RotatingFileHandler"
      filename: "prefect_{{ environment }}.log"
      maxBytes: 10485760
      backupCount: 5
workers:
{% for worker in workers %}
  - name: {{ worker.name }}
    type: {{ worker.type }}
    pools: {{ worker.pools }}
    work_queues:
      - "{{ environment }}-queue"
    prefetch_seconds: 10
    {% if worker.type == "docker" %}
    docker:
      image: "prefecthq/prefect:2-python3.9"
      networks: ["prefect-{{ environment }}"]
    {% endif %}
    {% if worker.type == "kubernetes" %}
    kubernetes:
      namespace: "prefect-{{ environment }}"
      image: "prefecthq/prefect:2-python3.9"
      service_account_name: "prefect-worker"
      resources:
        requests:
          cpu: "500m"
          memory: "512Mi"
        limits:
          cpu: "1000m"
          memory: "1Gi"
    {% endif %}
{% endfor %}
deployments:
{% for deployment in deployments %}
  - name: {{ deployment.name }}
    flow_path: {{ deployment.flow_path }}
    {% if deployment.schedule %}
    schedule:
      cron: {{ deployment.schedule }}
      timezone: "UTC"
    {% endif %}
    tags: {{ deployment.tags }}
    parameters:
      {% for key, value in deployment.parameters.items() %}
      {{ key }}: {{ value if not value is string else '"' + value + '"' }}
      {% endfor %}
    apply_to:
      tags: {{ deployment.tags }}
{% endfor %}
environment:
  PREFECT_SERVER_HOST: "0.0.0.0"
  PREFECT_SERVER_PORT: 4200
  PREFECT_UI_ENABLED: "true"
  """
    # 保存模板
    os.makedirs("templates", exist_ok=True)
    with open("templates/prefect_config.yaml.j2", "w") as f:
        f.write(template_content)
    print("Template created: templates/prefect_config.yaml.j2")

完整的配置生成脚本

#!/usr/bin/env python3
"""Prefect配置生成器脚本"""
import argparse
import json
import yaml
import os
from pathlib import Path
from typing import Dict, Any
def create_complete_prefect_config():
    """创建完整的Prefect配置"""
    return {
        "profiles": {
            "default": {
                "settings": {
                    "PREFECT_API_URL": "http://localhost:4200/api",
                    "PREFECT_API_KEY": None
                }
            }
        },
        "logging": {
            "level": "INFO",
            "format": "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
        },
        "deployments": [
            {
                "name": "etl-pipeline",
                "flow_path": "./flows/etl.py",
                "parameters": {},
                "schedule": {
                    "cron": "0 0 * * *",
                    "timezone": "UTC"
                },
                "tags": ["production", "etl"],
                "storage": {
                    "type": "local",
                    "basepath": "/opt/prefect/flows"
                },
                "infrastructure": {
                    "type": "process",
                    "env": {
                        "PYTHONPATH": "${PYTHONPATH}:/opt/prefect"
                    },
                    "working_dir": "/opt/prefect"
                }
            }
        ],
        "workers": [
            {
                "name": "worker-1",
                "type": "process",
                "pools": ["default"],
                "work_queues": ["default"],
                "prefetch_seconds": 10
            }
        ],
        "environment": {
            "PREFECT_SERVER_HOST": "0.0.0.0",
            "PREFECT_SERVER_PORT": 4200,
            "PREFECT_UI_ENABLED": "true",
            "PREFECT_API_DATABASE_CONNECTION_URL": "sqlite+aiosqlite:///./prefect.db",
            "PREFECT_AGENT_PREFETCH_SECONDS": 5,
            "PREFECT_LOGGING_TO_API_ENABLED": "true"
        }
    }
def save_config(config: Dict[str, Any], output_format: str = "yaml", output_path: str = None):
    """保存配置到文件"""
    if output_path is None:
        output_path = f"prefect_config.{output_format}"
    if output_format == "yaml":
        with open(output_path, 'w') as f:
            yaml.dump(config, f, default_flow_style=False, sort_keys=False)
    elif output_format == "json":
        with open(output_path, 'w') as f:
            json.dump(config, f, indent=2)
    print(f"Configuration saved to {output_path}")
    return output_path
def main():
    parser = argparse.ArgumentParser(description="Generate Prefect configuration")
    parser.add_argument("--format", choices=["yaml", "json"], default="yaml",
                       help="Output format (default: yaml)")
    parser.add_argument("--output", "-o", help="Output file path")
    parser.add_argument("--env", choices=["development", "staging", "production"],
                       default="development", help="Environment type")
    args = parser.parse_args()
    # 生成配置
    config = create_complete_prefect_config()
    # 根据环境调整配置
    if args.env == "development":
        config["logging"]["level"] = "DEBUG"
        config["profiles"]["default"]["settings"]["PREFECT_API_URL"] = "http://localhost:4200/api"
    elif args.env == "production":
        config["logging"]["level"] = "WARNING"
        config["profiles"]["production"] = {
            "settings": {
                "PREFECT_API_URL": "https://api.prefect.cloud/api",
                "PREFECT_API_KEY": "${PREFECT_API_KEY}"
            }
        }
    # 保存配置
    save_config(config, args.format, args.output)
if __name__ == "__main__":
    main()

使用示例

# 1. 基础配置生成
config = create_prefect_config()
save_config_as_yaml(config, "basic_prefect.yaml")
# 2. 使用配置生成器
generator = PrefectConfigGenerator()
config = (generator
    .add_profile("production", {"PREFECT_API_URL": "http://localhost:4200/api"})
    .add_worker("my-worker", type="process")
    .generate_yaml("generated_config.yaml"))
# 3. 使用命令行
# python generate_prefect_config.py --format yaml --env production
# 4. 使用模板
generator = TemplateConfigGenerator()
config = generator.generate_prefect_config("production")
print(config)

这些方法可以帮助你灵活地生成Prefect配置文件,可以根据具体需求选择合适的方式。

抱歉,评论功能暂时关闭!