Docker总结记录

目录

1:架构

2:理解容器

3:云服务器安装Docker

4:命令操作

4.1:下载镜像:docker  pull

4.2:启动容器:docker  run

4.3:文件映射 (目录挂载和卷映射)

4.3.1:前言(修改页面):exec进入容器直接修改

4.3.2:目录挂载

4.3.3:卷映射

4.4:自定义网络

4.5:针对修改,生成镜像和保存镜像文件

4.6:分享到hub.docker

4.7:容器一键启动 docker compose

4.7.1:前言

4.7.2:docker compose:利用yaml快速启动容器

4.8:镜像快速制作 dockerfile

4.9:其他命令

5:问题


1:架构

1.1、docker客户端:操作docker主机的一端,通过发送不同命令给docker主机的进程Docker Daemon。

1.2、Docker主机:安装了Docker环境的就是Docker主机,只要安装了Docker就会有一个Docker Daemon进程一直在后台运行,用来处理docker客户端发来的请求。

1.3、应用市场:将所有应用都放在应用市场里(mysql、redis等等)。

------------------------------------------------------------------

docker pull redis 我想要获取redis的应用,那就需要在客户端发送命令docker pull redis,到Docker主机中的进程Docker Daemon,然后这个进程就会向应用市场拉取镜像,放到docker主机中。

docker run redis:想启动这个应用,他会优先在Docker主机中寻找这个镜像,然后根据这个镜像启动一个应用(如果在Docker主机找不到镜像,他会到应用市场下载一个镜像到Docker主机中,然后再根据镜像启动应用·)。

启动的这个应用,称为容器。我们可以启动多个应用(容器),每个容器就是运行中的应用

docker build xxx:自己想制作镜像,之前都是通过应用市场拉取公共镜像,如果我们想自己制作镜像,那我们就需要通过docker build xxx命令发送给Docker Daemon,然后Docker Daemon进程就会帮我制造一个镜像到docker主机中。

docker push xxx:如果我们希望把我们制作的镜像交给应用市场保存,那就可以docker push xxx,这样其他人(张三、李四、王五就可以获取到这个应用了)

build:构建自己的应用

分享:自己build-自己push-别人pull

运行:run

2:理解容器

传统部署:他们共享一个环境,如果其中一个应用出现问题比如出现内存泄漏,那这个应用就会无限占用空间,从而导致其他应用受到牵连。

虚拟化部署:开几个虚拟机,互相不影响,但是缺点是虚拟机会有完整的操作系统,从而比较笨重。

容器部署:同虚拟化部署一样具有隔离机制,每个应用可以共享操作系统内核(但是文件系统、cpu、内存、进程都是隔离的),可以看做轻量化的虚拟机

3:云服务器安装Docker

 先购买云服务器

Docker: Accelerated Container Application Development

安装docker

官网安装介绍CentOS 操作系统 |Docker 文档

如果是香港服务器,就完全按照官网命令来最好,不需要配镜像这些

页面要选择好我们安装的类型,因为我们订购的云服务器是centos所以这里也选centos

官网网站上有安装步骤介绍,复制官网提供的命令即可

卸载旧版本docker

sudo yum remove docker \
                  docker-client \
                  docker-client-latest \
                  docker-common \
                  docker-latest \
                  docker-latest-logrotate \
                  docker-logrotate \
                  docker-engine

配置下载源(这里用的是阿里的镜像,因为官网的有点慢)

sudo yum install -y yum-utils
sudo yum-config-manager \
--add-repo \
http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo

安装docker

sudo yum install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

启动docker

sudo systemctl start docker

启动完事之后我们要再输入一个命令(必要配置,非官网)

systemctl enable docker

配置镜像源:因为docker默认会从dockerhub下载比较慢,所以可以配置镜像(必要配置,非官网)

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


查看docker中正在运行的应用(可以发现没有任何应用运行,因为刚开始)

docker ps

不知道命令:可以在后面加  --help他会有说明,比如docker  run  --help

4:命令操作

1、镜像操作

实验要求:启动一个nginx,并将它的首页改为自己的页面,发布出去,让所有人都可以使用。

