Springboot +Mybatis整合常见错误 报错:Property ‘sqlSessionFactory‘ or ‘sqlSessionTemplate‘ are required

记录一下在使用Spring Boot 时,由于对框架不熟悉,在项目启动的过程中遇到的问题


问题描述:

问题一:dao层注入问题

Field xxMapper in xx.service.impl.xxServiceImpl required a bean of type 'xx.mapper.xxMapper

以前是通过xml文件配置的,但spring boot不用配置xml文件。
解决方法:
1:可以在每个dao加上@Mapper或者@Repository注解
2:统一在启动类配置@MapperScan

复制代码
1 @SpringBootApplication
2 @MapperScan("com.example.demo.base.mapper")
3 @ComponentScan(basePackages = {"com.example.demo"})
4 public class DemoApplication {
5     public static void main(String[] args) {
6         SpringApplication.run(DemoApplication.class, args);
7     }
8 }
复制代码

问题二:配置好之后,启动项目又出现数据源配置问题

Failed to configure a DataSource: ‘url’ attribute is not specified and no embedded datasource could be configured

项目中加了Mybatis依赖jar包,SpringBoot数据源自动配置需要我们配置主类中加入@EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class})自动注入

复制代码
1 @SpringBootApplication(exclude={DataSourceAutoConfiguration.class})
2 @MapperScan("com.example.demo.base.mapper")
3 @ComponentScan(basePackages = {"com.example.demo"})
4 public class DemoApplication {
5     public static void main(String[] args) {
6         SpringApplication.run(DemoApplication.class, args);
7     }
8 }
复制代码

接下来又报错。。。

Property 'sqlSessionFactory' or 'sqlSessionTemplate' are required

原因是mybatis-spring-1.3.2中取消了自动注入SqlSessionFactory 和 SqlSessionTemplate,也就是mybatis依赖版本的问题。

方案一:
mybatis版本太高(1.3.2),于是降级到1.1.1解决问题。
(换这个太麻烦,没有实践,但听说还是有用。。)

方案二:
创建一个Dao的基类CommonDao,让这个基类继承SqlSessionDaoSupport,并通过set方法注入SqlSessionFactory属性即可:

复制代码
1 public class CommonDao extends SqlSessionDaoSupport {
2     @Resource
3     public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory){
4         super.setSqlSessionFactory(sqlSessionFactory);
5     }
6 }
复制代码

然后让Dao实现类再继承这个CommonDao基类即可。(但我dao用的是接口,不适用)

方案三:
后面将(exclude={DataSourceAutoConfiguration.class})去掉,将application.properties中配置放到新建的application.yml中

application.yml

复制代码
spring:
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/demo_db?serverTimezone=GMT%2B8
    username: root
    password: root
    driver-class-name: com.mysql.jdbc.Driver
复制代码

项目启动成功

方案四:
上面说SqlSessionFactory 和 SqlSessionTemplate没有自动注入,那我们可以自己手动注入

复制代码
 1 @Value("${mybatis-plus.mapper-locations}")
 2     private String mapperLocations;
 3     //配置FactoryBean
 4     @Bean(name = "sqlSessionFactoryBean")
 5     public SqlSessionFactoryBean sqlSessionFactoryBean() {
 6         SqlSessionFactoryBean sqlSessionFactoryBean = null;
 7         try {
 8             // 加载JNDI配置
 9             Context context = new InitialContext();
10             // 实例SessionFactory
11             sqlSessionFactoryBean = new SqlSessionFactoryBean();
12             // 配置数据源
13             sqlSessionFactoryBean.setDataSource(dataSource());
14             // 加载MyBatis配置文件
15             PathMatchingResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
16             // 能加载多个,所以可以配置通配符(如:classpath*:mapper/**/*.xml)
17             sqlSessionFactoryBean.setMapperLocations(resourcePatternResolver.getResources(mapperLocations));
18         } catch (Exception e) {
19             System.out.println("创建SqlSession连接工厂错误:{}");
20         }
21         return sqlSessionFactoryBean;
22     }
23     @Bean
24     public SqlSessionTemplate sqlSessionTemplate() throws Exception {
25         SqlSessionTemplate sqlSessionTemplate=new SqlSessionTemplate(sqlSessionFactoryBean().getObject(),ExecutorType.BATCH);
26         return sqlSessionTemplate;
27     }

