Ubuntu 14.04下适应Docker搭建solrCloud集群

主要内容:

技术关键点:docker17.06.3安装,docker自制镜像及相关容器操作,docker分配固定IP及添加端口映射,solrCloud集群部署等

主要思路:在Ubuntu14.04操作系统的宿主机中,安装docker17.06.3,将宿主机的操作系统制作成docker基础镜像,之后使用自制的基础镜像在docker中启动3个容器,分配固定IP,再在3个容器中配置solrCloud集群。

注:solrCloud采用的solr内置jetty,需要单独配置zookeeper
容器IP及名称见下表:

编号    静态IP        容器名称

1      172.18.0.11   server1
2      172.18.0.12   server2
3      172.18.0.13   server3

一、在宿主机安装docker最新版

1、更新apt-get

apt-get update

2、安装curl工具

apt-get install curl

3、获取并安装docker最新版

curl -fsSL https://get.docker.com/ | sh

4、查看docker版本

docker -v

未分类

二、在宿主机制作Ubuntu14.10基础镜像ubuntu-self

1、将本机操作系统打包成tar文件

tar --numeric-owner --exclude=/proc --exclude=/sys -cvf ubuntu-self.tar /

未分类

2、将制作的tar文件导入docker镜像库中,并命令为:ubuntu-self:

cat ubuntu-self.tar | docker import - ubuntu-self

3、现在可以运行它了:

docker run -i -t ubuntu-self 

注:官方提供的镜像库中Ubuntu无法sudo,不太好用,这里我自己利用本机的操作系统生成了一个基础纯净版镜像,命名为ubuntu-self,大小约3.5G,里面没有安装任何软件。

三、配置宿主机的hosts文件,以便利用ssh登录容器

1、修改hosts文件,添加如下内容:

vi /etc/hosts
172.18.0.11 server1
172.18.0.12 server2
172.18.0.13 server3

2、查看hosts文件

cat /etc/hosts

未分类

四、启动一个容器,设置静态IP,命名为server1

1、在宿主机上创建自定义网络

docker network create --subnet=172.18.0.0/16 search_network

备注:这里选取了172.18.0.0网段,也可以指定其他任意空闲的网段,search_network为自定义网桥的名字,可自己任意取名。

2、利用docker启动容器server1,分配固定IP 172.18.0.11,并将容器的8983端口与宿主机8983进行映射,以便可以从外部访问容器

docker run -itd --name server1 --net search_network --ip 172.18.0.11 -p 8983:8983 ubuntu-self /bin/bash

注:该命令执行完之后直接进入到server1的命令行界面,主机名称变为docker分配的随机字符串,查看ip是否为静态,执行结果如下图所示:

未分类

五、继续操作,在容器server1中配置ssh服务

1、aptget升级

apt-get update

2、安装openssh服务

apt-get install openssh-server

3、开启ssh服务

sudo /etc/init.d/ssh start

4、设置ssh开机启动

vi /etc/rc.local

添加如下内容:

service ssh start

5、退出容器,在宿主机中采用ssh登录

exit

未分类

6、在宿主机切换到tank用户(root登录ssh需要修改ssh配置文件,这里用tank用户登录更方便些),并ssh到容器server1

su tank
ssh server1

未分类

六、在容器server1中安装jdk1.8并配置java环境变量

1、解压缩文件

tar -zxvf jdk1.8.0_141.tar.gz -C /usr/local/java/

2、向/etc/profile文件中追加下面内容:

export JAVA_HOME=/usr/local/java/jdk1.8.0_141
export JRE_HOME=${JAVA_HOME}/jre
export CLASSPATH=.:${JAVA_HOME}/lib:${JRE_HOME}/lib
export PATH=$PATH:${JAVA_HOME}/bin

3、让文件生效

source /etc/profile

4、验证java成功安装

java -version

未分类

七、在容器server1中安装配置zookeeper-3.4.10

1、解压zookeeper 安装包到/usr/local目录中

tar -zxvf zookeeper-3.4.10.tar.gz -C /usr/local/

2、创建zookeeper的data和logs目录,确保拥有读写权限

mkdir /home/tank/zookeeper/data
mkdir /home/tank/zookeeper/log

3、将zookeeper安装目录下conf文件夹中的zoo_sample.cfg重命名为zoo.cfg

未分类

4、修改zoo.cfg内容,zoo.cfg配置完后如下:

# The number of milliseconds of each tick
tickTime=2000
# The number of ticks that the initial
# synchronization phase can take
initLimit=10
# The number of ticks that can pass between
# sending a request and getting an acknowledgement
syncLimit=5
# the directory where the snapshot is stored.
# do not use /tmp for storage, /tmp here is just
# example sakes.
dataDir=/home/tank/zookeeper/data
dataLogDir=/home/tank/zookeeper/log
# the port at which the clients will connect
clientPort=2181
# the maximum number of client connections.
# increase this if you need to handle more clients
#maxClientCnxns=60
#
# Be sure to read the maintenance section of the
# administrator guide before turning on autopurge.
#
# http://zookeeper.apache.org/doc/current/zookeeperAdmin.html#sc_maintenance
#
# The number of snapshots to retain in dataDir
#autopurge.snapRetainCount=3
# Purge task interval in hours
# Set to "0" to disable auto purge feature
#autopurge.purgeInterval=1
server.1=172.18.0.11:2888:3888
server.2=172.18.0.12:2888:3888
server.3=172.18.0.13:2888:3888

5、进入/home/tank/zookeeper/data中,新建myid文件,写入1

未分类

注:其他两个节点的myid内容应分别是2和3

八、在容器server1中安装配置solr-6.6.0

1、解压solr-6.6.0.tgz到/usr/local目录下

tar -zxvf solr-6.6.0.tgz -C /usr/local/

未分类

2、创建solrCloud根目录solr_cloud_home文件夹

mkdir /usr/local/solrCloud/solr_cloud_home

3、复制/usr/local/solr-6.6.0/server/solr/目录下的文件到

