最初からKubernetesやGKEを使っているプロジェクトには無縁だと思うのだが、途中から部分的にKubernetes化を進めて、Kubernetesクラスタ外からServiceを利用できるようにしたいというケースもあるかと思う。
Nginx Ingress Controller
このようなケースではserviceをKubernetesクラスタ外に公開する必要があるので、ingressを用意する必要がある。ingressから受けたリクエストをうまくルーティングする層が必要になるわけだが、その際に役に立つのがingress-nginxだ。
設定
kube-system
に配置しているが、別のnamespaceでingressを設定すればクロスネームスペースからでも利用できる。この辺は各自の環境に合わせて忖度してほしい。
default-backend
default-backendはingressで設定してないルール外へのリクエストを受けるためのバックエンド。ingress-nginxはこれを利用するのでまずはdefault-backendをserviceとして用意しておく。
apiVersion: v1
kind: Service
metadata:
name: nginx-default-backend
namespace: kube-system
labels:
k8s-addon: ingress-nginx.addons.k8s.io
spec:
ports:
- port: 80
targetPort: http
selector:
app: nginx-default-backend
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: nginx-default-backend
namespace: kube-system
labels:
k8s-addon: ingress-nginx.addons.k8s.io
spec:
replicas: 1
template:
metadata:
labels:
k8s-addon: ingress-nginx.addons.k8s.io
app: nginx-default-backend
spec:
terminationGracePeriodSeconds: 60
containers:
- name: default-http-backend
image: gcr.io/google_containers/defaultbackend:1.0
livenessProbe:
httpGet:
path: /healthz
port: 8080
scheme: HTTP
initialDelaySeconds: 30
timeoutSeconds: 5
ports:
- name: http
containerPort: 8080
protocol: TCP
---
configmap
ingress-nginxにしかけるconfigmap。nginxのバックエンドにupstreamされる際のログのフォーマットが設定できる。publicでHTTP2にする場合はuse-proxy-protocol: "true"
を設定するが、今回のようにinternalで素のHTTP/1.1であればナシでいい。
apiVersion: v1
kind: ConfigMap
metadata:
name: ingress-nginx-internal
namespace: kube-system
labels:
k8s-addon: ingress-nginx.addons.k8s.io
data:
# HTTP/1.1であればproxy protocolナシで
# use-proxy-protocol: "true"
log-format-upstream: '{ "time": "$time_iso8601", "remote_addr": "$proxy_protocol_addr",
"x-forward-for": "$proxy_add_x_forwarded_for", "request_id": "$request_id", "remote_user":
"$remote_user", "bytes_sent": $bytes_sent, "request_time": $request_time, "status":
$status, "vhost": "$host", "request_proto": "$server_protocol", "path": "$uri",
"request_query": "$args", "request_length": $request_length, "duration": $request_time,
"method": "$request_method", "http_referrer": "$http_referer", "http_user_agent":
"$http_user_agent" }'
deployment
続いてdeployment。ingress-nginxコンテナを配置する。ingress-nginxを複数配置したいようなケースも当然あるので、Kubernetesのingressが利用するingress-nginxを特定できるようにオプションで--ingress-class
を設定しておき、任意の名前をつけておくとよい。
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: ingress-nginx-internal
namespace: kube-system
labels:
k8s-addon: ingress-nginx.addons.k8s.io
spec:
replicas: 1
template:
metadata:
labels:
app: ingress-nginx-internal
k8s-addon: ingress-nginx.addons.k8s.io
spec:
terminationGracePeriodSeconds: 60
containers:
- image: gcr.io/google_containers/nginx-ingress-controller:0.9.0-beta.11
name: ingress-nginx-internal
imagePullPolicy: Always
ports:
- name: http
containerPort: 80
protocol: TCP
livenessProbe:
httpGet:
path: /healthz
port: 10254
scheme: HTTP
initialDelaySeconds: 30
timeoutSeconds: 5
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
args:
- /nginx-ingress-controller
- --default-backend-service=$(POD_NAMESPACE)/nginx-default-backend
- --configmap=$(POD_NAMESPACE)/ingress-nginx-internal
- --publish-service=$(POD_NAMESPACE)/ingress-nginx-internal
- --ingress-class=nginx-internal
service
ServiceのAnnotationとしてservice.beta.kubernetes.io/aws-load-balancer-internal
を設定している。AWS環境であれば、service作成時にinternalなClassic ELBが自動で作成される。セキュリティグループも新規作成したELB用に新たに作られるので、適切なACLを設定してほしい。
apiVersion: v1
kind: Service
metadata:
name: ingress-nginx-internal
namespace: kube-system
labels:
k8s-addon: ingress-nginx.addons.k8s.io
annotations:
service.beta.kubernetes.io/aws-load-balancer-internal: "xx.xx.xx.xx/xx"
spec:
type: LoadBalancer
selector:
app: ingress-nginx-internal
ports:
- name: http
port: 80
targetPort: http
ingress
最後に任意のnamespace上に、ingress-nginxを利用して個別のserviceにVirtualHost的なプロキシをするためのingressを用意する。deploymentで--ingress-class
を指定しているので、この値をkubernetes.io/ingress.class
のアノテーションで設定しておく。
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: ingress-internal
namespace: your_namespace
annotations:
ingress.kubernetes.io/ingress: nginx
kubernetes.io/ingress.class: "nginx-internal"
spec:
rules:
- host: hogehoge.your_internal.local
http:
paths:
- path: /
backend:
serviceName: hogehoge
servicePort: 80
こうしておくと、hogehoge.your_internal.local
へのHTTPリクエストはhogehogeというサービスにプロキシされるようになる。個別のnamespaceにそれぞれingress-nginxを立てるよりは、どこか一箇所に集約し、ELBを1本でも節約できる方向に持っていった方がよいと思う。
ちなみにここに定義したからといってRoute53に設定DNSが定義されるわけではないので注意。これは各々の運用によると思うが、kubernetes-incubator/external-dnsを利用するのも良いだろう。
まとめ
部分的にKubernetesを使う上でのエッセンスなので覚えておくと良いでしょう(ウチはただいま絶賛移行中)