跳过正文

容器(8):使用Portainer Agent监控远程电脑上的Docker容器

·4348 字·9 分钟
锦李本鲤
作者
锦李本鲤
光锥之内,皆为命运。
容器技术系列 - 这篇文章属于一个选集。
§ 8: 本文

缘起
#

我们之前介绍了使用Portainer来监控和管理电脑上运行的容器,Portainer提供了可视化的界面,可以方便地查看容器日志、重启容器、管理镜像文件等。在配置了反向代理后,我们可以方便地使用域名来远程访问,例如https://portainer.example.com

但是之前的文章只介绍了用Portainer监控本地电脑上的容器。后来我又在我的其他电脑上安装运行了Docker容器,如果也想用Portainer来监控,一个自然的想法是,在每台运行Docker容器的电脑上都安装Portainer,用不同的域名来反向代理,例如portainer1.example.comportainer2.example.comportainer3.example.com等等。

这种方法当然是行得通的,就是比较碎片化。Portainer是支持远程监控其他电脑上的Docker容器的,这就是Portainer里的Agent。

简介
#

Docker提供了一个位于/var/run/docker.sock的套接口(socket),用于Docker守护进程与其他工具之间的通信。其他工具可以通过这个套接口获取Docker容器的运行状态,也可以向这个套接口发送命令来管理Docker容器。

工作原理
#

Portainer可以通过访问远程电脑上的Docker套接口来管理远程电脑上的Docker容器。具体过程如下:

  1. 首先我们确定一个主Portainer,例如本地电脑A上运行的Portainer。其他远程电脑运行Agent。
  2. Agent1向主Portainer报告自己这台电脑上的Docker容器的状态,主Portainer将其显示在Portainer的UI界面上。用户可以在这个界面上看到其他电脑上运行的容器状态。
  3. 若用户想要管理其他电脑上的容器,则直接在Portainer的UI界面上操作,Portainer会将用户的操作发送给Agent。Agent完成相应的操作后将新的状态再次报告给主Portainer。

实现方式
#

上述工作原理的实现方式很多,常见的有两种:Portainer Agent和Portainer Edge Agent。

Portainer Agent
#

  1. 在远程电脑B上运行一个Portainer Agent容器,这个Agent读取电脑B上的/var/run/docker.sock
  2. 本地电脑A上运行Portainer Server (即普通的Portainer),添加一个远程环境指向电脑B,向电脑B上的Agent容器索要电脑B上的容器状态,向电脑B上的Agent发送操作指令。电脑B上的Agent执行电脑A通过Portainer发送来的指令。

这种实现方式的关键是本地电脑A的Portainer要能主动访问到远程电脑B的Agent。如果A和B在同一个局域网里,那只需要通过局域网里的IP地址和端口直接访问就行了;如果A和B不在同一个局域网里,我们就需要将电脑B上的Agent容器暴露到公网,可以通过域名反向代理来暴露。

Portainer Edge Agent
#

上述Agent方式将远程电脑的Agent都暴露在公网的方法显然不太安全,尽管我们可以设置口令进行加密,但依然可能被攻击。

于是有了一种更安全的方法,远程电脑B上的Agent不需要暴露到公网,本地电脑A也不会主动去找B。反之,远程电脑B上的Agent每隔一段时间(默认是5秒)向电脑A上的Portainer汇报一次,并查看是否有什么任务交给自己。

在本地电脑A看来,它不知道远程电脑B在哪,只知道自己会定时获取到电脑B交给自己的状态,自己给B列的任务也会被B拿走执行。

这样,我们就只暴露了本地电脑A的Portainer,这个Portainer只负责收取其他远程Agent的状态更新和向其他Agent发布任务。无论是本地电脑A还是其他电脑,他们都不接受别的电脑的指令,这样自然更安全。这种Agent称为Edge Agent。

对比
#

普通的Agent是本地电脑主动找远程电脑,按需指挥它们;Edge Agent是远程电脑定时向本地电脑汇报,并领取任务。

因此,普通Agent模式更高效,没有任务时不会去找远程电脑。Edge Agent则是不管远程电脑有没有任务,都定时去找本地电脑。不过,这种通信并不会太消耗电脑性能资源,如果我们设置的汇报时间间隔大一点儿(例如30秒),那么Edge Agent模式的性能开销就微乎其微了。

综上,这里我们选择使用Edge Agent模式。

Portainer Edge Agent部署
#

这里我们选用的工具链如下:

  • Portainer: 免费的社区版portainer-ce
  • 反向代理:Traefik和免费版的Cloudflare Tunnel

Portainer Server配置
#

Portainer Server实际上就是我们在之前的文章中讲的Portainer的部署,只不过为了让Edge Agent能连上,我们需要添加一些反向代理的设置。

  1. Portainer容器的docker-compose.yml文件

  2. 设置Traefik路由规则portainer.yml

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    
    http:
     routers:
       portainer:
         rule: "Host(`550w.jinli.io`)"
         entryPoints:
           - websecure
         service: portainer-api
         tls: {}
     portainer-edge:
       rule: "Host(`edge.jinli.io`)"
       entryPoints:
         - websecure
       tls: {}
       service: portainer-edge
    
     services:
       portainer-api:
         loadBalancer:
           servers:
             - url: "http://portainer:9000"
       portainer-edge:
         loadBalancer:
           servers:
             - url: "http://portainer:8000"

    之前的路由规则只暴露了9000端口,用于Portainer的网页界面。 现在我们暴露8000端口,用于Edge Agent的连接。

    在Portainer中创建Edge Agent环境时,Portainer默认使用Portainer的域名加8000端口(如portainer.example.com:8000)进行连接。但由于我们用Cloudflare Tunnel来做反向代理,Cloudflare Tunnel不会监听8000端口,因此,我们需要再给portainer:8000分配一个域名(如edge.example.com,并且让Edge Agent连接新的域名,而非portainer.example.com:8000

  3. Cloudflare Tunnel

    先在Cloudflare网页客户端中为edge.example.com添加DNS记录(如果之前没添加portainer.example.com的话,也创建一个),记录是指向Cloudflare Tunnel的CNAME。

    Cloudflare Tunnel里我们需要同时代理域名portainer.example.comedge.example.com,将下面的代码加入到原来的config.yml中:

    1
    2
    3
    4
    5
    6
    7
    8
    
    - hostname: edge.jinli.io
      service: https://traefik:443
      originRequest:
        noTLSVerify: true
    - hostname: edge.jinli.io
      service: https://traefik:443
      originRequest:
        noTLSVerify: true

这些全部设置好之后,启动这些容器服务,就可以通过https://portainer.example.com在浏览器中访问Portainer界面了。

Portainer网页界面添加Edge Agent环境
#

在Portainer的网页界面左侧,管理员(Administration)区域有个Environment-related选项,打开这个选项就可以看到如下界面。

点击“添加环境”(Add Environment),选择Docker Standalone,点击Start Wizard,选择Edge Agent,根据提示创建一个Edge Agent。

创建完成后,会显示一个Docker命令告诉你怎样在远程电脑上创建Edge Agent容器。由于我们使用docker-compose.yml来管理容器,所以我们不用这个命令。但我们需要记下来这个命令里出现的EDGE_IDEDGE_KEY,将这两个值保存下来。

创建Edge Agent
#

  1. 重新编码EDGE_KEY

    EDGE_KEY保存了Portainer Server的信息,Edge Server就靠这里面的信息来寻找Portainer Server。

    在命令行运行如下命令对EDGE_KEY进行解码(把命令中的your_edge_key换成你在上一步获取的EDGE_KEY的值):

    1
    
    echo your_edge_key | base64 -d

    你会得到格式如下的一串字符:

    1
    
    https://portainer.example.com|portainer.example.com:8000|xxxxxxxxfinger_print|3

    其中第一段https://portainer.example.com就是告诉Edge Server去https://portainer.example.com找Portainer Server。

    第二段的portainer.example.com:8000表示在Edge Agent找到Portainer Server后,向portainer.example.com:8000发送请求建立连接。

    我们刚才说了,我们的portainer.example.com是用Cloudflare Tunnel代理的,Cloudflare Tunnel不会监听8000端口。所以我们创建了个新域名edge.example.com来代理portainer:8000。于是我们用新域名替换掉第二段里的portainer.example.com:8000,替换后的字符串长这样:

    1
    
    https://portainer.example.com|edge.example.com|xxxxxxxxfinger_print|3

    最后,我们把替换后的字符串重新编码:

    1
    
    echo -n https://portainer.example.com|edge.example.com|xxxxxxxxfinger_print|3 | base64 -w0

    将重新编码的字符串保存下来,这就是新的EDGE_KEY

    注意,重新编码的字符串末尾如果有=,Edge Agent容器很可能不认,可以删掉。

  2. Edge Agent容器部署

    接下来我们在远程电脑上部署Edge Agent。

    先创建一个.env文件,写入EDGE_ID和魔改后的EDGE_KEY,也可以把要多长时间汇报一次状态也定义到环境变量里(这里是每30秒):

    1
    2
    3
    
    EDGE_ID=xxxxxxxxxx
    EDGE_KEY=xxxxxxxxxxxxxxxxxxxxxxx
    EDGE_POLL_INTERVAL=30

    在创建一个docker-compose.yml文件:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    
    version: "3.8"
    services:
      portainer-edge-agent:
        image: portainer/agent:2.39.0
        container_name: portainer-edge-agent
        restart: unless-stopped
        environment:
          EDGE: "1"
          EDGE_ID: ${EDGE_ID}
          EDGE_KEY: ${EDGE_KEY}
          EDGE_INSECURE_POLL: "1"
          EDGE_POLL_INTERVAL: ${EDGE_POLL_INTERVAL}
        volumes:
          - /var/run/docker.sock:/var/run/docker.sock
          - /var/lib/docker/volumes:/var/lib/docker/volumes

    直接启动这个容器就行了,不需要设置代理,因为Portainer Server不需要去找它,它会根据EDGE_KEY里的信息去寻找Portainer Server。

部署完毕
#

完成上述步骤后,就可以在本地电脑的Portainer界面里看到远程电脑的容器了:

Portainer Agent界面

点击进入,看到的界面和我们在本地电脑上的Portainer容器管理界面是一样的。

添加更多Edge Agent
#

如果你想把更多电脑添加到本地的Portainer界面,只需要重复上面的步骤就行了。

注意,添加每台电脑时都需要重新创建一个新的环境,并获取新的EDGE_IDEDGE_KEY,环境不能共用。

常见问题
#

如果在部署Portainer Edge Agent容器后容器内报类似下面所示的错误:

1
2026/03/19 08:30:37.506PM FTL github.com/portainer/agent/cmd/agent/main.go:262 > unable to associate Edge key | error="invalid key format"

这说明在对EDGE_KEY重新编码时使用的base64设置不对。我们需要将EDGE_KEY编码为Base64URL格式,需要使用base64url命令,但Linux默认只有base64命令,为此我们加了-w0参数,但这个参数只是为了禁用换行功能。

如果出现了上述错误,可以使用某些可以进行Base64URL编码的网站来编码,例如:https://www.base64encode.org/,记得勾选Perform URL safe encoding选项。

容器技术系列 - 这篇文章属于一个选集。
§ 8: 本文