docker RUN ENTRYPOINT CMD命令

运行时机

RUN在Dockerfile构建镜像的过程(Build)中运行,最终被commit的到镜像.

ENTRYPOINT和CMD在容器运行(run、start)时运行

都有shell和exec形式

shell形式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
RUN command param1 param2
CMD command param1 param2
ENTRYPOINT command param1 param2
# ENTRYPOINT的Shell格式会忽略任何CMD或docker run提供的参数
#eg
RUN apt-get install python3
ENTRYPOINT echo "Hello world"
CMD echo "Hello world"
ENV name Cloud Man
ENTRYPOINT echo "Hello, $name"
# shell 格式底层会调用 /bin/sh -c <command>
# 所以执行 docker run <image> 将输出
# Hello, Cloud Man

shell形式还有一个严重的问题:由于其默认使用/bin/sh来运行命令,如果镜像中不包含/bin/sh,容器会无法启动.

exec形式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
RUN ["executable", "param1", "param2"]
ENTRYPOINT ["executable", "param1", "param2"]
CMD ["executable", "param1", "param2"]
#eg
RUN ["apt-get", "install", "python3"]
ENTRYPOINT ["/bin/echo", "Hello world"]
CMD ["/bin/echo", "Hello world"]
ENV name Cloud Man
ENTRYPOINT ["/bin/echo", "Hello, $name"]
# Hello, $name
ENV name Cloud Man
ENTRYPOINT ["/bin/sh", "-c", "echo Hello, $name"]
# Hello, Cloud Man

CMD还多了一种用于为ENTRYPOINT提供参数的形式:
此时 ENTRYPOINT 必须使用 Exec 格式
参数格式

1
CMD ["param1","param2"]

shell形式和exec的形式的本质区别在于shell形式提供了默认的指令/bin/sh -c,所以其指定的command将在shell的环境下运行.因此指定command的pid将不会是1,因为pid为1的是shell,command进程是shell的子进程.

exec形式则不然,但由于exec指定的命令不由shell启动,因此也就无法使用shell中的环境变量,如$HOME.如果希望能够使用环境变量,可以指定命令为sh:CMD [ “sh”, “-c”, “echo”, “$HOME” ]

重载问题

Dockerfile中只有最后一个ENTRYPOINT指令会生效,其他会被重载.

Dockerfile中只有最后一个CMD指令会生效,其他会被重载.

CMD指定的命令可以被docker run传递的命令覆盖.

1
2
3
4
5
6
CMD echo "Hello world"
# 运行容器 docker run -it [image] 将输出:
# Hello world
docker run -it [image] /bin/bash
# CMD 会被忽略掉,命令 bash 将被执行
# root@10a32dc7d3d3:/#

ENTRYPOINT指定的命令不会被docker run传递的命令覆盖.容器名后面的所有内容都当成参数传递给其指定的命令.

1
2
3
4
5
6
7
8
ENTRYPOINT ["/bin/echo", "Hello"]
CMD ["echo","world"]
# 当容器通过 docker run -it [image] 启动时,输出为:
# Hello echo world
# 而如果通过 docker run -it [image] CloudMan 启动,则输出为:
# Hello CloudMan

当然,ENTRYPOINT指定的命令并不是不能重载的,只需指定–entrypoint来重载即可.

最佳实践

  1. 使用 RUN 指令安装应用和软件包,构建镜像.

  2. 如果 Docker 镜像的用途是运行应用程序或服务,比如运行一个 MySQL,应该优先使用 Exec 格式的 ENTRYPOINT 指令.CMD 可为 ENTRYPOINT 提供额外的默认参数,同时可利用 docker run 命令行替换默认参数.

  3. 如果想为容器设置默认的启动命令,可使用 CMD 指令.用户可在 docker run 命令行中替换此默认命令.

    ENTRYPOINT和CMD进行组合,运行shell脚本

    1
    2
    3
    4
    5
    6
    7
    FROM busybox
    MAINTAINER femn
    COPY ./entrypoint.sh /
    RUN chmod +x /entrypoint.sh
    ENTRYPOINT ["/entrypoint.sh"]
    CMD ["echo","CMD"]

entrypoint.sh如下:

1
2
3
4
5
#!/bin/sh
set -e
echo "ENTRYPOINT"
exec "$@"
# 脚本中使用exec "$@"来运行CMD中的命令

运行时则输出 ENTRYPOINT CMD,而运行的参数 都是传递给entrypoint.sh的.

Share Comments

docker容器与镜像

docker镜像:业务代码和运行环境进行整体的打包.

1.1快速创建环境(开发,测试,生产)

1.2整体交付(运行环境+代码(之前只是代码))

1.3可保证环境(开发,测试,生产)的一致性

1.4更好的完成devops

镜像(Image)就是一堆只读层(read-only layer)的统一视角

这些层是Docker内部的实现细节,并且能够在主机的文件系统上访问到,统一文件系统(union file system)技术能够将不同的层整合成一个文件系统,为这些层提供了一个统一的视角,这样就隐藏了多层的存在,在用户的角度看来,只存在一个文件系统,你可以在你的主机文件系统上找到有关这些层的文件.它们存在于/var/lib/docker/aufs目录下

1
sudo tree -L 1 /var/lib/docker/

容器=镜像+读写层.并且容器的定义并没有提及是否要运行容器

1
2
3
4
5
6
# 创建容器
docker create <image_id>
# 运行容器
docker start <image_id>
# 上两条命令组合下如下命令
docker run <image_id>

所以容器(container)的定义和镜像(image)几乎一模一样,也是一堆层的统一视角,唯一区别在于容器的最上面那一层是可读可写的.

镜像分层技术:AUFS (Another UnionFS)支持将多个目录挂载到同一个虚拟目录下.已构建的镜像会设置成只读模式,read-write操作是在read-only上的一种增量操作.

把一个镜像创造成一个容器才能往里写东西.
所谓UnionFS就是把不同物理位置的目录合并mount到同一个目录中.UnionFS的一个最主要的应用是,把一张CD/DVD和一个硬盘目录给联合 mount在一起
然后,你就可以对这个只读的CD/DVD上的文件进行修改(当然,修改的文件存于硬盘上的目录里)

docker容器管理技术,是内核技术,容器本身是进程 其启动程序就是容器应用进程,容器就是为了主进程而存在的,主进程退出,容器就失去了存在的意义,从而退出,其它辅助进程不是它需要关心的东西

它并不是虚拟技术(VM),docker最关键的点是提出了docker images标准化,image打包了应用:如nginx镜像,通过镜像启动一个nginx容器,其实就是在主机上启动了一个nignx进程.
容器不等于微服务,推荐只运行一个服务,如果运行多个服务,需要结合进程管理工具(supervisor,S6),
因为容器本身就是进程,所以数据库容器也可以运行,但需要对数据做好保护(volume).

既然是进程,那么在启动容器的时候,需要指定所运行的程序及参数.CMD 指令就是用于指定默认的容器主进程的启动命令的,
在运行时可以指定新的命令来替代镜像设置中的这个默认命令,比如,ubuntu 镜像默认的 CMD 是 /bin/bash,如果我们直接 docker run -it ubuntu 的话,会直接进入 bash.

容器中的应用都应该以前台执行,而不是像虚拟机、物理机里面那样,用 upstart/systemd 去启动后台服务,容器内没有后台服务的概念.

ubuntu install docker

1
2
3
4
5
6
7
8
9
10
11
12
sudo apt-get update
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
sudo add-apt-repository "deb [arch=amd64] \
https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
sudo apt-get update
sudo apt-cache policy docker-ce
sudo apt-get install -y docker-ce
sudo systemctl status docker
sudo usermod -aG docker $(whoami)
service docker start
docker images
# 虚拟机是用此方法下载 当前用户就可以使用whoami

debain install docker

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 坑一
sudo apt-get update
sudo apt-get install docker.io
apt-get purge docker.io*
# 坑二
apt-get dist-upgrade -y
apt-key adv --keyserver hkp://pgp.mit.edu:80 --recv-keys \
58118E89F3A912897C070ADBF76221572C52609D
echo "deb https://apt.dockerproject.org/repo debian-jessie main" | \
tee /etc/apt/sources.list.d/docker.list
apt-get update
apt-get install docker-engine -y
apt-get purge docker-engine -y
# 成功
apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 \
--recv-keys 58118E89F3A912897C070ADBF76221572C52609D
echo "deb https://apt.dockerproject.org/repo ubuntu-trusty main " > \
/etc/apt/sources.list.d/docker.list
apt-get update
apt-get install docker-engine=1.10.1-0~trusty
apt install docker.io
docker -v
# 本机是此方法下载docker 必须是root身份才能使用

启动docker服务

docker官方镜像 类似AppStore 是由各自官方机构做的镜像认证后,上传到docker官方镜像的,这个仓库的地址是在国外,所以下载的速度会慢点
利用镜像的分层技术,如果主机上已有layer(层)存在,那只会下载新增加的layer(类似git代码提交机制)

1
2
3
4
5
6
7
8
9
service docker start
docker port nostalgic_morse 5000 # 查看主机端口在容器的映射情况
# modify docker daemon启动参数 Docker的配置文件
vim /usr/lib/systemd/system/docker.service
# centos ubuntu:/lib/systemd/system/docker.service
ExecStart=之前的不要动加上 --insecure-registry=0.0.0.0/0
systemctl daemon-reload
systemctl restart docker
ps aux |grep docker #看参数有没有正确的添加.

