Concourse - Opérations#
Concourse Workers#
Se connecter à une instance Concourse#
Choisissez votre environnement :
export ZONE_NAME='infra-example'
export ZONE_NAME='ocb-example'
Se connecter à l'instance infra ou client avec le client fly :
fly switch -z "${ZONE_NAME}" -n main # Instance infra
fly switch -z "${ZONE_NAME}" -n main -c # Instance client
fly login
fly status
fly userinfo
Exemples :
$ fly switch -z ocb-example -n main
$ fly login
$ fly status
logged in successfully
$ fly userinfo
username team/role
gabitbol build/member,build/viewer,cron/member,cron/viewer,dashboard/member,dashboard/viewer,deploy/member,deploy/viewer,main/owner,test/member,test/viewer
Lister les instances de workers#
Après s'être connecté sur l'instance Concourse, vous pouvez lister les workers de cette zone :
fly workers
fly workers --details
Exemples :
# Sur une instance infra
$ fly workers
name containers platform tags team state version age
caascad-ci-workers-infra-ocb-example-0 16 linux none none running 2.3 8d
# Sur une instance cliente
$ fly workers
name containers platform tags team state version age
caascad-ci-workers-client-ocb-example-0 2 linux none none running 2.3 8d
Il est aussi possible de se connecter sur ces Concourse Workers.
Se connecter à une VM Worker#
Choisissez votre environnement :
export ZONE_NAME='infra-stg'
export TARGET="infra"
export ZONE_NAME='infra-prd'
export TARGET="infra"
export ZONE_NAME='ocb-example'
export TARGET="infra"
export ZONE_NAME='ocb-example'
export TARGET="client"
Se connecter via le bastion de la zone, sur le serveur Worker souhaité en définissant son ID par exemple :
export WORKER_ID=0
ssh -t "cloud@bst.${ZONE_NAME}.caascad.com" ssh "caascad-ci-workers-${TARGET}-${ZONE_NAME}-${WORKER_ID}.${ZONE_NAME}.caascad.com"
export WORKER_ID=1
ssh -t "cloud@bst.${ZONE_NAME}.caascad.com" ssh "caascad-ci-workers-${TARGET}-${ZONE_NAME}-${WORKER_ID}.${ZONE_NAME}.caascad.com"
...
Warning
Si vous rencontrez l'erreur WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! lors de la connexion SSH vers le worker, il faut supprimer le fingerprint ssh du fichier ~/.ssh/known_hosts sur le bastion :
ssh -t "cloud@bst.${ZONE_NAME}.caascad.com" ssh-keygen -R "caascad-ci-workers-${TARGET}-${ZONE_NAME}-${WORKER_ID}.${ZONE_NAME}.caascad.com"
Tester les services#
Les services concourse-worker et vault-agent doivent être enabled et started :
sudo systemctl status vault-agent
sudo systemctl status concourse-worker
Vérifier les logs de ces 2 services :
sudo journalctl -u vault-agent
sudo journalctl -u concourse-worker
En cas de problème lire la section troubleshooting.
(Re)démarrer le service#
Pour (re)démarrer le service concourse-worker :
sudo systemctl restart concourse-worker ; sleep 3s ; sudo systemctl status concourse-worker
Warning
Le redémarrage du service concourse-worker peut prendre jusqu'à 15/20min.
Check the connectivity between the worker and Concourse Web#
Get the hostname of the Concourse Web service:
systemctl cat concourse-worker | grep 'Environment=CONCOURSE_TSA_HOST'
export CI_HOST=$(systemctl cat concourse-worker | awk -F '[=:]' '/Environment=CONCOURSE_TSA_HOST/ {print $3}')
Use nc to check if the hostname is reachable on tcp/2222:
nc -vz ${CI_HOST} 2222
You should see something like this:
Connection to ci.ocb-esagso.caascad.com (90.84.183.1) 2222 port [tcp/*] succeeded!
If not, the problem could be:
- Concourse Web (hosted in Kubernetes) is down ;
- The DNS resolution don't work or give a false IP ;
- Ingresses on Kubernetes is misconfigured ;
- The loadbalancer provided by the Cloud Provider is down or misconfigured ;
- There is an ACL blocking the request.
Increase size of data disk#
Warning
On High I/O Flavor (i3.xxx), the disk size can not be increased.
-
Increase
concourse_extra_disksfirst disk (default to 150 GB) by editing the tfvarsEdit the
fe_concourse_workersconfiguration (on FE) or theaws_concourse_workersconfiguration (on AWS) to override the following variable:envs: ["ocb-example"]: configurations: [=~"^fe_concourse_workers_(infra|client)$"]: tfvars: { workers: [string]: #FEConcourseWorker & { concourse_extra_disks: [200] } } -
Stop Concourse services and apply this change
On Concourse Worker:
sudo systemctl stop concourse-workerOn your computer:
cd contexts/caascad trackbone apply -z ocb-example -c fe_concourse_workers_infra trackbone apply -z ocb-example -c fe_concourse_workers_client -
Login into the worker (read this section for more information)
-
Grow the partition
sudo xfs_growfs /dev/vdbThen, start the concourse-worker on Concourse Worker:
sudo systemctl start concourse-worker
Provisioning#
L'installation des Concourse workers a lieu en deux étapes :
- Configuration de la partie OS via cloud-init ;
- Récupération des binaires (y compris concourse) pour les services via le script
worker-provisioning.
OS#
L'essentiel se trouve dans ~/git/caascad/terraform/lib/configurations/<cloud_provider>_concourse_workers/files/cloud-init.yaml.
- Une distribution basée sur Ubuntu 22.04.xx est utilisée ;
- Le disque est formaté avec une partition
xfs; - Les packages sont mis à jour régulièrement (unattended-upgrades).
Services#
Au démarrage, le script ~/git/caascad/terraform/lib/configurations/<cloud_provider>_concourse_workers/files/worker-provisioning.sh est executé via le service worker-provisioning :
- Les binaires sont téléchargés dans un dossier
downloadstemporaire dans/opt/concourse/worker; - Ils sont déplacés dans
/usr/local/bin; - Les services sont activés et lancés.
Troubleshooting#
Lorsqu'une nouvelle instance est initialisée, il est déjà possible de s'y connecter et de vérifier l'avancement de l'installation via :
sudo tail -f /var/log/syslog
Après le premier redémarrage, il est aussi possible de retrouver des informations dans les fichiers /var/log/cloud-init*.
Vérifier que le disque supplémentaire est bien présent lsblk, df ou cat /proc/mounts. Si ce n'est pas le cas, vérifier /etc/fstab. Une ligne de configuration doit avoir y être ajoutée par l'instruction mounts dans le fichier cloud-init.
Reconstruire un worker#
Warning
A n'exécuter que si les autres actions n'ont pas fonctionné.
Identifier le ou les workers en stalled dans la liste des instances.
Entrer dans le shell Trackbone de la configuration Concourse worker (en fonction du CSP et infra ou client)
cd ~/git/caascad/terraform/envs-ng
nix-shell
cd contexts/caascad/
trackbone shell -z ${ZONE_NAME} -c (fe|aws)_concourse_workers_infra # CI Infra
trackbone shell -z ${ZONE_NAME} -c (fe|aws)_concourse_workers_client # CI Client
Si besoin, récupérer la liste des objets terraform
terraform init
terraform state list
Taint du worker à reconstruire (les simples quotes sont importantes) et reapply
# Exemple sur une infra Flexible Engine
terraform taint 'flexibleengine_compute_instance_v2.instance["2"]'
./apply.sh
Attendre environ 15 minutes pour vérifier que les workers soient en statut running.
Si la machine n'apparaît pas, il faut vérifier son provisioning.
Problèmes connus#
/opt/concourse/worker/* full#
Description : Alarmes dans Karma type FileSystemSpaceFillingUp / FileSystemSpaceAlmostOutOfSpace impactant les points de montage :
/opt/concourse/worker/opt/concourse/worker/volumes
Solutions :
-
Se connecter sur le serveur Concourse Worker impacté
-
Vérifier la saturation des systèmes de fichiers Concourse :
df -h /opt/concourse/worker/{,volumes} -
Redémarrer le service
concourse-workeravec le paramètreCONCOURSE_GARDEN_DESTROY_CONTAINERS_ON_STARTUP=true:Warning
Le redémarrage du service
concourse-workerpeut prendre jusqu'à 15/20min.sudo systemctl set-environment CONCOURSE_CONTAINERD_DESTROY_CONTAINERS_ON_STARTUP=true sudo systemctl restart concourse-worker.service | head -n 3 sudo systemctl unset-environment CONCOURSE_CONTAINERD_DESTROY_CONTAINERS_ON_STARTUP sleep 3s ; systemctl status concourse-worker.service | head -n 3 df -h /opt/concourse/worker/{,volumes}
Annuler les builds d'un worker saturé#
Description : Il peut arriver parfois que l'ensemble des workers d'une chaîne CI Concourse soit saturé de jobs / builds. Exemple de l'évolution de l'indicateur sur le nombre de jobs planifié sur les Workers Concourse :

Liste de symptômes identifiés :
-
Allongement de la durée de traitement de chacun de ces builds, avec comme causes possible :
Solutions (Classées par ordre de préférence) :
-
Se connecter sur l'instance concourse impactée
-
Lister les builds en
Pendingsur les dernières 24h par exemple (On peut filtrer sur les statutssucceeded,pending,started,failed,aborted,errored) :fly builds -a -c 50000 --since="$(date '+%Y-%m-%d %H:%M:00' -d '1 day ago')" | grep -E '\s+pending\s+' -
Si l'on souhaite annuler un build en particulier :
fly abort-build -b <GLOBAL_BUILD_ID> -
On peut mettre en pause les pipelines les plus exigeantes en ressource :
caascad-alert-prepare(Team =deploy)fly pause-pipeline --team deploy -p 'caascad-alert-prepare'
-
Il est aussi possible d'isoler progressivement un worker (Il n'acceptera plus de nouveaux jobs, sachant que pour le remettre en service il faudra le redémarrer) :
fly land-worker -w <WORKER NAME> -
Enfin, si l'on souhaite annuler tous ces builds
pending(avec brutalité) :j=1 ; for i in $(fly builds -a -c 50000 --since="$(date '+%Y-%m-%d %H:%M:00' -d '1 day ago')" | grep -E 'caascad-alert-prepare\/.*\s+pending\s+' | awk '{print $1}') ; do echo "$(fly abort-build -b ${i}): n°$(((j++))) (id = ${i})" ; done
Image fetching failed#
Description : Certains jobs d'une pipeline se terminent en échec avec le statut failed et un message d'erreur du type image fetching failed ou d'un message plus verbeux via la CLI :
selected worker: caascad-ci-workers-infra-infra-prd-1
fetching docker-registry.ocb-corp.caascad.com/internal/nix@sha256:3f4b9595bb7d4f259de01ff67fe30098e0586b2a1452f58ca6a8ac965f4b91e8
ERRO[0000] download failed: get image: GET https://docker-registry.ocb-corp.caascad.com/v2/internal/nix/manifests/sha256:3f4b9595bb7d4f259de01ff67fe30098e0586b2a1452f58ca6a8ac965f4b91e8: MANIFEST_UNKNOWN: manifest unknown; map[]
image fetching failed
errored
Solutions :
-
Se connecter sur l'instance concourse impactée
-
Vérifier la présence des erreurs
image fetching faileddans les 3 dernières heures par exemple (Copier-coller le bloc de texte) :fly builds -a -c 3000 --since="$(date '+%Y-%m-%d %H:%M:00' -d '3 hours ago')" |grep -E '\s+errored\s+' |awk '{print $1" "$2" "$4 " "$7}' |while read line do echo "${line}" |awk '{print "\n==>> " $3 "\tPipeline/Job/ID=" $2 "\tTeam="$4}' fly watch -b $(echo "${line}" |awk '{print $1'}) |grep 'image fetching failed' -A 1 -B 4 done - Si l'erreur est confirmée, on peut soit :
- Agir sur la pipeline en la forçant à revérifier les ressources du type
registry-imageexport TEAM='pipeline_team' fly switch -z "${ZONE_NAME}" -n "${TEAM}" # Instance infra fly switch -z "${ZONE_NAME}" -n "${TEAM}" -c # Instance client fly check-resource-type -r <pipeline_name>/registry-image - Agir sur toutes les pipelines en erreur jusqu'à J-1 par exemple
fly builds -a -c 3000 --since="$(date '+%Y-%m-%d %H:%M:00' -d '1 day ago')" |grep -E '\s+errored\s+' |awk -F ' +|/' '{print $2 "\t" $9}' |sort |uniq |while read line do fly switch -z "${ZONE_NAME}" -n $(echo "${line}" |awk '{print $2}') fly check-resource-type -r $(echo "${line}" |awk '{print $1}')/registry-image done
- Agir sur la pipeline en la forçant à revérifier les ressources du type
Nettoyer les caches d'une pipeline#
Pour nettoyer les caches d'une pipeline, il faut :
- Se connecter sur l'instance concourse impactée
- Identifier la pipeline et la team impactée
fly pipelines -a PIPELINE='ma_pipeline' TEAM='la_team' - Se positionner sur la team précédemment identifiée
fly switch -z "${ZONE_NAME}" -n "${TEAM}" # Instance infra fly switch -z "${ZONE_NAME}" -n "${TEAM}" -c # Instance cliente fly login - Nettoyer le cache des ressources
fly resources -p "${PIPELINE}" | awk '{print $1}' | while read line do yes | fly clear-resource-cache -r "${PIPELINE}/${line}" fly check-resource -r "${PIPELINE}/${line}" done - Nettoyer le cache des tâches
# Identifier les jobs de la pipeline et définir le job fly jobs -p "${PIPELINE}" # Définir le job pour lequel on veut nettoyer le cache des tâches JOB='mon_job' fly get-pipeline -p "${PIPELINE}" | yq -r --arg JOB "${JOB}" '.jobs[] | select(.name == $JOB) | .plan[].task | select (.)' | while read line do yes | fly clear-task-cache -j "${PIPELINE}/${JOB}" -s "${line}" done
Worker stalled#
Description : Il arrivera parfois qu'un worker Concourse passera à l'état stalled (Injoignable, inaccessible, non fonctionnel). Si cet état est normal pendant un provisioning de worker, il ne l'est pas quand l'infrastructure Concourse est à l'état nominal, par exemple :
$ fly workers
name containers platform tags team state version age
caascad-ci-workers-infra-infra-stg-1 228 linux none none running 2.3 38d
caascad-ci-workers-infra-infra-stg-2 227 linux none none running 2.3 38d
caascad-ci-workers-infra-infra-stg-3 226 linux none none running 2.3 38d
the following workers have not checked in recently:
name containers platform tags team state version age
caascad-ci-workers-infra-infra-stg-0 236 linux none none stalled 2.3 38d
these stalled workers can be cleaned up by running:
fly -t infra-stg.infra prune-worker -w (name)
Solution :
-
Se connecter sur l'instance concourse impactée et vérifier l'état des workers
-
Si le worker est toujours à l'état
stalled, essayer de vous connecter sur le serveur Concourse Worker -
Si le worker est :
- Accessible :
- Vérifier l'état des services Concourse
- Redémarrer les services défaillant si nécessaire
- Inaccessible :
- Dans la console FE, tenter de redémarrer le serveur (Section
Elastic Cloud Server) - Si cela ne suffit pas, reconstruire le worker
- Dans la console FE, tenter de redémarrer le serveur (Section
- Accessible :
-
Vérifier le statut du worker (Doit être
running).
Note
Il arrive parfois que Concourse Web ne "retrouve" pas le worker, il faut alors essayer de redémarrer le Concourse Web associé
PVC PostgreSQL full#
Description : Cette problématique de saturation de la base de données est généralement causé par un afflux excessif de logs (Les logs représentent la majorité des données Concourse dans la base).
Solution : Purger les logs des pipelines qui génère ces logs.
Se connecter sur le répliqua Leader :
export ZONE="ocb-example"
export NAMESPACE="concourse-infra" # or "concourse"
kswitch "${ZONE}"
export POSTGRES_MASTER="$(kubectl get pods -n "${NAMESPACE}" -l spilo-role=master -o custom-columns=NAME:.metadata.name --no-headers)"
kubectl exec -it -n "${NAMESPACE}" "${POSTGRES_MASTER}" -- psql -U concourse
Au besoin arrêter Concourse Web :
kubectl scale --replicas=0 deployment -n ${NAMESPACE} concourse-web
Requête SQL pour lister les tables de la base concourse et les classer par taille :
SELECT table_schema, table_name, pg_relation_size('"'||table_schema||'"."'||table_name||'"')
FROM information_schema.tables
ORDER BY 3;
Les logs des pipelines sont stockés dans les tables pipeline_build_events_XXX.
Si vous prenez la décision de purger les logs d'une pipline, vous pouvez le faire avec la requête :
TRUNCATE pipeline_build_events_XXX;
Si vous avez arrêté Concourse Web, pensez à le redémarrer :
kubectl scale --replicas=1 deployment -n ${NAMESPACE} concourse-web
Concourse Web#
Redémarrer Concourse Web#
Se connecter sur le cluster K8s qui héberge le Concourse impacté :
kswitch ${ZONE_NAME}
Supprimer le pod concourse-web-xxxx en choisissant le namespace concourse-infra (Infra) ou concourse (Client) :
kubectl -n (concourse-infra|concourse) delete pods -l app=concourse-web
Gérer les pipelines#
Quelques commandes utiles dans la gestion des pipelines :
-
Lister tous les pipelines :
fly pipelines -a -
Lister tous les pipelines en pause :
fly paused-pipelines -a -
Mettre en pause une pipeline :
# Ex : Met en pause la pipeline `test-status` dans la team `test` fly pause-pipeline --team test -p 'test-status' -
Réactiver une pipeline :
# Ex : Réactive la pipeline `test-status` dans la team `test` fly unpause-pipeline --team test -p 'test-status'
Gérer les jobs#
Quelques commandes utiles dans la gestion des jobs :
-
Lister tous les jobs d'une pipeline :
# Ex : Liste les jobs de la pipeline `test-status` dans la team `test` fly jobs --team test -p 'test-status' -
Lister tous les jobs en pause d'une pipeline :
# Ex : Liste les jobs de la pipeline `test-status` dans la team `test` fly paused-jobs --team test -p 'test-status'
Gérer les builds#
Statut d'un build :
pending: Build en attente d'exécutionstarted: Build en cours d'exécutionsucceeded: Build terminé sans erreursaborted: Build interrompu par un opérateur / utilisateurfailed: Build en échec (Erreur lors de l'exécution du code du build, ex : téléchargement de dépendance Nix en échec, erreur Terraform, erreur de compilation de code, etc.)errored: Build en échec (Erreur lors de l'exécution du build sur un worker, ex : erreur de pull d'image depuis notre Docker Registry, Workers indisponible, etc.).
Quelques commandes utiles dans la gestion des builds :
-
Lister tous les builds (Permet d'obtenir le Global Build ID) :
fly builds -a -
Lister tous les builds depuis une date (Format =
AAAA-MM-JJ hh:mm:ss) :fly builds -c 50000 -a --since="$(date '+%Y-%m-%d %H:%M:00' -d '1 day ago')" fly builds -c 50000 -a --since="$(date '+%Y-%m-%d %H:%M:00' -d '4 hours ago')" fly builds -c 50000 -a --since="$(date '+%Y-%m-%d %H:%M:00' -d '10 minutes ago')" -
Lister tous les derniers builds avec le statut
failed:fly builds -c 50000 -a --since="$(date '+%Y-%m-%d %H:%M:00' -d '4 hours ago')" | grep -E '\s+failed\s+' -
Lister tous les derniers builds avec le statut
errored:fly builds -c 50000 -a --since="$(date '+%Y-%m-%d %H:%M:00' -d '4 hours ago')" | grep -E '\s+errored\s+' -
Consulter les logs d'un build via la CLI (plus verbeux que la WebUI) :
# Avec son Global Build ID, pas besoin de préciser la team fly watch -b <GLOBAL_BUILD_ID> # Avec le numéro de Build, sa pipeline et son job # Penser à basculer sur la team auquel est rattaché ce build # Ex : Affiche le log du build `666` de la pipeline `test-status` dans je job `job-test` dans la team `test` fly switch -n test fly watch -j 'test-status/job-test' -b 666 -
Annuler un build en cours :
# Avec son Global Build ID, pas besoin de préciser la team fly abort-build -b <GLOBAL_BUILD_ID> # Avec le numéro de Build, sa pipeline et son job # Penser à basculer sur la team auquel est rattaché ce build # Ex : Affiche le log du build `666` de la pipeline `test-status` dans je job `job-test` dans la team `test` fly switch -n test fly abort-build -j 'test-status/job-test' -b 666 -
Se connecter dans le conteneur d'un build en cours pour debug :
# Avec son Global Build ID, pas besoin de préciser la team fly hijack -b <GLOBAL_BUILD_ID> # Avec le numéro de Build, sa pipeline et son job # Penser à basculer sur la team auquel est rattaché ce build # Ex : Affiche le log du build `666` de la pipeline `test-status` dans je job `job-test` dans la team `test` fly hijack -j 'test-status/job-test' -b 666
Get the admin password#
You only need to do this if you don't have admin rights on Concourse set in Keycloak. You would need to use the admin user to do admin operations like configure teams, list workers...
The simplest way to fetch the password (which differ between every deployments) is by running a kubectl command:
kswitch $ZONE_NAME
# Change 'concourse-infra' to 'concourse' for client instance
kubectl get secret -n concourse-infra concourse-web -o jsonpath='{.data.local-users}' | base64 -d | cut -d ':' -f2
Pour l'exporter :
export CONCOURSE_ADD_LOCAL_USER="$(kubectl get secret -n concourse-infra concourse-web -o jsonpath='{.data.local-users}' | base64 -d)"
Known issues#
Connection reset by peer#
Description: concourse-web logs are flooded with this king of messages:
{"timestamp":"2020-05-11T10:28:50.912441788Z","level":"info","source":"tsa","message":"tsa.connection.handshake-failed",
"data":{"error":"write tcp 172.16.0.95:2222->172.16.0.65:17448: write: connection reset by peer","remote":"172.16.0.65:17448","session":"332"}}
Solution: this message is caused by the FE loadbalancer healthcheck. This causes the log level info to be really noisy. We can try to disable healthcheck on the loadbalancer or changed the log level to error.

