Pulumi(Go)结合Docker构建镜像报错:无法找到Dockerfile路径
使用Pulumi(Go)部署Docker镜像到GCP Artifact Registry时的Dockerfile路径问题
报错信息
ERROR: resolve : lstat apps: no such file or directory
另一种场景下的完整报错:
error: an unhandled error occurred: program failed: waiting for RPCs: docker build -f apps/remix-app-vite/Dockerfile --target production --platform=linux/amd64 /Users/joseasilis/Documents/programming/alertdown -t remix-app-dev failed with error: exit status 1 docker:image:Image (temporal-worker-dev): error: ERROR: resolve : lstat apps: no such file or directory docker:image:Image (remix-app-dev): error: ERROR: resolve : lstat apps: no such file or directory
目录结构
- 这是一个TypeScript的nx monorepo,Pulumi的Go代码位于
<root>/libs/infrastructure/src/pulumi目录 - Dockerfile分别位于
<root>/apps/remix-app-vite/和<root>/apps/temporal-worker/目录下
构建镜像的核心Go代码
type buildAndPushToArtifactRegistryInput struct { ctx *pulumi.Context artifactRegistryRepo *artifactregistry.Repository dockerImageName string dockerImagePath string dockerfileName string provider *gcp.Provider } func buildAndPushToArtifactRegistry(input *buildAndPushToArtifactRegistryInput) (*docker.Image, error) { if input.artifactRegistryRepo == nil { return nil, errors.New("artifactRegistryRepo cannot be nil") } dependingSources := []pulumi.Resource{ input.artifactRegistryRepo, } // Get the current working directory workingDir, err := os.Getwd() if err != nil { return nil, err } // Go up to the project root (from /libs/infrastructure/src/pulumi) projectRoot := filepath.Join(workingDir, "../../../../") projectRoot = filepath.Clean(projectRoot) // Debug information fmt.Printf("Working Directory: %s\n", workingDir) fmt.Printf("Project Root: %s\n", projectRoot) fmt.Printf("Dockerfile Path: %s\n", filepath.Join("apps", input.dockerImagePath, "Dockerfile")) // Create Docker build args buildArgs := &docker.DockerBuildArgs{ Context: pulumi.String(projectRoot), Dockerfile: pulumi.String(filepath.Join("apps", input.dockerImagePath, "Dockerfile")), ExtraOptions: pulumi.StringArray{ pulumi.String("--platform=linux/amd64"), }, Target: pulumi.String("production"), } image, err := docker.NewImage(input.ctx, input.dockerImageName, &docker.ImageArgs{ Build: buildArgs, ImageName: pulumi.String(input.dockerImageName), }, pulumi.DependsOn(dependingSources), pulumi.Provider(input.provider)) if err != nil { return nil, err } return image, nil }
代码调用与常量定义
调用方式
remixDockerImage, remixDockerImageErr := buildAndPushToArtifactRegistry(&buildAndPushToArtifactRegistryInput{ ctx: input.Ctx, artifactRegistryRepo: remixRepository, dockerImageName: input.Constants.RemixAppDockerImageName, dockerImagePath: input.Constants.RemixAppDockerImagePath, dockerfileName: input.Constants.RemixAppDockerfileName, provider: input.Provider, }) if remixDockerImageErr != nil { return BuildAndPushDockerImagesOutput{}, remixDockerImageErr } temporalDockerImage_, temporalWorkerDockerImageErr := buildAndPushToArtifactRegistry(&buildAndPushToArtifactRegistryInput{ ctx: input.Ctx, artifactRegistryRepo: temporalWorkerRepository, dockerImageName: input.Constants.TemporalDockerImageName, dockerImagePath: input.Constants.TemporalDockerImagePath, dockerfileName: input.Constants.TemporalDockerfileName, provider: input.Provider, })
常量定义
var remixDockerfileLocation = "apps/remix-app-vite" var temporalDockerfileLocation = "apps/temporal-worker" func GetConstants(pulumiContext *pulumi.Context) *Constants { stackName := pulumiContext.Stack() return &Constants{ // Others omitted RemixAppDockerImageName: fmt.Sprintf("remix-app-%s", stackName), RemixAppDockerImagePath: "remix-app-vite", RemixAppDockerfileName: "Dockerfile", RemixAppDockerfileLocation: remixDockerfileLocation, TemporalDockerImageName: fmt.Sprintf("temporal-worker-%s", stackName), TemporalDockerImagePath: "temporal-worker", TemporalDockerfileName: "Dockerfile", } }
调试输出路径信息
// For First app Working Directory: /Users/joseasilis/Documents/programming/alertdown/libs/infrastructure/src/pulumi Project Root: /Users/joseasilis/Documents/programming/alertdown Dockerfile Path: apps/remix-app-vite/Dockerfile
Working Directory: /Users/joseasilis/Documents/programming/alertdown/libs/infrastructure/src/pulumi Project Root: /Users/joseasilis/Documents/programming/alertdown Dockerfile Path: apps/temporal-worker/Dockerfile
已尝试的解决方法
- 将Dockerfile重命名为
Dockerfile.remix和Dockerfile.temporal,并传入对应名称,问题未解决 - 将应用目录作为上下文路径(不指定Dockerfile),虽然能找到Dockerfile,但因需要整个monorepo作为上下文导致镜像构建失败
可行解决方案建议
1. 提前验证Dockerfile是否存在
在构建参数前加入文件存在性检查,确认路径计算正确:
// 在计算projectRoot后添加 dockerfileFullPath := filepath.Join(projectRoot, "apps", input.dockerImagePath, input.dockerfileName) if _, err := os.Stat(dockerfileFullPath); err != nil { return nil, fmt.Errorf("Dockerfile not found at %s: %w", dockerfileFullPath, err) }
如果这里报错,说明路径计算有误,需要重新调整projectRoot的计算逻辑。
2. 使用绝对路径指定Dockerfile
尝试直接传入Dockerfile的绝对路径给DockerBuildArgs,避免相对路径的解析问题:
dockerfileAbsPath := filepath.Join(projectRoot, "apps", input.dockerImagePath, input.dockerfileName) buildArgs := &docker.DockerBuildArgs{ Context: pulumi.String(projectRoot), Dockerfile: pulumi.String(dockerfileAbsPath), ExtraOptions: pulumi.StringArray{ pulumi.String("--platform=linux/amd64"), }, Target: pulumi.String("production"), }
3. 检查.dockerignore配置
确认项目根目录的.dockerignore文件没有排除apps目录,如果存在类似apps/的规则,会导致Docker在上下文加载时跳过该目录,引发找不到文件的错误。
4. 显式指定相对路径前缀
在Dockerfile路径前添加./,明确是相对于Context的相对路径:
Dockerfile: pulumi.String(filepath.Join("./apps", input.dockerImagePath, "Dockerfile")),
内容的提问来源于stack exchange,提问作者Jose A
相关产品推荐
相关产品推荐