k8s中的command和docker的entrypoint区别

Docker Entrypoint & Cmd
先回顾下CMD指令的含义,CMD指令可以指定容器启动时要执行的命令,但它可以被docker run命令的参数覆盖掉。

ENTRYPOINT 指令和CMD类似,它也可用户指定容器启动时要执行的命令,但如果dockerfile中也有CMD指令,CMD中的参数会被附加到ENTRYPOINT 指令的后面。 如果这时docker run命令带了参数,这个参数会覆盖掉CMD指令的参数,并也会附加到ENTRYPOINT 指令的后面。这样当容器启动后,会执行ENTRYPOINT 指令的参数部分。

可以看出,相对来说ENTRYPOINT指令优先级更高。我们来看个例子,下面是Dockerfile的内容:

### test
FROM ubuntu
MAINTAINER hello
RUN echo hello1 > test1.txt
RUN echo hello2 > /test2.txt
EXPOSE 80
ENTRYPOINT [“echo”]
CMD [“defaultvalue”]

假设通过该Dockerfile构建的镜像名为 myimage。

当运行 docker run myimage 输出的内容是 defaultvalue,可以看出CMD指令的参数得确是被添加到ENTRYPOINT指令的后面,然后被执行。
当运行docker run myimage hello world 输出的内容是 hello world ,可以看出docker run命令的参数得确是被添加到ENTRYPOINT指令的后面,然后被执行,这时CMD指令被覆盖了。
另外我们可以在docker run命令中通过 –entrypoint 覆盖dockerfile文件中的ENTRYPOINT设置,如:
docker run –entrypoint=”echo” myimage good 结果输出good
注意,不管是哪种方式,创建容器后,通过 docker ps –no-trunc查看容器信息时,COMMAND列会显示最终生效的启动命令。

此外,很多的数据库软件的docker镜像,一般在entrypoint的位置会设置一个docker-entrypoint.sh文件,此文件位于/usr/local/bin位置,用于在容器初次启动的时候进行数据库的初始化操作。
Kubernetes Command & args
下表总结了Docker和Kubernetes使用的字段名称:

当你覆盖默认的Entrypoint和Cmd时,将应用以下规则:

如果不为容器提供command或args参数,则使用Docker镜像中定义的默认值。
如果提供command但没有提供args参数,则仅使用提供的command。Docker镜像中定义的默认EntryPoint和默认Cmd将被忽略。
如果仅为容器提供args,则Docker镜像中定义的默认Entrypoint将与您提供的args一起运行。
如果提供command和args,则将忽略Docker镜像中定义的默认Entrypoint和默认Cmd。 您的command与 args一起运行。
可以看到,k8s利用了Dockerfile的覆盖机制,使用command和args参数有选择性的覆盖了Docker镜像中的Entrypoint和Cmd启动参数,下面是一些例子:

 

使用command和args的例子:

apiVersion: v1
kind: Pod
metadata:
name: command-demo
labels:
purpose: demonstrate-command
spec:
containers:
– name: command-demo-container
image: debian
command: [“printenv”]
args: [“HOSTNAME”, “KUBERNETES_PORT”]
restartPolicy: OnFailure
参考资料:
https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/

python3 安装pyodbc失败

