0%

在构建裸机Kubernetes集群时,您可能会遇到一个常见问题,就像我在做的那样,除了使用NodePort之外,您真的不知道如何向Internet公开Kubernetes服务。如果使用的是NodePort服务类型,它将分配一个要打开的较大端口号,并且您必须允许防火墙规则连接到这些端口。这对您的基础架构不利,尤其是在将服务器暴露于外部Internet时。好吧,还有另一种简洁的方法可以将您的Kubernetes服务公开出去,并且使用服务的原始端口号。例如,您可以将Kubernetes群集中部署的MySQL服务通过3306而不是32767端口暴露给外界。答案是使用Kubernetes External IP服务类型

就个人而言,我发现在Kubernetes社区中并未广泛讨论此主题,这可能是因为许多人正在使用云提供商的负载均衡器或将Metal LB用于本地部署。

什么是External IP服务

Kubernetes官方文档中可以看到External IP的描述:

如果存在路由到一个或多个集群节点的外部IP,则Kubernetes Services可以在这些External IP上公开。在服务端口上使用外部IP(作为目标IP)进入群集的流量将被路由到服务端点之一。External IP不受Kubernetes的管理,由集群管理员负责。

对于大多数人来说,这种解释是可以理解的。这里最重要的是确保使用哪个IP来访问Kubernetes集群。使用External IP服务类型,我们可以将服务绑定到用于连接集群的IP。

如果您了解Kubernetes网络的工作方式,那将是很好的。如果您不熟悉它,请查看Mark Betz撰写的博客文章,以详细了解它们。这里最重要的是要知道Kubernetes网络与Overlay网络一起工作。这意味着一旦您到达群集中的任何节点(主节点或工作节点),您就可以虚拟访问群集中的所有节点。

下图就是他们的组网图:

kubernetes-external-ip

在上图中,节点1和节点2都有1个IP地址。节点1上的IP地址1.2.3.4绑定到实际Pod驻留在节点2中的httpd服务,而IP地址1.2.3.5绑定到实际Pod驻留在节点1中的nginx服务。底层的overlay网络使这成为可能。当我们curl 1.2.3.4时,应该看到来自httpd服务的响应,而curl 1.2.3.5时,则应该看到来自nginx服务的响应。

为什么不使用Ingress

即使Ingress也用于将服务公开给外部,但Ingress是为L7路由构建的。这意味着它构建为支持HTTP(端口80)和/或HTTPS(端口443)流量,而不支持其他端口。Ingress充当基于主机的路由,或类似于Web Server中的虚拟主机。一些能够为其他端口提供服务的ingress controllers,或者可能为L4路由提供了解决方法,但我从未真正尝试使用它们。

External IP的优缺点

使用External IP的优点是:

  • 您可以完全控制所使用的IP。您可以使用属于您的ASN的IP,而不要使用云提供商的ASN。

外部IP的缺点是:

  • 我们现在将要进行的简单设置并不是高可用的。这意味着,如果节点异常,则该服务将不再可用,您将需要手动修复该问题。
  • 管理IP需要做一些手工工作。IP不是为您动态配置的,因此需要人工干预。

如何使用External IP服务

同样,我们将使用与我们的群集设置相同的组网图,不同的是IP地址和主机名不同。这不是一个好例子,但是当我们验证设置时,很容易区分是哪个。在实际示例中,您可能希望在一个外部IP上公开MySQL DB,在另一个外部IP上公开Kafka群集。

kubernetes-external-ip-demo

我已为本教程配置了2个VM。k3s-external-ip-master将是我们的Kubernetes master节点,其IP为1.2.4.120。k3s-external-ip-worker将是Kubernetes worker节点,其IP为1.2.4.114。

步骤1:部署Kubernetes集群

让我们在主节点上安装k3s,然后让另一个节点加入集群。

1
2
$ k3sup install --ip <master node ip> --user <username>
$ k3sup join --server-ip <master node ip> --ip <worker node ip> --user <username>

您现在应该会看到类似的内容:

1
2
3
4
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
k3s-external-ip-master Ready master 7m24s v1.16.3-k3s.2
k3s-external-ip-worker Ready <none> 2m21s v1.16.3-k3s.2

步骤2:创建Kubernetes Deployment资源

我们将创建nginx和httpd资源。

1
2
$ kubectl create deployment nginx --image=nginx
$ kubectl create deployment httpd --image=httpd

你现在应该看到这个:

1
2
3
4
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx-86c57db685-fzxn5 1/1 Running 0 22s
httpd-7bddd4bd85-zk8ks 1/1 Running 0 16s

步骤3:将Deployment公开为External IP类型

让我们创建Nginx服务的yaml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ cat << EOF > nginx-service.yaml
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
selector:
app: nginx
ports:
- name: http
protocol: TCP
port: 80
targetPort: 80
externalIPs:
- 1.2.4.114
EOF

创建httpd服务的yaml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ cat << EOF > httpd-service.yaml
apiVersion: v1
kind: Service
metadata:
name: httpd-service
spec:
selector:
app: httpd
ports:
- name: http
protocol: TCP
port: 80
targetPort: 80
externalIPs:
- 1.2.4.120
EOF

使用kubectl命令创建2个服务的yaml:

1
2
$ kubectl create -f nginx-service.yaml
$ kubectl create -f httpd-service.yaml

现在您的Kubernetes服务应该如下所示:

1
2
3
4
5
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.43.0.1 <none> 443/TCP 18m
httpd-service ClusterIP 10.43.240.149 1.2.4.120 80/TCP 32s
nginx-service ClusterIP 10.43.13.149 1.2.4.114 80/TCP 26s

您可能会在此处看到服务类型为ClusterIP。我不确定为什么它不显示“外部IP”。

k8s官网有说明,External IP与type无关

步骤4:瞧!

让我们curl httpd服务,您应该看到Apache默认页面。

1
2
3
4
5
6
7
8
9
10
$ curl -i 1.2.4.120
HTTP/1.1 200 OK
Date: Fri, 20 Dec 2019 03:36:23 GMT
Server: Apache/2.4.41 (Unix) <------
Last-Modified: Mon, 11 Jun 2007 18:53:14 GMT
ETag: "2d-432a5e4a73a80"
Accept-Ranges: bytes
Content-Length: 45
Content-Type: text/html
<html><body><h1>It works!</h1></body></html>

接下来,curl nginx服务,您应该看到nginx默认页面。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ curl -i 1.2.4.114
HTTP/1.1 200 OK
Server: nginx/1.17.6 <------
Date: Fri, 20 Dec 2019 03:36:01 GMT
Content-Type: text/html
Content-Length: 612
Last-Modified: Tue, 19 Nov 2019 12:50:08 GMT
Connection: keep-alive
ETag: "5dd3e500-264"
Accept-Ranges: bytes
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
....

下一步是什么

浮动IP

如今,大多数云提供商都提供浮动IP服务。浮动IP允许您拥有1个IP,并将该IP动态分配给所需的任何IP。在这种情况下,可以将IP分配给Kubernetes集群中的任何工作节点。

在DigitalOcean(我相信其他提供商也允许这样做)中,您可以使用API调用将IP重新分配给其他VM。这意味着您可以在其他VM发生故障时迅速将其主动重新分配给其他VM,或者可以定期轮换IP。

