ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

银河麒麟V10离线环境Docker部署PostgreSQL/Nginx/Redis实战

银河麒麟V10离线环境Docker部署PostgreSQL/Nginx/Redis实战 上个月去客户现场做基础环境部署运维递给我一台刚拆封的银河麒麟V10服务器环境条件很直白没有外网。任务却列得很细——在这台国产操作系统上用Docker离线把PostgreSQL、Nginx、Redis全部跑起来。很多人一听“离线”就头大其实拆开看就两件事怎么把Docker引擎装上去怎么把容器镜像带进去。两件事想明白后面的操作跟普通Linux环境没有本质区别。这篇就按我实际操作的顺序把整体思路、准备过程、部署细节和踩过的坑都写出来给同样要在隔离网络、信创环境里做容器化部署的朋友一份可以直接抄的模板。1. 部署思路与离线方案的整体设计1.1 为什么坚持用Docker容器化在正式动手之前我想先说清楚一个为什么这东西不是非容器不可。一台机器上直接yum装PostgreSQL、编译装Nginx、源码装Redis也完全能跑。但现场环境的特点决定了容器方案更合适——内网服务器往往不止一套业务组件版本相互影响是常态。装一个Redis 6后来项目要用Redis 7直接装会把环境搞乱容器就没这个烦恼镜像标好版本互不干扰。另一个好处是回滚。物理机上的中间件配置改坏了恢复起来要一点一点抠。容器就简单配置坏了把容器删了用原始镜像重新起一个几十秒的事。在内网环境里没有太多试错成本这种可恢复性非常宝贵。还有资源管理。PostgreSQL、Nginx、Redis三个服务挤在一台机器上如果没有做任何限制Redis吃满内存会拖死数据库。容器可以给每个服务设置明确的内存和CPU上限Redis就算被业务打爆也只能在它自己的“笼子”里蹦跶不会连累邻居。1.2 离线部署要解决的两大问题离线部署跟在线部署最大的区别是“软件安装包”和“应用镜像”都没有现成渠道。在线环境下yum install docker-ce一条命令Docker引擎就连同依赖装好了docker pull postgres:15一条命令镜像就到了。离线环境这两条路全部堵死。所以核心思路就变成找一台和目标服务器操作系统同源、能访问外网的中转机把所有需要的安装包和镜像提前准备好用U盘或者内部文件服务器传进隔离区再把文件安装和导入到目标机器上。这里要提醒一点离线包不是只拷几个rpm文件就行。docker-ce依赖一堆动态库和工具链少一个装到一半报依赖冲突那滋味相当酸爽。正确操作是把整个依赖目录都拉下来打包带走。镜像方面用docker save把镜像从外网机器导出成tar文件传到内网后用docker load导回来。打比方说这就像搬家之前先把家具打包封箱到了新家再一件件拆包走的是同一个逻辑。三个镜像加起来也就五六百兆一个U盘完全装得下。1.3 中转机和镜像架构匹配这一节是整个方案里最容易翻车的地方我单独立一条来写。麒麟服务器常见两个CPU架构x86_64和aarch64尤其是国产CPU的机器飞腾、鲲鹏基本都是aarch64也就是ARM架构。麻烦就在这。如果中转机是x86的顺手docker pull默认拉的就是amd64镜像拷贝到ARM目标机上docker load能成功docker images也能看到镜像但一运行就报exec format error中文意思就是可执行文件格式不认。这不是你操作错了是架构不匹配。正确做法是提前用uname -m确认目标机架构然后在外网用--platform参数指定架构拉镜像# 目标机是ARM架构时拉取 docker pull --platform linux/arm64 postgres:15 # 目标机是x86架构时拉取 docker pull --platform linux/amd64 postgres:15这条命令是可以直接在x86中转机上执行的拉下来的是ARM版镜像虽然在本机跑不了但保存后再传到ARM机器上就能正常使用。架构问题一旦在前期确认清楚后面会顺利很多。2. 麒麟系统上离线安装Docker引擎2.1 先确认系统版本和CPU架构安装之前先花两分钟摸清家底。麒麟系统版本分支比较多服务器版常见的是RPM系桌面版有的走Debian系命令细节有差异。我用的是银河麒麟V10服务器版后面操作都以此为准。cat /etc/os-release uname -m uname -r第一条看系统版本第二条看架构第三条看内核版本。如果uname -m输出aarch64后面所有镜像准备就按ARM来。如果提示/os-release路径不存在去看/etc/system-release效果一样。这套做法的意义在于所有准备工作都依赖这条命令的输出结果。架构、系统版本、内核版本三要素确认了才能决定去拉哪个版本的docker RPM包拉哪个架构的容器镜像。跳过这一步直接干后面大概率返工。2.2 在外网环境准备Docker安装包我习惯在目标机上对党同操作系统再找一台能访问外网的同架构服务器下载Docker相关RPM包。以CentOS类系统为例先配置好Docker官方源或镜像源然后执行mkdir -p /tmp/docker_rpm cd /tmp/docker_rpm yum install docker-ce docker-ce-cli containerd.io docker-compose-plugin \ --downloadonly --downloaddir/tmp/docker_rpm如果yum版本不支持--downloadonly先安装yum-plugin-downloadonly这个插件。下载完成后不要只拷这几个rpm依赖包大概率还缺很多把目录一起打包tar czf docker-offline-rpm.tar.gz /tmp/docker_rpm这个包传到内网目标机上解压然后进入目录统一安装。装的时候不要“挑着装”一条rpm -Uvh *.rpm全部打进去让系统自己解决依赖问题。我之前图省事只拷了主包结果现场缺依赖浪费了大概一个小时。2.3 安装并启动Docker包内传到内网后执行tar xzf docker-offline-rpm.tar.gz cd docker_rpm rpm -Uvh *.rpm安装过程如果没报依赖错误基本就成了。接下来启动Docker守护进程并设置开机自启systemctl daemon-reload systemctl enable --now docker systemctl status docker查看systemctl status docker输出要是active (running)再执行docker version能看到Client和Server两段信息才算真正成功。如果只有Client看不到Server说明daemon没起来按2.4的排查思路来。2.4 启动失败时的常见调整Docker启动失败在国产系统上不算罕见最常见的是containerd服务没起来或者存储驱动不兼容。我的排查顺序是这样先看containerd状态。Docker依赖containerd来管理容器运行时它没起来Docker就起不来。systemctl status containerd journalctl -u docker -n 50如果日志里报存储驱动、overlay相关错误大概率是当前内核或文件系统配合度不够比如某些安全模块对overlayfs限制很严。这种情况下不用硬磕直接修改/etc/docker/daemon.json把存储驱动切到vfs{ storage-driver: vfs, log-driver: json-file, log-opts: { max-size: 100m, max-file: 5 } }vfs方案性能确实比overlay2差一些但胜在兼容性最好。内网环境如果没有极高并发需求跑中间件完全够用。另外log-opts限制单日志文件大小避免容器长期运行把磁盘日志撑爆这个配置在哪都建议加上。3. PostgreSQL容器化离线部署3.1 准备并导入PostgreSQL镜像外网中转机上先拉镜像。确认目标机架构假设是ARMdocker pull --platform linux/arm64 postgres:15 docker save -o postgres-15.tar postgres:15 gzip postgres-15.tar把postgres-15.tar.gz传到内网目标机解压后导入gunzip -k postgres-15.tar.gz docker load -i postgres-15.tar docker images | grep postgres看到postgres:15出现在镜像列表里镜像就绪。这里多说一句传输过程中文件损坏是离线操作的大坑镜像tar包动辄几百兆U盘拷一半断电、文件服务器限流都有可能损坏文件。建议在外网生成一个md5校验文件内网load之前先跑一遍md5sum -c校验通过再docker load能省很多无用功。3.2 规划数据目录与容器启动PostgreSQL的数据是“命根子”容器本身可以随时删数据目录必须在宿主机上并且做好权限处理。mkdir -p /data/postgres/data chown -R 999:999 /data/postgres/data为什么是999官方PostgreSQL镜像默认以postgres用户运行这个用户在容器内的UID就是999。以root身份在宿主机创建目录PostgreSQL进程往目录里写数据时会因为权限对不上而报Permission denied初始化直接失败。chown之后才能正常写入。启动容器docker run -d --name pg15 \ --restartalways \ -e POSTGRES_USERpostgres \ -e POSTGRES_PASSWORDStrongPass123 \ -e TZAsia/Shanghai \ -p 5432:5432 \ -v /data/postgres/data:/var/lib/postgresql/data \ postgres:15各参数意义POSTGRES_USER和POSTGRES_PASSWORD设置初始用户和密码TZ把容器时区设为北京时间-p做端口映射-v把宿主机数据目录挂载为容器内的数据目录。--restartalways确保机器重启后容器自动拉起非常实用的参数。3.3 初始化验证与常见排查要点启动成功后等几秒钟让数据库完成初始化然后看日志确认docker logs pg15 | grep database system is ready进入容器验证版本和表结构docker exec -it pg15 psql -U postgres -c select version();从应用服务器连接时如果连不上按顺序查三个地方第一ss -lntup | grep 5432确认端口在监听第二确认容器映射正常docker ps里看到0.0.0.0:5432-5432/tcp第三检查麒麟系统防火墙是否放行了5432firewall-cmd --list-ports看一下如果没有放行按内网安全策略增加对应白名单。4. Nginx容器化离线部署4.1 镜像准备与目录规划Nginx的镜像很小我通常选alpine版本数十兆的体积性能和完整版差距不大。拉取和导入跟PostgreSQL完全一样docker pull --platform linux/arm64 nginx:1.24-alpine docker save -o nginx-1.24-alpine.tar nginx:1.24-alpine gzip nginx-1.24-alpine.tar内网load之后规划宿主机目录mkdir -p /data/nginx/{conf.d,html,logs}三个目录分别存配置、站点文件、日志。这个设计的好处是日常维护根本不用进容器。改配置直接改宿主机文件然后把Nginx reload一下部署新页面直接往html目录丢文件看日志直接tail宿主机日志文件。对运维来说这是最舒服的方式。4.2 配置文件设计与启动参数在/data/nginx/conf.d下建一个default.conf内容按现场需求调整。下面这个模板同时处理静态页面和API反向代理server { listen 80; server_name _; root /usr/share/nginx/html; index index.html; location / { try_files $uri $uri/ /index.html; } location /api/ { proxy_pass http://192.168.100.10:8080/api/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } }启动命令docker run -d --name nginx124 \ --restartalways \ -p 80:80 -p 443:443 \ -v /data/nginx/conf.d:/etc/nginx/conf.d:ro \ -v /data/nginx/html:/usr/share/nginx/html:ro \ -v /data/nginx/logs:/var/log/nginx \ nginx:1.24-alpine这里有个容易踩的坑conf.d目录下只认.conf结尾的配置文件名你放.txt它当没看见放.conf立刻生效。所以新加配置时文件名后缀千万别写错。4.3 配置校验与重载Nginx最让人舒心的一点是支持热加载。改完配置文件最好先校验一下语法docker exec nginx124 nginx -t输出syntax is oktest is successful再执行reloaddocker exec nginx124 nginx -s reloadreload不会中断现有连接服务全程在线。注意不要动不动就docker restart nginx124那样会有瞬时断连生产环境的Nginx尽量用reload而不是restart。日志这块提醒一句容器里的Nginx不会自动按天切割access.log跑久了单个文件会非常大。常见做法是宿主机制定定时任务每天零点把/data/nginx/logs/access.log备份改名然后执行docker exec nginx124 nginx -s reopen让Nginx重新打开日志文件形成滚动效果。5. Redis容器化离线部署5.1 镜像准备与配置项解读Redis镜像同样选alpine版docker pull --platform linux/arm64 redis:7.0-alpine docker save -o redis-7.0-alpine.tar redis:7.0-alpine gzip redis-7.0-alpine.tar内网load之后先准备配置文件。容器里没有vim而且容器一删配置就没了所以建议直接在宿主机写好再挂载进去。/data/redis/conf/redis.conf内容如下bind 0.0.0.0 protected-mode yes port 6379 requirepass RedisStrongPass appendonly yes appendfsync everysec dir /data maxmemory 2048mb maxmemory-policy allkeys-lru几项关键配置逐个说。bind 0.0.0.0是监听所有网卡配合protected-mode yes和requirepass强密码避免Redis裸奔被内网扫描工具扫到爆破。appendonly yes开启AOF持久化保障实例重启后的数据恢复能力appendfsync everysec每秒刷盘一次在安全性和性能之间取平衡。dir /data必须和容器挂载的数据目录对上否则AOF文件会写到容器可写层容器一删数据就没了。maxmemory要根据宿主机物理内存来设定后面一节详细说。maxmemory-policy allkeys-lru保证内存满了以后按LRU策略淘汰旧数据而不是直接拒绝写入。5.2 启动容器与验证docker run -d --name redis7 \ --restartalways \ -p 6379:6379 \ -v /data/redis/conf:/etc/redis:ro \ -v /data/redis/data:/data \ -e TZAsia/Shanghai \ redis:7.0-alpine redis-server /etc/redis/redis.conf注意最后的redis-server参数它告诉容器启动时不以默认配置运行而是加载我们挂载进去的redis.conf。启动完成后验证docker exec -it redis7 redis-cli -a RedisStrongPass ping返回PONG就代表服务正常。再做一个写入读取测试确认持久化可用docker exec -it redis7 redis-cli -a RedisStrongPass set test val docker exec -it redis7 redis-cli -a RedisStrongPass get test查看宿主机/data/redis/data目录能看到appendonly.aof文件就说明AOF生效了。5.3 资源限制与持久化的坑Redis在容器里跑最容易忽略的是fork行为对内存的冲击。RDB快照和AOF重写时Redis会fork一个子进程利用写时复制技术生成持久化文件。子进程虽然不复制全部数据但它要把整个内存页表复制一份实际内存占用会瞬间涨到接近两倍。如果宿主机内存是4GBmaxmemory设了3GB那一做持久化物理内存直接不够容器被cgroup杀掉或者触发系统OOM后果就是Redis进程消失、服务中断。我的建议是maxmemory不超过宿主机物理内存的60%。比如8GB物理内存容器限制6GBmaxmemory设4GB比较稳妥给fork留出足够余量。同时宿主机上最好把内存过度分配打开echo vm.overcommit_memory 1 /etc/sysctl.conf sysctl -p这条是Redis官方启动时经常给的警告在容器环境里同样需要。否则Redis启动时会有个“WARNING Memory overcommit must be enabled”的提示内存压力大的时候容易出问题。6. 常见问题与排查技巧实录6.1 镜像平台不对导致容器无法启动这个坑我在现场见过好几次表现是镜像load成功、docker images能看到docker run一执行就报“exec format error”。排查命令docker image inspect postgres:15 | grep Architecture如果输出amd64而你目标机是arm那肯定起不来。解决方法是回到中转机用--platform参数重新拉取对应架构的镜像再save、load一遍。这也是为什么我反复强调动手之前先uname -m确认架构。6.2 容器时间差8小时容器默认使用UTC时区日志时间和北京时间刚好差8小时。排查问题时日志对不上非常难受。解决方式很简单启动容器时加-e TZAsia/Shanghai同时挂载宿主机本地时间-v /etc/localtime:/etc/localtime:ro这两个参数在三个容器上最好都加上。如果服务已经跑起来了需要重建容器才能生效所以建议在第一次启动时就写好省得后面重来。6.3 端口冲突与防火墙拦截docker run时报“bind: address already in use”是典型的端口占用先用ss看端口谁在监听ss -lntup | grep 5432Redis的6379还经常被本地其他服务占用。端口冲突相对好办改个映射端口或者把老服务停掉就行。更隐蔽的是防火墙放行问题固定好端口后如果从外部连不上第一反应就是看firewalld规则firewall-cmd --list-ports firewall-cmd --zonepublic --add-port6379/tcp --permanent firewall-cmd --reload6.4 数据卷权限与安全模块拦截容器挂在宿主机目录后经常碰到的症状是目录写了没数据或者直接Permission denied。原因不外乎两类一是uid对不上比如PostgreSQL镜像内的用户是999宿主机目录归root就写不进去二是安全模块拦截麒麟系统有的环境SELinux是开启的容器写文件会被scontext策略挡住。解决权限问题就用chown把目录放权给容器用户解决SELinux临时测试可以用setenforce 0关掉看看是否恢复如果是SELinux的问题再考虑用chcon -Rt container_file_t /data这类方案或者根据实际安全策略调整不要一上来就永久关闭。6.5 重启容器后数据“消失”很多人遇到重启容器后数据没了第一反应是镜像问题其实多半是挂载没生效。检查一下docker inspect redis7 --format {{json .Mounts}}重点看挂载位置和容器内部路径是否写对。常见出错的场景是磁盘目录换个路径比如/data/postgres/data写成了/data/postgresql/data容器内部路径写错比如PostgreSQL挂在/data/redis/data上还有一种情况是第一次启动时挂载路径没生效但初始化成功了数据写在容器可写层后来挂载路径修正后容器启动用了新的空目录看起来数据就没了。这类问题一旦发生数据恢复难度极大。所以正规做法是启动前把挂载路径规划清楚写完启动命令后先docker inspect确认Mounts没写错再开始写数据。6.6 配置文件改了但没生效Redis或Nginx改了宿主机上的配置后服务表现却还是老样子先区分两个情况一是改的文件没被容器加载。比如改了/data/nginx/conf.d/default.confdocker exec nginx124 ls /etc/nginx/conf.d看一下容器内的文件内容是不是你改过的不对就检查挂载的ro权限和后缀名二是改了没重载。Nginx需要nginx -s reloadRedis改配置后哪怕只是改maxmemory也要重启容器或CONFIG REWRITERedis不会自动感知外部文件变化。7. 顺手写了一套部署脚本做成可复用的模板7.1 镜像一键导入脚本离线部署做多了发现最机械重复的动作就是一个个docker load干脆写成一个脚本放到离线包目录里一键执行。#!/bin/bash # 离线环境中批量导入docker镜像 set -e cd $(dirname $0) for f in *.tar; do if [ -f $f ]; then echo [INFO] loading $f ... docker load -i $f echo [INFO] $f loaded fi done echo [INFO] all images loaded, current list: docker images把这个脚本和三个tar包放在同一个目录传进内网后执行bash load_images.sh就能把所有镜像一次性导入。脚本里的set -e保证任何一个镜像load失败就停下不会让你误以为全部成功。7.2 目录规划模板与日常运维速查部署之前把目录建好是比任何容器参数都重要的一步。下面是我常用的目录模板组件容器名宿主机端口数据目录配置目录日志目录PostgreSQL 15pg155432/data/postgres/data/data/postgres/conf数据目录内Nginx 1.24nginx12480/443/data/nginx/html/data/nginx/conf.d/data/nginx/logsRedis 7.0redis76379/data/redis/data/data/redis/conf/data/redis/logs建好目录后再跑容器命令心里有底得多。日常运维里这几个命令频率最高操作命令查看容器日志docker logs -f pg15查看容器资源占用docker stats --no-stream进入容器内部docker exec -it redis7 /bin/sh查看挂载是否正确docker inspect redis7 --format {{json .Mounts}}重启容器docker restart nginx124这套流程下来我在现场从装系统到三个中间件全部对外提供服务基本两个小时搞定。真正花时间的不是敲命令而是前期的架构确认和目录规划。这些前置工作做扎实了后面的每一步都是机械操作。按照这个套路换一台新的国产服务器准备好离线包十分钟能跑完一遍容器拉起。工具的价值不在命令多花哨而在流程能不能稳定复现我这套是可以。
返回列表