标签:编辑 指示 命名空间 ade nat selinux 加入集群 center 权限控制
首先要了解什么是容器,容器就是一个操作系统的最小镜像,在上面可以运行应用程序(可以认为是轻量级的虚拟机,但是这玩意和虚拟机的实现原理不一样)。我们常用的容器就是docker。docker的出现让部署服务和系统运维的效率有了极大地提高,可以做到开发环境无缝迁移到生产环境。在生产环境中经常做的就是部署成千上万的服务,然后在这个基础上做负载均衡之类的事情,有了docker就能通过部署对应服务的容器镜像完成。有了docker还不够,因为我们需要管理成千上万的容器,于是就有了容器调度工具比如k8s之类的,我们可以做到监控容器运行状态,然后自动重启一些坏掉的容器,相当于做了热备份。把容器和容器调度跑到云端,这样结合起来的服务就是容器云。
相比于虚拟机,有什么特点?
(1)可移植性
目前容器技术的现代形式主要体现在应用程序容器化(如 Docker)和系统容器化(如 LXC)中。这两种形式的容器都能让 IT 团队从底层架构中抽象出程序代码,从而实现跨各种部署环境的可移植性。
(2)轻量级
容器通常位于物理服务器及其主机操作系统之上。它可以通过单个操作系统安装来运行多个工作环境。因此容器特别 “轻” —— 它们只有几兆字节,只需几秒钟即可启动。
(3)降低成本
与虚拟机相比,内存,CPU 和存储效率的提高是容器技术的关键优势。由于可以在同一基础架构上支持更多容器,那么这些资源的减少就可以转化为巨大的成本节省,同时还可以减少管理开销。 虚拟机容器重量级轻量级表现有限原生表现每个 VM 都在自己的操作系统中运行所有容器共享主机操作系统硬件级虚拟化操作系统虚拟化启动时间(以分钟为单位)启动时间(以毫秒为单位)分配所需的内存需要更少的内存完全隔离进程级隔离。
Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的Linux机器或Windows 机器上,也可以实现虚拟化,容器是完全使用沙箱机制,相互之间不会有任何接口。
一个完整的Docker有以下几个部分组成:
DockerClient客户端
Docker Daemon守护进程
Docker Image镜像
DockerContainer容器
Docker 使用客户端-服务器(C/S)架构模式,使用远程API(应用程序编程接口)来管理和创建Docker容器。Docker容器通过 Docker 镜像来创建。容器与镜像的关系类似于面向对象编程中的对象与类。
Docker采用 C/S架构 Docker daemon 作为服务端接受来自客户的请求,并处理这些请求(创建、运行、分发容器)。 客户端和服务端既可以运行在一个机器上,也可通过 socket 或者RESTful API 来进行通信。
Docker daemon 一般在宿主主机后台运行,等待接收来自客户端的消息。 Docker 客户端则为用户提供一系列可执行命令,用户用这些命令实现跟 Docker daemon 交互。
Docker并不是全能的,设计之初也不是KVM之类虚拟化手段的替代品,简单总结几点:
(1)Docker是基于Linux 64bit的,无法在32bit的linux/Windows/unix环境下使用。
(2)LXC(Linux container容器是一种内核虚拟化技术)是基于cgroup等linux kernel功能的,因此container的guest系统只能是linux base的。
(3)隔离性相比KVM之类的虚拟化方案还是有些欠缺,所有container公用一部分的运行库。
(4)网络管理相对简单,主要是基于namespace隔离。
(5)cgroup的cpu和cpuset提供的cpu功能相比KVM的等虚拟化方案相比难以度量(所以dotcloud主要是按内存收费)。
(6)Docker对disk的管理比较有限。
(7)container随着用户进程的停止而销毁,container中的log等用户数据不便收集。
官方的Docker Hub是一个用于管理公共镜像的仓库,用户可以在上面找到需要的镜像,也可以把私有镜像推送上去。但是,在生产环境中,往往需要拥有一个私有的镜像仓库用于管理镜像,通过开源软件Registry可以实现这个目标。
Registry在Github上有两份代码:老代码库和新代码库。老代码是采用Python编写的,存在pull和push的性能问题,在0.9.1版本之后就标志为deprecated,意思为不再继续开发。从2.0版本开始,Registry就在新代码库进行开发,新代码库采用Go语言编写,修改了镜像id的生成算法、Registry上镜像的保存结构,大大优化了pull和push镜像的效率。
官方在Docker Hub上提供了Registry的镜像,可以直接使用该Registry镜像来构建一个容器,搭建私有仓库服务。
Docker Hub提供了很多镜像,但在实际工作中,Docker Hub中的镜像并不能满足工作的需要,往往需要构建自定义镜像。构建自定义镜像主要有两种方式:docker commit和Dockerfile。
可以将docker commit视为在以往版本控制系统里提交变更,然后进行变更的提交即可。docker commit、docker export和docker add类似都可以输出image,但是最好的生成image的方法还是使用Dockerfile。
Dockerfile是由一系列命令和参数构成的脚本,这些命令应用于基础镜像并最终创建一个新的镜像。它们简化了从头到尾的流程并极大地简化了部署工作。Dockerfile从FROM命令开始,紧接着跟随各种方法、命令和参数,其产出为一个新的可以用于创建容器的镜像。
ortainer是Docker的图形化管理工具,提供状态显示面板、应用模板快速部署、容器镜像网络数据卷的基本操作(包括上传和下载镜像、创建容器等操作)、事件日志显示、容器控制台操作、Swarm集群和服务等集中管理和操作、登录用户管理和控制等功能。功能十分全面,基本能满足中小型企业对容器管理的全部需求。
Kubernetes(简称K8S)是开源的容器集群管理系统,可以实现容器集群的自动化部署、自动扩缩容、维护等功能。它既是一款容器编排工具,也是全新的基于容器技术的分布式架构领先方案。在Docker技术的基础上,为容器化的应用提供部署运行、资源调度、服务发现和动态伸缩等功能,提高了大规模容器集群管理的便捷性。
K8S集群中有管理节点与工作节点两种类型,部署架构如图7-5-1所示。管理节点主要负责K8S集群管理,集群中各节点间的信息交互、任务调度,还负责容器、Pod、NameSpaces、PV等生命周期的管理。工作节点主要为容器和Pod提供计算资源,Pod及容器全部运行在工作节点上,工作节点通过Kubelet服务与管理节点通信以管理容器的生命周期,并与集群其他节点进行通信。
将提供的压缩包Docker.tar.gz上传至/root目录并解压。
# tar -zxvf Docker.tar.gz
配置本地YUM源。
# cat /etc/yum.repod.s/local.repo
[kubernetes]
name=kubernetes
baseurl=file:///root/Docker
gpgcheck=0
enabled=1
Docker CE支持64位版本CentOS 7,并且要求内核版本不低于3.10。CentOS7.5_1804满足最低内核的要求,但由于内核版本比较低,部分功能(如overlay2存储层驱动)无法使用,并且部分功能可能不太稳定,建议升级内核。
升级系统内核,命令如下:
# yum upgrade -y
配置防火墙及SELinux
# systemctl stop firewalld && systemctl disable firewalld
# iptables -t filter -F
# iptables -t filter -X
# sed -i ‘s/SELINUX=enforcing/SELINUX=disabled/g‘ /etc/selinux/config
# reboot
[root@master ~]# cat >> /etc/sysctl.conf << EOF
net.ipv4.ip_forward=1
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
EOF
[root@master ~]# modprobe br_netfilter
[root@master ~]# sysctl -p
net.ipv4.ip_forward = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
yum-utils提供了yum-config-manager的依赖包,device-mapper-persistent-data和lvm2are需要devicemapper存储驱动。
[root@master ~]# yum install -y yum-utils device-mapper-persistent-data
随着Docker的不断流行与发展,Docker组织也开启了商业化之路,Docker从17.03版本之后分为CE(CommunityEdition)和EE(EnterpriseEdition)两个版本。
Docker EE专为企业的发展和IT团队建立,为企业提供最安全的容器平台,以应用为中心的平台,有专门的团队支持,可在经过认证的操作系统和云提供商中使用,并可运行来自DockerStore的经过认证的容器和插件。
Docker CE是免费的Docker产品的新名称,Docker CE包含了完整的Docker平台,非常适合开发人员和运维团队构建容器APP。
此处安装指定版本的Docker CE。
[root@master ~]# yum install docker-ce-18.09.6 docker-ce-cli-18.09.6 containerd.io -y
启动Docker并设置开机自启。
[root@master ~]# systemctl daemon-reload
[root@master ~]# systemctl restart docker
[root@master ~]# systemctl enable docker
查看Docker的系统信息。
[root@master ~]# docker info
Containers: 0
Running: 0
Paused: 0
Stopped: 0
Images: 0
Server Version: 18.09.6
Storage Driver: devicemapper
至此,Docker引擎的安装就完成了。
准备两台虚拟机。
一台为Docker主节点(5.1节点),一台为Docker客户端。
官方的Docker Hub是一个用于管理公共镜像的仓库,用户可以在上面找到需要的镜像,也可以把私有镜像推送上去。但是,在生产环境中,往往需要拥有一个私有的镜像仓库用于管理镜像,通过开源软件Registry可以实现这个目标。
官方在Docker Hub上提供了Registry的镜像,可以直接使用该Registry镜像来构建一个容器,搭建私有仓库服务。
将Registry镜像运行并生成一个容器。
[root@master ~]# ./image.sh
[root@master ~]# docker run -d -v /opt/registry:/var/lib/registry -p 5000:5000 --restart=always --name registry registry:latest
Registry服务默认会将上传的镜像保存在容器的/var/lib/registry中,将主机的/opt/registry目录挂载到该目录,即可实现将镜像保存到主机的/opt/registry目录了。
使用docker ps命令查看容器运行情况:
[root@master ~]# docker ps
Registry容器启动后,打开浏览器输入地址http://ip_add:5000/v2/,如果查看到下图所示的情况说明Registry运行正常。
先配置私有仓库,示例代码如下:
[root@master ~]# vi /etc/docker/daemon.json
{
"insecure-registries": ["ip:5000"]
}
[root@master ~]# systemctl restart docker
使用docker tag命令将centos:latest这个镜像标记为ip:5000/centos:latest。
[root@master ~]# docker tag centos:latest ip:5000/centos:latest
使用docker push上传标记的镜像:
[root@master ~]# docker push ip:5000/centos:latest
使用curl命令查看仓库中的镜像:
[root@master ~]# curl -L http://ip:5000/v2/_catalog
如同上述代码所示,提示{"repositories":["centos"]},则表明镜像已经上传成功了。
也可以在浏览器中访问上述地址查看
[root@slave ~]# cat /etc/docker/daemon.json
{
"insecure-registries": ["ip:5000"]
}
[root@slave ~]# systemctl restart docker
[root@slave ~]# docker pull ip:5000/centos:latest
[root@slave ~]# docker images
可将Docker镜像看作只读模板,通过它可以创建Docker容器。
例如,某个镜像可能包含一个Ubuntu操作系统、一个Apache HTTP Server以及用户开发的Web应用。
镜像有多种生成方法:
①可以从无到有开始创建镜像。
②可以下载并使用别人创建好的现成的镜像。
③可以在现有镜像上创建新的镜像。
可以将镜像的内容和创建步骤描述在一个文本文件中,这个文件被称作Dockerfile,通过执行docker build <docker-file>命令可以构建出Docker镜像。
可以使用docker images命令来列出本地主机上的镜像。
[root@master ~]# ./image.sh
[root@master ~]# docker images
各个选项说明:
l REPOSITORY:表示镜像的仓库源
l TAG:镜像的标签
l IMAGE ID:镜像ID
l CREATED:镜像创建时间
l SIZE:镜像大小
同一仓库源可以有多个TAG,代表这个仓库源的不同个版本。例如http仓库源里有 2.2.31、2.2.32等多个不同的版本,可以使用REPOSITORY:TAG命令来定义不同的镜像。
例如,要使用版本为2.2.31的http镜像来运行容器,命令如下:
[root@master ~]# docker run -i -t -d httpd:2.2.31 /bin/bash
参数说明:
l -i:交互式操作。
l -t:终端。
l -d:后台运行。
l httpd:2.2.31:镜像名,使用https:2.2.31镜像为基础来启动容器。
l /bin/bash:容器交互式Shell。
如果要使用版本为2.2.32的http镜像来运行容器时,命令如下:
[root@master ~]# docker run -i -t -d httpd:2.2.32 /bin/bash
如果不指定镜像的版本标签,则默认使用latest标签的镜像。
当本地主机上使用一个不存在的镜像时,Docker会自动下载这个镜像。如果需要预先下载这个镜像,可以使用docker pull命令来下载,语法如下。
# docker pull [OPTIONS] NAME[:TAG|@DIGEST]
OPTIONS说明:
l -a:拉取所有tagged镜像。
l --disable-content-trust:忽略镜像的校验,默认开启。
查找镜像一般有两种方式,可以通过Docker Hub(https://hub.docker.com/)网站来搜索镜像,也可以使用docker search命令来搜索镜像。其语法如下:
# docker search [OPTIONS] TERM
OPTIONS说明:
l --automated:只列出automated build类型的镜像。
l --no-trunc:显示完整的镜像描述。
l --filter=stars:列出收藏数不小于指定值的镜像。
例如,需要一个httpd镜像作Web服务时,可以使用docker search命令搜索httpd来寻找适合的镜像。
[root@master ~]# docker search --filter=stars=10 java
参数说明:
l NAME:镜像仓库源的名称。
l DESCRIPTION:镜像的描述。
l OFFICIAL:是否是Docker官方发布。
l stars:类似GitHub里面的star,表示点赞、喜欢的意思。
l AUTOMATED:自动构建。
镜像删除使用docker rmi命令,语法如下:
# docker rmi [OPTIONS] IMAGE [IMAGE...]
OPTIONS说明:
l -f:强制删除。
l --no-prune:不移除该镜像的过程镜像,默认移除。
例如,强制删除本地镜像busybox。
[root@master ~]# docker rmi -f busybox:latest
容器是一种轻量级的、可移植的、自包含的软件打包技术,使应用程序几乎可以在任何地方以相同的方式运行。开发人员在自己笔记本上创建并测试好的容器,无需任何修改就能够在生产系统的虚拟机、物理服务器或公有云主机上运行。
容器由应用程序本身和依赖两部分组成。容器在宿主机操作系统的用户空间中运行,与操作系统的其他进程隔离。这一点显著区别于的虚拟机。
传统的虚拟化技术,比如VMWare、KVM、Xen,目标是创建完整的虚拟机。为了运行应用,除了部署应用本身及其依赖(通常几十MB),还得安装整个操作系统(几十GB)。
那为什么需要容器呢?容器到底解决了什么问题?
简要的答案是容器使软件具备了超强的可移植能力。
运行第一个容器,执行以下命令。
[root@master ~]# docker run -it --rm -d -p 80:80 nginx:latest
参数说明:
l -i:交互式操作。
l -t:终端。
l -rm:容器退出后随之将其删除,可以避免浪费空间。
l -p:端口映射。
l -d:容器在后台运行。
其过程可以简单的描述为:
①下载Nginx镜像。
②启动容器,并将容器的80端口映射到宿主机的80端口。
当使用docker run来创建容器时,Docker在后台运行的标准流程如下:
①检查本地是否存在指定的镜像,不存在就从公有仓库下载。
②利用镜像创建并启动一个容器。
③分配一个文件系统,并在只读的镜像层外面挂载一层可读写层。
④从宿主主机配置的网桥接口中桥接一个虚拟接口到容器中去。
⑤从地址池配置一个IP地址给容器。
⑥执行用户指定的应用程序。
接下来可以通过浏览器验证容器是否正常工作。在浏览器中输入地址http://host_IP。
启动容器的语法如下:
# docker start [CONTAINER ID]
例如,启动所有的Docker容器:
# docker start $(docker ps -aq)
列出运行中的容器。
# docker ps
# docker container ls
列出所有容器(包含终止状态)。
# docker ps –a
查看具体容器的信息。
# docker inspect [container ID or NAMES]
查看容器的使用资源状况。
# docker stats [container ID or NAMES]
查看容器日志。
# docker logs [OPTIONS] [container ID or NAMES]
OPTIONS说明:
l --details:显示更多的信息。
l -f,--follow:跟踪实时日志。
l --sincestring:显示自某个timestamp之后的日志,或相对时间,如42分钟。
l --tailstring:从日志末尾显示多少行日志,默认是all。
l -t,--timestamps:显示时间戳。
l --until string:显示自某个timestamp之前的日志,或相对时间,如42分钟。
进入容器。
# docker exec -it [CONTAINER ID] bash
进入容器后,输入exit或者按Crtl+C键即可退出容器,示例代码如下:
[root@master ~]# docker exec -it 5d42a9fafeb6 bash
root@5d42a9fafeb6:/# exit
exit
删除终止状态的容器。
# docker rm [CONTAINER ID]
删除所有处于终止状态的容器。
# docker container prune
删除未被使用的数据卷。
# docker volume prune
删除运行中的容器。
# docker rm -f [CONTAINER ID]
批量停止所有的容器。
# docker stop $(docker ps -aq)
批量删除所有的容器。
# docker rm $(docker ps -aq)
终止容器进程,容器进入终止状态。
# docker container stop [CONTAINER ID]
将容器快照导出为本地文件,语法如下:
# docker export [CONTAINER ID] > [tar file]
例如:
[root@master ~]# docker export 5d42a9fafeb6 > nginx.tar
同样的,也可以把容器快照文件再导入为镜像,语法如下:
# cat [tar file] | docker import - [name:tag]
例如:
[root@master ~]# cat nginx.tar | docker import - nginx:test
[root@master ~]# docker images
使用docker import命令导入一个容器快照到本地镜像库时,将丢弃所有的历史记录和元数据信息,即仅保存容器当时的快照状态。
docker commit命令用于从容器创建一个新的镜像,其语法如下:
# docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]
OPTIONS说明:
l -a:提交的镜像作者。
l -c:使用Dockerfile指令来创建镜像。
l -m:提交时的说明文字。
l -p:在commit时,将容器暂停。
查看已有的容器。
[root@master ~]# docker ps
将容器5d42a9fafeb6保存为新的镜像,并添加提交人信息和说明信息。
[root@master ~]# docker commit -a "xiandian" -m "nginx-test" 5d42a9fafeb6 nginx:v1
sha256:94f6bc30fd2755a5524d5ce5f97279f2734976fa8854bb83ea9b96fa546f7688
构建完成后查看构建结果。
[root@master ~]# docker images
使用docker commit命令构建的镜像,除了制定镜像的人知道执行过什么命令,怎么生成的镜像,别人根本无从得知。建议使用Dockerfile来制作镜像,镜像的生成过程是透明的, docker commit可用于被入侵后保存现场等操作。
Dockerfile是一个文本文档,其中包含了组合映像的命令,可以使用在命令行中调用任何命令。Docker通过读取Dockerfile中的指令自动生成映像。
docker build命令用于从Dockerfile构建映像。可以在docker build命令中使用-f标志指向文件系统中任何位置的Dockerfile,其语法如下:
# docker build -f /path/to/a/Dockerfile
Dockerfile一般分为4部分:基础镜像信息、维护者信息、镜像操作指令和容器启动时执行指令,“#”为Dockerfile中的注释。
Dockerfile主要指令如下:
l FROM:指定基础镜像,必须为第一个命令。
l MAINTAINER:维护者信息。
l RUN:构建镜像时执行的命令。
l ADD:将本地文件添加到容器中,tar类型文件会自动解压(网络压缩资源不会被解压),可以访问网络资源,类似wget。
l COPY:功能类似ADD,但是是不会自动解压文件,也不能访问网络资源。
l CMD:构建容器后调用,也就是在容器启动时才进行调用。
l ENTRYPOINT:配置容器,使其可执行化。配合CMD可省去“application”,只使用参数。
l LABEL:用于为镜像添加元数据。
l ENV:设置环境变量。
l EXPOSE:指定与外界交互的端口。
l VOLUME:用于指定持久化目录。
l WORKDIR:工作目录,类似于cd命令。
l USER:指定运行容器时的用户名或UID,后续的RUN也会使用指定用户。使用USER指定用户时,可以使用用户名、UID或GID,或是两者的组合。当服务不需要管理员权限时,可通过该命令指定运行用户。
l ARG:用于指定传递给构建运行时的变量。
l ONBUILD:用于设置镜像触发器。
接下来以centos:latest为基础镜像,安装jdk1.8并构建新的镜像centos-jdk。
新建文件夹用于存放JDK安装包和Dockerfile文件。
[root@master ~]# mkdir centos-jdk
[root@master ~]# mv jdk-8u141-linux-x64.tar.gz ./centos-jdk/
[root@master ~]# cd centos-jdk/
编写Dockerfile。
[root@master centos-jdk]# cat Dockerfile
# CentOS with JDK 8
# Author Guo
# 指定基础镜像
FROM centos
# 指定作者
MAINTAINER Guo
# 新建文件夹用于存放jdk文件
RUN mkdir /usr/local/java
# 将JDK文件复制到镜像内并自动解压
ADD jdk-8u141-linux-x64.tar.gz /usr/local/java/
# 创建软链接
RUN ln -s /usr/local/java/jdk1.8.0_141 /usr/local/java/jdk
# 设置环境变量
ENV JAVA_HOME /usr/local/java/jdk
ENV JRE_HOME ${JAVA_HOME}/jre
ENV CLASSPATH .:${JAVA_HOME}/lib:${JRE_HOME}/lib
ENV PATH ${JAVA_HOME}/bin:$PATH
使用docker build命令构建新镜像。
[root@master centos-jdk]# docker build -t="centos-jdk" .
Sending build context to Docker daemon 185.5MB
Step 1/9 : FROM centos
---> 0f3e07c0138f
Step 2/9 : MAINTAINER dockerzlnewbie
---> Running in 1a6a5c210531
Removing intermediate container 1a6a5c210531
---> 286d78e0b9bf
Step 3/9 : RUN mkdir /usr/local/java
---> Running in 2dbbac61b2cf
Removing intermediate container 2dbbac61b2cf
---> 369567834d80
Step 4/9 : ADD jdk-8u141-linux-x64.tar.gz /usr/local/java/
---> 8fb102032ae2
Step 5/9 : RUN ln -s /usr/local/java/jdk1.8.0_141 /usr/local/java/jdk
---> Running in d8301e932f7c
Removing intermediate container d8301e932f7c
---> 7c82ee6703c5
Step 6/9 : ENV JAVA_HOME /usr/local/java/jdk
---> Running in d8159a32efae
Removing intermediate container d8159a32efae
---> d270abf08fa2
Step 7/9 : ENV JRE_HOME ${JAVA_HOME}/jre
---> Running in 5206ba2ec963
Removing intermediate container 5206ba2ec963
---> a52dc52bae76
Step 8/9 : ENV CLASSPATH .:${JAVA_HOME}/lib:${JRE_HOME}/lib
---> Running in 41fbd969bd90
Removing intermediate container 41fbd969bd90
---> ff44f5f90877
Step 9/9 : ENV PATH ${JAVA_HOME}/bin:$PATH
---> Running in 7affe7505c82
Removing intermediate container 7affe7505c82
---> bdf402785277
Successfully built bdf402785277
Successfully tagged centos-jdk:latest
查看构建的新镜像。
[root@master centos-jdk]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
centos-jdk latest bdf402785277 11 minutes ago 596MB
使用新构建的镜像运行容器验证JDK是否安装成功。
[root@master centos-jdk]# docker run -it centos-jdk /bin/bash
[root@2f9219541ca6 /]# java -version
java version "1.8.0_141"
Java(TM) SE Runtime Environment (build 1.8.0_141-b15)
Java HotSpot(TM) 64-Bit Server VM (build 25.141-b15, mixed mode)
[root@2f9219541ca6 /]#
可以发现JDK已经安装成功了,至此,使用Dockerfile构建镜像已完成。
[root@master ~]# cat /etc/hosts
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
Ip1 master
Ip2 node
安装chrony服务(双节点)
所有节点安装chrony服务。
# yum install -y chrony
Master节点修改/etc/chrony.conf文件,注释默认NTP服务器,指定上游公共NTP服务器,并允许其他节点同步时间。
[root@master ~]# sed -i ‘s/^server/#&/‘ /etc/chrony.conf
[root@master ~]# cat >> /etc/chrony.conf << EOF
local stratum 10
server master iburst
allow all
EOF
Master节点重启chronyd服务并设为开机启动,开启网络时间同步功能。
[root@master ~]# systemctl enable chronyd && systemctl restart chronyd
[root@master ~]# timedatectl set-ntp true
Node节点修改/etc/chrony.conf文件,指定内网 Master节点为上游NTP服务器,重启服务并设为开机启动。
[root@node ~]# sed -i ‘s/^server/#&/‘ /etc/chrony.conf
[root@node ~]# echo server 192.168.2.100 iburst >> /etc/chrony.conf //IP为master节点地址
[root@node ~]# systemctl enable chronyd && systemctl restart chronyd
所有节点执行chronyc sources命令,查询结果中如果存在以“^*”开头的行,即说明已经同步成功。
所有节点开启Docker API。
# vi /lib/systemd/system/docker.service
将
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
修改为
ExecStart=/usr/bin/dockerd -H tcp://0.0.0.0:2375 -H unix:///var/run/docker.sock
# systemctl daemon-reload
# systemctl restart docker
# ./image.sh
在Master节点创建Swarm集群。
[root@master ~]# docker swarm init --advertise-addr 192.168.2.100
初始化命令中“--advertise-addr”选项表示管理节点公布它的IP是多少。其它节点必须能通过这个IP找到管理节点。
输出结果中包含3个步骤:
①Swarm创建成功,swarm-manager成为manager node。
②添加worker node需要执行的命令。
③添加manager node需要执行的命令。
复制前面的docker swarm join命令,在Node节点执行以加入Swarm集群,
[root@node ~]# docker swarm join --token SWMTKN-1-2oyrpgkp41z40zg0z6l0yppv6420vz18rr171kqv0mfsbiufii-c3ficc1qh782wo567uav16n3n 10.18.4.39:2377
This node joined a swarm as a worker.
如果初始化时没有记录下docker swarm init提示的添加worker的完整命令,可以通过docker swarm join-token worker命令查看。
[root@master ~]# docker swarm join-token worker
To add a worker to this swarm, run the following command:
docker swarm join --token SWMTKN-1-2oyrpgkp41z40zg0z6l0yppv6420vz18rr171kqv0mfsbiufii-c3ficc1qh782wo567uav16n3n 10.18.4.39:2377
登录Master节点,查看各节点状态。
[root@master ~]# docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
jit2j1itocmsynhecj905vfwp * master Ready Active Leader 18.09.6
8mww97xnbfxfrbzqndplxv3vi node Ready Active 18.09.6
登录Master节点,安装Portainer。
[root@swarm ~]# docker volume create portainer_data
portainer_data
[root@swarm ~]# docker service create --name portainer --publish 9000:9000 --replicas=1 --constraint ‘node.role == manager‘ --mount type=bind,src=//var/run/docker.sock,dst=/var/run/docker.sock --mount type=volume,src=portainer_data,dst=/data portainer/portainer -H unix:///var/run/docker.sock
登录portainer,首次登陆需要设置用户名和密码。
现在已经创建好了Swarm集群,执行如下命令部署一个运行httpd镜像的Service。
[root@master ~]# docker service create --name web_server httpd
部署Service的命令形式与运行容器的docker run很相似,--name为Service命名,httpd为镜像的名字。
通过docker service ls命令可以查看当前Swarm中的Service。
[root@master ~]# docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
2g18082sfqa9 web_server replicated 1/1 httpd:latest
REPLICAS显示当前副本信息,1/1意思是web_server这个Service期望的容器副本数量为1,目前已经启动的副本数量为1,即当前Service已经部署完成。命令docker service ps可以查看Service每个副本的状态。
[root@master ~]# docker service ps web_server
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
4vtrynwddd7m web_server.1 httpd:latest node Running Running 27 minutes ago
可以查看到Service唯一的副本被分派到node,当前的状态是Running。
刚刚仅部署了只有一个副本的Service,不过对于Web服务,通常会运行多个实例。这样可以负载均衡,同时也能提供高可用。
Swarm要实现这个目标非常简单,增加Service的副本数就可以了。在Master节点上执行如下命令。
[root@master ~]# docker service scale web_server=5
副本数增加到5,通过docker service ls和docker service ps命令查看副本的详细信息。
[root@master ~]# docker service ls
[root@master ~]# docker service ps web_server
既然可以通过scale up扩容服务,当然也可以通过scale down减少副本数,运行下面的命令。
[root@master ~]# docker service scale web_server=2
访问Service,要访问http服务,首先得保证网络通畅,其次需要知道服务的IP。查看容器的网络配置。
在Master上运行了一个容器,是web_server的一个副本,容器监听了80端口,但并没有映射到Docker Host,所以只能通过容器的IP访问。但是服务并没有暴露给外部网络,只能在Docker主机上访问,外部无法访问。要将Service暴露到外部,方法其实很简单,执行下面的命令即可。
[root@master ~]# docker service update --publish-add 8080:80 web_server
--publish-add 8080:80将容器的80映射到主机的8080端口,这样外部网络就能访问到Service了。通过http://任意节点IP:8080即可访问Service。
Service的容器副本可能会伸缩,甚至失败,会在不同的主机上创建和销毁,这就引出一个问题,如果Service有需要管理的数据,那么这些数据应该如何存放呢?如果把数据打包在容器里,这显然不行,除非数据不会发生变化,否则,如何在多个副本之间保持同步呢?volume是将宿主级的目录映射到容器中,以实现数据持久化。可以用两种方式来实现:
l volume默认模式:工作节点宿主机数据同步到容器内。
l volume NFS共享存储模式:管理节点宿主同步到工作节点宿主,工作节点宿主同步到容器。
生产环境中一般推荐使用volume NFS共享存储模式。
登录Master节点,安装NFS服务端、配置NFS主配置文件、添加权限并启动。
[root@master ~]# yum install nfs-utils -y
添加目录让相应网段可以访问并添加读写权限。
[root@master ~]# vi /etc/exports
创建共享目录,添加权限。
[root@master ~]# mkdir -p /root/share
[root@master ~]# chmod 777 /root/share
/root/share为共享目录,生效配置。
[root@master ~]# exportfs -rv
exporting 10.18.4.39/24:/root/share
开启RPC服务并设置开机自启。
[root@master ~]# systemctl start rpcbind
[root@master ~]# systemctl enable rpcbind
启动NFS服务并设置开机自启
[root@master ~]# systemctl start nfs
[root@master ~]# systemctl enable nfs
查看NFS是否挂载成功。
[root@master ~]# cat /var/lib/nfs/etab
登录Node节点,安装NFS客户端并启动服务。
[root@node ~]# yum install nfs-utils -y
[root@node ~]# systemctl start rpcbind
[root@node ~]# systemctl enable rpcbind
[root@node ~]# systemctl start nfs
[root@node ~]# systemctl enable nfs
部署的服务可能分不到各个节点上,在所有节点创建docker volume。
[root@master ~]# docker volume create --driver local --opt type=nfs --opt o=addr=10.18.4.39,rw --opt device=:/root/share foo33
--opt device=:/root/share用于指向共享目录,也可以是共享目录下的子目录。
查看volume。
# docker volume ls
可以查看到docker volume列表中有foo33,查看volume详细信息。
# docker volume inspect foo33
[
{
"CreatedAt": "2019-10-31T07:36:47Z",
"Driver": "local",
"Labels": {},
"Mountpoint": "/var/lib/docker/volumes/foo33/_data",
"Name": "foo33",
"Options": {
"device": ":/root/share",
"o": "addr=10.18.4.39,rw",
"type": "nfs"
},
"Scope": "local"
}
]
可以看出NFS的/root/share被挂载到了/var/lib/docker/volumes/foo33/_data目录。
创建并发布服务。
[root@master ~]# docker service create --name test-nginx-nfs --publish 80:80 --mount type=volume,source=foo33,destination=/app/share --replicas 3 nginx
查看服务分布的节点。
[root@master ~]# docker service ps test-nginx-nfs
在Master节点/root/share目录中生成一个index.html文件。
[root@master ~]# cd /root/share/
[root@master share]# touch index.html
[root@master share]# ll
查看宿主机目录挂载情况。
[root@master share]# docker volume inspect foo33
[
{
"CreatedAt": "2019-10-31T07:44:49Z",
"Driver": "local",
"Labels": {},
"Mountpoint": "/var/lib/docker/volumes/foo33/_data",
"Name": "foo33",
"Options": {
"device": ":/root/share",
"o": "addr=10.18.4.39,rw",
"type": "nfs"
},
"Scope": "local"
}
]
[root@master share]# ls /var/lib/docker/volumes/foo33/_data
查看容器目录。
[root@master ~]# docker ps
[root@master ~]# docker exec -it a1bce967830e bash
可以发现,NFS已经挂载成功。
调度节点,默认配置下Master也是worker node,所以Master上也运行了副本。如果不希望在Master上运行Service,可以执行如下命令。
[root@master ~]# docker node update --availability drain master
通过 docker node ls 命令查看各节点现在的状态。
[root@master ~]# docker node ls
Drain表示Master已经不负责运行Service,之前Master运行的那1个副本会如何处理呢?使用docker service ps命令来查看。
[root@master ~]# docker service ps test-nginx-nfs
Master上的副本test-nginx-nfs.3已经被Shut down了,为了达到3个副本数的目标,在Node上添加了新的副本test-nginx-nfs.3。
所有节点将提供的压缩包K8S.tar.gz上传至/root目录并解压。
# tar -zxvf K8S.tar.gz
所有节点配置本地YUM源。
# cat /etc/yum.repod.s/local.repo
[kubernetes]
name=kubernetes
baseurl=file:///root/Kubernetes
gpgcheck=0
enabled=1
所有节点升级系统内核。
# yum upgrade -y
所有节点,修改/etc/hosts文件。
# cat /etc/hosts
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
10.18.4.33 master
10.18.4.42 node
所有节点配置防火墙及SELinux。
# systemctl stop firewalld && systemctl disable firewalld
# iptables -F
# iptables -X
# iptables -Z
# /usr/sbin/iptables-save
# sed -i ‘s/SELINUX=enforcing/SELINUX=disabled/g‘ /etc/selinux/config
# reboot
Kubernetes的想法是将实例紧密包装到尽可能接近100%。所有的部署应该与CPU和内存限制固定在一起。所以如果调度程序发送一个Pod到一台机器,它不应该使用交换。设计者不想交换,因为它会减慢速度。所以关闭Swap主要是为了性能考虑。
所有节点关闭Swap。
# swapoff -a
# sed -i "s/\/dev\/mapper\/centos-swap/\#\/dev\/mapper\/centos-swap/g" /etc/fstab
所有节点安装chrony服务。
# yum install -y chrony
Master节点修改/etc/chrony.conf文件,注释默认NTP服务器,指定上游公共NTP服务器,并允许其他节点同步时间。
[root@master ~]# sed -i ‘s/^server/#&/‘ /etc/chrony.conf
[root@master ~]# cat >> /etc/chrony.conf << EOF
local stratum 10
server master iburst
allow all
EOF
Master节点重启chronyd服务并设为开机启动,开启网络时间同步功能。
[root@master ~]# systemctl enable chronyd && systemctl restart chronyd
[root@master ~]# timedatectl set-ntp true
Node节点修改/etc/chrony.conf文件,指定内网Master节点为上游NTP服务器,重启服务并设为开机启动。
[root@node ~]# sed -i ‘s/^server/#&/‘ /etc/chrony.conf
[root@node ~]# echo server 10.18.4.33 iburst >> /etc/chrony.conf //IP为master节点地址
[root@node ~]# systemctl enable chronyd && systemctl restart chronyd
所有节点执行chronyc sources命令,查询结果中如果存在以“^*”开头的行,即说明已经同步成功。
# chronyc sources
RHEL/CentOS7上的一些用户报告了由于iptables被绕过而导致流量路由不正确的问题,所以需要在各节点开启路由转发。
所有节点创建/etc/sysctl.d/K8S.conf文件,添加如下内容。
# cat << EOF | tee /etc/sysctl.d/K8S.conf
net.ipv4.ip_forward = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
EOF
# modprobe br_netfilter
# sysctl -p /etc/sysctl.d/K8S.conf
net.ipv4.ip_forward = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
由于IPVS已经加入到了内核主干,所以需要加载以下内核模块以便为kube-proxy开启IPVS功能。
在所有节点执行以下操作。
# cat > /etc/sysconfig/modules/ipvs.modules <<EOF
#!/bin/bash
modprobe -- ip_vs
modprobe -- ip_vs_rr
modprobe -- ip_vs_wrr
modprobe -- ip_vs_sh
modprobe -- nf_conntrack_ipv4
EOF
# chmod 755 /etc/sysconfig/modules/ipvs.modules
# bash /etc/sysconfig/modules/ipvs.modules && lsmod | grep -e ip_vs -e nf_conntrack_ipv
上面脚本创建了/etc/sysconfig/modules/ipvs.modules文件,保证在节点重启后能自动加载所需模块。使用lsmod | grep -e ip_vs -e nf_conntrack_ipv4命令查看是否已经正确加载所需的内核模块。
# lsmod | grep -e ip_vs -e nf_conntrack_ipv4
所有节点安装ipset软件包。
# yum install ipset ipvsadm -y
Kubernetes默认的容器运行时仍然是Docker,使用的是Kubelet中内置dockershim CRI实现。需要注意的是,在Kubernetes1.14的版本中,支持的版本有1.13.1、17.03、17.06、17.0918.06和18.09,案例统一使用Docker 18.09版本。
所有节点安装Docker,启动Docker引擎并设置开机自启。
# yum install -y yum-utils device-mapper-persistent-data lvm2
# yum install docker-ce-18.09.6 docker-ce-cli-18.09.6 containerd.io -y
# mkdir -p /etc/docker
# tee /etc/docker/daemon.json <<-‘EOF‘
{
"exec-opts": ["native.cgroupdriver=systemd"]
}
EOF
# systemctl daemon-reload
# systemctl restart docker
# systemctl enable docker
# docker info |grep Cgroup
Cgroup Driver: system
Kubelet负责与其他节点集群通信,并进行本节点Pod和容器生命周期的管理。Kubeadm是Kubernetes的自动化部署工具,降低了部署难度,提高效率。Kubectl是Kubernetes集群命令行管理工具。
所有节点安装Kubernetes工具并启动Kubelet。
# yum install -y kubelet-1.14.1 kubeadm-1.14.1 kubectl-1.14.1
# systemctl enable kubelet && systemctl start kubelet
// 此时启动不成功正常,后面初始化的时候会变成功
登录Master节点,初始化Kubernetes集群。
[root@master ~]# ./kubernetes_base.sh
[root@master ~]# kubeadm init --apiserver-advertise-address 10.18.4.33 --kubernetes-version="v1.14.1" --pod-network-cidr=10.16.0.0/16 --image-repository=registry.aliyuncs.com/google_containers
初始化操作主要经历了下面15个步骤,每个阶段均输出均使用[步骤名称]作为开头:
①[init]:指定版本进行初始化操作。
②[preflight]:初始化前的检查和下载所需要的Docker镜像文件。
③[kubelet-start]:生成Kubelet的配置文件/var/lib/kubelet/config.yaml,没有这个文件Kubelet无法启动,所以初始化之前的Kubelet实际上启动失败。
④[certificates]:生成Kubernetes使用的证书,存放在/etc/kubernetes/pki目录中。
⑤[kubeconfig]:生成KubeConfig文件,存放在/etc/kubernetes目录中,组件之间通信需要使用对应文件。
⑥[control-plane]:使用/etc/kubernetes/manifest目录下的YAML文件,安装Master组件。
⑦[etcd]:使用/etc/kubernetes/manifest/etcd.yaml安装Etcd服务。
⑧[wait-control-plane]:等待control-plan部署的Master组件启动。
⑨[apiclient]:检查Master组件服务状态。
⑩[uploadconfig]:更新配置。
11[kubelet]:使用configMap配置Kubelet。
12[patchnode]:更新CNI信息到Node上,通过注释的方式记录。
13[mark-control-plane]:为当前节点打标签,打了角色Master和不可调度标签,这样默认就不会使用Master节点来运行Pod。
14[bootstrap-token]:生成的Token需要记录下来,后面使用kubeadm join命令往集群中添加节点时会用到。
15[addons]:安装附加组件CoreDNS和kube-proxy。
输出结果中的最后一行用于其它节点加入集群。
Kubectl默认会在执行的用户home目录下面的.kube目录中寻找config文件,配置kubectl工具。
[root@master ~]# mkdir -p $HOME/.kube
[root@master ~]# sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
[root@master ~]# sudo chown $(id -u):$(id -g) $HOME/.kube/config
检查集群状态。
[root@master ~]# kubectl get cs
登录Master节点,部署flannel网络。
[root@master ~]# kubectl apply -f yaml/kube-flannel.yaml
[root@master ~]# kubectl get pods -n kube-system
NAME READY STATUS RESTARTS AGE
coredns-8686dcc4fd-v88br 0/1 Running 0 4m42s
coredns-8686dcc4fd-xf28r 0/1 Running 0 4m42s
etcd-master 1/1 Running 0 3m51s
kube-apiserver-master 1/1 Running 0 3m46s
kube-controller-manager-master 1/1 Running 0 3m48s
kube-flannel-ds-amd64-6hf4w 1/1 Running 0 24s
kube-proxy-r7njz 1/1 Running 0 4m42s
kube-scheduler-master 1/1 Running 0 3m37s
登录Node节点,使用kubeadm join命令将Node节点加入集群。
[root@master ~]# ./kubernetes_base.sh
[root@node ~]# kubeadm join 10.18.4.33:6443 --token qf4lef.d83xqvv00l1zces9 --discovery-token-ca-cert-hash sha256:ec7c7db41a13958891222b2605065564999d124b43c8b02a3b32a6b2ca1a1c6c
[preflight] Running pre-flight checks
[preflight] Reading configuration from the cluster...
[preflight] FYI: You can look at this config file with ‘kubectl -n kube-system get cm kubeadm-config -oyaml‘
[kubelet-start] Downloading configuration for the kubelet from the "kubelet-config-1.14" ConfigMap in the kube-system namespace
[kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml"
[kubelet-start] Writing kubelet environment file with flags to file "/var/lib/kubelet/kubeadm-flags.env"
[kubelet-start] Activating the kubelet service
[kubelet-start] Waiting for the kubelet to perform the TLS Bootstrap...
This node has joined the cluster:
* Certificate signing request was sent to apiserver and a response was received.
* The Kubelet was informed of the new secure connection details.
Run ‘kubectl get nodes‘ on the control-plane to see this node join the cluster.
登录Master节点,检查各节点状态。
[root@master ~]# kubectl get nodes
NAME STATUS ROLES AGE VERSION
master Ready master 4m53s v1.14.1
node Ready <none> 13s v1.14.1
使用kubectl create命令安装Dashboard。
[root@master ~]# kubectl create -f yaml/kubernetes-dashboard.yaml
创建管理员。
[root@master ~]# kubectl create -f yaml/dashboard-adminuser.yaml
serviceaccount/kubernetes-dashboard-admin created
clusterrolebinding.rbac.authorization.K8S.io/kubernetes-dashboard-admin created
检查所有Pod状态。
[root@master ~]# kubectl get pods -n kube-system
NAME READY STATUS RESTARTS AGE
coredns-8686dcc4fd-8jqzh 1/1 Running 0 11m
coredns-8686dcc4fd-dkbhw 1/1 Running 0 11m
etcd-master 1/1 Running 0 11m
kube-apiserver-master 1/1 Running 0 11m
kube-controller-manager-master 1/1 Running 0 11m
kube-flannel-ds-amd64-49ssg 1/1 Running 0 7m56s
kube-flannel-ds-amd64-rt5j8 1/1 Running 0 7m56s
kube-proxy-frz2q 1/1 Running 0 11m
kube-proxy-xzq4t 1/1 Running 0 11m
kube-scheduler-master 1/1 Running 0 11m
kubernetes-dashboard-5f7b999d65-djgxj 1/1 Running 0 11m
查看Dashboard端口号。
[root@master ~]# kubectl get svc -n kube-system
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kube-dns ClusterIP 10.96.0.10 <none> 53/UDP,53/TCP,9153/TCP 15m
kubernetes-dashboard NodePort 10.102.195.101 <none> 443:30000/TCP 4m43s
可以查看到kubernetes-dashboard对外暴露的端口号为30000,在Firefox浏览器中输入地址(https://10.18.4.33:30000),即可访问Kubernetes Dashboard。
单击“接受风险并继续”按钮,即可进入Kubernetes Dasboard认证界面。
登录Kubernetes Dasboard需要输入令牌,通过以下命令获取访问Dashboard的认证令牌。
[root@master ~]# kubectl -n kube-system describe secret $(kubectl -n kube-system get secret | grep kubernetes-dashboard-admin-token | awk ‘{print $1}‘)
Name: kubernetes-dashboard-admin-token-j5dvd
Namespace: kube-system
Labels: <none>
Annotations: kubernetes.io/service-account.name: kubernetes-dashboard-admin
kubernetes.io/service-account.uid: 1671a1e1-cbb9-11e9-8009-ac1f6b169b00
Type: kubernetes.io/service-account-token
Data
====
ca.crt: 1025 bytes
namespace: 11 bytes
token: eyJhbGciOiJSUzI1NiIsImtpZCI6IiJ9.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlLXN5c3RlbSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJrdWJlcm5ldGVzLWRhc2hib2FyZC1hZG1pbi10b2tlbi1qNWR2ZCIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50Lm5hbWUiOiJrdWJlcm5ldGVzLWRhc2hib2FyZC1hZG1pbiIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50LnVpZCI6IjE2NzFhMWUxLWNiYjktMTFlOS04MDA5LWFjMWY2YjE2OWIwMCIsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDprdWJlLXN5c3RlbTprdWJlcm5ldGVzLWRhc2hib2FyZC1hZG1pbiJ9.u6ZaVO-WR632jpFimnXTk5O376IrZCCReVnu2Brd8QqsM7qgZNTHD191Zdem46ummglbnDF9Mz4wQBaCUeMgG0DqCAh1qhwQfV6gVLVFDjHZ2tu5yn0bSmm83nttgwMlOoFeMLUKUBkNJLttz7-aDhydrbJtYU94iG75XmrOwcVglaW1qpxMtl6UMj4-bzdMLeOCGRQBSpGXmms4CP3LkRKXCknHhpv-pqzynZu1dzNKCuZIo_vv-kO7bpVvi5J8nTdGkGTq3FqG6oaQIO-BPM6lMWFeLEUkwe-EOVcg464L1i6HVsooCESNfTBHjjLXZ0WxXeOOslyoZE7pFzA0qg
将获取到的令牌输入浏览器,认证后即可进入Kubernetes控制台。
Kuboard是一款免费的Kubernetes图形化管理工具,其力图帮助用户快速在Kubernetes上落地微服务。登录Master节点,使用kuboard.yaml文件部署Kuboard。
[root@master ~]# kubectl create -f yaml/kuboard.yaml
deployment.apps/kuboard created
service/kuboard created
serviceaccount/kuboard-user created
clusterrolebinding.rbac.authorization.K8S.io/kuboard-user created
serviceaccount/kuboard-viewer created
clusterrolebinding.rbac.authorization.K8S.io/kuboard-viewer created
clusterrolebinding.rbac.authorization.K8S.io/kuboard-viewer-node created
clusterrolebinding.rbac.authorization.K8S.io/kuboard-viewer-pvp created
ingress.extensions/kuboard created
在浏览器中输入地址http://10.18.4.33:31000,即可进入Kuboard的认证界面,在Token文本框中输入令牌后可进入Kuboard控制台。
在Kuboard控制台中可以查看到集群概览,至此Kubernetes容器云平台就部署完成了。
IPVS是基于TCP四层(IP+端口)的负载均衡软件。
IPVS会从TCPSYNC包开始为一个TCP连接所有的数据包,建立状态跟踪机制,保证一个TCP连接中所有的数据包能到同一个后端。所以IPVS是基于TCP状态机进行控制管理,只感知TCP头而不对TCP的payload进行查看。因此,对IPVS后端服务器集群还有一个假定,那就是所有后端都具有同样的应用层的服务功能,但是由于IPVS可以给后端设定权重,所以每个后端的服务的能力可以不同。
一个合理的负载均衡软件,应该能够尽量提高服务接入能力(ReqPS,Request Per Second),而将服务处理能力(ResPS,Response Per second)通过合理分配后端来达到最佳。
根据处理请求和响应数据包的模式的不同,IPVS具有如下4种工作模式:
①NAT模式
②DR(Direct Routing)模式
③TUN(IP Tunneling)模式
④FULLNAT模式
而根据响应数据包返回路径的不同,可以分为如下2种模式:
①双臂模式:请求、转发和返回在同一路径上,client和IPVS director、IPVS director和后端real server都是由请求和返回2个路径连接。
②三角模式:请求、转发和返回3个路径连接client、IPVS director和后端real server成为一个三角形。
登录Master节点,修改ConfigMap的kube-system/kube-proxy中的config.conf文件,修改为mode: "ipvs"。
[root@master ~]# kubectl edit cm kube-proxy -n kube-system
ipvs:
excludeCIDRs: null
minSyncPeriod: 0s
scheduler: ""
syncPeriod: 30s
kind: KubeProxyConfiguration
metricsBindAddress: 127.0.0.1:10249
mode: "ipvs" //修改此处
nodePortAddresses: null
oomScoreAdj: -999
portRange: ""
resourceContainer: /kube-proxy
udpIdleTimeout: 250ms
[root@master ~]# kubectl get pod -n kube-system | grep kube-proxy | awk ‘{system("kubectl delete pod "$1" -n kube-system")}‘
pod "kube-proxy-bd68w" deleted
pod "kube-proxy-qq54f" deleted
pod "kube-proxy-z9rp4" deleted
由于已经通过ConfigMap修改了kube-proxy的配置,所以后期增加的Node节点,会直接使用IPVS模式。查看日志。
[root@master ~]# kubectl logs kube-proxy-9zv5x -n kube-system
I1004 07:11:17.538141 1 server_others.go:177] Using ipvs Proxier. #正在使用ipvs
W1004 07:11:17.538589 1 proxier.go:381] IPVS scheduler not specified, use rr by default
I1004 07:11:17.540108 1 server.go:555] Version: v1.14.1
I1004 07:11:17.555484 1 conntrack.go:52] Setting nf_conntrack_max to 524288
I1004 07:11:17.555827 1 config.go:102] Starting endpoints config controller
I1004 07:11:17.555899 1 controller_utils.go:1027] Waiting for caches to sync for endpoints config controller
I1004 07:11:17.555927 1 config.go:202] Starting service config controller
I1004 07:11:17.555965 1 controller_utils.go:1027] Waiting for caches to sync for service config controller
I1004 07:11:17.656090 1 controller_utils.go:1034] Caches are synced for service config controller
I1004 07:11:17.656091 1 controller_utils.go:1034] Caches are synced for endpoints config controller
日志中打印出了“Using ipvs Proxier”字段,说明IPVS模式已经开启。
使用ipvsadm测试,可以查看之前创建的Service是否已经使用LVS创建了集群。
[root@master ~]# ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 172.17.0.1:30099 rr
TCP 172.17.0.1:30188 rr
TCP 172.17.0.1:30301 rr
TCP 172.17.0.1:31000 rr
出于安全考虑,默认配置下Kubernetes不会将Pod调度到Master节点。查看Master节点Taints字段默认配置:
[root@master ~]# kubectl describe node master
……
CreationTimestamp: Fri, 04 Oct 2019 06:16:45 +0000
Taints: node-role.kubernetes.io/master:NoSchedule //状态为NoSchedule
Unschedulable: false
……
如果希望将K8S-master也当作Node节点使用,可以执行如下命令:
[root@master ~]# kubectl taint node master node-role.kubernetes.io/master-
node/master untainted
[root@master ~]# kubectl describe node master
……
CreationTimestamp: Fri, 04 Oct 2019 06:16:45 +0000
Taints: <none> //状态已经改变
Unschedulable: false
……
可以看到,Master节点的调度状态已经发生改变。
Kubectl是Kubernetes集群的命令行工具,通过kubectl能够对集群本身进行管理,并能够在集群上进行容器化应用的安装部署。运行kubectl命令的语法如下:
# kubectl [command] [TYPE] [NAME] [flags]
l comand:指定要对资源执行的操作,例如create、get、describe和delete。
l TYPE:指定资源类型,资源类型需注意大小写。
l NAME:指定资源的名称,名称也是大小写敏感的。如果省略名称,则会显示所有的资源。
l flags:指定可选的参数。
K8S把所有的东西都抽象成了资源,而kubectl get命令就是用来查看这些资源的,最常见的资源就是Pod。
Pod的概念其实和Docker中的容器非常相似。Pod是K8S中的最小工作单位,可以把Pod理解成一个一个的小机器人,而K8S抽象出来的大资源池就是它们的工厂。
那么Pod和Docker容器是什么关系呢,简单来说,Pod将一个或多个Docker容器封装成一个统一的整体进行管理并对外提供服务。
不仅部署的服务要封装成Pod,就连K8S平台自身也是运行在一堆Pod上。
查看一下K8S的Pod的命令如下。
# kubectl get pod -n kube-system
参数“-n”指定了要查看哪个命名空间下的Pod。K8S平台自身所有的Pod都被放置在kube-system命名空间下。
命名空间namespace是K8S中“组”的概念,提供同一服务的Pod应该被放置同一命名空间下,而不是混杂在一起。K8S可以用命名空间来做权限控制和资源隔离。如果不指定的话,Pod将被放置在默认的命名空间default下。
执行了kubectl get pod -n kube-system命令后可以看到以下内容:
[root@master ~]# kubectl get pod -n kube-system
NAME READY STATUS RESTARTS AGE
coredns-8686dcc4fd-v88br 1/1 Running 0 63m
coredns-8686dcc4fd-xf28r 1/1 Running 0 63m
etcd-master 1/1 Running 0 62m
kube-apiserver-master 1/1 Running 0 62m
kube-controller-manager-master 1/1 Running 0 62m
kube-flannel-ds-amd64-6hf4w 1/1 Running 0 58m
kube-flannel-ds-amd64-t5j2k 1/1 Running 0 53m
kube-proxy-9kx9n 1/1 Running 0 53m
kube-proxy-r7njz 1/1 Running 0 63m
kube-scheduler-master 1/1 Running 0 62m
kubernetes-dashboard-5f7b999d65-77q4d 1/1 Running 0 52m
其中每一行就是一个资源,这里看到的资源是Pod,这个列表里包含了K8S在所有节点上运行的Pod,加入的节点越多,那么显示的Pod也就越多。
以下是查询结果中的参数说明:
l NAME:Pod的名字,K8S可以为Pod随机分配一个五位数的后缀。
l READY:Pod中已经就绪的Docker容器的数量,Pod封装了一个或多个Docker容器,此处“1/1”的含义为“就绪1个容器/共计1个容器”。
l STATUS:Pod的当前状态,常见的状态有Running、Error、Pending等。
l RESTART:Pod一共重启了多少次,K8S可以自动重启Pod。
l AGE:Pod启动的时间。
kubectl get可以列出K8S中所有资源,这里只介绍了如何用kubectl获取Pod的列表,还可以获取其它资源列表信息,如get svc(查看服务)、get rs(查看副本控制器)、get deploy(查看部署)等。
如果想要查看更多信息,指定-o wide参数即可,语法如下:
# kubectl get <资源> -n <命名空间> -o wide
加上这个参数之后就可以看到资源的IP和所在节点。
kubectl describe命令可以用来查看某一资源的具体信息,同样可以使用-n参数指定资源所在的命名空间。
例如,可以用如下命令来查看刚才Pod列表中的某个Pod的详细信息。
# kubectl describe pod kube-flannel-ds-amd64-6hf4w -n kube-system
查询结果中可以看到很多的信息,首先是基本属性,可以在详细信息的开头找到。
Name: kube-flannel-ds-amd64-6hf4w #Pod名称
Namespace: kube-system #所处命名空间
Priority: 0
PriorityClassName: <none>
Node: master/10.18.4.33 #所在节点
Start Time: Tue, 29 Oct 2019 07:19:12 +0000 #启动时间
Labels: app=flannel #标签
controller-revision-hash=8676477c4
pod-template-generation=1
tier=node
Annotations: <none> #注释
Status: Running #当前状态
IP: 10.18.4.33 #所在节点IP
Controlled By: DaemonSet/kube-flannel-ds-amd64 #由哪种资源控制
其中几个比较常用的,例如Node、Labels和Controlled By。通过Node可以快速定位到Pod所处的机器,从而检查该机器是否出现问题或宕机等。通过labels可以检索到该Pod的大致用途及定位。而通过Controlled By可以知道该Pod是由哪种K8S资源创建的,然后就可以使用“kubectl get<资源名>”命令继续查找问题。
在中间部分可以找到Containers段落。该段落详细的描述了Pod中每个Docker容器的信息,常用的比如Image字段,当Pod出现ImagePullBackOff错误的时候就可以查看该字段,确认拉取的是什么镜像。其他的字段名都很通俗,直接翻译即可。
Containers:
kube-flannel:
Container ID: docker://d41165b1f1a5261d813a9fb3c07caadffd0b224e095bb15f3eb1342da0d01c32
Image: quay.io/coreos/flannel:v0.11.0-amd64
Image ID: docker://sha256:ff281650a721f46bbe2169292c91031c66411554739c88c861ba78475c1df894
Port: <none>
Host Port: <none>
Command:
/opt/bin/flanneld
Args:
--ip-masq
--kube-subnet-mgr
State: Running
Started: Tue, 29 Oct 2019 07:19:18 +0000
Ready: True
Restart Count: 0
Limits:
cpu: 100m
memory: 50Mi
Requests:
cpu: 100m
memory: 50Mi
Environment:
POD_NAME: kube-flannel-ds-amd64-6hf4w (v1:metadata.name)
POD_NAMESPACE: kube-system (v1:metadata.namespace)
Mounts:
/etc/kube-flannel/ from flannel-cfg (rw)
/run/flannel from run (rw)
/var/run/secrets/kubernetes.io/serviceaccount from flannel-token-pmrss (ro)
在describe查看详情的时候,最常用的信息获取处就是Event段落,可以在介绍内容的末尾找到它,如下:
Events: <none>
Events字段为<none>就说明该Pod一切正常。当Pod的状态不是Running时,这里一定会有或多或少的问题,然后就可以通过其中的信息分析Pod出现问题的详细原因了。
如果要查看一个Pod的具体日志,就可以通过“kubectl logs <pod名>”命令来查看。kubectl logs只能查看Pod的日志。通过添加-f参数可以持续查看日志。例如,查看kube-system命名空间中某个flannel Pod的日志。
# kubectl logs -f kube-flannel-ds-amd64-6hf4w -n kube-system
K8S中的所有东西都可以通过kubectl create命令创建,无论是创建一个Pod还是一个大型的滚动升级服务Deployment,create命令都可以做到。使用create生成一个资源主要有两种常用方法,从YAML配置文件创建和简易创建。
如果想让K8S生成一个和预期一模一样的资源,那就要充分而详细地描述这个资源,K8S就提供了这么一个方法,可以使用YAML文件按照K8S指定好的结构定义一个对象,然后使用如下方法将该文件传递给K8S。
# kubectl create -f <配置文件名.yaml>
例如,使用下面的配置文件就可以创建一个最简单的Pod。
[root@master ~]# cat kubia.yaml
apiVersion: v1
kind: Pod
metadata:
name: kubia-manual
spec:
containers:
- image: luksa/kubia
name: kubia
ports:
- containerPort: 8080
protocol: TCP
然后使用“kubectl create -f kubia.yaml”命令即可创建。
[root@master ~]# kubectl create -f kubia.yaml
pod/kubia-manual created
如果配置文件有问题,那么K8S就会报错,多数错误一般都是拼写导致的。使用YAML文件相对比较复杂,可以先来学习更简单的简易创建模式。
K8S为一些常用的资源提供了简易创建的方法,比如说Service、Namespace、Deployment等,这些方法可以使用“kubectl create <资源类型> <资源名>”的方式创建。例如,创建一个名叫hello-world的命名空间,直接使用下面命令即可:
[root@master ~]# kubectl create namespace hello-world
namespace/hello-world created
K8S可以通过配置文件来生成资源,而为了尽可能详细地描述资源的模样,K8S提供了数量庞大的配置项。使用explain命令可以快速地了解到某个配置项的作用,其语法如下:
# kubectl explain <配置名>
要了解创建Pod的基本属性都是干什么的,使用kubectl explain pod命令即可。
[root@master ~]# kubectl explain pod
KIND: Pod
VERSION: v1
DESCRIPTION:
Pod is a collection of containers that can run on a host. This resource is
created by clients and scheduled onto hosts.
FIELDS:
apiVersion <string>
APIVersion defines the versioned schema of this representation of an
object. Servers should convert recognized schemas to the latest internal
value, and may reject unrecognized values. More info:
https://git.K8S.io/community/contributors/devel/api-conventions.md#resources
kind <string>
Kind is a string value representing the REST resource this object
represents. Servers may infer this from the endpoint the client submits
requests to. Cannot be updated. In CamelCase. More info:
https://git.K8S.io/community/contributors/devel/api-conventions.md#types-kinds
metadata <Object>
Standard object‘s metadata. More info:
https://git.K8S.io/community/contributors/devel/api-conventions.md#metadata
spec <Object>
Specification of the desired behavior of the pod. More info:
https://git.K8S.io/community/contributors/devel/api-conventions.md#spec-and-status
status <Object>
Most recently observed status of the pod. This data may not be up to date.
Populated by the system. Read-only. More info:
https://git.K8S.io/community/contributors/devel/api-conventions.md#spec-and-status
可以看到,输出结果中信息很详细,并且每个解释的最后都附带了一条链接,便于更加深入地进行了解。
kubectl delete命令用于删除资源,语法如下:
# kubectl delete <资源类型> <资源名>
例如,删除一个名为kubia-manual的Pod。
[root@master ~]# kubectl delete pod kubia-manual
pod "kubia-manual" deleted
如果要删除所有的Pod,命令如下:
# kubectl delete pod --all
如果要删除一切,命令如下:
# kubectl delete all --all
K8S的每个资源都是通过一个YAML配置文件生成的,哪怕是简易创建的资源,也是K8S从一个默认的配置文件创建而来的。可以使用“kubectl get <资源类型> <资源名> -o yaml”命令查看某个现有资源的配置项。例如,查看kube-proxy-9kx9n的配置项:
# kubectl get pod kube-proxy-9kx9n -n kube-system -o yaml
执行之后就可以看到一个很长的配置列表,使用kubectl edit命令就可以编辑刚才打开的配置列表。
# kubectl edit pod kube-proxy-9kx9n -n kube-system
注意:对于运行中的资源无法修改其名称或类型,可以修改其它属性,例如,将拉取镜像的标签指定为latest。
修改完成后输入“:wq”命令保存即可。
使用“kubectl edit <资源类型> <资源名>”命令可以编辑一个资源的具体配置项,edit命令在实际使用中更偏向于人工修改某个配置项来解决问题。例如,修改镜像地址解决拉取不到镜像的问题。
kubectl edit可以对某个资源进行简单快捷的修改,但是如果想对资源进行大范围的修改,就可以用到kubectl apply命令。其基本用法如下:
# kubectl apply -f <新配置文件名.yaml>
kubeclt apply可以理解成edit命令的升级版,其最大的区别就是,apply接受一个YAML配置文件,而不是打开一个编辑器去修改。K8S在接受到这个配置文件后,会根据metadata中的元数据来查找目标资源,如果没有的话则直接新建;如果有的话就依次比对配置文件之间有什么不同点,然后更新并应用不同的配置。
这么做的好处有很多,例如,通过kubectl apply -f https://some-network-site/resourse.yaml命令从一个网站上部署了用户的资源,这样当它的管理者更新了这个配置文件后,用户只需要再次执行这个命令,就可以应用更新后的内容了,而不用关心到底修改了哪些配置项。
一旦运行了K8S集群,就可以在其上部署容器化应用程序。为此,需要创建Deployment配置,Deployment指示K8S如何创建和更新应用程序的实例。创建Deployment后,K8S调度组件将应用程序实例提到集群中的各个节点上。
创建应用程序实例后,Kubernetes Deployment Controller会持续监控这些实例。如果托管实例的节点关闭或被删除,则Deployment控制器会替换它,K8S提供了一种自我修复机制来解决机器故障或维护问题。
使用kubectl create命令创建一次部署,该部署用于管理Pod。
[root@master ~]# kubectl create deployment my-first-nginx --image=nginx
deployment.apps/my-first-nginx created
命令中nginx为Deployment名称,--image为指定使用的镜像,默认从Docker Hub拉取。
创建完成后查看Deployment列表。
[root@master ~]# kubectl get deployment
NAME READY UP-TO-DATE AVAILABLE AGE
my-first-nginx 1/1 1 1 28s
查看Pod运行状态。
[root@master ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
my-first-nginx-6cbc56bdc4-m6kd9 1/1 Running 0 36s
K8S暴露服务有3种方式,分别为LoadBlancer Service、NodePort Service和Ingress。
Ingress的官方定义为管理对外服务到集群内服务之间规则的集合,可以理解为Ingress定义规则来允许进入集群的请求被转发到集群中对应服务上,从而实现服务暴露。Ingress能把集群内Service配置成外网能够访问的URL,流量负载均衡,终止SSL,提供基于域名访问的虚拟主机等等。
LoadBlancer Service是K8S结合云平台的组件,如国外GCE、AWS、国内阿里云等等,使用它向使用的底层云平台申请创建负载均衡器来实现,但它有局限性,对于使用云平台的集群比较方便。
NodePort Service是通过在节点上暴露端口,然后通过将端口映射到具体某个服务上来实现服务暴露,比较直观方便,但是对于集群来说,随着Service不断增加,需要的端口越来越多,很容易出现端口冲突,而且不容易管理。当然对于小规模的集群服务,还是比较不错的。
NodePort服务是引导外部流量服务的最原始方式。NodePort方式会在所有节点上开放一个特定端口,任何发送到该端口的流量都被转发到对应服务。此处案例采用NodePort的方式来暴露Nginx服务。
[root@master ~]# kubectl expose deployment my-first-nginx --port=80 --type=NodePort
service/my-first-nginx exposed
查看Nginx服务对外暴露的端口。
[root@master ~]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 19h
my-first-nginx NodePort 10.99.186.60 <none> 80:30551/TCP 44s
暴露服务后,就可以通过“http://任意节点IP:30551”来访问Nginx服务了。
图 7-6-3 通过Node节点IP访问Nginx服务
在实际生产系统中,经常会遇到某个服务需要扩容的场景,也可能会遇到由于资源紧张或者工作负载降低而需要减少服务实例数量的场景。此时,可以利用K8S的弹性伸缩功能来完成这些任务。
弹性伸缩是指适应负载变化,以弹性可伸缩方式提供资源。反映到K8S中,可根据负载的高低,动态地调整Pod的副本数。目前K8S提供了API接口实现Pod的弹性伸缩,Pod的副本数本来通过Replication Controller进行控制,所以Pod的弹性伸缩就是修改Replication Controller的Pod副本数,可以通过kubectl scale命令来完成。
通过执行如下命令将Nginx Deployment控制的Pod副本数量从初始的1更新为5。
[root@master ~]# kubectl scale deployment my-first-nginx --replicas=5
deployment.extensions/my-first-nginx scaled
执行kubectl get pods命令来验证Pod的副本数量是否增加到5。
[root@master ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
my-first-nginx-6cbc56bdc4-6qnxj 1/1 Running 0 3m18s
my-first-nginx-6cbc56bdc4-bznzk 1/1 Running 0 3m18s
my-first-nginx-6cbc56bdc4-kghzw 1/1 Running 0 3m18s
my-first-nginx-6cbc56bdc4-m6kd9 1/1 Running 0 15m
my-first-nginx-6cbc56bdc4-tmcxz 1/1 Running 0 3m18s
将--replicas设置为比当前Pod副本数量更小的数字,系统将会“杀掉”一些运行中的Pod,即可实现应用集群缩容。
[root@master ~]# kubectl scale deployment my-first-nginx --replicas=2
deployment.extensions/my-first-nginx scaled
[root@master ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
my-first-nginx-6cbc56bdc4-m6kd9 1/1 Running 0 17m
my-first-nginx-6cbc56bdc4-tmcxz 1/1 Running 0 5m30s
还可以使用autoscale自动设置在K8S集群中运行的pod数量(水平自动伸缩)。
指定Deployment、ReplicaSet或ReplicationController,并创建已经定义好资源的自动伸缩器。使用自动伸缩器可以根据需要自动增加或减少系统中部署的Pod数量。
[root@master ~]# kubectl autoscale deployment my-first-nginx --min=1 --max=10
horizontalpodautoscaler.autoscaling/my-first-nginx autoscaled
设置后,Pod的副本数就会根据负载在1-10之间自动伸缩。
标签:编辑 指示 命名空间 ade nat selinux 加入集群 center 权限控制
原文地址:https://www.cnblogs.com/315z/p/13143700.html