5.应用管理

应用管理

容器传参

Docker

回顾Dockerfile,我们在Dockerfile中通过CMD指令。比如我们需要一个docker执行sleep睡眠指令,我们需要在CMD中指定sleep命令。同时sleep指令要求传入一个时间参数

我们以shell形式或JSON数组格式传入,要求执行sleep 5秒钟

  • JSON的第一位应该是可执行文件
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带来的额外输入

  • CMD会被覆盖
  • ENTRYPOINT 会附加在原有指令后面
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

kubernetes Pod

docker tag sleeper-3 s09g/sleeper

docker push s09g/sleeper

回到kubernetes视角,如何pod文件中指定附加参数

docker文件有一个ENTRYPOINT和一个CMD指令。

  • ENTRYPOINT是在启动时运行的命令
  • CMD是传递给该命令的default参数
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

环境变量

在yaml文件中定义环境变量

kubernetes中要设置环境变量,需要使用env属性。
env是数组,因此env属性下的每个项目都以破折号开头,表示数组中的项目。

env:
- name: color
  value: blue
- name: environment
  value: prod

每个项目都有一个名称和一个值属性。name是随容器提供的环境变量的名称,value是它的值。

当有Pod文件时,管理存储在文件中的环境数据会很复杂。除了使用plain text键值对格式指定环境变量的直接方法,还有使用ConfigMap和Secrets等方法。

ConfigMap

可以使用ConfigMap集中管理环境变量。

ConfigMap用于在Kubernetes中以键值对的形式传递配置数据。创建Pod时,将ConfigMap注入到Pod中,键值对可用作Pod中应用程序的环境变量。

配置ConfigMap涉及两个阶段:

  1. 创建ConfigMap
  2. 注入Pod

命令式和声明式两种方法都可以创建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

创建了一个定义文件

  • apiVersion为V1
  • kind为ConfigMap。
  • metadata下,为ConfigMap指定一个名称
  • 使用data,代替spec。采用键值格式。
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

Secrets

ConfigMap以纯文本格式存储配置数据。如果需要通过用户名和密码连接数据库,虽然可以将主机名和用户名移到ConfigMap中,但ConfigMap不是存储密码的正确位置。

Secrets用于存储密码或密钥等敏感信息。它类似于ConfigMaps,只是以编码格式存储。

与ConfigMaps完全相同,使用Secrets:

  1. 创建secrets
  2. 注入pod

命令式和声明式两种方法都可以创建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

注意

  1. Secrets没有加密。它们只是经过编码,任何人都可以获取Secrets对象,然后解码查看Secrets数据。所以不要把secrets文件和代码一起推送到GitHub。
  2. etcd中的secrets没有加密。默认情况下etcd中的数据都不加密。需要额外启用静态加密。
  3. 任何能够在同一名称空间中创建pod或deployment的人都可以访问Secrets。如果别的用户使用了同名的Secrets,就能够看到pod上装载的Secrets对象。

Job

之间,我们部署过简单的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"]
  • API版本为batch/v1
  • kind是Job
  • 在Spec下有template。将pod中的所有内容都移到了template下

使用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

一般比较重要的字段包括:

  • activeDeadlineSeconds,设置 Pod 运行的超时时间
  • backoffLimit,设置 Pod 的失败重试次数
  • completions,Job 完成需要运行多少个 Success Pod,默认是 1 个
  • parallelism,与 completions 相关,表示允许并发运行的 Pod 数量,避免过多占用资源。

CronJob

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"]
  • API版本为batch/v1beta1。
  • kind是CronJob
  • spec下,
    • schedule选项采用类似Cron的格式字符串,可以在其中指定作业的运行时间。
    • jobTemplate作业模板,是应该运行的实际作业。job定义的spec中的所有内容都在这里

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

多容器Pod

微服务架构允许我们根据需要使用需要扩展、缩减以及修改每个服务, 而不是修改整个应用程序。
有时可能需要两个服务一起工作,例如Web服务器和日志记录服务。每个Web服务器实例都需要一个日志收集代理一组。但是二者具有独立的代码库,单独开发和部署。

多容器Pod

  • 共享同一Pod生命周期, 一起创建和销毁。
  • 共享相同的网络空间,可以通过localhost相互访问,
  • 共享存储卷。

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

你可能感兴趣的:(kubernetes)