5种Linux下安全删除文件的软件工具(最新)

本文主要讲述Linux系统下5种不同的安全删除文件工具的用法介绍。 通常情况下,我们在Linux系统下删除文件的主要方法是通过rm命令或者unlink命令进行删除。 但是正如我们之前介绍过的rm命令删除文件的原理,该命令只是将文件所对应的inode链接删除,但是文件在磁盘存储的数据依然还在,这样就有可能造成数据的泄露。 下面我们会介绍5种Linux系统下永久安全的从磁盘中删除文件数据的工具。

Shred 删除工具

Shred工具主要是通过多次的覆盖文件的方式来达到安全删除文件的目的。

Shred 工具用法及选项

用法如下:

Shred [选项]  文件名

选项如下:

-f, --force           必要时修改权限以使目标可写
-n, --iterations=N    覆盖N 次,而非使用默认的3 次
--random-source=文件      从指定文件中取出随机字节
-s, --size=N          粉碎数据为指定字节的碎片(可使用K、M 和G 作为单位)
-u             truncate and remove file after overwriting
--remove[=HOW]  like -u but give control on HOW to delete;  See below
-v, --verbose  show progress
-x, --exact    do not round file sizes up to the next full block;
this is the default for non-regular files
-z, --zero     add a final overwrite with zeros to hide shredding
--help            显示此帮助信息并退出
--version         显示版本信息并退出

实例:删除当前目录下的文件fio.txt, 执行下面的命令:

# shred -zvu -n 3 fio.txt

命令输出如下:

root@devops:~/osetc# shred -zvu -n 3 fio.txt
shred: fio.txt:正在删除
shred: fio.txt:名称已更改为 0000000
shred: 0000000:名称已更改为 000000
shred: 000000:名称已更改为 00000
shred: 00000:名称已更改为 0000
shred: 0000:名称已更改为 000
shred: 000:名称已更改为 00
shred: 00:名称已更改为 0
shred: fio.txt:已删除

Wipe删除工具

接下来要介绍的第二种安全删除文件的工具是wipe, 该工具可以安全的删除指定磁盘上的文件,当文件被删除后,是无法再恢复的。

安装wipe工具

执行下面的命令:

#sudo apt-get install wipe

命令输出如下:

root@devops:~/osetc# apt-get install wipe
正在读取软件包列表... 完成
正在分析软件包的依赖关系树
正在读取状态信息... 完成
下列软件包是自动安装的并且现在不需要了:
java-common libappindicator1 libindicator7 Linux-headers-4.10.0-28
Linux-headers-4.10.0-28-generic Linux-image-4.10.0-28-generic
Linux-image-extra-4.10.0-28-generic
使用'apt autoremove'来卸载它(它们)。
下列【新】软件包将被安装:
wipe
升级了 0 个软件包,新安装了 1 个软件包,要卸载 0 个软件包,有 205 个软件包未被升级。
需要下载 41.8 kB 的归档。
解压缩后会消耗 128 kB 的额外空间。
获取:1 http://mirrors.aliyun.com/ubuntu xenial/universe amd64 wipe amd64 0.22-3 [41.8 kB]
已下载 41.8 kB,耗时 0秒 (268 kB/s)
正在选中未选择的软件包 wipe。
(正在读取数据库 ... 系统当前共安装有 290931 个文件和目录。)
正准备解包 .../archives/wipe_0.22-3_amd64.deb  ...
正在解包 wipe (0.22-3) ...
正在处理用于 man-db (2.7.5-1) 的触发器 ...
正在设置 wipe (0.22-3) ...

实例,安全删除当前目录下的文件fio.txt, 执行下面的命令:

wipe -rfi fio.txt

命令输出如下:

root@devops:~/osetc# wipe -rfi fio.txt
File fio.txt (0 bytes) wiped
Operation finished.
1 file wiped and 0 special files ignored in 0 directories, 0 symlinks removed but not followed, 0 errors occured.

Secure-delete删除文件工具

接下来要介绍的是一款名叫secure-delete的安全文件删除工具集合,该集合里包含用来安全删除指定文件的工具叫srm

安装secure-delete 工具集

执行下面的命令:

#sudo apt-get install secure-delete

命令输出如下

root@devops:~/osetc# sudo apt-get install secure-delete
sudo: 无法解析主机:devops
正在读取软件包列表... 完成
正在分析软件包的依赖关系树
正在读取状态信息... 完成
下列软件包是自动安装的并且现在不需要了:
java-common libappindicator1 libindicator7 Linux-headers-4.10.0-28
Linux-headers-4.10.0-28-generic Linux-image-4.10.0-28-generic
Linux-image-extra-4.10.0-28-generic
使用'sudo apt autoremove'来卸载它(它们)。
下列【新】软件包将被安装:
secure-delete
升级了 0 个软件包,新安装了 1 个软件包,要卸载 0 个软件包,有 205 个软件包未被升级。
需要下载 62.7 kB 的归档。
解压缩后会消耗 161 kB 的额外空间。
获取:1 http://mirrors.aliyun.com/ubuntu xenial/universe amd64 secure-delete amd64 3.1-6ubuntu1 [62.7 kB]
已下载 62.7 kB,耗时 0秒 (365 kB/s)
正在选中未选择的软件包 secure-delete。
(正在读取数据库 ... 系统当前共安装有 290943 个文件和目录。)
正准备解包 .../secure-delete_3.1-6ubuntu1_amd64.deb  ...
正在解包 secure-delete (3.1-6ubuntu1) ...
正在处理用于 man-db (2.7.5-1) 的触发器 ...
正在设置 secure-delete (3.1-6ubuntu1) ...

如果要删除当前目录下的fio.txt文件,可以执行下面的命令:

#srm –vz fio.txt

命令输出如下:

root@devops:~/osetc# srm -vz fio.txt
Using /dev/urandom for random input.
Wipe mode is secure (38 special passes)
Wiping fio.txt ************************************** Removed file fio.txt ... Done

Sfill 安全删除文件工具

Sfill工具是secure-delete工具集里的一个安全删除文件的另一个工具,该工具主要用来安全的删除磁盘上所有可用空间上的文件。主要针对通过rm命令删除文件的inode链接的那些空间上的文件。该工具会对磁盘进行检查,将找到的可用空间通过/dev/urandom 设备上的随机数据进行填充。

执行命令:

#sfill –v osetc

命令输出如下:

root@devops:~# sfill -v osetc/
Using /dev/urandom for random input.
Wipe mode is secure (38 special passes)
Wiping now ...
Creating osetc/oooooooo.ooo ...

Sdmem 删除内存数据工具

最后我们要介绍一种可以安全的删除RAM 内存储器中的数据的工具名叫sdmem

执行命令如下:

#sdmem –f –v

命令输出如下:

root@devops:~/osetc# sdmem -f -v
Starting Wiping the memory, press Control-C to abort earlier. Help: "sdmem -h"
Wipe mode is secure (38 special passes)

99%的人还阅读了:

CURL命令忽略https请求的SSL证书

在使用wget命令来获取文件的时候,我们可以使用wget命令的 –-no-check-certificate 选项来忽略证书的认证。 那么在执行curl命令是否也可以忽略SSL证书的警告信息呢。
当然是有的,我们可以在执行curl 命令的时候,使用 -k 或者 –insecure 选项,来忽略签名认证的警告。 这样就可以让curl命令执行不安全的SSL连接,进而去获取数据。
命令使用示例如下:
curl -k https://osetc.com

curl --insecure https://www.osetc.com
在php中我们通过curl来实现post 和get请求的时候,也可以忽略SSL证书的验证,大家可以参考下面的代码:
function request_by_curl($url, $post_data = '', $timeout = 30) {
    $ch = curl_init(); 
    curl_setopt($ch, CURLOPT_URL, $url);

    //post提交,否则get
    if ($post_data != '') {
        curl_setopt($ch, CURLOPT_POST, 1); 
        curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
    }

     curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
     curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout); 
     curl_setopt($ch, CURLOPT_HEADER, false);

     //跳过SSL验证 
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, '0'); 
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, '0'); 
    $file_contents = curl_exec($ch); 
    curl_close($ch); 
    return $file_contents; 
}
函数调用如下:
//$result存储返回的数据
//post 
$result = request_by_curl('http://www.xxx.com', 'username=admin&password=123'); 
//get
 $result = request_by_curl('http://www.xxx.com');

99%的人还阅读了:

Linux:用腻了 wget 或 curl,有什么更好的替代品吗?