容器操作命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
docker run -dit -p 80:80 --name app nginx
# -d后台运行 -it此时的容器就有了标准的输入和输出
docker create -p 80:80 --name app nginx
# 创建容器
docker start app #运行容器
docker commit <container-id>
# 将容器的可读写层转换为一个只读层,这样就把一个容器转换成了不可变的镜像
# 用Dockerfile 生成镜像,命名为app
docker build -t app .
docker exec -it app bash
# 在运行中的容器执行一个新进程
# 以bash的方式登陆到容器里面 exit 从容器中退出
docker exec -it app bash ls /tmp
docker rename app new_app #重命容器名
docker stop/kill/pause/unpause/restart <container_id>
# 对容器中的进程发信号
docker inspect/stats/port/ps/top/dip/dpid <container-id>
# 查看容器状态
docker top app #查看当前容器跑了多少进程
docker ps # 列出所有运行中的容器,这隐藏了非运行态容器的存在
docker ps -a # 查看所有容器
docker inspect nginx/app |grep -i memory # 查看指定镜像/容器
# 会提取出容器或者镜像最顶层的元数据
docker stats $(docker ps | awk 'NR>1 {print $NF}')
docker update -m 256m app #更新容器信息
docker rm -f -v app # 移除构成容器的可读写层
# -f 强制删除 -v 连同Volumes也一起删除
# -v如果这个容器有Volume这个文件存在的话,也一起删除掉
docker copy 本地文件 容器路径
docker copy app:/usr/share/ngxin/html/index.html .
docker copy index.html app:/tmp
docker export app (把容器保存成tar文件)
docker import app.tar (把tar文件导会到镜像列表)

镜像命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
docker commit -a admin@femnyy.com -m 'app commit image' app app:2.0
# -a作者 -m提交时的标记 app容器名 app:2.0镜像名
# 用容器创建镜像 推荐用Dockerfile
docker save nginx >nginx.tar
# 镜像 -->tar包
docker rmi nginx
# 删除镜像
# 1.此镜像是其他镜像的父镜像
# 2.有容器使用镜像 镜像已经被创建为容器 不能删除本地镜像
docker load < nginx.tar
# tar包--导入到镜像列表中
docker images
# 列出了所有顶层(top-level)镜像
docker history <image_id>
# 列出镜像的所有层
docker pull nginx
# 下载镜像
docker tag nginx 192.168.1.116:5000/nginx:last
# 镜像的重命名(标记镜像)
docker push 192.168.1.116:5000/nginx:last # 本机测试的成功
# 上传镜像
docker search nginx
# 查看镜像

容器生成镜像

# 进入容器
docker exec -it docker_web_1 bash
# 在容器中进行操作
pip install gevent==1.3.3
# 退出容器
exit

docker ps |grep docker_web
# 得到 conta id: fdeb8e0c67ee 
docker images |grep docker_web 
# 得到repository:docker_web:latest

docker history docker_web:latest |wc -l
docker commit -m "pip install gevent==1.3.3" fdeb8e0c67ee docker_web:latest
docker history docker_web:latest |wc -l

容器与镜像

镜像不能直接访问,只有启动成容器,才可以访问.镜像和容器的关系,就像是面向对象程序设计中的类和实例一样,镜像是静态的定义,容器是镜像运行时的实体.容器可以被创建、启动、停止、删除、暂停等.同一个 Images 可生成多个不同配置的 Container.

容器连接(相当于进程间的通信吧)

1
2
3
4
5
6
sudo docker run -d --name db training/postgres
# 指定了 training/webapp 镜像,并且这个镜像已经包含了简单的 Python Flask web 应用程序
# 最后,我们指定了我们容器要运行的命令: python app.py.这样我们的 web 应用就启动了
sudo docker run -d -P --name web --link db:db training/webapp python app.py
docker ps
# db和web,我们还在名字列中可以看到web容器也显示db/web.这告诉我们web容器和db容器是父/子关系.

Volume(持久化容器数据,以及容器之间共享数据)

保留容器的数据

当容器删除时,就是杀死进程,只要我们的数据在,当再启动时,数据又回来了,但我们想在容器中产生的数据保留起来

容器间的数据共享(root用户下,主机巻)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
docker run -d -v /web_data:/tmp:ro --name data-container nginx
cd /web_data
mkdir files
touch test.file
docker exec -it data-container ls /tmp
#第二个容器的数据来自 第一个容器的数据
docker run -d --volumes-from data-container --name web-container nginx
#第三个容器的数据来自 第一个容器的数据
docker run -d --volumes-from data-container --name web-container-2 nginx
# 查看容器 有没有第一个容器中的文件
docker exec -it web-container ls /tmp
# 验证只读
docker exec -it web-container bash
cd /tmp
touch a.txt
exit
# 在宿主机添加一个新文件,相当于往三个容器分发信息.
cd /web_data
touch a.txt

Volume分为容器卷(在Dockerfile写了VOLUME)和主机卷

容器卷的定义的目录,将会自动保存在宿主机的/var/lib/docker/volumes目录下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
mkdir docker
cd docker && vim Dockerfile
FROM centos:7
RUN yum -y install epel-release && \
yum -y install nginx && \
yum clean all
EXPOSE 80 443
VOLUME ["/usr/share/nginx/html"]
CMD ["nginx","-g", "daemon off;"]
# 用Dockerfile 生成镜像app
docker build -t app .
cd /var/lib/docker/volumes
docker rm -f web-container data-container
docker run -d --name app app
ll #会有一个随机生成字符串的目录 VOLUME的东西,将会同步到这个目录下的_data目录下
docker rm -f -v app # -v

主机卷(/web-data),这个不仅仅会在/var/lib/docker/volumes:/usr/share/nginx/html保留,双保险.

1
docker run -d -p 80:80 --name app -v /web-data:/tmp app

备份app容器中 /dbdata容器巻中的数据–>打包在容器中的/backup/backup.tar–>将此包copy到主机的当前目录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 通过ubuntu这个镜像生成一个容器
# --rm 这个容器退出 就把这个容器删掉
# 使用 --volumes-from 标记来创建一个加载 dbdata 容器卷的容器:app
docker run --rm --volume-from app -v ${pwd}:/backup ubuntu \
tar cvfz /backup/backup.tar /dbdata
# 1.数据恢复
docker run --rm --volume-from app -v ${pwd}:/backup busybox \
bash -c "cd /dbdata && tar zxvf /backup/backup.tar --strip 1"
# 2
# 如果要恢复数据到一个容器,首先创建一个带有空数据卷的容器 dbdata2
sudo docker run -v /dbdata --name dbdata2 ubuntu /bin/bash
# 然后创建另一个容器,挂载 dbdata2 容器卷中的数据卷
# 并使用 untar 解压备份文件到挂载的容器卷中.
sudo docker run --volumes-from dbdata2 -v $(pwd):/backup busybox tar xvf
/backup/backup.tar
#为了查看/验证恢复的数据,可以再启动一个容器挂载同样的容器卷来查看
sudo docker run --volumes-from dbdata2 busybox /bin/ls /dbdata

Docker的网络模式:nat,host,container,none,overlay

默认启动的是nat

nat需要进行端口的管理,通过docker容器转发的功能,转发到宿主机的网卡上,每次启动容器的时候,它的IP会变:

1
2
3
4
5
6
7
8
9
10
11
docker run -d -p 80:80 --name app nginx
ps -aux |grep docker
# 得到如下信息
/usr/bin/docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 80 \
-container-ip 172.17.0.3 -container-port 80
# 但我试了两次 IP都没有变
docker restart app
/usr/bin/docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 80 \
-container-ip 172.17.0.3 -container-port 80
docker stop app
docker start app

host:容器中的网卡直接与宿主机的网卡沟通:只能使用80端口,但可以固定容器的IP.攻击了容器也就相当于攻击了宿主机.

1
2
3
4
docker run -d --name app2 --net=host nginx
docker ps -a
# 查看端口是否已经冲突,如果冲突则这个容器会启动失败 状态为Exited
docker start -a app2 # 端口也占用

container模式,用于容器之间经常通信的情况:

1
2
3
4
5
6
7
8
docker pull busybox
docker run -it --name busybox1 busybox sh
ip addr
ctrl+p+q # 不使用exit,让容器在后台运用
docker run -it --name busybox2 --net=container:busybox1 busybox sh
ip addr
# 此两个的容器的IP是一模一样的,而且还可以相互通信
ping busybox1

none模式:没有IP

1
docker run -it --name none --net=none busybox sh

