Golang 및 WebAssembly의 첫 번째 단계
8023 단어 opensourcelinuxprogramminggo
한 가지 옵션은 바이너리를
CGO_ENABLED=1
(어쨌든 기본 동작)로 컴파일하고 라이브러리(이 경우 유효성 검사기)를 동적으로 로드하는 것입니다.so
파일을 생성할 수 있습니다. so
파일을 보장하는 것은 악몽이 될 것입니다. 새 파일은 실행 중인 컨테이너의 디버깅이 필요합니다쿠버네티스 세계의 대안 솔루션은 Pod의 각각에 대해 사이드카로 작은 유닉스 소켓 기반 HTTP 서비스를 생성하는 것이지만 복잡성 때문에 이 방향으로 이동하고 싶지 않았습니다.
세 번째 옵션은 유효성 검사기를 WebAssembly, 특히 WASI 모듈로 실행하기 위해 Go 코드를 포주하는 것입니다.
WASM과 WASI의 차이점은 무엇입니까? 간단히 말해서 WASM은 웹용이며 우리에게 필요한 핵심 기능인 함수 호출을 지원하지 않습니다.
CGO_ENABLED=1
가 필요하지만 제한된 수의 라이브러리에만 의존합니다base64
인코딩이 지원되지 않으므로 내장 파서를 잊어버려야 합니다내가 가장 좋아하는 코드 타임:
import (
"fmt"
"os"
"github.com/valyala/fastjson"
)
func main() {}
//export IsStorageClassValid
func IsStorageClassValid() {
json := []byte(os.Getenv("STORAGE_CLASS_JSON"))
if fastjson.Exists(json, "volumeBindingMode") && fastjson.GetString(json, "volumeBindingMode") != "WaitForFirstConsumer" {
fmt.Fprint(os.Stderr, "only volumeBindingMode WaitForFirstConsumer is supported")
fmt.Fprint(os.Stdout, false)
return
}
fmt.Fprint(os.Stdout, true)
}
func main() {}
가 필요합니다함수
//export
가 필수입니다.모듈을 컴파일해봅시다.
go mod init
docker run -v $(PWD):/go/src/ebs.csi.aws.com -w /go/src/ebs.csi.aws.com tinygo/tinygo:0.23.0 bash -c "go mod tidy && tinygo build -o main.wasm -target wasi --no-debug main.go"
이야기의 다른 측면을 구현할 시간입니다. Go용 WebAssembly 런타임을 찾았습니다. Wasmer-go은 Wasmer을 기반으로 하는 Go용 완전하고 성숙한 WebAssembly 런타임입니다.
// DriversDir driver location, configure with -ldflags -X github.com/ondat/discoblocks/pkg/drivers.DriversDir=/yourpath
var DriversDir = "/drivers"
func init() {
files, err := os.ReadDir(filepath.Clean(DriversDir))
if err != nil {
log.Fatal(fmt.Errorf("unable to load drivers: %w", err))
}
for _, file := range files {
if !file.IsDir() {
continue
}
driverPath := fmt.Sprintf("%s/%s/main.wasm", DriversDir, file.Name())
if _, err := os.Stat(driverPath); err != nil {
log.Printf("unable to found main.wasm for %s: %s", file.Name(), err.Error())
continue
}
wasmBytes, err := os.ReadFile(filepath.Clean(driverPath))
if err != nil {
log.Fatal(fmt.Errorf("unable to load driver content for %s: %w", driverPath, err))
}
engine := wasmer.NewEngine()
store := wasmer.NewStore(engine)
module, err := wasmer.NewModule(store, wasmBytes)
if err != nil {
log.Fatal(fmt.Errorf("unable to compile module %s: %w", driverPath, err))
}
drivers[file.Name()] = &Driver{
store: store,
module: module,
}
}
}
var drivers = map[string]*Driver{}
// GetDriver returns given service
func GetDriver(name string) *Driver {
return drivers[name]
}
// Driver is the bridge to WASI modules
type Driver struct {
store *wasmer.Store
module *wasmer.Module
}
// IsStorageClassValid validates StorageClass
func (d *Driver) IsStorageClassValid(sc *storagev1.StorageClass) (bool, error) {
rawSc, err := json.Marshal(sc)
if err != nil {
return false, fmt.Errorf("unable to parse StorageClass: %w", err)
}
wasiEnv, instance, err := d.init(map[string]string{
"STORAGE_CLASS_JSON": string(rawSc),
})
if err != nil {
return false, fmt.Errorf("unable to init instance: %w", err)
}
isStorageClassValid, err := instance.Exports.GetRawFunction("IsStorageClassValid")
if err != nil {
return false, fmt.Errorf("unable to find IsStorageClassValid: %w", err)
}
_, err = isStorageClassValid.Native()()
if err != nil {
return false, fmt.Errorf("unable to call IsStorageClassValid: %w", err)
}
errOut := string(wasiEnv.ReadStderr())
if errOut != "" {
return false, fmt.Errorf("function error IsStorageClassValid: %s", errOut)
}
resp, err := strconv.ParseBool(string(wasiEnv.ReadStdout()))
if err != nil {
return false, fmt.Errorf("unable to parse output: %w", err)
}
return resp, nil
}
func (d *Driver) init(envs map[string]string) (*wasmer.WasiEnvironment, *wasmer.Instance, error) {
builder := wasmer.NewWasiStateBuilder("wasi-program").
CaptureStdout().CaptureStderr()
for k, v := range envs {
builder = builder.Environment(k, v)
}
wasiEnv, err := builder.Finalize()
if err != nil {
return nil, nil, fmt.Errorf("unable to build module: %w", err)
}
importObject, err := wasiEnv.GenerateImportObject(d.store, d.module)
if err != nil {
return nil, nil, fmt.Errorf("unable to generate imports: %w", err)
}
instance, err := wasmer.NewInstance(d.module, importObject)
if err != nil {
return nil, nil, fmt.Errorf("unable to create instance: %w", err)
}
start, err := instance.Exports.GetWasiStartFunction()
if err != nil {
return nil, nil, fmt.Errorf("unable to get start: %w", err)
}
_, err = start()
if err != nil {
return nil, nil, fmt.Errorf("unable to start instance: %w", err)
}
return wasiEnv, instance, nil
}
발신자 측.
driver := drivers.GetDriver(storageClass.Provisioner)
if driver == nil {
return errors.New("driver not found")
}
valid, err := driver.IsStorageClassValid(&storageClass)
if err != nil {
return fmt.Errorf("failed to call driver: %w", err)
} else if !valid {
return fmt.Errorf("invalid StorageClass: %w", err)
}
한 가지 더 있습니다. 모든 것을 컨테이너 이미지로 배송하세요.
FROM tinygo/tinygo:0.23.0 as drivers
COPY ebs.csi.aws.com/ /go/src/ebs.csi.aws.com
RUN cd /go/src/ebs.csi.aws.com ; go mod tidy && tinygo build -o main.wasm -target wasi --no-debug main.go
...
FROM redhat/ubi8-micro:8.6
COPY --from=drivers /go/src /drivers
COPY --from=builder /go/pkg/mod/github.com/wasmerio/[email protected]/wasmer/packaged/lib/linux-amd64/libwasmer.so /lib64
참고로 바이너리가 단일 바이너리로 정적으로 컴파일되지 않기 때문에
scratch
또는 distroless
이미지를 기본으로 사용할 수 없습니다.그게 다에요 여러분!!!
Reference
이 문제에 관하여(Golang 및 WebAssembly의 첫 번째 단계), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/mhmxs/first-steps-with-golang-and-webassembly-4pkg텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)