4.1:下载镜像:docker  pull

 检索:docker search  nginx

下载:docker  pull  ngnix这种下载方式默认是下载最新版

如果我们想下载指定版本,到hubdocker.com去找指定版本docker  pull  ngnix:1.26.0

列表:docker images  (可以查看到我们所下载的所有镜像)

删除:docker rmi   【镜像名字】 (删除某一个镜像)

4.2:启动容器:docker  run

运行:docker  run  ngnix(本地执行 docker run nginx 命令如果本地环境中没有名为 nginx 的镜像,那么 Docker会自动尝试从Docker Hub拉取这个镜像)

运行后就会看到如下图,不要关闭,关闭的话服务就会停止了。复制一个会话。

复制一个会话。进行另外操作

查看:docker  ps(查看正在运行的容器)--docker  run  ngnix会给这个容器起随机名字name

这样要一直挂着不太方便,所以可以用其他的run启动(图中写错了,前面是外部端口后面是内部)

docker run -d --name mynginx-p 80:80 nginx

解释:
        -d  后台启动,这样就不会一直占着。

        --name  myngin  自定在容器的名字,如果不自定义就会随机起个容器名

          -p  80:80   端口映射,如果不设置端口映射,我们在其他电脑访问不到。这个就相当于docker对外暴露一个外部端口,然后这个外部端口再映射到内部。
         -e环境变量,在hub.docker拉完镜像启动后,要看他的文档,启动要不要加参数

根据公网ip访问下启动的nginx容器,发现可以访问到,因为我们开启了端口映射。

4.3:文件映射 (目录挂载和卷映射)

4.3.1:前言(修改页面):exec进入容器直接修改

我们想修改,访问nginx的访问页面怎么办?

1:先进入容器文件系统,然后修改nginx所在的html文件

进入:docker  exec

容器就相当于是一个linux文件系统,我们操作这个linux系统怎么办?

用这个:docker exec -it mynginx /bin/bash

(进入容器就用不了docker命令了,docker命令只有在外部用,如果想从容器退出到外部可以用ctrl+D)
先进入cd  /usr/share/nginx/html/   然后对这个html修改下就可以了

4.3.2:目录挂载

把内部文件和主机外部文件进行映射,修改外部文件就会修改到内部文件,这样就不用进入内部了。

上面这个做法有点麻烦,因为我们每次想要修改都要进入容器内部有点麻烦

docker run -d -p 80:80 -v /app/nghtml:/usr/share/nginx/html --name app01 nginx

访问公网,就发现首页变成了刚刚设置的2222

内部文件和映射的外部文件,是双向映射的,修改外部影响内部,修改内部影响外部。

4.3.3:卷映射

3:卷映射:上面那种方式他会自动帮我们创建文件夹但是不会创建文件,现在我希望他一开始就帮我创建一个一模一样内容的文件,如果想改变再去修改这个映射文件。

docker run -d -p 99:80 -v nghtml:/usr/share/nginx/html --name app02 nginx
生成的卷固定的位置是  /var/lib/docker/volumes/<volume-name>

如上图我修改了外部文件的index.html即卷文件

现在我进入容器内部,发现内部文件也被随着修改了

然后访问试试:http://150.109.147.146:99/--------发现样式改了

4.4:自定义网络

容器之间的访问:每启动一个容器都会为这个容器分配唯一id,进入一个容器使用另一个的(容器ip+容器端口)可以进行访问。缺点:ip由于各种原因发生变化,所以用域名。

创建自定义网络,容器名就是稳定域名。

docker network create mynet
启动两个容器(要使用刚刚自定义的网络--network mynet)
docker run -d --name app01 --network mynet -p 80:80 nginx
docker run -d --name app02 --network mynet -p 99:80 nginx

进入容器01
docker exec -it app1 bash
容器01调用容器02

curl  http://app02:80

4.5:针对修改,生成镜像和保存镜像文件

对镜像进行了修改,然后再生成一个镜像
docker commit -m"updatehtml" (生成一个新镜像,-m后面是注释)

