序言
在大部分的k8s教程中,都是先讲k8s的基本理论,对于刚刚接触k8s的同学来说,可能一脸懵逼,望而生畏,所以我带领大家一步步动手部署,在部署的过程中一步步教大家理解其中的概念,让更多的技术人员迈入k8s的大门。
工作目录:
/data:工作的主目录
/data/yaml:放置yaml文件
使用到的镜像:
nginx:1.15.11
php:7.2-fpm
一、让我们认识k8s的Pod
1.1部署第一个pod
在大部分web应用中都离不开nginx,所以我们先从部署一个nginx开始我们的k8s奇妙之旅。
新建yaml文件
viapp-v1.yaml
apiVersion:v1
kind:Pod
metadata:
name:my-app-v1
spec:
containers:
-name:my-nginx
image:nginx:1.15.11
ports:
-containerPort:80
创建我们的第一个pod
kubectlcreate-fapp-v1.yaml
查看我们刚刚建立的pod
kubectlgetpod
接下来让我们解析下上述yaml文件的含义:
apiVersion,指定api版本,我们可以通过kubectlapi-versions命令从我们的k8s集群中获取kind,资源类型,这里我们创建的是一个Pod资源,这里资源类型可以是Deployment、Job、Ingress、Service、ConfigMap等(我们后续都会讲解)metadata:包含了我们定义的Pod的一些meta信息,比如名称、namespace、标签等等spec:包括一些containers,storage,volumes,或者其他Kubernetes需要知道的参数,以及诸如是否在容器失败时重新启动容器的属性请大家记住一个完整的yaml文件必须包含apiVersion、kind、metadata、spec这四个元素。
1.2.1YAML文件的格式
它的基本语法规则如下:
大小写敏感使用缩进表示层级关系缩进时不允许使用Tab键,只允许使用空格。缩进的空格数目不重要,只要相同层级的元素左侧对齐即可#表示注释,从这个字符一直到行尾,都会被解析器忽略。在我们的kubernetes中,只需要两种结构类型就行了:
ListsMaps也就是说,你可能会遇到Lists的Maps和Maps的Lists,等等。不过不用担心,你只要掌握了这两种结构也就可以了,其他更加复杂的我们暂不讨论。
Maps
首先我们来看看Maps,我们都知道Map是字典,就是一个
key:value
的键值对,Maps可以让我们更加方便的去书写配置信息,例如:
---
apiVersion:v1
kind:Pod
第一行的
---
是分隔符,是可选的,在单一文件中,可用连续三个连字号
---
区分多个文件。这里我们可以看到,我们有两个键:
kind
和
apiVersion
,他们对应的值分别是:v1和Pod。上面的YAML文件转换成JSON格式的话,你肯定就容易明白了:
{
apiVersion:v1,
kind:pod
}
我们再创建一个相对复杂一点的YAML文件,创建一个KEY对应的值不是字符串而是一个Maps:
---
apiVersion:v1
kind:Pod
metadata:
name:kube-site
labels:
app:web
上面的YAML文件,metadata这个KEY对应的值就是一个Maps了,而且嵌套的labels这个KEY的值又是一个Map,你可以根据你自己的情况进行多层嵌套。
上面我们也提到了YAML文件的语法规则,YAML处理器是根据行缩进来知道内容之间的关联性的。比如我们上面的YAML文件,使用了两个空格作为缩进,空格的数量并不重要,但是得保持一致,并且至少要求一个空格(什么意思?就是你别一会缩进两个空格,一会缩进4个空格)。
我们可以看到name和labels是相同级别的缩进,所以YAML处理器就知道了他们属于同一个MAP,而app是labels的值是因为app的缩进更大。
注意:在YAML文件中绝对不要使用tab键。
同样的,我们可以将上面的YAML文件转换成JSON文件:
{
apiVersion:v1,
kind:Pod,
metadata:{
name:kube-site,
labels:{
app:web
}
}
}
或许你对上面的JSON文件更熟悉,但是不得不承认YAML文件的语义化程度更高
Lists
Lists就是列表,说白了就是数组,在YAML文件中我们可以这样定义:
args
-Cat
-Dog
-Fish
可以有任何数量的项在列表中,每个项的定义以破折号(-)开头的,与父元素直接可以缩进一个空格。对应的JSON格式如下:
{
args:[Cat,Dog,Fish]
}
当然,list的子项也可以是Maps,Maps的子项也可以是list,如下所示:
---
apiVersion:v1
kind:Pod
metadata:
name:kube-site
labels:
app:web
spec:
containers:
-name:front-end
image:nginx
ports:
-containerPort:80
-name:flaskapp-demo
image:jcdemo/flaskapp
ports:
-containerPort:
比如这个YAML文件,我们定义了一个叫containers的List对象,每个子项都由name、image、ports组成,每个ports都有一个key为containerPort的Map组成,同样的,我们可以转成如下JSON格式文件:
{
apiVersion:v1,
kind:Pod,
metadata:{
name:kube-site,
labels:{
app:web
}
},
spec:{
containers:[{
name:front-end,
image:nginx,
ports:[{
containerPort:
}]
},{
name:flaskapp-demo,
image:jcdemo/flaskapp,
ports:[{
containerPort:
}]
}]
}
}
是不是觉得用JSON格式的话文件明显比YAML文件更复杂了呢?
1.2.2真正理解k8s中的pod
2.1pod的概念
pod是k8s中最重要的概念,所以我们一定要先理解pod的概念。
pod是k8s中调度的最小单元,我们要清楚pod是一个逻辑上的概念,并不存在一个所谓的Pod隔离环境,也就是说,K8s真正处理的还是宿主机操作系统上Linux容器的Namespace和Cgroups,Pod其实是
一组
共享了某些资源的容器。具体的说:Pod里的所有容器,共享的是同一个NetworkNamespace,并且可以声明共享同一个Volume。
2.2pod共享网络
比如说现在有一个Pod,其中包含了一个容器A和一个容器B,它们两个就要共享NetworkNamespace。在Kubernetes里的解法是这样的:它会在每个Pod里,额外起一个Infracontainer小容器来共享整个Pod的NetworkNamespace。
Docker的4种网络模式中有一种模式是container模式,它能够让很多容器共享一个网络名称空间,具体的原理是先使用briage模式启动第一个容器,之后启动的其他容器纷纷使用container模式将网络环境绑定到这第一个容器上。这样这些容器的网络就连接到了一起,他们互相可以使用localhost这种方式进行网络通信。
Infracontainer是一个非常小的镜像,大概~KB左右,是一个汇编语言写的、永远处于“暂停”状态的容器。由于有了这样一个Infracontainer之后,其他所有容器都会通过JoinNamespace的方式加入到Infracontainer的NetworkNamespace中。
所以说一个Pod里面的所有容器,它们看到的网络视图是完全一样的。即:它们看到的网络设备、IP地址、Mac地址等等,跟网络相关的信息,其实全是一份,这一份都来自于Pod第一次创建的这个Infracontainer,这就是Pod解决网络共享的一个解法。
在Pod里面,一定有一个IP地址,是这个Pod的NetworkNamespace对应的地址,也是这个Infracontainer的IP地址。所以大家看到的都是一份,而其他所有网络资源,都是一个Pod一份,并且被Pod中的所有容器共享,这就是Pod的网络实现方式。
由于需要有一个相当于说中间的容器存在,所以整个Pod里面,必然是Infracontainer第一个启动,并且整个Pod的生命周期是等同于Infracontainer的生命周期的,与容器A和B是无关的。这也是为什么在Kubernetes里面,它是允许去单独更新Pod里的某一个镜像的,即:做这个操作,整个Pod不会重建,也不会重启,这是非常重要的一个设计。
注意:这种共享网络机制,可以让同一个pod中的容器访问使用localhost,同时也限制了同一个pod中的容器端口不能重复。
2.3pod共享存储
Pod共享存储就相对比较简单,现在有两个容器,一个是Nginx,另外一个是php容器,在Nginx里放一些文件,让我能通过Nginx访问到,如果访问的是php文件,PHP-FastCGI也要能够访问到,所以我们需要去share这个目录,我share文件或者是share目录在Pod里面是非常简单的,实际上就是把volume变成了Podlevel。然后所有容器,就是所有同属于一个Pod的容器,他们共享所有的volume。
1.2部署Nginx+PHP
我们改造一下我们的第一个pod,验证一下pod中的容器是共享网络的和如何实现共享存储。
新建yaml文件
viapp-v2.yaml
apiVersion:v1
kind:Pod
metadata:
name:my-app-v2
spec:
containers:
-name:my-nginx
image:nginx:1.15.11
ports:
-containerPort:
volumeMounts:
-name:app-