0%

Docker 19.03以上版本可以使用 docker buildx build命令使用BuildKit构建镜像。该命令支持--platform参数可以同时构建多种系统架构的Docker镜像。

新建builder实例

Docker for Linux不支持构建arm架构镜像,可以运行一个新的容器让其支持该特性,Docker Desktop版本无需进行此项设置。

1
docker run --rm --privileged tonistiigi/binfmt:latest --install all 

由于Docker默认的build实例不支持同时指定多个 --platform,必须首先创建一个新的builder实例。

1
2
3
4
5
6
7
8
9
# 适用于国内环境
$ docker buildx create --use --name=mybuilder-cn --driver docker-container --driver-opt image=dockerpracticesig/buildkit:master

# 适用于腾讯云环境(腾讯云主机、coding.net 持续集成)
$ docker buildx create --use --name=mybuilder-cn --driver docker-container --driver-opt image=dockerpracticesig/buildkit:master-tencent

# $ docker buildx create --name mybuilder --driver docker-container

$ docker buildx use mybuilder

构建镜像

1
2
3
4
5
FROM --platform=$TARGETPLATFORM alpine

RUN uname -a > os.txt

CMD ["cat", "os.txt"]
1
2
3
4
docker buildx build --platform linux/arm,linux/arm64,linux/amd64 -t {镜像名称} . --push

# 查看镜像信息
docker buildx imagetools inspect {镜像名称}

--push 参数表示将构建好的镜像推送到Docker仓库

架构相关变量

TARGETPLATFORM
构建镜像的目标平台,例如 linux/amd64linux/arm/v7windows/amd64

TARGETOS
构建镜像的OS类型,例如 linuxwindows

TARGETARCH
构建镜像的架构类型,例如 amd64arm

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

BUILDOS
构建镜像主机的OS类型,例如 linux

BUILDARCH
构建镜像主机的架构类型,例如 amd64

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

使用举例
例如要构建 linux/arm/v7linux/amd64两种架构的镜像

1
2
3
4
5
6
7
8
9
10
11
FROM docker.io/library/python:3.10-alpine

# 使用变量必须申明
ARG TARGETOS
ARG TARGETARCH

WORKDIR /app

COPY main.py /app

# ENTRYPOINT [/app]

方法1

最简单的一种方法,将当前用户添加到root组。这样做之后该用户将拥有所有root权限,从而使用户在使用sudo时不再需要输入密码。

1
sudo adduser {当前用户名} root 

注意: 将用户添加到root组后,该用户将拥有最高权限,因此在执行系统级操作时要特别小心,以避免可能的系统破坏。

方法2(推荐)

通过编辑/etc/sudoers文件来配置特定用户或组的sudo行为。这种方法允许更灵活的控制哪些命令可以绕过密码提示。
![[Linux配置sudo无密码/IMG-20250319160639846.png]]

单用户配置

1
{用户名} ALL=(ALL:ALL) NOPASSWD:ALL #在 root 后新添加一行

用户组配置

1
%{用户组} ALL=(ALL:ALL) NOPASSWD:ALL # 在 %sudo 后添加

使用此方式需将用户添加进组

1
sudo usermod -a -G {用户组} {用户名}

安装Golang

Windows版本下载安装程序安装即可,Ubuntu下有两种安装方式
第一种方式

1
2
sudo apt update
sudo apt install golang

此种方式安装的版本并不是最新版,或者下载 [官方包]执行

1
2
sudo tar -zxvf go1.24.2.linux-amd64.tar.gz -C  /usr/local # 解压缩到/usr/local
sudo chmod 755 -R /usr/local/go # 修改权限,一般解压后就有权限,此步可以省略

配置环境变量

Windows

打开 系统设置->关于->高级系统设置->环境变量,注意如果只给当前用户使用添加到用户的环境变量即可,全部用户可用需配置系统环境变量。

GOROOT

![[Golang环境配置/IMG-20250319151338172.png]]

GOPATH
![[Golang环境配置/IMG-20250319151450977.png]]

GOPROXY

![[Golang环境配置/IMG-20250319151516015.png]]

Ubuntu

如果只给当前用户使用编辑用户目录下.bashrc文件,全部用户可用需配置/etc/profile

1
2
3
export GOROOT=/usr/local/go # Golang根目录
export GOPATH=/home/go-project # 项目根目录
export PATH=$GOROOT/bin:$GOPATH/bin:$PATH

