使用Golang+WASM开发应用在Zeit/Now平台部署遇到的问题

使用Golang+WASM开发应用在Zeit/Now平台部署遇到的问题 如果你还没看过的话,Brian Ketelsen 的这篇文章非常值得一读:https://brianketelsen.com/web-assembly-and-go-a-look-to-the-future/。我已经成功构建了文章中链接的 MarkdownVecty 应用(https://github.com/bketelsen/wasmplay/tree/master/markdownvecty),能在浏览器中运行真正的 Golang/WASM 应用确实令人兴奋。

随后我思考是否可以通过 Zeit/Now 将这个应用部署到云端。部署输出简单的 Golang 应用是完全可行的,正如 @lukemorton 的这个页面所示:https://www.lukemorton.co.uk/thoughts/2017-01-15-deploying-go-on-zeit-now。

有没有熟悉 Golang + Zeit/Now 的朋友(不一定要懂 WASM,我认为问题不在这里)能查看我在 Slack 的 Zeit/Now 频道中的留言?我感觉离解决方案很近了,但目前就是差那么一点!

这是我在 Slack 中的留言链接:https://zeit-community.slack.com/archives/C1TMPLCKS/p1530218186000512

非常感谢大家能提供的任何帮助!


更多关于使用Golang+WASM开发应用在Zeit/Now平台部署遇到的问题的实战教程也可以访问 https://www.itying.com/category-94-b0.html

18 回复

太棒了!我会去看看的。谢谢!

更多关于使用Golang+WASM开发应用在Zeit/Now平台部署遇到的问题的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


这听起来是个好建议,非常感谢!我现在正深入研究一些Logic Pro的技术内容,所以明天会仔细研究您的话。

我稍后会回来。我会尝试你修改后的Dockerfile,看看进展如何。非常感谢你到目前为止的帮助!

还有一个问题。我尝试查阅官方Docker文档,但理解起来不太顺利。有没有其他能快速轻松了解Dockerfile工作原理的途径?

进一步查看发现benweissmann在这个讨论中展示了如何使用go dep工具实现此功能:https://github.com/golang/dep/issues/1374

func main() {
    fmt.Println("hello world")
}

再次感谢Curt!说实话,现在对我来说有点晚了,我明天早上会尝试实践你的建议!

我自己都没注意到这一点。这让事情变得格外奇怪,不是吗?

等等,错误信息显示问题出在服务器包中的 markdown.go 文件里,但根据行号来看,这似乎不是服务器代码。

我看不到你在 Slack 上的消息,因为我不在那个频道。请问你是要部署一个 Go 服务器来提供 wasm 页面,还是仅仅想用 now 来托管编译 wasm 生成的静态 HTML 和二进制输出?

网络上存在大量水平各异、质量参差不齐的博客文章。

我个人主要从Dockerfile参考文档Dockerfile编写最佳实践中学习了大部分知识。

我认为你需要通过使用 go get 命令来安装这些依赖项,或者对要迁移的文件单独运行 go install。因为如果我没记错的话,这些内容会被添加到 go/bin 目录中,而这实际上是编译器在导入包时使用的内容。

func main() {
    fmt.Println("hello world")
}

容器在隔离环境中运行,这是 Docker 的核心特性,因此它会使用自己的 $GOPATH

默认的 golang:* 容器在其定义的 Dockerfile 中都设置了 ENV GOPATH=/go

“Docker 工作空间”是指执行 docker build <路径> 时指定的文件夹。

我的 Dockerfile 感觉不对劲并不令人意外!目前我确实对 Dockerfile 相关的某些概念理解不足。其中一个问题是:Docker 使用哪个 $GOPATH 设置,是我本地的 $GOPATH 还是其他设置?

你提到“你的 docker 上下文根目录位于 $GOPATH/src/github.com/carlca”。如果我想更改这个设置,该怎么做?特别是考虑到我本地的 $GOPATH/Users/carlca/Code/go

我认为如果可能的话,我宁愿避免使用 go dep。按照目前的发展趋势,如果必须使用依赖管理工具,我更倾向于以某种方式使用 vgo

此外,经过反思,我完全不明白为什么会出现那些依赖错误。server.go 代码在源码层面根本没有引用 wasm 相关内容。为什么运行 RUN go install github.com/carlca/server 会涉及这些依赖?我们在这里要做的显然只是让 server.go 服务一个预编译的实体,即 example.wasm

RUN go install github.com/carlca/server

这确实很奇怪。从最初的Dockerfile来看,我找不到任何Docker Go编译器应该尝试构建markdown.go的理由…

FROM golang:alpine
ADD . /Users/carlca/code/go/src/github.com/carlca/wasm/first/wasmplay/markdownvecty/index.html
ADD . /Users/carlca/code/go/src/github.com/carlca/wasm/first/wasmplay/markdownvecty/example.wasm
ADD . /go/src/github.com/carlca/server
RUN go install github.com/carlca/server
CMD ["/go/bin/server"]
EXPOSE 3000

我对你的 now 命令一无所知。所以在进行任何其他操作之前,请确保 docker build 在你的系统上正常运行,然后你可以调试实际的部署。

正如我之前所说,你的 docker 上下文需要位于 ~/code/go/src/github.com/carlca,而不是更深的目录。但由于我不了解 now,我不能确定这是否是问题所在。

因此,请尝试从该路径构建你的镜像。确保 Dockerfile 位于正确的位置,或使用 -f 开关指定其位置。

但老实说,我甚至不确定这个边界是否选择得当。你真的希望将应用程序拆分到两个 git 仓库中吗?

此外,我强烈建议你完成一个合适的 docker 教程/演练,不涉及任何 Go 特定内容(最好除了少量包管理和输出一些静态文本文件之类的内容外,不做任何特定操作)。

