docker编译openwrt

  1. docker构建编译所需的系统镜像
  2. 下载源代码
  3. 首次编译
  4. 选择自己需要的软件再次编译
  5. 集成第三方软件包编译/编译单独ipk

为了不让编译环境污染宿主机,采用docker的方式编译,由docker为我们创建一个专门用于编译openwrt的系统,执行docker build的时候会自动下载编译工具所需要的依赖。你可以使用别人写好的Dockerfile文件: https://github.com/mwarning/docker-openwrt-build-env

1
2
git clone https://github.com/mwarning/docker-openwrt-builder.git
cd docker-openwrt-builder

查看Dockerfile,可以看到是基于debian的系统,安装了一些依赖,并创建了一个user用户(原因是不能使用root用户编译,也不能使用sudo执行编译)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
FROM debian:buster

RUN apt-get update &&\
    apt-get install -y \
        sudo time git-core subversion build-essential g++ bash make \
        libssl-dev patch libncurses5 libncurses5-dev zlib1g-dev gawk \
        flex gettext wget unzip xz-utils python python-distutils-extra \
        python3 python3-distutils-extra python3-setuptools swig rsync curl \
        libsnmp-dev liblzma-dev libpam0g-dev cpio rsync gcc-multilib && \
    apt-get clean && \
    useradd -m user && \
    echo 'user ALL=NOPASSWD: ALL' > /etc/sudoers.d/user

# set system wide dummy git config
RUN git config --system user.name "user" && git config --system user.email "user@example.com"

USER user
WORKDIR /home/user

为了加快构建速度,使用国内的源,在FROM debian:buster后面添加一行

1
RUN sed -i 's/deb.debian.org/mirrors.ustc.edu.cn/g' /etc/apt/sources.list

此时Dockerfile如下

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
FROM debian:buster
RUN sed -i 's/deb.debian.org/mirrors.ustc.edu.cn/g' /etc/apt/sources.list

RUN apt-get update &&\
    apt-get install -y \
        sudo time git-core subversion build-essential g++ bash make \
        libssl-dev patch libncurses5 libncurses5-dev zlib1g-dev gawk \
        flex gettext wget unzip xz-utils python python-distutils-extra \
        python3 python3-distutils-extra python3-setuptools swig rsync curl \
        libsnmp-dev liblzma-dev libpam0g-dev cpio rsync gcc-multilib && \
    apt-get clean && \
    useradd -m user && \
    echo 'user ALL=NOPASSWD: ALL' > /etc/sudoers.d/user

# set system wide dummy git config
RUN git config --system user.name "user" && git config --system user.email "user@example.com"

USER user
WORKDIR /home/user

构建镜像

1
docker build -t openwrt_builder .

/docker%E7%BC%96%E8%AF%91openwrt/Pasted%20image%2020240226093400.png

执行此命令后,我们本地就多出了一个安装好编译依赖的debian镜像

1
2
root@tignioj:~/docker-openwrt-builder# docker images | grep openwrt
openwrt_builder            latest     0175798f5da9   4 weeks ago     716MB

创建编译系统的容器(镜像类似于系统的安装光盘,是固定的,容器类似于安装后的系统,可以开机关机、安装软件)

1
2
mkdir ~/mybuild
docker run -v ~/mybuild:/home/user --name openwrt_builder -itd openwrt_builder

进入容器

1
docker exec -it openwrt_builder /bin/bash

修改当前目录所属用户给user(这个user用户是在Dockerfile中创建的)

1
sudo chown -R user:user .

经过上面的步骤,我们进入了一个已经准备好编译环境的系统,此时可以开始跟着官方的步骤开始编译了

下载openwrt源代码:

1
git clone https://git.openwrt.org/openwrt/openwrt.git

进入代码目录

1
cd openwrt

最好使用稳定版 git checkout 指定版本,而不是默认使用HEAD分支,如果你不使用稳定版,会带来某些问题,比如opkg安装程序会报错内核版本不匹配

1
2
3
# Select a specific code revision
git branch -a
git tag  # 查看有哪些分支

切换到指定版本

