qbittorrentvpn.yaml 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. ---
  2. apiVersion: apps/v1
  3. kind: Deployment
  4. metadata:
  5. name: qbittorrentvpn
  6. namespace: plex
  7. spec:
  8. strategy:
  9. type: Recreate
  10. selector:
  11. matchLabels:
  12. app: qbittorrentvpn
  13. replicas: 1
  14. template:
  15. metadata:
  16. labels:
  17. app: qbittorrentvpn
  18. annotations:
  19. backup.velero.io/backup-volumes-excludes: seedbox,media,media2,data-ec,scratch
  20. spec:
  21. affinity:
  22. nodeAffinity:
  23. requiredDuringSchedulingIgnoredDuringExecution:
  24. nodeSelectorTerms:
  25. - matchExpressions:
  26. - key: seedbox
  27. operator: In
  28. values:
  29. - "true"
  30. containers:
  31. - name: qbittorrentvpn
  32. image: binhex/arch-qbittorrentvpn:5.1.4-1-01
  33. ports:
  34. - containerPort: 8080
  35. name: http-web-svc
  36. securityContext:
  37. privileged: true
  38. envFrom:
  39. - secretRef:
  40. name: qbittorrentvpn-secret
  41. livenessProbe:
  42. exec:
  43. command: ["curl", "--fail", "localhost:8080"]
  44. volumeMounts:
  45. - mountPath: "/media"
  46. name: media
  47. - mountPath: "/media2"
  48. name: media2
  49. - mountPath: "/dataec"
  50. name: data-ec
  51. - mountPath: "/config"
  52. name: config
  53. - mountPath: "/scratch"
  54. name: seedbox
  55. resources:
  56. requests:
  57. memory: "0"
  58. limits:
  59. memory: "4.5Gi"
  60. volumes:
  61. - name: media
  62. persistentVolumeClaim:
  63. claimName: plex-pvc
  64. - name: media2
  65. persistentVolumeClaim:
  66. claimName: media2-pvc
  67. - name: data-ec
  68. persistentVolumeClaim:
  69. claimName: data-ec-pvc
  70. - name: config
  71. persistentVolumeClaim:
  72. claimName: qbittorrentvpn-pvc
  73. - name: seedbox
  74. hostPath:
  75. path: /seedbox/torrents
  76. type: Directory
  77. ---
  78. apiVersion: apps/v1
  79. kind: Deployment
  80. metadata:
  81. name: qbittorrentvpn-exporter
  82. namespace: plex
  83. spec:
  84. strategy:
  85. type: Recreate
  86. selector:
  87. matchLabels:
  88. app: qbittorrentvpn-exporter
  89. replicas: 1
  90. template:
  91. metadata:
  92. labels:
  93. app: qbittorrentvpn-exporter
  94. spec:
  95. containers:
  96. - name: qbittorrentvpn-exporter
  97. image: ghcr.io/esanchezm/prometheus-qbittorrent-exporter:latest
  98. ports:
  99. - containerPort: 8000
  100. name: metrics
  101. envFrom:
  102. - secretRef:
  103. name: qbittorrentvpn-exporter-secret
  104. livenessProbe:
  105. exec:
  106. command:
  107. - "/bin/sh"
  108. - "-c"
  109. - 'wget -O - 0.0.0.0:8000 | grep -E "qbittorrent_up\{.* 1.0"'
  110. initialDelaySeconds: 3
  111. timeoutSeconds: 5
  112. periodSeconds: 3
  113. resources:
  114. requests:
  115. memory: "0"
  116. limits:
  117. memory: "256Mi"
  118. ---
  119. apiVersion: v1
  120. kind: Service
  121. metadata:
  122. name: qbittorrentvpn-service
  123. namespace: plex
  124. spec:
  125. selector:
  126. app: qbittorrentvpn
  127. type: ClusterIP
  128. ports:
  129. - name: qbittorrentvpn-web-port
  130. protocol: TCP
  131. port: 8080
  132. targetPort: http-web-svc
  133. ---
  134. apiVersion: v1
  135. kind: Service
  136. metadata:
  137. name: qbittorrentvpn-exporter-service
  138. namespace: plex
  139. labels:
  140. app: qbittorrentvpn-exporter
  141. spec:
  142. selector:
  143. app: qbittorrentvpn-exporter
  144. type: ClusterIP
  145. ports:
  146. - name: metrics
  147. protocol: TCP
  148. port: 8000
  149. targetPort: metrics
  150. ---
  151. apiVersion: networking.k8s.io/v1
  152. kind: Ingress
  153. metadata:
  154. name: qbittorrentvpn
  155. namespace: plex
  156. annotations:
  157. traefik.ingress.kubernetes.io/router.entrypoints: websecure
  158. traefik.ingress.kubernetes.io/router.middlewares: kube-system-lanonly@kubernetescrd
  159. spec:
  160. rules:
  161. - host: qbittorrentvpn.lan.jibby.org
  162. http:
  163. paths:
  164. - path: /
  165. pathType: Prefix
  166. backend:
  167. service:
  168. name: qbittorrentvpn-service
  169. port:
  170. number: 8080
  171. ---
  172. apiVersion: monitoring.coreos.com/v1
  173. kind: PrometheusRule
  174. metadata:
  175. labels:
  176. prometheus: qbittorrent
  177. role: alert-rules
  178. name: prometheus-qbittorrent-rules
  179. namespace: plex
  180. spec:
  181. groups:
  182. - name: ./qbittorrent.rules
  183. rules:
  184. - alert: QbittorrentErroredTorrents
  185. expr: sum(qbittorrent_torrents_count{status="error"}) > 0
  186. ---
  187. apiVersion: batch/v1
  188. kind: CronJob
  189. metadata:
  190. name: qbittorrentvpn-restart
  191. namespace: plex
  192. spec:
  193. schedule: "*/30 * * * *"
  194. successfulJobsHistoryLimit: 1
  195. failedJobsHistoryLimit: 1
  196. concurrencyPolicy: Forbid
  197. jobTemplate:
  198. spec:
  199. template:
  200. metadata:
  201. labels:
  202. app: qbittorrentvpn-restart
  203. spec:
  204. serviceAccountName: qbittorrentvpn-restart-serviceaccount
  205. securityContext:
  206. runAsUser: 1000
  207. runAsGroup: 1000
  208. restartPolicy: OnFailure
  209. containers:
  210. - name: qbittorrentvpn-restart
  211. image: python:3.14
  212. command:
  213. - python3
  214. - -c
  215. - |
  216. import subprocess
  217. import json
  218. import pprint
  219. import urllib.parse
  220. import sys
  221. import datetime
  222. # Vars to configure
  223. namespace = 'plex'
  224. qparams = {'labelSelector': 'app=qbittorrentvpn'}
  225. max_runtime = datetime.timedelta(days=3)
  226. # serviceaccount/k8s specific vars. Likely don't need to edit these.
  227. serviceaccount_dir = '/var/run/secrets/kubernetes.io/serviceaccount'
  228. apiserver = 'https://kubernetes.default.svc'
  229. token = open(f'{serviceaccount_dir}/token').read()
  230. result = subprocess.run([
  231. 'curl',
  232. '--cacert', f'{serviceaccount_dir}/ca.crt',
  233. '--header', f'Authorization: Bearer {token}',
  234. '-X', 'GET',
  235. f'{apiserver}/api/v1/namespaces/{namespace}/pods?{urllib.parse.urlencode(qparams)}'
  236. ],
  237. capture_output=True,
  238. check=True,
  239. )
  240. pod_list = json.loads(result.stdout)
  241. items = pod_list.get('items')
  242. if items is None or len(items) < 1:
  243. print(f'No pod found? Exiting. {pod_list=}')
  244. sys.exit(1)
  245. if len(items) > 1:
  246. print(f'>1 pod? Exiting. {items=}, {len(items)=}')
  247. sys.exit(1)
  248. pod = items[0]
  249. container_statuses = pod['status']['containerStatuses']
  250. if len(container_statuses) != 1:
  251. print(f'len(containerStatuses) != 1? Exiting. {container_statuses=}')
  252. sys.exit(1)
  253. running = container_statuses[0]['state'].get('running')
  254. if not running:
  255. print(f'Pod not running? Exiting. {container_statuses["state"]=}')
  256. started_at = datetime.datetime.fromisoformat(running["startedAt"])
  257. runtime = datetime.datetime.now(tz=datetime.UTC) - started_at
  258. print(f'{runtime=} > {max_runtime=} ? {runtime > max_runtime}')
  259. if runtime > max_runtime:
  260. pod_name = pod['metadata']['name']
  261. print(f'Deleting pod {pod_name}')
  262. result = subprocess.run([
  263. 'curl',
  264. '--cacert', f'{serviceaccount_dir}/ca.crt',
  265. '--header', f'Authorization: Bearer {token}',
  266. '-X', 'DELETE',
  267. f'{apiserver}/api/v1/namespaces/{namespace}/pods/{pod_name}'
  268. ],
  269. capture_output=True,
  270. check=True,
  271. )
  272. ---
  273. apiVersion: v1
  274. kind: ServiceAccount
  275. metadata:
  276. name: qbittorrentvpn-restart-serviceaccount
  277. namespace: plex
  278. ---
  279. apiVersion: rbac.authorization.k8s.io/v1
  280. kind: RoleBinding
  281. metadata:
  282. name: qbittorrentvpn-restart-serviceaccount-edit
  283. namespace: plex
  284. roleRef:
  285. apiGroup: rbac.authorization.k8s.io
  286. kind: ClusterRole
  287. name: edit
  288. subjects:
  289. - kind: ServiceAccount
  290. name: qbittorrentvpn-restart-serviceaccount
  291. namespace: plex