docker,dockerfile,docker-compose及应用部署

543 阅读11分钟

docker简介

docker是一个应用容器引擎,可以让开发人员把编写好的代码和运行代码所需要的环境打包进一个容器里,通过移植容器可以避免多次搭建环境以及代码在一台机器上可以运行而到了另一台机器上因环境问题报错。

用户基于镜像来运行容器,可以把镜像和容器类比成类和对象。

docker、dockerfile与docker-compose区别

docker是一个可以基于镜像创建容器的软件

dockerfile把手工安装docer镜像的过程变成一个配置文件脚本,以后只需要运行这个文件就可获得所需环境

docker-compose用于编排容器。通过编辑docker-composer.yml配置文件,可以一个命令启动多个需要不同参数配置的容器

docker和虚拟机的区别

image.png

ubuntu下安装docker

# 更新apt
sudo apt-get update
# 安装依赖
sudo apt-get -y install apt-transport-https ca-certificates curl software-properties-common
# 安装证书
curl -fsSL https://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | sudo apt-key add -
# 更换阿里源
sudo add-apt-repository "deb [arch=amd64] https://mirrors.aliyun.com/docker-ce/linux/ubuntu $(lsb_release -cs) stable"
# 安装docker
sudo apt install docker-ce docker-compose docker-registry

镜像加速,去阿里云拿加速地址 https://cr.console.aliyun.com/cn-hangzhou/instances/mirrors

 $ sudo mkdir -p /etc/docker
 $ vim /etc/docker/daemon.json
 # 加入以下代码
 {
   "registry-mirrors": ["加速器地址"]
 }
 
 $ sudo systemctl daemon-reload
 $ sudo systemctl restart docker

kali下安装docker

curl -fsSL https://download.docker.com/linux/debian/gpg | sudo apt-key add -
echo 'deb https://download.docker.com/linux/debian stretch stable'> /etc/apt/sources.list.d/docker.list
如果显示没有权限,就运行下面这条命令
echo "deb https://download.docker.com/linux/debian stretch stable"  | sudo tee -a /etc/apt/sources.list.d/docker.list
apt-get install apt-transport-https  ca-certificates  curl  gnupg2  software-properties-common
sudo apt-get update
sudo apt install docker.io
如果报错用下面这条命令
apt-get install docker-ce
docker --version

docker基本命令

查看docker信息docker info

重启dockersystemctl restart docker

停止运行dockersystemctl stop docker

查看版本docker version

列出本地的镜像docker images=docker container ls

查找镜像,例docker search ubuntu

拉取镜像docker pull 镜像名[:版本]

删除镜像,例docker rmi ubuntu

创建交互式容器:docker run -i -t --name=name1 centos /bin/bash

-i:交互式容器 -t:tty终端 -d:守护式容器 --name:指定容器名称

查看正在运行的容器docker ps

查看运行过的容器docker ps -a

查看最后一次运行的容器docker ps -l

退出当前容器:exit

创建一个容器,但是不运行docker create --name 容器名 镜像名

创建守护式容器(不会自动登录进去):docker run -d --name=运行后的名字 镜像名字

登录进去docker exec -it 正在运行的容器名字 /bin/bash

退出容器,但不关闭ctrl+p+q

创建容器并登录进去:docker run -it --name=运行后的名字 镜像名字 /bin/bash

也可以用docker attach 容器名

attach不会在容器中创建进程执行额外的命令,只是附着到容器上

exec会在运行的容器上创建进程执行新的命令。

停止运行容器:docker stop 容器名字

开启已有的容器:docker start 容器名字

docker start 的对象是一个容器

docker run 的对象是一个镜像

查看容器的详细信息:docker inspect 容器名

查看容器运行日志docker logs 容器名

删除容器:docker rm 容器名称/容器id,注意不能删除正在运行的容器

文件拷贝: docker cp 待拷贝的文件或目录 容器名称:容器目录 docker cp 容器名称:容器目录 带拷贝的文件或目录

目录挂载 在创建容器的时候,将宿主机的目录与容器的目录进行映射,这样就可以通过修改宿主机的某个目录文件去影响容器 docker run -di --name=自定义容器名 -v /opt/:/usr/local/myhtml 镜像名

":"前面的目录是宿主机目录,后面的目录是容器内目录。

容器打包

