Почему Centos minimal не minimal

 Posted on July 12, 2017 at 10:59 p.m.

Статья про оптимизацию минимального образа Centos 7 для загрузки в OpenStack

Как и обещал, начинаю серию статей про серверную оптимизацию. Для начала подготовим систему для тестов: установим ОС, MySQL-сервер, Nginx, Apache. Далее произведем базовую настройку, после чего будем проводить итерации: тестирование/оптимизация.

Приступим к подготовке ОС и сегодня будем урезать базовый дистрибутив Centos 7 устанавливаемый на instance servers.ru.

Содержание:

Intro

Первым делом осмотримся в системе и посмотрим что нам предлагают servers.ru в образе Centos 7:

# yum list installed | wc -l
357

# free -h
              total        used        free      shared  buff/cache   available
Mem:           992M         79M        551M         12M        361M        742M
Swap:            0B          0B          0B

# df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/vda1        29G  1.3G   26G   5% /
devtmpfs        477M     0  477M   0% /dev
tmpfs           497M     0  497M   0% /dev/shm
tmpfs           497M   13M  484M   3% /run
tmpfs           497M     0  497M   0% /sys/fs/cgroup
tmpfs           100M     0  100M   0% /run/user/1000

Итого имеем: 357 пакетов, 1.3Gb занято на диске, доступно 742Mb RAM. Ради эксперимента установил Centos 7 minimal на виртуальную машину и получил 305 пакетов. Взглянем на kickstart файл, который использовался для установки:

%packages
@core
chrony
cloud-init
cloud-utils-growpart
dracut-config-generic
dracut-norescue
firewalld
grub2
kernel
nfs-utils
rsync
tar
yum-utils
-NetworkManager
-aic94xx-firmware
-alsa-firmware
-alsa-lib
-alsa-tools-firmware
-biosdevname
-iprutils
-ivtv-firmware
-iwl100-firmware
-iwl1000-firmware
-iwl105-firmware
-iwl135-firmware
-iwl2000-firmware
-iwl2030-firmware
-iwl3160-firmware
-iwl3945-firmware
-iwl4965-firmware
-iwl5000-firmware
-iwl5150-firmware
-iwl6000-firmware
-iwl6000g2a-firmware
-iwl6000g2b-firmware
-iwl6050-firmware
-iwl7260-firmware
-libertas-sd8686-firmware
-libertas-sd8787-firmware
-libertas-usb8388-firmware
-plymouth

Видимо образ уже оптимизировали, но в итоге получилось еще больше пакетов чем в минимальной установке. Попробуем немного облегчить систему. Можно было бы просто удалить ненужные пакеты, но это слишком легко, пойдем длинным путем - подготовим свой образ для загрузки в OpenStack.

Готовим свой образ для загрузки в OpenStack

Первое что нам понадобится - это образ NetInstall Centos 7 (скачиваем с официального сайта). Далее устанавливаем для своей системы пакеты: qemu, libvirtd, virt-install, virt-manager и virt-sysprep.

Подготавливаем файл kickstart на основе позаимствованного у servers.ru:

# System authorization information
auth --enableshadow --passalgo=sha512
# Reboot after installation
reboot
# Use network installation
url --url="http://mirror.yandex.ru/centos/7/os/x86_64/"
# Firewall configuration
firewall --enabled --service=ssh
firstboot --disable
ignoredisk --only-use=vda
# Keyboard layouts
keyboard --vckeymap=us --xlayouts='us'
# System language
lang en_US.UTF-8
repo --name "os" --baseurl="http://mirror.yandex.ru/centos/7/os/x86_64/" --cost=100
repo --name "updates" --baseurl="http://mirror.yandex.ru/centos/7/updates/x86_64/" --cost=100
repo --name "extras" --baseurl="http://mirror.yandex.ru/centos/7/extras/x86_64/" --cost=100
# Network information
network  --bootproto=dhcp
network  --hostname=localhost.localdomain
# Root password
rootpw --iscrypted notpasswd
selinux --enforcing
services --disabled="kdump" --enabled="network,sshd,rsyslog"
timezone UTC --isUtc
# Disk
bootloader --append="console=tty0" --location=mbr --timeout=1 --boot-drive=vda
# Clear the Master Boot Record
zerombr
# Partition clearing information
clearpart --all --initlabel
# Disk partitioning information
part / --fstype="xfs" --ondisk=vda --size=8191

