Docker——consul自动发现与更新服务

什么是consul,有什么作用

consul是HashiCorp公司推出使用go语言编写的开源工具,用于实现分布式系统的服务发现与配置

具有如下特性:

  • 1、consul支持健康检查,允许存储键值对
  • 2、一致性协议采用Raft算法,用来保证服务的高可用
  • 3、成员管理和消息广播采用GOSSIP协议,支持ACL访问控制
  • 4、方便部署,与Docker等轻量级容器可无缝配合

1.Consul的作用

在这里插入图片描述
如上图所示,生产环境下,我们只需要前端的Nginx能够与后端的web服务器建立联系,反向代理功能能够完成就OK了

但是如果后端服务器要加节点了怎么办,可能第一时间想到的是去Nginx配置文件加上指向后端web地址就可以了,但是如果后端跑的是容器,少量节点还可以人工手动配置,如果这个时候后端节点增加的是1000台,10000台,靠人工添加是特别特别繁琐的

而且在日常维护中,后端节点突然down掉,容器down掉呢?任务量可想而知

consul:提供了自动发现与更新服务,不管你后端加容器还是减少容器,自动帮你完成配置文件的更新

2.consul是如何做到做到服务的自动发现与更新呢?

在这里插入图片描述

  • 图解:

上方拓扑图是基于Docker完成的,然后将consul、consul template、registrator和nginx组装成一个值得信任且可扩展的服务框架,此架构可以灵活的、不需要重启任何服务、不需要重写任何配置的添加和移除服务

前端Nginx做反向代理,server_1,server_2是裸金属节点,节点中跑了多个容器
Nginx还跑了一个consul(服务的自动发现与更新)

|反向代理只需要指向后端的容器
如果容器down了,或者新加了容器,减少了容器,其中的地址,端口都会随着它发生改变
Nginx.conf文件也会变化,

变化的精髓就在consul template模板,它会将变化的东西写成变量,不变的则正常写入,有了这个模板之后,就放在这,有了变化之后就可以利用这个模板修改原有的nginx.conf

  • 如何发现变化?

Consul agent负责监控下端节点反馈的信息,监控功能依赖于registrator,所以说对于跑业务的节点,需要安装registrator,registrator会定期的发送报告给Consul agent,告诉它我哪些东西改变了

Consul agent再将报告发送给consul server,consul server将请求信息发送给consul template,consul template根据请求修改nginx.conf

  • 注意点:

后端业务节点需要安装registrator自动注册机制,监控的节点要装consul server,还需要consul template模板,consul server中自带agent,所以装了server就行了

Docker consul自动发现服务架构的构建

1.底层环境

服务器:20.0.0.21Docker-ceCompose 3ConsulConsul-template
服务器:20.0.0.22Docker-ceregistrator
环境防火墙与内核全关

2.docker安装

可以点击连接这里不再演示

3.配置consul服务器

[root@docker ~]# mkdir consul
[root@docker ~]# cd consul/

##将软件包拖进来##
unzip consul_0.9.2_linux_amd64.zip 

###解压后会有一个执行命令###
[root@docker consul]# unzip consul_0.9.2_linux_amd64.zip 
Archive:  consul_0.9.2_linux_amd64.zip
  inflating: consul                  
[root@docker consul]# ls
consul                        
consul-template_0.19.3_linux_amd64.zip
consul_0.9.2_linux_amd64.zip 

##放到bin下交给系统管理与识别
[root@docker consul]# mv consul /usr/bin/
###配置consul####
[root@docker consul]# consul agent \
-server \
-bootstrap \
-ui \
-data-dir=/var/lib/consul-data \
-bind=20.0.0.21 \
-client=0.0.0.0 \
-node=consul-server01 &> /var/log/consul.log &
#########脚本解释###########
-server:里面会以前端的页面形式展现出来
-bootstrap:所以需要安装前端的脚本bootstrap
-ui:ui界面
-data-dir:数据的存放目录
-bind: 绑定本地地址
-client=0.0.0.0 监听任意客户端网段发来的通告信息(可以监控后面的容器发生了什么样的变化)
-node:声明node节点名称, &>表述混合输出,最后一个&,表示放到后台运行
########################脚本解释################3



