对开发和管理人员来说,docker是一个用容器开发、发布、运行应用程序的平台。Docker主要包含两个概念,镜像(Image)、容器(container)、Docker Registry:
-
镜像 ,镜像是一个可执行的package,包含程序运行需要的一切,如代码、库(libraries)、环境变量、配置文件等等
-
容器,容器是镜像的实例,可以通过
docker ps
查看当前运行的所有容器进程。 -
Docker Registry,镜像的集中式存储管理服务,一个Docker Registry包含多个仓库( Repository ) , 每个仓库可以包含多个标签( Tag ) ,每个标签对应一个镜像。
Docker架构图:
镜像和虚拟机
容器直接运行在linux上,并且和其它容器共享主机的内核,所以他比其它执行方式实用更少的内存,从而使得它更加轻量级。
而虚拟机运行了一个完整的用户操作系统,通过虚拟机虚拟访问主机资源,通常虚拟机提供环境需要的资源很多。
Docker版本:
- CE
- EE
Docker CE安装
清除历史版本
$ sudo yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-engine
设置仓库
-
安装需要的package
$ sudo yum install -y yum-utils \ device-mapper-persistent-data \ lvm2
-
设置稳定的仓库
$ sudo yum-config-manager \ --add-repo \ https://mirrors.ustc.edu.cn/docker-ce/linux/centos/docker-ce.repos
或者
$ sudo yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
因为该系统中我将/bin/python指向了python3.x的版本,所以执行时遇到了错误:
File "/usr/bin/yum-config-manager", line 135
except yum.Errors.RepoError, e:
^
解决版本就是修改/usr/bin/yum-config-manager
,将第一行指定的python作修改如下:
#!/usr/bin/python2.7 -tt
启用nightly和test repositories(可选)
#启用 $ sudo yum-config-manager --enable docker-ce-nightly $ sudo yum-config-manager --enable docker-ce-test # 禁用 $ sudo yum-config-manager --disable docker-ce-nightly
安装CE
sudo yum install docker-ce
或者查看可选的版本进行安装:
$ yum list docker-ce --showduplicates | sort -r
docker-ce.x86_64 3:18.09.1-3.el7 docker-ce-stable
docker-ce.x86_64 3:18.09.0-3.el7 docker-ce-stable
docker-ce.x86_64 18.06.1.ce-3.el7 docker-ce-stable
docker-ce.x86_64 18.06.0.ce-3.el7 docker-ce-stable
$ sudo yum install docker-ce-<VERSION STRING>
启动docker
$ sudo systemctl start docker
镜像源的修改
# vi /etc/docker/daemon.json
{
"registry-mirrors": [
"https://*****.mirror.aliyuncs.com",
"https://reg-mirror.qiniu.com/"
]
}
一些基础命令
## 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
## 批量删除已经停用的容器
docker container rm `docker ps -a|grep Exited|awk '{print $1}'`
容器(Containers)
在以前,我们要写一个Python的应用,我们第一件事情就是在机器上安装Python的运行环境,但是这会导致一个问题,你的环境要和你应用预期的一直,而且生产环境也是这样的。
但是如果有了Docker,你就可以将Python运行环境移植成一个镜像。然后,你的构建可以包含基础的python镜像和应用代码。
我们可以通过Dockerfile
去定义这样轻便的镜像。
Dockerfile
定义了容器内的环境发生了什么,在这个环境里网络接口、磁盘驱动器等资源的访问都将会被虚拟化,所以需要将端口映射到容器外部,并指定要拷贝至容器内访问的文件。这样通过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
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"]
开发完Dockerfile之后,下面就是构建应用:
$ docker build --tag=friendlyhello<:version> .
这样之后我们构建的镜像就会存在与我们本地的Docker注册表中。
运行容器:
// 终端运行
$ docker run -p 4000:80 friendlyhello
// 后台运行
$ docker run -d -p 4000:80 friendlyhello
查看后台运行的容器:
$ docker container ls
CONTAINER ID IMAGE COMMAND CREATED
1fa4ab2cf395 friendlyhello "python app.py" 28 seconds ago
停止指定的容器:
$ docker container stop 1fa4ab2cf395
将本地的镜像和注册表中的仓库作关联:
$ docker tag image username/repository:tag
// 示例:
$ docker tag friendlyhello gordon/get-started:part2
发布镜像:
$ docker push username/repository:tag
从远程仓库拉取尽量并运行:
$ docker run -p 4000:80 username/repository:tag
//例如:
$ docker run -p 4000:80 gordon/get-started:part2
服务(Services)
在分布式应用中,应用的不同部分称为服务,例如,有一个视频分享网站,它可能包含在数据库中储应用数据的服务、用户在后台上传视频的转码服务、前段的服务等等。
服务就是生产中容器,一个服务只运行一个镜像,但是它定义了镜像的运行方式,如应该用什么端口、服务达到所需的生产能力要多少容器的副本等等。
在Docker平台上,可以通过编写docker-compose.yml
做到定义、运行、伸缩服务。
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:
该文件告诉Docker:
- 从
username/repo:tag
拉取镜像 - 运行5个该镜像的实例命名为web,并限制每个实例最多只占用所有CPU资源的10%,以及50MB内存。
- 如果有一个失败,则立即重启容器。
- 映射容器内的80端口到主机的4000端口
- 通知所有容器通过一个叫
webnet
的网络负载均衡共享80端口 - 使用默认配置定义
webnet
在运行之前,我们需要安装Compose:
$ sudo curl -L "https://github.com/docker/compose/releases/download/1.23.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
$ sudo chmod +x /usr/local/bin/docker-compose
运行新定义的的应用
$ docker swarm init
// 启动并设置应用名为getstartedlab
$ docker stack deploy -c docker-compose.yml getstartedlab
// 下线应用
$ docker stack rm getstartedlab
$ docker swarm leave --force
和服务相关的命令:
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
集群(Swarm)
Swarm就是一组运行Docker并加入集群的机器。集群化之后,继续运行Docker命令将会通过swarm manager
在集群上执行。swarm上机器可以是物理机也可是虚拟机,这些都是集群的一个节点。
我们可以在Compose
文件中命令Swarm集群的manager
使用不同的策略运行容器,例如:最空的节点,即将容器填充至最少使用的机器上、在每台机器上启动一个容器实例。
创建swarm
swarm由多个节点构成,但是总的只有两种角色:manager和worker
启用swarm模式并让当前机器成为swarm manger:
$ docker swarm init --advertise-addr <myvm1 ip>
Swarm initialized: current node (rtocnzus280j43ijh0vit7k39) is now a manager.
To add a worker to this swarm, run the following command:
docker swarm join --token SWMTKN-1-2hlvapcqiuxghfkg6vnz3z81b3fvzu2lrn9l3ektylocnc5yvs-95cs5503f3mqnunhj7f89eboi 192.168.56.101:2377
To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.
登陆其他机器,将其加入swarm集群作为worker:
docker swarm join --token <token> <ip>:<port>
其它一些命令:
// 查看集群节点
docker node ls
// 从集群中删除该机器节点,如果是manger需要加--force
docker swarm leave <--force>
//在集群上发布服务
docker stack deploy -c docker-compose.yml getstartedlab
如果还没有集群资源,可以在本机创建虚拟机进行模拟,docker支持使用Hyper-V或者VirtualBox为驱动创建虚拟机,具体可以参考:VMS ON YOUR LOCAL MACHINE (MAC, LINUX, WINDOWS 7 AND 8)
Stack
Stack就是一组可以共享依赖、相互关联的服务,一个单独的stack可以定义并协调整个应用的功能,其实在Service小节,我们创建Compose文件,并使用docker stack deploy
部署服务时我们就开始接触了stack,在那一节中我们定义一个单独的服务stack,然后让它运行在一台主机上,但是在生产中我们通产不会这么做,通常我们都会有多个服务相互关联,并在多台机器上运行。
创建一个新的服务并发布
定义服务:
# docker-compose.yml
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
networks:
webnet:
对于第三节我们定义的docker-compose.yml
,我们发现这里多个一个对等服务visualizer
。
发布服务:
$ docker stack deploy -c docker-compose.yml getstartedlab
Updating service getstartedlab_web (id: angi1bf5e4to03qu9f93trnxm)
Creating service getstartedlab_visualizer (id: l9mnwkeq2jiononb5ihz9u7a4)
更详细的可以参考:Get started with Docker
上传镜像至HTTP协议的私服
登陆私服
# docker login http://docker.xxxx.com
登陆完之后,对应的授权信息会以加密的方式存储在/root/.docker/config.json
中。
因为Docker默认是不支持HTTP协议的私服操作,需要编辑/etc/docker/daemon.json
设置insecure-registries
使其支持HTTP方式:
{
"insecure-registries":["docker.xxxx.com"]
}
标记本地镜像
标记的母的就是将其归类到指定的仓库。
# docker tag [OPTIONS] IMAGE[:TAG] [REGISTRYHOST/][namespace/]NAME[:TAG]
例如:
# docker tag ananconda3-5.2.0:v1 docker.xxxx.com/rdc-base/ananconda3-5.2.0:v1
也可以删除标记:
# docker rmi [REGISTRYHOST/][nameSpace/]NAME[:TAG]
上传至私服
docker push docker.xxxx.com/rdc-base/ananconda3-5.2.0:v1
可能出现的问题
denied: requested access to the resource is denied
该问题一目了然是一个权限问题,通常来说问题出现在了namespace上,对应上面的就是rdc-base
,说实话刚接触Docker的人会对repository name这个东西比较懵逼,和我们传统的认知有点区别,我们传统的认为仓库就是不同软件不同版本的集合,但在Docker中仓库是同一类型的镜像,只是TAG(版本)不同。
那么我们这里的rdc-base
是什么呢,很多地方使用username解释的,所实话很误导,其实rdc-base
就是一个仓库的命名空间(namespace),对该repository名称做了一个限定,之所以很多地方可以直接用Docker hub或者私服的账号代替,那是因为Docker hub或者私服可能默认给你创建了一个同名的namespace,而很多账号创建后不一定会有默认同名的namespace,所以可能需要网页登录并手动创建。
待我们push完我们镜像到私服上,仓库实际上的完整名称应该为:rdc-base/ananconda3-5.2.0