Mac OS:终端下使用curl命令下载文件

Ubuntu 下curl安装及使用指南

Linux 下curl命令post传递表单数据

12 个最佳的免费网络监控工具

要让一个多级机构运行良好而且平稳的话,一个非常艰巨重大的任务就是做好网络管理。每个机构都配备专门的人员,即网络分析师,来进行网络管理。他们使用了许多工具来监视网络的运行状况,并查看网络流量的上升和下降状况。他们还必须确保整个网络能够平稳地运行,因为只要有一分钟的网络中断就会使得整个机构的工作出现混乱。

使得机构工作平稳运行的最重要的方式之一就是使用各种网络监视工具。使用IDS检测来自外部网络的威胁和问题,使用网络监视软件来监视由于内部服务器负载过高或者某个网络部员工的小错误而引起的问题。网络监视软件可以跟踪数据包的流向,而且它还可以对数据包活动表现异常的地方进行检测,以确定出错的地方。例如,为了检测web服务器的运行状况,网络监视软件将定期不间断地发送32位字节的ping请求,跟踪服务器是否收到了该请求并及时响应。

现有的网络监控工具可以说是数以百计,但是这些工具往往比较昂贵,因此花些时间去选购是很值得的,需要仔细研究其适用性、性能、专业性等方面的特性,需要判断的因素很多,但短时间内理解这些指标并作出选择可不是一件容易的事。因此,我们利用专业经验,建立了包括一些最佳免费网络监控工具的清单,为实现网络的安全、稳定长期运行提供帮助,以下是具体的清单列表:

1) Fiddler

Fiddler(几乎)是适用于任何平台和任何操作系统的最好的免费网络工具,并提供了一些广受欢迎的关键特性。如:性能测试、捕捉记录HTTP/HTTPs请求响应、进行web调试等很多功能.

Fiddler

2) Nagios

Nagios是另外一款在互联网上的免费网络监控工具。 它是开源的监控解决方案,它表现非凡并且持续为全球成千上万的组织提供可靠的监测。

Nagios

3) Nedi

Nedi也是一款对用户来说开源的网络监控工具。 NeDi可以在你的核心网络设备上,针对CDP, FDP和、或LLDP充分发挥潜力。它还可以包括其他网络组件, 当他们位于网络边界上,也可以工作地非常好。

4) EasyNetMonitor

一个监控你电脑本地和互联网主机之间网络的最小免费工具。开始我们的EasyNetMonitor,打开弹出菜单可以看到你电脑的网络状态信息。

免费网络监控 EasyNetMonitor 是一个小而简单易用的测试远程主机和其他网络主机之间网络到达率的工具。很简单的点击EasyNetMonitor图标就可以获得关于你本地网络和互联网主机的实时信息。

5) Microsoft Network Monitor

这个是Microsoft. Network Monitor 3.4稳定版非开发版协议分析器提供的网络监控工具。微软消息分析器替代了网络监控3.4。微软消息分析器提供最新的协议分析,比如捕捉屏幕,显示,协议消息流量分析、事件和在其他系统或应用程序消息故障排除和诊断方案方面。这是一个更强大的工具来捕获和分析协议消息。

Microsoft Network Monitor

6) Cacti

Cacti 是一个完整的网络绘图解决方案旨在利用RRDTool的数据存储和图形绘制功能。Cacti提供了一个快速的轮询器,先进的图形模板,多个数据采集方法和用户管理的开箱即用的特性。所有的封装都是为局域网内的数百台设备复杂网络的安装提供很直观的,易用的界面。

Cacti

7) Zenoss

Zenoss 的核心是为企业IT监控工具提供一些关键特性-

  • 跨平台设备性能和可用性监控
  • 高度可定制的基于web的控制台和仪表板
  • 设备探索、建模和分类
  • 三层网络拓扑图
  • 最好的数据收集
  • 错误和事件监控和管理
  • 事件分类、重复数据删除、自动清档、映射转换,和生命周期管理
  • 事件触发和通知
  • 可用性监控设备、网络、进程和服务等

8) Paessler

Paessler是一个强大的易用的网络监控软件。PRTG网络监视器运行在Windows机器的网络上,可以收集你指定的机器,软件和设备的各种统计数据。PRTG附带一个易于使用的web界面点击配置。你可以很容易地共享数据给非技术的同事和客户,包括通过现场图和自定义报告。

Paessler

9) Bandwidthd

BandwidthD可以跟踪使用TCP / IP网络的子网和构建html文件图表显示的利用率。

Bandwidthd

10) Icinga

Icinga是一个处理多服务,多设备以及它们之间复杂依赖关系的网络监控工具。不需要进行复杂的安装或维护监测系统。

11) The Dude

The Dude 网络监控是由 MikroTik 提供的新的应用程序,可以大大提高你的管理您的网络环境。它会自动扫描指定的子网内的所有设备,绘制地图和布局你的网络,监控服务的设备和提醒你,以防一些服务问题。

The Dude

12) Total Network Monitor

Total Network Monitor 是一个为持续监控本地网络,个人电脑以及需要细心观察和深入控制的服务提供的免费网络监控软件。

Total Network Monitor

参考:http://os.51cto.com/art/201502/464662.htm

 

RHCSA 系列(三): 如何管理 RHEL7 的用户和组

和管理其它Linux服务器一样,管理一个 RHEL 7 服务器要求你能够添加、修改、暂停或删除用户帐户,并且授予他们执行其分配的任务所需的文件、目录、其它系统资源所必要的权限。

User and Group Management in Linux

RHCSA: 用户和组管理 – Part 3

管理用户帐户

如果想要给RHEL 7 服务器添加账户,你需要以root用户执行如下两条命令之一:

# adduser [new_account]
# useradd [new_account]

当添加新的用户帐户时,默认会执行下列操作。

  • 它/她的主目录就会被创建(一般是”/home/用户名”,除非你特别设置)
  • 一些隐藏文件 如.bash_logout, .bash_profile 以及 .bashrc 会被复制到用户的主目录,它们会为用户的回话提供环境变量。你可以进一步查看它们的相关细节。
  • 会为您的账号添加一个邮件池目录。
  • 会创建一个和用户名同样的组(LCTT 译注:除非你给新创建的用户指定了组)。

用户帐户的全部信息被保存在/etc/passwd文件。这个文件以如下格式保存了每一个系统帐户的所有信息(字段以“:”分割)

[username]:[x]:[UID]:[GID]:[Comment]:[Home directory]:[Default shell]
  • [username][Comment] 其意自明,就是用户名和备注
  • 第二个‘x’表示帐户的启用了密码保护(记录在/etc/shadow文件),密码用于登录[username]
  • [UID][GID]是整数,它们表明了[username]的用户ID 和所属的主组ID

最后。

  • [Home directory]显示[username]的主目录的绝对路径
  • [Default shell] 是当用户登录系统后使用的默认shell

另外一个你必须要熟悉的重要的文件是存储组信息的/etc/group。和/etc/passwd类似,也是每行一个记录,字段由“:”分割

[Group name]:[Group password]:[GID]:[Group members]
  • [Group name] 是组名
  • 这个组是否使用了密码 (如果是”x”意味着没有)
  • [GID]: 和/etc/passwd中一样
  • [Group members]:用户列表,使用“,”隔开。里面包含组内的所有用户

添加过帐户后,任何时候你都可以通过 usermod 命令来修改用户账户信息,基本的语法如下:

# usermod [options] [username]

相关阅读

示例1 : 设置帐户的过期时间

如果你的公司有一些短期使用的帐户或者你要在有限时间内授予访问,你可以使用 –expiredate 参数 ,后加YYYY-MM-DD 格式的日期。为了查看是否生效,你可以使用如下命令查看

# chage -l [username]

帐户更新前后的变动如下图所示

Change User Account Information

修改用户信息

示例 2: 向组内追加用户

除了创建用户时的主用户组,一个用户还能被添加到别的组。你需要使用 -aG或 -append -group 选项,后跟逗号分隔的组名。

示例 3: 修改用户主目录或默认Shell

如果因为一些原因,你需要修改默认的用户主目录(一般为 /home/用户名),你需要使用 -d 或 -home 参数,后跟绝对路径来修改主目录。

如果有用户想要使用其它的shell来取代默认的bash(比如zsh)。使用 usermod ,并使用 -shell 的参数,后加新的shell的路径。

示例 4: 展示组内的用户

当把用户添加到组中后,你可以使用如下命令验证属于哪一个组

# groups [username]
# id [username]

