Aller au contenu

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.

  1. Increase concourse_extra_disks first disk (default to 150 GB) by editing the tfvars

    Edit the fe_concourse_workers configuration (on FE) or the aws_concourse_workers configuration (on AWS) to override the following variable:

    envs: ["ocb-example"]: configurations: [=~"^fe_concourse_workers_(infra|client)$"]: tfvars: {
            workers: [string]: #FEConcourseWorker & {
                    concourse_extra_disks: [200]
        }
    }
    
  2. Stop Concourse services and apply this change

    On Concourse Worker:

    sudo systemctl stop concourse-worker
    

    On 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
    

  3. Login into the worker (read this section for more information)

  4. Grow the partition

    sudo xfs_growfs /dev/vdb
    

    Then, start the concourse-worker on Concourse Worker:

    sudo systemctl start concourse-worker
    

Provisioning#

L'installation des Concourse workers a lieu en deux étapes :

  1. Configuration de la partie OS via cloud-init ;
  2. 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 downloads temporaire 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 :

  1. Se connecter sur le serveur Concourse Worker impacté

  2. Vérifier la saturation des systèmes de fichiers Concourse :

    df -h /opt/concourse/worker/{,volumes}
    

  3. Redémarrer le service concourse-worker avec le paramètre CONCOURSE_GARDEN_DESTROY_CONTAINERS_ON_STARTUP=true :

    Warning

    Le redémarrage du service concourse-worker peut 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 : Concourse Worker High Job Schedule rate

Liste de symptômes identifiés :

  • Allongement de la durée de traitement de chacun de ces builds, avec comme causes possible :

    • Load Average élevée sur les workers Concourse Worker High Load Average

    • Usage I/O (Disque / Réseau) élevée sur les workers. Concourse Worker High I/O usage

Solutions (Classées par ordre de préférence) :

  1. Se connecter sur l'instance concourse impactée

  2. Lister les builds en Pending sur les dernières 24h par exemple (On peut filtrer sur les statuts succeeded, 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+'
    

  3. Si l'on souhaite annuler un build en particulier :

    fly abort-build -b <GLOBAL_BUILD_ID>
    

  4. 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'
      
  5. 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>
    

  6. 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 :

  1. Se connecter sur l'instance concourse impactée

  2. Vérifier la présence des erreurs image fetching failed dans 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
    

  3. Si l'erreur est confirmée, on peut soit :
    • Agir sur la pipeline en la forçant à revérifier les ressources du type registry-image
      export 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
      

Nettoyer les caches d'une pipeline#

Pour nettoyer les caches d'une pipeline, il faut :

  1. Se connecter sur l'instance concourse impactée
  2. Identifier la pipeline et la team impactée
    fly pipelines -a
    
    PIPELINE='ma_pipeline'
    TEAM='la_team'
    
  3. 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
    
  4. 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
    
  5. 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 :

  1. Se connecter sur l'instance concourse impactée et vérifier l'état des workers

  2. Si le worker est toujours à l'état stalled, essayer de vous connecter sur le serveur Concourse Worker

  3. Si le worker est :

  4. 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écution
  • started : Build en cours d'exécution
  • succeeded : Build terminé sans erreurs
  • aborted : Build interrompu par un opérateur / utilisateur
  • failed : 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.