Перейти к содержанию

Настройка CI/CD в GitLab для автоматической сборки проекта сайта на MkDocs

В нашем случае будет использоваться связка docker контейнеров:

  • Traefik
  • Gitlab
  • Gitlab-runner
  • Nginx

Для начала создадим структуру директорий в которых будут храниться конфиги и данные наших контейнеров:

sudo mkdir -p /srv/docker/{conf,data/www}

Описывать параметры контейнеров будем в файле /srv/docker/docker-compose.yml.

Настройка Traefik

# Пропишем версию
version: '3.3'

# Перечислим сервисы
services:
   traefik:
        image: traefik:latest
        container_name: traefik
        restart: always
        security_opt:
          - no-new-privileges:true
        ports:
          - 192.168.0.111:80:80
          - 192.168.0.111:443:443
        environment:
          - CF_API_EMAIL=user@example.com
          - CF_DNS_API_TOKEN=1234567890abcdefgh1234567890
        volumes:
          - $DIR_CONF/traefik/traefik.yml:/traefik.yml:ro
          - $DIR_CONF/traefik/acme.json:/acme.json
          - $DIR_CONF/traefik/config.yml:/config.yml:ro
          - $DIR_CONF/traefik/custom/:/custom/:ro
          - /var/run/docker.sock:/var/run/docker.sock:ro
          - /etc/localtime:/etc/localtime:ro
        labels:
          - "traefik.enable=true"
          - "traefik.http.routers.traefik.entrypoints=http"
          - "traefik.http.routers.traefik.rule=Host(`traefik.example.ru`)"
          - "traefik.http.middlewares.traefik-https-redirect.redirectscheme.scheme=https"
          - "traefik.http.middlewares.sslheader.headers.customrequestheaders.X-Forwarded-Proto=https"
          - "traefik.http.middlewares.traefik-auth.basicauth.users=username:$$apr1$$QDKFN7LI$$iz.jL7bGBJCEygQDKQdUC0"
          - "traefik.http.routers.traefik.middlewares=traefik-https-redirect"
          - "traefik.http.routers.traefik-secure.entrypoints=https"
          - "traefik.http.routers.traefik-secure.rule=Host(`traefik.example.ru`)"
          - "traefik.http.routers.traefik-secure.middlewares=traefik-auth"
          - "traefik.http.routers.traefik-secure.tls=true"
          - "traefik.http.routers.traefik-secure.tls.certresolver=cloudflare"
          - "traefik.http.routers.traefik-secure.tls.domains[0].main=example.ru"
          - "traefik.http.routers.traefik-secure.tls.domains[0].sans=*.example.ru"
          - "traefik.http.middlewares.test-ipwhitelist.ipwhitelist.ipstrategy.excludedips=172.18.0.0/24"
          - "traefik.http.middlewares.WhitelistHome.ipwhitelist.sourcerange=172.18.0.0/24, 192.168.0.0/16"
          - "traefik.http.routers.traefik-secure.service=api@internal"

В моём случае настройка DNS записей производится через CloudFlare. Чтобы задать параметр CF_DNS_API_TOKEN нужно перейти на страницу API Tokens создать токен, выбрав "Edit zone DNS", задав разрешения и выбрав какие домены должны будут управляться этим токеном.

Для дополнительной защиты админки используется парольная авторизация, это параметр:

"traefik.http.middlewares.traefik-auth.basicauth.users=username:$$apr1$$QDKFN7LI$$iz.jL7bGBJCEygQDKQdUC0"

Где в качестве логина испльзуется username, а пароль: test12345 Для генерации этой пары воспользуйтесь командой:

echo $(htpasswd -nb username test12345) | sed -e s/\\$/\\$\\$/g

Также настроен белый список для доступа к админке, это параметр:

"traefik.http.middlewares.WhitelistHome.ipwhitelist.sourcerange=172.18.0.0/24, 192.168.0.0/16"

В файле /srv/docker/.env пропишем пути:

DIR_CONF=/srv/docker/conf
DIR_DATA=/srv/docker/data

Теперь можно и запустить контейнер:

cd /srv/docker
docker-compose up -d