####查看集群信息,这里出现一个报错,是因为我把server敲成了serber
[root@docker consul]# consul members
Error retrieving members: Get http://127.0.0.1:8500/v1/agent/members: dial tcp 127.0.0.1:8500: getsockopt: connection refused

解决:[root@docker consul]# pkill -9 consul  再将consul脚本再执行一遍
##查看集群信息
[root@docker consul]# consul members
Node                        Address          Status    Type      Build    Protocol   DC
consul-server01  20.0.0.21:8301      alive     server     0.9.2         2          dc1

##查看consul的领导者
###端口号8300的开放,是为了方便node节点的registrator(注册器)与它通讯
[root@docker consul]# consul info | grep leader
	leader = true
	leader_addr = 20.0.0.21:8300       

####还可以通过httpd api 获取集群信息####
curl 127.0.0.1:8500/v1/status/peers   ###查看集群成员
["20.0.0.21:8300"]

curl 127.0.0.1:8500/v1/status/leader  ##查看集群的老大
"20.0.0.21:8300"

curl 127.0.0.1:8500/v1/catalog/services  ##查看所有注册的服务
{"consul":[]}

curl 127.0.0.1:8500/v1/status/nginx    ##查看nginx的服务信息,因为这个时候我还没有开服务所以显示为空

curl 127.0.0.1:8500/v1/catalog/nodes ##查看集群节点详细信息
[{"ID":"75c320a5-e04c-ca23-b8e5-b7439f6ad73c","Node":"consul-server01","Address":"20.0.0.21",
"Datacenter":"dc1","TaggedAddresses":{"lan":"20.0.0.21","wan":"20.0.0.21"},"Meta":{},"CreateIndex":5,"ModifyIndex":6}]

打开浏览器访问20.0.0.21:8500
在这里插入图片描述

4.配置容器服务器

1.安装Gliderlabs/Registrator Gliderlabs/Registrator
可检查容器运行状态自动注册,还可注销docker容器的服务到服务配置中心。
目前支持Consul、Etcd和SkyDNS2。
20.0.0.22节点,执行以下操作:

###首先要安装docker-ce######安装完之后####
[root@node ~]# docker run -d \
--name=registrator \
--net=host \
-v /var/run/docker.sock:/tmp/docker.sock \
--restart=always \
gliderlabs/registrator:latest \
-ip=20.0.0.22 \
consul://20.0.0.21:8500

#########脚本解释##########
--name:指定容器的名称
--net=host   指定里面的网络形式   host仅主机
-v 数据卷,前面是宿主机的,后面的是容器的,注意:里面联通的是sock,为了里外相互联通
--restart : always   重启策略,一旦容器发现down状态,会对容器始终进行重启(always)

镜像来自于gliderlabs仓库,名字为redistrator,标签为latest

-ip:本地的IP地址
Consul://指向master地址,服务端口8500
####################脚本注释########################

####查看镜像###########
[root@node ~]# docker images
REPOSITORY               TAG                 IMAGE ID            CREATED             SIZE
gliderlabs/registrator   latest              3b59190c6c80        4 years ago         23.8MB

####20.0.0.21主机查看8500端口可以看到两个服务器都在#######
[root@docker consul]# netstat -anutp | grep 8500
tcp6       0      0 :::8500                 :::*                    LISTEN      48701/consul        
tcp6       0      0 20.0.0.21:8500          20.0.0.22:60366         ESTABLISHED 48701/consul 

###http://20.0.0.21:8500/这个时候可以刷新浏览器########
###因为现在还没有容器,所以是空的#######

在这里插入图片描述

##创建容器,测试consul是否能够自动发现
20.0.0.22
docker run -itd -p 83:80 --name test-01 nginx
docker run -itd -p 84:80 --name test-02 nginx
docker run -itd -p 88:80 --name test-03 httpd
docker run -itd -p 89:80 --name test-04 httpd