下面图片的演示了示例2到示例4

Adding User to Supplementary Group

添加用户到额外的组

在上面的示例中:

# usermod --append --groups gacanepa,users --home /tmp --shell /bin/sh tecmint

如果想要从组内删除用户,取消 –append 选项,并使用 –groups 和你要用户属于的组的列表。

示例 5: 通过锁定密码来停用帐户

如果想要关闭帐户,你可以使用 -l(小写的L)或 -lock 选项来锁定用户的密码。这将会阻止用户登录。

示例 6: 解锁密码

当你想要重新启用帐户让它可以继续登录时,使用 -u 或 –unlock 选项来解锁用户的密码,就像示例5 介绍的那样

# usermod --unlock tecmint

下面的图片展示了示例5和示例6:

Lock Unlock User Account

锁定上锁用户

示例 7:删除组和用户

如果要删除一个组,你需要使用 groupdel ,如果需要删除用户 你需要使用 userdel (添加 -r 可以删除主目录和邮件池的内容)。

# groupdel [group_name]        # 删除组
# userdel -r [user_name]       # 删除用户,并删除主目录和邮件池

如果一些文件属于该组,删除组时它们不会也被删除。但是组拥有者的名字将会被设置为删除掉的组的GID。

列举,设置,并且修改标准 ugo/rwx 权限

著名的 ls 命令 是管理员最好的助手. 当我们使用 -l 参数, 这个工具允许您以长格式(或详细格式)查看一个目录中的内容。

而且,该命令还可以用于单个文件中。无论哪种方式,在“ls”输出中的前10个字符表示每个文件的属性。

这10个字符序列的第一个字符用于表示文件类型:

  • – (连字符): 一个标准文件
  • d: 一个目录
  • l: 一个符号链接
  • c: 字符设备(将数据作为字节流,例如终端)
  • b: 块设备(以块的方式处理数据,例如存储设备)

文件属性的接下来的九个字符,分为三个组,被称为文件模式,并注明读(r)、写(w)、和执行(x)权限授予文件的所有者、文件的所有组、和其它的用户(通常被称为“世界”)。

同文件上的读取权限允许文件被打开和读取一样,如果目录同时有执行权限时,就允许其目录内容被列出。此外,如果一个文件有执行权限,就允许它作为一个程序运行。

文件权限是通过chmod命令改变的,它的基本语法如下:

# chmod [new_mode] file

new_mode 是一个八进制数或表达式,用于指定新的权限。随意试试各种权限看看是什么效果。或者您已经有了一个更好的方式来设置文件的权限,你也可以用你自己的方式自由地试试。

八进制数可以基于二进制等价计算,可以从所需的文件权限的文件的所有者、所有组、和世界组合成。每种权限都等于2的幂(R = 2^2,W = 2^1,x = 2^0),没有时即为0。例如:

File Permissions

文件权限

在八进制形式下设置文件的权限,如上图所示

# chmod 744 myfile

请用马上来对比一下我们以前的计算,在更改文件的权限后,我们的实际输出为:

Long List Format

长列表格式

示例 8: 寻找777权限的文件

出于安全考虑,你应该确保在正常情况下,尽可能避免777权限(任何人可读、可写、可执行的文件)。虽然我们会在以后的教程中教你如何更有效地找到您的系统的具有特定权限的全部文件,你现在仍可以组合使用ls 和 grep来获取这种信息。

在下面的例子,我们会寻找 /etc 目录下的777权限文件。注意,我们要使用第二章:文件和目录管理中讲到的管道的知识:

# ls -l /etc | grep rwxrwxrwx

Find All Files with 777 Permission

查找所有777权限的文件

示例 9: 为所有用户指定特定权限

shell脚本,以及一些二进制文件,所有用户都应该有权访问(不只是其相应的所有者和组),应该有相应的执行权限(我们会讨论特殊情况下的问题):

# chmod a+x script.sh

注意: 我们可以使用表达式设置文件模式,表示用户权限的字母如“u”,组所有者权限的字母“g”,其余的为“o” ,同时具有所有权限为“a”。权限可以通过+ 来授予和收回。

Set Execute Permission on File

为文件设置执行权限

长目录列表还用两列显示了该文件的所有者和所有组。此功能可作为系统中文件的第一级访问控制方法:

Check File Owner and Group

检查文件的所有者和所有组

改变文件的所有者,您应该使用chown命令。请注意,您可以在同时或分别更改文件的所有组:

# chown user:group file

你可以更改用户或组,或在同时更改两个属性,但是不要忘记冒号区分,如果你想要更新其它属性,让另外的部分为空:

# chown :group file              # 仅改变所有组
# chown user: file               # 仅改变所有者

示例 10:从一个文件复制权限到另一个文件

如果你想“克隆”一个文件的所有权到另一个,你可以这样做,使用–reference参数,如下:

# chown --reference=ref_file file

ref_file的所有信息会复制给 file

Clone File Ownership

复制文件属主信息

设置 SETGID 协作目录

假如你需要授予在一个特定的目录中拥有访问所有的文件的权限给一个特定的用户组,你有可能需要使用给目录设置setgid的方法。当setgid设置后,该真实用户的有效GID会变成属主的GID。

因此,任何访问该文件的用户会被授予该文件的属组的权限。此外,当setgid设置在一个目录中,新创建的文件继承组该目录的组,而且新创建的子目录也将继承父目录的setgid权限。

# chmod g+s [filename]

要以八进制形式设置 setgid,需要在基本权限前缀以2。

# chmod 2755 [directory]

总结

扎实的用户和组管理知识,以及标准和特殊的 Linux权限管理,通过实践,可以帮你快速解决 RHEL 7 服务器的文件权限问题。

我向你保证,当你按照本文所概述的步骤和使用系统文档(在本系列的第一章 回顾基础命令及系统文档中讲到), 你将掌握基本的系统管理的能力。

请随时使用下面的评论框让我们知道你是否有任何问题或意见。


via: http://www.tecmint.com/rhcsa-exam-manage-users-and-groups/

作者:Gabriel Cánepa 译者:xiqingongzi 校对:wxy

本文由 LCTT 原创翻译,Linux中国 荣誉推出

如何用 Bash 创建一个二进制发布包

linux下安装JDK的时候,Sun公司为JDK6的linux版本提供了一个shell的安装包,用起来特别的好用,基本上和在Windows下安装软件没什么两样,shell文件执行之后,几乎一切都系都设置好了,不用我们自己再动手设置PATH和JAVA_HOME,可是一个shell文件中是如何把二进制代码包含进来的呢?

再例如淘宝为linux开发的淘宝插件,其实也是一个shell文件,但是执行这个shell文件之后,会安装很多二进制的东西,同样的问题,Shell只是文本文件,其中的二进制是怎么来包含进来的呢?

从文本文件转换出可执行文件,通过编译器把源程序编程成可执行程序当然是可以的,但是前面提到的哪两种情况都不是这样做的。原因有两个,第一对于大的项目来说,编译需要的时间比较长,环境比较复杂;第二,更加重要的是,这样做其实和从源代码编译程序没什么两样,对于不想让用户看到自己的源码的商业软件来说,这显然是不可取的。

我们看看一般的程序是如何发布他们的二进制包的。

一般的程序,无非是把二进制和必要的文档压缩到一个压缩文件中,然后通过README文档的解释程序的运行依赖什么样的假设,然后,你就可以把程序自行的移动到你想移动的地方去了。

很多时候,我们把程序放到任何地方都是可以运行的,但是程序可以运行,并不是说我们已经完成了程序的安装,举个例子来说,如果我们解压JDK的二进制包之后,直接把程序移动到一个地方,然后把对应的bin目录添加到PATH中就可以执行JDK提供的一系列工具了。

但是如果我们安装其他的依赖与JDK的程序的时候,比如TOMCAT,那么就会有问题。因为我们只是在PATH中加入了JDK的bin目录而没有制定JAVA_HOME这个环境变量,所以TOMCAT很可能会不能运行。

再比如我们使用man命令来查看一个程序的手册,一般情况下二进制包中也会包含man文档的,但是如果我们只是把解压的二进制包的路径添加到了PATH中,还不能在man中找到对应的文档。

也正是因为这样的原因吧,所以很多的二进制包的发布是使用deb或者rpm包来发布。安装的时候少了很多的烦恼。要制做deb或者rpm包当然是需要学习成本的,而且deb和rpm也只能在对应的linux发行版中使用,如果想要为所有的Linux发行版都提供一个安装文件,那么使用shell文件来做无疑是最好的办法。