solr_cloud_home中
cp /usr/local/solr-6.6.0/server/solr/* /usr/local/solrCloud/solr_cloud_home/

查看solr_cloud_home目录,如图所示:

ls

未分类

4、创建配置存放目录solr_cloud_collection文件夹

mkdir /usr/local/solrCloud/solr_cloud_collection

5、复制/usr/local/solr-6.6.0/example/example-DIH/solr/solr/目录下的文件到solr_cloud_collection/cloud_core中

mkdir /usr/local/solr_cloud_collection/cloud_core
cp /usr/local/solr-6.6.0/example/example-DIH/solr/solr/* /usr/local/solr_cloud_collection/cloud_core/
ls

未分类

九、在宿主机提交容器server1为新的镜像,命名为ubuntu-self-solr

sudo docker commit server1 ubuntu-self-solr
docker images

未分类

十、利用上一步生成的镜像启动容器server2,server3

1、在宿主机启动容器server2,设置IP为172.18.0.12

docker run -itd --name server2 --net search_network --ip 172.18.0.12 ubuntu-self-solr /bin/bash

2、将容器server2中/home/tank/zookeeper/data/myid内容由1改为2

3、退出容器server2,回到宿主机

exit

4、在宿主机启动容器server3,设置IP为172.18.0.13

docker run -itd --name server3 --net search_network --ip 172.18.0.13 ubuntu-self-solr /bin/bash

5、将容器server3中/home/tank/zookeeper/data/myid内容由1改为3

6、退出容器server3,回到宿主机

exit

7、在宿主机中查看docker容器运行情况

docker ps

未分类

十一、在宿主机ssh登录容器server1,server2,server3 并分别启动zookeeper

ssh server1
cd /usr/local/zookeeper-3.4.10/
bin/zkServer.sh start
/usr/local/zookeeper-3.4.10/bin/zkServer.sh start

未分类

注:在server2,server3上同样执行此操作

十二、在宿主机ssh登录容器server1,server2,server3并启动solr

1、ssh登录server1,cloud模式下启动solr

ssh server1
cd /usr/local/solr-6.6.0
bin/solr start -cloud -p 8983 -s "/usr/local/solrCloud/solr_cloud_home/" -z "172.18.0.11:2181,172.18.0.12:2181,172.18.0.13:2181"

未分类

2、打开宿主机浏览器,访问页面http://172.18.0.11:8983/solr/,可以进入solr页面即代表启动成功

注:在server2,server3上同样执行此操作

十三、在容器server1上创建Collection(只需要在一台solr节点上操作)

1、由solr命令建立索引,这里索引命名为:cloudsuite_web_search
进入solr/bin目录,使用solr命令:

cd /usr/local/solr-6.6.0
bin/solr create_collection -c cloudsuite_web_search -shards 3 -replicationFactor 3 -d /usr/local/solrCloud/solr_cloud_collection/cloud_core/conf -p 8983 

-c 核心名称tar
-shards 分片数量
– replicationFactor 副本数量 (一般指有几台solr集群)

2、将solr提供的xml示例文件上传至索引

bin/post -c cloudsuite_web_search *.xml

十四、在宿主机上通过浏览器访问solrCloud集群,验证操作成功

安装成功后,无论从哪个节点访问8983端口,均可以看到cloud的拓扑模式,如下所示

未分类

CentOS 7安装Harbor Docker Registry

Harbor是VMware公司开源的企业级Docker Registry,在原生Docker Registry的基础上增加了一些安全、访问控制、管理等功能以满足企业对于镜像仓库的需求。Harbor以docker-compose的规范形式组织各个组件,并通过docker-compose工具进行启停。

安装docker-compose

[root@node1 /root/harbor]#curl -L https://github.com/docker/compose/releases/download/1.13.0/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose
[root@node1 /root/harbor]#chmod +x /usr/local/bin/docker-compose
[root@node1 /root/harbor]#docker-compose version
docker-compose version 1.13.0, build 1719ceb
docker-py version: 2.2.1
CPython version: 2.7.13
OpenSSL version: OpenSSL 1.0.1t  3 May 2016

修改harbor.cfg配置文件里的hostname参数为本机ip

hostname = 172.172.20.33

开始安装harbor

[root@node1 /root/harbor]#./install.sh 

[Step 0]: checking installation environment …

Note: docker version: 1.12.6

Note: docker-compose version: 1.13.0

[Step 1]: loading Harbor images …
dd60b611baaa: Loading layer [==================================================>] 133.2 MB/133.2 MB
0bfc226dc2e8: Loading layer [==================================================>] 1.536 kB/1.536 kB
66c3231118d2: Loading layer [==================================================>] 17.69 MB/17.69 MB
fe2c778bb727: Loading layer [==================================================>] 17.69 MB/17.69 MB
Loaded image: vmware/harbor-jobservice:v1.1.2                                   ] 196.6 kB/17.69 MB
fe4c16cbf7a4: Loading layer [==================================================>] 128.9 MB/128.9 MB
c4a8b7411af4: Loading layer [==================================================>] 60.57 MB/60.57 MB
3f117c44afbb: Loading layer [==================================================>] 3.584 kB/3.584 kB
3569f62067e2: Loading layer [==================================================>] 17.86 MB/17.86 MB
Loaded image: vmware/nginx:1.11.5-patched                                       ] 196.6 kB/17.86 MB
Loaded image: photon:1.0
4a050fccec52: Loading layer [==================================================>] 12.16 MB/12.16 MB
d918d73369ec: Loading layer [==================================================>]  17.3 MB/17.3 MB
22898836924e: Loading layer [==================================================>] 15.87 kB/15.87 kB
Loaded image: vmware/notary-photon:server-0.5.0                                 ]    512 B/15.87 kB
a39bd6a7f897: Loading layer [==================================================>] 10.95 MB/10.95 MB
6f79b8337a1f: Loading layer [==================================================>]  17.3 MB/17.3 MB
74bbd0e81dd0: Loading layer [==================================================>] 15.87 kB/15.87 kB
Loaded image: vmware/notary-photon:signer-0.5.0                                 ]    512 B/15.87 kB
2df722677b4c: Loading layer [==================================================>] 7.062 MB/7.062 MB
e5338f288c70: Loading layer [==================================================>] 7.062 MB/7.062 MB
Loaded image: vmware/harbor-adminserver:v1.1.2                                  ]  98.3 kB/7.062 MB
b79e6c985050: Loading layer [==================================================>] 21.26 MB/21.26 MB
568e827ac2db: Loading layer [==================================================>] 7.168 kB/7.168 kB
e120e08d1ae8: Loading layer [==================================================>] 12.92 MB/12.92 MB
c678c146825f: Loading layer [==================================================>] 9.728 kB/9.728 kB
835ee5702bce: Loading layer [==================================================>]  2.56 kB/2.56 kB
eaf7ac0e9e24: Loading layer [==================================================>] 21.26 MB/21.26 MB
Loaded image: vmware/harbor-ui:v1.1.2                                           ] 229.4 kB/21.26 MB
c8ef72937018: Loading layer [==================================================>] 67.93 MB/67.93 MB
01e57c31fb31: Loading layer [==================================================>] 3.584 kB/3.584 kB
ae8312f0516f: Loading layer [==================================================>] 3.072 kB/3.072 kB
47b646017cc6: Loading layer [==================================================>] 3.072 kB/3.072 kB
Loaded image: vmware/harbor-log:v1.1.2                                          ]    512 B/3.072 kB
5d6cbe0dbcf9: Loading layer [==================================================>] 129.2 MB/129.2 MB
435f2dfbd884: Loading layer [==================================================>] 344.6 kB/344.6 kB
814d7b59f0cc: Loading layer [==================================================>] 4.657 MB/4.657 MB
aae399245bd0: Loading layer [==================================================>] 1.536 kB/1.536 kB
21e2ae955f72: Loading layer [==================================================>] 33.84 MB/33.84 MB
a2d0f7b84059: Loading layer [==================================================>] 25.09 kB/25.09 kB
819fa6af55b8: Loading layer [==================================================>] 3.584 kB/3.584 kB
78914c99a468: Loading layer [==================================================>] 167.7 MB/167.7 MB
36e79c658afb: Loading layer [==================================================>] 6.144 kB/6.144 kB
f73503aca003: Loading layer [==================================================>] 9.216 kB/9.216 kB
a21b39f6da59: Loading layer [==================================================>] 1.536 kB/1.536 kB
2f0fcce131fa: Loading layer [==================================================>]  7.68 kB/7.68 kB
cbf999ad70ad: Loading layer [==================================================>] 4.608 kB/4.608 kB
8005207f317c: Loading layer [==================================================>] 4.608 kB/4.608 kB
Loaded image: vmware/harbor-db:v1.1.2                                           ]    512 B/4.608 kB
69c25b821c78: Loading layer [==================================================>] 22.79 MB/22.79 MB
5b403ac6f7ea: Loading layer [==================================================>] 3.584 kB/3.584 kB
9e2e304b5fe5: Loading layer [==================================================>] 2.048 kB/2.048 kB
Loaded image: vmware/registry:2.6.1-photon                                      ]    512 B/2.048 kB
78dbfa5b7cbc: Loading layer [==================================================>] 130.9 MB/130.9 MB
5f70bf18a086: Loading layer [==================================================>] 1.024 kB/1.024 kB
8deec01122be: Loading layer [==================================================>] 344.6 kB/344.6 kB
574ab36807f2: Loading layer [==================================================>] 1.536 kB/1.536 kB
d8f2cde2eef8: Loading layer [==================================================>] 20.48 kB/20.48 kB
eaa3924b054e: Loading layer [==================================================>]  5.12 kB/5.12 kB
8aa2c772121c: Loading layer [==================================================>] 184.3 MB/184.3 MB
c3014bbccb0b: Loading layer [==================================================>] 8.704 kB/8.704 kB
978a35efaa8c: Loading layer [==================================================>] 4.608 kB/4.608 kB
c2385ae7d6e5: Loading layer [==================================================>]  16.6 MB/16.6 MB
Loaded image: vmware/harbor-notary-db:mariadb-10.1.10                           ] 196.6 kB/16.6 MB


[Step 2]: preparing environment …
Generated and saved secret to file: /data/secretkey
Generated configuration file: ./common/config/nginx/nginx.conf
Generated configuration file: ./common/config/adminserver/env
Generated configuration file: ./common/config/ui/env
Generated configuration file: ./common/config/registry/config.yml
Generated configuration file: ./common/config/db/env
Generated configuration file: ./common/config/jobservice/env
Generated configuration file: ./common/config/jobservice/app.conf
Generated configuration file: ./common/config/ui/app.conf
Generated certificate, key file: ./common/config/ui/private_key.pem, cert file: ./common/config/registry/root.crt
The configuration files are ready, please use docker-compose to start the service.


[Step 3]: checking existing instance of Harbor …


[Step 4]: starting Harbor …
Creating network "harbor_harbor" with the default driver
Creating harbor-log … 
Creating harbor-log … done
Creating harbor-db … 
Creating harbor-adminserver … 
Creating registry … 
Creating harbor-db
Creating registry
Creating harbor-adminserver … done
Creating harbor-ui … 
Creating harbor-ui … done
Creating nginx … 
Creating harbor-jobservice … 
Creating nginx
Creating harbor-jobservice … done

✔ —-Harbor has been installed and started successfully.—-

Now you should be able to visit the admin portal at http://172.172.20.33. 
For more details, please visit https://github.com/vmware/harbor .

安装完毕后使用docker-compose查看

[root@node1 /root/harbor]#docker-compose ps
       Name                     Command               State                                Ports                               
——————————————————————————————————————————
harbor-adminserver   /harbor/harbor_adminserver       Up                                                                       
harbor-db            docker-entrypoint.sh mysqld      Up      3306/tcp                                                         
harbor-jobservice    /harbor/harbor_jobservice        Up                                                                       
harbor-log           /bin/sh -c crond && rm -f  …   Up      127.0.0.1:1514->514/tcp                                          
harbor-ui            /harbor/harbor_ui                Up                                                                       
nginx                nginx -g daemon off;             Up      0.0.0.0:443->443/tcp, 0.0.0.0:4443->4443/tcp, 0.0.0.0:80->80/tcp 
registry             /entrypoint.sh serve /etc/ …   Up      5000/tcp                    

如果需要修改配置重新安装执行以下操作

docker-compose down -v
./prepare
./install.sh

harbor的关闭和启动

[root@node1 /root/harbor]#docker-compose down -v
Stopping harbor-jobservice … done
Stopping nginx … done
Stopping harbor-ui … done
Stopping harbor-adminserver … done
Stopping registry … done
Stopping harbor-db … done
Stopping harbor-log … done
Removing harbor-jobservice … done
Removing nginx … done
Removing harbor-ui … done
Removing harbor-adminserver … done
Removing registry … done
Removing harbor-db … done
Removing harbor-log … done
Removing network harbor_harbor
[root@node1 /root/harbor]#
[root@node1 /root/harbor]#docker-compose ps
Name   Command   State   Ports 
——————————
[root@node1 /root/harbor]#
[root@node1 /root/harbor]#docker-compose up -d
Creating network "harbor_harbor" with the default driver
Creating harbor-log … 
Creating harbor-log … done
Creating registry … 
Creating harbor-adminserver … 
Creating harbor-db … 
Creating harbor-adminserver
Creating registry
Creating harbor-adminserver … done
Creating harbor-ui … 
Creating harbor-ui … done
Creating nginx … 
Creating harbor-jobservice … 
Creating nginx
Creating harbor-jobservice … done
[root@node1 /root/harbor]#
[root@node1 /root/harbor]#docker-compose ps
       Name                     Command               State                                Ports                               
——————————————————————————————————————————
harbor-adminserver   /harbor/harbor_adminserver       Up                                                                       
harbor-db            docker-entrypoint.sh mysqld      Up      3306/tcp                                                         
harbor-jobservice    /harbor/harbor_jobservice        Up                                                                       
harbor-log           /bin/sh -c crond && rm -f  …   Up      127.0.0.1:1514->514/tcp                                          
harbor-ui            /harbor/harbor_ui                Up                                                                       
nginx                nginx -g daemon off;             Up      0.0.0.0:443->443/tcp, 0.0.0.0:4443->4443/tcp, 0.0.0.0:80->80/tcp 
registry             /entrypoint.sh serve /etc/ …   Up      5000/tcp          

通过浏览器访问harbor,默认用户名和密码是admin/Harbor12345

http://172.172.20.33

未分类

未分类

使用docker构建部署django+mysql项目(docker-compose)

这里需要升级docker版本,因为centos7 yum源默认自带的docker版本无法使用compose,详情见: http://nanguawu.me/container/5013.html

容器部署目录结构:

[root@vm2 web_django]# tree -L 2
.
├── db
│   ├── auto.cnf
│   ├── ca-key.pem
│   ├── ca.pem
│   ├── client-cert.pem
│   ├── client-key.pem
│   ├── data01
│   ├── ib_buffer_pool
│   ├── ibdata1
│   ├── ib_logfile0
│   ├── ib_logfile1
│   ├── mysql
│   ├── performance_schema
│   ├── private_key.pem
│   ├── public_key.pem
│   ├── server-cert.pem
│   ├── server-key.pem
│   └── sys
├── docker-compose.yml
├── Dockerfile
├── manage.py
├── requirements.txt
└── website
    ├── __init__.py
    ├── settings.py
    ├── urls.py
    └── wsgi.py
6 directories, 21 files

requirements.txt文件内容:

django==1.10.8
MySQL-python

Dockfile文件内容:

FROM python:2.7
MAINTAINER Larryliang "[email protected]"
ENV PYTHONUNBUFFERD 1
RUN mkdir /code
RUN mkdir /code/db
RUN mkdir /code/website
WORKDIR /code
ADD requirements.txt /code/
RUN  pip install -r requirements.txt -i https://pypi.douban.com/simple   --trusted-host pypi.douban.com
ADD . /code/

docker-compose.yml文件内容:

version: '3'
services:
  db:
    image: mysql
    expose: 
      - "3306"
    volumes:
      - ./db:/var/lib/mysql
    environment:
      - MYSQL_DATABASE=data01
      - MYSQL_ROOT_PASSWORD=data01
  web:
    build: .
    command: python manage.py runserver 0.0.0.0:8000
    volumes:
      - .:/code
    ports:
      - "8000:8000"
    depends_on:
      - db

Build项目

[root@vm2 web_django]# docker version
Client:
 Version:      17.07.0-ce
 API version:  1.31
 Go version:   go1.8.3
 Git commit:   8784753
 Built:        Tue Aug 29 17:42:01 2017
 OS/Arch:      linux/amd64
Server:
 Version:      17.07.0-ce
 API version:  1.31 (minimum version 1.12)
 Go version:   go1.8.3
 Git commit:   8784753
 Built:        Tue Aug 29 17:43:23 2017
 OS/Arch:      linux/amd64
 Experimental: false
[root@vm2 web_django]# docker-compose  build
db uses an image, skipping
Building web
Step 1/10 : FROM python:2.7
 ---> 8a90a66b719a
Step 2/10 : MAINTAINER Larryliang "[email protected]"
 ---> Using cache
 ---> 580a124b98a8
Step 3/10 : ENV PYTHONUNBUFFERD 1
 ---> Using cache
 ---> 87f8ce3ca0a2
Step 4/10 : RUN mkdir /code
 ---> Using cache
 ---> 83f26201a74f
Step 5/10 : RUN mkdir /code/db
 ---> Using cache
 ---> 06c6e22dd86a
Step 6/10 : RUN mkdir /code/website
 ---> Using cache
 ---> c0abb5fd2218
Step 7/10 : WORKDIR /code
 ---> Using cache
 ---> 942e3b5a8e20
Step 8/10 : ADD requirements.txt /code/
 ---> Using cache
 ---> 0a4a0bd7b379
Step 9/10 : RUN pip install -r requirements.txt -i https://pypi.douban.com/simple   --trusted-host pypi.douban.com
 ---> Using cache
 ---> 09600fc029fd
Step 10/10 : ADD . /code/
 ---> 11773f943a61
Successfully built 11773f943a61
Successfully tagged webdjango_web:latest

创建项目和APP

[root@vm2 web_django]# docker-compose  run web django-admin.py startproject website  .
Creating network "webdjango_default" with the default driver
Pulling db (mysql:latest)...
latest: Pulling from library/mysql
ad74af05f5a2: Already exists
0639788facc8: Pull complete
de70fa77eb2b: Pull complete
724179e94999: Pull complete
50c77fb16ba6: Pull complete
d51f459239fb: Pull complete
937bbdd4305a: Pull complete
35369f9634e1: Pull complete
f6016aab25f1: Pull complete
5f1901e920da: Pull complete
fdf808213c5b: Pull complete
Digest: sha256:96edf37370df96d2a4ee1715cc5c7820a0ec6286551a927981ed50f0273d9b43
Status: Downloaded newer image for mysql:latest
Creating webdjango_db_1 ... 
Creating webdjango_db_1 ... done

修改配置website/settings.py如下,之后重新build

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'data01',
        'USER': 'root',
        'PASSWORD': 'data01',
        'HOST': 'db',
        'PORT': '3306',
    }
}

启动容器:

[root@vm2 web_django]# docker-compose  up
Starting webdjango_db_1 ... 
Starting webdjango_db_1 ... done
Recreating webdjango_web_1 ... 
Recreating webdjango_web_1 ... done
Attaching to webdjango_db_1, webdjango_web_1
db_1   | 2017-09-07T12:22:23.513202Z 0 [Warning] TIMESTAMP with implicit DEFAULT value is deprecated. Please use --explicit_defaults_for_timestamp server option (see documentation for more details).
db_1   | 2017-09-07T12:22:23.515941Z 0 [Note] mysqld (mysqld 5.7.19) starting as process 1 ...
db_1   | 2017-09-07T12:22:23.612191Z 0 [Note] InnoDB: PUNCH HOLE support available
db_1   | 2017-09-07T12:22:23.612240Z 0 [Note] InnoDB: Mutexes and rw_locks use GCC atomic builtins
db_1   | 2017-09-07T12:22:23.612247Z 0 [Note] InnoDB: Uses event mutexes
db_1   | 2017-09-07T12:22:23.612252Z 0 [Note] InnoDB: GCC builtin __atomic_thread_fence() is used for memory barrier
db_1   | 2017-09-07T12:22:23.612256Z 0 [Note] InnoDB: Compressed tables use zlib 1.2.3
db_1   | 2017-09-07T12:22:23.612260Z 0 [Note] InnoDB: Using Linux native AIO
db_1   | 2017-09-07T12:22:23.612510Z 0 [Note] InnoDB: Number of pools: 1
db_1   | 2017-09-07T12:22:23.612640Z 0 [Note] InnoDB: Using CPU crc32 instructions
db_1   | 2017-09-07T12:22:23.699132Z 0 [Note] InnoDB: Initializing buffer pool, total size = 128M, instances = 1, chunk size = 128M
db_1   | 2017-09-07T12:22:23.706780Z 0 [Note] InnoDB: Completed initialization of buffer pool
db_1   | 2017-09-07T12:22:23.708851Z 0 [Note] InnoDB: If the mysqld execution user is authorized, page cleaner thread priority can be changed. See the man page of setpriority().
db_1   | 2017-09-07T12:22:23.721021Z 0 [Note] InnoDB: Highest supported file format is Barracuda.
db_1   | 2017-09-07T12:22:24.210121Z 0 [Note] InnoDB: Creating shared tablespace for temporary tables
db_1   | 2017-09-07T12:22:24.210959Z 0 [Note] InnoDB: Setting file './ibtmp1' size to 12 MB. Physically writing the file full; Please wait ...
db_1   | 2017-09-07T12:22:24.884250Z 0 [Note] InnoDB: File './ibtmp1' size is now 12 MB.
db_1   | 2017-09-07T12:22:24.937435Z 0 [Note] InnoDB: 96 redo rollback segment(s) found. 96 redo rollback segment(s) are active.
db_1   | 2017-09-07T12:22:24.937508Z 0 [Note] InnoDB: 32 non-redo rollback segment(s) are active.
db_1   | 2017-09-07T12:22:24.938536Z 0 [Note] InnoDB: Waiting for purge to start
db_1   | 2017-09-07T12:22:24.989070Z 0 [Note] InnoDB: 5.7.19 started; log sequence number 12143700
db_1   | 2017-09-07T12:22:24.989543Z 0 [Note] InnoDB: Loading buffer pool(s) from /var/lib/mysql/ib_buffer_pool
db_1   | 2017-09-07T12:22:24.992481Z 0 [Note] Plugin 'FEDERATED' is disabled.
db_1   | 2017-09-07T12:22:25.148161Z 0 [Note] Found ca.pem, server-cert.pem and server-key.pem in data directory. Trying to enable SSL support using them.
db_1   | 2017-09-07T12:22:25.186490Z 0 [Warning] CA certificate ca.pem is self signed.
db_1   | 2017-09-07T12:22:25.189451Z 0 [Note] Server hostname (bind-address): '*'; port: 3306
db_1   | 2017-09-07T12:22:25.189543Z 0 [Note] IPv6 is available.
db_1   | 2017-09-07T12:22:25.189564Z 0 [Note]   - '::' resolves to '::';
db_1   | 2017-09-07T12:22:25.189595Z 0 [Note] Server socket created on IP: '::'.
db_1   | 2017-09-07T12:22:25.314065Z 0 [Note] InnoDB: Buffer pool(s) load completed at 170907 12:22:25
db_1   | 2017-09-07T12:22:25.320253Z 0 [Warning] 'user' entry 'root@localhost' ignored in --skip-name-resolve mode.
db_1   | 2017-09-07T12:22:25.320383Z 0 [Warning] 'user' entry 'mysql.sys@localhost' ignored in --skip-name-resolve mode.
db_1   | 2017-09-07T12:22:25.320534Z 0 [Warning] 'db' entry 'performance_schema mysql.session@localhost' ignored in --skip-name-resolve mode.
db_1   | 2017-09-07T12:22:25.320580Z 0 [Warning] 'db' entry 'sys mysql.sys@localhost' ignored in --skip-name-resolve mode.
db_1   | 2017-09-07T12:22:25.337153Z 0 [Warning] 'proxies_priv' entry '@ root@localhost' ignored in --skip-name-resolve mode.
db_1   | 2017-09-07T12:22:25.486920Z 0 [Warning] 'tables_priv' entry 'user mysql.session@localhost' ignored in --skip-name-resolve mode.
db_1   | 2017-09-07T12:22:25.486995Z 0 [Warning] 'tables_priv' entry 'sys_config mysql.sys@localhost' ignored in --skip-name-resolve mode.
db_1   | 2017-09-07T12:22:25.741875Z 0 [Note] Event Scheduler: Loaded 0 events
db_1   | 2017-09-07T12:22:25.742708Z 0 [Note] mysqld: ready for connections.
db_1   | Version: '5.7.19'  socket: '/var/run/mysqld/mysqld.sock'  port: 3306  MySQL Community Server (GPL)
db_1   | 2017-09-07T12:22:25.742789Z 0 [Note] Executing 'SELECT * FROM INFORMATION_SCHEMA.TABLES;' to get a list of tables using the deprecated partition engine. You may use the startup option '--disable-partition-engine-check' to skip this check. 
db_1   | 2017-09-07T12:22:25.742799Z 0 [Note] Beginning of list of non-natively partitioned tables
db_1   | 2017-09-07T12:22:25.877821Z 0 [Note] End of list of non-natively partitioned tables

或者放到后台启动:

[root@vm2 web_django]# docker-compose  up -d
Starting webdjango_db_1 ... 
Starting webdjango_db_1 ... done
Starting webdjango_web_1 ... 
Starting webdjango_web_1 ... done

测试结果:

[root@vm2 web_django]# curl  http://192.168.100.120:8000/ 
<!DOCTYPE html>
<html lang="en"><head>
  <meta http-equiv="content-type" content="text/html; charset=utf-8">
  <meta name="robots" content="NONE,NOARCHIVE"><title>Welcome to Django</title>
  <style type="text/css">
    html * { padding:0; margin:0; }
    body * { padding:10px 20px; }
    body * * { padding:0; }
    body { font:small sans-serif; }
    body>div { border-bottom:1px solid #ddd; }
    h1 { font-weight:normal; }
    h2 { margin-bottom:.8em; }
    h2 span { font-size:80%; color:#666; font-weight:normal; }
    h3 { margin:1em 0 .5em 0; }
    h4 { margin:0 0 .5em 0; font-weight: normal; }
    table { border:1px solid #ccc; border-collapse: collapse; width:100%; background:white; }
    tbody td, tbody th { vertical-align:top; padding:2px 3px; }
    thead th {
      padding:1px 6px 1px 3px; background:#fefefe; text-align:left;
      font-weight:normal; font-size:11px; border:1px solid #ddd;
    }
    tbody th { width:12em; text-align:right; color:#666; padding-right:.5em; }
    #summary { background: #e0ebff; }
    #summary h2 { font-weight: normal; color: #666; }
    #explanation { background:#eee; }
    #instructions { background:#f6f6f6; }
    #summary table { border:none; background:transparent; }
  </style>
</head>
<body>
<div id="summary">
  <h1>It worked!</h1>
  <h2>Congratulations on your first Django-powered page.</h2>
</div>
<div id="instructions">
  <p>
    Of course, you haven't actually done any work yet. Next, start your first app by running <code>python manage.py startapp [app_label]</code>.
  </p>
</div>
<div id="explanation">
  <p>
    You're seeing this message because you have <code>DEBUG = True</code> in your Django settings file and you haven't configured any URLs. Get to work!
  </p>
</div>
</body></html>

观察容器状态:

[root@vm2 web_django]# docker ps  
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                    NAMES
2d549e242606        webdjango_web       "python manage.py ..."   7 minutes ago       Up 50 seconds       0.0.0.0:8000->8000/tcp   webdjango_web_1
ce18133de30c        mysql               "docker-entrypoint..."   About an hour ago   Up 51 seconds       3306/tcp                 webdjango_db_1
[root@vm2 web_django]# docker top 2d549e242606  #查看容器中进程
UID                 PID                 PPID                C                   STIME               TTY                 TIME                CMD
root                20355               20335               0                   08:28               ?                   00:00:00            python manage.py runserver 0.0.0.0:8000
root                20433               20355               1                   08:28               ?                   00:00:05            /usr/local/bin/python manage.py runserver 0.0.0.0:8000
[root@vm2 web_django]# docker exec -it  2d549e242606 df -Th  #容器中执行一条命令并返回
Filesystem          Type     Size  Used Avail Use% Mounted on
overlay             overlay   27G  3.9G   24G  15% /
tmpfs               tmpfs    489M     0  489M   0% /dev
tmpfs               tmpfs    489M     0  489M   0% /sys/fs/cgroup
/dev/mapper/cl-root xfs       27G  3.9G   24G  15% /code
shm                 tmpfs     64M     0   64M   0% /dev/shm
tmpfs               tmpfs    489M     0  489M   0% /sys/firmware
[root@vm2 web_django]# docker diff  2d549e242606  #查看容器变化
C /usr
C /usr/local
C /usr/local/lib
C /usr/local/lib/python2.7
A /usr/local/lib/python2.7/decimal.pyc
A /usr/local/lib/python2.7/UserList.pyc
A /usr/local/lib/python2.7/argparse.pyc
A /usr/local/lib/python2.7/BaseHTTPServer.pyc
C /usr/local/lib/python2.7/wsgiref
A /usr/local/lib/python2.7/wsgiref/__init__.pyc
A /usr/local/lib/python2.7/wsgiref/simple_server.pyc
A /usr/local/lib/python2.7/wsgiref/util.pyc
A /usr/local/lib/python2.7/wsgiref/handlers.pyc
A /usr/local/lib/python2.7/wsgiref/headers.pyc
A /usr/local/lib/python2.7/imghdr.pyc
C /usr/local/lib/python2.7/email
C /usr/local/lib/python2.7/email/mime
A /usr/local/lib/python2.7/email/mime/nonmultipart.pyc
A /usr/local/lib/python2.7/email/mime/message.pyc
A /usr/local/lib/python2.7/email/mime/base.pyc
A /usr/local/lib/python2.7/email/mime/multipart.pyc
A /usr/local/lib/python2.7/email/mime/audio.pyc
A /usr/local/lib/python2.7/email/mime/image.pyc
A /usr/local/lib/python2.7/email/mime/text.pyc
A /usr/local/lib/python2.7/sndhdr.pyc
[root@vm2 web_django]# docker inspect 2d549e242606 
[
    {
        "Id": "2d549e2426067461adc1d635bb5f41db464bf0b8d57fbe57ddd9b5381c67e24e",
        "Created": "2017-09-07T12:22:22.395256812Z",
        "Path": "python",
        "Args": [
            "manage.py",
            "runserver",
            "0.0.0.0:8000"
        ],
        "State": {
            "Status": "running",
            "Running": true,
            "Paused": false,
            "Restarting": false,
            "OOMKilled": false,
            "Dead": false,
            "Pid": 20355,
            "ExitCode": 0,
            "Error": "",
            "StartedAt": "2017-09-07T12:28:57.89394279Z",
            "FinishedAt": "2017-09-07T12:28:52.523766859Z"
        },
        ...
        ...
            "Networks": {
                "webdjango_default": {
                    "IPAMConfig": null,
                    "Links": null,
                    "Aliases": [
                        "web",
                        "2d549e242606"
                    ],
                    "NetworkID": "fc66e543785ee4abb611ad6b3319717fb0f6af56fd710358990e0f0309d34b59",
                    "EndpointID": "54ef82cd8b8bcea0abe60e4c9428ab6401839ac1df6972be6da9b75923372994",
                    "Gateway": "172.18.0.1",
                    "IPAddress": "172.18.0.3",
                    "IPPrefixLen": 16,
                    "IPv6Gateway": "",
                    "GlobalIPv6Address": "",
                    "GlobalIPv6PrefixLen": 0,
                    "MacAddress": "02:42:ac:12:00:03",
                    "DriverOpts": null
                }
            }
        }
    }
]

关闭容器:

[root@vm2 web_django]# docker-compose  down 
Stopping webdjango_web_1 ... done
Stopping webdjango_db_1  ... done
Removing webdjango_web_1     ... done
Removing webdjango_web_run_5 ... done
Removing webdjango_web_run_4 ... done
Removing webdjango_web_run_3 ... done
Removing webdjango_web_run_2 ... done
Removing webdjango_web_run_1 ... done
Removing webdjango_db_1      ... done
Removing network webdjango_default

至此django环境搭建测试完毕。

CentOS 7.3 上用 docker 部署 redis 介绍

Redis最新的版本已经是4.0.1了,我查了下镜像也更新了。于是在本地部署体验下,当然,这篇文章不是来介绍Redis 4.0的新功能,而是来介绍如何用docker来部署的入门级课程。

1. Docker 安装启动

$ yum -y install docker-io
$ service docker start
$ chkconfig docker on

2. 下载镜像

$ docker pull redis

3. 启动容器

这里我把容器的映射建立在/docker/redis/data,/docker/redis/conf目录下面,这两个目录自己创建,配置文件redis.conf从别的途径获取的,启动前,需要对目录加入白名单,不然启动会失败,错误为没有权限

$ chcon -Rt svirt_sandbox_file_t /docker/redis/data

启动语句如下

docker run --name redis -p 6379:6379 
-v /docker/redis/conf/redis.conf:/usr/local/etc/redis/redis.conf 
-v /docker/redis/data:/data 
-d redis redis-server /usr/local/etc/redis/redis.conf

去掉上面的-d参数,可以看见启动日志,如果启动失败,则可以看见错误的日志,也可以用命令查看日志

$ docker logs redis,redis是容器的名字

4. 关闭防火墙

firewall-cmd --zone=public --add-port=6379/tcp --permanent
systemctl restart firewalld

5. 先在本地启动redis客户端

$ docker run -it --link redis:redis --rm redis redis-cli -h redis -p 6379

或者

$ docker exec -it redis /bin/bash
> redis-cli

未分类

6. 用工具进行连接

常用的工具是redis desktop manager,可以很好的管理redis,也可以在上面执行管理的命令。

使用Docker compose配置WordPress运行环境并支持https

最近把 WordPress 迁移到了腾讯云,为了配置方便使用了 docker 来运行,这里记录下配置过程

准备 compose 文件

WordPress 的 docker compose 文件网上有很多,需要一个 mysql 的镜像,还有 WordPress 的镜像,大概长这样:

version: '3'
services:
   db:
     image: mysql:5.7
     volumes:
       - db_data:/var/lib/mysql
     restart: always
     environment:
       MYSQL_ROOT_PASSWORD: somewordpress
       MYSQL_DATABASE: wordpress
       MYSQL_USER: wordpress
       MYSQL_PASSWORD: wordpress
   wordpress:
     depends_on:
       - db
     image: wordpress:latest
     ports:
       - "8000:80"
     restart: always
     environment:
       WORDPRESS_DB_HOST: db:3306
       WORDPRESS_DB_USER: wordpress
       WORDPRESS_DB_PASSWORD: wordpress
volumes:
    db_data:

定制 Dockerfile 添加 https 支持

借助于 letsencrypt 这个项目, 给个人网站添加 letsencrypt 变得十分容易,详细见这篇文章:

如何免费的让网站启用HTTPS: (https://coolshell.cn/articles/18094.html)

大概流程就是安装一个软件包 letsencrypt ,然后配置你的网站信息即可,但是我们的 WordPress 是安装在 docker 里面,所以我们要想办法把这个软件包打进镜像里面。

接下来我们要对 WordPress 这个镜像进行自定义,参考这篇文章:

docker + wordpress + letsencrypt: (https://breeto.id.au/2017/03/docker-wordpress-letsencrypt/)

先定制 Dockerfile,集成 letsencrypt

新建文件夹 wordpress_tls 添加 Dockerfile

FROM wordpress:php7.1
RUN echo "export TERM=xterm LANG=en_US.UTF-8" >> ~/.bashrc 
    && apt-get update && apt-get -y install git 
    && rm -rf "/opt/letsencrypt" 
    && git clone https://github.com/letsencrypt/letsencrypt /opt/letsencrypt 
    && cd /opt/letsencrypt 
    && ./letsencrypt-auto --version

wordpress 官方镜像使用的 ubuntu 源是国外源,打包镜像的速度会让你怀疑人生。可以把宿主机的 ubuntu 源放进 docker 镜像里。

$cp /etc/apt/sources.list ./

修改 Dockerfile

FROM wordpress:php7.1
ADD sources.list /etc/apt/sources.list
RUN apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 
    && apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 3B4FE6ACC0B21F32 // 改成你的 key
RUN echo "export TERM=xterm LANG=en_US.UTF-8" >> ~/.bashrc 
    && apt-get update && apt-get -y install git 
    && rm -rf "/opt/letsencrypt" 
    && git clone https://github.com/letsencrypt/letsencrypt /opt/letsencrypt 
    && cd /opt/letsencrypt 
    && ./letsencrypt-auto --version

添加新的源会有认证的问题,可以参考 http://naveenubuntu.blogspot.com/2011/08/fixing-gpg-keys-in-ubuntu.html 解决

配置 https

启动容器:

$docker-compose up -d

然后配置 https

$docker-compose exec wordpress_tls /opt/letsencrypt/certbot-auto --apache -d your.domain.com --agree-tos -n -m [email protected]

Let’s Encrypt 的证书90天就过期了,过期后执行

$ docker-compose exec wordpress_tls /opt/letsencrypt/certbot-auto renew

来更新,可以把更新脚本写进 crontab
$crontab -e

0 0 1 * * docker-compose exec wordpress_tls /opt/letsencrypt/certbot-auto renew

完整示例

https://github.com/myfjdthink/docker-wordpress

缩减docker镜像大小

镜像是 Docker 运维的基本单元。 优化镜像体积,能够:

  • 缩短部署时的下载时间;

  • 提升安全性,因为可供攻击的目标更少;

  • 减少故障恢复时间;

  • 节省存储开销。

正确认识分层和共享

认清与理解 Docker 镜像的层次结构,是进行镜像优化的前提和基础。

分层

docker 镜像的存储结构是分层次的。 无论底层的文件系统(可选可配置)是基于快照还是分块。

镜像构建的过程里,每步操作都产生一个只读的层:可重用,可复制,但是不可修改。 层次叠加,形成了包含历史(history)结构的镜像。 镜像启动后,产生了容器(container)。容器也是一个单独的层,可读写,但是不持久化,易丢失。

共享

共享,以分层为基础。 以一个镜像为祖先,分别用多种方式做不同操作,各自生成新层,则形成多个新的镜像,祖先镜像则成为共享的部分。 继承自共同的祖先,实现了对共同内容的利用。

未分类

镜像优化

对单个镜像体积做优化,采取方式有两类思路:

  • 选择尽可能小的基础镜像

  • 打包尽可能少的内容入镜像

    • 如:去除或减少编译、测试等中间步骤内容

    • 使用单行命令

工程实践中,还要考虑多镜像复用,尽量:

  • 将能复用的部分放进模版镜像。

选择最小化基础镜像,创建模版镜像

基础镜像需要满足的基本条件:

  • 正确的 init 系统。(discuss)

  • 日志。Docker 的应用程序日志一般默认在标准输出,而系统进程仍然会往 /dev/log 写数据。需要有日志处理程序 syslog

  • 后台任务如 cron

  • 工具进程如 sshd(慎重选择)

这些需求,与在传统的服务器和虚拟机上做部署,是相似的。

如果仅仅基于这种相似性,就选择了使用 CentOS/Debian/Ubuntu 做为基础镜像,那么就有问题了。 首先,这些传统发行版的 Docker 镜像并不能符合基础需求; 其次,这些发行版的体积太大。

所有,仍然需要选择较小的发行版制作初始镜像。

市面上可选的有:

  • phusion/baseimage

  • baseimage-amzn

  • Alpine

  • busybox

使用共同的模版镜像

如果有多个业务需要运维,则在公共基础镜像基础上,构建私有的模版镜像。 共同的模版镜像,能够:

  • 实现镜像共享

  • 减少重复工作。解决边缘问题

  • 减少开发时间。专注于上层应用

  • 减少编译时间。

  • 减少部署时间。基于镜像的层共享

模版镜像的目标是抽取业务的公共部分。

持续的审查和优化

上线后,需要经常关注这些问题:

  • 某个组件放在模版镜像中,现在已经很少用到了?或者反之。

  • 新业务使用到某个公共镜像,效果还不错,要不要推而广之?

解决这些问题,还需要经常复审镜像服用的效率。 简单的方案,可以对各类环境中的模版使用情况进行统计,尽量合并相似的镜像,将使用率较高的组件吸收进模版镜像。

常用考评指标:

  • 开发效率

  • 编译效率

  • 传输效率

  • 存储效率

要不要使用 –flatten

–flatten 是现在还是一个测试特性,可以减少镜像总体积,然而与复用的原则有冲突。 需要更加实际情况去做考虑。

  • 要不要使用 –flatten

为什么选择最小的基础镜像(如alpine)之后,应用镜像反而变大了?

这是因为应用程序需要一系列依赖。 如果使用最小的基础镜像,在制作应用镜像时,比如需要去安装依赖。 安装步骤可能会引入更多不必要的元素。 所以反而体积会超过已包含所需依赖的公共镜像。

centos -> app

alpine -> dependencies -> app  # Too much dependencies

docker容器内通过supervisor来守护进程

安装:

可通过easy install supervisor或pip install supervisor,当然还可以通过下载源码通过Python setup.py install 来安装(注意:要在python2.x下进行安装)

还可以通过linxu下的包管理来安装,如yum install supervisor

使用:

.为要维护的进程创建.ini文件,并放到/etc/supervisor.d目录下
.启动supervisord服务
/usr/bin/supervisord -c /etc/supervisor.conf

可通过supervisorctl status 查看supervisord当前管理的所有进程的状态

遇到的问题:

通过docker run -d 方式启动容器报“Unlinking stale socket /tmp/supervisor.sock”错误,而通过docker run -it 启动后手动执行 /usr/bin/supervisord -c /etc/supervisor.conf则没问题

解决:

将Dockerfile中的CMD [“/usr/bin/supervisord”, “-c”, “/etc/supervisord.conf”] 修改成ENTRYPOINT [“/usr/bin/supervisord”, “-n”, “-c”, “/etc/supervisord.conf”] 重新生成镜像,用该镜像启动容器docker run -d即可,问题解决。

docker 容器基础技术:linux cgroup 简介

Linux cgroups 的全称是 Linux Control Groups,它是 Linux 内核的特性,主要作用是限制、记录和隔离进程组(process groups)使用的物理资源(cpu、memory、IO 等)。

2006 的时候,Google 的一些工程师(主要是 Paul Menage 和 Rohit Seth)启动了这个项目,最初的名字叫 process containers。因为 container 在内核中名字有歧义,2007 的时候改名为 control groups,并合并到 2008 年发布的 2.6.24 内核版本。

最初 cgroups 的版本被称为 v1,这个版本的 cgroups 设计并不友好,理解起来非常困难。后续的开发工作由 Tejun Heo 接管,他重新设计并重写了 cgroups,新版本被称为 v2,并首次出现在 kernel 4.5 版本。

cgroups 从设计之初使命就很明确,为进程提供资源控制,它主要的功能包括:

  • 资源限制:限制进程使用的资源上限,比如最大内存、文件系统缓存使用限制
  • 优先级控制:不同的组可以有不同的优先级,比如 CPU 使用和磁盘 IO 吞吐
  • 审计:计算 group 的资源使用情况,可以用来计费
  • 控制:挂起一组进程,或者重启一组进程

目前 cgroups 已经成为很多技术的基础,比如 LXC、docker、systemd等。

NOTE:资源限制是这篇文章的重点,也是 docker 等容器技术的基础。其他特性可以参考内核 cgroups 文档。

cgroups 核心概念

前面说过,cgroups 是用来对进程进行资源管理的,因此 cgroup 需要考虑如何抽象这两种概念:进程和资源,同时如何组织自己的结构。cgroups 中有几个非常重要的概念:

  • task:任务,对应于系统中运行的一个实体,一般是指进程
  • subsystem:子系统,具体的资源控制器(resource class 或者 resource controller),控制某个特定的资源使用。比如 CPU 子系统可以控制 CPU 时间,memory 子系统可以控制内存使用量
  • cgroup:控制组,一组任务和子系统的关联关系,表示对这些任务进行怎样的资源管理策略
  • hierarchy:层级树,一系列 cgroup 组成的树形结构。每个节点都是一个 cgroup,cgroup 可以有多个子节点,子节点默认会继承父节点的属性。系统中可以有多个 hierarchy

虽然 cgroups 支持 hierarchy,允许不同的子资源挂到不同的目录,但是多个树之间有各种限制,增加了理解和维护的复杂性。在实际使用中,所有的子资源都会统一放到某个路径下(比如 ubuntu16.04 的 /sys/fs/cgroup/),因此本文并不详细介绍多个树的情况,感兴趣的可以参考 RedHat 的这篇文档。

子资源系统(Resource Classes or SubSystem)

目前有下面这些资源子系统:

  • Block IO(blkio):限制块设备(磁盘、SSD、USB 等)的 IO 速率
  • CPU Set(cpuset):限制任务能运行在哪些 CPU 核上
  • CPU Accounting(cpuacct):生成 cgroup 中任务使用 CPU 的报告
  • CPU (CPU):限制调度器分配的 CPU 时间
  • Devices (devices):允许或者拒绝 cgroup 中任务对设备的访问
  • Freezer (freezer):挂起或者重启 cgroup 中的任务
  • Memory (memory):限制 cgroup 中任务使用内存的量,并生成任务当前内存的使用情况报告
  • Network Classifier(net_cls):为 cgroup 中的报文设置上特定的 classid 标志,这样 tc 等工具就能根据标记对网络进行配置
  • Network Priority (net_prio):对每个网络接口设置报文的优先级
  • perf_event:识别任务的 cgroup 成员,可以用来做性能分析

Hierarchy

Linux 进程之间组成一棵树的结构,每个进程(除了 init 根进程之外)都有一个父进程,子进程创建之后会继承父进程的一些属性(比如环境变量,打开的文件描述符等)。

和进程模型类似,只不过 cgroups 是一个森林结构。

使用 cgroups

cgroup 内核功能比较有趣的地方是它没有提供任何的系统调用接口,而是通过文件系统来操作,cgroup 实现了一个

使用 cgroups 的方式有几种:

  • 使用 cgroups 提供的虚拟文件系统,直接通过创建、读写和删除目录、文件来控制 cgroups
  • 使用命令行工具,比如 libcgroup 包提供的 cgcreate、cgexec、cgclassify 命令
  • 使用 rules engine daemon 提供的配置文件
  • 当然,systemd、lxc、docker 这些封装了 cgroups 的软件也能让你通过它们定义的接口控制 cgroups 的内容

直接操作 cgroup 文件系统

查看 cgroups 挂载信息

在 ubuntu 16.04 的机器上,cgroups 已经挂载到文件系统上了,可以通过 mount 命令查看:

➜  ~ mount -t cgroup
cgroup on /sys/fs/cgroup/systemd type cgroup (rw,nosuid,nodev,noexec,relatime,xattr,release_agent=/lib/systemd/systemd-cgroups-agent,name=systemd)
cgroup on /sys/fs/cgroup/net_cls,net_prio type cgroup (rw,nosuid,nodev,noexec,relatime,net_cls,net_prio)
cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup (rw,nosuid,nodev,noexec,relatime,cpu,cpuacct)
cgroup on /sys/fs/cgroup/hugetlb type cgroup (rw,nosuid,nodev,noexec,relatime,hugetlb,release_agent=/run/cgmanager/agents/cgm-release-agent.hugetlb)
cgroup on /sys/fs/cgroup/cpuset type cgroup (rw,nosuid,nodev,noexec,relatime,cpuset,clone_children)
cgroup on /sys/fs/cgroup/freezer type cgroup (rw,nosuid,nodev,noexec,relatime,freezer)
cgroup on /sys/fs/cgroup/perf_event type cgroup (rw,nosuid,nodev,noexec,relatime,perf_event,release_agent=/run/cgmanager/agents/cgm-release-agent.perf_event)
cgroup on /sys/fs/cgroup/pids type cgroup (rw,nosuid,nodev,noexec,relatime,pids,release_agent=/run/cgmanager/agents/cgm-release-agent.pids)
cgroup on /sys/fs/cgroup/devices type cgroup (rw,nosuid,nodev,noexec,relatime,devices)
cgroup on /sys/fs/cgroup/blkio type cgroup (rw,nosuid,nodev,noexec,relatime,blkio)
cgroup on /sys/fs/cgroup/memory type cgroup (rw,nosuid,nodev,noexec,relatime,memory)

如果没有的话,也可以通过以下命令来把想要的 subsystem mount 到系统中:

$ mount -t cgroup -o cpu,cpuset,memory cpu_and_mem /cgroup/cpu_and_mem

上述命令表示把 cpu、cpuset、memory 三个子资源 mount 到 /cgroup/cpu_and_mem 目录下。

每个 cgroup 目录下面都会有描述该 cgroup 的文件,除了每个 cgroup 独特的资源控制文件,还有一些通用的文件:

  • tasks:当前 cgroup 包含的任务(task)pid 列表,把某个进程的 pid 添加到这个文件中就等于把进程移到该 cgroup
  • cgroup.procs:当前 cgroup 中包含的 thread group 列表,使用逻辑和 tasks 相同
  • notify_on_release:0 或者 1,是否在 cgroup 销毁的时候执行 notify。如果为 1,那么当这个 cgroup 最后一个任务离开时(退出或者迁移到其他 cgroup),并且最后一个子 cgroup 被删除时,系统会执行 release_agent 中指定的命令
  • release_agent:需要执行的命令

创建 cgroup

创建 cgroup,可以直接用 mkdir 在对应的子资源中创建一个目录:

➜  ~ mkdir /sys/fs/cgroup/cpu/mycgroup
➜  ~ ls /sys/fs/cgroup/cpu/mycgroup
cgroup.clone_children  cpuacct.stat   cpuacct.usage_percpu  cpu.cfs_quota_us  cpu.stat           tasks
cgroup.procs           cpuacct.usage  cpu.cfs_period_us     cpu.shares        notify_on_release

上面命令在 cpu 子资源中创建了 mycgroup,创建 cgroup 之后,目录中会自动创建需要的文件。我们后面会详细讲解这些文件的含义,目前只需要知道它们能够控制对应子资源就行。

删除 cgroup

删除子资源,就是删除对应的目录:

$ rmdir /sys/fs/cgroup/cpu/mycgroup/

删除之后,如果 tasks 文件中有进程,它们会自动迁移到父 cgroup 中。

设置 cgroup 参数

设置 group 的参数就是直接往特定的文件中写入特定格式的内容,比如要限制 cgroup 能够使用的 CPU 核数:

$ echo 0-1 > /sys/fs/cgroup/cpuset/mycgroup/cpuset.cpus

把进程加入到 cgroup

要把某个已经运行的进程加入到 cgroup,可以直接往需要的 cgroup tasks 文件中写入进程的 PID:

$ echo 2358 > /sys/fs/cgroup/memory/mycgroup/tasks

在 cgroup 中运行进程

如果想直接把进程运行在某个 cgroup,但是运行前还不知道进程的 Pid 应该怎么办呢?

我们可以利用 cgroup 的继承方式来实现,因为子进程会继承父进程的 cgroup,因此我们可以把当前 shell 加入到要想的 cgroup:

echo $$ > /sys/fs/cgroup/cpu/mycgroup/tasks

上面的方案有个缺陷,运行完之后原来的 shell 还在 cgroup 中。如果希望进程运行完不影响当前使用的 shell,可以另起一个临时的 shell:

sh -c "echo $$ > /sys/fs/cgroup/memory/mycgroup/tasks & & stress -m 1"

把进程移动到 cgroup

如果想要把进程移动到另外一个 cgroup,只要使用 echo 把进程 PID 写入到 cgroup tasks 文件中即可,原来 cgroup tasks 文件会自动删除该进程。

cgroup-tools

cgroup-tools 软件包提供了一系列命令可以操作和管理 cgroup,ubuntu 系统中可以通过下面的命令安装:

sudo apt-get install -y cgroup-tools

列出 cgroup mount 信息

最简单的,lssubsys 可以查看系统中存在的 subsystems:

➜  ~ lssubsys -am
cpuset /sys/fs/cgroup/cpuset
cpu,cpuacct /sys/fs/cgroup/cpu,cpuacct
blkio /sys/fs/cgroup/blkio
memory /sys/fs/cgroup/memory
devices /sys/fs/cgroup/devices
freezer /sys/fs/cgroup/freezer
net_cls,net_prio /sys/fs/cgroup/net_cls,net_prio
perf_event /sys/fs/cgroup/perf_event
hugetlb /sys/fs/cgroup/hugetlb
pids /sys/fs/cgroup/pids

创建 cgroup

cgcreate 可以用来为用户创建指定的 cgroups:

➜  sudo cgcreate -a cizixs -t cizixs -g cpu,memory:test1 
➜  ls cpu/test1 
cgroup.clone_children  cpuacct.stat   cpuacct.usage_all     cpuacct.usage_percpu_sys   cpuacct.usage_sys   cpu.cfs_period_us  cpu.shares  notify_on_release
cgroup.procs           cpuacct.usage  cpuacct.usage_percpu  cpuacct.usage_percpu_user  cpuacct.usage_user  cpu.cfs_quota_us   cpu.stat    tasks

上面的命令表示在 /sys/fs/cgroup/cpu 和 /sys/fs/cgroup/memory 目录下面分别创建 test1 目录,也就是为 cpu 和 memory 子资源创建对应的 cgroup。

  • 选项 -t 指定 tasks 文件的用户和组,也就是指定哪些人可以把任务添加到 cgroup 中,默认是从父 cgroup 继承
  • -a 指定除了 tasks 之外所有文件(资源控制文件)的用户和组,也就是哪些人可以管理资源参数
  • -g 指定要添加的 cgroup,冒号前是逗号分割的子资源类型,冒号后面是 cgroup 的路径(这个路径会添加到对应资源 mount 到的目录后面)。也就是说在特定目录下面添加指定的子资源

删除 cgroup

知道怎么创建,也要知道怎么删除。不然系统中保留着太多用不到的 cgroup 浪费系统资源,也会让管理很麻烦。

cgdelete 可以删除对应的 cgroups,它和 cgcreate 命令类似,可以用 -g 指定要删除的 cgroup:

➜  cgroup sudo cgdelete -g cpu,memory:test1
cgdelete 也提供了 -r 参数可以递归地删除某个 cgroup 以及它所有的子 cgroup。

如果被删除的 cgroup 中有任务,这些任务会自动移到父 cgroup 中。

设置 cgroup 的参数

cgset 命令可以设置某个子资源的参数,比如如果要限制某个 cgroup 中任务能使用的 CPU 核数:

$ cgset -r cpuset.cpus=0-1 /mycgroup

-r 后面跟着参数的键值对,每个子资源能够配置的键值对都有自己的规定,我们会在后面详细解释。

cgset 还能够把一个 cgroup 的参数拷贝到另外一个 cgroup 中:

$ cgset --copy-from group1/ group2/

NOTE: cgset 如果设置没有成功也不会报错,请一定要注意。

在某个 cgroup 中运行进程

cgexec 执行某个程序,并把程序添加到对应的 cgroups 中:

➜  cgroup cgexec -g memory,cpu:cizixs bash

cgroups 是可以有层级结构的,因此可以直接创建具有层级关系的 cgroup,然后运行在该 cgroup 中:

$ cgcreate -g memory,cpu:groupname/foo
$ cgexec -g memory,cpu:groupname/foo bash

把已经运行的进程移动到某个 cgroup

要把某个已经存在的程序(能够知道它的 pid)移到某个 cgroup,可以使用 cgclassify 命令:

比如把当前 bash shell 移入到特定的 cgroup 中

$ cgclassify -g memory,cpu:/mycgroup $$

$$ 表示当前进程的 pid 号,上面命令可以方便地测试一些耗费内存或者 CPU 的进程,如果 /mycgroup 对 CPU 和 memory 做了限制。

这个命令也可以同时移动多个进程,它们 pid 之间用空格隔开:

$ cgclassify -g cpu,memory:group1 1701 1138

cgroup 子资源参数详解

每个 subssytem 负责系统的一部分资源管理,又分别提供多个参数可以控制,每个参数对应一个文件,往文件中写入特定格式的内容就能控制该资源。

blkio:限制设备 IO 访问

限制磁盘 IO 有两种方式:权重(weight)和上限(limit)。权重是给不同的应用(或者 cgroup)一个权重值,各个应用按照百分比来使用 IO 资源;上限是直接写死应用读写速率的最大值。

设置 cgroup 访问设备的权重:

设置的权重并不能保证什么,当只有某个应用在读写磁盘时,不管它权重多少,都能使用磁盘。只有当多个应用同时读写磁盘时,才会根据权重为应用分配读写的速率。

  • blkio.weight:设置 cgroup 读写设备的权重,取值范围在 100-1000
  • blkio.weight_device:设置 cgroup 使用某个设备的权重。当访问该设备时,它会使用当前值,覆盖 blkio.weight 的值。内容的格式为 major:minor weight,前面是设备的 major 和 minor 编号,用来唯一表示一个设备,后面是 100-1000 之间的整数值。设备号的分配可以参考:https://www.kernel.org/doc/html/v4.11/admin-guide/devices.html

设置 cgroup 访问设备的限制:

除了设置权重之外,还能设置 cgroup 磁盘的使用上限,保证 cgroup 中的进程读写磁盘的速率不会超过某个值。

  • blkio.throttle.read_bps_device:最多每秒钟从设备读取多少字节
  • blkio.throttle.read_iops_device:最多每秒钟从设备中执行多少次读操作
  • blkio.throttle.write_bps_device:最多每秒钟可以往设备写入多少字节
  • blkio.throttle.write_iops_device:最多每秒钟可以往设备执行多少次写操作

读写字节数的限制格式一样 major:minor bytes_per_second,前面两个数字代表某个设备,后面跟着一个整数,代表每秒读写的字节数,单位为比特,如果需要其他单位(KB、MB等)需要自行转换。比如要限制 /dev/sda 读速率上线为 10 Mbps,可以运行:

$ echo "8:0 10485760" >
/sys/fs/cgroup/blkio/mygroup/blkio.throttle.read_bps_device

iops 代表 IO per second,是每秒钟执行读写的次数,格式为 major:minor operations_per_second。比如,要限制每秒只能写 10 次,可以运行:

$ echo "8:0 10" >
/sys/fs/cgroup/blkio/mygroup/blkio.throttle.write_iops_device

除了限制磁盘使用之外,blkio 还提供了 throttle 规则下磁盘使用的统计数据。

  • blkio.throttle.io_serviced:cgroup 中进程读写磁盘的次数,文件中内容格式为 major:minor operation number,表示对磁盘进行某种操作(read、write、sync、async、total)的次数
  • blkio.throttle.io_service_bytes:和上面类似,不过这里保存的是操作传输的字节数
  • blkio.reset_stats:重置统计数据,往该文件中写入一个整数值即可
  • blkio.time:统计 cgroup 对各个设备的访问时间,格式为 major:minor milliseconds
  • blkio.io_serviced:CFQ 调度器下,cgroup 对设备的各种操作次数,和 blkio.throttle.io_serviced 刚好相反,所有不是 throttle 下的请求
  • blkio.io_services_bytes:CFQ 调度器下,cgroup 对各种设备的操作字节数
  • blkio.sectors:cgroup 中传输的扇区次数,格式为 major:minor sector_count
  • blkio.queued:cgroup IO 请求进队列的次数,格式为 number operation
  • blkio.dequeue:cgroup 的 IO 请求被设备出队列的次数,格式为 major:minor number
  • blkio.avg_queue_size:
  • blkio.merged:cgroup 把 BIOS 请求合并到 IO 操作请求的次数,格式为 number operation
  • blkio.io_wait_time:cgroup 等待队列服务的时间
  • blkio.io_service_time:CFQ 调度器下,cgroup 处理请求的时间(从请求开始调度,到 IO 操作完成)

cpu:限制进程组 CPU 使用

CPU 子资源可以管理 cgroup 中任务使用 CPU 的行为,任务使用 CPU 资源有两种调度方式:完全公平调度(CFS,Completely Fair Scheduler)和 实时调度(RT,Real-Time Scheduler)。前者可以根据权重为任务分配响应的 CPU 时间片,后者能够限制使用 CPU 的核数。

CFS 调优参数:

CFS 调度下,每个 cgroup 都会分配一个权重,但是这个权重并不能保证任务使用 CPU 的具体数据。如果只有一个进程在运行(理论上,现实中机器上不太可能只有一个进程),不管它所在 cgroup 对应的 CPU 权重是多少,都能使用所有的 CPU 资源;在 CPU 资源紧张的情况,内核会根据 cgroup 的权重按照比例分配个给任务各自使用 CPU 的时间片。

CFS 调度模式下,也可以给 cgroup 分配一个使用上限,限制任务能使用 CPU 的核数。

设置 CPU 数字的单位都是微秒(microsecond),用 us 表示。

  • cpu.cfs_quota_us:每个周期 cgroup 中所有任务能使用的 CPU 时间,默认为 -1,表示不限制 CPU 使用。需要配合 cpu.cfs_period_us 一起使用,一般设置为 100000(docker 中设置的值)
  • cpu.cfs_period_us:每个周期中 cgroup 任务可以使用的时间周期,如果想要限制 cgroup 任务每秒钟使用 0.5 秒 CPU,可以在 cpu.cfs_quota_us 为 100000 的情况下把它设置为 50000。如果它的值比 cfs_quota_us 大,表明进程可以使用多个核 CPU,比如 200000 表示进程能够使用 2.0 核
  • cpu.stat:CPU 使用的统计数据,nr_periods 表示已经过去的时间周期;nr_throttled 表示 cgroup 中任务被限制使用 CPU 的次数(因为超过了规定的上限);throttled_time 表示被限制的总时间
  • cpu.shares:cgroup 使用 CPU 时间的权重值。如果两个 cgroup 的权重都设置为 100,那么它们里面的任务同时运行时,使用 CPU 的时间应该是一样的;如果把其中一个权重改为 200,那么它能使用的 CPU 时间将是对方的两倍。

RT 调度模式下的参数:

RT 调度模式下和 CFS 中上限设置类似,区别是它只是限制实时任务的 CPU。

  • cpu.rt_period_us:设置一个周期时间,表示多久 cgroup 能够重新分配 CPU 资源
  • cpu.rt_runtime_us:设置运行时间,表示在周期时间内 cgroup 中任务能访问 CPU 的时间。这个限制是针对单个 CPU 核数的,如果是多核,需要乘以对应的核数

cpuacct: 任务使用 CPU 情况统计

cpuacct 不做任何资源限制,它的功能是资源统计,自动地统计 cgroup 中任务对 CPU 资源的使用情况,统计数据也包括子 cgroup 中的任务。

  • cpuacct.usage:该 cgroup 中所有任务(包括子 cgroup 中的任务,下同)总共使用 CPU 的时间,单位是纳秒(ns)。往文件中写入 0 可以重置统计数据
  • cpuacct.stat:该 cgroup 中所有任务使用 CPU 的user 和 system 时间,也就是用户态 CPU 时间和内核态 CPU 时间
  • cpuacct.usage_percpu:该 cgroup 中所有任务使用各个 CPU 核数的时间,单位为纳秒(ns)

cpuset: cpu 绑定

除了限制 CPU 的使用量,cgroup 还能把任务绑定到特定的 CPU,让它们只运行在这些 CPU 上,这就是 cpuset 子资源的功能。除了 CPU 之外,还能绑定内存节点(memory node)。

NOTE:在把任务加入到 cpuset 的 task 文件之前,用户必须设置 cpuset.cpus 和 cpuset.mems 参数。

  • cpuset.cpus:设置 cgroup 中任务能使用的 CPU,格式为逗号(,)隔开的列表,减号(-)可以表示范围。比如,0-2,7 表示 CPU 第 0,1,2,和 7 核。
  • cpuset.mems:设置 cgroup 中任务能使用的内存节点,和 cpuset.cpus 格式一样
    上面两个是最常用的参数,cpuset 中有很多其他参数,需要对 CPU 调度机制有深入的了解,很少用到,而且我也不懂,所以就不写了,具体可以参考参考文档中 RedHat 网站。

memory:限制内存使用

memory 子资源系统能限制 cgroup 中任务对内存的使用,也能生成它们使用数据的报告。

控制内存使用:

  • memory.limit_in_bytes:cgroup 能使用的内存上限值,默认为字节;也可以添加 k/K、m/M 和 g/G 单位后缀。往文件中写入 -1 来移除设置的上限,表示不对内存做限制
  • memory.memsw.limit_in_bytes:cgroup 能使用的内存加 swap 上限,用法和上面一样。写入 -1 来移除上限
  • memory.failcnt:任务使用内存量达到 limit_in_bytes 上限的次数
  • memory.memsw.failcnt:任务使用内存加 swap 量达到 memsw.limit_in_bytes 上限的次数
  • memory.soft_limit_in_bytes:设置内存软上限。如果内存充足, cgroup 中的任务可以用到 memory.limit_in_bytes 设定的内存上限;当时当内存资源不足时,内核会让任务使用的内存不超过 soft_limit_in_bytes 中的值。文件内容的格式和 limit_in_bytes 一样
  • memory.swappiness:设置内核 swap out 进程内存(而不是从 page cache 中回收页) 的倾向。默认值为 60,低于 60 表示降低倾向,高于 60 表示增加倾向;如果值高于 100,表示允许内核 swap out 进程地址空间的页。如果值为 0 表示倾向很低,而不是禁止该行为。

OOM 操作:

OOM 是 out of memory 的缩写,可以翻译成内存用光。cgroup 可以控制内存用完之后应该怎么处理进程,默认情况下,用光内存的进程会被杀死。

memory.oom_control:是否启动 OOM killer,如果启动(值为 0,是默认情况)超过内存限制的进程会被杀死;如果不启动(值为 1),使用超过限定内存的进程不会被杀死,而是被暂停,直到它释放了内存能够被继续使用。

统计内存使用情况:

  • memory.stat:汇报内存的使用情况,里面的数据包括:
    • cache:页缓存(page cache)字节数,包括 tmpfs(shmem)
    • rss:匿名和 swap cache 字节数,不包括 tmpfs
    • mapped_file:内存映射(memory-mapped)的文件大小,包括 tmpfs,单位是字节
    • pgpgin: paged into 内存的页数
    • pgpgout:paged out 内存的页数
    • swap:使用的 swap 字节数
    • active_anon:活跃的 LRU 列表中匿名和 swap 缓存的字节数,包括 tmpfs
      -inactive_anon:不活跃的 LRU 列表中匿名和 swap 缓存的字节数,包括 tmpfs
    • active_file:活跃 LRU 列表中文件支持的(file-backed)的内存字节数
    • inactive_file:不活跃列表中文件支持的(file-backed)的内存字节数
    • unevictable:不可以回收的内存字节数
  • memory.usage_in_bytes:cgroup 中进程当前使用的总内存字节数
  • memory.memsw.usage_in_bytes:cgroup 中进程当前使用的总内存加上总 swap 字节数
  • memory.max_usage_in_bytes:cgroup 中进程使用的最大内存字节数
  • memory.memsw.max_usage_in_bytes:cgroup 中进程使用的最大内存加 swap 字节数

net_cls:为网络报文分类

net_cls 子资源能够给网络报文打上一个标记(classid),这样内核的 tc(traffic control)模块就能根据这个标记做流量控制。

net_cls.classid:包含一个整数值。从文件中读取是的十进制,写入的时候需要是十六进制。比如,0x100001 写入到文件中,读取的将是 1048577, ip 命令操作的形式为 10:1。

这个值的格式为 0xAAAABBBB,一共 32 位,分成前后两个部分,前置的 0 可以忽略,因此 0x10001 和 0x00010001 一样,表示为 1:1。

net_prio:网络报文优先级

net_prio(Network Priority)子资源能够动态设置 cgroup 中应用在网络接口的优先级。网络优先级是报文的一个属性值,tc可以设置网络的优先级,socket 也可以通过 SO_PRIORITY 选项设置它(但是很少应用会这么做)。

  • net_prio.prioidx:只读文件,里面包含了一个整数值,内核用来标识这个 cgroup
  • net_prio.ifpriomap:网络接口的优先级,里面可以包含很多行,用来为从网络接口中发出去的报文设置优先级。每行的格式为 network_interface priority,比如 echo "eth0 5" > /sys/fs/cgroup/net_prio/mycgroup/net_prio.ifpriomap

devices:设备黑白名单

device 子资源可以允许或者阻止 cgroup 中的任务访问某个设备,也就是黑名单和白名单的作用。

  • devices.allow:cgroup 中的任务能够访问的设备列表,格式为 type major:minor access,
    • type 表示类型,可以为 a(all), c(char), b(block)
    • major:minor 代表设备编号,两个标号都可以用* 代替表示所有,比如 : 代表所有的设备
    • accss 表示访问方式,可以为 r(read),w(write), m(mknod) 的组合
  • devices.deny:cgroup 中任务不能访问的设备,和上面的格式相同
  • devices.list:列出 cgroup 中设备的黑名单和白名单

freezer

freezer 子资源比较特殊,它并不和任何系统资源相关,而是能暂停和恢复 cgroup 中的任务。

  • freezer.state:这个文件值存在于非根 cgroup 中(因为所有的任务默认都在根 cgroup 中,停止所有的任务显然是错误的行为),里面的值表示 cgroup 中进程的状态:
    • FROZEN:cgroup 中任务都被挂起(暂停)
    • FREEZING:cgroup 中任务正在被挂起的过程中
    • THAWED:cgroup 中的任务已经正常恢复

要想挂起某个进程,需要先把它移动到某个有 freezer 的 cgroup 中,然后 Freeze 这个 cgroup。

NOTE:如果某个 cgroup 处于挂起状态,不能往里面添加任务。用户可以写入 FROZEN 和 THAWED 来控制进程挂起和恢复,FREEZING 不受用户控制。

总结

cgroup 提供了强大的功能,能够让我们控制应用的资源使用情况,也能统计资源使用数据,是容器技术的基础。但是 cgroup 整个系统也很复杂,甚至显得有些混乱,目前 cgroup 整个在被重写,新的版本被称为 cgroup V2,而之前的版本也就被称为了 V1。

cgroup 本身并不提供对网络资源的使用控制,只能添加简单的标记和优先级,具体的控制需要借助 linux 的 TC 模块来实现。

使用docker安装apache环境部署wordpress

环境:centos7

首先在DaoCloud注册

#安装、启动docker
curl -sSL https://get.daocloud.io/docker | sh
systemctl docker start
#拉取所需镜像(nginx-proxy、wordpress、mysql)
dao pull daocloud.io/daocloud/nginx-proxy:latest
dao pull daocloud.io/daocloud/dao-wordpress:latest
dao pull daocloud.io/library/mysql:latest
#mysql
docker run  --restart="always" -d 
  -v /data/docker/msqyl:/etc/mysql/conf.d 
  -p 0.0.0.0:3306:3306 --name mysql 
  -e MYSQL_ROOT_PASSWORD=root mysql:latest
默认账号密码:root/rot
#nginx-proxy
docker run --restart="always" -d -p 80:80 -v   
      /var/run/docker.sock:/tmp/docker.sock:ro 
      daocloud.io/daocloud/nginx-proxy

详细的使用方法查看nginx-proxy 使用

#wordpress
docker run --restart="always" --name wordpress --link mysql:mysql -e VIRTUAL_HOST=www.domain.com -v /data/wordpress:/var/www/html -d -p 8080:80 daocloud.io/daocloud/dao-wordpress:latest

www.domain.com 替换成你自己需要绑定的域名,方便nginx-proxy 自动发现

启动后发现没有办法正常访问

apache2配置

进入wordpress容器进行配置

docker exec -it wordpress /bin/bash

写入域名

echo "ServerName www.domain.com" >> /etc/apache2/httpd.conf

重启

/etc/init.d/apache2 restart

最后输入域名或者localhost:8080 进行访问

使用docker部署hexo博客

hexo博客

在文章 用Hexo搭建个人博客(https://blog.xiayyu.me/2017/06/15/hexo-blog/) 中有关于hexo的详细介绍.本文主要介绍如何利用docker方便快捷的搭建静态网页服务器,用来部署我的hexo博客.

hexo服务器

直接上docker-compose.yml

version: '3'
services:
  # 静态网页服务器容器
  blog:
    container_name: blog
    image: nginx:stable-alpine
    restart: unless-stopped
    volumes:
      - blog-sftp:/usr/share/nginx/html:ro
    environment:
      # 以下三个环境变量为jrcs/letsencrypt-nginx-proxy-companion所需
      VIRTUAL_HOST: ${WEB_DOMAIN_NAME}, www.${WEB_DOMAIN_NAME}, blog.${WEB_DOMAIN_NAME}
      LETSENCRYPT_HOST: ${WEB_DOMAIN_NAME}, www.${WEB_DOMAIN_NAME}, blog.${WEB_DOMAIN_NAME}
      LETSENCRYPT_EMAIL: ${LETSENCRYPT_EMAIL}
  # sftp服务器容器用来上传静态网页内容
  sftp:
    container_name: blog-sftp
    image: atmoz/sftp:alpine
    restart: unless-stopped
    volumes:
      - blog-sftp:/home/blog/upload
    ports:
      - "2222:22"
    # cmd: 用户:密码:uid:gid:目录,必须写到目录,否则出现文件权限问题.
    command: blog:${SFTP_PW}:1001:100:upload
# 将网页服务器添加到同一docker网络中
networks:
  default:
    external:
      name: nginx-proxy
# 设置sftp容器和blog容器的共享volume
volumes:
  blog-sftp:

hexo默认支持的部署方式中sftp实现起来方便,特别是对于windows友好.为安全起见我没有直接使用vps的sftp服务,而是通过docker添加了一个sftp服务,并且通过docker volume实现容器间的文件共享.启动上述docker容器之后,只需要在hexo的配置文件_config.yml中填好相应的部署信息就ok了

deploy:
  type: sftp
  host: [hostname]
  user: blog
  pass: [passwd]
  port: 2222
  remotePath: /upload