报错1: 关键报错信息: fatal error: sql.h: No such file or directory 

 

 

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
[root@centfos python38]# pip3 install pyodbc
Collecting pyodbc
Using cached https://files.pythonhosted.org/packages/81/0d/bb08bb16c97765244791c73e49de9fd4c24bb3ef00313aed82e5640dee5d/pyodbc-4.0.30.tar.gz
Installing collected packages: pyodbc
Running setup.py install for pyodbc ... error
ERROR: Command errored out with exit status 1:
command/usr/local/python38/bin/python3.8 -u -c 'import sys, setuptools, tokenize; sys.argv[0] = '"'"'/tmp/pip-install-rqyoyjuj/pyodbc/setup.py'"'"'; __file__='"'"'/tmp/pip-install-rqyoyjuj/pyodbc/setup.py'"'"';f=getattr(tokenize, '"'"'open'"'"', open)(__file__);code=f.read().replace('"'"'\r\n'"'"', '"'"'\n'"'"');f.close();exec(compile(code, __file__, '"'"'exec'"'"'))' install --record /tmp/pip-record-_cvw7biv/install-record.txt --single-version-externally-managed --compile
cwd: /tmp/pip-install-rqyoyjuj/pyodbc/
Complete output (14 lines):
running install
running build
running build_ext
building 'pyodbc' extension
creating build
creating build/temp.linux-x86_64-3.8
creating build/temp.linux-x86_64-3.8/src
gcc -pthread -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -DPYODBC_VERSION=4.0.30 -I/usr/local/python38/include/python3.8 -c src/buffer.cpp -o build/temp.linux-x86_64-3.8/src/buffer.o -Wno-write-strings
In file included from src/buffer.cpp:12:0:
src/pyodbc.h:56:17: fatal error: sql.h: No such file or directory
#include <sql.h>
^
compilation terminated.
error: command 'gcc' failed with exit status 1
----------------------------------------
ERROR: Command errored out with exit status 1: /usr/local/python38/bin/python3.8 -u -c 'import sys, setuptools, tokenize; sys.argv[0] = '"'"'/tmp/pip-install-rqyoyjuj/pyodbc/setup.py'"'"'; __file__='"'"'/tmp/pip-install-rqyoyjuj/pyodbc/setup.py'"'"';f=getattr(tokenize, '"'"'open'"'"', open)(__file__);code=f.read().replace('"'"'\r\n'"'"', '"'"'\n'"'"');f.close();exec(compile(code, __file__, '"'"'exec'"'"'))' install --record /tmp/pip-record-_cvw7biv/install-record.txt --single-version-externally-managed --compile Check the logs for full command output.
WARNING: You are using pip version 19.2.3, however version 20.0.2 is available.
You should consider upgrading via the 'pip install --upgrade pip' command.

 

 

 

 

解决方法:

1
[root@centos python38]# sudo yum install unixODBC-devel

 

然后再安装pyodbc:

1
2
3
4
5
6
7
8
[root@centos python38]# pip3 install pyodbc
Collecting pyodbc
Using cached https://files.pythonhosted.org/packages/81/0d/bb08bb16c97765244791c73e49de9fd4c24bb3ef00313aed82e5640dee5d/pyodbc-4.0.30.tar.gz
Installing collected packages: pyodbc
Running setup.py install for pyodbc ... done
Successfully installed pyodbc-4.0.30
WARNING: You are using pip version 19.2.3, however version 20.0.2 is available.
You should consider upgrading via the 'pip install --upgrade pip' command.

 

 

 

报错2: 关键报错信息: ModuleNotFoundError: No module named ‘_ctypes’

 

解决方法:

1
yum install libffi-devel

然后从config开始重新编译安装。

Docker 时区问题

一、问题描述
遇到docker时间不一致,大多是因为默认时区没有设置导致,一般在宿主机上使用 date 命令看到的是 CTS 时间,进入docker后使用 date 命令查看的是 UTC 时间。
CTS: China Standard Time,UTC+8:00 中国沿海时间(北京时间)
UTC: Universal Time Coordinated 世界协调时间

所以我们需要修改 docker 容器的时区,针对不同场景修改的方式也不一样,大家可以参考一下选择最适合自己的修改方式

二、修改 Dockerfile 文件方式更改时区
适用场景:如果我们是用 Dockerfile 方式构建的镜像,那么我们可以修改 Dockerfile 让构建的镜像直接为东八区

