mirror of
https://github.com/jpetazzo/container.training.git
synced 2026-08-19 03:56:24 +00:00
Merge branch 'main' into 2021-02-enix
This commit is contained in:
@@ -1 +1,3 @@
|
||||
INFRACLASS=scaleway
|
||||
#SCW_INSTANCE_TYPE=DEV1-L
|
||||
#SCW_ZONE=fr-par-2
|
||||
|
||||
@@ -5,6 +5,9 @@ if ! [ -f ~/.config/scw/config.yaml ]; then
|
||||
warn "~/.config/scw/config.yaml not found."
|
||||
fi
|
||||
|
||||
SCW_INSTANCE_TYPE=${SCW_INSTANCE_TYPE-DEV1-M}
|
||||
SCW_ZONE=${SCW_ZONE-fr-par-1}
|
||||
|
||||
infra_list() {
|
||||
scw instance server list -o json |
|
||||
jq -r '.[] | [.id, .name, .state, .commercial_type] | @tsv'
|
||||
@@ -13,9 +16,6 @@ infra_list() {
|
||||
infra_start() {
|
||||
COUNT=$1
|
||||
|
||||
SCW_INSTANCE_TYPE=${SCW_INSTANCE_TYPE-DEV1-M}
|
||||
SCW_ZONE=${SCW_ZONE-fr-par-1}
|
||||
|
||||
for I in $(seq 1 $COUNT); do
|
||||
NAME=$(printf "%s-%03d" $TAG $I)
|
||||
sep "Starting instance $I/$COUNT"
|
||||
@@ -36,16 +36,16 @@ infra_stop() {
|
||||
scw_get_ids_by_tag $TAG | wc -l
|
||||
info "Deleting instances..."
|
||||
scw_get_ids_by_tag $TAG |
|
||||
xargs -n1 -P10 -I@@ \
|
||||
scw instance server delete force-shutdown=true server-id=@@
|
||||
xargs -n1 -P10 \
|
||||
scw instance server delete zone=${SCW_ZONE} force-shutdown=true with-ip=true
|
||||
}
|
||||
|
||||
scw_get_ids_by_tag() {
|
||||
TAG=$1
|
||||
scw instance server list name=$TAG -o json | jq -r .[].id
|
||||
scw instance server list zone=${SCW_ZONE} name=$TAG -o json | jq -r .[].id
|
||||
}
|
||||
|
||||
scw_get_ips_by_tag() {
|
||||
TAG=$1
|
||||
scw instance server list name=$TAG -o json | jq -r .[].public_ip.address
|
||||
scw instance server list zone=${SCW_ZONE} name=$TAG -o json | jq -r .[].public_ip.address
|
||||
}
|
||||
|
||||
+46
-48
@@ -2,11 +2,11 @@
|
||||
"""
|
||||
There are two ways to use this script:
|
||||
|
||||
1. Pass a tag name as a single argument.
|
||||
It will then take the clusters corresponding to that tag, and assign one
|
||||
domain name per cluster. Currently it gets the domains from a hard-coded
|
||||
path. There should be more domains than clusters.
|
||||
Example: ./map-dns.py 2020-08-15-jp
|
||||
1. Pass a file name and a tag name as a single argument.
|
||||
It will load a list of domains from the given file (one per line),
|
||||
and assign them to the clusters corresponding to that tag.
|
||||
There should be more domains than clusters.
|
||||
Example: ./map-dns.py domains.txt 2020-08-15-jp
|
||||
|
||||
2. Pass a domain as the 1st argument, and IP addresses then.
|
||||
It will configure the domain with the listed IP addresses.
|
||||
@@ -19,55 +19,53 @@ import requests
|
||||
import sys
|
||||
import yaml
|
||||
|
||||
# configurable stuff
|
||||
domains_file = "../../plentydomains/domains.txt"
|
||||
# This can be tweaked if necessary.
|
||||
config_file = os.path.join(
|
||||
os.environ["HOME"], ".config/gandi/config.yaml")
|
||||
tag = None
|
||||
os.environ["HOME"], ".config/gandi/config.yaml")
|
||||
apiurl = "https://dns.api.gandi.net/api/v5/domains"
|
||||
|
||||
if len(sys.argv) == 2:
|
||||
tag = sys.argv[1]
|
||||
domains = open(domains_file).read().split()
|
||||
domains = [ d for d in domains if not d.startswith('#') ]
|
||||
ips = open(f"tags/{tag}/ips.txt").read().split()
|
||||
settings_file = f"tags/{tag}/settings.yaml"
|
||||
clustersize = yaml.safe_load(open(settings_file))["clustersize"]
|
||||
else:
|
||||
domains = [sys.argv[1]]
|
||||
ips = sys.argv[2:]
|
||||
clustersize = len(ips)
|
||||
|
||||
# inferred stuff
|
||||
apikey = yaml.safe_load(open(config_file))["apirest"]["key"]
|
||||
|
||||
# now do the fucking work
|
||||
while domains and ips:
|
||||
domain = domains[0]
|
||||
domains = domains[1:]
|
||||
cluster = ips[:clustersize]
|
||||
ips = ips[clustersize:]
|
||||
print(f"{domain} => {cluster}")
|
||||
zone = ""
|
||||
node = 0
|
||||
for ip in cluster:
|
||||
node += 1
|
||||
zone += f"@ 300 IN A {ip}\n"
|
||||
zone += f"* 300 IN A {ip}\n"
|
||||
zone += f"node{node} 300 IN A {ip}\n"
|
||||
r = requests.put(
|
||||
f"{apiurl}/{domain}/records",
|
||||
headers={"x-api-key": apikey},
|
||||
data=zone)
|
||||
print(r.text)
|
||||
# Figure out if we're called for a bunch of domains, or just one.
|
||||
first_arg = sys.argv[1]
|
||||
if os.path.isfile(first_arg):
|
||||
domains = open(first_arg).read().split()
|
||||
domains = [ d for d in domains if not d.startswith('#') ]
|
||||
tag = sys.argv[2]
|
||||
ips = open(f"tags/{tag}/ips.txt").read().split()
|
||||
settings_file = f"tags/{tag}/settings.yaml"
|
||||
clustersize = yaml.safe_load(open(settings_file))["clustersize"]
|
||||
else:
|
||||
domains = [first_arg]
|
||||
ips = sys.argv[2:]
|
||||
clustersize = len(ips)
|
||||
|
||||
#r = requests.get(
|
||||
# f"{apiurl}/{domain}/records",
|
||||
# headers={"x-api-key": apikey},
|
||||
# )
|
||||
# Now, do the work.
|
||||
while domains and ips:
|
||||
domain = domains[0]
|
||||
domains = domains[1:]
|
||||
cluster = ips[:clustersize]
|
||||
ips = ips[clustersize:]
|
||||
print(f"{domain} => {cluster}")
|
||||
zone = ""
|
||||
node = 0
|
||||
for ip in cluster:
|
||||
node += 1
|
||||
zone += f"@ 300 IN A {ip}\n"
|
||||
zone += f"* 300 IN A {ip}\n"
|
||||
zone += f"node{node} 300 IN A {ip}\n"
|
||||
r = requests.put(
|
||||
f"{apiurl}/{domain}/records",
|
||||
headers={"x-api-key": apikey},
|
||||
data=zone)
|
||||
print(r.text)
|
||||
|
||||
#r = requests.get(
|
||||
# f"{apiurl}/{domain}/records",
|
||||
# headers={"x-api-key": apikey},
|
||||
# )
|
||||
|
||||
if domains:
|
||||
print(f"Good, we have {len(domains)} domains left.")
|
||||
print(f"Good, we have {len(domains)} domains left.")
|
||||
|
||||
if ips:
|
||||
print(f"Crap, we have {len(ips)} IP addresses left.")
|
||||
print(f"Crap, we have {len(ips)} IP addresses left.")
|
||||
|
||||
@@ -58,27 +58,24 @@
|
||||
|
||||
*probably aggregation layer*
|
||||
|
||||
|
||||
---
|
||||
|
||||
## How are resources organized?
|
||||
|
||||
- Let's have a look at the Kubernetes API hierarchical structure
|
||||
|
||||
- Useful: `.metadata.selfLink` contains the URI of a resource
|
||||
- We'll ask `kubectl` to show us the exacts requests that it's making
|
||||
|
||||
.exercise[
|
||||
|
||||
- Check the `apiVersion` and URI of a "core" resource, e.g. a Node:
|
||||
- Check the URI for a cluster-scope, "core" resource, e.g. a Node:
|
||||
```bash
|
||||
kubectl get nodes -o json | jq .items[0].apiVersion
|
||||
kubectl get nodes -o json | jq .items[0].metadata.selfLink
|
||||
kubectl -v6 get node node1
|
||||
```
|
||||
|
||||
- Get the `apiVersion` and URI for a "non-core" resource, e.g. a ClusterRole:
|
||||
- Check the URI for a cluster-scope, "non-core" resource, e.g. a ClusterRole:
|
||||
```bash
|
||||
kubectl get clusterrole view -o json | jq .apiVersion
|
||||
kubectl get clusterrole view -o json | jq .metadata.selfLink
|
||||
kubectl -v6 get clusterrole view
|
||||
```
|
||||
|
||||
]
|
||||
@@ -123,6 +120,17 @@ class: extra-details
|
||||
|
||||
## Namespaced resources
|
||||
|
||||
- What about namespaced resources?
|
||||
|
||||
.exercise[
|
||||
|
||||
- Check the URI for a namespaced, "core" resource, e.g. a Service:
|
||||
```bash
|
||||
kubectl -v6 get service kubernetes --namespace default
|
||||
```
|
||||
|
||||
]
|
||||
|
||||
- Here are what namespaced resources URIs look like:
|
||||
|
||||
```
|
||||
@@ -168,7 +176,7 @@ class: extra-details
|
||||
kubectl get pods --namespace=kube-system --selector=k8s-app=kube-proxy
|
||||
PODNAME=$(
|
||||
kubectl get pods --namespace=kube-system --selector=k8s-app=kube-proxy \
|
||||
-o json | jq .items[0].metadata.name)
|
||||
-o json | jq -r .items[0].metadata.name)
|
||||
```
|
||||
|
||||
- Execute a command in a pod, showing the API requests:
|
||||
|
||||
+30
-10
@@ -60,21 +60,41 @@
|
||||
|
||||
## Command-line arguments
|
||||
|
||||
- Pass options to `args` array in the container specification
|
||||
- Indicate what should run in the container
|
||||
|
||||
- Example ([source](https://github.com/coreos/pods/blob/master/kubernetes.yaml#L29)):
|
||||
- Pass `command` and/or `args` in the container options in a Pod's template
|
||||
|
||||
- Both `command` and `args` are arrays
|
||||
|
||||
- Example ([source](https://github.com/jpetazzo/container.training/blob/main/k8s/consul-1.yaml#L70)):
|
||||
```yaml
|
||||
args:
|
||||
- "--data-dir=/var/lib/etcd"
|
||||
- "--advertise-client-urls=http://127.0.0.1:2379"
|
||||
- "--listen-client-urls=http://127.0.0.1:2379"
|
||||
- "--listen-peer-urls=http://127.0.0.1:2380"
|
||||
- "--name=etcd"
|
||||
args:
|
||||
- "agent"
|
||||
- "-bootstrap-expect=3"
|
||||
- "-retry-join=provider=k8s label_selector=\"app=consul\" namespace=\"$(NS)\""
|
||||
- "-client=0.0.0.0"
|
||||
- "-data-dir=/consul/data"
|
||||
- "-server"
|
||||
- "-ui"
|
||||
```
|
||||
|
||||
- The options can be passed directly to the program that we run ...
|
||||
---
|
||||
|
||||
... or to a wrapper script that will use them to e.g. generate a config file
|
||||
## `args` or `command`?
|
||||
|
||||
- Use `command` to override the `ENTRYPOINT` defined in the image
|
||||
|
||||
- Use `args` to keep the `ENTRYPOINT` defined in the image
|
||||
|
||||
(the parameters specified in `args` are added to the `ENTRYPOINT`)
|
||||
|
||||
- In doubt, use `command`
|
||||
|
||||
- It is also possible to use *both* `command` and `args`
|
||||
|
||||
(they will be strung together, just like `ENTRYPOINT` and `CMD`)
|
||||
|
||||
- See the [docs](https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#notes) to see how they interact together
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ Level 2: make it so that the number of replicas can be set with `--set replicas=
|
||||
|
||||
Level 3: change the colors of the lego bricks.
|
||||
|
||||
(For level 3, fork the repository and use ctr.run to build images.)
|
||||
(For level 3, you'll have to build/push your own images.)
|
||||
|
||||
See next slide if you need hints!
|
||||
|
||||
@@ -44,20 +44,12 @@ Also add `replicas: 5` to `values.yaml` to provide a default value.
|
||||
|
||||
## Changing the color
|
||||
|
||||
- Fork the repository
|
||||
- Create an account on e.g. Docker Hub (e.g. `janedoe`)
|
||||
|
||||
- Make sure that your fork has valid Dockerfiles
|
||||
|
||||
(or identify a branch that has valid Dockerfiles)
|
||||
|
||||
- Use the following images:
|
||||
|
||||
ctr.run/yourgithubusername/wordsmith/db:branchname
|
||||
|
||||
(replace db with web and words for the other components)
|
||||
- Create an image repository (e.g. `janedoe/web`)
|
||||
|
||||
- Change the images and/or CSS in `web/static`
|
||||
|
||||
- Commit, push, trigger a rolling update
|
||||
- Build and push
|
||||
|
||||
(`imagePullPolicy` should be `Always`, which is the default)
|
||||
- Trigger a rolling update using the image you just pushed
|
||||
|
||||
@@ -244,7 +244,7 @@ fine for personal and development clusters.)
|
||||
|
||||
- Add the `stable` repo:
|
||||
```bash
|
||||
helm repo add stable https://kubernetes-charts.storage.googleapis.com/
|
||||
helm repo add stable https://charts.helm.sh/stable
|
||||
```
|
||||
|
||||
]
|
||||
@@ -255,6 +255,22 @@ It's OK to add a repo that already exists (it will merely update it).
|
||||
|
||||
---
|
||||
|
||||
class: extra-details
|
||||
|
||||
## Deprecation warning
|
||||
|
||||
- That "stable" is being deprecated, in favor of a more decentralized approach
|
||||
|
||||
(each community / company / group / project hosting their own repository)
|
||||
|
||||
- We're going to use it here for educational purposes
|
||||
|
||||
- But if you're looking for production-grade charts, look elsewhere!
|
||||
|
||||
(namely, on the Helm Hub)
|
||||
|
||||
---
|
||||
|
||||
## Search available charts
|
||||
|
||||
- We can search available charts with `helm search`
|
||||
|
||||
+17
-17
@@ -72,7 +72,7 @@
|
||||
|
||||
- Deploy DockerCoins, and scale up the `worker` Deployment:
|
||||
```bash
|
||||
kubectl apply -f ~/container.training/k8/dockercoins.yaml
|
||||
kubectl apply -f ~/container.training/k8s/dockercoins.yaml
|
||||
kubectl scale deployment worker --replicas=10
|
||||
```
|
||||
|
||||
@@ -118,7 +118,7 @@
|
||||
|
||||
- Deploy `httplat`:
|
||||
```bash
|
||||
kubectl create deployment httplat -- httplat http://rng/
|
||||
kubectl create deployment httplat --image=jpetazzo/httplat -- httplat http://rng/
|
||||
```
|
||||
|
||||
- Expose it:
|
||||
@@ -512,20 +512,20 @@ no custom metrics API (custom.metrics.k8s.io) registered
|
||||
Here is the rule that we need to add to the configuration:
|
||||
|
||||
```yaml
|
||||
- seriesQuery: |
|
||||
httplat_latency_seconds_sum{kubernetes_namespace!="",kubernetes_name!=""}
|
||||
resources:
|
||||
overrides:
|
||||
kubernetes_namespace:
|
||||
resource: namespace
|
||||
kubernetes_name:
|
||||
resource: service
|
||||
name:
|
||||
matches: "httplat_latency_seconds_sum"
|
||||
as: "httplat_latency_seconds"
|
||||
metricsQuery: |
|
||||
rate(httplat_latency_seconds_sum{<<.LabelMatchers>>}[2m])
|
||||
/rate(httplat_latency_seconds_count{<<.LabelMatchers>>}[2m])
|
||||
- seriesQuery: |
|
||||
httplat_latency_seconds_sum{kubernetes_namespace!="",kubernetes_name!=""}
|
||||
resources:
|
||||
overrides:
|
||||
kubernetes_namespace:
|
||||
resource: namespace
|
||||
kubernetes_name:
|
||||
resource: service
|
||||
name:
|
||||
matches: "httplat_latency_seconds_sum"
|
||||
as: "httplat_latency_seconds"
|
||||
metricsQuery: |
|
||||
rate(httplat_latency_seconds_sum{<<.LabelMatchers>>}[2m])
|
||||
/rate(httplat_latency_seconds_count{<<.LabelMatchers>>}[2m])
|
||||
```
|
||||
|
||||
(I built it following the [walkthrough](https://github.com/DirectXMan12/k8s-prometheus-adapter/blob/master/docs/config-walkthrough.md
|
||||
@@ -636,7 +636,7 @@ kubectl get --raw /apis/custom.metrics.k8s.io/v1beta1
|
||||
Check that our `httplat` metrics are available:
|
||||
```bash
|
||||
kubectl get --raw /apis/custom.metrics.k8s.io/v1beta1\
|
||||
/namespaces/coins/services/httplat/httplat_latency_seconds
|
||||
/namespaces/customscaling/services/httplat/httplat_latency_seconds
|
||||
```
|
||||
|
||||
Also check the logs of the `prometheus-adapter` and the `kube-controller-manager`.
|
||||
|
||||
@@ -68,6 +68,128 @@
|
||||
|
||||
“Ah yes, this secret is a ...”
|
||||
|
||||
---
|
||||
|
||||
## Accessing private repositories
|
||||
|
||||
- Let's see how to access an image on private registry!
|
||||
|
||||
- These images are protected by a username + password
|
||||
|
||||
(on some registries, it's token + password, but it's the same thing)
|
||||
|
||||
- To access a private image, we need to:
|
||||
|
||||
- create a secret
|
||||
|
||||
- reference that secret in a Pod template
|
||||
|
||||
- or reference that secret in a ServiceAccount used by a Pod
|
||||
|
||||
---
|
||||
|
||||
## In practice
|
||||
|
||||
- Let's try to access an image on a private registry!
|
||||
|
||||
- image = docker-registry.enix.io/jpetazzo/private:latest
|
||||
- user = reader
|
||||
- password = VmQvqdtXFwXfyy4Jb5DR
|
||||
|
||||
.exercise[
|
||||
|
||||
- Create a Deployment using that image:
|
||||
```bash
|
||||
kubectl create deployment priv \
|
||||
--image=docker-registry.enix.io/jpetazzo/private
|
||||
```
|
||||
|
||||
- Check that the Pod won't start:
|
||||
```bash
|
||||
kubectl get pods --selector=app=priv
|
||||
```
|
||||
|
||||
]
|
||||
|
||||
---
|
||||
|
||||
## Creating a secret
|
||||
|
||||
- Let's create a secret with the information provided earlier
|
||||
|
||||
.exercise[
|
||||
|
||||
- Create the registry secret:
|
||||
```bash
|
||||
kubectl create secret docker-registry enix \
|
||||
--docker-server=docker-registry.enix.io \
|
||||
--docker-username=reader \
|
||||
--docker-password=VmQvqdtXFwXfyy4Jb5DR
|
||||
```
|
||||
|
||||
]
|
||||
|
||||
Why do we have to specify the registry address?
|
||||
|
||||
If we use multiple sets of credentials for different registries, it prevents leaking the credentials of one registry to *another* registry.
|
||||
|
||||
---
|
||||
|
||||
## Using the secret
|
||||
|
||||
- The first way to use a secret is to add it to `imagePullSecrets`
|
||||
|
||||
(in the `spec` section of a Pod template)
|
||||
|
||||
.exercise[
|
||||
|
||||
- Patch the `priv` Deployment that we created earlier:
|
||||
```bash
|
||||
kubectl patch deploy priv --patch='
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
imagePullSecrets:
|
||||
- name: enix
|
||||
'
|
||||
```
|
||||
|
||||
]
|
||||
|
||||
---
|
||||
|
||||
## Checking the results
|
||||
|
||||
.exercise[
|
||||
|
||||
- Confirm that our Pod can now start correctly:
|
||||
```bash
|
||||
kubectl get pods --selector=app=priv
|
||||
```
|
||||
|
||||
]
|
||||
|
||||
---
|
||||
|
||||
## Another way to use the secret
|
||||
|
||||
- We can add the secret to the ServiceAccount
|
||||
|
||||
- This is convenient to automatically use credentials for *all* pods
|
||||
|
||||
(as long as they're using a specific ServiceAccount, of course)
|
||||
|
||||
.exercise[
|
||||
|
||||
- Add the secret to the ServiceAccount:
|
||||
```bash
|
||||
kubectl patch serviceaccount default --patch='
|
||||
imagePullSecrets:
|
||||
- name: enix
|
||||
'
|
||||
```
|
||||
|
||||
]
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -94,28 +94,20 @@
|
||||
|
||||
## Building on the fly
|
||||
|
||||
- Some services can build images on the fly from a repository
|
||||
- Conceptually, it is possible to build images on the fly from a repository
|
||||
|
||||
- Example: [ctr.run](https://ctr.run/)
|
||||
|
||||
.exercise[
|
||||
(deprecated in August 2020, after being aquired by Datadog)
|
||||
|
||||
- Use ctr.run to automatically build a container image and run it:
|
||||
- It did allow something like this:
|
||||
```bash
|
||||
docker run ctr.run/github.com/jpetazzo/container.training/dockercoins/hasher
|
||||
```
|
||||
|
||||
<!--
|
||||
```longwait Sinatra```
|
||||
```key ^C```
|
||||
-->
|
||||
- No alternative yet
|
||||
|
||||
]
|
||||
|
||||
There might be a long pause before the first layer is pulled,
|
||||
because the API behind `docker pull` doesn't allow to stream build logs, and there is no feedback during the build.
|
||||
|
||||
It is possible to view the build logs by setting up an account on [ctr.run](https://ctr.run/).
|
||||
(free startup idea, anyone?)
|
||||
|
||||
???
|
||||
|
||||
|
||||
Reference in New Issue
Block a user