java微服务部署

1、java代码

java服务名称统一格式

1、提交代码、使用服务名称命名
	java-app-producer
	java-app-consumer
	java-app-daem1

健康检查接口

添加健康检查、方便k8s探针探测

添加一个健康检查接口、使用curl命令传递、传递1就关闭服务、传递0就开启服务、传递status检查服务状态、k8s探针就探测health接口

livenessProbe:
  httpGet:
    path: /api/actuator/health   # 生产者
    port: 8081
  initialDelaySeconds: 10
  periodSeconds: 5
readinessProbe:
  httpGet:
    path: /api/actuator/health   # 生产者
    port: 8081
  initialDelaySeconds: 5
  periodSeconds: 5


消费者的 path 改为 /test/actuator/health,端口改为 8082
	关闭服务(变为不健康):  curl "http://<服务IP>:<端口>/test/health?cmd=1"
	开启服务(变为健康):  curl "http://<服务IP>:<端口>/test/health?cmd=0"
	查询健康状态:  curl "http://<服务IP>:<端口>/test/health?cmd=0"
	探针健康检查(K8s自动调用):  curl "http://<服务IP>:<端口>/test/actuator/health"

1、生产者

下面是生产者的代码

# 创建目录
mkdir -p daemapp-1/java-app1-producer/src/main/java/com/example/controller
mkdir -p daemapp-1/java-app1-producer/src/main/resources

# Dockerfile
cat > daemapp-1/java-app1-producer/Dockerfile << 'EOF'
FROM openjdk:11.0.7
LABEL maintainer="jigaobo jigaobo@qq.com"

RUN  mkdir -p   /jigaobo  &&  mkdir /app

WORKDIR /app

COPY target/java-app-producer-1.0.jar /app/

EXPOSE 8081
CMD ["java", "-Duser.timezone=Asia/Shanghai", "-jar", "/app/java-app-producer-1.0.jar", "--spring.config.location=/app/config/application.yml"]
EOF

# pom.xml
cat > daemapp-1/java-app1-producer/pom.xml << 'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>java-app-producer</artifactId>
    <version>1.0</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.6.13</version>
    </parent>

    <properties>
        <java.version>11</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
            <version>2021.0.4.0</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>
EOF

# App1Application.java
cat > daemapp-1/java-app1-producer/src/main/java/com/example/App1Application.java << 'EOF'
package com.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@SpringBootApplication
@EnableDiscoveryClient
public class App1Application {
    public static void main(String[] args) {
        SpringApplication.run(App1Application.class, args);
    }
}
EOF

# TestController.java
cat > daemapp-1/java-app1-producer/src/main/java/com/example/controller/TestController.java << 'EOF'
package com.example.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/test")
public class TestController {

    // 健康状态标志,默认健康
    private static volatile boolean healthy = true;

    @GetMapping("/call")
    public String callApp1() {
        return "调用业务逻辑";
    }

    // 健康状态控制接口
    @GetMapping("/health")
    public String health(@RequestParam String cmd) {
        if ("1".equals(cmd)) {
            healthy = false;
            return "服务已关闭";
        } else if ("0".equals(cmd)) {
            healthy = true;
            return "服务已开启";
        } else if ("status".equalsIgnoreCase(cmd)) {
            return healthy ? "服务健康" : "服务不健康";
        } else {
            return "未知命令";
        }
    }

    // K8s 探针专用健康检查接口
    @GetMapping("/actuator/health")
    public String actuatorHealth() {
        return healthy ? "UP" : "DOWN";
    }
}
EOF

# application.yml
cat > daemapp-1/java-app1-producer/src/main/resources/application.yml << 'EOF'
server:
  port: 8081
spring:
  application:
    name: java-app-producer
  cloud:
    nacos:
      discovery:
        server-addr: cluster-ip-nacos:8848
EOF