kubernetes-floating-ip

从图中可以看到,我们有1个浮动IP 1.2.3.6,该IP首先分配给节点1,当节点1不可用时将切换到节点2。IP 1.2.3.6适用于我们的MySQL服务,并将放入我们的应用程序配置中。

我尚未尝试此设置,因此无法确认它是否有效。我将在以后的博客文章中更新结果。

任播IP

anycast

您可以将Anycast IP用作外部IP,以便它们具有高可用性。对于不熟悉Anycast IP的用户,这意味着1个IP可能会路由到2个或更多服务器。你可以在这里阅读更多。就个人而言,我不确定如何设置此设置。但是,我认为这在技术上是可行的。我认为这是运行外部IP服务的最佳方法。

结论

您可以通过很多选项为裸机Kubernetes集群获取IP。例如,您可以为此使用Inlets和MetalLB。此设置可能不是您的组织需要的最合适的设置。但是,很高兴知道如何使用此方法。

免责声明:我仅将其用于实验和测试,而本文不适用于生产。如果您打算在生产中使用它,请咨询您的解决方案架构师或CTO。

访问Kubernetes集群中托管的服务的两种最常见方法是通过IngressLoad Balancer。对于公有云用户来说,这些是访问服务的简单有效方法。云托管的控制器可以完成分配公共IP,设置负载平衡和管理SSL的繁重工作。

在本地运行私有托管的Kubernetes集群的运营商将很快意识到,将服务公开提供比在公有云上更为复杂。服务将在公共互联网上还是仅对本地用户公开?应该如何设置Ingress,负载平衡,IP分配和SSL管理?在维护安全性的同时将服务公开发布的最有效方法是什么?

Calico项目通过其服务IP发布功能为某些问题提供了答案,该功能与现有的机架(ToR)基础架构集成在一起,可以提供Kubernetes服务IP或Kubernetes外部服务IP的路由。在以下情况下,服务IP发布是一个很好的解决方案:

  • 您的ToR解决方案能够运行边界网关协议(BGP)
  • 您希望将服务从群集共享到网络基础结构的其余部分
  • 您想利用网络负载均衡

为了启用服务IP发布功能,Calico需要与BGP路由器建立对等关系,该路由器在Calico的内部路由器之外,但在网络本地。BGP是网络中使用的最基本的路由协议之一。在较高级别,BGP通过在信任对等方之间共享路由来工作。与ToR对等时,Calico共享Kubernetes服务的路由,这使它们可用于整个网络。

为了帮助讨论此功能,假定我们安装以下方式配置了网络和群集:

  • 托管Kubernetes节点的服务器机架通过顶部机架式路由器连接到物理网络
  • Calico作为CNI和网络策略插件运行
  • Calico和Rack顶部路由器配置为使用BGP对等

在此设置中,我们具有以下网络配置:

  • 机架顶部路由器的IP地址是192.168.1.1,服务器的IP地址是从192.168.1.0/24分配的
  • Kubernetes Pod网络配置了CIDR 10.48.0.0/16
  • Kubernetes服务集群IP范围配置为10.49.0.0/16
  • 外部服务IP范围配置为192.168.3.0/24

您的ToR BGP路由器的确切配置超出了本文的范围,并且会因您使用的供应商或软件包而异。如果您没有可用的服务器机架,但仍然想尝试该功能,那么在单独的服务器上运行的Bird Internet Routing Daemon(BIRD)是尝试进行操作的不错选择。

第一步是启用ToR和Calico网络之间的对等连接。其工作方式将根据您的ToR实现而有所不同,但是要牢记一些关键事项:

  • 必须将ToR路由器配置为与在每个节点上运行的Calico对等
  • ToR需要接受来自外部网络,外部服务网络和Pod服务网络的路由和流量
  • 如果可以选择,ToR应该启用正常重启,以防止网络服务中断

将ToR配置为接受路由后,下一步是在Calico端启用对等。首先通过以下清单告诉Calico有关外部BGP路由器的信息:

1
2
3
4
5
6
7
8
9
calicoctl apply -f - << EOF
apiVersion: projectcalico.org/v3
kind: BGPPeer
metadata:
name: bgppeer-global-64512
spec:
peerIP: 192.168.1.1
asNumber: 64512
EOF

启用对等功能后,Calico可以使Pod成为网络中的头等公民,而无需覆盖网络,并使他们可以直接在群集外部访问。

尽管启用了对等连接,但是Calico仍需要进一步配置以公开Kubernetes服务IP范围。这可以通过创建新的Calico BGPConfiguration资源来完成:

1
2
3
4
5
6
7
8
9
calicoctl create -f - <<EOF
apiVersion: projectcalico.org/v3
kind: BGPConfiguration
metadata:
name: default
spec:
serviceClusterIPs:
- cidr: 10.49.0.0/16
EOF

启用服务群集IP范围的发布后,ToR上的路由表将如下所示:

1
2
3
4
5
6
7
$ ip r
...
10.49.0.0/16
nexthop via 192.168.1.10 dev eth2 weight 1
nexthop via 192.168.1.11 dev eth2 weight 1
nexthop via 192.168.1.12 dev eth2 weight 1
...

注意到10.49.0.0/16网络的路由如何在节点之间实现ECMP负载平衡。您公开的所有服务将由ToR在所有节点上进行负载平衡。为了说明这一点,我们可以创建一个基本的Nginx服务。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
control:~$ kubectl apply -f - <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
selector:
matchLabels:
app: nginx
replicas: 2
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
EOF
control:~$ kubectl expose nginx
control:~$ kubectl get services
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.49.0.1 <none> 443/TCP 19m
nginx ClusterIP 10.49.62.131 <none> 80/TCP 4m43s

现在,您可以从外部网络通过其内部群集IP地址访问Kubernetes托管服务。

1
2
3
4
5
6
external:~$ curl 10.49.62.131
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...

这里有两个阶段的负载平衡。首先,ToR通过将其路由到节点来对与群集IP的连接进行负载平衡。然后,使用NAT(网络地址转换)在该节点上运行的kube-proxy负载均衡到特定的Pod,以将目标IP从群集IP更改为后备Pod之一的IP地址。后备Pod可能在本地节点上,也可能在其他节点之一上,从而导致了另一个网络跃点。

如果我们想避免额外的潜在网络跳数,可以通过将服务上的外部流量策略设置为本地来实现。

1
2
control:~$ kubectl patch service nginx \
-p '{"spec":{"type": "NodePort", "externalTrafficPolicy":"Local"}}'

现在,在ToR上,您将看到路由表的新增内容,包括Nginx服务到正在运行Nginx的特定节点的ECMP负载平衡:

1
2
3
4
5
6
7
8
9
10
$ ip r
...
10.49.0.0/16
nexthop via 192.168.2.10 dev eth2 weight 1
nexthop via 192.168.2.11 dev eth2 weight 1
nexthop via 192.168.2.12 dev eth2 weight 1
...
10.49.62.131
nexthop via 192.168.2.11 dev eth2 weight 1
nexthop via 192.168.2.12 dev eth2 weight 1

