hello云胜

技术与生活

0%

部署

测试环境下的快捷部署方案

生产环境应该使用外置的高可用 postgresql 和 redis。

1
2
3
4
5
6
helm repo add gitlab https://charts.gitlab.io/
helm search repo -l gitlab/gitlab

NAME CHART VERSION APP VERSION DESCRIPTION
gitlab/gitlab 9.8.2 v18.8.2 GitLab is the most comprehensive AI-powered Dev...
gitlab/gitlab 9.8.1 v18.8.1 GitLab is the most comprehensive AI-powered Dev...

我这里安装最新的 v18.8.2 版本

1
helm pull gitlab/gitlab --version 9.8.2

准备工作

集群中需要有一个默认的 storageClass。否则需要指定。

1
kubectl patch sc alicloud-disk-cloud-sperf -p '{"metadata":{"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'

安装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
helm upgrade --install gitlab . **\**
--namespace gitlab \
--create-namespace \**
** --timeout 600s **\
** --set global.hosts.domain=gitys.com **\
** --set global.hosts.externalIP=10.10.10.10 **\
** --set certmanager-issuer.email=helloyunsheng@126.com \
--set gitlab-runner.install=false \
--set minio.persistence.size=20Gi \
--set postgresql.primary.persistence.size=20Gi \
--set postgresql.readReplicas.persistence.size=20Gi \
--set redis.master.persistence.size=20Gi \
--set redis.replica.persistence.size=20Gi \
--set gitaly.persistence.size=20Gi \
--set prometheus.server.persistentVolume.size=20Gi

gitlab-runner.install=false 先不安装 runner。后面单独部署 runner。

minio.persistence.size=20Gi 这个是因为我这使用的是阿里云 ack,他的 pvc 要求最小是 20G,而 gitlab 的默认是 10G,会导致 pvc 一直 pending。

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
$ kubectl -n gitlab get pod
NAME READY STATUS RESTARTS AGE
gitlab-certmanager-68bd67c778-w95m6 1/1 Running 0 4d20h
gitlab-certmanager-cainjector-668b49cdd9-6z6vv 1/1 Running 0 4d20h
gitlab-certmanager-webhook-785f55b64b-dsk87 1/1 Running 0 4d20h
gitlab-gitaly-0 1/1 Running 0 4d20h
gitlab-gitlab-exporter-6c87fcfd44-b8stl 1/1 Running 0 4d20h
gitlab-gitlab-shell-868c489c45-bzhlk 1/1 Running 0 4d20h
gitlab-gitlab-shell-868c489c45-dtgl9 1/1 Running 0 4d20h
gitlab-kas-656b58dd77-h79lw 1/1 Running 3 (4d20h ago) 4d20h
gitlab-kas-656b58dd77-t7fx9 1/1 Running 2 (4d20h ago) 4d20h
gitlab-migrations-fb7b833-kbpxv 0/1 Completed 2 4d20h
gitlab-minio-84988bcc94-4cgw2 1/1 Running 0 4d20h
gitlab-minio-create-buckets-6523680-hsddh 0/1 Completed 0 4d20h
gitlab-nginx-ingress-controller-686c67b947-8sgkc 1/1 Running 0 4d20h
gitlab-nginx-ingress-controller-686c67b947-kc8xb 1/1 Running 0 4d20h
gitlab-postgresql-0 2/2 Running 0 4d20h
gitlab-prometheus-server-5d8c694546-k88zz 2/2 Running 0 4d20h
gitlab-redis-master-0 2/2 Running 0 4d20h
gitlab-registry-7bf874b696-7z24g 1/1 Running 0 4d20h
gitlab-registry-7bf874b696-fps6x 1/1 Running 0 4d20h
gitlab-sidekiq-all-in-1-v2-559c95f89d-rcvv5 1/1 Running 1 (4d20h ago) 4d20h
gitlab-toolbox-78674765bb-b95b7 1/1 Running 0 4d20h
gitlab-webservice-default-6867b55b56-hvfcx 2/2 Running 1 (4d20h ago) 4d20h
gitlab-webservice-default-6867b55b56-qkz2h 2/2 Running 1 (4d20h ago) 4d20h

注意:

  1. 下载镜像很慢。很慢。
  2. 我这里没有同时安装 gitlab runner。因为 gitlabrunner 是负责执行业务流水线的实例,我希望安装在单独的 ns 或其他 k8s 集群中,不和 gitlab 主程序混在一起。

查看ingress

1
2
3
4
5
6
$ kubectl -n gitlab get ingress
NAME CLASS HOSTS ADDRESS PORTS AGE
gitlab-kas gitlab-nginx kas.gitys.com 10.222.242.114 80, 443 3d23h
gitlab-minio gitlab-nginx minio.gitys.com 10.222.242.114 80, 443 3d23h
gitlab-registry gitlab-nginx registry.gitys.com 10.222.242.114 80, 443 3d23h
gitlab-webservice-default gitlab-nginx gitlab.gitys.com 10.222.242.114 80, 443 3d23h

登录

我本地配置了 hosts 域名解析

查看初始化的默认密码

1
2
$ kubectl -n gitlab get secret gitlab-gitlab-initial-root-password -ojsonpath='{.data.password}' | base64 -d ; echo
xM5e5iyYDbOtxibK5BloBCMJ0KhSi3eNRZWDziGnI8LtYQ5IGbK7pqTTRuqoyWC0

不要怀疑,这一串就是。

改密

设置为中文

Linux操作系统

进程

新进程的创建时通过fork一个父进程实现的。就像我们写代码,从头写太麻烦,复制一个,改吧改吧。

0号进程:系统创建的第一个进程。这是唯一一个没有通过 fork 或者 kernel_thread 产生的进程。是内核进程。但0号进程不是一个实实在在可以看到的进程

1号进程:它将运行一个用户进程。是所有用户态进程的始祖

2号进程:管理所有内核态的进程,是后面所有内核态进程的始祖

用户进程和用户进程必须进行权限分割。内核态 用户态的概念

用户进程要访问核心资源,只能通过系统调用。

当一个用户态的程序运行到一半,要访问一个核心资源,例如访问网卡发一个网络包,就需要暂停当前的运行,调用系统调用,接下来就轮到内核中的代码运行了。

暂停的那一刻,要把当时 CPU 的寄存器的值全部暂存到一个地方

这个过程就是这样的:用户态 - 系统调用 - 保存寄存器 - 内核态执行系统调用 - 恢复寄存器 - 返回用户态,然后接着运行。

image-20211021110125680

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ ps -ef
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 2019 ? 1-01:41:30 /sbin/init
root 2 0 0 2019 ? 00:00:00 [kthreadd]
root 3 2 0 2019 ? 00:47:34 [migration/0]
root 4 2 0 2019 ? 00:25:27 [ksoftirqd/0]
root 5 2 0 2019 ? 00:00:00 [migration/0]
root 6 2 0 2019 ? 00:01:18 [watchdog/0]
...
root 99584 99545 0 Jun03 ? 00:00:00 [sh] <defunct>
root 99587 99581 0 Apr18 ? 00:00:00 /bin/sh -c sh /home/hcsp/rsync/cron-ftp-rsync.sh > /home/hcsp/rsync/cron.out
root 99589 99587 0 Apr18 ? 00:00:00 sh /home/hcsp/rsync/cron-ftp-rsync.sh
root 99598 99589 0 Apr18 ? 00:00:00 ftp -v -n
root 99612 1 0 2020 ? 00:00:00 sshd: api [priv]
api 99617 99612 0 2020 ? 00:00:00 sshd: api@notty

PID 1 的进程就是我们的 init 进程 systemd,PID 2 的进程是内核线程 kthreadd

其中用户态的不带中括号,内核态的带中括号。

所有带中括号的内核态的进程,祖先都是 2 号进程。而用户态的进程,祖先都是 1 号进程。

每个进程都有自己独立的虚拟内存空间

进程上下文切换

上下文切换主要干两件事情,一是切换进程空间,也即虚拟内存;二是切换寄存器和 CPU 上下文。

抢占的时机

真正的抢占还需要时机,一定要规划几个时机,这个时机分为用户态和内核态。

用户态的抢占时机

线程

对于任何一个进程来讲,即便我们没有主动去创建线程,进程也是默认有一个主线程的。

线程是负责执行二进制指令的。进程要比线程管的宽多了,除了执行指令之外,内存、文件系统等等都要它来管。

所以,进程相当于一个项目,而线程就是为了完成项目需求,而建立的一个个开发任务

可不可以多进程代替多线程?

一个进程之内的线程共享内存。

进程之间内存相互隔离,进程间通信问题。


每个线程有私有的数据有两种,一是执行方法的的栈空间,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ ulimit -a
core file size (blocks, -c) 0
data seg size (kbytes, -d) unlimited
scheduling priority (-e) 0
file size (blocks, -f) unlimited
pending signals (-i) 515259
max locked memory (kbytes, -l) 64
max memory size (kbytes, -m) unlimited
open files (-n) 655350
pipe size (512 bytes, -p) 8
POSIX message queues (bytes, -q) 819200
real-time priority (-r) 0
stack size (kbytes, -s) 10240 --------------栈空间大小
cpu time (seconds, -t) unlimited
max user processes (-u) 655350
virtual memory (kbytes, -v) unlimited
file locks (-x) unlimited

二是线程私有数据。Thread Specific Data。这个存储时一个key,但各线程可根据自己的需要往 key 中填入不同的值,这就相当于提供了一个同名而不同值的全局变量。而等到线程退出的时候,就会调用析构函数释放 自己的value。

并发数据保护

互斥锁 Mutex

task

在 Linux 里面,无论是进程,还是线程,到了内核里面,我们统一都叫任务(Task),由一个统一的结构task_struct进行管理

image-20211021132951706

系统调用

glibc,对系统调用进行了封装。用户进程一般通过glibc进行系统调用

一旦进行系统调用,那么用户进程中断,陷入(trap)内核态

用户态函数栈

image-20211021135904588

栈基地址存的是前一个栈帧的地址,从里面拉取局部变量

内核态函数栈

内核态和用户态

image-20211021142354731

调度

进程数目远远超过 CPU 的数目,因而就需要进行进程的调度,有效地分配 CPU 的时间,既要保证进程的最快响应,也要保证进程之间的公平。这也是一个非常复杂的、需要平衡的事情。

调度策略

进程大概可以分成两种:实时进程普通进程

优先级,优先级其实就是一个数值,对于实时进程,优先级的范围是 0~99;对于普通进程,优先级的范围是 100~139。数值越小,优先级越高。从这里可以看出,所有的实时进程都比普通进程优先级要高。

普通进程使用的调度策略是 fair_sched_class 完全公平调度算法

在 Linux 里面,实现了一个基于 CFS 的调度算法。CFS 全称 Completely Fair Scheduling,叫完全公平调度

首先,你需要记录下进程的运行时间。CPU 会提供一个时钟,过一段时间就触发一个时钟中断。就像咱们的表滴答一下,这个我们叫 Tick。CFS 会为每一个进程安排一个虚拟运行时间 vruntime。如果一个进程在运行,随着时间的增长,也就是一个个 tick 的到来,进程的 vruntime 将不断增大。没有得到执行的进程 vruntime 不变。

显然,那些 vruntime 少的,原来受到了不公平的对待,需要给它补上,所以会优先运行这样的进程。

CFS 需要一个数据结构来对 vruntime 进行排序,找出最小的那个。–》能够平衡查询和更新速度的是树,在这里使用的是红黑树。

image-20211021153054847

vruntime 最小的在树的左侧,vruntime 最多的在树的右侧。 CFS 调度策略会选择红黑树最左边的叶子节点作为下一个将获得 cpu 的任务。

内存管理

虚拟地址

虚拟空间一切二,一部分用来放内核的东西,称为内核空间,一部分用来放进程的东西,称为用户空间

分段

image-20211022141151663

其实 Linux 倾向于另外一种从虚拟地址到物理地址的转换方式,称为分页(Paging)。

对于物理内存,操作系统把它分成一块一块大小相同的页,这样更方便管理,例如有的内存页面长时间不用了,可以暂时写到硬盘上,称为换出。一旦需要的时候,再加载进来,叫作换入。这样可以扩大可用物理内存的大小,提高物理内存的利用率。

这个换入和换出都是以页为单位的。页面的大小一般为 4KB。为了能够定位和访问每个页,需要有个页表,保存每个页的起始地址,再加上在页内的偏移量,组成线性地址,就能对于内存中的每个位置进行访问了。

虚拟地址分为两部分,页号页内偏移。页号作为页表的索引,页表包含物理页每页所在物理内存的基地址。这个基地址与页内偏移的组合就形成了物理内存地址。

image-20211022141525189

image-20211022141758524

虚拟内存区域可以映射到物理内存,也可以映射到文件,映射到物理内存的时候称为匿名映射,anon_vma 中,anoy 就是 anonymous,匿名的意思,映射到文件就需要有 vm_file 指定被映射的文件。

image-20211022174103451

每个 CPU 都有自己的本地内存,CPU 访问本地内存不用过总线,因而速度要快很多,每个 CPU 和内存在一起,称为一个 NUMA 节点。但是,在本地内存不足的情况下,每个 CPU 都可以去另外的 NUMA 节点申请内存,这个时候访问延时就会比较长。

每一个节点分成一个个区域 zone,zone内又分为多部份

有一块是DMA(Direct Memory Access,直接内存存取)的内存。DMA 是这样一种机制:要把外设的数据读入内存或把内存的数据传送到外设,原来都要通过 CPU 控制完成,但是这会占用 CPU,影响 CPU 处理其他事情,所以有了 DMA 模式。CPU 只需向 DMA 控制器下达指令,让 DMA 控制器来处理数据的传送,数据传送完毕再把信息反馈给 CPU,这样就可以解放 CPU。

内存映射mmap

其实内存映射不仅仅是物理内存和虚拟内存之间的映射,还包括将文件中的内容映射到虚拟内存空间。这个时候,访问内存空间就能够访问到文件里面的数据。

如果我们要申请小块内存,就用 brk。如果申请一大块内存,就要用 mmap。对于堆的申请来讲,mmap 是映射内存空间到物理内存。

另外,如果一个进程想映射一个文件到自己的虚拟内存空间,也要通过 mmap 系统调用。这个时候 mmap 是映射内存空间到物理内存再到文件。

文章复制自公众号 小姐姐味道

少量个人根据自身情况添加

本文将由一个Linux命令概览开始,说明Linux命令的方方面面。如果你读完这部分还是一头雾水,那么就证明需要按照下面的小白教程去学习了,内容涵盖了:如何挑选Linux发行版、如何安装Linux系统,按照主题学习Linux系统的命令等。

入门后的 **学习方式:多敲多打,用条件反射替代大脑记忆–如果你将来或者现在要用它来吃饭的话。**其中,也有一些难啃的骨头,关注小姐姐味道微信公众号,我们一起用锋利的牙齿,来把它嚼碎。

1. Linux命令概览

这部分是给稍微有点Linux经验的同学准备的,如果你是初学者,请跳过此part直接进入第二部分。

图片

1.1目录操作

工作中,最常打交道的就是对目录和文件的操作。linux提供了相应的命令去操作他,并将这些命令抽象、缩写。

1.1.1 基本操作

可能是这些命令太常用了,多打一个字符都是罪过。所以它们都很短,不用阿拉伯数字,一个剪刀手就能数过来。

图片

看命令。
mkdir 创建目录 make dir

cp 拷贝文件 copy

mv 移动文件 move

rm 删除文件 remove

例子:

1
2
3
4
5
6
7
8
9
10
11
# 创建目录和父目录a,b,c,d
mkdir -p a/b/c/d

# 拷贝文件夹a到/tmp目录
cp -rvf a/ /tmp/

# 移动文件a到/tmp目录,并重命名为b
mv -vf a /tmp/b

# 删除机器上的所有文件
rm -rvf /

1.1.2 漫游

linux上是黑漆漆的命令行,依然要面临人生三问:我是谁?我在哪?我要去何方?

ls 命令能够看到当前目录的所有内容。ls -l能够看到更多信息,判断你是谁。
pwd 命令能够看到当前终端所在的目录。告诉你你在哪。
cd 假如你去错了地方,cd命令能够切换到对的目录。
find find命令通过筛选一些条件,能够找到已经被遗忘的文件。

至于要去何方,可能就是主宰者的意志了。

1.2 文本处理

这是是非常非常加分的技能。get到之后,也能节省更多时间来研究面向对象。小姐姐味道已经输出了“最常用的vim、sed、awk技巧系列”。

图片

1.2.1 查看文件

cat
最常用的就是cat命令了,注意,如果文件很大的话,cat命令的输出结果会疯狂在终端上输出,可以多次按ctrl+c终止。

1
2
3
4
5
# 查看文件大小
du -h file

# 查看文件内容
cat file

less
既然cat有这个问题,针对比较大的文件,我们就可以使用less命令打开某个文件。类似vim,less可以在输入/后进入查找模式,然后按n(N)向下(上)查找。
有许多操作,都和vim类似,你可以类比看下。

tail
大多数做服务端开发的同学,都了解这么命令。比如,查看nginx的滚动日志。

1
tail -f access.log

tail命令可以静态的查看某个文件的最后n行,与之对应的,head命令查看文件头n行。但head没有滚动功能,就像尾巴是往外长的,不会反着往里长。

1
2
tail -n100 access.log
head -n100 access.log

1.2.1 统计

sort和uniq经常配对使用。sort可以使用-t指定分隔符,使用-k指定要排序的列。

下面这个命令输出nginx日志的ip和每个ip的pv,pv最高的前10

1
2
3
# 2019-06-26T10:01:57+08:00|nginx001.server.ops.pro.dc|100.116.222.80|1x.xx.150.232:41021|0.014|0.011|0.000|200|200|273|-|/visit|sign=91CD1988CE8B313B8A0454A4BBE930DF|-|-|http|POST|112.4.238.213

awk -F"|" '{print $3}' access.log | sort | uniq -c | sort -nk1 -r | head -n10

1.2.3 其他

grep
grep用来对内容进行过滤,带上--color参数,可以在支持的终端可以打印彩色,参数n则输出具体的行数,用来快速定位。
比如:查看nginx日志中的POST请求。

1
grep -rn --color POST access.log

推荐每次都使用这样的参数。

如果我想要看某个异常前后相关的内容,就可以使用ABC参数。它们是几个单词的缩写,经常被使用。A after 内容后n行B before 内容前n行C count? 内容前后n行
就像是这样:

1
grep -rn --color Exception -A10 -B2   error.log

diff

diff命令用来比较两个文件是否的差异。当然,在ide中都提供了这个功能,diff只是命令行下的原始折衷。对了,diff和patch还是一些平台源码的打补丁方式,你要是不用,就pass吧。

1.3压缩

为了减小传输文件的大小,一般都开启压缩。linux下常见的压缩文件有tar、bzip2、zip、rar等,7z这种用的相对较少。

图片

.tar 使用tar命令压缩或解压
.bz2 使用bzip2命令操作
.gz 使用gzip命令操作
.zip 使用unzip命令解压
.rar 使用unrar命令解压

最常用的就是.tar.gz文件格式了。其实是经过了tar打包后,再使用gzip压缩。

创建压缩文件

1
tar cvfz  archive.tar.gz dir/

解压

1
tar xvfz. archive.tar.gz

快去弄清楚它们的关系吧。

1.4 日常运维

开机是按一下启动按钮,关机总不至于是长按启动按钮吧。对了,是shutdown命令,不过一般也没权限-.-!。passwd命令可以用来修改密码,这个权限还是可以有的。

图片

mount
mount命令可以挂在一些外接设备,比如u盘,比如iso,比如刚申请的ssd。可以放心的看小电影了。

1
mount /dev/sdb1 /xiaodianying

chown
chown 用来改变文件的所属用户和所属组。
chmod 用来改变文件的访问权限。

这两个命令,都和linux的文件权限777有关。
示例:

1
2
3
4
5
6
7
8
# 毁灭性的命令
chmod 000 -R /

# 修改a目录的用户和组为 xjj
chown -R xjj:xjj a

# 给a.sh文件增加执行权限(这个太常用了)
chmod a+x a.sh

yum
假定你用的是centos,则包管理工具就是yum。如果你的系统没有wget命令,就可以使用如下命令进行安装。

1
yum install wget -y

systemctl
当然,centos管理后台服务也有一些套路。service命令就是。systemctl兼容了service命令,我们看一下怎么重启mysql服务。 推荐用下面这个。

1
2
service mysql restart
systemctl restart mysqld

对于普通的进程,就要使用kill命令进行更加详细的控制了。kill命令有很多信号,如果你在用kill -9,你一定想要了解kill -15以及kill -3的区别和用途。

su
su用来切换用户。比如你现在是root,想要用xjj用户做一些勾当,就可以使用su切换。

1
2
su xjj
su - xjj

-可以让你干净纯洁的降临另一个账号,不出意外,推荐。

查看端口监听

1
netstat -nltp

1.5 系统状态概览

登陆一台linux机器,有些命令能够帮助你快速找到问题。这些命令涵盖内存、cpu、网络、io、磁盘等。

图片

unameuname命令可以输出当前的内核信息,让你了解到用的是什么机器。

1
uname -a

ps
ps命令能够看到进程/线程状态。和top有些内容重叠,常用。

1
2
# 找到java进程
ps -ef|grep java

top系统状态一览,主要查看。cpu load负载、cpu占用率。使用内存或者cpu最高的一些进程。下面这个命令可以查看某个进程中的线程状态。

1
top -H -p pid

free
top也能看内存,但不友好,free是专门用来查看内存的。包括物理内存和虚拟内存swap。

df
df命令用来查看系统中磁盘的使用量,用来查看磁盘是否已经到达上限。参数h可以以友好的方式进行展示。

1
df -h

ifconfig
查看ip地址,不啰嗦,替代品是ip addr命令。

ping
至于网络通不通,可以使用ping来探测。(不包括那些禁ping的网站)

netstat虽然ss命令可以替代netstat了,但现实中netstat仍然用的更广泛一些。比如,查看当前的所有tcp连接。

1
netstat -ant

此命令,在找一些本地起了什么端口之类的问题上,作用很大。

1.6 工作常用

还有一些在工作中经常会用到的命令,它们的出现频率是非常高的 ,都是些熟面孔。

图片

export
很多安装了jdk的同学找不到java命令,export就可以帮你办到它。export用来设定一些环境变量,env命令能看到当前系统中所有的环境变量。比如,下面设置的就是jdk的。

1
export PATH=$PATH:/home/xjj/jdk/bin

有时候,你想要知道所执行命令的具体路径。那么就可以使用whereis命令,我是假定了你装了多个版本的jdk。

crontab
这就是linux本地的job工具。不是分布式的,你要不是运维,就不要用了。比如,每10分钟提醒喝茶上厕所。

1
*/10 * * * * /home/xjj/wc10min

datedate命令用来输出当前的系统时间,可以使用-s参数指定输出格式。但设置时间涉及到设置硬件,所以有另外一个命令叫做hwclock

xargsxargs读取输入源,然后逐行处理。这个命令非常有用。举个栗子,删除目录中的所有class文件。

1
2
3
4
find . | grep .class$ | xargs rm -rvf

#把所有的rmvb文件拷贝到目录
ls *.rmvb | xargs -n1 -i cp {} /mount/xiaodianying

1.7 网络

linux是一个多作业的网络操作系统,所以网络命令有很多很多。工作中,最常和这些打交道。

ssh
这个,就不啰嗦了。你一定希望了解ssh隧道是什么。你要是想要详细的输出过程,记得加参数-v

scp
scp用来进行文件传输。也可以用来传输目录。也有更高级的sftp命令。

1
2
scp a.txt 192.168.0.12:/tmp/a.txt
scp -r a_dir 192.168.0.12:/tmp/

wget
你想要在服务器上安装jdk,不会先在本地下载下来,然后使用scp传到服务器上吧(有时候不得不这样)。wget命令可以让你直接使用命令行下载文件,并支持断点续传。

1
wget -c http://oracle.fuck/jdk2019.bin

mysql
mysql应用广泛,并不是每个人都有条件用上navicat的。你需要了解mysql的连接方式和基本的操作,在异常情况下才能游刃有余。

1
mysql -u root -p -h 192.168.1.2

不要觉得复杂,命令是有限的,但激情无限;都会也不要骄傲,一个vim就够折腾一辈子。捷径就是总结,深入只有探索。白马过隙,终会行云流水,手到擒来。
物是人非,年华易老。唯有时光,不会辜负。

2. 挑选一个Linux发行版

