【保姆级教程】Docker服务在双架构(X86和ARM)编译统一实践

2023-05-16

在现代计算机系统中,X86和ARM64是两种常见的处理器架构。为了满足不同架构的需求,Docker镜像也需要支持双架构编包形式。本文将介绍Docker镜像双架构编包统一的实践

    一、Docker镜像编包

    在Docker镜像中,通常使用多阶段构建来实现。在第一阶段,构建出对应架构服务的二进制文件;在第二阶段,运行对应架构的二进制文件,下面是两个基于Debian的Dockerfile文件示例,分别用于构建X86架构和ARM64架构的Docker镜像:

1.X86架构的Dockerfile文件示例:

FROM debian:latest AS builder

RUN apt-get update && apt-get install -y build-essential

WORKDIR /app

COPY . .

RUN make

FROM debian:latest

WORKDIR /app

COPY --from=builder /app/app /app

CMD ["/app/app/install.sh"]

CMD ["/app/app/build.sh"]

复制

 2.ARM64架构的Dockerfile文件示例:

FROM arm64v8/debian:latest AS builder

RUN apt-get update && apt-get install -y build-essential

WORKDIR /app

COPY . .

RUN make

FROM arm64v8/debian:latest

WORKDIR /app

COPY --from=builder /app/app /app

CMD ["/app/app/install_arm.sh"]

CMD ["/app/app/build_arm.sh"]

复制

      这两个Dockerfile文件的主要区别在于基础镜像的选择和FROM语句中的架构标识符。X86架构的Dockerfile文件使用了debian:latest作为基础镜像,而ARM64架构的Dockerfile文件使用了arm64v8/debian:latest作为基础镜像。此外,ARM64架构的Dockerfile文件在FROM语句中使用了arm64v8标识符,以指定ARM64架构。最后,CMD执行的安装脚本也不一样,应该是不同的环境需要不同的安装脚本。

    为了方便在双架构环境下部署Docker服务,可以编写一个bash脚本,以执行docker build命令的形式来调用上面两种Dockerfile文件的运行。下面是两个示例bash脚本:

1.X86的bash脚本

#!/bin/bash

docker build -t myapp:x86 -f Dockerfile.x86 .

docker manifest create myapp:latest myapp:x86

docker manifest push myapp:latest

复制

2.Arm64的bash脚本

#!/bin/bash

docker build -t myapp:arm64 -f Dockerfile.arm64 .

docker manifest create myapp:latest myapp:arm64

docker manifest push myapp:latest

复制

      这个bash脚本中,首先使用docker build命令分别构建X86架构和ARM64架构的Docker镜像,并分别打上myapp:x86和myapp:arm64的标签。然后,使用docker manifest create命令创建一个名为myapp:latest的manifest文件,并将myapp:x86和myapp:arm64的标签添加到manifest文件中。最后,使用docker manifest push命令将manifest文件推送到Docker Hub上,以便在不同架构的计算机系统上使用myapp:latest标签来获取Docker镜像。

具体流程大概是这样:

    二、Docker镜像多架构编包统一

从上面的流程图中,可以看到,编译双架构的镜像基本上需要两套完全独立的脚本,这显然会增加代码量和维护成本,那么有没有可以统一多架构编包的脚本和流程内?答案是显然的,下面就以上面的流程为例,生成一套多架构统一的编译脚本集。

1.合并build_docker.sh脚本

首先是编镜像的启动脚本build_docker.sh,这里之所以有两个脚本,是因为要执行不同的dockerfile,事实上,可以通过传入参数的形式,来动态决定执行不同的dockersfile,比如下面这个示例:

#!/bin/bash

# 获取传入的架构参数
ARCH=$1

# 根据不同的架构参数,构建不同的Dockerfile文件
case $ARCH in
  "x86_64")
    DOCKERFILE="Dockerfile.x86_64"
    ;;
  "armv7l")
    DOCKERFILE="Dockerfile.armv7l"
    ;;
  "aarch64")
    DOCKERFILE="Dockerfile.aarch64"
    ;;
  *)
    echo "Unsupported architecture: $ARCH"
    exit 1
    ;;
esac

# 构建Docker镜像
docker build -t myimage:$ARCH -f $DOCKERFILE .

