Skip to content

Containerize Product service (multi-stage Dockerfile, /health & /ready, env config)#79

Open
devin-ai-integration[bot] wants to merge 2 commits into
mainfrom
devin/1783564336-containerize-product
Open

Containerize Product service (multi-stage Dockerfile, /health & /ready, env config)#79
devin-ai-integration[bot] wants to merge 2 commits into
mainfrom
devin/1783564336-containerize-product

Conversation

@devin-ai-integration

@devin-ai-integration devin-ai-integration Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Summary

Containerizes the Product service and adds proper liveness/readiness probes with fully externalized config.

  • Multi-stage Dockerfile (Services/Product/Product.API/Dockerfile): sdk:10.0 build stage (restore csproj → publish) + slim aspnet:10.0 runtime stage. Runs as the non-root $APP_UID user, declares a HEALTHCHECK that curls /health, and sets config only via env (ASPNETCORE_URLS=http://+:5004, ASPNETCORE_ENVIRONMENT=Production).
  • Health endpoints in Program.cs:
    • /health — liveness, Predicate = _ => false (no dependency checks, 200 while the process serves requests).
    • /ready — readiness, runs only checks tagged ready; a AddDbContextCheck<ProductDbContext> reports DB reachability (200 when reachable, 503 otherwise). The old /healthz mapping is replaced.
  • Externalized config: removed the hardcoded connection string from appsettings.json (was Host=localhost;...;Password=postgres). The connection string now comes only from ConnectionStrings__DefaultConnection (already supplied by docker-compose.yml).
  • .dockerignore at src/ (the compose build context) excluding bin/, obj/, .git/, IDE files, and local .env/config so they never enter the image.
AddHealthChecks().AddDbContextCheck<ProductDbContext>("database", tags: ["ready"]);
MapHealthChecks("/health", { Predicate = _ => false });          // liveness
MapHealthChecks("/ready",  { Predicate = c => c.Tags.Contains("ready") }); // readiness

Verification

  • dotnet build -c Release succeeds.
  • docker build -f Services/Product/Product.API/Dockerfile . builds; container reports (healthy).
  • curl /health200 Healthy.
  • curl /ready503 with no DB, 200 Healthy when a reachable Postgres is provided via ConnectionStrings__DefaultConnection.

Note: the ..\..\Shared\* project references (unused at runtime) remain as-is — they're a repo-wide convention shared by all services and build with only a warning; left unchanged to keep this PR scoped to containerization.

Link to Devin session: https://partner-workshops.devinenterprise.com/sessions/b51bc93741e946fcbf6a6061c22462e3
Requested by: @mbatchelor81


Open in Devin Review

@mbatchelor81 mbatchelor81 self-assigned this Jul 9, 2026
@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 2 potential issues.

Open in Devin Review

Comment thread src/Services/Product/Product.API/Program.cs
Comment on lines +43 to +44
HEALTHCHECK --interval=30s --timeout=5s --start-period=20s --retries=3 \
CMD curl -fsS http://localhost:5004/health || exit 1

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔍 Docker HEALTHCHECK only tests liveness, not database connectivity

The Dockerfile HEALTHCHECK hits /health, which is configured with Predicate = _ => false — meaning no actual health checks run and it always returns 200 as long as the process is alive. Database connectivity is only checked via /ready. This is a valid liveness-vs-readiness separation for Kubernetes, but in plain Docker or docker-compose (which is what this repo uses), there's no built-in readiness concept. Docker will mark the container as 'healthy' even if the database is completely unreachable. If the intent is for depends_on with condition: service_healthy to gate on DB availability, this HEALTHCHECK won't accomplish that.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Intentional — the container HEALTHCHECK targets liveness (/health) on purpose. Docker uses the healthcheck for restart/replacement decisions, so gating it on DB reachability would flap the container unhealthy (and risk restart loops) during transient DB outages even though the process is fine. DB reachability is exposed separately via /ready for orchestrators that support readiness gating. Compose here doesn't use depends_on: condition: service_healthy, so there's no behavioral regression; leaving the separation in place.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant