一 应用场景描述

现在需要搭建一个内部邮件服务器用于接收zabbix报警信息,邮件服务器选用iRedmail开源邮件系统。但是由于可用服务器不多,邮件服务器的使用压力也不大,于是考虑在一台空闲的物理机上安装KVM虚拟机或者Docker容器的方式来部署iRedmail邮件服务器。考虑到目前Docker容器很流行的,使用KVM虚拟机还要配置和管理KVM虚拟机,不如使用Docker容器来得方便。于是就有了这篇文章。


二 Docker基础知识

目前最新版本的Docker在CentOS6.5上运行不良,最好升级操作系统到CentOS7并升级内核到4.0以上,以下有说明。


在CentOS7上面安装Docker

yum -y install docker*


启动Docker服务

service docker start


获取最近的CentOS镜像

docker pull centos


获取CentOS6的镜像

docker pull centos:6 


查看本地所有的镜像

docker p_w_picpaths


查看单个镜像

docker p_w_picpaths centos



docker run -v /usr/sbin:/usr/sbin --rm centos /usr/sbin/ip addr show

--rm 容器停掉后会被删除


docker run -v /usr/sbin:/usr/sbin  --name=test   centos /usr/sbin/ip addr show 

--name 给容器改名字


docker start -i test    开启test容器


docker run --name=mybash -it centos /bin/bash

可以在容器中安装软件

yum -y install net-tools

docker ps -a列出所有的容器

docker ps 列出正在运行的容器

docker start -ai mybash  开启容器

docker run --name="log_test" -v /dev/log:/dev/log --rm centos logger "Testing logging to the host"

journalctl -b | grep Testing

docker inspect mybash  查看容器的元数据信息

docker inspect   --format='``.`NetworkSettings`.`IPAddress`' mybash

docker inspect --format='``.`State`.`Pid`' mybash

docker inspect --format='``.`HostConfig`.`PortBindings`' mybash

docker exec  在一个运行中的容器中执行命令

docker exec -it mybash /bin/bash

docker stop mybash

docker kill --signal="SIGHUP" mybash

docker rm goofy_wozniak    
docker rm clever_yonath furious_shockley drunk_newton    
d

docker run -i centos:6 /bin/bash -c "yum clean all;yum -y update;yum -y install -y httpd;yum clean all"

docker ps -l 查看最近创建过的容器

docker commit -m "centos6 with httpd" -a "john wang" ff0bfd94552a centos_httpd   从容器中创建镜像提交到本地镜像库

docker run -d -p 8080:80 centos_httpd:latest /usr/sbin/httpd -DFOREGROUND 

使用centos_httpd这个最新的镜像运行一个容器,映射容器的80端口到本地的8080端口

curl http://localhost:8080   

从Dockerfile中创建镜像

mkdir -p httpd-project

cd httpd-project

docker build -t centos_httpd .

docker run -d -t --name=mycentos_httpd -p 80:80 -i centos_httpd:latest /usr/sbin/httpd -DFOREGROUND

从构建的镜像运行容器

# netstat -tulnp|grep 80
tcp6       0      0 :::80                   :::*                    LISTEN      26472/docker-proxy  

 

# curl localhost:80Your Web server test is successful.

docker tag 5c2ed3dea01f centos  给镜像打tag

docker tag 474ff279782b cnegus/myrhel7

docker save -o centos6.tar centos:latest  保存镜像为tar文件

cat centos6.tar|docker import - john/centos6  

docker rmi 340d0ced58bb  删除镜像

docker rmi $(docker p_w_picpaths -a -q)  删除所有镜像


三 Docker网络知识

Docker启动后会默认创建一个名为docker0的虚拟网卡,一般是172.17.42.1/16,这个地址是可以变更的,创建一个容器的时候默认使用--net=bridge参数为容器分配一个docker0相同网段的IP。Docker创建一个容器的时候会创建一对对端网卡,一个分配给容器默认为eth0,一个绑定到docker0,名字一般是vethxxxx。详细知识请参考官方文档。

由于docker0默认为容器分配的IP不满足需求,我们配置的iredmail邮件服务器需要有内网网络的固定IP。所以这里要对docker的网络配置做些更改。

本文都是在CentOS7上安装Docker

1.在Docker主机重新创建一个桥接网卡

cd /etc/sysconfig/network-scripts/

cp ifcfg-enp4s0 ifcfg-br0


vim ifcfg-enp4s0

TYPE=Ethernet

BRIDGE=br0

BOOTPROTO=none

#IPADDR=172.28.10.125

#NETMASK=255.255.255.0

#GATEWAY=172.28.10.27

DEFROUTE=yes

PEERDNS=yes

PEERROUTES=yes

IPV4_FAILURE_FATAL=no

IPV6INIT=yes

IPV6_AUTOCONF=yes

