项目容器化改造与devops实践

🐳项目容器化改造与devops实践

本文记录了笔者在项目中使用docker和jenkins的实践,涉及的代码均为简单示例,不作为教程参考。

🐋容器化改造

云原生时代,为了提高应用的拓展性和伸缩性、简化部署流程,对传统项目进行容器化改造已经成为了大势所趋。而在容器领域,除了Docker,其实还有Podman、Containerd、LXD等值得考虑的选择,但作为容器领域的先行者,Docker目前已经构建起了相当丰富的生态。在此情况下,我打算在我们的项目中接入Docker。

📷镜像打包

网上关于go应用的docker部署方案有很多种,但大体上可以分为两类:

  1. 在容器内对代码进行编译,然后运行应用。
  2. 在容器外对代码进行编译,仅在容器内运行编译好的可执行文件。

前者在进行镜像打包的时候,需要以包含go运行时的镜像为基础,进行构建,打包出来的镜像相对会比较大。而后者在进行镜像打包的时候,只需要选择一个尽可能轻量的linux镜像即可,比如alpine。

以下是一份简单的dockerfile示例,beta是我打包好的可执行文件的名称。

bash 复制代码
FROM alpine:latest
ENV TZ Asia/Shanghai
WORKDIR /app
COPY ./config .
COPY ./log .
RUN chmod +x /app/beta
VOLUME ["/app/config/","/app/log/"]
EXPOSE 8080
CMD ["/app/beta"]

在这个环节中有一个需要注意的点:确定好应用在运行时所依赖的目录结构与相关文件,比如此处的config和log目录,在镜像打包阶段,需要将这些目录与相关文件一并复制到镜像中。

然后,使用docker build指令进行进行镜像的构建。

erlang 复制代码
docker build -t myapp1:v1 .

🎹容器编排

在评估了项目目前的业务模块数量和应用发布需求后,我发现我们暂时还用不上K8S等高级的容器编排工具。为了保证应用能够简单且高效地进行发布,降低维护成本,我们决定使用docker-compose。

以下是一份简单的docker-compose.yml示例。

yaml 复制代码
version: '3'
services:
  # 数据库
  mariadb:
    image: circleci/mariadb
    container_name: mariadb
    command: --default-authentication-plugin=mysql_native_password
    environment:
      MARIADB_ROOT_PASSWORD: 456123
      MYSQL_ROOT_HOST: '%'
      MYSQL_USER: test
      MYSQL_PASSWORD: 456123
      TIME_ZONE: Asia/Shanghai
    privileged: true
    volumes:
      - ./db/data:/var/lib/mysql
      - ./db/log:/var/log/mysql
      - ./db/conf:/etc/mysql
      - /usr/share/zoneinfo/Asia/Shanghai:/etc/localtime:ro # 时区配置
    restart: always
    networks:
      - my-net
    ports:
      - 3306:3306
​
  # 项目的各个应用模块
  myapp1:
    image: myapp1:v1
    container_name: myapp1
    restart: always
    ports:
      - "8000:8000"
    environment:
      - TZ=Asia/Shanghai
    volumes:
      - /usr/share/zoneinfo:/usr/share/zoneinfo
    extra_hosts:
      - "host.docker.internal:host-gateway"
    depends_on:
      - mariadb
    networks:
      - my-net
​
  myapp2:
    image: myapp2:v1
    container_name: myapp2
    restart: always
    ports:
      - "9000:9000"
    environment:
      - TZ=Asia/Shanghai
    volumes:
      - /usr/share/zoneinfo:/usr/share/zoneinfo
    extra_hosts:
      - "host.docker.internal:host-gateway"
    depends_on:
      - mariadb
    networks:
      - my-net
​
networks:
  my-net:
    driver: bridge
​

这里我们需要注意几个点:

  1. 根据实际的场景需要,选择好容器间的网络连接类型。这里我们选择了桥接网络,并配置了extra_hosts确保容器内部可以正常地与宿主机进行连通。
  2. 通过挂载宿主机的/usr/share/zoneinfo目录,确保go应用在alpine镜像中运行时,不会出现时区问题。具体可参考这篇文章: 容器化Go应用--基础镜像的未知时区问题 (zhihu.com)
  3. 使用environment和command指令,在运行容器的时候,进行一些账户初始化、配置初始化等操作。
  4. 在进行目录挂载的时候,要根据dockerfile中通过volume指令定义好的路径进行配置,避免因挂载路径不存在导致的容器启动错误。