%post --erroronfail

# workaround anaconda requirements
passwd -d root
passwd -l root

echo -n "Creating grub.conf for pvgrub"
rootuuid=$( awk '$2=="/" { print $1 };'  /etc/fstab )
mkdir /boot/grub
echo -e 'default=0\ntimeout=0\n\n' > /boot/grub/grub.conf
for kv in $( ls -1v /boot/vmlinuz* |grep -v rescue |sed s/.*vmlinuz-//  ); do
  echo "title CentOS Linux 7 ($kv)" >> /boot/grub/grub.conf
  echo -e "\troot (hd0)" >> /boot/grub/grub.conf
  echo -e "\tkernel /boot/vmlinuz-$kv ro root=$rootuuid console=hvc0 LANG=en_US.UTF-8" >> /boot/grub/grub.conf
  echo -e "\tinitrd /boot/initramfs-$kv.img" >> /boot/grub/grub.conf
  echo
done

#link grub.conf to menu.lst to work
echo -n "Linking menu.lst to old-style grub.conf for pv-grub"
ln -sf grub.conf /boot/grub/menu.lst
ln -sf /boot/grub/grub.conf /etc/grub.conf

# setup systemd to boot to the right runlevel
echo -n "Setting default runlevel to multiuser text mode"
rm -f /etc/systemd/system/default.target
ln -s /lib/systemd/system/multi-user.target /etc/systemd/system/default.target
echo .

# this is installed by default but we don't need it in virt
echo "Removing linux-firmware package."
yum -C -y remove linux-firmware

# Remove firewalld; it is required to be present for install/image building.
echo "Removing firewalld."
yum -C -y remove firewalld --setopt="clean_requirements_on_remove=1"

# remove avahi and networkmanager
echo "Removing avahi/zeroconf and NetworkManager"
yum -C -y remove avahi\* Network\*

echo -n "Getty fixes"
# although we want console output going to the serial console, we don't
# actually have the opportunity to login there. FIX.
# we don't really need to auto-spawn _any_ gettys.
sed -i '/^#NAutoVTs=.*/ a\
NAutoVTs=0' /etc/systemd/logind.conf

echo -n "Network fixes"
# initscripts don't like this file to be missing.
cat > /etc/sysconfig/network << EOF
NETWORKING=yes
NOZEROCONF=yes
EOF

# For cloud images, 'eth0' _is_ the predictable device name, since
# we don't want to be tied to specific virtual (!) hardware
rm -f /etc/udev/rules.d/70*
ln -s /dev/null /etc/udev/rules.d/80-net-name-slot.rules

# simple eth0 config, again not hard-coded to the build hardware
cat > /etc/sysconfig/network-scripts/ifcfg-eth0 << EOF
DEVICE="eth0"
BOOTPROTO="dhcp"
ONBOOT="yes"
TYPE="Ethernet"
USERCTL="yes"
PEERDNS="yes"
IPV6INIT="no"
PERSISTENT_DHCLIENT="1"
EOF

# simple eth1 config, again not hard-coded to the build hardware
cat > /etc/sysconfig/network-scripts/ifcfg-eth1 << EOF
DEVICE="eth1"
BOOTPROTO="dhcp"
ONBOOT="yes"
TYPE="Ethernet"
USERCTL="yes"
PEERDNS="yes"
IPV6INIT="no"
PERSISTENT_DHCLIENT="1"
EOF

# set virtual-guest as default profile for tuned
echo "virtual-guest" > /etc/tuned/active_profile

# generic localhost names
cat > /etc/hosts << EOF
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6

EOF
echo .

# Because memory is scarce resource in most cloud/virt environments,
# and because this impedes forensics, we are differing from the Fedora
# default of having /tmp on tmpfs.
echo "Disabling tmpfs for /tmp."
systemctl mask tmp.mount

cat <<EOL > /etc/sysconfig/kernel
# UPDATEDEFAULT specifies if new-kernel-pkg should make
# new kernels the default
UPDATEDEFAULT=yes

# DEFAULTKERNEL specifies the default kernel package type
DEFAULTKERNEL=kernel
EOL

# make sure firstboot doesn't start
echo "RUN_FIRSTBOOT=NO" > /etc/sysconfig/firstboot

echo "Cleaning old yum repodata."
yum clean all

echo "set instance type markers"
echo 'genclo' > /etc/yum/vars/infra

# chance dhcp client retry/timeouts
cat  >> /etc/dhcp/dhclient.conf << EOF

timeout 300;
retry 60;
EOF

# clean up installation logs"
rm -rf /var/log/yum.log
rm -rf /var/lib/yum/*
rm -rf /root/install.log
rm -rf /root/install.log.syslog
rm -rf /root/anaconda-ks.cfg
rm -rf /var/log/anaconda*
rm -rf /root/anac*

echo "Fixing SELinux contexts."
touch /var/log/cron
touch /var/log/boot.log
mkdir -p /var/cache/yum
/usr/sbin/fixfiles -R -a restore

# reorder console entries
sed -i 's/console=tty0/console=tty0 console=ttyS0,115200n8/' /boot/grub2/grub.cfg

#Servers.ru settings
sed -i 's/centos/cloud-user/' /etc/cloud/cloud.cfg
sed -i 's/lock_passwd: true/lock_passwd: false/' /etc/cloud/cloud.cfg

%end

%packages --excludedocs --nobase --instLangs=en
@core --nodefaults
cloud-init
cloud-utils-growpart
dracut-config-generic
dracut-norescue
firewalld
grub2
kernel
nfs-utils
rsync
tar
sudo
-NetworkManager
-aic94xx-firmware
-alsa-*
-biosdevname
-btrfs-progs*
-iprutils
-ivtv-firmware
-iwl*firmware
-libertas-sd8686-firmware
-libertas-sd8787-firmware
-libertas-usb8388-firmware
-plymouth
-kexec-tools
%end

Так как в облачных серверах servers.ru используются два сетевых интерфейса, нам нужно создать еще одну виртуальную сеть. Создаем файл dummynet.xml:

<network>
  <name>dummynet</name>
  <bridge name="virbr100" />
  <forward mode="route" />
  <ip address="10.10.120.1" netmask="255.255.255.0" />
</network>

Подключаем сеть:

# virsh net-create dummynet.xml

Для того чтобы сделать файл kickstart доступным по сети запустим Web-сервер в директории с этим файлом (очень часто использую этот метод в работе - удобно стягивать с серверов конфигурационные файлы):

# python -m SimpleHTTPServer 80

Запускаем создание образа:

# qemu-img create -f qcow2 /tmp/centos.qcow2 10G

# virt-install --virt-type kvm \
--name centos \
--ram 2048 \
--disk /tmp/centos.qcow2,format=qcow2 \
--network network=default \
--network network=dummynet \
--os-type=linux --os-variant=centos7.0 \
--location=/tmp/CentOS-7-x86_64-NetInstall-1611.iso \
--nographics \
--extra-args="ip=dhcp ks=http://192.168.122.1/centos7_min.cfg console=tty0 console=ttyS0,115200n8"

В примере:

  • 192.168.122.1 - наш ip в виртуальной сети
  • centos7_min.cfg - имя kickstart файла

Подготавливаем образ для загрузки:

# virt-sysprep -d centos
# virsh undefine centos

Готовый образ /tmp/centos.qcow2 загружаем через портал servers.ru и создаем новый instance с новым образом.

Итоговый результат

В итоге получаем:

# yum list installed | wc -l
300

# free -h
              total        used        free      shared  buff/cache   available
Mem:           992M         89M        725M        6.5M        177M        742M
Swap:            0B          0B          0B

# df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/vda1        30G  643M   30G   3% /
devtmpfs        484M     0  484M   0% /dev
tmpfs           497M     0  497M   0% /dev/shm
tmpfs           497M  6.6M  490M   2% /run
tmpfs           497M     0  497M   0% /sys/fs/cgroup
tmpfs           100M     0  100M   0% /run/user/1000
tmpfs           100M     0  100M   0% /run/user/0

Результат:

  • количество пакетов сократилось с 357 до 300;
  • потребление RAM осталось на том же уровне;
  • занятое место на диске уменьшилось с 1.3Gb до 643Mb.

P.S. Это только первая итерация в подготовке своего образа, возможно в процессе дальнейших исследований я вернусь к этой статье и переделаю некоторые моменты.

Если у вас есть замечания/предложения - пишите в комментариях, буду признателен.