如何在Docker容器内的PHP Laravel中调用宿主机Docker创建容器
问题场景
我们用Docker运行PHP Laravel作为API服务器,需要从容器内调用宿主机的Docker创建并运行新容器(非Docker-in-Docker场景)。尝试了两种基于Docker API的curl方法均失败:
方法1:PHP curl扩展调用
$ch = curl_init(); curl_setopt($ch, CURLOPT_URL, "http://localhost/containers/create"); curl_setopt($ch, CURLOPT_UNIX_SOCKET_PATH, "/var/run/docker.sock"); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_POSTFIELDS, $payload); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_HTTPHEADER, [ 'Content-Type: application/json', ]); $response = curl_exec($ch); $error = curl_error($ch); curl_close($ch);
执行结果:
{ "error": "Couldn't connect to server" }
方法2:shell_exec执行curl命令
$createCommand = sprintf( "curl --unix-socket /var/run/docker.sock -H 'Content-Type: application/json' -d '%s' -X POST http://localhost/v1.47/containers/create 2>&1", json_encode([ 'Image' => 'test-image', 'Cmd' => ['python3', 'main.py', $args1, $args2], ]) ); $createOutput = shell_exec($createCommand);
执行结果:
curl (7) failed to connect
解决方案
核心问题是容器内无法访问宿主机的/var/run/docker.sock,需先完成挂载权限配置,再修正调用逻辑:
1. 挂载宿主机Docker套接字到Laravel容器
启动Laravel容器时,通过-v参数将宿主机的Docker套接字映射到容器内,同时确保容器内用户有读写权限:
docker run -d \ -v /var/run/docker.sock:/var/run/docker.sock \ -v /usr/bin/docker:/usr/bin/docker \ --user root \ your-laravel-image:tag
-v /var/run/docker.sock:/var/run/docker.sock:将宿主机的Docker套接字映射到容器内,让容器能通过套接字调用宿主机Docker API-v /usr/bin/docker:/usr/bin/docker:可选,若需直接在容器内执行docker命令,挂载宿主机的docker二进制文件--user root:确保容器内用户对套接字有读写权限(生产环境可精细化配置,比如将容器用户加入宿主机docker用户组)
2. 修正PHP curl调用逻辑
需指定Docker API版本(如v1.47),同时补充容器启动逻辑:
$payload = json_encode([ 'Image' => 'test-image', 'Cmd' => ['python3', 'main.py', $args1, $args2], // 可添加端口映射、卷挂载等其他容器配置 ]); $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, "http://localhost/v1.47/containers/create"); curl_setopt($ch, CURLOPT_UNIX_SOCKET_PATH, "/var/run/docker.sock"); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, $payload); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_HTTPHEADER, [ 'Content-Type: application/json', ]); $response = curl_exec($ch); if ($response === false) { $error = curl_error($ch); // 处理错误逻辑 } else { $containerInfo = json_decode($response, true); // 创建成功后启动容器 $startCh = curl_init(); curl_setopt($startCh, CURLOPT_URL, "http://localhost/v1.47/containers/{$containerInfo['Id']}/start"); curl_setopt($startCh, CURLOPT_UNIX_SOCKET_PATH, "/var/run/docker.sock"); curl_setopt($startCh, CURLOPT_POST, true); curl_setopt($startCh, CURLOPT_RETURNTRANSFER, true); $startResponse = curl_exec($startCh); curl_close($startCh); } curl_close($ch);
3. 修正shell_exec调用的转义问题
方法2中json_encode后的字符串含引号,直接拼接会导致shell语法错误,需转义特殊字符:
$containerConfig = json_encode([ 'Image' => 'test-image', 'Cmd' => ['python3', 'main.py', $args1, $args2], ]); // 转义shell特殊字符避免语法错误 $escapedConfig = escapeshellarg($containerConfig); $createCommand = sprintf( "curl --unix-socket /var/run/docker.sock -H 'Content-Type: application/json' -d %s -X POST http://localhost/v1.47/containers/create 2>&1", $escapedConfig ); $createOutput = shell_exec($createCommand);
4. 权限排查(关键)
若挂载后仍无法连接,检查以下内容:
- 宿主机
/var/run/docker.sock的权限:执行ls -l /var/run/docker.sock,确保所属组为docker,权限为rw-rw---- - 容器内用户组配置:若未用root用户,可在Dockerfile中添加
RUN usermod -aG docker www-data(假设Laravel运行用户为www-data),同时确保宿主机docker组GID与容器内一致
内容的提问来源于stack exchange,提问作者nan yoo
相关产品推荐
相关产品推荐

