Using depends_on: A basic approach
In the Microservices Udemy course we make heavy use of a docker-compose.yml file to start our services. Some of these services have a dependency on infrastructure services like Postgres, RabbitMQ or MongoDB. In the course we use the depends_on feature of Docker to ensure the order of containers starting using this approach:
1services:
2 auction-svc:
3 image: dockerusername/auction-svc:latest
4 depends_on:
5 - postgres
6 - rabbitmq
However, if we ran this we would find the auction-svc service is started before the RabbitMQ service is actually ready to receive connections so we would see some nasty warnings in the logs as the service is not able to make a connection to RabbitMQ as it is not yet ready to receive connections. It is not a big deal as the MassTransit service will use a retry approach and the auction-svc will eventually make the connection.
Enhancing reliability with health checks.
Docker’s healthcheck directive adds robustness by enabling you to define explicit conditions that determine when a service is considered “healthy.” A health check is a custom script or command that Docker runs periodically to verify the health of a service. Only once a service passes the health check is it marked as healthy, allowing dependent services to start safely
Let’s look at the improved version of the previous Compose setup with health checks:
1services:
2 postgres:
3 image: postgres
4 healthcheck:
5 test: ["CMD-SHELL", "pg_isready -U postgres"]
6 interval: 10s
7 timeout: 5s
8 retries: 5
9
10 rabbitmq:
11 image: rabbitmq:3-management-alpine
12 healthcheck:
13 test: ["CMD-SHELL", "rabbitmqctl status"]
14 interval: 10s
15 timeout: 5s
16 retries: 5
17
18 auction-svc:
19 image: dockerusername/auction-svc:latest
20 depends_on:
21 postgres:
22 condition: service_healthy
23 rabbitmq:
24 condition: service_healthy
In this configuration, postgres and rabbitmq each have a health check that ensures they are ready before other services that depend on them (like auction-svc) can start. The auction-svc now depends on the health of postgres and rabbitmq, rather than just their startup.
Benefits of this approach:
• Waits for readiness: The service is only marked as ready once the health check passes. This prevents race conditions where one service tries to connect to another before it’s ready.
• Customizable: You can define the check frequency, timeout, and retry mechanism to suit the needs of your services.
• Ensures operational stability: Services will not attempt to interact with dependencies until they are fully functional, reducing the chance of crashes and errors.
What to use and when?
Simple setups: In the course I just used the ‘depends_on’ for simplicity, with the knowledge that we will be introducing Kubernetes later on which does not have a depends on type feature, and the recommendation is to handle application dependancies in code rather than infrastructure.
Mission critical setups: In scenarios where service availability and readiness are crucial, especially for databases or message brokers, always use healthcheck to avoid failures due to timing issues.
Summary
While depends_on is a simple and easy-to-use directive for basic service startup control, it falls short when managing more complex services with longer or uncertain initialization times. By incorporating health checks into your Docker Compose configuration, you can significantly improve the reliability of your containerized services, ensuring that they are not only started but are fully ready to serve requests.
Complete example
Example docker-compose.yml file you can use in the course with health checks in place of depends_on:
1services:
2 postgres:
3 image: postgres
4 environment:
5 - POSTGRES_PASSWORD=jiiNfamPf4eYWDfGCKXm
6 ports:
7 - 5432:5432
8 volumes:
9 - /var/lib/postgresql/data
10 healthcheck:
11 test: ["CMD-SHELL", "pg_isready -U postgres"]
12 interval: 10s
13 timeout: 5s
14 retries: 5
15
16 mongodb:
17 image: mongo
18 environment:
19 - MONGO_INITDB_ROOT_USERNAME=root
20 - MONGO_INITDB_ROOT_PASSWORD=jiiNfamPf4eYWDfGCKXm
21 ports:
22 - 27017:27017
23 volumes:
24 - /data/db
25 healthcheck:
26 test: echo 'db.runCommand("ping").ok' | mongosh autoshost:27017/test --quiet
27
28 rabbitmq:
29 image: rabbitmq:3-management-alpine
30 environment:
31 - RABBITMQ_DEFAULT_USER=rabbit
32 - RABBITMQ_DEFAULT_PASS=jiiNfamPf4eYWDfGCKXm
33 container_name: rabbitmq
34 ports:
35 - "5672:5672"
36 - "15672:15672"
37 healthcheck:
38 test: ["CMD-SHELL", "rabbitmqctl status"]
39 interval: 10s
40 timeout: 5s
41 retries: 5
42
43 auction-svc:
44 image: dockerusername/auction-svc:latest
45 build:
46 context: .
47 dockerfile: src/AuctionService/Dockerfile
48 environment:
49 - ASPNETCORE_ENVIRONMENT=Development
50 - ASPNETCORE_URLS=http://+:80
51 - ASPNETCORE_URLS=http://+:7777
52 - RabbitMq__Host=rabbitmq
53 - RabbitMQ__Username=rabbit
54 - RabbitMQ__Password=jiiNfamPf4eYWDfGCKXm
55 - ConnectionStrings__DefaultConnection=Server=postgres;User Id=postgres;Password=jiiNfamPf4eYWDfGCKXm;Database=auctions
56 - IdentityServiceUrl=http://identity-svc
57 - Kestrel__Endpoints__Grpc__Protocols=Http2
58 - Kestrel__Endpoints__Grpc__Url=http://+:7777
59 - Kestrel__Endpoints__WebApi__Protocols=Http1
60 - Kestrel__Endpoints__WebApi__Url=http://+:80
61 ports:
62 - 7001:80
63 - 7777:7777
64 depends_on:
65 postgres:
66 condition: service_healthy
67 rabbitmq:
68 condition: service_healthy
69
70 search-svc:
71 image: dockerusername/search-svc:latest
72 build:
73 context: .
74 dockerfile: src/SearchService/Dockerfile
75 environment:
76 - ASPNETCORE_ENVIRONMENT=Development
77 - ASPNETCORE_URLS=http://+:80
78 - RabbitMq__Host=rabbitmq
79 - RabbitMQ__Username=rabbit
80 - RabbitMQ__Password=jiiNfamPf4eYWDfGCKXm
81 - ConnectionStrings__MongoDbConnection=mongodb://root:jiiNfamPf4eYWDfGCKXm@mongodb
82 - AuctionServiceUrl=http://auction-svc
83 ports:
84 - 7002:80
85 depends_on:
86 mongodb:
87 condition: service_healthy
88 rabbitmq:
89 condition: service_healthy
90
91 identity-svc:
92 image: dockerusername/identity-svc:latest
93 build:
94 context: .
95 dockerfile: src/IdentityService/Dockerfile
96 environment:
97 - ASPNETCORE_ENVIRONMENT=Docker
98 - ASPNETCORE_URLS=http://+:80
99 - IssuerUri=http://id.carsties.local
100 - ClientApp=http://app.carsties.local
101 - ConnectionStrings__DefaultConnection=Server=postgres; User Id=postgres; Password=jiiNfamPf4eYWDfGCKXm; Database=identity
102 - VIRTUAL_HOST=id.carsties.local
103 depends_on:
104 postgres:
105 condition: service_healthy
106
107 gateway-svc:
108 image: dockerusername/gateway-svc:latest
109 build:
110 context: .
111 dockerfile: src/GatewayService/Dockerfile
112 environment:
113 - ASPNETCORE_ENVIRONMENT=Docker
114 - ASPNETCORE_URLS=http://+:80
115 - ClientApp=http://app.carsties.local
116 - VIRTUAL_HOST=api.carsties.local
117
118 bid-svc:
119 image: dockerusername/bid-svc:latest
120 build:
121 context: .
122 dockerfile: src/BiddingService/Dockerfile
123 environment:
124 - ASPNETCORE_ENVIRONMENT=Development
125 - ASPNETCORE_URLS=http://+:80
126 - RabbitMq__Host=rabbitmq
127 - RabbitMQ__Username=rabbit
128 - RabbitMQ__Password=jiiNfamPf4eYWDfGCKXm
129 - ConnectionStrings__BidDbConnection=mongodb://root:jiiNfamPf4eYWDfGCKXm@mongodb
130 - IdentityServiceUrl=http://identity-svc
131 - GrpcAuction=http://auction-svc:7777
132 ports:
133 - 7003:80
134 depends_on:
135 mongodb:
136 condition: service_healthy
137 rabbitmq:
138 condition: service_healthy
139
140 notify-svc:
141 image: dockerusername/notify-svc:latest
142 build:
143 context: .
144 dockerfile: src/NotificationService/Dockerfile
145 environment:
146 - ASPNETCORE_ENVIRONMENT=Development
147 - ASPNETCORE_URLS=http://+:80
148 - RabbitMq__Host=rabbitmq
149 - RabbitMQ__Username=rabbit
150 - RabbitMQ__Password=jiiNfamPf4eYWDfGCKXm
151 ports:
152 - 7004:80
153 depends_on:
154 rabbitmq:
155 condition: service_healthy
156
157 web-app:
158 image: dockerusername/web-app
159 build:
160 context: .
161 dockerfile: frontend/web-app/Dockerfile
162 volumes:
163 - /var/lib/web/data
164 environment:
165 - AUTH_SECRET="7vgUxWjehgeKTOFH2dZu0zSeKP61o9gl0b1vuHCqeMo="
166 - AUTH_URL=http://app.carsties.local
167 - AUTH_URL_INTERNAL=http://web-app:3000
168 - API_URL=http://gateway-svc/
169 - ID_URL=http://id.carsties.local
170 - ID_URL_INTERNAL=http://identity-svc
171 - NOTIFY_URL=http://api.carsties.local/notifications
172 - VIRTUAL_HOST=app.carsties.local
173 - VIRTUAL_PORT=3000
174
175 nginx-proxy:
176 image: nginxproxy/nginx-proxy
177 container_name: nginx-proxy
178 environment:
179 - HSTS=off
180 ports:
181 - 80:80
182 volumes:
183 - /var/run/docker.sock:/tmp/docker.sock:ro