前言

这篇文章的解决办法适用在 Mac 上,无法在 Linux 上使用。

在开发网站的时候,我们起码会有两个环境:开发环境和线上环境。

假设线上域名为 domain.com,在开发环境里面,可以修改 /etc/hosts 把 domain.com 指向 127.0.0.1,在80端口监听的 Nginx 则会转发 domain.com 下的请求到 127.0.0.1。

这样的问题在于无法同时访问线上和开发环境,而且有时候还需要确认自己访问的是线上还是开发环境,建议使用 *.test.domain.com 来指向开发环境。比如 hr.domain.com,则开发环境则使用 hr.test.domain.com。

不使用hr.domain.dev是因为现在前后端分离的情况越来越普遍:

  • 在前后端不分离的情况下,本地开发环境可以使用 hr.domain.dev 作为本地域名,因为本地会启动一个服务器,不会出现 cookie 无法同步的问题。
  • 在前后端分离的情况下,如果本地域名叫 hr.domain.dev,就会出现 cookie 无法和 REST API 同步的问题。本地开发的域名设置为 hr.test.domain.com 就可以避免 cookie 无法和 REST API 同步的问题(有一个要求:服务器的 cookie 写在 /)。

为什么要做这件事

在实际的开发过程中,一个网站是由多个人协作开发,每个人都会参与不同的项目。那么每个开发者都需要针对每一个项目去修改 /etc/hosts 。

既然我们确定了开发环境中使用 .test.domain.com 结尾的域名,那么在 Mac 下是否能做到把 .test.domain.com 指向127.0.0.1的功能呢?如果可以做到这一点,那么所有的开发者在配置一个新项目的时候,都不需要再修改自己的 /etc/hosts 文件了。

原理

Mac 本身就提供了指定某一个后缀的 domain 使用的 nameserver 的功能,设置起来也非常简单。举例说明,假设我们要指定 *.dev 使用的 nameserver,则可以这么做:

  1. 终端进入 /etc/resolver,如果不存在这个文件夹,则创建这个文件夹。
  2. 在这个文件夹下创建文件名为 dev 的文件(需要使用 sudo,文件名就是要指定 nameserver 的domain 的后缀,如果是 *.test.domain.com,则文件名则为test.domain.com),内容如下:
nameserver 127.0.0.1
port 8000

那么,符合 *.dev 的域名都会使用监听了127.0.0.1下的8000端口的 dns server。

这个设置会立刻生效,可以使用 scutil 来检测自己的设置是否成功::

scutil --dns

在输出结果里面应该能看到这么一段:

resolver #10
  domain   : dev
  nameserver[0] : 127.0.0.1
  port     : 8000
  flags    : Request A records, Request AAAA records
Reachable, Local Address, Directly Reachable Address

那么我们就剩下了一个问题,怎么搭建一个本地的 dns server。

使用 dnsmasq

我们可以使用 dnsmasq 来搭建一个本地的 dns server,具体细节可以阅读 这篇文章

但是这个解决办法要求所有开发者的 Mac 上都得安装 dnsmasq,还需要修改系统的启动项。dnsmasq 安装和配置会有些麻烦,比如笔者直接使用 brew install dnsmasq 就无法在 Mac OS X 10.11下安装成功,在 brew update 之后才成功。

这个解决办法的好处是可以适用更广的环境,在 Linux 下应该也能使用这个解决办法。

在 node.js 环境中启动一个 dns server

这个解决办法参考自 pow

我们团队已经使用 node.js 实现了一个前端环境配置工具,这个工具可以帮助开发者配置 nginx。对我们而言,使用 node.js 启动一个 dns server 则成了一个最优解决方案。在这种情况下,不需要安装 dnsmasq,没有系统启动项,只需要更新一下这个前端配置工具即可。

dns server 使用的是这个仓库的代码:dnsserver。package.json 中这么配置:

"dnsserver": "https://github.com/sstephenson/dnsserver.js/tarball/4f2c713b2e"

启动 dns server 的代码如下:

var dnsserver = require('../lib/dnsserver');

var server = dnsserver.createServer();
server.bind(8000, '127.0.0.1');

server.on('request', function(req, res) {
  console.log("req = ", req);
  var question = req.question;

  if (question.type == 1 && question.class == 1) {
    // IN A query
    res.addRR(question.name, 1, 1, 3600, '127.0.0.1');
  } else {
    res.header.rcode = 3; // NXDOMAIN
  }

  res.send();
});

server.on('error', function(e) {
  throw e;
});

参考

Never touch your local /etc/hosts file in OS X again
resolver configuration file format
Do /etc/resolver/ files work in Mountain Lion for DNS resolution?