从零搭建服务器:Docker容器化部署与Nginx反向代理实战
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
输入密码或使用密钥对登录。
登录后,立即进行以下几项操作:
-
更新系统包
:
apt update && apt upgrade -y。确保系统所有软件包都是最新的,修复已知安全漏洞。 -
创建新用户
:永远不要长期使用root用户操作。创建一个具有sudo权限的普通用户。
adduser yourusername usermod -aG sudo yourusername -
配置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。 务必在重启前测试用新用户和密钥能否登录,否则可能把自己锁在门外。
-
将
-
配置防火墙
: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/
下。对于生产环境,可以考虑:
-
配置日志轮转
:在
/etc/docker/daemon.json中配置log-driver和log-opts,限制日志文件的最大大小和数量。{ "log-driver": "json-file", "log-opts": { "max-size": "10m", "max-file": "3" } } - 使用集中式日志 :将容器日志发送到ELK(Elasticsearch, Logstash, Kibana)或Loki等日志聚合系统。
-
基础监控
:使用
docker stats命令查看容器资源使用情况,或部署开源的监控方案如Prometheus + Grafana,配合cAdvisor收集容器指标。
6.4 使用
.env
文件管理敏感信息
绝对不要在
docker-compose.yml
中硬编码密码、密钥等敏感信息。应该使用环境变量文件(
.env
)。
-
在项目根目录创建
.env文件:MYSQL_ROOT_PASSWORD=your_very_strong_password_here SECRET_KEY=your_flask_secret_key -
在
docker-compose.yml中引用:services: webapp: # ... environment: - SECRET_KEY=${SECRET_KEY} db: # ... environment: - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD} -
将
.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
。
走完这一整套流程,你收获的不仅仅是一个在线的项目,更是一套可复用的、对现代应用部署的深刻理解。从最基础的服务器安全,到服务编排,再到反向代理和自动化,每一步都踩得踏实,未来无论项目复杂度如何增长,你都有足够的知识储备去应对和扩展。
更多推荐


所有评论(0)