保存文件:把上面保存的镜像,保存为一个文件(这里为tar)

别人想通过镜像文件获取镜像:

我们可以把这个tar文件发给别人,然后别人有了这个tar文件后可以通过docker load -i mynginx.tar把这个镜像加载过来

4.6:分享到hub.docker

Docker 主页

先网页注册账号,244.***@qq.com,密码 65...t

输入docker login  再依次输入注册过的账号密码

docker tag mynginx:v1.0 1217544/mynginx:v1.0 (mynginx:v1.0是本地就有的镜像,1217544/mynginx:v1.0是用户名+准备腿给远程起的名字)

docker push 1217544/mynginx:v1.0      (推送给远程,后面这个是刚刚起的名字)

个人资料里就有了刚刚推上来的镜像

如果别人想拉,就搜索这个镜像就好了。docker pull 1217544/mynginx:v1.0

4.7:容器一键启动 docker compose

4.7.1:前言

拉别人镜像,然后run。只需要关注端口映射、配置文件、环境变量

docker network create blog

(记住要先部署mysql、然后在部署项目)

docker run -d -p 8080:80 -e WORDPRESS_DB_HOST=mysql -e WORDPRESS_DB_USER=root -e WORDPRESS_DB_PASSWORD=123456 -e WORDPRESS_DB_NAME=wordpress -v wordpress:/var/www/html --restart always --name wordpress-app --network blog wordpress:latest

docker run -d \ --name mysql \ --network blog \ -p 3306:3306 \ -e MYSQL_ROOT_PASSWORD=123456 \ -e MYSQL_DATABASE=wordpress \ -v mysql-data:/var/lib/mysql \ -v /app/myconf:/etc/mysql/conf.d \ --restart always \ mysql:8.0

以上是部署了wordpress博客系统项目,和mysql环境。

但是缺点:如果换个机器,还想想要再部署这两个,还要重新写这两条命令麻烦。

4.7.2:docker compose:利用yaml快速启动容器

        如果有多个服务,里面会涉及很多参数,如果对每个服务进行单独run一下就很麻烦。所以我们把多个服务的命令和参数放在yaml配置文件里进行配置,这个yaml文件就相当于多个run命令的集合。然后用docker compose -f compose.yaml up -d就可以一次性启动多个容器了,不需要我们一条一条敲run命令了然后还可以对这个yaml重复利用。

配置文件定义一些顶级元素即可(主要配前4个重要的就行)

yaml配置查参考官网   指定项目名称 |Docker 文档

name: myblog
services:
  mysql:
    container_name: mysql
    image: mysql:8.0
    ports:
     - "3306:3306"
    environment:
     - MYSQL_ROOT_PASSWORD=123456
     - MYSQL_DATABASE=wordpress
    volumes:
     - mysql-data:/var/lib/mysql
     - /app/myconf:/etc/mysql/conf.d
    restart: always
    networks:
     - blog
  wordpress:
    image: wordpress
    ports:
     - "8080:80"
    environment:
     - WORDPRESS_DB_HOST=mysql
     - WORDPRESS_DB_USER=root
     - WORDPRESS_DB_PASSWORD=123456
     - WORDPRESS_DB_NAME=wordpress
    volumes:
     - wordpress:/var/www/html
    networks:
     - blog
    depends_on:
     - mysql

volumes:
  mysql-data:
  wordpress:

networks:
  blog:

利用yaml配置文件启动容器:

docker compose up

上线:(适合首次启动)
docker compose -f compose.yaml up -d 

下线:下线还是会保留卷,如果需要删除卷需要添加参数

docker compose -f compose.yaml down

启动容器

docker-compose start X1 X2 X3

停止容器
docker compose stop X1 X2 X3

效果

4.8:镜像快速制作 dockerfile

我们往常都是通过hub.docker用别人的镜像,如果我们想自己制作镜像怎么办?

