1. 项目缘起:为什么从零开始搭建服务器

最近在帮一个朋友部署他的个人项目,他之前一直用一些免费的云服务或者本地跑跑,现在想正式上线,拥有一个稳定、可控的线上环境。他问我:“是不是买个云服务器,装个宝塔面板就完事了?” 我告诉他,宝塔确实方便,但如果你想真正理解一个项目从开发到上线的完整链路,想拥有更灵活、更可控、更“DevOps”一点的部署方式,从零开始手动搭建环境,再用Docker容器化部署,是绕不开的一课。这不仅仅是“部署”,而是一次对现代应用部署流程的深度实践。

这个“从零到一”的过程,涵盖了Linux服务器基础操作、网络与安全配置、服务安装与编排、容器化技术应用等多个环节。它解决的问题很直接:让你亲手将一个“裸”的云服务器,变成一个能稳定、安全运行你业务应用的“生产环境”。无论你是个人开发者想部署自己的博客、工具站,还是小团队需要搭建测试或生产环境,这套流程都极具参考价值。它让你摆脱对集成面板的依赖,真正掌控你的服务器,并且在未来面对更复杂的微服务架构时,也能游刃有余。

2. 环境准备:选择与初始化你的云服务器

2.1 云服务商与服务器选型

第一步是选择云服务商和服务器。国内主流的有阿里云、腾讯云、华为云等,国外有AWS、DigitalOcean、Vultr等。选择时主要考虑:地理位置(影响访问延迟)、价格、易用性和社区支持。对于个人项目或小型应用,我通常建议从最基础的配置开始。

以腾讯云轻量应用服务器为例,1核2G的配置,对于初期流量不大的Web应用、API服务或博客系统完全够用。操作系统选择上, 强烈推荐 Ubuntu 22.04 LTS 。LTS代表长期支持,稳定性和社区支持都更好。CentOS曾经是主流,但Red Hat改变了其策略后,Ubuntu Server成为了更普适和友好的选择。

购买后,你会获得服务器的公网IP地址、root用户名和密码(或密钥对)。这是你通往服务器世界的钥匙。

2.2 首次登录与基础安全加固

拿到服务器后,第一件事不是急着装软件,而是做好安全加固。通过SSH连接服务器:

ssh root@你的服务器公网IP

输入密码或使用密钥对登录。

登录后,立即进行以下几项操作:

  1. 更新系统包 apt update && apt upgrade -y 。确保系统所有软件包都是最新的,修复已知安全漏洞。
  2. 创建新用户 :永远不要长期使用root用户操作。创建一个具有sudo权限的普通用户。
    adduser yourusername
    usermod -aG sudo yourusername
    
  3. 配置SSH密钥登录,禁用密码登录 :这是防止暴力破解的关键。在你的本地电脑生成SSH密钥对(如果还没有): ssh-keygen -t rsa -b 4096 。然后将公钥( ~/.ssh/id_rsa.pub )的内容,复制到服务器的 ~/.ssh/authorized_keys 文件中。接着,编辑SSH配置文件 /etc/ssh/sshd_config
    • PasswordAuthentication 设置为 no
    • PermitRootLogin 设置为 prohibit-password no (如果你确定不用root登录)。
    • 可以修改 Port 为其他非22端口(如2222)以增加隐蔽性,但这并非必需,密钥认证已足够安全。 修改后重启SSH服务: systemctl restart sshd 务必在重启前测试用新用户和密钥能否登录,否则可能把自己锁在门外。
  4. 配置防火墙 :Ubuntu默认使用 ufw (Uncomplicated Firewall)。启用它并放行必要的端口。
    ufw allow 22/tcp # SSH端口,如果你改了端口,这里要对应修改
    ufw allow 80/tcp # HTTP
    ufw allow 443/tcp # HTTPS
    ufw --force enable # 启用防火墙
    ufw status verbose # 查看规则
    

完成这些,你的服务器就有了一个相对安全的基础。接下来,我们开始安装核心服务。

