C++Hello world镜像制作
- 创建目录,在目录下创建c++源代码demo.c
bash
mkdir 目录
cd 目录
vim demo.c
- 创建Dockerfile
bash
#指定基础镜像
FROM centos:7
#设置版本
ENV VWESION=1.0
#替换国内源
RUN sed -i 's|^mirrorlist=|#mirrorlist=|g' /etc/yum.repos.d/CentOS-Base.repo && \
sed -i 's|^#baseurl=http://mirror.centos.org/centos|baseurl=https://mirrors.aliyun.com/centos-vault/centos|g' /etc/yum.repos.d/CentOS-Base.repo && \
yum clean all && yum makecache
#设置工作目录
WORKDIR /src
#拷贝源文件
COPY demo.c .
#安装gcc
RUN yum makecache && yum install gcc -y
#编译源文件
RUN gcc demo.c -o demo && \
rm -f demo.c && \
yum remove -y gcc
#运行可执行文件
CMD ["/src/demo"]
- 执行构建,运行镜像查看结果
bash
docker build -t cpp:v0.1 .
docker run --name test1 --rm cpp:v0.1
CMD和ENTRYPOINT
这两个指令都是在docker image中执行一条命令,但它们之间有区别。
执行一个没有调用ENTRYPOINT或者CMD的docker镜像会返回错误,一般的镜像最后都提供了CMD或者ENTRYPOINT作为入口。
操作
覆盖
在写dockerfile时,ENTRYPOINT或者CMD命令会自动覆盖之前的ENTRYPOINT或者CMD命令,用户也可以在命令中指定具体命令,如果不希望docker镜像执行的具体程序被用户执行docker run覆盖,可以使用ENTRYPOINT。
多次覆盖
- 创建一个Dockerfile,指定多个CMD
bash
FROM busybox
CMD echo "hello world"
CMD echo "hello vientiane"
- 编译运行,可以看到第一个CMD被覆盖了

参数覆盖 - 在docker run的时候指定后面的启动参数
bash
FROM busybox
CMD echo "hello world"
CMD echo "hello vientiane"
bash
docker run --name test1 --rm cpp:v0.2 echo "hello CMD"

- 但如果是ENTRYPOINT的话,那无法覆盖,除非指定参数--entrypoint


Shell和Exec
- 编写Dockerfile,执行ping命令
bash
FROM ubuntu:22.04
RUN apt-get update -y && apt install -y iputils-ping
CMD ping localhost
-
编译镜像,进入镜像里面查看,可以看到pid为1的是/bin/sh

-
但如果使用exec模式,可以看到pid为1的进程是ping而不是/bin/sh

组合
- 新建dockerfile,同时设置ENTRYPOINT和CMD
bash
FROM ubuntu:22.04
RUN apt-get update -y && apt install -y iputils-ping
ENTRYPOINT ["/bin/ping","-c","3"]
CMD ["localhost"]
- 编译镜像后启动运行,可以看到CMD的内容作为ENTRYPOINT的参数添加到了后面
bash
docker build -t cpp:v0.4 .
docker run --name test1 --rm cpp:v0.4

- 因为CMD的内容可以更换,如果我们运行的时候替换成另外一个网址,我们就可以看到它ping的是另外一个网址
bash
docker run --name test1 --rm cpp:v0.4 www.baidu.com

