Feb 19

一、简介

GTK+ 是一套跨平台的图形用户界面(Graphical User Interface)开发工具包,按 LGPL 许可协议发布的。虽然最初是为 GIMP(GNU Image Manipulation Program) 写的,但早已发展为一个功能强大、设计灵活的通用图形库。特别是在 GTK+ 被 Linux 桌面项目 GNOME 选中使得它广为流传,成为 Linux 下开发图形用户界面应用程序的主流开发工具之一。当然 GTK+ 应用程序开发与运行并不要求必须在 Linux 上,事实上,GTK+ 早已经被成功地移植到了 Mac OS X 以及 MS Windows 上。在开发早期的 GIMP 版本时,Peter Mattis 和 Spencer Kimball 创建了 GTK(GIMP Toolkit) 工具包,作为 Motif 工具包的替代,后者在那个时候不是免费的。当 GTK 开发工具包获得了面向对象特性和可扩展性之后,才在名称后面追加上了符号 "+",也即 GTK+。

二、GCC 工具链

GCC(GNU Compiler Collections) 是 Linux 系统中默认的开发工具、Autotools 是 Linux 中开发大型项目管理工具。Red Hat 系中这些开发工具的安装可执行

$ sudo dnf install "Development Tools"

Debian 系可执行

$ sudo apt-get install build-essential

Gentoo 由于本身的理念就是编译所有的包,因此系统已经自带了 GCC 工具链,无须额外安装其他 GCC 开发工具。

三、C/C++ 的手册页

在编程的过程中有时会记不得某个函数的用法,此时查找手册页是比较快的。为了能够用命令行 man 查看 C/C++ 的函数,需要安装相关的手册页及其工具。在 Red Hat 系中执行

$ sudo dnf install man-pages libstdc++-docs

Debian 系可执行

$ sudo apt-get install manpages-dev manpages-posix manpages-posix-dev glibc-doc libstdc++6-4.3-doc

Gentoo 中安装稍微麻烦一点,可执行

$ sudo sh -c 'echo "sys-devel/gcc doc cxx " >> /etc/portage/package.use'
$ sudo emerge -av1 gcc
$ sudo emerge -av man-pages man-pages-posix

手册页的索引由 mandb 命令管理,有时在安装了新的手册页后,可能需要更新一下索引才能用 man kman f 查询到函数,也即

$ mandb c

然后就可以查看这些文档了。比如,查看 fopen 的手册页:

$ man fopen

四、GTK+ 库的概述

GTK+ 是基于以下库开发的:

  • glib:GTK+ 与 GNOME 的底层核心库,主要提供了 C 数据结构、可移植封装、运行时功能接口,譬如事件循环、多线程、动态装载和对象机制等。换而言之,Glib 是 GTK+ 能够“面向对象”的基础。GTK+ 中与界面无关的底层部分基本都被并入 glib。GLib 库还进一步分离成 GIO、GObject 等库。GIO 专门处理输入输出流。而 GObject 则维护着 GTK+ 所使用的一套对象系统。正是 GObject 提供的面向对象的机制,使得 GTK+ 可绑定很多种开发语言,例如 C++、Python、Perl、Java、C#、PHP 等其他高级语言。
  • pango:国际化文本陈列及渲染库,它是 GTK+ 的文本与字体处理核心;
  • atk:可访问接口库,它可以让 GTK+ 程序很方便地使用屏幕阅读器、放大镜以及一些输入设备等;
  • cairo:过去 cario 被称为 Xr 或 Xr/Xc,它是一个跨平台的开放源代码的矢量图形函数库,可以提供高质量的显示和打印输出。通过 Glitz 函数库, Cairo 能使用 OpenGL 或 X Render 扩展的硬件加速功能来绘制图像,这基于 Cairo 的应用能在现代化的 3D 显示硬件上获得益处。
  • gdk-pixbuf:GDK(GDK 是 GTK+ 从窗口系统细节中提取出来的一组接口,可以直接访问窗口细节。实际上,GDK 是 GTK+ 用户界面图形库提供的一些底层“图形实现”和“窗口实现”的方法,在 Linux 中,GDK 是对 Xlib 库提供接口的封装。)的一个部分,提供了一组位图函数,包括位图变换、位图文件读写等等,用于加载图像和维护图像“缓存”的(pixel buffer)。

要装 GTK+,先安装这写库,当然它们又依赖其它一些库,在 Linux 系统中可完全交由 Linux 的包管理器处理。还有一些库是 GTK+ 运行依赖的,下面是几个重要的:

  • gettext:它是国际化库,主要用于制作多语言程序。运行时 gettext 自动识别操作系统语言,然后从已有语言包中选择一个最合适用户的语言。当操作系统上没有 gettext() 函数的时候需要安装它。
  • libiconv:它是字符集转换库,通常操作系统上没有提供 iconv() 函数的时候才会安装它。GTK+ 内部使用 UTF-8 字符集,有时需要字符集转换。
  • freetype:它是一个操作字体的函数库,不但可以处理点阵字体,也可以处理多种矢量字体,包括 truetype 字体,为上层应用程序提供了一个统一的调用接口。
  • fontconfig:它提供系统范围内字体设置、定制和允许应用程序访问的函数库。实际上,GTK+ 的字体绘制是通过 pango、freetype 与 fontconfig 三者协作来完成的。其中,fontconfig 负责字体的管理和配置,freetype 负责单个字符的绘制,pango 则完成对文字的排版布局。
  • libjpeg:它提供了 JPEG 算法压缩文件图形文件供 GTK+ 程序读写 JPEG 格式的图形文件;
  • libtiff:它是用操作 TIFF 格式(标记图象文件格式)的图形文件并为 GTK+ 程序读写 TIFF 格式的图形文件的库;
  • libpng:它是用来创建与操作 PNG 格式的图形文件并为 GTK+ 程序提供读写 PNG 文件的库。PNG 格式的图形文件是被设计来替代 GIF 格式的,它对于更小范围的 TIFF 格式是来说,有了很多的进步和拓展并且减少了关于专利权的麻烦。

五、GTK+ 的 C 开发包

目前,GTK+ 开发包有三个版本:GTK+ 1、GTK+ 2 与 GTK+ 3,在 Linux 系统上是可以共存的,但由它们开发的程序需要对应的 GTK+ 开发包才能正常编译与运行。现在 GTK+ 2 是主流,但都在向 GTK+ 3 过度。根据自己的需要选择 GTK+ 版本,这里就以 GTK+ 2 为例。安装 GTK+ 开发环境,在 Red Hat 系中执行

$ sudo dnf install gtk2-devel gtk2-devel-docs

Debian 系可执行

$ sudo apt-get install libgtk2.0-dev libgtk2.0-doc

Gentoo 中执行

$ sudo sh -c 'echo "x11-libs/gdk-pixbuf doc \
                    dev-libs/glib doc \
                    x11-libs/pango doc \
                    x11-libs/cairo doc \
                    dev-libs/atk doc \
                    x11-libs/gtk+ doc" >> /etc/portage/package.use'
$ sudo emerge -av gtk+

若在开发过程中需整合 GNOME 环境,在 Red Hat 系中执行

$ sudo dnf install libgnome-devel gnome-devel-docs

Debian 系可执行

$ sudo apt-get install gnome-devel gnome-devel-docs

Gentoo 中执行

$ sudo emerge -av gnome-devel-docs

六、GTK+ 应用程序接口文档查看器

在 GNOME 桌面中,查看 GTK+ 应用程序接口文档的查看器是 DevHelp。安装它的方法很简单,在 Red Hat 系中执行

$ sudo dnf install devhelp

Debian 系可执行

$ sudo apt-get install devhelp

Gentoo 中执行

$ sudo emerge -av devhelp

通常,这些 API 文档会随着开发工具包的安装会被放入 /usr/share/gtk-doc/ 目录下。有了 DevHelp,查看它们非常容易,只需要在应用程序列表中点击 Devhelp 图标,或者在终端模拟器中执行

$ devhelp

八、pkg-config 的安装与使用

pkg-config 是编译器的辅助工具,可以帮助 GCC 找到所需要的头文件与库文件路径。它的安装非常简单。在 Red Hat 系中执行

$ sudo dnf install pkg-config

Debian 系可执行

$ sudo apt-get install pkg-config

Gentoo 中执行

$ sudo emerge -av dev-util/pkgconfig

现在试试 pkg-config,先查看 Linux 系统中是否安装 glib 库:

$ pkg-config --list-all | grep -i glib

若已安装,例如 glib-2.0,接着查看一下所安装的 glib 的具体版本:

$ pkg-config --modversion glib-2.0

再来列出 GCC 所需要的 glib-2.0 头文件所在目录

$ pkg-config --cflags glib-2.0

接着列出 GCC 所需要的 glib-2.0 库文件所在目录

$ pkg-config --libs glib-2.0

当然 pkg-config 无法智能到无所不知的地步,它是通过存放在标准目录 /usr/lib/pkgconfig 下的 .pc 文件来查找 GCC 所需头文件与库文件路径的。当然,也可以通过设置环境变量 PKG_CONFIG_PATH 让 pkg-config 找到非标准目录下的 .pc 文件,在这些文件里同样记录了 GCC 所需要的头文件和库文件所在的路径。例如

$ export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig
$ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib

另外,值得提到一下 GTK+ 1 自带的工具 gtk-config,它与 pkg-config 的运作机制相像,但显然已被 pkg-config 取代。

九、GTK+ 的基本概念与约定

首先来解释 GTK+ 中几个基本的概念,以方便将来的分析。

  • 物件(GtkWidget):GTK+ 中每一个窗口里的组成要素都被视为一个物件,如按钮、文本等等,窗口本身也是一个物件。总之 GTK+ 的界面就是由物件构成的。注意,物件都使用指针来管理,物件外在表现就是一个特定类型的指针。
  • 容器(GtkContainer):物件里的一大类,容器的特点是其内部能够容纳其他物件。容器最基本的功能之一是将各种物件良好地组织起来。 GTK+ 的容器能在大小改变时自动调整内含物件的大小,这使得 GTK+ 能够很智能地相应窗口或其他物件的大小改变。这为我们提供了很大的方便,往往我们不需要指定某个物件的大小,只需说明他所在的容器位置, GTK+ 会把物件的实际位置和大小自动计算出来。
  • 继承、组合:虽然是 C 语言写的,但 GTK+ 灵活地运用了面向对象思想。 GTK+ 的物件体系中就有继承、组合这样的关系,如窗口(GtkWindow)是由容器(GtkContainer)派生出来的。
  • 类型转换宏:C 语言本身没有“继承”这个概念,那么,如果把派生的物件直接当做基物件使用,会出现一个编译警告,即“隐式指针类型转换”,但不会出错。为了消除这个警告,需要做指针类型转换。一般情况下类型转换使用类型转换宏。类型转换宏内部会检查物件的继承关系,确定能否进行转换,然后再做显式类型转换。
  • 事件(event):用户的操作,比如按下某个按钮或快捷键,被视为一个事件。
  • 信号(signal):GTK+ 是基于信号回调(signal-slot)机制的。信号捆绑了一个事件和一个函数,在用户触发这个事件时,这个函数会被调用一次。从这个角度来说, GTK+ 是基于物件的,即程序围绕物件属性、事件、方法进行。
  • 主循环(main loop):GTK+ 程序在一个主循环中运行。当一个事件被触发时,它将被插入队列中;在主循环中被触发的事件会被逐个处理(和这个事件绑定的函数被逐个调用);没有事件被触发时,程序就处于等待状态,等待下一个事件被用户触发。直到退出主循环的函数被调用,GTK+ 程序才结束。

GTK+ 拥有开源软件的很多特点,比如结构高度严谨、可读性甚好。现在介绍一下 GTK+ 的关键字命名方式也即 GTK+ 命名规范,以便阅读一段 GTK+ 程序。

  • 普通变量类型名:全小写写法。其中以 "g" 开头属于 GLib 库,如 "gint";
  • 物件类型名:驼峰写法(首字母大写),以 "Gtk" 开头,形如 "GtkWindow"。在 GTK+ 内部,类型是像下面这样定义的(以 GtkWindow 为例)。
    typedef _GtkWindow GtkWindow
  • 函数名:小写夹下划线写法,以 "gtk_"、"g_" 为前缀,形如 "gtk_main()"、"g_print()"。如果是针对某类物件的函数,则前缀中还有物件类型名,形如 "gtk_window_new()"。
  • 常量名:大写夹下划线写法,以 "GTK_" 为前缀,形如 "GTK_WINDOW_TOPLEVEL"。
  • 类型转换宏:大写夹下划线写法,以 "GTK_" 为前缀。一般来说,宏名字和类型名相仿,比如要把 GtkWindow* 类型的物件转换为 GtkContainer* 类型,就使用宏 "GTK_CONTAINER()"。

GTK+ 本身只负责界面组织,它提供的函数大致可分为三类,物件(Widget)、对象(Object)和其它工具函数。物件就不多说了,GTK+ 中的对象是一些功能更加复杂的不可见元素,它们和界面息息相关,比如 GtkBuilder。工具函数提供一些与界面关系密切的实用功能,比如剪贴板读写。

十、GTK+ 开发环境的简单测试

GTK+ 安装完成后,做个测试程序,代码如下

$ cat hellogtk.c

#include <gtk/gtk.h>

void hello(GtkWidget *widget,gpointer data)
{
    g_print("Hello GTK+!\n");
}

gint delete_event(GtkWidget *widget,GdkEvent *event,gpointer data)
{
    g_print ("delete event occurred\n");
    return(TRUE);
}

void destroy(GtkWidget *widget,gpointer data)
{
    gtk_main_quit();
}

int main( int argc, char *argv[] )
{
    GtkWidget *window;
    GtkWidget *button;
    gtk_init (&argc, &argv);
    window=gtk_window_new (GTK_WINDOW_TOPLEVEL);
    gtk_signal_connect (GTK_OBJECT(window),"delete_event",GTK_SIGNAL_FUNC(delete_event),NULL);
    gtk_signal_connect (GTK_OBJECT (window), "destroy",GTK_SIGNAL_FUNC (destroy), NULL);
    gtk_container_set_border_width (GTK_CONTAINER (window), 10);
    button = gtk_button_new_with_label ("Hello Ubuntu!");
    gtk_signal_connect (GTK_OBJECT (button), "clicked",GTK_SIGNAL_FUNC (hello), NULL);
    gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
                                                 GTK_SIGNAL_FUNC(gtk_widget_destroy),GTK_OBJECT (window));
    gtk_container_add (GTK_CONTAINER (window), button);
    gtk_widget_show (button);
    gtk_widget_show (window);   /*显示一个窗口*/
    gtk_main();   /*进入主循环*/
    return(0);
}

用下面命令编译运行

$ gcc hellogtk.c -o hellogtk `pkgconfig --cflags --libs gtk+2.0`
$ ./hellogtk

会显示带有一个按钮的窗口,点击按钮以后窗口关闭,命令行显示 Hello GTK+!

十一、安装 GTK+ 界面快速设计工具

Glade 是 GTK+ 的界面辅助设计工具,可以通过拖放控件的方式快速设计出用户界面,这样的优势在于在设计的同时能直观地看到界面上的控件,并且可以随时调整界面上的设计。用 Glade 设计的图形用户界面是以 XML 格式的文件保存,它描述了控件的结构、每个控件的属性。用户可以动态加载这个界面文件。而且,界面和程序逻辑是完全分离,用户修改了界面,也不需要重新编译程序。Glade 有三个版本:基于 GTK+ 1 版本的 Glade、基于 GTK+ 2 及之后版本的 Glade2、Glade3。一般推荐使用 Glade3 设计 GTK+ 程序界面。

Glade 的布局文件有 2 种格式: Libglade、GtkBuilder。由于布局文件格式的不一样,最终使用的库函数不一样。选择 GtkBuilder 直接生成 XML 格式文件,但是后缀名仍是 .glade。如果选择 Libglade,可通过下述命令行

$ gtk-builder-convert

将它装换为 XML 格式文件。总的来说,推荐使用 GtkBuilder 格式的布局文件。

Glade 的安装很容易,不要要记住,若要使用 Libglade 格式的布局文件,需要安装 libglade 库。在 Red Hat 系中执行