Shell的学习成本低,而且对linux平台来说有通用,那么是如何做到的呢?

想想我们在手动安装的情形,无非是把压缩的二进制包解压,移动到特定的目录下,在PATH变量中添加二进制包的可执行文件的路径等等工作。首先我们把二进制包压缩文件和shell文件分开。这样一来,shell中只要完成解压,然后把解压后的目录移动到指定的目录中去,设置各种各样的环境变量然后就完成了工作了。

但是我们在如何把压缩文件和这个shell解压之后要执行的命令的shell文件一起放到一个shell文件中呢?

要做到这一点,首先这个shell文件中,要有可以解压的二进制内容,其次,这个shell要做的工作就是,把二进制内容,解压,然后把原来手动做的工作在这个shell中用命令完成。再用shell写个脚本完成一些手动完成的工作,这个任务比较容易,所以制作shell安装包的难点就是如何在其中包含二进制内容了。

如何在一个文本文件中记录二进制的内容呢?

这个问题早就被解决了。答案就是使用Base64编码。在linux下就有base64 这个命令程序就是来做这个工作的。base64可以把文件进行base64的编码,输出的标准输出中去或者把文件中的Base64编码的内容解码。命令base64除了可以对文件的内容做Base64的编解码外,也可以对标准输入中的数据进行Base64编辑码。

有了这些预备的知识,那么我们就可以看看具体的如何来做shell的二进制发布包了。

首先假设我们要发布的文件都放在名为test的当前文档中。

  1. 把要发布的文件打包

    tar zcf test.tar ./test
  2. 对打好的二进制包做Base64编码

    base64 ./test.tar > test_base64.txt
  3. 准备安装文件的shell文件

    test_base64="";#test_base64中的所有内容
    echo $test_base64|base64 -d >test.tar
    tar zxf test.tar
    rm test.tar
    # 其他的安装代码

写到这里,我们已经把道理将明白了。但是可不可以写一个shell程序,专门来生成这样的发布包软件呢?当然可以。下面是我写的这个打包程序的源代码。

