Docker学习随笔(一)

Docker学习

文档地址:https://docs.docker.com/engine/

Docker的基本组成

在这里插入图片描述

镜像(image):

docker镜像就好比是一个模板,可以通过这个模板来创建容器服务,tomcat镜像 ===> run ===> tomcat1容器(提供服务器),通过这个镜像可以创建多个容器(最终服务运行或者项目运行就是在容器中的)

容器(container):

Docker利用容器技术,独立运行一个或者一个组应用,通过镜像来创建的。

启动,停止,删除,基本命令。

目前就是可以把这个容器理解为就是一个简易的linux系统。

仓库(reponsitory):

仓库就是存放镜像的地方。

仓库分为公有仓库和私有仓库。

Docker HUb (默认是国外的)。

阿里云…都有容器服务器(配置镜像加速)。

安装Docker

环境准备

1、会一点点的Linux的基础

2、CentOS 7

3、服务器连接工具

环境查看
在这里插入图片描述
在这里插入图片描述

安装

帮助文档

# 1、先卸载原Docker
yum remove docker \
                  docker-client \
                  docker-client-latest \
                  docker-common \
                  docker-latest \
                  docker-latest-logrotate \
                  docker-logrotate \
                  docker-engine
# 2、需要的安装包
yum install -y yum-utils
# 3、设置镜像的仓库
yum-config-manager \
    --add-repo \
    https://download.docker.com/linux/centos/docker-ce.repo # 官方默认,不推荐使用
    
yum-config-manager \
    --add-repo \
    http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo # 阿里云,推荐使用

# 更新 yum 软件包索引(最好)
yum makecache fast

# 4、安装 docker docker-ce 社区    ee 企业版
yum install docker-ce docker-ce-cli containerd.io

# 5、启动 docker
systemctl start docker
# 6、使用docker version 查看版本号
docker version
# 7、测试 hello word
docker run hello-world
# 7.1(附加)、如果报错 error pulling image configuration: (时间不同步)
执行一下命令:ntpdate time.windows.com
如遇到命令不存在: yum -y install netdate

在这里插入图片描述

# 查看镜像
[root@VM-0-4-centos /]# docker images
REPOSITORY    TAG       IMAGE ID       CREATED         SIZE
hello-world   latest    bf756fb1ae65   13 months ago   13.3kB
# 卸载docker
yum remove docker-ce docker-ce-cli containerd.io # 删除依赖
rm -rf /var/lib/docker  # 删除目录

配置阿里云镜像加速

https://cr.console.aliyun.com/cn-hangzhou/instances/mirrors

sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
  "registry-mirrors": ["https://2lrywg60.mirror.aliyuncs.com"]
}
EOF
sudo systemctl daemon-reload 
sudo systemctl restart docker

Run 的流程

在这里插入图片描述

底层原理

Docker是怎么工作的?

Docker是一个 Client - Server 结构的系统,Docker的守护进程运行在主机上,通过 Socket 从客户端访问。

DockerServer 接收到 Docker-Clinet 的指令,就会执行这个命令。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-P4uV0ME0-1613350683831)(images\docker是怎么样工作的.png)]

Docker为什么比 VM 快?

1、Docker有着比虚拟机更少的抽象层。

2、Docker 利用的是宿主机的内核,vm需要的是 Guest OS。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sPQ6UWHN-1613350683833)(images\Snipaste_2021-02-09_21-04-24.png)]

所以说,新建一个容器的时候,doker不需要像虚拟机一样重新加载一个操作系统内核,避免引导。虚拟机是加载 Guest OS,分钟级别的。而 Docker 是利用宿主机的操作系统,省略了这个复杂的过程,秒级。

学习完毕所有命令,再回顾理论,就会很清晰。

Docker的常用命令

帮助命令

docker version    # 查看版本信息
docker info 	  # 显示docker的系统信息,包括镜像和容器的数量
docker 命令 --help # 帮助命令

帮助文档的地址:https://docs.docker.com/reference/

镜像命令

docker images : 查看所有镜像

