Last active
August 29, 2015 14:22
-
-
Save whitekid/0b6e9781455e550f0042 to your computer and use it in GitHub Desktop.
sync docker container network namespace
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"errors" | |
"fmt" | |
"io/ioutil" | |
"log" | |
"os" | |
"time" | |
"github.com/fsouza/go-dockerclient" | |
) | |
const connect_retry_interval = 3 * time.Second | |
func get_docker_client() (*docker.Client, error) { | |
endpoint := os.Getenv("DOCKER_HOST") | |
if endpoint == "" { | |
endpoint = "unix:///var/run/docker.sock" | |
} | |
cert_path := os.Getenv("DOCKER_CERT_PATH") | |
var client *docker.Client | |
var err error | |
log.Printf("Connecting %v", endpoint) | |
if cert_path != "" { | |
ca := fmt.Sprintf("%s/ca.pem", cert_path) | |
cert := fmt.Sprintf("%s/cert.pem", cert_path) | |
key := fmt.Sprintf("%s/key.pem", cert_path) | |
client, err = docker.NewTLSClient(endpoint, cert, key, ca) | |
} else { | |
client, err = docker.NewClient(endpoint) | |
} | |
if err != nil { | |
return nil, err | |
} | |
if _, err = client.ListImages(docker.ListImagesOptions{}); err != nil { | |
return nil, err | |
} | |
log.Printf("Connected at %v", endpoint) | |
return client, nil | |
} | |
func get_container_pid(client *docker.Client, id string) int { | |
container, _ := client.InspectContainer(id) | |
return container.State.Pid | |
} | |
func file_exists(name string) bool { | |
if _, err := os.Stat(name); os.IsNotExist(err) { | |
return false | |
} | |
return true | |
} | |
func sync_netns(client *docker.Client) { | |
const netns_dir = "/var/run/netns" | |
log.Printf("sync docker namespaces...") | |
// delete invalid netns | |
files, _ := ioutil.ReadDir(netns_dir) | |
for _, f := range files { | |
if f.IsDir() { | |
continue | |
} | |
if f.Mode()&os.ModeSymlink != 0 { | |
netns_file := fmt.Sprintf("/var/run/netns/%s", f.Name()) | |
fname, _ := os.Readlink(netns_file) | |
if !file_exists(fname) { | |
log.Printf("Remove staled netns file: %s", netns_file) | |
err := os.Remove(netns_file) | |
if err != nil { | |
log.Printf("%s", err) | |
} | |
} | |
} | |
} | |
// create missed netns | |
containers, err := client.ListContainers(docker.ListContainersOptions{All: true}) | |
if err != nil { | |
log.Printf("%v", err) | |
} | |
for _, c := range containers { | |
pid := get_container_pid(client, c.ID) | |
// skip stopped container | |
if pid == 0 { | |
continue | |
} | |
if !file_exists(fmt.Sprintf("/var/run/netns/%v", c.ID)) { | |
create_netns(c.ID, pid) | |
} | |
} | |
} | |
func create_netns(id string, pid int) { | |
log.Printf("Create namespace %v", id) | |
err := os.Symlink(fmt.Sprintf("/proc/%v/ns/net", pid), fmt.Sprintf("/var/run/netns/%v", id)) | |
if err != nil { | |
log.Printf(err.Error()) | |
} | |
} | |
func delete_netns(id string) { | |
log.Printf("Delete namespace %v", id) | |
err := os.Remove(fmt.Sprintf("/var/run/netns/%s", id)) | |
if err != nil { | |
log.Printf(err.Error()) | |
} | |
} | |
func handle_event() error { | |
client, err := get_docker_client() | |
if err != nil { | |
return err | |
} | |
sync_netns(client) | |
listener := make(chan *docker.APIEvents, 10) | |
err = client.AddEventListener(listener) | |
if err != nil { | |
log.Fatalf("AddEventListener failed: %v", err) | |
} | |
defer client.RemoveEventListener(listener) | |
for { | |
select { | |
case event, ok := <-listener: | |
if !ok { | |
return errors.New("Docker connectin close.. retring.") | |
} | |
log.Printf("Event received: Status=%v, ID=%v", event.Status, event.ID) | |
switch event.Status { | |
case "create", "start": | |
pid := get_container_pid(client, event.ID) | |
create_netns(event.ID, pid) | |
case "stop", "destroy": | |
delete_netns(event.ID) | |
} | |
} | |
} | |
} | |
func main() { | |
for { | |
err := handle_event() | |
log.Printf("ERROR: %v", err.Error()) | |
time.Sleep(connect_retry_interval) | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/env python | |
import os | |
import json | |
from requests.packages.urllib3.exceptions import ProtocolError | |
from docker import Client | |
def get_docker(): | |
return Client(base_url='unix:///var/run/docker.sock') | |
def sync_netns(dk): | |
netns_dir = '/var/run/netns' | |
# delete invalid netns | |
for f in os.listdir(netns_dir): | |
fn = os.path.join(netns_dir, f) | |
if os.path.islink(fn) and not os.path.exists(os.readlink(fn)): | |
os.unlink(fn) | |
# create missing netns | |
for c in dk.containers(): | |
cid = c['Id'] | |
pid = dk.inspect_container(c['Id'])["State"]["Pid"] | |
if not os.path.islink("/var/run/netns/%s" % cid): | |
os.symlink("/proc/%s/ns/net" % pid, "/var/run/netns/%s" % cid) | |
def main(): | |
while True: | |
try: | |
dk = get_docker() | |
sync_netns(dk) | |
for event_r in dk.events(): | |
event = json.loads(event_r) | |
status, cid = event['status'], event['id'] | |
# get pid of container | |
pid = dk.inspect_container(cid)["State"]["Pid"] | |
if status == 'start': | |
os.symlink("/proc/%s/ns/net" % pid, "/var/run/netns/%s" % cid) | |
elif status == 'kill': | |
os.unlink("/var/run/netns/%s" % cid) | |
except ProtocolError, e: | |
# restart docker | |
print e | |
if __name__ == '__main__': | |
main() | |
# vim: et ai ts=4 sw=4 |
@ziozzang 그냥 개념 증명이라 생각해주세요... docker-py를 설치하는 비용도 만만하지 않아서 실제로는 다르게 쓰겠죠..
https://gist.github.com/ziozzang/80b7a7bffc185b4d507b
간단하게 배쉬로 만들어 봤스므니다..
근데 이벤트 드리븐이 안되서 그냥 슬립으로 처리를 하긴 했는데 동작은 잘 됩니다. -,.-)b
근데 파이썬보다 코드가 짧네요.. ㅋㅋ
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
개인적인 의견으로