复制

 当然,如果业务本身已经有很多参数了,问了避免混淆和命令层级的一致性,也可以使用opt的别名进行,比如:

#!/bin/bash

# 默认架构为x86_64
ARCH="x86_64"

# 处理命令行参数
while getopts "a:" opt; do
  case $opt in
    a)
      ARCH=$OPTARG
      ;;
    \?)
      echo "Invalid option: -$OPTARG" >&2
      exit 1
      ;;
    :)
      echo "Option -$OPTARG requires an argument." >&2
      exit 1
      ;;
  esac
done

# 根据不同的架构参数,构建不同的Dockerfile文件
case $ARCH in
  "x86_64")
    DOCKERFILE="Dockerfile.x86_64"
    ;;
  "armv7l")
    DOCKERFILE="Dockerfile.armv7l"
    ;;
  "aarch64")
    DOCKERFILE="Dockerfile.aarch64"
    ;;
  *)
    echo "Unsupported architecture: $ARCH"
    exit 1
    ;;
esac

# 构建Docker镜像
docker build -t myimage:$ARCH -f $DOCKERFILE .

复制

 这里有个优雅的点:如果没有指定-a选项,则默认使用x86_64架构,方便与已有编译脚本的融合和兼容。如果传入的架构参数不支持,脚本会输出错误信息并退出。

当然,业务需要可能是一次编译多个架构,那还需要对这个脚本再更新一下:

#!/bin/bash

# 默认架构为x86_64
ARCHS=("x86_64")

# 处理命令行参数
while getopts "a:" opt; do
  case $opt in
    a)
      ARCHS+=("$OPTARG")
      ;;
    \?)
      echo "Invalid option: -$OPTARG" >&2
      exit 1
      ;;
    :)
      echo "Option -$OPTARG requires an argument." >&2
      exit 1
      ;;
  esac
done

# 如果有两个以上的架构参数,则同时构建多个Docker镜像
if [ ${#ARCHS[@]} -ge 2 ]; then
  # 构建Docker镜像
  docker buildx build --platform "${ARCHS[@]/#/--platform }" -t myimage .
else
  # 只有一个架构参数,则按照之前的方式构建Docker镜像
  ARCH=${ARCHS[0]}
  # 根据不同的架构参数,构建不同的Dockerfile文件
  case $ARCH in
    "x86_64")
      DOCKERFILE="Dockerfile.x86_64"
      ;;
    "armv7l")
      DOCKERFILE="Dockerfile.armv7l"
      ;;
    "aarch64")
      DOCKERFILE="Dockerfile.aarch64"
      ;;
    *)
      echo "Unsupported architecture: $ARCH"
      exit 1
      ;;
  esac

  # 构建Docker镜像
  docker build -t myimage:$ARCH -f $DOCKERFILE .
fi

复制

这里涉及到--platform的使用,对应的dockerfile为:

FROM --platform=$BUILDPLATFORM golang:1.14 as builder

复制

事实上,还有其他的dockerfile命令可以用

架构相关变量

Dockerfile 支持如下架构相关的变量

TARGETPLATFORM

构建镜像的目标平台,例如 linux/amd64, linux/arm/v7, windows/amd64

TARGETOS

TARGETPLATFORM 的 OS 类型,例如 linux, windows

TARGETARCH

TARGETPLATFORM 的架构类型,例如 amd64, arm

TARGETVARIANT

TARGETPLATFORM 的变种,该变量可能为空,例如 v7

BUILDPLATFORM

构建镜像主机平台,例如 linux/amd64

BUILDOS

BUILDPLATFORM 的 OS 类型,例如 linux

BUILDARCH

BUILDPLATFORM 的架构类型,例如 amd64

BUILDVARIANT

BUILDPLATFORM 的变种,该变量可能为空,例如 v7

那么通过这三种方式,做到了build_docker.sh脚本的统一

2.合并dockerfile文件

刚才,主要解决了build_docker.sh的合并统一,现在还要解决dockfile文件的一致的问题

在刚才的build_docker脚本中使用

docker build或者

docker buildx build --platform的命令运行dockerfile文件

这里由于dockerfile文件需要根据不同的架构进行编包,内容不同,所以写了两个文件dockerfile.x86和dockerfile.arm

