Docker相关

尚硅谷大数据技术之Kubernetes
第1章 环境准备
1.1 组网
需要在机房操作具体服务器,具体内容见下面视频:
https://pan.baidu.com/s/1WRFfau1TwcC9iLoumHP6qA
1.2 Raid磁盘阵列规划
1.2.1 什么是Raid
简单来说,RAID是一种把多块独立的磁盘按照不同的方式组合成一个磁盘组,从而提供比单个独立磁盘更高存储效率和更高安全性的技术。
RAID的优势:
 加快数据的存取速度,提升服务器的工作效率
 提供了容错能力,数据安全性提高
 在提高性能和保证数据安全的基础上合理利用磁盘空间
 由多个独立磁盘组成一个磁盘组,提供了更加便捷的管理
1.2.2 Raid的常见类型

1.2.3 硬Raid配置
由于硬件厂商众多,服务器的上所使用的硬RAID卡虽功能基本一致,但管理页面和流程却不尽相同。我们此处就以联想的Think Server RD430 为例为大家讲解下配置流程。
1)开机进入RAID 操作管理界面
不同品牌的机器进入RAID管理界面的组合按键不一样,注意开机后引导过程的提示。以联想的Think Server RD430为例,启动服务器,在POST过程中,当完成RAID卡自检后,根据提示按+进入。

2)查看当前磁盘使用信息(首页)

3)清理掉原来的RAID配置,并创建新的磁盘组配置

4)将磁盘依次添加到新的磁盘组内

5)选择合适的RAID级别,更新RAID磁盘大小,并保存设置

6)初始化磁盘组完成配置

1.2.4 软Raid配置
软RAID一般借助系统自带的RAID管理工具进行操作,可以在安装系统前完成设置,也可以在安装完操作系统后使用命令进行调整。此次以CentOS 7.5为例给大家演示。
1)安装操作系统过程中选择手动分区方式为RAID

2)选中所有磁盘,并选择【我要配置分区】,进行手动设置

3)点击+号,进行人为手动分区(/boot、/、swap等)或自动分区

4)选择分区类型为RIAD类型,并选择RAID等级,根据后期不同的需求给分区设置不同的RAID类型。例如:/分区可以设置为RAID1,提高系统化的可用性。

  1. 保存设置并初始化完成配置

视频:https://pan.baidu.com/s/1xKNAtm94VeR-J0cIwHMzeA

1.3 虚拟机准备(Centos7)
1.3.1 新建虚拟机
(1)点击创建新的虚拟机

(2)选择类型

(3)选择安装来源

(4)选择操作系统

(5)命名及选择位置

(6)选择磁盘大小

(7)完成

1.3.2 配置虚拟机
(1)进入配置页面

(2)选择镜像文件

(3)配置内存及处理器(根据自己机器配置)

1.3.3 安装Centos7
(1)选择安装

等待一段时间会进入图像化页面。
(2)选择语言

(3)配置分区

之后就可以点击开始安装了。
(4)配置root密码

配置完成后耐心等待安装,安装完成后重启即可。
1.4 修改静态IP及主机名
修改静态IP,根据自己实际情况修改。
[root@localhost ~]# vi /etc/sysconfig/network-scripts/ifcfg-ens33
TYPE=Ethernet
PROXY_METHOD=none
BROWSER_ONLY=no
BOOTPROTO=static
DEFROUTE=yes
IPV4_FAILURE_FATAL=no
IPV6INIT=yes
IPV6_AUTOCONF=yes
IPV6_DEFROUTE=yes
IPV6_FAILURE_FATAL=no
IPV6_ADDR_GEN_MODE=stable-privacy
NAME=ens33
UUID=004147e0-f126-462f-a91b-5203194dfb5f
DEVICE=ens33
ONBOOT=yes
IPADDR=192.168.1.101
DNS1=114.114.114.114
DNS2=8.8.8.8
GATEWAY=192.168.1.2
NETMASK=255.255.255.0
重启网络服务
[root@localhost ~]# systemctl restart network
修改主机名
[root@localhost ~]# hostnamectl set-hostname hadoop101
1.5 克隆三台虚拟机
将hadoop101克隆三次,克隆后分别修改主机名和静态ip。
1.6 系统初始化(所有机器)
1.6.1 设置host文件
[root@hadoop102 ~]# vi /etc/hosts
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
192.168.1.102 hadoop102
192.168.1.103 hadoop103
192.168.1.104 hadoop104
1.6.2 修改yum缓存地址
[root@hadoop102 ~]# vi /etc/yum.conf
[main]
cachedir=/yum
keepcache=1
debuglevel=2
logfile=/var/log/yum.log
exactarch=1
obsoletes=1
gpgcheck=1
plugins=1
installonly_limit=5
#然后将资料中的yum文件夹上传到/下。
[root@hadoop102 ~]# chmod 777 /yum/
1.6.3 安装依赖
[root@hadoop102 ~]# yum install -y conntrack ntpdate ntp ipvsadm ipset jq iptables curl sysstat libseccomp wget vim net-tools git
1.6.4 设置防火墙为Iptables并清空规则
[root@hadoop102 ~]# systemctl stop firewalld && systemctl disable firewalld
[root@hadoop102 ~]# yum -y install iptables-services && systemctl start iptables && systemctl enable iptables && iptables -F && service iptables save
1.6.5 关闭SELINUX
[root@hadoop102 ~]# swapoff -a && sed -i ‘/ swap / s/^(.)$/#\1/g’ /etc/fstab
[root@hadoop102 ~]# setenforce 0 && sed -i 's/^SELINUX=.
/SELINUX=disabled/’ /etc/selinux/config
1.6.6 针对K8S调整内核参数
[root@hadoop102 ~]# cat > kubernetes.conf <<EOF
net.bridge.bridge-nf-call-iptables=1
net.bridge.bridge-nf-call-ip6tables=1
net.ipv4.ip_forward=1
net.ipv4.tcp_tw_recycle=0
vm.swappiness=0 # 禁止使用 swap 空间,只有当系统 OOM 时才允许使用它
vm.overcommit_memory=1 # 不检查物理内存是否够用
vm.panic_on_oom=0 # 开启 OOM
fs.inotify.max_user_instances=8192
fs.inotify.max_user_watches=1048576
fs.file-max=52706963
fs.nr_open=52706963
net.ipv6.conf.all.disable_ipv6=1
net.netfilter.nf_conntrack_max=2310720
EOF
[root@hadoop102 ~]# cp kubernetes.conf /etc/sysctl.d/kubernetes.conf
[root@hadoop102 ~]# sysctl -p /etc/sysctl.d/kubernetes.conf
1.6.7 调整系统时区

设置系统时区为 中国/上海

[root@hadoop102 ~]# timedatectl set-timezone Asia/Shanghai

将当前的 UTC 时间写入硬件时钟

[root@hadoop102 ~]# timedatectl set-local-rtc 0

重启依赖于系统时间的服务

[root@hadoop102 ~]# systemctl restart rsyslog
[root@hadoop102 ~]# systemctl restart crond
1.6.8 关闭系统不需要的服务
[root@hadoop102 ~]# systemctl stop postfix && systemctl disable postfix
1.6.9 设置 rsyslogd 和 systemd journald
#持久化保存日志的目录
[root@hadoop102 ~]# mkdir /var/log/journal
[root@hadoop102 ~]# mkdir /etc/systemd/journald.conf.d
[root@hadoop102 ~]# cat > /etc/systemd/journald.conf.d/99-prophet.conf <<EOF
[Journal]

持久化保存到磁盘

Storage=persistent

压缩历史日志

Compress=yes

SyncIntervalSec=5m
RateLimitInterval=30s
RateLimitBurst=1000

最大占用空间 10G

SystemMaxUse=10G

单日志文件最大 200M

SystemMaxFileSize=200M

日志保存时间 2 周

MaxRetentionSec=2week

不将日志转发到 syslog

ForwardToSyslog=no
EOF
[root@hadoop102 ~]# systemctl restart systemd-journald
1.6.10 升级系统内核为4.4
CentOS 7.x 系统自带的 3.10.x 内核存在一些 Bugs,导致运行的 Docker、Kubernetes 不稳定,所以我们将系统内核升级为4.4版本。
[root@hadoop102 ~]# rpm -Uvh http://www.elrepo.org/elrepo-release-7.0-3.el7.elrepo.noarch.rpm

安装完成后检查 /boot/grub2/grub.cfg 中对应内核 menuentry 中是否包含 initrd16 配置,如果没有,再安装一次!

[root@hadoop102 ~]# yum --enablerepo=elrepo-kernel install -y kernel-lt

设置开机从新内核启动

[root@hadoop102 ~]# grub2-set-default ‘CentOS Linux (4.4.204-1.el7.elrepo.x86_64) 7 (Core)’
#重启机器后查看内核版本
[root@hadoop102 ~]# uname -r
4.4.204-1.el7.elrepo.x86_64
第2章 Docker
2.1 简介
Docker 最初是 dotCloud 公司创始人 Solomon Hykes 在法国期间发起的一个公司内部项目,它是基于 dotCloud 公司多年云服务技术的一次革新,并于 2013 年 3 月以 Apache 2.0 授权协议开源,主要项目代码在 GitHub 上进行维护。Docker 项目后来还加入了 Linux 基金会。
Docker 自开源后受到广泛的关注和讨论,至今其 GitHub 项目 已经超过 5 万 4 千个星标和一万多个 fork。甚至由于 Docker 项目的火爆,在 2013 年底,dotCloud 公司决定改名为 Docker。Docker 最初是在 Ubuntu 12.04 上开发实现的;Red Hat 则从 RHEL 6.5 开始对 Docker 进行支持;Google 也在其 PaaS 产品中广泛应用 Docker。
Docker 使用 Google 公司推出的 Go 语言 进行开发实现,对进程进行封装隔离,属于操作系统层面的虚拟化技术。由于隔离的进程独立于宿主和其它的隔离的进程,因此也称其为容器。
2.2 Docker的优点
2.2.1 高效利用系统资源
由于容器不需要进行硬件虚拟以及运行完整操作系统等额外开销,Docker 对系统资源的利用率更高。无论是应用执行速度、内存损耗或者文件存储速度,都要比传统虚拟机技术更高效。因此,相比虚拟机技术,一个相同配置的主机,往往可以运行更多数量的应用。
2.2.2 快速启动
传统的虚拟机技术启动应用服务往往需要数分钟,而 Docker 容器应用,由于直接运行于宿主内核,无需启动完整的操作系统,因此可以做到秒级、甚至毫秒级的启动时间。大大的节约了开发、测试、部署的时间。
2.2.3 运行环境一致
开发过程中一个常见的问题是环境一致性问题。由于开发环境、测试环境、生产环境不一致,导致有些 bug 并未在开发过程中被发现。而 Docker 的镜像提供了除内核外完整的运行时环境,确保了应用运行环境一致性,从而不会再出现 「这段代码在我机器上没问题啊」 这类问题。
2.2.4 持续交付和部署
对开发和运维人员来说,最希望的就是一次创建或配置,可以在任意地方正常运行。
使用 Docker 可以通过定制应用镜像来实现持续集成、持续交付、部署。开发人员可以通过 Dockerfile 来进行镜像构建和测试,而运维人员则可以直接在生产环境中快速部署该镜像,甚至进行自动部署。
而且使用 Dockerfile 使镜像构建透明化,不仅仅开发团队可以理解应用运行环境,也方便运维团队理解应用运行所需条件,帮助更好的生产环境中部署该镜像。
2.2.5 可移植性
由于 Docker 确保了执行环境的一致性,使得应用的迁移更加容易。Docker 可以在很多平台上运行,无论是物理机、虚拟机、公有云、私有云,甚至是笔记本,其运行结果是一致的。因此用户可以很轻易的将在一个平台上运行的应用,迁移到另一个平台上,而不用担心运行环境的变化导致应用无法正常运行的情况。
2.2.6 对比传统虚拟机
特性 容器 虚拟机
启动 秒级 分钟级
硬盘 一般为MB 一般为GB
性能 接近原生 较差
系统支持 单机支持上千容器 一般几十个
2.3 基本概念
Docker 包括三个基本概念:
 镜像(Image)
 容器(Container)
 仓库(Repository)
2.3.1 镜像
我们都知道,操作系统分为内核和用户空间。对于 Linux 而言,内核启动后,会挂载 root 文件系统为其提供用户空间支持。而 Docker 镜像(Image),就相当于是一个 root 文件系统。
Docker 镜像是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等)。镜像不包含任何动态数据,其内容在构建之后也不会被改变。
镜像构建时,会一层层构建,前一层是后一层的基础。每一层构建完就不会再发生改变,后一层上的任何改变只发生在自己这一层。
2.3.2 容器
镜像(Image)和容器(Container)的关系,就像是面向对象程序设计中的 类 和 实例 一样,镜像是静态的定义,容器是镜像运行时的实体。容器可以被创建、启动、停止、删除、暂停等。
容器的实质是进程,但与直接在宿主执行的进程不同,容器进程运行于属于自己的独立的 命名空间。因此容器可以拥有自己的 root 文件系统、自己的网络配置、自己的进程空间,甚至自己的用户 ID 空间。
2.3.3 仓库
镜像构建完成后,可以很容易的在当前宿主机上运行,但是,如果需要在其它服务器上使用这个镜像,我们就需要一个集中的存储、分发镜像的服务,Docker Registry 就是这样的服务。
一个 Docker Registry 中可以包含多个 仓库(Repository);每个仓库可以包含多个标签(Tag);每个标签对应一个镜像。
通常,一个仓库会包含同一个软件不同版本的镜像,而标签就常用于对应该软件的各个版本。我们可以通过 <仓库名>:<标签> 的格式来指定具体是这个软件哪个版本的镜像。如果不给出标签,将以 latest 作为默认标签。
以 Centos镜像 为例,centos是仓库的名字,其内包含有不同的版本标签,如6.8, 7.5。我们可以通过 centos:6.8 来具体指定所需哪个版本的镜像。如果忽略了标签,比如 centos,那将视为 centos:latest。
仓库名经常以 两段式路径 形式出现,比如atguigu/hadoop,前者往往意味着 Docker Registry 多用户环境下的用户名,后者则往往是对应的软件名。
2.4 安装Docker(三台机器)
Docker CE 支持 64 位CentOS 7,并且要求内核版本不低于3.10。
2.4.1 安装依赖包
[root@hadoop102 ~]# yum install -y yum-utils device-mapper-persistent-data lvm2
2.4.2 更换yum源为国内
[root@hadoop102 ~]# yum-config-manager
–add-repo
http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
2.4.3 安装Docker-CE
[root@hadoop102 ~]# yum update -y && yum install -y docker-ce
[root@hadoop102 ~]# reboot
2.4.4 更改内核版本
重启后我们通过命令发现内核版本又变成历史版本,所以我们需要更改一下默认内核版本。
[root@hadoop102 ~]# uname -r
3.10.0-1062.4.3.el7.x86_64
[root@hadoop102 ~]# grub2-set-default ‘CentOS Linux (4.4.204-1.el7.elrepo.x86_64) 7 (Core)’ && reboot
2.4.5 启动Docker
[root@hadoop102 ~]# systemctl start docker
#设为开机自启
[root@hadoop102 ~]# systemctl enable docker
2.4.6 配置Docker
[root@hadoop102 ~]# cat > /etc/docker/daemon.json <<EOF
{
“exec-opts”: [“native.cgroupdriver=systemd”],
“log-driver”: “json-file”,
“log-opts”: {
“max-size”: “100m”
}
}
EOF
#存放docker的自配置文件
[root@hadoop102 ~]# mkdir -p /etc/systemd/system/docker.service.d

重启docker服务

[root@hadoop102 ~]# systemctl daemon-reload && systemctl restart docker && systemctl enable docker
参数解析:
exec-opts:配置cgroup组为systemd
log-driver:将日志存储为json类型
max-size:日志大小
2.4.7 简单使用
(1)拉取镜像
从 Docker 镜像仓库获取镜像的命令是 docker pull,其命令格式为:
docker pull [选项] [Docker Registry 地址[:端口号]/]仓库名[:标签]
 Docker 镜像仓库地址:地址的格式一般是 <域名/IP>[:端口号]。默认地址是 Docker Hub。
 仓库名:如之前所说,这里的仓库名是两段式名称,即 <用户名>/<软件名>。对于 Docker Hub,如果不给出用户名,则默认为 library,也就是官方镜像。
下面我们拉取centos镜像。
[root@hadoop102 ~]# docker pull centos:6.8

6.8: Pulling from library/centos
7ce0cebb9dca: Pull complete
Digest: sha256:39abd0c8e375de6fb7334d42ec2a46643f34cbc1bbaf37e2b484065f05eaa7a2
Status: Downloaded newer image for centos:6.8
docker.io/library/centos:6.8
上面的命令中没有给出 Docker 镜像仓库地址,因此将会从 Docker Hub (官方镜像仓库)获取镜像。而镜像名称是 centos:6.8,因此将会获取官方镜像 library/centos 仓库中标签为 6.8 的镜像。
(2)查看镜像
[root@hadoop102 ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
centos 6.8 82f3b5f3c58f 8 months ago 195MB
(3)启动容器
有了镜像后,我们就能够以这个镜像为基础启动并运行一个容器。以上面的 centos:6.8为例,如果我们打算启动里面的 bash 并且进行交互式操作的话,可以执行下面的命令。
[root@hadoop102 ~]# docker run -it --rm centos:6.8 bash
[root@5935046f6b00 /]#
[root@5935046f6b00 /]# exit
exit
[root@hadoop102 ~]#
此时我们已经处于该容器的命令行中,5935046f6b00为容器ID。
参数解析:
 docker run :运行容器的命令。
 -it:这是两个参数,一个是 -i:交互式操作,一个是 -t 终端。我们这里打算进入 bash 执行一些命令并查看返回结果,因此我们需要交互式终端。
 --rm:这个参数是说容器退出后随之将其删除。默认情况下,为了排障需求,退出的容器并不会立即删除,除非手动 docker rm。我们这里只是随便执行个命令,看看结果,不需要排障和保留结果,因此使用 --rm 可以避免浪费空间。
 centos:6.8:要创建容器的镜像。
 bash:放在镜像后的是命令,这里我们希望有个交互式 Shell,因此用的是 bash。
2.5 Dockerfile(扩展)
2.5.1 介绍
Dockfile 是一种被Docker 程序解释的脚本,Dockerfile由一条一条的指令组成,每条指令对应Linux 下面的一条命令。Docker 程序将这些Dockerfile 指令翻译真正的Linux命令。Dockerfile有自己书写格式和支持的命令,Docker程序解决这些命令间的依赖关系, Docker程序将读取Dockerfile,根据指令生成定制的 image
2.5.2 FROM
构建指令,必须指定且需要在Dockerfile其他指令的前面。后续的指令都依赖于该指令指定的image。
FROM指令指定的基础image可以是官方远程仓库中的,也可以位于本地仓库。
FROM centos:6.8
2.5.3 MAINTAINER
构建指令,用于将image的制作者相关的信息写入到image中。
MAINTAINER atguigu
2.5.4 RUN
构建指令,RUN可以运行任何被基础image支持的命令。如基础image选择了Centos,那么软件管理部分只能使用Centos的包管理命令。
RUN echo ‘hello atguigu!’
注意:Dockerfile中每一个RUN命令都会分一层,尽量将多条命令写成一行。
2.5.5 ENV
构建指令,在image中设置一个环境变量,在该指令后面的其他操作就可以使用此环境变量,比如要设置jdk,则可以这样写:
ENV JAVA_HOME /opt/module/jdk_1.8
2.5.6 COPY
复制文件或文件夹到容器中。
COPY <源路径> <目标路径>
注意:源路径指的是构建上下文路径,目标路径则是容器内的绝对路径。
2.5.7 CMD
设置指令,用于container启动时指定的操作。该操作可以是执行自定义脚本,也可以是执行系统命令。
该指令只能在文件中存在一次,如果有多个,则只执行最后一条。例如:
CMD echo ‘hello’
2.5.8 ENTRYPOINT
设置指令,指定容器启动时执行的命令,可以多次设置,但是只有最后一个有效,例如:
ENTRYPOINT ls -l
该指令的使用分为两种情况,一种是独自使用,另一种和CMD指令配合使用。
当独自使用时,如果你还使用了CMD命令且CMD是一个完整的可执行的命令,那么CMD指令和ENTRYPOINT会互相覆盖只有最后一个CMD或者ENTRYPOINT有效,如下面的例子CMD不会执行,只有ENTRYPOINT会被执行。
CMD echo ’ hello’
ENTRYPOINT ls -l
另一种用法和CMD指令配合使用来指定ENTRYPOINT的默认参数,这时CMD指令不是一个完整的可执行命令,仅仅是参数部分;ENTRYPOIVT指令只能使用JSON方式指定执行命令,而不能指定,如:
FROM centos
CMD ["-l"]
ENTRYPOINT ["/usr/bin/ls"]
2.5.9 USER
指定启动容器后的用户,该用户需要提前存在,否则无法切换。
USER atguigu
2.5.10 EXPOSE
设置指令,该指令会将容器中的端口映射成宿主机器中的某个端口。当你需要访问容器的时候,可以不是用容器的IP地址而是使用宿主机器的P地址和映射后的端口。要完成整个操作需要两个步骤,首先在Dockerfile 使用EXPOSE设置需要映射的容器端口,然后在运行容器的时候指定-p选项加上EXPOSE设置的端口,这样EXPOSE设置的端口号会被随机映射成宿主机器中的一个端口号。
也可以指定需要映射到宿主机器的那个端口,这时要确保宿主机器上的端口号没有被使用。EXPOSE指令可以一次设置多个端口号,相应的运行容器的时候,可以配套的多次使用-p选项。
映射一个端口
EXPOSE 22
相应的运行容器使用的命令
docker run -p port1 image

映射多个端口
EXPOSE port1 port2 port3
相应的运行容器使用的命令
docker run -p port1 -p port2-p port3 image
还可以指定需要映射到宿主机器上的某个端口号
docker run -p host_portl:port1 -p host_port2:port2 -p host_port3:port3 image
2.5.11 VOLUME
设置指令,使容器中的一个目录具有持久化存储数据的功能,该目录可以被容器本身使用,也可以共享给其他容器使用。容器使用的是AUFS,这种文件系统不能持久化数据,当容器关闭后,所有的更改都会丢失。当容器中的应用有持久化数据的需求时可以在Dockerfile中使用该指令。
VOLUME ["/tmp/data"]
2.5.12 WORKDIR
设置指令,可以多次切换(相当于cd命令),对RUN,CMD,ENTRYPOINT生效。
WORKDIR /opt
第3章 Kubernetes
3.1 简介
Kubernetes简称k8s,是Google开源的一个跨主机集群的容器调度平台,它可以自动化应用容器的部署、扩展和操作 , 提供以容器为中心的基础架构。
3.2 特点
可移植: 支持公有云,私有云,混合云,多重云(multi-cloud)
可扩展: 模块化,插件化,可挂载,可组合
自动化: 自动部署,自动重启,自动复制,自动伸缩/扩展
3.3 常用对象
3.3.1 pod
Pod是最小部署单元,一个Pod由一个或多个容器组成,Pod中容器共享存储和网络。
3.3.2 volume
数据卷,是Pod中能够被多个容器访问的共享目录。定义在Pod之上,被一个Pod里的多个容器挂载到具体的文件目录之下;与Pod生命周期相同。可以让一个Pod里的多个容器共享文件、让容器的数据写到宿主机的磁盘上或者写文件到 共享存储中。
3.3.3 controller
它用来管理pods,可以确保pod健康,确定并动态调整pod 在集群中中运行的数量,并在pod失败后重新调度新的pod启动,还可以进行一些定时任务。
3.3.4 service
pod是非持久的,会不断的重启,导致pod的ip是随时变化的,同时pod的数量会是动态变化的,客户端很难和pod直接通讯,service是用来解决这一问题的。service 为提供同一服务的pods 提供了统一的入口,生命周期内其绑定ip是不会变化的。
3.4 组成

3.5 安装k8s集群
3.5.1 安装Kubeadm(三台机器)
#导入阿里云的yum仓库
[root@hadoop102 ~]# cat < /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=http://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=0
repo_gpgcheck=0
gpgkey=http://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg http://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg
EOF
#开始安装
[root@hadoop102 ~]# yum -y install kubeadm-1.15.1 kubectl-1.15.1 kubelet-1.15.1
#设置开机自启
[root@hadoop102 ~]# systemctl enable kubelet.service
3.5.2 导入镜像(三台机器)
初始化k8s集群时会从官方仓库pull镜像,此时需要访问外网,这里已经提前下载好了,只需将kubeadm-basic.images.tar.gz镜像导入hadoop102,hadoop103,hadoop104三台机器。
[root@hadoop102 software]# ls
kubeadm-basic.images.tar.gz
[root@hadoop102 software]# tar -zxvf kubeadm-basic.images.tar.gz
#写个脚本导入镜像
[root@hadoop102 software]# vim load-image.sh
#!/bin/bash

ls /opt/software/kubeadm-basic.images > /tmp/image-list.txt

cd /opt/software/kubeadm-basic.images

for i in $( cat /tmp/image-list.txt )
do
docker load -i $i
done

rm -rf /tmp/image-list.txt
[root@hadoop102 software]# chmod +x load-image.sh
[root@hadoop102 software]# ./load-image.sh
查看导入结果
[root@hadoop102 software]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
k8s.gcr.io/kube-proxy v1.15.1 89a062da739d 4 months ago 82.4MB
k8s.gcr.io/kube-apiserver v1.15.1 68c3eb07bfc3 4 months ago 207MB
k8s.gcr.io/kube-controller-manager v1.15.1 d75082f1d121 4 months ago 159MB
k8s.gcr.io/kube-scheduler v1.15.1 b0b3c4c404da 4 months ago 81.1MB
centos 6.8 82f3b5f3c58f 8 months ago 195MB
k8s.gcr.io/coredns 1.3.1 eb516548c180 10 months ago 40.3MB
k8s.gcr.io/etcd 3.3.10 2c4adeb21b4f 12 months ago 258MB
k8s.gcr.io/pause 3.1 da86e6ba6ca1 23 months ago 742kB
3.5.3 初始化主节点
[root@hadoop102 ~]# mkdir -p install-k8s/core
[root@hadoop102 ~]# cd install-k8s/core
[root@hadoop102 core]# kubeadm config print init-defaults > kubeadm-config.yaml
[root@hadoop102 core]# vim kubeadm-config.yaml
localAPIEndpoint:
advertiseAddress: 192.168.79.102
bindPort: 6443
kubernetesVersion: v1.15.1
networking:
dnsDomain: cluster.local
podSubnet: “10.244.0.0/16”
serviceSubnet: 10.96.0.0/12
scheduler: {}

apiVersion: kubeproxy.config.k8s.io/v1alpha1
kind: KubeProxyConfiguration
featureGates:
SupportIPVSProxyMode: true
mode: ipvs
[root@hadoop102 core]# kubeadm init --config=kubeadm-config.yaml --experimental-upload-certs | tee kubeadm-init.log
3.5.4 加入主节点及工作节点
查看kubeadm-init.log可以发现下面的内容,执行下面红色内容。
Your Kubernetes control-plane has initialized successfully!

To start using your cluster, you need to run the following as a regular user:
这里是加入主节点,在hadoop102执行
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown ( i d − u ) : (id -u): (idu):(id -g) $HOME/.kube/config

You should now deploy a pod network to the cluster.
Run “kubectl apply -f [podnetwork].yaml” with one of the options listed at:
https://kubernetes.io/docs/concepts/cluster-administration/addons/

Then you can join any number of worker nodes by running the following on each as root:
这里是加入子节点,在其余两台机器执行,注意:每个人不一样
kubeadm join 192.168.79.102:6443 --token abcdef.0123456789abcdef
–discovery-token-ca-cert-hash sha256:91ed76e9852dee956404390fd3d741a425de68c9456fc37228d8289b9c6ef548
[root@hadoop102 core]# kubectl get node
NAME STATUS ROLES AGE VERSION
hadoop102 NotReady master 3m49s v1.15.1
hadoop103 NotReady 5s v1.15.1
hadoop104 NotReady 3s v1.15.1
3.5.5 部署网络
现在所有机器的状态都是NotReady,我们需要配置flannel网络来让节点工作。
[root@hadoop102 ~]# cd install-k8s/
[root@hadoop102 install-k8s]# mkdir -p plugin/flannel
[root@hadoop102 install-k8s]# cd plugin/flannel
[root@hadoop102 flannel]# wget https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml --no-check-certificate
[root@hadoop102 flannel]# kubectl create -f kube-flannel.yml
查看节点状态
[root@hadoop102 flannel]# kubectl get node
NAME STATUS ROLES AGE VERSION
hadoop102 Ready master 10m v1.15.1
hadoop103 Ready 6m31s v1.15.1
hadoop104 Ready 6m29s v1.15.1
状态切换需要时间,发现还是NotReady需耐心等待!
3.6 helm
3.6.1 什么是helm
Helm 是 Kubernetes 的包管理器。包管理器类似于我们在 Ubuntu 中使用的apt、Centos中使用的yum 或者Python中的 pip 一样,能快速查找、下载和安装软件包。Helm 由客户端组件 helm 和服务端组件 Tiller 组成, 能够将一组K8S资源打包统一管理, 是查找、共享和使用为Kubernetes构建的软件的最佳方式。
3.6.2 重要概念
(1)chart
chart是创建一个应用的信息集合,包括各种Kubernetes对象的配置模板、参数定义、依赖关系、文档说明等。chart是应用部署的自包含逻辑单元。可以将chart想象成yum中的软件安装包。
(2)release
release是chart的运行实例,代表了一个正在运行的应用。当chart被安装到Kubernetes集群,就生成一个release。chart能多次安装到同一个集群,每次安装都是一个release。

3.6.3 安装客户端
(1)上传安装包
[root@hadoop102 software]# ls
helm-v2.13.1-linux-amd64.tar.gz
(2)解压
[root@hadoop102 software]# tar -zxvf helm-v2.13.1-linux-amd64.tar.gz -C /opt/module/
[root@hadoop102 module]# ls
linux-amd64
(3)拷贝脚本
[root@hadoop102 module]# cp linux-amd64/helm /usr/local/bin/
[root@hadoop102 module]# chmod +x /usr/local/bin/helm
3.6.3 安装服务端
(1)导入tiller镜像(三台机器)
[root@hadoop102 software]# ls
helm-tiller.tar
[root@hadoop102 software]# docker load -i helm-tiller.tar
(2)创建rbac文件
[root@hadoop102 linux-amd64]# vim rbac.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: tiller
namespace: kube-system

apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
name: tiller
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:

  • kind: ServiceAccount
    name: tiller
    namespace: kube-system
    (3)使用rbac文件创建
    [root@hadoop102 linux-amd64]# kubectl create -f rbac.yaml
    serviceaccount/tiller created
    clusterrolebinding.rbac.authorization.k8s.io/tiller created
    (4)初始化tiller
    [root@hadoop102 linux-amd64]# helm init --service-account tiller --skip-refresh
    (5)查看状态
    [root@hadoop102 software]# kubectl get pod -n kube-system -l app=helm
    NAME READY STATUS RESTARTS AGE
    tiller-deploy-58565b5464-44mbn 1/1 Running 0 6m15s
    第4章 Hadoop on k8s
    4.1 拉取镜像
    4.1.1 创建存放资源的文件夹
    [root@hadoop102 module]# mkdir hadoop-helm
    [root@hadoop102 module]# cd hadoop-helm/
    4.1.2 从官方helm库拉取镜像
    地址:https://hub.helm.sh/charts/stable/hadoop
    [root@hadoop102 hadoop-helm]# helm fetch stable/hadoop --version 1.1.1
    [root@hadoop102 hadoop-helm]# ls
    hadoop-1.1.1.tgz
    4.2 修改脚本
    [root@hadoop102 hadoop-helm]# tar -zxvf hadoop-1.1.1.tgz
    [root@hadoop102 hadoop-helm]# ls
    hadoop hadoop-1.1.1.tgz
    [root@hadoop102 hadoop-helm]# cd hadoop
    [root@hadoop102 hadoop]# vim templates/hadoop-configmap.yaml
    默认为每次启动都格式化namenode,所以修改34行为:
    if [[ “${HOSTNAME}” =~ “hdfs-nn” ]]; then
    if [ -f “/root/hdfs/namenode/exsit” ]
    then
    echo “服务曾经已然初始化!”
    $HADOOP_PREFIX/sbin/hadoop-daemon.sh start namenode
    else
    mkdir -p /root/hdfs/namenode
    $HADOOP_PREFIX/bin/hdfs namenode -format -force -nonInteractive
    $HADOOP_PREFIX/sbin/hadoop-daemon.sh start namenode
    touch /root/hdfs/namenode/exsit
    fi
    fi
    修改后会判断是否格式化过namenode
    [root@hadoop102 hadoop]# vim templates/hdfs-nn-statefulset.yaml
    将54-65行注释掉
    54 #readinessProbe:
    55 # httpGet:
    56 # path: /
    57 # port: 50070
    58 # initialDelaySeconds: 5
    59 # timeoutSeconds: 2
    60 #livenessProbe:
    61 # httpGet:
    62 # path: /
    63 # port: 50070
    64 # initialDelaySeconds: 10
    65 # timeoutSeconds: 2
    [root@hadoop102 hadoop]# vim templates/hdfs-dn-statefulset.yaml
    将54-65行注释掉
    #readinessProbe:
    # httpGet:
    # path: /
    # port: 50075
    # initialDelaySeconds: 5
    # timeoutSeconds: 2
    #livenessProbe:
    # httpGet:
    # path: /
    # port: 50075
    # initialDelaySeconds: 10
    # timeoutSeconds: 2
    还可以修改datanode个数等信息,在values.yaml中修改即可。
    [root@hadoop102 hadoop]# vim values.yaml

4.3 导入hadoop镜像
(1)上传镜像(三台机器)
[root@hadoop102 software]# ls
hadoop.tar
(2)加载镜像(三台机器)
[root@hadoop102 software]# docker load -i hadoop.tar
4.4 启动chart
[root@hadoop102 hadoop]# helm install --name hadoop .
–name:为启动的chart起名。
[root@hadoop102 hadoop]# kubectl get pod
NAME READY STATUS RESTARTS AGE
hadoop-hadoop-hdfs-dn-0 1/1 Running 1 76s
hadoop-hadoop-hdfs-nn-0 1/1 Running 0 76s
hadoop-hadoop-yarn-nm-0 1/1 Running 1 76s
hadoop-hadoop-yarn-nm-1 1/1 Running 0 25s
hadoop-hadoop-yarn-rm-0 1/1 Running 0 76s
4.5 暴露端口
此时hadoop运行无误,但是此时我们不能访问,因为hadoop的端口是pod内部共享的,我们在外面用不了,所以此时需要将我们用到的端口暴露出来。
4.5.1 50070
(1)创建svc文件
[root@hadoop102 hadoop]# vim nnport.yaml
apiVersion: v1
kind: Service
metadata:
name: nnport
labels:
name: nnport
spec:
type: NodePort #这里代表是NodePort类型的,暴露端口需要此类型
ports:

  • port: 50070 #这里的端口就是要暴露的,供内部访问。
    targetPort: 50070 #端口一定要和暴露出来的端口对应
    protocol: TCP
    nodePort: 30070 # 所有的节点都会开放此端口,此端口供外部调用,需要大于30000。
    selector:
    app: hadoop
    component: hdfs-nn
    release: hadoop
    上述文件的selector要和我们此时的环境对应上,可以通过下面命令查看:
    [root@hadoop102 hadoop]# kubectl edit svc hadoop-hadoop-hdfs-nn

(2)开启端口
[root@hadoop102 hadoop]# kubectl apply -f nnport.yaml
查看:
[root@hadoop102 hadoop]# kubectl get svc
nnport NodePort 10.97.175.201 50070:30070/TCP 63s
(3)访问webui

4.5.2 8088
(1)创建svc文件(与50070同理)
[root@hadoop102 hadoop]# vim rmport.yaml
apiVersion: v1
kind: Service
metadata:
name: rmport
labels:
name: rmport
spec:
type: NodePort #这里代表是NodePort类型的
ports:

  • port: 8088
    targetPort: 8088
    protocol: TCP
    nodePort: 30088 # 所有的节点都会开放此端口,此端口供外部调用。
    selector:
    app: hadoop
    component: yarn-rm
    release: hadoop

(2)开启端口
[root@hadoop102 hadoop]# kubectl create -f rmport.yaml
(3)访问webui

4.6 测试hadoop
4.6.1 测试hdfs
(1)进入nn容器
[root@hadoop102 hadoop]# kubectl exec -it hadoop-hadoop-hdfs-nn-0 /bin/bash
root@hadoop-hadoop-hdfs-nn-0:/usr/local/hadoop-2.9.0#
(2)创建文件夹并上传文件
root@hadoop-hadoop-hdfs-nn-0:/usr/local/hadoop-2.9.0# hadoop fs -mkdir /test
root@hadoop-hadoop-hdfs-nn-0:/usr/local/hadoop-2.9.0# hadoop fs -put README.txt /test
(3)查看结果

4.6.2 测试YARN
(1)进入rm容器
[root@hadoop102 hadoop]# kubectl exec -it hadoop-hadoop-yarn-rm-0 /bin/bash
root@hadoop-hadoop-yarn-rm-0:/usr/local/hadoop-2.9.0#
(2)运行官方wordcount案例
root@hadoop-hadoop-yarn-rm-0:/usr/local/hadoop-2.9.0# pwd
/usr/local/hadoop-2.9.0
root@hadoop-hadoop-yarn-rm-0:/usr/local/hadoop-2.9.0#
hadoop jar share/hadoop/mapreduce/hadoop-mapreduce-examples-2.9.0.jar wordcount /test /result
(3)查看结果

第5章 Kafka on k8s
5.1 NFS
我们采用了nfs作为持久化存储后端,首先先安装nfs。
5.1.1 安装NFS
[root@hadoop102 ~]# yum install -y nfs-utils
[root@hadoop102 ~]# yum -y install rpcbind
5.1.2 配置NFS服务端数据目录
[root@hadoop102 ~]# mkdir -p /nfs/k8s/
[root@hadoop102 ~]# chmod 755 /nfs/k8s
[root@hadoop102 ~]# vim /etc/exports
/nfs/k8s/ *(async,insecure,no_root_squash,no_subtree_check,rw)
5.1.3 启动服务查看状态
[root@hadoop102 ~]# systemctl start nfs.service
[root@hadoop102 ~]# systemctl enable nfs.service
[root@hadoop102 ~]# showmount -e
Export list for hadoop102:
/nfs/k8s *
5.2 PV
这里采用StorageClass动态实现PV的方式,创建好StorageClass对象,只要在helm的kafka的value文件中指定storageClass为StorageClass名称即可动态在NFS上创建PV以及PVC。
5.2.1 创建Provisioner
Provisioner是NFS的自动配置程序,可以使用自动配置好的NFS服务器,来自动创建持久卷,也就是自动创建PV。
[root@hadoop102 ~]# mkdir /opt/module/kafka-helm/
[root@hadoop102 ~]# cd /opt/module/kafka-helm/
[root@hadoop102 kafka-helm]# vim nfs-client.yaml
kind: Deployment
apiVersion: extensions/v1beta1
metadata:
name: nfs-client-provisioner-new
spec:
replicas: 1
strategy:
type: Recreate
template:
metadata:
labels:
app: nfs-client-provisioner
spec:
serviceAccountName: default-admin
containers:
- name: nfs-client-provisioner
image: quay.io/external_storage/nfs-client-provisioner:latest
volumeMounts:
- name: nfs-client-root
mountPath: /persistentvolumes
env:
- name: PROVISIONER_NAME
value: fuseim.pri/ifs
- name: NFS_SERVER
value: 192.168.79.102
- name: NFS_PATH
value: /nfs/k8s
volumes:
- name: nfs-client-root
nfs:
server: 192.168.79.102
path: /nfs/k8s
[root@hadoop102 kafka-helm]# kubectl create -f nfs-client.yaml
deployment.extensions/nfs-client-provisioner-new created
5.2.2 创建ServiceAccount
给Provisioner授权,使得Provisioner拥有对NFS增删改查的权限。
[root@hadoop102 kafka-helm]# vim nfs-client-sa.yaml

apiVersion: v1
kind: ServiceAccount
metadata:
labels:
k8s-app: gmo
name: default-admin
namespace: default


apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
name: default-crb
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:

  • kind: ServiceAccount
    name: default-admin
    namespace: default
    其中的ServiceAccount name需要和nfs-client.yaml中的serviceAccountName一致。
    [root@hadoop102 kafka-helm]# kubectl create -f nfs-client-sa.yaml
    serviceaccount/default-admin created
    clusterrolebinding.rbac.authorization.k8s.io/default-crb created
    5.2.3 创建StorageClass对象
    [root@hadoop102 kafka-helm]# vim nfs-client-class.yaml
    apiVersion: storage.k8s.io/v1
    kind: StorageClass
    metadata:
    name: kafka-nfs-storage
    provisioner: fuseim.pri/ifs
    provisioner需要和nfs-client.yaml中的PROVISIONER_NAME一致
    创建:
    [root@hadoop102 kafka-helm]# kubectl create -f nfs-client-class.yaml
    storageclass.storage.k8s.io/kafka-nfs-storage created
    5.3 Kafka
    5.3.1 获取安装包
    [root@hadoop102 kafka-helm]# helm repo add incubator http://storage.googleapis.com/kubernetes-charts-incubator
    [root@hadoop102 kafka-helm]# helm fetch incubator/kafka --version 0.11.0
    [root@hadoop102 kafka-helm]# ls
    kafka-0.11.0.tgz
    [root@hadoop102 kafka-helm]# tar -zxvf kafka-0.11.0.tgz
    [root@hadoop102 kafka-helm]# ls
    kafka kafka-0.11.0.tgz
    5.3.2 导入镜像
    将k8szk.tar和kafka.tar上传到集群,三台机器都导入镜像。
    [root@hadoop102 kafka-helm]# ls /opt/software/
    k8szk.tar kafka.tar
    [root@hadoop102 kafka-helm]# docker load -i /opt/software/k8szk.tar
    [root@hadoop102 kafka-helm]# docker load -i /opt/software/kafka.tar
    [root@hadoop102 kafka-helm]# docker images
    confluentinc/cp-kafka 4.1.2-2 c9df5cafe23b 16 months ago 559MB
    registry.cn-hangzhou.aliyuncs.com/appstore/k8szk v2 2fd25e05d6e2 2 years ago 284MB
    5.3.3 修改基本配置
    (1)修改zookeeper镜像
    [root@hadoop102 kafka]# pwd
    /opt/module/kafka-helm/kafka
    [root@hadoop102 kafka]# vim charts/zookeeper/templates/statefulset.yaml
    将- name: zookeeper后面也就是50行image后面的内容修改为:
    image: “registry.cn-hangzhou.aliyuncs.com/appstore/k8szk:v2”
    (2)zookeeper添加持久化存储
    [root@hadoop102 kafka]# vim values.yaml
    persistence:
    enabled: true
    size: “1Gi”
    mountPath: “/opt/kafka/data”
    storageClass: “kafka-nfs-storage”
    (3)kafka添加持久化存储
    [root@hadoop102 kafka]# vim values.yaml
    zookeeper:
    enabled: true
    resources: ~
    env:
    ZK_HEAP_SIZE: “1G”
    persistence:
    enabled: true
    size: “1Gi”
    storageClass: “kafka-nfs-storage”
    (4)配置kafka
    [root@hadoop102 kafka]# vim values.yaml
    在configurationOverrides:下添加配置,例如:
    configurationOverrides:
    “offsets.topic.replication.factor”: 3
    “log.cleaner.enable”: true
    “log.cleanup.policy”: delete
    “delete.topic.enable”: true
    5.3.4 安装kafka
    [root@hadoop102 kafka]# helm install --name kafka --namespace kafka .
    5.3.5 测试
    (1)创建topic
    kubectl -n kafka exec kafka-0 – /usr/bin/kafka-topics --zookeeper kafka-zookeeper:2181 --create --replication-factor 3 --partitions 3 --topic atguigu
    Created topic “atguigu”.
    (2)列出topic
    kubectl -n kafka exec kafka-0 – /usr/bin/kafka-topics --zookeeper kafka-zookeeper:2181 --list
    __confluent.support.metrics
    atguigu
    (3)查看topic
    kubectl -n kafka exec kafka-0 – /usr/bin/kafka-topics --zookeeper kafka-zookeeper:2181 --describe --topic atguigu
    (4)消费数据
    kubectl -n kafka exec -ti kafka-1 – /usr/bin/kafka-console-consumer --bootstrap-server kafka:9092 --topic atguigu --from-beginning
    (5)生产数据
    kubectl -n kafka exec -ti kafka-2 – /usr/bin/kafka-console-producer --broker-list kafka:9092 --topic atguigu
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1. 什么是Docker?为什么要使用DockerDocker是一种容器化技术,可以将应用程序打包成容器,使其能够在任何环境中运行,而不需要担心环境差异问题。Docker可以提高应用程序的可移植性、可伸缩性和安全性。 2. Docker和虚拟机的区别是什么? 虚拟机是一种完整的操作系统虚拟化技术,而Docker是一种应用程序级别的虚拟化技术。虚拟机需要模拟整个操作系统,包括内核、系统服务等,而Docker只需要模拟应用程序的运行环境。因此,Docker比虚拟机更轻量级、更快速、更节省资源。 3. Docker的三个基本组件是什么? Docker的三个基本组件是Docker Engine、Docker Hub和Docker CLI。Docker Engine是Docker的核心组件,负责管理Docker容器和镜像。Docker Hub是Docker的公共镜像仓库,用户可以在其中分享和下载镜像。Docker CLI是Docker的命令行接口,用户可以使用Docker CLI来创建、管理和运行Docker容器。 4. Docker的镜像和容器是什么? Docker镜像是一个静态的文件,包含了应用程序运行所需的所有文件和依赖项。Docker容器是一个运行中的实例,它基于Docker镜像创建,包含了应用程序的运行环境和状态。 5. 如何创建Docker镜像? 创建Docker镜像的基本步骤是:创建一个Dockerfile文件,定义Docker镜像的构建过程;使用docker build命令构建Docker镜像;使用docker push将Docker镜像推送到Docker Hub镜像仓库中。 6. 如何创建Docker容器? 创建Docker容器的基本步骤是:使用docker run命令创建Docker容器,并指定所需的Docker镜像和容器的名称;使用docker start、docker stop、docker restart等命令管理Docker容器的生命周期。 7. 如何保证Docker容器的安全性? 保证Docker容器的安全性可以从以下几个方面入手:使用最小化的Docker镜像和应用程序;限制Docker容器的权限;监控Docker容器的运行状态;及时更新Docker镜像和应用程序等。 8. 如何扩展Docker容器? 扩展Docker容器可以从以下几个方面入手:使用Docker Swarm或Kubernetes等容器编排工具管理多个Docker容器;使用Docker Compose将多个Docker容器组合成一个应用程序;使用Docker网络使多个Docker容器之间进行通信。 9. 如何在Docker容器中运行多个服务? 在Docker容器中运行多个服务可以使用以下几种方式:使用Docker Compose将多个Docker容器组合成一个应用程序;使用Docker网络将多个Docker容器连接在一起;在Docker容器中运行多个进程,使用supervisord等工具管理进程。 10. 如何实现Docker容器的持久化存储? 实现Docker容器的持久化存储可以从以下几个方面入手:使用Docker数据卷将数据存储在主机上;使用Docker网络将数据存储在其他Docker容器中;使用第三方存储系统,如NFS、GlusterFS等。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值