构建一套开放的RestfullApi
Mr.Lee 2025-04-25 19:00:23 ProjectkubernetesHelmRestfullApi
这个项目的起因呢.... 最近在开发中需要测试Api接口, 就想找个公共的Api对付一下完事. 结果标准的Restfull风格的api都需要鉴权. 所以就想自己实现一套. 反正后面也用的到......
正好借着这个机会, 完整记录一下应用接入kubernetes
的过程.
# 初始化项目
# 初始化目录结构
init-project
# 初始化后端项目
cd internal
mvn archetype:generate \
-DinteractiveMode=false \
-DarchetypeArtifactId=maven-archetype-quickstart \
-DgroupId=com.striveonger.common \
-DartifactId=own-open-apis \
-Dversion=1.0.0
mv own-open-apis/* ./
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
因为这次只需要后端的API服务. 所以....就只构建后端代码喽~
# 业务逻辑
所谓 OpenApis
就是实现Restfull风格的几个接口, 篇幅原因, 我这里, 只放Controller的代码片段. 整体业务逻辑也不是复杂, 感兴趣的小伙伴, 可以来这里看完整的内容.
@PostMapping("/storage/{key}")
public Result save(@PathVariable String key, @RequestBody Map<String, String> data) {
log.info("save key={}, data={}", key, data);
String value = data.get("value");
String description = data.get("description");
if (storage.containsKey(key)) {
return Result.fail().message("key already exists");
}
StorageDTO item = new StorageDTO();
item.setKey(key);
item.setValue(value);
item.setDescription(description);
item.setCreateTime(LocalDateTime.now());
item.setUpdateTime(LocalDateTime.now());
storage.put(key, item);
return Result.success();
}
@DeleteMapping("/storage/{key}")
public Result delete(@PathVariable String key) {
log.info("delete key={}", key);
StorageDTO item = storage.remove(key);
if (Objects.nonNull(item)) {
return Result.success();
} else {
return Result.status(ResultStatus.NOT_FOUND);
}
}
@PutMapping("/storage/{key}")
public Result update(@PathVariable String key, @RequestBody Map<String, String> data) {
log.info("update key={}, data={}", key, data);
String value = data.get("value");
String description = data.get("description");
if (storage.containsKey(key)) {
StorageDTO item = storage.get(key);
item.setValue(value);
item.setDescription(description);
item.setUpdateTime(LocalDateTime.now());
storage.put(key, item);
return Result.success();
}
return Result.status(ResultStatus.NOT_FOUND);
}
@GetMapping("/storage/all")
public Result list() {
log.info("list");
List<StorageDTO> list = storage.values().stream().sorted(Comparator.comparing(StorageDTO::getUpdateTime)).toList();
return Result.success().data(list);
}
@GetMapping("/storage/{key}")
public Result get(@PathVariable String key) {
log.info("get key={}", key);
if (Objects.isNull(key)) {
return Result.fail().message("key is null");
}
StorageDTO item = storage.get(key);
if (Objects.nonNull(item)) {
return Result.success().data(item);
} else {
return Result.status(ResultStatus.NOT_FOUND);
}
}
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
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
# 构建镜像
# Dockerfile
FROM ubuntu:22.04
LABEL maintainer="striveonger@163.com"
LABEL version="1.0.0"
COPY --from=ubuntu/jre:17_edge /opt/java /opt/java
# 安装时区包
RUN apt-get update && apt-get install -y tzdata
# 设置时区变量及符号链接
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
WORKDIR /opt/app
COPY internal/target/own-open-apis.jar /opt/app/app.jar
COPY ci-cd/docker/app.sh /opt/app/app.sh
ENV JAVA_HOME=/opt/java
EXPOSE 18081
ENTRYPOINT ["sh", "/opt/app/app.sh"]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# app.sh
#!/bin/bash
set -x
# JVM Configuration
JAVA_OPT="${JAVA_OPT}"
APP_PORT=18081
APP_JAR="app.jar"
JAVA_OPT="${JAVA_OPT} -Dserver.port=${APP_PORT}"
exec ${JAVA_HOME}/bin/java ${JAVA_OPT} -jar ${APP_JAR}
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
# 启动容器
# 1. 后端打包
❯ mvn -f internal/pom.xml clean package -DskipTests
[INFO] Scanning for projects...
........
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1.509 s
[INFO] Finished at: 2025-04-27T17:34:14+08:00
[INFO] ------------------------------------------------------------------------
# 2. 构建镜像
❯ docker build -f ./ci-cd/docker/Dockerfile -t striveonger/own-open-apis:$(cat ./ci-cd/VERSION) .
[+] Building 0.4s (12/12) FINISHED docker:orbstack
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 362B 0.0s
=> [internal] load metadata for docker.io/library/ubuntu:22.04 0.0s
=> [internal] load metadata for docker.io/ubuntu/jre:17_edge 0.0s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> FROM docker.io/ubuntu/jre:17_edge 0.0s
=> [stage-0 1/5] FROM docker.io/library/ubuntu:22.04 0.0s
=> [internal] load build context 0.1s
=> => transferring context: 32.69MB 0.1s
=> CACHED [stage-0 2/5] COPY --from=ubuntu/jre:17_edge /opt/java /opt/java 0.0s
=> CACHED [stage-0 3/5] WORKDIR /opt/app 0.0s
=> [stage-0 4/5] COPY internal/target/own-open-apis.jar /opt/app/app.jar 0.1s
=> [stage-0 5/5] COPY ci-cd/docker/app.sh /opt/app/app.sh 0.0s
=> exporting to image 0.1s
=> => exporting layers 0.1s
=> => writing image sha256:c4489d7a5518618ead9e7856d9d6c85c519df4611653d30f54153a21a754b0a3 0.0s
=> => naming to docker.io/striveonger/own-open-apis:1.0.0
# 3. 启动镜像
❯ docker run -dit --name own-open-apis \
-m 256M --memory-swap=-1 \
--net mova --ip 10.13.144.165 \
docker.io/striveonger/own-open-apis:1.0.0
13534f5291dc36525166fb49a7267c19bd50fb7476ee1aee24befeec3299d00c
# 4. 测试接口
❯ curl -X POST 'http://10.13.144.165:18081/api/v1/storage/test' -H 'Content-Type: application/json' -d '{
"value": "{\"a\": 1, \"b\": 2}",
"description": "临时存储的信息"
}'
{"state":200,"code":"SUCCESS","message":"Success","now":"2025-04-27 18:27:13.807"}
# 5. 删除容器
❯ docker stop own-open-apis
own-open-apis
❯ docker rm own-open-apis
own-open-apis
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
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
# Helm Chart
❯ cd ci-cd/helm/
❯ ll
total 0
❯ helm create own-open-apis
Creating own-open-apis
❯ mv own-open-apis/* ./
❯ rm -rf own-open-apis/
❯ tree .
.
├── Chart.yaml
├── charts
├── templates
│ ├── NOTES.txt
│ ├── _helpers.tpl
│ ├── deployment.yaml
│ ├── ingress.yaml
│ └── service.yaml
└── values.yaml
3 directories, 7 files
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# Chart.yaml
# Helm Chart API 版本 (v2 支持模板继承等功能)
apiVersion: "v2"
# Chart 名称(应用标识)
name: "own-open-apis"
# 维护者信息
maintainers:
- name: "Striveonger"
email: "striveonger@163.com"
# Chart 描述(建议包含应用功能说明)
description: "A Helm chart for Kubernetes"
# 资源类型(application 或 library)
type: "application"
# Chart 版本
version: "1.0.0"
# 应用版本(对应容器镜像版本)
appVersion: "1.0.0"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Chart.yaml
文件, 用来描述 Chart包(own-open-apis.tgz
) 信息的文件
# values.yaml
app:
replicaCount: 1
resources:
cpu: "2"
memory: "256Mi"
port: 18081
image:
repository: "striveonger/own-open-apis"
tag: "1.0.0"
pullPolicy: "IfNotPresent"
ingress:
enabled: true
path: "/own/open/api"
env:
- name: SPRING_PROFILES_ACTIVE
value: "dev"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
values.yaml
文件中包含了chart的 默认值, 可以理解为启动chart包的配置文件. 一般启动时, 根据不同环境用--set
来动态配置
# _helpers.tpl
{{/*
Expand the name of the chart.
*/}}
{{- define "own-open-apis.name" -}}
{{- default .Chart.Name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "own-open-apis.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Common labels
*/}}
{{- define "own-open-apis.labels" -}}
helm.sh/chart: {{ include "own-open-apis.chart" . }}
{{ include "own-open-apis.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}
{{/*
Selector labels
*/}}
{{- define "own-open-apis.selectorLabels" -}}
app.kubernetes.io/name: {{ include "own-open-apis.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/namespace: {{ .Release.Namespace }}
{{- end }}
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
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
定义
chart
中复用的模板辅助对象.
# deployment.yaml
---
apiVersion: "apps/v1"
kind: "Deployment"
metadata:
namespace: "{{ .Release.Namespace }}"
name: "{{ include "own-open-apis.name" . }}-deployment"
labels:
{{ include "own-open-apis.labels" . | indent 4 }}
spec:
replicas: {{ .Values.app.replicaCount }}
selector:
matchLabels:
{{ include "own-open-apis.selectorLabels" . | indent 6 }}
template:
metadata:
namespace: "{{ .Release.Namespace }}"
name: "{{ include "own-open-apis.name" . }}"
labels:
{{ include "own-open-apis.labels" . | indent 8 }}
spec:
containers:
- name: "{{ include "own-open-apis.name" . }}"
image: "{{ .Values.app.image.repository }}:{{ .Values.app.image.tag }}"
imagePullPolicy: "{{.Values.app.image.pullPolicy }}"
resources:
limits:
cpu: "{{ .Values.app.resources.cpu }}"
memory: "{{ .Values.app.resources.memory }}"
requests:
cpu: "{{ .Values.app.resources.cpu }}"
memory: "{{ .Values.app.resources.memory }}"
ports:
- containerPort: {{ .Values.app.port }}
protocol: "TCP"
volumeMounts:
- name: node-logs
mountPath: /var/log/own-open-apis
env:
{{- toYaml .Values.env | nindent 10 }}
restartPolicy: "Always"
volumes:
- name: node-logs
hostPath:
path: /var/log/own-open-apis
type: "DirectoryOrCreate"
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
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
创建Kubernetes 工作负载的基本清单.
# service.yaml
apiVersion: "v1"
kind: "Service"
metadata:
namespace: "{{ .Release.Namespace }}"
name: {{ include "own-open-apis.name" . }}-service
labels:
{{ include "own-open-apis.labels" . | indent 4 }}
spec:
type: "ClusterIP"
ports:
- protocol: "TCP"
port: {{ .Values.app.port }}
targetPort: {{ .Values.app.port }}
sessionAffinity: "ClientIP"
selector:
{{ include "own-open-apis.selectorLabels" . | indent 4 }}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
稳定的Kubernetes内部的网络标识
# ingress.yaml
{{- if .Values.app.ingress.enabled -}}
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: {{ include "own-open-apis.name" . }}-ingress
namespace: {{ .Release.Namespace }}
labels:
{{ include "own-open-apis.labels" . | indent 4 }}
spec:
entryPoints:
- web
routes:
- match: PathPrefix(`{{ .Values.app.ingress.path }}`)
kind: Rule
services:
- name: {{ include "own-open-apis.name" . }}-service
port: {{ .Values.app.port }}
{{- end -}}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
配置访问网关(Nginx 的平替~)
# NOTES.txt
Helm chart `{{ .Chart.Name }}-{{ .Chart.AppVersion }}` install success...
1
# 安装 Chart
❯ helm install own-open-apis ci-cd/helm/ -n own --create-namespace
NAME: own-open-apis
LAST DEPLOYED: Sun Apr 27 22:41:49 2025
NAMESPACE: own
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
Helm chart `own-open-apis-1.0.0` install success...
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9