容器化系统折腾

一、简介

相较于传统的 Linux 发行版来说, 纯容器化系统一般有以下优点:

  • 系统直接内置容器化工具, 例如 Docker、Podman 等
  • 自定义服务大部分可直接通过容器化运行, 简化部署和依赖
  • 系统特定区域具有不可变性, 即无法自行修改和写入, 保证系统完整性
  • 系统会自动滚动更新以保持最新状态
  • 系统一般为精简系统, 资源占用低, 攻击面较小

从上面这些优点来看, 纯容器化系统一般适合运行一些固定服务, 且可以容忍一定的服务中断(系统需要滚动更新). 当然纯容器化系统也有一些缺点:

  • 系统内可能没有内置任何包管理器, 不方便自行扩展系统级组件
  • 某些分区无法写入, 跟现有的一些配置规范等冲突, 需要大量调整
  • 系统默认集成的一些组件可能比较固定且无法替换
  • 需要重新学习配置文件等, 有一定时间成本(yaml 工程师)

二、容器化系统简史

最开始做容器化系统的应该是大名鼎鼎的 CoreOS, 当时 CoreOS 开源了很多工具, 比如大名鼎鼎的 etcd 等; 后来 CoreOS 被红帽收购, 原来的版本也停止更新, 新版本 CoreOS 由红帽重构, 此后便出现了两个版本:

  • Fedora CoreOS(fcos): 红帽收购后重新基于 Fedora 系统创建的 CoreOS
  • FlatCar: CoreOS 的直接替代品, 现在由 “巨硬” 收购了

从 “名义” 上来说 Fedora CoreOS 算是 CoreOS 的继承者, 但实际上内部已经重构; 所以从 “血统” 上来讲还是 FlatCar 更加像以前的 CoreOS.

三、Fedora CoreOS VS FlatCar

Fedora CoreOS 是红帽基于 Fedora 重新创建的 CoreOS, 该系统的特点是使用 rpm-ostree 工具来跟踪系统变化; rps-ostree 工具有点类似于 Git 一样跟踪系统变化, 同时也允许安装第三方 rpm 包; 相较于 FlatCar 来说其扩展性更强, 且并不是按照分区来保证系统的不可变性, 这样灵活度更高.

与 Fedora CoreOS 不同的是 FlatCar 采用与现在很多安卓手机的类似机制, 采用 A/B 分区的模式进行系统更新, 即系统启动时运行在 A 分区, 更新时只更新 B 分区, 下次重启自动切换到 B 分区启动; 这种方法的好处是简单直接可靠, 坏处就是没有 Fedora CoreOS 那样灵活.

还有一些区别就是 Fedora CoreOS 同时内置了 Podman 和 Docker 工具, 而 FlatCar 只内置了 Docker; 同时 Fedora CoreOS 网络工具采用的 NetworkManager, 而 FlatCar 采用的是 systemd-networkd.

综合来说两者各有优缺点, 我个人比较不喜欢 NetworkManager, 但 Podman 与 systemd 深度集成这点我还是挺喜欢的; 最后测试完纠结好久还是选择了 FlatCar, 因为 Podman 与 Docker 还是有些差异, 既然用不上又不喜欢 NetworkManager 同时 rpm-ostree 有一定学习成本, 那就干脆 FlatCar 好了; 不过值得说明的是两个系统配置文件大部分通用, 所以只是个人喜好问题.

四、FlatCar 安装

FlatCar 官方默认提供了各种软硬件环境的集成安装, 对于纯物理机提供 iso、iPEX 等安装方式, 针对于 VM 部署也提供了预构建的磁盘镜像; 由于安装方式过多, 这里只以 VMWare 平台 VCSA/ESXi 为例, 其他平台请参考 官方文档.

针对于 VMWare 平台, 需要先下载官方提供的 ova 虚拟机模版文件:

1
curl -LO https://stable.release.flatcar-linux.net/amd64-usr/current/flatcar_production_vmware_ova.ova

4.1、ESXi 部署

