自分用メモ。
istioがinjectされたアプリを外部公開する場合は、istio-ingressgatewayを使って公開するか、NodePortで公開した普通のServiceにhttpフロントエンドなんかをおいて、それをLBの裏に置くというのがよくやられることだけれど、独自のingressgatewayを作って、それをNodePortで公開してLBの裏に置く方法をまとめておく。lbの裏にいるやつもカナリアリリースとかしたいよね。
istioを自前でインストールしている場合は、インストール時に--set gateways.istio-ingressgateway.type=NodePort
にやればよいのだけど、gkeでistioをonにして自動で入るやつを使う場合は、k8sとistioの自動アップデートで設定が戻ってしまうので既存のingressgatewayの設定を弄らず独自に作れということらしい。
istio-ingressgateway + cert-managerでも別にええんやないのという話もあるけれど、cert-managerのバージョンアップでハマったりしたので、gcpのmanaged certificateを使いってやりたかったということで勉強がてらやってみた。やってみてistio-ingressgatewayがだいたいわかった。
ベースのドキュメントはこれ
custom ingressgatewayを作る
istio-systemのnamespaceにingressgatewayとして動作させるserviceを作成する。通常のingressgatewayはtype: LoadBalancer
で構築されているのをNodePortに変える。
apiVersion: v1
kind: Service
metadata:
name: custom-ingressgateway
namespace: istio-system
labels:
app: custom-ingressgateway
istio: custom-ingressgateway
spec:
type: NodePort
selector:
app: custom-ingressgateway
istio: custom-ingressgateway
ports:
- name: http
port: 80
targetPort: 80
実体のproxyのDeploymentを作成する。設定については以下をベースに、 kubectl describe pod istio-ingressgateway -n istio-system
を見てパクってきた。
apiVersion: v1
kind: ServiceAccount
metadata:
name: custom-ingressgateway-service-account
namespace: istio-system
labels:
app: custom-ingressgateway
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: custom-ingressgateway
namespace: istio-system
labels:
app: custom-ingressgateway
istio: custom-ingressgateway
spec:
replicas: 1
template:
metadata:
labels:
app: custom-ingressgateway
istio: custom-ingressgateway
annotations:
sidecar.istio.io/inject: "false"
seccomp.security.alpha.kubernetes.io/pod: docker/default
spec:
serviceAccountName: custom-ingressgateway-service-account
containers:
- name: istio-proxy
image: gke.gcr.io/istio/proxyv2:1.1.7-gke.0
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
- containerPort: 443
- containerPort: 31400
- containerPort: 15011
- containerPort: 8060
- containerPort: 15030
- containerPort: 15031
args:
- proxy
- router
- --domain
- $(POD_NAMESPACE).svc.cluster.local
- --log_output_level=default:info
- --drainDuration
- '45s' #drainDuration
- --parentShutdownDuration
- '1m0s' #parentShutdownDuration
- --connectTimeout
- '10s' #connectTimeout
- --serviceCluster
- custom-ingressgateway
- --zipkinAddress
- zipkin:9411
- --proxyAdminPort
- "15000"
- --controlPlaneAuthPolicy
- NONE
- --discoveryAddress
- istio-pilot:15010
resources:
requests:
cpu: 10m
volumeMounts:
- mountPath: /etc/certs
name: istio-certs
readOnly: true
- mountPath: /etc/istio/ingressgateway-certs
name: ingressgateway-certs
readOnly: true
- mountPath: /etc/istio/ingressgateway-ca-certs
name: ingressgateway-ca-certs
readOnly: true
env:
- name: POD_NAME
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.namespace
- name: INSTANCE_IP
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: status.podIP
- name: HOST_IP
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: status.hostIP
- name: ISTIO_META_POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: ISTIO_META_CONFIG_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: ISTIO_META_ROUTER_MODE
value: sni-dnat
volumes:
- name: istio-certs
secret:
defaultMode: 420
optional: true
secretName: istio.istio-ingressgateway-service-account
- name: ingressgateway-certs
secret:
defaultMode: 420
optional: true
secretName: istio-ingressgateway-certs
- name: ingressgateway-ca-certs
secret:
defaultMode: 420
optional: true
secretName: istio-ingressgateway-ca-certs
autoscaleの設定も入れておく。これはgkeでistioをonにした場合に設定されている物と同様。
apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
name: custom-ingressgateway
namespace: istio-system
spec:
maxReplicas: 5
minReplicas: 1
scaleTargetRef:
apiVersion: apps/v1beta1
kind: Deployment
name: custom-ingressgateway
targetCPUUtilizationPercentage: 80
status:
currentCPUUtilizationPercentage: 0
currentReplicas: 1
desiredReplicas: 1
istio-system内のgatewayとpodの設定はここまで。この後は自分のアプリ向けの設定を適当なnamespace下で示指する。
アプリ側のGateway, VirtualService等の設定
Gatewayは以下の通り。hostsに*
があるのはlbのhealth check用。
ingressgatewayの後ろのserviceのhealth checkのpathがdefaultの/
でない場合にhealth checkがこけるので面倒なワークアラウンドを実施している。
gke.gcr.io/istio/proxyv2
のlivenessProbeの設定ができればそこでpathを変えたりできるんだろうけれど……
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
annotations:
name: custom-gateway
namespace: tekitou-na-namespace
spec:
selector:
istio: custom-ingressgateway
servers:
- hosts:
- 'ore.no.domain'
- '*'
port:
name: http
number: 80
protocol: HTTP
ヘルスチェック用のserviceを作る。ただhealth checkを通すためだけの糞コンテナをデプロイ。ore.no.domain
向けのVirtualServiceはすでにあるものとする。
apiVersion: apps/v1beta2
kind: Deployment
metadata:
name: healthcheck
namespace: tekitou-na-namespace
labels:
app: healthcheck
tier: web
spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
app: healthcheck
template:
metadata:
labels:
app: healthcheck
version: v1
tier: web
spec:
containers:
- name: healthcheck
image: aihara/healthcheck:latest
args:
- -port
- "80"
- -path
- /
ports:
- name: http
containerPort: 80
protocol: TCP
livenessProbe:
httpGet:
path: /
port: 80
readinessProbe:
httpGet:
path: /
port: 80
---
apiVersion: v1
kind: Service
metadata:
name: healthcheck
namespace: tekitou-na-namespace
labels:
app: healthcheck
tier: web
spec:
type: NodePort
ports:
- port: 80
targetPort: 80
selector:
app: healthcheck
tier: web
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: healthcheck-virtualservice
namespace: tekitou-na-namespace
spec:
hosts:
- "*"
gateways:
- bandit-gateway
http:
- route:
- destination:
host: healthcheck
Ingress
namespaceはistio-systemにしているが、ExternalServiceを使って自分のアプリのnamespaceに入れてもよいかもしれない。
ingressgatewayを通ると、X_FORWARDED_PROTO
が問答無用でhttpになってしまうので、nginxでX_FORWARDED_PROTO
がhttpのときにhttpsにリダイレクトするみたいなことをすると無限リダイレクトが発生するので、EnvoyFilterで頑張るか、kubernetes.io/ingress.allow-http: "false"
でそもそもlbレベルでhttpsを無効化するなどをする。podの中のnginxで証明書を使ってるとかだと、下のissueのようにappendHeaders
で無理やりhttpsをつけるなどする。
EnvoyFilterは試してないけどX_FORWARDED_PORT
が443のときにX_FORWARDED_PROTO
をhttpsにするとか書いて試して置きたい。組み込みluaのデバックめんどいからだるいけど。
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: lb
namespace: istio-system
annotations:
kubernetes.io/ingress.global-static-ip-name: tekitou-na-static-ip
kubernetes.io/ingress.class: gce
kubernetes.io/ingress.allow-http: "false"
ingress.gcp.kubernetes.io/pre-shared-cert: tekitouna-cert
spec:
rules:
- host: ore.no.domain
http:
paths:
- backend:
serviceName: custom-ingressgateway
servicePort: 80
これでmanaged certificateを設定したlbの裏にingressgatewayを置くことができたが、あんまりうれしみが無いかもしれない。