本章主要将如何将已经提交到Harbor仓库的镜像在k8s体系内完成部署工作。
简单扫盲
粗略介绍下k8s的内容体系,便于理解后面配置文件。
k8s体系简介
k8s整体可以分几个部分:namespace,configMap,deployment,pod,service,ingress。
namespace
命名空间,其实和java中namespace类似,主要是为了对k8s的对象进行区域划分以及资源隔离。
你可以认为每个namespace下的对象是不能够共享的。并且你可以针对namespace进行配置,设置这个namespace下可以使用的服务器资源的配额。例如cpu核心,存储,网络等等。
这里不再详述。
namespace简写ns
configMap
k8s的配置对象,可以绑定到k8s对象上面,让对象能够读取configMap中的配置信息,主要是key:value信息。当然是否要使用,需要自行判断。同时需要程序端配合。不然你运营配置了,程序没加载,依然没用。
deployment&pod
这俩需要一块说明一下。
K8s中的pod,你可以理解为docker中的容器。k8s中创建一个pod,就等于docker run创建了一个容器。也就是说pod是镜像的实例,是实际运行jar包或者nginx的对象。
pod实际上是可以单独创建的。
kubectl create pod命令就可以创建一个pod容器。当然用kubectl apply一个yaml文件效果一样。
但是单独创建的pod有一些问题。
1. pod作为容器,是会动态分配ip的,动态分配的ip不可控。
2. pod被删除后,或者pod由于程序问题死掉了,是不会自动恢复的。也就是说,你直接创建的pod,如果执行了kubectl delete pod,那么pod就真的被删除掉了。不会享受到k8s的高可用。
这么玩费劲搞k8s有啥用?所以我们一般不会直接创建pod,而是将创建pod和pod管理这个事情托管给deployment。
deployment可以理解为一组管理策略。我们创建一个deployment,然后在里面指定我要启用几个pod,pod用什么镜像,需要配置什么env环境变量等等信息。
然后通过kubectl create deployment或者kubectl apply -f deployment.yaml创建deployment对象。那么k8s会根据deployment对象的配置,自行创建需要的pod。并且pod和deployement存在关联关系。
例如我deploy中定义要创建2个pod,那么系统中就会自动创建两个pod。这时你如果删除一个,那么系统会自动再重新创建一个pod,补足2个pod配置要求。同样的,pod死掉,系统也会自动补足2个,确保k8s平台有2个活着的pod。这是k8s高可用的一个前提。
deployment简写deploy
service
deploy虽然可以解决pod管理问题,但是依然无法解决pod动态ip的问题(这是由于框架需要决定的,毕竟pod随时可以扩展,并且随时会被替换,不适合制定固定ip)。于是我系统如何被请求到就出现一个问题。
k8s设计了service解决该问题。
service一旦被创建,那么ip变会被固定下来,并且service不是一容器形式存在,所以也不牵扯死掉重新创建等问题。所以k8s中,通过service将pod中的服务提供出来。
service一般是绑定到deploy上面,通过配置targetPort将deploy下pod的服务端口开放出来。并且service还具备负载均衡能力。如果deploy下配置了多个pod,那么service会根据负载均衡策略对pod进行轮训转发。这样就解决了pod动态ip,并且deploy下的pod可能会随时被替换的问题。
serivce简写svc
ingress
k8s的一个路由组件。
k8s提供的网络访问方式有3中,ClusterIP,NodePort,LoadBalancer。
ClusterIP方式,只能够在k8s集群内部服务器间通讯。
NodePort方式,可以将node上的pod或者service的业务端口映射到主节点上,从而可以通过主节点ip:映射端口方式,对集群外终端提供请求服务。
LoadBanlancer需要配合前端负载均衡服务或者设备使用,这里不提。
ClusterIP,显然无法满足外部请求需要;NodePort,小规模K8s的话可以使用,他最多可以使用1w左右节点,默认从3w以后开始分配。
为了满足大规模k8s集群应用,ingress诞生了。
ingress有几种,一般多用nginx实现版本。
他会在k8s集群中的每个worknode上创建一个示例pod,对改节点上的service和pod进行访问代理。
同时他可以定义一个全局的ingress服务,对每个node上的ingresscontroller进行管理。
同时,ingress可以提供域名和路由的配置,这样就是先了外部请求进入ingress,ingress根据配置进行匹配,匹配到响应服务后,转发到对应节点的对应服务上,从而完成服务对外提供。就解决了端口问题。
例如:
www.abc.cn->svc1
www.def.cn->svc2
k8s部署
k8s部署说白了就是从harbor拉取镜像,创建容器,然后配置网络,实现外部对容器的请求。
整体用到的内容,就是上面扫盲的内容。
具体实现的办法,是通过yaml,完成上面内容的配置,然后通过kubectl apply -f xxx.yaml完成相关对象的创建。
后端部署模板
模板代码
apiVersion: v1
kind: Namespace
metadata:
name: mysql
#对应部署的命名空间
---
apiVersion: v1
kind: ConfigMap
#类型为configmap
metadata:
name: uaa
namespace: mysql
#对应部署的命名空间
data:
application.yml: ''
---
apiVersion: apps/v1
kind: Deployment
#类型为deployment,deployment主要管理无状态应用
metadata:
labels:
app: uaa
name: uaa #应用名称
namespace: mysql #所属命名空间
spec:
replicas: 1 # 配置创建pod实例数量
selector: # k8s中对象的关联关系就是通过对对象打label和selector实现的绑定 所有符合selector的对象都是改对象的关联对象
matchLabels: &id001 # yaml中锚点,类似变量
app: uaa
app-pod: uaa
template:
metadata:
annotations:
serialNumber: '20220523094344'
labels: *id001 # 引用上面定义的锚点 变量的值
namespace: mysql
spec:
containers:
- args: []
env:
- name: RUNTIME
value: docker
- name: RUNTIME_ENV
value: prd
- name: RUNTIME_MYSQL_PASSWORD #数据库密码
value: '123456'
- name: spring.redis.host #redis ip
value: 192.168.216.219
image: shandong.harbor.cn:55443/test/uaa:1.3.39 #镜像版本
imagePullPolicy: Always #镜像拉取策略
name: test-uaa
ports:
- containerPort: 8080
readinessProbe:
initialDelaySeconds: 5
periodSeconds: 3
tcpSocket:
port: 8080
resources:
limits: #资源限制
cpu: '8'
memory: 2G
requests:
cpu: 100m
memory: 500m
volumeMounts:
- mountPath: /var/log/andevopslog
name: logpath
imagePullSecrets:
- name: images-registry #定义镜像拉取的密钥 需要按照harbor账号密码进行配置
initContainers: #初始化容器,初始化如果要执行sql,打镜像的时候写在初始化容器里,先于主容器执行
- command:
- sh
- -c
- mysql -h$RUNTIME_MYSQL_HOST -P$RUNTIME_MYSQL_PORT -u$RUNTIME_MYSQL_USER
-p$RUNTIME_MYSQL_PASSWORD < /opt/app/upgrade_sql/update.sql
env: #环境变量同上
- name: RUNTIME_MYSQL_PASSWORD
value: '123456'
- name: SQL_EXECUTE_MODE
value: upgrade
- name: server.port
value: '8080'
name: init-uaa-sql-execute-job
restartPolicy: Always
volumes:
- emptyDir: {}
name: logpath
---
apiVersion: v1
kind: Service #定义服务名,k8s内部访问的端口
metadata:
labels: &id001
app-pod: uaa
name: uaa
namespace: mysql
spec:
ports:
- name: http-80
port: 80 #k8s集群内部服务之间访问service的入口
targetPort: 8080 #容器的端口
selector: *id001
---
apiVersion: extensions/v1beta1
kind: Ingress #定义对外暴露使用的方式为ingress
metadata:
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$2
labels:
app: uaa
name: uaa
namespace: mysql
spec:
rules:
- host: demo.test.com.cn #对外暴露使用的域名
http:
paths:
- backend:
serviceName: uaa #对外暴露指向内部的服务名service
servicePort: 80
path: /test/uaa(/|$)(.*) #路由地址
---
上面模板包含了一个后台应用上线需要配置的所有相关对象。
这里要说明的是,env下配置的环境变量,需要和研发配合才能生效,否则就算容器配置了该变量,也不一定起作用,后面会单独讲解。
使用的时候,根据需要,修改namespace,各个对象的name,镜像信息,env信息,端口,ingress的域名和路由即可。
前端部署模板
模板代码
apiVersion: v1
kind: Namespace
metadata:
name: mysql #定义部署的命名空间
---
apiVersion: v1
kind: ConfigMap #定义类型为configmap,configmap主要是一些配置,下发是nginx的配置
metadata:
name: coreweb
namespace: mysql
data:
nginx.conf: |
#user nobody;
worker_processes 1;
#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;
#pid logs/nginx.pid;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
#log_format main '$remote_addr - $remote_user [$time_local] "$request" '
# '$status $body_bytes_sent "$http_referer" '
# '"$http_user_agent" "$http_x_forwarded_for"';
#access_log logs/access.log main;
sendfile on;
tcp_nopush on;
client_max_body_size 500m;
#keepalive_timeout 0;
keepalive_timeout 200;
gzip on;
gzip_vary on;
gzip_min_length 1k;
gzip_comp_level 4;
gzip_types text/plain application/x-javascript text/css application/xml text/javascript application/javascript;
gzip_disable "MSIE [1-6]\.";
add_header X-Frame-Options DENY;
server {
listen 8080; #端口
server_name localhost;
location ^~ / {
#root html;
alias /usr/share/nginx/html/; #这里路径要和dockerfile中copy的前端文件路径一致
index index.html index.htm;
try_files $uri $uri/ /index.html;
client_max_body_size 500m;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}
---
apiVersion: apps/v1
kind: Deployment #类型为deployment,deployment主要管理无状态应用
metadata:
labels:
app: coreweb
name: coreweb
namespace: mysql
spec:
replicas: 1
selector:
matchLabels: &id001
app: coreweb
app-pod: coreweb
template:
metadata:
annotations:
serialNumber: '20220523124824'
labels: *id001
namespace: mysql
spec:
containers: #定义容器内的环境变量,前端不会请求这些变量,可有可无
- args: []
env:
- name: RUNTIME
value: docker
- name: RUNTIME_ENV
value: prd
image: shandong.harbor.cn:55443/test/coreweb:1.3.229 #镜像版本
imagePullPolicy: Always #镜像拉取策略
name: onepark-coreweb
ports:
- containerPort: 8080
readinessProbe:
initialDelaySeconds: 5
periodSeconds: 3
tcpSocket:
port: 8080
resources:
limits:
cpu: '8'
memory: 2G
requests:
cpu: 100m
memory: 500m
volumeMounts:
- mountPath: /var/log/andevopslog #容器日志路径
name: logpath
- mountPath: /etc/nginx/nginx.conf #将configmap挂载进去
name: config
subPath: nginx.conf
imagePullSecrets:
- name: image-secret #定义镜像拉取的密钥
restartPolicy: Always
volumes:
- emptyDir: {} #emptyDir类型的volume在pod分配到node上时被创建,kubernetes会在node上自动分配 一个目录,因此无需指定宿主机node上对应的目录文件。这个目录的初始内容为空,当Pod从node上移除时,emptyDir中的数据会被永久删除。
name: logpath
- configMap:
name: coreweb
name: config
---
apiVersion: v1
kind: Service #定义服务名,k8s内部访问的端口
metadata:
labels: &id001
app-pod: coreweb
name: coreweb
namespace: mysql
spec:
ports:
- name: http-80
port: 80 #k8s集群内部服务之间访问service的入口
targetPort: 8080 #容器的端口
selector: *id001
---
apiVersion: extensions/v1beta1
kind: Ingress #定义对外暴露使用的方式为ingress
metadata:
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$2
labels:
app: coreweb
name: coreweb
namespace: mysql
spec:
rules:
- host: web.demo.cn #定义访问的域名
http:
paths:
- backend:
serviceName: test #对外暴露指向内部的服务名service
servicePort: 80
path: /()(.*)
---
前端就相对简单了,主要是nginx的配置要对起来。
下面给一个示例图片
PS:
首先你要先确保你的k8s环境和网络没问题!!!