setup_deploy.py 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290
  1. import os
  2. import sys
  3. import getpass
  4. import subprocess
  5. def clear_screen():
  6. os.system('cls' if os.name == 'nt' else 'clear')
  7. print("="*60)
  8. print(" Local Food AI - Distributed Deployment Configuration Tool")
  9. print("="*60)
  10. # Check Docker availability
  11. try:
  12. subprocess.run(["docker", "info"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, check=True)
  13. print("[+] Docker is correctly configured and accessible.")
  14. except (subprocess.CalledProcessError, FileNotFoundError):
  15. print("[-] Warning: Docker is not running or not accessible. Please ensure Docker Desktop or Docker Engine is installed and running before deploying.")
  16. print("\nSelect the role for this specific node in the network:")
  17. print("1. All-in-One (Runs everything, default)")
  18. print("2. Application Frontend (Runs Streamlit, Nginx, AI Services)")
  19. print("3. Database Node (Runs MySQL & Ingestion)")
  20. print("4. Monitoring Node (Runs Zabbix Server & UI)")
  21. choice = input("\nEnter choice (1-4) [1]: ").strip() or "1"
  22. # Environment Variables
  23. env_vars = {}
  24. if choice != "1":
  25. print("\n--- Network Configuration ---")
  26. if choice != "3":
  27. env_vars['DB_HOST'] = input("Enter the IP address of the Database Node: ").strip()
  28. else:
  29. env_vars['DB_HOST'] = "mysql"
  30. if choice != "4":
  31. env_vars['ZBX_SERVER_HOST'] = input("Enter the IP address of the Monitoring Node: ").strip()
  32. else:
  33. env_vars['ZBX_SERVER_HOST'] = "zabbix-server"
  34. else:
  35. env_vars['DB_HOST'] = "mysql"
  36. env_vars['ZBX_SERVER_HOST'] = "zabbix-server"
  37. print("\n--- Security Configuration ---")
  38. env_vars['MYSQL_ROOT_PASSWORD'] = getpass.getpass("Enter MySQL Root Password (will not echo): ")
  39. env_vars['DB_READER_PASS'] = getpass.getpass("Enter DB Reader Password: ")
  40. env_vars['DB_LOADER_PASS'] = getpass.getpass("Enter DB Loader Password: ")
  41. env_vars['DB_APP_AUTH_PASS'] = getpass.getpass("Enter App Auth Password: ")
  42. env_vars['MYSQL_ZABBIX_PASSWORD'] = getpass.getpass("Enter Zabbix DB Password: ")
  43. # Generate .env
  44. print("\n[+] Generating .env file...")
  45. with open(".env", "w") as f:
  46. for k, v in env_vars.items():
  47. f.write(f"{k}={v}\n")
  48. # Base compose dictionaries
  49. compose_services = {}
  50. mysql_service = """
  51. mysql:
  52. build:
  53. context: ./docker/mysql
  54. ports:
  55. - "3307:3306"
  56. volumes:
  57. - mysql_data:/var/lib/mysql
  58. - ./my.cnf:/etc/mysql/conf.d/custom_ai_app.cnf
  59. - ./init.sql:/docker-entrypoint-initdb.d/1-init.sql
  60. environment:
  61. - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}
  62. healthcheck:
  63. test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
  64. interval: 10s
  65. timeout: 5s
  66. retries: 20
  67. restart: always
  68. logging:
  69. driver: "json-file"
  70. options:
  71. max-size: "50m"
  72. max-file: "3"
  73. """
  74. ingest_service = """
  75. ingest:
  76. build:
  77. context: .
  78. dockerfile: docker/ingest/Dockerfile
  79. environment:
  80. - DB_HOST=${DB_HOST}
  81. - DB_USER=food_loader
  82. - DB_PASS=${DB_LOADER_PASS}
  83. volumes:
  84. - ./:/app
  85. profiles:
  86. - manual
  87. """
  88. ai_services = """
  89. ollama:
  90. image: ollama/ollama:latest
  91. volumes:
  92. - ollama_data:/root/.ollama
  93. restart: always
  94. logging:
  95. driver: "json-file"
  96. options:
  97. max-size: "50m"
  98. max-file: "3"
  99. searxng:
  100. image: searxng/searxng:latest
  101. ports:
  102. - "8080:8080"
  103. volumes:
  104. - ./searxng:/etc/searxng
  105. environment:
  106. - SEARXNG_BASE_URL=http://localhost:8080/
  107. restart: always
  108. logging:
  109. driver: "json-file"
  110. options:
  111. max-size: "50m"
  112. max-file: "3"
  113. """
  114. app_service = """
  115. app:
  116. build:
  117. context: .
  118. dockerfile: docker/app/Dockerfile
  119. ports:
  120. - "8502:8501"
  121. environment:
  122. - DB_HOST=${DB_HOST}
  123. - DB_USER=food_reader
  124. - DB_PASS=${DB_READER_PASS}
  125. - APP_AUTH_USER=food_app_auth
  126. - APP_AUTH_PASS=${DB_APP_AUTH_PASS}
  127. - OLLAMA_HOST=http://ollama:11434
  128. - SEARXNG_HOST=http://searxng:8080
  129. restart: always
  130. logging:
  131. driver: "json-file"
  132. options:
  133. max-size: "50m"
  134. max-file: "3"
  135. nginx:
  136. image: nginx:latest
  137. ports:
  138. - "80:80"
  139. volumes:
  140. - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
  141. restart: always
  142. logging:
  143. driver: "json-file"
  144. options:
  145. max-size: "50m"
  146. max-file: "3"
  147. """
  148. monitoring_services = """
  149. zabbix-server:
  150. image: zabbix/zabbix-server-mysql:ubuntu-7.0-latest
  151. environment:
  152. - DB_SERVER_HOST=${DB_HOST}
  153. - MYSQL_USER=zabbix
  154. - MYSQL_PASSWORD=${MYSQL_ZABBIX_PASSWORD}
  155. - ZBX_SNMPTRAPPER=1
  156. restart: always
  157. logging:
  158. driver: "json-file"
  159. options:
  160. max-size: "50m"
  161. max-file: "3"
  162. ports:
  163. - "10051:10051"
  164. zabbix-web:
  165. image: zabbix/zabbix-web-nginx-mysql:ubuntu-7.0-latest
  166. ports:
  167. - "8081:8080"
  168. - "8444:8443"
  169. environment:
  170. - DB_SERVER_HOST=${DB_HOST}
  171. - MYSQL_USER=zabbix
  172. - MYSQL_PASSWORD=${MYSQL_ZABBIX_PASSWORD}
  173. - ZBX_SERVER_HOST=zabbix-server
  174. - PHP_TZ=Europe/Paris
  175. restart: always
  176. logging:
  177. driver: "json-file"
  178. options:
  179. max-size: "50m"
  180. max-file: "3"
  181. zabbix-agent:
  182. image: zabbix/zabbix-agent:ubuntu-7.0-latest
  183. environment:
  184. - ZBX_HOSTNAME=DistributedNode
  185. - ZBX_SERVER_HOST=${ZBX_SERVER_HOST}
  186. privileged: true
  187. pid: "host"
  188. volumes:
  189. - /var/run:/var/run
  190. restart: always
  191. logging:
  192. driver: "json-file"
  193. options:
  194. max-size: "50m"
  195. max-file: "3"
  196. """
  197. airflow_services = """
  198. airflow-webserver:
  199. image: apache/airflow:2.8.1
  200. environment:
  201. - AIRFLOW__CORE__EXECUTOR=SequentialExecutor
  202. - AIRFLOW__DATABASE__SQL_ALCHEMY_CONN=sqlite:////opt/airflow/airflow.db
  203. - AIRFLOW__CORE__LOAD_EXAMPLES=False
  204. ports:
  205. - "8082:8080"
  206. volumes:
  207. - ./dags:/opt/airflow/dags
  208. - ./logs:/opt/airflow/logs
  209. - ./data:/opt/airflow/data
  210. - /var/run/docker.sock:/var/run/docker.sock
  211. command: webserver
  212. restart: always
  213. logging:
  214. driver: "json-file"
  215. options:
  216. max-size: "50m"
  217. max-file: "3"
  218. airflow-scheduler:
  219. image: apache/airflow:2.8.1
  220. environment:
  221. - AIRFLOW__CORE__EXECUTOR=SequentialExecutor
  222. - AIRFLOW__DATABASE__SQL_ALCHEMY_CONN=sqlite:////opt/airflow/airflow.db
  223. - AIRFLOW__CORE__LOAD_EXAMPLES=False
  224. volumes:
  225. - ./dags:/opt/airflow/dags
  226. - ./logs:/opt/airflow/logs
  227. - ./data:/opt/airflow/data
  228. - /var/run/docker.sock:/var/run/docker.sock
  229. command: bash -c "airflow db migrate && airflow users create --role Admin --username admin --email admin --firstname admin --lastname admin --password admin && airflow scheduler"
  230. restart: always
  231. logging:
  232. driver: "json-file"
  233. options:
  234. max-size: "50m"
  235. max-file: "3"
  236. """
  237. header = "services:\n"
  238. footer = """
  239. volumes:
  240. mysql_data:
  241. ollama_data:
  242. """
  243. compose_content = header
  244. if choice == "1":
  245. compose_content += mysql_service + ingest_service + ai_services + app_service + monitoring_services + airflow_services
  246. elif choice == "2":
  247. compose_content += ai_services + app_service
  248. footer = "volumes:\n ollama_data:\n"
  249. elif choice == "3":
  250. compose_content += mysql_service + ingest_service + airflow_services
  251. footer = "volumes:\n mysql_data:\n"
  252. elif choice == "4":
  253. compose_content += monitoring_services
  254. footer = ""
  255. print("\n[+] Generating docker-compose.yml for selected role...")
  256. with open("docker-compose.yml", "w") as f:
  257. f.write(compose_content + footer)
  258. print("\n" + "="*60)
  259. print("⚠️ IMPORTANT HYPERVISOR NETWORKING REMINDER:")
  260. print("If this node is running inside VirtualBox or Hyper-V, you MUST configure the VM network adapter to use a 'Bridged Adapter' or 'External Virtual Switch' so it shares the host's subnet. Otherwise, cross-node communication will fail.")
  261. print("="*60)
  262. print("\nDone! You can now run `docker compose up -d`.")