进阶配置

GOPROXY可在环境变量中配置也可使用go env -w命令配置

1
2
3
go env -w GOPROXY=https://goproxy.cn # 七牛
go env -w GOPROXY=https://mirrors.aliyun.com/goproxy/ # 阿里
go env -w GOPROXY=https://goproxy.io # 官方

GO111MODULE
从go 1.11版本开始,推荐使用Go Modules进行包管理。

1
go env -w GO111MODULE=on

在项目目录下初始化模块

1
go mod init 

准备

镜像选择

quay.io/coreos/etcd:3.2.7
bitname/etcd

创建ETCD数据目录

1
2
3
# data 存储容器持久化数据
# config 存储容器使用的配置文件
mkdir -p /usr/local/docker/etcd/{data,config}

创建ETCD配置文件

配置文件路径为 /usr/local/docker/etcd/config/etcd.config.yml

1
2
3
4
5
6
7
8
9
10
11
12
name: etcd # etcd member 名称,可根据实际情况修改
data-dir: /var/etcd #etcd 数据目录,可根据实际情况修改
listen-client-urls: http://0.0.0.0:2379 #client 流量监听地址,没特殊需求按文档填写即可
advertise-client-urls: http://0.0.0.0:2379 # 该 member 向外部通告的客户端 url 列表,单节点部署时不需要修改,集群部署模式需修改为容器所在节点对外提供服务的 IP
listen-peer-urls: http://0.0.0.0:2380 # peer 流量监听地址,没特殊需求按文档填写即可
initial-advertise-peer-urls: http://0.0.0.0:2380 # 该 member 向同一集群内其他 member 通告的 peer url 列表,单节点部署时不需要修改,集群部署模式需修改为容器所在节点对外提供服务的 IP
initial-cluster: etcd=http://0.0.0.0:2380 # 初始化集群节点信息,单节点部署时不需要修改,集群部署模式需要填写集群中所有 member 的信息
initial-cluster-token: etcd-cluster # 初始化集群时使用的 token,随便写
initial-cluster-state: new # 初始化集群状态,可选的值为 **new** 或者 **existing**,通常采用 **new**
logger: zap
log-level: info
# log-outputs: stderr

创建并启动ETCD服务

1
docker run -d --name etcd -p 2379:2379 -p 2380:2380 -v /usr/local/docker/etcd/data:/var/etcd -v /usr/local/docker/etcd/config:/var/lib/etcd/config quay.io/coreos/etcd: 3.5.12 

使用docker-compose部署

创建Docker-compose

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
version: '3'

services:
etcd:
container_name: etcd
image: quay.io/coreos/etcd:v3.5.12
command: /usr/local/bin/etcd --config-file=/var/lib/etcd/config/etcd.conf.yml
volumes:
- ${DOCKER_VOLUME_DIRECTORY:-.}/data:/var/lib/etcd
- ${DOCKER_VOLUME_DIRECTORY:-.}/config/etcd.config.yml:/var/lib/etcd/conf/etcd.conf.yml
ports:
- 2379:2379
- 2380:2380
restart: always

networks: # 船舰一个新的bridge模式网络,名称 etcd-tier,名称可以根据需求自定义
default:
name: etcd-tier
driver: bridge

基于环境变量配置, 配置参考配置文件方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
version: '3'

services:
etcd:
container_name: etcd
image: quay.io/coreos/etcd:v3.5.12
environment:
- ETCD_NAME=etcd
- ETCD_DATA_DIR=
- ETCD_LISTEN_CLIENT_URLS=
- ETCD_ADVERTISE_CLIENT_URLS=
- ETCD_LISTEN_PEER_URLS=
- ETCD_INSTALL_ADVERTISE_PEER_URLS=
- ETCD_INSTALL_CLUSTER_TOKEN=
- ETCD_INSTALL_CLUSTER
- ETCD_INSTALL_CLUSTER_STATE=new
- ETCD_LOGGER=zap
- ETCD_LOG_LEVEL=info
- ALLOW_NONE_AUTHENTICATION="yes" # 允许无身份验证访问
- TZ="Asia/Shanghai"
volumes:
- ${DOCKER_VOLUME_DIRECTORY:=.}/data:/var/etcd
- /etc/localtime:/etc/localtime:rw
ports:
- 2379:2379
- 2380:2380
restart: always
networks:
default:
name: etcd-tier
driver: bridge

