【问题标题】:Scaling Airflow with a Celery cluster using Docker swarm使用 Docker swarm 通过 Celery 集群扩展 Airflow
【发布时间】:2021-11-26 08:54:46
【问题描述】:

正如标题所说,我想使用 Docker swarm 设置将在集群(1 个主节点,2 个节点)上运行的 Airflow。

当前设置:

现在我有使用在单个 EC2 上运行的 CeleryExecutor 的 Airflow 设置。 我有一个提取 Airflow 图像和pip install -r requirements.txt 的 Dockerfile。 从这个 Dockerfile 我正在创建一个本地图像,这个图像在 docker-compose.yml 中使用,它启动了 Airflow 需要的不同服务(webserver、scheduler、redis、flower 和一些 worker。metadb 是 Postgres,它在一个单独的RDS)。 docker-compose 用于 docker swarm 模式,即。 docker stack deploy . airflow_stack

所需设置:

我想将当前设置扩展到 3 个 EC2(1 个主节点,2 个节点),主节点将运行 Web 服务器、调度、redis 和花,而工作节点将在节点中运行。 在搜索和网络和文档之后,有几件事我仍然不清楚,我很想知道

  1. 据我了解,为了让节点运行工作程序,我从 Dockerfile 构建的本地映像需要推送到某个存储库(如果确实需要,我会使用 AWS ECR)气流工作人员能够从该图像创建容器。对吗?
  2. 正在同步卷和环境文件,现在,我正在安装卷并将环境插入到 docker-compose 文件中。这些挂载和环境是否会同步到节点(和气流工作容器)?如果没有,如何确保一切都是同步的,因为气流要求所有组件(除了 redis)都具有所有依赖项等。
  3. 使用 CeleryExecuter 时需要设置的环境之一是 broker_url,我如何确保节点识别主服务器上的 redis 代理

我确信还有一些我忘记的东西,但我写的是一个好的开始。 任何帮助或建议将不胜感激

谢谢!

Dockerfile:

FROM apache/airflow:2.1.3-python3.9
USER root


RUN apt update;
RUN apt -y install build-essential;

USER airflow
COPY requirements.txt requirements.txt
COPY requirements.airflow.txt requirements.airflow.txt

RUN pip install --upgrade pip;
RUN pip install --upgrade wheel;

RUN pip install -r requirements.airflow.txt
RUN pip install -r requirements.txt


EXPOSE 8793 8786 8787

docker-compose.yml:

version: '3.8'
x-airflow-celery: &airflow-celery
  image: local_image:latest
  volumes:
    -some_volume
  env_file:
    -some_env_file

services:
  webserver:
    <<: *airflow-celery
    command: airflow webserver
    restart: always
    ports:
      - 80:8080
    healthcheck:
      test: [ "CMD-SHELL", "[ -f /opt/airflow/airflow-webserver.pid ]" ]
      interval: 10s
      timeout: 30s
      retries: 3

  scheduler:
    <<: *airflow-celery
    command: airflow scheduler
    restart: always
    deploy:
      replicas: 2

  redis:
    image: redis:6.0
    command: redis-server --include /redis.conf
    healthcheck:
      test: [ "CMD", "redis-cli", "ping" ]
      interval: 30s
      timeout: 10s
      retries: 5
    ports:
      - 6379:6379
    environment:
      - REDIS_PORT=6379

  worker:
    <<: *airflow-celery
    command: airflow celery worker
    deploy:
      replicas: 16

  flower:
    <<: *airflow-celery
    command: airflow celery flower
    ports:
      - 5555:5555