1
git checkout v23.05.2 # 指定稳定版
1
2
3
# Update the feeds
./scripts/feeds update -a
./scripts/feeds install -a
1
2
# Configure the firmware image
make menuconfig

先认识一下界面

/docker%E7%BC%96%E8%AF%91openwrt/Pasted%20image%2020240226085544.png

在这个例子里面,我们暂时使用x86平台,到后面我们再使用指定的路由器平台,所以这些默认不动即可!

/docker%E7%BC%96%E8%AF%91openwrt/Pasted%20image%2020240226083923.png

openwrt编译默认不带luci的web界面,你需要手动勾选安装,找到, LuCI-> Collections-> luci,双击使得前面的变成*符号

/docker%E7%BC%96%E8%AF%91openwrt/Pasted%20image%2020240226085646.png

设置web界面为中文, 双击空格使得前面的< >变成<*>符号

1
LuCI->Modules->Translations -> <*> Chinese Simplified (zh_Hans)

我们选择x86平台就是为了能在宿主机上运行,为了能docker中运行openwrt,找到target image勾选tar.gz (默认是勾选上的,没有自己勾上)

/docker%E7%BC%96%E8%AF%91openwrt/Pasted%20image%2020240127153010.png

接着保存配置菜单,移动到Save,回车

/docker%E7%BC%96%E8%AF%91openwrt/Pasted%20image%2020240226085951.png
选择OK
/docker%E7%BC%96%E8%AF%91openwrt/Pasted%20image%2020240226085958.png
然后光标移动到EXIT退出菜单。

1
2
# Build the firmware image
make download -j$(nproc)
  • -j$(nproc), 其中 nproc会返回你系统的最大线程数量,例如-j8表示7线程编译(会保留一个线程防止系统卡死)
  • V=s: 打印详细信息

编译前,请确保有良好的科学环境,终端输入curl -I www.google.com ,检查状态码是否为200,如果卡住了说明网络环境不适合编译。

1
2
HTTP/1.1 200 OK
Content-Type: text/html; charset=ISO-8859-1

第一次编译推荐使用多线程编译,一个小时以内可以完成。单线程编译可能要5小时。

1
make -j$(nproc)

如果编译出错了,那么就单线程编译一遍,前面多线程编译过的内容会跳过。通常出问题都是网络问题。

1
make -j1 V=s

加上V=s后可以看到详细的错误信息,例如可能出现的网络问题

/docker%E7%BC%96%E8%AF%91openwrt/Pasted%20image%2020240302043626.png

编译成功后,到这里你可以看到在bin/target/x86/64目录下看到编译的固件

/docker%E7%BC%96%E8%AF%91openwrt/Pasted%20image%2020240226104638.png

怎么在docker运行我们编译好的固件?请查看-> index.zh-cn

经过第一次编译后,后面再次编译速度就会快很多,这时候我们就可以选择自己需要的插件编译进固件里面,例如 samba4

1
make menuconfig

找到LuCI->Applications->luci-app-samba4, 双击空格使得前面的<>变成<*>,其中*表示集成进固件里面, M表示作为ipk包。

/docker%E7%BC%96%E8%AF%91openwrt/Pasted%20image%2020240226105556.png
光标移动到save,保存.config,然后再次编译,发现速度会快很多。

1
<*> luci-app-dockerman.................. LuCI Support for docker

提醒:仅针对x86平台,如果编译luci-app-dockerman,则需要自己手动勾选依赖dockerd,否则docker无法正常启动

在Utilities下找到,把前面的设置成<*>

1
<*> dockerd............ Docker Community Edition Daemon  --->
1
 <*> luci-app-p910nd........... p910nd - Printer server module

找到内核Kernal Modules -> USB Support

1
 <*> kmod-usb-printer...... ........ Support for printers

Base System 中选中 block-mount

1
2
3
-*- kmod-usb-storage............................. USB Storage support
<*> kmod-usb-storage-extras............ Extra drivers for usb-storage
<*> kmod-usb-storage-uas............... USB Attached SCSI (UASP) support