3. 核心服务安装:为Docker铺平道路

我们的目标是使用Docker部署项目,但Docker本身需要一些依赖,并且我们还需要一个反向代理服务器来管理流量。

3.1 安装Docker与Docker Compose

Docker的安装官方文档非常清晰。我们使用官方提供的便捷脚本(适用于测试和开发环境,生产环境建议通过仓库安装):

# 安装必要工具
apt install -y apt-transport-https ca-certificates curl software-properties-common
# 添加Docker官方GPG密钥
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
# 设置稳定版仓库
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# 安装Docker引擎
apt update
apt install -y docker-ce docker-ce-cli containerd.io

安装完成后,启动Docker并设置开机自启: systemctl start docker && systemctl enable docker 。为了不用每次都加 sudo ,可以将当前用户加入 docker 组: sudo usermod -aG docker $USER ,然后 退出SSH重新登录 生效。

Docker Compose是一个用于定义和运行多容器Docker应用的工具。我们安装其独立版本(v2):

# 下载最新稳定版Docker Compose
DOCKER_CONFIG=${DOCKER_CONFIG:-$HOME/.docker}
mkdir -p $DOCKER_CONFIG/cli-plugins
curl -SL https://github.com/docker/compose/releases/latest/download/docker-compose-linux-x86_64 -o $DOCKER_CONFIG/cli-plugins/docker-compose
# 赋予执行权限
chmod +x $DOCKER_CONFIG/cli-plugins/docker-compose
# 验证安装
docker compose version

3.2 安装Nginx作为反向代理

虽然我们可以用Docker运行Nginx,但我更倾向于在宿主机上安装一个Nginx作为“流量总入口”。这样做有几个好处:管理证书方便(用Certbot)、配置统一、不依赖Docker网络(即使所有容器重启,反向代理依然在)。当然,这只是一个选择,你也可以完全容器化。

安装Nginx很简单: apt install -y nginx 。安装后,其配置文件在 /etc/nginx 目录下。我们先保持默认,稍后再配置。

4. 项目容器化:编写Dockerfile与Compose文件

假设我们有一个简单的Python Flask Web应用,项目结构如下:

myapp/
├── app.py
├── requirements.txt
└── Dockerfile

app.py 是应用主文件, requirements.txt 列出了依赖(如 Flask==2.3.2 )。

4.1 编写Dockerfile

Dockerfile是构建镜像的蓝图。一个典型的Python应用Dockerfile如下:

# 使用官方Python精简版作为基础镜像
FROM python:3.11-slim

# 设置工作目录
WORKDIR /app

# 复制依赖文件并安装
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# 复制应用代码
COPY . .

# 暴露端口(Flask默认5000)
EXPOSE 5000

# 定义容器启动命令
CMD ["python", "app.py"]

这个Dockerfile做了几件事:从一个干净的基础镜像开始;设置容器内的工作路径;先复制依赖文件并安装(利用Docker层缓存,如果 requirements.txt 不变,这层会被缓存,加速后续构建);再复制所有代码;声明容器运行时监听的端口;最后指定启动命令。

4.2 编写Docker Compose文件

对于单服务,似乎用 docker build docker run 就够了。但Compose文件( docker-compose.yml )的优势在于它能以声明式的方式定义整个应用栈(服务、网络、卷),并且便于管理。即使只有一个服务,我也推荐使用Compose,因为配置更清晰,且易于未来扩展(比如加入数据库)。

在项目根目录创建 docker-compose.yml

version: '3.8'

services:
  webapp:
    build: .
    container_name: my_flask_app
    ports:
      - "5000:5000"
    environment:
      - FLASK_ENV=production
    restart: unless-stopped
    volumes:
      - ./logs:/app/logs

这个配置定义了一个名为 webapp 的服务:使用当前目录的Dockerfile构建镜像;将容器命名为 my_flask_app ;将宿主机的5000端口映射到容器的5000端口;设置环境变量;设置重启策略(除非手动停止,否则异常退出会自动重启);挂载一个卷,将宿主机当前目录下的 logs 文件夹映射到容器的 /app/logs ,用于持久化日志。