可以通过scp命令将打包的镜像上传到其他服务器(机器) scp xx.tar 其他服务器ip:/root/xx.tar

  • export容器打包(但最好还是用save存储打包) 1、容器打包:docker export <容器ID/名称> -o 压缩包名.tar #导出容器里的所有文件 2、导入容器:docker import xx.tar mytomcat:mycersion

  • save打包:

    1.打包docker save <容器ID/名称> -o /root/xx.tar 新镜像名字

    2.导入镜像:docker load -i /root/xx.tar之后会产生一个新的镜像,之后正常使用

导出后再导入(exported-imported)的镜像会丢失所有的历史,而保存后再加载(saveed-loaded)的镜像没有丢失历史和层(layer)。

这意味着使用导出后再导入的方式,你将无法回滚到之前的层(layer),同时,使用保存后再加载的方式持久化整个镜像,就可以做到层回滚来回滚之前的层)。

上传dockerhub

制作镜像docker commit 容器名 想要创建的镜像名:版本号

登录dockerhub,创建一个仓库

本地登录账户docker login

本地连接镜像和仓库docker tag 创建的镜像名:版本号 dockerhub用户名/仓库名:版本号

push镜像,docker push dockerhub用户名/仓库名:版本号

搭建私有镜像仓库

私有仓库本身也是一个镜像

拉取私有仓库镜像docker pull registry

创建docker run -d --name=registry -p 5000:5000 registry

打开浏览器输入http://自己ip:5000/v2/_catalog,看到{"repositories":[]},表示私有仓库搭建成功并且内容为空

修改/etc/docker/daemon.json,让docker信任私有仓库。添加{"insecure-registries":["自己ip:5000"]}

重启dockersystemctl restart docker

打标签docker tag 本地镜像名:版本 ip:5000/自己设定的镜像名:版本

上传docker push ip:5000/自己设定的镜像名:版本

拉取镜像docker pull 镜像名字

Dockerfile

Dockerfile使用基于DSL语法的指令来构建一个docker镜像,之后使用docker build命令来构建一个新的镜像

再次强调Dockerfile的作用就是用来制作镜像。

Dockerfile语法

| FROM      构建新镜像基于的基础镜像 
| LABEL     标签
| RUN       构建镜像时运行的Shell命令                 
| COPY      拷贝文件或目录到镜像中                     
| ADD       解压压缩包并拷贝                        
| ENV       设置环境变量                             
| USER      为RUN、CMD和ENTRYPOINT执行命令指定运行用户   
| EXPOSE    声明容器运行的服务端口                         
| WORKDIR   为RUN、CMD、ENTRYPOINT、COPYADD设置工作目录   
| CMD       运行容器时默认执行,如果有多个CMD指令,最后一个生效 

构建镜像步骤 1、创建一个目录 2、在目录下创建Dockerfile文件以及其他文件 3、通过docker build构建镜像 4、通过构建的镜像启动容器

示例1

在lab目录下新建两个文件,sources.listDockerfile

sources.list文件中存放镜像源的链接,Dockerfile文件内容如下

#获取版本号为latest的ubuntu镜像
FROM ubuntu:latest
​
#修改apt的镜像源地址
COPY ./sources.list /tmp/
RUN cat /tmp/sources.list > /etc/apt/sources.list  && rm -f /tmp/sources.list
​
#更新本地缓存包
RUN apt-get update && apt-get install -y python3 

执行命令进行打包成镜像

docker build -t lab:v1 .

示例2,创建一个python项目

在目录python-demo新建两个文件,index.pyDockerfile

# index.py
name = input("请输入您的姓名:")
print(name)
# Dockerfile
FROM python:3.10.0-bullseye
WORKDIR /usr/src/app
COPY . .
CMD [ "python", "./index.py" ]
docker build -t python-demo:v1 .
docker run -it --rm --name python-demo-1 python-demo:v1 

示例3,nodejs的包管理,创建如下文件

