1 - 准备工作

进行Dapr开发前的准备工作

1.1 - Fork Dapr相关的仓库

Fork Dapr相关的仓库,以便修改和提交PR

https://github.com/dapr/ 组织下的各个需要用到的仓库都 fork 一遍,如:

以及一些部分同学可能不需要关注的仓库:

如果已经 fork 了,注意 master 分支到最新(GitHub 页面上点 “Fetch upstream” 即可)。

注意:绝对不要在 master 分支上直接修改代码。

1.2 - 安装golang

安装golang

版本要求

dapr 对 golang 的版本一直要求比较高,基本上是用最新版本的 golang。

具体版本要求可以查看 dapr 下的 go.mod 中的要求,如:

https://github.com/dapr/dapr/blob/master/go.mod

module github.com/dapr/dapr

go 1.20

require (......)

下载安装golang

在golang官方下载地址下载安装对应的版本即可。

https://golang.org/dl/

安装方式参考官方安装文档:

https://go.dev/doc/install

1.3 - 安装jdk/maven等

[可选]安装jdk/maven等Java开发工具

如果需要开发 dapr java-sdk,则需要安装 Java SDK。

为了方便在多个 JDK 版本中切换,建议采用 sdkman 来管理 JDK 版本。

安装 sdkman

参考:

判断需要的 jdk 版本

参考java sdk 项目的 maven pom 文件设定:

https://github.com/dapr/java-sdk/blob/master/pom.xml

对 java source 和 target 的要求都是8,也就是 Java 8

    <maven.compiler.source>8</maven.compiler.source>
    <maven.compiler.target>8</maven.compiler.target>

安装 jdk 版本

TBD

sdk list java
sdk install java 11.0.20-zulu

安装 mvn

1.4 - 安装配置IDE

安装配置用于dapr开发的IDE

goland

IDEA 系列,一直都很好用。我长期使用 goland。

但最近为了 remote ssh改用vs code了。

备注: IDEA 系列的 remote ssh 模式是真的很难用

vs code

为了更好的利用 macbook pro m1 max ,remote ssh模式成为刚需。

在 IDEA remote ssh 不给力的情况下,只好改用 vs code。

VS code 的 remote ssh 是真方便,相比之下 IDEA 的复杂设置强太多了。

1.5 - 为lint做准备

安装配置 golangci-lint、gofumpt、goimports

golangci-lint

amd64 机器上安装

适用于 amd64 linux 和 macOS。

dapr提供了 make lint target 来执行 golangci-lint, 如果没有安装 golangci-lint 则会报错:

$ cd dapr
$ make lint       
golangci-lint run --timeout=20m
make: golangci-lint: No such file or directory
make: *** [Makefile:341: lint] Error 127

之前 dapr 用的是 v1.31 老版本,在2022年5月之后, dapr CI 中采用的是最新版本,具体是 golangci-lint v1.51.2

TIPs: 如何知道 dapr 目前采用的是哪个版本的 golangci-lint?

请查看 dapr 仓库下的 Makefile 文件,找到下述内容:

Please use golangci-lint version v1.51.2 , otherwise you might encounter errors.

查看 .github/workflows/dapr.yml 文件,找到下述内容:

GOLANGCILINT_VER: “v1.51.2”

安装方式参考 https://golangci-lint.run/usage/install/ 。在release页面找到对应版本,主要不要直接安装最新版本:

https://github.com/golangci/golangci-lint/releases/tag/v1.51.2

linux下执行如下命令:

$ curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.51.2 # 特别注意这里一定要指定正确的版本
golangci/golangci-lint info checking GitHub for tag 'v1.51.2'
golangci/golangci-lint info installed /home/sky/work/soft/gopath/bin/golangci-lint


$ golangci-lint --version
golangci-lint has version 1.51.2 built from 3e8facb4 on 2023-02-19T21:43:54Z

m1 macbook上安装

在 m1 macbook 上, dapr之前使用的 1.31 版本发布较早,没有提供对 m1 (也就是darwin-arm64)的支持,但最新的dapr改用 1.45.2 版本之后就支持 arm64 了,所以可以用同样的方式安装:

$ curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.52.1
golangci/golangci-lint info checking GitHub for tag 'v1.52.1'
golangci/golangci-lint info found version: 1.52.1 for v1.52.1/linux/amd64
golangci/golangci-lint info installed /home/sky/work/soft/gopath/bin/golangci-lint
 
$ golangci-lint --version
golangci-lint has version 1.52.1 built with go1.20.2 from d92b38cc on 2023-03-21T19:48:38Z

注意:golangci-lint 新版本似对 go 有版本要求,如果遇到报错,如我在 golang 1.17 版本下运行会报错:

$ golangci-lint --version

panic: load embedded ruleguard rules: rules/rules.go:13: can't load fmt

goroutine 1 [running]:
github.com/go-critic/go-critic/checkers.init.22()
	github.com/go-critic/go-critic@v0.6.2/checkers/embedded_rules.go:46 +0x494

$ go version  
go version go1.17.8 darwin/arm64

升级 golang 到新版本如 1.18 就正常了。

gofumpt

运行 lint 之后如果发现 File is not gofumpt-ed

$ make lint
golangci-lint run --timeout=20m

tests/perf/utils/grpc_helpers.go:4               gofumpt    File is not `gofumpt`-ed
tests/perf/utils/grpc_helpers.go:9               gofumpt    File is not `gofumpt`-ed

则需要安装 gofumpt 进行文件格式,参考 https://github.com/mvdan/gofumpt

$ go install mvdan.cc/gofumpt@latest
go: downloading golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde
go: downloading golang.org/x/sys v0.0.0-20220829200755-d48e67d00261

然后对有问题的文件执行 gofumpt :

gofumpt -w tests/perf/utils/grpc_helpers.go 

goimports

运行 lint 之后如果发现 File is not goimports-ed

$ make lint
golangci-lint run --timeout=20m

tests/perf/utils/grpc_helpers.go:5               goimports  File is not `goimports`-ed with -local github.com/dapr/

则需要安装 goimports 对import内容进行文件格式:

go get golang.org/x/tools/cmd/goimports

手工执行

$ goimports -e -d -local github.com/dapr/ tests/perf/utils/grpc_helpers.go
diff -u tests/perf/utils/grpc_helpers.go.orig 
tests/perf/utils/grpc_helpers.go
--- tests/perf/utils/grpc_helpers.go.orig       2022-04-08 22:47:08.199473748 +0800
+++ tests/perf/utils/grpc_helpers.go    2022-04-08 22:47:08.199473748 +0800
@@ -4,10 +4,11 @@
        "context"
        "time"
 
-       v1 "github.com/dapr/dapr/pkg/proto/common/v1"
-       runtimev1pb "github.com/dapr/dapr/pkg/proto/runtime/v1"
        "google.golang.org/grpc"
        "google.golang.org/protobuf/types/known/anypb"