$ sudo dnf install glade3 libglade2-devel

Debian 系可执行

$ sudo apt-get install glade libglade2-dev

Gentoo 中执行

$ sudo sh -c 'echo "dev-util/glade doc" >> /etc/portage/package.use'
$ sudo emerge -av dev-util/glade libglade

安装完成后,在应用程序列表中点击 Devhelp 图标,或者在终端模拟器中执行

$ glade

即可启动 Glade 工具。

1)Glade 画 UI,注意保存为 Libglade 格式,然后在下述 C 代码中使用该布局文件

$ cat libgladedemo.c

#include <glade/glade.h>
#include <gtk/gtk.h>

//假设布局文件定义了一个名为 button1 的按钮,并且其 clicked 信号处理函数如下
//若是在 MS Windows 中,回调函数要加上修饰词 G_MODULE_EXPORT,也即
//G_MODULE_EXPORT
void on_button1_clicked(GtkWidget* widget,gpointer data)
{
    g_print("hello, world!\n\r");
}

int main(int argc,char **argv)
{
    //Libglade 类型,用于布局
    GladeXML *gxml;
    GtkWidget *window;
    gtk_init(&argc,&argv);

    //下面开始通过文件获取布局信息了
    gxml=glade_xml_new("glade.glade",NULL,NULL);
    //信号连接
    glade_xml_signal_autoconnect(gxml);

    //获取构件
    window=glade_xml_get_widget(gxml,"window1");  /* window1 是 glade3 中窗口的名字*/
    button = glade_xml_get_widget(gxml,"button1");  /* button1 是 glade3 中按钮的名字*/

    gtk_widget_show(window);
    gtk_main();
    return 0;
}

在编译代码时,加上 libglade-2.0,如:

$ gcc libgladedemo.c -o libgladedemo `pkg-config --cflags --libs gtk+-2.0 libglade-2.0`
$ ./libgladedemo

2)Glade 画 UI,注意保存为 GtkBuilder 格式,同样在 C 代码中使用该布局文件

$ cat gtkbuilderdemo.c

#include <gtk/gtk.h>

//假设布局文件定义了一个名为 button1 的按钮,并且其 clicked 信号处理函数如下
//若是在 MS Windows 中,回调函数要加上修饰词 G_MODULE_EXPORT,也即
//G_MODULE_EXPORT
void on_button1_clicked(GtkWidget* widget,gpointer data)
{
    g_print("Hello World !\r\n");
}

int main (int argc, char **argv)
{
    //GtkBuilder 类型,用于布局
    GtkBuilder *gtkBuilder;
    GtkWidget *mainwin;
    gtk_set_locale();
    /* Initialize the widget set */
    gtk_init (&argc, &argv);
    /* Create the main window */
    //通过 main.glade 建立布局
    gtkBuilder= gtk_builder_new();
    gtk_builder_add_from_file(gtkBuilder,"main.glade",NULL);

    //连接信号,信号名在布局文件中定义,实现在 cpp 文件中
    gtk_builder_connect_signals (gtkBuilder, NULL);

    // 通过布局文件获得构件,此处为一个对话框型的窗体
    mainwin= GTK_WIDGET(gtk_builder_get_object(gtkBuilder,"dialog1"));
    button = GTK_WIDGET(gtk_builder_get_object (builder, "button1"));
    g_object_unref ( G_OBJECT(gtkBuilder) );
    /* Show the application window */
    gtk_widget_show_all ( mainwin );
    /* Enter the main event loop, and wait for user interaction */
    gtk_main ();
    /* The user lost interest */

    return 0;
}

在编译代码时,无须增加额外库,也即:

$ gcc gtkbuilderdemo.c -o gtkbuilderdemo `pkg-config --cflags --libs gtk+-2.0`
$ ./gtkbuilderdemo

十二、GTK+ 的其他语言开发包

将 GTK+ 与 C 语言的近亲 C++ 绑定,便是 GTKmm。Glade 还是 GTKmm 的界面快速构建工具,推荐使用 Glade3。若使用 libglade 格式的界面布局文件,需额外安装相应的库。在 Red Hat 系中执行

$ sudo dnf install gtkmm24-devel gtkmm24-docs libglademm24-devel

Debian 系可执行

$ sudo apt-get install libgtkmm-2.4-dev libgtkmm-2.4-doc libglademm-2.4-dev libglademm-2.4-doc

Gentoo 中执行

$ sudo sh -c 'echo "dev-cpp/libsigc++ \
                    dev-cpp/glibmm doc \
                    dev-cpp/pangomm doc \
                    dev-cpp/cairomm doc \
                    dev-cpp/atkmm doc \
                    dev-cpp/gtkmm doc \
                    dev-cpp/libglademm doc" >> /etc/portage/package.use'
$ sudo emerge -av gtkmm libglademm

测试一下 GTKmm 开发环境,下面是一个简单 Hello GTKmm 的例子

$ cat hellogtkmm.cpp

#include <gtkmm.h>
#include <libglademm/xml.h>         /* 访问Glade文件所需的头文件 */
#include <iostream>                  /* 输出错误信息到控制台 */
#include <assert.h>                  /* assert()断言 */
 
using namespace Gnome::Glade;
 
class HelloApp: public Gtk::Main {    /* 继承“Gtk::Main”类 */
public:
    HelloApp(int argc, char *argv[]):
        Gtk::Main(argc,argv),             /* 必须在初始化列表中调用父类的构造函数 */
        main_window(0)                     /* 为主窗口的指针赋初值 */
    {
        try
        {
            /* 从同目录下的GLADE文件创建“Gnome::Glade::Xml”对象 */
            ref_xml = Xml::create("helloapp.glade");
        }
        catch(const XmlError& ex)
        {
            /*   出错时错误信息输出到控制台,并返回 */
            std::cerr << ex.what() << std::endl;
            return;
        }
   
        /*
          取得主窗口的指针,存入main_window变量中,并确保成功。
          main_window为0值表示失败,多是因为指定的控件名称不正确或不存在。
          第一个参数就是Glade文件中定义的控件名称,是字符串型。
        */
        ref_xml->get_widget("main_window", main_window);
        assert(main_window);
    }
 
    ~HelloApp()
    {
        /* 由于采用了智能指针,我们不需要管理资源的释放 */
    }
 
    show_window()
    {
        /*
          如果取得主窗口的指针成功,就调用父类的run()函数显示它,
          并进入监听事件的循环状态,当主窗口关闭时返回。
        */
        if (main_window) {
            run( *main_window );
        }
    }
   
protected:
    /* 通过它访问Glade文件的内容,是一种智能指针,能自动释放占用的资源 */
    Glib::RefPtr<Gnome::Glade::Xml> ref_xml;
   
    Gtk::Window* main_window;         /* 存储主窗口的指针 */
};
 
int main (int argc, char *argv[])
{
    /*
      可以看到,这跟“最简单的Gtkmm程序”非常相似。HelloApp继承自
      “Gtk::Main”类,也需要通过 argc 和 argv 两个参数进行实例化。
    */
    HelloApp app(argc, argv);
   
    /*
      在这个函数中调用Gtk::Main::run函数来实现与上面相同的功能。   
      这里不需要窗口对象作参数,因为它已封装在HelloApp类中了。
    */
    app.show_window();
   
    return 0;
}

$ g++ hellogtkmm.cpp -o hellogtkmm `pkg-config --cxxflags --libs gtkmm-2.0`

$ ./hellogtkmm

将 GTK+ 与 Python 语言绑定,便是 PyGTK。Glade 仍是 PyGTK 的界面快速构建工具,若使用 libglade 格式的界面布局文件,需额外安装相应的库。在 Red Hat 系中执行

$ sudo dnf install pygtk2-devel pygtk2-docs pygtk2-libglade

Debian 系可执行

$ sudo apt-get install python-gtk2-dev python-gtk2-doc python-glade2

Gentoo 中执行

$ sudo sh -c 'echo "dev-python/pygtk doc \
                    dev-python/pygobject doc \
                    dev-python/pycairo doc" >> /etc/portage/package.use'
$ sudo emerge -av pygtk pygtk2-libglade

还需测试一下 PyGTK 开发环境,下面是一个简单 Hello PyGTK 的例子

$ cat hellopygtk.py

from gtk import * 
 
window = GtkWindow(WINDOW_TOPLEVEL) # 创建一个顶层窗口 
window.set_title("Hello, world!") 
window.connect("destroy", mainquit) # 将注销事件与mainquit处理连接 
 
window.show() # 显示主窗口 
mainloop() # 进入事件循环 

$ python hellopygtk.py

附录

作为跨平台的图形用户界面开发工具,GTK+ 程序在非 Unix/Linux 平台上并非原生样式。若有这种需要,wxWidgets 便是一个更好的选择,它是由 C++ 开发的跨平台的图形用户界面开发包。另外,基于 wxWidgets 工具包,还开发了 C/C++ 集成开发环境 Code::Blocks,它是跨平台的集成开发环境。该集成开发环境还为 wxWidgets 图形界面开发提供了快速开发插件 wxSmith。在 Linux 平台上,为了让图形用户界面显示 GTK+ 样式,wxWidgets 封装了 GTK+ 包,因此 wxWidgets 在 Linux 平台上也被命名为 wxGTK。好了,要在 Linux 平台上开发 wxWidgets 程序,安装所有的工具包了。在 Red Hat 系中执行

$ sudo dnf install wxGTK-devel wxGTK-docs codeblocks codeblocks-contrib

Debian 系可执行

$ sudo apt-get install libwxgtk3.0-dev wx3.0-doc wx3.0-headers wx3.0-i18n wx3.0-examples codeblocks codeblocks-contrib

Gentoo 中执行

$ sudo sh -c 'echo "dev-util/codeblocks doc contrib " >> /etc/portage/package.use'
$ sudo emerge -av wxGTK codeblocks

在 Gentoo 上使用 wxWidgets,还需要用 eselect 的选择合适的版本

$ sudo eselect list wxWidgets
$ sudo eselect wxWidgets set 1

在 Linux 使用 wxWidgets 的方法非常简单,它提供了 wx-config 工具帮助 GCC 查找头文件与库文件。例如

$ wx-config --cppflags
$ wx-config --libs

再来看一个编译 wxWidgets 程序的实例

$ g++ hellowx.cpp -o hellowx `wx-config --cppflags --libs`
$ ./hellowx

与 GTK+ 类似,wxWidgets 也有其他语言的绑定,这里介绍一下 wxWidgets 与 Python 的绑定,也即 wxPython 开发包,对应的界面快速设计工具是 wxGlade。它们的安装非常简单,Red Hat 系中执行

$ sudo dnf install wxpython wxpython-devel wxpython-docs wxGlade

Debian 系可执行

$ sudo apt-get install python-wxgtk3.0 python-wxtools python-wxversion python-wxglade

Gentoo 中执行

$ sudo emerge -av wxPython wxglade

测试一下 wxPython 开发环境,下面是一个简单 Hello wxPython 的例子

$ cat hellowxpy.py

import sys, os
from wxPython.wx import *
 
class main_window(wxFrame):
      def __init__(self, parent, id, title):
      	  wxFrame.__init__(self, parent, -1, title, size = (200, 100),style=wxDEFAULT_FRAME_STYLE|wxNO_FULL_REPAINT_ON_RESIZE)
      	  self.control = wxTextCtrl(self, -1, style=wxTE_MULTILINE)
      	  self.Show(true)
 
class App(wxApp):
      def OnInit(self):
      	  frame = main_window(None, -1, "wxPython: (A Demonstration)")
      	  self.SetTopWindow(frame)
      	  return true
 
app = App()
app.MainLoop()

$ python hellowxpy.py
Nov 9

在开源领域 Linux 风声水起,缘于一众富有想象力的开源工具。在 Linux 服务器的自动化管理方面,也有众多选择,例如 Puppet、Chef、SaltStack、Ansible。前三个管理工具采用的是服务端/客户端模式,换而言之,既要配置客户端又要配置服务端才能让它们工作。Ansible 与它们不同,它充分利用 Linux 服务器的现有设施。使用 Ansible 无需安装服务端和客户端,只要配置好 OpenSSH 服务即可。Ansible 的配置文件采用的是易读格式,例如,Ansible 的主机文件使用 INI 格式,支持分组,能够指定模式;而 Playbook 则是 YAML 格式。这比 Puppet 等工具使用独有配置文件格式要容易读写。当前使用 Ansible 较为知名的用户包括 Fedora、Rackspace、Evernote 等等。

1、安装 Ansible

Ansible 能够安装到 Linux、BSD、Mac OS X 等平台,Python 版本最低要求为 2.6。大多数 Linux 发行版可以通过包管理器安装 Ansible。例如,对于 Red Hat 系来说,需要配置 EPEL 源,然后执行

$ sudo yum install ansible

Debian 系只需

$ sudo apt-get install ansible

Gentoo 可执行

$ sudo emerge -avt ansible

如果所用系统的软件包仓库中找不到 Ansible 或者系统本身没有软件仓库,那么也可以通过 pip 来安装 Ansible

$ pip install --user ansible

上述命令行执行同时也会安装 paramiko、PyYAML、jinja2 等 Python 依赖库。 现在来看看 Ansible 是否能正常工作:

$ ansible 127.0.0.1 -m ping -u james -k

请注意此地连接的是本地主机,为保证 ansible 正常工作,需在本地配置 OpenSSH 服务并以允许密码认证方式登录,同时还需创建 james 账户。

2、Ansible 的主机文件

Ansible 的主机文件用来定义你要管理的主机,系统级主机文件的默认位置在 /etc/ansible/hosts。当然也可以设置 Ansible 的用户级主机文件,它可覆盖系统级主机文件,具体来说就是在用户家目录创建 Ansible 配置文件并在其中指定用户级主机文件的路径,例如

$ nano -w ~/.ansible.cfg
[defaults]
# some basic default values...
# ansible 1.8 or ago
#hostfile     = $HOME/.ansible/hosts
# ansible 1.9 or later
inventory      = $HOME/.ansible/hosts
$ mkdir -p ~/.ansible
$ touch ~/.ansible/hosts

最后,还可通过 ansible 命令行选项 -i 指定主机文件的路径,它能覆盖前面两者的配置。主机文件是一些被管理的机器的 IP 或域名组成的。这些主机可以分组存放,组名可以使用 [] 指定,未分组的机器需保留在主机文件的顶部,主机文件中可以指定远程主机的 SSH 端口。例如:

202.121.111.8
[vps]
202.121.111.10
[web]
202.121.111.18
172.16.10.124:5555
[db]
202.121.111.19

同时,分组也能嵌套:

[vps:child]
web
db

此外,也可以通过数字和字母模式来指定一系列连续主机,如:

202.121.111.[1:3] # 相当于 202.121.111.1、202.121.111.2、202.121.111.3
[a:c].exmaple.org # 相当于 a.example.org b.example.org、c.example.org

3、Ansible 的 Ad-Hoc 模式

Ansible 的强大很大程度上体现在它 Playbook 上,后者基本上就是一些写好的 "Ansible 脚本"。不过在正式介绍它之前,先从简单的 ansible 命令行开始,这就是 ansible 的 Ad-Hoc 模式。Ad-Hoc 模式与在 Bash Shell 中执行单行命令差不多,用来快速完成简单任务。先来看看 ansible 的命令行选项:

  • -i,--inventory-file=:指定主机文件,不指定的话默认依顺序使用用户级、系统级的主机文件
  • all:针对主机文件中定义的所有主机执行,也可以指定组名或模式
  • -m,--module-name=:指定所用的模块,不指定的话默认使用 command 模块
  • -a:指定所用模块的参数,不指定的话所用模块不使用参数
  • -u,--user=:指定远端机器的用户,若不指定,默认是执行 ansible 的本地用户账户名
  • -c,--connection=:指定远端主机用户登录时的认证模块,通常有 smart、paramiko、sshpass,默认是 smart
  • -f,--forks=:参数让 ansible 在主机上并行运行指令
  • -k,--ask-pass:询问 SSH 密码;若使用 SSH 密匙,请不要使用 -k