这是公开Kubernetes网络服务的简便方法,但是在操作上,它具有将Kubernetes群集中该IP范围内的每个服务都暴露给网络其余部分的缺点。如果您想更精细地控制提供哪些服务,或者想要分配真正面向公众的IP地址,Calico可以通过发布外部服务IP来解决这个问题。该方法类似,主要区别在于外部IP不由Kubernetes集群管理,必须手动分配给服务。

下一个示例将说明这一点。首先重新配置BGPConfiguration,以发布外部IP而不是内部IP(值得注意的是,您可以一次公开这两个集合,但是在此示例中,我们要关闭对内部网络的公共访问,同时仍提供对应用程序的访问)。

1
2
3
4
5
6
7
8
9
10
control :~$calicoctl create -f - <<EOF
apiVersion: projectcalico.org/v3
kind: BGPConfiguration
metadata:
name: default
spec:
serviceClusterIPs:
serviceExternalIPs:
- cidr: 192.168.3.0/24
EOF

检查路由时,请注意已删除到群集IP范围的路由,并已添加到外部服务网络的路由。

1
2
3
4
5
6
7
$ ip r
...
192.168.3.0/24
nexthop via 192.168.1.10 dev eth2 weight 1
nexthop via 192.168.1.11 dev eth2 weight 1
nexthop via 192.168.1.12 dev eth2 weight 1
...

该服务的群集IP不再公开可见。

1
2
external :~$ curl -m 10 10.49.62.131
curl: (28) Connection timed out after 10001 milliseconds

我们现在在发布外部IP范围,但我们还需要为服务分配一个外部IP:

1
2
3
4
5
6
control:~$ kubectl patch svc nginx \
-p '{"spec": {"externalIPs": ["192.168.3.180"]}}'
control:~$ kubectl get services
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.49.0.1 <none> 443/TCP 152m
nginx NodePort 10.49.62.131 192.168.3.180 80:31890/TCP 109m

检查连接性:

1
2
3
4
5
6
external:~$ curl 192.168.3.180
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...

到此为止,我们快速完成了Calico如何在Kuberentes群集之外发布服务和外部服务IP的快速浏览。对于具有启用BGP路由的本地云,这是一种简单的解决方案,无需提供安装和维护自定义Kubernetes负载均衡器或Ingress控制器的额外工作即可访问Kubernetes服务。如果您想了解更多有关此功能的信息,请查阅官方的Calico项目”Advertise Kubernetes Service IPs“。

Linux具有丰富的虚拟网络功能,可用作托管VM和容器以及云环境的基础。本文将简要介绍所有常用的虚拟网络接口类型。内容没有代码分析,仅简要介绍了接口及其在Linux上的用法。可以使用ip link help命令获取接口列表的帮助。

内容涵盖了以下常用的接口以及一些易于相互混淆的接口:

  • Bridge
  • Bonded interface
  • Team device
  • VLAN (Virtual LAN)
  • VXLAN (Virtual eXtensible Local Area Network)
  • MACVLAN
  • IPVLAN
  • MACVTAP/IPVTAP
  • MACsec (Media Access Control Security)
  • VETH (Virtual Ethernet)
  • VCAN (Virtual CAN)
  • VXCAN (Virtual CAN tunnel)
  • IPOIB (IP-over-InfiniBand)
  • NLMON (NetLink MONitor)
  • Dummy interface
  • IFB (Intermediate Functional Block)
  • netdevsim

阅读本文之后,我们将了解这些接口是什么,它们之间的区别,何时使用它们以及如何创建它们。

Bridge

Linux网桥的行为类似于网络交换机。它在与其连接的接口之间转发数据包。它通常用于在路由器,网关或虚拟机与主机上的网络命名空间之间转发数据包。它还支持STP,VLAN过滤和多播侦听。

bridge

如果想要在VM,容器和主机之间建立通信通道,请使用网桥。

以下是创建网桥的方法:

1
2
3
4
5
# ip link add br0 type bridge
# ip link set eth0 master br0
# ip link set tap1 master br0
# ip link set tap2 master br0
# ip link set veth1 master br0

上述命令创建了一个名为br0网桥,并且设置了两个TAP设备(tap1tap2),一个VETH设备(veth1),和物理设备(eth0)作为它的从设备,如上图所示。

Bond

Linux bond驱动提供了一种用于将多个网络接口聚合为单个逻辑“绑定”接口的方法。绑定接口的行为取决于模式。一般来说,提供热备用或负载平衡两种模式。

bond

如果想提高链接速度或在服务器上进行故障转移时,请使用bond接口。

以下是创建bond接口的方法:

1
2
3
ip link add bond1 type bond miimon 100 mode active-backup
ip link set eth0 master bond1
ip link set eth1 master bond1

上述命令创建了一个名称为bond1active-backup模式的bond接口。对于其他模式,请参阅 内核文档

Team device

与bond接口类似,team device的目的是提供一种在L2层将多个NIC(端口)分组为一个逻辑端口(teamdev)的机制。

team

需要注意的是,team device不会尝试复制或模仿bond接口。它的作用是使用不同的方法来解决相同的问题。

但是bond和team device之间也存在一些功能差异。例如,team device支持LACP负载平衡,NS/NA(IPV6)链接监视,D-Bus接口等,而这些功能在bond中是不存在的。有关bond和team device之间差异的更多详细信息,请参见 bond vs team device

综上,如果想使用bond无法提供的某些功能,请使用team device。

创建team device的方法如下:

1
2
3
4
5
# teamd -o -n -U -d -t team0 -c '{"runner": {"name": "activebackup"},"link_watch": {"name": "ethtool"}}'
# ip link set eth0 down
# ip link set eth1 down
# teamdctl team0 port add eth0
# teamdctl team0 port add eth1

上述命令将创建一个team0的team device,且模式为active-backup,并添加eth0eth1作为team0的子接口。

最近,一个名为net_failover的新驱动已添加到Linux内核。它是用于虚拟化的另一种故障转移网络设备,并管理主([passthru/VF虚拟功能] 设备)从网络设备和备用(原始超虚拟接口)从网络设备。

net_failover

VLAN

VLAN(也称为虚拟LAN)通过向网络数据包添加标签来分隔广播域。VLAN使网络管理员可以将同一台交换机下或不同交换机之间的主机分组。

VLAN标头如下所示:

vlan_01

如果想在VM,命名空间或主机中分隔子网,请使用VLAN。

以下是创建VLAN的方法:

1
2
# ip link add link eth0 name eth0.2 type vlan id 2
# ip link add link eth0 name eth0.3 type vlan id 3

上述命令将添加名为eth0.2VLAN2和名为eth0.3VLAN 3 。拓扑如下所示:

vlan

注意:在配置VLAN时,需要确保连接到主机的交换机能够处理VLAN标签,例如,通过将交换机端口设置为中继模式。

VXLAN

VXLAN(虚拟可扩展局域网)是一种隧道协议,旨在解决IEEE 802.1q中有限的VLAN ID(4,096)的问题。由IETF RFC 7348描述。

通过24位网段ID(又称为VXLAN网络标识符(VNI)),VXLAN最多允许2 ^ 24(16,777,216)个虚拟LAN,这是VLAN容量的4,096倍。

