Docker实战之三:zabbix自定义监控项

机器部署情况一览

总的来说,有四台机器,各自的功能如下:

a. 假设有一个机器在运行web应用,容器是tomcat,这个应用有个接口http://localhost:8080/zabbixcustomitemdemo/count,可以返回最近一分钟的某个业务量(例如网站访问次数);

b. 有一台机器安装了zabbix agent,作为自定义监控项的载体;

c. 有一台机器安装了zabbix server;
d. 有一台机器安装了mysql,作为zabbix系统的数据库;

整体部署如下图所示:

未分类

运行web应用的server

这是个基于maven的java web应用,里面有个spring mvc的controller,提供一个http服务,范围某个业务每分钟的业务量,代码如下图所示:

@Controller
public class CountController {

    @RequestMapping("/count")
    @ResponseBody
    public int count(String model, String type) {
        int base;
        int max;
        int min;

        if("a".equals(model)){
            base = 50000;
        }else{
            base =10000;
        }

        if("0".equals(type)){
            max = 9000;
            min = 1000;
        }else{
            max = 1000;
            min = 0;
        }

        return base + new Random().nextInt(max)%(max-min+1);
    }
}

从以上代码我们可以看出,http服务会返回随机数,此服务接受两个参数model和type,当model等于”a”时返回的随机数从50000开始,model不等于”a”时返回的随机数从10000开始,当type等于”0”时,在base的基础上增加的值是1000到9000之间,当type不等于”0”时,在base的基础上增加的值是0到1000之间;

整个工程的代码已经上传到git上,地址是[email protected]:zq2599/blog_demos.git,这个目录下由多个工程,本次实战的工程是zabbixcustomitemdemo,如下图:

未分类

docker-compose.yml文件

上面我们已经把四台机器的功能和关系梳理清楚了,现在就来制定docker-compose.yml文件吧:

version: '2'
services:
  zabbix-mysql-service: 
    image: daocloud.io/library/mysql:8
    container_name: zabbix-mysql-service
    environment:
      - MYSQL_ROOT_PASSWORD=888888
    restart: always
  zabbix-server-service:
    image: monitoringartist/zabbix-xxl:3.2.6
    links: 
      - zabbix-mysql-service:mysqlhost
    container_name: zabbix-server-service
    restart: always
    depends_on:
      - zabbix-mysql-service
    ports:
      - "8888:80"
    environment:
      - ZS_DBHost=mysqlhost
      - ZS_DBUser=root
      - ZS_DBPassword=888888
  zabbix-agent-a:
    image: zabbix/zabbix-agent:ubuntu-3.2.6
    links: 
      - zabbix-server-service:zabbixserverhost
    container_name: zabbix-agent-a
    restart: always
    depends_on:
      - zabbix-server-service
    environment:
      - ZBX_HOSTNAME=zabbix-agent-service-a
      - ZBX_SERVER_HOST=zabbixserverhost
  tomcat-server-service:
    image: bolingcavalry/bolingcavalrytomcat:0.0.1
    container_name: tomcat-server
    restart: always
    ports:
      - "8080:8080"

yml文件的内容如上所示,其中mysql、zabbix server,zabbix agent的配置和上一章《Docker下实战zabbix三部曲之二:监控其他机器》是一样的,新增的是一个tomcat的镜像,这个镜像是我在tomcat官方镜像的基础上做了点小改动,使得这个tomcat支持在线部署web应用,关于tomcat在线部署应用,请看文章《实战docker,编写Dockerfile定制tomcat镜像,实现web应用在线部署》: https://blog.csdn.net/boling_cavalry/article/details/70184605

准备好yml文件之后,打开终端,在yml文件所在目录下执行docker-compose up -d可以将yml文件中所有的容器都启动;

注意,如果您的电脑之前已经运行过上一章《Docker下实战zabbix三部曲之二:监控其他机器》中的docker-compose.yml文件,那么本次执行docker-compose up -d会提示启动失败,已有同样名称的容器存在,这时候可以去上一章的docker-compose.yml文件所在目录执行docker-compose down,也可以通过docker ps -a将所有容器列出,再通过docker stop命令依次停止所有容器,再执行docker-compose rm命令依次删除;

部署web应用

打开终端,进入web工程zabbixcustomitemdemo的目录下,执行命令mvn clean package -U -Dmaven.test.skip=true tomcat7:redeplo y,即可将web工程部署到tomcat容器上,关于在线部署的细节请参照文章《实战docker,编写Dockerfile定制tomcat镜像,实现web应用在线部署》;

部署成功后,打开浏览器,访问http://localhost:8080/zabbixcustomitemdemo/count,web server会返回一个数字,如下图所示:

未分类

制作访问url的shell脚本

接下来我们要在zabbix agent上做一个shell脚本,此脚本的功能时发起http请求http://localhost:8080/zabbixcustomitemdemo/count?model=a&type=0,就能得到web服务响应的数字,如果此脚本每分钟被调用一次,就能得到完整的监控曲线图了;

a. 首先,执行docker exec -it zabbix-agent-a /bin/bash登录zabbix agent的容器;
b. 登录后,执行apt-get update更新apt;
c. 先后执行apt-get install wget和apt-get install vim,安装wget和vi工具;
d. 新建目录/usr/work/,在此目录下用vi创建一个shell文件biz_count.sh,内容如下:

#"!/bin/bash
wget -qO- http://tomcathost:8080/zabbixcustomitemdemo/count?model=$1&type=$2
echo ""

上面代码的功能是访问http服务获取一个数字,其中model和type用的是shell的入参;
注意两个细节:
第一个:最后一行代码echo “”,实践证明这一行是很有用的,有了这一行就会在输出http返回的数字后进行换行,有了换行数据才能成功上报到zabbix server;
第二个:wget命令后面的url参数中,”&”符号前面要加转义的斜杠””;