在 Dockerfile 文件中 添加

ENV TZ=Asia/Shanghai

重新构建镜像,查看时间,发现已经是东八区,成功。

三、新增 run 参数更改时区
适用场景:不想重新构建镜像,删除容器重新部署对程序本身没有影响

删除 容器

docker rm -f tomcat
run 的时候加上参数

-e TZ=”Asia/Shanghai”
例如

docker run -e TZ=”Asia/Shanghai” -p 8090:8090 -d –name ch ch/ch
-e 时区改为东八区,默认时区为0区

-p 暴露端口 前面的端口(docker tomcat 容器端口) 后面的端口(宿主机器端口,我们要在浏览器访问的端口)

-d 后台运行(不会在控制台输出日志)

完成后 查看时间,已经是东八区,成功

四、修改 tomcat 配置文件更改时区
适用场景:tomcat 容器运行的程序,不想重新构建镜像容器

注意:交付的Docker映像已修剪到最低限度-因此,拉取的容器未安装任何编辑器,所以无法 进入到容器编辑配置文件。

所以得将配置文件 copy 一份到本地

docker cp tomcat:/usr/local/tomcat/bin/catalina.sh .

修改 catalina.sh 配置文件

找到 JAVA_OPTS=”$JAVA_OPTS -Djava.protocol.handler.pkgs=org.apache.catalina.webresources” 这一行 行尾添加 Duser.timezone=GMT+08

JAVA_OPTS=”$JAVA_OPTS -Djava.protocol.handler.pkgs=org.apache.catalina.webresources -Duser.timezone=GMT+08″
然后将修改后的配置文件,覆盖掉容器内的

docker cp .\catalina.sh tomcat:/usr/local/tomcat/bin/catalina.sh
最后重启容器

docker restart tomcat
发现时区已经改为 东八区 ,成功。
————————————————
版权声明:本文为CSDN博主「码农农码一生」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/chenhao_c_h/article/details/110041401

jenkins BlueOcean修复Pipeline不支持中文的问题

问题

在Jenkins的BlueOcean中,修改Pipeline,结果发现,如果编写过程中,凡是能导致jenkinsFile有中文信息的,点击 save and run之后 Console 中出现错误,无法保存。

分析

根据错误信息,定位到问题来源jenkins-js-extension.js,该问题和 js的btoa和atob 不支持unicode有关。

解决

  1. 将jenkins的./webapps/plugins/blueocean-pipeline-editor/WEB-INF/lib/blueocean-pipeline-editor.jar下载到本地,
  2. 解压出jenkins-js-extension.js,搜索btoa,有两处一样的代码,搜索atob也是一样的,成对出现。
  3. 修改代码,将两处encode和decode修改为以下结果:
  4. 将修改之后的jenkins-js-extension.js拖入blueocean-pipeline-editor.jar。
  5. 将修改之后的blueocean-pipeline-editor.jar上传到Jenkins的原处:./webapps/plugins/blueocean-pipeline-editor/WEB-INF/lib/blueocean-pipeline-editor.jar
var Base64 = {
    encode: function encode(data) {
        return btoa(unescape(encodeURIComponent(data)));
    },
    decode: function decode(str) {
        return decodeURIComponent(escape(atob(str)));
    }
};

An error occurred during installation: No such plugin: cloudbees-folder

在启动jenkins时候报错

An error occurred during installation: No such plugin: cloudbees-folder
1
字面意思是没有找到cloudbees-folder这个插件。有一些文章说下载这个插件到本地就好了。

然而jenkins启动的时候不仅仅有这一个插件。

https://github.com/jenkinsci/docker/issues/424 github issues里有一些讨论。似乎重启jenkins就可以了

# 访问这个地址就是重启
http://yourhost:8080/restart
1
2
然后我这里看来并不管用。

最后无意中发现我拉取的镜像是jenkins…并非jenkins/jenkins。我用的是docker…