VXLAN将带有VXLAN标头的第2层帧封装到UDP-IP数据包中,如下所示:

vxlan_01

VXLAN通常部署在虚拟主机上的数据中心中,该主机可以分布在多个机架上。

vxlan

下面是使用VXLAN的方法:

1
# ip link add vx0 type vxlan id 100 local 1.1.1.1 remote 2.2.2.2 dev eth0 dstport 4789

作为参考,可以阅读VXLAN内核文档VXLAN简介

MACVLAN

使用VLAN,我们可以在一个接口上创建多个接口,并根据VLAN标记过滤数据包。使用MACVLAN,我们可以在一个接口上创建具有不同第2层(即以太网MAC)地址的多个接口。

在使用MACVLAN之前,如果要从VM或命名空间连接到物理网络,则需要创建TAP/VETH设备,并将一侧连接到网桥,并同时将物理接口连接到主机上的网桥,如下所示。

br_ns

现在,借助MACVLAN,我们可以将与MACVLAN关联的物理接口直接绑定到命名空间,而无需桥接。

macvlan

MACVLAN有五种类型:

1.Private:即使外部交换机支持hairpin模式,也不允许同一物理接口上的MACVLAN实例之间进行通信。

macvlan_01

  1. VEPA:同一物理接口上从一个MACVLAN实例到另一个MACVLAN实例的数据通过该物理接口传输。连接的交换机需要支持hairpin模式,或者必须有TCP/IP路由器转发数据包才能进行通信。

macvlan_02

  1. Bridge:所有端点都通过物理接口通过简单的桥接器直接相互连接。

macvlan_03

  1. Passthru:允许将单个VM直接连接到物理接口。

macvlan_04

  1. Source:该模式用于基于允许的源MAC地址列表过滤流量,以创建基于MAC的VLAN关联。请参阅提交消息

模式是根据不同的需求选择的。桥接模式是最常用的。如果要从容器直接连接到物理网络时,请使用MACVLAN。

设置MACVLAN的方法如下:

1
2
3
4
5
6
# ip link add macvlan1 link eth0 type macvlan mode bridge
# ip link add macvlan2 link eth0 type macvlan mode bridge
# ip netns add net1
# ip netns add net2
# ip link set macvlan1 netns net1
# ip link set macvlan2 netns net2

上述命令将以桥接模式创建两个新的MACVLAN设备,并将这两个设备分配给两个不同的命名空间。

IPVLAN

IPVLAN与MACVLAN相似,区别在于端点具有相同的MAC地址。

ipvlan

IPVLAN支持L2和L3模式。IPVLAN L2模式在桥接模式下的行为类似于MACVLAN。父接口看起来像一个网桥或交换机。

ipvlan_01

在IPVLAN L3模式下,父接口的行为就像路由器,并且数据包在端点之间路由,从而提供了更好的可伸缩性。

ipvlan_02

关于何时使用IPVLAN, IPVLAN内核文档 说MACVLAN和IPVLAN在许多方面都非常相似,并且可以依据特定的场景很好地定义选择哪个。如果以下情况之一定义我们需要的场景,则可以选择使用ipvlan:
(a)连接到外部交换机/路由器的Linux主机已配置了策略,每个端口仅允许一个mac。
(b)在主服务器上创建的虚拟设备均未超过mac容量,并且无法将NIC置于混杂模式,因此性能下降是一个问题。
(c)如果要将从设备放入敌对/不受信任的网络命名空间中,则从设备上的L2可能会被更改/滥用。

设置IPVLAN实例的方法如下:

1
2
3
# ip netns add ns0
# ip link add name ipvl0 link eth0 type ipvlan mode l2
# ip link set dev ipvl0 netns ns0

上述命令将创建一个名为ipvl0L2的IPVLAN设备,并分配给ns0命名空间。

MACVTAP/IPVTAP

MACVTAP/IPVTAP是一种新的设备驱动,旨在简化虚拟化桥接网络。当在物理接口顶部创建MACVTAP/IPVTAP实例时,内核还将创建一个字符设备/dev/tapX,以与TUN/TAP设备一样使用,并可以由KVM / QEMU直接使用。

使用MACVTAP/IPVTAP,我们可以用单个模块替换TUN/TAP和网桥驱动的组合:

macvtap

通常,MACVLAN/IPVLAN用于使访客和主机都直接显示在主机所连接的交换机上。MACVTAP和IPVTAP之间的差异与MACVLAN/IPVLAN相同。

以下是创建MACVTAP实例的方法:

1
# ip link add link eth0 name macvtap0 type macvtap

MACsec

MACsec(媒体访问控制安全)是用于有线以太网LAN中安全性的IEEE标准。与IPsec相似,作为第2层规范,MACsec不仅可以保护IP流量,还可以保护ARP,NS(邻居发现)和DHCP。MACsec标头如下所示:

macsec_01

MACsec的主要用例是保护标准LAN上的所有消息(包括ARP,NS和DHCP消息)的安全。

macsec

MACsec配置的方法如下:

1
# ip link add macsec0 link eth1 type macsec

注意:上述命令仅在eth1设备上添加了名为macsec0的MACsec。有关更详细的配置,请参阅Sabrina DubrocaMACsec简介“配置示例”部分 。

VETH

VETH(虚拟以太网)设备是本地以太网隧道。设备是成对创建的,如下图所示。在VETH对中的一个设备上传输的数据包将立即在另一设备上接收。当任何一台设备关闭时,该VETH对的链接状态为关闭。

veth

当任意命名空间需要与主机命名空间或彼此之间进行通信时,请使用VETH配置。

以下是设置VETH配置的方法:

1
2
3
# ip netns add net1
# ip netns add net2
# ip link add veth1 netns net1 type veth peer name veth2 netns net2

上述命令将创建两个名称空间net1net2,以及一对VETH设备,并将veth1分配给net1veth2分配给net2。这两个命名空间与此VETH对相连。分配一对IP地址,这样就可以在两个命名空间之间通信。

VCAN

与网络环回设备类似,VCAN(虚拟CAN)驱动提供了虚拟本地CAN(控制器局域网)接口,因此用户可以通过VCAN接口发送/接收CAN消息。如今,CAN主要用于汽车领域。

有关更多CAN协议信息,请参考 内核CAN文档

如果需要在本地主机上测试CAN协议实现时,请使用VCAN。

创建VCAN的方法如下:

1
# ip link add dev vcan1 type vcan

VXCAN

与VETH驱动类似,VXCAN(虚拟CAN隧道)在两个VCAN网络设备之间实现本地CAN流量隧道。创建VXCAN实例时,两个VXCAN设备将成对创建。当一端接收到数据包时,该数据包出现在设备对上,反之亦然。VXCAN可用于跨命名空间通信。

如果想跨命名空间发送CAN消息时,请使用VXCAN配置。

设置VXCAN实例的方法如下:

1
2
3
# ip netns add net1
# ip netns add net2
# ip link add vxcan1 netns net1 type vxcan peer name vxcan2 netns net2

注意:Red Hat Enterprise Linux尚不支持VXCAN。

IPOIB

