Utilisation de la méthode scientifique pour déboguer des applications conteneurisées | par Michael Bogan | Meilleure programmation | Novembre 2020

Online Coding Courses for Kids

Voyons maintenant cette approche en action.

1. Demandez «Que devrait-il se passer?»

Tout d’abord, nous devons comprendre ce que notre application essaie de faire. Pour commencer à enquêter, nous devons comprendre les détails du rendu de la page. Voyons comment le Dockerfile décrit notre service:

FROM python:3WORKDIR /usr/src/appCOPY requirements.txt ./RUN pip install --no-cache-dir -r requirements.txtRUN echo "healthy" >> /tmp/healthy &&         cat /tmp/healthyENV FLASK_ENV=developmentENV FLASK_APP=myapp.pyCOPY myapp.py .CMD [ "python3", "myapp.py" ]

Assez simple. Nous avons une application Python3, elle installe certaines exigences puis démarre myapp.py. Jetons un coup d’œil à ce que fait notre application Python, puis nous pourrons décider de la meilleure façon de procéder.

from flask import Flask, Responseimport osimport psycopg2app = Flask(__name__)query = ("select * from actor order by random() limit 5;")host = os.getenv("host")password = os.getenv("PSQL_PASSWORD")user = os.getenv("PSQL_USERNAME")dbname = os.getenv("PSQL_DBNAME")print(host)print(user)print(dbname)def callpsql():    conn = psycopg2.connect(dbname=dbname, user=user, password=password, host=host, port="5432")    cur = conn.cursor()    cur.execute(query)    results = cur.fetchall()    cur.close()    conn.close()    return results@app.route('/')def index():    response = callpsql()    print("Getting some juicy logs")    results = "I've sold to these famous people from server "+os.getenv("DYNO")+": 
n"
for row in response: results = results+"
n"+str(row[1])+"t"+str(row[2])
return results@app.route('/health')def health(): f = open("/tmp/healthy", "r") print(f) health = f.readline().rstrip() resp = Response(health) if health != "healthy": resp.status=500 resp.headers['health-check'] = health return respif __name__ == "__main__": port = 5000 app.run(debug=True, host='0.0.0.0', port=port)

Une autre application assez simple. Cela prend quelques variables d’environnement pour l’authentification et exécute une requête SQL sur une base de données Postgres lorsque la page racine est appelée.

2. Déterminez les raisons qui pourraient expliquer pourquoi ne pas événement

Maintenant que nous savons quelle est l’application en essayant Pour ce faire, nous passons à l’étape suivante, expliquons pourquoi cela ne se produit pas dans notre nouvel environnement.

Ici, nous pouvons regarder les journaux Heroku --tail commande suggérée sur la page d’erreur. Cela devrait nous donner une bonne idée de ce que fait notre service à tout moment et aussi nous aider à formuler une hypothèse sur ce qui pourrait être le problème.

$ heroku logs --tail -a heroku-troubleshooting...2020-08-22T22:45:34.805282+00:00 heroku[router]: at=error code=H14 desc="No web processes running" method=GET path="/" host=heroku-troubleshooting.herokuapp.com request_id=09292b76-b372-49e9-858b-e8611c020fd5 fwd="xx.xx.xx.xx" dyno= connect= service= status=503 bytes= protocol=https

Nous voyons l’erreur «Aucun processus Web en cours d’exécution» apparaître chaque fois que nous chargeons la page. Il semble que Heroku ne sache pas que nous avons lancé Flask. Génial! Nous avons notre hypothèse: “Flask ne fonctionne probablement pas dans l’environnement d’Heroku.”

3. Concevoir un test pour valider / invalider ces revendications en ajustant une configuration ou en utilisant un outil

Passons maintenant aux étapes suivantes: créer un test pour valider ce que nous pensons qu’il se passe, observer le résultat et tirer une conclusion.

Alors testons notre hypothèse. Nous allons sauter sur notre dynamomètre et valider que le service Flask est (ou non) en cours d’exécution. Notre Dockerfile spécifie que python doit être un service en cours d’exécution et à l’écoute.

$ heroku ps:exec -a heroku-troubleshootingEstablishing credentials... error ▸    Could not connect to dyno! ▸    Check if the dyno is running with `heroku ps'

4. Observez le résultat

Nous avons libéré notre conteneur, il devrait donc fonctionner. Cependant, les journaux de notre test nous ont montré que nous ne pouvions pas nous connecter au dynamomètre qui devrait exécuter le code. L’exécution de la commande suggérée peut nous donner un autre indice.

$ heroku ps -a heroku-troubleshooting

No dynos on ⬢ heroku-troubleshooting

5. Tirez une conclusion

Hum, ceci est embarrassant. La conclusion de la méthode scientifique? Nous nous sommes trompés dans notre affirmation initiale selon laquelle notre code publié nous laissait un dynamomètre en cours d’exécution pour servir le code. Nous avons définitivement poussé et publié notre image, mais nous n’avons ajouté aucun dynamomètre à notre application. Il semble que nous ayons validé que l’application ne fonctionne pas et que notre hypothèse était incorrecte.

6. Répétez jusqu’à ce que le résultat soit ce qui devrait se passer

Nous passons donc à l’étape suivante, qui consiste à répéter le processus, et nous continuerons à répéter ce processus tout au long de cet article jusqu’à ce que notre problème soit résolu.

Alors formons une nouvelle hypothèse et testons-la. Notre nouvelle hypothèse, basée sur le dernier test, est qu’avec le dyno en marche, nous devrions pouvoir accéder à notre application Flask depuis le monde extérieur. Nous allons mettre à l’échelle un dynamomètre pour démarrer l’application et vérifier à nouveau les journaux pour tester cette assertion.

$ heroku dyno:scale -a heroku-troubleshooting worker=1Scaling dynos... done, now running worker at 1:Standard-1X$ heroku logs --tail -a heroku-troubleshooting2020-08-22T23:29:05.207217+00:00 app[api]: Scaled to worker@1:Standard-1X by user2020-08-22T23:29:18.202160+00:00 heroku[worker.1]: Starting process with command `python3 myapp.py`2020-08-22T23:29:18.760500+00:00 heroku[worker.1]: State changed from starting to up2020-08-22T23:29:21.559386+00:00 app[worker.1]: None2020-08-22T23:29:21.559547+00:00 app[worker.1]: None2020-08-22T23:29:21.559548+00:00 app[worker.1]: None2020-08-22T23:29:21.559549+00:00 app[worker.1]: * Serving Flask app "myapp" (lazy loading)2020-08-22T23:29:21.559556+00:00 app[worker.1]: * Environment: development2020-08-22T23:29:21.559556+00:00 app[worker.1]: * Debug mode: on2020-08-22T23:29:21.636027+00:00 app[worker.1]: * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)2020-08-22T23:29:21.637965+00:00 app[worker.1]: * Restarting with stat2020-08-22T23:29:21.897821+00:00 app[worker.1]: * Debugger is active!2020-08-22T23:29:21.929543+00:00 app[worker.1]: * Debugger PIN: 485-744-571

On y va. Maintenant, l’application est lancée! Il est temps de tester à nouveau le site pour voir s’il fonctionne.

Message d’erreur

Notre conclusion à présent est que ce n’est certainement pas le cas. Nous devrions revoir nos commandes et voir si le statut a changé du tout afin de pouvoir créer une nouvelle hypothèse.

$ heroku logs -a heroku-troubleshooting
2020-08-22T23:32:12.098568+00:00 heroku[router]: at=error code=H14 desc="No web processes running" method=GET path="/" host=heroku-troubleshooting.herokuapp.com request_id=4d3cbafa-cb31-4452-bfc6-7ee18ce72eb0 fwd="xx.xx.xx.xx" dyno= connect= service= status=503 bytes= protocol=https
$ heroku ps -a heroku-troubleshooting
=== worker (Standard-1X): python3 myapp.py (1)
worker.1: up 2020/08/22 16:31:59 -0700 (~ 3m ago)

Il semble que nous exécutions un service, mais nous ne pouvons plus obtenir de données en utilisant uniquement les journaux. En théorie, nous pouvons entrer dans le service et nous assurer qu’il fonctionne.

$ heroku ps:exec -d worker.1 -a heroku-troubleshootingEstablishing credentials... error ▸    Could not connect to dyno! ▸    Check if the dyno is running with `heroku ps'

Ici, nous pouvons observer que même avec le dynamomètre en marche, nous ne pouvons pas nous y connecter. En regardant notre Dockerfile et quelques documents, nous pouvons conclure qu’il existe un peu de prérequis nécessaire pour que l’environnement Heroku prenne en charge exec dans les conteneurs déployés. Nous devrions ajouter quelques commandes utilitaires dont nous pourrions avoir besoin pendant que nous sommes ici pour tester les hypothèses futures pendant que nous testons si cette nouvelle configuration nous permettra de nous connecter.

Dockerfile

...RUN apt-get update &&         apt-get install -y                 curl                 openssh-server                            net-tools                            dnsutils                            iproute2RUN mkdir -p /app/.profile.d &&         touch /app/.profile.d/heroku-exec.sh &&         echo '[ -z "$SSH_CLIENT" ] && source <(curl --fail --retry 3 -sSL "$HEROKU_EXEC_URL")' >> /app/.profile.d/heroku-exec.sh &&         chmod +x /app/.profile.d/heroku-exec.sh...

Encore un push et une version plus tard et nous avons validé que notre conclusion était correcte et nous pouvons enfin nous connecter:

$ heroku ps:exec -d worker.1 -a heroku-troubleshootingEstablishing credentials... doneConnecting to worker.1 on ⬢ heroku-troubleshooting...The programs included with the Debian GNU/Linux system are free software;the exact distribution terms for each program are described in theindividual files in /usr/share/doc/*/copyright.Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extentpermitted by applicable law.~ $

Maintenant que nous sommes dans l’application, nous pouvons vérifier notre hypothèse selon laquelle le port écoute comme il se doit et que notre application est réactive depuis le conteneur lui-même. Nous pouvons utiliser des “net-tools” netstat comme test pour nous montrer une liste de ports ouverts sur notre conteneur et à l’écoute des appels pour ouvrir une socket.

~ $ netstat -apn | grep 5000 | grep LIST(Not all processes could be identified, non-owned process info will not be shown, you would have to be root to see it all.)tcp        0      0 0.0.0.0:5000            0.0.0.0:*               LISTEN      3/python3

Notre analyse montre que le port est ouvert comme prévu et notre hypothèse est correcte.

Parce que rien n’a changé, nous devons concevoir un nouveau test. «Curl» peut agir comme notre navigateur Internet en ligne de commande pour nous montrer une vue simplifiée du port que nous avons maintenant vérifié est ouvert et écoute correctement.

~ $ curl -I localhost:5000HTTP/1.0 500 INTERNAL SERVER ERRORContent-Type: text/html; charset=utf-8X-XSS-Protection: 0Server: Werkzeug/1.0.1 Python/3.8.5Date: Sun, 23 Aug 2020 00:27:14 GMT~ $ curl localhost:5000...psycopg2.OperationalError: could not connect to server: No such file or directory        Is the server running locally and accepting        connections on Unix domain socket "/var/run/postgresql/.s.PGSQL.5432"?...

Ah-ha! Notre analyse de cette erreur prouve que les appels échouent car nous recevons une erreur 500 dans notre application. Notre nouvelle hypothèse est que c’est sûrement au moins un raison pour laquelle Heroku n’enverra pas de trafic vers notre application. Si nous regardons notre code Python, nous pouvons voir qu’il nécessite certaines variables d’environnement. Dans notre hôte local, nous les exportons dans le cadre d’un environnement virtuel de développement. Nous pouvons maintenant conclure que ceux-ci doivent également être ajoutés à notre application dans Heroku.

Notre nouvelle hypothèse devient: «Le service devrait fonctionner comme prévu dans les environnements locaux et de production.» Pour tester cette hypothèse et prendre en compte le nouveau changement, nous devons redémarrer notre worker qui devrait laisser l’application récupérer les nouvelles variables d’environnement.

$ heroku ps:restart worker.1 -a heroku-troubleshootingRestarting worker.1 dyno on ⬢ heroku-troubleshooting... done$ heroku ps:exec -d worker.1 -a heroku-troubleshootingEstablishing credentials... doneConnecting to worker.1 on ⬢ heroku-troubleshooting...The programs included with the Debian GNU/Linux system are free software;the exact distribution terms for each program are described in theindividual files in /usr/share/doc/*/copyright.Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extentpermitted by applicable law.~ $ curl -I localhost:5000HTTP/1.0 200 OKContent-Type: text/html; charset=utf-8Content-Length: 148Server: Werkzeug/1.0.1 Python/3.8.5Date: Sun, 23 Aug 2020 00:50:35 GMT~ $ curl localhost:5000I've sold to these famous people from server worker.1: 

Julianne Dench
Sylvester Dern
Greta Keitel
Dustin Tautou
Penelope Monroe

Nous avons maintenant vérifié que le conteneur local fonctionne comme prévu après avoir réexécuté nos tests précédents. Néanmoins, nos journaux et notre navigateur concluent que l’application ne fonctionne toujours pas comme prévu.

Journaux de nos boucles internes:

2020-08-23T00:50:35.931678+00:00 app[worker.1]: 127.0.0.1 - - [23/Aug/2020 00:50:35] "[37mHEAD / HTTP/1.1[0m" 200 -
2020-08-23T00:50:41.807629+00:00 app[worker.1]: 127.0.0.1 - - [23/Aug/2020 00:50:41] "[37mGET / HTTP/1.1[0m" 200 -

Journaux de notre navigateur externe:

2020-08-23T00:50:47.151656+00:00 heroku[router]: at=error code=H14 desc="No web processes running" method=GET path="/" host=heroku-troubleshooting.herokuapp.com request_id=a9421f09-94f2-4822-9608-f2f63ffe5123 fwd="68.251.62.251" dyno= connect= service= status=503 bytes= protocol=https
Message d’erreur

Avec l’application fonctionnant dans le conteneur, il semble que tout devrait fonctionner correctement, hypothétiquement. L’utilisation de notre réexécution des tests précédente et une analyse plus approfondie de l’erreur «Aucun processus Web en cours d’exécution» nous donne un autre indice. Il existe deux types de dynamomètres, et nous l’avons déployé en tant que dynamomètre «worker». En regardant le Documentation, nous pouvons en conclure que nous avons peut-être déployé sur le mauvais type de ressource qui n’obtient pas d’entrée. En théorie, Heroku devrait fournir un port externe dynamique si nous configurons le dynamomètre en tant que «web» au lieu de «worker». Il est temps de tester le redéploiement en tant que dynamomètre «Web».

heroku ps:scale worker=0 -a heroku-troubleshootingheroku container:push web -a heroku-troubleshootingheroku container:release web -a heroku-troubleshootingheroku ps:scale web=1 -a heroku-troubleshooting

La navigation sur le site donne toujours la même conclusion que nos tests initiaux, mais maintenant nos journaux observés nous donnent notre prochain fil à étudier.

2020-08-23T01:03:12.890612+00:00 app[web.1]:  * Serving Flask app "myapp" (lazy loading)2020-08-23T01:03:12.890619+00:00 app[web.1]:  * Environment: development2020-08-23T01:03:12.890620+00:00 app[web.1]:  * Debug mode: on2020-08-23T01:03:12.905580+00:00 app[web.1]:  * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)2020-08-23T01:03:12.906860+00:00 app[web.1]:  * Restarting with stat2020-08-23T01:03:13.661451+00:00 app[web.1]:  * Debugger is active!2020-08-23T01:03:13.662395+00:00 app[web.1]:  * Debugger PIN: 196-723-0972020-08-23T01:04:07.990856+00:00 heroku[web.1]: Error R10 (Boot timeout) -> Web process failed to bind to $PORT within 60 seconds of launch2020-08-23T01:04:08.016515+00:00 heroku[web.1]: Stopping process with SIGKILL2020-08-23T01:04:08.224860+00:00 heroku[web.1]: Process exited with status 1372020-08-23T01:04:08.290383+00:00 heroku[web.1]: State changed from starting to crashed2020-08-23T01:04:08.293139+00:00 heroku[web.1]: State changed from crashed to starting“Web process failed to bind to $PORT”

Nous pouvons conclure que nous devons lier notre processus à une variable plutôt qu’à notre port Flask par défaut. C’est assez simple à corriger dans notre application Python. Si nous avons la bonne variable mappée dans notre environnement de production et que notre application l’utilise comme configuration de port, alors nous devrions avoir un site entièrement fonctionnel. Pour tester cela, nous pouvons passer la variable d’environnement de l’hôte et l’utiliser dans notre app.run argument.

myapp.pyif __name__ == "__main__":    port = int(os.getenv("PORT", 5000))    app.run(debug=True, host='0.0.0.0', port=port)
Des personnes célèbres

Et voila! Notre analyse du test montre que l’application fonctionne à la fois localement et lorsqu’elle est déployée. Une dernière validation pour nous assurer que nos changements continuent à fonctionner également dans notre environnement de développement.

Des gens plus célèbres.

Close Menu