关于k8s的B2I容器化完整解决方案,使用docker-java将jar包构建镜像并推送至远程镜像仓库,并能在k8s中运行
一站式服务,从入门到入土,嘴对嘴一口口喂您,不要一分钱,全部免费送。先谢赞,人在美国,刚下飞机,已跟小姨子私奔。后面会再写一篇k8s调k8sApiService.createDeployments()接口通过已上传的镜像实现自动化部署的教程,这样我们就可以实现使用k8s无限套娃(我部署我自己,或者说是我部署我自己的克隆人),敬请期待。那么我们开始正题吧:1.开放docker.service加上:-
一站式服务,从入门到入土,嘴对嘴一口口喂您,不要一分钱,全部免费送。
先谢赞,人在美国,刚下飞机,已跟小姨子私奔。
后面会再写一篇k8s调k8sApiService.createDeployments()接口通过已上传的镜像实现自动化部署的教程,这样我们就可以实现使用k8s无限套娃(我部署我自己,或者说是我部署我自己的克隆人),敬请期待。
那么我们开始正题吧:
1.开放docker.service
加上:-H tcp://0.0.0.0:2375 -H unix://var/run/docker.sock --insecure-registry=172.16.0.131
vi /lib/systemd/system/docker.service
ExecStart=/usr/bin/dockerd -D --tlsverify=true --tlscert=/home/user/certs/server-cert.pem --tlskey=/home/user/certs/server-key.pem --tlscacert=/home/user/certs/ca.pem -H fd:// --containerd=/run/containerd/containerd.sock --exec-opt native.cgroupdriver=systemd -H tcp://0.0.0.0:2375 -H unix://var/run/docker.sock --insecure-registry=172.16.0.131
systemctl daemon-reload
service docker restart//重启启动docker
systemctl status docker/
2.Docker签名认证,生成docker ce.pem等证书
第一步:
首先,我们需要选择一个放证书的文件夹,这个文件夹很多文章,包括官网都建议创建一个.docker文件夹,我个人认为,这个文件夹在哪里不重要,只要能保证服务器安全,防火墙有效,就可以了。
在/home/user/下面创建了一个/certs/文件夹。用$pwd,就可以看到该文件夹是/home/user/certs/。转到该文件夹,
执行如下命令:$ openssl genrsa -aes256 -out ca-key.pem 4096,
生成CA私钥,并设置pass phrase,我设置的就是123456,比较简单,因为是测试环境。但是要记住这个密码,后边命令还会用到。
再输入:$ openssl req -new -x509 -days 365 -key ca-key.pem -sha256 -out ca.pem,
生成CA公钥,也就是证书。还让你输入国家名,省名啥的,这些都是随便填一个就行,因为密钥算法会把这些信息加密进密钥的。值得一提的是Common Name,说是要你填写,server FQDN或your name,意味着可以随便写,但是我在这里建议,写Docker所在服务器的IP,这个很重要。这个IP后边还会用到,我这里是192.168.99.101,在生产环境下,用使用你docker宿主机的DNS name替换下面的填入Common name,如api.google.com等,这个IP不难拿到,你用$ifconfig命令就可以拿到。我在这里填CDH是错误的
第二步:
生成服务器私钥,命令如下:
$openssl genrsa -out server-key.pem 4096
再用私钥生成服务器公钥请求文件,也就是证书,命令如下:
$openssl req -subj "/CN=172.16.0.131" -sha256 -new -key server-key.pem -out server.csr,
这里的172.16.0.131同样是Docker所在服务器的IP,用自己的Docker服务器替换上去。
下面我们可以用CA来签署证书了。这里我们可以填写IP地址或则DNS name,如,我们需要允许10.10.10.20和127.0.0.1连接:
$echo subjectAltName = IP:10.10.10.20, IP:127.0.0.1 > extfile.cnf,
上述命令有点像一个过滤器,如果地址填的不全,远程API就无法访问该Docker,那么我们就把,地址填的全一些,我的命令是这样滴:
$echo subjectAltName = DNS:172.16.0.131, IP: 172.16.0.131, IP:0.0.0.0, IP:127.0.0.1 > extfile.cnf
然后,将上述多个生成信息,写入文件。用如下命令。
$openssl x509 -req -days 365 -sha256 -in server.csr -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out server-cert.pem -extfile extfile.cnf
再看客户端私钥:
$openssl genrsa -out key.pem 4096
下一步再生成客户端证书请求文件:
$openssl req -subj '/CN=client' -new -key key.pem -out client.csr
用CA为客户端签署证书文件:
$openssl x509 -req -days 365 -sha256 -in client.csr -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out cert.pem -extfile extfile.cnf
这时候,还需要输入密码,我的密码是admin123,输上去即可。
第三步:使用证书
还是要回到我们上文提到的docker.service文件中,那个文件里需要添加上你为它生成的文件的路径和文件名。
ExecStart=/usr/bin/dockerd -D --tlsverify=true --tlscert=/home/user/certs/server-cert.pem --tlskey=/home/user/certs/server-key.pem --tlscacert=/home/user/certs/ca.pem -H tcp://0.0.0.0:2376 -H fd://
这里把所有的认证文件都加上了准确的路径。
再重新装载配置文件
$sudo systemctl daemon-reload,回车
$sudo service docker restart,回车
$sudo service docker status,来查看进程状态。
参考:https://blog.csdn.net/yaofeng_hyy/article/details/80923941?utm_source=blogxgwz2
或者使用脚本自动生成:安全的docker-api的使用(java)
3.部署这个微服务时,需要用到 ce.pem等证书,所以在Dockerfile里构建容器时需要copy进容器,或者也可以选择yaml挂载证书。
#基础镜像,如果本地仓库没有,会从远程仓库拉取
FROM openjdk:8-jdk-alpine
#容器中创建目录
RUN mkdir -p /usr/local/pasq
RUN mkdir -p /usr/local/pasq/file
#编译后的jar包copy到容器中创建到目录内
COPY k8s-0.0.1-SNAPSHOT.jar /usr/local/pasq/k8s.jar
COPY key.pem /usr/local/pasq/key.pem
COPY ca-key.pem /usr/local/pasq/ca-key.pem
COPY ca.pem /usr/local/pasq/ca.pem
COPY cert.pem /usr/local/pasq/cert.pem
#指定容器启动时要执行的命令
ENTRYPOINT ["java","-jar","/usr/local/pasq/k8s.jar"]
4.yaml挂载文件路径,里面会存储程序里生成的jar和Dockerfile
deployment.yaml:注意volumeMounts和volumes里的name要相同
apiVersion: apps/v1
kind: Deployment
metadata:
name: k8s062908-deploy
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app: k8s062908
release: stabel
template:
metadata:
labels:
app: k8s062908
release: stabel
env: test
spec:
containers:
- name: k8s062908
#这里注意是命名空间+仓库名称
image: registry.cn-hangzhou.aliyuncs.com/xxxxx/k8s062908:1.0
#本地有的话取本地,没有的话取远程仓库
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 80
#容器内挂载点
volumeMounts:
- mountPath: /usr/local/pasq/file
name: vm-patha
volumes:
- name: vm-patha
hostPath:
path: /usr/local/pasq/file
service.yaml正常写就行
apiVersion: v1
kind: Service
metadata:
name: k8s062908
namespace: default
spec:
type: NodePort
selector:
app: k8s062908
release: stabel
ports:
- name: http
port: 8080
targetPort: 8080
nodePort: 31028
5.编写上传附件接口
<body>
<p>单文件上传</p>
<form method="POST" enctype="multipart/form-data" action="/api/v1/loadToRepository/yangsenjohnson/2.0/" id="fm">
文件:<input type="file" name="file"/>
</form>
<button id="submit" style="height: 40px;width: 80px;margin-top: 20px">提交</button>
<script>
var s = document.getElementById("submit");
s.onclick = function(){
var fm = document.getElementById("fm");
fm.submit();
}
</script>
</body>
起一个新线程:
try {
String imageName =
(("".equals(registryAddress) || registryAddress == null) ? "" : (registryAddress + "/"))
+ project
+ "/"
+ file.getOriginalFilename().split("\\.")[0]
+ ":"
+ version;
String rUsername = new String(registryUserName.getBytes("ISO-8859-1"), "utf-8");
if (file.isEmpty()) {
System.out.println("upload Jar isEmpty loadImageToRepository");
}
new UploadThread(
registryAddress,
dockerHost,
rUsername,
registryPassword,
file.getOriginalFilename(),
imageName,
file.getInputStream(),
file,
dockerCertPath,
dockerPath,
dockerVersion)
.start();
} catch (IOException e) {
System.err.println("获取文件流失败");
}
6.通过k8s API的readNamespacedSecret获取保存的docker密钥
7.通过docker-java的dockerClient.pushImageCmd接口进行推送镜像至阿里云镜像仓库
1)file文件流只能读取一次,需要放在最前面处理,之前放在后面的buildImageCmd之前处理时,file.isEmpty()会为空,这样file.transferTo(dest)时会找不到临时文件,
// 获取上传的文件名 start file文件流只能读取一次,需要放在最前面处理
String fileName = file.getOriginalFilename();
System.out.println("upload Jar filename1 " + fileName);
String filepath = dockerPath;
System.out.println("upload Jar filepath 1" + filepath);
File dest = new File(filepath + "/" + fileName);
System.out.println("upload Jar path1: " + filepath + "/" + fileName);
if (dest.isFile()) {
System.out.println("upload Jar isFile1 ");
}
System.out.println("upload Jar 21: ");
try {
if (!dest.isDirectory()) { // 如果文件夹不存在就新建
dest.mkdirs();
}
if (dest.isAbsolute()) {
System.out.println("upload Jar isAbsolute1:");
}
System.out.println("upload Jar 21: ");
file.transferTo(dest);
System.out.println("upload Jar to path success");
} catch (IOException e) {
e.printStackTrace();
System.out.println("upload Jar to path failed" + e.getMessage());
}
// end file文件流只能读取一次,需要放在最前面处理
2)将docker的信息注入authConfig
//如果registryAddress为空则为dockerhub,不需要拼接registryAddress
AuthConfig authConfig = new AuthConfig();
// new AuthConfig().withUsername(registryUserName).withPassword(registryPassword);
if(("".equals(registryAddress)||registryAddress==null)){
authConfig.withUsername(registryUserName).withPassword(registryPassword);
}else{
authConfig.withUsername(registryUserName).withPassword(registryPassword)
.withRegistryAddress(registryAddress);
}
3)实例化dockerClient
DockerClientConfig dockerClientConfig =
DefaultDockerClientConfig.createDefaultConfigBuilder()
.withDockerHost(dockerHost)
.withDockerTlsVerify(true)
.withDockerCertPath(dockerCertPath)
.withDockerConfig(dockerCertPath)
.withApiVersion(dockerVersion)
.withRegistryUrl("https://index.docker.io/v1/")
.withRegistryUsername("username")
.withRegistryPassword("password")
.withRegistryEmail("email")
.build();
DockerCmdExecFactory dockerCmdExecFactory =
new JerseyDockerCmdExecFactory()
.withReadTimeout(60000)
.withConnectTimeout(1000)
.withMaxTotalConnections(100)
.withMaxPerRouteConnections(10);
DockerClient dockerClient =
DockerClientBuilder.getInstance(dockerClientConfig)
.withDockerCmdExecFactory(dockerCmdExecFactory)
.build();
4)加载镜像
// 加载镜像
LoadImageCmd loadImageCmd = dockerClient.loadImageCmd(inputStream);
loadImageCmd.exec();
5)生成Dockerfile
ForFile forFile = new ForFile(dockerPath);
if (new File(file.getOriginalFilename()).exists()) {
System.out.println(file.getOriginalFilename() + " exists ");
}
if (new File(dockerPath + "/" + file.getOriginalFilename()).exists()) {
System.out.println(dockerPath + "/" + file.getOriginalFilename() + " exists ");
}
if (new File("./" + file.getOriginalFilename()).exists()) {
System.out.println("./" + file.getOriginalFilename() + " exists ");
}
String filecontent =
"FROM openjdk:8-jdk-alpine\n"
+ "RUN mkdir -p /usr/local/pasq\n"
+ "RUN mkdir -p /usr/local/pasq/file\n"
+ "COPY "
// + "./"
+ file.getOriginalFilename()
+ " /usr/local/pasq/file/"
+ file.getOriginalFilename()
+ "\n"
+ "ENTRYPOINT [\"java\",\"-jar\",\"/usr/local/pasq/file/"
+ file.getOriginalFilename()
+ "\"]";
System.out.println(filecontent);
File docFileDir = forFile.createFile("Dockerfile", filecontent);
/**
* 创建文件
*
* @param fileName 文件名称
* @param filecontent 文件内容
* @return 是否创建成功,成功则返回true
*/
public static File createFile(String fileName, String filecontent) {
Boolean bool = false;
filenameTemp = path + "/" + fileName; // 文件路径+名称+文件类型
File file = new File(filenameTemp);
try {
// 如果文件不存在,则创建新的文件
if (!file.exists()) {
file.createNewFile();
bool = true;
System.out.println("success create file,the file is " + filenameTemp);
}
// 创建文件成功后,写入内容到文件里
writeFileContent(filenameTemp, filecontent);
} catch (Exception e) {
e.printStackTrace();
}
return file;
}
6)根据Dockerfile构建镜像(看网上的教程都没有这一步,但实际开发过程中发现,没有build只有loadImageCmd的话无法构建jar包,所以需要buildImageCmd)
BuildImageResultCallback callback =
new BuildImageResultCallback() {
@Override
public void onNext(BuildResponseItem item) {
System.out.println("BuildResponseItem" + item);
super.onNext(item);
}
};
String imageid = originalImageName;
BuildImageCmd buildImageCmd = dockerClient.buildImageCmd(docFileDir);
imageid = buildImageCmd.exec(callback).awaitImageId();
System.out.println("imageid:" + imageid);
7)获取build返回的imageid后,打tag
dockerClient.tagImageCmd(imageid, imageName.split(":")[0], imageName.split(":")[1]).exec();
8)pushImageCmd
// push至镜像仓库
PushImageResultCallback pushImageResultCallback =
new PushImageResultCallback() {
@Override
public void onNext(PushResponseItem item) {
System.out.println("id:" + item.getId() + " status: " + item.getStatus());
super.onNext(item);
}
@Override
public void onComplete() {
System.out.println("Image pushed completed!");
super.onComplete();
}
@Override
public void onError(Throwable throwable) {
System.out.println("Image pushed onError!");
super.onError(throwable);
}
};
dockerClient
.pushImageCmd(imageName.split(":")[0])
.withTag(imageName.split(":")[1])
.withAuthConfig(authConfig)
.exec(pushImageResultCallback)
.awaitSuccess();
参考:https://blog.csdn.net/u012843873/article/details/84318793
更多推荐
所有评论(0)