本文使用「署名 4.0 国际 (CC BY 4.0)」许可协议,欢迎转载、或重新修改使用,但需要注明来源。 [署名 4.0 国际 (CC BY 4.0)](https://creativecommons.org/licenses/by/4.0/deed.zh) 本文作者: 苏洋 创建时间: 2022年07月15日 统计字数: 11793字 阅读时间: 24分钟阅读 本文链接: https://soulteary.com/2022/07/15/building-a-vector-database-from-scratch-source-code-compilation-and-installation-of-milvus-2.html ----- # 从零开始构建向量数据库:Milvus 的源码编译安装(二) 本篇文章接[上一篇](https://soulteary.com/2022/07/09/building-a-vector-database-from-scratch-source-code-compilation-and-installation-of-milvus-1.html),继续聊聊向量数据库领域,知名的开源技术项目:Milvus,在不同 CPU 架构的 macOS 环境下的编译安装。 如果你在寻找如何从源码编译安装,如何在 x86 和 ARM 两种架构的 macOS 上折腾 Milvus,那么这篇文章应该是最为详细的教程向参考内容啦。 ## 写在前面 关于 Milvus 这个项目的背景知识,以及项目目前的一些现存问题,我在上一篇文章[《从零开始构建向量数据库:Milvus 的源码编译安装(一)》](https://soulteary.com/2022/07/09/building-a-vector-database-from-scratch-source-code-compilation-and-installation-of-milvus-1.html)中有提到过。所以,就不再做展开了,如果你希望做一些了解,可以自行翻阅之前的内容。 目前,软件可以在 Linux、macOS、Windows 上完成编译,考虑到不少工程师都是使用(或被迫选择)macOS 作为图形界面系统,所以本篇文章仅介绍如何在 **macOS 环境**下从源码构建 Milvus。 接下来,我们还是先从编译入手,从零到一的开始了解这个项目。 ## 前置准备 在正式折腾编译构建之前,还是先分别从操作系统、开发环境、源码获取等几个方面来展开聊聊要做哪些准备工作。 ### 操作系统 本文选择的 macOS 版本是最新版 12.4,一般情况下,我们只要打开系统更新,然后让设备自己更新到最新版本,就完成了 80% 的系统环境相关的准备工作啦。 如果你希望在一个全新的环境中进行安装,避免所有的潜在干扰,可以参考苹果官方文档对系统进行重装(绝大多数情况下,不需要这样操作): - [《重新安装 macOS》](https://support.apple.com/zh-cn/guide/mac-help/mchlp1599/mac) - [《如何创建可引导的 macOS 安装器》](https://support.apple.com/zh-cn/HT201372) 对于完成剩余 20% 的工作,我们需要借助 Homebrew。由于 Homebrew 使用了海外的 CDN 服务,所以我们在国内进行软件下载,可能会受到一些网络因素的影响,导致软件下载过程中断或失败。 如果你希望能够避免这类网络的影响,可以尝试使用国内的“[清华源镜像](https://mirrors.tuna.tsinghua.edu.cn/help/homebrew/)”来尝试安装这些软件。 如果你能够直接完成依赖和工具的下载,那么就不需要配置“镜像”,因为镜像的同步机制,存在数据同步不完整的情况,在这个情况下,我们进行依赖安装,可能会出现部分依赖失败。解决这个问题也很简单,只需要在安装进行不下去的时候,“unset” 掉“镜像”,切换为官方源即可。 ### Golang 和 C++ 开发环境 在上一篇文章中,我们就提到过,Milvus 中绝大多数代码是由 Golang 编写的,但是核心的部分又包含了一大堆 C++ 的内容([internal/core/src](https://github.com/milvus-io/milvus/tree/master/internal/core/src)),所以我们需要同时确保两种语言的运行环境。 为了让 Golang 环境的折腾能省点劲,我曾写过两篇文章介绍简单可行的方案: - 如何在 Linux 环境中[《搭建可维护的 Golang 开发环境》](https://soulteary.com/2022/07/04/build-a-maintainable-golang-development-environment.html)(同样适用于 macOS) - 以及在[《M1 芯片 Mac 上更好的 Golang 使用方案》](https://soulteary.com/2022/05/12/better-golang-usage-on-m1-mac.html)(同样适用于 Intel 芯片的 x86 架构) 经过实际测试,只需要按照文章中的步骤,一步一步 “CTRL+C / CTRL+V”,这个方案一定能够让你在十分钟内,快速的用上新鲜热乎的 Golang。 关于 C++ 环境的准备,我们会在下文中详细的展开,这里就先不赘述啦。 ### 源码获取 关于如何快速获取源代码,以及避免因为代码仓库过大,容易受到一些国内经常会遇到的网络质量影响而“拉(pull)”不下来数据的问题,我在上一篇文章中的相同的小节[“源码获取”](https://soulteary.com/2022/07/09/building-a-vector-database-from-scratch-source-code-compilation-and-installation-of-milvus-1.html#%E6%BA%90%E7%A0%81%E8%8E%B7%E5%8F%96)小节中有提到过,你可以直接参考文中提到的解决方案。为了节约篇幅,这里就不再展开啦。 ## 准备基础依赖库:OpenBLAS OpenBLAS 作为泛 AI 领域中,经常能够看到的科学计算开源库,自然也是要安装的。(据说未来版本中,会变成可选项) 在之前的文章[《走进向量计算:从源码编译 OpenBLAS》](https://soulteary.com/2022/07/08/into-vector-computing-compiling-openblas-from-source.html)中,我详细的介绍了如何进行 OpenBLAS 的编译安装,你可以参考文中的内容,来获取 OpenBLAS 的编译产物,并自行完成编译产物的有效性验证。 如果你是使用 `brew` 安装的: ```bash openblas is keg-only, which means it was not symlinked into /usr/local, because macOS provides BLAS in Accelerate.framework. For compilers to find openblas you may need to set: export LDFLAGS="-L/usr/local/opt/openblas/lib" export CPPFLAGS="-I/usr/local/opt/openblas/include" For pkg-config to find openblas you may need to set: export PKG_CONFIG_PATH="/usr/local/opt/openblas/lib/pkgconfig" ``` ## 准备构建依赖:cmake 想要构建包含 C++ 组件的项目,CMake 必不可少。 Milvus 官方项目要求 CMake 的版本是大于等于 3.18,在上一篇文章中,我们有提到了“尽量和官方构建使用相同版本”的建议,不过因为下面的现实问题,完全保持一致的版本比较难,也没有意义: - Mac M1 芯片设备退出时间点较晚,Milvus 立项时间较早,这导致很多项目使用的软件版本不支持 M1 设备(ARMv64 设备) - HomeBrew 和 macOS 新版本所预构建的工具的版本号都比较新,安装旧版本非常折腾,且不保证能够通过测试 - 我们很少会使用 macOS 作为生产环境,更多的时候,macOS 会被用来做开发环境,所以我们也并没有必要严格要求这个环境下的组件版本和 Linux 环境下完全一致 但是考虑到过程的严谨,期望构建产物少一些预料之外的问题。在这里,我们还是分别聊聊两种芯片架构的 macOS 设备。 ### Intel x86 芯片的 macOS 设备 虽然在 CMake 官方网站,我们没有找到预构建程序,但是在 [CMake 的 GitHub 仓库发布页面中](https://github.com/Kitware/CMake/releases/tag/v3.18.6),我们能够找到和 Milvus 官方 CI 使用版本一致的软件包:[3.18.6 版本的 CMake](https://github.com/Kitware/CMake/releases/download/v3.18.6/cmake-3.18.6-Darwin-x86_64.tar.gz)。 将文件下载之后,放到可执行文件目录(`/usr/local/bin/`),或者类似我们上文中安装 `gvm` 的时候,把包含 `cmake` 的路径添加到环境变量中即可完成预构建的 CMake 的安装。 安装完毕之后,我们同样可以使用 `cmake --version` 命令,来简单验证安装是否正确: ```bash cmake --version cmake version 3.18.6 CMake suite maintained and supported by Kitware (kitware.com/cmake). ``` 当然,这里也可以使用 `brew install cmake` 来直接安装更新版本的 CMake 程序,简化安装过程。 ### M1 ARMv64 芯片的 macOS 因为[直到 3.19.3 以上版本](https://github.com/Kitware/CMake/releases/tag/v3.19.3)的 cmake 才提供了 ARMv64 支持,所以我们和 macOS 环境中构建 OpenBLAS 小节中遇到了一样的问题:无法在 macOS 环境下,使用和 Milvus 官方仓库中一致版本号的 `cmake`。 我们同样有两个选择,一个是参考前文中处理 Intel 芯片 macOS 环境中 CMake 的处理方式,只选择官方开始支持 ARMv64 的软件版本,完成预构建程序的安装。 另外一个方案,则是直接使用 `brew install cmake` 安装 `cmake` 最新的 “stable” 版本,因为两种方式本质都使用了较高的新版本 cmake,并没有本质差别,所以我更推荐使用后者,安装 “stable” 版本的软件: ```bash brew install cmake ==> Downloading https://mirrors.tuna.tsinghua.edu.cn/homebrew-bottles/cmake-3.23.2.arm64_monterey.bottle.tar.gz ######################################################################## 100.0% ==> Pouring cmake-3.23.2.arm64_monterey.bottle.tar.gz ==> Caveats To install the CMake documentation, run: brew install cmake-docs Emacs Lisp files have been installed to: /opt/homebrew/share/emacs/site-lisp/cmake ==> Summary 🍺 /opt/homebrew/Cellar/cmake/3.23.2: 3,043 files, 42.2MB ==> Running `brew cleanup cmake`... Disable this behaviour by setting HOMEBREW_NO_INSTALL_CLEANUP. Hide these hints with HOMEBREW_NO_ENV_HINTS (see `man brew`). ``` 程序安装完毕,我们使用 `cmake --version` 来进行一个简单的验证,输出程序的版本号: ```bash cmake version 3.23.2 CMake suite maintained and supported by Kitware (kitware.com/cmake). ``` ## 准备构建依赖:clang-format、clang-tidy 在 Milvus 代码仓库里的[构建工具镜像代码中](https://github.com/milvus-io/milvus/blob/master/build/docker/builder/cpu/ubuntu18.04/Dockerfile#L17),我们能够看到两个构建依赖叫做:`clang-format-10` 和 `clang-tidy-10`,这两个工具被用于构建过程中,对代码进行[格式化和检查](https://github.com/milvus-io/milvus/search?q=clang-format)。 我们在 HomeBrew 官方软件包仓库中,可以找到一个名为 `clang-format` 的[软件包](https://formulae.brew.sh/formula/clang-format#default),它包含了我们所需要的 `clang-format` 相关组件。但是和上面安装 `OpenBLAS` 和 `cmake` 一样,软件仓库中并没有提供我们所需要的版本(Milvus 官方仓库的版本)。 所以,我们需要参考上一篇文章中 Ubuntu 22.04 中“自己动手,丰衣足食”的精神,开启手动编译。 依旧是先使用 `git clone` 下载所需要的代码,和 Milvus 官方仓库保持一致: ```bash git clone -b ubuntu/focal https://git.launchpad.net/ubuntu/+source/llvm-toolchain-10 Cloning into 'llvm-toolchain-10'... remote: Enumerating objects: 106493, done. remote: Counting objects: 21% (22364/106493) remote: Counting objects: 100% (106493/106493), done. remote: Compressing objects: 100% (78521/78521), done. remote: Total 106493 (delta 25377), reused 105666 (delta 24555)B/s Receiving objects: 100% (106493/106493), 164.17 MiB | 268.00 KiB/s, done. Resolving deltas: 100% (25377/25377), done. Updating files: 100% (90282/90282), done. ``` 在代码下载完毕之后,我们还是进入目录,并准备一个用于构建的新目录: ```bash cd llvm-toolchain-10 && \ mkdir build && cd build ``` 在 Ubuntu 环境中,我们想要使用 `cmake` 构建 `llvm-toolchain` 需要 `ninja-build`,在 `macOS` 中,我们所需要的这个包被叫做 [“ninja”](https://formulae.brew.sh/formula/ninja),我们可以使用 `brew install ninja` 来完成它的安装: ```bash brew install ninja ==> Downloading https://mirrors.tuna.tsinghua.edu.cn/homebrew-bottles/ninja-1.11.0.arm64_monterey.bottle.tar.gz ######################################################################## 100.0% ==> Pouring ninja-1.11.0.arm64_monterey.bottle.tar.gz ==> Caveats zsh completions have been installed to: /opt/homebrew/share/zsh/site-functions Emacs Lisp files have been installed to: /opt/homebrew/share/emacs/site-lisp/ninja ==> Summary 🍺 /opt/homebrew/Cellar/ninja/1.11.0: 10 files, 435.3KB ==> Running `brew cleanup ninja`... Disable this behaviour by setting HOMEBREW_NO_INSTALL_CLEANUP. Hide these hints with HOMEBREW_NO_ENV_HINTS (see `man brew`). ``` 在完成 `ninja` 安装之后,我们就可以使用相同的参数来生成构建所需要的配置文件了: ```bash cmake -DLLVM_ENABLE_PROJECTS="clang;clang-tools-extra" -DCMAKE_BUILD_TYPE=Release -DPYTHON_EXECUTABLE=$(which python3) -DBENCHMARK_DOWNLOAD_DEPENDENCIES=ON ../llvm ``` 在构建配置文件生成完毕之后,还是执行 `cmake --build .` 命令,就可以开始 `clang` 工具的编译工作了: ```bash cmake --build . ``` 这里默认编译将会使用一个核心,如果我们的设备有更多的核心数,建议添加参数来提升构建速度(比如用8个核心一起并行构建): ```bash cmake --build . -j8 ``` 在漫长的编译构建结束后,我们将看到类似下面的日志: ```bash ... [100%] Building CXX object utils/benchmark/src/CMakeFiles/benchmark.dir/timers.cc.o [100%] Linking CXX static library ../../../lib/libbenchmark.a [100%] Built target benchmark [100%] Building CXX object utils/benchmark/src/CMakeFiles/benchmark_main.dir/benchmark_main.cc.o [100%] Linking CXX static library ../../../lib/libbenchmark_main.a [100%] Built target benchmark_main [100%] Copying llvm-locstats into /Users/soulteary/app/llvm-toolchain-10/build/./bin [100%] Built target llvm-locstats ``` 和上一篇文章中对 Linux 编译产物的验证方式一样,我们分别使用 `./bin/clang-format --version` 和 `./bin/clang-tidy --version` 就能够完成对两个程序的分别验证了: ```bash # ./bin/clang-format --version clang-format version 10.0.0 (https://git.launchpad.net/ubuntu/+source/llvm-toolchain-10 6f319ce10f040c979f7bb074a7b7465b8c7bf9f7) # ./bin/clang-tidy --version LLVM (http://llvm.org/): LLVM version 10.0.0 Optimized build. Default target: arm-apple-darwin21.5.0 Host CPU: cyclone ``` 在完成对程序可运行的验证之后,我们将两个程序分别复制到 `homebrew` 或者系统的可执行文件的目录中即可: ```bash cp bin/clang-format /usr/local/bin/ && \ cp bin/clang-tidy /usr/local/bin/ ``` ## 其余 C++ 开发环境的依赖 上文中,我们其实已经进行了一些 C++ 环境的依赖和工具的准备,不过为了让 C++ 部分程序能够顺利编译和使用,我们还需要继续补完一些内容。 在 Milvus 官方仓库的依赖安装脚本中,我们能找到一个函数 `install_mac_deps`,里面定义了基本所有的官方认为需要的工具: ```bash function install_mac_deps() { sudo xcode-select --install > /dev/null 2>&1 brew install boost libomp ninja tbb cmake llvm ccache brew uninstall grep brew install grep export PATH="/usr/local/opt/grep/libexec/gnubin:$PATH" brew update && brew upgrade && brew cleanup if [[ $(arch) == 'arm64' ]]; then brew install openssl brew install librdkafka brew install pkg-config sudo mkdir /usr/local/include sudo mkdir /usr/local/opt sudo ln -s "$(brew --prefix llvm)" "/usr/local/opt/llvm" sudo ln -s "$(brew --prefix libomp)/include/omp.h" "/usr/local/include/omp.h" sudo ln -s "$(brew --prefix libomp)" "/usr/local/opt/libomp" sudo ln -s "$(brew --prefix boost)/include/boost" "/usr/local/include/boost" sudo ln -s "$(brew --prefix tbb)/include/tbb" "/usr/local/include/tbb" sudo ln -s "$(brew --prefix tbb)/include/oneapi" "/usr/local/include/oneapi" fi } ``` 将上面的内容全部复制,然后粘贴在终端中执行,接着输入 `install_mac_deps` ,敲击回车,会看到终端提醒我们需要输入密码,才能继续执行程序。 ```bash # install_mac_deps Password: ==> Downloading https://ghcr.io/v2/homebrew/core/icu4c/manifests/70.1 ######################################################################## 100.0% ==> Downloading https://ghcr.io/v2/homebrew/core/icu4c/blobs/sha256:43cf787a35559b90597db8e1aaba95dbeedb84b1ee3d2e942be8938ae618724c ==> Downloading from https://pkg-containers.githubusercontent.com/ghcr1/blobs/sha256:43cf787a35559b90597db8e1aaba95dbeedb84b1ee3d2e942be8938ae618724c?se=2022-07-06T14%3A35%3A00Z&sig=v38FExWrsokAKB1VwnoRNd%2FjGPYEhs ######################################################################## 100.0% ... ``` 在输入密码之后,耐心等待程序运行完毕,macOS 所需要的 C++ 编译环境就完全就绪了。 ## Golang 开发环境调整 在文章的“前置准备”小节中,我们提到过 Golang 环境的快速安装配置。但是在 Ubuntu 和 macOS 环境中,两者其实还是有一些不同之处:在 macOS 12.4 版本中,如果我们使用和 Milvus 官方一致的 Golang 1.16.9 将会出现构建失败的问题,所以我们必须升版本到 1.17 及以上。 使用 `gvm` 命令,来将 Golang 切换到一个合适的版本: ```bash gvm install go1.17 -B && \ gvm use go1.17 --default ``` ## 从源码编译 Milvus 主程序 看到这里,距离完成 Milvus 项目的构建,我们已经完成了 90% 的工作。接下来,我们就来解决剩下的 10% 的工作。 这部分的在 macOS 环境下的 Milvus 编译,和 Ubuntu 环境大同小异。唯一的差别是,我们需要在执行编译之前,先声明一个环境变量,避免程序在编译过程中因为找不到 `pkg-config` 而无法继续进行: ```bash export PKG_CONFIG_PATH="/opt/homebrew/opt/openssl@3/lib/pkgconfig" ``` 在完成变量声明之后,我们同样先切换到早早准备好的 Milvus 代码目录中,接着执行 `make milvus`: ```bash cd milvus make milvus ``` 然后在一阵花花绿绿的终端日志滚动结束之后,我们执行 `./bin/milvus` 命令,就能够看到符合预期的 Milvus 执行程序输出的帮助信息了: ```bash # ./bin/milvus Usage: milvus run [server type] [flags] Start a Milvus Server. Tips: Only the server type is 'mixture', flags about starting server can be used. [flags] -rootcoord 'true' Start the rootcoord server. -querycoord 'true' Start the querycoord server. -indexcoord 'true' Start the indexcoord server. -datacoord 'true' Start the datacoord server. -alias '' Set alias milvus stop [server type] [flags] Stop a Milvus Server. [flags] -alias '' Set alias milvus mck run [flags] Milvus data consistency check. Tips: The flags are optional. [flags] -etcdIp '' Ip to connect the ectd server. -etcdRootPath '' The root path of operating the etcd data. -minioAddress '' Address to connect the minio server. -minioUsername '' The username to login the minio server. -minioPassword '' The password to login the minio server. -minioUseSSL 'false' Whether to use the ssl to connect the minio server. -minioBucketName '' The bucket to operate the data in it milvus mck cleanTrash [flags] Clean the back inconsistent data Tips: The flags is the same as its of the 'milvus mck [flags]' [server type] standalone rootcoord proxy querycoord indexcoord embedded querynode indexnode datacoord datanode mixture ``` ## 最后 聊到这里 ,Milvus 在两种不同 CPU 架构的 macOS 设备上的编译安装就讲完啦。虽然已经在之前的文章中,铺垫和介绍了一部分内容,但是目前文章字数依然破万字。所以,也就不继续展开更多细节啦。 在下一篇 Milvus 相关的内容中,我们会继续聊聊容器相关的构建和使用,如何构建一个“知根知底”,更加“透明可控”的容器镜像。以及如何优化这个容器镜像的性能极限,让它在开发和生产环境中的性价比更高。 如果你想了解如何构建一个更加可靠、可信赖的容器镜像,我们下一篇文章见。 --EOF