[root@VM-0-4-centos ~]# docker images
REPOSITORY    TAG       IMAGE ID       CREATED         SIZE
hello-world   latest    bf756fb1ae65   13 months ago   13.3kB

REPOSITORY: 镜像的仓库源
TAG:		镜像的标签
IMAGE ID:	镜像的ID
CREATED:	镜像的创建时间
SIZE:		镜像的大小

# 可选项
-a, --all     # 列出所有镜像
-q, --quiet   # 只显示镜像id

docker searsh 搜索镜像

#docker search mysql
NAME                              DESCRIPTION                                     STARS     OFFICIAL   AUTOMATED
mysql                             MySQL is a widely used, open-source relation…   10475
mariadb                           MariaDB is a community-developed fork of MyS…   3896
mysql/mysql-server                Optimized MySQL Server Docker images. Create…   769  
percona                           Percona Server is a fork of the MySQL relati…   526 
centos/mysql-57-centos7           MySQL 5.7 SQL database server                   87        

# 可选项
-filter=STARS=3000  #搜索出来的镜像就是 stars 大于 3000
[root@VM-0-4-centos ~]# docker search mysql --filter=STARS=3000
NAME      DESCRIPTION                                     STARS     OFFICIAL   AUTOMATED
mysql     MySQL is a widely used, open-source relation…   10475     [OK]       
mariadb   MariaDB is a community-developed fork of MyS…   3896      [OK]   

docker pull 下载镜像

# 下载镜像 docker pull 镜像名 [:tag]
[root@VM-0-4-centos ~]# docker pull mysql
Using default tag: latest # 如果不写 tag,默认就是latest
latest: Pulling from library/mysql
a076a628af6f: Pull complete  # 分层下载,docker image 的核心,联合核减系统
f6c208f3f991: Pull complete 
88a9455a9165: Pull complete 
406c9b8427c6: Pull complete 
7c88599c0b25: Pull complete 
Digest: sha256:feada149cb8ff54eade1336da7c1d080c4a1c7ed82b5e320efb5beebed85ae8c # 签名
Status: Downloaded newer image for mysql:latest
docker.io/library/mysql:latest # 真实地址

# 等价于它
docker pull mysql
Status: Downloaded newer image for mysql:latest

# 制定版本下载
[root@VM-0-4-centos ~]# docker pull mysql:5.7
5.7: Pulling from library/mysql
# Already exists  已经存在的可以共用(牛逼)
a076a628af6f: Already exists
1831ac1245f4: Pull complete 
Digest: sha256:b3d1eff023f698cd433695c9506171f0d08a8f92a0c8063c1a4d9db9a55808df
Status: Downloaded newer image for mysql:5.7
docker.io/library/mysql:5.7

# 查看所有下载好的镜像
[root@VM-0-4-centos ~]# docker images
REPOSITORY    TAG       IMAGE ID       CREATED         SIZE
mysql         5.7       a70d36bc331a   3 weeks ago     449MB
mysql         latest    c8562eaf9d81   3 weeks ago     546MB
hello-world   latest    bf756fb1ae65   13 months ago   13.3kB

docker rmi 删除镜像

# docker rmi 镜像id  根据id删除镜像
# docker rmi 镜像id 镜像id  根据多个id删除镜像
[root@VM-0-4-centos ~]# docker rmi c8562eaf9d81
Untagged: mysql:latest
Untagged: mysql@sha256:feada149cb8ff54eade1336da7c1d080c4a1c7ed82b5e320efb5beebed85ae8c

# 骚操作 递归删除所有镜像
# docker rmi -f $(docker images -aq)
Untagged: mysql:5.7
Untagged: mysql@sha256:b3d1eff023f698cd433695c9506171f0d08a8f92a0c8063c1a4d9db9a55808df
Deleted: sha256:a70d36bc331a13d297f882d3d63137d24b804f29fa67158c40ad91d5050c39c5
Untagged: hello-world:latest
Untagged: hello-world@sha256:31b9c7d48790f0d8c50ab433d9c3b7e17666d6993084c002c2ff1ca09b96391d
Deleted: sha256:bf756fb1ae65adf866bd8c456593cd24beb6a0a061dedf42b26a993176745f6b

