Spring Cloud Kubernetes之實戰一配置管理

一直以來,玩springcloud的,基本都是在玩Springboot1.x,Springcloud(Dalston版)的眾多相關組件來做配置中心、服務註冊與發現,網關用的是Netflix公司對springboot做的LB,等等,但是這些東西太過沉重,複雜了。在一個以k8s為基礎的iaas服務系統,如果不用k8s的特性來做這些事,那是說不過去的。理由這就不重複述說了。一句話:減少系統服務的複雜性。

本文主要介紹springcloud結合k8s,做配置管理,避免更多服務組件的冗餘,完美填坑版!

環境:

ubuntu16.04

docker18.04

k8s1.13.x +

maven3.5.3

java1.8 +

springboot 2.1.1

spring-cloud-kubernetes:1.0.1.RELEAS

1. 前提

Ubuntu下安裝docker18.04 or 其它較高版本,k8s1.13.x及以上,jvm環境等。

2. 創建項目

Spring Cloud Kubernetes之實戰一配置管理

基礎依賴:

<code><parent>

<groupid>org.springframework.boot/<groupid>

<artifactid>spring-boot-starter-parent/<artifactid>

<version>2.1.8.RELEASE/<version>

<relativepath>

/<parent>

\t<properties>

\t\t<project.build.sourceencoding>UTF-8/<project.build.sourceencoding>

\t\t<project.reporting.outputencoding>UTF-8/<project.reporting.outputencoding>

\t\t<java.version>1.8/<java.version>

\t\t<swagger.version>2.6.1/<swagger.version>

\t\t<xstream.version>1.4.7/<xstream.version>

\t\t<pagehelper.version>4.1.6/<pagehelper.version>

\t\t<fastjson.version>1.2.51/<fastjson.version>

\t\t<shiro.version>1.3.0/<shiro.version>

\t\t

\t\t<kubernetes-client-version>5.0.0/<kubernetes-client-version>

\t\t<fabric8-kubernetes-client.version>4.6.1/<fabric8-kubernetes-client.version>

\t\t<springcloud.version>Greenwich.SR4/<springcloud.version>

\t\t<springcloud.kubernetes.version>1.1.1.RELEASE/<springcloud.kubernetes.version>

\t\t<mysql.version>5.1.46/<mysql.version>

\t/<properties>

\t<dependencymanagement>

\t\t<dependencies>

\t\t\t<dependency>

\t\t\t\t<groupid>org.springframework.cloud/<groupid>

\t\t\t\t<artifactid>spring-cloud-dependencies/<artifactid>

\t\t\t\t<version>${springcloud.version}/<version>

\t\t\t\t<type>pom/<type>

\t\t\t\t<scope>import/<scope>

\t\t\t/<dependency>

\t\t/<dependencies>

\t/<dependencymanagement>/<code>

核心依賴:

<code><dependency>

\t\t\t<groupid>org.springframework.boot/<groupid>

\t\t\t<artifactid>spring-boot-starter-web/<artifactid>

\t\t\t<exclusions>

\t\t\t\t<exclusion>

\t\t\t\t\t<groupid>org.springframework.boot/<groupid>

\t\t\t\t\t<artifactid>spring-boot-starter-tomcat/<artifactid>

\t\t\t\t/<exclusion>

\t\t\t/<exclusions>

\t\t/<dependency>

\t\t<dependency>

\t\t\t<groupid>org.springframework.boot/<groupid>

\t\t\t<artifactid>spring-boot-starter-undertow/<artifactid>

\t\t/<dependency>

\t\t

\t\t<dependency>

<groupid>org.springframework.boot/<groupid>

<artifactid>spring-boot-actuator/<artifactid>

/<dependency>



<dependency>

<groupid>org.springframework.boot/<groupid>

<artifactid>spring-boot-actuator-autoconfigure/<artifactid>

/<dependency>



<dependency>

<groupid>org.springframework.cloud/<groupid>

<artifactid>spring-cloud-starter-kubernetes-config/<artifactid>

/<dependency>



<dependency>

<groupid>org.springframework.boot/<groupid>

<artifactid>spring-boot-starter-test/<artifactid>

