Docker交叉编译cgo

1,156 阅读2分钟

1.前⾔

越来越多的ARM服务器进⼊了我们的视野,docker等容器也都在跨平台编译给出了⽀持,随着国产化的需求不断加强,软件产物在国产化芯⽚交付量越来越多 ,交叉编译显得尤为重要

2.Buildx

macOS 或 Windows 系统的 Docker Desktop,以及 Linux 发⾏版通过 deb 或者 rpm 包所安装的 docker 内置了 buildx,可以使⽤命令 docker buildx 命令查看是否安装了buildx,如果没有安装的话,可以到buildx的release下⾯⼆进制即可,安装⽅式如下:

export DOCKER_BUILDKIT=1
# 当然,此处使⽤的⼆进制版本可能不适合你,可以直接到https://github.com/docker/buildx/releases寻
找适合你的
wget https://github.com/docker/buildx/releases/download/v0.8.2/buildx-v0.8.2.linuxamd64
mv buildx-v0.8.2.linux-amd64 buildx
mkdir -p ~/.docker/cli-plugins
mv buildx ~/.docker/cli-plugins/docker-buildx

buildx 构建的⽅法如下:

# buildx 和 docker build 命令的使⽤体验基本⼀致
docker buildx build .

3.golang跨平台构建镜像示例 本人的开发环境是mac m1 芯片 服务器环境为linux arm 架构 本案例为go加载so动态库的简单demo

  1. 代码目录结构如下

image.png

  1. add.c
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

char* Add(char* src, int n)
{
     char str[20];
     sprintf(str, "%d", n);
     char *result = malloc(strlen(src)+strlen(str)+1);
     strcpy(result, src);
     strcat(result, str);
     return result;
}

3.add.h

#ifndef __ADD_H__
#define __ADD_H__

char* Add(char* src, int n);

#endif

4.main.go

package main

/*
// 头文件的位置,相对于源文件是当前目录,所以是 .,头文件在多个目录时写多个  #cgo CFLAGS: ...
#cgo CFLAGS: -I./include
// 从哪里加载动态库,位置与文件名,-ladd 加载 libadd.so 文件
#cgo LDFLAGS: -L./lib -ladd -Wl,-rpath,lib
#include "add.h"
*/
import "C"
import "fmt"

func main() {
   val := C.Add(C.CString("go"), 2022)
   fmt.Println("run c: ", C.GoString(val))
}
  1. dockerfile
FROM   golang:1.17.11-stretch 

ENV CGO_ENABLED=1
ENV GOPROXY=https://goproxy.cn,direct
ENV GOOS=linux
ENV GOARCH=arm
ENV CC=arm-linux-gnueabi-gcc
ENV LD_LIBRARY_PATH=/usr/arm-linux-gnueabi/lib/
RUN apt-get update
RUN apt-get install gcc-arm-linux-gnueabi -y
WORKDIR /app
COPY . .
RUN arm-linux-gnueabi-gcc  -fPIC -shared -o lib/libadd.so ./include/add.c &&  ln -s /usr/arm-linux-gnueabi/lib/ld-linux.so.3 /lib/ld-linux.so.3
RUN go build -o main main.go
RUN export LD_LIBRARY_PATH=/usr/arm-linux-gnueabi/lib/
RUN echo $LD_LIBRARY_PATH
CMD ["./main"]
  1. 构建arm平台运行的镜像产物
#--platform 为指定构建哪些平台的镜像产物
docker buildx build --platform linux/amd64,linux/arm64 -t demo:tag -f dockerfile --push .

有几个坑,需要注意

Docker 在 Linux 系统架构下是不支持 arm 架构镜像,因此我们可以运行一个新的容器让其支持该特性,Docker 桌面版则无需进行此项设置(mac系统)。 需要在内核中使用 QEMU 仿真支持来进行多架构镜像构建

# 安装模拟器(用于多平台镜像构建)
docker run --rm --privileged tonistiigi/binfmt:latest --install all
# 验证是 binfmt_misc 否开启
ls -al /proc/sys/fs/binfmt_misc/
# 验证是否启用了相应的处理器
cat /proc/sys/fs/binfmt_misc/qemu-aarch64