function mkpackage(){
    target_dir=$1
    felow_install_shell_command_file=$2
    echo "tar -zcf ._test_dir.tar.gz $target_dir"
    tar -zcf ._test_dir.tar.gz $target_dir
    base64 ._test_dir.tar.gz >._base64
    rm ._test_dir.tar.gz
    printf "test_base64=\"">install.sh
    while IFS='' read -r line || [[ -n "$line" ]]; do
    printf "$line\\" >>install.sh
    printf "n" >>install.sh
    done <./._base64
    rm ._base64
    echo "\"" >>install.sh
    echo 'printf $test_base64|base64 -d >._temp.tar.gz;'>>install.sh
    echo 'tar zxf ._temp.tar.gz' >> install.sh
    echo 'rm ._temp.tar.gz' >>install.sh
    if [[ -e $fellow_install_shell_command_file ]]; then
        cat $fellow_install_shell_command_file >>install.sh
    fi
    chmod +x install.sh
}
function usage(){
   echo "usage:"
   echo "    $1 test_dir [the_command.sh]"
}
if [[ $# != 0 ]]
then
    mkpackage $1 $2
else
   usage $0
fi

这个程序接受两个输入参数,第一个表示要打的程序包目录,第二个是解压后,安装动作的shell脚本文件。最后这个程序生成一个名为 install.sh 的文件。执行 install.sh 之后,会首先得到加压的程序包内容,然后执行第二个参数指定的 shell 脚本的内容。这样我们就做了用Shell发布二进制文件的一个打包程序。当然了,在最后发布之前,还需要把这个shell压缩一下。以便消除因为Base64编码而带来的文件长度的变大的影响。

PHP 之 FastCGI 与 mod_php 详解

网上对于FastCGI与mod_php的知识比较杂乱而不全面,故在此整理一下,以便入门学习者查阅方便。

背景

PHP最常用的方式是以模块的方式(mod_php)运行在Apache中,也是Apache运行PHP的默认方式;但在Nginx中,Nginx又使用的是PHP-FPM,但是PHP-FPM到底是个什么东东?跟php有什么关系?今天我们一起来探究一番。

PHP处理器(PHP handlers)

首先需要记住的是,任何一种Web服务器(Apache、Nginx等)都是被设计成向用户发送html、图片等静态资源的,Web服务器自身并不能解释任何动态脚本(PHP、Python等)。

PHP处理器就是用来解释Web应用中的PHP代码,并将它解释为HTML或其他静态资源,然后将解析的结果传给Web服务器,最后再由Web服务器发送给用户。

大多数的Web服务器都不能解析PHP代码,因此它需要一个能解析PHP代码的程序,这就是PHP处理器。

现在我们知道了,Apache与Nginx都需要PHP处理器来处理php代码,那么怎么连接上服务器与php处理器呢?也就是说服务器与php处理器如何通信?

答案是通过SAPI(Server Application Programming Interface 服务器端应用编程端口),简单来说,SAPI指的是PHP具体应用的编程接口, 就像PC一样,无论安装哪些操作系统,只要满足了PC的接口规范都可以在PC上正常运行, PHP脚本要执行有很多种方式,通过Web服务器,或者直接在命令行下,也可以嵌入在其他程序中,有兴趣大家可以研究PHP内核。

我们这里继续讨论PHP最常用的SAPI提供的2种连接方法:mod_php和mod_fastcgi。

mod_php模式

咱们回顾一下,Apache是怎么能够识别php代码的?是不是Apache的配置文件httpd.conf中加上或者修改这样几句:

//添加
LoadModule php5_module modules/libphp5.so
AddType application/x-httpd-php .php
//修改

DirectoryIndex index.php index.html index.htm index.html

也即php作为Apache的一个子模块来运行,当通过web访问php文件时,Apache就会调用php5_module来解析php代码。

配置加载mod_php模块后,php便是Apahce进程本身一部分,每个新的Apache子进程都会加载此模块。

mod_fastcgi模式

我们先看PHP-FPM官网的说明:

PHP-FPM – A simple and robust FastCGI Process Manager for PHPPHP-FPM (FastCGI Process Manager) is an alternative PHP FastCGI implementation with some additional features useful for sites of any size, especially busier sites.

PHP-FPM是一个PHP的FastCGI进程管理器,解释的非常简单。这说明PHP-FPM是辅助mod_fastcgi模式进行工作的,然而FastCGI又是个什么东西?管理着什么进程?

什么是CGI?

CGI(Common Gateway Interface) 是WWW技术中最重要的技术之一,有着不可替代的重要地位。

CGI是外部应用程序(CGI程序)与Web服务器之间的接口标准,是在CGI程序和Web服务器之间传递信息的规程。

CGI规范允许Web服务器执行外部程序,并将它们的输出发送给Web浏览器,CGI将Web的一组简单的静态超媒体文档变成一个完整的新的交互式媒体。

说白了,CGI是一种外部应用程序(CGI程序)与Web服务器的协议,CGI是为了保证Server传递过来的数据是标准格式。

什么是FastCGI?

FastCGI像是一个常驻(long-live)型的CGI,它可以一直执行着,只要激活后,不会每次都要花费时间去fork一次(这是CGI最为人诟病的fork-and-execute 模式)。它还支持分布式的运算, 即 FastCGI 程序可以在网站服务器以外的主机上执行并且接受来自其它网站服务器来的请求。

FastCGI是语言无关的、可伸缩架构的CGI开放扩展,其主要行为是将CGI解释器进程保持在内存中并因此获得较高的性能。众所周知,CGI解释器的反复加载是CGI性能低下的主要原因,如果CGI解释器保持在内存中并接受FastCGI进程管理器调度,则可以提供良好的性能、伸缩性、Fail- Over特性等等。

一般情况下,FastCGI的整个工作流程是这样的:

  1. Web Server启动时载入FastCGI进程管理器(IIS ISAPI或Apache Module)。
  2. FastCGI进程管理器自身初始化,启动多个CGI解释器进程(可见多个php-cgi)并等待WebServer的连接。
  3. 当客户端请求到达Web Server时,FastCGI进程管理器选择并连接到一个CGI解释器。 Web server将CGI环境变量和标准输入发送到FastCGI子进程php-cgi。
  4. FastCGI子进程完成处理后将标准输出和错误信息从同一连接返回Web Server。当FastCGI子进程关闭连接时,请求便告处理完成,FastCGI子进程接着等待并处理来自FastCGI进程管理器(运行在Web Server中)的下一个连接,在CGI模式中,php-cgi在此便已经退出。

也就是说FastCGI是CGI的升级版,一种语言无关的协议,用来沟通程序(如PHP, Python, Java)和Web服务器(Apache2, Nginx), 理论上任何语言编写的程序都可以通过FastCGI来提供Web服务。

FastCGI的特点是会在一个进程中依次完成多个请求,以达到提高效率的目的,大多数FastCGI实现都会维护一个进程池。

通俗解释:FastCGI事先就需要启动,而且可以启动多个CGI模块,在那里一直运行等着web发请求,然后再给php解析运算,完成后生成html返回给web后,但是完成后它不会退出,而是继续等着下一个web请求。

PHP-FPM

PHP-FPM就是针对于PHP的FastCGI的一种实现,他负责管理一个进程池,来处理来自Web服务器的请求。

但是PHP-FPM仅仅是个“PHP FastCGI 进程管理器”, 它仍会调用PHP解释器本身来处理请求,PHP解释器(在Windows下)就是php-cgi.exe。

结束语

说了这么多,也不知道是否表达清楚,如果有不正确的地方请指正,谢谢。

Zorin OS:Linux新手最应该使用的个人桌面系统

Zorin OS 是一款基于 Ubuntu 的 Linux 发行版,特别为 Linux 新手而设计。它拥有类似 Windows 的图形用户界面,以及很多类似 Windows 下的应用软件。Zorin OS 还带有能让用户运行很多Windows 软件的程序。该发行版的终极目标是提供一份可作为 Windows 替换的 Linux 选择,并让 Windows 用户在避免烦琐的同时享受 Linux 的所有特性。

这一段话是几乎所有的中国的 Linux 网站上对 Zorin OS 的描述。

本人是 linux 新手,在 Linux 中国的新手群里得到各位大神的指点,安装了很多适合个人用的 linux 桌面系统(Ubuntu/fedora/麒麟,还有一些非常用的小众 linux 桌面系统),说实话,被折腾的够呛,主要原因是安装网卡驱动、显卡驱动、影音解码软件,然后出现各种问题,导致无法安心下来学习 linux 系统的相关知识和操作。无意之中看到 Zorin OS 的相关介绍,介绍比较动心,于是去官方下载了 Zorin OS 10 Ultimate(64位)版(官方下载地址:http://zorinos.com/download.html)。在虚拟机进行了安装,安装后感觉该系统的确适合我们新手使用,同时安装简单,安装后的系统各种程序齐全,功能强大。

具体步骤和情况如下:

安装Zorin OS 10 Ultimate(64位),需要10G左右的空间,建议分区15-20G左右,内存我虚拟机设定为1G。

安装过程

安装还是试用

安装类型

选择键盘布局

设置用户和密码

正在复制文件

安装完成后会进行必要的更新。

更新完成后会下载语言包(因为我选择的是中文安装)。

下载语言包

语言包安装完成后会进行硬件检测和硬件驱动安装。然后提示你重新启动,点击重新启动即可。

使用 Ansible 高效交付 Docker 容器

使用 playbook、插件和 Docker 模块设置和扩展 Ansible 环境

Docker 和它的容器工作流可封装、共享和部署您的应用程序环境。Ansible 是一个与 Docker 高度兼容的自动化工具,它使用一个强大的接口来管理远程服务器上的容器。在本文中,我将探索为何和如何使用 Ansible 的可扩展设计来将 Docker 和 Ansible 的最佳功能合并在一起。

Docker 之所以如此流行,是因为它创造了一种采用方便的命令行接口 (CLI) 和 HTTP API 工具来封装、运行和维护容器的独特方式。这种简化降低了此技术的入门门槛,使得将应用程序及其运行时环境封装到一个简单 Dockerfile 中的独立镜像中变得可行。Docker 使您能够开发更复杂的项目,但您仍需要配置这些容器。在本文中,我将展示 Ansible 如何通过更清晰的语法带来配置管理器的特性。您将学习如何仅使用已安装的 Python 和 Docker 构建任何堆栈。

在介绍 Ansible 的细节之前,我首先将介绍 Ansible 的 分析 中提到的一些要点:

  • 尽管容器导致一些新工作流出现,但编排和配置工具仍然非常活跃。
  • Ansible 和 Salt 等新参与者正在挑战现有的工具,比如 Chef 和 Puppet。
  • 许多与 Docker 有关联的开发人员也很关心这些工具。

更确切地讲,借助 Docker,您可以在几秒内实现完全隔离的堆栈环境,或者在服务器之间复制准确的设置。但是,Docker 不包含提供端到端体验的可靠工具,无论是针对开发还是生产。Docker 团队通过新的集群工具解决了这些不断演变的挑战,尝试将 Docker 转变为一个大规模运行容器的可靠解决方案。然而,Docker 仍然需要您手动硬编码任务和重复常见的设置。所以,针对容器的编排和配置管理的关键 Docker 流程仍有待解决。在本文中,您将学习如何结合使用 Ansible 和 Docker 来帮助解决这些问题。

DevOps 的兴起

在部署到生产中之前,现代应用程序通常涉及到一个复杂的部署管道。最佳实践建议在每次小型迭代后尽早地、频繁地发布代码。任务的手动执行无法扩展,组织已开始完善介于开发人员与系统管理员之间的流程,所以 DevOps 就诞生了。从那时起,敏捷团队就开始尝试强化和自动化测试代码,以及将其交付给用户的方式。

通过实现最新的技术和方法,公司对其服务器上的代码建立了信心。然而,随着应用程序在规模和复杂性上不断增长,开发人员和系统管理员继续面临着无数挑战。现在比以往更需要为产品提供受支持的社区驱动工具。

Ansible 的可扩展设计

在此环境中,Ansible 提供了一个有趣的框架来管理基础架构。您可以获得服务器定义的控制权,比如要安装的包或要复制的文件,并将该配置扩展到数千个服务器。Ansible playbook 构成了集群的目标状态的一种安全表示。它的 YAML 语法和庞大的模块列表生成了任何开发人员都能快速理解的易读性配置文件。不同于 Chef 或 Puppet,Ansible 是无代理的,这意味着您要在远程主机上运行命令,只需一个 SSH 连接即可。可以看到,Ansible 可轻松地处理 DevOps 复杂性。

但是,Ansible 是在容器快速兴起和它们在云开发环境中带来革命之前设计的。那么 Ansible 是否仍然有用?微型服务的范式和复杂开发环境引入了新的需求:

  • 轻量型镜像。为了容易传输或节省成本,镜像被剥离到仅剩下最低限度的依赖项。
  • 单一用途,单一流程。如果应用程序不是非常需要 SSH 守护进程,则无需运行它。
  • 短暂性。容器随时可能死亡、转移和复活。

在此上下文中,Ansible 的可扩展架构解决了这些问题。一个 Docker 模块在较高层面上管理主机和容器。尽管您可能会争论哪个编排工具(来自 Google 的 Kubernetes,还是来自 New Relic 的 Centurion)最适合此环境,但 Docker 模块执行效率很高,这正是我在本文中使用它的原因。但是,您还可以构建从其官方 Ansible 镜像启动的容器,然后在本地模式下从内部运行 playbook。尽管此方法非常适合 Packer,而且肯定也适合许多使用情况,但它的缺点通常极为关键。

  • 您被锁定在一个基础镜像中,无法再利用特殊的秘诀或其他堆栈。
  • 最终的工件已安装 Ansible 和它的依赖项,它们与实际应用程序毫无关系,这让工件变得更笨重。
  • 尽管 Ansible 可管理数千个服务器,但它只配备(Provision)了一个容器。

此方法将容器视为小型 VM,您可以在其中使用一个特定的解决方案。幸运的是,Ansible 拥有模块化设计。模块分散在不同的存储库中,而且 Ansible 的大部分功能都可以通过插件进行扩展。

在下一节中,您将设置一个有效的环境,针对您的需求来调整 Ansible。

设置一个 Ansible 环境

假设您想要一个很容易部署的工具,它在轻量型容器中配置应用程序环境。由于与这些容器分离,您需要一个安装了 Ansible 的客户端,您将使用它来向 Docker 守护进程发送命令。此设置如 图 1 中所示。

图 1. 使用 Ansible 配备容器所需的组件

您在此配置中必须管理的依赖项,已通过从容器中运行 Ansible 来最小化。此架构将主机限制为容器与命令之间的一个通信桥梁。

可通过许多选项在您服务器上安装 Docker:

  • 使用 docker-machine 将它安装在远程主机上。
  • 安装在本地。顺便说一下,您可能不想亲自管理一个严格基于容器的基础架构;在这种情况下,可以考虑采用外部提供程序。
  • 依赖于外部提供程序。
  • 使用 boot2docker,这是一个在 Windows 和 Mac 上运行 Docker 容器的轻量型 Linux 发行版。

无论选择何种解决方案,请确保部署了 Docker 1.3 版或更高版本(1.3 版引入了进程注入)。您还需要运行一个 SSH 服务器来安全地处理 Ansible 命令。

以下的命令使用公钥设置了一种方便可靠的身份验证方法。

# install dependencies
sudo apt-get install -y openssh-server libssl-dev
# generate private and public keys
ssh-keygen -t rsa -f ansible_id_rsa
# allow future client with this public key to connect to this server
cat ansible_id_rsa.pub >> ~/.ssh/authorized_keys
# setup proper permissions
chmod 0700  ~/.ssh/
chmod 0600  ~/.ssh/authorized_keys
# make sure the daemon is running
sudo service ssh restart

配置 SSH 和安全性问题不属于本文的讨论范围。细心的读者可查阅 /etc/ssh/sshd_config 文件,进一步了解配置 SSH 的可用选项。

下一步是将公钥加载到运行 Ansible 的客户端容器上并配备构建器容器。使用一个 Dockerfile 来配备构建器。参见以下代码。

配备构建器的 Dockerfile:

FROM python:2.7

# Install Ansible from source (master)
RUN apt-get -y update && \
    apt-get install -y python-httplib2 python-keyczar python-setuptools python-pkg-resources
git python-pip && \
    apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
RUN pip install paramiko jinja2 PyYAML setuptools pycrypto>=2.6 six \
    requests docker-py  # docker inventory plugin
RUN git clone http://github.com/ansible/ansible.git /opt/ansible && \
    cd /opt/ansible && \
    git reset --hard fbec8bfb90df1d2e8a0a4df7ac1d9879ca8f4dde && \
    git submodule update --init

ENV PATH /opt/ansible/bin:$PATH
ENV PYTHONPATH $PYTHONPATH:/opt/ansible/lib
ENV ANSIBLE_LIBRARY /opt/ansible/library

# setup ssh
RUN mkdir /root/.ssh
ADD ansible_id_rsa /root/.ssh/id_rsa
ADD ansible_id_rsa.pub /root/.ssh/id_rsa.pub

# extend Ansible
# use an inventory directory for multiple inventories support
RUN mkdir -p /etc/ansible/inventory && \
    cp /opt/ansible/plugins/inventory/docker.py /etc/ansible/inventory/
ADD ansible.cfg  /etc/ansible/ansible.cfg
ADD hosts  /etc/ansible/inventory/hosts

这些指令改编自官方构建版本,自动化了一次来自 Ansible 主分支上的提交 fbec8bfb90df1d2e8a0a4df7ac1d9879ca8f4dde 有效安装。

主机和 ansible.cfg 配置文件已封装。通过使用容器,可以确保您将共享同一个环境。在本示例中,Dockerfile 安装了 Python 2.7.10 版和 Ansible 2.0.0。

主机配置文件:

# hosts
# this file is an inventory that Ansible is using to address remote servers.
Make sure to replace the information with your specific setup and variables
that you don't want to provide for every command.

[docker]
# host properties where docker daemon is running
192.168.0.12 ansible_ssh_user=xavier

Ansible 配置文件:

# ansible.cfg

[defaults]

# use the path created from the Dockerfile
inventory = /etc/ansible/inventory

# not really secure but convenient in non-interactive environment
host_key_checking = False
# free you from typing `--private-key` parameter
priva_key_file = ~/.sh/id_rsa

# tell Ansible where are the plugins to load
callback_plugins   = /opt/ansible-plugins/callbacks
connection_plugins = /opt/ansible-plugins/connections

在构建 Ansible 容器之前,您必须导出 DOCKER_HOST 环境变量,因为 Ansible 将使用它连接到远程 Docker 守护进程。在使用 HTTP 端点时,需要修改 /etc/default/docker

# make docker to listen on HTTP and default socket
DOCKER_OPTS="-H tcp://0.0.0.0:2375 -H unix:///var/run/docker.sock"

输入命令 sudo service docker restart 来重新启动 Docker 守护进程,以便让对它的配置文件的更改生效。

以下命令将会构建并验证您用来输入命令的 Ansible 容器:

# you need DOCKER_HOST variable to point to a reachable docker daemon
# pick the method that suits your installation

# for boot2docker users
eval "$(boot2docker shellinit)"
# for docker-machine users, provisioning the running VM was named "dev"
eval "$(docker-machine env dev)"
# for users running daemon locally
export DOCKER_HOST=tcp://$(hostname -I | cut -d" " -f1):2375
# finally users relying on a remote daemon should provide the server's public ip
export DOCKER_HOST=tcp://1.2.3.4:2375

# build the container from Dockerfile
docker build -t article/ansible .

# provide server API version, as returned by `docker version | grep -i "server api"`
# it should be at least greater or equal than 1.8
export DOCKER_API_VERSION=1.19

# create and enter the workspace
docker run -it --name builder \
    # make docker client available inside
    -v /usr/bin/docker:/usr/bin/docker \
    -v /var/run/docker.sock:/var/run/docker.sock \
    # detect local ip
    -e DOCKER_HOST=$DOCKER_HOST \
    -e DEFAULT_DOCKER_API_VERSION=DOCKER_API_VERSION \
    -v $PWD:/app -w /app \  # mount the working space
    article/ansible bash

# challenge the setup
$ container > ansible docker -m ping
192.168.0.12 | SUCCESS => {
    "invocation": {
        "module_name": "ping",
        "module_args": {}
    },
    "changed": false,
    "ping": "pong"
}

目前为止,一切顺利。您能够从容器输入命令。在下一节中,将对 Ansible 使用特定于 Docker 的扩展。

使用 playbook 和插件扩展 Ansible 环境

实质上,Ansible 通过 playbook 自动化了它的执行,这些 playbook 是指定要执行的每个任务和它们的属性的 YAML 文件。

Ansible 还使用了清单 (inventory) 来将用户提供的主机映射到基础架构中的具体端点。不同于上一节中使用的静态 hosts 文件,Ansible 也支持动态内容。内置的列表包含一个 Docker 插件,该插件可查询 Docker 守护进程并向 Ansible playbook 共享大量信息。

# provision.yml

- name: debug docker host
  hosts: docker
  tasks:
  - name: debug infrastructure
    # access container data : print the state
    debug: var=hostvars["builder"]["docker_state"]

# you can target individual containers by name
- name: configure the container
  hosts: builder
  tasks:
   - name: run dummy command
     command: /bin/echo hello world

下面的命令查询 Docker 主机,导入事实,打印一些事实,并使用它们对构建器容器执行第二个任务(如上述所示)。

ansible-playbook provision.yml -i /etc/ansible/inventory
# ...
TASK [setup] ********************************************************************
fatal: [builder]: FAILED! => {"msg": "ERROR! SSH encountered an unknown error during the
connection. Re-run the command using -vvvv, which enables SSH debugging
output to help diagnose the issue", "failed": true}
# ...

Ansible 不能连接容器,因为它没有运行 SSH 服务器。SSH 服务器是一个要管理的额外进程,它与实际应用程序完全无关。在下一节中,我们将使用一个连接插件来排除此困难。

连接插件是实现传输命令(比如 SSH 或本地执行)的类。Docker 1.3 随带了 docker exec,并能够在容器命名空间内运行任务。此外,因为您之前已学习如何连接特定的目标容器,所以您可以使用此功能来处理 playbook。

像其他插件类型一样,连接挂钩继承一个抽象类,会在您将其放到预期的目录(您在配置文件 ansible.cfg 中定义的是/opt/ansible-plugins/connections)时自动可用。

# saved as ./connection_plugins/docker.py

import subprocess
from ansible.plugins.connections import ConnectionBase

class Connection(ConnectionBase):

   @property
    def transport(self):
        """ Distinguish connection plugin. """
        return 'docker'

   def _connect(self):
        """ Connect to the container. Nothing to do """
        return self

   def exec_command(self, cmd, tmp_path, sudo_user=None, sudoable=False,
                     executable='/bin/sh', in_data=None, su=None,
                     su_user=None):
        """ Run a command within container namespace. """

    if executable:
        local_cmd = ["docker", "exec", self._connection_info.remote_addr, executable, '-c', cmd]
    else:
        local_cmd = '%s exec "%s" %s' % ("docker", self._connection_info.remote_addr, cmd)

    self._display.vvv("EXEC %s" % (local_cmd), host=self._connection_info.remote_addr)
    p = subprocess.Popen(local_cmd,
        shell=isinstance(local_cmd, basestring),
        stdin=subprocess.PIPE, stdout=subprocess.PIPE,
        stderr=subprocess.PIPE)

    stdout, stderr = p.communicate()
    return (p.returncode, '', stdout, stderr)

    def put_file(self, in_path, out_path):
        """ Transfer a file from local to container """
        pass

    def fetch_file(self, in_path, out_path):
        """ Fetch a file from container to local. """
        pass

    def close(self):
        """ Terminate the connection. Nothing to do for Docker"""
        pass

此代码挂钩到 Ansible 模块中,以便通过一个本地 docker exec 而不是默认的 ssh 来运行命令。您需要重新排列一些设置步骤来告诉 Ansible 使用此插件。

# modify the builder Dockerfile to upload the plugin code
where Ansible is expecting connection plugins
echo "ADD connection_plugins/docker.py /opt/ansible-plugins/connections/docker.py" >> Dockerfile

# then, you need to explicitly tell which connection hook to use
when executing playbooks.
# you can achieve this by inserting the 'connection' property at the top
of provision tasks in provision.yml

- name: configure the container
  connection: docker
  hosts: builder

# you are ready to redeploy the builder container
# (providing DOCKER_HOST and DOCKER_API_VERSION are still set like before)

# rebuild the image
docker build -t article/ansible .

# restart the builder environment
docker run -it --name builder \
    # make docker client available inside
    -v /usr/bin/docker:/usr/bin/docker \
    -v /var/run/docker.sock:/var/run/docker.sock \
    # detect local ip
    -e DOCKER_HOST=$DOCKER_HOST \
    -e DEFAULT_DOCKER_API_VERSION=DOCKER_API_VERSION \
    -v $PWD:/app -w /app \  # mount the working space
    article/ansible bash

# rerun provisioning from inside
ansible-playbook -i /etc/ansible/inventory provision.yml
# ... Hurrah, full green output ...

目前为止,您在容器中运行 Ansible 任务,对容器或主机没有太多需求。尽管此实现满足了初始需求,但仍有一些不严密的地方需要解决。

前面的代码在同一个节点上运行任务。一种更逼真的工作流会启动一个新基础镜像,配置它,最终提交、推送和关闭得到的工件。得益于 Ansible 中内置的 Docker 模块,这些步骤无需额外的代码即可实现。

---
- name: initialize provisioning
  hosts: docker

  - name: start up target container
    docker:
      image: python:2.7
      name: lab
      pull: missing
      detach: yes
      tty: yes
      command: sleep infinity
      state: started
  # dynamically update inventory to make it available down the playbook
  - name: register new container hostname
    add_host: name=lab

- name: provision container
  connection: docker
  hosts: lab
  tasks:
      # ...

- name: finalize build
  hosts: docker
  tasks:
    - name: stop container
      docker:
        name: lab
        image: python:2.7
        state: stopped

前面已经提到过,自动命名和存储在成功配备后构建的镜像会很方便。不幸的是,Ansible 中的 Docker 模块没有实现方法来标记和推送镜像。您可以使用简单的 shell 命令来克服此限制。

# name the resulting artifact under a human readable image tag
docker tag lab article/lab:experimental

# push this image to the official docker hub
# make sure to replace 'article' by your own Docker Hub login (https://hub.docker.com)
# (this step is optional and will only make the image available from any docker host.
You can skip it or even use your own registry)
docker push article/lab:experimental

我们的工具正在成形,但它仍缺少一个必要特性:层缓存。

在使用 Dockerfile 构建容器时,通常需要迭代许多次才能完成。为了显著加快该过程,成功的步骤会被缓存并在后续运行中重用。

要复制此行为,我们的工具在每次成功完成任务后提交了容器状态。如果发生构建错误,该工具会从上次的快照位置重新启动配备过程。Ansible 承诺实现幂等的任务,所以以前成功的任务不会处理两次。

借助 Ansible,您可以使用回调插件来挂住任务事件。这些类应实现了特定的回调,这些回调在 playbook 生命周期的各个步骤上触发。

# save as callback_plugins/docker-cache.py
import hashlib
import os
import socket

# Hacky Fix `ImportError: cannot import name display`
# pylint: disable=unused-import
import ansible.utils
import requests
import docker


class DockerDriver(object):
    """ Provide snapshot feature through 'docker commit'. """

    def __init__(self, author='ansible'):
        self._author = author
        self._hostname = socket.gethostname()
        try:
            err = self._connect()
        except (requests.exceptions.ConnectionError, docker.errors.APIError), error:
            ansible.utils.warning('Failed to contact docker daemon: {}'.format(error))
            # deactivate the plugin on error
            self.disabled = True
            return

        self._container = self.target_container()
        self.disabled = True if self._container is None else False

    def _connect(self):
        # use the same environment variable as other docker plugins
        docker_host = os.getenv('DOCKER_HOST', 'unix:///var/run/docker.sock')
        # default version is current stable docker release (10/07/2015)
        # if provided, DOCKER_VERSION should match docker server api version
        docker_server_version = os.getenv('DOCKER_VERSION', '1.19')
        self._client = docker.Client(base_url=docker_host,
                                     version=docker_server_version)
        return self._client.ping()

    def target_container(self):
        """ Retrieve data on the container you want to provision. """
        def _match_container(metadatas):
            return metadatas['Id'][:len(self._hostname)] == self._hostname

        matchs = filter(_match_container, self._client.containers())
        return matchs[0] if len(matchs) == 1 else None

    def snapshot(self, host, task):
        tag = hashlib.md5(repr(task)).hexdigest()
        try:
            feedback = self._client.commit(container=self._container['Id'],
                                           repository='factory',
                                           tag=tag,
                                           author=self._author)
        except docker.errors.APIError, error:
            ansible.utils.warning('Failed to commit container: {}'.format(error))
            self.disabled = True


# pylint: disable=E1101
class CallbackModule(object):
    """Emulate docker cache.
    Commit the current container for each task.

    This plugin makes use of the following environment variables:
        - DOCKER_HOST (optional): How to reach docker daemon.
          Default: unix://var/run/docker.sock
        - DOCKER_VERSION (optional): Docker daemon version.
          Default: 1.19
        - DOCKER_AUTHOR (optional): Used when committing image. Default: Ansible

    Requires:
        - docker-py >= v0.5.3

    Resources:
        - http://docker-py.readthedocs.org/en/latest/api/
    """

    _current_task = None

    def playbook_on_setup(self):
        """ initialize client. """
        self.controller = DockerDriver(self.conf.get('author', 'ansible'))

    def playbook_on_task_start(self, name, is_conditional):
        self._current_task = name

    def runner_on_ok(self, host, res):
        if self._current_task is None:
            # No task performed yet, don't commit
            return
        self.controller.snapshot(host, self._current_task)

因为您已将代码上传到期望的位置,并重新构建了构建器容器,所以您可以像 docker exec 连接插件一样注册此插件。

# modify the builder Dockerfile to upload the code where Ansible is expecting callback plugins
echo "ADD callback_plugins/docker-cache.py /opt/ansible-plugins/callbacks/docker-cache.py" >> Dockerfile

重新构建构建器容器并重新运行 Ansible playbook 后,该模块会自动加载,您可以查看中间容器是如何创建的。

REPOSITORY          TAG                     IMAGE ID            CREATED             VIRTUAL SIZE
factory             bc0fb8843e88566c    bbdfab2bd904        32 seconds ago      829.8 MB
factory             d19d39e0f0e5c133    e82743310d8c        55 seconds ago      785.2 MB

结束语

配备是一个复杂流程,您在本教程中执行的实现为未来的开发奠定了基础。代码本身已经过简化,而且一些步骤仍需人类干预。缓存实现肯定值得更多关注,例如更具体的提交命名或清理技能。

尽管如此,您创建了一个可运行 Ansible playbook 来管理容器配置的工具。借助此实现,您可以通过为基础架构的微型服务来组合、重用和设置陈述性的构建文件,充分利用 Ansible 的全部威力。此解决方案可帮助避免锁定问题。您开发的插件包装了 playbook,您可对不同的目标重用这些 playbook,而且极低的需求使得该项目能兼容大部分提供程序。

Linux:MBR & GPT

MBR & GPT[转] 2011-04-25 08:57

MBR 和 GPT 的问题已经走到了非常现实的阶段-3T硬盘开始普及。MBR 由于对 3T 硬盘寻址无法支持,终于要开始被弃用了。但仅仅只是“开始”,我感觉至少10年之内 MBR 还不会消失。恐怕当GPT适应不了需求的时候,MBR才会消失。 一、MBR 的失误 说“失误”牵强了一点。我们不能要求当年只有5M的硬盘就扣出2M来放寻址信息。计算机永远比你想象的要发展的快。 首先你要有一些基本的硬盘的MBR知识,如果你不熟悉,可以先在 wiki 看一看硬盘的基本结构。 在了解了磁道、柱面、扇区之后,再看下面的 MBR 结构就很了然了:

基本MBR结构
偏移量(位置) (within sector) 长度 (in bytes) 解释
十进制 十六进制
000 – 445 000 – 1BD 446 引导程序
446 – 509 1BE – 1FD 64 分区表
510 – 511 1FE – 1FF 2 引导记录

表 1.

 

MBR 永远占用了第一个扇区,通常为512bytes大小。在这512bytes中被MBR本身的程序用去了446bytes。于是,真正描述分区信息的就只有 64 bytes。而根据4个主分区的设计,每一个分区能用到的只有 16bytes. 那么!这16bytes是如何工作的?

16-byte 分区表结构
Relative Offsets (within entry) 长度

(bytes)

内容
0 1 引导标记 (80h = active)
1 – 3 3 CHS 开始位置
4 1 分区类型描述
5 – 7 3 CHS 结束位置
8 – 11 4 启始扇区
12 – 15 4 分区大小 (扇区单位)

表 2.

 

在表2中,有2个“开始位置”,我们分开理解。 硬盘的历史过程中,第一个遇到的问题就是 8G 瓶颈。这就是因为在 CHS 描述方式中,描述起点和终点均为3个 byte。我们假设是 FF FF FF ,所以是 16*16*16*16*16*16*512=8589934592 这就是 8G 瓶颈的由来了。 但是今天 CHS 方式几乎已经不用了。后面还有 4 个 bytes 的“启始” 是给 LPA 方式用的。他的极限就是 8589934592 * 256 =2199023255552 这就是 2T 的限制由来了。 在硬盘容量的历史中,有很多因素决定了它的限制。BIOS,ATA(IDE),文件系统。百度上的这篇文章简单的介绍了各个阶段的限制。

二、GPT 的思路

GPT (GUID Partition Table)属于 EFI 方案中的一部分。虽然很早就开始制定这个标准,但到今天,还是有很多系统不支持 GPT。

首先,引用一张wikipedia上的结构图,来了解一下 GPT 的基本结构。

GPT 为了兼容 MBR,LBA 0 依旧保留了MBR的结构。在GPT工作是,会优先读取 GPT (LBA1) 内容。如果没有 GPT 内容,则认为这是一块MBR磁盘。再从LBA 0 读取MBR。

在硬盘末尾,GPT 备份了一份,这样当GPT出错时,可以快速的从硬盘末尾恢复。LBA -1 (负1) 表示倒数第一块 LBA。

从LBA 2 到 LBA 33 ,一共预留了 128 个分区表空间。 GPT 支持在一块硬盘上创建 128 个分区(真有这种需求?)。所以每一个分区可以使用 128 bytes 的空间。

来看一看 LBA 1 的结构吧,有了 512bytes 的巨型空间,都用不掉了。

 

0

8字节

签名(”EFI PART”, 45 46 49 20 50 41 52 54)

8

4字节

修订(比如,1.0,值是 00 00 01 00)

12

4字节

分区表头的大小(单位是字节,通常是92字节,即 5C 00 00 00)

16

4字节

分区表头前3项(第0-15字节)的CRC32校验,如果值正在计算,则是 0

20

4字节

保留,必须是 0

24

8字节

当前LBA(这个分区表头的位置)

32

8字节

备份LBA(备份分区表头的位置)

40

8字节

第一个可用于分区的LBA(主分区表的最后一个LBA + 1)

48

8字节

最后一个可用于分区的LBA(备份分区表的最后一个LBA − 1)

56

16字节

硬盘GUID(在类UNIX系统中也叫UUID

72

8字节

分区表项的起始LBA(对于主分区表来说,这个值是 LBA 2)

80

4字节

分区表的数量

84

4字节

一个分区表的大小(通常是128)

88

4字节

分区串行的CRC32校验

92

* 保留,剩余的字节必须是0(对于512字节LBA的硬盘即是420个字节)

 

此表来源为 中文 Wikipedia, 稍微修改了两个字,便于理解。

通过这张表,程序可以获知分区数量,第一个分区在哪个位置,以及GPT头是否正确(CRC32)。在这里着重解决的是分数数量问题,以及分区表的安全性问题。而分区的大小问题,则依赖了 GPT 的分区表:

 

0

16字节

分区类型GUID

16

16字节

分区GUID

32

8字节

起始LBA(小端序

40

8字节

末尾LBA

48

8字节

属性标签(如:60表示“只读”)

56

72字节

分区名(可以包括36个UTF-16(小端序)字符)

 

重点就在这里了,在描述分区位置的时候,使用了8个字节,其最大值为 FF FF FF FF FF FF FF FF 。那么,假设每一个LBA 为512 bytes。其能描述的大小为 8589934592 TB = 8 ZB 。关于这个数字我算下来的确是在 ZB 级别的。但是搜索到的文章说法不一:

突破2TB限制 3TB硬盘装操作系统实战 9.4ZB (GPT分区表采用8个字节即64bit来存储扇区数,因此它最大可支持264个扇区。) 关于windows server 2003最大支持硬盘容量的问题 18EB

对于上面这些数字,18EB 恐怕是 LBA 数量,忘记乘 512 bytes 了。9.4ZB 暂时不知道是怎么来的。但这个数字暂且不做定论。因为微软的 FAQ 也提到了,这只是理论值,谁知道下一个瓶颈在哪里呢。

Q. How big can a GPT disk be?

A. In theory, a GPT disk can be up to 2^64 logical blocks in length. Logical blocks are commonly 512 bytes in size.

The maximum partition (and disk) size is a function of the operating system version. Windows XP and the original release of Windows Server 2003 have a limit of 2TB per physical disk, including all partitions. For Windows Server 2003 SP1, Windows XP x64 edition, and later versions, the maximum raw partition of 18 exabytes can be supported. (Windows file systems currently are limited to 256 terabytes each.)

 

Linux:如何在 Ubuntu 启用 Radeon 开源驱动的 UVD 硬件解码

Phoronix 今天公布了在 Ubuntu 启用 Radeon 开源驱动 UVD 硬件解码的方法。

首先,你需要一块 Radeon HD 4000 系列或更新的显卡。然后,可以按照以下步骤逐步安装:

  • 安装 VPDAU 的头文件:sudo apt-get install libvdpau-dev
  •  Mesa 的 Git 仓库抓取最新代码,加上 –with-gallium-drivers=r600 –enable-vdpau 编译参数进行编译安装。
  • 在 /etc/ld.so.conf.d/z.conf 加上 /usr/local/lib/vdpau 参数,没有这个配置文件可自行创建,然后执行 ldconfig 命令。
  • 安装 drm-next 分支的 3.10 内核,可以从 Ubuntu Mainline Kernel PPA 下载安装。下载地址
  • 下载 GPU 对应型号的 UVD Firmware 并放入到 /lib/firmware 目录,若不清楚显卡型号,可以全部下载。下载地址 
  • 重启电脑,选择新内核进入系统。
  • 使用兼容 VDPAU 接口的播放器进行播放即可。

提示一:Linux 版本的 Flash Player 是通过 VDPAU 获得 GPU 硬件解码的,理论上 Flash Player 也可以利用 UVD 进行播放视频。

提示二:其他发行版可以参考以上指南,根据实际环境配置和调整。

结合早前 Phoronix 进行的 Radeon Gallium3D 性能测试,AMD 的 Radeon 开源驱动进展良好,某程度上开源驱动和 Catalyst 闭源驱动的差距越来越小。有组建 Linux HTPC 的朋友可以考虑一下。使用笔记本的朋友,还是先安抚一下电池君,给多一点耐心吧。

Phoronix 原文链接 

via http://linuxtoy.org/archives/how-to-useopen-source-radeon-uvd-on-ubuntu.html