IPV6_DEFROUTE=yes

IPV6_PEERDNS=yes

IPV6_PEERROUTES=yes

IPV6_FAILURE_FATAL=no

NAME=enp4s0

UUID=5cabea9a-df74-4ac0-a8ae-42fb61357a09

DEVICE=enp4s0

ONBOOT=yes



vim ifcfg-br0


TYPE=Bridge

BOOTPROTO=static

IPADDR=172.28.10.125

NETMASK=255.255.255.0

GATEWAY=172.28.10.27

DEFROUTE=yes

PEERDNS=yes

PEERROUTES=yes

IPV4_FAILURE_FATAL=no

IPV6INIT=yes

IPV6_AUTOCONF=yes

IPV6_DEFROUTE=yes

IPV6_PEERDNS=yes

IPV6_PEERROUTES=yes

IPV6_FAILURE_FATAL=no

NAME=br0

#UUID=5cabea9a-df74-4ac0-a8ae-42fb61357a09

DEVICE=br0

ONBOOT=yes



然后重新启动网卡

service network restart


查看网卡信息

# ifconfig

br0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500

        inet 172.28.10.125  netmask 255.255.255.0  broadcast 172.28.10.255

        inet6 fe80::1e6f:65ff:fed7:6531  prefixlen 64  scopeid 0x20<link>

        ether 1c:6f:65:d7:65:31  txqueuelen 0  (Ethernet)

        RX packets 371701  bytes 179614127 (171.2 MiB)

        RX errors 0  dropped 24  overruns 0  frame 0

        TX packets 104043  bytes 8964068 (8.5 MiB)

        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0


enp4s0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500

        ether 1c:6f:65:d7:65:31  txqueuelen 1000  (Ethernet)

        RX packets 279799000  bytes 46612531640 (43.4 GiB)

        RX errors 0  dropped 0  overruns 0  frame 0

        TX packets 104058  bytes 8964998 (8.5 MiB)

        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0


lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536

        inet 127.0.0.1  netmask 255.0.0.0

        inet6 ::1  prefixlen 128  scopeid 0x10<host>

        loop  txqueuelen 0  (Local Loopback)

        RX packets 12  bytes 1124 (1.0 KiB)

        RX errors 0  dropped 0  overruns 0  frame 0

        TX packets 12  bytes 1124 (1.0 KiB)

        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0



如果Docker已经启动,先关掉docker

service docker stop


vim /etc/sysconfig/docker

添加

OPTIONS='-b=br0   --selinux-enabled'

然后启动docker

service docker start

查看docker进程

ps -ef|grep docker

root     11358     1  0 09:00 ?        00:00:00 /usr/bin/docker -d -b=br0 --selinux-enabled


docker会修改iptables的nat表

# iptables -t nat -L -n

Chain POSTROUTING (policy ACCEPT)

target     prot opt source               destination         

MASQUERADE  all  --  172.28.10.0/24       0.0.0.0/0        



创建一个CentOS6的容器用于安装iredmail


#docker run -it --net=none  --name=iredmail  --hostname=mx.xxx.com  --add-host=mx.xxx.com:127.0.0.1  --privileged=true  centos:6  /bin/bash

--name 指定容器的名称

--hostname 指定容器的主机名,生成容器后没法在容器内部修改主机名,修改容器的容器的配置文件也无法生效,所以创建容器的时候最好指定主机名



进入iredmail容器

# docker exec -it iredmail /bin/bash


[root@7e34924492f3 /]# ifconfig

lo        Link encap:Local Loopback  

          inet addr:127.0.0.1  Mask:255.0.0.0

          inet6 addr: ::1/128 Scope:Host

          UP LOOPBACK RUNNING  MTU:65536  Metric:1

          RX packets:0 errors:0 dropped:0 overruns:0 frame:0

          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0

          collisions:0 txqueuelen:0 

          RX bytes:0 (0.0 b)  TX bytes:0 (0.0 b)


由于创建iredmail容器的时候使用的是--net=none,docker将不会为这个容器分配IP地址

根据官方文档的步骤我写了个脚本可以自动为iredmail容器添加固定IP。

docker_container_ip.sh

#!/bin/bash
#author: john wang
#this script is used to auto add specified private ip for docker container

#container can be id or name such as 7e34924492f3  or iredmail

container=iredmail
bridge=br0
mac=12:34:56:78:9a:bc
ip=172.28.10.72/24
gateway=172.28.10.27



docker_status=$(ps -ef|grep docker|grep $bridge|grep -v grep|grep -v $0)
if [ "$docker_status" == "" ];then
   echo -e "\e[035mDocker is not running\e[0m"
   echo -e "\e[035mStarting docker\e[0m"
   /sbin/service docker start
else
   echo -e "\e[035mDocker is already running\e[0m"
fi


container_status=$(docker ps|grep $container)



function set_ip () {
echo -e "\e[035m set ip $ip gateway $gateway mac $mac  for  $container\e[0m"
#### set_ip $1
####
   id=$(docker ps|grep $container|awk '{print $1}')
 if [ "$id" != "" ];then
   A=veth${id:0:4}
   B=eth${id:0:4} 
###  Error: argument "veth7e34924492f3" is wrong: "name" too long
### use ${id:0:4} to cut container id, only  use prefix 4 characters

   pid=$1
   mkdir -p /var/run/netns
   ln -sf /proc/$pid/ns/net  /var/run/netns/$pid   > /dev/null 

###########
####   /opt/script/docker_container_ip.sh: line 43: ip: command not found 
####   /opt/script/docker_container_ip.sh: line 44: brctl: command not found
####   tail -f /var/spool/mail/root
####
###########


   /usr/sbin/ip link add $A  type veth   peer name   $B  > /dev/null  2>&1
   /usr/sbin/brctl addif $bridge $A            > /dev/null  2>&1
   /usr/sbin/ip link set $A up                 > /dev/null  2>&1
   
   /usr/sbin/ip link set $B  netns $pid    > /dev/null  2>&1
   /usr/sbin/ip netns exec $pid ip link set dev $B name eth0    > /dev/null 2>&1
   /usr/sbin/ip netns exec $pid ip link set eth0 address $mac    > /dev/null  2>&1
   /usr/sbin/ip netns exec $pid ip link set eth0 up              > /dev/null  2>&1
   /usr/sbin/ip netns exec $pid ip addr add $ip dev eth0         > /dev/null  2>&1
   /usr/sbin/ip netns exec $pid ip route add default via $gateway  > /dev/null 2>&1
### 2>&1
  else 
     echo -e "\e[035mContainer ID is not valid\e[0m" 
  fi


                   }




if [ "$container_status" != "" -a "$docker_status" != "" ];then
#create a pair of peer interfaces A and B
#bind the A end to the bridge,and bring it up
#place B inside the container's network namespace
#rename to eht0,and activate it with a free IP
   pid=$(docker inspect -f '``.`State`.`Pid`' $container)
   set_ip $pid
else
   echo -e "\e[035m **********WARNING   container $container is not running **********\e[0m"
   echo -e "\e[035m **********Starting  container $container\e[0m"
   docker start $container
   pid=$(docker inspect -f '``.`State`.`Pid`' $container)
   set_ip $pid
fi


添加定时任务

*/2 * * * * /opt/script/docker_container_ip.sh >> /tmp/docker_container_ip.log


这样就可以避免iredmail容器重启或者docker重新启动后容器内的IP消失

root@mx /]# ifconfig
eth0      Link encap:Ethernet  HWaddr 12:34:56:78:9A:BC  
          inet addr:172.28.10.72  Bcast:0.0.0.0  Mask:255.255.255.0
          inet6 addr: fe80::1034:56ff:fe78:9abc/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:7642 errors:0 dropped:0 overruns:0 frame:0
          TX packets:517 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:618391 (603.8 KiB)  TX bytes:39509 (38.5 KiB)

lo        Link encap:Local Loopback  
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:0 (0.0 b)  TX bytes:0 (0.0 b)

[root@mx /]# hostname






使用CentOS7默认的内核运行Docker时会莫名其妙出现内核报错,导致CentOS重启后无法启动

升级内核到4.2.3解决这个问题

参考文章

http://dl528888.blog.51cto.com/2382721/1655142









四 安装和配置iredmail


然后参考http://john88wang.blog.51cto.com/2165294/1610504

这里的步骤安装和配置iredmail


按照配置完iredmail后需要启动相关服务,安装后iredmail后提示重启启动服务器,但是使用Docker容器重启后并不会自动启动相关服务

执行脚本/opt/script/iredmail_service.sh

#!/bin/bash
services="amavisd cbpolicyd clamd clamd.amavisd crond dovecot httpd iredapd mysqld postfix rsyslog saslauthd slapd spamassassin sshd iptables"

for i in $(echo $services)
do
   service $i start
done


测试可以使用后就可以使用docker commit将这个运行的iredmail容器制作成镜像提交到本地镜像库


docker commit -m "centos with iredmail" -a "john wang"  3fffabaa79cd centos_iredmail




五 从Dockerfile中创建iredmail镜像

可以使用Dockerfile创建iredmail镜像

mkdir -p docker_iredmail

cd docker_iredmail/

vim Dockerfile

FROM centos:6

### Update and customize centos packages.
RUN yum -y install wget vim lrzsz tar bzip2 telnet man 

### Download and unzip iRedMail.
ENV IREDMAIL iRedMail-0.9.2
RUN wget https://bitbucket.org/zhb/iredmail/downloads/$IREDMAIL.tar.bz2 ;\
    tar xvjf $IREDMAIL.tar.bz2 ;\
    rm $IREDMAIL.tar.bz2
RUN rpm -ivh http://dl.fedoraproject.org/pub/epel/epel-release-latest-6.noarch.rpm

### Install iRedMail.
RUN  echo "127.0.0.1 mx.xxx.com" > /etc/hosts
#RUN  hostname mx.xxx.com
COPY depends.txt /$IREDMAIL/
COPY config /$IREDMAIL/
RUN  for i in $(cat /$IREDMAIL/depends.txt);\
     do \
       yum -y install $i;\
     done
       
RUN chmod +x /$IREDMAIL/iRedMail.sh
RUN echo y | /$IREDMAIL/iRedMail.sh








vim depends.txt

这个文件是安装和配置iredmail需要的软件包

MySQL-python
acl
altermime
amavisd-new
awstats
clamav-db
clamd
cluebringer
crontabs
dos2unix
dovecot
dovecot-managesieve
dovecot-pigeonhole
fail2ban
httpd
logwatch
mod_ssl
mod_wsgi
mysql-server
openldap-clients
openldap-servers
patch
perl-LDAP
perl-Mail-SPF
php
php-common
php-gd
php-imap
php-intl
php-ldap
php-mbstring
php-mcrypt
php-mysql
php-pear-Net-IDNA2
php-pear-Net-LDAP2
php-pecl-apc
php-pgsql
php-xml
postfix
python-beautifulsoup4
python-jinja2
python-ldap
python-lxml
python-netifaces
python-setuptools
python-sqlalchemy
python-webpy
spamassassin
tmpwatch
unrar
unzip
openldap
apr
apr-util
apr-util-ldap
arj
cabextract
clamav
cronie
cronie-anacron
cyrus-sasl
db4-cxx
db4-devel
ed
file
freeze
gamin-python
gdbm-devel
glibc-devel
glibc-headers
httpd-tools
ipset
kernel-headers
libXpm
libc-client
libicu
libmcrypt
libmnl
libmspack
libtool-ltdl
libxslt
logrotate
lrzip
lzo
lzop
mailcap
mailx
mysql
mysql-libs
nomarch
p7zip
p7zip-plugins
perl
perl-Archive-Tar
perl-Archive-Zip
perl-Authen-SASL
perl-BerkeleyDB
perl-Cache-FastMmap
perl-Compress-Raw-Zlib
perl-Compress-Zlib
perl-Config-IniFiles
perl-Convert-ASN1
perl-Convert-BinHex
perl-Convert-TNEF
perl-Convert-UUlib
perl-Crypt-OpenSSL-Bignum
perl-Crypt-OpenSSL-RSA
perl-Crypt-OpenSSL-Random
perl-DBD-MySQL
perl-DBD-SQLite
perl-DBI
perl-Date-Manip
perl-Digest-HMAC
perl-Digest-SHA
perl-Digest-SHA1
perl-Encode-Detect
perl-Error
perl-ExtUtils-MakeMaker
perl-ExtUtils-ParseXS
perl-GSSAPI
perl-HTML-Parser
perl-HTML-Tagset
perl-IO-Compress-Base
perl-IO-Compress-Zlib
perl-IO-Multiplex
perl-IO-Socket-INET6
perl-IO-Socket-SSL
perl-IO-Zlib
perl-IO-stringy
perl-List-MoreUtils
perl-MIME-tools
perl-Mail-DKIM
perl-MailTools
perl-Module-Pluggable
perl-Net-DNS
perl-Net-LibIDN
perl-Net-SSLeay
perl-Net-Server
perl-NetAddr-IP
perl-Package-Constants
perl-Pod-Escapes
perl-Pod-Simple
perl-Razor-Agent
perl-Socket6
perl-Test-Harness
perl-Text-Iconv
perl-Time-HiRes
perl-TimeDate
perl-URI
perl-Unix-Syslog
perl-XML-Filter-BufferText
perl-XML-LibXML
perl-XML-NamespaceSupport
perl-XML-SAX
perl-XML-SAX-Writer
perl-YAML-Syck
perl-devel
perl-libs
perl-libwww-perl
perl-version
php-cli
php-pdo
php-pear
portreserve
postgresql-libs
procmail
python-babel
python-cherrypy
python-html5lib
python-inotify
redhat-logos
rsyslog
unzoo
db4
db4-utils
glibc
glibc-common





参考文章:

https://docs.docker.com/articles/networking/

https://github.com/docker-build/iRedMail

https://hub.docker.com/search/?q=iRedMail&page=1&isAutomated=0&isOfficial=0&starCount=0&pullCount=0

http://john88wang.blog.51cto.com/2165294/1610504

http://dl528888.blog.51cto.com/2382721/1655142