目录

Kubernetes的安装和使用(一)
Kubernetes的安装和使用(二)
Kubernetes的安装和使用(三)

k8s的使用

构建和运行镜像

编写一个go程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package main

import (
"io"
"log"
"net/http"
)

func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
io.WriteString(w, "[v1] Hello, Kubernetes!")
})

log.Printf("v1 access http://localhost:3000\n")
panic(http.ListenAndServe(":3000", nil))
}

编写Dockerfile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 引入golang的环境,并设置别名
FROM golang:1.20-alpine AS builder
# 工作目录
WORKDIR /project
# 把当前文件夹的内容都添加到镜像的/project文件夹下
ADD . .
# 编译golang源代码
# 设置代理、操作系统、CPU架构、禁用cgo编译
# 设置mod为自动模式,防止因为没有设置go.mod而编译报错
RUN GOPROXY=https://goproxy.cn,direct GOOS=linux GOARCH=amd64 CGO_ENABLED=0 GO111MODULE=auto go build -o main -ldflags "-w -extldflags -static"

# 引入alpine系统环境
FROM alpine as prod
# 从上面的build镜像复制编译好的文件/project/main到当前目录
COPY --from=builder /project/main .
# 暴露3000端口
EXPOSE 3000
# 启动main程序
ENTRYPOINT ["/main"]

打包镜像

docker build . -t derobukal/hellok8s:v1

执行镜像,并把3000端口暴露出来

docker run --rm -p 3000:3000 derobukal/hellok8s:v1

之后可以用curl访问3000端口,结果正常显示了

~ curl 127.0.0.1:3000
[v1] Hello, Kubernetes!%   

之后可以把这个镜像推送到镜像仓库

docker login
docker push derobukal/hellok8s:v1

使用Pod

编写pod.yaml

1
2
3
4
5
6
7
8
9
10
11
apiVersion: v1
kind: Pod # 资源类型为pod
metadata:
name: go-http # 名称,需要在当前命名空间中唯一
labels:
app: go
version: v1
spec:
containers: # pod内的容器组
- name: go-http
image: derobukal/hellok8s:v1 # 镜像默认来源 DockerHub

之后创建pod

~ kc apply -f pod.yaml
pod/go-http created
~ kc get pods
NAME      READY   STATUS    RESTARTS   AGE
go-http   1/1     Running   0          65s

然后临时开启端口转发,就可以访问相应的服务了

~ kc port-forward go-http 3000:3000
Forwarding from 127.0.0.1:3000 -> 3000
Forwarding from [::1]:3000 -> 3000

~ curl http://127.0.0.1:3000
[v1] Hello, Kubernetes!%

在进行以上测试的时候,如果出现pod启动不了的情况,可能是因为防火墙的原因,可以开启网络代理之后再试。

使用Deployment

一般来说,pod不会被直接的使用,而是用Deployment来进行相关的操作。

编写deployment.yaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
apiVersion: apps/v1
kind: Deployment
metadata:
# deployment 唯一名称
name: hellok8s-go-http
spec:
replicas: 2 # 副本数量
selector:
matchLabels:
app: hellok8s # 管理template下所有 app=hellok8s的pod,(要求和template.metadata.labels完全一致!!!否则无法部署deployment)
template: # template 定义一组容器
metadata:
labels:
app: hellok8s
spec:
containers:
- image: derobukal/hellok8s:v1
name: hellok8s

部署deployment

~ kc apply -f deployment.yaml 
deployment.apps/hellok8s-go-http created
~ kc get deployments
NAME               READY   UP-TO-DATE   AVAILABLE   AGE
hellok8s-go-http   2/2     2            2           79s

kc get pod -o wide可以查看更详细的pod信息。我们可以把配置中的副本数改为3,之后重新执行部署命令,然后查看详细的pod信息如下,可以看到又新增了一个pod

NAME                                READY   STATUS    RESTARTS   AGE     IP           NODE       NOMINATED NODE   READINESS GATES
go-http                             1/1     Running   0          14m     10.244.0.6   minikube   <none>           <none>
hellok8s-go-http-6db476b8cb-4n2nx   1/1     Running   0          4m35s   10.244.0.7   minikube   <none>           <none>
hellok8s-go-http-6db476b8cb-s76jr   1/1     Running   0          4m35s   10.244.0.8   minikube   <none>           <none>
hellok8s-go-http-6db476b8cb-tjqs9   1/1     Running   0          8s      10.244.0.9   minikube   <none>           <none>

我们选取任意一个pod进行端口转发,随后请求可以看到结果正常

~ kc port-forward hellok8s-go-http-6db476b8cb-4n2nx 3000:3000 
Forwarding from 127.0.0.1:3000 -> 3000
Forwarding from [::1]:3000 -> 3000
Handling connection for 3000

更新deployment

我们将golang程序中的v1修改为v2,之后打包新版本镜像并上传

docker build . -t derobukal/hellok8s:v2
docker push derobukal/hellok8s:v2

之后我们修改deployment.yaml中的镜像为derobukal/hellok8s:v2,然后更新部署

~ kc apply -f deployment.yaml 
deployment.apps/hellok8s-go-http configured

可以看到pod都重新部署了

~ kc get pod -o wide                                         
NAME                               READY   STATUS    RESTARTS   AGE   IP            NODE       NOMINATED NODE   READINESS GATES
go-http                            1/1     Running   0          25m   10.244.0.6    minikube   <none>           <none>
hellok8s-go-http-8b44d58c5-5bmx7   1/1     Running   0          34s   10.244.0.12   minikube   <none>           <none>
hellok8s-go-http-8b44d58c5-djfcd   1/1     Running   0          35s   10.244.0.11   minikube   <none>           <none>
hellok8s-go-http-8b44d58c5-rt29z   1/1     Running   0          58s   10.244.0.10   minikube   <none>           <none>

之后选取一个Pod进行端口转发,并请求发现返回结果发生了变化

~ kc port-forward hellok8s-go-http-8b44d58c5-5bmx7 3000:3000 
Forwarding from 127.0.0.1:3000 -> 3000
Forwarding from [::1]:3000 -> 3000
Handling connection for 3000

~ curl http://127.0.0.1:3000
[v2] Hello, Kubernetes!%

回滚deployment

查看版本信息

~ kc rollout history deployment/hellok8s-go-http
deployment.apps/hellok8s-go-http 
REVISION  CHANGE-CAUSE
1         <none>
2         <none>

查看具体版本2

~ kc rollout history deployment/hellok8s-go-http --revision=2
deployment.apps/hellok8s-go-http with revision #2
Pod Template:
Labels:	app=hellok8s
    pod-template-hash=8b44d58c5
Containers:
hellok8s:
    Image:	derobukal/hellok8s:v2
    Port:	<none>
    Host Port:	<none>
    Environment:	<none>
    Mounts:	<none>
Volumes:	<none>

查看具体版本1

~ kc rollout history deployment/hellok8s-go-http --revision=1
deployment.apps/hellok8s-go-http with revision #1
Pod Template:
Labels:	app=hellok8s
    pod-template-hash=6db476b8cb
Containers:
hellok8s:
    Image:	derobukal/hellok8s:v1
    Port:	<none>
    Host Port:	<none>
    Environment:	<none>
    Mounts:	<none>
Volumes:	<none>

回退到版本1

~ kc rollout undo deployment/hellok8s-go-http --to-revision=1
deployment.apps/hellok8s-go-http rolled back

此时查看pod可以发现pod已经发生了改变,访问服务返回的也是v1版本信息了。

部署失败

我们构建一个如下的程序

1
2
3
4
5
package main

func main() {
panic("something went wrong")
}

之后打包一个新版本镜像:docker build . -t derobukal/hellok8s:v_error
并对这个镜像进行push:docker push derobukal/hellok8s:v_error

之后可以简单地使用命令而不修改deployment.yaml文件来重新部署deployment,通过命令修改镜像:

~ kc set image deployment/hellok8s-go-http hellok8s=derobukal/hellok8s:v2_error  
deployment.apps/hellok8s-go-http image updated
~ kc get pods
NAME                                READY   STATUS              RESTARTS        AGE
go-http                             1/1     Running             1 (7m34s ago)   54m
hellok8s-go-http-55669566cb-l69hx   0/1     ContainerCreating   0               41s
hellok8s-go-http-6db476b8cb-dlr2z   1/1     Running             1 (7m34s ago)   22m
hellok8s-go-http-6db476b8cb-glx7h   1/1     Running             1 (7m34s ago)   22m
hellok8s-go-http-6db476b8cb-sfxnr   1/1     Running             1 (7m34s ago)   22m

重新部署之后我们查看pod可以发现,之前的pod仍然在正常运行,而新启动的pod则处于ContainerCreating状态。我们可以直接回退到上一个版本