IPOIB设备支持IP-over-InfiniBand协议。这将通过InfiniBand(IB)传输IP数据包,因此我们可以将IB设备用作快速NIC。

IPoIB驱动支持两种操作模式:datagram和connected。在datagram模式下,使用IB UD(不可靠数据包)传输。在connected模式下,使用IB RC(可靠连接)传输。connected模式利用了IB传输的连接特性,并允许MTU最多达到64K的最大IP数据包大小。

有关更多详细信息,请参见 IPOIB内核文档

如果想拥有IB设备并想通过IP与远程主机通信时,请使用IPOIB设备。

以下是创建IPOIB设备的方法:

1
# ip link add ib0 name ipoib0 type ipoib pkey IB_PKEY mode connected

NLMON

NLMON是Netlink监视设备。

如果想要监视系统Netlink消息时,请使用NLMON设备。

以下是创建NLMON设备的方法:

1
2
3
# ip link add nlmon0 type nlmon
# ip link set nlmon0 up
# tcpdump -i nlmon0 -w nlmsg.pcap

上述命令将创建一个名称为NLMON的nlmon0设备并进行设置。使用数据包嗅探器(例如, tcpdump)捕获Netlink消息。Wireshark的最新版本具有对Netlink消息进行解码的功能。

Dummy interface

虚拟接口完全是虚拟的,例如loopback接口。虚拟接口的目的是提供一种设备,可以在不实际传输数据包的情况下路由数据包。

使用虚拟接口使无效的SLIP(串行Internet协议)地址看起来像本地程序的真实地址。如今,虚拟接口主要用于测试和调试。

以下是创建虚拟接口的方法:

1
2
3
# ip link add dummy1 type dummy
# ip addr add 1.1.1.1/24 dev dummy1
# ip link set dummy1 up

IFB

IFB(中间功能块)驱动提供了一种设备,该设备允许集中来自多个源的流量并调整传入流量,而不是将其丢弃。

如果想要排队和调整传入流量时,请使用IFB接口。

以下是创建IFB接口的方法:

1
2
3
4
5
# ip link add ifb0 type ifb
# ip link set ifb0 up
# tc qdisc add dev ifb0 root sfq
# tc qdisc add dev eth0 handle ffff: ingress
# tc filter add dev eth0 parent ffff: u32 match u32 0 0 action mirred egress redirect dev ifb0

上述命令将创建一个名为ifb0的IFB设备,并将根据qdisc调度程序替换为SFQ(随机公平队列),SFQ是无类排队调度程序。然后,在eth0上添加一个qdisc调度程序,并将所有入口流量重定向到ifb0

有关更多IFB qdisc用例,请参考 IFB上的Linux Foundation Wiki

netdevsim接口

netdevsim是一种模拟的联网设备,用于测试各种联网API。目前,它特别专注于测试硬件卸载,tc/XDP BPF和SR-IOV。

如下命令可以创建一个netdevsim设备

1
2
# ip link add dev sim0 type netdevsim
# ip link set dev sim0 up

要启用tc卸载:

1
# ethtool -K sim0 hw-tc-offload on

加载XDP BPF或tc BPF程序:

1
# ip link set dev sim0 xdpoffload obj prog.o

要添加用于SR-IOV测试的VF,请执行以下操作:

1
2
# echo 3 > /sys/class/net/sim0/device/sriov_numvfs
# ip link set sim0 vf 0 mac

要更改vf编号,需要先完全禁用它们:

1
2
# echo 0 > /sys/class/net/sim0/device/sriov_numvfs
# echo 5 > /sys/class/net/sim0/device/sriov_numvfs

注意:默认情况下,RHEL中没有编译netdevsim。

Linux支持多种隧道,但是新用户可能会因为它们之间的差异而感到困惑,无法确定哪种隧道适合给定的场景。本文将简要介绍Linux内核中常用的隧道接口。内容没有代码分析,仅简要介绍了接口及其在Linux上的用法。感兴趣的话,可以通过iproute2的命令ip link help获得隧道接口列表以及特定隧道配置的帮助。

这篇文章介绍了以下常用接口:

  • IPIP
  • SIT
  • ip6tnl
  • VTI和VTI6
  • GRE和GRETAP
  • IP6GRE和IP6GRETAP
  • FOU
  • GUE
  • GENEVE
  • ERSPAN和IP6ERSPAN

阅读本文之后,我们将了解这些接口是什么,它们之间的区别,何时使用它们以及如何创建它们。

IPIP

顾名思义,IPIP隧道是RFC 2003中定义的IP over IP隧道。IPIP隧道标头如下所示:

ipip

通常用于通过公共IPv4互联网连接两个内部IPv4子网。它具有最低的开销,但只能传输IPv4单播流量。这意味着您无法通过IPIP隧道发送多播。

IPIP隧道同时支持IP over IP和MPLS over IP。

注意:加载ipip模块或首次创建IPIP设备时,Linux内核将在每个名称空间中创建tunl0默认设备,其属性为local = any和remote = any。接收IPIP协议数据包时,如果内核找不到其他本地/远程属性与其来源或目标地址更匹配的设备,则内核会将其转发给tunl0作为备用设备。

如下是创建IPIP隧道的方法:

1
2
3
4
5
6
7
8
9
10
11
12
On Server A:
# ip link add name ipip0 type ipip local LOCAL_IPv4_ADDR remote REMOTE_IPv4_ADDR
# ip link set ipip0 up
# ip addr add INTERNAL_IPV4_ADDR/24 dev ipip0
Add a remote internal subnet route if the endpoints don't belong to the same subnet
# ip route add REMOTE_INTERNAL_SUBNET/24 dev ipip0

On Server B:
# ip link add name ipip0 type ipip local LOCAL_IPv4_ADDR remote REMOTE_IPv4_ADDR
# ip link set ipip0 up
# ip addr add INTERNAL_IPV4_ADDR/24 dev ipip0
# ip route add REMOTE_INTERNAL_SUBNET/24 dev ipip0

注意:请根据测试环境将LOCAL_IPv4_ADDR,REMOTE_IPv4_ADDR,INTERNAL_IPV4_ADDR,REMOTE_INTERNAL_SUBNET替换为地址。

SIT

SIT代表“简单Internet过渡”。主要目的是互连位于全球IPv4互联网中的隔离IPv6网络。

最初,它只有IPv6 over IPv4隧道模式。然而,经过多年的发展,它获得了对多种不同模式的支持,例如ipip(与IPIP隧道相同),ip6ip,mplsip等。模式any用于同时接受IP和IPv6流量,这在某些部署中可能很有用。 SIT隧道还支持ISATA,用法见示例

SIT隧道报文头看起来如下所示:

sit.png

加载sit模块后,Linux内核将创建一个默认设备,名为sit0。

如下是创建SIT隧道的方法:

1
2
3
4
On Server A:
# ip link add name sit1 type sit local LOCAL_IPv4_ADDR remote REMOTE_IPv4_ADDR mode any
# ip link set sit1 up
# ip addr add INTERNAL_IPV4_ADDR/24 dev sit1

然后,在远端执行相同的步骤。

ip6tnl

ip6tnl是基于IPv6的IPv4/IPv6隧道接口,看起来像SIT隧道的IPv6版本。报文头看起来如下所示:

