(GKE)KubernetesにおけるProduction環境構築 (II): ACMEv2におけるCert-Managerの設定



背景：　GKEのProduction環境でのIngressはTLSの認証書を発行し、自動更新したい

必要な環境変数:

``` 
export GCLOUD_PROJECT="$(gcloud config get-value project)"
export MY_DOMAIN=example.com 
export CERT_MANAGER_GCP_SA=cert-manager
export CERT_MANAGER_GKE_SA=clouddns-service-account
export CERT_MANAGER_NS=cert-manager
export ACME_EMAIL=admin@example.com
export CERTIFICATE_NAME=example-com
export CLUSTER_ISSUER_NAME=letsencrypt-prod
```
手順:

#####I. Google Account と GKE Secretの設定
 1. CloudDNS　にて　```${MY_DOMAIN}``` のA recordを設定する
 2. GCPのIAMにて```${CERT_MANAGER_GCP_SA}```と呼ばれるService Accountを作る
 3. kubectlで```${CERT_MANAGER_GKE_SA}```と呼ばれるsecretを作る.**このsecretのNamespace必ずcert-manager自体のNamespace ```${CERT_MANAGER_NS}```一致することが重要！**

#####II.Cert-Manager 自体をinstallする
#####III.Certificateを生成する為にClusterIssuer を作る
#####IV. Certificateを発行する.    
  (**```spec.issuerRef.kind ```を指定しないと ```${CLUSTER_ISSUER_NAME}```のcluster issuerが見つからない。**)

===================================================================

I. Google Account と GKE Secretの設定


```
gcloud iam service-accounts create ${CERT_MANAGER_GCP_SA} --display-name "cert-manager"
gcloud projects add-iam-policy-binding ${GCLOUD_PROJECT} --member serviceAccount:${CERT_MANAGER_GCP_SA}@${GCLOUD_PROJECT}.iam.gserviceaccount.com --role roles/dns.admin
gcloud iam service-accounts keys create ${CERT_MANAGER_GCP_SA}-key.json --iam-account ${CERT_MANAGER_GCP_SA}@${GCLOUD_PROJECT}.iam.gserviceaccount.com

####The secret namespace must be the same as cert-manager#####
kubectl create secret generic ${CERT_MANAGER_GKE_SA} --from-file=${CERT_MANAGER_GCP_SA}-key.json=${CERT_MANAGER_GCP_SA}-key.json -n ${CERT_MANAGER_NS}
```

II. Cert-Manager 自体をinstallする

```
export VER_CERT_MANAGER=v0.7.0

# Install the CustomResourceDefinition resources separately
kubectl apply -f https://raw.githubusercontent.com/jetstack/cert-manager/release-0.7/deploy/manifests/00-crds.yaml

# Create the namespace for cert-manager
kubectl create namespace ${CERT_MANAGER_NS}

# Label the cert-manager namespace to disable resource validation
kubectl label namespace ${CERT_MANAGER_NS} certmanager.k8s.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 \
  --name cert-manager \
  --namespace ${CERT_MANAGER_NS} \
  --version ${VER_CERT_MANAGER} \
  jetstack/cert-manager



######## Verify the installation(Optional)######## 
# Create a ClusterIssuer to test the webhook works okay
cat <<EOF > test-resources.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: cert-manager-test
---
apiVersion: certmanager.k8s.io/v1alpha1
kind: Issuer
metadata:
  name: test-selfsigned
  namespace: cert-manager-test
spec:
  selfSigned: {}
---
apiVersion: certmanager.k8s.io/v1alpha1
kind: Certificate
metadata:
  name: selfsigned-cert
  namespace: cert-manager-test
spec:
  commonName: example.com
  secretName: selfsigned-cert-tls
  issuerRef:
    name: test-selfsigned
EOF

# Create the test resources
kubectl apply -f test-resources.yaml
kubectl describe certificate -n cert-manager-test
kubectl delete -f test-resources.yaml
################################################  
```

III. Certificateを生成する為に[Cluster Issuer](http://docs.cert-manager.io/en/venafi/getting-started/3-configuring-first-issuer.html)の作成

```
######Create ClusterIssuer######
kubectl apply -f - << EOF
apiVersion: certmanager.k8s.io/v1alpha1
kind: ClusterIssuer
metadata:
  name: ${CLUSTER_ISSUER_NAME}
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    # Email address used for ACME registration
    email: ${ACME_EMAIL}
    # Name of a secret used to store the ACME account private key
    privateKeySecretRef:
      name: letsencrypt-prod-pk
    # Enable the HTTP-01 challenge provider
    http01: {}
    # ACME dns-01 provider configurations
    dns01:
      # Here we define a list of DNS-01 providers that can solve DNS challenges
      providers:
      - name: prod-clouddns
        clouddns:
          # A secretKeyRef to a the google cloud json service account
          serviceAccountSecretRef:
            name: ${CERT_MANAGER_GKE_SA}
            key: ${CERT_MANAGER_GCP_SA}-key.json
          # The project in which to update the DNS zone
          project: ${GCLOUD_PROJECT}
EOF

kubectl describe ClusterIssuer

```



IV. Certificateを発行する([Let’s EncryptのACME v2](https://letsencrypt.org/docs/client-options/))

```
######Create Certificate Using ClusterIssuer######
kubectl apply -f - <<EOF
apiVersion: certmanager.k8s.io/v1alpha1
kind: Certificate
metadata:
  name: ${CERTIFICATE_NAME}
  namespace: default
spec:
  secretName: cert-manager-tls
  issuerRef:
    name: ${CLUSTER_ISSUER_NAME}
    #The kind should be refered explicitly
    kind: ClusterIssuer
  commonName: ${MY_DOMAIN}
  dnsNames:
  - ${MY_DOMAIN}
  acme:
    config:
    - dns01:
        provider: prod-clouddns
      domains:
      - ${MY_DOMAIN}
EOF

kubectl describe certificate
kubectl get clusterissuer,certificate,order,challenge --all-namespaces=true
```

非常にいい参考になるページ
1.[Cert-Manager](http://docs.cert-manager.io/en/latest/)
2.[Cluster Issuer](http://docs.cert-manager.io/en/venafi/getting-started/3-configuring-first-issuer.html)
3.[GKE で TLS 証明書を自動管理(cert-manager DNS-01 編)](https://qiita.com/apstndb/items/3a39a1e6acacbbc30765)
4.[Let’s EncryptのACME v2](https://letsencrypt.org/docs/client-options/)