+
+       v1 "github.com/dapr/dapr/pkg/proto/common/v1"
+       runtimev1pb "github.com/dapr/dapr/pkg/proto/runtime/v1"
 )
 
 $ goimports -w tests/perf/utils/grpc_helpers.go

配置 goland 自动执行

参考下面文章的建议:

需要修改的地方有几个,打开 goland 的 settings:

  • “Action on Save” 中,勾选 “reformat code” 和 “Optimize imports”

    goland

  • “Code Style” -> “Go” -> “Import” 中,“sorting by” 下拉框默认是 “gofmt”,修改为 “goimports”,然后勾选相关的选项

    goland2

  • “Code Style” -> “Go” -> “other” 中, 勾选 “add a leading space to comments”,这会在注释内容前加一个空格

goland3

配置vs code

参考: Formatting Go code with goimports (hyr.mn)

打开 File -> Preferences -> Settings, 搜索 “format on save”,勾选:

vscode-format-on-save

搜索 go:format,在 format tool 中选择 goimports:

vscode-goimports

注意:

  1. 如果 go:format 搜索时找不到 extends / go,则应该是没有安装 go extensin,或者安装之后没有重启 vs code
  2. 下拉框选 goimports 时如果报错没有安装 goimports,点安装即可

参考资料

1.6 - 安装Protoc

安装Protoc用于从proto文件生成代码

Dapr 新版本中对proto文件的代码生成有了极大改进,非常方便。

Dapr 新版本中proto文件的代码生成可以简单参考官方 README 文件的说明:

https://github.com/dapr/dapr/tree/master/dapr

步骤一:安装Protoc

目前 daprd 要求的版本是 [v3.21.12 ](https://github.com/protocolbuffers/protobuf/releases/tag/v21.12)。

linux-amd64 安装

如果之前安装过其他,则需要删除已经安装的版本:

sudo rm -rf /usr/local/include/google/protobuf/
sudo rm /usr/local/bin/protoc

下载并解压缩之后,按照 readme.txt 文件的提示,复制bin文件和clude目录到合适的位置:

$ wget https://github.com/protocolbuffers/protobuf/releases/download/v21.12/protoc-21.12-linux-x86_64.zip
$ unzip protoc-21.12-linux-x86_64.zip

$ sudo cp -r include/google/ /usr/local/include/
# 需要设置权限可读和可执行,755
$ sudo chmod -R 755 /usr/local/include/google
$ sudo cp bin/protoc /usr/local/bin/
$ sudo chmod +x /usr/local/bin/protoc

验证安装结果:

$ protoc --version
libprotoc 3.21.12

Macos-amd64 安装

备注:不再更新,我已经没有intel cpu的macos了。

Macos-arm64 安装

如果之前安装过其他,则需要删除已经安装的版本:

sudo rm -rf /usr/local/include/google/protobuf/
sudo rm /usr/local/bin/protoc

下载 pr

$ wget https://github.com/protocolbuffers/protobuf/releases/download/v21.12/protoc-21.12-osx-aarch_64.zip
$ unzip protoc-21.12-osx-aarch_64.zip
$ sudo cp -r include/ /usr/local/include/
# 需要设置权限可读和可执行,755
$ sudo chmod -R 755 /usr/local/include/google
$ sudo cp bin/protoc /usr/local/bin/
$ sudo chmod +x /usr/local/bin/protoc

如果遇到macos禁止protoc运行,在设置中找到 “security & Privacy”,会有 protoc 运行的提示,点击容许即可。

protoc 安装完成之后,验证一下版本:

$ protoc --version
libprotoc 3.21.12

步骤二:初始化proto工具

安装 protoc-gen-go 和 protoc-gen-go,dapr的 make file 为此准备了专门的命令 init-proto

# 进入 dapr/dapr 仓库
$ cd dapr 
$ make init-proto
go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.28.1
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.2.0

步骤三:从proto生成代码

终于可以开始正式的代码生成了,dapr的 make file 也为此准备了专门的命令 gen-proto

# 进入 dapr/dapr 仓库
$ cd dapr 
$ make gen-proto
protoc --go_out=. --go_opt=module=github.com/dapr/dapr --go-grpc_out=. --go-grpc_opt=require_unimplemented_servers=false,module=github.com/dapr/dapr ./dapr/proto/common/v1/*.proto
protoc --go_out=. --go_opt=module=github.com/dapr/dapr --go-grpc_out=. --go-grpc_opt=require_unimplemented_servers=false,module=github.com/dapr/dapr ./dapr/proto/internals/v1/*.proto
protoc --go_out=. --go_opt=module=github.com/dapr/dapr --go-grpc_out=. --go-grpc_opt=require_unimplemented_servers=false,module=github.com/dapr/dapr ./dapr/proto/operator/v1/*.proto
protoc --go_out=. --go_opt=module=github.com/dapr/dapr --go-grpc_out=. --go-grpc_opt=require_unimplemented_servers=false,module=github.com/dapr/dapr ./dapr/proto/placement/v1/*.proto
protoc --go_out=. --go_opt=module=github.com/dapr/dapr --go-grpc_out=. --go-grpc_opt=require_unimplemented_servers=false,module=github.com/dapr/dapr ./dapr/proto/runtime/v1/*.proto
protoc --go_out=. --go_opt=module=github.com/dapr/dapr --go-grpc_out=. --go-grpc_opt=require_unimplemented_servers=false,module=github.com/dapr/dapr ./dapr/proto/sentry/v1/*.proto
go mod tidy

执行 git status 命令,对比一下新生成的代码和dapr 仓库中已经保存的代码,如果代码没有改动说明我们的protoc代码生成和dapr项目保持一致了。

常见错误

找不到共享文件

报错如下:

$ make gen-proto

protoc --go_out=. --go_opt=module=github.com/dapr/dapr --go-grpc_out=. --go-grpc_opt=require_unimplemented_servers=false,module=github.com/dapr/dapr ./dapr/proto/common/v1/*.proto
google/protobuf/any.proto: File not found.
dapr/proto/common/v1/common.proto:10:1: Import "google/protobuf/any.proto" was not found or had errors.
dapr/proto/common/v1/common.proto:55:3: "google.protobuf.Any" is not defined.
dapr/proto/common/v1/common.proto:76:3: "google.protobuf.Any" is not defined.
make: *** [Makefile:260:gen-proto-common] 错误 1

这个错误发生的原因通常是 protoc 安装时的 include 文件未能放置好,或者是相关的目录没有权限。经测试验证需要读和可执行权限,因此设置755:

# 需要设置权限可读和可执行,755
$ sudo chmod -R 755 /usr/local/include/google

2 - dapr各项目的构建

进行Dapr开发前进行各项目的基本构建,确保准备工作基本就绪

2.1 - kit项目的构建

dapr/kit项目存放的是公用代码,被dapr/dapr和dapr/conponent-contrib项目使用

在终端中执行以下命令:

make go.mod
make test
make lint
make check-diff

2.2 - components-contrib项目的构建

dapr/components-contrib项目存放的各种组件的代码

components-contrib

在终端中执行以下命令:

make go.mod
make modtidy-all
make test
make lint
make check-mod-diff

备注: conf-tests 和 e2e-tests-zeebe 在本地是跑不起来的。

2.3 - dapr项目的构建

dapr/dapr项目存放的是dapr runtime和dapr控制平面的代码

在终端中构建

在终端中执行以下命令:

make modtidy
make modtidy-all
make test
make lint
make check-diff

备注: conf-tests 和 e2e-tests-zeebe 在本地是跑不起来的。

在vs code中构建

设置 build tag

在 vs code 中打开 dapr/dapr 项目之后,会遇到报错:

vscode-build-error

原因是。MockActors 定义在 pkg/actors/actors_mock.go 这个文件中,但它的 build tag 设置为 unit,而默认 vs code 没有使用 build tag,所以这个 MockActors 文件不在编译范围内,导致使 MockActors 的其他 go 文件报错。

vscode-build-tags

解决方式是设置 vs code 的 build tags(以及test tags),打开 (如果没有就新建)项目根目录下的 .vscoee/settings.json,加入以下内容:

{
    ......
    "go.buildFlags": [
        "-tags=unit,e2e"
    ],
    "go.testTags": "unit,e2e",
}

正常就立即生效,错误消失。如果没有,则重启 vscode 看是否生效。

2.4 - go-sdk项目的构建

dapr/go-sdk项目存放的是golang的SDK代码

在终端中执行以下命令:

make tidy
make test
make lint
make check-diff

2.5 - java-sdk项目的构建

构建java-sdk项目

2.5.1 - Java SDK的proto代码生成

介绍Dapr的Java SDK项目中的proto代码生成

dapr java sdk 仓库 clone下来代码之后,直接用IDE 如 IntelliJ 导入,是会报错的:有部分 dapr 的类找不到。这是因为这些代码是需要从 dapr 的 proto 代码中临时生成然后被项目装载。因此如果要进行 dapr java sdk 项目的开发或者代码阅读,需要从proto文件中生成这些类。

备注:

不同语言的sdk对生成代码的处理方式不同:

  • java sdk:采用的是不保存生成的代码到git仓库,每次需要的时候从proto文件生成,然后以 generated-sources 的方式被挂载到 mvn 项目中。
  • go sdk:有些语言的sdk如 go sdk,是将生成的代码直接提交到git仓库,这样如果不需要重新生成代码,是可以clone下来仓库就直接可以使用和阅读的。

准备protoc等工具

安装Protoc,目前 daprd 要求的版本是 v3.14.0。sdk 和 daprd保持一致。

具体见准备工作中的 安装Protoc

从proto文件生成Java代码

在 dapr java sdk 项目下,有名为 sdk-augogen的子项目,在这里执行 generate-sources 命令就可以生成对应的java类:

$ cd sdk-augogen
$ mvn generate-sources

代码生成的过程有几个主要步骤:

步骤1: 下载proto文件

从命令输出中能看到,maven会下载三个proto文件: common.proto, dapr.proto,appcallback.proto:

[INFO] --- download-maven-plugin:1.6.0:wget (getCommonProto) @ dapr-sdk-autogen ---
[INFO] Downloading: https://raw.githubusercontent.com/dapr/dapr/4a6369caaba9cf46eae9bfa4fa6e76b474854c89/dapr/proto/common/v1/common.proto
[INFO] Downloading: https://raw.githubusercontent.com/dapr/dapr/4a6369caaba9cf46eae9bfa4fa6e76b474854c89/dapr/proto/runtime/v1/dapr.proto
[INFO] Downloading: https://raw.githubusercontent.com/dapr/dapr/4a6369caaba9cf46eae9bfa4fa6e76b474854c89/dapr/proto/runtime/v1/appcallback.proto

对照 sdk-autogen 子项目下的 pom.xml 文件,可以找到相关的maven插件配置:

<plugin>
        <groupId>com.googlecode.maven-download-plugin</groupId>
        <artifactId>download-maven-plugin</artifactId>
        <version>1.6.0</version>
        <executions>
          <execution>
            <id>getCommonProto</id>
            <!-- the wget goal actually binds itself to this phase by default -->
            <phase>initialize</phase>
            <goals>
              <goal>wget</goal>
            </goals>
            <configuration>
              <url>${dapr.proto.baseurl}/common/v1/common.proto</url>
              <outputFileName>common.proto</outputFileName>
              <!-- default target location, just to demonstrate the parameter -->
              <outputDirectory>${protobuf.input.directory}/dapr/proto/common/v1</outputDirectory>
            </configuration>
          </execution>
          <execution>
            <id>getDaprProto</id>
            <!-- the wget goal actually binds itself to this phase by default -->
            <phase>initialize</phase>
            <goals>
              <goal>wget</goal>
            </goals>
            <configuration>
              <url>${dapr.proto.baseurl}/runtime/v1/dapr.proto</url>
              <outputFileName>dapr.proto</outputFileName>
              <!-- default target location, just to demonstrate the parameter -->
              <outputDirectory>${protobuf.input.directory}</outputDirectory>
            </configuration>
          </execution>
          <execution>
            <id>getDaprClientProto</id>
            <!-- the wget goal actually binds itself to this phase by default -->
            <phase>initialize</phase>
            <goals>
              <goal>wget</goal>
            </goals>
            <configuration>
              <url>${dapr.proto.baseurl}/runtime/v1/appcallback.proto</url>
              <outputFileName>appcallback.proto</outputFileName>
              <!-- default target location, just to demonstrate the parameter -->
              <outputDirectory>${protobuf.input.directory}</outputDirectory>
            </configuration>
          </execution>
        </executions>
      </plugin>

在这里特别要注意的是 ${dapr.proto.baseurl},这个属性在根目录下的 pom.xml 中设置,比如我当前版本的值是:

  <dapr.proto.baseurl>
https://raw.githubusercontent.com/dapr/
dapr/4a6369caaba9cf46eae9bfa4fa6e76b474854c89/dapr/proto
  </dapr.proto.baseurl>

这意味着如果要针对某个版本的 proto 文件进行代码生成,则需要修改这里的 ${dapr.proto.baseurl} 的值,比如指向某个分支/tag或者如上面指向某个commit。

步骤2: 生成代码

protoc-jar-maven-plugin 插件负责将前面下载的proto文件生成java 代码,生成之后的代码存在的 target/generated-sources 目录下:

[INFO] --- protoc-jar-maven-plugin:3.11.4:run (default) @ dapr-sdk-autogen ---
[INFO] Protoc version: 3.13.0
protoc-jar: protoc version: 3.13.0, detected platform: osx-x86_64 (mac os x/x86_64)
protoc-jar: cached: /var/folders/vr/8yl21p696dd41c776p67qlkm0000gp/T/protocjar.webcache/com/google/protobuf/protoc/maven-metadata.xml
protoc-jar: cached: /var/folders/vr/8yl21p696dd41c776p67qlkm0000gp/T/protocjar.webcache/com/google/protobuf/protoc/3.13.0/protoc-3.13.0-osx-x86_64.exe
protoc-jar: executing: [/var/folders/vr/8yl21p696dd41c776p67qlkm0000gp/T/protocjar852218627845177615/bin/protoc.exe, --version]
libprotoc 3.13.0

[INFO]     Processing (grpc-java): dapr.proto
[INFO]     Processing (grpc-java): common.proto
[INFO]     Processing (grpc-java): appcallback.proto

[INFO] Adding generated sources (java): /Users/sky/work/code/skyao/java-sdk/sdk-autogen/target/generated-sources
[INFO] Adding generated sources (grpc-java): /Users/sky/work/code/skyao/java-sdk/sdk-autogen/target/generated-sources

[INFO] BUILD SUCCESS

刷新项目,比如 idea 下可以在 java-sdk 根目录下pom.xml上右键 maven -> reload project 。

相关的文件路径小结

一目了然:

遇到过的问题

proto文件缓存

有一个比较坑的事情是,download-maven-plugin 插件在下载文件时,不是每次都去请求,有时会从maven本地缓存中拿文件。

比如我某次被坑的时候,就发现有如下日志:

[INFO] --- download-maven-plugin:1.6.0:wget 
[INFO] Got from cache: /Users/sky/.m2/repository/.cache/download-maven-plugin/common.proto_d701b2bbc1c789dfa25b99c95a8cd8c2
[INFO] Got from cache: /Users/sky/.m2/repository/.cache/download-maven-plugin/dapr.proto_372181342afa932d4684925872c96d22
[INFO] Got from cache: /Users/sky/.m2/repository/.cache/download-maven-plugin/appcallback.proto_e110e7ac8ee53213ecf133574e146d64
[INFO] 

这个缓存会导致拿到的 proto 文件和原来期望的proto文件内容可能不一样,因此后面生成的代码也会有偏差,某些情况下就会出现代码编译不过,错误可能是某个类或者某个方法找不到。

这个时候最简单的办法就是删除这个缓存目录,让 maven 去远程下载,然后再重新生成:

rm -rf ~/.m2/repository/.cache/download-maven-plugin/

mvn generate-sources

Error extracting protoc for version 3.17.3 错误

在配置 Artifactory 开启 mirror 之后,遇到一个奇怪的问题:

[ERROR] Failed to execute goal com.github.os72:protoc-jar-maven-plugin:3.11.4:run (default) on project dapr-sdk-autogen: Error extracting protoc for version 3.17.3: Unsupported platform: protoc-3.17.3-linux-x86_64.exe -> [Help 1]
org.apache.maven.lifecycle.LifecycleExecutionException: Failed to execute goal com.github.os72:protoc-jar-maven-plugin:3.11.4:run (default) on project dapr-sdk-autogen: Error extracting protoc for version 3.17.3
    at org.apache.maven.lifecycle.internal.MojoExecutor.doExecute (MojoExecutor.java:306)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute (MojoExecutor.java:211)
......
Caused by: org.apache.maven.plugin.MojoExecutionException: Error extracting protoc for version 3.17.3
......
Caused by: java.io.FileNotFoundException: Unsupported platform: protoc-3.17.3-linux-x86_64.exe
......

修改 maven settings.xml ,不使用 artifactory 而是直接连接 maven 中央仓库,就可以回避这个问题。

参考: https://github.com/os72/protoc-jar-maven-plugin/issues/68

3 - 构建原生开发环境

构建在本机编码并在本机测试的原生开发环境

3.1 - Linux amd64

在 Linux amd64操作系统上构建本地开发环境,以ubuntu为例

3.1.1 - 开发

在linux平台上进行dapr开发的编码、单元测试、打包等工作

编码

通过 replace 引用修改的代码

以 dapr 仓库 和 components-contrib 仓库为例,如果我们在本地修改了 components-contrib 仓库的代码,然后希望在 dapr 仓库中使用这些修改后的代码,则可以修改 dapr 仓库中的 go.mod 文件,将对 components-contrib 仓库的依赖进行 replace 。

如果目标代码还没有提交到 github,则 replace 为 components-contrib 仓库的本地目录,如:

replace github.com/dapr/components-contrib => /home/sky/work/code/dapr-fork/components-contrib

如果代码已经提交到 github 仓库,则可以指定要 replace 的仓库、分支和commit:

replace github.com/dapr/components-contrib => github.com/skyao/components-contrib v1.7.1-0.20220418003919-48d8dbd6dfc8

后面这个方案适合在提交跨多个仓库的pr时使用。

测试

本地跑单元测试:

make test

e2e 测试和性能测试等运行比较麻烦,单独在测试篇中讲。

打包

构建二进制包

在 dapr/dapr 仓库下执行 make build 命令:

$ make build

CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build  -ldflags="-X github.com/dapr/dapr/pkg/version.gitcommit=b298e16f6bd641545c1b76b074b622578989f171 -X github.com/dapr/dapr/pkg/version.gitversion=v1.7.0-rc.4-34-gb298e16-dirty -X github.com/dapr/dapr/pkg/version.version=edge -X github.com/dapr/kit/logger.DaprVersion=edge -s -w" -o ./dist/linux_amd64/release/daprd ./cmd/daprd/;
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build  -ldflags="-X github.com/dapr/dapr/pkg/version.gitcommit=b298e16f6bd641545c1b76b074b622578989f171 -X github.com/dapr/dapr/pkg/version.gitversion=v1.7.0-rc.4-34-gb298e16-dirty -X github.com/dapr/dapr/pkg/version.version=edge -X github.com/dapr/kit/logger.DaprVersion=edge -s -w" -o ./dist/linux_amd64/release/placement ./cmd/placement/;
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build  -ldflags="-X github.com/dapr/dapr/pkg/version.gitcommit=b298e16f6bd641545c1b76b074b622578989f171 -X github.com/dapr/dapr/pkg/version.gitversion=v1.7.0-rc.4-34-gb298e16-dirty -X github.com/dapr/dapr/pkg/version.version=edge -X github.com/dapr/kit/logger.DaprVersion=edge -s -w" -o ./dist/linux_amd64/release/operator ./cmd/operator/;
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build  -ldflags="-X github.com/dapr/dapr/pkg/version.gitcommit=b298e16f6bd641545c1b76b074b622578989f171 -X github.com/dapr/dapr/pkg/version.gitversion=v1.7.0-rc.4-34-gb298e16-dirty -X github.com/dapr/dapr/pkg/version.version=edge -X github.com/dapr/kit/logger.DaprVersion=edge -s -w" -o ./dist/linux_amd64/release/injector ./cmd/injector/;
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build  -ldflags="-X github.com/dapr/dapr/pkg/version.gitcommit=b298e16f6bd641545c1b76b074b622578989f171 -X github.com/dapr/dapr/pkg/version.gitversion=v1.7.0-rc.4-34-gb298e16-dirty -X github.com/dapr/dapr/pkg/version.version=edge -X github.com/dapr/kit/logger.DaprVersion=edge -s -w" -o ./dist/linux_amd64/release/sentry ./cmd/sentry/;

生成的二进制文件存放在 dist/linux_amd64 目录下:

dapr-build-files

构建dapr容器

先设置 dapr 相关的环境变量:

export DAPR_TAG=dev
export DAPR_REGISTRY=docker.io/skyao

在 dapr/dapr 仓库下执行 make docker-build 命令:

$ make docker-build

Building docker.io/skyao/dapr:dev docker image ...
docker build --build-arg PKG_FILES=* -f ./docker/Dockerfile ./dist/linux_amd64/release -t docker.io/skyao/dapr:dev-linux-amd64
Sending build context to Docker daemon  236.4MB
Step 1/4 : FROM gcr.io/distroless/static:nonroot
 ---> bbd57f9cdb20
Step 2/4 : ARG PKG_FILES
 ---> Using cache
 ---> 0072a00d6320
Step 3/4 : WORKDIR /
 ---> Using cache
 ---> 01a8cf2e1345
Step 4/4 : COPY /$PKG_FILES /
 ---> Using cache
 ---> 42555e58ba97
Successfully built 42555e58ba97
Successfully tagged skyao/dapr:dev-linux-amd64
docker build --build-arg PKG_FILES=daprd -f ./docker/Dockerfile ./dist/linux_amd64/release -t docker.io/skyao/daprd:dev-linux-amd64
Sending build context to Docker daemon  236.4MB
Step 1/4 : FROM gcr.io/distroless/static:nonroot
 ---> bbd57f9cdb20
Step 2/4 : ARG PKG_FILES
 ---> Using cache
 ---> 0072a00d6320
Step 3/4 : WORKDIR /
 ---> Using cache
 ---> 01a8cf2e1345
Step 4/4 : COPY /$PKG_FILES /
 ---> Using cache
 ---> 3d189af067d0
Successfully built 3d189af067d0
Successfully tagged skyao/daprd:dev-linux-amd64
docker build --build-arg PKG_FILES=placement -f ./docker/Dockerfile ./dist/linux_amd64/release -t docker.io/skyao/placement:dev-linux-amd64
Sending build context to Docker daemon  236.4MB
Step 1/4 : FROM gcr.io/distroless/static:nonroot
 ---> bbd57f9cdb20
Step 2/4 : ARG PKG_FILES
 ---> Using cache
 ---> 0072a00d6320
Step 3/4 : WORKDIR /
 ---> Using cache
 ---> 01a8cf2e1345
Step 4/4 : COPY /$PKG_FILES /
 ---> Using cache
 ---> 18f7e2261d82
Successfully built 18f7e2261d82
Successfully tagged skyao/placement:dev-linux-amd64
docker build --build-arg PKG_FILES=sentry -f ./docker/Dockerfile ./dist/linux_amd64/release -t docker.io/skyao/sentry:dev-linux-amd64
Sending build context to Docker daemon  236.4MB
Step 1/4 : FROM gcr.io/distroless/static:nonroot
 ---> bbd57f9cdb20
Step 2/4 : ARG PKG_FILES
 ---> Using cache
 ---> 0072a00d6320
Step 3/4 : WORKDIR /
 ---> Using cache
 ---> 01a8cf2e1345
Step 4/4 : COPY /$PKG_FILES /
 ---> Using cache
 ---> 10523d0ff4a5
Successfully built 10523d0ff4a5
Successfully tagged skyao/sentry:dev-linux-amd64

生成的镜像文件可以通过 docker images 命令查看:

$ docker images
REPOSITORY                                 TAG               IMAGE ID       CREATED        SIZE
skyao/sentry                               dev-linux-amd64   10523d0ff4a5   2 days ago     39.4MB
skyao/placement                            dev-linux-amd64   18f7e2261d82   2 days ago     17MB
skyao/daprd                                dev-linux-amd64   3d189af067d0   2 days ago     113MB
skyao/dapr                                 dev-linux-amd64   42555e58ba97   2 days ago     239MB

3.1.2 - 原生运行daprd

在linux平台上原生运行daprd

运行二进制文件

daprd的二进制文件可以来自从源码编译的 dist/release 目录下,也可以来自通过 dapr cli 安装的 daprd。大多数情况下我们在开发时是运行刚编译出来的daprd 二进制文件。

Daprd 在运行时,通常需要和业务应用一起启动,需要和配置的外部组件相互连接,有些功能还需要 dapr 控制面的配合。

最小依赖运行

这是启动 daprd 最简单的方式,只需要指定 app-idcomponents-path (里面为空,不配置component):

$ ./dist/linux_amd64/release/daprd --components-path=./dist/components --app-id=app1

INFO[0000] starting Dapr Runtime -- version edge -- commit b298e16f6bd641545c1b76b074b622578989f171  app_id=app1 instance=skywork scope=dapr.runtime type=log ver=edge
INFO[0000] log level set to: info                        app_id=app1 instance=skywork scope=dapr.runtime type=log ver=edge
INFO[0000] metrics server started on :9090/              app_id=app1 instance=skywork scope=dapr.metrics type=log ver=edge
INFO[0000] loading default configuration                 app_id=app1 instance=skywork scope=dapr.runtime type=log ver=edge
INFO[0000] standalone mode configured                    app_id=app1 instance=skywork scope=dapr.runtime type=log ver=edge
INFO[0000] app id: app1                                  app_id=app1 instance=skywork scope=dapr.runtime type=log ver=edge
INFO[0000] mTLS is disabled. Skipping certificate request and tls validation  app_id=app1 instance=skywork scope=dapr.runtime type=log ver=edge
INFO[0000] local service entry announced: app1 -> 192.168.100.10:35921  app_id=app1 instance=skywork scope=dapr.contrib type=log ver=edge
INFO[0000] Initialized name resolution to mdns           app_id=app1 instance=skywork scope=dapr.runtime type=log ver=edge
INFO[0000] loading components                            app_id=app1 instance=skywork scope=dapr.runtime type=log ver=edge
INFO[0000] waiting for all outstanding components to be processed  app_id=app1 instance=skywork scope=dapr.runtime type=log ver=edge
INFO[0000] all outstanding components processed          app_id=app1 instance=skywork scope=dapr.runtime type=log ver=edge
INFO[0000] gRPC proxy enabled                            app_id=app1 instance=skywork scope=dapr.runtime type=log ver=edge
INFO[0000] enabled gRPC tracing middleware               app_id=app1 instance=skywork scope=dapr.runtime.grpc.api type=log ver=edge
INFO[0000] enabled gRPC metrics middleware               app_id=app1 instance=skywork scope=dapr.runtime.grpc.api type=log ver=edge
INFO[0000] API gRPC server is running on port 50001      app_id=app1 instance=skywork scope=dapr.runtime type=log ver=edge
INFO[0000] enabled metrics http middleware               app_id=app1 instance=skywork scope=dapr.runtime.http type=log ver=edge
INFO[0000] enabled tracing http middleware               app_id=app1 instance=skywork scope=dapr.runtime.http type=log ver=edge
INFO[0000] http server is running on port 3500           app_id=app1 instance=skywork scope=dapr.runtime type=log ver=edge
INFO[0000] The request body size parameter is: 4         app_id=app1 instance=skywork scope=dapr.runtime type=log ver=edge
INFO[0000] enabled gRPC tracing middleware               app_id=app1 instance=skywork scope=dapr.runtime.grpc.internal type=log ver=edge
INFO[0000] enabled gRPC metrics middleware               app_id=app1 instance=skywork scope=dapr.runtime.grpc.internal type=log ver=edge
INFO[0000] internal gRPC server is running on port 35921  app_id=app1 instance=skywork scope=dapr.runtime type=log ver=edge
WARN[0000] app channel is not initialized. did you make sure to configure an app-port?  app_id=app1 instance=skywork scope=dapr.runtime type=log ver=edge
WARN[0000] failed to init actors: no actor state store defined  app_id=app1 instance=skywork scope=dapr.runtime type=log ver=edge
WARN[0000] failed to read from bindings: app channel not initialized   app_id=app1 instance=skywork scope=dapr.runtime type=log ver=edge
INFO[0000] dapr initialized. Status: Running. Init Elapsed 2.781127ms  app_id=app1 instance=skywork scope=dapr.runtime type=log ver=edge

如果需要启动指定的 component ,则需要在通过 --components-path 指定的目录下(如./dist/components )放置对应的 yaml 文件。

依赖外部组件

当测试某些构建块的功能时,如 state / pubsub 等,流程比较简单,本机开发时只需要在本机将对应的组件的外部依赖启动起来,然后在 daprd 将配置好 component 的 yaml 文件放置在 --components-path 指定的目录下(如./dist/components ) 即可。

需要和业务App互动

TBD

需要dapr控制平面

TBD

在IDE中运行

最小依赖运行

在 IDE (如 goland) 中启动 daprd 的方式是打开 cmd/daprd/main.go 文件,然后运行,注意也要配置最基本的参数如 app-idcomponents-path

以 goland 为例:

goland-run-darpd

运行日志如下:

GOROOT=/usr/local/go #gosetup
GOPATH=/home/sky/work/soft/gopath #gosetup
/usr/local/go/bin/go build -o /tmp/GoLand/___daprd github.com/dapr/dapr/cmd/daprd #gosetup
/tmp/GoLand/___daprd --app-id=app1 --components-path=./dist/components
INFO[0000] starting Dapr Runtime -- version edge -- commit   app_id=app1 instance=skywork scope=dapr.runtime type=log ver=unknown
......

在 IDE 中用这种方式运行 daprd,最大的优势在于可以 debug!

3.1.3 - 在容器中运行daprd

在linux平台上通过容器运行daprd

3.1.4 - 在容器中运行外部组件

在linux平台上通过容器运行daprd需要访问的外部组件

本地开发时,各个组件只需要使用最简单的部署方案即可。

redis

执行下面命令,指定端口映射和访问密码:

docker run -d -p 6379:6379 redis --requirepass "abc123"

可以看到后台启动的 redis 进行:

ps -ef | grep redis
999         3348    3327  0 14:18 ?        00:00:12 redis-server *:6379

然后就可以配置基于 redis 的 component,如 state:

apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
  name: statestore
spec:
  type: state.redis
  version: v1
  initTimeout: 1m
  metadata:
  - name: redisHost
    value: localhost:6379
  - name: redisPassword
    value: abc123
  - name: actorStateStore
    value: true

kafka

kafka 相对 redis 要麻烦一些,因为 kafka 需要 zookeeper。因此需要使用 docker-compose 工具来启动多个 container。

https://github.com/conduktor/kafka-stack-docker-compose

这里有配置好的多种 kafka 方案,我们可以选择最简单的一种,zk-single-kafka-single

$ wget https://github.com/conduktor/kafka-stack-docker-compose/blob/master/zk-single-kafka-single.yml
$ docker-compose -f zk-single-kafka-single.yml up -d

# 可以看到 kafka 和 zeekeeper 在运行
$ ps -ef | grep kafka 
sky         3172    2436  0 18:05 pts/0    00:00:00 /usr/bin/python3 /usr/bin/docker-compose -f zk-single-kafka-single.yml up

sky         3930    3908  1 18:06 ?        00:00:04 java -Xmx512M -Xms512M -server -XX:+UseG1GC -XX:MaxGCPauseMillis=20 -XX:InitiatingHeapOccupancyPercent=35 -XX:+ExplicitGCInvokesConcurrent -XX:MaxInlineLevel=15 -Djava.awt.headless=true -Xlog:gc*:file=/var/log/kafka/zookeeper-gc.log:time,tags:filecount=10,filesize=100M -Dcom.sun.management.jmxremote=true -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Dkafka.logs.dir=/var/log/kafka -Dlog4j.configuration=file:/etc/kafka/log4j.properties -cp /usr/bin/../share/java/kafka/*:/usr/bin/../share/java/confluent-telemetry/* org.apache.zookeeper.server.quorum.QuorumPeerMain /etc/kafka/zookeeper.properties

sky         4114    4090  3 18:06 ?        00:00:10 java -Xmx1G -Xms1G -server -XX:+UseG1GC -XX:MaxGCPauseMillis=20 -XX:InitiatingHeapOccupancyPercent=35 -XX:+ExplicitGCInvokesConcurrent -XX:MaxInlineLevel=15 -Djava.awt.headless=true -Xlog:gc*:file=/var/log/kafka/kafkaServer-gc.log:time,tags:filecount=10,filesize=100M -Dcom.sun.management.jmxremote=true -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Djava.rmi.server.hostname=127.0.0.1 -Dcom.sun.management.jmxremote.local.only=false -Dcom.sun.management.jmxremote.rmi.port=9999 -Dcom.sun.management.jmxremote.port=9999 -Dcom.sun.management.jmxremote.port=9999 -Dkafka.logs.dir=/var/log/kafka -Dlog4j.configuration=file:/etc/kafka/log4j.properties -cp /usr/bin/../share/java/kafka/*:/usr/bin/../share/java/confluent-telemetry/* kafka.Kafka /etc/kafka/kafka.properties

kafka 和 zookeeper 运行的端口信息可以通过下面的命令看到:

$ docker-compose -f zk-single-kafka-single.yml ps
 Name             Command            State                                         Ports                                       
-------------------------------------------------------------------------------------------------------------------------------
kafka1   /etc/confluent/docker/run   Up      0.0.0.0:9092->9092/tcp,:::9092->9092/tcp, 0.0.0.0:9999->9999/tcp,:::9999->9999/tcp
zoo1     /etc/confluent/docker/run   Up      0.0.0.0:2181->2181/tcp,:::2181->2181/tcp, 2888/tcp, 3888/tcp 

kafka 运行在 localhost:9092

然后就可以配置基于 kafka 的 component,如 pubsub:

apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
  name: pubsub
spec:
  type: pubsub.kafka
  version: v1
  metadata:
  - name: brokers
    value: localhost:9092
  - name: consumerGroup
    value: pubsubgroup1
  - name: authRequired
    value: "false"
  - name: initialOffset
    value: oldest
  - name: disableTls
    value: true

参考: https://www.conduktor.io/kafka/how-to-start-kafka-using-docker

3.1.5 - 在k8s上debug控制平面

本地打包可debug的二进制文件和镜像,部署后再以debug的方式启动

参考官方文档:

https://docs.dapr.io/developing-applications/debugging/debug-k8s/debug-dapr-services/

准备工作

打包和推送可debug的镜像

cd dapr
make release GOOS=linux GOARCH=amd64 DEBUG=1

export DAPR_TAG=dev
export DAPR_REGISTRY=docker.io/skyao
docker login
make docker-push DEBUG=1

但 docker-push 速度会很慢,非常拖累速度,本地开发和debug完全没有不要用 docker 的正式 register,完全可以本地启动一个私有的 registery 。

docker private register

运行一下命令来启动一个容器承担 docker private register 的角色,以加速镜像文件推送到 docker register 的速度,避免浪费太多时间在镜像的上传上:

 docker run -d -p 5000:5000 --name registry registry:2 

为了可以使用 http,而不是默认使用 https 来执行镜像推送,需要修改 docker 配置:

sudo vi /etc/docker/daemon.json

增加内容:

{
		......,
        "insecure-registries": [
          "127.0.0.1:5000"
        ]
}

修改之后要重启 docker daemon,否则不生效:

sudo systemctl daemon-reload
sudo systemctl restart docker

然后再重启 registry 容器,否则无法访问:

docker restart registry

这时可以访问 http://127.0.0.1:5000/v2/ 查看是否正常工作。

此时再执行 make docker-push DEBUG=1 就非常快了:

export DAPR_TAG=dev
export DAPR_REGISTRY=127.0.0.1:5000
make docker-push DEBUG=1

参考资料:

准备 debug values 配置文件

准备一个 values-debug-templates.yaml 文件放在 dapr 仓库的 charts/dapr/ 目录下,内容为:

global:
   registry: "docker.io/skyao"
   tag: "dev-linux-amd64"
dapr_operator:
  debug:
    enabled: true
  runAsNonRoot: false
dapr_placement:
  debug:
    enabled: true
  runAsNonRoot: false
dapr_sentry:
  debug:
    enabled: true
  runAsNonRoot: false
dapr_sidecar_injector:
  debug:
    enabled: true
  runAsNonRoot: false
  sidecarRunAsNonRoot: false

一般每次我们只debug 一个控制面组件,因此上面的四段内容我们只需要用对应的其中一段即可。

如果有本地启动 docker private registry, 注意修改这里的 registry 地址:

global:
   registry: "127.0.0.1:5000"
   tag: "dev-linux-amd64"
......

开始 debug

debug sentry

复制 values-debug-templates.yaml 文件,改名 values-debug.yaml ,内容只保留 dapr_sentry 这一段:

global:
   registry: "127.0.0.1:5000"
   tag: "dev-linux-amd64"
dapr_sentry:
  debug:
    enabled: true
  runAsNonRoot: false

安装控制面:

cd dapr
helm install dapr charts/dapr --namespace dapr-system --values charts/dapr/values-debug.yaml --wait

其中 sentry 会以 debug 模式启动,然后等待连接:

k logs -n dapr-system dapr-sentry-7d9898fdb9-rt4kj  
2023-03-29T07:09:47Z warning layer=rpc Listening for remote connections (connections are not authenticated nor encrypted)
2023-03-29T07:09:47Z info layer=debugger launching process with args: [/sentry --log-level info --enable-metrics --metrics-port 9090 --trust-domain cluster.local]
API server listening at: [::]:40000

下面是 debug 模式启动的 sentry pod 的部分内容,可以看到容器的启动命令(/dlv)和参数(exec /sentry):

  dapr-sentry:
    Container ID:  docker://6b1c380486b9d06c639b63f152ea5ca61a30bd740cf3cbdf4a80d899a3abb973
    Image:         127.0.0.1:5000/sentry:dev-linux-amd64
    Image ID:      docker-pullable://127.0.0.1:5000/sentry@sha256:6625c43de22c794ca4b5e828fe18ba9ccf8d7f62a77ed143129b1a78d6e35a4c
    Ports:         50001/TCP, 9090/TCP, 40000/TCP
    Host Ports:    0/TCP, 0/TCP, 0/TCP
    Command:
      /dlv
    Args:
      --listen=:40000
      --accept-multiclient
      --headless=true
      --log
      --api-version=2
      exec
      /sentry
      --
      --log-level
      info
      --enable-metrics
      --metrics-port
      9090
      --trust-domain
      cluster.local
    State:          Running
      Started:      Wed, 29 Mar 2023 16:11:08 +0800
    Ready:          False
    Restart Count:  0
    Liveness:       http-get http://:8080/healthz delay=30000s timeout=1s period=3s #success=1 #failure=5
    Readiness:      http-get http://:8080/healthz delay=30000s timeout=1s period=3s #success=1 #failure=5
    Environment:
      NAMESPACE:  dapr-system (v1:metadata.namespace)
    Mounts:
      /var/run/dapr/credentials from credentials (ro)
      /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-xwc55 (ro)

此时的 dapr 控制平面状态为:

$ kubectl get pods -n dapr-system -o wide
NAME                                    READY   STATUS             RESTARTS      AGE    IP             NODE      NOMINATED NODE   READINESS GATES
dapr-dashboard-7c5c6bf5cf-rs757         1/1     Running            0             6m1s   10.244.0.104   skywork   <none>           <none>
dapr-operator-cbcb6c4fb-vz625           0/1     Running            6 (90s ago)   6m1s   10.244.0.102   skywork   <none>           <none>
dapr-placement-server-0                 0/1     CrashLoopBackOff   6 (13s ago)   6m1s   10.244.0.103   skywork   <none>           <none>
dapr-sentry-7d9898fdb9-rt4kj            0/1     Running            0             6m1s   10.244.0.101   skywork   <none>           <none>
dapr-sidecar-injector-dcbd7fcbd-cj52p   1/1     Running            0             6m1s   10.244.0.105   skywork   <none>           <none>

做一下 dapr-sentry pod 的 debug 端口的端口映射:

$ kubectl port-forward -n dapr-system dapr-sentry-7d9898fdb9-rt4kj 40000:40000 
Forwarding from 127.0.0.1:40000 -> 40000
Forwarding from [::1]:40000 -> 40000

打开 IDE,以 vs code 为例,我打开 dapr/dapr 仓库,然后设置断点为 cmd/sentry/main.go 中 main 行数的第一行,这样就可以从 sentry 启动开始 debug。

修改 lanch.json 文件,加入以下内容:

{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "debug sentry",
            "type": "go",
            "request": "attach",
            "mode": "remote",
            "remotePath": "${workspaceFolder}",
            "port": 40000,
            "host": "127.0.0.1"
        }
    ]
}

然后在 “Run and Debug” 中选择 “debug sentry”,点击三角形启动:

debug

图上可以看到 debug 启动成功,代码执行停留在 main() 函数的第一行位置,后面就可以按照自己的需要进行断点设置和代码debug了。

如果要重新开始 debug (比如再次debg sentry 启动的过程),最快捷的方式是重新启动 sentry 的pod:

# 删除 dapr-sentry 的现有 pod
k delete pod -n dapr-system dapr-sentry-7d9898fdb9-rt4kj
# 稍后 k8s 会重新启动一个新的 pod,然后依然是以 debug 的方式启动
# 需要再做一次端口映射
kubectl port-forward -n dapr-system dapr-sentry-7d9898fdb9-xxxx 40000:40000

常见错误

runAsNonRoot 错误导致容器启动失败

cd dapr
helm install dapr charts/dapr --namespace dapr-system --values charts/dapr/values-debug.yaml --wait

发现 dapr 的控制面启动不起来:

$ k get pods -n dapr-system                                            
NAME                                     READY   STATUS                       RESTARTS      AGE
dapr-dashboard-7c5c6bf5cf-qkq9j          1/1     Running                      0             7m14s
dapr-operator-684d455b4f-9gwk5           0/1     CreateContainerConfigError   0             7m14s
dapr-placement-server-0                  0/1     CrashLoopBackOff             6 (78s ago)   7m14s
dapr-sentry-6b47f647bb-nr8ld             0/1     CreateContainerConfigError   0             7m14s
dapr-sidecar-injector-699dfb595f-xvgjc   0/1     CreateContainerConfigError   0             7m14s

injector 报错:

  Normal   Scheduled  6m25s                   default-scheduler  Successfully assigned dapr-system/dapr-sidecar-injector-699dfb595f-xvgjc to skywork
  Warning  Failed     4m18s (x12 over 6m25s)  kubelet            Error: container has runAsNonRoot and image will run as root (pod: "dapr-sidecar-injector-699dfb595f-xvgjc_dapr-system(1643bcd9-3453-430a-b135-a48361311639)", container: dapr-sidecar-injector)
  Normal   Pulled     75s (x25 over 6m25s)    kubelet            Container image "192.168.0.90:5000/injector:dev-linux-amd64" already present on machine

这是因为容器要求 runAsNonRoot,但是 debug 打包出来的镜像要求 root。

解决方式: 在 values 文件中将 runAsNonRoot 设置为 false 来暂时避开这个问题。

sentry 调试时其他组件启动失败

k get pods -n dapr-system
NAME                                     READY   STATUS             RESTARTS      AGE
dapr-dashboard-7c5c6bf5cf-r5nsz          1/1     Running            0             37s
dapr-operator-6487d8f4fb-vqdnc           0/1     Running            0             37s
dapr-placement-server-0                  0/1     CrashLoopBackOff   2 (19s ago)   37s
dapr-sentry-b74b47fc-sfq6f               0/1     Running            0             37s
dapr-sidecar-injector-7699f486c6-lqxs8   1/1     Running            0             37s

placement 失败的原因是:

k logs -n dapr-system dapr-placement-server-0 
time="2023-03-28T15:55:27.175204696Z" level=info msg="Starting Dapr Placement Service -- version edge -- commit ccec8b8dc58f4b5e892f7806f895f6d5a85d7eeb" instance=dapr-placement-server-0 scope=dapr.placement type=log ver=edge
time="2023-03-28T15:55:27.175289436Z" level=info msg="Log level set to: info" instance=dapr-placement-server-0 scope=dapr.placement type=log ver=edge
time="2023-03-28T15:55:27.175357952Z" level=info msg="metrics server started on :9090/" instance=dapr-placement-server-0 scope=dapr.metrics type=log ver=edge
time="2023-03-28T15:55:27.175394106Z" level=fatal msg="open /var/run/dapr/credentials/ca.crt: no such file or directory" instance=dapr-placement-server-0 scope=dapr.placement type=log ver=edge

operator 失败的原因:

I0329 07:04:11.751075       1 leaderelection.go:248] attempting to acquire leader lease dapr-system/operator.dapr.io...
time="2023-03-29T07:04:11.852009943Z" level=info msg="Getting TLS certificates" instance=dapr-operator-6487d8f4fb-vqdnc scope=dapr.operator type=log ver=edge
time="2023-03-29T07:04:11.852046755Z" level=info msg="TLS certificate not found; waiting for disk changes. err=open /var/run/dapr/credentials/ca.crt: no such file or directory" instance=dapr-operator-6487d8f4fb-vqdnc scope=dapr.operator type=log ver=edge
time="2023-03-29T07:04:11.852065157Z" level=info msg="Starting watch for certs on filesystem: /var/run/dapr/credentials" instance=dapr-operator-6487d8f4fb-vqdnc scope=dapr.operator type=log ver=edge

3.2 - 使用M1芯片的Macos

在使用M1芯片的 Macos 上构建本地开发环境

4 - 构建远程开发环境

构建在本机IDE中通过remote ssh方式进行编码和测试的远程开发环境

4.1 - goland远程开发环境

在goland中通过remote ssh方式进行编码和测试

准备工作

sftp

服务器端要求安装 sftp,通常安装ssh之后就可以了,尝试用sftp命令登录试试:

$ sftp sky@192.168.0.40              
Connected to 192.168.0.40.
sftp> 

安装 Jetbrans Gateway

参考 https://www.jetbrains.com/help/go/remote-development-starting-page.html

在 goland 中启用 Remote Development Gateway 插件。重启 goland,就能在 goland 的欢迎界面看到 remote development 和 ssh 。

添加 ssh 登录之后:

jetbrains-client-hold

运行

经常卡在这里,长时间等待然后报错,但有时又工作正常,快速进入ide界面。

jetbrains-client-hold

反复测试过,不清楚原因。

这就导致每次使用时都有进不去的风险,非常头疼。

暂时没搞定。

参考

4.2 - vscode远程开发环境

在vscode中通过remote ssh方式进行编码和测试

准备工作

无,只要安装好 vs code 和远程服务器能支持 ssh 即可,非常方便。

运行

打开vs code,左下角点击 “open a remote windows”,然后选 “connect to host… Remote-SSH”:

vscode-open-remote

输入可以连接到远程服务器的 ssh 命令即可。

也可以通过 “open ssh configurtion file” 选择修改 config 文件,比如设置 port :

Host home.yourdomain.com
  HostName home.yourdomain.com
  Port 34022
  User sky

Host 192.168.0.40
  HostName 192.168.0.40
  User sky

打开 dapr 项目所在目录:

vscode-ssh

注意:plugin 要在 remote 这边再安装一次,比如最基本的 go plugin。

参考

5 - 构建混合开发环境

本地编码然后通过交叉编译在其他平台测试的混合开发环境