对于 ESXi 平台可以直接部署, 但每次部署都需要上传 ova 虚拟机模版:

  • 首先新建虚拟机, 并选择 “从 OVA 文件部署”
    QsmH5u

  • 然后输入虚拟机名称, 并上传 ova 文件
    Fi4FTS

  • 其中启动打开电源选项请根据实际需求调整, 如果稍后要调整磁盘、网卡等则可以先取消勾选, 防止直接启动
    d7PNQO

  • 其他设置中的 Options 配置暂时可以不写, 下一章节将会详细介绍配置
    cMAZaL

  • 完成后可调整虚拟机硬件配置(比如磁盘大小、CPU等), 然后启动即可
    Yh5ZA3

4.2、VCenter 部署

对于 VCenter 来说与 ESXi 大体相同, 不同的是 VCenter 需要新创建一个 “内容库”, 然后上传 ova 虚拟机模版, 安装时需要从内容库来选择 ova, 免去了每次都要上传 ova 的问题.

  • 首先创建存储 ova 的内容库
    qhfHbC

  • 名称可随意填写
    oNZLBN

  • 选择本地内容库
    XhA4mV

  • 然后不启用安全策略, 选择存储即可创建完成
    ALIuPv

  • 接下来点击 “操作” - “导入项目”
    BAXQ3m

  • 然后选择本地文件导入即可
    dCzUiM

后续步骤与 ESXi 基本一致, 都是新建虚拟机然后选择 ova 部署, 最后启动就可.

五、FlatCar 配置

上面水了一堆, 其实并没有体现出容器化核心配置以及优势; 本部分将着重介绍容器化系统的配置方式和相关的配置样例.

5.1、配置格式

由于历史原因容器化系统从最初的 CoreOS 发展到现在 Fedora CoreOS 和 FlatCar 经历了一系列变更, 其中配置文件最初以 Ignition File 变为现在的好几种格式; 下面说一下这几种格式的区别和应该用哪个:

  • Ignition File: 采用 JSON 格式描述, 是 Fedora CoreOS 和 FlatCar 最终使用的配置文件; 但是大量配置造成了 Ignition File 基本可读性很差, 所以一般都是通过其他配置转换成 Ignition File.
  • Butane Config: 采用 YAML 格式描述, 比较贴近系统原理, 配置清晰且可读性强; Fedora CoreOS 和 FlatCar 都支持此配置, 一般通过工具将其转换成 Ignition File 再使用.
  • Container Linux Config: 采用 YAML 格式描述, 该配置格式最初为 CoreOS 创建, 有一定上层抽象且目前似乎只支持 FlatCar, 同样通过工具转换为 Ignition File 使用, 所以不太推荐.

所以综上所述, 对于配置文件只需要看 Butane Config 就行了; 而 Butane Config 我个人认为 Fedora CoreOS 的文档样例描述的相对清晰, 两者都支持 Butane Config 所以区别不大, 所以即使最终选择使用 FlatCar 系统, 在学习配置时也可以参考 Fedora CoreOS 的文档.

5.2、Butane 安装

由于 Butane Config 需要转换成 Ignition File 才能被系统使用, 所以这里会用到一个转换工具, 即 butane 命令; 该命令可执行文件可以直接从 GitHub 下载, 也可以采用 Docker 镜像 quay.io/coreos/butane:release; 具体的安装方式和细节请参考 官方文档.

1
2
3
4
docker run --rm -i \
quay.io/coreos/butane:latest \
--pretty --strict < butane_config.yaml \
| base64 -w0

5.3、配置使用

详细配置说明放在下面, 这里讲一下怎么简单使用这个配置.

假设有以下 Butane Config, 且文件名为 test.yaml:

1
2
3
4
5
6
7
8
9
10
variant: flatcar
version: 1.0.0
kernel_arguments:
should_not_exist:
- flatcar.autologin
passwd:
users:
- name: root
ssh_authorized_keys:
- ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMskC9phaoO1WJkQOvXcUxH+DlG8u/2u1ReMXOO9vkbW mritd@linux.com

我们需要先使用以下命令将其转换为 Ignition 配置:

1
butane --pretty --strict test.yaml | base64 -w0

转换完成后将会输出一长串 base64 编码的文本, 在创建虚拟机时将此内容添加到 Ignition/coreos-cloudinit data 字段中, 同时在 Ignition/coreos-cloudinit data encoding 中指定编码为 base64, 虚拟机启动后将会自动应用配置.

UHHlUX

5.4、用户配置