<scope>test/<scope>

/<dependency>



\t\t

\t\t<dependency>


<groupid>org.springframework.cloud/<groupid>

<artifactid>spring-cloud-commons/<artifactid>

/<dependency>

\t\t

\t\t<dependency>

<groupid>org.springframework.cloud/<groupid>

<artifactid>spring-cloud-kubernetes-core/<artifactid>

/<dependency>

<dependency>

<groupid>org.springframework.cloud/<groupid>

<artifactid>spring-cloud-kubernetes-discovery/<artifactid>

/<dependency>

\t\t

\t\t<dependency>

<groupid>org.springframework.cloud/<groupid>

<artifactid>spring-cloud-starter-kubernetes-ribbon/<artifactid>

/<dependency>

\t\t

\t\t<dependency>

<groupid>org.springframework.cloud/<groupid>

<artifactid>spring-cloud-starter-netflix-ribbon/<artifactid>

/<dependency>

<dependency>

<groupid>org.springframework.cloud/<groupid>

<artifactid>spring-cloud-starter-netflix-hystrix/<artifactid>


/<dependency>/<code>

**本次依賴引入配置管理、服務的發現(即消費者)。**

如果有操作redis和db的話,引入相應的依賴:

<code>

<dependency>

<groupid>org.mybatis.spring.boot/<groupid>

<artifactid>mybatis-spring-boot-starter/<artifactid>

<version>1.1.1/<version>

/<dependency>

<dependency>

\t\t\t<groupid>mysql/<groupid>

\t\t\t<artifactid>mysql-connector-java/<artifactid>

\t\t\t<version>${mysql.version}/<version>

\t\t/<dependency>





\t <dependency>

\t <groupid>com.github.pagehelper/<groupid>

\t <artifactid>pagehelper/<artifactid>

\t <version>${pageHelper.version}/<version>

\t /<dependency>



<dependency>

<groupid>com.alibaba/<groupid>

<artifactid>druid/<artifactid>

<version>1.1.3/<version>

/<dependency>



\t\t<dependency>

\t\t <groupid>org.apache.commons/<groupid>

\t\t <artifactid>commons-lang3/<artifactid>

\t\t /<dependency>

\t\t

\t\t<dependency>

\t\t <groupid>commons-collections/<groupid>

\t\t <artifactid>commons-collections/<artifactid>

\t\t <version>3.2.2/<version>

\t\t/<dependency>





\t\t<dependency>

\t\t\t<groupid>org.springframework.boot/<groupid>

\t\t\t<artifactid>spring-boot-starter-data-redis/<artifactid>

\t\t/<dependency>

\t\t<dependency>

\t\t\t<groupid>org.springframework.boot/<groupid>

\t\t\t<artifactid>spring-boot-starter-redis/<artifactid>

\t\t\t<version>1.4.7.RELEASE/<version>

\t\t/<dependency>

\t\t

\t\t<dependency>

<groupid>com.google.guava/<groupid>

<artifactid>guava/<artifactid>

<version>19.0/<version>

/<dependency>/<code>

剩下的就是構建鏡像時的插件:

<code><build>

\t\t<finalname>${project.artifactId}/<finalname>

\t\t<plugins>

\t\t\t<plugin>

\t\t\t\t<groupid>org.springframework.boot/<groupid>

\t\t\t\t<artifactid>spring-boot-maven-plugin/<artifactid>

\t\t\t\t<configuration>

\t\t\t\t\t<jvmarguments>-Dfile.encoding=UTF-8/<jvmarguments>

\t\t\t\t\t<fork>true/<fork>

\t\t\t\t/<configuration>

\t\t\t/<plugin>

\t\t\t<plugin>

\t\t\t\t<groupid>org.jacoco/<groupid>


\t\t\t\t<artifactid>jacoco-maven-plugin/<artifactid>

\t\t\t\t<version>0.7.8/<version>

\t\t\t\t<executions>

\t\t\t\t\t<execution>

\t\t\t\t\t\t<goals>

\t\t\t\t\t\t\t<goal>prepare-agent/<goal>

\t\t\t\t\t\t\t<goal>report/<goal>

\t\t\t\t\t\t/<goals>