overlay模式,实现跨主机的通信,docker:1.10+,才有的模式.

  1. 约定的容器启动时 会被当作一个服务被consul发现,从而分发IP

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    vim /lib/systemd/system/docker.service
    # 注意改自己的IP地址,和网卡的名称
    # 多台也是这个的一样的配置(不需要去改变IP地址和网卡名称)
    # 使用hostA做为consul的服务端 hostA的IP;192.168.1.116 网卡名称为:enp2s0
    # hostA hostB都要操作
    ExecStart=/usr/bin/dockerd -H tcp://0.0.0.0:2376 \
    -H unix:///var/run/docker.sock --cluster-store=consul://192.168.1.116:8500 \
    --cluster-advertise=enp2s0:2376 --insecure-registry=0.0.0.0/0
    systemctl daemon-reload
    systemctl restart docker
    ps -aux |grep docket
  2. 在consul的主机执行就可以:hostA

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    docker pull progrium/consul
    # -h 取名 -server服务端 并把ui打开
    docker run -d -p 8400:8400 -p 8500:8500 -p 8600:53/udp -h consul progrium/consul \
    -server -bootstrap -ui-dir /ui
    docker ps -a
    # 在浏览器中打开
    192.168.1.116:8500
    # 在KEY/VALUE中 会显示有多少台主机进行着通信 hostA,hostB
    # create overlay network 创建IP地址池 会用consul定义一个IP地址池(如10.0.9.0/24)
    docker network ls
    # -d overlay使用overlay驱动 --subnet 网段 edu_net为名称
    # 会同步到多台主机上(hostA,hostB)
    docker network create -d overlay --subnet=10.0.9.0/24 edu-net
  3. 当约定好的主机启动容器时,consul服务就会从地址池中取得IP,给容器使用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    # hostA
    docker run -d --name app1 --net=edu-net nginx
    # 登陆进出
    docker exec -it app1 bash
    ip addr
    ping www.baidu.com
    # hostB 没有虚拟技术 所以没有实做
    docker run -d --name app2 --net=edu-net nginx
    docker exec -it app2 bash
    ping app1

官方文档

一个中文的docker文档

对docker的解释

Share Comments

docker-swarm

swarm集群

Docker Machine是一个工具,用来在虚拟主机(电脑上,云端)上安装Docker Engine,并使用 docker-machine命令来管理这些虚拟主机(Machine)
Docker Swarm则是多终端的Docker 连接工具

install swarm

1
2
3
docker pull swarm
# 查看swarm版本
docker run --rm swarm -v

Host_consul_ip的例子

基于 consel 服务发现后端来配置一个本地 Swarm 集群

1.1 配置每个节点主机

1
2
3
4
5
6
7
8
9
# 我用的是virtualbox,ubuntu16,安装好docker并修改好如下的内容,
# reboot之后clone,clone过后的虚拟机只需要修改一下ip就行
vim /lib/systemd/system/docker.service
ExecStart=xx 再这后面加上如下内容
-H tcp://0.0.0.0:2375 -H unix:///var/run/docker.sock
sudo systemctl daemon-reload
sudo systemctl restart docker
ps -aux |grep docket
docker pull swarm

坑一

1
2
3
4
5
6
# docker daemon on node is running with
# -H tcp://0.0.0.0:2375 -H unix:///var/run/docker.sock
alias docker="docker -H tcp://0.0.0.0:2375 \
-H unix:///var/run/docker.sock"
type docker
unalias docker

坑二

1
2
3
4
5
6
docker -H tcp://0.0.0.0:2375 -H unix:///var/run/docker.sock \
--daemon=true --pidfile="/var/run/docker_2375.pid" \
--graph="/var/lib/docker_2375"
docker -H tcp://0.0.0.0:2375 --daemon=true \
--pidfile="/var/run/docker_2375.pid" \
--graph="/var/lib/docker_2375"

坑三

1
2
3
4
5
6
7
vim /lib/systemd/system/docker.service
# 成功的
DOCKER_OPTS=-g /usr/bin/docker -H tcp://0.0.0.0:2375 \
-H unix:///var/run/docker.sock
# 这个是失败的
OPTIONS=-g /usr/bin/docker -H tcp://0.0.0.0:2375 \
-H unix:///var/run/docker.sock

1.2 启动集群

1.2.1.启动 Consel 服务后端(consul_ip or token,本机ip:109)至少一个群集管理器

1
2
3
4
5
6
7
8
# -h ==--name
docker run -d -p 8500:8500 --name=consul progrium/consul -server \
-bootstrap -ui-dir /ui
# 输出一个id:4b80d44e61d4e00510
# sudo docker restart id # 重新启动此容器
docker ps -a
# 在浏览器中打开
192.168.1.109:8500

1.2.2.启动管理节点主机(manager0_ip,ip为100的虚拟主机,一个群组工作者)

1
2
3
4
5
6
7
8
9
10
11
# docker run -d -p 4000:4000 swarm manage -H :4000 --replication \
# --advertise <manager0_ip>:4000 consul://<consul_ip>:8500
# 如果有4000端口占用的情况
docker ps -a
docker rm -f -v containter_id
docker run -d -p 4000:4000 swarm manage -H :4000 --replication \
--advertise 192.168.1.100:4000 consul://192.168.1.109:8500
# 输出一个id:b7a3c10f294f4718bcd0a516f
# 用户也可以启动 从管理节点
# docker run -d swarm manage -H :4000 --replication --advertise \
# <manager1_ip>:4000 consul://<consul_ip>:8500

1.2.3.启动工作节点(node_ip,ip为101,102,103的虚拟主机)

1
2
3
4
5
6
7
8
# docker run -d swarm join --advertise=<node_ip>:2375 \
# consul://<consul_ip>:8500
docker run -d swarm join --advertise=192.168.1.101:2375 \
consul://192.168.1.109:8500
docker run -d swarm join --advertise=192.168.1.102:2375 \
consul://192.168.1.109:8500
docker run -d swarm join --advertise=192.168.1.103:2375 \
consul://192.168.1.109:8500

节点启动后,用户可以指定 Docker 服务地址为 :4000> 来测试各种 Docker 命令,可以看到整个 Swarm 集群就像一个虚拟的 Docker 主机一样正常工作.

1
2
3
sudo docker -H 192.168.1.100:4000 ps -a
sudo docker -H tcp://192.168.1.100:4000 info
sudo docker ps --no-trunc

由于 Swarm 实际上是通过 agent 调用了本地的 Docker daemon 来运行容器,当 Swarm 集群服务出现故障时,无法接受新的请求,但已经运行起来的容器将不会受到影响.

在集群上运行容器

1
sudo docker -H 192.168.1.100:2375 run -d --name web1 nginx

2. token的例子

dockerHub 的服务发现后端,需要各个节点能通过公网访问到 DockerHub 的服务接口

在任意一台安装了 Swarm 的机器上执行 swarm create 命令来在 DockerHub 服务上进行注册

2.1 创建集群 id

Swarm 会通过服务发现后端(此处为 DockerHub 提供)来获取一个唯一的由数字和字母组成的 token,用来标识要管理的集群

1
2
3
4
5
# ip_109本机执行
sudo docker ps -a
sudo docker run --rm swarm create
# 只需要运行一次就行了,它会一直保存着,虽然在本机docker ps -a也查看不到
token_id=ce2f14fa5ec481170d2bf62f40f832f7

2.2 配置集群节点

添加节点A(101)、B(102)到集群,在所有要加入集群的普通节点上面执行 swarm join 命令,表示把这台机器加入指定集群当中

1
2
3
4
5
6
7
8
9
# ip_109本机执行
sudo docker run --rm swarm join --addr=192.168.1.100:2375 \
token://$token_id
sudo docker run --rm swarm join --addr=192.168.1.101:2375 \
token://$token_id
sudo docker run --rm swarm join --addr=192.168.1.102:2375 \
token://$token_id
# 列出节点 只需要出现一条记录之后就ctrl+c
sudo docker run --rm swarm list token://$token_id

2.3配置管理节点

在任何一台主机A(A:192.168.1.100)B或者C上开启管理程序

1
2
3
4
5
6
7
8
9
10
# ip_100上执行
docker run -d -p 8888:2375 swarm manage token://$token_id
# 可以通过 docker ps 命令来查看启动的 swarm manager 服务容器
docker ps
# ip_109本机上 管理集群
sudo docker -H 192.168.1.100:8888 ps
# 在集群上运行容器
sudo docker -H 192.168.1.100:8888 run -d --name web1 nginx

几个命令就将多个单机版本的 Docker daemon 变成一个 cluster,还支持了 service 概念(多个容器实例副本的抽象)

3. docker-machine swarm -d virtualbox

docker daemon是root

python此用户才能在virtualbox中看到创建的dev等虚拟机,docker-machine是普通用户的命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
docker run swarm -v
sid=$(sudo docker run swarm create)
echo $sid
docker ps -a #有上面两个但状态都是Exited
docker-machine create -d virtualbox --swarm --swarm-master \
--swarm-discovery token://$sid swarm-master
# The host-only adapter is corrupted. Let's stop the VM, fix the host-only adapter and restart the VM
# 但我没有去做操作,它自动去改了?
docker-machine ls
docker-machine create -d virtualbox --engine-label itype=frontend \
--swarm --swarm-discovery token://$sid swarm-node-01
docker-machine create -d virtualbox --engine-label itype=frontend \
--swarm --swarm-discovery token://$sid swarm-node-02
docker-machine create -d virtualbox --engine-label itype=frontend \
--swarm --swarm-discovery token://$sid swarm-node-03
docker-machine env swarm-master
docker-machine env --swarm swarm-master
eval "$(docker-machine env --swarm swarm-master)"
docker info
docker run swarm list token://$sid
dokcer ps -a
docker-machine ls
eval "$(docker-machine env swarm-master)"
docker ps -a
eval "$(docker-machine env --swarm swarm-master)"
docker-machine ls
docker ps
# 进入环境了,docker run
sudo docker run -itd --name engmgr ubuntu
sudo docker ps
# 居然不是下在本机的 成功 有没有可能是主机的docker是什么权限,环境中的也是啊
# swarm-node-02/engmgr 而我的是engmgr
for i in `seq 1 6`; do sudo docker run -itd -e \
constraint:itype!=frontend --name eng$i ubuntu; done
sudo docker ps
# swarm-node-02/eng1 等还是没有前缀名
# filter
sudo docker run -itd --name engmgr-c -e \
affinity:container==engmgr ubuntu
docker-machine kill $(docker-machine ls -q)
# 好像就回到了本机了
sudo docker images
# ubuntu的镜像也在下载过来了

注意:您会注意到命令不能与前缀sudo .这是因为它假设您正在使用的登录到服务器docker-machine ssh使用Docker窗机设置后命令

4. docker-machine swarm -d digital

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
DOTOKEN=DOTOKEN_ID
# 生成节点
for i in 1 2 3; do docker-machine create --driver digitalocean \
--digitalocean-image ubuntu-16-04-x64 \
--digitalocean-access-token $DOTOKEN node-$i; done
# 该开的端口,也都开了
# 命令成功完成后,您可以通过访问您的DigitalOcean仪表板或输入以下命令来验证是否已创建所有计算机:
#更新docker-machine之后执行,就如此简单的创建了主机,在控制面板就可以看到新创建出来的实例了:
docker-machine ls
eval "$(docker-machine env node-1)"
# 使节点成为集群成员,选择其中一个节点并将其作为Docker Swarm(Node-1)管理器
docker-machine ssh node-1
docker swarm init --advertise-addr 45.55.87.176
# 会输出如下信息
# docker swarm join --token SWMTKN-1-xx 45.55.87.176:2377
# 并将其余部分配置为Docker Swarm工作线程.
docker-machine ssh node-2
docker swarm join --token SWMTKN-1-xx 45.55.87.176:2377
docker-machine ssh node-3
docker swarm join --token SWMTKN-1-xx 45.55.87.176:2377
# Docker Swarm管理命令必须在管理器节点上执行
docker-machine ssh node-1
docker node ls
docker node --help
# 您可以随时从群集中添加或删除节点. 此外,工作节点可以升级为管理器,并且管理器可以转换为工作器
docker info
# 在Docker Swarm中运行服务 docker node命令一样, docker service命令只能在管理器节点上执行
docker service create -p 80:80 --name webserver nginx
# 要查看集群上运行的服务,请键入:
docker service ls # 可以输入node-1IP到浏览器中,可以看到Nginx的默认页面.
# 您可以通过使用docker service ps后跟服务名称来确定服务正在运行的节点
docker service ps webserver
docker-machine stop node-1 node-2 node-3
docker swarm --help

大坑:

-driver virtualbox

docker-machine version 0.12.0 是没有问题的

但 amazonec2 和 digitalocean ,都要是0.12.1版本才行,不然在创建时,会报如下错

Error creating machine: Error running provisioning: ssh command error:
Unable to query docker version: Cannot connect to the docker engine endpoint

5. docker-machine 在AWS创建Docker主机(会创建一个新的实例,会有收费)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# http://usyiyi.cn/documents/docker/machine/examples/aws.html
# 开放22,2376,2377,80 Docker machine的安全组
docker-machine create --driver amazonec2 \
--amazonec2-access-key you_access_key \
--amazonec2-secret-key you_secret_key \
--amazonec2-region=us-west-2 aws-01
# /home/python/.docker/machine/machines/aws-01
docker-machine ssh aws-01 # 虽然有下面的各种坑,但还是能进去,一直报错所以一直没有执行此命令
docker swarm init --advertise-addr 54.244.58.2
# 不像digitalocean 一样顺利,还得vim /lib/systemd/system/docker.service 然后重启,再执行就和digitalocean一样了
docker swarm join --token SWMTKN-1-xx 54.244.58.2:2377
docker-machine create --driver amazonec2 \
--amazonec2-access-key you_access_key \
--amazonec2-secret-key you_secret_key \
--amazonec2-region=us-west-2 aws02
docker-machine ssh aws02
sudo docker swarm join --token SWMTKN-1-xx 54.244.58.2:2377
docker-machine ssh aws-01
docker service create -p 80:80 --name webserver nginx
docker service ls
docker service ps webserver
# Docker Swarm的另一个功能是缩放服务的能力,即启动服务的其他实例.假设我们想将我们之前启动的webserver服务扩展为五个实例
docker service scale webserver=5 #output: webserver scaled to 5
docker service ps webserver # 可以看到这5个服务在都分布在哪些节点上

坑一:

创建实例时,先要在EC2控制面板创建一对密钥对,在创建实例时,选择此密钥对,并保存好,当你创建好实例之后,要连接实例时需要此.pem文件,才能连接上,不然就悲剧了.

坑二:

IAM:不是你的用户名和密码(root) ,失败

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
docker-machine create --driver amazonec2 \
--amazonec2-access-key username \
--amazonec2-secret-key password \
--amazonec2-vpc-id vpc-a24484c4 aws-sandbox
# 你也可以创建其它的用户来管理aws,失败
docker-machine create --driver amazonec2 \
--amazonec2-access-key AKIAIDGRV7O6MZ2DOYDA \
--amazonec2-secret-key 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 \
--amazonec2-vpc-id vpc-978f4cf1 aws-sandbox
# 但只能是root用户才能访问成功
docker-machine create --driver amazonec2 \
--amazonec2-access-key you_access_key \
--amazonec2-secret-key you_secret_key \
--amazonec2-vpc-id vpc-a24484c4 \
--amazonec2-zone=b aws-sandbox
# unable to find a subnet in the zone: us-east-1b to issue
ssh -i "femn.pem" ubuntu@ec2-34-212-39-188.us-west-2.compute.amazonaws.com
sudo apt install awscli
aws configure

坑三: 配置aws CLI ,IAM上面已得知了,Default region name:必须是这个实例的实际地址

一定要选自己的地区 us-west-2

Share Comments

docker网络代理设置

背景
在一些实验室环境,服务器没有直接连接外网的权限,需要通过网络代理.我们通常会将网络代理直接配置在/etc/environment、/etc/profile之类的配置文件中,这对于大部分操作都是可行的.
然而,docker命令却使用不了这些代理.比如docker pull时需要从外网下载镜像,就会出现如下错误:

1
docker pull hello-world

Unable to find image,hello-world:latest locally
Pulling repository docker.io/library/hello-world

docker: Network timed out while trying to connect to here. You may want to check your internet connection or if you are behind a proxy..

解决方案一:
停止docker服务,手动以使用2375端口监听所有网络接口的方式启动docker daemon.

1
2
systemctl stop docker.service
nohup docker daemon -H tcp://0.0.0.0:2375 -H unix:///var/run/docker.sock &

详情参见

解决方案二:
该方法是持久化的,修改后会一直生效.该方法覆盖了默认的docker.service文件.

  1. 为docker服务创建一个内嵌的systemd目录

    1
    mkdir -p /etc/systemd/system/docker.service.d
  2. 创建/etc/systemd/system/docker.service.d/http-proxy.conf文件,并添加HTTP_PROXY环境变量.其中[proxy-addr]和[proxy-port]分别改成实际情况的代理地址和端口:

1
2
3
vim /etc/systemd/system/docker.service.d/http-proxy.conf
[Service]
Environment="HTTP_PROXY=http://0.0.0.0:2375/"
  1. 如果还有内部的不需要使用代理来访问的Docker registries,那么还需要制定NO_PROXY环境变量:

    1
    2
    3
    4
    [Service]
    Environment="HTTP_PROXY=http://[proxy-addr]:[proxy-port]/" \
    "HTTPS_PROXY=https://[proxy-addr]:[proxy-port]/" \
    "NO_PROXY=localhost,127.0.0.1,docker-registry.somecorporation.com"
  2. 更新配置:

    1
    systemctl daemon-reload
  3. 重启Docker服务:

    1
    systemctl restart docker
Share Comments

Dockerfile docker-compose

Dockerfile定制镜像命令,docker镜像:业务代码和运行环境进行整体的打包:

一个Dockerfile的文件:系统基本所需要的环境和软件

1
2
3
4
5
6
7
8
9
10
11
vim Dockerfile
FROM centos7
RUN # 对原镜像进行命令操作
ADD . /opt/ # 将宿主机此目录下的文件原copy到容器的/opt/下
EXPOSE 80 8000 # 容器可映射的端口
VOLUME ["/home/python",""]
WORKDIR '/home/xx' # 相当于宿主机的cd /home/xx
CMD ["nginx", "-g", "daemon off;"] #一开始运行这个容器所执行的命令
# 所有的文件复制均使用 COPY 指令,仅在需要自动解压缩的场合使用 ADD
# 用Dockerfile 生成镜像app
docker build -t app .

Dockerfile 模板文件,可以很方便的定义一个单独的应用容器,记录了(单)一个镜像的制作过程

如果 dockerhub 的镜像不能满足我的需求就 dockerfile 自己 build 镜像

为什么还要docker-compose,是因为Dockerfile要去执行CMD [“bash”]之后,再去启动nginx,node等其它的服务,不能直观的看到端口之间的映射,以及服务之间的link的原因吗?

