Проектирование приложений под ОС UNIX

Исходные данные:  операционная система Debian bookworm11.

Для отчета по курсу требуется реализовать индивидуальное задание включающее разработку "драйвера" файловой системы, а также произвести настройку инфраструктуры для разработки включающей CI/CD и автотестирование. К отчету прикладывается: исходный код "драйвера", декларативное описание конфигурации инфраструктуры в ansible, список коммитов с датами в gitlab по "драйверу" и инфраструктуре.

Лекция 1. GitLab

1. Установить пакеты curl и sudo

2. Добавить репозитарий gitlab-ce через скрипт:

curl https://packages.gitlab.com/install/repositories/gitlab/gitlab-ce/script.deb.sh | sudo bash

3. Установить пакет gitlab-ce.

4. Внести правки в /etc/gitlab/gitlab.rb в соответствии с примером конфигурации /etc/gitlab/gitlab.rb:

external_url 'http://gitlab.develop.local'
web_server['external_users'] = ['www-data']
nginx['enable'] = false

5. Установить пакет веб-сервера nginx.

6. Настроить виртуальный сайт gitlab.develop.local в соответствии с примером конфигурации /etc/nginx/sites-available/gitlab:

upstream gitlab-workhorse {
  server unix://var/opt/gitlab/gitlab-workhorse/sockets/socket fail_timeout=0;
}
server {
  server_name gitlab.develop.local;
  server_tokens off;
  root /opt/gitlab/embedded/service/gitlab-rails/public;
  client_max_body_size 250m;
  access_log  /var/log/nginx/gitlab_access.log;
  error_log   /var/log/nginx/gitlab_error.log;
  location / {
    proxy_read_timeout      3600;
    proxy_connect_timeout   300;
    proxy_redirect          off;
    proxy_buffering off;
    proxy_set_header    Host                $http_host;
    proxy_set_header    X-Real-IP           $remote_addr;
    proxy_set_header    X-Forwarded-For     $proxy_add_x_forwarded_for;
    proxy_set_header    X-Forwarded-Proto   $scheme;
    proxy_http_version 1.1;
    proxy_pass http://gitlab-workhorse;
  }
  error_page 502 /502.html;
}

6. Выполнить начальную конфигурацию gitlab сервера:

gitlab-ctl reconfigure

7. Внести в файл /etc/hosts на виртуальной машине запись:

127.0.0.1 gitlab.develop.local

8. Внести в файл hosts на локальной машине, для операционной системы windows путь c:\windows\system32\drivers\etc\hosts в соответствии с примером конфигурации:

192.168.0.40 gitlab.develop.local

9. Произвести начальную настройку gitlab через браузер по адресу http://gitlab.develop.local
10. Создать дополнительного пользователя user и группу fstask по адресам:

http://gitlab.develop.local/admin/users/new

http://gitlab.develop.local/admin/groups/new

11. Добавить пользователя как Maintainer группы fstab

12. Зайти под пользователем user, создать проект с файлом README.md и названием test в группе fstask:

http://gitlab.develop.local/fstask/test

Лекция 2. Docker и GitLab-CI

1. Ознакомиться с инструкцией по установке gitlab-runner по адресу:

https://docs.gitlab.com/runner/install/linux-repository.html

2. Добавить apt репозитарии gitlab-runner через скрипт:

curl -L https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh | sudo bash

3. Установить пакет gitlab-runner

4. Добавить apt репозитарии для установки docker в файл /etc/apt/sources.list.d/docker.list:

deb https://download.docker.com/linux/debian bookworm stable

5. Добавить сигнатуры от репозитория docker:

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

6. Обновить дерево пакетов в системе:

apt update

7. Установить пакет docker-ce:

apt install docker-ce docker-ce-cli containerd.io

8. Зарегистрировать gitlab-runner. Предварительно нужно зайти под аккаунтом администратора по адресу http://gitlab.develop.local/admin/runners и узнать значение токена для регистрации, так же необходимо указать название тега и типа раннера ­ docker. Интерактивная команда запуска регистрации:

gitlab-runner register

9. Добавить пользователя gitlab-runner в группу docker:

gpasswd -a gitlab-runner docker

10. Удалить файл приводящий к проблемам при shell runner-ах:

rm /home/gitlab-runner/.bash_logout

11. Создать в проекте http://gitlab.develop.local/fstask файл .gitlab-ci.yml со следующим содержимым:

stages: - test
mytest: image: docker:stable stage: test tags: - docker script: - docker build -t test . - docker run test

12. Создать в проекте http://gitlab.develop.local/fstask файл Dockerfile:

FROM alpine:latest
RUN mkdir /app
COPY app.sh /app
RUN chmod +x /app/app.sh
CMD /app/app.sh

13. Создать в проекте http://gitlab.develop.local/fstask файл app.sh:

#!/bin/sh
echo Our first program
exit 0

14. Модифицировать файл /etc/gitlab-runner/config.toml в соответствии с примером конфигурации:

 [runners.docker] tls_verify = false image = "alpine:latest" privileged = false disable_entrypoint_overwrite = false oom_kill_disable = false disable_cache = false volumes = ["/var/run/docker.sock:/var/run/docker.sock", "/cache"] shm_size = 0 extra_hosts = ["gitlab.develop.local:192.168.0.40"] #your IP

15. Перезапустить сервис gitlab-runner:

systemctl restart gitlab-runner.service

16. После всех этапов необходимо перевыполнить последний pipeline по адресу http://gitlab.develop.local/fstask/test/pipelines, он должен иметь статус passed.

Лекция 3. Nexus Repository

Особенность заключается в том что в Debian Bookworm официально отсутствует java JRE/JDK 8-й версии, требуемый для корректного функционирования nexus.

