k8s部署
引言#
一直想要学会k8s,但是事实上来说学习这个技术并不是靠bilibili
的教学就能够学会的,更何况很多时候都会忘记,暑假时候我看完了B站的课,但有种理论知识会了实际不会的无力感,同时更有种知识一点点逝去的害怕,于是在强哥的支持下(白嫖服务器,白嫖域名😍😍),完成了k8s的部署
同时我这一套配置可是any亲传,后继有人呐……
步骤#
- 使用
Dockerfile
打包镜像并push
到dockerHub
上 - 部署
mysql\pgsql\redis
这些我的应用所需要的依赖 - 拉取刚刚提交的镜像并启动一个
pod
- 暴露我的端口并让外界所访问
实操#
首先需要下载 Lens
和 Termius
以及一个文件夹
,后续的一切操作都在这两个部分操作
最终的目录应该是这样的:#
这个是本地文件夹下的目录,然后会在 Lens
操作生成Pod,很好用的(等会再说)
我的Dockerfile:
#
1 | FROM golang:1.22 AS builder |
其中 COPY --from=builder /build/config.yaml ./config.yaml
这个语句表示把配置文件移到 workDir
下,如果没有配置文件,单个容器是运行不起来的。但是在k3s中,配置文件是被 configMap 管理的,所以不需要把配置文件移到 workDir 下(这样子配置文件是焊死的)
推送到DockerHub:#
直接问Gpt,这一块没什么重点
1 | docker login # 没有登录先登录 |
安装依赖#
然后就是安装依赖,因为强哥的服务器上啥都没有,同时我也没有啥空闲的数据库,所以只能自己起Pod
命名空间#
在我的部署中,所有的Pod都是在 hduhelp
这个命名空间中,特别是service的连接如果没有在同一个 namespace 中可能会出现大问题
Mysql#
启动一个Pod需要考虑那么几件事情
- 怎么编写
deployment.yaml
来自定义数据库 - 怎么在其他Pod连接到这个数据库Pod
- 怎么实现这个数据库Pod数据持久化
有这个三个问题我们就会明白为什么需要创建文件(在刚才图片的k8s/mysql目录下)
- deployment-mysql.yaml
1 | apiVersion: apps/v1 |
整体的逻辑肯定都能看懂,下面说一下比较重要的东西:
volumeMounts:
这里总共做了两次的卷映射,一个是sql初始化文件的映射,就是
init.sql
,这个文件把他挂载到/docker-entrypoint-initdb.d/init.sql
下,可以让容器创建的时候有且仅有一次创建好需要的表,username,password,同时赋予这个username所有权限; 另一个是mysql数据的映射,就是会将 mysql容器
/var/lib/mysql
这个路径下的数据与我pv
中定义的数据相关联关于env
由于之前的username,password已经创建,所以这里只要写一个root_password,mysql定义必须要写这个字段,要么就要设置 可以为空
- configmap-mysql.yaml + pv-mysql.yaml + pvc-mysql.yaml
这三个部分我觉得是联合在一起的,所以需要一起看
1 | # pv-mysql.yaml |
这里有几个比较重要的点:
- pv和pvc是互相关联作用的,主要通过 storageClassName 来关联,他的这个关联方式是自动匹配,一个pv自动关联一个pvc
- hostPath: 这个需要我们在服务器内创建目录:
"/home/echin/hduhelp-pv/mysql-pv/data"
- init.sql: 这个就是mysql的启动文件,如果是通过应用的orm去连接数据库他会显示连接不上,具体原因我没有细究,后面直接使用any的这套配置了,好用
- service-mysql.yaml
1 | apiVersion: v1 |
这里的重点就是 selector
就是service会与pod进行关联,管理ip映射,一个pod访问另一个pod的服务就是通过访问service暴露出来的端口来实现的,同时这里不建议NodePort这个type,但是用了似乎也没事?
- 大致流程:
首先启动deployment-mysql.yaml
来启动一个Pod,在启动的开始会运行 configMap-init.sql 中的 sql语句创建数据库和用户密码,同时他会把密码通过 pvc-pv-hostPath
的方式存储在本地里面,然后通过 service-mysql.yaml
将服务绑定到相应的service上供其他Pod使用
- 启动:
进入Termius,创建pv映射目录
1 | cd /home/echin |
直接通过 Lens 启动,不需要在Termius上大费周章
1 | cd absolutelyPathToYourYaml |
验证我们成功运行了Pod:
映射远程端口到本地
打开我们的IDE或者DataGrip:
成功连接
后面去查看我们的持久化数据卷是否映射成功:
如图所示是映射成功的
Postgresql#
postgresql 的书写逻辑和 mysql 相同,迁移过来就行,记得创建"/home/echin/hduhelp-pv/pgsql-pv/data"
configmap-pgsql.yaml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24apiVersion: v1
kind: ConfigMap
metadata:
name: configmap-pgsql
namespace: hduhelp
data:
init.sql: |
DO $$
BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname='username') THEN
CREATE USER "username" WITH PASSWORD 'password';
END IF;
END
$$;
DO $$
BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_database WHERE datname='pgsql') THEN
CREATE DATABASE "pgsql" ENCODING 'UTF8' LC_COLLATE='en_US.UTF-8' LC_CTYPE='en_US.UTF-8' TEMPLATE template0;
END IF;
END
$$;
GRANT ALL PRIVILEGES ON DATABASE "pgsql" TO "username";deployment-pgsql.yaml
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
40apiVersion: apps/v1
kind: Deployment
metadata:
name: pgsql-pod
namespace: hduhelp
spec:
selector:
matchLabels:
app: pgsql
template:
metadata:
labels:
app: pgsql
spec:
containers:
- name: postgres
image: postgres:12
imagePullPolicy: IfNotPresent
ports:
- containerPort: 5432
env:
- name: POSTGRES_DB
value: postgres
- name: POSTGRES_USER
value: username
- name: POSTGRES_PASSWORD
value: password
volumeMounts:
- name: pgsql-storage
mountPath: /var/lib/postgresql/data
- name: init-sql
mountPath: /docker-entrypoint-initdb.d/init.sql
subPath: init.sql
volumes:
- name: init-sql
configMap:
name: configmap-pgsql
- name: pgsql-storage
persistentVolumeClaim:
claimName: pgsql-pvcpv-pgsql.yaml
1
2
3
4
5
6
7
8
9
10
11
12
13apiVersion: v1
kind: PersistentVolume
metadata:
name: pgsql-pv
spec:
capacity:
storage: 5Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: manual
hostPath:
path: "/home/echin/hduhelp-pv/pgsql-pv/data"pvc-pgsql.yaml
1
2
3
4
5
6
7
8
9
10
11apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pgsql-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
storageClassName: manualservice-pgsql.yaml
1
2
3
4
5
6
7
8
9
10
11
12
13apiVersion: v1
kind: Service
metadata:
name: pgsql-service
namespace: hduhelp
spec:
selector:
app: pgsql
ports:
- port: 5432
targetPort: 5432
nodePort: 30432
type: NodePort
Redis#
Redis 同上 ,记得创建 "/home/echin/hduhelp-pv/redis-pv/data"
deployment-redis.yaml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24metadata:
name: redis-deployment
namespace: hduhelp
spec:
selector:
matchLabels:
app: redis
template:
metadata:
labels:
app: redis
spec:
containers:
- name: redis
image: redis:6.2.5
ports:
- containerPort: 6379
volumeMounts:
- name: redis-storage
mountPath: /data
volumes:
- name: redis-storage
persistentVolumeClaim:
claimName: redis-pvcpv-redis.yaml
1
2
3
4
5
6
7
8
9
10
11
12
13apiVersion: v1
kind: PersistentVolume
metadata:
name: redis-pv
spec:
capacity:
storage: 5Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: manual
hostPath:
path: "/home/echin/hduhelp-pv/redis-pv/data"pvc-redis.yaml
1
2
3
4
5
6
7
8
9
10
11apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: redis-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
storageClassName: manualservice-redis.yaml
1
2
3
4
5
6
7
8
9
10
11
12
13apiVersion: v1
kind: Service
metadata:
name: redis-service
namespace: hduhelp
spec:
selector:
app: redis
ports:
- port: 6379
targetPort: 6379
nodePort: 30637
type: NodePortApp (gin-quickly-template)
下面要创建真正的应用了
首先应用要启动需要所谓的配置文件:
- config-app.yaml
1 | apiVersion: v1 |
注意这里的Host统一要改成service中定义的依赖名,比如说我在 mysql的service Pod中的 metadata 为 mysql-service,所以这里要写成 mysql-service
- deployment-app.yaml
1 | apiVersion: apps/v1 |
这里要注意的是路径的绑定 - /Serve/config
在我的源程序中写着我的应用会读取 ./ 或者 ./config
下的配置文件,因此我这里选择了后者
我之前的写法是:
1 | volumeMounts: |
以为这样子就可以将配置文件映射到/Serve
下,但是事实是他这个configMap会覆盖掉原先存在的文件夹以及文件,因此他会报错:
其实还有一种写法应该也可以实现:
1 | volumeMounts: |
🆗容器起来以后我们就需要让我们的容器暴露在集群内,就需要定义service
其实我一开始觉得不需要Service,但是后来我发现写Ingress是依赖service的,原理和Pod间调用一样,就是Pod的创建删除过于频繁,用service来自动管理Ip连接
- service-app.yaml
1 | apiVersion: v1 |
这里不写NodePort的原因就是感觉写NodePort太蠢了…………….
然后可以测试一下是否启动成功(前提是你的依赖已经全部部署):
启动部署:
端口转发到本地:
测试:
成功了!
最后需要把集群内的服务暴露出去,这个可以通过Ingress的traefic控制器代理出去
- Ingress-traefic.yaml
1 | apiVersion: networking.k8s.io/v1 |
Ingress 搭载的是 service,所以必须要搭建刚才的service
最后测试一下:
1 | curl http://dsn/v1/ping |
结果:
至此 k3s 部署完毕!
总结#
- configMap 的路径映射
- init.sql 的巧妙使用
- service 的通过 selector 的label 匹配
- 简单的 kubectl 命令行
- pv 和 pvc 的用法
- 强哥 和 any 的教导和支持😘😘😘