通常,执行 Ansible 需要 SSH 密码认证,提示 SSH 密码:

$ ansible all -m ping -u root --ask-pass

在一些机器上使用 --ask-pass 会需要指定连接方式,例如:

$ ansible all -m ping -u root --ask-pass -c paramiko

当然也可以使用模块 sshpass,然而 sshpass 并不总是在标准的仓库中提供,因此 paramiko 可能更为简单。需要注意,操作很多主机时需逐个输入账户名与密码,这些都是相当麻烦的。为简单起见,可做下述配置

$ sudo nano -w /etc/ansible/hosts
#定义被控主机
[webservers]
172.16.10.123 ansible_ssh_user=root ansible_ssh_pass=centos
172.16.10.125 ansible_ssh_port=5555 ansible_ssh_user=root ansible_ssh_pass=centos
web1 ansible_ssh_host=172.16.10.126 ansible_ssh_port=5555 ansible_ssh_user=root ansible_ssh_pass=centos
[dbservers]
172.16.10.125 ansible_ssh_user=root ansible_ssh_pass=centos

下面再看看远端主机的 uptime:

$ ansible webservers -a 'uptime'

此处省略了 -i,ansible 使用默认的主机文件;webservers 代表默认主机文件中的某个主机组;也省略了 -m,ansible 默认使用 command 模块;-a 指定模块的参数,即执行 uptime 命令。如果被管理端主机的 Python 为 2.4,那么需要 python-simplejson 包,可以通过以下命令在所有 CentOS 主机上安装它:

$ ansible webservers -m raw -a 'yum -y install python-simplejson'

此处使用 Ansible 的 raw 模块,它的作用相当于直接在 OpenSSH 中执行 -a 后面的命令。再如利用 Ansible 可执行系统更新

$ ansible webservers -m raw -a 'yum update -y'

或者安装某些服务

$ ansible webservers -m raw -a 'yum -y install httpd'
$ ansible webservers -m raw -a "service httpd start"
$ ansible webservers -m raw -a "chkconfig httpd on"

实际上,将 OpenSSH 密码放入主机文件是非常不安全的,强烈推荐为 Ansible 设置 SSH 公钥认证。除了密码安全问题之外,对于非超级用户登录远程主机并执行超级用户命令,需要开启 su 或 sudo 权限。例如,vps 组中的账户 james 配置了 sudo 权限,可用该账户升级系统,例如

$ ansible vps -m raw -a "yum upgrade -y" -u james -k -b --become-user=root --become-method=sudo

或者

$ ansible vps -m raw -a "sudo yum upgrade -y" -u james -k

4、Ansible 的帮助文档

Ansible 的强大功能均是由它众多模块提供的,诸如为 Linux 创建用户及组、安装软件包、分发配置文件、管理服务等等。要想了解 Ansible 模块的全部功能,必要的文档是不可少的。在命令行下,可通过 ansible-doc 查询模块文档,如:

$ ansible-doc raw

列出模块 raw 中相关指令

$ ansible-doc -s raw

要查看所有安装模块

$ ansible-doc -l

若 Ansible 提供了模块功能,尽量使用模块,例如 Red Hat 系有 yum 模块,因此前述 CentOS 上的操作可替换为

$ ansible webservers -m yum -a 'name=* state=latest'
$ ansible webservers -m yum -a 'name=httpd state=latest'
$ ansible webservers -m service -a 'name=httpd state=started'
$ ansible webservers -m service -a 'name=httpd enabled=yes'

5、设置 SSH 公钥认证

现在让我们创建和配置 SSH 公钥认证,以便省去 -c 和 --ask-pass 选项。首先,生成密匙对:

$ ssh-keygen -t rsa

上述命令会在当前用户的 ~/.ssh 中生成密匙对,假设当前用户为 james。显然有很多种方式把 SSH 公匙放到远程主机上,既然要介绍 ansible 的用法,就用它来完成这个操作吧:

$ ansible all -m copy -a "src=/home/james/.ssh/id_rsa.pub dest=/tmp/id_rsa.pub" --ask-pass -c paramiko

下一步,把公钥文件添加到远程服务器里。输入:

$ ansible all -m shell -a "cat /tmp/id_rsa.pub >> /root/.ssh/authorized_keys" --ask-pass -c paramiko

由于把密匙放入超级用户目录,但又没有指定 -u 选项,因此默认登录远程主机的账户是执行 ansible 的本地账户,通常会因权限不够执行会失败。不要急,需要用 root 来执行这个命令,加上 -u 参数即可:

$ ansible all -m shell -a "cat /tmp/id_rsa.pub >> /root/.ssh/authorized_keys" --ask-pass -c paramiko -u root

刚才演示了通过 ansible 来传输文件的操作。事实上 ansible 有一个更加方便的内置 SSH 密钥管理支持:

$ ansible all -m authorized_key -a "user=james key='{{ lookup('file', '/home/james/.ssh/id_rsa.pub') }}' path=/home/james/.ssh/authorized_keys manage_dir=no" --ask-pass -c paramiko

至此,公钥已经设置好了。试着随便跑一个命令,比如 hostname,希望不会被提示要输入密码

$ ansible all -m shell -a "hostname" -u root

现在可以用 root 来执行命令,并且不会被输入密码的提示干扰了。最后,把 /tmp 中的公钥文件删除:

$ ansible all -m file -a "dest=/tmp/id_rsa.pub state=absent" -u root

下面来做一些更复杂的事情,而且无须输入密码。例如:

$ ansible all -m yum -a "name=httpd state=latest" -u root

利用 Ansible 的 yum 模块安装好了最新版的 Apache 服务器。实际上,上一条命令可以用下述 Playbook 替代:

---
- name: test.yaml                  # Playbook 的名称
  hosts: webservers                # Playbook 所作用的主机列表
  remote_user: root                # 远程主机的用户名
  tasks:                           # 任务列表
  - yum: name=httpd state=latest   # 使用 yum 模块执行任务一

现在有一个简单的 Playbook 了,可以这样运行它:

$ ansible-playbook test.yaml -f 10

既然 Playbook 的操作如此简单,不妨把导入 SSH 公钥的操作从 ansible 命令行转移到 Playbook 中,这将在设置新主机的时候提供很大的方便,甚至让新主机直接可以运行一个 Playbook。为了演示,把之前的公钥例子放进一个 Playbook 里:

---
- hosts: webservers               # Playbook 所作用的主机列表
  remote_user: james              # 执行任务的远程主机用户名
  sudo: yes                       # 启用 sudo 权限执行任务
  tasks:
  - authorized_key:
        user=root
        key="{{ lookup('file', '/home/james/.ssh/id_rsa.pub') }}"
        path=/root/.ssh/authorized_keys
        manage_dir=no

在准备开始写更多更复杂的 Playbook 之前,另一个值得考虑的事情是,引入版本管理器可以有效节省时间。虽然需要管理的 Linux 的服务器需求会不断变化,然而并不需要在每次机器发生变化时都重新写一个 Playbook,只需要更新相关的部分并提交这些修改。这里强烈推荐版本管理器 Git 来管理各类 Playbook 脚本。

6、使用 Playbook 管理复杂任务

对于需反复执行的、较为复杂的任务,我们可以通过定义 Playbook 来搞定。Playbook 是 Ansible 真正强大的地方,它允许使用变量、条件、迭代以及模板,也能通过角色及包含指令来重用既有内容。我们来看一个简单的例子,该例子在远端机器上创建一个新的用户:

$ nano -w user.yaml
---
- name: create user                    # Playbook 的名称
  hosts: vps                           # 执行任务的主机表
  user: root                           # 执行任务的远程主机用户
  gather_facts: false                  # 不获取远程主机的信息

  vars:                                # 自定义变量列表
  - user1: "toy"                       # 第一个自定义变量

  tasks:                               # 任务列表
  - name: create {{ user1 }} on vps    # 第一个任务的名称
    user: name="{{ user1 }}"           # 任务格式:"action: module options" 或 "module: options"
  - name: template configuration file  # 第二个任务的名称
    template: src=template.j2 dest=/etc/foo.conf
    notify:                            # 可用于在每个 play 的最后被触发,避免多次有改变发生时每次都执行指定的操作,取而代之,仅在所有的变化发生完成后一次性地执行指定操作。
    - restart memcached                # 调用 handler 中定义的操作名
    - restart apache

  handlers:                            # 用于当关注的资源发生变化时采取一定的操作。handler 是 task 列表,这些 task 与前述的 task 并没有本质上的不同。
  - name: restart memcached            # 第一个 handler 名
    service:  name=memcached state=restarted
  - name: restart apache               # 第二个 handler 名
    service: name=httpd state=restarted

首先,我们给 Playbook 指定了一个名称;接着,通过 hosts 让该 Playbook 仅作用于 vps 组;user 指定以 root 帐号执行,Ansible 也支持普通用户以 sudo 方式执行任务;gather_facts 的作用是搜集远端机器的相关信息,稍后可通过变量形式在 Playbook 中使用;vars 用于定义变量,也可单独放在文件中;tasks 指定要执行的任务,其中 notify 用来监视资源变化,据此调用相关的 handlers;handlers 类似于 tasks,但它仅根据 notify 信号来决定是否执行相应的 handler。要执行 Playbook,可以敲入:

$ ansible-playbook user.yaml

6.1、Ansible 中的变量

正如前述例子中所看到的,Ansible 的 Playbook 中是可以定义变量的。Ansible 的变量名仅能由字母、数字和下划线组成,且只能以字母开头。Ansible 本身也保存了一些变量,它们称之为 facts。facts 是由正在通信的远程目标主机发回的信息,这些信息被保存在 Ansible 变量中。要获取指定的远程主机所支持的所有 facts,可使用如下命令进行:

$ ansible vps -m setup

通过上述命令,Ansible 会存储很多远程主机相关的变量供 Playbook 使用,例如 ansible_os_family 等。 Ansible 可以通过角色传递变量。当给一个远程主机应用角色的时候可以传递变量,然后在角色内使用这些变量。例如:

---
- hosts: webservers
  roles:     # 角色列表
  - common   # 第一个角色
  - { role: wordpress, dir: '/var/www/htdocs/blog',  port: 8080 } # 第二个角色

Ansible 通过 register 把任务的输出定义为变量,然后用于其他任务,示例如下:

---
- tasks:
  - shell: /usr/bin/foo
    register: foo_result  # 将该任务的输出作为 foo_result 变量,
    ignore_errors: True

除此之外,Ansible 可以通过命令行传递变量。在运行 Playbook 的时候,通过命令行可以传递一些变量供 Playbook 使用。例如:

$ ansible-playbook test.yml --extra-vars "hosts=www user=mageedu"

上述命令行传入了 hosts 与 user 变量可覆盖 Playbook 中的相关定义。

6.2、Ansible 中的条件

如果需要根据变量、facts 或此前任务的执行结果来作为某 task 执行与否的前提时要用到条件测试。只需在 tasks 后添加 when 子句即可使用条件测试;when 语句支持 Jinja2 表达式语法。when 语句中还可以使用 Jinja2 的大多 filter,例如要忽略此前某语句的错误并基于其结果(failed 或者 success)运行后面指定的语句,可使用类似如下形式:

---
- tasks:
  - command: /bin/false
    register: result     # 注册器
    ignore_errors: True  # 忽略错误信息
  - command: /bin/something
    when: result|failed  # 第一条命令失败时(result 为 failed 时),才执行第二条命令
  - command: /bin/something_else
    when: result|success #(result 为 success 时),才执行
  - command: /bin/still/something_else
    when: result|skipped # skipped:已经执行过跳过执行

此外,when 语句中还可以使用 facts 或 Playbook 中定义的变量。例如:

---
- tasks:
  - name: "shutdown Red Hat flavored systems"
    command: /sbin/shutdown -h now
    when: ansible_os_family == "RedHat"  #条件测试,ansible_os_family 来自 facts

6.3、Ansible 中的迭代

在迭代中只能使用 item 变量,变量引用为{{ }}两个大括号,变量两边有空格。当有需要重复性执行的任务时,可以使用迭代机制。其使用格式为将需要迭代的内容定义为 item 变量引用,并通过 with_items 语句来指明迭代的元素列表即可。例如:

---
- name: add several users
  user: name={{ item }} state=present groups=wheel stae=present  # 用户得存在且加入 wheel 组中
  with_items:
  - testuser1  # 分别使用 testuser1 替换 name={{item}} 中的 item 项
  - testuser2

上面语句的功能等同于下面的语句:

---
- name: add user testuser1
  user: name=testuser1 state=present groups=wheel
- name: add user testuser2
  user: name=testuser2 state=present groups=wheel

事实上,with_items 中可以使用元素还可为 hashes,例如:

---
- name: add several users
  user: name={{ item.name }} state=present groups={{ item.groups }}
  with_items:
  - { name: 'testuser1', groups: 'wheel' }
  - { name: 'testuser2', groups: 'root' }

迭代还支持列表,使用 with_flattened 语句。例如

---
- vars:                                
  - packages_LNMP:   # 定义列表
    - [ 'nginx', 'mysql-server', 'php-fpm' ]

- tasks:
  - name: Install LNMP
    yum: name={{ item }} state=present
    with_flattened:
    - packages_LNMP  # 再在迭代中引用它

6.4、Ansible 中的角色

Playbook 还可以构造非常重要的文档目录组织结构,也即被称为角色,一个带有角色的 Playbook 的结构如下

$ tree /root/lamp
/root/lamp/
    ├── hosts
    ├── group_vars
    │      └── all
    ├── roles
    │      ├── Apache
    │      │      ├── defaults
    │      │      │      └── main.yml
    │      │      ├── files
    │      │      │      └── httpd.conf
    │      │      ├── handlers
    │      │      │      └── main.yml
    │      │      ├── meta
    │      │      │      └── main.yml
    │      │      ├── tasks
    │      │      │      ├── delete_httpd.yml
    │      │      │      └── main.yml
    │      │      ├── templates
    │      │      │      └── file.j2
    │      │      └── vars
    │      │             └── main.yml
    │      ├── MariaDB
    │      │      ├── tasks
    │      │      │      └── main.yml
    │      │      ... 
    │      └── PHP
    │             ├── tasks
    │             │      └── main.yml
    │             ... 
    └── site.yml

以上显示了名为 lamp 的 Playbook 的目录结构。其中 site.yml 是该 Playbook 的主文件,,它里面的可能写法是

---
- name: install LAMP
  hosts: all
  user: root
  roles:
  - Apache     # 调用 roles/Apache/tasks/main.yml
  - MariaDB    # 调用 roles/MariaDB/tasks/main.yml
  - PHP        # 调用 roles/PHP/tasks/main.yml

hosts 是该 Playbook 的主机文件,它只在该 Playbook 中有效;roles 目录中有三个角色 Apache、MariaDB 与 PHP。下面再来看看角色 Apache 中各个子目录或文件的作用:

  • defaults:当前 role 中的各种变量,主要存放在 defaults/main.yml 中,也可以通过 include 包含更多。它具有最低级别,可被 vars 等处的变量覆盖;
  • files:存放各种文件,Ansible 默认会到这里目录去找文件,对应 task 里面的 copy 或 script 等模块;
  • tasks:tasks:任务列表主要存放在 tasks/main.yml 中,也可以通过 include 包含更多的任务列表。可能的写法如下
    ---
    - name: install httpd
      yum: name=httpd  state=present
    - name: configuration Apache
      copy: src=httpd.conf dest=/etc/httpd/httpd.conf
    - name: configuration iptables
      template: src=file.js2 dest=/etc/sysconfig/iptables
      notify:                     # 会调用 roles/Apache/handlers/main.yml
      - restart httpd           # 对应 name 为 restart httpd 和 restart iptables 的相应命令并执行,
      - restart iptables        # 若之前 Apache 服务已安装,再次执行,不会触发 notify
    #- include: delete_httpd.yml
    
  • handlers:处理列表主要存放在 handlers/main.yml 中,也可以通过 include 包含更多的处理列表。可能的写法如下
    ---
    - name: restart httpd
      service: name=httpd  state=restarted
    - name: restart iptables
      service: name=iptables  state=restarted
  • templates:存放模板,对应 tasks 里面的模块 template,会自动在此目录中寻找 Jinja2 模板文件;
  • vars:定义的变量主要存放在 vars/main.yml 中,也可以通过 include 包含更多。它只对当前 role 有作用;
  • meta:元信息主要存放在 meta/main.yml 中,也可以通过 include 包含更多。它的作用是定义 role 和 role 直接的依赖关系,标准格式如下
    ---
    dependencies:
    - { role: Apache, port: 80 }
    - { role: mariadb, dbname: blarg, other_parameter: 12 }

