Node.js项目CI/CD #
一、项目结构 #
text
nodejs-project/
├── src/
│ └── index.js
├── test/
│ └── index.test.js
├── Dockerfile
├── package.json
├── package-lock.json
└── .gitlab-ci.yml
二、基础配置 #
package.json #
json
{
"name": "nodejs-project",
"version": "1.0.0",
"scripts": {
"start": "node src/index.js",
"test": "jest",
"test:coverage": "jest --coverage",
"lint": "eslint src/",
"build": "webpack --mode production"
},
"devDependencies": {
"eslint": "^8.0.0",
"jest": "^29.0.0",
"webpack": "^5.0.0"
}
}
Dockerfile #
dockerfile
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build
FROM node:18-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./
EXPOSE 3000
CMD ["npm", "start"]
三、完整CI/CD配置 #
yaml
stages:
- lint
- test
- build
- docker
- deploy
variables:
NODE_VERSION: "18"
DOCKER_IMAGE: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
.node_template:
image: node:${NODE_VERSION}-alpine
cache:
key:
files:
- package-lock.json
paths:
- node_modules/
before_script:
- npm ci --prefer-offline
lint:
extends: .node_template
stage: lint
script:
- npm run lint
allow_failure: true
test:
extends: .node_template
stage: test
script:
- npm run test:coverage
coverage: '/All files[^|]*\|[^|]*\s+([\d\.]+)/'
artifacts:
when: always
reports:
junit: test-results/junit.xml
coverage_report:
coverage_format: cobertura
path: coverage/cobertura-coverage.xml
test_integration:
extends: .node_template
stage: test
services:
- name: postgres:14
alias: db
- name: redis:7
alias: cache
variables:
POSTGRES_DB: test
POSTGRES_USER: test
POSTGRES_PASSWORD: test
DATABASE_URL: postgres://test:test@db:5432/test
REDIS_URL: redis://cache:6379
script:
- npm run test:integration
build:
extends: .node_template
stage: build
script:
- npm run build
artifacts:
paths:
- dist/
expire_in: 1 week
docker_build:
stage: docker
image: docker:latest
services:
- docker:dind
before_script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
script:
- docker pull $CI_REGISTRY_IMAGE:latest || true
- docker build --cache-from $CI_REGISTRY_IMAGE:latest -t $DOCKER_IMAGE .
- docker push $DOCKER_IMAGE
- |
if [ "$CI_COMMIT_BRANCH" == "main" ]; then
docker tag $DOCKER_IMAGE $CI_REGISTRY_IMAGE:latest
docker push $CI_REGISTRY_IMAGE:latest
fi
only:
- main
- develop
deploy_staging:
stage: deploy
image: bitnami/kubectl:latest
environment:
name: staging
url: https://staging.example.com
script:
- kubectl config set-cluster k8s --server="$KUBE_URL" --insecure-skip-tls-verify=true
- kubectl config set-credentials admin --token="$KUBE_TOKEN"
- kubectl config set-context default --cluster=k8s --user=admin
- kubectl config use-context default
- |
cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: nodejs-app
namespace: staging
spec:
replicas: 2
selector:
matchLabels:
app: nodejs-app
template:
metadata:
labels:
app: nodejs-app
spec:
containers:
- name: nodejs-app
image: $DOCKER_IMAGE
ports:
- containerPort: 3000
env:
- name: NODE_ENV
value: staging
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
EOF
- kubectl rollout status deployment/nodejs-app -n staging
only:
- develop
deploy_production:
stage: deploy
image: bitnami/kubectl:latest
environment:
name: production
url: https://example.com
script:
- kubectl config set-cluster k8s --server="$KUBE_URL" --insecure-skip-tls-verify=true
- kubectl config set-credentials admin --token="$KUBE_TOKEN"
- kubectl config set-context default --cluster=k8s --user=admin
- kubectl config use-context default
- kubectl set image deployment/nodejs-app nodejs-app=$DOCKER_IMAGE -n production
- kubectl rollout status deployment/nodejs-app -n production
only:
- main
when: manual
四、配置详解 #
1. 模板复用 #
yaml
.node_template:
image: node:${NODE_VERSION}-alpine
cache:
key:
files:
- package-lock.json
paths:
- node_modules/
before_script:
- npm ci --prefer-offline
2. 缓存优化 #
yaml
cache:
key:
files:
- package-lock.json
paths:
- node_modules/
3. 测试覆盖率 #
yaml
test:
coverage: '/All files[^|]*\|[^|]*\s+([\d\.]+)/'
artifacts:
reports:
coverage_report:
coverage_format: cobertura
path: coverage/cobertura-coverage.xml
4. Docker构建 #
yaml
docker_build:
script:
- docker pull $CI_REGISTRY_IMAGE:latest || true
- docker build --cache-from $CI_REGISTRY_IMAGE:latest -t $DOCKER_IMAGE .
- docker push $DOCKER_IMAGE
5. Kubernetes部署 #
yaml
deploy:
script:
- kubectl set image deployment/nodejs-app nodejs-app=$DOCKER_IMAGE
- kubectl rollout status deployment/nodejs-app
五、高级配置 #
1. 多版本测试 #
yaml
test:
parallel:
matrix:
- NODE_VERSION: [16, 18, 20]
image: node:${NODE_VERSION}-alpine
script:
- npm test
2. 安全扫描 #
yaml
include:
- template: Jobs/SAST.gitlab-ci.yml
- template: Jobs/Dependency-Scanning.gitlab-ci.yml
dependency_scanning:
stage: test
3. 代码质量 #
yaml
code_quality:
stage: test
image: docker:latest
services:
- docker:dind
variables:
CODE_QUALITY_IMAGE: "registry.gitlab.com/gitlab-org/ci-cd/codequality:0.85.26"
script:
- docker pull --quiet "$CODE_QUALITY_IMAGE" || docker pull "$CODE_QUALITY_IMAGE"
- |
docker run --env SOURCE_CODE="$PWD" \
--volume "$PWD":/code \
--volume /var/run/docker.sock:/var/run/docker.sock \
"$CODE_QUALITY_IMAGE" /code
artifacts:
reports:
codequality: gl-code-quality-report.json
4. 自动发布 #
yaml
publish_npm:
stage: deploy
image: node:18-alpine
script:
- npm config set //registry.npmjs.org/:_authToken $NPM_TOKEN
- npm publish --access public
only:
- tags
六、环境变量配置 #
GitLab CI/CD变量 #
| 变量 | 说明 |
|---|---|
CI_REGISTRY |
GitLab容器镜像仓库地址 |
CI_REGISTRY_USER |
镜像仓库用户名 |
CI_REGISTRY_PASSWORD |
镜像仓库密码 |
KUBE_URL |
Kubernetes API地址 |
KUBE_TOKEN |
Kubernetes访问令牌 |
NPM_TOKEN |
npm发布令牌 |
项目变量设置 #
- 进入Settings → CI/CD → Variables
- 添加以下变量:
NPM_TOKEN(Masked)KUBE_URLKUBE_TOKEN(Masked)
七、最佳实践 #
1. 使用alpine镜像 #
yaml
image: node:18-alpine
2. 缓存依赖 #
yaml
cache:
paths:
- node_modules/
3. 使用artifacts传递产物 #
yaml
build:
artifacts:
paths:
- dist/
4. 条件部署 #
yaml
deploy_production:
only:
- main
when: manual
5. 环境管理 #
yaml
deploy_staging:
environment:
name: staging
url: https://staging.example.com
八、故障排查 #
1. 依赖安装失败 #
yaml
before_script:
- npm ci --prefer-offline || npm install
2. 测试超时 #
yaml
test:
timeout: 30m
3. Docker构建失败 #
yaml
docker_build:
script:
- docker build --no-cache -t $DOCKER_IMAGE .
下一步 #
现在你已经掌握了Node.js项目CI/CD,接下来让我们学习 Docker集成!
最后更新:2026-03-28