Simple reverse-proxy configuration with Traefik and docker-compose - docker-compose

I'm having some hard time with Traefik 2.x and its configuration options.
I have the following docker-compose.yml file which should:
Create Traefik container
Create Grafana container (a web dashboard listening on port 3000)
Make Grafana reachable via port 80 through Traefik acting as a reverse proxy
Contacting the machine's IP at port 8080 I can correctly see the Traefik dashboard.
However, at port 80 I receive a 404
docker-compose.yml:
version: "3"
services:
traefik:
container_name: "traefik"
image: "traefik:latest"
command:
- --api.insecure=true
- --api.dashboard=true
- --providers.docker=true
- --providers.docker.exposedbydefault=false
- --providers.docker.network=frontend
- --entrypoints.frontend.address=:80
volumes:
- "/var/run/docker.sock:/var/run/docker.sock"
labels:
- "traefik.enable=true"
networks:
- frontend
ports:
- "80:80"
- "8080:8080"
grafana:
container_name: "grafana"
image: "grafana/grafana:latest"
networks:
- frontend
- backend
labels:
- "traefik.enable=true"
- "traefik.docker.network=frontend"
- "traefik.port=3000"
networks:
frontend:
name: frontend
backend:
name: backend

I found the correct edit to my configuration.
In the labels section inside the grafana container, it is needed to set a rule for Traefik in order to recognize the URL
- "traefik.http.routers.whoami.rule=Host(`myIPhere`)"
Where I have inserted the IP of my machine in the Host field
Now Traefik recognizes the rule and routes the traffic incoming on port 80 to port 3000 as expected.

Related

Traefik 2 overrides an upstream's MIME type

I'm trying to convert a project that uses an nginx router to use the Traefik 2.x router. It almost works, but not quite: a file that is served with a Content-Type: application/javascript header gets converted to Content Type: text/vnd.trolltech.linguist. This breaks the web app.
traefik.yaml looks like this:
providers:
docker:
endpoint: unix:///var/run/docker.sock
watch: true
exposedbydefault: false
entryPoints:
vite:
address: ':5173'
goapp:
address: ':80'
docker-compose.yaml looks something like this:
version: '3'
services:
reverse-proxy:
image: traefik:v2.4.7
container_name: traefik
labels:
- "traefik.enable=true"
- "traefik.http.middlewares.autodetect.contenttype.autodetect=true"
- "traefik.http.routers.traefik=true"
ports:
- 80:80
- 5173:5173
restart: unless-stopped
security_opt:
- no-new-privileges:true
volumes:
- ./docker/traefik/traefik.yaml:/etc/traefik/traefik.yaml:ro
- ./docker/certs:/etc/certs:ro
- /var/run/docker.sock:/var/run/docker.sock:ro
goapp:
container_name: goapp
image: vitestuff/goapp
environment:
- air_wd=/app/project
build:
context: docker/goproj
labels:
- "traefik.enable=true"
- "traefik.http.routers.goapp.rule=Host(`app.localhost`)"
- "traefik.http.services.goapp.loadbalancer.server.port=80"
volumes:
- consistency: cached
source: .
target: /app/project
type: bind
vite:
container_name: vite
image: vitestuff/vite-env
ports:
- "5173"
build:
context: docker/vitevol
args:
PORT: 5173
labels:
- "traefik.enable=true"
- "traefik.http.routers.vite.rule=Host(`app.localhost:5173`)"
- "traefik.http.services.vite.loadbalancer.server.port=5173"
volumes:
- consistency: consistent
source: ./frontend
target: /app/frontend
type: bind
As currently configured, the goapp container is correctly served out at http://app.localhost, and the embedded script link with src="http://app.localhost:5173/src/main.ts" get hit on the server, but has the wrong MIME type: the vite container serves out the file with a MIME type of "application/javascript", but Traefik overrides this with a MIME type of "text/vnd.trolltech.linguist". This not being a legal value for a <script> tag, I get a CORS related error.
General background: I'm trying to port the following demo I wrote using an nginx router instead. I'd like to demo Traefik, but alas, I cannot get the damn thing to work.
The problem here turns out to be with my Traefik related labels. Here is a corrected docker-compose.yaml that correctly defines the services:
version: '3'
services:
reverse-proxy:
image: traefik:v2.8
container_name: traefik
labels:
- "traefik.enable=true"
ports:
- 80:80
- 5173:5173
restart: unless-stopped
security_opt:
- no-new-privileges:true
volumes:
- ./docker/traefik/traefik.yaml:/etc/traefik/traefik.yaml:ro
- /var/run/docker.sock:/var/run/docker.sock:ro
goapp:
container_name: goapp
image: vitestuff/goapp
environment:
- air_wd=/app/project
build:
context: docker/goproj
labels:
- "traefik.enable=true"
- "traefik.http.routers.goapp.rule=Host(`app.localhost`)"
- "traefik.http.services.goapp.loadbalancer.server.port=80"
- "traefik.http.routers.goapp.entrypoints=goapp"
volumes:
- consistency: cached
source: .
target: /app/project
type: bind
vite:
container_name: vite
image: vitestuff/vite-env
ports:
- "5173"
build:
context: docker/vitevol
args:
PORT: 5173
labels:
- "traefik.enable=true"
- "traefik.http.routers.vite.rule=Host(`app.localhost`)"
- "traefik.http.services.vite.loadbalancer.server.port=5173"
- "traefik.http.routers.vite.entrypoints=vite"
volumes:
- consistency: consistent
source: ./frontend
target: /app/frontend
type: bind
Short version: the new definitions create a vite and a goapp router that are explicitly tied to separate entrypoints. Once this is done, both services get their files out with the correct MIME types.