group_vars 目录中存放供各个角色共同使用的 Ansible 全局变量,一般存放在 group_vars/all 文件中,也可以通过 include 包含更多。

运行带角色的 Playbook 方法与前面类似,只需要调用该 Playbook 的主文件。例如:

$ ansible-playbook site.yml

6.5、Ansible 中的 include

通过一个具体的 Playbook 的例子来说明 include 的用法

$ tree /root/sample
/root/sample/
    ├── hosts
    ├── roles
    │      └── install_client
    │             └── tasks
    │                    ├── db.yml
    │                    ├── app.yml
    │                    └── main.yml
    └── site.yml
$ nano -w ~/sample/hosts
[db]
192.168.24.10
[app]
192.168.24.11
$ nano -w ~/sample/site.yml
---
- hosts: '{{ myhosts }}'
  user: james
  sudo: yes
  sudo_user: root
  roles:
    - install_client
$ nano -w ~/sample/roles/install_client/tasks/main.yml       # 本次测试的关键地方
---
- include: db.yml
  when: "myhosts == 'db'"
- include: app.yml
  when: "myhosts == 'app'"
$ nano -w ~/sample/roles/install_client/tasks/db.yml
---
- name: Touch db file
  shell: touch /tmp/db.txt
$ nano -w ~/sample/roles/install_client/tasks/app.yml
---
- name: Touch app file
  shell: touch /tmp/app.txt

6.6、Ansible 中的标签

当 Playbook 中有很多配置工作时,若中间的某个环节出错了,修改后重新执行的话,会发现有一大堆无关步骤可能隐藏错误。虽然 Ansible 提供了 retry 文件,但它却只是根据主机来判断是否重新执行,仍然不够方便;又或者,中间的某些步骤特别耗时,比如下载一个很大的数据包,每次执行特别浪费时间。注释掉 Playbook 中不需要的部分是绕过它中间环节的办法之一,但不是最最好的办法。实际上,Playbook 中有一个名为 tags 的关键字,它可以有效的解决 Playbook 的调试问题。tags 可以和一个 play(就是很多个 task)或者一个 task 进行捆绑;而 ansible-playbook 提供了 "--skip-tags" 和 "--tags" 来指明是跳过特定的 tags 还是执行特定的 tags。请看例子:

---
- name: test1.yml
  hosts: test-agent
  tasks:
  - command: echo "test1"
    tags:
    - test1
  - command: echo "test2"
    tags:
    - test2
  - command: echo "test3"
    tags:
    - test3

执行

$ ansible-playbook test1.yml --tags="test1,test3"

则只会执行 test1 和 test3 的 echo 命令。执行

$ ansible-playbook test1.yml --skip-tags="test2"

同样只会执行 test1 和 test3 的 echo 命令。

Jul 13

WebDAV(Web-based Distributed Authoring and Versioning)是基于 HTTP 1.1 的一个通信协议。它为 HTTP 1.1 添加了一些扩展(就是在 GET、POST、HEAD 等几个 HTTP 标准方法以外添加了一些新的方法),使得应用程序可以直接将文件写到 Web 服务器上,并且在写文件时候可以对文件加锁,写完后对文件解锁,还可以支持对文件所做的版本控制。现在主流的 Web 服务器一般都支持 WebDAV,如需了解更多有关 webDAV 的一些开源或商业项目,可访问 http://webdav.org/。

下面就来介绍一下在 Fedora 的 Apache 服务器中如何配置 WebDAV。首先,当然是安装 Apache 服务器

$ sudo yum install httpd

查看一下 Apache 服务器是否包含了 WebDAV 模块

$ cat /etc/httpd/conf.modules.d/00-dav.conf
LoadModule dav_module modules/mod_dav.so
LoadModule dav_fs_module modules/mod_dav_fs.so
LoadModule dav_lock_module modules/mod_dav_lock.so

确认完了 WebDAV 模块之后,该建立 WebDAV 目录了,这很简单:

$ sudo mkdir -p /var/www/webdav

安全起见,还需为 WebDAV 创建一些用户(当然不创建用户也行,不过记得修改后面的配置文件),可执行以下命令产生密码验证文件

$ sudo htpasswd -cm /var/www/webdav/.htpassword james

提示输入密码时输入该用户的密码;若还需要增加用户 tom,可执行以下命令

$ sudo htpasswd -m /var/www/webdav/.htpassword tom

删除某个用户,如 tom,可以使用下列指令:

$ sudo htpasswd -D /var/www/webdav/.htpassword tom

最后,还需在 Apache 的配置目录 /etc/httpd/conf.d/ 中新建一个名为 webdev.conf 的 WebDAV 配置文件,并输入以下内容

$ sudo nano -w /etc/httpd/conf.d/webdev.conf
<IfModule mod_dav_fs.c>
    DAVLockDB /var/lib/dav/lockdb
</ifmodule>

Alias /webdav "/var/www/webdav/"

<Directory "/var/www/webdav/">
   Options Indexes MultiViews
   IndexOptions FancyIndexing
   AddDefaultCharset UTF-8
   AllowOverride None
   <IfModule mod_authz_core.c>
       # Apache 2.4
       Require all granted
   </IfModule>
   <IfModule !mod_authz_core.c>
       # Apache 2.2
       Order allow,deny
       allow from all
   </IfModule>

   Dav On # 开启 WebDAV
   AuthType Basic
   AuthName "WebDAV Server"
   AuthUserFile /var/www/webdav/.htpassword #密码验证文件
   Require valid-user
   <Limitexcept Get Options>
      Require user james
   </Limitexcept>
</Directory>

一切就绪了之后,重启 Apache 服务器

$ sudo service httpd restart

该测试一下 WebDAV 服务了,这只需在浏览器的地址栏中输入 http://localhost/webdav 来访问共享资源了;也可通过 MS Windows 的网上邻居访问。当然,GNU/Linux 也可使用命令行 cadaver 进入访问

$ sudo yum install cadaver
$ cadaver http://127.0.0.1/webdav/
Jan 22

通常,一个大型工程总会被分拆为一些子工程。这既有利于工程开发难度的降低,也有利于使用现成的方案或者第三方方案作为子工程。git-submodule 就是完成这样一种子工程拆分与整合的工具。下面开始简单介绍一下 git-submodule 的用法。

A、带 submodule 的 git 仓库的创建
正如 git 仓库有两种创建方式一样,带 submodule 的 git 仓库的创建也有两种。

1、新建一个大型工程

$ cd /path/to/massproj
$ echo Example of Mass Project > README.md
$ git init
$ git add README.md
$ git commit -m 'Mass Project created.'

接着使用 git submodule add 为新建的工程增加一个第三方子工程,例如

$ git submodule add git@domain.com:subproj.git subproj
$ git status

可能会注意到,git 只记录了 submodule 目录,而没有记录目录下的文件。实际上,git 是按照 commit id 来比对 submodule 变动的。直接递交本次更改

$ git commit -m 'thirdpart submodule added.'

若有管理该项目的 Git 服务器,可上传该项目至服务器,例如

$ git remote add origin git@domain.com:massproj.git
$ git push origin master

2、克隆一个带 submodule 的 git 仓库

$ cd /path/to/hold/projcet/
$ git clone git@domain.com:massproj.git

不过不同于常规的 git 仓库克隆,还需要将该仓库中的子模块初始化以及更新:

$ git submodule init
$ git submoudle update

如果觉得这样克隆一个 git 仓库太麻烦,那么试试

$ git clone --rescursive git@domain.com:massproj.git

该命令行会自动完成一系列必需的动作。

B、带 submodule 的 git 仓库更新
git 仓库正常更新过程是这样的:

$ git pull

然后根据修改情况,自动解决冲突、递交变更并合并到 git 仓库。不过对于带 submodule 的 git 仓库的更新,就有两种情况了。

1、更新主 git 仓库,如前,先执行

$ git pull

就要留意 submodule 的变更,先试试

$ git status

若发现 submodule 有修改,需立即执行

$ git submodule update

更复杂一些的情况是,如果有一个 submodule 依赖另一个 submodule,那么很可能需要在 git pull 和 git submodule update 之后,再分别到每个有依赖关系的 submodule 目录中再执行一次 git submodule update。为了免去麻烦,可以执行

$ git submodule foreach "git submodule update"

来实现一次性更新所有 submodule。

2、更新某个子模块,需先进入子模块目录,然后执行普通的更新操作,例如

$ cd subproj
$ git pull

若有很多子模块需要做类似的操作,可简单执行

$ git submodule foreach "git pull"

若子模块之间还有依赖关系,可采用

$ git submodule foreach "git pull" --rescursive

来一次性更新所有模块。

不过需要注意的是,这样的更新操作都是在本地 git 仓库没有做任何修改的前提之下完成的。假如本地仓库有所改动,特别是本地仓库中的子模块有所修改,那么有可能还需要解决一系列冲突才能合并。这需要用到下面这段的知识。

C、带 submodule 的 git 仓库的修改
本地仓库的普通修改不是这里的主要内容,这里要讨论一下本地仓库中子模块的更改。比如我们拥有某个 submodule 的远程仓库操作权限,此时正好碰到该子工程被大型工程调用时需要修改代码。切回到的该子工程在本地的原先仓库位置进行相关操作当然略显麻烦,若能在该大型工程的子模块仓库中直接修改无疑更方便一些。这里就来介绍一下子模块修改的大概流程。

1、有一个重要的事实需要特别强调,在执行 git submodule update 时 git 默认并不会将 submodule 切到任何 branch。因此,submodule 的 HEAD 默认是处于游离状态的(‘detached HEAD’ state)。在子模块修改前,记得一定要将当前的 submodule 分支切换到相应分支,例如切换到 master 分支:

$ cd /path/to/massproj/subproj
$ git checkout master

然后才能对子模块做修改和提交。另外,子模块中所跟踪的远程分支可被 .gitmodules 或者 .git/config 的配置覆盖,例如

$ nano -w .gitmodules
[submodule "subproj"]
   path = subproj
   url = git@github.com:subproj.git
   branch = master

实际上,.gitmodules 的修改可通过在增加 submodule 时直接指定跟踪分支自动完成,例如

$ git submodule add git@github.com:subproj.git -b master

若有很多 submodule 需要类似分支切换操作,可使用如下命令

$ git submodule foreach "git checkout master"

实际上,高版本的 git 解决这一问题的方式更简单,例如执行

$ git submodule update --remote

来跟踪远程分支,默认是 origin/master;若子模块之间还有依赖关系,可采用

$ git submodule update --remote --rescursive

一次性跟踪所有子模块的远程分支。

2、下面开始对子模块目录做一些修改,例如修改 README.md 文件:

$ cd /path/to/massproj/subproj
$ echo this is a new line. >> README.md

再将修改递交到 git 仓库中:

$ git add README.md
$ git commit -m "new comments added."
$ git push

递交完之后,回到大型工程的顶层目录,该示例中就是

$ cd ..

再试试

$ git status

会看到子模块 subproj 还需要再做一次递交

$ git add subproj
$ git commit -m 'submodule updated'
$ git push

由于 submodule 的更新只记录 commit id,因此,子模块的修改必须按上述流程递交版本。也即先在 submodule 內做递交,之后回到顶层目录再作一次递交,不然会引起 git 仓库的版本错乱。

3、如果你不慎在修改子模块之前忘记将它切换到远程跟踪分支,且又做了提交,此时可以用 cherry-pick 命令挽救。具体做法如下:先到该子模块将 HEAD 从游离状态切换到远程跟踪分支,例如 master 分支:

$ cd subproj
$ git checkout master

这时候,git 会报 Warning 说有一个提交没有在 branch 上,记住这个提交的 change-id(假如 change-id 为 aaaa)。随后将刚刚的提交重新作用远程跟踪分支上:

$ git cherry-pick aaaa

最后只需将更新提交到远程版本库中:

$ git push

D、git 仓库中 submodule 的更名或移动
随着大型工程的进行,有可能需要变更子模块,例如移动子模块的位置,或者第三方方案本身需要更名等。由于 submodule 的更名与移动两者差别不大,这里主要介绍子模块移动目录的流程。至于更名,只略微提及。假设要将 subproj 移动到 /massproj/platform 目录之下,先新建目录并移动子模块到里面

$ cd /path/to/massproj
$ mkdir platform
$ mv subproj platform/

修改相关的配置文件,主要是 .gitmodules、.git/config、子模块目录中的 .git 以及 .git/modules 目录中几个文件与目录:

$ sed -i 's@subproj@platoform/subproj@' .gitmodules
$ sed -i 's@subproj@platoform/subproj@' platform/subproj/.git
$ mv .git/modules/subproj .git/modules/platform/subproj
$ sed -i 's@subproj@platoform/subproj@' .git/modules/platform/subproj/config

若是子模块更名,那么还请留意 .gitmodules、.git/config 以及 git/modules/<path/to/submodule>/config 中 url 设置,根据需要进行调整。最后让 git 记录所有的变更

$ git rm --cached subproj
$ git add platform/subproj .gitmodules
$ git submodule sync -- platform/subproj

若觉得上述手续太烦且网络速度还可以的话,可直接删除原先的子模块,随后指定新路径添加模块。有关这个方法,需先了解一下后一段知识,再回过头来看一下以下操作

$ cd /path/to/massproj
$ git rm --cached subproj
$ rm -rf subproj
$ rm -rf .git/modules/subproj
$ nano -w .gitmodules
...remove subproj...
$ nano -w .git/config
...remove subproj...
$ git submodule add git@github.com:subproj.git platform/subproj -b master
$ git add .gitmodules
$ git commit -m 'subproj moved to platform/.'
$ git push

E、带 submodule 的 git 仓库中 submodule 的删除
这里以 platform/subproj 子模块为例介绍一下子模块删除的大概流程。先到大型工程的顶层目录清理子模块文件:

$ cd /path/to/massproj
$ git rm --cached platform/subproj
$ rm -rf platform/subproj
$ rm -rf .git/modules/platform/subproj

接着删除 git 中记录的相关数据

$ nano -w .gitmodules
...remove platform/subproj...
$ nano -w .git/config
...remove platform/subproj...

随后将变更递交到远程仓库

$ git add .gitmodule
$ git commit -m 'platform/subproj submodule removed.'
$ git push

Sep 21
A、简单说明
CentOS 是一款基于 Red Hat Enterprise Linux 提供的可自由使用源代码的企业级 Linux 发行版本。作为服务器的工作环境,相对于其他 Linux 发行版,它的稳定性是值得信赖的。CentOS 的安装方式也非常的多样,例如用 CentOS 光盘镜像开启图形或文本界面安装程序;再如以 kickstart 进行网络安装。这里主要介绍以 chroot 方式实现 CentOS 的自举,主要利用的工具是 Red Hat 的包管理工具 rpm 以及 yum。它们分别提供了与 chroot 配合的 --root 与 --installroot 选项。这种安装方式不仅适用于双 Linux 系统的安装,也适用于利用 Linux Live CD 环境安装 Fedora/CentOS。
 
B、准备 Linux 宿主环境
想要通过自举的方式安装 CentOS,就需要提供一个 Linux 宿主环境。一般而言,这个宿主环境可以是 Red Hat 系的操作系统,也可以是非 Red Hat 系的操作系统。宿主环境可以是硬盘中安装的 Linux,也可以是 Live CD 中的 Linux。尽管如此,Linux 宿主环境还是必须提供 Red Hat 的包管理工具。如果宿主环境是 Red Hat 系,那么显然已经满足了这一要求,不过最好保证 yum 是最新的
$ sudo yum update yum
下面将给出 Gentoo、Debian 发行版中是如何安装 Red Hat 包管理器?为方便计,切换到 root 用户:
$ su -
1. 在 Gentoo 中安装 YUM 包管理器
Gentoo Portage 中提供了 rpm、yum 等 Red Hat 工具,请安装最新版本,这里以 amd64 架构为例:
# echo 'sys-apps/yum ~amd64' >> /etc/portage/package.keywords
# echo 'dev-python/sqlitecachec ~amd64' >> /etc/portage/package.keywords
# echo 'app-arch/rpm sqlite' >> /etc/portage/package.use
# emerge -avuDN sys-apps/yum app-arch/rpm dev-python/m2crypto
 