Butane Config 中可以通过 passwd 属性配置容器化系统的内置用户和用户组, 需要注意的是默认情况下 root 用户是禁止 SSH 登陆的. 简单的配置样例如下:

1
2
3
4
5
passwd:
users:
- name: root
ssh_authorized_keys:
- ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMskC9phaoO1WJkQOvXcUxH+DlG8u/2u1ReMXOO9vkbW mritd@linux.com

以上配置在系统首次启动时会对 root 用户设置 ssh 登陆密钥, 后续就可以通过 ssh 使用 root 用户登陆; 除了 ssh_authorized_keys 字段以外还有一些常用的配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
passwd:
# 用户配置
users:
- name: test
# 设置特定用户的密码, 密码可通过以下命令生成
# openssl passwd -6 -salt SLAT PASSWD
password_hash: $6$slat$OKqcnY96krXmy7rRCdpIgN90jMQ5kqJkgJxIc1sEE21SBIyW7kd4hYa91pfCy.lo1qiN4NM7B9R8NTrdGLWq81
ssh_authorized_keys:
- ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMskC9phaoO1WJkQOvXcUxH+DlG8u/2u1ReMXOO9vkbW mritd@linux.com
# 定义用户的 UID(一般用于新增用户使用)
uid: 1023
# 定义用户的家目录
home_dir: /home/test
# 是否自动创建家目录
no_create_home: false
# 用户的主用户组
primary_group: test
# 用户的其他用户组
groups:
- admin
# 用户登陆的 shell
shell: /bin/bash
# 如果对一个已有用户设置为 false, 则启动后会删除此用户
should_exist: true
# 设置用户是否为系统级用户
system: true
# 组配置
groups:
# 用户组名称
- name: test1
# 组 ID
gid: 9977
# 组密码
password_hash: ...
# 是否应该存在(false 则删除已存在的组)
should_exist: true
# 是否为系统组
system: false

5.5、磁盘配置

在某些情况下我们可能需要采用独立磁盘作为数据存储, 例如挂载独立的 SSD 磁盘等; 磁盘相关的处理可以通过 storage.disks 字段进行配置, 配置完成后系统首次启动将会按照配置中的定义自动进行磁盘分区. 下面是磁盘配置的详细样例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
storage:
# 磁盘配置
disks:
# 设备文件位置, 推荐直接使用 PCI 位置进行匹配, 因为 /dev/sda 这种配置多磁盘可能会
# 出现磁盘盘符漂移问题
device: /dev/disk/by-path/pci-0000:02:00.0-scsi-0:0:0:0
# 是否强制清除分区表
wipe_table: true
# 定义分区结构
partitions:
# 定义分区 Label
label: data
# 分区编号, 如果为 0 则自动选择下一个可用的分区编号
number: 1
# 分区大小, 如果为 0 则默认占据最大空间
size_mib: 0
# 分区开头位置, 同样如果为 0 则采用最大可用的开头位置
start_mib: 0
# 如果分区表已存在是否重置, 为 true 时如果分区表已存在且与配置不符则会自动重建
# 为 false 时如果分区表已存在且不匹配则启动失败
wipe_partition_entry: false
# 如果为 true, 当分区表存在且除大小以外都与当前配置相同时, 会自动扩容分区
resize: false

5.6、文件系统配置

上面使用完 storage.disks 对磁盘进行分区后, 还需要通过 storage.filesystems 对磁盘分区进行格式化创建文件系统和挂载; 文件系统创建及挂载的配置样例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
storage:
# 文件系统配置
filesystems:
# 通过 Label 选中目标分区
label: data
# 配置格式化的格式
format: xfs
# mkfs 的额外参数
options:
- "-f"
# 是否在创建之前清除数据
wipe_filesystem: true
# 文件系统挂载点
path: /data
# 挂载的额外参数
mount_options:

值得一提的是 FlatCar 默认采用 ext4 作为根文件系统格式, 你可以通过以下配置改变根文件系统格式为 xfs:

1
2
3
4
5
filesystems:
- device: /dev/disk/by-partlabel/ROOT
wipe_filesystem: true
format: xfs
label: ROOT

5.7、文件配置

在 FlatCar 和 Fedora CoreOS 这类系统中, 其实大部分配置都是基于文件配置, 通过在 yaml 中定义配置文件内容和属性来达到自动配置的目的. 大部分常用的文件配置参数如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
storage:
# 文件配置
files:
# 文件的存储路径
- path: /data/testfile
# 是否覆盖文件
overwrite: true
# 文件权限
mode: 0644
# 文件属主
user:
id: 0
name: root
# 文件属组
group:
id: 0
name: root
# 文件内容
contents:
# 直接在 yaml 中定义文件内容
inline: |
asdsngjsdngda
# 与 inline 互斥, 可以通过此字段定义远程文件, 系统启动后会自动下载
# 支持协议 http, https, tftp, s3, gs
source: http://exmaple.com/testfile
# http 请求头, 当使用远程文件时可设置
http_headers:
- name: X-AUTH
value: xxxxxxxxx
# 与 contents 结构类似, 但是此部分定义的文件内容将会附加到目标文件之后
append:
contents:
inline: |
asnfbshdgbds
# 目录配置
# 一般用于创建特定目录使用, 比如 Fedora CoreOS 在使用 Podman 挂载时不会自动
# 创建宿主机目录, 此时可以通过此配置预先创建目录
directories:
- path: /data/mysql
mode: 0755
user:
id: 3306
group:
id: 3306

# 链接文件配置
links:
# 目标文件位置
- path: /etc/localtime
# 原始文件位置
target: ../usr/share/zoneinfo/Asia/Shanghai
# 是否创建硬链接
hard: false
user:
id: 0
group:
id: 0

5.7.1、配置 Hostname

以下配置样例用于配置当前主机的 Hostname:

1
2
3
4
5
6
storage:
files:
- path: /etc/hostname
mode: 0644
contents:
inline: test

5.7.2、配置 sysctl

以下配置样例用于配置特定的 sysctl 参数

1
2
3
4
5
6
7
storage:
files:
- path: /etc/sysctl.d/55-custom.conf
mode: 0644
contents:
inline: |
net.core.rmem_max=2500000

5.7.3、配置静态 IP

注意: 静态 IP 配置和 DNS 配置仅适用于 FlatCar, Fedora CoreOS 采用的是 NetworkManager, 所以配置有所不同, 请参考 Host Network Configuration 文档.

以下配置样例用于为第一个有线网卡配置静态 IP(默认情况下为 DHCP):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
storage:
files:
- path: /etc/systemd/network/25-xnet.network
contents:
inline: |
[Match]
Name=en*

[Network]
DHCP=no
NTP=time.windows.com time.apple.com

[Address]
Address=172.16.4.24/24

[Route]
Destination=0.0.0.0/0
Gateway=172.16.4.253

5.7.4、配置 DNS

以下配置样例用于配置系统 DNS:

1
2
3
4
5
6
7
8
9
10
storage:
files:
- path: /etc/systemd/resolved.conf.d/25-xnet.conf
contents:
inline: |
[Resolve]
DNS=223.5.5.5
DNS=119.29.29.29
#Domains=~.
#DNSStubListener=no

5.7.5、配置系统更新策略

由于 FlatCar 是自动滚动更新的, 滚动更新时需要进行重启保证 A/B 分区切换, 所以为了可控性一般我们需要配置一下可以重启的时间(更新策略):

1
2
3
4
5
6
7
8
9
10
storage:
files:
- path: /etc/flatcar/update.conf
mode: 0420
contents:
# 如果有更新的话, 默认在每天 10:30 进行重启更新, 最长的更新窗口期为 1 小时
inline: |
REBOOT_STRATEGY=reboot
LOCKSMITHD_REBOOT_WINDOW_START=10:30
LOCKSMITHD_REBOOT_WINDOW_LENGTH=1h

5.7.6、其他配置

文件配置是一个灵活的配置选项, 除了做一些系统配置外我们还可以利用它放入一些我们自己的东西, 比如自定义脚本之类的:

1
2
3
4
5
6
7
8
9
storage:
files:
- path: /opt/scripts/pre-update.sh
mode: 0755
contents:
inline: |
#!/usr/bin/env bash

curl -fsSL -XPOST -H 'Authorization: Bearer xxxxxxxxxxxxxxxxxxxxx' https://noti.example.com/message -d 'markdown=false' -d 'message=应用正在更新...'

