1. 前言
树苗科技公司业务上云2年有余,随着技术的不断发展。运维团队支持业务变更配置、开资源、删资源等过程加班到深夜一两点已是家常便饭。为了进一步提升工作效率,运维总监张三决定对当前业务交付路径进行统一梳理,借助IaC的理念和Terraform自动化工具逐步实施运维生产自动化,改善组内成员频繁加班且重复劳动的现状。
期望:
能够建设生产网和测试网络的业务交付自动化
能够有一个轻量级前段给业务团队自己申请
能够有权限管理和版本控制,防止一键删资源跑路的情况
能够使用容器技术,在短时间内交付可扩容的业务脚本
前情回顾:
李四逐步意识到,自己正在逐渐走上快车道。离躺着办公距离不远了。
基础设施自动化帮助李四完成了从手动工作到半自动工作的转化,将通用的业务逐步抽离出来。 让运维不再苦B。这一天,李四离开办公区,仿佛看到了幸福的未来。
但是做了多年管理的李四逐渐意识到代码化的优势非常明显,但也很可能随便下面一个运维同学一个destroy命令业务就没了。因此需要做好代码的变更和版本管理。接下来就可以进行下一步:版本管理的搭建。
所以,今天李四下定决心要把基于Gitlab的版本控制再加上,进一步加快自己躺赢的节奏。
2. 组织背景
树苗科技公司共有四个部门,运维部门,品种研发部门、企业财务部门和企业安全部门。
运维部门:主要负责云上资源的管理工作,支撑各部门的业务。
品种研发部门:主要负责新品种研发和产品发布上市过程。会与运维部门频繁交付。经常会申请开通测试环境,新业务建设和旧业务扩容等需求。
3. 业务痛点
多方协作难,上一套新的规格的资产,跑着让合规看看、安全看看、审计看看,一个月过去了。业务方开始升级,抱怨太慢了。
业务收口难,对于云上资产的规格难以收口,如果能把企业资产梳理下,其实也就需要维护十几套资源足够。
业务体验差,业务方如果有个简单的申请入口就好了。
4. 调研
首先进入李四眼帘的,就是Git这套东西。不仅仅能够兼容未来企业逐步发展国际业务的需求(多团队可以基于同一套体系运转)。也可以做好自身的快速业务构建。经过一番调研,李四发现:
GitHub 和 GitLab 都是基于 Web 的 Git 仓库,使用起来二者差不多,它们都提供了分享开源项目的平台,为开发团队提供了存储、分享、发布和合作开发项目的中心化云存储的场所。
GitHub 作为开源代码库,拥有超过 900 万的开发者用户,目前仍然是最火的开源项目托管平台,GitHub 同时提供公共仓库和私有仓库,但如果使用私有仓库,是需要付费的。
GitLab 解决了这个问题,你可以在上面创建私人的免费仓库。
而GitLab 让开发团队对他们的代码仓库拥有更多的控制,相比较 GitHub , 它有不少特色:
(1) 允许免费设置仓库权限;
(2) 允许用户选择分享一个 project 的部分代码;
(3) 允许用户设置 project 的获取权限,进一步提升安全性;
(4) 可以设置获取到团队整体的改进进度;
(5) 通过 innersourcing 让不在权限范围内的人访问不到该资源;
所以,从代码的私有性上来看,GitLab 是一个更好的选择。但是对于开源项目而言,GitHub 依然是代码托管的首选。
因为这部分业务本身也不打算公开,完全的内部使用诉求。因此李四好不由于的选择了Gitlab作为版本控制和访问控制的基础。
李四又拿出之前给张三汇报的建设框架,绿色部分已经验证完成。接下来需要把黄色部分也搭建起来。
5. 需求
代码能够被统一管理(不再需要存储在本地空间,万一李四电脑坏了,那可完了)
能够多人协作,让李四下面的小张和小王同学也能够共同参与。但同时又可以防止误操作
借助版本控制能力,李四能够对每一次变更做好code review。防止业务事故发生。更谨慎点,李四甚至开始思考多人审批来保障业务可靠性。
6. 解决思路
Docker 安装
路径1:官方网站
缺点:安装太慢
路径2: 阿里云容器镜像服务
缺点:需要登录
安装Portainer
portainer是一个Docker的可视化的管理工具。使用下面的命名安装portainer:
docker run -d -v "/var/run/docker.sock:/var/run/docker.sock" -p 9000:9000 portainer/portainer
三、单机版运行
如果仅有一个docker宿主机,则可使用单机版运行,Portainer单机版运行十分简单,只需要一条语句即可启动容器,来管理该机器上的docker镜像、容器等数据。
docker run -d -p 9000:9000 --restart=always -v /var/run/docker.sock:/var/run/docker.sock --name portainer docker.io/portainer/portainer
上面的命令执行完成之后,打开浏览器访问:
localhost:9000
一开始会让你设置管理员密码,设置完成之后,进入系统,界面应该是下面这个样式的:
安装gitlab-ce
确认docker配置
内存要在4G以上,不然会出现502错误。 CPU被打满。
使用下面的命令安装gitlab-ce
sudo docker run --detach \
--hostname mygitlab.com \
--publish 8443:443 --publish 80:80 --publish 2222:22 \
--name gitlab \
--restart always \
--volume /Volumes/mydisk/gitlab/config \
--volume /Volumes/mydisk/gitlab/logs \
--volume /Volumes/mydisk/gitlab/data \
gitlab/gitlab-ce:latest
上面的参数说明:
hostname按照自己的需要改
volume的冒号前面为物理机器上的实际目录,需提前建好,然后改为正确路径。
publish的三个端口映射自己看情况来,一般自己网内使用,光一个80就好了,443和22都需要额外配置数字证书什么的
上述过程我其实一开始错配刘一个gitlab,导致项目和端口等信息冲突。可以通过docker控制台删除gitlab,再次部署即可。
执行完成后,可以在docker控制台看到运行和部署状态:
设置gitlab管理员密码
访问localhost之后,会进入登录页面。 so,密码是啥? 根据下面配置即可。
进入cli后,需要通过下面几个语句进行配置:
1、要重置您的root密码,请首先使用root特权登录到服务器。使用以下命令启动Ruby on Rails控制台
# gitlab-rails console -e production
user = User.where(id: 1).first--------------------------------------------------------------------------------
Ruby: ruby 2.7.4p191 (2021-07-07 revision a21a3b7d23) [x86_64-linux]
GitLab: 14.4.0 (51b27ab5805) FOSS
GitLab Shell: 13.21.1
PostgreSQL: 12.7
--------------------------------------------------------------------------------
user = User.where(id: 1).first
2、等待控制台加载完毕,有多种找到用户的方法,您可以搜索电子邮件或用户名
irb(main):002:0> user = User.where(id: 1).first
=> #
3、现在,您可以更改密码
irb(main):004:0> user.password = '1q2w3e4r%'
=> "1q2w3e4r%"
irb(main):005:0> user.password_confirmation = '1q2w3e4r%'
=> "1q2w3e4r%"
4、重要的是,您必须同时更改密码和password_confirmation才能使其正常工作,别忘了保存更改
irb(main):006:0> user.save!
Enqueued ActionMailer::MailDeliveryJob (Job ID: c02951a0-caeb-45d7-815d-8b71b55988c4) to Sidekiq(mailers) with arguments: "DeviseMailer", "password_change", "deliver_now", {:args=>[#>]}
=> true
通过Git进行项目管理
进行到这一步,李四相信问题正在一步步解决。 现在通过gitlab能够进一步解决他所担心的随意操作的问题以及误操作带来的极大风险。
李四同样也做了一些调研。 发现gitlab可以帮助他实现:
项目管理
共享
wiki
bug跟踪
CI & CD 持续集成持续交付
Gitlab-runner安装
在有了gitlab做cicd的管理以后,还需要有配套的runner去帮助李四去执行脚本。 这里李四选择使用docker去执行。
更多细节可以参考官网文档:链接
使用docker安装runner非常简单,难是在YAML的配置上。
docker run -d --name gitlab-runner --restart always --link gitlab:gitlab-ce -v /Users/tiankai/gitlab-runner/config:/etc/gitlab-runner -v /var/run/docker.sock:/var/run/docker.sock gitlab/gitlab-runner
简单做一个解释:
--name : 要定义的runner的容器的名字,这里我的与gitlab相对应,叫gitlabrunner
--restart: 每次都会启动,省去很多事情
--link: 比较关键,因为容器的网络本身是不互通的。--link可以用来链接2个容器,使得源容器(被链接的容器)和接收容器(主动去链接的容器)之间可以互相通信,并且接收容器可以获取源容器的一些数据,如源容器的环境变量。
--link的格式:
--link :alias
其中,name和ID是源容器的name和ID,alias是源容器在link下的别名。
其他信息可以去官网去看
安装完成后,就可以在docker中看到这个容器了。
Gitlab-runner配置
安装完后,可以执行docker ps看到runner的正常状态,也可以通过portainer看到docker的状态。都非常方便。 接下来就需要把runner注册到gitlab中。
上面就是我已经跑起来的三个服务,分别是gitlab,gitlab-runner和portainer。
运行下面命令
docker exec -it gitlab-runner gitlab-runner register
输入gitlab的URL地址
Please enter the gitlab-ci coordinator URL (e.g)
http://gitlab
这里要注意下,是http://容器的名字。(这是容器里面特有的一个逻辑,2个容器的网络可以通过名字直接访问,如果是ip,就需要通过宿主机进行桥接访问) 如果配置后,runner执行过程中无法从gitlab拉取项目,那多半是网络问题。
输入token
Please enter the gitlab-ci token for this runner:后面的步骤根据提示输入即可。token查看地址
选择执行方式,这里建议直接docker
Please enter the executor: parallels, ssh, virtualbox, docker-ssh+machine, kubernetes, docker, docker-ssh, shell, docker+machine:
更多区别可以看:链接
docker
选择要执行的镜像,hashicorp的镜像地址:
hashicorp/terraform
到这,runner已经注册成功,刷新ci/cd下的runner页面,应该可以看到这样的一个runner状态:
Gitlab-runner启用
使用管理员账户登录gitlab,访问 链接(mygitlab.com是我定义的域名,host文件与127.0.0.1做了映射。如果没有,就是localhost即可)在页面的底部可以看到已经注册成功的runner。在这里可以点击runner进入配置,可以给runner指派项目。指派的项目就可以使用该runner了。
点击进入详情,给runner做项目配置,以及打开不定义tag也可以去启动runner。(这个要注意,很多时候是因为不知道如何定义tag,导致一直无法触发cicd的流程;使用熟练了以后,可以再逐步标准化的通过tag管理多个runner的调度。
通过gitlab-ci.yml控制ci流程
在自己IDE中创建测试项目,Git的HTTP地址从项目-clone中获取即可。
执行一个测试脚本:
注意:terraform被runner调度起来后,会再调度一个terraform的容器用来执行。所以这里要写terraform的gitlab-ci.yml。
这也是最难的部分。
# 定义image,从本地加载。镜像名称通过docker ps复制过来。一些情况下会是域名/镜像名称
image:
name: hashicorp/terraform
entrypoint:
- 'usr/bin/env'
- 'PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin'
#定义stages, 上面是gitlab的默认过程。也可以通过自己定义
stages:
- build
- test
- deploy
#在执行之前,因为代码都放在terraform目录下,因此先要切换过去目录。
default:
before_script:
- terraform --version
- cd terraform/
#build阶段,初始化terraform,并保存一些cache文件用于下一步的执行
build:
script:
- terraform init
cache:
paths:
- terraform/.terraform.lock.hcl
- terraform/terraform.tfstate
- terraform/terraform/.tfstate.backup
- terraform/.terraform/*
- terraform/plan
#test阶段,输出plan计划用于决策
test:
script:
- terraform plan
cache:
paths:
- terraform/.terraform.lock.hcl
- terraform/terraform.tfstate
- terraform/terraform/.tfstate.backup
- terraform/.terraform/*
- terraform/plan
#部署阶段,查看变化信息,把过程中添加一个人工审核过程。
#artifacts将执行完成的信息,保存在cicd流程结束后,用于导出和查看。
deploy:
script:
- terraform plan
- terraform apply --auto-approve
when: manual
allow_failure: false
cache:
paths:
- terraform/.terraform.lock.hcl
- terraform/terraform.tfstate
- terraform/terraform/.tfstate.backup
- terraform/.terraform/*
- terraform/plan
artifacts:
paths:
- terraform/.terraform.lock.hcl
- terraform/terraform.tfstate
- terraform/terraform/.tfstate.backup
- terraform/.terraform/*
- terraform/plan
提交变更,并执行CICD
git push
提交后,gitlab cicd流程触发。如下图所示:
test阶段,可以看到执行了terraform plan指令,可以为deploy的人工决策作为依据。
7. 总结
现在,李四的目标更进一步。通过docker+gitlab+gitlab-runner成功部署了tree-project。 并且创建了一个项目。但并未对项目进行整体的管理和流程设计。
8. 问题记录
Failed to connect to mygitlab.com port 80 after 0 ms: Connection refused
这个问题搞了我一天,最后发现还是要回去学习docker的网络原理。 因为mygitlab.com是在宿主机定义的,gitlab的本身网络是Docker 内置了一个默认 bridge network(名为 bridge,第一行),也就是上文中 docker0 接口所属的 network,所有未指定 network 的容器,默认连接到此 network 中,其网段为 172.17.0.1/16。所以,两个未进行任何连接操作的容器是可以通过 IP 地址互相通信的,因为他们同在一个 network 下,但通讯只能通过 IP 地址进行(比如 ping 172.17.0.5),不可以通过容器名通信(比如 ping container-name)。但自定义创建的网络可以通过容器名进行通信。
注意:这里官方不推荐--link,会有很多引发的问题。
解决方法:
# 登录docker
docker exec -it 022489fb9f2f /bin/bash
# 进入runner配置文件目录
vi /etc/gitlab-runner/config.toml
# 添加external hosts
[runners.docker]
extra_hosts = ["mygitlab.com:192.xxx.xxx.xxx"]
/etc/gitlab-runner/config.toml and it started cloning correctly.
https://gitlab.com/gitlab-org/gitlab-foss/-/issues/26489
IP地址获取:
在宿主机ifconfig
找到:
en0: flags=8863 mtu 1500
options=6463
ether f8:ff:c2:41:24:2c
inet6 fe80::40c:2c02:745a:fe5%en0 prefixlen 64 secured scopeid 0x6
inet 30.240.90.190 netmask 0xffffe000 broadcast 30.240.95.255
nd6 options=201
media: autoselect
# inet30.240.90.190就是要配置的宿主机地址