标签归档:docker

做了一个用来生成中国大陆路由的容器

背景

因为在研究BGP,也有中国大陆与其他地区进行路由选路的需求,所以在琢磨着要不在互联网上整一个bgp speaker,然后这个bgp speaker向所有连过来的对端通告全部的中国大陆路由,这样路由器就不需要配置大量的静态路由,减少配置量,而且更新的话也能够应用到所有出口路由器。

实时全球路由表?

某天,根据这篇文章试了一下,确实可行,但是吧……这效果强虽强,但是资源消耗也有点离谱。

这个容器实时从RIPE读取全球路由表的变更,一整天下来平均带宽有1.78Mbit/s,相当于一天就跑了18G的流量(单向)。要是放在vps上的话,一个月估计的流量500G。

我是放在家里出口的RouterOS上以容器的方式跑的,算上容器本身和加的路由表,需要吃掉大约400M的内存。我就想想,有没有一些更加轻量级的方案呢?

记得之前是见到过一个chnroute的东西,可以生成中国的路由表。然后灵机一动,要不我也用脚本写个差不多的玩意吧,看起来也不是很难。bgp软件话似乎可以用bird,可以直接重载,也不会闪断。

脑袋一拍,开干,结果就弄了个这样的玩意出来。

设计思路

github上的README也已经写了设计思路的,非常简单,以bird软件作为核心然后主要的配置有3个:

一个是手动写的需要配置的静态路由,比如说苹果的7.0.0.0/8,实测如果走VPN的话会遇到各种各样奇奇怪怪的问题,可能和我用的是自建的DNS递归服务器,然后我又用国区账号,部分苹果业务由云上贵州来负责的原因。反正就是会有这样的需求,先预留着。

一个是通过脚本生成中国的路由表,从网上搜一搜就找到类似的了,核心是从APNIC下载分发的资源表,过滤出属于中国的部分,然后通过文本处理格式化成bird能够读取的配置文件。然后定期重新加载就行了。

最后一个是协议的配置,我现在选择的是用bgp来输出,有其他需要的话可以考虑ospf或者isis之类的,不过bgp作为各ISP之间互联用的协议,又是我的学习目标,果断选择BGP来输出了。这块的配置我放在了custom.conf,有需要可以灵活修改。

最后,为了方便部署,选择了容器化,用docker打个包,就可以到处用了。现在是托管在dockerhub,未来可能放在其他地方。

如何使用

这里强烈推荐一下claw cloud的白嫖,只要有一个注册时间超过180天的github账号就可以使用他们的免费等级的服务,可以有5刀的credit,不绑信用卡也可以用。他们目前有新加坡,日本,美西岸,美东岸,德国几个区域。每个区域最高能放4个vcpu,8G内存,和10G的磁盘。(当然,如果拉满的话,价格是绝对会超5刀每月的),限制了每个月10G的流量。也限制了每个机器只能有1个端口(tcp或者udp)。

限制是挺多的,特别是流量,不过对于我这个拍脑袋整出来的东西,已经可以算得上是奢华了。从APNIC下载delegation的文件大概3.8M,每天一次,然后算上bgp的连接,一天估计跑不了10M。

于是,噔噔蹬蹬。

膨胀了啊,在k8s上直接replica 2!然而内存使用量还是太少了,都识别不出来。

下面贴一下过程:

先去注册,然后切换到需要部署的区域,然后点击App Launchpad。

在右上角点击create App进入部署的界面。

Application Name随便填,image选择public,然后填入ferrets/cn_route-bird。没错我已经预编译了一份并且上传到了dockerhub。

Usage里面CPU和RAM都拉到最低(为啥RAM不能选16M?)。Replica建议大于1,反正价格你也看到了,每天0.02刀,30天就是……0.6刀(手动狗头)。

Network里面Container Port写179,这是bgp的默认端口。打开互联网访问,完了之后会分配一个。

Advanced Configuration里面有一个需要填的,就是Configmaps。点击Add。

File Name写/etc/bird.d/custom.conf

File content就复制粘贴github上的内容就行,填完了之后confirm。

