1. 程式人生 > >Kubernetes部署SpringBoot連線外部資料庫使用svc模式

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

注:

  1. 其實mysql的ip可以寫死,譬如我mysql地址是192.168.200.223,在Kubernetes是可以訪問到的,寫這個ip其實可以訪問。
  2. mysqljdbc服務會映射出一個ClusterIP,寫這個ip也可以訪問
  3. 寫服務名的好處就是配置無需根據你的資料庫進行更新,只需要修改以下配置檔案就可以了
  4. 寫ip的方式比較好調通,寫服務名的方式主要是由於dns配置問題,可能會出現各類問題假如有問題可以參考一下我的安裝文件
  5. 假如服務不正常可以通過如下指令進入虛擬手動排查問題
kubectl get pods

這裡寫圖片描述

kubectl exec -it springboot-demo-76ddcb9f67-stdmj /bin/bash