Closed dancrumb closed 7 years ago
I ran into the same problem and like this idea from @kennu
Personally, I would make Things Just Work, by making Fig autodetect which container ports are exposed to a linked container, ping them before starting the linked container (with a sane timeout), and ultimately provide a configuration setting to override/disable this functionality.
I think this would solve a lot typical use cases, like for me when depending on the official mongodb container.
I agree with @soupdiver. I am also having trouble in conjunction with a mongo container, and although I have it working with a start.sh script, the script is not very dynamic and adds another file I need to keep in my repo (I would like to just have a Dockerfile and docker-compose.yml in my node repo). It would be nice if there were some way to just Make It Work, but I think something simple like a wait timer won't cut it in most cases.
IMO pinging is not enough, because the basic network connection may be available, but the service itself is still not ready. This is the case with the MySQL image for example, using curl or telnet for the connection check on the exposed ports would be safer, although I don't know if it would be enough. But most containers don't have these tools installed by default.
Could docker or fig handle these checks?
Could docker or fig handle these checks?
In short: no. For various reasons;
and I don't think you'd want Fig/Compose to modify your container by installing software (such as curl or telnet) in it.
No, for sure not.
Fig/Compose cannot automatically invent how to do that.
Not invent. I was thinking more about an instruction for fig or docker, how to check it, eg.
web:
image: nginx
link: db
db:
is_available: "curl DB_TCP_ADDR:DB_TCP_PORT"
The telnet
command would be executed on the docker-host, not in the container.
But I am just thinking loud, I know that this is not the perfect solution. But the current way of using custom check-scripts for the containers could be improved.
The telnet command would be executed on the docker-host, not in the container.
Then curl
or <name a tool that's needed>
would have to be installed on the host. This could even have huge security issues (e.g. someone wants to be funny and uses is_available: "rm -rf /"
). Apart from that, being able to access the database from the host is no guarantee that it's also accessible from inside the container.
But I am just thinking loud, ...
I know, and I appreciate it. Just think there's no reliable way to automate this, or would serve most use-cases. In many cases you'd end up with something complex (take, for example, the curl
example; how long should it try to connect? Retry?). Such complexity is better to move inside the container, which would also be useful if the container was started with Docker, not Fig/Compose.
@thaJeztah I totally agree with you. And it's very likely that there will be no 100% solution.
I’m going to repeat a suggestion I made earlier: It would be sufficient for me if I could state in the fig.yml “wait for this container to exit before running this other container”.
This would allow me to craft a container that knows how to wait for all it’s dependencies - check ports, initialise databases, whatever - and would require fig know as little as possible.
I would see it configured as something like:
“”" app: links:
runthisfirst: links:
runthisfirst has a link that means the database starts up so it can check access. app will only run once runthisfirst has exited (bonus points if runthisfirst has to exit successfully).
Is this feasible as an answer?
KJL
On 10 Feb 2015, at 05:28, Tobias Munk notifications@github.com wrote:
@thaJeztah https://github.com/thaJeztah I totally agree with you. And it's very likely that there will be no 100% solution.
— Reply to this email directly or view it on GitHub https://github.com/docker/fig/issues/374#issuecomment-73561930.
I've just tried migrating my shell script launchers and ran into this issue. It would be nice even just to add a simple sleep/wait key that just sleeps for that number of seconds before launching the next container.
db:
image: tutum/mysql:5.6
sleep: 10
app:
link:
- db:db
I really dno't like this for a number of reasons.
a) I think it's the wrong place for this b) How long do you sleep for? c) What if the timeout is not long enougH?
Aside from the obvious issues I really don't think infrastructure should care about what the application is and vice versa. IHMO the app should be written to be more tolerant and/or smarter about it's own requirements.
That being said existing applications and legacy applications will need something -- But it should probably be more along the lines of:
a docker-compose.yml
:
db:
image: tutum/mysql:5.6
app:
wait: db
link:
- db:db
Where wait
waits for "exposed" services on db
to become available.
The problem is how do you determine that?
In the simplest cases you wait until you can successfully open a tcp or udp connection to the exposed services.
This might be overkill for this problem but what would be a nice solution is if docker provided an event triggering system where you could initiate a trigger from one container that resulted in some sort of callback in another container. In the case of waiting on importing data into a MySQL database before starting another service, just monitoring whether the port was available isn't enough.
Having an entrypoint script set an alert to Docker from inside the container (set a pre-definied environment variable for example) that triggered an event in another container (perhaps setting the same synchronized environment variable) would enable scripts on both sides to know when certain tasks are complete.
Of course we could set up our own socket server or other means but that's tedious to solve a container orchestration issue.
@aanand I almost have something working using your wait approach as the starting point. However, there is something else happening between docker-compose run and docker run where the former appears to hang whilst the later works a charm.
example docker-compose.yml:
db:
image: postgres
ports:
- "5432"
es:
image: dockerfile/elasticsearch
ports:
- "9200"
wait:
image: n3llyb0y/wait
environment:
PORTS: "5432 9200"
links:
- es
- db
then using...
docker-compose run wait
however this is not to be. The linked services start and it looks like we are about to wait only for it to choke (at least within my virtualbox env. I get to the nc
loop and we get a single dot then...nothing).
However, with the linked services running I can use this method (which is essentially what I have been doing for our CI builds)
docker run -e PORTS="5432 9200" --links service_db_1:wait1 --links service_es_1:wait2 n3llyb0y/wait
It feels like docker-compose run
should work in the same way. The difference is that when using docker-compose run
with the detach flag -d
you get no wait benefit as the wait container backgrounds and I think (at this moment in time) that not using the flag causes the wait to choke on the other non-backgrounded services. I am going to take a closer look
After a bit of trial and error it seems the above approach does work! It's just the busybox base doesn't have a netcat util that works very well. My modified version of @aanand wait utility does work against docker-compose 1.1.0 when using docker-compose run <util label>
instead of docker-compose up
. Example of usage in the link.
Not sure if it can handle chaining situations as per the original question though. Probably not.
Let me know what you think.
This is a very interesting issue. I think it would be really interesting to have a way that one container waits until another one it's ready. But as everybody says, what does ready mean? In my case I have a container for MySQL, another one that manage its backups and is also in charge of import an initial database, and then the containers for each app that need the database. It's obvious that to wait the ports to be exposed is not enough. First the mysql container must be started and then the rest should wait until the mysql service is ready to use, not before. To get that, I have needed to implement a simple script to be executed on reboot that uses the docker exec
functionality. Basically, the pseudo-code would be like:
run mysql
waitUntil "docker exec -t mysql mysql -u root -prootpass database -e \"show tables\""
run mysql-backup
waitUntil "docker exec -t mysql mysql -u root -prootpass database -e \"describe my_table\""
run web1
waitUntil "dexec web1 curl localhost:9000 | grep '<h1>Home</h1>'"
run web2
waitUntil "dexec web2 curl localhost:9000 | grep '<h1>Home</h1>'"
run nginx
Where waitUntil
function has a loop with a timeout that evals the docker exec …
command and check if the exit code is 0.
With that I assure that every container waits until its dependencies are ready to use.
So I think it could be an option to integrate within compose utility. Maybe something like that, where wait_until
declares a list of other dependencies (containers) and waits for each one until they respond ok to the corresponding command (or maybe with an optional pattern or regex to check if the result matches to something you expect, even though using grep
command could be enough).
mysql:
image: mysql
...
mysql-backup:
links:
- mysql
wait_until:
- mysql: mysql -u root -prootpass database -e "show tables"
...
web1:
links:
- mysql
wait_until:
- mysql: mysql -u root -prootpass database -e "describe my_table"
...
web2:
links:
- mysql
wait_until:
- mysql: mysql -u root -prootpass database -e "describe my_table"
...
nginx:
links:
- web1
- web2
wait_until:
- web1: curl localhost:9000 | grep '<h1>Home</h1>'
- web2: curl localhost:9000 | grep '<h1>Home</h1>'
...
Wha not a simple eait for the port like it? http://docs.azk.io/en/azkfilejs/wait.html#
@robsonpeixoto: Waiting for the port isn't sufficient for a lot of use cases. For example, let's say you are seeding a database with data on creation and don't want the web server to start and connect to it until the data operation has completed. The port will be open the whole time so that wouldn't block the web server from starting.
Something like AWS CloudFormation's WaitCondition would be nice. http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-waitcondition.html
+1 I'm having the same issue when using Docker for testing my Rails apps which depend on MySQL
+1 I have this issue too. I like @adrianhurt idea, where you actually supply the condition to be evaluated to determine if the wait is complete. That way you still have a nice declarative yml, and you don't have to have an arbitrary definition of "ready".
+1
I've had this tab open for a while: http://crosbymichael.com/docker-events.html ...seems relevant
+1
+1 for simple timeout
+1 for a ready condition
+1
I am solving this very reliably on the application level since a while, like it was recommended in this thread.
Just to give you an idea how this can be implemented for MySQL + PHP here's my code.
From igorw/retry :)
Since the network is reliable, things should always work. Am I right? For those cases when they don't, there is retry.
+1
@schmunk42 Nice stuff - I like that it's a good example of both establishing the connection and performing an idempotent database setup operation.
Might be good to create a (some) basic example(s) for inclusion in the docs, for different cases, e.g. NodeJS, Ruby, PHP.
+1, at least should provide some options to add some delay before the container starts successfully.
+1
How to solve problems when you try to connect services that aren't your code.
For example, if a have the service Service
and the database InfluxDB
. Services
requires InfluxDB
and the InfluxDB
has a slow startup.
How can docker-compose wait for InfluxDB
be ready?
I the code is my, I can solve it putting a retry. But for third app I can't changes the code.
@robsonpeixoto there are some examples in this ticket with netcat or similar ways. You can take a look to my MySQL example in another ticket: https://github.com/docker/docker/issues/7445#issuecomment-101523662
That's the reason I think each container should have the optional ability to indicate its own readiness. For a DB, for example, I want to wait until the service is completely ready, not when the process is created. I solve this with customized checks with docker exec
and checking if it can solve a simple query, for example.
Some optional flag for docker run
to indicate an internal check command would be great to later link it from another container using a special flag for the link.
Something like:
$ sudo docker run -d --name db training/postgres --readiness-check /bin/sh -c "is_ready.sh"
$ sudo docker run -d -P --name web --link db:db --wait-for-readiness db training/webapp python app.py
Where is_ready.sh
is a simple boolean test which is in charge of the decision of when the container is considered as ready.
+1
@schmunk42 nice quote!
+1
+1
+1
+1
+1
+1
+1
+1
+1
Actually I changed my mind about this, so -1
It makes more sense for your container to check wether the 3rd party service is available, and this is easily done with a little bash wrapper script that uses nc
for example.
Relying on a delay is tempting, but it's a poor solution because:
Relying on writing a wrapper bash script is better because:
Reading through this threadnought I see that no one mentions secrets. I'm trying to use a data-only container which requests secrets once it is run. The problem I have: If my secrets take too long to transmit/decrypt then my dependant container fails because the data it's expecting isn't there. I can't really use the method of "well put everything in the container before you run it" because they are secrets.
I know there is some ambiguity around data-only containers in compose due to the context of the return code, but is there a better way to do this?
Similarly changing my mind on this, -1. @dnephin's approach is entirely correct. If your application depends on a service, the application itself should be able to handle unavailability of that service gracefully (e.g. re-establishing a connection). It shouldn't be some bash wrapper script or some logic in Compose or Docker, it's the responsibility of the application itself. Anything not at the application level will also only work on initialization; if that service goes down, a wrapper script or something will not be executed.
Now if we could get application/library/framework developers to realize and support this responsibility, that would be fantastic.
Tough to do considering the approaches you would take involve sideloading other daemons which isn't recommended. For my example where I have a rails app attempting to connect to a MySQL database while another rails app is currently migrating and seeding the DB on initial startup, for me to make the rails app know to not attempt to use the DB, I would either have to mod the ActiveRecord library (not going to happen) or run a script that keeps checking to see if the DB has been migrated and seeded. But how do I know for sure without knowing what data is supposed to be in there and/or having some script that runs on the system that's seeding the DB to inform the rest to connect to it.
Maybe i'm missing the obvious solution but your answer of "developers should be able to deal with this in their own code" breaks down when you're using off the shelf libraries and when you're not "supposed" to sideload daemons into a container.
I have a MySQL container that takes a little time to start up as it needs to import data.
I have an Alfresco container that depends upon the MySQL container.
At the moment, when I use fig, the Alfresco service inside the Alfresco container fails when it attempts to connect to the MySQL container... ostensibly because the MySQL service is not yet listening.
Is there a way to handle this kind of issue in Fig?