当尝试拉取后者并启动之后,就没有这个报错了。

k8s创建Deployment报错:no matches for kind “Deployment“ in version “extensions/v1beta1“

报错类型:

[root@master ~]# kubectl create -f lzb-test.yaml
error: unable to recognize “lzb-test.yaml”: no matches for kind “Deployment” in version “extensions/v1beta1”
解决:

修改yaml文件:


apiVersion: extensions/v1beta1
kind: Deployment
metadata:
修改如下:


apiVersion: apps/v1
kind: Deployment
这个主要是由于版本升级的原因

我的 k8s 版本是 1.18.5

在这个版本中 Deployment 已经启用extensions/v1beta1

DaemonSet, Deployment, StatefulSet, and ReplicaSet resources will no longer be served from extensions/v1beta1, apps/v1beta1, or apps/v1beta2 by default in v1.16.

Rook用还是不用,这就是Kubernetes

就在今年的五月份,Rook官方宣告Rook 1.0.0主版本发布了,“可用于Kubernetes,已经生产就绪的云原生存储”。大约在一年前,这个解决方案首次出现在我们的视野里,但是距离我们实际使用它又过去了一段时间。最后,我们很高兴在这里分享我们学到的一些经验教训。

0_MrxXNO55N-rSYLEb.png

简而言之,Rook就是一组Kubernetes的Operator,它可以完全控制多种数据存储解决方案(例如Ceph、EdgeFS、Minio、Cassandra)的部署,管理以及自动恢复。

到目前为止,rook-ceph-operator仍然是最先进(也是唯一一个稳定的)解决方案。

注意:Rook 1.0.0这次发布了一些和Ceph有关的显著特性,包括对Ceph Nautilus的支持,以及启动NFS守护进程用于导出CephFS卷或者RGW桶的CephNFS CRD。此外,新版本还加入了期待已久的对EdgeFS的beta级支持。

在本文中,我们将会:

  • 解答在Kubernetes集群里使用Rook来部署Ceph有什么好处的问题;
  • 分享我们在生产环境使用Rook的一些经验和见解;
  • 解释我们为什么对Rook说”Yes”的原因并分享我们的未来规划。

但是现在,让我们先从一些通俗的概念和理论讲起吧。

“Rook是我的强项!”(来自一位不知名的棋手)

0_DGLCeT9iunOYK6Oh.png

使用Rook的其中一个主要好处在于它是通过原生的Kubernetes机制和数据存储交互。这就意味着你不再需要通过命令行手动配置Ceph。

  • 你想要在一个集群里部署CephFS吗?只需要创建一个YAML文件就行了!
  • 什么?你还想要部署一个支持S3 API的对象存储?行,另外再建一个YAML文件就行!

Rook具备了一个典型的Kubernetes Operator的所有功能。与它的交互依赖于自定义资源定义(CRD)来描述我们需要的Ceph实例的一些属性(除非另有明确说明,否则在本文的其余部分中,我们将会隐去Ceph的字眼,因为它是Rook目前唯一稳定的存储解决方案)。通过使用给定的参数,一个Operator将会自动执行设置所需的命令。

我们不妨看一个创建对象存储的具体示例,以CephObjectStoreUser为例:

apiVersion: ceph.rook.io/v1
kind: CephObjectStore
metadata:
name: {{ .Values.s3.storeName }}
namespace: kube-rook
spec:
metadataPool:
failureDomain: host
replicated:
  size: 3
dataPool:
failureDomain: host
erasureCoded:
  dataChunks: 2
  codingChunks: 1
gateway:
type: s3
sslCertificateRef:
port: 80
securePort:
instances: 1
allNodes: false
---
apiVersion: ceph.rook.io/v1
kind: CephObjectStoreUser
metadata:
name: {{ .Values.s3.username }}
namespace: kube-rook
spec:
store: {{ .Values.s3.storeName }}
displayName: {{ .Values.s3.username }} 

