Примечания по шагам TDD (Traefik + DroneCI + Docker)

CI/CD

предисловие

Я обновил сервер несколько лет назад.Исходный сервер был комбинацией Nginx+Jenkins+Docker.Из-за моего сильного интереса к обновленным инструментам я хотел сосредоточиться на возне с новыми игрушками и начал использовать Traefik+DroneCi+Docker на замену оригинальной.Бросающая дорога комбинации.

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


Traefik

Traefik — это обратный прокси-сервер с открытым исходным кодом и инструмент балансировки нагрузки.Многие сравнивают его с Nginx.На самом деле, я лично считаю, что у обоих есть свои достоинства. Когда я использую его, поскольку traefik плохо поддерживает статические веб-сайты, я все еще использую его с Nginx. Но это не останавливает тот факт, что это отличный инструмент обратного прокси.

настройка и запуск

Конфигурация Traefik включает в себя статическую конфигурацию и динамическую конфигурацию. Статическая конфигурация — это конфигурация самого Traefik при запуске, и ее необходимо перезапустить, чтобы она вступила в силу. Динамическая конфигурация может рассматриваться как конфигурация прокси-службы и не требует перезапуска. после модификации. Поддерживается динамическая или статическая конфигурацияCliform и конфигурационный файл form, но конфигурационный файл и параметры cli нельзя накладывать друг на друга.

  • статическая конфигурация

    Взяв в качестве примера запуск службы Traefik в docker-compose, мы можем посмотреть на путь cli и файлов конфигурации:

    services:
      traefik:
        restart: always
        image: traefik:latest
        ports:
          - "80:80"
          - "443:443"
        # command:
        #  - "--providers.docker=true"
        #  - "--providers.docker.exposedbydefault=false"
        volumes:
          - /var/run/docker.sock:/var/run/docker.sock
          - ./acme.json:/acme.json
          - ./traefik/:/etc/traefik # 如果有配置文件了,则command 失效 
      who:
        image: containous/whoami
        labels:
          - "traefik.enable=true"
          - "traefik.http.routers.whoa.rule=Host(`who.nefelibata.art)"
    

    Мы настроили контейнер traefik и включилиtraefik.tomlа такжеdynamic.tomlдокумент./traefikкаталог сопоставляется с контейнером/etc/traefikкаталог, который traefik будет читать при запуске/etc/traefikв каталогеtraefik.toml, если вы хотите использовать метод cli, вы передадите параметры конфигурации через команду.

  • Динамическая конфигурация

    • Форма файла конфигурации

      Сначала нам нужноtraefik.tomlСуществуют следующие конфигурации:

      [providers]
        ## ...
        [providers.file]
          filename = "/etc/traefik/dynamic_conf.toml"
          watch = true
      

      затем вdynamic_conf.tomlСредняя конфигурацияroutersа такжеservices

      [http.routers]
        [http.routers.https-egg]
          rule = "Host(`egg.nefelibata.art`)"
          service = "egg-service"
          [http.routers.https-egg.tls] ## 开启https
             certresolver = "le"
        [http.routers.http-egg]
          rule = "Host(`egg.nefelibata.art`)"
          service = "egg-service"
      
      [http.services]
        [[http.services.egg-service.loadBalancer.servers]]
          url = "http://egg_server:9000"
      

      http.routersПосле пользовательского имени жестких требований нет, но дочерние элементы должны быть расширены на основе этого имени, например:http.routers.https-eggКогда tls включен, он используетсяhttp.routers.https-egg.tls

      Следует отметить, что мыegg.nefelibata.artОпределены два маршрутизатора, это связано с тем, что если установленоtlsЕсли true, доступ по http больше не поддерживается. Если вы хотите поддерживать обаhttpа такжеhttps, необходимо определить不同名маршрутизация

    • Форма меток Docker

      Если мы не хотим настраивать все это в файле динамической конфигурации, мы можемtraefik.tomlвнутриprovidersНапишите следующую конфигурацию ниже:

      [providers]
        [providers.docker]
          # 以下均为可选项
          network = "traefik"
          exposedByDefault = false
          defaultRule = "Host(`{{ normalize .Name }}.nefelibata.localhost`)"
          watch = true
        ## ... 其他配置
      

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

      egg_server:
          build: ./egg_server
          expose:
            - "9000"
          networks:
            - default
          labels:
            - "traefik.enable=true"
            - "traefik.http.routes.egg_server.rule=Host(`egg.nefelibata.art`)"
      

      Тогда конфигурация не требуетсяservicesТеперь просто откройте порт для контейнера.

      Обратите внимание, что если он отключен в конфигурацииexposedByDefaultопция, если она не определена в этикетках других контейнеровtraefik.enable=true, служба контейнера будет игнорироваться traefik

Открыть информационную панель

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

## traefik.toml
### 其他配置...
[api]
### 其他配置... providers, ping .etc

## dynamic.toml
[http.routers.api]
    rule = "Host(`traefik.nefelibata.art`)"
    service = "api@internal"
    middlewares = ["dashboard-auth"]

[http.middlewares]
   [http.middlewares.dashboard-auth.basicAuth]
   users = [
     "evont: $xxxxxxxxxxx"
   ]

существуетtraefik.tomlоткрыть вapiпосле опции (или в cli--api=true), traefik будет иметь специальный сервис под названиемapi@internal, после его настройки, как правило, для предотвращения доступа других, будет выполняться аутентификация, поэтомуmiddlewares, используя предоставленный traefikbasicAuthпромежуточное ПО, использованиеhtpasswdСгенерируйте пользовательский ключ, обратите внимание, например, ваше имяevont, пароль123456, окончательный результатevont:$apr1$bL6G3wl2$HllalTsbNwJ/zhoBMhx541, при открытии панели инструментов и входе в систему введенный пароль по-прежнему 123456 вместо ключевой строки.

После успешной настройки перезапустите службу, откройте доменное имя службы, и вы увидите интерфейс входа в систему.

После успешного входа в систему вы можете войти в интерфейс управления и увидеть настроенные нами правила маршрутизации.

Статическая поддержка веб-сайта

У Traefik нет хорошей поддержки статических веб-сайтов (по крайней мере, я не нашел способа ее использовать), поэтому я могу использовать Nginx в качестве сервера только для статических веб-сайтов, но порты 443 и 80 не могут быть предоставлены двум обратным прокси-инструментам. в то же время, поэтому я могу передать только Traefik перенаправляет запрос на Nginx.Мы запускаем службу Nginx, указываем сети, чтобы Nginx и Traefik были в одной сети, а затем назначаем Nginx ограниченное доменное имя через метки.

services:
  nginx:
    restart: always
    image: nginx
    networks:
      - default
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf
      - ./html:/usr/share/nginx/html/
    labels:
      - traefik.enable=true
      - traefik.http.routers.w3.rule=Host(`www.nefelibata.art`) || Host(`mock.nefelibata.art`)
      - traefik.http.routers.w3.tls=true
      - traefik.http.routers.w3.tls.certresolver=le
      - traefik.http.routers.w3-http.rule=Host(`www.nefelibata.art`) || Host(`mock.nefelibata.art`)

в Nginxnginx.confВ файле по-прежнему используется порт 80, а директория статического сайта может быть размещена в соответствии с server_name

http {
  server {
    listen       80;
    server_name  www.nefelibata.art;
    root /usr/share/nginx/html/www1;
    location / {
      index index.html;
    }
  }
  server {
    listen       80;
    server_name  mock.nefelibata.art;
    root /usr/share/nginx/html/www2;
    location / {
      index index.html;
    }
  }
}

поддержка https (также поддерживает http)

Ранее мы упоминали tls и https, естьcertresolver = "le", то этоleОткуда он взялся и как был сгенерирован сертификат для сайта?

если вы хотите использоватьLet's EncryptДля автоматической генерации сертификатов traefik предоставляет нам очень удобное решение, нам нужно только использовать следующую конфигурацию в статической конфигурации:

## traefik.toml
### 其他配置...
[certificatesResolvers.le.acme]
  email = "evontgoh@foxmail.com" # 你自己的邮箱
  storage = "acme.json"

  [certificatesResolvers.le.acme.httpChallenge]
    entryPoint = "http"

и настроить в docker-composevolumesкарта местногоacme.jsonв контейнер

Если у вас есть собственный сертификат, вы также можете пропустить вышеуказанные шаги, вdynamic.tomlСредняя конфигурация

