0%

问题

  • openjdkoracle jdk一些情况下并不兼容;
  • openjdk镜像很小只有一百朵兆,但是oracle jdk搜到的镜像都很大.

步骤

  1. 下载最新的oracle jre 8
https://javadl.oracle.com/webapps/download/AutoDL?BundleId=242050_3d5a2bb8f8d4428bbe94aed7ec7ae784
  1. 上传到linux并解压
[root@node1 jdk8]# tar -xvzf jre-8u251-linux-x64.tar.gz 
[root@node1 jdk8]# pwd
/root/jdk8
[root@node1 jdk8]# ll
总用量 85492
drwxr-xr-x 5 10143 10143       76 7月   9 14:04 jre1.8.0_251
-rw-r--r-- 1 root  root  87543611 7月   9 13:55 jre-8u251-linux-x64.tar.gz
  1. 删除一些说明文档和无用包
[root@node1 jdk8]# cd jre1.8.0_251/

rm -rf COPYRIGHT LICENSE README release THIRDPARTYLICENSEREADME-JAVAFX.txt THIRDPARTYLICENSEREADME.txt Welcome.html
rm -rf   lib/plugin.jar \
           lib/ext/jfxrt.jar \
           bin/javaws \
           lib/javaws.jar \
           lib/desktop \
           plugin \
           lib/deploy* \
           lib/*javafx* \
           lib/*jfx* \
           lib/amd64/libdecora_sse.so \
           lib/amd64/libprism_*.so \
           lib/amd64/libfxplugins.so \
           lib/amd64/libglass.so \
           lib/amd64/libgstreamer-lite.so \
           lib/amd64/libjavafx*.so \
           lib/amd64/libjfx*.so

[root@node1 jre1.8.0_251]# ll
总用量 4
drwxr-xr-x  2 10143 10143  208 7月   9 13:56 bin
drwxr-xr-x 13 10143 10143 4096 7月   9 13:56 lib
drwxr-xr-x  4 10143 10143   47 3月  12 14:33 man
  1. 重新打包
[root@node1 jre1.8.0_251]# tar zcvf jre8.tar.gz *
[root@node1 jre1.8.0_251]# ll
总用量 43896
drwxr-xr-x  2 10143 10143      208 7月   9 13:56 bin
-rw-r--r--  1 root  root  44942683 7月   9 14:04 jre8.tar.gz
drwxr-xr-x 13 10143 10143     4096 7月   9 13:56 lib
drwxr-xr-x  4 10143 10143       47 3月  12 14:33 man
  1. 直接在当前目录下创建Dockerfile
[root@node1 jre1.8.0_251]# vim Dockerfile

编写Dockerfile

FROM docker.io/jeanblanchard/alpine-glibc
MAINTAINER wuzhiyong
ADD jre8.tar.gz /usr/java/jdk/
ENV JAVA_HOME /usr/java/jdk
ENV PATH ${PATH}:${JAVA_HOME}/bin
WORKDIR /opt
  1. 生成镜像
# 生成镜像
[root@node1 jre1.8.0_251]# docker build -t wuzhiyong/java8.0_251 .
Sending build context to Docker daemon  161.1MB
Step 1/6 : FROM docker.io/jeanblanchard/alpine-glibc
 ---> c1bfe6541128
Step 2/6 : MAINTAINER wuzhiyong
 ---> Using cache
 ---> 5e4699308c35
Step 3/6 : ADD jre8.tar.gz /usr/java/jdk/
 ---> 8b6b66ec0f68
Step 4/6 : ENV JAVA_HOME /usr/java/jdk
 ---> Running in 26f286188aa8
Removing intermediate container 26f286188aa8
 ---> 7d1501133f4c
Step 5/6 : ENV PATH ${PATH}:${JAVA_HOME}/bin
 ---> Running in 418be370f8b9
Removing intermediate container 418be370f8b9
 ---> 8a920d8285df
Step 6/6 : WORKDIR /opt
 ---> Running in 107687a0007d
Removing intermediate container 107687a0007d
 ---> e7a62387e585
Successfully built e7a62387e585
Successfully tagged wuzhiyong/java8.0_251:latest
#查看镜像大小
[root@node1 jre1.8.0_251]# docker images | grep java8
wuzhiyong/java8.0_251                                            latest                    e7a62387e585        15 seconds ago      133MB
  1. 上传到镜像仓库,可以下载使用
docker pull hub.deri.org.cn/library/oracle_jdk_1.8_251:latest

引入依赖

<!-- 注意版本号,测试2.0.3版本有严重问题,启动失败 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-mail</artifactId>
    <version>2.1.1.RELEASE</version>
</dependency>

注册邮箱

  • 163,126,qq都行,这边测试126邮箱;
  • 开启IMAP/SMTP服务;
  • 获取授权密码,注意保存.

配置application.yaml

spring:
  mail:
    # 注册的邮箱
    username: xxxxxx@126.com
    # 密码为授权码
    password: XXXXXXXXXXXXXXX
    # smtp服务器地址
    host: smtp.126.com
    # 安全相关配置,非必须
    properties:
      mail:
        smtp:
          auth: true
          starttls:
            enable: true
            required: true

测试发送

@Service
public class MailService {
    // 这个在IDEA中会提示没有bean,注入失败,不用管~
    @Autowired
    JavaMailSender mailSender;

    public void send(){
        // 普通文本邮件
        SimpleMailMessage simpleMailMessage = new SimpleMailMessage();
        //邮件发送方,需要写完整邮箱地址
        simpleMailMessage.setFrom("XXXXX@126.com");
        //邮件接收方邮箱
        simpleMailMessage.setTo("1154365135@qq.com");
        //邮件标题
        simpleMailMessage.setSubject("测试主题~~");
        //邮件内容
        simpleMailMessage.setText("测试内容~~");
        //发送邮件
        mailSender.send(simpleMailMessage);
    }

    public void sendHtml() throws MessagingException {
        // html邮件
        MimeMessage mimeMessage = mailSender.createMimeMessage();
        MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);
        //邮件发送方
        helper.setFrom("authservice@126.com");
        //邮件接收方邮箱
        helper.setTo("1154365135@qq.com");
        //邮件标题
        helper.setSubject("测试HTML~~");
        //邮件内容
        helper.setText("<html>\n" +
                "<head>\n" +
                "<meta charset=\"utf-8\">\n" +
                "<title>菜鸟教程(runoob.com)</title>\n" +
                "</head>\n" +
                "<body>\n" +
                "    <h1>我的第一个标题</h1>\n" +
                "    <p style=\"color: blue;\">我的第一个段落。</p>\n" +
                "</body>\n" +
                "</html>", true);
        //发送邮件
        mailSender.send(mimeMessage);
    }

        //附件邮件, 未测试
    public void sendAttachmentMail(String to,String subject,String content,String filePath) throws MessagingException {
        MimeMessage mimeMessage=mailSender.createMimeMessage();
        MimeMessageHelper helper=new MimeMessageHelper(mimeMessage,true);
        helper.setTo(to);
        helper.setSubject(subject);
        helper.setText(content,true);
        helper.setFrom(form);
        FileSystemResource file=new FileSystemResource(new File(filePath));
        String fileName=file.getFilename();
        helper.addAttachment(fileName,file);
        //helper.addAttachment(fileName+"02",file);  如果是多个附件的话,可以这样写。但是开发中一般都是把filepath做成一个数组,这样在这里遍历就可以了
        //helper.addAttachment(fileName+"03",file);

        mailSender.send(mimeMessage);
    }

    //图片邮件,未测试
    public void sendInlineResourceMail(String to,String subject,String content,String rscPath,String rscId) throws MessagingException {
        MimeMessage mimeMessage=mailSender.createMimeMessage();
        MimeMessageHelper helper=new MimeMessageHelper(mimeMessage,true);
        helper.setTo(to);
        helper.setSubject(subject);
        helper.setText(content,true);
        helper.setFrom(form);
        FileSystemResource file=new FileSystemResource(new File(rscPath));
        helper.addInline(rscId,file);

        mailSender.send(mimeMessage);
    }
}

验证码EasyCaptcha介绍

使用

<dependency>
    <groupId>com.github.whvcse</groupId>
    <artifactId>easy-captcha</artifactId>
    <version>1.6.2</version>
</dependency>

也许下载不下来,可以直接下载 easy-captcha.jar 包.

使用下面的命令可以将jar包安装到本地仓库.

mvn install:install-file -Dfile=D:/easy-captcha.jar -DgroupId=com.github.whvcse -DartifactId=easy-captcha -Dversion=1.6.2 -Dpackaging=jar

开发

// PNG格式 设置长宽和验证码长度
SpecCaptcha captcha = new SpecCaptcha(180, 40, 6);
// 设置验证码字符类型,数字/大小写字母
captcha.setCharType(Captcha.TYPE_ONLY_NUMBER);

// GIF格式 设置长宽和验证码长度
GifCaptcha captcha = new GifCaptcha(180, 48, 6);

// 中文验证码 设置长宽和验证码长度
ChineseCaptcha captcha = new ChineseCaptcha(180, 48);
// 设置字体, 设置默认字体Captcha.FONT_1...好像有问题,英文可以使用默认字体
captcha.setFont(new Font("楷体", Font.PLAIN, 28));

// 使用算术验证码,设置长宽和几位数运算
ArithmeticCaptcha captcha = new ArithmeticCaptcha(180, 48, 2);
// 获取运算的公式:3+2=?
captcha.getArithmeticString()

// 通用方法
captcha.text();  // 获取验证码的字符
captcha.textChar();  // 获取验证码的字符数组

// 验证码保存到文件
FileOutputStream outputStream = new FileOutputStream(new File("C:/captcha.png"))
captcha.out(outputStream);  // 输出验证码

// 输出base64编码, 返回给前端需要用到
specCaptcha.toBase64();

// 如果不想要base64的头部data:image/png;base64,
specCaptcha.toBase64("");  // 加一个空的参数即可

测试

已知问题

同样的程序本地测试没有问题,部署到docker上测试出现问题.

  • 解决办法: 不要使用openjdk做基础镜像,采用oracle jdk.
  • 这边提供一个能用的: docker pull hub.deri.org.cn/library/oracle_jdk_1.8_131:latest

1955年,卡普耶卡(D.R.Kaprekar)研究了对四位数的一种变换:任给出四位数k0,用它的四个数字由大到小重新排列成一个四位数m,再减去它的反序数rev(m),得出数k1=m-rev(m),然后,继续对k1重复上述变换,得数k2.如此进行下去,卡普耶卡发现,无论k0是多大的四位数,只要四个数字不全相同,最多进行7次上述变换,就会出现四位数6174。因此这项研究在国际数学界又被称为“马丁猜想—6174问题”。

有趣的数字6174

  • 随机生成四个不完全一样的数字(0000,1111,2222,等排除);
  • 四个数字组成一个最大的数 和 一个最小的数,如2,5,7,3组成的最大的数7532,最小的数2357;
  • 最大的数 - 最小的数,如果不等于6174,就按照上一步将差值重新组成一个最大的数 和 一个最小的数;
  • 最后一定有一次能得到差值为6174.俗称数字黑洞.

JAVA实现

public class Test6174 {
    public static void main(String[] args) throws InterruptedException {
        // 无线循环测试
        while (true) {
            // 随机生成四个数字,考虑到出现四个一样的概率非常低 没有处理
            List<Integer> meta = new ArrayList<>();
            for (int i = 0; i < 4; i++) {
                meta.add(new Random().nextInt(10));
            }
            System.out.print("原始数字:" + meta);
            int result = 0, count = 0;
            while (result != 6174) {
                // 获取四个数字组合的最大的数 和 最小的数
                int max = getMax(meta);
                int min = getMin(meta);
                result = Math.abs(max - min);
                count++;
                // 数字为啥是6174?
                if (result == 6174) {
                    System.out.println(",次数:" + count);
                }
                meta = getMeta(result);
            }
            Thread.sleep(1000);
        }
    }

    public static int getMax(List<Integer> meta) {
        List<Integer> tmp = meta.stream().sorted().collect(Collectors.toList());
        return tmp.get(0) * 1000 + tmp.get(1) * 100 + tmp.get(2) * 10 + tmp.get(3);
    }

    public static int getMin(List<Integer> meta) {
        List<Integer> tmp = meta.stream().sorted().collect(Collectors.toList());
        return tmp.get(0) + tmp.get(1) * 10 + tmp.get(2) * 100 + tmp.get(3) * 1000;
    }

    public static List<Integer> getMeta(int num) {
        List<Integer> tmp = new ArrayList<>();
        tmp.add(num / 1000 % 10);
        tmp.add(num / 100 % 10);
        tmp.add(num / 10 % 10);
        tmp.add(num % 10);
        return tmp;
    }
}

同样的黑洞数字还有很多哦

不如写个程序来找出不同位数的黑洞数字吧

public class TestN {
    public static void main(String[] args) throws InterruptedException {
        // 输入几位数就是找几位数的黑洞数字
        int digits = 5;
        List<Integer> meta = new ArrayList<>();
        for (int i = 0; i < digits; i++) {
            meta.add(new Random().nextInt(10));
        }
        if (getMax(meta) - getMin(meta) == 0){
            System.out.println("数字完全一样,不符合要求");
            System.exit(0);
        }
        System.out.println("原始数字:" + meta);
        int result = 0;
        while (true) {
            int max = getMax(meta);
            int min = getMin(meta);
            result = Math.abs(max - min);
            meta = getMeta(result, digits);
            System.out.println(result);
        }
    }

    // 获取数字组合的最大数
    public static int getMax(List<Integer> meta) {
        List<Integer> tmp = meta.stream().sorted().collect(Collectors.toList());
        double result = 0;
        for (int i = 0; i < tmp.size(); i++) {
            result = result + tmp.get(i) * (Math.pow(10.0, (double) (i)));
        }
        return (int) result;
    }

    // 获取数字组合的最小数
    public static int getMin(List<Integer> meta) {
        List<Integer> tmp = meta.stream().sorted().collect(Collectors.toList());
        double result = 0;
        for (int i = 0; i < tmp.size(); i++) {
            result = result + tmp.get(i) * (Math.pow(10.0, (double) (tmp.size() - i - 1)));
        }
        return (int) result;
    }

    // 获取数的各位数
    public static List<Integer> getMeta(int num, int c) {
        List<Integer> tmp = new ArrayList<>();
        for (int i = 0; i < c; i++) {
            tmp.add(num / ((int) Math.pow(10.0, i)) % 10);
        }
        return tmp;
    }
}

如上程序测试运行:

digits(位数) 黑洞数(个) 结果
1 - -
2 5 9,81,63,27,45
3 1 495
4 1 6174
5 4 71973,83952,74943,62964
6 7 840852,860832,862632,642654,...
7 8 9529641,8719722,8649432,7519743,...
8 3 64308654,83208762,86526432
9 14 954197541,883098612,976494321,...

问题

[root@master busybox]# kubectl get pod -nkube-system -owide
NAME                             READY   STATUS    RESTARTS   AGE     IP               NODE     NOMINATED NODE   READINESS GATES
coredns-5c98db65d4-8zjps         1/1     Running   1          2d23h   10.244.0.13      master   <none>           <none>
coredns-5c98db65d4-d2kth         1/1     Running   1          2d23h   10.244.0.14      master   <none>           <none>
[root@master busybox]# kubectl get pod -owide
NAME                      READY   STATUS    RESTARTS   AGE    IP            NODE     NOMINATED NODE   READINESS GATES
curl-6bf6db5c4f-pjld9     1/1     Running   1          3d     10.244.1.2    node2    <none>           <none>
gateway-99b655cc6-np685   1/1     Running   0          44s    10.244.0.54   master   <none>           <none>
test-post-start1          1/1     Running   0          115s   10.244.1.6    node2    <none>           <none>
test-post-start2          1/1     Running   0          115s   10.244.0.52   master   <none>           <none>
test-post-start3          1/1     Running   0          115s   10.244.0.53   master   <none>           <none>

如上所示,出现部署在master节点上的pod,无法解析gateway.default.svc.cluster.local域名,但是部署在node2,确可以解析,如上curl-6bf6db5c4f-pjld9,test-post-start1通过nslookup都可以解析.

# 报错
/ # nslookup gateway
nslookup: can't resolve '(null)': Name does not resolve

nslookup: can't resolve 'gateway': Try again

/ # nslookup gateway.default.svc.cluster.local
Server:    10.244.0.10
Address 1: 10.244.0.10

nslookup: can't resolve 'gateway.default.svc.cluster.local'

分析

进入master节点pod,直接通过coredns pod ip解析测试

kubectl exec -it test-post-start2 sh
/ # nslookup gateway.default.svc.cluster.local 10.244.0.13
Server:    10.244.0.13
Address 1: 10.244.0.13 10-244-0-13.kube-dns.kube-system.svc.cluster.local

Name:      gateway.default.svc.cluster.local
Address 1: 10.244.106.29 gateway.default.svc.cluster.local

发现直接通过coredns pod ip解析可以成功,证明coredns服务本身没有问题.

查看dns clusterIP.

[root@master ~]# kubectl get svc -nkube-system
NAME            TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)                  AGE
kube-dns        ClusterIP   10.244.0.10    <none>        53/UDP,53/TCP,9153/TCP   21m
# 通过clusterIP解析域名失败
nslookup gateway.default.svc.cluster.local 10.244.0.10

通过以上测试证明问题出现在coredns service上.

解决

导出现有kube-dns service配置

kubectl get svc -nkube-system kube-dns -oyaml > kube-dns-svc.yaml

修改kube-dns-svc.yaml.

apiVersion: v1
kind: Service
metadata:
  annotations:
    prometheus.io/port: "9153"
    prometheus.io/scrape: "true"
  labels:
    k8s-app: kube-dns
    kubernetes.io/cluster-service: "true"
    kubernetes.io/name: KubeDNS
  name: kube-dns
  namespace: kube-system
spec:
  ports:
  - name: dns
    port: 53
    protocol: UDP
    targetPort: 53
  - name: dns-tcp
    port: 53
    protocol: TCP
    targetPort: 53
  - name: metrics
    port: 9153
    protocol: TCP
    targetPort: 9153
  selector:
    k8s-app: kube-dns
  sessionAffinity: None
  type: ClusterIP
kubectl apply -f kube-dns-svc.yaml

查看最新的coredns clusterIP,当前为10.244.47.231.

[root@master ~]# kubectl get svc -nkube-system
NAME            TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)                  AGE
kube-dns        ClusterIP   10.244.47.231    <none>        53/UDP,53/TCP,9153/TCP   21m

进去之前无法解析的pod中测试,证明新的clusterIP没有问题.

nslookup gateway.default.svc.cluster.local 10.244.47.231

修改kubelet --clusterDNS,这样新创建的pod /etc/resolv.confnameserver为新的coredns clusterIP.

# 修改kubelet配置
vim  /var/lib/kubelet/config.yaml

# 找到clusterDNS
clusterDNS:
- 10.244.47.231

# 重启kubelet生效,注意k8s中所有节点都需要修改重启
systemctl restart kubelet.service

最后测试,新的pod/etc/resolv.conf.解析没有问题.

/ # cat /etc/resolv.conf 
nameserver 10.244.47.231
search default.svc.cluster.local svc.cluster.local cluster.local
options ndots:5

查询series cardinality命令

InfluxDB maintains an in-memory index of every series in the system. As the number of unique series grows, so does the RAM usage. High series cardinality can lead to the operating system killing the InfluxDB process with an out of memory (OOM) exception. See SHOW CARDINALITY to learn about the InfluxSQL commands for series cardinality.

// 翻译
InfluxDB会在系统上为每个series维护一个内存索引,而随着这些series的增加,RAM内存使用率也会增加。如果series cardinality如果太高,就会导致操作系统触发OOMKiller机制,将Influxdb进程KILL掉. 使用 SHOW CARDINALITY 命令可以查看到 series cardinality。
-- show estimated cardinality of the series on current database
SHOW SERIES CARDINALITY
-- show estimated cardinality of the series on specified database
SHOW SERIES CARDINALITY ON mydb
-- show exact series cardinality
SHOW SERIES EXACT CARDINALITY
-- show series cardinality of the series on specified database
SHOW SERIES EXACT CARDINALITY ON mydb
To reduce series cardinality, series must be dropped from the index. DROP DATABASE, DROP MEASUREMENT, and DROP SERIES will all remove series from the index and reduce the overall series cardinality.

//大意
要减少或者删除series cardinality, 需要删除库//series

将series由保存到内存改为保存到TSI文件

修改配置

这里我们需要修改的配置是index-version项,可以在influxdb.conf[data]下修改,也可以通过环境变量INFLUXDB_DATA_INDEX_VERSION修改.

[data]
  # The type of shard index to use for new shards.  The default is an in-memory index that is
  # recreated at startup.  A value of "tsi1" will use a disk based index that supports higher
  # cardinality datasets.
  # 这个配置项默认值inmem,可以取消注释修改为tsi1,那么后续的index将会保存在TSI文件中了.重启生效.
  # index-version = "inmem"

重构TSI索引

  1. 停止InfluxDB服务
  2. 删除所有_series文件夹
    默认情况下,_series保存在/data/<dbName>/_series,检查并删除/data目录下所有_series
  3. 删除所有index文件夹
    默认情况下,index文件夹在/data/<dbName/<rpName>/<shardID>/index
  4. 使用influx_inspect重构TSI index
# 格式
influx_inspect buildtsi -datadir <data_dir> -waldir <wal_dir>
# 示例
influx_inspect buildtsi -datadir /data -waldir /wal
  1. 启动influxDB服务