minikubeでTLS通信して裏側のNginxでHTTP/2.0を受ける
概要
minikubeでnginx, MySQL, Go環境を作ることはできた。
これから作るアプリケーションではgRPCを導入したいので、HTTP/2.0による通信を行いたい。
一旦minikube裏側のnginx podにtlsで通信しアクセスログにHTTP/2.0が出力されることを目標にする。
minikubeを起動する
$ # とりあえず全部消しとく $ minikube stop $ minikube delete $ rm -rf ~/.minikube $ $ # hyperkitで起動 $ minikube start --vm-driver=hyperkit 😄 minikube v0.35.0 on darwin (amd64) 🔥 Creating hyperkit VM (CPUs=2, Memory=2048MB, Disk=20000MB) ... 💿 Downloading Minikube ISO ... 184.42 MB / 184.42 MB [============================================] 100.00% 0s 📶 "minikube" IP address is 192.168.64.52 🐳 Configuring Docker as the container runtime ... ✨ Preparing Kubernetes environment ... 💾 Downloading kubelet v1.13.4 💾 Downloading kubeadm v1.13.4 🚜 Pulling images required by Kubernetes v1.13.4 ... 🚀 Launching Kubernetes v1.13.4 using kubeadm ... ⌛ Waiting for pods: apiserver proxy etcd scheduler controller addon-manager dns 🔑 Configuring cluster permissions ... 🤔 Verifying component health ..... 💗 kubectl is now configured to use "minikube" 🏄 Done! Thank you for using minikube! $ minikube status host: Running kubelet: Running apiserver: Running kubectl: Correctly Configured: pointing to minikube-vm at 192.168.64.52
ingressに関する設定
ingress controllerをインストール
minikube addons list
にあるingressを有効化するとingress controller podが立ち上がるが、これを使うとingress controllerから裏側のnginx podにssl通信を通すことが出来ない。
ingress controllerにenable-ssl-passthrough
オプションを指定すると裏側までssl通信を行うことができるが、minikube addonsではそれが指定できない。
k8sのパッケージ管理ツールhemlをインストールして利用する。
controller.extraArgs.enable-ssl-passthroughをオプションに指定してインストール
$ # ingress controllerをインストールする $ ## helmが動くようにする $ helm init --upgrade $ $ ## tiller-deploy-xxx podがRunningになることを確認 $ kubectl -n kube-system get pods NAME READY STATUS RESTARTS AGE ... tiller-deploy-6d6cc8dcb5-mfvbx 1/1 Running 0 41s $ $ ## helmでingress controllerのインストール $ helm install \ --namespace kube-system \ --set controller.hostNetwork=true \ --set controller.kind=DaemonSet \ --set controller.extraArgs.enable-ssl-passthrough="" \ stable/nginx-ingress $ $ ## xxx-nginx-ingress-controller-xxx podがRunningになることを確認 $ kubectl -n kube-system get pods NAME READY STATUS RESTARTS AGE foolhardy-whale-nginx-ingress-controller-s62mk 1/1 Running 0 7h foolhardy-whale-nginx-ingress-default-backend-bb4cdcf54-vqjkq 1/1 Running 0 7h
ingressの設定
基本的にはホスト名に応じてどのserviceにアクセスさせるか記載しただけだが
重要なポイントとしてはnginx.ingress.kubernetes.io/ssl-passthrough
annotationを指定してるとこ。
これでingressから裏側のpodにssl通信を通すことができる
ingress.yaml
apiVersion: extensions/v1beta1 kind: Ingress metadata: name: ucwork.local annotations: kubernetes.io/ingress.class: "nginx" nginx.ingress.kubernetes.io/ssl-passthrough: "true" nginx.ingress.kubernetes.io/force-ssl-redirect: "true" spec: rules: - host: nginx.ucwork.local http: paths: - backend: serviceName: nginx servicePort: 443
他のサイト見てるとここのspec直下にtlsを指定しているものもあるが
ssl-passthrough annotationを指定すると必要なくなった。。
ingress生成
さっきのingressgファイルをcreateじゃ
$ kubectl create -f ingress.yaml ingress.extensions "ucwork.local" created $ $ kubectl get ing NAME HOSTS ADDRESS PORTS AGE ucwork.local nginx.ucwork.local 80 3h
裏側のNginxの設定
自己証明書を作成してtls通信を実現する。
生成した鍵や証明書はSecretsにより利用。
鍵や証明書を利用するための設定ファイル(nginx.conf)はConfigMapにより設定する
自己証明書の作成
自己証明書作成時は色々聞かれるが
Common Nameにワイルドカード*.ucwork.local
を指定し、他は設定せずenter連打
$ openssl genrsa 2048 > server.key; $ $ openssl req -new -key server.key > server.csr ... Common Name (eg, fully qualified host name) []:*.ucwork.local ... $ openssl x509 -days 3650 -req -signkey server.key < server.csr > server.crt
自己証明書をSecretsとして登録
登録しとくとSecrets名を指定して利用できる!
$ kubectl create secret tls tls-secret --key ./server.key --cert ./server.crt secret "tls-secret" created $ # secretできてる $ kubectl get secret NAME TYPE DATA AGE default-token-x548f kubernetes.io/service-account-token 3 17m tls-secret kubernetes.io/tls 2 18s
Nginx設定ファイル(nginx.conf)をConfigMapとして指定
nginx/conf/nginx.conf
server { listen 443 ssl http2; server_name nginx.ucwork.local; server_tokens off; access_log /var/log/nginx/ucwork_ssl_access.log; error_log /var/log/nginx/ucwork_ssl_error.log; ssl_certificate /etc/nginx/tls/tls.crt; ssl_certificate_key /etc/nginx/tls/tls.key; location / { root /usr/share/nginx/html; index index.html index.htm; } }
設定ファイルの格納されたディレクトリを指定してConfigMap作成
$ kubectl create configmap nginx-conf --from-file=nginx/conf $ kubectl get configmap NAME DATA AGE nginx-conf 1 18s $ $ # confの内容がconfigmapに入ってる!! $ kubectl describe configmap +[master] Name: nginx-conf Namespace: default Labels: <none> Annotations: <none> Data ==== nginx.conf: ---- server { listen 443 ssl http2; server_name nginx.ucwork.local; server_tokens off; access_log /var/log/nginx/ucwork_ssl_access.log; error_log /var/log/nginx/ucwork_ssl_error.log; ssl_certificate /etc/nginx/tls/tls.crt; ssl_certificate_key /etc/nginx/tls/tls.key; location / { root /usr/share/nginx/html; index index.html index.htm; } } Events: <none>
NginxのService, deployment, pod, PVC周りの設定ファイル準備
以下マニュフェストを作成(マニュフェストっていうのかな・・?)
nginx-deployment.yaml
apiVersion: v1 kind: Service metadata: name: nginx labels: app: nginx spec: ports: - port: 443 selector: app: nginx tier: frontend type: LoadBalancer --- apiVersion: v1 kind: PersistentVolumeClaim metadata: name: nx-pv-claim labels: app: nginx spec: accessModes: - ReadWriteOnce resources: requests: storage: 20Gi --- apiVersion: apps/v1 # for versions before 1.9.0 use apps/v1beta2 kind: Deployment metadata: name: nginx labels: app: nginx spec: selector: matchLabels: app: nginx tier: frontend strategy: type: Recreate template: metadata: labels: app: nginx tier: frontend spec: containers: - image: shintaro0123/nginx:release-n-1.0.5 name: nginx ports: - containerPort: 443 name: nginx volumeMounts: - name: nginx-conf mountPath: /etc/nginx/conf.d - name: tls-cert mountPath: /etc/nginx/tls - name: nginx-persistent-storage mountPath: /var/www/html volumes: - name: nginx-persistent-storage persistentVolumeClaim: claimName: nx-pv-claim - name: nginx-conf configMap: name: nginx-conf - name: tls-cert secret: secretName: tls-secret
ポイント的には以下くらいかな
- name: nginx-confでconfigmapを指定。コンテナの/etc/nginx/conf.dにマウントする
- name: tls-certでsecretを指定。tlsを見に行っている場所(/etc/nginx/tls)にマウントする
ちなみにshintaro0123/nginx:release-n-1.0.5
は
こんな感じのDockerfileで生成されたimage。同階層に適当なindex.htmlをおいてもらえればいい
Dockerfile
FROM nginx:1.15.8 LABEL maintainer="shintaro.0112@gmail.com" RUN mkdir -p /etc/nginx/tls RUN chown -R root:root /etc/nginx/tls RUN chmod -R 600 /etc/nginx/tls ADD ./index.html /usr/share/nginx/html
NginxのService, deployment, pod, PVC周り作成
コマンド実行
$ kubectl create -f nginx-deployment.yaml service "nginx" created persistentvolumeclaim "nx-pv-claim" created deployment.apps "nginx" created
動作確認
ログを確認しながらhttpsでアクセスしてみる!
$ # アクセスしてみる $ curl --resolve nginx.ucwork.local:443:`minikube ip` -k https://nginx.ucwork.local $ $ # 裏のNginxのログ吐き出す $ kubectl get pods NAME READY STATUS RESTARTS AGE nginx-7cbf788fd4-lk6q2 1/1 Running 0 1m $ kubectl exec -it nginx-7cbf788fd4-lk6q2 bash root@nginx-7cbf788fd4-lk6q2:/# root@nginx-7cbf788fd4-lk6q2:/# tail -f /var/log/nginx/ucwork_ssl_access.log 192.168.64.52 - - [27/Mar/2019:12:20:59 +0000] "GET / HTTP/2.0" 200 149 "-" "curl/7.54.0" $ # HTTP/2.0出てるうううううううう!!!
ちなみにcurl -k https://`minikube ip` -H "Host: nginx.ucwork.local"
ではTLSのハンドシェイクでホスト名が読み取れず、HTTP/1.1になってしまった。。。。これでだいぶつまずいた・・・
もちろん/etc/hostsファイルにminikube ipとドメイン名の関係を書いてブラウザからアクセスしてもらっても全然問題ない
まとめ
TLS通信が絡むことでものすごいつまずいた。
minikube addonsのingressでなんとか頑張ろうとしたり、curlがハンドシェイクしなかったり。。。
あとはminikbue起動時に自動でこの辺の処理実行してくれるようにしたりして 実際のアプリ開発進めていこう!!!!!!