在拜读浙江大学SEL实验室著作的《Docker容器与容器云》第二版时,自己按照书中在自己的虚拟机中搭建了一个由docker容器构建的应用栈以及部署了对其的监控。由于docker hub中镜像版本的的更迭,按照书中的步骤进行部署,不免会遇到一些问题,在此记录下部署过程也分享下部署过程中踩过的坑。
搭建的环境是:
1:虚拟机工具是VMware Workstations
2:操作系统是CentOS Linux release 7.5.1804 (Core)
3:linux内核是3.10.0-862.el7.x86_64
整个部署过程可以分为三部分:
1.安装docker
2.Haproxy+Django+Redis搭建应用栈
3.Cadvisor+Consul+Prometheus+Grafana部署监控
以下操作均在root用户下进行,其他用户请注意授予sudo权限
执行以下命令:
yum install docker
回显信息:
执行命令:
[root@localhost ~]# systemctl start docker
查看docker基本信息:
[root@localhost ~]# docker info
回显信息:
Containers: 0
Running: 0
Paused: 0
Stopped: 0
Images: 0
Server Version: 1.13.1
...
docker安装完毕!
1.拉取最新版本的镜像,执行以下命令:
[root@localhost ~]# docker pull redis
[root@localhost ~]# docker pull django
[root@localhost ~]# docker pull haproxy
2.查看拉取的镜像,执行以下命令
[root@localhost ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
docker.io/haproxy latest d23194a3929a 5 days ago 72 MB
docker.io/redis latest 5d2989ac9711 2 weeks ago 95 MB
docker.io/django latest eb40dcf64078 2 years ago 436 MB
3.创建容器挂载点目录,之后要给容器挂在文件目录,书中没有提前挂载,在启动容器时,如果不使用-v参数指定挂载目录,docker会默认自己指定挂载目录。执行以下命令:
[root@localhost ~]# cd /home
[root@localhost home]# mkdir docker
[root@localhost home]# ls
docker
[root@localhost home]# cd docker/
[root@localhost docker]# mkdir redis django haproxy
[root@localhost docker]# ls
django haproxy redis
数据库配置包括主数据库和从数据库的配置,分别创建主数据库和从数据库的文件挂载点,执行以下命令:
[root@localhost ~]# cd /home/docker/redis
[root@localhost redis]# mkdir master slave1 slave2
1.master数据库的配置如下:
启动redis-master容器,执行以下命令:
[root@localhost redis]# docker run -it --name redis-master -v /home/docker/redis/master/:/data redis
--name 给容器进行命名,-v指定文件挂载目录
得到结果:
root@e54206d487ed:/data#
这个终端是容器虚拟出来的终端。
从容器终端中退出,使用快捷键Ctrl+D,或者执行以下命令:
root@e54206d487ed:/data# exit
切换到redis-master容器挂载的文件目录,创建redis的配置文件,文件名字是redis.conf配置文件内容,按照网上的模板书写就行,注意修改其中的参数。
[root@localhost ~] cd /home/docker/redis/master
[root@localhost ~] vim redis.conf
修改的参数为:
daemonize yes 注意是将yes修改为no,使Redis在容器前端运行,在前端运行时无法进行其他操作,可以再开一个终端。
pidfile /var/run/redis.pid
再次进入redis-master虚拟终端中,切换到容器中的volume目录:
[root@localhost master]# docker exec -it redis-master /bin/bash
root@e54206d487ed:/data# cd /data
复制启动配置文件从/data到redis工作目录/usr/local/bin下,执行以下命令:
root@e54206d487ed:/data# cp /data/redis.conf /usr/local/bin/
root@e54206d487ed:/data# ls
redis.conf
root@e54206d487ed:/data# cp /data/redis.conf /usr/local/bin/
root@e54206d487ed:/data# cd /usr/local/bin/
root@e54206d487ed:/usr/local/bin# ls
docker-entrypoint.sh gosu redis-benchmark redis-check-aof redis-check-rdb redis-cli redis-sentinel redis-server redis.conf
启动redis-master服务
root@e54206d487ed:/usr/local/bin# redis-server redis.conf
31:C 14 Jan 2019 06:02:44.717 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
31:C 14 Jan 2019 06:02:44.717 # Redis version=5.0.3, bits=64, commit=00000000, modified=0, pid=31, just started
31:C 14 Jan 2019 06:02:44.717 # Configuration loaded
至此主数据库部分配置完成。
2.从数据库配置
从数据库配置以slave1为例,启动redis-slave1容器:
[root@localhost redis]# docker run -it --name redis-slave1 --link=redis-master:master -v /home/docker/redis/slave1/:/data redis /bin/bash
配置redis-slave1的配置文件,注意修改参数有三个:
daemonize yes 注意是将yes修改为no,使Redis在容器前端运行,在前端运行时无法进行其他操作,可以再开一个终端。
pidfile /var/run/redis.pid
slaveof master 6379
再次进入redis-slave1虚拟终端中,切换到容器中的volume目录。将redis.conf复制到slave1目录下,并在容器中将文件从/data复制到/usr/local/bin下执行以下命令:
[root@localhost ~]# docker exec -it redis-slave1 /bin/bash
root@e93da3690ef9:/data# cp redis.conf /usr/local/bin/
root@e93da3690ef9:/data# cd /usr/local/bin/
root@e93da3690ef9:/usr/local/bin# ls
docker-entrypoint.sh gosu redis-benchmark redis-check-aof redis-check-rdb redis-cli redis-sentinel redis-server redis.conf
同样启动redis-slave1的服务,执行以下命令:
root@e93da3690ef9:/usr/local/bin# redis-server redis.conf
58:C 16 Jan 2019 03:40:36.496 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
58:C 16 Jan 2019 03:40:36.496 # Redis version=5.0.3, bits=64, commit=00000000, modified=0, pid=58, just started
58:C 16 Jan 2019 03:40:36.496 # Configuration loaded
执行命令redis-cli info 查看slave1的信息。按照书中所说应该是看到以下信息:
# Replication
role:slave
master_host:master
master_port:6379
master_link_status:up
...
但是可能查看信息时,显示的是role:master,原因在文章末尾进行分析。为了整个应用栈搭建流程的继续进行,先执行下列命令:
root@e93da3690ef9:/data# slaveof master 6379
再次查看信息,role变为slave。
3.数据库测试
进入redis-master容器中执行以下命令:
[root@localhost ~]# docker exec -it redis-master /bin/bash
root@a20d6dd3d296:/data# redis-cli
127.0.0.1:6379> set master xshm
OK
127.0.0.1:6379> get master
"xshm"
127.0.0.1:6379>
进入redis-slave1中进行查看:
[root@localhost ~]# docker exec -it redis-slave1 /bin/bash
root@e93da3690ef9:/data# redis-cli
127.0.0.1:6379> get master
"xshm"
127.0.0.1:6379>
测试成功。同理对redis-slave2进行配置和测试,至此应用栈数据库节点布置完成。
1.准备工作
首先启动创建容器,执行以下命令:
[root@localhost ~]# docker run -it --name=APP1 --link=redis-master:db -v /home/docker/django/APP1/:/usr/src/app django /bin/bash
root@b7295bc2530a:/#
为了访问数据库,需要在容器中安装Python语言的redis支持包,执行以下命令:
root@2ce010fd726c:/# pip install redis
进行简单的测试来验证支持包是否安装成功。执行以下命令:
root@2ce010fd726c:/# python
Python 3.4.5 (default, Dec 14 2016, 18:54:20)
[GCC 4.9.2] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import redis
>>> print(redis.__file__)
/usr/local/lib/python3.4/site-packages/redis/__init__.py
>>>
验证成功。
2.创建APP
在容器的volume目录/usr/src/app下,执行以下命令:
root@2ce010fd726c:/# cd /usr/src/app/
root@2ce010fd726c:/usr/src/app# mkdir dockerweb
root@2ce010fd726c:/usr/src/app# cd dockerweb
root@2ce010fd726c:/usr/src/app/dockerweb# django-admin.py startproject redisweb
root@2ce010fd726c:/usr/src/app/dockerweb# ls
redisweb
root@2ce010fd726c:/usr/src/app/dockerweb# cd redisweb
root@2ce010fd726c:/usr/src/app/dockerweb/redisweb# ls
manage.py redisweb
root@2ce010fd726c:/usr/src/app/dockerweb/redisweb# python manage.py startapp helloworld
root@2ce010fd726c:/usr/src/app/dockerweb/redisweb# ls
helloworld manage.py redisweb
在容器中创建APP之后,切换到主机的volume目录下,进行编辑和配置APP:
[root@localhost ~]# cd /home/docker/django/APP1/
[root@localhost APP1]# ls
dockerweb
[root@localhost APP1]# cd dockerweb/redisweb/helloworld/
[root@localhost helloworld]# ls
admin.py apps.py __init__.py migrations models.py __pycache__ tests.py views.py
[root@localhost helloworld]# vim views.py
文件内容为:
from django.shortcuts import render
from django.http import HttpResponse
# Create your views here.
import redis
def hello(request):
str=redis.__file__
str+="
"
r = redis.Redis(host='db',port=6379,db=0)
info = r.info()
str+=("Set Hi
")
r.set('Hi','HelloWorld-APP1')
str+=("Get Hi: %s
" % r.get('Hi'))
str+=("Redis Info:
")
str+=("Key: Info Value")
for key in info:
str+=("%s: %s
" % (key, info[key]))
return HttpResponse(str)
接下来修改redisweb项目的配置文件setting.py,添加新建的helloworld应用,执行过程如下:
[root@localhost helloworld]# cd ../redisweb/
[root@localhost redisweb]# ls
__init__.py __pycache__ settings.py urls.py wsgi.py
[root@localhost redisweb]# vim settings.py
文件内容修改:INSTALL_APPS选项下添加hellworld,注意还要将,ALLOWED_HOSTS = [],修改为ALLOWED_HOSTS = ['*']
书中未提到这一点。
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = ['*']
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'helloworld',
]
接下来是修改redisweb项目的URL模式文件urls.py。由于Django版本不同配置文件格式与书中有所不同。
from django.conf.urls import include, url
from django.contrib import admin
from helloworld.views import hello
urlpatterns = [
url(r'^admin/', include(admin.site.urls)),
url(r'^helloworld$',hello),
]
在容器的主机修改完这几个·文件之后进入容器,在目录/usr/src/app/dockerweb/redisweb下完成项目的生成。
[root@localhost redisweb]# docker exec -it APP1 /bin/bash
root@2ce010fd726c:/# cd /usr/src/app/dockerweb/redisweb/
root@2ce010fd726c:/usr/src/app/dockerweb/redisweb# python manage.py makemigrations
No changes detected
root@2ce010fd726c:/usr/src/app/dockerweb/redisweb# python manage.py migrate
...
APP1容器的配置已经完成,同理配置APP2。至此应用栈APP节点配置完成。
3.启动APP
以APP1为例。对于APP1使用8001端口,APP2使用8002端口,同时都使用0.0.0.0地址。进入容器执行命令:
root@2ce010fd726c:~# cd /usr/src/app/dockerweb/redisweb/
root@2ce010fd726c:/usr/src/app/dockerweb/redisweb# python manage.py runserver 0.0.0.0:8001
Performing system checks...
System check identified no issues (0 silenced).
January 18, 2019 - 05:24:25
Django version 1.10.4, using settings 'redisweb.settings'
Starting development server at http://0.0.0.0:8001/
Quit the server with CONTROL-C.
至此,APP节点启动完毕。
1.启动haproxy
执行以下命令:
[root@localhost ~]# docker run -it ---name=HAproxy --link=APP1:APP1 --link=APP2:APP2 -p 6301:6301 -v /home/docker/haproxy/:/tmp haproxy /bin/bash
2.haproxy文件配置
利用容器启动时挂载的volume将HAproxy的启动配置文件复制到容器中,在主机中执行以下过程:
[root@localhost ~]# cd /home/docker/haproxy/
[root@localhost haproxy]# vim haproxy.cfg
由于haproxy版本迭代,文件内容与书中略有不同,如下:
global
log 127.0.0.1 local0
maxconn 4096
chroot /usr/local/sbin
#daemon
nbproc 4
pidfile /usr/local/sbin/haproxy.pid
defaults
log 127.0.0.1 local3
mode http
option dontlognull
retries 2
maxconn 2000
balance roundrobin
timeout connect 5000ms
timeout client 50000ms
timeout server 50000ms
listen redis_proxy
bind 0.0.0.0:6301
stats enable
stats uri /haproxy-stats
server APP1 APP1:8001 check inter 2000 rise 2 fall 5
server APP2 APP2:8002 check inter 2000 rise 2 fall 5
随后进入容器中将配置文件复制到haproxy工作的目录中,执行以下命令:
[root@localhost haproxy]# docker attach HAproxy
root@9f04bcbd3515:/# cp /tmp/haproxy.cfg /usr/local/sbin/
接下来利用该文件启动haproxy代理,执行命令:
root@9f04bcbd3515:/tmp# haproxy -f haproxy.cfg
[WARNING] 017/055939 (14) : Proxy 'redis_proxy': in multi-process mode, stats will be limited to process assigned to the current request.
[WARNING] 017/055939 (14) : is only meaningful in daemon mode or master-worker mode. Setting limit to 1 process.
至此完成haproxy容器节点的全部部署,同时也完成了docker应用栈的部署。
应用栈启动后,在本地电脑浏览器中访问容器的宿主机,也就是电脑上的虚机的6301端口,在浏览器中访问http://10.34.132.128:6301/helloworld。刷新网页就会在两个APP容器节点切换。结果如图所示:
1.从docker镜像库拉取镜像,执行以下命令:
[root@localhost ~]# docker pull google/cadvisor
[root@localhost ~]# docker pull consul
[root@localhost ~]# docker pull prometheus
[root@localhost ~]# docker pull grafana/grafana
备注:cadvisor是Google开发的用于分析运行中容器的资源占用和性能指标的开源工具。相当于抓取容器的各个指标,把数据整理为prometheus可以识别的格式。
2.新建挂载目录,执行以下命令:
[root@localhost ~]# cd /home/docker/
[root@localhost docker]# mkdir consul prometheus grafana cadvisor
[root@localhost docker]# ls
cadvisor consul django grafana haproxy prometheus redis
3.启动cadvisor,consul,prometheus,grafana容器,执行以下命令:
[root@localhost ~]# docker run -d --name=cadvisor -p 8080:8080 -v /home/docker/cadvisor/:/data google/cadvisor
6333f440eadfb308ed6b50e3228cc84c0f41bd5ddf9d510b3556e414b9ec0879
[root@localhost ~]# docker run -d --name=consul -p 8500:8500 -v /home/docker/consul/:/data consul
f707219663cdfa40b4a1b35baa49f329250b66abd84df24e0bdfb5e345502768
[root@localhost ~]# docker run -d --name=prometheus -p 9090:9090 -v /home/docker/prometheus/:/data prom/prometheus
c880b73dd67a1d6f1b1d9153952c571ef5c42165b2c0f70121f80a250af7ed68
[root@localhost ~]# docker run -d --name=grafana -p 3000:3000 -v /home/docker/grafana/:/data grafana/grafana
1592c72150991051282dcbcf8d3b1128a7a7f9e9e54a8d515ff521c1e913626b
[root@localhost ~]#
4.查看运行的容器
在虚拟机的宿主机,也就是本地电脑的浏览器中分别访问容器的宿主机,也就是虚机的8080,8500,9090,3000端口,结果如下:
1.编写consul的cadvisor服务发现json文件,执行以下命令:
[root@localhost docker]# cd cadvisor/
[root@localhost cadvisor]# vim cadvisor.json
文件内容为:
{
"id": "cadvisor",
"name": "prometheus-cadvisor",
"tags": ["primiary"],
"address": "10.34.132.128",
"port": 8080,
"checks": [
{
"http":"http://10.34.132.128:8080/metrics",
"interval":"15s"
}
]
}
注意: "address"和"http"中的IP时容器宿主机(虚机)的IP。
2.执行以下命令将cadvisor服务注册到consul中去,执行以下命令:
[root@localhost cadvisor]# curl --request PUT --data @cadvisor.json http://10.34.132.128:8500/v1/agent/service/register
注意命令应该在cadvisor.json所在目录下执行。命令中的IP是安装consul容器所在的机器的IP。
如果想删除服务可以执行以下命令:
[root@localhost cadvisor]# curl --request PUT http://10.34.132.128:8500/v1/agent/service/deregister/cadvisor
3.查看结果,注册成功。
1.新建prometheus的配置文件,执行以下命令:
[root@localhost docker]# cd prometheus/
[root@localhost prometheus]# vim prometheus.yml
文件内容为:
# scrape_timeout is set to the global default (10s).
# Alertmanager configuration
alerting:
alertmanagers:
- static_configs:
- targets:
# - alertmanager:9093
# Load rules once and periodically evaluate them according to the global 'evaluation_interval'.
rule_files:
# - "first_rules.yml"
# - "second_rules.yml"
# A scrape configuration containing exactly one endpoint to scrape:
# Here it's Prometheus itself.
scrape_configs:
# The job name is added as a label `job=` to any timeseries scraped from this config.
- job_name: 'prometheus'
# metrics_path defaults to '/metrics'
# scheme defaults to 'http'.
static_configs:
- targets: ['10.34.132.128:9090']
- job_name: 'consul_prometheus'
consul_sd_configs:
- server: '10.34.132.128:8500'
services: []
relabel_configs:
- source_labels: ['__meta_consul_service']
regex: .*prometheus.*
action: keep
2.重启prometheus容器。查看结果,prometheus中已经监控到了consul_prometheus服务:
1.在浏览器中登录grafana。访问10.34.132.128:3000,grafana默认的账户和密码都是admin。
2.登录后添加prometheus数据源。点击Add data source,选择prometheus数据源如图:
3.grafana中对数据源prometheus进行设置。
在setting中设置prometheus数据源的name,url等,设置完毕后要注意保存。在dashboards中可以导入已有的模板。我选择了prometheus 2.0 stats,也可以选择其他的模板,或者自己新建。
至此数据源基本设置完成。
4.grafana查看仪表盘
点击home--》点击prometheus数据源
见证奇迹的时刻!!!!
至此,监控部署完成。
大部分问题使用搜索引擎就可以解决。以下是我在完成整个部署时踩过的一些坑,供大家解决问题时参考,其中在部署过程中已经提及了一些。
[root@localhost redis]# docker run -it --name redis-master -v /home/docker/redis/master/:/data redis /data/redis
这涉及到docker中的CMD和ENTRYPOINT请自行搜索。
在用电脑访问有关端口是,出现失败的情况请关闭虚拟机的防火墙火或对应端口,以及selinux。
这个问题主要是docker run 命令中的 --privileged=true这个参数。具体用法请自行搜索。
这个问题可能因为你在docker守护进程运行时改动docker宿主机的防火墙策略或者是selinux,导致问题出现,解决方法可以重启容器。
我推荐使用IP:port的格式,不要使用localhost:port格式,因为访问时在浏览器中输入的localhost解析的是你安装浏览器的机器,这和配置文件中得localhost不是同一台机器。
一个尚待解决的问题:
使用docker start redis-master 启动redis容器时,可以进入容器,但redis服务并未启动,需要执行redis-server redis.conf才会运行。照理来说,启动容器时redis服务也应该懂,但是并未启动。
如需转载请注明出处!!!