e. 执行chmod a+x biz_count.sh,给shell赋予可执行权限;

agent上添加监控项

继续在zabbix agent容器上,我们要添加一个自定义监控项,这样后面在zabbix server上就能使用该监控项了:
a. 在/etc/zabbix/zabbix_agentd.d目录下,新增一个biz.conf文件,内容如下:

UserParameter=get_total_num[*],/usr/work/biz_count.sh $1 $2

以上代码配置了一个自定义监控项,名称是get_total_num,可以接受两个入参,该监控项会调用biz_count.sh这个脚本,并且把外部传来的两个入参直接传递给biz_count.sh;

b. 执行chmod a+r biz.conf使得该文件可读;

在zabbix agent上测试

继续在zabbix agent容器上,执行以下命令来测试刚刚新加的监控项:

/usr/sbin/zabbix_agentd -t get_total_num[a,0]

中括号中的a,0表示两个入参分别是”a”和”0”,我们执行四次,入参分别用[a,0]、[b,0]、[a,1]、[b,1],得到的结果如下图所示:

未分类

四个返回值分别是54741、17097、50564、10919,结合前面的java代码可以发现两个参数都生效了,数字的大小范围因入参而变化;

为了让监控项生效,需要重启zabbix agent,不过这里有个更快捷的方法可以试试:
a. 执行exit退出zabbix agent容器;
b. 执行docker restart zabbix-agent-a重启zambia agent容器;

到了这里,自定义监控项已经准备好了,接下来在zabbix server上把它配置成功,我们就能看到监控数据和曲线图了,不过在配置前,我们可以在zabbix server上测试一下能否成功调用zabbix agent上的监控项;

在zabbix server上测试agent机器的监控项

首先我们要搞清楚zabbix agent机器的ip,有两种方法:
第一种,执行docker exec -it zabbix-agent-a /bin/bash登录zabbix agent的容器,在容器中执行ip addr命令可以得到ip;
第二种,直接执行docker exec -it zabbix-agent-a ip addr命令得到ip;

不论哪种,都能得到zabbix-agent的ip是172.31.0.5;

现在我们登录zabbix server容器,执行命令docker exec -it zabbix-server-service /bin/bash即可登录,登录后执行以下命令:

zabbix_get -s 172.31.0.5 -k get_total_num[a,0]

如下图所示,测试成功,调用agent的监控项返回了符合预期的数据:

未分类

还记得我们刚才在zabbix agent上配置好之后,需要重启agent服务或者重启zabbix agent容器,如果您忘了这一步,现在zabbix server上测试会得到如下错误提示:

未分类

这时候去重启一下,再回来测试就可以成功了。

在管理页面上添加监控项

在浏览器上输入”http://localhost:8888/“登录管理页面,先添加agent机器,如下图:

未分类

添加之后,点击下图红框位置,进入监控项页面:

未分类

如下图,点击右上角的“Create item”即可开始添加监控项:

未分类

新增的监控项,我们只要填写Name,Key,Update interval(更新频率)这几个字段,其他的保持不变,每个要更新的字段的内容如下图:

未分类

填写并保存后,我们可以在Monitoring -> Latest data中看到最新的监控项数据,如下图:

未分类

接下来我们添加一个监控图形,操作如下图所示,可以进入图形管理页面:

未分类

如下图,点击右上角的“Create graph”创建一个图形:

未分类

新建图形的时候,名称随意,只要Items选中刚刚创建的监控项即可,如下图:

未分类

创建成功,现在要看看效果了,操作如下图所示:

未分类

点击”add”之后,在弹出的页面上选择刚刚我们新建的图形选项,操作完毕后,点击下图红框位置,就能看见曲线图了:

未分类

曲线图如下:

未分类

以上就是自定义监控项开发和设置的所有过程,基于监控项的操作,除了图形还能添加tirgger用来告警,在添加action用来确定告警的动作

Docker实战之二:zabbix监控其他机器

部署情况

假设实际项目中我们有两台应用服务器,为了监控它们,我们要在上面分别安装zabbix-agent服务,然后通过配置让它们与zabbix server连接,所有监控数据和监控配置数据都被zabbix server保存在mysql中,部署情况如下图:

未分类

真实场景中是在应用服务器上安装zabbix agent服务,但是安装agent的过程和步骤不是本次实践的重点,为了快速体验服务本文使用了zabbix官方的agent镜像,这个镜像实际上就是在ubuntu14上安装了zabbix agent(在服务器上安装zabbix agent的过程就不在本文中详述了,对安装有兴趣的读者们可以去网上搜索相关资料)

docker-compose.yml文件

按照前面图片所示的部署情况,我们的docker-compose.yml内容如下,mysql和zabbix server是必须的,再新增了两个zabbix agent容器,名称分别是zabbix-agent-a和zabbix-agent-b:

version: '2'
services:
  zabbix-mysql-service: 
    image: daocloud.io/library/mysql:8
    container_name: zabbix-mysql-service
    environment:
      - MYSQL_ROOT_PASSWORD=888888
    restart: always
  zabbix-server-service:
    image: monitoringartist/zabbix-xxl:3.2.6
    links: 
      - zabbix-mysql-service:mysqlhost
    container_name: zabbix-server-service
    restart: always
    depends_on:
      - zabbix-mysql-service
    ports:
      - "8888:80"
    environment:
      - ZS_DBHost=mysqlhost
      - ZS_DBUser=root
      - ZS_DBPassword=888888
  zabbix-agent-a:
    image: zabbix/zabbix-agent:ubuntu-3.2.6
    links: 
      - zabbix-server-service:zabbixserverhost
    container_name: zabbix-agent-a
    restart: always
    depends_on:
      - zabbix-server-service
    environment:
      - ZBX_HOSTNAME=zabbix-agent-service-a
      - ZBX_SERVER_HOST=zabbixserverhost
  zabbix-agent-b:
    image: zabbix/zabbix-agent:ubuntu-3.2.6
    links: 
      - zabbix-server-service:zabbixserverhost
    container_name: zabbix-agent-b
    restart: always
    depends_on:
      - zabbix-server-service
    environment:
      - ZBX_HOSTNAME=zabbix-agent-service-b
      - ZBX_SERVER_HOST=zabbixserverhost

