Docker容器编排

 前言:本博客仅作记录学习使用,部分图片出自网络,如有侵犯您的权益,请联系删除 

本节目录如下

一、Docker Compose入门

1.1、为什么要使用 Docker Compose 部署容器

1.2、Docker Compose 的项目概念

1.3、Docker Compose 的工作机制

1.4、Docker Compose 的特点

1.5、Docker Compose 的应用场景

1.6、使用 Docker Compose 的基本步骤

二、示例

2.1、安装Docker Compose

2.2、使用 Docker Compose 部署 WordPress

三、编写Compose文件

3.1、YAML文件格式

3.2、Compose文件结构

3.3、服务定义

3.4、网络定义

3.5、卷定义

四、示例

4.1、编写单个服务的Compose文件

4.2、多个服务的Compose文件

五、使用Docker Compose部署和管理应用程序

5.1、Compose命令行格式

5.2、docker-compose build

5.3、docker-compose up

5.4、docker-compose down

5.5、其他常用的docker-compose命令

5.6、docker-compose 的3个命令up、run和start

5.7、Docker Compose的环境变量

5.8、组合使用多个Compose文件

致谢


一、Docker Compose入门

1.1、为什么要使用 Docker Compose 部署容器

  • 微服务架构:适合用容器实现,每个服务由一个容器承载
  • 简化部署:避免使用冗长和复杂的docker命令
  • 统一管理:Docker Compose 通过声明式配置文件统一管理多容器应用程序

1.2、Docker Compose 的项目概念

  • 项目:表示一个应用程序,涵盖所有资源,由一组关联容器组成
  • 服务:表示子应用程序运行一个镜像定义容器运行的镜像和参数
  • 容器:服务的副本,可运行多个实例以增减服务数量

1.3、Docker Compose 的工作机制

  • Compose 文件:YAML 格式,定义多容器应用
  • docker-compose 命令处理 Compose 文件,基于 Docker 引擎部署应用
  • docker-py:Python 软件,调用 Docker API 管理容器

1.4、Docker Compose 的特点

  • 隔离环境:使用项目名称隔离,适用于开发、CI 服务器等
  • 保留卷数据:确保卷中数据不丢失
  • 仅重建更改的容器快速更改环境,提高效率
  • 环境定制:支持变量,为不同环境定制编排

1.5、Docker Compose 的应用场景

  • 软件开发环境:创建隔离环境,记录配置服务依赖
  • 自动化测试环境:创建隔离测试环境,快速启动和销毁
     docker-compose up -d        # 启动应用程序
     ./run_tests                 # 运行测试
     docker-compose down         # 停止应用程序并删除相关的资源
  • 单主机部署:部署到远程 Docker 引擎,如 Docker Machine 或集群

1.6、使用 Docker Compose 的基本步骤

  1. 定义环境:使用Dockerfile定义应用程序环境
  2. 定义服务:使用Compose文件声明应用程序的启动配置
  3. 启动应用:执行 docker-compose up启动所有容器

二、示例

2.1、安装Docker Compose

本机为Docker-ce包安装,已经安装好了。下面讲其他安装方式:

Docker Compose 有多种安装方式,推荐从 GitHub 仓库下载二进制文件进行安装。

步骤 1: 下载二进制文件

使用 curl命令从 GitHub 下载 Docker Compose 的二进制文件:

 sudo curl -L "https://github.com/docker/compose/releases/download/1.26.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose

步骤 2: 添加可执行权限

为下载的二进制文件添加执行权限:

 chmod +x /usr/local/bin/docker-compose

步骤 3: 测试安装

检查 Docker Compose 是否安装成功:

 docker-compose --version

如果需要卸载 Docker Compose,执行以下命令:

 rm /usr/local/bin/docker-compose

2.2、使用 Docker Compose 部署 WordPress

2.2.1、 定义项目

(1)创建项目目录并进入 :

 mkdir my_wordpress && cd my_wordpress