\t\t\t\t\t/<execution>

\t\t\t\t/<executions>

\t\t\t/<plugin>

\t\t/<plugins>

\t/<build>/<code>

接下來,我們創建主類:

<code>@SpringBootApplication(scanBasePackages = { "com.leinao" })

@EnableConfigurationProperties(EnvConfig.class)

@EnableDiscoveryClient

@EnableHystrix

@EnableScheduling

public class AdminApp {

\tpublic static void main(String[] args) {

\t\tSpringApplication.run(AdminApp.class, args);

\t}

}/<code>

注意這裡創建啟動類時,對springboot的項目進行了優化,避免啟動時加載很多,啟動繁重,具體深度優化,可參考:https://mp.weixin.qq.com/s?__biz=MzU2NjIzNDk5NQ==&mid=2247487954&idx=1&sn=2426451f3bd83161cfe1237f82d6b448&key=f8fb043b3d2681a794e51a46e142af77355722dff712776af12b1f3c831218df6dfc329df63c8e5e550b3d88d58f0f178c4c3c16b141733e0e3344fa595e2bc25241d864d45132753fd99279b832de85&ascene=1&uin=MzQzMzI2NjAxMQ%3D%3D&devicetype=Windows+10&version=62070158&lang=zh_CN&pass_ticket=pnSSI9jAq0M11V5hYMmkoVm5qO%2FWk9l3UUUJMglbdtdDOzLHa7iHsDmwSzs486sD。

然後我們在進行配置,注意:據官方說,項目的src\\main\\resources路徑下不要創建application.yml文件,只創建名為bootstrap.yml的文件:

<code>management:

endpoint:

restart:

enabled: true

health:

enabled: true

info:

enabled: true

spring:

application:

name: edge-admin

cloud:

kubernetes:

config:

sources:

- name: ${spring.application.name}

namespace: default

discovery:

all-namespaces: true

reload:

#自動更新配置的開關設置為打開

enabled: true

#更新配置信息的模式:polling是主動拉取,event是事件通知

mode: polling

#主動拉取的間隔時間是500毫秒

period: 500

http:

encoding:

charset: UTF-8

enabled: true

force: true

mvc:

throw-exception-if-no-handler-found: true

main:

allow-bean-definition-overriding: true # 當遇到同樣名稱時,是否允許覆蓋註冊/<code>

這裡,我創建了bootstrap文件,同時也加了application文件,啟動時會先加載bootstrap,驗證有效。

在application.yaml中,我們加入如下內容:

<code>server:

port: 9999

undertow:

accesslog:

enabled: false

pattern: combined

servlet:

session:

timeout: PT120M



logging:

path: /data/${spring.application.name}/logs

management:

endpoint:

restart:

enabled: true

health:

enabled: true

info:

enabled: true

client:

http:

request:

connectTimeout: 8000

readTimeout: 30000



mybatis:

mapperLocations: classpath:mapper/*.xml

typeAliasesPackage: com.demo.*.model



backend:

ribbon:

eureka:

enabled: false

client:

enabled: true

ServerListRefreshInterval: 5000

hystrix.command.BackendCall.execution.isolation.thread.timeoutInMilliseconds: 5000

hystrix.threadpool.BackendCallThread.coreSize: 5/<code>

注意:這裡的server設置session的超時時間,對於springboot2.0與1.0版本完全不一樣了,具體看內容。

其他的application-test.yaml等配置文件,配置的是日誌的級別:

<code>logging:

level:

com:

demo: INFO

org:

springframework:

web: INFO/<code>

接下來配置環境配置:

EnvConfig.java類作為環境變量配置,註解ConfigurationProperties的prefix="spring_cloud",

表示該類用到的配置項都是名為"spring_cloud"的配置項的子內容 :

<code>package com.demo.config;

import org.springframework.boot.context.properties.ConfigurationProperties;

import org.springframework.context.annotation.Configuration;

/**

* 配置信息

* @author Damon

* @date 2019年10月25日 下午1:54:01

*

*/

@Configuration

@ConfigurationProperties(prefix = "greeting")

