virt-sysprep(镜像初始化工具) 参考 virt-sysprep基本使用
有时还要额外定制些镜像初始化工作,需要编写些脚本辅助清理初始化工作
清理 bash-history
代码语言:javascript复制#!/usr/bin/env bash## Remove bash history for root and system usersset -o errexitroots_hist="$(find /root -type f -name .bash_history)"users_hist="$(find /home -type f -name .bash_history | tr -s 'n' ' ')"rm -f ${roots_hist} ${users_hist}exit 0
root只有一个,user往往有多个,将换行替换成空格作为rm命令的参数
清理 cloud-init
代码语言:javascript复制#!/usr/bin/env bash## Remove all cloud-init run-time data and logs## The removal completely resets cloud-init. When the instance is next# started cloud-init will run all configured modules as if running for the# first timeset -o errexitrm -rf /var/lib/cloud/*rm -f /var/log/cloud-init.logexit 0
清理系统崩溃的dump数据
代码语言:javascript复制#!/usr/bin/env bash## Remove crash data generated by kexec-toolsset -o errexitcrash_data_location=(
"/var/crash/*"
"/var/log/dump/*")# Include hidden files in globshopt -s nullglob dotglobfor crash_data in ${crash_data_location[@]}do
rm -rf ${crash_data}doneexit 0
shopt -s nullglob dotglob
为了rm -rf *
可以包含隐藏文件
清理dhcp-client数据
代码语言:javascript复制#!/usr/bin/env bash## Remove DHCP client lease information. Note that Debian 10, and possibly# other OSes, now write a machine specific DUID (DHCP Unique ID) to the# leases fileset -o errexitlease_data_locations=(
"/var/lib/dhclient/*"
"/var/lib/dhcp/*")# Include hidden files in globshopt -s nullglob dotglob# Ensure all interfaces are down prior to removing the leases file.# Otherwise the file is recreated by ifdown/dhclientfor iface in $(ls /sys/class/net/ | grep -v lo); do
# Wait for each interface to be taken down. Timeout after 20 secs
timer=0
while grep up "/sys/class/net/${iface}/operstate" &>/dev/null &&
[[ timer -lt 20 ]]; do
sleep 1
let timer=${timer} 1 done
# If the interface is still up it is likely something has gone wrong
# with the usual procedures that take the interface down at shutdown.
# Make a best effort attempt to take the interface down manually
# temporarily ignoring errors
if grep up "/sys/class/net/${iface}/operstate" &>/dev/null; then
set o errexit ifdown ${iface}
set -o errexit fi
# Some implementations start the dhcp client when the interface is taken
# down (even if the interface is statically configured). It is the dhcp
# client that writes out to the leases file when the interface goes
# down. Kill the client as a precautionary measure to prevent further
# interference. Ignore errors in case the dhclient exits between
# obtaining its pid and killing it
pid="$(ps aux | grep /sbin/dhclient | grep "${iface}" | tr -s " " |
cut -d' ' -f2)"
if [ "x${pid}" != "x" ]; then
set o errexit kill -9 "${pid}"
set -o errexit fidone# Now that all interfaces are down remove all lease filesfor lease_file in ${lease_data_locations[@]}; do
rm -f ${lease_file}doneexit 0
设定timeout为20秒,等待interface down,若还没down则通过ifdown命令down掉,网口down的时候可能dhcpclient会起进程,kill掉对应网口的dhcpclient进程后进入清理文件的工作。
清理防火墙规则和配置
代码语言:javascript复制#!/usr/bin/env bash## Remove any custom firewall rules or firewalld configuration## Modern systems typically make use of the dynamic firewall daemon# firewalld which provides many advantages and additional features over# more traditional approaches. Customisation of the systems firewall rules# it handled through user space tools that output configuration# customisations to /etc/firewalld/zones and /etc/firewalld/services.# Deleting these files will remove any custom configuration from the# system## Older systems or other firewall implementations usually persist rules# information for iptables in /etc/sysconfig/iptables and use the file to# configure the firewall at startup. As such simply deleting the file will# be enough to remove any custom configuration from the systemset -o errexitfw_config_locations=(
"/etc/sysconfig/iptables"
"/etc/firewalld/services/*"
"/etc/firewalld/zones/*")# If using firewalld stop the daemon/service prior to removing the configif command -v systemctl &>/dev/null; then
if systemctl is-active firewalld.service &>/dev/null; then
systemctl stop firewalld.service fifi# Include hidden files in globsshopt -s nullglob dotglob# Remove any custom configurationfor fw_config in ${fw_config_locations[@]}do
rm -rf ${fw_config}doneexit 0
command -v systemctl
检查是否存在systemctl命令。整个脚本先停掉防火墙服务,然后清理相关文件。
清理日志
代码语言:javascript复制#!/usr/bin/env bash## Remove log files from the guest## Basic outline for treatment of log directories:# 1. Section 1 of 'log directories' loop:# Create a tmpfs file system and copy any existing files from the log# directory to the new file system# 2. Section 2 of 'log directories' loop:# Mount the tmpfs file system over the top of the existing on-disk log# files directory. This *hopefully* means than any process relying on# files in the log directory will still have access to them and will# allow a clean shutdown while still allowing removal of all on disk# log files.# Since tmpfs file systems live on memory the contents copied to them# will disappear on shutdown# 3. Section 3 of 'log directories' loop:# Once the tmpfs file system has been mounted the original on-disk log# directory will no longer be directly accessible. In order to access# and clear any log files from these disk areas we need to re-mount or# bind mount the device or file system on which the log directory is# residing to an alternate location. We can then access and remove# any files from the disk by doing so from the alternate mount point.## Static log files are removed directly at the end of the script## Original log list taken from Libguestfs's sysprep_operation_logfiles.ml# See https://github.com/libguestfs/libguestfs/tree/master/sysprepset -o errexit# Absolute path to guest log file directories# All files under the given directories will be removedlogd_locations=(
# Log files and directories
"/var/log"
# GDM and session preferences
"/var/cache/gdm"
"/var/lib/AccountService/users"
# Fingerprint service files
"/var/lib/fprint"
# fontconfig caches
"/var/cache/fontconfig"
# man pages cache
"/var/cache/man")# Absolute path to static log files that can be removed directlylogf_locations=(
# Logfiles configured by /etc/logrotate.d/*
"/var/named/data/named.run"
# Status file of logrotate
"/var/lib/logrotate.status"
# Installation files
"/root/install.log"
"/root/install.log.syslog"
"/root/anaconda-ks.cfg"
"/root/original-ks.cfg"
"/root/anaconda-post.log"
"/root/initial-setup-ks.cfg"
# Pegasus certificates and other files
"/etc/Pegasus/*.cnf"
"/etc/Pegasus/*.crt"
"/etc/Pegasus/*.csr"
"/etc/Pegasus/*.pem"
"/etc/Pegasus/*.srl")# Set mountpoint used to access original on disk contentmntpnt_orig_logd="/mnt/orig_log_dir"# Include hidden files in globshopt -s dotglob# Since the current contents of the log directories will essentially be# copied into memory, we need to ensure that we don't cause an out of# memory condition for the guest. The limit of 128m should be extremely# generous for most systemssum_logd_space=0for logd in ${logd_locations[@]}do
if [ -d ${logd} ]; then
logd_space="$(du -sm ${logd} | cut -f1)"
else
logd_space=0
fi
sum_logd_space=$(( ${sum_logd_space} ${logd_space} ))
if [ ${sum_logd_space} -gt 128 ]; then
echo "ERROR: Space for copying logs into memory > 128mb. Exiting"
exit 1
fidone# Test for tmpfs filesystem at /dev/shm creating one if it doesn't exist# If /dev/shm is not present, attempt to create itif ! mount -l -t tmpfs | grep /dev/shm &>/dev/null; then
[[ -d /dev/shm ]] || mkdir /dev/shm && chmod 1777 /dev/shm mount -t tmpfs -o defaults,size=128m tmpfs /dev/shmfi# Remove logs from given log directoriesfor logd in ${logd_locations[@]}do
if [ -d ${logd} ]; then
# Test if the path or its parents are already on tmpfs
logd_path="${logd}"
on_tmpfs=false
while [[ ${logd_path:0:1} = "/" ]] && [[ ${#logd_path} > 1 ]] &&
[[ ${on_tmpfs} = false ]]
do
defifs=${IFS}
IFS=$'n' # Set for convenience with mount output
for mountpoint in $(mount -l -t tmpfs | cut -d' ' -f3)
do
if [ "${mountpoint}" == "${logd_path}" ]; then
on_tmpfs=true
continue # No need to test further
fi
done
IFS=${defifs} # Restore the default IFS and split behaviour
logd_path=${logd_path%/*} # Test parent on next iteration
done
if [ "${on_tmpfs}" = false ]; then
# Initialise/reset var used to store where log dir is located
logd_located_on=""
# If log directory is a mounted partition we need the device
defifs=${IFS} && IFS=$'n' # Set for convenience with df output
for line in $(df | tr -s ' ')
do
# Sixth column of df output is the mountpoint
if echo ${line} | cut -d' ' -f6 | grep ^${logd}$ &>/dev/null; then
# First column of df output is the device
logd_located_on="$(echo ${line} | cut -d' ' -f1)"
fi
done
IFS=${defifs} # Restore the default IFS and split behaviour
# If the log directory is not a mounted partition it must be on
# the root file system
[[ "x${logd_located_on}" = "x" ]] && logd_located_on="/"
# Recreate the log directory under /dev/shm (on tmpfs)
shmlogd="/dev/shm/${logd}"
mkdir -p ${shmlogd}
chmod 1777 ${shmlogd}
# Copy all files from original log dir to new tmpfs based dir
files=(${logd}/*) # Array allows wildcard/glob with [[ test ]]
[[ -e ${files} ]] && cp -pr ${logd}/* ${shmlogd}
# Replace the original disk based log directory structure with
# the ephemeral tmpfs based storage by mounting it over the top of
# the original log directories location on the file system
mount --bind ${shmlogd} ${logd}
# Create a mount point from which the contents of the original
# on-disk log directory can be accessed post mount of the tmpfs
# file system
mkdir ${mntpnt_orig_logd}
# Mount or bind mount in order to access the original on disk logs
if [ ${logd_located_on} = "/" ]; then
# Temp file system is a folder on the root file system
mount_opts="--bind"
# Contents will be under mount point original path e.g
# /mountpoint/var/tmp
logd_path="${mntpnt_orig_logd}/${logd}"
else
# Temp file system is a disk partition
mount_opts=""
# Contents will be directly available under the mount point
logd_path="${mntpnt_orig_logd}"
fi
# Mount the device holding the temp file system or bind mount the
# root file system
mount ${mount_opts} ${logd_located_on} ${mntpnt_orig_logd}
# The lastlog file cannot be created on demand for some reason
# and errors occur if /var/log/lastlog is missing. So, check if
# '/var/log/lastlog' exists and store the location so we can
# recreate later
if [ "${logd}" == "/var/log" ]; then
lastlog="$(find ${logd_path} -type f -name lastlog)"
fi
# Delete all files from the on-disk log directory
find "${logd_path}" -type f | xargs -I FILE rm -f FILE # Recreate the /var/log/lastlog file if required
if [[ "${logd}" == "/var/log" ]] && [[ "x${lastlog}" != "x" ]]; then
touch "${lastlog}"
fi
# Cleanup
umount ${mntpnt_orig_logd} && rm -rf ${mntpnt_orig_logd}
fi
fidone# Remove static log files and files that may be removed directlyfor file in ${logf_locations[@]}do
[[ -e ${file} ]] && rm -f ${file}doneexit 0
参考 清理临时文件 代码分析
清理machine-id
代码语言:javascript复制#!/usr/bin/env bash## Remove the local machine id prevent the possibility of machines having# duplicate identities post cloning operations## The machine id is a identifier first generated at install from a random# source. The id then persists for all subsequent boots and can be used to# uniquely identify the system within the network. The machine-id is often# used in preference to other identifiers such as a mac address, that may# infact change over the lifetime of the machine## For older systems the machine-id is located at /var/lib/dbus/machine-id# and is generated using the dbus-uuid utility.# To trigger the generation of a new machine-id, the machine-id file must# simply be removed. dbus will then create a new file and populate it with# a machine-id string on next boot. Note that if the file is only emptied# (rather than completely removed) then dbus will simply complain about# the fact and will NOT generate a new machine-id.## For more modern systems the machine-id file is located at# /etc/machine-id and /var/lib/dbus/machine-id (if present) is either a# copy of /etc/machine-id or is simply a symlink pointing to it.# Modern systems now use the 'systemd-machine-id-setup' utility to# generate the id file in place of the dbus-uuid tool employed on older# systems.# To trigger the generation of a new machine-id the machine-id file under# /etc must be emptied (NOT removed) and the machine-id file under# /var/lib/dbus (as with older systems) must be removed. If the# /etc/machine-id file is removed rather than emptied the system will not# be able to generate a new machine-id. This has rather dire consequences# for the boot process.# Additionally, if the /etc/machine-id file is emptied but the# /var/lib/dbus/machine-id file remains populated with an id string# then the system will simply copy the dbus machine-id string# into /etc/machine-id on next boot - in other words a new id won't be# created and the old id will be copied back into /etc/machine-idset -o errexit# Machine ID file locationssysd_id="/etc/machine-id"dbus_id="/var/lib/dbus/machine-id"# Remove and recreate (and so empty) the machine-id file under /etcif [ -e ${sysd_id} ]; then
rm -f ${sysd_id} && touch ${sysd_id}fi# Remove the machine-id file under /var/lib/dbus if it is not a symlinkif [[ -e ${dbus_id} && ! -h ${dbus_id} ]]; then
rm -f ${dbus_id}fiexit 0
清理邮件
代码语言:javascript复制#!/usr/bin/env bash## Remove mail from the local mail spoolset -o errexitmta_list=(
"exim"
"postfix"
"sendmail")mail_spool_locations=(
"/var/spool/mail/*"
"/var/mail/*")# Best effort attempt to stop any MTA servicefor mta in ${mta_list[@]}do
# Systemd
if command -v systemctl &>/dev/null ; then
mta_service="$(systemctl list-units --type service | grep ${mta} |
cut -d' ' -f1)"
if [ "x${mta_service}" != "x" ]; then
if systemctl is-active ${mta_service} &>/dev/null; then
systemctl stop ${mta_service}
fi
fi
# Sys-v-init
else
mta_service="$(find /etc/init.d/ -iname "*${mta}*")"
if [ "x${mta_service}" != "x" ]; then
if ${mta_service} status | grep running &>/dev/null; then
${mta_service} stop fi
fi
fidone# Include hidden files in globsshopt -s nullglob dotglob# Remove any mailfor mail_spool in ${mail_spool_locations[@]}do
rm -rf ${mail_spool}doneexit 0
先停掉的systemd或Sys-v-init管理的邮件服务后再清理相关文件
清理安装包cache
代码语言:javascript复制#!/usr/bin/env bash## Remove cache files associated with the guests package managerset -o errexit# Set the locations under which various package managers store cache filescache_locations=(
# Debian and derivatives
"/var/cache/apt/"
# Fedora
"/var/cache/dnf/"
# Red Hat and derivatives
"/var/cache/yum/"
# SUSE and openSUSE
"/var/cache/zypp*")# Note that globs in the cache locations will be auto expanded by bashfor cache_dir in ${cache_locations[@]}do
if [ -d ${cache_dir} ]; then
# Recursively remove all files from under the given directory
find ${cache_dir} -type f | xargs -I FILE rm -f FILE fidoneexit 0
支持Debian,Fedora,centos/rh,suse
清理包管理数据库
代码语言:javascript复制#!/usr/bin/env bash## Remove dynamically created package manager files#set -o errexit# RPM Host DB files. RPM will recreate these files automatically if neededrm -f /var/lib/rpm/__db.*# APT lists. APT will recreate these on the first 'apt update'apt_lists=/var/lib/apt/listsif [ -d "${apt_lists}" ]; then
find "${apt_lists}" -type f | xargs rm -ffiexit 0
清理临时文件
代码语言:javascript复制#!/usr/bin/env bash## Remove temporary files from the guest## Basic outline:# 1. Section 1 of 'Main' loop:# Create a tmpfs file system and copy any existing files from the temp# directory to the new file system# 2. Section 2 of 'Main' loop:# Mount the tmpfs file system over the top of the existing on-disk temp# files directory. This *hopefully* means than any process relying on# files in the temp directory will still have access to them and will# allow a clean shutdown while still allowing removal of all on disk# temp files.# Since tmpfs file systems live on memory the contents copied to them# will disappear on shutdown# 3. Section 3 of 'Main' loop:# Once the tmpfs file system has been mounted the original on-disk temp# directory will no longer be directly accessible. In order to access# and clear any temp files from these disk areas we need to re-mount or# bind mount the device or file system on which the temp directory is# residing to an alternate location. We can then access and remove# any files from the disk by doing so from the alternate mount point.set -o errexit# Absolute path to guest temp file directoriestmp_locations=(
"/tmp"
"/var/tmp")# Set mountpoint used to access original on disk contentmntpnt_orig_tmp="/mnt/orig_tmp"# Include hidden files in globshopt -s dotglob# Since the current contents of the temp file system will essentially be# copied into memory, we need to ensure that we don't cause an out of# memory condition for the guest. The limit of 128m should be extremely# generous for most systemssum_tmp_space=0for tmp in ${tmp_locations[@]}do
if [ -d ${tmp} ]; then
tmp_space="$(du -sm ${tmp} | cut -f1)"
else
tmp_space=0
fi
sum_tmp_space=$(( ${sum_tmp_space} ${tmp_space} ))
if [ ${sum_tmp_space} -gt 128 ]; then
echo "ERROR: Space for copying tmp into memory > 128mb. Exiting"
exit 1
fidone# Test for tmpfs filesystem at /dev/shm creating one if it doesn't exist# If /dev/shm is not present, attempt to create itif ! mount -l -t tmpfs | grep /dev/shm &>/dev/null; then
[[ -d /dev/shm ]] || mkdir /dev/shm && chmod 1777 /dev/shm mount -t tmpfs -o defaults,size=128m tmpfs /dev/shmfi# Mainfor tmp in ${tmp_locations[@]}do
# Test if the path or its parents are already on a tmpfs file system
tmp_path="${tmp}"
on_tmpfs=false
while [[ ${tmp_path:0:1} = "/" ]] && [[ ${#tmp_path} > 1 ]] &&
[[ ${on_tmpfs} = false ]]
do
defifs=${IFS}
IFS=$'n' # Set for convenience with mount output
for mountpoint in $(mount -l -t tmpfs | cut -d' ' -f3)
do
if [ "${mountpoint}" == "${tmp_path}" ]; then
on_tmpfs=true
continue # No need to test further
fi
done
IFS=${defifs} # Restore the default IFS and split behaviour
tmp_path=${tmp_path%/*} # Set to test parent on next iteration
done
# Perform required operations to delete temp files
if [ "${on_tmpfs}" = false ]; then
# Initialise/reset the var used to store where the temp is located
tmp_located_on=""
# If the temp directory is a mounted partition we need the device
defifs=${IFS} && IFS=$'n' # Set for convenience with df output
for line in $(df | tr -s ' ')
do
# Sixth column of df output is the mountpoint
if echo ${line} | cut -d' ' -f6 | grep ^${tmp}$ &>/dev/null; then
# First column of df output is the device
tmp_located_on="$(echo ${line} | cut -d' ' -f1)"
fi
done
IFS=${defifs} # Restore the default IFS and split behaviour
# If the temp directory is not a mounted partition it must be on
# the root file system
[[ "x${tmp_located_on}" = "x" ]] && tmp_located_on="/"
# Recreate the temp directory under /dev/shm (on tmpfs)
shmtmp="/dev/shm/${tmp}"
mkdir -p ${shmtmp}
chmod 1777 ${shmtmp}
# Copy all files from original temp dir to new tmpfs based dir
files=(${tmp}/*) # Array allows wildcard/glob with [[ test ]]
[[ -e ${files} ]] && cp -pr ${tmp}/* ${shmtmp}
# Replace the original disk based temp directory structure with
# the ephemeral tmpfs based storage by mounting it over the top of
# the original temp directories location on the file system
mount --bind ${shmtmp} ${tmp}
# Create a mount point from which the contents of the original
# on-disk temp directory can be accessed post mount of the tmpfs
# file system
mkdir ${mntpnt_orig_tmp}
# Mount or bind mount in order to access the original on disk temp
if [ ${tmp_located_on} = "/" ]; then
# Temp file system is a folder on the root file system
mount_opts="--bind"
# Contents will be under mount point original path e.g
# /mountpoint/var/tmp
tmp_path="${mntpnt_orig_tmp}/${tmp}"
else
# Temp file system is a disk partition
mount_opts=""
# Contents will be directly available under the mount point
tmp_path="${mntpnt_orig_tmp}"
fi
# Mount the device holding the temp file system or bind mount the
# root file system
mount ${mount_opts} ${tmp_located_on} ${mntpnt_orig_tmp}
# Delete all files from the on-disk temp directory
files=(${tmp_path}/*)
[[ -e ${files} ]] && rm -rf ${tmp_path}/* # Cleanup
umount ${mntpnt_orig_tmp} && rm -rf ${mntpnt_orig_tmp}
fidoneexit 0
‘Main’循环分三部分:
- 创建一个tmpfs文件系统,将要清理的文件夹复制到tmpfs文件系统
- 上一步意味着,若有进程需要读写要清理的文件,不受影响,只是实际操作的位置变成了tmpfs文件系统。tmpfs 文件系统存在于内存中,关机后数据会消失。
- 清理要清理的文件夹。因为原来的目录已成为tmpfs的挂载点,原来的目录已经不可访问,所以为了删除原来目录的文件需要将原来的目录再次挂载到一个新的挂载点。对应代码中的
${mntpnt_orig_tmp}
mount
tmpfs
->最初要清理的目录
mount
最初要清理的目录
->${mntpnt_orig_tmp}
有上面两次挂载,前者只发生一次,关机后tmpfs自动消失。后者每一个while循环发生一次,因为只要清理完数据,就可以umount了。目的不一样,后者是为了清理数据,前者为了不影响需要操作清理对象的进程
代码用了几个技巧:
du -sm
获取文件夹的大小,单位MB,统计对象不超过128MB,防止发生内存溢出。- 用变量
defifs
临时保存了IFS
。 tmp_path=${tmp_path%/*}
可以获取其父目录继续下一次迭代。mount
和mount --bind
,分设备和文件夹两种情况。
清理yum manager uuid
代码语言:javascript复制#!/usr/bin/env bash## Remove the yum package manager UUID associated with the guest## A new UUID will be automatically generated the next time yum is runset -o errexituuid="/var/lib/yum/uuid"[[ -e ${uuid} ]] && rm -f ${uuid}exit 0