配置 Egress Gateway

目录

关于 Egress Gateway

Egress Gateway 用于控制 Pod 的外部网络访问,使用一组静态地址,具有以下特性:

  • 通过 ECMP 实现 Active-Active 高可用,支持水平吞吐量扩展
  • 通过 BFD 实现快速故障切换(<1秒)
  • 支持 IPv6 和双栈
  • 通过 NamespaceSelector 和 PodSelector 实现细粒度路由控制
  • 通过 NodeSelector 实现 Egress Gateway 的灵活调度

同时,Egress Gateway 具有以下限制:

  • 使用 macvlan 作为底层网络连接,要求底层网络支持 Underlay
  • 多实例 Gateway 模式下,需要多个 Egress IP
  • 目前仅支持 SNAT,不支持 EIP 和 DNAT
  • 目前不支持记录源地址转换关系

实现细节

每个 Egress Gateway 由多个具有多个网络接口的 Pod 组成。 每个 Pod 有两个网络接口:一个加入虚拟网络,用于 VPC 内通信, 另一个通过 Macvlan 连接到底层物理网络,用于外部网络通信。 虚拟网络流量最终通过 Egress Gateway 实例内的 NAT 访问外部网络。

每个 Egress Gateway 实例在 OVN 路由表中注册其地址。 当 VPC 内的 Pod 需要访问外部网络时, OVN 使用源地址哈希将流量转发到多个 Egress Gateway 实例地址, 实现负载均衡。随着 Egress Gateway 实例数量增加, 吞吐量也可以水平扩展。

OVN 使用 BFD 协议探测多个 Egress Gateway 实例。 当某个 Egress Gateway 实例故障时,OVN 将对应路由标记为不可用, 实现快速故障检测和恢复。

注意事项

  • 仅 Kube-OVN CNI 支持 Egress Gateway。
  • Egress Gateway 需要 Multus-CNI。

使用方法

创建 Network Attachment Definition

Egress Gateway 使用多个网卡同时访问内网和外网, 因此需要创建 Network Attachment Definition 以连接外部网络。 下面是使用 macvlan 插件和 Kube-OVN 提供的 IPAM 的示例:

apiVersion: k8s.cni.cncf.io/v1
kind: NetworkAttachmentDefinition
metadata:
  name: eth1
  namespace: default
spec:
  config: '{
      "cniVersion": "0.3.0",
      "type": "macvlan",
      "master": "eth1",
      "mode": "bridge",
      "ipam": {
        "type": "kube-ovn",
        "server_socket": "/run/openvswitch/kube-ovn-daemon.sock",
        "provider": "eth1.default"
      }
    }'
---
apiVersion: kubeovn.io/v1
kind: Subnet
metadata:
  name: macvlan1
spec:
  protocol: IPv4
  provider: eth1.default
  cidrBlock: 172.17.0.0/16
  gateway: 172.17.0.1
  excludeIps:
    - 172.17.0.2..172.17.0.10
  1. 连接外部网络的宿主机接口。
  2. Provider 名称,格式为 <network attachment definition name>.<namespace>
  3. 用于标识外部网络的 Provider 名称,必须与 NetworkAttachmentDefinition 中一致。
提示

你可以使用任意 CNI 插件创建 Network Attachment Definition 来访问对应网络。

创建 VPC Egress Gateway

创建 VPC Egress Gateway 资源,示例如下:

apiVersion: kubeovn.io/v1
kind: VpcEgressGateway
metadata:
  name: gateway1
  namespace: default
spec:
  replicas: 1
  externalSubnet: macvlan1
  nodeSelector:
    - matchExpressions:
        - key: kubernetes.io/hostname
          operator: In
          values:
            - kube-ovn-worker
            - kube-ovn-worker2
  selectors:
    - namespaceSelector:
        matchLabels:
          kubernetes.io/metadata.name: default
  policies:
    - snat: true
      subnets:
        - subnet1
    - snat: false
      ipBlocks:
        - 10.18.0.0/16
  1. 创建 VPC Egress Gateway 实例的命名空间。
  2. VPC Egress Gateway 实例的副本数。
  3. 连接外部网络的外部子网。
  4. VPC Egress Gateway 适用的节点选择器。
  5. VPC Egress Gateway 适用的命名空间和 Pod 选择器。
  6. VPC Egress Gateway 的策略,包括 SNAT 及适用的子网/IP 段。
  7. 是否为该策略启用 SNAT。
  8. 策略适用的子网。
  9. 策略适用的 IP 段。