如果可以将架构信息传递到dockerfile中,则可以将这两个文件合二为一

这里主要的执行命令为:

docker build --build-arg ARCH=x86_64 -t myimage:x86_64 .

复制

对应的dockerfile文件为:

# 构建参数
ARG ARCH

# 根据不同的架构,选择不同的基础镜像
FROM ${ARCH}/debian:latest

# 安装必要的软件包
RUN apt-get update && apt-get install -y gcc g++ make && rm -rf /var/lib/apt/lists/*

WORKDIR /app
COPY . .

RUN make

FROM ${ARCH}/debian:latest

WORKDIR /app 

COPY --from=builder /app/app /app 

CMD ["/app/app/install.sh"]

CMD ["/app/app/build.sh"]

复制

当然这里只是做了个样例,实际上除了FROM里面还有一些安装脚本需要选择,这里就需要用到了IF ELSE命令

 修改上面的脚本如下:

# 构建参数
ARG ARCH

# 根据不同的架构,选择不同的基础镜像
FROM ${ARCH}/debian:latest

# 安装必要的软件包
RUN apt-get update && apt-get install -y gcc g++ make && rm -rf /var/lib/apt/lists/*

# 复制应用程序源代码
COPY app /app

# 根据不同的架构,选择不同的应用程序目录
RUN if [ "$ARCH" = "x86_64" ]; then \
        cp -r /app/install_x86.sh /app/install.sh; \
    elif [ "$ARCH" = "arm64v8" ]; then \
        cp -r /app/install_arm.sh /app/install.sh; \
    else \
        echo "Unsupported architecture: $ARCH"; \
        exit 1; \
    fi

# 运行安装程序
CMD ["/app/install.sh"]

复制

 通过上面的方法,基本上实现了一个dockerfile文件的多架构镜像编译

那么情况变成了这个样子:

离成功又进了一步

3.合并安装依赖脚本(install.sh)

和上面类似,通过变量传入进行修改

#!/bin/bash

# 构建参数
ARCH=$1

# 安装不同的环境依赖
if [ "$ARCH" = "x86_64" ]; then
    apt-get update && apt-get install -y gcc g++ make
elif [ "$ARCH" = "arm64v8" ]; then
    apt-get update && apt-get install -y gcc-aarch64-linux-gnu g++-aarch64-linux-gnu make
else
    echo "Unsupported architecture: $ARCH"
    exit 1
fi

# 下载并编译golang程序
wget https://example.com/myapp.tar.gz
tar -xzf myapp.tar.gz
cd myapp
GOOS=linux GOARCH=$ARCH go build -o myapp

# 运行golang程序
./myapp

复制

当然,一般来说依赖安装会稍微复杂一些,有些涉及的是ARm和非ARM的版本问题,有的是版本号的升级和降级,除了上文额if和else之外,还可以使用sed –i命令进行个性化修改,比如安装脚本是这样的:

#!/bin/bash

# 安装x86架构的环境依赖
apt-get update && apt-get install -y gcc g++ make libssl-dev

# 下载并编译golang程序
wget https://example.com/myapp.tar.gz
tar -xzf myapp.tar.gz
cd myapp
GOOS=linux GOARCH=amd64 go build -o myapp

# 运行golang程序
./myapp

复制

那外部调用脚本可以用下面的方式进行调整

#!/bin/bash

# 修改install.sh中的环境依赖
sed -i 's/apt-get install -y gcc g++ make libssl-dev/apt-get install -y gcc-aarch64-linux-gnu g++-aarch64-linux-gnu make libssl-dev/g' install.sh

# 调用安装脚本
./install.sh

复制

当然sed -i是比较灵活的修改方式,需要注意可维护性,不然,可能出现改一个脚本,导致一堆脚本不可用

当然,看到这里,可能有个疑问,dockerfile的多架构适配是不是也可以用sed -i的方法,而不用ARG的传参?

这里笔者也比较了下两者的不同

最后,这里的建议是把基本的安装依赖作为基础镜像单独存储,这样可以避免在多个业务镜像中重复编译

大概是这样:

  三、golang多架构编译

1.Golang多系统多架构编译

在Golang中,我们可以通过不同的文件后缀来实现多架构编译。这是因为Golang的编译器可以根据文件后缀来判断需要编译的架构类型。首先,让我们来了解一下不同的文件后缀代表的含义。在Golang中,文件后缀通常由两部分组成,分别是操作系统(GOOS)和架构(GOARCH)。例如,文件名为“hello_windows_amd64.exe”,其中“windows”代表操作系统为Windows,“amd64”代表架构为64位的x86架构。

下面是一个基本的Golang多系统多架构编译示例:

package main

import "fmt"

func main() {
    fmt.Println("Hello, world!")
}

复制

 在Linux操作系统下,可以使用以下命令编译该程序:

$ go build -o hello_windows_amd64.exe

复制

 在ARM处理器架构下,可以使用以下命令编译该程序:

go build -o hello_linux_amd64

复制

这个方法在很多的ARM的信创适配上比较常用

以github上比较常见的日志库为例:

适配时报了这个错误

因为使用了Dup2这个方法报错,dup2是dup命令的一种,还有dup和dup3命令,三者的区别如下

dup(int filedes)函数返回一个可用的与filedes共享文件表项的最小描述符

dup2(int filedes,int filedes2)是使用一个描述符filedes2去指向filedes文件表项(也是共享)

dup3(int oldfd, int newfd, int flags)和dup2相似,不同在于,可以通过指定flags为O_CLOEXEC强制置位新文件描述符的 close-on-exec 标志

事实上,三个方法除了功能上的差异外,在平台适配上也有些不同:

Darwin(MacOS)的X86架构支持: Dup2

Linux的X86架构支持:    Dup2、Dup3

Linux的arm架构支持: Dup3

所以进行适配时,可以根据不同的平台编译不同的文件分别定义对应的方法实现,比如:

2.CGo多系统多架构编译

CGO是Go语言中用于与C语言进行交互的工具,它可以让我们在Go语言中调用C语言的函数和使用C语言的库。在进行CGO编译时,我们需要考虑多系统多架构的问题,以确保我们的程序可以在不同的操作系统和架构中正常运行。

下面是一些CGO多系统多架构编译的方法:

2.1 使用CGO_ENABLED环境变量

使用CGO_ENABLED环境变量。CGO_ENABLED环境变量可以用来控制CGO是否启用。在进行多系统多架构编译时,我们可以设置CGO_ENABLED环境变量为0,这样就可以禁用CGO,从而避免在不同的操作系统和架构中出现问题。

下面是一个具体的例子,假设我们需要编译一个使用了libcurl库的Go程序,并且需要在Linux和Windows操作系统中分别编译出x86和x64架构的程序。我们可以使用以下命令来进行编译:

CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build -o myprogram-linux-amd64 main.go 
CGO_ENABLED=1 GOOS=linux GOARCH=386 go build -o myprogram-linux-386 main.go 
CGO_ENABLED=1 GOOS=windows GOARCH=amd64 go build -o myprogram-windows-amd64.exe main.go 
CGO_ENABLED=1 GOOS=windows GOARCH=386 go build -o myprogram-windows-386.exe main.go

复制

这个命令会分别编译出Linux和Windows操作系统中的x86和x64架构的程序

2.2 使用交叉编译工具

使用交叉编译工具。交叉编译工具可以让我们在一台机器上编译出多个不同操作系统和架构的程序。在进行CGO编译时,我们可以使用交叉编译工具来编译出多个不同操作系统和架构的程序,从而确保我们的程序可以在不同的操作系统和架构中正常运行。

下面是一个详细的cgo交叉编译的例子,假设我们需要编译一个使用了libcurl库的Go程序,并且需要在Linux和Windows操作系统中分别编译出x86和x64架构的程序。

安装交叉编译工具 首先,我们需要安装交叉编译工具。在Ubuntu系统中,我们可以使用以下命令来安装交叉编译工具:

sudo apt-get install gcc-arm-linux-gnueabihf
sudo apt-get install gcc-mingw-w64-x86-64

复制

 这个命令会安装arm-linux-gnueabihf和mingw-w64-x86-64交叉编译工具,分别用于编译ARM和Windows x64架构的程序。

编写Go程序 接下来,我们需要编写一个使用了libcurl库的Go程序。假设我们的程序代码如下:

package main

// #cgo LDFLAGS: -lcurl
// #include <curl/curl.h>
import "C"

import (
    "fmt"
    "unsafe"
)

func main() {
    curl := C.curl_easy_init()
    if curl == nil {
        fmt.Println("Failed to initialize curl")
        return
    }
    defer C.curl_easy_cleanup(curl)

    url := C.CString("https://www.example.com")
    defer C.free(unsafe.Pointer(url))

    C.curl_easy_setopt(curl, C.CURLOPT_URL, url)

    res := C.curl_easy_perform(curl)
    if res != C.CURLE_OK {
        fmt.Println("Failed to perform curl request")
        return
    }

    fmt.Println("Curl request succeeded")
}

复制

 这个程序使用了libcurl库来发送HTTP请求。在程序中,我们使用了CGO LDFLAGS关键字来链接libcurl库,并使用了C语言的头文件来调用libcurl库的函数。

编译ARM架构的程序 接下来,我们需要编译ARM架构的程序。我们可以使用以下命令来编译ARM架构的程序:

CGO_ENABLED=1 GOOS=linux GOARCH=arm GOARM=7 CC=arm-linux-gnueabihf-gcc go build -o myprogram-arm main.go

复制

这个命令会使用arm-linux-gnueabihf-gcc交叉编译工具来编译ARM架构的程序,并使用CGO LDFLAGS关键字来链接libcurl库。其中,GOOS=linux表示编译Linux操作系统的程序,GOARCH=arm表示编译ARM架构的程序,GOARM=7表示编译ARMv7架构的程序。

编译Windows x64架构的程序 最后,我们需要编译Windows x64架构的程序。我们可以使用以下命令来编译Windows x64架构的程序:

CGO_ENABLED=1 GOOS=windows GOARCH=amd64 CC=x86_64-w64-mingw32-gcc go build -o myprogram-windows.exe main.go

复制

 这个命令会使用x86_64-w64-mingw32-gcc交叉编译工具来编译Windows x64架构的程序,并使用CGO LDFLAGS关键字来链接libcurl库。其中,GOOS=windows表示编译Windows操作系统的程序,GOARCH=amd64表示编译x64架构的程序。

这里还有一个比较好的例子:

如何使用 docker buildx 构建跨平台 Go 镜像

2.3 使用CGO LDFLAGS等关键字

使用CGO LDFLAGS等关键字。在进行CGO编译时,我们可以使用CGO LDFLAGS等关键字来指定需要链接的库和编译选项。这些关键字可以让我们在不同的操作系统和架构中使用不同的链接库和编译选项,从而确保我们的程序可以在不同的操作系统和架构中正常运行。

#cgo指令符是用于在Go语言中调用C语言函数和库的关键字。它可以让我们在Go语言中使用C语言的函数和库,从而扩展Go语言的功能。在进行cgo多架构编译时,我们可以使用#cgo指令符来指定不同操作系统和架构下的编译选项。

下面是一些#cgo指令符在cgo多架构编译中的使用方法:

#cgo CFLAGS #cgo CFLAGS指令符可以用来指定C语言编译器的编译选项。在进行多架构编译时,我们可以使用#cgo CFLAGS指令符来指定不同操作系统和架构下的编译选项。例如,我们可以使用以下指令符来指定ARM架构下的编译选项:

#cgo CFLAGS: -march=armv7-a -mfpu=neon

复制

这个指令符会在ARM架构下使用-march=armv7-a和-mfpu=neon编译选项来编译C语言代码。

#cgo LDFLAGS #cgo LDFLAGS指令符可以用来指定链接器的选项。在进行多架构编译时,我们可以使用#cgo LDFLAGS指令符来指定不同操作系统和架构下的链接选项。例如,我们可以使用以下指令符来指定Windows x64架构下的链接选项:

#cgo LDFLAGS: -L/usr/local/lib -lcurl

复制

 这个指令符会在Windows x64架构下使用-L/usr/local/lib和-lcurl链接选项来链接libcurl库。

#cgo windows #cgo windows指令符可以用来指定Windows操作系统下的编译选项。在进行多架构编译时,我们可以使用#cgo windows指令符来指定不同操作系统下的编译选项。例如,我们可以使用以下指令符来指定Windows操作系统下的编译选项:

#cgo windows CFLAGS: -D_WIN32_WINNT=0x0601

复制

 这个指令符会在Windows操作系统下使用-D_WIN32_WINNT=0x0601编译选项来编译C语言代码

#cgo linux

#cgo windows指令符可以用来指定Linux操作系统下的编译选项。在进行多架构编译时,我们可以使用#cgo linux指令符来指定不同操作系统下的编译选项。例如,我们可以使用以下指令符来指定Linux操作系统下的编译选项:

#cgo linux CFLAGS: -D_GNU_SOURCE

复制

 这个指令符会在Linux操作系统下使用-D_GNU_SOURCE编译选项来编译C语言代码。

#cgo darwin #cgo darwin指令符可以用来指定macOS操作系统下的编译选项。在进行多架构编译时,我们可以使用#cgo darwin指令符来指定不同操作系统下的编译选项。例如,我们可以使用以下指令符来指定macOS操作系统下的编译选项:

#cgo darwin CFLAGS: -mmacosx-version-min=10.10

复制

 这个指令符会在macOS操作系统下使用-mmacosx-version-min=10.10编译选项来编译C语言代码。

#cgo linux,arm64 和 #cgo linux,amd64

#cgo linux,amd64 LDFLAGS: /lib/linux/liba.a
#cgo linux,arm64 LDFLAGS: /lib/linux/liba_arm.a

复制

 等价于

#cgo linux,!arm64 LDFLAGS: /lib/linux/liba.a
#cgo linux,!amd64 LDFLAGS: /lib/linux/liba_arm.a

复制

通过上面两个代码层级的编译一致性,可以得到在编译阶段也可以做到合并统一

这时流程图变成了这样的

完美!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【保姆级教程】Docker服务在双架构(X86和ARM)编译统一实践 的相关文章

  • 数分下(第1讲):一阶微分方程的三类模型求解

    第1 1讲 xff1a 一阶微分方程的解法 第一周第一讲将用3个小时时间 xff0c 完整讲解一阶微分方程y 61 f x y 的三种典型模型求解方法 掌握以下知识点 xff0c 并熟练做题训练 对应同济高数教材第七章1 4节 知识点脑图如
  • 非常详细的 Linux C/C++ 学习路线总结!助我拿下腾讯offer

    点击关注上方 五分钟学算法 xff0c 设为 置顶或星标 xff0c 第一时间送达干货 转自后端技术学堂 正文 我的另一篇文章 腾讯 C 43 43 后台开发面试笔试知识点参考笔记 整理了 C 43 43 后台开发知识点 xff0c 本文尝
  • 一线互联网公司程序员技术面试的流程以及注意事项

    先来了解面试的流程是什么 xff0c 然后再一一做准备 xff01 企业一般通过几轮技术面试来考察大家的各项能力 xff0c 一般流程如下 xff1a 一面机试 xff1a 一般会考选择题和编程题 二面基础算法面 xff1a 就是基础的算法
  • 为什么C++永不过时?

    Linus曾说过 xff1a C 43 43 是一门很恐怖的语言 xff0c 而比它更恐怖的是很多不合格的程序员在使用着它 xff01 这足以说明C 43 43 有多难 xff01 不过 xff0c 你也要明白 难度越高意味着含金量与竞争力
  • STM32 USB学习笔记6

    主机环境 xff1a Windows 7 SP1 开发环境 xff1a MDK5 14 目标板 xff1a STM32F103C8T6 开发库 xff1a STM32F1Cube库和STM32 USB Device Library 现在来分
  • Invalid bound statement (not found)

    目录 一 遇到的问题 二 分析思路 1 映射文件 2 测试类 三 解决方案 一 遇到的问题 前几日 xff0c 有个工作不久的同事找我帮他解决一个 Mybatis 的问题 他写了一个增删改查 xff0c 但是在启动程序的时候报错 xff1a
  • ThinkPHP6 解决小程序调用接口返回错误是网页的尴尬

    背景 早在开始了解ThinkPHP时就一直记得一段话 xff1a 在一开始无知的我以为出现错误后能在调试阶段优雅的了解错误信息 xff0c 但结果大家试一下便知道 xff0c 十分尴尬 尤其是当在小程序里请求api xff0c 在过程中发生
  • 大数据技术Canal总结和详细案例

    0 Canal介绍 Canal 是用 Java 开发的基于数据库增量日志解析 xff0c 提供增量数据订阅 amp 消费的中间件 目前Canal 主要支持了 MySQL 的 Binlog 解析 xff0c 解析完成后才利用 Canal Cl
  • T507 Ubuntu18.04 LXDE桌面汉化

    本文硬件平台采用飞凌T507开发板 xff0c 主要讲解Ubuntu图形桌面LXDE如何修改为中文界面 xff0c 本文使用的思路和方法仅供参考使用 xff0c 其它arm开发板虽然芯片不同 xff0c 但思路和方法有很多的共性 xff0c
  • 工作站和台式机的区别

    转自 xff1a 微点阅读 xff08 www weidianyuedu com xff09 微点阅读 范文大全 免费学习网站 工作站电脑非常高配 xff0c 那么它和台式机有什么区别呢 下面由小编给你做出详细的工作站和台式机区别介绍 希望
  • 分布式锁-简单入门

    状态不是很好 xff0c 记一下以前学过的分布式锁吧 样例简介 不谈大概念 xff0c 就是简单入门以及使用 为什么要用分布式锁呢 xff1f 假设我需要一个定时操作 xff0c 每天在某个点 xff0c 我要处理一批数据 xff0c 要先
  • 深度神经网络的成功应用,深度神经网络发展历程

    深度学习与神经网络有什么区别 深度学习与神经网络关系2017 01 10最近开始学习深度学习 xff0c 基本上都是zouxy09博主的文章 xff0c 写的蛮好 xff0c 很全面 xff0c 也会根据自己的思路 xff0c 做下删减 x
  • Ubuntu22.04启用root账户 2208120941

    Ubuntu22 04启用root账户 2208120941 Ubuntu是有root账户的 只是没有密码 所以无法切换 所以启用的方法是 管理员账户用 sudo passwd root 给 root 设置密码 span class tok
  • WARNING: CLOCK SKEW DETECTED. YOUR BUILD MAY BE INCOMPLETE

    make时遇到这个问题几次了 xff0c 就记录一下吧 WARNING CLOCK SKEW DETECTED YOUR BUILD MAY BE INCOMPLETE 警告 xff1a 检测到时钟偏移 您的构建可能不完整 xff08 来自
  • ROS学习(五):构建系统

    ROS的构建系统默认使用CMake xff08 Cross Platform Make xff09 xff0c 其构建环境在功能 包目录中的CMakeLists txt文件中描述 在ROS中 xff0c CMake被修改为适合于ROS的 c
  • ROS学习(七):三维可视化工具(RViz)

    RViz是ROS的三维可视化工具 它的主要目的是以三维方式显示ROS消息 xff0c 可以将 数据进行可视化表达 例如 xff0c 可以无需编程就能表达激光测距仪 xff08 LRF xff09 传感器中的传感 器到障碍物的距离 xff0c
  • 你想拥有开挂的人生吗?

    此前 xff0c 时任百度首席科学家的吴恩达 xff0c 在接受机器之心视频专访时曾提及 xff0c 我想对所有还在考虑自己的职业生涯的年轻人说一句 xff0c 我知道当你很年轻的时候 xff0c 有时候你无法确定该追求怎样的事业 我认为我
  • 【数据处理】R语言--data.table包使用总结

    data table包使用总结 R中的data table包提供了一个data frame的高级版本 xff0c 让你的程序做数据整型的运算速度大大的增加 data table已经在金融 xff0c 基因工程学等领域大放光彩 他尤其适合那些
  • R语言--异常值检测

    自编函数 boxplot 原理 span class hljs title outlier span span class hljs type IQR span lt function x multiple 61 span class hl
  • Python时间序列案例分析实战--奶牛产奶量预测

    Python时间序列实践之奶牛产奶量预测 CSDN xff1a http blog csdn net kicilove article 数据及完整示例代码下载 xff08 https github com zhaohuicici Time

随机推荐