docker是一个开源的应用容器引擎,基于Go语言开发,docker可以让开发者打包他们的应用及依赖包到一个轻量级、可移植的容器,然后发布到任何流行的系统。
在项目开发过程中,项目环境的搭建与维护十分繁杂。如果将我们的项目及其依赖的服务打包成一个独立的环境空间。当我们需要部署项目时,只需要启动该环境空间服务。这样可以大大减少我们维护环境的工作,而docker就是这样一个独立的环境空间。
docker与虚拟机都是用于创建独立的环境,但是它们又有不同之处。
我们执行docker命令时,其本质是通过docker daenon去操作docker的镜像,容器,仓库等来完成相关功能。
目前只支持windows10,且不能是家庭版的操作系统。
第一步:下载docker安装包:https://hub.docker.com/editions/community/docker-ce-desktop-windows
第二步:安装docker(双击运行)
一般都不会在windows下配置,主要在linux服务器上安装docker
第一步:安装依赖
yum install -y yum-utils device-mapper-persistent-data lvm2
第二步:添加源
yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
第三步:安装docker
yum -y install docker-ce
第四步:验证
docker -v
第五步:给普通用户添加docker使用权限
(1)创建用户---------------------------------命令:useradd dockeruse
(2)激活用户(设置密码)---------------------命令:passwd dockeruse
(3)创建docker群组(必须叫docker)-----------命令: sudo groupadd -g 2200 docker
(4)将用户放入docker群组---------------------命令:sudo usermod -G docker dockeruse(注意:sudo是linux系统管理指令,是允许系统管理员让普通用户执行一些或者全部的root命令的一个工具,相当于由管理员运行)
(5)查看 /etc/sudoers文件的权限--------------命令:ls -l /etc/sudoers(注意:sudoers文件是用户列表文件,把哪个用户放进去,哪个用户就能申请管理员权限)
(6)修改 /etc/sudoers文件权限----------------命令:chmod u+w /etc/sudoers(使其拥有者拥有可写权限)
(7)修改 /etc/sudoers文件--------------------命令:vi /etc/sudoers
(8)重启linux-----------------------------------:reboot
(9)验证。切换到普通用户启用docker,验证docker
(10)这样普通用户就以使用docker了
第六步:修改docker下载镜像源地址(默认docker是从国外下载镜像。速度非常慢)
(1)新建/etc/docker/daemon.json文件,并添加镜像地址
vi /etc/docker/daemon.json
添加语句:
{
"registry-mirrors": ["https://wixr7yss.mirror.aliyuncs.com"]
}
(2)重新加载配置文件
systemctl daemon-reload
(3)重启docker
systemctl restart docker
第七步:关闭防火墙(docker发布网站,需要关闭linux系统防火墙。否则外界可能无法访问)
systemctl stop firewalld //关闭防火墙
systemctl disable firewalld //设置开机不启动防火墙
docker version
docker info
systemctl start/stop/restart docker
docker images
docker images | grep nginx
docker search 镜像名称
docker pull 镜像名称:版本号
# 不加版本号默认下载最新版本
docker save 镜像名称:版本号>xxx.tar(一般为tar包)
docker load < xxx.tar
docker rmi 镜像名称(或者id)
docker tag 原镜像名:版本号 新镜像名:版本号
docker history 镜像名称
docker run -d --name=xxx 镜像名称:版本号 [command]
分析:
-d:表示让容器在后台运行
--name=xxx:给容器取名,不指定将自动生成
command:容器启动后需要执行的命令。如果不指定,容器启动后,将马上退出
docker ps
docker ps -a
# up: 正在运行状态
# exit:未运行状态
docker top 容器id/name
docker stats 容器id/name
docker start/restart/stop 容器id/name
docker pause/unpause 容器id/name
docker rm 容器id/name
docker rm -f 容器id/name
docker logs -f 容器id/name
docker exec -it 容器id/name bash
分析:
exec:表示要执行一条命令
-it:表示进入容器
bash:容器执行的命令,表示进入容器的bash中(有些容器是sh)
exit
docker cp 容器id:容器内文件路径 主机目录
docker cp 主机下文件路径 容器id:容器内的目录
docker inspect 容器id/镜像id
docker inspect 容器id/镜像id
查看Env字段内容
docker diff 容器id
docker hub是docker镜像的一个汇总平台,在上面可以查看很多镜像的信息
地址:https://hub.docker.com
第一步:拉去nginx镜像(这里以1.17.9版本为例)
docker pull nginx:1.17.9
第二步:创建启动nginx容器
docker run -d -p 8888:80 nginx:1.17.9
分析:
-d:表示后台运行容器
-p 80:80 指定端口映射(第一个8888表示在本地linux中开放的端口,第二个80表示nginx对外开放的端口。当访问者以linux的ip+linux的端口8888访问网址时,会跳转到容器的开发端口80中)
将待发布的网页代码包上传到服务器目录下(/user/test/test.html)
启动容器
docker run -d --name=nginx -p 8888:80 -v /home/ouyi/test:/usr/share/nginx/html nginx:1.17.9
分析:-v /home/ouyi/test:/usr/share/nginx/html 目录挂载,前面为linux中存放代码文件的目录,后面为nginx的发布目录
(1)创建网络。多个容器如果要互相通信,需要在一个网络内。
docker network create testlink //创建一个网络testlink
docker network ls //查看创建的所有网络
(2)拉取bitnami/mariadb镜像
MariaDB数据库管理系统是MySQL的一个分支。
docker pull bitnami/mariadb:10.3.22
(3)使用docker创建并启动bitnami/mariadb镜像
docker run -d --name=mariadb -e MARIADB_ROOT_PASSWORD=mariadb -e MARIADB_USER=bn_testlink -e MARIADB_PASSWORD=bn_testlink -e MARIADB_DATABASE=bitnami_testlink --net=testlink -v /home/ouyi/mariadb_data:/bitnami bitnami/mariadb:latest
分析:
-e:给容器设置环境变量。这里设置了四个环境变量分别是MARIADB_ROOT_PASSWORD(数据库管理员密码),MARIADB_USER(创建一个数据库用户),MARIADB_PASSWORD(设置用户密码),MARIADB_DATABASE(创建一个数据库)
-v:设置数据持久化,将容器中(/bitnami)的数据同步到主机上(/home/ouyi/mariadb_data)
--net:设置容器网络
(4)启动testlink镜像生成容器,如果本地无testlink镜像,会自动拉取
docker run -d -p 80:8080 -p 443:443 --name=testlink -e TESTLINK_DATABASE_USER=bn_testlink -e TESTLINK_DATABASE_PASSWORD=bn_testlink -e TESTLINK_DATABASE_NAME=bitnami_testlink --net testlink -v /home/ouyi/testlink:/bitnami bitnami/testlink:latest
(5)访问linux_ip:80网址
第一步:获取Jenkins镜像
docker pull jenkins/jenkins
第二步:启动Jenkins镜像创建容器
docker run -d --name=jenkins -p 80:8080 jenkins/jenkins
第三步:查看jenkins的Admin初始密码
方式一:查看日志
docker logs jenkins
方式二:查看初始密码文件
docker exec -it jenkins cat /var/jenkins_home/secrets/initialAdminPassword
第四步:登录 http:docker宿主机ipo:80 访问Jenkins网站
到这里Jenkins搭建完成
补充:我们创建Jenkins容器时,最好加上目录挂载。防止容器被删除,数据丢失。
docker run -d --name=jenkins -p 80:8080 -v /home/ouyi/jenkins:/var/jenkins_home jenkins/jenkins
跟接口测试不同,UI自动化测试一般耗时较长。因此UI自动化测试搭建分布式的平台很有必要。selinux团队维护了一个GRID框架,可以很好的解决UI自动化测试。
原理:我们的UI自动化代码会发送命令给HUB节点,HUB节点则会自动将任务分配给node节点。node节点会启动浏览器取执行自动化用例。
注意:
docker搭建GRID框架,可以参考github地址:https://github.com/SeleniumHQ/docker-selenium
我们先搭建一个HUB,两个node节点的框架,且node节点直接使用HUB内核。
docker pull selenium/hub
docker pull selenium/node-chrome-debug
补充:selenium/hub 、 selenium/node-chrome、 selenium/node-chrome-debug 三个镜像。其中selenium/node-chrome-debug启动后会启动一个VNC Server,在脚本执行过程中,本地可以连上VNC Server,通过界面查看服务器的脚本执行情况。我们这里使用selenium/node-chrome-debug镜像
docker run -p 5555:4444 -e GRID_MAX_SESSION=6 -d --name hub selenium/hub
分析:
-e GRID_MAX_SESSION=6:指定hub最大的连接session为6,及最多连接6个node节点
访问:http://服务器ip:5555,查看HUB节点控制信息
z1
启动第一个node节点:
docker run -d -p 5900:5900 -e NODE_MAX_INSTANCES=6 -e NODE_MAX_SESSION=6 --link hub:hub --name=node1 selenium/node-chrome-debug
分析:
-e NODE_MAX_INSTANCES=6 -e NODE_MAX_SESSION=6 : NODE_MAX_INSTANCES,NODE_MAX_SESSION都是node容器中的环境变量,一般设置为一样的值,表示最大能启动的浏览器数量。
--link hub:hub:将hub容器创建连接,共享ip等。用于HUB节点与node节点通信
启动第二个节点:
docker run -d -p 5901:5900 -e NODE_MAX_INSTANCES=6 -e NODE_MAX_SESSION=6 --link hub:hub --name=node2 selenium/node-chrome-debug
访问:http://服务器ip:5555,继续访问HUB节点端口。
可以查看node节点的信息。
#! /usr/bin/python
# coding: utf-8
import os
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
import pytest
class TestDemo:
def setup(self):
# 初始化driver
chrome_driver = os.path.abspath(r"/opt/webdriver") #驱动路径,需要放置在node节点的主机目录下
os.environ["webdriver.chrome.driver"] = chrome_driver
# 设置driver的配置文件
chrome_capabilities = {
"browserName": "chrome", # 设置浏览器类型
"version": "",
"platform": "ANY", # 设置操作系统类型
"javascriptEnabled": True,
"webdriver.chrome.driver": chrome_driver, # 设置驱动
}
self.driver = webdriver.Remote("http://192.168.1.102:5555/wd/hub", desired_capabilities=chrome_capabilities) # 注意remote的是HUB节点
# self.driver = webdriver.Chrome()
self.driver.maximize_window()
self.driver.implicitly_wait(5)
@pytest.mark.parametrize("name",[("华为"),("腾讯"),("阿里"),("百度"),("字节"),("平安"),("python"),("java"),("c语言"),
("shell"),("docker"),("selenium"),("pytest"),("django"),("flask"),("四川"),("成都"),
("老虎"),("猴子"),("狮子"),("狗"),("猫"),("欧毅")])
def testdemo(self,name):
self.driver.get("https://www.baidu.com/")
time.sleep(5)
self.driver.find_element(By.ID,"kw").send_keys(f"{name}")
self.driver.find_element(By.ID,"su").click()
time.sleep(5)
r = self.driver.title
assert r == f"{name}_百度搜索"
def teardown(self):
self.driver.quit()
if __name__ == "__main__":
os.system("pytest -n 2 -v -s test_demo.py")
注意:并不是我们搭建了GRID就是多进程了,而是我们的代码要使用pytest-xdst去启动多进程。而我们启动的进程,传递给HUB节点,HUB节点会自动的分配给node节点去执行这些进程。
windows和mac上的docker-step已经默认安装了docker-compose,所以我们这里只讲Linux下的安装。
第一步:访问https://github.com/docker/compose/releases,下载安装包
第二步:上传到服务器/usr/local/bin/下,并改名为docker-compose
第三步:增加执行权限
chmod +x /usr/local/bin/docker-compose
第四步:校验
docker-compose version
第一步:下载registry镜像
docker pull registry
第二步:使用registry镜像启动容器
docker run -d -p 5000:5000 -v /home/ouyi/registry:/var/lib/registry --restart always --name registry registry
分析:
--restart always : 表示启动docker时,容器也自动启动。
第三步:更改配置
cd /etc/docker
vi daemon.json
systemctl restart docker
# daemon.json
{
"registry-mirrors": [ "https://pee6w651.mirror.aliyuncs.com"],"insecure-registries": ["192.168.31.60:5000"]
}
# https://pee6w651.mirror.aliyuncs.com,为阿里云镜像仓库地址,192.168.31.60:5000为本地仓库地址
第三步:在本地仓库上传下载镜像
上传镜像:
(1)更改本地镜像的命令规范为:registry_url:port/ImageName:version
docker tag docker tag jdk_8u191:20190307 192.168.179.128:5000/jdk_8u191:20190307
(2)上传镜像到本地仓库(docker push ip:port/镜像名称:版本号)
docker push 192.168.179.128:5000/jdk_8u191:20190307
(3)下载镜像(docker pull ip:port/镜像名称:版本号)
docker pull ip_add:5000/jdk_8u191:20190307
使用dockerfile可以将我们自己的项目及其依赖构建为一个docker镜像。
指定基础镜像,FROM命令必须是dockerfile的首个命令。例如:我们要把我们的软件打包成一个镜像,必须指定一个操作系统镜像,在这个基础上继续安装我们的软件
为镜像生成元素标签信息。例如:镜像版本,作者等
指定运行容器时的用户名或UID,后续RUN也会使用指定用户。
dockerfile执行命令的核心部分。它接受命令作为参数并用于创建镜像。每条RUN命令在当前基础镜像上执行,并且会提交一个新镜像层。
设置容器的运行目录。后续的RUN,CMD,ENTRPOINT,ADD等dockerfile命令会在运行目录中执行。
指定容器启动的环境变量。
指定dockerfile构建镜像时的环境变量。
复制文件
设置容器运行时执行的默认命令
指定容器的“入口”,设置容器运行时执行的默认命令
容器健康状态检测
实战案例:使用Nginx镜像为基础镜像,添加我们自己的项目,构建一个新镜像
将本地代码放置在宿主机指定目录(放置在需要build的Dockerfile的路径下)
编写dockerfile文件,并放置到宿主机上的任意目录(/home/ouyi/mydockerfile/nginx/Dockerfile)
# 指定基础镜像
FROM nginx:latest
# 指定维护者信息
LABEL maintainer="hahaha"
# 设置容器启动时的环境变量
ENV NGINX_VERSION=1.17.9
# 设置构建镜像时的环境变量
ARG WORK_PWD=/usr/share/nginx/html # 设置为nginx的发布目录,方便我们将本地代码拷贝到这个目录
# 设置容器用户
USER root
# 设置RUN ,安装curl软件,设置软连接把nginx服务器的日志显示到宿主机的标准输出和标准错误输出中,这样可以使用docker logs查看日志
RUN apt-get -y update && apt-get install -y curl && ln -sf /dev/stdout /var/log/nginx/access.log && ln -sf /dev/stderr /var/log/nginx/error.log
# 讲解:
# /dev/stdout和/dev/stderr是linux系统的标准输出和错误输出
# /var/log/nginx/access.log和/var/log/nginx/error.log是nginx服务的标准输出和错误输出
# 设置工作目录
WORKDIR $WORK_PWD
# 复制代码文件到工作目录
COPY ./index.html $WORK_PWD # 注意:index.html文件,必须放置在宿主机和Dockerfile放置在一起,因为这里不能使用绝对路径
# 设置容器的默认端口
EXPOSE 80
# 设置容器运行时执行的命令,使用ENTRYPOINT也行
CMD ["nginx","-g","daemon off;"]
# 设置停止容器的策略
STOPSIGNAL SIGRTMAX
使用docker build构建镜像
格式如下: docker build -t 镜像名字:版本 -f Dockerfile路径 .
注意:后面必须加上一个.
docker build -t my_nginx_hello:1.0 -f /home/ouyi/mydockerfile/nginx/Dockerfile .
方式一:
方式二:
方式一讲RUN指令分开描述,方式二讲所有RUN指令合并在一起。那么这两种方式有什么区别呢??
build构建镜像时,对RUN指令会进行检测,如果RUN执行跟上次构建时没有任何变化,则会使用缓存。所以使用第一种方式会大大减小我们镜像迭代构建的时间。但是每执行一次RUN命令,则会构建一个新镜像层,所以当RUN指令过多时,会影响系统的IO性能。所以这两种方式我们应该结合使用,对于确定不再改变的RUN指令可以合并在一起。
在构建镜像时,每RUN一次都会构建一个镜像层,而每一个镜像层都是独立的。我们知道镜像层越多,对系统IO的压力就越大,那么为什么还要使用这种方式呢??
docker pull更快
docker pull时会一层一层的下载镜像中的所有镜像层,而有些镜像层在本地的某些镜像中已经被使用过,这时则不会重新下载这些镜像层。从而提高pull速度。
如上图,对于多个容器如果它们使用了相同的镜像层,则这些相同的镜像层只会在本地存在一份,供容器共同使用。从而减小了镜像的占用空间。
namespace(名声空间)是Linux系统的底层概念有一些不同类型的命名空间被部署在核内。而docker就是使用名声空间技术来实现隔离的,每创建一个容器就是创建一个名声空间。不同的名声空间之间是无法实现交互的,他们的进程,网络,IP等都是互相隔离的。
docker容器是一个单独的名声空间,他们之间是无法网络通信的,那么docker如何实现网络通信呢??docker网络通信主要有三种模式:bridge,container,host模式。
docker默认的网络通信模式是bridge模式,它是如何实现通信原理的呢?我们先了解两个名词:
虚拟网卡:虚拟网卡有两个特性
虚拟网桥(bridge):bridge有以下特性
docker的bridge模式中容器之间的通信原理:
将一个虚拟网卡安装在docker容器中,另一个虚拟网卡安装在bridge中。当容器A想要访问容器B时,会访问容器A中的虚拟网卡v1,而容器A中的虚拟网卡关联的另一个虚拟网卡v2则会在bridge中发送广播报文。而bridge中的v4网卡会接受到报文并同步给网卡v3,这样容器A和容器B就实现了通信。
docker的bridge模式中容器和外界通信:
docker使用端口映射的方式实现容器跟宿主机通信。
docker run -p 8080:80
如上:-p 8080:80会实现以下功能:
将访问宿主机8080端口的所有网络请求全部转发给容器的80端口,其本质是使用iptables命令,在网络请求到达前将请求的ip地址及端口更改了。
容器创建出来就自动加入到了bridge中,一台宿主机就只创建一个bridge。–network参数指定容器的网络模式,默认就是使用bridge模式。
container模式:指定容器使用另一个容器的网络
引入:我们有这样的场景,容器A和容器B必须要共享同一个网络。例如:容器A是我们自己开发的服务,但是还没有完成所有接口的开发,将容器A部署后我们就要对它进行接口测试。这个时候就需要写一个mock服务也就是容器B,让他覆盖在容器A之上,即访问容器A的请求需要先访问容器B。在容器B中我们会做判断,当请求的是已开发完成的接口则让它继续访问容器A,如果是请求的未开发完成的接口,则在mock服务中做处理,返回我们mock的数据。那么如何实现这个需求呢??
方案一:将mock服务打包到开发服务的镜像中。缺点:需要配置mock服务需要的依赖,可能会影响到开发服务。不推荐
方案二:启动mock服务的容器时,使用container网络模式,让它使用开发服务容器的网络
--link contianer_name
是将另一个容器的参数以环境变量的形式注入到该容器中。与--net=container:container_name
区别开
host模式:指定容器使用宿主机的网络
docker run -v 宿主机目录路径:容器中的目录路径
docker run -v 宿主机目录路径:容器1中的目录路径
docker run -v 宿主机目录路径:容器2中的目录路径
# 当两个容器跟宿主机的同一个路径共享数据时,则两个容器也实现了数据共享
应用场景:
当我们的服务部署在多个服务器的多个容器中时,日志也会打印在不同的服务器上。这时我们可以启动日志收集容器,使用数据共享的方式去收集日志
办法:使用“docker logs -f 容器id”命令查看日志,根据报错解决
办法:极大的原因是配置给容器的端口不是默认端口,当我们访问linux的ip+端口时,会跳转到容器的端口,这个端口是固定的。如果配置错误就会出现容器已经启动但远程无法访问。
办法:是因为我们宿主机的挂载目录没有权限,chmod 777 宿主机目录