- Published on
docker 最佳实践
- Authors

- Name
- MissTree
目录
docker文件外挂载
文件外挂载(也称为数据卷挂载或绑定挂载)是一种非常有用的功能,它允许你将主机(物理机或虚拟机)上的文件或目录挂载到容器内部,使得容器可以访问和操作这些外部文件,同时也方便数据的持久化和共享(和端口映射非常像)。
在容器内部/srv/date,绑定到主机外的/usr/data , 在容器内/srv/date新增修改的文件,在主机上也会同步修改。主机上的/usr/data 文件发生变化,容器内的文件也会同步修改。这样在主机上修改对应文件不需打包到容器。而且外挂的容器在停止运行后删除都不会影响主机上的数据,方便Mysql数据库持久化数据的管理和共享。
# 具名挂载和匿名挂载
docker run -d -p 8080:8080 --name nginx_name --restart always -v /root/nginx/ nginx
# 上面的命令属于匿名挂载 ,-v 将容器内部 /root/nginx/ 目录挂载到主机 /var/lib/docker/volumes/ id字符目录。
docker run -d -P --name nginx_c --restart always -v juming_root:/root/nginx/ nginx
# 上面的命令属于具名挂载 ,-v 将容器内部 /root/nginx/ 目录挂载到主机 /var/lib/docker/volumes/juming_root 目录。
# 因为 juming_root 不是$juming_root方式的变量,而且不是绝对路径,所以会在docker内部自动创建一个目录。
# 环境变量挂载
export NGINX_ROOT=/root/nginx/ # /srv/nginx/
docker run -d -P --name nginx_c --restart always -v $NGINX_ROOT/data:/root/nginx/ nginx
# 上面的命令属于环境变量挂载 ,-v 将容器内部 /root/nginx/ 目录挂载到主机 $NGINX_ROOT 目录。
# 只读挂载 一旦设定为只读,容器将无法修改挂载的目录内容到主机。
# ro 只读 rw 读写
docker run -d -P --name nginx_c --restart always -v /root/nginx/:/root/nginx/:ro nginx
# 获取挂载目录信息
docker volume inspect juming_root # 查看juming_root挂载目录信息
# 返回信息格式
[
{
"CreatedAt":"2020-05-15T19:37:31+08:0",
"Driver":"local",
"Labels":null
"Mountpoint":"/var/lib/docker/volumes/juming_root/_data"
"Name":"juming_root
"Options":null.
"Scope":"local"
}
]
Dockerfile
就是通过Dockerfile文件来构建镜像。
- 每个保留关键字或者指令都必须是大写字母
- 指令后必须跟随至少一个参数
- 指令按照从上到下的顺序执行
- 注释以 # 开头
- 每条指令都会创建一个新的镜像层,并对镜像进行提交
# dockerfile
FROM nginx
COPY /usr/src/myapp
ENV ROOT = /usr/src/myapp
MAINTAINER mt<email@qq.com>
WORKDIR $ROOT # 生成的container默认进入工作目录
VOLUME ["java","Main"] # 挂载目录
EXPOSE 8080
RUN apt install -y vim # 启动镜像后执行的安装命令 让镜像可以使用 vim 命令
CMD echo "hello world" # 容器启动后执行的命令
Dockerfile 常用指令
- FROM:指定基础镜像,一切从此构建
- MAINTAINER:镜像维护者信息
- CMD:提供容器在启动时要运行的命令
- ENTRYPOINT:配置容器启动后追加执行的命令
- COPY:将文件从主机复制到镜像中
- ADD:将文件添加到镜像中
- ENV:设置构建时环境变量
- ARG:定义构建参数
- VOLUME:创建数据卷
- WORKDIR:设置工作目录
- USER:指定运行容器的用户
- LABEL:添加元数据信息
- EXPOSE:暴露端口
- ONBUILD:当配置镜像构建触发器
- RUN:镜像构建执行命令行命令
CMD和ENTRYPOINT的区别
FROM nginx
CMD ["ls","-al"]
# 或
ENTRYPOINT ["ls","-al"]
# 构建镜像
docker build -f Dockerfile -a image_name:[tag].
# CMD和ENTRYPOINT 定义的都是相同的命令,但是它们的执行方式是不同的。
docker run image_name ls -al # 执行的是CMD指令,只能执行定义的命令
docker run image_name l # 执行的是ENTRYPOINT指令,可以执行定义的命令,也可以执行其他命令
# 上面的作用都是展示镜像的文件目录
构建镜像 不要漏掉后面的·docker build -f Dockerfile -a image_name:[tag] .
数据卷容器