(2)在项目目录下创建docker-compose.yml文件,并定义服务:

 version: '3.3'
 services:
   db:
     image: mysql:5.7
     volumes:
       - db_data:/var/lib/mysql
     restart: always
     environment:
       MYSQL_ROOT_PASSWORD: somewordpress
       MYSQL_DATABASE: wordpress
       MYSQL_USER: wordpress
       MYSQL_PASSWORD: wordpress
 ​
   wordpress:
     depends_on:
       - db
     image: wordpress:latest
     ports:
       - "8000:80"
     restart: always
     environment:
       WORDPRESS_DB_HOST: db:3306
       WORDPRESS_DB_USER: wordpress
       WORDPRESS_DB_PASSWORD: wordpress
       WORDPRESS_DB_NAME: wordpress
     volumes:
       - db_data:/var/lib/mysql
 ​
 volumes:
   db_data:

2.2.2、构建项目

在项目目录下执行以下命令以启动服务:

 docker-compose up -d

2.2.3、访问 WordPress

WordPress 将在 Docker主机的8000 端口上运行,可以通过浏览器访问并完成安装。

2.2.4、关闭和清理

关闭并删除容器和网络 ,同时保留卷中的数据:

 # 删除容器和网络
 docker-compose down
 ​
 # 如果要同时删除卷,执行:
 docker-compose down --volumes

三、编写Compose文件

Compose是Docker Compose项目的配置文件,又称Compose模板文件。

3.1、YAML文件格式

3.1.1、特点

YAML是一种数据序列化格式,易于阅读和使用 ,尤其适合数据。有以下特点:

  • 大小写敏感
  • 使用缩进表示层级关系
  • 缩进只能使用空格 ,不能使用tab,不要求空格个数,但相同层级应当左对齐
  • 使用符号“#表示注释,YAML中只有行注释
  • 字符串可以不用引号标注
  • 每个冒号与后面所跟的参数之间都需要一个空格

3.1.2、数据分类

  • 标量 标量相当于常量,是YAML数据的最小单位,不可再分隔。YAML支持整数、浮点数、字符串、NULL、日期、布尔值和时间等多种标量类型
  • 序列 序列就是列表,相当于数组,使用一个短横线加一个空格表示一个序列项,实际上就是一种字典格式:

     - "3000"
     - "8000"
     ​
     # 流式语法:
     ["3000","8000"]
  • 映射 映射相当于JSON中的对象,也使用键值对表示,只是冒号后一定要加一个空格,同一层缩进层次的所有键值对属于一个映射

     PACK_ENV: development
     SHOW: 'true'
     ​
     # 流式语法:
     {RACK_ENV: development,SHOW: 'true'}

    映射可以嵌套映射:

     mysql:
       image: mysql:8
       container_name: mysql8

    映射可以嵌套序列:

     expose:
       - "3000"
       - "8000"

    序列可以嵌套序列

     # 多个值可以用映射(数组)或字典表示。(数组格式)
     environment:
       RACK_ENV: development
       SHOW: 'true'
        
     # 在映射中的任何布尔值都需要包括在引号中,字典格式则不需要:
     environment:
       - RACK-ENV=development
       - SHOW=true

3.2、Compose文件结构

Compose文件使用缩进表示层次结构。

 version: '3.7'  # 指定了 Compose 文件的版本
 ​
 # 定义服务
 services:
   web:
     image: nginx:alpine     # 使用官方的nginx镜像
     ports:
       - "80:80"             # 将容器的80端口映射到宿主机的80端口
     volumes:
       - web-data:/var/www   # 将宿主机的web-data目录挂载到容器的/var/www目录
     networks:
       - webnet              # 服务web将连接到webnet网络
 ​
   db:
     image: mysql:5.7        # 使用官方的mysql镜像
     environment:
       MYSQL_ROOT_PASSWORD: example  # 设置环境变量,这里为root用户设置密码
       MYSQL_DATABASE: mydb  # 初始化一个数据库
     volumes:
       - db-data:/var/lib/mysql  # 将宿主机的db-data目录挂载到容器的/var/lib/mysql目录
     networks:
       - webnet  # 服务db将连接到webnet网络
 ​
 # 定义网络
 networks:
   webnet:  # 定义了一个名为webnet的网络
 ​
 # 定义数据卷
 volumes:
   web-data:  # 定义了一个名为web-data的卷
   db-data:  # 定义了一个名为db-data的卷

Compose文件可以包含4节:version、services、networks和volumes