ip6tnl.png

ip6tnl支持ip6ip6,ipip6,和any模式。ipip6模式是IPv4 over IPv6,ip6ip6模式是IPv6 over IPv6,any模式支持IPv4/IPv6 over Pv6。

加载ip6tnl模块后,Linux内核将创建一个名为ip6tnl0的默认设备。

如下是创建ip6tnl隧道的方法:

1
# ip link add name ipip6 type ip6tnl local LOCAL_IPv6_ADDR remote REMOTE_IPv6_ADDR mode any

VTI和VTI6

Linux上的虚拟隧道接口(VTI)与Cisco的VTI和Juniper的安全隧道(st.xx)类似。

这个特定的隧道驱动程序实现IP封装,可以与xfrm一起使用以提供安全隧道的概念,然后在其之上使用内核路由。

通常,VTI隧道的运行方式几乎与ipip或sit隧道相同,不同之处在于,它们添加了fwmark和IPsec封装/解封装。

VTI6是VTI的IPv6实现。

如下是创建VTI隧道的方法:

1
2
3
4
5
6
7
8
# ip link add name vti1 type vti key VTI_KEY local LOCAL_IPv4_ADDR remote REMOTE_IPv4_ADDR
# ip link set vti1 up
# ip addr add LOCAL_VIRTUAL_ADDR/24 dev vti1

# ip xfrm state add src LOCAL_IPv4_ADDR dst REMOTE_IPv4_ADDR spi SPI PROTO ALGR mode tunnel
# ip xfrm state add src REMOTE_IPv4_ADDR dst LOCAL_IPv4_ADDR spi SPI PROTO ALGR mode tunnel
# ip xfrm policy add dir in tmpl src REMOTE_IPv4_ADDR dst LOCAL_IPv4_ADDR PROTO mode tunnel mark VTI_KEY
# ip xfrm policy add dir out tmpl src LOCAL_IPv4_ADDR dst REMOTE_IPv4_ADDR PROTO mode tunnel mark VTI_KEY

也可以通过libreswan或strongSwan配置IPsec。

GRE和GRETAP

通用路由封装,也称为GRE,在RFC 2784中定义。

GRE隧道在内部和外部IP头之间添加了一个额外的GRE头。从理论上讲,GRE可以封装具有有效以太网类型的任何第3层协议,而IPIP只能封装IP。GRE报文头看起来如下所示:

gre

请注意,GRE隧道支持传输多播流量和IPv6报文。

gre模块被加载,Linux内核将创建一个默认设备,命名gre0

如下是创建GRE隧道的方法:

1
# ip link add name gre1 type gre local LOCAL_IPv4_ADDR remote REMOTE_IPv4_ADDR [seq] key KEY

GRE隧道在OSI第3层上运行,而GRETAP在OSI第2层上运行,这意味着内部头中有一个以太网头部。

gretap

如下是创建GRETAP隧道的方法:

1
# ip link add name gretap1 type gretap local LOCAL_IPv4_ADDR remote REMOTE_IPv4_ADDR

IP6GRE和IP6GRETAP

IP6GRE是GRE的IPv6实现,它使我们能够封装基于IPv6的任何第3层协议。隧道头看起来如下所示:

gre6

与GRETAP一样,IP6GRETAP在内部标头中具有以太网头部:

gre6tap

如下是创建GRE隧道的方法:

1
2
# ip link add name gre1 type ip6gre local LOCAL_IPv6_ADDR remote REMOTE_IPv6_ADDR
# ip link add name gretap1 type ip6gretap local LOCAL_IPv6_ADDR remote REMOTE_IPv6_ADDR

FOU

隧道可以在网络堆栈的多个级别上发生。IPIP,SIT,GRE隧道位于IP级别,而FOU(UDP上的foo)是UDP级别的隧道。

使用UDP隧道具有一些优点,因为UDP与现有的硬件基础结构一起工作,例如NIC中的RSS,交换机中的ECMP。开发人员的验证结果显示SIT和IPIP协议的性能显著提高。

当前,FOU隧道支持基于IPIP,SIT,GRE的封装协议。FOU报文头示例如下所示:

fou

如下是创建FOU隧道的方法:

1
2
# ip fou add port 5555 ipproto 4
# ip link add name tun1 type ipip remote 192.168.1.1 local 192.168.1.2 ttl 225 encap fou encap-sport auto encap-dport 5555

第一条命令为绑定到5555的IPIP配置了FOU接收端口;对于GRE,需要设置ipproto 47

第二条命令使用目标端口5555设置了用于FOU封装的新IPIP虚拟接口(tun1)。

注意:Red Hat Enterprise Linux不支持FOU。

GUE

通用UDP封装(GUE)是另一种UDP隧道。FOU和GUE之间的区别在于GUE具有自己的封装头,其中包含协议信息和其他数据。

当前,GUE隧道支持内部IPIP,SIT,GRE封装。GUE标头示例如下所示:

gue

如下是创建GUE隧道的方法:

1
2
# ip fou add port 5555 gue
# ip link add name tun1 type ipip remote 192.168.1.1 local 192.168.1.2 ttl 225 encap gue encap-sport auto encap-dport 5555

这将为绑定到5555的IPIP设置一个GUE接收端口,并为GUE封装配置一个IPIP隧道。

注意:Red Hat Enterprise Linux不支持GUE。

GENEVE

通用网络虚拟化封装(GENEVE)支持VXLAN,NVGRE和STT的所有功能,旨在克服它们的局限性。许多人认为GENEVE最终可以完全取代这些早期格式。隧道头看起来如下所示:

geneve

看起来非常类似于VXLAN。主要区别在于GENEVE报文头是灵活的。通过使用新的Type-Length-Value(TLV)字段扩展标头来添加新功能非常容易。有关更多详细信息,可以查看最新的ietf草案或参考什么是GENEVE?

开放式虚拟网络(OVN)使用GENEVE作为默认封装。

如下是创建GENEVE隧道的方法:

1
# ip link add name geneve0 type geneve id VNI remote REMOTE_IPv4_ADDR

ERSPAN和IP6ERSPAN

封装的远程交换端口分析器(ERSPAN)使用GRE封装将基本的端口镜像功能从第2层扩展到第3层,从而允许通过可路由的IP网络发送镜像的流量。ERSPAN标头看起来如下所示:

erspan

ERSPAN隧道允许Linux主机充当ERSPAN流量源,并将ERSPAN镜像流量发送到远程主机或ERSPAN目标,后者接收并解析从Cisco或其他具有ERSPAN功能的交换机生成的ERSPAN数据包。此设置可用于分析,诊断和检测恶意流量。

Linux当前支持两个ERSPAN版本的大多数功能:v1(类型II)和v2(类型III)。

如下是创建ERSPAN隧道的方法:

1
2
3
4
5
6
7
# ip link add dev erspan1 type erspan local LOCAL_IPv4_ADDR remote REMOTE_IPv4_ADDR seq key KEY erspan_ver 1 erspan IDX
or
# ip link add dev erspan1 type erspan local LOCAL_IPv4_ADDR remote REMOTE_IPv4_ADDR seq key KEY erspan_ver 2 erspan_dir DIRECTION erspan_hwid HWID