上述清单里列出的参数都是一些常见配置,无需进一步说明。不过这里要重点关注一下参数值指定为模板变量的部分。

一般流程的结构如下:我们会通过一个YAML文件请求资源,然后一个operator会去执行所有必要的命令并返回一个“不那么真实”的secret,我们可以据此进行接下来的工作(参见下文)。然后,基于上述提供的变量,Rook会帮助用户生成要执行的命令以及secret的名称。

什么命令呢?当Rook Operator为对象存储创建一个用户时,它会在Pod里执行这条命令:

radosgw-admin user create --uid={{ .Values.s3.username }} --display-name={{ .Values.s3.username }} --rgw-realm={{ .Values.s3.storeName }} --rgw-zonegroup={{ .Values.s3.storeName }} 

它会生成如下结构的JSON数据:

{
"user_id": "{{ .Values.s3.username }}",
"display_name": "{{ .Values.s3.username }}",
"keys": [
    {
       "user": "{{ .Values.s3.username }}",
       "access_key": "NRWGT19TWMYOB1YDBV1Y",
       "secret_key": "gr1VEGIV7rxcP3xvXDFCo4UDwwl2YoNrmtRlIAty"
    }
],
...
} 

这里的keys在将来用于通过S3 API为应用程序提供访问对象存储的权限。Rook Operator将它们照单全收,然后以rook-ceph-object-user-{{ .Values.s3.crdName }}-{{ .Values.s3.username }}的格式作为secret存储到它的namespace里。

为了使用保存到secret里的数据,所有你要做的就是通过环境变量把它们传入到容器里。这里有一个Job模板的示例,它会自动给每个用户环境创建对应的bucket:

{{- range $bucket := $.Values.s3.bucketNames }}
apiVersion: batch/v1
kind: Job
metadata:
name: create-{{ $bucket }}-bucket-job
namespace: kube-rook
annotations:
"helm.sh/hook": post-install, post-upgrade
"helm.sh/hook-weight": "2"
spec:
template:
metadata:
  name: create-{{ $bucket }}-bucket-job
spec:
  restartPolicy: Never
  initContainers:
  - name: waitdns
    image: alpine:3.6
    command: ["/bin/sh", "-c"]
    args:
    - "while ! getent ahostsv4 rook-ceph-rgw-{{ $.Values.s3.storeName }}; do sleep 1; done"
  - name: config
    image: rook/toolbox:v0.7.1
    command: ["/bin/sh", "-c"]
    args:
    - >-
      s3cmd --configure --access_key=$(ACCESS-KEY) --secret_key=$(SECRET-KEY)
      --no-ssl --dump-config
      --host=rook-ceph-rgw-{{ $.Values.s3.storeName }}
      --host-bucket=rook-ceph-rgw-{{ $.Values.s3.storeName }}
      | tee /config/.s3cfg
    volumeMounts:
      - name: config
        mountPath: /config
    env:
    - name: ACCESS-KEY
      valueFrom:
        secretKeyRef:
          name: rook-ceph-object-user-{{ $.Values.s3.storeName }}-{{ $.Values.s3.username }}
          key: AccessKey
    - name: SECRET-KEY
      valueFrom:
        secretKeyRef:
          name: rook-ceph-object-user-{{ $.Values.s3.storeName }}-{{ $.Values.s3.username }}
          key: SecretKey
  containers:
  - name: create-bucket
    image: rook/toolbox:v0.7.1
    command: ["s3cmd", "mb", "s3://{{ $bucket }}"]
    ports:
    - name: s3-no-sll
      containerPort: 80
    volumeMounts:
    - name: config
      mountPath: /root
  volumes:
  - name: config
    emptyDir: {}
---
{{- end }} 

这个Job的所有操作仅在Kubernetes内部执行。YAML文件里描述的数据结构已经被添加到了Git仓库里以便复用。对于DevOps工程师和整体的CI/CD流程而言,这个功能真是棒极了。目前,Rook团队已经计划使用Bucket置备库,这将会使你和S3 bucket的交互更加方便。