dockerignore
docker是C-S架构,理论上Client和Server可以不在一台机器上,在构建docker镜像的时候,需要把文件从Client发送给Server,这些要发送的文件成为欸build context。
bash
docker build -f <dockerfile> -t <dockerfilename> .
如果想要忽略掉一些传输给server短的文件,就会用到.dockerignore文件,它会将有记录的所有文件都忽略掉,不会传输给server端。
操作
- 建立dockerfile
bash
FROM centos:7
COPY ./* /
- 建立.dockerignore文件,忽略以txt为后缀的文件,在文件里输入*.txt
bash
cat .dockerignore
- 制造一些文件
bash
touch 1.txt 2.txt 3.doc 4.rtf
- 构建镜像并查看结果
bash
docker build -f ./dockerfile -t cpp:v0.6 .
docker run -it cpp:v0.6

多阶段构建
构建docker镜像有两种方式,第一种是将全部组件及其依赖库的编译,测试,打包流程都封装仅一个docker镜像中,第二种是将每个阶段分散到多个dockerfile。
单文件构建
- 创建目录,在目录下创建c++源代码demo.c
bash
mkdir 目录
cd 目录
vim demo.c
- 创建Dockerfile
bash
#指定基础镜像
FROM centos:7
#设置版本
ENV VWESION=1.0
#替换国内源
RUN sed -i 's|^mirrorlist=|#mirrorlist=|g' /etc/yum.repos.d/CentOS-Base.repo && \
sed -i 's|^#baseurl=http://mirror.centos.org/centos|baseurl=https://mirrors.aliyun.com/centos-vault/centos|g' /etc/yum.repos.d/CentOS-Base.repo && \
yum clean all && yum makecache
#设置工作目录
WORKDIR /src
#拷贝源文件
COPY demo.c .
#安装gcc
RUN yum makecache && yum install gcc -y
#编译源文件
RUN gcc demo.c -o demo && \
rm -f demo.c && \
yum remove -y gcc
#运行可执行文件
CMD ["/src/demo"]
- 执行构建,运行镜像查看结果,可以看到生成的镜像很大
bash
docker build -t cpp:v0.1 .
docker run --name test1 --rm cpp:v0.8
docker image ls | grep cpp

多阶段构建
实际上我们把test.c编译完之后,并不需要一个大的gcc编译环境,只需要一个小的运行环境镜像即可。
- 创建目录,在目录下创建c++源代码demo.c
bash
mkdir 目录
cd 目录
vim demo.c
- 创建Dockerfile
bash
#第一阶段构建
#指定基础镜像
FROM centos:7 as base
#设置版本
ENV VWESION=1.0
#替换国内源
RUN sed -i 's|^mirrorlist=|#mirrorlist=|g' /etc/yum.repos.d/CentOS-Base.repo && \
sed -i 's|^#baseurl=http://mirror.centos.org/centos|baseurl=https://mirrors.aliyun.com/centos-vault/centos|g' /etc/yum.repos.d/CentOS-Base.repo && \
yum clean all && yum makecache
#设置工作目录
WORKDIR /src
#拷贝源文件
COPY demo.c .
#安装gcc
RUN yum makecache && yum install gcc -y
#编译源文件
RUN gcc demo.c -o demo && \
rm -f demo.c && \
yum remove -y gcc
#运行可执行文件
CMD ["/src/demo"]
#第二阶段构建
FROM centos:7
#拷贝第一阶段生成的可执行程序
COPY --from=base /src/demo /src/demo
#运行可执行程序
CMD ["/src/demo"]
- 执行构建,运行镜像查看结果,可以看到生成的镜像变小了很多
bash
docker build -t cpp:v0.9 .
docker run --name test1 --rm cpp:v0.9
docker image ls | grep cpp

- 当我们使用busybox这一类更小的镜像的时候,会发现构建完之后的镜像更小了
- 创建Dockerfile
bash
#第一阶段构建
#指定基础镜像
FROM centos:7 as base
#设置版本
ENV VWESION=1.0
#替换国内源
RUN sed -i 's|^mirrorlist=|#mirrorlist=|g' /etc/yum.repos.d/CentOS-Base.repo && \
sed -i 's|^#baseurl=http://mirror.centos.org/centos|baseurl=https://mirrors.aliyun.com/centos-vault/centos|g' /etc/yum.repos.d/CentOS-Base.repo && \
yum clean all && yum makecache
#设置工作目录
WORKDIR /src
#拷贝源文件
COPY demo.c .
#安装gcc
RUN yum makecache && yum install gcc -y
#编译源文件
RUN gcc demo.c -o demo && \
rm -f demo.c && \
yum remove -y gcc
#运行可执行文件
CMD ["/src/demo"]
#第二阶段构建
FROM busybox
#拷贝第一阶段生成的可执行程序
COPY --from=base /src/demo /src/demo
#运行可执行程序
CMD ["/src/demo"]
- 执行构建,运行镜像查看结果,可以看到生成的镜像只有几MB
bash
docker build -t cpp:v0.1 .
docker run --name test1 --rm cpp:v1.0
docker image ls | grep cpp

使用缓存
在镜像构建过程中,docker会根据dockerfile指定的顺序执行每一个指令,在执行每一条指令之前,docker都会在缓存中查找是否存在可重用的镜像,如果有就使用现存的镜像,不会重复创建。
如果在构建的时候不想使用缓存,可以在docker build中使用--no-cache=true。
操作
- 改变源代码文件demo.c重新构建镜像,这里也并没有使用到缓存,所以也需要构建很久。要怎么避免这种情况呢?
bash
#第一阶段构建
#指定基础镜像
FROM centos:7 as base
#设置版本
ENV VWESION=1.0
#替换国内源
RUN sed -i 's|^mirrorlist=|#mirrorlist=|g' /etc/yum.repos.d/CentOS-Base.repo && \
sed -i 's|^#baseurl=http://mirror.centos.org/centos|baseurl=https://mirrors.aliyun.com/centos-vault/centos|g' /etc/yum.repos.d/CentOS-Base.repo && \
yum clean all && yum makecache
#设置工作目录
WORKDIR /src
#拷贝源文件
COPY demo.c .
#安装gcc
RUN yum makecache && yum install gcc -y
#编译源文件
RUN gcc demo.c -o demo && \
rm -f demo.c && \
yum remove -y gcc
#运行可执行文件
CMD ["/src/demo"]
#第二阶段构建
FROM busybox
#拷贝第一阶段生成的可执行程序
COPY --from=base /src/demo /src/demo
#运行可执行程序
CMD ["/src/demo"]
- 可以将不经常修改的内容调节到dockerfile前边,经常修改的内容放在dockerfile文件后面(这里将COPY往后挪了),这样就可以尽量多的复用缓存了。
bash
#第一阶段构建
#指定基础镜像
FROM centos:7 as base
#设置版本
ENV VWESION=1.0
#替换国内源
RUN sed -i 's|^mirrorlist=|#mirrorlist=|g' /etc/yum.repos.d/CentOS-Base.repo && \
sed -i 's|^#baseurl=http://mirror.centos.org/centos|baseurl=https://mirrors.aliyun.com/centos-vault/centos|g' /etc/yum.repos.d/CentOS-Base.repo && \
yum clean all && yum makecache
#设置工作目录
WORKDIR /src
#安装gcc
RUN yum makecache && yum install gcc -y
#拷贝源文件
COPY demo.c .
#编译源文件
RUN gcc demo.c -o demo && \
rm -f demo.c && \
yum remove -y gcc
#运行可执行文件
CMD ["/src/demo"]
#第二阶段构建
FROM busybox
#拷贝第一阶段生成的可执行程序
COPY --from=base /src/demo /src/demo
#运行可执行程序
CMD ["/src/demo"]
这里仅用了1.7s

搭建MySQL主从同步
在docker-compose.yml文件中可以使用build选项来编译镜像。
bash
service:
#格式1,指定的上下文目录是./web,在构建的时候会自动在此目录下寻找Dockerfile,构建完后,镜像命名为test/web
frontend:
image: test/web
build: ./web
#格式2,指定的上下文目录是./database,在构建的时候会自动在此目录下寻找backend.dockerfile,构建完后,镜像命名为test/database
backend:
image:test/database
build:
context: ./database
dockerfile: ./backend.dockerfile
编辑完后,就使用docker compose build来构建所有服务的镜像。
操作
创建一个一主二从的MySQL集群
- 创建目录
bash
mkdir -p ./mysqlcluster
mkdir -p ./mysqlcluster/master
mkdir -p ./mysqlcluster/slave
- 进入./mysqlcluster/master,创建主dockerfile文件名为Dockerfile-master,配置系统时区为上海
bash
FROM mysql:5.7.36
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
- 进入./mysqlcluster/slave,创建从dockerfile,文件名为Dockerfile-slave,配置系统时区为上海
bash
FROM mysql:5.7.36
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
COPY ./slave/slave.sql /docker-entrypoint-initdb.d
- 创建从库配置脚本slave.sql
bash
change master to master_host='mysql-master'
master_user='root',master_password='root',master_port=3306;
start slave;
- 进入./mysqlcluster目录,创建docker-compose.yml配置文件
bash
version: "3"
services:
# MySQL 主节点
mysql-master:
build:
context: ./
dockerfile: ./master/Dockerfile-master
image: mysqlmaster:v1.0
restart: always
container_name: mysql-master
volumes:
- ./mastervarlib:/var/lib/mysql
ports:
- 9306:3306
environment:
MYSQL_ROOT_PASSWORD: root
privileged: true
command:
- '--server-id=1'
- '--log-bin=master-bin'
- '--binlog-ignore-db=mysql'
- '--binlog_cache_size=256M'
- '--binlog_format=mixed'
- '--lower_case_table_names=1'
- '--character-set-server=utf8'
- '--collation-server=utf8_general_ci'
# MySQL 从节点1
mysql-slave:
build:
context: ./
dockerfile: ./slave/Dockerfile-slave
image: mysqlslave:v1.0
restart: always
container_name: mysql-slave
volumes:
- ./slavevarlib:/var/lib/mysql
ports:
- 9307:3306
environment:
MYSQL_ROOT_PASSWORD: root
privileged: true
command:
- '--server-id=2'
- '--relay_log=slave-relay'
- '--lower_case_table_names=1'
- '--character-set-server=utf8'
- '--collation-server=utf8_general_ci'
depends_on:
- mysql-master
# MySQL 从节点2
mysql-slave2:
build:
context: ./
dockerfile: ./slave/Dockerfile-slave
image: mysqlslave:v1.0
restart: always
container_name: mysql-slave2
volumes:
- ./slavevarlib2:/var/lib/mysql
ports:
- 9308:3306
environment:
MYSQL_ROOT_PASSWORD: root
privileged: true
command:
- '--server-id=3'
- '--relay_log=slave-relay'
- '--lower_case_table_names=1'
- '--character-set-server=utf8'
- '--collation-server=utf8_general_ci'
depends_on:
- mysql-master
- 构建镜像
bash
docker compose build

- 启动服务进行测试
bash
docker compose up

- 查看状态
bash
docker compose ps

- 连接主库,查看数据库可以看到运行正常
bash
docker exec -it mysql-master bash
- 查看数据库角色和同步状态
bash
show master status\G
- 在主库上创建数据库,连接上任意一个从库,查从库数据库

