1.1 Linux基础
本节内容首先介绍Linux系统的启动过程,方便读者理解后面容器技术的概念,更容易体会到容器技术的优势。然后介绍Linux系统的基本知识,以便可以更轻松地掌握后面的内容。
1.1.1 systemd
计算机在启动一个操作系统时必须加载并初始化操作系统,方能运行其他的应用程序,这是计算机初始化必不可少的一个启动过程,也就是说计算机启动需要一款初始化系统。systemd是目前Linux系统中最流行的初始化系统之一,能提高系统的启动效率与质量,它不仅可以让系统进程并行启动,还能很好地守护init进程,减少系统内存的不必要开销。
在systemd诞生之前,还有两个系统初始化工具,分别是systemvinit和upstart, systemvinit是一套传统的初始化系统,已经逐渐地淡出了Linux历史舞台,现已基本被systemd和upstart取而代之,systemd和upstart有各自的特点,不过目前已经有绝大多数的Linux发行版都默认使用systemd,比如Fedora、openSUSE、Ubuntu、Gentoo、Arch Linux等一系列Linux发行版。
1. systemd基础
Linux系统启动要执行的程序是非常多的,比如挂载文件系统、加载硬件设备、启动后台服务、激活交换分区、启动用户程序等。每一个执行任务被systemd称为一个配置单元(Unit)。换句话来说,挂载一个文件系统、加载硬件设备、启动系统后台服务均被称为一个配置单元。
每一个配置单元都有彼此对应的配置单元文件,比如Apache有对应的一个配置单元文件apache2.service、MySQL有对应的一个配置单元文件msqld.service。此类型的配置单元文件的编写既简单又简洁。便于Linux管理人员编辑维护这些配置单元。如下是systemd中的一个系统日志服务的配置单元文件syslog.service的内容。
[Unit] Description=System Logging Service #描述信息 Requires=syslog.socket #指定依赖 Documentation=man:rsyslogd(8) Documentation=http://www.rsyslog.com/doc/ [Service] Type=notify #服务类型 ExecStart=/usr/sbin/rsyslogd -n StandardOutput=null Restart=on-failure #指定失败时重启 [Install] WantedBy=multi-user.target Alias=syslog.service #别名
systemd的配置单元文件可以简单分为三个部分,分别为Uint、Service、Install。
● [Unit]区块通常是配置文件的第一个区块,用来定义Unit的元数据,以及配置与其他Unit的关系。
● [Install]通常是配置文件的最后一个区块,用来定义如何启动,以及是否开机启动。
● [Service]区块用来定义Service的配置,只有Service类型的Unit才有这个区块。
由于一个Linux操作系统有很多systemd的配置单元文件,并且不同的配置单元文件加载的顺序自然也是不一样的,也就是说,一个Linux操作系统有多个存放systemd配置单元的目录。以Ubuntu系统为例,systemd的配置单元文件相关的位置主要有如下这些目录:
root@ops-admin:~# find / -name systemd /sys/fs/cgroup/systemd /etc/systemd /etc/xdg/systemd /run/user/1000/systemd /run/systemd /lib/systemd /usr/share/doc/systemd /usr/share/systemd /usr/lib/systemd /var/lib/systemd
现在我们知道systemd是什么了,接下来看看systemd是如何工作的。
在systemd体系框架中,所有的服务程序都是可以并发进行启动的。比如Avahi、D-Bus、livirtd、X11、HAL可以同时启动。但有这样一个问题:Avahi需要syslog的服务,Avahi和syslog同时启动,倘若假设Avahi的启动比较快,syslog还没来得及启动,然而Avahi却需要记录日志,在这种情况下系统就会产生问题。
因此,systemd系统采用了各个服务之间互相依赖的解决方案,这种解决方案的相互依赖具体分成三种关系类型:socket依赖、D-Bus依赖以及文件系统依赖。每一种类型的依赖都可以通过相应的技术解除依赖关系,从而解决了所有服务程序并发启动冲突的问题。在systemd初始化系统机制中,不管程序的依赖关系如何,全部可以并行启动,若调用的服务程序存在依赖关系,则自动激活其他程序。
以Ubuntu系统为例,systemd的启动顺序如下:
(1)Boot Sequence,启动顺序,比如硬盘启动、软盘启动、U盘启动等;
(2)Bootloader,引导加载;
(3)kernel + initramfs(initrd),加载内核以及initramfs或initrd;
(4)rootfs,启动文件系统;
(5)/sbin/init,启动init进程。
对于systemd的所有详细启动顺序情况,我们可以使用systemd本身自带的systemd-analyze,它是一个分析启动性能的工具,用于分析启动时服务时间消耗。默认显示启动是内核和用户空间的消耗时间,下面简单介绍systemd-analyze的基本用法。
# 查看启动耗时 root@ops-admin:~# systemd-analyze # 查看每个服务的启动耗时 root@ops-admin:~# systemd-analyze blame # 显示瀑布状的启动过程流 root@ops-admin:~# systemd-analyze critical-chain # 显示指定服务的启动流 root@ops-admin:~# systemd-analyze critical-chain atd.service # 将systemd启动顺序以及消耗时间的详细信息生成svg root@ops-admin:~# systemd-analyze plot > message.svg
以Ubuntu为例,执行systemd-analyze plot > message.svg命令生成矢量图(如图1-1所示,图中仅显示部分单元启动时间),直观地显示了systemd启动顺序以及消耗的时间。
图1-1 systemd启动顺序及消耗的时间
2. systemctl
systemctl是一个systemd工具,主要负责控制systemd系统和服务管理器,systemctl工具集成了诸多的命令,使得管理员更好地管理Linux系统。
systemctl list-units命令可以查看当前系统的所有配置单元(Unit):
# 列出正在运行的Uint root@ops-admin:~# systemctl list-units # 列出所有的Uint root@ops-admin:~# systemctl list-uints --all # 列出所有没有运行的Uint root@ops-admin:~# systemctl list-units -all --state=inactive # 列出所有加载失败的Uint root@ops-admin:~# systemctl list-units --failed # 列出所有正在运行的、类型为service的Unit root@ops-admin:~# systemctl list-units --type=service
systemctl status命令用于查看系统状态和每个配置单元的状态:
# 显示系统状态 root@ops-admin:~# systemctl status # 显示单个Unit的状态,以mysql为例 root@ops-admin:~# systemctl status mysql.service # 显示远程主机的某个Unit的状态,以mysql为例 root@ops-admin:~# systemctl -H {user}@{ip} status mysql.service
systemctl {action}对于用户来说,是常使用的命令,用于启动(start)、停止(stop)、重加载(reload)以及重启(restart)配置单元(主要是service),下面主要以mysql为例:
# 显示Unit参数、启动、停止、杀死、重新加载配置等操作如下 root@ops-admin:~# systemctl <show/start/stop/restart/kill/reload> mysql.service # 显示某个Unit的指定属性的值 root@ops-admin:~# systemctl show -p CPUShares mysql.service # 重载所有修改过的配置文件 root@ops-admin:~# systemctl daemon-reload # 设置某个Unit的指定属性 root@ops-admin:~# sudo systemctl set-property mysql.service CPUShares=500
使用systemctl list-dependencies命令查看systemctl配置单元的依赖关系,以mysql为例:
# 列出一个mysql Unit的所有依赖。 root@ops-admin:~# systemctl list-dependencies mysql.service # 如果要展开Target,就需要使用--all参数。 root@ops-admin:~# systemctl list-dependencies --all mysql.service
systemctl enable/disable命令,用于将systemd配置单元激活或撤销开机启动,以mysql为例:
# 激活开机启动mysql服务 root@ops-admin:~# systemctl enable mysql.service # 撤销开机启动mysql服务 root@ops-admin:~# systemctl disable mysql.service
systemctl list-unit-files命令,用于查看配置单元的所有文件(Unit File)以及配置单元的状态(Status)情况:
# 列出所有配置文件 root@ops-admin:~# systemctl list-unit-files # 列出指定类型的配置文件 root@ops-admin:~# systemctl list-unit-files --type=service
Unit的状态包含如下4种情况。
● enabled:已建立启动链接,已激活即开机启动。
● disabled:没建立启动链接,已撤销开机启动。
● static:该配置文件没有[Install]部分,即无法被执行,只能作为其他配置文件的依赖。
● masked:该配置文件被禁止建立启动链接。
常用的systemd电源管理命令有如下几种:
# 分别为:重启机器、关机、挂起、休眠、混合休眠 root@ops-admin:~# systemctl <reboot/poweroff/suspend/hibernate/hybrid>
关于Linux电源管理的几个命令会因为发行版或者文件系统的原因有所不同,主要表现在休眠上,一些发行版不提供休眠选项,或者因为使用了不支持休眠的文件系统,例如Btrfs由于不支持swap交换分区,导致系统内存不能挂载到硬盘中,无法实现休眠。在本章中不会过多牵涉复杂的文件系统,如果没有特别指出,默认文件系统都是ext4。
除了systemctl命令,还有两个命令后面也会常用。第一个是hostnamectl命令,它可以查看与设置主机信息:
# 查看主机信息 root@ops-admin:~# hostnamectl # 设置主机信息,以设置hostname为例 root@ops-admin:~# hostnamectl set-hostname {$hostname}
第二个是timedatectl命令,用于管理时区:
# 查看时区日期等信息 root@ops-admin:~# timedatectl # 设置主机的时区 root@ops-admin:~# timedatectl set-timezone Asia/Shanghai # 将硬件时钟配置为地方时 root@ops-admin:~# timedatectl set-local-rtc true
注意:在执行上述的命令时,*.service的后缀.service是可以省略不写的,其执行的结果是一样的。关于更多的systemd内置命令请到systemd官方wiki查阅。
1.1.2 Shell脚本
从1.1.1节有关Linux的启动过程和系统服务管理的学习中可以知道,要想在Linux终端环境下行云流水地完成一系列操作,必要的命令和概念必须熟知,但这毕竟是一个积累的过程,在积累到一定量之前,还有一样必不可少的技能就是编写Shell脚本。Shell脚本简单来说就是一个自动化执行命令的命令集合,但它拥有丰富的内置变量以及完善的控制语句,因此,Shell脚本可以实现相当复杂的操作。
在学习Shell编程之前,本节内容会先介绍Linux下的权限系统,包括文件权限以及用户权限两大部分。在此之后,我们会进入基本的Shell编程知识讲解。在本节的最后,我们会通过几个例子,使用Shell脚本监控Linux服务器状态。
1. Linux权限认识
你可能听说过“Linux中一切皆文件”的说法。在认识Linux权限特点之前,不妨先看看Linux的系统目录结构,大家熟知的Windows目录系统结构与Unix/Linux大相径庭,毕竟两者在实现的机制上完全不一样。Windows上是通过硬盘分区(C:、D:、E:)分隔目录结构的;而Unix/Linux系统,所有的目录结构都在一个最高级别的根目录”/“ 下,根目录是所有目录的起始点,其下面的子目录是一个层次或树状结构,这些不同的目录可以分布在不同的硬盘分区,甚至不同的设备上。
Unix/Linux系统目录是树状目录结构,下面通过tree -L 1或者ls -a指令打印Unix/Linux的目录结构。了解Linux各个系统目录的作用,是学习Linux至关重要的一步,我们知道该系统的一切都是由文件组成的,并且目录结构都是由FHS(Filesystem Hierarchy Standard)规定好了的,当我们掌握目录的结构时,管理Linux将会变得井然有序。下面我们具体分析根目录下的目录对应存储的文件。
● /bin:二进制可执行文件。
● /boot:系统引导文件。
● /dev:设备文件。
● /etc:系统管理和配置文件。
● /home:用户的家目录,基本是每一个普通用户存放一个文件夹。
● /lib:标准程序设计库,又叫动态链接共享库library。
● /lost+found:正常的情况下,该目录为空,保存丢失文件。
● /media:系统会自动识别一些设备,如U盘、光驱等识别后,会把识别的设备挂载到这个目录下。
● /mnt:临时挂载别的文件系统。
● /opt:第三方软件默认安装的位置。
● /proc:虚拟的目录,它是系统内存的映射,可以通过访问这个目录来获取系统信息。
● /root:超级权限者的用户主目录。
● /sbin:即super bin,系统管理员使用的系统管理程序。
● /tmp:存放一些临时文件。
● /usr:最庞大的目录,要用到的应用程序和文件几乎都在这个目录下。
● /var:某些大文件的溢出区,比方说各种服务的日志文件。
了解根目录各个文件夹的作用有助于后续遇到问题时可以正确进入相应目录查看相关信息。在Unix/Linux系统中,每一个资源文件仅仅属于一个所有者和一个拥有组,这样设置的目的旨在提高文件的安全性。下面我们通过ls -la命令来看看文件或目录的所有者与拥有组的属性。
user@ops-admin:~$ ls -la total 1208 drwxrwxr-x 1 user user 532 Jul 2 22:29 Applications drwxr-xr-x 1 user user 204 Jun 26 01:05 CodeLab drwxr-xr-x 1 user user 20 Jul 6 21:50 Desktop drwxrwxr-x 1 user user 348 Jun 15 20:19 Documents drwxr-xr-x 1 user user 378 Jul 6 22:07 Downloads -rw-rw-r--1 user user 1229660 Jul 6 22:16 index.pdf drwxrwxr-x 1 user user 90 Jul 3 20:09 Share drwxrwxr-x 1 user user 240 Jul 6 22:26 Sync ......
通过ls -la命令打印的信息如上,第三列的user就是代表文件或目录等的所有者,第四列的user就是代表文件或目录等的拥有组。
先看第一列的信息,第一列的信息是由10个字符表示的。每一位或者每一段都有其作用意义。
第1位:代表文件类型(文件夹或者文件), d代表目录,-代表文件,l代表链接文件,b代表设备文件中可存储的接口设备,c代表设备文件中串行端口设备,比如鼠标、键盘等。
第2~4位:代表文件属组权限(所有者),简称u。
第5~7位:代表同组用户权限(拥有组),简称g。
第8~10位:代表其他用户权限(其他用户),简称o。
在Unix/Linux系统中,文件及目录的权限分为三种:可读( r | 4 )、可写( w | 2 )、可执行( e | 1 ),可读就是用户是否有权限查看文件的内容、可写就是用户是否由权限修改文件的内容、可执行就是用户是否具有权限执行可执行文件。就这三种权限而言,我们还应该了解文件与文件夹的权限类别,如表1-1所示。
表1-1 文件与文件夹的权限类别
将上面drwxr-xr-x 1 user user 20 Jul 6 21:50 Desktop这一行记录作为示例讲解,第1位的d代表该文件是目录,第2~4位的rwx代表目录所有者可读可写可执行的权限,第5~7位的r-x代表目录拥有组可读可执行的权限,注意,-代表没有权限的意思,第8~10位的r-x与拥有组的权限一样,都代表可读可执行的权限。文件权限详解如图1-2所示。
图1-2 文件权限详解
文件的权限除了使用字母表示以外,还可以使用八进制数字表示:
r == 4 ( read ) w == 2 ( write ) x == 1 ( execute )
同时,每一段字母的组合可以使用数字组合来表示,如下所示:
rwx == 4+2+1 = 7 r-x == 4+1 = 5 r-x == 4+1 = 5
rwer-xr-x就可以使用755八进制来表示。
现在已经大致清楚了文件权限的表示方法,接下来再看Linux的多用户机制,Linux可以允许多用户同时在线,分别执行各自的任务,而且互不干扰。对于服务器管理维护人员而言,必须对用户权限加以严格控制,避免因为人为的原因导致数据泄露;同时掌握管理多用户的任务调度有助于在运维时隔离一些进程,这也是服务安全的重要一环。
Unix/Linux是个多用户多任务的分时操作系统,用户的ID统称为UID。超级管理员root的UID为0,1~499之间的UID是为系统保留的,系统创建普通的用户时,这些用户的UID都是从500算起的。用户的类型可以分为三种:超级用户、系统用户、普通用户。
为了方便管理用户,Linux引入用户组概念,用户加入此用户组就会拥有此用户组的相关权限,Unix/Linux系统中的用户组基本分为两种:基本组和附加组,一个用户只可以属于一个基本组,但是可以加入多个附加组,系统在创建用户账号时会默认将用户加入基本组。
关于用户与组的管理,Unix/Linux系统已经自带了很多命令来管理用户以及用户组。用户的管理需用超级权限,所以下面的命令默认都需要有root权限。
root权限可以通过sudo命令提权获得,sudo可以切换到其他身份执行命令,默认身份为root。在/etc/sudoers中设置了可执行sudo命令的用户。若其未经授权的用户企图使用sudo,则会发出警告的邮件给管理员。用户使用sudo时,必须先输入密码,之后默认会有5分钟的有效期限,超过期限则必须重新输入密码。
useradd用于创建用户的账号。
语法格式:
useradd [选项] 用户名称
下面以创建一个普通用户为例,用户名为demo,用户全称为demo_user,用户的家目录为/home/demo,用户demo的过期日期为2017-08-08,用户的基本组为root,登录系统Shell解析器为bash。
user@ops-admin:~$ sudo useradd -c demo_user -d /home/demo -e 2017-08-08-g root -s /bin/bash demo # 通过id指令查看上面添加的用户 user@ops-admin:~$ id demo uid=1001(demo) gid=0(root) 组=0(root)
groupadd用于创建用户组。
语法格式:
useradd [选项] 用户组名称
例如创建一个用户组same,用户组的GID为1008,不允许创建GID重复
user@ops-admin:~$ sudo groupadd -o -g 1008 same
passwd用于修改用户的密码。
语法格式:
passwd [选项] [用户名]
例如将用户demo的密码修改为admin,并设置过期警告天数为10天。
user@ops-admin:~$ sudo passwd -w 10 demo passwd:密码过期信息已更改。 user@ops-admin:~$ sudo passwd demo 输入新的UNIX密码: 重新输入新的UNIX密码: passwd:已成功更新密码
usermod和groupmod用于修改用户和用户组的信息。
语法格式:
<usermod/groupmod> [选项] 用户名称
例如,修改用户demo的UID为1002,用户家目录为/home/share,并修改Shell解析器为/bin/sh:
# 修改UID user@ops-admin:~$ sudo usermod -u 1002 demo # 修改家目录 user@ops-admin:~$ sudo usermod -d /home/share demo # 修改解析器 user@ops-admin:~$ sudo usermod -s /bin/sh demo # 将用户组same的GID修改成1009 user@ops-admin:~$ sudo groupadd -g 1009 same
userdel和groupdel用于删除用户账号和用户组。
语法格式:
<userdel/groupdel> [选项] 用户名称
例如下面将用户demo账号删除,并删除same用户组。
user@ops-admin:~$ sudo userdel demo user@ops-admin:~$ sudo groupdel same
以上就是对Linux最基本的权限介绍,关于更详细的内容最直接的办法可以通过man命令来获得(执行man可以查看对应命令的详细说明)。
2. Shell基础编程
在Unix/Linux系统中,Shell是一种命令行的解释器命令,是用户与系统内核之间进行通信的一种语言。第一个Unix Shell是sh,除此之外还有很多优秀的Shell,例如:ksh、bash、csh、tcsh等。
Shell具有两种工作模式,分别是互动模式和脚本模式。互动模式就是用户直接在终端上输入指定的命令并执行,等待命令执行完毕并分析返回的结果,然后再执行下一条命令。脚本模式就是在执行Shell命令过程中,不需要用户去干扰或控制,它会自动执行下去。脚本模式的执行效率是非常高的,也就是我们经常说的自动化运维,只要我们编辑好了Shell任务,然后跑在Unix/Linux进程中,这些任务将会被自动处理。
Shell是与Unix/Linux系统内核交互最好的一个解析器语言,同时Shell也是一门非常容易掌握的语言,它的语法结构极其简单,下面用两个脚本解释Shell的大部分语法概念。
示例1:备份远程MySQL服务器的数据库数据,主机名:172.16.168.1,端口号:3306,用户名:root,备份数据库:demo01与demo02,备份数据的存储目录是$HOME/data/mysql。
首先建立两个文件,一个用于配置MySQL相关信息,另一个用于执行备份过程。数据库配置信息文件( config.cfg ):
# 主机 host=172.16.168.1 # 端口 port=3306 # 用户 user=root # 备份的数据库名称,数组 dbs_name=("beego" "demo") # 备份数据库文件的存储目录 data_dir=$HOME/data/mysql
执行数据库备份的脚本文件( mysqldump.sh ):
#! /bin/bash # 引进数据库配置信息文件,source的作用是把config.cfg文件中的键值对写入shell的临时变量中。 source ./config.cfg # 在脚本中,有一些转义字符,其中\n表示输出换行,参数-e表示解析转义字符。 echo -e "\n正在备份数据库信息,请稍等...\n" # 创建存储备份数据库文件的目录 # date可以输出当前日期,符号+后面的内容表示如何格式化当前日期并输出。 # 可以看到shell的变量不仅可以是定义的一个值,也可以是一个待执行命令的返回内容。 datapath=$data_dir/$(date +%Y%m%d%H%M%S) mkdir -p $datapath # 根据数据库的名称开始遍历进行备份 # shell脚本支持for, while, if-else, case等等常见的基本编程语言语法。 for db_item in ${dbs_name[*]} do # 此处使用了文件重定向的知识,把标准输出指向一个文件路径即可生成一个文件。 mysqldump -h $host -u $user -p --databases $db_item > $datapath/$db_item.sql if [ $? -eq 0 ] # -eq表是等于 then echo -e "$db_item数据库备份成功~\n" else echo -e "$db_item数据库备份失败~\n" fi done echo -e "备份数据库信息完成\n"
下面是执行结果示例:
user@ops-admin:~$ bash mysqldump.sh 正在备份数据库信息,请稍等... Enter password: beego数据库备份成功~ Enter password: demo数据库备份成功~ 备份数据库信息完成 user@ops-admin:~$ tree $HOME/data/mysql /home/alic/data/mysql └── 20170410164117 ├── beego.sql └── demo.sql 1 directory, 2 files
示例2:监控服务器主机的磁盘容量使用情况,主机用于服务器,监控服务器磁盘容量的使用情况是极其重要的。当下我们写一个脚本用于监控服务器的磁盘容量使用情况,当磁盘容量的百分比大于90%时,主机自动发邮件给运维管理员,并且此脚本每五分钟监控一次。
首先写一个监控服务器主机的磁盘容量使用情况的Shell脚本,然后使用crontab定时执行即可。监控Shell脚本如下 ( monitor.sh ):
#! /bin/bash # 获取服务器磁盘空间使用百分数 # 这条语句使用了一种名为管道的方式,把前面命令执行的结果传递给后面的命令继续处理执行。 # 这里还用到了两个流式编辑器:awk和sed,和一个过滤器grep。 # df命令可以查看磁盘使用情况,grep过滤出包含/dev/sda的那一行 # 然后awk处理只显示从grep取得那行的第五列内容,最后sed删掉非数字的符号。 percentage=`df | grep -n '/dev/sda' | awk '{print $5}' | sed 's/[^0-9\.]//g'` # 获取该服务器的信息 server=`ifconfig wlan | sed -n '2p'` if [ $percentage -ge 90 ]; then echo "服务器磁盘空间使用超过90%, $server" | mail -s "server warning" alic@samego.com else echo "服务器磁盘空间使用正常..." fi
crontab定时任务:
crontab是一个定时任务的执行工具,它随系统启动,如果你有什么任务想定时启动或者执行,可以在crontab列表中添加相应的指令。相关用法可使用man crontab查看。
user@ops-admin:~$ crontab -e crontab: installing new crontab user@ops-admin:~$ crontab -l 0 */1 * * * bash /home/alic/tutorial/oschina/book/chapter-01/source/shell/monitor.sh >> /dev/null user@ops-admin:~$ sudo service cron restart cron stop/waiting cron start/running, process 13644
示例效果(直接运行):
user@ops-admin:~$ df | sed -n '4p' /dev/sda8 611605204084828017182336 71% / user@ops-admin:~$ bash monitor.sh 服务器磁盘空间使用正常...
上面两个脚本用到了常见的Shell脚本知识点,关于更复杂的脚本,还可以使用函数来包装命令集合,还有更复杂的可交互脚本可以通过while、case等方式提供执行参数,更详细的Shell编程知识可以在互联网中查找到,本书便不再赘述。