public class EnvConfig {

private String message = "This is a dummy message";



private String container_command;

private String model_dir_path;

private String so_path;

private String config_path;

private String task_role_name;

private String container_name;

private String container_workdir;

private String init_containers_image;

private String service_account_name;

private String spring_mq_host;

private String spring_mq_port;

private String spring_mq_user;

private String spring_mq_pwd;

private String jdbc_driverClassName;

private String jdbc_url;

private String jdbc_username;

private String jdbc_password;

private String spring_redis_host;

private String spring_redis_port;

private String spring_redis_pwd;

private String kube_apiserver_address;

private String image_path;

private String volume_image_path;

private String inference_job_namespace;

private String api_version;

private String remote_deployment_url;

private String remote_pods_url;

private String remote_deployment_pod_log_url;

private String base_path;

private String chunk_size;

private String cas_url;

private String create_job_url;

private String abnormal_data_dir;



private Long expire_time= 600000L;



public String getMessage() {

return this.message;

}

public void setMessage(String message) {

this.message = message;

}

\tpublic String getContainer_command() {

\t\treturn container_command;

\t}

\tpublic void setContainer_command(String container_command) {

\t\tthis.container_command = container_command;

\t}

\tpublic String getModel_dir_path() {

\t\treturn model_dir_path;

\t}

\tpublic void setModel_dir_path(String model_dir_path) {

\t\tthis.model_dir_path = model_dir_path;

\t}

\tpublic String getSo_path() {

\t\treturn so_path;

\t}

\tpublic void setSo_path(String so_path) {

\t\tthis.so_path = so_path;

\t}

\tpublic String getConfig_path() {

\t\treturn config_path;

\t}

\tpublic void setConfig_path(String config_path) {

\t\tthis.config_path = config_path;

\t}

\tpublic String getTask_role_name() {

\t\treturn task_role_name;

\t}

\tpublic void setTask_role_name(String task_role_name) {

\t\tthis.task_role_name = task_role_name;

\t}

\tpublic String getContainer_name() {

\t\treturn container_name;

\t}

\tpublic void setContainer_name(String container_name) {

\t\tthis.container_name = container_name;

\t}

\tpublic String getContainer_workdir() {

\t\treturn container_workdir;

\t}

\tpublic void setContainer_workdir(String container_workdir) {

\t\tthis.container_workdir = container_workdir;

\t}

\tpublic String getInit_containers_image() {

\t\treturn init_containers_image;

\t}

\tpublic void setInit_containers_image(String init_containers_image) {

\t\tthis.init_containers_image = init_containers_image;

\t}

\tpublic String getService_account_name() {

\t\treturn service_account_name;

\t}

\tpublic void setService_account_name(String service_account_name) {

\t\tthis.service_account_name = service_account_name;

\t}

\tpublic String getSpring_mq_host() {

\t\treturn spring_mq_host;

\t}

\tpublic void setSpring_mq_host(String spring_mq_host) {

\t\tthis.spring_mq_host = spring_mq_host;

\t}

\tpublic String getSpring_mq_port() {

\t\treturn spring_mq_port;

\t}

\tpublic void setSpring_mq_port(String spring_mq_port) {

\t\tthis.spring_mq_port = spring_mq_port;

\t}

\tpublic String getSpring_mq_user() {

\t\treturn spring_mq_user;

\t}

\tpublic void setSpring_mq_user(String spring_mq_user) {

\t\tthis.spring_mq_user = spring_mq_user;

\t}

\tpublic String getSpring_mq_pwd() {

\t\treturn spring_mq_pwd;

\t}

\tpublic void setSpring_mq_pwd(String spring_mq_pwd) {

\t\tthis.spring_mq_pwd = spring_mq_pwd;

\t}

\tpublic String getJdbc_driverClassName() {

\t\treturn jdbc_driverClassName;

\t}

\tpublic void setJdbc_driverClassName(String jdbc_driverClassName) {

\t\tthis.jdbc_driverClassName = jdbc_driverClassName;

\t}

\tpublic String getJdbc_url() {

\t\treturn jdbc_url;

\t}

\tpublic void setJdbc_url(String jdbc_url) {

\t\tthis.jdbc_url = jdbc_url;

\t}

\tpublic String getJdbc_username() {

\t\treturn jdbc_username;

\t}

\tpublic void setJdbc_username(String jdbc_username) {

\t\tthis.jdbc_username = jdbc_username;

\t}

\tpublic String getJdbc_password() {

\t\treturn jdbc_password;

\t}

\tpublic void setJdbc_password(String jdbc_password) {

\t\tthis.jdbc_password = jdbc_password;

\t}

\tpublic String getSpring_redis_host() {

\t\treturn spring_redis_host;

\t}

\tpublic void setSpring_redis_host(String spring_redis_host) {

\t\tthis.spring_redis_host = spring_redis_host;

\t}

\tpublic String getSpring_redis_port() {

\t\treturn spring_redis_port;

\t}

\tpublic void setSpring_redis_port(String spring_redis_port) {

\t\tthis.spring_redis_port = spring_redis_port;

\t}

\tpublic String getSpring_redis_pwd() {

\t\treturn spring_redis_pwd;

\t}

\tpublic void setSpring_redis_pwd(String spring_redis_pwd) {

\t\tthis.spring_redis_pwd = spring_redis_pwd;

\t}

\tpublic String getKube_apiserver_address() {

\t\treturn kube_apiserver_address;

\t}

\tpublic void setKube_apiserver_address(String kube_apiserver_address) {

\t\tthis.kube_apiserver_address = kube_apiserver_address;

\t}

\tpublic String getImage_path() {

\t\treturn image_path;

\t}

\tpublic void setImage_path(String image_path) {

\t\tthis.image_path = image_path;

\t}

\tpublic String getVolume_image_path() {

\t\treturn volume_image_path;

\t}

\tpublic void setVolume_image_path(String volume_image_path) {

\t\tthis.volume_image_path = volume_image_path;

\t}

\tpublic String getInference_job_namespace() {

\t\treturn inference_job_namespace;

\t}

\tpublic void setInference_job_namespace(String inference_job_namespace) {

\t\tthis.inference_job_namespace = inference_job_namespace;

\t}

\tpublic String getApi_version() {

\t\treturn api_version;

\t}

\tpublic void setApi_version(String api_version) {

\t\tthis.api_version = api_version;

\t}

\tpublic String getRemote_deployment_url() {

\t\treturn remote_deployment_url;

\t}

\tpublic void setRemote_deployment_url(String remote_deployment_url) {

\t\tthis.remote_deployment_url = remote_deployment_url;

\t}

\tpublic String getRemote_pods_url() {

\t\treturn remote_pods_url;

\t}

\tpublic void setRemote_pods_url(String remote_pods_url) {

\t\tthis.remote_pods_url = remote_pods_url;

\t}

\tpublic String getRemote_deployment_pod_log_url() {

\t\treturn remote_deployment_pod_log_url;

\t}

\tpublic void setRemote_deployment_pod_log_url(String remote_deployment_pod_log_url) {

\t\tthis.remote_deployment_pod_log_url = remote_deployment_pod_log_url;

\t}

\tpublic String getBase_path() {

\t\treturn base_path;

\t}

\tpublic void setBase_path(String base_path) {

\t\tthis.base_path = base_path;

\t}

\tpublic String getChunk_size() {

\t\treturn chunk_size;

\t}

\tpublic void setChunk_size(String chunk_size) {

\t\tthis.chunk_size = chunk_size;

\t}

\tpublic Long getExpire_time() {

\t\treturn expire_time;

\t}

\tpublic void setExpire_time(Long expire_time) {

\t\tthis.expire_time = expire_time;

\t}

\tpublic String getCas_url() {

\t\treturn cas_url;

\t}

\tpublic void setCas_url(String cas_url) {

\t\tthis.cas_url = cas_url;

\t}

\tpublic String getCreate_job_url() {

\t\treturn create_job_url;

\t}

\tpublic void setCreate_job_url(String create_job_url) {

\t\tthis.create_job_url = create_job_url;

\t}

\tpublic String getAbnormal_data_dir() {

\t\treturn abnormal_data_dir;

\t}

\tpublic void setAbnormal_data_dir(String abnormal_data_dir) {

\t\tthis.abnormal_data_dir = abnormal_data_dir;

\t}
}/<code>