💾存储改造

随着项目接入容器,伴随而来的一个问题是:由于各模块已经通过容器进行了隔离,原来将文件上传到指定文件夹的存储方案已经失效。在此情况下,我参考了一些go相关的文件存储方案,MINIO、seaweedfs、caddy,发现比较符合业务需求且配备了官方sdk的就只有MINIO。它是一个高性能的分布式对象存储解决方案,而且自带了文件的版本管理功能。

🚴devops

至此,我们便可以通过 docker-compose up -d 指令轻松地部署起我们的应用以及项目所依赖的数据库、中间件。

对于上线部署来说,做到这里已经是蛮不错的了。但考虑到我们日常开发过程中,需要快速迭代,进行效果展示,仅仅接入容器,我感觉仍然没办法很好地提高我们的开发效率。于是我打算更进一步,接入devops工具,打通开发到部署的"最后一公里"。

在工具选择中,选择了比较成熟的jenkins,但由于它是使用java编写的,不管是裸机安装还是通过容器进行部署,都需要搭配JDK,比较占用内存资源。

📧配置代码仓库的webhook

通过在代码仓库中设置webhook,即可实现,每当仓库中出现代码更新,就会提醒jenkins进行应用构建。不过在实际生产过程中,正式环境需要确保应用的稳定性,且需要对应用进行版本管理,所以仅仅建议在测试环境中接入webhook功能。

🚝编写shell脚本

以下是一个简单的在jenkins中的shell示例。

bash 复制代码
# 切换到指定路径
cd /home/myapp
​
#清除本地改动
git checkout .
# 拉取最新代码
git pull origin master
​
# 配置go参数并编译
export GO111MODULE=on
# 配置go代理
export GOPROXY=https://goproxy.cn
go env -w GOOS=linux
go build -o beta .
​
# 停止并删除旧容器
docker stop myapp1 && docker rm myapp1
​
# 删除旧镜像
docker rmi myapp1:v1
​
# 构建新镜像
docker build -t myapp1:v1 .
​
# 通过docker-compose启动新容器
docker-compose up -d

在这里,我们通过shell脚本来控制容器的构建。

至此,我们实现了docker+jenkins的部署方案。

💡关于容器的思考

其实刚开始的时候,团队关于接入容器始终是保持一个试探性的态度。为什么呢?就拿最简单的数据库来说,比如我运行一个mysql,如果在运行期间出了问题,我们没有办法立刻定位到相关的文件路径去进行错误排查或修复。虽然我们可以进行目录挂载,但大多的时候还是只能"docker exec"进入容器内,然后再进行排查(且容器只有在正常运行的状态下才能进入,否则只能"docker inspect"去定位容器目录在宿主机中的具体位置)。所以对于运维人员来说,相比传统的部署方式,使用容器只能说在某些方面是方便的,但又会在另一些方面带来不必要的麻烦。

但就像文章开头所说的,容器化始终是大势所趋,谈到云原生,基本也离不开容器。作为开发者,我认为我们还是需要拥抱容器,在实践中找到比较适合自己项目的部署流程。另外,对于个人开发者而言,我始终相信,容器是学习各类新工具的不二选择。

相关推荐
开心就好20251 天前
不同阶段的 iOS 应用混淆工具怎么组合使用,源码混淆、IPA混淆
后端·ios
架构师沉默1 天前
程序员如何避免猝死?
java·后端·架构
椰奶燕麦1 天前
Windows PackageManager (winget) 核心故障排错与通用修复指南
后端
zjjsctcdl1 天前
springBoot发布https服务及调用
spring boot·后端·https
zdl6861 天前
Spring Boot文件上传
java·spring boot·后端
世界哪有真情1 天前
哇!绝了!原来这么简单!我的 Java 项目代码终于被 “拯救” 了!
java·后端
RMB Player1 天前
Spring Boot 集成飞书推送超详细教程:文本消息、签名校验、封装工具类一篇搞定
java·网络·spring boot·后端·spring·飞书
重庆小透明1 天前
【搞定面试之mysql】第三篇 mysql的锁
java·后端·mysql·面试·职场和发展
武超杰1 天前
Spring Boot入门教程
java·spring boot·后端
IT 行者1 天前
Spring Boot 集成 JavaMail 163邮箱配置详解
java·spring boot·后端