Add tc filter to monitor traffic
# tc qdisc add dev MONITOR_DEV handle ffff: ingress
# tc filter add dev MONITOR_DEV parent ffff: matchall skip_hw action mirred egress mirror dev erspan1

概括

下面是我们介绍的所有隧道的总结。

隧道/链接类型 外部报文头 封装报文头 内部报文头
ipip IPv4 None IPv4
sit IPv4 None IPv4 / IPv6
ip6tnl IPv6 None IPv4 / IPv6
vti IPv4 IPsec IPv4
vti6 IPv6 IPsec IPv6
gre IPv4 GRE IPv4 / IPv6
gretap IPv4 GRE 以太网+ IPv4 / IPv6
ip6gre IPv6 GRE IPv4 / IPv6
ip6gretap IPv6 GRE 以太网+ IPv4 / IPv6
fou IPv4 / IPv6 UDP IPv4 / IPv6 / GRE
gue IPv4 / IPv6 UDP + GUE IPv4 / IPv6 / GRE
geneve IPv4 / IPv6 UDP +geneve 以太网+ IPv4 / IPv6
erspan IPv4 GRE + ERSPAN IPv4 / IPv6
ip6erspan IPv6 GRE + ERSPAN IPv4 / IPv6

注意:本教程中的所有配置都是临时生效的,并且在服务器重新启动后会丢失。如果要使配置在重新引导后保持不变,请考虑使用网络配置守护程序,例如NetworkManager或特定于发行版的机制。

okteto介绍

Okteto是一个基于Kubernetes的程序开发平台,通过直接在Kubernetes中开发和测试代码来构建应用程序。截止目前,该平台提供免费部署docker容器的服务,免费容量为8G内存,4核CPU和50G磁盘空间。

image

申请步骤

  1. 进入okteto官网,点击Login。

    image

  2. 通过github账户登录。

    image

  3. 登录成功后,可以看到控制台界面。

    image

  4. 点击Deploy可以选择部署预定义的应用,或者选择部署容器。

    image

    image

部署hello-world应用

  1. 打开hello-world应用配置页面,根据默认参数部署即可。

image

  1. 等待一段时间,查看状态为running。

    image

    1. 通过应用提供的rest接口访问,验证部署成功。

    image

自开始构建Okteto以来,我们一直在与许多开发人员进行交流。每个团队都有非常独特的挑战,但是经常出现的一个挑战是将一行代码从开发人员的IDE移植到Kubernetes需要多长时间。团队要么必须手动进行所有操作(构建,标记,推送,重新部署),要么将其更改推送到CI/CD服务,并等待直到更改被处理为止。

无论哪种方式,部署变更的时间都会打断您的流程,您的生产力因此而下降。这让我们思考:为什么开发人员需要了解整个构建部署基础结构才能验证特定于应用程序的更改?

我们很高兴宣布推出Okteto Push工具,这是将代码更改推送到Kubernetes的最快方法。一个直接在Kubernetes中自动构建,推送和更新应用程序的命令。

推送代码

okteto push的目标是消除开发人员在开发应用程序时需要执行的所有操作。编码,推送并实时查看更改。这样,我们所有人都可以专注于我们的应用程序,而不必担心底层的传递途径。

为了使用okteto push,我们需要的是包含代码,Dockerfile和最新版本Okteto CLI的本地文件夹。

首先,运行okteto login在Okteto Cloud中创建一个免费帐户。这将允许您使用Okteto构建服务直接在云中构建镜像。

1
$ okteto login

运行okteto push命令将代码推送到Kubernetes,将$IMAGE替换为自己的镜像仓库(例如pchico83/hello-world):

1
$ okteto push --name hello-world -t $IMAGE
1
2
3
4
5
6
7
 i  Running your build in Okteto Cloud...
[+] Building 2.8s (16/16) FINISHED
=> importing cache manifest from pchico83/hello-world:okteto 1.1s
...
...
...
✓ Source code pushed to the development environment 'hello-world'

okteto push将使用Okteto Build Service自动构建开发镜像,生成一个开发标签,将其推送到容器仓库中并将其部署到Kubernetes。如果已经在运行一个应用程序,它将自动找到该deployment 并将镜像替换为新的。如果没有,它甚至可以创建deployment和service。

一个简单的命令即可在几秒钟内将代码导入任何Kubernetes环境。无需在本地运行docker,选择容器标签或修改yaml文件即可更新应用。

学习更多

Okteto Push可与任何编程语言,框架或平台一起使用。在我们的ASP.NETGoJavaNode.jsPythonPHPRuby入门指南中了解更多信息。

下一步

在Okteto,我们致力于使每个开发人员都可以访问Kubernetes,并将内部循环开发周期减少到最小。Okteto Push仍然要求开发人员拥有Dockerfile,但是我们正在尝试使用Cloud Buildpacks和类似技术来完全消除这一要求。

结论

Okteto社区中的一些人在过去几周内一直在内部使用okteto push,到目前为止,反馈非常好。我们已经看到人们将其用作内部流程的一部分,作为提交前的验证机制,以创建预览环境,甚至用于产品部署。真的很令人兴奋!

如果利用容器化的构建环境,则构建Kubernetes很容易。本文档将帮助了解构建过程。

