在Docker中,镜像是容器的基础,可以通过镜像来运行容器。本文将介绍如何用Docker Hub中已有的镜像资源来搭建一个Docker应用栈。
在开始搭建过程前,对应用栈进行一个介绍:我们将搭建一个包含6个节点的Docker应用栈,其中包括一个代理节点、两个Web应用节点、一个祝数据库节点以及两个从数据库节点。
应用栈结构图如下:
其中,HAProxy是负载均衡的代理节点;Redis是非关系型的数据库,它由一个主数据节点和两个从数据节点构成,App是应用(这里使用Python语言、基于Django架构设计一个访问数据库的基础Web应用)。
在搭建过程中,可以从Docker Hub获取现有可用的镜像,在这些镜像的基础上启动容器,按照需求进行修改来实现既定的功能。
在这个过程中,需要好好体会Docker的高可移植性所带来的便利。
根据应用栈的描述,使用下面几个命令来获取HAProxy、Redis、Django的镜像。
docker pull ubuntu
docker pull django
docker pull haproxy
docker pull redis
查看下载的镜像
[root@master ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu latest cf0f3ca922e0 14 hours ago 64.2MB
haproxy latest c09d2530df01 2 days ago 91.2MB
redis latest de25a81a5a0b 2 days ago 98.2MB
django latest eb40dcf64078 2 years ago 436MB
我们搭建的应用栈是在同一个主机下搭建的。
然而,如果是一个真正的分布式架构集群,还需要处理容器的跨主机通信问题,但是我们先在本文不表,以后再做介绍。
鉴于在同一个主机下搭建的容器应用栈环境,只需要完成容器间的互联来实现容器间的通信即可。
这里采用docker run 命令的–link选项建立容器间的互联关系。介绍一下--link
选项的用法,通过--link
选项能够进行容器间安全的交互通信,这样做的好处是 不但可以避免容器的Ip和端口暴露到外网所导致的安全问题,还可以防止容器在重启后IP地址变化导致的访问失败 ,它的原理类似于DNS服务器的域名和地址映射,当容器IP地址发生变化的时候,Docker将自动维护映射关系中的Ip地址。
使用示例如下:
docker run --link redis:redis --name console ubuntu bash
通过--link name:alias
实现容器间的交互通信,上面的例子是在ubuntu镜像上启动一个容器,并命名为console,同时将新启动的console容器连接到名为redis的容器上。
当然了,现在还不能直接使用上面那行命令,因为容器还没有创建,我们只是下载了镜像而已。上面只是举例子,我们应用栈的连接信息如下:
综上所示,只需要把把HAProxy容器节点利用-p参数暴露端口给主机,即可通过主机Ip加暴露的端口从外网访问应用栈。
命令如下:
# 启动redis
docker run -itd --name redis-master redis /bin/bash
docker run -itd --name redis-slave1 --link redis-master:master redis /bin/bash
docker run -itd --name redis-slave2 --link redis-master:master redis /bin/bash
-i
开启了input(输入)功能,也称交互模式,始终保持输入流开放。
-t
开启了一个连接容器里边的terminal(伪终端)。
-d
是detach,让/bin/bash在后台运行,否则就会进入到容器。
--name
就是命名容器,命名为redis-master之类的。
/bin/bash
这是表示载入容器后运行bash ,docker中必须要保持一个进程的运行,要不然整个容器就会退出。这个就表示启动容器后启动bash。
# 启动Django容器,即App应用
docker run -itd --name APP1 --link redis-master:db -v ~/Projects/Django/APP1:/usr/src/app django /bin/bash
docker run -itd --name APP2 --link redis-master:db -v ~/Projects/Django/APP2:/usr/src/app django /bin/bash
-v
用于挂载一个volume,可以用多个-v参数同时挂载多个volume。volume的格式为[host-dir]:[container-dir]:[rw|ro]
。
# 启动HAProxy容器
docker run -itd --name HAProxy --link APP1:APP1 --link APP2:APP2 -p 6301:6301 -v ~/Projects/HAProxy:/tmp haproxy /bin/bash
-p
用于将容器的端口暴露给主机的端口,其格式为hostPort:containerPort
。
此外还有一些比较重要的命令:
-c
用于给运行在容器中所有进程分配CPU的shares值,这是一个相对权重,实际的处理速度还与宿主机的CPU有关。
-m
用于限制为容器中所有进程分配的内存总量,以B、K、M、G为单位。
[root@master ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
d95abf1e230b haproxy "/docker-entrypoint.…" 6 seconds ago Up 6 seconds 0.0.0.0:6301->6301/tcp HAProxy
c995657c55b9 django "/bin/bash" About a minute ago Up About a minute APP2
38a8c69511ea django "/bin/bash" 2 minutes ago Up 2 minutes APP1
a48912a7ef3a redis "docker-entrypoint.s…" 8 minutes ago Up 8 minutes 6379/tcp redis-slave2
6048f74ebc6b redis "docker-entrypoint.s…" 8 minutes ago Up 8 minutes 6379/tcp redis-slave1
1d6630c27d29 redis "docker-entrypoint.s…" 11 minutes ago Up 11 minutes 6379/tcp redis-master
通过attach [container id]
进入容器,可以查看/etc/hosts文件。
[root@master ~]# docker attach a48912a7ef3a
root@a48912a7ef3a:/data# 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 1d6630c27d29 redis-master
172.17.0.4 a48912a7ef3a
如果要正常退出不关闭容器,请按Ctrl+P+Q进行退出容器。
在应用栈的各个容器节点都启动后,需要对他们进行配置和修改,以便实现特定的功能和通信协作,下面按照容器启动顺序依次进行解释。
注意:上面启动的只是容器节点,并不是启动的Redis或者Django本身。
由于容器轻量化的设计,其中缺乏相应的文本编辑命令工具,如下所示。
[root@master ~]# docker attach redis-master
root@1d6630c27d29:/data# vi /etc/hosts
bash: vi: command not found
这时候,我们使用-v
参数的目的就出现了。利用-v参数挂载volume,在主机和容器间共享数据,这样就可以直接在主机上创建和编辑相关的文件。
在利用Redis镜像启动容器时,镜像中已经集成了volume的挂载命令。所以我们需要通过docker inspect
命令来查看所挂载的volume的情况。
可以执行如下命令:
docker inspect container_name | grep Mounts -A 10
或者
docker inspect container_id | grep Mounts -A 10
注意大小写。
grep mounts表示过滤(grep)掉其他信息,保留挂载(Mounts)信息 -A 10
10行。
我执行的命令如下:
[root@master ~]# docker inspect 1d6630c27d29 | grep Mounts -A 10
"Mounts": [
{
"Type": "volume",
"Name": "6832a825182e926c630ee7229395a7e43fcfc7a7ccaa56e4c77a8c2ac4fdb2f7",
"Source": "/var/lib/docker/volumes/6832a825182e926c630ee7229395a7e43fcfc7a7ccaa56e4c77a8c2ac4fdb2f7/_data",
"Destination": "/data",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
}
]
我们看到容器地址/data
映射的主机地址为/var/lib/docker/volumes/6832a825182e926c630ee7229395a7e43fcfc7a7ccaa56e4c77a8c2ac4fdb2f7/_data
所以,进入主机的上述目录
[root@master ~]# cd /var/lib/docker/volumes/6832a825182e926c630ee7229395a7e43fcfc7a7ccaa56e4c77a8c2ac4fdb2f7/_data
[root@master _data]# vi redis.conf
搜索redis对应版本的默认配置,可以直接复制到redis.conf中。
并修改以下几项:
# 注释这一行,表示Redis可以接受任意ip的连接
# bind 127.0.0.1
# 关闭保护模式
protected-mode no
# 让redis服务后台运行
daemonize yes
# 设定密码(可选,如果这里开启了密码要求,slave的配置里就要加这个密码. 只是练习配置,就不使用密码认证了)
# requirepass masterpassword
在主机创建好启动配置文件后,切换到容器中的volume目录,并复制启动配置文件到Redis的执行工作目录,然后启动Redis服务器,执行过程如下:
[root@master _data]# docker attach redis-master
root@1d6630c27d29:/data# ls
redis.conf
root@1d6630c27d29:/data# cp redis.conf /usr/local/bin
root@1d6630c27d29:/data# cd /usr/local/bin
root@1d6630c27d29:/usr/local/bin# ls
docker-entrypoint.sh redis-check-aof redis-sentinel stdout
gosu redis-check-rdb redis-server
redis-benchmark redis-cli redis.conf
root@1d6630c27d29:/usr/local/bin# redis-server redis.conf
30:C 19 Oct 2019 11:46:48.049 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
30:C 19 Oct 2019 11:46:48.049 # Redis version=5.0.6, bits=64, commit=00000000, modified=0, pid=30, just started
30:C 19 Oct 2019 11:46:48.049 # Configuration loaded
同样的,也是找到挂载文件的位置,创建并修改redis.conf。
与master不同的是,redis的从数据库需要修改如下几个参数:
# 注释这一行,表示Redis可以接受任意ip的连接
# bind 127.0.0.1
# 关闭保护模式
protected-mode no
# 让redis服务后台运行
daemonize yes
# 设定密码(可选,如果这里开启了密码要求,slave的配置里就要加这个密码)
requirepass masterpassword
# 设定主库的密码,用于认证,如果主库开启了requirepass选项这里就必须填相应的密码
masterauth
# 设定master的IP和端口号,redis配置文件中的默认端口号是6379
# 低版本的redis这里会是slaveof,意思是一样的,因为slave是比较敏感的词汇,所以在redis后面的版本中不在使用slave的概念,取而代之的是replica
# 将35.236.172.131做为主,其余两台机器做从。ip和端口号按照机器和配置做相应修改。
replicaof master 6379
即如果master不设置密码,那么直接在slave服务器配置replicaof(老版本是slaveof)即配置如下replicaof masterip port
。
这样设置的原因在于,对于masterip使用了–link参数设置的连接名来代替实际的IP地址。通过连接名互联通信时,容器会自动读取它的host信息,将连接名转换为实际IP地址。
在Redis-master容器内,启动redis的客户端程序,并存储一个数据,执行过程如下:
[root@master _data]# docker attach redis-master
root@1d6630c27d29:/usr/local/bin# ls
docker-entrypoint.sh redis-benchmark redis-cli redis.conf
dump.rdb redis-check-aof redis-sentinel stdout
gosu redis-check-rdb redis-server
root@1d6630c27d29:/usr/local/bin# redis-cli
127.0.0.1:6379> set mastername "leesangHyuk"
OK
127.0.0.1:6379> get mastername
"leesangHyuk"
随后在两个Slave里面查询数据:
[root@master _data]# docker attach redis-slave1
root@6048f74ebc6b:/usr/local/bin# redis-cli
127.0.0.1:6379> get mastername
"leesangHyuk"
同时也可以使用下面的命令查询连接情况:
redos-cli -p 6379 info
更多redis配置,请参考Reids在Docker下的配置。