楼上回答不够准确
原因是 Go的基础类型是值传递的,这就使得很多基础类型包括struct传递给goroutine时都是copy过去的,不存在现成安全问题,如果传递指针类型,对于指针类型本身也是copy过去的,只是传递过去的是地址而已。
引用传递的只有map、slice和chan,chan本来就是用于多协程,一般不会遇到这个问题。
map比slice更容易用于不安全的线程场景。所以一般都用map为例。
另外,go还提供了线程安全的map类型 sync包,了解一下
没有足够的数据
holdno 赞了回答 · 2019-11-06
楼上回答不够准确
原因是 Go的基础类型是值传递的,这就使得很多基础类型包括struct传递给goroutine时都是copy过去的,不存在现成安全问题,如果传递指针类型,对于指针类型本身也是copy过去的,只是传递过去的是地址而已。
引用传递的只有map、slice和chan,chan本来就是用于多协程,一般不会遇到这个问题。
map比slice更容易用于不安全的线程场景。所以一般都用map为例。
另外,go还提供了线程安全的map类型 sync包,了解一下
原因是 Go的基础类型是值传递的,这就使得很多基础类型包括struct传递给goroutine时都是copy过去的,不存在现成安全问题,如果传递指针类型,对于指针类型本身也是copy过去的,只是传递过去的是地址而已。
关注 7 回答 4
holdno 回答了问题 · 2019-11-04
又查阅了相关资料
自己总结一下
golang优化了time.Sleep()方法,避免其进行系统调用从而带来一些系统级别的开销
当我们在goroutine中调用time.Sleep()方法时
底层会创建一个 timer, 并将当前goroutine与timer进行绑定,并提供一个唤醒该goroutine的方法goroutineReady
然后通过addtimerLocked
方法启动全局时间事件驱动器timerproc
并将timer加入到全局timer管理器中(timer 通过最小堆的数据结构存放每个定时器),且每一个加入到timer管理器中的timer都会被按唤醒时间进行排序,最早需要唤醒的timer排在最前面
再通过gopark()方法将当前goroutine状态标记为为_Gwaiting
解除G与M的绑定关系
调用schedule()函数,重新选择一个goroutine去执行
如何被唤醒呢?
timer管理器会寻找timer定时管理器的第一个timer并判断唤醒条件,满足条件后通过调用goroutineReady
方法进行唤醒,主要操作是将对应的goroutine标记为_Grunnable并将其添加到P队列中等待被M执行
golang优化了time.Sleep()方法,避免其进行系统调用从而带来一些系统级别的开销 当我们在goroutine中调用time.Sleep()方法时 底层会创建一个 timer, 并将当前goroutine与timer进行绑定,并提供一个唤醒该goroutine的方法goroutineReady 然后通过addtimerLocked方法启...
关注 1 回答 2
holdno 提出了问题 · 2019-11-04
其实这个问题就是想了解
golang中time.Sleep是如何实现的
在这背后调度器对time.Sleep又是如何处理的?
其实这个问题就是想了解 golang中time.Sleep是如何实现的 在这背后调度器对time.Sleep又是如何处理的?
关注 1 回答 2
holdno 发布了文章 · 2019-09-25
A service account provides an identity for processes that run in a Pod
service account:服务账户,服务账户是针对运行在pod中的进程而言的
kubernetes中账户分为两种类型,用户账户和服务账户
通过上文的介绍已知,服务账户是跟随Pod进程自动创建的授权体系,通过它,使用者可以在Pod所在的namespace下为所欲为(哈哈哈
且服务账户默认是开启的
也就是说,如果是一个对外开放的k8s容器平台,且某个namespace没有设置对应的quota,或者quota足够大,那么部署在其下的业务方就可以通过k8s提供的client-go
,rest.InClusterConfig()
方法对当前namespace下进行部署(或其他)操作,这将导致集群中出现一批不受管控的pod(或可以讲作预期之外的pod以及其他资源),作为平台方应该是不希望出现这个场景的。
好在kubernetes 1.6以上版本可以手动控制禁用此能力
引用官方介绍:
In version 1.6+, you can opt out of automounting API credentials for a service account by setting automountServiceAccountToken: false on the service account:
apiVersion: v1
kind: ServiceAccount
metadata:
name: build-robot
automountServiceAccountToken: false
...
In version 1.6+, you can also opt out of automounting API credentials for a particular pod:
apiVersion: v1
kind: Pod
metadata:
name: my-pod
spec:
serviceAccountName: build-robot
automountServiceAccountToken: false
...
The pod spec takes precedence over the service account if both specify a automountServiceAccountToken value.
所以,应该在非特殊业务场景下,禁用掉automountServiceAccountToken
,从而进一步保证集群的可控性
A service account provides an identity for processes that run in a Pod
赞 0 收藏 0 评论 0
holdno 赞了回答 · 2019-09-18
从来不建议用join,会有很多不可预期的问题,而且不好优化,建议分开查询,先查activity,然后查出user_ids, 然后去查user信息,merge到activity上面,就ok了,这样写好处很多,最大好处就是逻辑清晰,易于维护。 再,请慎用join
从来不建议用join,会有很多不可预期的问题,而且不好优化,建议分开查询,先查activity,然后查出user_ids, 然后去查user信息,merge到activity上面,就ok了,这样写好处很多,最大好处就是逻辑清晰,易于维护。 再,请慎用join
关注 5 回答 4
holdno 发布了文章 · 2019-09-18
上篇文章有提到,通过POD ID只能够在k8s集群内部进行访问,作为一个博客!只给自己看...好像也行啊...
但是每次都要先登录到集群节点中才能看...这就...(脑海里闪过成吨shell... ssh@192.10o... kubectl get po...,噗~!
这是在玩自己吗?
集群都有了,服务也部了,咋就不能让他与万物互联了呢?
接下来我们就一同探秘,k8s中服务是如何暴露出去的
Kubernetes 的Pod的寿命是有限的。它们出生然后死亡,它们不会复活。ReplicationController是特别用来动态的创建和销毁Pods(如:动态伸缩或者执行rolling updates中动态的创建和销毁pod)。尽管每个Pod有自己的IP地址,随着时间变化随着时间推移即使这些IP地址也不能被认为是可靠的。这带来一个问题:如果一些Pod的集合(让我们称之为backends)为集群中的其他的Pod提供了一些功能(让我们称它们为frontends),这些frontends应该如何找到并一直知道哪些backends在这样的集合中呢?
Kubernetes的Service是一种抽象,它定义了一组Pods的逻辑集合和一个用于访问它们的策略 - 有的时候被称之为微服务。
举个例子,想象一下我们的博客后端运行了三个副本。这些副本都是可以替代的 - 前端不关心它们使用的是哪一个后端。尽管实际组成后端集合的Pod可能会变化,前端的客户端却不需要知道这个变化,也不需要自己有一个列表来记录这些后端服务。Service抽象能让你达到这种解耦。
Kubernetes中的Service是一个REST对象,这点与Pod类似。正如所有的REST对象一样,向apiserver POST一个Service的定义就能创建一个新的实例。以上篇文章中的echoserver为例,每一个Pod都开放了80端口,并且都有一个"app=my-blog"的标签。
{
"kind": "Service",
"apiVersion": "v1",
"metadata": {
"name": "my-blog"
},
"spec": {
"selector": {
"app": "my-blog"
},
"ports": [
{
"protocol": "TCP",
"port": 8080,
"targetPort": 80
}
]
}
}
这个定义会创建一个新的Service对象,名字为”my-blog”,它指向所有带有”app=my-blog”标签的Pod上面的80端口。这个Service同时也会被分配一个IP地址(有时被称作”cluster ip”),它会被服务的代理所使用(见下面)。这个Service的选择器,会不断的对Pod进行筛选,并将结果POST到名字同样为“my-blog”的Endpoints对象。
注意一个Service能将一个来源的端口映射到任意的targetPort。默认情况下,targetPort会被设置成与port字段一样的值。可能更有意思的地方在于,targetPort可以是一个字符串,能引用一个后端Pod中定义的端口名。实际指派给该名称的端口号在每一个Pod中可能会不同。这为部署和更新你的Service提供了很大的灵活性。例如,你可以在你的后端的下一个版本中更改开放的端口,而无需导致客户出现故障。
Kubernetes的Service支持TCP和UDP协议。默认是TCP。
说到service不得不提 endpoint。在 Service 创建的同时,还生成了一个 Endpoints。 该 Endpoints 与 Service 同名,它所暴露的地址信息正是对应 Pod 的地址。由此猜测是 Endpoints 维护了 Service 与 Pod 的映射关系。
为了验证我们的猜测,我们手动删除 Endpoints,发现之前能成功访问到 Pod 的 VIP,现在已经已经访问不到了。
ServiceController 主要处理的还是与 LoadBalancer 相关的逻辑,但是 EndpointController 的作用就没有这么简单了,我们在使用 Kubernetes 时虽然很少会直接与 Endpoint 资源打交道,但是它却是 Kubernetes 中非常重要的组成部分。
EndpointController 本身并没有通过 Informer 监听 Endpoint 资源的变动,但是它却同时订阅了 Service 和 Pod 资源的增删事件,它会根据 Service 对象规格中的选择器 Selector 获取集群中存在的所有 Pod,并将 Service 和 Pod 上的端口进行映射生成一个 EndpointPort 结构体,对于每一个 Pod 都会生成一个新的 EndpointSubset,其中包含了 Pod 的 IP 地址和端口和 Service 的规格中指定的输入端口和目标端口,在最后 EndpointSubset 的数据会被重新打包并通过客户端创建一个新的 Endpoint 资源。所以,除了 Service 的变动会触发 Endpoint 的改变之外,Pod 对象的增删也会触发。
在我的理解,service就是将不断变动的pod做了一个固定的端口映射(别管pod怎么变化,怎么漂移,这是我的事),你只需要处理service暴露出来的固定端口即可。到底是谁来处理service暴露出来的端口呢?那当然是代理了。
我们知道 Service 的代理是由 kube-proxy 实现的。而它的代理模式(Proxy mode)主要有两种:userspace 与 iptables。自 K8S v1.2 开始,默认的代理模式就是 iptables(/ipvs),并且它的性能也是要高于 userspace 的
userspace是在用户空间,通过kuber-proxy实现LB的代理服务。这个是kube-proxy的最初的版本,较为稳定,但是效率也自然不太高。
iptables的方式。是纯采用iptables来实现LB。是目前一般kube默认的方式。
ipvs:这种模式从Kubernetes 1.11进入GA,并在Kubernetes 1.12成为kube-proxy的默认代理模式。ipvs模式也是基于netfilter,对比iptables模式在大规模Kubernetes集群有更好的扩展性和性能,支持更加复杂的负载均衡算法(如:最小负载、最少连接、加权等),支持Server的健康检查和连接重试等功能。ipvs依赖于iptables,使用iptables进行包过滤、SNAT、masquared。ipvs将使用ipset需要被DROP或MASQUARED的源地址或目标地址,这样就能保证iptables规则数量的固定,我们不需要关心集群中有多少个Service了。
我们现在要做的呢,是将 VIP 请求给转发到对应的 Pod 上。而实现此的正是 iptables(/ipvs)。
举个例子,现在有podA,podB,podC和serviceAB。serviceAB是podA,podB的服务抽象(service)。 那么kube-proxy的作用就是可以将pod(不管是podA,podB或者podC)向serviceAB的请求,进行转发到service所代表的一个具体pod(podA或者podB)上。 请求的分配方法一般分配是采用轮询方法进行分配。
那有了kube-proxy,service是如何暴露的呢?
service的ServiceTypes能让你指定你想要哪一种服务。默认的和基础的是ClusterIP,这会开放一个服务可以在集群内部进行连接。NodePort 和LoadBalancer是两种会将服务开放给外部网络的类型。
apiVersion: v1
kind: Service
metadata:
# Service 实例名称
name: my-blog
spec:
ports:
- protocol: TCP
# Service 端口地址
port: 8080
# Pod 端口地址
targetPort: 80
selector:
# 匹配符合标签条件的 Pod
app: my-blog
type: NodePort <- Service Type
ServiceType字段的合法值是:
ClusterIP: 仅仅使用一个集群内部的IP地址 - 这是默认值,在上面已经讨论过。选择这个值意味着你只想这个服务在集群内部才可以被访问到。
NodePort: 在集群内部IP的基础上,在集群的每一个节点的端口上开放这个服务。你可以在任意<NodeIP>:NodePort地址上访问到这个服务。
如果定义为NodePort,Kubernetes master 将从给定的配置范围内(默认:30000-32767)分配端口,每个 Node 将从该端口代理到 Service。该端口将通过 Service 的 spec.ports[*].nodePort 字段被指定。那么我们就可以使用任意节点IP:NodePort来访问到my-blog容器的80端口
LoadBalancer: 在使用一个集群内部IP地址和在NodePort上开放一个服务之外,向云提供商申请一个负载均衡器,会让流量转发到这个在每个节点上以<NodeIP>:NodePort的形式开放的服务上。
在使用一个集群内部IP地址和在NodePort上开放一个Service的基础上,还可以向云提供者申请一个负载均衡器,将流量转发到已经以NodePort形式开发的Service上。
将上述service文件保存并执行命令
$ kubectl create -f service.yaml
service/my-blog created
表明我们成功创建了一个使用NodePort方式暴露的service
下面我们来验证一下
通过上第一行命令,我们可以看到刚刚创建的service已经以NodePort的方式对外暴露(第一个红框),暴露的端口号是32388,通过另外两行命令我们的项目my-blog部署在哪一台物理机节点上(对应EXTERNAL-IP就是物理机的真实IP),这里害怕遇到调皮的同学,所以给它加了个衣服:P
尝试通过IP+Port的方式请求一下
Amazing! 通了!终于不是独乐乐了~
不过... 以用户的习惯,通过IP+端口号来访问的姿势可能无法接受
可能我们还需要再搞个域名,让用户可以直接通过域名来访问我们的博客
这时候有些机灵鬼就跳出来了,这还不简单!上面再搭个nginx不就行了...
确实是这样...但是如果我再多部署一个博客呢?(那就再加一条nginx解析记录呗...我们不是一直这样用的吗???
那...我们还搞个毛k8s
k8s可不是为了让你部署一两个服务而诞生的...它是为了成千上万个服务~(那你为什么在教我们用k8s部署博客???我:你今天有点话多
如果每一个服务都要手动去修改nginx解析记录...你就不怕什么时候你手多抖两下吗?
总结一下上面service直接暴露服务的一些缺点
而Ingress就是为了解决上面的问题而诞生的
Ingress 的实现分为两个部分 Ingress Controller 和 Ingress .
Ingress Controller 会监听 api server上的 /ingresses 资源 并实时生效。
Ingerss 描述了一个或者多个 域名的路由规则,以 ingress 资源的形式存在。
简单说: Ingress 描述路由规则, Ingress Controller 负责动态实现规则。
下面我们来动手实现一下ingress,通过动手来学习
首先需要在我们的集群中安装ingress-nginx-controller
官方文档:https://kubernetes.github.io/...
// 登录集群,在集群中执行以下命令安装 ingress-nginx
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/mandatory.yaml
验证:
$ kubectl get pod -n ingress-nginx
NAME READY STATUS RESTARTS AGE
nginx-ingress-controller-66f7bd6b88-fwshc 1/1 Running 0 8s
说明安装成功
但是官方提供的这个yaml并没有指定nginx-ingress-controller自己本身的暴露方式
下面我们来手动设置以hostNetWork的方式暴露服务
$ kubectl edit deploy nginx-ingress-controller -n ingress-nginx
// 修改hostNetWork为true
spec.template.spec.hostNetWork: ture
deployment.extensions/nginx-ingress-controller edited
修改成功!下面我们来创建一个ingress资源
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: my-blog
namespace: test
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "false" // 这个参数标识 不强制要求使用HTTPS协议进行通信,文章结尾会讲到https的支持
spec:
rules:
- host: blog.holdno.com
http:
paths:
- path: /
backend:
serviceName: my-blog
servicePort: 8080
通过上面的yaml文件我们可以很清晰的看出,我们想要对用户暴露的域名是 blog.holdno.com,解析规则是根路径"/",对应的service为my-blog,转发到service的8080端口(前面我们service暴露的是8080端口到POD的80端口)
$ kubectl create -f ingress.yaml
ingress.extensions/my-blog created
$ kubectl get ingress -n test
NAME HOSTS ADDRESS PORTS AGE
my-blog blog.holdno.com 80 10s
ingress我们就部署成功了
通过上面两条命令找到nginx-ingress-controller所在的节点的真实IP(在部署ingress时最好是指定某几台性能较好的节点,通过nodeSelector进行调度)
通过域名解析平台将我们的域名blog.holdno.com解析到节点IP上
curl blog.holdno.com
// 下面会出现完美的一幕,大家亲手实践一下吧
到此,我们便成功的建立了从服务到用户之间的桥梁!之后无论POD怎么漂移,对于上层的ingress都是无谓的
虽然桥通了,但是感觉部署一个服务的过程很是心累...
后面的文章,我们将通过k8s提供的client-go来为大家梳理出一套自动化的部署流程,让我们的博客可以一键上云!
既然支持http了 自然少不了https的需求
那么我们就动手一步一步实现 ingress https的支持吧
首先需要了解的是 k8s内置的 secret资源,用来保存证书信息
我们通过阿里云购买一个免费的证书,并下载到机器上。(假设保存路径为 /ssl/blog.crt 和 /ssl/blog.key)
$ kubectl create secret tls my-secret --cert /ssl/blog.crt --key ./ssl/blog.key --namespace test // 一定要注意,secret是区分namespace的,不同namespace下的ingress资源和secret资源互相不可见
$ secret/blog created
修改我们的ingress配置
$ kubectl edit ingress my-blog -n test
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"extensions/v1beta1","kind":"Ingress","metadata":{"annotations":{"kubernetes.io/ingress.class":"nginx","nginx.ingress.kubernetes.io/ssl-redirect":"false"},"name":"my-blog","namespace":"test"},"spec":{"rules":[{"host":"blog.holdno.com","http":{"paths":[{"backend":{"serviceName":"my-blog","servicePort":777},"path":"/"}]}}]}}
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/ssl-redirect: "false" // 如果要强制使用https协议,需要将这个参数改为 true
creationTimestamp: "2019-09-17T12:20:01Z"
generation: 3
name: my-blog
namespace: test
resourceVersion: "1107154526"
selfLink: /apis/extensions/v1beta1/namespaces/test/ingresses/my-blog
uid: 78bac7dc-d945-11e9-b8cf-9a285cd2373e
spec:
rules:
- host: blog.holdno.com
http:
paths:
- backend:
serviceName: my-blog
servicePort: 8080
path: /
tls: <- 关键配置
- hosts:
- blog.holdno.com # 证书对应的host
secretName: blog # 对应的证书 也就是我们上一步创建的secret
status:
loadBalancer: {}
ingress.extensions/my-blog edited
接下里我们就可以在浏览器中访问 https://blog.holdno.com
来验证一下。
上篇文章有提到,通过POD ID只能够在k8s集群内部进行访问,作为一个博客!只给自己看...好像也行啊... 但是每次都要先登录到集群节点中才能看...这就...(脑海里闪过成吨shell... ssh@192.10o... kubectl get po...,噗~! 这是在玩自己吗?
赞 0 收藏 0 评论 0
holdno 发布了文章 · 2019-09-18
通过上一篇文章的摸爬滚打,一个“完整”的kubernetes集群已经出现在我们眼前。通过目光可以看出我们现在是又兴奋又焦虑...
k8s有是有了,它怎么用???
它怎么用???
它怎么用???
(我也是一脸懵逼,但我不敢说啊
(硬着头皮
那我们先来摸索一下,k8s界的helloworld:echoserver
名如其人,这就是k8s的部署模块
首先我们先创建一个名为test的namespace来关联我们的测试行为
namespace是什么:Namespace(命名空间)是kubernetes系统中的另一个重要的概念,通过将系统内部的对象“分配”到不同的Namespace中,形成逻辑上分组的不同项目、小组或用户组,便于不同的分组在共享使用整个集群的资源的同时还能被分别管理。
我们可以通过创建一个Kubernetes Deployment对象来运行一个应用, 可以在一个YAML文件中描述Deployment. 例如, 下面这个YAML文件描述了一个k8s界的helloworld
apiVersion: extensions/v1beta1
kind: Deployment // 定义对象类型
metadata:
name: my-blog // 定义服务名称
namespace: test // 定义namespace
spec:
replicas: 1 // 定义该服务在集群中初始部署的个数,这也是未来能够支撑我们发布并发出轨文章的重要参数之一
selector:
matchLabels:
app: my-blog
template:
metadata:
labels:
app: my-blog
spec:
containers: // 定义POD中容器的具体信息
- name: my-blog
image: googlecontainer/echoserver:1.10
ports:
- containerPort: 8080 // 定义容器对外暴露的端口
env: // 设置容器的环境变量
- name: NODE_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
通过内容可以看出,这是一个被命名为my-blog的项目,监听容器的8080端口,容器镜像是一个docker hub上公共的echoserver镜像
我们将上述内容保存在一个echoserver.yaml文件中
通过执行命令 kubectl create -f echoserver.yaml
可以看到控制台输出 deployment.extensions/my-blog created
则表示我们的demo已经被部署在了k8s集群中
验证:
$ kubectl get deploy -n test
NAME READY UP-TO-DATE AVAILABLE AGE
my-blog 1/1 1 1 3h22m
验证无误!
接下来我们就试着请求一下看看,是否可以真的工作
// 由于本专栏的重点是从0-1部署个人博客,所以k8s相关的操作就简单解释一下:P
// 首先我们先获取由上一步deploy出来的pod信息
$ kubectl get pod -n test -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
my-blog-7bcb7cdb76-jkcsh 1/1 Running 0 3h25m 172.16.0.5 10.0.0.9 <none> <none>
什么是POD:在Kubernetes中,最小的管理元素不是一个个独立的容器,而是Pod,Pod是最小的,管理,创建,计划的最小单元。
一个Pod(就像一群鲸鱼,或者一个豌豆夹)相当于一个共享context的配置组,在同一个context下,应用可能还会有独立的cgroup隔离机制,一个Pod是一个容器环境下的“逻辑主机”,它可能包含一个或者多个紧密相连的应用,这些应用可能是在同一个物理主机或虚拟机上。
Pod 的context可以理解成多个linux命名空间的联合
PID 命名空间(同一个Pod中应用可以看到其它进程)
网络 命名空间(同一个Pod的中的应用对相同的IP地址和端口有权限)
IPC 命名空间(同一个Pod中的应用可以通过VPC或者POSIX进行通信)
UTS 命名空间(同一个Pod中的应用共享一个主机名称)
同一个Pod中的应用可以共享磁盘,磁盘是Pod级的,应用可以通过文件系统调用,额外的,一个Pod可能会定义顶级的cgroup隔离,这样的话绑定到任何一个应用(好吧,这句是在没怎么看懂,就是说Pod,应用,隔离)
找到POD IP后我们尝试对它发起一个GET请求,
$ curl 172.16.0.5:8080 // 上述配置文件显示,my-blog这个项目的容器对外监听的是8080端口
// 成功得到请求结果
Hostname: my-blog-7bcb7cdb76-jkcsh
Pod Information:
node name: 10.0.0.9
pod name: my-blog-7bcb7cdb76-jkcsh
pod namespace: test
pod IP: 172.16.0.5
Server values:
server_version=nginx: 1.13.3 - lua: 10008
Request Information:
client_address=172.16.0.1
method=GET
real path=/
query=
request_version=1.1
request_scheme=http
request_uri=http://172.16.0.5:8080/
Request Headers:
accept=*/*
host=172.16.0.5:8080
user-agent=curl/7.47.0
Request Body:
-no body in request-
感动,居然成功了。想想我们把它换成真正的博客页面,那是多么美好的一件事情!
不对...
这个POD的IP是怎么回事?172.16.0.5这个IP我好想没见过啊,我的机器不是这个IP啊!
前面有讲到,Kubernetes的最小部署单元是Pod。k8s利用Flannel(或其他网络插件)作为不同HOST之间容器互通技术时,由Flannel和etcd维护了一张节点间的路由表。Flannel的设计目的就是为集群中的所有节点重新规划IP地址的使用规则,从而使得不同节点上的容器能够获得“同属一个内网”且”不重复的”IP地址,并让属于不同节点上的容器能够直接通过内网IP通信。
每个Pod启动时,会自动创建一个镜像为gcr.io/google_containers/pause:0.8.0的容器,容器内部与外部的通信经由此容器代理,该容器的IP也可以称为Pod IP。
原来啊,这些IP是Flannel创建出来的一个个内网IP地址,顾名思义,这个IP只有在集群网络内才可以通信。那作为博客站点,光能自己看有啥意思啊!唉,感觉忙活了半天坑了自己。
好在今天还是有点收获,实现了k8s的helloworld。但是只能内网通信的POD........可咋整???
查看原文通过上一篇文章的摸爬滚打,一个“完整”的kubernetes集群已经出现在我们眼前。通过目光可以看出我们现在是又兴奋又焦虑...k8s有是有了,它怎么用???它怎么用???它怎么用???(我也是一脸懵逼,但我不敢说啊
赞 0 收藏 0 评论 0
holdno 发布了文章 · 2019-09-17
每个程序员都希望能够一个属于自己的个人站点。那么就跟着作者,通过本专栏一起来搭建一个个人站点吧!
想到搭建个人站点,最基础的也就是准备好我们的运行环境(不应该是开发环境吗???,开发环境因人而异,语言千万种选择自己喜欢的就好啦)。
吸取日常的生活经验,我们领会到凡事三思而后行。
首先我们来分析下需求,个人站点、个人博客,为了能够让读者随时随地的浏览,那肯定离不开一个稳定的运行环境。
谈到稳定的环境,不得不皱起了三层厚的眉头。从我们本地开发,到测试环境,到生产环境...凭一己之力实在是不能保证运行环境的一致性。那我们肯定是要祭出解决环境一致性问题的神器 Docker。
想到Docker,额头总算是平整了一些。但是想到日后万一我的博客发出了世纪新闻(并发出轨?),那这弱小的docker那肯定是扛不住了呀。怎么办呢???(上k8s啊
啥?
k8s???
哦对,借助k8s的自动扩容无限伸缩搞定它!那...那我们用k8s来部署我们的个人站点吧!如果不想接数据库还能用用configmap来写写文章你说是不是。简直就是为博客而生啊!
那就让我们开始动手吧~
(不负责声明,本专栏所有内容是作者将自己的学习过程分享给大家,如有误导请指正
查看原文想到搭建个人站点,最基础的也就是准备好我们的运行环境(不应该是开发环境吗???,开发环境因人而异,语言千万种选择自己喜欢的就好啦)。吸取日常的生活经验,我们领会到凡事三思而后行。首先我们来分析下需求,个人站点、个人博客,为了能够让读者随时随地的浏览...
赞 0 收藏 0 评论 0
holdno 发布了文章 · 2019-09-16
RKE:Ranchar Kubernetes Engine
https://github.com/rancher/rke
由于RKE是由golang编写,所以直接下载对应系统的二进制文件即可
下载地址: https://github.com/rancher/rk...
1.建议使用Ubuntu 16.04.3 LTS版本;如果使用CentOS 7,建议7.3以上版本
2.各主机的hostname主机名必须不同!
3.hosts文件设置:/etc/hosts要配置正确,一定要有127.0.0.1 localhost 这一项;hosts文件中包含所有主机节点的IP和名称列表。
通过Rancher提供的脚本进行安装(以下是最新支持的docker版本)
Docker版本 | 安装脚本 |
---|---|
18.09.2 | curl https://releases.rancher.com/... | sh |
18.06.2 | curl https://releases.rancher.com/... | sh |
17.03.2 | curl https://releases.rancher.com/... | sh |
RKE通过SSH tunnel进行安装部署,需要事先建立RKE到各节点的SSH免密登录。如果集群中有3个节点,需要在RKE机器上执行1遍秘钥生成命令ssh-keygen,并将生成侧公钥通过分发命令:ssh-copy-id {user}@{ip}。
在各个节点上创建ssh用户,并将其添加至docker组中:
useradd dockeruser
usermod -aG docker dockeruser
注意:重启系统以后才能生效,只重启Docker服务是不行的!重启后,docker_user用户也可以直接使用docker run命令。
Ubuntu 16.04默认未安装,无需设置。
1)CentOS7下可修改配置文件
vi /etc/sysconfig/selinux
2)设置 SELINUX=disabled,重启后永久关闭。
必须开启!Ubuntu 16.04下默认已启用,无需设置。
1)CentOS7 下可编辑配置文件:
vi /etc/sysctl.conf
2)设置:
net.ipv4.ip_forward = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
3)执行如下命令生效:
sudo sysctl -p
开放集群主机节点之间6443、2379、2380端口,如果是刚开始试用,可以先关闭防火墙;
systemctl stop firewalld
Ubuntu默认未启用UFW防火墙,无需设置。也可手工关闭:sudo ufw disable
一定要禁用swap,否则kubelet组件无法运行。
1)永久禁用swap
可以直接修改
vi /etc/fstab
文件,注释掉swap项。
2)临时禁用
swapoff -a
修改配置文件/etc/default/grub,启用cgroup内存限额功能,配置两个参数:
GRUB_CMDLINE_LINUX_DEFAULT="cgroup_enable=memory swapaccount=1"
GRUB_CMDLINE_LINUX="cgroup_enable=memory swapaccount=1"
注意:要执行sudo update-grub 更新grub,然后重启系统后生效。
CentOS下执行:
grub2-mkconfig -o /boot/grub2/grub.cfg
1)在rke所在主机上创建密钥:
ssh-keygen
2)将所生成的密钥的公钥分发到各个节点:
ssh-copy-id dockeruser@xxx.xxx.xx.xx
...
可以输入命令rke_drawin-amd64 config(我本机是mac,所以使用drawin-amd64版本),跟着引导完成基础配置
配置完成后将会在当前目录下出现一个 cluster.yml
下面是该配置文件的内容(这里我配置的是包含两个节点的k8s集群,其中一个master,rke很容易支持HA部署,仅仅需要在配置文件中对多个节点指定role.controlplane即可)
nodes:
- address: xxx.xxx.xxx.xxx
port: "22"
internal_address: ""
role:
- controlplane
- worker
- etcd
hostname_override: master
user: dockeruser
docker_socket: /var/run/docker.sock
labels: {}
- address: xxx.xxx.xxx.xxx
port: "22"
internal_address: ""
role:
- worker
hostname_override: node-1
user: dockeruser
docker_socket: /var/run/docker.sock
ssh_key: ""
ssh_key_path: ""
ssh_cert: ""
ssh_cert_path: ""
labels: {}
services:
etcd:
image: ""
extra_args: {}
extra_binds: []
extra_env: []
external_urls: []
ca_cert: ""
cert: ""
key: ""
path: ""
snapshot: null
retention: ""
creation: ""
backup_config: null
kube-api:
image: ""
extra_args: {}
extra_binds: []
extra_env: []
service_cluster_ip_range: 10.43.0.0/16
service_node_port_range: ""
pod_security_policy: false
always_pull_images: false
kube-controller:
image: ""
extra_args: {}
extra_binds: []
extra_env: []
cluster_cidr: 10.42.0.0/16
service_cluster_ip_range: 10.43.0.0/16
scheduler:
image: ""
extra_args: {}
extra_binds: []
extra_env: []
kubelet:
image: ""
extra_args: {}
extra_binds: []
extra_env: []
cluster_domain: cluster.local
infra_container_image: ""
cluster_dns_server: 10.43.0.10
fail_swap_on: false
kubeproxy:
image: ""
extra_args: {}
extra_binds: []
extra_env: []
network:
plugin: flannel
options: {}
authentication:
strategy: x509
sans: []
webhook: null
addons: ""
addons_include: []
system_images:
etcd: rancher/coreos-etcd:v3.3.10-rancher1
alpine: rancher/rke-tools:v0.1.42
nginx_proxy: rancher/rke-tools:v0.1.42
cert_downloader: rancher/rke-tools:v0.1.42
kubernetes_services_sidecar: rancher/rke-tools:v0.1.42
kubedns: rancher/k8s-dns-kube-dns:1.15.0
dnsmasq: rancher/k8s-dns-dnsmasq-nanny:1.15.0
kubedns_sidecar: rancher/k8s-dns-sidecar:1.15.0
kubedns_autoscaler: rancher/cluster-proportional-autoscaler:1.3.0
coredns: rancher/coredns-coredns:1.3.1
coredns_autoscaler: rancher/cluster-proportional-autoscaler:1.3.0
kubernetes: rancher/hyperkube:v1.14.6-rancher1
flannel: rancher/coreos-flannel:v0.10.0-rancher1
flannel_cni: rancher/flannel-cni:v0.3.0-rancher1
calico_node: rancher/calico-node:v3.4.0
calico_cni: rancher/calico-cni:v3.4.0
calico_controllers: ""
calico_ctl: rancher/calico-ctl:v2.0.0
canal_node: rancher/calico-node:v3.4.0
canal_cni: rancher/calico-cni:v3.4.0
canal_flannel: rancher/coreos-flannel:v0.10.0
weave_node: weaveworks/weave-kube:2.5.0
weave_cni: weaveworks/weave-npc:2.5.0
pod_infra_container: rancher/pause:3.1
ingress: rancher/nginx-ingress-controller:0.21.0-rancher3
ingress_backend: rancher/nginx-ingress-controller-defaultbackend:1.5-rancher1
metrics_server: rancher/metrics-server:v0.3.1
ssh_key_path: ~/.ssh/id_rsa
ssh_cert_path: ""
ssh_agent_auth: true
authorization:
mode: rbac
options: {}
ignore_docker_version: false
kubernetes_version: ""
private_registries: []
ingress:
provider: ""
options: {}
node_selector: {}
extra_args: {}
cluster_name: ""
cloud_provider:
name: ""
prefix_path: ""
addon_job_timeout: 0
bastion_host:
address: ""
port: ""
user: ""
ssh_key: ""
ssh_key_path: ""
ssh_cert: ""
ssh_cert_path: ""
monitoring:
provider: ""
options: {}
restore:
restore: false
snapshot_name: ""
dns: null
rke_drawin-amd64 up
可以看到脚本开始输入一些日志信息
最终出现
Finished building Kubernetes cluster successfully
则表示集群安装成功。
集群安装成功后,RKE会在当前目录创建一个kube_config_cluster.yml文件,这个文件就是kubeconfig文件
默认情况下,kube配置文件被称为.kube_config_cluster.yml。将这个文件复制到你的本地~/.kube/config,就可以在本地使用kubectl了。
需要注意的是,部署的本地kube配置名称是和集群配置文件相关的。例如,如果您使用名为mycluster.yml的配置文件,则本地kube配置将被命名为.kube_config_mycluster.yml。
export KUBECONFIG=./kube_config_cluster.yml
kubectl get node
NAME STATUS ROLES AGE VERSION
master Ready controlplane,etcd,worker 6m27s v1.14.6
node-1 Ready worker 6m5s v1.14.6
看到节点信息表明安装成功
RKE支持为角色为worker和controlplane的主机添加或删除节点。
1)添加节点:
要添加其他节点,只需要更新具有其他节点的集群配置文件,并使用相同的文件运行集群配置即可。
2)删除节点:
要删除节点,只需从集群配置文件中的节点列表中删除它们,然后重新运行rke up命令。
RKE工具是满足高可用的。您可以在集群配置文件中指定多个控制面板主机,RKE将在其上部署主控组件。
默认情况下,kubelets被配置为连接到nginx-proxy服务的地址——127.0.0.1:6443,该代理会向所有主节点发送请求。
要启动HA集群,只需使用controlplane角色指定多个主机,然后正常启动集群即可。
RKE支持rke remove命令。该命令执行以下操作:
连接到每个主机并删除部署在其上的Kubernetes服务。
从服务所在的目录中清除每个主机:
请注意,这个命令是不可逆的,它将彻底摧毁Kubernetes集群。
ssh: handshake failed: ssh: unable to authenticate, attempted methods [publickey none], no supported methods remain
请检查配置文件中配置的用户是否可以使用指定的私钥登录机器
查看原文1.建议使用Ubuntu 16.04.3 LTS版本;如果使用CentOS 7,建议7.3以上版本2.各主机的hostname主机名必须不同!3.hosts文件设置:/etc/hosts要配置正确,一定要有127.0.0.1 localhost 这一项;hosts文件中包含所有主机节点的IP和名称列表。
赞 3 收藏 0 评论 2
查看全部 个人动态 →
vue结合plupload异步图片上传组件(有七牛JSSDK版本)
golang实现最新(2017-05-25)版阿里大于SDK
注册于 2017-03-27
个人主页被 1.8k 人浏览
推荐关注