如上所示,zabbix agent在配置过程中要用到zabbix server的ip信息,这里我们通过links参数,在zabbix agent的host文件中加入了zabbix server的ip信息,host name是zabbixserverhost;

另外,ZBX_HOSTNAME和ZBX_SERVER_HOST这两个环境变量,在zabbix agent镜像的官方文档中已经说明,如下图,ZBX_HOSTNAME用来表示自己的身份,ZBX_SERVER_HOST是用来标明zabbix server的ip信息的,这里直接用link参数中的alias来表示,就能通过host直接找到zabbix server的ip了:

未分类

启动docker 容器

打开控制台,在docker-compose.yml文件所在的目录下执行命令docker-compose up -d,如图:

未分类

进入控制台

等待大约1分钟,让zabbix server完成初始化,然后就能登录管理页面了,详情请参照《Docker下实战zabbix三部曲之一:极速体验》一文,登录后进入hosts页面,如下图:

未分类

获取监控机器ip

按照前面的部署描述图上的部署,有两台机器装了zabbix agent服务,然后想要加入监控,第一步我们要把机器的ip确定下来,在控制台执行docker exec -it zabbix-agent-a ip addr命令,可以看到如下输出,第一台机器的ip是172.31.0.4:

未分类

在控制台执行docker exec -it zabbix-agent-b ip addr命令,可以看到如第二台机器的ip是172.31.0.5;

添加机器监控

点击hosts页面右上角的Create host按钮,可以添加监控机器,如下图:

未分类

在添加机器的页面,主要参数填写如下:
a. Host name :机器的环境变量ZBX_HOSTNAME的值:zabbix-agent-service-a;
b. Visible name :和Host name的值相同;
c. Groups : 机器分组,这里选择Linux servers;
d. Agent interfaces:这里面只需要填写IP address,就是刚才我们通过命令docker exec -it zabbix-agent-a ip addr得到的ip:172.31.0.4;
这个页面只需要填写以上四点内容,其他的都保持默认值,填写完毕后点击底部的”Add”按钮,如下图:

未分类

增加成功后,在列表中可以看到新增的机器,如下图:

未分类

添加监控项

在机器列表页面中,点击机器名称,如下图红框中位置:

未分类

在打开的页面点击”Templates”,如下图红框所示:

未分类

再点击”Select”按钮,如下图红框所示:

未分类

在弹出的页面中,勾选“Template OS Linux”,然后再点击底部的“Select”按钮,这样就把linux服务的常用监控项给批量添加了,如下图:

未分类

注意,返回的页面中,一定要点击“Add”才能让把刚才的选择加上,如下图:

未分类

然后点击“Update”,完成监控项的批量添加,如下图:

未分类

按照同样的方法把另一台机器的监控也加上;

等待大约1-2分钟后,再刷新hosts页面,就能看到列表中的”ZBX”图标已经变为绿色,表示监控已经生效;

查看监控信息

我们来看看监控曲线图吧,点击下图红框中的“Graphs”:

未分类

可以看到有5个曲线图可以查看,如下图,我们看一下cpu load:

未分类

在跳转的页面中,点击“Previdew”,就能看到cpu load的曲线图了,如下图:

未分类

至此,我们添加监控机器的实战已经完成了,但是在真实场景中,我们除了cpu、磁盘等基础信息的监控,还要监控一些业务有关的数据,例如某个http服务每分钟的访问量。

Docker下实战之一:搭建zabbix监控平台

创建docker-compose.yml文件

首先创建一份docker-compose.yml文件,内容如下:

version: '2'
services:
  zabbix-mysql: 
    image: daocloud.io/library/mysql:8
    container_name: zabbix-mysql
    environment:
      - MYSQL_ROOT_PASSWORD=888888
    restart: always
  zabbix-server:
    image: monitoringartist/zabbix-xxl
    links: 
      - zabbix-mysql:mysqlhost
    container_name: zabbix-server
    restart: always
    depends_on:
      - zabbix-mysql
    ports:
      - "8888:80"
    environment:
      - ZS_DBHost=mysqlhost
      - ZS_DBUser=root
      - ZS_DBPassword=888888

创建容器

打开命令行,在刚才创建的docker-compose.yml目录下,执行docker-compose up -d,这样就会先后启动mysql和zabbix server两个服务的容器,如下图:

未分类

等待zabbix server初始化

在命令行输入命令docker logs -f zabbix-server-service,查看zabbix server的日志输出,下图是部分日志的截图,可以看到有数据库初始化的操作:

未分类

登录zabbix管理页面体验

等待大约一分钟之后,zabbix server的日志不再滚动,表示初始化已经完成,打开浏览器输入http://localhost:8888,可以看到zabbix的管理系统的登录页面,如下图:

未分类

输入用户名admin,密码zabbix

登录后即可看到管理系统了,如下图:

未分类

按照下图的操作,查看已经监控的主机情况,如图,目前只能看到一台机器的信息,就是zabbix server自己这台机器,从列表的几列信息中可以看到有64个监控项,43个触发器,10个图形:

未分类

上图右侧的红框中显示的status是”Disabled”,表示这个host的监控还没有启动,点击这个”Disabled”就能启动监控,将状态变为”Enabled”,大约1分钟后再刷新页面,可以看到展示如下图:

未分类

除了状态变为”Enabled”,右侧原本灰色的”ZBX”也变成了绿色,表示该机器的监控状态是正常的;

体验监控图形

按照下图的红框和箭头操作,可以看到zabbix server所在机器的cpu load的曲线图:

未分类

显示中文

按照下图的箭头依次点击红框中的内容:

未分类

在打开后的页面中按照下图的箭头依次选择和点击:

未分类

这时候再点击右上角的”注销”按钮,退出重现登录,如下图红框所示:

未分类

再重新登录后,就能看见页面已经全部中文显示了,如下图:

未分类

以上就是Docker下实战zabbix的第一部分,快速体验zabbix系统和服务,但只有一个zabbix server服务器意义不大,毕竟实际场景是要通过zabbix系统去监控其他机器和服务,下一章我们尝试把zabbix agent加入进来,以更接近实际场景的方式来继续学习zabbix。

通过 UI 管理 docker

Docker 正在被用在越来越多的场景中,对于不太习惯命令行工具的朋友来说,docker cli 用起来可能会比较吃力。本文笔者将介绍一个功能强大的 docker web 客户端:portainer(岸吊,参考下面插图)。按照 portainer 官方的说法,它可以让我们通过 UI 轻松的管理 docker 主机和 docker swarm 集群。说明:本文的演示环境为 ubuntu 16.04。

未分类

安装 portainer

就像我们通过容器来运行应用一样,运行一个容器就行了,非常简单:

$ docker run -d -p 9000:9000 -v /var/run/docker.sock:/var/run/docker.sock portainer/portainer

然后在浏览器中通过 9000 端口访问 protainer 站点就可以了!

配置管理员信息

第一次访问 portainer 时需要设置管理员的账号信息,我们把管理员账号命名为 admin 并设置密码:

未分类

选择 portainer 管理的对象

简单起见我们先配置 portainer 管理本机的 docker,选择下图中的 “Local” 并点击 “connect” 按钮:

未分类

了解 portainer 的主要功能

简单的安装和配置之后我们就进入了 portainer 的 dashboard 界面:

未分类

Portainer 的界面设计非常简洁,主菜单几乎就是 docker 主要功能的一一对应。接下来,我们将通过 App Templates 菜单来创建容器一个容器,并通过该容器来介绍一些 portainer 中比较常用的功能。

选择左侧的 App Templates 菜单,然后选择创建一个运行在 nginx 容器中的应用:

未分类

在容器属性的配置界面中,指定容器的名称为 webdemo,然后在高级选项中设置把宿主机的 80 端口映射到容器中的 80 端口:

未分类

然后点击 “Deploy the container” 按钮开始创建容器。容器创建完成后会显示在 Containers 界面中:

未分类

在这个管理界面上,我们可以选取一个或多个容器进行操作,比如 start、stop、kill、restart、pause、resume 和 remove,当然还可以通过 “Add container” 按钮添加新的容器。下面我们点击红框中的容器名称 “webdemo” 进入到容器详情界面:

未分类

这个界面中显示了单个容器的详情以及可以对这个容器执行的操作。最上方是针对容器的操作,接下来是容器的状态,然后是 “Access control”、”Create image”、”Container details”、”Volumes” 和 “Connected networks”,为了显示方便,笔者把下面的类别都折叠了。
在容器状态区域,需要关注下红框中的 Status、Logs、Console 和 Inspect,这些都是我们平时比较关心的内容。下面点击它们分别查看对应的视图。
Container statistics 视图实时的显示容器占用的资源信息,需要注意的是相关的数据没有被持久化,显示的内容总是每次打开这个视图后的数据:

未分类

容器日志视图显示容器中的日志输出:

未分类

通过控制台视图我们可以在容器中执行命令:

未分类

Inspect 视图则为我们展示容器的详细信息:

未分类

管理手动创建的容器

Portainer 并不是只能管理自己创建的容器,我们通过命令行创建的容器也会被 portainer 发现并管理。比如我们到宿主机的控制台上创建一个名为 mycon 的容器:

$ docker run --rm -id --name mycon ubuntu

然后在 portainer 的容器列表界面中刷新一下:

未分类

mycon 容器已经出现在容器列表中了。

其它操作

通过菜单中的 Images、Networks 和 Volumes 项可以分别管理容器镜像、network 和数据卷:

未分类

这些差别多就是平时使用比较频繁的功能了!

除了管理单机模式的 docker,portainer 还可以管理 docker swarm 集群。具体的用法并不复杂,这里就不再赘述了,有兴趣的朋友可以参考这里https://media-glass.es/portainer-the-ui-for-docker-d067f6335f23

如何为多个PHP-FPM容器构建单一的Nginx Docker镜像

在使用 Docker 容器来开发 PHP 微服务套件的过程中,作者遇到了容器数量过多的问题。

最近,我一直在使用 Docker 容器来开发 PHP 微服务套件。一个问题是 PHP 应用已经搭建,可以与 PHP-FPM 还有 Nginx(取代了简单的 Apche/PHP 环境)一起工作,因此每个 PHP 微服务需要两个容器(以及两个 Docker 镜像):一个 PHP-FPM 容器和一个 Nginx 容器。
这个应用运行了 6 个以上的服务,做个乘法就知道,在开发和生产之间会有约 30 个容器。于是我决定构建一个单独的 Nginx Docker 镜像,它可以使用 PHP-FPM 的主机名作为环境变量并运行单独的配置文件,而不用为每个容器构建单独的 Nginx 镜像。

未分类

在本文中,我介绍了自己从上图中的方法 1 到方法 2 的转换,最后采用的方案中采用了一种新的定制 Docker 镜像。该镜像的代码是开源的,如果读者碰到类似问题,可以提供给大家参考。