和Linux比较像的还有Unix,但如果你是一个二三十岁的小青年,你接触到可能只有Linux的世界了。从手机,到服务器上广泛使用的centos,到漂亮的桌面发行版ubuntu,甚至是风靡全球的树莓派,到处都是linux的身影。

2.1 你需要知道这些linux历史

知道一点相关操作系统的历史,是能够陶冶情操的。GNU/Linux是为了抵制一些商业公司的垄断行为而发展起来的,凝结了一代互联网人向往自由的心血。

和其他Unix比起来,Linux其实很年轻。直到1991年,一个叫Linus Torvalds的芬兰年轻人才开始开发我们现在所知道的Linux内核

Linux的吉祥物是企鹅,这个吉祥物直到1996年才确定,所以你会经常看到一些搞笑的图片。如果你是90后,那这只小企鹅几乎和你一般大,还是个年轻的小伙。

图片

Linux的发展历程比较的复杂。经过一次次的过关斩将,Linux走到今天确属不易。关于其发展历史,你可以通过下面的链接,查看高清图片。20年的时间,对软件行业来说,是一段非常漫长的时光,有多少的风光已经物是人非。

图片

高清见图片(http://1t.click/aUnx) 。可以看到,linux只占了那可怜的一小块。这就像人类的出现,在生命的长河中,微不足道,但却是一个质的飞跃。

你可能注意到,在前面的描述中,说的是GUN/Linux,而不仅仅是Linux。Linux本身只是一个内核,作用有限,只有和GNU联合起来,拥有完整的生态才会发挥它的作用。

谈到上面区别的原因,是为了记住Richard Stallman在1983年发起的GNU计划。他同时是smalltalk语言的发明者,被公认的第二个面向对象的语言。我在早些年,还研究过一段时间。哦,他还编写了一个巨无霸编辑器,Emacs

只有一个人被捧成神,他才会有能量折磨你。

针对于Linux历史,我们不做过多介绍。下面介绍几个经典的发行版本。

2.2 精选版本介绍

现在的Linux发行版本,已经有上千个,你要是喜欢、而且多金,你也可以做一个。如何在这其中,找到最合适的版本,是需要经过一番折腾的。很多发行版本,其实是很小众的。

这不像是哲学领域的某些东西,真理掌握在少数人手中。只有获得良好发展,并得到认可的Linux发行版,才有它的价值,可以说是彻头彻尾的实用主义。

但这东西又像女朋友,刚开始感觉风采迥异,各有千秋,到最后了解到是一样的庸俗不堪。但有人就是喜欢Linux相关的工作,一干就是一辈子…

我可以先说一下自己的历程。刚开始,接触的是红帽redhat,当时还没有分什么企业版。用了一段时间以后,又切换成更稳定的slackware。但是slackware上的程序更新实在太慢了,于是又切换成readhat血统的fedora,这个版本的软件保鲜度很高。其间,又尝试了其他几个linux版本,最终,在2013年前后,换成了滚动升级的archlinux,直到现在。

要我个人做个推荐的话:
1、个人用户(技术),桌面版用ubuntu=>archlinux
2、企业用户,服务器,使用centos

2.3 主要起源

这么多Linux版本,其实有两条主线。debian系列和redhat系列。很多发行版本,其实是二次翻新,很多就直接拿这两个基础系列进行改造。正所谓:操作系统千千万,都是帽子和大便。

debian

下面这个屎一样的图表,就是debian。呃呃呃,和大便只差一个字母。

图片

Debian计划是一个致力于创建一个自由操作系统的合作组织。它的特点是:稳定、安全,到现在为止,已经发展了20多年了。我们所熟悉的ubuntu,就是基于debian改进的。

redhat

图片

红帽是一家商业公司,涉足Linux比较早,现在对个人提供一些红帽认证之类的证书。现在云主机使用较多的centos,包括红帽公司的RHEL,占据了大部分服务器市场。近期,centos 8推出了centos stream滚动版本,看起来更像是一个正常的操作系统。

2.4 典型版本

我们看一下处于不同层次的几个典型版本。从应用方面来说,linux有桌面、服务器、研究用等用途。

2.4.1、ubuntu

图片

ubuntu的出现,对Linux的推广有不可磨灭的贡献。它是一个易于安装的桌面版本(也有服务器版本),界面非常漂亮。ubuntu是基于debian系统的unstable分支修改的,包管理软件是apt-get

它的创建者是Mark Shuttleworth,南非企业家,世界上第二名自资的太空游客。我想,无论是太空还是ubuntu,这都是梦想吧。

2.4.2、centos

图片

centos是目前最流行的服务器版本。它是RHEL源代码再编译的产物,主要是为了绕开一些法律问题。在包管理,甚至稳定性上,与红帽企业版没什么差别。

2.4.3、archlinux

图片

archlinux采用滚动升级的模式进行发行,尽全力提供最新的稳定版本。刚开始安装,arch只提供一个基本的系统,甚至连界面都没有,对初学者不是很友好。

但是,archlinux是非常干净的系统。很多软件,只有你需要的时候才会安装。它的软件和理念通常都是最新的,定制化非常强,深得许多Linux爱好者的喜爱。

2.4.4、gentoo

img

上面的archlinux,提供了编译后的软件包。用户在安装软件时,只需要下载、解压即可。gentoo将这个过程更近一步,可以说更加的变态。它下载的是软件的源代码,然后在本地进行编译,然后安装。

这通常非常的蛋疼,因为下载、编译会花费非常长的时间,但它有一个非常大的优点,就是稳定。

这个系统比较底层,对技能要求更多,不太推荐。

2.4.5 、LFS

图片

LFS的全拼是“linux from scratch”,意思是从零构建一个linux系统。它有一个非常详细的安装文档,教你怎样编译内核,编译引导程序,编译和配置必要的软件。

这是一个疯狂而必要的过程。如果你想要自己的Linux之上更上层楼,跟着文档做一遍是受益无穷的。你需要经过多次交叉编译,最终使用chroot命令切换到新系统进行后续操作。

想做一个自己的发行版么?从这开始吧。

2.4.6、kali

图片

kali linux是一个非常专业的发行版。如果你在做渗透方面的工作,将是一个非常好的选择。

发行版的安装包非常大,包含了常见的破解工具,渗透工具,攻击工具。这非常的危险,我曾用它暴力破解了非常多的wifi密码,成功的窥视了邻居的隐私。还是非常好用的。

3. 安装一个清爽的Linux系统

工欲善其事,必先利其器。你可能会想到买一台云主机练练手,但那毕竟要花点银子,我们可以自己安装一个。我们在上面提到,目前使用最广泛的,就是centos。不论你是自建机房,还是使用类似于阿里云这样的云端环境,大多数都会提供centos的安装。

你可能会找到多种安装虚拟机的方式。本小节,将使用虚拟双网卡的方式,准备一个纯洁的环境。这一小节,图片很多。

以阿里云为例,默认第一位就是CentOS,提供了从7.6版本到旧版本的多个镜像。

img

3.1 下载

下面的文章,我们就以CentOS 7稳定版本为基础环境。centos很流行,所以镜像也有很多。国内,我们从上海交大下载,速度应该会快一些。

1
http://ftp.sjtu.edu.cn/centos/7/isos/x86_64/CentOS-7-x86_64-Minimal-1908.iso

如果交大哪天不维护了。可以从这里找:

1
http://centos.mirror.ndchost.com/7/isos/x86_64/CentOS-7-x86_64-Minimal-1908.iso

为了让大家学到更多的知识,我们使用最小化的系统ISO。最小化的iso不到1GB,而预装了很多软件的dvd有4.3GB的大小。我们就用这个减肥版。

3.2 安装Linux

要想快速学习、体验Linux,最便捷的方式,就是使用虚拟机进行安装。目前,最流行的虚拟机,一个是VMware,一个是VirtualBox。在MacOS上还有一个Parallels Desktop

其中,VirtualBox免费而且跨平台,能够满足我们的需求。接下来,将一步步引导你进行安装。

(1) 点击新建,开启安装旅程。

图片

(2) 填写名称,版本。然后点击继续。

img

(3)按照你的机器配置,选择内存

我的机器是8GB内存的,就分配给虚拟机2GB,这个已经足够用了。

图片

(4) 创建一个虚拟磁盘

图片

点击继续后,将弹出一个对话框。我们不用多管,一直点继续,知道对话框消失。这非常的粗暴。

(5) 接下来,点击设置。

图片

(6) 切换到Storage选项,选择我们下载的iso

img

(7) 点击启动,开始安装。

图片

使用方向键切换,使得高亮聚焦在Install CentOS 7上。点击确定,开始安装。

(8) 弹出一个安装界面

接下来的步骤有点多,如果我们没有特别的介绍,那么直接continue就ok了。

图片

(9) 接下来,配置磁盘分区

图片

依然保持默认,并按按钮Done退出。

(10) 配置用户

linux上默认的用户名为root。接下来我们设置root用户的密码为123456。由于这是一个弱密码,所以需要点击两次确定退出。

图片

(11) 等待安装完毕,进行重启

img

(12) 安装成功

图片

3.3 联网

这个时候,我们安装的虚拟机,还不能联网,无法把自己的意念传达出去。由于我们没有对虚拟机进行任何设置,所以使用的是默认的NAT模式。

将光标聚焦到命令行窗口,然后输入命令dhclient。等待几秒钟,执行ping baidu.com测试以下网络,可以看到能够正常访问网络了。

图片

上面黑漆漆的窗口,就是我们现在的Linux界面。有人觉得很丑,就像是在玩dos,但像我这种不可救药的人,却觉得格外的亲切。

接下来的命令,我们不会再截图,而使用高亮的代码块表示。为了不至于让人晕头转向,请先看下图。

img

3.4 外部访问虚拟机

由于NAT模式的特点,我们的虚拟机能够访问外网,但无法被外部发现。酒香竟怕巷子深。为了解决这个问题,我们需要再添加一块网卡。

在做这些更改之前,需要首先关闭虚拟机。可以强制关闭机器,也可以在命令行中输入:

1
shutdown -h now

虚拟机关闭后,再次点击设置,切换到网络适配器选项卡。如图,添加一个新的网络适配器,适配器类型为Host-only Adapter。通过这块网卡,我们的宿主机就能够访问它了。

图片

再次启动虚拟机,执行dhclient命令后,执行ip addr查看主机的ip地址。可以看到,我们现在有两块网卡,两个ip地址。

记录下192打头的网络地址,我们会使用外部的程序,比如XShellSecureCRT等,进行连接。比如,我这里的ip地址是:192.168.99.100。不废话,看图。

图片

小提示:关于虚拟网卡的网段,如果有差异。你可以在全局设置里,改成和我一样的。

img

3.5 远程连接

你可能已经体验到,通过虚拟机自带的命令行界面进行输入,局限性非常大。通过远程连接,可以把终端界面切换到我们熟悉的操作模式,如果能够显示彩色的终端,那再好不过了。下面介绍几个工具,一般的,使用xshell的居多。

Windows

  • XShell 你可能在公司内,见过你的SRE同事,运指如飞,命令字符如流水一般撒过屏幕。即使非常繁杂,难以记忆的密码,也能瞬间输入。他可能用的就是xshell。
  • SecureCRT 比较老的一款产品,使用也较多。
  • MobaXterm MobaXterm就是一单文件纯绿色软件,下载过来exe包直接运行即可,不需要任何的安装过程。

它们都有免费版和专业版之分。无力购买的话,就找找破解版。但是注意,盗版汉化的ssh客户端,有些别有用心的人会在软件中植入木马,窃取你的密码、证书,这种情况已经发生过很多次。

MacOS

对于macos用户来说,简单的很。直接使用iTerm,输入命令行即可。比如使用下面的命令连接我们的机器。

1
ssh root@192.168.99.100

图片

Linux

唔,你都已经是Linux环境了,还折腾个啥虚拟机呢?直接用吧。

推荐使用XShell、SecureCRT、iTerm等工具,通过ssh进行远程连接。对于一些命令拷贝、验证来说,要方便快捷的多。

img

4. 对Linux命令行有个初步了解

万事开头难。面对黑漆漆的Linux窗口,要勇敢的走出第一步。不要怕输错了什么,系统健壮的很。命令行通常会拥有比图形界面更高的效率,更加重要的是它可以做自动化之类的小工具,这使得生产力产生质的飞跃。

现在,你已经安装好了centos,并远程连接上了它。我们拥有了它,但并不能了解它的脾气。接下来,让我们进入Linux命令行的世界。和我签订契约吧,少年。

本小节会使用非常详细的演进方式,来看一下一个命令,是怎样生成和执行的。

4.1、简单尝试

好啦,我们现在就在终端里了。什么叫做终端呢?你在很多黑客电影里,看到的黑漆漆的界面就是,它提供了一个可以输入字符串的交互式界面,至于那些闪光的、扫描机一样的东西,是不存在的。

尝试输入些什么吧。 比如:jdsjf

1
2
[root@localhost ~]# jdsjf
-bash: jdsjf: command not found

图片

我们再次把这张图贴一下。怎么回事?命令的输出翻译成中文,就是“找不到命令”的意思。什么叫命令?就是我们上面随便输入的字符串jdsjf

然后,我们看下提示中其他一些有用的东西。

↓↓↓↓↓↓

bash 代表的是我们所使用的shell,shell可以认为是一个解释器,将我们的输入解释成一系列可执行的指令。现在的linux发行版,最流行的就是bash解释器,几乎每个都预装了它。

命令找不到,证明我们的字符串bash解释不了。但是,Linux上一些目录里的文件,是可以被默认找到的,这些目录的集合,就叫PATH 。PATH还是一个环境变量,我们可以通过命令查看它的尊容。

1
2
[root@localhost ~]# echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin

想要知道系统中有哪些命令,就可以看下上面这些文件夹中,都有哪些文件。文件非常非常之多,但是大部分我们不会接触。所以,xjjdog才会写这么个东西–聚焦那些最常用,最有用的命令,最常用的参数,最有用的场景。

命令输出后,还有一些额外的东西,比如[root@localhost ~],这部分叫做提示符,光标会一直跳动,等待你的输入。这部分是可以定制的,甚至可以定制的十分漂亮。

4.2、Hello World

到现在为止,我们什么都没得到。按照程序员的想法来说,就要实现一个hello world的程序。在终端shell里,这个过程变得简单,远比写一个java程序简单。

1
2
[root@localhost ~]# echo "Hello World"
Hello World

如上所示,echo的意思就是输出一些内容。后面的Hello World,就叫做参数,它们之间以空格分隔,可以接受多个参数。

1
2
[root@localhost ~]# echo "Hello World" , "Fuck 996"
Hello World , Fuck 996

以上命令能够正常运行,证明echo是我们的终端能够认识的一个命令。那到底这个命令是在什么地方呢?可以使用whereis命令进行查找。

1
2
[root@localhost ~]# whereis echo
echo: /usr/bin/echo /usr/share/man/man1/echo.1.gz

命令显示。我们的echo命令全路径,是/usr/bin/echo,由于它处于PATH目录中,所以能够被识别到。

4.3、将命令加入PATH

接下来,我们把上面的命令,做成一个脚本。然后将这个脚本,放到PATH目录中。不过先等等,我们要先给命令起个名字。

首先需要创建一个文件。在Linux上,创建文件使用touch命令。

1
[root@localhost ~]# touch jdsjf

命令执行后,什么都没发生,它只是创建了一个空文件。接下来,我们向其中添加一些内容。

1
[root@localhost ~]# echo "echo 'Hello World'" > jdsjf

注意符号>,它的意思是,将前面的输出,重定向到后面的文件中。执行完上面的命令,jdsjf 中的内容,就变成了echo 'Hello World

接下来,我们尝试着去执行刚才生成的命令。

1
2
[root@localhost ~]# ./jdsjf
-bash: ./jdsjf : Permission denied

我们通过相对路径的方式,来执行刚刚生成的命令。结果,终端显示我们并没有这个命令的执行权限。

其实,Linux在权限控制这一方面,非常的详细。一个文件,有可读、可写、可执行三种属性。如果想要一个文件能够执行,需要给它添加执行权限,这个过程是由命令chmod完成的。

1
2
3
[root@localhost ~]# chmod u+x jdsjf
[root@localhost ~]# ./jdsjf
Hello World

我们将在后面的章节,来详细介绍权限方面的知识。如上所示,命令已经能正常输出,接下来,我们把命令移动到PATH中的一个目录。

1
2
3
[root@localhost ~]# mv jdsjf /usr/local/bin/
[root@localhost ~]# jdsjf
Hello World

不需要加任何的相对路径,现在,只需要输入jdsjf,就可以正常输出一串数字。我们成功的让一个没有任何意义的字符串,表达了它的想法。虽然我们依然是它的主宰。

你可以想一下下面这三个问题:

1、我可以自定义一个目录,比如/root/mybin,把它加入到PATH么?

2、我可以省略上面的touch命令,直接使用重定向生成文件么?

3、除了放到PATH和相对路径,还有没有其他的命令执行方式?

图片

5. Linux漫游方式

想要了解linux的基本使用方法,就要了解一个基本的事实–linux系统中,一切皆文件。

不管是命令,还是文档,甚至设备,目录,套接字,在linux上对它们的操作都是一致对待的。许多开发驱动程序的小伙伴,会发现使用的一些函数,和读写文件的没什么两样(open、close、read、write、ioctl)。今天我们所说的基本操作,针对的就是普通文件和目录,本小节将详细解释相关命令。

img

5.1、当前路径

img

到现在为止,我们还不知道自己在系统的什么地方。在浏览器上,我们能够通过导航栏上的url,了解到自己在互联网上的具体坐标。相似的功能,是由pwd命令提供的,它能够输出当前的工作目录。

pwd命令是非常非常常用的命令,尤其是在一些命令提示符设置不太友好的机器上。另外,它也经常用在shell脚本中,用来判断当前的运行目录是否符合需求。

有很多线上事故,都是由于没有确认当前目录所引起的。比如rm -rf *这种危险的命令。在执行一些高危命令时,随时确认当前目录,是个好的习惯。

1
2
[root@localhost ~]# pwd
/root

我们使用root用户默认登陆后,就停留在/root目录中。Linux中的目录层次,是通过/进行划分的。

5.2、文件系统用户标准

Linux的文件系统,从一开始就有一个规范标准。它还有一个专有缩写名词,叫做FHS (Filesystem Hierarchy Standard)。FHS经过多年的演进,目录结构也越来越清晰。除了一些标准的要求,还有一些使用者之间的约定。

接下来,我们大体看一下linux上的默认目录,对其有一个基本的感觉。

第1层 第二层 介绍
/bin 目录/usr/bin的软链接
/sbin 目录/usr/sbin的软链接
/lib 目录/usr/lib的软链接
/usr /bin 存放一些常用的命令
/usr /sbin 存放一些管理员常用的命令
/usr /lib 用来存放动态库和一些模块文件
/sys 内核中的数据结构的可视化接口
/proc 内存映像
/run 内存映像
/boot 存放引导程序,内核相关文件
/dev 存放一些设备文件,比如光盘
/etc 用于存储一些全局的、应用的配置文件
/var 与/var/run一样,存放的是系统运行时需要的文件,比如mysql的pid等
/tmp 非常特殊的临时文件夹,断电丢失
/home /** 用户目录,比如我的目录是/home/xjjdog
/root root用户的home目录
  • home 平常,我们打交道最多的目录,就集中在自己的用户目录,我们可以在里面做任何操作,比如我们现在root用户的/root目录。一些自己的资料,比如视频、音频、下载的文件,或者做测试用的一些数据资料,就可以自行在这些目录下规划。root用户比较特殊,普通用户的私人目录都是在/home下的。
  • /etc etc目录是经常要打交道的目录,存放了一些全局的系统配置文件和应用配置文件。比如你安装了php,或者nginx,它们的配置文件就躺在/etc目录下的某个文件夹里。
  • /var var目录存放一些运行中的数据,有必须的,也有非必须的。一些黑客入侵之后,会在这里面的某些文件中留下痕迹,他们会着重进行清理。var目录还是一些应用程序的默认数据存放之地,比如mysql的数据文件。
  • /tmp 目录是一个特殊的临时目录,文件在断电以后就消失了。但这个目录,所有的用户,都有写入权限,通常用来做文件交换用。
  • /proc/sys目录,是两个神奇的目录。它们两个是一种伪文件系统,可以通过修改其中一些文件的状态和内容,来控制程序的行为(修改后会直接刷到内存上,太酷了)。刚开始的时候,只有proc目录,由于里面内容有多又乱,后面又规划出sys目录,用来控制内核的一些行为。如果你在调优一些系统参数,和这些文件打交道的时间比较多。
  • 还有几个空的目录,我们没有列在上面的表格上。比如/srv目录,通常会把一些web服务的资料,比如nginx的,放在这里面。但是,这并不是强制要求的,所以我见过的/srv目录,通常会一直是空的。同样的,/opt目录也是这样一个存在,你就当它不存在就行。这都属于使用者规划的范畴,自定义性非常强。
  • 在使用Linux系统的时候,也可以创建自己的目录。比如,我就喜欢自己创建一个叫做/data的目录,用来存放一些数据库相关的内容。举个例子,/data/mysql存放mariadb的数据,而/data/es/存放elasticsearch的索引内容。

linux上的文件类型有很多,它们大部分都分门别类的存放在相应的目录中,比如/dev目录下,就是一些设备文件;/bin文件下,是一些可以执行命令。通常都好记的很。

5.3、查看文件列表

所以,上面的表格内容,我是怎么看到的呢,靠记忆么?ls命令,能够列出相关目录的文件信息。可以被评为linux下最勤劳的命令标兵。

图片

现在的终端,都能够输出彩色的信息,非常的直观。oh-my-zshoh-my-bash等项目,可以让你的终端更加的漂亮。把它加入到你的研究清单里吧。

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
[root@localhost /]# ls /
# 注意:ls可以接受路径参数,你不用先跳转,就可以输出相关信息
bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
[root@localhost /]# ls -l /
# 带上 -l参数,可以查看一些更加详细的信息。
total 20
lrwxrwxrwx. 1 root root 7 Nov 3 20:24 bin -> usr/bin
dr-xr-xr-x. 5 root root 4096 Nov 3 20:34 boot
drwxr-xr-x. 19 root root 3080 Nov 3 21:19 dev
drwxr-xr-x. 74 root root 8192 Nov 3 20:34 etc
drwxr-xr-x. 2 root root 6 Apr 11 2018 home
lrwxrwxrwx. 1 root root 7 Nov 3 20:24 lib -> usr/lib
lrwxrwxrwx. 1 root root 9 Nov 3 20:24 lib64 -> usr/lib64
drwxr-xr-x. 2 root root 6 Apr 11 2018 media
drwxr-xr-x. 2 root root 6 Apr 11 2018 mnt
drwxr-xr-x. 2 root root 6 Apr 11 2018 opt
dr-xr-xr-x. 108 root root 0 Nov 3 21:19 proc
dr-xr-x---. 2 root root 135 Nov 4 07:53 root
drwxr-xr-x. 24 root root 740 Nov 3 21:20 run
lrwxrwxrwx. 1 root root 8 Nov 3 20:24 sbin -> usr/sbin
drwxr-xr-x. 2 root root 6 Apr 11 2018 srv
dr-xr-xr-x. 13 root root 0 Nov 3 21:19 sys
drwxrwxrwt. 9 root root 4096 Nov 4 03:40 tmp
drwxr-xr-x. 13 root root 155 Nov 3 20:24 usr
drwxr-xr-x. 19 root root 267 Nov 3 20:34 var

ls最常用的,就是加参数l或者参数a

5.3.1、详细信息

图片

加上参数l,能够看到文件的一些权限信息已经更新日期等。但我们还看到了一些更有意思的东西。比如:

1
lib -> usr/lib

上面表示的,是软链接信息。

就如同我们上面表格所展示的一样,lib目录,是/usr/lib的快捷方式,它们之中的内容,没有什么两样。

关于ls -l展示的更加详细的内容,可以参照我下面的这张图。我们将在了解后面小节的内容后,再次对这张图进行回顾。

图片

5.3.2 隐藏文件

直接在你的/root目录里,执行ls -al,你会看到更多东西。这些额外的隐藏文件,都是以.开头,以配置文件居多。这就是参数a的作用。

1
2
3
4
5
6
7
8
9
10
11
[root@localhost ~]# ls -al
total 28
dr-xr-x---. 2 root root 135 Nov 4 07:53 .
dr-xr-xr-x. 17 root root 224 Nov 3 20:28 ..
-rw-------. 1 root root 1273 Nov 3 20:28 anaconda-ks.cfg
-rw-------. 1 root root 246 Nov 4 11:41 .bash_history
-rw-r--r--. 1 root root 18 Dec 28 2013 .bash_logout
-rw-r--r--. 1 root root 176 Dec 28 2013 .bash_profile
-rw-r--r--. 1 root root 176 Dec 28 2013 .bashrc
-rw-r--r--. 1 root root 100 Dec 28 2013 .cshrc
-rw-r--r--. 1 root root 129 Dec 28 2013 .tcshrc

细心的同学,应该会注意到两个特殊的目录。...。前者表示的是当前目录,而后者表示的是上层目录。

使用cd命令,将在这些目录中,自由穿梭。

小技巧:如果你对英文日期阅读困难,可以使用ls -al --full-time查看可读的日期。

5.4、切换目录

img

执行cd命令,可以将工作目录切换到目标文件夹。为了展示cd命令的效果。请在root用户下,执行下面的命令,这将创建一个7层的目录。

1
2
cd
mkdir -p a1/b2/c3/d4/e5/f6/{g7,g8,g9,g10}

我们使用cd命令,切换到最后一层。然后,我们使用..切换到上层目录。

1
2
3
4
5
6
7
[root@localhost ~]# cd a1/b2/c3/d4/e5/f6/g7
[root@localhost g7]# pwd
/root/a1/b2/c3/d4/e5/f6/g7

[root@localhost g7]# cd ..
[root@localhost f6]# pwd
/root/a1/b2/c3/d4/e5/f6

所以,切换到上面n层目录,只需使用多层级的../即可。有几个特殊的变量,需要说明一下。

  • ../ 指的是上层目录
  • ../../ 指的是上两层目录
  • ./ 指的是当前目录
  • ~ 指的是当前的用户目录,这是一个缩写符号
  • - 使用它,可以在最近两次的目录中来回切换

我们来使用命令把上面这些特殊变量验证一下。

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
# 跳转到用户根目录
[root@localhost tmp]# cd ~
[root@localhost ~]# pwd
/root

# 进入到第三层目录
[root@localhost ~]# cd a1/b2/c3/
[root@localhost c3]# pwd
/root/a1/b2/c3

# 跳回到前三层目录
[root@localhost c3]# cd ../../..
[root@localhost ~]# pwd
/root

# 跳到上次访问的目录
[root@localhost ~]# cd -
/root/a1/b2/c3
[root@localhost c3]# pwd
/root/a1/b2/c3

# 进入当前目录:等于什么都没干
[root@localhost c3]# cd ./
[root@localhost c3]# pwd
/root/a1/b2/c3

以上就是cd命令的常用用法。现在,我们返回头来看一下mkdir。顾名思义,就是创建目录的意思,但一般在工作中,都会加上-p参数,这样就可以一次性创建多层目录。注意mkdir后面的大括号{},可以一次性的指定多个目录进行创建,这通常能节省很多时间。

5.5、文件操作

图片

使用命令行操作文件,是非常方便的。

  • touch 新建文件
  • cp 复制文件
  • mv 移动文件
  • rm 删除文件

这四个风骚的命令,主宰着文件资料的去向。我们依然使用上面创建的目录,进行接下来的操作。

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
# 创建三个文件
[root@localhost ~]# touch long-long-long.txt
[root@localhost ~]# touch 996.txt
[root@localhost ~]# touch icu.txt
[root@localhost ~]# ls
996.txt a1 anaconda-ks.cfg icu.txt long-long-long.txt

# 复制一个文件
[root@localhost ~]# cp 996.txt 007.txt
[root@localhost ~]# mv long-long-long.txt short.txt
[root@localhost ~]# ls
007.txt 996.txt a1 anaconda-ks.cfg icu.txt short.txt

# 移动996.txt到a1目录,icu.txt到a1/b2目录
# 删除short.txt
[root@localhost ~]# mv 996.txt a1/
[root@localhost ~]# mv icu.txt a1/b2/
[root@localhost ~]# rm short.txt
rm: remove regular empty file ‘short.txt’? y

# 递归删除a1目录
[root@localhost ~]# rm -rvf a1/
removed directory: ‘a1/b2/c3/d4/e5/f6/g7’
removed directory: ‘a1/b2/c3/d4/e5/f6/g8’
removed directory: ‘a1/b2/c3/d4/e5/f6/g9’
removed directory: ‘a1/b2/c3/d4/e5/f6/g10’
removed directory: ‘a1/b2/c3/d4/e5/f6’
removed directory: ‘a1/b2/c3/d4/e5’
removed directory: ‘a1/b2/c3/d4’
removed directory: ‘a1/b2/c3’
removed ‘a1/b2/icu.txt’
removed directory: ‘a1/b2’
removed ‘a1/996.txt’
removed directory: ‘a1/’

[root@localhost ~]# ls
007.txt anaconda-ks.cfg

img

经过一番操作以后,只剩下了007了。除了上面基本的操作,接下来我要介绍一些更加重要的功能。

可以看到在使用rm删除文件的时候,进行了一次提示。这是为了避免误删除一些东西,但有时候,你需要不显示这种提示,就可以加-f参数。f参数对于cp、mv等命令来说,同样适用,它是force的意思。

1
2
3
rm -f file
cp -f file1 file2
mv -f file1 file2

另外,还有一个参数-r,这是递归的意思。我们的目录和文件,通常有多个层次,递归可以把操作全部作用于上面,比如上面的递归删除a1目录。

1
2
# 警告:以下命令会造成严重后果
rm -rf /

上面的这个命令,你一定经常看到。这不是笑话,已经有很多用户因此丢失了数据,这就是传说中的删根,最终你将一无所有。那参数v又是干什么用的呢?加上它之后,可以看到命令详细的执行过程。在平常的操作中,我一般都加上。

图片

6.开始操作文件

图片

你可能已经了解到,ll -l命令的第一列,能够显示linux的文件类型。请对此有一个大体的印象,因为后面的很多命令,会用到这些知识。

  • - 表示普通文件
  • d 表示目录文件
  • l 表示链接文件,比如快捷方式
  • s 套接字文件
  • c 字符设备文件,比如/dev/中的很多文件
  • b 表示块设备文件,比如一些磁盘
  • p 管道文件

Linux上的文件可以没有后缀,而且可以创建一些违背直觉的文件。比如后缀是png,但它却是一个压缩文件(通常不会这么做)。大学时,就有聪明的同学这样藏小电影,效果很好。

查看文件的具体类型,可以使用file命令,它很聪明,能够识别很多文件格式。

1
2
3
4
5
6
7
8
9
10
[root@localhost ~]# file /etc
/etc: directory
[root@localhost ~]# file /etc/group
/etc/group: ASCII text
[root@localhost ~]# file /dev/log
/dev/log: socket
[root@localhost ~]# file /dev/log
/dev/log: socket
[root@localhost ~]# file /bin
/bin: symbolic link to `usr/bin'

本部分的操作,面向的就是ASCII text类型的,普通文本文件。接下来,我们要创建一些文件。然后写入一些内容到文件里,以便进行后续的操作。

6.1、创建一个文件

6.1.1、数字序列

图片

使用重定向符,能够直接生成文件。下面,我要生成10到20的数字,每一个数字单独一行,写入一个叫做spring的文件。巧的很,seq命令可以完成这个过程。

1
seq 10 20 >> spring

我们在前面提到过>的意思,是将前面命令的输出,重定向到其他地方。在这里,我们用了两个>,它依然是重定向的意思,但表示的是,在原来文件的基础上,追加内容。

也就是编程语言里的w+a+的意思。

6.1.2、查看内容

img

为了查看文件的生成效果,可以使用cat命令检测。cat命令将会把文件的内容,输出打印到终端上。如果加上参数n,甚至可以打印行号。效果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
[root@localhost ~]# cat spring
10
11
12
13
14
15
16
17
18
19
20
[root@localhost ~]# cat -n spring
1 10
2 11
3 12
4 13
5 14
6 15
7 16
8 17
9 18
10 19
11 20

除了查看文件内容,cat命令通常用在更多的地方。只有和其他命令联合起来,它才会觉得生活有意义。

1
2
3
4
5
6
7
8
9
10
11
12
13
# 合并a文件和b文件到c文件
cat a b>> c

# 把a文件的内容作为输入,使用管道处理。我们在后面介绍
cat a | cmd

# 写入内容到指定文件。在shell脚本中非常常用。我们在后面会多次用到这种写法
cat > index.html <<EOF
<html>
<head><title></title></head>
<body></body>
</html>
EOF

由于我们的文件不大,cat命令没有什么危害。但假如文件有几个GB,使用cat就危险的多,这只叫做的小命令,会在终端上疯狂的进行输出,你可以通过多次按ctrl+c来终止它。

6.2、平和的查看文件

图片

既然cat命令不适合操作大文件,那一定有替换的方案。less和more就是。由于less的加载速度比more快一些,所以现在一般都使用less。它最主要的用途,是用来分页浏览文件内容,并提供一些快速查找的方式。less是一个交互式的命令,你需要使用一些快捷键来控制它。

这次我们使用seq生成一千万行记录,足足有76MB大小,然后用less打开它。

1
2
3
4
[root@localhost ~]# seq 10000000 > spring
[root@localhost ~]# du -h spring
76M spring
[root@localhost ~]# less spring

关于less,一般操作如下:

  • 空格 向下滚屏翻页
  • b 向上滚屏翻页
  • / 进入查找模式,比如/1111将查找1111字样
  • q 退出less
  • g 到开头
  • G 去结尾
  • j 向下滚动
  • k 向上滚动,这两个按键和vim的作用非常像

6.3、文件头尾

图片

head可以显示文件头,tail可以显示文件尾。它们都可以通过参数-n,来指定相应的行数。

1
2
3
4
5
6
7
8
[root@localhost ~]# head -n 3 spring
1
2
3
[root@localhost ~]# tail -n 3 spring
9999998
9999999
10000000

对于部分程序员来说,tail -f或许是最常用的命令之一。它可以在控制终端,实时监控文件的变化,来看一些滚动日志。比如查看nginx或者tomcat日志等等。通常情况下,日志滚动的过快,依然会造成一些困扰,需要配合grep命令达到过滤效果。

1
2
3
4
5
# 滚动查看系统日志
tail -f /var/log/messages

# 滚动查看包含info字样的日志信息
tail -f /var/log/messages | grep info

对于tail命令来说,还有一个大写的参数F。这个参数,能够监控到重新创建的文件。比如像一些log4j等日志是按天滚动的,tail -f无法监控到这种变化。

6.4、查找文件

img

考虑下面这个场景。我们需要找一个叫做decorator.py的文件,这个文件是个幽灵,可能存在于系统的任何地方。find命令,能够胜任这次捉鬼行动。

我们使用find命令,从根目录找起,由于系统的文件过多,下面的命令可能会花费一段时间。

1
2
[root@localhost site-packages]# find / -name decorator.py  -type f
/usr/lib/python2.7/site-packages/decorator.py

使用time命令,可以看到具体的执行时间。执行还是挺快的么!秒出!

1
2
3
4
5
6
[root@localhost site-packages]# time find / -name decorator.py  -type f
/usr/lib/python2.7/site-packages/decorator.py

real 0m0.228s
user 0m0.098s
sys 0m0.111s

find命令会查出一个路径的集合。通常是查询出来之后,进行额外的处理操作,一般配合xargs命令使用(xargs读取输入,然后逐行处理),至于find的exec参数?忘了它吧,不好用!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 删除当前目录中的所有class文件
find . | grep .class$ | xargs rm -rvf

# 找到/root下一天前访问的文件,type后面的类型参见文章开头
find /root -atime 1 -type f

# 查找10分钟内更新过的文件
find /root -cmin -10

# 找到归属于root用户的文件
find /root -user root

# 找到大于1MB的文件,进行清理
find /root -size +1024k -type f | xargs rm -f

find的参数非常非常多,记不住怎么办?除了常用的,其实都可以通过man命令查看。man的操作也和vi非常的类似,输入/EXAMPLES,会看到很多样例。不过我觉得还是上面列出的这些命令更加的适用。

6.4.1、数据来源

在上图中,你会看到mtime,ctime,atime类似的字样,它们的数据来自于何处呢?接下来我们顺理成章的看一下stat命令。

1
2
3
4
5
6
7
8
9
10
[root@localhost ~]# stat spring
File: ‘spring’
Size: 78888897 Blocks: 154080 IO Block: 4096 regular file
Device: fd00h/64768d Inode: 8409203 Links: 1
Access: (0644/-rw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root)
Context: unconfined_u:object_r:admin_home_t:s0
Access: 2019-11-04 18:01:46.698635718 -0500
Modify: 2019-11-04 17:59:38.823458157 -0500
Change: 2019-11-04 17:59:38.823458157 -0500
Birth: -

这不就是文件属性么?从文件大小,到文件类型,甚至到最后修改、访问时间,都可以从这里获取。Linux文件系统以块为单位存储信息,为了找到某一个文件所在存储空间的位置,会用i节点(inode) 对每个文件进行索引,你可以认为它是一个文件指针。

  • 文件的字节数

  • 文件拥有者user

  • 文件所属组group

  • 文件的读、写、执行权限

  • 文件的时间戳

    • ctime指inode上一次变动的时间
    • mtime指文件内容上一次变动的时间
    • atime指文件上一次打开的时间。
  • 链接数,即有多少文件名指向这个inode (ln命令)

  • 文件数据block的位置(具体的数据位置)

关于inode是一个比较大的话题,也是比较重要的知识点,有兴趣的可以自行搜索。我们只需要知道这些信息是从这里来的就可以了。

6.4.2、小练习

如果我只想获取Modify这个数值,可以组合使用一下上面学到的命令。首先获取最后三行,然后获取首行。效果如下:

1
2
[root@localhost ~]# stat spring | tail -n 3 | head -n 1
Modify: 2019-11-04 17:59:38.823458157 -0500

下面几个命令,效果是与上面等价的,输出结果也是一模一样。正所谓条条大路通罗马,接下来,我们首先介绍一下出现频率较高的grep。另外,我们在上面的这些命令中,多次使用了|,这是Linux中非常重要的管道概念,下面也会着重介绍。

1
2
3
4
stat spring | head -n 7 | tail -n 1
stat spring | grep Modify
stat spring | sed -n '7p'
stat spring | awk 'NR==7'

6.5、字符串匹配

图片

grep用来对内容进行过滤,带上--color参数,可以在支持的终端可以打印彩色,参数n则用来输出具体的行数,用来快速定位。这是一个必须要熟练使用的命令。

比如:查看nginx日志中的POST请求。

1
grep -rn --color POST access.log

推荐每次都使用这样的参数。

如果我想要看某个异常前后相关的内容,就可以使用ABC参数。它们是几个单词的缩写,经常被使用。

  • A after 内容后n行
  • B before 内容前n行
  • C 内容前后n行

就像是这样:

1
2
3
4
5
# 查看Exception关键字的前2行和后10行
grep -rn --color Exception -A10 -B2 error.log

#查找/usr/下所有import关键字,已经它们所在的文件和行数
grep -rn --color import /usr/

图片

6.6、管道

在上面的命令中,我们多次用到了|,这貌似可以完成一些神奇的事情。|pipe的意思,它可以把多个命令联系起来。通常,命令有下面的关联方式:

  • ; 顺序执行,如mkdir a;rmdir a
  • && 条件执行,如mkdir a && rmdir a
  • || 条件执行,如mkdir a || rmdir a,后面的命令将不执行
  • | 管道,前面命令的输出,将作为后面命令的输入

前三种的命令关联,是非常简单有逻辑的,非常的好理解。而管道,却有自己的特点。

接触过编程语言的都知道stdinstdoutstderr的概念。让我们重新组织一下针对于管道的定义:前面命令的输出(stdin),将作为后面命令的输入(stdout)。

我们拿一行命令来说明。

1
seq 20 100 | head -n 50 | tail -n 1

上面命令,将输出69。69是个神奇的数字,它是怎么办到的呢?我们来一张小图,一切就豁然开朗了。

img

关于输入输出和错误,linux使用一个数字进行缩写,这在一些脚本中,甚至在一些安装文件中,会经常用到。

  • 0 表示stdin标准输入
  • 1 表示stdout标准输出
  • 2 表示stderr标准错误

通过类似2>&1的语法,可以把错误信息定向到标准输出。我们用命令来证明一下。

1
2
3
4
5
6
7
8
9
# 错误信息无法输出到文件
[root@localhost ~]# cat aaaaaaaaa > b
cat: aaaaaaaaa: No such file or directory
[root@localhost ~]# cat b

# 错误信息被重定向了
[root@localhost ~]# cat aaaaaaaaa > b 2>&1
[root@localhost ~]# cat b
cat: aaaaaaaaa: No such file or directory

6.7、排序

图片

在了解管道的工作原理之后,就可以介绍一下sort命令了。它通常可以和uniq(去重)命令联合,完成一些排序、去重的操作。首先使用cat命令,生成如下内容的文件。

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
cat > sort.txt <<EOF
1 11
3 22
2 44
4 33
5 55
6 66
6 66
EOF
​```bash
接下来让这两个命令上台表演一下。sort可以使用-t指定分隔符,使用-k指定要排序的列。但是空格,是不需要做这些画蛇添足的指定的。
​```bash
# 根据第一列倒序排序
[root@localhost ~]# cat sort.txt | sort -n -k1 -r
6 66
6 66
5 55
4 33
3 22
2 44
1 11

# 统计每一行出现的次数,并根据出现次数倒序排序
# 此时,行数由7变成了6
[root@localhost ~]# cat sort.txt | sort | uniq -c | sort -n -k1 -r
2 6 66
1 5 55
1 4 33
1 3 22
1 2 44
1 1 11

注意:uniq命令,一般用在已经经过排序的结果集上。所以,很多情况需要首先使用sort命令进行排序后,再使用uniq命令。新手经常会忘记第一步,造成命令不能正常运行。

6.8、小练习

本部分,我们从文件的属性开始说起,了解了几个对文件操作的常用命令。并顺便介绍了管道的概念。下面,我们来练习一下。

找到系统中所有的grub.cfg文件,并输出它的行数。

分析:首先需要使用find命令,找到这些文件。然后使用xargs逐行处理。最后,使用wc命令,统计确切的行数。

1
2
[root@localhost grub2]# find / | grep grub.cfg | xargs wc -l
141 /boot/grub2/grub.cfg

输出系统的group列表

1
cat /etc/group | awk -F ':' '{print $1}'

下面这个命令输出nginx日志的ip和每个ip的pv,pv最高的前10

1
2
3
# 2019-06-26T10:01:57+08:00|nginx001.server.ops.pro.dc|100.116.222.80|1x.xx.150.232:41021|0.014|0.011|0.000|200|200|273|-|/visit|sign=91CD1988CE8B313B8A0454A4BBE930DF|-|-|http|POST|112.4.238.213

awk -F"|" '{print $3}' access.log | sort | uniq -c | sort -nk1 -r | head -n10

6.9、思考&扩展

1、Linux的终端,是如何实现彩色的文字的?我要如何输出一个绿色的Hello World?

2、软链接与硬链接有什么区别?

3、了解几个偏门但又不是非常偏的命令。

  • cut 有了awk,几乎不怎么会用cut了
  • tr
  • col
  • paste
  • join
  • split

7. 正则和高级用法

你可能遇到一些棘手的问题,通过搜索得到想要的结果,但下次还是要通过搜索解决问题,这种低效的手段不是我们所想要的。典型的就是一个线上运维工程师,当问题来临时,不会给你留太多的现场学习时间。

为了达到更高效的训练,我们要做两件事情:第一,总结归纳;第二,触类旁通。Linux的命令也是如此,一个问题,通常会有多种解决方式,要通过变化找出其中的共性。

这涉及到一些设计者对于规范约定俗成的遵守。一般的,你只需要掌握一小部分命令,然后对大批命令达到了解的程度,就可以在命令行的世界里游刃有余。举个例子,你知道ls是列出文件目录,你就会联想到lscpu是列出cpu信息;lsmem是列出内存信息;lsblk是磁盘信息等。这种共性很多,比如top系列,stat系列。

图片

7.1、辅助信息

7.1.1、Linux文件格式

在Linux上工作,是非常非常排斥二进制这种格式的,几乎什么都是可以读写的文本内容。大多数命令生成的结果,也都是文本文件。这些文件有一些特点,通常列与列都是通过空格或者<TAB>键分隔的。比如下面lsmem的结果,这种有规律的,有章可循的文件,是非常容易被处理的。

1
2
3
4
5
6
7
8
9
10
11
12
13
[root@localhost ~]# lsmem  
RANGE SIZE STATE REMOVABLE BLOCK
0x0000000000000000-0x0000000007ffffff 128M online no 0
0x0000000008000000-0x000000000fffffff 128M online yes 1
0x0000000010000000-0x0000000017ffffff 128M online no 2
0x0000000018000000-0x0000000027ffffff 256M online yes 3-4
0x0000000028000000-0x000000004fffffff 640M online no 5-9
0x0000000050000000-0x000000005fffffff 256M online yes 10-11
0x0000000060000000-0x000000007fffffff 512M online no 12-15

Memory block size: 128M
Total online memory: 2G
Total offline memory: 0B

有一大批针对于行操作的命令,同样有一批针对于列操作的命令。然后,有两个集大成者,叫做sedawk。由于这两个命令的内容非常多,我们将其列为单独的章节。

7.1.2、命令记不住怎么办?

通常linux命令都十分简单,但是有些还是有些复杂度的。比如findps这种命令,如果要照顾到所有的场合,可能需要非常巨大的篇幅。但是,万一用到这种偏门的场合怎么办?

全面了解一下是非常有必要的,以便在使用的时候能够唤起记忆中最浅显的印象。然后剩下的,就可以交给类似于man的这种命令了。Linux上的每一个命令,都会有配套的帮助文件,这远比网络上那些转来转去的信息,正确的多。

正式介绍一下下面的两个命令:

  • man 用来显示某个命令的文档信息。比如:man ls
  • info 你可以认为和man是一样的,虽然有一些能够互补的内容。它们会在内容中进行提示的
  • --help 很多命令通过参数--help提供非常简短的帮助信息。这通常是最有用最快捷的用例展示。如果你根本就记不住一个非常拗口的单词,那就找找这些地方吧

注意:这些帮助信息,仅集中在命令的作用域本身。对于它的组合使用场景,并没有过多信息。也就是说,它教会了你怎么用,但并没有告诉你用它能够来做什么。

这些帮助命令,一般会通过高亮关键字,增加阅读的体验。但我们可以更近一步,把帮助文件变成彩色的。在root用户下,执行下面的命令。然后,重新登录虚拟机

1
2
3
4
5
6
7
8
9
10
11
12
13
14
cat >> ~/.bashrc <<EOF
function man()
{
env \\
LESS_TERMCAP_mb=\$(printf "\e[1;31m") \\
LESS_TERMCAP_md=\$(printf "\e[1;31m") \\
LESS_TERMCAP_me=\$(printf "\e[0m") \\
LESS_TERMCAP_se=\$(printf "\e[0m") \\
LESS_TERMCAP_so=\$(printf "\e[1;44;33m") \\
LESS_TERMCAP_ue=\$(printf "\e[0m") \\
LESS_TERMCAP_us=\$(printf "\e[1;32m") \\
man "\$@"
}
EOF

再次执行man命令,就可以看到彩色的信息了。

图片

7.1.3、TAB补全

现在,在终端里,输入ca,然后快速按2次<TAB>键盘,命令行会进入补全模式,显示以ca打头的所有命令。

1
2
3
[root@localhost ~]# ca
cacertdir_rehash cache_dump cache_repair cache_writeback ca-legacy capsh case catchsegv
cache_check cache_metadata_size cache_restore cal caller captoinfo cat catman

如果你对某个命令,只有模糊的印象,只记得前面的几个字母,这个功能是极好的,命令范围会一步步缩减。

7.2、正则表达式

为了开始下面的内容,我们首先介绍一下正则表达式。在前面的一些命令中,也可以使用这些正则表达式,比如less、grep等。

有些书籍,能够把正则表达式写成一本书,我们这里仅作简单的介绍,但足够用了。一般的,正则表达式能用在匹配上,还能够把匹配的内容拿来做二次利用。关于后者,我们在sed命令中介绍。

标志 意义
^ 行首
$ 行尾
. 任意单个字符
* 匹配0个或者多个前面的字符
+ 1个或者多个匹配
? 0个或者1个匹配
{m} 前面的匹配重复m次
{m,n} 前面的匹配重复m到n次
[] 匹配一个指定范围内的字符
[^] 匹配指定范围外的任意单个字符
\ 转义字符
[0-9] 匹配括号中的任何一个字符,or的作用
` `
\b 匹配一个单词。比如\blucky\b 只匹配单词lucky

使用下面的命令创建一个文件,我们练习一下grep命令加上E参数后的正则表现。

1
2
3
4
5
6
cat > 996 <<EOF
996: 996 is a funcking thing . which make woman as man , man as ass .
we all on the bus , bus bus on the way . 996
way to icu. icuuuuuu......
The greedy green boss rides on the pity programmer
EOF

在终端执行下面命令,注意高亮的部分即为匹配到的字符串。

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
# 匹配996开头的行
[root@localhost ~]# cat 996 | grep -E ^996
996: 996 is a funcking thing . which make woman as man , man as ass .

# 匹配996结尾的行
[root@localhost ~]# cat 996 | grep -E 996$
we all on the bus , bus bus on the way . 996

# 匹配到icu和icuuuuuu
[root@localhost ~]# cat 996 | grep -E icu+
way to icu. icuuuuuu......

# 再次匹配到996
[root@localhost ~]# cat 996 | grep -E [0-9]
996: 996 is a funcking thing . which make woman as man , man as ass .
we all on the bus , bus bus on the way . 996

[root@localhost ~]# cat 996 | grep -E ^[\^0-9]
we all on the bus , bus bus on the way . 996
way to icu. icuuuuuu......
The greedy green boss rides on the pity programmer

# 匹配所有不包含996的行,良心命令,泪奔
[root@localhost ~]# cat 996 | grep -E -v [0-9]{3}
way to icu. icuuuuuu......
The greedy green boss rides on the pity programmer

# 匹配boss和icu
[root@localhost ~]# cat 996 | grep -E boss\|icu
way to icu. icuuuuuu......
The greedy green boss rides on the pity programmer

# 匹配所有行
[root@localhost ~]# cat 996 | grep -E .
996: 996 is a funcking thing . which make woman as man , man as ass .
we all on the bus , bus bus on the way . 996
way to icu. icuuuuuu......
The greedy green boss rides on the pity programmer

正则表达式非常的重要,在一些sed脚本中,awk脚本中,甚至是vim编辑器中,都会简化你的操作。以上内容应该熟记,达到不需要查找文档的地步。

下面有6个小问题,可以思考一下。

1、回过头去,执行一下man cat,是否发现了一个叫做tac的命令?它是干什么的?

2、上面提到的stat系列,你能想象iostat大体是干什么用的么?

3、grep -v是什么意思?

4、了解一下和mv非常像的rename命令来批量修改文件,看能否使用上面的正则。

5、有些命令如果拼写错误,如何快速修正?靠搜索么?了解一下fuck命令。我没有说错。

6、下面哪种写法表示如果cmd1成功执行,则执行cmd2命令?

  • A. cmd1&&cmd2
  • B. cmd1|cmd2
  • C. cmd1;cmd2
  • D. cmd1||cmd2

8. Linux下的压缩

压缩,是一件非常神奇的事情。

很久很久之前,就接触过一些64KB大小的电影,你花半小时都看不完。事实上,这些动画的真实容量是15GB,Warez组织把它压缩了25万倍

你要是Windows系统,可以在这里下载体验一下。但我们现在讲的是Linux,很打脸是不是?

1
2
链接: https://pan.baidu.com/s/12YJQ4jsbtRr7RxoLpARTyQ 
提取码: r7sp

压缩是件神奇的事。它能大能小,能伸能缩,在现实中很难找到这样的东西。

图片

为了减小传输文件的大小,或者为了传输方便,一般都会开启压缩。linux下常见的压缩文件有tar、bzip2、zip、rar等,7z这种用的相对较少。压缩之后的文件,非常适合在网络环境上传输。甚至,你可以认为iso文件为一种特殊的压缩方式。

.tar 使用tar命令压缩或解压.bz2 使用bzip2命令操作.gz 使用gzip命令操作.zip 使用unzip命令解压.rar 使用unrar命令解压.Z 使用compress,uncompress

img

准备工作:使用下面的命令,复制1000个文件。

1
2
3
4
cd ~
mkdir files
cd files
seq 1000 | xargs -I {} cp /etc/group {}

使用ls,就可以看到我们刚才创建的1000个文件。接下来,我们使用压缩命令将它打包成一个。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 查看1000个文件的总大小
[root@localhost files]# du -h .
4.0M .

# 切换到root目录
cd ~

# 使用tar进行压缩,压缩后的文件不到1MB
[root@localhost ~]# tar cvf files.tar files

[root@localhost ~]# du -h files.tar
1012K files.tar

# 使用gizp提高压缩比,压缩后的文件只有12KB
[root@localhost ~]# gzip files.tar
[root@localhost ~]# du -h files.tar.gz
12K files.tar.gz

tar和gzip一般是联合使用的。tar命令提供了一种特殊的功能,就是可以在打包解包的同时调用其他的压缩程序,比如:gzip,bzip2等。

下面的命令,与上面执行两次命令后是等同的。所以,一般使用下面的方式进行操作。

1
2
3
[root@localhost ~]# tar cvfz files2.tar.gz files
[root@localhost ~]# du -h files2.tar.gz
12K files2.tar.gz

与之对应的,就是解压操作。我们只需要改动命令行中的一个字母即可:c->x。但其实,参数vz也是可以省略的。

1
[root@localhost ~]# tar xvfz files2.tar.gz

我们更加常用的方式,是加上参数C,指定一个要解压的目录。比如下面的命令,把压缩内容解压到/opt目录中。

1
[root@localhost ~]# tar xvfz files2.tar.gz -C /opt/

那如果我仅仅想要看下压缩文件中包含哪些文件呢?这就要使用参数t

  • c 压缩
  • x 解压
  • t 查看列表

安装其他的

我们来看一下常用的zip和rar解压程序有没有安装。

1
2
3
4
[root@localhost ~]# which unzip
/usr/bin/which: no unzip in (/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin)
[root@localhost ~]# which unrar
/usr/bin/which: no unrar in (/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin)

所以,我们的系统并没有安装这两个应用。那我就使用centos的包管理工具yum安装一下。java中的jar命令也是与zip类似的,可自行探索。

1
2
3
4
5
6
7
[root@localhost ~]# yum install -y zip unzip rar unrar
Loaded plugins: fastestmirror
Loading mirror speeds from cached hostfile
* base: mirrors.aliyun.com
* extras: mirrors.tuna.tsinghua.edu.cn
* updates: mirrors.aliyun.com
...

rar不能安装成功,所以rar文件并不能被解压。没关系,我们在后面的章节把它安装上。

现在,你会在Linux安装tomcat了么?

接下来,我们思考一下:

1、 经过zip压缩的文件,再使用gzip压缩,容量还会减少么?

为了验证这个过程,可以使用dd命令,生成一个69MB大小的随机文件。dd命令也是个神奇哦。

1
2
3
4
5
6
7
8
[root@localhost ~]# dd if=/dev/urandom  of=test bs=1M count=69
69+0 records in
69+0 records out
72351744 bytes (72 MB) copied, 0.446161 s, 162 MB/s


[root@localhost ~]# du -h test
69M test

所以,回到文章最上面,我们可以随机生成一批文件,让压缩效果更有意义一点。

1
seq 1000 | xargs -i dd if=/dev/zero of={}.xjj bs=1k count=256

2、如果已经有了文件,tar命令是如何做到强制覆盖的?

9. Linux的权限体系

img

我们在最最最上面,刚接触命令行的时候,就使用chmod命令,给普通文本文件,赋予了执行权限。本小节将看一下用户权限文件权限这两个息息相关的概念,

9.1、添加用户

到现在为止,我们的系统中,还孤零零的只有这一个用户,是时候学学女娲,捏几个小泥人了。

首先创建两个用户:张三(zhang3)、李四(li4)。

1
[root@localhost ~]# useradd zhang3

查看下面命令的三个输出结果。

1
2
3
4
5
6
7
8
9
10
11
12
# 系统中多了一个叫做zhang3的组,group文件保存了系统的组信息
[root@localhost ~]# tail -n1 /etc/group
zhang3:x:1000:

# 系统中多了一个叫做zhang3的用户,shadow文件保存了它们的密码。很多安全渗透就是为了拿到它进行暴力破解
[root@localhost ~]# tail -n1 /etc/shadow
zhang3:!!:18207:0:99999:7:::

# home目录中,多了一个叫做zhang3的目录
[root@localhost ~]# ll /home --full-time
total 0
drwx------. 2 zhang3 zhang3 83 2019-11-06 22:09:33.357165082 -0500 zhang3

接下来,给我们刚刚建立的用户,使用passwd设置一个密码。密码需要输入两次进行确认。如果想要更改密码,可以使用chpasswd命令。

1
2
3
4
5
6
[root@localhost ~]# passwd zhang3
Changing password for user zhang3.
New password:
BAD PASSWORD: The password is shorter than 8 characters
Retype new password:
passwd: all authentication tokens updated successfully.

那么如何删除一个现有的用户呢?这是通过userdel命令实现的。加上参数f,会在其他用户使用系统的时候,强制退出。

1
userdel -f zhang3

9.2、文件权限说明

从上面的命令执行结果中,我们发现了有两件非常有意思的东西。添加用户后,除了在密码文件shadow中增加了一些内容,同时还在group文件中添加了信息。这涉及到用户的两个属性:用户名,组名。

一个用户只有一个名称代号,但是可以有多个组。下面命令创建一个密码为123的用户li4,并给它追加一个叫做zhang3的组。可以看到/etc/group文件中的信息变更。

1
2
3
4
[root@localhost ~]# useradd  -G zhang3 -p 123 li4
[root@localhost ~]# tail -n 2 /etc/group
zhang3:x:1000:li4
li4:x:1001:

好啦,接下来切换到我们的文件权限上面。为了进行下面命令的验证,我们首先创建一个名字叫confirm777.sh的脚本文件。为了让脚本对所有用户可见,我们把它创建在/tmp目录下。

1
2
3
4
cat > /tmp/confirm777.sh <<EOF
echo $USER
id
EOF

使用ll命令查看文件信息。

1
2
[root@localhost ~]# ll /tmp/confirm777.sh --full-time
-rw-r--r--. 1 root root 13 2019-11-07 04:25:55.418254935 -0500 confirm777.sh

图片

ll的命令可以看出,文件的所有者是root用户,文件所属的组,也是root组,它的权限是rw-r--r--。文件权限分为三部分。

  • 所有者权限,缩写为u。文件的所有者所拥有的权限。也就是root用户的权限,是rw-
  • 组用户权限,缩写为g。文件所属组内所有用户的权限。因为root组内只有root一个用户,所以组用户权限是r--
  • 其他用户权限,缩写为o。其他不相关用户的权限,比如我们刚创建的zhang3、li4用户,对文件的权限就是r--
  • 全部,缩写为a,表示对上面三类用户集体操作。

那rw-这些东西是什么意思呢?

  • r 表示可读权限。read。
  • w 表示可写权限。write。
  • x 表示可执行权限。execute。
  • - 权限占位符,表示没有当前权限。

注意:一个用户拥有文件的w权限,并不代表就可以删除文件。w仅仅针对于文件内容来说的。

一个文件,有3类用户,每类用户,有3种权限。使用最简单的小学乘法,我们能够得出,一个文件的权限位,需要3x3=9个标志位表示。

我们的文件名称,叫做confirm777.sh,这个名字是随便起的么?当然不是,777在linux代表特殊的含义,它代表文件对所有用户具有可读、可写、可执行的权限。可以想象,如果每个文件都有这样的权限,系统将无安全可言。那这一串数字是怎么来的呢?可以看下面的对照表。

  • r 4
  • w 2
  • x 1 执行

对以上三个属性进行任意组合,可以得到:

  • 4 r-- 4+0+0
  • 6 rw- 4+2+0
  • 5 r-x 4+0+1
  • 2 -w- 0+2+0
  • 3 -wx 0+2+1
  • 1 --x 0+0+1
  • 7 rwx 4+2+1

9.3、文件权限更改

下面介绍三个文件权限相关的命令。一般常用的,就是chown和chmod。

chown 更改文件的所有者。chgrp 更改文件的组。chmod 更改文件权限。

接下来,我们把confirm777.sh的所有者和组,修改成刚刚创建的用户zhang3

1
2
3
4
cd /tmp
[root@localhost tmp]# chown zhang3:zhang3 confirm777.sh
[root@localhost tmp]# ll confirm777.sh
-rw-r--r--. 1 zhang3 zhang3 13 Nov 7 04:25 confirm777.sh

给文件所有者增加执行权限。然后分别切换到zhang3li4用户执行一下。

通过su 命令,可以切换到其他用户,一般使用su -进行环境变量的清理;而命令id,能够看到当前正在执行的用户信息。

1
2
3
4
5
6
7
8
9
10
11
[root@localhost tmp]# chmod u+x confirm777.sh
[root@localhost tmp]# su li4
[li4@localhost tmp]$ ./confirm777.sh
bash: ./confirm777.sh: Permission denied
[li4@localhost tmp]$ exit
exit

[root@localhost tmp]# su zhang3
[zhang3@localhost tmp]$ ./confirm777.sh
root
uid=1000(zhang3) gid=1000(zhang3) groups=1000(zhang3) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023

可以看到,文件所有者zhang3可以执行文件,但不相关的li4,提示没有权限。接下来,我们验证用户组相关的权限位。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 去掉zhang3的执行权限
root@localhost tmp]# chmod u-x confirm777.sh
[root@localhost tmp]# ll confirm777.sh
-rw-r--r--. 1 zhang3 zhang3 13 Nov 7 04:25 confirm777.sh

# 增加zhang3组的执行权限,由于li4在zhang3组里,它拥有权限
[root@localhost tmp]# chmod g+x confirm777.sh
[root@localhost tmp]# ll confirm777.sh
-rw-r-xr--. 1 zhang3 zhang3 13 Nov 7 04:25 confirm777.sh

# 切换到zhang3进行执行
[root@localhost tmp]# su - zhang3
[zhang3@localhost tmp]$ ./confirm777.sh
bash: ./confirm777.sh: Permission denied
[zhang3@localhost tmp]$ exit
exit

# 切换到li4进行执行
[root@localhost tmp]# su - li4
[li4@localhost tmp]$ ./confirm777.sh
root
uid=1001(li4) gid=1001(li4) groups=1001(li4),1000(zhang3) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023

从命令的执行结果可以看出。这次,li4能够执行文件,相反的,zhang3却不能。

我们使用chmod命令来修改文件权限,使用的是类似于a+x这样的英文字母。拿第一个脚本来说,初始的权限是rw-r--r--,也就是644,在这种情况下,下面的两个脚本等效。

1
2
chmod u+x confirm777.sh
chmod 744 confirm777.sh

可以看到,第二个命令,使用的是数字样式的权限位,多了一步人脑转换过程。这在日常的使用中,是非常不方便的。所以,使用符号法的表示方式,能够更加直观,非常推荐。

为了更直观的表现这个过程,我专门制作了一张图。

图片

9.4、目录权限

这里有一个非常有意思的地方。把文件设置成可执行,可以把普通文件变成脚本,目录文件的可执行权限是什么鬼?有什么意义?对文件夹来说:

  • r 表示允许读取目录中的文件名,但不能进入该目录
  • w 表示允许用户修改目录,可以创建、迁移、删除、更名目录下的文件
  • x 可以获得目录下文件的列表,以及进入目录,执行cd命令

关于r和x的区别,可以看下面的命令结果,仔细感受一下它们的区别。一般的,几乎所有的目录,都拥有执行权限,不要随意对其进行设置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[root@localhost tmp]# su - li4
[li4@localhost ~]$ cd /tmp

[li4@localhost tmp]$ mkdir nox
[li4@localhost tmp]$ touch nox/{a,b,c,d}
[li4@localhost tmp]$ chmod a-x nox
[li4@localhost tmp]$ ls nox
ls: cannot access nox/a: Permission denied
ls: cannot access nox/b: Permission denied
ls: cannot access nox/c: Permission denied
ls: cannot access nox/d: Permission denied
a b c d
[li4@localhost tmp]$ cat nox/a
cat: nox/a: Permission denied

[li4@localhost tmp]$ chmod a+x nox
[li4@localhost tmp]$ chmod a-r nox
[li4@localhost tmp]$ ls nox
ls: cannot open directory nox: Permission denied

9.5、sticky bit

接下来,我们介绍一个比较烧脑的粘贴位。

假如你要删除一个文件,你可以没有这个文件的写权限,但是你必须要拥有这个文件上级目录的写权限。如何创建一个目录,可以让任何人些人文件,但是又不能删除其他用户的文件?这就是stick bit的作用。粘贴位一般只用于目录上,对文件来说并没有什么用处。粘贴位一般使用t表示。

我们可以看一个典型的目录/tmp

1
2
[root@localhost tmp]#  ls -dl /tmp
drwxrwxrwt. 9 root root 4096 Nov 7 06:27 /tmp

图片

可以看到,最后一位,显示的是t,而不是x,意思是普通用户不能删除其他用户的文件。所有用户在/tmp目录中,都可以随意创建文件,但是却删除不了其他人的文件,即使文件的权限是777

1
2
3
4
5
6
[root@localhost tmp]# touch /tmp/stick
[root@localhost tmp]# chown li4:li4 /tmp/stick
[root@localhost tmp]# chmod 777 /tmp/stick
[root@localhost tmp]# su - zhang3
[zhang3@localhost ~]$ rm /tmp/stick
rm: cannot remove ‘/tmp/stick’: Operation not permitted

我们在上面创建了两个用户zhang3和li4,并拿它们测试了chown和chmod命令,最后介绍了粘贴位。linux比较安全的原因,就是因为有比较详尽的权限划分。但权限是枚双刃剑,超权用户一个命令就可以搞垮系统,许多隐藏的木马,通过提权运行在不为人知的地方。

权限相关的几个命令会经常被使用,下面举几个例子。

1
2
3
4
5
6
7
8
9
10
11
# 设置/var/lib/mysql的用户和组为mysql
chown -R mysql:mysql /var/lib/mysql

# 设置目录可读可写,能够上传文件
chmod 777 /var/www/uploads

# 增加本目录下所有sh的执行权限
chomd a+x *.sh

# 变更file为可读可写可执行
chmod u=rwx,g=rwx,o=rwx file

下面依然是思考时间:

1、下面这个命令,执行以后,会发生什么情况?警告:不要执行,哪怕把000改成其他数字。

1
2
# R遍历子目录的意思
chmod -R 000 /

2、有一天,我看到一个命令chmod u+s file,文中并没有介绍s是什么意思,这是什么意思?

3、如何删除一个用户的组?

10. 如何对磁盘进行操作?

下面的场景非常的恐怖,对有些程序员来说可以是一场噩梦。

一大早刚刚去上班,煎饼果子刚啃了一半,几个全副武装的警察就闯进了公司。二话不说控制住了工作人员,并守株待兔的等着鱼儿来上班。

原因就是:公司涉嫌存储和扩散非法文件,需要查封所有的服务器进行彻查。

这些文件,有的简单的放在磁盘上,有的放在文件存储服务器上,有的,被切成了多片放在了不同的廉价机器上。

接下来会发生什么,要看技术人员的水平,但估计结果并不会太好。

图片

在上一小节,我们创建了两个普通用户,这两个用户没什么本事,和默认的用户root比起来,它们的权限就小得多。除了自己目录下的文件,其他的,它几乎都没有权限去修改。

这些文件,肯定是要存在磁盘上的。对磁盘的管理,有非常多的命令,这一小节的内容,对于系统管理员来说,经常使用;但对于开发来说,就要求比较低一些。因为开发只需要知道小电影存在什么地方了,不需要知道小电影是怎么存的。

那定罪的时候,运维和程序员,到底是谁的锅更大一些?其实是个悖论。运维人员在发呆的时候,脑子里回忆起了下面的知识。

10.1.添加新硬盘

你要是一个系统管理员,甚至是一个上了云的系统管理员,现在买了一块aws的扩展盘,它是不能被使用的。需要经过格式化挂载以后,才能投入生产。

还记得在安装系统的时候么?其中有一步,需要对虚拟机的磁盘,进行划分,我们直接采用默认的方式。不过现在已经改不了了,它已经是过去式了。

为了模拟对磁盘的管理,我们需要首先给虚拟机新加一块虚拟磁盘。首先,使用shutdown -h now命令关闭机器,进行下面的操作。

1、 进入settings选项,然后切换到storage,添加磁盘

图片

2、 点击创建一块磁盘

图片

3、 选择VDI

图片

4、 动态扩容,用多少扩多少

图片

5、 我们创建一块2GB大的,叫做disk2的磁盘

图片

启动机器。远程连接192的ip,别忘了执行dhclient命令。

首先使用fdisk看一下目前的磁盘状况。

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
root@localhost ~]# fdisk -l

Disk /dev/sda: 8589 MB, 8589934592 bytes, 16777216 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk label type: dos
Disk identifier: 0x000c2410

Device Boot Start End Blocks Id System
/dev/sda1 * 2048 2099199 1048576 83 Linux
/dev/sda2 2099200 16777215 7339008 8e Linux LVM

Disk /dev/sdb: 2147 MB, 2147483648 bytes, 4194304 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes


Disk /dev/mapper/centos-root: 6652 MB, 6652166144 bytes, 12992512 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes


Disk /dev/mapper/centos-swap: 859 MB, 859832320 bytes, 1679360 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes

从命令的结果,我们看到了有两块磁盘。/dev/sda/dev/sdb,其中sda已经被分配,已经被我们的文件系统所占用。现在,回忆一下/dev目录的用途,在这个目录下,存放了一些设备文件,假如我们再添加一块磁盘,它的句柄就应该是sdc (sd*)。

在这整块磁盘能够被使用之前,我们需要对它进行三次操作。

  1. 磁盘分区
  2. 磁盘格式化
  3. 磁盘挂载

10.2.分区

对磁盘分区依然是fdisk命令,以下命令,将进入交互模式。在交互模式中,输入n新建分区。由于我们的磁盘只有2GB,所以只创建一个分区就好。根据向导,一路确定向下,最后,输入w确定写入分区表,同时退出命令交互。

再次执行fdisk -l,可以看到已经多了一块2gb大小的分区。

1
2
3
4
5
6
7
[root@localhost ~]# fdisk /dev/sdb
...
[root@localhost ~]# fdisk -l
...
Device Boot Start End Blocks Id System
/dev/sdb1 2048 4194303 2096128 83 Linux
...

10.3.格式化

在命令行,输入mkfs,然后按<TAB>进行补全,将会显示一批命令。

1
2
[root@localhost ~]# mkfs.
mkfs.btrfs mkfs.cramfs mkfs.ext2 mkfs.ext3 mkfs.ext4 mkfs.minix mkfs.xfs

这批命令,都可以对磁盘进行格式化。目前,最常用的磁盘格式是ext4。但我们并没有找到windows操作系统的FAT以及NTFS等格式,但它们在概念上是等同的。

图片

下面介绍一下Linux下常用的磁盘格式。

  • btrfs GPL授权。是为了替换ext系统而发起的。不熟悉,不过多评价。
  • cramfs 门针对闪存设计的只读压缩的文件系统,其容量上限为256M,采用zlib压缩,很少用
  • ext2 ext的早先版本。
  • ext3 ext2的改进。
  • ext4 使用最多。如果对其他的不熟悉,老老实实用ext4吧。
  • minix 比较古老,也不常用。
  • xfs XFS 文件系统是扩展文件系统的一个扩展,是 64 位高性能日志文件系统。centos7.0开始的默认文件系统。

我们就录乡随俗,将磁盘给格式化成xfs。

1
[root@localhost ~]# mkfs.xfs /dev/sdb1

注意:如果想要把磁盘格式化成fat32的格式,需要安装一个软件。

1
yum install dosfstools -y

10.4.挂载

最后一步,是使用mount命令挂载磁盘。我们把它挂载到/data目录。

df命令能够看到系统的磁盘使用状况,参数hhuman的意思,以比较容易读的方式展现信息;lsblk则以另一个角度查看系统磁盘挂载情况。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
[root@localhost ~]# mkdir /data
[root@localhost ~]# mount /dev/sdb1 /data
[root@localhost ~]# df -h
Filesystem Size Used Avail Use% Mounted on
devtmpfs 908M 0 908M 0% /dev
tmpfs 920M 0 920M 0% /dev/shm
tmpfs 920M 8.6M 911M 1% /run
tmpfs 920M 0 920M 0% /sys/fs/cgroup
/dev/mapper/centos-root 6.2G 1.4G 4.9G 22% /
/dev/sda1 1014M 149M 866M 15% /boot
tmpfs 184M 0 184M 0% /run/user/0
/dev/sdb1 2.0G 33M 2.0G 2% /data

root@localhost ~]# lsblk -f
NAME FSTYPE LABEL UUID MOUNTPOINT
sda
├─sda1 xfs ac3a3ce8-6ab1-4c0b-91c8-b4e617f0dfb4 /boot
└─sda2 LVM2_member 3GzmOd-TUc1-p2ZN-wT5q-ttky-to9l-PF495o
├─centos-root xfs 9f86f663-060a-4450-90f9-3005ad9c5d92 /
└─centos-swap swap cf8709b0-b0ab-4d44-a23e-ad76f85efad6 [SWAP]
sdb
└─sdb1 xfs 0a7c861c-1a31-45b3-bf37-c72229f35704 /data

为了能够在开机的时候加载磁盘,我们需要修改/etc/fstab文件。这种文件修改的时候一定要小心,否则会造成系统无法启动。

1
2
3
4
5
6
[root@localhost ~]# echo "/dev/sdb1  xfs defaults 0 0" >> /etc/fstab
[root@localhost ~]# cat /etc/fstab
/dev/mapper/centos-root / xfs defaults 0 0
UUID=ac3a3ce8-6ab1-4c0b-91c8-b4e617f0dfb4 /boot xfs defaults 0 0
/dev/mapper/centos-swap swap swap defaults 0 0
/dev/sdb1 xfs defaults 0 0

10.5.交换分区

由于内存的容量有限,现在的操作系统,都会使用磁盘模拟一个虚拟内存区域,用于缓冲一些数据。由于磁盘的速度和内存不可同日而语,通常会造成应用程序的卡顿。卡归卡,应用进程却可以因此苟延残喘,续命。

swap分区,即交换区,系统在物理内存不够时,与swap进行交换。即当系统的物理内存不够用时,把硬盘中一部分空间释放出来,以供当前运行的程序使用。当那些程序要运行时,再从swap分区中恢复保存的数据到内存中。

现代互联网公司,一般对接口的响应时间有比较高的要求,swap分区一般是禁用的。关于swap,有下面几个相关的命令。

1
2
3
4
5
6
7
8
# 制作交换分区
[root@localhost ~]# mkswap /dev/sdb1

# 禁用所有交换分区
[root@localhost ~]# swapoff -a

# 启用交换分区
[root@localhost ~]# swapon

10.6 使用dd命令进行备份

下面的命令,将直接备份磁盘A到磁盘B。

1
# dd if=/dev/sda of=/dev/sdb

在上面的命令中,if代表输入的文件,of代表输出的文件。根据Linux下一切皆文件的原理,这里的文件指的就是设备。

dd命令还可以将整个磁盘打包成一个镜像文件。比如下面的命令。

1
# dd if=/dev/hda of=~/hdadisk.img

当然,恢复磁盘的时候,也是相当简单的,我们只需要逆向操作就可以了。

1
# dd if=hdadisk.img of=/dev/hda

End

这篇文章有6万字,经历了多个版本的整理,有小伙伴已经拿着它作为了公司的培训资料。到现在为止,你已经对Linux的命令行有了比较客观的了解。但我这里还有很多可以让你更上一层楼的文章,比如vim、sed、awk的使用。下面是几个扩展阅读,同样采用xjjdog专用的命令行三段解析法,希望对你有所帮助。

工程目录

创建playbook工程结构

1
2
3
4
5
mkdir -p ansible/roles/elastic/{files,templates,tasks,handlers,vars,defaults,meta} 

#创建yml空文件
touch ansible/roles/elastic/{defaults,vars,tasks,meta,handlers}/main.yml

本项目实际用到的结构目录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# tree -L 4
.
├── es-host
├── es.yml
└── roles
└── elastic
├── files
│   ├── elasticsearch-7.14.1-linux-x86_64.tar.gz
├── tasks
│   └── main.yml
└── templates
├── elasticsearch.yml
├── es.ini
└── jvm.options

服务器清单es-host

1
2
3
[es]
10.x.x.1 cluster_name=es-test host_name=es-k8s-0 network_host=10.x.x.1 http_port=9200 network_host_list=[\"10.x.x.1\",\"10.x.x.2\",\"10.x.x.3\"] node_name_list=[\"10.x.x.1\",\"10.x.x.2\",\"10.x.x.3\"]
另外两台的信息,略

这里的参数在后面的template文件中会用到

主清单文件es.yml

1
2
3
- hosts: es
roles:
- elastic

playbook

task文件

重点来了 main.yml

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
---
- name: 防火墙开放端口
firewalld:
port: "{{ item }}/tcp"
state: enabled
permanent: yes
immediate: yes
loop:
- 9200
- 9300

- name: 下发es安装包,并解压
ansible.builtin.unarchive:
src: elasticsearch-7.14.1-linux-x86_64.tar.gz
dest: /usr/local


- name: 下发修改后的es配置文件
ansible.builtin.template:
src: elasticsearch.yml
dest: /usr/local/elasticsearch/config

- name: 下发jvm参数配置
ansible.builtin.template:
src: jvm.options
dest: /usr/local/elasticsearch/config

- name: 安装前置依赖
yum:
name:
- vim
- epel-release
- supervisor
- unzip

- name: 下发supervisord托管配置文件
ansible.builtin.template:
src: es.ini
dest: /etc/supervisord.d

- name: 启动supervisord
ansible.builtin.shell:
cmd: "{{ item }}"
chdir: /tmp
loop:
- useradd -p /home/es -m es
- chown -R es:es /usr/local/elasticsearch
- chown -R es:es /data
- echo "vm.max_map_count=263000" >> /etc/sysctl.conf
- echo "es soft memlock unlimited" >> /etc/security/limits.conf
- echo "es hard memlock unlimited" >> /etc/security/limits.conf
- sysctl -p
- systemctl enable supervisord
- systemctl start supervisord

用到的配置文件

es的配置文件elasticsearch.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# ======================== Elasticsearch Configuration =========================
cluster.name: {{cluster_name}}
node.name: {{host_name}}
node.data: true
node.master: true
node.ingest: true
#bootstrap.memory_lock: true
path.data: /data
path.logs: /usr/local/elasticsearch/logs
network.host: {{network_host}}
http.port: {{http_port}}
discovery.zen.minimum_master_nodes: 2
discovery.seed_hosts: {{network_host_list}}
cluster.initial_master_nodes: {{node_name_list}}
action.destructive_requires_name: true
# 是否支持跨域,是:true,在使用head插件时需要此配置
http.cors.enabled: true
# “*” 表示支持所有域名
http.cors.allow-origin: "*"
http.cors.allow-headers: Authorization,X-Requested-With,Content-Type,Content-Length

jvm的配置信息

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
## JVM configuration

################################################################
## IMPORTANT: JVM heap size
################################################################
##
## You should always set the min and max JVM heap
## size to the same value. For example, to set
## the heap to 4 GB, set:
##
## -Xms4g
## -Xmx4g
##
## See https://www.elastic.co/guide/en/elasticsearch/reference/current/heap-size.html
## for more information
##
################################################################

# Xms represents the initial size of total heap space
# Xmx represents the maximum size of total heap space

-Xms8g
-Xmx8g

################################################################
## Expert settings
################################################################
##
## All settings below this section are considered
## expert settings. Don't tamper with them unless
## you understand what you are doing
##
################################################################

## GC configuration
8-13:-XX:+UseConcMarkSweepGC
8-13:-XX:CMSInitiatingOccupancyFraction=75
8-13:-XX:+UseCMSInitiatingOccupancyOnly

## G1GC Configuration
# NOTE: G1 GC is only supported on JDK version 10 or later
# to use G1GC, uncomment the next two lines and update the version on the
# following three lines to your version of the JDK
# 10-13:-XX:-UseConcMarkSweepGC
# 10-13:-XX:-UseCMSInitiatingOccupancyOnly
14-:-XX:+UseG1GC
14-:-XX:G1ReservePercent=25
14-:-XX:InitiatingHeapOccupancyPercent=30

## JVM temporary directory
-Djava.io.tmpdir=${ES_TMPDIR}

## heap dumps

# generate a heap dump when an allocation from the Java heap fails
# heap dumps are created in the working directory of the JVM
-XX:+HeapDumpOnOutOfMemoryError

# specify an alternative path for heap dumps; ensure the directory exists and
# has sufficient space
-XX:HeapDumpPath=data

# specify an alternative path for JVM fatal error logs
-XX:ErrorFile=logs/hs_err_pid%p.log

## JDK 8 GC logging
8:-XX:+PrintGCDetails
8:-XX:+PrintGCDateStamps
8:-XX:+PrintTenuringDistribution
8:-XX:+PrintGCApplicationStoppedTime
8:-Xloggc:logs/gc.log
8:-XX:+UseGCLogFileRotation
8:-XX:NumberOfGCLogFiles=32
8:-XX:GCLogFileSize=64m

# JDK 9+ GC logging
9-:-Xlog:gc*,gc+age=trace,safepoint:file=logs/gc.log:utctime,pid,tags:filecount=32,filesize=64m

Supervisord托管信息es.ini

1
2
3
4
5
6
7
8
9
10
[program:es]
command=/usr/local/elasticsearch/bin/elasticsearch
autostart=true
autorestart=true
startretries=3
user=es
stdout_logfile_maxbytes=500MB
stdout_logfile_backups = 20
stdout_logfile=/usr/local/elasticsearch/es.log
environment=JAVA_HOME=/usr/local/elasticsearch/jdk

执行安装

1
ansible-playbook -i es-host es.yml

验证

安装完成后,在浏览器里可以验证es集群的状态

1
http://你的ip:9200/_cat/health?v

image-20230908162430909

ok。

使用 Shields.io 制作 github 徽章

我们看 github 上的成熟项目,在 README 中都会有徽章。

我想在自己的开源项目里也加上这些徽章,看起来会专业点~~

Shields.io 就是一个主流的 徽章渲染服务器

定制 徽章 的主要工作就是拼接符合 渲染服务器 规则的 URL,拼接好 URL 后,我们只要将 URL 嵌入 MD 文档即可。

Shields.io 的徽章主要分成两类,静态徽章和动态徽章。

https://shields.io/badges

静态徽章

静态徽章的规则就是:

  1. https://img.shields.io/badge/是 url 前缀,后面跟的就是你自己需要显示的内容
  2. 内容用短横线-间隔。
1
https://img.shields.io/badge/{左半部分}-{右半部分}-{右半部分的颜色}

例如:

1
https://img.shields.io/badge/yunsheng-blog-blue

  • 左右两部分不能同时指定颜色,只能指定右边
  1. 如果不需要两部分,那么规则变为
1
https://img.shields.io/badge/{内容}-{颜色}
1
https://img.shields.io/badge/hello_yunsheng-blue

  • 单个_下划线会被渲染为空格。%20 也是渲染为空格。
  • 双下划线__,才会渲染为一个下划线_
  • 双横线–,会渲染为一个-
  1. 增加 logo

通过增加 query 参数设置

1
https://img.shields.io/badge/yunsheng-repo-green?logo=github

logo 从哪找呢?

https://simpleicons.org/

从这里直接使用 name 即可。

  • logo 也可以配置 color
1
https://img.shields.io/badge/yunsheng-repo-green?logo=go&logoColor=86328A

  • 自定义 logo

支持通过 base64 格式的数据指定 logo

1
https://img.shields.io/badge/{左半部分}-{右半部分}-{右半部分颜色}?logo=data:image/svg+xml;base64,{base64数据}

比如我从 iconfont 上复制一个 icon,转为 base64 数据

可以用命令行,也可以用我这个网站工具。

https://zlib.miaoji360.top/tools/image-to-base64

点击展开/收起 base64数据
1
https://img.shields.io/badge/yunsheng-repo-green?logo=data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBzdGFuZGFsb25lPSJubyI/PjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+PHN2ZyB0PSIxNzcwODYxNzY5NjAxIiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9IjYwNTQiIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iNjQiIGhlaWdodD0iNjQiPjxwYXRoIGQ9Ik05NDQuODM5NjggNjczLjIyODggMTE0LjUxMzkyIDY3My4yMjg4Yy0xNi4zNzg4OCAwLTI5LjY0OTkyLTEzLjM2ODMyLTI5LjY0OTkyLTI5Ljg4MDMyIDAtMTYuNTE3MTIgMTMuMjY1OTItMjkuOTA1OTIgMjkuNjQ5OTItMjkuOTA1OTJsODMwLjMyNTc2IDBjMTYuMzU4NCAwIDI5LjY0OTkyIDEzLjM4ODggMjkuNjQ5OTIgMjkuOTA1OTJDOTc0LjQ4OTYgNjU5Ljg2MDQ4IDk2MS4xOTgwOCA2NzMuMjI4OCA5NDQuODM5NjggNjczLjIyODh6TTg4NS41MjQ0OCAzMjYuODkxNTJjLTExLjU4MTQ0IDExLjY3ODcyLTMwLjM1MTM2IDExLjY3ODcyLTQxLjk0MzA0IDAtMTEuNTgxNDQtMTEuNjc4NzItMTEuNTgxNDQtMzAuNjAyMjQgMC00Mi4yODA5Nmw0MS45NDMwNC00Mi4yODA5NmMxMS41ODE0NC0xMS42Nzg3MiAzMC4zNTEzNi0xMS42Nzg3MiA0MS45Mjc2OCAwIDExLjU5NjggMTEuNjYzMzYgMTEuNTk2OCAzMC42MDIyNCAwIDQyLjI4MDk2TDg4NS41MjQ0OCAzMjYuODkxNTJ6TTgxOS43Mzc2IDU1My42NjY1NmMtMjcuNTA5NzYtMTM2LjM2MDk2LTE0Ni41ODA0OC0yMzkuMTUwMDgtMjkwLjA1ODI0LTIzOS4xNTAwOC0xNDMuNDk4MjQgMC0yNjIuNTYzODQgMTAyLjc5NDI0LTI5MC4wNjg0OCAyMzkuMTUwMDhMMTc3LjE4Nzg0IDU1My42NjY1NmMyMi44MDQ0OC0xNjguNjE2OTYgMTcxLjcyOTkyLTI5OC45NTE2OCAzNTIuNDkxNTItMjk4Ljk1MTY4IDE4MC43NTEzNiAwIDMyOS42ODcwNCAxMzAuMzM0NzIgMzUyLjQ2NTkyIDI5OC45NTE2OEw4MTkuNzM3NiA1NTMuNjY2NTZ6TTUyOS42NjQgMTk0LjkxMzI4Yy0xNi4zNjM1MiAwLTI5LjY0NDgtMTMuMzgzNjgtMjkuNjQ0OC0yOS44ODAzMmwwLTU5LjgwMTZjMC0xNi41MTIgMTMuMjgxMjgtMjkuODk1NjggMjkuNjQ0OC0yOS44OTU2OCAxNi4zODQgMCAyOS42NzA0IDEzLjM4ODggMjkuNjcwNCAyOS44OTU2OGwwIDU5LjgwMTZDNTU5LjMzNDQgMTgxLjUyOTYgNTQ2LjA0OCAxOTQuOTEzMjggNTI5LjY2NCAxOTQuOTEzMjh6TTE3My44MjQgMzI2Ljg5MTUybC00MS45NDMwNC00Mi4yODA5NmMtMTEuNTc2MzItMTEuNjc4NzItMTEuNTc2MzItMzAuNjE3NiAwLTQyLjI4MDk2IDExLjU5NjgtMTEuNjc4NzIgMzAuMzY2NzItMTEuNjc4NzIgNDEuOTQzMDQgMGw0MS45Mjc2OCA0Mi4yODA5NmMxMS41OTY4IDExLjY3ODcyIDExLjU5NjggMzAuNjAyMjQgMCA0Mi4yODA5NkMyMDQuMTc1MzYgMzM4LjU2NTEyIDE4NS40MDU0NCAzMzguNTY1MTIgMTczLjgyNCAzMjYuODkxNTJ6TTE0NC4xNzkyIDczMy4wMjUyOGw3NzEuMDAwMzIgMGMxNi4zNzM3NiAwIDI5LjY2MDE2IDEzLjM4MzY4IDI5LjY2MDE2IDI5LjkwNTkyIDAgMTYuNTEyLTEzLjI4NjQgMjkuODk1NjgtMjkuNjYwMTYgMjkuODk1NjhMMTQ0LjE3OTIgNzkyLjgyNjg4Yy0xNi4zODQgMC0yOS42NjUyOC0xMy4zODM2OC0yOS42NjUyOC0yOS44OTU2OEMxMTQuNTEzOTIgNzQ2LjQwODk2IDEyNy43OTUyIDczMy4wMjUyOCAxNDQuMTc5MiA3MzMuMDI1Mjh6TTIzMy4xMzkyIDg1Mi42MDggODI2LjIxNDQgODUyLjYwOGMxNi4zNzM3NiAwIDI5LjY2NTI4IDEzLjM4MzY4IDI5LjY2NTI4IDI5Ljg5NTY4IDAgMTYuNTE3MTItMTMuMjkxNTIgMjkuOTAwOC0yOS42NjUyOCAyOS45MDA4TDIzMy4xMzkyIDkxMi40MDQ0OGMtMTYuMzc4ODggMC0yOS42NjAxNi0xMy4zODM2OC0yOS42NjAxNi0yOS45MDA4QzIwMy40NzM5MiA4NjUuOTk2OCAyMTYuNzU1MiA4NTIuNjA4IDIzMy4xMzkyIDg1Mi42MDh6IiBmaWxsPSIjRTgzODI4IiBwLWlkPSI2MDU1Ij48L3BhdGg+PC9zdmc+

注意自定义 logo,不能指定 color。你的 base64 应该直接就是你需要的颜色

动态徽章

动态徽章就是数据需要从另一个接口取。

可以看到支持多种格式。我只研究了 JSON。

最重要的是 3 个参数

  • url:我们提供的后台接口,返回格式需要使 json

比如这里我们用他提供的 https://github.com/badges/shields/raw/master/package.json

  • query

使用 JSONPath 表达式获取一个值

右边的值就是我们能用 query 取的。

但是左边的值是默认的。要修改的话就需要我们的第三个参数

  • label。不是必填的,但是很重要

其他非必填参数,用来设置颜色,logo 等

1
https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fgithub.com%2Fbadges%2Fshields%2Fraw%2Fmaster%2Fpackage.json&query=%24.name&logo=github&logoColor=green&label=yunsheng&color=red

其他徽章

还有很多其他功能,如 github 各种状态,有直接的 url 提供。

github

比如在 social 中 github 相关的功能

举个例子

使用这个 url,可以显示我足足有 7 名 follower

仓库代码相关

这个 badge 可以用来显示你的这个仓库是什么语言,什么版本

比如,我的一个 go 代码项目

1
https://img.shields.io/github/go-mod/go-version/jedyang/feishu2md

他获取数据是去我这个仓库下的 go.mod 中获取 go 版本

release

在 version 下有 release 版本相关

1
https://img.shields.io/github/v/release/jedyang/feishu2md

密码学与安全技术

1,密码学简史

从历史角度看,密码学可以大致分为古典密码学和近现代密码学两个阶段。两者以现代信息技术的诞生为分界点,现在所讨论的密码学多是指后者,建立在信息论和数学成果基础之上。

近现代密码的研究源自第一、二次世界大战中对军事通信进行保护和破解的需求。

二战时期德国使用的恩尼格玛(Enigma)密码机(当时最先进的加密设备)被盟军成功破译(1939 年到 1941 年),导致大西洋战役德国失败。据称,二战时期光英国从事密码学研究的人员就达到 7000 人,而他们的成果使二战结束的时间至少提前了一到两年时间。

现代密码学的发展与电气技术特别计算机信息理论和技术关系密切,已经发展为包括随机数、Hash 函数、加解密、身份认证等多个课题的庞大领域,相关成果为现代信息系统特别互联网奠定了坚实的安全基础。

注:Enigma 密码机的加密消息在当年需要数年时间才能破解,而今天使用最新的人工智能技术进行破译只需要 10 分钟左右。

2,Hash 算法与数字摘要

Hash(哈希或散列)算法,又常被称为指纹(fingerprint)或摘要(digest)算法,是非常基础也非常重要的一类算法。可以将任意长度的二进制明文串映射为较短的(通常是固定长度的)二进制串(Hash 值),并且不同的明文很难映射为相同的 Hash 值。

对于某个文件,无需查看其内容,只要其 SHA-256 Hash 计算后结果相同,既有极大概率,文件未被篡改。

除了快速对比内容外,Hash 思想也经常被应用到基于内容的编址或命名算法中。

一个优秀的 Hash 算法,将能满足:

  • 正向快速:给定原文和 Hash 算法,在有限时间和有限资源内能计算得到 Hash 值;
  • 逆向困难:给定(若干)Hash 值,在有限时间内无法(基本不可能)逆推出原文;
  • 输入敏感:原始输入信息发生任何改变,新产生的 Hash 值都应该发生很大变化;
  • 碰撞避免:很难找到两段内容不同的明文,使得它们的 Hash 值一致(即发生碰撞)。

常见算法

目前常见的 Hash 算法包括国际上的 Message Digest(MD)系列和 Secure Hash Algorithm(SHA)系列算法,以及国内的 SM3 算法。

MD 算法主要包括 MD4 和 MD5 两个算法。

MD4(RFC 1320)是 MIT 的 Ronald L. Rivest 在 1990 年设计的,其输出为 128 位。**MD4 已证明不够安全。**MD5(RFC 1321)是 Rivest 于 1991 年对 MD4 的改进版本。它对输入仍以 512 位进行分组,其输出是 128 位。MD5 比 MD4 更加安全,但过程更加复杂,计算速度要慢一点。MD5 已于 2004 年被成功碰撞,其安全性已不足应用于商业场景。。

SHA 算法由美国国家标准与技术院(National Institute of Standards and Technology,NIST)征集制定。首个实现 SHA-0 算法于 1993 年问世,1998 年即遭破解。随后的修订版本 SHA-1 算法在 1995 年面世,它的输出为长度 160 位的 Hash 值,安全性更好。SHA-1 设计采用了 MD4 算法类似原理。SHA-1 已于 2005 年被成功碰撞,意味着无法满足商用需求。

为了提高安全性,NIST 后来制定出更安全的 SHA-224、SHA-256、SHA-384,和 SHA-512 算法(统称为 SHA-2 算法)。新一代的 SHA-3 相关算法也正在研究中。

此外,中国密码管理局于 2010 年 12 月 17 日发布了GM/T 0004-2012 《SM3 密码杂凑算法》,建立了国内商用密码体系中的公开 Hash 算法标准,已经被广泛应用在数字签名和认证等场景中。

注:MD5 和 SHA-1 算法的破解工作都是由清华大学教授、中国科学院院士王小云主导完成。

Hash 攻击与防护

Hash 算法并不是一种加密算法,不能用于对信息的保护。

但 Hash 算法可被应用到对登录口令的保存上。例如网站登录时需要验证用户名和密码,如果网站后台直接保存用户的口令原文,一旦发生数据库泄露后果不堪设想(事实上,网站数据库泄露事件在国内外都不少见)。

利用 Hash 的防碰撞特性,后台数据库可以仅保存用户口令的 Hash 值,这样每次通过 Hash 值比对,即可判断输入口令是否正确。即便数据库泄露了,攻击者也无法轻易从 Hash 值还原回口令。

然而,有时用户设置口令的安全强度不够,采用了一些常见的字符串,如 password、123456 等。有人专门搜集了这些常见口令,计算对应的 Hash 值,制作成字典。这样通过 Hash 值可以快速反查到原始口令。这一类型以空间换时间的攻击方法包括字典攻击和彩虹表攻击(只保存一条 Hash 链的首尾值,相对字典攻击可以节省存储空间)等。

为了防范这一类攻击,可以采用加盐(Salt)的方法。保存的不是原文的直接 Hash 值,而是原文再加上一段随机字符串(即“盐”)之后的 Hash 值。Hash 结果和“盐”分别存放在不同的地方,这样只要不是两者同时泄露,攻击者很难进行破解。

注意,我们在开发时应该把Hash 结果和“盐”分别存放在不同的地方,防止同时泄露。

3,加解密算法

加解密算法是现代密码学核心技术,从设计理念和应用场景上可以分为两大基本类型,如下表所示。

算法类型 特点 优势 缺陷 代表算法
对称加密 加解密的密钥相同 计算效率高,加密强度高 需提前共享密钥,易泄露 DES、3DES、AES、IDEA
非对称加密 加解密的密钥不相同 无需提前共享密钥 计算效率低,存在中间人攻击可能 RSA、ElGamal、椭圆曲线算法

对称加密算法

对称加密算法,顾名思义,加密和解密过程的密钥是相同的。

该类算法优点是**加解密效率(速度快,空间占用小)**和加密强度都很高。

缺点是参与方需要提前持有密钥,一旦有人泄露则系统安全性被破坏;另外如何在不安全通道中提前分发密钥也是个问题,需要借助额外的 Diffie–Hellman 协商协议或非对称加密算法来实现。

对称密码从实现原理上可以分为两种:分组密码和序列密码。前者将明文切分为定长数据块作为基本加密单位,应用最为广泛。后者则每次只对一个字节或字符进行加密处理,且密码不断变化,只用在一些特定领域(如数字媒介的加密)。

分组对称加密代表算法包括 DES、3DES、AES、IDEA 等。

  • DES(Data Encryption Standard):经典的分组加密算法,最早是 1977 年美国联邦信息处理标准(FIPS)采用 FIPS-46-3,将 64 位明文加密为 64 位的密文。其密钥长度为 64 位(包括 8 位校验码),现在已经很容易被暴力破解;
  • 3DES:三重 DES 操作:加密 –> 解密 –> 加密,处理过程和加密强度优于 DES,但现在也被认为不够安全;
  • AES(Advanced Encryption Standard):由美国国家标准研究所(NIST)采用,取代 DES 成为对称加密实现的标准,1997~2000 年 NIST 从 15 个候选算法中评选 Rijndael 算法(由比利时密码学家 Joan Daemon 和 Vincent Rijmen 发明)作为 AES,标准为 FIPS-197。AES 也是分组算法,分组长度为 128、192、256 位三种。AES 的优势在于处理速度快,整个过程可以数学化描述,目前尚未有有效的破解手段;
  • IDEA(International Data Encryption Algorithm):1991 年由密码学家 James Massey 与来学嘉共同提出。设计类似于 3DES,密钥长度增加到 128 位,具有更好的加密强度。

序列加密,又称流加密。1949 年,Claude Elwood Shannon(信息论创始人)首次证明,要实现绝对安全的完善保密性(Perfect Secrecy),可以通过“一次性密码本”的对称加密处理。即通信双方每次使用跟明文等长的随机密钥串对明文进行加密处理。序列密码采用了类似的思想,每次通过伪随机数生成器来生成伪随机密钥串。代表算法包括 RC4 等。

总结下,对称加密算法适用于大量数据的加解密过程;不能用于签名场景;并且需要提前安全地分发密钥。

注:分组加密每次只能处理固定长度的明文,因此对于过长的内容需要采用一定模式进行分割,《实用密码学》一书中推荐使用密文分组链(Cipher Block Chain,CBC)、计数器(Counter,CTR)等模式。

非对称加密算法

非对称加密是现代密码学的伟大发明,它有效解决了对称加密需要安全分发密钥的问题。

顾名思义,非对称加密中,加密密钥和解密密钥是不同的,分别被称为公钥(Public Key)和私钥(Private Key)。私钥一般通过随机数算法生成,公钥可以根据私钥生成。

其中,公钥一般是公开的,他人可获取的;私钥则是个人持有并且要严密保护,不能被他人获取。

非对称加密算法优点是公私钥分开,无需安全通道来分发密钥。缺点是处理速度(特别是生成密钥和解密过程)往往比较慢,一般比对称加解密算法慢 2~3 个数量级;同时加密强度也往往不如对称加密。

非对称加密算法的安全性往往基于数学问题,包括大数质因子分解、离散对数、椭圆曲线等经典数学难题。

代表算法包括:RSA、ElGamal、椭圆曲线(Elliptic Curve Crytosystems,ECC)、SM2 等系列算法。

  • RSA:经典的公钥算法,1978 年由 Ron Rivest、Adi Shamir、Leonard Adleman 共同提出,三人于 2002 年因此获得图灵奖。算法利用了对大数进行质因子分解困难的特性,但目前还没有数学证明两者难度等价,或许存在未知算法可以绕过大数分解而进行解密。
  • ElGamal:由 Taher ElGamal 设计,利用了模运算下求离散对数困难的特性,比 RSA 产生密钥更快。被应用在 PGP 等安全工具中。
  • 椭圆曲线算法(Elliptic Curve Cryptography,ECC):应用最广也是强度最高的系列算法,基于对椭圆曲线上特定点进行特殊乘法逆运算(求离散对数)难以计算的特性。最早在 1985 年由 Neal Koblitz 和 Victor Miller 分别独立提出。ECC 系列算法具有多种国际标准(包括 ANSI X9.63、NIST FIPS 186-2、IEEE 1363-2000、ISO/IEC 14888-3 等),一般被认为具备较高的安全性,但加解密过程比较费时。其中,密码学家 Daniel J.Bernstein 于 2006 年提出的 Curve25519/Ed25519/X25519 等算法(分别解决加密、签名和密钥交换),由于其设计完全公开、性能突出等特点,近些年引起了广泛关注和应用。
  • SM2(ShangMi 2):中国国家商用密码系列算法标准,由中国密码管理局于 2010 年 12 月 17 日发布,同样基于椭圆曲线算法,一般认为其安全强度优于 RSA 系列算法。

非对称加密算法适用于签名场景或密钥协商过程,但不适于大量数据的加解密。除了 SM2 之外,大部分算法的签名速度要比验签速度慢(1~2个数量级)。

RSA 类算法被认为已经很难抵御现代计算设备的破解,一般推荐商用场景下密钥至少为 2048 位。如果采用安全强度更高的椭圆曲线算法,256 位密钥即可满足绝大部分安全需求。

选择明文攻击

细心的读者可能会想到,非对称加密中公钥是公开的,因此任何人都可以利用它加密给定明文,获取对应的密文,这就带来选择明文攻击的风险。

为了规避这种风险,现代的非对称加密算法(如 RSA、ECC)都引入了一定的保护机制:对同样的明文使用同样密钥进行多次加密,得到的结果完全不同,这就避免了选择明文攻击的破坏。

在实现上可以有多种思路。一种是对明文先进行变形,添加随机的字符串或标记,再对添加后结果进行处理。另外一种是先用随机生成的临时密钥对明文进行对称加密,然后再将对称密钥进行加密,即利用多层加密机制。

混合加密机制

混合加密机制同时结合了对称加密和非对称加密的优点。

该机制的主要过程为:先用非对称加密(计算复杂度较高)协商出一个临时的对称加密密钥(或称会话密钥),然后双方再通过对称加密算法(计算复杂度较低)对所传递的大量数据进行快速的加密处理。

典型的应用案例是网站中使用越来越普遍的通信协议 – 安全超文本传输协议(Hyper Text Transfer Protocol Secure,HTTPS)。

与以明文方式传输数据的 HTTP 协议不同,HTTPS 在传统的 HTTP 层和 TCP 层之间引入 Transport Layer Security/Secure Socket Layer(TLS/SSL)加密层来实现安全传输。

SSL 协议是HTTPS 初期采用的标准协议,最早由 Netscape 于 1994 年设计实现,其两个主要版本(包括 v2.0 和 v3.0)曾得到大量应用。SSL 存在安全缺陷易受攻击(如 POODLE 和 DROWN 攻击),无法满足现代安全需求,已于 2011 和 2015 年被 IETF 宣布废弃。基于 SSL 协议(v3.1),IETF 提出了改善的安全标准协议 TLS,成为目前广泛采用的方案。2008 年 8 月,TLS 1.2 版本(RFC 5246)发布,修正了之前版本的不少漏洞,极大增强了安全性;2018 年 8 月,TLS 1.3 版本(RFC 8446)发布,提高了握手性能同时增强了安全性。商用场景推荐使用这两个版本。除了 Web 服务外,TLS 协议也被广泛应用到 FTP、Email、实时消息、音视频通话等场景中。

采用 HTTPS 建立安全连接(TLS 握手协商过程)的基本步骤如下:

TLS 握手协商过程图 1.8.3.2 - TLS 握手协商过程

  • 客户端浏览器发送握手信息到服务器,包括随机数 R1、支持的加密算法套件(Cipher Suite)类型、协议版本、压缩算法等。注意该过程传输为明文。
  • 服务端返回信息,包括随机数 R2、选定加密算法套件、协议版本,以及服务器证书。注意该过程为明文。
  • 浏览器检查带有该网站公钥的证书。该证书需要由第三方 CA 来签发,浏览器和操作系统会预置权威 CA 的根证书。如果证书被篡改作假(中间人攻击),很容易通过 CA 的证书验证出来。
  • 如果证书没问题,则客户端用服务端证书中公钥加密随机数 R3(又叫 Pre-MasterSecret),发送给服务器。此时,只有客户端和服务器都拥有 R1、R2 和 R3 信息,基于随机数 R1、R2 和 R3,双方通过伪随机数函数来生成共同的对称会话密钥 MasterSecret。
  • 后续客户端和服务端的通信都通过协商后的对称加密(如 AES)进行保护。

可以看出,该过程是实现防止中间人窃听和篡改的前提下完成会话密钥的交换。为了保障前向安全性(Perfect Forward Secrecy),TLS 对每个会话连接都可以生成不同的密钥,避免某个会话密钥泄露后对其它会话连接产生安全威胁。需要注意,选用合适的加密算法套件对于 TLS 的安全性十分重要。要合理选择安全强度高的算法组合,如 ECDHE-RSA 和 ECDHE-ECDSA 等,而不要使用安全性较差的 DES/3DES 等。

示例中对称密钥的协商过程采用了 RSA 非对称加密算法,实践中也可以通过 Diffie–Hellman(DH)协议来完成。

加密算法套件包括一组算法,包括交换、认证、加密、校验等。

  • 密钥交换算法:负责协商对称密钥,常见类型包括 RSA、DH、ECDH、ECDHE 等;
  • 证书签名算法:负责验证身份,常见类型包括 RSA、DSA、ECDSA 等;
  • 加密数据算法:对建立连接的通信内容进行对称加密,常见类型包括 AES 等;
  • 消息认证信息码(MAC)算法:创建报文摘要,验证消息的完整性,常见类型包括 SHA 等。

一个典型的 TLS 密码算法套件可能为 “TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384”,意味着:

  • 协商过程算法是 ECDHE(Elliptic Curve Diffie–Hellman Ephemeral),基于椭圆曲线的短期 EH 交换,每次交换都用新的密钥,保障前向安全性;
  • 证书签名算法是 ECDSA(Elliptic Curve Digital Signature Algorithm),基于椭圆曲线的签名;
  • 加密数据算法是 AES,密钥的长度和初始向量的长度都是 256,模式是 CBC;
  • 消息认证信息码算法是 SHA,结果是 384 位。

目前,推荐选用如下的加密算法套件:

  • TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
  • TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
  • TLS_RSA_WITH_AES_256_GCM_SHA384
  • TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384
  • TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384
  • TLS_DHE_RSA_WITH_AES_256_GCM_SHA384

注:TLS 1.0 版本已被发现存在安全漏洞,NIST、HIPAA 于 2014 年公开建议停用该版本的 TLS 协议。

离散对数与 Diffie–Hellman 密钥交换协议

Diffie–Hellman(DH)密钥交换协议是一个应用十分广泛的协议,最早由惠特菲尔德·迪菲(Bailey Whitfield Diffie)和马丁·赫尔曼(Martin Edward Hellman)于 1976 年提出。该协议可以实现在不安全信道中协商对称密钥。

DH 协议的设计基于著名的离散对数问题(Discrete Logarithm Problem,DLP)。离散对数问题是指对于一个很大的素数 p,已知 g 为 p 的模循环群的原根,给定任意 x,求解 X=g^x mod p 是可以很快获取的。但在已知 p,g 和 X 的前提下,逆向求解 x 很难(目前没有找到多项式时间实现的算法)。该问题同时也是 ECC 类加密算法的基础。

DH 协议的基本交换过程如下,以 Alice 和 Bob 两人协商为例:

  • Alice 和 Bob 两个人协商密钥,先公开商定 p,g;
  • Alice 自行选取私密的整数 x,计算 X=g^x mod p,发送 X 给 Bob;
  • Bob 自行选取私密的整数 y,计算 Y=g^y mod p,发送 Y 给 A;
  • Alice 根据 x 和 Y,求解共同密钥 Z_A=Y^x mod p;
  • Bob 根据 X 和 y,求解共同密钥 Z_B=X^y mod p。

实际上,Alice 和 Bob 计算出来的结果将完全相同,因为在 mod p 的前提下,Y^x =(g^y)^x =g^(xy) = (g^x)^y=X^y。而信道监听者在已知 p,g,X,Y 的前提下,无法求得 Z。

4,数字签名

类似在纸质合同上进行签名以确认合同内容和证明身份,数字签名既可以证实某数字内容的完整性,又可以确认其来源(即不可抵赖,Non-Repudiation)。

一个典型的场景是,Alice 通过信道发给 Bob 一个文件(一份信息),Bob 如何获知所收到的文件即为 Alice 发出的原始版本?Alice 可以先对文件内容进行摘要,然后用自己的私钥对摘要进行加密(签名),之后同时将文件和签名都发给 Bob。Bob 收到文件和签名后,用 Alice 的公钥来解密签名,得到数字摘要,与对文件进行摘要后的结果进行比对。如果一致,说明该文件确实是 Alice 发过来的(因为别人无法拥有 Alice 的私钥),并且文件内容没有被修改过(摘要结果一致)。

理论上所有的非对称加密算法都可以用来实现数字签名,实践中常用算法包括 1991 年 8 月 NIST 提出的 DSA(Digital Signature Algorithm,基于 ElGamal 算法)和安全强度更高的 ECSDA(Elliptic Curve Digital Signature Algorithm,基于椭圆曲线算法)等。

除普通的数字签名应用场景外,针对一些特定的安全需求,产生了一些特殊数字签名技术,包括盲签名、多重签名、群签名、环签名等。

5,数字证书

对于非对称加密算法和数字签名来说,很重要的步骤就是公钥的分发。理论上任何人都可以获取到公开的公钥。然而这个公钥文件有没有可能是伪造的呢?传输过程中有没有可能被篡改呢?一旦公钥自身出了问题,则整个建立在其上的的安全性将不复成立。

数字证书机制正是为了解决这个问题,它就像日常生活中的证书一样,可以确保所记录信息的合法性。比如证明某个公钥是某个实体(个人或组织)拥有,并且确保任何篡改都能被检测出来,从而实现对用户公钥的安全分发。

根据所保护公钥的用途,数字证书可以分为加密数字证书(Encryption Certificate)和签名验证数字证书(Signature Certificate)。前者往往用于保护用于加密用途的公钥;后者则保护用于签名用途的公钥。两种类型的公钥也可以同时放在同一证书中。

一般情况下,证书需要由证书认证机构(Certification Authority,CA)来进行签发和背书。权威的商业证书认证机构包括 DigiCert、GlobalSign、VeriSign 等。用户也可以自行搭建本地 CA 系统,在私有网络中进行使用。

X.509 证书规范

一般的,一个数字证书内容可能包括证书域(证书的版本、序列号、签名算法类型、签发者信息、有效期、被签发主体、签发的公开密钥)、CA 对证书的签名算法和签名值等。

目前使用最广泛的标准为 ITU 和 ISO 联合制定的 X.509 的 v3 版本规范(RFC 5280),其中定义了如下证书信息域:

  • 版本号(Version Number):规范的版本号,目前为版本 3,值为 0x2;
  • 序列号(Serial Number):由 CA 维护的为它所颁发的每个证书分配的唯一的序列号,用来追踪和撤销证书。只要拥有签发者信息和序列号,就可以唯一标识一个证书。最大不能超过 20 个字节;
  • 签名算法(Signature Algorithm):数字签名所采用的算法,如 sha256WithRSAEncryption 或 ecdsa-with-SHA256;
  • 颁发者(Issuer):颁发证书单位的信息,如 “C=CN, ST=Beijing, L=Beijing, O=org.example.com, CN=ca.org.example.com”;
  • 有效期(Validity):证书的有效期限,包括起止时间(如 Not Before 2018-08-08-00-00UTC,Not After 2028-08-08-00-00UTC);
  • 被签发主体(Subject):证书拥有者的标识信息(Distinguished Name),如 “C=CN, ST=Beijing, L=Beijing, CN=personA.org.example.com”;
  • 主体的公钥信息(Subject Public Key Info):所保护的公钥相关的信息;
    • 公钥算法(Public Key Algorithm):公钥采用的算法;
    • 主体公钥(Subject Public Key):公钥的内容;
  • 颁发者唯一号(Issuer Unique Identifier,可选):代表颁发者的唯一信息,仅 2、3 版本支持,可选;
  • 主体唯一号(Subject Unique Identifier,可选):代表拥有证书实体的唯一信息,仅 2、3 版本支持,可选;
  • 扩展(Extensions,可选):可选的一些扩展。可能包括:
    • Subject Key Identifier:实体的密钥标识符,区分实体的多对密钥;
    • Basic Constraints:一般指明该证书是否属于某个 CA;
    • Authority Key Identifier:颁发这个证书的颁发者的公钥标识符;
    • Authority Information Access:颁发相关的服务地址,如颁发者证书获取地址和吊销证书列表信息查询地址;
    • CRL Distribution Points:证书注销列表的发布地址;
    • Key Usage: 表明证书的用途或功能信息,如 Digital Signature、Key CertSign;
    • Subject Alternative Name:证书身份实体的别名,如该证书可以同样代表 .org.example.com,org.example.com,.example.com,example.com 身份等。

此外,证书的颁发者还需要对证书内容利用自己的私钥进行签名,以防止他人篡改证书内容。

证书格式

X.509 规范中一般推荐使用 PEM(Privacy Enhanced Mail)格式来存储证书相关的文件。证书文件的文件名后缀一般为 .crt.cer,对应私钥文件的文件名后缀一般为 .key,证书请求文件的文件名后缀为 .csr。有时候也统一用 .pem 作为文件名后缀。

PEM 格式采用文本方式进行存储,一般包括首尾标记和内容块,内容块采用 base64 编码。

例如,一个示例证书文件的 PEM 格式如下所示。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
-----BEGIN CERTIFICATE-----
MIICMzCCAdmgAwIBAgIQIhMiRzqkCljq3ZXnsl6EijAKBggqhkjOPQQDAjBmMQsw
CQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZy
YW5jaXNjbzEUMBIGA1UEChMLZXhhbXBsZS5jb20xFDASBgNVBAMTC2V4YW1wbGUu
Y29tMB4XDTE3MDQyNTAzMzAzN1oXDTI3MDQyMzAzMzAzN1owZjELMAkGA1UEBhMC
VVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNhbiBGcmFuY2lzY28x
FDASBgNVBAoTC2V4YW1wbGUuY29tMRQwEgYDVQQDEwtleGFtcGxlLmNvbTBZMBMG
ByqGSM49AgEGCCqGSM49AwEHA0IABCkIHZ3mJCEPbIbUdh/Kz3zWW1C9wxnZOwfy
yrhr6aHwWREW3ZpMWKUcbsYup5kbouBc2dvMFUgoPBoaFYJ9D0SjaTBnMA4GA1Ud
DwEB/wQEAwIBpjAZBgNVHSUEEjAQBgRVHSUABggrBgEFBQcDATAPBgNVHRMBAf8E
BTADAQH/MCkGA1UdDgQiBCBIA/DmemwTGibbGe8uWjt5hnlE63SUsXuNKO9iGEhV
qDAKBggqhkjOPQQDAgNIADBFAiEAyoMO2BAQ3c9gBJOk1oSyXP70XRk4dTwXMF7q
R72ijLECIFKLANpgWFoMoo3W91uzJeUmnbJJt8Jlr00ByjurfAvv
-----END CERTIFICATE-----

可以通过 openssl 工具来查看其内容。

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
# openssl x509 -in example.com-cert.pem -noout -text
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
22:13:22:47:3a:a4:0a:58:ea:dd:95:e7:b2:5e:84:8a
Signature Algorithm: ecdsa-with-SHA256
Issuer: C=US, ST=California, L=San Francisco, O=example.com, CN=example.com
Validity
Not Before: Apr 25 03:30:37 2017 GMT
Not After : Apr 23 03:30:37 2027 GMT
Subject: C=US, ST=California, L=San Francisco, O=example.com, CN=example.com
Subject Public Key Info:
Public Key Algorithm: id-ecPublicKey
Public-Key: (256 bit)
pub:
04:29:08:1d:9d:e6:24:21:0f:6c:86:d4:76:1f:ca:
cf:7c:d6:5b:50:bd:c3:19:d9:3b:07:f2:ca:b8:6b:
e9:a1:f0:59:11:16:dd:9a:4c:58:a5:1c:6e:c6:2e:
a7:99:1b:a2:e0:5c:d9:db:cc:15:48:28:3c:1a:1a:
15:82:7d:0f:44
ASN1 OID: prime256v1
X509v3 extensions:
X509v3 Key Usage: critical
Digital Signature, Key Encipherment, Certificate Sign, CRL Sign
X509v3 Extended Key Usage:
Any Extended Key Usage, TLS Web Server Authentication
X509v3 Basic Constraints: critical
CA:TRUE
X509v3 Subject Key Identifier:
48:03:F0:E6:7A:6C:13:1A:26:DB:19:EF:2E:5A:3B:79:86:79:44:EB:74:94:B1:7B:8D:28:EF:62:18:48:55:A8
Signature Algorithm: ecdsa-with-SHA256
30:45:02:21:00:ca:83:0e:d8:10:10:dd:cf:60:04:93:a4:d6:
84:b2:5c:fe:f4:5d:19:38:75:3c:17:30:5e:ea:47:bd:a2:8c:
b1:02:20:52:8b:00:da:60:58:5a:0c:a2:8d:d6:f7:5b:b3:25:
e5:26:9d:b2:49:b7:c2:65:af:4d:01:ca:3b:ab:7c:0b:ef

此外,还有 DER(Distinguished Encoding Rules)格式,是采用二进制对证书进行保存,可以与 PEM 格式互相转换。

证书信任链

证书中记录了大量信息,其中最重要的包括 签发的公开密钥CA 数字签名 两个信息。因此,只要使用 CA 的公钥再次对这个证书进行签名比对,就能证明所记录的公钥是否合法。

读者可能会想到,怎么证明用来验证对实体证书进行签名的 CA 公钥自身是否合法呢?毕竟在获取 CA 公钥的过程中,它也可能被篡改掉。

实际上,CA 的公钥是否合法,一方面可以通过更上层的 CA 颁发的证书来进行认证;另一方面某些根 CA(Root CA)可以通过预先分发证书来实现信任基础。例如,主流操作系统和浏览器里面,往往会提前预置一些权威 CA 的证书(通过自身的私钥签名,系统承认这些是合法的证书)。之后所有基于这些 CA 认证过的中间层 CA(Intermediate CA)和后继 CA 都会被验证合法。这样就从预先信任的根证书,经过中间层证书,到最底下的实体证书,构成一条完整的证书信任链。

某些时候用户在使用浏览器访问某些网站时,可能会被提示是否信任对方的证书。这说明该网站证书无法被当前系统中的证书信任链进行验证,需要进行额外检查。另外,当信任链上任一证书不可靠时,则依赖它的所有后继证书都将失去保障。

可见,证书作为公钥信任的基础,对其生命周期进行安全管理十分关键。后面章节将介绍的 PKI 体系提供了一套完整的证书管理的框架,包括生成、颁发、撤销过程等。

6,PKI 体系

按照 X.509 规范,公钥可以通过证书机制来进行保护,但证书的生成、分发、撤销等步骤并未涉及。

实际上,要实现安全地管理、分发证书需要遵循 PKI(Public Key Infrastructure)体系。该体系解决了证书生命周期相关的认证和管理问题。

需要注意,PKI 是建立在公私钥基础上实现安全可靠传递消息和身份确认的一个通用框架,并不代表某个特定的密码学技术和流程。实现了 PKI 规范的平台可以安全可靠地管理网络中用户的密钥和证书。目前包括多个具体实现和规范,知名的有 RSA 公司的 PKCS(Public Key Cryptography Standards)标准和 OpenSSL 等开源工具。

PKI 基本组件

一般情况下,PKI 至少包括如下核心组件:

  • CA(Certification Authority):负责证书的颁发和吊销(Revoke),接收来自 RA 的请求,是最核心的部分;
  • RA(Registration Authority):对用户身份进行验证,校验数据合法性,负责登记,审核过了就发给 CA;
  • 证书数据库:存放证书,多采用 X.500 系列标准格式。可以配合LDAP 目录服务管理用户信息。

其中,CA 是最核心的组件,主要完成对证书信息的维护。

常见的操作流程为,用户通过 RA 登记申请证书,提供身份和认证信息等;CA 审核后完成证书的制造,颁发给用户。用户如果需要撤销证书则需要再次向 CA 发出申请。

证书的签发

CA 对用户签发证书实际上是对某个用户公钥,使用 CA 的私钥对其进行签名。这样任何人都可以用 CA 的公钥对该证书进行合法性验证。验证成功则认可该证书中所提供的用户公钥内容,实现用户公钥的安全分发。

用户证书的签发可以有两种方式。可以由用户自己生成公钥和私钥,然后 CA 来对公钥内容进行签名(只有用户持有私钥);也可以由 CA 直接来生成证书(内含公钥)和对应的私钥发给用户(用户和 CA 均持有私钥)。

前者情况下,用户一般会首先自行生成一个私钥和证书申请文件(Certificate Signing Request,即 csr 文件),该文件中包括了用户对应的公钥和一些基本信息,如通用名(common name,即 cn)、组织信息、地理位置等。CA 只需要对证书请求文件进行签名,生成证书文件,颁发给用户即可。整个过程中,用户可以保持私钥信息的私密性,不会被其他方获知(包括 CA 方)。

需要注意,用户自行生成私钥情况下,私钥文件一旦丢失,CA 方由于不持有私钥信息,无法进行恢复,意味着通过该证书中公钥加密的内容将无法被解密。

7,同态加密

定义

同态加密(Homomorphic Encryption)是一种特殊的加密方法,允许对密文进行处理得到仍然是加密的结果。即对密文直接进行处理,跟对明文进行处理后再对处理结果加密,得到的结果相同。从抽象代数的角度讲,保持了同态性。

同态加密可以保证实现处理者无法访问到数据自身的信息。

同态加密在云计算和大数据的时代意义十分重大。目前,虽然云计算带来了包括低成本、高性能和便捷性等优势,但从安全角度讲,用户还不敢将敏感信息直接放到第三方云上进行处理。如果有了比较实用的同态加密技术,则大家就可以放心的使用各种云服务了,同时各种数据分析过程也不会泄露用户隐私。加密后的数据在第三方服务处理后得到加密后的结果,这个结果只有用户自身可以进行解密,整个过程第三方平台无法获知任何有效的数据信息。

另一方面,对于区块链技术,同态加密也是很好的互补。使用同态加密技术,运行在区块链上的智能合约可以处理密文,而无法获知真实数据,极大的提高了隐私安全性。

目前,已知的同态加密技术往往需要较高的计算时间或存储成本,相比传统加密算法的性能和强度还有差距,但该领域关注度一直很高,笔者相信,在不远的将来会出现接近实用的方案。

Dockerfile

Dockerfile可以分成4部分:基础镜像,维护者信息,镜像操作指令,启动命令

指令 作用
FROM 指定基础镜像
LABEL 打标签,k=v形式。多个kv之间空格隔开。v如果有空格等特殊字符,用“”引起。
MAINTAINER 指定维护者信息,已经过时,可以使用LABEL maintainer=xxx 来替代
ENV 环境变量,给RUN和CMD命令使用。会固化到镜像的ContainerConfig里
ARG 指定构建参数。声明后的RUN命令才能使用。可以在build时,通过设置–build-arg来更改
USER 切换用户,他以后的命令都是以该用户运行。USER 1000:1000
RUN build镜像期运行的命令。多个RUN指令之间没有关系。有关系要在同一个RUN使用&&
ADD 复制上下文中的文件进容器
COPY 复制上下文中的文件进容器
CMD
ENTRYPOINT
WORKDIR 指定以后命令的运行目录,不存在会创建
VOLUME 挂载,相当于run的-v参数。
EXPOSE 暴露端口,这只是一个声明。给用户看的而已。docker run -P的时候自动绑定端口,也会使用这个配置。

ENV vs ARG

  • ENV在构建期和运行期都能生效。ARG只在构建期生效
  • ENV的值不能在build时修改,ARG可以通过–build-arg来更改
  • ENV的值可以在run时修改,通过-e参数

ADD vs COPY

ADD复制的如果是压缩包自动解压,如果是远程包,自动下载。

VOLUME

注意:一旦容器内的目录被挂载出去,VOLUME命令之后的对目录的所有修改操作,不会再影响挂载出去的目录。

所以,一般把VOLUME指令放在所有变更命令的后面

RUN

指令的写法又两种:shell写法和exec写法。

shell写法:

RUN

exec写法

RUN [“命令”,“参数1”, “参数2”]

区别:

如果命令中有环境变量,exec写法取不到ENV环境变量

CMD && ENTRYPOINT

ENTRYPOINT和CMD的作用都是容器的启动命令。看起来有一些重复。但是他们还是有一些微妙的区别。

ENTRYPOINT是真正的门,真正的入口。

覆盖

ENTRYPOINT或者CMD命令会自动覆盖之前的ENTRYPOINT或者CMD命令。

所以,在docker镜像运行时, 我们可以在命令指定具体命令, 覆盖在Dockerfile里的命令.

比如,这样的一个Dockerfile

1
2
FROM ubuntu:trusty
CMD ["/bin/ping", "localhost"]

默认运行这个镜像是ping 本机。

我们可以在执行时覆盖这个默认命令。比如用hostname命令替换ping命令。

1
# docker run demo:1.0 hostname

image-20210820163403312

ENTRYPOINT也是一样的,只不过覆盖时要用–entrypoint

1
2
FROM ubuntu:trusty
ENTRYPOINT ["/bin/ping", "localhost"]
1
2
# docker run --entrypoint hostname demo:1.0
e2540a13d1f8

所以,CMD命令更容易被docker run命令的方式覆盖。相反, 如果你希望你的docker镜像只执行一个具体程序, 不希望用户在执行docker run的时候随意覆盖默认程序. 建议用ENTRYPOINT.

两种写法

CMD和ENTRYPOINT指令的也是有两种格式写法。shell写法和exec写法。

上面例子是Exec写法。

这两种写法是有一个本质区别的。

同样是ping localhost。用exec写法启动的容器

1
2
3
4
# docker exec caed34225b48 ps -f
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 08:43 ? 00:00:00 /bin/ping localhost
root 6 0 0 08:44 ? 00:00:00 ps -f

可以看到PID 1启动的就是ping命令。

再看下shell写法的结果

1
2
3
4
5
# docker exec 545269dc6d49 ps -f
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 08:46 ? 00:00:00 /bin/sh -c /bin/ping localhost
root 6 1 0 08:46 ? 00:00:00 /bin/ping localhost
root 7 0 0 08:46 ? 00:00:00 ps -f

可以看到PID 1启动的其实是sh程序,我们的ping命令作为sh的子程序运行了。

这样会导致的一个问题是,你从外部发给容器的POSIX信号,是被sh接收的,而sh命令并不会将信号转发给ping命令,会导致不能安全的关闭容器。比如用shell方法启动的容器一直在运行,我按Ctrl +C是无法停止的。

总结:exec写法不是bin/sh -c执行的,取不出环境变量的值。

所以最好时使用exec写法。

最终的用法:组合使用

从上面所知,ENTRYPOINT 用于指明程序必须运行的命令。CMD又比较方便覆盖。所以,我们可以将其联合使用,指定必须运行的命令。同时运行用户覆盖一些参数。ENTRYPOINT和CMD同时存在时, docker把CMD的命令拼接到ENTRYPOINT命令之后。

比如

1
2
3
FROM ubuntu:trusty
ENTRYPOINT ["/bin/ping","localhost","-c"]
CMD ["3"]

这样就运行用户自己指定运行的次数。

1
2
3
4
5
6
7
8
9
10
docker run demo:1.0 5
PING localhost (127.0.0.1) 56(84) bytes of data.
64 bytes from localhost (127.0.0.1): icmp_seq=1 ttl=64 time=0.045 ms
64 bytes from localhost (127.0.0.1): icmp_seq=2 ttl=64 time=0.039 ms
64 bytes from localhost (127.0.0.1): icmp_seq=3 ttl=64 time=0.042 ms
64 bytes from localhost (127.0.0.1): icmp_seq=4 ttl=64 time=0.041 ms
64 bytes from localhost (127.0.0.1): icmp_seq=5 ttl=64 time=0.035 ms

--- localhost ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 3999ms

多阶段构建

k8s中的RBAC

我们在业务开发中经常会遇到权限认证和授权业务。

前面说到的serviceAccount就是k8s的认证过程。而k8s的授权机制是通过RBAC来完成的。

RBAC顾名思义即基于角色的权限控制。

RBAC的授权是通过四种资源来配置的,它们又可以分成两组

  • Role(角色)ClusterRole(集群角色),它们指定了在资源上可以执行哪些动词
  • RoleBinding(角色绑定)ClusterRoleBinding(集群角色绑定),它们将上述角色绑定到特定的用户、组或ServiceAccounts上。

角色定义了可以做什么操作,而绑定定义了谁可以做这些操作

在k8s中所有的资源信息都在etcd里,要访问etcd必须通过apiserver。而apiserver就是通过role来控制哪些账号能访问哪些资源。

Role和ClusterRole

k8s了有两个和角色相关的资源:Role和ClusterRole

Role 是定义在一个 namespace 中,而 ClusterRole 是集群级别的。

定义一个Role

1
2
3
4
5
6
7
8
9
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: test-role
namespace: test
rules:
- apiGroups: [""] # 为空表示为默认的Core API组
resources: ["pods"] # 数据源类型
verbs: ["get","watch","list"] #赋予的动作权限

这个Role的规则含义是:在名字为 test的namespace 中,对Pods有get,watch,list的权限

注意一点:在指定资源时必须使用复数的形式。

定义一个ClusterRole

1
2
3
4
5
6
7
8
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: test-clusterrole
rules:
- apiGroups: [""]
resources: ["services"]
verbs: ["get","create","list"]

与Role的区别就是matadata中没有namespace的限定。

这个ClusterRole的含义是,对整个集群的service有get,create,list的权限

分别创建

1
2
kc apply -f test_role.yaml
kc apply -f test_clusterrole.yaml

rules的参数说明:

  1. apiGroups可配置参数
    “”(空表示core api组),“apps”, “autoscaling”, “batch”, “extensions”等等

这个apiGroups指的就是kubectl api-resources的APIGROUP

image-20230630143323612

  1. resources可配置参数

不同的apiGroups对应的resource是不一样的。

比如 “”组有pods,nodes等等resources

apps组才有deployments

就是和代码开发对应的

  1. verbs可配置参数
    “get”,“list”,“watch”, “create”,“update”, “patch”, “delete”,“exec”

  2. resourcesNames

    上面三个是必填的,这个resourcesNames是可选的

    他的含义是可以对具体某个资源进行限制。比如对名字是aaa的pod进行授权

RoleBinding 和 ClusterRoleBinding

我们定义了role,但现在role只是被定义出来,并没有实际的作用。因为现在role还是孤零零的存在,需要将role和用户进行绑定,才能真正发挥作用。

img

role可以和三种对象subjects绑定,一种是User,一个是Group,最重要的一个就是serviceaccount。

和useraccount绑定

1
2
3
4
5
6
7
8
9
10
11
12
13
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: test-rolebinding
namespace: test
subjects:
- kind: User # 权限资源类型
name: yys # 名称
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role #要绑定的Role的类型(可以是Role或ClusterRole)
name: test-role # Role的名称
apiGroup: rbac.authorization.k8s.io

这个绑定的含义是,给用户yys绑定了test-role角色,也就是yys拥有了对名字为 test的namespace 中,对Pods有get,watch,list的权限

实验

用root用户操作

  1. 新建用户yys

    1
    2
    3
    4
    [root@paas-m-k8s-master-1 test]# useradd yys
    [root@paas-m-k8s-master-1 test]# su - yys
    [yys@paas-m-k8s-master-1 ~]$ kubectl -n test get pod
    The connection to the server localhost:8080 was refused - did you specify the right host or port?

    默认肯定是访问不到的,如果想要访问,必须要创建用户的访问证书

  2. 创建访问证书

    1
    2
    3
    4
    5
    6
    7
    8
    9
    ## 下载证书生成工具 cfssl
    # wget https://pkg.cfssl.org/R1.2/cfssl_linux-amd64
    # wget https://pkg.cfssl.org/R1.2/cfssljson_linux-amd64
    # wget https://pkg.cfssl.org/R1.2/cfssl-certinfo_linux-amd64

    ## 给执行权限
    # chmod +x *
    # mkdir cert
    # cd cert

    创建CA证书签名请求JSON文件 yys-csr.json

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    {
    "CN": "yys", # 用户名称,必须填你的用户名,因为api-server鉴权时就用证书的CN字段作为用户名
    "hosts": [], # 主机地址,不填表示所有主机都可使用
    "key": {
    "algo": "rsa", # 加密算法
    "size": 2048
    },
    "names": [
    {
    "C": "CN",
    "L": "BeiJing",
    "O": "OKK",
    "ST": "BeiJing",
    "OU": "System"
    }
    ]
    }

    开始创建访问证书

    1
    2
    # cd /etc/kubernetes/pki/
    # /home/yys/cfssl_linux-amd64 gencert -ca=ca.crt -ca-key=ca.key -profile=kubernetes /home/yys/cert/yys-csr.json | /home/yys/cfssljson_linux-amd64 -bare yys

    image-20230630150228491

    执行成功后多出这三个文件

  3. 为yys用户生成集群kubeconfig文件

    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
    ## 给用户创建kubeconfig文件
    [root@paas-m-k8s-master-1 cert]# pwd
    /home/yys/cert
    [root@paas-m-k8s-master-1 cert]# kubectl config set-cluster kubernetes --certificate-authority=/etc/kubernetes/pki/ca.crt --embed-certs=true --server=https://你的master的ip:6443 --kubeconfig=yys.kubeconfig
    Cluster "kubernetes" set.
    [root@paas-m-k8s-master-1 cert]# ll
    总用量 8
    -rw-rw-r-- 1 yys yys 231 6月 30 15:00 yys-csr.json
    -rw------- 1 root root 1570 6月 30 15:07 yys.kubeconfig


    ## 设置客户端参数,绑定用户信息至kubeconfig中
    [root@paas-m-k8s-master-1 cert]# kubectl config set-credentials yys \
    --client-certificate=/etc/kubernetes/pki/yys.pem \
    --client-key=/etc/kubernetes/pki/yys-key.pem \
    --embed-certs=true \
    --kubeconfig=yys.kubeconfig
    User "yys" set.

    ## 设置上下文参数
    [root@paas-m-k8s-master-1 cert]# kubectl config set-context kubernetes \
    --cluster=kubernetes \
    --user=yys \
    --namespace=test \
    --kubeconfig=yys.kubeconfig
    Context "kubernetes created.
  4. 把kubeconfig文件复制到 用户的home目录的.kube下

    1
    2
    3
    mkdir -p /home/yys/.kube
    cp /home/yys/cert/yys.kubeconfig /home/yys/.kube/config
    chown -R yys:yys /home/yys/.kube
  5. 切换用户为yys,绑定kubectl读取config

    1
    2
    3
    4
    [root@paas-m-k8s-master-1 .kube]# su - yys
    [yys@paas-m-k8s-master-1 ~]$ kubectl config use-context kubernetes --kubeconfig=/home/yys/.kube/config
    Switched to context "kubernetes".

  6. 测试

    1
    2
    [yys@paas-m-k8s-master-1 ~]$ kubectl get pod
    Error from server (Forbidden): pods is forbidden: User "yys" cannot list resource "pods" in API group "" in the namespace "test"

    apply 绑定

    1
    2
    切回root用户
    # kc apply -f test-rolebing.yaml

    应用rolebinding

    再切回yys用户

    1
    2
    3
    4
    5
    6
    [yys@paas-m-k8s-master-1 .kube]$ kubectl get pod
    NAME READY STATUS RESTARTS AGE
    jdktest-9302-e2y-1li-f283862d4944-job-9d9l8 0/1 Error 0 638d
    nginx-volume-host 1/1 Running 0 217d
    ...
    pcloud-testjjj-latest-7t9-esm-e2487db22694-job-29492 0/1 Completed 0 710d

    打印的就是test namespace下的pod。因为我们创建这个Context时已经制定了namespace就是test

    如果干点别的

    1
    2
    [yys@paas-m-k8s-master-1 ~]$ kubectl get ns
    Error from server (Forbidden): namespaces is forbidden: User "yys" cannot list resource "namespaces" in API group "" at the cluster scope

    会报没权限

和serviceaccount绑定

我们已经知道,ServiceAccount的主要作用是pod和apiserver交互。我们编写的程序默认是不能直接调用api-server的,需要对pod进行合适的授权,这个授权就是通过和ServiceAccount绑定来做的。

关于ServiceAccount的介绍,请看这里

说说K8S中的ServiceAccount (qq.com)

在这篇文章里,我们创建了一个命名为mytest的serviceaccount。创建了一个名为test-sa的pod使用这个sa。

我们继续沿用。

实验

进入这个pod

1
2
3
4
5
6
[root@paas-m-k8s-master-1 test]# kc exec -it test-sa -- /bin/sh
#
# cd /var/run/secrets/kubernetes.io/serviceaccount
# ls
ca.crt namespace token
#

现在访问一下api-server

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# export CURL_CA_BUNDLE=/var/run/secrets/kubernetes.io/serviceaccount/ca.crt
# TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
#
# curl -H "Authorization: Bearer $TOKEN" https://kubernetes/api/v1/namespaces/default/pods
{
"kind": "Status",
"apiVersion": "v1",
"metadata": {

},
"status": "Failure",
"message": "forbidden: User \"system:serviceaccount:default:mytest\" cannot get path \"/\"",
"reason": "Forbidden",
"details": {

},
"code": 403
}#

可以看到现在调用api-server是Forbidden的。

绑定role

新建一个role,并进行roleBinding。

写在一起了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
namespace: default
name: role-demo
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "watch", "list"]
nonResourceURLs: []
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: rolebinding-demo
namespace: default
subjects:
- kind: ServiceAccount
name: mytest
namespace: default
roleRef:
kind: Role
name: role-demo
apiGroup: rbac.authorization.k8s.io

现在再进到之前的pod里

1
2
curl -H "Authorization: Bearer $TOKEN" https://kubernetes/api/v1/namespaces/default/pods

image-20230704160100890

可以看到已经可以调用了,说明通过roleBinding赋予了pod调用api-server的权限

第一步:S2i CLI 构建项目目录

S2I 命令行工具 带有一个方便的命令,可以引导构建器所需的目录结构。

安装 S2I CLI:

1
2
3
4
5
$ wget https://github.com/openshift/source-to-image/releases/download/v1.3.1/source-to-image-v1.3.1-a5a77147-linux-amd64.tar.gz

$ tar -zxvf source-to-image-v1.3.1-a5a77147-linux-amd64.tar.gz

$ cp s2i /usr/local/bin

创建镜像构建器

1
$ s2i create nerd-java8 nerd-java8-builder-docs

nerd-java8作为构建器镜像的名字,s2i-builder-docs是创建的初始目录名,目录结构如下所示。

1
2
3
4
5
6
7
8
9
10
nerd-java8-builder-docs]/
Dockerfile - 一个标准的Dockerfile,定义了构建器镜像。
Makefile - 用于测试和构建构建器镜像的帮助脚本。
test/
run - 测试脚本,测试构建器镜像是否正常工作。
test-app/ - 用于测试的应用程序的目录
s2i/bin
assemble - 负责构建应用程序的脚本
run - 负责运行应用程序的脚本
usage - 负责打印构建器镜像用法的脚本

第二步:修改 Dockerfile

如下修改 Dockerfile来定义构建器镜像。

Dockerfile

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
# nginx-centos7
FROM kubespheredev/s2i-base-centos7:1


LABEL maintainer="Runze Xia <runzexia@yunify.com>"

# 声明当前应用的版本
ENV NGINX_VERSION=1.6.3


LABEL io.k8s.description="Nginx Webserver" \
io.k8s.display-name="Nginx 1.6.3" \
io.kubesphere.expose-services="8080:http" \
io.kubesphere.tags="builder,nginx,html"

# 安装nginx并且清理yum cache
RUN yum install -y epel-release && \
yum install -y --setopt=tsflags=nodocs nginx && \
yum clean all

# 修改nginx的默认开放端口
RUN sed -i 's/80/8080/' /etc/nginx/nginx.conf
RUN sed -i 's/user nginx;//' /etc/nginx/nginx.conf

# 将S2I的脚本复制到构建器镜像当中
COPY ./s2i/bin/ /usr/libexec/s2i

RUN chown -R 1001:1001 /usr/share/nginx
RUN chown -R 1001:1001 /var/log/nginx
RUN chown -R 1001:1001 /var/lib/nginx
RUN touch /run/nginx.pid
RUN chown -R 1001:1001 /run/nginx.pid
RUN chown -R 1001:1001 /etc/nginx

USER 1001

# 声明默认使用的端口
EXPOSE 8080

# 修改构建器的默认启动命令,以展示构建器镜像的用法
CMD ["/usr/libexec/s2i/usage"]

S2I 脚本中会根据 Dockerfile 中定义的 Label 信息作为使用参数,如果使用非 KubeSphere 提供的基础镜像,请参考 S2I Script

第三步 处理S2I构建器脚本

当我们完成了 Dockerfile的定义,我们现在可以完成构建器镜像的其他部分。我们现在添加S2I脚本,我们将从 assemble(负责构建应用程序)开始。如下编辑 assemble文件,在我们的例子中,它只是把 nginx的配置文件以及静态内容复制到目标容器中:

assemble

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/bin/bash -e

if [[ "$1" == "-h" ]]; then
exec /usr/libexec/s2i/usage
fi

echo "---> Building and installing application from source..."
if [ -f /tmp/src/nginx.conf ]; then
mv /tmp/src/nginx.conf /etc/nginx/nginx.conf
fi

if [ "$(ls -A /tmp/src)" ]; then
mv /tmp/src/* /usr/share/nginx/html/
fi

默认情况下,s2i build将应用程序源代码放在 /tmp/src目录中,在上面的命令当中,我们将应用源代码复制到了了 kubespheredev/s2i-base-centos7:1定义的工作目录 /opt/app-root/src当中。

现在我们可以来处理第二个脚本 run(用于启动应用程序),在我们的例子当中,它只是启动 nginx服务器:

run

1
2
3
#!/bin/bash -e

exec /usr/sbin/nginx -g "daemon off;"

我们使用 exec命令将执行 run脚本替换为执行 nginx服务器的主进程。我们这样做是为了让所有 docker发出的信号都可以被 nginx收到,并且可以让 nginx使用容器的标准输入和标准输出流。

我们在例子当中被没有实现增量构建,因此我们可以直接删除 save-artifacts脚本。

最后我们在 usage脚本当中添加一些使用信息:c

usage

1
2
3
4
5
6
7
8
9
10
#!/bin/bash -e
cat <<EOF
This is the nginx-centos7 S2I image:
To use it, install S2I: https://github.com/kubesphere/s2i-operator
Sample invocation:
s2i build test/test-app kubespheredev/nginx-centos7 nginx-centos7-app
You can then run the resulting image via:
docker run -d -p 8080:8080 nginx-centos7-app
and see the test via http://localhost:8080
EOF

第四步 构建与运行

当我们完成了 Dockerfile和 S2I 的脚本,我们现在修改一下 Makefile当中的镜像名称:

Makefile

1
2
3
4
5
6
7
8
9
10
IMAGE_NAME = kubespheredev/nginx-centos7-s2ibuilder-sample

.PHONY: build
build:
docker build -t $(IMAGE_NAME) .

.PHONY: test
test:
docker build -t $(IMAGE_NAME)-candidate .
IMAGE_NAME=$(IMAGE_NAME)-candidate test/run

可以执行 make build命令来构建我们的构建器镜像了。

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
$ make build
docker build -t kubespheredev/nginx-centos7-s2ibuilder-sample .
Sending build context to Docker daemon 164.9kB
Step 1/17 : FROM kubespheredev/s2i-base-centos7:1
---> 48f8574c05df
Step 2/17 : LABEL maintainer="Runze Xia <runzexia@yunify.com>"
---> Using cache
---> d60ebf231518
Step 3/17 : ENV NGINX_VERSION=1.6.3
---> Using cache
---> 5bd34674d1eb
Step 4/17 : LABEL io.k8s.description="Nginx Webserver" io.k8s.display-name="Nginx 1.6.3" io.kubesphere.expose-services="8080:http" io.kubesphere.tags="builder,nginx,html"
---> Using cache
---> c837ad649086
Step 5/17 : RUN yum install -y epel-release && yum install -y --setopt=tsflags=nodocs nginx && yum clean all
---> Running in d2c8fe644415

…………
…………
…………

Step 17/17 : CMD ["/usr/libexec/s2i/usage"]
---> Running in c24819f6be27
Removing intermediate container c24819f6be27
---> c147c86f2cb8
Successfully built c147c86f2cb8
Successfully tagged kubespheredev/nginx-centos7-s2ibuilder-sample:latest

可以看到我们的镜像已经构建成功了,现在我们执行 s2i build ./test/test-app kubespheredev/nginx-centos7-s2ibuilder-sample:latest sample app命令来构建我们的应用镜像。

1
2
3
$ s2i build ./test/test-app(源码目录) kubespheredev/nginx-centos7-s2ibuilder-sample:latest sample-app
---> Building and installing application from source...
Build completed successfully

当我们完成了应用镜像的构建,我们可以在本地运行这个应用镜像看构建出的应用是否符合我们的要求:

1
2
$ docker images
$ docker run -p 8080:8080 sample-app

在浏览器中访问,可以看到我们的网页已经可以正常访问了:

img

第五步 推送镜像并在 KubeSphere 添加 S2I 模版

S2I 模版定义了应用程序构建的基础环境,包括构建器镜像 (Builder Image) 和 运行时镜像 (Runtime Image),以及环境参数、描述信息等。

当我们在本地完成 S2I 构建器镜像的测试之后,就可以推送镜像到自定义的镜像仓库当中,并创建构建器模版 yaml文件:

s2ibuildertemplate.yaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
apiVersion: devops.kubesphere.io/v1alpha1
kind: S2iBuilderTemplate
metadata:
labels:
controller-tools.k8s.io: "1.0"
builder-type.kubesphere.io/s2i: "s2i"
name: nginx-demo
spec:
containerInfo:
- builderImage: kubespheredev/nginx-centos7-s2ibuilder-sample
codeFramework: nginx # 代码框架类型
defaultBaseImage: kubespheredev/nginx-centos7-s2ibuilder-sample # 默认使用的构建器镜像 (可替换为自定义的镜像)
version: 0.0.1 # 构建器模版的版本
description: "This is a S2I builder template for Nginx builds whose result can be run directly without any further application server.." # 构建器模版的描述信息

提示:S2I 模版中 label 作为前端分类的参数信息,详见本文底部的 S2I 参数释义表。

在创建好构建器模版后我们可以使用 kubectl将构建器模版提交到 KubeSphere 环境当中:

1
2
$ kubectl apply -f s2ibuildertemplate.yaml
s2ibuildertemplate.devops.kubesphere.io/nginx created

现在我们来到 KubeSphere 的控制台界面,我们已经可以选择添加的构建器模版了:

img

至此我们就完成了 S2I 构建器镜像与构建器模版的创建。类似地,可以参考上述步骤,您可以基于 S2I CLI 自定义任何所需的 S2I 模板,然后在 KubeSphere 构建所需的镜像并一键部署至 Kubernetes 环境中,方便快速与多次构建环境。

S2I 模板参数释义表

S2I 模版中 label 作为前端分类的参数信息,其详细描述如下表所示。

label名称 选项 含义
builder-type.kubesphere.io/s2i: “s2i” “s2i” 描述该模版类型属于 S2I,即基于应用源代码进行构建
builder-type.kubesphere.io/b2i “b2i” 描述该模版类型属于 B2I, 即基于二进制或其他制品进行构建
binary-type.kubesphere.io “jar”,”war”,”binary” 描述该模版类型属于 B2I 下的细分类型,当选择 “B2I” 类型是需要加上此 label。比如提供的 jar 包则可以选择 “jar” 类型,其他类似。在 v2.1.1 版本中支持自定义 B2I 模版,例如 “nginx” 等

S2I 模版详细参数信息如下表所示,其中带有 * 的参数表示必填项:

修改项 类型 含义
*containerInfo []struct 构建镜像的相关信息列表
*containerInfo.builderImage string S2I 构建器镜像,比如:kubesphere/java-8-centos7:v2.1.0
containerInfo.runtimeImage string S2I 运行时镜像镜像,比如:kubesphere/java-8-runtime:v2.1.0
containerInfo.buildVolumes []string 挂载卷信息列表,格式”volume_name:mount_path”,比如:[“s2i_java_cache:/tmp/artifacts”,”test_cache:test_path”]
containerInfo.runtimeArtifacts []struct 输出制品的原路径和目标路径列表,仅用于分阶段构建时使用
containerInfo.runtimeArtifacts.source string 制品在构建器镜像中的原路径
containerInfo.runtimeArtifacts.destination string 移动到运行时镜像中的目标路径
containerInfo.runtimeArtifacts.keep bool 是否将数据保留在最终镜像中
*defaultBaseImage string 默认使用的构建器镜像
*codeFramework string 代码框架类型,比如 Java、Ruby 等
environment []struct 列表,用于设置一系列构建过程中的环境变量
environment.key string 环境变量名称
environment.type string 环境变量值类型
environment.description string 环境变量描述信息
environment.optValues []string 环境变量参数列表
environment.required bool 是否必须设置该环境变量
environment.defaultValue string 环境变量默认值
environment.value string 环境变量值
iconPath string 应用名称
version string S2I 模版版本
description string 用于描述此模版的功能、用途等

Sealos 4.0.0

经验教训:

  1. /根目录需要大一些,镜像很大。300G
  2. 镜像很大,安装的时间准备的长一些
  3. 使用nohup,避免中途断网

如果磁盘不是根目录,要创建软连接

1
2
3
4
5
6
mkdir -p /data/run/containerd
mkdir -p /data/var/lib/containers
mkdir -p /data/var/lib/kubelet
ln -s /data/run/containerd /run/containerd
ln -s /data/var/lib/containers /var/lib/containers
ln -s /data/var/lib/kubelet /var/lib/kubelet

下载sealos命令行工具

提前安装一下jq工具

1
yum install -y jq

查看当前的sealos命令行工具版本

1
curl --silent "https://api.github.com/repos/labring/sealos/releases" | jq -r '.[].tag_name'

选其中的稳定版

1
2
3
4
5
6
7
8
9
10
v5.0.0-beta5
v5.0.0-beta4
v4.4.0-beta3
v5.0.0-beta3
v5.0.0-beta2
v5.0.0-beta1
v4.3.7
v5.0.0-alpha2
v4.3.7-rc1
v4.3.6

我这里用最新的稳定版4.3.7

下载

1
wget https://github.com/labring/sealos/releases/download/v4.3.7/sealos_4.3.7_linux_amd64.tar.gz

安装

1
tar zxvf sealos_4.3.7_linux_amd64.tar.gz sealos && chmod +x sealos && mv sealos /usr/bin

配置服务器环境

服务器做免密

在其中一台master上执行sealos命令,要配置这台master到其他所有服务器的免密登录

1
2
3
4
5
6
ssh-keygen
cd .ssh
cat >> .ssh/authorized_keys << EOF master0的id_rsa.pub的内容 EOF
firewall-cmd --permanent --add-rich-rule="rule family='ipv4' source address="10.xx.xx.194" service name='ssh' accept"
firewall-cmd --reload
echo "sshd: master节点的ip" >> /etc/hosts.allow

关闭selinux

关闭selinux以允许容器访问宿主机的文件系统

1
2
3
# 查看,我的服务器交付时就已经关了,
[root@my-paas-master0 ~]# getenforce
Permissive

关闭方法:

1
2
setenforce 0  # 临时关闭
sed -i 's/^SELINUX=enforcing$/SELINUX=permissive/' /etc/selinux/config # 永久关闭,reboot生效

关闭swap

因为我们在oom时应该干脆的杀死应用,而不是用swap续命,引发级联故障。

1
2
3
4
5
# swap total是0。我的服务器交付时默认关闭了swap
[root@my-paas-master0 ~]# free -g
total used free shared buff/cache available
Mem: 7 0 6 0 0 7
Swap: 0 0 0

关闭方法:

1
2
swapoff -a
sed -i '/ swap / s/^\(.*\)$/#\1/g' /etc/fstab

打开NAT转发

如果没有开启包转发功能。那么系统只处理目的地为本机的数据包,不会转发发往其他地址的数据包。表现为如果使用nodeport的service,只有pod启动在这台服务器的服务器可以访问,其他服务器无法访问。

1
2
3
4
5
firewall-cmd --add-masquerade --permanent

# 检查是否允许NAT转发
firewall-cmd --query-masquerade

防火墙端口

测试环境我都是直接关了防火墙,生产环境不合适

官方文档 Ports and Protocols | Kubernetes

![image-20231019172733543](D:\github\docs\云原生\k8s\Sealos 4.0.0.assets\image-20231019172733543.png)

![image-20231019172850695](D:\github\docs\云原生\k8s\Sealos 4.0.0.assets\image-20231019172850695.png)

k8s master需要开启以下端口

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

firewall-cmd --permanent --add-port=6443/tcp

firewall-cmd --permanent --add-port=2379-2380/tcp

firewall-cmd --permanent --add-port=10250-10259/tcp

//firewall-cmd --permanent --add-port=10259/tcp

//firewall-cmd --permanent --add-port=10257/tcp

# 如果要让master也可以当作nodeport的暴露ip使用
firewall-cmd --permanent --add-port=30000-32767/tcp

# 还有几个官方文档没写,但是根据经验和踩坑教训的,一起开了。多开几个也没事
//firewall-cmd --permanent --add-port=10251/tcp
//firewall-cmd --permanent --add-port=10252/tcp
//firewall-cmd --permanent --add-port=10255/tcp
firewall-cmd --permanent --add-port=8472/udp
firewall-cmd --permanent --add-port=443/tcp
firewall-cmd --permanent --add-port=443/udp
firewall-cmd --permanent --add-port=53/udp
firewall-cmd --permanent --add-port=53/tcp
firewall-cmd --permanent --add-port=9153/tcp

firewall-cmd --reload

k8s node需要开启以下端口

1
2
3
4
5
firewall-cmd --permanent --add-port=10250/tcp

firewall-cmd --permanent --add-port=30000-32767/tcp

firewall-cmd --reload

如果你使用了istio还有把istio-pilot的端口加到防火墙里:

1
firewall-cmd --permanent --add-port=15010-15014/tcp

配置linux最大线程数,最大文件数配置::

1
2
3
4
5
echo "fs.file-max = 655350" >> /etc/sysctl.conf
echo "kernel.pid_max = 655350" >> /etc/sysctl.conf

echo "root soft nofile 655350" >> /etc/security/limits.conf
echo "root hard nofile 655350" >> /etc/security/limits.conf

安装socat

socat是一个网络工具, k8s 使用它来进行 pod 的数据交互

1
yum install -y socat

加载br_netfilter模块

1
modprobe  br_netfilter

配置了免密登录,不需要密码

1
2
3
4
5
6
nohup sealos run \
--masters 10.xx.xx.194 \
--nodes 1x.xx.12.209,1x.xx.12.216 \
labring/kubernetes:v1.25.0 \
labring/helm:v3.8.2 \
labring/calico:v3.24.1 >sealos.log 2>&1 &

如果有报错,先清理再重新安装

1
sealos reset

起个nginx测试一下

1
2
kubectl run ng --image=harbor-test.xxx.net/base/nginx:1.25.2
kubectl expose pod ng --port=80 --target-port=80 --type=NodePort

![image-20231020141624870](D:\github\docs\云原生\k8s\Sealos 4.0.0.assets\image-20231020141624870.png)

把防火墙打开

1
systemctl start firewalld

还是可以的,没问题

安装kubesphere

安装nfs

查看nfs的端口

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
# rpcinfo -p
program vers proto port service
100000 4 tcp 111 portmapper
100000 3 tcp 111 portmapper
100000 2 tcp 111 portmapper
100000 4 udp 111 portmapper
100000 3 udp 111 portmapper
100000 2 udp 111 portmapper
100024 1 udp 59758 status
100024 1 tcp 33494 status
100005 1 udp 20048 mountd
100005 1 tcp 20048 mountd
100005 2 udp 20048 mountd
100005 2 tcp 20048 mountd
100005 3 udp 20048 mountd
100005 3 tcp 20048 mountd
100003 3 tcp 2049 nfs
100003 4 tcp 2049 nfs
100227 3 tcp 2049 nfs_acl
100003 3 udp 2049 nfs
100003 4 udp 2049 nfs
100227 3 udp 2049 nfs_acl
100021 1 udp 35373 nlockmgr
100021 3 udp 35373 nlockmgr
100021 4 udp 35373 nlockmgr
100021 1 tcp 22351 nlockmgr
100021 3 tcp 22351 nlockmgr
100021 4 tcp 22351 nlockmgr

nfs服务需要开启 mountd,nfs,nlockmgr,portmapper,rquotad这5个服务

nfs 和 portmapper两个服务是固定端口的,nfs为2049,portmapper为111。其他的3个服务是用的随机端口。

把这些端口全部加到防火墙里

1
2
3
4
5
6
7
8
9
10
firewall-cmd --permanent --add-port=111/tcp
firewall-cmd --permanent --add-port=111/udp
firewall-cmd --permanent --add-port=2049/tcp
firewall-cmd --permanent --add-port=2049/udp
firewall-cmd --permanent --add-port=59758/udp
firewall-cmd --permanent --add-port=33494/tcp
firewall-cmd --permanent --add-port=20048/tcp
firewall-cmd --permanent --add-port=20048/udp
firewall-cmd --permanent --add-port=22351/tcp
firewall-cmd --permanent --add-port=35373/udp

每台服务器都不一样,需要手动加特别麻烦

1
for i in $(rpcinfo -p | awk 'NR>1 {print $4}' | sort -u);do firewall-cmd --permanent --add-port=$i/tcp; firewall-cmd --permanent --add-port=$i/udp;done

写了个脚本,自动加一下

1
2
3
mkdir -p /data/nfsstorage
mount -t nfs 10.xx.xx.194:/data/nfsstorage /data/nfsstorage
showmount -e 10.xx.xx.194

kubesphere需要开的端口.(猜测,解决问题过程发现的,还需要验证)

1
firewall-cmd --permanent --add-port=9115/tcp

calico需要的端口

查看官方文档,需要开通下列端口

System requirements | Calico Documentation (tigera.io)

![image-20231024092357785](D:\github\docs\云原生\k8s\Sealos 4.0.0.assets\image-20231024092357785.png)

1
2
3
4
5
6
firewall-cmd --permanent --add-port=179/tcp
firewall-cmd --permanent --add-port=4789/udp
firewall-cmd --permanent --add-port=5473/tcp
firewall-cmd --permanent --add-port=51820/udp
firewall-cmd --permanent --add-port=51821/udp
firewall-cmd --permanent --add-port=2379/tcp

但是好像不准,我用nmap -v -A localhost在其他机器上看到还用了9999端口

1
firewall-cmd --permanent --add-port=9999/tcp

问题

发现还需要一个5443端口

1
firewall-cmd --permanent --add-port=5443/tcp

kubesphere需要的端口

其他的前面已经开通了,追加一下几个

1
2
3
firewall-cmd --permanent --add-port=9090/tcp
firewall-cmd --permanent --add-port=9099-9100/tcp
firewall-cmd --permanent --add-port=8443/tcp

不行,还是有问题。目前只能还是关闭防火墙

kubesphere 默认密码P@88w0rd

改成 xxxPaasNo1

启用插件

clusterconfiguration

![image-20220817161651571](D:\github\docs\paas平台建设\云原生\k8s\Sealos 4.0.0.assets\image-20220817161651571.png)

nerdctl

1
2
3
4
5
6
7
8
wget http://1x.xx.66.1/4zLKJ/nerdctl-full-0.22.2-linux-amd64.tar.gz

mkdir nerdctl
tar -zxvf nerdctl-full-0.22.2-linux-amd64.tar.gz -C nerdctl
cp nerdctl/lib/systemd/system/*.service /etc/systemd/system/

systemctl enable buildkit containerd
systemctl start buildkit containerd

mkdir -p /usr/local/containerd/bin && tar -zxvf nerdctl-full-0.22.2-linux-amd64.tar.gz nerdctl && mv nerdctl /usr/local/containerd/bin

wget http://transfer.paas.xxx.net/1usiGhY/buildkit-v0.10.3.linux-amd64.tar.gz

1
2
3
tar -zxvf buildkit-v0.10.3.linux-amd64.tar.gz -C /usr/local/containerd/
ln -s /usr/local/containerd/bin/buildkitd /usr/local/bin/buildkitd
ln -s /usr/local/containerd/bin/buildctl /usr/local/bin/buildctl

创建systemd服务相关文件 /etc/systemd/system/buildkit.socket

1
2
3
4
5
6
7
8
9
[Unit]
Description=BuildKit
Documentation=https://github.com/moby/buildkit

[Socket]
ListenStream=%t/buildkit/buildkitd.sock

[Install]
WantedBy=sockets.target

/etc/systemd/system/buildkit.service

1
2
3
4
5
6
7
8
9
10
[Unit]
Description=BuildKit
Requires=buildkit.socket
After=buildkit.socketDocumentation=https://github.com/moby/buildkit

[Service]
ExecStart=/usr/local/bin/buildkitd --oci-worker=false --containerd-worker=true

[Install]
WantedBy=multi-user.target

启动buildkitd

1
2
3
systemctl daemon-reload
systemctl enable buildkit
systemctl start buildkit

外网互通场景安装

需要路由器配置流量转发

1
2
yum install traceroute.x86_64 -y
yum install net-tools -y

生成配置文件

1
2
3
4
5
6
sealos gen labring/kubernetes:v1.22.11 \
labring/calico:v3.22.1 \
labring/openebs:v1.9.0 \
registry.cn-shenzhen.aliyuncs.com/cnmirror/kubesphere:v3.3.0 \
--masters 1x.xx.232.237,1x.xx.232.238,1x.xx.232.239 \
--nodes 1x.xx.232.233,1x.xx.232.234,1x.xx.232.236 > Clusterfile

修改配置文件

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
apiVersion: apps.sealos.io/v1beta1
kind: Cluster
metadata:
creationTimestamp: null
name: default
spec:
hosts:
- ips:
- 1x.xxx.5.6:22
roles:
- master
- amd64
- ips:
- 1x.xxx.5.7:22
- 1x.xxx.5.8:22
- 1x.xxx.5.9:22
- 1x.xxx.5.10:22
roles:
- node
- amd64
image:
- labring/kubernetes:v1.22.11
- labring/calico:v3.22.1
- labring/openebs:v1.9.0
- registry.cn-shenzhen.aliyuncs.com/cnmirror/kubesphere:v3.3.0
ssh:
pk: /root/.ssh/id_rsa
port: 22
user: root
status: {}
---
apiVersion: kubeadm.k8s.io/v1beta2
kind: ClusterConfiguration
networking:
podSubnet: 1x.xxx.36.0/22
---
apiVersion: apps.sealos.io/v1beta1
kind: Config
metadata:
name: calico
spec:
path: manifests/calico.yaml
data: |
apiVersion: operator.tigera.io/v1
kind: Installation
metadata:
name: default
spec:
# Configures Calico networking.
calicoNetwork:
bgp: Enabled
# Note: The ipPools section cannot be modified post-install.
ipPools:
- blockSize: 26
# Note: Must be the same as podCIDR
cidr: 1x.xxx.36.0/22
encapsulation: None
natOutgoing: Enabled
nodeSelector: all()
nodeAddressAutodetectionV4:
interface: "eth.*|en.*"
1
1x.xxx.40.0/21 给  生产dubbo   网关 1x.xxx.47.254 子网掩码255.255.248.0
1
1x.xxx.36.0/22 给 测试dubbo    网关 1x.xxx.39.254

部署

1
nohup sealos apply -f Clusterfile  >sealos.log 2>&1 &
  1. prometheus开启失败

    查看日志MountVolume.SetUp failed for volume “secret-kube-etcd-client-certs” : secret “kube-etcd-client-certs” not found

    解决:

1
kubectl -n kubesphere-monitoring-system create secret generic kube-etcd-client-certs

ldap

1
kubectl -n kubesphere-system edit cc ks-installer

配置authentication字段

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

authentication:
jwtSecret: ''
maximumClockSkew: 10s
multipleLogin: true
oauthOptions:
accessTokenMaxAge: 0
accessTokenInactivityTimeout: 30m
identityProviders:
- name: ldap
type: LDAPIdentityProvider
mappingMethod: auto
provider:
host: 1x.xxx.7.142:389
managerDN: cn=hopuser,o=services
managerPassword: hopuser@2014
userSearchBase: o=xxx
loginAttribute: cn
mailAttribute: mail

查看ks-install的日志

1
kubectl logs -n kubesphere-system $(kubectl get pod -n kubesphere-system -l 'app in (ks-install, ks-installer)' -o jsonpath='{.items[0].metadata.name}') -f

结束之后

重启下api-server组件

1
kubectl -n kubesphere-system rollout restart deploy/ks-apiserver

如果还不行,openldap, controller-manager(不确定怎么好的)

1
2
kubectl -n kubesphere-system delete pod openldap-0
kubectl -n kubesphere-system rollout restart deploy/ks-controller-manager

文件在1x.xxx.2.103上,/root/ks 通过7.21上去

http://transfer.paas.xxx.net/1QJ9T6W/config.yaml

Dockerfile

1
2
3
4
5
6
FROM harbor-test.xxx.net/kubesphere/ks-console:v3.3.0
COPY ./logo.svg /opt/kubesphere/console/dist/assets/
COPY ./login-logo.svg /opt/kubesphere/console/dist/assets/
COPY ./favicon.ico /opt/kubesphere/console/dist/assets/
COPY ./locale-zh.03f0fb248751b0a3bd2d.json /opt/kubesphere/console/dist/
COPY ./config.yaml /opt/kubesphere/console/server/
1
2
3
docker build -t kubesphere/ks-console:v3.3.rrs .
docker tag kubesphere/ks-console:v3.3.rrs harbor-test.xxx.net/kubesphere/ks-console:v3.3.rrs
docker push harbor-test.xxx.net/kubesphere/ks-console:v3.3.rrs

不需要了

1
nerdctl pull harbor-test.xxx.net/kubesphere/ks-console:v3.3.rrs

然后修改ks-console的镜像

![image-20220818094957032](D:\github\docs\paas平台建设\云原生\k8s\Sealos 4.0.0.assets\image-20220818094957032-16607873976021.png)

![image-20220818095047981](D:\github\docs\paas平台建设\云原生\k8s\Sealos 4.0.0.assets\image-20220818095047981-16607874485993.png)

podman pull问题

1
* Error initializing source docker://registry.fedoraproject.org/java:openjdk-8-jre-alpine: Error reading manifest openjdk-8-jre-alpine in registry.fedoraproject.org/java: manifest unknown: manifest unknown

集群纳管

主集群中执行以下命令来获取jwtSecret

1
kubectl -n kubesphere-system get cm kubesphere-config -o yaml | grep -v "apiVersion" | grep jwtSecret

vakPJMEze4ws8mHgCq2jlvpVD3piOBhp

被纳管集群执行

1
kubectl edit cc ks-installer -n kubesphere-system

ks-installer 的 YAML 文件中对应输入上面所示的 jwtSecret

1
2
authentication:
jwtSecret: vakPJMEze4ws8mHgCq2jlvpVD3piOBhp

向下滚动并将 clusterRole 的值设置为 member

1
2
multicluster:
clusterRole: member

执行命令,查看进度

1
kubectl logs -n kubesphere-system $(kubectl get pod -n kubesphere-system -l 'app in (ks-install, ks-installer)' -o jsonpath='{.items[0].metadata.name}') -f

添加集群

获取被纳管集群的kubeconfig

1
cat $HOME/.kube/config

去主机群的页面,集群管理页面点击添加集群

问题:token not found in cache

原因其实是token过期了

ks-instaler里的auth配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
authentication:
jwtSecret: vakPJMEze4ws8mHgCq2jlvpVD3piOBhp
maximumClockSkew: 10s
multipleLogin: true
oauthOptions:
accessTokenInactivityTimeout: 30m
accessTokenMaxAge: 0
identityProviders:
- mappingMethod: auto
name: ldap
provider:
host: 1x.xxx.7.142:389
loginAttribute: cn
mailAttribute: mail
managerDN: cn=hopuser,o=services
managerPassword: hopuser@2014
userSearchBase: o=xxx
type: LDAPIdentityProvider

把这个accessTokenMaxAge设置成0,表示永不过期

之前设置成了10h,但是如果搭建的ks超过了10小时,再纳管就会过期。