Published on

docker 最佳实践

Authors
  • avatar
    Name
    MissTree
    Twitter

目录

docker镜像仓库

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之外项目选择)。