【编者的话】这是介绍Kubernetes的第三篇,主要集中讲述如何配置Kubernetes集群以及做者在配置过程当中遇到的问题。html
在本系列文章的第一部分中,咱们探讨了容器、Docker以及这些技术如何从新定义行业中的基础设施及其运营方式。第二部分文章则继续讨论,着眼点放在Kubernetes身上——包括Kubernetes是什么以及拥有哪些能力。在今天的第三部分文章中,我将进一步阐述如何上手Kubernetes,同时提供与结构设计相关的一点参考意见。github
Kubernetes的集群管理是经过kubectl
命令进行的,若是你使用Google Cloud,则会在SDK中自动安装kubectl
命令行工具。虽然你能够彻底使用这个命令行工具来配置、控制Kubernetes集群,可是我仍是十分推荐你使用单独的配置文件来配置集群,由于你可使用版本控制工具来追踪每次对集群配置的修改并且保证配置的统一,后面会着重介绍如何去作。web
kubectl
命令集合包含了很是多的子命令来帮助你控制Kubernetes集群的方方面面,如窥探当前集群的工做状态等等。下面是我以为很是经常使用的几个命令,并将其分类说明。redis
kubectl apply -f service-file.yml
apply
命令会收集配置文件(service-file.yml)中全部的配置项信息,并自动将配置文件中的配置项跟当前集群的运行配置作对比,而后自动将必要的更新应用到当前集群中。kubectl rollout
全部经过apply
命令触发的指令都会生成一个新的Rollout对象。经过使用kubectl rollout status
命令能够查看最近的更新状态、终止一个rollout或者回滚到上一个资源对象(Deployment,Pod,Service等)的版本。docker
kubectl describe [pod,service,...] [resource]
这条命令能够查看某个资源的详细信息,当错误发生的时候你必须首选考虑使用describe
命令来获取错误描述信息。kubectl logs [resource]
Kubernetes内的容器会倾向于把全部的日志定向到STDOUT
上,kubectl logs -f
命令能让你获取一个资源(Pod/Container)最新的日志。kubectl get [pods,deployments,services,...]
这条命令会将Kubernetes默认命名空间中运行的资源信息打印出来,若是要看特定命名空间的资源信息,则必须加上--namespace=[my namespace]
参数,若是查看全部命名空间的资源信息,则使用--all-namespace
参数。kubectl exec
这条命令是对docker exec
命令的包装,可让你执行容器内部的命令,若是pod中只有一个容器在运行则此命令能够做用在pod上如:kubectl exec -it [pod] /bin/bash
。咱们可使用kubectl exec -it [pod name] --/bin/bash
来进入一个运行中的pod,-i
用来启动标准输入流STDIN
,-t
将输入流定向到TTY(伪终端)中,这样就能模拟终端的bash命令操做了。固然若是一个Pod中启动了多个容器,你可使用-C[container-name]
参数来进入特定的容器中。数据库
当前尚未命令可以让一个Deployment自动将配置的Pod下的容器更新到最新的版本;可是为Kubernetes集群更新容器版本又是一个很是常见的操做,这时你就必须想清楚更新容器的步骤了,我列出如下几种方法:json
:latest
的Tag,而后手动删除运行中的Pod,而后Deployment会自动用最新的容器从新启动Pod; apply
这个配置文件到Kubernetes的集群中,这种方式须要对配置文件进行版本控制;kubectl set image deployment/[service name] *=[new image]
。我须要的方案是即可以在Kubernetes中留下更改历史,也不想由于总是去提交配置文件的更改致使陷入版本混乱的泥沼。因此,我我的选择第三种方案,我是这么操做的:api
:latest
tag;:latest
tag之外还要打上与代码git仓库HEAD指针指向的Commit号相同的tag(如:9c713a);kubectl set image deployment/[service name] *=[new image]
时填入git commit hash号标识的镜像。根据以上几点,我能够得到一些好处:缓存
set image
命令来更新Pods的镜像很是的优雅,由于可使用kubectl rollout status
来跟踪全部Deployment的Pods的更新状态;:latest
tag的镜像(注:在测试环境或者非生产环境使用git commit hash号来启动容器,可是在生产时使用:latest
,由于做者在push镜像时同时打了两个tag)。Kubernetes开发人员不只提供了至关全面的文档能够参考,并且提供了minikube这个可让开发者在本地环境运行Kubernetes集群的工具。minikube能够运行在多种不一样的虚拟化环境下,它能够很容易的启动一个全功能的,包含一个单一节点的Kubernetes集群。当集群启动后,minikube提供了多种命令来访问与窥探集群运行状况,同时kubectl工具也是自动为minikube配置好的。最经常使用的命令莫过于:minikube service [service name] --url
这条命令可以打印出本地集群中配好的Service的访问url地址。minikube dashboard
这会跳转到一个web-based的集群看板页面,帮助你经过可视化的方式了解集群运行的情况。
同时我建议将运行minikube的默认的VM配置加高一点,好比配置:4CPUs与8GB内存,使用VMWare Fusion 做为VM容器,例如可使用以下命令:minikube config set cpus 4 minikube config set memory 8192 minikube start --vm-driver vmwarefusion
若是要让kubectl退出对minikube的访问,必须从新配置kubectl让它链接到新的集群;以下命令:kubectl config get-contexts kubectl config use-context [cluster context name from above]
固然,若是要从新链接到本地的minikube,只须要使用这条命令:kubectl config use-context minikube
Service的配置文件支持JSON与YAML,可是我推荐使用YAML,由于它比较容易读写,并且支持注释,这对那些复杂的结构很是管用。
对于集群的配置,Service每每是最早配置的,这就是所谓的Service-first配置法。具体的作法是,为每一个要建立的服务分配一个单独的文件夹,这个文件夹中包含了全部启动这个服务的配置文件。为了搜索方面,我建议为每一个Kubernetes的Service资源建立一个yaml配置文件,能够这样命名:[service name]-k8s.yml
,这个配置文件中写入全部这个Service在Kubernetes环境启动的配置项。
以下是我配置一个Redis缓存服务的目录结构:
redis-cache/
Dockerfile
redis.conf
redis-cache-k8s.yml复制代码
如下是服务配置文件redis-cache-k8s.yml:
```apiVersion: v1
kind: Service
metadata:
name: redis-cache
labels:
role: cache
spec:
type: NodePort
ports:
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: redis-cache
spec:
revisionHistoryLimit: 3
replicas: 1
template:
metadata:
labels:
role: cache
spec:
containers:
- name: redis
image: redis:3
resources:
requests:
cpu: 100m
memory: 1Gi
ports:
- containerPort: 6379 ```复制代码
而后经过运行apply -f service/redis-cache/redis-cache-k8s.yml
命令就能让redis-cache服务在整个Deployment中跑起来。
我如今为每一个容器都打两个tag分别是::latest
与对应代码git库中的HEAD指针Commit的hash值如::9c713a
。不少人强烈推荐不要使用:latest
tag,缘由在这里(注:内容大概讲docker registry并不会主动判断一个镜像是不是最新的,而是能够人为的随意为一个老的镜像打上latest tag的,因此不少人认为latest标签的镜像其实并不必定是最新的),可是我以为这些都是讲给那些只用latest标签的人听的。我为何这么作的缘由在上面“部署新镜像”一节已经阐述。
我早期使用minikube遇到的问题是如何让个人Pod有权限从个人Google私有镜像仓库中拉取docker镜像。固然,当我在GKE中使用Kubernetes集群拉取镜像是没有问题的,由于当使用GKE时,全部的服务器都自动被赋予了访问Google私有镜像仓库的的权限,可是做为本地运行的minikube就没有权限了。
解决方法是在Pod的spec节中使用imagePullSecrets
值。首先,登陆Google Cloud,而后前往IAM并建立一个新的Service Account
并赋予Storage -> Storage Object Viewer
权限,确保勾选“Furnish a new private key”选项,完过后会给你一个JSON文件,这个文件你须要本地保存,是用于受权的;全部这些准备就绪后运行这段脚本生成一个新的Secret资源:
`#!/usr/bin/env sh
SPATH="$(cd $(dirname "$0") && pwd -P)"
SECRET_NAME=${1:-docker-registry-secret}
CONFIG_PATH=${2:-$SPATH/localkube.json}
if [[ ! -f $CONFIG_PATH ]]; then
echo "Unable to locate service account config JSON: $CONFIG_PATH";
exit 1;
fi
kubectl create secret docker-registry $SECRET_NAME \
--docker-server "gcr.io" \
--docker-username _json_key \
--docker-email [service account email address] \
--docker-password="cat $CONFIG_PATH
" ${@:3} 这段脚本会生成一个名为
docker-registry-secret的Secret资源,这个资源稍后要在Service的配置文件中的
imagePullSecrets值中被引用,以下所示:
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: redis-cache
spec:
...
template:
spec:
imagePullSecrets:
- name: docker-registry-secret
...
containers:
- name: redis
image: gcr.io/[google account id]/redis-cache:latest复制代码
...`
配置完成后应该就能从Google私有镜像仓库中拉取镜像了。
敏感的数据好比:密码,认证码与各类key,这些都是要作特殊处理并很容易出错的数据。首先,若是将这些数据不加密的保存在代码管理仓库中,无论这个仓库多么机密,并非一个保险的作法;但同时,你又必须把这些数据加密并保存在某个安全的地方,并使用一个跟踪系统对其作版本记录与权限控制。我使用了不少不一样的加密、保存方式,发现StackExchange的BlackBox很是好用。
BlackBox使用PGP/GPG(非对称加密)加密方式对文件进行加密,确保只有特定的用户才能访问加密的文件。对一个用户受权访问某个资源只需这个用户提供本身的GPG公钥,而移除一个用户的受权只须要在一些配置文件中将其名字去掉便可;而后你要作的事就是告诉BlackBox哪些文件须要加密,而其他的工做交给BlackBox便可,被加密的配置文件能够放心的放入git仓库了。
将这些加密的信息提供给Kubernetes须要一些额外的本地脚本,由于Kubernetes将配置信息以明文的方式存储在etcd中。好比:我会对两类文件进行加密:一、YAML配置文件,内部包含不少key-value形式保存的敏感信息(如:数据库密码等);二、一些公钥文件如:SSL Certificate或者其余秘钥。而后,我使用rake来解密这些文件,而后将解密的文件应用到Kubernetes集群中,使用命令:apply -f -
(注意:'-'表示STDIN)。
好比,我有个加密的YAML文件,以下:--- rails: secret_key_base: "..." service_api_key: "..." database: username: "..." password: "..."
而后导入Kubernetes的Secret:raw_secrets =
blackbox_cat secrets/my-secrets.yml.gpg`
secrets = YAML.load(raw_secrets)
secrets.each do |name, values|
k8s_secret = {
"apiVersion" => "v1",
"kind" => "Secret",
"type" => "Opaque",
"metadata" => { "name" => name },
"data" => {},
}
values.each do |key, value|
k8s_secret["data"][key] = Base64.strict_encode64(value)
end
stdout, status = Open3.capture2("kubectl apply -f -", stdin_data: k8s_secret.to_yaml)
end 而后在Service的配置文件中来引用这些Secret(使用Secret名称,我一般使用环境变量来指定):
...
env:
name: rails
key: secret_key_base复制代码
name: rails
key: service_api_key复制代码
name: database
key: username复制代码
name: database
key: password`复制代码
我认为这是我使用、配置Kubernetes过程当中遇到的主要问题,但愿对您有帮助。原文连接:CONTAINERS, DOCKER, AND KUBERNETES PART 3(翻译:肖劲)