本文由Scarb发表于金甲虫的博客,转载请注明出处

Docker Get Started 笔记

1. 适应

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
## List Docker CLI commands
docker
docker container --help

## Display Docker version and info
docker --version
docker version
docker info

## Execute Docker image
docker run hello-world

## List Docker images
docker image ls

## List Docker containers (running, all, all in quiet mode)
docker container ls
docker container ls --all
docker container ls -aq

2. 容器

接下来用Docker的方式来部署一个app。
Docker方式部署app分为三个层级,上往下分别是

  • 堆栈
  • 服务
  • 容器

2.1 Dockerfile

容器包含了一整套环境,可以通过写Dockerfile的方式定义一个容器。
进入一个空的目录,接下来定义一个包含官方python环境的Dockerfile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 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
COPY . /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"]

然后编写app.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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)

requirements.txt

1
2
Flask
Redis

这样就创建了一个python flask web app

2.2 构建和运行web app

接下来构建app。

1
2
# 用build命令创建一个Docker镜像,用-t指令指定一个名字。
docker build -t frientlyhello .
1
2
3
4
# 查看本地的镜像
$ docker image ls
REPOSITORY TAG IMAGE ID
friendlyhello latest 326387cea398

运行app

1
2
# 用run命令运行app,并将容器的80端口绑定到本地的4000端口
docker run -p 4000:80 friendlyhello

访问 http://localhost:4000 就可以看到想要的页面。

1
2
# 用-d选项后台运行app
docker run -d -p 4000:80 friendlyhello
1
2
3
4
# 列出正在运行的容器
$ docker container ls
CONTAINER ID IMAGE COMMAND CREATED
1fa4ab2cf395 friendlyhello "python app.py" 28 seconds ago
1
2
# 停止某个容器
docker container stop 1fa4ab2cf395

2.3 远程镜像仓库,镜像上传和拉取

这里我用了阿里云容器镜像服务管理镜像。

1
2
# 登录阿里云Docker Registry
$ docker login --username=username registry.cn-hangzhou.aliyuncs.com
1
2
# 拉取镜像
docker pull registry.cn-hangzhou.aliyuncs.com/scarb/get-started:tag
1
2
3
4
# 为镜像打标签
docker tag [ImageId] registry.cn-hangzhou.aliyuncs.com/scarb/get-started:tag
# 推送镜像
docker push registry.cn-hangzhou.aliyuncs.com/scarb/get-started:tag

然后可以run远程的镜像。Docker会先pull服务器上的镜像,然后运行。

1
docker run -p 4000:80 registry.cn-hangzhou.aliyuncs.com/scarb/get-started:tag

2.4 命令集合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
docker build -t friendlyhello .  # Create image using this directory's Dockerfile
docker run -p 4000:80 friendlyhello # Run "friendlyname" mapping port 4000 to 80
docker run -d -p 4000:80 friendlyhello # Same thing, but in detached mode
docker container ls # List all running containers
docker container ls -a # List all containers, even those not running
docker container stop <hash> # Gracefully stop the specified container
docker container kill <hash> # Force shutdown of the specified container
docker container rm <hash> # Remove specified container from this machine
docker container rm $(docker container ls -a -q) # Remove all containers
docker image ls -a # List all images on this machine
docker image rm <image id> # Remove specified image from this machine
docker image rm $(docker image ls -a -q) # Remove all images from this machine
docker login # Log in this CLI session using your Docker credentials
docker tag <image> username/repository:tag # Tag <image> for upload to registry
docker push username/repository:tag # Upload tagged image to registry
docker run username/repository:tag # Run image from a registry

3. 服务

这部分中,介绍服务的概念,并实现负载均衡。

在Docker平台,可以通过编写docker-compose.yml文件的方式来定义、运行和分配服务。

3.1 编写docker-compose.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
version: "3"
services:
web:
# replace username/repo:tag with your name and image details
image: username/repo:tag
deploy:
replicas: 5
resources:
limits:
cpus: "0.1"
memory: 50M
restart_policy:
condition: on-failure
ports:
- "4000:80"
networks:
- webnet
networks:
webnet:
  • 拉取[image]
  • 运行5个服务,每个服务是[image]的实例,叫做web。限制每个实例使用CPU所有核的10%,50MB的内存。
  • 如果一个容器启动失败,立即重启
  • 映射web的80端口到本地机器的4000端口
  • 让web的容器共享80端口,实现一个负载均衡的网络,叫webnet
  • 用默认设置定义webnet网络

