搭建docker,学习官方文档

想要了解Docker的一些特性,这篇文章很有帮助。

安装过程参考docker docs

一、安装docker

1.首先卸载旧版的docker(如果有的话)

sudo apt remove docker docker-engine docker.io

2.SET UP THE REPOSITORY

sudo apt update
sudo apt install apt-transport-https ca-certificates curl sofeware-properties-common
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -

验证

sudo apt-key fingerprint 0EBFCD88

SET UP THE STABLE REPOSITORY

sudo add-apt-repository “deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable”

安装docker ce

sudo apt update
sudo apt install docker-ce

验证

sudo docker run hello-world

一些docker操作的命令

sudo docker images 
## 可以查看已经下载的镜像
sudo docker rmi IMAGEID
## 删除他特定的镜像
sudo docker ps -a
## 可以查看创建的所有的容器
sudo docker stop  $(sudo docker ps -a -q)
## 停止所有容器
sudo docker stop  $(sudo docker ps -a -q)
## 删除所有容器

为了无需每次都输入sudo ,创建新的用户组

sudo groupadd docker ## 可能提示已经创建过,可能是安装过程已经创建

将当前用户添加到用户组

sudo usermod -aG docker $USER

注销在登陆后,就可以不输入sudo 运行docker

二、Get started

Container 搭建一个app

cd 到一个空目录

创建一个Dockerfile

# Use an official Python runtime as a parent image
FROM python:2.7-slim

# Set the working directory to /app
WORKDIR /app

# Copy the current directory contents into the container at /app
ADD . /app

# Install any needed packages specified in requirements.txt
RUN pip install --trusted-host pypi.python.org -r requirements.txt

# Make port 80 available to the world outside this container
EXPOSE 80

# Define environment variable
ENV NAME World

# Run app.py when the container launches
CMD ["python", "app.py"]

将requirements.txt 和 app.py 放在同一目录下

requirements.txt

Flask
Redis

app.py

from flask import Flask
from redis import Redis, RedisError
import os
import socket

# Connect to Redis
redis = Redis(host="redis", db=0, socket_connect_timeout=2, socket_timeout=2)

app = Flask(__name__)

@app.route("/")
def hello():
    try:
        visits = redis.incr("counter")
    except RedisError:
        visits = "<i>cannot connect to Redis, counter disabled</i>"

    html = "<h3>Hello {name}!</h3>" \
           "<b>Hostname:</b> {hostname}<br/>" \
           "<b>Visits:</b> {visits}"
    return html.format(name=os.getenv("NAME", "world"), hostname=socket.gethostname(), visits=visits)

if __name__ == "__main__":
    app.run(host='0.0.0.0', port=80)

运行

然后再Dockerfile所在目录执行如下命令

docker bild -t friendlyhello

运行app

docker run -p 4000:80 friendlyhello

访问主机4000端口可以得到网页返回内容

curl http://localhost:4000

分享image

登陆docker账号

登陆

本地的镜像通过username/repository:tag这样的规则关联起来
给镜像打标签

docker tag friendlyhello kotar/get-started:part2

查看现在的镜像

images

上传刚才打过标签的镜像到云端仓库

错误

错误的原因是,username必须是最初登陆的那一个,随便起一个的话会去找别人的库,当然没有权限。重新上传一下。

成功



Service

为了衡量app并实现负载均衡,我们必须上升到service层,Services are really just “containers in production.” service,它包括了一个镜像,可以定义镜像运行的端口,副本数量等。其实是container运行过程中的别称。
衡量一个service的容量,以便于在运行时给他分配相应的资源。
docker中使用docker-compose.yml定义运行和衡量services。

docker-compose.yml

一个例子

version: "3"
services:
  web:
    # replace dockerkotar/get-started:part2 with your name and image details
    image: dockerkotar/get-started:part2
    deploy:
      replicas: 5
      resources:
        limits:
          cpus: "0.1"
          memory: 50M
      restart_policy:
        condition: on-failure
    ports:
      - "80:80"
    networks:
      - webnet
networks:
  webnet:
  • 使用之前创建的镜像
  • 运行五个实例作为一个叫web的service,每个最多使用10%的CPU core和50M内存
  • 将主机的80端口映射到web的80端口
  • web中的容器通过一个负载均衡网络(webnet)共享端口80(在内部,容器本身在临时端口上发布到web的端口80)。
  • webnet 使用默认设置,(负载均衡的overlay网络)

运行负载均衡的app

docker swarm init

swarm init

(下一部分会讲解,不运行这个命令会有“this node is not a swarm manager”错误)
运行

docker stack deploy -c docker-compose.yml getstartedlab

deploy

现在程序在一个节点运行了五个实例

查看service id

docker service ls

service

service 中的每一个container叫做一个task,每一个task被赋于一个唯一的ID,可以使用以下命令查看

docker service ps getstartedlab_web

Task

访问app几次,查看效果

curl -4 http://localhost

web res

hostname 会变化,是考虑到了负载均衡

最后关闭app和swarm

docker stack rm getstartedlab
docker swarm leave --force

shutdown



Swarm

A swarm is a group of machines that are running Docker and joined into a cluster.
swarm就是一组运行docker的机器组成的集群,这个机器可以是物理机也可以是虚拟机。
Swarm managers是集群中执行命令和授权其他机器作为worker加入集群的机器。
(这里我有疑问,既然已经docker有那么多优点,和虚拟机不同,不需要装操作系统等,但这里操作的时候为什么又要依赖virtualbox,感觉这样就浪费了docker的优势。可能是我的理解还不够深入,继续研究)

1.创建一个集群

(1)安装虚拟机virtualbox,以支持hypervisor

在 /etc/apt/sources.list 中添加(mydist替换成codename,ubuntu16.04是xenial(可以通过cat /etc/os-release查看
))

deb https://download.virtualbox.org/virtualbox/debian <mydist> contrib

添加keys

wget -q https://www.virtualbox.org/download/oracle_vbox_2016.asc -O- | sudo apt-key add -
wget -q https://www.virtualbox.org/download/oracle_vbox.asc -O- | sudo apt-key add -

安装virtualbox

sudo apt-get update
sudo apt-get install virtualbox-5.2

(2)安装docker-compose和docker-machine

a. docker-compose

参考安装docker-compose的官方文档

sudo curl -L https://github.com/docker/compose/releases/download/1.21.2/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-compose

安装command completion for the bash and zsh

先有的版本command completion for bash 已经可用
只安装command completion for zsh

mkdir -p ~/.zsh/completion
curl -L https://raw.githubusercontent.com/docker/compose/1.21.2/contrib/completion/zsh/_docker-compose > ~/.zsh/completion/_docker-compose

在~/.zshrc中添加$fpath

fpath=(~/.zsh/completion $fpath)

确保compinit被加载在~/.zshrc中添加

autoload -Uz compinit && compinit -i

重启shell

exec $SHELL -l

然后验证docker-compose的安装

docker-compose --version

如果能够正确的输出版本信息就表示成功

b. docker-machine

docker-machine 是一个管理和供应docker化的hosts的工具。可以用docker-machine 安装docker到一个或者多个虚拟系统中(可以是远程或本地,ubuntu,windows,mac),docker化的主机本省可以被认为是一个machine。

docker-machine

安装docker-machine

下载docker-machine,然后安装到/usr/local/bin/docker-machine

base=https://github.com/docker/machine/releases/download/v0.14.0 &&
curl -L $base/docker-machine-$(uname -s)-$(uname -m) >/tmp/docker-machine &&
sudo install /tmp/docker-machine /usr/local/bin/docker-machine

检查docker-machine是否安装成功。

docker-machine version

安装bash completion scripts,运行以下命令

base=https://raw.githubusercontent.com/docker/machine/v0.14.0
for i in docker-machine-prompt.bash docker-machine-wrapper.bash docker-machine.bash
do
	sudo wget "$base/contrib/completion/bash/${i}" -P /etc/bash_completion.d
done

然后使之生效

source /etc/bash_completion.d/docker-machine-prompt.bash

修改~/.bashrc,添加 $(__docker_machine_ps1) 到 PS1 变量 (如何添加现在还不清楚,我把它添加到了最前部)

PS1='[\u@\h \W$(__docker_machine_ps1)]\$ '

(3)创建虚拟机

docker-machine create --driver virtualbox myvm1

docker-machine create --driver virtualbox myvm2

install vms

安装到这里我解决了之前的疑问(既然已经docker有那么多优点,和虚拟机不同,不需要装操作系统等,但这里操作的时候为什么又要依赖virtualbox,不是又要创建虚拟机装系统吗,感觉这样就浪费了docker的优势。),查看创建虚拟机时的输出信息,你会发现虚拟机中安装的系统不是普通linux镜像,而是一个叫做boot2docker.iso的镜像。
到boot2docker官方网页可以看到

boot2docker is a lightweight Linux distribution based on Tiny Core Linux made specifically to run Docker containers. It runs completely from RAM, weighs ~27MB and boots in ~5s

boot2docker是一个轻量级的linux分布式系统,其基于Tiny Core Linux,是为了运行docker容器专门打造的。它完全运行在内存中,27MB的大小,五秒之内就可以启动。

查看虚拟机,获取他们的IP

docker-machine ls

docker-machine ls

(4)初始化swarm和其节点

将第一个虚拟机作为manager,将第二个作为worker。

docker-machine ssh myvm1 “docker swarm init --advertise-addr 192.168.99.100:2376”

docker-machine ssh manager

使用2376端口添加worker的时候错误,建议改用2377,说明如下:

Always run docker swarm init and docker swarm join with port 2377 (the swarm management port), or no port at all and let it take the default.

The machine IP addresses returned by docker-machine ls include port 2376, which is the Docker daemon port. Do not use this port or you may experience errors.

把刚才的swarm manager退出

docker-machine ssh myvm1 “docker swarm leave --force”

然后重新创建

docker-machine ssh myvm1 “docker swarm init --advertise-addr 192.168.99.100:2377”

把myvm2加入作为worker 注意重新创建后token已经变化。

docker-machine ssh myvm2 “docker swarm join --token SWMTKN-1-14wh5kn71lnx5r72o29ppdrhq3h1fu2qk37pwtejexis0hrnwc-3h7zhw9ero8i212e7v2x2h28p 192.168.99.100:2377”

add worker

使用docker node ls在manager节点查看集群中的节点

docker-machine ssh myvm1 “docker node ls”

nodes

以上就是一个简单的集群

在swarm集群上部署app

(2)配置一个docke-machine shell

上边几步都是通过docker-machine ssh操作虚拟机的,然而这样操作有点麻烦。
另一种可以替代的选择是使用docker-machine env ,这种方法允许使用本地的docker-compose.yml来部署app

docker-machine env myvm1

env myvm1

然后运行以上结果输出命令的最后一行(去掉井号)

eval $(docker-machine env myvm1)

然后运行dockers-machine ls命令确认myvm1是否是active

docker-machine ls

docker-machine ls

配置完之后既可以连接到myvm1,又可以访问本地的文件。

如果想重置env

eval $(docker-machine env -u)

(3)开始部署

部署的命令和使用的云端镜像都是service部分的。
找到上一部分使用的docker-compose.yml的目录,然后u

docker stack deploy -c docker-compose.yml getstartedlab

试用以下命令查看集群中的task(也就是container)

docker stack ps getstartedlab

docker stack deploy

使用docker-machine ls 查看虚拟机的IP,然后使用curl访问他们两个之中任意一个

docker-machine ls
curl 192.168.99.100

会看到返回的html源码

多访问几次,会发现五个container都能访问到,这是因为swarm中的nodes加入了一个ingress routing mesh,其效果如下

ingress network



Stacks

stack是一组相关联的services,他们共享以来可以统一调度。stack是分布式app的最高层。

之前在Service部分运行的stack是单一service在单一host,到这在实际操作中并不常见,这一部分就要部署一个多Service的应用。

添加一个新Service并重新部署

添加一个service–visualizer,添加后的docker-compose.yml如下:

version: "3"
services:
    web:
        # replace dockerkotar/get-started:part2 with your name and image details
        image: dockerkotar/get-started:part2
        deploy:
            replicas: 5
            restart_policy:
                condition: on-failure
            resources:
                limits:
                    cpus: "0.1"
                    memory: 50M
        ports:
            - "80:80"
        networks:
            - webnet
    visualizer:
        image: dockersamples/visualizer:stable
        ports:
            - "8080:8080"
        volumes:
            - "/var/run/docker.sock:/var/run/docker.sock"
        deploy:
            placement:
                constraints: [node.role == manager]
        networks:
            - webnet
networks:
    webnet:

volumes 中的属性让visualizer可以访问主机的socket文件

placement限定了仅在manager结点运行

确认docker配置到了myvm1的环境(eval $(docker-machine env myvm1))
重新部署以下app

如果没有移除之前在service部分创建的应用,先将其移除,不然会引起端口占用错误。

docker stack rm getstartedlab

然后部署

docker stack deploy -c docker-compose.yml getstaetedlab

(部署的时候遇到了yaml语法错误,注意:yaml使用缩进表示层级,层级不能乱,而且禁止使用tab缩进)

访问visualizer

visualizer

docker的学习告一段落,官网还有使用AWS部署docker的教程以后再看。



Summary