创建并启动etcd

1
2
cd {docker-compose文件所在目录}
docker compose up -d

测试命令

1
2
3
4
5
6
7
8
9
10
11
12
etcdctl --endpoints=192.168.1.2:2379 --write-out=table endpoint health

etcdctl --endpoints=192.168.1.2:2379 --write-out=table endpoint status

# 查看member状态
etcdctl --endpoints=192.168.1.2:2379 --write-out=table member list

# 写入数据
etcdctl --endpoints=192.168.1.2:2379 put foo bar

# 读取数据
etcdctl --endpoints=192.168.1.2:2379 get foo

获取VideoCapture实例

1
2
3
4
5
6
7
8
# 读取视频流
strem_capture = cv2.VideoCapture("rtst://192.168.0.0/live/demo")

# 读取视频文件
file_capture = cv2.VideoCapture('demo.mp4')

# 读取摄像头
capture = cv2.VideoCapture(0)

获取摄像头编号可使用ls -al /dev/ |grep video,输出信息以video开头其后缀为数字即为可能的摄像头编号。

检查获取VideoCapture实例是否成功

1
2
3
# 校验获取VideoCapture类实例
if not capture.isOpened():
return

获取视频流信息

1
2
3
4
5
6
7
8
# 获取视频帧的宽
width = capture.get(cv2.CAP_PROP_FRAME_WIDTH)

# 获取视频帧的高
height = cpature.get(cv2.CAP_PROP_FRAME_HEIGHT)

# 获取视频帧率
fps = capture.get(cv2.CAP_PROP_FPS)

获取帧画面

1
success, frame = capture.read()

当需要同时处理多路摄像头时一般使用grab()retrieve()代替

1
2
3
4
5
6
7
success_1 = capture.grab()
success_2 = stream_capture.grab()

if success_1 and success_2:
frame_1 = capture.retrieve()
frame_2 = stream_capture.retrieve()

设置分辨率

1
2
3
4
5
# 设置摄像头分辨率的宽
capture.set(cv2.CAP_PROP_FRAME_WIDTH, 1920)

# 设置摄像头分辨率的高
capture.set(cv2.CAP_PROP_FRAME_HEIGHT, 1080)

保存视频文件

无论是视频文件存储还是摄像头画面保存都是用VideoWriter类,初始化时需要传入文件名(包含文件格式)、视频编解码器、视频保存帧率 、分辨率,保存视频的帧率最好和读入的帧率一致,分辨率可以更改,只是要求写入的帧大小要与分辨率保持一致。
若指定的文件名已存在则会覆盖文件。

1
2
3
writer = cv2.VideoWriter('output.mp4', 
cv2.VideoWriter_fourcc(*'MP4V'), 30, (1080,1920))
writer.write(frame)

释放资源

不管是VideoCapture还是VideoWriter类,使用完都应该释放资源

1
2
3
4
5
# 释放VideoCapture资源
capture.release()

# 释放VideoWriter资源
writer.release()

完整示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# -*- coding: utf-8 -*-
# /usr/bin/env/python3

import cv2
import time