通过Rook处理RADOS

传统的Ceph + RBD组合在为Pod挂载卷时施加了特定的限制。

换句话说,namespace必须包含访问Ceph的secret这样有状态应用才可以正常工作。如果你有,比方说,namespace里有2-3个环境的话,这很容易做到:你可以手动复制secret。但是如果针对每个功能都创建一个具有自己的namespace的单独环境的话该怎么办呢?我们已经通过shell-operator解决了这个问题,它会自动将secret复制到新的namespace里(在这篇文章中你可以找到一个这样的hook示例)。

#!/bin/bash
if [[ $1 == "--config" ]]; then
cat <<EOF
{"onKubernetesEvent":[
{"name": "OnNewNamespace",
"kind": "namespace",
"event": ["add"]
}
]}
EOF
else
NAMESPACE=$(kubectl get namespace -o json | jq '.items | max_by( .metadata.creationTimestamp ) | .metadata.name')
kubectl -n ${CEPH_SECRET_NAMESPACE} get secret ${CEPH_SECRET_NAME} -o json | jq ".metadata.namespace=\"${NAMESPACE}\"" | kubectl apply -f -
fi

但是,如果使用Rook的话就不存在这个问题了。卷的挂载流程是基于Flexvolume或者CSI(目前还是beta阶段)驱动,不需要任何secret。

Rook通过一些手段自动化地自行解决了这些问题,因此我们倾向于将它用于我们的新项目。

上手Rook

让我们通过安装Rook和Ceph来结束实战部分,这样你便可以实际上手体验。为了简化安装过程,开发人员提供了一个Helm包。让我们下载它:

$ helm fetch rook-master/rook-ceph --untar --version 1.0.0

你可以在rook-ceph/values.yaml文件里找到许多不同的设置。这里最重要的是为discover和agent指定toleration。这里我们不打算过多涉及Kubernetes里的taints和tolerations的细节,你只需要知道我们不想让应用的Pod调度到带有数据存储盘的节点上即可。理由也是显而易见的:这样做的话,我们可以避免Rook的Agent影响应用程序本身。

如今是时候在你喜欢的文本编辑器里打开rook-ceph/values.yaml然后将下列部分追加到文件的末尾:

discover:
toleration: NoExecute
tolerationKey: node-role/storage
agent:
toleration: NoExecute
tolerationKey: node-role/storage
mountSecurityMode: Any

我们还为每个保留用于数据存储的节点打上了相应的污点(taint):

$ kubectl taint node ${NODE_NAME}  node-role/storage="":NoExecute

然后通过下列命令安装Helm chart:

$ helm install --namespace ${ROOK_NAMESPACE} ./rook-ceph

现在我们可以去创建集群,然后指定OSD的路径:

apiVersion: ceph.rook.io/v1
kind: CephCluster
metadata:
clusterName: "ceph"
finalizers:
- cephcluster.ceph.rook.io
generation: 1
name: rook-ceph
spec:
cephVersion:
image: ceph/ceph:v13
dashboard:
enabled: true
dataDirHostPath: /var/lib/rook/osd
mon:
allowMultiplePerNode: false
count: 3
network:
hostNetwork: true
rbdMirroring:
workers: 1
placement:
all:
  tolerations:
  - key: node-role/storage
    operator: Exists
storage:
useAllNodes: false
useAllDevices: false
config:
  osdsPerDevice: "1"
  storeType: bluestore
nodes:
- name: ceph01
  deviceFilter: ^sd[b-i]
- name: ceph02
  deviceFilter: ^sd[b-i]
- name: ceph03
  deviceFilter: ^sd[b-i]

Ceph的状态应当是HEALTH_OK

$ kubectl -n ${ROOK_NAMESPACE} exec $(kubectl -n ${ROOK_NAMESPACE} get pod -l app=rook-ceph-operator -o name -o jsonpath='{.items[0].metadata.name}') -- ceph -s

