2019-04-04

这篇文档先说明编译安装内核的背景和原因,然后以 4.14 内核为例,介绍详细步骤。

- [为何要自编译内核?](#为何要自编译内核)

- [选择哪个内核版本?](#选择哪个内核版本)

- [选择 4.14 还是 4.19内核 ?](#选择-414-还是-419内核-)

- [编译内核](#编译内核)

    - [准备编译环境](#准备编译环境)

    - [下载 kernel.org 内核](#下载-kernelorg-内核)

    - [拷贝内核配置文件](#拷贝内核配置文件)

    - [设置自定义内核版本字符串](#设置自定义内核版本字符串)

    - [并发编译内核](#并发编译内核)

    - [安装内核模块](#安装内核模块)

    - [查看安装的设备驱动文件信息](#查看安装的设备驱动文件信息)

    - [安装内核文档](#安装内核文档)

    - [安装内核](#安装内核)

    - [安装内核配置文件](#安装内核配置文件)

    - [安装内核符号表文件](#安装内核符号表文件)

    - [创建 initramfs 文件](#创建-initramfs-文件)

    - [更新 Bootloader 配置文件](#更新-bootloader-配置文件)

    - [设置从新内核启动](#设置从新内核启动)

    - [重启机器,确认使用了新版内核](#重启机器确认使用了新版内核)

- [TODO](#todo)

- [参考](#参考)

## 为何要自编译内核?

CentOS 7.x 自带的 3.10.x 内核存在诸多 Bugs(见文档尾部的链接列表),导致运行的 Docker 或 Kubernetes 不稳定,进而影响上层业务的可用性。解决的版本是升级到对容器支持更好的高版本内核。

RedHat/CentOS 官方只维护 OS 自带的 3.10.x 内核,对于高版本内核,需要自己获取安装包,常见的两种方式是:

1. 社区编译版本,如 elrepo 发布的 lt 和 ml 内核 RPM 包;

2. 自己配置、编译;

社区编译发布的内核不满足我们的需求,如 elrepo 发布的两个版本:

1. 4.4.x lt:部分设备驱动程序版本较老,场内安装后出现找不到磁盘的情况,导致系统启动失败(更多场内 cases,参考文末的链接列表);

2. 5.x ml:当前主线版本内核,不稳定,而且不是 kernel.org 长期支持版本;

所以,我们只能自己配置、编译内核。

需要注意的是,RedHat 官方只支持 OS 自带和后续发布的 3.10 内核,不支持社区和自编译版本内核。

## 选择哪个内核版本?

我们当前使用的是 4.4 LTS 内核,在场内升级部署后,出现网卡驱动 Bugs、重启时不识别 RAID 卡、启动失败的情况。

所以选择内核版本的原则是:kernel.org 发布的、长期支持的、4.4 以上版本。

目前 kernel.org 发布的内核版本如下:

![](image/2019-04-04-11-50-50.png)

可见,候选的 LTS 内核是:

1. 4.9.167

2. 4.14.110

3. 4.19.33

与 4.4 一样,4.9.167 包含的 megaraid_sas RAID 卡驱动版本是 "06.811.02.00-rc1",不满足需求。

4.14.110 和 4.14.110 对应的驱动版本较新("07.702.06.00-rc1","07.706.03.00-rc1"),可以满足需求。

所以,我们可用的 LTS 内核缩减为两个:

1. 4.14.110

2. 4.19.33

## 选择 4.14 还是 4.19内核 ?

我们做了一些调研,结果如下:

1. 4.19.x 不稳定,特别是 iptables conntrack 相关功能,被吐槽为 [badest release in the last 6 years](https://lists.fedoraproject.org/archives/list/[email protected]/thread/7GXLO5GLD7H4UUFDORLZE4SHQUBFQQUK/?sort=date):

  - [randomly crashes iptables connlimit module](https://bugzilla.kernel.org/show_bug.cgi?id=202013)

  - [connlimit is broken in whole 4.19.x series and 4.20.0](https://bugzilla.kernel.org/show_bug.cgi?id=202065)

  - [system fails to boot with内核 4.19.8-300.fc29.x86_64](https://bugzilla.redhat.com/show_bug.cgi?id=1658623)

2. 4.14.x 发布时间(2017-12-12)较 4.19.x 发布时间(2018-10-22)早,更稳当;

3. Google Cloud 使用 4.14.x 作为自己公有云 VM 缺省内核;

所以,我们更倾向于满足需求的、更稳定的 4.14.x LTS 版本。

## 编译内核

### 准备编译环境

编译内核需要 gcc、bc、bison 等 GNU 编译工具链,以及一些库头文件。 这里安装相关软件包:

``` bash

[root@m7-uiauto-ssd001 ~]# yum groupinstall 'Development Tools'

[root@m7-uiauto-ssd001 ~]# yum install bc elfutils-libelf-devel openssl-devel ncurses ncurses-devel bison

```

### 下载 kernel.org 内核

从官方下载 4.14 系列最新版内核源码,解压到内核源码目录 /usr/src/kernels:

``` bash

[root@m7-uiauto-ssd001 kernels]# cd /usr/src/kernels

[root@m7-uiauto-ssd001 kernels]# wget https://cdn.kernel.org/pub/linux/kernel/v4.x/linux-4.14.110.tar.xz

[root@m7-uiauto-ssd001 kernels]# tar -xvf linux-4.14.110.tar.xz

```

### 拷贝内核配置文件

内核的配置参数非常多,稍有不慎就可能引起核心功能、驱动的缺失。所以,一般我们使用一个验证过的、版本相近的内核配置文件来配置内核,而不是自己从头一步步配置。

elrepo 发布的内核被广泛使用,可以使用它的内核配置文件作为我们配置的基础。

对于 elrepo 而言,4.14 是历史版本,不是当前的 lt 和 ml 版本,所以需要到 elrep archive repo 下载归档的 RPM,然后使用 cpio 解压 RPM,获得内核配置文件:

``` bash

[root@m7-uiauto-ssd001 kernels]# mkdir elrepo && cd elrepo

[root@m7-uiauto-ssd001 kernels]# wget http://mirror.rc.usf.edu/compute_lock/elrepo/kernel/el7/x86_64/RPMS/kernel-ml-4.14.15-1.el7.elrepo.x86_64.rpm

[root@m7-uiauto-ssd001 elrepo]# rpm2cpio kernel-ml-4.14.15-1.el7.elrepo.x86_64.rpm | cpio -idmv &>/dev/null

[root@m7-uiauto-ssd001 elrepo]# ls -l boot/

total 11100

-rw------- 1 root root 3704096 Dec 22 01:37 System.map-4.14.15-1.el7.elrepo.x86_64

-rw-r--r-- 1 root root  192761 Dec 22 01:37 config-4.14.15-1.el7.elrepo.x86_64

-rw-r--r-- 1 root root  361642 Dec 22 01:41 symvers-4.14.15-1.el7.elrepo.x86_64.gz

-rwxr-xr-x 1 root root 7095680 Dec 22 01:37 vmlinuz-4.14.15-1.el7.elrepo.x86_64

```

使用 eprepo RPM 内核配置文件配置内核:

``` bash

[root@m7-uiauto-ssd001 elrepo]# cd ../kernels/linux-4.14.110

[root@m7-uiauto-ssd001 linux-4.14.110]# make clean && make mrproper # 保证内核树的绝对干净;

[root@m7-uiauto-ssd001 linux-4.14.110]# cp ../elrepo/boot/config-4.14.15-1.el7.elrepo.x86_64 .config

```

### 设置自定义内核版本字符串

社区发布的内核包一般有一定的命名惯例,如 3.10.0-862.el7.x86_64、4.4.166-1.el7.elrepo.x86_64 等,用于区分来源和版本。

所以我们也需要给自编译的内核加上类似的版本字符串。 这里设置为 .0-el7.4pd.x86_64,对应的内核配置项为 General Setup -> Local version - append to内核 release:

``` bash

[root@m7-uiauto-ssd001 linux-4.14.110]# make menuconfig # 编辑后保存

[root@m7-uiauto-ssd001 linux-4.14.110]# diff ../elrepo/boot/config-4.14.15-1.el7.elrepo.x86_64 .config

3c3

< # Linux/x86_64 4.14.15-1.el7.elrepo.x86_64 kernel Configuration

---

> # Linux/x86 4.14.110 kernel Configuration

21c21

< CONFIG_LOCALVERSION=""

---

> CONFIG_LOCALVERSION=".0-el7.4pd.x86_64"

5231a5232

> # CONFIG_DRM_FBDEV_LEAK_PHYS_SMEM is not set

6178a6180

> # CONFIG_USB_ROLE_SWITCH is not set

```

### 并发编译内核

``` bash

[root@m7-uiauto-ssd001 linux-4.14.110]# make -j 24

```

### 安装内核模块

``` bash

[root@m7-uiauto-ssd001 linux-4.14.110]# make modules_install

```

内核模块被安装到系统的 /lib/modules/ 目录下,目录名是内核版本和上面配置的自定义版本字符串的组合:

``` bash

[root@m7-uiauto-ssd001 linux-4.14.110]# ls -l /lib/modules/ | grep 4pd

drwxr-xr-x  3 root root 4096 Apr  3 17:49 4.14.110.0-el7.4pd.x86_64

drwxr-xr-x  3 root root 4096 Apr  3 22:49 4.14.110.0-el7.4pd.x86_64

[root@m7-uiauto-ssd001 linux-4.14.110]# ls -l /lib/modules/4.14.110.0-el7.4pd.x86_64/

total 4084

lrwxrwxrwx  1 root root      30 Apr  3 22:49 build -> /usr/src/kernels/linux-4.14.110

drwxr-xr-x 13 root root    4096 Apr  3 22:49 kernel

-rw-r--r--  1 root root 1106323 Apr  3 22:49 modules.alias

-rw-r--r--  1 root root 1071287 Apr  3 22:49 modules.alias.bin

-rw-r--r--  1 root root    7058 Apr  3 22:49 modules.builtin

-rw-r--r--  1 root root    9279 Apr  3 22:49 modules.builtin.bin

-rw-r--r--  1 root root  341508 Apr  3 22:49 modules.dep

-rw-r--r--  1 root root  500405 Apr  3 22:49 modules.dep.bin

-rw-r--r--  1 root root    411 Apr  3 22:49 modules.devname

-rw-r--r--  1 root root  136971 Apr  3 22:49 modules.order

-rw-r--r--  1 root root    534 Apr  3 22:49 modules.softdep

-rw-r--r--  1 root root  437869 Apr  3 22:49 modules.symbols

-rw-r--r--  1 root root  537188 Apr  3 22:49 modules.symbols.bin

lrwxrwxrwx  1 root root      30 Apr  3 22:49 source -> /usr/src/kernels/linux-4.14.110

```

+ 内核模块目录中的 build 和 source 目录链接到内核源码目录,后续编译内核模块时需要这两个目录。

### 查看安装的设备驱动文件信息

``` bash

[root@m7-uiauto-ssd001 linux-4.14.110]# modinfo /lib/modules/4.14.110.0-el7.4pd.x86_64/kernel/drivers/scsi/megaraid/megaraid_sas.ko

filename:      /lib/modules/4.14.110.0-el7.4pd.x86_64/kernel/drivers/scsi/megaraid/megaraid_sas.ko

description:    Avago MegaRAID SAS Driver

author:        [email protected]

version:        07.706.03.00-rc1

license:        GPL

srcversion:    86E71DF0BD7853161D6991C

alias:          pci:v00001000d0000001Csv*sd*bc*sc*i*

alias:          pci:v00001000d0000001Bsv*sd*bc*sc*i*

alias:          pci:v00001000d00000017sv*sd*bc*sc*i*

alias:          pci:v00001000d00000016sv*sd*bc*sc*i*

alias:          pci:v00001000d00000015sv*sd*bc*sc*i*

alias:          pci:v00001000d00000014sv*sd*bc*sc*i*

alias:          pci:v00001000d00000053sv*sd*bc*sc*i*

alias:          pci:v00001000d00000052sv*sd*bc*sc*i*

alias:          pci:v00001000d000000CFsv*sd*bc*sc*i*

alias:          pci:v00001000d000000CEsv*sd*bc*sc*i*

alias:          pci:v00001000d0000005Fsv*sd*bc*sc*i*

alias:          pci:v00001000d0000005Dsv*sd*bc*sc*i*

alias:          pci:v00001000d0000002Fsv*sd*bc*sc*i*

alias:          pci:v00001000d0000005Bsv*sd*bc*sc*i*

alias:          pci:v00001028d00000015sv*sd*bc*sc*i*

alias:          pci:v00001000d00000413sv*sd*bc*sc*i*

alias:          pci:v00001000d00000071sv*sd*bc*sc*i*

alias:          pci:v00001000d00000073sv*sd*bc*sc*i*

alias:          pci:v00001000d00000079sv*sd*bc*sc*i*

alias:          pci:v00001000d00000078sv*sd*bc*sc*i*

alias:          pci:v00001000d0000007Csv*sd*bc*sc*i*

alias:          pci:v00001000d00000060sv*sd*bc*sc*i*

alias:          pci:v00001000d00000411sv*sd*bc*sc*i*

depends:

retpoline:      Y

intree:        Y

name:          megaraid_sas

vermagic:      4.14.110.0-el7.4pd.x86_64 SMP mod_unload modversions

parm:          lb_pending_cmds:Change raid-1 load balancing outstanding threshold. Valid Values are 1-128. Default: 4 (int)

parm:          max_sectors:Maximum number of sectors per IO command (int)

parm:          msix_disable:Disable MSI-X interrupt handling. Default: 0 (int)

parm:          msix_vectors:MSI-X max vector count. Default: Set by FW (int)

parm:          allow_vf_ioctls:Allow ioctls in SR-IOV VF mode. Default: 0 (int)

parm:          throttlequeuedepth:Adapter queue depth when throttled due to I/O timeout. Default: 16 (int)

parm:          resetwaittime:Wait time in seconds after I/O timeout before resetting adapter. Default: 180 (int)

parm:          smp_affinity_enable:SMP affinity feature enable/disable Default: enable(1) (int)

parm:          rdpq_enable: Allocate reply queue in chunks for large queue depth enable/disable Default: disable(0) (int)

parm:          dual_qdepth_disable:Disable dual queue depth feature. Default: 0 (int)

parm:          scmd_timeout:scsi command timeout (10-90s), default 90s. See megasas_reset_timer. (int)

```

### 安装内核文档

``` bash

[root@m7-uiauto-ssd001 linux-4.14.110]# install -d /usr/share/doc/linux-4.14.110.0-el7.4pd.x86_64

[root@m7-uiauto-ssd001 linux-4.14.110]# cp -r Documentation/* /usr/share/doc/linux-4.14.110.0-el7.4pd.x86_64

```

### 安装内核

``` bash

[root@m7-uiauto-ssd001 linux-4.14.110]# cp -iv arch/x86/boot/bzImage /boot/vmlinuz-4.14.110.0-el7.4pd.x86_64

'arch/x86/boot/bzImage' -> '/boot/vmlinuz-4.14.110.0-el7.4pd.x86_64'

```

### 安装内核配置文件

``` bash

[root@m7-uiauto-ssd001 linux-4.14.110]# cp -iv .config /boot/config-4.14.110.0-el7.4pd.x86_64

'.config' -> '/boot/config-4.14.110.0-el7.4pd.x86_64'

```

### 安装内核符号表文件

``` bash

[root@m7-uiauto-ssd001 linux-4.14.110]# cp -iv System.map /boot/System.map-4.14.110.0-el7.4pd.x86_64

'System.map' -> '/boot/System.map-4.14.110.0-el7.4pd.x86_64'

```

### 创建 initramfs 文件

创建 Booloader 加载的、启动时必须的 initramfs 文件:

``` bash

[root@m7-uiauto-ssd001 linux-4.14.110]# time mkinitrd /boot/initramfs-4.14.110.0-el7.4pd.x86_64.img 4.14.110.0-el7.4pd.x86_64

real  0m55.735s

user  0m59.108s

sys 0m55.645s

```

### 更新 Bootloader 配置文件

更新 GRUB Bootloader 配置文件:

``` bash

[root@m7-uiauto-ssd001 linux-4.14.110]# grub2-mkconfig -o /tmp/grub2.cfg

Generating grub configuration file ...

Found linux image: /boot/vmlinuz-4.14.110.0-el7.4pd.x86_64

Found initrd image: /boot/initramfs-4.14.110.0-el7.4pd.x86_64.img

Found linux image: /boot/vmlinuz-4.14.110.0-el7.4pd.x86_64

Found initrd image: /boot/initramfs-4.14.110.0-el7.4pd.x86_64.img

Found linux image: /boot/vmlinuz-4.4.166-1.el7.elrepo.x86_64

Found initrd image: /boot/initramfs-4.4.166-1.el7.elrepo.x86_64.img

Found linux image: /boot/vmlinuz-3.10.0-862.el7.x86_64

Found initrd image: /boot/initramfs-3.10.0-862.el7.x86_64.img

Found linux image: /boot/vmlinuz-0-rescue-d1db6964e41c45e19a373462a1b2e789

Found initrd image: /boot/initramfs-0-rescue-d1db6964e41c45e19a373462a1b2e789.img

done

[root@m7-uiauto-ssd001 linux-4.14.110]# cd /boot/grub2

[root@m7-uiauto-ssd001 grub2]# cp grub.cfg grub.cfg.bak.201904032254

[root@m7-uiauto-ssd001 grub2]# cp /tmp/grub2.cfg grub.cfg

```

### 设置从新内核启动

``` bash

[root@m7-uiauto-ssd001 grub2]# grub2-set-default 0

```

+ 第二个参数值 0 为内核 menuentry 在 grub.cfg 中的序号。

### 重启机器,确认使用了新版内核

``` bash

zhangjun:~ zhangjun$ ssh root@m7-uiauto-ssd001

Last login: Wed Apr  3 22:18:02 2019 from 172.27.153.58

[root@m7-uiauto-ssd001 ~]# uname -a

Linux m7-uiauto-ssd001 4.14.110.0-el7.4pd.x86_64 #1 SMP Wed Apr 3 22:41:54 CST 2019 x86_64 x86_64 x86_64 GNU/Linux

```

## TODO

1. 将编译后的内核打包成 RPM 包,便于后续安装;

2. grub 配置支持 UEFI;

3. 支持 NVIDIA GPU;

## 参考

1. [Kernel/Traditional compilation](https://wiki.archlinux.org/index.php/Kernel/Traditional_compilation)

2. [Redhat/CentOS 3.10 内核 Bugs 导致K8S和Docker异常的情况汇总](https://wiki.4paradigm.com/pages/viewpage.action?pageId=44415151)

3. [华夏升级4.4内核后启动hang](https://wiki.4paradigm.com/pages/viewpage.action?pageId=50566372)

4. [交行数据上传下载慢](https://wiki.4paradigm.com/pages/viewpage.action?pageId=47120044)

5. [Kubernetes And Docker Troubleshooting](https://wiki.4paradigm.com/display/PROD/Kubernetes+And+Docker+Troubleshooting)

你可能感兴趣的:(2019-04-04)