容器命令

说明:我们有了镜像才可以创建容器,Linux,下载一个 centos 镜像来学习。

docker pull centos

新建容器并启动

docker run [可选参数] image

# 参数说明
--name="Name"  	容器名字,tomcat01  tomcat02 用来区分容器
-d     			后台方式运行
-it				使用交互方式运行,进入容器查看内容
-p				制定容器端口 -p 8080:8080
		-p ip 主机端口:容器端口
		-p 主机端口:容器端口(常用)
		-p 容器端口
		容器端口
-P				随机制定端口

# 测试,启动并进入容器
[root@VM-0-4-centos ~]# docker run -it centos /bin/bash
[root@911c6a4376a6 /]# ls # 查看容器内的 centos 很多命令都是不完善的
bin  dev  etc  home  lib  lib64  lost+found  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var
# 从容器中退回主机
[root@911c6a4376a6 /]# exit
exit

列出所有运行中的容器

[root@VM-0-4-centos ~]# docker ps
-a		# 列出当前正在运行中的容器 + 带出历史运行过的容器
-n=?	# 显示最新创建的容器
-q		# 只显示容器的编号
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES
[root@VM-0-4-centos ~]# docker ps -a
CONTAINER ID   IMAGE          COMMAND       CREATED              STATUS                      PORTS     NAMES
911c6a4376a6   centos         "/bin/bash"   About a minute ago   Exited (0) 58 seconds ago             frosty_mclaren
c53e78ce61bd   bf756fb1ae65   "/hello"      12 hours ago         Exited (0) 12 hours ago               thirsty_fermat

退出容器

exit  # 直接容器停止并退出
Ctrl + P + Q # 退出不停止

删除容器

docker rm 容器id	# 删除制定容器,不能删除正在运行的容器,如果要强制删除就 rm -f
docker rm -f $(docker ps -aq)	  # 递归删除所有容器
docker ps -a -q|xargs docker rm   # 删除所有容器

启动/停止容器操作

docker start 容器id   # 启动容器
docker restart 容器id		# 重启容器
docker stop 容器id   	# 停止正在运行容器
docker kill 容器id    # 强制停止当前容器

常用其他命令

后台启动容器

# 命令 docker run -d 镜像名字
[root@VM-0-4-centos ~]# docker run -d centos
7fc42aaf4d5d2a7c9713639b47a9711ae9c8bc4f79e37a087676e2c5b6bbc5f7
# 问题 docker ps,发现centos 停止了
# 常见的坑 docker 容器使用后台运行,就必须要有个前台进程,docker 发现没有应用,就会自动停止
# nginx 容器启动后,发现自己没有提供服务,就会立刻停止,就是没有程序另了

查看日志

docker logs -f -t --tail number(条数) 容器

# 编写一个shell脚本模拟日志
[root@VM-0-4-centos ~]# docker run -d centos /bin/sh -c "while true;do eche dengxian;sleep 1;done"
 
# 显示日志
-tf			# 显示日志
--tail number(显示日志条数)
[root@VM-0-4-centos ~]# docker logs -tf --tail 2  12057bc3ef72
2021-02-10T02:01:53.559123855Z /bin/sh: dengxian
2021-02-10T02:01:54.561216524Z /bin/sh: dengxian

查看容器中进程信息

# 命令 docker top 容器id
[root@VM-0-4-centos ~]# docker top 12057bc3ef72
UID                 PID                 PPID                C                   STIME               TTY                 TIME                CMD
root                18700               18682               0                   09:59              
root                20286               18700               0                   10:03  /usr/bin/sleep 1

查看镜像元数据

#docker inspect 容器id