上述资源会在 default 命名空间下创建名为 gateway1 的 VPC Egress Gateway, 以下 Pod 会通过 macvlan1 子网访问外部网络:

  • default 命名空间中的 Pod。
  • subnet1 子网下的 Pod。
  • IP 属于 CIDR 10.18.0.0/16 的 Pod。
注意

匹配 .spec.selectors 的 Pod 始终启用 SNAT 访问外部网络。

创建完成后,查看 VPC Egress Gateway 资源:

$ kubectl get veg gateway1
NAME       VPC           REPLICAS   BFD ENABLED   EXTERNAL SUBNET   PHASE       READY   AGE
gateway1   ovn-cluster   1          false         macvlan1          Completed   true    13s

查看更多信息:

kubectl get veg gateway1 -o wide
NAME       VPC           REPLICAS   BFD ENABLED   EXTERNAL SUBNET   PHASE       READY   INTERNAL IPS     EXTERNAL IPS      WORKING NODES         AGE
gateway1   ovn-cluster   1          false         macvlan1          Completed   true    ["10.16.0.12"]   ["172.17.0.11"]   ["kube-ovn-worker"]   82s

查看工作负载:

$ kubectl get deployment -l ovn.kubernetes.io/vpc-egress-gateway=gateway1
NAME       READY   UP-TO-DATE   AVAILABLE   AGE
gateway1   1/1     1            1           4m40s

$ kubectl get pod -l ovn.kubernetes.io/vpc-egress-gateway=gateway1 -o wide
NAME                       READY   STATUS    RESTARTS   AGE     IP           NODE              NOMINATED NODE   READINESS GATES
gateway1-b9f8b4448-76lhm   1/1     Running   0          4m48s   10.16.0.12   kube-ovn-worker   <none>           <none>

查看 Pod 内的 IP 地址、路由和 iptables 规则:

$ kubectl exec gateway1-b9f8b4448-76lhm -c gateway -- ip address show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: net1@if13: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 62:d8:71:90:7b:86 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 172.17.0.11/16 brd 172.17.255.255 scope global net1
       valid_lft forever preferred_lft forever
    inet6 fe80::60d8:71ff:fe90:7b86/64 scope link
       valid_lft forever preferred_lft forever
17: eth0@if18: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1400 qdisc noqueue state UP group default
    link/ether 36:7c:6b:c7:82:6b brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 10.16.0.12/16 brd 10.16.255.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::347c:6bff:fec7:826b/64 scope link
       valid_lft forever preferred_lft forever

$ kubectl exec gateway1-b9f8b4448-76lhm -c gateway -- ip rule show
0:      from all lookup local
1001:   from all iif eth0 lookup default
1002:   from all iif net1 lookup 1000
1003:   from 10.16.0.12 iif lo lookup 1000
1004:   from 172.17.0.11 iif lo lookup default
32766:  from all lookup main
32767:  from all lookup default

$ kubectl exec gateway1-b9f8b4448-76lhm -c gateway -- ip route show
default via 172.17.0.1 dev net1
10.16.0.0/16 dev eth0 proto kernel scope link src 10.16.0.12
10.17.0.0/16 via 10.16.0.1 dev eth0
10.18.0.0/16 via 10.16.0.1 dev eth0
172.17.0.0/16 dev net1 proto kernel scope link src 172.17.0.11

$ kubectl exec gateway1-b9f8b4448-76lhm -c gateway -- ip route show table 1000
default via 10.16.0.1 dev eth0

$ kubectl exec gateway1-b9f8b4448-76lhm -c gateway -- iptables -t nat -S
-P PREROUTING ACCEPT
-P INPUT ACCEPT
-P OUTPUT ACCEPT
-P POSTROUTING ACCEPT
-N VEG-MASQUERADE
-A PREROUTING -i eth0 -j MARK --set-xmark 0x4000/0x4000
-A POSTROUTING -d 10.18.0.0/16 -j RETURN
-A POSTROUTING -s 10.18.0.0/16 -j RETURN
-A POSTROUTING -j VEG-MASQUERADE
-A VEG-MASQUERADE -j MARK --set-xmark 0x0/0xffffffff
-A VEG-MASQUERADE -j MASQUERADE --random-fully

在 Gateway Pod 中抓包验证网络流量:

$ kubectl exec -ti gateway1-b9f8b4448-76lhm -c gateway -- bash
nobody@gateway1-b9f8b4448-76lhm:/kube-ovn$ tcpdump -i any -nnve icmp and host 172.17.0.1
tcpdump: data link type LINUX_SLL2
tcpdump: listening on any, link-type LINUX_SLL2 (Linux cooked v2), snapshot length 262144 bytes
06:50:58.936528 eth0  In  ifindex 17 92:26:b8:9e:f2:1c ethertype IPv4 (0x0800), length 104: (tos 0x0, ttl 63, id 30481, offset 0, flags [DF], proto ICMP (1), length 84)
    10.17.0.9 > 172.17.0.1: ICMP echo request, id 37989, seq 0, length 64
06:50:58.936574 net1  Out ifindex 2 62:d8:71:90:7b:86 ethertype IPv4 (0x0800), length 104: (tos 0x0, ttl 62, id 30481, offset 0, flags [DF], proto ICMP (1), length 84)
    172.17.0.11 > 172.17.0.1: ICMP echo request, id 39449, seq 0, length 64
06:50:58.936613 net1  In  ifindex 2 02:42:39:79:7f:08 ethertype IPv4 (0x0800), length 104: (tos 0x0, ttl 64, id 26701, offset 0, flags [none], proto ICMP (1), length 84)
    172.17.0.1 > 172.17.0.11: ICMP echo reply, id 39449, seq 0, length 64
06:50:58.936621 eth0  Out ifindex 17 36:7c:6b:c7:82:6b ethertype IPv4 (0x0800), length 104: (tos 0x0, ttl 63, id 26701, offset 0, flags [none], proto ICMP (1), length 84)
    172.17.0.1 > 10.17.0.9: ICMP echo reply, id 37989, seq 0, length 64

OVN Logical Router 上自动创建路由策略:

$ kubectl ko nbctl lr-policy-list ovn-cluster
Routing Policies
     31000                            ip4.dst == 10.16.0.0/16   allow
     31000                            ip4.dst == 10.17.0.0/16   allow
     31000                           ip4.dst == 100.64.0.0/16   allow
     30000                              ip4.dst == 172.18.0.2  reroute  100.64.0.4
     30000                              ip4.dst == 172.18.0.3  reroute  100.64.0.3
     30000                              ip4.dst == 172.18.0.4  reroute  100.64.0.2
     29100                  ip4.src == $VEG.8ca38ae7da18.ipv4  reroute  10.16.0.12
     29100                   ip4.src == $VEG.8ca38ae7da18_ip4  reroute  10.16.0.12
     29000 ip4.src == $ovn.default.kube.ovn.control.plane_ip4  reroute  100.64.0.3
     29000       ip4.src == $ovn.default.kube.ovn.worker2_ip4  reroute  100.64.0.2
     29000        ip4.src == $ovn.default.kube.ovn.worker_ip4  reroute  100.64.0.4
     29000     ip4.src == $subnet1.kube.ovn.control.plane_ip4  reroute  100.64.0.3
     29000           ip4.src == $subnet1.kube.ovn.worker2_ip4  reroute  100.64.0.2
     29000            ip4.src == $subnet1.kube.ovn.worker_ip4  reroute  100.64.0.4
  1. VPC Egress Gateway 用于转发 .spec.policies 指定 Pod 流量的逻辑路由器策略。
  2. VPC Egress Gateway 用于转发 .spec.selectors 指定 Pod 流量的逻辑路由器策略。

如果需要启用负载均衡,修改 .spec.replicas,示例如下:

$ kubectl scale veg gateway1 --replicas=2
vpcegressgateway.kubeovn.io/gateway1 scaled

$ kubectl get veg gateway1
NAME       VPC           REPLICAS   BFD ENABLED   EXTERNAL SUBNET   PHASE       READY   AGE
gateway1   ovn-cluster   2          false         macvlan           Completed   true    39m

$ kubectl get pod -l ovn.kubernetes.io/vpc-egress-gateway=gateway1 -o wide
NAME                       READY   STATUS    RESTARTS   AGE   IP           NODE               NOMINATED NODE   READINESS GATES
gateway1-b9f8b4448-76lhm   1/1     Running   0          40m   10.16.0.12   kube-ovn-worker    <none>           <none>
gateway1-b9f8b4448-zd4dl   1/1     Running   0          64s   10.16.0.13   kube-ovn-worker2   <none>           <none>

$ kubectl ko nbctl lr-policy-list ovn-cluster
Routing Policies
     31000                            ip4.dst == 10.16.0.0/16    allow
     31000                            ip4.dst == 10.17.0.0/16    allow
     31000                           ip4.dst == 100.64.0.0/16    allow
     30000                              ip4.dst == 172.18.0.2  reroute  100.64.0.4
     30000                              ip4.dst == 172.18.0.3  reroute  100.64.0.3
     30000                              ip4.dst == 172.18.0.4  reroute  100.64.0.2
     29100                  ip4.src == $VEG.8ca38ae7da18.ipv4  reroute  10.16.0.12, 10.16.0.13
     29100                   ip4.src == $VEG.8ca38ae7da18_ip4  reroute  10.16.0.12, 10.16.0.13
     29000 ip4.src == $ovn.default.kube.ovn.control.plane_ip4  reroute  100.64.0.3
     29000       ip4.src == $ovn.default.kube.ovn.worker2_ip4  reroute  100.64.0.2
     29000        ip4.src == $ovn.default.kube.ovn.worker_ip4  reroute  100.64.0.4
     29000     ip4.src == $subnet1.kube.ovn.control.plane_ip4  reroute  100.64.0.3
     29000           ip4.src == $subnet1.kube.ovn.worker2_ip4  reroute  100.64.0.2
     29000            ip4.src == $subnet1.kube.ovn.worker_ip4  reroute  100.64.0.4

启用基于 BFD 的高可用

基于 BFD 的高可用依赖于 VPC BFD LRP 功能, 因此需要修改 VPC 资源以启用 BFD Port。 以下示例为默认 VPC 启用 BFD Port:

apiVersion: kubeovn.io/v1
kind: Vpc
metadata:
  name: ovn-cluster
spec:
  bfdPort:
    enabled: true
    ip: 10.255.255.255
    nodeSelector:
      matchLabels:
        kubernetes.io/os: linux
  1. 是否启用 BFD Port。
  2. BFD Port 的 IP 地址,必须是有效且不与其他 IP/子网冲突的地址。
  3. 用于选择运行 BFD Port 的节点的节点选择器,BFD Port 绑定选中节点的 OVN HA Chassis Group,以 Active/Backup 模式工作。

启用 BFD Port 后,会在对应的 OVN Logical Router 上自动创建专用的 BFD LRP:

$ kubectl ko nbctl show ovn-cluster
router 0c1d1e8f-4c86-4d96-88b2-c4171c7ff824 (ovn-cluster)
    port bfd@ovn-cluster
        mac: "8e:51:4b:16:3c:90"
        networks: ["10.255.255.255"]
    port ovn-cluster-join
        mac: "d2:21:17:71:77:70"
        networks: ["100.64.0.1/16"]
    port ovn-cluster-ovn-default
        mac: "d6:a3:f5:31:cd:89"
        networks: ["10.16.0.1/16"]
    port ovn-cluster-subnet1
        mac: "4a:09:aa:96:bb:f5"
        networks: ["10.17.0.1/16"]
  1. 在 OVN Logical Router 上创建的 BFD Port。

随后,在 VPC Egress Gateway 中将 .spec.bfd.enabled 设置为 true,示例如下:

apiVersion: kubeovn.io/v1
kind: VpcEgressGateway
metadata:
  name: gateway2
  namespace: default
spec:
  vpc: ovn-cluster
  replicas: 2
  internalSubnet: ovn-default
  externalSubnet: macvlan1
  bfd:
    enabled: true
    minRX: 100
    minTX: 100
    multiplier: 5
  policies:
    - snat: true
      ipBlocks:
        - 10.18.0.0/16
  1. Egress Gateway 所属的 VPC。
  2. Egress Gateway 实例连接的内部子网。
  3. Egress Gateway 实例连接的外部子网。
  4. 是否为 Egress Gateway 启用 BFD。
  5. BFD 的最小接收间隔,单位毫秒。
  6. BFD 的最小发送间隔,单位毫秒。
  7. BFD 的乘数,决定多少次丢包后判定故障。

查看 VPC Egress Gateway 信息:

$ kubectl get veg gateway2 -o wide
NAME       VPC    REPLICAS   BFD ENABLED   EXTERNAL SUBNET   PHASE       READY   INTERNAL IPS                    EXTERNAL IPS                    WORKING NODES                            AGE
gateway2   vpc1   2          true          macvlan           Completed   true    ["10.16.0.102","10.16.0.103"]   ["172.17.0.13","172.17.0.14"]   ["kube-ovn-worker","kube-ovn-worker2"]   58s

$ kubectl get pod -l ovn.kubernetes.io/vpc-egress-gateway=gateway2 -o wide
NAME                       READY   STATUS    RESTARTS   AGE     IP            NODE               NOMINATED NODE   READINESS GATES
gateway2-fcc6b8b87-8lgvx   1/1     Running   0          2m18s   10.16.0.103   kube-ovn-worker2   <none>           <none>
gateway2-fcc6b8b87-wmww6   1/1     Running   0          2m18s   10.16.0.102   kube-ovn-worker    <none>           <none>

$ kubectl ko nbctl lr-policy-list ovn-cluster
Routing Policies
     31000                            ip4.dst == 10.16.0.0/16    allow
     31000                            ip4.dst == 10.17.0.0/16    allow
     31000                           ip4.dst == 100.64.0.0/16    allow
     30000                              ip4.dst == 172.18.0.2  reroute  100.64.0.4
     30000                              ip4.dst == 172.18.0.3  reroute  100.64.0.3
     30000                              ip4.dst == 172.18.0.4  reroute  100.64.0.2
     29100                  ip4.src == $VEG.8ca38ae7da18.ipv4  reroute  10.16.0.102, 10.16.0.103  bfd
     29100                   ip4.src == $VEG.8ca38ae7da18_ip4  reroute  10.16.0.102, 10.16.0.103  bfd
     29090                  ip4.src == $VEG.8ca38ae7da18.ipv4     drop
     29090                   ip4.src == $VEG.8ca38ae7da18_ip4     drop
     29000 ip4.src == $ovn.default.kube.ovn.control.plane_ip4  reroute  100.64.0.3
     29000       ip4.src == $ovn.default.kube.ovn.worker2_ip4  reroute  100.64.0.2
     29000        ip4.src == $ovn.default.kube.ovn.worker_ip4  reroute  100.64.0.4
     29000     ip4.src == $subnet1.kube.ovn.control.plane_ip4  reroute  100.64.0.3
     29000           ip4.src == $subnet1.kube.ovn.worker2_ip4  reroute  100.64.0.2
     29000            ip4.src == $subnet1.kube.ovn.worker_ip4  reroute  100.64.0.4

$ kubectl ko nbctl list bfd
_uuid               : 223ede10-9169-4c7d-9524-a546e24bfab5
detect_mult         : 5
dst_ip              : "10.16.0.102"
external_ids        : {af="4", vendor=kube-ovn, vpc-egress-gateway="default/gateway2"}
logical_port        : "bfd@ovn-cluster"
min_rx              : 100
min_tx              : 100
options             : {}
status              : up

_uuid               : b050c75e-2462-470b-b89c-7bd38889b758
detect_mult         : 5
dst_ip              : "10.16.0.103"
external_ids        : {af="4", vendor=kube-ovn, vpc-egress-gateway="default/gateway2"}
logical_port        : "bfd@ovn-cluster"
min_rx              : 100
min_tx              : 100
options             : {}
status              : up

查看 BFD 连接状态:

$ kubectl exec gateway2-fcc6b8b87-8lgvx -c bfdd -- bfdd-control status
There are 1 sessions:
Session 1
 id=1 local=10.16.0.103 (p) remote=10.255.255.255 state=Up

$ kubectl exec gateway2-fcc6b8b87-wmww6 -c bfdd -- bfdd-control status
There are 1 sessions:
Session 1
 id=1 local=10.16.0.102 (p) remote=10.255.255.255 state=Up
注意

如果所有 Gateway 实例均不可用,应用了 VPC Egress Gateway 的出口流量将被丢弃。

配置参数

VPC BFD Port

字段类型可选默认值描述示例
enabledbooleanfalse是否启用 BFD Port。true
ipstring-BFD Port 使用的 IP 地址。
不得与其他地址冲突。支持 IPv4、IPv6 和双栈。
169.255.255.255
fdff::1
169.255.255.255,fdff::1
nodeSelectormatchLabelsobject-用于选择承载 BFD Port 的节点的标签选择器。
BFD Port 绑定选中节点的 OVN HA Chassis Group,以 Active/Backup 模式工作。
若未指定,Kube-OVN 会自动选择最多三个节点。
可通过执行 kubectl ko nbctl list ha_chassis_group 查看所有 OVN HA Chassis Group 资源。
键值对映射。-
matchExpressionsobject 数组-标签选择器要求列表,要求间为 AND 关系。-

VPC Egress Gateway

字段类型可选默认值描述示例
vpcstring默认 VPC 名称 (ovn-cluster)VPC 名称。vpc1
replicasinteger/int321副本数。2
prefixstring-工作负载部署名称的不可变前缀。veg-
imagestring-工作负载部署使用的镜像。docker.io/kubeovn/kube-ovn:v1 .14.0-debug
internalSubnetstring默认 VPC 内的子网名称。用于访问内网/外网的子网名称。subnet1
externalSubnet-ext1
internalIPsstring 数组-

用于访问内网/外网的 IP 地址。支持 IPv4、IPv6 和双栈。
指定的 IP 数量不得少于 replicas
建议设置为 <replicas> + 1,避免 Pod 创建异常。

10.16.0.101

fdff::1

169.255.255.255,fdff::1
externalIPs
bfdenabledbooleanfalseBFD 配置。是否为 Egress Gateway 启用 BFD。-
minRXinteger/int321000BFD 的 minRX/minTX,单位毫秒。500
minTX
multiplierinteger/int323BFD 乘数。1
policiessnatbooleanfalse出口策略。是否启用 SNAT/MASQUERADE。true
ipBlocksstring 数组-

适用的 IP 段。
支持 IPv4 和 IPv6。

192.168.0.1
192.168.0.0/24
fd00::1
fd00::/120
subnetsstring 数组-

适用的 VPC 子网名称。
支持 IPv4、IPv6 和双栈子网。

subnet1
selectorsnamespaceSelectormatchLabelsobject-

通过命名空间选择器和 Pod 选择器配置出口策略。
匹配的 Pod 会应用 SNAT/MASQUERADE。

命名空间选择器。空标签选择器匹配所有命名空间。

键值对映射。-
matchExpressionsobject 数组-标签选择器要求列表,要求间为 AND 关系。-
podSelectormatchLabelsobject-

Pod 选择器。空标签选择器匹配所有 Pod。

键值对映射。-
matchExpressionsobject 数组-标签选择器要求列表,要求间为 AND 关系。-
nodeSelectormatchLabelsobject-

用于选择承载工作负载部署的节点的节点选择器。
工作负载(Deployment/Pod)将在选中节点上运行。

键值对映射。-
matchExpressionsobject 数组-标签选择器要求列表,要求间为 AND 关系。-
matchFieldsobject 数组-字段选择器要求列表,要求间为 AND 关系。-
trafficPolicystringCluster

仅在启用 BFD 时生效
可选值:Cluster/Local
设置为 Local 时,出口流量会优先重定向到本节点上运行的 VPC Egress Gateway 实例,若实例不可用则重定向到其他实例。

Local

相关资源