我们还要确保应用程序的Pod不会调度到那些为Ceph保留的节点上去:

$ kubectl -n ${APPLICATION_NAMESPACE} get pods -o custom-columns=NAME:.metadata.name,NODE:.spec.nodeName

现在你可以定制其他额外的组件了。相关的信息可以在文档里找到。为了能够让我们在集群里的后续操作变得更加顺滑,我们强烈推荐开启Dashboard并且部署toolbox

人无完人:Rook的不足之处是什么?

如你所见,Rook的开发进展顺利。但是存在一些问题,使得一些Ceph的手动配置仍然无法避免。

  • 目前没有Rook驱动可以导出展示已挂载的区块的使用情况的指标数据,因此我们无法监控它们的状态(Rook v1.0.3在Flexvolume卷上加入了这项功能)。
  • Flexvolume和CSI(与RBD不同)无法调整卷的大小,因此Rook缺乏这项有用的(有时甚至非常关键)工具。(在写完这篇文章后不久,业内已经出现了一些实现方案但是也仅仅只是针对Flexvolume;不过也有讨论到CSI)
  • Rook仍然不如Ceph灵活。举个例子,要将CephFS元数据存储到SSD上,然后将相关数据存储到HDD时,你必须得在CRUSH映射里手动定义每组设备。
  • 尽管rook-ceph-operator被视为已经稳定了,但是从Ceph 13升级到14版本的过程中仍然会遇到一些问题。

 

小结

“今天Rook是通过小兵们来抵御外面的世界,但是终有一天它会在游戏里扮演一个至关重要的角色!”(这句话简直像是为本文量身定制的。)

我们无疑非常喜欢Rook项目,尽管有许多利弊,但是我们相信它绝对值得你的关注。

我们未来的计划归结为将rook-ceph变成我们addon-operator的一个模块。这样一来,我们可以很轻松方便地在我们维护的大量Kubernetes集群里使用它。

java8报错”Could not create the Java Virtual Machine”解决方案

 

本次实验环境:winjava@8。本篇内容主要讲这个大乌龙,而不是ide安装。

报错截图

主要的报错信息就是:

Could not create the Java Virtual Machine
Plain text

 

苏南大叔:java8报错"Could not create the Java Virtual Machine"解决方案 - java000

java8报错”Could not create the Java Virtual Machine”解决方案(图5-2)

 

乌龙的起源

这个乌龙,按理说还要归责于java身上。因为java版本升级到现在,用了一个和别人不一样的参数调用方式,而且居然不提示命令错误。而是提示如图这个错误消息。 “Could not create the Java Virtual Machine”。

其他可以在命令行里面运行的软件,包括java的早期版本。都是使用 --version 来查看软件版本的,而不是 -version。 而目前的最流行的java8系列,版本号查看方式却是 -version 。注意查看中划线的数量。这就是大乌龙的根本原因所在。

java -version
Plain text
java --version
Plain text

无论是win环境还是mac环境,都存在着这个问题。问题会导致,大家认为java根本没有安装好。

win:

 

苏南大叔:java8报错"Could not create the Java Virtual Machine"解决方案 - java001

java8报错”Could not create the Java Virtual Machine”解决方案(图5-3)

 

mac:

 

苏南大叔:java8报错"Could not create the Java Virtual Machine"解决方案 - java002

java8报错”Could not create the Java Virtual Machine”解决方案(图5-4)

 

然而转折

但是最新java9系列,又再次支持了--version命令。真心是没谁了。

 

苏南大叔:java8报错"Could not create the Java Virtual Machine"解决方案 - 030_java_version

java8报错”Could not create the Java Virtual Machine”解决方案(图5-5)

 

结论

学艺不精,主观臆断。也是苏南大叔的这次大乌龙的原因之一了。大家可千万别再犯苏南大叔这样的错误哦。java8系列是不支持--version的,但是java9却支持--version。所以,在未来的日子里面,大家还是可以以相同的习惯,继续使用--version的。