5.8、Systemd 配置

当需要运行特定服务时, 一般我们需要创建一个 Systemd Service 配置文件, 这时就需要使用 Systemd 配置; Systemd 配置与文件配置类似, 不同之处在于 Systemd 配置虽然也只是定义 Systemd 文件, 但是提供了自启动等针对于 Systemd 的高级配置. 以下为运行 watchtower 容器的样例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
systemd:
units:
- name: watchtower.service
enabled: true
contents: |
[Unit]
Description=A container-based solution for automating Docker container base image updates.
After=network-online.target
Wants=network-online.target

[Service]
ExecStartPre=-/usr/bin/docker rm -f watchtower
ExecStartPre=/usr/bin/docker pull containrrr/watchtower
ExecStart=/usr/bin/docker run --tty \
--name watchtower \
-v /var/run/docker.sock:/var/run/docker.sock \
containrrr/watchtower --cleanup --interval 3600

[Install]
WantedBy=multi-user.target

需要注意的是, name 属性需要以完整的 systemd units 名称结尾, 即可以通过文件名指定 units 类型, 例如 test.timer 代表创建一个定时器.

5.9、内核参数配置

除了一些常规配置以外, 特殊情况下可能需要配置一些内核参数来控制系统行为; 比如默认情况下 FlatCar 会自动登录, 想关闭此行为可以通过一下配置调整内核参数:

1
2
3
4
kernel_arguments:
# 确保将特定参数从内核参数中移除
should_not_exist:
- flatcar.autologin

同样也可以使用 should_exist 添加一些内核参数:

1
2
3
4
kernel_arguments:
# 确保特定参数被添加到内核参数列表
should_exist:
- systemd.unified_cgroup_hierarchy=0

六、完整样例

以下是一个 FlatCar 部署 GitLab 的完整样例, 仅供参考:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
variant: flatcar
version: 1.0.0
kernel_arguments:
should_not_exist:
- flatcar.autologin
passwd:
users:
- name: root
# openssl passwd -6 -salt SALT PASSWORD
password_hash: $6$kovacs$7svc/7vosETJvIXF3G1SIV9NGdu.j6FRPHPR9DAp0nAQ1CnLuc766hHJQWZlJjlCRj9Nlx4KJjSMGcdtvH4dJ/
ssh_authorized_keys:
- ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMskC9phaoO1WJkQOvXcUxH+DlG8u/2u1ReMXOO9vkbW mritd@linux.com
storage:
filesystems:
- device: /dev/disk/by-partlabel/ROOT
wipe_filesystem: true
format: xfs
label: ROOT
# TimeZone
links:
- path: /etc/localtime
target: ../usr/share/zoneinfo/Asia/Shanghai

files:
# Update policy
- path: /etc/flatcar/update.conf
mode: 0420
contents:
inline: |
REBOOT_STRATEGY=reboot
LOCKSMITHD_REBOOT_WINDOW_START=10:30
LOCKSMITHD_REBOOT_WINDOW_LENGTH=1h
# Sysctl Config
- path: /etc/sysctl.d/55-custom.conf
mode: 0644
contents:
inline: |
net.core.rmem_max=2500000
# Set hostname
- path: /etc/hostname
mode: 0644
contents:
inline: gitlab
# Set static ip
- path: /etc/systemd/network/25-xnet.network
contents:
inline: |
[Match]
Name=en*

[Network]
DHCP=no
NTP=time.windows.com time.apple.com

[Address]
Address=172.16.1.6/24

[Route]
Destination=0.0.0.0/0
Gateway=172.16.1.1
# DNS Config
- path: /etc/systemd/resolved.conf.d/25-xnet.conf
contents:
inline: |
[Resolve]
DNS=223.5.5.5
DNS=119.29.29.29
#Domains=~.
#DNSStubListener=no

# GitLab Config
# ref: https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/files/gitlab-config-template/gitlab.rb.template
- path: /etc/gitlab/gitlab.rb
contents:
inline: |
external_url 'https://git.example.com'

gitlab_rails['time_zone'] = 'Asia/Shanghai'

