对Docker的命令即组件关系作下梳理,以备不时之需。
子命令分类 | 子命令 |
Docker环境信息 | info,version |
容器生命周期管理 | create,exec,kill,pause,restart,rm,run,start,stop,unpause |
镜像仓库命令 | login,logout,pull,push,search |
镜像管理 | build,images,import,load,rmi,save,tag,commit |
容器运维操作 | attach,export,inspect,port,ps,rename,stats,stop,wait,cp,diff |
系统日志信息 | events,history,logs |
命令关系图:
命令实践:搭建第一个APP Stack
Docker的设计理念是希望用户能够保证一个容器只运行一个进程,即只提供一种服务。然而,对于用户而言,单一容器是无法满足需求的。通常用户需要利用多个容器,分别提供不同的服务,并在不同的容器间互连通信,最后形成一个Docker集群,以实现特定的功能。对于Docker而言,现在已经有了很多优秀的工具来帮助用户搭建和管理集群。下面通过示例搭建一个一台机器上的简化的Docker集群,基于Docker集群构建的应用称为Docker App Stack,即Docker应用栈。
- 获取应用栈各节点所需镜像:
直接使用docker官网hub实在太慢,这里推荐使用DaoCloud的服务,真的好快。
参考文章:http://www.oschina.net/news/57894/daocloud
# docker pull ubuntu
# docker pull django
# docker pull haproxy
# docker pull redis
- 应用栈容器节点启动
启动redis容器
# docker run -it --name redis-master redis /bin/bash
# docker run -it --name redis-slave1 --link redis-master:master redis /bin/bash
# docker run -it --name redis-slave2 --link redis-master:master redis /bin/bash
启动Django容器,即应用
$ sudo docker run -it --name APP1 --link redis-master:db -v ~/Projects/Django/APP1:/usr/src/app django /bin/bash
$ sudo docker run -it --name APP2 --link redis-master:db -v ~/Projects/Django/APP2:/usr/src/app django /bin/bash
启动HAProxy容器
$ sudo docker run -it --name HAProxy --link APP1:APP1 --link APP2:APP2 -p 6301:6301 -v ~/Projects/HAProxy:/tmp haproxy /bin/bash
查看集群启动情况:
$ sudo docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
8d3f339b8bad haproxy "/docker-entrypoint.s" 2 minutes ago Up 2 minutes 0.0.0.0:6301->6301/tcp HAProxy
cb7056eb047b django "/bin/bash" 4 minutes ago Up 3 minutes APP2
929c498fc10b django "/bin/bash" 5 minutes ago Up 5 minutes APP1
da9cbab4a734 redis "docker-entrypoint.sh" 8 minutes ago Up 8 minutes 6379/tcp redis-slave2
238b6931fc76 redis "docker-entrypoint.sh" 17 minutes ago Up 17 minutes 6379/tcp redis-slave1
60be2f9a1f2d redis "docker-entrypoint.sh" 18 minutes ago Up 18 minutes 6379/tcp redis-master
集群示意图:
- 应用栈容器节点配置
Redis-master配置
- 查看对应容器挂载的Volumes目录:
# docker inspect -f "{{.Mounts}}" $(docker ps -a | grep redis-master | awk '{print $1}')
[{28d77df1f0fc827f7e48d9406ffe8321d50b0f641c6fb5648072aa74b38bf765 /var/lib/docker/volumes/28d77df1f0fc827f7e48d9406ffe8321d50b0f641c6fb5648072aa74b38bf765/_data /data local true }]
可以看到宿主机中的"/var/lib/docker/volumes/28d77df1f0fc827f7e48d9406ffe8321d50b0f641c6fb5648072aa74b38bf765/_data"目录对应容器中的"/data"目录。
- 拷贝redis.conf到容器中
# cd `docker inspect -f "{{.Mounts}}" $(docker ps -a | grep redis-master | awk '{print $1}') | awk '{print $2}'`
将一个redis.conf文件拷贝过来
# cp /etc/redis/redis.conf ./
- 启动redis
切换到容器中:
# docker attach `docker ps -a | grep redis-master | awk '{print $1}'`
root@60be2f9a1f2d:/data# ls
redis.conf
将文件拷贝到/usr/loca/bin
创建redis启动需要的目录:
# mkdir -p /var/lib/redis
# mkdir -p /var/log/redis
启动
# redis-server redis.conf
注意:如果slave不能和master建立集群,需要修改redis.conf中的bind ip为容器的实际ip.默认的127.0.0.1可能不行。
确认
# ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.0 20248 3232 ? Ss 07:48 0:00 /bin/bash
root 20 0.0 0.2 33308 9064 ? Ssl 08:16 0:00 redis-server 127.0.0.1:6379
Redis Slave配置
先拷贝redis.conf
# docker inspect -f '{{.Mounts}}' $(docker ps -a | grep redis-slave1 | awk '{print $1}')[{c13c1bd2660a5b25171a8d924a6af7279b23c56ff58cca64d145de9eda9cfa7c /var/lib/docker/volumes/c13c1bd2660a5b25171a8d924a6af7279b23c56ff58cca64d145de9eda9cfa7c/_data /data local true }]
root@ubuntu:/home/zhangxa# cp /etc/redis/redis.conf /var/lib/docker/volumes/c13c1bd2660a5b25171a8d924a6af7279b23c56ff58cca64d145de9eda9cfa7c/_data/
对于从数据库,修改以下几个参数:
# cat redis.conf | grep slave
slaveof master 6379
由于我们使用了--link参数,因此容器能够解析master的IP。我们可以看下docker自动添加了域名:
# cat /etc/hosts
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.17.0.2 master 60be2f9a1f2d redis-master
172.17.0.3 238b6931fc76
最后启动redis-server
# redis-server redis.conf
root@238b6931fc76:/data# ps aux | grep redis
root 21 0.5 0.2 35404 9348 ? Ssl 08:30 0:00 redis-server 127.0.0.1:6379
同理完成另外一个Slave2的配置
测试R
测试Redis数据库配置
在master容器上redis-cli
> set master "aaa"
OK
172.17.0.2:6379> get master
"aaa"
在slave上测试
# redis-cli
127.0.0.1:6379> get master
"aaa"
APP容器节点配置
进入容器APP1
# docker attach $(docker ps -a | grep APP1| awk '{print $1}')
安装python redis模块
# pip install redis
Collecting redis
Downloading redis-2.10.5-py2.py3-none-any.whl (60kB)
100% |████████████████████████████████| 61kB 129kB/s
Installing collected packages: redis
Successfully installed redis-2.10.5
验证安装:
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
>>>
创建webapp
我们在创建容器时已经挂载了宿主机的~/Projects/APP1目录,
在容器内创建app应用。
# cd /usr/src/
root@929c498fc10b:/usr/src# ls
app
root@929c498fc10b:/usr/src# cd app
root@929c498fc10b:/usr/src/app# mkdir dockerweb
root@929c498fc10b:/usr/src/app# cd dockerweb/
root@929c498fc10b:/usr/src/app/dockerweb# django-admin.py startproject redisweb
root@929c498fc10b:/usr/src/app/dockerweb# ls
redisweb
# python manage.py startapp helloworld
root@cb7056eb047b:/usr/src/app/dockerweb/redisweb# ls
helloworld manage.py redisweb
root@cb7056eb047b:/usr/src/app/dockerweb/redisweb#
在对应的宿主机内编写:
# cd dockerweb/redisweb/helloworld/
root@ubuntu:/home/zhangxa/Projects/Django/APP2/dockerweb/redisweb/helloworld# ls
admin.py apps.py __init__.py migrations models.py tests.py views.py
#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+="<br>"
r = redis.Redis(host='db', port=6379, db=0)
info = r.info()
str+=("Set Hi <br>")
r.set('Hi','HelloWorld-APP1')
str+=("Get Hi: %s <br>" % r.get('Hi'))
str+=("Redis Info: <br>")
str+=("Key: Info Value")
for key in info:
str+=("%s: %s <br>" % (key, info[key]))
return HttpResponse(str)
需要注意的是,我们连接redis数据库时,使用了--link参数创建的db连接来代替具体的IP地址;同理对于APP2使用相同的db连接即可。
接着继续编辑redisweb项目的setting.py,加入'helloworld'
# cd ../redisweb/
root@ubuntu:/home/zhangxa/Projects/Django/APP2/dockerweb/redisweb/redisweb# ls
__init__.py __pycache__ settings.py urls.py wsgi.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'helloworld',
]
然后编辑urls.py,引入helloworld的hello视图:
from helloworld.views import hello
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^helloworld$', hello),
]
进入容器的/usr/src/app/dockerweb/redisweb目录完成项目的生成:
# docker attach $(docker ps -a | grep APP2 | cut -d ' ' -f 1)
# python manage.py makemigrations
No changes detected
# python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contenttypes, sessions
Running migrations:
Applying contenttypes.0001_initial... OK
Applying auth.0001_initial... OK
Applying admin.0001_initial... OK
Applying admin.0002_logentry_remove_auto_add... OK
Applying contenttypes.0002_remove_content_type_name... OK
Applying auth.0002_alter_permission_name_max_length... OK
Applying auth.0003_alter_user_email_max_length... OK
Applying auth.0004_alter_user_username_opts... OK
Applying auth.0005_alter_user_last_login_null... OK
Applying auth.0006_require_contenttypes_0002... OK
Applying auth.0007_alter_validators_add_error_messages... OK
Applying auth.0008_alter_user_username_max_length... OK
Applying sessions.0001_initial... OK
另外一个APP也是同样的配置过程。这样就完成了Docker App Stack应用栈的配置。
在启动Web app服务时,可以指定服务器的端口和IP。为了通过HAProxy容器节点接受外网所有的公共IP地址访问,实现均衡负载,需要指定服务器的IP地址和端口。对于APP1使用8001端口,APP2使用8002端口,同时都使用0.0.0.0地址。以APP1为例,启动服务过程如下:
# python manage.py runserver 0.0.0.0:8001
Performing system checks...
System check identified no issues (0 silenced).
March 19, 2017 - 11:56:42
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.
HAProxy节点的配置
# docker inspect -f '{{.Mounts}}' $(docker ps -a | grep HA | cut -d ' ' -f 1)
[{ /home/zhangxa/Projects/HAProxy /tmp true rprivate}]
利用容器启动时挂载的Volume将HAProxy的启动配置文件复制进容器中,在主机的Volume目录~/Projects/HAProxy目录下:
# cd $(docker inspect -f '{{.Mounts}}' $(docker ps -a | grep HA | cut -d ' ' -f 1) | cut -d ' ' -f 2 )
vim haproxy.cfg
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
option redispatch
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
随后,进入容器的/tmp目录下,将配置文件拷贝到HAProxy的工作目录下:
# cp /tmp/haproxy.cfg /usr/local/sbin
root@8d3f339b8bad:/# cd /usr/local/sbin
启动haproxy
# haproxy -f haproxy.cfg
然后在本地主机上就可以通过haproxy的地址进行访问web服务了http://172.17.0.7:6301/helloworld.
还可以在另外主机上通过本地主机IP访问,因此在创建容器时我们暴露了容器的内部端口http://192.168.17.133:6301/helloworld
我们还可以尝试干掉一台APP1后,仍然能够继续通过APP2访问。