~ kc rollout undo deployment/hellok8s-go-http --to-revision=2
deployment.apps/hellok8s-go-http rolled back
~ kc get pods
NAME                                READY   STATUS        RESTARTS      AGE
go-http                             1/1     Running       1 (12m ago)   58m
hellok8s-go-http-55669566cb-l69hx   0/1     Terminating   0             5m17s
hellok8s-go-http-8b44d58c5-74mhw    1/1     Running       0             22s
hellok8s-go-http-8b44d58c5-hfpjj    1/1     Running       0             20s
hellok8s-go-http-8b44d58c5-lzwfm    1/1     Running       0             19s

存活探针

存活探针顾名思义就是用来检查进程的存活情况的,它支持多种方式,下面是一个例子

flat
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
package main

import (
"fmt"
"io"
"net/http"
"time"
)

func main() {
started := time.Now()
http.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) {
duration := time.Since(started)
if duration.Seconds() > 15 {
w.WriteHeader(500)
w.Write([]byte(fmt.Sprintf("error: %v", duration.Seconds())))
} else {
w.WriteHeader(200)
w.Write([]byte("ok"))
}
})

http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
io.WriteString(w, "[v2] Hello, Kubernetes!")
})
http.ListenAndServe(":3000", nil)
}

这个程序的/healthz接口会在启动15秒钟后开始持续报错,我们使用这个代码创建镜像derobukal/hellok8s:liveness。我们先使用正常的镜像创建depolyment,并设置存活探针配置

flat
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
apiVersion: apps/v1
kind: Deployment
metadata:
# deployment唯一名称
name: hellok8s-go-http
spec:
replicas: 2 # 副本数量
selector:
matchLabels:
app: hellok8s # 管理template下所有 app=hellok8s的pod,(要求和template.metadata.labels完全一致!!!否则无法部署deployment)
template: # template 定义一组pod
metadata:
labels:
app: hellok8s
spec:
containers:
- image: derobukal/hellok8s:v1
name: hellok8s
# 存活探针
livenessProbe:
# http get 探测指定pod提供HTTP服务的路径和端口
httpGet:
path: /healthz
port: 3000
# 3s后开始探测
initialDelaySeconds: 3
# 每3s探测一次
periodSeconds: 3

livenessProbe中我们设置使用httpGet的方式来检查程序的存活状态,并且接口为healthz。启动这个deployment,一切正常

~ kc get pods
NAME                               READY   STATUS    RESTARTS   AGE
hellok8s-go-http-cf86fc9d6-qjt57   1/1     Running   0          8s
hellok8s-go-http-cf86fc9d6-tnf9r   1/1     Running   0          8s

之后我们切换镜像为我们刚刚创建的derobukal/hellok8s:liveness镜像

kc set image deployment/hellok8s-go-http hellok8s=derobukal/hellok8s:liveness

然后可以看到pod会每隔一会儿重启一下

~ kc get pods
NAME                                READY   STATUS    RESTARTS      AGE
hellok8s-go-http-78fd694d74-j2kr6   1/1     Running   3 (23s ago)   2m41s
hellok8s-go-http-78fd694d74-j5d5x   1/1     Running   3 (19s ago)   91s

如果我们这时候再把镜像设置为v1,很快就可以看到pod恢复正常了。

就绪探针

就绪探针是用来检测程序是否已经成功启动的,例如程序是否可以接受前端的流量、是否已经可以执行相关的任务了等等。我们编写如下程序并创建derobukal/hellok8s:readiness镜像

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package main

import (
"io"
"net/http"
)

func hello(w http.ResponseWriter, r *http.Request) {
io.WriteString(w, "[v2] Hello, Kubernetes!")
}

func main() {
http.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(500)
})

http.HandleFunc("/", hello)
http.ListenAndServe(":3000", nil)
}

使用如下的配置创建deployment

flat
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
apiVersion: apps/v1
kind: Deployment
metadata:
# deployment唯一名称
name: hellok8s-go-http
spec:
strategy:
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
replicas: 3 # 副本数量
selector:
matchLabels:
app: hellok8s # 管理template下所有 app=hellok8s的pod,(要求和template.metadata.labels完全一致!!!否则无法部署deployment)
template: # template 定义一组pod
metadata:
labels:
app: hellok8s
spec:
containers:
- image: derobukal/hellok8s:v1
name: hellok8s
# 就绪探针
readinessProbe:
# http get 探测pod提供HTTP服务的路径和端口
httpGet:
path: /healthz
port: 3000
initialDelaySeconds: 1 # 1s后开始探测
periodSeconds: 5 # 每5s探测一次
timeoutSeconds: 1 # 单次探测超时,默认1
failureThreshold: 3 # 探测失败时,k8s的重试次数,默认3,达到这个次数后 停止探测,并打上未就绪的标签

