使用docker进行容器化部署和运维近两年时间了,有必要做个总结记录。这里从零开始由浅入深,最终完成asp.net core+mysql+nginx的容器化部署。
一、常用基础命令
查看镜像
docker images
查看容器
docker ps -a
查看容器内部详细信息
docker inspect <容器id/名称>
查看容器的ip
docker inspect <容器id/名称> | grep IPAddress
启停容器
docker start/stop/restart <容器id/名称>
删除容器
docker rm <容器id/名称>
删除所有容器
docker rm $(docker ps -qa)
删除镜像
docker rmi <镜像id/名称>
docker image rm <镜像id/名称>
查看容器日志
docker logs -f -t --tail 30 <容器id/名称>
-t,加入时间戳
-f,跟随最新的日志打印
--tail 数字n,显示最后n行
进入容器
docker exec -it <容器id/名称> /bin/bash
打包镜像。
如将当前目录打包成镜像,并命名为hello
docker build -t <镜像名称> <路径>
docker build -t hello .
运行镜像。
如运行hello镜像,创建并运行容器hello-1
docker run --name <容器名称> -d -p 80:5000 <镜像名称>
docker run --name hello-1 -d -p 80:5000 hello
-d,为后台任务形式运行,
-p,指定端口映射,本地端口:容器端口
修改容器后,提交生成一个新的镜像。
如将id:9a0c3f的容器提交为新的镜像hello:v1.0.1
docker commit <容器id/名称> <新的镜像名称[:tag]>
docker commit 9a0c3f hello:v1.0.1
查询默认镜像站点的镜像。
比如查询mysql相关镜像。
docker search <镜像名称>
docker search mysql
查询mysql镜像历史版本
curl https://registry.hub.docker.com/v1/repositories/mysql/tags | tr -d '[[]" ]' | tr '}' ' ' | awk -F: -v image='mysql' '{if(NR!=NF && $3 != ""){printf("%s:%s ",image,$3)}}'
二、Docker工具、镜像、Demo项目
2.1 Docker工具
这里用到docker和docker-compose两个工具,安装过程不做赘述,仅展示测试的版本。
[app@test /]$ docker --version Docker version 19.03.4, build 9013bf583a [app@test /]$ docker-compose --version docker-compose version 1.22.0, build f46880fe
2.2 镜像
这里用到mysql,nginx,asp.net core三个镜像。
其中的mysql和nginx镜像直接通过命令 docker pull mysql和 docker pull nginx,从默认的docker镜像站点registry.hub.docker.com下载最新版本即可。
asp.net core的镜像获取方式则略有不同,2018 年5月之后,微软将所有docker image都推送到了 MCR(Miscrosoft Container Registry),官方地址https://github.com/microsoft/containerregistry。按照官方说法,目前已经对全球多区域进行负载均衡以提供对MCR目录的可靠一致性访问,换句话说,不需要设置docker仓库地址等从各种国内镜像加速去下载了,我试验了下速度的确很快。
mcr仓库列表地址:https://mcr.microsoft.com/v2/_catalog,找到asp.net core的仓库为 dotnet/core/aspnet
某个仓库内tags查询方式:
https://mcr.microsoft.com/v2/{namespace/repo}/tags/list
对应我们要查找的tag地址为:
https://mcr.microsoft.com/v2/dotnet/core/aspnet/tags/list
而mcr镜像下载的命令格式为:
docker pull mcr.microsoft.com/<namespace/repo>:<tag>
相应的我们下载asp.net core v3.1.1镜像的命令最终就是:
docker pull mcr.microsoft.com/dotnet/core/aspnet:3.1.1
当然,我们也可以直接在dockerhub进行搜索asp.net,直接获得下载命令。搜索链接为
https://hub.docker.com/search?q=asp.net&type=image
可以看到主要有三个镜像,分别为
ASP.NET,对应dotnet framework的版本镜像 docker pull mcr.microsoft.com/dotnet/framework/aspnet docker pull mcr.microsoft.com/dotnet/framework/aspnet:4.8 docker pull mcr.microsoft.com/dotnet/framework/aspnet:3.5 ASP.NET Core Runtime,对应dotnet core的版本镜像 docker pull mcr.microsoft.com/dotnet/aspnet docker pull mcr.microsoft.com/dotnet/aspnet:5.0 docker pull mcr.microsoft.com/dotnet/aspnet:3.1 ASP.NET Core 3.1 Runtime属于ASP.NET Core Runtime的一个版本
最后查看我们的镜像列表,其中 mcr.microsoft.com/dotnet/aspnet:3.1 就是我们要使用的镜像了,它与上面的 mcr.microsoft.com/dotnet/core/aspnet:3.1.1 都是.NET Core runtime,都不包括.NET Core SDK,仅仅版本有微小差别。
[app@test /]$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE nginx latest ad4c705f24d3 10 days ago 133MB mysql latest 0716d6ebcc1a 2 weeks ago 514MB mcr.microsoft.com/dotnet/core/aspnet 3.1.1 e28362768eed 19 months ago 207MB
mcr.microsoft.com/dotnet/aspnet 3.1 5d1ca8ae6edb 6 days ago 208MB
#mcr.microsoft.com/dotnet/core/aspnet:3.1.1镜像的容器内查看dotnet版本
root@490d942232fd:/app# dotnet --info It was not possible to find any installed .NET Core SDKs Did you mean to run .NET Core SDK commands? Install a .NET Core SDK from: https://aka.ms/dotnet-download Host (useful for support): Version: 3.1.1 Commit: a1388f194c .NET Core SDKs installed: No SDKs were found. .NET Core runtimes installed: Microsoft.AspNetCore.App 3.1.1 [/usr/share/dotnet/shared/Microsoft.AspNetCore.App] Microsoft.NETCore.App 3.1.1 [/usr/share/dotnet/shared/Microsoft.NETCore.App] To install additional .NET Core runtimes or SDKs: https://aka.ms/dotnet-download
#mcr.microsoft.com/dotnet/aspnet:3.1镜像的容器内查看dotnet版本
root@cc2dad7798b3:/app# dotnet --info It was not possible to find any installed .NET Core SDKs Did you mean to run .NET Core SDK commands? Install a .NET Core SDK from: https://aka.ms/dotnet-download Host (useful for support): Version: 3.1.19 Commit: aae002469c .NET Core SDKs installed: No SDKs were found. .NET Core runtimes installed: Microsoft.AspNetCore.App 3.1.19 [/usr/share/dotnet/shared/Microsoft.AspNetCore.App] Microsoft.NETCore.App 3.1.19 [/usr/share/dotnet/shared/Microsoft.NETCore.App] To install additional .NET Core runtimes or SDKs: https://aka.ms/dotnet-download
镜像仅仅包括runtime,不包括sdk,这样体积会比较小,当然 dotnet restore,dotnet run,dotnet publish 等sdk命令也就无法使用了,我们应在容器外进行 dotnet publish ,并将发布目录挂载共享到容器中构建运行。
2.3 Demo项目
编写了简单的asp.net core mvc+mysql 的测试项目,已提交https://github.com/gitsongs/docker-demo,可以直接下载使用。
git clone https://github.com/gitsongs/docker-demo.git
三、通过Dockerfile打包镜像
3.1 共享挂载卷
docker容器内部与宿主机是隔离的,如果我们运行asp.net core容器,并且每次进入容器里面部署源代码,这样很不方便,再者每次提交更新部署还要反复生成新的镜像也比较麻烦和耗时,如不提交镜像,容器一旦销毁那么容器内做的工作和数据就会丢失。而docker提供了挂载卷共享的功能,可以很好的应对这一问题。而且对于挂载卷目录或文件,对宿主机的文件修改会反应到容器内,反之对于容器内共享目录的文件修改不会影响到宿主机上,这体现了容器的隔离特性。这一功能在运维部署时可以用来持续构建(CI),基本思路是,首先通过git更新源码到宿主机上进行编译发布,然后将发布同步到挂载卷目录,最后在容器中构建运行,针对这个后续会单独写一篇文章进行详细介绍。
可以通过docker run命令中增加 -v <host目录:容器目录> 参数挂载共享卷。
docker run -it -v $HOME/docker-demo:/app mcr.microsoft.com/dotnet/core/aspnet:3.1.1
这样容器内的/app目录就对应的是宿主机的 $HOME/docker-demo 目录了。
也可以通过后续的Dockerfile 或 docker-compose.yml 进行指定。
3.2 数据挂载卷
除了上述的共享宿主机目录的挂载卷,还有数据卷。比如mysql容器,一旦容器销毁,那么我们的数据库数据就会丢失,为了应对这种情形,可以创建数据卷,然后挂载到容器中以进行容器数据的持久化,这样即便容器销毁,数据卷可以保留。
#创建数据卷 $ docker volume create --name demo.db #查找数据卷文件 $ find / -name "demo.db" /var/lib/docker/volumes/demo.db #查看数据卷信息 $ docker volume inspect demo.db [ { "Name": "demo.db", "Driver": "local", "Mountpoint": "/var/lib/docker/volumes/demo.db/_data", "Labels": {}, "Scope": "local" } ] # 挂载数据卷启动MySql实例 $ docker run --name docker-mysql -v demo.db:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 -d mysql
3.3 编辑Dockerfile
通过Dockerfile可以指定很多参数,用于打包镜像。比如可以通过挂载共享卷,将代码程序这一变量目录共享到容器中,而容器本质只是提供一个隔离的特定运行环境(下面的例子未采用这种做法)。
$cd ~/test $git clone https://github.com/gitsongs/docker-demo.git $cd docker-demo/docker-demo $dotnet publish -c Release -o ./publish $vim Dockerfile
$cat Dockerfile
FROM mcr.microsoft.com/dotnet/aspnet:3.1 WORKDIR /app COPY . /app ENV ASPNETCORE_URLS http://*:8881 ENV ASPNETCORE_ENVIRONMENT Production EXPOSE 8881 ENTRYPOINT /app/docker-netcore.start.sh
$cat docker-netcore.start.sh
#!/bin/bash
echo $(pwd) dotnet /app/publish/docker-demo.dll int=1 while(( $int<=100 )) do echo $int sleep 10s let "int++" done
这里的Dockerfile,我们指定容器使用镜像 mcr.microsoft.com/dotnet/aspnet:3.1 ,容器工作目录为/app,并将当前上下文目录即宿主机的~/test/docker-demo/docker-demo复制到容器/app目录下,指定了相关环境变量,最后指定容器入口程序为 docker-netcore.start.sh ,文件内容也很简单,就是运行docker-demo程序,在最后面还增加了一段100*10s的耗时程序,是因为入口程序执行完毕后容器就会自动停止运行,这里的耗时程序就是为了在测试过程中,避免一些因素导致docker-demo程序启动失败,容器自动停止,这样我们可以有时间通过交互模式进入容器进行一些排查操作,比如进入容器查看 dotnet --info 或者ping一下其它容器以测试是否联通。
完成以上工作后,就可以打包镜像了,这里将我们的镜像命名为 docker-netcore-image 。
docker build -t docker-netcore-image .
3.4 启动容器
接下来我们通过上面的镜像 docker-netcore-image 创建一个容器 docker-netcore 。查看项目docker-demo文件 appsettings.json ,可知项目依赖访问mysql数据库 docker-mysql ,所以要先创建一个mysql镜像的容器 docker-mysql ,而且两个容器要联通。
$ cat appsettings.json ... "ConnectionStrings": { "MySql": "server=docker-mysql;database=DemoDb;uid=root;pwd=123456;" } ...
具体操作过程如下:
# 创建容器网络,以联通两个容器 $ docker network create -d bridge docker-net-test # 查看容器网络 $ docker network ls # 创建容器docker-mysql $ docker run --name docker-mysql -d --network docker-net-test -e MYSQL_ROOT_PASSWORD=123456 mysql # 创建容器 $ docker run --name docker-netcore -d --network docker-net-test docker-netcore-image # 查看容器 $ docker ps -a # 进入容器 $ docker exec -it docker-netcore /bin/bash # 安装ping $ apt-get update $ apt install iputils-ping # 测试与docker-mysql是联通的 $ ping docker-mysql
# 一切正常,测试项目,返回json结果
$ curl localhost:8881/home/test
[{"id":1,"name":"u5F20u4E09","sex":"u7537","birthday":"2000-08...}]
四、通过docker-compose完成asp.net core+mysql+nginx容器化部署
docker-compose就像是容器的管家,用于定义和运行多容器。其使用yml文件来配置所有容器,最后使用一个命令就可以根据yml文件配置创建并启动所有容器服务。
我们的最终目的就是运行三个容器服务,使用docker-compose就会非常方便快捷,没有必要打包镜像再去运行。
配置docker-compose内容如下:
version: '2.2' services: docker-mysql-service: container_name: docker-mysql image: mysql environment: MYSQL_ROOT_PASSWORD: 123456 volumes: - ./mysql:/var/lib/mysql docker-netcore-service: container_name: docker-netcore build: . working_dir: /app depends_on: - docker-mysql-service links: - docker-mysql-service environment: - ASPNETCORE_URLS=http://0.0.0.0:8881 - ASPNETCORE_ENVIRONMENT='Production' image: mcr.microsoft.com/dotnet/aspnet:3.1 ports: - "127.0.0.1:8881:8881" volumes: - ~/test/docker-demo/docker-demo:/app - /etc/localtime:/etc/localtime entrypoint: /app/docker-netcore.start.sh reverse-proxy-service: container_name: docker-nginx image: nginx depends_on: - docker-netcore-service ports: - "5050:8080" volumes: - ./proxy.conf:/etc/nginx/conf.d/default.conf
配置nginx的proxy.conf内容如下:
server { listen 8080; location / { proxy_pass http://docker-netcore-service:8881; } }
最终操作过程:
$ docker-compose up -d Creating network "docker-demo_default" with the default driver Creating docker-mysql ... done Creating docker-netcore ... done Creating docker-nginx ... done $ docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 10be2df533b0 nginx "/docker-entrypoint.…" 15 seconds ago Up 14 seconds 80/tcp, 0.0.0.0:5050->8080/tcp docker-nginx d2699558111a mcr.microsoft.com/dotnet/aspnet:3.1 "/app/docker-netcore…" 16 seconds ago Up 15 seconds 127.0.0.1:8881->8881/tcp docker-netcore d907b9c112af mysql "docker-entrypoint.s…" 17 seconds ago Up 16 seconds 3306/tcp, 33060/tcp docker-mysql
$ curl localhost:5050/home/test [{"id":1,"name":"u5F20u4E09","sex":"u7537","birthday":"2000-08-15..."}]