基于Ubuntu20.04在k8s 1.25部署gin+MySQL服务
0. 前言
某天突发奇想,既然都学了 docker 了,那干脆,顺便把 kubernetes 也学了,于是开始了我长达一个月的环境搭建、踩坑历程。
最开始,我的想法是,在我的物理机使用 WSL + docker 来部署服务,但是 WSL 部署的服务好像只是单机版,和实际生产中的情况相差甚远,于是,我去弄了几台服务器,一台阿里云 2C2G,一台腾讯云 4C8G,一台腾讯云 2C2G。
基于本人比较喜欢折腾的特点,我没有选择常见的 CentOS 来搭建,而是使用了 Ubuntu (问就是平时用 WSL 用多了,对 Ubuntu 有了感情 bushi)。然后就开始了我漫长的异地组网历程。记得前后搭建了半个多月吧,前面七天基本在搭建环境,解决镜像源问题,后面七天在解决两个 node 之间的通信,后面发现,我租用的服务器,都是弹性服务器,没法换公网和内网的 ip,目前跨 VPC 构建 k8s 集群不是一个好方法 (毕竟企业不可能这样做,最多也就是学生搞来玩玩),没办法,只好自己搞虚拟机了,不过还好,又历经一周,虚拟机的搭建成功了,后面如果能搞到更多磁盘和内存的话,可能会尝试双 master 和多 node 的集群。
正文部分比较多配置文件的代码,如果想看有代码折叠版本的文章,欢迎移步个人小破站:基于Ubuntu20.04在k8s 1.25部署gin+MySQL服务 | Jayden's Blog (jaydenchang.top)
1. 环境搭建
1.1 环境说明
节点名称、ip:
- master:192.168.22.222
- node1:192.168.22.223
master 要求 至少 2G RAM,2 核 CPU
1.2 版本信息
- 系统版本:Ubuntu server 20.04.6
- Docker:20.10.21
- Kubernetes:1.25.0
1.3 环境配置
设置主机名及解析
# master
sudo systemctl set-hosename master
sudo cat > /etc/hosts << EOF
192.168.22.222 master
192.168.22.223 node1
EOF
# node1systemctl set-hosename node1
sudo cat > /etc/hosts << EOF
192.168.22.222 master
192.168.22.223 node1
EOF
关闭 swap
sudo swapoff -a
# 注释/etc/fstab文件的最后一行
sudo sed -i '/swap/s/^/#/' /etc/fstab
开启 IPv4 转发
sudo cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF
# 分割线
modprobe overlay
modprobe br_netfilter
# 分割线
sudo cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
EOF
#分割线
sudo sysctl --system
1.4 安装 containerd
从 k8s 1.25 开始使用 containerd 来作为底层容器支持,根据 k8s 和 containerd 的匹配要求,这里我们使用 containerd 1.7.0
# apt 安装无法安装最新的版本,这里使用tar包解压
wget https://github.com/containerd/containerd/releases/download/v1.7.0/containerd-1.7.0-linux-amd64.tar.gz
tar zxvf containerd-1.7.0-linux-amd64.tar.gz -C /usr/local
# 导出默认配置
sudo containerd config default > /etc/containerd/config.toml
# 编辑配置文件
sudo vim /etc/containerd/config.toml
# 进入到 vim 后搜索、替换
# 修改sandbox_image行替换为aliyun的pause镜像
sandbox_image = "registry.aliyuncs.com/google_containers/pause:3.8"
# 配置 systemd cgroup 驱动
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc]
...
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
SystemdCgroup = true
# 配置镜像加速
[plugins."io.containerd.grpc.v1.cri".registry]
[plugins."io.containerd.grpc.v1.cri".registry.mirrors]
[plugins. "io.containerd.grpc.v1.cri".registry.mirrors."docker.io"]
endpoint = ["https://registry.aliyuncs.com"]
添加 containerd 服务
sudo cat > /etc/systemd/system/containerd.service << EOF
# Copyright The containerd Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
[Unit]
Description=containerd container runtime
Documentation=https://containerd.io
After=network.target local-fs.target
[Service]
#uncomment to enable the experimental sbservice (sandboxed) version of containerd/cri integration
#Environment="ENABLE_CRI_SANDBOXES=sandboxed"
ExecStartPre=-/sbin/modprobe overlay
ExecStart=/usr/local/bin/containerd
Type=notify
Delegate=yes
KillMode=process
Restart=always
RestartSec=5
# Having non-zero Limit*s causes performance problems due to accounting overhead
# in the kernel. We recommend using cgroups to do container-local accounting.
LimitNPROC=infinity
LimitCORE=infinity
LimitNOFILE=infinity
# Comment TasksMax if your systemd version does not supports it.
# Only systemd 226 and above support this version.
TasksMax=infinity
OOMScoreAdjust=-999
[Install]
WantedBy=multi-user.target
EOF
加载配置,启动 contained 服务
systemctl daemon-reload
systemctl enable --now containerd
1.5 安装 kubernetes
安装必要组件
sudo apt-get install -y apt-transport-https ca-certificates curl
添加阿里云安装源
sudo cat <<EOF > /etc/apt/sources.list.d/kubernetes.list
deb https://mirrors.aliyun.com/kubernetes/apt/ kubernetes-xenial main
EOF
gpg --keyserver keyserver.ubuntu.com --recv-keys BA07F4FB
gpg --export --armor BA07F4FB | sudo apt-key add -
curl https://mirrors.aliyun.com/kubernetes/apt/doc/apt-key.gpg | apt-key add -
安装 k8s
sudo apt-get update
sudo apt-get install -y kubelet=1.25.0-00 kubeadm=1.25.0-00 kubectl=1.25.0-00
systemctl enable --now kubelet
标记软件包,避免自动更新
sudo apt-mark hold kubelet kubeadm kubectl
1.6 安装 docker
apt install docker.io
1.7 初始化 kubernetes 集群
使用 kubeadm 初始化
# 这里的apiserver那行是master的ip, 注意service-cidr和pod-network-cidr和节点ip不要出现在同一网段
kubeadm init \
--image-repository registry.aliyuncs.com/google_containers \
--apiserver-advertise-address=192.168.22.222 \
--service-cidr=10.96.0.0/12 \
--pod-network-cidr=22.22.0.0/16 \
--kubernetes-version v1.25.0
如果没有报错的话,配置环境变量
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
export KUBECONFIG=/etc/kubernetes/admin.conf
node1 端输入类似这样的命令
# 记得在 node1 开放6443端口
kubeadm join 192.168.22.223:6443 --token dc4wxa.qar86v4pb1b2umvm \
--discovery-token-ca-cert-hash sha256:1df0074a2226ed1a56f53b9d33bf263c51d3794b4c4b9d6132f07b68592ac38a
# token 是随机生成的
重新生成 token
kubeadm token create --print-join-command
1.8 安装 calico 网络插件
流行的有 flannel 和 calico,这里选择 calico
wget https://raw.staticdn.net/projectcalico/calico/v3.24.1/manifests/tigera-operator.yaml
sudo kubectl create -f tigera-operator.yaml
wget https://raw.staticdn.net/projectcalico/calico/v3.24.1/manifests/custom-resources.yaml
vim custom-resources.yaml
# 修改cidr配置
apiVersion: operator.tigera.io/v1
kind: Installation
metadata:
name: default
spec:
# Configures Calico networking.
calicoNetwork:
# Note: The ipPools section cannot be modified post-install.
ipPools:
- blockSize: 26
cidr: 10.244.0.0/16 # 修改为刚刚初始化时的 pod-network-cidr
encapsulation: VXLANCrossSubnet
natOutgoing: Enabled
nodeSelector: all()
注意,custom-resources.yaml
的 spec.calicoNetwork.ipPools.cidr
一定要和刚刚初始化的 pod-network-cidr
一致,不然无法添加 calico 插件
查看 pod 状态
$ kubectl get pod -A
NAMESPACE NAME READY STATUS RESTARTS AGE
calico-apiserver calico-apiserver-95575566-mpv54 1/1 Running 4 (156m ago) 3d1h
calico-apiserver calico-apiserver-95575566-n4x5w 1/1 Running 4 (156m ago) 3d1h
calico-system calico-kube-controllers-85666c5b94-lll7q 1/1 Running 4 (156m ago) 3d1h
calico-system calico-node-djqts 1/1 Running 4 (14h ago) 3d1h
calico-system calico-node-wp4cf 1/1 Running 4 (156m ago) 3d1h
calico-system calico-typha-76fd59d84d-xn79m 1/1 Running 6 (156m ago) 3d1h
calico-system csi-node-driver-74p7m 2/2 Running 8 (156m ago) 3d1h
calico-system csi-node-driver-t86b2 2/2 Running 8 (14h ago) 3d1h
kube-system coredns-c676cc86f-tmq7f 1/1 Running 4 (156m ago) 3d1h
kube-system etcd-master 1/1 Running 4 (156m ago) 3d1h
kube-system kube-apiserver-master 1/1 Running 4 (156m ago) 3d1h
kube-system kube-controller-manager-master 1/1 Running 4 (156m ago) 3d1h
kube-system kube-proxy-449rk 1/1 Running 4 (14h ago) 3d1h
kube-system kube-proxy-vswhw 1/1 Running 4 (156m ago) 3d1h
kube-system kube-scheduler-master 1/1 Running 4 (156m ago) 3d1h
tigera-operator tigera-operator-6675dc47f4-wcfxf 1/1 Running 7 (155m ago) 3d1h
2. 部署 MySQL
先编写一个 mysql-deploy.yaml
配置文件
apiVersion: apps/v1 # apiserver的版本
kind: Deployment # 副本控制器deployment,管理pod和RS
metadata:
name: mysql # deployment的名称,全局唯一
namespace: default # deployment所在的命名空间
labels:
app: mysql
spec:
replicas: 1 # Pod副本期待数量
selector:
matchLabels: # 定义RS的标签
app: mysql # 符合目标的Pod拥有此标签
strategy: # 定义升级的策略
type: RollingUpdate # 滚动升级,逐步替换的策略
template: # 根据此模板创建Pod的副本(实例)
metadata:
labels:
app: mysql # Pod副本的标签,对应RS的Selector
spec:
nodeName: node1 # 指定pod运行在的node
containers: # Pod里容器的定义部分
- name: mysql # 容器的名称
image: mysql:8.0 # 容器对应的docker镜像
volumeMounts: # 容器内挂载点的定义部分
- name: time-zone # 容器内挂载点名称
mountPath: /etc/localtime # 容器内挂载点路径,可以是文件或目录
- name: mysql-data
mountPath: /var/lib/mysql # 容器内mysql的数据目录
- name: mysql-logs
mountPath: /var/log/mysql # 容器内mysql的日志目录
ports:
- containerPort: 3306 # 容器暴露的端口号
env: # 写入到容器内的环境容量
- name: MYSQL_ROOT_PASSWORD # 定义了一个mysql的root密码的变量
value: "root"
volumes: # 本地需要挂载到容器里的数据卷定义部分
- name: time-zone # 数据卷名称,需要与容器内挂载点名称一致
hostPath:
path: /etc/localtime # 挂载到容器里的路径,将localtime文件挂载到容器里,可让容器使用本地的时区
- name: mysql-data
hostPath:
path: /data/mysql/data # 本地存放mysql数据的目录
- name: mysql-logs
hostPath:
path: /data/mysql/logs # 本地存入mysql日志的目录
在编写一个对外提供服务的 mysql-svc.yaml
apiVersion: v1
kind: Service
metadata:
name: mysql
labels:
name: mysql
spec:
type: NodePort
ports:
- port: 3306
targetPort: 3306
nodePort: 30001
selector:
app: mysql
创建服务
kubectl create -f mysql-deploy.yaml
kubectl create -f mysql-svc.yaml
查看节点是否正常运行
$ kubectl get pod
NAME READY STATUS RESTARTS AGE
mysql-566cddf86-v65mw 1/1 Running 2 (15h ago) 2d1h
访问数据库,密码的话,刚有在 yaml 中说明,为 root
,登陆成功后即可输入数据
kubectl exec -it mysql-566cddf86-v65mw -- mysql -u root -p
开放远程连接权限
FLUSH PRIVILEGES;
/* mysql8.0 只能以这种方式来赋权*/
alter user 'root'@'%' identified with mysql_native_password by 'root';
GRANT SELECT, INSERT, UPDATE, DELETE ON *.* TO 'root'@'%';
flush privileges;
然后 node1 节点要开放 3306 端口和 30001 端口 (刚刚设置的对外开放的端口),可以在宿主机连接集群的数据库
mysql -u root -h 192.168.22.223 -P 30001 -p
3. 部署 gin 服务
BuyHouse: 一个简单的gin+MySQL数据查询系统,课程实训项目 (gitee.com)
这里使用了学校实训项目的一个 demo,这里我只用到了 gin 部分。
制作镜像以前,我们先看看集群里 MySQL 的 ip
看到 mysql 集群 ip 为 22.22.166.184
,那么 gin 里数据库配置 (database/mysql.go
) 的 ip 也要改成 22.22.166.184
首先是把项目打包成 docker image,在项目根目录 (go.mod
所在目录),编写 Dockerfile
FROM golang:1.18-alpine AS builder
WORKDIR /app
COPY . /app
RUN go env -w GO111MODULE=on
RUN go env -w GOPROXY=https://goproxy.cn,direct
RUN CGO_ENABLED=0 go build -ldflags "-s -w" -o app
FROM alpine AS runner
WORKDIR /app
COPY --from=builder /app/app .
EXPOSE 9999:9999
ENTRYPOINT ["./app"]
这里使用多级构建 (实际也就两层,太懒了,不想搞太多了,十多兆已经是我可以接受的大小了 doge),如果不这样做,构建出的镜像差不多 1G,不论是推送到仓库还是拉取,都会很影响效率。
然后执行
docker build -t buy-house .
docker 就会拉取、打包镜像,用 docker images
可以查看多了一个 buy-house
的镜像,如果想要推送到个人仓库的话,执行
docker tag buy-house jaydenchang/buy-house
然后在 Docker 客户端 (已登陆了个人账号) 推送即可。
然后编写 go-deploy.yaml
,这里我使用自己制作的镜像,并上传到了个人仓库
apiVersion: apps/v1
kind: Deployment
metadata:
name: go-deployment
labels:
app: go
spec:
selector:
matchLabels:
app: go
replicas: 2
minReadySeconds: 5
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
template:
metadata:
labels:
app: go
spec:
containers:
- image: jaydenchang/buy-house:latest
name: go
imagePullPolicy: Always
command: ["./app","-v","v1.3"]
ports:
- containerPort: 9999
protocol: TCP
编写 go-svc.yaml
apiVersion: v1
kind: Service
metadata:
name: go-service
labels:
app: go
spec:
selector:
app: go
ports:
- name: go-port
protocol: TCP
port: 9999
targetPort: 9999
nodePort: 31080
type: NodePort
生成节点
kubectl create -f go-svc.yaml
kubectl create -f go-deploy.yaml
检查一下
$ kubectl get pod
NAME READY STATUS RESTARTS AGE
busybox 1/1 Running 2 (14m ago) 173m
go-deployment-66878c4885-twmzs 1/1 Running 0 29s
go-deployment-66878c4885-zxjqb 1/1 Running 0 29s
mysql-566cddf86-v65mw 1/1 Running 3 (14m ago) 2d2h
go 的镜像被分配到了 node1 节点,我们输入 node1IP:port
,也就是 192.168.22.223:31080
其他接口就不测试了 (一个拿不出眼的小项目就不展示太多了)。至此,整个部署过程结束。这次小试牛刀,搭建一个比较简易的双节点集群。未来的学习,可能会尝试更复杂的集群部署 (先给自己挖个坑吧)。
参考链接
ubuntu 运行 apt-get update 时阿里云 k8s 安装源报错_已解决_博问_博客园 (cnblogs.com)
一个k8s集群——跨云服务器部署_k8s跨云部署_qq_43285879的博客-CSDN博客
kanzihuang/kubespray-extranet: Create a kubernetes cluster on the public network (github.com)
跨VPC或者跨云供应商搭建K8S集群 - Search (bing.com)
公网环境搭建k8s集群 - ttlv - 博客园 (cnblogs.com)
Kubernetes(k8s)安装以及搭建k8s-Dashboard详解 - 掘金 (juejin.cn)
Kubernetes 1.27 快速安装手册 - 知乎 (zhihu.com)
基于Ubuntu-22.04 kubeadm安装K8s-v1.25.0 | Marshall's blog (aledk.com)
k8s 初始化master节点时无calico,coredns一直是pending状态_calico pending_copa~的博客-CSDN博客
K8s部署自己的web项目_k8s 前端_肖仙女hhh的博客-CSDN博客
公网创建 kubernetes 集群的解决方案 · GitHub
在Linux公网、云服务器搭建K8s集群 - 知乎 (zhihu.com)
(43条消息) kubernetes集群部署nginx应用服务_kubernetes部署nginx_鱼大虾的博客-CSDN博客
k8s集群部署mysql完整过程记录 - blayn - 博客园 (cnblogs.com)