依赖

  1. Docker,使用以下配置之一:
    • macOS:可以使用Mac的Docker或docker-machine。请参阅此处的安装说明。注意:需要将Docker VM设置为具有至少4.5GB的初始内存,否则构建可能会失败。(请参阅:#11852)。
    • 带有本地Docker的Linux:根据OS的说明安装Docker。
    • 远程Docker引擎:在云中使用大型计算机来加快构建速度。这有点棘手,所以请稍后再看。
  2. 可选的Google Cloud SDK

如果要将发布版本上传到Google Cloud Storage,则必须安装和配置Google Cloud SDK,否则可以放心地省略此设置。

概述

虽然可以使用本地golang来构建Kubernetes,但我们有一个在Docker容器中运行的构建过程。这简化了初始设置,并提供了非常一致的构建和测试环境。

关键脚本

build/目录中找到以下脚本。请注意,所有脚本必须从Kubernetes根目录运行。

  • build/run.sh: 在构建docker容器中运行命令。常用调用:
    • build/run.sh make: 在容器中仅构建linux二进制文件。根据需要传递选项和软件包。
    • build/run.sh make cross: 构建所有平台的所有二进制文件。
    • build/run.sh make kubectl KUBE_BUILD_PLATFORMS=darwin/amd64: 为特定平台构建特定二进制文件。
    • build/run.sh make test: 运行所有单元测试。
    • build/run.sh make test-integration: 运行集成测试。
    • build/run.sh make test-cmd: 运行CLI测试。
  • build/copy-output.sh: 这会将 _output/dockerized/bin 的内容从Docker容器拷贝到本地 _output/dockerized/bin目录. 它还将复制在构建过程中生成的特定文件模式。这是作为 build/run.sh的一部分自动运行的。
  • build/make-clean.sh: 清除 _output的内容,删除所有本地构建的容器镜像,然后删除数据容器。
  • build/shell.sh: 将带有当前存储库代码快照的构建容器内加入bash shell。

基本流程

位于build/目录下的脚本直接用于构建和测试。它们将确保构建kube-build Docker镜像(基于build/build-image/Dockerfile),然后在该容器中执行适当的命令。这些脚本将确保每次运行都缓存正确的数据以进行增量构建,并将结果复制回容器。

首先,通过在_output/images/build-image中创建一个“context”目录来构建kube-build容器镜像。它是在此处完成的,而不是在Kubernetes存储库的根目录进行,以最大程度地减少构建镜像时需要打包的数据量。

从此,该镜像运行3个不同的容器实例。每次运行后,数据容器在运行期间都将保留,而rsync和构建容器将被删除。

  1. 数据容器:用于存储需要持久存储以支持增量构建的所有数据。
  2. rsync容器:用于将数据传入和传出到数据容器。
  3. 构建容器:用于实际执行构建操作。

rsync容器在后台透明地使用,以有效地将数据移入和移出容器。这将使用Docker选择的临时端口。可以通过设置KUBE_RSYNC_PORT env变量来修改它。

所有Docker名称都以从文件路径派生的哈希作为后缀(以允许在CI机器上同时使用)和版本号。当版本号更改时,所有状态都将清除,并开始全新构建。这允许更改构建基础结构,并向CI系统发出需要删除旧设施的信号。

代理设置

如果使用了代理,并且让这些脚本使用docker-machine在macOS上设置本地VM,则需要设置Kubernetes构建的代理,应定义以下环境变量。

1
2
export KUBERNETES_HTTP_PROXY=http://username:password@proxyaddr:proxyport
export KUBERNETES_HTTPS_PROXY=https://username:password@proxyaddr:proxyport

可以选择为Kubernetes构建指定没有代理的地址,例如

1
export KUBERNETES_NO_PROXY=127.0.0.1

如果使用sudo进行Kubernetes的构建(例如make quick-release),则需要运行sudo -E make quick-release来传递环境变量。

远程Docker引擎

可以使用远程运行的Docker引擎。必须将Docker配置为连接到该计算机,并且本地rsync端口必须从localhost转发(通过SSH或nc)到远程计算机。

要使用GCP和docker-machine轻松做到这一点,请执行以下操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# Create the remote docker machine on GCE.  This is a pretty beefy machine with SSD disk.
KUBE_BUILD_VM=k8s-build
KUBE_BUILD_GCE_PROJECT=<project>
docker-machine create \
--driver=google \
--google-project=${KUBE_BUILD_GCE_PROJECT} \
--google-zone=us-west1-a \
--google-machine-type=n1-standard-8 \
--google-disk-size=50 \
--google-disk-type=pd-ssd \
${KUBE_BUILD_VM}

# Set up local docker to talk to that machine
eval $(docker-machine env ${KUBE_BUILD_VM})

# Pin down the port that rsync will be exposed on the remote machine
export KUBE_RSYNC_PORT=8730

# forward local 8730 to that machine so that rsync works
docker-machine ssh ${KUBE_BUILD_VM} -L ${KUBE_RSYNC_PORT}:localhost:${KUBE_RSYNC_PORT} -N &

通过docker-machine stop, docker-machine startdocker-machine rm 管理创建的VM。

发布版本

build/release.sh脚本将构建发行版。它将构建二进制文件,运行测试,(可选)构建运行时Docker镜像。

主要输出是tar文件:kubernetes.tar.gz。这包括:

  • 交叉编译的客户端实用程序
  • 脚本(kubectl),用于基于平台选择和运行正确的客户端二进制文件
  • 例子
  • 各种云的集群部署脚本
  • 包含所有服务器二进制文件的Tar文件

此外,还创建了其他一些tar文件:

  • kubernetes-client-*.tar.gz 特定平台的客户端二进制文件
  • kubernetes-server-*.tar.gz 特定平台的服务器二进制文件

构建最终发行版tar时,首先将它们暂存为_output/release-stage,然后再将其放入_output/release-tars

重现性

make release,其变体make quick-release和Bazel都提供了一个封闭的构建环境,应该为构建提供一定程度的可重复性。make自身不是密封的。

Kubernetes构建环境支持Reproducible Builds项目指定的SOURCE_DATE_EPOCH环境变量,可以将其设置为UNIX纪元时间戳。这将用于嵌入已编译的Go二进制文件中的构建时间戳,也许还有一天还会用于Docker镜像。

这个变量的一个合理设置是使用从正在构建的树的顶端开始的提交时间戳。这就是Kubernetes CI系统使用的。例如,可以使用以下命令获取:

1
SOURCE_DATE_EPOCH=$(git show -s --format=format:%ct HEAD)

问题描述

Kubernetes为每个容器分配一个IP地址,但是如何从Pod中的容器中获取IP地址?我无法从文档中找到方法。

高票回答

最简单的答案是确保您的pod或rc控制器yaml/json文件通过添加下面定义的配置块将pod IP添加为环境变量。

1
2
3
4
5
6
7
8
9
10
11
12
13
env:
- name: MY_POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: MY_POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: MY_POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP

重新创建pod/rc,然后尝试

1
echo $MY_POD_IP

还可以运行env来查看kubernetes为您提供的其他内容。

原文链接

How to know a Pod’s own IP address from inside a container in the Pod?

问题描述

我的deployment有单个pod,我的自定义docker镜像如下:

1
2
3
containers:
- name: mycontainer
image: myimage:latest

在开发过程中,我想推送新的最新版本并更新Deployment。无法明确定义tag/version并为每个构建增加它,并且无法找到如何做到这一点:

1
kubectl set image deployment/my-deployment mycontainer=myimage:1.9.1

高票回答

您可以使用grace period(例如30秒或更长时间,根据容器启动时间和镜像大小)配置您的pod,并设置“imagePullPolicy:”Always“。并使用kubectl delete pod pod_name。将创建一个新容器并且最新镜像自动下载,然后旧容器终止。

例子:

1
2
3
4
5
6
spec:
terminationGracePeriodSeconds: 30
containers:
- name: my_container
image: my_image:latest
imagePullPolicy: "Always"

我目前正在使用Jenkins进行自动构建和打镜像标签,它看起来像这样:

1
kubectl --user="kube-user" --server="https://kubemaster.example.com"  --token=$ACCESS_TOKEN set image deployment/my-deployment mycontainer=myimage:"$BUILD_NUMBER-$SHORT_GIT_COMMIT"

另一个技巧是最初运行:

1
kubectl set image deployment/my-deployment mycontainer=myimage:latest

然后:

1
kubectl set image deployment/my-deployment mycontainer=myimage

它实际上会触发滚动更新,但请确保您还有imagePullPolicy:“Always”设置。

我发现的另一个技巧是,您不必更改镜像名称,更改将触发滚动更新的字段的值,例如terminationGracePeriodSeconds。您可以使用kubectl编辑部署your_deployment或kubectl apply -f your_deployment.yaml或使用如下补丁:

1
2
kubectl patch deployment your_deployment -p \
'{"spec":{"template":{"spec":{"terminationGracePeriodSeconds":31}}}}'

只需确保始终更改数值。

原文链接

Kubernetes how to make Deployment to update image