測試demo類:

<code>/**

* @author Damon

* @date 2019年12月27日 上午9:16:41

*

*/

@RestController

public class DemoController {

\t

\t@Autowired

private EnvConfig envConfig;


\t

\t/**

\t *

\t * @author Damon

\t * @date 2019年12月26日

\t *

\t */

\t

\t@GetMapping(value = "/getTest")

\tpublic String getTest() {

\t\treturn envConfig.getBase_path();

\t}

}/<code>

重點:默認的svc是沒有權限訪問k8s的API Server的資源的,執行如下腳本,可以提升權限,允許其訪問configmap的可讀權限:

<code>#使用這個代表集群最高權限,deployment中無需引入serviceAccount: config-reader

apiVersion: rbac.authorization.k8s.io/v1

kind: ClusterRoleBinding

metadata:

name: fabric8-rbac

subjects:

- kind: ServiceAccount

# Reference to upper's `metadata.name`


name: default

# Reference to upper's `metadata.namespace`

namespace: default

roleRef:

kind: ClusterRole

name: cluster-admin

apiGroup: rbac.authorization.k8s.io/<code>

配置configmap:

<code>kind: ConfigMap

apiVersion: v1

metadata:

name: edge-admin

data:

application.yaml: |-

greeting:

message: Say Hello to the World