最后右上角Deploy Application就行。过一阵子,你就有了能够直接用bgp订阅的,能够自动更新的中国大陆路由表了。

考虑到高可用,和剩余的credit……找另外一个区域重复上述过程,避免一个区炸了之后就直接丢了路由。

配置订阅

下一步就是用出口路由器去连了这个bird。首先去App Launchpad,找到分配了啥网址和端口

然后,手动解析一下这个域名。(或者你的路由器可以直接填域名而不是ip地址来建立bgp会话,那就填域名)

从里面挑几个出来建立bgp会话,参考的mikrotik的配置脚本如下:

记得先写到中国大陆的静态路由。根据配置,bgp吐出来的ipv4的路由下一跳是114.114.114.114,ipv6的路由下一跳是240c::6666。必须先写静态路由以保证递归之后,从bgp会话收到的路由是从你想要的出口出口,避免路由震荡。或者你可以先在connection里面写上input.filter,先把路由全部过滤。搞好之后再取消过滤,应用到实际环境里面。

/ip route
add dst=114.114.114.114 gateway=wan
/ipv6 route
add dst=240c::6666 gateway=wan
/routing bgp template
add address-families=ip,ipv6 as=65000 cluster-id=192.168.0.0 disabled=no multihop=yes name=feed output.filter-chain=block .no-client-to-client-reflection=yes routing-table=main
/routing bgp connection
add template=feed connect=yes local.role=ibgp name=cn_route_feed-1 remote.address=[刚才解析出来的ip1] .as=65000 .port=[分配的端口]
add template=feed connect=yes local.role=ibgp name=cn_route_feed-1 remote.address=[刚才解析出来的ip2] .as=65000 .port=[分配的端口]
add template=feed connect=yes local.role=ibgp name=cn_route_feed-1 remote.address=[刚才解析出来的ip3] .as=65000 .port=[分配的端口]
add template=feed connect=yes local.role=ibgp name=cn_route_feed-1 remote.address=[刚才解析出来的ip4] .as=65000 .port=[分配的端口]

然后……为啥RouterOS不能去重路由……明明我都在bird上配置了cluster id。囧

如此一来,就有了相对稳定的,免费的,可以自动更新的,资源消耗相对低的中国路由表订阅了。

虽然订阅了8份之后,RouterOS里面的路由表数量高达85k+,都比实时全球路由表的数量(45k+)高一倍了。但……但这有高贵的ipv6啊!

Proxmox中LXC容器中docker调用宿主机的显卡加速

刚整了个AgentDVR,里面设置支持GPU解码,于是寻思着整一个,因为J4125的CPU带6路视频录像确实有点吃力,即使是使用了摄像头的stream2,用的是低分辨率的视频流来进行分析,对这个轻量级的CPU来说还是有点吃力。

于是进行了一番Google之后,顺利实现,下面记录一下过程。注意的是,我这里使用的是intel的核显,也就是vaapi,如果是AMD或者Nvidia的显卡的话,肯定会有点不一样,具体的差别需要继续搜索。

宿主机安装驱动

首先,安装显卡驱动和相关的查看工具

apt install i965-va-driver intel-media-va-driver vainfo

根据包的说明i965-va-driver是给Intel G45 & HD Graphics的显卡驱动,而intel-media-va-driver是8代之后的显卡驱动,可以根据实际情况酌情选择。至于vainfo,就是查看相关信息的工具。安装之后执行vainfo,可以看到显卡的加速信息:

root@pve4:~# vainfo
error: can't connect to X server!
libva info: VA-API version 1.10.0
libva info: Trying to open /usr/lib/x86_64-linux-gnu/dri/iHD_drv_video.so
libva info: Found init function __vaDriverInit_1_10
libva info: va_openDriver() returns 0
vainfo: VA-API version: 1.10 (libva 2.10.0)
vainfo: Driver version: Intel iHD driver for Intel(R) Gen Graphics - 21.1.1 ()
vainfo: Supported profile and entrypoints
      VAProfileMPEG2Simple            : VAEntrypointVLD
      VAProfileMPEG2Main              : VAEntrypointVLD
      VAProfileH264Main               : VAEntrypointVLD
      VAProfileH264Main               : VAEntrypointEncSliceLP
      VAProfileH264High               : VAEntrypointVLD
      VAProfileH264High               : VAEntrypointEncSliceLP
      VAProfileJPEGBaseline           : VAEntrypointVLD
      VAProfileJPEGBaseline           : VAEntrypointEncPicture
      VAProfileH264ConstrainedBaseline: VAEntrypointVLD
      VAProfileH264ConstrainedBaseline: VAEntrypointEncSliceLP
      VAProfileVP8Version0_3          : VAEntrypointVLD
      VAProfileHEVCMain               : VAEntrypointVLD
      VAProfileHEVCMain10             : VAEntrypointVLD
      VAProfileVP9Profile0            : VAEntrypointVLD
      VAProfileVP9Profile2            : VAEntrypointVLD

能看到显卡支持的编码,就算是成功了。这时候,可以在母机的/dev/dri/文件夹下面看到以下内容:

root@pve4:/etc/pve/lxc# ls -als /dev/dri
total 0
0 drwxr-xr-x  3 root root        100 Mar 12 16:22 .
0 drwxr-xr-x 23 root root       5140 Mar 17 07:40 ..
0 drwxr-xr-x  2 root root         80 Mar 12 16:22 by-path
0 crw-rw----  1 root video  226,   0 Mar 12 16:22 card0
0 crw-rw----  1 root render 226, 128 Mar 12 16:22 renderD128

将显卡透传给LXC容器

先给上两个参考文档:

简单来说,就是直接用文本编辑器修改lxc容器的配置文件,加上这么一块

lxc.cgroup2.devices.allow: c 226:0 rwm
lxc.cgroup2.devices.allow: c 226:128 rwm
lxc.mount.entry: /dev/dri/renderD128 dev/dri/renderD128 none bind,optional,create=file
lxc.mount.entry: /dev/dri/card0 dev/dri/card0 none bind,optional,create=file

根据proxmox论坛里面的说明,如果是proxmox 6或者之前的,就用lxc.cgroup。如果是Promox 7或者之后的,就用lxc.cgrpup2。保存退出。

启动LXC容器,再看看/dev/dri/就能看到已经挂载了:

root@ispy:~# ls -als /dev/dri
total 0
0 drwxr-xr-x 2 root   root          80 Mar 17 09:44 .
0 drwxr-xr-x 8 root   root         520 Mar 17 09:44 ..
0 crw-rw---- 1 nobody nogroup 226,   0 Mar 12 08:22 card0
0 crw-rw---- 1 nobody nogroup 226, 128 Mar 12 08:22 renderD128

然后,就是最后一个问题,权限问题,由于lxc容器有隔离,从上面的结果可以看到card0和renderD128被映射成了nobody:nogroup的ownership。这时候就有这么个几种解决办法:

简单粗暴,母机对两个设备给与666的权限

在母机上,直接运行

chmod 666 /dev/dri/card0 /dev/dri/renderD128

使得显卡所有人可以读写。但是这种方法存在一定的安全隐患,所以用来测试配置是否成功的时候可以用,测试OK的话,应该换个别的方法来赋予权限。

idmap+usermod

思路是这样,lxc容器有个设定,叫做idmap,用来控制容器内部的uid和gid如何映射到母机上对应的部分的。例如这个参考:https://bookstack.swigg.net/books/linux/page/lxc-gpu-access

首先,考虑到两个设备默认的权限是660,所以我们有几个方向:

  • 将LXC容器中需要调用显卡的用户映射为母机的root(明显有严重的安全隐患,而且无法处理多个用户需要调用显卡的情况,pass)
  • 修改/dev/dri/card0 /dev/dri/renderD128的ownership,然后和上面一样将LXC容器中需要调用显卡的用户映射为这个新的owner(也还行,但是同样无法处理多用户同时调用,而且母机系统的变动比较多)
  • 将LXC容器中的video组和render组映射到母机的video和render组,然后将需要调用显卡的用户都加入这两个组(不错,但是对于不同的系统,就需要检查对应的/etc/group,并进行对应的调整)

我选择第3种方法。

