关于k8s的B2I容器化完整解决方案,使用docker-java将jar包构建镜像并推送至远程镜像仓库,并能在k8s中运行

一站式服务,从入门到入土,嘴对嘴一口口喂您,不要一分钱,全部免费送。
先谢赞,人在美国,刚下飞机,已跟小姨子私奔。

后面会再写一篇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();

9)要部署的时候,需要拉取镜像到对应的node。需要实现pull,思考一下在哪个步骤实现。

参考:https://blog.csdn.net/u012843873/article/details/84318793

热门文章

暂无图片
编程学习 ·

UDP通信关于recvfrom问题

server端bind自己的IP后和client端通信,再recvfrom后,打印源地址和目的地址一致,那个大神帮忙解释一下,附上两端的代码
暂无图片
编程学习 ·

NLP 任务中有哪些巧妙的 idea?

文章目录1. 分布式假设(Distributional Hypothesis)2. 词袋模型(Bag-of-Words)3. 潜在语义分析(Latent Semantic Analysis)4. 概率主题模型(Probabilistic Topic Models )5. 基于BMES的中文分词或基于BIO的NER/Chunking6. 基于PageRank的TextRank转载来源:https://www…
暂无图片
编程学习 ·

关于table中合并单元格的一些问题

关于table中合并单元格的一些问题 首先要明白列表中定义tr为一行,td为一行中的单元格,也就是列的数量。所以在合并行(rowspan)时,得到的是新的一列。代码运行结果如下。<tr><td></td><td></td><td rowspan="3"></td>…
暂无图片
编程学习 ·

Spark1.x升级Spark2.x常见异常Kafka篇【TopicMetadataRequest】

一.原因分析 当Spark从1.x升级到2.x时,如果使用SparkStreaming加载Kafka的数据,即使Kafka版本没有变化【一般会有所升级】,对应的spark-streaming-kafka也必须升级到对应版本,访问方式也会有所变化。 此处是从Spark1.6.0升级到Spark2.4.3,Kafka略有升级【从2.1.0升级到2.2…
暂无图片
编程学习 ·

日志框架 SLF4j

是什么:SLF4J,即简单日志门面(Simple Logging Facade for Java),不是具体的日志解决方案,它只服务于各种各样的日志系统。按照官方的说法,SLF4J是一个用于日志系统的简单Facade,允许最终用户在部署其应用时使用其所希望的日志系统为什么:在java.util.logging, logback…
暂无图片
编程学习 ·

什么是体感互动以及其优势在哪

体感互动通常指的是隔空互动,通过体感设备,来检测人体,通过景物深度处理技术把人物从摄像头捕捉到的画面中分离出来; 随着手指的挥动、在不接触任何物体的情况下做出手势,根据自己的要求发出一些信号,画面就会做出相应的变化,例如对图片、视频进行放大缩小、拖拽、旋转、播…
暂无图片
编程学习 ·

单调栈解决Next Greater Number一类题

单调栈是什么? 单调栈使得每次新元素入栈后,栈内元素都保持有序(单调递增或者单调递减)。 单调递增栈:栈中数据出栈的序列为单调递增序列。 单调递减栈:栈中数据出栈的序列为单调递减序列。 注意:这里所说的递增递减是出栈的顺序,不是栈中数据的顺序。 单调栈的应用 通…
暂无图片
编程学习 ·

springboot aop 切到service层,不生效

今天发现一个问题,使用aop切到service层方法上,idea会有切成功的标志,编译也不报错,但就是不生效。研究了下发现,是因为我切的方法被同一个service中的其他方法调用,这样的话就不生效了,暂不清楚原因,解决方法时切到调用它的方法上,这只是切点不生效的一种情况,希望能…
暂无图片
编程学习 ·

遗传算法求解TSP问题python实现

