blob: 573f9ef27899635e423006ab6e890cd7dd85fccf [file] [log] [blame]
.. Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
.. http://www.apache.org/licenses/LICENSE-2.0
.. Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
Production Guide
================
The following are things to consider when using this Helm chart in a production environment.
Database
--------
It is advised to set up an external database for the Airflow metastore. The default Helm chart deploys a
Postgres database running in a container. For production usage, a database running on a dedicated machine or
leveraging a cloud provider's database service such as AWS RDS is advised. Supported databases and versions
can be found at :doc:`Set up a Database Backend <apache-airflow:howto/set-up-database>`.
First disable the Postgres in Docker container:
.. code-block:: yaml
postgresql:
enabled: false
To provide the database credentials to Airflow, store the credentials in a Kubernetes secret. Note that
special characters in the username/password must be URL encoded.
.. code-block:: bash
kubectl create secret generic mydatabase --from-literal=connection=postgresql://user:pass@host:5432/db
Helm defaults to fetching the value from a secret named ``[RELEASE NAME]-airflow-metadata``, but you can
configure the secret name:
.. code-block:: yaml
data:
metadataSecretName: mydatabase
.. _production-guide:pgbouncer:
PgBouncer
---------
If you are using PostgreSQL as your database, you will likely want to enable `PgBouncer <https://www.pgbouncer.org/>`_ as well.
Airflow can open a lot of database connections due to its distributed nature and using a connection pooler can significantly
reduce the number of open connections on the database.
.. code-block:: yaml
pgbouncer:
enabled: true
Depending on the size of your Airflow instance, you may want to adjust the following as well (defaults are shown):
.. code-block:: yaml
pgbouncer:
# The maximum number of connections to PgBouncer
maxClientConn: 100
# The maximum number of server connections to the metadata database from PgBouncer
metadataPoolSize: 10
# The maximum number of server connections to the result backend database from PgBouncer
resultBackendPoolSize: 5
Webserver Secret Key
--------------------
You should set a static webserver secret key when deploying with this chart as it will help ensure
your Airflow components only restart when necessary.
.. warning::
You should use a different secret key for every instance you run, as this key is used to sign
session cookies and perform other security related functions!
First, generate a strong secret key:
.. code-block:: bash
python3 -c 'import secrets; print(secrets.token_hex(16))'
Now add the secret to your values file:
.. code-block:: yaml
webserverSecretKey: <secret_key>
Alternatively, create a Kubernetes Secret and use ``webserverSecretKeySecretName``:
.. code-block:: yaml
webserverSecretKeySecretName: my-webserver-secret
# where the random key is under `webserver-secret-key` in the k8s Secret
Example to create a Kubernetes Secret from ``kubectl``:
.. code-block:: bash
kubectl create secret generic my-webserver-secret --from-literal="webserver-secret-key=$(python3 -c 'import secrets; print(secrets.token_hex(16))')"
The webserver key is also used to authorize requests to Celery workers when logs are retrieved. The token
generated using the secret key has a short expiry time though - make sure that time on ALL the machines
that you run airflow components on is synchronized (for example using ntpd) otherwise you might get
"forbidden" errors when the logs are accessed.
Extending and customizing Airflow Image
---------------------------------------
The Apache Airflow community, releases Docker Images which are ``reference images`` for Apache Airflow.
However, Airflow has more than 60 community managed providers (installable via extras) and some of the
default extras/providers installed are not used by everyone, sometimes others extras/providers
are needed, sometimes (very often actually) you need to add your own custom dependencies,
packages or even custom providers, or add custom tools and binaries that are needed in
your deployment.
In Kubernetes and Docker terms this means that you need another image with your specific requirements.
This is why you should learn how to build your own ``Docker`` (or more properly ``Container``) image.
Typical scenarios where you would like to use your custom image:
* Adding ``apt`` packages
* Adding ``PyPI`` packages
* Adding binary resources necessary for your deployment
* Adding custom tools needed in your deployment
See `Building the image <https://airflow.apache.org/docs/docker-stack/build.html>`_ for more
details on how you can extend and customize the Airflow image.
Managing DAG Files
------------------
See :doc:`manage-dags-files`.
.. _production-guide:knownhosts:
knownHosts
^^^^^^^^^^
If you are using ``dags.gitSync.sshKeySecret``, you should also set ``dags.gitSync.knownHosts``. Here we will show the process
for GitHub, but the same can be done for any provider:
Grab GitHub's public key:
.. code-block:: bash
ssh-keyscan -t rsa github.com > github_public_key
Next, print the fingerprint for the public key:
.. code-block:: bash
ssh-keygen -lf github_public_key
Compare that output with `GitHub's SSH key fingerprints <https://docs.github.com/en/github/authenticating-to-github/githubs-ssh-key-fingerprints>`_.
They match, right? Good. Now, add the public key to your values. It'll look something like this:
.. code-block:: yaml
dags:
gitSync:
knownHosts: |
github.com ssh-rsa AAAA...FAaQ==
Accessing the Airflow UI
------------------------
How you access the Airflow UI will depend on your environment, however the chart does support various options:
Ingress
^^^^^^^
You can create and configure ``Ingress`` objects. See the :ref:`Ingress chart parameters <parameters:ingress>`.
For more information on ``Ingress``, see the
`Kubernetes Ingress documentation <https://kubernetes.io/docs/concepts/services-networking/ingress/>`_.
LoadBalancer Service
^^^^^^^^^^^^^^^^^^^^
You can change the Service type for the webserver to be ``LoadBalancer``, and set any necessary annotations:
.. code-block:: yaml
webserver:
service: LoadBalancer
annotations: {}
For more information on ``LoadBalancer`` Services, see the `Kubernetes LoadBalancer Service Documentation
<https://kubernetes.io/docs/concepts/services-networking/service/#loadbalancer>`_.
Logging
-------
Depending on your choice of executor, task logs may not work out of the box. All logging choices can be found
at :doc:`manage-logs`.
Metrics
-------
The chart can support sending metrics to an existing StatsD instance or provide a Prometheus endpoint.
Prometheus
^^^^^^^^^^
The metrics endpoint is available at ``svc/{{ .Release.Name }}-statsd:9102/metrics``.
External StatsD
^^^^^^^^^^^^^^^
To use an external StatsD instance:
.. code-block:: yaml
statsd:
enabled: false
config:
metrics: # or 'scheduler' for Airflow 1
statsd_on: true
statsd_host: ...
statsd_port: ...
Datadog
^^^^^^^
If you are using a Datadog agent in your environment, this will enable Airflow to export metrics to the Datadog agent.
.. code-block:: yaml
statsd:
enabled: false
config:
metrics: # or 'scheduler' for Airflow 1
statsd_on: true
statsd_port: 8125
extraEnv: |-
- name: AIRFLOW__METRICS__STATSD_HOST
valueFrom:
fieldRef:
fieldPath: status.hostIP
Celery Backend
--------------
If you are using ``CeleryExecutor`` or ``CeleryKubernetesExecutor``, you can bring your own Celery backend.
By default, the chart will deploy Redis. However, you can use any supported Celery backend instead:
.. code-block:: yaml
redis:
enabled: false
data:
brokerUrl: redis://redis-user:password@redis-host:6379/0
For more information about setting up a Celery broker, refer to the
exhaustive `Celery documentation on the topic <http://docs.celeryproject.org/en/latest/getting-started/>`_.
Security Context Constraints
-----------------------------
A ``Security Context Constraint`` (SCC) is a OpenShift construct that works as a RBAC rule however it targets Pods instead of users.
When defining a SCC, one can control actions and resources a POD can perform or access during startup and runtime.
The SCCs are split into different levels or categories with the ``restricted`` SCC being the default one assigned to Pods.
When deploying Airflow to OpenShift, one can leverage the SCCs and allow the Pods to start containers utilizing the ``anyuid`` SCC.
In order to enable the usage of SCCs, one must set the parameter :ref:`rbac.createSCCRoleBinding <parameters:Kubernetes>` to ``true`` as shown below:
.. code-block:: yaml
rbac:
create: true
createSCCRoleBinding: true
In this chart, SCCs are bound to the Pods via RoleBindings meaning that the option ``rbac.create`` must also be set to ``true`` in order to fully enable the SCC usage.
For more information about SCCs and what can be achieved with this construct, please refer to `Managing security context constraints <https://docs.openshift.com/container-platform/latest/authentication/managing-security-context-constraints.html#scc-prioritization_configuring-internal-oauth/>`_.
Security Context
----------------
In Kubernetes a ``securityContext`` can be used to define user ids, group ids and capabilities such as running a container in privileged mode.
When deploying an application to Kubernetes, it is recommended to give the least privilege to containers so as
to reduce access and protect the host where the container is running.
In the Airflow Helm chart, the ``securityContext`` can be configured in several ways:
* :ref:`uid <parameters:Airflow>` (configures the global uid or RunAsUser)
* :ref:`gid <parameters:Airflow>` (configures the global gid or fsGroup)
* :ref:`securityContext <parameters:Kubernetes>` (same as ``uid`` but allows for setting all `Pod securityContext options <https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.18/#podsecuritycontext-v1-core>`_)
The same way one can configure the global :ref:`securityContext <parameters:Kubernetes>`, it is also possible to configure different values for specific workloads by setting their local ``securityContext`` as follows:
.. code-block:: yaml
workers:
securityContext:
runAsUser: 5000
fsGroup: 0
In the example above, the workers Pod ``securityContext`` will be set to ``runAsUser: 5000`` and ``runAsGroup: 0``.
As one can see, the local setting will take precedence over the global setting when defined. The following explains the precedence rule for ``securityContext`` options in this chart:
.. code-block:: yaml
uid: 40000
gid: 0
securityContext:
runAsUser: 50000
fsGroup: 0
workers:
securityContext:
runAsUser: 1001
fsGroup: 0
This will generate the following worker deployment:
.. code-block:: yaml
kind: StatefulSet
apiVersion: apps/v1
metadata:
name: airflow-worker
spec:
serviceName: airflow-worker
template:
spec:
securityContext: # As the securityContext was defined in ``workers``, its value will take priority
runAsUser: 1001
fsGroup: 0
If we remove both the ``securityContext`` and ``workers.securityContext`` from the example above, the output will be the following:
.. code-block:: yaml
uid: 40000
gid: 0
securityContext: {}
workers:
securityContext: {}
This will generate the following worker deployment:
.. code-block:: yaml
kind: StatefulSet
apiVersion: apps/v1
metadata:
name: airflow-worker
spec:
serviceName: airflow-worker
template:
spec:
securityContext:
runAsUser: 40000 # As the securityContext was not defined in ``workers`` or ``podSecurity``, the value from uid will be used
fsGroup: 0 # As the securityContext was not defined in ``workers`` or ``podSecurity``, the value from gid will be used
initContainers:
- name: wait-for-airflow-migrations
...
containers:
- name: worker
...
And finally if we set ``securityContext`` but not ``workers.securityContext``:
.. code-block:: yaml
uid: 40000
gid: 0
securityContext:
runAsUser: 50000
fsGroup: 0
workers:
securityContext: {}
This will generate the following worker deployment:
.. code-block:: yaml
kind: StatefulSet
apiVersion: apps/v1
metadata:
name: airflow-worker
spec:
serviceName: airflow-worker
template:
spec:
securityContext: # As the securityContext was not defined in ``workers``, the values from securityContext will take priority
runAsUser: 50000
fsGroup: 0
initContainers:
- name: wait-for-airflow-migrations
...
containers:
- name: worker
...
Built-in secrets and environment variables
------------------------------------------
The Helm Chart by default uses Kubernetes Secrets to store secrets that are needed by Airflow.
The contents of those secrets are by default turned into environment variables that are read by
Airflow (some of the environment variables have several variants to support older versions of Airflow).
By default, the secret names are determined from the Release Name used when the Helm Chart is deployed,
but you can also use a different secret to set the variables or disable using secrets
entirely and rely on environment variables (specifically if you want to use ``_CMD`` or ``__SECRET`` variant
of the environment variable.
However, Airflow supports other variants of setting secret configuration - you can specify a system
command to retrieve and automatically rotate the secret (by defining variable with ``_CMD`` suffix) or
to retrieve a variable from secret backed (by defining the variable with ``_SECRET`` suffix).
If the ``<VARIABLE_NAME>>`` is set, it takes precedence over the ``_CMD`` and ``_SECRET`` variant, so
if you want to set one of the ``_CMD`` or ``_SECRET`` variants, you MUST disable the built in
variables retrieved from Kubernetes secrets, by setting ``.Values.enableBuiltInSecretEnvVars.<VARIABLE_NAME>``
to false.
For example in order to use a command to retrieve the DB connection you should (in your ``values.yaml``
file) specify:
.. code-block:: yaml
extraEnv:
AIRFLOW_CONN_AIRFLOW_DB_CMD: "/usr/local/bin/retrieve_connection_url"
enableBuiltInSecretEnvVars:
AIRFLOW_CONN_AIRFLOW_DB: false
Here is the full list of secrets that can be disabled and replaced by ``_CMD`` and ``_SECRET`` variants:
+-------------------------------------------------------+------------------------------------------+--------------------------------------------------+
| Default secret name if secret name not specified | Use a different Kubernetes Secret | Airflow Environment Variable |
+=======================================================+==========================================+==================================================+
| ``<RELEASE_NAME>-airflow-metadata`` | ``.Values.data.metadataSecretName`` | | ``AIRFLOW_CONN_AIRFLOW_DB`` |
| | | | ``AIRFLOW__CORE__SQL_ALCHEMY_CONN`` |
+-------------------------------------------------------+------------------------------------------+--------------------------------------------------+
| ``<RELEASE_NAME>-fernet-key`` | ``.Values.fernetKeySecretName`` | ``AIRFLOW__CORE__FERNET_KEY`` |
+-------------------------------------------------------+------------------------------------------+--------------------------------------------------+
| ``<RELEASE_NAME>-webserver-secret-key`` | ``.Values.webserverSecretKeySecretName`` | ``AIRFLOW__WEBSERVER__SECRET_KEY`` |
+-------------------------------------------------------+------------------------------------------+--------------------------------------------------+
| ``<RELEASE_NAME>-airflow-result-backend`` | ``.Values.data.resultBackendSecretName`` | | ``AIRFLOW__CELERY__CELERY_RESULT_BACKEND`` |
| | | | ``AIRFLOW__CELERY__RESULT_BACKEND`` |
+-------------------------------------------------------+------------------------------------------+--------------------------------------------------+
| ``<RELEASE_NAME>-airflow-brokerUrl`` | ``.Values.data.brokerUrlSecretName`` | ``AIRFLOW__CELERY__BROKER_URL`` |
+-------------------------------------------------------+------------------------------------------+--------------------------------------------------+
| ``<RELEASE_NAME>-elasticsearch`` | ``.Values.elasticsearch.secretName`` | | ``AIRFLOW__ELASTICSEARCH__HOST`` |
| | | | ``AIRFLOW__ELASTICSEARCH__ELASTICSEARCH_HOST`` |
+-------------------------------------------------------+------------------------------------------+--------------------------------------------------+
There are also a number of secrets, which names are also determined from the release name, that do not need to
be disabled. This is because either they do not follow the ``_CMD`` or ``_SECRET`` pattern, are variables
which do not start with ``AIRFLOW__``, or they do not have a corresponding variable.
There is also one ``_AIRFLOW__*`` variable, ``AIRFLOW__CELERY__FLOWER_BASIC_AUTH``, that does not need to be disabled,
even if you want set the ``_CMD`` and ``_SECRET`` variant. This variable is not set by default. It is only set
when ``.Values.flower.secretName`` is set or when ``.Values.flower.user`` and ``.Values.flower.password``
are set. So if you do not set any of the ``.Values.flower.*`` variables, you can freely configure
flower Basic Auth using the ``_CMD`` or ``_SECRET`` variant without disabling the basic variant.
+-------------------------------------------------------+------------------------------------------+------------------------------------------------+
| Default secret name if secret name not specified | Use a different Kubernetes Secret | Airflow Environment Variable |
+=======================================================+==========================================+================================================+
| ``<RELEASE_NAME>-redis-password`` | ``.Values.redis.passwordSecretName`` | ``REDIS_PASSWORD`` |
+-------------------------------------------------------+------------------------------------------+------------------------------------------------+
| ``<RELEASE_NAME>-pgbouncer-config`` | ``.Values.pgbouncer.configSecretName`` | |
+-------------------------------------------------------+------------------------------------------+------------------------------------------------+
| ``<RELEASE_NAME>-pgbouncer-certificates`` | | |
+-------------------------------------------------------+------------------------------------------+------------------------------------------------+
| ``<RELEASE_NAME>-registry`` | ``.Values.registry.secretName`` | |
+-------------------------------------------------------+------------------------------------------+------------------------------------------------+
| ``<RELEASE_NAME>-kerberos-keytab`` | | |
+-------------------------------------------------------+------------------------------------------+------------------------------------------------+
| ``<RELEASE_NAME>-flower`` | ``.Values.flower.secretName`` | ``AIRFLOW__CELERY__FLOWER_BASIC_AUTH`` |
+-------------------------------------------------------+------------------------------------------+------------------------------------------------+
You can read more about advanced ways of setting configuration variables in the
:doc:`apache-airflow:howto/set-config`.