提醒:dnsmasq和dnsmasq-full不能同时勾选。例如选中passwall第三方插件时,可能会出现这种情况,请到Base System中取消调dnsmasq的勾选

1
2
make -j$(nproc) download
make -j$(nproc)

经过上面的的步骤,你已经学会了基本的编译,此时可以尝试添加第三方的软件包 https://github.com/kenzok8/openwrt-packages

执行

1
2
3
4
5
6
sed -i '$a src-git kenzo https://github.com/kenzok8/openwrt-packages' feeds.conf.default
sed -i '$a src-git small https://github.com/kenzok8/small' feeds.conf.default
git pull
./scripts/feeds update -a
./scripts/feeds install -a
make menuconfig

找到LuCI->Applications,勾选需要的软件,依赖会自动勾选

按下空格选中M表示作为ipk包编译

1
<M> luci-app-alist............ LuCI support for alist 

再次按下空格,出现*表示集成到固件里面

1
<*> luci-app-alist............ LuCI support for alist 

然后开始编译

1
2
make -j$(nproc) download
make -j$(nproc)
1
<M> luci-app-alist............ LuCI support for alist 

开始编译

1
make package/luci-app-alist/compile V=s

ipk生成路径,可以使用find命令查找

1
find bin/  -name "*alist*"   
1
2
3
4
5
user@c6ba0d0ab225:~/openwrt$ find bin/  -name "*alist*"                                                                                       
bin/packages/aarch64_cortex-a53/kenzo/luci-i18n-alist-zh-cn_1.0.11-1_all.ipk
bin/packages/aarch64_cortex-a53/kenzo/alist_3.30.0-2_aarch64_cortex-a53.ipk
bin/packages/aarch64_cortex-a53/kenzo/luci-app-alist_1.0.11-1_all.ipk
user@c6ba0d0ab225:~/lede$ 

然后把这些ipk上传到路由器上执行即可

1
2
3
opkg install luci-i18n-alist-zh-cn_1.0.11-1_all.ipk
opkg install alist_3.30.0-2_aarch64_cortex-a53.ipk
opkg install luci-app-alist_1.0.11-1_all.ipk

或者在web界面上传安装

1
sudo apt install libfuse-dev

注意:对于官方openwrt的固件,修改root分区大小后,如果刷到路由器里面,需要重新刷GPT和uboot,否则可能不生效。

找到 Target Images -> (102) Root filesystem partition size (in MiB) , 把102改为自己想要的大小。

1
mkdir -p files/etc/uci-defaults

files/etc/uci-defaults/添加脚本,等同于往路由器的/etc/uci-defaults/中添加脚本。

1
vim files/etc/uci-defaults/99-custom

在99-custom添加自定义ip地址、dns和网关命令

1
2
3
4
5
uci -q batch << EOI
set network.lan.ipaddr='192.168.30.99'
set network.lan.dns='192.168.30.1'
set network.lan.gateway='192.168.30.1'
EOI

然后编译出来的固件,就会使用你的自定义配置.

该目录中的所有脚本都会由boot服务自动执行,且仅在全新安装后的首次启动时执行!

  • 如果它们以代码 0 退出,则它们随后将被删除。
  • 以非零退出代码退出的脚本不会被删除,并将在下次启动时重新执行,直到它们也成功退出。

如果你选择了自定义路由器平台,官方openwrt编译出来的是.itb格式的固件,需要用到tftp刷机方式,不兼容常见的第三方uboot刷入方式。可参考教程:

暂时不清楚有什么优点

  • 简介:lede是openwrt的一个分支,默认使用中文,集成了一些基本的插件。

  • 编译方法:类似openwrt,其实就是仿造 https://github.com/mwarning/docker-openwrt-build-env 这个编写了一个linux环境,然后在这个环境里面执行编译

  • 这次我们不下载他们Dockerfile,而是自己仿造一个

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
FROM debian:buster
RUN sed -i 's/deb.debian.org/mirrors.ustc.edu.cn/g' /etc/apt/sources.list

