docker容器化部署nginx代理的go-web应用-解决反向代理、ssl、负载均衡问题

通常我们的web应用程序部署的时候不会直接暴露,通过nginx反向代理,一是隐藏真实后端,二是通过nginx的epoll网络IO多路复用,获取高性能的网络访问。

今天我们分享个通过nginx代理go的后端web服务。

主要内容:

  • nginx初始化配置
  • go应用的初始化配置
  • 部署实战(反向代理server | ssl | 负载均衡)

1.nginx初始化配置

nginx作为常用的web代理服务器,其优秀的性能自不必说,今天我们直接来看看nignx的docker化部署情况。

1.拉取镜像
docker pull nginx:latest

2.获取配置
先跑nginx容器,再把nginx的配置文件复制出来

pwd 
// /root/ngx-go-web/ngx

ls 
// conf  conf.d  html  logs  start.sh

docker run -d --name mynginx nginx:latest

docker cp /etc/nginx/nginx.conf ./conf/

3.停掉nginx容器
docker rm -f mynginx

4.查看nginx.conf

cat ./conf/nginx.conf
---
user  nginx;
worker_processes  auto;

error_log  /var/log/nginx/error.log notice;
pid        /var/run/nginx.pid;


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;
    server {
        listen  80;
    }
}
---

2.go应用的初始化配置

go用来编写代码,简单易上手,今天通过net/http包,简单演示go的web应用。

web-server

mkdir go-http

ls
// go.mod  server.go

新建文件后编写代码:

// server.go
package main

import (
        "encoding/json"
        "io"
        "log"
        "net/http"
)

func main()  {
        http.HandleFunc("/hello", hello)
        http.HandleFunc("/login", login)
        log.Println("HTTP server up at", "http://localhost:8080")
        _ = http.ListenAndServe("0.0.0.0:8080", nil)
}

func hello(w http.ResponseWriter, r *http.Request)  {
        w.Write([]byte("Hello from go http server"))
}

func login(w http.ResponseWriter, r *http.Request)  {
        body, err := io.ReadAll(r.Body)
        if err != nil {
                w.Write([]byte("Bad request data."))
                return
        }
        log.Println("req data:", string(body))

        var data map[string]interface{}
        _ = json.Unmarshal(body, &data)

        username := data["username"]
        passwd := data["password"]

        if username == "username" && passwd == "password" {
                w.Write([]byte(body))
                return
        }

        w.Write([]byte("Bad login parameters."))
}

golang运行环境

pwd 
// /root/ngx-go-web/go-http

vim Dockerfile
---
FROM golang:latest

ENV GO111MODULE=on

# 设置容器进入后的dir
WORKDIR /opt/go-server

# 复制当前文件到容器中
ADD . ./

# 容器中编译server二进制程序
RUN go build -o server .

# 声明暴露的端口,server运行的端口
EXPOSE 8080

# 容器启动时运行命令,启动即运行go的应用程序,使用CMD会被docker run的bash命令覆盖,注意
ENTRYPOINT ["./server"]
---

// 构建golang的运行镜像
docker build  -t go-server-demo:v1.0 .

查看镜像:

# docker images
REPOSITORY       TAG       IMAGE ID       CREATED             SIZE
go-server-demo   v1.0      600577fdb057   About an hour ago   848MB # go-web
golang           latest    c48137eaf961   8 days ago          778MB
nginx            latest    904b8cb13b93   2 weeks ago         142MB # nginx

3.部署实战

  • 手动部署
  • docker-compose编排部署

手动部署(脚本化)

启动go应用的容器:
docker run -d --name go-server -p 8080:8080 go-server-demo:v1.0

编辑nginx的配置:

user  nginx;
worker_processes  auto;

error_log  /var/log/nginx/error.log notice;
pid        /var/run/nginx.pid;


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;
    server {
        listen  80; # nginx监听80端口
        server_name 172.30.1.240;
        location / {
            proxy_pass http://172.30.1.240:8080/; # 代理转发到 real server处理
            proxy_redirect off ; 
            proxy_set_header Host $host; # 确保后端真实服务器收到的ip是客户端真实ip,非nginx的ip
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header REMOTE-HOST $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
    }
}

启动nginx容器
docker run -d --restart always -v /root/ngx-go-web/ngx/conf/nginx.conf:/etc/nginx/nginx.conf --name mynginx -p 80:80 nginx:latest

查看容器运行情况:

root@ubuntu:~/ngx-go-web/ngx/conf# docker ps
CONTAINER ID   IMAGE                 COMMAND                  CREATED             STATUS             PORTS                                       NAMES
d3bd80806a46   nginx:latest          "/docker-entrypoint.…"   About an hour ago   Up About an hour   0.0.0.0:80->80/tcp, :::80->80/tcp           mynginx
1d79a28260ba   go-server-demo:v1.0   "./server"               About an hour ago   Up About an hour   0.0.0.0:8080->8080/tcp, :::8080->8080/tcp   go-server
root@ubuntu:~/ngx-go-web/ngx/conf# docker top mynginx
UID                 PID                 PPID                C                   STIME               TTY                 TIME                CMD
root                49165               49141               0                   11:20               ?                   00:00:00            nginx: master process nginx -g daemon off;
systemd+            49216               49165               0                   11:20               ?                   00:00:00            nginx: worker process
systemd+            49217               49165               0                   11:20               ?                   00:00:00            nginx: worker process
root@ubuntu:~/ngx-go-web/ngx/conf# docker top go-server
UID                 PID                 PPID                C                   STIME               TTY                 TIME                CMD
root                46956               46935               0                   10:58               ?                   00:00:00            ./server

可以看到容器都正常运行起来了,接下来简单测试看看:
测试 api: /hello

测试 api: /login
用户名错误:

正常用户名和密码:

可以看到从测试情况来看,我们确实实现了nginx通过监听主机上的80端口,代理上游的真是服务器go-web应用,通过80 -> 8080端口,实现了请求的代理转发。

上面的部署很粗糙,只是为了熟悉整个过程,更好的实现应该是用脚本自动化部署,减少人工的配置、输入命令等。

分割线---
以上基于http监听80端口,通常生产情况,是基于https的访问,以下为nginx的443设置。
nginx:443
为方便测试,采用自签证书形式获取证书和秘钥。

1.生成秘钥
openssl genrsa -out ssl.key 2048 

2.生成证书请求
openssl req -new -key ssl.key -out ssl.csr

3.生成证书
openssl x509 -req -days 36500 -in ssl.csr -signkey ssl.key -out ssl.crt

4.查看生成证书
ls 
// ssl.crt  ssl.csr  ssl.key

pwd
// /root/ngx-go-web/ngx/cert/ssl

配置nginx的ssl部分
通常我们可以配置全局的ssl,也可以针对某个server进行配置,以下以server进行配置。

# cat nginx.conf
---
user  nginx;
worker_processes  auto;

error_log  /var/log/nginx/error.log notice;
pid        /var/run/nginx.pid;


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;
    # global ssl


    server {
        listen  443;
        server_name 172.30.1.1;
        # ssl
        ssl on;
        ssl_certificate  /etc/nginx/cert/ssl.crt;
        ssl_certificate_key /etc/nginx/cert/ssl.key;
        location / {
            proxy_pass http://172.30.1.1:8080/;
            index index.html index.htm;
        }
    }
}
---

停掉nginx容器,重新跑nginx

docker rm -f mynginx

docker run -d --restart always \
>         -p 443:443 \
>         --name mynginx \
>         -v /root/ngx-go-web/ngx/conf/nginx.conf:/etc/nginx/nginx.conf \
>         -v /root/ngx-go-web/ngx/cert/ssl/ssl.crt:/etc/nginx/cert/ssl.crt \
>         -v /root/ngx-go-web/ngx/cert/ssl/ssl.key:/etc/nginx/cert/ssl.key \
>         nginx:latest

