• 认识docker以及常用操作


    一、docker是什么

    Docker 是一个开源项目,诞生于 2013 年初,最初是 dotCloud 公司内部的一个业余项目。它基于 Google 公司推出的 Go 语言实现。 项目后来加入了 Linux 基金会,遵从了 Apache 2.0 协议,项目代码在GitHub 上进行维护。Docker 自开源后受到广泛的关注和讨论,以至于 dotCloud 公司后来都改名为 Docker Inc。Redhat 已经在其 RHEL6.5 中集中支持 Docker;Google 也在其 PaaS 产品中广泛应用。

    Docker 项目的目标是实现轻量级的操作系统虚拟化解决方案。 Docker 的基础是 Linux 容器(LXC)等技术。在 LXC 的基础上 Docker 进行了进一步的封装,让用户不需要去关心容器的管理,使得操作更为简便。用户操作 Docker 的容器就像操作一个快速轻量级的虚拟机一样简单。Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化。容器是完全使用沙箱机制,相互之间不会有任何接口(类似 iPhone 的 app),更重要的是容器性能开销极低。

    Docker采用 C/S架构 Docker daemon 作为服务端接受来自客户的请求,并处理这些请求(创建、运行、分发容器)。 客户端和服务端既可以运行在一个机器上,也可通过 socket 或者RESTful API 来进行通信。

    Docker daemon 一般在宿主主机后台运行,等待接收来自客户端的消息。 Docker 客户端则为用户提供一系列可执行命令,用户用这些命令实现跟 Docker daemon 交互。

    二、docker的优点

    在云时代,开发者创建的应用必须要能很方便地在网络上传播,也就是说应用必须脱离底层物理硬件的限制; 同时必须满足“任何时间任何地点”可获取可使用的特点。因此,开发者们需要一种新型的创建分布式应用程序的方式,快速分发部署,而这正是 Docker 所能够提供的最大优势。Docker 提供了一种更为聪明的方式,通过容器来打包应用、解耦应用和运行平台。这意味着迁移的时候,只需要在新的服务器上启动需要的容器就可以了, 无论新旧服务器是否是同一类别的平台。这无疑帮助我们节约了大量的宝贵时间,并降低部署过程出现问题的风险。

    对于开发和运维人员来说,最梦寐以求的效果可能就是一次创建和配置,之后可以在任意地方、任意时间让应用正常运行,而 Docker 恰恰可以实现这一中级目标。具体来说,在开发和运维过程中,Docker 具有以下几个方面的优势:

    更快的交付和部署:

    使用 Docker,开发人员可以使用镜像来快速构建一套标准的开发环境;开发完之后,测试和运维人员可以直接使用完全相同的环境来部署代码。只要是开发测试过的代码,就可以确保在生产环境无缝运行。Docker 可以快速创建和删除容器,实现快速迭代,节约开发、测试及部署的时间。

    更高效的利用资源:

    运行 Docker 容器不需要额外的虚拟化管理程序的支持,Docker 是内核级的虚拟化, 可以实现更高的性能,同时对资源的额外需求很低,与传统的虚拟机方式相比,Docker 的性能要提高 1~ 2 个数量级。

    更轻松的迁移和扩展:

    Docker 容器几乎可以在任意的平台上运行,包括物理机、虚拟机、公有云、私有云、个人电脑等等,同时支持主流的操作系统发行版本。这种兼容性能让用户可以在不同的平台之间轻松的迁移应用。

    更轻松的管理和更新:

    使用 Dockerfile,只需要小小的配置修改,就可以替代以往大量的更新工作。所有的修改都以增量的方式被分发和更新,从而实现自动化并且高效的容器管理。

    注意:docker虽然有在部署时有诸多的便利,但十分不推荐将数据直接保存在docker中。一种解决方案是使用docker运行数据库管理程序,但数据则另用服务器保存。

    三、Docker与虚拟化

    Docker 以及其他容器技术,都属于操作系统虚拟化范畴,操作系统细腻化最大的特点就是不需要额外的supervisor 支持。Docker 虚拟化方式之所以有众多优势,跟操作系统虚拟化技术自身的设计和实现分不开。传统方式是在硬件层面实现虚拟化,需要有额外的虚拟机管理应用和虚拟机操作系统层。Docker 容器时在操作系统层面实现虚拟化,直接复用本地主机的操作系统,因此更加轻量级。

    四、docker与虚拟机比较

    作为一种轻量级的虚拟化方式,Docker 在运行应用上跟传统的虚拟机的方式相比具有如下显著优势:

    • Docker 容器启动很快,启动和停止可以实现秒级,相比传统的虚拟机方式(分钟级)要快速很多。
    • Docker 容器对系统资源需求很少,一台主机上可以同时运行数千个 Docker 容器。
    • Docker 通过类似 git 设计理念的操作来方便用户获取、分发和更新应用镜像,存储复用,增量更新。
    • Docker 通过 Dockerfile 支持灵活的自动化创建和部署机制,可以提高工作效率,并标准化流程。
    *特性* *容器* *虚拟机*
    启动速度 秒级 分钟级
    性能 接近原生 较好
    内存 MB 级 GB 级
    硬盘适应 MB 级 GB 级
    运行密度 单台主机支持上千个 单台主机支持几个
    隔离性 安全隔离 完全隔离
    迁移 优秀 一般

    五、docker三大概念

    镜像(Image)

    创建容器的模板,是一个静态的概念。通过镜像可以启动多个容器。能够被导出成文件传输至不同主机。

    容器(Container)

    镜像是静态的定义,容器是镜像运行时的实体。容器可以被创建、启动、停止、删除、暂停等。

    仓库(Repository)

    用来保存镜像的仓库。当我们构建好自己的镜像之后,需要存放在仓库中,当我们需要启动一个镜像时,可以在仓库中下载下来。

    六、docker安装

    Docker 引擎是使用 Docker 容器的核心组件,可以在主流的操作系统和云平台上使用,包括 Linux 操作系统、MacOSS 和 Windows 系统上。用户可以访问 Docker 官网(https://www.docker.com/get-started)去获取 Docker 安装包。

    安装前环境准备

    Docker 目前支持 CentOS 7 及以后版本。系统的要求跟 Ubuntu 情况类似,64 位操作系统,内核至少是 3.10以上。

    查看内核:

    # 方式一:
    [root@localhost ~ ]# uname -a
    Linux localhost.localdomain 3.10.0-1127.19.1.el7.x86_64 #1 SMP Tue Aug 25 17:23:54 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
    
    # 方式二:
    [root@localhost ~ ]# cat /proc/version 
    Linux version 3.10.0-1127.19.1.el7.x86_64 (mockbuild@kbuilder.bsys.centos.org) (gcc version 4.8.5 20150623 (Red Hat 4.8.5-39) (GCC) ) #1 SMP Tue Aug 25 17:23:54 UTC 2020
    

    1、更新系统软件。

    如果内核等级较低,内核也要一起升级,这步操作应该在系统刚安装完执行,生产环境不要执行,切记切记!

    yum update -y --exclud=kernel*
    # --exclud 排除相关软件包。
    

    2、更换系统yum源。

    • 使用官方源。
    yum install -y yum-utils
    
    yum-config-manager --add-repo 
        https://download.docker.com/linux/centos/docker-ce.repo
    
    • 官方服务器在国外,如果比较慢,可以用阿里云镜像。
    # 备份原来 YUN 源
    cp /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.bak
    
    # 更换 YUM 源
    curl -o	/etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo
    

    安装docker-ce

    yum install -y docker-ce
    

    查看是否安装成功。

    [root@localhost ~ ]# docker run hello-world
    Unable to find image 'hello-world:latest' locally
    latest: Pulling from library/hello-world
    0e03bdcc26d7: Pull complete 
    Digest: sha256:e7c70bb24b462baa86c102610182e3efcb12a04854e8c582838d92970a09f323
    Status: Downloaded newer image for hello-world:latest
    
    Hello from Docker!
    This message shows that your installation appears to be working correctly.
    
    To generate this message, Docker took the following steps:
     1. The Docker client contacted the Docker daemon.
     2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
        (amd64)
     3. The Docker daemon created a new container from that image which runs the
        executable that produces the output you are currently reading.
     4. The Docker daemon streamed that output to the Docker client, which sent it
        to your terminal.
    
    To try something more ambitious, you can run an Ubuntu container with:
     $ docker run -it ubuntu bash
    
    Share images, automate workflows, and more with a free Docker ID:
     https://hub.docker.com/
    
    For more examples and ideas, visit:
     https://docs.docker.com/get-started/
    

    设置开机自启动。

    systemctl enable --now docker 
    

    查看docker信息。

    docker info
    

    安装前如果有旧版docker一定要先卸载再安装,否则会报错。

    # 卸载旧版
    yum remove docker 
                  docker-client 
                  docker-client-latest 
                  docker-common 
                  docker-latest 
                  docker-latest-logrotate 
                  docker-logrotate 
                  docker-engine
    

    获取帮助信息

    使用--help选项可获取帮助信息。

    # 获取docker帮助信息。
    docker --help
    
    # 获取某个子命令帮助信息。
    docker images --help
    

    七、镜像常用操作

    获取镜像

    镜像是运行容器的模板,官方 Docker Hub 仓库已经提供了许多镜像共开发者使用。如果我们需要获取某个镜像则可以去 docker 仓库下载所需的镜像。

    格式:

    docker pull [镜像仓库URL]/[命名空间名称]/[仓库名称]:[镜像版本号]
    

    实例:

    docker pull docker.io/library/busybox:latest
    默认URL : docker.io
    命名空间: library
    仓库名称:busybox
    版本号: latest
    
    # 不指定则为默认仓库URL
    docker pull busybox:latest
    
    # 可以直接使用docker run,如果本地没有镜像会自动去仓库拉取。
    [root@localhost ~ ]# docker run hello-world
    Unable to find image 'hello-world:latest' locally
    latest: Pulling from library/hello-world
    0e03bdcc26d7: Pull complete 
    Digest: sha256:e7c70bb24b462baa86c102610182e3efcb12a04854e8c582838d92970a09f323
    Status: Downloaded newer image for hello-world:latest
    。。。省略。。。
    

    下载的时候,我们可以看到有若干层组成,像 0e03bdcc26d7 这样的字符串是层的唯一 ID(实际上,完整的 ID 包括 256 比特, 64 个十六进制字符组成)。使用 docker pull 命令下载中会获取并输出镜像的各层信息。当不同的镜像包括相同的层的时候,本地仅存一份内容,减小存储空间。

    查看镜像基本信息

    使用 docker images 或 docker images ls 命令可以列举本地主机上已有镜像的基本信息。

    [root@localhost ~ ]# docker images
    REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
    nginx-2             v1                  dc1178ad9ae1        17 hours ago        131MB
    busybox             latest              dc3bacd8b5ea        8 days ago          1.23MB
    mysql               latest              dd7265748b5d        11 days ago         545MB
    hello-world         latest              bf756fb1ae65        11 months ago       13.3kB
    
    字段 含义
    REPOSITORY 仓库名称
    TAG 版本号(latest:表示最新的版本)
    IMAGE ID 镜像ID
    CREATED 时间段
    SIZE 镜像文件的体积

    其中镜像的 ID 信息十分重要,它唯一标识了镜像。在使用镜像 ID 的时候,一般可以使用该 ID 的前若干个字符组成的可区分串来替代完整的 ID。

    TAG 信息用于标记来自同一个仓库的不同镜像。TAG 在同一个仓库中是唯一的。

    镜像大小信息只是表示了该镜像的逻辑体积大小,实际上由于相同的镜像层本地只会存储一份,物理上占用 的存储空间会小于各镜像逻辑体积之和。

    常用选项:
    -a : 显示所有的镜像(包括临时镜像文件)。

    root@localhost ~ ]# docker images -a
    REPOSITORY                TAG             IMAGE ID            CREATED             SIZE
    <none>                    <none>          ab9924336485       10 hours ago        913MB
    <none>                    <none>          c0fe586306a8       28 hours ago        913MB
    centos7/nginx             v1              cc39a9a08eea       2 days ago          571MB
    

    -q : 只显示镜像ID。

    [root@localhost ~ ]# docker images -q
    ab9924336485
    c0fe586306a8
    cc39a9a08eea
    

    --digests=true|false:列出镜像的数字摘要值。

    [root@localhost ~ ]# docker images --digests=true
    REPOSITORY       TAG        DIGEST       IMAGE ID            CREATED             SIZE
    registry.cn-shanghai.aliyuncs.com/sepzhan/zmc   v1                  sha256:a078c591c52e2f6404fe8930ee2f0b308746d6c19cd2cbb3a94a1919e7168910   2400030cc4a7      10 hours ago        913MB
    

    查看镜像详细信息

    使用 docker inspect 命令获取镜像的详细信息,包括 PID、作者、架构等等。

    [root@localhost ~ ]# docker inspect nginx_2:v1
    [
        {
            "Id": "sha256:2c45f814f15baad983b0825585331ccab975d559c92a3e0d8618a351910264c8",
            "RepoTags": [
                "nginx_2:v1",
                "nginx_3:v1"
            ],
            "RepoDigests": [],
            "Parent": "",
            "Comment": "Imported from -",
            "Created": "2020-12-02T03:05:35.99589032Z",
            。。。省略。。。
    

    选项:
    -f : 可以使用golang的模板获取所需信息。

    # 查看镜像完整ID
    [root@localhost ~ ]# docker inspect -f '{{.Id}}' nginx
    sha256:bc9a0695f5712dcaaa09a5adc415a3936ccba13fc2587dfd76b1b8aeea3f221c
    

    为镜像添加tag

    为了方便后续工作中使用特定的镜像,还可以使用 docker tag 命令来为本地的镜像添加标签、修改镜像名称和版本号。

    格式:

    docker tag [原镜像仓库url]/[原镜像命名空间]/[原镜像仓库名称]:[版本号] 
    					[新镜像仓库url]/[新镜像命名空间]/[新镜像仓库名称]:[版本号]   
    

    实例:

    # 修改镜像名与TAG
    [root@localhost ~ ]# docker tag nginx_2:v1 nginx_3:v1
    [root@localhost ~ ]# docker images
    REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
    nginx_2             v1                  2c45f814f15b        58 seconds ago      131MB
    nginx_3             v1                  2c45f814f15b        58 seconds ago      131MB
    
    docker tag nginx:1.19.2   registry.cn-hangzhou.aliyuncs.com/alvinos/py15-nginx:1.19.2
    

    修改tag后,源镜像还会存在,ID则为同一个,在删除镜像时若指定的是ID则会删除所有匹配到ID的镜像,若指定的是镜像名称:版本则仅删除指定名称的单个镜像。

    查看镜像构建历史

    格式:

    docker  history [镜像名字:镜像版本号 | 镜像ID]
    

    实例:

    [root@localhost ~ ]# docker history nginx:latest
    IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
    bc9a0695f571        7 days ago          /bin/sh -c #(nop)  CMD ["nginx" "-g" "daemon…   0B                  
    <missing>           7 days ago          /bin/sh -c #(nop)  STOPSIGNAL SIGQUIT           0B                  
    <missing>           7 days ago          /bin/sh -c #(nop)  EXPOSE 80                    0B                  
    <missing>           7 days ago          /bin/sh -c #(nop)  ENTRYPOINT ["/docker-entr…   0B                  
    <missing>           7 days ago          /bin/sh -c #(nop) COPY file:0fd5fca330dcd6a7…   1.04kB              
    。。。省略。。。
    

    搜索镜像

    在 docker 中搜索镜像主要使用 Search 子命令,默认只搜索 Docker Hub 官方镜像仓库中的镜像。

    格式:

    docker search [option] [所搜索的镜像名称] [keyword]
    

    实例:

    [root@Centos7 docker]# docker search python
    NAME                             DESCRIPTION                                     STARS               OFFICIAL            AUTOMATED
    python                           Python is an interpreted, interactive, objec…   5661                [OK]                
    django                           Django is a free web application framework, …   1021                [OK]                
    pypy                             PyPy is a fast, compliant alternative implem…   256                 [OK]                
    nikolaik/python-nodejs           Python with Node.js                             55                                      [OK]
    joyzoursky/python-chromedriver   Python with Chromedriver, for running automa…   54                                      [OK]
    

    输出信息:

    字段 含义
    NAME 仓库名称
    DESCRIPTION 描述
    STARS 收藏个数
    OFFICIAL 是否是官方镜像
    AUTOMATED 是否是自构建的镜像

    常用选项:

    -f : 过滤

    # 搜索被收藏超过 300 个的并且关键词包括 Python 的镜像
    docker search -f stars=300 python
    
    # 搜索官方提供的带有 Redis 关键字的镜像
    docker search -f is-official=true redis
    NAME        DESCRIPTION              STARS            OFFICIAL            AUTOMATED
    redis      Redis is an open source key-value store that…   8792                [OK]
    

    --no-trunc: 不截断输出结果。

    --limit: 限制输出结果。

    删除镜像

    使用docker rmi命令,相当于docker image rm命令。

    格式:

    docker rmi [option] [镜像名称:版本号 | 镜像ID]
    

    实例:

    [root@localhost ~ ]# docker images
    REPOSITORY      TAG                 IMAGE ID            CREATED             SIZE
    nginx_2          v1                 2c45f814f15b        32 minutes ago      131MB
    nginx_3          v1                 2c45f814f15b        32 minutes ago      131MB
    nginx            latest             bc9a0695f571        7 days ago          133MB
    
    [root@localhost ~ ]# docker rmi nginx_3:v1
    Untagged: nginx_3:v1
    [root@localhost ~ ]# docker images
    REPOSITORY      TAG                 IMAGE ID            CREATED              SIZE
    nginx_2          v1                  2c45f814f15b        32 minutes ago      131MB
    nginx            latest              bc9a0695f571        7 days ago          133MB
    

    选项:
    -f : 强制删除。镜像已经运行为容器则无法删除,可以使用-f强制删除。

    [root@localhost ~ ]# docker ps
    CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                   NAMES
    9bf5ef44a314        nginx               "/docker-entrypoint.…"   51 minutes ago      Up 51 minutes       0.0.0.0:32770->80/tcp   funny_northcutt
    [root@localhost ~ ]# docker rmi nginx:latest
    Error response from daemon: conflict: unable to remove repository reference "nginx:latest" (must force) - container b1bc639c450e is using its referenced image bc9a0695f571
    [root@localhost ~ ]# docker rmi -f nginx:latest
    Untagged: nginx:latest
    [root@localhost ~ ]# docker images
    REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
    nginx_2             v1                  2c45f814f15b        38 minutes ago      131MB
    <none>              <none>              bc9a0695f571        7 days ago          133MB
    

    清理镜像

    使用一段时间之后,docker 会产生很多临时镜像文件,以及一些没有被使用的镜像, 我们可以通过 docker image prune 命令来进行清理。

    格式:

    docker image prune [选项]
    

    选项:

    • -a:清理所有没有当前使用的镜像,不仅是临时镜像。
    • -f:强制删除。同rmi删除。

    推送镜像

    push将本地镜像推送到网上的个人的私有仓库中,例如阿里云的私有仓库。

    格式:

    docker push [镜像仓库URL]/[命名空间名称]/[仓库名称]:[版本号]
    

    实例:

    1、先登录私有仓库。

    docker login --username=“登录名,最好纯英文” 仓库URL
    

    2、将镜像改名。

    # 私有仓库名最好设置为镜像名,在私有仓库内只能查看版本,而没有镜像名。
    docker tag 本地镜像名 [镜像仓库URL]/[命名空间名称]/[仓库名称]:[版本号]
    

    3、推送镜像,push一次只能推送一个镜像。

    [root@Centos7 docker]# docker push registry.cn-hangzhou.aliyuncs.com/alvinos/py15-nginx:1.19.2
    The push refers to repository [registry.cn-hangzhou.aliyuncs.com/alvinos/py15-nginx]
    908cf8238301: Pushed 
    eabfa4cd2d12: Pushed 
    60c688e8765e: Pushed 
    f431d0917d41: Pushed 
    07cab4339852: Pushed 
    1.19.2: digest: sha256:794275d96b4ab96eeb954728a7bf11156570e8372ecd5ed0cbc7280313a27d19 size: 1362
    

    构建镜像

    构建镜像一般有三种情况,基于容器导入、基于本地模板导入、基于 Dockerfile 创建,本节主讲基于容器保存镜像和本地保存镜像文件导入。

    1、保存容器为镜像

    格式:

    docker commit [选项] [容器ID | 容器名称:版本号]
    

    选项:

    • -a : 指定作者。
    • -m : 简介。
    • -p : 保存镜像时,容器暂停运行。

    实例:

    [root@localhost ~ ]# docker commit -a 'chirou' -m 'nginx_demo' -p 50cf6c577510
    sha256:94738e2585944aa455e0c3e1bb174fba02dc18ea17135811d1b874ea0beaab7e
    [root@localhost ~ ]# docker images
    REPOSITORY               TAG             IMAGE ID            CREATED             SIZE
    <none>                  <none>           94738e258594       4 seconds ago		 133MB
    
    # 使用inspect查看详细信息。
    [root@localhost ~ ]# docker inspect 94738e258594 | grep chirou
            "Author": "chirou",
    [root@localhost ~ ]# docker inspect 94738e258594 | grep nginx_demo
            "Comment": "nginx_demo",
    

    2、导入与导出容器

    某些时候,需要将容器或镜像保存成文件从一个系统迁移到另外一个系统,此时可以使用 Docker 的导入和导出功能,这也是Docker 自身提供的一个重要特性。导出的文件是一个tar包,可以通过压缩命令进行压缩,然后进行传输。

    export和import

    export将容器导出到标准输出,可以使用输出重定向或-o选项至文件中。

    格式:

    docker export [容器名或ID] > [文件名称]
    

    实例:将nginx容器导出。

    [root@localhost ~ ]# docker export nginx > export_nginx.tar
    

    import 是将export导出的文件导入为镜像,可以自定义导入的镜像名称和版本号。

    import虽然可以导入save保存的文件但是导入后无法运行。

    格式:

    docker import [文件名称] [自定义镜像名称]:[版本号]
    

    实例:将上面导出的文件导入。

    [root@localhost ~ ]# docker import export_nginx.tar import_nginx:v1
    87b71baffd0c8e5b2b98884caee97a9d3abcee444e9bad10c865db0daaafa024
    
    [root@localhost ~ ]# docker images
    REPOSITORY             TAG              IMAGE ID            CREATED             SIZE
    import_nginx           v1              87b71baffd0c        44 seconds ago      131MB
    nginx                 latest           bc9a0695f571        10 days ago         133MB
    
    # 通过export导出的文件,使用import导入后无法直接运行。
    [root@localhost mnt ]# docker run -d import_nginx:v1
    docker: Error response from daemon: No command specified.
    

    可以看到,通过export导出的容器文件,再使用import导入后,SIZE是要比源镜像小的。这是因为export导出的是容器,并没有源镜像的全部内容,比如会丢失构建历史记录和元数据信息等文件,相当于仅保存容器当时的快照状态,这会导致export导出的文件无法直接通过run命令运行,解决方法:

    运行时通过-it选项,给容器分配一个伪终端。

    docker run -dit import_nginx:v1 sh
    

    推荐使用commit来保存容器,然后再使用save保存,能避免上述问题。

    3、导入与导出镜像

    save和load

    save能将镜像完整的保存下来,包括镜像ID和构建历史。一样可用输出重定向或-o选项保存至tar包中,并且save支持将多个镜像保存至一个tar包中。

    格式:

    docker save [镜像名或ID ...] > [压缩包名称]
    docker save -o [压缩包名称] [镜像名称或ID ...]
    

    实例:

    # 不指定版本号则默认为latest,表示最新版。
    [root@localhost ~ ]# docker save busybox nginx > box_nginx.tar
    [root@localhost ~ ]# ll
    -rw-r--r--  1 root root 138553344 12月  5 19:30 box_nginx.tar
    

    load是将save保存的镜像文件载入为镜像。save保存时若使用镜像ID保存镜像,导入时则没有镜像名称,load在导入时也不能自定义镜像名称,可以在导入后使用docker tag命令修改。

    格式:

    docker load < [压缩包名称]
    <相当于-i选项,指定导入的文件,默认是STDIN。
    

    实例:

    [root@localhost ~ ]# docker save dc3bacd8b5ea bc9a0695f571 > box_nginx.tar
    
    # 将原镜像删除
    [root@localhost ~ ]# docker rmi bc9a0695f571 dc3bacd8b5ea
    # 载入镜像
    [root@localhost ~ ]# docker load < box_nginx.tar 
    Loaded image ID: sha256:bc9a0695f5712dcaaa09a5adc415a3936ccba13fc2587dfd76b1b8aeea3f221c
    Loaded image ID: sha256:dc3bacd8b5ea796cea5d6070c8f145df9076f26a6bc1c8981fd5b176d37de843
    
    # 此时ID还是原来的,但tag信息都为none
    [root@localhost ~ ]# docker images
    <none>              <none>              bc9a0695f571        10 days ago         133MB
    <none>              <none>              dc3bacd8b5ea        11 days ago        1.23MB
    

    八、容器常用操作

    查看容器

    ps

    该子命令能查看当前正在运行的容器。

    实例:

    [root@localhost ~ ]# docker ps
    CONTAINER ID     IMAGE     COMMAND     CREATED      STATUS       PORTS           NAMES
    b1a13dfe7105    nginx     "/docker-entrypoint.…"   3 seconds ago    Up 2 seconds        0.0.0.0:32774->80/tcp   reverent_maxwell
    

    如果要查看已停止的容器则使用 -a 选项。该选项能查看所有容器,包括正在运行和已经停止的。

    # 先停止该容器。
    [root@localhost ~ ]# docker stop reverent_maxwell
    [root@localhost ~ ]# docker ps -a
    CONTAINER ID     IMAGE      COMMAND          CREATED      STATUS      PORTS      NAMES
    b1a13dfe7105   nginx     "/docker-entrypoint.…"   2 minutes ago     Exited (0) 6  conds ago          reverent_maxwell
    

    -q:仅显示ID信息。

    [root@localhost ~ ]# docker ps -aq
    b1a13dfe7105
    

    inspect

    该子命令能查看该容器详细信息。

    [root@localhost ~ ]# docker inspect b1a13dfe7105
    [
        {
            "Id": "b1a13dfe71056d8c02fb90e52a503bbedc60301f62e9c49604fa86c2258fd310",
            "Created": "2020-12-05T12:18:17.241753416Z",
            "Path": "/docker-entrypoint.sh",
            "Args": [
                "nginx",
                "-g",
                "daemon off;"
            。。。省略行。。。
    

    同样支持 -f 选项过滤指定信息。

    [root@localhost ~ ]# docker inspect -f '{{.Id}}' b1a13dfe7105
    b1a13dfe71056d8c02fb90e52a503bbedc60301f62e9c49604fa86c2258fd310
    

    查看容器日志

    格式:

    docker logs [option] [容器名称或ID]
    
    • -f:跟踪日志输出。类似tail -f命令。
    • --since:显示某个开始时间的所有日志。
    • -t:显示时间戳。
    • --tail N:仅列出最新N条容器日志。

    实例:

    [root@localhost ~ ]# docker logs b1a13dfe7105
    /docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
    /docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
    /docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
    10-listen-on-ipv6-by-default.sh: Getting the checksum of /etc/nginx/conf.d/default.conf
    

    显示时间戳:

    [root@localhost ~ ]# docker logs -t b1a13dfe7105
    2020-12-05T12:18:17.500960288Z /docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
    2020-12-05T12:18:17.500995723Z /docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
    。。。省略行。。。
    

    仅查看3条日志:

    # 仅查看3条日志。
    [root@localhost ~ ]# docker logs --tail 3 b1a13dfe7105
    192.168.112.1 - - [05/Dec/2020:13:46:53 +0000] "GET /favicon.ico HTTP/1.1" 404 153 "http://192.168.112.129:32775/" "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:83.0) Gecko/20100101 Firefox/83.0" "-"
    192.168.112.1 - - [05/Dec/2020:13:46:54 +0000] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:83.0) Gecko/20100101 Firefox/83.0" "-"
    192.168.112.1 - - [05/Dec/2020:13:46:55 +0000] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:83.0) Gecko/20100101 Firefox/83.0" "-"
    

    查看2020年12月5日之后的日志:

    # 日期格式不能错误。
    [root@localhost ~ ]# docker logs --since="2020-12-04" b1a13dfe7105
    /docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
    /docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
    /docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
    10-listen-on-ipv6-by-default.sh: Getting the checksum of /etc/nginx/conf.d/default.conf
    10-listen-on-ipv6-by-default.sh: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf
    /docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
    /docker-entrypoint.sh: Configuration complete; ready for start up
    /docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
    。。。省略行。。。
    

    运行容器

    docker run能将一个镜像运行为容器。容器当中至少有一个进程运行在前台。

    格式:

    docker run [选项] [镜像名称|镜像ID] [容器启动后内部执行的命令]
    

    选项:
    -d : 以守护进程的方式运行。

    -p : 指定端口映射。

    # 格式
    docker run -p 宿主主机端口:容器向外暴露的端口 [镜像名称:tag | 镜像ID]
    docker run -d -p 8899:80 nginx:1.19.2
    

    -P : 随机端口映射。

    docker run -d -P nginx:1.19.2
    

    --name: 指定容器的名称,同一台宿主主机上的docker名称不能重复。

    docker run -d --name 自定义容器名 -P nginx:1.19.2
    

    --rm:当一个容器停止后,就立即删除。

    docker run -d --rm nginx:1.19.2
    

    -v: 映射存储卷,可以映射文件及文件夹。

    映射后在宿主机文件内的修改都会映射到容器内的文件中。

    docker run -d -v 宿主机文件路径:容器内文件路径 nginx:1.19.2
    

    -i : 打开标准输出。

    -t : 创建一个伪准端。通常与-i选项一起使用。

    # 运行容器后执行bash命令。
    docker run -ditP nginx bash
    

    -e : 在容器内设置一个环境变量。

    docker run -d -e NGINX_NAME=nginx nginx:1.19.2
    

    --network 指定网络模式,下篇再讲docker网络。

    --link:链接到另一个容器。

    -h:指定容器内的主机名。

    停止、启动、重启容器

    stop能将一个正在运行的容器停止。

    docker stop [容器名称 | 容器ID]
    

    start将被stop停止的容器启动。

    docker start [容器名称 | 容器ID]
    

    restart重启容器。

    当修改了容器内某些配置文件后,可以使用此命令使配置生效。

    docker restart [容器名称 | 容器ID]
    

    进入容器

    在使用容器的过程中,我们难免需要进入容器进行排查问题。下面来介绍进入容器的四种方式。

    1、attach

    通过管道,连接容器内PID=1的进程,容器至少有一个进程运行前台。attach 是最早 docker 官方推出的进入容器的命令了,不过使用该命令有一个问题。当多个窗口同时使用该命令进入该容器时,所有的窗口都会同步显示。如果有一个窗口阻塞了,那么其他窗口也无法再进行操作,当所有窗口退出时,容器结束。

    docker attach [容器名或ID]
    

    2、exec

    继attach 之后,exec 是官方推出的有一个新的进入容器的命令,这也是目前推荐使用的进入容器的方式。这个命令相当于在容器中执行一个命令。

    docker exec [参数] [容器名或ID] [命令]
    

    实例:

    [root@localhost ~ ]# docker exec -it reverent_maxwell sh
    用这种方式相当于进入该容器,并且在退出时不会将容器也停止。
    

    退出容器可使用exit命令或Ctrl+d快捷键。

    3、nsenter

    进入容器中,但不进入容器内的进程。Nsenter 是 Linux 提供的命令。需要配合 docker inspect 来使用(早期没有 exec 命令时,企业当中最长用的方式之一),Docker 是用 golang 语言开发,所以它也支持 go 语言的模板语法。

    [root@localhost ~ ]# nsenter --target $( docker inspect -f {{.State.Pid}} nginxv1 ) --mount --uts --ipc --net --pid
    mesg: ttyname failed: No such device root@6f99ae8757f7:/#
    

    4、ssh

    在镜像(或容器) 中安装 SSH Server,这样就能保证多人进入容器且相互之间不受干扰了,相信大家在当前的生产环境中(没有使用 Docker 的情况)也是这样做的。但是使用了 Docker 容器之后不建议使用 ssh 进入到 Docker 容器内。

    复制文件

    复制命令类似于 Linux 系统中的 scp 命令,是将宿主主机上的内容上传到容器中,也可能是将容器中的文件下载到宿主主机中。

    从容器内复制文件到宿主主机。

    docker cp [容器ID:容器内文件路径] 宿主主机路径
    

    从宿主主机复制文件到容器。

    docker cp 宿主主机路径 [容器ID:容器内文件路径]
    

    实例:

    # 复制到容器内根目录。
    [root@localhost ~ ]# docker cp conf.d/ b1a13dfe7105:/
    # 使用exec在容器内执行ls命令。
    [root@localhost ~ ]# docker exec b1a13dfe7105 ls /
    bin
    boot
    conf.d
    

    删除容器

    stop命令仅仅是将容器停止,若要删除容器则使用docker rm子命令。

    格式:

    docker rm [option] [镜像名称或ID]
    

    rm能删除已停止的容器,对于正在运行的容器可以使用-f选项强制删除,一般不推荐直接将一个正在运行的容器强制删除。

    实例:

    # 查看正在运行的容器。
    [root@localhost ~ ]# docker ps
    CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                NAMES
    1a9b4ae8766b        nginx               "/docker-entrypoint.…"   About an hour ago   Up About an hour    0.0.0.0:80->80/tcp   pedantic_hopper
    # 直接使用rm无法删除。
    [root@localhost ~ ]# docker rm 1a9b4ae8766b
    Error response from daemon: You cannot remove a running container 1a9b4ae8766b9266f0e5256df1ac56b9647483815f60b447ae9f15d6c3355dec. Stop the container before attempting removal or force remove
    # 使用-f选项可以强制删除。
    [root@localhost ~ ]# docker rm -f 1a9b4ae8766b
    1a9b4ae8766b
    

    使用docker ps -a与rm和stop命令配合,可以达到一个批量处理容器的方式:

    # 停止所有正在运行的容器
    docker stop $(docker ps)
    
    # 删除所有容器,包括正在运行的容器
    docker rm -f $(docker ps -a)
    

    九、Dockerfile构建镜像

    Dockerfile 由一行行命令语句组成, 并且支持以#开头的注释行。一般而言, Dockerfile 主体内容分为四部分:基础镜像信息、 维护者信息、 镜像操作指令和容器启动时执行指令。

    Docker 以从上到下的顺序运行 Dockerfile 的指令。为了指定基本映像,第一条指令必须是 FROM。一个声明以#字符开头则被视为注释。可以在 Docker 文件中使用 RUN,CMD,FROM,EXPOSE,ENV 等指令。

    指令 含义
    FROM 指定基础镜像,必须为第一个命令
    MAINTAINER 维护者信息
    RUN 构建镜像docker build时执行的命令
    ADD 将本地文件添加到容器中,tar 类型文件会自动解压(网络压缩资源不会被解压)
    COPY 功能类似ADD,但是是不会自动解压文件,也不能访问网络资源
    CMD 在docker run时会执行的命令,如果存在多个则仅最后一个生效。
    LABEL 用于为镜像添加元数据
    ENV 设置环境变量
    EXPOSE 指定于外界交互的端口
    VOLUME 用于指定持久化目录
    WORKDIR 工作目录,类似于cd命令
    ARG 用于指定传递给构建运行时的变量
    ONBUILD 用于设置镜像触发器

    FROM

    格式:
    	FROM <image> 
        FROM <image>:<tag> 
        FROM <image>@<digest> 
    示例: 
    	FROM mysql:5.6 
    注: tag 或 digest 是可选的,如果不使用这两个值时,会使用 latest 版本的基础镜像
    

    MAINTAINER

    格式:
    	MAINTAINER <name>
    示例:
        MAINTAINER Jasper Xu MAINTAINER sorex@163.com
        MAINTAINER Jasper Xu <sorex@163.com>
    

    RUN

    RUN 用于在镜像容器中执行命令,一个Dockerfile文件内可以有多个RUN其有以下两种命令执行方式: 
    shell 执行 
    	格式:RUN <command> 
    exec 执行 
    	格式:RUN ["executable", "param1", "param2"] 
    	示例:
    		 RUN ["executable", "param1", "param2"] 
    		 RUN apk update RUN ["/etc/execfile", "arg1", "arg1"] 
    注: RUN 指令创建的中间镜像会被缓存,并会在下次构建中使用。如果不想使用这些缓存镜像,可以在构建 时指定--no-cache 参数,如:docker build --no-cache
    

    ADD

    格式:
        ADD <src>... <dest>
        ADD ["<src>",... "<dest>"] 用于支持包含空格的路径
    示例:
        ADD hom* /mydir/	# 添加所有以"hom"开头的文件
        ADD hom?.txt /mydir/	# ? 替代一个单字符,例如:"home.txt"
        ADD test relativeDir/	# 添加 "test" 到 `WORKDIR`/relativeDir/ ADD test /absoluteDir/	# 添加 "test" 到 /absoluteDir/
    

    COPY

    指令:COPY
    功能描述:复制文件到镜像中。
    语法:COPY < src>… < dest>|[“< src>”,… “< dest>”]
    提示:指令逻辑和 ADD 十分相似,同样 Docker Daemon 会从编译目录寻找文件或目录,dest 为镜像中的绝对路径或者相对于 WORKDIR 的路径。
    

    CMD

    格式:
        CMD ["executable","param1","param2"] (执行可执行文件,优先)
        CMD ["param1","param2"] (设置了 ENTRYPOINT,则直接调用 ENTRYPOINT 添加参数) 
        CMD command param1 param2 (执行 shell 内部命令)
    示例:
        CMD echo "This is a test." | wc -
        CMD ["/usr/bin/wc","--help"]
        注:	CMD 不同于 RUN,CMD 用于指定在容器启动时所要执行的命令,而 RUN 用于指定镜像构建时所要执行的命令。
    

    LABEL

    格式:
    	LABEL <key>=<value> <key>=<value> <key>=<value> ...
    示例:
    	LABEL version="1.0" description="这是一个 Web 服务器" by="IT 笔录"
    注:
    使用 LABEL 指定元数据时,一条 LABEL 指定可以指定一或多条元数据,指定多条元数据时不同元数据之间通过空格分
    隔。推荐将所有的元数据通过一条 LABEL 指令指定,以免生成过多的中间镜像。
    

    ENV

    格式:
    	ENV <key> <value>	
    	#<key>之后的所有内容均会被视为其<value>的组成部分,因此,一次只能设置一个变量
    	ENV <key>=<value> ...	
    	#可以设置多个变量,每个变量为一个"<key>=<value>"的键值对,如果<key>中包含空格,可以使用来进行转义,也可以通过""来进行标示;另外,反斜线也可以用于续行
    示例:
        ENV myName John Doe 
        ENV myDog Rex The Dog 
        ENV myCat=fluffy
    

    EXPOSE

    格式:
    	EXPOSE <port> [<port>...]
    示例:
        EXPOSE 80 443
        EXPOSE 8080
        EXPOSE 11211/tcp 11211/udp
    注:
    EXPOSE 并不会让容器的端口访问到主机。要使其可访问,需要在 docker run 运行容器时通过-p 来发布这些端口,或通过-P 参数来发布 EXPOSE 导出的所有端口
    

    VOLUME

    格式:
    	VOLUME ["/path/to/dir"]
    示例:
        VOLUME ["/data"]
        VOLUME ["/var/www", "/var/log/apache2", "/etc/apache2"]
    
    注:
    # 1、卷可以容器间共享和重用。
    # 2、容器并不一定要和其它容器共享卷。
    # 3、修改卷后会立即生效。
    # 4、对卷的修改不会对镜像产生影响。
    # 5、卷会一直存在,直到没有任何容器在使用它。
    

    WORKDIR

    格式:
    	WORKDIR /path/to/workdir
    示例:
        WORKDIR /a		(这时工作目录为/a) 
        WORKDIR b	(这时工作目录为/a/b) 
        WORKDIR c	(这时工作目录为/a/b/c)
    注:
    通过 WORKDIR 设置工作目录后,Dockerfile 中其后的命令 RUN、CMD、ENTRYPOINT、ADD、COPY
    等命令都会在该目录下执行。在使用 docker run 运行容器时,可以通过-w 参数覆盖构建时所设置的工作目录。
    

    ARG

    格式:
    	ARG <name>[=<default value>]
    示例:
    	ARG site
    	ARG build_user=www
    

    ONBUILD

    格式:
    示例:
        ONBUILD ADD . /app/src
        ONBUILD RUN /usr/local/bin/python-build --dir /app/src
    注:
    当所构建的镜像被用做其它镜像的基础镜像,该镜像中的触发器将会被钥触发。
    

    构建镜像build

    在 Dockerfile 文件的存放目录下,使用docker build子命令执行构建动作。

    格式:

    docker build -t [自定义镜像名称:tag] .
    

    -t:是指定镜像名称和tag。

    .代表本次执行的上下文路径。

    上下文路径,是指 docker 在构建镜像,有时候想要使用到本机的文件(比如复制),docker build 命令得知这个路径后,会将路径下的所有内容打包。

    由于 docker 的运行模式是 C/S。我们本机是 C,docker 引擎是 S。实际的构建过程是在 docker 引擎下完成的,所以这个时候无法用到我们本机的文件。这就需要把我们本机的指定目录下的文件一起打包提供给 docker 引擎使用。

    如果未说明最后一个参数,那么默认上下文路径就是 Dockerfile 所在的位置。

    注意:上下文路径下不要放无用的文件,因为会一起打包发送给 docker 引擎,如果文件过多会造成过程缓慢。

    构建镜像实例

    构建一个Django容器

    1、在宿主机上安装好python3以及Django。

    yum install -y python3 
    pip3 install django -i https://mirrors.aliyun.com/pypi/simple --trusted-host aliyun.com/pypi/simple
    

    2、创建一个app项目。

    django-admin startproject app
    cd app
    django-admin startapp application
    

    3、修改settings.py文件。

    cd app
    [root@localhost app ]# vim settings.py 
    
    。。。省略行。。。
    # 在中括号内插入'*'
    ALLOWED_HOSTS = ['*']
    
    。。。省略行。。。
    
    # 将以下几行注释掉。
    DATABASES = {
    #    'default': {
    #        'ENGINE': 'django.db.backends.sqlite3',
    #        'NAME': BASE_DIR / 'db.sqlite3',
    #    }
    }
    。。。省略行。。。
    

    4、在宿主机系统上测试运行。

    cd ..
    python3 manage.py runserver 0.0.0.0:8000
    

    5、编辑Dockerfile文件

    [root@localhost ~ ]# vim Dockerfile
    
    # 基于python3.6构建Django
    FROM python:3.6
    # 安装Django
    RUN pip3 install django==2.2.2 -i https://mirrors.aliyun.com/pypi/simple/ --trusted-host mirrors.aliyun.com/pypi/simple/
    # 将宿主机生成的app目录复制到镜像内。
    COPY app /root/
    # 镜像启动为容器后,在容器内启动Django。
    CMD cd /root && python3 manage.py runserver 0.0.0.0:8000
    

    6、构建镜像。

    docker build -t python3/django:v1 .
    

    7、启动镜像,指定映射端口。

    docker run -dit -p 3800:8000 python3/django:v1
    

    8、测试可正常访问。

    [root@localhost ~ ]# curl 127.0.0.1:3800
    
    <!doctype html>
    <html>
        <head>
            <meta charset="utf-8">
            <title>Django: the Web framework for perfectionists with deadlines.</title>
            <meta name="viewport" content="width=device-width, initial-scale=1">
            <link rel="stylesheet" type="text/css" href="/stat
            。。。省略行。。。
    

    构建一个NGINX容器

    1、在宿主机上配置好nginx的yum源文件。

    更换yum源。
    vim nginx.repo
    [nginx-stable]
    name=nginx stable repo
    baseurl=http://nginx.org/packages/centos/$releasever/$basearch/
    gpgcheck=1
    enabled=1
    gpgkey=https://nginx.org/keys/nginx_signing.key
    module_hotfixes=true
    
    [nginx-mainline]
    name=nginx mainline repo
    baseurl=http://nginx.org/packages/mainline/centos/$releasever/$basearch/
    gpgcheck=1
    enabled=0
    gpgkey=https://nginx.org/keys/nginx_signing.key
    module_hotfixes=true
    

    2、编辑Dockerfile文件。

    [root@localhost ~ ]# vim Dockerfile
    
    # 指定依赖镜像
    FROM centos:7
    
    # 安装nginx基础环境。
    RUN yum install -y  yum-utils
    # 将yum源文件复制到镜像内。
    ADD nginx.repo /etc/yum.repos.d/
    # 建立yum元数据缓存
    RUN yum makecache
    # 安装nginx
    RUN yum install -y nginx
    
    # 容器启动时启动nginx
    CMD nginx -g 'daemon off;'
    

    3、构建镜像

    docker build -t centos7/nginx:v1 .
    

    4、运行镜像为容器。

    docker run -d -p 8888:80 --name my_nginx centos7/nginx:v1
    

    5、测试是否能访问。

    [root@localhost ~ ]# curl 127.0.0.1:8888
    <!DOCTYPE html>
    <html>
    <head>
    <title>Welcome to nginx!</title>
    <style>
        body {
             35em;
            margin: 0 auto;
            font-family: Tahoma, Verdana, Arial, sans-serif;
            。。。省略行。。。
    

    使用NGINX代理Django

    1、本地创建default.conf文件。

    vim default.conf
    server {
        listen       80;
        server_name  localhost;
    
        #charset koi8-r;
        #access_log  /var/log/nginx/host.access.log  main;
    
        location / {
            proxy_pass http://192.168.112.129:3800/;
            index  index.html index.htm;
        }
    
    }
    

    2、将本地文件复制到容器中。

    docker cp default.conf my_nginx:/etc/nginx/conf.d/
    
    

    3、重启容器。

    docker restart my_nginx
    

    4、测试访问nginx

    [root@localhost ~ ]# curl 127.0.0.1:8888
    
    <!doctype html>
    <html>
        <head>
            <meta charset="utf-8">
            <title>Django: the Web framework for perfectionists with deadlines.</title>
            <meta name="viewport" content="width=device-width, initial-scale=1">
            <link rel="stylesheet" type="text/css" href="/static/admin/css/fonts.css">
            <style type="text/css">
    

    将以上镜像上传到镜像仓库

    1、登录仓库。

    docker login --username=sepzhan registry.cn-shanghai.aliyuncs.com
    
    registry.cn-hangzhou.aliyuncs.com/dock_tst/test_cr:[镜像版本号]
    

    2、更改镜像名。

    # django改为v1
    docker tag python3_django:v1 registry.cn-shanghai.aliyuncs.com/sepzhan/zmc:v1
    # nginx改为v2
    docker tag centos7/nginx:v1 registry.cn-shanghai.aliyuncs.com/sepzhan/zmc:v2
    

    3、推送镜像至仓库。

    # 推送Django镜像
    docker push registry.cn-shanghai.aliyuncs.com/sepzhan/zmc:v1
    # 推送nginx镜像
    docker push registry.cn-shanghai.aliyuncs.com/sepzhan/zmc:v2
    
  • 相关阅读:
    wget
    android layout 布局属性
    Android 官方推荐 : DialogFragment 创建对话框
    Android 屏幕旋转 处理 AsyncTask 和 ProgressDialog 的最佳方案
    Android Fragment 真正的完全解析
    Android-Universal-Image-Loader 图片异步加载类库的使用(超详细配置)
    Visual Studio VS2013模块对于SAFESEH 映像是不安全的 怎么办
    PS 图层后面有索引两字怎么办
    PS 如何使用液化工具给人物减肥
    PS 图层后面有索引两字怎么办
  • 原文地址:https://www.cnblogs.com/ChiRou/p/14091204.html
Copyright © 2020-2023  润新知