Alibaba Cloud Linux 3服务器环境搭建全记录:初始化、安全加固与Docker部署

发布时间:2026/9/20 6:21:46
Alibaba Cloud Linux 3服务器环境搭建全记录:初始化、安全加固与Docker部署 这一阵子帮团队把一台新购的 ECS 实例从零开始搭成可用的 Web 服务器系统镜像选的是 Alibaba Cloud Linux 3。之前我在 CentOS 7 上待惯了突然切到阿里云自研的这条发行版线心里确实没底。一周折腾下来从系统初始化、安全加固到 Docker 部署、业务上线整体走完了一遍服务器环境搭建的闭环。这篇文章就把这台机器的完整初始化过程、关键决策和踩过的坑记录下来给同样在 Alibaba Cloud Linux 3 上做服务器环境搭建的朋友做个参考。1. 一上来别急着装软件先摸清 Alibaba Cloud Linux 3 的底细1.1 三分钟确认系统身份不是所有 Linux 都叫 CentOS很多老朋友的习惯是拿到服务器先敲cat /etc/redhat-release但在 Alibaba Cloud Linux 3 上这个文件的内容是Alibaba Cloud Linux (Aliyun Linux) release 3如果你还用老经验去配 CentOS 7 的源后边大概率会踩坑。我第一次登录后的第一件事是看系统身份cat /etc/os-release输出里有几个信息值得注意IDalinux、PLATFORM_IDplatform:al8、PRETTY_NAMEAlibaba Cloud Linux 3 (Soaring Falcon)。这里的platform:al8说明它和 RHEL 8 兼容很多针对 RHEL 8 的二进制包和编译方式可以直接用但这不代表你可以照着 CentOS 8 的教程乱抄命令。再配合uname -r看内核版本默认是 5.10 系列。这个内核版本比 CentOS 7 的 3.10 新了太多意味着 io_uring、更高版本的 eBPF 特性都是可用的后续装一些现代云原生组件时会省事不少。我建议你把cat /etc/os-release、uname -r、sestatus这三条命令的输出固定记在一个笔记文件里后边排查问题时能快速确认自己到底跑在什么环境上避免在论坛里提问时连系统版本都说不清楚。1.2 dnf 与模块化仓库和 CentOS 7 最大的认知差Alibaba Cloud Linux 3 默认的包管理器是 dnf虽然yum命令也能用两者做了兼容软链但底层逻辑已经变了。最直观的感受是安装包时提示语不同dnf会明确告诉你操作的是哪个仓库遇到依赖问题时给出的提示也比 yum 友好得多。真正让我觉得需要适应的是 AppStream 模块化仓库。以前在 CentOS 7 里装一个软件通常只要yum install nginx就完事。到了 Alibaba Cloud Linux 3 上nginx 在 AppStream 里可能分成nginx:1.20、nginx:1.22等多个模块流默认装的不是你想象的版本升级行为也可能不同。第一次跑dnf install nginx时我压根没留意模块流信息结果装出来的版本和预期不一致后面排查依赖冲突时才发现问题。所以在开始装任何东西之前我强烈建议先执行一遍dnf list --available | wc -l dnf module list --enabled花两分钟知道当前系统有哪些模块流被启用能避免后面很多为什么装出来版本不对为什么两个软件互相冲突的问题。这个认知差是 Alibaba Cloud Linux 3 和 CentOS 7 之间最需要先跨过去的坎。2. 系统初始化三板斧源、更新、时间2.1 首次全量更新前我做的两个准备工作在跑dnf update之前我习惯先做两件事一是给磁盘留一个快照二是确认当前系统盘的剩余空间。更新过程中最怕出现磁盘占满导致 RPM 事务中断那个状态处理起来非常恶心宁可慢一点也别冒险。新版 Alibaba Cloud Linux 3 的默认源指向阿里云内网镜像在 ECS 上用速度极快不需要手动换源。我第一次跑更新时看到下载速度稳定在 100MB/s 以上心里踏实了不少。执行全量更新dnf clean all dnf makecache dnf update -ydnf update和dnf upgrade在常规用法上没有本质区别upgrade是update的别名如果你在文档里看到有人说两者有差异多半是在讲老版本 yum 的历史遗留问题。更新完如果有内核相关的升级建议重启一遍让系统跑在新内核上再继续配置。别嫌重启麻烦很多人就是省了这一步后边装内核模块时出现版本对不上。2.2 默认源与 EPEL够用就行别贪多Alibaba Cloud Linux 3 自带的 BaseOS 和 AppStream 仓库已经覆盖了大部分常用软件但有些没那么核心的包还是需要 EPEL。EPEL 对应 RHEL 8 的扩展仓库安装方式很简单dnf install -y epel-release dnf makecache这里我要多说一句EPEL 能装但别顺手加一堆第三方源。网上有些教程让你同时开 EPEL、Remi、RPMFusion在 CentOS 7 时代这样操作勉强能忍到了 Alibaba Cloud Linux 3 的模块化仓库体系下第三方源之间对同一个软件包的不同版本定义会把依赖关系变成一团乱麻后边我踩到的 AppStream 依赖冲突就是这么来的。安装完 EPEL 后我顺手装了几个几乎每个服务器都要用到的基础工具dnf install -y vim tar unzip wget curl git tree lsof tcpdump net-tools bind-utilsnet-tools提供ifconfig、netstat虽然新系统推荐ip命令但在排查老脚本问题时这几个命令偶尔还是会用到。bind-utils里的dig和nslookup在排查 DNS 问题时比ping有用得多。2.3 chrony 时间同步云服务器最容易忽略的细节时间同步平时看着不起眼一旦出问题就是各种诡异故障HTTPS 证书校验失败、日志时间错乱导致排查困难、分布式组件互相认证超时。Alibaba Cloud Linux 3 默认预装了 chrony但很多人登录后根本没确认它是否在运行。我先检查了时间同步状态systemctl status chronyd timedatectl如果 chronyd 没有运行手动启动并设置开机自启systemctl enable --now chronyd验证同步情况用chronyc sources -v输出的^*开头表示当前已同步到一个时间源^?则说明有问题。云服务器还要注意把时区改成业务实际使用的时区我的机器跑的是国内业务所以执行了timedatectl set-timezone Asia/Shanghai。另外一个小经验别同时在系统里跑 chronyd 和 ntpd两个时间同步服务会互相打架导致时间频繁跳变。如果你发现 ECS 里预装的是 ntpd那就把 chronyd 彻底禁用二选一别来回切换。3. 安全硬化实操SSH、防火墙与 SELinux 的相处之道3.1 SSH 密钥登录改造风险操作的标准流程新服务器开箱后默认允许 root 密码登录密码强度再高也架不住互联网上的暴力扫描。我用两步完成 SSH 加固先生成本地密钥并上传公钥再修改 sshd 配置收紧登录策略。在本地生成密钥不要用空口令保护私钥至少设置一个 passphrasessh-keygen -t ed25519 -C your-comment ssh-copy-id rootyour-server-ip如果是 Windows 环境可以用ssh-copy-id的替代脚本或者手动把公钥追加到服务器的~/.ssh/authorized_keys。公钥文件权限必须是 600文件夹权限必须是 700否则 SSH 会忽略这个文件这是很多人密钥登录失败的原因。接下来修改/etc/ssh/sshd_config核心改动如下Port 22022 PermitRootLogin prohibit-password PasswordAuthentication no PubkeyAuthentication yes改完后执行sshd -t检查语法然后systemctl reload sshd。记住是 reload 不是 restartreload 不会断开当前已有连接风险低很多。这里有一个关键顺序问题先保证公钥登录已经能成功再关密码登录最后再改 SSH 端口。改完端口前先确认安全组和 firewalld 里已经放行了新端口否则你会把自己锁在外面。我就是先改了端口再想起来安全组没放行差点连不上机器。3.2 系统防火墙与云安全组两条防线别打架云服务器实际上有两层网络防线云平台安全组在虚拟网络层面工作操作系统里的 firewalld/nftables 在实例内部工作。很多人只配置了安全组就完全依赖它或者只开了 firewalld 不管安全组这两种单腿走路都很危险。我在初始化时把 firewalld 打开并明确规划了两者的分工防护层控制位置管什么配置工具云安全组ECS 控制台 / OpenAPI公网入方向流量阿里云控制台firewalld实例操作系统本机入方向流量含内网firewall-cmd应用层Nginx / MySQL 等业务访问控制各应用配置安全组负责大面上的公网访问放行firewalld 负责把内网之间的访问也管起来。比如我的 MySQL 只允许应用服务器访问那就在 firewalld 里把 3306 端口的源地址限制到应用服务器内网 IP单纯依赖安全组的话同一内网里的其他机器也能访问到数据库端口风险明显更大。firewalld 常用操作systemctl enable --now firewalld firewall-cmd --permanent --add-port22022/tcp firewall-cmd --permanent --add-servicehttp firewall-cmd --permanent --add-servicehttps firewall-cmd --reload firewall-cmd --list-all云安全组那边同步把 22022、80、443 端口的入方向规则配上。两边放行的口径必须一致否则就会出现firewalld 已经放行了但外网还是不通的困惑。排查这类问题时我一般先看安全组再看 firewalld最后看应用本身按顺序走别乱猜。3.3 SELinux 不关机用对方式解决权限问题很多从 CentOS 7 时代过来的运维装完系统第一件事就是setenforce 0然后顺手把/etc/selinux/config改成 disabled。我在这次搭建中坚持没关 SELinux不是因为信仰而是想验证一下 Alibaba Cloud Linux 3 默认开启的 SELinux 在实际部署中到底会不会成为阻力。使用下来我的结论是SELinux 的绝大多数报错都是因为文件上下文或布尔值配置不对而不是 SELinux 本身不能用。以 Nginx 为例如果你的网站目录放在/data/www而不是默认的/usr/share/nginx/htmlSELinux 会拦截 Nginx 读取这些文件报错往往是 403 而不是明晃晃的Permission denied。修复方式不是关闭 SELinux而是设置正确的上下文semanage fcontext -a -t httpd_sys_content_t /data/www(/.*)? restorecon -Rv /data/www另一个常见问题是 Nginx 反代到后端端口时被 SELinux 拦解决办法是打开布尔开关setsebool -P httpd_can_network_connect 1先查一下当前 SELinux 状态getenforce如果输出是 Enforcing那就继续用如果已经是 Disabled也不用急着改回去等下次维护窗口再评估。至少在部署阶段我建议保持 Enforcing遇到拦截就花几分钟看审计日志大部分问题都比想象中简单。4. 容器环境落地Docker 部署 Nginx MySQL Redis4.1 Docker CE 安装与镜像加速Alibaba Cloud Linux 3 的软件源里可以直接dnf install docker但那个版本更新不够及时。我更倾向于装 Docker CE 官方源然后用--releasever8的方式与 RHEL 8 匹配dnf config-manager --add-repohttps://download.docker.com/linux/centos/docker-ce.repo dnf install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin这里有个小坑阿里云官方推荐的安装命令可能和你手头的版本有细微差异安装时留意一下docker-compose-plugin是否装上了后面要用到docker compose子命令。如果镜像仓库访问较慢建议在/etc/docker/daemon.json中配置 registry-mirrors使用云厂商提供的镜像加速地址。配置完成后systemctl daemon-reload systemctl enable --now docker docker infodocker info输出里可以确认镜像加速器是否生效。同时我建议把当前用户加入 docker 组省得每次敲 sudousermod -aG docker yourusername这个操作对生产环境的安全性有影响加入 docker 组等同于拥有 root 权限所以只建议在信任的单用户管理机上执行。4.2 一条 docker-compose.yml 跑通 Web 服务这次我搭的是一套最典型的 Web 服务Nginx 做反向代理MySQL 8.0 存数据Redis 7 做缓存。用 docker compose 统一管理比一个个docker run清晰得多。先建目录结构mkdir -p /data/nginx/{conf.d,html,logs} mkdir -p /data/mysql/{data,conf} mkdir -p /data/redis/data然后写/data/docker-compose.ymlversion: 3.8 services: nginx: image: nginx:1.24-alpine container_name: web_nginx restart: always ports: - 80:80 - 443:443 volumes: - /data/nginx/html:/usr/share/nginx/html - /data/nginx/conf.d:/etc/nginx/conf.d - /data/nginx/logs:/var/log/nginx networks: - backend depends_on: - php - mysql php: image: php:8.2-fpm-alpine container_name: web_php restart: always volumes: - /data/nginx/html:/var/www/html networks: - backend mysql: image: mysql:8.0 container_name: web_mysql restart: always environment: MYSQL_ROOT_PASSWORD: StrongRootPassw0rd MYSQL_DATABASE: appdb MYSQL_USER: appuser MYSQL_PASSWORD: AppPassw0rd command: --character-set-serverutf8mb4 --collation-serverutf8mb4_unicode_ci volumes: - /data/mysql/data:/var/lib/mysql - /data/mysql/conf:/etc/mysql/conf.d networks: - backend redis: image: redis:7-alpine container_name: web_redis restart: always command: redis-server --appendonly yes volumes: - /data/redis/data:/data networks: - backend networks: backend: driver: bridge注意几个细节MySQL 的初始化命令里直接指定了 utf8mb4避免建表之后再改字符集Redis 开了 AOF 持久化所有容器挂在同一个 bridge 网络这样 Nginx 可以通过服务名直接解析到 PHP 和 MySQL不需要暴露多余的物理机端口。启动cd /data docker compose up -d docker compose psPHP 容器是因为后续业务需要才加的如果你的项目是纯静态站或 Node.js 服务可以视情况去掉。关键是理解 compose 的组织逻辑每个服务独立、共享网络、数据卷外置。4.3 systemd 与重启策略让服务真正跑在生产环境docker compose 的restart: always已经能在很多情况下保证容器自动拉起但要扛住 Docker 服务本身的重启场景最好把整个 compose 项目注册成 systemd 服务这样服务器重启后 Docker 守护进程启动随后自动拉起所有容器。创建/etc/systemd/system/webapp.service[Unit] DescriptionWebapp compose stack Requiresdocker.service Afterdocker.service [Service] Typeoneshot RemainAfterExityes WorkingDirectory/data ExecStart/usr/bin/docker compose up -d ExecStop/usr/bin/docker compose down [Install] WantedBymulti-user.target然后systemctl daemon-reload systemctl enable --now webapp.service这样一来用systemctl restart webapp就能整体控制整套服务操作习惯和以前管理系统服务完全一致。日常排查时docker compose logs -f --tail200 service是最常用的命令能看到某个容器的实时日志比逐个docker logs高效得多。5. 实战中的坑与排查记录5.1 MySQL 8.0 容器内存超预期第一次docker compose up后我随手docker stats看了一眼结果 MySQL 容器内存直接飙到 800 多 MB整台 4G 内存的机器明显吃紧。查了一下MySQL 8.0 的默认配置里 InnoDB Buffer Pool 是自动检测并设置为物理内存的 75% 左右在小内存服务器上完全不合适。处理方式是显式限制 MySQL 容器可用内存并在 MySQL 配置文件里指定合适的 Buffer Pool 大小# docker-compose.yml 中为 mysql 服务增加 deploy: resources: limits: memory: 1g同时在/data/mysql/conf/my.cnf里写入[mysqld] innodb_buffer_pool_size 256M innodb_log_file_size 64M performance_schema OFF重启后内存稳定在 400M 左右效果明显。这个坑几乎每个用 MySQL 8.0 容器的人都会碰到关键是别让容器里的 MySQL 自动适配宿主机内存小规格机器一定要手动设置。5.2 Nginx 502 的 SELinux 拦路虎服务启动后访问首页一切正常但访问一个需要转发到后端 PHP 的接口时Nginx 返回 502。我第一反应是 PHP-FPM 挂掉了看日志发现 PHP 容器运行正常也能 ping 通。再次看 Nginx 错误日志发现连接 PHP 9000 端口被拒绝。排查到最后问题出在 SELinux 的httpd_can_network_connect布尔值上。容器里的 Nginx 本质上还是通过宿主机的网络栈转发连接而 Alibaba Cloud Linux 3 默认没有放开 httpd 进程的网络连接权限。解决办法之前提到过setsebool -P httpd_can_network_connect 1执行后立即生效502 消失。这个案例很典型界面表现是应用层的 502根因却是系统安全模块的拦截。如果一开始就直接关掉 SELinux这个坑根本不会暴露但也让你失去了了解系统真实运作机制的机会。5.3 AppStream 模块化带来的依赖冲突我在安装某个扩展包时dnf 提示与已安装的nginx包冲突原因是 EPEL 仓库里的 nginx 版本和 AppStream 默认模块流版本不一致。这在 CentOS 7 时代很少见到了模块化仓库时代不同源对同一个包的不同版本定义就会互相踩脚。解决方案是显式指定模块流或者干脆禁用其中一个源中不需要的模块dnf module list nginx dnf module enable nginx:1.22如果某个第三方源老是干扰主源最直接的办法是给仓库配置文件加enabled0需要时再手动指定--enablerepo。现在我的原则很简单能不用第三方源就不用常用的东西默认源 EPEL 足够覆盖只有极个别软件才会单独加源而且加源后严格限制它的使用范围。5.4 journald 日志无限增长服务器跑了两天后我突然发现系统盘使用率上涨得异常快查了一圈发现/var/log/journal目录占了将近 1.5G。Alibaba Cloud Linux 3 默认的 journald 会持久化系统日志如果不限制大小日志会一直增长最终把小磁盘撑爆。我的处理是修改/etc/systemd/journald.confSystemMaxUse500M MaxRetentionSec7day然后重启 journald 服务systemctl restart systemd-journald这样日志量被控制在 500M 以内超过一周的历史日志自动清理。另外建议顺手加一个定时清理 Docker 日志的任务容器日志同样会占空间我通常用 logrotate 配合或者在 docker-compose 里对每个服务设置logging.driver的max-size和max-file选项双保险。最后补一句个人体会整套环境从初始化到业务跑起来最深的感受是别只盯着安装命令要理解这套系统自己的脾气。Alibaba Cloud Linux 3 和 CentOS 7 的差异确实存在但基本上集中在软件包管理、模块化仓库、以及默认安全策略上把这些底层逻辑摸清之后后面的搭建过程其实就是水到渠成的事。如果你刚拿到一台 Alibaba Cloud Linux 3 的机器我建议先从cat /etc/os-release和dnf module list开始把基础打牢后面的坑会少很多。