1
0

setup_deploy.py 7.9 KB

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