注意 :这里将端口直接映射到宿主机,只是为了测试方便。在生产部署中,我们通常不会直接将应用端口暴露给公网,而是通过宿主机上的Nginx反向代理来访问,因此Compose文件中可以去掉 ports 映射,或者只映射到 127.0.0.1:5000:5000

5. 部署与上线:整合Nginx与HTTPS

5.1 构建并运行容器

在服务器上,将你的项目代码上传(可以用Git克隆,或用 scp 命令)。进入项目目录,运行:

docker compose up -d --build

-d 代表后台运行, --build 表示在启动前重新构建镜像。执行后,使用 docker compose ps 查看服务状态, docker compose logs -f webapp 查看实时日志。此时,你的应用应该已经在容器内运行,并监听5000端口。你可以在服务器上执行 curl http://localhost:5000 测试是否正常响应。

5.2 配置Nginx反向代理

现在,我们需要让外网用户能通过80/443端口访问我们的应用。编辑Nginx的站点配置文件,通常放在 /etc/nginx/sites-available/ ,然后软链接到 /etc/nginx/sites-enabled/

创建配置文件 /etc/nginx/sites-available/myapp

server {
    listen 80;
    server_name yourdomain.com www.yourdomain.com; # 替换为你的域名

    location / {
        proxy_pass http://127.0.0.1:5000; # 指向Docker容器应用
        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;
    }

    # 可选的静态文件服务(如果你的应用有静态文件)
    # location /static/ {
    #     alias /path/to/your/static/files;
    #     expires 30d;
    # }
}

关键点在于 proxy_pass 指令,它将所有到达Nginx 80端口的请求,转发给了本机(127.0.0.1)的5000端口,也就是我们Docker容器内应用监听的端口(通过之前的端口映射暴露给了宿主机)。

创建软链接并测试配置:

ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/
nginx -t # 测试配置文件语法
systemctl reload nginx # 重载配置,不中断服务

现在,通过浏览器访问你的服务器公网IP或域名(如果域名已解析),应该就能看到你的应用了。

5.3 使用Certbot获取并配置HTTPS证书

在当今,HTTPS是必须的。Let‘s Encrypt提供了免费的SSL/TLS证书,我们可以用Certbot工具自动化获取和续期。

安装Certbot和Nginx插件:

apt install -y certbot python3-certbot-nginx

然后运行Certbot,它会自动读取你的Nginx配置,列出 server_name ,并引导你完成证书申请和Nginx配置更新:

certbot --nginx

按照提示操作,选择你要申请证书的域名,并选择是否将HTTP流量重定向到HTTPS(强烈建议选择是)。成功后,Certbot会自动修改你的Nginx配置文件,添加SSL相关配置,并设置自动续期任务。

完成后,再次访问你的域名,地址栏应该显示安全的HTTPS连接。你的Nginx配置也会被Certbot更新,类似于:

server {
    listen 443 ssl http2;
    server_name yourdomain.com www.yourdomain.com;
    ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;
    # ... 其他SSL配置由Certbot自动管理 ...

    location / {
        proxy_pass http://127.0.0.1:5000;
        # ... proxy_set_header 配置 ...
    }
}

server {
    listen 80;
    server_name yourdomain.com www.yourdomain.com;
    return 301 https://$server_name$request_uri; # HTTP重定向到HTTPS
}

6. 生产环境考量与进阶优化

至此,一个基本的“从零到一”部署流程已经完成。但对于真正的生产环境,我们还需要考虑更多。

6.1 使用独立的Docker网络

默认情况下,Compose会为你的项目创建一个独立的桥接网络。但为了更精细的控制,或者让多个Compose项目间的容器能安全通信,可以定义自定义网络。在 docker-compose.yml 中:

networks:
  app-network:
    driver: bridge

services:
  webapp:
    # ... 其他配置 ...
    networks:
      - app-network

