Restructured Documentation
Automatic Documentation Deployment / Sync Docs to https://kb.bunny-lab.io (push) Successful in 8s
Automatic Documentation Deployment / Sync Docs to https://kb.bunny-lab.io (push) Successful in 8s
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
---
|
||||
tags:
|
||||
- Containers
|
||||
- Docker
|
||||
- Containerization
|
||||
---
|
||||
|
||||
## Purpose
|
||||
This document will outline the general workflow of using Visual Studio Code to author and update custom containers and push them to a container registry hosted in Gitea. This will be referencing the `git-repo-updater` project throughout.
|
||||
|
||||
!!! note "Assumptions"
|
||||
This document assumes you are authoring the containers in Microsoft Windows, and does not include the fine-tuning necessary to work in Linux or MacOS environments. You are on your own if you want to author containers in Linux.
|
||||
|
||||
## Install Visual Studio Code
|
||||
The management of the Gitea repositories, Dockerfile building, and pushing container images to the Gitea container registry will all involve using just Visual Studio Code. You can download Visual Studio Code from this [direct download link](https://code.visualstudio.com/docs/?dv=win64user).
|
||||
|
||||
## Configure Required Docker Extensions
|
||||
You will need to locate and install the `Dev Containers`, `Docker`, and `WSL` extensions in Visual Studio Code to move forward. This may request that you install Docker Desktop onto your computer as part of the installation process. Proceed to do so, then when the Docker "Engine" is running, you can proceed to the next step.
|
||||
|
||||
!!! warning
|
||||
You need to have Docker Desktop "Engine" running whenever working with containers, as it is necessary to build the images. VSCode will complain if it is not running.
|
||||
|
||||
## Add Gitea Container Registry
|
||||
At this point, we need to add a registry to Visual Studio Code so it can proceed with pulling down the repository data.
|
||||
|
||||
- Click the Docker icon on the left-hand toolbar
|
||||
- Under "**Registries**", click "**Connect Registry...**"
|
||||
- In the dropdown menu that appears, click "**Generic Registry V2**"
|
||||
- Enter `https://git.bunny-lab.io/container-registry`
|
||||
- Registry Username: `nicole.rappe`
|
||||
- Registry Password or Personal Access Token: `Personal Access API Token You Generated in Gitea`
|
||||
- You will now see a sub-listing named "**Generic Registry V2**"
|
||||
- If you click the dropdown, you will see "**https://git.bunny-lab.io/container-registry**"
|
||||
- Under this section, you will see any containers in the registry that you have access to, in this case, you will see `container-registry/git-repo-updater`
|
||||
|
||||
## Add Source Control Repository
|
||||
Now it is time to pull down the repository where the container's core elements are stored on Gitea.
|
||||
|
||||
- Click the "**Source Control**" button on the left-hand menu then click the "**Clone Repository**" button
|
||||
- Enter `https://git.bunny-lab.io/container-registry/git-repo-updater.git`
|
||||
- Click the dropdown menu option "**Clone from URL**" then choose a location to locally store the repository on your computer
|
||||
- When prompted with "**Would you like to open the cloned repository**", click the "**Open**" button
|
||||
|
||||
## Making Changes
|
||||
You will be presented with four files in this specific repository. `.env`, `docker-compose.yml`, `Dockerfile`, and `repo_watcher.sh`
|
||||
|
||||
- `.env` is the environment variables passed to the container to tell it which ntfy server to talk to, which credentials to use with Gitea, and which repositories to download and push into production servers
|
||||
- `docker-compose.yml` is an example docker-compose file that can be used in Portainer to deploy the server along with the contents of the `.env` file
|
||||
- `Dockerfile` is the base of the container, telling docker what operating system to use and how to start the script in the container
|
||||
- `repo_watcher.sh` is the script called by the `Dockerfile` which loops checking for updates in Gitea repositories that were configured in the `.env` file
|
||||
|
||||
### Push to Repository
|
||||
When you make any changes, you will need to first commit them to the repository
|
||||
|
||||
- Save all of the edited files
|
||||
- Click the "**Source Control**" button in the toolbar
|
||||
- Write a message about what you changed in the commit description field
|
||||
- Click the "**Commit**" button
|
||||
- Click the "**Sync Changes**" button that appears
|
||||
- You may be presented with various dialogs, just click the equivalant of "**Yes/OK**" to each of them
|
||||
|
||||
### Build the Dockerfile
|
||||
At this point, we need to build the dockerfile, which takes all of the changes and packages it into a container image
|
||||
|
||||
- Navigate back to the file explorer inside of Visual Studio Code
|
||||
- Right-click the `Dockerfile`, then click "**Build Image...**"
|
||||
- In the "Tag Image As..." window, type in `git.bunny-lab.io/container-registry/git-repo-updater:latest`
|
||||
- When you navigate back to the Docker menu, you will see a new image appear under the "**Images**" section
|
||||
- You should see something similar to "Latest - X Seconds Ago` indicating this is the image you just built
|
||||
- Delete the older image(s) by right-clicking on them and selecting "**Remove...**"
|
||||
- Push the image to the container registry in Gitea by right-clicking the latest image, and selecting "**Push...**"
|
||||
- In the dropdown menu that appears, enter `git.bunny-lab.io/container-registry/git-repo-updater:latest`
|
||||
- You can confirm if it was successful by navigating to the [Gitea Container Webpage](https://git.bunny-lab.io/container-registry/-/packages/container/git-repo-updater/latest) and seeing if it says "**Published Now**" or "**Published 1 Minute Ago**"
|
||||
|
||||
!!! warning "CRLF End of Line Sequences"
|
||||
When you are editing files in the container's repository, you need to ensure that Visual Studio Code is editing that file in "**LF**" mode and not "**CRLF**". You can find this toggle at the bottom-right of the VSCode window. Simply clicking on the letters "**CRLF**" will let you toggle the file to "**LF**". If you do not make this change, the container will misunderstand the dockerfile and/or scripts inside of the container and have runtime errors.
|
||||
|
||||
## Deploy the Container
|
||||
You can now use the `.env` file along with the `docker-compose.yml` file inside of Portainer to deploy a stack using the container you just built / updated.
|
||||
|
||||
## Related Documentation
|
||||
- [Related Containers Documentation](<../../../reference/Containers/index.md>) — Find the connected deployments, procedures, and references for this subject.
|
||||
@@ -0,0 +1,31 @@
|
||||
---
|
||||
tags:
|
||||
- Docker
|
||||
- Macvlan
|
||||
- Networking
|
||||
---
|
||||
|
||||
## Purpose
|
||||
You may find that you only have one network adapter on a server / VM and need to have multiple virtual networks associated with it. For example, Home Assistant exists on the `192.168.3.0/24` network but it needs to also access devices on the `192.168.4.0/24` surveillance network. To facilitate this, we will make a MACVLAN Sub-Interface. This will make a virtual interface that is parented to the actual physical interface.
|
||||
|
||||
!!! info "Assumptions"
|
||||
It is assumed that you are running Rocky Linux (or CentOS / RedHat).
|
||||
|
||||
## Create the Permanent Sub-Interface
|
||||
You will begin with making a new interface, it will have the name `macvlan0`.
|
||||
|
||||
```sh
|
||||
nmcli connection add type macvlan ifname surveillance dev ens18 mode bridge ipv4.method manual ipv4.addresses 192.168.4.100/24
|
||||
nmcli connection up macvlan-surveillance
|
||||
nmcli connection show
|
||||
```
|
||||
|
||||
## Bind a Docker Network to the Sub-Interface
|
||||
Now you need to run the following command to allow docker to use this interface for the `surveillance_network`
|
||||
|
||||
```sh
|
||||
docker network create -d macvlan --subnet=192.168.4.0/24 --gateway=192.168.4.1 -o parent=surveillance surveillance_network
|
||||
```
|
||||
|
||||
## Related Documentation
|
||||
- [Related Containers Documentation](<../../../reference/Containers/index.md>) — Find the connected deployments, procedures, and references for this subject.
|
||||
@@ -0,0 +1,60 @@
|
||||
---
|
||||
tags:
|
||||
- Containers
|
||||
- Docker
|
||||
- Bash
|
||||
- Scripting
|
||||
- Linux
|
||||
---
|
||||
|
||||
## Purpose
|
||||
If you find that you need to migrate a container, along with any supporting files, permissions, etc from an old server to a new server, rsync helps make this as painless as possible.
|
||||
Be sure to perform the following steps to make sure that you can copy the container's files.
|
||||
|
||||
!!! warning
|
||||
You need to stop the running containers on the old server before copying their data over, otherwise the state of the data may be unstable. Once you have migrated the data, you can spin up the containers on the new server and confirm they work before deleting the data on the old server.
|
||||
|
||||
On the destination (new) server, the directory needs to exist and be writable via the person copying the data over SSH:
|
||||
|
||||
## Copying Data Between the Old and New Servers
|
||||
=== "Safe Method"
|
||||
|
||||
```sh
|
||||
sudo mkdir -p /srv/containers/example
|
||||
sudo chmod 740 /srv/containers/example
|
||||
sudo chown nicole:nicole /srv/containers/example
|
||||
```
|
||||
|
||||
=== "Quick & Dirty Method"
|
||||
|
||||
```sh
|
||||
sudo mkdir -p /srv/containers
|
||||
sudo chmod 777 /srv/containers
|
||||
```
|
||||
|
||||
On the source (old) server, perform an rsync over to the new server, authenticating yourself as you will be prompted to do so:
|
||||
|
||||
=== "Safe Method"
|
||||
|
||||
```sh
|
||||
rsync -avz -e ssh --progress /srv/containers/example/* nicole@192.168.3.30:/srv/containers/example
|
||||
```
|
||||
|
||||
=== "Quick & Dirty Method"
|
||||
|
||||
```sh
|
||||
rsync -avz -e ssh --progress /srv/containers/example nicole@192.168.3.30:/srv/containers
|
||||
```
|
||||
|
||||
=== "Quick & Dirty w/ Provided SSH Key Method"
|
||||
This method assumes that you have the private key for your SSH-based authentication locally on the server somewhere safe with permissions `chmod 600` applied to it. In this example, I placed the private key at `/tmp/id_rsa_OpenSSH`.
|
||||
|
||||
```sh
|
||||
rsync -avz -e "ssh -i /tmp/id_rsa_OpenSSH" --progress /srv/containers/pihole nicole@192.168.3.62:/srv/containers
|
||||
```
|
||||
|
||||
## Spinning Up Docker / Portainer Stack
|
||||
Once everything has been moved over, copy the `docker-compose` and `.env` (environment variables) from the old server to the new one, pointing to the same location since we maintained the same folder structure, and the container should spin up like nothing ever happened.
|
||||
|
||||
## Related Documentation
|
||||
- [Related Containers Documentation](<../../../reference/Containers/index.md>) — Find the connected deployments, procedures, and references for this subject.
|
||||
@@ -0,0 +1,277 @@
|
||||
---
|
||||
tags:
|
||||
- Kubernetes
|
||||
- Docker
|
||||
- Containerization
|
||||
---
|
||||
|
||||
## Purpose
|
||||
Convert the example Docker Compose workload into Kubernetes manifests and expose it through the documented Rancher RKE2 and Traefik environment.
|
||||
|
||||
!!! info "RKE2 Cluster Deployment"
|
||||
This document assumes that you have an existing Rancher RKE2 cluster deployed. If not, you can deploy one following the [Deploy RKE2 Cluster](<../../../deployments/Containers/Kubernetes/Rancher RKE2.md>) documentation.
|
||||
|
||||
We also assume that the cluster name within Rancher RKE2 is named `local`, which is the default cluster name when setting up a Kubernetes Cluster in the way seen in the above documentation.
|
||||
|
||||
## Installing Kompose
|
||||
The first step involves downloading Kompose from https://kompose.io/installation. Once you have it downloaded and installed onto your environment of choice, save a copy of your `docker-compose.yml` file somewhere on-disk, then open up a terminal and run the following command:
|
||||
|
||||
```sh
|
||||
kompose --file docker-compose.yaml convert --stdout > ntfy-k8s.yaml
|
||||
```
|
||||
|
||||
This will attempt to convert the `docker-compose.yml` file into a Kubernetes manifest YAML file. The Before and after example can be seen below:
|
||||
|
||||
=== "(Original) docker-compose.yml"
|
||||
|
||||
```yaml
|
||||
version: "2.1"
|
||||
services:
|
||||
ntfy:
|
||||
image: binwiederhier/ntfy
|
||||
container_name: ntfy
|
||||
command:
|
||||
- serve
|
||||
environment:
|
||||
- NTFY_ATTACHMENT_CACHE_DIR=/var/lib/ntfy/attachments
|
||||
- NTFY_BASE_URL=https://ntfy.bunny-lab.io
|
||||
- TZ=America/Denver # optional: Change to your desired timezone
|
||||
#user: UID:GID # optional: Set custom user/group or uid/gid
|
||||
volumes:
|
||||
- /srv/containers/ntfy/cache:/var/cache/ntfy
|
||||
- /srv/containers/ntfy/etc:/etc/ntfy
|
||||
ports:
|
||||
- 80:80
|
||||
restart: always
|
||||
networks:
|
||||
docker_network:
|
||||
ipv4_address: 192.168.5.45
|
||||
|
||||
networks:
|
||||
default:
|
||||
external:
|
||||
name: docker_network
|
||||
docker_network:
|
||||
external: true
|
||||
```
|
||||
|
||||
=== "(Converted) ntfy-k8s.yaml"
|
||||
|
||||
```yaml
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
annotations:
|
||||
kompose.cmd: C:\ProgramData\chocolatey\lib\kubernetes-kompose\tools\kompose.exe --file ntfy-k8s.yaml convert --stdout
|
||||
kompose.version: 1.37.0 (fb0539e64)
|
||||
labels:
|
||||
io.kompose.service: ntfy
|
||||
name: ntfy
|
||||
spec:
|
||||
ports:
|
||||
- name: "80"
|
||||
port: 80
|
||||
targetPort: 80
|
||||
selector:
|
||||
io.kompose.service: ntfy
|
||||
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
annotations:
|
||||
kompose.cmd: C:\ProgramData\chocolatey\lib\kubernetes-kompose\tools\kompose.exe --file ntfy-k8s.yaml convert --stdout
|
||||
kompose.version: 1.37.0 (fb0539e64)
|
||||
labels:
|
||||
io.kompose.service: ntfy
|
||||
name: ntfy
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
io.kompose.service: ntfy
|
||||
strategy:
|
||||
type: Recreate
|
||||
template:
|
||||
metadata:
|
||||
annotations:
|
||||
kompose.cmd: C:\ProgramData\chocolatey\lib\kubernetes-kompose\tools\kompose.exe --file ntfy-k8s.yaml convert --stdout
|
||||
kompose.version: 1.37.0 (fb0539e64)
|
||||
labels:
|
||||
io.kompose.service: ntfy
|
||||
spec:
|
||||
containers:
|
||||
- args:
|
||||
- serve
|
||||
env:
|
||||
- name: NTFY_ATTACHMENT_CACHE_DIR
|
||||
value: /var/lib/ntfy/attachments
|
||||
- name: NTFY_BASE_URL
|
||||
value: https://ntfy.bunny-lab.io
|
||||
- name: TZ
|
||||
value: America/Denver
|
||||
image: binwiederhier/ntfy
|
||||
name: ntfy
|
||||
ports:
|
||||
- containerPort: 80
|
||||
protocol: TCP
|
||||
volumeMounts:
|
||||
- mountPath: /var/cache/ntfy
|
||||
name: ntfy-claim0
|
||||
- mountPath: /etc/ntfy
|
||||
name: ntfy-claim1
|
||||
restartPolicy: Always
|
||||
volumes:
|
||||
- name: ntfy-claim0
|
||||
persistentVolumeClaim:
|
||||
claimName: ntfy-claim0
|
||||
- name: ntfy-claim1
|
||||
persistentVolumeClaim:
|
||||
claimName: ntfy-claim1
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
labels:
|
||||
io.kompose.service: ntfy-claim0
|
||||
name: ntfy-claim0
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
resources:
|
||||
requests:
|
||||
storage: 100Mi
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
labels:
|
||||
io.kompose.service: ntfy-claim1
|
||||
name: ntfy-claim1
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
resources:
|
||||
requests:
|
||||
storage: 100Mi
|
||||
```
|
||||
|
||||
## Deploy Workload into Rancher RKE2 Cluster
|
||||
At this point, you need to import the yaml file you created into the Kubernetes cluster. This will occur in four sequential stages:
|
||||
|
||||
- Setting up a "**Project**" to logically organize your containers
|
||||
- Setting up a "**Namespace**" for your container to isolate it from other containers in your Kubernetes cluster
|
||||
- Importing the YAML file into the aforementioned namespace
|
||||
- Configuring Ingress to allow external access to the container / service stack.
|
||||
|
||||
### Create a Project
|
||||
The purpose of the project is to logically organize your services together. This can be something like `Home Automation`, `Log Analysis Systems`, `Network Tools`, etc. You can do this by logging into your Rancher RKE2 cluster (e.g. https://rke2-cluster.bunny-lab.io). This Project name is unique to Rancher and purely used for organizational purposes and does not affect the namespaces / containers in any way.
|
||||
|
||||
- Navigate to: **Clusters > `local` > Cluster > Projects/Namespaces > "Create Project"**
|
||||
- **Name**: <Friendly Name> (e.g. `Home Automation`)
|
||||
- **Description**: <Useful Description for the Group of Services> (e.g. `Various services that automate things within Bunny Lab`)
|
||||
- Click the "**Create**" button
|
||||
|
||||
### Create a Namespace within the Project
|
||||
At this point, we need to create a namespace. This basically isolates the networking, credentials, secrets, and storage between the services/stacks. This ensures that if someone exploits one of your services, they will not be able to laterally move into another service within the same Kubernetes cluster.
|
||||
|
||||
- Navigate to: **Clusters > `local` > Cluster > Projects/Namespaces > <ProjectName> > "Create Namespace"**
|
||||
- The name for the namespace should be named based on its operational-context, such as `prod-ntfy` or `dev-ntfy`.
|
||||
|
||||
### Import Converted YAML Manifest into Namespace
|
||||
At this point, we can now proceed to import the YAML file we generated in the beginning of this document.
|
||||
|
||||
- Navigate to: **Clusters > `local` > Cluster > Projects/Namespaces**
|
||||
- At the top-right of the screen will be an upload / up-arrow button with tooltip text stating "Import YAML" > Click on this button
|
||||
- Click the "**Read from File**" button
|
||||
- Navigate to your `ntfy-k8s.yaml` file. (Name will differ from your actual converted file) > then click the "**Open**" button.
|
||||
- On the top-right of the dialog box will be a "**Default Namespace**" dropdown menu, select the `prod-ntfy` namespace we created earlier.
|
||||
- Click the blue "**Import** button at the bottom of the dialog box.
|
||||
|
||||
!!! warning "Be Patient"
|
||||
This part of the process can take a while depending on the container stack and complexity of the service. It has to download container images and deploy them into newly spun-up pods within Kubernetes. Just be patient and click on the `prod-ntfy` namespace, then look at the "**Workloads**" tab to see if the "ntfy" service exists and is Active, then you can move onto the next step.
|
||||
|
||||
### Configuring Ingress
|
||||
This final step within Kubernetes itself involves reconfiguring the container to list via a "NodePort" instead of "ClusterIP". Don't worry, you do not have to mangle with the ports that the container uses, this is entirely within Kubernetes itself and does not make changes to the original `docker-compose.yml` ports of the container(s) you imported.
|
||||
|
||||
- Navigate to: **Clusters > `local` > Service Discovery > Services > ntfy**
|
||||
- On the top-right, click on the blue "**Show Configuration**" button
|
||||
- On the bottom-right, click the blue "**Edit Config**" button
|
||||
- On the bottom-right, click the "**Edit as YAML**" button
|
||||
- Within the yaml editor, you will see a section named `spec:`, within that section is a subsection named `type:`. You will see a value of `type: ClusterIP` > You want to change that to `type: NodePort`
|
||||
- On the bottom-right, click the blue "**Save**" button and wait for the process to finish.
|
||||
- On the new page that appears, click on the `ntfy` service again
|
||||
- Click on the "**Ports**" tab
|
||||
- You will see a column of the table labeled "Node Port" with a number in the 30,000s such as `30996`. This will be important for later.
|
||||
|
||||
!!! success "Verifying Access Before Configuring Reverse Proxy"
|
||||
At this point, you will want to verify that you can access the service via the cluster node IP addresses such as the examples seen below, all of the cluster nodes should route the traffic to the container's service and will be used for load-balancing later in the reverse proxy configuration file.
|
||||
|
||||
- http://192.168.3.69:30996
|
||||
- http://192.168.3.70:30996
|
||||
- http://192.168.3.71:30996
|
||||
- http://192.168.3.72:30996
|
||||
|
||||
## Configuring Reverse Proxy
|
||||
If you were able to successfully verify access to the service when talking to it directly via one of the cluster node IP addresses with its given Node Port port number, then you can proceed to creating a reverse proxy configuration file for the service. This will be very similar to the original `docker-compose.yml` version of the reverse proxy configuration file, but with additional IP addresses to load-balance across the Kubernetes cluster nodes.
|
||||
|
||||
!!! info "Section Considerations"
|
||||
This section of the document does not (*currently*) cover the process of setting up health checks to ensure that the load-balanced server destinations in the reverse proxy are online before redirecting traffic to them. This is on my to-do list of things to implement to further harden the deployment process.
|
||||
|
||||
This section also does not cover the process of setting up a reverse proxy. If you want to follow along with this document, you can deploy a Traefik reverse proxy via the [Traefik](<../../../deployments/Networking and Access/Reverse Proxies/Traefik.md>) deployment documentation.
|
||||
|
||||
With the above considerations in-mind, we just need to make some small changes to the existing Traefik configuration file to ensure that it load-balanced across every node of the cluster to ensure high-availability functions as-expected.
|
||||
|
||||
=== "(Original) ntfy.bunny-lab.io.yml"
|
||||
|
||||
```yaml
|
||||
http:
|
||||
routers:
|
||||
ntfy:
|
||||
entryPoints:
|
||||
- websecure
|
||||
tls:
|
||||
certResolver: letsencrypt
|
||||
service: ntfy
|
||||
rule: Host(`ntfy.bunny-lab.io`)
|
||||
|
||||
services:
|
||||
ntfy:
|
||||
loadBalancer:
|
||||
passHostHeader: true
|
||||
servers:
|
||||
- url: http://192.168.5.45:80
|
||||
```
|
||||
|
||||
=== "(Updated) ntfy.bunny-lab.io.yml"
|
||||
|
||||
```yaml
|
||||
http:
|
||||
routers:
|
||||
ntfy:
|
||||
entryPoints:
|
||||
- websecure
|
||||
tls:
|
||||
certResolver: letsencrypt
|
||||
service: ntfy
|
||||
rule: Host(`ntfy.bunny-lab.io`)
|
||||
|
||||
services:
|
||||
ntfy:
|
||||
loadBalancer:
|
||||
passHostHeader: true
|
||||
servers:
|
||||
- url: http://192.168.3.69:30996
|
||||
- url: http://192.168.3.70:30996
|
||||
- url: http://192.168.3.71:30996
|
||||
- url: http://192.168.3.72:30996
|
||||
```
|
||||
|
||||
!!! success "Verify Access via Reverse Proxy"
|
||||
If everything worked, you should be able to access the service at https://ntfy.bunny-lab.io, and if one of the cluster nodes goes offline, Rancher will automatically migrate the load to another cluster node which will take over the web request.
|
||||
|
||||
## Related Documentation
|
||||
- [Related Containers Documentation](<../../../reference/Containers/index.md>) — Find the connected deployments, procedures, and references for this subject.
|
||||
@@ -0,0 +1,17 @@
|
||||
---
|
||||
tags:
|
||||
- Containers
|
||||
- Workflows
|
||||
- Documentation
|
||||
---
|
||||
|
||||
# Containers
|
||||
## Purpose
|
||||
Find workflows for containers. Follow the subject guide to choose the relevant environment and connect this material to the other document types.
|
||||
|
||||
## Includes
|
||||
- Docker
|
||||
- Kubernetes
|
||||
|
||||
## Follow the Subject
|
||||
[Containers](<../../reference/Containers/index.md>) explains the relationships and offers starting points for the documented tasks.
|
||||
Reference in New Issue
Block a user