移动云ECS初始化、系统配置、中间件和应用部署、网络架构文档 这篇文档以移动云中的几台ECS为例, 记录了ECS的初始化、中间件和应用部署的示例、ECS集群搭建的实例、还有负载均衡的配置实例
0x00 必备组件安装和初始化 在一切开始之前, 首先初始化移动云的云公网IP和NAT配置, 具体参考NAT网关和弹性公网IP配置
0x00-0 初始化额外数据盘 以其中一台ECS为例, 操作系统 Ubuntu2204 LTS, 有100G系统盘和额外挂载的500G云硬盘
移动云控制台显示硬盘已经挂载但未被初始化, 使用fdisk -l
校验
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 root@ecs-hykg-app:/# fdisk -l Disk /dev/sda: 100 GiB, 107374182400 bytes, 209715200 sectors Disk model: QEMU HARDDISK Units: sectors of 1 * 512 = 512 bytes Sector size (logical/physical): 512 bytes / 512 bytes I/O size (minimum/optimal): 512 bytes / 512 bytes Disklabel type : dos Disk identifier: 0x65f2787d Device Boot Start End Sectors Size Id Type /dev/sda1 * 2048 209715166 209713119 100G 83 Linux Disk /dev/sdb: 500 GiB, 536870912000 bytes, 1048576000 sectors Disk model: QEMU HARDDISK Units: sectors of 1 * 512 = 512 bytes Sector size (logical/physical): 512 bytes / 512 bytes I/O size (minimum/optimal): 512 bytes / 512 bytes
对硬盘进行分区, fdisk /dev/sdb
, 系列命令如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 root@ecs-hykg-app:/# fdisk /dev/sdb Welcome to fdisk (util-linux 2.37.2). Changes will remain in memory only, until you decide to write them. Be careful before using the write command . Device does not contain a recognized partition table. Created a new DOS disklabel with disk identifier 0xd9d54c85. Command (m for help ): n Partition type p primary (0 primary, 0 extended, 4 free) e extended (container for logical partitions) Select (default p): p Partition number (1-4, default 1): 1 First sector (2048-1048575999, default 2048): Last sector, +/-sectors or +/-size{K,M,G,T,P} (2048-1048575999, default 1048575999): Created a new partition 1 of type 'Linux' and of size 500 GiB. Command (m for help ): p Disk /dev/sdb: 500 GiB, 536870912000 bytes, 1048576000 sectors Disk model: QEMU HARDDISK Units: sectors of 1 * 512 = 512 bytes Sector size (logical/physical): 512 bytes / 512 bytes I/O size (minimum/optimal): 512 bytes / 512 bytes Disklabel type : dos Disk identifier: 0xd9d54c85 Device Boot Start End Sectors Size Id Type /dev/sdb1 2048 1048575999 1048573952 500G 83 Linux Command (m for help ): wq The partition table has been altered. Calling ioctl() to re-read partition table. Syncing disks.
创建挂载点: mkdir /data
磁盘格式化为ext4
: mkfs.ext4 /dev/sdb1
挂载: mount /dev/sdb1 /data/
持久化挂载, 避免重启后失效 : 需要将配置文件写入fstab
获取需要操作盘的UUID
信息
1 2 blkid /dev/sdb1 /dev/sdb1: UUID="f704f222-ba2d-41df-b1d8-f31715d9ba67" BLOCK_SIZE="4096" TYPE="ext4" PARTUUID="d9d54c85-01"
编辑/etc/fstab
文件
1 2 3 vim /etc/fstab UUID=f704f222-ba2d-41df-b1d8-f31715d9ba67 /data ext4 defaults 1 1
大容量硬盘可以使用parted
分区, 使用GPT
格式分区表
完整教程参考移动云磁盘初始化指南
或者使用初始化脚本LinuxVMDiskAutoInitialize2.sh
0x00-1 初始化SSH允许远程登录 安装 openssh-server 服务, 配置允许 root 用户远程登录, 使用强密码, 监听 22 端口
由于需要映射 SSH 服务到公网, 密码验证的安全性较差, 所以需要额外配置安全组规则, 使用白名单模式只放行 hysz 的公网IP
0x01 ECS-HYKG-APP的中间件和应用部署示例 以ECS-HYKG-APP
这台ECS为例, 介绍下主应用服务器的部署逻辑
0x01-0 直接部署服务——以Nacos的部署为例 最主要需要注意的事项是相关部署的文件或者程序需要在/home/
归档, 便于以后查阅
实机部署Nacos 2.0.3
, 可以参考Nacos官方文档
部署程序依赖的依赖的JDK 1.8+
环境, 验证安装
1 2 3 4 5 6 7 8 root@ecs-hykg-app:~# apt install openjdk-8-jdk root@ecs-hykg-app:~# which java /usr/bin/java root@ecs-hykg-app:~# java -version openjdk version "1.8.0_422" OpenJDK Runtime Environment (build 1.8.0_422-8u422-b05-1~22.04-b05) OpenJDK 64-Bit Server VM (build 25.422-b05, mixed mode)
下载 Nacos 安装包, 安装到/opt/nacos-2.0.3
路径, 软链接到/home
使用程序中自带的SQL Schema
脚本初始化 MySQL 数据库, 修改 Nacos 配置文件, 配置数据库的连接
1 2 3 4 5 6 7 8 9 10 11 12 # 数据库连接信息配置 #*************** Config Module Related Configurations ***************# ### If use MySQL as datasource: spring.datasource.platform=mysql ### Count of DB: db.num=1 ### Connect URL of DB: db.url.0=jdbc:mysql://ip:port/nacos?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC db.user.0=nacos db.password.0=nacos
启动 Nacos 服务, 验证 8848 端口是否被 java 程序监听, 即可判断是否启动成功
1 2 3 cd /home/nacos-2.0.3/bi./startup.sh -m standalone netstat -tulnp | grep 8848
如果需要集群话部署, 可以参考基于Docker的单节点部署集群模式Nacos服务
0x01-1 通过容器部署后端服务——以OA服务为例 首先是完成 Docker 的安装, 可以使用运维脚本InstallDocker.sh 安装Docker及相关组件
Docker数据目录的迁移 之前初始化了数据盘, 现在需要将 Docker 等存储空间占用较大的程序或日志放置在数据盘 中
找到 Docker 的配置文件daemon.json
, 修改data-root
的配置
1 2 3 4 5 vim /etc/docker/daemon.json { "data-root" : "/data/docker" } chmod -R 755 /data/docker
配置镜像源的授权 找到 Docker 的授权文件config.json
, 修改auths
选项
1 2 3 4 5 6 7 { "auths" : { "harbor.domain.com" : { "auth" : "YourAuthToken" } } }
配置完成就重启守护进程和 Docker 服务
1 2 systemctl daemon-reload systemctl restart docker
docker-compose的部署 根据系统架构在 Github 拉取最新的Release
1 2 3 cd /usr/local/binwget https://github.com/docker/compose/releases/download/v2.32.0/docker-compose-linux-x86_64 mv docker-compose-linux-x86_64 docker-compose && chmod +x docker-compose
在构建docker-compose
应用时, 记得也将日志目录映射到数据盘/data
目录中
编译并部署OA服务 Jenkins的构建脚本配置 首先使用 Maven 工具将源码编译成一个 Jar 包, 然后基于 JDK8 的基础镜像构建后端服务, Jenkins构建脚本如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 # !/bin/bash set -exu cat << EOF > Dockerfile FROM alpine_jdk8u301:v1 LABEL org.opencontainers.image.authors="songdaoyuan@focus-in.cn" ENV TZ=Asia/Shanghai ENV LANG=zh_CN.UTF-8 WORKDIR /project COPY $target $JOB_NAME.jar CMD java -server -Xms1g -Xmx2g -Dserver.port=8080 -jar $JOB_NAME.jar --spring.profiles.active=prod EXPOSE 8080 EOF # 推送到 harbor 仓库 # 同时推送版本号标签和latest标签 docker build -t harbor.domain.com/hykg/$JOB_NAME:v$BUILD_NUMBER . docker tag harbor.domain.com/hykg/$JOB_NAME:v$BUILD_NUMBER harbor.domain.com/hykg/$JOB_NAME:latest docker push harbor.domain.com/hykg/$JOB_NAME:v$BUILD_NUMBER docker push harbor.domain.com/hykg/$JOB_NAME:latest docker rmi harbor.domain.com/hykg/$JOB_NAME:v$BUILD_NUMBER docker rmi harbor.domain.com/hykg/$JOB_NAME:latest # $ECLOUD_HYKG_APP_HOST 实际指向移动云的NAT-弹性IP, 通过端口可以免密连接到不同的服务器ssh -p 10022 root@$ECLOUD_HYKG_APP_HOST "cd /home/docker-compose/$JOB_NAME/ && bash update.sh"
构建完成后镜像被推送到 harbor 仓库, 最后一次构建完成的镜像会被打上版本标签 v$BUILD_NUMBER
和 latest
docker-compose.yml的和update.sh的配置 这两个文件和Jenkins构建脚本一起实现了全自动构建与更新 的流程
docker-compose.yml 的内容
在服务器的/home/docker-compose/$JOB_NAME/
路径存放着docker-compose.yml
文件和控制镜像更新的update.sh
脚本, 这里以OA中的prod-basic-gateway
服务为例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 version: "2" services: prod-basic-gateway: image: harbor.domain.com/hykg/prod-basic-gateway:latest container_name: prod-basic-gateway restart: always ports: - "8081:8080" volumes: - "/data/logs/focusin-log/prod:/var/logs/focusin/" networks: - docker-network networks: docker-network: external: true
可以看到容器使用了预先创建好的一个名为 docker-network 的桥接网络, 组建了容器间的大内网环境, 日志则持久化到数据盘中
这里可以考虑兼容运维习惯, 将/data/logs/
软链接到/var/log
目录
update.sh 的内容
1 2 3 4 5 6 #!/bin/bash echo "正在更新 192.168.0.11:8081 网关服务 镜像" docker pull harbor.domain.com/hykg/prod-basic-gateway:latest docker-compose down && docker-compose up -d docker system prune -f
运行脚本后会首先拉取标签为latest
的镜像, 可以保证每次都能获取到最新版本的镜像, 然后重启并且应用新的镜像, 最后使用docker system prune -f
可以删除旧的镜像
0x01-2 配置日志映射服务 部署Promtail+Loki+Grafana (PLG架构)的日志服务 这个是推荐的方案, 可以参考之前的文章部署Promtail+Loki+Grafana (PLG架构)的日志服务
快速搭建一个HTTP服务器来应急使用 在特定路径启用一个基于 python 的 http 服务器, 监听 9988 端口, 然后在 NG 上配置反向代理, 通过公网 IP 加端口即可看到日志
1 2 cd /data/logs/focusin-log/prodpython3 -m http.server 9988
0x02 ECS-Zookeeper-0x集群部署示例 以ECS-Zookeeper-0x
这三台ECS为例, 介绍下基于 Docker 的集群部署
参考文档ZK集群部署
0x02-0 配置节点的hosts解析 编辑/etc/hosts
文件, 将 IP 和节点简称写入文件, 便于后续的节点发现和通信, 下面的命令在三台主机上都需要执行
1 2 3 4 5 6 7 8 9 10 11 12 13 vim /etc/hosts 192.168.0.18 ecs-zookeeper-01 192.168.0.10 ecs-zookeeper-02 192.168.0.16 ecs-zookeeper-03 192.168.0.18 ecs-zk-01 192.168.0.10 ecs-zk-02 192.168.0.16 ecs-zk-03 192.168.0.18 zk-01 192.168.0.10 zk-02 192.168.0.16 zk-03
0x02-1 配置节点之间的SSH免密登录 三台主机之间需要配置SSH免密登录, 便于交换文件和信息, 下面的命令在三台主机上都需要执行
1 2 3 4 ssh-keygen -t rsa ssh-copy-id zk-01 ssh-copy-id zk-02 ssh-copy-id zk-03
0x02-2 使用容器部署Zookeeper服务 安装配置Docker 参考上面的 Docker 部署流程, 部署完成后拉取镜像备用
1 docker pull harbor.domain.com/hykg/zookeeper:3.5.10
Zookeeper配置文件的持久化挂载 主机上/home/
建立挂载目录和 zookeeper 配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 mkdir -p /home/zookeeper-3.5.10/confmkdir -p /home/zookeeper-3.5.10/datacd /home/zookeeper-3.5.10/confvim zoo.cfg clientPort=2181 dataDir=/data dataLogDir=/data/log tickTime=2000 initLimit=5 syncLimit=2 autopurge.snapRetainCount=3 autopurge.purgeInterval=0 maxClientCnxns=60 server.1=192.168.0.18:2888:3888 server.2=192.168.0.10:2888:3888 server.3=192.168.0.16:2888:3888 cd ../data/touch myid && echo 1 > myid
容器的启动和停止脚本 启动脚本RunZooKeeperCluster.sh
1 2 3 4 vim RunZooKeeperCluster.sh docker run --network host -v /home/zookeeper-3.5.10/data:/data -v /home/zookeeper-3.5.10/conf:/conf --name zookeeper -d harbor.domain.com/hykg/zookeeper:3.5.10
!!!注意 这里必须使用 host 网络模式, 很重要!!! 不清楚就查阅 docker 中网络模式的区别
停止脚本StopZooKeeperCluster.sh
1 2 3 4 vim StopZooKeeperCluster.sh docker stop zookeeper
在三台 ECS 上依次启动容器, 等待一段时间后自动组成 Zookeeper 集群
0x03 ECS-Nginx-Master/Slave/Web的部署 以ECS-Nginx-Master
这台 ECS 为例, 介绍下 NGINX 的部署流程
0x03-0 编译所需包的准备 由于需要一些额外的功能, 这里选择了通过编译安装NGINX 1.20.2
体检将ngx_http_proxy_connect_module-0.0.7
模块和NGINX
源码解压
1 2 3 4 root@ecs-nginx-master:/home/source# tar -xzf nginx-1.20.2.tar.gz ngx_http_proxy_connect_module-0.0.7.tar.gz root@ecs-nginx-master:/home/source# ls nginx-1.20.2 nginx-1.20.2.tar.gz ngx_http_proxy_connect_module-0.0.7 ngx_http_proxy_connect_module-0.0.7.tar.gz
0x03-1 修补并编译NGINX 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 root@ecs-nginx-master:/home/source# cd nginx-1.20.2/ root@ecs-nginx-master:/home/source/nginx-1.20.2# patch -p1 < /home/source/ngx_http_proxy_connect_module-0.0.7/patch/proxy_connect_rewrite_1018.patch patching file src/http/ngx_http_core_module.c patching file src/http/ngx_http_parse.c patching file src/http/ngx_http_request.c patching file src/http/ngx_http_request.h patching file src/http/ngx_http_variables.c apt update && apt upgrade apt install build-essential libpcre3 libpcre3-dev zlib1g zlib1g-dev libssl-dev libgd-dev -y ./configure --prefix=/usr/local/nginx --with-http_ssl_module --with-http_stub_status_module --with-http_gzip_static_module --with-http_realip_module --with-stream --add-module=/home/source/ngx_http_proxy_connect_module-0.0.7 make && make install
兼容运维习惯, 软连接文件夹到/etc/nginx
, 软连接可执行文件到/usr/local/bin
1 2 cd /etc && ln -s /usr/local/nginx/ ./nginxcd /usr/local/bin && ln -s /usr/local/nginx/sbin/nginx ./nginx
0x03-2 编辑NGINX配置文件 首先创建日志文件夹, 然后修改 NGINX 配置文件
1 2 mkdir -p /var/log/nginxvim nginx.conf
下面是主配置文件nginx.conf
的示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 user root; worker_processes auto; error_log /var/log/nginx/error.log notice; pid /var/run/nginx.pid; events { worker_connections 1024; } http { include /usr/local/nginx/conf/mime.types; default_type text/html; 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 /var/log/nginx/access.log main; sendfile on; tcp_nopush on; tcp_nodelay on; keepalive_timeout 300s; client_body_timeout 300s; proxy_send_timeout 300s; proxy_read_timeout 300s; proxy_connect_timeout 300s; client_body_buffer_size 1024k; gzip on; gzip_min_length 10k; gzip_buffers 4 32k; gzip_http_version 1.1; gzip_comp_level 5; gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript application/javascript; gzip_vary on; server_names_hash_max_size 1024; server_names_hash_bucket_size 512; client_max_body_size 1024m; # 引入 项目的配置文件 和 工具网站 include /etc/nginx/sites-enabled/*/*/*.conf; include /etc/nginx/tools/*.conf; } stream { # 引入 TCP/UDP 的四层代理 include /etc/nginx/stream-enabled/*/*.conf; }
由配置文件可知
后端的配置文件放置在:
/etc/nginx/sites-enabled/prod/server/server.conf
前端的配置文件放置在:
/etc/nginx/sites-enabled/prod/proxy-web/proxy-web.conf
中间件的配置文件放置在:
/etc/nginx/tools/nacos.conf
TCP/UDP的反代配置文件在:
/etc/nginx/stream-enabled/prod/mysql.conf
0x03-3 使用Systemd管理NGINX服务 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 echo "[Unit] Description=The NGINX HTTP and reverse proxy server After=network.target Wants=network.target [Service] # 解除 65535 线程的限制, 不需要可以注释掉 # LimitNOFILE=65535 Type=forking PIDFile=/var/run/nginx.pid ExecStartPre=/usr/local/nginx/sbin/nginx -t ExecStart=/usr/local/nginx/sbin/nginx ExecReload=/usr/local/nginx/sbin/nginx -s reload ExecStop=/usr/local/nginx/sbin/nginx -s stop [Install] WantedBy=multi-user.target" | sudo tee /etc/systemd/system/nginx.service
如果在之前的编译过程中修改了路径, 或者修改了 NGINX 的配置文件, 这里创建Systemd
服务的时候需要同步修改路径
启用并且验证服务
1 2 3 systemctl daemon-reload systemctl enable nginx systemctl start nginx
偶发的Systemd错误处理 偶发情况下, 使用Systemd
管理 NGINX 会出现报错
1 nginx.service: Can't open PID file /run/nginx.pid (yet?) after start-post: Operation not permitted
实际上 pid 文件存在, 但是报错信息显示无法找到该文件, 在Systemd
的配置文件中加上延迟启动参数可以解决这个问题
0x03-4 前端应用与NGINX WEB 前端应用的编译与发布 上面主要介绍的是 NGINX Master 的配置文件, 对于前端应用来说, 流量首先需要通过 NGINX Master 转发到 NGINX Web, 最后才是转发给前端应用
在 Jenkins 构建流水线中, 前端服务编译完成后, 构建脚本将 dist 包分发到 ECS-Nginx-Web 服务器上, 完整脚本如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 # !/bin/bash set -exu # 控制 node 的内存分配, 解决编译 OOM 的问题 export NODE_OPTIONS="--max-old-space-size=8192" # 前端环境编译 npm install yarn -g yarn install node --max-old-space-size=8192 $(which yarn) run build:$env # $ECLOUD_HYKG_APP_HOST 实际指向的是移动云的NAT网关, 通过不同端口即可到达不同的服务器, 10025 是ECS-NG-WEBscp -r -P 10025 ./dist/* $ECLOUD_HYKG_APP_HOST:/data/$env/$JOB_NAME
NGINX的双层代理 ECS-Nginx-Web的一层代理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 server { listen 81; root /data/prod/prod-fs-oa-web-fd/; location / { index index.html; try_files $uri $uri/ /index.html; } error_page 404 /404.html; location = /40x.html { } error_page 500 502 503 504 /50x.html; location = /50x.html { } }
ECS-Nginx-Master的二层代理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 upstream prod-fs-oa-web-fd.com { server 192.168.0.2:81; } server { listen 18081; server_name 192.168.0.13; access_log /var/log/nginx/prod-fs-oa-web-fd.access.log main; client_max_body_size 0; location / { proxy_pass http://prod-fs-oa-web-fd.com/; proxy_redirect off; proxy_cookie_path / /; proxy_set_header Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Real-IP $remote_addr; } error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; } }
0x04 移动云网络架构说明