协作头脑风暴
brainstorming
在实现前澄清需求、探索设计选择,并形成清晰的开发方向。
devops-gitlab-ci【开发与工程】GitLab CI/CD pipeline configuration and management. Covers: .gitlab-ci.yml structure, stages, jobs, parallel, needs, artifacts, cache, child/parent pipelines, multi-project pipelines, rules, YAML anchors, templates, includes, Container Registry, Kaniko, security scanning (SAST, DAST, dependency), runner management, autoscaling. Do NOT use for: GitHub…
独立 GitHub 快照 · 自动分类 · 以原作者文档为准
Design, implement, and optimize GitLab CI/CD pipelines for automated build, test, security scanning, and deployment workflows.
Exact user phrases: "GitLab CI", ".gitlab-ci.yml", "GitLab pipeline", "GitLab runner", "GitLab CI/CD", "CI pipeline", "GitLab security scanning", "SAST", "DAST", "child pipeline", "multi-project pipeline".
Writes to .gitlab-ci.yml, template YAML files in ci/ directory, and GitLab CI configuration.
YAML configuration with GitLab CI keywords, ready to commit.
No preamble. No postamble. No explanations.
| Runner Type | Use Case | Pros | Cons |
|---|---|---|---|
| Shared (SaaS) | Small projects, quick start | No maintenance, auto-scaled | Limited concurrency, no custom HW |
| Group runner | Team-level standardization | Shared across projects, custom config | Team manages |
| Specific runner | Single project, custom HW | Full control, GPU/dedicated | Per-project maintenance |
| Auto-scaling (Docker Machine) | Variable load | Cost efficient, elastic | Complex setup |
| Auto-scaling (Kubernetes) | K8s-native, containerized | Native K8s, efficient | Requires K8s cluster |
| Scan Type | GitLab Template | When to Run | Best For |
|---|---|---|---|
| SAST | Jobs/SAST.gitlab-ci.yml | Every PR | Source code vulnerabilities |
| Secret Detection | Jobs/Secret-Detection.gitlab-ci.yml | Every push | Hardcoded credentials |
| Dependency Scanning | Jobs/Dependency-Scanning.gitlab-ci.yml | Every PR | Known CVEs in deps |
| Container Scanning | Jobs/Container-Scanning.gitlab-ci.yml | After image build | Image vulnerabilities |
| DAST | Jobs/DAST.gitlab-ci.yml | Deploy to review app | Runtime vulnerabilities |
| API Fuzzing | Jobs/API-Fuzzing.gitlab-ci.yml | Deploy to staging | API security testing |
| License Scanning | Jobs/License-Scanning.gitlab-ci.yml | Every PR | License compliance |
stages:
- build
- test
- security
- package
- deploy
variables:
DOCKER_DRIVER: overlay2
DOCKER_TLS_CERTDIR: ""
DEPLOY_ENV: $CI_COMMIT_BRANCH
REGISTRY: $CI_REGISTRY_IMAGE
TAG: $CI_COMMIT_SHORT_SHA
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- node_modules/
- .npm/
build-app:
stage: build
image: node:22-alpine
script:
- npm ci --cache .npm --prefer-offline
- npm run build
artifacts:
paths:
- dist/
- node_modules/
expire_in: 1 hour
unit-test:
stage: test
image: node:22-alpine
script:
- npm ci --cache .npm --prefer-offline
- npm run test:ci
artifacts:
when: always
reports:
junit: test-results/junit.xml
coverage_report:
coverage_format: cobertura
path: coverage/cobertura-coverage.xml
include:
- template: Jobs/SAST.gitlab-ci.yml
- template: Jobs/Secret-Detection.gitlab-ci.yml
- template: Jobs/Dependency-Scanning.gitlab-ci.yml
sast:
stage: security
variables:
SAST_EXCLUDED_PATHS: node_modules, tests, vendor
SEARCH_MAX_DEPTH: 10
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH == "main"
secret_detection:
stage: security
rules:
- if: $CI_COMMIT_BRANCH == "main"
dependency_scanning:
stage: security
variables:
DS_DEFAULT_ANALYZERS: node
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
container-build:
stage: package
image:
name: gcr.io/kaniko-project/executor:v1.23.2-debug
entrypoint: [""]
script:
- /kaniko/executor
--context $CI_PROJECT_DIR
--dockerfile $CI_PROJECT_DIR/Dockerfile
--destination $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA
--destination $CI_REGISTRY_IMAGE:$CI_COMMIT_BRANCH
--cache=true
--cache-ttl=168h
container-scan:
stage: security
image:
name: aquasec/trivy:latest
entrypoint: [""]
script:
- trivy image --severity CRITICAL,HIGH --format sarif --output trivy.sarif $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA
artifacts:
reports:
sast: trivy.sarif
needs: [container-build]
.deploy-template: &deploy-template
image: alpine:3.19
before_script:
- apk add --no-cache kubectl gettext
- kubectl config use-context $KUBE_CONTEXT
script:
- envsubst < k8s/deployment.yaml | kubectl apply -f -
- kubectl rollout status deployment/$APP_NAME -n $NAMESPACE --timeout=5m
deploy-staging:
<<: *deploy-template
stage: deploy
variables:
NAMESPACE: staging
APP_NAME: myapp-staging
environment:
name: staging
url: https://staging.example.com
rules:
- if: $CI_COMMIT_BRANCH == "main"
when: manual
allow_failure: true
deploy-production:
<<: *deploy-template
stage: deploy
variables:
NAMESPACE: production
APP_NAME: myapp-prod
environment:
name: production
url: https://example.com
rules:
- if: $CI_COMMIT_BRANCH == "main"
when: manual
allow_failure: false
# Parent .gitlab-ci.yml
stages:
- triggers
trigger-backend:
stage: triggers
trigger:
include: backend/.gitlab-ci.yml
strategy: depend
rules:
- changes:
- backend/**/*
trigger-frontend:
stage: triggers
trigger:
include: frontend/.gitlab-ci.yml
strategy: depend
rules:
- changes:
- frontend/**/*
trigger-lib:
stage: triggers
trigger:
project: team/lib
branch: main
strategy: depend
rules:
- if: $CI_COMMIT_BRANCH == "main"
.job-template:
image: node:22-alpine
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- node_modules/
before_script:
- npm ci
lint:
extends: .job-template
stage: test
script:
- npm run lint
test:
extends: .job-template
stage: test
script:
- npm run test:ci
coverage: '/Coverage: \d+\.\d+%/'
review-deploy:
stage: deploy
image: alpine:3.19
script:
- apk add --no-cache kubectl
- kubectl create namespace review-$CI_MERGE_REQUEST_IID || true
- kubectl set image deployment/app app=$CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA -n review-$CI_MERGE_REQUEST_IID || true
environment:
name: review/$CI_MERGE_REQUEST_IID
url: https://review-$CI_MERGE_REQUEST_IID.example.com
on_stop: review-cleanup
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
review-cleanup:
stage: deploy
script:
- kubectl delete namespace review-$CI_MERGE_REQUEST_IID || true
environment:
name: review/$CI_MERGE_REQUEST_IID
action: stop
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
when: manual
Writing secrets in .gitlab-ci.yml exposes them in pipeline logs and to anyone with repo access. Use CI/CD variables with masked and protected flags.
Without caching, every pipeline reinstalls all dependencies. Cache node_modules, .m2, vendor/bundle. Use key based on lock file hash.
The only/except keywords are deprecated and less flexible. Use rules for all conditional logic. Rules support if, changes, exists, and variables.
Artifacts without expire_in accumulate indefinitely, filling storage quotas. Set expire_in on all artifacts (1 hour for temporary, 30 days for reports).
Automatically deploying to production without manual approval risks deploying broken code. Always use when: manual for production deployments.
Docker-in-Docker requires privileged mode, which bypasses security controls. Use Kaniko for container builds on K8s runners. Reserve DIND for when Docker socket is required.
Having too many stages slows pipeline execution (each stage completes before next starts). Combine independent jobs into the same stage using needs for DAG execution.
GitLab CI: single app for code + CI, auto DevOps, built-in registry, self-managed option. GitHub Actions: marketplace ecosystem, simpler YAML, tighter GitHub integration. GitLab CI excels at security scanning and Kubernetes deployment.
GitLab CI: cloud-native, simpler configuration, integrated with GitLab. Jenkins: extensive plugin ecosystem, any VCS, mature. GitLab CI is better for GitLab-centric teams; Jenkins for complex multi-VCS organizations.
DIND: requires privileged mode, full Docker daemon, faster for multi-stage builds. Kaniko: rootless, no privileged mode, K8s-native, safer. Use Kaniko on K8s runners, DIND on Docker executors.
After completing this skill:
| Decision | GitLab CI Only | Hybrid (Jenkins/Argo) |
|---|---|---|
| Simplicity | Single platform | Multiple platforms to manage |
| Pipeline as Code | Full .gitlab-ci.yml | Split across tools |
| Features | Built-in registry, SAST, DAST | Specialized tool capabilities |
| Migration | Easier (all in GitLab) | Harder (multi-tool) |
| Cost | Same license | Additional licenses |
| Best for | GitLab-native orgs | Existing CI investments |
| Aspect | Docker Executor | Shell Executor |
|---|---|---|
| Isolation | Full container isolation | Same host environment |
| Reproducibility | High (same image everywhere) | Low (depends on host state) |
| Performance | Slight overhead | Native speed |
| Cache | Volume mounts | Direct filesystem access |
| Use case | Standard CI/CD | Performance-critical or hardware access |
stages:
- build
- test
- package
- deploy
variables:
DOCKER_DRIVER: overlay2
DOCKER_TLS_CERTDIR: ""
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- node_modules/
- .npm/
build-app:
stage: build
image: node:22
script:
- npm ci
- npm run build
artifacts:
paths:
- dist/
expire_in: 1 week
unit-test:
stage: test
image: node:22
script:
- npm ci
- npm run test:unit
- npm run test:coverage
coverage: '/All files[^|]*\|[^|]*\s+([\d\.]+)/'
artifacts:
reports:
coverage_report:
coverage_format: cobertura
path: coverage/cobertura-coverage.xml
deploy-staging:
stage: deploy
image: alpine/k8s:1.28
script:
- kubectl set image deployment/app app=${CI_REGISTRY_IMAGE}:${CI_COMMIT_SHORT_SHA}
environment:
name: staging
url: https://staging.example.com
only:
- main
trigger-downstream:
stage: deploy
trigger:
project: myorg/e2e-tests
branch: main
strategy: depend
only:
- tags
#!/usr/bin/env bash
wait_for_pipeline() {
local project_id=$1
local pipeline_id=$2
local token=$3
while true; do
status=$(curl -sf --header "PRIVATE-TOKEN: $token" \
"https://gitlab.com/api/v4/projects/${project_id}/pipelines/${pipeline_id}" \
| jq -r '.status')
echo "Pipeline status: $status"
case "$status" in
success) return 0 ;;
failed|canceled) return 1 ;;
*) sleep 10 ;;
esac
done
}
before_script for long setup that should be in the image — bloats every job.gitlab-ci.yml — always use CI/CD Settings → Variablesonly/except instead of rules — rules are more flexible and easier to debugneeds dependencies — jobs wait unnecessarily for sibling completionDOCKER_BUILDKIT=1 and inline cache in GitLab runnersparallel:matrix to run across multiple runner instancesGIT_STRATEGY: fetch instead of clone — saves time for large repositoriesGIT_DEPTH: 50 to shallow-clone only the needed commit history