### GitLab email server settings
###! Docs: https://docs.gitlab.com/omnibus/settings/smtp.html
###! **Use smtp instead of sendmail/postfix.**
gitlab_rails['smtp_enable'] = true
gitlab_rails['smtp_address'] = "smtp.example.com"
gitlab_rails['smtp_port'] = 465
gitlab_rails['smtp_user_name'] = "gitlab@example.com"
gitlab_rails['smtp_password'] = "asdassgdfgd"
gitlab_rails['smtp_domain'] = "example.com"
gitlab_rails['smtp_authentication'] = "login"
gitlab_rails['smtp_enable_starttls_auto'] = false
gitlab_rails['smtp_tls'] = true
gitlab_rails['gitlab_email_from'] = 'gitlab@example.com'
gitlab_rails['gitlab_email_reply_to'] = 'gitlab@example.com'

### Default Theme
### Available values:
##! `1` for Indigo
##! `2` for Dark
##! `3` for Light
##! `4` for Blue
##! `5` for Green
##! `6` for Light Indigo
##! `7` for Light Blue
##! `8` for Light Green
##! `9` for Red
##! `10` for Light Red
gitlab_rails['gitlab_default_theme'] = 2

### Reply by email
###! Allow users to comment on issues and merge requests by replying to
###! notification emails.
###! Docs: https://docs.gitlab.com/ee/administration/reply_by_email.html
gitlab_rails['incoming_email_enabled'] = false

### GitLab Shell settings for GitLab
gitlab_rails['gitlab_shell_ssh_port'] = 2222

#### Change the initial default admin password and shared runner registration tokens.
####! **Only applicable on initial setup, changing these settings after database
####! is created and seeded won't yield any change.**
gitlab_rails['initial_root_password'] = "hgfghwerwefwfw"
gitlab_rails['initial_shared_runners_registration_token'] = "asfdfhfghfgsfsdfs"

### Settings used by GitLab application
gitlab_rails['registry_enabled'] = false

### Settings used by Registry application
registry['enable'] = false

## GitLab NGINX
nginx['listen_port'] = '2080'
nginx['listen_https'] = false
nginx['redirect_http_to_https'] = false
nginx['real_ip_trusted_addresses'] = ['127.0.0.0/8', '10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16']
nginx['real_ip_header'] = 'X-Forwarded-For'
nginx['real_ip_recursive'] = 'on'

## GitLab Logging
logging['logrotate_frequency'] = "daily" # rotate logs daily
logging['logrotate_size'] = nil # do not rotate by size by default
logging['logrotate_rotate'] = 30 # keep 30 rotated logs
logging['logrotate_compress'] = "compress" # see 'man logrotate'
logging['logrotate_method'] = "copytruncate" # see 'man logrotate'
logging['logrotate_postrotate'] = nil # no postrotate command by default
logging['logrotate_dateformat'] = nil # use date extensions for rotated files rather than numbers e.g. a value of "-%Y-%m-%d" would give rotated files like p

# Caddy config
- path: /etc/caddy/Caddyfile
contents:
inline: |
(LOG_FILE) {
log {
format transform "[{ts}] {request>remote_ip} [{status}] {request>proto} {request>method} {request>host} {request>uri} {request>headers>User-Agent>[0]}" {
time_format "iso8601"
}
output file "{args.0}" {
roll_size 100mb
roll_keep 3
roll_keep_for 7d
}
}
}

(TLS_MODERN) {
protocols tls1.3
}

(TLS_INTERMEDIATE) {
protocols tls1.2 tls1.3
ciphers TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
}

(TLS_OLD) {
protocols tls1.0 tls1.3
ciphers TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA TLS_RSA_WITH_AES_128_GCM_SHA256 TLS_RSA_WITH_AES_256_GCM_SHA384 TLS_RSA_WITH_AES_128_CBC_SHA TLS_RSA_WITH_AES_256_CBC_SHA TLS_RSA_WITH_3DES_EDE_CBC_SHA
}

(HSTS) {
# HSTS (63072000 seconds)
header / Strict-Transport-Security "max-age=63072000"
}

(SECURITY) {
# hidden server name
header -Server
}

(ACME_PROVIDER_CLOUDFLARE) {
dns cloudflare {$CLOUDFLARE_API_TOKEN}
}

(ACME_PROVIDER_DNSPOD) {
dns dnspod {$DNSPOD_TOKEN}
}

