之前写过一篇关于Docker的文章,回头看了一眼自己差点没有看明白...最近有时间又仔细研究了一遍(主要是生产环境真的要用到了...),顺便从0学习了一下Linux,踩了不少坑。所以准备再写几篇关于Docker的文章。希望对大家有所帮助。
操作系统为Centos7。项目为asp .netcore webapp。先简单介绍下Docker的安装
更新yum包
sudo yum update
安装需要的软件包, yum-util 提供yum-config-manager功能
sudo yum install -y yum-utils device-mapper-persistent-data lvm2
设置Docker的yum源
sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
安装Docker 可以用【:版本号】加在后面安装指定的版本,不加就安装最新版本。安装的docker ce,还有个docker ee,是收费版的
sudo yum install docker-ce -y
启动docker
sudo systemctl start docker
使docker服务自动启动
sudo systemctl enable docker
配置镜像加速
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": ["https://registry.docker-cn.com"]
}
EOF
重启docker以使用新的镜像地址
sudo systemctl daemon-reload
sudo systemctl restart docker
这里就按照官方默认的文件及目录结构来构建镜像和容器,方便了解每个命令的含义
假设我的项目名称为:myapp1,Dockerfile文件名称为:Dockerfile_myapp1
目录结构如下
/mnt/vda1/code/myapp1
/mnt/vda1/docker/Dockerfile_myapp1
那么构建一个镜像的命令为:
docker build [选项] <上下文路径/URL/->
sudo docker build -t myapp1:v1 -f /mnt/vda1/docker/Dockerfile_myapp1 /mnt/vda1/code/myapp1
-t 指定image的名称和版本,不加:v1 则默认版本为latest
-f 指定Dockerfile文件地址
/mnt/vda1/code/myapp1为镜像构建的**上下文路径**。所谓的上下文就是说在Dokerfile中可以操作的宿主机器的根路径,超出该路径的文件容器中是访问不到的
docker run -d -p 8001:80 myapp1:v1 --myapp2uri=192.168.3.102 --myapp3uri=myapp3
前面的内容很容易理解,Dockerfile文件照葫芦画瓢也可以写一个能用的,但是一旦涉及到功能需求的变化(比如说做自动构建和发布)就头大了。下面我给Dockerfile中常用到的每一个指令结合上面的项目来做一个详细的介绍
#FROM指定基础镜像,也就是说后面的的WorkDir,Run命令都是在这个镜像的基础上执行的
#使用sdk2.2执行项目发布 "AS"可以给该镜像起一个别名,可以为build也可以叫做build1,build2 。。。
FROM mcr.microsoft.com/dotnet/core/sdk:2.2 AS build
#WORKDIR设置容器内的工作目录。该命令之后的命令都已该目录为根目录进行相关的操作
#当容器启动之后,进入容器会首先进入该目录,容器的根目录为“/“。
WORKDIR /app1
#COPY将宿主机的文件拷贝到容器中去
#第一个“./”为上下文的根目录,上下文的定义在上面的镜像构建中提到过,第二个“./”等于/app1(工作目录)
#此命令将宿主的机的/mnt/vda1/code/myapp1中的所有文件拷贝到容器内的 /app1文件夹下
COPY ./ ./
#RUN 执行命令行命令
#生成项目。这里的dotnet命令使用的是上面的sdk:2.2中的dotnet命令
RUN dotnet build
#发布项目的Release版本到publish文件夹下
#该命令会在sdk2.2生成的容器中的/app1文件夹下执行
RUN dotnet publish -c Release -o publish
#使用runtime2.2运行项目
FROM mcr.microsoft.com/dotnet/core/aspnet:2.2 AS runtime
#设置容器内的工作目录
WORKDIR /app2
#生命准备使用的端口
EXPOSE 80
#--from=build指定这条命令的上下文是build容器,“./”等于/app2
COPY --from=build /app1/publish ./
#设置容器内的时区,如果不设置,默认时区是标准时间比北京时间晚8个小时
RUN echo "Asia/shanghai" > /etc/timezone
RUN cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
#程序入口点。这里的dotnet命令使用的是上面的aspnet:2.2中的dotnet命令
#该指令的含义是在容器启动时执行dotnet DockerWeb1.dll命令
#该数组后面还可以继续追加需要的参数,但是为了扩展性及安全性,我们把其余的变量在运行时指定或者在编排工具中指定
ENTRYPOINT ["dotnet", "myapp1.dll"]
#CMD命令同样可以实现ENTERPOINT的功能
#CMD ["dotnet", "myapp1.dll"]
上面的CMD命令被注释掉了,因为CMD很容易被运行时替换掉,拿上面的启动容器的命令来举例:
如果Dockerfile中使用的是CMD而非ENTRYPOINT,那么启动容器的命令就需要写为:
docker run -d -p 8001:80 myapp:v1 dotnet myapp1.dll --myapp2uri=192.168.3.102 --myapp3uri=myapp3
这是因为在myapp:v1后面的命令全是cmd命令,会替换掉Dockerfile中的cmd命令。
不过在执行CMD命令之前会先执行EnterPoint命令。所以实际的执行顺序为:
docker run -d -p 8001:80 myapp:v1 [ENTERPOINT] [CMD]
当然ENTERPOINT也可以被替换,但是并没有替换的必要,因为我们上面的Enterpoint只是指定了一个启动项而已
Dockerfile中的每一个命令都会生成一层镜像,注意是每一个,有时候还会生成多个。。。这个我还没搞明白。 所以上面的一个Dokerfile会生成十四五个image,有一些无用了会被删掉,还有一些会作为中间镜像以的名称存在于image中,可以执行docker images -a命令查看 目前还没有找到自动删除中间层的方法,可以用这个命令进行清理:docker rmi $(docker images --filter dangling=true -q)
总是通过那么大一长串命令启动容器和构建镜像实在很麻烦,用docker-compose来管理容器和镜像就会方便很多。
明白了Docker,Docker-compose就容易理解多了。这里只是简单贴一个DockerCompose的配置文件
version: "3"
services:
myapp1:
#build image的相关操作
build:
context: /mnt/vda1/code/myapp1
dockerfile: /mnt/vda1/docker/Dockerfile_myapp1
image: myapp1
restart: always
#设置容器名称
container_name: myapp1container
ports:
- "8001:80"
#设置文件夹挂载
volumes:
- /mnt/vda1/data/influxexcel:/app/wwwroot/Excels
myapp2:
build:
context: /mnt/vda1/code/myapp2
dockerfile: /mnt/vda1/docker/Dockerfile_myapp2
image: myapp2
restart: always
container_name: myapp2container
#CMD参数
command: --ApiUrl1=myapp1container
ports:
- "8002:80"
可能报错:Resource temporarily unavailable
修改 sysctl -w net.core.somaxconn=32768 立即生效
vi /etc/sysctl.conf 增加一行 net.core.somaxconn= 32768 sysctl -p 重启生效