java代码生成docker镜像

问题描述

这几天,由于项目业务的增加,需要在scala写的项目中执行已经写好的python代码,但具体怎么执行,最后其他同事验证了可以将python代码放到docker容器中去跑;所以我需要再web端预生成python的docker镜像为后续执行python脚本做准备(web端使用java代码写的), 不过这里要提前先说一声,java代码中生成docker镜像,花费的时间实在太久,我自己测试时弄了一个最简单的python 3.7.0的镜像花费了15分钟才打好,估计这需求后续会去掉; 这文章就看看图一乐就行了;如果有哪位不幸也遇到了这样恶心的需求,希望看了我这篇文章,直接就拒绝他,原因就是时间太久,也省的浪费时间;

解决问题

先说一下我对docker的认知,免的有人说的这篇文章写的狗屁不是,我上一次玩docker还是两年前,对docker的认知就是"镜像",“容器”,“仓库”;所以我可以这么说,我基本不会docker,但奈何赶鸭子上架,只能干;
基于这样的情况下,最简单的就是百度,看了不少博客,说docker官网没提供java的Api,但gitHub上有人封装好了,可以直接用,所以这里就用的gitHub封装好的api玩的,这里也放个参考的最靠谱的一个博主链接
但毕竟项目结构不同,所以我在把此博主的代码copy下来后自己做了适合自己项目的封装,具体代码如下:

<dependency>
	<groupId>com.github.docker-java</groupId>
	<artifactId>docker-java</artifactId>
	<version>3.2.8</version>
</dependency>
<dependency>
	<groupId>com.github.docker-java</groupId>
	<artifactId>docker-java-transport-httpclient5</artifactId>
	<version>3.2.8</version>
</dependency>

<!-- https://mvnrepository.com/artifact/com.google.guava/guava -->
<dependency>
	<groupId>com.google.guava</groupId>
	<artifactId>guava</artifactId>
	<version>30.1.1-jre</version>
</dependency>

相比我放上面的博主的链接,我这里多导了个guava依赖,是因为代码中报了如下的错:

Exception in thread "main" java.lang.NoClassDefFoundError: com/google/common/collect/Multimap

看了这个博主的文章,虽然我俩问题产生的原因不一样,但报错是相同的,所以导了这个包,最后导报错解决;链接

工具类DockerUtils