RUN apt-get update &&\
    apt-get install -y \
        sudo time git-core subversion build-essential g++ bash make \
        libssl-dev patch libncurses5 libncurses5-dev zlib1g-dev gawk \
        flex gettext wget unzip xz-utils python python-distutils-extra \
        python3 python3-distutils-extra python3-setuptools swig rsync curl \
        libsnmp-dev liblzma-dev libpam0g-dev cpio rsync gcc-multilib && \
    apt-get clean && \
    useradd -m user && \
    echo 'user ALL=NOPASSWD: ALL' > /etc/sudoers.d/user

# set system wide dummy git config
RUN git config --system user.name "user" && git config --system user.email "user@example.com"

USER user
WORKDIR /home/user

构建镜像

1
docker build -t lede_builder .

运行镜像

1
docker run  -v ~/lede_mybild:/home/user lede_builder /bin/bash
1
2
3
4
5
git clone https://github.com/coolsnowwolf/lede
cd lede
./scripts/feeds update -a
./scripts/feeds install -a
make menuconfig

第一次编译建议不要勾选任何插件,因为第一次编译包含了很多基础包的编译,过程比较持久,如果加上了插件造成报错可能会感到困惑:到底是插件的问题,还是我系统没配置好?因此第一次仅仅勾选你的路由器平台即可。这里拿RAX3000M举例,首先选择平台,接着是芯片,第三项是具体型号

/docker%E7%BC%96%E8%AF%91openwrt/Pasted%20image%2020240226112522.png

默认情况下,openwrt和lede后台地址都是192.168.1.1,有没有办法在编译的时候自定义呢?当然可以,只需要在编译的根目录下创建文件夹files,然后往里面添加初始化脚本即可。files相当于路由器的根目录

1
mkdir -p files/etc/uci-defaults

假设我们要自定义ip地址

1
vim files/etc/uci-defaults/99-custom

往里面添加内容

1
2
3
4
5
6
uci -q batch << EOI
set network.lan.ipaddr='192.168.30.101'
set network.lan.dns='192.168.30.1'
set network.lan.gateway='192.168.30.1'
delete uhttpd.main.listen_https
EOI
  • 注意到我这里删掉了uhttpd的https监听地址,原因是lede默认没有安装luci-app-openssl,如果不关闭https监听会无法启动web界面(仅x86)

开始编译固件 (-j 后面是线程数)

1
2
make download -j8
make -j$(nproc)

如果发现编译出错,那么可以使用单线程编译,并输出详细信息。大部分情况下的首次编译出现错误都是网络问题。

1
make -j1 V=s

编译完成后,可以在bin/target/平台目录下看到自己编译后的包,其中 xxx-squashfs-sysupgrade.bin就是我们要的固件

/docker%E7%BC%96%E8%AF%91openwrt/Pasted%20image%2020240226064650.png

经过前面的首次编译后,一些基础的包都已经编译完成,再次编译时候会跳过他们。此时选择自己需要的插件编译速度,就取决于插件本身。

1
make menuconfig

选择自己的插件后

1
2
make download -j$(nproc)
make -j$(nproc)

注意,勾选luci应用后,依赖会自动勾选上,此时再次取消勾选luci,依赖不会取消,如果需要重新配置,请删掉.config

1
2
3
rm -rf .config
make menuconfig
make -j$(nproc)

或者再选择插件前,先备份一下.config

1
cp .config .config.backup

一些发行版会添加自己的拓展包,例如lede和immortalWRT的代码中都有automount和autosamba,但是这些官方openwrt是没有的。

默认情况下lede的代码没有勾选ipv6-helper,请到 Extra pckages勾选ipv6-helper

Extra packages -> automount

Extra packages -> autosamb

注意:这个脚本有BUG,在RAX3000M-emmc勾选了此拓展包会导致无线网络消失。删除后才能恢复,因此不建议使用此拓展包。详细信息: https://github.com/immortalwrt/immortalwrt/issues/1201

与openwrt的相同,请参考上面

  • 地址: https://github.com/immortalwrt/immortalwrt
  • 简介: immortalwrt甚至集成了很多第三方的软件包,无需额外添加软件源,感觉更方便,编译步骤和lede一样,过程不再赘述。

