Java启动Docker镜像HTTP访问异常问题求助
Java应用调用Docker Socket创建容器后HTTP访问失败(终端执行正常)
我正在开发一款Heroku替代方案,遇到以下问题:
- 在终端直接执行curl命令调用Docker Socket创建Nginx容器,能正常获取容器ID并成功运行
- 通过Spring Boot应用的HTTP接口触发相同的容器创建逻辑后,访问对应端口显示错误页面
相关截图
- 终端正常运行截图:

- 错误页面截图:

核心代码片段
创建容器的curl命令:
String command = "curl --silent -XPOST --unix-socket /run/docker.sock -d '{\"Image\":\"nginx:latest\"}' -H 'Content-Type: application/json' http://localhost/containers/create?name=nginx";
完整Controller代码
package com.handson.chatbot.controller; import com.handson.chatbot.model.StudentApplication; import com.handson.chatbot.repo.StudentApplicationsService; import kotlin.Pair; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.util.PathMatcher; import org.springframework.web.bind.annotation.*; import org.springframework.web.client.RestTemplate; import org.springframework.web.servlet.HandlerMapping; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.*; import java.sql.Timestamp; import java.util.*; import java.util.concurrent.TimeUnit; @RestController @RequestMapping("/app") public class AppController { Map<String, Long> aliasToLastCalledTime = new HashMap<>(); @Autowired StudentApplicationsService studentService; @Autowired private PathMatcher pathMatcher; @Autowired private RestTemplate restTemplate; OkHttpClient client = new OkHttpClient().newBuilder() .build(); private String getWildcardParam(HttpServletRequest request) { String patternAttribute = (String) request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE); String mappingAttribute = (String) request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE); return this.pathMatcher.extractPathWithinPattern(patternAttribute, mappingAttribute); } // create api to register applications (student-docker, appid, = store in database with port // apps (docker_image, appid, port) @RequestMapping(method = RequestMethod.GET, value = "/{alias}/**") public void getItem(HttpServletRequest request, @PathVariable String alias, HttpServletResponse response) throws IOException, InterruptedException { String wantedApp = alias; System.out.println("request for app:" + wantedApp); // check on database for the student of app // random student between 9000 - 20000 // String student = "9001"; // get this from database according to appid StudentApplication studentApplication = studentService.getStudentApplicationByAlias(wantedApp); System.out.println(studentApplication); //////// String dockerImage = studentApplication.getDocker_image(); try { forwardGetRequestToApp(request, response, studentApplication.getPort()); } catch (Exception e) { // start docker image // String command1 = "sudo service docker start"; // Process p1 = Runtime.getRuntime().exec(new String[]{"bash", "-c", command1}); // String command = "curl --unix-socket /var/run/docker.sock -H \"Content-Type: application/json\" -d '{\"Image\": \"redis\"}' " + // " -X POST http://localhost/v1.41/containers/create"; String command = "curl --silent -XPOST --unix-socket /run/docker.sock -d '{\"Image\":\"nginx:latest\"}' -H 'Content-Type: application/json' http://localhost/containers/create?name=nginx"; String p = executeShell(command); System.out.println(p); // java.util.Scanner s = new java.util.Scanner(p.getInputStream()); // System.out.println(s.nextLine()); // wait for 30 seconds TimeUnit.SECONDS.sleep(30); forwardGetRequestToApp(request, response, studentApplication.getPort()); } // save the last timestamp for each application Timestamp timestamp = new Timestamp(System.currentTimeMillis()); long timestampToMinutes = TimeUnit.MILLISECONDS.toMinutes(timestamp.getTime()); aliasToLastCalledTime.put(wantedApp, timestampToMinutes); Timer timer = new Timer(); timer.schedule(new TimerTask() { @Override public void run() { Timestamp currentTime = new Timestamp(System.currentTimeMillis()); for (Map.Entry<String, Long> ts : aliasToLastCalledTime.entrySet()) { if ((TimeUnit.MILLISECONDS.toMinutes(currentTime.getTime()) - ts.getValue()) >= 30) { // kill docker that was not used for 30 minutes } } } }, 0, 60 * 1000); } private void forwardGetRequestToApp(HttpServletRequest request, HttpServletResponse response, Integer port) throws IOException { String url = "http://localhost:" + port + "/" + this.getWildcardParam(request); //ResponseEntity res = this.restTemplate.getForEntity(url, String.class); Request.Builder builder = new Request.Builder() .url(url) .get(); String h = null; Enumeration<String> headerNames = request.getHeaderNames(); while (headerNames.hasMoreElements()) { h = headerNames.nextElement(); builder.addHeader(h, request.getHeader(h)); } Response response1 = client.newCall(builder.build()).execute(); response.setContentLength((int) response1.body().contentLength()); byte[] buffer = new byte[10240]; try ( OutputStream output = response.getOutputStream(); ) { for (int length = 0; (length = response1.body().byteStream().read(buffer)) > 0; ) { output.write(buffer, 0, length); } } Iterator<Pair<String, String>> h2 = response1.headers().iterator(); while (h2.hasNext()) { Pair<String, String> h3 = h2.next(); response.addHeader(h3.component1(), h3.component2()); } } private String executeShell(String cmd) throws InterruptedException, IOException { String res = ""; String line; OutputStream stdin; InputStream stderr; InputStream stdout; System.out.println("executing:\n" + cmd); Process process = Runtime.getRuntime().exec(cmd); stdin = process.getOutputStream(); stderr = process.getErrorStream(); stdout = process.getInputStream(); line = "\n"; stdin.write(line.getBytes()); stdin.flush(); stdin.write(line.getBytes()); stdin.flush(); stdin.close(); BufferedReader brCleanUp = new BufferedReader(new InputStreamReader(stdout)); while ((line = brCleanUp.readLine()) != null) { res += line + "\n"; System.out.println(line); } brCleanUp.close(); brCleanUp = new BufferedReader(new InputStreamReader(stderr)); while ((line = brCleanUp.readLine()) != null) { System.out.println("Abnormal process termination" + line); throw new RuntimeException("process failed and: " + line); } brCleanUp.close(); int exitValue = process.exitValue(); if (exitValue > 0) { throw new RuntimeException("exit value: " + String.valueOf(exitValue)); } return res; } }
预期与实际结果
- 预期:通过HTTP端口访问到Docker容器运行的Nginx服务
- 实际:访问后显示错误页面
内容的提问来源于stack exchange,提问作者Peleg Uzan
相关产品推荐
相关产品推荐