docker ps
CONTAINER ID   IMAGE                 COMMAND                  CREATED         STATUS         PORTS                                           NAMES
a36599a1afd6   nginx:latest          "/docker-entrypoint.…"   8 seconds ago   Up 6 seconds   80/tcp, 0.0.0.0:443->443/tcp, :::443->443/tcp   mynginx
1d79a28260ba   go-server-demo:v1.0   "./server"               4 hours ago     Up 4 hours     0.0.0.0:8080->8080/tcp, :::8080->8080/tcp       go-server

重新测试基于https-443监听端口的nginx是否设置成功:

可以看到,均能正常访问nginx的443端口,说明nginx监听443端口转发请求到上游的真实server成功。

负载均衡配置

nginx的负载均衡主要由轮询、ip哈希、权重、最少连接、公平、随机等,默认轮询分配。
首先我们的后端服务器先起来,比如服务端口分别是8080/8081,相应的nginx的配置如下:

# 属于http部分
http {
    # load balance
    upstream myServers {
        server 172.30.7.240:8080;
        server 172.30.7.240:8081;
    }
    server {
        ...
        location / {
            proxy_pass http://myServers;
        }
    }
}

--- 分割线---
ps.更多负载均衡设置
# weight 权重负载
    upstream myServers {
        server 172.30.1.1:8080 weight=3;
        server 172.30.1.1:8081 weight=2;
    }

# ip_hash ip哈希负载,解决session分配问题
    upstream myServers {
        ip_hash;
        server 172.30.1.1:8080;
        server 172.30.1.1:8081;
    }

# least_conn 最少连接数
    upstream myServers {
        least_conn;
        server 172.30.1.1:8080;
        server 172.30.1.1:8081;
    }

重启nginx,reload配置即可,测试后发现默认就是轮询的策略进行负载,实际生产时根据具体后端业务进行策略选择。

docker-compose编排部署

本文基于负载均衡的例子,编写yaml文件:

version: "2.1"
services:
  mynginx:
    image: nginx:latest
    hostname: nginx
    container_name: mynginx
    restart: always
    ports:
      - 80:80
      - 443:443
    volumes:
      - /root/ngx-go-web/ngx/conf/nginx.conf:/etc/nginx/nginx.conf
      - /root/ngx-go-web/ngx/cert/ssl/ssl.crt:/etc/nginx/cert/ssl.crt
      - /root/ngx-go-web/ngx/cert/ssl/ssl.key:/etc/nginx/cert/ssl.key
    depends_on:
      - go-server-1
      - go-server-2
    networks:
      app_net:
        ipv4_address: 172.237.237.10
  go-server-1:
    images: go-server-demo:v1.0
    hostname: go-server-1
    container_name: go-server-1
    restart: always
    ports:
      - 8080:8080
    volumes:
      - /root/ngx-go-web/go-http/config.yaml:/opt/go-http/config.yaml
    networks:
      app_net:
        ipv4_address: 172.237.237.11
  go-server-2:
    images: go-server-demo:v1.0
    hostname: go-server-2
    container_name: go-server-2
    restart: always
    ports:
      - 8081:8081
    volumes:
      - /root/ngx-go-web/go-http/config.yaml:/opt/go-http/config.yaml
    networks:
      app_net:
        ipv4_address: 172.237.237.12
networks:
  app_net:
    driver: bridge
    ipam:
      driver: default
      config:
        - subnet: 172.237.237.0/24

相关运行:

# 后台起服务,到docker-compose.yaml文件目录内
docker-compose up -d

# 查看服务
docker-compose ps

# 停掉服务
docker-compose stop

# 重启服务
docker-compose restart

# 重启某服务
docker-compose restart mynginx

通过docker-compose我们就可以实现容器的编排运行,当然如果业务需要也可以上k8s,那就是另外一套玩法了,这种业务体量已经可以抵挡一定的业务访问,根据需要部署docker/k8s。

参考:

posted on 2023-03-16 11:38  进击的davis  阅读(1069)  评论(0)    收藏  举报

导航