首先,要确认一下系统的video组和render组,首先是母机的,因为系统都是proxmox(debian),所以gid理论上都是一样的,video的gid是44,render是103:

root@pve4:/etc/pve/lxc# cat /etc/group
……
video:x:44:root
……
render:x:103:root
……

然后就是检查LXC容器的用户组,我用的是Ubuntu20.04,video是gid是44,render是107:

root@ispy:/# cat /etc/group
……
video:x:44:
……
render:x:107:

准备工作就做好了。接下来关掉容器,直接用文本编辑器修改LXC容器的配置文件,加入这么一块内容:

lxc.idmap: u 0 100000 65535
lxc.idmap: g 0 100000 44
lxc.idmap: g 44 44 1
lxc.idmap: g 45 100045 62
lxc.idmap: g 107 103 1
lxc.idmap: g 108 100108 65427

下面给一点解释:

lxc.idmap: u 0 100000 65535		//映射LXC容器中的uid,将容器中[0-65535)映射为母机的[100000-165535)
lxc.idmap: g 0 100000 44		//映射LXC容器中的gid,将容器中[0-44)映射为母机的[100000-100044)
lxc.idmap: g 44 44 1			//映射LXC容器中的gid,将容器中[44-45)映射为母机的[44-45)
lxc.idmap: g 45 100045 62		//映射LXC容器中的gid,将容器中[45-106)映射为母机的[100045-100106)
lxc.idmap: g 107 103 1			//映射LXC容器中的gid,将容器中[107-108)映射为母机的[103-104)
lxc.idmap: g 108 100108 65427	//映射LXC容器中的gid,将容器中[108-65535)映射为母机的[100108-165535)

然后就是要配置/etc/subgid文件,添加以下内容:

root:44:1
root:103:1

最后,把LXC容器开起来,将需要调用显卡的用户加入两个组:

root@ispy:/# usermod -G video root
root@ispy:/# usermod -G render root

执行测试

测试的方法和母机差不多,都是用vainfo来检查,或者使用ffmpeg来测试,但这两个都需要安装一堆软件包,所以这时候,先给容器打个快照。

vainfo的方法可以参考母机,不同的发行版有不同的包管理器,安装vainfo然后运行就能看到结果了。

另外一个方法是使用ffmpeg,这个更接近实际应用,毕竟很多应用也是通过调用ffmpeg来执行编解码的操作。具体ffmpeg的使用方法可以参考官方的文档

这里给出比较有用的一个参考:

ffmpeg -hwaccel vaapi -hwaccel_output_format vaapi -i input.mp4 -f null -

这样可以让ffmpeg调用vaapi来进行解码,并且不输出任何东西,如果能够顺利解码,那就算是成功了。

LXC透传给Docker

透传给Docker的部分不算非常困难,只需要在docker-compose文件里面加上这么一节就可以透进去了:


version: '2.4'
services:
  agentdvr:
    image: doitandbedone/ispyagentdvr
    restart: unless-stopped
    environment:
      - TZ=Asia/Shanghai
    ports:
      - 8090:8090
      - 3478:3478/udp
      - 50000-50010:50000-50010/udp
    volumes:
      - ./ispyagentdvr/config:/agent/Media/XML
      - ./ispyagentdvr/media:/agent/Media/WebServerRoot/Media
      - ./ispyagentdvr/commands:/agent/commands
    devices:
      - /dev/dri:/dev/dri

但是我们遇到了和LXC透传的时候一样的问题,文件权限的问题。ispy提供的docker镜像是基于Ubuntu18.04的,


root@34c54716eee7:/# ls -las /dev/dri
total 0
0 drwxr-xr-x 2 root   root        80 Mar 17 17:44 .
0 drwxr-xr-x 6 root   root       360 Mar 17 17:44 ..
0 crw-rw---- 1 nobody video 226,   0 Mar 12 16:22 card0
0 crw-rw---- 1 nobody   107 226, 128 Mar 12 16:22 renderD128
root@34c54716eee7:/# cat /etc/group 
root:x:0:
……
video:x:44:
……
messagebus:x:102:

容器里面就没有render组,root用户也不在两个用户组里面。于是还得自己build一下镜像……

