kubernetes 1.8 高可用安装(二)

2、设置kubeconfig

2.1 设置kubectl的kubeconfig(admin.conf)

# 设置集群参数
kubectl config set-cluster kubernetes 
  --certificate-authority=/etc/kubernetes/pki/ca.pem 
  --embed-certs=true 
  --server=https://master_VIP:6443 
  --kubeconfig=admin.conf


# 设置客户端认证参数
kubectl config set-credentials kubernetes-admin 
  --client-certificate=/etc/kubernetes/pki/admin.pem 
  --embed-certs=true 
  --client-key=/etc/kubernetes/pki/admin-key.pem 
  --kubeconfig=admin.conf

# 设置上下文参数
kubectl config set-context kubernetes-admin@kubernetes 
  --cluster=kubernetes 
  --user=kubernetes-admin 
  --kubeconfig=admin.conf

# 设置默认上下文
kubectl config use-context kubernetes-admin@kubernetes --kubeconfig=admin.conf

2.2 配置 bootstrap.kubeconfig

# 生成配置
cd /etc/kubernetes/
export BOOTSTRAP_TOKEN=$(head -c 16 /dev/urandom | od -An -t x | tr -d ' ')
export KUBE_APISERVER="https://master_VIP:6443"
echo "Token: ${BOOTSTRAP_TOKEN}"

# 生成token文件
cat > token.csv <<EOF
${BOOTSTRAP_TOKEN},kubelet-bootstrap,10001,"system:kubelet-bootstrap"
EOF

#设置集群参数
kubectl config set-cluster kubernetes 
  --certificate-authority=/etc/kubernetes/pki/ca.pem 
  --embed-certs=true 
  --server=${KUBE_APISERVER} 
  --kubeconfig=bootstrap.kubeconfig

# 设置客户端认证参数
 kubectl config set-credentials kubelet-bootstrap 
  --token=${BOOTSTRAP_TOKEN} 
  --kubeconfig=bootstrap.kubeconfig

# 设置上下文参数
 kubectl config set-context default 
  --cluster=kubernetes 
  --user=kubelet-bootstrap 
  --kubeconfig=bootstrap.kubeconfig

# 设置默认上下文
kubectl config use-context default --kubeconfig=bootstrap.kubeconfig

2.3生成kube-proxy.kubeconfig

#设置集群参数
kubectl config set-cluster kubernetes 
  --certificate-authority=/etc/kubernetes/pki/ca.pem 
  --embed-certs=true 
  --server=${KUBE_APISERVER} 
  --kubeconfig=kube-proxy.kubeconfig

# 设置客户端认证参数
kubectl config set-credentials kube-proxy 
  --client-certificate=/etc/kubernetes/pki/kube-proxy.pem 
  --client-key=/etc/kubernetes/pki/kube-proxy-key.pem 
  --embed-certs=true 
  --kubeconfig=kube-proxy.kubeconfig

# 设置上下文参数
kubectl config set-context default 
  --cluster=kubernetes 
  --user=kube-proxy 
  --kubeconfig=kube-proxy.kubeconfig

# 设置默认上下文
kubectl config use-context default --kubeconfig=kube-proxy.kubeconfig

kubernetes 1.8 高可用安装(一)

1、创建证书

1.1 安装cfssl工具

wget https://pkg.cfssl.org/R1.2/cfssl_linux-amd64
wget https://pkg.cfssl.org/R1.2/cfssljson_linux-amd64
chmod +x cfssl_linux-amd64 cfssljson_linux-amd64
mv cfssl_linux-amd64 /usr/local/bin/cfssl
mv cfssljson_linux-amd64 /usr/local/bin/cfssljson

1.2 生成ca证书

创建ca-config.json

{
  "signing": {
    "default": {
      "expiry": "87600h"
    },
    "profiles": {
      "kubernetes": {
        "usages": [
            "signing",
            "key encipherment",
            "server auth",
            "client auth"
        ],
        "expiry": "87600h"
      }
    }
  }
}

创建ca-csr.json

{
  "CN": "kubernetes",
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "CN",
      "ST": "BeiJing",
      "L": "BeiJing",
      "O": "k8s",
      "OU": "System"
    }
  ]
}

生成证书

cfssl gencert -initca ca-csr.json | cfssljson -bare ca

查看ca证书

ca-config.json  ca.csr  ca-csr.json  ca-key.pem  ca.pem

1.3 生成kubernetes证书

创建kubernetes-csr.json

{
    "CN": "kubernetes",
    "hosts": [
        "127.0.0.1",
        "10.96.0.1",
        "master_ip1",
        "master_ip2",
        "master_ip3",
        "master_VIP",
        "localhost",
        "kubernetes",
        "kubernetes.default",
        "kubernetes.default.svc",
        "kubernetes.default.svc.cluster",
        "kubernetes.default.svc.cluster.local"
    ],
    "key": {
        "algo": "rsa",
        "size": 2048
    },
    "names": [
        {
            "C": "CN",
            "ST": "BeiJing",
            "L": "BeiJing",
            "O": "k8s",
            "OU": "System"
        }
    ]
}

master_IP :就是准备跑master的机器

10.96.0.1 : 是集群ip,根据自己环境去修改

生成证书

cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes kubernetes-csr.json | cfssljson -bare kubernetes

查看证书

kubernetes.csr  kubernetes-csr.json  kubernetes-key.pem  kubernetes.pem

1.4生成admin证书

cat <<EOF > admin-csr.json
{
  "CN": "admin",
  "hosts": [],
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "CN",
      "ST": "BeiJing",
      "L": "BeiJing",
      "O": "system:masters",
      "OU": "System"
    }
  ]
}
EOF

生成证书

cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes admin-csr.json | cfssljson -bare admin

1.5 创建kube-controller-manager客户端证书和私钥

创建controller-manager-csr.json

{
  "CN": "system:kube-controller-manager",
  "hosts": [
      "master_ip1",
      "master_ip2",
      "master_ip3",
      "master_VIP"
  ],
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "CN",
      "ST": "BeiJing",
      "L": "BeiJing",
      "O": "system:kube-controller-manager",
      "OU": "System"
    }
  ]
}

生成证书和私钥:

cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes controller-manager-csr.json | cfssljson -bare controller-manager

1.6 创建kube-scheduler客户端证书和私钥

创建scheduler-csr.json

{
  "CN": "system:kube-scheduler",
  "hosts": [
      "master_ip1",
      "master_ip2",
      "master_ip3",
      "master_VIP"
  ],
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "CN",
      "ST": "BeiJing",
      "L": "BeiJing",
      "O": "system:kube-scheduler",
      "OU": "System"
    }
  ]
}

生成证书和私钥:

cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes scheduler-csr.json | cfssljson -bare scheduler

1.7 创建kube-proxy客户端证书和私钥

cat <<EOF > kube-proxy-csr.json
{
  "CN": "system:kube-proxy",
  "hosts": [],
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "CN",
      "ST": "BeiJing",
      "L": "BeiJing",
      "O": "k8s",
      "OU": "System"
    }
  ]
}
EOF

生成证书

cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes  kube-proxy-csr.json | cfssljson -bare kube-proxy

1.8 密钥分发

将证书分发到各个机器

总的证书概览:

  • etcd:使用 ca.pem、kubernetes-key.pem、kubernetes.pem;
  • kube-apiserver:使用 ca.pem、kubernetes-key.pem、kubernetes.pem;
  • kubelet:使用 ca.pem;
  • kube-proxy:使用 ca.pem、kube-proxy-key.pem、kube-proxy.pem;
  • kubectl:使用 ca.pem、admin-key.pem、admin.pem;

证书后缀说明

  • 证书:.crt, .pem
  • 私钥:.key
  • 证书请求:.csr

kubernetes 1.8 高可用安装

1、如何获取rpm包,及制作kubernetes镜像

1.1 官方yum源,有翻墙能力的 请使用

cat <<EOF > /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=http://yum.kubernetes.io/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg
       https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg
EOF

1.2 自行编译生成相关rpm包

下载源码包
git clone 
https://github.com/kubernetes/release
 kubeadm
cd kubeadm/rpm
sh docker-build.sh

结果在 rpm/output/x86_64/yum/

kubectl-1.8.2-0.x86_64.rpm
kubelet-1.8.2-0.x86_64.rpm
kubeadm-1.8.2-0.x86_64.rpm
kubernetes-cni-0.5.1-0.x86_64.rpm

期间会生成一个docker image叫kubelet-rpm-builder,也需要翻墙
我已上传自己私有仓

https://hub.docker.com/r/foxchan/kubelet-rpm-builder/

注意:如果提前下载好kubelet-rpm-builder,需要修改rpm目录下的docker-build.sh 和Dockerfile

Dockerfile修改如下:

FROM foxchan/kubelet-rpm-builder
USER root
ADD entry.sh /root/
COPY ./ /root/rpmbuild/SPECS
ENTRYPOINT ["/root/entry.sh"]

docker-build.sh

#!/bin/sh
set -e
echo "Cleaning output directory..."
sudo rm -rf output/*
mkdir -p output
docker run -ti --rm -v $PWD/output/:/root/rpmbuild/RPMS/ kubelet-rpm-builder $1
sudo chown -R $USER $PWD/output
echo
echo "----------------------------------------"
echo
echo "RPMs written to: "
ls $PWD/output/*/
echo
echo "Yum repodata written to: "
ls $PWD/output/*/repodata/

2、利用github,创建kubernetes镜像

2.1 Dockerfile上传到github

github上新建一个工程,https://github.com/foxchenlei/docker-library

然后在工程中新增: ./kube-apiserver-amd64/Dockerfile,Dockerfile内容为:

FROM gcr.io/google_containers/kube-apiserver-amd64:v1.8.2 
MAINTAINER FoxChan

2.2 Docker Hub上创建Automated build

到docker hub上,Create -> Create Automated Build,新增一个Github类型的自动编译,选择docker-library项目;修改Repository的Name为kube-apiserver-amd64,简单填下描述,这样就创建了一个Automated Build。

进到Repository kube-apiserver-amd64,Build Setting中填写Dockerfile Location为kube-apiserver-amd64,修改Docker Tag Name为1.8.2,Save Change and Trigger;然后点Build Details,可以看到build的过程,状态切为Success就可以了。

2.3 剩下的,就是重复上面的操作,把你需要的镜像全部构建到hub.docker.com,以后需要的时候 就pull下来

微服务架构日志集中化 安装 EFK (Fluentd ElasticSearch Kibana) 采集nginx日志

本文描述如何通过FEK组合集中化nginx的访问日志。本人更喜欢按顺序来命名,所以使用FEK而不是EFK.

首先在nginx服务器上执行以下操作.

安装ruby

http://blog.csdn.net/chenhaifeng2016/article/details/78678680

安装Fluentd

curl -L https://toolbelt.treasuredata.com/sh/install-redhat-td-agent2.sh | sh
systemctl start td-agent

安装插件

td-agent-gem install fluent-plugin-elasticsearch
td-agent-gem install fluent-plugin-typecast

安装ElasticSearch

下载软件包并解压

wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-6.0.0.tar.gz

创建组和用户

groupadd elsearch
useradd elsearch -g elsearch -p elsearch
chown -R elsearch:elsearch /usr/local/src/elsearch

编辑/etc/sysctl.conf

vm.max_map_count=655360

查看一下是否生效

sysctl -p

编辑/etc/security/limits.conf,在文件尾添加以下内容

elsearch soft nofile 65536
elsearch hard nofile 65536

编辑config/elasticsearch.yml,修改以下配置项

network.host: 0.0.0.0

切换到用户elsearch

su elsearch

运行elasticsearch

bin/elasticsearch

如果要以守护进程方式运行加上参数-d

未分类

安装Kibana

下载并解压缩文件。

wget https://artifacts.elastic.co/downloads/kibana/kibana-6.0.0-linux-x86_64.tar.gz

修改配置文件configkibana.yaml

server.port: 5601
server.host: "0.0.0.0"
elasticsearch.url: "http://localhost:9200"

运行kibana

bin/kibana

安装Logstash (这一步非必须)

下载并解压缩文件

wget https://artifacts.elastic.co/downloads/logstash/logstash-6.0.0.tar.gz

创建配置文件logstash.conf

<img src="http://devops.webres.wang/wp-content/uploads/2017/12/2-16.png" alt="" width="340" height="258" class="alignnone size-full wp-image-16270" />

运行./logstash -f logstash.conf

输入hello the world

未分类

在kibana创建索引模式

未分类

在kibana的Discover界面就可以看到信息了

未分类

接下来配置fluentd

编辑配置文件/etc/td-agent/td-agent.conf

[plain] view plain copy
<source>  
  @type tail  
  path /usr/local/nginx/logs/access.log  
  pos_file /usr/local/nginx/logs/access.log.pos  

  tag nginx.access  
  format /^(?<host>[^ ]*) [^ ]* (?<user>[^ ]*) 
(?<time>[^
]*)] "(?<method>S+)(?: +(?<path>[^ ]*) +S*)?" (?<code>[^ ]*) (?<size>[^ ]*)(?: "(?<referer>[^"]*)" "(?<agent>[^"]*)")?$/  
  time_format %d/%b/%Y:%H:%M:%S %z  
</source>  
<match nginx.access>  
  @type elasticsearch  
  host localhost  
  port 9200  
#  index_name fluentd  
  flush_interval 10s  
  logstash_format true  
#  typename fluentd  
</match>  

在/usr/local/nginx/logs/下面创建access.log.pos文件

touch access.log.pos

chmod a+rw access.log.pos

重启fluentd

systemctl restart fluentd

修改nginx.conf, 更改日志格式

[plain] view plain copy
log_format  main  '$remote_addr $http_host [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent"';  

重启nginx

nginx -s reload

通过浏览器访问nginx

查看fluentd的日志

tail -f /var/log/td-agent/td-agent.log

说明fluent读取nginx的access.log成功并发给了elasticsearch.

在kibana查看日志

未分类

参考资料

正则表达式:http://fluentular.herokuapp.com/
http://blog.csdn.net/qq_27252133/article/details/53520416

补充说明:

采集端可以使用logstash, filebeat, 也可以使用fluentbit。因为fluent是CNCF的项目,所以只是为了了解一下。

centos iptables只允许某一ip访问某端口设置方法

首先,清除所有预设置

iptables -F

其次,设置只允许指定ip地址访问指定端口

1、在tcp协议中,禁止所有的ip访问本机的8080端口。

iptables -I INPUT -p tcp --dport 8080 -j DROP

2、允许192.168.1.123访问本机的1521端口

iptables -I INPUT -s 211.211.211.211 -p tcp --dport 8080 -j ACCEPT

最后,保存当前规则

/etc/rc.d/init.d/iptables save 
service iptables restart

添加后打开iptables后如图所示,

-A INPUT -s 192.168.1.123/32 -p tcp -m tcp --dport 1521 -j ACCEPT
-A INPUT -p tcp -m tcp --dport 1521 -j DROP

未分类

Docker容器资源限制测试

Docker运行时的容器本质是进程。在Linux中,通过Namespace进行资源隔离,Cgroups进行资源限制。

一、Docker容器Cpu资源限制测试

容器资源CPU限制设置测试

默认所有的容器对于 CPU 的利用占比都是一样的,-c 或者 –cpu-shares 可以设置 CPU 利用率权重,默认为 1024,可以设置权重为 2 或者更高(单个 CPU 为 1024,两个为 2048,以此类推)。如果设置选项为 0,则系统会忽略该选项并且使用默认值 1024。通过以上设置,只会在 CPU 密集(繁忙)型运行进程时体现出来。当一个 container 空闲时,其它容器都是可以占用 CPU 的。cpu-shares 值为一个相对值,实际 CPU 利用率则取决于系统上运行容器的数量。

假如一个 1core 的主机运行 3 个 container,其中一个 cpu-shares 设置为 1024,而其它 cpu-shares 被设置成 512。当 3 个容器中的进程尝试使用 100% CPU 的时候「尝试使用 100% CPU 很重要,此时才可以体现设置值」,则设置 1024 的容器会占用 50% 的 CPU 时间。如果又添加一个 cpu-shares 为 1024 的 container,那么两个设置为 1024 的容器 CPU 利用占比为 33%,而另外两个则为 16.5%。简单的算法就是,所有设置的值相加,每个容器的占比就是 CPU 的利用率,如果只有一个容器,那么此时它无论设置 512 或者 1024,CPU 利用率都将是 100%。当然,如果主机是 3core,运行 3 个容器,两个 cpu-shares 设置为 512,一个设置为 1024,则此时每个 container 都能占用其中一个 CPU 为 100%。

1.1、通过参数–cpu-shares分配cpu使用权重

现在运行两个测试 container,一个权重设置为 2,一个权重设置 4,启动命令如下:

[root@ok188 ~]# docker run -it -d --cpu-shares 2 --name 2_cpu centos:7 /bin/bash
f3f125f7455974be77e58c0864d045b3b56ae2d007bd9095c47faca50893547c
[root@ok188 ~]# docker run -it -d --cpu-shares 4 --name 4_cpu centos:7 /bin/bash
5e623b55a22ef6d1e41e5978dd1c5d05d743b3a91498697db3b4b9c493f03f8b

通过压测工具如Stress进行压测,Stress使用实例:

  • 产生13个cpu进程4个io进程1分钟后停止运行
[root@ok188 ~]# stress -c 13 -i 4 --verbose --timeout 1m
  • 测试硬盘,通过mkstemp()生成800K大小的文件写入硬盘,对CPU、内存的使用要求很低
[root@ok188 ~]# stress -d 1 --hdd-noclean --hdd-bytes 800k
  • 产生13个进程,每个进程都反复不停的计算由rand ()产生随机数的平方根
[root@ok188 ~]# stress -c 13
  • 向磁盘中写入固定大小的文件,这个文件通过调用mkstemp()产生并保存在当前目录下,默认是文件产生后就被执行unlink(清除)操作,但是可以使用–hdd-bytes选项将产生的文件全部保存在当前目录下,这会将你的磁盘空间逐步耗尽
# 生成小文件
[root@ok188 ~]# stress -d 1 --hdd-noclean --hdd-bytes 13
# 生成大文件
[root@ok188 ~]# stress -d 1 --hdd-noclean --hdd-bytes 3G

对两个容器同时进行 CPU压测,在宿主机中查看两个容器CPU占用情况

[root@ok188 ~]# docker stats 2_cpu
CONTAINER      CPU %         MEM USAGE / LIMIT     MEM %      NET I/O         BLOCK I/O                 
2_cpu          33.31%        24.34MiB / 1.932GiB   1.23%     18.2MB / 322kB   49.1MB / 27.7MB
[root@ok188 ~]# docker stats 4_cpu
CONTAINER     CPU %         MEM USAGE / LIMIT     MEM %      NET I/O          BLOCK I/O                
4_cpu         65.96%        38.82MiB / 1.932GiB   1.96%     18.3MB / 451kB    8.19kB / 27.7MB

观察以上结果发现容器名为4_cpu权重比2_cpu大2倍,所以4_cpu可使用的cpu更多。
停止压测名为4_cpu的容器, 在宿主机中查看两个容器CPU占用情况

[root@ok188 ~]# docker stats 2_cpu
CONTAINER      CPU %         MEM USAGE / LIMIT     MEM %      NET I/O         BLOCK I/O                 
2_cpu          99.50%        24.34MiB / 1.932GiB   1.23%     18.2MB / 322kB   49.1MB / 27.7MB
[root@ok188 ~]# docker stats 4_cpu
CONTAINER     CPU %         MEM USAGE / LIMIT     MEM %      NET I/O          BLOCK I/O                
4_cpu         0.00%         37.5MiB / 1.932GiB   1.90%     18.3MB / 452kB    8.19kB / 27.7MB

结论参数cpu-shares 只是设置cpu使用权重,只会在 CPU 密集(繁忙)型运行进程时体现出来。当一个 container 空闲时,其它容器都是可以占用 CPU 的。
备注:物理主机为多核的情况下,显示的cpu使用比例有差异,本人测试主机为单核。

1.2、通过 –cpu-period & –cpu-quota 限制容器的 CPU 使用上限

默认的 CPU CFS「Completely Fair Scheduler」period 是 100ms。我们可以通过 –cpu-period 值限制容器的 CPU 使用。一般 –cpu-period 配合 –cpu-quota 一起使用。

为啥把这两个参数放一起呢?因为这两个参数是相互配合的,–cpu-period和–cpu-quota的这种配置叫Ceiling Enforcement Tunable Parameters,–cpu-shares的这种配置叫Relative Shares Tunable Parameters。–cpu-period是用来指定容器对CPU的使用要在多长时间内做一次重新分配,而–cpu-quota是用来指定在这个周期内,最多可以有多少时间用来跑这个容器。跟–cpu-shares不同的是这种配置是指定一个绝对值,而且没有弹性在里面,容器对CPU资源的使用绝对不会超过配置的值。

设置 cpu-period 为 100000,cpu-quota 为 50000,表示最多可以使用 cpu到50%。

[root@nsj-13-58 ~]# docker run -it --cpu-period=100000 --cpu-quota=50000 --name 0.5_p_cpu centos:7 /bin/bash

压测容器,并查看CPU占用情况:

[root@ok188 ~]# docker stats 0.5_p_cpu
CONTAINER       CPU %     MEM USAGE / LIMIT     MEM %         NET I/O             BLOCK I/O
0.5_p_cpu       50.33%    22.12MiB / 1.932GiB   1.12%         18.3MB / 348kB      524kB / 27.7MB

通过以上测试可以得知,–cpu-period 结合 –cpu-quota 配置是固定的,无论宿主机系统 CPU 是闲还是繁忙,如上配置,容器最多只能使用 CPU 到 50%。

二、Docker容器Memory资源限制测试

默认启动一个container,对于容器的内存是没有任何限制的。

2.1、默认启动一个 container,对于容器的内存是没有任何限制的。

[root@ok188 ~]# docker run -it -d --name no_limit_memory centos:7 /bin/bash
[root@ok188 ~]# docker stats no_limit_memory
CONTAINER           CPU %      MEM USAGE / LIMIT   MEM %      NET I/O             BLOCK I/O
no_limit_memory     0.00%      932KiB / 1.932GiB   0.05%      586B / 0B           8.19kB / 0B         
[root@ok188 ~]# free -g
              total        used        free      shared  buff/cache   available
Mem:              1           0           0           0           1           1
Swap:             1           0           1

MEM LIMIT显示的是容器宿主机的内存大小,Mem+Swap的总大小

2.2、通过 -m 参数限制内存大小

设置-m值为500Mb,表示容器程序使用内存受限。按照官方文档的理解,如果指定 -m 内存限制时不添加 –memory-swap 选项,则表示容器中程序可以使用 500m内存和500m swap 内存。那么容器里程序可以跑到500m*2=1g后才会被oom给杀死。

[root@ok188 ~]# docker run -it -d -m 500m --name limit_memory_1 centos:7 /bin/bash     
21eb95cffa45972603cb0e67b7ee0724d019cd7182fa5668bf07665ddf4f83cc
[root@ok188 ~]# docker stats limit_memory_1
CONTAINER           CPU %       MEM USAGE / LIMIT   MEM %      NET I/O             BLOCK I/O
limit_memory_1      0.00%       916KiB / 500MiB       0.09%      586B / 0B           0B / 0B

在libcontainer源码里

memory.memsw.limit_in_bytes

值是被设置成我们指定的内存参数的两倍。

其中代码如下:

// By default, MemorySwap is set to twice the size of RAM.
// If you want to omit MemorySwap, set it to `-1'.
if d.c.MemorySwap != -1 {
if err := writeFile(dir, "memory.memsw.limit_in_bytes", strconv.FormatInt(d.c.Memory*2, 10)); err != nil {
return err
}

使用压测工具进行压测,当压测值是 memory + swap之和上限时,则容器中的进程会被直接 OOM kill。

2.3、参数–memory-swappiness=0 表示禁用容器 swap 功能。

[root@ok188 ~]# docker run -it -d -m 500m --memory-swappiness=0 --name limit_memory_noswap_1 centos:7 /bin/bash

使用压测工具进行压测,当压测值是 1G ,则容器中的进程会被直接 OOM kill。查看容器内系统日志:

2.4、指定限制内存大小并且设置 memory-swap 值为 -1

表示容器程序使用内存受限,而 swap 空间使用不受限制(宿主 swap 支持使用多少则容器即可使用多少。如果 –memory-swap 设置小于 –memory 则设置不生效,使用默认设置)。–memory-swap -1

[root@ok188 ~]# docker run -it -d -m 500m --memory-swap -1  --name limit_memory_2 centos:7 /bin/bash

2.5、指定限制内存大小并且设置 memory-swap 值

指定限制内存大小500Mb并且设置 memory-swap 值400Mb当压测值是900Mb时,则容器中的进程会被直接 OOM kill。

[root@ok188 ~]# docker run -it -d -m 500m --memory-swap 400m  --name limit_memory_3 centos:7 /bin/bash

备注:实际生产环境不推荐使用swap功能,建议直接禁用swap功能。

2.6、参数–oom-kill-disable ,加上之后则达到限制内存之后也不会被 kill

正常情况不添加 –oom-kill-disable 容器程序内存使用超过限制后则会直接 OOM kill,加上之后则达到限制内存之后也不会被 kill。

[root@ok188 ~]# docker run -it -d -m 500m --oom-kill-disable --name limit_memory_4 centos:7 /bin/bash

==注意如果是以下的这种没有对容器作任何资源限制的情况,添加 –oom-kill-disable 选项就比较危险了:==

[root@ok188 ~]# docker run -it -d --oom-kill-disable --name limit_memory_5 centos:7 /bin/bash

因为此时容器内存没有限制,使用的上限值是物理内存的上限值。 并且不会被 oom kill,此时系统则会 kill 系统进程用于释放内存。

后记:目前 Docker 支持的资源限制选项

未分类

附Docker容器限制源码路径:

github.com/opencontainers/runc/libcontainer/cgroups/fs

Ubuntu 17.10 用 apt 搭建 lamp 环境、安装 phpmyadmin、redis 服务+扩展、mysql 扩展、开启错误提示、配置虚拟主机

一、最终环境

Ubuntu17.10、Apache2.4.27、MySQL5.7.20、PHP7.1.8

二、安装 apache

官方源有,直接安装:

sudo apt-get install apache2

三、安装 mysql

官方源有,直接安装:

sudo apt-get install mysql-server

安装期间会提示设置 MySQL administrator 的密码

============================================

PS:需要什么软件或包,直接用 apt-cache 搜索

apt-cache search <关键词>

确认包名后,直接用 apt-get install 安装。

四、安装 php

官方源有 php7.1,直接安装:

sudo apt-get install php7.1 php7.1-dev

注意带上版本号,不然默认安装 7.x。php7.1 是主程序,php7.1-dev 是 7.1 版的开发包(有 phpize、php-config 等等,phpize 可以为 已编译好的 php 加载外挂模块,php-config 可以获得 php 的详细配置)。

============================================

如果要装 php5.6 的话,推荐这个 PPA 源:ppa:ondrej/php。这个源有 php5.6 和 php7.x 以及绝大多数的 php 扩展,包括 redis、memcache、mongodb 等等。

添加 ppa:ondrej/php 源:

sudo add-apt-repository ppa:ondrej/php
sudo apt-get update

安装 php5.6:

sudo apt-get install php5.6 php5.6-dev

五、重启 apache

sudo /etc/init.d/apache2 restart

更多选项:

Usage: apache2 {start|stop|graceful-stop|restart|reload|force-reload}

六、检查 apache

访问 http://localhost/。这是 apache 服务器的默认页在 /var/www/html 下,里面还介绍了 apche 的相关配置文件。

未分类

七、检查 mysql

终端输入 mysql,跟着打两个 Tab,看到所有有关 mysql 的命令包:

mingc@mingc-GE60-2PL:~$ mysql
mysql                      mysql_install_db
mysqladmin                 mysqloptimize
mysqlanalyze               mysql_plugin
mysqlbinlog                mysqlpump
mysqlcheck                 mysqlrepair
mysql_config_editor        mysqlreport
mysqld                     mysql_secure_installation
mysqld_multi               mysqlshow
mysqld_safe                mysqlslap
mysqldump                  mysql_ssl_rsa_setup
mysqldumpslow              mysql_tzinfo_to_sql
mysql_embedded             mysql_upgrade
mysqlimport               

输入 mysql -u<你的账号> -p ,回车,输入密码,进入 mysql:

mingc@mingc-GE60-2PL:~$ mysql -uroot -p
Enter password:
Welcome to the MySQL monitor.  Commands end with ; or g.
Your MySQL connection id is 73
Server version: 5.7.20-0ubuntu0.17.10.1 (Ubuntu)

Copyright (c) 2000, 2017, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or 'h' for help. Type 'c' to clear the current input statement.

mysql>

八、检查 php

php -v

不出意外的话,应该显示 php 的版本

============================================

也可以查看 apache 和 mysql 版本:

apache2 -v
mysql -V

九、创建 php 探针

sudo vim /var/www/html/info.php

添加如下内容:

<?php
phpinfo();

改变所有者:

sudo chown www-data:www-data /var/www/html/info.php

访问 http://localhost/info.php,得到有关 PHP 的详细页面。

未分类

十、安装 phpmyadmin

sudo apt-get install phpmyadmin

安装期间,

询问要连接的服务器,选择 apache2;

询问创建 phpmyadmin 的数据库,选择“是”;

询问设置登录 phpmyadmin 的用户和密码。

然后浏览器访问:http://localhost/phpmyadmin

未分类

十一、安装 redis 服务和 redis 扩展

官方源有,直接安装:

(1)redis 服务器

sudo apt-get install redis-server

期间,一些需要的工具包也会自动安装。安装好后,默认自启动、后台运行,配置文件在 /etc/redis 目录下。

(2)php 的 redis 扩展模块

sudo apt-get install php-redis

其他的扩展也可以这么装,利用 apt-cache search 搜索包名,再用 apt-get install 安装。

重启 apche, sudo /etc/init.d/apache2 restart

访问 http://localhost/info.php,确认 redis 扩展:

未分类

测试一下:

终端输入 redis-,跟着打两个 Tab 键,看到有关 redis 的命令和工具:

mingc@mingc-GE60-2PL:~$ redis-
redis-benchmark  redis-check-rdb  redis-server    
redis-check-aof  redis-cli

redis-cli 是 访问 redis-server 的客户端接口,执行 redis-cli 即可登录到 redis 服务器:

mingc@mingc-GE60-2PL:~$ redis-cli
127.0.0.1:6379>

更多 redis-cli 的命令选项,使用 redis-cli –help 查看。

再写一段 php 脚本测试一下, vim /var/www/html/test_redis.php

内容如下:

<?php
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
echo "Connection to server sucessfully";
echo "Server is running: " . $redis->ping(); 

十二、安装 mysql 扩展

在上面的 http://localhost/info.php 里可以看到,没有 mysql 扩展。虽说 php7 弃用了 mysql 扩展,但有些旧项目还是需要的。装一下 mysql 扩展。

这里选择编译安装(通过 apt 安装的 php-mysql 扩展,如果是 php7.x,那就是装 pdo 和 pdo_mysql 扩展)

(1)直接从 PECL 官方站 搜索 mysql,找到 MySQL 扩展的 页面 ,点击页面的 [ Browse Source ],选择最新的 commit、下载 tar.gz 包。我下载后的包名为 mysql-fae9884.tar.gz。

(2)编译安装:

tar -xf mysql-fae9884.tar.gz
cd mysql-fae9884
phpize
./configure --with-php-config=/usr/bin/php-config
sudo make && sudo make install

前面装 php7.1 的时候,也装了 php7.1-dev,这里面就有 phpize 和 php-config。而 –with-php-config 是 php-config 程序(命令)的位置,可以使用 whereis php-config 查看。

编译安装成功后,会有成功提示 和 mysql 扩展模块(.so 文件)的路径:

Build complete.
Don't forget to run 'make test'.

Installing shared extensions:     /usr/lib/php/20160303/  

然后,编辑 php 用于 apache 的配置文件 /etc/php/7.1/apache2/php.ini,在最后添加一行:

extension=mysql.so

重启 apache, sudo /etc/init.d/apache2 restart

访问 http://localhost/info.php,确认下有了 mysql 扩展。

未分类

写一段 php 脚本测试: vim /etc/www/html/test_mysql.php

内容如下:

<?php
$mysql = mysql_connect('127.0.0.1', 'root', 'root');
if(!$mysql) {
    die(mysql_error($mysql));
}
echo 'Ok' . "rn";

由于 mysql 模块是手动编译安装的,所以默认不会进入 php 的命令行配置。

下面把 mysql 模块加入到 php 的 cli(命令行) 配置,让 php 的命令行模式也支持 mysql 模块:

sudo vim /etc/php/7.1/mods-available/mysql.ini

编辑内容如下:

extension=mysql.so

建立符号链接:

sudo ln -s /etc/php/7.1/mods-available/mysql.ini /etc/php/7.1/cli/conf.d/mysql.ini

检查一下,确认 php 命令行模式启用了 mysql 扩展模块:

php -m | grep mysql  

十三、开启 php 和 apache 的错误提示

默认不显示 php 错误,下面开启。

(1)修改 php 的配置文件,在 /etc/php/7.1/apache2 下,打开 php.ini。

(2)搜索 display_errors = Off,修改为 On

(3)搜索 error_reporting = E_ALL & ~E_NOTICE,修改为 E_ALL | E_STRICT(搜不到的话就搜短一点:“error_reporting =”)。

(4)修改 apache 的配置文件,在 /etc/apache2 下,打开 apache.conf。

(5)文件最后添加两行:

php_flag display_errors        on
php_value error_reporting       2039

(6)重启 apache:

sudo /etc/init.d/apache2 restart

十四、创建虚拟主机

创建虚拟主机自然用 apache 配置,了解一下 apache 的配置目录:

/etc/apache2
├── apache2.conf      # 主配置文件,其他的一些配置文件通过 Include 指令包含进来
├── conf-available    # 所有可用的配置文件(里面的 *.conf 文件内容几乎都被默认注释了)
├── conf-enabled      # 可用的配置文件中,启用了哪些,一般都是符号链接、指向上面的 conf-available 目录里每个 *.conf 文件
├── envvars           # 环境变量
├── magic
├── mods-available    # 所有可用的模块
├── mods-enabled      # 哪些模块被启用了
├── ports.conf        # 定义端口监听
├── sites-available   # 重点来了:所有可用的站点
└── sites-enabled     # 哪些站点被启用了

步骤简单,就是在 sites-available 目录定义站点配置文件,然后在 sites-enabled 里建立指向这个配置文件的符号链接。

(1)建配置

cd /etc/apache2/sites-available/
sudo cp 000-default.conf my.site.conf
vim my.site.conf

修改如下(注意别把注释与指令行放在一行,不然会有语法错误;精简版在后面):

<VirtualHost *:80>
    # The ServerName directive sets the request scheme, hostname and port that
    # the server uses to identify itself. This is used when creating
    # redirection URLs. In the context of virtual hosts, the ServerName
    # specifies what hostname must appear in the request's Host: header to
    # match this virtual host. For the default virtual host (this file) this
    # value is not decisive as it is used as a last resort host regardless.
    # However, you must set it for any further virtual host explicitly.

    # 域名
    ServerName my.site
    # 域名别名,可以设置多个,空格隔开
    ServerAlias my.site

    ServerAdmin webmaster@localhost

    # 站点根目录
    DocumentRoot /var/www/my.site
    <Directory "/var/www/my.site/">
        # 启用符号链接
        Options FollowSymLinks

        DirectoryIndex index.php index.html index.htm

        # 注意这个配置,会影响本地目录下的 .htaccess 的启用
        AllowOverride All

        Order deny,allow
        Allow from All

        # 限制访问目录,多个目录用冒号隔开
        # php_admin_value open_basedir "/var/www/my.site/:/tmp:/usr/lib/php/"
    </Directory>

    # Available loglevels: trace8, ..., trace1, debug, info, notice, warn,
    # error, crit, alert, emerg.
    # It is also possible to configure the loglevel for particular
    # modules, e.g.
    #LogLevel info ssl:warn

    ErrorLog ${APACHE_LOG_DIR}/error.log
    CustomLog ${APACHE_LOG_DIR}/access.log combined

    # For most configuration files from conf-available/, which are
    # enabled or disabled at a global level, it is possible to
    # include a line for only one particular virtual host. For example the
    # following line enables the CGI configuration for this host only
    # after it has been globally disabled with "a2disconf".
    #Include conf-available/serve-cgi-bin.conf
</VirtualHost>

# vim: syntax=apache ts=4 sw=4 sts=4 sr noet

精简版(去掉了多余注释):

<VirtualHost *:80>
    ServerName my.site
    ServerAlias my.site
    ServerAdmin webmaster@localhost
    DocumentRoot /var/www/my.site
    <Directory "/var/www/my.site/">
        Options FollowSymLinks
        DirectoryIndex index.php index.html index.htm
        AllowOverride All
        Order deny,allow
        Allow from All
        # php_admin_value open_basedir "/var/www/my.site/:/tmp:/usr/lib/php/"
    </Directory>
    ErrorLog ${APACHE_LOG_DIR}/error.log
    CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>

(2)建符号链接

sudo ln -s /etc/apache2/sites-available/my.site.conf /etc/apache2/sites-enabled/my.site.conf

(3)检查语法

终端执行:

apachectl configtest

(4)添加 hosts 解析

vim /etc/hosts ,添加一行:

127.0.0.1   my.site

(5)重启 apache

sudo /etc/init.d/apache2 restart

然后,按照配置的站点目录 /etc/www/my.site,创建目录和测试文件:

sudo makedir -p /etc/www/my.site
cd /etc/www/my.site/
sudo echo "<h2>Welcome to my.site!</h2>" > index.html

浏览器访问 http://my.site,

未分类

OK,完成~~

十五、相关链接

PHP 及扩展的 PPA 源:https://launchpad.net/~ondrej/+archive/ubuntu/php/+index?batch=75&memo=75&start=75

centos7下部署iptables环境纪录(关闭默认的firewalle)

CentOS7默认的防火墙不是iptables,而是firewall.
由于习惯了用iptables作为防火墙,所以在安装好centos7系统后,会将默认的firewall关闭,并另安装iptables进行防火墙规则设定

下面介绍centos7关闭firewall安装iptables,并且开启80端口、3306端口的操作记录:

[root@localhost ~]# cat /etc/redhat-release
CentOS Linux release 7.2.1511 (Core)

1、关闭firewall:

[root@localhost ~]# systemctl stop firewalld.service            //停止firewall
[root@localhost ~]# systemctl disable firewalld.service        //禁止firewall开机启动

2、安装iptables防火墙

[root@localhost ~]# yum install iptables-services            //安装
[root@localhost ~]# vim /etc/sysconfig/iptables              //编辑防火墙配置文件
# Firewall configuration written by system-config-firewall
# Manual customization of this file is not recommended.
*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
-A INPUT -p icmp -j ACCEPT
-A INPUT -i lo -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 22 -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 80 -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 3306 -j ACCEPT
-A INPUT -j REJECT --reject-with icmp-host-prohibited
-A FORWARD -j REJECT --reject-with icmp-host-prohibited
COMMIT
[root@localhost ~]# systemctl restart iptables.service                  //最后重启防火墙使配置生效
[root@localhost ~]# systemctl enable iptables.service                  //设置防火墙开机启动
[root@localhost ~]# iptables -L                 //查看防火墙规则,默认的是-t filter,如果是nat表查看,即iptables -t nat -L

3、关闭SELINUX

[root@localhost ~]# vim /etc/selinux/config
#SELINUX=enforcing           //注释掉
#SELINUXTYPE=targeted           //注释掉
SELINUX=disabled              //增加
[root@localhost ~]# setenforce 0       //使配置立即生效

docker容器跨服务器的迁移方式export和save

程序放在docker里面迁移起来很是方便,像是以前的话,需要重新部署环境和静态文件。 放在docker里面的话,只是需要export备份封装后,scp、rsync迁移到别的服务器就可以了。

[root@zhdy01 ~]# docker ps -a

CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES

6712e7adee85        665e                "/bin/bash"         28 seconds ago      Up 24 seconds                           youthful_perlman

[root@zhdy01 ~]# docker export 6712 > centos7_nginx.tar

[root@zhdy01 ~]# du -sh centos7_nginx.tar 

359M centos7_nginx.tar

然后把这个centos7_nginx.tar 传到别的服务器上。

[root@zhdy01 ~]# cat centos7_nginx.tar |docker import - c7_nginx
sha256:da676fc85a74162743df4d18d7983017e011b288a95cc503edd4fe229f207d25
[root@zhdy01 ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
c7_nginx latest da676fc85a74 8 seconds ago 364MB
centos7_nginx latest 665e56a46000 6 days ago 385MB

上面的方式是用docker export。 export是当前的状态,docker save 是针对镜像images。

主要的区别是 save是可以回滚以前的配置。 export 只是当前的。

咱们用save 看看备份效果。 是1.1G ,这里是包含那些记录的。刚才咱们用export测试的时候,会发现文件只有300M左右。

来看看效果:(漫长的等待。。。)

[root@zhdy01 ~]# docker save centos7_nginx >centos7_nn.tar
[root@zhdy01 ~]# du -sh *
273M centos7_net.tar
359M centos7_nginx.tar
1.1G centos7_nn.tar

我估计如果有分布式文件系统 ,比如mfs,nfs。可以更好的试试用docker的数据卷来做本地文件夹和容器内的关联。 这样的话,备份更加自定义了。 毕竟环境这东西不会变,变的只是data数据,然后文件目录又在分布式文件里面,可以更好做迁移。只要那边启动一个环境,目录一关联就可以了。

备份迁移的方式自己选,推荐用export,毕竟save太大了,对于历史也没啥用处 !

对于数据安全关注更深的话,可以用docker volumes这样的数据映射。

如何编写 Dockerfile 文件创建 Docker 镜像

一、前言

承接上篇文章 docker 镜像与容器,本篇来讲讲如何创建 Dockerfile 来构建一个镜像。上篇文章有讲到构建一个自定义镜像是手动去构建的,虽然步骤清晰,但是操作比较繁琐,镜像分发起来也不是很方便,所以有必要用一种更好的办法去替换这种模式去创建自定义镜像,于是 Dockerfile 就是最优替代方案。废话少说,现在就来看看如何编写一个 Dockerfile 文件并创建容器镜像的,先说明一个本篇文章的运行环境吧,有看过上篇文章的朋友应该知道,我用的 docker 的镜像加速地址是阿里云的,我觉得这是我用 docker 最无痛的环境了。

二、Dockerfile 示例

# Base images 基础镜像
FROM centos

#MAINTAINER 维护者信息
MAINTAINER lorenwe 

#ENV 设置环境变量
ENV PATH /usr/local/nginx/sbin:$PATH

#ADD  文件放在当前目录下,拷过去会自动解压
ADD nginx-1.13.7.tar.gz /tmp/

#RUN 执行以下命令
RUN rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7 
    && yum update -y 
    && yum install -y vim less wget curl gcc automake autoconf libtool make gcc-c++ zlib zlib-devel openssl openssl-devel perl perl-devel pcre pcre-devel libxslt libxslt-devel 
    && yum clean all 
    && rm -rf /usr/local/src/*
RUN useradd -s /sbin/nologin -M www

#WORKDIR 相当于cd
WORKDIR /tmp/nginx-1.13.7

RUN ./configure --prefix=/usr/local/nginx --user=www --group=www --with-http_ssl_module --with-pcre && make && make install

RUN cd / && rm -rf /tmp/

COPY nginx.conf /usr/local/nginx/conf/

#EXPOSE 映射端口
EXPOSE 80 443

#ENTRYPOINT 运行以下命令
ENTRYPOINT ["nginx"]

#CMD 运行以下命令
CMD ["-h"]

以上代码示例是我编写的一个认为很有代表性的 dockerfile 文件,涉及到的内容不多,但基本上把所有 dockerfile 指令都用上了,也包含一些细节方面的东西,为了达到示例的效果所以并不是最简洁的 dockerfile,建立一个文件夹将以上 dockerfile 放在该文件内,再去 nginx 官网把 nginx 源码包下来放到该文件夹内,之后再在该文件夹内打开命令行窗口,最好是以管理员权限打开命令行窗口,以免出现一些权限问题的错误,此时的目录结构应该是以下样子的

未分类

三、指令分析

FROM 表示的是这个 dockerfile 构建镜像的基础镜像是什么,有点像代码里面类的继承那样的关系,基础镜像所拥有的功能在新构建出来的镜像中也是存在的,一般用作于基础镜像都是最干净的没有经过任何三方修改过的,比如我用的就是最基础的 centos,这里有必要说明一下,因为我用的镜像加速源是阿里云的,所以我 pull 下来的 centos 是自带阿里云的 yum 源的镜像,如果你用的不是阿里云的镜像加速源,pull 下来的镜像 yum 源也不一样,到时 yum 安装软件的时候可能会遇到很多问题(你懂得)。

MAINTAINER 就是维护者信息了,填自己名字就可了,不用说什么了

ENV 设置环境变量,简单点说就是设置这个能够帮助系统找到所需要运行的软件,比如我上面写的是 “ENV PATH /usr/local/nginx/sbin:$PATH”,这句话的意思就是告诉系统如果运行一个没有指定路径的程序时可以从 /usr/local/nginx/sbin 这个路径里面找,只有设置了这个,后面才可以直接使用 ngixn 命令来启动 nginx,不然的话系统会提示找不到应用。

ADD 顾名思义,就是添加文件的功能了,但是他比普通的添加做的事情多一点,源文件可以是一个文件,或者是一个 URL 都行,如果源文件是一个压缩包,在构建镜像的时候会自动的把压缩包解压开来,示例我写的是 ‘ADD nginx-1.13.7.tar.gz /tmp/’ 其中 nginx-1.13.7.tar.gz 这个压缩包是必须要在 dockefile 文件目录内的,不在 dockerfile 文件目录内的 比如你写完整路径 D:test/nginx-1.13.7.tar.gz 都是会提示找不到文件的。

RUN 就是执行命令的意思了,RUN 可以执行多条命令, 用 && 隔开就行,如果命令太长要换行的话在末尾加上 ‘’ 就可以换行命令,RUN 的含义非常简单,就是执行命令,但其中有一些细节还是需要注意的,现在就通过上面的示例来看看需要注意的地方有哪些吧。其中 RUN rpm –import /etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7 的作用就是导入软件包签名来验证软件包是否被修改过了,为做到安全除了系统要官方的之外软件也要保证是可信的。yum update -y 升级所有包,改变软件设置和系统设置,系统版本内核都升级,我们知道 linux 的软件存在依赖关系,有时我们安装新的软件他所依赖的工具软件也需要是最新的,如果没有用这个命令去更新原来的软件包,就很容易造成我们新安装上的软件出问题,报错提示不明显的情况下我们更是难找到问题了,为避免此类情况发生我们还是先更新一下软件包和系统,虽然这会使 docker 构建镜像时变慢但也是值得的,至于后面的命令自然是安装各种工具库了,接着来看这句 yum clean all ,把所有的 yum 缓存清掉,这可以减少构建出来的镜像大小,rm -rf /usr/local/src/ 清除用户源码文件,都是起到减少构建镜像大小的作用。RUN 指令是可以分步写的,比如上面的 RUN 可以拆成以下这样:

# 不推荐
RUN rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7 
RUN yum update -y 
RUN yum install -y vim less wget curl gcc automake autoconf libtool make gcc-c++ zlib zlib-devel openssl openssl-devel perl perl-devel pcre pcre-devel libxslt libxslt-devel 
RUN yum clean all 
RUN rm -rf /usr/local/src/*

这样也是可以的,但是最好不要这样,因为 dockerfile 构建镜像时每执行一个关键指令都会去创建一个镜像版本,这有点像 git 的版本管理,比如执行完第一个 RUN 命令后在执行第二个 RUN 命令时是会在一个新的镜像版本中执行,这会导致 yum clean all 这个命令失效,没有起到精简镜像的作用,虽然不推荐多写几个 RUN,但也不是说把所有的操作都放在一个 RUN 里面,这里有个原则就是把所有相关的操作都放在同一个 RUN 里面,就比如我把 yum 更新,安装工具库,清除缓存放在一个 RUN 里面,后面的编译安装 nginx 放在另外一个 RUN 里面。

WORKDIR 表示镜像活动目录变换到指定目录,就相当于 linux 里面 cd 到指定目录一样,其实完全没有必要使用这个指令的,在需要时可以直接使用 cd 命令就行,因为这里使用了 WORKDIR,所以后面的 RUN 编译安装 nginx 不用切换目录,讲到这里又想起了另外一个问题,如下:

RUN cd /tmp/nginx-1.13.7

RUN ./configure

RUN ./configure这样可不可以呢,我想前面看懂的朋友应该知道答案了吧,这里还是再啰嗦一下,这样是会报找不到 configure 文件错误的,原因很简单,因为这个两个命令都不是在同一个镜像中执行的,第一个镜像 cd 进入的目录并不代表后面的镜像也进入了。
COPY 这个指令很简单,就是把文件拷贝到镜像中的某个目录,注意源文件也是需要在 dockerfile 所在目录的,示例的意思是拷贝一份 nginx 配置文件,现在就在 dockerfile 所在目录创建这个文件

user  www;
worker_processes  2;
daemon off;

pid        logs/nginx.pid;

events {    
    worker_connections  1024;
}

http {
    include       mime.types;
    default_type  application/octet-stream;

    sendfile        on;
    keepalive_timeout  65;
    server {
        listen       80;
        server_name  localhost;
        location / {
            root   html;
            index  index.html index.htm;
        }
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
    }
}

配置很简单,就是对官方的配置文件把注释去掉了,注意里面的 daemon off; 配置,意思是关闭 nginx 后台运行,原因在上一篇文章中讲过,这里再来絮叨一下,容器默认会把容器内部第一个进程是否活动作为docker容器是否正在运行的依据,如果 docker 容器运行完就退出了,那么docker容器便会直接退出,docker run 的时候把 command 作为容器内部命令,如果使用 nginx,那么 nginx 程序将后台运行,这个时候 nginx 并不是第一个执行的程序,而是执行的 bash,这个 bash 执行了 nginx 指令后就挂了,所以容器也就退出了,如果我们设置了 daemon off 后启动 nginx 那么 nginx 就会一直占用命令窗口,自然 bash 没法退出了所以容器一直保持活动状态。

EXPOSE 示例注释写的是映射端口,但我觉得用暴露端口来形容更合适,因为在使用 dockerfile 创建容器的时候不会映射任何端口,映射端口是在用 docker run 的时候来指定映射的端口,比如我把容器的 80 端口映射到本机的 8080 端口,要映射成功就要先把端口暴露出来,有点类似于防火墙的功能,把部分端口打开。

ENTRYPOINT 和 CMD 要放在一起来说,这两者的功能都类似,但又有相对独特的地方,他们的作用都是让镜像在创建容器时运行里面的命令。当然前提是这个镜像是使用这个 dockerfile 构建的,也就是说在执行 docker run 时 ENTRYPOINT 和 CMD 里面的命令是会执行的,两者是可以单独使用,并不一定要同时存在,当然这两者还是有区别的。

先从 CMD 说吧,CMD 的一个特点就是可被覆盖,比如把之前的 dockerfile 的 ENTRYPOINT 这一行删除,留下 CMD 填写[“nginx”],构建好镜像后直接使用 docker run lorenwe/centos_nginx 命令执行的话通过 docker ps 可以看到容器正常运行了,启动命令也是 “ngixn”,但是我们使用 docker run lorenwe/centos_nginx bin/bash 来启动的话通过 docker ps 查看到启动命令变成了 bin/bash,这就说明了 dockerfile 的 CMD 指令是可被覆盖的,也可以把他看做是容器启动的一个默认命令,可以手动修改的。

而 ENTRYPOINT 恰恰相反,他是不能被覆盖,也就是说指定了值后再启动容器时不管你后面写的什么 ENTRYPOINT 里面的命令一定会执行,通常 ENTRYPOINT 用法是为某个镜像指定必须运行的应用,例如我这里构建的是一个 centos_nginx 镜像,也就是说这个镜像只运行 ngixn,那么我就可以在 ENTRYPOINT 写上[“nginx”],有些人在构建自己的基础镜像时(基础镜像只安装了一些必要的库)就只有 CMD 并写上 [‘bin/bash’],当 ENTRYPOINT 和 CMD 都存在时 CMD 中的命令会以 ENTRYPOINT 中命令的参数形式来启动容器,例如上面的示例 dockerfile,在启动容器时会以命令为 nginx -h 来启动容器,遗憾的是这样不能保持容器运行,所以可以这样启动 docker run -it lorenwe/centos_nginx -c /usr/local/nginx/conf/nginx.conf,那么容器启动时运行的命令就是 nginx -c /usr/local/nginx/conf/nginx.conf,是不是很有意思,可以自定义启动参数了。

当然还有一些没有用到的指令:

ARG,ARG指令用以定义构建时需要的参数,比如可以在 dockerfile中写上这句 ARG a_nother_name=a_default_value,ARG指令定义的参数,在docker build命令中以 –build -arg a_name=a_value 形式赋值,这个用的一般比较少。

VOLUME,VOLUME指令创建一个可以从本地主机或其他容器挂载的挂载点,用法是比较多的,都知道 docker 做应用容器比较方便,其实 docker 也可做数据容器,创建数据容器镜像的 dockerfile 就主要是用 VOLUME 指令,要讲明 VOLUME 用法有必要在开一篇文章,再此就不做介绍了,
USER,USER用来切换运行属主身份的。docker 默认是使用 root 用户,但若不需要,建议切换使用者身分,毕竟 root 权限太大了,使用上有安全的风险。LABEL,定义一个 image 标签。

四、构建演示

dockerfile 构建镜像的命令很简单,在我的示例中我的命令是 “docker build -t lorenwe/centos_nginx . “,注意后面的点不能省略,表示的从当前目录中寻找 dockerfile 来构建镜像

D:dockerlorenwe>docker build -t lorenwe/centos_nginx .
Sending build context to Docker daemon  995.8kB
Step 1/13 : FROM centos
 ---> d123f4e55e12
Step 2/13 : MAINTAINER lorenwe
 ---> Running in e5c7274f50e8
 ---> 606f7222e69a
Removing intermediate container e5c7274f50e8
Step 3/13 : ENV PATH /usr/local/nginx/sbin:$PATH
 ---> Running in 23716b428809
 ---> 5d8ee1b5a899
         ....
Successfully built eaee6b40b151
Successfully tagged lorenwe/centos_nginx:latest

看到以上内容就说明成功,构建过程可能需要一点点时间,毕竟要安装一些软件,如果你跟我一样是配置的阿里云的容器源构建时应该不会出现什么问题,因为我之前是有拉取过 centos ,所以在 build 时直接使用本地的 centos,如果你没有拉取过 centos,那么在 build 时还会把 centos 拉取下来

D:dockerlorenwe>docker images
REPOSITORY               TAG     IMAGE ID       CREATED          SIZE
lorenwe/centos_nginx     latest  eaee6b40b151   7 minutes ago    427MB
lorenwe/centos_net_tools latest  35f8073cede1   6 days ago       277MB
centos                   latest  d123f4e55e12   3 weeks ago      197MB
d4w/nsenter              latest  9e4f13a0901e   14 months ago    83.8kB

D:dockerlorenwe>docker run -itd --name nginx1 lorenwe/centos_nginx
15d4f108dab7c2f276209ebeb501cac0d3be828e1e81bae22d3fd97c617439eb

D:dockerlorenwe>docker ps
CONTAINER ID    IMAGE    COMMAND     CREATED    STATUS     PORTS     NAMES

D:dockerlorenwe>docker ps -a
CONTAINER ID   IMAGE                 COMMAND    CREATED   STATUS   PORTS   NAMES
15d4f108dab7   lorenwe/centos_nginx  "nginx -h"                            nginx1

D:dockerlorenwe>docker run -itd --name nginx2 lorenwe/centos_nginx -c /usr/local/nginx/conf/nginx.conf
b6b0e962ca3056d67c24145b08975ffddb9cc050fce5f09f65310fb323ffc1c3

D:dockerlorenwe>docker ps
CONTAINER ID   IMAGE                 COMMAND        CREATED    STATUS    PORTS     NAMES
b6b0e962ca30   lorenwe/centos_nginx  "nginx -c /usr/loc..."              80/tcp    nginx2

D:dockerlorenwe>docker run -itd -p 8080:80 --name nginx3 lorenwe/centos_nginx -c /usr/local/nginx/conf/nginx.conf
2f6997745641e3e3edbbfe5213e6235cab3b5a929f116a2c132df504156090c6

D:dockerlorenwe>docker ps
CONTAINER ID   IMAGE                 COMMAND    CREATED   STATUS     PORTS                  NAMES
2f6997745641   lorenwe/centos_nginx  "nginx -c /usr/loc..."          0.0.0.0:8080->80/tcp   nginx3
b6b0e962ca30   lorenwe/centos_nginx  "nginx -c /usr/loc..."          80/tcp                 nginx2

D:dockerlorenwe>docker stop nginx2
nginx2

其中 “docker run -itd -p 8080:80 –name nginx3 lorenwe/centos_nginx -c /usr/local/nginx/conf/nginx.conf” 中的 -p 8080:80 表示把主机的 8080 端口映射到容器的 80 端口,因为之前我们在 dockerfile 中把 80 端口暴露出来了,做好端口映射后现在就可以在主机中打开浏览器访问 127.0.0.1:8080 就能看到 nginx 的欢迎页面了 (^v^).

D:dockerlorenwe>docker run -itd -v D:/docker/lorenwe/html:/usr/local/nginx/html  -p 8081:80 --name nginx4 lorenwe/centos_nginx -c /usr/local/nginx/conf/nginx.conf
cd2d4eb70a39057aed3bfcb64e1f03433e2054d7ff5d50098f49d2e6f2d9e02e

我再在原来的参数中加入了 -v 参数,其作用就是把一个本地主机的目录挂载到容器内部,这个目录是一个共享的状态,两边都可以进行修改,这就是容器的共享卷,其作用就不言而喻了,现在我们在 D:dockerlorenwe 的目录下新建一个叫 html 的文件夹,再在 html 文件夹内新建一个 index.html 随便写上一点内容后再去主机浏览器上访问一下 127.0.0.1:8081 看看是不是你想要看到内容。虽然通过 -v 参数可以满足大部分应用场景,但是 docker 的 VOLUME 还有其他更好用法,欲知后事如何,请看下回分解!