---

spring:

profiles: dev

greeting:

message: Say Hello to the Developers

---

spring:

profiles: test

greeting:


message: Say Hello to the Test

---

spring:

profiles: prod

greeting:

message: Say Hello to the Prod/<code>

接下來就是執行deployment啟動項目了:

<code>apiVersion: apps/v1

kind: Deployment

metadata:

name: edge-admin-deployment

labels:

app: edge-admin

spec:

replicas: 1

selector:

matchLabels:

app: edge-admin

template:

metadata:

labels:

app: edge-admin

spec:

nodeSelector:

edge-admin: "true"

containers:

- name: edge-admin

image: 10.11.2.20:8000/harbor/edge-admin

imagePullPolicy: IfNotPresent

ports:

- name: admin01

containerPort: 1002

volumeMounts:

- mountPath: /home/edge-admin

name: edge-admin-path

- mountPath: /data/edge-admin

name: edge-admin-log-path

- mountPath: /etc/kubernetes

name: kube-config-path

- mountPath: /abnormal_data_dir

name: abnormal-data-dir

args: ["sh", "-c", "nohup java $JAVA_OPTS -jar -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=128m -Xms1024m -Xmx1024m -Xmn256m -Xss256k -XX:SurvivorRatio=8 -XX:+UseConcMarkSweepGC edge-admin.jar --spring.profiles.active=dev", "&"]

hostAliases:

- ip: "10.10.1.5"

hostnames:

- "k8s.api.server"

- "foo.remote"

- ip: "127.0.0.1"

hostnames:

- "foo.localhost"

- ip: "0.0.0.0"

hostnames:

- "foo.all"

#利用admin-rbac.yaml來獲取權限

#serviceAccount: config-reader

#serviceAccountName: config-reader

volumes:

- name: edge-admin-path

hostPath:

path: /var/pai/edge-admin

- name: edge-admin-log-path

hostPath:

path: /data/edge-admin

- name: kube-config-path

hostPath:

path: /etc/kubernetes

- name: abnormal-data-dir

hostPath:

path: /data/images/detect_result/defect/<code>

其中,前面說的,項目啟動參數對其性能優化,是對jvm的參數設置。分別執行kubectl apply -f deployment.yaml和configmap.yaml,創建demo時所用的configmap的資源以及利用k8s部署啟動項目。

最後打開瀏覽器:執行ip:port/hello,即可看到configmap中對應的屬性值,這裡就不展示了,有興趣的可以試試。

以上即是對springcloud和k8s首次結合後利用其configmap特性,來做配置管理,摒棄springcloud-config、spring-boot-starter-actuator的組件,減少系統的複雜性,畢竟k8s是肯定會被用到的,所以可以直接用其特性來做系統服務的環境配置管理。


分享到:


相關文章: