在 Ubuntu 18.04 安装 Docker
1 更换国内源
Ubuntu 的软件源配置文件是 /etc/apt/sources.list
。将系统自带的该文件做个备份,将该文件替换为下面内容,即可使用 TUNA 的软件源镜像。
sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak
sudo vim /etc/apt/sources.list
# 默认注释了源码镜像以提高 apt update 速度,如有需要可自行取消注释
deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal main restricted universe multiverse
# deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal main restricted universe multiverse
deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal-updates main restricted universe multiverse
# deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal-updates main restricted universe multiverse
deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal-backports main restricted universe multiverse
# deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal-backports main restricted universe multiverse
deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal-security main restricted universe multiverse
# deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal-security main restricted universe multiverse
# 预发布软件源,不建议启用
# deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal-proposed main restricted universe multiverse
# deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal-proposed main restricted universe multiverse
2 使用 Docker 仓库进行安装
参考文章:Ubuntu Docker 安装 | 菜鸟教程 (runoob.com)
2.1 设置仓库
更新 apt 包索引。
sudo apt-get update
安装 apt 依赖包,用于通过HTTPS来获取仓库:
sudo apt-get install apt-transport-https ca-certificates curl gnupg-agent software-properties-common
添加 Docker 的官方 GPG 密钥:
curl -fsSL https://mirrors.ustc.edu.cn/docker-ce/linux/ubuntu/gpg | sudo apt-key add -
设置稳定版仓库
sudo add-apt-repository \
"deb [arch=amd64] https://mirrors.ustc.edu.cn/docker-ce/linux/ubuntu/ \
$(lsb_release -cs) \
stable"
2.2 安装 Docker Engine-Community
安装最新版本的 Docker Engine-Community 和 containerd
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io
测试 Docker 是否安装成功,输入以下指令:
sudo docker run hello-world
3 搭建一个应用栈
Docker应用栈的结构图:
3.1 获取镜像
sudo docker pull ubuntu
sudo docker pull django
sudo docker pull haproxy
sudo docker pull redis
sudo docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
redis latest bc8d70f9ef6c 19 hours ago 105MB
haproxy latest 397cf3d55fac 31 hours ago 93.8MB
ubuntu latest 7e0aa2d69a15 2 weeks ago 72.7MB
hello-world latest d1165f221234 2 months ago 13.3kB
django latest eb40dcf64078 4 years ago 436MB
3.2 应用栈容器节点启动
启动应用栈节点之前先整理应用栈节点的连接过程:
- 启动 master-redis 容器节点;
- 两个 redis-slave 容器节点启动时连接到 redis-master;
- 三个 APP 容器节点启动时连接到 master-redis;
- HAProxy 容器节点启动时连接到三个 APP 节点。
此外,为了能够从外网访问应用栈,并通过 HAproxy 节点访问应用栈中的 APP,在启动 HAProxy 节点时使用 -p
参数将端口暴露给主机。
启动Redis容器:
sudo docker run -it --name master-redis redis /bin/bash
sudo docker run -it --name redis-slave1 --link master-redis:master redis /bin/bash
sudo docker run -it --name redis-slave2 --link master-redis:master redis /bin/bash
启动Django容器:
sudo docker run -it --name APP1 --link master-redis:db -v ~/Projects/Django/APP1:/usr/src/app django /bin/bash
sudo docker run -it --name APP2 --link master-redis:db -v ~/Projects/Django/APP2:/usr/src/app django /bin/bash
sudo docker run -it --name APP3 --link master-redis:db -v ~/Projects/Django/APP3:/usr/src/app django /bin/bash
启动HAProxy容器:
sudo docker run -it --name HAProxy --link APP1:APP1 --link APP2:APP2 --link APP3:APP3 -p 6301:6301 -v ~/Projects/HAProxy:/tmp haproxy /bin/bash
启动的容器信息可以通过 docker ps
命令查看
3.3 应用栈容器节点配置
1) Redis Master 主数据库容器节点配置
打开一个新的终端,执行如下命令:
sudo docker inspect --format "{{.Mounts}}" master-redis
[{volume 3f240f3bbf6b1228986b9c3095c29347d20bd07175c940224989bea86b8f37cc /var/lib/docker/volumes/3f240f3bbf6b1228986b9c3095c29347d20bd07175c940224989bea86b8f37cc/_data /data local true }]
可知,该volume在主机中的目录为/var/lib/docker/volumes/3240f3bbf6b1228986b9c3095c29347d20bd07175c940224989bea86b8f37cc/_data
,在容器中的目录为/data
。此时可以进入主机的volume目录,利用启动配置 文件模板来创建主数据库的启动配置 文件,执行命令如下:
cd /var/lib/docker/volumes/3f240f3bbf6b1228986b9c3095c29347d20bd07175c940224989bea86b8f37cc/_data
sudo mkdir conf
sudo apt install redis
sudo cp /etc/redis/redis.conf ./conf/redis.conf
sudo vim ./conf/redis.conf
对于Redis的主数据库,需要修改模板文件中的几个参数:
daemonize yes
bind 0.0.0.0
protected-mode no
在主机创建好启动配置文件后,切换到容器中的volume目录,将redis.conf
拷贝到Redis的执行工作目录,然后启动redis服务器:
sudo docker exec -it master-redis /bin/bash
cp ./conf/redis.conf /usr/local/bin
cd /usr/local/bin
redis-server redis.conf
如果无法进入,则先启动下master-redis
,再重新进入一次:
sudo docker start master-redis
若遇到log日志文件的错误:
root@377fc1d8d855:/usr/local/bin# redis-server redis.conf
*** FATAL CONFIG FILE ERROR (Redis 6.2.3) ***
Reading the configuration file, at line 171
>>> 'logfile /var/log/redis/redis-server.log'
Can't open the log file: No such file or directory
执行以下操作:
mkdir /var/log/redis
touch /var/log/redis/redis-server.log
mkdir /var/run/redis
touch /var/run/redis/redis-server.pid
mkdir /var/lib/redis
chmod 777 /var/log/redis/redis-server.log
chmod 777 /var/run/redis/redis-server.pid
redis-server redis.conf
2) Redis Slave 从数据库容器节点的配置
打开一个新的终端,执行如下命令:
sudo docker inspect --format "{{.Mounts}}" redis-slave1
[{volume d9b1cd86593167232327cd088ea37d4051a329c37e013780d56979166b3336f2 /var/lib/docker/volumes/d9b1cd86593167232327cd088ea37d4051a329c37e013780d56979166b3336f2/_data /data local true }]
cd /var/lib/docker/volumes/d9b1cd86593167232327cd088ea37d4051a329c37e013780d56979166b3336f2/_data
sudo mkdir conf
sudo cp /etc/redis/redis.conf ./conf/redis.conf
sudo vim ./conf/redis.conf
对于Redis的从数据库,需要修改模板文件中的几个参数:
daemonize yes
replicaof master 6379
protected-mode no
# bind 127.0.0.1 ::1
# 不注释的话,启动redis时,会报错warning: Could not create server TCP listening socket ::1:6379: bind: Cannot assign requested address Failed listening on port 6379 (TCP),aborting.
sudo docker start redis-slave1
sudo docker exec -it redis-slave1 /bin/bash
cp ./conf/redis.conf /usr/local/bin
cd /usr/local/bin
mkdir /var/log/redis
touch /var/log/redis/redis-server.log
mkdir /var/run/redis
touch /var/run/redis/redis-server.pid
mkdir /var/lib/redis
chmod 777 /var/log/redis/redis-server.log
chmod 777 /var/run/redis/redis-server.pid
redis-server redis.conf
同理,可以完成对另一个Redis Slave 容器节点的配置。
3) Redis 数据库容器节点的测试
通过启动Redis的客户端程序来测试数据库。
首先在 Redis Master 容器内(即切换到配置Redis Master主数据库容器节点时打开的终端)启动Redis的客户端程序,并存储一个数据:
apt-get update && apt-get install procps
# redis-cli
127.0.0.1:6379> set master master-redis
OK
127.0.0.1:6379> get master
"master-redis"
随后在两个 Redis Slave 容器内(即切换到配置Redis Slave从数据库容器节点时打开的终端)分别启动 Redis 的客户端程序,查询先前在 Master 数据库中存储的数据:
apt-get update && apt-get install procps
# redis-cli
127.0.0.1:6379> get master
"master-redis"
PS:如果连接不上,可能时Redis没有启动成功,请去容器内查看日志文件
/var/log/redis/redis-server.log
4) APP 容器节点 (Django) 的配置
Django容器启动后,需要利用Django框架开发一个简单的Web程序。为了访问数据库,需要在容器中安装Python的Redis支持包:
sudo docker start APP1
sudo docker exec -it APP1 /bin/bash
pip install redis
安装完成后,验证支持包是否安装成功:
root@4c6475830e31:/# 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
在容器的volume目录下/usr/src/app下,开始创建APP:
cd /usr/src/app
mkdir dockerweb
cd dockerweb
django-admin.py startproject redisweb
cd redisweb
python manage.py startapp helloworld
在容器中创建APP后,切换到宿主机的volume目录~/Projects/Django/App1
下,进行相应的编辑来配置App:
cd ~/Projects/Django/APP1
ls
可以看到,在容器内创建的APP文件在宿主机的volume目录下同样可见。
修改helloword应用的视图文件views.py:
cd dockerweb/redisweb/helloworld
ls
sudo 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连接即可。
接下来修改项目的配置文件settings.py
,如下:
sudo vim ../redisweb/settings.py
在settings.py文件中的 INSTALLED_APPS 选项下添加 helloworld:
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,它将设置访问应用的 URL 模式,并为 URL 模式调用的视图函数之间的映射表:
sudo vim ../redisweb/urls.py
修改后的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
下完成项目的生成:
cd /usr/src/app/dockerweb/redisweb
python manage.py makemigrations
python manage.py migrate
至此所有APP1容器的配置已经完成,APP2和APP3容器的配置也是同样的过程,只需要稍作修改即可。在配置完成APP1、APP2和App3容器后,就完成了应用栈的APP部分的全部配置。
在启动 APP 容器的 Web 服务器时,可以指定服务器的端口和 IP 地址。为了通过 HAproxy 容器节点接受外网所有的公共IP地址访问,实现负载均衡,需要指定服务器的IP地址和端口。对于APP1使用 8001 端口,而APP2使用 8002 端口,APP3使用 8003 端口,同时都使用0.0.0.0 地址。以APP1为例,启动服务器的过程如下:
python manage.py runserver 0.0.0.0:8001
5) HAproxy 容器节点配置
所有对应用栈的访问均通过HAproxy负载均衡代理容器节点实现负载均衡。
首先,在宿主机的volume目录~/Projects/HAProxy/
下:
sudo docker start HAProxy
cd ~/Projects/HAProxy/
sudo 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
server APP3 APP3:8003 check inter 2000 rise 2 fall 5
随后进入容器的 volume 目录 /tmp 下,将 Haproxy 的启动配置文件复制到 HAproxy 的工作目录中:
sudo docker exec -it HAProxy /bin/bash
cd /tmp
cp haproxy.cfg /usr/local/sbin
cd /usr/local/sbin
ls
接下来利用该配置文件来启动 HAProxy 代理:
haproxy -f haproxy.cfg
应用栈访问测试
sudo docker inspect --format='{{.NetworkSettings.IPAddress}}' HAProxy
172.17.0.8
在浏览器中访问http://172.17.0.8:6301/helloworld,可以看到APP1、APP2或APP3的页面:
本地测试通过后,尝试在其他主机上通过应用栈入口地址的 IP地址 和 6301端口 访问应用栈APP,即http://192.168.56.129:6301/helloworld,如下图所示:
需要注意的是,如果修改了配置文件的内容,需要先结束所有的HAProxy进程,并重新启动代理。用户可以使用killall命令来结束进程,如果镜像中没有安装该命令,则需要先安装psmisc包:
apt install psmisc
killall haproxy
至此完成了HAProxy容器节点的全部部署,同时也完成了整个Docker应用栈的部署。
4 实现私有云
4.1 启动 Docker
service docker start
4.2 获取镜像
wget http://download.openvz.org/template/precreated/ubuntu-14.04-x86_64-minimal.tar.gz
sudo cat ubuntu-14.04-x86_64-minimal.tar.gz | sudo docker import - ubuntu:base
sudo docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu base 4320e4a78591 About an hour ago 215MB
4.3 实现 SSHD,在 Base 镜像的基础上生成一个新镜像
sudo docker run -t -i ubuntu:base /bin/bash
vim /etc/apt/sources.list
deb http://mirrors.163.com/ubuntu/ trusty main restricted universe multiverse
deb http://mirrors.163.com/ubuntu/ trusty-security main restricted universe multiverse
deb http://mirrors.163.com/ubuntu/ trusty-updates main restricted universe multiverse
deb http://mirrors.163.com/ubuntu/ trusty-proposed main restricted universe multiverse
deb http://mirrors.163.com/ubuntu/ trusty-backports main restricted universe multiverse
deb-src http://mirrors.163.com/ubuntu/ trusty main restricted universe multiverse
deb-src http://mirrors.163.com/ubuntu/ trusty-security main restricted universe multiverse
deb-src http://mirrors.163.com/ubuntu/ trusty-updates main restricted universe multiverse
deb-src http://mirrors.163.com/ubuntu/ trusty-proposed main restricted universe multiverse
deb-src http://mirrors.163.com/ubuntu/ trusty-backports main restricted universe multiverse
安装supervisor服务:
apt update
apt install supervisor
cd /etc/supervisor/
cp supervisord.conf conf.d/
cd conf.d/
vim supervisord.conf
; supervisor config file
[unix_http_server]
file=/var/run/supervisor.sock ; (the path to the socket file)
chmod=0700 ; sockef file mode (default 0700)
[supervisord]
logfile=/var/log/supervisor/supervisord.log ; (main log file;default $CWD/supervisord.log)
pidfile=/var/run/supervisord.pid ; (supervisord pidfile;default supervisord.pid)
childlogdir=/var/log/supervisor ; ('AUTO' child log dir, default $TEMP)
nodaemon = true;
; the below section must remain in the config file for RPC
; (supervisorctl/web interface) to work, additional interfaces may be
; added by defining them in separate rpcinterface: sections
[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
[supervisorctl]
serverurl=unix:///var/run/supervisor.sock ; use a unix:// URL for a unix socket
; The [include] section can just contain the "files" setting. This
; setting can list multiple files (separated by whitespace or
; newlines). It can also contain wildcards. The filenames are
; interpreted as relative to this file. Included files *cannot*
; include files themselves.
[include]
files = /etc/supervisor/conf.d/*.conf
[program:sshd]
command = /usr/sbin/sshd -D;
mkdir /var/run/sshd
passwd root
vim /etc/ssh/sshd_config
head -1 /proc/self/cgroup|cut -d/ -f3|cut -c1-12 # 查看自身容器id
0da52602ee6b # 输出值
exit
退出后自动生成一个容器,接下来把容器 commit 生成封装了 SSHD 的镜像。
sudo docker commit 0da52602ee6b ubuntu:sshd
sha256:4f86a61e6fca0d9908e882a13afdd3a9d2724bfd135c355367af2b09630458ec # 输出值
# sudo docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu sshd 4f86a61e6fca 2 minutes ago 215MB
ubuntu base 4320e4a78591 About an hour ago 215MB
4.4 开始分配容器
sudo docker run -p 301:22 -d --name test ubuntu:sshd /usr/bin/supervisord
sudo docker run -p 302:22 -d --name dev ubuntu:sshd /usr/bin/supervisord
sudo docker run -p 303:22 -d --name client1 ubuntu:sshd /usr/bin/supervisord
...
sudo docker run -p xxxxx:22 -d --name clientN ubuntu:sshd /usr/bin/supervisord
这样就隔离了N个容器,且每一个都是以 Ubuntu 为中心的纯净的 Ubuntu 系统,按这种分配方式,所有容器的性能和宿主机一样。
4.5 搭建自己的私有仓库
服务的封装是 Docker 的“杀手锏”,用户可以搭建自己的私有仓库。
参考资料:
《云计算导论(第2版)》