【问题讨论】:

    标签: docker-compose celery airflow docker-swarm


    【解决方案1】:

    听起来你正朝着正确的方向前进(不过最后有一条一般性评论)。

    1. 是的,您需要将镜像推送到容器注册表并通过公共(或私有,如果您进行身份验证)标签引用它。在这种情况下,标签通常是registry/name:tag。例如,您可以在此处看到 Airlfow 的 CI 图像之一:https://github.com/apache/airflow/pkgs/container/airflow%2Fmain%2Fci%2Fpython3.9 - 目的有点不同(我们将其用于 CI 构建)但机制是相同的:您在本地构建它,使用“注册表”标记/image:tag" docker build . --tag registry/image:tag 并运行 docker push registry/image:tag。 然后,每当您通过 docker compose 通过registry/image:tag 引用它时,docker compose/swarm 都会提取正确的图像。只需确保在构建映像时创建唯一的 TAG,以了解您推送的映像(并考虑未来的映像)。

    2. Env 文件应该没问题,它们会分布在各个实例中,但本地安装的卷不会。您要么需要一些共享文件系统(如 NFS,如果您使用 AWS,则可能是 EFS)来存储 DAG,或者使用其他一些同步方法来分发 DAG。例如,它可以是 git-sync - 它具有非常好的属性,特别是如果您使用 Git 存储 DAG 文件,或者将 DAG 烘焙到图像中(这需要在图像更改时重新推送图像)。您可以在我们的 Helm Chart https://airflow.apache.org/docs/helm-chart/stable/manage-dags-files.html

      中看到解释的不同选项
    3. 您不能使用localhost,您需要将其设置为特定主机并确保您的代理 URL 可从所有实例访问。这可以通过将特定的 IP 地址/DNS 名称分配给您的“代理”实例并在防火墙中打开正确的端口(确保您控制可以从哪里访问这些端口)甚至可能采用一些负载平衡来完成。

    我不太了解 DockerSwarm 设置它的难易程度,但最重要的是,手动完成这一切似乎需要大量工作。

    我强烈、非常强烈地鼓励您使用 Kubernetes 和 Airlfow 社区开发的 Helm Chart:https://airflow.apache.org/docs/helm-chart/stable/index.html。在 K8S(扩展、共享文件系统 - PV、网络和连接、资源管理等)或我们的 Helm(Git-Sync 端容器、代理配置等)中解决了很多问题和必要的配置

    【讨论】:

    • 感谢您的回答!关于使用 Kubernetes 和 Helm,我同意这是扩展 Airflow 的最佳方式,但我们的 DevOps 团队目前还没有能力。使用带有 Docker swarm 的 Celery 集群是我可以在对 DevOps 提供最少帮助的情况下做的事情
    • 祝你好运!
    【解决方案2】:

    我在 Docker Swarm 上运行 Airflow CeleryExecutor。

    假设您在节点上设置了 Docker Swarm,您可以执行以下操作:

    1. 像这样将共享卷映射到 NFS 文件夹(对于插件和日志或您需要共享的任何其他内容也是如此)
    volumes:
      dags:
        driver_opts:
          type: "none"
          o: "bind"
          device: "/nfs/airflow/dags"
    
    1. 我个人使用 Docker Secrets 来处理我的 webserver 密码、数据库密码等(类似地,我使用 Docker configs 来传递我的 celery 和 webserver 配置)
    secrets:
      postgresql_password:
        external: true
      fernet_key:
        external: true
      webserver_password:
        external: true
    

    为了让 Airflow 读取这些秘密,我添加了一个简单的 bash 脚本,该脚本被添加到 entrypoint.sh 脚本中。所以在我的堆栈文件中我不需要硬编码任何密码,但是如果 DOCKER-SECRET 字符串可用,那么它将在/run/secrets/ 中查找(我想我在设置它时以它为例https://gist.github.com/bvis/b78c1e0841cfd2437f03e20c1ee059fe

    在我的入口点脚本中,我添加了扩展 Docker Secrets 的脚本:

    source /env_secrets_expand.sh

    x-airflow-variables: &airflow-variables
        AIRFLOW__CORE__EXECUTOR: CeleryExecutor
        ...
        AIRFLOW__WEBSERVER__SECRET_KEY: DOCKER-SECRET->webserver_secret_key
    
    

    这也是 postgres 图像的设置方式,没有环境变量:

    services:
      postgres:
        image: postgres:11.5
        secrets:
          - source: postgresql_password
            target: /run/secrets/postgresql_password
        environment:
          - POSTGRES_USER=airflow
          - POSTGRES_DB=airflow
          - POSTGRES_PASSWORD_FILE=/run/secrets/postgresql_password
    
    1. 您显然可以使用 Swarm 标签或主机名来确定某个服务应该运行哪些节点
      scheduler:
        <<: *airflow-common
        environment: *airflow-variables
        command: scheduler
        deploy:
          replicas: 2
          mode: replicated
          placement:
            constraints:
              - node.labels.type == worker
          restart_policy:
            condition: on-failure
            delay: 5s
            max_attempts: 3
            window: 120s
        logging:
          driver: fluentd
          options:
            tag: docker.airflow.scheduler
            fluentd-async-connect: "true"
    

    对于 Celery 工作人员,我有我的默认队列,然后是一个特殊队列,由于历史原因,该队列被固定到单个节点(客户端已将该特定 IP 地址列入白名单,因此我需要确保任务仅在该节点上运行) .所以我的入口点运行exec airflow celery "$@" -q "$QUEUE_NAME",我的堆栈文件是这样的:

      worker_default:
        <<: *airflow-common
        environment:
          <<: *airflow-variables
          QUEUE_NAME: default
        command: worker
        deploy:
          replicas: 3
          mode: replicated
          placement:
            constraints:
              - node.labels.type == worker
    
      worker_nodename:
        <<: *airflow-common
        environment:
          <<: *airflow-variables
          QUEUE_NAME: nodename
        command: worker
        deploy:
          replicas: 1
          mode: replicated
          placement:
            constraints:
              - node.hostname == nodename
    
    

    每当我合并到 main 时,我都会使用 Gitlab CI/CD 来部署我的 DAG/插件,如果我更新 Dockerfile 或其他某些文件,我会构建图像并部署服务。我已经以这种方式运行 Airflow 几年了(2017 年或 2018 年),但我确实计划最终切换到 Kubernetes,因为这似乎是更标准的方法。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-07-12
      • 2016-11-27
      • 2017-07-10
      • 1970-01-01
      • 2022-01-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多