但是很多时候,需要多个镜像合作才能启动一个服务,比如前端要有nginx,数据库mysql,邮件服务等等,当然你可以把所有这些都弄到一个镜像里去,但这样做就无法复用了.

更常见的是,nginx,mysql,smtp都分别是个镜像,然后这些镜像合作,共同服务一个项目.
docker-compose就是解决这个问题的.你的项目需要哪些镜像,每个镜像怎么配置,要挂载哪些volume,等等信息都包含在docker-compose.yml里.
要启动服务,只需要docker-compose up就行,停止也只需要docker-compse stop/down

dockerfile+docker-compose, 是因为docker-compose.yml本身没有镜像构建的信息,如果镜像是从docker registry拉取下来的,那么Dockerfile就不需要;如果镜像是需要build的,那就需要提供Dockerfile.

docker-compost:定义和运行多个Docker容器的应用

编排工具,定义并运行基于Docker的分布式应用,简化部署复杂应用的流程,管理容器化应用的完整开发周期

1.允许用户通过一个单独的docker-compose.yml模板文件(YAML格式)来定义一组相关联的应用容器为一个项目:project
,包括注明需要哪些服务,服务之间的关联,开放的port,对外连结的路径volumes

2.以启动container为目的的、先build image再以此启动container

3.定义好了管理起来轻松,你如果喜欢用docker run带一大串parameters也是可以的

install compose

1
2
3
4
5
6
7
8
9
10
curl -L https://github.com/docker/compose/releases/download/1.8.0/docker-compose-`uname -s`-`uname -m` \
> /usr/local/bin/docker-compose
# 如果命令行下载超时
https://github.com/docker/compose/releases/download/1.8.0/docker-compose-Linux-x86_64
cd Download
cp docker-compose-Linux-x86_64 /usr/local/bin/docker-compose
chmod a+x /usr/local/bin/docker-compose
docker-compose -h
docker-compose up/down/stop

管理server(contient)执行的顺序,但其实这只是希望是这样执行(docker run),但其实真正的启动时的, 还需要其它的工具去控制.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
mkdir 1
cd 1
vim docker-compose.yml
nginx:
image: nginx
# 1. 只定义 image :先找本地、本地没有找 docker hub 、再没有就报错
# 2.只定义build:跑Dockerfile做image,有image直接用,image没有name和tag
# 3.同时定义image和build:没有image就先跑build,name和tag用image定义的那个
# 有了image就直接跑container
# build: ./nginx ##nginx目录下有Dockerfile文件
ports:
- "80:80"
volumes:
- $PWD/nginx/nginx.conf:/etc/nginx/nginx.conf
- ./nginx/sites-enabled:/etc/nginx/sites-enabled
restart: always
node:
image: node
ports:
-"8000:8000"
volumes:
- $PWD/node:/src/app
restart: always
mkdir node
mkdir nginx
cd nginx
mkdir sites-enabled
vim nginx.conf
daemon off;
还要去配置node
cd ../
docker-compose up
docker-compose down
docker ps -l

用docker-compose 自动一个一个的安装软件(client)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
vim docker-compose.yml
version: '2'
services:
mongodb:
image: tutum/mongodb
ports:
- "27017:27017"
- "28017:28017"
environment:
- AUTH=no
container_name: mongodb
restart: always
memcached:
image: memcached
ports:
- "11211:11211"
container_name: memcached
restart: always
# 效果:只要运行docker-compose up,就会自动安装mongodb和memcached
# 并且会在机器重启的时候自启动

一个项目的例子

Share Comments

docker-machine

Docker Engine 主要用来接收和处理docker命令请求的(Docker Engine==Docker引擎==Docker Engine的客户端==docker)

Docker我们一般理解的,都是C/S模型,用户通过docker client向docker daemon发送REST 请求.

Docker Engine包括这么几个部分:

1.Docker Daemon — docker 的守护进程,属于C/S中的server

2.Docker REST API — docker daemon向外暴露的REST 接口

3.Docker CLI — docker向外暴露的命令行接口(Command Line API)

因此,客户端访问服务端的方式有两种,一种是使用命令行工具,比如docker run, docker ps…等等.另一种就是直接通过调用REST API,比如发送一个curl http请求

Docker Machine是一个工具,用来在虚拟主机上安装Docker Engine,并使用 docker-machine命令来管理这些虚拟主机(Machine).是一个可以帮助我们在电脑上、在云端、在数据中心内创建 Docker 主机的应用

主要用来管理 docker化的 host (安装了Docker Engine的主机)

使用 docker-machine命令,你可以启动、查看、停止以及重启一个主机,升级Docker client和daemon,配置Machine 的 Docker client与你的主机进行通信

无论是Mac,Windows或是Linux,你都可以在其上安装Docker Machine,使用docker-machine命令来创建和管理大量的Docker hosts.
它会自动创建主机,在主机上安装Docker Engine,然后配置docker client.每个被管理的主机(“machine”)都是一个Docker 主机和一个配置过的client的组合.

docker Machine的具体使用场景:

1.你目前只有一个老版本的MacOSX(比如10.10.2)或者Windows系统,想在上边运行docker.

2.想在远程系统上创建Docker主机,比如想在网络上,云平台上,以及本地创建Docker host,你就需要Docker Machine.

3.docker在不同的linux上得安装方法不一样,通过这个工具可以提供统一的安装方法.

你可以使用Machine在一个或多个虚拟机上安装Docker Engine.这些虚拟机可以是在本地(当你使用Machine在VirtualBox安装和运行Docker Engine时)

也可以是远程的(使用Machine在云平台上创建Docker化的主机时).这些虚拟化的主机可以被认为是”machine”

Machine
解决因操作系统异构导致的安装 Docker 困难的问题,所有系统部署 Docker 都变得非常方便.

install machine

1
2
3
4
5
6
7
8
9
curl -L https://github.com/docker/machine/releases/download/\
v0.14.0/docker-machine-`uname -s`-`uname -m` \
>/tmp/docker-machine &&
chmod a+x /tmp/docker-machine
sudo cp /tmp/docker-machine /usr/local/bin/docker-machine
#如果超时,直接从网页中下载
https://github.com/docker/machine/releases/download/v0.12.1/docker-machine-Linux-x86_64
chmod a+x docker-machine-Linux-x86_64
sudo mv docker-machine-Linux-x86_64 /usr/local/bin/docker-machine

use machine

你也可以在VirtualBox等虚拟机上安装Docker engine到Docker中.在本地客户端,就好像所有的Docker 引擎运行在本地一样

  1. 需要先安装virtualbox这个软件
  2. 需要的是boot2docker.iso 只是一个普通的iso文件.
    Docker Machine使用boot2docker作为virtualbox的镜像 – boot2docker是一个运行Docker容器的轻量级Linux系统,完全在内存中运行
    不是下载此镜像:sudo docker pull boot2docker/boot2docker
    而是 将iso放到 /home/python/.docker/machine/cache/ 再运行
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    docker-machine create -d virtualbox dev
    # -driver virtualbox 表示我们在VirtualBox 的虚拟机里面部署 docker,代表的是驱动类型
    # 最后的参数"dev" 是虚拟机(Machine)的名称
    # http://os1h5rv4q.bkt.clouddn.com/docker_dev.jpg
    #用这个Machine可以配置本地Docker客户端,用到env命令完成
    docker-machine env dev
    # 目前已經建立出來的 docker VM
    docker-machine ls
    # Docker客户端将会使用设置的相同的环境变量通过Docker API与运行在上面的机器互相通信
    # 设置完成后,就可以在本地机器访问远程Docker
    eval $(docker-machine env dev)
    docker ps
    # 到指定的Docker Machine
    docker-machine ssh dev
    # 启动/停止/移除/查看状态 Machine
    docker-machine start/stop/rm/status dev

简单一个例子

1
2
3
4
5
6
7
8
9
10
docker-machine create -d virtualbox vm1
docker-machine create -d virtualbox vm2
docker-machine create -d virtualbox vm3
# 连接vm1
docker-machine ssh vm1
docker swarm init --advertise-addr 192.168.99.100(vm1:ip)
# 上面的命令会提示vm1是管理员,并生成如下命令
# 只需要连接vm2,vm3运行如下命令就可以成为node
docker swarm join --token SWMTKN-1-5b08c43sjxzeg2gk4xjcmfkrn41ap7i77okitjr7a45ja1czd5-0td3csojve9nsbwja1f0vzn4b 192.168.99.100:2377
在vm1中操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# 在vm1查看 node数量
docker node ls
# 在vm1创建 docker service
docker service create --name=my_nginx nginx
docker service ls
# 更新 service
docker service update --publish-add 80 my_nginx
# 访问
192.168.99.100:30000
# docker service scale
# 启动多个服务
docker service scale my_nginx=10
docker service ls
# 查看 service 状态
docker service ps my_nginx # 可以看到10个服务分别在vm1,2,3执行
# 查看 log
docker service logs -f my_nginx
# Docker Swarm Visualizer 可视化工具
docker service create --name=viz --publish=8080:8080/tcp --constraint=node.role==manager --mount=type=bind,src=/var/run/docker.sock,dst=/var/run/docker.sock dockersamples/visualizer
# 访问
192.168.99.100:8080
# 停止服务
docker service scale my_nginx=0
# 删除服务
docker service rm my_nginx
Share Comments

docker-hub

新建私有镜像仓库,只有registry:2这个镜像运行时,才能push,pull私有镜像库

私有镜像仓库的好处:

1.上传下载速度快

2.企业可自行维护镜像仓库

3.需要维护镜像仓库服务器

4.同步docker官方镜像到企业私有镜像仓库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
docker pull registry:2
# 使用官方的 registry 镜像来启动本地的私有仓库
# 仓库会被创建在容器下/var/lib/registry下
# 将镜像文件存放在本机的指定路径/data/registry
docker run -d -p 5000:5000 --restart=always --name registry \
-v /data/registry:/var/lib/registry registry:2
# 将镜像运行为容器 -d 将这个进程后台运行 -p 映射端口主机端口:容器端口
# --restart=always当容器服务死掉之后自动重启 --name容器的名字
# -v 映射容器的目录到宿主机下
# -m 128m 内存限制在128M
# 查看主机端口在容器的映射情况
docker port nostalgic_morse 5000
docker pull nginx
#镜像的重命名 标记镜像
docker tag nginx 47.52.94.126:5000/nginx:last
docker tag nginx 192.168.1.116:5000/nginx:last
# modify docker daemon启动参数
vim /usr/lib/systemd/system/docker.service
# centos ubuntu:/lib/systemd/system/docker.service
ExecStart=之前的不要动加上 --insecure-registry=0.0.0.0/0
systemctl daemon-reload
systemctl restart docker
# 看参数有没有正确的添加.
ps aux |grep docker
# 上传标记镜像到私有仓库
docker push 192.168.1.116:5000/nginx:last # 本机测试的成功
docker pull 192.168.1.116:5000/nginx:last
# 只要能连接上私有仓库,就可以从私有仓库中pull
# 因为本机做为私有仓库,已经有这image了,所以下载得很快
# docker push 47.52.94.126:5000/nginx:last 上传public IP失败

镜像上传到Docker Hub

1
2
3
4
5
6
7
8
9
docker login #输入 hub.docker.com注册时的用户名和密码
docker pull registry
docker tag registry femn/registry:last
# 镜像名:femn/registry tag:last
#上传到Docker Hub 镜像名必须是 username/imagesname,才能上传
# 并且不需要提前去Docker Hub创建仓库
docker push femn/registry
# 将本地镜像上传到https://hub.docker.com的个人仓库中了.

Docker Hub

Docker通过docer search、pull、login和push等命令提供了连接Docker Hub服务的功能.

Docker Hub是一个由Docker公司负责维护的公共注册中心,它包含了超过15,000个可用来下载和构建容器的镜像,并且还提供认证、工作组结构、工作流工具(比如webhooks)
构建触发器以及私有工具(比如私有仓库可用于存储你并不想公开分享的镜像)

自动构建

自动构建功能会自动从Github或BitBucket直接将镜像构建或更新至Docker Hub,通过为Github或Bitbucket的仓库添加一个提交的hook来实现,
当你推送提交的时候就会触发构建和更新,但是,你不能通过docker push推送一个自动化构建,而只能通过在Github或者BitBucket提交你的代码来管理它.
你可以在一个Docker的仓库中创建多个自动构建,配置它们只指定的Dockerfile或Git 分支

Automated Builds的一个实例

建Dockerfile的目的,就是为了使用dockerfile里面的from语法的作用,来借用github服务器新建我需要的镜像

由于不能从google container上直接pull镜像,所以这里通过docker hub的Automated Builds功能从项目的dockerfile中Build到docker的官方服务器上,然后再从它们上面拉取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 步骤2中 创建一个带有Dockerfile的Github项目
mkdir docker_lib
cd docker_lib
git init
mkdir google-etcd-amd64
cd google-etcd-amd64
vim Dockerfile
FROM gcr.io/google_containers/etcd:2.0.12
MAINTAINER femn2014
git add .
git commit -m '增加Dockerfile文件,解决镜像被墙的问题'
# 在github 创建一个名为docker_lib的项目
git remote add origin https://github.com/femn2014/docker_lib.git
git push -u origin master
# 当完成此实例的所有步骤之后,再测试下,推送提交的时候就会触发构建和更新
vim Dockerfile
FROM nginx
MAINTAINER femn2014
git add .
git commit -m '当推送提交时触发构建和更新'
git push -u origin master
# 这时 docker pull femn/etcd 时下载的将会是nginx的镜像

总结来说 自动构建的步骤

1.创建一个Docker Hub账户并且登陆
2.通过create选择 create automated build创建一个项目,再选择连接一个包含Dockerfile的GitHub或者BitBucket.
3.给自动构建创建一个名称:etcd(相当于也就是Docker Hub的项目的镜像名称)
4.配置自动化构建 (click here to customize)
5.选择你想用于构建的分支(默认是master分支)
6.指定Dockerfile的路径,默认是/,也可以在github中建立个目录再创建Dockerfile文件,为了方便我们后面区分镜像包
7.指定一个Docker标签来构建

设置头像
国内docker_hub
另一个镜像管理工具Quay

Share Comments

supervisor

Linux的后台进程运行有好几种方法,例如nohup,screen等,但是,如果是一个服务程序,要可靠地在后台运行,我们就需要把它做成daemon,最好还能监控进程状态,在意外结束时能自动重启.

supervisor就是用Python开发的一套通用的进程管理程序,能将一个普通的命令行进程变为后台daemon,并监控进程状态,异常退出时能自动重启.另一个进程管理工具:S6

Debian / Ubuntu可以直接通过apt安装:

1
sudo apt-get install supervisor

如果是通过pip安装的话,就需要echo_supervisord_conf程序生成supervisor的初始化配置文件:

1
2
3
sudo pip install supervisor
mkdir /etc/supervisor
echo_supervisord_conf > /etc/supervisor/supervisord.conf

supervisor安装完成后会生成三个执行程序:supervisortd 守护进程服务,用于接收进程管理命令.supervisorctl 客户端,用于和守护进程通信,发送管理进程的指令.
echo_supervisord_conf 生成初始配置文件程序
然后,给我们自己开发的应用程序编写一个配置文件,让supervisor来管理它.每个进程的配置文件都可以单独分拆,放在/etc/supervisor/conf.d/目录下,以.conf作为扩展名

vim server.conf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[program:python8010]
command = /usr/bin/python3 HttpsRunner.py
directory = /server/app1.6
user = root
redirect_stderr = true
stdout_logfile_maxbytes = 150MB
stdout_logfile_backups = 20
stdout_logfile = /server/app1.6/python8010.log
[program:mongod]
;command = /mongo/mongodb-linux-x86_64-ubuntu1404-3.2.10/bin/mongod --dbpath /mnt/mongodb --auth
command = /mongo/mongodb-linux-x86_64-ubuntu1404-3.2.10/bin/mongod -f /server/app1.6/mongodb.conf
directory = /server/app1.6
user = root
redirect_stderr = true
stdout_logfile_maxbytes = 150MB
stdout_logfile_backups = 20
stdout_logfile = /server/app1.6/mongodb.log

Supervisor只能管理非daemon的进程,也就是说Supervisor不能管理守护进程.否则提示Exited too quickly (process log may have details)异常.
启动Supervisor服务:

1
supervisord -c /etc/supervisor/supervisord.conf

管理进程:

1
2
3
4
5
6
7
8
9
10
11
12
13
supervisorctl status
supervisorctl stop mongod
supervisorctl start python8010
supervisorctl restart python8001
# 重启所有属于名为groupworker这个分组的进程(start,restart同理)
supervisorctl stop groupworker
# 停止全部进程,注:start、restart、stop都不会载入最新的配置文件
supervisorctl stop all
# 载入最新的配置文件,停止原有进程并按新的配置启动、管理所有进程
supervisorctl reread/reload
# 根据最新的配置文件,启动新配置或有改动的进程,配置没有改动的进程不会受影响而重启
supervisorctl update
supervisorctl tail <APP_NAME>

进入CLI管理台:

1
supervisorctl

Web管理界面

1
2
3
4
5
vim /etc/supervisor/supervisord.conf
[inet_http_server] ; inet (TCP) server disabled by default
port=0.0.0.0:9001 ; (ip_address:port specifier, *:port for all iface)
username=root ; (default is no username (open server))
password=root ; (default is no password (open server))

重启Supervisor服务,如果有Unlinking stale socket /var/run/supervisor.sock 执行unlink /var/run/supervisor.sock:

1
2
ps aux |grep supervisord
supervisord -c /etc/supervisor/supervisord.conf
开机启动Supervisor:

方法1:
Linux 在启动的时候会执行 /etc/rc.local 里面的脚本,所以只要在这里添加执行命令就可以
以下内容需要添加在 exit 命令前,而且由于在执行 rc.local 脚本时,PATH 环境变量未全部初始化,因此命令需要使用绝对路径.

1
/usr/bin/supervisord -c /etc/supervisor/supervisord.conf

如果是 Ubuntu 16.04 以上,rc.local 被当成了服务,而且默认是不会启动,需要手动启用一下服务

启动rc.local服务:

1
sudo systemctl enable rc-local.service

方法二:
Supervisord 默认情况下并没有被安装成服务,它本身也是一个进程.官方已经给出了脚本可以将 Supervisord 安装成服务,
可以参考这里查看各种操作系统的安装脚本,但是我用官方这里给的 Ubuntu 脚本却无法运行.
安装方法可以参考 serverfault
这个脚本下载下来后,还需检查一下与我们的配置是否相符合,比如默认的配置文件路径,pid 文件路径等,如果存在不同则需要进行一些修改

Share Comments

Shell基本

shell:是一种命令解释器还是一种编程语言(shell脚本,shell编程),擅长处理文本类型的数据,所以Shell脚本在管理Linux系统中发挥了巨大作用
备份文件、安装软件、下载数据之类的事情,学着使用sh,bash会是一个好主意.

1
2
3
4
5
6
#!/bin/bash
# "#!"是一个约定的标记,它告诉系统这个脚本需要什么解释器来执行
# shell脚本会被分解成一行一行的依次执行.
# bash shell会将空格,制表符,换行符当作字段分隔符
IFS=$'\n' # 只识别换行符
IFS=$'\n:;"' # 将换行,冒号,分号和双引号作为字段分隔符

脚本文件没有可执行权限时

1
sh test.sh

脚本有可执行权限

1
2
3
4
5
6
7
8
9
./test.sh
# 注意,一定要写成./test.sh,而不是test.sh,运行其它二进制的程序也一样,
# 直接写test.sh,linux系统会去PATH里寻找有没有叫test.sh的,
# 而只有/bin, /sbin, /usr/bin,/usr/sbin等在PATH里,你的当前目录通常不在PATH里
# 所以写成test.sh是会找不到命令的,要用./test.sh告诉系统说,就在当前目录找
source 或.
# 命令执行与上面执行不同,上面执行 shell 都会分配一个子进程来进行
# source 或. 命令就在本进程执行,也就是说一些非环境变量也能读取到

重定向输入和输出:将某个命令的输出重定向到另一个位置(文件,或者命令)

命令行的数据流定义:STDIN(0),STDOUT(1),STDERR(2),用来管理SHELL中的信息流的分流.

重定向输出:将命令的输出发到一个文件中

1
2
3
4
5
STDOUT: >(覆盖) >>(追加) 重定向到文件
STDERR: 2>
STDOUT STDERR: 2>&1
ehco "alias ll='ls -alF'" >> test.txt
date > test.txt

重定向输入:将文件的内容重定向到命令中

1
2
3
grep femn < /etc/passwd
grep femn < /etc/passwd
echo `grep femn < /etc/passwd`

管道命令:将一个集合的输出做会一个命令的输入 STDOUT,STDERR | STDIN

1
rpm -qa | sort | more

生成已安装包的列表,经过管理排序,再经过管理传给More命令显示

Shell基本类型

赋值与引用:
脚本编程中的一个主要构件 反引号 ( `` ) 它允许你将shell命令的输出赋给变量

1
2
3
4
5
testing=`date +%y%m%d` #你必须用把整个命令行命令圈起来
$HOME ${HOME}
echo # -n 在同一行显示一个文本字符串作为命令输出.
echo -e "OK! \n" # -e 开启转义
/$15 # 转义符

变量类型只能为Int,str,数组,而且等于两边不能有空格

数字
在将一个数学运算结果赋值给一个变量时,你可以用美元符和方括号将数字表达式圈起来

1
2
$[option]
$(())

注意只支持整数运算

1
2
3
var=1
var1=$[2 * 5]
var1=$(($var*5))

字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
var=femn
greeting="hello, ${var} !"
var="my name"
echo ${#var} #获取字符串长度
echo ${var:1:4} #字符串分片
if [-z str 为0 ]
# -z 字符串的长度为零则为真,退出状态为0.
if [-n str 非0]
# -n str 字符串的长度不为零则为真
var=http://www.aaa.com/123.htm
1. # 号截取,删除左边字符,保留右边字符.
echo ${var#*//} # www.aaa.com/123.htm
2. ## 号截取,删除左边字符,保留右边字符
echo ${var##*/} # 123.htm
3. %号截取,删除右边字符,保留左边字符
echo ${var%/*} # http://www.aaa.com
4. %% 号截取,删除右边字符,保留左边字符
echo ${var%%/*} # http:
5. 从左边第几个字符开始,及字符的个数
echo ${var:0:5} # http:
6. 从左边第几个字符开始,一直到结束
echo ${var:7} # www.aaa.com/123.htm
7. 从右边第几个字符开始,及字符的个数
echo ${var:0-7:3} # 123
8. 从右边第几个字符开始,一直到结束
echo ${var:0-7} #123.htm

数组

1
2
3
4
list=(1 2 3 4)
echo ${list[0]} #
echo ${list[@]} #获取数组中所有的元素
echo ${#list[@]} # 计算数组个数

shell语法

结构化命令允许你改变程序执行的顺序,在某些条件下执行一些命令而在其它条件下跳过另一些命令.

if-then语句,只能测试跟命令的退出状态码有关的条件.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
1.if command:then
commands
fi
# 使用一行,多用于终端
if [ $(ps -ef | grep -c "ssh") -gt 1 ]; then echo "true"; fi
# 如果else分支没有语句执行,就不要写这个else.
2.if command1
then
commands
else
commands
fi
# 使用一行,多用于终端
if [ $(ps -ef | grep -c "ssh") -gt 1 ]; then echo "true"; \
else echo 'false'; fi
3.if command1
then
commands
elif command2
then
commands
fi

case命令,替代elif语句来不断检查相同变量值.case命令提供了一个更清晰的方法来为变量每个可以的值指定不同的选项.

1
2
3
4
5
6
7
8
case $USER in
femn | pattern2 )
commands1;;
parrent3)
commands2;;
*)
commands;;
esac

test命令会将所有的标点和大小写也考虑在内

在其它编程语言中,if语句之后的对象是一个等式来测试是 true or false .bash shell并不是这样工作的.

当command1命令退出状态码是0,位于then部分的命令就会被执行.如果是非0则执行else命令块.

test命令提供了在if-then语句中测试不同条件的途径.
test命令格式:test condition(要测试的一系列参数和值)

1
2
3
4
5
6
7
# test condition==[ condition ]
# 你必须在左括号右侧和右括号左侧各加一个空格,否则会报错.
if test ${num1} -eq ${num2}
if [ ${num1} -eq ${num2} ]
# 复合条件测试
[ condition1 ] && [ condition2 ]
||

test可以判断3类条件:1.数值比较,2.字符串比较,3.文件比较
数值比较:

1
2
3
4
5
6
-gt: 大于(-g)
-lt: 小于(-l)
-eq: 等于(-e)
-ne: 不等于
-ge: 大于等于
-le: 小于等于

bash shell能处理的数仅有整数.使用bash计算器时,你可以让shell将浮点值作为字符串值存储进一个变量,如果你只是要通过echo语句来显示这个结果,那它能很好地工作,便它无法在基于数字的函数中工作.

字符串比较:

1
2
3
4
5
6
<
>
=
!=
-z
-n

字符串顺序:要测试一个字符串要比另一个字符串大就开始变得烦琐了,大于小于符号必须转义,否则shell会把它们当做重写向符号而把字符串值当做文件名.而且大于小于顺序和sort命令所采用的不同.test命令中大写字母会被当成小于小写字母.
if [Test \< test]

文件比较:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 检查new.sh文件是否比old.sh新,在此之前要先确认文件是否存在.
if [ ./new.sh -nt ./old.sh ]
if [ ./old.sh -ot ./new.sh ] #
if [ -e $HOME ] #检查文件是否存在 下面的所有检查都要满足这个前提条件
if [ -d $HOME ] # 检查文件是否存在并是个目录
if [ -f $HOME/.vimrc ] # 并是个文件
if [ ! -f $HOME/.vimrc ] # 如果不是文件则执行
if [ -r $HOME ] # 并是可读
if [ -w $HOME/.vimrc ] # 并是可写
if [ -x $HOME/.vimrc ] # 并是可执行
if [ -O $HOME/.vimrc ] # 并是 属于当前用户所有
if [ -s $HOME/.vimrc ] # 并是不为空,说明有数据,删除时就小心了.

if-then的高级特征:

双圆括号命令允许你将高级数学表达式放入比较中

1
(( expression ))

test命令只允许在比较中进行简单的算术操作.双圆括号命令提供了更多的为用过其它编程语言的程序员所熟悉的数学符号.你不需要将圆括号中表达式里的大于号转义,这是双圆括号命令提供的另一个高级特性.

用于高级字符串处理功能的双方括号. [[ expression ]]

1
2
3
4
if (( $var1 ** 2 > 90 )) || [[ $USER == f* ]]
# 幂运算之后比较大小
# 匹配$USER是否以f字母开头
# 模式匹配中,你可以定义一个正则表达式来匹配字符串值.

循环中定义的test命令和if-then语句中定义的是一样的格式.

你需要重复一组命令直到达到某个特定条件.for, while, until
for循环假定每个值都是用空格分割的

1
2
3
4
5
6
7
8
9
for var in list
do
commands
done
for file in `ls /etc`
for loop in 1 2 3 4 5
for var in item1 item2 ... itemN; do command1; command2… done;
for test in I don\'t know if "this'll " work

用通配符读取目录:你可以你for命令来自动遍历满是文件的目录.进行操作时,你必须在文件名或路径名使用通配符.它会强制shell命令使用文件扩展匹配(file globbing):它是生成匹配指定的通配符的文件名或路径外的过程.

1
2
3
4
5
6
7
8
9
10
11
12
13
for file in $HOME/*
do
if [-d "$file"]
# 为了解决含有空格的目录名或文件名,将$file变量用双引号圈起来
then
echo "$file is a directory"
elif [-f "$file"]
then
echo "$file is a file"
else
echo "$file is doesn't exist"
fi
done

C语言的for命令:通常会定义一个变量,然后这个变量会在每次迭代时自动改变.程序员会将这个变量当作计数器,并在每次迭代中让计数器增一或减一.
这是bash中C语言风格的for循环的基本格式:for((variable assignment;condition;iteration process))

1
2
3
4
5
6
7
8
9
10
11
for (i=0;i<10;i++)
{
print("the next number is %d\n",i)
}
for ((a=1,b=10;a<10;a++,b--))
do
echo "$a - $b"
done
# 使用多个变量时,循环会单独处理每个变量,
# 允许你为每个变量定义不同的迭代过程,但只能定义一种条件.

while
while命令在某种意义上是if-then语句和for循环的混杂体.它会在每个迭代的一开始测试test命令.只有测试条件成立,while命令才会继续遍历执行定义好的命令.

1
2
3
4
5
6
7
8
9
10
11
12
while test command
do
other command
done
#!/bin/bash
int=1
while(( $int<=5 ))
do
echo $int
let "int++"
done ))

break终止执行后面的所有循环
continue命令类似,只有一点差别,它不会跳出所有循环,仅仅跳出当前循环

until命令和while命令工作的方式完全相反
测试条件中用到的变量必须被修改,否则会进入一个无限循环.
嵌套循环nested loop:可以在循环内使用任何类型的命令,自然也包括其它循环命令.在使用nested loop时,你是在迭代中使用迭代,命令运行的次数是乘积关系.内部循环会有外部循环的每次迭代中遍历一遍它所有的值.
以两个for命令写个nested loop

shell变量

bash shell提供了不同的方法从用户处获得数据,包括命令行参数,命令行选项以及直接从键盘读取输入的能力.

命令行参数(添加在命令后的数据值)
  1. $?专属变量来保存上个执行命令的退出状态码(0:命令成功结束,126:没有执行权限,1:无效参数等通用未知错误)

  2. $1-$9 第一到第九个命令行参数

  3. ${10} # 第十个命令行参数的读取方式

当传给$0变量的真实字符串是整个脚本路径时,程序中就会使用整个路径,而不仅仅是程序名.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
vim tesh.sh
#!/bin/bash
name=`basename $0`
# basename命令仅仅会返回程序名
echo the command entered is $name
echo the command entered is $0
sh ./test.sh
# the command entered is test.sh
# the command entered is ./test.sh
sh ~/test.sh
# the command entered is test.sh
# the command entered is /home/femn/test.sh
# 检查命令行参数中是否有数据,有的话就执行then语句
if [ -n "$1" ]
# 所有命令行参数的个数
if [ $# -ne 2 ]
# $*和$@变量提供了对所有参数的快速访问
# $*变量将会把所有参数当成单个参数
for param in "$*"
# $@变量会单独处理每个参数
for param in "$@"

shell 命令

bash shell工具链中另一个工具是shift命令.bash shell提供了shell命令来帮助操作命令行参数.
默认情况下,shell命令会将参数变量减一,所以变量$2会移到$1,而变量$1的值会被删除并且无法恢复
变量$0是程序名不会改变.这是遍历命令行参数的另一个绝妙方法,尤其是你不知道有多少参数时.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
vim test1.sh
#!/bin/bash
# demonstration the shift command
count=1
while [ -n "$1" ]
do
echo "Parameter #$count = $1"
count=$(( $count+1 ))
shift
done
sh ./test1.sh femn leipengkai yuyu
# Parameter #1 = femn
# Parameter #2 = leipengkai
# Parameter #3 = yuyu

命令行选项(是跟在单破折线后面的单个字母,可以修改命令行为的单字母值)

当脚本看到”–” ,脚本会安全地将剩下的命令行参数当做参数来处理而不是选项.
你可以像处理命令行参数一样处理命令行选项
你经常遇到想在脚本中同时使用选项和参数的情况.
Linux处理这个问题的标准方式是用特殊的字符将二者分开,该字符(–双破折线)会告诉脚本选项何时结束,以及普通参数何时开始.

getopt命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
getopt -q ab:cd -a -b test1 -cde test2 test3
# 用getopt 命令生成的格式化后的版本来替换已有的命令行选项和参数
# -a -b 'test1' -c -d -- 'test2' 'test3'
vim test2.sh
#!/bin/bash
# extraction command line options and values.
while [ -n "$1" ]
do
case "$1" in
-a)
echo "Found the -a options";;
-b) param="$2"
echo "Found the -b options,with parameter value $param"
shift;;
-c)
echo "Found the -c options";;
--) shift
break;;
*) echo "$1 is not an optionso";;
esac
shift
done
count=1
for param in "$@"
do
echo "Parameter #$count: $param"
count=$(( $count +1 ))
done
sh ./test2.sh -b test2 -a -d
# Found the -b options,with parameter value test2
# Found the -a options
# -d is not an optionso
sh ./test2.sh -b test2 -a -b
# Found the -b options,with parameter value test2
# Found the -a options
# Found the -b options,with parameter value
# ./test2.sh: 18: shift: can't shift that many

你可以在脚本中使用getopt命令来格式化输入脚本的任何命令行选项和参数,但用起来略微复杂.

1
set -- `getopt -q ab:cd "$@" ` options parameters用"$@"

该方法将原本的脚本的命令行参数传给getopt命令,之后再将getopt的输出传给set命令,用getopt命令格式化后的命令行参数来替换原始命令行参数.

set 命令的选项之一是双破折线,它会将命令行参数替换成set命令的命令行的值.

1
2
3
4
5
6
7
8
9
10
11
12
13
sh ./test2.sh -ac
# 合并选项 增加下面这句
set -- `getopt -q ab:c "$@"`
while [ -n "$1" ]
# Found the -a options
# Found the -c options
sh ./test2.sh -a -b test1 -cd test2 test3
# Found the -a options
# Found the -b options,with parameter value 'test1'
# Found the -c options
# Parameter #1: 'test2'
# Parameter #2: 'test3'

但getopt命令并不擅长处理带空格的参数值,它会将空格当做参数分隔符,而不是根据双引号将二者当作一个参数.

getopts命令扩展了getopt命令的功能:getopts optstring variable

与getopt命令将命令行上找到的选项和参数处理后只生成一个输出不同,getopts命令能够和已有的shell参数变量对应地顺序工作.

getopts命令将当前参数保存在命令行中定义的variable中.它会用到两个环境变量,如果选项需要参数值,OPTARG环境变量就会保存这个值.OPTIND环境变量保存了参数列表中getopts正在处理的参数位置.

获得用户输入

你想在运行脚本时,问一个问题,并等运行脚本的人来回答.bash shell为此提供了read命令.
read命令接受从标准输入(键盘)或另一个文件描述符的输入.
echo -n -n会移除字符串末尾的换行符,允许脚本用户紧跟其后输入数据,而不是下一行
read -n1 -t 5 -p 接受单个字符后退出而不必回车,超时,直接在read命令行提示符
read -s隐藏方式读取,实际上数据会被显示,只是read命令会将文本颜色设成跟背景色一样.

1
2
3
4
5
6
vim test.sh
#!/bin/bash
# echo -n "ENter your name:"
# read name
read -p "ENter your name:" name
echo "Hello $name, Welcome"

从文件中读取.每次调用read命令会从文件中读取一行文本,当文件没能内容时,read命令会退出并返回一个非零的状态码.

最常见的方法是将文件通过cat命令后的输出通过管道直接传给含有read命令的while命令.cat test |while read line

Share Comments

WebSocket

网页的消息推送一般常见的实现方法有轮询,长连接,WebSocket.

为什么要用 WebSockets?

一个 WebSocket 连接允许在客户端和服务端之间进行全双工通讯,从而每一端都可以通过建立的连接向另一端推送数据.

WebSocket,以及与其相关的 服务端发送事件 (SSE) 及 WebRTC 数据通路 等技术之所以重要的原因是:
HTTP不能打开并一直保持连接,不能在服务端和 Web 浏览器之间进行频繁的数据推送.在这之前,大多数的 Web 应用会通过频繁的异步 JavaScript 和 XML (AJAX) 请求来实现长轮循

WebSocket并不限于以Ajax(或XHR)方式通信,因为Ajax技术需要客户端发起请求,而WebSocket服务器和客户端可以彼此相互推送信息;XHR受到域的限制,而WebSocket允许跨域通信.

实现 WebSocket

Web 浏览器和服务端都必须要实现 WebSocket 协议,以便建立和维护连接.因为 WebSocket 的连接是持续连通的,不像经典的 HTTP 连接,因此服务器需要做更多工作.

基于多线程或多进程的服务器不能很好地提供 WebSockets 服务,因为它们的设计是:打开一个连接,尽快处理完请求,然后关闭连接.
因此,采用 Tornado 或 打包了 gevent 的 Green Unicorn 等异步服务器,对于实现一个实用的服务端 WebSockets 都是必要的

而在客户端,使用 WebSocket 并不要求有某个 JavaScript 库.实现了 WebSocket 的 Web 浏览器都会通过 WebSockets 对象 导出所有必要的客户端功能.

github中的例子

对这个例子的的解释

Websocket资源

Share Comments