为什么用 Nginx?

Nginx 和 PHP-FPM 配合使用能使 PHP 应用的性能更好,但不好的地方在于,和 PHP Apache 镜像不同,PHP-FPM Docker 镜像默认不是和和 Nginx 绑定在一起的。如果需要通过 Nginx 容器和 PHP-FPM 连接,需要在 Nginx 配置里为该后端增加 DNS 记录。比如,如果名为 php-fpm-api 的 PHP-FPM 容器正在运行,Nginx 配置文件应该包含下面部分:

location ~ .php$ {
        fastcgi_split_path_info ^(.+.php)(/.+)$;
        # This line passes requests through to the PHP-FPM container
        fastcgi_pass php-fpm-api:9000;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;
    }

如果只服务于单独的 Nginx 容器,Nginx 配置中容器名字写死还可以接受,但如上所述,需要允许多个 Nginx 容器,每个对应于一个 PHP 服务。创建一个新的 Nginx 镜像(以后需要进行维护和升级)会有些痛苦,即使管理一批不同的数据卷,仅仅改变变量名看起来也有很多工作。

第一种方案: 使用 Docker 文档中的方法

最初,我认为这会很简单。Docker 文档中有少许的几个章节讨论如何使用 envsubst 来完成该工作,但不幸的是,在我的 Nginx 配置文件中,这种方法不奏效。

vhosts.conf

server {
    listen 80;
    index index.php index.html;
    root /var/www/public;
    client_max_body_size 32M;
    location / {
        try_files $uri /index.php?$args;
    }
    location ~ .php$ {
        fastcgi_split_path_info ^(.+.php)(/.+)$;
        fastcgi_pass ${NGINX_HOST}:9000;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;
    }
}

该vhosts.conf文件使用了 Nginx 内置变量,因此当依照文档运行 Docker 命令 (/bin/bash -c “envsubst < /etc/nginx/conf.d/mysite.template > /etc/nginx/conf.d/default.conf && nginx -g ‘daemon off;’”) 时,得到错误提示$uri和$fastcgi_script_name没有定义。这些变量通常通过 Nginx 传入,因此不能简单的识别出他们是什么并传给自身,而且这样会影响容器的动态配置。

使用另一个 Docker 镜像,差点成功

接下来,我开始研究不同的 Nginx 镜像。找到的有两个,但它们都在随后的几年中都没有任何更新。我开始使用 martin/nginx,试图找到可以工作的原型。

Martin 的镜像和其它镜像有点不一样,因为它要求特定的文件夹结构。在 root 下增加Dockerfile:

FROM martin/nginx

接下来,我添加了一个app/空目录和conf/目录,conf/目录下只有一个文件
vhosts.conf:

server {
    listen 80;
    index index.php index.html;
    root /var/www/public;
    client_max_body_size 32M;
    location / {
        try_files $uri /index.php?$args;
    }
    location ~ .php$ {
        fastcgi_split_path_info ^(.+.php)(/.+)$;
        fastcgi_pass $ENV{"NGINX_HOST"}:9000;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;
    }
}

这个文件和之前的配置文件几乎一样,除了有一行的改动:
fastcgi_pass $ENV{“NGINX_HOST”}:9000;。现在想要启动带命名为 php-fpm-api 的 PHP 容器的 Nginx 容器,就可以构建一个新的镜像,让它在以下环境变量下运行:

docker build -t shiphp/nginx-env:test .
docker run -it --rm -e NGINX_HOST=php-fpm-api shiphp/nginx-env:test

它可以正常工作了。但是,这种方法有两个困扰的地方:
正在使用的基础镜像已经有两年了。这会引入安全和性能风险。
有个空的/app目录看起来并不必需,因为文件会被存储在一个不同的目录中。

最终解决方案

我认为作为定制解决方案,从 Martin 镜像开始比较好,因此我给项目建了分叉,创建了新的 Nginx 基础镜像并修复了上述两个问题。现在,如果要在 Nginx 容器中允许动态命名的后端,可以参照:

# 从 Docker Hub 得到最新版本
docker pull shiphp/nginx-env:latest
# 运行名为"php-fpm-api"的 PHP 容器 
docker run --name php-fpm-api -v $(pwd):/var/www php:fpm
# 允许链接到 PHP-FPM 容器的 NGinx 容器
docker run --link php-fpm-api -e NGINX_HOST=php-fpm-api shiphp/nginx-env

如果想增加自己的文件或 Nginx 配置文件,来定制镜像,用Dockerfile来扩展它就可以:

FROM shiphp/nginx-env
ONBUILD ADD <PATH_TO_YOUR_CONFIGS> /etc/nginx/conf.d/
...

现在所有的 PHP-FPM 容器都能使用单个Docker镜像中自己的实例,这样在升级 Nginx,改变权限或做某些调整时,就变得非常轻松了。

所有的代码都在 Github 上,如果你看到任何问题或有改进建议,可以直接创建 issue。

GitHub 项目链接:https://github.com/shiphp/nginx-env

阿里云DOCKER标准日志采集(NGINX)

部署Logtail容器

1. 拉取Logtail镜像

docker pull registry.cn-hangzhou.aliyuncs.com/log-service/logtail

2. 启动Logtail容器

docker run -d -v /:/logtail_host:ro -v /var/run/docker.sock:/var/run/docker.sock --env ALIYUN_LOGTAIL_CONFIG=/etc/ilogtail/conf/${your_region_name}/ilogtail_config.json --env ALIYUN_LOGTAIL_USER_ID=${your_aliyun_user_id} --env ALIYUN_LOGTAIL_USER_DEFINED_ID=${your_machine_group_user_defined_id} registry.cn-hangzhou.aliyuncs.com/log-service/logtail

参数说明

参数                                  参数说明