3.2 启动服务,更改配置,停止服务

1
2
3
4
# 在docker stack deploy之前启动虫群
docker swarm init
# 用docker-compose.yml部署应用,叫getstartedlab
docker stack deploy -c docker-compose.yml getstartedlab

这样我们就在机器上启动了一个服务,在5个容器中运行着我们之前部署的镜像。

1
2
# 查看服务ID
docker service ls

服务中的一个容器叫做任务,任务有独立的ID。一个服务会创建[replicas]个任务。

1
2
3
4
# 查看服务下的任务
docker service ps getstartedlab_web
# 查看正在运行的容器IDs
docker container ls -q

可以通过修改docker-compose.yml文件的方式修改服务,docker支持热部署,修改文件以后只要重新部署就可以应用修改。

1
2
3
4
# 取消部署
docker stack rm getstartedlab
# 停止虫群
docker swarm leave --force

3.3 命令集合

1
2
3
4
5
6
7
8
docker stack ls                                            # List stacks or apps
docker stack deploy -c <composefile> <appname> # Run the specified Compose file
docker service ls # List running services associated with an app
docker service ps <service> # List tasks associated with an app
docker inspect <task or container> # Inspect task or container
docker container ls -q # List container IDs
docker stack rm <appname> # Tear down an application
docker swarm leave --force # Take down a single node swarm from the manager

4. 虫群

虫群是一组运行Docker的机器组成的集群。
由虫群管理机向工人机发布消息。
docker swarm init命令将Docker的模式从单机切换到虫群模式开始使用。

1
2
# 切换到swarm模式,并成为管理机
docker swarm init

4.1 创建集群

我们创建两台虚拟机,组一个虫群。一台为管理机,一台工人机。

1
2
3
4
5
6
7
8
# 用VirtualBox驱动创建虚拟机
docker-machine create --driver virtualbox myvm1
docker-machine create --driver virtualbox myvm2
# 列出虚拟机信息
docker-machine ls
NAME ACTIVE DRIVER STATE URL SWARM DOCKER ERRORS
myvm1 - virtualbox Running tcp://192.168.99.100:2376 v17.06.2-ce
myvm2 - virtualbox Running tcp://192.168.99.101:2376 v17.06.2-ce
1
2
3
# 如果已经创建好虚拟机,启动
docker-machine start myvm1
docker-machine start myvm2
1
2
3
4
5
6
7
8
9
# 用ssh发布命令,让myvm1运行docker swarm init成为管理机
$ docker-machine ssh myvm1 "docker swarm init --advertise-addr 192.168.99.100:2377"
To add a worker to this swarm, run the following command:

docker swarm join \
--token <token> \
<myvm ip>:<port>

To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.

注意:在机器的2377端口运行docker swarm initdocker swarm join命令,或者不设,取默认值。2376端口是Docker daemon端口,不要在这个端口上运行命令。

1
2
3
4
# 将第二台虚拟机作为工作机加到管理机
$ docker-machine ssh myvm2 "docker swarm join \
--token <token> \
<ip>:2377"
1
2
3
4
5
# 在管理机运行docker node ls查看虫群中的节点
$ docker-machine ssh myvm1 "docker node ls"
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS
brtu9urxwfd5j0zrmkubhpkbd myvm2 Ready Active
rihwohkh3ph38fhillhhb84sk * myvm1 Ready Active Leader

4.2 在集群上部署app

1
2
3
4
# 将本地目录的docker-compse.yml复制到myvm的根目录
docker-machine scp docker-compse.yml myvm1:~
# 在myvm1部署应用
docker-machine ssh myvm1 "docker stack deploy -c docker-compose.yml getstartedlab"

用ssh与虚拟机进交互很繁琐,配置一下shell环境,可实现直接在myvm1运行shell。

1
2
3
4
5
6
7
8
9
10
11
12
$ docker-machine env myvm1
export DOCKER_TLS_VERIFY="1"
export DOCKER_HOST="tcp://192.168.99.100:2376"
export DOCKER_CERT_PATH="/Users/sam/.docker/machine/machines/myvm1"
export DOCKER_MACHINE_NAME="myvm1"
# Run this command to configure your shell:
eval $(docker-machine env myvm1)
# 运行eval会设置shell环境为myvm1,运行了之后查看当前的机器(用*表示)
$ docker-machine ls
NAME ACTIVE DRIVER STATE URL SWARM DOCKER ERRORS
myvm1 * virtualbox Running tcp://192.168.99.100:2376 v17.06.2-ce
myvm2 - virtualbox Running tcp://192.168.99.101:2376 v17.06.2-ce
1
2
# 取消shell的环境配置
eval $(docker-machine env -u)

