La page précédente déployait indicat à la main depuis ton portable, avec un kubeconfig qui est cluster-admin. Cette page confie le travail à GitHub Actions : un push sur main construit l'image, la tague avec le SHA du commit, la pousse dans le registre et la déploie, avec une identité qui ne peut toucher qu'à .
Avant de commencer
Un ServiceAccount pour le pipeline
apiVersion: v1kind: ServiceAccountmetadata: name: ci-deploy---apiVersion: rbac.authorization.k8s.io/v1kind: Rolemetadata: name: ci-deployrules: - apiGroups: [""] resources: [pods, pods/log, services] verbs: [get, list, watch, create, update, patch, delete] - apiGroups: [apps] resources: [deployments, replicasets] verbs: [get, list, watch, create, update, patch, delete] - apiGroups: [batch] resources: [jobs] verbs: [get, list, watch, create, update, patch, delete] - apiGroups: [networking.k8s.io] resources: [ingresses] verbs: [get, list, watch, create, update, patch, delete] - apiGroups: [autoscaling] resources: [horizontalpodautoscalers] verbs: [get, list, watch, create, update, patch, delete]---apiVersion: rbac.authorization.k8s.io/v1kind: RoleBindingmetadata: name: ci-deployroleRef: apiGroup: rbac.authorization.k8s.io kind: Role name: ci-deploysubjects: - kind: ServiceAccount name: ci-deploy namespace: $kubectl -n apply -f deploy/ci-rbac.yaml$kubectl -n kube-system auth can-i get secrets --as=system:serviceaccount::ci-deployno
Un kubeconfig pour le pipeline
apiVersion: v1kind: Secretmetadata: name: ci-deploy-token annotations: kubernetes.io/service-account.name: ci-deploytype: kubernetes.io/service-account-token$kubectl -n apply -f deploy/ci-token.yaml$TOKEN=$(kubectl -n get secret ci-deploy-token -o jsonpath='{.data.token}' | base64 -d)$CA=$(kubectl -n get secret ci-deploy-token -o jsonpath='{.data.ca\.crt}')$cat > ci-kubeconfig.yaml <<EOF$apiVersion: v1$kind: Config$clusters:$ - name: k3s$ cluster: { server: "https://:6443", certificate-authority-data: $CA }$users:$ - name: ci-deploy$ user: { token: $TOKEN }$contexts:$ - name: ci$ context: { cluster: k3s, user: ci-deploy, namespace: }$current-context: ci$EOF$kubectl --kubeconfig ci-kubeconfig.yaml auth can-i create deploymentsyes
Le secret de CI et le registre
$gh secret set KUBECONFIG_B64 --repo < <(base64 -w0 ci-kubeconfig.yaml)$rm ci-kubeconfig.yamlkustomize : un seul endroit pour le tag d'image
apiVersion: kustomize.config.k8s.io/v1beta1kind: Kustomizationnamespace: resources: [migrate.yaml, deployment.yaml, service.yaml, ingress.yaml, hpa.yaml]images: - name: newTag: latest$kubectl kustomize deploy/ | grep -c 'image: :latest'2
Le pipeline
name: deployon: push: branches: [main]permissions: contents: read packages: writejobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: docker/setup-buildx-action@v3 - uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - uses: docker/build-push-action@v6 with: context: . push: true tags: :${{ github.sha }} cache-from: type=gha cache-to: type=gha,mode=max deploy: needs: build runs-on: ubuntu-latest environment: production env: KUBECONFIG_B64: ${{ secrets.KUBECONFIG_B64 }} steps: - uses: actions/checkout@v4 - name: Kubeconfig run: | mkdir -p ~/.kube echo "$KUBECONFIG_B64" | base64 -d > ~/.kube/config chmod 600 ~/.kube/config - name: Deploy run: | sed -i "s|newTag: .*|newTag: ${{ github.sha }}|" deploy/kustomization.yaml kubectl delete job indicat-migrate --ignore-not-found kubectl apply -k deploy/ -l app=indicat-migrate kubectl wait --for=condition=complete job/indicat-migrate --timeout=300s kubectl apply -k deploy/ kubectl rollout status deploy/indicat --timeout=300s- Le minimum : lire le code, écrire des paquets. Les permissions par défaut du token sont plus larges ; les énoncer ici les restreint pour ce workflow.
- Un tag par commit. Le même SHA dans
git log, dans le registre et danskubectl get deploy -o wide: plus à deviner ce qui tourne. - Cache de couches dans le service de cache de GitHub ; un build qui n'a changé que le code applicatif prend quelques secondes.
- Rattache le job à l'environnement
production: ses secrets, ses règles de protection (étape suivante), et un historique de déploiements dans la barre latérale du dépôt. - Le Job d'abord, seul, sélectionné par son label ; puis tout le reste. Une migration qui échoue arrête le pipeline avant qu'un pod change.
$git add deploy/ .github/ .gitlab-ci.yml 2>/dev/null; git commit -m "ci: deploy on push"$git push origin main$gh run list --repo --workflow deploy --limit 1 --json conclusion --jq '.[0].conclusion'success
$[ "$(kubectl -n get deploy indicat -o jsonpath='{.spec.template.spec.containers[0].image}' | cut -d: -f2)" = "$(git rev-parse HEAD)" ] && echo running HEADrunning HEAD
Si le job deploy échoue
Le log du job contient l'erreur kubectl. Par ordre de probabilité :
Unable to connect to the server::6443 n'est pas joignable depuis le runner (pare-feu, adresse privée).forbidden: il manque au Role une ressource quedeploy/contient maintenant ; ajoute-la dansci-rbac.yaml.error: no objects passed to applyà l'étape du Job : le labelapp: indicat-migratemanque surmigrate.yaml.- Le Job de migrations échoue :
kubectl logs job/indicat-migratedepuis ton portable ; le Deployment n'a pas été touché. rollout statusexpire :ImagePullBackOffparce que le nom d'image dansdeploy/diffère de(kustomize n'a rien remplacé), ou les pods ratent leurs sondes (le dépannage de la page précédente s'applique).
Environnements et protection
Dépôt Settings → Environments → production : ajoute des Required reviewers, et restreins Deployment branches à main. Settings → Branches → Add rule sur main : exige une pull request et des status checks.
Rollback
Chaque commit a son image. Revenir en arrière, c'est déployer une plus ancienne : relance le pipeline du dernier bon commit, ou, depuis ton portable, annule le rollout.
$gh run list --repo --workflow deploy --limit 5$gh run rerun <run-id> --repo $kubectl -n rollout undo deploy/indicatTerminé
Un push sur main construit :sha, migre, déploie, et s'arrête à la première erreur, avec une identité qui ne voit pas au-delà de . Ton kubeconfig admin reste sur ton portable.