目标
- 搭建一个包含3个节点的 k8s 集群
- 部署几个基础服务,包括 nginx、mysql
操作环境准备
- 安装 VirtualBox
- 创建 3 个虚拟机实例,使用的镜像为 ubuntun-server(使用桥接网络模式)
-
(可选)配置 SSH 免密登录虚拟机
-
检查宿主机和三个虚拟机的IP地址,如下:
| 主机名称 | IP | 备注 |
|---|---|---|
| host | 192.168.1.8 | 宿主机 WSL,通过SSH登录操作虚拟机 |
| usvc2 | 192.168.1.9 | 后面在该节点初始化控制面 |
| usvc3 | 192.168.1.10 | 工作节点 |
| usvc4 | 192.168.1.11 | 工作节点 |
部署 k8s 集群
配置节点环境(重要)
3 个 ubuntu-server 节点都需要进行配置。
参考 kubernetes 文档中的安装和配置先决条件,主要操作就是:
- 启用 ipv4 数据包转发
- 安装容器运行时
- 检查和配置容器运行时使用的 cgroup 驱动
启用 ipv4 数据包转发,在我的操作环境下,因为缺少必要的内核模块,完整配置过程如下:
cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF
sudo modprobe overlay
sudo modprobe br_netfilter
# 设置所需的 sysctl 参数,参数在重新启动后保持不变
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
# 应用 sysctl 参数而不重新启动
sudo sysctl --system
安装 docker engine 过程参考 Install Docker Engine on Ubuntu
在我的操作环境中,安装了最新的 docker engine, 里面已经包含了 containerd 作为具体 CRI,但是需要修改配置文件以启用 CRI 插件。另外,由于较新的 kubernetes 版本的 kubelet 都默认使用了 systemd 作为 cgroup 驱动,所以为了保持一致,同样需要修改 containerd 的配置文件以使用 systemd 作为 cgroup 驱动。
# 因为默认的config.toml包含的内容不全,所以这里删除后重新生成。
sudo rm /etc/containerd/config.toml
containerd config default > config.toml
sudo mv config.toml /etc/containerd/
查看并修改 /etc/containerd/config.toml。因为新生成的配置文件已经开启了 cri, 所以这里要修改的点只有两个。
[plugins."io.containerd.grpc.v1.cri"]
device_ownership_from_security_context = false
disable_apparmor = false
disable_cgroup = false
disable_hugetlb_controller = true
disable_proc_mount = false
disable_tcp_service = true
drain_exec_sync_io_timeout = "0s"
enable_selinux = false
enable_tls_streaming = false
enable_unprivileged_icmp = false
enable_unprivileged_ports = false
ignore_deprecation_warnings = []
ignore_image_defined_volumes = false
max_concurrent_downloads = 3
max_container_log_line_size = 16384
netns_mounts_under_state_dir = false
restrict_oom_score_adj = false
sandbox_image = "registry.k8s.io/pause:3.6" # 这里改为"registry.cn-hangzhou.aliyuncs.com/google_containers/pause:3.9",注意版本号要和kubelet使用的pause镜像版本一致.
selinux_category_range = 1024
stats_collect_period = 10
stream_idle_timeout = "4h0m0s"
stream_server_address = "127.0.0.1"
stream_server_port = "0"
systemd_cgroup = false
tolerate_missing_hugetlb_controller = true
unset_seccomp_profile = ""
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
BinaryName = ""
CriuImagePath = ""
CriuPath = ""
CriuWorkPath = ""
IoGid = 0
IoUid = 0
NoNewKeyring = false
NoPivotRoot = false
Root = ""
ShimCgroup = ""
SystemdCgroup = false # 这里改成true,表示使用 systemd 作为 cgroup 驱动
修改后重启 containerd:
sudo systemctl daemon-reload
sudo systemctl restart containerd
安装kubeadm
我这里使用的 kubernetes 版本是 v1.29,按照官方的教程操作下来比较顺利。
初始化控制平面节点
查看kubeadm 的默认初始化配置,并写入到文件:
kubeadm config print init-defaults > init-defaults.yaml
这里需要对生成的配置文件进行部分修改,然后用修改后的文件执行初始化操作。
apiVersion: kubeadm.k8s.io/v1beta3
bootstrapTokens:
- groups:
- system:bootstrappers:kubeadm:default-node-token
token: abcdef.0123456789abcdef
ttl: 24h0m0s
usages:
- signing
- authentication
kind: InitConfiguration
localAPIEndpoint:
advertiseAddress: 192.168.1.9 # 这里改成控制平面节点的IP
bindPort: 6443
nodeRegistration:
criSocket: unix:///var/run/containerd/containerd.sock
imagePullPolicy: IfNotPresent
name: node
taints: null
---
apiServer:
timeoutForControlPlane: 4m0s
apiVersion: kubeadm.k8s.io/v1beta3
certificatesDir: /etc/kubernetes/pki
clusterName: kubernetes
controllerManager: {}
dns: {}
etcd:
local:
dataDir: /var/lib/etcd
imageRepository: registry.cn-hangzhou.aliyuncs.com/google_containers # 这里改成国内镜像地址
kind: ClusterConfiguration
kubernetesVersion: 1.29.0
networking:
dnsDomain: cluster.local
serviceSubnet: 10.96.0.0/12
podSubnet: 10.244.0.0/16 # 这里添加该配置,方便后面直接安装 Pod 网络插件 Flannel
scheduler: {}
执行命令,初始化控制平面:
sudo kubeadm init --config init-defaults.yaml
不出意外执行成功!
这里按照提示拷贝配置文件即可。
安装 Pod 网络插件
其实就是执行如下的命令:
kubectl apply -f https://github.com/flannel-io/flannel/releases/latest/download/kube-flannel.yml
安装存储插件
也可以选择其它的存储插件,这个部署比较简单。
加入工作节点
经过前面的操作,k8s集群的控制平面节点已经初始化完成,并且安装了网络和存储插件,接下来就需要向集群添加两个工作节点了。
按照官方教程操作比较简单,完成后回到控制平面节点,执行 kubectl get nodes 就可以看到集群内存在3个节点了。
部署应用
部署 nginx
需要创建如下资源:
- Deployment
- Service,使用 NodePort 方式,方便外部访问
- ConfigMap,用于管理 nginx 的配置
nginx-cm.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: nginx-cm
data:
default.conf: |
server {
listen 80;
server_name localhost;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
location /nginx-status {
stub_status;
access_log off;
allow 127.0.0.1;
deny all;
}
}
nginx-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 2
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:stable
ports:
- containerPort: 80
volumeMounts:
- name: nginx-config
mountPath: /etc/nginx/conf.d
volumes:
- name: nginx-config
configMap:
name: nginx-cm
nginx-service.yaml
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
type: NodePort
selector:
app : nginx
ports:
- name: http
protocol: TCP
port: 80
targetPort: 80
上面通过 ConfigMap 来管理 nginx 的配置,会在容器的 /etc/nginx/conf.d 目录下生成一个default.conf 文件,并且通过 kubectl edit cm nginx-cm 修改 ConfigMap 后,容器内的配置会自动更新。用法参考 在Pod中将ConfigMap当作文件使用。
查看节点地址:
kubectl get nodes -o wide
查看Service在节点上的分配的端口:
kubectl get svc
访问 nginx:http://<node-id>:<node-port>/ 验证部署成功。
部署 mysql
这里演示创建一个一主两从的 MySQL 服务,拓扑结构如下:
graph TD
M[Master] -->|Replication| R1[Replica 1]
M[Master] -->|Replication| R2[Replica 2]
显然这是一个有状态的应用(节点拓扑状态、存储状态),所以需要使用 StatefuSet 的方式来部署。整个容器化部署过程中涉及到以下几个关键点:
- 需要区分 Pod 的身份是主节点还是从节点,进行对应的配置
- Pod 必须按顺序初始化和启动,以配置复制关系
- 如果是从节点,Pod 第一次启动时需要先进行数据备份后设置同步位置,接着从 Master 节点开始同步数据(replica1 从 host 备份,replica2 从 replica1 备份);否则,直接从之前的同步位置继续同步。可以根据是否存在数据来判断是否为初次启动。
完整过程参考运行一个有状态的应用
虚拟机管理
目的是为了方便批量启动/关闭所有的虚拟机。
创建vms.bat 文件,底层调用 VBoxManage 命令来管理虚拟机,需要将 VBoxMange 命令添加到 Path。
使用方式:
vms.bat list // 查看所有的虚拟机
vms.bat start // 启动所有的虚拟机
vms.bat stop // 关闭所有打开的虚拟机
@echo off
REM 检查是否提供了操作参数
IF [%1] == [] (
echo Usage: %0 list^|start^|stop
exit /b
)
REM 根据提供的参数执行相应的操作
IF /I [%1] == [list] (
echo Listing all virtual machines...
VBoxManage list vms
) ELSE IF /I [%1] == [start] (
echo Starting all virtual machines...
for /f "tokens=1*" %%a in ('VBoxManage list vms') do (
VBoxManage startvm %%a --type headless
)
) ELSE IF /I [%1] == [stop] (
echo Stopping all running virtual machines...
for /f "tokens=1*" %%a in ('VBoxManage list runningvms') do (
VBoxManage controlvm %%a acpipowerbutton
)
) ELSE (
echo Invalid option: %1
echo Usage: %0 list^|start^|stop
exit /b
)
注意:因为这里批量启动了虚拟机后集群节点并不会达到 Ready 的状态,原因是没有进行登录。为了方便,可以将每个虚拟机设置为自动登录。
参考 Enable Autologin in Ubuntu Server from Commandline