前言
如题,笔者最近几天为公司搭建了一个官网,再加上之前一段时间给公司开发的几个子站点,在具体部署的时候,发现了几个坑,特别是关于域名映射 IP 方面的,所以在这里总结记录下。
为了使得整体的叙事更加连贯,这边也会顺便说下前期开发 web 网站的一些基本流程。
web网站开发流程
简单的说,一个 web 网站的开发可以分为:可行性研究,需求分析,原型设计,UI设计,编码,测试,部署等几个阶段。举一个最简单的例子,比如某天公司领导要我们为公司开发一个官网,那么可能简单商量了几下大概的需求之后,我们就开始写需求文档了,或者有时候为了图省事,直接就开始利用 AI 工具来开发原型页面了。现在的 AI 时代,我们绝大部分软件开发设计场景都可以用 AI 工具来做辅助,常见的比如 codex, Claude, workbuddy, traebuddy, codebuddy, trae, cursor (包括 VS Code 等挂上对应的 AI 插件也可以)等等。这里面有些工具适合辅助写文档和需求,有些则适合写代码,这边就没有具体区分了。
那么可能我们直接就按照领导的需求设计出来对应的原型,经过原型评审之后,将改好的合适的原型发给 UI 设计师,让 UI 设计师对应进行 UI 设计。这里其实流程也和以前不同了。比如过去我们常常是用 Axure 等工具来进行原型设计,这样设计当然不会错,但是拖动组件什么的比较低效,那么现在完全可以把需求整理成相应的文档告诉 AI 工具,让 AI 工具直接生成对应的 web 网站,涉及到数据交互的部分就 mock 生成。然后我们把对应的原型 web 站点部署(或者直接内网访问,比如内网IP + 端口,绝大部分前端框架都是支持的)后,把地址告诉 UI 设计师,让他们根据原型稿进行设计。理想情况下,UI 设计师在设计完对应的 UI 稿后,也不需要再给程序员一张张设计图纸,而是可以直接给对应 UI 设计稿的 html/vue/TSX 文件,这个现在很多 UI 设计平台都可以做到。这样比之原来也是大大提升了相关的效率。再过去,前端工程师要根据 UI 设计稿的内容去一步步还原,包括下载对应的图片素材、取色、取样式、布局、修改内外边距等等。这个做法在 AI 时代也显得效率有些低了。比如如果公司用的前端技术栈是 React 系的,那么 UI 设计师直接给出对应各个页面的 TSX 文件即可,这样对应的程序员就在 UI 给出的工程文件基础上进行开发,也就是搭建数据库、开发接口、绑定接口、实现交互等等。这就意味着,前后端开发分成两拨人的时代也要渐渐消退了。前后端在具体开发的适合,自然还是要分离的,这是系统架构的进步,但是却不需要分成两拨人。
逐渐消失的前端/后端工程师
这里的意思是慢慢地不需要再有前端/后端工程师这个头衔了(当然这针对一些规模不大的项目,可能在一些大项目及特殊项目中,这两拨人还是存在),实际上的工作内容会被一拨人(原本是前端/后端/全栈工程师)取代。原因是前后端开发虽然使得整体 web 开发的效率变高了,但是却也带来了一个新的痛点,那就是前后端联调。所谓的联调,就是前端工程师画好了页面、后端工程师开发好了接口后,由前端开始逐步的集成后端的接口了。在过去,经常出现这样的场景:前端开发表示页面已经开发完毕了,但是大量的按钮、页面无法点击;后端开发表示接口开发完了,但是大量的接口报500,404等;前端表示某个字段没有返回,后端说设计如此......这些问题都是客观存在的,过往的编码工期,如果是是 50 人天,那么在前后端联调上花费的时间可能会超过25人天,这其中,实际有效的工时可能只有 10 人天。那么现在,随着 AI 时代的兴起,原本的前后端工程师都可以转型的通用型的工程师,在开发一些大型项目的时候,可以不必以前后端的形式划分开发职责,而是以功能模块的形式分配任务。这里面最重要的点是相关的开发人员都要熟悉前后端的开发逻辑。就是说我可以不知道具体的代码是怎么写的,但是得知道大概是一个怎样的流程,以及数据库应该怎么设计,哪些功能留在后端,哪些功能在前端实现,我期待的样子是怎样的,那么把这些内容以文字、图片等形式告诉 AI,具体的实现由 AI 工具来执行。
在由通用工程师将对应的UI 设计稿还原并开发完相应的功能后,他们还需要进行自测,自测基础功能没问题后,可以把对应的功能模块提交给测试人员进行测试。测试人员还是要遵循传统的黑盒测试流程,比如站在用户的角度,提出各种问题。当然,在 AI 时代,测试人员也可以利用 AI 工具辅助进行一些其他的测试,比如后端接口测试、压力测试、性能测试、安全测试等等,在过去可能要涉及到大量的编码工作,现在编码工作相对就会少了很多。
web 网站部署
测试工作完成后会把相关的网站进行部署。大公司有专门的运维人员,小公司这块的工作会交给普通的程序员来做。这里漏了一个小话题,就是一般 web 站点的运行环境可以分为开发环境、测试环境和生产环境,有些可能还会有预发布环境等。测试的测试工作一般是在测试环境进行的。最终测试没问题后相关的项目会被发布到生产环境。生产环境通常是由域名访问某个站点。这个原来是一道很经典的面试题,就是我们在浏览时上输入一个网址到我们看到对应的网站内容时,发生了哪些事情。通常一个公司会购买一个或多个域名,比如 google.com, jd.com等等,域名的后缀很多,比如 .cn .com .org .net .top .cc 等等,不同的域名含义也不同,价格也不同(和域名本身的长短也有关系),这个可以具体参考下一些资料,这里不再赘述。在购买了域名后(通常是在一些域名服务商那里购买,比如阿里云、腾讯云等),还需要配置 DNS 解析服务,也就是我们的域名对应公网 IP 地址。比如我们在浏览器输入栏输入 google.com , 浏览器会先去本地或者各级 DNS 服务器找该域名对应的 DNS 记录,找到之后访问对应的公网 IP,这里 http 默认的端口是 80,https 默认的端口是 443,当然,有些域名或者 IP 还会带其他端口,比如 8080,9090 等等。这里需要注意一点是,在中国,如果网站要正常运营,还需要申请域名 ICP 备案和公安备案(其他一些服务还需要对应的许可证),并且相关的备案信息要放在网站下发醒目位置,如果没放被相关执法部门查到,可能会电话通知或者令网站下线整改。这里还要插一句,如果是需要启用 https 协议,一般是要购买对应的 https 证书的,确实也有免费的可以申请使用,但是通常 1-3 个月就会过期,过期了得再申请。付费的版本过期时间比较长,通常是 1-2 年,也有短的,比如半年的。https 证书也分为通配符证书和特定的证书,这个价钱是不同的,比如 *.google.coom 这种就是通配符证书,会贵一些,一般的比如 example.google.com 这种证书会更便宜。
说回前面,我们在域名服务商的业务系统里配好对应的 DNS 解析公司的公网 IP (可能是公司的自建服务器,也可能是云服务器,比如前文说的阿里云等)之后,比较简单的做法是把我们开发好的项目部署到这个公网 IP 对应的服务器上。比如就部署 443 端口上,这样用户访问对应的域名就可以访问到开发好的 web 站点了,比如 example.com 。但是这样做会有一个问题,就是如果我司又新开发了几个新的 web 网站都需要部署到外网访问(甚至有时候部署的服务器没有对应的公网IP,只有内网IP),这时候可以通过新开公网 IP 或者在原来的公网 IP 的其他端口部署来实现,但是这样做有几个不好的地方,首先,必须经常配置 DNS 解析服务,新来一个站点就要新增一条 DNS 解析的记录,其次,如果是将站点部署到非 80/443 的端口,那么用户实际访问的时候,得以域名 + 端口的形式方可访问成功,比如example:8080, 这样看起来就不太好看了,而且也增加了用户的记忆负担。那么面对这种情况,通常我们是怎么做的?答案是 nginx 反向代理。
nginx 反向代理
首先,我们需要在 DNS 解析对应的公网IP上部署 nginx 服务(可以用 docker 或直接按照),取决于业务规模,这台服务器可以认为就是专门的 nginx 服务器(当然也可以在这台服务器部署别的服务)。关于 nginx 的具体配置,以下是一份参考 conf 文件:
bash
# 把ssl配置抽离,多个server复用
# /etc/nginx/conf.d/ssl_common.conf
# ssl_certificate、ssl_certificate_key 不在此处,每个server独立指定证书路径
ssl_session_timeout 5m;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
ssl_prefer_server_ciphers on;
client_max_body_size 1024M;
# --------------------------
# 80端口:全部HTTP请求301跳转HTTPS
# --------------------------
server {
listen 80;
listen [::]:80;
# 关键:同时匹配裸域名 + 所有子域名
server_name example.com *.example.com;
# 永久重定向,带上原始请求URI
return 301 https://$host$request_uri;
}
# --------------------------
# 1.裸域名 example.com 官网入口
# 代理转发内网业务服务 192.168.xx.xx:3000
# --------------------------
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name example.com;
# HTTPS证书路径
ssl_certificate /etc/nginx/conf.d/v2x.example.com_nginx/example.com_ca.crt;
ssl_certificate_key /etc/nginx/conf.d/v2x.example.com_nginx/example.com.key;
# 引入公共ssl配置
include /etc/nginx/conf.d/ssl_common.conf;
location / {
proxy_pass http://192.168.xx.xx:3000;
# 代理关键请求头,向后端透传客户端真实信息
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
tcp_nodelay on;
}
# 后端服务异常5xx错误页面
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
# --------------------------
# 2. www.example.com 强制重定向到裸域名 example.com
# --------------------------
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name www.example.com;
ssl_certificate /etc/nginx/conf.d/v2x.example.com_nginx/example.com_ca.crt;
ssl_certificate_key /etc/nginx/conf.d/v2x.example.com_nginx/example.com.key;
include /etc/nginx/conf.d/ssl_common.conf;
# 301永久重定向,保留请求路径与参数
return 301 https://example.com$request_uri;
}
# --------------------------
# 3. v2x.example.com 小程序API后端
# 代理转发内网 192.168.xx.yy:8080
# --------------------------
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name v2x.example.com;
ssl_certificate /etc/nginx/conf.d/v2x.example.com_nginx/example.com_ca.crt;
ssl_certificate_key /etc/nginx/conf.d/v2x.example.com_nginx/example.com.key;
include /etc/nginx/conf.d/ssl_common.conf;
location / {
proxy_pass http://192.168.xx.yy:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
tcp_nodelay on;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
# --------------------------
# 4. avo.example.com web管理后台
# 代理转发内网 192.168.xx.yy:9090
# --------------------------
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name avo.example.com;
ssl_certificate /etc/nginx/conf.d/v2x.example.com_nginx/example.com_ca.crt;
ssl_certificate_key /etc/nginx/conf.d/v2x.example.com_nginx/example.com.key;
include /etc/nginx/conf.d/ssl_common.conf;
location / {
proxy_pass http://192.168.xx.yy:9090;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
tcp_nodelay on;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
如上,我这个案例里把 ssl(https) 证书分开写了,这是为了处理不同站点 ssl 证书不同的情况,如果各个站点(包括子域)的证书是一样的,可以合并 ssl 证书。这里是一个 nginx 服务器的公共配置文件,单台 Nginx 作为统一 HTTPS 入口,一张泛域名证书,对外暴露 443 端口,根据不同域名,反向代理到公网/内网多套后端服务。这里处于隐私的考虑隐藏了一些具体的域名和 IP 信息。这套 nginx 主要有以下作用:
- example.com → 内网 192.168.xx.xx:3000 官网服务
- www.example.com → 301 重定向到 example.com
- v2x.example.com → 内网 192.168.xx.yy:8080 API 接口服务
- avo.example.com → 内网 192.168.xx.yy:9090 Web 管理后台
- 所有 HTTP (80) 请求强制 301 跳转 HTTPS (443)
这样做有很多好处,首先,dns 服务我们只需要配置一次即可(dns服务本身配置也需要时间生效,而且可能不一定有权限登录公司的 dns 管理系统);其次,任意的内网/公网 IP + 任意端口的访问都可以以 https + 子域 的形式进行访问,既美观又方案安全;最后,公司所有的业务站点都在同一个 nginx 服务下配置,方便管理也已于理解,也避免了有时需要内网映射公网 IP 的麻烦。另外,还可以利用 nginx 做负载均衡、流量控制和动静分离等优化,所以这种做法百利而无一害。
总结
AI 时代,变化迅速,既有旧技术的利用,也有新技术的学习和生产效率的提高。不过,笔者始终认为程序员这个行业,这些岗位是不会被替代的,但是我们从事的内容可能会发生一些变化,就像工业革命时代,火车取代了马车夫一样,马车夫一样可以去火车站工作,人移动的需求始终存在,而我们应该做的,其实是终身学习。