node-demo
├── Dockerfile
└── package.json
└── index.js
└── .dockerignore
// package.json
{
"name": "TKE express demo",
"description": "...",
"version": "1.0",
"main": "index.js",
"scripts": {
 "start": "node index.js"
},
"dependencies": {
 "express": "^4.17.1"
}
}
// index.js
const express = require('express')
const app = express()
const port = 3000
​
app.get('/', (req, res) => {
res.send('hello~')
})
​
app.listen(port, () => {
console.log(`open port: http://localhost:${port}`)
})
# Dockerfile
FROM node:17-slim
WORKDIR /usr/src/app
COPY package.json ./
RUN npm install --only=production
COPY . ./
CMD [ "npm", "start"]
docker build -t node-demo:v1 .
docker run -dp 3005:3000 --name node-demo-1 node-demo:v1

然后打开网页localhost:3005就能访问网页。另外输入命令docker logs node-demo-1查看日志信息

.dockerignore就是在Dockerfile执行COPY指令时,设置不想把一些文件或文件夹添加到镜像里。

比如node_modules,因为容器在使用npm install安装包时也会生成node_modules,或者一些配置文件等等,可以把这些需要排除在外的文件或文件夹写进.dockerignore里,比如在.dockerignore里输入以下代码

# .dockerignore
node_modules
.env

docker部署示例1

部署todo项目

部署todo项目

https://hackweek-1251009918.cos.ap-shanghai.myqcloud.com/tke/d/getting-started-master.zip

这里只使用app目录下的文件,在其下新建Dockerfile

FROM node:12-alpine
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories
RUN apk add --no-cache python3 g++ make
RUN yarn config set registry http://mirrors.cloud.tencent.com/npm/
WORKDIR /app
COPY . .
RUN yarn install --production
CMD ["node", "src/index.js"]
docker build -t tke-todo:v1 .
docker run -dp 5001:3000 tke-todo

访问网站可以添加一些待办事项,如果要做到容器共享和持久化存储,这就需要用到存储卷Volumne

存储卷Volume

存储卷的生命周期不依赖于单个容器,它可以用来存储和共享应用产生的一些“动态”文件,比如数据库中的数据、日志、用户上传的文件、数据处理产生的数据等等。

对存储卷的管理命令

docker volume create todo-db   #创建一个名为todo-db的存储卷
docker volume ls               #列出所有存储卷
docker volume inspect todo-db  #查看存储卷的信息
docker volume --help           #了解dokcer volume的所有指令

与容器的挂载

在创建容器时使用-v将存储卷挂载到容器的某个文件树,比如以下是将todo-db挂载到容器的/etc/todos

docker run -dp 5002:3000 -v todo-db:/etc/todos tke-todo

访问5002端口所在的应用创建一些待办后,删除容器,然后创建一个新的容器

docker run -dp 5003:3000 -v todo-db:/etc/todos tke-todo

访问5003端口所在的应用发现数据仍然保留

docker部署示例2

搭建wordpress与数据库

使用docker创建mysql

如果是基于数据库镜像创建了一个容器,数据库存储的数据也仍然会在容器销毁的情况下清空数据,因此需要使用存储卷来保存数据

docker volume create wordpress-mysql-data

创建一个名称为tke-mysql的容器,并配置数据库的环境变量(设置root的密码为tketke,创建的初始数据库为wordpress)以及将wordpress-mysql-data:存储卷挂载到/var/lib/mysql(因为wordpress用的mysql的路径就是这个)

sudo docker run -d \
    --name tke-mysql \
    -v wordpress-mysql-data:/var/lib/mysql \
    -e MYSQL_ROOT_PASSWORD=tketke \
    -e MYSQL_DATABASE=wordpress \
    mysql:5.7

登录到正在运行的数据库容器,并查看数据库信息

docker exec -it tke-mysql mysql -u root -p 
show databases;

docker创建桥接网络

两个容器需要通信,因此先创建一个名为wordpressnet的网络

docker network create wordpressnet # 默认创建bridge类型的网络
docker network --help
docker network ls

将之前创建好的tke-mysql桥接到wordpressnet网络

docker network connect wordpressnet tke-mysql

还需要知道正在运行的MySQL容器的IP地址,我们可以在在终端中输入以下命令查看容器的详细信息

docker inspect tke-mysql

NetworkSettings下的网络Networks里找到wordpressnet,并找到它的IP地址IPAddress,也可以直接在终端中输入以下内容就可以返回IP地址了

docker inspect -f '{{ $network := index .NetworkSettings.Networks "wordpressnet" }}{{ $network.IPAddress}}' tke-mysql

docker搭建wordpress

启动一个wordpress,这里创建的wordpress_data挂载卷会自动创建