注意:如果镜像是远程私有镜像,要在部署的时候添加--with-registry-auth选项,这个选项处理本地登录token。有这个选项后节点机才能登录,拉取镜像。
docker login registry.example.com
docker stack deploy --with-registry-auth -c docker-compose.yml getstartedlab

用myvm1或者myvm2的地址访问app

当新的机器资源被加到集群中后,重新运行docker stack deploy即可使用新的机器。

1
2
3
4
5
6
# 卸载stack
docker stack rm getstartedlab
# 重启机器
docker-machine start <machine-name>
docker-machine stop $(docker-machine ls -q) # Stop all running VMs
docker-machine rm $(docker-machine ls -q) # Delete all VMs and their disk images

4.3 命令列表

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 virtualbox myvm1 # Create a VM (Mac, Win7, Linux)
docker-machine create -d hyperv --hyperv-virtual-switch "myswitch" myvm1 # Win10
docker-machine env myvm1 # View basic information about your node
docker-machine ssh myvm1 "docker node ls" # List the nodes in your swarm
docker-machine ssh myvm1 "docker node inspect <node ID>" # Inspect a node
docker-machine ssh myvm1 "docker swarm join-token -q worker" # View join token
docker-machine ssh myvm1 # Open an SSH session with the VM; type "exit" to end
docker node ls # View nodes in swarm (while logged on to manager)
docker-machine ssh myvm2 "docker swarm leave" # Make the worker leave the swarm
docker-machine ssh myvm1 "docker swarm leave -f" # Make master leave, kill swarm
docker-machine ls # list VMs, asterisk shows which VM this shell is talking to
docker-machine start myvm1 # Start a VM that is currently not running
docker-machine env myvm1 # show environment variables and command for myvm1
eval $(docker-machine env myvm1) # Mac command to connect shell to myvm1
& "C:\Program Files\Docker\Docker\Resources\bin\docker-machine.exe" env myvm1 | Invoke-Expression # Windows command to connect shell to myvm1
docker stack deploy -c <file> <app> # Deploy an app; command shell must be set to talk to manager (myvm1), uses local Compose file
docker-machine scp docker-compose.yml myvm1:~ # Copy file to node's home dir (only required if you use ssh to connect to manager and deploy the app)
docker-machine ssh myvm1 "docker stack deploy -c <file> <app>" # Deploy an app using ssh (you must have first copied the Compose file to myvm1)
eval $(docker-machine env -u) # Disconnect shell from VMs, use native docker
docker-machine stop $(docker-machine ls -q) # Stop all running VMs
docker-machine rm $(docker-machine ls -q) # Delete all VMs and their disk images

5. 堆叠

堆叠是一组共享设置,相互关联的服务。并且可以统一编排和伸缩。
之前所用的Compose和docker stack deploy就是用了堆叠的概念。
不过这仅仅是单个服务在单个机器上跑。

5.1 新建堆叠

修改docker-compose.yml,添加visualizer来显示机器上的容器状况。
添加redis来存储当前页面被访问的次数。

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
version: "3"
services:
web:
# replace username/repo:tag with your name and image details
image: username/repo:tag
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
redis:
image: redis
ports:
- "6379:6379"
volumes:
- "/home/docker/data:/data"
deploy:
placement:
constraints: [node.role == manager]
command: redis-server --appendonly yes
networks:
- webnet
networks:
webnet:

redis会部署在集群管理机上,在管理机上为redis创建文件夹

1
docker-machine ssh myvm1 "mkdir ./data"
1
2
3
4
5
6
7
8
# 部署
$ docker stack deploy -c docker-compose.yml getstartedlab
# 查看服务是否都运行起来
$ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
x7uij6xb4foj getstartedlab_redis replicated 1/1 redis:latest *:6379->6379/tcp
n5rvhm52ykq7 getstartedlab_visualizer replicated 1/1 dockersamples/visualizer:stable *:8080->8080/tcp
mifd433bti1d getstartedlab_web replicated 5/5 gordon/getstarted:latest *:80->80/tcp