然后在Nginx的 proxy_pass 中,可以使用Compose服务名作为主机名,前提是Nginx也在同一个网络中(可以将宿主机Nginx也容器化,或者使用 host 网络模式)。对于宿主机Nginx,继续使用 127.0.0.1 即可。

6.2 数据持久化与备份

如果你的应用有数据库(如MySQL、PostgreSQL),务必使用**命名卷(Named Volume) 绑定挂载(Bind Mount)**来持久化数据,防止容器删除后数据丢失。

docker-compose.yml 中为数据库服务添加卷:

services:
  db:
    image: mysql:8.0
    volumes:
      - db_data:/var/lib/mysql # 命名卷
      - ./mysql/conf.d:/etc/mysql/conf.d:ro # 绑定挂载,只读配置文件
    environment:
      MYSQL_ROOT_PASSWORD: your_strong_password

volumes:
  db_data: # 声明命名卷

对于重要的数据卷,需要建立定期备份机制,可以使用 cron 任务执行 docker run --rm -v db_data:/volume -v /path/to/backup:/backup alpine tar czf /backup/backup-$(date +%Y%m%d).tar.gz -C /volume ./ 之类的命令。

6.3 日志管理与监控

Docker默认的日志驱动是 json-file ,日志会存储在宿主机的 /var/lib/docker/containers/ 下。对于生产环境,可以考虑:

  1. 配置日志轮转 :在 /etc/docker/daemon.json 中配置 log-driver log-opts ,限制日志文件的最大大小和数量。
    {
      "log-driver": "json-file",
      "log-opts": {
        "max-size": "10m",
        "max-file": "3"
      }
    }
    
  2. 使用集中式日志 :将容器日志发送到ELK(Elasticsearch, Logstash, Kibana)或Loki等日志聚合系统。
  3. 基础监控 :使用 docker stats 命令查看容器资源使用情况,或部署开源的监控方案如Prometheus + Grafana,配合cAdvisor收集容器指标。

6.4 使用 .env 文件管理敏感信息

绝对不要在 docker-compose.yml 中硬编码密码、密钥等敏感信息。应该使用环境变量文件( .env )。

  1. 在项目根目录创建 .env 文件:
    MYSQL_ROOT_PASSWORD=your_very_strong_password_here
    SECRET_KEY=your_flask_secret_key
    
  2. docker-compose.yml 中引用:
    services:
      webapp:
        # ...
        environment:
          - SECRET_KEY=${SECRET_KEY}
      db:
        # ...
        environment:
          - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}
    
  3. .env 文件添加到 .gitignore 中,确保不会被提交到版本库。在服务器上,单独维护这个文件。

6.5 实现CI/CD自动化

手动上传代码、构建、重启容器效率低下。可以结合GitHub Actions、GitLab CI/CD或Jenkins等工具,实现自动化部署。基本流程是:代码推送到特定分支(如 main ) → CI/CD平台检测到变更 → 在平台上或通过SSH连接到你的服务器 → 拉取最新代码 → 执行 docker compose up -d --build → 完成部署。

一个简单的GitHub Actions工作流示例( .github/workflows/deploy.yml ):

name: Deploy to Server
on:
  push:
    branches: [ main ]
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v3
      - name: Deploy via SSH
        uses: appleboy/ssh-action@v0.1.5
        with:
          host: ${{ secrets.SERVER_HOST }}
          username: ${{ secrets.SERVER_USER }}
          key: ${{ secrets.SSH_PRIVATE_KEY }}
          script: |
            cd /path/to/your/project
            git pull origin main
            docker compose up -d --build

需要在GitHub仓库的Settings -> Secrets中配置 SERVER_HOST SERVER_USER SSH_PRIVATE_KEY

走完这一整套流程,你收获的不仅仅是一个在线的项目,更是一套可复用的、对现代应用部署的深刻理解。从最基础的服务器安全,到服务编排,再到反向代理和自动化,每一步都踩得踏实,未来无论项目复杂度如何增长,你都有足够的知识储备去应对和扩展。

更多推荐