0%

最近在公司部分服务往 k8s 迁移的工作中,有个服务第一次镜像打包并在 k8s 中部署后,发现在访问 Apple Push Server(https://api.push.apple.com)会报证书验证错误: not a valid x509 certificate

心想,大厂 Apple 不至于证书配置错误吧,赶紧浏览器访问下,确实一切正常.

alt ApplePushServerCert

联想到这个服务原来还是跑在一台比较古老的 EC2 上,以及最近 golang 服务因为 k8s dns 问题,基础镜像均使用了 Debian,于是怀疑是不是最近 Debian 证书更新导致了某些 CA ROOT 被移除,Google 了下,果然发现,2020.06 的证书更新中,移除了某些 CA ROOT: ca-certificates: Removal of GeoTrust Global CA requires investigation.

从图中的 Apple Api Push 域名的证书链中,能发现 Apple IST CA2 - G1 证书由 GeoTrust Global CA 交叉签名出来的 CA,而 GeoTrust Global CA 属于 Symantec,Symantec 在 2018 年由于不规范签发证书,被 Google 以及 Mozilla 等在 2018 年就决定被不信任。

和浏览器不一样,主流 OS 仅维护一个ca-certificates包,并没有什么证书白名单等处理,因此当 Debian 移除了部分 CA 后,程序访问相关证书的网站会直接报错。

由于 Apple 也在慢慢淘汰 Symantec 系列的 CA,因此,本质上问题还是 Apple Push 不应该使用 GeoTrust ROOT CA 包括其交叉签名出的 ROOT CA 所签发出的证书,而既然 Apple 已经这(dian)么(da)做(qi)了(ke),当然选择兼容他啦(如同主流浏览器白名单)!

解决方案

最佳实践当然是和 Mozilla 一样,做个证书白名单,但是稍微麻烦点。Symantec 随便不被信任了,但是也不至于乱签发证书以及挺多厂商可能也没来得及换,所以直接重新信任GeoTrust Global CA也是可以的。

这里以基于 Debian 的镜像处理为例:

1
2
3
RUN mkdir /usr/local/share/ca-certificates/geo-trust-global-ca && \
wget -P /usr/local/share/ca-certificates/geo-trust-global-ca/root.crt https://www.geotrust.com/resources/root_certificates/certificates/GeoTrust_Global_CA.pem && \
update-ca-certificates

Ref

Terraform

Terraform 是 HashiCorp 公司旗下的 Provision Infrastructure 产品, 是 AWS APN Technology Partner 与 AWS DevOps Competency Partner。Terraform 是一个 IT 基础架构自动化编排工具,它的口号是“Write, Plan, and Create Infrastructure as Code”, 是一个“基础设施即代码”工具。

Terraform VS Yaml

相比于直接使用 Yaml 文件管理 k8s 集群,虽然 yaml 文件也能版本控制,但是可能会存在如下问题:

资源删除/重命名 后,该如何自行执行更新?

以删除资源为例,可能需要通过执行 kubectl delete -f xxx.yaml 来执行删除,而使用 terraform 管理的话,就很简单: terraform apply.

初始化

配置 Terraform backend

为了方便多人协作以及 CI,这里推荐使用 S3 作为 terraform backend,其他 backend 可以查看官方文档.

以下配置包含了使用 aws s3 作为 state 的存储以及使用 dynamodb 作为 lock.

1
2
3
4
5
6
7
8
9
10
terraform {
backend "s3" {
bucket = "bucket"
key = "k8s/terraform.tfstate"
region = "ap-northeast-1"
dynamodb_table = "k8s"
profile = "profile"
}
}

配置 k8s provider

k8s provider 配置比较简单,只需要配置 kube config 的路径:

1
2
3
provider "kubernetes" {
config_path = "~/.kube/xxx"
}

如果需要同时管理 Helm,可以同时添加配置:

1
2
3
4
5
provider "helm" {
kubernetes {
config_path = "~/.kube/xxx"
}
}

管理资源

通过 Terraform 管理 k8s 资源,在官方文档已经很详细了,这里我列举几个常见用法.

Secret

以 secret 资源为例,常常需要配置不少不适合提交到代码仓库中的敏感信息,通过 Terraform 我们可以通过参数的形式传入变量,将敏感信息保存在执行跳板机/运维人员本地,来避免提交敏感信息到代码仓库。

以下为配置案例,首先,定义变量:

1
2
3
variable "AWS_SECRET_KEY" {
description = "AWS_SECRET_KEY"
}

在使用 secret 的地方引用变量:

1
2
3
4
5
6
7
8
9
10
11
12
13
resource "kubernetes_secret" "aws" {
metadata {
name = "aws"
namespace = "prod"
}

data = {
AWS_ACCESS_KEY = var.AWS_ACCESS_KEY
AWS_SECRET_KEY = var.AWS_SECRET_KEY
}

type = "Opaque"
}

通过文件存储变量,创建文件/etc/terraform/prod.tfars 并包含如下信息:

1
AWS_SECRET_KEY="secret_key"

执行的时候,可以通过以下命令传入变量:

1
terraform apply -var-file=/etc/terraform/prod.tfars

Config Map 中的文件引用

Config Map 可以用来存储文件信息,并用于 Pod 的 Volume,在 Terraform 我们可以通过直接引用文件的形势,方便的管理 Config Map:

1
2
3
4
5
6
7
8
9
10
resource "kubernetes_config_map" "xxxsrv-conf" {
metadata {
name = "xxxsesrv"
namespace = "prod"
}

data = {
"service.conf" = "${file("data/configmap/xxxsrv.conf")}"
}
}

Helm

在 Terraform 管理 helm 也是很方便,以安装 cert-manager 为例,我们能通过在 Terraform 中声明好 Helm Chart 的版本,value.yaml 等信息,来方便的管理 helm 资源。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
resource "helm_release" "cert-manager" {
repository = "https://charts.jetstack.io"
name = "cert-manager"
chart = "cert-manager"
namespace = "cert-manager"

version = "v1.0.0-alpha.1"

values = [
"${file("data/cert-manager.yaml")}"
]

}

CI/CD 中使用 Terraform

得利于我们将 Terraform 的 state 信息存放于 s3 等 remote backend, 我们可以方便的在本地以及 CI 中执行 terraform 命令。

由于 secret 存放于执行跳板机,以 Gitlab CI Runner 为例,我们推荐在专门的跳板机上配置好相关 tfvars 文件,并将相关 runner 用于对应的 Terraform 仓库。

完整的.gitlab-ci.yml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# Official image for Hashicorp's Terraform. It uses light image which is Alpine
# based as it is much lighter.
#
# Entrypoint is also needed as image by default set `terraform` binary as an
# entrypoint.
image:
name: registry.gitlab.com/gitlab-org/gitlab-build-images:terraform
entrypoint:
- "/usr/bin/env"
- "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"

# Default output file for Terraform plan
variables:
PLAN: plan.tfplan
JSON_PLAN_FILE: tfplan.json
ARGS: -var-file=/etc/terraform/prod.tfvars

cache:
paths:
- .terraform

before_script:
- shopt -s expand_aliases
- alias convert_report="jq -r '([.resource_changes[]?.change.actions?]|flatten)|{\"create\":(map(select(.==\"create\"))|length),\"update\":(map(select(.==\"update\"))|length),\"delete\":(map(select(.==\"delete\"))|length)}'"
- terraform --version
- terraform init

stages:
- validate
- build
- test
- deploy

validate:
stage: validate
tags:
- ops
- terraform
script:
- terraform validate

plan:
stage: build
script:
- terraform plan $ARGS -out=$PLAN
- terraform show --json $PLAN | convert_report > $JSON_PLAN_FILE
tags:
- ops
- terraform
artifacts:
name: plan
paths:
- $PLAN
reports:
terraform: $JSON_PLAN_FILE

# Separate apply job for manual launching Terraform as it can be destructive
# action.
apply:
stage: deploy
environment:
name: production
script:
- terraform apply -input=false $PLAN
tags:
- ops
- terraform
dependencies:
- plan
when: manual
only:
- master

缺陷

Terraform 目前还不支持 CRD,虽然官方 Blog有提到在 alpha 测试,但是距离 GA 还有一段时间吧~

由于 Ghost 一些沙雕问题,最终,我还是决定尝试下静态博客,选择了 Hexo.

托管于 Github/Gitlab,并使用 Gitlab CI 自动打包 Push 到 S3.

阅读全文 »

作为容器编排的事实标准,Kubernetes 不管是生产环境还是测试环境都很适合,然而个人用的话,资源占用是一个很大的问题,2c4g *3 的机器配置刚好够跑个 Master 节点。

阅读全文 »