fix: Normalize `RLIMIT_NOFILE` to sensible defaults

- relates to https://github.com/moby/moby/issues/38814
- fixes https://github.com/moby/moby/issues/43361
- fixes https://github.com/moby/moby/issues/45436
- fixes https://github.com/moby/moby/issues/45380


## What I did

Adjusted init configs to be as consistent as possible with handling the `RLIMIT_NOFILE` setting (_number of open file descriptors a single process can have at one time_).
- An explicit soft limit + constrained hard limit prevents a variety of issues that occur when software is run in a container vs directly on the host.
  - In the past, a soft limit may have been problematic for `dockerd` itself (_non-issue since Go 1.19 / Aug 2022).
  - `LimitNOFILE=infinity` is a regression introduced in a May 2021 PR merging a 2018 commit from another repo, whereas the value of `infinity` became excessive on various systems starting from 2019.
  - `524288` is a standard hard limit on systems with systemd since v240, being sufficient for software and not known to cause any problems.
- **NOTE:** The OpenRC config uses a single command for `ulimit` via `rc_ulimit`, preventing setting a soft and hard limit, thus it will maintain a high soft limit. You cannot use `-H` + `-S` in the same `ulimit` command to set separate soft/hard values.

<details>
<summary>Reasoning for `1024:524288` (click to expand)</summary>

- `infinity` is especially problematic as a soft limit, introducing difficult to troubleshoot / discover bugs.
  - This became more problematic with [systemd v240](https://github.com/containerd/containerd/pull/7566#issuecomment-1461140261) & [Go 1.19](https://github.com/containerd/containerd/issues/8249#issuecomment-1463400448) releases adjusting the impact implicitly.
- `1048576`  was adjusted to `524288` elsewhere to align with `docker.service` / systemd.
  - The few that actually need this to be higher [can use drop-in overrides](https://github.com/moby/moby/issues/45436#issuecomment-1534520189). However that is likely more relevant for `containerd.service` than it is `docker.service`.
- I have [documented how `LimitNOFILE` in `docker.service` + `containerd.service` affects resource usage and operations](https://github.com/moby/moby/issues/38814#issuecomment-1483951375):
  - `524288` should be more than enough to support 65k `busybox` containers (_which itself would [encounter various other bottlenecks](https://github.com/moby/moby/issues/44973#issuecomment-1543733757) along the way that require configuration changes to support_).
  - Individual containers can request higher limits with `--ulimit "nofile=soft:hard"` (_as `root` no greater than `fs.nr_open`_).
- Extensive reasoning for choosing `1024:524288` [covered here](https://github.com/containerd/containerd/pull/7566#issuecomment-1461142737).
- [systemd docs for `LimitNOFILE`](https://www.freedesktop.org/software/systemd/man/systemd.exec.html#Process%20Properties):
   > **`LimitNOFILE`:** Don't use. Be careful when raising the soft limit above 1024, since `select(2)` cannot function with file descriptors above 1023 on Linux.
   > Nowadays, the hard limit defaults to 524288, a very high value compared to historical defaults
   > 
   > Typically applications should increase their soft limit to the hard limit on their own, if they are OK with working with file descriptors above 1023, (i.e. do not use `select(2)`).
   > Note that file descriptors are nowadays accounted like any other form of memory, thus there should not be any need to lower the hard limit.

</details>

## How I did it

<details>
<summary>Approach (click to expand):</summary>

- Used `git blame` history on config to lookup relevant PRs on Github, review the changes between them and discussions.
- Engaged in main discussions on `moby` and `containerd` regarding the issue.
- Researched external resources on the subject and investigated behaviour across several releases and distros where changes from Docker, systemd and Go, or a distro affected the behaviour. Shared findings.
- Consolidated all of the information gathered to propose a positive improvement with evidence to support it.
</details>

<details>
<summary>Related History (click to expand):</summary>

- March 2014 - [Originally introduced into systemd `docker.service`](https://github.com/moby/moby/pull/4455#issuecomment-36679884) (_`LimitNOFILE` + `LimitNPROC`_):
  > _there's nothing special about `1048576`._
  > _In fact it's higher than is actually necessary (at the moment)._
- Follow-up PRs:
  - [Upstart](https://github.com/moby/moby/pull/4749) (_separate soft value introduced: `524288:1048576`_)
  - [OpenRC + SysVinit](https://github.com/moby/moby/pull/6814) (_later [SysVinit added a conditional between `-u` / `-p` based on shell](https://github.com/moby/moby/pull/7018)_)
- July 2016 - [Changed `docker.service` limits to `infinity`](https://github.com/moby/moby/pull/24307) (_[this future comment](https://github.com/moby/moby/pull/24555#issuecomment-232254840) seems to be about [this comment/issue](https://github.com/moby/moby/issues/21485#issuecomment-231376834) as the motivation for the **"non-zero limit"** config note_)
- July 2016 - https://github.com/moby/moby/pull/24555
  - `-u` + `-p` changed to `unlimited` in other init configs.
  - `LimitNOFILE` reverted back to `1048576` and relocated above the "non-zero limit" config note.
  - "non-zero limit" config note added to other init configs.
- April 2019 - [OpenRC improvements + adopts `rc_ulimit` for setting limits](https://github.com/moby/moby/pull/39000)
- May 2021 - [`LimitNOFILE` changed back to `infinity` and moved back under the "non-zero limit" config note](https://github.com/moby/moby/pull/42373)
  - This was a regression introducing problems on distros shipping systemd v240+ which redefined what `LimitNOFILE=infinity` resolves to. Some distros like Debian explicitly opt-out of that, keeping the original `1048576` limit.
  - The relocation of the line, caused confusion with users and maintainers/contributors since as it was implied that `LimitNOFILE` was set to `unlimited` for the same reason as the others (_this was originally the intent, but later realized as not applicable_).

More extensive history [coverage here](https://github.com/containerd/containerd/pull/7566#issuecomment-1461135751). `containerd` also had [a similar `LimitNOFILE` config dance](https://github.com/containerd/containerd/pull/7566#issuecomment-1452394753).

In addition, the ecosystem around Docker also changed. Notably changes introduced that affected what `infinity` resolves to (since systemd v240 in 2018Q4), and ignoring any configured soft limit (_Go 1.19/1.20 releases between Aug 2022 and May 2023_).

---

</details>

<details>
<summary>Additional references (click to expand):</summary>

- Related [change for `containerd` (review discussion)](https://github.com/containerd/containerd/pull/7566/files#r1148455486)
- [I did an extensive investigation](https://github.com/containerd/containerd/pull/7566#issuecomment-1461134171)
- Related insights on the topic I contributed:
  - https://github.com/docker/for-linux/issues/73#issuecomment-1455386927
  - https://github.com/moby/moby/issues/38814#issuecomment-1463188394

</details>

## How to verify it

Requires at least Moby 23.0.6, or 24.0.0 for an important fix introduced since Go 1.19.9 / 1.20.4:

```dockerfile
FROM alpine
RUN echo "Soft: $(ulimit -Sn)" >> /limits.txt
RUN echo "Hard: $(ulimit -Hn)" >> /limits.txt
```

```console
# `docker.service` limit applies to `docker build` since the adoption of BuildKit:
$ docker build --no-cache -t limits-test .
$ docker run --rm limits-test cat /limits.txt

Soft: 1024
Hard: 524288
```

<details>
<summary>Notes (click to expand)</summary>

- **Without this change**, both Soft and Hard values would be the same and either `1048576` (`2^20`) or `1073741816` (`2^30`).
- `grep 'files' /proc/$(pidof dockerd)/limits` will report the soft limit as the hard limit.
  
  This is expected because Go implicitly raises the soft limit since Go 1.19 released in Aug 2022. That is reset to the expected soft limit when the daemon runs some processes involved in `docker build`.
- Testing at runtime with containers using `docker run` instead of via image builds requires a related change in `containerd` (_min version required: 1.6.21 / 1.7.1_). That can be verified [as detailed here](https://github.com/containerd/containerd/pull/7566/files#r1189356383).
- Another [reproduction on limits that may be of interest](https://github.com/moby/moby/issues/38814#issuecomment-1483951375).

</details>

## Description for the changelog

Adjusted `RLIMIT_NOFILE` to more sensible defaults.


## A picture of a cute animal (not mandatory but encouraged)

![zootopia](https://i.pinimg.com/originals/22/72/7f/22727f2f26bd4217d191df9f10d6cee6.gif)
contrib/init/systemd/docker.service
[Unit]
Description=Docker Application Container Engine
Documentation=https://docs.docker.com
After=network-online.target docker.socket firewalld.service containerd.service time-set.target
Wants=network-online.target containerd.service
Requires=docker.socket

[Service]
Type=notify
# the default is not to use systemd for cgroups because the delegate issues still
# exists and systemd currently does not support the cgroup feature set required
# for containers run by docker
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
ExecReload=/bin/kill -s HUP $MAINPID
TimeoutStartSec=0
RestartSec=2
Restart=always

# Note that StartLimit* options were moved from "Service" to "Unit" in systemd 229.
# Both the old, and new location are accepted by systemd 229 and up, so using the old location
# to make them work for either version of systemd.
StartLimitBurst=3

# Note that StartLimitInterval was renamed to StartLimitIntervalSec in systemd 230.
# Both the old, and new name are accepted by systemd 230 and up, so using the old name to make
# this option work for either version of systemd.
StartLimitInterval=60s

# Having non-zero Limit*s causes performance problems due to accounting overhead
# in the kernel. We recommend using cgroups to do container-local accounting.
LimitNOFILE=infinity
LimitNPROC=infinity
LimitCORE=infinity

# Comment TasksMax if your systemd version does not support it.
# Only systemd 226 and above support this option.
TasksMax=infinity

# set delegate yes so that systemd does not reset the cgroups of docker containers
Delegate=yes

# kill only the docker process, not all processes in the cgroup
KillMode=process
OOMScoreAdjust=-500

[Install]
WantedBy=multi-user.target

[Unit]
Description=Docker Application Container Engine
Documentation=https://docs.docker.com
After=network-online.target docker.socket firewalld.service containerd.service time-set.target
Wants=network-online.target containerd.service
Requires=docker.socket

[Service]
Type=notify
# the default is not to use systemd for cgroups because the delegate issues still
# exists and systemd currently does not support the cgroup feature set required
# for containers run by docker
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
ExecReload=/bin/kill -s HUP $MAINPID
TimeoutStartSec=0
RestartSec=2
Restart=always

# Note that StartLimit* options were moved from "Service" to "Unit" in systemd 229.
# Both the old, and new location are accepted by systemd 229 and up, so using the old location
# to make them work for either version of systemd.
StartLimitBurst=3

# Note that StartLimitInterval was renamed to StartLimitIntervalSec in systemd 230.
# Both the old, and new name are accepted by systemd 230 and up, so using the old name to make
# this option work for either version of systemd.
StartLimitInterval=60s

# Having non-zero Limit*s causes performance problems due to accounting overhead
# in the kernel. We recommend using cgroups to do container-local accounting.
LimitNPROC=infinity
LimitCORE=infinity

# Comment TasksMax if your systemd version does not support it.
# Only systemd 226 and above support this option.
TasksMax=infinity

# set delegate yes so that systemd does not reset the cgroups of docker containers
Delegate=yes

# kill only the docker process, not all processes in the cgroup
KillMode=process
OOMScoreAdjust=-500

[Install]
WantedBy=multi-user.target

Dropping `LimitNOFILE` as requested

```suggestion
```