这篇文章上次修改于 902 天前,可能其部分内容已经发生变化,如有疑问可询问作者。
1 目标
在本地,一键启动 k8s 集群,并将服务部署到 k8s 集群上。
2 部署 k8s
2.1 部署 minikube
minikube 是一个虚拟机,启动后会在内部自动创建一个 k8s 集群。minikube 是 k8s 的 node。
2.1.1 安装 minikube
minikube 安装:
curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64
sudo install minikube-linux-amd64 /usr/local/bin/minikube
2.1.2 启动 minikube
minikube start \
--extra-config=apiserver.service-node-port-range=1-65535 \
--listen-address=0.0.0.0
其中,
- --extra-config 用来修改 minikube 默认暴露给主机的端口范围,默认为:30000-32767。
--listen-address=0.0.0.0 是为了将 minikube 内部的服务暴露在 minikube 外部,或者也可以使用 ingress-nginx 插件。minikube 暴露给主机的地址为 192.168.49.2,假如 minikube 中服务监听的端口为 3000,则在主机上访问该服务的方式为 192.168.49.2:3000。可以用 minikube service --url
命令查看服务地址,例如: $ minikube service --url signaling-0 http://192.168.49.2:21116
2.1.3 kubectl
kubectl 可方便地与 k8s 集群进行交互。minikube 内部默认安装 kubectl,例如查看所有 pod:
minikube kubectl -- get pods
可以为 minikube kubectl -- 命令起一个别名:
alias kubectl="minikube kubectl --"
查看所有 pod 命令可简化为:
kubectl get pods
kubectl 常用的命令:
列出 pod,service 等资源:
# List all pods in plain-text output format. kubectl get pods # List all services in plain-text output format. kubectl get svc # List all daemon sets in plain-text output format. kubectl get ds
查看 pod 等资源的状态,事件等:
# Display the details of all the pods that are managed by the replication controller named <rc-name>. # Remember: Any pods that are created by the replication controller get prefixed with the name of the replication controller. kubectl describe pods <rc-name>
进入 pod 中的容器:
# Get an interactive TTY and run /bin/bash from pod <pod-name>. By default, output is from the first container. kubectl exec -it <pod-name> -- /bin/bash
说明:pod 是 k8s 的管理单元,pod 中可包含多个容器,pod 中的所有容器共享 namespace。
2.1.4 其它命令
停止 minikube:
minikube stop
删除 k8s 集群:
minikube delete
2.2 部署 Lens
Lens 是 k8s 集群的操作界面,可方便地与 k8s 集群进行交互。
2.2.1 安装 Lens
wget https://api.k8slens.dev/binaries/Lens-5.4.4-latest.20220325.1.amd64.deb
sudo dpkg -i Lens-5.4.4-latest.20220325.1.amd64.deb
2.2.2 启动 Lens
- 命令方式:
lens
- 界面方式:在应用中搜索到 Lens 后双击。
2.2.3 Lens 管理 k8s
左边菜单栏中列出来 k8s 中的各种资源,可对各种资源进行查看、修改、删除。
例如,查看所有 pod:
对 Deployment 或 StatefulSet 扩缩容:
进入容器:
将 k8s 中 TCP 服务的端口映射到主机:
内置了 Helm,Helm 用来管理 k8s 应用,可发现、共享和使用为 k8s 构建的软件。
例如,在 k8s 安装 redis 或 nats:
3 部署 Nginx
部署:
docker run -d --name xremote_nginx --network host \
-v ${HOME}/nginx/nginx.conf:/etc/nginx/nginx.conf nginx
${HOME}/nginx/nginx.conf 中添加了要代理的 minikube 中的 UDP 服务端口。示例如下:
stream {
log_format proxy '$remote_addr [$time_local] '
'$protocol $status $bytes_sent $bytes_received '
'$session_time "$upstream_addr" '
'"$upstream_bytes_sent" "$upstream_bytes_received" "$upstream_connect_time"';
access_log /var/log/nginx/udp-access.log proxy;
open_log_file_cache off;
upstream signaling-0 {
server 192.168.49.2:21116;
}
server{
listen 21116 udp reuseport;
proxy_connect_timeout 8s;
proxy_pass signaling-0;
}
}
使用 Nginx 的原因:代理 minikube 中的 UDP 服务。
- 使用 NodePort 等 service 可将 k8s 中的服务暴露给 node,也就是 minikube,而不是主机。
- minikube 暴露给主机的地址默认为 192.168.49.2,假如 minikube 中服务监听的端口为 3000,则在主机上访问该服务的方式为 192.168.49.2:3000,而不是 ${host_ip}:3000。所以,如果需要从另外一台主机访问 minikube 中的服务,需要 nginx 进行代理。
- 对于 minikube 中的 TCP 服务,可不通过 nginx 进行代理,可使用 kubectl port-forward 命令直接将端口映射到主机。
4 部署服务
4.1 部署文件
deployment.yml:定义资源如何部署。
apiVersion: apps/v1
kind: Deployment
metadata:
name: signaling-0
spec:
selector:
matchLabels:
app: signaling-0
replicas: 1
template:
metadata:
labels:
app: signaling-0
spec:
containers:
- name: signaling
image: code.com:6543/signaling-server:latest
ports:
- name: server
containerPort: 21116
protocol: UDP
imagePullPolicy: IfNotPresent
env:
- name: RUN_XREMOTE_SERVER_MODE
value: production
command: ["/bin/bash"]
args: ["-c", "/opt/signaling_server/run.sh"]
volumeMounts:
- name: config
mountPath: /opt/signaling_server/config/signaling/production.toml
subPath: production.toml
volumes:
- name: config
configMap:
name: signaling
1.
字段说明:
kind:指定了这个 API 对象的类型是一个 Deployment。
Deployment:是一个定义多副本应用(即多个副本 Pod)的对象。此外,Deployment 还负责在 Pod 定义发生变化时,对每个副本进行滚动更新(Rolling Update)。
- 场景:一个应用的所有 Pod,是完全一样的。所以,它们互相之间没有顺序,也无所谓运行在哪台宿主机上。
Pod :如果 k8s 看作操作系统,pod 可看作进程组,容器可看作进程。
- 凡是调度、网络、存储,以及安全相关的属性,基本上是 Pod 级别的。
- Pod 里的所有容器,共享的是同一个 Network Namespace,并且可以声明共享同一个 Volume。
- 另一种常见的 kind 是 StatefulSet,相对于 Deployment,一个应用的 Pod 之间存在着依赖关系,比如:主从关系、主备关系。redis 和 nats 是 StatefulSet。
- metadata:是 API 对象的“标识”,即元数据,它也是我们从 Kubernetes 里找到这个对象的主要依据。
spec:用来描述它所要表达的功能。
spec.selector.matchLabels:过滤规则,一般称之为:Label Selector。这个字段一般和 spec.template.metadata.labels.app 保持一致,表示该 deployment 控制下面 spec 中定义的 Pod。
- 使用一种 API 对象(Deployment)管理另一种 API 对象(Pod)的方法,在 Kubernetes 中,叫作“控制器”模式(controller pattern)
- spec.replicas:Pod 副本个数。
- spec.template:Pod 模板,描述了我想要创建的 Pod 的细节。本例中,Pod 里只有一个容器,该容器的镜像(spec.containers.image)是 code.autox.ds:6543/internal/mapping-experimental/offboard/signaling-server:latest,监听端口(containerPort)是 21116,将 signaling 这个配置挂载到了 /opt/signaling_server/config/signaling/production.toml,后面会提到 ConfigMap。
config_map.yml:存储一些配置信息。
apiVersion: v1
kind: ConfigMap
metadata:
name: signaling
data:
production.toml: |-
debug = false
[signaling]
data_center = "beijing"
service_name = "signaling"
signaling_port = 21116
在 Kubernetes 中,有几种特殊的 Volume,它们的作用是为容器提供预先定义好的数据。所以,从容器的角度来看,这些 Volume 里的信息就是仿佛是被 K8s “投射”(Project)进入容器当中的。到目前为止,Kubernetes 支持的 Projected Volume 一共有四种:Secret、ConfigMap、Downward API 和 ServiceAccountToken。
前面的 deployment.yml 中的 spec.template.spec.volumes 中会声明一个 volume,该 volume 的来源是一个 ConfigMap,然后该 volume 会被挂载到 /opt/signaling_server/config/signaling/production.toml。
service.yml:service 的种类较多,其中 NodePort 的作用是将服务端口暴露在 k8s 外部。
kind: Service
apiVersion: v1
metadata:
name: signaling-0
spec:
ports:
- name: server
nodePort: 21116
port: 21116
targetPort: 21116
protocol: UDP
selector:
app: signaling-0
type: NodePort
将服务暴露在集群外的方式:
Service
- NodePort:最简单。
- LoadBalancer:适用于公有云。
- ExternalName:Kubernetes 在 1.7 之后支持的一个新特性。
- Ingress:是 Service 的“Service”。
本例中:
- type=NodePort
ports 字段
- nodePort:暴露在集群外部的端口。
- port:暴露在集群内部的端口。
- targetPort:容器监听的端口。
- selector 字段表示它代理的是 signaling-0 这个 Pod 的端口。
要访问这个 Service,只需要访问:
<node 的 IP 地址>:21116
另一种 service:ClusterIP
- 将服务暴露在集群内部,即在集群中的一个 pod 内部可访问到另一个 pod。nats 和 redis 有用到。
当 ClusterIP 中将 clusterIP 字段设置为 None,便不会被分配 VIP,被称为 Headless Service,它所代理的所有 Pod 的 IP 地址,都会被绑定一个 DNS 记录,形式为
. 。例如,redis 的 headless 定义如下: apiVersion: v1 kind: Service metadata: name: redis-headless namespace: default spec: ... clusterIP: None type: ClusterIP ...
通过域名 redis-headless.default 即可访问 redis。
以上几个 yml 文件也可以放在一个 yml 文件中,并以 ”---“ 分隔。
4.2 部署
对每个 yml 文件执行:
kubectl apply -f xxx.yml
5 查看日志
方式 1)Lens 界面方式:
方式 2)命令行方式:
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
discovery-568569c86f-jflrq 1/1 Running 1 (51m ago) 58m
$ kubectl exec -it discovery-568569c86f-jflrq -- /bin/sh
# ls
config discovery-server log run.sh sum.md5
# ls log
discovery_server_20220408-0607.log
说明:由于 服务的日志写在了文件中,而不是标准输出中,所以用 kubectl logs 命令看不到日志。
6 错误处理
6.1 拉取镜像失败
发生 ErrImagePull 错误:
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
discovery-568569c86f-njmf4 0/1 ErrImagePull 0 41m
是因为 minikube 没有权限拉取镜像。
解决:
minikube -p minikube docker-env
eval $(minikube -p minikube docker-env)
cd xremote/server
bash release_build.sh discovery
相当于把镜像 push 到了 minikube 中。
或者这种方式应该也可以:https://minikube.sigs.k8s.io/docs/handbook/untrusted_certs/ ,本人没有尝试。
没有评论