# logback-spring.xml
cat > daemapp-1/java-app1-producer/src/main/resources/logback-spring.xml << 'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <!-- 取 K8s 注入的环境变量 -->
    <springProperty scope="context" name="POD_NAME" source="POD_NAME" defaultValue="unknown"/>
    <!-- 日志文件路径 = 挂载目录 + 动态文件名 -->
    <property name="LOG_FILE" value="/data/java/logs/${POD_NAME}.log"/>

    <appender name="FILE" class="ch.qos.logback.core.FileAppender">
        <file>${LOG_FILE}</file>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <root level="INFO">
        <appender-ref ref="FILE"/>
    </root>
</configuration>
EOF

2、消费者

下面是消费者的代码

# 创建目录
mkdir -p daemapp-1/java-app2-consumer/src/main/java/com/example/controller
mkdir -p daemapp-1/java-app2-consumer/src/main/java/com/example/client
mkdir -p daemapp-1/java-app2-consumer/src/main/resources

# Dockerfile
cat > daemapp-1/java-app2-consumer/Dockerfile << 'EOF'
FROM openjdk:11.0.7
LABEL maintainer="jigaobo jigaobo@qq.com"

RUN  mkdir -p   /jigaobo  &&  mkdir /app

WORKDIR /app

COPY target/java-app-consumer-1.0.jar /app/

EXPOSE 8081
#CMD ["java", "-Duser.timezone=Asia/Shanghai", "-jar", "/app/java-app-consumer-1.0.jar"]
CMD ["java", "-Duser.timezone=Asia/Shanghai", "-jar", "/app/java-app-consumer-1.0.jar", "--spring.config.location=/app/config/application.yml"]
EOF

# pom.xml
cat > daemapp-1/java-app2-consumer/pom.xml << 'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>java-app-consumer</artifactId>
    <version>1.0</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.6.13</version>
    </parent>

    <properties>
        <java.version>11</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
            <version>2021.0.4.0</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
            <version>3.1.5</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-loadbalancer</artifactId>
            <version>3.1.5</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>
EOF

# App2Application.java
cat > daemapp-1/java-app2-consumer/src/main/java/com/example/App2Application.java << 'EOF'
package com.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class App2Application {
    public static void main(String[] args) {
        SpringApplication.run(App2Application.class, args);
    }
}
EOF

# App1Client.java
cat > daemapp-1/java-app2-consumer/src/main/java/com/example/client/App1Client.java << 'EOF'
package com.example.client;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;

@FeignClient(name = "java-app-consumer")
public interface App1Client {
    
    @GetMapping("/api/hello")
    String hello(@RequestParam("name") String name);
}
EOF

# TestController.java
cat > daemapp-1/java-app2-consumer/src/main/java/com/example/controller/TestController.java << 'EOF'
package com.example.controller;

import com.example.client.App1Client;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/test")
public class TestController {
    
    @Autowired
    private App1Client app1Client;
    
    @GetMapping("/call")
    public String callApp1() {
        return app1Client.hello("Nacos");
    }
}
EOF

# application.yml
cat > daemapp-1/java-app2-consumer/src/main/resources/application.yml << 'EOF'
server:
  port: 8082
spring:
  application:
    name: java-app-consumer
  cloud:
    nacos:
      discovery:        #服务发现
        server-addr: cluster-ip-nacos:8848
EOF

# logback-spring.xml
cat > daemapp-1/java-app2-consumer/src/main/resources/logback-spring.xml << 'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <!-- 取 K8s 注入的环境变量 -->
    <springProperty scope="context" name="POD_NAME" source="POD_NAME" defaultValue="unknown"/>
    <!-- 日志文件路径 = 挂载目录 + 动态文件名 -->
    <property name="LOG_FILE" value="/data/java/logs/${POD_NAME}.log"/>

    <appender name="FILE" class="ch.qos.logback.core.FileAppender">
        <file>${LOG_FILE}</file>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <root level="INFO">
        <appender-ref ref="FILE"/>
    </root>
</configuration>
EOF

3、编译打镜像

