docker

本文深入解析Docker的诞生背景,详细介绍Docker的核心概念如镜像、容器和仓库,以及如何利用DockerFile构建和管理镜像。文章涵盖Docker的基本命令、镜像原理、数据卷、数据卷容器和DockerFile的高级用法,适合Docker初学者及进阶者阅读。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、docker简介

1.1 为什么会有docker的出现?

以前啊,开发工程师,项目开发完了之后,在本地运行没有问题,打包成war包,交给运维,运维要在linux上部署,部署需要安装个个软件,比如你项目中用到了mq,redis,es等等,结果运维运行出问题了,但是你本地运行没问题,两个人就吵起来了。其实是环境不一样,可能软件版本也不一样。

还有以前搞集群,运维每台服务器都要装相同的软件,server01,server02,server03,这里有三台服务器,运维每台服务器都要装相同的软件,redis,es,mq等等

docker怎么来解决这些问题呢?

第一个问题解决:开发工程师把本地运行的,一些环境配置,一些软件redis,es,mq 什么版本的,装到一个箱子里面,然后给运维,运维直接用开发工程师给的这些环境在服务器上部署。这样就避免了版本环境不一致的问题。

第二个问题解决:运维工程师把这些redis,mq,es软件做成一个镜像,然后在其他服务器上面直接安装就好了,不要每个软件在每个服务器都安装一边。

在这里插入图片描述

1.2 docker 三要素

Person

  • 镜像 – 就是模板。一个镜像可以创建多个容器。相当于java里面的类(Class Person)。
  • 容器 – 是镜像创建的运行实例。相当于java里面类创建的实例(new Person)。
  • 仓库 – 集中存放镜像文件的场所。

在这里插入图片描述

二、docker-HelloWord镜像

2.1 在虚拟机上 centos 7 安装docker

省略。。。(自己在网上搜教程,一大把。官网也有。)

2.2 阿里云加速器

自己在网上搜教程,一大把。官网也有。
配置一下,然后让它生效。

2.3 hello-word

查看docker版本
# docker version

启动docker
# systemctl start docker

运行第一个hello-world
# docker run hello-world

run 干了什么?

在这里插入图片描述

三、docker命令

3.1 帮助命令

查看docker版本信息
# docker version

查看docker个人信息
# docker info 

查看docker帮助命令
# docker -help

3.2 镜像命令

查看docker本地所有镜像
# docker images

在这里插入图片描述
repository:表示镜像的仓库源
tag:镜像的标签
image ID:镜像ID
created: 镜像创建时间
size:镜像大小

同一个仓库源可以有多个TAG,代表这个仓库源的不同版本,我们使用REPOSITORY:TAG 来定义不同的镜像。
如果你不指定版本,默认使用 latest ,例如你使用centos ,docker默认使用 centos:latest

