从零开始搞个简易分布式部署环境
从零开始,意味着连个服务器都没有,所以第一步,随便上哪个顺眼的云厂家去租个便宜大碗的服务器(不要window系统的就行),说大碗也不太对,主要是这碗能在手里用得久,这个就自己扒拉去了。
简易,意味着这个分布式环境都是在这个服务器里用容器搭出来的自娱自乐的环境,跟实际正经的由不同主机组成的分布式环境依然有相当大的区别,最大的区别就是:省钱!
至于为什么不在自己电脑上搞虚拟机双系统啥的去模拟,我只能说我自己的破烂电脑背上这么多负担还真不一定比租个云服务器便宜方便,如果你电脑配置很牛逼,那当我没说。
那么,就开始从零开始了。
这个分布式环境都需要什么?
首先,要有一个应用,如果是搞Java开发的,大概率就会是个springboot应用,那么随便写一个返回 hello world 的 controller 再写好 Application 最后打包成一个 jar,这就是第一个需要的东西。
这一步是在自己本机完成的,也是本文中本机唯一做的事,接下来的其他步骤就全是在租下来的云服务器上的操作了。
云服务器环境准备
首先,更新服务器的 apt 或者 yum 或其他什么都好的安装工具,因为不更新的话有很大概率你啥都安装不了(苦涩)。
sudo apt update && sudo apt upgrade
# 或者
sudo yum update
# 下面各种乱七八糟的安装就用 apt 的写了,yum 的也一样,就这三个字母的差别。
然后,下载 docker-ce ,注意这个后缀,带此后缀的是 docker 的社区版,免费开源,更新频繁(每月发布新版本),适合个人开发者和小团队使用。
apt install -y docker-ce docker-ce-cli containerd.io
在这个环节,大概率会遇到:
- yum 版本太低,docker-ce没法下载,忽悠你下载 docker
解: yum install -y yum-utils
把Red Hat Enterprise Linux(RHEL)或 CentOS 的系统上可能缺失(因为系统已经被改成了阿里云的系统)的 yum-utils 下载回来,这是个包管理工具。
- 没配置下载仓库
解: yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
记得用国内镜像,docker 官方镜像容易超时连接失败。
- 下载完docker命令不生效
解:systemctl enable docker
检查 docker 是否可用,systemctl start docker
启动docker。
- 其他
解:未完待续……
大部分问题善用搜索引擎或描述给人机师傅基本也能得到解决。这里就只记录我自己遇到过的。
JDK下载与容器创建
在 docker 安装完后,就开始下载 jdk 了。我准备测试三个jdk环境:8、11、17。
# 下载解压jdk8,下面的 11 和 17 也是相同的命令
wget -qO- https://download.java.net/openjdk/jdk8u42/ri/openjdk-8u42-b03-linux-x64-14_jul_2022.tar.gz | tar -xz
wget -qO- https://download.java.net/openjdk/jdk11/ri/openjdk-11+28_linux-x64_bin.tar.gz | tar -xz
wget -qO- https://download.java.net/openjdk/jdk17/ri/openjdk-17+35_linux-x64_bin.tar.gz | tar -xz
这三个jdk需要放在单独的文件夹下,因为等会儿创建容器的时候要把这个路径映射到容器里,方便日后容器内测试不同版本jdk。这里也先提前写一个切换环境变量的脚本放在同级文件夹下,这样容器内就可以直接操作使用。
#!/bin/bash# 检查是否传入参数(JDK版本号)
if [ $# -eq 0 ]; thenecho "错误:请指定JDK版本号(如8、11、17等)"echo "用法:./set_java.sh <版本号>"exit 1
fi# 设置JAVA_HOME和PATH
export JAVA_HOME="/opt/java/jdk$1" # 使用第一个参数$1替换版本号
export PATH="$JAVA_HOME/bin:$PATH"# 验证Java版本
java -version
经测试,容器内使用这个脚本需要用source
而不是bash
执行才能顺利地修改容器内的 Java 环境,有些教程会让写成系统命令,配置成类似 systemctl 那样的,但我这边用了下不太行得通。而用bash
执行会创建一个隔离的shell环境,导致切换环境变量的操作只在这次执行中生效,当执行完毕想再用 java -version 确认版本会发现依然是旧的jdk。
脚本准备好,接下来就是创建容器了。
# 容器创建
docker run -itd --name apps \# 这里依次映射了三个版本的jdk路径-v /home/workspace/java/jdk8:/opt/java/jdk8 \-v /home/workspace/java/jdk11:/opt/java/jdk11 \-v /home/workspace/java/jdk17:/opt/java/jdk17 \# 也可以直接把上层文件夹映射到容器里-v /home/workspace/java:/opt/java \# 这里是要部署的应用的映射地址-v /home/workspace/app1/app1:/data/app1/ \# 预先配置容器创建时的jdk版本-e JAVA_HOME=/opt/java/jdk8 \-e PATH=/opt/java/jdk/bin:$PATH \# 映射端口取8085,也可以用常用的80和8080-p 8085:8085 \# 使用ubuntu20.04镜像ubuntu:20.04 /bin/bash
MySQL容器创建
MySQL同样弄了两个版本,一个是最常用的5.7,一个是MySQL8,也是纯测试用。
由于我租的服务器资源有限,这里显式指定了容器的内存(512 M)和可用的 cpu (0.5个核的算力),防止日后占用过多资源。
# 5.7
docker run -d --name mysql57 \-e MYSQL_ROOT_PASSWORD=root \# 将容器的3306映射到宿主机的3306,方便以后用工具连接查看-p 3306:3306 \-v /home/workspace/mysql/mysql57:/var/lib/mysql \--memory="512m" \--cpus="0.5" \mysql:5.7# 8.0
docker run -d --name mysql80 \-e MYSQL_ROOT_PASSWORD=root \# 将容器的3306映射到宿主机的3307,方便以后用工具连接查看-p 3307:3306 \-v /home/workspace/mysql/mysql80:/var/lib/mysql \--memory="512m" \--cpus="0.5" \mysql:8.0
--memory="512m"
是容器内所有进程(包括MySQL及其子进程)的总内存上限,当容器的内存使用量超过 512 MB 时,Docker 会触发内存不足终止机制(OOM Killer),强制终止容器进程(显示为容器崩溃或退出)。
这个限制仅包括物理内存,不包括交换分区(Swap)。如需限制交换分区,需额外配置 --memory-swap
。
交换分区(Swap Space) 是什么?
是操作系统(如 Linux)中一种特殊的磁盘空间,用于在物理内存(RAM)不足时,临时存储被换出的内存页(即不活跃的进程数据)。它的核心作用是 扩展虚拟内存 ,防止系统因内存耗尽而崩溃。
docker 的 memory-swap 需要结合宿主机的 swap 配置启用,也需要这个容器已经配置了
--memory
。相关配置在官方文档中可以找到说明:https://docs.docker.com/engine/containers/resource_constraints/#–memory-swap-details
MySQL 8.0 默认配置可能默认需要较多内存(如 innodb_buffer_pool_size
默认 128MB),建议根据业务调整 MySQL 配置以避免内存不足。
可通过 docker stats <容器名>
实时监控容器的内存和 CPU 使用情况。
Redis 和 RabbitMQ 容器创建
如果你的应用没用到这两个,没关系,早晚会用到的(
# Redis 创建,同样限制了最大可用内存和 CPU 计算资源
docker run -d --name redis \-p 6379:6379 \--memory 100m \--cpus 0.1 \redis:alpine# RabbitMQ 创建,同样限制了最大可用内存和 CPU 计算资源
docker run -d --name rabbitmq \-p 15672:15672 \--memory 200m \--cpus 0.2 \rabbitmq:management-alpine
nginx 配置
nginx 基本就是配置文件的编写。
docker run -d --name nginx-proxy \--network my-network \-p 80:80 \-p 443:443 \-v /home/workspace/nginx/:/home/workspace/nginx \-v /home/workspace/nginx/nginx.conf:/etc/nginx/nginx.conf \nginx
--network
是 docker 网络的配置,这点后面会说,并且前面几个容器也需要补充配置,好在不需要删除容器,有单独将容器添加到网络里的命令。
nginx 绑定的配置文件里需要下面这些东西。
// nginx 的配置文件其实不是json格式,但代码块用json有比较方便的颜色区分……
events {worker_connections 1024;
}http {server {listen 80;# 监听端口server_name 云服务器IP;// 下面这个 // 不是注释的意思,别删错了return 301 https://$host$request_uri;}server{listen 443 ssl;server_name 云服务器IP;//这两个是ssl证书地址,有专门用于测试的获取渠道//正规的一般需要付费购买,且前提是有域名ssl_certificate /home/workspace/nginx/ssl/cert.pem;ssl_certificate_key /home/workspace/nginx/ssl/key.pem;ssl_protocols TLSv1.2 TLSv1.3;ssl_ciphers HIGH:!aNULL:!MD5;location / {proxy_pass http://apps(部署应用的容器名):8085;proxy_set_header Host $host;proxy_set_header X-Real-IP $remote_addr;proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;proxy_set_header X-Forwarded-Proto $scheme;}}
}
本地创建ssl测试证书的命令是:
openssl req -x509 -newkey rsa:4096 -nodes -out cert.pem -keyout key.pem -days 365
,生成的两个文件放到前面配置的 ssl_certificate 和 ssl_certificate_key 的路径下就行。
上面的 nginx 配置实际上只是转发云服务器的80端口到指定的有且只有一个的应用容器上,但在分布式情况下,应用容器不会只有一个,因此你需要的是下面字更多的版本。
events {worker_connections 1024;
}http {upstream app_servers{server apps1:8085;server apps2:8085;server apps3:8085;}server {listen 80;server_name 云服务器IP;// 下面这个 // 不是注释的意思,别删错了return 301 https://$host$request_uri;}server{listen 443 ssl;server_name 云服务器IP;ssl_certificate /home/workspace/nginx/ssl/cert.pem;ssl_certificate_key /home/workspace/nginx/ssl/key.pem;ssl_protocols TLSv1.2 TLSv1.3;ssl_ciphers HIGH:!aNULL:!MD5;location / {// 下面这个 // 不是注释的意思,别删错了proxy_pass http://app_servers;proxy_set_header Host $host;proxy_set_header X-Real-IP $remote_addr;proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;proxy_set_header X-Forwarded-Proto $scheme;}}
}
nginx 的配置文件修改后要生效只需要简单地重启容器docker start nginx-proxy
到这里一个极简的分布式环境差不多就搞好了,可以开始玩了(。