Docker版本选择:
Docker之前没有区分版本,但是2017年推出(将docker更名为)新的项目Moby,github地址:https://github.com/moby/moby,Moby项目属于Docker项目的全新上游,Docker将是一个隶属于的Moby的子产品,而且之后的版本之后开始区别为CE版本(社区版本)和EE(企业收费版),CE社区版本和EE企业版本都是每个季度发布一个新版本,但是EE版本提供后期安全维护1年,而CE版本是4个月
Docker安装
下载rpm包安装:
官方rpm包下载地址:
https : //download.docker.com/linux/centos/7/x86_64/stable/Packages/阿里全世界下载地址:
https ://mirrors.aliyun.com/docker-ce/linux/centos/7/x86_64/stable/Packages/
Centos yum源安装:
- 下载docker源
rm -rf /etc/yum.repos.d/* wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo wget -O /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo wget -O /etc/yum.repos.d/docker-ce.repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
- 安装
yum install docker-ce
- 启动
systemctl start docker
systemctl enable docker
Ubuntu安装
版本:Ubuntu 18.04.3
参考:https ://yq.aliyun.com/articles/110806
- 阿里脚本安装最新版
- 使用官方安装脚本自动安装(仅适用于公网环境)
curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun
- 自定义版本下载
- 配置阿里yum源
sudo vim /etc/apt/sources.list deb http://mirrors.aliyun.com/ubuntu/ bionic main restricted universe multiverse deb-src http://mirrors.aliyun.com/ubuntu/ bionic main restricted universe multiverse deb http://mirrors.aliyun.com/ubuntu/ bionic-security main restricted universe multiverse deb-src http://mirrors.aliyun.com/ubuntu/ bionic-security main restricted universe multiverse deb http://mirrors.aliyun.com/ubuntu/ bionic-updates main restricted universe multiverse deb-src http://mirrors.aliyun.com/ubuntu/ bionic-updates main restricted universe multiverse deb http://mirrors.aliyun.com/ubuntu/ bionic-proposed main restricted universe multiverse deb-src http://mirrors.aliyun.com/ubuntu/ bionic-proposed main restricted universe multiverse deb http://mirrors.aliyun.com/ubuntu/ bionic-backports main restricted universe multiverse deb-src http://mirrors.aliyun.com/ubuntu/ bionic-backports main restricted universe multiverse
- 安装必要的一些系统工具
sudo apt-get update sudo apt-get -y install apt-transport-https ca-certificates curl software-properties-common
2. 安装GPG证书
curl -fsSL http://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | sudo apt-key add -
3. 写入软件源信息
sudo add-apt-repository "deb [arch=amd64] http://mirrors.aliyun.com/docker-ce/linux/ubuntu $(lsb_release -cs) stable"
4. 更新
sudo apt-get -y update
5. 发现Docker-CE的版本:
# apt-cache madison docker-ce docker-ce | 5:19.03.2~3-0~ubuntu-bionic | http://mirrors.aliyun.com/docker-ce/linux/ubuntu bionic/stable amd64 Packages docker-ce | 5:19.03.1~3-0~ubuntu-bionic | http://mirrors.aliyun.com/docker-ce/linux/ubuntu bionic/stable amd64 Packages
6. 安装指定版本的Docker-CE:(VERSION例如上面的17.03.1〜ce-0〜ubuntu-xenial)
sudo apt install docker-ce-cli=5:18.09.9~3-0~ubuntu-bionic sudo apt install docker-ce=5:18.09.9~3-0~ubuntu-bionic
7. 验证版本
- 验证docker信息 (以下是ubuntu安装的信息)
# docker info
Containers: 0
Running: 0
Paused: 0
Stopped: 0
Images: 0
Server Version: 18.09.9
Storage Driver: overlay2
Backing Filesystem: extfs
Supports d_type: true
Native Overlay Diff: true
Logging Driver: json-file
Cgroup Driver: cgroupfs
Plugins:
Volume: local
Network: bridge host macvlan null overlay
Log: awslogs fluentd gcplogs gelf journald json-file local logentries splunk syslog
Swarm: inactive
Runtimes: runc
Default Runtime: runc
Init Binary: docker-init
containerd version: 894b81a4b802e4eb2a91d1ce216b8817763c29fb
runc version: 425e105d5a03fabd737a126ad93d62a9eeede87f
init version: fec3683
Security Options:
apparmor
seccomp
Profile: default
Kernel Version: 4.15.0-62-generic
Operating System: Ubuntu 18.04.3 LTS
OSType: linux
Architecture: x86_64
CPUs: 2
Total Memory: 1.861GiB
Name: ubuntu
ID: 2C5Z:IASC:G3GK:325R:RDGI:GDJ4:23EE:O76B:Z2RY:HY5U:RSLC:J7OO
Docker Root Dir: /var/lib/docker
Debug Mode (client): false
Debug Mode (server): false
Registry: https://index.docker.io/v1/
Labels:
Experimental: false
Insecure Registries:
127.0.0.0/8
Live Restore Enabled: false
Product License: Community Engine
WARNING: No swap limit support
解决方法:
sudo vim /etc/default/grub 在GRUB_CMDLINE_LINUX=""这行加上 GRUB_CMDLINE_LINUX="cgroup_enable=memory swapaccount=1"
更新下grub,然后重启
sudo update-grub reboot
Docker命令
- 查看信息
docker info
- 查看版本
docker version
- 查看当前容器状态
docker ps
4. 搜索nginx的镜像(第一个是默认的,也是下载最多的)
docker search nginx
5. 下载nginx的镜像
docker pull nginx
6. 查看已经下载的镜像列表,TAG版本号默认都是latest
docker images
7、进入容器后,安装基础命令:
apt update
apt install procps
apt install iputils-ping
apt install net-tools
启动容器
# 启动一个在后台运行的 docker 容器
docker run -it -d --name 'test-nginx' nginx
# -p指定端口映射,
-p 80:80
# 指定 ip 地址和传输协议 udp 或者 tcp:
-p 192.168.7.108:80:80/tcp
# 也可以在创建时手动指定容器的 dns
--dns 223.6.6.6
# 指定名称
--name "centos3"
vi docker-enter.sh chmod +x docker-enter.sh
进入容器
- 使用执行命令方式进入容器
docker exec -it b7a13ace208d bash
- 使用使用容器pid方式进入容器
docker inspect -f "{{.State.Pid}}" 02a1907e7c89 19080 nsenter -t 19080 -m -u -i -n -p
3. 脚本方式进入容器
#!/bin/bash docker_in(){ NAME_ID=$1 PID=$(docker inspect -f "{{.State.Pid}}" ${NAME_ID}) nsenter -t ${PID} -m -u -i -n -p } docker_in $1
启动
./docker-enter.sh centos-test
更多命令
以名义名:nginx为例子
- 删除docker的nginx镜像文件
docker rmi nginx
2. 手动导出docker镜像
docker save nginx -o /root/nginx.tar.gz docker save nginx > /root/nginx.tar.gz
- 手动导入docker仅限
docker save nginx -o /root/nginx.tar.gz docker save nginx > /root/nginx.tar.gz
- 停止和启动一个容器
# d5ab2595f09a是CONTAINER ID
docker stop d5ab2595f09a
docker start d5ab2595f09a
- 删除一个已经停止的容器
docker rm d5ab2595f09a
- 强制关闭一个运行中的容器
docker kill d1ad4fa0b74c
docker加快加速配置:
国内下载国外的某些有时候会很慢,因此可以更改docker配置文件添加一个加速器,可以通过加速器达到加速下载替代的目的。
获取加速地址:
浏览器打开http://cr.console.aliyun.com,编码或登录阿里云账号,单击垂直的嵌套加速器,将会得到一个专属的加速地址,而且下面有使用配置说明:
1. 可以通过修改daemon配置文件/etc/docker/daemon.json来使用加速器
sudo mkdir -p /etc/docker sudo tee /etc/docker/daemon.json <<-'EOF' { "registry-mirrors": ["你的加速地址"] } EOF
2. 重启服务
sudo systemctl daemon-reload sudo systemctl restart docker
使用docker info 查看
Docker命令与镜像管理:
Docker 镜像含有启动容器所需要的文件系统及所需要的内容, 因此镜像主要用于创建并启动 docker 容器。
Docker 镜像含里面是一层层文件系统,叫做 Union FS(联合文件系统) ,联合文件系统,可以将几层目录挂载到一起,形成一个虚拟文件系统,虚拟文件系统的目录结构就像普通 linux 的目录结构一样, docker 通过这些文件再加上宿主机的内核提供了一个 linux 的虚拟环境,每一层文件系统我们叫做一层 layer,联合文件系统可以对每一层文件系统设置三种权限,只读( readonly)、读写( readwrite)和写出( whiteout-able),但是 docker 镜像中每一层文件系统都是只读的,构建镜像的时候,从一个最基本的操作系统开始,每个构建的操作都相当于做一层的修改,增加了一层文件系统,一层层往上叠加,上层的修改会覆盖底层该位置的可见性,这也很容易理解,就像上层把底层遮住了一样,当使用镜像的时候,我们只会看到一个完全的整体,不知道里面有几层也不需要知道里面有几层,结构如下:
一个典型的 Linux 文件系统由 bootfs 和 rootfs 两部分组成, bootfs(boot filesystem) 主要包含 bootloader 和 kernel, bootloader 主要用于引导加载 kernel,
当 kernel 被加载到内存中后 bootfs 会被 umount 掉, rootfs (root file system) 包含的就是典型 Linux 系统中的/dev, /proc, /bin, /etc 等标准目录和文件, 下图就是 docker image 中最基础的两层结构,不同的 linux 发行版(如 ubuntu和 CentOS ) 在 rootfs 这一层会有所区别。但是对于 docker 镜像通常都比较小, 官方提供的 centos 基础镜像在 200MB 左右,一些其他版本的镜像甚至只有几 MB, docker 镜像直接调用宿主机的内核,镜像中只提供 rootfs,也就是只需要包括最基本的命令、工具和程序库就可以了, 比如 alpine 镜像,在 5M 左右。
下图就是有两个不同的镜像在一个宿主机内核上实现不同的 rootfs。
容器、 镜像父镜像:
docker常用命令示例
- 搜索官方镜像
# 带指定版本号 docker search centos:7.2.1511 # 不带版本号默认 latest docker search centos
- 下载镜像
docker pull 仓库服务器:端口/项目名称/镜像名称:版本号
- 查看本地镜像
docker images
REPOSITORY #镜像所属的仓库名称
TAG #镜像版本号(标识符), 默认为 latest
IMAGE ID #镜像唯一 ID 标示
CREATED #镜像创建时间
VIRTUAL SIZE #镜像的大小
- 镜像导出:可以将镜像从本地导出问为一个压缩文件,然后复制到其他服务器进行导入使用
# 方法1 docker save centos -o /opt/centos.tar.gz # 方法2 docker save centos > /opt/centos-1.tar.gz
- 查看镜像内容:包含了镜像的相关配置, 配置文件、分层
tar xvf centos.tar.gz cat manifest.json
- 镜像导入:将镜像导入到 docker
docker load < /opt/centos.tar.gz
删除镜像
docker rmi centos
- 删除容器(-f 强制)
docker rm 容器 ID/容器名称
docker命令:
- 命令格式
docker [OPTIONS] COMMAND
- 常用的
COMMAND
[COMMAND]
attach 此方式进入容器的操作都是同步显示的且 exit 后容器将被关闭
build 从Dockerfile构建一个镜像
commit 从容器的更改中创建一个新镜像
cp 在容器和本地文件系统之间复制文件/文件夹
create 创建新容器
diff 检查容器文件系统上文件或目录的更改
events 从服务器获取实时事件
exec 在运行的容器中运行命令
export 将容器的文件系统导出为tar包
history 显示镜像的历史
images 列出镜像
import 从tarball导入内容以创建文件系统镜像
info 显示整个系统的信息
inspect 返回Docker对象的底层信息
kill 停止一个或多个正在运行的容器
load 从tar包或标准输出加载镜像
login Log in to a Docker registry
logout Log out from a Docker registry
logs 获取容器的日志
pause 暂停一个或多个容器中的所有进程
port 列出容器的端口映射或特定映射
ps 列出正在运行的容器
pull 下载镜像
push 上传镜像
rename 重命令一个容器
restart 重启容器
rm 移除一个或多个容器
rmi 移除一个或多个镜像
run 在新容器中运行命令
save 将一个或多个图像保存到tar存档文件(默认情况下流到STDOUT)
search 在Docker仓库中搜索镜像
start 启动一个或多个处在停止状态的容器
stats 显示容器资源使用统计数据的实时流
stop 停止一个或多个正在运行的容器
tag Create a tag TARGET_IMAGE that refers to SOURCE_IMAGE
top 显示容器的运行进程
unpause Unpause all processes within one or more containers
update Update configuration of one or more containers
version 查看版本
wait 阻塞直到一个或多个容器停止,然后打印它们的退出代码
子命令帮助
docker COMMAND --help
退出容器不注销:Ctrl + P + Q
run
在新容器中运行命令
- 命令格式
docker run [选项] 镜像名 [shell 命令] [参数...]
- 常用选项
-p list 指定容器的端口发布到主机
-P 将所有公开的端口发布到随机端口
--name string 为容器分配一个名称
-d 在后台运行容器并打印容器ID
-it 创建并进入容器
--rm 当容器退出时自动删除它
例如:
- 启动的容器在执行完 shell 命令就退出了
#docker run [镜像名] [shell 命令] docker run centos /bin/echo 'hello wold'
- 从镜像启动一个容器:会直接进入到容器, 并随机生成容器 ID 和名称
docker run -it docker.io/centos bash
- 随机映射端口:
前台启动并随机映射本地端口到容器的 80
docker run -P docker.io/nginx
前台启动的会话窗口无法进行其他操作,除非退出, 但是退出后容器也会退出随机端口映射, 其实是默认从 32768 开始
- 指定端口映射:
方式 1:本地端口 81 映射到容器 80 端口:
docker run -p 81:80 --name "nginx-test" nginx
方式 2:本地 IP:本地端口:容器端口
docker run -p 192.168.10.205:82:80 --name "nginx-test" docker.io/nginx
方式 3:本地 IP:本地随机端口:容器端口
docker run -p 192.168.10.205::80 --name "nginx-test" docker.io/nginx
方式 4:本机 ip:本地端口:容器端口/协议,默认为 tcp 协议
docker run -p 192.168.10.205:83:80/udp --name "nginx-test" docker.io/nginx
方式 5:一次性映射多个端口+协议:
docker run -p 86:80/tcp -p 443:443/tcp -p 53:53/udp --name "nginx-test" docker.io/nginx
5. 后台启动容器
docker run -d -P --name "nginx-test" docker.io/nginx
- 容器退出后自动删除
docker run -it --rm --name nginx-test docker.io/nginx
- 指定容器 DNS:
Dns 服务,默认采用宿主机的 dns 地址
一是将 dns 地址配置在宿主机
二是将参数配置在 docker 启动脚本里面 –dns=1.1.1.1
docker run -it --rm --dns 223.6.6.6 centos bash
--rm 容器退出后会删除容器
ps
列出正在运行的容器
- 命令格式
docker ps [OPTIONS]
- 常用选项
-a 显示所有容器(默认显示正在运行)
-f 根据提供的条件过滤输出
-n int 显示最后创建的n个容器(包括所有状态)(默认值-1)
-l 显示最新创建的容器(包括所有状态)
--no-trunc 不截断输出
-q 只显示数字id
-s 显示总文件大小
例:
- 显示正在运行的容器:
docker ps
- 显示所有容器
docker ps -a
rm
移除一个或多个容器
- 命令格式
docker rm [OPTIONS] CONTAINER [CONTAINER...]
- 常用选项
- 删除运行中的容器:即使容正在运行当中, 也会被强制删除掉
docker rm -f 11445b3a84d3 # 11445b3a84d3是CONTAINER ID,通过docker ps 查询
- 批量删除已退出容器
docker rm -f `docker ps -aq -f status=exited`
- 批量删除所有容器
docker rm -f $(docker ps -a -q)
logs
获取容器的日志
- 命令格式
docker logs [OPTIONS] CONTAINER
- 常用选项
--details 显示提供给日志的额外细节
-f 跟踪日志输出
--since string 显示从时间戳开始的日志(如:42m表示42 分钟)
--tail string 从日志末尾显示的行数(默认为“all”)
-t 显示时间戳
--until string 在时间戳前显示日志(如:42m表示42 分钟)
例:
- 查看 Nginx 容器访问日志
docker logs nginx-test-port3 #一次查看
docker logs -f nginx-test-port3 #持续查看
load
从tar存档或STDIN加载镜像
- 命令格式
docker load [OPTIONS]
- 常用选项
-i string 从tar存档文件中读取,而不是从STDIN中读取
-q 不输出
例:
- 导入镜像
docker load -i nginx.tar.gz
port
列出容器的端口映射或特定映射
- 命令格式
docker port CONTAINER [PRIVATE_PORT[/PROTO]]
- 例:
- 查看容器已经映射的端口
docker port nginx-test
stop
停止一个或多个正在运行的容器
- 命令格式
docker stop [OPTIONS] CONTAINER [CONTAINER...]
- 常用选项
-t int 等待stop几秒钟后再杀死它(默认为10)
例:
- 容器的关闭
docker stop f821d0cd5a99
- 批量关闭正在运行的容器
docker stop $(docker ps -a -q)
kill
杀死一个或多个正在运行的容器
- 命令格式
docker kill [OPTIONS] CONTAINER [CONTAINER...]
- 常用选项
-s string 发送到容器的信号(默认为“KILL”)
例:
- 批量强制关闭正在运行的容器
docker kill $(docker ps -a -q)
start
启动一个或多个停止的容器
- 命令格式
docker start [OPTIONS] CONTAINER [CONTAINER...]
- 常用选项
-a 附加STDOUT/STDERR和转发信号
--detach-keys string 覆盖用于分离容器的键序列
-i 将容器的STDIN
例:
- 容器的启动
docker start f821d0cd5a99
attach
此方式进入容器的操作都是同步显示的且 exit 后容器将被关闭
- 命令格式
docker attach [OPTIONS] CONTAINER
- 常用选项
--detach-keys string 覆盖用于分离容器的键序列
--no-stdin 不附加STDIN
--sig-proxy 代理所有接收到的进程信号(默认为true)
例:
docker attach 63fbc2d5a3ec
exec
执行单次命令与进入容器,不是很推荐此方式, 虽然 exit 退出容器还在运行
- 命令格式
docker exec [OPTIONS] CONTAINER COMMAND [ARG...]
- 常用选项
-d 分离模式:在后台运行命令
--detach-keys string 覆盖用于分离容器的键序列
-e list 设置环境变量
-i 保持STDIN打开,即使没有连接
--privileged 为该命令授予扩展特权
-t 分配一个pseudo-TTY
-u string 用户名或UID(格式:< name| UID >[:<group|gid>])
-w string 容器内的工作目录
例:
- 进入容器
docker exec -it centos-test /bin/bash
inspect
返回Docker对象的底层信息
- 命令格式
docker inspect [OPTIONS] NAME|ID ...
- 常用选项
-f string 使用给定的Go模板格式化输出
例:
- 可以获取到容器的 PID
# docker inspect -f "{{.State.Pid}}" centos-test3 5892
commit
从容器的更改中创建一个新镜像
- 命令格式
docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]
- 常用选项
-a string 作者
-c list 对创建的映像应用Dockerfile指令
-m string 提交消息
-p 提交期间暂停容器(默认为true)
例:
- 在宿主机基于容器 ID 提交为镜像
docker commit -a "chen" -m "nginx_yum_v1" --change="EXPOSE_80_443" f5f8c13d0f9f centos-nginx:v1
- 提交的时候标记 tag 号:
> 标记 tag 号,生产当中比较常用,后期可以根据 tag 标记创建不同版本的镜像以及创建不同版本的容器。
docker commit -m "nginx image" f5f8c13d0f9f jack/centos-nginx:v1
Docker 镜像与制作
Docker 镜像有没有内核?
从镜像大小上面来说,一个比较小的镜像只有十几 MB,而内核文件需要一百多兆, 因此镜像里面是没有内核的, 镜像在被启动为容器后将直接使用宿主机的内核, 而镜像本身则只提供相应的 rootfs, 即系统正常运行所必须的用户空间的文件系统,比如/dev/, /proc, /bin, /etc 等目录, 所以容器当中基本是没有/boot目录的,而/boot 当中保存的就是与内核相关的文件和目录。
为什么没有内核?
由于容器启动和运行过程中是直接使用了宿主机的内核,所以没有直接调用过物理硬件, 所以也不会涉及到硬件驱动, 因此也用不上内核和驱动,另外有内核的那是虚拟机。
手动制作nginx镜像
Docker 制作类似于虚拟机的镜像制作,即按照公司的实际业务务求将需要安装的软件、相关配置等基础环境配置完成,然后将其做成镜像,最后再批量从镜像批量生产实例,这样可以极大的简化相同环境的部署工作, Docker 的镜像制作分为手动制作和自动制作(基于 DockerFile), 企业通常都是基于 Dockerfile 制作精细, 其中手动制作镜像步骤具体如下:
下载镜像并初始化系统:
基于某个基础镜像之上重新制作, 因此需要先有一个基础镜像,本次使用官方提供的 centos 镜像为基础:
1. docker下载centos镜像,并运行进入
docker pull centos
docker run -it docker.io/centos /bin/bash
- 进入容器
- 进入容器后,更改yun源
yum install wget -y
cd /etc/yum.repos.d/
rm -rf ./*
wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo
wget -O /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo
- yum安装nginx与常用工具
yum install –y nginx wget pcre pcre-devel zlib zlib-devel openssl openssl-devel iproute net-tools iotop
- 修改nginx配置,关闭后台运行
vim /etc/nginx/nginx.conf user nginx; worker_processes auto; error_log /var/log/nginx/error.log; pid /run/nginx.pid; daemon off;
4. 自定义 web 页面
echo "Docker Yum Nginx" > /usr/share/nginx/html/index.html
- 退出容器
5. 提交为镜像:在宿主机基于容器 ID 提交为镜像
docker commit -a "xu" -m "nginx_yum_v1" --change="EXPOSE 80 4433" 75f52cfb13d2 centos-nginx:v1
带 tag 的镜像提交:
提交的时候标记 tag 号:
标记 tag 号,生产当中比较常用,后期可以根据 tag 标记创建不同版本的镜像以及创建不同版本的容器。
docker commit -m "nginx image" 75f52cfb13d2 centos-nginx:v3
6. 查看创建的镜像
docker image ls
7. 从自己镜像启动容器,因为本机用的是云主机,没有备案和域名,所以使用4433端口映射
docker run -d -p 4433:80 --name my-centos-nginx 87e99ab0c9ab /usr/sbin/nginx # 87e99ab0c9ab 是IMAGE ID
8. 访问测试
容器端口映射成功
Dockerfile介绍
DockerFile 可以说是一种可以被 Docker 程序解释的脚本, DockerFile 是由一条条的命令组成的,每条命令对应 linux 下面的一条命令, Docker 程序将这些DockerFile 指令再翻译成真正的 linux 命令,其有自己的书写方式和支持的命令, Docker 程序读取 DockerFile 并根据指令生成 Docker 镜像,相比手动制作镜像的方式, DockerFile 更能直观的展示镜像是怎么产生的,有了 DockerFile,当后期有额外的需求时,只要在之前的 DockerFile 添加或者修改响应的命令即可重新生成新的 Docke 镜像,避免了重复手动制作镜像的麻烦。
FROM 指定基础镜像
定制镜像,需要先有一个基础镜像,在这个基础镜像上进行定制。FROM 就是指定基础镜像,必需放在有效指令的第一行
怎么选择合适的镜像呢?官方有nginx、redis、mysql、httpd、tomcat等服务类的镜像,也有操作系统类,如:centos、ubuntu、debian等。
例:
FROM centos:latest
MAINTAINER 指定维护者信息
格式
MAINTAINER <name>
RUN 执行命令
RUN 指令是用来执行命令的。shell命令功能丰富,所以RUN 指令经常用来调用shell指令。
格式
RUN <command> # 或 RUN ["executable", "param1", "param2"]
第一种不用多说,第二种方式:
RUN ["/bin/bash", "-c", "echo hello world"]
Dockerfile中,每一个指令都会创建一层镜像,RUN写多了,创建的镜像层数就会增多,所以一般RUN 指令的写法为
RUN yum -y install epel-release && yum -y install nginx && rm -rf /usr/share/nginx/html/* && echo "<h1> docker test nginx </h1>" > /usr/share/nginx/html/index.html
CMD 启动容器时执行的命令
指定启动容器时执行的命令,每个 Dockerfile 只能有一条 CMD 命令。如果指定了多条命令,只有最后一条会被执行。
支持三种格式
# 使用 exec 执行,推荐方式; CMD ["executable","param1","param2"] # 在 /bin/sh 中执行,提供给需要交互的应用; CMD command param1 param2 # 提供给 ENTRYPOINT 的默认参数; CMD ["param1","param2"]
如果用户启动容器时候指定了运行的命令,则会覆盖掉 CMD 指定的命令。
EXPOSE 分配端口号
告诉 Docker 服务端容器暴露的端口号,供互联系统使用。在启动容器时需要通过 -P,Docker 主机会自动分配一个端口转发到指定的端口
格式
EXPOSE <port> [<port>...]
ENV 环境变量
指定一个环境变量,会被后续 RUN 指令使用,并在容器运行时保持。
格式
ENV <key> <value>
例如
ENV PG_MAJOR 9.3 ENV PG_VERSION 9.3.4 RUN curl -SL http://example.com/postgres-$PG_VERSION.tar.xz | tar -xJC /usr/src/postgress && … ENV PATH /usr/local/postgres-$PG_MAJOR/bin:$PATH
ADD 复制/解压
该命令将复制指定的
<src>
到容器中的<dest>
。 其中<src>
可以是Dockerfile所在目录的一个相对路径;也可以是一个 URL;还可以是一个 tar 文件(自动解压为目录)
格式
ADD <src> <dest>
COPY 复制
复制本地主机的
<src>
(为 Dockerfile 所在目录的相对路径)到容器中的<dest>
格式
COPY <src> <dest>
当使用本地目录为源目录时,推荐使用 COPY
ENTRYPOINT 容器启动后执行的命令
配置容器启动后执行的命令,并且不可被 docker run 提供的参数覆盖。
两种格式:
ENTRYPOINT ["executable", "param1", "param2"]
# shell中执行
ENTRYPOINT command param1 param2
每个 Dockerfile 中只能有一个 ENTRYPOINT,当指定多个时,只有最后一个起效
VOLUME 挂载点
创建一个可以从本地主机或其他容器挂载的挂载点,一般用来存放数据库和需要保持的数据等
格式
VOLUME ["/data"]。
USER 指定用户名
指定运行容器时的用户名或 UID,后续的 RUN 也会使用指定用户
格式
USER daemon
当服务不需要管理员权限时,可以通过该命令指定运行用户。并且可以在之前创建所需要的用户,
例如:
RUN groupadd -r postgres && useradd -r -g postgres postgres
要临时获取管理员权限可以使用 gosu,而不推荐 sudo。
WORKDIR 工作目录
为后续的 RUN、CMD、ENTRYPOINT 指令配置工作目录
格式
WORKDIR /path/to/workdir。
可以使用多个 WORKDIR 指令,后续命令如果参数是相对路径,则会基于之前命令指定的路径。例如
WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd
则最终路径为 /a/b/c。
ONBUILD
配置当所创建的镜像作为其它新创建镜像的基础镜像时,所执行的操作指令。
格式
ONBUILD [INSTRUCTION]
例如,Dockerfile 使用如下的内容创建了镜像 image-A。
...
ONBUILD ADD . /app/src
ONBUILD RUN /usr/local/bin/python-build --dir /app/src
...
如果基于 image-A 创建新的镜像时,新的Dockerfile中使用 FROM image-A指定基础镜像时,会自动执行ONBUILD 指令内容,等价于在后面添加了两条指令。
FROM image-A
#Automatically run the following
ADD . /app/src
RUN /usr/local/bin/python-build --dir /app/src
使用 ONBUILD
指令的镜像,推荐在标签中注明,例如 ruby:1.9-onbuild
构建镜像
格式
docker build [选项] 路径
-t
选项,指定镜像的标签信息
docker build -t nginx:v1 /usr/local/src/
构建时,目录内除了构建所需要的文件,不要有其它文件
DockerFile 制作yum版 nginx 镜像:
- 准备目录
mkdir -pv /opt/dockerfile/web/nginx
- 编写dockerfile文件
vim /opt/dockerfile/web/nginx/Dockerfile
#Nginx web image
FROM centos:latest
RUN yum install epel-release -y
RUN yum install nginx -y && rm -rf /usr/share/nginx/html/*
ADD html.tar.gz /usr/share/nginx/html/
#copy
EXPOSE 80 443 8080
CMD ["/usr/sbin/nginx","-g","daemon off;"]
RUN就是运行shell命令
ADD
- 创建网页测试文件
vi /opt/dockerfile/web/nginx/index.html
<h1> docker nginx test, nginx container by dockerfile</h1>
- 打包网页测试文件
tar czvf html.tar.gz index.html
- 构建镜像,也可以把此命令写到脚本里方便以后运行
docker build -t chenjb/centos-nginx /opt/dockerfile/web/nginx/
构建
6. 查看镜像
docker images
- 启动容器
docker run -it -d -p 80:80 --name "nginx-test" chenjb/centos-nginx
- 查看进程
docker ps
- 测试网页,本机用的是云主机还得配置安全组有点麻烦,这里就直接使用命令行进行测试了
# curl 127.0.0.1:8355
<h1> docker nginx test, nginx container by dockerfile</h1>
DockerFile 制作编译版 nginx 镜像:
下载镜像并初始化系统:
docker pull centos mkdir -pv /opt/dockerfile/web/nginx
目录结构按照业务类型或系统类型等方式划分,方便后期镜像比较多的时候进行分类
编写 Dockerfile:
vim /opt/dockerfile/web/nginx/Dockerfile
生成的镜像的时候会在执行命令的当前目录查找 Dockerfile 文件, 所以名称不可写错, 而且 D 必须大写
My Dockerfile
"#"为注释,等于 shell 脚本的中#
除了注释行之外的第一行,必须是 From xxx (xxx 是基础镜像)
# 第一行先定义基础镜像,后面的本地有效的镜像名,如果本地没有会从远程仓库下载,第一行很重要
From centos:latest
# 镜像维护者的信息:
MAINTAINER han 583343636@qq.com
#自动解压压缩包
ADD nginx-1.16.1.tar.gz /usr/local/src/
#执行的命令,将编译安装 nginx 的步骤执行一遍
RUN rpm -ivh http://mirrors.aliyun.com/epel/epel-release-latest-7.noarch.rpm
RUN yum install -y vim wget tree lrzsz gcc gcc-c++ automake pcre pcre-devel zlib zlib-devel openssl openssl-devel iproute net-tools iotop
RUN cd /usr/local/src/nginx-1.16.1 && ./configure --prefix=/usr/local/nginx --with-http_sub_module && make && make install
RUN cd /usr/local/nginx/
ADD nginx.conf /usr/local/nginx/conf/nginx.conf
RUN useradd nginx -s /sbin/nologin
RUN ln -sv /usr/local/nginx/sbin/nginx /usr/sbin/nginx
RUN echo "Nginx-make version 1.16.1 test page" > /usr/local/nginx/html/index.html
# 向外开放的端口,多个端口用空格做间隔,启动容器时候-p 需要使用此端向外映射,如: -p 8081:80,则 80 就是这里的 80
EXPOSE 80 443
# 运行的命令,每个 Dockerfile 只能有一条,如果有多条则只有最后一条被执行
CMD ["nginx","-g","daemon off;"]
#如果在从该镜像启动容器的时候也指定了命令,那么指定的命令会覆盖Dockerfile 构建的镜像里面的 CMD 命令,即指定的命令优先级更高, Dockerfile 的优先级较低一些
准备源码包与配置文件:
- 配置nginx文件,并关闭后台运行添加配置 daemon off
cp /usr/local/nginx/conf/nginx.conf /opt/dockerfile/web/nginx
- nginx 源码包
cp /usr/local/src/nginx-1.16.1.tar.gz /opt/dockerfile/web/nginx
- 执行镜像构建
docker build –t jack/nginx-1.16.1:v1 /opt/dockerfile/web/nginx/
4. 开始构建:可以清晰看到各个步骤执行的具体操作
构建成功了
5. 查看是否生成本地镜像,如果构建时出错可能会导致生成名字为none的镜像,使用命令删除即可
docker images
docker rmi -f $(docker images | grep "none" | awk '{print $3}') #过滤出仓库名和tag名为none的镜像ID并强制删除
6. 从镜像启动容器
docker run -d -p 80:80 --name mak1e-nginx peterhx/nginx-make-1.16.1:v1 /usr/sbin/nginx
- 访问 web 界面
制作 tomcat 镜像:
基于官方提供的 centos 7.6. 基础镜像构建 JDK 和 tomcat 镜像,先构建 JDK 镜像,然后再基于 JDK 镜像构建 tomcat 镜像。
1、先构建 JDK 镜像
- 下载基础镜像 Centos:
docker pull centos
- 执行构建 JDK 镜像:
mkdir -pv /opt/dockerfile/{web/{nginx,tomcat,jdk,apache},system/{centos,ubuntu,redhat}}
cd /opt/dockerfile/web/jdk/
- 编辑Dockerfile
# JDK Base Images
FROM centos:latest
ADD jdk-8u212-linux-x64.tar.gz /usr/local/src/
RUN ln -sv /usr/local/src/jdk1.8.0_212 /usr/local/jdk
ADD profile /etc/profile
ENV JAVA_HOME /usr/local/jdk
ENV JRE_HOME $JAVA_HOME/jre
ENV CLASSPATH $JAVA_HOME/lib/:$JRE_HOME/lib/
ENV PATH $PATH:$JAVA_HOME/bin
RUN rm -rf /etc/localtime
&& ln -snf /usr/share/zoneinfo/Asia/Shanghai/etc/localtime
&& echo "Asia/Shanghai" > /etc/timezone
- 上传 JDK 压缩包和 profile 文件:
将 JDK 压缩包上传到 Dockerfile 当前目录,然后执行构建:
profile文件内容,(最下边声明的环境变量,其他的一样)
# cat profile
###########################
# System wide environment and startup programs, for login setup # Functions and aliases go in /etc/bashrc # It's NOT a good idea to change this file unless you know what you # are doing. It's much better to create a custom.sh shell script in # /etc/profile.d/ to make custom changes to your environment, as this # will prevent the need for merging in future updates. pathmunge () { case ":${PATH}:" in *:"$1":*) ;; *) if [ "$2" = "after" ] ; then PATH=$PATH:$1 else PATH=$1:$PATH fi esac } if [ -x /usr/bin/id ]; then if [ -z "$EUID" ]; then # ksh workaround EUID=`/usr/bin/id -u` UID=`/usr/bin/id -ru` fi USER="`/usr/bin/id -un`" LOGNAME=$USER MAIL="/var/spool/mail/$USER" fi # Path manipulation if [ "$EUID" = "0" ]; then pathmunge /usr/sbin pathmunge /usr/local/sbin else pathmunge /usr/local/sbin after pathmunge /usr/sbin after fi HOSTNAME=`/usr/bin/hostname 2>/dev/null` HISTSIZE=1000 if [ "$HISTCONTROL" = "ignorespace" ] ; then export HISTCONTROL=ignoreboth else export HISTCONTROL=ignoredups fi export PATH USER LOGNAME MAIL HOSTNAME HISTSIZE HISTCONTROL # By default, we want umask to get set. This sets it for login shell # Current threshold for system reserved uid/gids is 200 # You could check uidgid reservation validity in # /usr/share/doc/setup-*/uidgid file if [ $UID -gt 199 ] && [ "`/usr/bin/id -gn`" = "`/usr/bin/id -un`" ]; then umask 002 else umask 022 fi for i in /etc/profile.d/*.sh /etc/profile.d/sh.local ; do if [ -r "$i" ]; then if [ "${-#*i}" != "$-" ]; then . "$i" else . "$i" >/dev/null fi fi done unset i unset -f pathmunge export JAVA_HOME=/usr/local/jdk export TOMCAT_HOME=/apps/tomcat export PATH=$JAVA_HOME/bin:$JAVA_HOME/jre/bin:$TOMCAT_HOME/bin:$PATH export CLASSPATH=.$CLASSPATH:$JAVA_HOME/lib:$JAVA_HOME/jre/lib:$JAVA_HOME/lib/tools.jar
执行构建
cat build-command.sh
#!/bin/bash
docker build -t centos-jdk1.8:v1 .
5. 启动容器
docker run -it centos-jdk1.8:v1 bash
进入容器后,要能查看到Java的环境(如果查不出来等于白做,检查是哪个环节出错)
2、从 JDK 镜像构建 tomcat-8 镜像
- 进入tomcat目录:
cd /opt/dockerfile/web/tomcat
- 编辑Dockerfile文件
vim Dockerfile
FROM centos-jdk:v1
RUN useradd www -u 2020
ENV TZ "Asia/Shanghai"
ENV LANG en_US.UTF-8
ENV TERM xterm
ENV TOMCAT_MAJOR_VERSION 8
ENV TOMCAT_MINOR_VERSION 8.0.49
ENV CATALINA_HOME /apps/tomcat
ENV APP_DIR ${CATALINA_HOME}/webapps
RUN mkdir /apps
ADD apache-tomcat-8.5.42.tar.gz /apps
RUN ln -sv /apps/apache-tomcat-8.5.42 /apps/tomcat
-
上传 tomcat 压缩包:
apache-tomcat-8.5.42.tar.gz
-
通过脚本构建 tomcat 基础镜像
# cat build-command.sh #!/bin/bash docker build -t tomcat85:v1 .
- 验证镜像构建完成
docker images
- 构建业务镜像 app1:
mkdir -p /opt/dockerfile/app/tomcat-app1
cd /opt/dockerfile/app/tomcat-app1/
- 准备 Dockerfile:
vim Dockerfile
FROM tomcat85:v1
ADD run_tomcat.sh /apps/tomcat/bin/run_tomcat.sh
ADD myapp/* /apps/tomcat/webapps/myapp/
RUN chown www.www /apps/ -R
RUN chmod +x /apps/tomcat/bin/run_tomcat.sh
EXPOSE 8080 8009
CMD ["/apps/tomcat/bin/run_tomcat.sh"]
- 准备自定义 myapp 页面:
mkdir myapp echo "Docker Tomcat Web Page1" > myapp/index.html
- 准备容器启动执行脚本:
vim run_tomcat.sh
#!/bin/bash
echo "10.10.10.10 tom.test.com" >> /etc/hosts
echo "nameserver 223.5.5.5" > /etc/resolv.conf
su - www -c "/apps/tomcat/bin/catalina.sh start"
su - www -c "tail -f /etc/hosts"
- 运行命令或执行脚本构建Tomcat-app1镜像
cat build-command.sh #!/bin/bash docker build -t tomcat85-app1:v1 .
- 查看镜像
docker images
- 从镜像启动容器测试:
docker run -d -p 8888:8080 tomcat85-app1:v1
访问测试
制作 haproxy 镜像:
- 进入目录
mkdir -pv /opt/dockerfile/app/haproxy cd /opt/dockerfile/app/haproxy
- 准备 Dockerfile:
vim Dockerfile #Haproxy Base Image FROM centos ADD haproxy-2.0.5.tar.gz /usr/local/src/ RUN yum install gcc gcc-c++ glibc glibc-devel pcre pcre-devel openssl openssl-devel systemd-devel net-tools vim iotop bc zip unzip zlib-devel lrzsz tree screen lsof tcpdump wget ntpdate –y && cd /usr/local/src/haproxy-2.0.5 && make ARCH=x86_64 TARGET=linux-glibc USE_PCRE=1 USE_OPENSSL=1 USE_ZLIB=1 USE_SYSTEMD=1 USE_CPU_AFFINITY=1 PREFIX=/usr/local/haproxy && make install PREFIX=/usr/local/haproxy && cp haproxy /usr/sbin/ && mkdir /usr/local/haproxy/run ADD haproxy.cfg /etc/haproxy/ ADD run_haproxy.sh /usr/bin RUN chmod +x /usr/bin/run_haproxy.sh EXPOSE 80 9999 CMD ["/usr/bin/run_haproxy.sh"]
-
准备 haproxy 源码文件:
haproxy-2.0.5.tar.gz
-
准备
run_haproxy.sh
脚本
vim run_haproxy.sh
#!/bin/bash
haproxy -f /etc/haproxy/haproxy.cfg
tail -f /etc/hosts
- 准备 haproxy 配置文件:
vim haproxy.cfg
global
chroot /usr/local/haproxy
#stats socket /var/lib/haproxy/haproxy.sock mode 600 level admin
uid 99
gid 99
daemon
nbproc 1
pidfile /usr/local/haproxy/run/haproxy.pid
log 127.0.0.1 local3 info
defaults
option http-keep-alive
option forwardfor
mode http
timeout connect 300000ms
timeout client 300000ms
timeout server 300000ms
listen stats
mode http
bind 0.0.0.0:9999
stats enable
log global
stats uri /haproxy-status
stats auth haadmin:q1w2e3r4ys
listen web_port
bind 0.0.0.0:80
mode http
log global
balance roundrobin
server web1 192.168.99.21:8888 check inter 3000 fall 2 rise 5
server web2 192.168.99.22:8889 check inter 3000 fall 2 rise 5
6. 准备构建脚本:
docker build -t haproxy:v1 .
7. 从镜像启动容器:
docker run -it -d -p 80:80 -p 9999:9999 haproxy:v1
8. web 访问验证
9. 访问tomcat业务app1
本地镜像上传至官方 docker 仓库:
将自制的镜像上传至 docker 仓库; https://hub.docker.com/
- 准备账户:
登录到 docker hub 创建官网创建账户, 登录后点击 settings 完善账户信息,填写账户基本信息 -
在虚拟机使用自己的账号登录
docker login docker.io
- 查看认证信息:
#登录成功之后会在当前目录生成一个隐藏文件用于保存登录认证信息
[root@Server1 ~]# cat /root/.docker/config.json { "auths": { "https://index.docker.io/v1/": { "auth": "cGV0ZXJoeDo5NjExMjFoeC4=" }, "registry.cn-beijing.aliyuncs.com": { "auth": "NTgzMzQzNjM2QHFxLmNvbTo5NjExMjFoeA==" } }, "HttpHeaders": { "User-Agent": "Docker-Client/19.03.8 (linux)" } }
- 查看镜像 ID
docker images
- 根据规定的格式给镜像修改tag版本号 并上传到docker仓库中
docker tag a187dde48cd2 peterhx/alpine-test:alpine123
docker push peterhx/alpine-test:alpine123
-
上传完成
7. 到 docker 官网验证
8. 更换到其他 docker 服务器下载镜像
# 登录 docker login docker.io # 下载 docker pull peterhx/alpine-test:alpine123 # 查看 docker images
- 从镜像启动一个容器
docker run -it peterhx/alpine-test:alpine123 bash
docker 仓库之分布式 Harbor
Harbor 是一个用于存储和分发 Docker 镜像的企业级 Registry 服务器,由vmware 开源,其通过添加一些企业必需的功能特性,例如安全、标识和管理等,扩展了开源 Docker Distribution。作为一个企业级私有 Registry 服务器,Harbor 提供了更好的性能和安全。提升用户使用 Registry 构建和运行环境传输镜像的效率。 Harbor 支持安装在多个 Registry 节点的镜像资源复制,镜像全部保存在私有 Registry 中, 确保数据和知识产权在公司内部网络中管控, 另外,Harbor 也提供了高级的安全特性,诸如用户管理,访问控制和活动审计等。
官网地址: https://vmware.github.io/harbor/cn/,
官方 github 地址:https://github.com/vmware/harbor
Harbor 功能官方介绍:
基于角色的访问控制:用户与 Docker 镜像仓库通过“项目”进行组织管理,一个用户可以对多个镜像仓库在同一命名空间(project)里有不同的权限。镜像复制:镜像可以在多个 Registry 实例中复制(同步)。尤其适合于负载均衡,高可用,混合云和多云的场景。图形化用户界面:用户可以通过浏览器来浏览,检索当前 Docker 镜像仓库,管理项目和命名空间。
AD/LDAP: Harbor 可以集成企业内部已有的 AD/LDAP,用于鉴权认证管理。
审计管理:所有针对镜像仓库的操作都可以被记录追溯,用于审计管理。国际化:已拥有英文、中文、德文、日文和俄文的本地化版本。更多的语言将会添加进来。RESTful API - RESTful API :提供给管理员对于 Harbor 更多的操控, 使得与其它管理软件集成变得更容易。
部署简单:提供在线和离线两种安装工具, 也可以安装到 vSphere 平台(OVA 方式)虚拟设备。
安装 Harbor:
下载地址:
https://github.com/goharbor/harbor/releases安装文档:
https://github.com/vmware/harbor/blob/master/docs/installation_guide.md
本次使用 harbor 版本 1.2.2 离线安装包,具体名称为 harbor-offline-installer-v1.7.5.tgz
下载 Harbor 安装包:
方式1:下载离线安装包:https://github.com/goharbor/harbor/releases
cd /usr/local/src/ wget https://storage.googleapis.com/harbor-releases/release-1.7.0/harbor-offline-installer-v1.7.5.tgz
方式2:下载在线安装包
cd /usr/local/src/ wget https://storage.googleapis.com/harbor-releases/release-1.7.0/harbor-online-installer-v1.7.5.tgz
配置安装 Harbor:
在另一台云主机的机器(内网IP为:192.168.0.59)上安装harbor仓库
- 解压
tar xf harbor-offline-installer-v1.7.5.tgz
ln -sv /usr/local/src/harbor /usr/local/
- 下载docker-compose
cd /usr/local/harbor/
yum install python-pip epel-release -y
pip install docker-compose
安装docker-compose时报错,缺少依赖及python的开发环境,yum install -y gcc python-devel 即可
- 修改harbor的配置文件,最终配置如下,只修改前两个即可,其余的根据需要修改
# vim harbor.cfg
...
hostname = 192.168.0.59
...
harbor_admin_password = harbor123
...
其它配置说明
# 写本机ip,不要写localho
st或127.0.0.1
hostname = 192.168.0.59
# 访问UI和令牌/通知服务的协议
ui_url_protocol = http
# harbor DB的根用户的密码
db_password = root123
# 最大进程数
max_job_workers = 3
# 确定是否为注册表的令牌生成证书
customize_crt = on
# 证书路径
ssl_cert = /data/cert/server.crt
ssl_cert_key = /data/cert/server.key
# 密钥存储的路径
secretkey_path = /data
# Admiral的url,注释此属性,或在Harbor独立时将其值设置为NA
admiral_url = NA
# Clair的postgres数据库的密码
clair_db_password = root123
# 电子邮件服务器
email_identity = harbor
email_server = smtp.qq.com
email_server_port = 25
email_username = hanxu@foxmail.com
email_password = 123
email_from = admin <hanxu@foxmail.com>
email_ssl = false
email_insecure = false
# 启动后从UI更改管理密码。
harbor_admin_password = root123
# 如果希望根据LDAP服务器验证用户的凭证,请将其设置为ldap_auth。
auth_mode = db_auth
ldap_url = ldaps://ldap.mydomain.com
ldap_basedn = ou=people,dc=mydomain,dc=com
ldap_uid = uid
ldap_scope = 3
ldap_timeout = 5
# 打开或关闭自注册功能
self_registration = on
# 令牌服务创建的令牌的过期时间
token_expiration = 30
# 设置为“adminonly”,以便只有管理员用户才能创建项目
# 默认值“everyone”允许每个人创建一个项目
project_creation_restriction = everyone
#
verify_remote_cert = on
4. 准备
./prepare
docker-compose.yml 文件,用于配置数据目录等配置信息
5. 运行当前目录下的脚本开始安装
./install.sh
安装完成,可以通过设定的连接通过浏览器进行访问(默认使用的是80,加密端口是443,此主机没有备案所以下面还需要修改一下端口)
后期修改配置:
如果 harbor 运行一段时间之后需要更改配置,则步骤如下:这里顺便修改一下harbor仓库的访问的默认端口
1. 先停止 harbor:
2. 编辑 harbor.cfg 进行相关配置:(修改之前一定要记得先copy一份)
vim harbor.cfg # 主配置文件不需要修改
# 修改docker-compose.yml文件,默认访问的端口映射为888端口
① 这里修改了两个端口 80改为888,443改为了4433
② 修改完之后还要修改一个文件
# pwd
/usr/local/harbor/common/templates/registry
[root@Server2 registry]# ls
config.yml config.yml.bak root.crt
[root@Server2 registry]# vim config.yml
url后加上修改后的默认端口,保存退出
③ 修改docker的启动文件
#vim /usr/lib/systemd/system/docker.service 修改如下一行 ExecStart=/usr/bin/dockerd --insecure-registry=x.x.x.x:888
修改后重新加载并重启服务
systemctl daemon-reload && systemctl restart docker.service
3. prepare
./prepare
4. 启动 harbor 服务:
docker-compose start
登录测试一下,登录成功
常见的2个报错信息解答:
(1)Error response from daemon: Get https://192.168.0.59/v1/users/: dial tcp 192.168.0.59:443: getsockopt: connection refused
(2)Error response from daemon: Get https://192.168.0.59:888/v1/users/: http: server gave HTTP response to HTTPS client
报这2个错误的都是如下2个原因:
1、是端口错了!
2、未在docker启动文件中添加--insecure-registry信任关系!
大多数这个错误是第2个原因,因为你没有添加信任关系的话,docker默认使用的是https协议,所以端口不对(443),会报连接拒绝这个错误;
或者提示你 "服务器给HTTPS端的是HTTP响应" 这个错误,因为你没添加端口信任,服务器认为这是默认的https访问,返回的却是http数据!
解决方法:
正确的添加信任关系包括端口号:
--insecure-registry=192.168.0.59:888
一定要把主机与端口同时添加进去!
继续
- 查看本地的镜像
2. 查看本地端口
3. web 浏览器访问 Harbor 管理界面
如果可以正常登录就说明之前的配置没毛病,下面就可上传镜像了
配置 docker 使用 harbor 仓库上传下载镜像:
- 编辑 docker 配置文件:
注意:如果我们配置的是 https 的话,本地 docker 就不需要有任何操作就可以访问 harbor 了,在第一台主机的docker启动文件中也添加上内网harbor仓库的地址
vim /lib/systemd/system/docker.service
在ExecStart
追加
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock --insecure-registry 192.168.0.59:888
- 重启 docker 服务:
systemctl daemon-reload && systemctl restart docker
- 验证能否登录 harbor:
docker login 192.168.0.59:888
如果这里突然遇到密码错误(确认不是忘记了密码),可以试试重启docker-compose
docker-compose stop
docker-compose start
- 从server1导入一个小镜像,
docker load < /root/alpine.tar.gz
- 验证镜像导入成功(至少要有个镜像,没有的话按照上面导入)
docker images
- 镜像打 tag:
> 修改 images 的名称,不修改成指定格式无法将镜像上传到 harbor 仓库,格式为: HarborIP/项目名/image 名字:版本号:
首先要新建一个项目,进入项目里,右侧推送镜像,按照指定格式进行修改才能上传到此项目中来
- 查看镜像
docker images
- 修改镜像的tag号因为使用的端口不一样,如果是443端口就不需要带端口号,不过目前也还没有配置加密证书所以还得带着端口号才可以
docker tag alpine:latest X.X.X.X:888/alpine/alpine-test2:v4
-
将镜像 push 到 harbor:因为默认上传时使用加密的传输方式也就是https,这里因为修改了端口所以有些区别
格式为: docker push 镜像名:版本
docker push x.x.x.x:888/alpine/alpine-test2:v4
- push 完成
-
harbor 界面验证镜像上传成功
-
验证镜像信息
从 harbor 下载镜像并启动容器:
- 更改 docker 配置文件:
目前凡是需要从 harbor 镜像服务器下载 image 的 docker 服务都要更改,不更改的话无法下载:
sudo vim /lib/systemd/system/docker.service
在ExecStart追加 ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock --insecure-registry 192.168.0.59:888
- 重启 docker:
systemctl daemon-reload
systemctl restart docker
3. 查看下载命令:
-
执行下载:
docker pull X.X.X.X:888/alpine/alpine-test2:v4
- 验证镜像下载完成
- 启动容器:
docker run -it x.x.x.x:888/alpine/alpine-test2:v4 sh
7. 成功启动,并可以正常使用
实现 harbor 高可用:
Harbor 支持基于策略的 Docker 镜像复制功能, 这类似于 MySQL 的主从同步, 其可以实现不同的数据中心、 不同的运行环境之间同步镜像, 并提供友好的管理界面,大大简化了实际运维中的镜像管理工作,已经有用很多互联网公司使用harbor 搭建内网 docker 仓库的案例,并且还有实现了双向复制的案列,本文将实现单向复制的部署:
1. 新部署一台 harbor2 服务器192.168.0.36
过程见前一节
- 验证从 harbor 登录,harbor2
- 主 harbor :192.168.0.59
复制的原理是从源harbor上推到目标harbor,所以下面的配置是要在之前创建的harbor上配置。
主harbor:192.168.0.59
从harbor:192.168.0.36
- 主 harbor:创建目标
-
主 harbor:测试连接成功后,确定
-
从 harbor2:创建一个 alpine 项目:
与主 harbor1 项目名称保持一致:
-
主 harbor:配置复制
-
主 harbor:点击复制,新建规则:
-
主 harbor:编辑同步策略
-
主 harbor:查看镜像同步状态
-
从 harbor:查看镜像是否同步成功
测试从 harbor 镜像下载和容器启动:
- 编辑 docker 配置文件:
vim /lib/systemd/system/docker.service
在ExecStart
追加
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock --insecure-registry 192.168.0.36:888 --insecure-registry 192.168.0.59:888
其中 192.168.0.36/59 是我们部署 Harbor 的地址,即 hostname 配置项值。配置完后需要重启 docker 服务。
- 重启 docker 服务:
systemctl daemon-reload
systemctl restart docker
- 从 harbor 项目设置为公开:
-
docker 客户端下载镜像
docker pull 192.168.0.36:888/alpine/alpine111:v10
5. 下载完后查看镜像
- docker 客户端从镜像启动容器:
docker run -it 192.168.0.36:888/alpine/alpine111:v10 sh
如果可以从新harbor2上拉取镜像并运行,就说明harbor已经高可用且同步成功
至此,高可用模式的 harbor 仓库部署完毕
Docker 数据管理
如果运行中的容器修如果生成了新的数据或者修改了现有的一个已经存在的文件内容,那么新产生的数据将会被复制到读写层进行持久化保存,这个读写层也就是容器的工作目录,此即“写时复制(COW) copy on write”机制
数据类型:
Docker 的镜像是分层设计的,底层是只读的,通过镜像启动的容器添加了一层可读写的文件系统,用户写入的数据都保存在这一层当中,如果要将写入的数据永久生效,需要将其提交为一个镜像然后通过这个镜像在启动实例,然后就会给这个启动的实例添加一层可读写的文件系统,目前 Docker 的数据类型分为两种, 一是数据卷, 二是数据容器,数据卷类似于挂载的一块磁盘,数据容器是将数据保存在一个容器上
- 查看指定 PID 的容器信息
docker inspect f55c55544e05
LowerDir: image 镜像层(镜像本身,只读)
UpperDir:容器的上层(读写)
MergedDir:容器的文件系统,使用 Union FS(联合文件系统)将 lowerdir 和
WorkDir:容器在 宿主机的工作目录
什么是数据卷(data volume):
数据卷实际上就是宿主机上的目录或者是文件,可以被直接 mount 到容器当中使用。
实际生成环境中,需要针对不同类型的服务、 不同类型的数据存储要求做相应的规划, 最终保证服务的可扩展性、 稳定性以及数据的安全性。
如下图:
左侧是无状态的 http 请求服务, 右侧为有状态。
下层为不需要存储的服务,上层为需要存储的部分服务
创建 APP 目录并生成 web 页面:
此 app 以数据卷的方式,提供给容器使用, 比如容器可以直接宿主机本地的 web_app,而需要将代码提前添加到容器中,此方式适用于小型 web 站点。
mkdir /data/testapp –p
echo "testapp page" > /data/testapp/index.html
在宿主机或容器修改数据
- 给目录下添加index文件
mkdir /data/app
echo "docker volume Pages1" > /data/app/index.html
启动容器并验证数据:
启动两个容器, web1 容器和 web2 容器, 分别测试能否在容器内访问到宿主机的添加的数据。
注意使用 -v
参数, 将宿主机目录映射到容器内部, web2 的 ro 标示在容器内对该目录只读,默认是可读写的:
docker run -d --name web1 -v /data/app/:/usr/share/nginx/html -p 85:80 nginx
docker run -d --name web2 -v /data/app/:/usr/share/nginx/html:ro -p 86:80 nginx
2、web 界面访问(这里使用命令行测试)
3、删除容器
删除容器的时候指定参数-v, 可以删除/var/lib/docker/containers/的容器数据目录,但是不会删除数据卷的内容
docker rm -fv d044730c0f4f
数据卷的特点及使用:
1、数据卷是宿主机的目录或者文件,并且可以在多个容器之间共同使用。
2、 在宿主机对数据卷更改数据后会在所有容器里面会立即更新。
3、数据卷的数据可以持久保存,即使删除使用使用该容器卷的容器也不影响。
4、 在容器里面的写入数据不会影响到镜像本身。文件挂载:
文件挂载用于很少更改文件内容的场景, 比如 nginx 的配置文件、 tomcat 的配置文件等
数据卷使用场景:
1、 日志输出
2、 静态 web 页面
3、 应用配置文件
4、 多容器间目录或文件共享
如何一次挂载多个目录
- 多个目录可以位于不同的目录下
mkdir /data/xuecho "xu" > /data/xu/index.html
- 启动容器
docker run -ti -d --name web1 -p 85:80
-v /data/app/nginx.conf:/etc/nginx/nginx.conf:ro
-v /data/nginx/:/usr/share/nginx/html
nginx
3. 验证 web 访问
数据卷容器:
数据卷容器功能是可以让数据在多个 docker 容器之间共享,即可以让 B 容器访问 A 容器的内容,而容器 C 也可以访问 A 容器的内容,
即先要创建一个后台运行的容器作为 Server,用于卷提供, 这个卷可以为其他容器提供数据存储服务,其他使用此卷的容器作为 client 端:
启动一个卷容器 Server:
先启动一个容器, 并挂载宿主机的数据目录:
将宿主机的 catalina.sh 启动脚本和 chen 的 web 页面,分别挂载到卷容器 server端,然后通过 server 端共享给 client 端使用。
docker run -d --name volume-server
-v /data/app/nginx.conf:/etc/nginx/nginx.conf:ro
-v /data/nginx:/usr/share/nginx/html
nginx
- 启动两个端容器 Client
docker run -d --name web1 -p 85:80
--volumes-from volume-server nginx
docker run -d --name web2 -p 86:80
--volumes-from volume-server nginx
- 其实实质就是把挂载在其它容器的卷,再挂到其它容器上
docker run -d --name web3 -p 83:80 --volumes-from web2 nginx
- 测试访问 web 页面
三个容器访问到的数据是一样的
-
进入其它一个容器,修改网站的默认测试页面
docker exec -it 85466a0c2fda bash
5. 验证宿主机数据
# cat /data/nginx/index.html 3333
- 关闭卷容器 Server 测试能否启动新容器:
docker stop volume-server docker run -d --name web6 -p 86:80 --volumes-from volume-server nginx
停止了卷服务的容器,还可以从这个卷服务容器启动新的容器
- 测试删除源卷容器 Server 创建容器
docker rm -fv volume-server
[root@Server1 ~]# docker run -d --name web7 -p 87:80 --volumes-from volume-server nginx
docker: Error response from daemon: No such container: volume-server.
# 这里就不行了,报错
- 测试之前的容器是否正常——已经运行的容器不受任何影响
之前已启动的容器不受影响
重新创建容器卷 Server
docker run -d --name volume-server -v /data/app/nginx.conf:/etc/nginx/nginx.conf:ro -v /data/nginx:/usr/share/nginx/html nginx
创建出 volume server 之后, 就可以创建基于 volume server 的新容器。
docker run -d --name web8 -p 88:80 --volumes-from volume-server nginx
在当前环境下, 即使把提供卷的容器 Server 删除, 已经运行的容器 Client 依然可以使用挂载的卷, 因为容器是通过挂载访问数据的, 但是无法创建新的卷容器客户端, 但是再把卷容器 Server 创建后即可正常创建卷容器 Client,
此方式可以用于线上共享数据目录等环境, 因为即使数据卷容器被删除了,其他已经运行的容器依然可以挂载使用
数据卷容器可以作为共享的方式为其他容器提供文件共享,类似于 NFS 共享,可以在生产中启动一个实例挂载本地的目录,然后其他的容器分别挂载此容器的目录,即可保证各容器之间的数据一致性。
十五:
主要介绍docker网络相关知识,docker服务安装完成之后,至少在每个主机机会生成一个名称为docker0的网卡
其IP地址都是172.17.0.1/16,并且会生成某种不同类型的网络,如下图:
docker结合负载实现网站高可用:
整体规划图:
下图为一个小型的网络架构图,其中nginx使用docker运行
服务器名 | 系统 | ip |
---|---|---|
服务器1 | centos 7.6 | 192.168.99.101 |
服务器2 | centos 7.6 | 192.168.99.102 |
码头工人1 | Ubuntu的04.04 | 192.168.99.22 |
码头工人2 | Ubuntu的04.04 | 192.168.99.23 |
1.安装并配置keepalived
- Server1安装
yum install keepalived haproxy -y vim /etc/keepalived/keepalived.conf ... ! Configuration File for keepalived global_defs { notification_email { root@localhost } notification_email_from root@localhost smtp_server localhost smtp_connect_timeout 30 router_id LVS_DEVEL1 vrrp_skip_check_adv_addr #vrrp_strict vrrp_iptables vrrp_garp_interval 0 vrrp_gna_interval 0 } vrrp_instance MAKE_VIP_INT { state MASTER interface eth0 virtual_router_id 1 priority 100 authentication { auth_type PASS auth_pass 1111 } virtual_ipaddress { 192.168.99.10 dev eth0 label eth0:1 } }
启动
systemctl restart keepalived
systemctl enable keepalived
- Server2安装
yum install keepalived haproxy –y
配置
vim /etc/keepalived/keepalived.conf ! Configuration File for keepalived global_defs { notification_email { root@localhost } notification_email_from root@localhost smtp_server localhost smtp_connect_timeout 30 router_id LVS_DEVEL2 vrrp_skip_check_adv_addr #vrrp_strict vrrp_iptables vrrp_garp_interval 0 vrrp_gna_interval 0 } vrrp_instance MAKE_VIP_INT { state BACKUP interface eth0 virtual_router_id 1 priority 50 authentication { auth_type PASS auth_pass 1111 } virtual_ipaddress { 192.168.99.10 dev eth0 label eth0:1 } }
启动
systemctl restart keepalived
systemctl enable keepalived
2.安装并配置haproxy
- 各服务器配置内核参数
echo "net.ipv4.ip_nonlocal_bind=1" >> /etc/sysctl.conf sysctl -p
- Server1配置haproxy
vim /etc/haproxy/haproxy.cfg global maxconn 100000 uid 99 gid 99 daemon nbproc 1 log 127.0.0.1 local0 info defaults option http-keep-alive #option forwardfor maxconn 100000 mode tcp timeout connect 500000ms timeout client 500000ms timeout server 500000ms listen stats mode http bind 0.0.0.0:9999 stats enable log global stats uri /haproxy-status stats auth haadmin:q1w2e3r4ys #================================================================ frontend docker_nginx_web bind 0.0.0.0:80 mode http default_backend docker_nginx_hosts backend docker_nginx_hosts mode http balance roundrobin server 192.168.99.22 192.168.99.22:81 check inter 2000 fall 3 rise 5 server 192.168.99.23 192.168.99.23:81 check inter 2000 fall 3 rise 5
systemctl enable haproxy
systemctl restart haproxy
- Server2配置haproxy
vim /etc/haproxy/haproxy.cfg global maxconn 100000 uid 99 gid 99 daemon nbproc 1 log 127.0.0.1 local0 info defaults option http-keep-alive #option forwardfor maxconn 100000 mode tcp timeout connect 500000ms timeout client 500000ms timeout server 500000ms listen stats mode http bind 0.0.0.0:9999 stats enable log global stats uri /haproxy-status stats auth haadmin:q1w2e3r4ys #================================================================ frontend docker_nginx_web bind 0.0.0.0:80 mode http default_backend docker_nginx_hosts backend docker_nginx_hosts mode http balance roundrobin server 192.168.99.22 192.168.99.22:81 check inter 2000 fall 3 rise 5 server 192.168.99.23 192.168.99.23:81 check inter 2000 fall 3 rise 5
systemctl enable haproxy
systemctl restart haproxy
3. docker服务器启动nginx容器并验证
- dcoekr1启动Nginx容器
docker1:192.168.99.22
# 先下载nginx镜像
docker pull nginx docker run --name nginx-web1 -d -p 81:80 nginx
验证网络访问
- docker2启动nginx容器
docker2:192.168.99.23
# 先下载nginx镜像
docker pull nginx docker run --name nginx-web1 -d -p 81:80 nginx
验证网络访问
- 访问VIP
-
Server1 haproxy状态页面:192.168.99.101:9999 / haproxy-status
-
Server2 haproxy状态页面:192.168.99.102:9999 / haproxy-status
容器之间的互联
通过容器名称互联:
即在同一个主机上的容器之间可以通过自定义的容器名称相互访问,某个业务前端静态页面是使用nginx,动态页面使用的是tomcat,由于容器在启动的时候其内部IP地址是DHCP随机分配的,所以如果通过内部访问的话,自定义名称是相对比较固定的,因此比较适用于此场景。
- 先创建第一个容器,后续会使用到这个容器的名称:
docker run -it -d --name app1 -p 88:80 nginx
- 查看当前主机文件内容
[root@Server1 ~]# docker exec -it app1 bash root@260fea18537b:/# cat /etc/hosts 127.0.0.1 localhost ::1 localhost ip6-localhost ip6-loopback fe00::0 ip6-localnet ff00::0 ip6-mcastprefix ff02::1 ip6-allnodes ff02::2 ip6-allrouters 172.17.0.2 260fea18537b
- 创建第二个容器
docker run -it -d -p 89:80 --name app2 --link app1 nginx
- 查看第二个容器的主机文件内容
[root@Server1 ~]# docker exec -it app2 bash root@cd11e8d1d51e:/# cat /etc/hosts 127.0.0.1 localhost ::1 localhost ip6-localhost ip6-loopback fe00::0 ip6-localnet ff00::0 ip6-mcastprefix ff02::1 ip6-allnodes ff02::2 ip6-allrouters 172.17.0.2 app1 260fea18537b ## 连接的对方容器的 ID 和容器名称
172.17.0.3 cd11e8d1d51e
- 检测通信
ping app1
可能需要先安装ping工具
apt-get update && apt install -y iputils-ping
通过自定义容器别名互联:
上一步骤中,自定义的容器名称可能后期会发生变化,那么一旦名称发生变化,程序之间也要随之发生变化,此类程序通过容器名称进行服务调用,但是容器名称发生变化之后再使用之前的名称肯定是无法成功调用,每次都进行更改的话又比较麻烦,因此可以使用自定义别名的方式解决,即容器名称可以随意更容易,只要不更改别名即可,具体如下:
命令格式:
docker run -d --name 新容器名称
--link 目标容器名称:自定义的名称
-p 本地端口:容器端口 镜像名称 shell命令
- 启动第三个容器
docker run -it -d -p 90:80 --name app3 --link app1:web1 nginx
- 查看当前容器的主机
[root@Server1 ~]# docker exec -it app3 bash
root@9b99ad938dbe:/# cat /etc/hosts
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.17.0.2 web1 260fea18537b app1 # 多了一个别名web1
172.17.0.4 9b99ad938dbe
- 检查自定义别名通信
ping web1
-
docker网络类型:
docker的网络使用docker network ls命令看到有一种类型,下面将介绍每一种类型的具体工作方式:
使用参数–net=网络类型
指定,而不指定替代就是bridge模式。
查看当前docke的网卡信息:
docker network list
网桥:可以,使用自定义IP
主机:不获取IP直接使用物理机IP,并监听物理机IP监听端口
无:没有网络
(1)主机模式:使用参数–net = host指定。
启动的容器如果指定了使用主机模式,那么新创建的容器不会创建自己的虚拟网卡,而是直接使用托管机的网卡和IP地址,因此在容器里面查看到的IP信息就是宿主机的信息,
访问容器的时候直接使用主机机IP +容器端口即可,不过容器的其他资源他们必须文件系统,系统进程等还是和主机保持隔离。模式的网络性能最高,但是各个容器之间的端口不能相同,适用于运行容器端口比较固定的业务。
- 为避免端口冲突,先删除所有的容器:
docker rm -fv `docker ps -a -q`
2. 启动一个新容器,并指定网络模式为主机
docker run -d --name app1 --net=host nginx
3. 访问
主机机验证因为是主机模式,所以直接访问主机机的IP:192.168.0.36
访问到的是nginx的默认首页
主机模式不支持端口映射,当指定端口映射的时候会提示如下警告信息:
使用主机网络模式时,将替换已指定的端口
docker run -d --name app2 -p 81:80 --net=host nginx
(2)None模式:使用参数–net = none指定
在使用none模式后,Docker容器不会进行任何网络配置,其没有网卡,没有IP也没有路由,因此无法与外部通信,需要手动添加网卡配置IP等,所以极少使用
命令使用方式:
docker run -it -d --name net_none -p 80:80 --net=none nginx
(3)容器模式:使用参数–net = container:name或ID
使用此模式创建的容器需要指定和一个已经存在的容器共享一个网络,而不是和容纳机共享网,新创建的容器不会创建自己的网卡也不会配置自己的IP,或者和一个已经存在的被指定的容器IP和端口范围,
因此该容器的端口不能和被指定的端口冲突,除了网络之外的文件系统,进展信息等仍保持相互隔离,两个容器的进展可以通过lo网卡及容器IP进行通信。
- 先起一个容器
docker run -it -d --name app11 -p 7070:8080 nginx
- 另一个容器直接使用对方的网络
docker run -it -d --name app12 --net=container:app11 tomcat85-app1:v1
- nginx的默认端口为80,而8080是tomcat的默认端口,所以,如果我们直接访问托宿主机的7070,应该是访问到tomcat的页面才对。如图
(4)bridge模式: docker的交替模式即不指定任何模式就是bridge模式,也是使用比较多的模式,此模式创建的容器会为每一个容器分配自己的网络IP等信息,且多个容器连接到一个虚拟网桥与外界通信
查看bridge模式的信息
docker network inspect bridge
docker跨主机互联之简单实现:
跨主机互联是说A主机的容器可以访问B主机上的容器,但是可以保证各主机之间的网络是可以相互通信的,然后各容器才可以通过主机访问到对方的容器,实现原理是在主机做一个网络路由就可以实现A主机的容器访问B主机的容器的目的,
复杂的网络或者大型的网络可以使用google开源的k8s进行互联。
修改各主机机网段: Docker的替代网段是172.17.0.x / 24,而且每个主机机都是一样的,因此路由选择的路由就是各个主机的网络不能一致,具体如下:
服务器A:192.168.0.36
服务器B:192.168.0.59
- 服务器A更改网段
vim /lib/systemd/system/docker.service
在ExecStart
结尾追加--bip=10.10.0.1/24
,ip是你想设置的网络的网关ip
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock --bip=10.10.0.1/24
- 重启docker服务
systemctl daemon-reload
systemctl restart docker
- 验证一个服务器网卡
ifconfig docker0
如果你的系统是Centos,需要打开ipforward
echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf sysctl -p
- 启动一个容器
docker run -it -p 87:80 nginx bash [root@781e7f053c20 /]# ifconfig eth0
- 添加静态路由(主机上)
route add -net 10.10.0.0/24 gw 192.168.0.59
iptables -A FORWARD -s 192.168.0.0/24 -j ACCEPT
- 服务器B更改网段
vim /lib/systemd/system/docker.service
在ExecStart
结尾追加--bip=10.20.0.1/24
,注意不能跟服务器A一样
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock --bip=10.20.0.1/24
- 重启docker服务
systemctl daemon-reload
systemctl restart docker
- 验证网卡: ifconfig docker0
- 如果你的系统是Centos,需要打开ipforward规则转发
echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf sysctl -p
- 启动一个容器
docker run -it -p 82:80 nginx bash
[root@781e7f053c20 /]# ifconfig eth0
- 添加静态路由
route add -net 10.10.0.0/24 gw 192.168.0.36
iptables -A FORWARD -s 192.168.0.0/24 -j ACCEPT
容器间互相ping测试
互连两台服务器分别启动两个容器进行测试:10.10.0.0网段是服务器A内的容器,10.20.0.0网段是服务器B内的容器
在另一台容器里反ping试一下
如果可以ping通就表示没有问题,两个不同宿主机上的容器可以互相通信了
创建自定义网络:
可以基于docker命令创建自定义网络,自定义网络可以自定义IP地域和网关等信息。
帮助
docker network create –help
- 创建自定义docker网络
docker network create -d bridge --subnet 10.100.0.0/24
--gateway 10.100.0.1 my-net
- 查看网络
docker network list
- 使用自定义网络创建容器
[root@Server1 ~]# docker run -itd --net=my-net centos-nginx:v1 bash
[root@Server1 ~]# docker exec -it 25d2c84d58bd bash
[root@25d2c84d58bd /]# yum install -y net-tools &> /dev/null
[root@25d2c84d58bd /]# ifconfig eth0
- 创建默认网络容器
docker run -it centos-nginx:v1 bash
[root@ed06c2785c92 /]# ifconfig eth0 [root@ed06c2785c92 /]# ping www.baidu.com
- 如何与使用交替网络的容器通信:
现在有一个docker0(10.10.0.0/24)网络一个自定义的my-net(10.100.0.0/24)网络,每个网络上分别运行了不同数量的容器,那么怎么才能让位于不同网络的容器可以相互通信呢? 那就是修改防火墙规则条目 -
保存下iptbales
iptables-save > iptables.sh
- 修改iptables.sh
# vim iptables.sh
把DOCKER-ISOLATION-STAGE-2除了RETURN
结尾的都注释了
- 重新导入修改后的iptables规则
iptables-restore < iptables.sh
4. 验证通信
先看一下其他网段的容器ip地址
ping测试一下
经过以上的配置,目前就已经实现了不通网络间的通信
十六、Docker资源限制
官网:https://docs.docker.com/config/containers/resource_constraints/
默认情况下, 容器没有资源限制, 可以使用主机内核调度程序允许的尽可能多的给定资源, Docker 提供了控制容器可以限制容器使用多少内存或 CPU 的方法,设置 docker run 命令的运行时配置标志。
其中许多功能都要求宿主机的内核支持 Linux 功能, 要检查支持, 可以使用docker info 命令,如果内核中禁用了某项功能, 可能会在输出结尾处看到警告,
如下所示:
WARNING: No swap limit support
对于 Linux 主机, 如果没有足够的内容来执行其他重要的系统任务, 将会抛出OOM (Out of Memory Exception,内存溢出、 内存泄漏、 内存异常), 随后系统会开始杀死进程以释放内存, 凡是运行在宿主机的进程都有可能被 kill, 包括 Dockerd和其它的应用程序, 如果重要的系统进程被 Kill,会导致和该进程相关的服务全部宕机。
产生 OOM 异常时, Dockerd 尝试通过调整 Docker 守护程序上的 OOM 优先级来减轻这些风险,以便它比系统上的其他进程更不可能被杀死,但是容器的 OOM优先级未调整, 这使得单个容器被杀死的可能性比 Docker 守护程序或其他系统进程被杀死的可能性更大,不推荐通过在守护程序或容器上手动设置--oomscore-adj 为极端负数,或通过在容器上设置--oom-kill-disable 来绕过这些安全措施。
OOM 优先级机制:
linux 会为每个进程算一个分数,最终他会将分数最高的进程 kill。
/proc/PID/oom_score_adj
:范围为-1000 到 1000,值越高越容易被宿主机 kill掉,如果将该值设置为-1000,则进程永远不会被宿主机 kernel kill。
/proc/PID/oom_adj
:范围为-17 到+15,取值越高越容易被干掉,如果是-17,则表示不能被 kill,该设置参数的存在是为了和旧版本的 Linux 内核兼容。
/proc/PID/oom_score
:这个值是系统综合进程的内存消耗量、 CPU 时间(utime + stime)、存活时间(uptime - start time)和 oom_adj 计算出的,消耗内存越多分越高,存活时间越长分越低。
容器的内存限制:
Docker 可以强制执行硬性内存限制,即只允许容器使用给定的内存大小。
Docker 也可以执行非硬性内存限制,即容器可以使用尽可能多的内存,除非内核检测到主机上的内存不够用了。
--oom-score-adj: 宿主机 kernel 对进程使用的内存进行评分, 评分最高的将被宿主机内核 kill 掉, 可以指定一个容器的评分制但是不推荐手动指定。
--oom-kill-disable: 对某个容器关闭 oom 机制。
内存限制参数:
-m, --memory:容器可以使用的最大内存量,如果设置此选项,则允许的最内存值为 4m (4 兆字节)。
--memory-swap: 容器可以使用的交换分区大小, 必须要在设置了物理内存限制的前提才能设置交换分区的限制
--memory-swappiness: 设置容器使用交换分区的倾向性,值越高表示越倾向于使用 swap 分区,范围为 0-100, 0 为能不用就不用, 100 为能用就用。
--kernel-memory: 容器可以使用的最大内核内存量,最小为 4m,由于内核内存与用户空间内存隔离,因此无法与用户空间内存直接交换,因此内核内存不足的容器可能会阻塞宿主主机资源,这会对主机和其他容器或者其他服务进程产生影响,因此不要设置内核内存大小。
--memory-reservation:允许指定小于--memory 的软限制,当 Docker 检测到主机上的争用或内存不足时会激活该限制,如果使用--memory-reservation,则必须将其设置为低于--memory 才能使其优先。 因为它是软限制,所以不能保证容器不超过限制。
--oom-kill-disable 默认情况下,发生 OOM 时, kernel 会杀死容器内进程,但是可以使用--oom-kill-disable 参数,可以禁止 oom 发生在指定的容器上,即 仅在已设置-m
选项的容器上禁用 OOM,如果-m 参数未配置,产生 OOM 时,主机为了释放内存还会杀死系统进程。
swap 限制:
--memory-swap:只有在设置了 --memory 后才会有意义。使用 Swap,可以让容器将超出限制部分的内存置换到磁盘上, WARNING:经常将内存交换到磁盘的应用程序会降低性能。
不同 --memory-swap 的设置会产生不同的效果:
值为正数, 那么--memory 和--memory-swap 都必须要设置,--memory-swap
表示你能使用的内存和 swap 分区大小的总和,例如:--memory=300m
,--memory-swap=1g
, 那么该容器能够使用 300m 内存和 700m swap,即--memory
是实际物理内存大小值不变,而 swap 的实际大小计算方式为
(--memory-swap)-(--memory)=容器可用 swap(−−memory−swap)−(−−memory)=容器可用swap。如果设置为 0:则忽略该设置,并将该值视为未设置,即未设置交换分区。
如果等于--memory 的值,并且--memory 设置为正整数: 容器无权访问 swap 即也没有设置交换分区。
如果设置为 unset:如果宿主机开启了 swap,则实际容器的swap 值为 2x( --memory),即两倍于物理内存大小,但是并不准确(在容器中使用free 命令所看到的 swap 空间并不精确, 毕竟每个容器都可以看到具体大小,但是宿主机的 swap 是有上限而且不是所有容器看到的累计大小)。
如果设置为-1:如果宿主机开启了 swap,则容器可以使用主机上 swap 的最大空间。
动态修改容器内存,先计算出所需要的内存的字节数:如268435456(256M),只能调大不能调小
echo "268435456" > /sys/fs/cgroup/memory/docker/9fa20d824b18.../memory.limit_in_bytes
9fa20d824b18... 是容器ID
内存限制验证:
假如一个容器未做内存使用限制, 则该容器可以利用到系统内存最大空间, 默认创建的容器没有做内存资源限制。
#测试镜像
docker pull lorel/docker-stress-ng
查看帮助
apt install stress-ng
stress-ng --help
或者这样也行
docker run -it --rm lorel/docker-stress-ng -help
内存大小硬限制
查看docker状态
docker stats
- 启动两个工作进程,每个工作进程最大允许使用内存 256M,且宿主机不限制当前容器最大内存:
docker run -it --rm --name c1 lorel/docker-stress-ng --vm 2 --vm-bytes 256M
--vm 启动多少个进程
--vm-bytes 为每个进程分配的内存
- 宿主机限制容器最大内存使用:
docker run -it --rm -m 256m --name c2 lorel/docker-stress-ng
--vm 2 --vm-bytes 256M
- 宿主机 cgroup 验证:
# cat /sys/fs/cgroup/memory/docker/容器 ID /memory.limit_in_bytes
268435456
宿主机基于 cgroup 对容器进行内存资源的大小限制
注:通过 echo 命令可以改内存限制的值,但是可以在原基础之上增大内存限制,缩小内存限制会报错 write error: Device or resource busy
内存大小软限制:
docker run -it --rm -m 256m --memory-reservation 128m
--name c3 lorel/docker-stress-ng --vm 2 --vm-bytes 256M
- 宿主机 cgroup 验证:
# cat /sys/fs/cgroup/memory/docker/容器 ID/memory.soft_limit_in_bytes
134217728
返回的软限制结果
关闭 OOM 机制
docker run -it --rm -m 256m --oom-kill-disable
--name c4 lorel/dockerstress-ng --vm 2 --vm-bytes 256M
# cat /sys/fs/cgroup/memory/docker/容器 ID/memory.oom_control
oom_kill_disable 1
under_oom 1
oom_kill 0
交换分区限制
docker run -it --rm -m 256m --memory-swap 512m
--name magedu-c1 centos bash
宿主机 cgroup 验证:
# cat /sys/fs/cgroup/memory/docker/容器 ID/memory.memsw.limit_in_bytes
536870912
容器的 CPU 限制
一个宿主机,有几十个核心的 CPU, 但是宿主机上可以同时运行成百上千个不同的进程用以处理不同的任务, 多进程共用一个 CPU 的核心依赖计数就是为可压缩资源, 即一个核心的 CPU 可以通过调度而运行多个进程, 但是同一个单位时间内只能有一个进程在 CPU 上运行, 那么这么多的进程怎么在 CPU 上执行和调度的呢?
实时优先级: 0 - 99
非实时优先级(nice): -20 - 19, 对应 100 - 139 的进程优先级
Linux kernel 进程的调度基于 CFS(Completely Fair Scheduler), 完全公平调度
CPU 密集型的场景:优先级越低越好, 计算密集型任务的特点是要进行大量的计算,消耗 CPU 资源,比如计算圆周率、 数据处理、 对视频进行高清解码等等,全靠 CPU 的运算能力。
IO 密集型的场景:优先级值高点, 涉及到网络、磁盘 IO 的任务都是 IO 密集型任务,这类任务的特点是 CPU 消耗很少,任务的大部分时间都在等待 IO 操作完成(因为 IO 的速度远远低于 CPU 和内存的速度),比如 Web 应用, 高并发,数据量大的动态网站来说,数据库应该为 IO 密集型。
磁盘的调度算法
# cat /sys/block/sda/queue/scheduler
noop deadline [cfq]
默认情况下,每个容器对主机 CPU 周期的访问权限是不受限制的, 但是我们可以设置各种约束来限制给定容器访问主机的 CPU 周期,大多数用户使用的是默认的 CFS 调度方式, 在 Docker 1.13 及更高版本中, 还可以配置实时优先级。
参数:
--cpus :指定容器可以使用多少可用 CPU 资源, 例如,如果主机有两个 CPU,并且设置了--cpus =“1.5”,那么该容器将保证最多可以访问 1.5 个的 CPU(如果是 4 核 CPU, 那么还可以是 4 核心上每核用一点,但是总计是 1.5 核心的CPU), 这相当于设置--cpu-period =“100000”和--cpu-quota =“150000”
主要在 Docker 1.13 和更高版本中使用, 目的是替代--cpu-period
和--cpuquota
两个参数,从而使配置更简单, 最大不能超出宿主机的 CPU 总核心数(在操作系统看到的 CPU 超线程后的数值)。
分配给容器的 CPU 超出了宿主机 CPU 总数。
# docker run -it --rm --cpus 2 centos bash
docker: Error response from daemon: Range of CPUs is from 0.01 to 1.00, as there are only 1 CPUs available.
See 'docker run --help'.
--cpu-period:(CPU 调度周期)设置 CPU 的 CFS 调度程序周期,必须与
--cpuquota
一起使用,默认周期为 100 微秒
--cpu-quota: 在容器上添加 CPU CFS 配额, 计算方式为cpu-quota/cpu-period
的结果值, 早期的 docker(1.12 及之前)使用此方式设置对容器的 CPU 限制值,==新版本 docker(1.13 及以上版本)通常使用--cpus 设置此值。==--cpuset-cpus:用于指定容器运行的 CPU 编号,也就是我们所谓的绑核。
--cpuset-mem:设置使用哪个 cpu 的内存,仅对 非统一内存访问(NUMA)架构有效。
--cpu-shares:用于设置 cfs 中调度的相对最大比例权重,cpu-share 的值越高的容器,将会分得更多的时间片(宿主机多核 CPU 总数为 100%, 假如容器 A 为1024, 容器 B 为 2048, 那么容器 B 将最大是容器 A 的可用 CPU 的两倍 ),默认的时间片 1024,最大 262144。
测试 CPU 限制
- 未限制容器 CPU
对于一台四核的服务器,如果不做限制, 容器会把宿主机的 CPU 全部占完。
分配 4 核 CPU 并启动 4 个工作线程
docker run -it --rm --name c10 lorel/docker-stress-ng
--cpu 4 --vm 4
在宿主机使用 dokcer top 命令查看容器运行状态
docker top CONTAINER [ps OPTIONS]
容器运行状态:
docker stats
在宿主机查看 CPU 限制参数:
# cat /sys/fs/cgroup/cpuset/docker/${容器ID}/cpuset.cpus
0-3
- 限制容器 CPU
只给容器分配最多两核宿主机 CPU 利用率
docker run -it --rm --name c11
--cpus 2 lorel/docker-stress-ng --cpu 4 --vm 4
宿主机 cgroup 验证
# cat /sys/fs/cgroup/cpu,cpuacct/docker/容器 ID/cpu.cfs_quota_us 200000
每核心 CPU 会按照 1000 为单位转换成百分比进行资源划分, 2 个核心的 CPU 就是 200000/1000=200%, 4 个核心 400000/1000=400%,以此类推
宿主机 CPU 利用率
- 将容器运行到指定的 CPU 上
docker run -it --rm --name c12 --cpus 1 --cpuset-cpus 1,3 lorel/docker-stress-ng --cpu 2 --vm 2 # cat /sys/fs/cgroup/cpuset/docker/容器 ID /cpuset.cpus 1,3
容器运行状态
docker stats
- 基于 cpu—shares 对 CPU 进行切分
启动两个容器, c13 的--cpu-shares
值为 1000, c14 的--cpu-shares
为 500, 观察最终效果,--cpu-shares
值为 1000 的 c13 的 CPU 利用率基本是--cpu-shares
为 500 的 c14 的两倍:
docker run -it --rm --name c13 --cpu-shares 10
lorel/docker-stress-ng --cpu 1 --vm 2
docker run -it --rm --name c14 --cpu-shares 5
lorel/docker-stress-ng --cpu 1 --vm 2
验证容器运行状态
docker stats
宿主机 cgroup 验证
# cat /sys/fs/cgroup/cpu,cpuacct/docker/容器 ID/cpu.shares
1000
# cat /sys/fs/cgroup/cpu,cpuacct/docker/容器 ID/cpu.shares
500
- 动态修改 CPU shares 值:
--cpu-shares
的值可以在宿主机 cgroup 动态修改, 修改完成后立即生效,其值可以调大也可以减小。
echo 2000 > /sys/fs/cgroup/cpu,cpuacct/docker/容器 ID/cpu.shares
验证修改后的容器运行状态 docker stats
十七
单机编排之Docker撰写:
当在主机启动主机的容器时候,如果都是手动操作会觉得比较麻烦而且容器出错,这个时候推荐使用docker单机编排工具docker-compose,docker-compose是docker容器的一种单机编排服务,docker- compose是一个管理多个容器的工具,可以解决容器之间的依赖关系,就像启动一个nginx前端服务的时候会调用后端的tomcat,那就得先启动tomcat,但是启动tomcat容器还需要依赖数据库,则那就还得先启动数据库,docker-compose就可以解决这样的嵌套依赖关系,其完全可以替代docker run对容器进行创建,启动和停止。
docker-compose项目是Docker官方的开源项目,负责实现对Docker容器放置的快速编排,docker-compose将所管理的容器分为三层,分别是工程(项目),服务以及容器
github地址https://github.com/docker/compose
基础环境准备:
服务器1 :192.168.99.21,港口服务器2:192.168.99.22,
docker -compose 下面开始在服务器1上安装harbor和创建nginx,tomcat,haproxy大量备用
配置安装港口:
- 下载Harbor离线安装包:
cd /usr/local/src/ wget https://storage.googleapis.com/harbor-releases/release-1.7.0/harbor-offline-installer-v1.7.5.tgz
- 解压
tar xf harbor-offline-installer-v1.7.5.tgz ln -sv /usr/local/src/harbor /usr/local/
- 下载docker-compose
Ubuntu:
apt update apt install -y python-pip pip install docker-compose
中心:
yum install epel-release yum install -y python-pip pip install --upgrade pip pip install docker-compose
- 修改配置文件,最终配置如下
# vim harbor.cfg ... hostname = 192.168.99.22 ... harbor_admin_password = root123 ...
- 安装
./install.sh
- 编辑docker配置文件:
注意:如果我们配置的是https的话,本地docker就不需要任何任何操作就可以访问Harbor了
vim /lib/systemd/system/docker.service 在ExecStart追加 ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock --insecure-registry 192.168.99.21
其中192.168.99.21是我们部署Harbor的地址,即hostname配置项值。配置完后需要重启docker服务。
- 重启docker服务:
systemctl daemon-reload
systemctl restart docker
- 重启docker-compose
docker-compose restart
- 验证能否登录Harbor:
docker login 192.168.99.21
https://docs.docker.com/compose/reference/官方文档
制作nginx镜像
- 下载概述并初始化系统:
docker pull centos mkdir -pv /opt/dockerfile/web/nginx/html
- 编写Dockerfile:
cd /opt/dockerfile/web/nginx vim Dockerfile From centos:latest MAINTAINER han 123456@qq.com ADD nginx-1.10.3.tar.gz /usr/local/src/ RUN rpm -ivh http://mirrors.aliyun.com/epel/epel-release-latest-7.noarch.rpm && yum install -y vim wget tree lrzsz gcc gcc-c++ automake pcre pcre-devel zlib zlib-devel openssl openssl-devel iproute net-tools iotop && cd /usr/local/src/nginx-1.10.3 && ./configure --prefix=/usr/local/nginx --with-http_sub_module && make && make install && cd /usr/local/nginx/ ADD nginx.conf /usr/local/nginx/conf/nginx.conf RUN useradd nginx -s /sbin/nologin && ln -sv /usr/local/nginx/sbin/nginx /usr/sbin/nginx && echo "test nginx page" > /usr/local/nginx/html/index.html EXPOSE 80 443 CMD ["nginx","-g","daemon off;"]
- 准备网页
cd /opt/dockerfile/web/nginx/html echo "test nginx" > index.html cd /opt/dockerfile/web/nginx vim nginx.conf #user nobody; worker_processes 1; #pid logs/nginx.pid; events { worker_connections 1024; } http { include mime.types; default_type application/octet-stream; #log_format main '$remote_addr - $remote_user [$time_local] "$request" ' # '$status $body_bytes_sent "$http_referer" ' # '"$http_user_agent" "$http_x_forwarded_for"'; #access_log logs/access.log main; sendfile on; #tcp_nopush on; #keepalive_timeout 0; keepalive_timeout 65; #gzip on; server { listen 80; server_name localhost; #charset koi8-r; #access_log logs/host.access.log main; location / { root /usr/local/nginx/html; index index.html index.htm; } #error_page 404 /404.html; # redirect server error pages to the static page /50x.html # error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } } }
- 目录如下
# cd .. # tree . . ├── Dockerfile ├── html │ └── index.html ├── nginx-1.10.3.tar.gz └── nginx.conf
- 构建镜像
docker build -t mynginx:v1 /opt/dockerfile/web/nginx
- 查看是否生成新镜像
docker images
- 从总体启动容器
docker run --rm -p 81:80 --name nginx-web1
-v /opt/dockerfile/web/nginx/html/:/usr/local/nginx/html/
-v /opt/dockerfile/web/nginx/nginx.conf:/usr/local/nginx/conf/nginx.conf
mynginx:v1
- 访问网络界面
-
打标签:
docker tag mynginx:v1 192.168.99.21/nginx/mynginx:v1
-
在港口管理界面创建项目(需要先创建项目再上传高层)
-
将向前推到港口:
docker push 192.168.99.21/nginx/mynginx:v1
- 发布完成
制作JDK环境
- 执行完善JDK的方针:
mkdir -p /opt/dockerfile/web/jdk cd /opt/dockerfile/web/jdk/
- 编辑Dockerfile
vim Dockerfile #JDK Base Image FROM centos:latest ADD jdk-8u211-linux-x64.tar.gz /usr/local/src/ RUN ln -sv /usr/local/src/jdk1.8.0_211 /usr/local/jdk ADD profile /etc/profile ENV JAVA_HOME /usr/local/jdk ENV JRE_HOME $JAVA_HOME/jre ENV CLASSPATH $JAVA_HOME/lib/:$JRE_HOME/lib/ ENV PATH $PATH:$JAVA_HOME/bin RUN rm -rf /etc/localtime && ln -snf /usr/share/zoneinfo/Asia/Shanghai/etc/localtime && echo "Asia/Shanghai" > /etc/timezone
- 准备环境变量文件
vim profile
# /etc/profile # System wide environment and startup programs, for login setup # Functions and aliases go in /etc/bashrc # It's NOT a good idea to change this file unless you know what you # are doing. It's much better to create a custom.sh shell script in # /etc/profile.d/ to make custom changes to your environment, as this # will prevent the need for merging in future updates. pathmunge () { case ":${PATH}:" in *:"$1":*) ;; *) if [ "$2" = "after" ] ; then PATH=$PATH:$1 else PATH=$1:$PATH fi esac } if [ -x /usr/bin/id ]; then if [ -z "$EUID" ]; then # ksh workaround EUID=`/usr/bin/id -u` UID=`/usr/bin/id -ru` fi USER="`/usr/bin/id -un`" LOGNAME=$USER MAIL="/var/spool/mail/$USER" fi # Path manipulation if [ "$EUID" = "0" ]; then pathmunge /usr/sbin pathmunge /usr/local/sbin else pathmunge /usr/local/sbin after pathmunge /usr/sbin after fi HOSTNAME=`/usr/bin/hostname 2>/dev/null` HISTSIZE=1000 if [ "$HISTCONTROL" = "ignorespace" ] ; then export HISTCONTROL=ignoreboth else export HISTCONTROL=ignoredups fi export PATH USER LOGNAME MAIL HOSTNAME HISTSIZE HISTCONTROL # By default, we want umask to get set. This sets it for login shell # Current threshold for system reserved uid/gids is 200 # You could check uidgid reservation validity in # /usr/share/doc/setup-*/uidgid file if [ $UID -gt 199 ] && [ "`/usr/bin/id -gn`" = "`/usr/bin/id -un`" ]; then umask 002 else umask 022 fi for i in /etc/profile.d/*.sh /etc/profile.d/sh.local ; do if [ -r "$i" ]; then if [ "${-#*i}" != "$-" ]; then . "$i" else . "$i" >/dev/null fi fi done unset i unset -f pathmunge export JAVA_HOME=/usr/local/jdk export TOMCAT_HOME=/apps/tomcat export PATH=$JAVA_HOME/bin:$JAVA_HOME/jre/bin:$TOMCAT_HOME/bin:$PATH export CLASSPATH=.$CLASSPATH:$JAVA_HOME/lib:$JAVA_HOME/jre/lib:$JAVA_HOME/lib/tools.jar
看下目录结构
# tree /opt/dockerfile/web/jdk /opt/dockerfile/web/jdk ├── Dockerfile ├── jdk-8u211-linux-x64.tar.gz └── profile
- 构建
cd /opt/dockerfile/web/jdk
docker build -t myjdk:v1 .
- 打标记
docker tag myjdk:v1 192.168.99.21/jdk/myjdk:v1
-
在港口页面上创建jdk项目
-
将全面上传到Harbor(以下有讲)
docker push 192.168.99.21/jdk/myjdk:v1
从JDK整合重构tomcat
- 进入tomcat目录:
mkdir -p /opt/dockerfile/web/tomcat cd /opt/dockerfile/web/tomcat
- 编辑Dockerfile文件
vim Dockerfile FROM 192.168.99.21/jdk/myjdk:v1 RUN useradd www -u 2020 ENV TZ "Asia/Shanghai" ENV LANG en_US.UTF-8 ENV TERM xterm ENV TOMCAT_MAJOR_VERSION 8 ENV TOMCAT_MINOR_VERSION 8.0.49 ENV CATALINA_HOME /apps/tomcat ENV APP_DIR ${CATALINA_HOME}/webapps RUN mkdir /apps ADD apache-tomcat-8.5.45.tar.gz /apps RUN ln -sv /apps/apache-tomcat-8.5.45 /apps/tomcat
- 发布tomcat压缩包:
apache-tomcat-8.5.45.tar.gz
tree /opt/dockerfile/web/tomcat /opt/dockerfile/web/tomcat ├── apache-tomcat-8.5.45.tar.gz └── Dockerfile
- 通过脚本构建tomcat基础之上
docker build -t mytomcat:v1 .
- 验证合并重建完成
docker images
- 进行打上标签
docker tag mytomcat:v1 192.168.99.21/tomcat/mytomcat:v1
-
在港口页面上创建tomcat项目
-
发布
docker push 192.168.99.21/tomcat/mytomcat
构建tomcat-app业务运营:
- 准备目录
mkdir -pv /opt/dockerfile/web/tomcat-app cd /opt/dockerfile/web/tomcat-app
- 准备Dockerfile:
vim Dockerfile FROM 192.168.99.21/tomcat/mytomcat:v1 ADD run_tomcat.sh /apps/tomcat/bin/run_tomcat.sh ADD myapp/* /apps/tomcat/webapps/myapp/ RUN chown www.www /apps/ -R RUN chmod +x /apps/tomcat/bin/run_tomcat.sh EXPOSE 8080 8009 CMD ["/apps/tomcat/bin/run_tomcat.sh"]
- 准备自定义myapp页面:
mkdir myapp echo "MyTomcat Web app Page1" > myapp/index.html
- 准备容器启动执行脚本
run_tomcat.sh
:
vim run_tomcat.sh #!/bin/bash echo "nameserver 223.5.5.5" > /etc/resolv.conf su - www -c "/apps/tomcat/bin/catalina.sh start" su - www -c "tail -f /etc/hosts"
- 文件目录
# tree . ├── Dockerfile ├── myapp │ └── index.html └── run_tomcat.sh
- 构建
docker build -t mytomcat-app:v1 .
- 查看镜像
docker images
- 给Tomcat打标签
docker tag mytomcat-app:v1 192.168.99.21/tomcat/mytomcat-app:v1
- 推送
docker push 192.168.99.21/tomcat/mytomcat-app:v1
制作haproxy补充:
- 进入目录
mkdir -pv /opt/dockerfile/app/haproxy cd /opt/dockerfile/app/haproxy
- 准备Dockerfile:
vim Dockerfile #Haproxy Base Image FROM centos ADD haproxy-2.0.5.tar.gz /usr/local/src/ RUN yum -y install gcc gcc-c++ glibc glibc-devel pcre pcre-devel openssl openssl-devel systemd-devel net-tools vim iotop bc zip unzip zlib-devel lrzsz tree screen lsof tcpdump wget ntpdate && cd /usr/local/src/haproxy-2.0.5 && make ARCH=x86_64 TARGET=linux-glibc USE_PCRE=1 USE_OPENSSL=1 USE_ZLIB=1 USE_SYSTEMD=1 USE_CPU_AFFINITY=1 PREFIX=/usr/local/haproxy && make install PREFIX=/usr/local/haproxy && cp haproxy /usr/sbin/ && mkdir /usr/local/haproxy/run ADD haproxy.cfg /etc/haproxy/ ADD run_haproxy.sh /usr/bin RUN chmod +x /usr/bin/run_haproxy.sh EXPOSE 80 9999 CMD ["/usr/bin/run_haproxy.sh"]
- 准备
run_haproxy.sh
脚本
vim run_haproxy.sh #!/bin/bash haproxy -f /etc/haproxy/haproxy.cfg tail -f /etc/hosts
- 准备
haproxy.cfg
配置文件:
vim haproxy.cfg global chroot /usr/local/haproxy #stats socket /var/lib/haproxy/haproxy.sock mode 600 level admin uid 99 gid 99 daemon nbproc 1 pidfile /usr/local/haproxy/run/haproxy.pid log 127.0.0.1 local3 info defaults option http-keep-alive option forwardfor mode http timeout connect 300000ms timeout client 300000ms timeout server 300000ms listen stats mode http bind 0.0.0.0:9999 stats enable log global stats uri /haproxy-status stats auth haadmin:q1w2e3r4ys listen web_port bind 0.0.0.0:80 mode http log global balance roundrobin server web1 192.168.99.22:81 check inter 3000 fall 2 rise 5
调度到初步nginx服务的81端口
- 准备haproxy原始文件:
haproxy-2.0.5.tar.gz
# tree /opt/dockerfile/app/haproxy
/opt/dockerfile/app/haproxy
├── Dockerfile
├── haproxy-2.0.5.tar.gz
├── haproxy.cfg
└── run_haproxy.sh
- 准备编制脚本:
docker build -t haproxy:v1 .
- 给通用打标签
docker tag haproxy:v1 192.168.99.21/haproxy/haproxy:v1
- 港口创建haproxy项目
-
推送到仓库
docker push 192.168.99.21/haproxy/haproxy:v1
从docker compose启动临时容器
换成server2继续:
- 安装docker-18.09.9
过程略 -
下载docker-compose
Ubuntu:
apt update apt install -y python-pip pip install docker-compose
Centos:
yum install epel-release yum install -y python-pip pip install --upgrade pip pip install docker-compose
- 验证 docker-compose 版本
docker-compose -v
- 查看 docker-compose 帮助
docker-compose --help
- 编辑 docker 配置文件:
vim /lib/systemd/system/docker.service
在ExecStart
追加
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock --insecure-registry 192.168.99.21
- 重启 docker 服务:
systemctl daemon-reload
systemctl restart docker
- 测试连接
docker login 192.168.99.21
- 创建网页存放目录,给容器挂载用
mkdir -pv /opt/dockerfile/web/nginx/html cd /opt/dockerfile/web/nginx/html echo "test nginx" > index.html
- 目录可以在任意目录, 推荐放在有意义的位置。如:
cd /opt/ mkdir docker-compose cd docker-compose
- 单个容器的 docker compose 文件:
编写一个 yml 格式的配置 docker-compose 文件, 启动一个 nginx 服务
vim docker-compose.yml service-nginx-web: image: 192.168.99.21/nginx/mynginx:v1 container_name: nginx-web1 expose: - 80 - 443 ports: - "80:80" - "443:443" volumes: - "/opt/dockerfile/web/nginx/html/:/usr/local/nginx/html/"
service-nginx-web:服务名
image:镜像名
container_name:容器名
expose:开放端口
post:宿主机映射端口
volume:数据卷挂载
- 启动容器
docker-compose up -d
不加是 d 前台启动
- 启动完成
# docker ps | grep nginx 1e453106ca9c 192.168.99.21/nginx/mynginx:v1 "nginx -g 'daemon of…" 49 seconds ago Up 47 seconds 443/tcp, 0.0.0.0:80->80/tcp
- 查看容器进程
docker-compose ps
- web 访问测试
启动多个容器
docker pull 192.168.99.21/tomcat/mytomcat-app:v1
- 编辑配置文件
cd /opt/docker-compose/ vim docker-compose.yml service-nginx-web: image: 192.168.99.21/nginx/mynginx:v1 container_name: nginx-web1 expose: - 80 - 443 ports: - "81:80" - "443:443" volumes: - "/opt/dockerfile/web/nginx/html/:/usr/local/nginx/html/" service-tomcat-app1: image: 192.168.99.21/tomcat/mytomcat-app:v1 container_name: tomcat-app1 expose: - 8080 ports: - "8080:8080"
- 重启容器
docker-compose stop
docker-compose up -d
- web 访问测试
-
重启/停止/启动单个指定容器
docker-compose restart/stop/start service-nginx-web
写容器的 service 名称,则是指定。
不指定则是所有
- 重启所有容器:
docker-compose restart
实现Nginx反向代理Tomcat
创建nginx配置文件
- 创建nginx目录
mkdir /opt/app mkdir -p nginx/html/app{1..2} cd /opt/app mkdir -p nginx/conf cd nginx/conf
- 创建nginx配置文件
vim nginx.conf #user nobody; worker_processes 1; #pid logs/nginx.pid; events { worker_connections 1024; } http { include mime.types; default_type application/octet-stream; #log_format main 'remote_addr - remote_user [time_local] "request" ' # 'status body_bytes_sent "http_referer" '
# '"http_user_agent" "http_x_forwarded_for"';
# access_log logs/access.log main;
sendfile on;
#tcp_nopush on;
#keepalive_timeout 0;
keepalive_timeout 65;
#gzip on; upstream tomcat_webserver {
server service-tomcat-app1:8080;
server service-tomcat-app2:8080;
}
server {
listen 80;
server_name localhost;
#charset koi8-r;
#access_log logs/host.access.log main;
location /app1 {
root /apps/nginx/html;
index index.html index.htm;
}
location /app2 {
root /apps/nginx/html;
index index.html index.htm;
}
location /tomcat-app {
proxy_pass http://tomcat_webserver;
proxy_set_header Host host; proxy_set_header X-Forwarded-For proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP remote_addr; } #error_page 404 /404.html; # redirect server error pages to the static page /50x.html # error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } } }
- 创建网页
echo app111111 > /opt/app/nginx/html/app1/index.html echo app222222 > /opt/app/nginx/html/app2/index.html
创建haproxy配置文件
cd /opt/app mkdir -p haproxy/conf
vim ./haproxy/conf/haproxy.cfg
global chroot /usr/local/haproxy uid 99 gid 99 daemon nbproc 1 pidfile /usr/local/haproxy/run/haproxy.pid log 127.0.0.1 local3 info defaults option http-keep-alive option forwardfor mode http timeout connect 300000ms timeout client 300000ms timeout server 300000ms listen stats mode http bind 0.0.0.0:9999 stats enable log global stats uri /haproxy-status stats auth admin:123 listen web_port bind 0.0.0.0:80 mode http log global balance roundrobin server web1 nginx-web1:80 check inter 3000 fall 2 rise 5
nginx-web1:80 这里写的是容器内部的端口,所以nginx容器开放的什么端口就写多少,默认80。因为没有对宿主机映射,所以可以不会端口冲突。
nginx-web1 是容器名
准备tomcat配置文件
cd /opt/app mkdir -p tomcat/conf
vim tomcat/conf/server.xml <?xml version="1.0" encoding="UTF-8"?> <!-- Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. --> <!-- Note: A "Server" is not itself a "Container", so you may not define subcomponents such as "Valves" at this level. Documentation at /docs/config/server.html --> <Server port="8005" shutdown="SHUTDOWN"> <Listener className="org.apache.catalina.startup.VersionLoggerListener" /> <!-- Security listener. Documentation at /docs/config/listeners.html <Listener className="org.apache.catalina.security.SecurityListener" /> --> <!--APR library loader. Documentation at /docs/apr.html --> <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" /> <!-- Prevent memory leaks due to use of particular java/javax APIs--> <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" /> <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" /> <Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" /> <!-- Global JNDI resources Documentation at /docs/jndi-resources-howto.html --> <GlobalNamingResources> <!-- Editable user database that can also be used by UserDatabaseRealm to authenticate users --> <Resource name="UserDatabase" auth="Container" type="org.apache.catalina.UserDatabase" description="User database that can be updated and saved" factory="org.apache.catalina.users.MemoryUserDatabaseFactory" pathname="conf/tomcat-users.xml" /> </GlobalNamingResources> <!-- A "Service" is a collection of one or more "Connectors" that share a single "Container" Note: A "Service" is not itself a "Container", so you may not define subcomponents such as "Valves" at this level. Documentation at /docs/config/service.html --> <Service name="Catalina"> <!--The connectors can use a shared executor, you can define one or more named thread pools--> <!-- <Executor name="tomcatThreadPool" namePrefix="catalina-exec-" maxThreads="150" minSpareThreads="4"/> --> <!-- A "Connector" represents an endpoint by which requests are received and responses are returned. Documentation at : Java HTTP Connector: /docs/config/http.html Java AJP Connector: /docs/config/ajp.html APR (HTTP/AJP) Connector: /docs/apr.html Define a non-SSL/TLS HTTP/1.1 Connector on port 8080 --> <Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" /> <!-- A "Connector" using the shared thread pool--> <!-- <Connector executor="tomcatThreadPool" port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" /> --> <!-- Define a SSL/TLS HTTP/1.1 Connector on port 8443 This connector uses the NIO implementation. The default SSLImplementation will depend on the presence of the APR/native library and the useOpenSSL attribute of the AprLifecycleListener. Either JSSE or OpenSSL style configuration may be used regardless of the SSLImplementation selected. JSSE style configuration is used below. --> <!-- <Connector port="8443" protocol="org.apache.coyote.http11.Http11NioProtocol" maxThreads="150" SSLEnabled="true"> <SSLHostConfig> <Certificate certificateKeystoreFile="conf/localhost-rsa.jks" type="RSA" /> </SSLHostConfig> </Connector> --> <!-- Define a SSL/TLS HTTP/1.1 Connector on port 8443 with HTTP/2 This connector uses the APR/native implementation which always uses OpenSSL for TLS. Either JSSE or OpenSSL style configuration may be used. OpenSSL style configuration is used below. --> <!-- <Connector port="8443" protocol="org.apache.coyote.http11.Http11AprProtocol" maxThreads="150" SSLEnabled="true" > <UpgradeProtocol className="org.apache.coyote.http2.Http2Protocol" /> <SSLHostConfig> <Certificate certificateKeyFile="conf/localhost-rsa-key.pem" certificateFile="conf/localhost-rsa-cert.pem" certificateChainFile="conf/localhost-rsa-chain.pem" type="RSA" /> </SSLHostConfig> </Connector> --> <!-- Define an AJP 1.3 Connector on port 8009 --> <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" /> <!-- An Engine represents the entry point (within Catalina) that processes every request. The Engine implementation for Tomcat stand alone analyzes the HTTP headers included with the request, and passes them on to the appropriate Host (virtual host). Documentation at /docs/config/engine.html --> <!-- You should set jvmRoute to support load-balancing via AJP ie : <Engine name="Catalina" defaultHost="localhost" jvmRoute="jvm1"> --> <Engine name="Catalina" defaultHost="localhost"> <!--For clustering, please take a look at documentation at: /docs/cluster-howto.html (simple how to) /docs/config/cluster.html (reference documentation) --> <!-- <Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"/> --> <!-- Use the LockOutRealm to prevent attempts to guess user passwords via a brute-force attack --> <Realm className="org.apache.catalina.realm.LockOutRealm"> <!-- This Realm uses the UserDatabase configured in the global JNDI resources under the key "UserDatabase". Any edits that are performed against this UserDatabase are immediately available for use by the Realm. --> <Realm className="org.apache.catalina.realm.UserDatabaseRealm" resourceName="UserDatabase"/> </Realm> <Host name="localhost" appBase="/data/tomcat/webapps/app" unpackWARs="true" autoDeploy="true"> <!-- SingleSignOn valve, share authentication between web applications Documentation at: /docs/config/valve.html --> <!-- <Valve className="org.apache.catalina.authenticator.SingleSignOn" /> --> <!-- Access log processes all example. Documentation at: /docs/config/valve.html Note: The pattern used is equivalent to using pattern="common" --> <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" prefix="localhost_access_log" suffix=".txt" pattern="%h %l %u %t "%r" %s %b" /> </Host> </Engine> </Service> </Server>
- 准备tomcat动态页面
cd /opt/app mkdir -p tomcat/app1/tomcat-app cd tomcat/app1/tomcat-app
注意这里:因为nginx中位置配置的配置路径是tomcat-app,往tomcat调度的时候会带上这个路径,所以,挂载进去的路径也要与之匹配。即:要能够访问http:// tomcat -app1:8080 / tomcat-app,才能通过nginx来调度
- 动态页面示例
vim showhost.jsp <%@page import="java.util.Enumeration"%> <br /> host: <%try{out.println(""+java.net.InetAddress.getLocalHost().getHostName());}catch(Exception e){}%> <br /> remoteAddr: <%=request.getRemoteAddr()%> <br /> remoteHost: <%=request.getRemoteHost()%> <br /> sessionId: <%=request.getSession().getId()%> <br /> serverName:<%=request.getServerName()%> <br /> scheme:<%=request.getScheme()%> <br /> <%request.getSession().setAttribute("t1","t2");%> <% Enumeration en = request.getHeaderNames(); while(en.hasMoreElements()){ String hd = en.nextElement().toString(); out.println(hd+" : "+request.getHeader(hd)); out.println("<br />"); } %>
创建docker-compose.yml
mkdir -p /opt/app cd /opt/app vim docker-compose.yml service-haproxy: image: 192.168.99.21/haproxy/haproxy:v1 container_name: haproxy volumes: - ./haproxy/conf/haproxy.cfg:/etc/haproxy/haproxy.cfg expose: - 80 - 443 - 9999 ports: - "80:80" - "443:443" - "9999:9999" links: - service-nginx-web service-nginx-web: image: 192.168.99.21/nginx/mynginx:v1 container_name: nginx-web1 volumes: - ./nginx/html/app1:/apps/nginx/html/app1 - ./nginx/html/app2:/apps/nginx/html/app2 - ./nginx/conf/nginx.conf:/usr/local/nginx/conf/nginx.conf expose: - 80 - 443 links: - service-tomcat-app1 - service-tomcat-app2 service-tomcat-app1: image: 192.168.99.21/tomcat/mytomcat-app:v1 container_name: tomcat-app1 volumes: - ./tomcat/app1:/data/tomcat/webapps/app/ROOT - ./tomcat/conf/server.xml:/apps/tomcat/conf/server.xml expose: - 8080 service-tomcat-app2: image: 192.168.99.21/tomcat/mytomcat-app:v1 container_name: tomcat-app2 volumes: - ./tomcat/app1:/data/tomcat/webapps/app/ROOT - ./tomcat/conf/server.xml:/apps/tomcat/conf/server.xml expose: - 8080
最终文件目录
cd /opt/app # tree . ├── docker-compose.yml ├── haproxy │ └── conf │ └── haproxy.cfg │ ├── nginx │ ├── conf │ │ └── nginx.conf │ └── html │ ├── app1 │ │ └── index.html │ └── app2 │ └── index.html └── tomcat ├── app1 │ └── tomcat-app │ └── showhost.jsp └── conf └── server.xml
测试访问
http://192.168.99.22/tomcat-app/showhost.jsp
以上就是Docke容器大部分的使用情景及功能实现