# 先定义一个路径变量 root_path=/root/nginx/
docker run -d -P --name nginx01 -v $root_path:/root/nginx/ nginx
docker run -d -P --name nginx02 --volumes-from nginx01 nginx
docker run -d -P --name nginx03 --volumes-from nginx02 nginx
# 多个容器间共享卷分两种情况
# 1. 多个容器间共享数据卷路径为相对路径,数据卷容器的生命周期一直持续到没有容器使用为止。
# 2. 多个容器间共享数据卷容器为主机上的绝对路径,那么数据卷容器的生命周期和主机一致,不会随着容器的删除而删除。
安装Jenkins
# 设置环境变量 /srv/jenkins
export JENKINS_HOME=/srv/jenkins
# 安装最新
docker pull jenkins/jenkins
docker run -d -p 6060:8080 --name jenkins_name --restart always -v $JENKINS_HOME/config:/etc/jenkins -v $JENKINS_HOME/logs:/var/log/jenkins -v $JENKINS_HOME/data:/var/opt/jenkins jenkins/jenkins
安装Sentry
# 设置环境变量 /srv/sentry
export SENTRY_HOME=/srv/sentry
# 安装最新
docker pull sentry:latest
docker run -d -p 6060:8080 --name sentry_name --restart always -v $SENTRY_HOME/config:/etc/sentry -v $SENTRY_HOME/logs:/var/log/sentry -v $SENTRY_HOME/data:/var/opt/sentry sentry
安装Nginx
# 拉取最新镜像
docker pull ngninx
# 后台运行程序
docker run -d -p 5566:80 --name nginx_name --restart always -v /root/nginx/conf:/etc/nginx -v /root/nginx/logs:/var/log/nginx -v /root/nginx/html:/usr/share/nginx/html nginx
# 虚拟机访问开发端口
curl localhost:5566
docker网络
# 网络链接初始方式
docker network ls
docker run -d -P --name nginx_01 nginx
docker run -d -P --name nginx_02 nginx
docker run -d -P --name nginx_03 --link nginx_02 nginx
# 上面只有nginx_03 可以访问nginx_02,因为已经链接了,但是nginx_01 无法访问nginx_02和nginx_03,nginx_02和nginx_03无法访问nginx_01,因为没有链接。
docker exec -it nginx_03 curl nginx_02 #不能用ping,因为nginx没有安装ping
#可以通过 docker inspect nginx_03 查看网络信息。
docker exec -it nginx_03 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.6 nginx_02 1bafbaed8f7b
172.17.0.7 27086da8d203
# 可以看到nginx_03 可以访问nginx_02,但是nginx_02 /etc/hosts下没有配置。
# 自定义网络
nginx_03只可以链接nginx_02,其他两个容器间是不可以互相访问的。可以看到 --link的方式是有局限性的,docker0问题是不支持访问链接,所以需要自定义网络互相通信。
docker network ls
NETWORK ID NAME DRIVER SCOPE
83d78155f06e bridge bridge local # docker0 是docker内部的网络
ca60db59e9d2 host host local # host 是主机的网络
d78278035f82 new_network bridge local
85d2f944b8c4 none null local
# 创建一个自定义 docker network create --help 查看帮助
docker network create --driver bridge --subnet 172.18.0.0/16 --gateway 172.18.0.0 mynet
--driver bridge # 网络驱动类型,默认为 bridge 桥接
--subnet=172.18.0.0/16 # 子网地址和掩码 16就是还可以255*255个ip,24就是255个ip
--gateway 172.18.0.0 # 网关地址 基本和上面的子网地址一样
mynet # 网络名称
# 还可以让主机自动分配
docker network create mynet02
# 查看自定义网络 会看到新的网络 mynet
docker network ls
# 启动容器绑定自定义网络
docker run -d -P --name nginx_01 --net mynet nginx
docker run -d -P --name nginx_02 --net mynet nginx
#这样两个容器就可以互相访问了。
不同的集群使用不同的网络,保证集群的安全性。但是不能实现网络之间的之间的通信,若是两个网络的容器可以互相访问,就失去了网络隔离的意义。只能设置容器对网络的访问权限。
==容器对网络的访问==
# 将网络赋予容器的权限 可以通过 docker inspect nginx_03 查看网络信息
docker network connect mynet nginx_03
redis集群 高可用、负载均衡的redis集群服务
# 创建一个自定义redis网络
docker network create redis_net
# 可以脚本遍历多个创建
docker run -d --name redis_01 -p 16371:16379 -p 6371:6379 -v /srv/redis/data:/data -v /srv/redis/conf/redis.conf:/etc/redis/redis.conf --ip:172.18.0.0 --net redis_net redis
# 集群设置
docker exec -it redis_01 /bin/sh
redis-cli --cluster create 172.18.0.1:6379 172.18.0.2:6379 172.18.0.3:6379 172.18.0.4:6379 172.18.0.5:6379 --cluster-replicas 1
set a 1
# 退出当前容器,并且关闭容器,进入新容器通过新的redis容器获取值
get a
容器内部使用docker
# 修改docker文件 将docker.sock文件的权限docker修改为root:root
cd /var/run
chmod root:root docker.sock
docker stop jenkins
docker container update -v /var/run/docker.sock:/var/run/docker.sock -v /usr/bin/docker:/user/bin/docker -v /etc/docker/daemon.json:/etc/docker/daemon.json jenkins
# 启动容器
docker start jenkins
# 进入容器
docker exec -it jenkins /bin/bash
# 查看容器内的docker版本
docker -v
后面的Jenkins 就可以使用docker命令了,然后打包项目为一个镜像,使用镜像启动容器,然后将容器打包为一个镜像,然后将镜像推送到远程仓库。更新项目就可以通过更新镜像就可以了
容器内部使用nvm
在真实项目中,前端CICD使用的是nodejs,但是nodejs的版本是非常多的,我们需要使用不同的版本,但是我们不能每次都安装一个版本,所以我们需要使用nvm来管理nodejs的版本。
# 使用 Jenkins 官方镜像作为基础镜像
FROM jenkins/jenkins:lts
# 切换到 root 用户,因为 Jenkins 镜像默认以 Jenkins 用户运行,某些操作需要 root 权限
USER root
# 安装必要的依赖
RUN apt-get update && apt-get install -y curl && apt-get install -y git && apt-get install -y build-essential
# 安装 NVM
RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash
# 设置 NVM 环境变量
ENV NVM_DIR=/var/jenkins_home/.nvm
ENV NODE_VERSION=14.17.0
# 安装 Node.js 并设置为默认版本
RUN /bin/bash -c "source $NVM_DIR/nvm.sh && nvm install $NODE_VERSION && nvm alias default $NODE_VERSION"
# 将 NVM 加入 PATH
ENV PATH=$NVM_DIR/versions/node/v$NODE_VERSION/bin:$PATH
# 切换回 Jenkins 用户
USER jenkins
# 容器启动时的命令
CMD ["jenkins", "start"]
当然也可以使用jenkins官方插件的nodejs,或者像上面的docker一样将nvm调到内部使用,建议使用插件nodejs(有限制,要在config配置对应的node版本,然后在自由风格的项目选择配置的node版本,不能使用pinpline之外项目选择)。