Routing incoming requests to deployments using same host but diffent target ports (example with wordpress and phpmyadmin)

I want to host a wordpress site using a number of services (wordpress, mysql, phpmyadmin, backend-service) for full functionality. I already have built those services using docker-compose and now I want to host them using kubernetes running on google (gke). On the SAME HOST I want to reach the wordpress service and the phpmyadmin service using different PORTS in the request. This seems logical to me, since I do not want to have a second external IP just for the phpmyadmin service.
Using docker-compose, this was easy. All external requests on port 80 for wordpress and all requests on port 8080 for phpmyadmin:
services:
# Database
db:
image: mysql:5.7
volumes:
- ./database/:/var/lib/mysql
restart: always
environment:
MYSQL_ROOT_PASSWORD: password
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD: password
networks:
- wpsite
ports:
- '3306:3306'
phpmyadmin:
depends_on:
- db
image: phpmyadmin/phpmyadmin
restart: always
ports:
- '8080:80'
environment:
PMA_HOST: db
MYSQL_ROOT_PASSWORD: password
networks:
- wpsite
wordpress:
container_name: wordpress-wpd
build:
dockerfile: Dockerfile
context: ./xdebug
depends_on:
- db
ports:
- '8000:80'
restart: always
volumes:
- ./Wordpress/:/var/www/html
environment:
WORDPRESS_DB_HOST: db:3306
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: password
PHP_EXTENSION_XDEBUG: 1
networks:
wpsite:
mail:
backend:
container_name: backend
build:
dockerfile: Dockerfile
context: ./Backend/
ports:
- '5555:5555' #prod
- '6666:6666' #add debug
- '80:80' #apache debug
restart: always
volumes:
- ./backend/:/var/www/backend/
networks:
wpsite:
networks:
wpsite:
mail:
Now using kubernetes this seems to be harder. With ingress, I can only allocate traffic from port 80 based on path, not port. I thought of using a load balancer to allocate traffic to my wordpress and phpmyadmin.
loadbalancer.yaml
apiVersion: v1
kind: Service
metadata:
name: frontend-service
spec:
ports:
- name: wordpress
#target: wordpress-deployment
port: 80
targetPort: 80
- name: phpmyadmin
#target: phpmyadmin-deployment
port: 8080
targetPort: 80
selector:
app: frontend
type: LoadBalancer
phpmyadmin.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: phpmyadmin-deployment
labels:
app: frontend
spec:
replicas: 1
selector:
matchLabels:
app: frontend
template:
metadata:
labels:
app: frontend
spec:
containers:
- name: phpmyadmin
image: phpmyadmin/phpmyadmin
ports:
- containerPort: 80
env:
- name: PMA_HOST
value: mysql-service
- name: PMA_PORT
value: "3306"
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-secrets
key: MYSQL_ROOT_PASSWORD
wordpress.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: wordpress-deployment
labels:
app: frontend
spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
app: frontend
template:
metadata:
labels:
app: frontend
spec:
containers:
- name: home-wordpress
image: eu.gcr.io/myproject/home_wordpress:latest
ports:
- containerPort: 80
volumeMounts:
- mountPath: /var/www/html
name: wordpress-persistent-storage
env:
- name: WORDPRESS_DB_HOST
value: mysql-service
- name: WORDPRESS_DB_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-secrets
key: MYSQL_PASSWORD
- name: WORDPRESS_DB_USER
valueFrom:
secretKeyRef:
name: mysql-secrets
key: MYSQL_USER
volumes:
- name: wordpress-persistent-storage
persistentVolumeClaim:
claimName: wordpress-pv-claim
Problem:
Since both containers (wordpress and phpmyadmin) are using the same port 80 at the end, it seems to me that the frontend-service is forwarding traffic from port 80 and from port 8080 to them both. I sometimes see the wordpress container replying to a request and sometimes the phpmyadmin container to the same (host/port) request.
Question:
Can this be solved using additional NodePort-services?
So for wordpress:
LoadBalancer service 80:80 --> (additional) NodePort wordpress service 80:80 --> wordpress deployment (80)
For phpmyadmin:
LoadBalancer service 8080:8080 --> (additional) NodePort phpmyadmin service 8080:80 --> phpmyadmin deployment (80)
In doing so I would need to use different labels/selectors for the loadbalancer and the deployments so the phpmyadmin deployment will not receive the port 80 wordpress requests from the loadbalancer directly.
I did not understand the kubernetes documentation on labels and selectors very well and it is confusing that there are various possibilities to position these in a yaml file (under spec? Under metadata? On what basis to choose key? On what basis to choose value?).
Or is there a simpler solution to all this? Overall it seems much more complicated than the docker-compose example...