(ACME_PROVIDER_DUCKDNS) {
dns duckdns {$DUCKDNS_API_TOKEN}
}

(ACME_PROVIDER_GANDI) {
dns gandi {$GANDI_API_TOKEN}
}

(ACME_PROVIDER_ALIYUN) {
dns alidns {
access_key_id {$ALIYUN_ACCESS_KEY_ID}
access_key_secret {$ALIYUN_ACCESS_KEY_SECRET}
}
}

(ACME_DNS) {
# 压缩支持
encode zstd gzip

# TLS 配置
tls {
import TLS_{args.0}
import ACME_PROVIDER_{args.1}
resolvers 8.8.8.8
}

# HSTS
import HSTS

# security config
import SECURITY
}


# sysctl -w net.core.rmem_max=2500000
# ref https://github.com/lucas-clemente/quic-go/wiki/UDP-Receive-Buffer-Size
{
# ZeroSSL
acme_ca https://acme.zerossl.com/v2/DV90
#email {$ACME_EMAIL:}
#cert_issuer {$ACME_ISSUER:} {$ACME_ISSUER_PARAMS:}

# ECC cert
key_type p384

servers :443 {
protocols h1 h2 h3
}

}

git.example.com {
reverse_proxy gitlab:2080

import ACME_DNS MODERN ALIYUN
import LOG_FILE /data/git.example.com.log
}

- path: /etc/caddy/env
contents:
inline: |
ALIYUN_ACCESS_KEY_ID=xxxxxxxxxxxxxxxxxxxx
ALIYUN_ACCESS_KEY_SECRET=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

- path: /etc/mc/config.json
contents:
inline: |
{
"version": "10",
"aliases": {
"nas": {
"url": "https://s3.example.com",
"accessKey": "admin",
"secretKey": "xxxxxxxxxxxxxxxxxxxxxx",
"api": "s3v4",
"path": "auto"
}
}
}

- path: /opt/scripts/gitlab-backup.sh
mode: 0755
contents:
inline: |
#!/usr/bin/env bash

set -e

TMPDIR=$(mktemp -d)
BACKUP_DIR=${TMPDIR}/gitlab
BACKUP_DATE=$(date '+%Y-%m-%d-%H-%M-%S')
BACKUP_FILE=${BACKUP_DATE}_gitlab_backup.tar
GITLAB_VERSION=$(docker exec gitlab gitlab-rails runner 'puts Gitlab::VERSION')
PACKAGE_NAME=gitlab_${GITLAB_VERSION}_${BACKUP_DATE}.tar

function cleanup(){
rm -rf ${TMPDIR}
}

trap cleanup EXIT

echo "GitLab Backup Running..."
echo "Backup Filename => ${PACKAGE_NAME}"
mkdir -p ${BACKUP_DIR}

echo "Creating GitLab Backup Tarball..."
docker exec -i gitlab gitlab-backup create BACKUP=${BACKUP_DATE} STRATEGY=copy

echo "Moving GitLab Tarball..."
mv /data/gitlab/data/backups/${BACKUP_FILE} ${BACKUP_DIR}

echo "Copying GitLab Config..."
docker cp gitlab:/etc/gitlab/gitlab.rb ${BACKUP_DIR}
docker cp gitlab:/etc/gitlab/gitlab-secrets.json ${BACKUP_DIR}

echo "Packaging All Backup Files..."
(cd ${TMPDIR} && tar -cvf ${PACKAGE_NAME} gitlab)

echo "Sync Backup File To Remote Storage..."
docker run --rm -v /etc/mc:/root/.mc -v /tmp:/host/tmp hub.mi.os.sb/minio/mc cp /host/${TMPDIR}/${PACKAGE_NAME} nas/gitlab/${PACKAGE_NAME}

echo "Backup Success!"

- path: /opt/scripts/gitlab-restore.sh
mode: 0755
contents:
inline: |
#!/usr/bin/env bash

set -e

tar -xvf $1

TARBALL=$(basename $(find gitlab/ -type f -name '*.tar' | head -n 1))
BACKUP_NAME=$(echo ${TARBALL} | sed 's@_gitlab_backup.tar$@@')

mv gitlab/${TARBALL} /data/gitlab/data/backups
chmod 777 /data/gitlab/data/backups/${TARBALL}