在这个示例中:

  • version指定了 Compose 文件的版本,这会影响文件中可用的语法和功能。必须指定
  • services部分定义了两个服务:web 和 db。每个服务都有自己的配置,如使用的镜像、端口映射、环境变量、挂载的卷和连接的网络。
  • networks部分定义了一个名为webnet的网络,它将被web和db 服务使用。
  • volumes部分定义了两个数据卷:web-data 和 db-data,它们分别用于 web服务的网页数据和 db服务的数据库数据。

3.3、服务定义

在services节中定义若干服务,每个服务实际上是一个容器,需要基于镜像运行。下面介绍部分常用键及选项

3.3.1、image

image键用于指定启动容器的镜像,可以是镜像名称或镜像ID,例如:

 image: redis
 iamge: ubuntu:14.04
 image: tutum/influxdb
 image: example-registry.com:4000/postgresql
 image: a3hec234

若镜像不存在则从镜像注册中心拉取。若定义有build键,则将基于Dockerfile构建一个镜像。

3.3.2、build

build键用于定义构建镜像时的配置,可以定义包括构建上下文路径的字符串

 build: ./bir

也可定义一个对象

 build:
   context: ./dir
   dockerfile: Dockerfile-alternate
   args:
     buildno: 1

如果image和build同时定义,那么Docker Compose会构建镜像并且将镜像命名为image键所定义的名称。

 # 镜像将从./dir中构建,被命名为webapp,并被设tagtest标签
 build: ./dir
 image: webapp:tagtest

build键下面可使用以下选项

  • context:定义构建上下文路径,可以是包含Dockerfile的目录,或是访问git仓库的URL
  • dockerfile:指定Dockerfile。
  • args:指定构建参数,即仅在构建阶段访问的环境变量,允许是空值

3.3.3、depends_on

定义服务之间的依赖,解决容器依赖。启动先后的问题

 version: "3.7"
 services:
   web:
     build:
     depends_on:
       - db
       - redis
   redis:
     image: redis
   db:
     image: postgres
  • 按依赖启动服务:db和redis先于web启动执行docker-compose up <服务名>命令将自动包括该服务的依赖。
  • 按依赖顺序停止服务:web先于db和redis停止

3.3.4、networks

默认下,会自动创建名为"[项目名]_default"的默认网络。服务的每个容器都会加入默认网络,该网络上的其他容器都可以访问。每项服务都可使用networks键指定要连接的网络,格式如下:

 services:
   some-service:
     netowrks:
       - some-network
       - other-network

networks有一个特别的aliases选项,用来设置该网络上的别名:

 # 下面示例提供了三个服务:web、worker、db,以及两个网络:new和legacy,db服务可以通过new网络中的主机名db或别名database访问,也可以通过legacy网络中的主机名db或别名mysql访问
 version: "3.7"
 services"
   web:
     image: "nginx:alpine"
     networks:
       -new
   worker:
     image: "my-worker-image:latest"
     networks:
       -legacy
    db:
      image: mysql
      networks:
        new:
          aliases:
            - database
        legancy:
          aliases:
            - mysql
 networks:
   new:
   legacy:

要让服务加入一个现有的网络,可以使用external选项

 networks:
   default:
     external:
       name: my-pre-existing-network

3.3.5、volumes

作为服务的下级键,用于定义要挂载的主机路径或命名卷

可以挂载一个主机上的路径作为单个服务定义的一部分,此时不要在volumes节中定义卷。如果要在多个服务之间重用一个卷,应使用volumes节定义一个命名卷,然后由volumes键引用。

 # 由Web服务使用的命名卷和一个为单个服务定义的绑定挂载(db 服务中volumes 字段所定义的第 1 个路径)。db 服务也使用一个名为 dbdata 的命名卷(db服务中 volumes字段所定义的第2个路径),只是命名卷采用的是字符串格式。命名卷必须在volumes节中定义。
 version: "3.7"
 services:
   web:
     image: nginx:alpine
     volumes:
       - type: volume
         source: mydata
         target: /data
         volume:
           novopy: true
       - type: bind
         source: ./static
         target: /opt/app/static
   db:
     image: postgres:latest
     volumes:
       - "/var/run/postgres/postgres.sock:/var/run/postgres/postgres.sock"
       - "dbdata:/var/lib/postgresql/data"
 volumes:
   mydata:
   dbdata:

volumes键的定义有两种格式:长格式,使用多个选项定义,上述就是使用的长格式。另一种是短格式,直接使用"主机:容器"格式指定主机上的路径,或使用"主机:容器:ro"格式定义访问模式

 volumes:
   # 仅定义一个路径,让Docker引擎自动创建一个匿名卷
   - /var/lib/mysql
   # 定义一个绝对路径映射(绑定挂载)
   - /opt/data:/var/lib/mysql
   # 定义主机上相对Compose文件的路径(绑定挂载)
   - ./cache:/tmp/cache
   # 定义相对于用户的路径(绑定挂载)
   - ~/configs:/etc/configs/:ro
   # 命名卷
   - datavolume:/var/lib/mysql

3.3.6、其他常用键

  • command:用于覆盖容器启动后默认执行的命令
  • entrypoint:用于覆盖容器的默认入口设置,将覆盖使用 Dockerfile 的 ENTRYPOINT 指令在服务镜像上设置的任何默认入口,并清除镜像上的任何默认命令。这意味着如果 Dockerfile 中有 CMD指令,也将被忽略。
  • env_file:设置从外部文件中添加的环境变量
  • environment:用于添加环境变量
  • expose:用于暴露没有发布到主机的端口,只允许被连接的服务访问。仅可以指定内都端口。
  • external_links:用于连接未在Compose 文件中定义,甚至是非 Dodker Compose 管理的容器,尤其是那些提供共享或通用服务的容器。
  • ports:指定要暴露的端口
  • restart:定义容器重启策略

3.4、网络定义

3.4.1、driver

定义用于此网络的网络驱动,默认驱动取决于Docker引擎的配置方式,单主机上bridge驱动,Swarm集群中overlay驱动:

 driver: overlay

3.4.2、external

设置网络是否在Docker Compose外部创建。

 # proxy是到外部网络的网关。这里没有创建一个名为“[项目名]_outside”的网络而是让Docker Compose查找一个名为outside的现有网络,并将proxy服务的容器连接到该网络。
 version:“3.7”
 services:
   proxy:
     build: ./proxy
     networks:
       - outside
       - default
   app:
     build: ./app
     networks:
       - default
 networks:
   outside:
     external: true

3.5、卷定义

 # 其中一个数据库的数据目录作为一个卷与其他服务共享,可以被周期性地备份。
 version:"3.7°
 services:
   db:
     image: db
     volurmes:
       - data-volume:/var/ib/db
   backup:
     image: backup-servicel
     volumes:
       - data-yolume:/var/ib/backup/data
 volumes:
   data-volume:

volumes节中定义的卷可以只需一个名称,不做其他具体配置,这种情形会使用Docker配置的默认驱动,也可以使用以下键进行具体配置。

3.5.1、driver

此键定义用于卷驱动,默认就是 Docker 所配置的驱动,多数情况下是 local(本地驱动)。如果驱动不可用,则执行docker-compose up命令创建卷时,Docker会返回错误。下面是一个简单的示例

 driver: foobar

3.5.2、external

该键用于设置卷是否在 Docker Compose 外部创建。如果设置为 true,则 docker-compose up命令不会尝试创建它,如果该卷不存在,则会引发错误。

 # Docker Compose 不会尝试创建一个名为“[项目名]_data”的卷,而是查找一个名称为data的现有卷,并将其挂载到db服务的容器中。
 version: "3.7"
 services:
   db:
     image:postgres
     volumes:
       - data:/var/lib/postgresql/data
 volumes:
   data:
   external: true

四、示例

4.1、编写单个服务的Compose文件

使用Docker Compose部署MySQL8.0服务器:编写docker-compose.yml文件

 version: '3.7'
 services:
   mysql:
     image: mysql:8
     container_name: mysql8
     ports:
     - 3306:3306
     command:
       --default-authentication-plugin=mysql_native_passord
       --character-set-server=utf8mb4
       --collation-server=utf8mb4_general_ci
       --explicit_defaults_for_timestamp=true
       --lower_case_table_names=1
     environment:
     - MYSQL_ROOT_PASSWORD=root
     volumes:
     - /etc/localtime:/etc/localtime:ro
     - volumes.mysql8-data:/var/lib/mysql
 volumes:
   volumes.mysql8-data: null

