Создайте подобный сайт на WordPress.com
Начало работы

Лента блога

SaltStack. Основы

SaltStack — система управления конфигурациями и удаленного выполнения операций. Для коммуникации Salt может использовать SSH или собственный протокол, работающий на базе ZeroMQ.

Для работы используются 2 TCP порта 4505 и 4506.

Для работы Salt не нужен SSH

Отличия и сходства с Ansible:

  • Ansible и Salt написаны на Python
  • Ansible работает только через SSH, Salt может работать через SSH или собственный протокол
  • В классической модели работы Salt использует на каждом хосте агенты
  • Асинхронная связь сервера с клиентами (из-за этого параметра Salt работает быстрее чем Ansible)

Ключевые понятия и элементы:

  • Salt master — процесс, который управляет агентами (миньонами)
  • Salt minion — процесс, работающий на управляемых машинах.
  • Salt state (SLS) — конфигурационные файлы, написанные на YAML (аналог Playbook в Ansible)
  • Formulas — множества вызовов к модулям состояний (state modules), организованных таким образом, чтобы достичь определённого результата; они описывают как должна выглядеть система в том случае, когда формула применена. Формулы это заранее приготовленные состояния (salt states).
  • Templates ( шаблоны)
  • Pillars — защищённые хранилища важной информации, собственно salt state’ов.
  • Grains — факты о системах (ОС, ip-адреса, модель процессора и т.д.)
  • Mine – хранилище результатов исполнения команд на миньонах на мастер-сервере. С помощью Mine нельзя откатиться обратно.

Источник — http://xgu.ru/wiki/SaltStack

Установка и настройка

В документации можно посмотреть, как правильно установить Salt Master и Salt Minion.

Для удобства на моём GitHub можно забрать готовый стенд с одним мастером и двумя миньонами. Настроены базово.

Примеры работы команд:

  • Проверка работы Salt на хостах:
salt '*' cmd.run "uname -a"
  • Запуск команды «uname -a» на хосте:
salt minion1 cmd.run "uname -a"
  • Просмотр информации о хосте minion1:
salt minion1 grains.items
  • Пробный запуск (без внесения изменений) файла (/srv/salt/nginx/init.sls) состояния:
sudo salt minion1 state.apply nginx test=True
  • Если проблем не обнаружено, то можно запустить state:
sudo salt minion1 state.apply nginx 

Пример установки nginx на хосте с помощью Salt:

Далее все действия выполняются от root

Создадим каталог nginx /srv/salt/nginx

mkdir -p /srv/salt/nginx

Далее создадим файл /srv/salt/nginx/init.sls со следующим содержимым:

vim /srv/salt/nginx/init.sls

#Установка nginx
nginx:              
  pkg:           
    - installed    

#Запуск службы
  service:            
    - running     

#Отслеживание изменений, при которых будет перезапуск службы
    - watch:      
      - pkg: nginx    
      - file: /etc/nginx/nginx.conf    

#Отправка файла /etc/nginx/nginx.conf на удаленный хост
/etc/nginx/nginx.conf:
  file:
    - managed
    - source: salt://nginx/nginx.conf #путь к файлу на мастер-хосте
    - user: root
    - group: root
    - mode: 644

Создадим файл конфигурации nginx.conf который будет отправлен на удаленный хост:

vim /srv/salt/nginx/nginx.conf

user www-data;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;

events {
        worker_connections 512;
}