sudo docker run -dp 8008:80 \
    --name tke-wordpress \
    --network wordpressnet \
    -v wordpress_data:/var/www/html \
    -e WORDPRESS_DB_HOST=172.18.0.2:3306 \
    -e WORDPRESS_DB_USER=root \
    -e WORDPRESS_DB_PASSWORD=tketke \
    -e WORDPRESS_DB_NAME=wordpress \
    wordpress

docker-compose.yml

如果只有一个docker镜像作为服务的话,只需要做好这一个镜像,然后用docker的run命令启动起来就可以了。但是如果是一个很大的服务的话,需要启动好几个容器来共同协作,这时如果手动docker run来启动好几个容器就显得比较繁琐,这时就可以用docker-compose.yml来规定从那些镜像开启容器,同时映射端口即其他的一些内容。

docker-compose命令

docker-compose up  启动服务
docker-compose up -d  后台启动服务
docker-compose down   卸载服务(容器也会被删除)
docker-compose logs  当后台启动服务的时候,可以使用这种方式查看日志

示例:搭建lamp

注意这里用docker-compose.yml只开启一个镜像的容器,其实可以开很多不同的镜像。

创建一个文件夹lab2并进入

添加index.php

<?php
    echo "hello world~";
?>

编写Dockerfile文件