Существует 2 варианта как можно это исправить:

  • Первый способ (более простой)

    Установить пакет nvidia-openjdk-8-jre: apt install nvidia-openjdk-8-jre

    Установить пакет ca-certificates-java: apt install ca-certificates-java

    Тогда корневой директорией с Java 8 будет /usr/lib/jvm/nvidia-java-8-openjdk-amd64

  • Второй способ

    Добавить репозиторий от версии Stretch (oldstable).

    1. Добавить в файл /etc/apt/apt.conf.d/99release запись:

    APT::Default-Release "stable";

    2. Добавить в /etc/apt/sources.list строку:

    deb http://security.debian.org/debian-security stretch/updates main

    3. Обновить репозитарий пакетов.:

    apt update

    4. Установить пакет с java 8-й версии:

    apt install openjdk-8-jre

Установка Nexus:

1. Скачать архив с дистрибутивом Nexus:

wget https://download.sonatype.com/nexus/3/latest-unix.tar.gz

2. Переместить и разархивировать пакет:

mv latest-unix.tar.gz /opt && cd /opt && tar xf latest-unix.tar.gz

3. Создать символическую ссылку с общем именем для упращения обновления:

ln -s nexus-3.42.0-01 nexus

4. Изменить файл nexus.vmoptions в каталоге nexus/bin, чтобы уменьшить требования к занимаемой оперативной памяти:

-Xms768m
-Xmx768m
-XX:MaxDirectMemorySize=768m

5. Создать Unit-файл /etc/systemd/system/nexus.service для автоматического запуска службы через systemd:

[Unit]
Description=nexus service
After=network.target
[Service]
Type=forking
LimitNOFILE=65536
ExecStart=/opt/nexus/bin/nexus start
ExecStop=/opt/nexus/bin/nexus stop
User=nexus
Restart=on-abort
[Install]
WantedBy=multi-user.target

6. Запустить Nexus первый раз в ручном режиме:

/opt/nexus/bin/nexus run

7. Обновить информацию о сервисах и включить nexus:

systemctl daemon-reload

systemctl enable nexus.service

8. Создать пользователя nexus с отключенным входом в систему:

useradd nexus -s /bin/false

9. Изменить права доступа на каталоги nexus sonatype-work nexus-3.42.0-01 в /opt:

chown -R nexus:nexus nexus sonatype-work nexus-3.42.0-01

10. Запустить службу nexus:

systemctl start nexus.service

11. Добавить виртуальный сайт в список nginx /etc/nginx/sites-available/nexus со следующим содержимым:

server { server_name nexus.develop.local; proxy_send_timeout 120; proxy_read_timeout 300; proxy_buffering off; keepalive_timeout 5 5; tcp_nodelay on; client_max_body_size 1G; location / { proxy_pass http://127.0.0.1:8081/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; }
}

12. Включить виртуальный сайт nexus:

cd /etc/nginx/sites-enabled

ln -s /etc/nginx/sites-available/nexus nexus

systemctl reload nginx

13. Добавить в файл /etc/hosts на сервере запись:

127.0.0.1 nexus.develop.local

14. Внести в файл hosts на локальной машине, для операционной системы windows путь c:\windows\system32\drivers\etc\hosts в соответствии с примером конфигурации:

192.168.0.40 nexus.develop.local

15. Зайти по адресу http://nexus.develop.local и авторизоваться пользователем admin с паролем из файла /opt/sonatype-work/nexus3/admin.password

16. Создать новый репозиторий с именем main и портом 18888 по адресу http://nexus.develop.local/#admin/repository/repositories:

17. Добавить новую роль main-access с привилегиями nx-repository-view-docker-main-* и пользователя user с паролем 12345678 привязанного к роли main-access.

18. Для проверки подключения к репозиторию, предварительно, необходимо создать файл /etc/docker/daemon.json, с опцией разрешающей доступ к репозиторию по протоколу HTTP:

{ "insecure-registries" : ["http://nexus.develop.local:18888"]
}

19. Перечитать конфигурационный файл службой docker'a:

systemctl reload docker

20. Проверить корректность авторизации пользователя в удаленном репозитории:

docker login -u user -p 12345678 nexus.develop.local:18888

rm /root/.docker/config.json

21. Модифицировать конфигурационный файл /etc/gitlab-runner/config.toml добавив информацию о новом псевдониме:

 extra_hosts = ["gitlab.develop.local:192.168.0.40", "nexus.develop.local:192.168.0.40"]

22. Перезапустить gitlab-runner:

systemctl restart gitlab-runner

23. Добавить переменные USER_LOGIN и USER_PASS в настройки проекта fstask/test (пример адреса http://gitlab.develop.local/fstask/test/-/settings/ci_cd)

24. Изменить gitlab-ci файл на следующее содержание:

stages: - build - test
variables: NEXUS_HOST: nexus.develop.local:18888 IMAGE_NAME: $NEXUS_HOST/$CI_PROJECT_NAME:$CI_COMMIT_REF_SLUG
before_script:
  - docker login -u $USER_LOGIN -p $USER_PASS $NEXUS_HOST
mybuild: image: docker:stable stage: build tags: - docker script: - docker build --pull -t $IMAGE_NAME . - docker push $IMAGE_NAME
mytest: image: docker:stable stage: test tags: - docker script: - docker pull $IMAGE_NAME - docker run $IMAGE_NAME

25. Проверить, что pipeline состоит из двух этапов и оба этапа завершены со статусом passed.

Лекция 4. Сборка основного проекта

1. Изменить роль пользователя user http://gitlab.develop.local/groups/fstask/-/group_members на Owner
2. Перенести CI/CD переменные USER_LOGIN и USER_PASS из проекта test в http://gitlab.develop.local/fstask
3. Создать базовый проект http://gitlab.develop.local/fstask/gcc-base
4. Создать Dockerfile включающий компилятор и систему сборки:

FROM alpine:latest
RUN apk update && apk add \ --virtual mybuild \ build-base \ cmake

5. Создать .gitlab-ci.yml описывающий этапы сборки и тегирования:

stages: - build - release
variables: NEXUS_HOST: nexus.develop.local:18888 IMAGE_NAME: $NEXUS_HOST/$CI_PROJECT_NAME:$CI_COMMIT_REF_SLUG IMAGE_RELEASE_NAME: $NEXUS_HOST/$CI_PROJECT_NAME:latest
buildgcc: image: docker:stable stage: build tags: - docker before_script: - docker login -u $USER_LOGIN -p $USER_PASS $NEXUS_HOST script: - docker build --pull -t $IMAGE_NAME . - docker push $IMAGE_NAME
release: image: docker:stable stage: release tags: - docker before_script: - docker login -u $USER_LOGIN -p $USER_PASS $NEXUS_HOST script: - docker pull $IMAGE_NAME - docker tag $IMAGE_NAME $IMAGE_RELEASE_NAME - docker push $IMAGE_RELEASE_NAME

6. Проверить, что pipeline проекта gcc-base состоит из двух этапов и оба этапа завершены со статусом passed.

7. Создать основной проект http://gitlab.develop.local/fstask/fatdriver
8. Добавить исходный код программы в main.cpp:

#include < iostream>using namespace std;
int main()
{ cout << "Hello WOrld" << endl; return 0;
}

9. Написать файл сборки для cmake - CMakeLists.txt, включающий запуск тестов:

cmake_minimum_required(VERSION 2.8)
add_executable(main main.cpp)
enable_testing()
add_test(NAME Test1 COMMAND "./main")

10. Создать Dockerfile основанный на образе gcc-base и добавить команды для сборки приложения и запуска тестов в контейнере:

FROM nexus.develop.local:18888/gcc-base:latest
RUN mkdir /app
COPY main.cpp CMakeLists.txt /app/
CMD cd /app && cmake . && make && make test

11. Добавить .gitlab-ci.yml в основной проект, включающий этап сборки и тестирования:
stages:

stages: - build - test
variables: NEXUS_HOST: nexus.develop.local:18888 IMAGE_NAME: $NEXUS_HOST/$CI_PROJECT_NAME:$CI_COMMIT_REF_SLUG
before_script: - docker login -u $USER_LOGIN -p $USER_PASS $NEXUS_HOST
mybuild: image: docker:stable stage: build tags: - docker before_script: - docker login -u $USER_LOGIN -p $USER_PASS $NEXUS_HOST script: - docker build --pull -t $IMAGE_NAME . - docker push $IMAGE_NAME
mytest: image: docker:stable stage: test tags: - docker script: - docker pull $IMAGE_NAME - docker run $IMAGE_NAME

12. Проверить, что pipeline проекта fatdriver состоит из двух этапов и оба этапа завершены со статусом passed.   

Лекция 5. Gitlab артифакты и ansible

Добавление триггера для пересборки fatdriver по сигналу от gcc-base:
1. Добавим запуск через trigger_job в .gitlab-ci.yml проекта gcc-base :

trigger_job: stage: child trigger: project: fstask/fatdriver

2. Изменим запуск тестов с make test на ctest, чтобы можно было сохранить протокол запуска в Dockerfile проекта fstask, полученный файл:

FROM nexus.develop.local:18888/gcc-base:latest
RUN mkdir /app
COPY main.cpp CMakeLists.txt /app/
CMD cd /app && cmake . && make && ctest -O log.txt .

3. Добавим в gitlab-ci файл указание на сохранение артифакта log.txt с временем хранения 1 неделя.
4. Добавим копирование файла log.txt в запущенный контейнер верхнего уровня, в результате получится следующий gitlab-ci файл:

stages: - build - test
variables: NEXUS_HOST: nexus.develop.local:18888 IMAGE_NAME: $NEXUS_HOST/$CI_PROJECT_NAME:$CI_COMMIT_REF_SLUG TEST_CONTAINER_NAME: testimage
mybuild: image: docker:stable stage: build tags: - docker before_script: - docker login -u $USER_LOGIN -p $USER_PASS $NEXUS_HOST script: - docker build --pull -t $IMAGE_NAME . - docker push $IMAGE_NAME
mytest: image: docker:stable stage: test tags: - docker before_script: - docker login -u $USER_LOGIN -p $USER_PASS $NEXUS_HOST script: - docker pull $IMAGE_NAME - docker rm -v $TEST_CONTAINER_NAME || echo - docker run --name $TEST_CONTAINER_NAME $IMAGE_NAME - docker cp $TEST_CONTAINER_NAME:/app/log.txt . artifacts: paths: - ./log.txt expire_in: 1 week

Подготовка системы автоматизации и декларативного описания администрирования - Ansible.

1. Установим менеджер пакетов python pip3:
apt install python3-pip
2. Установим пакет ansible (если возникает проблема, то добавьте параметр break-system-packages):
pip3 install --break-system-packages ansible
3. Для подключения к удаленному узлу создадим пару ключей ssh:
ssh-keygen
4. Добавим публичный ключ для удаленного подключения:
cat /home/user/.ssh/id_rsa.pub >> .ssh/authorized_keys
5. Создадим простой playbook для установки nginx, со следующей структурой: playbook/{roles, handlers, templates}, сам playbook nginx.yml разместим в каталоге playbook.
6. Добавим пользователя user в /etc/sudoers файл, для этого добавим строку:

user ALL=(ALL:ALL) ALL

7. Создадим inventory файл hosts.txt, где адрес 192.168.0.40 - IP адрес вашего хоста:

[test]
192.168.0.40

8. Создадим playbook/nginx.yml, добавим опцию become: true, для того чтобы поднять права пользователя до root через sudo:

- hosts: test become: true roles: - nginx

9. Добавим задачу в роли nginx playbook/nginx/tasks/main.yml, следующего содержания:

- name: Install nginx apt: name: nginx state: present update_cache: true

10. Первый раз запустим playbook без параметра -C, чтобы автоматически установился python-apt, на запрос пароля введем пароль пользователя(!) :
ansible-playbook -i hosts.txt playbook/nginx.yml -K -D
11. Добавим перезапуск сервиса nginx через handler, создав файл playbook/nginx/handlers/main.yml:

- name: reload nginx service: name: nginx state: reloaded

12. Скопируем старый файл конфигурации в шаблоны и внесем несколько корректировок:
cp /etc/nginx/nginx.conf playbook/roles/nginx/templates/nginx.conf.j2
13. Изменим файл playbook/nginx/tasks/main.yml роли следующим образом (добавим удаление сайта по умолчанию и создание конфига из шаблона):

- name: Install nginx apt: name: nginx state: present update_cache: true
- name: Remove default site file: path: /etc/nginx/sites-enabled/default state: absent notify: - reload nginx
- name: Create nginx.conf template: src: nginx.conf.j2 dest: /etc/nginx/nginx.conf mode: 0644 owner: root group: root notify: - reload nginx

14. Проверим изменения с параметром -C :
ansible-playbook -i hosts.txt playbook/nginx.yml -K -D -C
15. Применим изменения на сервере без -C :
ansible-playbook -i hosts.txt playbook/nginx.yml -K -D
16. Повторный перезапуск playbook'а должен показать, что изменения на сервере больше не вносились.

Лекция 6. Установка и обновление nexus через ansible

1. Создадим новый проект infrastructure в gitlab: http://gitlab.develop.local/projects/new

2. Добавим публичный ключ для авторизации gitlab от пользователя, по адресу http://gitlab.develop.local/profile/keys

3. Перейдем в каталог с playbook'ами с прошлой лекции и добавим ее в gitlab:

git init
git remote add origin git@gitlab.develop.local:user/infrastructure.git
git add .
git commit -m "Initial commit"
git push -u origin master

4. Разобъем структуру playbook'a по установке на 4 этапа: Подготовка, Установка или Обновление, Обновление прокси и Пост обработка.

5. Модифицируем файл hosts.txt из ~/testbook, где 192.168.0.40 ваш IP-адрес:

[nginx]
192.168.0.40
[nexus]
192.168.0.40

6. Создадим роль playbook/roles/nexus со следующей структурой: defaults, handlers, tasks, templates

7. Создадим файл с начальными установками defaults/main.yml:

nexus_install_dir: "/opt"
nexus_user: "nexus"
nexus_group: "{{ nexus_user }}"
nexus_upgrade: false
nexus_min_heap: "1200m"
nexus_max_heap: "{{ nexus_min_heap }}"
nexus_direct_memory: "{{ nexus_min_heap }}"
nexus_version_running: ""

8. Создадим обработчики handlers/main.yml, которые потребуются в ходе исполнения задач:

- name: nexus stop systemd: name: nexus.service state: stopped
- name: nexus start systemd: name: nexus.service state: started
- name: nexus restart systemd: name: nexus.service state: restarted
- name: nexus unit reload systemd: daemon-reload: yes name: nexus.service
- name: nexus wait port wait_for: port: 8081 timeout: 600

9. Скопируем шаблоны для юнита systemd и nginx из лекции 3:

cp /etc/systemd/system/nexus.service templates/nexus.service.j2

cp /etc/nginx/sites-available/nexus templates/nexus-vhost.conf.j2

10. Создадим файл tasks/main.yml с задачами разбитый на модули:

- name: Check requirements import_tasks: "nexus_requirements.yml"
- name: Install or upgrade import_tasks: "nexus_install.yml"
- name: Nexus vhost proxy import_tasks: "nexus_nginx.yml" tags: - nexus_proxy
- name: Nexus post install import_tasks: "nexus_post_install.yml"

11. Создадим модуль установки необходимых пакетов tasks/nexus_requirements.yml:

- name: Repo prepare 1 copy: content: "APT::Default-Release \"stable\";" dest: /etc/apt/apt.conf.d/99release
- name: Repo prepare 2 apt_repository: repo: deb http://security.debian.org/debian-security stretch/updates main state: present
- name: Install openjdk-8-jre apt: name: openjdk-8-jre state: present update_cache: yes

12. Создадим модуль установки и обновления nexus tasks/nexus_install.yml:

- name: Get latest nexus version uri: url: "https://download.sonatype.com/nexus/3/latest-unix.tar.gz" method: CONNECT status_code: 302 register: nexus_latest check_mode: no
- name: Extract version from remote url set_fact: nexus_latest_version: "{{ nexus_latest.location | regex_search(regexp, '\\1') |first }}" vars: regexp: '^https://.*nexus-(\d+\.\d+\.\d+-\d+)-unix.tar.gz'
- name: Check if nexus installed stat: path: "{{ nexus_install_dir }}/nexus" register: nexus_link
- name: Extract nexus current version set_fact: nexus_version_running: "{{ nexus_link.stat.lnk_target | regex_search(regexp, '\\1') | first }}" vars: regexp: '^.*nexus-(\d+\.\d+\.\d+-\d+)' when: - nexus_link.stat.exists | default(false) - nexus_link.stat.islnk | default(false)
- name: Check existing user user: name: "{{ nexus_user }}" group: "{{ nexus_group }}" shell: "/bin/false" state: present
- name: Create nexus service file template: src: nexus.service.j2 dest: /etc/systemd/system/nexus.service mode: 0644 owner: root group: root notify: - nexus unit reload
- name: Get latest nexus version get_url: url: "https://download.sonatype.com/nexus/3/latest-unix.tar.gz" dest: "{{ nexus_install_dir }}"
- name: Package name set_fact: nexus_package: "nexus-{{ nexus_latest_version }}-unix.tar.gz"
- name: First install or upgrade unarchive: src: "{{ nexus_install_dir }}/{{ nexus_package }}" dest: "{{ nexus_install_dir }}" creates: "{{ nexus_install_dir }}/nexus-{{ nexus_latest_version }}" copy: no when: ((not nexus_link.stat.exists) or (nexus_version_running != nexus_latest_version and nexus_upgrade)) and (not ansible_check_mode) #check previous installation and upgrade if nexus_upgrade = true notify: - nexus stop
- name: Flush or execute current handlers meta: flush_handlers
- name: Update symlinc to latest nexus version file: path: "{{ nexus_install_dir }}/nexus" src: "{{ nexus_install_dir }}/nexus-{{ nexus_latest_version }}" owner: "{{ nexus_user }}" group: "{{ nexus_group }}" state: link notify: - nexus restart - nexus wait port when: ((not nexus_link.stat.exists) or (nexus_version_running != nexus_latest_version and nexus_upgrade)) and (not ansible_check_mode) #check previous installation and upgrade if nexus_upgrade = true
- name: Nexus JVM min heap lineinfile: dest: "{{ nexus_install_dir }}/nexus/bin/nexus.vmoptions" regexp: "^-Xms.*" line: "-Xms{{ nexus_min_heap }}" notify: - nexus restart - nexus wait port when: not ansible_check_mode
- name: Nexus JVM max heap lineinfile: dest: "{{ nexus_install_dir }}/nexus/bin/nexus.vmoptions" regexp: "^-Xmx.*" line: "-Xmx{{ nexus_max_heap }}" notify: - nexus restart - nexus wait port when: not ansible_check_mode
- name: Nexus JVM direct memory lineinfile: dest: "{{ nexus_install_dir }}/nexus/bin/nexus.vmoptions" regexp: "^-XX:MaxDirectMemorySize=.*" line: "-XX:MaxDirectMemorySize={{ nexus_direct_memory }}" notify: - nexus restart - nexus wait port when: not ansible_check_mode
- name: Chown configuration and data files to nexus file: path: "{{ item }}" owner: "{{ nexus_user }}" group: "{{ nexus_group }}" recurse: yes with_items: - "{{ nexus_install_dir }}/nexus/" - "{{ nexus_install_dir }}/sonatype-work/"
- name: Flush or execute current handlers meta: flush_handlers

13. Создадим модуль настройки nginx proxy для nexus tasks/nexus_nginx.yml:

- name: Create nexus site config template: src: "nexus-vhost.conf.j2" dest: "/etc/nginx/sites-available/nexus" mode: 0644 owner: root group: root notify: - reload nginx
- name: Create nexus site link file: path: "/etc/nginx/sites-enabled/nexus" src: "/etc/nginx/sites-available/nexus" state: link notify: - reload nginx

14. Создадим модуль tasks/nexus_post_install.yml с выполнением шагов после установки, в данном конексте сработает только при первой установке и распечатает пароль администратора:

- name: Check if admin.password file exists stat: path: "{{ nexus_install_dir }}/sonatype-work/nexus3/admin.password" register: admin_password_file
- name: Save content admin.password to var set_fact: default_admin_password: "{{ lookup('file', '{{ nexus_install_dir }}/sonatype-work/nexus3/admin.password') }}" when: admin_password_file.stat.exists
- name: Display admin password to console debug: "msg={{ default_admin_password }}" when: admin_password_file.stat.exists

16. Выполним плейбук сначала с параметром Check:

ansible-playbook -i hosts.txt playbook/infra.yml -K -D -C

17. Выполним обновление nexus:

ansible-playbook -i hosts.txt playbook/infra.yml -K -D -e nexus_upgrade=true

18. Повторно выполним playbook чтобы удостовериться, что больше никакие изменения вноситься не будут.

Лекция 7. Установка и обновление gitlab через ansible

1. Модифицируем файл hosts.txt из ~/testbook, где 192.168.0.40 ваш IP-адрес:

[nginx]
192.168.0.40
[nexus]
192.168.0.40
[gitlab]
192.168.0.40

2. Создадим роль playbook/roles/gitlab со следующей структурой: defaults, handlers, tasks, templates

3. Создадим файл с начальными установками defaults/main.yml:

gitlab_server_name: 'gitlab.develop.local'
gitlab_full_url: 'http://{{ gitlab_server_name }}'
gitlab_web_user: 'www-data'
gitlab_nginx_enabled: 'false'
gitlab_upgrade: false

4. Создадим обработчики handlers/main.yml, которые потребуются в ходе исполнения задач:

- name: reconfigure gitlab
  command: gitlab-ctl reconfigure

5. Создадим основной файл с задачами handlers/main.yml:

- name: Install prerequisites
  apt:
    name:
      - curl
      - sudo
    state: present
- name: Add gitlab repo
  apt_repository:
    repo: deb https://packages.gitlab.com/gitlab/gitlab-ce/debian/ bookworm main
    state: present
    filename: gitlab_gitlab-ce
- name: Add Gitlab key
  apt_key:
    url: https://packages.gitlab.com/gpg.key
    state: present
- name: Gather packages list
  package_facts:
    manager: "auto"
- name: Install or upgrade Gitlab
  apt:
    name: gitlab-ce
    state: latest
    update_cache: true
  when: "'gitlab-ce' not in ansible_facts.packages or gitlab_upgrade"
- name: Create gitlab.rb config file
  template:
    src: "gitlab.rb.j2"
    dest: "/etc/gitlab/gitlab.rb"
    mode: 0600
    owner: root
    group: root
  notify:
    - reconfigure gitlab
- name: Create gitlab site config
  template:
    src: "gitlab-vhost.conf.j2"
    dest: "/etc/nginx/sites-available/gitlab"
    mode: 0644
    owner: root
    group: root
  notify:
    - reload nginx
- name: Create gitlab site link
  file:
    path: "/etc/nginx/sites-enabled/gitlab"
    src: "/etc/nginx/sites-available/gitlab"
    state: link
  notify:
    - reload nginx

6. Создадим шаблон для конфигурационного файла gitlab с именем templates/gitlab.rb.j2:

external_url '{{ gitlab_full_url }}'
web_server['external_users'] = ['{{ gitlab_web_user }}']
nginx['enable'] = {{ gitlab_nginx_enabled }}

7. Создадим шаблон для nginx proxy для gitlab с именем templates/gitlab-vhost.conf.j2:

upstream gitlab-workhorse { server unix:/var/opt/gitlab/gitlab-workhorse/sockets/socket;
}
server { server_name {{ gitlab_server_name }}; server_tokens off; ## Don't show the nginx version number, a security best practice root /opt/gitlab/embedded/service/gitlab-rails/public; access_log /var/log/nginx/gitlab_access.log; error_log /var/log/nginx/gitlab_error.log; location / { client_max_body_size 0; gzip off; proxy_read_timeout 300; proxy_connect_timeout 300; proxy_redirect off; proxy_http_version 1.1; proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_pass http://gitlab-workhorse; }
}

8. Добавим в infra playbook, выполнение роли gitlab:

- hosts: nginx become: true roles: - nginx
- hosts: nexus become: true roles: - nexus
- hosts: gitlab become: true roles: - gitlab

9. Выполним плейбук сначала с параметром Check:

ansible-playbook -i hosts.txt playbook/infra.yml -K -D -C

17. Выполним обновление gitlab:

ansible-playbook -i hosts.txt playbook/infra.yml -K -D -e gitlab_upgrade=true

18. Повторно выполним playbook чтобы удостовериться, что больше никакие изменения вноситься не будут.

Лекция 8. Установка gitlab-runner через ansible

1. Модифицируем файл hosts.txt из ~/testbook, где 192.168.0.40 ваш IP-адрес:

[nginx]
192.168.0.40

[nexus]
192.168.0.40

[gitlab]
192.168.0.40

[runner]
192.168.0.40

2. Создадим роль playbook/roles/gitlab-runner со следующей структурой: defaults, handlers, tasks, templates

3. Создадим файл с начальными установками defaults/main.yml:

runner_volumes:
  - "/var/run/docker.sock:/var/run/docker.sock"
  - "/cache"

runner_extra_hosts:
  - "gitlab.develop.local:192.168.0.40"
  - "nexus.develop.local:192.168.0.40"

4. Создадим обработчики handlers/main.yml, которые потребуются в ходе исполнения задач:

- name: restart gitlab-runner
  systemd:
    name: gitlab-runner.service
    state: restarted

5. Создадим основной файл с задачами handlers/main.yml:

- name: Add gitlab-runner repo
  apt_repository:
    repo: deb https://packages.gitlab.com/runner/gitlab-runner/debian/ bookworm main
    state: present
    filename: runner_gitlab-runner

- name: Add Gitlab key
  apt_key:
    url: https://packages.gitlab.com/gpg.key
    state: present

- name: Install or upgrade Gitlab
  apt:
    name:
    - gitlab-runner
    - python-gitlab
    state: present
    update_cache: yes

- name: Add gilab-runner user to docker group
  user:
    name: gitlab-runner
    groups: docker
    append: yes

- name: Remove obsolete .bash_logout file from gitlab-runner
  file:
    path: "/home/gitlab-runner/.bash_logout"
    state: absent

- name: Create gitlab-runner config file
  template:
    src: "config.toml.j2"
    dest: "/etc/gitlab-runner/config.toml"
    mode: 0600
    owner: root
    group: root
  notify:
    - restart gitlab-runner

- name: "Register shell runner"
  gitlab_runner:
    api_url: "{{ gitlab_full_url }}"
    api_token: "{{ gitlab_api_token }}"
    registration_token: "{{ shell_register_token }}"
    description: Test Shell runner
    state: present
    active: True
    access_level: not_protected
    tag_list: ['shell']
    run_untagged: False
    locked: True
  when: not ansible_check_mode

- name: "Docker runner"
  gitlab_runner:
    api_url: "{{ gitlab_full_url }}"
    api_token: "{{ gitlab_api_token }}"
    registration_token: "{{ docker_register_token }}"
    description: Docker runner
    state: present
    active: True
    access_level: not_protected
    tag_list: ['docker']
    run_untagged: False
    locked: True
  when: not ansible_check_mode

6. Создадим шаблон для конфигурационного файла gitlab-runner с именем templates/comfig.toml.j2:

concurrent = 1
check_interval = 0

[session_server]
  session_timeout = 1800

[[runners]]
  name = "Test Shell runner"
  url = "{{ gitlab_full_url | default('no host') }}"
  token = "{{ shell_register_token }}"
  executor = "shell"
  [runners.custom_build_dir]
  [runners.cache]
    [runners.cache.s3]
    [runners.cache.gcs]

[[runners]]
  name = "Docker runner"
  url = "{{ gitlab_full_url | default('no host')}}"
  token = "{{ docker_register_token }}"
  executor = "docker"
  [runners.custom_build_dir]
  [runners.docker]
    tls_verify = false
    image = "alpine:latest"
    privileged = false
    disable_entrypoint_overwrite = false
    oom_kill_disable = false
    disable_cache = false
    volumes = [{% for volume in runner_volumes %}"{{ volume }}"{{ ", " if not loop.last }}{% endfor %}]
    shm_size = 0
    extra_hosts = [{% for host in runner_extra_hosts %}"{{ host }}"{{ ", " if not loop.last }}{% endfor %}]
  [runners.cache]
    [runners.cache.s3]
    [runners.cache.gcs]

7. Создадим роль docker и задачу playbook/roles/docker/tasks/main.yml со следующим содержанием:

- name: Add Docker repo
  apt_repository:
    repo: deb https://download.docker.com/linux/debian bookworm stable
    state: present
    filename: docker

- name: Add Docker key
  apt_key:
    url: https://download.docker.com/linux/debian/gpg
    state: present

- name: Install or upgrade Gitlab
  apt:
    name:
      - docker-ce
      - docker-ce-cli
      - containerd.io
    state: present
    update_cache: true

8. Добавим в infra playbook, выполнение ролей docker и gitlab-runner:

- hosts: nginx
  become: true
  roles:
    - nginx

- hosts: nexus
  become: true
  roles:
    - nexus

- hosts: gitlab
  become: true
  roles:
    - gitlab

- hosts: runner
  become: true
  roles:
    - docker
    - gitlab-runner

9. Создадим файлы с дополнительными переменными в group_vars/{gitlab.yml, nexus.yml, tokens.yml}

10. gitlab.yml содержит следующие переменные:

gitlab_server_name: 'gitlab.develop.local'
gitlab_full_url: 'http://{{ gitlab_server_name }}/'

runner_extra_hosts:
  - "gitlab.develop.local:192.168.0.40"
  - "nexus.develop.local:192.168.0.40"

11. nexus.yml содержит следующие переменные:

nexus_hostname: "nexus.develop.local"

12. Создадим файл с защищенными переменными:

ansible-vault create group_vars/tokens.yml

13. Добавим следующие переменные:

shell_register_token: {{ YOUR VALUE }}
docker_register_token: {{ YOUR VALUE }}
gitlab_api_token: {{ YOUR VALUE }}

Значения первых двух переменных можно посмотреть в конфигурационном файле /etc/gitlab-runner/config.toml, либо в gitlab настройках, вкладка runners (под администратором).

Значение переменной gitlab_api_token, необходимо сгенерировать зайдя в gitlab под администратором(root) по адресу http://gitlab.develop.local/profile/personal_access_tokens, где необходимо сгенерировать токен с флагом "api".

14. Выполним проверку с параметром Check, где параметр vault-id запрашивает пароль у пользователя от хранилища, а -e считывает дополнительные переменные в данном случае зашифрлованные:

ansible-playbook -i hosts.txt playbook/infra.yml -K -D -C --vault-id @prompt -e @group_vars/tokens.yml

15. Выполним infra playbook:

ansible-playbook -i hosts.txt playbook/infra.yml -K -D --vault-id @prompt -e @group_vars/tokens.yml

16. Повторно выполним playbook чтобы удостовериться, что больше никакие изменения вноситься не будут.

Лекция 9. Тестирование playbook на новой системе

1. Установим "чистую" операционную систему Debian Bookworm в виртуальную машину VirtualBox или KVM. Запишите IP-адрес виртуальной машины и проверьте доступность подключения с управляющего хоста (там где выполняются плейбуки) по ssh. В случае если IP-адрес доступен на прямую на 22-м порту, нужно указать только его, если же ипользуется тип сети NAT, то нужно организовать проброс портов из виртуальной машины, на 127.0.0.1, например, на 2200.

2. Создадим новый файл hosts_demo.txt из ~/testbook, где 127.0.0.1 ваш IP-адрес, а ansible_port - порт гостевой ОС (при условии что порт не стандартный):

[all:vars]
ansible_port=2200
[nginx]
127.0.0.1
[nexus]
127.0.0.1
[gitlab]
127.0.0.1
[runner]
127.0.0.1

2. Изменим файл playbook/infra.yml следующим образом:

- hosts: nginx become: true roles: - base - nginx
- hosts: nexus become: true roles: - base - nginx - nexus
- hosts: gitlab become: true roles: - base - nginx - gitlab
- hosts: runner become: true roles: - base - docker - gitlab-runner

3. Добавим новую роль base, задачу запишем в файл playbook/roles/base/tasks/main.yml:

- name: Install host requirements apt: name: - python-apt state: present update_cache: yes check_mode: no

4. Изменим файл playbook/roles/docker/tasks/main.yml:

- name: Add Docker repo apt_repository: repo: deb https://download.docker.com/linux/debian bookworm stable state: present filename: docker update_cache: no
- name: Add Docker key apt_key: url: https://download.docker.com/linux/debian/gpg state: present
- name: Install docker apt: name: - docker-ce - docker-ce-cli - containerd.io state: present update_cache: true when: not ansible_check_mode

5. Изменим файл playbook/roles/gitlab-runner/defaults/main.yml:

runner_volumes: - "/var/run/docker.sock:/var/run/docker.sock" - "/cache"
runner_extra_hosts: - "gitlab.develop.local:192.168.0.40" - "nexus.develop.local:192.168.0.40"
register_runners: false

6. Изменим файл playbook/roles/gitlab-runner/tasks/main.yml:

- name: Add gitlab-runner repo apt_repository: repo: deb https://packages.gitlab.com/runner/gitlab-runner/debian/ bookworm main state: present filename: runner_gitlab-runner update_cache: no
- name: Add Gitlab key apt_key: url: https://packages.gitlab.com/gpg.key state: present
- name: Install or upgrade Gitlab apt: name: - gitlab-runner - python-gitlab state: present update_cache: yes when: not ansible_check_mode
- name: Add gilab-runner user to docker group user: name: gitlab-runner groups: docker append: yes
- name: Remove obsolete .bash_logout file from gitlab-runner file: path: "/home/gitlab-runner/.bash_logout" state: absent
- name: Create gitlab-runner config file template: src: "config.toml.j2" dest: "/etc/gitlab-runner/config.toml" mode: 0600 owner: root group: root notify: - restart gitlab-runner
- name: "Register shell runner" gitlab_runner: api_url: "{{ gitlab_full_url }}" api_token: "{{ gitlab_api_token }}" registration_token: "{{ shell_register_token }}" description: Test Shell runner state: present active: True access_level: not_protected tag_list: ['shell'] run_untagged: False locked: True when: not ansible_check_mode and register_runners
- name: "Docker runner" gitlab_runner: api_url: "{{ gitlab_full_url }}" api_token: "{{ gitlab_api_token }}" registration_token: "{{ docker_register_token }}" description: Docker runner state: present active: True access_level: not_protected tag_list: ['docker'] run_untagged: False locked: True when: not ansible_check_mode and register_runners

7. Изменим файл playbook/roles/gitlab/tasks/main.yml:

- name: Install prerequisites apt: name: - curl - sudo state: present
- name: Add gitlab repo apt_repository: repo: deb https://packages.gitlab.com/gitlab/gitlab-ce/debian/ bookworm main state: present filename: gitlab_gitlab-ce update_cache: no
- name: Add Gitlab key apt_key: url: https://packages.gitlab.com/gpg.key state: present
- name: Gather packages list package_facts: manager: "auto"
- name: Install or upgrade Gitlab apt: name: gitlab-ce state: latest update_cache: true when: "('gitlab-ce' not in ansible_facts.packages or gitlab_upgrade) and not ansible_check_mode"
- name: Create gitlab.rb config file template: src: "gitlab.rb.j2" dest: "/etc/gitlab/gitlab.rb" mode: 0600 owner: root group: root notify: - reconfigure gitlab
- name: Flush or execute current handlers meta: flush_handlers
- name: Create gitlab site config template: src: "gitlab-vhost.conf.j2" dest: "/etc/nginx/sites-available/gitlab" mode: 0644 owner: root group: root notify: - reload nginx
- name: Create gitlab site link file: path: "/etc/nginx/sites-enabled/gitlab" src: "/etc/nginx/sites-available/gitlab" state: link notify: - reload nginx when: not ansible_check_mode
- name: Add or replace /etc/hosts gitlab record lineinfile: path: "/etc/hosts" line: "{{ ansible_default_ipv4.address }}\t{{ gitlab_server_name }}" state: present

8. Изменим файл playbook/roles/nexus/tasks/nexus_install.yml:

- name: Get latest nexus version uri: url: "https://download.sonatype.com/nexus/3/latest-unix.tar.gz" method: CONNECT status_code: 302 register: nexus_latest check_mode: no
- name: Extract version from remote url set_fact: nexus_latest_version: "{{ nexus_latest.location | regex_search(regexp, '\\1') |first }}" vars: regexp: '^https://.*nexus-(\d+\.\d+\.\d+-\d+)-unix.tar.gz'
- name: Check if nexus installed stat: path: "{{ nexus_install_dir }}/nexus" register: nexus_link
- name: Extract nexus current version set_fact: nexus_version_running: "{{ nexus_link.stat.lnk_target | regex_search(regexp, '\\1') | first }}" vars: regexp: '^.*nexus-(\d+\.\d+\.\d+-\d+)' when: - nexus_link.stat.exists | default(false) - nexus_link.stat.islnk | default(false)
- name: Check nexus group group: name: nexus state: present
- name: Check existing user user: name: "{{ nexus_user }}" group: "{{ nexus_group }}" shell: "/bin/false" state: present
- name: Create nexus service file template: src: nexus.service.j2 dest: /etc/systemd/system/nexus.service mode: 0644 owner: root group: root notify: - nexus unit reload
- name: nexus enable systemd unit systemd: daemon-reload: yes name: nexus.service enabled: yes
- name: Get latest nexus version get_url: url: "https://download.sonatype.com/nexus/3/latest-unix.tar.gz" dest: "{{ nexus_install_dir }}"
- name: Package name set_fact: nexus_package: "nexus-{{ nexus_latest_version }}-unix.tar.gz"
- name: First install or upgrade unarchive: src: "{{ nexus_install_dir }}/{{ nexus_package }}" dest: "{{ nexus_install_dir }}" creates: "{{ nexus_install_dir }}/nexus-{{ nexus_latest_version }}" copy: no when: ((not nexus_link.stat.exists) or (nexus_version_running != nexus_latest_version and nexus_upgrade)) and (not ansible_check_mode) #check previous installation and upgrade if nexus_upgrade = true notify: - nexus stop
- name: Flush or execute current handlers meta: flush_handlers
- name: Update symlinc to latest nexus version file: path: "{{ nexus_install_dir }}/nexus" src: "{{ nexus_install_dir }}/nexus-{{ nexus_latest_version }}" owner: "{{ nexus_user }}" group: "{{ nexus_group }}" state: link notify: - nexus restart - nexus wait port when: ((not nexus_link.stat.exists) or (nexus_version_running != nexus_latest_version and nexus_upgrade)) and (not ansible_check_mode) #check previous installation and upgrade if nexus_upgrade = true
- name: Nexus JVM min heap lineinfile: dest: "{{ nexus_install_dir }}/nexus/bin/nexus.vmoptions" regexp: "^-Xms.*" line: "-Xms{{ nexus_min_heap }}" notify: - nexus restart - nexus wait port when: not ansible_check_mode
- name: Nexus JVM max heap lineinfile: dest: "{{ nexus_install_dir }}/nexus/bin/nexus.vmoptions" regexp: "^-Xmx.*" line: "-Xmx{{ nexus_max_heap }}" notify: - nexus restart - nexus wait port when: not ansible_check_mode
- name: Nexus JVM direct memory lineinfile: dest: "{{ nexus_install_dir }}/nexus/bin/nexus.vmoptions" regexp: "^-XX:MaxDirectMemorySize=.*" line: "-XX:MaxDirectMemorySize={{ nexus_direct_memory }}" notify: - nexus restart - nexus wait port when: not ansible_check_mode
- name: Chown configuration and data files to nexus file: path: "{{ item }}" owner: "{{ nexus_user }}" group: "{{ nexus_group }}" recurse: yes with_items: - "{{ nexus_install_dir }}/nexus/" - "{{ nexus_install_dir }}/sonatype-work/"
- name: Flush or execute current handlers meta: flush_handlers

9. Изменим файл playbook/roles/nexus/tasks/nexus_nginx.yml:

- name: Create nexus site config template: src: "nexus-vhost.conf.j2" dest: "/etc/nginx/sites-available/nexus" mode: 0644 owner: root group: root notify: - reload nginx
- name: Create nexus site link file: path: "/etc/nginx/sites-enabled/nexus" src: "/etc/nginx/sites-available/nexus" state: link notify: - reload nginx when: not ansible_check_mode

10. Изменим файл playbook/roles/nexus/tasks/nexus_post_install.yml:

- name: Check if admin.password file exists stat: path: "{{ nexus_install_dir }}/sonatype-work/nexus3/admin.password" register: admin_password_file
- name: Save content admin.password to var slurp: src: '{{ nexus_install_dir }}/sonatype-work/nexus3/admin.password' register: default_admin_password when: admin_password_file.stat.exists
- name: Display admin password to console debug: "msg={{ default_admin_password['content'] | b64decode }}" when: admin_password_file.stat.exists

11. Изменим файл playbook/roles/nexus/tasks/nexus_requirements.yml:

- name: Repo prepare 1 copy: content: "APT::Default-Release \"stable\";" dest: /etc/apt/apt.conf.d/99release
- name: Repo prepare 2 apt_repository: repo: deb http://security.debian.org/debian-security stretch/updates main state: present
- name: Install openjdk-8-jre apt: name: openjdk-8-jre state: present update_cache: yes when: not ansible_check_mode

12. Выполним проверку с параметром Check, для новой inventory группы, плейбук должен завершиться без ошибок:

ansible-playbook -i hosts_demo.txt playbook/infra.yml -K -D  --vault-id @prompt -e @group_vars/tokens.yml

13. Выполним infra playbook и применим изменения на новом хосте:

ansible-playbook -i hosts_demo.txt playbook/infra.yml -K -D  --vault-id @prompt -e @group_vars/tokens.yml

14. Повторно выполним playbook чтобы удостовериться, что больше никакие изменения вноситься не будут.