關於Airflow是什麼, 歡迎參考「🔒 阿Han的軟體心法實戰營 - MLOps」。
當我們架設好Docker環境, 並撰寫一個DAG為Docker Operator的關卡時, 執行後竟然出現以下錯誤:
raise AirflowException("Failed to establish connection to any given Docker hosts.")
airflow.exceptions.AirflowException: Failed to establish connection to any given Docker hosts.
為什麼會這樣呢? 因為容器需要與host的docker溝通才能夠執行其他容器, 這邊有幾個方向如下:
為什麼會有兩個方式呢? 不能選其中一種就好了嗎? 別急…, 我們一一來說明這兩種方式的利與弊, 最後再由您自行選擇適合的一種方法。
我們要安裝一下 apache-airflow-providers-docker 這個套件, 怎麼安裝呢? 官方的docker compose支援環境變數來配置要安裝的套件與版本, 也就是我們可以修改 .env這個檔案, 加入:
_PIP_ADDITIONAL_REQUIREMENTS=apache-airflow-providers-docker==3.13.0
接著我們看一下今日主題的重點:
很簡單的一個範例, 我們透過docker呼叫python的容器, 並執行Hello World!
from airflow import DAG
from airflow.providers.docker.operators.docker import DockerOperator
from airflow.utils.dates import days_ago
default_args = {
'owner': 'airflow',
'start_date': days_ago(1),
}
with DAG(
dag_id='docker_operator_example',
default_args=default_args,
schedule_interval=None,
catchup=False,
) as dag:
run_docker = DockerOperator(
task_id='run_docker',
image='python:3-alpine', # 要運行的 Docker 映像
command='python -c \\'print("Hello, World!")\\'', # 容器中要運行的命令
docker_url='unix://var/run/docker.sock', # Docker daemon 的 URL
network_mode='bridge', # Docker 網絡模式
auto_remove=True, # 在運行結束後自動移除容器
)
run_docker
我們可以在官方範本的volumes掛載 /var/run/docker.sock。
x-airflow-common:
&airflow-common
...
volumes:
...
- /var/run/docker.sock:/var/run/docker.sock:rw
但運行仍卡關。
究竟是為什麼呢? 難道是我們沒有正確掛載進去? 既然心中有🤔疑問, 那我們就進到容器來檢查看看!
cat /var/run/docker.sock
cat: /var/run/docker.sock: Permission denied
啊! 原來是權限問題啊! 那我們檢查一下host的權限。
ls -al /var/run/docker.sock
srw-rw---- 1 root docker 0 八 29 07:26 /var/run/docker.sock
果不其然, 使用者是root而群組歸屬於Docker, 但由於airflow的使用者是本機的使用者, 加上群組是「0」這個群組, 導致無法正常溝通, 針對這樣的問題, 最暴力的方式就是把權限全部打開, 包括其他使用者都能夠存取docker.sock, 但這樣有點危險, 請謹慎使用, 這邊為了實驗我們先暫時打開並測試與執行。
❗ 請務必謹慎使用, 我們僅以實驗性質進行分享, 並不擔保資安責任。
sudo chmod 777 /var/run/docker.sock
我們可以看到打開權限之後, 重新執行DAG就成功了!
我們可以用這套 https://github.com/Tecnativa/docker-socket-proxy 來幫我們做代理, 那麼只要簡單的在docker compose裡面加掛這個組件即可。
只要兩個步驟就能完成:
run_docker = DockerOperator(
task_id='run_docker',
image='python:3-alpine', # 要運行的 Docker 映像
command='python -c \\'print("Hello, World!")\\'', # 容器中要運行的命令
docker_url='TCP://docker-socket-proxy:2375', # Docker daemon 的 URL
network_mode='bridge', # Docker 網絡模式
auto_remove=True, # 在運行結束後自動移除容器
)
✌️✌️✌️ 果然也是成功的!
今天的疑難雜症解方主要是針對我們在Airflow上會面臨到的Docker環境問題, 雖然使用Docker相對於裸機來說較為複雜一些, 但對於後續的維護帶來的效益非常的龐大, 因此我們初期設計時可以多花一些時間深入理解Airflow設計的裡面與使用技巧, 讓我們的自動化產線能夠更加穩定, 關於MLOps的相關知識也歡迎參考「🔒 阿Han的軟體心法實戰營 - MLOps」, 讓我們一起學習成長吧!