traefik+portainer+docker swarm+nginx构建集群服务

482 阅读4分钟

最近有个项目需要进行扩容,之前是直接jar包运行,通过nginx进行负载均衡。搭架子倒是简单,但是每次更新很麻烦,这次正好一并换掉。

成型后大概的拓扑图:

画板

路径
http://traefiktraefik dashboard
http://edu后台页面
http://edu/api/admin后台接口
http://edu/portainer/portainer dashboard

准备三台机器,server-201(192.168.68.201),server-202(192.168.68.202),server-203(192.168.68.203),系统为ubuntu24.04,均已安装docker 官方文档

1.初始化docker swarm和网络

开放swarm所需要的端口:Getting started with Swarm mode | Docker Docs

  • 2377/tcp 集群间管理通信
  • 7946 tcp/udp 节点间通信
  • 4789 udp 用于overlay网络

其他端口:

  • 9001 tcp portainer-agent
firewall-cmd --zone=public --permanent --add-port={2377/tcp,7946/tcp,7946/udp,4789/tcp,9001/tcp}
# 重新加载使开放端口生效
firewall-cmd --reload

规划server-201为manager,202和203为node

初始化集群:Create a swarm | Docker Docs

# 201上执行  成功后会打印join集群的token
docker swarm init --advertise-addr 192.168.68.201:2377 --listen-addr 192.168.68.201:2377
# 202 203分别执行  token记得替换成上面打印的结果
docker swarm join --token SWMTKN-1-49nj1cmql0jkz5s954yi3oex3nedyz0fb0xx14ie39trti4wxv-8vxv8rssmk743ojnwacrr2e7c 192.168.68.201:2377

查看集群节点,在201上执行:

docker node ls

总共三个节点,201为manager

网络

traefik和portainer agent都需要在同一网络下才能发现,规划两个overlay网络:

  • app_net:所有需要traefik代理的服务都在这个网络
  • portainer_agent_network:只用来服务portainer agent

在201上执行:

# --attachable标记的网络才能在创建后继续加入容器
docker network create --driver overlay --attachable portainer_agent_network
docker network create --driver overlay --attachable app_net

查看网络:

2.部署portainer+traefik+nginx

不直接映射portainer和nginx,通过traefik代理。规划的访问路径:

注意:

  • edu和traefik只是我自定义的host,需要在本地配置hosts,真实环境换成实际的域名即可
# windows:C:\Windows\System32\drivers\etc\hosts  linux: /etc/hosts
192.168.68.201  edu
192.168.68.201  traefik

创建目录用来挂载容器数据:

cd /root
mkdir traefik nginx portainer
# traefik配置文件
touch /root/traefik/traefik.yml
# nginx 配置和静态目录
touch /root/nginx/nginx.conf
mkdir /root/nginx/html/admin
# portainer目录
mkdir /root/portainer

traefik.yml:官方模板

global:
  checkNewVersion: true
  sendAnonymousUsage: true
entryPoints:
  web:
    address: :80

  websecure:
    address: :443
accessLog:
  filePath: /log/log.txt
api:
  dashboard: true
providers:
# 开启docker代理
  docker: {}
# 开启swarm代理
  swarm:
    endpoint: "unix:///var/run/docker.sock"

nginx.conf:根据自己项目配置

user root;
worker_processes  1;
worker_rlimit_nofile 1024;
events {
    worker_connections  1024;
    use epoll;
}


http {
    include       mime.types;
    default_type  application/octet-stream;

    server_tokens off;
    sendfile        on;
    keepalive_timeout  65;

    gzip on;
    gzip_min_length 1k;
    gzip_comp_level 9;
    gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png;
    gzip_vary on;
    gzip_disable "MSIE [1-6]\.";

    server {
        listen    80;
        server_name edu.host;
        add_header 'Access-Control-Allow-Origin' *;
        add_header 'Access-Control-Allow-Credentials' 'true';
        add_header 'Access-Control-Allow-Methods' *;
        add_header 'Access-Control-Allow-Headers' *;
        add_header Strict-Transport-Security "max-age=31536000";
        client_max_body_size 150m;
        client_body_buffer_size 20m;
        location / {
            alias /usr/share/nginx/html/admin/;
            try_files $uri $uri/ /index.html;
            index  index.html index.htm;

        }
    }
}

2.1 docker compose部署(推荐)

services:
  traefik:
    container_name: traefik
    restart: always
    image: traefik:v3.2.0
    networks:
      - app_net
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - /root/traefik/traefik.yml:/etc/traefik/traefik.yml
    labels:
      - "traefik.http.routers.traefik.rule=Host(`traefik`)" # 通过traefik访问
      - "traefik.http.routers.traefik.service=api@internal" # 指定的服务
      - "traefik.http.middlewares.traefik-auth.basicauth.users=traefik:$$apr1$$q1UDMg1q$$MgKSNFf4U94se9zeJ7nfS1"  # dashboard鉴权一下
      - "traefik.http.routers.traefik.middlewares=traefik-auth@docker"  #指定鉴权中间件到traefik路由上
  portainer:
    container_name: portainer
    restart: always
    image: portainer/portainer-ce
    networks:
      - app_net
      - portainer_agent_network
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - /root/portainer:/data
    labels:
      - "traefik.http.routers.portainer.rule=Host(`edu`)&&PathPrefix(`/portainer`)" # 通过 /portainer 子路径 访问
      - "traefik.http.services.portainer.loadbalancer.server.port=9000" #指定访问的端口
      - "traefik.http.middlewares.portainer-stripprefix.stripprefix.prefixes=/portainer" # 中间件 代理后删除  /portainer 路径
      - "traefik.http.routers.portainer.middlewares=portainer-stripprefix@docker" # 指定中间件到portainer路由上
      - "traefik.docker.network=app_net" # portainer有两个网络  指定traefik使用app_net网络进行代理
  nginx:
    container_name: nginx
    restart: always
    image: nginx
    networks:
      - app_net
    volumes:
      - /root/nginx/html:/usr/share/nginx/html:ro
      - /root/nginx/nginx.conf:/etc/nginx/nginx.conf:ro
    labels:
      - "traefik.http.routers.nginx.rule=Host(`edu`) && !PathPrefix(`/api`)&&!PathPrefix(`/portainer`)" # 匹配 edu 和非 /api开头的路径
      - "traefik.http.services.nginx.loadbalancer.server.port=80" #指定访问的端口 80
networks:
  app_net:
    external: true # 外部网络,即网络已经创建好了
  portainer_agent_network:
    external: true
docker compose up -d

访问 http://traefik

注意:这里traefik配置了认证,具体参考Traefik BasicAuth Documentation | Traefik | v3.2,hash后的密码中$需替换为$$

输入输入用户名密码后即可访问控制台

访问http://edu/portainer/ 进入portainer dashboard,设置用户名密码后添加docker swarm环境:

跟随页面指引,创建portainer-agent服务,通过portainer监控docker swarm集群:

# 201上执行
docker service create \
  --name portainer_agent \
  --network portainer_agent_network \
  -p 9001:9001/tcp \
  --mode global \
  --constraint 'node.platform.os == linux' \
  --mount type=bind,src=//var/run/docker.sock,dst=/var/run/docker.sock \
  --mount type=bind,src=//var/lib/docker/volumes,dst=/var/lib/docker/volumes \
  --label="traefik.enable=false"\
  portainer/agent
# --label="traefik.enable=false" 不让traefik管理portainer agent,否则traefik会一直打印错误日志

http://edu 访问nginx代理页面

2.2 单独部署(不建议)

2.2.1部署portainer

