Interactions between Pod priority and quality of service(Qos)

[TOC]

Pod 优先级和服务质量之间的相互作用

集群中为了保障核心服务正常运行,有时候会舍弃掉不那么核心的服务。在通过Kubernetes实现的时候发现有两个手段:

  • 设置pod的服务质量。Guaranteed, Burstable, BestEffort
  • 设置pod的优先级

可是当这两个发生冲突的时候,优先考虑谁呢?比如现在这种情况:

1
在资源不够用的情况下,一个高优先级,但是Qos属于Burstable或BestEffort的pod会抢占,低优先级但Qos属于Guaranteed的pod吗?

挠头不?

进过查找资料,发现官方给出的答案是这样的:

01

02

其实这里是要分情况来看,简单来说是两个环节:pod调度环节节点压力驱逐环节

pod调度环节

这里有最核心的一句话。

1
The scheduler's preemption logic does not consider QoS when choosing preemption targets. 

调度器的抢占逻辑在选择抢占目标时不考虑 QoS。只看pod的优先级,所以无论Guaranteed, Burstable, BestEffort,只要你的优先级最低,并且能腾出空间给新的pod运行,那很可能就是你了。

节点压力驱逐环节

而对于节点压力驱逐环节可能会繁琐一点

1
2
3
As a result, kubelet ranks and evicts pods in the following order:
BestEffort or Burstable pods where the usage exceeds requests. These pods are evicted based on their Priority and then by how much their usage level exceeds the request.
Guaranteed pods and Burstable pods where the usage is less than requests are evicted last, based on their Priority.

第一条:usage exceeds requests

第一条,第一点Qos

1
BestEffort or Burstable pods where the usage exceeds requests.

第一条,第二点Priority

1
These pods are evicted based on their Priority

第一条,第三点usage level

1
and then by how much their usage level exceeds the request.

第二条:usage is less than requests

1
Guaranteed pods and Burstable pods where the usage is less than requests are evicted last, based on their Priority.

第二条,第一点Qos

第二条,第二点Priority

顺序 条件 举例
1 Qos: BestEffort
Priority: 0
内存 request: 0
内存usage:>0
2 Qos: BestEffort
Priority: >0
内存 request: 0
内存usage:>0
Priority低的先驱逐,priority相同的,usage越高的先驱逐
3 Qos: Burstable
Priority: 0
内存usage:>request
4 Qos: Burstable
Priority: >0
内存usage:>request
Priority低的先驱逐,priority相同的,usage越高的先驱逐
5 Qos: Burstable
Priority: 0
内存usage:<request
6 Qos: Burstable
Priority: >0
内存usage:<request
Priority低的先驱逐,priority相同的,usage越高的先驱逐
7 Qos: Guaranteed
Priority: 0
8 Qos: Guaranteed
Priority: 0
Priority低的先驱逐,priority相同的,request越高的先驱逐

所以,你学废了吗?

参考:

https://kubernetes.io/docs/concepts/scheduling-eviction/node-pressure-eviction/#pod-selection-for-kubelet-eviction

https://kubernetes.io/docs/concepts/scheduling-eviction/pod-priority-preemption/#interactions-of-pod-priority-and-qos

kubernetes常见问题

[TOC]

Rancher平台使用问题记录

平台问题 详细说明 备注
rancher与k8s
异常断连

image
image-1
1.在查看大量日志时出现过该问题
2.一些官方组织的交流群中建议优化 k8s系统参数,增加ip port range

image-2
k8s grpc-lb无效 方案有4种:
1、grpc-client + k8s headless
2、envoy (grpc proxy)
3、server mesh (istio/linkerd)
4、
rancher平台部署
节点亲和性
pod亲和性/反亲和性
删除deployment,对应service未删除
headless服务部署
节点选择/在页面打污点
k3s部署 指定reserve预留资源
agent 无法设置reserve?
https://docs.rancher.cn/docs/k3s/installation/install-options/server-config/_index/#agent-%E7%BD%91%E7%BB%9C
https://kubernetes.io/docs/tasks/administer-cluster/reserve-compute-resources/
zoomkeeper更新pod始终有一个没有update rollingUpdatePartition: 0 即可 https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#partitions

Kafka部署extralaccess
longhorn storage 在etcd恢复中是否可用?
Kubernetes在在删除namespace后的恢复问题? 1.通过etcd恢复所有的资源配置
2.配置所有的storageclass创建pv的回收策略为:retain

开发人员使用需求汇总

需求内容 详细说明 时间 备注
pod对cpu/memery资源可见性限制
pod支持绑定宿主机CPU核
api-gateway
nuclio pod 统一对外开放访问
有状态数据库服务更新手动确认 更新要求:
1.先只更新一个pod
2.手动确认第一个pod运行正常之后再更新其他pod
3.pod确认时间几分钟到数小时不等,无法使用readness进行确认
https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#partitions

k3s 在混用docker/containerd之后,重启docker导致的pod hostport模式中iptables路由混乱问题测试验证

hostport添加分析

步骤 添加的chain 添加内容 备注
1 PREROUTING CNI-HOSTPORT-DNAT
2 CNI-HOSTPORT-DNAT CNI-DN-xxxx 每新增一条hostport端口,就增加一条CNI-DN-xxxx chain
3 CNI-DN-xxxx DNAT到对应的pod IP 描述具体的hostport路由到的service

nodeport添加分析

步骤 添加的chain 添加内容 备注
1 PREROUTING KUBE-SERVICES
2 KUBE-SERVICES KUBE-NODEPORTS
3 KUBE-NODEPORTS KUBE-SVC-xxx 每新增一个nodeport端口,就增加一条KUBE-SVC-xxxchain记录
4 KUBE-SVC-xxx 具体路由到对应的service

测试步骤:

1、在集群运行一个nodeport和一个hostport服务,查看iptables路由情况

2、将hostport所有在节点k3s agent的启动容器从docker修改为containerd,重启k3s,查看iptables路由情况

3、重启docker,查看iptables路由情况

4、将k3s agent的启动容器改回docker,查看路由情况

测试结果:

1、在集群运行一个nodeport和一个hostport服务,查看iptables路由情况

pod ip分别为

  • nodeport pod:10.42.1.30
  • hostport pod:10.42.1.31 
  • CNI-HOSTPORT-DNAT 仅有一条记录

image-3

image-4

image-5

2、将hostport所有在节点k3s agent的启动容器从docker修改为containerd,重启k3s,查看iptables路由情况

pod ip分别为

  • nodeport pod:10.42.1.32
  • hostport pod:10.42.1.33
  • CNI-HOSTPORT-DNAT 有两条记录: 10.42.1.31 10.42.1.33 (以前的hostport dnat记录没有删除)

image-6

image-7

image-8

image-9

image-10

3、重启docker,查看iptables路由情况

pod ip分别为

  • nodeport pod:10.42.1.32
  • hostport pod:10.42.1.33
  • CNI-HOSTPORT-DNAT 有两条记录 (以前的hostport dnat记录没有删除)

image-11

image-12

image-13

image-14

4、将k3s agent的启动容器改回docker,查看路由情况

pod ip分别为

  • nodeport pod:10.42.1.34
  • hostport pod:10.42.1.35
  • CNI-HOSTPORT-DNAT 有两条记录 10.42.1.33 10.42.1.35(以前的hostport dnat记录没有删除)

image-15

image-16

image-17

image-18

image-19

test环境中,有重复iptables规则的主机有

image-20

image-21

image-22

image-23

趴趴语法01-课程概述

[TOC]

1.课程概述

语法: Grammar, rules, patterns

1.1 语法的几要素

  • 词法:morphology
  • 句法:syntax
  • 语用学:pragmatics
  • 语义学:sematics

1.2 学习方法

  • 语法规则
  • 借助母语,对比参考,first language
  • 术语
  • development:语言历史,拉丁语/希腊语等。对比中文的文言文
  • variation:地区不同,British English,American English,Canada English,aave

2.名词

noun

名词内容:

  • 物体
  • 地点
  • 抽象概念:快乐,

2.1名词

常见名词后缀, suffix

er: teach –> teacher

or: educate –> educator

ist: art –> artist

ion: educate –> education action

ment: improve –> improvement movement

ness: happy –> happiness loneliness

ce: important –> importance differance

名词分类:

  • 普通名词:common
    • concrete/ abstract
    • countable/ uncountable
  • 专有名词:proper,首字母一定大写,无论位置。
    • 名称,具体的人,地点
    • 月份,星期
    • 节日
countable uncountable
裸奔 I have milk.
a/an/ s I have a cat.
some/any some/ any cats. some/any milk.
一些 a few cats. a little milk.
许多 a lot of cats. a lot of milk.
许多 many cats. much milk.
Common Proper
person Alex
city Paris
book One Day
holiday Christmas

