stateless linux是fedora core的一个项目,顾名思义,就是系统运行时不保存持久状态信息,简单来说就是不让系统对持久存储设备(比如硬盘)进行写操作。一般用于 readonly root技术。在我最近的程序中,用stateless linux来达到多个虚拟机共享同一份磁盘image。

stateless linux的设置文件是/etc/sysconfig/readonly-root,/etc/rc.sysinit在系统启动时读取这个文件,并根据文件中的设置进行配置。


从/etc/rc.sysinit中摘取出来的关于readonly-root的设置代码,在代码中添加了注释:
READONLY=
if [ -f /etc/sysconfig/readonly-root ]; then
    . /etc/sysconfig/readonly-root
fi

#/etc/sysconfig/readonly-root设置READONLY,TEMPORARY_STATE,RW_MOUNT,RW_LABEL,STATE_LABEL,STATE_MOUNT几个变量。
#在readonly-root文件中对这几个变量的作用做了详细的注释。从下面的代码中也可以体现出来。

if strstr "$cmdline" readonlyroot ; then
    READONLY=yes
    [ -z "$RW_MOUNT" ] && RW_MOUNT=/var/lib/stateless/writable
fi

#cmdline变量就是启动时传递给kernel的参数,可以从/proc/cmdline中看到,例如:ro root=LABEL=/

if strstr "$cmdline" noreadonlyroot ; then
    READONLY=no
fi

# READONLY和TEMPORARY_STATE只要设置其中一个为yes就可以。

if [ "$READONLY" = "yes" -o "$TEMPORARY_STATE" = "yes" ]; then

#mount_empty函数的作用就是把参数$1指定的文件或目录复制到$RW_MOUNT目录下作为备份,然后把备份文件使用bind方式mount到原来的文件上。
#这样原来的文件仍然可见可读,而且保证了只读,因为所有的写操作都会作用在备份文件上。cpio的-p参数指定了目标文件夹是$RW_MOUNT,-d参数
#表示如果$1是多级目录表示的一个文件,则这个目录结构会自动在$RW_MOUNT下原样创建。如果$1是一个目录,则目录下的文件不会被复制,这应该
#是mount_empty中的empty的意思。

    mount_empty() {
        if [ -e "$1" ]; then
            echo "$1" | cpio -p -vd "$RW_MOUNT" &>/dev/null
            mount -n --bind "$RW_MOUNT$1" "$1"
        fi
    }

#如果$1是一个目录,则mount_dirs把$1下层的目录结构按照原样在$RW_MOUNT下创建。
    mount_dirs() {
        if [ -e "$1" ]; then
            mkdir -p "$RW_MOUNT$1"
            # fixme: find is bad
            find "$1" -type d -print0 | cpio -p -0vd "$RW_MOUNT" &>/dev/null
            mount -n --bind "$RW_MOUNT$1" "$1"
        fi
    }

    mount_files() {
        if [ -e "$1" ]; then
            cp -a --parents "$1" "$RW_MOUNT"
            mount -n --bind "$RW_MOUNT$1" "$1"
        fi
    }

#简而言之,mount_{empty,dirs,files}都是针对参数$1而言,mount_empty只管$1,不管$1下面的任何结点,mount_dirs只管维持$1下层的目录结构,
#mount_files维持$1下面的所有目录和文件。

    # Common mount options for scratch space regardless of
    # type of backing store
    mountopts=

#blkid命令用来索引或查看block设备的属性。-t参数用来指定一个属性(属性以name=value的方式表示,例如LABEL=/),-l参数表示
#搜索其属性值和-t参数指定的属性值符合的block设备。-o指定输出格式,可选的有full, value, device。以下是输出示例。
#[root@jcwkyl etc]# blkid -t LABEL=/ -l -o full
#/dev/sda3: LABEL="/" UUID="8844f9c3-2836-4f29-aaab-3fb6b6d0a1bf" SEC_TYPE="ext2" TYPE="ext3"
#[root@jcwkyl etc]# blkid -t LABEL=/ -l -o value
#/
#8844f9c3-2836-4f29-aaab-3fb6b6d0a1bf
#ext2
#ext3
#[root@jcwkyl etc]# blkid -t LABEL=/ -l -o device
#/dev/sda3

    # Scan partitions for local scratch storage
    rw_mount_dev=$(blkid -t LABEL="$RW_LABEL" -l -o device)

