[PYTHON] À propos de CI / CD dans l'environnement Chalice x Circle CI

préface

https://chalice.readthedocs.io/en/latest/

AWS Chalice est un framework qui vous permet de créer un environnement sans serveur aussi facilement que Heroku en combinant AWS Lambda et API Gateway. Actuellement, seul Python est pris en charge en tant que langage de développement (on ne sait pas s'il le sera à l'avenir), mais si vous acceptez les restrictions, il semble qu'un environnement plus facile et plus facile à développer que les autres frameworks sans serveur soit fourni. Je pense. Même dans mon environnement, j'utilise souvent Chalice pour développer une gamme assez large de processus tels que la création d'API simples, de lots et de processus cron.

Je n'expliquerai pas ce que vous pouvez faire avec Chalice dans cet article.

Et comme M. Sutajio a écrit un article sur le test (https://qiita.com/studio3104/items/8a6b7e5f696e8453d97a), quel est l'environnement CI / CD lors du développement avec Chalice? Je souhaite écrire un article basé sur mon propre exemple. Ici, l'environnement CI utilisé est «Circle CI».

Il existe plusieurs thèmes, mais ici nous décrirons les thèmes suivants.

Prend en charge monorepo (vérification des différences pour chaque projet)

calice est un style dans lequel un échafaudage de projet est créé et développé sous la forme de `` calice new-project sada '' pour chaque fonction que vous souhaitez créer. La création d'un référentiel tel que GitHub pour chaque projet le rendra plus fragmenté. Je voudrais donc avoir une configuration monorepo, mais si je mets simplement plusieurs projets dans un référentiel, tous les projets peuvent être testés, vérifiés et déployés lors de la modification de certains projets pendant CI / CD. Il a tendance à fonctionner et ralentit le CI / CD.

Donc, en tant que stratégie de base, je voudrais prendre la forme de «exécuter uniquement CI / CD pour les projets qui ont changé dans le référentiel GitHub».

En réalisant cela, je me suis référé au contenu du blog suivant. Un script qui compare le contenu de la tête sur GitHub pour déterminer s'il existe des différences. https://blog.hatappi.me/entry/2018/10/08/232625

$ cat tools/is_changed 
#!/bin/bash

if [ "${CIRCLE_BRANCH}" = "master" ]; then
  DIFF_TARGET="HEAD^ HEAD"
else
  DIFF_TARGET="origin/master"
fi

DIFF_FILES=(`git diff ${DIFF_TARGET} --name-only --relative=${1}`)

if [ ${#DIFF_FILES[@]} -eq 0 ]; then
  exit 1
else
  exit 0
fi

En préparant un script de cette manière et en passant le projet Chalice que vous souhaitez vérifier au moment de CI / CD à l'argument du script, vous pouvez vérifier la différence pour chaque projet, sauter le projet sans différence et ne suivre que celui avec différence Il est possible d'effectuer le traitement CI de.

$ cat tools/echo_changed 

for dir in ${PROJECTS}; do \
  if ${WORKDIR}/is_changed "${dir}"; then
    echo "changed."
  else
    echo "nothing change."
  fi
done

unit test / code coverage Je pense que M. Sutajio a décrit en détail comment rédiger le test, je vais donc omettre les détails ici.

Dans mon environnement, j'utilise une combinaison de «couverture» et «pytest». Nous effectuons des tests unitaires et mesurons la couverture de test obtenue en conséquence.

$ coverage run -m pytest
$ coverage report
Name                Stmts   Miss  Cover
---------------------------------------
app.py                 35     22    37%
tests/__init__.py       0      0   100%
tests/conftest.py       4      0   100%
tests/test_app.py       5      0   100%
---------------------------------------
TOTAL                  44     22    50%

Si vous exécutez la couverture dans un environnement avec un test unitaire, vous pouvez générer un rapport comme celui ci-dessus. Vous pouvez enregistrer le rapport HTML en tant qu'artéfacts pour Circle CI et l'afficher à partir de l'écran des résultats de CI.

    - run:
        name: Run Tests
        command: |
          $HOME/.local/bin/coverage run -m pytest
          $HOME/.local/bin/coverage report
          $HOME/.local/bin/coverage html  # open htmlcov/index.html in a browser
    - store_artifacts:
        path: htmlcov

Veuillez vous référer aux documents officiels suivants pour les paramètres détaillés.

https://circleci.com/docs/2.0/code-coverage/#python

Aussi, si vous voulez arrêter le test quand il tombe en dessous d'un certain taux de couverture, vous pouvez faire rapport de couverture --fail-number [num] et il retournera return code = 2 si la couverture est inférieure ou égale au nombre. .. En d'autres termes, vous pouvez arrêter CI lorsque le seuil n'est pas atteint.

Si vous écrivez le traitement de cette zone comme un shell, ce sera comme suit. De tous les projets dans monorepo, s'il y a une différence, une mesure de test unitaire / couverture est effectuée, et si le test échoue ou même une couverture est en dessous du seuil, CI s'arrêtera.

$ cat tools/coverage 

ret=0
for dir in ${PROJECTS}; do 
  if ${WORKDIR}/is_changed "${dir}"; then
    cd ${WORKDIR}/../${dir}/ 
    sudo pip install -r requirements.txt
    coverage run -m pytest
    coverage report --fail-under=${THRESHOLD}
    r=$?
    coverage html -d ../htmlcov/${dir}
    if [ $r != 0 ]; then ret=$r; fi
  else
    echo "nothing change."
  fi
done

exit $ret

lint / code smells En plus des tests et des mesures de couverture, il est courant dans les flux CI / CD d'utiliser les outils Lint et Code Smells pour identifier de manière heuristique ** la «mauvaise écriture» **.

Je n'entrerai pas non plus dans les détails à ce sujet, mais dans mon environnement, j'utiliserai une combinaison de pep8 (pycodestyle) et pyflakes. Les deux outils géreront le CI selon les besoins, comme return code = 1 lorsqu'il y a une indication.

Ce qui suit est un exemple de résultat de pyflakes, mais il est souligné que dans les cas suivants, la variable sada est déclarée mais pas utilisée.

$ pyflakes app.py 
app.py:32: local variable 'sada' is assigned to but never used

Cependant, dans chaque cas, si vous effectuez avec les paramètres par défaut, vous obtiendrez des points à signaler de manière assez détaillée, c'est donc une opération réaliste pour décrire le fichier de paramètres en fonction de l'environnement et l'ignorer, ou pour décrire les règles en fonction de l'environnement. Je pense.

Si vous écrivez le traitement de cette zone comme un shell, ce sera comme suit.

$ cat tools/lint 

ret=0
for dir in ${PROJECTS}; do 
  if ${WORKDIR}/is_changed "${dir}"; then
    cd ${WORKDIR}/../${dir}/ 
    sudo pip install -r requirements.txt
    pep8 ${WORKDIR}/../${dir}/*.py --config ${WORKDIR}/../.config/pep8 
    pyflakes ${WORKDIR}/../${dir}/*.py 
    r=$?
    if [ $r != 0 ]; then ret=$r; fi
  else
    echo "nothing change."
  fi
done

exit $ret

validation du fichier de configuration du calice

Avec le contenu ci-dessus, je pense que le CI général peut être implémenté, mais dans le cas de charice, il est nécessaire de préparer un fichier de paramétrage ** (config.json, deploy /) ** pour chaque étape, et ce paramètre Je souhaite également vérifier la validité du fichier.

Le moyen le plus simple de le faire est d'exécuter simplement la commande calice package sur CI / CD et de voir si vous pouvez empaqueter calice correctement pour vérifier la validité du fichier de configuration.

$ cat tools/package 

for dir in ${PROJECTS}; do 
  if ${WORKDIR}/is_changed "${dir}"; then
    cd ${WORKDIR}/../${dir}/ 
    sudo pip install -r requirements.txt
    chalice package /tmp/${CIRCLE_SHA1}
  else
    echo "nothing change."
  fi
done

Diagnostic de vulnérabilité

De nos jours, il existe de nombreux cas sensibles concernant la sécurité des applications, je ne veux donc pas faire entrer le code le plus dérangeant possible dans l'application. Par conséquent, je voudrais vérifier à chaque fois au moment du CI pour voir si l'une des bibliothèques que j'utilise contient des vulnérabilités.

Je pense qu'il existe des solutions, mais ici nous utilisons le service d'une entreprise de sécurité appelée ** snyk ** (https://snyk.io/).

snyk est un SaaS qui offre la possibilité d'effectuer un test de vulnérabilité (diagnostic de vulnérabilité) dans le code source. Il prend en charge divers langages, mais bien sûr Python utilisé par Chalice est également pris en charge. En gros, regardez le contenu de requirements.txt pour comprendre la bibliothèque utilisée, et vérifiez si la version de la bibliothèque incluse dans la base de données de vulnérabilité est incluse.

Comme il est facile de se lier à GitHub, il est facile de l'incorporer dans le flux CI. Il est illimité pour les OSS publics et vous pouvez l'utiliser gratuitement jusqu'à 200 tests dans votre référentiel privé. https://snyk.io/plans/

Plutôt que d'écrire la définition dans circle.yml, snyk se présente sous la forme d'un lien avec GitHub. Je laisserai la méthode de réglage détaillée, etc. au document officiel. https://snyk.io/docs/github/

auto deploy Enfin, à propos de la partie CD de CI / CD. Puisque le calice peut être facilement déployé avec une commande telle que «calice deploy», il est facile de déployer automatiquement une source qui ne pose aucun problème en raison de CI.

Ce qui suit est le processus de déploiement de calice au dernier état uniquement pour les projets avec des différences écrites en shell.

$ cat tools/deploy 

for dir in ${PROJECTS}; do \
  echo "${WORKDIR}/../${dir}/"
  if ${WORKDIR}/is_changed "${dir}"; then
    cd ${WORKDIR}/../${dir}/
    sudo pip install -r requirements.txt
    AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID} AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY} chalice deploy --stage ${STAGE}
  else 
    echo "nothing change."
  fi
done

Je pense qu'il est controversé d'utiliser les outils CI pour le déploiement automatique dans les environnements de production. (Dans mon environnement, je n'effectue pas de déploiement automatique dans l'environnement de production, mais souvent uniquement dans l'environnement de vérification)

Au lieu de résumer

AWS Chalice est un framework qui vous permet d'écrire facilement un traitement sans serveur sous la forme d'Heroku, bien qu'il soit limité à Python, et je pense qu'il a l'avantage de pouvoir écrire un traitement Lambda comme une application normale. Et en raison de ses avantages, il est facile de l'intégrer dans le flux CI / CD comme décrit ci-dessus, même dans un environnement sans serveur tel que Lambda.

Alors utilisons tous aussi Chalice. Happy Chalice Life and Serverless Life!!

Recommended Posts

À propos de CI / CD dans l'environnement Chalice x Circle CI
Pensez à créer un environnement Python 3 dans un environnement Mac
Installez LightGBM dans l'environnement virtualenv OSX