回顾Dockerfile,我们在Dockerfile中通过CMD指令。比如我们需要一个docker执行sleep睡眠指令,我们需要在CMD中指定sleep命令。同时sleep指令要求传入一个时间参数
我们以shell形式或JSON数组格式传入,要求执行sleep 5秒钟
FROM ubuntu
# CMD sleep 5
CMD ["sleep","5"]
打包并执行
docker build . -t sleeper
docker image ls
docker run sleeper
docker执行sleep 5秒钟后退出。此处sleep 5 是hard code。如果我们想要覆盖sleep 5,就需要覆盖启动指令
docker run sleeper sleep 10
但这不是一个理想的设计,一个好的方案是我们只传入参数,而不是指令+参数,所以docker提供了ENTRYPOINT
docker run带来的额外输入
FROM ubuntu
ENTRYPOINT ["sleep"]
打包并执行
docker build . -t sleeper-2
docker image ls
docker run sleeper-2 5
但是如果忘记传入参数,就会触发报错
docker run sleeper-2
# sleep: missing operand
所以Docker将CMD接在ENTRYPOINT后面
FROM ubuntu
ENTRYPOINT ["sleep"]
CMD ["5"]
打包并执行
docker build . -t sleeper-3
docker image ls
docker run sleeper-3
docker run sleeper-3 2
docker tag sleeper-3 s09g/sleeper
docker push s09g/sleeper
回到kubernetes视角,如何pod文件中指定附加参数
docker文件有一个ENTRYPOINT和一个CMD指令。
apiVersion: v1
kind: Pod
metadata:
name: sleeper
spec:
containers:
- name: sleeper
image: s09g/sleeper
command: ["sleep"]
args: ["10"]
pod文件使用command字段覆盖了入口点ENTRYPOINT指令,args字段覆盖了docker文件中的命令CMD指令。
不是command字段覆盖了docker文件中的cmd指令
kubectl create -f sleep-pod.yml
kubernetes中要设置环境变量,需要使用env属性。
env是数组,因此env属性下的每个项目都以破折号开头,表示数组中的项目。
env:
- name: color
value: blue
- name: environment
value: prod
每个项目都有一个名称和一个值属性。name是随容器提供的环境变量的名称,value是它的值。
当有Pod文件时,管理存储在文件中的环境数据会很复杂。除了使用plain text键值对格式指定环境变量的直接方法,还有使用ConfigMap和Secrets等方法。
可以使用ConfigMap集中管理环境变量。
ConfigMap用于在Kubernetes中以键值对的形式传递配置数据。创建Pod时,将ConfigMap注入到Pod中,键值对可用作Pod中应用程序的环境变量。
配置ConfigMap涉及两个阶段:
命令式和声明式两种方法都可以创建ConfigMap。
使用命令式方法,可以在命令行中直接指定键值对ConfigMap。命令后跟配置名称和来自文本的选项。
from-literal选项用于在命令本身中指定键值对。
要添加多个键值对,需要多次指定from-literal选项。如果有太多的配置项时,命令行就很不合适
# kubectl create configmap --from-literal==
kubectl create configmap web-config --from-literal=UI_COLOR=red --from-literal=APP_MODE=prod
另一种方法是通过文件。使用from-file选项可指定包含所需数据的文件的路径。文件中的数据将被读取并存储在该文件的名称下。
vim config-map.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: web-config
data:
UI_COLOR: red
APP_MODE: prod
创建了一个定义文件
kubectl create -f config-map.yaml
kubectl get configmaps
kubectl describe configmaps
向Pod注入环境变量,要使用envFrom的新属性
envFrom属性是一个列表,可以根据需要传递任意多的环境变量。
列表中的每一项都对应于ConfigMap项,填入之前创建的ConfigMap的名称。
vim pod-sample-webapp.yaml
apiVersion: v1
kind: Pod
metadata:
name: sample-webapp
labels:
name: sample-webapp
spec:
containers:
- name: sample-webapp
image: sample-webapp
ports:
- containerPort: 8080
envFrom:
- configMapRef:
name: web-config
这是使用ConfigMap注入环境变量。
envFrom:
- configMapRef:
name: web-config
也可以作为单个环境变量注入
env:
- name: UI_COLOR
valueFrom:
configMapKeyRef:
name: web-config
key: UI_COLOR
也可以将整个数据作为文件注入到卷中
volumes:
- name: web-config-volume
configMap:
name: web-config
ConfigMap以纯文本格式存储配置数据。如果需要通过用户名和密码连接数据库,虽然可以将主机名和用户名移到ConfigMap中,但ConfigMap不是存储密码的正确位置。
Secrets用于存储密码或密钥等敏感信息。它类似于ConfigMaps,只是以编码格式存储。
与ConfigMaps完全相同,使用Secrets:
命令式和声明式两种方法都可以创建Secrets。
使用命令式方法,可以在命令行中直接指定键值对Secrets。命令后跟配置名称和来自文本的选项。
from-literal选项用于在命令本身中指定键值对。
要添加多个键值对,需要多次指定from-literal选项。如果有太多的配置项时,命令行就很不合适
# kubectl create secret generic --from-literal==
kubectl create secret generic web-secret --from-literal=DB_User=root --from-literal=DB_Password=passwd
Secret 对象分很多类,generic代表一般的机密信息。此外还有镜像仓库的认证信息,通信的证书和私钥等等类别
另一种方法是通过文件。使用from-file选项可指定包含所需数据的文件的路径。文件中的数据将被读取并存储在该文件的名称下。
Secrets用于存储敏感数据,并以编码格式存储。
如果以明文形式指定数据并不安全。因此,在使用声明性方法创建Secrets时,必须以编码格式指定Secrets值。
vim secret-data.yaml
apiVersion: v1
kind: Secret
metadata:
name: webapp-secret
data:
DB_User: cm9vdA==
DB_Password: cGFzc3dk
使用echo -n XXX | base64
命令将数据从纯文本转换为BASE64编码格式
echo -n "passwd" | base64
# cGFzc3dk
kubectl create –f secret-data.yaml
kubectl get secrets
kubectl describe secrets
kubectl get secret webapp-secret –o yaml
echo –n ‘cm9vdA==’ | base64 --decode
# root
get显示Secrets中的属性,但隐藏secret值本身。
需要编码值进行解码,使用之前相同base64命令.
向Pod注入Secrets,要使用envFrom属性。
列表中的每一项都对应于一个Secrets项,指定先前创建的Secrets的名称。
vim pod-sample-webapp.yaml
apiVersion: v1
kind: Pod
metadata:
name: sample-webapp
labels:
name: sample-webapp
spec:
containers:
- name: sample-webapp
image: sample-webapp
ports:
- containerPort: 8080
envFrom:
- secretRef:
name: webapp-secret
类似,除了使用secret注入环境变量。
envFrom:
- secretRef:
name: webapp-secret
也可以作为单个环境变量注入
env:
- name: DB_Password
valueFrom:
secretKeyRef:
name: webapp-secret
key: DB_Password
也可以将整个数据作为文件注入到卷中
volumes:
- name: webapp-secret-volume
secret:
secretName: webapp-secret
比较常见的是把secrets作为一个卷装在pod里。
secrets中的每个属性都创建为一个文件,其内容为secrets的值。
secret中有2个属性,创建2个文件,如果查看DB_Password文件的内容,可以看到其中的密码。
ls /opt/webapp-secret-volume
# DB_Host DB_Password DB_User
cat /opt/webapp-secret-volume/DB_Password
# passwd
文件名作为key,内容作为value
注意
之间,我们部署过简单的Web服务器来为提供服务。
这种工作负载将持续运行,直到手动终止。这样长时间运行服务一般用于在线业务。
但是还存在其他类型的工作负载,如批处理、生成报告和发送电子邮件, 执行特定任务,然后完成。这些工作负载的生存期很短,执行一组任务, 然后退出。这样短时间运行任务,一般作为离线业务。离线业务的特点是必定会退出,不会无期限地运行下去,所以它的调度策略与在线业务存在很大的不同,需要考虑运行超时、状态检查、失败重试、获取计算结果等管理事项。
离线业务也可以分为两种。一种是临时任务,运行完后退出;另一种是定时任务,可以按周期运行。临时任务就是 Kubernetes 里API 对象 Job,定时任务就是 API 对象 CronJob,。
回顾Docker作业,这样的工作负载在Docker中比较常见。
Docker容器启动,执行请求的操作, 打印输出,然后退出。
运行Docker ps命令时,会看到容器处于已退出状态。
docker run ubuntu expr 1 + 1
# 2
docker ps -a
# CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
# c9d7f24d3456 hello-world "/hello" 9 seconds ago Exited (0) 7 seconds ago agitated_burnell
执行的操作的返回代码显示在括号中。由于任务已成功完成, 因此返回代码为零。
回到Kubernetes,我们创建一个pod定义文件来执行相同的操作。
创建pod,运行容器, 执行计算任务并退出,pod进入已完成状态。
vim exprpod.yaml
apiVersion: v1
kind: Pod
metadata:
name: expr
spec:
containers:
- name: expr
image: ubuntu
command: ['expr', '3', '+', '2']
但kubernetes随后会重新创建容器,以尝试使其保持运行状态。容器再次执行所需的计算任务并退出,Kubernetes继续把它再次提出来。
Kubernetes希望应用程序永远存在。pod的默认行为是尝试重新启动容器以使其保持运行。
Pod上有个重新启动策略restartPolicy,默认情况下设置为Always。所以pod总是退出后重新创建。
我们可以将这个属性设定为Never或OnFailure,来覆写这个行为。这样, Kubernetes在Job完成后不会重新启动容器。
如果我们有一个大型数据集, 需要多个pod来并行处理数据。需要确保所有pod成功执行分配给它们的任务,然后退出。
我们从job定义文件开始
vim job.yaml
apiVersion: batch/v1
kind: Job
metadata:
name: expr-job
spec:
template:
spec:
restartPolicy: Never
containers:
- image: ubuntu
name: expr-job
command: ["expr"]
args: ["3", "+", "2"]
使用kubectl create命令创建Job,使用kubectl get jobs命令查看新创建的Job,Job已创建并成功完成。
kubectl apply -f job.yml
kubectl get job
# NAME COMPLETIONS DURATION AGE
# expr-job 1/1 7s 7s
kubectl get pod
# NAME READY STATUS RESTARTS AGE
# expr-job-kblhb 0/1 Completed 0 10s
kubectl logs expr-job-kblhb
# 5
kubectl delete job expr-job
运行kubectl get pod命令,查看创建的pod,可以看到它处于已完成状态,没有重新启动, 这表明Kubernetes没有尝试重新启动Pod。
运行kubectl logs命令以查看输出。
运行kubectl delete job命令,删除该Job。删除Job会导致删除该Job创建的pod。
为了运行多个pod,在spec下设置了一个完成值completions。
vim job.yaml
apiVersion: batch/v1
kind: Job
metadata:
name: expr-job
spec:
completions: 3
template:
spec:
restartPolicy: Never
containers:
- image: ubuntu
name: expr-job
command: ["expr"]
args: ["3", "+", "2"]
创建Job时,看到所需是3,成功是3。
kubectl apply -f job.yml
kubectl get jobs
# NAME COMPLETIONS DURATION AGE
# expr-job 3/3 11s 33s
kubectl get pods
# NAME READY STATUS RESTARTS AGE
# expr-job-7b52f 0/1 Completed 0 12s
# expr-job-bgpzv 0/1 Completed 0 16s
# expr-job-vlfp2 0/1 Completed 0 8s
如果Pod失败,为了有3个完成,Job会尝试创建新的pod,直到它有3个成功完成。
vim error-job.yaml
apiVersion: batch/v1
kind: Job
metadata:
name: error-job
spec:
completions: 3
template:
spec:
restartPolicy: Never
containers:
- image: s09g/random-error
name: error-job
kubectl apply -f error-job.yml
kubectl get job error-job
# NAME COMPLETIONS DURATION AGE
# error-job 2/2 47s 47s
kubectl get pods -w
默认情况下,将逐个创建Pod。第二个Pod仅在第一个Pod完成后创建。我们可以并行创建pod,而不是按顺序创建。
在Job spec规范中添加一个parallelism的属性。将其设置为3, 并行创建3个Pod。
vim error-job.yaml
apiVersion: batch/v1
kind: Job
metadata:
name: error-job
spec:
completions: 3
parallelism: 3
template:
spec:
restartPolicy: Never
containers:
- image: s09g/random-error
name: error-job
一般比较重要的字段包括:
CronJob是一个可以像Linux中的Crontab一样进行定时调度的作业。
vim cronjob.yaml
apiVersion: batch/v1
kind: CronJob
metadata:
name: hello
spec:
schedule: '*/1 * * * *'
jobTemplate:
spec:
template:
spec:
restartPolicy: Never
containers:
- image: busybox
name: hello
command: ["/bin/echo"]
args: ["hello", "world"]
CronJob定义有点复杂,必须小心。
有三个等级spec部分:一个用于CronJob,一个用于Job, 一个用于Pod。
kubectl apply -f cronjob.yml
kubectl get pod
# NAME READY STATUS RESTARTS AGE
# hello-27952293-j9b4w 0/1 Completed 0 11s
kubectl get cj
# NAME SCHEDULE SUSPEND ACTIVE LAST SCHEDULE AGE
# hello */1 * * * * False 0 24s 47s
微服务架构允许我们根据需要使用需要扩展、缩减以及修改每个服务, 而不是修改整个应用程序。
有时可能需要两个服务一起工作,例如Web服务器和日志记录服务。每个Web服务器实例都需要一个日志收集代理一组。但是二者具有独立的代码库,单独开发和部署。
多容器Pod
pod yaml文件中spec部分下的container部分是一个数组,允许一个pod中有多个container
vim myapp-pod.yml
apiVersion: v1
kind: Pod
metadata:
name: myapp-pod
labels:
app: myapp
type: myservice
spec:
containers:
- name: nginx
image: nginx
- name: log-agent
image: log-agent