kubernates networking python server in tomcat container

First of all, I am sorry that the grammar may be incorrect because I used Google Translate.
1.Deploy pods and services in a Kubernetes environment.
apiVersion: v1
kind: Pod
metadata:
name: testml
labels:
app: testml-pod
spec:
containers:
- name: testmlserver
image: test_ml_server:2.8
ports:
- containerPort: 8080
- containerPort: 5100
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
volumeMounts:
- name: testserver-api
mountPath: /app/test/api
- name: testmlserver-csv
mountPath: /app/test/csv
- name: testmldb
image: test_ml_db:1.4
ports:
- containerPort: 1433
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
volumeMounts:
- name: estmldb
mountPath: /var/opt/mssql/data
volumes:
- name: testmlserver-api
hostPath:
path: /usr/testhostpath/testmlserver/api
- name: testmlserver-csv
hostPath:
path: /usr/testmlhostpath/testserver/csv
- name: testmldb
hostPath:
path: /usr/testmlhostpath/testmldb
After the server container is deployed, run the python server in the container.
apiVersion: v1
kind: Service
metadata:
name: testml-service
spec:
type: NodePort
ports:
- name: testml-server-port
port: 8080
targetPort: 8080
protocol: TCP
nodePort: 30080
- name: testml-python-port
port: 5100
targetPort: 5100
protocol: TCP
nodePort: 30051
- name: testml-db-port
port: 1433
targetPort: 1433
protocol: TCP
nodePort: 30014
selector:
app: test-pod
In this way, both pods and services have been deployed.
Connect to the server(tomcat) container and run the python server file.
At this time, the address value used when calling the python server from the web is 'http://testml:5100'
I tried to write it and communicate with it.
However, Cross-Origin Read Blocking (CORB) has occurred
I tried also with'http://localhost:5100' because there is another way to communicate in one container, but Connetc refused.
In the docker-compose environment, I checked that the python server is called when communicating with localhost, but I do not know the cause of the error in the kubernets environment.
Checking various things As a result of checking the port in the server (tomcat) container, it is confirmed that 0.0.0.0 does not apply to only the python port.
How can I call the python server normally in the server container?
In web server(tomcat) connect with the db container by pod name as shown below. POD NAME => testml
<property name="url" value="jdbc:log4jdbc:sqlserver://testml:1433;database=test_ml;autoReconnect=true" />
In the same way, I tried to connect the python server with pod name, but it fails.
<api.host.server=http://testml:5100>
I think you can't connect by pod's name unless you have a headless service defined. You can connect via Pod's IP but that is not a recommended approach since the Pod's IP is dynamic and can change across updates.
However, as you have created a Service object as well, you can use that for communication using it's name as http://testml-service:port.
Further, as the Service object is of type NodePort, you can also connect via the IP of the nodes of the cluster.

Kubernetes Pod with multiple containers can't connect to each other (DNS issue?!)

For our CI pipeline I setup a Kubernetes pod config (see below). There is one issue that the php app can't connect to the mysql container because it can't resolve the host "mysql".
Error message:
mysqli_connect(): php_network_getaddresses: getaddrinfo failed: Name or service not known
pod config:
apiVersion: v1
kind: Pod
spec:
containers:
- name: php
image: docker.pkg.github.com/foo-org/bar-php/bar-php:latest
- name: nginx
image: docker.pkg.github.com/foo-org/bar-nginx/bar-nginx:latest
command:
- cat
tty: true
- name: mysql
image: docker.pkg.github.com/foo-org/bar-mysql/bar-mysql:latest
env:
- name: MYSQL_ROOT_PASSWORD
value: bazz
ports:
- containerPort: 3306
readinessProbe:
tcpSocket:
port: 3306
initialDelaySeconds: 5
tty: true
imagePullSecrets:
- name: ci-gh-registry
This runs in GKE but I guess this doesn't make a difference?
Any ideas why and how to fix it?
provide host as 127.0.0.1 or localhost instead of mysql containers in a pod communicate over localhost

How to handle multiple ports under the same container in kubernetes yaml file

snippet inside docker-compose.yml
graphite:
image: sitespeedio/graphite:1.1.5-3
ports:
- "2003:2003"
- "8080:80"
restart: always
current snippet inside kube.yml that needs changes
spec:
containers:
- name: graphite
image: sitespeedio/graphite:1.1.5-3
restartPolicy: Always
ports:
- containerPort: 2003
here, how to write the second mentioned - "8080:80" port in kubernetes yaml file?
spec:
containers:
- name: graphite
image: sitespeedio/graphite:1.1.5-3
restartPolicy: Always
ports:
- containerPort: 2003
name: graphite_two
- containerPort: 8080
name: graphite_one
will solve your issue.
kubectl explain pods.spec.containers.ports provides the detail information about the keys and values