<p>I'm having difficulty understanding which would be best for my situation and how to actually implement it.</p>

<p>In a nutshell, the problem is this:</p>

<ul>
<li>I'm spinning up my DB (Postgres), BE (Django), and FE (React) deployments with Skaffold</li>
<li>About 50% of the time the BE spins up before the DB</li>
<li>One of the first things Django tries to do is connect to the DB</li>
<li>It only tries once (by design and can't be changed), if it can't, it fails and the application is broken</li>
</ul>

<hr>

<ul>
<li><strong>Thus</strong>, I need to make sure every single time I spin up my deployments, the DB deployment is running before starting the BE deployment</li>
</ul>

<p>I came across <a href="https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/" rel="nofollow noreferrer">readiness, liveness, and starup probes</a>. I've read it a couple times and readiness probes sound like what I need: I don't want the BE deployment to start until the DB deployment is ready to accept connections.</p>

<p>I guess I'm not understanding how to set it up. This is what I've tried, but I still run into instances where one is being loaded before another.</p>

<p><strong>postgres.yaml</strong></p>

<pre><code>apiVersion: apps/v1
kind: Deployment
metadata:
  name: postgres-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      component: postgres
  template:
    metadata:
      labels:
        component: postgres
    spec:
      containers:
        - name: postgres
          image: testappcontainers.azurecr.io/postgres
          ports:
            - containerPort: 5432
          env: 
            - name: POSTGRES_DB
              valueFrom:
                secretKeyRef:
                  name: testapp-secrets
                  key: PGDATABASE
            - name: POSTGRES_USER
              valueFrom:
                secretKeyRef:
                  name: testapp-secrets
                  key: PGUSER
            - name: POSTGRES_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: testapp-secrets
                  key: PGPASSWORD
            - name: POSTGRES_INITDB_ARGS
              value: "-A md5"
          volumeMounts:
            - name: postgres-storage
              mountPath: /var/lib/postgresql/data
              subPath: postgres
      volumes:
        - name: postgres-storage
          persistentVolumeClaim:
            claimName: postgres-storage
---
apiVersion: v1
kind: Service
metadata:
  name: postgres-cluster-ip-service
spec:
  type: ClusterIP
  selector:
    component: postgres
  ports:
    - port: 1423
      targetPort: 5432
</code></pre>

<p><strong>api.yaml</strong></p>

<pre><code>apiVersion: apps/v1
kind: Deployment
metadata:
  name: api-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      component: api
  template:
    metadata:
      labels:
        component: api
    spec:
      containers:
        - name: api
          image: testappcontainers.azurecr.io/testapp-api
          ports:
            - containerPort: 5000
          env:
            - name: PGUSER
              valueFrom:
                secretKeyRef:
                  name: testapp-secrets
                  key: PGUSER
            - name: PGHOST
              value: postgres-cluster-ip-service
            - name: PGPORT
              value: "1423"
            - name: PGDATABASE
              valueFrom:
                secretKeyRef:
                  name: testapp-secrets
                  key: PGDATABASE
            - name: PGPASSWORD
              valueFrom:
                secretKeyRef:
                  name: testapp-secrets
                  key: PGPASSWORD
            - name: SECRET_KEY
              valueFrom:
                secretKeyRef:
                  name: testapp-secrets
                  key: SECRET_KEY
            - name: DEBUG
              valueFrom:
                secretKeyRef:
                  name: testapp-secrets
                  key: DEBUG
          readinessProbe:
            httpGet:
              host: postgres-cluster-ip-service
              port: 1423
            initialDelaySeconds: 10
            periodSeconds: 5
            timeoutSeconds: 2
---
apiVersion: v1
kind: Service
metadata:
  name: api-cluster-ip-service
spec:
  type: ClusterIP
  selector:
    component: api
  ports:
    - port: 5000
      targetPort: 5000
</code></pre>

<p><strong>client.yaml</strong></p>

<pre><code>apiVersion: apps/v1
kind: Deployment
metadata:
  name: client-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      component: client
  template:
    metadata:
      labels:
        component: client
    spec:
      containers:
        - name: client
          image: testappcontainers.azurecr.io/testapp-client
          ports:
            - containerPort: 3000
          readinessProbe:
            httpGet:
              path: api-cluster-ip-service
              port: 5000
            initialDelaySeconds: 10
            periodSeconds: 5
            timeoutSeconds: 2
---
apiVersion: v1
kind: Service
metadata:
  name: client-cluster-ip-service
spec:
  type: ClusterIP
  selector:
    component: client
  ports:
    - port: 3000
      targetPort: 3000
</code></pre>

<p>I don't think the <code>ingress.yaml</code> and the <code>skaffold.yaml</code> will be helpful, but let me know if I should add those.</p>

<p>So what am I doing wrong here?</p>

<hr>

<p><strong>Edit:</strong></p>

<p>So I've tried out a few things based on David Maze's response. This helped me understand what is going on better, but I am still running into issues I'm not quite understanding how to resolve.</p>

<p>The first problem is that even with a default <code>restartPolicy: Always</code>, and even though Django fails, the Pods themselves don't fail. The Pods think they are perfectly healthy even though Django has failed.</p>

<p>The second problem is that apparently the Pods need to be made aware of Django's status. That is the part I'm not quite wrapping my brain around, particularly should probes be checking the status of other deployments or themselves? </p>

<p>Yesterday my thinking was the former, but today I'm thinking it is the latter: the Pod needs to know the program contained in it has failed. However, everything I've tried just results in a failed probe, connection refused, etc.:</p>

<pre><code># referring to itself
host: /health
port: 5000

host: /healthz
port: 5000

host: /api
port: 5000

host: /
port: 5000

host: /api-cluster-ip-service
port: 5000

host: /api-deployment
port: 5000

# referring to the DB deployment
host: /health
port: 1423 #or 5432

host: /healthz
port: 1423 #or 5432

host: /api
port: 1423 #or 5432

host: /
port: 1423 #or 5432

host: /postgres-cluster-ip-service
port: 1423 #or 5432

host: /postgres-deployment
port: 1423 #or 5432
</code></pre>

<p>So apparently I'm setting up the probe wrong, despite it being a "super-easy" implementation (as a few blogs have described it). For example, the <code>/health</code> and <code>/healthz</code> routes: are these built into Kubernetes or do these need to be setup? Rereading the docs to hopefully clarify this.</p>

cjones
<p>You're just not waiting long enough.</p>

<p>The deployment artifacts you're showing here look pretty normal.  It's even totally normal for your application to fail fast if it can't reach the database, say because it hasn't started up yet.  Every pod has a <a href="https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy" rel="nofollow noreferrer">restart policy</a>, though, which defaults to <code>Always</code>.  So, when the pod fails, Kubernetes will restart it; and when it fails again, it will get restarted again; and when it keeps failing, Kubernetes will pause tens of seconds between restarts (the dreaded <code>CrashLoopBackOff</code> state).</p>

<p>Eventually if you're in this wait-and-restart loop, the database will actually come up, and then Kubernetes will restart your application pods, at which point the application will start up normally.</p>

<p>The only thing that I'd change here is that your readiness probes for the two pods should probe the services themselves, not some other service.  You probably want the <code>path</code> to be something like <code>/</code> or <code>/healthz</code> or something else that is an actual HTTP request path in the service.  That can return 503 Service Unavailable if it detects its dependency isn't available, or you can just crash.  Just crashing is fine.</p>

<p>This is a totally normal setup in Kubernetes; there's no way to more directly say that pod A can't start until service B is ready.  The flip side of this is that the pattern is actually pretty generic: if your application crashes and restarts whenever it can't reach its database, it doesn't matter if the database is hosted outside the cluster, or if it crashes sometime well after startup time; the same logic will try to restart your application until it works again.</p>
