|
4 | 4 | "bufio" |
5 | 5 | "context" |
6 | 6 | "fmt" |
7 | | - "io" |
8 | 7 | "log/slog" |
9 | | - "net/http" |
10 | 8 | "os/exec" |
11 | 9 | "strings" |
12 | 10 | "time" |
@@ -129,53 +127,29 @@ func (d *DockerRuntime) EnsureOllama(ctx context.Context) (string, error) { |
129 | 127 | return resp.ID, nil |
130 | 128 | } |
131 | 129 |
|
132 | | -// waitForOllamaHealthy polls the Ollama HTTP API directly until it responds |
133 | | -// or the timeout expires. This is more reliable than Docker HEALTHCHECK because |
134 | | -// it doesn't depend on tools being installed inside the container. |
| 130 | +// waitForOllamaHealthy polls the Docker HEALTHCHECK status until the container |
| 131 | +// becomes healthy or the timeout expires. The HEALTHCHECK uses "ollama list" |
| 132 | +// which runs inside the container, avoiding network reachability issues between |
| 133 | +// the API container and the Ollama container (they may be on different Docker networks). |
135 | 134 | func (d *DockerRuntime) waitForOllamaHealthy(ctx context.Context, containerID string, timeout time.Duration) error { |
136 | 135 | deadline := time.Now().Add(timeout) |
137 | | - httpClient := &http.Client{Timeout: 3 * time.Second} |
138 | | - |
139 | 136 | for time.Now().Before(deadline) { |
140 | | - // Get container IP to call the Ollama API directly. |
141 | 137 | info, err := d.client.ContainerInspect(ctx, containerID) |
142 | 138 | if err != nil { |
143 | 139 | return fmt.Errorf("inspecting ollama container: %w", err) |
144 | 140 | } |
145 | | - |
146 | | - // Try all available network IPs (container may not have a default bridge IP). |
147 | | - var ollamaURL string |
148 | | - if info.NetworkSettings != nil { |
149 | | - // First try the default IP. |
150 | | - if info.NetworkSettings.IPAddress != "" { |
151 | | - ollamaURL = fmt.Sprintf("http://%s:%s/api/tags", info.NetworkSettings.IPAddress, OllamaInternalPort) |
152 | | - } |
153 | | - // If no default IP, try IPs from named networks. |
154 | | - if ollamaURL == "" { |
155 | | - for _, net := range info.NetworkSettings.Networks { |
156 | | - if net.IPAddress != "" { |
157 | | - ollamaURL = fmt.Sprintf("http://%s:%s/api/tags", net.IPAddress, OllamaInternalPort) |
158 | | - break |
159 | | - } |
160 | | - } |
161 | | - } |
| 141 | + if info.State.Health != nil && info.State.Health.Status == "healthy" { |
| 142 | + slog.Info("ollama container is healthy", "id", containerID[:12]) |
| 143 | + return nil |
162 | 144 | } |
163 | | - |
164 | | - if ollamaURL != "" { |
165 | | - req, err := http.NewRequestWithContext(ctx, http.MethodGet, ollamaURL, nil) |
166 | | - if err == nil { |
167 | | - resp, err := httpClient.Do(req) |
168 | | - if err == nil { |
169 | | - io.Copy(io.Discard, resp.Body) |
170 | | - resp.Body.Close() |
171 | | - if resp.StatusCode == http.StatusOK { |
172 | | - slog.Info("ollama container is healthy", "id", containerID[:12], "url", ollamaURL) |
173 | | - return nil |
174 | | - } |
| 145 | + slog.Debug("waiting for ollama to become healthy", |
| 146 | + "id", containerID[:12], |
| 147 | + "health", func() string { |
| 148 | + if info.State.Health != nil { |
| 149 | + return info.State.Health.Status |
175 | 150 | } |
176 | | - } |
177 | | - } |
178 | | - |
| 151 | + return "no-healthcheck" |
| 152 | + }()) |
179 | 153 | select { |
180 | 154 | case <-ctx.Done(): |
181 | 155 | return ctx.Err() |
|
0 commit comments