네트워크 정책
네트워크 정책 (Network policy)
NetworkPolicy 리소스는 라벨 셀렉터와 매칭되는 엔드포인트 모음에 적용되는, 순서가 있는 규칙 집합을 나타내요. 네임스페이스 단위 리소스라서 특정 네임스페이스의 NetworkPolicy는 그 네임스페이스 안의 워크로드 엔드포인트에만 적용되죠. 이 문서에서는 샘플 YAML부터 필드별 정의, 셀렉터 문법과 성능 힌트까지 네트워크 정책 리소스의 전체 레퍼런스를 정리해요.
출처: 문서
본문
네트워크 정책 리소스(NetworkPolicy)는 라벨 셀렉터와 매칭되는 엔드포인트 모음에 적용되는, 순서가 있는 규칙 집합을 나타내요.
NetworkPolicy는 네임스페이스 단위 리소스예요. 특정 네임스페이스의 NetworkPolicy는 그 네임스페이스의 워크로드 엔드포인트 리소스에만 적용돼요. 두 리소스의 namespace 값이 같으면 같은 네임스페이스에 속한 거예요. 네임스페이스에 속하지 않는 네트워크 정책은 글로벌 네트워크 정책 리소스를 참고하세요.
NetworkPolicy 리소스는 Calico 엔드포인트 그룹과 호스트 엔드포인트 사이의 네트워크 연결 규칙을 정의하는 데 쓸 수 있고, 프로파일 리소스가 정의되어 있다면 그보다 우선해요.
샘플 YAML
이 샘플 정책은 frontend 엔드포인트에서 database 엔드포인트의 6379 포트로 가는 TCP 트래픽을 허용해요.
apiVersion: projectcalico.org/v3
kind: NetworkPolicy
metadata:
name: allow-tcp-6379
namespace: production
spec:
selector: role == 'database'
types:
- Ingress
- Egress
ingress:
- action: Allow
metadata:
annotations:
from: frontend
to: database
protocol: TCP
source:
selector: role == 'frontend'
destination:
ports:
- 6379
egress:
- action: Allow
정의
메타데이터 (Metadata)
| 필드 | 설명 | 허용 값 | 스키마 | 기본값 |
|---|---|---|---|---|
| name | 네트워크 정책의 이름이에요. 반드시 지정해야 해요. | ., _, -를 선택적으로 포함하는 영숫자 문자열 |
string | |
| namespace | 네임스페이스는 리소스 이름에 추가로 붙는 한정자예요. | string | "default" |
스펙 (Spec)
| 필드 | 설명 | 허용 값 | 스키마 | 기본값 |
|---|---|---|---|---|
| order | 우선순위 순서를 제어해요. Calico는 값이 가장 낮은 정책을 먼저 적용해요. | float | ||
| selector | 이 정책이 적용될 엔드포인트를 고르는 셀렉터예요. | selector | all() | |
| types | 트래픽 방향을 기준으로 정책을 적용해요. 인바운드 트래픽에 적용하려면 Ingress로, 아웃바운드 트래픽에 적용하려면 Egress로, 둘 다 적용하려면 Ingress, Egress로 설정해요. |
Ingress, Egress |
문자열 목록 | ingress/egress 규칙 유무에 따라 달라져요* |
| ingress | 정책이 적용하는 ingress 규칙의 순서 목록이에요. | Rule 목록 | ||
| egress | 이 정책이 적용하는 egress 규칙의 순서 목록이에요. | Rule 목록 | ||
| serviceAccountSelector | 이 정책이 적용될 서비스 어카운트를 고르는 셀렉터예요. projectcalico.org/name 라벨을 사용해 이름으로 특정 서비스 어카운트를 선택할 수 있어요. |
selector | all() | |
| performanceHints | 정책을 더 효율적으로 처리하도록 Calico 정책 엔진에 전달하는 힌트 목록이에요. 힌트는 정책의 강제(enforcement) 동작을 절대 바꾸지 않아요. 사용할 수 있는 힌트는 아래에 설명되어 있어요. | AssumeNeededOnEveryNode |
문자열 목록 |
* types에 값이 없으면 Calico는 다음과 같이 기본값을 정해요.
Ingress 규칙 있음 Egress 규칙 있음 Types값없음 없음 Ingress있음 없음 Ingress없음 있음 Egress있음 있음 Ingress, Egress
Rule (규칙)
하나의 규칙(rule)은 패킷 집합과 매칭하고 그 패킷들에 어떤 액션을 적용해요. 규칙을 여러 개 지정하면 순서대로 실행돼요.
| 필드 | 설명 | 허용 값 | 스키마 | 기본값 |
|---|---|---|---|---|
| metadata | 규칙별 메타데이터예요. | RuleMetadata | ||
| action | 이 규칙과 매칭될 때 수행할 액션이에요. | Allow, Deny, Log, Pass |
string | |
| protocol | 프로토콜 긍정 매치예요. | TCP, UDP, ICMP, ICMPv6, SCTP, UDPLite, 1-255 |
string | integer | |
| notProtocol | 프로토콜 부정 매치예요. | TCP, UDP, ICMP, ICMPv6, SCTP, UDPLite, 1-255 |
string | integer | |
| icmp | ICMP 매치 기준이에요. | ICMP | ||
| notICMP | ICMP에 대한 부정 매치예요. | ICMP | ||
| ipVersion | IP 버전 긍정 매치예요. | 4, 6 |
integer | |
| source | 소스 매치 파라미터예요. | EntityRule | ||
| destination | 목적지 매치 파라미터예요. | EntityRule | ||
| http | HTTP 요청 매치 파라미터예요. 이 필드를 사용하려면 애플리케이션 레이어 정책을 활성화해야 해요. | HTTPMatch |
Log 액션 이후에는 처리가 다음 규칙으로 계속 이어져요. Allow와 Deny는 즉시이자 최종 액션이라 이후 규칙은 더 처리되지 않아요.
NetworkPolicy나 GlobalNetworkPolicy에서 action을 Pass로 설정하면 남은 정책들을 건너뛰고 엔드포인트에 할당된 첫 번째 프로파일로 점프해서 프로파일에 구성된 정책을 적용해요. 엔드포인트에 구성된 프로파일이 없으면 기본 적용 액션은 Deny예요.
RuleMetadata
정책 전체가 아니라 특정 규칙에 연결되는 메타데이터예요. 메타데이터 내용은 규칙이 해석되거나 강제되는 방식에 영향을 주지 않아요. Calico와 상호작용하는 운영자나 애플리케이션이 쓸 추가 정보를 저장하는 방법일 뿐이에요.
| 필드 | 설명 | 스키마 | 기본값 |
|---|---|---|---|
| annotations | 임의의 식별용이 아닌(non-identifying) 메타데이터예요. | map of string to string |
예시:
metadata:
annotations:
app: database
owner: devops
어노테이션은 유효한 문법과 문자 집합에 관한 Kubernetes와 동일한 규칙을 따라요.
iptables 데이터플레인을 쓰는 Linux에서는 규칙 어노테이션이 Calico 규칙에 해당하는 iptables 규칙에 -m comment --comment "<key>=<value>" 형태의 주석으로 렌더링돼요.
ICMP
| 필드 | 설명 | 허용 값 | 스키마 | 기본값 |
|---|---|---|---|---|
| type | ICMP 타입 매치예요. | 0-254 정수 | integer | |
| code | ICMP 코드 매치예요. | 0-255 정수 | integer |
EntityRule
EntityRule은 규칙 전체가 매칭되기 위해 패킷의 소스나 목적지가 만족해야 할 속성을 지정해요. 패킷은 다음 조합으로 매칭할 수 있어요:
- 셀렉터를 사용하거나 특정 Kubernetes
Service를 지정해서 소스/목적지의 아이덴티티를 매칭해요. 셀렉터는 워크로드 엔드포인트, 호스트 엔드포인트, (네임스페이스 단위 또는 글로벌) 네트워크 셋과 매칭할 수 있어요. - 소스/목적지 IP 주소, 프로토콜, 포트.
규칙에 매치 기준이 여러 개 들어 있으면(예: IP와 포트) 규칙 전체가 패킷과 매칭되려면 모든 매치 기준이 만족되어야 해요.
| 필드 | 설명 | 허용 값 | 스키마 | 기본값 |
|---|---|---|---|---|
| nets | 나열된 CIDR 중 하나에 IP가 속하는 패킷과 매칭해요. | 유효한 IPv4 CIDR 목록 또는 유효한 IPv6 CIDR 목록 (한 규칙에 IPv4와 IPv6 CIDR를 섞으면 안 돼요) | cidr 목록 | |
| notNets | CIDR에 대한 부정 매치예요. 나열된 CIDR 어디에도 속하지 않은 IP의 패킷과 매칭해요. | 유효한 IPv4 CIDR 목록 또는 유효한 IPv6 CIDR 목록 (한 규칙에 IPv4와 IPv6 CIDR를 섞으면 안 돼요) | cidr 목록 | |
| selector | 선택된 엔드포인트에 대한 긍정 매치예요. namespaceSelector도 정의되어 있으면 적용 대상 엔드포인트 집합은 선택된 네임스페이스 안의 엔드포인트로 한정돼요. |
유효한 셀렉터 | selector | |
| notSelector | 선택된 엔드포인트에 대한 부정 매치예요. namespaceSelector도 정의되어 있으면 적용 대상 엔드포인트 집합은 선택된 네임스페이스 안의 엔드포인트로 한정돼요. |
유효한 셀렉터 | selector | |
| namespaceSelector | 선택된 네임스페이스에 대한 긍정 매치예요. 지정하면 선택된 Kubernetes 네임스페이스의 워크로드 엔드포인트만 매칭돼요. 네임스페이스에 적용된 라벨을 기준으로 네임스페이스를 매칭해요. 셀렉터가 적용될 스코프를 정의하고, 정의하지 않으면 셀렉터는 NetworkPolicy의 네임스페이스에 적용돼요. kubernetes.io/metadata.name 라벨을 사용하면 이름으로 특정 네임스페이스를 매칭할 수 있어요. global() 셀렉터를 사용하면 GlobalNetworkSet, 호스트 엔드포인트 같은 네임스페이스가 없는 리소스 중 이 정책이 적용될 것들을 선택할 수 있어요. |
유효한 셀렉터 | selector | |
| ports | 지정된 포트에 대한 긍정 매치예요 | ports 목록 | ||
| notPorts | 지정된 포트에 대한 부정 매치예요 | ports 목록 | ||
| serviceAccounts | 서비스 어카운트 아래에서 실행 중인 엔드포인트와 매칭해요. namespaceSelector도 정의되어 있으면 적용 대상 서비스 어카운트 집합은 선택된 네임스페이스의 서비스 어카운트로 한정돼요. |
ServiceAccountMatch | ||
| services | Kubernetes 데이터스토어 드라이버를 사용할 때만 지원되고, etcd 데이터스토어 드라이버에서는 무시돼요. 지정된 서비스와 매칭해요. egress 규칙 목적지에 지정하면 다른 선택 기준을 설정할 수 없어요. ingress 규칙 소스에 지정하면 포트에 대한 긍정/부정 매치만 지정할 수 있어요. | ServiceMatch |
네트워크 정책에서 셀렉터를 사용할 때는 셀렉터가 매칭하는 대상은 (알려진) 리소스라는 점, 반면 규칙이 매칭하는 대상은 패킷이라는 점을 기억하세요. all() 셀렉터를 가진 규칙은 "모든 패킷"과 매칭되는 게 아니라 "스코프 안의 모든 엔드포인트와 네트워크 셋에서 오는 패킷"과 매칭돼요. 모든 패킷과 매칭하려면 규칙에 셀렉터를 아예 포함하지 말아야 해요.
빈 매치 필드 (Empty match fields)
매치 필드의 빈 값은 아무것도 매칭하지 않는 빈 집합이 아니라, 필드가 아예 설정되지 않은 것처럼 취급돼요. 예를 들어 nets: []는 규칙의 매칭을 막지 않아요. 규칙이 IP 주소 제약에서 벗어나서 어떤 소스/목적지 주소와도 매칭돼요. notNets, ports, notPorts, 그리고 selector, notSelector, namespaceSelector의 빈 문자열도 마찬가지예요. Calico는 보통 리소스를 저장할 때 빈 값을 제거하므로, 정책을 다시 읽으면 그 필드는 없어져 있어요.
notSelector 이해하기 (Understanding notSelector)
notSelector는 다소 미묘해요. notSelector의 not은 셀렉터가 아니라 패킷 매치를 부정하기 때문이에요:
selector: !has(foo)는 "foo" 라벨이 없는 엔드포인트/네트워크 셋에서 오거나 향하는 패킷과 매칭돼요.notSelector: has(foo)는 "foo" 라벨을 가진 엔드포인트/네트워크 셋에서 오거나 향하는 트래픽을 제외하고, 어디서든(클러스터 밖 포함) 오거나 향하는 패킷과 매칭돼요.
EntityRule에서의 셀렉터 성능 (Selector performance in EntityRules)
정책을 데이터플레인에 렌더링할 때 Calico는 활성 규칙들의 셀렉터와 매칭되는 엔드포인트를 찾아야 해요. 이 계산은 특정 흔한 셀렉터 유형에 최적화되어 있어요. 최적화된 셀렉터 유형을 사용하면 CPU 사용량(과 정책 렌더링 시간)이 몇 자릿수 줄어들어요. 대규모(수백 개의 활성 규칙, 수십만 개의 엔드포인트)에서는 특히 중요해요.
최적화된 연산자는 다음과 같아요:
label == "value"label in { 'v1', 'v2' }has(label)<expression 1> && <expression 2>는<expression 1>이나<expression 2>중 하나라도 최적화되어 있으면 최적화돼요.
다음은 has(label)과 비슷하게 동작해요. 해당 라벨을 가진 모든 엔드포인트를 훑어서 매치를 찾아요:
label contains 's'label starts with 's'label ends with 's'
나머지 연산자, 특히 all(), !, ||, !=는 최적화되지 않아요.
예시:
a == 'b'- 최적화됨a == 'b' && has(c)- 최적화됨a == 'b' || has(c)-||를 쓰기 때문에 최적화되지 않음c != 'd'-!=를 쓰기 때문에 최적화되지 않음!has(a)-!를 쓰기 때문에 최적화되지 않음a == 'b' && c != 'd'- 최적화됨.a =='b'가 최적화됐으므로a == 'b' && <anything>도 최적화돼요.c != 'd' && a == 'b'- 최적화됨.a =='b'가 최적화됐으므로<anything> && a == 'b'도 최적화돼요.
셀렉터 (Selectors)
라벨 셀렉터는 리소스의 라벨을 기준으로 매칭되거나 매칭되지 않는 표현식이에요.
Calico 라벨 셀렉터는 여러 연산자를 지원하고, 불리언 연산자와 괄호로 더 큰 표현식을 조합할 수 있어요.
| 표현식 | 의미 |
|---|---|
| 논리 연산자 | |
( <expression> ) |
<expression>이 매칭될 때 정확히 매칭돼요. (괄호는 표현식 묶기에 사용해요.) |
! <expression> |
<expression>이 매칭되지 않을 때 정확히 매칭돼요. 팁: !는 YAML 문자열의 시작 위치에서 특수 문자예요. YAML 문자열 시작에 !를 써야 한다면 문자열을 따옴표로 감싸세요. |
<expression 1> && <expression 2> |
"And": <expression 1>과 <expression 2>가 모두 매칭될 때 정확히 매칭돼요 |
<expression 1> || <expression 2> |
"Or": <expression 1> 또는 <expression 2> 중 하나가 매칭될 때 정확히 매칭돼요. |
| 매치 연산자 | |
all() |
스코프 안의 모든 리소스와 매칭돼요. 아무것도 매칭하지 않으려면 이 연산자를 !와 조합해 !all()을 만드세요. |
global() |
네임스페이스가 없는 모든 리소스와 매칭돼요. namespaceSelector에서 글로벌 네트워크 셋 같은 전역 리소스를 고를 때 유용해요. |
k == 'v' |
라벨 'k'와 값 'v'를 가진 리소스와 매칭돼요. |
k != 'v' |
라벨 'k'가 없거나, 라벨 'k'의 값이 v와 같지 않은 리소스와 매칭돼요 |
has(k) |
값과 무관하게 라벨 'k'를 가진 리소스와 매칭돼요. 라벨 k가 없는 파드를 매칭하려면 이 연산자를 !와 조합해 !has(k)를 만드세요 |
k in { 'v1', 'v2' } |
라벨 'k'를 가지고 값이 주어진 집합에 속하는 리소스와 매칭돼요 |
k not in { 'v1', 'v2' } |
라벨 'k'가 없거나, 라벨 'k'의 값이 주어진 집합에 속하지 않는 리소스와 매칭돼요 |
k contains 's' |
라벨 'k'를 가지고 값에 부분문자열 's'를 포함하는 리소스와 매칭돼요 |
k starts with 's' |
라벨 'k'를 가지고 값이 부분문자열 's'로 시작하는 리소스와 매칭돼요 |
k ends with 's' |
라벨 'k'를 가지고 값이 부분문자열 's'로 끝나는 리소스와 매칭돼요 |
연산자의 우선순위는 다음과 같아요:
- 가장 높음: 모든 매치 연산자
- 괄호
( ... ) !를 사용한 부정&&를 사용한 논리곱- 가장 낮음:
||를 사용한 논리합
예를 들어 다음 표현식은
! has(my-label) || my-label starts with 'prod' && role in {'frontend','business'}
이렇게 "괄호로 묶여" 있어요:
((!(has(my-label)) || ((my-label starts with 'prod') && (role in {'frontend','business'}))
이 표현식은 다음을 매칭해요:
- 라벨 "my-label"이 없는 모든 리소스.
- 다음 두 조건을 모두 만족하는 모든 리소스:
my-label의 값이 "prod"로 시작하고,- role 라벨의 값이 "frontend" 또는 "business" 중 하나인 경우.
스코프와 all(), global() 연산자 이해하기: 셀렉터에는 매칭 대상이 되는 리소스 스코프가 있고, 그 스코프는 셀렉터가 쓰인 맥락에 따라 달라져요. 예를 들면:
IPPool의nodeSelector는Node리소스를 대상으로 매칭해요.NetworkPolicy의 최상위 셀렉터는NetworkPolicy와 같은 네임스페이스의 워크로드를 대상으로 매칭해요.GlobalNetworkPolicy의 최상위 셀렉터에는 그런 제약이 없어요. 네임스페이스 단위WorkloadEndpoint와 네임스페이스가 없는HostEndpoint를 포함한 모든 엔드포인트를 대상으로 매칭해요.NetworkPolicy(또는GlobalNetworkPolicy) 규칙의namespaceSelector는 워크로드가 아니라 네임스페이스의 라벨을 대상으로 매칭해요.namespaceSelector는 EntityRule에서 함께 쓰는selector의 스코프를 결정해요.namespaceSelector가 없으면 규칙의selector는 그 정책 유형의 기본 스코프와 매칭돼요. (NetworkPolicy는 같은 네임스페이스,GlobalNetworkPolicy는 모든 엔드포인트/네트워크 셋이에요)global()연산자는 (오직)namespaceSelector에서만 쓸 수 있고, 메인selector의 스코프를 GlobalNetworkSet 같은 네임스페이스가 없는 리소스까지 포함하도록 바꿔줘요. 덕분에 네임스페이스 단위NetworkPolicy리소스도 원래라면 불가능한 글로벌 네임스페이스가 없는 리소스를 참조할 수 있어요.
포트 (Ports)
Calico는 포트를 표현하는 다음 문법들을 지원해요.
| 구문 | 예시 | 설명 |
|---|---|---|
| int | 80 | 지정된 정확한(숫자) 포트 |
| start:end | 6040:6050 | start ≤ x ≤ end 범위 안의 모든(숫자) 포트 |
| string | named-port | 하나 이상 엔드포인트의 ports 목록에 정의된 이름 있는 포트 |
개별 숫자 포트는 YAML/JSON 정수로 지정할 수 있어요. 포트 범위나 이름 있는 포트는 문자열로 표현해야 해요. 예를 들어 다음은 유효한 포트 목록이에요:
ports: [8080, '1234:5678', 'named-port']
이름 있는 포트 (Named ports)
EntityRule에서 숫자 포트 대신 이름 있는 포트를 쓰면 간접 참조 층이 하나 생겨서, 이름 있는 포트가 엔드포인트마다 다른 숫자 값으로 매핑될 수 있어요.
예를 들어 여러 HTTP 서버가 워크로드로 실행 중이고, 어떤 서버는 HTTP 포트를 80으로, 어떤 서버는 8080으로 노출한다고 해봐요. 각 워크로드에 올바른 로컬 포트로 매핑되는 http-port라는 이름 있는 포트를 만들 수 있어요. 그러면 규칙에서는 서버 유형마다 다른 규칙을 쓰는 대신 http-port라는 이름을 참조하면 되죠.
보조 정보: 이름 있는 포트 하나가 여러 엔드포인트를 가리킬 수 있고(Calico는 이름 있는 포트를 엔드포인트/포트 조합의 집합으로 펼쳐야 해요), 단순 숫자 포트보다 CPU 비용이 훨씬 커요. 이름 있는 포트는 간접 참조가 꼭 필요한 곳에서만 아껴 쓰는 걸 권해요.
ServiceAccountMatch
ServiceAccountMatch는 EntityRule에서 서비스 어카운트를 매칭해요.
| 필드 | 설명 | 스키마 |
|---|---|---|
| names | 이름으로 서비스 어카운트를 매칭해요 | 문자열 목록 |
| selector | 라벨로 서비스 어카운트를 매칭해요 | selector |
ServiceMatch
ServiceMatch는 EntityRule에서 서비스를 매칭해요.
| 필드 | 설명 | 스키마 |
|---|---|---|
| name | 서비스의 이름이에요. | string |
| namespace | 서비스의 네임스페이스예요. | string |
서비스 매치는 Kubernetes 데이터스토어 드라이버를 사용할 때만 지원돼요. etcd 데이터스토어 드라이버는 지원하지 않아요.
성능 힌트 (Performance Hints)
성능 힌트는 정책의 의도된 용도를 Calico에게 알려줘서 더 효율적으로 처리하게 하는 방법이에요. 현재 힌트는 하나만 정의되어 있어요:
AssumeNeededOnEveryNode: 보통 Calico는 정책이 실제로 사용 중인 노드(즉, 셀렉터가 로컬 엔드포인트와 매칭되는 노드)에서만 정책의 규칙과 셀렉터를 계산해요. 대부분의 경우 이렇게 하면 작업을 아낄 수 있어요.AssumeNeededOnEveryNode힌트는 Calico에게 그 정책을 모든 노드에서 "사용 중"으로 취급하라고 알려줘요. 모든(또는 거의 모든) 엔드포인트에 적용된다고 알려진 큰 정책 집합에 유용해요. 사실상 모든 노드에 정책을 "프리로드"해서 정책과 매칭되는 첫 엔드포인트가 나타날 때 해야 할 일을 줄여주고, 마지막 엔드포인트가 드레인될 때 정책을 정리하는 작업도 막아줘요.
애플리케이션 레이어 정책 (Application layer policy)
애플리케이션 레이어 정책은 Calico의 선택적 기능이고, 다음 매치 기준을 사용하려면 반드시 활성화해야 해요.
보조 정보: 애플리케이션 레이어 정책 매치 기준은 다음 제약과 함께 지원돼요.
- ingress 정책만 지원돼요. egress 정책에는 애플리케이션 레이어 정책 매치 절을 포함하면 안 돼요.
- 애플리케이션 레이어 정책 매치 절을 포함하는 규칙은 action이
Allow여야 해요.
HTTPMatch
HTTPMatch는 HTTP 요청의 속성을 매칭해요. 규칙에 HTTPMatch 절이 있으면 그 규칙은 HTTP 트래픽에만 매칭돼요. 다른 애플리케이션 레이어 프로토콜은 그 규칙과 매칭되지 않아요.
예시:
http:
methods: ['GET', 'PUT']
paths:
- exact: '/projects/calico'
- prefix: '/users'
| 필드 | 설명 | 스키마 |
|---|---|---|
| methods | HTTP 메서드를 매칭해요. 대소문자를 구분해요. 표준 HTTP 메서드 설명. | 문자열 목록 |
| paths | HTTP 경로를 매칭해요. 대소문자를 구분해요. | HTTPPathMatch 목록 |
HTTPPathMatch
| 구문 | 예시 | 설명 |
|---|---|---|
| exact | exact: "/foo/bar" |
쿼리 문자열이나 프래그먼트를 제외하고 쓰인 그대로의 정확한 경로와 매칭돼요. |
| prefix | prefix: "/keys" |
주어진 접두사로 시작하는 모든 경로와 매칭돼요. |
더 알아보기 (Learn more)
NetworkPolicy는 네임스페이스 안에서 파드 간 통신을 세밀하게 제어하는 기본 수단이에요. 매치 필드를 비워 두면 필드를 설정하지 않은 것과 똑같이 동작한다는 점만 기억하면 실수를 줄일 수 있어요. 대규모 클러스터에서는 최적화된 셀렉터 연산자를 쓰면 정책 렌더링 비용을 크게 아낄 수 있어요.