参数说明:
-a:列出本地所有镜像(含中间镜像层
-q:只显示镜像ID
–digests:显示镜像的摘要信息
–no-trunc:显示完成的镜像信息

注意:镜像是分层的,就好比maven里面的依赖关系一样。你下载一个jar,他可能依赖其它jar包

去dockerHub上搜索镜像
# docker search XXX镜像的名字

参数说明:
-s:列出收藏数不小于指定值的镜像
–no-trunc:显示完成的镜像描述
–automated:只列出 “自动化构建” 类型的镜像

下载镜像(默认[: latest])

# docker pull XXX镜像名

# docker pull XXX镜像名 [: tag]
docker镜像删除命令
# docker rmi XXX镜像名/ID

删除单个镜像
# docker rmi -f 镜像名/ID

删除多个镜像
# docker rmi -f 镜像名1 镜像名2 ...

删除所有镜像
# docker rmi -f $(docker images -qa)

注:-f 强制删除的意思

3.3 容器命令

新建并启动容器

# docker run [options] image [command][arg...]

options 说明(常用):

–name 容器新名字:为容器指定一个名称;
-d:后台运行容器,并返回容器ID,也是启动守护式容器;
-i:以交互模式运行容器,通常与 -t 使用;
-t:为容器分配一个伪输出终端,通常与 -i 使用;
-P:随机端口映射;
-p:指定端口映射,有一下四种模式
ip:hostPort:containerPort
ip::containerPort
hostPort:containerPort
containerPort

列出当前所有正在运行的容器

# docker ps [options]

options 说明(常用):

-a:列出当前所有正在运行的容器+历史上运行过的
-l:显示最近创建的容器
-n:显示最近n个创建的容器
-q:只显示容器编号
–no-trunc:不截断输出

退出容器

1.容器停止并退出
# exit

2.容器不停止退出
# ctrl+p+q
启动容器

# docker start 容器名/ID
重启容器

# docker restart 容器名/ID
停止容器

# docker stop 容器名/ID

强制关闭容器

# docker kill 容器名/ID
删除容器

# docker rm 容器ID

强制删除容器

# docker rm -f 容器ID

删除多个容器

# docker rm -f $(docker ps -aq)

# docker ps -aq | xargs docker rm

四、docker命令(重要)

启动守护式容器
  	
# docker run -d 容器名
查看容器日志

# docker logs -f -t --tail 容器ID

-t:加入时间戳
-f:跟随最新的日志打印
–tail:数字显示最后多少条

查看容器内运行的进程

# docker top 容器ID
查看容器内部的细节

# docker inspect 容器ID
操作容器,但不进入

# docker exec -it 容器ID bashShell 

重新进入(一般和ctrl+p+q 配合使用)

# docker attach 容器ID
从容器内拷贝文件到主机上
# docker cp 容器ID:容器内路径 目标主机路径

五、docker镜像原理

5.1 Union文件系统(UnionFS)

Union文件系统是一种分层,轻量级并且高性能的文件系统,它支持对系统文件的修改作为一次提交来一层层的叠加.
Union文件系统是docker镜像的基础

5.2 docker镜像加载原理

docker镜像的最底层是bootfs.

5.3 为什么docker镜像要采用这种分层结构呢?

其实最主要的就是 资源共享 这个思维,你下载一个tomcat镜像其实里面包含了centos\jdk等等其他的,如果你在下载其他资源这时候就不要再下载已经存在的镜像了.
这里不作详细说明,感兴趣的同学可以去查阅资料.

六、docker镜像commit

docker commit 提交容器副本使之成为新的镜像

dcoker commit -m="提交描述信息" -a="作者名" 容器ID 新创建镜像名:[版本号]

写个案例:

现在我这边有一个tomcat镜像,我现在启动这个tomcat镜像,正常访问.
在这里插入图片描述
在这里插入图片描述
查看这个tomcat的文档信息,也没有问题.
在这里插入图片描述
下面我来把这个tomcat文档删了

在这里插入图片描述
我们再看一下页面

在这里插入图片描述
ok,这时候文档已经访问不了了.下面我们就把这个容器打包成一个镜像.
在这里插入图片描述
下面我们把所有容器都停了吧,再来启动我们刚刚自己生成的nodocumenttomcat,为了和之前的做出区别换个端口吧,8089
在这里插入图片描述
打开页面看效果,按理说文档应该是没有的,我们看是不是
在这里插入图片描述
ok,案例写完了.

七、docker容器数据卷

容器数据卷是用来做数据持久化的!

7.1 容器内添加数据卷

docker run -it -v /宿主机路径:/容器内路径 镜像名/ID

我们来试一下这个命令

在这里插入图片描述

这个是容器的centos
在这里插入图片描述
这个是主机centos
在这里插入图片描述

看一下容器信息
在这里插入图片描述
我们在宿主机新建一个test文件,写了一段话,看看容器里面会不会有这个文件?
在这里插入图片描述
容器里面是有的哦,说明这两个文件是同步的
在这里插入图片描述

这个是容器对主机只读权限.

docker run -it -v /宿主机路径:/容器内路径:ro 镜像名/ID

之前是这样
在这里插入图片描述
现在是这样子的
在这里插入图片描述
在容器里创建文件的时候提示read-only
在这里插入图片描述

7.1 用DockerFile添加数据卷

7.1.1 什么叫DockerFile?

由一系列用于根据基础镜像构建新的镜像文件的专用指令序列组成;

先看一下DockerFile文件结构:

FROM:   FROM是Dockerfile里的第一条指令(必须是),后面跟有效的镜像名(如果该镜像你的本地仓库没有则会从远程仓库Pull取)。然后后面的其它指令FROM的镜像中执行。

MAINTAINER:   用于提供信息的指令,用于让作者提供本人的信息;不限制其出现的位置,但建议紧跟在FROM之后;

RUN:   运行命令,命令较长使可以使用\来换行。推荐使用上面数组的格式    

CMD:  CMD指定容器启动是执行的命令,每个Dockerfile只能有一条CMD命令,如果指定了多条,只有最后一条会被执行。如果你在启动容器的时候也指定的命令,那么会覆盖Dockerfile构建的镜像里面的CMD命令。

ENTRYPOINT:  和CMD类似都是配置容器启动后执行的命令,并且不可被 docker run 提供的参数覆盖。  
 
USER:  指定运行容器时的用户名和UID,后续的RUN指令也会使用这里指定的用户。

EXPOSE:  设置Docker容器内部暴露的端口号,如果需要外部访问,还需要启动容器时增加-p或者-P参数进行分配。

ENV:  设置环境变量,可以在RUN之前使用,然后RUN命令时调用,容器启动时这些环境变量都会被指定

ADD:  将指定的<src>复制到容器文件系统中的<dest> 所有拷贝到container中的文件和文件夹权限为0755,uid和gid为0 

VOLUME:  如果mountpoint路径下事先有文件存在,docker run命令会在卷挂载完成后将此前的文件复制到新挂载的卷中;

WORKDIR:  切换目录,为后续的RUN、CMD、ENTRYPOINT 指令配置工作目录。 

ONBUILD  :  ONBUILD不能自我嵌套,且不会触发FROM和MAINTAINER指令;

ARG:  ARG指定了一个变量在docker build的时候使用,可以使用--build-arg <varname>=<value>来指定参数的值,不过如果构建的时候不指定就会报错。

COPY:  用于从docker主机复制文件至正在创建的映像文件中;

其实就是相当于一个.class文件.

看一下tomcat的DockerFile,你会发现它是依赖于jdk的.
在这里插入图片描述
我们先写一个DockerFile,后面会再跟大家讲一下这些参数.

首先我们在根目录下创建一个mydocker文件夹
在这里插入图片描述
再创建一个Dockerfile文件
在这里插入图片描述
开始编写我们的文件,保存退出
在这里插入图片描述
使用命令,根据DockerFile文件生成镜像

docker build -f /mydocker/Dockerfile -t jikang0821/centos .

-f : 指明DockerFile文件位置
. : 当前路径

运行这个命令
在这里插入图片描述
在这里插入图片描述
我们来运行这个镜像,箭头指向的是我们添加的数据卷
在这里插入图片描述
它真的和我们用 docker run -it -v /宿主机路径:/容器内路径 镜像名/ID效果一样吗?
在这里插入图片描述
那么对应的主机文件生成在哪了呢?这里交给大家一个命令快速查看容器卷和主机对应的位置

# docker inspect -f "{{.Mounts}}" 容器ID

在这里插入图片描述
在这里插入图片描述
有了吧?ok,大功告成.

八、数据卷容器

第七章我们说了容器数据卷,那么什么是数据卷容器呢?
数据卷容器:命名的容器挂载数据卷,其他容器通过挂载这个(父容器)实现数据共享,挂载数据卷的容器,称之为数据卷容器.

--volumes-from

类似于java中的继承,我们之前不是通过DockerFile文件构建了一个镜像,然后又用这个镜像启动了一个容器,如果我们再用这个镜像启动另一个容器并且这个容器要--volumes-from(继承)上一个容器,这就是容器之间的数据共享,挂载数据卷的容器就是数据卷容器.

给大家写个案列,看一下吧.
在这里插入图片描述
这是我们上面用dockerFile文件build的镜像,然后生成的容器4814609ae6bb,能理解吧?
在这里插入图片描述
下面我们再用这个jikang0821/centos镜像生成另一个容器,并且要继承上一个容器4814609ae6bb

docker run -it --volumes-from 4814609ae6bb jikang0821/centos

在这里插入图片描述
我们看看这个容器1c5f134a28dd是不是继承上一个容器4814609ae6bb的数据?看下图,确实继承咯.
在这里插入图片描述
注意: 我说继承只是方便大家理解,实际上他们的关系是共享,容器2继承容器1,会拥有容器1的数据,相反往容器2添加数据也会影响容器1,他们之间的数据是一致性的!

九、DockerFile解析

9.1 DockerFile是什么?

DockerFile是用来构建Docker镜像的文件,是由一系列命令和参数构成的脚本.

构建三步骤: 编写DockerFile文件 -> docker build -> docker run

9.1 DockerFile保留字指令

FROM:   FROM是Dockerfile里的第一条指令(必须是),后面跟有效的镜像名(如果该镜像你的本地仓库没有则会从远程仓库Pull取)。然后后面的其它指令FROM的镜像中执行。

MAINTAINER:   用于提供信息的指令,用于让作者提供本人的信息;不限制其出现的位置,但建议紧跟在FROM之后;

RUN:   运行命令,命令较长使可以使用\来换行。推荐使用上面数组的格式    

CMD:  CMD指定容器启动是执行的命令,每个Dockerfile只能有一条CMD命令,如果指定了多条,只有最后一条会被执行。如果你在启动容器的时候也指定的命令,那么会覆盖Dockerfile构建的镜像里面的CMD命令。

ENTRYPOINT:  和CMD类似都是配置容器启动后执行的命令,并且不可被 docker run 提供的参数覆盖。  
 
USER:  指定运行容器时的用户名和UID,后续的RUN指令也会使用这里指定的用户。

EXPOSE:  设置Docker容器内部暴露的端口号,如果需要外部访问,还需要启动容器时增加-p或者-P参数进行分配。

ENV:  设置环境变量,可以在RUN之前使用,然后RUN命令时调用,容器启动时这些环境变量都会被指定

ADD:  将指定的<src>复制到容器文件系统中的<dest> 所有拷贝到container中的文件和文件夹权限为0755,uid和gid为0 

VOLUME:  如果mountpoint路径下事先有文件存在,docker run命令会在卷挂载完成后将此前的文件复制到新挂载的卷中;

WORKDIR:  切换目录,为后续的RUN、CMD、ENTRYPOINT 指令配置工作目录。 

ONBUILD  :  ONBUILD不能自我嵌套,且不会触发FROM和MAINTAINER指令;

ARG:  ARG指定了一个变量在docker build的时候使用,可以使用--build-arg <varname>=<value>来指定参数的值,不过如果构建的时候不指定就会报错。

COPY:  用于从docker主机复制文件至正在创建的映像文件中;

1、FROM

解释:FROM是Dockerfile里的第一条指令(必须是),后面跟有效的镜像名(如果该镜像你的本地仓库没有则会从远程仓库Pull取)。然后后面的其它指令FROM的镜像中执行。

必须是第一个非注释行,用于指定所用到的基础镜像

语法格式:

FROM  <image>[:<tag>] 

FROM  <image>@<digest>
FROM  busybox:latest
        
FROM centos:6.9

注意:尽量不要在一个dockerfile文件中使用多个FROM指令;

2、MAINTAINER

格式:MAINTAINER <name>
例如:MAINTANIER MageEdu Linux Operation and Maintance Institute <mage@magedu.com>

解释:用于提供信息的指令,用于让作者提供本人的信息;不限制其出现的位置,但建议紧跟在FROM之后;

3、RUN

用于指定docker build过程中要运行的命令,而不是docker run此dockerfile构建成的镜像时运行;

格式:RUN <command>或 RUN["executable", "param1", "param2"]
解释:运行命令,命令较长使可以使用\来换行。推荐使用上面数组的格式

语法格式:

RUN  <command> 或

RUN  ["<executeable>", "<param1>", "<param2>", ...]

RUN ["/bin/bash", "-c", "<executeable>", "<param1>", "<param2>", ...]

例如:RUN yum install iproute nginx && yum clean all

4、CMD

类似于RUN指令,用于运行程序;但二者运行的时间点不同;CMD在docker run时运行,而非docker build;
格式:
CMD ["executable","param1","param2"] 使用 exec 执行,推荐方式;
CMD command param1 param2 在 /bin/sh 中执行,提供给需要交互的应用;
CMD ["param1","param2"] 为ENTRYPOINT指令指定的程序提供默认参数;
解释:

CMD指定容器启动是执行的命令,每个Dockerfile只能有一条CMD命令,如果指定了多条,只有最后一条会被执行。如果你在启动容器的时候也指定的命令,那么会覆盖Dockerfile构建的镜像里面的CMD命令。

CMD指令的首要目的在于为启动的容器指定默认要运行的程序,程序运行结束,容器也就结束;不过,CMD指令指定的程序可被docker run命令行参数中指定要运行的程序所覆盖

注意:如果dockerfile中存在多个CMD指令,仅最后一个生效;

CMD  ["/usr/sbin/httpd", "-c","/etc/httpd/conf/httpd.conf"]

5、ENTRYPOINT

格式:

ENTRYPOINT ["executable", "param1","param2"]

ENTRYPOINT command param1 param2(shell中执行)。

例如:CMD ["-c"] ENTRYPOINT ["top", "-b"]
类似于CMD指令,但其不会被docker run的命令行参数指定的指令所覆盖,而且这些命令行参数会被当作参数送给ENTRYPOINT指令指定的程序;但是,如果运行docker run时使用了–entrypoint选项,此选项的参数可当作要运行的程序覆盖ENTRYPOINT指令指定的程序;

解释:和CMD类似都是配置容器启动后执行的命令,并且不可被 docker run 提供的参数覆盖。

每个 Dockerfile 中只能有一个 ENTRYPOINT,当指定多个时,只有最后一个起效。

ENTRYPOINT没有CMD的可替换特性,也就是你启动容器的时候增加运行的命令不会覆盖ENTRYPOINT指定的命令。

所以生产实践中我们可以同时使用ENTRYPOINT和CMD, 例如:

ENTRYPOINT ["/usr/bin/rethinkdb"]

CMD ["--help"]

6、USER

指定运行镜像时,或运行Dockerfile文件中的任何RUN/CMD/ENTRYPOINT指令指定的程序时的用户名或UID;

格式:USER <UID>|<Username>

解释:指定运行容器时的用户名和UID,后续的RUN指令也会使用这里指定的用户。

注意:应该使用/etc/passwd文件存在的用户的UID,否则,docker run可能会出错;

7、EXPOSE

用于为容器指定要暴露的端口;
格式:EXPOSE<port> [<port>...] <prot>为tcp或udp二者之一,默认为tcp;
例如: EXPOSE 11211/tcp 11211/udp
解释:设置Docker容器内部暴露的端口号,如果需要外部访问,还需要启动容器时增加-p或者-P参数进行分配。

8、ENV

解释:设置环境变量,可以在RUN之前使用,然后RUN命令时调用,容器启动时这些环境变量都会被指定
定义环境变量,此些变量可被当前dockerfile文件中的其它指令所调用,调用格式为variablename或variable_name或variablename{variable_name};
ENV定义的环境变量在镜像运行的整个过程中一直存在,因此,可以使用inspect命令查看,甚至也可以在docker run启动此镜像时,使用–env选项来修改指定变量的值;
格式:ENV<key> <value> 一次定义一个变量

ENV <key>=<value> ...   一次可定义多个变量 ,如果<value>中有空白字符,要使用\字符进行转义或加引号;

ENV  myName="Obama Clark"   myDog=Hello\ Dog  \ myCat=Garfield

等同于:

ENV myName  Obama Clark
ENV myDog  Hello Dog 
ENV myCat  Garfield

解释:设置环境变量,可以在RUN之前使用,然后RUN命令时调用,容器启动时这些环境变量都会被指定

9、ADD:

类似于COPY指令,额外还支持复制TAR文件,以及URL路径;

格式:

ADD <src>... <dest>

ADD ["<src>",... "<dest>"]

解释:将指定的< src>复制到容器文件系统中的< dest> 所有拷贝到container中的文件和文件夹权限为0755,uid和gid为0

如果文件是可识别的压缩格式,则docker会帮忙解压缩

示例: ADD haproxy.cfg /etc/haproxy/haproxy.cfg ADD logstash_*.cnf /etc/logstash/ ADD http://www.magedu.com/download/nginx/conf/nginx.conf /etc/nginx/ 注意:以URL格式指定的源文件,下载完成后其目标文件的权限为600; 注意: < src>必须是build上下文中的路径,因此,不能使用类似“…/some_dir/some_file”类的路径; 如果< src>是URL,且< dest>不以/结尾,则< src>指定的文件将被下载并直接被创建为< dest>;如果< dest>以/结尾,则URL指定的文件将被下载至< dest>中,并保留原名; 如果< src>是一个host本地的文件系统上的tar格式的文件,它将被展开为一个目录,其行为类似于tar -x命令;但是,如果通过URL下载到的文件是tar格式的,是不会自动进行展开操作的; < src>如果是目录,递归复制会自动行;如果有多个< src>,包括在< src>上使用了通配符这种情形,此时< dest>必须是目录,而且必须得以/结尾; < dest>如果事先不存在,它将被自动创建,包括其父目录;

10、VOLUME

用于目标镜像文件中创建一个挂载点目录,用于挂载主机上的卷或其它容器的卷;
格式:VOLUME ["/data"] VOLUME ["<mountpoint>", ...]
解释:可以将本地文件夹或者其他container的文件夹挂载到container中。

注意:如果mountpoint路径下事先有文件存在,docker run命令会在卷挂载完成后将此前的文件复制到新挂载的卷中;

11、WORKDIR

用于为Dockerfile中所有的RUN/CMD/ENTRYPOINT/COPY/ADD指令指定工作目录;
格式:WORKDIR/path/to/workdir
解释:切换目录,为后续的RUN、CMD、ENTRYPOINT 指令配置工作目录。
可以多次切换(相当于cd命令),
 也可以使用多个WORKDIR 指令,后续命令如果参数是相对路径,则会基于之前命令指定的路径。例如

WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd
则最终路径为 /a/b/c。

WORKDIR  /var/log 
WORKDIR  $STATEPATH

注意:WORDIR可出现多次,也可使用相对路径,此时表示相对于前一个WORKDIR指令指定的路径;WORKDIR还可以调用由ENV定义的环境变量的值;

12、ONBUILD :定义触发器

ONBUILD 指定的命令在构建镜像时并不执行,而是在它的子镜像中执行

语法格式

ONBUILD  <INSTRUCTION>

例如:ONBUILD ADD my.cnf /etc/mysql/my.cnf

当前dockerfile构建出的镜像被用作基础镜像去构建其它镜像时,ONBUILD指令指定的操作才会被执行;

注意:ONBUILD不能自我嵌套,且不会触发FROM和MAINTAINER指令;

13、ARG

格式:ARG<name>[=<default value>]
解释:ARG指定了一个变量在docker build的时候使用,可以使用–build-arg =来指定参数的值,不过如果构建的时候不指定就会报错。

14、COPY指令

用于从docker主机复制文件至正在创建的映像文件中;

语法格式:

COPY  <src> ...  <dest>

COPY  ["<src>",...  "<dest>"]  (文件名中有空白字符时使用此种格式)

< src>:要复制的源文件或目录,支持使用通配符;

< dest>:目标路径,正在创建的镜像文件的文件系统路径;建立使用绝对路径,否则,则相对于WORKDIR而言;

所有新复制生成的目录文件的UID和GID均为0;

例如:

COPY  server.xml  /etc/tomcat/server.xml
COPY  *.conf   /etc/httpd/conf.d/

注意:
< src>必须是build上下文中的路径,因此,不能使用类似“…/some_dir/some_file”类的路径;
< src>如果是目录,递归复制会自动行;如果有多个< src>,包括在< src>上使用了通配符这种情形,此时< dest>必须是目录,而且必须得以/结尾;

< dest>如果事先不存在,它将被自动创建,包括其父目录;

十、Docker小结

一张图,大家看一下

在这里插入图片描述

10.1 本地镜像发布到阿里云

step1. 登录阿里云账号,创建明命名空间在这里插入图片描述
step 2 创建镜像仓库
在这里插入图片描述
step 3 选择管理
在这里插入图片描述
step 4 根据操作指南动手
在这里插入图片描述
我这边弄个tomcat
在这里插入图片描述
在这里插入图片描述
我们去阿里云看看,我们的镜像在阿里云能不能看到(我之前设置的私有的,后来换成公开的)
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值