使用docker-compose conifg命令查看,并会以更规范的形式显示整个compose文件

 [root@docker test]# docker-compose config
 name: test
 networks:
   default:
     name: test_default
 services:
   mysql:
     command: --default-authentication-plugin=mysql_native_passord --character-set-server=utf8mb4 --collation-server=utf8mb4_general_ci --explicit_defaults_for_timestamp=true --lower_case_table_names=1
     container_name: mysql8
     environment:
       - MYSQL_ROOT_PASSWORD=root
     image: mysql:8
     networks:
       default: null
     ports:
       - mode: ingress
         protocol: tcp
         published: "3306"
         target: 3306
     volumes:
       - bind:
           create_host_path: true
         read_only: true
         source: /etc/localtime
         target: /etc/localtime
         type: bind
       - source: volumes.mysql8-data
         target: /var/lib/mysql
         type: volume
         volume: {}
 version: "3.7"
 volumes:
   volumes.mysql8-data:
     name: test_volumes.mysql8-data

启动该服务:

 [root@docker-2322030238 test]# docker-compose up -d
 [+] Running 1/3
  ⠧ Network test_default               Created  0.7s     # 创建网络
  ⠦ Volume "test_volumes.mysql8-data"  Created  0.6s     # 创建卷
  ✔ Container mysql8                   Started     

实验完毕,执行以下操作关闭并清理服务:

 [root@docker-2322030238 test]# docker-compose down --volumes
 [+] Running 3/0
  ✔ Container mysql8                 Removed 0.0s 
  ✔ Volume test_volumes.mysql8-data  Removed  0.0s 
  ✔ Network test_default             Removed  0.1s 

4.2、多个服务的Compose文件

示例:建立和运行一个简单的Django/PostgreSQL应用程序

4.2.1、定义项目组件

一个Dockerfile文件、一个Python依赖文件和一个名为docker-compose.yml的Compose文件

(1)创建项目目录

 mkdir django-pg && cd django-pg

(2)创建并编辑Dockerfile文件

 # 从Python3父镜像开始
 FROM python:3
 ENV PYTHONUNBUFFERED 1
 # 在镜像中添加code目录
 RUN mkdir /code
 WORKDIR /code
 COPY requirements.txt /code/
 # 在镜像中安装由requirement.txt文件指定要安装的Python依赖
 RUN pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
 COPY . /code/

(3)创建并编辑requirements.txt文件

 Django>=2.0,<3.0
 psycopg2>=2.7,<3.0

(4)创建并编辑docker-compose.yml文件

 version: '3'
 services:
   db:
     image: postgres
     volumes:
       - db_data:/var/lib/postgresql
   web:
     build: .
     command: python manage.py runserver 0.0.0.0:8000
     volumes:
       - .:/code
     ports:
       - "8000:8000"
     depends_on:
       - db
 volumes:
   db_data: {}

4.2.2、创建Django项目

(1)切换到项目目录的根目录

(2)执行docker-compose run命令创建Django初始项目

 docker-compose run web django-admin startproject myexample .

(3)查看所创建的项目目录的内容

 [root@docker-2322030238 django-pg]# ls -l
 total 16
 -rw-r--r-- 1 root root 281 May 12 11:44 docker-compose.yml
 -rw-r--r-- 1 root root 324 May 12 11:56 Dockerfile
 -rwxr-xr-x 1 root root 629 May 12 12:07 manage.py
 drwxr-xr-x 2 root root  74 May 12 12:07 myexample
 -rw-r--r-- 1 root root  36 May 12 11:36 requirements.txt

容器以root身份运行,可执行以下命令修改这些文件的所有者

 chown -R $USER:$USER

4.2.3、连接数据库

(1)编辑项目目录中的myexample/settings.py文件,将其中的"DATABASES"定义修改如下:

 DATABASES={
     'default':{
         'ENGINE':'django.db.backends.postgresql',
         'NAME':'postgres',
         'USER':'postgres',
         'PASSWORD':'postgres'
         'HOST':'db',
         'PORT':'5432',
     }
 }

