前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >mac 上学习k8s系列(13)编译源码阅读

mac 上学习k8s系列(13)编译源码阅读

作者头像
golangLeetcode
发布2022-08-02 19:29:35
1.5K0
发布2022-08-02 19:29:35
举报
文章被收录于专栏:golang算法架构leetcode技术php

看到k8s的源码,第一感觉是无从下手,量太大。其实看懂k8s的源码,前提是你对go比较熟悉,另外bash能看懂,源码编译用到了大量bash脚本。首先我们看下源码结构,主要的目录可以分为三类:

  1. 文档类:api、docs、logo
  2. 工具类(build、cluster、Godeps、hack、staging、translations)工具类主要用到的build目录下的文件,自己动手编译的时候会用到
  3. 代码类(cmd、pkg、plugin、test、third_party)核心代码集中在cmd和pkg中。
  • cmd内部包含各个组件的入口,具体核心的实现部分在pkg目录下
  • plugin目录之前的版本包括scheduler部分的代码,plugin主要包含的是认证与鉴权部分的代码。

源码编译可以有两种方式:容器编译和主机编译。主机编译和主机编译的差别在于,容器编译会先拉取编译用的镜像,启动一个容器内的编译环境,然后在容器内部运行主机编译的脚本。

主机编译

主机编译需要安装基本环境(Go、gcc)

  • 使用kubernetes自带的Makefile,使用make即可编译
  • 对不同的模块可以进行单独的编译,例如编译kubelet:make all WHAT=cmd/kubelet GOFLAGS=-v命令编译kubelet模块
  • 编译生成的二进制包在_output/bin目录下

配置编译环境:

老版本需要将下载好的kubernetes源码拷贝到GOPATH/src/k8s.io目录,进入GOPATH/src/k8s.io/kubernetes目录,GOPATH为/root/kube,新版本使用了go module,直接编译就行。

尝试编译kubelet:

代码语言:javascript
复制
   make all WHAT=cmd/kubelet GOFLAGS=-v

报错了:

代码语言:javascript
复制
This script requires a minimum bash version of 4.2, but got version of 3.2

直接brew install bash会失败,arm不支持,需要配置iterm 的rossta环境。

代码语言:javascript
复制
 %arch -x86_64 brew install bash

然后make

代码语言:javascript
复制
  Kubernetes requires go1.17.0 or greater.

我本机的go 是1.16.2,查找对应版本的分支

代码语言:javascript
复制
  git pull origin release-1.21:release-1.21 --depth=1
  git checkout release-1.21
  make all WHAT=cmd/kubelet GOFLAGS=-v

终于编译成功了,看下生成的文件

代码语言:javascript
复制
% ls _output/local/bin/darwin/arm64/
conversion-gen                  defaulter-gen                   
go2make                         openapi-gen
deepcopy-gen                    go-bindata                     
kubelet                         prerelease-lifecycle-gen

如果直接执行make 生成所有文件如下:

代码语言:javascript
复制
ls _output/bin 
apiextensions-apiserver         genman                          kube-aggregator                 kubectl-convert
conversion-gen                  genswaggertypedocs              kube-apiserver                  kubelet
deepcopy-gen                    genyaml                         kube-controller-manager         kubemark
defaulter-gen                   ginkgo                          kube-proxy                      linkcheck
e2e.test                        go-bindata                      kube-scheduler                  mounter
gendocs                         go-runner                       kubeadm                         openapi-gen
genkubedocs                     go2make                         kubectl                         prerelease-lifecycle-gen

先看下Makefile

源码位置:kubernetes/Makefile

首先设置了输出文件的地址

代码语言:javascript
复制
OUT_DIR ?= _output
BIN_DIR := $(OUT_DIR)/bin

make执行的指令是

代码语言:javascript
复制
all: generated_files
      hack/make-rules/build.sh $(WHAT)

Makefile还有一些其他指令可以自行研究:

代码语言:javascript
复制
     ginkgo:
  hack/make-rules/build.sh vendor/github.com/onsi/ginkgo/ginkgo
      verify:
  KUBE_VERIFY_GIT_BRANCH=$(BRANCH) hack/make-rules/verify.sh
      update: generated_files
  CALLED_FROM_MAIN_MAKEFILE=1 hack/make-rules/update.sh
      check test: generated_files
  hack/make-rules/test.sh $(WHAT) $(TESTS)
      test-integration: generated_files
  hack/make-rules/test-integration.sh $(WHAT)
      test-e2e-node: ginkgo generated_files
  hack/make-rules/test-e2e-node.sh
      test-e2e-kubeadm: 
  hack/make-rules/test-e2e-kubeadm.sh
      test-cmd: generated_files
  hack/make-rules/test-cmd.sh
      clean: clean_meta
  build/make-clean.sh
  hack/make-rules/clean.sh
      clean_meta:
  rm -rf $(META_DIR)
      clean_generated:
  find . -type f -name '$(GENERATED_FILE_PREFIX)*'  | xargs rm -f
      vet: generated_files
  CALLED_FROM_MAIN_MAKEFILE=1 hack/make-rules/vet.sh $(WHAT)
      release-in-a-container:
  build/release-in-a-container.sh
      release-images:
  build/release-images.sh
      release-skip-tests quick-release:
  build/release.sh
      quick-release-images:
  build/release-images.sh
      package package-tarballs:
  build/package-tarballs.sh
      else
  hack/make-rules/cross.sh
      $(filter-out %$(EXCLUDE_TARGET),$(notdir $(abspath $(wildcard cmd/*/)))): generated_files
  hack/make-rules/build.sh cmd/$@
      generated_files gen_openapi:
  $(MAKE) -f Makefile.generated_files $@ CALLED_FROM_MAIN_MAKEFILE=1
      help:
  hack/make-rules/make-help.sh

1,首先看下generated_files指令

代码语言:javascript
复制
generated_files gen_openapi:
  $(MAKE) -f Makefile.generated_files $@ CALLED_FROM_MAIN_MAKEFILE=1

执行子文件:kubernetes/Makefile.generated_files

主要是完成openapi 和香港文档的生成工作:

代码语言:javascript
复制
      .PHONY: generated_files
generated_files: gen_prerelease_lifecycle gen_deepcopy gen_defaulter gen_conversion gen_openapi gen_bindata
      gen_openapi: $(OPENAPI_GEN) $(KUBE_OPENAPI_OUTFILE) $(AGGREGATOR_OPENAPI_OUTFILE) $(APIEXTENSIONS_OPENAPI_OUTFILE) $(CODEGEN_OPENAPI_OUTFILE) $(SAMPLEAPISERVER_OPENAPI_OUTFILE)
      OPENAPI_GEN := $(BIN_DIR)/openapi-gen

2,然后看下 kubernetes/hack/make-rules/build.sh

这个脚本主要完成编译工作:

代码语言:javascript
复制
      KUBE_ROOT=$(dirname "${BASH_SOURCE[0]}")/../..
      KUBE_VERBOSE="${KUBE_VERBOSE:-1}"
      source "${KUBE_ROOT}/hack/lib/init.sh"
      kube::golang::build_binaries "$@"
      kube::golang::place_bins

先初始化编译环境,设置环境变量,加载依赖脚本kubernetes/hack/lib/init.sh

代码语言:javascript
复制
      export GO111MODULE=off
      source "${KUBE_ROOT}/hack/lib/util.sh"
      source "${KUBE_ROOT}/hack/lib/logging.sh"
      source "${KUBE_ROOT}/hack/lib/version.sh"
      source "${KUBE_ROOT}/hack/lib/golang.sh"
      source "${KUBE_ROOT}/hack/lib/etcd.sh"
      # list of all available group versions.  This should be used when generated code
# or when starting an API server that you want to have everything.
# most preferred version for a group should appear first
KUBE_AVAILABLE_GROUP_VERSIONS="${KUBE_AVAILABLE_GROUP_VERSIONS:-\

kubernetes/hack/lib/golang.sh这个脚本完成主要的编译工作,这里会检查支持的系统和对应的cpu架构,可以看到不支持mac M1,所以我们能编译成功,但是运行不了

代码语言:javascript
复制
readonly KUBE_SUPPORTED_SERVER_PLATFORMS=(
  linux/amd64
  linux/arm
  linux/arm64
  linux/s390x
  linux/ppc64le
)
readonly KUBE_SUPPORTED_NODE_PLATFORMS=(
  linux/amd64
  linux/arm
  linux/arm64
  linux/s390x
  linux/ppc64le
  windows/amd64
)
readonly KUBE_SUPPORTED_CLIENT_PLATFORMS=(
  linux/amd64
  linux/386
  linux/arm
  linux/arm64
  linux/s390x
  linux/ppc64le
  darwin/amd64
  darwin/arm64
  windows/amd64
  windows/386
)

然后是我们的编译目标:

代码语言:javascript
复制
kube::golang::server_targets() {
  local targets=(
    cmd/kube-proxy
    cmd/kube-apiserver
    cmd/kube-controller-manager
    cmd/kubelet
    cmd/kubeadm
    cmd/kube-scheduler
    vendor/k8s.io/kube-aggregator
    vendor/k8s.io/apiextensions-apiserver
    cluster/gce/gci/mounter
  )
  echo "${targets[@]}"
}
代码语言:javascript
复制
readonly KUBE_ALL_TARGETS=(
  "${KUBE_SERVER_TARGETS[@]}"
  "${KUBE_CLIENT_TARGETS[@]}"
  "${KUBE_TEST_TARGETS[@]}"
  "${KUBE_TEST_SERVER_TARGETS[@]}"
)
        IFS=" " read -ra KUBE_SERVER_TARGETS <<< "$(kube::golang::server_targets)"
readonly KUBE_SERVER_TARGETS
readonly KUBE_SERVER_BINARIES=("${KUBE_SERVER_TARGETS[@]##*/}")
        readonly KUBE_CLIENT_TARGETS=(
  cmd/kubectl
  cmd/kubectl-convert
)
readonly KUBE_CLIENT_BINARIES=("${KUBE_CLIENT_TARGETS[@]##*/}")
readonly KUBE_CLIENT_BINARIES_WIN=("${KUBE_CLIENT_BINARIES[@]/%/.exe}")

A,然后我们看下完成编译的函数

代码语言:javascript
复制
kube::golang::build_binaries() {
 kube::golang::setup_env
    if [[ ${#targets[@]} -eq 0 ]]; then
      targets=("${KUBE_ALL_TARGETS[@]}")
    fi
       kube::golang::build_binaries_for_platform "${platform}"

先设置好环境变量,然后 通过下面函数获取目标

代码语言:javascript
复制
kube::golang::server_targets() 

编译对应目标平台版本的二进制。

代码语言:javascript
复制
kube::golang::build_binaries_for_platform() {
     build_args=(

      -installsuffix static

      ${goflags:+"${goflags[@]}"}

      -gcflags "${gogcflags:-}"

      -asmflags "${goasmflags:-}"

      -ldflags "${goldflags:-}"

      -tags "${gotags:-}"

    )

    CGO_ENABLED=0 kube::golang::build_some_binaries "${statics[@]}"

我们可以看到最终调用了go install 完成了最终的编译

代码语言:javascript
复制
kube::golang::build_some_binaries() {

         for package in "$@"; do

           go test -c -o "$(kube::golang::outfile_for_binary "${package}" "${platform}")" \

          -covermode count \

          -coverpkg k8s.io/...,k8s.io/kubernetes/vendor/k8s.io/... \

          "${build_args[@]}" \

          -tags coverage \

          "${package}"

          uncovered+=("${package}")

         go install "${build_args[@]}" "$@"

B,最后把二进制拷贝到目标位置

代码语言:javascript
复制
kube::golang::place_bins() {

容器编译

代码语言:javascript
复制
make quick-release
或者
执行./build/run.sh hack/build-go.sh cmd/kubelet单独编译kubelet模块

1,

看Makefile对应指令

代码语言:javascript
复制
release-skip-tests quick-release:
  build/release.sh

看下kubernetes/build/release.sh对应源码

代码语言:javascript
复制
kube::build::verify_prereqs
kube::build::build_image
kube::build::run_build_command make cross

在kubernetes/build/common.sh里

代码语言:javascript
复制
function kube::build::build_image()
            cp "${KUBE_ROOT}/build/build-image/Dockerfile" "${LOCAL_OUTPUT_BUILD_CONTEXT}/Dockerfile"
            kube::build::docker_build "${KUBE_BUILD_IMAGE}" "${LOCAL_OUTPUT_BUILD_CONTEXT}" 'false' "--build-arg=KUBE_BUILD_IMAGE_CROSS_TAG=${KUBE_BUILD_IMAGE_CROSS_TAG} --build-arg=KUBE_BASE_IMAGE_REGISTRY=${KUBE_BASE_IMAGE_REGISTRY}"

最终调用的是docker build 命令完成镜像的编译

代码语言:javascript
复制
function kube::build::docker_build() 
   local -ra build_cmd=("${DOCKER[@]}" build -t "${image}" "--pull=${pull}" "${build_args[@]}" "${context_dir}")

然后执行代码的编译

代码语言:javascript
复制
function kube::build::run_build_command()
   kube::build::run_build_command_ex "${KUBE_BUILD_CONTAINER_NAME}" -- "$@"
代码语言:javascript
复制
function kube::build::run_build_command_ex() 
    local -ra docker_cmd=("${DOCKER[@]}" run "${docker_run_opts[@]}" "${KUBE_BUILD_IMAGE}")

2,kubernetes/build/run.sh和kubernetes/build/run.sh差不多

代码语言:javascript
复制
source "$KUBE_ROOT/build/common.sh"
kube::build::verify_prereqs
kube::build::build_image
kube::build::run_build_command "$@"

既然是通过镜像编译,我们看下Dockerfile:build/build-image/Dockerfile

基础镜像是

代码语言:javascript
复制
FROM ${KUBE_BASE_IMAGE_REGISTRY}/kube-cross:${KUBE_BUILD_IMAGE_CROSS_TAG}

我们需要下载镜像然后和重新打tag,目前M1上没有成功

代码语言:javascript
复制
1、docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/kube-cross:v1.11.5-1
2、docker tag registry.cn-hangzhou.aliyuncs.com/google_containers/kube-cross:v1.11.5-1 k8s.gcr.io/kube-cross:v1.11.5-1

Dockerfile里执行的脚本

代码语言:javascript
复制
ADD rsyncd.sh /
RUN chmod a+rx /rsyncd.sh

位置:build/build-image/rsyncd.sh

设置好docker环境后执行 make cross 命令在docker环境上进行编译

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-10-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 golang算法架构leetcode技术php 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档