root@ispy:~/agentdvr# cat build/Dockerfile 
FROM doitandbedone/ispyagentdvr
RUN     groupadd -g 107 render
RUN     usermod -g 44 root
RUN     usermod -g 107 root

完了之后就可以在docker 容器中调用GPU进行加速了。

建立私有docker镜像源

参考:https://linuxhint.com/setup_own_docker_image_repository/

最近非常着迷docker和kubernetes这玩意,感觉完全就是未来发展方向,于是决定好好研究一番。

有时候服务器处于内网环境,不好上网扒镜像,又或者说,由于一些众所周知的原因,从docker hub扒镜像会非常的缓慢,所以,我觉得有必要建立一个本地的docker镜像源。

建立方法

建立方法很简单,docker官方提供了一个镜像方便人们建立私有的镜像源。

根据参考文档,可以直接用一条命令来将服务跑起来,就像这样:

docker container run -d -p 5000:5000 --name registry -v<br> ~/docker/registry:/var/lib/registry registry

不过我个人推荐使用yaml文件+docker compose,方便日后维护,也习惯一下这种方式,为以后上kubernetes做准备。我就弄了一个,内容如下:

[root@docker-repo ~]# cat repo.yaml 
version: "2.4"
services:
  private_docker_repo:
    image: library/registry:latest
    ports:
      - "5000:5000"
    restart: unless-stopped
    volumes:
      - /storage:/var/lib/registry

然后,只要用

docker-compose -f repo.yaml up -d

就可以将服务跑起来了。-f参数是用来指定yaml文件,如果使用了docker-compose.yml作为文件名,可以省略掉。不过推荐自定义文件名,这样就可以将很多yaml放在同一个文件夹了。

日后如果registry镜像有更新,或者是修改什么设置,只要修改好配置文件,再pull一次和up一次即可,docker-compose会处理好更新的。

例子中,我的给机器挂了个100G的盘,挂在了/storage,到时候,数据会存在这里。

DNS修改

然后就是处理dns或者hosts,根据参考文档来看,不做也行,不过做了的话,可以少打几只字。( =ω=)

推荐做dns强制解析,那就不需要和原文一样去修改hosts。以后镜像源迁移、或者是docker母机增加减少之类的就不需要每次都处理hosts文件了。

举个例子,我修改DNS,将pdr(private docker registry)解析为镜像源服务器地址。

在docker母机上增加源

默认情况下,docker只会从docker hub拉镜像,要指定似有的源,就要修改一下配置文件。配置文件是/etc/docker/daemon.json,如果没有的话,就新建一个,添加以下内容:

{
"insecure-registries": ["pdr:5000", "192.168.11.207:5000"]
}

然后,重启docker。(对,是要重启docker的,所有容器都会重启)然后就可以在客户端使用这个似有源了。

对于在私有源上的镜像,用法和官方源差不多,只不过用户名变成了“服务器ip:端口”,于是,镜像名称格式如下:

IP:PORT/IMAGE_NAME:TAG_NAME

比如说,mariadb,在docker hub上,镜像的名称就是mariadb,或者其他用户的话就是xxx/mariadb(xxx是用户名)。但是在私有源上,就是192.168.11.207:5000/mariadb,或者在处理了dns或者hosts之后,可以用pdr:5000/mariadb。(看,可以少打很多个字!)

至此,似有镜像源搭建完成!

至于镜像源里面有什么镜像和有什么tag这个我暂时还没发现,暂时只发现在/storage/docker/registry/v2/repositories能看到有啥镜像。根据官方文档的说明:可以使用 http://服务器IP:5000/v2/_catalog 查看都有什么image。

列出镜像

做了个小脚本,放在docker-compose文件挂载的目录里面,我的是/storage,然后就可以直接列出镜像和列出指定镜像的tag了

#!/bin/bash
case $1 in
	"")
		ls -l docker/registry/v2/repositories/ | grep "drwx" | awk '{print $9}'
		;;
	*)
		ls -l docker/registry/v2/repositories/$1/_manifests/tags/ | grep "drwx" | awk '{print $9}'
		;;
esac

效果如下图