我的环境
Windows10
JDK8
SpringCloud:Hoxton.SR1
SpringBoot:2.2.4.RELEASE
spring-cloud-alibaba-dependencies:2.1.1.RELEASE(注意:包名是以com.alibaba.cloud开头)
Nacos-server:NACOS1.1.4
2. 启动nacos服务(单机模式&内嵌数据库)
0.前提:配置JAVA_HOME环境变量,不配置会导致无法运行Nacos
1.下载源码或者安装包
安装包地址:https://github.com/alibaba/nacos/releases
2.解压后进入nacos/bin目录
3.输入命令启动服务
linux:sh startup.sh -m standalone
windows:cmd startup.cmd
4.控制台启动下,看到"Nacos started successfully in stand alone mode.”后表示服务已启动
5.nacos默认使用8848端口,可通过http://127.0.0.1:8848/nacos/index.html进入自带的控制台界面,默认用户名/密码是nacos/nacos
3.配置相关的pom配置文件
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.1.1.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<!-- 1. nacos-服务发现功能依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>0.2.2.RELEASE</version>
</dependency>
<!-- 2. nacos-配置管理功能依赖,目前用不到 -->
<!-- <dependency>-->
<!-- <groupId>org.springframework.cloud</groupId>-->
<!-- <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>-->
<!-- <version>0.2.2.RELEASE</version>-->
<!-- </dependency>-->
注意:版本 0.2.x.RELEASE 对应的是 Spring Boot 2.x 版本,版本 0.1.x.RELEASE 对应的是 Spring Boot 1.x 版本。
例如:springboot2.2.4.RELEASE对应0.2.2RELEASE
4.修改application.properties。添加一些关于Nacos的配置,去掉关于Eureka配置
#指定nacos的ip和端口号,向nacos注册
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
5.修改启动类更换相关注解。如果在你的应用启动程序启动类加了@EnableEurekaClient ,需将其修改为@EnableDiscoveryClient
@SpringBootApplication
//@EnableEurekaClient
@EnableDiscoveryClient
public class ProviderApplication {…}
启动你的应用即可,至此,你已实现 “零行代码使用 Nacos 替换 Eureka”!
附录一:dependencyManagement和dependencies的区别
1.dependencies即使在子项目中不写该依赖项,那么子项目仍然会从父项目中继承该依赖项(全部继承)
2.dependencyManagement里只是声明依赖,并不实现引入,因此子项目需要显示的声明需要用的依赖。
如果不在子项目中声明依赖,是不会从父项目中继承下来的;只有在子项目中写了该依赖项,并且没有指定具体版本,才会从父项目中继承该项,
并且version和scope都读取自父pom;另外如果子项目中指定了版本号,那么会使用子项目中指定的jar版本
附录二:windows中nacos启动失败,显示8848端口被占用
方案一:重启电脑
方案二:修改nacos/conf/application.properties文件中的端口号,例如:
server.port=8848 ==> server.port=18848
6.将服务网关的配置文件配置到nacos的public名存空间下因为项目发布的时候其他空间的访问key会随着系统的改变而改变,public的话默认都是一样的
配置自己想要的如 访问数据库,服务网关连接路径可搜索
springcloud gateway配置大全 了解自己的配置
举个例子:
{
"refreshGatewayRoute": true, //刷新网关路由为true可默认刷新
"routeList": [
{
"id": "user-service-api02", //区别id
"predicates": [
{
"name": "Path",
"args": {
"_genkey_0": "/usr02/**" //访问路径+ant风格的表达式
}
}
],
"filters": [
{
"name": "StripPrefix",
"args": {
"_genkey_0": "1"
}
}
],
"uri": "lb://user-service-api", //你所访问消费者的名称
"order": 0
}
]
}
7.服务网关的配置
目录如下:
package com.zking.gatewayserver.config;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.PropertyKeyConst;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.listener.Listener;
import com.alibaba.nacos.api.exception.NacosException;
import com.zking.gatewayserver.entity.FilterEntity;
import com.zking.gatewayserver.entity.PredicateEntity;
import com.zking.gatewayserver.entity.RouteEntity;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
import org.springframework.cloud.gateway.filter.FilterDefinition;
import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import org.springframework.web.util.UriComponentsBuilder;
import reactor.core.publisher.Mono;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.Executor;
/**
* 此类实现了Spring Cloud Gateway + nacos 的动态路由,它实现一个Spring提供的事件推送接口ApplicationEventPublisherAware
*/
@Component
public class DynamicRoutingConfig implements ApplicationEventPublisherAware {
private final Logger logger = LoggerFactory.getLogger(DynamicRoutingConfig.class);
//private static final String DATA_ID = "zuul-refresh-dev.json";
//private static final String Group = "DEFAULT_GROUP";
@Autowired
private RouteDefinitionWriter routeDefinitionWriter;
@Autowired
private GatewayNacosProperties gatewayNacosProperties;
private ApplicationEventPublisher applicationEventPublisher;
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.applicationEventPublisher = applicationEventPublisher;
}
/**
* 这个方法主要负责监听Nacos的配置变化,这里先使用参数构建一个ConfigService,再使用ConfigService开启一个监听,
* 并且在监听的方法中刷新路由信息。
*
* @throws NacosException
*/
@Bean
public void refreshRouting() throws NacosException {
Properties properties = new Properties();
properties.put(PropertyKeyConst.SERVER_ADDR, gatewayNacosProperties.getServerAddr());
// properties.put(PropertyKeyConst.NAMESPACE, gatewayNacosProperties.getNamespace());
ConfigService configService = NacosFactory.createConfigService(properties);
//解决无法读取nacos原有配置而需要更新才能读取配置BUG 5000:等待的最长时间
String json=configService.getConfig(gatewayNacosProperties.getDataId(),gatewayNacosProperties.getGroup(),5000);
this.parsejson(json);
System.out.println("json:"+json);
configService.addListener(gatewayNacosProperties.getDataId(), gatewayNacosProperties.getGroup(), new Listener() {
@Override
public Executor getExecutor() {
return null;
}
@Override
public void receiveConfigInfo(String configInfo) {
// parsejson(configInfo);
logger.info(configInfo);
boolean refreshGatewayRoute = JSONObject.parseObject(configInfo).getBoolean("refreshGatewayRoute");
if (refreshGatewayRoute) {
List<RouteEntity> list = JSON.parseArray(JSONObject.parseObject(configInfo).getString("routeList")).toJavaList(RouteEntity.class);
for (RouteEntity route : list) {
update(assembleRouteDefinition(route));
}
} else {
logger.info("路由未发生变更");
}
}
});
}
/**
*解析json获取路由信息
* @param json
*/
public void parsejson(String json){
logger.info(json);
boolean refreshGatewayRoute = JSONObject.parseObject(json).getBoolean("refreshGatewayRoute");
if (refreshGatewayRoute) {
List<RouteEntity> list = JSON.parseArray(JSONObject.parseObject(json).getString("routeList")).toJavaList(RouteEntity.class);
for (RouteEntity route : list) {
update(assembleRouteDefinition(route));
}
} else {
logger.info("路由未发生变更");
}
}
/**
* 路由更新
*
* @param routeDefinition
* @return
*/
public void update(RouteDefinition routeDefinition) {
try {
this.routeDefinitionWriter.delete(Mono.just(routeDefinition.getId()));
logger.info("路由更新成功");
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
try {
routeDefinitionWriter.save(Mono.just(routeDefinition)).subscribe();
this.applicationEventPublisher.publishEvent(new RefreshRoutesEvent(this));
logger.info("路由更新成功");
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
}
public RouteDefinition assembleRouteDefinition(RouteEntity routeEntity) {
RouteDefinition definition = new RouteDefinition();
// ID
definition.setId(routeEntity.getId());
// Predicates
List<PredicateDefinition> pdList = new ArrayList<>();
for (PredicateEntity predicateEntity : routeEntity.getPredicates()) {
PredicateDefinition predicateDefinition = new PredicateDefinition();
predicateDefinition.setArgs(predicateEntity.getArgs());
predicateDefinition.setName(predicateEntity.getName());
pdList.add(predicateDefinition);
}
definition.setPredicates(pdList);
// Filters
List<FilterDefinition> fdList = new ArrayList<>();
for (FilterEntity filterEntity : routeEntity.getFilters()) {
FilterDefinition filterDefinition = new FilterDefinition();
filterDefinition.setArgs(filterEntity.getArgs());
filterDefinition.setName(filterEntity.getName());
fdList.add(filterDefinition);
}
definition.setFilters(fdList);
// URI
URI uri = UriComponentsBuilder.fromUriString(routeEntity.getUri()).build().toUri();
definition.setUri(uri);
return definition;
}
}
package com.zking.gatewayserver.config;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
/**
* 1. 保存Gateway(网关)中与nacos相关的属性
* 2. 这些信息是自定义配置属性,它们保存在配置文件application.yml中
* 3. 通过@ConfigurationProperties注解读取
*/
//@ConfigurationProperties(prefix = "gateway.nacos", ignoreUnknownFields = true)
@Configuration
@Data
public class GatewayNacosProperties {
@Value("${gateway.nacos.server-addr}")
private String serverAddr;
// @Value("${gateway.nacos.namespace}")
// private String namespace;
@Value("${gateway.nacos.data-id}")
private String dataId;
@Value("${gateway.nacos.group}")
private String group;
}
package com.zking.gatewayserver.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import reactor.core.publisher.Mono;
/**
* 请求限流配置
*/
@Configuration
public class RequestRateLimiterConfig {
/**
* 按IP来限流
*/
@Bean
public KeyResolver ipAddrKeyResolver() {//JDK8的新特性——Lambda表达式
return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getAddress().getHostAddress());
}
///**
// * 按用户限流
// */
//@Bean
//KeyResolver userKeyResolver() {
// return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("user"));
//}
///**
// * 按URL限流,即以每秒内请求数按URL分组统计,超出限流的url请求都将返回429状态
// *
// * @return
// */
//@Bean
//@Primary
//KeyResolver apiKeyResolver() {
// return exchange -> Mono.just(exchange.getRequest().getPath().toString());
//}
}
package com.zking.gatewayserver.entity;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* 过滤器实体类
*/
public class FilterEntity {
//过滤器对应的Name
private String name;
//路由规则
private Map<String, String> args = new LinkedHashMap<>();
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Map<String, String> getArgs() {
return args;
}
public void setArgs(Map<String, String> args) {
this.args = args;
}
}
import java.util.LinkedHashMap;
import java.util.Map;
/**
* 路由断言实体类
*/
public class PredicateEntity {
//断言对应的Name
private String name;
//断言规则
private Map<String, String> args = new LinkedHashMap<>();
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Map<String, String> getArgs() {
return args;
}
public void setArgs(Map<String, String> args) {
this.args = args;
}
}
package com.zking.gatewayserver.entity;
import java.util.ArrayList;
import java.util.List;
/**
* 路由实体类
*/
public class RouteEntity {
//路由id
private String id;
//路由断言集合
private List<PredicateEntity> predicates = new ArrayList<>();
//路由过滤器集合
private List<FilterEntity> filters = new ArrayList<>();
//路由转发的目标uri
private String uri;
//路由执行的顺序
private int order = 0;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public List<PredicateEntity> getPredicates() {
return predicates;
}
public void setPredicates(List<PredicateEntity> predicates) {
this.predicates = predicates;
}
public List<FilterEntity> getFilters() {
return filters;
}
public void setFilters(List<FilterEntity> filters) {
this.filters = filters;
}
public String getUri() {
return uri;
}
public void setUri(String uri) {
this.uri = uri;
}
public int getOrder() {
return order;
}
public void setOrder(int order) {
this.order = order;
}
}
7.1 yml文件配置
server:
#此处的5000端口号,就好像以前外置的tomcat的8080,让我们通过浏览器进行访问
#但此服务只是做了一个路由,它会将请求路由到其它微服务(一般是消费者)进行处理
port: 5000
spring:
application:
#微服务名
name: gateway-server
cloud:
nacos:
discovery:
#指定nacos注册中心的地址
server-addr: zz.com:8848
//此zz.com是你的本机域名为127.0.0.1 打开//C:\Windows\System32\drivers\etc\hosts,末尾添加1行
// 127.0.0.1 zz.com
gateway:
discovery:
locator:
#是否与服务发现组件进行结合,通过serviceId(必须设置成大写)转发到具体的服务实例。默认false
#为true代表开启基于服务发现的路由规则。
#之前默认的规则就不能访问了
enabled: false
#配置之后访问时serviceId无需大写
lower-case-service-id: true
# 路由(routes:路由,它由唯一标识(ID)、目标服务地址(uri)、一组断言(predicates)和一组过滤器组成(filters)。filters 不是必需参数。)
routes:
# http://localhost:5000/usr/hello
#路由标识(id:标识,具有唯一性)
- id: student-router
#目标服务地址(uri:地址,请求转发后的地址),会自动从注册中心获得服务的IP,不需要手动写死
uri: lb://student-api
#优先级,越小越优先
#order: 999
#路由条件(predicates:断言)
predicates:
# 路径匹配,
- Path=/student/**
filters:
- name: Hystrix
args:
name: fallback
fallbackUri: forward:/fallback
- name: RequestRateLimiter
args:
#用于限流的键的解析器的 Bean 对象的名字,使用 SpEL表达式根据#{@beanName}获取Bean对象
key-resolver: '#{@ipAddrKeyResolver}'
#令牌桶填充速率,允许用户每秒处理多少个请求
redis-rate-limiter.replenishRate: 20
#令牌桶总容量,允许在一秒钟内完成的最大请求数
redis-rate-limiter.burstCapacity: 100
- StripPrefix=1
redis:
host: 192.168.68.133
port: 6379
password: 123
database: 0
ribbon:
eager-load:
enabled: true
clients: user-service-api
logging:
level:
#开启spring-Cloud-gateway的日志级别为debug,方便debug调试
org.springframework.cloud.gateway: trace
org.springframework.http.server.reactive: debug
org.springframework.web.reactive: debug
reactor.ipc.netty: debug
#springboot监控Actuator,暴露所有端点
management:
endpoints:
web:
exposure:
include: '*'
#自定义配置
gateway:
nacos:
server-addr: ${spring.cloud.nacos.discovery.server-addr}
#namespace: 558dc9bc-a775-41ad-9f26-43ca3aa73e6b //注释掉默认public
data-id: dynamic-routing.json //nacos中配置文件的名称
group: DEFAULT_GROUP
8.配置好测试一下没问题即可打成jar包发布
8.1
修改主模块的pom
<version>0.0.1-SNAPSHOT</version>
<!-- 1.注意更改为pom而不是jar -->
<!--
<packaging>jar</packaging>
-->
<packaging>pom</packaging>
<!-- 2.主模块不要配置插件 -->
<build></build>
8.2.在各个子module模块的pom.xml文件中添加插件依赖
<build>
<plugins>
<!--添加maven插件-->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<!--添加自己的启动类路径!-->
<mainClass>com.imooc.DemoApplication</mainClass>
</configuration>
<executions>
<execution>
<goals>
<!--可以把依赖的包都打包到生成的Jar包中-->
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
8.3点击idea的view ——》Tool windows ——》maven projects
先双击clean(去掉之前打的包target文件夹)——》再创建install
先编译父模块–公共模块-其它模块
8.4.将项目各子模块target目录下的jar包,复制到指定目录,例如:d:\temp\apps目录下,再通过java命令直接运行
cmd
d:
cd d:\temp\apps
java -jar *.jar --spring.profiles.active=xxx
例如:
java -jar student-service-0.0.1-SNAPSHOT.jar //无参数
java -jar eureka-server-cluster-0.0.1-SNAPSHOT.jar --spring.profiles.active=peer2 //带参数
java -jar student-service-0.0.1-SNAPSHOT.jar
java -jar student-api-0.0.1-SNAPSHOT.jar
java -jar gateway-server-0.0.1-SNAPSHOT.jar
注意事项:对于指定环境的配置(spring.cloud.nacos.config.namespace=83eed625-d166-4619-b923-93df2088883a),都不要配置在应用的bootstrap.yml中。
而是在发布脚本的启动命令中,用命令后加参数的方式来动态指定,会更加灵活,例如:
-Dspring.profiles.active=DEV
–spring.cloud.nacos.config.namespace=f1ce84ff-c591-42b8-b441-2f0084863904
java -jar nacos-config-client-0.0.1-SNAPSHOT.jar --spring.cloud.nacos.config.namespace=f1ce84ff-c591-42b8-b441-2f0084863904
9.开启你的Linux服务器
docker安装nacos
docker pull nacos/nacos-server
docker run --env MODE=standalone --restart=always --name nacos -d -p 8848:8848 nacos/nacos-server
注1:不管是windows、linux系统均通过修改host文件实现虚拟域名对nacos的访问,因为docker中启动nacos容器其IP是不固定的
window中修改:
打开hosts,C:\Windows\System32\drivers\etc\hosts,末尾添加1行
127.0.0.1 zz.com
linux中修改方式:
1.宿主机修改hosts文件
vim /etc/hosts
## 在里面添加要映射的域名即可
127.0.0.1 zz.com
1.将你本地的nacos文件copy到你的服务器的nacos中
2.宿主机创建文件夹apps,rz上传eureka-server-cluster.jar包至apps
## 此目录稍后作为数据卷,在宿主机和容器之间共享数据
mkdir /apps
3.使用jre:8镜像启动容器,并挂载指定目录为数据卷
-- 启动生产者(7001)
docker run --restart=always \
--net=host \
-d \
-it \
--name student-server \
--mount type=bind,source=/apps,target=/apps \
jdk8:v3.0 //这是你的docker镜像名
-- 消费者
docker run --restart=always \
--net=host \
-d \
-it \
--name student-api \
--mount type=bind,source=/apps,target=/apps \
jdk8:v3.0 //这是你的docker镜像名
-- 启动网关、宿主机5000:容器5000端口映射
docker run --restart=always \
--net=host \
-d \
-it \
--name gateway\
--mount type=bind,source=/apps,target=/apps \
jdk8:v3.0 //这是你的docker镜像名
注1:jre:8是自定义镜像,已安装jre1.8
注2:网关5000与宿主机的5000端做映射,要修改防火墙放5000端口才可以访问
4.分别进入docker容器中,进入apps目录 使用java -jar …命令逐个运行,
成功,即可配置nginx
10.开始配置nginx
Nginx也是一款服务器,我们常用它做如:反向代理、负载均衡、动态与静态资源的分离的工作,
2.安装nginx
1.搜索nginx镜像
docker search nginx
2.拉取镜像
docker pull nginx
3.在宿主机中创建挂载目录
mkdir -p /data/nginx/{conf,conf.d,html,log}
注1:因为在nginx镜像中没有安装vi或vim编辑器(安装又太麻烦了),无法编辑nginx配置文件,
所以直接通过数据卷挂载上去更加方便
注2:将nginx配置文件nginx.conf上传到宿主机的挂载目录“/data/nginx/conf”下
注3:将vue前端项目打包后上传到宿主机的挂载目录“/data/nginx/html”下
注1:nginx配置文件相关调试命令可参考附录二
4.根据nginx镜像创建nginx容器(测试nginx的安装与访问,并 没有发布任何的java或vue项目,最后此容器要删除的)
docker run \
--name mynginx0 \
-d -p 80:80 \
nginx:latest
5.查看本地的容器,可以看到nginx容器已经创建成功了
docker ps
6.测试nginx是否安装成功
打开浏览器输入:http://192.168.183.133( http://宿主机ip:80),正常情况下会显示nginx的欢迎页面
nginx配置文件nginx.conf
#工作进程的个数,一般与计算机的cpu核数一致
worker_processes 1;
events {
#单个进程最大连接数(最大连接数=连接数*进程数)
worker_connections 1024;
}
http {
include mime.types;#文件扩展名与文件类型映射表
default_type application/octet-stream;#默认文件类型
sendfile on;#开启高效文件传输模式,sendfile指令指定nginx是否调用sendfile函数来输出文件,对于普通应用设为 on,如果用来进行下载等应用磁盘IO重负载应用,可设置为off,以平衡磁盘与网络I/O处理速度,降低系统的负载。注意:如果图片显示不正常把这个改成off。
keepalive_timeout 65; #长连接超时时间,单位是秒
gzip on;#启用Gizp压缩
#服务器的集群
upstream tomcats { #服务器集群名字
#TODO:172.17.0.3是docker容器的IP
server 172.17.0.3:8080 weight=1;#服务器配置 weight是权重的意思,权重越大,分配的概率越大。域名为你当前服务器域名+你所配置的端口80
server 172.17.0.4:8080 weight=2;
}
#当前的Nginx的配置
server {
listen 80;#监听80端口,可以改成其他端口
server_name localhost;#当前服务的域名,没有域名可随便填写
root /usr/share/nginx/html/dist;#将要访问的网站的目录
location / {
try_files $uri $uri/ /index.html;#该句代码是为解决history路由不能跳转的问题,在vue-router官网有介绍
}
location ^~/api/ {
#^~/api/表示匹配前缀是api的请求,proxy_pass的结尾有/, 则会把/api/*后面的路径直接拼接到后面,即移除api
proxy_pass http://tomcats/;
}
}
}
3.ningx发布java和vue项目
nginx+tomcat实现反向代理及均衡
nginx+html静态服务器
1.创建nginx容器
docker run \
--name mynginx \
-d -p 80:80 \
-v /data/nginx/conf/nginx.conf:/etc/nginx/nginx.conf \
-v /data/nginx/log:/var/log/nginx \
-v /data/nginx/html:/usr/share/nginx/html \
nginx:latest
注1: 因为在nginx镜像中没有安装vi或vim编辑器(安装太麻烦了),无法编辑nginx配置文件,所以所有数据和配置都是通过数据卷挂载
第一个-v:挂载nginx的主配置文件,以方便在宿主机上直接修改容器的配置文件
第二个-v:挂载容器内nginx的日志,容器运行起来之后,可以直接在宿主机的这个目录中查看nginx日志
第三个-v:挂载静态页面目录
注意命令之间的换行与空格
注2:如果容器创建失败,可通过如下命令,查看在docker容器启动日志
docker logs -f -t --tail 行数 容器名
docker logs -f -t --tail 100 mynginx
注3:如果要进入容器内部,这里是bash而非sh,要根据实际的基础镜像来
docker exec -it mynginx /bin/bash
记得允许防火墙访问端口哦!!!!!