Striveonger

vuePress-theme-reco Mr.Lee    2015 - 2025
Striveonger Striveonger
主页
分类
  • 文章
  • 笔记
  • 工具
标签
时间轴
author-avatar

Mr.Lee

264

Article

134

Tag

主页
分类
  • 文章
  • 笔记
  • 工具
标签
时间轴

构建一套开放的RestfullApi

vuePress-theme-reco Mr.Lee    2015 - 2025

构建一套开放的RestfullApi

Mr.Lee 2025-04-25 19:30:23 favorKubernetesHelmRestfullApi

这个项目的起因呢.... 最近在开发中需要测试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

因为这次只需要后端的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

# 构建镜像

# 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

# 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

# 启动容器

# 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

# 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

charts 允许将大型应用拆分成多个组件(如前端、后端、数据库),每个组件对应一个子 Chart

# 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

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

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

定义 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

创建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

稳定的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

配置访问网关(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

# 成果展示

image-20250427224401107