• 天池Docker学习赛笔记


    容器的基本概念

    什么是容器?

    容器就是一个视图隔离、资源可限制、独立文件系统的进程集合。所谓“视图隔离”就是能够看到部分进程以及具有独立的主机名等;控制资源使用率则是可以对于内存大小以及 CPU 使用个数等进行限制。容器就是一个进程集合,它将系统的其他资源隔离开来,具有自己独立的资源视图。

    容器具有一个独立的文件系统,因为使用的是系统的资源,所以在独立的文件系统内不需要具备内核相关的代码或者工具,我们只需要提供容器所需的二进制文件、配置文件以及依赖即可。只要容器运行时所需的文件集合都能够具备,那么这个容器就能够运行起来。

    如何为进程提供一个独立的运行环境呢?

    • 针对不同进程使用同一个文件系统所造成的问题而言,Linux 和 Unix 操作系统可以通过 chroot 系统调用将子目录变成根目录,达到视图级别的隔离;进程在 chroot 的帮助下可以具有独立的文件系统,对于这样的文件系统进行增删改查不会影响到其他进程;
    • 因为进程之间相互可见并且可以相互通信,使用 Namespace 技术来实现进程在资源的视图上进行隔离。在 chroot 和 Namespace 的帮助下,进程就能够运行在一个独立的环境下了;
    • 但在独立的环境下,进程所使用的还是同一个操作系统的资源,一些进程可能会侵蚀掉整个系统的资源。为了减少进程彼此之间的影响,可以通过 Cgroup 来限制其资源使用率,设置其能够使用的 CPU 以及内存量。

    什么是镜像?

    我们将容器运行时所需要的所有的文件集合称之为容器镜像。

    docker 把应用程序及其依赖,打包在 image 文件里面。只有通过这个文件,才能生成 Docker 容器。image 文件可以看作是容器的模板。Docker 根据 image 文件生成容器的实例。同一个 image 文件,可以生成多个同时运行的容器实例。

    image 是二进制文件。实际开发中,一个 image 文件往往通过继承另一个 image 文件,加上一些个性化设置而生成。举例来说,你可以在 Ubuntu 的 image 基础上,往里面加入 Apache 服务器,形成你的 image。

    那么,一般都是通过什么样的方式来构建镜像的呢?通常情况下,我们会采用 Dockerfile 来构建镜像,这是因为 Dockerfile 提供了非常便利的语法糖,能够帮助我们很好地描述构建的每个步骤。当然,每个构建步骤都会对已有的文件系统进行操作,这样就会带来文件系统内容的变化,我们将这些变化称之为 changeset。当我们把构建步骤所产生的变化依次作用到一个空文件夹上,就能够得到一个完整的镜像。

    changeset 的分层以及复用特点能够带来几点优势

    第一,能够提高分发效率,简单试想一下,对于大的镜像而言,如果将其拆分成各个小块就能够提高镜像的分发效率,这是因为镜像拆分之后就可以并行下载这些数据;

    第二,因为这些数据是相互共享的,也就意味着当本地存储上包含了一些数据的时候,只需要下载本地没有的数据即可,举个简单的例子就是 golang 镜像是基于 alpine 镜像进行构建的,当本地已经具有了 alpine 镜像之后,在下载 golang 镜像的时候只需要下载本地 alpine 镜像中没有的部分即可;

    第三,因为镜像数据是共享的,因此可以节约大量的磁盘空间,简单设想一下,当本地存储具有了 alpine 镜像和 golang 镜像,在没有复用的能力之前,alpine 镜像具有 5M 大小,golang 镜像有 300M 大小,因此就会占用 305M 空间;而当具有了复用能力之后,只需要 300M 空间即可。

    容器的使用

    本部分主要围绕学习赛所用到的命令进行讲解从而学习docker的使用流程

    构建Docker镜像

    在项目的根目录下,新建一个文本文件Dockerfile,其中包含一组指令来告诉 Docker 如何构建我们的镜像。Docker会根据该文件生成二进制的 image 文件。

    # 指定基础镜像
    FROM registry.cn-shanghai.aliyuncs.com/tcc-public/java:jdk_13.0.2
    # 把当前文件夹里的文件拷贝到镜像的根目录下
    ADD . /
    # 指定接下来的工作路径为/(根目录)
    WORKDIR /
    # 指定镜像启动后要执行的命令
    CMD ["sh","run.sh"]
    

    构建启动后要执行的run.sh

    run.sh内容

    java -jar XX.jar
    

    编写业务部分

    这部分就是我们真正的项目内容,按照赛题来说,其实业务代码逻辑很简单,生成json就可以了,镜像下面也已经给好了csv文件,json这块我用了fastjson,打jar包的时候把依赖一起打进去就可以啦。

    import com.alibaba.fastjson.JSONArray;
    import com.alibaba.fastjson.JSONObject;
    
    import java.io.*;
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.List;
    
    public class TC {
        public static void main(String[] args) {
            List<Integer> res = new ArrayList<>();
            int sum = 0;
            try (BufferedReader file = new BufferedReader(new InputStreamReader(new FileInputStream("/tcdata/num_list.csv"), "UTF-8"))) {
                String record;
                while ((record = file.readLine()) != null) {
                    res.add(Integer.parseInt(record));
                    sum += Integer.parseInt(record);
                }
            } catch (Exception e) {
    
            }
            Collections.sort(res,Collections.reverseOrder());
            JSONObject jsonObject = new JSONObject();
            JSONArray jsonArray = new JSONArray();
            for(int i = 0;i < 10;i++){
                if(i >= res.size())
                    break;
                jsonArray.add(res.get(i));
            }
            jsonObject.put("Q1","Hello world");
            jsonObject.put("Q2",sum);
            jsonObject.put("Q3",jsonArray);
    
            String sourceString = jsonObject.toJSONString();	//待写入字符串
            byte[] sourceByte = sourceString.getBytes();
            if(null != sourceByte) {
                try {
                    File file = new File("result.json");        //文件路径(路径+文件名)
                    if (!file.exists()) {    //文件不存在则创建文件,先创建目录
                        file.createNewFile();
                    }
                    FileOutputStream outStream = new FileOutputStream(file);    //文件输出流用于将数据写入文件
                    outStream.write(sourceByte);
                    outStream.close();    //关闭文件输出流
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }
    

    本地测试

    镜像抓取

    镜像抓取命令

    docker image pull library/hello-world
    

    上面代码中,docker image pull是抓取 image 文件的命令。library/hello-world是 image 文件在仓库里面的位置,其中library是 image 文件所在的组,hello-world是 image 文件的名字。

    Base Docker Image List中给出了主办方为我们提供的镜像列表,所以我们通过以下指令获取java镜像

    docker pull registry.cn-shanghai.aliyuncs.com/tcc-public/java:jdk_13.0.2
    

    本地构建

    docker build -t registry.cn-shanghai.aliyuncs.com/wx_namespace/wx_namespace_learning:2 .
    

    本地运行测试

    这块我还有一点疑问,就是我docker run container后进入到容器中查看并没有发现/tcdata/num_list.csv文件,可是提交却能够通过,这块还不知道是为什么?

    回答:跟群友交流得知,这个文件并不在提供给我们的镜像中,而是在评测环境中。

    docker run -v /data:/tcdata your_image sh run.sh
    

    可以通过 -v 把运行环境的某个路径映射成镜像里的/tcdata。比如这里我把我主机的/data目录映射成 /tcdata目录。
    有点类似windows上面的映射网络驱动器。

    sudo docker run –it registry.cn-shanghai.aliyuncs.com/wx_namespace/wx_namespace_learning:2 /bin/bash
    

    -it参数:容器的 Shell 映射到当前的 Shell,然后你在本机窗口输入的命令,就会传入容器。

    /bin/bash:容器启动以后,内部第一个执行的命令。这里是启动 Bash,保证用户可以使用 Shell。

    我们可以通过run指令查看运行结果,进行本地测试。

    构建镜像并推送

    命令行推送

    推送的话说起来其实很简单

    docker push registry.cn-shenzhen.aliyuncs.com/test_for_tianchi/test_for_tianchi_submit:1.0
    

    但是由于比赛推荐我们使用私有仓库,所以要先登录下阿里云账号

    sudo docker login --username=XXX registry.cn-shanghai.aliyuncs.com
    

    IDE + Cloud Toolkit方法

    IDE + Cloud Toolkit
    这种方式推送到镜像仓库比较便捷,具体可以见链接

    提交运行

    推送到镜像仓库后,在竞赛界面提交运行即可。

  • 相关阅读:
    JNday7-pm
    JNday7-am
    bzoj1047理想的正方形
    关于OI中简单的常数优化
    bzoj1050旅行
    bzoj1044木棍分割
    bzoj1875 HH去散步
    bzoj1059矩阵游戏
    bzoj2705Longge的问题
    bzoj1833数字计数
  • 原文地址:https://www.cnblogs.com/wunsiang/p/12785823.html
Copyright © 2020-2023  润新知