元営業WEBエンジニアのアプリ開発日記

営業出身のWEB系エンジニアが気になったものから作ってはメモを残してくブログ

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-passthroughannotationを指定してるとこ。
これで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起動時に自動でこの辺の処理実行してくれるようにしたりして 実際のアプリ開発進めていこう!!!!!!