[[tls.certificates]]
  certFile = "/path/to/domain.cert"
  keyFile = "/path/to/domain.key"

Эта часть может относиться кtls-конфигурация traefik

версия ямы

В начале настройки большинство онлайн-руководств по-прежнему основывались на v1, поэтому настройка не удалась. Позже выяснилось, что разница между версиями v1 и v2 слишком велика, элементы конфигурации были другими и дажеtraefik китайская документацияпо-прежнему основаны на конфигурации v1, например, при определении правил маршрутизации

## v1 规则如下:
[frontends] ## 规定前端进入规则
   [frontends.frontend1]
   backend = "backend1" # 指定后端服务
   [frontends.frontend1.routes]  ## 定义路由
      [frontends.frontend1.routes.route0]
        rule = "Host:test.localhost"  ## 注意,这里写法也变了
[backends] ## 定义后端服务
  [backends.backend1]
    [backends.backend1.servers.server0]
        url = "http://xx.xx.xx.xx:80"
  
## v2 规则弃用了frontend & backend 
[http.routers] ## 用routers 规定路由规则
  [http.routers.router0]
    rule = "Host(`test.localhost`)" ## 写法变了
    service = "my-service"

[http.services]
  [[http.services.my-service.loadBalancer.servers]]
    url = "http://xx.xx.xx.xx:80"

В то же время вышеперечисленные правила определены в v1[file]Под полем, во v2, это в[providers]вниз[providers.file]определяется в и становится отдельным файлом динамической конфигурации

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


Drone CI

Я всегда использовал Jenkins, стандартный отраслевой инструмент CI, но мне всегда казалось, что он немного громоздкий из-за его богатых функций Drone оптимизирован для контейнерных сред, таких как Docker и K8s, а также достаточно легкий и гибкий. Как выбрать можно посмотретьэта статья

OAuth

Во-первых, Drone поддерживает только Git.Взяв Github в качестве примера, чтобы вытащить код, вам нужноDeveloper settings(вы можете использовать другие хранилища git), чтобы создать новое приложение OAuth, заполните доменное имя вашего сервиса Drone, обратите внимание, что URL-адрес обратного вызова должен заполнить логин

Установить

Он по-прежнему основан на концепции, что все сервисы сервера находятся в Docker.docker-compose.ymlСоздайте новый сервис Drone в , многие туториалы настроят клиент (агентов) агента Drone-runner, но это не обязательно, на самом деле вы можете полностью использовать сервер Drone для завершения сервиса

version: "3"

services:
  drone-server:
    image: drone/drone:latest
    labels:
      - traefik.http.routers.drone.rule=Host(`ci.nefelibata.art`)
      - traefik.enable=true
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - /etc/docker/:/etc/docker
      - ./drone:/var/lib/drone/ # 注意设置这一目录,用于放置sqlite文件,如果用mysql 或其他数据库,酌情处理
    restart: always
    networks:
      - default
    environment:
      - DRONE_OPEN=TRUE    
      - DRONE_ADMIN=xxx
      - DRONE_USER_CREATE=username:xxx,admin:true
      - DRONE_DATABASE_DATASOURCE=/var/lib/drone/drone.sqlite # 指向该目录
      - DRONE_DATABASE_DRIVER=sqlite3 # 数据库引擎
      - DRONE_RPC_SECRET=${DRONE_RPC_SECRET}
      - DRONE_RPC_PROTO=${DRONE_RPC_PROTO}
      - DRONE_AGENTS_DISABLED=true 
      - DRONE_GITHUB_CLIENT_ID=${DRONE_GITHUB_CLIENT_ID}
      - DRONE_GITHUB_CLIENT_SECRET=${DRONE_GITHUB_CLIENT_SECRET}
      - DRONE_SERVER_HOST=${DRONE_SERVER_HOST}
      - DRONE_SERVER_PROTO=${DRONE_SERVER_PROTO}
networks:
  default:
    external:
      name: traefik

В конфигурации, чтобы не хотеть некоторые из наших клавиш, которые будут выставлены, мы можем написать эту часть переменных в.envФайл, Docker считывает файлы в том же каталоге, пишет указанная переменная $ {xxx} переменная:

DRONE_GITHUB_CLIENT_ID=xxxxx  # 填入OAuth 生成的client id
DRONE_GITHUB_CLIENT_SECRET=xxxxx # 填入OAuth 生成的client secret
DRONE_RPC_SECRET=xxxxx  # 可以通过openssl rand -hex 16 生成
DRONE_SERVER_HOST=ci.nefelibata.art
DRONE_SERVER_PROTO=http
DRONE_RPC_PROTO=http

Зарегистрированный дрон по умолчанию является общедоступным, то есть все те, у кого есть доступ к вашему адресу CI, могут зарегистрироваться и использовать вашу систему CI, если вы хотите ограничить пользователей, вы можете настроить среду- DRONE_USER_FILTER=evont, xxxспособ добавить пользователей, которым разрешено присоединиться (но ранее зарегистрированные пользователи не будут ограничены, странная логика).

Запустите службу, получите доступ к доменному имени службы и перейдите к Github, чтобы войти в систему. Если пользователь входа в систему ограничен на предыдущем шаге, а текущее имя учетной записи Github не входит в разрешенную учетную запись, при возврате к сервис.

В случае успеха вы можете увидеть список ваших проектов репозитория Github.

Войдите в проект и активируйте проект, если вошедший в систему пользователь является администратором, он будетProject settingsпоявляться вTrustedвариант, иначе есть только одинProtectedвариант, толькоTrustedРепозиторий может выполняться только с хостом в процессе сборкиVolumesкарта.

сборка проекта

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

Создайте новый проект, после завершения основного кода создайте новый в корневом каталоге проекта следующим образом..drone.yml, с помощью DroneDockerПлагин, указывающий адрес зеркального репозитория и ветку.

Поскольку вы используете частный репозиторий, вам необходимо войти в систему. В это время мы можем использовать интерфейс CISecretsСтолбец может быть заполнен некоторыми переменными для использования в процессе сборки, и они не будут отображаться в.drone.ymlбинго. Как показано на предыдущем рисунке, вSecretsЧастично добавить обычайDOCKER_USERNAMEа такжеDOCKER_PASSWORDполе, затем в.drone.ymlпрошедшийfrom_secretвходящийusernameа такжеpassword, его не нужно прописывать в конфигурационном файле, чтобы его не увидели другие, у которых есть доступ к коду.

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

---
kind: pipeline
type: docker
name: default


steps:
  - name: egg-docker
    image: plugins/docker
    settings: 
      mirror: https://xxxx(自己的用户id).mirror.aliyuncs.com
      username: 
        from_secret: DOCKER_USERNAME
      password:
        from_secret: DOCKER_PASSWORD 
      repo: registry.cn-hangzhou.aliyuncs.com/nefelibata/egg
      registry: registry.cn-hangzhou.aliyuncs.com
      auto_tag: true

trigger:
  branch:
    - master
  event:
    - push

После подтверждения отправьте код в проект, чтобы запустить сборку. Если версия дрона выше 1.4.0 и агент не включен, вы, вероятно, застряли в состоянии ожидания, как и я, потому что по умолчанию дрон находится в многомашинном режиме (multi-machine mode), если он находится под одним сервером, прокси-сервер ставить не нужно. Многие конфигурации в Интернете преподаютсяDRONE_AGENTS_ENABLED=false, однако на самом деле это должно бытьDRONE_AGENTS_DISABLED=trueвключить автономный режим (single-machine mode)

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

Сборка завершена, загнали на зеркальный склад, посыпаем цветами!


Docker

  • networks

    Контейнеры между разными Docker Compose отличаются друг от друга.Каждый Docker Compose имеет свои сети.Вышеуказанные сервисы разделены по разнымdocker-compose.ymlВ файле, чтобы сделать их взаимосвязанными, нам нужно сделать их в одной сети, в это время мы можем сначала выполнитьdocker network create treaefikСоздайте общую сеть, а затем в каждойdocker-compose.ymlНастройте сети так, чтобы они указывали на эту вновь созданную сеть, и, наконец, укажите ее сети в контейнере.

      services:
         nginx:
          # ...
          image: nginx
          networks:
            - default
          # ...
      networks:
        default:
          external:
            name: treaefik
    
  • разное

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