I recently went through the same pain and finally got this to work.
Here is what I did:
1) Clone https://github.com/linuxkit/linuxkit.git
2) Follow the instructions here to modify the kernel as you want: https://github.com/linuxkit/linuxkit/blob/master/docs/kernels.md#modifying-the-kernel-config
Specifically you want to add the CAN network as a module and the CAN vcan driver as a module.
3) Copy your modified config (for example config-4.9.x-x86_64
) to linuxkit/kernel
to override the one that is already there.
4) From linuxkit/kernel
run make build_4.9.x HASH=mods HASH_COMMIT=HEAD
5) Using the simple Dockerfile below (call it 'Dockerfile.kernel') run docker build . -f Dockerfile.kernel -t kernel_extract
(there is probably a more elegant way to do this, but I'm somewhat of a docker n00b).
6) Run the image with docker run -it --entrypoint=/bin/sh kernel_extract
7) In the image extract the modules by doing:
cd /ksrc
tar -xvf kernel.tar
find lib/modules/ -name "*.ko" | grep can > /tmp/files
tar cpzf can.tar.gz -T /tmp/files
8) While the container is still running copy the tarball out with:
id=$(docker container ls | grep kernel_extract | awk '{print $1}')
docker cp $id:/ksrc/can.tar.gz .
9) Install the can.tar.gz
files in your docker image.
10) Run your docker image with --net=host
and --cap-add=ALL
to make sure you can load the kernel modules and setup the interface.
11) When you startup your docker image you need to run some code like this (inside your container, I used a shell script on the entrypoint to do this and then exec the thing I really want to do):
for module in "can" "can-raw" "can-gw" "can-bcm" "can-dev" "vcan"; do
module_name=$(echo $module | tr "-" "_")
lsmod | grep $module_name >/dev/null
if [ $? -ne 0 ]; then
insmod $(find /lib/modules -name "${module}.ko")
fi
done
for iface in "can0" "can1"; do
ip link show $iface >/dev/null 2>&1
if [ $? -ne 0 ]; then
ip link add $iface type vcan
ip link set $iface up
fi
done
Because you are messing with the host kernel (in this case the hyperkit vm that is running on mac), it will persist beyond each individual container run, which is why there is care to check that it doesn't try to insmod twice.
It is unfortunately kind of convoluted, and perhaps someone with more docker nuance could describe a more elegant way, but that does work!
Dockerfile.kernel:
FROM linuxkit/kernel:4.9.184-mods-amd64 AS ksrc
FROM linuxkit/alpine:3fdc49366257e53276c6f363956a4353f95d9a81 AS build
RUN apk add build-base
COPY --from=ksrc / /ksrc
ENTRYPOINT ["/bin/sh"]