##端口号冲突报错,改成8480######
[root@node ~]# docker run -itd -p 83:80 --name test-02 nginx
3d1842a261dfdd7faa70feffb2fe53f5890b6c635e6ca8265ac184149e358656
docker: Error response from daemon: driver failed programming external connectivity on 
endpoint test-02 (760dca26264e173f84cc0230836b3275eaf652374cbcd1ed240856b747222b1e): Bind 
for 0.0.0.0:83 failed: port is already allocated.

#########这个时候你再打开20.0.0.21:8500,点击NODES后会看到自动发现了20.0.0.22上的4个服务###

在这里插入图片描述

#####查看镜像状态#####
[root@node ~]# docker ps -a
CONTAINER ID        IMAGE                           COMMAND                  CREATED                      STATUS              PORTS                            NAMES
4e9c21eeafbe        httpd                           "httpd-foreground"        2 minutes ago       Up About a minute   0.0.0.0:89->80/tcp           test-04
e6ce7832e809        httpd                          "httpd-foreground"        2 minutes ago       Up 2 minutes             0.0.0.0:88->80/tcp          test-03
3fcbe2a4d7a1        nginx                          "/docker-entrypoint.…"   5 minutes ago       Up 5 minutes             0.0.0.0:84->80/tcp           test-02
5821bd0c9b49        nginx                         "/docker-entrypoint.…"   6 minutes ago       Up 6 minutes             0.0.0.0:83->80/tcp           test-01
67582691ae53        gliderlabs/registrator:latest   "/bin/registrator -i…"   16 minutes ago      Up 16 minutes                                        registrator


##这个时候再次查看master上所有注册的服务#####,可以看到多了nginx与httpd服务###
[root@docker consul]# curl 127.0.0.1:8500/v1/catalog/services
{"consul":[],"httpd":[],"nginx":[]}

5.安装consul-template

  • Consul-Template是一个守护进程,

用于实时查询Consul集群信息,并更新文件系统上任意数量的指定模板,生成配置文件。更新完成以后,可以选择运行shell命令执行更新操作,重新加载Nginx。Consul-Template可以查询Consul中的服务目录、Key、Key-values等。这种强大的抽象功能和查询语言模板可以使Consul-Template特别适合动态的创建配置文件。

例如:创建Apache/Nginx Proxy Balancers、Haproxy Backends

#####首先准备template nginx模板文件#######在consul主机:20.0.0.21
[root@docker consul]# cd ~
[root@docker ~]# cd consul/

###创建模板###########
[root@docker consul]# vim nginx.ctmpl    ##模板固定后缀.ctmpl,consul-template的缩写
##consul-server发现变化后,将地址与端口写入参数中,转发给consul-template,consul-template直接调用API,将值写进nginx.conf
##range:范围,是遍历的手法,相当于for循环
upstream http_backend {               ##upstream表示做的是4层转发,nginx可以同时做4层与7层转发,lvs只能做4层转发
  {{range service "nginx"}}           ##争对于于nginx,它把它转发的请求转发给了nginx,range
   server {{.Address}}:{{.Port}};     ##这里定义了两个API(内置变量),Address地址,Port端口
   {{end}}                             ##相当于for中的break,退出循环
}

##nginx中有多个功能模块,转发可以是从server中的local段进行###
server {
  listen 83;           ##添加的监听端口,以防端口冲突,随便定义,访问20.0.0.21:83/80都可以
  server_name localhost 20.0.0.21;                     ##本地IP地址
  access_log /var/log/nginx/ZZT.cn-access.log;         ##日志文件存放位置
  index index.html index.php;                          ##支持的首页类型
  location / {          ##location中可以实现跳转,依赖于proxy_pass(反向代理转发模块)
   proxy_set_header HOST $host;                     ##开头要对请求转发头部做定义:HOST $host表示来访者的主机名,就是主机头信息
   proxy_set_header X-Real-IP $remote_addr;              ##$remote_addr是一个内置变量,表示的是请求者的地址,远端请求的地址
   proxy_set_header Client-IP $remote_addr;             ##含有同上,都表示请求者的地址,远端请求的地址
   proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;   ##X-Forwarded-For表示转发,转发的是$proxy_add_x_forwarded_for,它代表的是反向代理的所谓的路径
   proxy_pass http://http_backend;          ##重点!!!:proxy_pass(反向代理转发模块),proxy_pass启用的是http协议,加载的是http_backend这个函数模块
  }                                                                                ##http_backend这个是自己写的,它来自哪里?看第一行!!!!
}