这些设置由docker-compose.yml文件所指定的postgres镜像所决定。保存关闭

(2)在项目目录的根目录下执行docker-compose up命令

 docker-compose up

出现问题,待解决......

五、使用Docker Compose部署和管理应用程序

5.1、Compose命令行格式

 docker-compose [-f <arg>…][选项][命令][参数…]
  • -f(--file): 指定一个或多个Compose文件的名称和路径
  • -p(--project-name): 指定项目名称
  • --project-directory: 指定项目路径
  • --verbose: 输出更多调试信息
  • --log-level: 设置日志级别
  • -v(-version): 显示Docker Compose命令的版本信息
  • -h(--help): 获取Compose命令行的帮助信息

5.2、docker-compose build

构建或重新构建服务并设置标签

 docker-compose build [选项][--build-arg 键=值·…][服务…]
  • --compress: 使用gzip压缩构建上下文
  • --force-m:删除构建过程中的临时容器
  • --no-cache: 构建镜像的过程中不使用缓存
  • --pul:总是尝试拉取最新版本的镜像
  • --build-arg key=val:为服务设置构建时变量

5.3、docker-compose up

构建镜像,创建、启动和连接指定的服务容器

 docker-compose up [选项][--scale 服务=数值·…][服务…
  • -d(--detach):分离模式,后台运行服务容器
  • --quiet-pull:拉取镜像时不输出信息
  • --no-deps:不启动所连接的服务
  • --force-recreate:强制重新创建容器
  • --no-recreate:如果容器已存在,则不重新创建
  • --no-build:不构建缺失的镜像
  • --no-start:创建服务后不启动它们
  • --build:启动容器前构建镜像

5.4、docker-compose down

停止容器并删除docker-compose up命令启动的对容器、网络、卷和镜像。默认情况下只有以下对象会被删除

  • Compose文件中定义的服务的容器
  • Compose文件中networks节所定义的网络
  • 所使用的默认网络

外部定义的网络和卷不会删除,使用--volumes可以删除由容器使用的数据卷

 docker-compose down --volumes

5.5、其他常用的docker-compose命令

  • start: 启动已存在的容器。
  • stop:停止运行的容器。
  • pause: 暂停容器。
  • unpause: 恢复暂停的容器。
  • kill:发送信号终止容器。
  • run:为服务执行一次性命令。
  • ps:查看当前运行的容器。
  • exec:在容器中执行命令。
  • rm:删除停止状态的容器。

5.6、docker-compose 的3个命令up、run和start

  • up:启动或重新启动所有服务。
  • run:运行一次性或临时任务。
  • start:仅重启已创建但已停止的容器。

5.7、Docker Compose的环境变量

环境变量的优先级:

  1. Compose文件中直接设置的值。
  2. 环境文件。
  3. Shell环境变量。
  4. Dockerfile中的ARG或ENV指令。
  5. 未定义的变量。

举例:环境文件和Compose文件设置同一环境变量。环境文件./Docker/api/api.env中:

 NODE_ENV=test

Compose文件docker-compose.yml中:

 version: '3'
 services: 
   api:
     image: 'node:6-alpine'
     env_file:
       - ./Docker/api/api.env
     environment:
       - NODE_ENV=production

运行容器时,会优先使用Compose文件中的环境变量

 docker-compose exec api node
 > process.env.NODE_ENV
 'production'

只有Compose文件中没有使用environment或env_file键时,Dockerfile文件中的ARG或ENV指令设置才会生效

5.8、组合使用多个Compose文件

  • 基础Compose文件定义基本配置
  • Override文件覆盖或新增服务配置
  • 使用-f选项指定文件列表,Docker Compose将按顺序合并配置

5.8.1、合并配置规则

  • 单值键:新值替换旧值。
  • 多值键:两组值连接。
  • environment、labels、volumes和devices:优先使用本地定义的值。

致谢

在此,我要对所有为知识共享做出贡献的个人和机构表示最深切的感谢。同时也感谢每一位花时间阅读这篇文章的读者,如果文章中有任何错误,欢迎留言指正。

学习永无止境,让我们共同进步!!

  • 15
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小李学不完

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值