关于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();

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

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值