Настройка Gitlab

   gitlab:
        image: gitlab/gitlab-ce:latest
        container_name: gitlab
        restart: always
        environment:
          GITLAB_OMNIBUS_CONFIG: |
            external_url 'https://gitlab.example.ru'
            nginx['listen_port'] = 80
            nginx['listen_https'] = false
            nginx['proxy_set_headers'] = {
            "X-Forwarded-Proto" => "https",
            "X-Forwarded-Ssl" => "on"
            }
            gitlab_rails['smtp_enable'] = true
            gitlab_rails['smtp_address'] = "smtp.mail.ru"
            gitlab_rails['smtp_port'] = 465
            gitlab_rails['smtp_user_name'] = "maillogin"
            gitlab_rails['smtp_password'] = "mailpassword"
            gitlab_rails['smtp_domain'] = "mail.ru"
            gitlab_rails['smtp_authentication'] = "login"
            gitlab_rails['smtp_enable_starttls_auto'] = false
            gitlab_rails['smtp_tls'] = true
            gitlab_rails['smtp_openssl_verify_mode'] = 'peer'
            gitlab_rails['gitlab_email_from'] = 'maillogin'
            gitlab_rails['gitlab_email_reply_to'] = 'maillogin'
        shm_size: '256m'
        volumes:
          - $DIR_CONF/gitlab:/etc/gitlab
          - $DIR_DATA/gitlab:/var/opt/gitlab
          - /etc/localtime:/etc/localtime:ro
        labels:
          - "traefik.enable=true"
          - "traefik.http.routers.gitlab.entrypoints=http"
          - "traefik.http.routers.gitlab.rule=Host(`gitlab.example.ru`)"
          - "traefik.http.middlewares.gitlab-https-redirect.redirectscheme.scheme=https"
          - "traefik.http.middlewares.sslheader.headers.customrequestheaders.X-Forwarded-Proto=https"
          - "traefik.http.routers.gitlab-secure.middlewares=WhitelistHome"
          - "traefik.http.routers.gitlab.middlewares=gitlab-https-redirect"
          - "traefik.http.routers.gitlab-secure.entrypoints=https"
          - "traefik.http.routers.gitlab-secure.rule=Host(`gitlab.example.ru`)"
          - "traefik.http.routers.gitlab-secure.tls=true"
          - "traefik.http.routers.gitlab-secure.service=gitlab"
          - "traefik.http.services.gitlab.loadbalancer.server.port=80"

Если вы используете свой домен подключеyный к VK Work Space, то maillogin указываете целиком - maillogin@example.ru при этом smtp_domain остается mail.ru.

Снова выполняем docker-compose up -d, ждём... долго...

Проверить статус загрузки можно командой:

docker logs gitlab -f
Периодически пытаемя заходим на gitlab.example.ru. Если удалось, то достаём пароль от панели управления:

$ docker exec -it gitlab grep 'Password:' /etc/gitlab/initial_root_password

Соотвественно для авторизации используем логин root и пароль из вывода сверху.

Настройка CI/CD в Gitlab

После авторизации в настройках профиля можно будет изменить язык на русский. Создаем новый проект и переходим слева в меню в "Настройки" -> "CI/CD" -> разворачиваем "Runner'ы". Для настройки самого runner нам понадобятся данные URL и токен регистрации.

Панель управления Gitlab, настройка CI/CD

Добавляем в /srv/docker/docker-compose.yml описание Gitlab-runner:

   gitlab-runner:
        image: 'gitlab/gitlab-runner:latest'
        container_name: gitlab-runner
        restart: always
        links:
          - gitlab
        volumes:
          - $DIR_CONF/gitlab-runner:/etc/gitlab-runner
          - /var/run/docker.sock:/var/run/docker.sock
          - /etc/localtime:/etc/localtime:ro

Выполняем docker-compose up -d и запускаем процесс регистрации нашего ранера: Добавляем в /srv/docker/docker-compose.yml описание Gitlab-runner:

docker exec -ti gitlab-runner gitlab-runner register
[user@host:/srv/docker]$ docker exec -ti gitlab-runner gitlab-runner register
Runtime platform                                    arch=amd64 os=linux pid=29 revision=c6bb62f6 version=14.10.0
Running in system-mode.                            