还是使用/healthz接口进行检测,启动deployment后发现pod正常启动

~ kc get pods
NAME                                READY   STATUS    RESTARTS   AGE
hellok8s-go-http-7b48477987-2x4ll   1/1     Running   0          12s
hellok8s-go-http-7b48477987-hl4kt   1/1     Running   0          14s
hellok8s-go-http-7b48477987-xdtvl   1/1     Running   0          14s

之后修改镜像kc set image deployment/hellok8s-go-http hellok8s=derobukal/hellok8s:readiness

之后发现有两个pod一直无法进入ready状态

~ kc get pods
NAME                                READY   STATUS    RESTARTS   AGE
hellok8s-go-http-56dd6754c7-6qltj   0/1     Running   0          20s
hellok8s-go-http-56dd6754c7-kmzb2   0/1     Running   0          20s
hellok8s-go-http-7856db556-5d9vr    1/1     Running   0          31s
hellok8s-go-http-7856db556-jctgt    1/1     Running   0          31s

任务

任务用于执行一些job,例如如下的任务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
apiVersion: batch/v1
kind: Job
metadata:
name: pods-job
spec:
# completions: 3 # 启用它表示串行执行3次
# parallelism: 3 # 启动它表示并发数,由completions指定总次数
# backoffLimit: 3 # 限制重试次数,默认6,超过次数则不再启动新pod
# activeDeadlineSeconds: 10 # 限制job执行时间,超时还不终止则强制终止,并且稍后执行自动删除(若设置),且不受restartPolicy字段影响
ttlSecondsAfterFinished: 10 # 多少秒后自动删除执行成功的job,避免太多不再需要的job累积
template:
spec:
restartPolicy: Never # or OnFailure, 不能是其他值;推荐Never,因为这个策略下控制会启动新的pod,不会删除失败的pod,有助于排查问题;OnFailure是不断重启旧的pod
containers:
- command: ['sh', '-c', 'echo "Start Job!"; sleep 30; echo "Job Done!"']
image: busybox
name: pods-job-container

如上的任务只是简单的进行了信息的打印

~ kc apply -f job.yaml       
job.batch/pods-job created
~ kc get pods         
NAME             READY   STATUS              RESTARTS   AGE
pods-job-nthvl   0/1     ContainerCreating   0          7s
~ kc get pods
NAME             READY   STATUS    RESTARTS   AGE
pods-job-nthvl   1/1     Running   0          23s
~ kc get pods
NAME             READY   STATUS    RESTARTS   AGE
pods-job-nthvl   1/1     Running   0          48s
~ kc get pods
NAME             READY   STATUS      RESTARTS   AGE
pods-job-nthvl   0/1     Completed   0          56s

定时任务

定时任务CronJob用于执行定时任务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
apiVersion: batch/v1
kind: CronJob
metadata:
name: pods-cronjob
spec:
schedule: "*/1 * * * *" # 最小到min级别,这表示每分钟1次
startingDeadlineSeconds: 3 # 最大启动时间,超时后变成失败
concurrencyPolicy: Forbid # Allow/Forbid/Replace,上个周期的Job未执行结束时,是否允许下个周期的Job开始执行,默认Allow
suspend: false # 是否暂停cronjob的执行,一般通过kubectl edit修改
successfulJobsHistoryLimit: 3 # 保留多少条执行成功的Job记录,默认3
failedJobsHistoryLimit: 1 # 保留多少条执行失败的Job记录,默认1
jobTemplate:
spec:
template:
spec:
restartPolicy: Never
containers:
- command: [ 'sh', '-c', 'echo "Start Job!"; sleep 30; echo "Job Done!"' ]
image: busybox
name: pods-cronjob-container

如上这个任务会每分钟执行一次,详情如下

~ kc apply -f job.yaml
cronjob.batch/pods-cronjob created
~ kc get cronjob
NAME           SCHEDULE      SUSPEND   ACTIVE   LAST SCHEDULE   AGE
pods-cronjob   */1 * * * *   False     0        <none>          8s
~ kc get pods
NAME                          READY   STATUS              RESTARTS   AGE
pods-cronjob-28410113-fs4hr   0/1     ContainerCreating   0          0s
~ kc get job 
NAME                    COMPLETIONS   DURATION   AGE
pods-cronjob-28410113   0/1           6s         6s
~ kc logs pods-cronjob-28410113-fs4hr
Start Job!
Job Done!
~ kc delete cronjob pods-cronjob       
cronjob.batch "pods-cronjob" deleted