http {

        sendfile on;
        tcp_nopush on;
        tcp_nodelay on;
        keepalive_timeout 65;
        types_hash_max_size 2048;

        include /etc/nginx/mime.types;
        default_type application/octet-stream;

        ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; # Dropping SSLv3, ref: POODLE
        ssl_prefer_server_ciphers on;

        access_log /var/log/nginx/access.log;
        error_log /var/log/nginx/error.log;

        gzip on;

        include /etc/nginx/conf.d/*.conf;
        include /etc/nginx/sites-enabled/*;
}

Далее создадим файл top.sls который будет запускать файл init.sls из каталога nginx:

vim /srv/salt/top.sls

base:
  'minion1':      #'*' - установка на всех хостах
    - nginx

Проверим как отработает тестовый запуск:

root@master:/srv/salt# salt '*' state.highstate test=True

minion2:
----------
          ID: states
    Function: no.None
      Result: False
     Comment: No Top file or master_tops data matches found. Please see master log for details.
     Changes:   

Summary for minion2
------------
Succeeded: 0
Failed:    1
------------
Total states run:     1
Total run time:   0.000 ms
minion1:
----------
          ID: nginx
    Function: pkg.installed
      Result: None
     Comment: The following packages would be installed/updated: nginx
     Started: 15:18:32.250134
    Duration: 164.761 ms
     Changes:   
              ----------
              installed:
                  ----------
                  nginx:
                      ----------
                      new:
                          installed
                      old:
----------
          ID: /etc/nginx/nginx.conf
    Function: file.managed
      Result: None
     Comment: The file /etc/nginx/nginx.conf is set to be changed
              Note: No changes made, actual changes may
              be different due to other states.
     Started: 15:18:32.428594
    Duration: 36.336 ms
     Changes:   
              ----------
              newfile:
                  /etc/nginx/nginx.conf
----------
          ID: nginx
    Function: service.running
      Result: None
     Comment: Service is set to be started
     Started: 15:18:32.491839
    Duration: 15.134 ms
     Changes:   

Summary for minion1
------------
Succeeded: 3 (unchanged=3, changed=2)
Failed:    0
------------
Total states run:     3
Total run time: 216.231 ms
ERROR: Minions returned with non-zero exit code
root@master:/srv/salt# 

Сначала мы увидим ошибку, в ошибке будет написано, что по хосту minion2 не найдено изменений. Так и должно быть, по хосту minion1 проблем не найдено, можем запустить state:

salt '*' state.highstate

После установки, можно проверить, был ли установлен nginx, указав в адресной строке адрес сервер minion1:

Если у нас получается зайти на веб-интерфейс, значит установка прошла успешно.

Настройка SSH по ключу

Когда у нас настроен классический доступ по SSH, то от нас требуется знать логин, пароль, и номер SSH-порта (если он вдруг изменён. SSH порт по умолчанию TCP 22)

Если у Вас в распоряжении, есть 2-3 сервера, то проблем возникнуть не должно, но если у Вас много linux-машин, постоянно вводить пароль будет очень неудобно. Для того, чтобы упростить доступ, был придуман доступ по SSH-ключу.

Как работает авторизация пользователя по ключу?

На linux-машине генерируется 2 ключа: открытый и закрытый. Открытый ключ отправляется на другой сервер, к которому планируется настроить доступ без пароля. С помощью открытого ключа шифруются сообщения. Закрытый ключ нужен для того, чтобы расшифровать сообщения, зашифрованные открытым ключом. Если доступ к закрытому ключу будет получен злоумышленниками, то доступ будет скомпрометирован.

Настройка доступа по SSH-ключу между двумя linux-машинами

У нас есть два linux-сервера: Debian 10 (10.166.0.3) и CentOS 7 (10.166.0.4). Начнём настраивать доступ по ключу с сервера CentOS 7.

  1. По умолчанию ключи хранятся в скрытой папке пользователя. (/home/user/.ssh). Проверяем что на серверах данная папка есть:
#На CentOS 7 (10.166.0.4)
[tv1n94@c7-ssh ~]$ ls -la
total 12
drwx------. 3 tv1n94 tv1n94  74 Jan 10 14:29 .
drwxr-xr-x. 4 root   root    35 Jan 10 14:29 ..
-rw-r--r--. 1 tv1n94 tv1n94  18 Apr  1  2020 .bash_logout
-rw-r--r--. 1 tv1n94 tv1n94 193 Apr  1  2020 .bash_profile
-rw-r--r--. 1 tv1n94 tv1n94 231 Apr  1  2020 .bashrc
drwx------. 2 tv1n94 tv1n94   6 Jan 10 14:32 .ssh
[tv1n94@c7-ssh ~]$ 

#На Debian 10 (10.166.0.3)
tv1n94@d10-ssh:~$ ls -la
total 28
drwxr-xr-x 4 tv1n94 tv1n94 4096 Jan 10 14:50 .
drwxr-xr-x 4 root   root   4096 Jan 10 14:29 ..
-rw-r--r-- 1 tv1n94 tv1n94  220 Apr 18  2019 .bash_logout
-rw-r--r-- 1 tv1n94 tv1n94 3526 Apr 18  2019 .bashrc
drwx------ 3 tv1n94 tv1n94 4096 Jan 10 14:50 .gnupg
-rw-r--r-- 1 tv1n94 tv1n94  807 Apr 18  2019 .profile
drwx------ 2 tv1n94 tv1n94 4096 Jan 10 14:50 .ssh
tv1n94@d10-ssh:~$ 

2. На CentOS 7 выполняем генерацию открытого и закрытого ключа. Предварительно нужно проверить, что нет сгенерированных ключей. Если уже есть сгенерированные ключи, то заново генерировать не нужно.

[tv1n94@c7-ssh ~]$ ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/home/tv1n94/.ssh/id_rsa): 
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in /home/tv1n94/.ssh/id_rsa.
Your public key has been saved in /home/tv1n94/.ssh/id_rsa.pub.
The key fingerprint is:
SHA256:+LjfzdcTs8vGeGqliDfn1WZK/zQC0JNwwJr1SttRsCk tv1n94@c7-ssh
The key's randomart image is:
+---[RSA 2048]----+
|        .o.o.    |
|         o+ +.   |
|        +E.*.    |
|       + .oo.    |
|      . S +..    |
|       o o ..  +.|
|      . . . ..*+O|
|       . o * *=%o|
|      ... o B+=o*|
+----[SHA256]-----+
[tv1n94@c7-ssh ~]$ 

При генерации ключей будет запрошен путь к их расположению и пароль на использование ключа. Если вы всё планируете оставить без изменений (стандартный путь и ключ без пароля), то можно просто 3 раза нажать на Enter. Если вы введете пароль на использование ключа, то его нужно будет вводить при каждом подключении к другим серверам.

3. В папке .ssh у нас должно появится 2 ключа (id_rsa — закрытый и id_rsa.pub — открытый). Открытый ключ нужно отправить на сервер Debian 10 (10.166.0.3).

#Проверяем, что ключи были созданы
[tv1n94@c7-ssh ~]$ ls -la .ssh/
total 8
drwx------. 2 tv1n94 tv1n94   38 Jan 10 14:53 .
drwx------. 3 tv1n94 tv1n94   74 Jan 10 14:29 ..
-rw-------. 1 tv1n94 tv1n94 1679 Jan 10 14:53 id_rsa
-rw-r--r--. 1 tv1n94 tv1n94  395 Jan 10 14:53 id_rsa.pub
[tv1n94@c7-ssh ~]$ 

#Копируем открытый ключ на другой сервер
[tv1n94@c7-ssh ~]$ ssh-copy-id -i .ssh/id_rsa.pub tv1n94@10.166.0.3
/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: ".ssh/id_rsa.pub"
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
tv1n94@10.166.0.3's password: 

Number of key(s) added: 1

Now try logging into the machine, with:   "ssh 'tv1n94@10.166.0.3'"
and check to make sure that only the key(s) you wanted were added.

#Пытаемся подключиться
[tv1n94@c7-ssh ~]$ ssh 10.166.0.3
Linux d10-ssh 4.19.0-13-cloud-amd64 #1 SMP Debian 4.19.160-2 (2020-11-28) x86_64

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Sun Jan 10 14:50:57 2021 from 35.235.240.176
tv1n94@d10-ssh:~$ 
tv1n94@d10-ssh:~$ 

Если вдруг на linux-машине нет утилиты ssh-copy-id, то нам нужно просто перенести ключ id_rsa.pub на другой сервер в папку .ssh. Например, можно использовать утилиту scp.

На данном этапе мы настроили доступ по ключу с сервера CentOS 7 на сервер Debian 10. Давайте теперь настроим доступ по ключу с сервера Debian 10 на сервер CentOS 7:

#Генерируем ключи
tv1n94@d10-ssh:~$ ssh-keygen 
Generating public/private rsa key pair.
Enter file in which to save the key (/home/tv1n94/.ssh/id_rsa): 
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in /home/tv1n94/.ssh/id_rsa.
Your public key has been saved in /home/tv1n94/.ssh/id_rsa.pub.
The key fingerprint is:
SHA256:JQM6VM85NpcmV2yLPOeXwpiHmo7ltNl2cEbF0QdQ9qg tv1n94@d10-ssh
The key's randomart image is:
+---[RSA 2048]----+
|    ..o    .o+=+ |
|   . . + . oo.ooo|
|    o   @.*o o. o|
|     . . @+ +.   |
|        S  XE  . |
|          = B o  |
|        oo = o   |
|       =o+. .    |
|      ..=...     |
+----[SHA256]-----+

#Отправляем открытый ключ на сервер CentOS 7 
tv1n94@d10-ssh:~$ ssh-copy-id -i .ssh/id_rsa.pub tv1n94@10.166.0.4
/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: ".ssh/id_rsa.pub"
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
tv1n94@10.166.0.4's password: 

Number of key(s) added: 1

Now try logging into the machine, with:   "ssh 'tv1n94@10.166.0.4'"
and check to make sure that only the key(s) you wanted were added.

#Пробуем залогиниться
tv1n94@d10-ssh:~$ ssh 10.166.0.4
Last failed login: Sun Jan 10 15:17:19 UTC 2021 from d10-ssh.europe-north1-a.c.psychic-root-276212.internal on ssh:notty
There were 2 failed login attempts since the last successful login.
Last login: Sun Jan 10 14:29:22 2021 from 35.235.240.176
[tv1n94@c7-ssh ~]$ exit
logout
Connection to 10.166.0.4 closed.
tv1n94@d10-ssh:~$ 

На данном этапе настройка доступа по ssh-ключу завершена, теперь мы можем логиниться между серверами без ввода пароля.

Когда используется много серверов, сложно удержать в голове все ip-адреса, для того, чтобы можно было логиниться на сервер с помощью его доменного имени, нужно внести данный сервер в файл /etc/hosts

[root@c7-ssh tv1n94]# vi /etc/hosts
#Пример файла /etc/hosts
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
10.166.0.4 c7-ssh
10.166.0.3 d10-ssh

#Попробуем залогиниться по доменному имени
[tv1n94@c7-ssh ~]$ ssh d10-ssh
The authenticity of host 'd10-ssh (10.166.0.3)' can't be established.
ECDSA key fingerprint is SHA256:FOJ8Uot3DriaFAfXdFZwFL5CEXSNOEqEXh52O4F2OMY.
ECDSA key fingerprint is MD5:d2:5d:89:22:c7:5b:6d:fe:a7:c6:50:0f:a1:de:86:3f.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'd10-ssh' (ECDSA) to the list of known hosts.
Linux d10-ssh 4.19.0-13-cloud-amd64 #1 SMP Debian 4.19.160-2 (2020-11-28) x86_64

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Sun Jan 10 15:08:04 2021 from 10.166.0.4
tv1n94@d10-ssh:~$ 

mdraid: компоненты и управление

mdraid — linux-утилита для создания и управления программными RAID-массивами.

Преимущества программного RAID-массива:

  • Бесплатно (не надо тратиться на покупку аппаратного RAID-контроллера)
  • Одинаковый интерфейс управления на всех популярных linux
  • Гибкая настройка (можно перенести RAID в другой сервер, нет привязки к железу, производителю дисков и т д.)

Недостатки программного RAID-массива:

  • Отсутствие BBU (батарейки RAID-контроллера. Батарейка RAID-контроллера позволит перенести информацию с кэша на диски, если сервер выключится)
  • Отсутствие выделенного кэша (В аппаратных RAID-контроллерах есть специальный кэш, информация сначала пишется в него, и только потом переносится на диски, это может сильно ускорить работу)
  • Отсутствие службы поддержки
  • Производители некоторого ПО (например vmWare) не поддерживают работу на программном RAID

mdraid может работать не только с дисками, но и с разделами, томами LVM и т.д. Информация о RAID-массиве записывается в суперблок диска.

Рассмотрим на примере ОС CentOS как управлять RAID-массивами

Как создать RAID-массив?

Если вы только устанавливаете ОС на сервер, то RAID-массив можно создать прямо из установщика. Давайте на примере сделаем RAID1:

Переходим в раздел INSTALLATION DESTINATION
Перед нами откроется список дисков, с помощью клавиши ctrl выделяем оба диска и в разделе partitioning выбираем пункт I will configure partitioning и нажимаем Done

Если бы у нас был создан аппаратный RAID на уровне BIOS, то мы бы увидели просто один диск, на который сразу могли бы устанавливать ОС.

Перед нами откроется окно ручной разметки дисков, в левом нижнем углу можно посмотреть количество выделенных дисков (в нашем случае их должно быть 2). Нажимаем на раздел Click here to create them automatically
Тут проходим по каждому созданному разделу (/, /boot, /swap) и в Device Type указываем тип RAID, RAID Level — RAID1 и нажимаем Done
Применяем список настроек — Accept Changes

После успешной установки ОС мы увидим следующее состояние дисков и разделов:

[root@c7-raid ~]# lsblk
NAME      MAJ:MIN RM  SIZE RO TYPE  MOUNTPOINT
sda         8:0    0    8G  0 disk
├─sda1      8:1    0  6.2G  0 part
│ └─md125   9:125  0  6.2G  0 raid1 /
├─sda2      8:2    0  823M  0 part
│ └─md126   9:126  0  822M  0 raid1 [SWAP]
└─sda3      8:3    0    1G  0 part
  └─md127   9:127  0 1022M  0 raid1 /boot
sdb         8:16   0    8G  0 disk
├─sdb1      8:17   0  6.2G  0 part
│ └─md125   9:125  0  6.2G  0 raid1 /
├─sdb2      8:18   0  823M  0 part
│ └─md126   9:126  0  822M  0 raid1 [SWAP]
└─sdb3      8:19   0    1G  0 part
  └─md127   9:127  0 1022M  0 raid1 /boot
sr0        11:0    1 1024M  0 rom
[root@c7-raid ~]# cat /proc/mdstat
Personalities : [raid1]
md125 : active raid1 sda1[0] sdb1[1]
      6491136 blocks super 1.2 [2/2] [UU]
      bitmap: 0/1 pages [0KB], 65536KB chunk

md126 : active raid1 sdb2[1] sda2[0]
      841728 blocks super 1.2 [2/2] [UU]

md127 : active raid1 sda3[0] sdb3[1]
      1046528 blocks super 1.2 [2/2] [UU]
      bitmap: 0/1 pages [0KB], 65536KB chunk

unused devices: <none>
[root@c7-raid ~]#

#Исправный диск будет отображаться буквой U, неисправный прочерком (_). Сейчас мы #видим что под каждый раздел создалась отдельная RAID-группа.

Собирать все разделы в RAID необязательно, можно собрать лишь тот, который требуется, например /

Дальнейшее управление RAID-массивом будет происходить через утилиту mdadm:

Установка утилиты mdadm: yum install -y mdadm

В Debian\Ubuntu можно установить командой: sudo apt install -y mdadm

Просмотр всех созданных RAID-массивов: mdadm —examine —scan

[root@c7-raid ~]# mdadm --examine --scan
ARRAY /dev/md/root  metadata=1.2 UUID=f8eaae19:53973f7b:a79fe7da:e0493fea name=c7-raid:root
ARRAY /dev/md/swap  metadata=1.2 UUID=bd51902c:d3a004a1:dd7a3ec2:9b0fc8ae name=c7-raid:swap
ARRAY /dev/md/boot  metadata=1.2 UUID=30b96e90:1b5f1775:232bd954:b28f671f name=c7-raid:boot

#/dev/md/root - адрес символьной ссылки на массив
#metadata=1.2 - используемая версия суперблока
#UUID=f8eaae19:53973f7b:a79fe7da:e0493fea - UUID массива
#name=c7-raid:root - имя массива

Вывод подробной информации о RAID-массиве: mdadm -D /dev/md127

[root@c7-raid md]# mdadm -D /dev/md127
/dev/md127:
           Version : 1.2
     Creation Time : Sun Nov  8 16:10:55 2020
        Raid Level : raid1
        Array Size : 1046528 (1022.00 MiB 1071.64 MB)
     Used Dev Size : 1046528 (1022.00 MiB 1071.64 MB)
      Raid Devices : 2
     Total Devices : 2
       Persistence : Superblock is persistent

     Intent Bitmap : Internal

       Update Time : Sun Nov  8 16:52:55 2020
             State : clean
    Active Devices : 2
   Working Devices : 2
    Failed Devices : 0
     Spare Devices : 0

Consistency Policy : bitmap

              Name : c7-raid:boot  (local to host c7-raid)
              UUID : 30b96e90:1b5f1775:232bd954:b28f671f
            Events : 22

    Number   Major   Minor   RaidDevice State
       0       8        3        0      active sync   /dev/sda3
       1       8       19        1      active sync   /dev/sdb3

Что делать, если RAID-массив деградировал?

Допустим, мы заметили, что один диск у нас вышел из строя. Мы извлекли сбойный диск и установили вместо него новый (с таким же размером)

  1. Сначала проверяем состояние RAID-массива и виден ли новый диск:
[root@c7-raid ~]# lsblk
NAME      MAJ:MIN RM  SIZE RO TYPE  MOUNTPOINT
sda         8:0    0    8G  0 disk
├─sda1      8:1    0  6.2G  0 part
│ └─md126   9:126  0  6.2G  0 raid1 /
├─sda2      8:2    0  823M  0 part
│ └─md125   9:125  0  822M  0 raid1 [SWAP]
└─sda3      8:3    0    1G  0 part
  └─md127   9:127  0 1022M  0 raid1 /boot
sdb         8:16   0    8G  0 disk
sr0        11:0    1  906M  0 rom
[root@c7-raid ~]# cat /proc/mdstat
Personalities : [raid1]
md125 : active (auto-read-only) raid1 sda2[0]
      841728 blocks super 1.2 [2/1] [U_]

md126 : active raid1 sda1[0]
      6491136 blocks super 1.2 [2/1] [U_]
      bitmap: 1/1 pages [4KB], 65536KB chunk

md127 : active raid1 sda3[0]
      1046528 blocks super 1.2 [2/1] [U_]
      bitmap: 1/1 pages [4KB], 65536KB chunk

unused devices: <none>

#Видим что появился диск sdb, но на нём нет RAID-масива

Если сбойный диск ещё не извлечен и он числится в RAID-массиве, то необходимо сделать следующие действия:

  • Пометить диск как сбойный (для каждого raid-массива): mdadm /dev/md125 —set-faulty /dev/sdb
  • Удалить диск из каждого RAID-массива: mdadm /dev/md125 —remove /dev/sdb

2. Копируем на новый диск все разделы с рабочего диска (очень важно не перепутать диски местами, сначала идёт диск с которого копируем): sfdisk -d /dev/sda | sfdisk —force /dev/sdb

3. Проверяем, что были созданы идентичные разделы: lsblk

[root@c7-raid ~]# lsblk
NAME      MAJ:MIN RM  SIZE RO TYPE  MOUNTPOINT
sda         8:0    0    8G  0 disk
├─sda1      8:1    0  6.2G  0 part
│ └─md126   9:126  0  6.2G  0 raid1 /
├─sda2      8:2    0  823M  0 part
│ └─md125   9:125  0  822M  0 raid1 [SWAP]
└─sda3      8:3    0    1G  0 part
  └─md127   9:127  0 1022M  0 raid1 /boot
sdb         8:16   0    8G  0 disk
├─sdb1      8:17   0  6.2G  0 part
├─sdb2      8:18   0  823M  0 part
└─sdb3      8:19   0    1G  0 part
sr0        11:0    1  906M  0 rom

4. Добавляем диски в RAID-массив:

  • mdadm —manage /dev/md125 —add /dev/sdb2
  • mdadm —manage /dev/md126 —add /dev/sdb1
  • mdadm —manage /dev/md127 —add /dev/sdb3

5. После добавления диска сразу начнётся ребилд массива: cat /proc/mdstat

[root@c7-raid ~]# cat /proc/mdstat
Personalities : [raid1]
md125 : active raid1 sdb2[2] sda2[0]
      841728 blocks super 1.2 [2/2] [UU]

md126 : active raid1 sdb1[2] sda1[0]
      6491136 blocks super 1.2 [2/1] [U_]
      [==========>..........]  recovery = 54.2% (3524608/6491136) finish=0.4min speed=104103K/sec
      bitmap: 1/1 pages [4KB], 65536KB chunk

md127 : active raid1 sdb3[2] sda3[0]
      1046528 blocks super 1.2 [2/1] [U_]
        resync=DELAYED
      bitmap: 0/1 pages [0KB], 65536KB chunk

unused devices: <none>

После успешного ребилда RAID-массивы вернутся в обычное состояние:

[root@c7-raid ~]# cat /proc/mdstat
Personalities : [raid1]
md125 : active raid1 sdb2[2] sda2[0]
      841728 blocks super 1.2 [2/2] [UU]

md126 : active raid1 sdb1[2] sda1[0]
      6491136 blocks super 1.2 [2/2] [UU]
      bitmap: 1/1 pages [4KB], 65536KB chunk

md127 : active raid1 sdb3[2] sda3[0]
      1046528 blocks super 1.2 [2/2] [UU]
      bitmap: 0/1 pages [0KB], 65536KB chunk

unused devices: <none>

Если вдруг загрузчик находится у вас только на одном диске, то необходимо установить загрузчик на этот диск:

# grub-install /dev/sda
# update-grub

Создание RAID-массива без использования системных дисков из ОС

Допустим, в данный сервер мы установили ещё 3 диска одинаковых размеров и хотим собрать из них RAID5 и смонтировать его в ОС:

  1. Находим буквы дисков с одинаковым размером: lsblk | grep 20
[root@c7-raid ~]# lsblk | grep 20
sdb         8:16   0   20G  0 disk
sdd         8:48   0   20G  0 disk
sde         8:64   0   20G  0 disk

2. Добавляем в диски суперблок: mdadm —zero-superblock /dev/sd{b,d,e}

3. Создаём RAID5 из 3 дисков: mdadm —create /dev/md0 —level 5 -n3 /dev/sd{b,d,e}

4. Проверяем, что RAID создан успешно:

[root@c7-raid ~]# cat /proc/mdstat
Personalities : [raid1] [raid6] [raid5] [raid4]
md0 : active raid5 sde[3] sdd[1] sdb[0]
      41908224 blocks super 1.2 level 5, 512k chunk, algorithm 2 [3/3] [UUU]

5. Создаём том для LVM (Необязательно, делается для того, чтобы потом было проще увеличить размер): pvcreate /dev/md0

Удаление RAID-массива:

  1. Останавливаем RAID-массив: mdadm -S /dev/md0
  2. Очищаем суперблоки дисков: mdadm —zero-superblock /dev/sd{b,d,e}
  3. Удаляем метаданные и остальную информацию с дисков: wipefs —all —force /dev/sd{b,d,e}

Docker. Часть 2. Dockerfile и Docker-compose

Как мы уже знаем, Docker-контейнер разворачивается из Docker-образа, а настройки Docker-образа хранятся в Dockerfile.

Создание Docker-контейнера на основе Dockerfile

  1. Создаём отдельную папку и переходим в неё: mkdir docker && cd docker
  2. Создаём Dockerfile: vi Dockerfile
#Укажем базовый образ ОС (будет поиск по DockerHub)
FROM ubuntu

#Указываем свои учётные данные при создании образа
MAINTAINER ALEXEY <ваш адрес почты>

#Укажем команды (устанавливаем cowsay и добавляем её в быстрый запуск)
RUN apt-get update && apt-get install -y cowsay && ln -s /usr/games/cowsay /usr/bin/cowsay

#Команда cowsay будет автоматически запускаться при обращении к контейнеру
ENTRYPOINT ["cowsay"]

3. Создаём Docker Image на основе Dockerfile: docker build -t tv1n94/ubuntu-cows-file .

4. Отправляем наш Docker Image в реестр DockerHub: docker push tv1n94/ubuntu-cows-file

5. Запускаем Docker контейнер на основе образа с DockerHub: docker run tv1n94/ubuntu-cows cowsay «WHATSAPP, BRO!»

vagrant@d10:~/docker$  docker run tv1n94/ubuntu-cows cowsay "WHATSAPP, BRO!"
Unable to find image 'tv1n94/ubuntu-cows:latest' locally
latest: Pulling from tv1n94/ubuntu-cows
e6ca3592b144: Already exists
534a5505201d: Already exists
990916bd23bb: Already exists
e1948d9d9063: Already exists
Digest: sha256:730e0ff5d4c1f0f97dec0cf0acd39c2e7ed2c75dea6a0ad4d80e2d2890445944
Status: Downloaded newer image for tv1n94/ubuntu-cows:latest
 ________________
< WHATSAPP, BRO! >
 ----------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||
vagrant@d10:~/docker$

Соединение контейнеров между собой

Допустим у нас есть контейнер с базой данных и отдельный контейнер с СУБД управления. Давайте свяжем между собой 2 контейнера:

  1. Запускаем контейнер с БД (Через параметр -e можно указывать переменные контейнера. Укажем пароль root к MYSQL): docker run —name mariadb -e MYSQL_ROOT_PASSWORD=pass123456 -d mariadb

2. Запускаем контейнер Adminer и указываем, что БД находится в другом контейнере: docker run —link mariadb:db -p 1234:8080 -d adminer

Параметр —link отвечает за соединение с другим контейнером.

Для более удобного объединения контейнеров между собой используют Docker-compose.

Dokcer-compose — инструмент для создания и запуска многоконтейнерных приложений. В Compose контейнеры описываются специальным файлом docker-compose.yml (или .yaml)

Давайте создадим отдельную папку и добавим туда файл docker-compose.yml:

mkdir mydocker && cd mydocker

vi docker-compose.yml

# Use root/example as user/password credentials
#Версия файла. Нужно проверить, какая версия поддерживается докером
version: '3.1'


services:
  #имя контейнера
  db:
    #Образ из которого будет разворачиваться контейнер
    image: mariadb
    #Функция автоматическокго рестарта (no - никогда, on-failure - после
    #критической ошибки, always - рестарт в любом случае)
    restart: always
    #Дополнительные значения переменных (например пароль)
    environment:
      MYSQL_ROOT_PASSWORD: 123456
    #Указываем папку, смонтированную из ОС (папка в ОС:папка в контейнере)
    volumes:
       - ./db:/var/lib/mysql

  adminer:
    image: adminer
    restart: always
	#проброс порта (порт снаружи:порт внутри)
    ports:
      - 1234:8080

Параметр version отвечает за версию Docker-compose. С версией нужно быть внимательным, иногда в новой версии нет каких-то функций или добавляются новые. Проверить работу docker с версиями можно на официальном сайте docker, предварительно поняв, какая версия Docker установлена на вашем компьютере:

vagrant@d10-sysops:~/mydocker$ docker -v
Docker version 19.03.8, build afacb8b7f0
vagrant@d10-sysops:~/mydocker$ docker-compose -v
docker-compose version 1.25.5, build 8a1c60f6
vagrant@d10-sysops:~/mydocker$

В Docker-compose не должно быть TABов, можно использовать только пробелы.

Из Docker-compose можно ссылаться не только на образ, но и на Dockerfile, тогда вместо параметра IMAGE надо указать параметр BUILD. Одновременно нельзя использовать оба параметра для одного сервиса!

services:
  db:    
    build: ./db
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: 123456

В разделе build указана папка db, в ней должен быть файл docker-compose.yml. Файл описания может быть назван другим именем, но тогда в разделе build ещё нужно указать имя файла.

Тома Docker (volumes) — это папки операционной системы, которые смонтированы в Docker-контейнер. Могут быть смонтированы как отдельные файловые системы внутри контейнера. Если выключить контейнер, то данные в папке не пропадут.

Добавить volumes можно при запуске контейнера, например docker run -v /data ubuntu или в Dockerfile, указав параметр VOLUME /data

Основные команды docker-compose

Запуск образа на основе Docker-composedocker-compose up
Запуск образа Docker-compose в интерактивном режиме:docker-compose up -d
Просмотр запущенных контейнеровdocker-compose ps
Запрос адреса виртуальной машины на которой работает Docker (Работает только на Windows)docker-machine ip default
Останавливаем рабочий проектdocker-compose stop
Удаление образа dbdocker-copose rm db
Выполнение перестройки проекта (применение изменений)docker-copose build

UnionFS

Каждый образ состоит из нескольких слоёв. UnionFS объединяет все слои в контейнер. До запуска контейнера все слои находятся в режиме чтения.
При запуске контейнера появляется дополнительный слой чтение/запись, чтобы можно было вносить изменения. Если остановить контейнер, то слой чтение\запись удалится. Если нужно чтобы изменения сохранились, то необходимо сделать docker commit

Посмотреть историю изменения слоёв можно с помощью команды: docker history ubuntu-cows:latest

Самый простой способ уменьшить размер образа — это удалить ненужные слои(Сделать меньше шагов при формировании инструкций). Также если во время создания образа есть какие-то инструкции, который распаковывают архивы, то архив потом лучше удалять…

Docker. Часть 1. Основы основ

Все чаще я стал слышать про Docker, но пока не могу понять, где я смогу применять его в своей работе. Поэтому я решил разобраться с ним, чтобы улучшить свой скилл и написать ещё одно умное слово в своём резюме))

Попробую объяснить простыми словами то, что я понял про Docker:

Docker — маленькая виртуальная машина. Обычно цель данной машины поддерживать работу одного приложения или проекта…

Официальный сайт проекта — docker.com

Изучая Docker, надо знать немного терминов-компонентов:

  • Docker Engine — ПО Docker, установленное на сервер или компьютер
  • Docker Container — отдельный контейнер Docker. Работает независимо от других контейнеров. Создаётся из Docker Image, имеет свою директорию.
  • Docker Image — образ (шаблон), из которого можно развернуть Docker Container.
  • Docker Registers — место, где располагаются образы и откуда их можно скачать или залить туда собственный образ. Регистры бывают публичными и приватными. Публичный регистр — DockerHub (hub.docker.com).
  • Dockerfile — YAML-файл, который описывает Docker Image (конфиг образа).

Как примерно работает Docker и его контейнеры?

  1. Устанавливаем Docker Engine и создаём или скачиваем Dockerfile
  2. Запускаем Build Dockerfile и получаем из него Docker Image
  3. Выполняем Run Docker Image и получаем Docker Container

Установка и настройка Docker Engine на linux (Debian 10):

  1. Выполняем обновление репозиториев и ставим пакет apt-transport-https:
sudo apt update 
sudo apt install apt-transport-https

2. Устанавливаем gpg-ключ от репозитория download.docker.com

curl -fsSL https://download.docker.com/linux/debian/gpg | sudo apt-key add -

3. Указываем в параметрах репозитория, что планируем скачивать только стабильные версии

sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/debian $(lsb_release -cs) stable"

4. Далее, снова обновляем список пакетов и устанавливаем docker-ce. CE — community edition — бесплатная версия от Docker.

sudo apt update
sudo apt install docker-ce

5. Проверить, что docker установлен можно командами docker —v или systemctl status docker

vagrant@d10-sysops:~$ docker -v
Docker version 19.03.8, build afacb8b7f0
vagrant@d10-sysops:~$ systemctl status docker
● docker.service - Docker Application Container Engine
   Loaded: loaded (/lib/systemd/system/docker.service; enabled; vendor preset: enabled)
   Active: active (running) since Sun 2020-09-13 17:50:37 GMT; 1min 43s ago
     Docs: https://docs.docker.com
 Main PID: 336 (dockerd)
    Tasks: 13
   Memory: 137.7M
   CGroup: /system.slice/docker.service
           └─336 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
vagrant@d10-sysops:~$

6. На этом установка Docker Engine завершена. Нужно понимать, что Docker, почти на все свои команды требует права администратора, чтобы перед каждой командой не вводить sudo, можно добавить пользователя (с которого вы работаете) в группу Docker. Обратите внимание: пользователь получит права Администратора.

sudo usermod -aG docker $USER

Базовые команды Docker

Самая первая команда для новичков — это docker run hello-world

vagrant@d10-sysops:~$ docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
0e03bdcc26d7: Pull complete
Digest: sha256:4cf9c47f86df71d48364001ede3a4fcd85ae80ce02ebad74156906caff5378bc
Status: Downloaded newer image for hello-world:latest

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
    (amd64)
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
 https://hub.docker.com/

For more examples and ideas, visit:
 https://docs.docker.com/get-started/

vagrant@d10-sysops:~$

После ввода данной команды Docker начнёт искать Dockerfile с именем hello-world, если файл с таким именем будет не найден, то Docker начнём искать на DockerHub Docker Image с именем hello-world.

Просмотр всех Docker Images:docker images
Поиск на dockerhub образа с именем tomcat:docker search tomcat
Скачать образ tomcat:docker pull tomcat
Показать запущенные Docker-контейнерыdocker ps
Показать все Вocker-контейнерыdocker ps -a
Удалить Docker Imagedocker rmi <image name>
Удалить Docker Containerdocker rm <container id>
Запустить Docker Containerdocker start <container id>
Остановить Docker Containerdocker stop <container id>

Практический пример

Задача: развернуть веб-сервер nginx через docker, настроить, чтобы веб-страница открывалась на порту 1234, внести изменения на главную страницу, потом, на основе данного контейнера создать новый образ, удалить старый образ и удалить сам контейнер.

  1. Находим Docker Image c помощью команды (выведем только 4 самых популярных образа и шапку таблицы): docker search nginx | head -n 5
Первым в списке как раз будет официальный Docker Image от nginx

2. Скачаем Docker Image nginx к нам: docker pull nginx

Начнётся скачивание Docker Image nginx

3. Далее нам нужно запустить Docker контейнер. Предварительно пробросим порт, по которому будет доступна веб-страница, в нашем случае это порт 1234. Вводим команду для фонового запуска контейнера на основе образа: docker run -d -p 1234:80 nginx

Помимо запуска контейнера в фоновом режиме, его можно запустить в интерактивном (вместо параметра -d указываем параметр -it), тогда в терминале вы увидите много служебной информации, а при нажатии клавиш ctrl+c у вас остановится контейнер.

Для проверки можно ввести команду просмотра всех активны контейнеров: docker ps
Теперь самое время проверить, как работает Docker Container. Зайдём на веб-страницу и посмотрим результат. Отлично, nginx заработал.

4. Если потребуется, то мы можем зайти внутрь Docker контейнера и внести там изменения:

vagrant@d10-sysops:~$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                  NAMES
74350aa9180c        nginx               "/docker-entrypoint.…"   15 minutes ago      Up 15 minutes       0.0.0.0:1234->80/tcp   quirky_einstein
vagrant@d10-sysops:~$ docker exec -it 74350aa9180c /bin/bash
root@74350aa9180c:/#

5. Изменим содержимое веб-страницы внутри контейнера и перезапустим контейнер:

root@74350aa9180c:~# echo "TEST DOCKER CONTAINER" > /usr/share/nginx/html/index.html
root@74350aa9180c:~# exit
vagrant@d10-sysops:~$
vagrant@d10-sysops:~$ docker stop 74350aa9180c
vagrant@d10-sysops:~$ docker start 74350aa9180c
Проверяем, что страница поменялась.

6. Допустим мы внесли много изменений в контейнер и не хотим его заново настраивать. В таком случае можно просто сохранить изменения в отдельный Docker Image командой: docker commit <container id> <repositiry>:<tag>

vvagrant@d10-sysops:~$ docker commit 74350aa9180c testnginx:v1
ssha256:8d98f8491f4d7b56030f910f3992466f4e83b1ca0312c20c4847d4218e98c07a
vvagrant@d10-sysops:~$ docker images
RREPOSITORY              TAG                 IMAGE ID            CREATED             
ttestnginx               v1                  8d98f8491f4d        8 seconds ago       
nnginx                   latest              7e4d58f0e5f3        4 days ago          1

Теперь, на основе этого образа мы можем сделать ещё несколько независимых контейнеров, которые будут работать на других портах:

vagrant@d10-sysops:~$ docker run -d -p 1235:80 testnginx:v1
362e605e7f9e1ec00837825bdc5d3fe4a6f7f9e5a892283fa617c137ebcbc65b
vagrant@d10-sysops:~$ docker run -d -p 1236:80 testnginx:v1
fe2e1e5d054fd0f3de6cbf9f174c45853c10b9c5b172097ebb266641ddcc34de

На этом основы Dokcer заканчиваются, спасибо, за внимание.

Большую часть информации для этой статьи я взял из данного видео — https://www.youtube.com/watch?v=I18TNwZ2Nqg&t=1931s&ab_channel=ADV-IT

NFS: теория и практика

NFS (Network File System) — протокол, который позволяет пользователям совместно работать с файлами, расположенными на разных компьютерах.

У протокола NFS есть несколько версий:

  • NFSv2 — старая версия, работает только через UDP
  • NFSv3 — работает через TCP и UDP
  • NFSv4 — работает только через TCP

Рекомендуется использовать протокол NFS версии 3 и 4. Разные версии протокола не совместимы друг с другом, но серверы, поддерживающие протокол NFS обычно предоставляют сразу несколько версий протокола.

В NFS версии 2 и 3 нет контроля состояния файлов, после удачного монтирования клиенту выдаётся специальный ключ, который позволяет получить доступ содержимому каталога. Ключ сохраняется после перезагрузки

В NFSv4 нет ключей. Протокол сохраняет состояния файлов; и клиент и сервер сохраняют информацию об открытых файлах и блокировках.

Режимы аутентификации в NFS:

  • AUTH_NONE — без аутентификации
  • AUTH_SYS — работает в NFSv2 и v3, права на каталог ставятся на конкретный UID. При запросе доступа, сервер проверяет свой файл /etc/passwd на наличие UID и смотрит можно ли предоставить доступ.
  • RPCSEC_GSS — работает в NFSv4

Базовая настройка NFS сервера на CentOS 7

  1. Устанавливаем пакеты для работы NFS:
sudo yum install nfs-utils nfs-utils-lib

2. Запускаем службы и добавляем их в автозагрузку

systemctl start rpcbind
systemctl start nfs-server
systemctl start nfs-lock
systemctl start nfs-idmap
systemctl enable rpcbind
systemctl enable nfs-server
systemctl enable nfs-lock
systemctl enable nfs-idmap

3. Открываем порты в firewalld

sudo firewall-cmd --permanent --add-port=111/tcp
sudo firewall-cmd --permanent --add-port=54302/tcp
sudo firewall-cmd --permanent --add-port=20048/tcp
sudo firewall-cmd --permanent --add-port=2049/tcp
sudo firewall-cmd --permanent --add-port=46666/tcp
sudo firewall-cmd --permanent --add-port=42955/tcp
sudo firewall-cmd --permanent --add-port=875/tcp
sudo firewall-cmd reload

4. Создаём папку /nfs-share к которой будем предоставлять доступ

mkdir /nfs-share

5. Настройки доступа к NFS-серверу лежат в файле /etc/exports. Давайте настроим его: vi /etc/exports

Добавим в файл следующую строку:

/nfs-share 192.168.223.0/24(rw,sync,no_root_squash,no_all_squash)

Разберем параметры, указанные в этой строке:

  • /nfs-share — адрес папки, к которой будет предоставляться доступ
  • 192.168.223.0/24 — сеть. из которой будут приниматься запросы. Вместо адреса сети можно указать FQDN-клиента, @имя группы, имя пользователя.
  • rw — разрешение на чтение и запись (также можно указать ro — только чтение)
  • sync — синхронизация указанной директории (файлы не зависают в оперативной памяти, а сразу записываются на сервер)
  • no_root_squash — включение root-привилегий (По умолчанию, сервер NFS перехватывает входящие запросы, посланные от имени пользователя root (UID=0) и подменяет пользователя на nobody (UID=65534). Этот приём называется поражением в правах — squashing root. Если нужно, чтобы у root-пользователя были все права, то необходимо указать параметр no_root_squash)
  • no_all_squash — включение пользовательской авторизации

Дополнительные параметры можно найти на официальной MAN-страницы NFS — https://linux.die.net/man/5/nfs

6. После добавления информации в файл /etc/exports, необходимо назначить права на папку /nfs-share. Например: создадим пользователя bill и разрешим чтение и запись только для root и bill, остальным разрешим только чтение.

[root@centos ~]# useradd bill
[root@centos ~]# passwd bill
[root@centos ~]# passwd bill
Changing password for user bill.
New password:
Retype new password:
passwd: all authentication tokens updated successfully.
[root@centos ~]# cat /etc/passwd | grep bill
bill:x:1001:1001::/home/bill:/bin/bash
[root@centos ~]# cat /etc/group | grep bill
bill:x:1001:
[root@centos ~]# chmod 775 /nfs-share
[root@centos ~]# ls -l / | grep nfs-share
drwxrwxr-x    2 root   root      6 Aug 30 18:52 nfs-share
[root@centos ~]# chown root:bill /nfs-share
[root@centos ~]# ls -l / | grep nfs-share
drwxrwxr-x    2 root   bill      6 Aug 30 18:52 nfs-share
[root@centos ~]#

7. Перезапускаем службу NFS: service nfs-server restart

На этом настройка NFS-сервера закончена.

Настройка NFS-клиента:

  1. Устанавливаем пакеты NFS-клиента:
yum install nfs-utils nfs-utils-lib

2. Создаём папку /mnt/nfs в которую будем монтировать папку /nfs-share

[root@vtl ~]# mkdir /mnt/nfs
[root@vtl ~]# mount -t nfs 192.168.223.204:/nfs-share /mnt/nfs
[root@vtl ~]# ls -l /mnt/nfs
total 8
-rw-r--r--. 1 root root  7 Aug 30 19:29 test
-rw-r--r--. 1 root root 10 Aug 30 19:30 test2
[root@vtl ~]# cd /mnt/nfs
[root@vtl nfs]# rm test2
rm: remove regular file ‘test2’? y
[root@vtl nfs]#

Монтирование прошло успешно. Мы даже смогли удалить файл, так как разрешили пользователям root и bill читать и изменять файлы. Давайте создадим пользователя ivan и попробуем подключиться от его имени.

[ivan@vtl root]$ cd /mnt/nfs
[ivan@vtl nfs]$ ls
test
[ivan@vtl nfs]$ rm test
rm: remove write-protected regular file ‘test’? y
[ivan@vtl nfs]$ ls
[ivan@vtl nfs]$ cd ..
[ivan@vtl mnt]$ ls -l
total 0
drwxr-xr-x. 2 root root 6 Aug 10 09:51 arctape
drwxr-xr-x. 2 root root 6 Sep 26  2019 hgfs
drwxrwxr--. 2 root ivan 6 Aug 30 19:37 nfs
[ivan@vtl mnt]$ cat /etc/passwd | grep ivan
ivan:x:1001:1001::/home/ivan:/bin/bash
[ivan@vtl mnt]$

Как мы видим, от пользователя ivan также получилось удалить файл. Проблема заключается в том, что во время проверки доступа проверяется не имя пользователя а его UID. Давайте поменяем UID пользователя ivan и попробуем ещё раз что-нибудь удалить:

[root@vtl ~]# vi /etc/passwd
[root@vtl ~]# vi /etc/group
[root@vtl ~]# cat /etc/passwd | grep ivan
ivan:x:1011:1011::/home/ivan:/bin/bash
[root@vtl ~]# cat /etc/group | grep ivan
ivan:x:1011:
[root@vtl ~]#
[root@vtl nfs]# su ivan
bash: /home/ivan/.bashrc: Permission denied
bash-4.2$ ls
test3
bash-4.2$ rm test3
rm: remove write-protected regular file ‘test3’? y
rm: cannot remove ‘test3’: Permission denied
bash-4.2$

Монтирование с помощью команды mount будет работать до перезагрузки системы. Если требуется, чтобы папка монтировалась автоматически во время старта системы, то необходимо добавить строку в файл /etc/fstab: vi /etc/fstab

192.168.223.204:/nfs-share /mnt/nfs nfs rw,sync,hard,intr 0 0

После перезагрузки системы папка должна автоматически примонтироваться. Если не получается перезагрузить сервер, а проверить изменения нужно, то можно ввести команду: sudo mount -fav

Важно помнить при монтировании каталога используются только клиент и сервер, нельзя монтировать каталоги по очереди между тремя серверами.

Команда для размонтирования папки: umount /mnt/nfs

Как решить проблемы с размонтированием папки?

Самая частая ошибка при размонтировании — это device is busy. Я часто забываю выйти из каталога, который хочу отмонтировать и мне достаточно перейти в другой каталог командой cd.

Если проблемы более серьёзные, то можно попробовать следующие варианты:

1) Размонтировать с параметром force: umount -f /mnt/nfs

2) Если предыдущая команда не помогла, то находим процесс, которым занята папка, а потом убиваем его:

[root@vtl ~]# ps aux | grep /mnt/data
root      11251  0.0  0.0 112712   968 pts/0    S+   20:00   0:00 /mnt/data
[root@vtl ~]# kill 11251 #или вводим kill -9 11251
[root@vtl ~]# umount -f /mnt/nfs

3) Если и данный вариант не помог, значит NFS-сессия на самом деле повисла и поможет только перезагрузка сервера. Перезагрузка службы обычно не помогает.

Offline обновление CentOS

Обычно с обновлением linux, проблем не возникает: написал в командной строке yum update, подождал, перезагрузил и готово. Но бывают случаи, когда надо обновить до конкретной версии ядра или просто на серверах нет интернета. В такой момент очень помогает офлайн обновление.

Давайте попробуем на примере обновить CentOS версии 1611 до версии 1708:

  1. Проверяем текущую версию ОС
[root@test ~]# cat /etc/redhat-release
CentOS Linux release 7.3.1611 (Core)

2. Подключаем образ ОС с версией 1708 и монтируем его в папку /mnt/iso

[root@test ~]# mkdir /mnt/iso
[root@test ~]# lsscsi
[0:0:0:0]    disk    ATA      VBOX HARDDISK    1.0   /dev/sda
[0:0:1:0]    cd/dvd  VBOX     CD-ROM           1.0   /dev/sr0
[root@test ~]# mount -t iso9660 -o ro /dev/sr0 /mnt/iso
[root@test ~]# ls -l /mnt/iso

#В данном примере мы монтируем физический диск, вставленный в сервер. 
#Если на сервере просто лежит ISO-образ, то команда монтирования будет следующей
[root@test ~]# mount -o loop <iso_name> /mnt/iso

3. Проверка числового значения, находящегося в первой строке файла .discinfo

[root@test iso]# head -n1 /mnt/iso/.discinfo
1504618416.850667

4. Переходим в каталог /etc/yum.repos.d/ и временно переносим от туда все каталоги с текущими репозиториями в каталог /root/old_repo:

[root@test iso]# cd /etc/yum.repos.d/
[root@test yum.repos.d]# ls -la
total 40
drwxr-xr-x.  2 root root  187 Nov 15  2016 .
drwxr-xr-x. 78 root root 8192 Jul 30 10:59 ..
-rw-r--r--.  1 root root 1664 Nov 29  2016 CentOS-Base.repo
-rw-r--r--.  1 root root 1309 Nov 29  2016 CentOS-CR.repo
-rw-r--r--.  1 root root  649 Nov 29  2016 CentOS-Debuginfo.repo
-rw-r--r--.  1 root root  314 Nov 29  2016 CentOS-fasttrack.repo
-rw-r--r--.  1 root root  630 Nov 29  2016 CentOS-Media.repo
-rw-r--r--.  1 root root 1331 Nov 29  2016 CentOS-Sources.repo
-rw-r--r--.  1 root root 2893 Nov 29  2016 CentOS-Vault.repo
[root@test yum.repos.d]# 
[root@test yum.repos.d]# 
[root@test yum.repos.d]# mkdir /root/old_repo
[root@test yum.repos.d]# mv *.repo /root/old_repo/
[root@test yum.repos.d]# ls -la
total 12
drwxr-xr-x.  2 root root    6 Jul 30 11:19 .
drwxr-xr-x. 78 root root 8192 Jul 30 10:59 ..
[root@test yum.repos.d]#

5. Создаём файл с расширением .repo (iso.repo) vi iso.repo и указываем его параметры:

[offline_repo] 
#В media_id указываем значение, которое получили в 3 пункте
mediaid=1504618416.850667
#name должно совпадать с именем репозитория в квадратных скобках
name=offline_repo
#Указываем адрес до смонтированного каталога
baseurl=file:///mnt/iso/
#Указываем наш ключ
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
#Параметр показывает что к этому репозиторию можно обращаться
enabled=1 
#Включить проверку ключа
gpgcheck=1

6. Запускаем процесс обновления: yum update

7. После успешного обновления проверяем текущую версию ОС:

[root@test yum.repos.d]# cat /etc/redhat-release
CentOS Linux release 7.4.1708 (Core)

8. Отключаем диск с образом, перезагружаем ОС, возвращаем на место старые репозитории, удаляем файл с оффлайн репозиторием:

[root@test ~]# umount /mnt/iso
[root@test ~]# reboot

[vagrant@test ~]$ cat /etc/redhat-release
CentOS Linux release 7.4.1708 (Core)
[root@test ~]# rm /etc/yum.repos.d/iso.repo
rm: remove regular file ‘/etc/yum.repos.d/iso.repo’? y
[root@test ~]#

#После удаления файла с нашим репозиторием в каталоге должны остаться файлы с другими репозиториями. У меня они подтянулись с перезагрузкой
[root@test ~]# ls -l /etc/yum.repos.d/
total 28
-rw-r--r--. 1 root root 1664 Aug 30  2017 CentOS-Base.repo
-rw-r--r--. 1 root root 1309 Aug 30  2017 CentOS-CR.repo
-rw-r--r--. 1 root root  649 Aug 30  2017 CentOS-Debuginfo.repo
-rw-r--r--. 1 root root  314 Aug 30  2017 CentOS-fasttrack.repo
-rw-r--r--. 1 root root  630 Aug 30  2017 CentOS-Media.repo
-rw-r--r--. 1 root root 1331 Aug 30  2017 CentOS-Sources.repo
-rw-r--r--. 1 root root 3830 Aug 30  2017 CentOS-Vault.repo
[root@test ~]#

Если после перезагрузки файлы не вернулись обратно в каталог — просто перенестите их из каталога /root/old_repo следующей командой:

mv /root/old_repo/*.repo /etc/yum.repos.d/

Если просто ввести команду yum update, то ОС обновится просто до последней версии репозитория.

Изучаем keepalive

Keepalived — сервис, для настройки плавающего ip-адреса. В случае выхода из строя одного сервера, плавающий ip-адрес будет сразу переброшен на другой сервер.

Keepalive использует следующие протоколы:

  • VRRP разделяет устройства на Master и Backup, плавающий адрес хранится на всегда на Мастере.
  • BFD — протокол быстрого обнаружения неисправных линков. Сервера постоянно обмениваются между собой Hello-пакетами. Если пакет не приходит, то протокол отправляет информацию в VRRP и плавающий адрес перелетает на другой сервер. На практике, протокол BFD может определить неисправный линк менее чем за одну секунду.
  • IP Multicast

Настройка Keepalived на CentOS 7 (SELinux отключен):

Требования для настройки keepalived: сервера должны находиться в одном VLAN.

Есть 2 сервера: Node1 (192.168.223.222, Master) и Node2 (192.168.223.221, Slave). Настроим плавающий ip 192.158.223.223. В keepalived можно добавить и больше серверов, но все остальные должны иметь роли Backup.

  1. На node1 и node2 установим keepalived и добавим в автозагрузку, разрешим трафик keepalived в firewalld:
yum install keepalived
systemctl enable keepalived
firewall-cmd --add-rich-rule='rule protocol value="vrrp" accept' --permanent
firewall-cmd --reload

2. На node1 внесём изменения в файл /etc/keepalived/keepalived.conf

vi /etc/keepalived/keepalived.conf

! Configuration File for keepalived
global_defs {
   router_id uMASTER
}
#Указываем название задания (VI_1)
vrrp_instance VI_1 {
    #Указываем роль сервера
    state MASTER
    #Указываем имя интерфейса, к которому будет привязан VRRP инстанс
    interface ens33
    #Указываем id (1-255) роутера, на остальных серверах значение должно остаться таким же
    virtual_router_id 230
    #Ставим приоритет. У Master приоритет должен быть выше чем у Backup. 
    #Значение по умолчанию=100
    priority 102                       
    #Период проверки доступности сервера. Значение по умолчанию=1
    advert_int 1
    authentication {
        #Указываем тип аутентификации
        auth_type PASS
        #Указываем пароль для аутентификации
        auth_pass YOURPASSWORD          
    }


   virtual_ipaddress {
       #Указываем ip-адрес, имя устройства и метку устройства
       192.168.223.223/24 dev ens33 label ens33:0
    }
}

3.На node2 внесем изменения в файл /etc/keepalived/keepalived.conf

vi /etc/keepalived/keepalived.conf

! Configuration File for keepalived
global_defs {
   router_id uBACKUP
}

vrrp_instance VI_1 { #Имя задания должно совпадать
    state BACKUP 
    interface ens33
    virtual_router_id 230
    priority 101                       
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass YOURPASSWORD
    }


   virtual_ipaddress {
       192.168.223.223/24 dev ens33 label ens33:0
    }
}

4. На node1 и node2 перезапускаем службу командой service keepalived restart

5. Проверяем, что на Master сервере появился новый сетевой интерфейс ens33:0

Сетевой интерфейс не должен появится одновременно на двух серверах. Если такое случилось, то keepalived настроен не верно.

6. Теперь можно проверить работу самого keepalive. Поставим постоянный пинг до адреса 192.168.223.223 и немного подождав выключим node1. Мы увидим небольшой разрыв и далее удачный пинг:

При сбое node1 плавающий ip перелетит на node2. Если работа node1 восстановится, то keepalived переведет плавающий ip обратно на node1.

NTP-севера и клиенты

NTP-сервер — это сервер точного времени. К нему подключаются NTP-клиенты, чтобы синхронизировать своё время.

Перед тем, как изучать NTP, давайте немного рассмотрим теорию, как вообще работает время в Linux:

В ОС Linux есть два типа часов:

  • Системные часы — время операционной системы. Предоставляется ядром linux и реализуется простым подсчётом количества секунд от 0 часов 01.01.1970. NTP работает с системными часами! Посмотреть время системных часов можно командой: date
  • Аппаратные часы — часть материнской платы. Время, которое отображается в BIOS. Посмотреть время аппаратных часов: hwclock

При включении сервера, обычно ядро linux запрашивает время у аппаратных часов и отправляет его в системные часы.

Внутри самой ОС аппаратное время можно назначить системному и наоборот:

  • Назначить системное время аппаратному: hwclock —systohc
  • Назначить аппаратное время системному: hwclock —hctosys

Протокол NTP имеет иерархическую структуру, которая состоит из уровней:

  • Эталонные часы от которых начинается цепочка, обычно имеют уровень 0. К часам нельзя подключиться по ip, поэтому уровня 0 в NTP не существует.
  • К данным часам подключается сервер, у которого есть внешний адрес, и, иногда доменное имя. У сервера будет уровень 1.
  • Сервер который мы настраиваем у себя в локальной сети будет являться клиентом сервера с уровнем 1 и иметь уровень 2.
  • И т.д.

Если сервер уровня 1 перестаёт работать, то время нам будет отдавать сервер уровня 2. Максимальное количество уровней — 256.

Настройка NTP-сервера на CentOS 7

Предварительные условия: установлены обновления, отключен SElinux, работает firewalld.

  1. Установим ntpd демон: yum install -y ntp
  2. Очень важно чтобы на сервере работал только один NTP-демон. Остановим и уберём из автозагрузки демон chronyd:
systemctl stop chrony
systemctl disable chrony

3. Разрешим трафик NTP в firewalld:

firewall-cmd --zone=public --add-service=ntp --permanent
firewall-cmd --zone=public --add-port=123/udp --permanent
firewall-cmd --reload

#Проверим что порт 123 открыт
netstat -tulpn | grep 123
udp        0      0 192.168.200.200:123      0.0.0.0:*                           14943/ntpd
udp        0      0 127.0.0.1:123           0.0.0.0:*                           14943/ntpd
udp        0      0 0.0.0.0:123             0.0.0.0:*                           14943/ntpd
udp6       0      0 ::1:123                 :::*                                14943/ntpd
udp6       0      0 :::123                  :::*                                14943/ntpd

4. Устанавливаем часовой пояс (Москва, +3):timedatectl set-timezone Europe/Moscow

5. Вносим изменения в файл /etc/ntp.conf

vi /etc/ntp.conf

#Указываем файл, в котором будет храниться частота смещения времени
driftfile /var/lib/ntp/drift

#Устанавливаем запрет на подключение к серверу из вне
restrict default nomodify notrap nopeer noquery

#Разрешаем доступ с localhost
restrict 127.0.0.1
restrict ::1

#Разрешаем запрашивать время клиентов из сети 192.168.200.0/24
restrict 10.29.7.0 mask 255.255.255.0 nomodify notrap

# Use public servers from the pool.ntp.org project.
# Please consider joining the pool (http://www.pool.ntp.org/join.html).
server ntp1.stratum2.ru iburst
server ntp2.stratum2.ru iburst
server ntp3.stratum2.ru iburst
server ntp4.stratum2.ru iburst
#Параметры серверов: 
# iburst - отправка сразу нескольких пакетов к серверу
# true - отмечаем сервер как правильный

#Защита NTP-сервера от уязвимости CVE-2013-5211
disable monitor

#Указываем файл в который будут отправляться логи.
logfile /var/log/ntp.log

#Остальные параметры можно оставить как есть или удалить. 

6. Запускаем службу ntpd и добавляем её в автозагрузку:

systemctl start ntpd
systemctl enable ntpd

7. Проверяем, как сервер синхронизировался с внешним сервером:

ntpq -p 
     remote           refid      st t when poll reach   delay   offset  jitter
==============================================================================
*unknown230-n1.h 88.147.254.229   2 u  242 1024  377   21.715   -0.402   0.202
+n1.sigma.d6.hsd 88.147.254.229   2 u 1020 1024  377   21.398    0.173   0.473
+ns1.hsdn.org    88.147.254.227   2 u  892 1024  377   21.658   -0.463   0.571

Столбцы означают следующую информацию:

  • remote — имя хоста или адрес NTP-сервера
  • refid — адрес вышестоящего сервера
  • st — уровень (stratum) сервера
  • t — тип клиента (u-unicast, m-multicast)
  • when — прошло секунд от последней синхронизации
  • poll — период синхронизации в секундах
  • reach — состояние доступности сервера; после восьми успешных попыток синхронизации значение этого параметра становится равным 377
  • delay — задержка ответа сервера
  • ofset — разница во времени между серверами (- часы отстают, + часы спешат)
  • jitter — джиттер

Слева от перечисленных серверов располагаются символы, означающие состояние NTP-сервера:

  • * пригоден для обновления
  • + рекомендован для обновления
  • не рекомендуется синхронизироваться
  • x сервер недоступен

Настройка NTP-клиента:

  1. Устанавливаем ntp: yum install -y ntp
  2. Остановим и уберём из автозагрузки демон chronyd:
systemctl stop chrony
systemctl disable chrony

3. Разрешим трафик NTP в firewalld:

firewall-cmd --zone=public --add-service=ntp --permanent
firewall-cmd --zone=public --add-port=123/udp --permanent
firewall-cmd --reload

#Проверим что порт 123 открыт
netstat -tulpn | grep 123
udp        0      0 192.168.200.1:123       0.0.0.0:*                           14943/ntpd
udp        0      0 127.0.0.1:123           0.0.0.0:*                           14943/ntpd
udp        0      0 0.0.0.0:123             0.0.0.0:*                           14943/ntpd
udp6       0      0 ::1:123                 :::*                                14943/ntpd
udp6       0      0 :::123                  :::*                                14943/ntpd

4. Устанавливаем часовой пояс (Москва, +3):timedatectl set-timezone Europe/Moscow

5. Вносим изменения в файл /etc/ntp.conf

vi /etc/ntp.conf

#Указываем файл, в котором будет храниться частота смещения времени
driftfile /var/lib/ntp/drift

#Устанавливаем запрет на подключение к серверу из вне
restrict default nomodify notrap nopeer noquery

#Разрешаем доступ с localhost
restrict 127.0.0.1
restrict ::1

#Разрешаем запрашивать время клиентов из сети 192.168.200.0/24
restrict 10.29.7.0 mask 255.255.255.0 nomodify notrap

# Use public servers from the pool.ntp.org project.
# Please consider joining the pool (http://www.pool.ntp.org/join.html).
server 192.168.200.200 iburst true
server 192.168.200.201 iburst

#Параметры серверов: 
# iburst - отправка сразу нескольких пакетов к серверу
# true - отмечаем сервер как правильный
# true используют когда указано 2 сервера, чтобы не было конфликта при синхронизации

#Защита NTP-сервера от уязвимости CVE-2013-5211
disable monitor

#Указываем файл в который будут отправляться логи.
logfile /var/log/ntp.log

6. Запускаем службу ntpd и добавляем её в автозагрузку:

systemctl start ntpd
systemctl enable ntpd

7. Проверяем, как сервер синхронизировался с внешним сервером:

ntpq -p 
     remote           refid      st t when poll reach   delay   offset  jitter
==============================================================================
+192.168.200.200 88.147.254.235   3 u  248  512  377    0.495   -0.156   0.050
*192.168.200.201 88.147.254.237   3 u  247  512  377    0.297    0.119   0.067

На этом настройка NTP-сервера и клиента завершена.

Если вы планируете разворачивать NTP-сервера на виртуальной машине (например я использую vmWare ESXi), то стоит учитывать что гипервизор иногда синхронизирует время на гостевой машине со своими часами. Если при синхронизации будет большой разрыв, то демон перестанет работать, и в логах можно будет увидеть следующие ошибки:

  • При запросе состояния (service ntpd status):
    • ntp systemd[1]: ntpd.service: main process exited, code=exited, status=255/n/a
    • ntp systemd[1]: Unit ntpd.service entered failed state.
    • ntp systemd[1]: ntpd.service failed.
  • При просмотре логов (less /var/log/ntp.log):
    • 0.0.0.0 0617 07 panic_stop -2106 s; set clock manually within 1000 s

Одним из вариантов решения данной проблемы будет полное отключение синхронизации времени. Более подробно это рассказывается тут.

Если кратко, то помимо отключения снятия галочки time synchronization в настройках ВМ, необходимо выключить машину и дописать следующие параметры: VM Options — Advanced options — Configuration Parameters — Edit Configuration

NameValue
tools.syncTime0
time.synchronize.continue0
time.synchronize.restore0
time.synchronize.resume.disk0
time.synchronize.shrink0
time.synchronize.tools.startup0
time.synchronize.tools.enable0
time.synchronize.resume.host0

Vagrant. Описание и простые команды

Vagrant — ПО для создания и настройки виртуальной среды. Vagrant является надстройкой для VirtualBox, vmWare, Hyper-V и т.д.

Основные элементы Vagrant:

  • Boxes — сжатые образы виртуальных машин. Один образ можно использовать несколько раз (аналог iso- файла)
  • cli — консольные команды для управления средой
  • VagrantFile — файл с описанием конфигурации
  • Plugins — отдельные компоненты, которые упростят работу. Например можно поставить надстройку над vmWare и управлять машинами через Vagrant

Установка VirtualBox и Vagrant у меня не вызвала проблем, кроме той, что я ставил их рядом с vmWare, как получилось установить я рассказывал тут.

Попробуем развернуть простую виртуальную машину c CentOS 7. Для этого сначала проверим что vagrant работает корректно, а потом запросим VagrantFile

vagrant status
vagrant init centos/7

После инициализации centos/7 в текущий каталог загружается VagrantFile, который содержит настройки виртуальной машины. Для загрузки VagrantFile с популярными ОС надо вписать следующие команды:

  • Debian 10 Buster — vagrant init debian/buster64
  • CentOS 6 — vagrant init centos/6
  • CentOS 7 — vagrant init centos/7
  • CentOs 8 — vagrant init centos/8
  • Ubuntu 20.04 — vagrant init ubuntu/bionic64
  • Ubuntu 18.04 — vagrant init bento/Ubuntu-18.04
  • Ubuntu 16.04 — vagrant init ubuntu/xenial64

После того, как VagrantFile находится у нас в каталоге, необходимо запустить среду командой vagrant up. При первом запуске будет загружен Box c ОС. Далее эта команда будет просто включать ВМ.

При успешном старте в VirtualBox мы увидим новую включенную машину

Далее мы можем подключиться к виртуальной машине через SSH с помощью команды vagrant ssh. Подключение происходит по ssh-ключу логина и пароля нет

Подключившись по SSH, мы увидели версию CentOS 7 и базовые характеристики ВМ. Также виртуальной машиной можно управлять с помощью следующих команд:

  • vagrant halt — выключить ВМ
  • vagrant suspend — приостановить ВМ
  • vagrant reload — перезагрузить ВМ и применить конфигурацию из VagrantFile
  • vagrant destroy <имя машины> — удалить ВМ (Если ВМ одна можно писать просто vagrant destroy)
  • vagrant destroy -f — удалить все виртуальные машины

Если нас не устраивает базовая конфигурация виртуальной машины, то нам необходимо изменить VagrantFile следующим образом:

Vagrant.configure("2") do |config|
 
  #Указываем ОС и версию виртулаьной машины
  config.vm.box = "centos/7"
  
  #Запрещаем проверку обновлений с сервера
  config.vm.box_check_update = false
  
  #Пробрасываем Web-порт виртуальной машины на loacalhost:8080
  config.vm.network "forwarded_port", guest: 80, host: 8088

  #При запуске ВМ будет открываться VirtualBox
  # config.vm.provider "virtualbox" do |vb|
  #   vb.gui = true
  # end
   
   #Указываем имя, отображаемое в VirtualBox
    config.vm.provider "virtualbox" do |v|
     v.name = "C7-vagrant"
    end
   
   
  #Изменяем параметры виртуальной машины
   config.vm.provider "virtualbox" do |v|
    v.memory = 1024
    v.cpus = 2
   end
 
  #Выполнить следующий скрипт при старте вм
   config.vm.provision "shell", inline: <<-SHELL
     yum update
     yum install -y nginx
     setenforce 0
     service firewalld stop
   SHELL
   
end

После изменения VagrantFile необходимо проверить файл на наличие ошибок командой vagrant status, и, если нет ошибок перезапустить ВМ с применением настроек vagrant reload