作業メモ。
Kubernetes完全ガイド impress top gearシリーズを読みながら手元で確認した時のメモ。
環境
$ kubectl version
Client Version: version.Info{Major:"1", Minor:"10", GitVersion:"v1.10.3", GitCommit:"2bba0127d85d5a46ab4b778548be28623b32d0b0", GitTreeState:"clean", BuildDate:"2018-05-28T20:03:09Z", GoVersion:"go1.9.3", Compiler:"gc", Platform:"darwin/amd64"}
Server Version: version.Info{Major:"1", Minor:"11+", GitVersion:"v1.11.5-eks-6bad6d", GitCommit:"6bad6d9c768dc0864dab48a11653aa53b5a47043", GitTreeState:"clean", BuildDate:"2018-12-06T23:13:14Z", GoVersion:"go1.10.3", Compiler:"gc", Platform:"linux/amd64"}
Kubernetes 環境としては EKS を利用。
LoadBalancer Service
ExternalIP 及び NodePort は Node のグローバル IP にアクセスし、Pod へのトラフィックの転送を行っていた。
LoadBalancer Service では Pod への転送を行う LoadBalancer はクラスタ外で作成する。
例えば AWS であれば ClassicLoadBalancer(以降 CLB) を作成し、トラフィックを転送出来る。
apiVersion: v1
kind: Service
metadata:
name: sample-lb
spec:
type: LoadBalancer
ports:
- name: "http-port"
protocol: "TCP"
port: 8080
targetPort: 80
nodePort: 30082
selector:
app: sample-app
意味としては以下。
- CLB でリッスンするポート番号:8080
- 各 Node への IP アドレスで受け付けるポート:30082
- コンテナで受け付けるポート:80
# サービスを作成
$ kubectl apply -f sample-lb.yaml
service "sample-lb" created
# 一定時間経過後 サービスの情報が確認できる. CLUSTER-IP 及び EXTERNAL-IP が存在する
$kubectl get service sample-lb
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
sample-lb LoadBalancer 10.100.51.18 af6a051c10ba7... 8080:30082/TCP 15s
# 詳細は describe で確認出来る
$kubectl describe service sample-lb
Name: sample-lb
Namespace: default
Labels: <none>
Annotations: kubectl.kubernetes.io/last-applied-configuration={"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"name":"sample-lb","namespace":"default"},"spec":{"ports":[{"name":"http-port","nodePo...
Selector: app=sample-app
Type: LoadBalancer
IP: 10.100.51.18
LoadBalancer Ingress: af6a051c10ba711e9a297068bea14a4c-639722574.ap-northeast-1.elb.amazonaws.com
Port: http-port 8080/TCP
TargetPort: 80/TCP
NodePort: http-port 30082/TCP
Endpoints: 172.31.20.41:80,172.31.29.9:80,172.31.9.21:80
Session Affinity: None
External Traffic Policy: Cluster
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal EnsuringLoadBalancer 54s service-controller Ensuring load balancer
Normal EnsuredLoadBalancer 49s service-controller Ensured load balancer
# ExternalIP の DNS 名でアクセス可能
$curl http://af6a051c10ba711e9a297068bea14a4c-639722574.ap-northeast-1.elb.amazonaws.com:8080/
sample-deployment-86d576464c-wkhjd
今回の場合、CLB には各インスタンスの 30082 ポート番号で登録されていた。
また、上記の際に 各ワーカー Node のセキュリティグループには CLB に設定されているセキュリティグループからの Inbound をすべて許可するルールが追加設定されており、これによって CLB->Node 間の通信が可能になっている模様。
接続元 IP アドレスを絞る
Configure Your Cloud Provider's Firewalls
ロードバランサーに対して接続元 IP を絞る場合があるが、先程のマニュフェストファイルの場合、接続元 IP アドレスは絞り込みされておらず、どの IP からでもアクセスが出来る。
接続元 IP アドレスを制限した場合、 loadBalancerSourceRanges
を遣う。
$cp sample-lb.yaml sample-lb-restrict-ip.yaml
$vi sample-lb-restrict-ip.yaml
apiVersion: v1
kind: Service
metadata:
name: sample-lb
spec:
type: LoadBalancer
ports:
- name: "http-port"
protocol: "TCP"
port: 8080
targetPort: 80
nodePort: 30082
selector:
app: sample-app
loadBalancerSourceRanges:
- 130.211.204.1/32
# 適用
$kubectl apply -f sample-lb-restrict-ip.yaml
service "sample-lb" configured
# 先程は接続できたクライアントからも接続できない
$curl http://af6a051c10ba711e9a297068bea14a4c-639722574.ap-northeast-1.elb.amazonaws.com:8080/
# CLB に設定されているセキュリティグループを見ると 8080 ポートの接続元 IP が絞り込まれている
$aws ec2 describe-security-groups --group-ids sg-040af982fa57f35e5
{
"SecurityGroups": [
{
"Description": "Security group for Kubernetes ELB af6a051c10ba711e9a297068bea14a4c (default/sample-lb)",
"GroupName": "k8s-elb-af6a051c10ba711e9a297068bea14a4c",
"IpPermissions": [
{
"FromPort": 8080,
"IpProtocol": "tcp",
"IpRanges": [
{
"CidrIp": "130.211.204.1/32"
}
],
"Ipv6Ranges": [],
"PrefixListIds": [],
"ToPort": 8080,
"UserIdGroupPairs": []
},
{
"FromPort": 3,
"IpProtocol": "icmp",
"IpRanges": [
{
"CidrIp": "0.0.0.0/0"
}
],
"Ipv6Ranges": [],
"PrefixListIds": [],
"ToPort": 4,
"UserIdGroupPairs": []
}
],
"OwnerId": "xxxx",
"GroupId": "sg-040af982fa57f35e5",
"IpPermissionsEgress": [
{
"IpProtocol": "-1",
"IpRanges": [
{
"CidrIp": "0.0.0.0/0"
}
],
"Ipv6Ranges": [],
"PrefixListIds": [],
"UserIdGroupPairs": []
}
],
"Tags": [
{
"Key": "kubernetes.io/cluster/eks-first-cluster",
"Value": "owned"
}
],
"VpcId": "vpc-8086a2e5"
}
]
}
Headless services を試す
Headless services を使うことで個々の Pod の IP アドレスを DNS ラウンドロビンで返却することが出来る。
今までの Service では提供されるエンドポイントへアクセスすると負荷分散が行われた。
しかし、負荷分散を行いたくない場合もある。
そのような場合に Headless services を使う。
試す。
Headless Service を使いたい時には clusterIP:None を指定する。
apiVersion: v1
kind: Service
metadata:
name: sample-headless
spec:
type: ClusterIP
clusterIP: None
ports:
- name: "http-port"
protocol: "TCP"
port: 80
targetPort: 80
selector:
app: sample-app
$ kubectl apply -f sample-headless.yaml
service "sample-headless" created
# エンドポイントが提供されないので CLUSTER-IP が None になっている
$kubectl get service sample-headless
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
sample-headless ClusterIP None <none> 80/TCP 39s
# 各 Pod の IP を確認
$kubectl describe pod -l app=sample-app |grep IP
IP: 172.31.20.41
IP: 172.31.29.9
IP: 172.31.9.21
# サービス名で名前解決すると各 Pod の IP が返却される
$kubectl run testpod --image=centos:6 --restart=Never -i --rm -- dig +short sample-headless.default.svc.cluster.local
172.31.20.41
172.31.29.9
172.31.9.21
SatefulSet で使って Pod 名で名前解決する
Service と紐づくのが StatefulSet の場合、Pod 名での名前解決も可能。
StatefulSet と Headless service を作って確認
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: sample-statefulset-headless
spec:
serviceName: sample-headless
replicas: 3
selector:
matchLabels:
app: sample-app-headless
template:
metadata:
labels:
app: sample-app-headless
spec:
containers:
- name: nginx-container
image: nginx:1.12
apiVersion: v1
kind: Service
metadata:
name: sample-headless
spec:
type: ClusterIP
clusterIP: None
ports:
- name: "http-port"
protocol: "TCP"
port: 80
targetPort: 80
selector:
app: sample-app-headless
# StatefulSet と Healess Serivce を作成
$kubectl apply -f sample-statefulset-headless.yaml
$kubectl apply -f sample-headless.yaml
# Pod 「sample-statefulset-headless-0」の IP は172.31.28.56
$kubectl describe pod sample-statefulset-headless-0 |grep IP
IP: 172.31.28.56
# Pod の名前解決で IP 取得可能
$kubectl run testpod --image=centos:6 --restart=Never -i --rm -- dig +short sample-statefulset-headless-0.sample-headless.default.svc.cluster.local
172.31.28.56
# サービス名で名前解決した場合、同じように各 Pod の IP が返却される
$kubectl run testpod --image=centos:6 --restart=Never -i --rm -- dig +short sample-headless.default.svc.cluster.local
172.31.25.247
172.31.28.56
172.31.7.49
# サービス名で名前解決できる IP と Pod の IP が同じ
$kubectl describe pod -l app=sample-app-headless |grep IP
IP: 172.31.28.56
IP: 172.31.7.49
IP: 172.31.25.247