######这个时候在本地(20.0.0.21)安装一个nginx,充当着反向代理#########
[root@docker consul]# yum -y install gcc gcc-c++ zlib-devel pcre-devel

将nginx包源拖进/opt
[root@docker nginx]# cd /opt/
[root@docker opt]# tar zxvf nginx-1.15.9.tar.gz
[root@docker opt]# cd nginx-1.15.9/

[root@docker nginx-1.15.9]# ./configure --prefix=/usr/local/nginx
[root@docker nginx-1.15.9]# make && make install
[root@docker nginx-1.15.9]# cd /usr/local/nginx/conf/

[root@docker conf]# vim nginx.conf
##19行添加###
19     include       vhost/*.conf;     
##include表示包含,包含的路径    vhost/下的任何.conf都能识别##
##所以等会更新的子配置文件要放到vhost/###


###因为vhost还没有,所以我们需要创建
[root@docker conf]# mkdir /usr/local/nginx/conf/vhost

##还需要创建template模板中写的日志存放位置##
[root@docker conf]# mkdir /var/log/nginx

##做完这个时候就可以启动nginx了##
[root@docker conf]# /usr/local/nginx/sbin/nginx 
[root@docker conf]# netstat -anutp | grep nginx
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      52639/nginx: master 
###接下来让template模板起作用,####
##先解压consul-template,因为我这个时候还没安装consul-template###
将包源拖进/opt下面
[root@docker vhost]# cd /opt/
[root@docker opt]# unzip consul-template_0.19.3_linux_amd64.zip
[root@docker opt]# mv consul-template /usr/bin/

###接下来让template模板起作用###
[root@docker opt]# consul-template -consul-addr 20.0.0.21:8500 \
-template "/root/consul/nginx.ctmpl:/usr/local/nginx/conf/vhost/zzt.conf:/usr/local/nginx/sbin/nginx -s reload" \
--log-level=info


/root/consul/nginx.ctmpl模板位置
/usr/local/nginx/conf/vhost/zzt.conf 模板生成的配置文件放到的位置并起个名字叫zzt.conf
/usr/local/nginx/sbin/nginx -s reload 对nginx进行重载
--log-level=info  生成日志的级别:info(信息级别)

###执行后进入监听状态,你重新打开一个20.0.0.21的终端,看一下zzt.conf有没有生成到/usr/local/nginx/conf/vhost/
#########如果出现报错###########
nginx: [emerg] unknown directive "server20.0.0.6:83}" in /usr/local/nginx/conf/vhost/zzt.conf:3
2020/11/16 12:00:26.042823 [ERR] (cli) 1 error occurred:

* failed to execute command "/usr/local/nginx/sbin/nginx -s reload" from "/root/consul/nginx.ctmpl" => "/usr/local/nginx/conf/vhost/zzt.conf": child: command exited with a non-zero exit status:

    /usr/local/nginx/sbin/nginx -s reload
###################################
解决思路,删除vhost下面的模板配置文件,检查template模板语法问题与nginx.conf配置文件
最后发现是模板格式问题,重新编写就好了

###########################################

###生成就代表成功了#####


##使用另外一个终端查看#########
[root@docker ~]# cd /usr/local/nginx/conf/vhost/
[root@docker vhost]# ls
zzt.conf
[root@docker vhost]# vim zzt.conf 

upstream http_backend {

   server 20.0.0.22:83;                ###地址自动被写入了

   server 20.0.0.22:84;

}

server {
  listen 83;
  server_name localhost 20.0.0.21;
//...............................//

##查看端口是否
[root@docker vhost]# netstat -anutp | grep nginx
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      52639/nginx: master 
tcp        0      0 0.0.0.0:83              0.0.0.0:*               LISTEN      52639/nginx: master 

[root@docker vhost]# netstat -anutp | grep 80
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      52639/nginx: master 
[root@docker vhost]# netstat -anutp | grep 83
tcp        0      0 20.0.0.21:8300          0.0.0.0:*               LISTEN      48701/consul        
tcp        0      0 20.0.0.21:8301          0.0.0.0:*               LISTEN      48701/consul        
tcp        0      0 20.0.0.21:8302          0.0.0.0:*               LISTEN      48701/consul        
tcp        0      0 0.0.0.0:83                  0.0.0.0:*               LISTEN      52639/nginx: master 
tcp        0      0 127.0.0.1:6010          0.0.0.0:*               LISTEN      1831/sshd: root@pts 
tcp        0      0 20.0.0.21:44525         20.0.0.21:8300          TIME_WAIT   -                   
tcp        0      0 20.0.0.21:22            20.0.0.1:58235          ESTABLISHED 1831/sshd: root@pts 
tcp6       0      0 ::1:6010                :::*                    LISTEN      1831/sshd: root@pts 
udp        0      0 20.0.0.21:8301          0.0.0.0:*                           48701/consul        
udp        0      0 20.0.0.21:8302          0.0.0.0:*                           48701/consul        




###打开笔记本访问浏览器,生成日志文件
###这个时候浏览器打开20.0.0.21:80   20.0.0.21:83 都能访问####

在这里插入图片描述
在这里插入图片描述

###查看访问日志###
[root@docker vhost]# cd /var/log/nginx/
[root@docker nginx]# ls
ZZT.cn-access.log
[root@docker nginx]# cat ZZT.cn-access.log 
20.0.0.1 - - [15/Nov/2020:02:04:49 +0800] "GET / HTTP/1.1" 200 612 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36"
20.0.0.1 - - [15/Nov/2020:02:04:49 +0800] "GET /favicon.ico HTTP/1.1" 404 555 "http://20.0.0.21:83/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36"

6.验证反向代理的轮询机制

###为了验证Nginx是不是开启了反向代理,我们需要进入容器查看访问信息###
20.0.0.22服务器操作:

###test-01和test-01是我们容器中的nginx##
[root@node ~]# docker ps -a
CONTAINER ID        IMAGE                           COMMAND                  CREATED             STATUS              PORTS                NAMES
4e9c21eeafbe        httpd                           "httpd-foreground"       2 hours ago         Up 2 hours          0.0.0.0:89->80/tcp   test-04
e6ce7832e809        httpd                           "httpd-foreground"       2 hours ago         Up 2 hours          0.0.0.0:88->80/tcp   test-03
3fcbe2a4d7a1        nginx                           "/docker-entrypoint.…"   2 hours ago         Up 2 hours          0.0.0.0:84->80/tcp   test-02
5821bd0c9b49        nginx                           "/docker-entrypoint.…"   2 hours ago         Up 2 hours          0.0.0.0:83->80/tcp   test-01
67582691ae53        gliderlabs/registrator:latest   "/bin/registrator -i…"   2 hours ago         Up 2 hours                               registrator


####可以看到后端是轮询访问###
[root@node ~]# docker logs -f test-01
20.0.0.21 - - [15/Nov/2020:02:04:53 +0000] "GET / HTTP/1.0" 200 612 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36" "20.0.0.1"


[root@node ~]# docker logs -f test-02
2020/11/15 02:04:53 [error] 28#28: *1 open() "/usr/share/nginx/html/favicon.ico" failed (2: No such file or directory), client: 20.0.0.21, server: localhost, request: "GET /favicon.ico HTTP/1.0", host: "20.0.0.21", referrer: "http://20.0.0.21:83/"
20.0.0.21 - - [15/Nov/2020:02:04:53 +0000] "GET /favicon.ico HTTP/1.0" 404 555 "http://20.0.0.21:83/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36" "20.0.0.1"

7.验证自动发现与更新服务

###我再创建个nginx,创建完成后,刷新你的笔记本浏览器,在看新建的容器的访问日志###
[root@node ~]# docker run -itd -p 85:80 --name test-05 nginx

[root@node ~]# docker ps -a
CONTAINER ID        IMAGE                           COMMAND                  CREATED             STATUS              PORTS                NAMES
f4dee7f087e9        nginx                           "/docker-entrypoint.…"   6 seconds ago       Up 5 seconds        0.0.0.0:85->80/tcp   test-05
4e9c21eeafbe        httpd                           "httpd-foreground"       2 hours ago         Up 2 hours          0.0.0.0:89->80/tcp   test-04
e6ce7832e809        httpd                           "httpd-foreground"       2 hours ago         Up 2 hours          0.0.0.0:88->80/tcp   test-03
3fcbe2a4d7a1        nginx                           "/docker-entrypoint.…"   2 hours ago         Up 2 hours          0.0.0.0:84->80/tcp   test-02
5821bd0c9b49        nginx                           "/docker-entrypoint.…"   2 hours ago         Up 2 hours          0.0.0.0:83->80/tcp   test-01
67582691ae53        gliderlabs/registrator:latest   "/bin/registrator -i…"   2 hours ago         Up 2 hours                               registrator

##可以看到是轮询##
[root@node ~]# docker logs -f test-05
20.0.0.21 - - [15/Nov/2020:02:17:30 +0000] "GET / HTTP/1.0" 304 0 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36" "20.0.0.1"

##########访问20.0.0.21:8500,可以看到consul也增加了#########

在这里插入图片描述

###打开20.0.0.21查看模板自动更新了###
[root@docker nginx]# cd /usr/local/nginx/conf/vhost/
[root@docker vhost]# ls
zzt.conf
[root@docker vhost]# cat zzt.conf 
upstream http_backend {
  
   server 20.0.0.22:83;
   
   server 20.0.0.22:84;
   
   server 20.0.0.22:85;
   
}
###再来验证一下容器down掉会不会自动减少####
[root@node ~]# docker stop test-01
test-01
[root@node ~]# docker rm test-01 
test-01
[root@node ~]# docker ps -a
CONTAINER ID        IMAGE                           COMMAND                  CREATED             STATUS              PORTS                NAMES
f4dee7f087e9        nginx                           "/docker-entrypoint.…"   7 minutes ago       Up 7 minutes        0.0.0.0:85->80/tcp   test-05
4e9c21eeafbe        httpd                           "httpd-foreground"       2 hours ago         Up 2 hours          0.0.0.0:89->80/tcp   test-04
e6ce7832e809        httpd                           "httpd-foreground"       2 hours ago         Up 2 hours          0.0.0.0:88->80/tcp   test-03
3fcbe2a4d7a1        nginx                           "/docker-entrypoint.…"   2 hours ago         Up 2 hours          0.0.0.0:84->80/tcp   test-02
67582691ae53        gliderlabs/registrator:latest   "/bin/registrator -i…"   2 hours ago         Up 2 hours                               registrator

###刷新consul页面,减少了!!!!###

在这里插入图片描述

###打开20.0.0.21查看模板自动更新了###
[root@docker vhost]# cat zzt.conf 
upstream http_backend {
  
   server 20.0.0.22:84;
   
   server 20.0.0.22:85;
   
}

################实验完成#######################

8.consul添加多节点

//添加一台已有docker环境的服务器20.0.0.23加入到已有集群中
//将consul_0.9.1_linux_amd64.zip拖进/opt,解压完成之后,移到/usr/bin/下面

[root@docker consul]# consul agent \
-server \
-bootstrap \
-ui \
-data-dir=/var/lib/consul-data \
-bind=20.0.0.21 \
-client=0.0.0.0 \
-node=consul-server02 \
-enable-script-checks=true \
-datacenter=dc1 \
-join 20.0.0.21 &> /var/log/consul.log &

-enable-script-checks=true:设置检查服务为可用
-datacenter:已经有的数据中心名称
-join:加入到已有的群集中,所以IP要写20.0.0.21
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值