mvn  clean	&& mvn package
docker build    -t     java-app-producer:v1   .
docker build    -t     java-app-consumer:v1   .


docker save -o /k8s/data/image/producer.tar.gz          java-app-producer:v1
docker save -o /k8s/data/image/consumer.tar.gz          java-app-consumer:v1

docker load -i /k8s/data/image/producer.tar.gz
docker load -i /k8s/data/image/consumer.tar.gz  

2、使用configmap挂载

在 Kubernetes 中使用 ConfigMap 挂载配置文件,可以让你在不重建镜像的情况下灵活修改 Spring Boot或Spring Cloud 的 application.yml 等配置。

#生产者配置
mkdir daemapp1
cat > producer/application.yml   << 'EOF'
server:
  port: 8081
spring:
  application:
    name: java-app-producer
  cloud:
    nacos:
      discovery:
        server-addr: cluster-ip-nacos:8848      #注册中心地址
EOF


#消费者配置
cat > consumer/application.yml   << 'EOF'
server:
  port: 8081
spring:
  application:
    name: java-app-consumer
  cloud:
    nacos:
      discovery:
        server-addr: cluster-ip-nacos:8848       #注册中心地址
EOF


#创建cm
kubectl create configmap java-app-producer-config --from-file=application.yml=./producer/application.yml	-n <名称空间>

kubectl create configmap java-app-consumer-config --from-file=application.yml=./consumer/application.yml  -n <名称空间>


3、pod的yaml文件

注意:先创建PVC、然后创建pod

1、pvc

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: java-logs-pvc
spec:
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 1Gi
  storageClassName: managed-nfs-storage   # 按需换成你的 SC

2. 生产者 Deployment

apiVersion: apps/v1
kind: Deployment
metadata:
  name: java-app-producer
spec:
  replicas: 1
  selector:
    matchLabels:
      app: java-app-producer
  template:
    metadata:
      labels:
        app: java-app-producer
    spec:
      nodename: k8s-33
      containers:
      - name: java-app-producer
        image: java-app-producer:v1
        ports:
        - containerPort: 8081
        env:
        - name: POD_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.name
        volumeMounts:
        - name: logs-pvc
          mountPath: /data/java/logs
        - name: config-volume
          mountPath: /app/config/application.yml
          subPath: application.yml
        livenessProbe:
          httpGet:
            path: /test/actuator/health
            port: 8081
          initialDelaySeconds: 10
          periodSeconds: 5
        readinessProbe:
          httpGet:
            path: /test/actuator/health
            port: 8081
          initialDelaySeconds: 5
          periodSeconds: 5
      volumes:
      - name: logs-pvc
        persistentVolumeClaim:
          claimName: java-logs-pvc
      - name: config-volume
        configMap:
          name: java-app-producer-config

3. 消费者 Deployment

apiVersion: apps/v1
kind: Deployment
metadata:
  name: java-app-consumer
spec:
  replicas: 1
  selector:
    matchLabels:
      app: java-app-consumer
  template:
    metadata:
      labels:
        app: java-app-consumer
    spec:
      nodename: k8s-33
      containers:
      - name: java-app-consumer
        image: java-app-consumer:v1
        ports:
        - containerPort: 8081
        env:
        - name: POD_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.name
        volumeMounts:
        - name: logs-pvc
          mountPath: /data/java/logs
        - name: config-volume
          mountPath: /app/config/application.yml
          subPath: application.yml
#        livenessProbe:
#          httpGet:
#            path: /test/actuator/health
#            port: 8081
#          initialDelaySeconds: 10
#          periodSeconds: 5
#        readinessProbe:
#          httpGet:
#            path: /test/actuator/health
#            port: 8081
          initialDelaySeconds: 5
          periodSeconds: 5
      volumes:
      - name: logs-pvc
        persistentVolumeClaim:
          claimName: java-logs-pvc
      - name: config-volume
        configMap:
          name: java-app-consumer-config

posted @ 2025-07-22 11:44  姬高波  阅读(20)  评论(0)    收藏  举报