Enter the GitLab instance URL (for example, https://gitlab.com/):
https://gitlab.example.ru
Enter the registration token:
XMvANz3QF6fFgFsivdsocNsSAp7M1s
Enter a description for the runner:
[ffffffffffff]: blog
Enter tags for the runner (comma-separated):
mkdocs* 
Enter optional maintenance note for the runner:

Registering runner... succeeded                     
Enter an executor: docker, docker-ssh, ssh, docker-ssh+machine, kubernetes, custom, parallels, shell, virtualbox, docker+machine:
docker
Enter the default Docker image (for example, ruby:2.7):
squidfunk/mkdocs-material
Runner registered successfully. Feel free to start it, but if it's running already the config should be automatically reloaded! 

URL и токен нужно указать такие же как и на странице настроек в панели управления Gitlab. Пункты "Enter an executor" и "Enter the default Docker image" нужно оставить как указано.

Регистрация Gitlab-runner

Настройка Nginx

   nginx:
        image: nginx:latest
        container_name: nginx
        restart: always
        volumes:
          - $DIR_DATA/www:/www
          - $DIR_CONF/nginx:/etc/nginx
          - /etc/localtime:/etc/localtime:ro
        labels:
          - "traefik.enable=true"
          - "traefik.http.routers.nginx.entrypoints=http"
          - "traefik.http.routers.nginx.rule=Host(`example.ru`, `www.example.ru`)"
          - "traefik.http.middlewares.nginx-https-redirect.redirectscheme.scheme=https"
          - "traefik.http.routers.nginx.middlewares=nginx-https-redirect"
          - "traefik.http.routers.nginx-secure.entrypoints=https"
          - "traefik.http.routers.nginx-secure.rule=Host(`example.ru`, `www.example.ru`)"
          - "traefik.http.routers.nginx-secure.tls=true"
          - "traefik.http.routers.nginx-secure.tls.certresolver=http"
          - "traefik.http.routers.nginx-secure.service=nginx"
          - "traefik.http.services.nginx.loadbalancer.server.port=80"

Выполняем docker-compose up -d, и создаем конфиг нового сайта:

sudo nano /srv/docker/conf/nginx/sites-enabled
server {
        listen 80;
        server_name example.ru www.example.ru;

        root /www/example.ru/site;

location / {
        index index.html;
}

location ~* ^.+\.(jpg|jpeg|gif|png|ico|css|pdf|ppt|txt|bmp|rtf|js|woff)$ {
        expires 30d;
}

}

Проверяем корректность синтаксиса в конфигах nginx:

docker exec -ti nginx nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

Если всё ок, то делаем reload сервера:

docker kill -s HUP nginx

Тепеь создадим пустышку сайта, для этого перейдём в директорию /srv/docker/data/www/example.ru и выполним команду:

docker run --rm -it -v ${PWD}:/docs squidfunk/mkdocs-material new .

В результате у нас появятся два файла:

.
├─ docs/
│  └─ index.md
└─ mkdocs.yml

Файл mkdocs.yml — это файл конфигурации, используемый MkDocs. Файлы, находящиеся в папке docs (в частности, index.md) — это исходные материалы, из которых в дальнейшем будет сгенерирован наш сайт.

Генерируем сайт по существующим материалам:

docker run --rm -it -v ${PWD}:/docs squidfunk/mkdocs-material build

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

Настройка репозитория

Теперь настроим Gitlab CI и Gitlab Runner для генерации статического сайта. Для этого нам понадобиться добавить наш сайт в систему контроля версии и создать файл .gitlab-ci.yml, который будет автоматически собирать наш сайт после сохранения новых изменений.

  1. Перейдём в директорию /srv/docker/data/www/example.ru
  2. Создадим файл .gitignore и внесём в него 1 строку (команду игнорировать папку site); для этого в терминале введём команду
    echo '[Ss]ite/' > .gitignore
    
  3. Создадим репозиторий командой
    git init
    
  4. Создадим первый коммит, состоящий из всех созданных нами файлов
    git add -A && git commit -m 'Новый сайт'
    
    Снова вернёмся в панель управления Gitlab, где нужно будет скопировать путь до нашего проекта:

Далее воспользуемся этой ссылкой для сохранения созданного репозитория в созданный проект gitlab:

git remote add origin <ссылка на проект gitlab>
git push -u origin master

Теперь, когда репозиторий создан и связан с Gitlab, создадим в папке с исходными материалами файл .gitlab-ci.yml:

stages:
  - build
  - deploy

build_docs_job:
  stage: build
  tags: [mkdocs]
  only:
    - /^master$/
    - merge_requests
  image:
    name: squidfunk/mkdocs-material
    entrypoint: [""]
  script:
    - 'mkdocs build --site-dir site'
  artifacts:
    name: "site_$($CI_PIPELINE_IID)"
    paths:
      - site

deploy:
  stage: deploy
  tags: [mkdocs]
  image:
    name: squidfunk/mkdocs-material
    entrypoint: [""]
  dependencies:
    - build_docs_job
  only:
    - /^master$/
  before_script:
    - (if [ -d "site" ]; then echo ok; else exit "no build folder, try to run pipeline again"; fi);
  script:
    - cp -r site/* /site/

Чтобы сработал deploy и были скопированы файлы при работе команды:

 cp -r site/* /site/
нужно прокинуть папку /srv/data/www/example.ru/site внуть docker контейнера squidfunk/mkdocs-material, который будет запускаться ранером в процессе сборки. Для этого отредактируем файл:

sudo nano /srv/docker/conf/gitlab-runner/config.toml
где параметр:
    volumes = ["/cache"]

заменить на:
    volumes = ["/cache", "/srv/docker/data/www/example.ru/site:/site"]

Этим файлом мы указали Gitlab CI собирать сайт каждый раз, когда появляются новые коммиты в ветке master или в процессе создания или обновления merge request-ов.

Настройка почтовых уведомлений через SMTP

Для проверки корректности работы SMTP зайдём в консоль контейнера:

docker exec -it gitlab bash
Далее заходим в консоль гита:
gitlab-rails console -e production

и выполняем отправку тестового письма:

Notify.test_email('test@example.ru', 'zagolovok_gitlab email', 'Test_mail').deliver_now

GitLab SMTP Check

Ошибки и их решения

Развернуть
$ git commit -m 'Новый сайт'
*** Пожалуйста, скажите мне кто вы есть.

Запустите

  git config --global user.email "you@example.com"
  git config --global user.name "Ваше Имя"

для указания идентификационных данных аккаунта по умолчанию.
Пропустите параметр --global для указания данных только для этого репозитория.
$ git clone https://gitlab.example.ru/user/blog.git
Клонирование в «blog»…
fatal: unable to access 'https://gitlab.example.ru/user/blog.git/': server certificate verification failed. CAfile: none CRLfile: none
Решение:
GIT_SSL_NO_VERIFY=true git clone https://gitlab.example.ru/user/blog.git
К началу