然后在没有 docker 参与的情况下处理 Go 相关事务。如果你有多余的硬件,甚至可以尝试将二进制文件部署到另一台计算机上并在那里进行测试。

当这两项都成功后,你可以尝试将它们结合起来并部署到 docker 容器中。如果在本地运行正常,你就可以尝试在提供商添加的特定流程基础上进行操作。

你的Dockerfile感觉有问题。

你创建了三个目录:/Users/carlca/code/go/src/github.com/carlca/wasm/first/wasmplay/markdownvecty/index.html/Users/carlca/code/go/src/github.com/carlca/wasm/first/wasmplay/markdownvecty/example.wasm/go/src/github.com/carlca/server,所有这些目录都包含来自Docker上下文的所有内容。

我确信这不是你想要的。

在之前的迭代中,你甚至将代码拉取到了Go期望找到引用依赖项的位置。

一个更合适的Dockerfile可能是这样的:

FROM golang:alpine AS build
ADD wasm/first/wasmplay/markdownvecty/ /Users/carlca/code/go/src/github.com/carlca/wasm/first/wasmplay/markdownvecty/
ADD server /go/src/github.com/carlca/server

RUN go get github.com/carlca/server && go install github.com/carlca/server

FROM alpine:latest
COPY --from=build /go/bin/server /go/bin/server

EXPOSE 3000

CMD ["/go/bin/server"]

这将生成一个更小的镜像。

不过,它期望你的Docker上下文根目录位于$GOPATH/src/github.com/carlca,这可能也不是你想要的,但由于你目前将应用程序结构分为两个独立的仓库,这是你唯一的选择。

再次尝试了您的修改,但出现了以下错误信息:

┌─( ~/code/go/src/github.com/carlca/wasm/first/wasmplay/markdownvecty )────────────────────────────────────────────────────────────────────( @Carls-Mac-Pro )─┐
└─❱❱❱ now                                                                                                                                        +919 15:47 ❰─┘
> Deploying ~/code/go/src/github.com/carlca/wasm/first/wasmplay/markdownvecty under carl.caulkett@gmail.com
> Your deployment's code and logs will be publicly accessible because you are subscribed to the OSS plan.

> NOTE: You can use `now --public` or upgrade your plan (https://zeit.co/account/plan) to skip this prompt
> Synced 1 file (940B) [840ms]
> https://markdownvecty-dygwgkhowq.now.sh [in clipboard] (bru1) [4s]
> Building…
> ▲ docker build
Sending build context to Docker daemon 3.892 MBkB
> Step 1/8 : FROM golang:alpine AS build
>  ---> 22e024490b47
> Step 2/8 : ADD wasm/first/wasmplay/markdownvecty/ /Users/carlca/code/go/src/github.com/carlca/wasm/first/wasmplay/markdownvecty/
> ADD failed: stat /var/lib/docker/tmp/docker-builder086580357/wasm/first/wasmplay/markdownvecty: no such file or directory
> Error! Build failed

您提到我目前将应用程序结构分成了两个独立的代码库。我完全不受这种方法的限制。如果有任何改进建议,将不胜感激!

在Zeit/Now平台上部署Golang+WASM应用确实会遇到一些特殊配置问题。从你的描述看,主要问题可能在于WASM文件的正确部署和服务配置。

以下是完整的解决方案:

1. 项目结构和配置

首先确保你的项目结构正确:

.
├── main.go
├── wasm_exec.js
├── main.wasm
├── index.html
└── now.json

2. Go代码示例 (main.go)

package main

import (
    "fmt"
    "syscall/js"
)

func main() {
    c := make(chan struct{}, 0)
    
    // 注册Go函数到全局,供JS调用
    js.Global().Set("goAdd", js.FuncOf(add))
    
    fmt.Println("WASM Go Application Initialized")
    <-c
}

func add(this js.Value, args []js.Value) interface{} {
    if len(args) < 2 {
        return 0
    }
    return args[0].Int() + args[1].Int()
}

构建WASM文件:

GOOS=js GOARCH=wasm go build -o main.wasm main.go
cp "$(go env GOROOT)/misc/wasm/wasm_exec.js" .

3. HTML文件 (index.html)

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <script src="wasm_exec.js"></script>
</head>
<body>
    <h1>Go WASM App</h1>
    <div id="output"></div>
    
    <script>
        const go = new Go();
        WebAssembly.instantiateStreaming(fetch("main.wasm"), go.importObject)
            .then((result) => {
                go.run(result.instance);
                // 调用Go函数
                const result = goAdd(5, 3);
                document.getElementById('output').innerHTML = 
                    `5 + 3 = ${result}`;
            });
    </script>
</body>
</html>

4. Now平台配置 (now.json)

{
  "version": 2,
  "builds": [
    {
      "src": "*.html",
      "use": "@now/static"
    },
    {
      "src": "*.wasm",
      "use": "@now/static",
      "config": {
        "headers": {
          "Content-Type": "application/wasm"
        }
      }
    },
    {
      "src": "*.js",
      "use": "@now/static"
    }
  ],
  "routes": [
    {
      "src": "/(.*)",
      "dest": "/index.html"
    }
  ]
}

5. 部署命令

now --prod

关键点说明:

  • WASM文件必须设置正确的MIME类型:application/wasm
  • 确保wasm_exec.js与WASM文件在同一目录
  • Now平台使用V2配置,通过now.json定义构建规则
  • 静态文件服务使用@now/static构建器

这个配置应该能解决你在Zeit/Now频道中提到的问题。部署成功后,应用将通过Now的CDN提供服务,WASM模块能够正常加载和执行。

回到顶部