${your_region_name}                 region名,请替换为您创建的日志服务project所在Region。Region名称请从Logtail安装参数列表中选择。
${your_aliyun_user_id}                  用户标识,请替换为您的阿里云主账号用户ID。主账号用户ID为字符串形式。
${your_machine_group_user_defined_id}   您集群的机器组自定义标识。如您尚未开启自定义标识,请参考自定义机器组的步骤一,开启userdefined-id。

示例:docker run -d -v /:/logtail_host:ro -v /var/run/docker.sock:/var/run/docker.sock –env ALIYUN_LOGTAIL_CONFIG=/etc/ilogtail/conf/cn-hangzhou_internet/ilogtail_config.json –env ALIYUN_LOGTAIL_USER_ID=1473463995701522 –env ALIYUN_LOGTAIL_USER_DEFINED_ID=aliyun-sunaiwen-machine registry.cn-hangzhou.aliyuncs.com/log-service/logtail

查看lables配置信息

docker inspect 3fb1dd4bd2c7

logtail配置

未分类

未分类

使用Docker 实现微服务并搭建博客,一文全掌握。

未分类

Docker 是一个容器工具,提供虚拟环境。很多人认为,它改变了我们对软件的认识。

本文,通过搭建一个博客的例子,来介绍如何使用Docker实现微服务。
站在 Docker 的角度,软件就是容器的组合:业务逻辑容器、数据库容器、储存容器、队列容器……Docker 使得软件可以拆分成若干个标准化容器,然后像搭积木一样组合起来。
这正是微服务(microservices)的思想:软件把任务外包出去,让各种外部服务完成这些任务,软件本身只是底层服务的调度中心和组装层。

未分类

微服务很适合用 Docker 容器实现,每个容器承载一个服务。一台计算机同时运行多个容器,从而就能很轻松地模拟出复杂的微服务架构。

未分类

上一篇教程介绍了 Docker 的概念和基本用法,本文接着往下介绍,如何在一台计算机上实现多个服务,让它们互相配合,组合出一个应用程序。

未分类

我选择的示例软件是 WordPress。它是一个常用软件,全世界用户据说超过几千万。同时它又非常简单,只要两个容器就够了(业务容器 + 数据库容器),很适合教学。而且,这种”业务 + 数据库”的容器架构,具有通用性,许多应用程序都可以复用。

为了加深读者理解,本文采用三种方法,演示如何架设 WordPress 网站。

  • 方法 A:自建 WordPress 容器
  • 方法 B:采用官方的 WordPress 容器
  • 方法 C:采用 Docker Compose 工具

一、预备工作:image 仓库的镜像网址

本教程需要从仓库下载 image 文件,但是国内访问 Docker 的官方仓库很慢,还经常断线,所以要把仓库网址改成国内的镜像站。这里推荐使用官方镜像 registry.docker-cn.com 。下面是我的 Debian 系统的默认仓库修改方法,其他系统的修改方法参考官方文档。
打开/etc/default/docker文件(需要sudo权限),在文件的底部加上一行。

DOCKER_OPTS="--registry-mirror=https://registry.docker-cn.com"

然后,重启 Docker 服务。

$ sudo service docker restart

现在就会自动从镜像仓库下载 image 文件了。

二、方法 A:自建 WordPress 容器

前面说过,本文会用三种方法演示 WordPress 的安装。第一种方法就是自建 WordPress 容器。

2.1 官方 的 PHP image

首先,新建一个工作目录,并进入该目录。

$ mkdir docker-demo && cd docker-demo

然后,执行下面的命令。

$ docker container run 
  --rm 
  --name wordpress 
  --volume "$PWD/":/var/www/html 
  php:5.6-apache

上面的命令基于php的 image 文件新建一个容器,并且运行该容器。php的标签是5.6-apache,说明装的是 PHP 5.6,并且自带 Apache 服务器。该命令的三个参数含义如下。

  • –rm:停止运行后,自动删除容器文件。
  • –name wordpress:容器的名字叫做wordpress。
  • –volume “$PWD/”:/var/www/html:将当前目录($PWD)映射到容器的/var/www/html(Apache 对外访问的默认目录)。因此,当前目录的任何修改,都会反映到容器里面,进而被外部访问到。

运行上面的命令以后,如果一切正常,命令行会提示容器对外的 IP 地址,请记下这个地址,我们要用它来访问容器。我分配到的 IP 地址是 172.17.0.2。

打开浏览器,访问 172.17.0.2,你会看到下面的提示。

Forbidden
You don't have permission to access / on this server.

这是因为容器的/var/www/html目录(也就是本机的docker-demo目录)下面什么也没有,无法提供可以访问的内容。

请在本机的docker-demo目录下面,添加一个最简单的 PHP 文件index.php。

<?php 
phpinfo();?>

保存以后,浏览器刷新172.17.0.2,应该就会看到熟悉的phpinfo页面了。

未分类

2.2 拷贝 WordPress 安装包

既然本地的docker-demo目录可以映射到容器里面,那么把 WordPress 安装包拷贝到docker-demo目录下,不就可以通过容器访问到 WordPress 的安装界面了吗?

首先,在docker-demo目录下,执行下面的命令,抓取并解压 WordPress 安装包。

$ wget https://cn.wordpress.org/wordpress-4.9.4-zh_CN.tar.gz
$ tar -xvf wordpress-4.9.4-zh_CN.tar.gz

解压以后,WordPress 的安装文件会在docker-demo/wordpress目录下。

这时浏览器访问http://172.17.0.2/wordpress,就能看到 WordPress 的安装提示了。

未分类

2.3 官方的 MySQL 容器

WordPress 必须有数据库才能安装,所以必须新建 MySQL 容器。

打开一个新的命令行窗口,执行下面的命令。