capture = cv2.VideoCapture('rtsp://192.168.0.0/live/demo)
fourcc = cv2.VideoWriter_fourcc(*'MP$v') # 或H264,H265
fps = capture.get(cv2.CAP_PROP_FPS)
width = capture.get(cv2.CAP_PROP_FRAME_WIDTH)
height = capture.get(cv2.CAP_PROP_FRAME_HEIGHT)

writer = cv2.VideoWriter('demo.mp4', fourcd, fps, (height, width))


while True:
if not capture.isOpened():
time.sleep(0.5)
continue
success, frame = capture.read()
if success:
cv2.imshow('DEMO', frame) # 显示画面
writer.write(frame) # 保存视频文件
if (cv2.waitKey(20) & 0xff) == ord('q'): # 等待20ms并判断是否按下'q'退出,waitkey只能传入整数,
break

capture.release() # 释放VideoCapture
writer.release() # 释放VideoWriter
cv2.destroyAllWindows() # 销毁所有opencv显示窗口

货币格式

1
2
3
4
5
<!-- 默认保留两位小数 输出: $12.34 -->
<TextBlock Text="{Binding Price, StringFormat={}{0:C}}" />

<!-- 保留一位小数 输出: $123.4 -->
<TextBlock Text="{Binding Price, StringFormat={}{0:C1}}" />

固定文字

1
2
3
4
5
6
<!-- 固定前缀 输出: 单价:$12.34 -->
<TextBlock Text="{Binding Price, StringFormat=单价: {0:C}}" />

<!-- 固定后缀 输出: 12.345元 -->
<TextBlock Text="{Binding Price, StringFormat={}{0}元}" />

数字格式化

1
2
3
4
5
6
7
8
9
10
11
<!-- 固定位数,仅支持整形 输出: 086723 -->
<TextBlock Text="{Binding Total, StringFormat={}{0:D6}}" />

<!-- 固定小数点后位数 输出: 8234.9354 -->
<TextBlock Text="{Binding Total, StringFormat={}{0:F4}}" />

<!-- 使用用分割符并指定小数点后位数 输出: 8234.933 -->
<TextBlock Text="{Binding Total, StringFormat={}{0:N3}}" />

<!-- 格式化百分比 输出: 78.9%-->
<TextBlock Text="{Binding Persent, StringFormat={}{0:P1}}" />

占位符

1
2
3
4
<!-- 输出: 0123.46 -->
<TextBox Text="{Binding Price, StringFormat={}{0:0000.00}}" />
<!-- 输出: 123.46 -->
<TextBox Text="{Binding Price, StringFormat={}{0:####.##}}" />

时间日期

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<!-- 输出: 5/4/2015 -->
<TextBox Text="{Binding DateTimeNow, StringFormat={}{0:d}}" />
<!-- 输出: Monday, May 04, 2015 -->
<TextBox Text="{Binding DateTimeNow, StringFormat={}{0:D}}" />
<!-- 输出: Monday, May 04, 2015 5:46 PM -->
<TextBox Text="{Binding DateTimeNow, StringFormat={}{0:f}}" />
<!-- 输出: Monday, May 04, 2015 5:46:56 PM -->
<TextBox Text="{Binding DateTimeNow, StringFormat={}{0:F}}" />
<!-- 输出: 5/4/2015 5:46 PM -->
<TextBox Text="{Binding DateTimeNow, StringFormat={}{0:g}}" />
<!-- 输出: 5/4/2015 5:46:56 PM -->
<TextBox Text="{Binding DateTimeNow, StringFormat={}{0:G}}" />
<!-- 输出: May 04 -->
<TextBox Text="{Binding DateTimeNow, StringFormat={}{0:m}}" />
<!-- 输出: May 04 -->
<TextBox Text="{Binding DateTimeNow, StringFormat={}{0:M}}" />
<!-- 输出: 5:46 PM -->
<TextBox Text="{Binding DateTimeNow, StringFormat={}{0:t}}" />
<!-- 输出: 5:46:56 PM -->
<TextBox Text="{Binding DateTimeNow, StringFormat={}{0:T}}" />
<!-- 输出: 2015年05月04日 -->
<TextBox Text="{Binding DateTimeNow, StringFormat={}{0:yyyy年MM月dd日}}" />
<!-- 输出: 2015-05-04 -->
<TextBox Text="{Binding DateTimeNow, StringFormat={}{0:yyyy-MM-dd}}" />
<!-- 输出: 2015-05-04 17:46 -->
<TextBox Text="{Binding DateTimeNow, StringFormat={}{0:yyyy-MM-dd HH:mm}}" />
<!-- 输出: 2015-05-04 17:46:56 -->
<TextBox Text="{Binding DateTimeNow, StringFormat={}{0:yyyy-MM-dd HH:mm:ss}}" />

多重绑定

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!--
\a &#x07; BEL
\b &#x08; BS - Backspace
\f &#x0c; FF - Formfeed
\n &#x0a; LF, NL - Linefeed, New Line
\r &#x0d; CR - Carriage return
\t &#x09; HT - Tab, Horizontal Tabelator
\v &#x0b; VT - Vertical Tabelator
-->
<TextBlock.Text>
<MultiBinding StringFormat="姓名: {0}&#x09;{1}">
<Binding Path="FirstName" />
<Binding Path="LastName" />
</MultiBinding>
</TextBlock.Text>

消息格式

每个提交消息都由一个标题、一个正文和一个页脚组成。而标题又具有特殊格式,包括修改类型、影响范围和内容主题:

1
2
3
4
5
修改类型(影响范围): 标题
<--空行-->
[正文]
<--空行-->
[页脚]

标题是强制性的,但标题的范围是可选的

修改类型

每个类型值都表示了不同的含义,类型值必须是以下的其中一个:

  • feat:提交新功能
  • fix:修复了bug
  • docs:只修改了文档
  • style:调整代码格式,未修改代码逻辑(比如修改空格、格式化、缺少分号等)
  • refactor:代码重构,既没修复bug也没有添加新功能
  • perf:性能优化,提高性能的代码更改
  • test:添加或修改代码测试
  • chore:对构建流程或辅助工具和依赖库(如文档生成等)的更改
代码回滚

代码回滚比较特殊,如果本次提交是为了恢复到之前的某个提交,那提交消息应该以“revert:”开头,后跟要恢复到的那个提交的标题。然后在消息正文中,应该写上“This reverts commit ”,其中“”是要还原的那个提交的SHA值。

影响范围

范围不是固定值,它可以是你提交代码实际影响到的任何内容。例如$location、$browser、$compile、$rootScope、ngHref、ngClick、ngView等,唯一需要注意的是它必须足够简短。

当修改影响多个范围时,也可以使用“*”。

标题

标题是对变更的简明描述:

  • 使用祈使句,现在时态:是“change”不是“changed”也不是“changes”
  • 不要大写首字母
  • 结尾不要使用句号
正文

正文是对标题的补充,但它不是必须的。和标题一样,它也要求使用祈使句且现在时态,正文应该包含更详细的信息,如代码修改的动机,与修改前的代码对比等。

页脚

任何Breaking Changes(破坏性变更,不向下兼容)都应该在页脚中进行说明,它经常也用来引用本次提交解决的GitHub Issue

Breaking Changes应该以“BREAKING CHANGE:”开头,然后紧跟一个空格或两个换行符,其他要求与前面一致。

智能校园安全物联平台(Intelligent campus security IoT platform)

分支功能描述

master: 长期分支,用于对外版本发布,所有版本出自此版本库。此分支不允许直接提交代码,只从bugfix分支和develop分支合并。

develop: 长期分支,用于日常代码开发,与master分支 保持同步,当新功能开发完成后线合并到此分支,经过测试后再合并到master分支。

bugfix: 临时分支,当出现bug时基于master分支新建bugfix/bug-1,bug分支可根据bug编号命名。bug测试完毕合并进入develop分支和master分支

feature: 临时分支,开发新功能时从develop分支新建feature/feature-1,feature分支可根据功能命名。新特性开发完成合并进入develop分支并删除feature分支。

release: 临时分支,需要发布版本时从master分支新建release/release-1.0.0,release分支根据版本号命名。

release分支禁止再合并功能,只提交bug修改,版本发布完成后合并进入master和develop,并再对应的提交上打版本Tag。

提交规范

参考格式

1
2
3
4
5
<type>: <subject>
<BLANK LINE> 空行
<body>
<BLANK LINE> 空行
<footer>
  • type: 本次commit的类型,如bugfix,docs,style等

  • feat: 添加新特性

  • fix: 修复bug

  • docs: 修改文档

  • style: 修改格式缩进,不改变代码逻辑

  • refactor: 代码重构,没有添加新下功能或者修复bug

  • perf: 增加代码进行性能测试

  • test: 增加测试用例

  • chore: 改变构建流程或者增加依赖库、工具等

  • scope: 本次commit波及范围

  • subject: 简明扼要阐述本次commit的主旨

    • 使用祈使句
    • 首字母不要大写
    • 结尾无需添加标点
  • body: 详细描述本次commit,如需换行则使用|

  • footer: 描述下与之关联的 issue 或 breadk change

标题行: 50个字符以内,描述主要变更内容

主体内容: 更详细下说明文本,建议72个字符以内。需要描述信息包括:

  • 为什么这个变更是必须的,它可能是用来修复一个bug,增加一个feature,提升性能、可靠性、稳定性等
  • 如何解决这个问题,具体描述解决问题的步骤
  • 是否存在副作用、风险

如果需要的话可以添加一个连接到issue或其他文档

示例

1
2
3
4
5
6
7
8
9
10
11
12
docs(README): README添加代码提交规范

添加代码规范,提升提交日志的可读性和功能

#123 #没有关联的issue可以省略

----------------------
feat: 增加XXX功能

增加XXX功能,实现XXX效果

#21