部署Vue3项目到生产K8s

1. 配置Node.js和Nginx基础镜像

准备Node.js 20和Nginx的基础镜像,用于构建和运行Vue3项目。

2. Nexus新建NPM仓库

在Nexus中创建一个专门用于存储npm依赖的仓库,例如名为vue3-npm的仓库。

3. 批量上传NPM依赖到Nexus私服

将项目所需的npm依赖包批量上传到Nexus私服中,以加速构建过程并确保依赖的可用性。

4. 新建流水线

创建一个Jenkins流水线脚本,实现从代码拉取到K8s部署的全自动化流程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
pipeline {
agent any
tools {
nodejs 'node20'
}
environment {
docker_directory = 'docker'
frontend_dir = 'xjkyGPT-web'
serverport = '80' // 前端服务端口,通常为80
}
stages {
stage('scm') {
steps {
checkout([$class: 'GitSCM', branches: [[name: '${branch}']], doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [], userRemoteConfigs: [[credentialsId: 'xjcares_git', url: 'http://172.31.70.132:33788/xjcares/xjkygpt.git']]])
sh 'echo \'nodefinished:<\'$(date +%Y-%m-%d\\ %H:%M:%S)\'>\''
}
}

stage('Build Frontend') {
steps {
sh '''
cd ${frontend_dir}
pwd
# 备份原始 package-lock.json (如果存在),以防 npm ci 修改它
if [ -f package-lock.json ]; then
cp package-lock.json package-lock.json.bak
fi

rm -rf node_modules

# 使用Node.js 20容器构建,并在容器内配置npm指向Nexus
# 关键修改1: -w /app (而不是 /app/xjkyGPT-web)
docker run --rm -v $(pwd):/app -w /app 172.31.10.118/library/node:20-alpine sh -c "
# 在容器内也创建 .npmrc 文件
# 关键修改2: 使用 //registry-url/:_authToken 格式
cat > .npmrc <<INNEREOF
registry=http://172.31.70.135:8081/repository/vue3-npm/
//172.31.70.135:8081/repository/vue3-npm/:_authToken=YWRtaW46eGpreUAxMjMsLg==
fetch-retries=3
fetch-retry-factor=1.5
fetch-retry-maxtimeout=60000
fetch-retry-mintimeout=10000
INNEREOF

# 查看 .npmrc 内容以确认
echo '--- .npmrc contents ---'
cat .npmrc
echo '-----------------------'

# 查看当前目录内容 (现在应该是 xjkyGPT-web 的内容)
echo '--- Current directory contents ---'
ls -la
echo '----------------------------------'

# 查看 package.json 以确认项目结构
echo '--- package.json ---'
cat package.json
echo '------------------'

# 安装依赖 (使用 Nexus 私服)
npm ci --registry=http://172.31.70.135:8081/repository/vue3-npm/ --unsafe-perm=true --allow-root

# 执行构建
npm run build
"
'''
sh 'echo \'nodefinished:<\'$(date +%Y-%m-%d\\ %H:%M:%S)\'>\''
}
}

stage('build and push image') {
steps {
sh '''
docker login -u admin -p Harbor12345 172.31.10.118
cd ${frontend_dir}

# 生成唯一tag,使用时间戳+构建号
BUILD_TAG=$(date +%Y%m%d-%H%M%S)-${BUILD_NUMBER}
REPOSITORY=172.31.10.118/xjcares/${module}-frontend:${BUILD_TAG}

# 创建Dockerfile用于构建前端镜像
cat > Dockerfile <<EOF
FROM 172.31.10.118/library/nginx:alpine
ENV TZ=PRC
RUN ln -snf /usr/share/zoneinfo/\$TZ /etc/localtime && echo \$TZ > /etc/timezone
COPY dist/ /usr/share/nginx/html/
COPY nginx.conf /etc/nginx/conf.d/default.conf
EOF

if [ ! -f nginx.conf ]; then
cat > nginx.conf <<'NGINXEOF'
events {
worker_connections 1024;
}

http {
include /etc/nginx/mime.types;
default_type application/octet-stream;

server {
listen 80;
root /usr/share/nginx/html;
index index.html;

# 代理 API 请求到后端服务
location /api/ {
# 去掉 proxy_pass 末尾的斜杠
proxy_pass http://gateway-service:8888;

# 添加重写规则,去掉 /api/ 前缀
rewrite ^/api/(.*)$ /$1 break;

proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}

# 处理Vue Router History模式
location / {
try_files $uri $uri/ /index.html;
}

# 静态资源缓存 - FIXED: escaped backslash
location ~* \\.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
}
}
NGINXEOF
fi

docker build -t $REPOSITORY .
docker push $REPOSITORY

# 将镜像tag保存到workspace根目录下的文件
echo "$REPOSITORY" > ../IMAGE_TAG.txt

docker logout 172.31.10.118
docker rmi $REPOSITORY
'''
}
}

stage('deploy') {
steps {
kubeconfig(caCertificate: '', credentialsId: 'snowlotus-kubeconfig', serverUrl: '') {
sh '''
pwd

# 从workspace根目录读取IMAGE_TAG.txt
NEW_IMAGE_TAG=$(cat IMAGE_TAG.txt)

# 创建K8s部署YAML文件
cat > frontend-deployment.yaml <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
name: ${module}-frontend
labels:
app: ${module}-frontend
spec:
replicas: ${instanceNum}
selector:
matchLabels:
app: ${module}-frontend
template:
metadata:
labels:
app: ${module}-frontend
spec:
containers:
- name: ${module}-frontend
image: $NEW_IMAGE_TAG
ports:
- containerPort: 80
resources:
requests:
memory: ${memory}
cpu: "0.1"
limits:
memory: ${memory}
cpu: "0.5"
EOF

# 创建Service YAML文件
cat > frontend-service.yaml <<EOF
apiVersion: v1
kind: Service
metadata:
name: ${module}-frontend-svc
labels:
app: ${module}-frontend
spec:
selector:
app: ${module}-frontend
ports:
- protocol: TCP
port: 80
targetPort: 80
nodePort: 31833
type: NodePort
EOF

# 应用新配置
kubectl delete -f frontend-deployment.yaml --ignore-not-found=true
kubectl delete -f frontend-service.yaml --ignore-not-found=true
kubectl apply -f frontend-deployment.yaml
kubectl apply -f frontend-service.yaml
'''
}
}
}
}
}

5. 项目中新建.npmrc文件

在前端项目的根目录下创建.npmrc文件,配置npm私服地址,使得本地、测试环境和生产环境都能统一使用私服进行依赖安装。

1
2
3
4
5
6
registry=http://172.31.70.135:8081/repository/vue3-npm/
//172.31.70.135:8081/repository/vue3-npm/:_authToken=YWRtaW46eGpreUAxMjMsLg==
fetch-retries=3
fetch-retry-factor=1.5
fetch-retry-maxtimeout=60000
fetch-retry-mintimeout=10000

6. 结果

整个流程实现了在流水线中自动构建、打包、封装成镜像,并部署到K8s集群。无需手动上传node_modules,无需手动配置Nginx的前端路由,前端项目作为一个独立的K8s Pod运行。