Ingressをいれよう

はじめに

前回Kubernetesを利用してwebサービスを立ち上げた。その時に気になっていたSSL化とFQDNによるアクセスを実現してみようと思う。

調べたところTLS終端ができてURLパスに応じたバックエンドへのルーティングもできるIngressという機能がkubernetesに提供されているらしい。これを使おう。今回は検証用パス(/test)の一つだけしか作成しないが、パスの追加も簡単にできそうである。

環境構築

Helmを使う

IngressをインストールするにはHelmと呼ばれるkubernetesのパッケージマネージャを利用する。kubernetesにおけるHelmの位置づけは、LINUXにおけるyumやrpmと同様であり、ファイルの依存関係やアップデート、削除などが容易にできる。

Azure Cloud Shellには既にhelm clientはインストール済みで、以下のコマンドで存在とバージョンを確認ができる。バージョン2と3ではコマンド書式が違うようなので利用時には注意が必要である。さしあたって、気を付けるところはinstallコマンドのおいてバージョン2ではname指定がオプションだったのが、バージョン3では引数になって必須になっているところかと。

$ helm version
version.BuildInfo{Version:"v3.2.0", GitCommit:"e11b7ce3b12db2941e90399e874513fbd24bcb71", GitTreeState:"clean", GoVersion:"go1.13.10"}

Ingressはkubernetesの公式リポジトリ内にあるので、以下のようにしてリポジトリを追加する。

$ helm search repo stable
Error: no repositories configured

# 公式Chartリポジトリを追加
$ helm repo add stable https://kubernetes-charts.storage.googleapis.com/
"stable" has been added to your repositories

# リポジトリにingressの追加を確認
$ helm search repo | grep nginx-ingress
stable/nginx-ingress                    1.36.3          0.30.0                  An nginx Ingress controller that uses ConfigMap...

Ingressのインストール

検証した後に簡単にリソースを消せるように、ここで新たな名前空間ingress-basicを作成しておきます。そこにIngressをインストールします。

# 名前空間ingress-basicを作成
$ kubectl create namespace ingress-basic

# ingressをインストール
$ helm install nginx-ingress stable/nginx-ingress \
    --namespace ingress-basic \
    --set controller.replicaCount=2 \
    --set controller.nodeSelector."beta\.kubernetes\.io/os"=linux \
    --set defaultBackend.nodeSelector."beta\.kubernetes\.io/os"=linux

Ingressコントローラとバックエンドの2つのサービスが立ち上がります。コントローラにはグローバルIPが割り当てられていることが確認できる。

$ kubectl get svc
NAME                            TYPE           CLUSTER-IP    EXTERNAL-IP    PORT(S)                      AGE
kubernetes                      ClusterIP      10.0.0.1      <none>         443/TCP                      174m
nginx-ingress-controller        LoadBalancer   10.0.6.130    20.44.142.64   80:32343/TCP,443:32472/TCP   11m
nginx-ingress-default-backend   ClusterIP      10.0.97.127   <none>         80/TCP                       11m

$ kubectl get pod -o wide
NAME                                             READY   STATUS    RESTARTS   AGE   IP            NODE                                NOMINATED NODE   READINESS GATES
nginx-ingress-controller-5bccf8954f-hkvhs        1/1     Running   0          12m   10.244.0.11   aks-agentpool-39326823-vmss000001   <none>           <none>
nginx-ingress-controller-5bccf8954f-qsggq        1/1     Running   0          12m   10.244.1.6    aks-agentpool-39326823-vmss000000   <none>           <none>
nginx-ingress-default-backend-76b8f499cb-2jpxz   1/1     Running   0          12m   10.244.1.7    aks-agentpool-39326823-vmss000000   <none>           <none>

テスト用のWebサービスを作成

動作確認用にWebサービスを立ち上げておきます。

webアプリケーションをデプロイします。マニフェストは以下のとおり。コンテナイメージには、予め自身のホスト名を表示するだけのNode.jsのアプリを作成しています。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
spec: # ======================== スペック =================================
  replicas: 2       # レプリカ数
  selector: # ================== 検索条件 =================================
    matchLabels:
      app: web
  template: # ================== テンプレート =============================
    metadata:
      labels:
        app: web
        env: production
    spec:
      containers:
      - name: web-container # コンテナ名
        image: registry0511.azurecr.io/web-image:v1.0   # コンテナイメージの場所
        ports:
        - containerPort: 3000 # ポート番号
        env:
      imagePullSecrets:
        - name: acr-pull-key # コンテナレジストリのPULLキー

上記マニフェスト(web-deployment.yaml)を適用します。

# webサービスのデプロイ
$ kubectl apply -f web-deployment.yaml -n ingress-basic

# ポッドが2つ立ち上がっていることを確認
$ kubectl get pod -n ingress-basic
NAME                                             READY   STATUS    RESTARTS   AGE
web-6465b99c89-2kw9d                             1/1     Running   0          9h
web-6465b99c89-7q9sn                             1/1     Running   0          9h

続いて、ロードバランサ―です。ロードバランサ―はデフォルトでEXTERNAL-IPにグローバルアドレスが付与されますが、今回は内部アドレスを持たせたいので、annotationsにazure-load-balancer-internalをtrueにします。

apiVersion: v1
kind: Service
metadata:
  namespace: ingress-basic
  name: web-lb
  annotations:
    service.beta.kubernetes.io/azure-load-balancer-internal: "true"
spec: # ======================== スペック =================================
  type: LoadBalancer
  ports:
    - port: 80         # 自身のポート番号
      targetPort: 3000 # 振り先のポート番号
      protocol: TCP
  selector:
    app: web

上記マニフェスト(lb-service.yaml)を適用します。しばらくすると、EXTERNAL-IPに内部アドレスが付与されています。

$ kubectl apply -f internal-lb-service.yaml

$ kubectl get svc -n ingress-basic
NAME                            TYPE           CLUSTER-IP     EXTERNAL-IP     PORT(S)                      AGE
web-lb                          LoadBalancer   10.0.241.171   10.240.0.6      80:32104/TCP                 9h

FQDNの割り当て

グローバルアドレスにFQDNを割り当てるシェルを作成(createDns.sh)します。ここではホスト名をdemo-ingressとしています。シェルの詳細はAKSのマニュアルをご参照ください。

#!/bin/sh
# Public IP address of your ingress controller
IP="<グローバルアドレス>"

# Name to associate with public IP address
DNSNAME="demo-ingress"

# Get the resource-id of the public ip
PUBLICIPID=$(az network public-ip list --query "[?ipAddress!=null]|[?contains(ipAddress, '$IP')].[id]" --output tsv)

# Update public ip address with DNS name
az network public-ip update --ids $PUBLICIPID --dns-name $DNSNAME

createDns.shを実行します。Azure上のGUIからDNS名が割り当てられるが確認できます。これでインターネット上からも名前解決できるようになっているので、nslookupやdigで確認しておきます。

SSL証明書の自動更新

SSL証明書は無料のLet’s encrypを利用する。kubernetesにはcert-manaerという証明書を自動で発行管理するマネージャがあるのでそれを使う。これもAKSのマニュアルに作成用のシェルが記載されているのでそのまま使います。

#!/bin/sh
# Install the CustomResourceDefinition resources separately
kubectl apply --validate=false -f https://raw.githubusercontent.com/jetstack/cert-manager/release-0.13/deploy/manifests/00-crds.yaml

# Label the ingress-basic namespace to disable resource validation
kubectl label namespace ingress-basic cert-manager.io/disable-validation=true

# Add the Jetstack Helm repository
helm repo add jetstack https://charts.jetstack.io

# Update your local Helm chart repository cache
helm repo update

# Install the cert-manager Helm chart
helm install \
  cert-manager \
  --namespace ingress-basic \
  --version v0.13.0 \
  jetstack/cert-manager
# cert-managerがインストールされている事を確認
$ helm ls --namespace ingress-basic
NAME            NAMESPACE       REVISION        UPDATED                                 STATUS          CHART                   APP VERSION
cert-manager    ingress-basic   1               2020-05-15 12:06:46.425381354 +0000 UTC deployed        cert-manager-v0.13.0    v0.13.0

# cert-managerのサービスが起動されている事を確認
$ kubectl get svc --namespace ingress-basic
NAME                            TYPE           CLUSTER-IP     EXTERNAL-IP    PORT(S)                      AGE
cert-manager                    ClusterIP      10.0.61.46     <none>         9402/TCP                     2m16s
cert-manager-webhook            ClusterIP      10.0.86.58     <none>         443/TCP                      2m16s

証明書の発行にあたり身元を示す発行者、CA ClusterIssuerリソースを作成します。メールアドレスの記載を自身のアドレスに変えて適用します。

$ kubectl apply -f cluster-issuer.yaml --namespace ingress-basic
clusterissuer.cert-manager.io/letsencrypt created
$ kubectl get clusterissuer --namespace ingress-basic
NAME          READY   AGE
letsencrypt   True    36m

$ kubectl describe clusterissuer --namespace ingress-basic
Name:         letsencrypt
 (略)
Status:
  Acme:
    Last Registered Email:  <メールアドレス>
    Uri:                    https://acme-v02.api.letsencrypt.org/acme/acct/86105715
  Conditions:
    Last Transition Time:  2020-05-14T05:37:58Z
    Message:               The ACME account was registered with the ACME server
    Reason:                ACMEAccountRegistered
    Status:                True
    Type:                  Ready
Events:                    <none>

Ingressルートの作成

外部からのトラフィックを内部ロードバランサに振り分けるIngressリソースを作成します。

適用するマニフェストは以下のとおり。先ほど作成した証明書の発行者やTLSを終端するFQDN、振り分けるパス(/test)や負荷分散リソース名などを記載する。

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: demo-ingress
  namespace: ingress-basic
  annotations:
    kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/rewrite-target: /
    cert-manager.io/cluster-issuer: letsencrypt
spec: # ======================== スペック =================================
  tls:
  - hosts:
    - demo-ingress.japaneast.cloudapp.azure.com
    secretName: tls-secret
  rules:
  - host: demo-ingress.japaneast.cloudapp.azure.com
    http:
      paths:
      - backend:
          serviceName: web-lb
          servicePort: 80
        path: /test
$ kubectl apply -f ingress.yaml
ingress.extensions/demo-ingress created

マニフェストを適用して1分程度すると、証明書リソースが出来上がる。

$ kubectl get certificate --namespace ingress-basic
NAME         READY   SECRET       AGE
tls-secret   False   tls-secret   19s

$ kubectl get certificate --namespace ingress-basic
NAME         READY   SECRET       AGE
tls-secret   True    tls-secret   42s

これで、サイト名https://demo-ingres.japaneast.cloudapp.azure.comをインターネット上から閲覧できるようになっているはず。

タイトルとURLをコピーしました