uncountable nouns 度量方式: 量词

example
液体 glass, cup, bottle, drop
一条、一篇 a piece of a piece of news/ paper/furniture/music
a slice of a slice of bread/meat
长条形 a loaf

2.2 可数or不可数

uncoutable

  • 抽象概念
  • 泛指,总称
  • 整体,不能分割: 水
  • 学科名词: math

I’m looking for (a job/ work).

That’s (a good suggestion/ good advice) .

???

We may say we hate Mondays, but research suggests Tuesdays, Wednessdays and Thursdays are equally loathed.

horrible historys.

可数、不可数转换

countable nouns <–> uncountable

  • 一次多义
    • Tish is an empty room. (房间)
    • There is room for discussion. (空间)
    • I have a wonderful experiance in Landon.
    • I have a lot of experience in teaching.
    • I need to write a term paper.
    • I wrote it on a piece of paper.
  • 种类
    • wather: 水,一片水域
    • fish:一种鱼
    • people: 一个种族
  • 抽象名词具体化:
    • histories:具体历史事件
    • success:a huge success. 一次巨大的成功
    • time: have a good time.
  • 专有名词泛化
    • 上面的 Mondays
    • Alexs,所有叫Alex的人们

2.3 可数名词的数

英语中很强调单复数的概念

  • singular: 单数
  • plural: 复数

常见可数名词的复数

  • s, sh, ch, x, es

不规则情况:

  • 辅音+y:baby-> babies, lady -> ladies.

  • -o:

    • +es: Negroes, heroes, potatoes, tomatoes, mangoes (黑人英雄爱吃土豆,番茄和芒果)
    • +s: photos, pianos
    • +s/+es: zeros/zeroes
  • -f/fe:

    • f->v,+es: 见下表
    • +s: roofs, beliefs
    leaf thief wife shelf knife wolf half life
    leaves thieves wives shelves knieves wolves halves lives
    树叶落下时, 小偷 妻子 架子上的 夺去了 半条
  • 彻底不规则

    • child: children
    • ox: oxes
  • Mutant plurals: 变种复数

    • foot: feet
    • tooth: teeth
    • goose: geese
    • mouse: mice
    • man/woman: men,women
    • louse: lice (虱子)

2.4 单数or复数

单复数分不清:

  • Ronny caught three fish this afternoon.
  • People are very worried about this problem.
  • Art is a means of expression.

2.4.1 单复数同行:

  • fish, sheep, deer,
  • Chinese, Janpanese, Swiss, British
  • yuan. vs dollers

2.4.2 集体名词:表示复数的概念

  • People , Police, audience
  • 表示单个:a person/ a police officer/

2.4.3 像复数的的单数名词:

  • means, series, species
  • news
  • 学科名词: maths, politics, physics
  • 名称:国家名、书名
    • The United States is a big country.
    • The Arabian Nights is my favorite book.
  • 时间,举例,钱数
    • Five years is a long time.

2.4.4 二合一物品

shoes, glasses,

双数:眼镜、裤子、鞋子、袜子、剪刀

单数表示: a pair of , 表示一双xx

2.4.5 永远是复数

closthes, goods, savings, earnings

2.4.6 单复数词义不同

arm 手臂 arms 武器
custom 习俗 customs 海关
spirit 精神 spirits 烈酒
line lines 台词
heart 心脏 hearts 红桃(扑克牌)

2.5 名词作定语和所有格

2.5.1 名词作定语

  • 做单数
    • paper money 材料
    • a coffee cup 用途
    • dog food 类别
    • summer holidays 时间
    • kitchen windows 地点
    • a story book 内容
  • 做复数
    • man/woman: a woman writer, two women writers
    • sports meeting
    • goods train / clothes shop
    • customs paper

名词作定语 vs 同根形容词

a gold watch / a golden watch

health codition / healthy children

2.5.2 所有格

所有格:'s, 多用在生命体上

for example:

  • the girl’s bag
  • the firls’ bag
  • Tina and Mary’s room.
  • Tina’s and Mary’s rooms.

名词作定语 vs 所有格

I’m Carol’s ex-husband’s sister’s roomate.

我是卡罗尔前夫妹妹的室友。

dog food / this dog’s food

a student teacher / this student’s teacher

所有格: of

  • 无生命体:the legs of the table
  • 生命体:
    • the photo of Mary
    • Mary’s photo

3.动词 verbs

3.1 行为动词 Action verbs

k3s 安装

[TOC]

k3s 简介

K3s 是一个轻量级的 Kubernetes 发行版,它针对边缘计算、物联网等场景进行了高度优化。由于运行 K3s 所需的资源相对较少,所以 K3s 也适用于开发和测试场景。

部署起来非常简单,所有服务打包为单个二进制文件,同时启动程序自动处理了众多TLS认证的内容,存储也是用轻量级数据库或者内置etcd等。

k3s安装

第一个master

1
2
3
4
5
6
7
8
9
10
11
# first master

curl -sfL http://rancher-mirror.cnrancher.com/k3s/k3s-install.sh | \
K3S_TOKEN='262c73d1ac2e7a8179c861a8a47640f8' \
INSTALL_K3S_EXEC="server --cluster-init" \
INSTALL_K3S_MIRROR=cn \
INSTALL_K3S_VERSION=v1.20.4+k3s1 sh -s -

# check
systemctl status k3s.service
kubectl get nodes

第二个master

只需要在第一个master的基础上,指定第一个master的IP K3S_URL="https://${first_master_ip}:6443" ,同时去掉初始化选项 --cluster-init 即可。如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
# other master
first_master_ip=172.26.186.176

curl -sfL http://rancher-mirror.cnrancher.com/k3s/k3s-install.sh | \
K3S_TOKEN='262c73d1ac2e7a8179c861a8a47640f8' \
K3S_URL="https://${first_master_ip}:6443" \
INSTALL_K3S_EXEC="server" \
INSTALL_K3S_MIRROR=cn \
INSTALL_K3S_VERSION=v1.20.4+k3s1 sh -s -

# check
systemctl status k3s.service
kubectl get nodes

node节点

计算节点再去掉 INSTALL_K3S_EXEC 参数,直接去加入集群即可

1
2
3
4
5
6
7
8
9
10
11
12
# slave
first_master_ip=172.26.186.176

curl -sfL http://rancher-mirror.cnrancher.com/k3s/k3s-install.sh | \
K3S_TOKEN='262c73d1ac2e7a8179c861a8a47640f8' \
K3S_URL="https://${first_master_ip}:6443" \
INSTALL_K3S_MIRROR=cn \
INSTALL_K3S_VERSION=v1.20.4+k3s1 sh -s -

# check
systemctl status k3s-agent.service
kubectl get nodes

注意:

master节点的systemd service 为:k3s.service, 而node节点的systemd service为:k3s-agent.service

容器核心技术

[TOC]

1.容器的重要特征

随着互联网和软件技术的发展,容器技术越来越为大家所熟知和使用,那么容器技术到底好在哪里呢? 主要是他有如下四个重要特征

隔离性:基于Linux kernel提供的 namespace资源隔离方案。

安全性:资源隔离,资源访问自然受到了严格的限制,因此同时也把安全问题解决了

便捷性:相对于虚拟机技术,容器技术启动速度非常快

可配额:基于Linux 的Cgroups 即:Control Groups,可以对一个或一组资源控制和监控

docker优势

  • 资源利用率高
  • 启动快
  • 运行环境一致
    • 便于持续交付和部署
    • 便于迁移
    • 便于维护和扩展

docker劣势

2 Linux namespace

Linux Namespace是Linux提供的一种内核级别环境隔离的方法。提供了对UTS、IPC、mount、PID、network、User等的隔离机制。

linux_namespace

分类 系统调用参数 相关内核版本 隔离内容
Mount namespaces CLONE_NEWNS Linux 2.4.19 挂载点(文件系统)
UTS namespaces CLONE_NEWUTS Linux 2.6.19 主机名与域名,影响uname(hostname, domainname)
IPC namespaces CLONE_NEWIPC Linux 2.6.19 信号量、消息队列和共享内存, inter-process communication,有全局id
PID namespaces CLONE_NEWPID Linux 2.6.24 进程编号
Network namespaces CLONE_NEWNET Linux 2.6.29 网络设备、网络栈、端口等等
User namespaces CLONE_NEWUSER Linux 3.8 用户和用户组

三个系统调用

调用 作用
clone() 实现线程的系统调用,用来创建一个新的进程,并可以通过设计上述参数达到隔离。 创建时传入 flags参数,可选值有 CLONE_NEWIPC, CLONE_NEWNET, CLONE_NEWNS, CLONE_NEWPID, CLONE_NEWUTS, CLONE_NEWUSER, 分别对应上面六种namespace。
unshare() 使某进程脱离某个namespace
setns() 把某进程加入到某个namespace

