docker入门(一):MacOS上搭建docker简单应用栈(

2019-09-29  本文已影响0人  alexlee666

一、What & why docker?

1.1 docker是什么?

1.2 为什么使用docker?

docker之所以流行,在于它能够较好地解决目前软件行业的痛点,比如:

docker能够较好的避免这些问题。


二、docker应用栈简单实例

2.1 系统环境和docker版本信息


$ sw_vers
ProductName:    Mac OS X
ProductVersion: 10.13.6
BuildVersion:   17G65



$ docker version
Client: Docker Engine - Community
 Version:           18.09.2
 API version:       1.39
 Go version:        go1.10.8
 Git commit:        6247962
 Built:             Sun Feb 10 04:12:39 2019
 OS/Arch:           darwin/amd64
 Experimental:      false

Server: Docker Engine - Community
 Engine:
  Version:          18.09.2
  API version:      1.39 (minimum version 1.12)
  Go version:       go1.10.6
  Git commit:       6247962
  Built:            Sun Feb 10 04:13:06 2019
  OS/Arch:          linux/amd64
  Experimental:     false


2.2 简单应用栈系统架构

本文将要在docker中搭建一个基于Django架构设计的访问redis数据库的Web应用,该应用由1个网络代理节点、2个Web应用节点、1个redis主数据库节点以及2个redis从数据库节点组成,如图2-1所示。

图2-1. 基于Django架构设计的访问redis数据库的Web应用

各组件的角色如下:

为了提升网站的性能,使用基于缓存的key-value数据库redis,redis采用主从架构以提高可靠性;ddjango-APP1和APP2访问redis数据库,来自浏览器的网络请求通过haproxy转发给django-APP1和APP2,然后响应请求。


2.3 搭建docker应用栈

搭建和测试过程大体上分为4步完成:

2.3.1 拉取镜像image

$ docker pull ubuntu
$ docker pull haproxy   
$ docker pull redis
$ docker pull django

$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
haproxy             latest              0b132e159de1        12 days ago         72.2MB
redis               latest              d3e3588af517        3 weeks ago         95MB
ubuntu              latest              7698f282e524        3 weeks ago         69.9MB
django              latest              eb40dcf64078        2 years ago         436MB

解释:

2.3.2 节点互联和节点启动

按照顺序依次启动各节点:redis-master -> redis-slave -> django -> haproxy。

# docker rm redis-master 用来移除已有的容器
$ 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
$ docker run -it --name APP1 --link redis-master:db -v ~/Projects/Django/APP1:/usr/src/app django /bin/bash
$ docker run -it --name APP2 --link redis-master:db -v ~/Projects/Django/APP2:/usr/src/app django /bin/bash
$ docker run -it --name HAProxy --link APP1:APP1 --link APP2:APP2 -p 6301:6301 -v ~/Projects/HAProxy:/tmp haproxy /bin/bash

解释 - 以 docker run -it --name APP1 --link redis-master:db -v ~/Projects/Django/APP1:/usr/src/app django /bin/bash 为例:

注:1个terminal中执行1个docker run命令,因为一旦docker run执行成功便进入到了该容器的工作目录。

列出处于running状态的docker容器:


$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                    NAMES
2b0601b91e9f        haproxy             "/docker-entrypoint.…"   6 days ago          Up 3 days           0.0.0.0:6301->6301/tcp   HAProxy
9ca81c5b0b20        django              "/bin/bash"              6 days ago          Up 3 days                                    APP2
5eb3aa0300d9        django              "/bin/bash"              6 days ago          Up 3 days                                    APP1
2649504f9b3c        redis               "docker-entrypoint.s…"   6 days ago          Up 3 days           6379/tcp                 redis-slave2
3b19ea1de6a0        redis               "docker-entrypoint.s…"   6 days ago          Up 3 days           6379/tcp                 redis-slave1
0bcfcd4dfc2b        redis               "docker-entrypoint.s…"   6 days ago          Up 3 days           6379/tcp                 redis-master

注:列出所有的容器使用命令:docker ps -a。

2.3.3 配置节点

接下来就可以完成容器中如redis和haproxy等服务的配置,按照顺序来依次完成配置:redis-master -> redis-slave -> django -> haproxy。

2.3.3.1 redis-master配置

该部分需要修改redis-master的配置文件redis.conf,并使用该配置来启动redis-master服务。
首先连接到redis-master容器并进入redis-server所在的目录:

$ docker attach redis-master
You cannot attach to a stopped container, start it first
LMA23004071M:~ ycaha$ docker restart redis-master
redis-master
LMA23004071M:~ ycaha$ docker attach redis-master
root@0bcfcd4dfc2b:/data# cd /usr/local/bin
root@0bcfcd4dfc2b:/usr/local/bin#
root@0bcfcd4dfc2b:/usr/local/bin# redis-server redis.conf

注:默认情况下,/usr/local/bin这个目录下不存在redis.conf,因此需要创建一个redis.conf。然而redis组件不包含vim工具,因此需要从该容器对应的host挂载volume目录中创建redis.conf,那么该容器工作目录/data中就会自动同步redis.conf。但是由于MacOS的特殊性,导致host中不存在目录/var/lib/docker/,因此该方法也不行。
最后找到一个方法:可以先在host上编辑redis.conf文件,并使用docker命令直接copy redis.conf到容器工作目录/data中,然后进入该容器/data目录并将该文件copy到/usr/local/bin目录:

redis.conf文件可以从官网下载:https://github.com/antirez/redis/
在host上修改redis.conf,添加如下内容:

daemonize yes
pidfile /var/run/redis.pid
logfile "/usr/local/bin/redis.log"

使用docker命令将host上的redis.conf copy到容器目录中并启动redis服务:

$ docker cp ~/Desktop/redis.conf redis-master:/data
$ docker attach redis-master
root@0bcfcd4dfc2b:/data# cp redis.conf /usr/local/bin
root@0bcfcd4dfc2b:/data# cd /usr/local/bin
root@0bcfcd4dfc2b:/usr/local/bin# redis-server redis.conf

2.3.3.2 redis-slave1 和redis-slave2 配置

redis-master中的redis服务启动后,可以开始配置redis-slave1和redis-slave2:
在host上修改redis.conf,添加如下内容:

daemonize yes
pidfile /var/run/redis.pid
slaveof master 6379

使用docker命令将host上的redis.conf copy到各自容器目录中并启动redis服务:

$ docker cp ~/Desktop/redis.conf redis-slave1:/data
$ docker cp ~/Desktop/redis.conf redis-slave2:/data
$ docker attach redis-slave1
root@3b19ea1de6a0:/data# cp redis.conf /usr/local/bin
root@3b19ea1de6a0:/data# cd /usr/local/bin
root@3b19ea1de6a0:/data#/usr/local/bin# redis-server redis.conf

# 另起一个terminal

$ docker attach redis-slave2
root@2649504f9b3c:/data# cp redis.conf /usr/local/bin
root@2649504f9b3c:/data# cd /usr/local/bin
root@2649504f9b3c:/data#/usr/local/bin# redis-server redis.conf

2.3.3.3 测试redis-master和redis-slave1 & redis-slave2数据是否同步

向redis-master中启动一个redis进程,插入一条record,然后check 该record是否被同步到了redis-slave1和redis-slave2中?

首先在redis-master容器中启动redis进程并开启一个redis-client,然后插入一条record:

root@0bcfcd4dfc2b:/usr/local/bin# redis-server redis.conf
root@0bcfcd4dfc2b:/usr/local/bin# redis-cli
127.0.0.1:6379> set master1 616
OK

然后在redis-slave1和redis-slave2中查看该record是否同步:

$ docker attach redis-slave1
root@3b19ea1de6a0:/data# cd /usr/local/bin
root@3b19ea1de6a0:/usr/local/bin# redis-server redis.conf 
root@3b19ea1de6a0:/usr/local/bin# redis-cli
127.0.0.1:6379> get master1
"616"

如果同步成功表明redis数据库节点搭建和配置完成,接下来配置django框架和haproxy网络代理部分。

2.3.3.4 django-APP1和django-APP2配置

django容器启动后,可以开始基于python编写的django框架创建Web application,该application需要访问redis,因此需要在django容器中安装redis模块并check redis模块是否安装成功:

$ docker attach APP1
root@5eb3aa0300d9:/usr/src/app/dockerweb/redisweb# pip install redis
root@5eb3aa0300d9:/usr/src/app/dockerweb/redisweb# 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.)
redis.AuthenticationError(         redis.__getattribute__(
......

按照如上方法,在django-APP2容器中安装redis模块。

接下来开始在django-APP1中创建application helloworld:
首先进入该容器的工作目录/usr/src/app(在2.3.2中配置的)并创建application:
注:django中一个project中可以包含多个application,django使用django-admin.py来完成project的创建,使用manage.py 来完成application的创建,关于django-admin.py和manage.py 的详细用法可以参考:https://blog.csdn.net/weixin_42134789/article/details/80753001

# cd /usr/src/app
# mkdir dockerweb
# cd dockerweb
# django-admin.py startproject redisweb
# ls
redisweb 
# cd redisweb
# ls
manage.py  redisweb
# python manager.py startapp helloworld
# ls
helloworld  manage.py  redisweb

在django-APP1中创建application helloworld后,进入host的挂载volume目录~/Projects/Django/App1:

# cd ~/Projects/Django/App1
# ls
dockerweb

可以看到,在django-APP1容器内创建的application helloworld已经被同步到了host的volume目录下。
在django中,一个视图函数(类),简称视图,是一个简单的Python 函数(类),它接受Web请求并且返回Web响应。在这里我们需要修改helloworld应用的视图类,为此直接然后修改helloword应用的视图文件views.py:

# cd dockerweb/redisweb/helloworld
# ls
admin.py  __init__.py  migrations  models.py  tests.py  views.py
# vim views.py

# 修改后的views.py文件如下:
from django.shortcuts import render
from django.http import HttpResponse

# Create your views here.
import redis

def hello(requset):
    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项目的配置文件setiing.py,添加新建的helloworld应用:

# cd ../redisweb
# ls
__init__.py  __pycache__  settings.py  urls.py  wsgi.py

# 在setting.py文件中的INSTALLED_APPS选项下添加helloworld:
# 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,它将设置访问应用的URL模式,并为URL模式调用的视图函数之间的映射表。在url.py文件中,引入helloworld应用的hello视图,并为hello视图添加一个urlpatterns变量。修改后的urls.py文件如下:

from django.conf.urls import * 
from django.contrib import admin
admin.autodiscover()
from helloworld.views import hello

urlpatterns = [ 
   url(r'^admin/', include(admin.site.urls)),
   url(r'^helloworld$', hello),
]

以上修改完成后,再次进入容器,在目录/usr/src/app/dockerweb/redisweb下生成项目:

# python manage.py makemigrations
No changes detected
# python manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, sessions
Running migrations:
  Rendering model states... DONE
  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

至此django-APP1的配置完成,django-APP2可以按照上述步骤配置,只是在编辑views.py需要将hello函数中的r.set('Hi', 'HelloWorld-APP1')修改为r.set('Hi', 'HelloWorld-APP2')即可。

2.3.3.5 haproxy配置

在启动APP容器的Web服务器时,可以指定服务器的端口和IP地址,为了通过HAproxy容器节点接受外网所有的公共IP地址访问,实现负载均衡,需要指定服务器的IP地址和端口。对于APP1使用8001端口,而APP2使用8002端口,同时,都使用0.0.0.0地址。以APP1为例,启动服务器的过程如下:

# python manage.py runserver 0.0.0.0:8001
# python manage.py runserver 0.0.0.0:8001
Performing system checks...

System check identified no issues (0 silenced).
September 20, 2016 - 23:16:44
Django version 1.10, using settings 'redisweb.settings'
Starting development server at http://0.0.0.0:8001/
Quit the server with CONTROL-C.

HAproxy容器节点配置

所有对应用栈的访问均通过HAproxy负载均衡代理容器节点实现负载均衡。
首先,将HAProxy的启动配置我呢间复制到容器中,在宿主机的volumes目录~/Projects/HAProxy/下:

# cd ~/Projects/HAProxy/
# vim haproxy.cfg

修改后的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
    stats auth phil:NRG93012
        server APP1 APP1:8001 check inter 2000 rise 2 fall 5
        server APP2 APP2:8002 check inter 2000 rise 2 fall 5

随后,进入容器的volume目录/tmp下,将Haproxy的启动配置文件复制到HAproxy的工作目录:

# cd /tmp
# cp haproxy.cfg /usr/local/sbin
# cd /usr/local/sbin
# ls
haproxy  haproxy-systemd-wrapper  haproxy.cfg

然后,利用配置文件启动HAProxy代理:

# haproxy -f haproxy.cfg

三、常见问题和解决方案

Q1. 在2.3.2 节点互联和节点启动中,redis-master、redis-slave1以及redis-slave2均没有指定挂载的volume,如何查看默认的持久化目录?为什么提示找不到该持久化目录?

A1: 使用docker inspect redis-master可以查看redis-master这个容器的详细信息,其中就包括持久化方面的配置:


......
"Mounts": [
            {
                "Type": "volume",
                "Name": "ca6e59825eba5475e7473cb198906f7537cba2ae760ac83da3d10564df27b64b",
                "Source": "/var/lib/docker/volumes/ca6e59825eba5475e7473cb198906f7537cba2ae760ac83da3d10564df27b64b/_data",
                "Destination": "/data",
                "Driver": "local",
                "Mode": "",
                "RW": true,
                "Propagation": ""
            }
        ],
......

其中 "Destination": "/data"指明容器工作目录是/data,"Source": "/var/lib/docker/volumes/ca6e59825eba5475e7473cb198906f7537cba2ae760ac83da3d10564df27b64b/_data"指明了该容器对应的宿主机持久化目录,但是查看该目录却发现该目录不存在。


$ cd /var/lib/docker/
-bash: cd: /var/lib/docker/: No such file or directory

这是由于MacOS和Linux的差异导致的,解决方法可以参考:
https://www.jianshu.com/p/f0ad94ba8ba4

Q2: redis容器目录中找不到redis.conf。
A2: 自己创建一个。

Q3: 在使用docker run命令启动容器时可能会遇到如下问题:

docker: Error response from daemon: Conflict. The container name "/redis-slave1" is already in use by container ......

A3: 移除该容器,并重新启动该容器,比如:


docker rm redis-slave1
docker run -it --name redis-slave1 --link redis-master:master redis /bin/bash 


参考:https://www.jianshu.com/p/2a873474e976

上一篇 下一篇

猜你喜欢

热点阅读