I have a service (an ASP.NET Core Web application) that depends on MSSQL. The services are orchestrated using Docker compose, and I want docker compose to first start the database and wait for it to be ready before running my service. For that, I am defining the docker-compose.yml
as:
version: '3.7'
services:
sql.data:
container_name: db_service
image: microsoft/mssql-server-linux:2017-latest
healthcheck:
test: ["CMD", "/opt/mssql-tools/bin/sqlcmd", "-S", "http://localhost:1433", "-U", "sa", "-P", "Pass_word", "-Q", "SELECT 1", "||", "exit 1"]
my_service:
container_name: my_service_container
image: ${DOCKER_REGISTRY-}my_service
build:
context: .
dockerfile: MyService/Dockerfile
depends_on:
- sql.data
With this health-check, Docker compose does not wait for the database service to be ready, and starts my_service
immediately after, and, as expected, my_service
fails connecting to the database. Part of the log is:
Recreating db_service ... done
Recreating my_service_container ... done
Attaching to db_service, my_service_container
my_service_container | info: ...Context[0]
my_service_container | Migrating database associated with context Context
my_service_container | info: Microsoft.EntityFrameworkCore.Infrastructure[10403]
my_service_container | Entity Framework Core 3.1.1 initialized 'Context' using provider 'Microsoft.EntityFrameworkCore.SqlServer' with options: MigrationsAssembly=MyService
my_service_container | fail: Context[0]
my_service_container | An error occurred while migrating the database used on context Context
my_service_container | Microsoft.Data.SqlClient.SqlException (0x80131904): A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was not accessible. Verify that the instance name is correct and that SQL Server is configured to allow remote connections. (provider: TCP Provider, error: 40 - Could not open a connection to SQL Server)
...
exception details
...
my_service_container | ClientConnectionId:00000000-0000-0000-0000-000000000000
my_service_container exited with code 0
db_service | 2020-03-05 05:45:51.82 Server Microsoft SQL Server 2017 (RTM-CU13) (KB4466404) - 14.0.3048.4 (X64)
Nov 30 2018 12:57:58
Copyright (C) 2017 Microsoft Corporation
Developer Edition (64-bit) on Linux (Ubuntu 16.04.5 LTS)
2020-03-05 05:45:51.82 Server UTC adjustment: 0:00
2020-03-05 05:45:51.82 Server (c) Microsoft Corporation.
2020-03-05 05:45:51.82 Server All rights reserved.
2020-03-05 05:45:51.82 Server Server process ID is 4120.
2020-03-05 05:45:51.82 Server Logging SQL Server messages in file '/var/opt/mssql/log/errorlog'.
2020-03-05 05:45:51.82 Server Registry startup parameters:
-d /var/opt/mssql/data/master.mdf
-l /var/opt/mssql/data/mastlog.ldf
-e /var/opt/mssql/log/errorlog
As shown in the logs, the docker compose first starts the DB, but does not wait for it become ready before running my service.
I tried different syntax for the healthcheck
, e.g.,:
test: /opt/mssql-tools/bin/sqlcmd -S http://localhost:1433 -U sa -P ${SA_PASSWORD} -Q "SELECT 1" || exit 1
But neither worked as expected.
I have checked the following sources online, but using neither I was able to solve the problem:
- stackoverflow answer
- github comment
- github sample
Is this functionality even supported in version 3.7
? because of this confusing comment
Question
Any thoughts on how best I can wait for MSSQL service to start?
8条答案
按热度按时间yfjy0ee71#
After searching and trying many different scenarios, I was able to add waiting using the following composer file. This is for
asp.net
core solution. The key is that you have to overwriteentrypoint
if it is specified indockerfile
. Also, you need to make sure to save "wait-for-it.sh" LF as line ending instead of CRLF, otherwise you'll get the error offile not found
.The
dockerfile
should have the following (download it from here: https://raw.githubusercontent.com/vishnubob/wait-for-it/master/wait-for-it.sh , make sure to save the file):docker-compose.yml
pgccezyw2#
When you use
depends_on
, docker-compose will just launch your base service with more priority and never wait for start services.There are some useful external program that help you to wait for specific service (port), then run another service.
vishnubob/wait-for-it is one of them which blocks execution flow until your specific port(s) get ready.
Another good choice is eficode/wait-for which already prepared for docker-compose.
Example usage (according to eficode/wait-for docs)
-- UPDATE --
Consider you have a Python application that depend on a database like PostgreSQL, and also your application will run with this command:
python app.py
As Official Docker Document said, Put
vishnubob/wait-for-it
in your image (inside of your other project files likeapp.py
)Now just put this lines in your
docker-compose.yml
:Note: Don't forget to put this commands in your
Dockerfile
inside your image files:tyg4sfes3#
Create two separate dockerfiles (e.g):
Set up the sequence within docker-compose.yml
Mssql.Dockerfile
App.Dockerfile:
Docker-compose.yml:
Note: The connection string will look like:
"Server=sqlserver;Database=Northwind;Trusted_Connection=False;User Id=sa;Password=Password123;MultipleActiveResultSets=true"
vdgimpew4#
Here is a complete example
deyfvvtc5#
You can write a simple script, which will be launched in container with your app. For example, you can just set a delay, using
sleep N
( where N it is a time which is needed for starting your DB), or you can use auntil
cycle in which you to can try to connect to your DB, and when it will be possible, then you can start your app.I know this is not a perfect solution, but it helped me when I had a similar problem
ej83mcc06#
Using Kubernetes Deployment, the following probe successfullly identified the ready state:
You could code this as a livenessProbe with initialDelay if your Kubernetes version is <1.20.
Explanation: sqlcmd returns a '0' status irrespective of whether the query returns anything or not. However,
grep -q
returns 0 or 1 depending on presence of the word "READY".I have not used docker-compose but I suspect this command would work as a healthcheck test (assuming SA_PASSWORD is injected into the environment), ie:
pdsfdshx7#
you can also delay the docker startup until mssql is up:
docker-compose.yaml
luaexgnf8#
I think your initial attempt isn't actually far off. Using a health check seems the most appropriate route, so I would continue with that approach, however you'll want to take advantage of the
condition
functionality ofdepends_on
. With this, you can useservice_healthy
condition, which will wait for your SQL Server health check to report healthy.See this article on the Docker website which mentions this: https://docs.docker.com/compose/startup-order/
Your
docker-compose.yml
would look like this:You could also take advantage of the
healthcheck
options, as below, and a slightly neater syntax: