taketiyo.log

Web Engineering 🛠 & Body Building 💪

【kubernetes】複数ドメインでhttps接続が可能なクラスタの構築手順【Docker】

Programming

  / /

kubernetesにて複数ドメインかつhttps接続を用いるWebサービスのクラスタを構築したのでその手順をメモしておきます。なお、環境はGoogle Kubernetes Engine、作業はCloud Shell上にて行っています。
 

目次

 

達成出来ること

  • 一つのIPアドレスで複数のドメインを管理し、かつ各々がhttps接続可能な状態にする
  • TLS証明書の自動取得、及び自動更新を行う

 

Kubernetes クラスタの作成

GCPのWebUIよりKubernetes クラスタの作成を行います。
 
「+ クラスタを作成」→「標準クラスタ」を選択

 
任意のクラスタ名、リージョン or ゾーンを設定します。
マスターのバージョンはデフォルトで「1.9.7-gke.7」が選択されていますが、こちらを最新のバージョンに変更します。

※1.9.7に搭載されているGLBCはIngressのSNI構成を正常に反映してくれないバグが存在しています。
※執筆時最新バージョン「1.11.2-gke15」

 

 
アプリケーションの動作に必要な処理能力を持つノードプールを作成します。

 
ここではクラスタのワーカーノードの個数を指定します。
Google Kubernetes Engine ではマスターノードの存在は無視(隠蔽)されるため、純粋に必要なワーカーノードのみを準備すればOKです。
画像の例ではゾーン毎に1ノード、計3個のワーカーノードを指定しています。
 
設定が完了したら、画面下部の「作成」を押下して完了です。
クラスタの作成には数分かかるため、完了するまで待機します。
 

Cloud Shell からクラスタに接続

Cloud Shell を起動し、作成したクラスタへ接続します。
まず、作成したクラスタの右にある「接続」を押下します。

 
Cloud Shell が起動するのと同時に、クラスタへの接続コマンドが入力された状態になるので、そのままエンターを押せば接続が完了します。
試しに下記コマンドを入力して、正常にクラスタへ接続出来ているか確認します。

kubectl get nodes


 
作成したノードが全て取得出来れば正常です。
 

クラスタにHelm をインストール

HelmはKubernetes のパッケージマネージャーです。
※後ほどこれを用いてクラスタにcert-manager をインストールします。
 
こちらを参考にHelm をインストールします。
 

Helm のインストール手順

  • 公式Github のリリースページより最新のバイナリを取得します。
  • パッケージを解凍します。
    • tar -zxvf helm-v2.11.0-linux-amd64.tar.gz
  • バイナリをパスが通った場所へ移動します。
    • sudo mv linux-amd64/helm /usr/local/bin/helm

※参考link
 
Helm はKubernetesクラスタ上にTillerと呼ばれるサービス群を必要とするため、そのインストールを行います。
 
ネームスペースkube-systemtillerという名前のサービスアカウントを作成

kubectl create serviceaccount -n kube-system tiller

 
作成したサービスアカウント(tiller) にcluster-adminの権限を与える

kubectl create clusterrolebinding tiller-binding \ 
    --clusterrole=cluster-admin \ 
    --serviceaccount kube-system:tiller

 
Tiller をクラスタへインストール

helm init --service-account tiller

 
Tiller のPod 起動後、リポジトリをアップデート

helm repo update

 

クラスタにcert-manager をインストール

前項でインストールしたHelm を利用してcert-manager をインストールします。

helm install --name cert-manager --version v0.5.0 \ 
    --namespace kube-system stable/cert-manager

 

Let‘s Encrypt のセットアップ

環境変数にEmail をセットします。

export EMAIL=test@example.com

※証明書の有効期限通知等を受け取れるため、実在するメールアドレスが望ましいです。
 
ClusterIssuer を作成します。

curl -sSL https://rawgit.com/ahmetb/gke-letsencrypt/master/yaml/letsencrypt-issuer.yaml | \ 
    sed -e "s/email: ''/email: $EMAIL/g" | \ 
    kubectl apply -f-

 

アプリケーションのDeployment、Service を作成

今回はサンプルとして2つのDeployment、Service を準備しました。

# deployment-service.yaml

# Site 1
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: site1-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      run: site1-replica
  template:
    metadata:
      labels:
        run: site1-replica
    spec:
      restartPolicy: Always
      containers:
      - image: httpd
        name: site1
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: site1-service
spec:
  type: NodePort
  ports:
  - port: 8001
    targetPort: 80
    protocol: TCP
  selector:
    run: site1-replica