# 测试
[root@VM-0-4-centos ~]# docker inspect 12057bc3ef72
[
   {
       "Id": "12057bc3ef72faa754b53de68c279fd4cd1f5248002ee3b25d37f80447ecb164",
       "Created": "2021-02-10T01:59:02.74332898Z",
       "Path": "/bin/sh",
       "Args": [
           "-c",
           "while true;do eche dengxian;sleep 1;done"
       ],
       "State": {
           "Status": "running",
           "Running": true,
           "Paused": false,
           "Restarting": false,
           "OOMKilled": false,
           "Dead": false,
           "Pid": 18700,
           "ExitCode": 0,
           "Error": "",
           "StartedAt": "2021-02-10T01:59:03.117114088Z",
           "FinishedAt": "0001-01-01T00:00:00Z"
       },
       .......

进入当前正在运行的容器

# docker exec -it 容器id /bin/bash
[root@VM-0-4-centos ~]# docker exec -it 12057bc3ef72 /bin/bash
[root@12057bc3ef72 /]# ls
bin  dev  etc  home  lib  lib64  lost+found  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var
[root@12057bc3ef72 /]# ps -ef
UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 01:59 ?        00:00:00 /bin/sh -c while true;do eche dengxian;sleep 1;done
root      1803     0  0 02:14 pts/0    00:00:00 /bin/bash
root      1829     1  0 02:14 ?        00:00:00 /usr/bin/coreutils --coreutils-prog-shebang=sleep /usr/bin/sleep 1
root      1830  1803  0 02:14 pts/0    00:00:00 ps -ef

# 方式二 
docker attach 容器id
[root@VM-0-4-centos ~]# docker attach 12057bc3ef72
正在执行当前的代码...

# docker exec	# 进入容器后开启一个新的终端,可以在里面操作
# docker attch	# 进入容器正在执行的终端,不会启动新的进程!

从容器内拷贝文件到主机中

[root@VM-0-4-centos ~]# docker attach 9d01abc47f10
[root@9d01abc47f10 /]# cd home    
[root@9d01abc47f10 home]# touch test.java
# 退出容器
[root@VM-0-4-centos ~]# docker cp 9d01abc47f10:/home/test.java /home
[root@VM-0-4-centos ~]# cd /
[root@VM-0-4-centos /]# cd home
[root@VM-0-4-centos home]# ls 
test.java  www

# 拷贝是一个手动的过程,之后可以使用 -v 数据卷的技术,可以实现自动拷贝

小结

在这里插入图片描述

练习

docker 安装nginx

# 1、建议先 docker search 搜索nginx
# 2、docker pull nginx  安装
# 3、 启动:docker
[root@VM-0-4-centos ~]# docker run -d --name nginx01 -p 3344:80 nginx
faa1cee0f25551006d6cb3661d14d16daef327ba9b4b43ea04e5e1a19a4d5062

在这里插入图片描述

可视化

protainer(玩一玩)

[root@VM-0-4-centos ~]# docker run -d -p 9000:9000 -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data portainer/portainer
Unable to find image 'portainer/portainer:latest' locally
latest: Pulling from portainer/portainer
d1e017099d17: Pull complete 
717377b83d5c: Pull complete 
Digest: sha256:f8c2b0a9ca640edf508a8a0830cf1963a1e0d2fd9936a64104b3f658e120b868
Status: Downloaded newer image for portainer/portainer:latest
83f7c54a6ccaa79acb607d980ee4f4330eb2fbef8019b4418213c99cc83ffb8d

Docker镜像讲解

镜像是什么

镜像是一种轻量级、可执行的独立软件包,用来打包软件运行环境和基于运行环境开发的软件,它包含运行某个软甲你所需的所有内容,包括代码、运行时、库、环境变量和配置文件

所有的应用,直接打包 docker 镜像,就可以直接跑起来。

如何得到镜像:

  • 远程仓库下载
  • 拷贝
  • 自己制作一个镜像 Docker File

Docker 镜像加载原理

UnionFS(联合文件系统)

UnionFS(联合文件系统):Union文件系统(UnionFS) 是一种分层、轻量级并且高性能的文件系统,他支持对文件系统的修改作为一次提交来一层层叠加,同事可以将不同目录挂在到同一虚拟文件系统下(Unite Several Directories Into A Single Virtual fileSystem)。Union文件系统是 Docker 镜像的基础,镜像可以通过分层来进行继承,基于基础镜像(没有父镜像)可以制作具体的应用镜像。

特性:一次同时加载多个文件系统,但从外面看起来,只能看到一个文件系统,联合加载会把各层文件叠加起来,这样最终的文件系统会包含所有底层的文件和目录。

Docker 镜像加载原理

docker的镜像实际上由一层一层的文件系统组成,这种层级的文件系统UnionFS.

Bootfs(Boot File System)主要包含 BootLoaderkernel, BootLoader主要是引导加载 Kernel, Linux刚启动时会加载bootfs文件系统,在Docker镜像的最底层是boots。这一层与我们典型的Linux/Unix系统是一样的,包含boot加载器和内核。当boot加载完成之后整个内核就都在内存中了,此时内存的使用权已由bootfs转交给内核,此时系统也会卸载bootfs,
RootFs (Root File System),在bootfs之上。包含的就是典型Linux.系统中的 /dev, /proc, /bin, /etc 等标准目录和文件。roots就是各种不同的操作系统发行版,比如Ubuntu , Centos等等。
在这里插入图片描述
平时安装进虚拟机的 CentOS 都是好几G,为什么Docker这里才 200M!
在这里插入图片描述

对于一个精简的 OS,rootfs可以很小,只需要包括最基本的命令,工具和程序库就可以了,因为底层直接用Host 的 Karnel,自己只需要提供 bootfs 就可以了,由此可见对于不同的 Linux 发行版,rootfs 基本是一致的,rootfs会有差别,因此不同的发行分版可以共用 BootFS。

虚拟机是分钟级,容器是秒级。

分层理解

分层的镜像

下载一个镜像,注意观察下载的日志输出,可以看出是一层一层的在下载!
在这里插入图片描述

为什么Docker 镜像采用这种分层的结构呢?

最大的好处:资源共享,比如有多个镜像都从相同的 Base镜像构建而来,那么宿主机只需要在磁盘上保留一份 Base镜像,同事内存中也只需要加载一份Base 镜像,这样就可以为所有的容器服务了,而且镜像的每一层都可以被共享。

查看镜像分层的方式可以通过 docker images inspect 命令

[root@VM-0-4-centos ~]# docker image inspect redis:latest
[
	// .....
    "RootFS": {
        "Type": "layers",
        "Layers": [
        "sha256: cb42413394c4059335228c137fe884ff3ab8946a014014309676c25e3ac86864",
        "sha256: 8e14cb7841faede6e42ab797f915c329c22f3b39026f8338c4c75de26e5d4e82",
        "sha256: 1450b8f0019c829e638ab5c1f3c2674d117517669e41dd2d0409a668e0807e96",
        "sha256: f927192cc30cb53065dc266f78ff12dc06651d6eb84088e82be2d98ac47d42a0",
        "sha256: a24a292d018421783c491bc72f6601908cb844b17427bac92f0a22f5fd809665",
        "sha256: 3480f9cdd491225670e9899786128ffe47054b0a5d54c48f6b10623d2f340632"
        ]
    }
]

理解:

所有的Docker|镜像都起始于一个基础镜像层,当进行修改或增加新的内容时,就会在当前镜像层之上,创建新的镜像层。
举一个简单的例子,假如基于Ubuntu Linux 16.04创建一个新的镜像,这就是新镜像的第一层;如果在该镜像中添加Python包,就会在基础镜像层之上创建第二个镜像层; 如果继续添加一个安全补丁,就会创建第三个镜像层.
该镜像当前已经包含3个镜像层,如下图所示(这只是一个用于演示的很简单的例子)。
在这里插入图片描述

在添加额外的镜像层的同时,镜像始终保持是当前所有镜像的组合,理解这一点非常重要,下图举了一个简单的例子,每个镜像层包含三个文件,而镜像包含了来自两个镜像层的六个文件。
在这里插入图片描述

上图中的镜像层跟之前图中的略有区别,主要目的是便于展示文件。
下图中展示了一个稍微复杂的三层镜像,在外部看来整个镜像只有6个文件,这是因为最上层中的文件7是文件5的一个更新版本。
在这里插入图片描述
这种情况下,上层镜像层中的文件覆盖了底层镜像层中的文件。这样就使得文件的更新版本作为一个新镜像层添加到镜像当中。Docker通过存储引擎(新版本采用快照机制)的方式来实现镜像层堆栈,并保证多镜像层对外展示为统一的文件系统。
Linux上可用的存储引擎有 AUFS、Overlay2、Device Mapper、Btrfs以及ZFS。顾名思义,每种存储引擎都基于Linux 中对应的文件系统或者块设备技术,并且每种存储引擎都有其独有的性能特点。
在这里插入图片描述

特点

Docker镜像都是只读的,当容器启动时,一个新的可读写层被加载到镜像的顶部!

这一层就是我们通常说的容器层,容器之下的都叫镜像层!
在这里插入图片描述

如何提交一个自己的镜像

commit 镜像

docker commit 提交容器成为一个新的副本

# 命令·和 git 类似
docker commit -m="描述信息" -a="作者" 容器id  目标镜像名:[TAG]

实战测试

# 1. 启动一个默认的 tomcat

# 2. 发现默认的 tomcat 没有 webapps 应用,镜像的原因,官方镜像默认 webapps 下面是没有文件的!

# 3. 自己拷贝进去了基本的文件

# 4. 将我们操作过的容器通过commit提交成一个镜像! 我们以后就使用我们修改过的镜像即可,这就是我们自己的一个修改的镜像(后面有进阶,这个暂时提交到本地Linux)
[root@VM-0-4-centos ~]# docker commit -a="VisDeng" -m="add webapps init" 09036ad3559a tomcat01:1.0
sha256:1820f42c325d0237d2e11d84c8823ac21458514785c6e0bec431b1f856672978
[root@VM-0-4-centos ~]# docker images
REPOSITORY            TAG       IMAGE ID       CREATED         SIZE
tomcat01              1.0       1820f42c325d   4 seconds ago   654MB
tomcat                latest    040bdb29ab37   4 weeks ago     649MB

如果想要保存当前容器的状态,就可以通过 commit 提交,获得一个镜像。

就好比 VM 的快照!

容器数据卷

什么是容器数据卷?

Docker的理念回顾

将应用和环境打包成一个镜像!

如果数据都在容器中,那么我们将容器删除,数据就会丢失!数据可以持久化

MySQL,容器删了,删除跑路! MySQL数据可以存储在本地!

容器之间可以有一个数据共享的技术!Docker 容器中产生的数据,同步到本地!

这就是卷技术!

简单来说就是:目录的挂载,将我们容器内的目录,挂载到 Linux 上!
在这里插入图片描述

总结:容器的持久化和同步操作!容器间也是可以数据共享的!

使用数据卷

方式一:直接使用命令 -v

# 1、启动 CentOS,设置 需要自动同步的目录 (图一)
[root@VM-0-4-centos ~]# docker run -it -v /home/ceshi:/home centos /bin/bash

# 2、home下自动创建了 ceshi 的文件夹
[root@VM-0-4-centos home]# ls
ceshi   www

# 3、 进入CentOS容器中的 home 目录,创建test.txt 文件
[root@VM-0-4-centos ceshi]# docker exec -it 3625225fcd7c /bin/bash
[root@3625225fcd7c /]# cd home
[root@3625225fcd7c home]# touch test.txt

# 4、退出容器,进入本机home/ceshi 目录
[root@VM-0-4-centos home]# cd ceshi
[root@VM-0-4-centos ceshi]# ls # 文件自动同步了过来
test.txt

# 也可以修改外部文件,容器内也会同步,类似于 Vue 的双向绑定

在这里插入图片描述

好处:我们以后只需要本地修改即可,容器内会自动同步!

实战:安装MySQL 并且同步数据 (待更新)

MySQL的数据持久化问题!

DockerFile

Docker 网络

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值