我们有一个app.jar,我们要为他编写一个镜像。首先要把这个app.jar上传至服务器,在这个位置编写镜像配置文件:主要包含(基础环境、软件包、启动命令
vim Dockerfile
文件内容是:

FROM openjdk:17

LABEL author=hf

COPY app.jar /app.jar

EXPOSE 8080

ENTRYPOINT ["java","-jar","/app.jar"]

构建镜像:docker build -f Dockerfile -t myjavaapp:1.0 .

docker分层机制:比如有一个镜像,然后再这个镜像的基础上加一些功能,再生成第二个镜像。这两个镜像中都有jdk17,那么这两个镜像会共用一个jdk17,不会生成两个。从而节省资源。

4.9:其他命令

查看:docker  ps -a(查看所有容器,包括已经停止的)

停止:docker  stop

启动:docker  start

重启:docker  restart

状态:docker  stats  + 运行容器ID(查看启动容器的状态)

日志:docker  logs + 运行容器ID

删除:docker  rm+运行容器ID(在删除之前需要停止容器)

删除所有容器:docker rm -f $(docker ps -aq)

5:问题

docker中run和start的区别

docker run 后面跟的是镜像,是利用镜像,生成容器并启动容器。

而docker start后面跟的是容器,他是启动一个之前生成过的容器。

内部外部端口映射,内部端口和外部端口号可以重复吗?

答案:内部端口号可以重复,外部不可以。内部每一个容器都可以有一个相同的端口号,因为每个容器都相当于是隔离的,所以他们有同一个端口号是可以的。外部端口只能有一个。

docker如果是已经运行了,那还能执行命令吗?
可以,
docker exec -it mynginx /bin/bash,每个容器都相当于独立的虚拟机,也就是Linux文件系统,我们想操作这个系统可以通过docker exec执行

docker compose start和docker compose up区别?

docker-compose up是下载并运行,适合首次启动,他的参数是根据配置文件来的。
docker-compose start用于启动已有的容器,如果配置改了,然后又停止,再次启动还是改动后的配置,而不是初始的yaml文件

一键启动超多中间件:

docker compose -f compose.yaml up -d (这个命令就相当于run多个服务,里面的参数是根据官网找的,下载并运行)

name: devsoft
services:
  redis:
    image: bitnami/redis:latest
    restart: always
    container_name: redis
    environment:
      - REDIS_PASSWORD=123456
    ports:
      - '6379:6379'
    volumes:
      - redis-data:/bitnami/redis/data
      - redis-conf:/opt/bitnami/redis/mounted-etc
      - /etc/localtime:/etc/localtime:ro

  mysql:
    image: mysql:8.0.31
    restart: always
    container_name: mysql
    environment:
      - MYSQL_ROOT_PASSWORD=123456
    ports:
      - '3306:3306'
      - '33060:33060'
    volumes:
      - mysql-conf:/etc/mysql/conf.d
      - mysql-data:/var/lib/mysql
      - /etc/localtime:/etc/localtime:ro

  rabbit:
    image: rabbitmq:3-management
    restart: always
    container_name: rabbitmq
    ports:
      - "5672:5672"
      - "15672:15672"
    environment:
      - RABBITMQ_DEFAULT_USER=rabbit
      - RABBITMQ_DEFAULT_PASS=rabbit
      - RABBITMQ_DEFAULT_VHOST=dev
    volumes:
      - rabbit-data:/var/lib/rabbitmq
      - rabbit-app:/etc/rabbitmq
      - /etc/localtime:/etc/localtime:ro
  opensearch-node1:
    image: opensearchproject/opensearch:2.13.0
    container_name: opensearch-node1
    environment:
      - cluster.name=opensearch-cluster # Name the cluster
      - node.name=opensearch-node1 # Name the node that will run in this container
      - discovery.seed_hosts=opensearch-node1,opensearch-node2 # Nodes to look for when discovering the cluster
      - cluster.initial_cluster_manager_nodes=opensearch-node1,opensearch-node2 # Nodes eligibile to serve as cluster manager
      - bootstrap.memory_lock=true # Disable JVM heap memory swapping
      - "OPENSEARCH_JAVA_OPTS=-Xms512m -Xmx512m" # Set min and max JVM heap sizes to at least 50% of system RAM
      - "DISABLE_INSTALL_DEMO_CONFIG=true" # Prevents execution of bundled demo script which installs demo certificates and security configurations to OpenSearch
      - "DISABLE_SECURITY_PLUGIN=true" # Disables Security plugin
    ulimits:
      memlock:
        soft: -1 # Set memlock to unlimited (no soft or hard limit)
        hard: -1
      nofile:
        soft: 65536 # Maximum number of open files for the opensearch user - set to at least 65536
        hard: 65536
    volumes:
      - opensearch-data1:/usr/share/opensearch/data # Creates volume called opensearch-data1 and mounts it to the container
      - /etc/localtime:/etc/localtime:ro
    ports:
      - 9200:9200 # REST API
      - 9600:9600 # Performance Analyzer

  opensearch-node2:
    image: opensearchproject/opensearch:2.13.0
    container_name: opensearch-node2
    environment:
      - cluster.name=opensearch-cluster # Name the cluster
      - node.name=opensearch-node2 # Name the node that will run in this container
      - discovery.seed_hosts=opensearch-node1,opensearch-node2 # Nodes to look for when discovering the cluster
      - cluster.initial_cluster_manager_nodes=opensearch-node1,opensearch-node2 # Nodes eligibile to serve as cluster manager
      - bootstrap.memory_lock=true # Disable JVM heap memory swapping
      - "OPENSEARCH_JAVA_OPTS=-Xms512m -Xmx512m" # Set min and max JVM heap sizes to at least 50% of system RAM
      - "DISABLE_INSTALL_DEMO_CONFIG=true" # Prevents execution of bundled demo script which installs demo certificates and security configurations to OpenSearch
      - "DISABLE_SECURITY_PLUGIN=true" # Disables Security plugin
    ulimits:
      memlock:
        soft: -1 # Set memlock to unlimited (no soft or hard limit)
        hard: -1
      nofile:
        soft: 65536 # Maximum number of open files for the opensearch user - set to at least 65536
        hard: 65536
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - opensearch-data2:/usr/share/opensearch/data # Creates volume called opensearch-data2 and mounts it to the container

  opensearch-dashboards:
    image: opensearchproject/opensearch-dashboards:2.13.0
    container_name: opensearch-dashboards
    ports:
      - 5601:5601 # Map host port 5601 to container port 5601
    expose:
      - "5601" # Expose port 5601 for web access to OpenSearch Dashboards
    environment:
      - 'OPENSEARCH_HOSTS=["http://opensearch-node1:9200","http://opensearch-node2:9200"]'
      - "DISABLE_SECURITY_DASHBOARDS_PLUGIN=true" # disables security dashboards plugin in OpenSearch Dashboards
    volumes:
      - /etc/localtime:/etc/localtime:ro
  zookeeper:
    image: bitnami/zookeeper:3.9
    container_name: zookeeper
    restart: always
    ports:
      - "2181:2181"
    volumes:
      - "zookeeper_data:/bitnami"
      - /etc/localtime:/etc/localtime:ro
    environment:
      - ALLOW_ANONYMOUS_LOGIN=yes

  kafka:
    image: 'bitnami/kafka:3.4'
    container_name: kafka
    restart: always
    hostname: kafka
    ports:
      - '9092:9092'
      - '9094:9094'
    environment:
      - KAFKA_CFG_NODE_ID=0
      - KAFKA_CFG_PROCESS_ROLES=controller,broker
      - KAFKA_CFG_LISTENERS=PLAINTEXT://:9092,CONTROLLER://:9093,EXTERNAL://0.0.0.0:9094
      - KAFKA_CFG_ADVERTISED_LISTENERS=PLAINTEXT://kafka:9092,EXTERNAL://119.45.147.122:9094
      - KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,EXTERNAL:PLAINTEXT,PLAINTEXT:PLAINTEXT
      - KAFKA_CFG_CONTROLLER_QUORUM_VOTERS=0@kafka:9093
      - KAFKA_CFG_CONTROLLER_LISTENER_NAMES=CONTROLLER
      - ALLOW_PLAINTEXT_LISTENER=yes
      - "KAFKA_HEAP_OPTS=-Xmx512m -Xms512m"
    volumes:
      - kafka-conf:/bitnami/kafka/config
      - kafka-data:/bitnami/kafka/data
      - /etc/localtime:/etc/localtime:ro
  kafka-ui:
    container_name: kafka-ui
    image: provectuslabs/kafka-ui:latest
    restart: always
    ports:
      - 8080:8080
    environment:
      DYNAMIC_CONFIG_ENABLED: true
      KAFKA_CLUSTERS_0_NAME: kafka-dev
      KAFKA_CLUSTERS_0_BOOTSTRAPSERVERS: kafka:9092
    volumes:
      - kafkaui-app:/etc/kafkaui
      - /etc/localtime:/etc/localtime:ro

  nacos:
    image: nacos/nacos-server:v2.3.1
    container_name: nacos
    ports:
      - 8848:8848
      - 9848:9848
    environment:
      - PREFER_HOST_MODE=hostname
      - MODE=standalone
      - JVM_XMX=512m
      - JVM_XMS=512m
      - SPRING_DATASOURCE_PLATFORM=mysql
      - MYSQL_SERVICE_HOST=nacos-mysql
      - MYSQL_SERVICE_DB_NAME=nacos_devtest
      - MYSQL_SERVICE_PORT=3306
      - MYSQL_SERVICE_USER=nacos
      - MYSQL_SERVICE_PASSWORD=nacos
      - MYSQL_SERVICE_DB_PARAM=characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
      - NACOS_AUTH_IDENTITY_KEY=2222
      - NACOS_AUTH_IDENTITY_VALUE=2xxx
      - NACOS_AUTH_TOKEN=SecretKey012345678901234567890123456789012345678901234567890123456789
      - NACOS_AUTH_ENABLE=true
    volumes:
      - /app/nacos/standalone-logs/:/home/nacos/logs
      - /etc/localtime:/etc/localtime:ro
    depends_on:
      nacos-mysql:
        condition: service_healthy
  nacos-mysql:
    container_name: nacos-mysql
    build:
      context: .
      dockerfile_inline: |
        FROM mysql:8.0.31
        ADD https://raw.githubusercontent.com/alibaba/nacos/2.3.2/distribution/conf/mysql-schema.sql /docker-entrypoint-initdb.d/nacos-mysql.sql
        RUN chown -R mysql:mysql /docker-entrypoint-initdb.d/nacos-mysql.sql
        EXPOSE 3306
        CMD ["mysqld", "--character-set-server=utf8mb4", "--collation-server=utf8mb4_unicode_ci"]
    image: nacos/mysql:8.0.30
    environment:
      - MYSQL_ROOT_PASSWORD=root
      - MYSQL_DATABASE=nacos_devtest
      - MYSQL_USER=nacos
      - MYSQL_PASSWORD=nacos
      - LANG=C.UTF-8
    volumes:
      - nacos-mysqldata:/var/lib/mysql
      - /etc/localtime:/etc/localtime:ro
    ports:
      - "13306:3306"
    healthcheck:
      test: [ "CMD", "mysqladmin" ,"ping", "-h", "localhost" ]
      interval: 5s
      timeout: 10s
      retries: 10
  prometheus:
    image: prom/prometheus:v2.52.0
    container_name: prometheus
    restart: always
    ports:
      - 9090:9090
    volumes:
      - prometheus-data:/prometheus
      - prometheus-conf:/etc/prometheus
      - /etc/localtime:/etc/localtime:ro

  grafana:
    image: grafana/grafana:10.4.2
    container_name: grafana
    restart: always
    ports:
      - 3000:3000
    volumes:
      - grafana-data:/var/lib/grafana
      - /etc/localtime:/etc/localtime:ro

volumes:
  redis-data:
  redis-conf:
  mysql-conf:
  mysql-data:
  rabbit-data:
  rabbit-app:
  opensearch-data1:
  opensearch-data2:
  nacos-mysqldata:
  zookeeper_data:
  kafka-conf:
  kafka-data:
  kafkaui-app:
  prometheus-data:
  prometheus-conf:
  grafana-data:

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值