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。
参考:
浙公网安备 33010602011771号