docker exec -it gitlab gitlab-ctl stop puma
docker exec -it gitlab gitlab-ctl stop sidekiq
docker exec -it gitlab gitlab-backup restore BACKUP=${BACKUP_NAME} force=yes
docker exec -it gitlab gitlab-rake gitlab:check SANITIZE=true

docker cp gitlab/gitlab.rb gitlab:/etc/gitlab
docker cp gitlab/gitlab-secrets.json gitlab:/etc/gitlab

systemctl restart gitlab
journalctl -fu gitlab

systemd:
units:
- name: gitlab.service
enabled: true
contents: |
[Unit]
Description=The DevSecOps Platform
After=network-online.target
Wants=network-online.target

[Service]
TimeoutStartSec=0
ExecStartPre=-/usr/bin/docker pull gitlab/gitlab-ce
ExecStart=/usr/bin/docker run --rm --tty \
--name gitlab \
--hostname gitlab \
-p 2222:22 \
-p 2080:2080 \
-e GITLAB_OMNIBUS_CONFIG="from_file('/host/etc/gitlab/gitlab.rb')" \
-v /etc/gitlab:/host/etc/gitlab \
-v /opt/scripts:/host/opt/scripts \
-v /data/gitlab/data:/var/opt/gitlab \
-v /data/gitlab/logs:/var/log/gitlab \
-v /data/gitlab/config:/etc/gitlab \
gitlab/gitlab-ce

[Install]
WantedBy=multi-user.target

- name: gitlab-backup.service
enabled: false
contents: |
[Unit]
Description=GitLab Backup Service
After=network-online.target gitlab.service
Wants=network-online.target

[Service]
Type=simple
Restart=on-failure
ExecStart=/opt/scripts/gitlab-backup.sh

[Install]
WantedBy=multi-user.target

- name: gitlab-backup.timer
enabled: true
contents: |
[Unit]
Description=GitLab Auto Backup Timer
After=network-online.target gitlab.service
Wants=network-online.target

[Timer]
# Run at 3am, 11am, 5pm and 8pm every day in UTC+8
OnCalendar=*-*-* 3,11,17,20:00:00 Asia/Shanghai

[Install]
WantedBy=timers.target

- name: caddy.service
enabled: true
contents: |
[Unit]
Description=The Ultimate Server
After=network-online.target gitlab.service
Wants=network-online.target

[Service]
TimeoutStartSec=0
ExecStartPre=-/usr/bin/docker pull mritd/caddy
ExecStart=/usr/bin/docker run --rm --tty \
--name caddy \
--env-file /etc/caddy/env \
--link gitlab \
-p 80:80 \
-p 443:443/tcp \
-p 443:443/udp \
-v /etc/caddy:/etc/caddy \
-v /data/caddy/data:/data \
-v /data/caddy/config:/config \
mritd/caddy

[Install]
WantedBy=multi-user.target

- name: watchtower.service
enabled: true
contents: |
[Unit]
Description=A container-based solution for automating Docker container base image updates.
After=network-online.target
Wants=network-online.target

[Service]
ExecStartPre=/usr/bin/docker pull containrrr/watchtower
ExecStart=/usr/bin/docker run --rm --tty \
--name watchtower \
-v /var/run/docker.sock:/var/run/docker.sock \
containrrr/watchtower --cleanup --schedule "0 30 17 * * *"

[Install]
WantedBy=multi-user.target

七、其他说明

默认情况下 FlatCar 的 /usr 分区是不可写入的, 所以不要尝试向此目录中写入文件这会导致启动失败; 除此之外类似 /opt 之类的目录都可以持久化存放数据; 不过 Fedora CoreOS 似乎并不相同, 具体需要查阅官方文档.

FlatCar 如果想要扩展一些系统级的目录需要使用 Systemd-sysext, 具体请查看 官方文档; Fedora CoreOS 可以通过 rpm-ostree 直接安装软件包, 但有些服务可能需要重启才能生效(例如 open-vm-tools).

本篇文章仅描述了基本使用, 复杂情况例如批量更新控制等限于篇幅还请阅读官方文档.


容器化系统折腾
https://mritd.com/2023/07/20/containerized-system-test/
作者
Kovacs
发布于
2023年7月20日
许可协议