常用操作

  • 查看当前系统的Namespace

    • lsns -t <type>
  • 查看某进程的Namespace

    • ls -la /proc/<pid>/ns/
  • 进入某Namespace运行命令

    • nsenter -t <pid> -n ip addr
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
32
33
34
35
36
root@dell:~# unshare --help 

Options:
-n, --net[=<file>] unshare network namespace
-f, --fork fork before launching <program>
..
root@dell:~# unshare -fn sleep 60


# another terminal
(base) dell@dell:~$ sudo su -
root@dell:~# ps aux |grep sleep
root 7280 0.0 0.0 16308 756 pts/1 S+ 18:16 0:00 unshare -fn sleep 60
root 7281 0.0 0.0 16320 792 pts/1 S+ 18:16 0:00 sleep 60
root@dell:~# lsns -t net
NS TYPE NPROCS PID USER COMMAND
4026532008 net 595 1 root /sbin/init splash
4026532545 net 2 7280 root unshare -fn sleep 60
4026532636 net 1 2841 rtkit /usr/lib/rtkit/rtkit-daemon

# 新的Namespace里面 network信息
root@dell:~# nsenter -t 7280 -n ip a
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00

# 主机network信息
root@dell:~# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
2: enp0s25: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether f8:b1:56:c0:e2:a5 brd ff:ff:ff:ff:ff:ff
inet 192.168.1.4/24 brd 192.168.1.255 scope global dynamic noprefixroute enp0s25
valid_lft 604134sec preferred_lft 604134sec

docker example

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
root@dell:~# docker run -itd --name nginx nginx:1.19.10 
8db4d89be717eddefb5f0253a89f0b23f27f7c6a96c9dd3feea896ccd5097af3

root@dell:~# docker exec -it nginx bash
容器中查看Namespace
root@8db4d89be717:/# lsns
NS TYPE NPROCS PID USER COMMAND
4026531835 cgroup 3 1 root nginx: master process nginx -g daemon off;
4026531837 user 3 1 root nginx: master process nginx -g daemon off;
4026532546 mnt 3 1 root nginx: master process nginx -g daemon off;
4026532547 uts 3 1 root nginx: master process nginx -g daemon off;
4026532548 ipc 3 1 root nginx: master process nginx -g daemon off;
4026532549 pid 3 1 root nginx: master process nginx -g daemon off;
4026532551 net 3 1 root nginx: master process nginx -g daemon off;
exit

# 主机上查看Namespace
root@dell:/proc/89/ns# lsns
NS TYPE NPROCS PID USER COMMAND
4026531835 cgroup 603 1 root /sbin/init splash
4026531836 pid 602 1 root /sbin/init splash
...
4026532546 mnt 2 13562 root nginx: master process nginx -g daemon off;
4026532547 uts 2 13562 root nginx: master process nginx -g daemon off;
4026532548 ipc 2 13562 root nginx: master process nginx -g daemon off;
4026532549 pid 2 13562 root nginx: master process nginx -g daemon off;
4026532551 net 2 13562 root nginx: master process nginx -g daemon off;

root@dell:/proc/89/ns# cd /proc/13562/ns/
root@dell:/proc/13562/ns# ls -l
total 0
lrwxrwxrwx 1 root root 0 Jan 1 18:36 cgroup -> 'cgroup:[4026531835]'
lrwxrwxrwx 1 root root 0 Jan 1 18:36 ipc -> 'ipc:[4026532548]'
lrwxrwxrwx 1 root root 0 Jan 1 18:36 mnt -> 'mnt:[4026532546]'
lrwxrwxrwx 1 root root 0 Jan 1 18:36 net -> 'net:[4026532551]'
lrwxrwxrwx 1 root root 0 Jan 1 18:36 pid -> 'pid:[4026532549]'
lrwxrwxrwx 1 root root 0 Jan 1 18:37 pid_for_children -> 'pid:[4026532549]'
lrwxrwxrwx 1 root root 0 Jan 1 18:36 user -> 'user:[4026531837]'
lrwxrwxrwx 1 root root 0 Jan 1 18:36 uts -> 'uts:[4026532547]'


root@dell:~# docker inspect nginx|grep -i pid
"Pid": 13562,
"PidMode": "",
"PidsLimit": null,

3 Linux Cgroups

Cgroups最初的目标是为资源管理提供的一个统一的框架,既整合现有的cpuset等子系统,也为未来开发新的子系统提供接口。现在的cgroups适用于多种应用场景,从单个进程的资源控制,到实现操作系统层次的虚拟化(OS Level Virtualization)。Cgroups提供了一下功能:
1.限制进程组可以使用的资源数量(Resource limiting )。比如:memory子系统可以为进程组设定一个memory使用上限,一旦进程组使用的内存达到限额再申请内存,就会出发OOM(out of memory)。
2.进程组的优先级控制(Prioritization )。比如:可以使用cpu子系统为某个进程组分配特定cpu share。
3.记录进程组使用的资源数量(Accounting )。比如:可以使用cpuacct子系统记录某个进程组使用的cpu时间
4.进程组隔离(isolation)。比如:使用ns子系统可以使不同的进程组使用不同的namespace,以达到隔离的目的,不同的进程组有各自的进程、网络、文件系统挂载空间。
5.进程组控制(control)。比如:使用freezer子系统可以将进程组挂起和恢复。

通过mount -t cgroup 命令或进入 /sys/fs/cgroup 目录,我们看到目录中有若干个子目录,我们可以认为这些都是受 cgroups 控制的资源以及这些资源的信息。

  • blkio: — 这个子系统为块设备设定输入/输出限制,比如物理设备(磁盘,固态硬盘,USB 等等)。
  • cpu — 这个子系统使用调度程序提供对 CPU 的 cgroup 任务访问。
  • ​cpuacct — 这个子系统自动生成 cgroup 中任务所使用的 CPU 报告。
  • cpuset — 这个子系统为 cgroup 中的任务分配独立 CPU(在多核系统)和内存节点。
  • devices — 这个子系统可允许或者拒绝 cgroup 中的任务访问设备。
  • freezer — 这个子系统挂起或者恢复 cgroup 中的任务。
  • ​memory — 这个子系统设定 cgroup 中任务使用的内存限制,并自动生成内存资源使用报告。
  • ​net_cls — 这个子系统使用等级识别符(classid)标记网络数据包,可允许 Linux 流量控制程序(tc)识别从具体 cgroup 中生成的数据包。
  • net_prio — 这个子系统用来设计网络流量的优先级
  • hugetlb — 这个子系统主要针对于HugeTLB系统进行限制,这是一个大页文件系统。

cpu example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
root@u18-01:~# while [ True ];do
> x=$x+1
> done;

# 查看cpu使用情况
root@u18-01:~# top
...
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
5028 root 20 0 32308 15996 3504 R 100.0 0.1 1:20.16 bash

# 添加CPU资源限制
root@u18-01:~# echo 50000 > /sys/fs/cgroup/cpu/mydemo/cpu.cfs_quota_us
root@u18-01:~# echo 5028 > /sys/fs/cgroup/cpu/mydemo/tasks
root@u18-01:~# top
...
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
5028 root 20 0 32472 16148 3504 R 49.7 0.1 1:41.24 bash

memery example

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
root@u18:~# cat malloc.c 
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#define BLOCK_SIZE (100*1024*1024)
char* allocMemory() {
char* out = (char*)malloc(BLOCK_SIZE);
memset(out, 'A', BLOCK_SIZE);
return out;



root@u18:~# cat main.go
package main

//#cgo LDFLAGS:
//char* allocMemory();
import "C"
import (
"fmt"
"time"
)

func main() {
// only loop 10 times to avoid exhausting the host memory
holder := []*C.char{}
for i := 1; i <= 20; i++ {
fmt.Printf("Allocating %dMb memory, raw memory is %d\n", i*100, i*100*1024*1025)
// hold the memory, otherwise it will be freed by GC
holder = append(holder, (*C.char)(C.allocMemory()))
time.Sleep(time.Second*5)
}
time.Sleep(time.Hour)
}



root@u18:~# CGO_ENABLED=1 GOOS=linux CGO_LDFLAGS="-static" go build
root@u18:~# ./malloc
Allocating 100Mb memory, raw memory is 104960000
Allocating 200Mb memory, raw memory is 209920000
Allocating 300Mb memory, raw memory is 314880000
Allocating 400Mb memory, raw memory is 419840000
Allocating 500Mb memory, raw memory is 524800000
Allocating 600Mb memory, raw memory is 629760000
Allocating 700Mb memory, raw memory is 734720000
Allocating 800Mb memory, raw memory is 839680000
Allocating 900Mb memory, raw memory is 944640000
Allocating 1000Mb memory, raw memory is 1049600000
Allocating 1100Mb memory, raw memory is 1154560000
Allocating 1200Mb memory, raw memory is 1259520000
Killed


# another terminal
root@u18:~# ps aux |grep malloc
root 6871 1.4 1.2 1175156 104648 pts/0 Sl+ 14:29 0:00 ./malloc
root 6878 0.0 0.0 13144 1124 pts/1 S+ 14:29 0:00 grep --color=auto malloc
root@u18:~# echo 6871 > tasks
root@u18:~# echo 1049600000 > memory.limit_in_bytes


4 Union FS

联合文件系统(unite serveral directories into a single virtual filesystem): 将不同目录挂载到同一个虚拟文件系统下。

容器文件系统

container_system

Linux vs Docker 启动

Linux

  • 在启动后,首先将rootfs 设置为readonly, 进行一系列检查, 然后将其切换为“readwrite”供用户使用。

Docker 启动

  • 初始化时也是将rootfs 以readonly 方式加载并检查,然而接下来利用union mount 的方式将一个readwrite文件系统挂载在readonly 的rootfs 之上;

  • 并且允许再次将下层的FS(file system) 设定为readonly 并且向上叠加。

  • 这样一组readonly 和一个writeable 的结构构成一个container 的运行时态, 每一个FS 被称作一个FS层。

Docker 写操作

由于镜像具有共享特性,所以对容器可写层的操作需要依赖存储驱动提供的写时复制和用时分配机制,以此来
支持对容器可写层的修改,进而提高对存储和内存资源的利用率。

  • 写时复制

    • 写时复制,即Copy-on-Write。
    • 一个镜像可以被多个容器使用,但是不需要在内存和磁盘上做多个拷贝。
    • 在需要对镜像提供的文件进行修改时,该文件会从镜像的文件系统被复制到容器的可写层的文件系统进行修改,而镜像里面的文件不会改变。
    • 不同容器对文件的修改都相互独立、互不影响。
  • 用时分配

    按需分配空间,而非提前分配,即当一个文件被创建出来后,才会分配空间。

容器存储驱动

container_storage_driver

OverlayFS

OverlayFS 也是一种与AUFS 类似的联合文件系统,同样属于文件级的存储驱动,包含了最初的Overlay 和
更新更稳定的overlay2。
Overlay 只有两层:upper 层和lower 层,Lower 层代表镜像层,upper 层代表容器可写层。

overlayfs

example

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# 创建upper and lower数据
root@u18:~/test# mkdir upper lower merged
root@u18:~/test# echo "from lower" > lower/in_lower.txt
root@u18:~/test# echo "from upper" > upper/in_upper.txt
root@u18:~/test# echo "from lower" > lower/in_both.txt
root@u18:~/test# echo "from upper" > upper/in_both.txt

root@u18:~/test# tree
.
├── lower
│   ├── in_both.txt
│   └── in_lower.txt
├── merged
├── upper
│   ├── in_both.txt
│   └── in_upper.txt

4 directories, 4 files


# overlay挂载
root@u18:~/test# mount -t overlay overlay -o lowerdir=`pwd`/lower,upperdir=`pwd`/upper `pwd`/merged
root@u18:~/test# tree
.
├── lower
│   ├── in_both.txt
│   └── in_lower.txt
├── merged
│   ├── in_both.txt
│   ├── in_lower.txt
│   └── in_upper.txt
├── upper
│   ├── in_both.txt
│   └── in_upper.txt

5 directories, 7 files


# 查看结果
root@u18:~/test# cat merged/in_both.txt
from upper
root@u18:~/test# cat merged/in_lower.txt
from lower
root@u18:~/test# cat merged/in_upper.txt
from upper


OCI容器标准

OCI组织(Open container initiative): 一个致力于定义容器镜像标准和运行时标准的开放式组织。定义了如下标准:

  • Image Specification: 镜像规范,定义应用如何打包
  • Runtime specification: 容器运行规范,定义如何解压并运行应用
  • Distribution Specification: 容器分发规范

Low-Level和High-Level容器运行时

  • Low-Level容器运行时:容器是通过Linux nanespace和Cgroups实现的,Namespace能让你为每个容器提供虚拟化系统资源,像是文件系统和网络,Cgroups提供了限制每个容器所能使用的资源的如内存和CPU使用量的方法。在最低级别的运行时中,容器运行时负责为容器建立namespaces和cgroups,然后在其中运行命令,Low-Level容器运行时支持在容器中使用这些操作系统特性。目前来看低级容器运行时有:runc :我们最熟悉也是被广泛使用的容器运行时,代表实现Docker。runv:runV 是一个基于虚拟机管理程序(OCI)的运行时。它通过虚拟化 guest kernel,将容器和主机隔离开来,使得其边界更加清晰,这种方式很容易就能帮助加强主机和容器的安全性。代表实现是kata和Firecracker。runsc:runsc = runc + safety ,典型实现就是谷歌的gvisor,通过拦截应用程序的所有系统调用,提供安全隔离的轻量级容器运行时沙箱。截止目前,貌似并不没有生产环境使用案例。wasm : Wasm的沙箱机制带来的隔离性和安全性,都比Docker做的更好。但是wasm 容器处于草案阶段,距离生产环境尚有很长的一段路。
  • High-Level容器运行时:通常情况下,开发人员想要运行一个容器不仅仅需要Low-Level容器运行时提供的这些特性,同时也需要与镜像格式、镜像管理和共享镜像相关的API接口和特性,而这些特性一般由High-Level容器运行时提供。就日常使用来说,Low-Level容器运行时提供的这些特性可能满足不了日常所需,因为这个缘故,唯一会使用Low-Level容器运行时的人是那些实现High-Level容器运行时以及容器工具的开发人员。那些实现Low-Level容器运行时的开发者会说High-Level容器运行时比如containerd和cri-o不像真正的容器运行时,因为从他们的角度来看,他们将容器运行的实现外包给了runc。但是从用户的角度来看,它们只是提供容器功能的单个组件,可以被另一个的实现替换,因此从这个角度将其称为runtime仍然是有意义的。即使containerd和cri-o都使用runc,但是它们是截然不同的项目,支持的特性也是非常不同的。dockershim, containerd 和cri-o都是遵循CRI的容器运行时,我们称他们为高层级运行时(High-level Runtime)。

docker引擎架构

docker_arch

containerd 是一个工业级标准的容器运行时,它强调简单性、健壮性和可移植性,containerd 可以负责干下面这些事情:

  • 管理容器的生命周期(从创建容器到销毁容器)
  • 拉取/推送容器镜像
  • 存储管理(管理镜像及容器数据的存储)
  • 调用 runc 运行容器(与 runc 等容器运行时交互)
  • 管理容器网络接口及网络

显然_runc_是只是一个命令行工具,_containerd_是一个长期居住守护进程。

docker-containerd-shim的作用:

  • 允许runc在创建&运行容器之后退出
  • 用shim作为容器的父进程,而不是直接用containerd作为容器的父进程,是为了防止这种情况:当containerd挂掉的时候,shim还在,因此可以保证容器打开的文件描述符不会被关掉
  • 依靠shim来收集&报告容器的退出状态,这样就不需要containerd来wait子进程

因此,使用shim的主要作用,就是将containerd和真实的容器(里的进程)解耦,这是第二点和第三点所描述的。而第一点,为什么要允许runc退出呢? 因为,Go编译出来的二进制文件,默认是静态链接,因此,如果一个机器上起N个容器,那么就会占用M*N的内存,其中M是一个runc所消耗的内存。 但是出于上面描述的原因又不想直接让containerd来做容器的父进程,因此,就需要一个比runc占内存更小的东西来作父进程,也就是shim。

参考:

Pritunl安装使用

[TOC]

Pritunl 安装

Pritunl 是一款VPN server,可以方便的搭建和管理,下面介绍一下如何在ubuntu中安装pritunl。

mongodb安装

Pritunl 默认使用mongodb保存数据。安装过程就不详细介绍了,直接上代码

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
# mongodb 4
curl -fsSL https://www.mongodb.org/static/pgp/server-4.4.asc | sudo apt-key add -

echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu bionic/mongodb-org/4.4 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-4.4.list

sudo apt install mongodb-org

sudo systemctl start mongod.service

sudo systemctl status mongod



# mongodb 5
sudo tee /etc/apt/sources.list.d/mongodb-org-5.0.list << EOF
deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu bionic/mongodb-org/5.0 multiverse
EOF

wget -qO - https://www.mongodb.org/static/pgp/server-5.0.asc | sudo apt-key add -

sudo apt update

# WireGuard server support
sudo apt -y install wireguard wireguard-tools

sudo ufw disable

sudo apt -y install mongodb-org
sudo systemctl enable mongod
sudo systemctl start mongod

pritunl

apt安装过程

1
2
3
4
5
6
7
8
9
10
sudo tee /etc/apt/sources.list.d/pritunl.list <<EOF
deb https://repo.pritunl.com/stable/apt bionic main
EOF

sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com --recv 7568D9BB55FF9E5287D586017AE645C0CF8E292A
curl https://raw.githubusercontent.com/pritunl/pgp/master/pritunl_repo_pub.asc | sudo apt-key add -
sudo apt-get update

apt install pritunl
systemctl start pritunl

安装完成之后配置文件在 /etc/pritunl.conf ,使用默认的就行。这里可以修改mongodb的地址、账号密码,以及修改pritunl暴露的port。

1
2
3
4
5
6
7
8
9
10
11
12
root@VM-0-7-ubuntu:/etc# cat pritunl.conf 
{
"static_cache": true,
"ssl": false,
"port": 60080,
"temp_path": "/tmp/pritunl_%r",
"log_path": "/var/log/pritunl.log",
"www_path": "/usr/share/pritunl/www",
"bind_addr": "0.0.0.0",
"mongodb_uri": "mongodb://localhost:27017/pritunl?authSource=admin",
"local_address_interface": "auto"
}

pritunl必须通过https访问,默认会自动生成https端口和地址,web端会提示你地址不安全

WX20211129-175915@2x

这时候只需要在浏览器输入 thisisunsafe 就会自动进入登录页面。这是chrome的一个后门 ^_^

第一次登录需要输入setup-key。只需要在命令行输入pritunl setup-key 即可拿到key。

WeEG06lTLOTXvcLeKBYU_database_setup0

默认密码可执行命令:pritunl default-password 查看

WX20211129-181123@2x

pritunl配置

pritunl配置分类大概流程如下

  • 1.创建server,确定VPN的IP地址范围

  • 2.创建Organization,添加到server中。一个server可以有多个Organization

  • 3.创建user,user是属于Organization

这样就创建了一套服务,同一个server内的user可以互相访问。不同的server可以用来创建多套VPN server。

下载user的配置文件,在client运行即可

WX20211129-182217@2x

WX20211129-182334@2x

Windows10 Cordova环境搭建打包Android安装包

[TOC]

之前写了Vue项目打包成Android和iOS安装包,不过那是基于Linux的环境。最近疫情紧张,都在家里远程办公,,于是整理了Windows10下Cordova环境搭建以及打包Android安装包。

Cordova环境依赖:

  • win10系统
  • Node环境
  • Java环境
  • AndroidStudio
  • Ant
  • Gradle

安装node环境:

1.使用node官网网址下载node包,最好使用稳定版本。https://nodejs.org/

js_download_page

2.一路安装next,然后在CMD中使用命令查看node版本

1
2
node -v
npm -v

js_npm_version

Java环境配置:

jdk8下载地址:https://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html

Windows 10 就下载 jdk-8u241-windows-x64.exe这个文件。

记住安装地址,我是安装在D盘下

jdk_install_path

第一步:在桌面上依次 右键单击计算机—属性—高级系统设置—环境变量。

第二步:新建一个名为JAVA_HOME的系统变量,第二栏的值即为你自己jdk的安装路径,这里的是我的,自己的依照自己的情况来。

java_home

第三步:新建一个名为CLASSPATH的系统变量,在第二栏一字不差地输入 %JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib\tools.jar。看下一个图。

第四步:找到一个叫PATH的系统变量,双击其编辑,在最后加上下图中圈出的语句,然后确定。

java_path

第五步:使用CMD检查,使用javac命令,配置成功则出现以下

javav

安装安卓SDK:

第一步:推荐安装AndroidStudio,会自动配置SDK以及SDK-tools等相关的,可以在编辑器里选择要下载使用的SDK版本,安装好后,仿照配置Java环境的方式配置ANDROID_HOME。

安装AS:

android-studio-install

安装SDK:

sdk-install

SDK Tools:

sdk-tools

如果你无法访问Google,那么恭喜你,估计你很难完整的下载到SDK。下面告诉你如何解决:

修改DNS,进入网站http://ping.chinaz.com/,进行 dl.google.com ping检查,选择大陆响应时间最短的IP地址,用这个IP添加到host里面,hosts文件地址:C:\WINDOWS\System32\drivers\etc\hosts

ping-dl-google-com

host-dl-google-com

1
203.208.41.41 dl.google.com

第二步:配置其他安卓环境变量,也是在Path中进行配置

1
2
3
4
ANDROID_HOME C:\Users\alex\AppData\Local\Android\Sdk
%ANDROID_HOME%\build-tools\29.0.3
%ANDROID_HOME%\platform-tools
%ANDROID_HOME%\tools

android-home

注意:在配置build-tools的时候,版本号不要搞错了。随着你安装时间的不同,这个版本号会不一样。

sdk-build-version

android-path

第三步:终端中输入adb,出现下图表示成功

adb-version

安装Ant

第一步:https://ant.apache.org/bindownload.cgi 进行安装,选择1.10.*版本的,该版本对应的JDK8。

ant

解压目录:

install-path

第二步:配置Ant环境变量

1
2
ANT_HOME D:\Program Files\Android\apache-ant-1.10.7
path %ANT_HOME%/bin

第三步:检测安装是否成功

1
ant -version

ant-version

安装Gradle

第一步:下载并解压 https://services.gradle.org/distributions/ 下载记住自己解压的地址。
特别注意要下载.bin.zip的版本

第二步:配置Gradle环境变量

1
2
GRADLE_HOME D:\Program Files\Android\gradle-6.1.1
path %GRADLE_HOME%/bin

第三步:检测安装是否成功

1
gradle -version

gradle-version

到此为止,在Windows10下所有的cordava环境依赖全部安装完毕了,来看看所有的变量都添加了之后的样子:

sys-env-path

由于默认情况下执行 gradle 各种命令是去国外的 gradle 官方镜像源获取需要安装的具体软件信息,所以在不使用代理的情况下,从国内访问国外服务器的速度相对比较慢。阿里旗下维护着一个国内 maven 镜像源,同样适用于 gradle。

找到 (用户家目录)/.gradle/init.gradle 文件,如果找不到 init.gradle 文件,自己新建一个

修改/添加 init.gradle 文件内的 repositories 配置

1
2
3
4
5
6
7
allprojects {
repositories {
maven {
url "http://maven.aliyun.com/nexus/content/groups/public"
}
}
}

安装cordova

全局安装

1
npm install -g cordova

cordova添加Android

1
cordova platform add android

android-platform-add

cordova运行Android调试

1
cordova run android --device

android-platform-run-01

android-platform-run-02

浏览器调试工具:

1
chrome://inspect/#**devices**

chrome-inspect

cordova打包APP

1
cordova build android --release

android-platform-build-01

android-platform-build-02

参考: https://blog.csdn.net/weixin_43765499/article/details/89142608

Vue项目打包成Android和iOS安装包

[TOC]

准备环境配置

准备环境需要安装Android Sdk , jdk , Gradle

安装Android Sdk

Android Sdk 可以通过安装 android-studio, 然后再编辑器里面选择安装SDK即可。

也可以通过sdk-tools来安装,这种方式无需界面,对于远程Linux的操作非常方便。下面介绍这种方式:

下载sdk-tolls: https://dl.google.com/android/repository/sdk-tools-linux-4333796.zip

解压到指定目录

1
2
cd /home/alex/android-sdk
unzip sdk-tools-linux-4333796.zip

编辑 ~/.profile 配置环境变量

1
2
export ANDROID_HOME=/home/alex/android-sdk
export PATH=$ANDROID_HOME/platform-tools:$ANDROID_HOME/tools:$ANDROID_HOME/tools/bin:$PATH

sdkmanager –list即可查看所有已经安装的和未安装的packages,如下:

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
Installed packages:
Path | Version | Description | Location
------- | ------- | ------- | -------
build-tools;24.0.3 | 24.0.3 | Android SDK Build-Tools 24.0.3 | build-tools/24.0.3/
build-tools;25.0.3 | 25.0.3 | Android SDK Build-Tools 25.0.3 | build-tools/25.0.3/
emulator | 26.1.3 | Android Emulator | emulator/
extras;android;m2repository | 47.0.0 | Android Support Repository | extras/android/m2repository/
extras;google;m2repository | 57 | Google Repository | extras/google/m2repository/
extras;m2reposi...ut-solver;1.0.2 | 1 | Solver for ConstraintLayout 1.0.2 | extras/m2reposi...t-solver/1.0.2/
extras;m2reposi...nt-layout;1.0.2 | 1 | ConstraintLayout for Android 1... | extras/m2reposi...t-layout/1.0.2/
patcher;v4 | 1 | SDK Patch Applier v4 | patcher/v4/
platform-tools | 26.0.0 | Android SDK Platform-Tools 26 | platform-tools/
platforms;android-24 | 2 | Android SDK Platform 24 | platforms/android-24/
platforms;android-25 | 3 | Android SDK Platform 25 | platforms/android-25/
tools | 26.0.2 | Android SDK Tools | tools/

Available Packages:
Path | Version | Description
------- | ------- | -------
add-ons;addon-g..._apis-google-15 | 3 | Google APIs
add-ons;addon-g..._apis-google-16 | 4 | Google APIs
add-ons;addon-g..._apis-google-17 | 4 | Google APIs
add-ons;addon-g..._apis-google-18 | 4 | Google APIs
add-ons;addon-g..._apis-google-19 | 20 | Google APIs
add-ons;addon-g..._apis-google-21 | 1 | Google APIs
add-ons;addon-g..._apis-google-22 | 1 | Google APIs
add-ons;addon-g..._apis-google-23 | 1 | Google APIs
add-ons;addon-g..._apis-google-24 | 1 | Google APIs
add-ons;addon-g...e_gdk-google-19 | 11 | Glass Development Kit Preview
build-tools;19.1.0 | 19.1.0 | Android SDK Build-Tools 19.1
build-tools;20.0.0 | 20.0.0 | Android SDK Build-Tools 20
build-tools;21.1.2 | 21.1.2 | Android SDK Build-Tools 21.1.2
build-tools;22.0.1 | 22.0.1 | Android SDK Build-Tools 22.0.1
build-tools;23.0.1 | 23.0.1 | Android SDK Build-Tools 23.0.1
build-tools;23.0.2 | 23.0.2 | Android SDK Build-Tools 23.0.2
build-tools;23.0.3 | 23.0.3 | Android SDK Build-Tools 23.0.3
build-tools;24.0.0 | 24.0.0 | Android SDK Build-Tools 24
build-tools;24.0.1 | 24.0.1 | Android SDK Build-Tools 24.0.1
build-tools;24.0.2 | 24.0.2 | Android SDK Build-Tools 24.0.2
build-tools;24.0.3 | 24.0.3 | Android SDK Build-Tools 24.0.3
build-tools;25.0.0 | 25.0.0 | Android SDK Build-Tools 25
build-tools;25.0.1 | 25.0.1 | Android SDK Build-Tools 25.0.1
build-tools;25.0.2 | 25.0.2 | Android SDK Build-Tools 25.0.2
build-tools;25.0.3 | 25.0.3 | Android SDK Build-Tools 25.0.3
build-tools;26.0.0 | 26.0.0 | Android SDK Build-Tools 26
build-tools;26.0.1 | 26.0.1 | Android SDK Build-Tools 26.0.1
cmake;3.6.4111459 | 3.6.4111459 | CMake 3.6.4111459
docs | 1 | Documentation for Android SDK
emulator | 26.1.3 | Android Emulator
extras;android;gapid;1 | 1.0.3 | GPU Debugging tools
extras;android;gapid;3 | 3.1.0 | GPU Debugging tools
extras;android;m2repository | 47.0.0 | Android Support Repository
extras;google;auto | 1.1 | Android Auto Desktop Head Unit...
extras;google;g...e_play_services | 43 | Google Play services
  • 下载地址 http://www.android-studio.org/ 解压到/usr/local/目录下
  • 运行 到android-studio文件夹下的bin ./studio.sh
  • 配置Android SDK环境变量,vim ~/.profile中加入下面代码
1
2
3
export ANDROID_HOME=$HOME/Android/Sdk (不存在请单独安装Android Sdk)
export PATH=${PATH}:${ANDROID_HOME}/tools
export PATH=${PATH}:${ANDROID_HOME}/platform-tools

sdkmanager –install

我们可以通过sdkmanager –install命令来进行packages组件的安装
sdkmanager –install “platforms;android-26”
sdkmanager –install “build-tools;25.0.3”
sdkmanager –install “extras;google;m2repository”
sdkmanager –install “extras;android;m2repository”

安装完成后,Android sdk目录结构如下所示:

1
2
3
4
5
6
7
8
9
total 32
drwxr-xr-x 4 jenkins root 4096 Aug 15 17:30 build-tools
drwxr-xr-x 7 jenkins root 4096 Aug 14 15:52 emulator
drwxr-xr-x 5 jenkins root 4096 Aug 15 17:31 extras
drwxr-xr-x 2 jenkins root 4096 Aug 14 15:52 licenses
drwxr-xr-x 3 jenkins root 4096 Aug 14 15:52 patcher
drwxr-xr-x 4 jenkins root 4096 Aug 15 17:30 platforms
drwxr-xr-x 5 jenkins root 4096 Aug 14 15:08 platform-tools
drwxr-xr-x 6 jenkins root 4096 Aug 14 15:52 tools

sdkmanager –help

我们可以通过sdkmanager –help命令来查看其帮助

安装 java jdk

  • 下载地址 https://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html
  • 解压到/usr/local/目录下
  • 配置Java jdk的环境变量 vim /etc/profile
1
2
3
export JAVA_HOME=/usr/local/jdk1.8.0_231
export CLASSPATH=.:$JAVA_HOME/jre/lib/rt.jar:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
export PATH=$JAVA_HOME/bin:$PATH
  • 激活 source /etc/profile
  • 检查是否安装成功 终端输入java和javac

安装Gradle

  • 必须满足要求 Java JDK或JRE版本8或更高版本
  • 下载地址 https://services.gradle.org/distributions/ gradle-x.x-bin.zip
  • 创建目录并解压 mkdir /opt/gradle
  • 配置环境变量 vim /etc/profile
1
export PATH=$PATH:/opt/gradle/gradle-x.xx/bin
  • 激活 source /etc/profile

安装cordova与vue3.0

  • cordova sudo npm install -g cordova
  • vue3.0 sudo npm install -g @vue/cli@3.0 检查 vue -V

APP打包设置

前端代码打包静态文件

  1. 白屏,在前端工程文件vue.config.js 添加
1
2
module.exports = {
publicPath: './',
  1. 在前端工程文件 /Projects/src/router.js 修改 mode: "hash"

  2. 调用不了后端接口,前端工程文件中

Projects/src/main.js 中加

1
axios.defaults.baseURL = 'https://backend.server.com'
  1. 生成静态文件 dist 目录

    1
    npm run bulid

创建cordova项目,并打包

  • cordova create app_name com.dls package_name (app_name为目录名称, com.dls.package_name包名)
  • 将生成的静态文件dist,移动到cordova项目的app_name目录下,改名为 www
  • 在soober目录下进行打包 cordova platforms add android --save

检查环境配置

  • 到项目根目录下 cordova requirements 确保所需环境都对

配置 icon 和 启动页

在config.xml 文件中添加如下配置,并将res文件夹放置在Cordova项目(Soober)根目录下

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
<?xml version='1.0' encoding='utf-8'?>
<widget id="com.dls.assistant" version="1.3.0" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
<name>soober</name>
<description>
A sample Apache Cordova application that responds to the deviceready event.
</description>
<author email="dev@cordova.apache.org" href="http://cordova.io">
Apache Cordova Team
</author>
<content src="index.html" />
<plugin name="cordova-plugin-whitelist" spec="1" />
<access origin="*" />
<allow-navigation href="*" />
<allow-intent href="http://*/*" />
<allow-intent href="https://*/*" />
<allow-intent href="tel:*" />
<allow-intent href="sms:*" />
<allow-intent href="mailto:*" />
<allow-intent href="geo:*" />
<platform name="android">
<allow-intent href="market:*" />
<icon src="res/icon/android/ldpi.png" density="ldpi" />
<icon src="res/icon/android/mdpi.png" density="mdpi" />
<icon src="res/icon/android/hdpi.png" density="hdpi" />
<icon src="res/icon/android/xhdpi.png" density="xhdpi" />
<icon src="res/icon/android/xxhdpi.png" density="xxhdpi" />
<icon src="res/icon/android/xxxhdpi.png" density="xxxhdpi" />

<splash density="hdpi" src="res/screen/android/splash-land-hdpi.png" />
<splash density="ldpi" src="res/screen/android/splash-land-ldpi.png" />
<splash density="mdpi" src="res/screen/android/splash-land-mdpi.png" />
<splash density="xhdpi" src="res/screen/android/splash-land-xhdpi.png" />
<splash density="land-hdpi" src="res/screen/android/splash-land-hdpi.png" />
<splash density="land-ldpi" src="res/screen/android/splash-land-ldpi.png" />
<splash density="land-mdpi" src="res/screen/android/splash-land-mdpi.png" />
<splash density="land-xhdpi" src="res/screen/android/splash-land-xhdpi.png" />
<splash density="port-hdpi" src="res/screen/android/splash-port-hdpi.png" />
<splash density="port-ldpi" src="res/screen/android/splash-port-ldpi.png" />
<splash density="port-mdpi" src="res/screen/android/splash-port-mdpi.png" />
<splash density="port-xhdpi" src="res/screen/android/splash-port-xhdpi.png" />

<!-- <preference name="android-manifest/application/activity/@android:theme" value="@style/WelcomeStyle" /> -->

<!-- <preference name="StatusBarOverlaysWebView" value="true" /> -->
<!-- <preference name="StatusBarStyle" value="lightContent" /> -->

<!-- 自动隐藏启动页面AutoHideSplashScreen(默认为: true) -->
<!-- <preference name="AutoHideSplashScreen" value="false" /> -->

<!-- 显示启动页面的时间长度SplashScreenDelay(默认为: 3000) -->
<preference name="SplashScreenDelay" value="1500" />

<!-- 淡入淡出效果FadeSplashScreen(默认为: true) -->
<!-- <preference name="FadeSplashScreen" value="false"/> -->

<!-- 淡入淡出效果的执行时间长度FadeSplashScreenDuration(默认为: 500) -->
<preference name="FadeSplashScreenDuration" value="500"/>

<!-- 启动页面是否允许旋转(默认为: true) -->
<preference name="ShowSplashScreenSpinner" value="false"/>


<!-- 以图片原始比例显示图片,否则拉伸图片来填充 -->
<preference name="SplashMaintainAspectRatio" value="true" />

<!-- 设置为false时,在下次打开APP时,还会显示启动页面。 -->
<!-- <preference name="SplashShowOnlyFirstTime" value="false" /> -->


</platform>
<platform name="ios">
<allow-intent href="itms:*" />
<allow-intent href="itms-apps:*" />
<!-- iOS 8.0+ -->
<!-- iPhone 6 Plus -->
<icon src="res/icon/ios/icon-60@3x.png" width="180" height="180" />
<!-- iOS 7.0+ -->
<!-- iPhone / iPod Touch -->
<icon src="res/icon/ios/icon-60.png" width="60" height="60" />
<icon src="res/icon/ios/icon-60@2x.png" width="120" height="120" />
<!-- iPad -->
<icon src="res/icon/ios/icon-76.png" width="76" height="76" />
<icon src="res/icon/ios/icon-76@2x.png" width="152" height="152" />
<!-- Spotlight Icon -->
<icon src="res/icon/ios/icon-40.png" width="40" height="40" />
<icon src="res/icon/ios/icon-40@2x.png" width="80" height="80" />
<!-- iOS 6.1 -->
<!-- iPhone / iPod Touch -->
<icon src="res/icon/ios/icon.png" width="57" height="57" />
<icon src="res/icon/ios/icon@2x.png" width="114" height="114" />
<!-- iPad -->
<icon src="res/icon/ios/icon-72.png" width="72" height="72" />
<icon src="res/icon/ios/icon-72@2x.png" width="144" height="144" />
<!-- iPad Pro -->
<icon src="res/icon/ios/icon-167.png" width="167" height="167" />
<!-- iPhone Spotlight and Settings Icon -->
<icon src="res/icon/ios/icon-small.png" width="29" height="29" />
<icon src="res/icon/ios/icon-small@2x.png" width="58" height="58" />
<icon src="res/icon/ios/icon-small@3x.png" width="87" height="87" />
<!-- iPad Spotlight and Settings Icon -->
<icon src="res/icon/ios/icon-50.png" width="50" height="50" />
<icon src="res/icon/ios/icon-50@2x.png" width="100" height="100" />
<!-- iPad Pro -->
<icon src="res/icon/ios/icon-83.5@2x.png" width="167" height="167" />

<!-- Ohters -->
<icon src="res/icon/ios/icon-20.png" width="20" height="20" />
<icon src="res/icon/ios/icon-1024.png" width="1024" height="1024" />


<splash src="res/screen/ios/Default~iphone.png" width="320" height="480"/>
<splash src="res/screen/ios/Default@2x~iphone.png" width="640" height="960"/>
<splash src="res/screen/ios/Default-Portrait~ipad.png" width="768" height="1024"/>
<splash src="res/screen/ios/Default-Portrait@2x~ipad.png" width="1536" height="2048"/>
<splash src="res/screen/ios/Default-Landscape~ipad.png" width="1024" height="768"/>
<splash src="res/screen/ios/Default-Landscape@2x~ipad.png" width="2048" height="1536"/>
<splash src="res/screen/ios/Default-568h@2x~iphone.png" width="640" height="1136"/>
<splash src="res/screen/ios/Default-667h.png" width="750" height="1334"/>
<splash src="res/screen/ios/Default-736h.png" width="1242" height="2208"/>
<splash src="res/screen/ios/Default-Landscape-736h.png" width="2208" height="1242"/>
<splash src="res/screen/ios/Default-2436h.png" width="1125" height="2436"/>
<splash src="res/screen/ios/Default-Landscape-2436h.png" width="2436" height="1125"/>

<preference name="StatusBarOverlaysWebView" value="true" />
<preference name="StatusBarStyle" value="blackTranslucent" />
<preference name="WebViewBounce" value="false" />
<preference name="DisallowOverscroll" value="true" />

<!-- 自动隐藏启动页面AutoHideSplashScreen(默认为: true ) -->
<preference name="AutoHideSplashScreen" value="true" />

<!-- 显示启动页面的时间长度SplashScreenDelay(默认为: 3000) -->
<preference name="SplashScreenDelay" value="0" />

<!-- 淡入淡出效果FadeSplashScreen(默认为:true) -->
<!-- <preference name="FadeSplashScreen" value="false"/> -->

<!-- 淡入淡出效果的执行时间长度FadeSplashScreenDuration(默认为:500) -->
<!-- <preference name="FadeSplashScreenDuration" value="1000"/> -->

<!-- iOS上禁止启动页面 -->
<preference name="FadeSplashScreenDuration" value="0"/>

<!-- 启动页面是否允许旋转(默认为:true) -->
<!-- <preference name="ShowSplashScreenSpinner" value="false"/> -->

<!-- 使用 WKWebViewEngine -->
<!-- <feature name="CDVWKWebViewEngine">
<param name="ios-package" value="CDVWKWebViewEngine" />
</feature>
<preference name="CordovaWebViewEngine" value="CDVWKWebViewEngine" /> -->

<!-- <preference name="AllowUntrustedCerts" value="on" /> -->
<!-- <preference name="InterceptRemoteRequests" value="all" /> -->
<!-- <preference name="WKWebViewOnly" value="true" /> -->

</platform>
</widget>

res目录结构:

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
alex@Alex:~/AndroidStudioProjects/app_name$ tree res/
res/
├── icon
│   ├── android
│   │   ├── hdpi.png
│   │   ├── ldpi.png
│   │   ├── mdpi.png
│   │   ├── xhdpi.png
│   │   ├── xxhdpi.png
│   │   └── xxxhdpi.png
│   └── ios
│   ├── icon-20@3x.png
│   ├── icon-29@2x.png
│   ├── icon-29@3x.png
│   ├── icon-29.png
│   ├── icon@2x.png
│   ├── icon-40@2x.png
│   ├── icon-40.png
│   ├── icon-50@2x.png
│   ├── icon-50.png
│   ├── icon-57@2x.png
│   ├── icon-57.png
│   ├── icon-60@2x.png
│   ├── icon-60@3x.png
│   ├── icon-60.png
│   ├── icon-72@2x.png
│   ├── icon-72.png
│   ├── icon-76@2x.png
│   ├── icon-76.png
│   ├── icon.png
│   ├── icon-small@2x.png
│   └── icon-small.png
└── screen
├── android
│   ├── splash-land-hdpi.png
│   ├── splash-land-ldpi.png
│   ├── splash-land-mdpi.png
│   ├── splash-land-xhdpi.png
│   ├── splash-port-hdpi.png
│   ├── splash-port-ldpi.png
│   ├── splash-port-mdpi.png
│   └── splash-port-xhdpi.png
└── ios
├── Contents.json
├── Default-2436h.png
├── Default@2x~iphone.png
├── Default-568h@2x~iphone.png
├── Default-667h.png
├── Default-736h.png
├── Default~iphone.png
├── Default-Landscape-2436h.png
├── Default-Landscape@2x~ipad.png
├── Default-Landscape-736h.png
├── Default-Landscape~ipad.png
├── Default-Portrait@2x~ipad.png
└── Default-Portrait~ipad.png

# 有时候图片会有alpha, 在iOS提交到APP store的时候会被拒绝.解决办法执行如下命令,修改所有icon的png.
# for i in `ls *.png`; do convert $i -background black -alpha remove -alpha off $i; done

Android 启动页 黑屏问题解决

  • 修改 platforms/android/app/src/main/AndroidManifest.xml 文件
1
android:theme="@style/WelcomeStyle"
  • 修改 platforms/android/app/src/main/res/values/styles.xml 文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<resources>
<style name="WelcomeStyle" parent="@android:style/Theme.DeviceDefault.NoActionBar">
<!--指向启动前图片-->
<item name="android:windowBackground">@drawable/splash</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowFullscreen">false</item>
<!--不让windowBackground延申到navigation bar区域-->
<item name="android:windowDrawsSystemBarBackgrounds">false</item>
<!--适配Android P刘海屏-->
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
</style>
<style name="Appwelcome" parent="android:Theme.Translucent.NoTitleBar.Fullscreen">
</style>
</resources>
  • 添加 platforms/android/app/src/main/res/drawable/splash.xml 文件
1
2
3
4
5
6
7
8
9
10
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
android:opacity="opaque">
<!-- <item android:drawable="@android:color/white" />-->
<item android:gravity="fill">
<shape android:shape="rectangle">
<solid android:color="@android:color/white"/>
</shape>
</item>
</layer-list>

浏览器唤醒打开APP

修改 platforms/android/app/src/main/AndroidManifest.xml 文件

1
2
3
4
5
6
7
8
9
10
11
12
      <activity android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|smallestScreenSize|screenLayout|uiMode" android:label="@string/activity_name" android:launchMode="singleTop" android:name="MainActivity" android:theme="@style/WelcomeStyle" android:windowSoftInputMode="adjustResize">
...
<!-- # 添加内容-开始 -->
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="wxea5a9199cd873171" />
</intent-filter>
<!-- # 添加内容-结束 -->

</activity>

配置权限与安装对应插件

内置打开网页, 打开摄像头, 唤起支付等功能需要安装对应的插件. 同时配置相应的权限.

功能 插件名称 需要配置权限 其它
支付 cordova-plugin-alipay-v2 添加appid
摄像头 cordova-plugin-camera
内置浏览 cordova-plugin-inappbrowser
启动页 cordova-plugin-splashscreen
cordova-fix-blackscreen
cordova-plugin-whitelist
返回键 cordova-plugin-background-mode Android系统返回键
ios web引擎 cordova-plugin-wkwebview-engine 有bug先不用
cordova-plugin-wkwebview-file-xhr 有bug先不用
Android web引擎 cordova-plugin-crosswalk-webview@latest 有bug先不用
微信支付、登录、分享 cordova-plugin-wechat 添加wechatappid

配置插件

1
2
3
4
5
6
7
8
9
10
cordova plugin add cordova-plugin-alipay-v2 --variable APP_ID=xxx
cordova plugin add cordova-plugin-camera
cordova plugin add cordova-plugin-inappbrowser
cordova plugin add cordova-plugin-splashscreen
cordova plugin add cordova-fix-blackscreen
cordova plugin add cordova-plugin-background-mode
cordova plugin add cordova-plugin-wechat --variable wechatappid=微信APPid
# cordova plugin add cordova-plugin-wkwebview-engine
# cordova plugin add cordova-plugin-crosswalk-webview@latest --save
cordova plugin list

配置权限

iOS和Android的权限配置分别在打包目录文件中设置,所以先要生成打包目录文件.

Android:

生成打包目录文件cordova platform add android --save

platforms/android/CordovaLib/AndroidManifest.xml 文件添加权限

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?xml version="1.0" encoding="utf-8"?>

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.apache.cordova" android:versionName="1.0" android:versionCode="1">
<!-- <uses-permission android:name="android.permission.RECEIVE_SMS"/> -->
<!-- <uses-permission android:name="android.permission.RECORD_AUDIO"/> -->
<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<!-- <uses-permission android:name="android.permission.READ_PHONE_STATE"/> -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<!-- <uses-permission android:name="android.permission.BROADCAST_SMS"/> -->
<uses-permission android:name="android.permission.INTERNET"/>
<!-- <uses-permission android:name="android.permission.READ_SMS"/> -->
<application android:usesCleartextTraffic="true">
</application>
</manifest>

iOS:

生成打包目录文件cordova platform add ios@latest --save

ios/app_name/app_name-Info.plist文件中添加

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<dict
<!-- <key>NSLocationAlwaysUsageDescription</key>
<string>程序需要访问您的位置,以便APP始终获取您的位置信息</string> -->
<key>NSCameraUsageDescription</key>
<string>程序想要调用你的摄像头,以便APP可以获取图片.</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>程序想要访问你的相册,以便APP可以获取图片.</string>
...
<!-- <key>UIBackgroundModes</key>
<array>
<string>去掉麦克风使用</string>
</array> -->
<key>LSApplicationQueriesSchemes</key>
<array>
<string>wechat</string>
<string>weixin</string>
</array>
</dict>

运行和打包apk

在线运行Android项目:

1
cordova run android --device 

浏览器调试工具

1
chrome://inspect/#**devices**

打包发布apk文件:

1
cordova build android --release

Android APP打包keystore:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
alex@Alex:~/AndroidStudioProjects/my_app$ keytool -genkey -v -keystore ./my_app.keystore -alias my_app -keyalg RSA -validity 3650

alex@Alex:~/AndroidStudioProjects/my_app$ ls
config.xml hooks node_modules package.json package-lock.json platforms plugins res my_app.keystore www
alex@Alex:~/AndroidStudioProjects/my_app$ cat build.json
{
"android": {
"release": {
"keystore": "my_app.keystore",
"alias": "my_app",
"storePassword": "storePassword",
"password": "password"
}
}
}

ios项目免证书真机调试

  • platforms/ios下的ios文件夹压缩成ios.zip

  • 拷贝到mac电脑上使用xocde打开

  • 点击左上角的Xcode,选择Preferences–>Accounts–>左下角加号(填写自己的ippid)–>Manage Certificates–>点击弹出框的左下角+证书(Apple Development)–>

  • 使用usb连上手机,点击项目名称,在TARGETS(Debug)下选择signing&Capabilites中的team中选择自己的账号,如果报错,就将自己的包名使用唯一

  • 点击xcode的运行按钮

xcode run and build

96619796

584220193

844538922

1341378044

2073041873

ubuntu18 命令行设置wireless 连接WiFi热点

[TOC]

nmcli

查看可用WiFi

1
2
3
4
5
$ nmcli dev wifi 
IN-USE SSID MODE CHAN RATE SIGNAL BARS SECURITY
HiWiFi_5489B6 Infra 3 130 Mbit/s 70 ▂▄▆_ WPA1 WPA2
PING Infra 11 195 Mbit/s 54 ▂▄__ WPA2
H3C Infra 1 270 Mbit/s 44 ▂▄__ WPA1 WPA2

rfkill

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
# rfkill list 
0: bluedroid_pm: Bluetooth
Soft blocked: yes
Hard blocked: no
1: phy0: Wireless LAN
Soft blocked: no
Hard blocked: no
2: brcmfmac-wifi: Wireless LAN
Soft blocked: no
Hard blocked: no

# rfkill block 1
# rfkill block 2
# rfkill list
0: bluedroid_pm: Bluetooth
Soft blocked: yes
Hard blocked: no
1: phy0: Wireless LAN
Soft blocked: yes
Hard blocked: no
2: brcmfmac-wifi: Wireless LAN
Soft blocked: yes
Hard blocked: no

# rfkill unblock 1
# rfkill unblock 2





iw

1
2
3
4
5
6
7
.扫描可用的WiFi

# 不加less可能会产生太多输出
iw dev wlan0 scan |less

# 或者
iwlist wlan0 scanning

ifconfig

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# ifconfig wlan0 
wlan0: flags=4098<BROADCAST,MULTICAST> mtu 1500
ether 00:04:4b:8c:55:d4 txqueuelen 1000 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 70 bytes 7620 (7.6 KB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

# ifconfig wlan0 down
ip link set wlan0 down

# ifconfig wlan0 up
# ip link set wlan0 up

wpa

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
wpa_passphrase HiWiFi_5489B6 Alex.sg-ai.c0m > wpa_passphrase.conf
wpa_supplicant -i wlan0 -c ./wpa_passphrase.conf



wpa_passphrase HiWiFi_5489B6 Alex.sg-ai.c0m > wpa_passphrase.conf
# cat wpa_passphrase.conf
network={
ssid="HiWiFi_5489B6"
#psk="Alex.sg-ai.c0m"
psk=a4975b898770daca9cdb38b5167b216561e83cc00460edb3ef2ce5a9ee1f77ce
}

wpa_supplicant -Dnl80211 -iwlan0 -cwpa_passphrase.conf -B
dhclient wlan0
killall wpa_supplicant

stop networkd-dispatcher

1
2
3
4
5
6
7
8
9
$ systemctl stop networkd-dispatcher
$ systemctl disable networkd-dispatcher
$ systemctl mask networkd-dispatcher
$ apt purge nplan netplan.io

apt install nplan
systemctl unmask networkd-dispatcher
systemctl start network-dispatcher
systemctl enable network-dispatcher