--- apiVersion: v1 kind: Namespace metadata: name: fit --- # The whole log is one JSON file, so the "database" is this volume. It is # ReadWriteOnce, which is why the deployment below replaces rather than rolls. apiVersion: v1 kind: PersistentVolumeClaim metadata: name: fit-data-pvc namespace: fit spec: accessModes: - ReadWriteOnce resources: requests: storage: 1Gi --- apiVersion: v1 kind: Service metadata: name: fit-svc namespace: fit spec: selector: app: fit ports: - port: 80 targetPort: 8080 protocol: TCP --- apiVersion: apps/v1 kind: Deployment metadata: name: fit-dep namespace: fit spec: replicas: 1 # Two pods would both hold the file in memory and overwrite each other's # writes, and the volume only attaches to one node anyway. Recreate makes the # old pod let go before the new one starts. strategy: type: Recreate selector: matchLabels: app: fit template: metadata: labels: app: fit spec: imagePullSecrets: - name: harborcred securityContext: runAsNonRoot: true runAsUser: 65534 runAsGroup: 65534 # Without fsGroup the volume comes up owned by root and nobody cannot # write the file it exists to hold. fsGroup: 65534 containers: - name: fit image: harbor.scottyah.com/scottyah/fit:latest imagePullPolicy: Always ports: - containerPort: 8080 env: - name: ADDR value: ":8080" - name: BASE_URL value: "https://fit.scottyah.com" - name: DATA_PATH value: "/data/fit.json" # Every date in the app is a local date, so this decides when the # day rolls over. - name: TZ_NAME value: "America/Los_Angeles" - name: GOMEMLIMIT value: "40MiB" volumeMounts: - name: data mountPath: /data resources: requests: memory: "16Mi" cpu: "10m" limits: memory: "64Mi" cpu: "200m" livenessProbe: httpGet: path: /healthz port: 8080 initialDelaySeconds: 5 periodSeconds: 20 readinessProbe: httpGet: path: /healthz port: 8080 initialDelaySeconds: 2 periodSeconds: 10 securityContext: allowPrivilegeEscalation: false # /data is the only thing the process writes. readOnlyRootFilesystem: true capabilities: drop: ["ALL"] volumes: - name: data persistentVolumeClaim: claimName: fit-data-pvc --- # The app has no login of its own; this is the only thing between the log and # the internet, so the Service must not be exposed any other way. apiVersion: traefik.io/v1alpha1 kind: Middleware metadata: name: fit-basic-auth namespace: fit spec: basicAuth: secret: fit-basic-auth --- apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: fit-ingress namespace: fit annotations: traefik.ingress.kubernetes.io/router.entrypoints: websecure traefik.ingress.kubernetes.io/router.tls: "true" traefik.ingress.kubernetes.io/router.middlewares: fit-fit-basic-auth@kubernetescrd spec: ingressClassName: traefik tls: - hosts: - fit.scottyah.com secretName: scottyah-tls rules: - host: fit.scottyah.com http: paths: - path: / pathType: Prefix backend: service: name: fit-svc port: number: 80