2、在 Debian 中安装 YUM 包管理器
Debian 源中也提供了 Red Hat 的包管理工具,不过要安装最新的 yum 包
# apt-get install -t experimental yum rpm python-m2crypto
 
C、准备 CentOS 的安装空间
有了宿主环境,还需要安装 CentOS 的空间。通常根据宿主环境的不同,可以是同一块硬盘中的空闲空间,也可以从其他机器上暂时拆一下一块硬盘提供的空闲空间,还可以是移动硬盘或者 U 盘,当然也不排除虚拟机空间。为了方便起见,不妨假设我们在虚拟机中操作。下面开始分区相关操作。
 
空间分区
# fdisk /dev/sda
假设分了三个区,分别是交换分区 /dev/sda1、根分区 /dev/sda2、启动分区 /dev/sda3。格式化分区
# mkswap /dev/sda1
# mke2fs -j /dev/sda2
# mke2fs -j /dev/sda3
接着挂载分区
# mkdir /mnt/centos
# mount /dev/sda2 /mnt/centos
# mkdir /mnt/centos/boot
# mount /dev/sda3 /mnt/centos/boot
# swapon /dev/sda1
 
D、将 Red Hat 包管理工具安装到 CentOS 的分区中
先建立 RPM 数据库
# mkdir -p /mnt/centos/var/lib/rpm
# rpm --root /mnt/centos --initdb
接着到 CentOS 的官方镜像网站 http://vault.centos.org 下载需要版本的 centos-release 并安装,也可到 http://rpm.pbone.nethttp://www.rpmfind.net 网找查找 centos-release 包:
# rpm -ivh --nodeps --root /mnt/centos http://vault.centos.org/6.4/os/x86_64/Packages/centos-release-6-3.el6.centos.x86_64.rpm
上述命令会将 centos-release 包提供的 CentOS 的官方源的配置文件安装到 /mnt/centos/etc/yum.repos.d 目录。
 
由于 Red Hat 系并没有提供 locale-gen 之类的命令配置系统 locale 的数量,这里采用 RPM 宏的方式来选择它们
# echo %_install_langs en:zh:zh_CN:zh_CN.UTF-8 >> /mnt/centos/etc/rpm/macros.lang
 
可以将 Red Hat 的包管理工具 YUM 安装到 /mnt/centos 了:
# yum --installroot /mnt/centos install yum --nogpgcheck
注意上述命令中的 --nogpgcheck 选项,以防出现 GPG 公匙验证错误。若直接使用官方的 CentOS 镜像进行安装比较慢,也可以替换 CentOS 的源配置,例如
# mv /mnt/centos/etc/yum.repos.d/CentOS-Base.repo /mnt/centos/etc/yum.repos.d/CentOS-Base.repo.backup 
# wget -O /mnt/centos/etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-6.repo 
# yum makecache
再试试上述的 yum 操作。
 
若不想进入下一步繁琐的 chroot 操作,实际上,最小安装也可以通过下面的方法直接实现
# wget http://vault.centos.org/6.4/os/x86_64/images/install.img
# yum install squashfs-tools
# unsquashfs -d /mnt/centos install.img
该镜像安装完成之后,chroot 的相关操作会相对简单一些。
 
E、进入预装的 CentOS 系统
复制域名解析配置文件
# cp -L /etc/resolv.conf /mnt/centos/etc/resolv.conf
挂载必要的
# mount --bind /dev /mnt/centos/dev
# mount -t proc none /mnt/centos/proc
# mount -t sysfs none /mnt/centos/sys
好了,进入 chroot 环境
# chroot /mnt/centos /bin/bash
 
1、重建 RPM 数据库
可能由于宿主环境中的 RPM 数据库的版本问题,在 chroot 环境中执行 yum 可能会报 DB 数据库错误,用下述方法重建 RPM 数据库
# rpmdb --rebuilddb -vv
 
2、重置 root 用户的密码
为了确保我们能够以 root 用户进入 CentOS 系统,需要重置密码。由于 Red Hat 系的发行版中都默认开启了 SELinux,因此,chroot 环境中是不能通过 passwd 修改 root 密码的。我们可以通过手动修改 /etc/passwd 文件并通过 shadow-utils 工具来加密密码。为了简单起见,我们重置 root 密码为空
# sed -i 's@^root:x:@root::@' /etc/passwd
接着创建 /etc/shadow 文件
# yum install shadow-utils
# pwconv
 
3、设置系统的默认时区
# cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
 
4、配置 /etc/fstab
在开始内核安装之前,务必配置好系统根分区的信息,例如
# cat > /etc/fstab  << EOF
/dev/sda1     swap          swap       defaults           0 0
/dev/sda2     /             ext3       defaults           1 1
/dev/sda3     /boot         ext3       defaults           1 2
/dev/cdrom    /mnt/cdrom    iso9660    noauto,ro          1 1
tmpfs         /dev/shm      tmpfs      defaults           0 0
devpts        /dev/pts      devpts     gid=5,mode=620     0 0
sysfs         /sys          sysfs      defaults           0 0
proc          /proc         proc       defaults           0 0
EOF
注意到里面的 /mnt/cdrom,故需新建该目录
# mkdir -p /mnt/cdrom
另外,若喜欢使用 UUID 作为分区标记,可先用
# blkid /dev/sda2
# blkid /dev/sda3
参看各个分区的 UUID,随后修改 /etc/fstab 文件。
 
5、安装 Linux 内核
该轮到内核安装了,由于 CentOS 是自举安装的,因此推荐直接安装二进制内核以解决相关的依赖性问题:
# yum install kernel
 
6、配置 CentOS 的网络
设定主机名并启用网络
# cat > /etc/sysconfig/network << EOF
NETWORKING=yes
HOSTNAME=bootstrap
EOF
本地解析主机名设定
# echo '127.0.0.1 bootstrap' >> /etc/hosts
 
根据网络情况设定 IP 地址。如果机器获得静态 IP 地址,则
# cat > /etc/sysconfig/network-scripts/ifcfg-eth0 << EOF
DEVICE=eth0
HWADDR=00:00:00:00:01
ONBOOT=yes
BOOTPROTO=static
TYPE=Ethernet
IPADDR=10.10.19.234
NETMASK=255.255.254.0
GETEWAY=10.10.18.254
EOF
假如机器以动态方式获得 IP 地址,则需先安装 DHCP 客户端
# yum install dhclient
随后配置网络
# cat > /etc/sysconfig/network-scripts/ifcfg-eth0 << EOF
DEVICE=eth0        
ONBOOT=yes
BOOTPROTO=dhcp
EOF
其他方式的网络获得方法,请自行查阅资料。
 
最后,根据实际情况配置域名解析服务器
# cat /etc/resolv.conf
nameserver 208.67.222.222
nameserver 208.67.220.220
 
7、配置 Linux 键盘布局以及终端字体(可选)
这一部分对于大多数用户来说是不需要配置的,因为通常的键盘布局都是美式的;用户的工作方式也大多集中于图形操作界面。不过,如需相关配置的话,请先查阅 /usr/share/doc/initscripts 中的说明文件。
 
配置键盘布局,通常使用的键盘都是美式的,可作如下操作
# yum install kbd
# echo KEYTABLE="/lib/kbd/keymaps/i386/qwerty/us.map.gz" >> /etc/sysconfig/keyboard
有关键盘布局的信息,可到 /lib/kdb/keymanps 目录查看。
 
终端字体的安装
# yum install terminus-fonts-console
使用term系列字体,很漂亮,偶现在用的是ter-g16f.psf.gz字体。即使生效的办法是
# setfont /lib/kbd/consolefonts/ter-g16f.psf.gz
若想永久生效,可配置
# echo SYSFONT="/lib/kbd/consolefonts/ter-g16f.psf.gz" >> /etc/sysconfig/i18n
如需更多终端字体,请到 /lib/kbd/consolefonts 目录中查看。
 
8、安装必要的系统工具
为了方便调试系统以及维护系统,常用的系统日志与日程管理工具是必需的:
# yum install rsyslog crontabs cronie cronie-anacron
 
9、安装引导程序
目前 Linux 的引导程序基本上都是 grub,不过由于 CentOS 相对陈旧,它还没有提供 grub2。
# yum install grub
接着根据分区情况配置 grub
# cat > /boot/grub/grub.conf << EOF
timeout 5
default 0

# (0) CentOS
title CentOS 6.4
root (hd0,1)
kernel /vmlinuz-2.6.32-358.18.1.el6.x86_64 root=/dev/sda2 ro
initrd /initrd-2.6.32-358.18.1.el6.x86_64.img
EOF
用下述命令将 grub 安装到
# grub-install /dev/sda
若安装失败,可尝试
# grub
> root (hd0,1)
> setup (hd0)
> quit
 
10、退出 chroot 环境并重启系统
# exit
# cd /
# umount /mnt/centos/dev
# umount /mnt/centos/proc
# umount /mnt/centos/sys
# umount /mnt/centos/boot
# umount /mnt/centos
# reboot
 
D、进入全新的 CentOS 系统
 
如果一切正常的话,电脑将进入全新的 CentOS 系统,以 root 用户无密码登录即可
login: root
 
进入系统的第一件事情就是重置 root 用户密码,先将常用的 passwd 工具装上
# yum install passwd
试着执行一下 passwd 命令,由于 SELinux 的原因,通常会出现下面的错误
# passwd
passwd: unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 is not authorized to change the password of root
为了让 passwd 正常工作,请禁用 SELinux:
# setenforce 0
如果想永久的禁用 SELinux,请执行
# cat > /etc/selinux/config << EOF
SELINUX=disabled
SELINUXTYPE=targeted
EOF
# ln -s /etc/selinux/config /etc/sysconfig/selinux
好了,试试重置 root 用户的密码吧:
# passwd
 
为方便控制用户权限,可安装 sudo 包:
# yum install sudo
接着开启 wheel 组的 root 权限
# sed -i 's|^# %wheel  ALL=(ALL:ALL) NOPASSWD: ALL|%wheel  ALL=(ALL:ALL) NOPASSWD: ALL|' /etc/sudoers
增加普通用户到 CentOS 系统并将它加入 wheel 组:
# adduser easior -a wheel
接着重置 easior 的密码
# passwd easior
好了再次重启系统
# reboot
测试普通用户的权限设置是否正常?
 
以普通用户登录
login: easior
password: 
尝试用 sudo 安装基本工具
$ sudo yum install nano man-pages openssh
一切正常的话,可以进行其他配置了。
 
E、CentOS/RHEL 软件源简介
 
CentOS 官方发行版提供了四个配置文件:CentOS-Base.repo、CentOS-Vault.repo、CentOS-debuginfo.repo、CentOS-Media.repo。后三个配置文件分别是发行版所有过往的源配置文件、官方源中相应包的 debuginfo 源配置文件、光盘镜像的源配置文件,它们默认是不开启的。因此,CentOS 官方发行版中的源是由 CentOS-Base.repo 决定的。虽然该配置文件里包含了 base、updates、extras、CentOSPlus、contrib 源,但官方源中去除了很多有版权争议的软件,导致可安装的软件数量非常有限;而且相对于 Fedora 等版本,软件版本也不是最新的。这就需要我们使用第三方软件源作为补充,简化 CentOS 用户的使用。下面稍微对 CentOS/RHEL 中的常用源做一些说明。有关 CentOS/RHEL 源的更多介绍,请看 http://wiki.centos.org/AdditionalResources/Repositories。当然,里面的内容可能有些过时。
 
CentOS/RHEL 官方源
正如前面提到的,CentOS/RHEL 官方提供了一些源,不过默认状态下只开启了少量的几个源。例如 CentOS 只开启了 CentOS-base.repo 中的 base、update、extras 这三个源,而其他源均不开启。base 与 updates 是 CentOS/RHEL 发行版中提供的基础组件软件包及其更新。extras 提供了一些额外的不破坏 CentOS/RHEL 系统兼容性且不改变基础组件的软件包。这些软件包已经由 CentOS 开发小组经过测试确保它们在 CentOS 中正常工作。这些软件可能不是由 RHEL 上游提供的。值得指出的是,extras 源中含有持续发行软件源 centos-release-cr,它含有下一个 CentOS 发行版的软件包。CentOSPlus 源是为那些更改 CentOS 基本组件的软件包而设立的。使用该源会导致 CentOS 与上游提供者的内容有异。CentOS 的开发小组已经针对该源内的每个程序作出测试,确定它们能在 CentOS 下创建及运作。contrib 源中则含有 CentOS 用户供献的组件,它们并不会与核心发行版本的组件重叠。这些组件并没有经过 CentOS 的开发者测试,也未必会紧跟 RHEL 的发行。
 
CentOS-Testing 源
该源是由 CentOS 开发者维护并为 CentOSPlus 与 extras 两个源提供软件包的。这些软件包既不一定进入 CentOS 的正式源,也不一定保证正常工作。这些软件包是专门给测试者使用并由他们反馈相关的功能与稳定性。源中这些处于开发阶段的软件随时可能移除,因此在生产环境中务必关闭该源。 CentOS-Testing 源默认不自带在 CentOS 系统中,不过仍可通过下面的方法添加
$ sudo wget http://dev.centos.org/centos/6/testing/CentOS-Testing.repo  -O /etc/yum.repos.d/CentOS-Testing.repo
$ sudo sed -i 's/^enable=0/enable=1'  /etc/yum.repos.d/CentOS-Testing.repo
 
EPEL 源
EPEL(Extra Packages for Enterprise Linux) 源是由 Fedora 社区创建维护的,为 RHEL 及衍生发行版如 CentOS、Scientific Linux 等提供高质量软件包的项目。EPEL 中含有大量的软件,对官方标准源是一个很好的补充。Fedora 的官方说明在 http://fedoraproject.org/wiki/EPEL。EPEL 源的添加方法很简单
$ sudo rpm -ivh http://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm
 
RPMForge 源(REPOForge 源)
RPMForge 源是由 Dag 及其它包装者合作维护的,它为 CentOS/RHEL 提供了超过 10000 多个包,其中包含了dropbox、wine、vlc、mplayer、xmms-mp3 以及其他一些非常流行的媒体工具。它本身不是 CentOS/RHEL 的一部分,但是它与这些发行版是相容的。目前,REPOForge 是 RPMForge 继任者,不过源本身并没有改名。RPMForge 源被 CentOS 社区认为是最安全也是最稳定的一个第三方软件源。RPMForge 的官方网站在 http://repoforge.org/。它的添加方法如下:
i386 架构的系统,请执行
$ sudo rpm -ivh http://pkgs.repoforge.org/rpmforge-release/rpmforge-release-0.5.2-2.el5.rf.i386.rpm
而 x86_64 架构的系统则执行
$ sudo rpm -ivh http://pkgs.repoforge.org/rpmforge-release/rpmforge-release-0.5.2-2.el5.rf.x86_64.rpm
接着注入 DAG 的 PGP 公匙
$ sudo rpm --import http://apt.sw.be/RPM-GPG-KEY.dag.txt
 
RPM Fusion 源
RPM Fusion 提供了很多 Fedora Project 或 Red Hat 没有提供的软件包。所有这些软件均以预编译的 RPM 包的形式提供给 Fedora 与 CentOS/RHEL。 RPM Fusion 混合了原先的 Dribble、Freshrpms 与 Livna。RPM Fusion 源中除了提供大量的多媒体工具之外,还提供了 VirtualBox 相关的工具。RPM Fusion 官网位于 http://rpmfusion.org。配置 RPM Fusion 源非常简单,请执行
$ sudo rpm -ivh http://download1.rpmfusion.org/free/el/updates/6/i386/rpmfusion-free-release-6-1.noarch.rpm 
$ sudo rpm -ivh http://download1.rpmfusion.org/nonfree/el/updates/6/i386/rpmfusion-nonfree-release-6-1.noarch.rpm
 
Adobe 源
该源提供了多种语言的 Adobe Reader 与 flash-plugin,它的详细介绍请参看 http://blogs.adobe.com/acroread/2008/02/adobe_reader_now_available_via.html。对于 i386 架构的系统,请执行
$ sudo rpm -ivh http://linuxdownload.adobe.com/linux/x86_64/adobe-release-x86_64-1.0-1.noarch.rpm
来配置 Adobe 源;而对 x86_64 架构的系统,则可执行
$ sudo rpm -ivh http://linuxdownload.adobe.com/linux/i386/adobe-release-i386-1.0-1.noarch.rpm
 
Google 源
Google 源提供了诸如 Google Chrome、Google Earth、Google Talk Plugin 等应用,具体信息参见 http://www.google.com/linuxrepositories/。尽管 Google 并没有提供源的安装方法,不过,关于 Google 源的示例却不难找到,下面便是源安装示例:
$ sudo cat > /etc/yum.repos.d/google.repo << EOF
[google-chrome]
name=Google Chrome - $basearch
baseurl=http://dl.google.com/linux/chrome/rpm/stable/$basearch/
enabled=1
gpgcheck=1
gpgkey=https://dl-ssl.google.com/linux/linux_signing_key.pub

[google-earth]
name=Google Earth - $basearch
baseurl=http://dl.google.com/linux/earth/rpm/stable/$basearch/
enabled=1
gpgcheck=1
gpgkey=https://dl-ssl.google.com/linux/linux_signing_key.pub

[google-musicmanager]
name=Google Music Manager - $basearch
baseurl=http://dl.google.com/linux/musicmanager/rpm/stable/i386
enabled=1
gpgcheck=1
gpgkey=https://dl-ssl.google.com/linux/linux_signing_key.pub

[google-talkplugin]
name=Google Talk Plugin - $basearch baseurl=http://dl.google.com/linux/talkplugin/rpm/stable/$basearch
enabled=1
gpgcheck=1
gpgkey=https://dl-ssl.google.com/linux/linux_signing_key.pub 

[google-mod-pagespeed]
name=Google Mod PageSpeed - $basearch baseurl=http://dl.google.com/linux/mod-pagespeed/rpm/stable/$basearch
enabled=1
gpgcheck=1
gpgkey=https://dl-ssl.google.com/linux/linux_signing_key.pub
EOF
 
以上各源对 CentOS/RHEL 等系统完全兼容,但各软件源之间并不能保证完全兼容没有冲突。如果需要使用以上源,最好安装 yum-plugin-priorities 插件调整各个源之间的优先级
$ sudo yum install yum-plugin-priorities
安装 yum-plugin-priorities 插件后,可以通过修改 /etc/yum.repos.d/ 中各个源的配置文件,在其中插入指令
priority=N
从而设置各个源的优先级 priority,其中 N 为1到99的正整数,数值越小优先级越高。一般官方源优先级设置为 1,最高;第三方源的优先级这只推荐为 >10。例如下面示例中将 base、updates、extras 源优先级设为了 1,而 CentOSplus、contrib 的优先级则为 2:
$ cat /etc/yum.repos.d/CentOS-Base.repo
# CentOS-Base.repo
#
# The mirror system uses the connecting IP address of the client and the
# update status of each mirror to pick mirrors that are updated to and
# geographically close to the client.  You should use this for CentOS updates
# unless you are manually picking other mirrors.
#
# If the mirrorlist= does not work for you, as a fall back you can try the 
# remarked out baseurl= line instead.
#
#

[base]
name=CentOS-$releasever - Base
mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=os
#baseurl=http://mirror.centos.org/centos/$releasever/os/$basearch/
gpgcheck=1
gpgkey=http://mirror.centos.org/centos/RPM-GPG-KEY-CentOS-6
priority=1

#released updates 
[update]
name=CentOS-$releasever - Updates
mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=updates
#baseurl=http://mirror.centos.org/centos/$releasever/updates/$basearch/
gpgcheck=1
gpgkey=http://mirror.centos.org/centos/RPM-GPG-KEY-CentOS-6
priority=1

#additional packages that may be useful
[extras]
name=CentOS-$releasever - Extras
mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=extras
#baseurl=http://mirror.centos.org/centos/$releasever/extras/$basearch/
gpgcheck=1
gpgkey=http://mirror.centos.org/centos/RPM-GPG-KEY-CentOS-6
priority=1

#additional packages that extend functionality of existing packages
[centosplus]
name=CentOS-$releasever - Plus
mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=centosplus
#baseurl=http://mirror.centos.org/centos/$releasever/centosplus/$basearch/
gpgcheck=1
enabled=0
gpgkey=http://mirror.centos.org/centos/RPM-GPG-KEY-CentOS-6
priority=2

#contrib - packages by Centos Users
[contrib]
name=CentOS-$releasever - Contrib
mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=contrib
#baseurl=http://mirror.centos.org/centos/$releasever/contrib/$basearch/
gpgcheck=1
enabled=0
gpgkey=http://mirror.centos.org/centos/RPM-GPG-KEY-CentOS-6
priority=2
 
F、一些简单的配置
作为服务器而言,CentOS 的配置通常集中在网络服务上面,例如 LAMP、FTP、RSYNC 等。相对而言,它的桌面应用的相关配置反而显得并不重要。不过不管怎样,CentOS 的终端界面总是需要配置的,特别是中文显示、输入等等。
 
为了让中文能够正常显示,需要设定系统的 locale 以及安装一些中文字体
$ su -c 'echo Lang="zh_CN.UTF-8" >> /etc/sysconfig/i18n'
$ sudo yum install wqy-micro-fonts wqy-bitmap-fonts
即使如此,中文字符在 Linux 的终端仍显示为乱码,这涉及到 Linux 内核的问题。解决的办法是要么给 Linux 内核打 UTF-8 补丁,要么安装 Framebuffer 终端模拟器。这里采用后者,为此添加 EPEL 源
$ sudo rpm -ivh http://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm
$ sudo yum clean all
$ sudo yum install fbterm
接着安装中文手册页,并运行 fbterm 测试中文显示是否正常:
$ sudo yum install man-pages-zh-CN
$ fbterm
随便打开一个中文 man page 看看吧!
 
为了在终端输入中文字符,可以安装 fcitx-fbterm 或者 ibus-fbterm 来配合 fbterm 使用。这里使用 ibus-fbterm 来输入中文,不过可惜所有源中均无此包,只能自己动手了。先安装基本的工具链
$ sudo yum install @Development\ Tools
某些包的编译过程可能还需好内核功能:
$ sudo yum install kernel-devel kernel-headers
接着安装 RPM 开发工具并在用户家目录建立 RPM 开发目录
$ sudo yum install rpmdevtools
$ rpmdev-setuptree
接着到 Fedora 官方中下载 ibus-fbterm 的 RPM 源码包进行编译安装
$ wget http://dl.fedoraproject.org/pub/fedora/linux/releases/19/Everything/source/SRPMS/i/ibus-fbterm-0.9.1-16.fc19.src.rpm
$ rpm -ivh /ibus-fbterm-0.9.1-16.fc19.src.rpm
进入 ~/rpmbuild/SPECS 目录适当修改 ibus-fbterm.spec,接着便可生成 ibus-fbterm 的 rpm 包了
$ cd  ~/rpmbuild/SPECS
$ nano -w ibus-fbterm.spec
$ sudo yum install yum-utils
$ sudo yum-builddep ibus-fbterm.spec
$ rpmbuild -ba ibus-fbterm.spec
$ cd ../RPMS/x86_64
$ sudo rpm -ivh ibus-fbterm.*.rpm
为了方便用户使用,可直接查看页面上给出的 FedoraPeople 上的 DoReMi 源。 
 
再来安装 Linux 终端下的图片浏览器 fbi 与 PDF 文档阅读工具 fbgs
$ sudo yum install fbida fbida-fbgs
另外,还有截屏工具 fbgrab
$ sudo yum install fbgrab
 
文本浏览器
$ sudo yum install w3m w3m-img links2
网络聊天工具
$ sudo yum install finch irssi
 
想要获得终端的鼠标支持功能,需安装 gpm 并启动 gpm 服务
$ sudo yum install gpm
$ sudo service gpm start
$ sudo chkconfig gpm on
试试用鼠标左键选择、鼠标中键复制吧。
 
在 CentOS 中安装 VirtualBox 非常简单,只需开启 RPM Fusion 源。不过,VirtualBox 官方也提供了相应的源,这里不妨用 VirtualBox 官方的源:
$ sudo wget http://download.virtualbox.org/virtualbox/rpm/rhel/virtualbox.repo -O /etc/yum.repos.d/virtualbox.repo
接着根据需要安装相应版本的 VirtualBox
$ sudo yum install VirtualBox-4.2
接着配置 VirtualBox,下面的命令行会创建 vboxusers 用户以及用户组,同时将自动编译相关的内核模块
$ sudo service vboxdrv setup
试试以 headless 方式启动 VirtualBox 吧!
Sep 13

众所周知,除去 rawhide 之外,Fedora 的版本是不能滚动的。但是我们仍可以通过启用不同版本的源,直接由 YUM 实现跨版本升级。跨版本升级的好处是显而易见的,如同滚动升级一样,系统上常用的包,除了需要更新之外,不必像装新系统那样一次次地从头安装。这里以 Fedora 18 升级到 Fedora 20(Branched Release) 为例,说明一下大概的步骤。至于上面提到的 Branched Release 的相关信息以及它的生存周期,请参考 https://fedoraproject.org/wiki/Releases/Branched

在开始升级系统之前,先清理一下系统中的孤包、残留包以及没有合并的配置文件。为此,先安装
$ sudo yum install yum-utils
随后执行
$ sudo package-cleanup --orphans
查看孤包列表,根据需要删除孤包列表中的某些软件。注意,要小心列表中的包,因为里面的包并不一定都是没用的包。再来执行
$ sudo package-cleanup --leaves
查看残余包列表,根据需要删除其中的某些包。作为准备工作的最后一步,记得合并一下所有变更过的配置文件:
$ sudo yum install rpmconf
$ sudo rpmconf -a
根据提示查看更改细节,然后决定合并的方式。
 
现在可以开始跨版本升级 Fedora 了,为了防止系统更新过程中 X Window 不能正常工作,请切换到控制台完成以下操作(即 Alt-F1~F6)。先升级 YUM 工具:
$ sudo yum update yum --releasever=20 --disablerepo=* --enablerepo=fedora --enablerepo=updates-testing --disableplugin=presto
注意上述命令中禁用了 yum-presto 插件,以防版本差距较大的 delta 包破坏系统的更新过程。另外,此过程中还可能会出现 GPG 公匙验证错误,可用下面的方式暂时忽略它
$ sudo yum update yum --releasever=20 --disablerepo=* --enablerepo=fedora --enablerepo=updates-testing --nogpgcheck --disableplugin=presto
当然,也可以参看 https://fedoraproject.org/keys 页面中的信息,然后根据提示注入 GPG 公匙,例如 Fedora 20(Branched Release) 的公匙注入方法:
$ sudo rpm --import https://fedoraproject.org/static/246110C1.txt
包管理器 YUM 升级成功之后,可以升级系统了(这是一件相对冒险的事情,记得备份好系统):
$ sudo yum distro-sync --releasever=20 --disablerepo=* --enablerepo=fedora --enablerepo=updates-testing --nogpgcheck --disableplugin=presto
这一步将会经过漫长的时间处理包之间的依赖关系,如果一切顺利的话,便可直接更新系统版本了。倘若这一步仍有依赖性无法解决,那么有三个方法可以解决:一、根据提示暂时删除那些破坏依赖关系的包,不过对我们工作有用的那些包,最好不要用这招,以免日后忘了重新安装它;二、与升级 YUM 的方法一样,单独升级某几个破坏依赖的包,例如
$ sudo yum update python-pexec --releasever=20 --disablerepo=* --enablerepo=fedora --enablerepo=updates-testing --nogpgcheck --disableplugin=presto
三、利用 --skip-broken 暂时跳过无法解决依赖的那些包:例如
$ sudo yum distro-sync --releasever=20 --disablerepo=* --enablerepo=fedora --enablerepo=updates-testing --nogpgcheck --disableplugin=presto --skip-broken
如果开始执行上面这一行命令的话,会有大面积的包需要下载与更新,这就需要我们耐心等待。
 
长时间等待之后,Fedora 18 系统顺利更新到了 Fedora 20(Branched Release)。不过版本更新过程还没有结束,下面开始做必要的检查工作。首先,确认 Fedora 18 中的第三方源中哪些源还能正常工作:
$ sudo yum repolist
根据提示信息,手动禁用那些无法工作的源,例如
$ sudo yum-config-manager --disable livna
接着再来确认 Fedora 20(Branched Release) 的最小系统是否已经更新完成:
$ sudo yum update "Minimal Install"
重新安装 Fedora 系统的引导程序,目前 grub2 是 Fedora 20 中的引导程序,因此
$ sudo grub2-install /dev/sda
$ su -c 'grub2-mkconfig -o /boot/grub2/grub.cfg'
修复一下初始话脚本的启动次序
$ su
# cd /etc/rc.d/init.d; for f in *; do [ -x $f ] && /sbin/chkconfig $f resetpriorities; done
最后,大范围的版本升级可能造成系统配置的更改,请再次运行下述命令合并配置文件
$ sudo rpmconf -a
好了,基本上重启之前的准备工作已经完成了。重启系统吧:
$ sudo reboot

 

尽管 Fedora 20 的系统能够启动起来,不过,由于目前刚进入 Branched Release 不久,系统里面的包还不是很完整。例如,通过下述方式可查看系统中残留的 Fedora 18 的软件包:

$ sudo package-cleanup --leaves

这个列表中有一部分包来自于原先 Fedora 18 系统的第三方源。对于这些包,我们不必急着删除它们,可以等到第三方源更新到 Fedora 20 的时候再升级它们。

Sep 6

cgit 是一款为 Git 服务器提供 Web 浏览界面的工具,与 gitweb 的功能类似。不过,cgit 的界面更加友好一些,速度更快一些。这里就来谈谈如何在 Fedora 19 中安装与配置 cgit,以及用它替代 gitweb 实现与 Gitolite 的整合。

cgit 的安装非常便当,仅需执行:

$ sudo yum install cgit
接着适当地调整 cgit 目录的访问权限,这里以 Apache 服务器为例:
$ sudo nano -w /etc/httpd/conf.d/cgit.conf
#
# cgit configuration for apache
#

ScriptAlias /cgit /var/www/cgi-bin/cgit
Alias /cgit-data /usr/share/cgit/

<Directory "/usr/share/cgit/">
   AllowOverride None
   Options None
  <IfModule mod_authz_core.c>
       # Apache 2.4
       Require all granted
   </IfModule>
   <IfModule !mod_authz_core.c>
       # Apache 2.2
       Order allow,deny
       Allow from all
   </IfModule>
</Directory>

<Directory "/var/www/cgi-bin"> 
  AllowOverride None 
  Options ExecCGI FollowSymlinks
  <IfModule mod_authz_core.c>
       # Apache 2.4
       Require all granted
   </IfModule>
   <IfModule !mod_authz_core.c>
       # Apache 2.2
       Order allow,deny
       Allow from all
   </IfModule>
</Directory>
注意,确保上述设置与 cgit 的一下配置一致,也即编辑 /etc/cgitrc 文件:
$ sudo nano -w /etc/cgitrc
查看下面几行
# cgit config

css=/cgit/cgit.css
logo=/cgit/cgit.png
如果不想被搜索引擎索引的话,还可往里添加一行:
# if you don't want that webcrawler (like google) index your site
robots=noindex, nofollow
修改完成之后,重启 Apache 服务器
$ sudo service httpd restart
如果Apache 服务器已经配置好了的话,可以测试一下 cgit 的功能是否正常工作。这只需在可以访问当该服务器器的电脑上打开网页浏览器,并在地址栏输入 http://serverIP/cgit。如果网页能够打开,那么表明 cgit 已经能够工作了。
 
需要注意,上面只是测试了 cgit 提供的 Web 界面是否工作,但是还没有涉及到 Git 服务器的部分。下面开始来谈论 cgit 如何关联到 Git 服务器。实际上,它是通过 /etc/cgitrc 配置文件来关联 Git 服务器的:
$ sudo nano -w /etc/cgitrc
根据具体情况编辑该配置文件,例如修改下面这些行:
#
# List of repositories.
# This list could be kept in a different file (e.g. '/etc/cgitrepos')
# and included like this:
#   include=/etc/cgitrepos

#repo.url=foo
#repo.path=/var/lib/git/foo.git
#repo.desc=the master foo repository
#repo.owner=fooman@example.com
#repo.readme=info/web/about.html

repo.url=MyRepo
repo.path=/var/lib/git/MyRepo.git
repo.desc=This is my git repository
实际上,真正与 Git 服务有关的只有最后三行,这三行给出了 Git 仓库的位置、名称以及扼要的描述。更多的 Git 仓库,均可按该格式添加至 cgit 的配置文件。现在在网页浏览器的地址栏再次输入 http://serverIP/cgit 测试一下,看看能不能访问到 Git 仓库信息。
 
为了更好地控制 Git 服务器的访问权限,一般 Git 服务部分都是通过 Gitolite 这样的工具实现的。Gitolite 的配置过程在前面已经描述过了。自然而然的,需要考虑如何以 cgit 替代 gitweb,将它与 Gitolite 整合在一起。解决这个问题的方法与实现 gitweb 的整合过程差不多。首先,修改 Gitolite 新建仓库的默认权限,这可通过修改 /var/lib/gitolite/.gitolite.rc 的 UMASK 实现:
$ sudo nano -w /var/lib/gitolite/.gitolite.rc
更改 UMASK 为 0022,也即
#$REPO_UMASK = 0077; # gets you 'rwx------'
#$REPO_UMASK = 0027; # gets you 'rwxr-x---'
$REPO_UMASK = 0022; # gets you 'rwxr-xr-x'
接着通过下述命令修改 Gitolite 中所有现有仓库的权限:
$ sudo find /path/to/the/repository/ -type d -exec chmod og+rx {} \;
$ sudo find /path/to/the/repository/ -type f -exec chmod og+r {} \;
最后,为了让 cgit 能够自动访问到 Gitolite 中仓库信息,仍需修改配置文件 /etc/cgitrc:
$ sudo nano -w /etc/cgitrc
提供类似于 gitweb 中所需要的 projects.list 等信息,例如:
#
# information related to gitolite 
#

enable-git-config=1
enable-gitweb-owner=1
remove-suffix=1
project-list=/var/lib/gitolite/projects.list
scan-path=/var/lib/gitolite/repositories
请注意最后这 5 行,它们包含了与 Gitolite 整合所需要的所有内容。至于 Gitolite 中的修改,完全与 gitweb 所需的修改一致,这里就不再重复了。
 
如果还想为 cgit 锦上添花的话,可安装 highlight 实现 cgit 访问的 Git 仓库中源代码的高亮:
$ sudo yum install highlight

不过这还需要 cgit 的配置,也即

$ sudo nano -w /etc/cgitrc

增加或去掉下面这一行的注释

source-filter=/usr/lib/cgit/filters/syntax-highlighting.sh
Jul 30

Gitolite 是一款由 Perl 语言开发的 Git 服务管理工具,通过 SSH 公钥对用户进行认证,并能够通过配置文件对版本库的写操作进行基于分支、标签以及文件路径等的权限控制。 gitweb 为 Gitolite 提供了版本库的 Web 界面,方便用户浏览版本库的变更与版本打包;另一方面,由于 Gitolite 并没有提供匿名用户的读权限,因此,需要借助 git-daemon 来弥补这一缺陷。

1、Gitolite 的安装与配置

Gitolite 的安装非常简单,执行下述命令即可:
$ sudo yum install gitolite
查看 Gitolite 的用户信息
$ cat /etc/passwd | grep -i gitolite
gitolite:x:976:971:git repository hosting:/var/lib/gitolite:/bin/sh
这表明 Fedora 中 Gitolite 的家目录位于 /var/lib/gitolite。
 
下面开始初始化 Gitolite。由于 Gitolite 采用的是 SSH 协议,因此在初始化开始之前必须注入 Gitolite 管理者的 SSH 公匙。这里不去谈论 SSH 密匙的生成过程了,直接假定拥有 SSH 密匙的用户 James 将成为 Gotolite 的管理者:
$ cp ~/.ssh/id_rsa.pub /tmp/james.pub
请注意公匙的命名方法,这是 Gitolite 区别各个用户的默认办法。另外,可能出于安全的考虑,我们可能修改过 ~/.ssh 目录下文件的权限。由于下面的操作需要用户 James 的公匙具有写权限,所以需要执行下面的命令行
$ chmod a+r /tmp/james.pub
还有一件事情需要指出,既然 Gitolite 使用的是 SSH 协议,因此它工作的前提是运行 sshd 系统服务,请查看它是否运行
$ systemctl status sshd

如果没有运行的话,请执行下述命令行,至于 SSH 端口之类的问题就不做赘述了:

$ sudo systemctl start sshd
$ sudo systemctl enable sshd
现在可以初始化 Gitolite 了:
$ sudo -u gitolite -H gl-setup /tmp/james.pub
 
好了,下面该让我们稍微解释一下 Gitolite 对 Git 服务器的管理方式了。 Gitolite 通过导入用户的 SSH 公匙的方式来实现对 GIT 服务器的用户管理。Gitolite 利用配置文件 /var/lib/gitolite/repositoreis/gitolite-admin.git/config/gitolite.conf 实现了用户权限的控制。非常有意思的是,Gitolite 以 GIT 版本库的形式在管理 gitolite.conf 与所有用户的 SSH 公匙。具体来说,Gitolite 中预设了两个版本库,即 gitolite-admin.git 与 testing.git:
$ sudo ls /var/lib/gitolite/repositories
gitolite-admin.git  testing.git
可能我们已经注意到,版本库目录中的版本库名字均是以 .git 结尾的,这是 Gitolite 的约定。Gitolite 的管理者正是通过 gitolite-admin.git 来管理版本库的,他可以以版本控制的方式管理它。而 Gitolie 会自动根据该版本库中的修改来产生新的配置。好了,让我们的管理者把 gitolite-admin.git 克隆到他的家目录中去吧:
$ cd
$ git clone gitolite@serverIP:gitolite-admin.git
$ cd gitolite-admin
$ cat conf/gitolite.conf
repo    gitolite-admin
        RW+     =   james

repo    testing
        RW+     =   @all
我们很容易看到 gitolite.conf 中奇特的语法。每一个版本库的授权均以 repo 指令开头。repo 后面紧跟的是版本库列表。版本库列表中的版本库名之间用空格分开,版本库名中可以含有相对路径名。根据 Gitolite 的默认规则,repo 后面的版本库名不需要以 .git 为后缀,因为 Gitolite 会自动添加该后缀名。另外,版本库列表里面还可以包括版本库组。版本库组就是一些以空格间隔的版本库名的合集,版本库组名以@开头,例如
@testings testing james/testing
repo @testings
repo 指令也可以紧跟以正则表达式定义的通配符版本库。需要注意通配符版本库在匹配时,会自动在通配符版本库的正则表达式前、后分别加上前缀 ^ 和后缀 $。例如
repo james/.+
要当心的是版本库名中的正则表达式过于简单,就可能引起歧义,让 Gitolite 以为它就是普通版本库名称。为了不必要的麻烦,可在正则表达式的前或后主动加上 ^ 或 $ 符号,例如
repo james/.+$
 
repo 指令后续的几行就是版本库的授权命令列表。每条授权指令有相同的缩进,且以 = 为标记分为前后两段,等号前面的是权限表达式,等号后面的是用户列表。授权指令的语法类似于
perms [regex ...] = user1 [user2 ...]
正如我们所看到的,授权指令中包含一个可选的 regex 列表。regex 列表有正则表达式给出,它们与 repo 所指定的版本库里的分支、标签甚至是文件名或路径进行匹配。具体说来,正则表达式 regex 可能与分支名匹配,也可能与分支组匹配。分支组名以@开头,它是一些有空格间隔的分支名的合集,例如
@mainstream master developing testing
regex 也可能与 refs/tags/ 中的标签名匹配;regex 还可能与版本库中的文件名或文件路径匹配。如果 regex 中的表达式不以 $ 结尾的话,那么它将匹配以该正则表达式开头的任意字符,相当于在表达式后面添加 .*$。如果授权指令中不包含任何 regex 列表,那么该授权将针对由 repo 指定整个的版本库。
 
perms 部分只能出现如下授权之一:-、C、R、RW、RW+、RWC、RW+C、RWD、RW+D、RWCD、RW+CD。它们的具体含义如下:
  • -:不能写入、但是可以读取;
  • C:仅在通配符版本库中可以使用。用于指定谁可以创建和通配符匹配的版本库。
  • R, RW, 和 RW+:R 为只读。RW 为读写权限。RW+ 含义为除了具有读写外,还可以对 rewind 的提交强制 PUSH。
  • RWC, RW+C:只有当授权指令中定义了 regex 列表才可以使用该授权指令。其中 C 的含义是允许创建与 regex 匹配的分支、标签、文件名或者路径。
  • RWD, RW+D:只有当授权指令中定义了 regex 列表才可以使用该授权指令。其中 D 的含义是允许删除与 regex 匹配的分支、标签、文件名或者路径。
  • RWCD, RW+CD:只有当授权指令中定义了 regex 列表才可以使用该授权指令。
Gitolie 中的用户通常是以公匙文件名来指定的,这是 Gitolite 中的用户的表示方法之一。另一种 Gitolite 用户的表示方法就是用户组,用户组的命名以@开头,例如
@myteam james alice
定义了一个名为 @myteam 的用户组,james、alice 都是属于 myteam 这个用户组。Gitolite 中有个特殊的用户组,即@all,用来表示所有的用户。
 
下面给出一个权限控制的示例
@admin james
@myteam james alice
@test alice

@testings james/testing alice/testing

@mainstream master developing

repo    james/.+$
        C                           =   @admin
        R       @mainstream         =   @test
        -                           =   bob
        RW                          =   @myteam bob

repo    testing @testings
        RW+                         =   @admin
        RW      master              =   bob
        RW      developing$         =   alice
        -                           =   bob
        RW      tmp/                =   @all
        RW      refs/tags/v[0-9]    =   alice
这里有一点需要指出,那就是当同一个用户或者组有多个授权时,Gitolite 将采用所有授权的并集。
 
为了将上面的介绍与实际应用相结合,不妨考虑为 Gitolite 增加一个版本库 myproj、两个用户组 @admin 与 @myteam,以及用户 Alice 并授予她 myproj.git 的读写权限与用户 Bob 但只给予 myproj.git 的读权限。先让 Gitolite 的管理者 James 执行下述命令:
$ cd ~/gitolite-admin
$ cp /path/to/alice.pub keydir/
$ cp /path/to/bob.pub keydir/
$ nano -w conf/gitolite.conf
@admin james
@myteam james alice

repo    gitolite-admin
        RW+     =   @admin

repo    testing
        RW+     =   @all

repo    myproj
        RW+     =   @myteam
        RW+CD   =   @admin
        R       =   bob
$ git add conf/gitolite.conf keydir/alice.pub keydir/bob.pub
$ git commit -m 'new user and repo created.'
$ git push origin master
接着再让 James 在自己的主机家目录下创建新项目 myproj 并推至服务器:
$ mkdir -p ~/myproj
$ cd ~/myproj
$ git init
$ echo "hello, Gitolite!" >> hello.txt
$ git add hello.txt
$ git commit -m 'initial commit.'
$ git remote add origin gitolite@serverIP:myproj.git
$ git push origin master
现在试试让 Alice 在自己的主机上克隆并做一次提交
$ cd ~
$ git clone gitolite@serverIP:myproj.git
$ cd myproj
$ echo "hi, I'm Alice." >> hello.txt
$ git add hello.txt
$ git commit  -m 'say hello to Git Server."
$ git push origin master

再来让 Bob 试试 GIT 服务器的情况:

$ cd ~
$ git clone gitolite@serverIP:gitolite-admin.git

正常情况,Bob 无法克隆该仓库;

$ git clone gitolite@serverIP:testing.git
$ cd testing
$ echo 1 > index.txt
$ git add index.txt
$ git commit -m 'write permission for testing repo.'
$ git push origin master

这表明 Bob 拥有 testing.git 的读写权限;再来看

$ cd
$ git clone gitolite@serverIP:myproj.git
$ myproj
$ echo "hello, I'm Bob." >> hello.txt
$ git add hello.txt
$ git commit -m 'say hello to Gitolite.'
$ git push origin master

这表明 Bob 只能读取 myproj 但没有写权限。这几个操作表明 Gitolite 比较完美的控制了用户操作 GIT 服务器的权限。

2、与 gitweb 的整合
 
最初 gitweb 是为 GIT 服务器的 http 协议提供 Web 界面的。这里正是借助这个功能,为 Gitolite 实现同样的功能。同时,Gitolite 也为 gitweb 提供了两个便利:为 gitweb 提供了设置版本库的描述信息途径,可用于在 gitweb 的项目列表页面显示;为 gitweb 自动生成项目的列表文件,避免 gitweb 使用效率低的目录递归搜索查找 Git 版本库列表。
 
安装 gitweb 与 highlight:
$ sudo yum install highlight gitweb
其中 highlight 是为了在网页中查看代码的时候可以高亮代码的语法。查看 gitweb 的配置文件所在的位置:
$ rpm -qc gitweb
/etc/gitweb.conf
/etc/httpd/conf.d/git.conf
/etc/httpd/conf.d/git.conf 是与 Apache 相关的配置。gitweb 本身的配置文件是 /etc/gitweb.conf,下面开始配置 gitweb:
$ sudo nano -w /etc/gitweb.conf
根据需要调整里面的内容,下面这几行的内容必须调整
our $projectroot="/var/lib/gitolite/repositories";
our $projects_list="/var/lib/gitolite/projects.list";
至于语法高亮,自己看着办
# Add highlighting at the end 
$feature{'highlight'}{'default'}= [1];
 
为了能够让 Apache 访问到 Gitolite 目录中的文件,需要将 apache 加入到 gitolite 组中,并修改相关文件的权限
$ sudo usermod -a -G gitolite apache
$ sudo chmod g+r /var/lib/gitolite/projects.list
$ sudo chmod -R g+rx /var/lib/gitolite/repositories
由于 Gitolite 中默认新建文件的权限是 0077,这意味着要让 Apache 访问之后新建版本库,必须对之后新建版本库权限作类似的修改。为了免去这一麻烦,这里利用 Gitolite 的配置文件 ~/.gitolite.rc 来指定新建文件的权限:
$ sudo nano -w /var/lib/gitolite/.gitolite.rc
根据自己的需要修改下面的行
#$REPO_UMASK = 0077; # gets you 'rwx------'
#$REPO_UMASK = 0027; # gets you 'rwxr-x---'
$REPO_UMASK = 0022; # gets you 'rwxr-xr-x'
好了,一切就绪了,重启 Apache 服务:
$ sudo service httpd restart
 
以上配置基本都是在告诉 Apache 一些 GIT 服务器的信息。不过,为了让 gitweb 工作,还需要让 Gitolite 知道哪些仓库启用了 gitweb。这可以通过下面这个例子来了解这个机制,让管理者再次修改 Gitolite 的配置文件
$ cd
$ cd gitolite-admin
$ nano -w conf/gitolite.conf
repo    gitolite-admin
        RW+     =   james

repo    testing
        testing "James" =   "Git repository for testing"
        RW+     =   @all
        R       =   gitweb
$ git add conf/gitolite.conf
$ git commit -m 'gitweb support for Gitolite.'
$ git push origin master