文章目录1 遗传算法总体设计2 算子设计2.1 选择算子2.2 交叉算子2.3 变异算子2.4 将种群的个体按照里程排序,并返回当前种群中的最优个体及其里程2.5 设置种群数,变异概率及其进化次数2.6 主函数3 实验结果分析4 附python源码(完整版) 1 遗传算法总体设计Step1: 初始化参数(…
暂无图片
编程学习 ·

云管理服务AWS Organizations正式在AWS中国区域上线

近期, AWS中国(宁夏)区域(由西云数据运营)和AWS中国(北京)区域(由光环新网运营)正式上线了云管理服务AWS Organizations。作为一种管理服务,AWS Organizations可集中控制和管理多个AWS账户,无论是初创公司还是大型企业均可以使用,而不需要额外付费。随着企业或机构…
暂无图片
编程学习 ·

约瑟夫生死小游戏

约瑟夫生者死者小游戏 30 个人在一条船上,超载,需要 15 人下船。 于是人们排成一队,排队的位置即为他们的编号。 报数,从 1 开始,数到 9 的人下船。 如此循环,直到船上仅剩 15 人为止,问都有哪些编号的人下船了呢? # 首先来一个超载的小船30人 people = list(range(1,3…
暂无图片
编程学习 ·

python 实现螺旋矩阵

创建一个大小为m * n的矩阵, 并以螺旋方式遍历它。 在遍历时,我们跟踪变量“ val”以填充下一个值, 我们将“ val”一个接一个地递增,并将其值放入矩阵中。 以下是简单实现: def spiral_matrix(m,n)::param x: colunm index:param y: row indexa = [[0 for _ in range(m)]…
暂无图片
编程学习 ·

quartus ii 使用modelsim altera进行仿真

第一种:先随便写一个程序,有输入,有时钟,有输出再点击processing-->start-->start test bench template writer然后就会在modlsim的文件中生成一个.vt的文件 然后打开这个文件接下来就是再initial和always里面添加信号保存,再点击首先看仿真软件是不是modelsin-altera,再…
暂无图片
编程学习 ·

C++Primer5th 第十六章 模板与泛型编程

第十六章 模板与泛型编程16.1 定义模板16.1.1 函数模板实例化函数模板 模板类型参数 非类型模板参数 inline和constexpr的函数模板 编写类型无关的代码 模板编译 模板大多数编译错误在实例化期间报告16.1.2 类模板定义类模板 实例化类模板 类模板的成员函数 类模板成员函数的实…
暂无图片
编程学习 ·

CSV文件转Excel后数字自动转换成科学计数法的解决方法

CSV文件用Excel打开后,长度超过11位的数字自动转换成科学计数法显示,末尾数字变成“0000”,如何解决这一问题?以“老劳模系统数据.CSV”为例,身份证码是科学计数法了第一步:新建excel,用 office excel 打开第二步:点击“数据”---“从文本/cvs”如果乱码,则选择编码第…
暂无图片
编程学习 ·

内网渗透 -- 获取内网浏览器历史记录等相关信息

“我喜欢你,做我女朋友可以吗?”电话的那头没有反应,男生沉不住气了,小心翼翼地问着,“你在干嘛呀?”“我在点头。”---- 网易云热评环境:小攻:Kali 2020,ip:192.168.1.133小受:win7 x86,ip:192.168.1.137一、生成木马及监听主机参考上篇文章:二、获取浏览器历史…
暂无图片
编程学习 ·

JetPack 之 Paging3.0 简单上手指南!

作者:Chsmy之前有一篇Paging2.x的使用和分析,Paging2.x运行起来的效果无限滑动还挺不错的,不过代码写起来有点麻烦,功能也不是太完善,比如下拉刷新的方法都没有提供,我们还得自己去调用DataSource#invalidate()方法重置数据来实现。最近google出了3.0的测试版,功能更加强…
暂无图片
编程学习 ·

u-boot2020.04移植(1、u-boot的编译)

最近公司项目用到了xilinx的zynq7000 soc,开发方式有所不同,驱动的数据都是通过设备树来提供,以前没接触过,所以想系统的学习一下相关的内容,但是手头只有一块三星的s5pv210开发板,使用的u-boot和linux系统不支持设备树,只好自己移植,以此记录一下移植过程,加深理解与…
暂无图片
编程学习 ·

mxnet安装环境配置

一、安装Miniconda 官方网址:https://conda.io/en/latest/miniconda.html 本人选择python3.7版本Windows64位 安装完成后打开Anaconda Prompt创建虚拟环境conda create –n env python=3.7 这里的env为自定义环境名激活环境 conda activate env 退出环境: conda deactivate查…