我自己家用宽带算电信的,想要搭建服务首先需要公网ip,电信在几年前就把所有的公网ip回收,只发放100.64.0.0的内网ip.如果想要外网ip首先就得去打电话申请.我自己是浙江宁波的,要公网ip没花多大力气,一句话的事情.但是想要用公网ip搭建web服务,就需要80,443,8080端口,这几个端口电信算不会给动态外网ip的家用宽带开放的,想要搭网站,就得用cdn来帮忙.我会一步步详细,列出步骤,完成这些步骤的操作,需要你熟悉linux的定时任务,和一定的开发能力,如果你是小白或者初学者,这个教程对你来说非常有难度.

使用的服务: 腾讯云[云解析,ssl证书,cdn,云解析api](可以使用其他厂商的服务,各个厂商之间基本都差不多),php(可以使用其他变成语言),nginx(可以替换为apache),linux定时任务 使用的硬件: 一台电脑,一个有点高大上的路由器

获取公网ip

这一步,我只知道电信是打电话就可以完成,其他宽带我就不清楚了,毕竟没用过.我是直接和客服说我要公网ip,客服就给我开通了,告诉我过一会重启光猫就行.但是重启以后,确实是公网ip,但是只有出的数据没有入的数据,反正最后就是打客服电话,直到能用了为止.

架设服务器

问客服要公网ip的同时,你还必须要求客服将光猫的上网模式改为桥接模式,也就是用路由器去拨号.这样做的目的,是让公网ip挂在路由器上,而不是光猫.

电信给的光猫功能很简单,对于开发者来说是不够用的.如果ip在光猫上,那么你想做一些家用网盘,web网站之类的服务就会多一道门槛.总之就是光猫只用来连接,拨号挂服务器的操作都交给路由器.(如下图,一共也就这么一个能用的功能,对比我的八爪鱼,这玩意简直就是渣渣,这也是为什么要用光猫桥接模式,让公网ip在路由器上的原因之一)

QQ截图20191225213007.png

我周围家用带宽都是100M起步,如果想在家里跑满带宽,要准备千兆路由器和超五类以上的网线,超五类的价格和六类差不多,反正我选得选超六类.路由器我用的是RT-AC5300和RT-AC66U-B1,比一般的tp-link强太多太多,毕竟一分钱一分货,用它来做一些网络服务,完全能满足需求,上个图给你们看看

QQ截图20191225200644.png

将linux服务器发布到公网

这一步你可以选择只暴露81端口也可以将整台主机暴露到公网上(只要公网能访问就行)

RT-AC5300为例,我选择的是将整台主机暴露到公网(我比较懒,对边左边的端口转发,DMZ设置真的很简单)

保存设置以后就可以了,安装完web服务并修改端口,就可以从外网访问web服务

QQ截图20191225212337.png

动态dns

网络服务必须有固定的地址,固定ip或者一个不变的域名,家用宽带都是动态ip,所以只能选择域名,RT-AC5300自带的ddns,也就那样,重点是免费,但是不能满足我的需求,有的家用宽带会在一定时间内断线,重连之后ip就变了,这个ddns没办法实时改变解析的ip,想要高可用,只能另外想办法.既然你要搭网站,肯定要购买域名的,而且在各大云服务器厂商的购买的域名都会有对于的api可以通过代码来改变解析的信息.我在腾讯云买的域名,所以就能用腾讯云的api接口去动态解析.

QQ截图20191225201025.png

linux定时任务+php+腾讯云api 更及时解析域名

这一步需要你有一定的开发能力,linux系统的操作能力,阅读api文档的能力,如果没有,这一步可能得找人帮忙了.php可以换成其他语言,也可以用shell编写,只要能发送请求,跑通api接口就行.

我以php为例

安装php

  • 没有php就先安装
#安装php解析器
yum install -y php

腾讯云api接口编写

需要用到的两篇文档,如果你的域名是在阿里云购买的,你就需要去查看阿里云的文档,代码写了注释就不讲解了,有开发能力的自然能看懂

<?php 
/**
 * 获取ip接口地址
 * 获取ip接口文档地址 http://ip.taobao.com/index.html
 * 淘宝获取本机IP地址 http://www.taobao.com/help/getip.php
 */

// 淘宝提取ip不可靠
// $ip = json_decode(json_decode(curl('http://ip.taobao.com/service/getIpInfo.php?ip=myip'),true)['body'],true)['data']['ip'];

/**
 * 获取ip接口地址
 * 获取ip接口文档地址 https://ip-api.com/
 * ip-api获取本机IP地址 https://ip-api.com/docs
 */

// $ip = json_decode(json_decode(curl('http://ip-api.com/json/?lang=zh-CN&fields=status,message,query'),true)['body'],true)['query'];
$ip = json_decode(json_decode(curl('https://api.ipify.org/?format=json&callback=?'),true)['body'],true)['ip'];

// 如果没有获取到ip就退出
if(!$ip)
{
    echo 'false';
    exit;
}
// https://cloud.tencent.com/document/api/1427/56194
// 使用签名方法 v1 的公共参数
// https://cloud.tencent.com/document/api/1427/56188#.E4.BD.BF.E7.94.A8.E7.AD.BE.E5.90.8D.E6.96.B9.E6.B3.95-v1-.E7.9A.84.E5.85.AC.E5.85.B1.E5.8F.82.E6.95.B0
// 修改记录
// https://cloud.tencent.com/document/api/1427/56157
// 获取域名的解析记录
// https://cloud.tencent.com/document/api/1427/56166

// 控制台秘钥
$SecretId = '填自己的密钥id';
$secretKey = '填自己密钥的key';


// 请求类型
$method = 'GET';
// 请求地址
$url = 'dnspod.tencentcloudapi.com/?';
// 公共参数
$common = [
    'Action' => '', // Y 操作的接口名称。取值参考接口文档中输入参数公共参数 Action 的说明。例如云服务器的查询实例列表接口,取值为 DescribeInstances。
    // 'Region' => '', // - 地域参数,用来标识希望操作哪个地域的数据。接口接受的地域取值参考接口文档中输入参数公共参数 Region 的说明。注意:某些接口不需要传递该参数,接口文档中会对此特别说明,此时即使传递该参数也不会生效。
    'Timestamp' => time(), // Y 当前 UNIX 时间戳,可记录发起 API 请求的时间。
    'Nonce' => '1997', // Y 用户可自定义随机正整数,与 Timestamp 联合起来, 用于防止重放攻击。
    'SecretId' => $SecretId, // Y 在云API密钥 上申请的标识身份的 SecretId,一个 SecretId 对应唯一的 SecretKey , 而 SecretKey 会用来生成请求签名 Signature。
    // 'Signature' => '', // Y 请求签名,用来验证此次请求的合法性,需要用户根据实际的输入参数计算得出。具体计算方法参见 文档。
    'Version' => '', // Y 操作的 API 的版本。取值参考接口文档中入参公共参数 Version 的说明。例如云服务器的版本 2017-03-12。
    'SignatureMethod' => 'HmacSHA256', // Y 签名方式,目前支持 HmacSHA256 和 HmacSHA1。只有指定此参数为 HmacSHA256 时,才使用 HmacSHA256 算法验证签名,其他情况均使用 HmacSHA1 验证签名。
    // 'Token' => '', // N 即 安全凭证服务 所颁发的临时安全凭证中的 Token,使用时需要将 SecretId 和 SecretKey 的值替换为临时安全凭证中的 TmpSecretId 和 TmpSecretKey。使用长期密钥时不能设置此 Token 字段。
    // 'Language' => 'zh-CN', // N 指定接口返回的语言,仅部分接口支持此参数。取值:zh-CN,en-US。zh-CN 返回中文,en-US 返回英文。
];

// 腾讯云 云解析 修改解析记录请求参数
$input = [
    'Action' => 'ModifyRecord', // Y 公共参数,本接口取值:ModifyRecord。
    'Version' => '2021-03-23', // Y 公共参数,本接口取值:2021-03-23。
    // 'Region' => '', // N 公共参数,本接口不需要传递此参数。
    'Domain' => '1997sty.com', // Y 域名
    'RecordType' => 'A', // Y 记录类型,通过 API 记录类型获得,大写英文,比如:A 。
    'RecordLine' => '默认', // Y 记录线路,通过 API 记录线路获得,中文,比如:默认。
    'Value' => $ip, // Y 记录值,如 IP : 200.200.200.200, CNAME : cname.dnspod.com., MX : mail.dnspod.com.。
    'RecordId' => '786106059', // Y 记录 ID 。
    // 'DomainId' => '', // N 域名 ID 。参数 DomainId 优先级比参数 Domain 高,如果传递参数 DomainId 将忽略参数 Domain 。
    'SubDomain' => 'ddns', // N 主机记录,如 www,如果不传,默认为 @。
    // 'RecordLineId' => '', // N 线路的 ID,通过 API 记录线路获得,英文字符串,比如:10=1。参数RecordLineId优先级高于RecordLine,如果同时传递二者,优先使用RecordLineId参数。
    'MX' => '0', // N MX 优先级,当记录类型是 MX 时有效,范围1-20,MX 记录时必选。
    'TTL' => '600', // N TTL,范围1-604800,不同等级域名最小值不同。
    // 'Weight' => '', // N 权重信息,0到100的整数。仅企业 VIP 域名可用,0 表示关闭,不传该参数,表示不设置权重信息。
    // 'Status' => '', // N 记录初始状态,取值范围为 ENABLE 和 DISABLE 。默认为 ENABLE ,如果传入 DISABLE,解析不会生效,也不会验证负载均衡的限制。
];

// 腾讯云 云解析 获取解析记录请求参数
// $input = [
//  'Action' => 'DescribeRecordList', // Y 公共参数,本接口取值:DescribeRecordList。
//  'Version' => '2021-03-23', // Y 公共参数,本接口取值:2021-03-23。
//  // 'Region' => '', // N 公共参数,本接口不需要传递此参数。
//  'Domain' => '1997sty.com', // Y 要获取的解析记录所属的域名
//  // 'DomainId' => '', // N 要获取的解析记录所属的域名Id,如果传了DomainId,系统将会忽略Domain参数
//  // 'Subdomain' => '', // N 解析记录的主机头,如果传了此参数,则只会返回此主机头对应的解析记录
//  // 'RecordType' => '', // N 获取某种类型的解析记录,如 A,CNAME,NS,AAAA,显性URL,隐性URL,CAA,SPF等
//  // 'RecordLine' => '', // N 获取某条线路名称的解析记录
//  // 'RecordLineId' => '', // N 获取某个线路Id对应的解析记录,如果传RecordLineId,系统会忽略RecordLine参数
//  // 'GroupId' => '', // N 获取某个分组下的解析记录时,传这个分组Id
//  // 'Keyword' => '', // N 通过关键字搜索解析记录,当前支持搜索主机头和记录值
//  // 'SortField' => '', // N 排序字段,支持 name,line,type,value,weight,mx,ttl,updated_on 几个字段。
//  // 'SortType' => '', // N 排序方式,正序:ASC,逆序:DESC。默认值为ASC。
//  // 'Offset' => '', // N 偏移量,默认值为0。
//  // 'Limit' => '', // N 限制数量,当前Limit最大支持3000。默认值为100。
// ];

// 合并参数
$key = array_merge($common,$input);
// 进行键排序
ksort($key);
// 创建变量
$keystr = '';
// 循环拼接字符串
foreach ($key as $k => $v) 
{
    $keystr .= $k.'='.$v.'&';
}
// 拼接签字字符串
$srcStr = $method.$url.rtrim($keystr,'&');
// 生成签名字符串  请求签名,用来验证此次请求的合法性,需要用户根据实际的输入参数计算得出。
// 验证时,如果有中文 需要验证中文字符串 发送请求时改为中文的url编码
$signStr = base64_encode(hash_hmac('sha256', $srcStr, $secretKey, true));
// 拼接请求地址
$str = 'https://'.$url.http_build_query($key).'&Signature='.urlencode($signStr);
// 发送请求
$res = json_decode(curl($str,$method),true)['body']; echo $res."\n";






/**
 * CURL发送请求
 * @param string $url 地址
 * @param string $method 请求方式
 * @param array|string $data 请求参数
 * @param array $header 请求头
 * @return string 返回参数
 */
function curl($url,$method='GET',$data=null,$header=null)
{
    // 启动一个CURL会话
    $curl = curl_init();
    // 要访问的地址
    curl_setopt($curl,CURLOPT_URL,$url);
    //返回数据不直接输出
    curl_setopt($curl,CURLOPT_RETURNTRANSFER,true);
    curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1); // 使用自动跳转
    curl_setopt($curl, CURLOPT_AUTOREFERER, 1); // 自动设置Referer
    curl_setopt($curl, CURLOPT_TIMEOUT, 30); // 设置超时限制防止死循环
    curl_setopt($curl, CURLOPT_HEADER, true); // 显示返回的Header区域内容

    // curl 请求头设置
    if(!empty($header))
    {
        curl_setopt($curl, CURLOPT_HTTPHEADER, $header); // 模拟参数传递
    }
    else
    {
        // curl_setopt($curl, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']); // 模拟用户使用的浏览器
    }

    // https
    // https请求 不验证证书和hosts  php版本过低需要开启此项 否则会无法访问https的地址
    curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE); 
    // 从证书中检查SSL加密算法是否存在(默认不需要验证  php版本过低需要开启此项 否则会无法访问https的地址
    curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE);

    //如果是post请求
    if($method === 'POST' || $method === 'post')
    {
        //开启发送post请求选项
        curl_setopt($curl,CURLOPT_POST,true);
        //发送post的数据
        curl_setopt($curl,CURLOPT_POSTFIELDS,$data);
    }
    //发送请求
    $res = curl_exec($curl);
    // 返回参数包含请求头 则此条无效
    // $data = json_decode($res,JSON_UNESCAPED_UNICODE);
    // 获取响应头大小
    $headerSize = curl_getinfo($curl, CURLINFO_HEADER_SIZE);
    // 根据头大小去获取头信息内容
    $header = substr($res, 0, $headerSize);
    // 根据头大小去获取响应信息内容
    $body = substr($res, $headerSize);
    // 获取http状态码
    $return_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
    if(substr($body,0,1) != '{')
    {
        $body = htmlspecialchars($body);
    }
    //关闭连接
    curl_close($curl);
    $arr = [
        'header' => $header,
        'body' => $body,
        'return_code' => $return_code,
    ];
    $res = json_encode($arr);
    return $res;
}

设置定时任务

通过linux定时任务来实时更新解析,确保域名解析正确

  • 我设置的算5分钟同步一次,当然求稳的老哥可以设置1分钟1次这样能提高可用性,记得把输出结果定向到黑洞文件,以免定时任务产生大量的日志
#编辑定时任务
crontab -e

#每5分钟执行一次该脚本
*/5 * * * * /usr/bin/php /root/txclould_edit_cname_ip.php > /dev/null 2>&1

安装web软件

apache或者nginx都可以,我使用的是nginx,两种安装方式选择一种即可

yum安装(推荐)

使用官方yum源进行安装(推荐)

  • 安装的是最新版本
  • 软件目录结构比较标准

非官方yum源进行安装

  • 安装的不是最新版
  • 目录结构会发生变化

官方yum源安装步骤

  • 创建该文件后使用yum install -y nginx