#这段代码中的注释说的非常清楚了,首先尝试从/etc/fstab中的设置来mount,如果/etc/fstab中没有设置,
#则直接把上一步找到的分区mount到$RW_MOUNT,如果还没有成功,则把$RW_MOUNT目录mount成tmpfs

    # First try to mount scratch storage from /etc/fstab, then any
    # partition with the proper label.  If either succeeds, be sure
    # to wipe the scratch storage clean.  If both fail, then mount
    # scratch storage via tmpfs.
    if mount $mountopts "$RW_MOUNT" > /dev/null 2>&1 ; then
        rm -rf "$RW_MOUNT" > /dev/null 2>&1
    elif [ x$rw_mount_dev != x ] && mount $rw_mount_dev $mountopts "$RW_MOUNT" > /dev/null 2>&1; then
        rm -rf "$RW_MOUNT"  > /dev/null 2>&1
    else
        mount -n -t tmpfs $mountopts none "$RW_MOUNT"
    fi

    for file in /etc/rwtab /etc/rwtab.d/* ; do
        is_ignored_file "$file" && continue
            [ -f $file ] && cat $file | while read type path ; do
            case "$type" in
                empty)
                    mount_empty $path
                    ;;
                files)
                    mount_files $path
                    ;;
                dirs)
                    mount_dirs $path
                    ;;
                *)
                    ;;
            esac
            [ -n "$SELINUX_STATE" -a -e "$path" ] && restorecon -R "$path"
        done
    done

    # In theory there should be no more than one network interface active
    # this early in the boot process -- the one we're booting from.
    # Use the network address to set the hostname of the client.  This
    # must be done even if we have local storage.
    ipaddr=
    if [ "$HOSTNAME" = "localhost" -o "$HOSTNAME" = "localhost.localdomain" ]; then
        ipaddr=$(ip addr show to 0/0 scope global | awk '/[[:space:]]inet / { print gensub("/.*","","g",$2) }')
        if [ -n "$ipaddr" ]; then
            eval $(ipcalc -h $ipaddr 2>/dev/null)
            hostname ${HOSTNAME}
        fi
    fi
   
    # Clients with read-only root filesystems may be provided with a
    # place where they can place minimal amounts of persistent
    # state.  SSH keys or puppet certificates for example.
    #
    # Ideally we'll use puppet to manage the state directory and to
    # create the bind mounts.  However, until that's all ready this
    # is sufficient to build a working system.

    # First try to mount persistent data from /etc/fstab, then any
    # partition with the proper label, then fallback to NFS
    state_mount_dev=$(blkid -t LABEL="$STATE_LABEL" -l -o device)
    if mount $mountopts $STATE_OPTIONS "$STATE_MOUNT" > /dev/null 2>&1 ; then
        /bin/true
    elif [ x$state_mount_dev != x ] && mount $state_mount_dev $mountopts "$STATE_MOUNT" > /dev/null 2>&1;  then
        /bin/true
    elif [ ! -z "$CLIENTSTATE" ]; then
        # No local storage was found.  Make a final attempt to find
        # state on an NFS server.

        mount -t nfs $CLIENTSTATE/$HOSTNAME $STATE_MOUNT -o rw,nolock
    fi

    if [ -d $STATE_MOUNT/etc ]; then
        # Copy the puppet CA's cert from the r/o image into the
        # state directory so that we can create a bind mount on
        # the ssl directory for storing the client cert.  I'd really
        # rather have a unionfs to deal with this stuff
        cp --parents -f -p /var/lib/puppet/ssl/certs/ca.pem $STATE_MOUNT 2>/dev/null

        # In the future this will be handled by puppet
        for i in $(grep -v "^#" $STATE_MOUNT/files); do
            if [ -e $i ]; then
                 mount -n -o bind $STATE_MOUNT/${i} ${i}
            fi
         done
    fi
fi

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