@@ -119,7 +119,7 @@ def wrapper_needs_docker(*args: Any, **kwargs: Any) -> None:
119119
120120
121121def get_free_port (
122- within_range : tuple [int , int ] | None = (49152 , 65535 ),
122+ within_range : tuple [int , int ] = (49152 , 65535 ),
123123 exclude : Sequence [int ] = (),
124124) -> int :
125125 """Find a random free port to use for HTTP."""
@@ -136,7 +136,7 @@ def get_free_port(
136136 # Check if the port is free
137137 with closing (socket .socket (socket .AF_INET , socket .SOCK_STREAM )) as s :
138138 try :
139- s .bind (("localhost " , port ))
139+ s .bind (("127.0.0.1 " , port ))
140140 except OSError :
141141 # Port is already in use
142142 continue
@@ -537,6 +537,7 @@ def get_project_containers(project_id: str) -> list[Container]:
537537
538538def serve (
539539 images : list [str ],
540+ host_ip : str = "127.0.0.1" ,
540541 ports : list [str ] | None = None ,
541542 volumes : list [str ] | None = None ,
542543 gpus : list [str ] | None = None ,
@@ -550,6 +551,7 @@ def serve(
550551
551552 Args:
552553 images: a list of Tesseract image IDs as strings.
554+ host_ip: IP address to bind the Tesseracts to.
553555 ports: port or port range to serve each Tesseract on.
554556 volumes: list of paths to mount in the Tesseract container.
555557 gpus: IDs of host Nvidia GPUs to make available to the Tesseracts.
@@ -597,22 +599,30 @@ def serve(
597599 if propagate_tracebacks :
598600 args .append ("--debug" )
599601
600- logger .info (f"Serving Tesseract at http://localhost:{ port } " )
601- logger .info (f"View Tesseract: http://localhost:{ port } /docs" )
602+ # Always bind to all interfaces inside the container
603+ args .extend (["--host" , "0.0.0.0" ])
604+
605+ if host_ip == "0.0.0.0" :
606+ ping_ip = "127.0.0.1"
607+ else :
608+ ping_ip = host_ip
609+
610+ logger .info (f"Serving Tesseract at http://{ ping_ip } :{ port } " )
611+ logger .info (f"View Tesseract: http://{ ping_ip } :{ port } /docs" )
602612
603613 container = docker_client .containers .run (
604614 image = image_ids [0 ],
605615 command = ["serve" , * args ],
606616 device_requests = gpus ,
607- ports = {f"{ port } " : "8000" },
617+ ports = {f"{ host_ip } : { port } " : container_port },
608618 detach = True ,
609619 volumes = volumes ,
610620 )
611621 # wait for server to start
612622 timeout = 30
613623 while True :
614624 try :
615- response = requests .get (f"http://localhost :{ port } /health" )
625+ response = requests .get (f"http://{ ping_ip } :{ port } /health" )
616626 except requests .exceptions .ConnectionError :
617627 pass
618628 else :
@@ -628,7 +638,13 @@ def serve(
628638 return container .name
629639
630640 template = _create_docker_compose_template (
631- image_ids , ports , volumes , gpus , num_workers , debug = propagate_tracebacks
641+ image_ids ,
642+ host_ip ,
643+ ports ,
644+ volumes ,
645+ gpus ,
646+ num_workers ,
647+ debug = propagate_tracebacks ,
632648 )
633649 compose_fname = f"docker-compose-{ _id_generator ()} .yml"
634650
@@ -647,6 +663,7 @@ def serve(
647663
648664def _create_docker_compose_template (
649665 image_ids : list [str ],
666+ host_ip : str = "127.0.0.1" ,
650667 ports : list [str ] | None = None ,
651668 volumes : list [str ] | None = None ,
652669 gpus : list [str ] | None = None ,
@@ -674,6 +691,9 @@ def _create_docker_compose_template(
674691 )
675692 )
676693
694+ # Prepend host IP to ports
695+ ports = [f"{ host_ip } :{ port } " for port in ports ]
696+
677697 gpu_settings = None
678698 if gpus :
679699 if (len (gpus ) == 1 ) and (gpus [0 ] == "all" ):
@@ -691,11 +711,12 @@ def _create_docker_compose_template(
691711 "environment" : {
692712 "TESSERACT_DEBUG" : "1" if debug else "0" ,
693713 },
714+ "num_workers" : num_workers ,
694715 }
695716
696717 services .append (service )
697718 template = ENV .get_template ("docker-compose.yml" )
698- return template .render (services = services , num_workers = num_workers )
719+ return template .render (services = services )
699720
700721
701722def _id_generator (
0 commit comments