First you need to install Docker and Kind.
For docker you can use this useful script:
# Add Docker's official GPG key:
sudo apt-get update
sudo apt-get install ca-certificates curl gnupg
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/debian/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg
# Add the repository to Apt sources:
echo \
"deb [arch="$(dpkg --print-architecture)" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian \
"$(. /etc/os-release && echo "$VERSION_CODENAME")" stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
Add your user to docker group:
sudo usermod -aG docker $USER
Now create a file with Kind configuration:
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
extraPortMappings:
- containerPort: 30167
hostPort: 80
- containerPort: 30794
hostPort: 443
kubeadmConfigPatches:
- |
kind: InitConfiguration
nodeRegistration:
kubeletExtraArgs:
node-labels: "ingress-ready=true"
name: kindly
networking:
ipFamily: ipv4
apiServerAddress: 192.168.1.88
Install Kind
curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.20.0/kind-linux-amd64
chmod +x ./kind
sudo mv ./kind /usr/local/bin/kind
And create the cluster:
kind create cluster --config kind.yaml
Install Kubectl:
sudo apt-get update && sudo apt-get install -y apt-transport-https gnupg2
curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -
echo "deb https://apt.kubernetes.io/ kubernetes-xenial main" | sudo tee -a /etc/apt/sources.list.d/kubernetes.list
sudo apt-get update
sudo apt-get install -y kubectl
Install Krew and some good plugins:
(
set -x; cd "$(mktemp -d)" &&
OS="$(uname | tr '[:upper:]' '[:lower:]')" &&
ARCH="$(uname -m | sed -e 's/x86_64/amd64/' -e 's/\(arm\)\(64\)\?.*/\1\2/' -e 's/aarch64$/arm64/')" &&
KREW="krew-${OS}_${ARCH}" &&
curl -fsSLO "https://github.com/kubernetes-sigs/krew/releases/latest/download/${KREW}.tar.gz" &&
tar zxvf "${KREW}.tar.gz" &&
./"${KREW}" install krew
)
The plugins (you can install more here):
k krew install ns
k krew install ctx
k krew install tree
k krew install view-secret
k krew install modify-secret
...
Install helm:
curl https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 | bash
MetalLB instalation for LoadBalancer:
kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.13.7/config/manifests/metallb-native.yaml
You have to wait for the pods to be ready:
kubectl get pods -n metallb-system
NAME READY STATUS RESTARTS AGE
controller-789c75c689-x8rd9 1/1 Running 0 100s
speaker-cxw6j 1/1 Running 0 100s
Traefik installation:
k create ns traefik && k ns traefik
helm repo add traefik https://helm.traefik.io/traefik
helm repo update
helm install traefik traefik/traefik
If the load balancer is not ready, you need to check the ports:
k get svc -n traefik
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
traefik LoadBalancer 10.96.145.19 <pending> 80:32699/TCP,443:30199/TCP 3m32s
And now you will need to change the ports in the Traefik configuration:
You need to put the ports 80 and 443 in the ports 30167 and 30794:
k edit svc traefik -n traefik
Finally we only need to adjust the metallb configuration:
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
name: example
namespace: metallb-system
spec:
addresses:
- 172.18.255.200-172.18.255.250
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
name: empty
namespace: metallb-system
Let’s publish a simple web to check if everything is working:
First we are going to allow traefik to across the namespace:
kubectl edit deploy traefik -n traefik
Add this line to args
...
- --providers.kubernetescrd.allowCrossNamespace=true
...
Create a namespace:
kubectl create ns tests && kubectl ns tests
A deployment:
apiVersion: apps/v1
kind: Deployment
metadata:
name: apache-deployment
spec:
selector:
matchLabels:
app: apache2
replicas: 1 # tells deployment to run 2 pods matching the template
template:
metadata:
labels:
app: apache2
spec:
containers:
- name: apache2
image: httpd
ports:
- containerPort: 80
The service:
apiVersion: v1
kind: Service
metadata:
labels:
app.kubernetes.io/instance: traefik
name: apache2
spec:
type: ClusterIP
internalTrafficPolicy: Cluster
ipFamilies:
- IPv4
ipFamilyPolicy: SingleStack
ports:
- port: 80
selector:
app: apache2
And the ingress:
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
labels:
app.kubernetes.io/instance: traefik
name: 213.136.89.166
namespace: traefik
spec:
entryPoints:
- web
routes:
- kind: Rule
match: Host(`213.136.89.166`)
services:
- kind: Service
name: apache2
namespace: tests
port: 80
Beautiful screen when you go to the IP:
]]>I always prayed for a easy scalable database compatible with MySQL. So go to the point. Of course you will need to have a K8s cluster.
Create the namespace:
k create ns vitess & k ns vitess
Clone the repo of Vitess:
git clone https://github.com/vitessio/vitess
cd vitess/examples/operator
Install the operator:
kubectl apply -f operator.yaml
Wait …
k get pods
NAME READY STATUS RESTARTS AGE
vitess-operator-69f65546b7-zpht5 1/1 Running 0 87s
Now we have the operator we can start with the samples. The first one starts the cluster:
k apply -f 101_initial_cluster.yaml
After a cup of 🍵 …
example-commerce-x-x-zone1-vtorc-c13ef6ff-54d657995f-nkjfp 1/1 Running 3 (126m ago) 129m
example-etcd-faf13de3-1 1/1 Running 1 (126m ago) 129m
example-etcd-faf13de3-2 1/1 Running 1 (126m ago) 129m
example-etcd-faf13de3-3 1/1 Running 1 (126m ago) 129m
example-vttablet-zone1-2469782763-bfadd780 3/3 Running 2 (126m ago) 129m
example-vttablet-zone1-2548885007-46a852d0 3/3 Running 2 (126m ago) 129m
example-zone1-vtadmin-c03d7eae-7f46bfcd7c-qhpk8 2/2 Running 0 129m
example-zone1-vtctld-1d4dcad0-5db7c9799d-wrxf5 1/1 Running 3 (126m ago) 129m
example-zone1-vtgate-bc6cde92-7f6dccb697-vfmkr 1/1 Running 3 (126m ago) 129m
vitess-operator-79bd947f7b-sgtd5 1/1 Running 0 129m
There’s a bash script to create all “port-forwards” for services, but for this blog entry we are going to focus only in the connection to mysql.
We need to discover where’s the svc for “vtgate”
kubectl get service --selector="planetscale.com/component=vtgate,planetscale.com/cell"
With the result:
k port-forward --address 0.0.0.0 svc/example-zone1-vtgate-bc6cde92 15306:3306
Let’s connect to the database with:
mysql -h 127.0.0.1 -P 15306 -u user
...
show databases;
You are seeing the database commerce. Let’s connect one application to this
For simplify we’ll do it with helm. The most important thing is the values. This is a sample of the file values.yaml:
## WordPress Settings
wordpressUsername: test
wordpressPassword: test
wordpressEmail: moncho@pena.com
wordpressFirstName: Moncho
wordpressLastName: Pena
wordpressBlogName: My Blog!
## Database Settings
externalDatabase:
host: example-zone1-vtgate-bc6cde92.vitess.svc.cluster.local
user: user
password: ""
database: commerce
port: 3306
## Enable Maria DB
mariadb:
enabled: false
The most important part is to point to the service located into namespace vitess: example-zone1-vtgate-bc6cde92.vitess.svc.cluster.local
k create ns wordpress && k ns wordpress
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update
helm install myblog -f values.yaml bitnami/wordpress
Wait a few seconds:
k get pods
NAME READY STATUS RESTARTS AGE
myblog-wordpress-76cb978894-vwlm8 1/1 Running 0 11m
Let’s take a look 👀 to the database:
MySQL [commerce]> show tables;
+-----------------------+
| Tables_in_commerce |
+-----------------------+
| wp_commentmeta |
| wp_comments |
| wp_links |
| wp_options |
| wp_postmeta |
| wp_posts |
| wp_term_relationships |
| wp_term_taxonomy |
| wp_termmeta |
| wp_terms |
| wp_usermeta |
| wp_users |
+-----------------------+
12 rows in set (0.004 sec)
It works. A WP pointing to Vitess … Simply awesome.
]]>Understanding the graph:
Why should we use Minio? In this sample I’m using Minio, but if you prefer you could use a similar alternative for Object Storage, the most knowed is Amazon S3, but Digital Ocean has one too. If your team uses Git (a usual situation at 2022) you probably know that WP save images into a directory called wp-content, this could be a mess if you add these images to the repository. Another good thing is you are going to have a nice distributed system, your own cdn, and finally you will have the possibility to replicate the WP containers.
With this arquitecture we have a base with 4 containers: Wordpress, Maria DB, Minio and Traefik, and we can add more replicas, and much better we can add as workers as we need them.
Let’s do a quick test with only a simple Docker Compose file.
mkdir wp
cd wp
mkdir mariadb_data && chown 1001 mariadb_data
mkdir minio_data && chown 1001 minio_data
mkdir wordpress_data && chown 1001 wordpress_data
docker-compose up
Here the docker compose file:
version: '2'
services:
mariadb:
image: docker.io/bitnami/mariadb:10.6
volumes:
- './mariadb_data:/bitnami/mariadb'
environment:
# ALLOW_EMPTY_PASSWORD is recommended only for development.
- ALLOW_EMPTY_PASSWORD=yes
- MARIADB_USER=bn_wordpress
- MARIADB_DATABASE=bitnami_wordpress
wordpress:
image: docker.io/bitnami/wordpress:5
ports:
- '8080:8080'
- '8443:8443'
volumes:
- './wordpress_data:/bitnami/wordpress'
depends_on:
- mariadb
environment:
# ALLOW_EMPTY_PASSWORD is recommended only for development.
- ALLOW_EMPTY_PASSWORD=yes
- WORDPRESS_DATABASE_HOST=mariadb
- WORDPRESS_DATABASE_PORT_NUMBER=3306
- WORDPRESS_DATABASE_USER=bn_wordpress
- WORDPRESS_DATABASE_NAME=bitnami_wordpress
minio:
image: docker.io/bitnami/minio:2022
ports:
- '9000:9000'
- '9001:9001'
environment:
- MINIO_ROOT_USER=minioadmin
- MINIO_ROOT_PASSWORD=minioadmin
volumes:
- './minio_data:/data'
You can download the file here.
If everything wen fine you now should go to WP Login Page http://localhost:8080/wp-login.php.
user: user
pass: bitnami
How can I access to Minio? From this url: http://localhost:9001/login. You should see the credentials into the docker-compose file 😀.
Create a new user with read and write permissions:
Create a new bucket with (Important!) “Public Access”.
And now we are going to install a useful plugin.
Awesome, you will see a “wizard setup” with the Minio option. Fill the fields:
NOTE: as you noticed, the URL in this case is in a local network (http://192.168.0.48:9000). Remember: wordpress should access to this url to upload and show objects.
Add a image to the “Media” section. If you take a look into the Minio Admin Tool, you will see the images uploaded.
Let’s do a quick test now. We have 2 machines with docker installed. You are going to choose one of them to work as a master.
This is a simple yaml with the configuation with Traefik and Portainer. For simplify we are not going to use SSL, but it should be very easy to add a web secure access.
version: "3.3"
services:
traefik:
image: "traefik:latest"
command:
- --api.dashboard=true
- --api.insecure=true
- --entrypoints.web.address=:80
- --providers.docker
ports:
- "80:80"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro"
labels:
- "traefik.enable=true"
# Dashboard
# Take care of the domain
- "traefik.http.routers.traefik-public.rule=Host(`traefik.local`)"
- "traefik.http.routers.traefik-public.entrypoints=web"
- "traefik.http.services.traefik-public.loadbalancer.server.port=8080"
- "traefik.http.routers.traefik-public.service=api@internal"
portainer:
image: portainer/portainer-ce:latest
command: -H unix:///var/run/docker.sock
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- portainer_data:/data
deploy:
mode: replicated
replicas: 1
placement:
constraints: [node.role == manager]
labels:
- "traefik.enable=true"
# Frontend
# Take care of the domain
- "traefik.http.routers.frontend.rule=Host(`portainer.local`)"
- "traefik.http.routers.frontend.entrypoints=web"
- "traefik.http.services.frontend.loadbalancer.server.port=9000"
- "traefik.http.routers.frontend.service=frontend"
volumes:
portainer_data:
You can download the file here.
If you are working in a local networks it’s a good idea to create “fake domains” into your “/etc/hosts”. Sample with the server IP:
192.168.0.94 traefik.local
192.168.0.94 portainer.local
192.168.0.94 wordpress.local
192.168.0.94 minio.local
192.168.0.94 minio.admin.local
Run this commands into the server 192.168.0.94.
docker network create wordpress_network --scope swarm
docker swarm init
docker stack deploy -c portainer.yaml portainer
If every thing it’s ok you can go to Portainer from http://portainer.local and add this minimal stack of wordpress:
version: '3'
volumes:
db_data:
wordpress_data:
minio_data:
networks:
wordpress_network:
portainer_default:
external: true
services:
mariadb:
image: docker.io/bitnami/mariadb:10.6
networks:
- wordpress_network
volumes:
- 'db_data:/bitnami/mariadb'
environment:
# ALLOW_EMPTY_PASSWORD is recommended only for development.
- ALLOW_EMPTY_PASSWORD=yes
- MARIADB_USER=bn_wordpress
- MARIADB_DATABASE=bitnami_wordpress
wordpress:
image: docker.io/bitnami/wordpress:6
networks:
- wordpress_network
- portainer_default
volumes:
- 'wordpress_data:/bitnami/wordpress'
depends_on:
- mariadb
environment:
# ALLOW_EMPTY_PASSWORD is recommended only for development.
- ALLOW_EMPTY_PASSWORD=yes
- WORDPRESS_DATABASE_HOST=mariadb
- WORDPRESS_DATABASE_PORT_NUMBER=3306
- WORDPRESS_DATABASE_USER=bn_wordpress
- WORDPRESS_DATABASE_NAME=bitnami_wordpress
labels:
- "traefik.enable=true"
# Frontend
# Take care of the domain
- "traefik.docker.network=portainer_default"
- "traefik.http.routers.wordpress.rule=Host(`wordpress.local`)"
- "traefik.http.routers.wordpress.entrypoints=web"
- "traefik.http.services.wordpress.loadbalancer.server.port=8080"
- "traefik.http.routers.wordpress.service=wordpress"
minio:
image: docker.io/bitnami/minio:2022
networks:
- wordpress_network
- portainer_default
environment:
- MINIO_ROOT_USER=minioadmin
- MINIO_ROOT_PASSWORD=minioadmin
volumes:
- 'minio_data:/data'
labels:
- "traefik.enable=true"
# Frontend
# Take care of the domain
- "traefik.docker.network=portainer_default"
- "traefik.http.routers.minio.rule=Host(`minio.local`)"
- "traefik.http.routers.minio.entrypoints=web"
- "traefik.http.services.minio.loadbalancer.server.port=9000"
- "traefik.http.routers.minio.service=minio"
# minio-admin
- "traefik.http.routers.minio-admin.rule=Host(`minio.admin.local`)"
- "traefik.http.routers.minio-admin.entrypoints=web"
- "traefik.http.services.minio-admin.loadbalancer.server.port=9001"
- "traefik.http.routers.minio-admin.service=minio"
You can download the file here.
If everything is ok you can go to Minio (http://minio.admin.local) and crete the bucket and the user. And then go to WP and configure the Media Cloud Plugin.
Let’s add 2 replicas to Wordpress container:
And now go to the master server and log the 2 containers which are running wordpress.
docker logs -f 7da055fb1de5
...
The replicas are working and the traffic is balanced!!!
To see all nodes you are going to add to the cluster, in portainer you should install the “Portainer Agent”, write these commands:
docker network create \
--driver overlay \
portainer_agent_network
docker service create \
--name portainer_agent \
--network portainer_agent_network \
-p 9001:9001/tcp \
--mode global \
--constraint 'node.platform.os == linux' \
--mount type=bind,src=//var/run/docker.sock,dst=/var/run/docker.sock \
--mount type=bind,src=//var/lib/docker/volumes,dst=/var/lib/docker/volumes \
portainer/agent:2.13.1
And now the last thing. We are going to run a new server with docker and connect to master. First we need to know the token from the manager:
moncho@debianita:~$ docker swarm join-token worker
To add a worker to this swarm, run the following command:
docker swarm join --token [here_the_huge_token] 192.168.0.94:2377
Then we are going to run the command into the worker. If you see the message “This node joined a swarm as a worker.” then you are finished!. Now you can add how much workers as you want and then up the number of replicas, you can scale “to infinity and beyond!”.
NOTE: I know “Docker Swarm” is going to be discontinued. But, in this moment, is included in the latest versions of Docker. So, it’s a good tool to understand how it works a cluster and in a easy mode start to work with containers like a Pro. In the future I will focus in Kubernetes because now I don’t have production projects in Docker Swarm 😀. See you soom (I do hope) in the next post.
]]>The problem:
curl -i https://yourdomain.com
...
X-Forwarded-For: 10.1.3.150
X-Forwarded-Host: yourdomain.com
X-Forwarded-Port: 443
X-Forwarded-Proto: https
X-Forwarded-Server: traefik-55c96f54d6-7cmfn
X-Real-Ip: 10.1.3.150
...
The X-Real-Ip is the IP of a node 😞.
The solution:
FIRST: You have to Configure proxy protocol support for your Classic Load Balancer.
As you can see ELB Amazon uses TCP, with HTTP or HTTPS protocol you don’t should to have this problem.
Steps to follow …
Add the policy:
aws elb create-load-balancer-policy --load-balancer-name name-of-the-balancer --policy-name traefik-ProxyProtocol-policy --policy-type-name ProxyProtocolPolicyType --policy-attributes AttributeName=ProxyProtocol,AttributeValue=true --region eu-central-1
Verify:
aws elb describe-load-balancers --load-balancer-name name-of-the-balancer --region eu-central-1 | grep traefik -A 5 -B 5
You will see:
"Policies": {
"AppCookieStickinessPolicies": [],
"LBCookieStickinessPolicies": [],
"OtherPolicies": [
"traefik-ProxyProtocol-policy"
]
},
...
Now look for the correct port:
aws elb describe-load-balancers --load-balancer-name name-of-the-balancer --region eu-central-1 | grep 443 -A 5 -B 5
Result:
...
"Listener": {
"Protocol": "TCP",
"LoadBalancerPort": 443,
"InstanceProtocol": "TCP",
"InstancePort": 31533
},
...
Then you have to add the policy to the port 31533:
aws elb set-load-balancer-policies-for-backend-server --load-balancer-name name-of-the-balancer --instance-port 31533 --policy-names traefik-ProxyProtocol-policy --region eu-central-1
Review if the policy was added:
aws elb describe-load-balancers --load-balancer-name name-of-the-balancer --region eu-central-1 | grep -R BackendServerDescriptions -A 7
Result:
"BackendServerDescriptions": [
{
"InstancePort": 31533,
"PolicyNames": [
"traefik-ProxyProtocol-policy"
]
}
],
SECOND: Add a special configuration to Traefik to be ready for the Proxy protocol. More info here.
Find into the Load Balancer description the CIDR networks:
This is the configuration in YAML:
## Static configuration
entryPoints:
websecure:
address: ":443"
proxyProtocol:
trustedIPs:
- "10.1.4.0/24"
- "10.1.5.0/24"
- "10.1.6.0/24"
For the CLI:
--entryPoints.websecure.address=:443
--entryPoints.websecure.proxyProtocol.trustedIPs=10.1.4.0/24,10.1.5.0/24,10.1.6.0/24
Note: if you want to now the ranges use ipaddressguide
Ok. If now we do the curl …
curl -i https://yourdomain.com
...
X-Forwarded-For: 93.22.110.23
X-Forwarded-Host: yourdomain.com
X-Forwarded-Port: 443
X-Forwarded-Proto: https
X-Forwarded-Server: traefik-55c96f54d6-7cmfn
X-Real-Ip: 93.22.110.23
...
You got the REAL IP!
]]>First we need to create 3 repository secrets:
You can get this webhook from the button “Copy Link” (Remember you have to activate the webhook):
Create a new file for the Actions Definition like this.
name: Testing the Actions with Jekyll
on:
push:
branches: [ master ]
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Build the site in the jekyll/builder container
run: |
docker run \
-v ${{ github.workspace }}:/srv/jekyll -v ${{ github.workspace }}/_site:/srv/jekyll/_site \
jekyll/builder:latest /bin/bash -c "chmod -R 777 /srv/jekyll && jekyll build --future"
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Login to your own Registry
uses: docker/login-action@v1
with:
registry: registry.yourdomain.com
username: ${{ secrets.REGISTRY_USER }}
password: ${{ secrets.REGISTRY_PASS }}
- name: Build and push
id: docker_build
uses: docker/build-push-action@v2
with:
push: true
context: .
tags: registry.yourdomain.com/moncho/bdunk-web:latest
- name: Image digest
run: echo ${{ steps.docker_build.outputs.digest }}
- name: curl
uses: wei/curl@v1
with:
args: -X POST ${{ secrets.PORTAINER_WEBHOOK }}
When the action is going to be called, in this case when we make a push to master.
on:
push:
branches: [ master ]
We generate all the necessary files and save into _site foder.
Build the site in the jekyll/builder container
Login againsts our own Registry.
registry: registry.yourdomain.com
Pushing the image. In this step is very important to write the correct context.
with:
push: true
context: .
tags: registry.yourdomain.com/moncho/bdunk-web:latest
And finally we do a call to the Portainer webhook, so it’s going to get the latest image and pull it … Awesome!. More info about this.
args: -X POST ${{ secrets.PORTAINER_WEBHOOK }}
Next post we are going to make a complete sample with a complex webpage.
]]>There’s a Official repo called registry, now we can create our own image store.
Let’s create a new stack!
First we need to create the network:
docker network create registry_network --scope swarm
And we need a file to save the Basic Auth. Remember this user and password
mkdir -p registry/auth && mkdir -p registry/data
echo $(htpasswd -nb user password) | sed -e s/\\$/\\$\\$/g > registry/auth/registry.password
Into Portainer go to Stacks and press “+ Add stack”.
Choose a name and paste this configuration:
version: "3.3"
services:
registry:
image: registry:2
environment:
REGISTRY_AUTH: htpasswd
REGISTRY_AUTH_HTPASSWD_PATH: /auth/registry.password
REGISTRY_AUTH_HTPASSWD_REALM: Registry
volumes:
- ./registry/data:/var/lib/registry
- ./registry/auth:/auth
networks:
- registry_network
- portainer_default
labels:
- "traefik.enable=true"
- "traefik.docker.network=portainer_default"
- "traefik.http.routers.registry.rule=(Host(`registry.yourdomain.com`))"
- "traefik.http.routers.registry.entrypoints=websecure"
- "traefik.http.services.registry.loadbalancer.server.port=5000"
- "traefik.http.services.registry.loadBalancer.sticky.cookie=true"
- "traefik.http.routers.registry.service=registry"
- "traefik.http.routers.registry.tls=true"
- "traefik.http.routers.registry.tls.certresolver=leresolver"
networks:
portainer_default:
external: true
registry_network:
external: true
Press deploy button and wait some seconds ⧖.
If everythig gone well you should login.
# docker login -u user -p password registry.contabo.bdunk.com
Login Succeeded
here you can find a very simple sample to make a image.
Building the image:
docker build .
Listing the images with docker images
`
REPOSITORY TAG IMAGE ID CREATED SIZE
<none> <none> 60081ed32117 5 minutes ago 133MB
A quick test for the image:
docker run -p 8888:80 60081ed32117
Making a curl:
# curl localhost:8888
hello conch
Creating a tag for the image:
docker image tag 60081ed32117 registry.yourdomain.com/your-username/nginx-test:latest
And then push 🚀
# docker push registry.yourdomain.com/your-username/nginx-test
The push refers to repository [registry.yourdomain.com/moncho/nginx-test]
0b411e19d514: Pushed
1914a564711c: Mounted from your-username/nginx-test
db765d5bf9f8: Mounted from your-username/nginx-test
903ae422d007: Mounted from your-username/nginx-test
66f88fdd699b: Mounted from your-username/nginx-test
2ba086d0a00c: Mounted from your-username/nginx-test
346fddbbb0ff: Mounted from your-username/nginx-test
latest: digest: sha256:e7e7ff0a82e1a97630e9b40377c1e97fadad09072789f9b4ce4e664bcef50671 size: 1777
Yes! The image is now into the repository. Good Job!
Next part is about how to make auto deploys.
See you soon in part 4.
]]>With this arquitecture you have these incredible features:
The first step is start with this command:
docker swarm init
In Digital Ocean you got this error:
Error response from daemon: could not choose an IP address to advertise since this system has multiple addresses on interface eth0 (206.81.24.179 and 10.19.0.6) - specify one with --advertise-addr
Choose the external IP:
docker swarm init --advertise-addr 206.81.24.179
If everything is Ok. You will read:
Swarm initialized: current node (cyjmwjxm4ivlvh6pbdruospgj) is now a manager.
To add a worker to this swarm, run the following command:
docker swarm join --token SWMTKN-1-1n3t6qxkftfa24zq6r5xnkxu9n554wt32m1ype50xmha53h6z0-ejwiozr4iqqozxsddn37zoi6a 206.81.24.179:2377
To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.
As I commnented at the begining you can add new servers with the command join. No worries if you don’t save or remember the token, you can access at any momment with this command:
docker swarm join-token worker
Check the network created.
# docker network ls
NETWORK ID NAME DRIVER SCOPE
1cf3640c4dae bridge bridge local
8a937e6a0cb9 docker_gwbridge bridge local
b95fa8892056 host host local
mf24yxxrpofp ingress overlay swarm
3f7807925072 none null local
We choose Portainer because is a good idea to have a UI for the control of your server.
This is the stack file you can find here. Read the comments to understand how it works.
version: "3.3"
services:
traefik:
image: "traefik:latest"
command:
# We want the dashboard of traefik active
- --api.dashboard=true
- --api.insecure=true
- --entrypoints.web.address=:80
- --entrypoints.websecure.address=:443
- --providers.docker
# Change this if you want more logs (DEBUG level could be too much)
- --log.level=ERROR
- --certificatesresolvers.leresolver.acme.tlschallenge=true
- --certificatesresolvers.leresolver.acme.httpchallenge=true
- --certificatesresolvers.leresolver.acme.email=your_email@here.com
# To save the certs is hight recommended to use a volume as you can see it bellow
- --certificatesresolvers.leresolver.acme.storage=./acme.json
- --certificatesresolvers.leresolver.acme.httpchallenge.entrypoint=web
ports:
- "80:80"
- "443:443"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro"
# In the same folder you have this file
# mkdir traefik && touch traefik/acme.json && chmod 400 traefik/acme.json
- "./traefik/acme.json:/acme.json"
labels:
- "traefik.enable=true"
# Redirect all traffic from http to https
- "traefik.http.routers.http-catchall.rule=hostregexp(`{host:.+}`)"
- "traefik.http.routers.http-catchall.entrypoints=web"
- "traefik.http.routers.http-catchall.middlewares=redirect-to-https"
- "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"
- "traefik.http.middlewares.redirect-to-https.redirectscheme.permanent=true"
# Middleware
# admin-auth middleware with HTTP Basic auth
# Using the environment variables USERNAME and HASHED_PASSWORD
# Sample echo $(htpasswd -nB user) | sed -e s/\\$/\\$\\$/g
# In this case the password is password: Don't use this in production 😁
- "traefik.http.middlewares.admin-auth.basicauth.users=user:$$2y$$05$$0et0ONr1P6o3Fh2f/6h6G.sRNu740FOSTg.zXwA87Gq/VSvTOj2oW"
# Dashboard
# Remember to point to the Docker Swarm IP server
- "traefik.http.routers.traefik-public.rule=Host(`traefik.yourdomain.com`)"
- "traefik.http.routers.traefik-public.entrypoints=websecure"
- "traefik.http.routers.traefik-public.middlewares=admin-auth"
- "traefik.http.services.traefik-public.loadbalancer.server.port=8080"
- "traefik.http.routers.traefik-public.service=api@internal"
- "traefik.http.routers.traefik-public.tls.certresolver=leresolver"
portainer:
image: portainer/portainer-ce:latest
command: -H unix:///var/run/docker.sock
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- portainer_data:/data
deploy:
mode: replicated
replicas: 1
placement:
constraints: [node.role == manager]
labels:
- "traefik.enable=true"
# Frontend
# Remember to point to the Docker Swarm IP server
- "traefik.http.routers.frontend.rule=Host(`portainer.yourdomain.com`)"
- "traefik.http.routers.frontend.entrypoints=websecure"
- "traefik.http.services.frontend.loadbalancer.server.port=9000"
- "traefik.http.routers.frontend.service=frontend"
- "traefik.http.routers.frontend.tls.certresolver=leresolver"
volumes:
portainer_data:
With Traefik you resolve the problem with certs. One tip: review if the domains point to the server before launch the stack.
host -t A portainer.yourdomain.com
...
Deploy this stack 🚀
# docker stack deploy -c portainer.yaml portainer
Creating network portainer_default
Creating service portainer_traefik
Creating service portainer_portainer
Take a look to containers with docker ps -a
.
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
0dbf02813006 traefik:latest "/entrypoint.sh --ap…" 34 seconds ago Up 28 seconds 80/tcp portainer_traefik.1.7777idunbforuu53enwi47qhr
02e7c7ff96b1 portainer/portainer-ce:latest "/portainer -H unix:…" 2 minutes ago Up 2 minutes 8000/tcp, 9000/tcp portainer_portainer.1.e69yugombuxs6cb9gqgcr1q8d
Watch logs of the container:
docker logs -f 0dbf02813006
time="2021-05-24T17:02:09Z" level=info msg="Configuration loaded from flags."
No erros good news!
Go to the Traefik dasboard (traefik.yourdomain.com) and use the user and password for Basic Auth.
The first time you go to Portainer Admin you have to choose user and password.
In this moment you passed the most difficult part, now you have the base to create other stacks.
Next part is about how to create your own registry.
See you soon in part 3.
]]>In these serie of posts (first of fifth) I want to reach these conditions:
First of all you can find the files used in this article here.
Let’s take a 👀 to the main file.
I’m using DO but you could do it with other provider.
provider "digitalocean" {
token = var.do_token
}
This token is defined as a variable into the vars file
variable "do_token" {
description = "The token"
}
If you don’t define a “default value” you will asked for the value when you try to apply the script.
Here the type of the droplet:
resource "digitalocean_droplet" "docker_swarm" {
image = "ubuntu-20-10-x64"
name = "docker-swarm"
region = "fra1"
size = "s-4vcpu-8gb"
ssh_keys = [ "123123" ]
user_data = file("userdata.yaml")
}
I choosed Ubuntu as server.
How to know your ssh_key id:
curl -X GET -H "Content-Type: application/json" -H "Authorization: Bearer here_your_token" "https://api.digitalocean.com/v2/account/keys"
At the begining of the respose you have the id:
{"ssh_keys":[{"id":123123,"fingerprint ...
May be you have some question about this file userdata.yaml. This is like a script to deploy after the machine is created. More info in this repo. If you want to know more deeply here some samples of configuration files.
In my case I only want to install docker dependencies:
#cloud-config
package_update: true
packages:
- docker.io
- docker-compose
- vim
- htop
I use the DO DNSs so I can update with the IP assigned easily.
resource "digitalocean_record" "edge" {
domain = var.domain
type = "A"
name = "traefik"
ttl = "300"
value = digitalocean_droplet.docker_swarm.ipv4_address
}
Let’s launch the terraform:
terraform init
terraform validate
terraform plan
terraform apply
You need to paste the token:
$ terraform apply
var.do_token
The token
Enter a value:
After 1-2 minutes …
Apply complete! Resources: 3 added, 0 changed, 0 destroyed.
Outputs:
connect_ssh = "ssh root@46.101.204.107"
The output is because we define into main file:
output "connect_ssh" {
description = "How to connect to the New Server"
value = "ssh root@${digitalocean_droplet.docker_swarm.ipv4_address}"
}
The Droplet:
And if we connect with the server with ssh:
root@docker-swarm:~# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
Well done! We have the base to start with the Docker Swarm.
See you soon in part 2.
]]>And some people asked to me for a 'How to' … and well … here we go!
We need a Arduino. In this case I’m using a Arduino Wifi Shield.
This is all the code you will need.
With te Serial Monitor of the Arduino you can see if you are connected to the Wifi and what is your IP.
You can test if everything is working fine with curl
# Low the LED
curl http://192.168.0.21/L
# Hight the LED
curl http://192.168.0.21/H
How to install:
wget http://opensimulator.org/dist/opensim-0.9.0.0.tar.gz
cd opensim-0.9.0.0/bin
Then if you try to start opensim you will recieve this warning:
./opensim.sh: 5: ./opensim.sh: mono: not found
Let’s install Mono dependencies:
apt-get install mono-complete
And start again with:
./opensim.sh
First question is about region.
Create a user with the command 'create user'.
IMPORTANT: If you fill First with 'moncho' and Last with 'pena' your user id will be 'moncho pena'.
You can use another 'Second Life' Client but in this sample I’m using Singularity.
Thiis is the configuration of the grid.
How to login:
Ok. You are in a virtual world an you now are like a god. Create a new object. Then select, click on the tab Content and New Script.
This is the code where we are calling the “webhook” of the Arduino. We change the texture of the object everytime we touch it and we do the call to switch the led.
string texture1 = "00000000-0000-2222-3333-100000001041";//UUID of texture 1 (or inventory name if in inventorY)
string texture2= "00000000-0000-1111-9999-000000000003";//UUID of texture2
float time = 0.0;//time to sleep in seconds
integer side = ALL_SIDES;//which side to change texture on
string URL = "http://192.168.0.21";
string HTTP_USER_AGENT = "HTTP_USER_AGENT";
switch(string texture)
{
llSetTexture(texture, side);
currentstate = ~currentstate;//swith states
llSleep(time);
}
default
{
touch_start(integer total_number)
{
if(currentstate) {
switch(texture1);
llHTTPRequest(URL + "/H", [], "" );
llSay(0, "On");
} else {
switch(texture2);
llHTTPRequest(URL + "/L", [], "" );
llSay(0, "Off");
}
}
}
Now if you click the object you will see a warning icon like this:
This is why you have to allow to connect OpenSim to the Arduino Shield IP. Go to the 'OpenSim.ini' and add this line:
OutboundDisallowForUserScriptsExcept = 192.168.0.21
This a new video with this sample working:
Maybe OpemSim is not a fashionable application but is fine to do tests.
]]>I will install in a Debian/Ubuntu system.
Prerequisites:
apt-get install build-essential libpcre3-dev libssl-dev
Get the source code from the download page:
wget https://openresty.org/download/openresty-VERSION.tar.gz tar -xzvf openresty-VERSION.tar.gz cd openresty-VERSION/ ./configure -j2 make -j2 make install
Add this line to your ~/.bashrc or ~/.bash_profile file.
export PATH=/usr/local/openresty/bin:$PATH
Then execute:
openresty
And you could visit the page http://127.0.0.1 (the better place), and you will see this:
]]>