$ docker container run 
  -d 
  --rm 
  --name wordpressdb 
  --env MYSQL_ROOT_PASSWORD=123456 
  --env MYSQL_DATABASE=wordpress 
  mysql:5.7

上面的命令会基于 MySQL 的 image 文件(5.7版本)新建一个容器。该命令的五个命令行参数的含义如下。

  • -d:容器启动后,在后台运行。
  • –rm:容器终止运行后,自动删除容器文件。
  • –name wordpressdb:容器的名字叫做wordpressdb
  • –env MYSQL_ROOT_PASSWORD=123456:向容器进程传入一个环境变量MYSQL_ROOT_PASSWORD,该变量会被用作 MySQL 的根密码。
  • –env MYSQL_DATABASE=wordpress:向容器进程传入一个环境变量MYSQL_DATABASE,容器里面的 MySQL 会根据该变量创建一个同名数据库(本例是WordPress)。

运行上面的命令以后,正常情况下,命令行会显示一行字符串,这是容器的 ID,表示已经新建成功了。

这时,使用下面的命令查看正在运行的容器,你应该看到wordpress和wordpressdb两个容器正在运行。

$ docker container ls

其中,wordpressdb是后台运行的,前台看不见它的输出,必须使用下面的命令查看。

$ docker container logs wordpressdb

2.4 定制 PHP 容器

现在 WordPress 容器和 MySQL 容器都已经有了。接下来,要把 WordPress 容器连接到 MySQL 容器了。但是,PHP 的官方 image 不带有mysql扩展,必须自己新建 image 文件。

首先,停掉 WordPress 容器。

$ docker container stop wordpress

停掉以后,由于–rm参数的作用,该容器文件会被自动删除。

然后,在docker-demo目录里面,新建一个Dockerfile文件,写入下面的内容。

FROM php:5.6-apache
RUN docker-php-ext-install mysqli
CMD apache2-foreground

上面代码的意思,就是在原来 PHP 的 image 基础上,安装mysqli的扩展。然后,启动 Apache。

基于这个 Dockerfile 文件,新建一个名为phpwithmysql的 image 文件。

$ docker build -t phpwithmysql .

2.5 WordPress 容器连接 MySQL

现在基于 phpwithmysql image,重新新建一个 WordPress 容器。

$ docker container run 
  --rm 
  --name wordpress 
  --volume "$PWD/":/var/www/html 
  --link wordpressdb:mysql 
  phpwithmysql

跟上一次相比,上面的命令多了一个参数–link wordpressdb:mysql,表示 WordPress 容器要连到wordpressdb容器,冒号表示该容器的别名是mysql。

这时还要改一下wordpress目录的权限,让容器可以将配置信息写入这个目录(容器内部写入的/var/www/html目录,会映射到这个目录)。

$ chmod -R 777 wordpress

接着,回到浏览器的http://172.17.0.2/wordpress页面,点击”现在就开始!”按钮,开始安装。

WordPress 提示要输入数据库参数。输入的参数如下。

未分类

  • 数据库名:wordpress
  • 用户名:root
  • 密码:123456
  • 数据库主机:mysql
  • 表前缀:wp_(不变)

点击”下一步”按钮,如果 WordPress 连接数据库成功,就会出现下面的页面,这就表示可以安装了。

未分类

至此,自建 WordPress 容器的演示完毕,可以把正在运行的两个容器关闭了(容器文件会自动删除)。

$ docker container stop wordpress wordpressdb

三、方法 B:Wordpress 官方镜像

上一部分的自建 WordPress 容器,还是挺麻烦的。其实不用这么麻烦,Docker 已经提供了官方 WordPress image,直接用那个就可以了。有了上一部分的基础,下面的操作就很容易理解了。

3.1 基本用法

首先,新建并启动 MySQL 容器。

$ docker container run 
  -d 
  --rm 
  --name wordpressdb 
  --env MYSQL_ROOT_PASSWORD=123456 
  --env MYSQL_DATABASE=wordpress 
  mysql:5.7

然后,基于官方的 WordPress image,新建并启动 WordPress 容器。

$ docker container run 
  -d 
  --rm 
  --name wordpress 
  --env WORDPRESS_DB_PASSWORD=123456 
  --link wordpressdb:mysql 
  wordpress

上面命令中,各个参数的含义前面都解释过了,其中环境变量WORDPRESS_DB_PASSWORD是 MySQL 容器的根密码。

上面命令指定wordpress容器在后台运行,导致前台看不见输出,使用下面的命令查出wordpress容器的 IP 地址。

$ docker container inspect wordpress

上面命令运行以后,会输出很多内容,找到IPAddress字段即可。我的机器返回的 IP 地址是172.17.0.3。

浏览器访问172.17.0.3,就会看到 WordPress 的安装提示。

未分类

3.2 WordPress 容器的定制

到了上一步,官方 WordPress 容器的安装就已经成功了。但是,这种方法有两个很不方便的地方。

  • 每次新建容器,返回的 IP 地址不能保证相同,导致要更换 IP 地址访问 WordPress。
  • WordPress 安装在容器里面,本地无法修改文件。

解决这两个问题很容易,只要新建容器的时候,加两个命令行参数就可以了。

先把刚才启动的 WordPress 容器终止(容器文件会自动删除)。

$ docker container stop wordpress

然后,使用下面的命令新建并启动 WordPress 容器。

$ docker container run 
  -d 
  -p 127.0.0.2:8080:80 
  --rm 
  --name wordpress 
  --env WORDPRESS_DB_PASSWORD=123456 
  --link wordpressdb:mysql 
  --volume "$PWD/wordpress":/var/www/html 
  wordpress

上面的命令跟前面相比,命令行参数只多出了两个。

  • -p 127.0.0.2:8080:80:将容器的 80 端口映射到127.0.0.2的8080端口。
  • –volume “$PWD/wordpress”:/var/www/html:将容器的/var/www/html
    目录映射到当前目录的wordpress子目录。