#我用的是官方yum源,如果你不设置这一步直接使用yum install -y nginx安装,可能默认的配置文件会和我的不同
vi /etc/yum.repos.d/nginx.repo

[nginx-stable]
name=nginx stable repo
baseurl=http://nginx.org/packages/centos/$releasever/$basearch/
gpgcheck=1
enabled=1
gpgkey=https://nginx.org/keys/nginx_signing.key
  • 启动nginx和设置开机启动
systemctl start nginx
systemctl enable nginx

编译安装

  • 下载安装包,解压
wget http://nginx.org/download/nginx-1.16.0.tar.gz
tar -xf nginx-1.16.0.tar.gz
  • 进入目录,进行配置操作
#进入解压后的目录
cd nginx-1.16.0.tar.gz
#--help 可以查看参数
./configure --help
#配置参数后的命令
./configure --prefix=/opt/demo/nginx --add-module=/home/fastdfs-nginx-module/src  --with-http_stub_status_module --with-http_ssl_module

参数说明

  • --prefix : 用于指定nginx编译后的安装目录
  • --add-module : 为添加的第三方模块,此次添加了fdfs的nginx模块
  • --with..._module : 表示启用的nginx模块,如此处启用了http_ssl_module模块

可能出现的错误

  • ./configure: error: the HTTP rewrite module requires the PCRE library.
  • 解决方法:yum install -y pcre-devel
  • SSL modules require the OpenSSL library.
  • 解决方法:yum install -y openssl-devel

nginx服务配置文件

想要让网页显示 80,443,8080都是不行的,我这边选择把监听端口改成81,在/etc/nginx/conf.d/default.conf中改动

改动之后记得重启nginx服务,访问一下

  • 主配置文件 : /etc/nginx/nginx.conf
#定义worker进程管理的用户
user  nginx;
#定义有几个worker进程
worker_processes  1;
#定义错误日志路径信息
error_log  /var/log/nginx/error.log warn;
#定义pid文件路径信息
pid        /var/run/nginx.pid;
#一个worker进程可以同时接收1024访问请求
events {
    worker_connections  1024;
}
http {
#加载一个配置文件
    include       /etc/nginx/mime.types;
#指定默认识别文件类型
    default_type  application/octet-stream;
#定义日志的格式
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';
#指定日志路径
    access_log  /var/log/nginx/access.log  main;
    sendfile        on;
    #tcp_nopush     on;
#超时时间
    keepalive_timeout  65;
    #gzip  on;
#加载名称匹配的配置文件
    include /etc/nginx/conf.d/*.conf;
}
  • 扩展配置(虚拟主机配置文件) : /etc/nginx/conf.d/default.conf
server {
#监听端口
    listen       81;
#网站域名
    server_name  localhost;
    location / {
#站点目录位置
        root   /usr/share/nginx/html;
#首页文件
        index  index.html index.htm;
    }
#错误页面信息
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
}

访问结果

QQ截图20191225205508.png

设置cdn服务

cdn我选择的是腾讯云的,具体设置参考官方文档(点我).而你需要准备的东西有2个,一个是家用宽带的域名,一个是cdn使用的域名.

  • cdn.home.1997sty.com:81这个域名我指向的是家用宽带的ip地址,也就是api接口实时更新解析记录的地址
  • home.1997sty.com这个域名我指向的是cdn服务器,是正式访问的地址

QQ截图20191225210623.png

设置完成后,自动启动,过几分钟就能用正式访问的地址访问网站,因为设置81端口,记得在源站设置的时候加上端口号

QQ截图20191225210638.png

外网访问效果

QQ截图20191225210858.png

设置强制https

  • 按照下图配置,不需要在nginx上操作,只需要设置cdn即可
  • ssl证书可以点击sll证书管理去申请,反正有免费的
  • 设置完成后过几分钟再访问,会将请求强制跳转到https

QQ截图20191225211023.png

QQ截图20191225211036.png

QQ截图20191225211105.png

访问效果

QQ截图20191225211806.png