import com.alibaba.fastjson.JSON;
import com.github.dockerjava.api.DockerClient;
import com.github.dockerjava.api.async.ResultCallback;
import com.github.dockerjava.api.command.*;
import com.github.dockerjava.api.model.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class DockerUtils {
    private final static Logger logger = LoggerFactory.getLogger(DockerUtils.class);

    /*// docker服务端IP地址
    @Value("DOCKER_HOST")
    public static final String DOCKER_HOST="tcp://127.0.0.1:2375";
    // docker安全证书配置路径
    public static final String DCOEKR_CERT_PATH="";
    // docker是否需要TLS认证
    public static final Boolean DOCKER_TLS_VERIFY=false;
    // Harbor仓库的IP
//    public static final String REGISTRY_URL="192.168.79.131:8443";
    public static final String REGISTRY_URL="";
    // Harbor仓库的名称
//    public static final String REGISTRY_PROJECT_NAME="test";
    public static final String REGISTRY_PROJECT_NAME="";
    // Harbor仓库的登录用户名
//    public static final String REGISTRY_USER_NAME="admin";
    public static final String REGISTRY_USER_NAME="";
    // Harbor仓库的登录密码
//    public static final String REGISTRY_PASSWORD="Harbor12345";
    public static final String REGISTRY_PASSWORD="";
    // docker远程仓库的类型,此处默认是harbor
    public static final String REGISTRY_TYPE="harbor";

    public static final String REGISTRY_PROTOCAL="https://";


    *//**
     * 构建DocekrClient实例
     * @param dockerHost
     * @param tlsVerify
     * @param dockerCertPath
     * @param registryUsername
     * @param registryPassword
     * @param registryUrl
     * @return
     *//*
    public static DockerClient getDocekrClient(String dockerHost,boolean tlsVerify,String dockerCertPath,
                                               String registryUsername, String registryPassword,String registryUrl){
        DefaultDockerClientConfig dockerClientConfig = null;
        if(tlsVerify){
            dockerClientConfig = DefaultDockerClientConfig.createDefaultConfigBuilder()
                    .withDockerHost(DOCKER_HOST)
                    .withDockerTlsVerify(true)
                    .withDockerCertPath(DCOEKR_CERT_PATH)
                    .withRegistryUsername(REGISTRY_USER_NAME)
                    .withRegistryPassword(REGISTRY_PASSWORD)
                    .withRegistryUrl(registryUrl)
                    .build();
        }else {
            dockerClientConfig = DefaultDockerClientConfig.createDefaultConfigBuilder()
                    .withDockerHost(DOCKER_HOST)
                    .withDockerTlsVerify(false)
                    .withDockerCertPath(DCOEKR_CERT_PATH)
                    .withRegistryUsername(REGISTRY_USER_NAME)
                    .withRegistryPassword(REGISTRY_PASSWORD)
                    .withRegistryUrl(registryUrl)
                    .build();
        }
        DockerHttpClient httpClient = new ApacheDockerHttpClient.Builder()
                .dockerHost(dockerClientConfig.getDockerHost())
                .sslConfig(dockerClientConfig.getSSLConfig())
                .build();

        return DockerClientImpl.getInstance(dockerClientConfig,httpClient);
    }

    public static DockerClient getDockerClient(){
        return getDocekrClient(DOCKER_HOST,DOCKER_TLS_VERIFY,DCOEKR_CERT_PATH,REGISTRY_USER_NAME,REGISTRY_PASSWORD,REGISTRY_URL);
    }*/

    /**
     * 获取docker基础信息
     * @param dockerClient
     * @return
     */
    public static String getDockerInfo(DockerClient dockerClient){
        Info info = dockerClient.infoCmd().exec();
        return JSON.toJSONString(info);
    }

    /**
     * 给镜像打标签
     * @param dockerClient
     * @param imageIdOrFullName
     * @param respository
     * @param tag
     */
    public static void tagImage(DockerClient dockerClient, String imageIdOrFullName, String respository,String tag){
        TagImageCmd tagImageCmd = dockerClient.tagImageCmd(imageIdOrFullName, respository, tag);
        tagImageCmd.exec();
    }

    /**
     * load镜像
     * @param dockerClient
     * @param inputStream
     */
    public static void loadImage(DockerClient dockerClient, InputStream inputStream){
        LoadImageCmd loadImageCmd = dockerClient.loadImageCmd(inputStream);
        loadImageCmd.exec();
    }

    /**
     * pull镜像
     * @param dockerClient
     * @param repository
     */
    public static PullImageCmd pullImage(DockerClient dockerClient,String repository){
        PullImageCmd pullImageCmd = dockerClient.pullImageCmd(repository);
        pullImageCmd.exec(new ResultCallback<PullResponseItem>() {
            @Override
            public void onStart(Closeable closeable) {
                System.out.println("开始拉取");
            }

            @Override
            public void onNext(PullResponseItem object) {
                System.out.println("拉取下一个");
            }

            @Override
            public void onError(Throwable throwable) {
                System.out.println("拉取发生错误:"+throwable.getMessage());
            }

            @Override
            public void onComplete() {
                System.out.println("拉取成功");
            }

            @Override
            public void close() throws IOException {
                System.out.println("拉取结束");
            }
        });
        return pullImageCmd;
    }

    /**
     * 推送镜像
     * @param dockerClient
     * @param imageName
     * @return
     * @throws InterruptedException
     */
    public static Boolean pushImage(DockerClient dockerClient,String imageName) throws InterruptedException {
        final Boolean[] result = {true};
        ResultCallback.Adapter<PushResponseItem> callBack = new ResultCallback.Adapter<PushResponseItem>() {
            @Override
            public void onNext(PushResponseItem pushResponseItem) {
                if (pushResponseItem != null){
                    ResponseItem.ErrorDetail errorDetail = pushResponseItem.getErrorDetail();
                    if (errorDetail!= null){
                        result[0] = false;
                        logger.error(errorDetail.getMessage(),errorDetail);
                    }
                }
                super.onNext(pushResponseItem);
            }
        };
        dockerClient.pushImageCmd(imageName).exec(callBack).awaitCompletion();
        return result[0];
    }

    /**
     * 从镜像的tar文件中获取镜像名称
     * @param imagePath
     * @return
     */
    public static String getImageName(String imagePath){
        try {
            return UnCompress.getImageName(imagePath);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 通过dockerFile构建镜像,这里为了给打成的镜像有名字和tag,多传了几个参数
     * @param dockerClient
     * @param dockerFile
     * @return
     */
    public static String buildImage(DockerClient dockerClient, File dockerFile,String imageName,String tags,long currentTimeMillis){
        Set<String> tagsSet = new HashSet<>();
        //拼成 name-时间戳:tag 格式
        tagsSet.add(imageName+"-"+System.currentTimeMillis()+":"+tags);
        BuildImageCmd buildImageCmd = dockerClient.buildImageCmd(dockerFile)
                .withTags(tagsSet);
        BuildImageResultCallback buildImageResultCallback = new BuildImageResultCallback() {
            @Override
            public void onNext(BuildResponseItem item) {
                logger.info("{}", item);
                super.onNext(item);
            }
        };

        return buildImageCmd.exec(buildImageResultCallback).awaitImageId();
//        logger.info(imageId);
    }

    /**
     * 获取镜像列表
     * @param dockerClient
     * @return
     */
    public static List<Image> imageList(DockerClient dockerClient){
        List<Image> imageList = dockerClient.listImagesCmd().withShowAll(true).exec();
        return imageList;
    }
}
UnCompress

没太仔细研究干啥用的类,反正工具类DockerUtils 调用此类的方法,我又没用到,嘿嘿

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;

import java.io.*;
import java.util.List;

/**
 * 解压缩
 * @author Administrator
 *
 */
public class UnCompress {
    private static final String BASE_DIR = "";

    // 符号"/"用来作为目录标识判断符
    private static final String PATH = File.separator;
    private static final int BUFFER = 1024;
    private static final String EXT = ".tar";
    /**
     * 归档
     *
     * @param srcPath
     * @param destPath
     * @throws Exception
     */
    public static void archive(String srcPath, String destPath)
            throws Exception {

        File srcFile = new File(srcPath);

        archive(srcFile, destPath);

    }

    /**
     * 归档
     *
     * @param srcFile
     *            源路径
     * @param destFile
     *            目标路径
     * @throws Exception
     */
    public static void archive(File srcFile, File destFile) throws Exception {

        TarArchiveOutputStream taos = new TarArchiveOutputStream(
                new FileOutputStream(destFile));

        archive(srcFile, taos, BASE_DIR);

        taos.flush();
        taos.close();
    }

    /**
     * 归档
     *
     * @param srcFile
     * @throws Exception
     */
    public static void archive(File srcFile) throws Exception {
        String name = srcFile.getName();
        String basePath = srcFile.getParent();
        String destPath = basePath+File.separator + name + EXT;
        archive(srcFile, destPath);
    }

    /**
     * 归档文件
     *
     * @param srcFile
     * @param destPath
     * @throws Exception
     */
    public static void archive(File srcFile, String destPath) throws Exception {
        archive(srcFile, new File(destPath));
    }

    /**
     * 归档
     *
     * @param srcPath
     * @throws Exception
     */
    public static void archive(String srcPath) throws Exception {
        File srcFile = new File(srcPath);

        archive(srcFile);
    }

    /**
     * 归档
     *
     * @param srcFile
     *            源路径
     * @param taos
     *            TarArchiveOutputStream
     * @param basePath
     *            归档包内相对路径
     * @throws Exception
     */
    private static void archive(File srcFile, TarArchiveOutputStream taos,
                                String basePath) throws Exception {
        if (srcFile.isDirectory()) {
            archiveDir(srcFile, taos, basePath);
        } else {
            archiveFile(srcFile, taos, basePath);
        }
    }

    /**
     * 目录归档
     *
     * @param dir
     * @param taos
     *            TarArchiveOutputStream
     * @param basePath
     * @throws Exception
     */
    private static void archiveDir(File dir, TarArchiveOutputStream taos,
                                   String basePath) throws Exception {

        File[] files = dir.listFiles();

        if (files.length < 1) {
            TarArchiveEntry entry = new TarArchiveEntry(basePath
                    + dir.getName() + PATH);

            taos.putArchiveEntry(entry);
            taos.closeArchiveEntry();
        }

        for (File file : files) {

            // 递归归档
            archive(file, taos, basePath + dir.getName() + PATH);

        }
    }

    /**
     * 归档内文件名定义
     *
     * <pre>
     * 如果有多级目录,那么这里就需要给出包含目录的文件名
     * 如果用WinRAR打开归档包,中文名将显示为乱码
     * </pre>
     */
    private static void archiveFile(File file, TarArchiveOutputStream taos, String dir) throws Exception {
        TarArchiveEntry entry = new TarArchiveEntry(dir + file.getName());
        //如果打包不需要文件夹,就用 new TarArchiveEntry(file.getName())
        entry.setSize(file.length());
        taos.putArchiveEntry(entry);
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(
                file));
        int count;
        byte data[] = new byte[BUFFER];
        while ((count = bis.read(data, 0, BUFFER)) != -1) {
            taos.write(data, 0, count);
        }

        bis.close();
        taos.closeArchiveEntry();
    }

    /**
     * 解归档
     *
     * @param srcFile
     * @throws Exception
     */
    public static void dearchive(File srcFile) throws Exception {
        String basePath = srcFile.getParent();
        dearchive(srcFile, basePath);
    }

    /**
     * 解归档
     *
     * @param srcFile
     * @param destFile
     * @throws Exception
     */
    public static void dearchive(File srcFile, File destFile) throws Exception {
        TarArchiveInputStream tais = new TarArchiveInputStream(
                new FileInputStream(srcFile));
        dearchive(destFile, tais);
        tais.close();

    }

    /**
     * 解归档
     *
     * @param srcFile
     * @param destPath
     * @throws Exception
     */
    public static void dearchive(File srcFile, String destPath) throws Exception {
        dearchive(srcFile, new File(destPath));
    }

    /**
     * 文件 解归档
     *
     * @param destFile
     *            目标文件
     * @param tais
     *            ZipInputStream
     * @throws Exception
     */
    private static void dearchive(File destFile, TarArchiveInputStream tais) throws Exception {
        TarArchiveEntry entry = null;
        while ((entry = tais.getNextTarEntry()) != null) {
            // 文件
            String dir = destFile.getPath() + File.separator + entry.getName();
            File dirFile = new File(dir);
            // 文件检查
            fileProber(dirFile);
            if (entry.isDirectory()) {
                dirFile.mkdirs();
            } else {
                dearchiveFile(dirFile, tais);
            }
        }
    }

    /**
     * 文件 解归档
     *
     * @param srcPath
     *            源文件路径
     *
     * @throws Exception
     */
    public static void dearchive(String srcPath) throws Exception {
        File srcFile = new File(srcPath);
        dearchive(srcFile);
    }

    /**
     * 文件 解归档
     *
     * @param srcPath
     *            源文件路径
     * @param destPath
     *            目标文件路径
     * @throws Exception
     */
    public static void dearchive(String srcPath, String destPath) throws Exception {
        File srcFile = new File(srcPath);
        dearchive(srcFile, destPath);
    }

    /**
     * 文件解归档
     *
     * @param destFile
     *            目标文件
     * @param tais
     *            TarArchiveInputStream
     * @throws Exception
     */
    private static void dearchiveFile(File destFile, TarArchiveInputStream tais)
            throws Exception {

        BufferedOutputStream bos = new BufferedOutputStream(
                new FileOutputStream(destFile));

        int count;
        byte data[] = new byte[BUFFER];
        while ((count = tais.read(data, 0, BUFFER)) != -1) {
            bos.write(data, 0, count);
        }

        bos.close();
    }

    /**
     * 文件探针
     *
     * <pre>
     * 当父目录不存在时,创建目录!
     * </pre>
     *
     * @param dirFile
     */
    private static void fileProber(File dirFile) {

        File parentFile = dirFile.getParentFile();
        if (!parentFile.exists()) {

            // 递归寻找上级目录
            fileProber(parentFile);

            parentFile.mkdir();
        }

    }

    public static String getImageName(String tempFilePath) throws Exception{
//        System.out.println("tempFilePath = " + tempFilePath);
        String dirPath = tempFilePath.substring(0, tempFilePath.lastIndexOf("."));
        File dirFile = new File(dirPath);
        if (!dirFile.exists()) {
            dirFile.mkdirs();
        }
        UnCompress.dearchive(tempFilePath, dirPath);
        if (!dirPath.endsWith(File.separator)) {
            dirPath += File.separator;
        }
        //目标文件
        String destFilePath = dirPath + "manifest.json";
        File destFile = new File(destFilePath);
        if (!destFile.exists()) {
            return null;
        }
        StringBuilder sb = new StringBuilder("");
        InputStream io = new FileInputStream(new File(destFilePath));
        byte[] bytes = new byte[1024];
        int len = 0;
        while ((len = io.read(bytes)) > 0) {
            sb.append(new String(bytes, 0, len));
        }
        String content = sb.toString();
        //只取第一个配置项
        List<JSONObject> jsonList = (List<JSONObject>) JSONArray.parse(content);
        System.out.println("jsonList = " + jsonList);
        return ((List<String>)jsonList.get(0).get("RepoTags")).get(0);
        /*if (content.startsWith("[")) {
            content = content.substring(1, content.length() - 2);
        }
        System.out.println("content = " + content);
        JSONObject json = JSONObject.parseObject(content.toString());
        List<String> list = (List<String>) json.get("RepoTags");
        System.out.println("list = " + list);
        return list.get(0);*/
    }

}

DockerClient

import com.github.dockerjava.core.DefaultDockerClientConfig;
import com.github.dockerjava.core.DockerClientImpl;
import com.github.dockerjava.httpclient5.ApacheDockerHttpClient;
import com.github.dockerjava.transport.DockerHttpClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
public class DockerClient {

    // docker服务端IP地址
    public static String DOCKER_HOST;

    @Value("${DOCKER_HOST}")
    public void setDockerHost(String dockerHost) {
        this.DOCKER_HOST = dockerHost;
    }

    public static String getDockerHost() {
        return DOCKER_HOST;
    }

    // docker安全证书配置路径
    public static String DCOEKR_CERT_PATH;

    @Value("${DCOEKR_CERT_PATH}")
    public void setDcoekrCertPath(String dcoekrCertPath) {
        this.DCOEKR_CERT_PATH = dcoekrCertPath;
    }

    public static String getDcoekrCertPath() {
        return DCOEKR_CERT_PATH;
    }

    // docker是否需要TLS认证
    public static Boolean DOCKER_TLS_VERIFY;

    @Value("${DOCKER_TLS_VERIFY}")
    public void setDockerTlsVerify(String dockerTlsVerify) {
        //因为配置文件中,虽然写的是true/false;但传进来就是String值,所以这里做个转换
        this.DOCKER_TLS_VERIFY = Boolean.valueOf(dockerTlsVerify);
    }

    public static Boolean getDockerTlsVerify() {
        return DOCKER_TLS_VERIFY;
    }

    // Harbor仓库的IP
    public static String REGISTRY_URL;

    @Value("${REGISTRY_URL}")
    public void setRegistryUrl(String registryUrl) {
        this.REGISTRY_URL = registryUrl;
    }

    public static String getRegistryUrl() {
        return REGISTRY_URL;
    }

    // Harbor仓库的名称
    public static String REGISTRY_PROJECT_NAME;

    @Value("${REGISTRY_PROJECT_NAME}")
    public void setRegistryProjectName(String registryProjectName) {
        this.REGISTRY_PROJECT_NAME = registryProjectName;
    }

    public static String getRegistryProjectName() {
        return REGISTRY_PROJECT_NAME;
    }

    // Harbor仓库的登录用户名
    public static String REGISTRY_USER_NAME;
    @Value("${REGISTRY_USER_NAME}")
    public void setRegistryUserName(String registryUserName) {
        this.REGISTRY_USER_NAME = registryUserName;
    }

    public static String getRegistryUserName() {
        return REGISTRY_USER_NAME;
    }
    // Harbor仓库的登录密码
    public static String REGISTRY_PASSWORD;
    @Value("${REGISTRY_PASSWORD}")
    public void setRegistryPassword(String registryPassword) {
        this.REGISTRY_PASSWORD = registryPassword;
    }

    public static String getRegistryPassword() {
        return REGISTRY_PASSWORD;
    }

    // docker远程仓库的类型,此处默认是harbor
    public static String REGISTRY_TYPE;

    @Value("${REGISTRY_TYPE}")
    public void setRegistryType(String registryType) {
        this.REGISTRY_TYPE = registryType;
    }

    public static String getRegistryType() {
        return REGISTRY_TYPE;
    }

    public static String REGISTRY_PROTOCAL;

    @Value("${REGISTRY_PROTOCAL}")
    public void setRegistryProtocal(String registryProtocal) {
        this.REGISTRY_PROTOCAL = registryProtocal;
    }

    public static String getRegistryProtocal() {
        return REGISTRY_PROTOCAL;
    }


    /**
     * 构建DocekrClient实例
     *
     * @param dockerHost
     * @param tlsVerify
     * @param dockerCertPath
     * @param registryUsername
     * @param registryPassword
     * @param registryUrl
     * @return
     */
    public static com.github.dockerjava.api.DockerClient getDockerClient(String dockerHost, boolean tlsVerify, String dockerCertPath,
                                                                         String registryUsername, String registryPassword, String registryUrl) {
        DefaultDockerClientConfig dockerClientConfig = DefaultDockerClientConfig.createDefaultConfigBuilder()
                    .withDockerHost(dockerHost)
                    .withDockerTlsVerify(tlsVerify)
                    .withDockerCertPath(dockerCertPath)
                    .withRegistryUsername(registryUsername)
                    .withRegistryPassword(registryPassword)
                    .withRegistryUrl(registryUrl)
                    .build();
        DockerHttpClient httpClient = new ApacheDockerHttpClient.Builder()
                .dockerHost(dockerClientConfig.getDockerHost())
                .sslConfig(dockerClientConfig.getSSLConfig())
                .build();

        return DockerClientImpl.getInstance(dockerClientConfig, httpClient);
    }

    public static com.github.dockerjava.api.DockerClient getDockerClient() {
        return getDockerClient(getDockerHost(), getDockerTlsVerify(), getDcoekrCertPath(), getRegistryUserName(), getRegistryPassword(), getRegistryUrl());
    }
}

docker.properties配置文件
#docker服务端IP地址
DOCKER_HOST=tcp://127.0.0.1:2375
#docker安全证书配置路径
DCOEKR_CERT_PATH=
#docker是否需要TLS认证
DOCKER_TLS_VERIFY=false
#Harbor仓库的IP
REGISTRY_URL=
#Harbor仓库的名称
REGISTRY_PROJECT_NAME=
#Harbor仓库的登录用户名
REGISTRY_USER_NAME=
#Harbor仓库的登录密码
REGISTRY_PASSWORD=
#docker远程仓库的类型,此处默认是harbor
REGISTRY_TYPE=harbor
REGISTRY_PROTOCAL=https://

我这里只调用了buildImage和pullImage两个方法,其实打镜像只调用buildImage就够了,之所以多加个pullImage方法,是在buildImage前调用一下pullImage先进行个镜像拉取,会省一些时间,但别高兴,打个最基础的python镜像并拉取几个执行时需要的依赖,最后我测试的时间,依旧是10分钟+;如果有人测试其他方法报错了,请自行解决,嘿嘿

这里要额外说一嘴buildImage方法,这个方法调用时可以加withTags,但这个方法有点坑,本着见文知意,这个方法我以为是给镜像打标签,但最后经过测试,我想错了,直接放上几个我之前测试的截图,大家就知道什么情况了;

  1. 如果withTags中的Set集合,放多个值,那么就会打多个镜像,name分别为Set集合中的值,tag均为latest
    在这里插入图片描述
  2. 如果如果withTags中的Set集合,写成 a:b形式,创建的镜像,名字就是a,tag就是b(至于规则的值在Set集合中又有多个,会产生什么结果,我没测试,感兴趣的可以去试试)
    在这里插入图片描述
  3. 如果不调用withTags方法,那创建的镜像,name和tag均为 none;见上面两种图的第一条docker镜像数据

业务场景

另外说一下我的代码逻辑吧;首先是代码中生成dockerFile文件,然后通过上面的Api,使用dockerFile文件生成镜像(文件作为buildImage方法的第二个参数),再创建docker-compose.yml文件,到这里web端逻辑就结束了;server需要通过docker-compose.yml去使用刚生成的docker镜像启动容器,具体代码也懒的粘了,
多提一嘴,buildImage() 第二个参数如果是文件夹路径,后面会自动拼接Dockerfile文件

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值