FROM tutum/lamp 
# 换源
RUN sed -i "s/http://archive.ubuntu.com/http://mirrors.aliyun.com/g" /etc/apt/sources.list 
RUN apt-get update -y
# 删除环境原本的一堆东西
RUN rm /var/www/html/*  
# 源码copy进去目录,可自行修改
COPY ./index.php /var/www/html/
WORKDIR /var/www/html/
RUN chown www-data:www-data /var/www/html/* -R
RUN chmod -R 755 /var/www/html/
RUN service apache2 restart
EXPOSE 80
CMD ["apachectl", "-DFOREGROUND"]

执行docker build -t myhello .成功创建myhello:latest镜像

同目录下编写docker-compose.yml

version: "3"
services:
  web:
    image: "myhello:latest"
    container_name: "myweb"
    ports:
      - "80:80"

开启容器sudo docker-compose up -d

之后可以从物理机访问虚拟机ip的80端口。

关闭容器sudo docker-compose down

可以通过sudo docker inspect myweb1查看此容器的一些配置信息。

dockerfile中的entrypoint和cmd

两个结论

  • 实际上docker容器进程的完整启动参数为ENTRYPOINT CMD,如果没有指定ENTRYPOINT,docker会提供一个隐式的值/bin/sh -c
  • docker run后面跟的容器启动参数仅会覆盖CMD部分

exec模式与shell模式

CMD和ENTRYPOINT两个命令均支持exec模式和shell模式

  • exec模式格式为ENTRYPOINT ["param1", "param2"]CMD ["param1", "param2"]
  • shell模式格式为ENTRYPOINT param1, param2CMD param1, param2

如果需要容器进程处理外部信号的情况下,shell模式下信号实际上时发送给了sh,而不是容器中的应用进程。

因此比较推荐使用exec模式,shell模式实际使用较少。

CMD

  • CMD [“param1”, “param2”] 为ENTRYPOINT提供默认参数,需要指定ENTRYPOINT
  • CMD [“executable”,”param1”,”param2”] exec模式
  • CMD command param1 param2 shell模式

CMD为容器提供默认的启动命令,如果在启动容器时通过命令行指定了的启动参数,则该启动参数会覆盖CMD默认的启动参数。

ENTRYPOINT

  • ENTRYPOINT [“executable”, “param1”, “param2”] exec模式
  • ENTRYPOINT command param1 param2 shell模式

不能被docker run增加的参数覆盖,启动时要执行ENTRYPOINT的参数。

ENTRYPOINT-exec

当为exec模式时,容器启动时,在命令行上添加的参数会被追加到ENTRYPOINT的参数列表中。

例如:

FROM ubuntu:latest
ENTRYPOINT [ "echo", "hello" ]

执行docker run --rm 0d89e8d4425a world,会输出hello world

ENTRYPOINT-shell

当ENTRYPOINT为shell模式时,docker run启动后追加的参数会被忽略。

例如:

FROM ubuntu:latest
​
ENTRYPOINT echo hello

执行docker run --rm 0841e19b4d2e world仅输出hello

ENTRYPOINT命令的覆盖

ENTRYPOINT的命令可以通过docker run中增加--entrypoint选项来使用命令行中指定的参数覆盖ENTRYPOINT的参数。

ENTRYPOINT与CMD的组合使用

当同时指定CMD和ENTRYPOINT模式时,实际上为ENTRYPOINT CMD

FROM ubuntu:latestENTRYPOINT [ "echo", "hello" ]
CMD [ "world" ]

docker run --rm 7edf658370d9会输出hello world,而docker run --rm 7edf658370d9 kitty会输出hello kitty

如何查看ENTRYPOINT和CMD

可以通过docker history ${image} --no-trunc来查生成镜像的所有Dockerfile命令

腾讯云容器服务EKS

腾讯云容器服务TKE就是基于Kubernetes的容器管理服务,而且也能提供软件运行所需要的单台、几台、乃至数千台的线上服务器。这里选择创建弹性集群EKS,相比TKE对初学者来说,EKS功能更加简单,但也完美兼容原生的Kubernetes,价格也更加便宜,支持按量付费。

创建私有网络和子网

在创建弹性集群EKS前,需要先创建私有网络。而在创建私有网络时,需要先选择好服务器所在的地域和可用区。

一个私有网络VPC可以同时拥有多个子网(默认配置为100个),相同私有网络下不同子网默认内网互通。

尽量给不同的私有网络规划不同的网段,确保相同私有网络的子网网段不同。

image.png

创建EKS弹性集群

弹性容器服务(Elastic Kubernetes Service,EKS)是腾讯云容器服务推出的无须用户购买节点即可部署工作负载的服务模式。

使用容器服务时,需要先创建集群,容器服务运行在集群中。一个集群由若干节点(云服务器)构成,可运行多个容器服务。

点击左侧菜单的【容器服务】-【集群】-【创建】,在打开的【创建弹性集群】页面

  • 集群名称可以任意输入,比如tke-start
  • Kubernetes版本建议选择最新版
  • 所在地域要和前面创建的私有网络VPC是同一个地域,这样【集群网络】就可以下拉选择之前创建好的私有网络VPC了
  • Service CIDR默认即可

使用EKS快速部署应用

在大多数情况下,会使用工作负载如Deployment来创建Pod

Deployment声明了Pod的模板和控制Pod的运行策略,适用于部署无状态的应用程序。

创建好EKS弹性集群后,就可以在弹性集群列表中点击创建好的集群ID/名称,进入到该集群的控制管理页面。

点击左侧菜单【工作负载】-【Deployment】,点击【新建】进入【新建Deployment】页面。

注意这里是通过图形化的方式创建一个Deployment,但一般都是通过kubectl apply -f a.yaml的方式来部署一个Deployment,具体操作见下一篇文章

  • 工作负载名: 可以与应用、项目名相关,比如接下来要快速部署nginx,这里可以输入nginx
  • 标签: 是一个键值对(Key-Value),用于对资源进行分类管理,这里可以先输入nginx
  • 命名空间: 可以根据实际需要创建和选择,这里可以先选择default;
  • 类型: 选择Deployment(可扩展的部署Pod);
  • 数据卷: 可以先不填;
  • 安全组: 可以先选择默认安全组放通全部端口
  • 实例内容器: 名称填容器的名称,比如nginx,镜像则点击右侧的【选择镜像】,在【Docker Hub镜像】标签页,选择“nginx”镜像即可;镜像版本(Tag)可以点击右侧的“选择镜像版本”来进行选择,比如latest;环境变量,可以先不填;CPU/内存限制,可以先默认,或者尽可能填小一点,节约成本;
  • 实例数量,镜像访问凭证,都可以先默认,即“手动调节”,数量也为“1”;镜像访问凭证可以选择“qcloudregistrykey”;
  • 访问设置(Service): Service,勾选启用;服务访问方式选择“公网LB访问”;IP版本可以选择“IPv4”;负载均衡器,选择“自动创建”;端口映射,协议选择“TCP”,容器端口,填写“80”,服务端口,也填写“80”;

点击左侧菜单的【服务与路由】下的【Service】,就能够看到公网LB的IP地址,复制这个IP地址到浏览器,就能访问Nginx页面了。当能看到Nginx页面时,就说明应用就创建成功了。