很明显,'R = gitweb' 这一行告诉 Gitolite 版本库 testing.git 启用 gitweb,这样 Gitolite 就会把 testing.git 写入 projects.list 文件;而 'testing "James" = "git repository for testing"' 这一行则是为了在网页中显示 testing 仓库的额外信息,也即仓库的创建者以及仓库的概述。其他版本库启用 gitweb 的设置完全类同,这里就不一一修改了。实际上,版本库的信息的设定也通过如下的命令来完成:

repo    testing
        config gitweb.owner         =   some person's name
        config gitweb.description   =   some description
        config gitweb.category      =   some category 
 
是时候打开网页浏览器开始测试了,在地址栏中输入 http://serverIP/git,正常情况应该出现下图。
 
3、与 git-daemon  的整合
 
最后,让我们利用 git-daemon 为 Gitolite 开启匿名访问功能。首先安装它:
$ sudo yum install git-daemon
需要注意的是,由 Fedora 19 中 git-daemon 的 RPM 包存在问题,详见 https://bugzilla.redhat.com/show_bug.cgi?id=980574。因此,如果要在 Fedora 19 中启动 git-daemon 的服务以配合 Gitolite,那么需要作如下的修改:
$ mv /usr/lib/systemd/system/git.service /usr/lib/systemd/system/git@.service
随后将它按如下方式修改:
$ cat /usr/lib/systemd/system/git@.service
[Unit]
Description=Git Repositories Server Daemon
Documentation=man:git-daemon(1)
Wants=git.socket

[Service]
User=nobody
ExecStart=-/usr/libexec/git-core/git-daemon --base-path=/var/lib/gitolite/repositories --export-all --user-path=public_git --syslog --inetd --verbose
StandardInput=socket
由该段代码可以看到,Fedora 19 中 git-daemon 包并没有创建新的用户组,而是以 nobody 用户启动了系统服务。但是,Gitolite 的目录则是赋予了 gitolite 组:
$ ls -ald /var/lib/gitolite
drwxr-xr-- 5 gitolite gitolite 4096 Jul 29 22:31 /var/lib/gitolite
因此,为了让 git-daemon 能够访问 Gitolite 的家目录,需要将 nobody 加入 gitolite 组并赋予 Gitolite 家目录以可读权限
$ sudo usermod -a -G gitolite nobody
$ sudo chmod g+r -R /var/lib/gitolite/repositories
现在启动 git-daemon 服务并让它随机器启动:
$ sudo systemctl start git.socket
$ sudo systemctl enable git.socket
git-daemon 默认端口是 9418,查看该端口是否打开
$ netstat -nat | grep 9418
如果没有打开的话,请编辑 /etc/sysconfig/iptables:
$ nano -w /etc/sysconfig/iptables
在 Drop 与 Reject 规则之前,增加一行
-A INPUT -p tcp -m conntrack --ctstate NEW -m tcp --dport 9418 -j ACCEPT
重启防火墙:
$ sudo service iptables restart
至此,git-daemon 已经知道 Gitolite 的工作目录了。不过,Gitolite 中的版本库还需要为 git-daemon 自动生成 git-daemon-export-ok 文件, 这便是下面的设置,仍需管理者运行:
$ cd 
$ cd gitolite-admin
$ nano -w conf/gitolite.conf
repo    gitolite-admin
        RW+     =   james

repo    testing
        RW+     =   @all
        R       =   daemon
$ git add conf/gitolite.conf
$ git commit -m 'git daemon support for Gitolite.'
$ git push origin master

'R=daemon' 这一行就是告诉 Gitolite 为 git-daemon 生成 git-daemon-export-ok 文件。

好了,现在让我们做简单的测试:

$ git clone git://serverIP/testing.git
一切正常。

 

Jul 22

LAMP 服务器就绪之后,就可以开始考虑构建网站了,这是 LAMP 应用的一个方面;根据 LAMP 服务提供的 WEB 界面来管理 LAMP 主机则是应用的另一个方面。当然,有 Web 界面的管理工具很多,这类工具在远程主机的控制,特别是在 Headless 主机的控制上发挥着至关重要的作用。下面开始要介绍的是一些依赖与 LAMP 服务的管理工具。

二、与 LAMP 服务相关的管理工具

1、phpMyAdmin 的安装

phpMyAdmin 是 MariaDB 数据库的 Web 界面管理工具。通过它,就能远程管理 MariaDB 的数据库了。由于它已经在 Fedora 的 YUM 仓库之中,它的安装非常容易:

$ sudo yum install phpmyadmin
phpMyAdmin 的配置文件位于如下目录
$ rpm -qc phpMyAdmin
/etc/httpd/conf.d/phpMyAdmin.conf
/etc/phpMyAdmin/config.inc.php
与 Apache 的相关配置文件是 /etc/httpd/conf.d/phpMyAdmin.conf。
 
现在重启 Apache 服务
$ sudo service httpd restart
先作本地测试,这个很容易,通过浏览器访问 http://localhost/phpmyadmin  即可管理 MariaDB 数据库。
 
另一方面,phpMyAdmin 的默认设置是不允许远程主机访问的,为了让其他远程主机也能访问,作如下修改
$ sudo nano -w /etc/httpd/conf.d/phpMyAdmin.conf
Directory /usr/share/phpMyAdmin/>
   <IfModule mod_authz_core.c>
     # Apache 2.4
     <RequireAny>
       Require all granted
     </RequireAny>
   </IfModule>
   <IfModule !mod_authz_core.c>
     # Apache 2.2
     Order Deny,Allow
     Allow from all
   </IfModule>
</Directory>
再次重启 Apache 服务
$ sudo service httpd restart
任找一台能够访问的远程主机,通过在浏览器中输入 http://ipaddr/phpmyadmin 访问后台数据库并管理它。
 
2、Webalizer 
Webalizer 是一种网页日志分析工具,它用比较直观地图形表格来分析 Web 服务器的状态。官方网站地址是 http://www.mrunix.net/webalizer/。由于 Fedora 已经提供了 Webalizer 的 RPM 包,因此,只需执行
$ sudo yum install webalizer
即可完成安装。与 phpMyAdmin 类似,Webalizer 默认不能远程访问,作如下修改
$ sudo nano -w /etc/httpd/conf.d/webalizer.conf
Alias /usage /var/www/usage
<Location /usage>
   # Alternative e.g. "Require ip 192.168.10"
   #Require local
   Require all granted
</Location>
为了修改能够即时生效,需重启 Apache 服务:
$ sudo service httpd restart
注意,此时 Webalizer 并没有工作,需执行
$ sudo webalizer
才行。Webalizer 的使用非常简单,只需在浏览器中输入 http://ipaddr/usage/
 
3、phpsysinfo 的安装
phpsysinfo 是一款简单的工具,它提供了 LAMP 主机的系统信息。它的项目主页在 http://phpsysinfo.sourceforge.net/。由于 Fedora 官方没有提供该 RPM 包,如有需要,可到按下述方法安装它:
$ sudo rpm -ivh http://fedorapeople.org/~easior/fedora/19/i386/doremi-release-19-1.noarch.rpm
$ sudo yum install phpsysinfo

为了让 Apache 服务能够即时认出 phpsysinfo,需重启 Apache 服务:

$ sudo service httpd restart
phpsysinfo 的用法很简单。只需要在网页浏览器中输入 http://ipaddr/phpsysinfo 即可。
 
4. Webmin 的安装
 
Webmin是目前功能最强大的基于Web的 Unix/Linux 系统管理工具。管理员通过浏览器访问Webmin的各种管理功能并完成相应的管理动作。Webmin 让您能够在远程使用支持 HTTPS (SSL 上的 HTTP)协议的 Web 浏览器通过 Web 界面管理您的主机。这在保证了安全性的前提下提供了简单深入的远程管理。它的项目主页在 http://www.webmin.com。由于 Fedora 官方没有提供 Webmin 的 RPM 包,为了方便用户使用它,Webmin 的官方提供了 YUM 源。因此,先做如下设置
$ su -c 'echo > /etc/yum.repos.d/webmin.repo << EOF
[Webmin]
name=Webmin Distribution Neutral
#baseurl=http://download.webmin.com/download/yum
mirrorlist=http://download.webmin.com/download/yum/mirrorlist
enabled=1
gpgcheck=1
gpgkey=http://www.webmin.com/jcameron-key.asc
EOF'
好了,可以直接安装 Webmin 了:
$ sudo yum install webmin
需要注意,由于 Webmin 启用 HTTPS 协议,因此它并没有共用 Apache 的服务端口,而是使用了10000端口,因此需要在防火墙中打开该端口:
$ sudo nano -w /etc/sysconfig/iptables
在里面增加一行
-A INPUT -p tcp -m tcp --dport 10000 -j ACCEPT
保存后,重启 iptables 服务
$ sudo service iptables restart
 
现在可以试试用 Webmin 管理 LAMP 服务器的主机了。打开浏览器,输入 https://ipaddr:10000,通常,Webmin 需要超级用户登录。登录之后,就会出现如下的界面。
 
Webmin 的功能相当强大,大多数操作只需要动动鼠标就能完成,这比 SSH 操作方便的多。而且 Webmin 也提供了 SSH 模块,利用 Web 浏览器同样能够执行 SSH 操作。
 
与 LAMP 相关的 Web 管理工具层出不穷,例如 Nagios、phpVirtualBox 等等。这些工具的介绍恐怕不是短时间之内所能穷尽的,有兴趣的话可自行查找相关资料。后面要介绍的是利用 LAMP 服务建立网站,主要侧重的是如何利用现成的建站软件构建网站。至于利用 LAMP 开发网站,那恐怕是 Web 程序员的事情了。

 

Jul 22

虽然 Fedora 作为服务器来用不是上佳的选择,不过拿来练手倒是上上之选。因为 Fedora 中的大多数 Web 服务除去安全性等方面需要多加注意之外,基本上属于即装即用型的。Fedora 上的 Web 服务器的搭建也有很多选择,而且都是一些非常成熟的组合。反正不能免俗,不妨选择最常用的 LAMP 组合作为 Web 服务器的基础。不过话说回来,物换星移,Fedora 19 以 MariaDB 替换了 MySQL,也算是必然趋势。尽管如此,这些对于普通用户的影响并不大,mysqld 还是那个 mysqld。 作为普通用户,这里主要介绍的是与 LAMP 服务器相关的一些应用。至于 LAMP 服务本身相关的网站开发、优化等内容,暂时没有时间也没有能力触及了。

下面主要的测试环境是 Fedora 19。其他环境中 Web 服务器安装大同小异,很容易自行调整。当然,在开始之前最好关掉 SELinux,具体来说

$ sudo nano -w /etc/sysconfig/selinux
SELINUX=disable

修改完之后需重启机器才能有效。为了免去重启机器,可执行

$ sudo setenforce 0

即可即时生效。

一、LAMP 的安装与测试

1. Apache 服务器的安装

Apache 是世界上非常流行的 Web 服务器软件之一,类似的还有lighttpd、Nginx、Tomcat 等。不过,Apache 功能强大,一直深受建站爱好者的喜爱。它的安装非常容易:

$ sudo yum install httpd

Apache 的配置文件位于 /etc/httpd 目录,其中 /etc/httpd/conf/httpd.conf 是其默认配置文件,默认虚拟主机的目录位于 /var/www/html,与 Apache 相关的 Web 应用的配置目录可能位于 /etc/httpd/conf.d/。下面启动 Apache 服务并让它随机器一起启动:

$ sudo service httpd start
$ sudo chkconfig httpd on

为了看看 Apache 有没有正常工作,先做一下本地测试。打开本地的网页浏览器,在地址栏输入 http://localhost。正常情况应该出现如下的页面:

2. MariaDB 数据库的安装

Linux 中的 SQL 数据库很多,例如 MariaDB(MySQL)、PostgreSQL、SQLite、Firebird 等。不过这里就只安装 MariaDB:

$ sudo yum install mariadb mariadb-server

MariaBD 服务的开启方法与 MySQL 一样:

$ sudo service mysqld start
$ sudo chkconfig mysqld on

实际上,MariaDB 的用法也与 MySQL 别无二致。例如,MariaDB 数据的默认账户是 root,它与 Linux 系统的 root 账户没有什么联系。通常,MariaDB 的默认账户是没有密码的,这对于数据库的管理来说是不安全的。给它设置一个密码,例如toor:

$ sudo mysqladmin -u root password toor

现在尝试在本地登录 MariaDB 数据库:

$ sudo mysql -u root -p

数据库一切正常。

3. 网页开发语言 PHP 的安装

网页开发语言众多,例如PHP、JSP、ASP等。不过在 Linux 下面,PHP 整合的相对好一些:

$ sudo yum install php

测试一下 PHP 是否正常工作?首先,在本地的 /var/www/html 目录新建如下的文件:

$ su -c 'echo > /var/www/html/info.php << EOF

<?php
    phpinfo();
?>
EOF'

接着,在本地浏览器的地址栏中输入 http://localhost/info.php。如果出现如下的页面,就说明 PHP 工作正常!

4. 整合Apache与PHP
 
具体的整合可能涉及到很多优化方面的内容,这里只谈非常常见的几点。
$ sudo nano -w /etc/httpd/conf/httpd.conf
先在
AddType application/x-gzip .gz .tgz
所在行的下面添加
AddType application/x-httpd-php .php .phtml
AddType applicatoin/x-httpd-php-source .phps
接着再将
   DirectoryIndex index.html
改为
    DirectoryIndex index.html index.htm index.php
修改完成后保存退出。
 
为了提升 PHP 安全性,可在 /etc/php.ini 中进行作如下设置
$ sudo nano -w /etc/php.ini
将下面这一行
disable_functions =
修改为:
disable_functions = phpinfo,passthru,exec,system,chroot,scandir,chgrp,chown,shell_exec,proc_open,proc_get_status,ini_alter,ini_alter,ini_restore,dl,pfsockopen,openlog,syslog,readlink,symlink,popepassthru,stream_socket_server

5. 防火墙设置与 LAMP 的远程测试

Fedora 中默认防火墙的安全级别比较高,基本上关闭了所有的服务端口。为了让远程机器能够访问 LAMP 服务器,需要适当调整防火墙设置。Fedora 在程序菜单中提供了防火墙的图形界面工具,不过这里主要介绍防火墙的命令行设置方法。由于 iptables 的规则是有优先次序的,通常需要手动编辑 iptables 的配置文件 /etc/sysconfig/iptables。下面为了简单起见,直接清空防火墙的原先设置,完全重写了所有的 iptables 规则。因此,使用下面这些规则的时候要小心,例如与 SSH 相关的规则没有在里面。关于如何开启 SSH 端口,可自行查阅 iptables 规则的写法。

首先,停止所有的 iptables 服务:

$ sudo service iptables stop
清除所有的 iptables 规则表:
$ sudo iptables -F -t filter
$ sudo iptables -X
设定预设 iptables 规则:
$ sudo iptables -P INPUT DROP
$ sudo iptables -P OUTPUT ACCEPT
接受所有本地回环网络的数据:
$ sudo iptables -A INPUT -i lo -j ACCEPT
接受所有 Established 连接:
$ sudo iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
设置 ICMP 服务(保证 
$ sudo iptables -A INPUT -p icmp -m limit --limit 1/s --limit-burst 10 -j ACCEPT
开启 Apache 服务的端口:
$ sudo iptables -A INPUT -p tcp --dport 80 -j ACCEPT
开启 MariaDB 服务的端口:
$ sudo iptables -A INPUT -p tcp --dport 3306 -j ACCEPT
最后,保存所有 iptables 的规则并再次启动 iptables 服务
$ sudo service iptables save
$ sudo service iptables start

为了确认本地端口已经正常开启,请执行以下命令

$ netstat -nat

检查相应服务的端口是否被监听。如果一切正常的话,可以开始在远端主机测试 LAMP 服务器了。假设 LAMP 主机的 IP 地址为 ipaddr。首先测试 Apache 服务,打开网页浏览器,在地址栏输入 http://ipaddr 或者 http://ipaddr/info.php。如果出现图或中的页面,就说明 Apache 工作正常;接着测试 MariaDB 服务,请安装 MariaDB 的客户端,然后登录 MariaDB 主机进行测试:

$ sudo yum install mariadb
$ mysql -h http://ipaddr -u root -p
至此,LAMP 服务器的安装配置已经完成,后面将接着介绍它的应用。