Serveur de test pour l'équipe de développement

Bonjour, Habr! Dans cet article, je souhaite partager l'expérience du déploiement d'un serveur de test pour l'équipe de développement. Brièvement l'essence du problème - il y a une équipe de développement et plusieurs projets en php. Alors que nous étions peu nombreux et que le projet en était essentiellement un, nous avons utilisé 1 serveur de test et pour montrer la tâche au client, le développeur a «réparti» le serveur pendant un certain temps. S'il n'y avait pas de «fenêtres» à temps, alors nous devions attendre. Au fil du temps, l'équipe et la complexité des tâches ont augmenté, respectivement, le temps de vérification et l'activité du serveur de test ont augmenté, ce qui a eu une incidence négative sur le délai d'exécution et le bonus. Par conséquent, j'ai dû chercher une solution et c'est sous la coupe.

Introduction


Quel était:

  1. Un serveur de test
  2. Gitlab et redmine sur un autre serveur
  3. Désir de régler un problème

Tous les serveurs sont dans notre réseau local, le serveur de test est inaccessible de l'extérieur.

Ce qui était requis:

  1. Possibilité de tester plusieurs projets / branches en même temps
  2. Le développeur peut aller sur le serveur, le configurer et ne rien casser aux autres
  3. Tout doit être aussi pratique que possible et effectué sur 1 bouton, de préférence à partir de gitlab (CI / CD).

Options de décision


1. Un serveur, plusieurs hĂ´tes


L'option la plus simple. Nous utilisons le même serveur de test, seul le développeur doit créer un hôte pour chaque branche / projet et l'ajouter à la configuration nginx / apache2.

Avantages:

  1. Rapidement et tout le monde comprend
  2. Peut automatiser

Inconvénients:

  1. La clause 2 des exigences n'est pas satisfaite - le développeur peut commencer à mettre à jour la base de données et, dans certaines circonstances, tout mettre (Salut Andrey!)
  2. Automatisation assez complexe avec un tas de fichiers de configuration

2. À chaque développeur sur le serveur!


Attribuer à chaque serveur et le développeur est responsable de son économie.

Avantages:

  1. Le développeur peut personnaliser entièrement le serveur pour votre projet

Inconvénients:

  1. La clause 2 des exigences n'est pas remplie
  2. Cher et les ressources peuvent rester inactives pendant que le développement est en cours, pas de test
  3. L'automatisation est encore plus compliquée qu'au point 1 en raison de serveurs différents

3. Conteneurisation - docker, kubernetes


Cette technologie pénètre de plus en plus nos vies. À la maison, j'utilise depuis longtemps Docker pour mes projets.
Docker - logiciel pour automatiser le déploiement et la gestion des applications dans un environnement de virtualisation au niveau du système d'exploitation. Vous permet de "compresser" l'application avec tous ses environnements et dépendances dans un conteneur qui peut être porté sur n'importe quel système Linux avec prise en charge de cgroups dans le noyau, et fournit également un environnement de gestion de conteneur.
Avantages:

  1. Un serveur est utilisé
  2. Toutes les exigences sont remplies.

Inconvénients:

  1. Les images et les conteneurs prennent parfois beaucoup de place, il faut nettoyer les couronnes déjà dépassées pour libérer de l'espace.

Implémentation de Docker


Lors de l'utilisation de gitlab, AutoDevOps, les paramètres de kubernetes ont très souvent attiré mon attention. De plus, les gars barbus sur diverses rencontres disent à quel point ils travaillent avec kubernetes. Par conséquent, il a été décidé d'essayer de déployer le cluster dans ses installations, le serveur a été demandé (et vous ne pouvez pas toucher le test, les gens y testent) et cela a commencé!

Depuis que j'ai de l'expérience avec kubernetes 0, tout a été fait selon le manuel avec une tentative de comprendre comment fonctionnent tous ces clusters. Après un certain temps, j'ai réussi à augmenter le cluster, mais il y a eu ensuite des problèmes avec les certificats, les clés et en effet avec la difficulté de déploiement. J'avais besoin d'une solution plus simple pour enseigner à mes collègues comment travailler avec cela (par exemple, je ne veux pas passer les mêmes vacances assis sur Skype et aider à la configuration). Par conséquent, kubernetes a été laissé seul. Docker lui-même est resté et il était nécessaire de trouver une solution pour le routage des conteneurs. Puisqu'ils pouvaient être récupérés sur différents ports, le même nginx pouvait être utilisé pour la redirection interne. C'est ce qu'on appelle un serveur proxy inverse.
Un serveur proxy inverse est un type de serveur proxy qui relaie les demandes des clients d'un réseau externe vers un ou plusieurs serveurs logiquement situés sur le réseau interne. Dans le même temps, il considère le client comme si les ressources demandées se trouvaient directement sur le serveur proxy.

Proxy inverse


Afin de ne pas réinventer la roue, j'ai commencé à chercher des solutions toutes faites. Et il a été trouvé - c'est du traefik .

Træfik est un proxy inverse HTTP moderne et un équilibreur de charge qui simplifie le déploiement des microservices. Træfik s'intègre aux composants d'infrastructure existants (Docker, mode Swarm, Kubernetes, Marathon, Consul, Etcd, Rancher, Amazon ECS, ...) et est configuré automatiquement et dynamiquement. Pour travailler avec docker, il vous suffit de spécifier son socket et c'est tout, puis Tr fik lui-même trouve tous les conteneurs et les achemine vers eux (pour plus de détails, voir «Emballage d'applications dans docker»).

Configuration du conteneur Træfik
Je le lance via docker-compose.yml

version: '3' services: traefik: image: traefik:latest # The official Traefik docker image command: --api --docker # Enables the web UI and tells Træfik to listen to docker ports: - 443:443 - 80:80 # The HTTP port - 8080:8080 # The Web UI (enabled by --api) volumes: - /var/run/docker.sock:/var/run/docker.sock # So that Traefik can listen to the Docker events - /opt/traefik/traefik.toml:/traefik.toml - /opt/traefik/certs/:/certs/ networks: - proxy container_name: traefik restart: always networks: proxy: external: true 


Ici, nous informons le proxy que nous devons écouter les ports 80,443 et 8080 (la face Web du proxy), monter le socket docker, le fichier de configuration et le dossier de certificat. Pour faciliter la dénomination des sites de test, nous avons décidé de créer une zone de domaine local * .test. Lors de l'accès à un site sur celui-ci, l'utilisateur accède à notre serveur de test. Par conséquent, les certificats du dossier traefik sont auto-signés, mais il prend donc en charge Let's Encrypt.

Génération de certificats

 openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout domain.key -out domain.crt 

Avant de commencer, vous devez créer un réseau proxy dans le docker (vous pouvez le nommer vous-même).

 docker network create proxy 

Ce sera le réseau pour connecter traefik avec des conteneurs de sites php. Par conséquent, nous le spécifions dans le paramètre réseaux du service et dans les réseaux de l'ensemble du fichier en spécifiant dans le paramètre external: true.

Fichier Traefik.toml
 debug = false logLevel = "DEBUG" defaultEntryPoints = ["https","http"] #  insecureSkipVerify = true #   [entryPoints] [entryPoints.http] address = ":80" [entryPoints.https] address = ":443" [entryPoints.https.tls] [docker] endpoint = "unix:///var/run/docker.sock" domain = "docker.localhost" watch = true exposedbydefault = false 


Tout est assez simple ici - nous spécifions les points d'entrée du trafic http et https, n'oubliez pas de mettre insecureSkipVerify = true si les certificats sont locaux. Dans la section entryPoints.https.tls, vous ne pouvez pas spécifier de certificats, alors traefik substituera son certificat.

Vous pouvez démarrer le service

 docker-compose up -d 

Si vous allez sur site.test , vous obtiendrez une erreur 404, car ce domaine n'est lié à aucun conteneur.

Nous emballons les applications dans Docker


Vous devez maintenant configurer le conteneur avec l'application, Ă  savoir:

1. spécifier un réseau proxy dans les réseaux
2. ajouter des étiquettes avec la configuration de traefik

Voici la configuration de l'une des applications

applications docker-compose.yml
 version: '3' services: app: build: data/docker/php #   restart: always working_dir: /var/www/html/public volumes: - ./:/var/www/html #    - /home/develop/site-files/f:/var/www/html/public/f #       links: - mailcatcher - memcached - mysql labels: - traefik.enabled=true - traefik.frontend.rule=Host:TEST_DOMAIN,crm.TEST_DOMAIN,bonus.TEST_DOMAIN - traefik.docker.network=proxy - traefik.port=443 - traefik.protocol=https networks: - proxy - default mailcatcher: image: schickling/mailcatcher:latest restart: always memcached: image: memcached restart: always mysql: image: mysql:5.7 restart: always command: --max_allowed_packet=902505856 --sql-mode="" environment: MYSQL_ROOT_PASSWORD: 12345 MYSQL_DATABASE: site volumes: - ./data/cache/mysql-db:/var/lib/mysql #      phpmyadmin: image: phpmyadmin/phpmyadmin restart: always links: - mysql environment: MYSQL_USERNAME: root MYSQL_ROOT_PASSWORD: 12345 PMA_ARBITRARY: 1 PMA_HOST: mysql_1 labels: - traefik.enabled=true - traefik.frontend.rule=Host:pma.TEST_DOMAIN - traefik.docker.network=proxy - traefik.port=80 - traefik.default.protocol=http networks: - proxy - default networks: proxy: external: true 


Dans le service d'application, dans la section réseau, vous devez spécifier le proxy et le défaut, cela signifie qu'il sera disponible sur deux réseaux, comme le montre la configuration, je ne transfère pas les ports à l'extérieur, tout se passe sur le réseau.

Ensuite, configurez les étiquettes

  - traefik.enabled=true # traefik    - traefik.frontend.rule=Host:TEST_DOMAIN,crm.TEST_DOMAIN,bonus.TEST_DOMAIN #    traefik     - traefik.docker.network=proxy #   - traefik.port=443 #,     ssl   80   http - traefik.protocol=https #  #  phpmyadmin   http  

Dans la section des réseaux généraux, spécifiez external: true

La constante TEST_DOMAIN doit être remplacée par un domaine, par exemple, site.test

Lancez l'application

 docker-compose up -d 

Maintenant, si vous accédez aux domaines site.test, crm.site.test, bonus.site.test, vous pouvez voir le site de travail. Et sur le domaine pma.site.test, il y aura phpmyadmin pour un travail pratique avec la base de données.

Configurer GitLab


Nous créons un gestionnaire de tâches, pour cela nous exécutons

 gitlab-runner register 

Nous spécifions l'url de gitlab, le jeton et à travers lesquels la tâche sera exécutée (exécuteurs). Puisque mon test et gitlab sont sur des serveurs différents, je sélectionne ssh executor. Vous devrez spécifier l'adresse du serveur et le login / mot de passe pour la connexion via ssh.

Runner peut être attaché à un ou plusieurs projets. Puisque ma logique de travail est la même partout, un runner partagé a été créé (commun à tous les projets).
Et la touche finale est de créer un fichier de configuration CI

.gitlab-ci.yml
 stages: - build - clear #  develop build_develop: stage: build #   build tags: #     - ssh-develop environment: # ,       -   name: review/$CI_BUILD_REF_NAME #  url: https://site$CI_PIPELINE_ID.test #url     on_stop: clear when: manual script: - cd ../ && cp -r $CI_PROJECT_NAME $CI_PIPELINE_ID && cd $CI_PIPELINE_ID #     - cp -r /home/develop/site-files/.ssh data/docker/php/.ssh #  ssh - sed -i -e "s/TEST_DOMAIN/site$CI_PIPELINE_ID.test/g" docker-compose.yml #   - docker-compose down #   - docker-compose up -d --build #  - script -q -c "docker exec -it ${CI_PIPELINE_ID}_app_1 bash -c \"cd ../ && php composer.phar install --prefer-dist \"" #   - script -q -c "docker exec -it ${CI_PIPELINE_ID}_app_1 bash -c \"cd ../ && php composer.phar first-install $CI_PIPELINE_ID\"" #     #  production build_prod: stage: build tags: - ssh-develop environment: name: review/$CI_BUILD_REF_NAME url: https://site$CI_PIPELINE_ID.test on_stop: clear when: manual script: - cd ../ && cp -r $CI_PROJECT_NAME $CI_PIPELINE_ID && cd $CI_PIPELINE_ID - cp -r /home/develop/site-files/.ssh data/docker/php/.ssh #  ssh - docker-compose down - docker-compose up -d --build - script -q -c "docker exec -it ${CI_PIPELINE_ID}_app_1 bash -c \"cd ../ && php composer.phar install --prefer-dist --no-dev\"" - script -q -c "docker exec -it ${CI_PIPELINE_ID}_app_1 bash -c \"cd ../ && php composer.phar first-install $CI_PIPELINE_ID\"" clear: stage: clear tags: - ssh-develop environment: name: review/$CI_BUILD_REF_NAME action: stop script: - cd ../ && cd $CI_PIPELINE_ID && docker-compose down && cd ../ && echo password | sudo -S rm -rf $CI_PIPELINE_ID #       when: manual 


Dans cette configuration, 2 étapes sont décrites - construire et effacer. La phase de construction a 2 options - build_develop et build_prod



Gitlab construit un organigramme de processus compréhensible. Dans mon exemple, tous les processus démarrent manuellement (quand: paramètre manuel). Cela est fait pour que le développeur, après avoir déployé le site de test, puisse insérer ses modifications dans le conteneur sans reconstruire le conteneur entier. Une autre raison est le nom de domaine - site $ CI_PIPELINE_ID.test, où CI_PIPELINE_ID est le numéro du processus qui a démarré l'assemblage. Autrement dit, ils ont soumis le site avec le domaine site123.test pour vérification, et afin d'apporter des modifications à chaud, les modifications sont immédiatement versées dans le conteneur par le développeur.

Une petite fonctionnalité de ssh executor. Une fois connecté au serveur, un dossier du formulaire est créé.

 /home//builds/_runner/0/_/_ 

Par conséquent, une ligne a été ajoutée

 cd ../ && cp -r $CI_PROJECT_NAME $CI_PIPELINE_ID && cd $CI_PIPELINE_ID 

Dans celui-ci, nous montons dans le dossier ci-dessus et copions le projet dans le dossier avec le numéro de processus. Vous pouvez donc déployer plusieurs branches d'un même projet. Mais dans les paramètres du gestionnaire, vous devez vérifier Verrouiller sur les projets en cours, afin que le gestionnaire n'essaye pas de développer plusieurs branches en même temps.

L'étape d'effacement arrête les conteneurs et supprime le dossier, vous pouvez avoir besoin des privilèges root, nous utilisons donc le mot de passe echo | sudo -S rm où mot de passe est votre mot de passe.

Collecte des ordures


De temps en temps, vous devez supprimer les conteneurs inutilisés afin de ne pas prendre de place, pour cela un script avec un tel contenu se bloque dans la couronne

 #!/bin/bash #   : docker ps --filter status=dead --filter status=exited -aq | xargs -r docker rm -v #   : yes | docker container prune #    : yes | docker image prune #    : yes | docker volume prune 

effectué une fois par jour.

Conclusion


Cette solution nous a permis d'optimiser considérablement les tests et la sortie de nouvelles fonctionnalités. Prêt à répondre aux questions, la critique constructive est acceptée.

Bonus


Afin de ne pas collecter Ă  chaque fois des images du Dockerfile, vous pouvez les stocker dans le registre Docker local.

Fichier docker-compose.yml
 registry: restart: always image: registry:2 ports: - 5000:5000 volumes: - /opt/docker-registry/data:/var/lib/registry #     


Cette option n'utilise pas d'authentification, ce n'est pas un moyen sûr (!!!), mais il convient au stockage d'images non critiques.

Vous pouvez configurer gitlab pour afficher

  gitlab_rails['registry_enabled'] = true gitlab_rails['registry_host'] = "registry.test" gitlab_rails['registry_port'] = "5000" 

Après cela, une liste d'images apparaît dans gitlab

Source: https://habr.com/ru/post/fr426319/


All Articles