Kubernetes部署SpringBoot連線外部資料庫使用svc模式
這篇文章主要講解Kubernetes部署SpringBoot的過程,其中主要的難點是用svc名稱動態獲取資料庫IP。網上有一部分文件有說,但是不進行講解,我在最初部署的時候測試不成功,後來發現主要是Kubernetes內部dns配置的問題。以下文章都是基於centos7二進位制安裝kubernetes1.12新增證書配置這篇文章搭建的kubernetes的基礎上完成的,其中比較重要的是coredns,沒有配置這部,網上其他文章無法成功,因為沒辦法解析域名,下面開始我的流程步驟
一 編寫SpringBoot工程
建立過程參見IDEA建立SpringBoot工程我就不描述了,我就把需要注意的程式碼列出來。
1 編寫pom.xml
<?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>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<parent >
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<spark.version>2.3.0</spark.version>
<spring-cloud.version>Edgware.RELEASE</spring-cloud.version>
<ojdbc6.version>11.2.0.1.0</ojdbc6.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.12</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
<exclusions>
<exclusion>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-jdbc</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
2 編寫datasource
package com.example.demo.datasource;
import com.alibaba.druid.pool.DruidDataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.jdbc.core.JdbcTemplate;
import javax.sql.DataSource;
/**
* Created by arnold.zhu on 6/13/2017.
*/
@Configuration
public class MysqlDataSource {
private Logger logger = LoggerFactory.getLogger(MysqlDataSource.class);
@Autowired
private Environment env;
@Value("${spring.datasource.mysql.url}")
private String dbUrl;
@Value("${spring.datasource.mysql.username}")
private String username;
@Value("${spring.datasource.mysql.password}")
private String password;
@Value("${spring.datasource.mysql.driver-class-name}")
private String driverClassName;
@Value("${spring.datasource.mysql.initialSize}")
private int initialSize;
@Value("${spring.datasource.mysql.minIdle}")
private int minIdle;
@Value("${spring.datasource.mysql.maxActive}")
private int maxActive;
@Value("${spring.datasource.mysql.maxWait}")
private int maxWait;
@Value("${spring.datasource.mysql.timeBetweenEvictionRunsMillis}")
private int timeBetweenEvictionRunsMillis;
@Value("${spring.datasource.mysql.minEvictableIdleTimeMillis}")
private int minEvictableIdleTimeMillis;
@Value("${spring.datasource.mysql.validationQuery}")
private String validationQuery;
@Value("${spring.datasource.mysql.testWhileIdle}")
private boolean testWhileIdle;
@Value("${spring.datasource.mysql.testOnBorrow}")
private boolean testOnBorrow;
@Value("${spring.datasource.mysql.testOnReturn}")
private boolean testOnReturn;
@Value("${spring.datasource.mysql.filters}")
private String filters;
@Value("${spring.datasource.mysql.logSlowSql}")
private String logSlowSql;
@Value("${spring.datasource.mysql.dbType}")
private String dbType;
@Bean(name = "mysqlJdbcDataSource")
@Qualifier("mysqlJdbcDataSource")
public DataSource dataSource() {
DruidDataSource datasource = new DruidDataSource();
System.out.println("-----------------------------------");
System.out.println(env.getProperty("MYSQL_SERVICE_HOST"));
System.out.println("-----------------------------------");
datasource.setUrl(dbUrl.replaceAll("$\\{MYSQL_SERVICE_HOST\\}",env.getProperty("MYSQL_SERVICE_HOST")));
datasource.setUsername(username);
datasource.setPassword(password);
datasource.setDriverClassName(driverClassName);
datasource.setInitialSize(initialSize);
datasource.setMinIdle(minIdle);
datasource.setMaxActive(maxActive);
datasource.setMaxWait(maxWait);
datasource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
datasource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
datasource.setValidationQuery(validationQuery);
datasource.setTestWhileIdle(testWhileIdle);
datasource.setTestOnBorrow(testOnBorrow);
datasource.setTestOnReturn(testOnReturn);
datasource.setDbType(dbType);
//此功能不支援hive
// try {
// datasource.setFilters(filters);
// } catch (SQLException e) {
// logger.error("druid configuration initialization filter", e);
// }
return datasource;
}
@Bean(name = "mysqlJdbcTemplate")
public JdbcTemplate mysqlJdbcTemplate(@Qualifier("mysqlJdbcDataSource") DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
}
3 編寫測試Dao與Impl
package com.example.demo.dao;
public interface MysqlDao {
public String test();
}
package com.example.demo.dao.impl;
import com.example.demo.dao.MysqlDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import java.util.Map;
@Repository
public class MysqlDaoImpl implements MysqlDao {
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public String test() {
String sql = "select HOST from db limit 1";
Map<String, Object> map = jdbcTemplate.queryForMap(sql);
return map.get("HOST").toString();
}
}
4 編寫Controller
package com.example.demo.controller;
import com.example.demo.dao.MysqlDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@Autowired
private MysqlDao mysqlDao;
@RequestMapping(value = {"/hello"},method= {RequestMethod.GET,RequestMethod.POST})
public String hello() {
return mysqlDao.test();
}
}
5 編寫SpringBoot配置檔案application.yml
server:
port: 8080
spring:
application:
name: service-hadoop
datasource:
mysql:
type: com.mysql.jdbc.Driver
url: jdbc:mysql://${MYSQL_SERVICE_HOST}:3306/mysql
driver-class-name: com.mysql.jdbc.Driver
username: root
password: root
initialSize: 5
minIdle: 5
maxActive: 20
maxWait: 600000
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 1
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
filters: stat,wall,log4j #負載監控功能 此功能不支援hive
logSlowSql: true
dbType: mysql
注:
1. 在application.yml檔案中${MYSQL_SERVICE_HOST}與MysqlDataSource.java中的dbUrl.replaceAll(“$\{MYSQL_SERVICE_HOST\}”,env.getProperty(“MYSQL_SERVICE_HOST”))對應
2. ${MYSQL_SERVICE_HOST}這個的數值是從Kubernetes傳入的環境變數,所以使用者名稱與密碼也可以採用這種方式進行部署,只需要按照需求修改MysqlDataSource.java即可
datasource.setUsername(username);
datasource.setPassword(password);
改為
datasource.setUsername(env.getProperty("MYSQL_SERVICE_USERNAME"))
datasource.setPassword(env.getProperty("MYSQL_SERVICE_PASSWORD"))
此處的變數名需要與Kubernetes配置檔案中的變數名對應
二 編寫Kubernetes配置檔案
1 mysql服務配置檔案
由於mysql服務是外部資料庫獨立於Kubernetes之外,所以需要將mysql服務引入到Kubernetes之中,利用的是Kubernetes的Endpoints技術。SpringBoot連線資料庫時,其實是通過Dns解析Service服務,無論是外部資料庫或者是Kubernetes啟動的Mysql服務,都是通過Service層進行隔離,兩者並沒有區別,下面是配置檔案。
1.1 mysql-endpoint.yaml
apiVersion: v1
kind: Endpoints
metadata:
name: mysqljdbc
subsets:
- addresses:
- ip: 192.168.200.223
ports:
- port: 3306
protocol: TCP
1.2 mysql-svc.yaml
apiVersion: v1
kind: Service
metadata:
name: mysqljdbc
spec:
selector:
app: mysql
ports:
- port: 3306
targetPort: 3306
protocol: TCP
2 SpringBoot配置檔案
2.1 springboot-rc.yml
apiVersion: apps/v1beta1
kind: Deployment
metadata:
name: springboot-demo
labels:
app: springboot-demo
spec:
replicas: 1
selector:
matchLabels:
app: springboot-demo
template:
metadata:
labels:
app: springboot-demo
spec:
containers:
- name: springboot-demo
image: springboot-demo
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8080
env:
- name: MYSQL_SERVICE_HOST
value: 'mysqljdbc'
- name: MYSQL_SERVICE_PORT
value: '3306'
MYSQL_SERVICE_HOST和MYSQL_SERVICE_PORT就是環境變數,與SpringBoot中的配置檔案進行對應,只能接收字串,mysqljdbc其實指代的是機器名,然後通過coredns進行Service名稱解析
2.2 springboot-svc.yml
apiVersion: v1
kind: Service
metadata:
name: springboot-demo
spec:
type: NodePort
ports:
- name: springboot-svc
port: 8080
nodePort: 30000
selector:
app: springboot-demo
三 啟動與驗證
1 啟動服務
kubectl create -f mysql-endpoint.yaml
kubectl create -f mysql-svc.yaml
kubectl create -f springboot-rc.yml
kubectl create -f springboot-svc.yml
2 驗證服務
kubectl get pods
kubectl get svc
注:
- 其實mysql的ip可以寫死,譬如我mysql地址是192.168.200.223,在Kubernetes是可以訪問到的,寫這個ip其實可以訪問。
- mysqljdbc服務會映射出一個ClusterIP,寫這個ip也可以訪問
- 寫服務名的好處就是配置無需根據你的資料庫進行更新,只需要修改以下配置檔案就可以了
- 寫ip的方式比較好調通,寫服務名的方式主要是由於dns配置問題,可能會出現各類問題假如有問題可以參考一下我的安裝文件
- 假如服務不正常可以通過如下指令進入虛擬手動排查問題
kubectl get pods
kubectl exec -it springboot-demo-76ddcb9f67-stdmj /bin/bash