Envoy 的编译还是比较复杂的,这里将在 ubuntu:22.04
上构建 Envoy。
准备源码
这里我们使用 Envoy 最新的 tag v1.24.1
:
1 | # git clone https://github.com/envoyproxy/envoy.git |
准备构建环境
下载 Docker 镜像
1 | # docker pull ubuntu:22.04 |
运行 Docker 容器
1 | # docker run -d -it --name envoy_build --network="host" -v $PWD:$PWD ubuntu:22.04 /bin/bash |
安装基础的构建工具
1 | # apt-get -y install \ |
安装 Bazelisk
Envoy 使用 Bazel 作为构建系统。官方推荐使用 Bazelisk 作为 bazel
1 | # wget -O /usr/local/bin/bazel https://github.com/bazelbuild/bazelisk/releases/latest/download/bazelisk-linux-$([ $(uname -m) = "aarch64" ] && echo "arm64" || echo "amd64") |
编译
开始编译
到这一步就可以开始编译了,此时默认将使用 gcc 编译:
1 | # bazel build --jobs=2 --copt=-Wno-error=vla-parameter envoy |
- 这里指定
-Wno-error=vla-parameter
编译选项是为了规避一个编译问题 - 如果想使用 Clang 作为编译器,需要额外安装 Clang+LLVM。
生成交付件
在编译成功后,生成的交付件位于 bazel-bin/source/exe/envoy-static
。执行如下命令,确认编译成功。
1 | # bazel-bin/source/exe/envoy-static --version |
bazel-bin/
目录其实是一个符号连接,它连接到 $(bazel info bazel-genfiles)
目录:
1 | # ls -l bazel-bin |
快速测试 Envoy
使用如下方式对生成的 Envoy 交付件进行快速功能测试:
1 | # $(bazel info bazel-genfiles)/source/exe/envoy-static --config-path /root/data/code/envoy/envoy_test.yaml |
配置文件 envoy_test.ymal
的内容如下:
1 | admin: |
该配置主要实现了以下功能:
- 提供了一个管理站点,站点端口为 9901
- 通过
静态资源方式
配置了一个 HTTP 代理站点,站点的端口为 10000,上游集群为local_nginx
,且集群中只有一个 server,即172.17.0.2:80
所以通过访问宿主机的 9901 端口,可以访问 envoy 的管理界面,访问宿主机的 10000 端口,即可访问 172.17.0.2:80
:
1 | # curl http://127.0.0.1:9901/clusters |
1 | # curl http://127.0.0.1:10000/ -I |
其他
生成调试版本
使用如下方式生成调试版本,方便我们在 gdb 下调试 Envoy 代码:
1 | # TEST_TMPDIR="$PWD/envoy_compile_db" bazel build -c dbg --jobs=2 --copt=-Wno-error=vla-parameter envoy |
另外这里有一点需要特别注意,由于 Envoy 生成调试版本时默认会生成独立的调试信息文件(.dwp
文件),在我的环境中没法完全调试(看不到函数参数,行号等信息),得益于这个 commit,我们可以关闭这种调试信息生成方式。修改源码目录下的 .bazelrc
文件,将如下两行注释掉:
1 | build:linux --fission=dbg,opt |
这样就可以对生成的交付件调试了:
1 | #0 Envoy::OptionsImpl::OptionsImpl(int, char const* const*, std::function<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > (bool)> const&, spdlog::level::level_enum) (this=0x7917fcfc338, argc=1, argv=0x7fffffffe0f8, hot_restart_version_cb=..., |
清理交付件
执行 bazel clean
清理生成的交付件。
使用 CI docker 镜像构建
也可以使用 Envoy 的 CI 容器镜像来进行构建,此时可以免去各种依赖安装问题。执行如下命令会使用 CI 容器进行构建:
1 | # ./ci/run_envoy_docker.sh './ci/do_ci.sh bazel.dev' |
生成的交付件默认位于 /tmp/envoy-docker-build/envoy/source/exe/envoy-fastbuild
。通过设置 ENVOY_DOCKER_BUILD_DIR
环境变量可以修改交付件的目录。
关于 CI docker 镜像的使用,可以参考这里。
生成调试版本:
1 | ./ci/run_envoy_docker.sh './ci/do_ci.sh bazel.debug.server_only' |
同样需要通过上述方式关闭生成独立的调试文件。要不然也无法直接用 gdb 调试生成的交付件,会出现错误:
1 | Reading symbols from envoy... |
生成编译数据库
使用如下命令生成 编译数据库
,之后就可以在 Vim 编辑器中以支持 LSP(Language Server Protocol)的方式阅读 Envoy 源代码了:
1 | # mkdir envoy_compile_db |
实际测试时使用该方法生成的 compile_commands.json
,代码定义无法跳转。还尝试过通过 bear 来生成 compile_commands.json
,仍然不行:
1 | bear -- bazel build --spawn_strategy=local -c dbg --jobs=2 --copt=-Wno-error=vla-parameter envoy |
尝试了一个开源项目 bazel-compile-commands-extractor,通过如下方法生成 compile_commands.json
:
在 WORKSPACE 文件中添加如下内容:
1 | load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") |
在 BUILD 文件中添加如下内容:
1 | load("@hedron_compile_commands//:refresh_compile_commands.bzl", "refresh_compile_commands") |
之后执行如下命令,生成 compile_commands.json
文件:
1 | TEST_TMPDIR="$PWD/envoy_compile_db" bazel run :refresh_compile_commands |
但该方法生成的 compile_commands.json
也无法完全正常工作,从 LSP 报错信息来看,主要是一些头文件找不到,通过如下脚本添加了一些符号链接:
1 | from pathlib import Path |
貌似完全工作了,有些折腾。
Reference
Building Envoy with Bazel
Bazel 输出目录布局
libc++ 与 libstdc++ 是什么关系?
Clangd Getting started