# 创建portainer overlay网络
docker network create --driver overlay --attachable portainer_agent_network
# 拉取镜像
docker pull portainer/portainer-ce
docker pull portainer/agent
# 挂载portainer数据目录
mkdir portainer
# 启动portainer
docker run -d -p 8000:8000 -p 9000:9000 \
--name=portainer \
--network=app_net \
--restart=always \
--label="traefik.http.routers.portainer.rule=PathPrefix(\`/portainer\`)&& !PathPrefix(\`/api\`)" \
--label="traefik.http.services.portainer.loadbalancer.server.port=9000" \
--label="traefik.http.middlewares.portainer-stripprefix.stripprefix.prefixes=/portainer" \
--label="traefik.http.routers.portainer.middlewares=portainer-stripprefix@docker" \
-v /var/run/docker.sock:/var/run/docker.sock \
-v ./portainer:/data \
portainer/portainer-ce

# portainer-agent
docker service create \
  --name portainer_agent \
  --network portainer_agent_network \
  -p 9001:9001/tcp \
  --mode global \
  --constraint 'node.platform.os == linux' \
  --mount type=bind,src=//var/run/docker.sock,dst=/var/run/docker.sock \
  --mount type=bind,src=//var/lib/docker/volumes,dst=/var/lib/docker/volumes \
  --label="traefik.enable=false"\
  portainer/agent

访问192.168.68.201:9000进入portainer dashboard,给portainer容器添加portainer_agent_network网络,才能访问到portainer-agent,进行docker swarm集群管理

2.2.2.部署traefik

# 全部在201上操作
# 拉取镜像
 docker pull traefik:v3.2.0
# 新建配置文件  https://github.com/traefik/traefik/blob/master/traefik.sample.yml
docker run -d -p 80:80 \
--name=traefik --network=app_net \
-v /root/traefik/traefik.yml:/etc/traefik/traefik.yml \
-v /var/run/docker.sock:/var/run/docker.sock \
--label="traefik.http.routers.traefik.rule=Host(\`traefik\`)" \
--label="traefik.http.routers.traefik.service=api@internal" \
--label="traefik.http.middlewares.traefik-auth.basicauth.users=test:$$apr1$$H6uskkkW$$IgXLP6ewTrSuBkTrqE8wj/" \
--label="traefik.http.routers.traefik.middlewares=traefik-auth@docker" \
traefik:v3.2.0
# 只映射80端口 dashboard由traefik自身代理

2.2.3.部署nginx

docker run -d  \
--name=nginx \
--network=app_net \
--restart=always \
--label="traefik.http.routers.nginx.rule=Host(\`edu\`) && !PathPrefix(\`/api\`)" \
--label="traefik.http.services.nginx.loadbalancer.server.port=80" \
-v /root/nginx/html:/usr/share/nginx/html:ro \
-v /root/nginx/nginx.conf:/etc/nginx/nginx.conf:ro \
nginx
# !PathPrefix在这里会报错  最后是直接在portainer中创建的nginx容器

3.部署portainer-agent

通过portainer监控docker swarm集群

# 启动portainer-agent
docker service create \
  --name portainer_agent \
  --network portainer_agent_network \
  -p 9001:9001/tcp \
  --mode global \
  --constraint 'node.platform.os == linux' \
  --mount type=bind,src=//var/run/docker.sock,dst=/var/run/docker.sock \
  --mount type=bind,src=//var/lib/docker/volumes,dst=/var/lib/docker/volumes \
  --label="traefik.enable=false"\
  portainer/agent
# --label="traefik.enable=false" 不让traefik管理portainer agent,否则后台一直打印错误日志

4.部署service

登录portainer后台可以直接新增service,注意需要选择app_net才能使treafik访问到,添加对应的service label:

也可以在201上直接创建服务:

#部署edu-admin服务
 docker service create \
 --replicas 3 \
 --network=app_net \
 --label="traefik.http.routers.edu-admin.rule=Host(\`edu\`) && PathPrefix(\`/api/admin\`)" \
 --label="traefik.http.services.edu-admin.loadbalancer.server.port=8080" \
 --label="traefik.docker.network=app_net" \
--env spring.redis.host=192.168.68.203 \
--env spring.profiles.active=dev \
 --name=admin admin

启动成功后访问swagger :http://edu/api/admin/doc.html

image.png