浏览器访问127.0.0.2:8080:80就能看到 WordPress 的安装提示了。而且,你在wordpress子目录下的每次修改,都会反映到容器里面。

最后,终止这两个容器(容器文件会自动删除)。

$ docker container stop wordpress wordpressdb

四、方法 C:Docker Compose 工具

上面的方法 B 已经挺简单了,但是必须自己分别启动两个容器,启动的时候,还要在命令行提供容器之间的连接信息。因此,Docker 提供了一种更简单的方法,来管理多个容器的联动。

4.1 Docker Compose 简介

未分类

Compose 是 Docker 公司推出的一个工具软件,可以管理多个 Docker 容器组成一个应用。你需要定义一个 YAML 格式的配置文件docker-compose.yml,写好多个容器之间的调用关系。然后,只要一个命令,就能同时启动/关闭这些容器。

$ docker-compose up
$ docker-compose stop

4.2 Docker Compose 的安装

Mac 和 Windows 在安装 docker 的时候,会一起安装 docker compose。Linux 系统下的安装参考官方文档。

安装完成后,运行下面的命令。

$ docker-compose --version

4.3 WordPress 示例

在docker-demo目录下,新建docker-compose.yml文件,写入下面的内容。

mysql:
    image: mysql:5.7
    environment:
     - MYSQL_ROOT_PASSWORD=123456
     - MYSQL_DATABASE=wordpress
web:
    image: wordpress
    links:
     - mysql
    environment:
     - WORDPRESS_DB_PASSWORD=123456
    ports:
     - "127.0.0.3:8080:80"
    working_dir: /var/www/html
    volumes:
     - wordpress:/var/www/html

上面代码中,两个顶层标签表示有两个容器mysql和web。每个容器的具体设置,前面都已经讲解过了,还是挺容易理解的。

启动两个容器。

$ docker-compose up

浏览器访问 http://127.0.0.3:8080,应该就能看到 WordPress 的安装界面。

现在关闭两个容器。

$ docker-compose stop

关闭以后,这两个容器文件还是存在的,写在里面的数据不会丢失。下次启动的时候,还可以复用。下面的命令可以把这两个容器文件删除(容器必须已经停止运行)。

$ docker-compose rm

Docker使用zookeeper

Apache ZooKeeper是一个开源的服务器,可以实现高度可靠的分布式协调。
记录Docker里面使用zookeeper的方法

镜像

docker pull zookeeper

启动一个Zookeeper服务器实例

启动一个zookeeper实例很简单:

docker run --name some-zookeeper --restart always -d zookeeper

由于Zookeeper “fails fast”,最好始终重新启动它。

这里可以加上-p参数把端口映射到主机端口:

docker run --name some-zookeeper -p 2181:2181 --restart always -d zookeeper

这样, 就把容器的2181端口映射到宿主机器的2181端口上了, java程序等可以直接连接(127.0.0.1:2181)

从另一个Docker容器中的应用程序连接到Zookeeper

docker run --name some-app --link some-zookeeper:zookeeper -d application-that-uses-zookeeper

从Zookeeper命令行客户端连接到Zookeeper

docker run -it --rm --link some-zookeeper:zookeeper zookeeper zkCli.sh -server zookeeper

查看日志

docker logs -f e36790ea5c5e

其中e36790ea5c5e是容器的ID, 可以通过docker container ls 来查看.

END

解决 error creating overlay mount to /var/lib/docker/overlay2

最近在centos7.1使用docker运行redis镜像,出现下面的错误:

/usr/bin/docker-current: Error response from daemon: error creating overlay mount to /var/lib/docker/overlay2/65f3c109fb903539820f84856d2725af784f2f03f95b1f0214e34184e4d61ff7-init/merged: invalid argument.
See '/usr/bin/docker-current run --help'.

在网上搜索一番后,一个可行的方案如下(改变storage driver类型, 禁用selinux):

停止docker服务

systemctl stop docker

清理镜像

rm -rf /var/lib/docker

修改存储类型

vi /etc/sysconfig/docker-storage

把空的DOCKER_STORAGE_OPTIONS参数改为overlay:

DOCKER_STORAGE_OPTIONS="--storage-driver overlay"

禁用selinux

vi /etc/sysconfig/docker

去掉option的–selinux-enabled

启动docker应该就可以了

systemctl start docker

方案抄自 Ysssssssssssssss的博客 和 redis的讨论: error creating overlay mount to …/merged: invalid argument., 基本可以确定是启用selinux导致的。

ubuntu Docker 环境下设置crontab

设置crontab 第一个问题就是时区不对的问题,默认系统时区为UTC,时间不同步,设置定时任务时会有偏差。因此,先更改时区

解决办法:在Dockerfile中加入即可

RUN echo”Asia/Shanghai” > /etc/timezone 
RUN dpkg-reconfigure -f noninteractive tzdata

容器启动之后,设置了crontab,结果一直不生效,后发现crontab 服务没有启动,于是网上各种搜索,发现了如下命令

ubuntu下定时执行工具cron开启关闭重启
配置文件一般为/etc/init.d/cron

启动:sudo /etc/init.d/cron start
关闭:sudo /etc/init.d/cron stop
重启:sudo /etc/init.d/cron restart
重新载入配置:sudo /etc/init.d/cron reload

运行遇到了问题

Rather than invoking init scripts through /etc/init.d, use the service(8)
utility, e.g. service cron start

Since the script you are attempting to invoke has been converted to an
Upstart job, you may also use the start(8) utility, e.g. start cron

尝试了提到的命令start cron /status cron,均报错。后搜索发现启动其实非常简单直接执行cron即可!