---
# Site2
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: site2-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      run: site2-replica
  template:
    metadata:
      labels:
        run: site2-replica
    spec:
      restartPolicy: Always
      containers:
      - image: httpd
        name: site2
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: site2-service
spec:
  type: NodePort
  ports:
  - port: 8002
    targetPort: 80
    protocol: TCP
  selector:
    run: site2-replica

 
作成します。

kubectl apply -f deployment-service.yaml

 

固定グローバルIPを取得し、Ingress を作成

固定IPを取得します。

gcloud compute addresses create example-ingress-ip --global

 
取得した固定IPは次のコマンドで確認出来ます。

gcloud compute addresses list --global

 
TLS通信を行わない形でIngress を作成します。

# ingress.yaml

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: example-ingress
  annotations:
    kubernetes.io/ingress.global-static-ip-name: example-ingress-ip
spec:
  rules:
  - host: site1.example.com
    http:
      paths:
      - path: /*
        backend:
          serviceName: site1-service
          servicePort: 8001
  - host: site2.example.com
    http:
      paths:
      - path: /*
        backend:
          serviceName: site2-service
          servicePort: 8002

 
※参考
 
作成します。

kubectl apply -f ingress.yaml

 

証明書の取得

証明書取得のためのマニフェストファイルを作成します。
 
今回の例ではsite1.example.comsite2.example.comの2つのドメインの証明書を取得したいため、2つのマニフェストを記載しています。

# certificate.yaml

apiVersion: certmanager.k8s.io/v1alpha1
kind: Certificate
metadata:
  name: cert-site1
spec:
  secretName: cert-site1
  issuerRef:
    name: letsencrypt-prod
    kind: ClusterIssuer
  commonName: site1.example.com
  dnsNames:
  - site1.example.com
  acme:
    config:
    - http01:
        ingress: example-ingress
      domains:
      - site1.example.com
---
apiVersion: certmanager.k8s.io/v1alpha1
kind: Certificate
metadata:
  name: cert-site2
spec:
  secretName: cert-site2
  issuerRef:
    name: letsencrypt-prod
    kind: ClusterIssuer
  commonName: site2.example.com
  dnsNames:
  - site2.example.com
  acme:
    config:
    - http01:
        ingress: example-ingress
      domains:
      - site2.example.com

 
マニフェストファイルを適用します。

kubectl apply -f certificate.yaml

この時マニフェストは、Ingress の設定にLet’s Encrypt 認証用の一時的なルーティングを動的に追加し、認証を通過させます。
※詳細なフローはこちらに掲載されています。
 
証明書の取得には数分から数十分かかるため、完了するまで待機します。
証明書の取得が完了すると、指定したsecretNameにてSecretが作成されます。
※今回はcert-site1及びcert-site2
 
証明書の取得完了後、次のコマンドにて作成されたSecret を確認することが出来ます。

kubectl get secret

 
このマニフェストの設定によって、証明書の自動更新も行われるようになります。
 

取得した証明書をIngress に設定

先程作成したIngress に対して作成した証明書を設定します。
ingress.yamlを次のように拡張します。

# ingress.yaml

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: example-ingress
  annotations:
    kubernetes.io/ingress.global-static-ip-name: example-ingress-ip
spec:
  rules:
  - host: site1.example.com
    http:
      paths:
      - path: /*
        backend:
          serviceName: site1-service
          servicePort: 8001
  - host: site2.example.com
    http:
      paths:
      - path: /*
        backend:
          serviceName: site2-service
          servicePort: 8002
  # GLBC supports SNI, after ver 1.1. Recommend to select GKE master ver 1.11.2-gke.9 and later.
  tls:
  - secretName: cert-site1
    hosts:
    - site1.example.com
  - secretName: cert-site2
    hosts:
    - site2.example.com

 
適用します。

kubectl apply -f ingress.yaml

 
Ingress の設定が適用されるまで数分から数十分かかるため待機します。
 

ドメインの向き先を取得した固定IPに設定

最後にsite1.example.comsite2.example.comの向き先を取得した固定グローバルIPに設定すれば完了です。
この段階でhttps://site1.example.comhttps://site2.example.comへの接続が可能となります。
 

おわりに

Kubernetes を用いることで得られるメリットは様々ありますが、その中の一つとしてスケーラビリティがあります。Helm、cert-manager を用いることで、高速かつ容易にTLS対応も行えるため、マイクロサービス時代のディファクトスタンダードとして、今後の強力な選択肢となりそうです。