Dockerfile文件,根据官网描述,建议基于ubuntu20.04-LTS,那么第一行的FROM就要改了

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
FROM ubuntu:20.04
RUN sed -i s@/archive.ubuntu.com/@/mirrors.aliyun.com/@g /etc/apt/sources.list

RUN apt-get update &&\
  DEBIAN_FRONTEND=noninteractive  apt install -y \
  sudo ack antlr3 asciidoc autoconf automake autopoint binutils bison build-essential \
  bzip2 ccache clang cmake cpio curl device-tree-compiler ecj fastjar flex gawk gettext gcc-multilib \
  g++-multilib git gnutls-dev gperf haveged help2man intltool lib32gcc-s1 libc6-dev-i386 libelf-dev \
  libglib2.0-dev libgmp3-dev libltdl-dev libmpc-dev libmpfr-dev libncurses5-dev libncursesw5 \
  libncursesw5-dev libpython3-dev libreadline-dev libssl-dev libtool lld llvm lrzsz mkisofs msmtp \
  nano ninja-build p7zip p7zip-full patch pkgconf python2.7 python3 python3-pip python3-ply \
  python3-docutils python3-pyelftools qemu-utils re2c rsync scons squashfs-tools subversion swig \
  texinfo uglifyjs upx-ucl unzip vim wget xmlto xxd zlib1g-dev 

RUN apt-get clean && \
    useradd -m user && \
    echo 'user ALL=NOPASSWD: ALL' > /etc/sudoers.d/user

# set system wide dummy git config
RUN git config --system user.name "user" && git config --system user.email "user@example.com"

USER user
WORKDIR /home/user

注意到这里还加了一行 DEBIAN_FRONTEND=noninteractive,防止创建镜像的过程出现交互行为。

1
docker build -t immortalwrt_builder .
1
docker run -itd --name iwt_builder -v ~/iwt_builder:/home/user immortalwrt_builder 
1
docker exec -it iwt_builder bash

注意,docker里面的ubuntu系统需要修改用户目录权限给user才能下载源代码

1
sudo chown -R user:user .

下载源代码

1
2
git clone -b openwrt-23.05 --single-branch --filter=blob:none https://github.com/immortalwrt/immortalwrt
cd immortalwrt

安装feeds

1
2
./scripts/feeds update -a
./scripts/feeds install -a

编译菜单,同样,先别选择插件,仅选择你的平台即可!

1
make menuconfig

首次编译

1
make -j$(nproc)

选择插件后再次编译

1
2
make menuconfig
make -j$(nproc)
1
make V=s 2>&1 | tee build.log | grep -i -E "^make.*(error|[12345]...Entering dir)"

另一种方法是检查相应的logs文件夹,如make[3] -C package/kernel/mac80211 compile,那么可以转到<buildroot>/logs/package/kernel/mac80211查看compile.txt

1
make ...; echo -e '\a'

加入某个包编译错误了,

1
2
3
4
5
# Ignore compilation errors
IGNORE_ERRORS=1 make ...
 
# Ignore all errors including firmware assembly stage
make -i ...

tmux小技巧往期文章-> index.zh-cn

  • 如果是远程ssh连接服务器编译,最好使用tmux,可以多窗口,且ssh断掉后进程不会中断,再次ssh进入服务器可以回到tmux会话。 创建一个名称为openwrt的session
1
tmux new -s openwrt

面板垂直分割,键盘按下快捷键。以下<prefix> 表示同时按下Ctrl + B

  • 例如下面这个命令,表示同时按下Ctrl + B 后,松开键盘,再按下%
1
<prefix> + %

面板水平分割

1
<prefix> + "

退出tmux,但不退出tmux的进程

1
<prefix>  + Q

回到tmux

1
tmux attach

没研究

相关内容

在线选择自己的openwrt固件
RAX3000M使用官方OpenWRT的23.05.2一些问题以及解决方案
RAX3000M-openwrt使用ipv6中继
RAX3000M搞机目录
openwrt编译passwall到固件里面